[Scummvm-git-logs] scummvm master -> fa6b927ca889b216bbf9b4636e46adcc5417be23

sev- noreply at scummvm.org
Tue May 5 11:04:27 UTC 2026


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

Summary:
d698c4885d PELROCK: Initial engine skeleton
8583a0febd PELROCK: Loads scene and main character
afac30a24c PELROCK: Reads room animations and renders on screen
91575276ca PELROCK: Reads hotspots and walkboxes
94e520c797 PELROCK: Separate animation sets
09d049a337 PELROCK: Display animations
1c9ef9c65b PELROCK: Implements Room exits
4aec798219 PELROCK: Load cursors, move alfred when changing room
a20b8fde58 PELROCK: Reads popup balloon animation and icons
faa428a45e PELROCK: Generalize bg copy and restoration
a09737f03c PELROCK: Positioning of action balloon
47ddfa060b PELROCK: Font rendering
8febd828eb PELROCK: identifies valid actions associated with hotspots
f131a5bad9 PELROCK: Calculates clickable areas
f0a541069d PELROCK: Selects sprites that are candidates for hotspots
98858463ec PELROCK: Display available actions
2efc7a1a41 PELROCK: Renders descriptions
7c6d67b89e PELROCK: Fixes verb action selection
a75cf60c5c PELROCK: Load conversations
1647b7d852 PELROCK: Reads conversations correctly into a tree
ae3266b3e1 PELROCK: Split alfred frames
6850b9a42b PELROCK: Displays walking and talking animations
0535b469a4 PELROCK: Properly refreshes scenes
0a07291d34 PELROCK: Change list to array
5ce6183800 PELROCK: Plays subanimations in order following control bytes
c3c6f36c95 PELROCK: Animation timing
7e0d3a6bad PELROCK: Walk target calculation
1623209a0b PELROCK: Quick and dirty walking algorithm
65b577da75 PELROCK: Improve walk algorithm
92489bb33e PELROCK: Improve text display
18060a6d5f PELROCK: Adds extra pixel char width on font
cbe2b9958d PELROCK: Font rendering
7148baeb87 PELROCK: Eliminates extra space in text dialog
b007fac21b PELROCK: Click on action
75f2713f77 PELROCK: Better long click handling
262ff0cfb3 PELROCK: Fixes wrong description loading
ac8813f39d PELROCK: Loads NPCs Talking anims
e65b34ec6c PELROCK: Reads second talking animation properly
b79204d594 PELROCK: Fixes talking animations offset
397b1d1fe8 PELROCK: Fixes text color selection
1ecefd962b PELROCK: Creates ResourceManager
36661bdcee PELROCK: Refactors room resource loading into its own class
9779766739 PELROCK: Refactor to use common extractSingleFrame function
b25ff75330 PELROCK: refactor animations
89c6caf126 PELROCK: Refactor other resource loading
07471b2029 PELROCK: Creates ResourceManager
321e401256 PELROCK: Comb animation
04a7b8c2c4 PELROCK: Alfred interacting animation
aaeac0248f PELROCK: Turn if into switch when handling alfred state
3226b5217a PELROCK: Initial Intro video playback
45624b5954 PELROCK: Renders first video sequence
8e3b5c5526 PELROCK: Initial scaling algorithm
79fdb25fcf PELROCK: Initial scaling algorithm
2df4ff4791 PELROCK: Loads shadow map
3240f59bd4 PELROCK: Character shading
58faeb41d2 PELROCK: Dialog choice overlay
a7e5c9cf2a PELROCK: Loads room names
10986e7bb0 PELROCK: Music playback (from mp3)
d2fa47a115 PELROCK: Fix music stopage
f1a3cd07d2 PELROCK: Renders settings screen
a0bc558ee0 PELROCK: Plays Sfx
b899645c4b PELROCK: Condense Alfred state
4e21a273c6 PELROCK: Rudimentary ambient sound generation
9c95c80859 PELROCK: Sprite movements (left/right and top/down)
ea20c30125 PELROCK: Fixes walking algorithm
ac84939fbb PELROCK: Refactor actions
a74e8c2904 PELROCK: Fixed walk before talk
f345388fbc PELROCK: Debug position markers
5751f8c2d5 PELROCK: Sprite order
d64304311f PELROCK: Sets temporary Alfred zorder
df8045d555 PELROCK: Fixes decompression script
9f89aabd1a PELROCK: Loads and renders inventory icons
8463665fa1 PELROCK: Reads the right settings screen
4c4a20c93c PELROCK: Loads right palette for settings menu
c7ec5a2917 PELROCK: Splits menu and game event handling
15b0077a61 PELROCK: Remove g_engine references from engine
9a2c56321b PELROCK: Loads inventory descriptions
4ebec869e3 PELROCK: Associates descriptions to objects
c3300ba0c6 PELROCK: Fixes persistent text
47ba1000c9 PELROCK: Navigate through inventory icons
e3517058e6 PELROCK: Fixes decoding issues
9b14c94681 PELROCK: Room 2 palette anim
7dd082958e PELROCK: Palette anim from room 0
9fc45ddbca PELROCK: Consolidate palette animations
e20590b5bd PELROCK: Loads stickers
a3fb1e4e1c PELROCK: Refactor pathfinding into its own module
7bc6fce6fc PELROCK: Restructuring of renderloop
b58ca4248c PELROCK: Create EventManager
e6ad7d5ed4 PELROCK: First attempt at conversation flow
4a16eee238 PELROCK: Creates double font
105ea475b0 PELROCK: Implements double height font
f5a9d2bcf2 PELROCK: Decodes text bytes properly
deb0853882 PELROCK: Fixes responses during conversations
318b7f345f PELROCK: Initial attempt at word wrapping algorithm
570547df39 PELROCK: Center text above character
dce198bd6e PELROCK: Enable talking animations
97e87afd09 PELROCK: Highlighting of conversation choices
2c4d6eb290 PELROCK: Fixes word wrap rendering
d5fd802816 PELROCK: Stop conversation when no more choices are available.
1d9b7dc63b PELROCK: Applies z-movement to sprites
d0f1be8645 PELROCK: Animation speeds for talking
60d73a5f1c PELROCK: Fixes incorrect zorder drawing
2326250c3d PELROCK: Moves menu handling into its own class
567ff2ce41 PELROCK: Loads menu texts
ae7169c6f8 PELROCK: Processes new line commands
fcb23865fd PELROCK: Process colored text
c45bf2369c PELROCK: Add and remove Stickers
69fc606859 PELROCK: Opens/Closes door and enables exit
1ea9598309 PELROCK: Refactor popup state
0d4a8eee0a PELROCK: Loads extra screens
eb5618572c PELROCK: Queued actions
7728e3ea12 PELROCK: Improve exit calculation
ed2e14da57 PELROCK: Simple inventory management
b533f0ac91 PELROCK: Remove debug artifacts
558c76b6d5 PELROCK: Idle animation
cf4d609cda PELROCK: Fixes text processing
441230ed99 PELROCK: Action Handler
3566f40b75 PELROCK: Adds item to action popup
b4f04b9518 PELROCK: Additional text processing
fedc0a9a10 PELROCK: Inventory icon flashes when picked up
2e4bbe82e6 PELROCK: Cleanup of pelrock engine class
149fd0a54e PELROCK: Better handling of mouse events
70b784d2d0 PELROCK: Calculate facing direction
262ec696c5 PELROCK: Fixes crash with non-existing hotspot
86f6b84e83 PELROCK: Pickup animation
e960d138d9 PELROCK: State change through function
ae72e1d66e PELROCK: Fixes redundant frame when changing alfred state
b8398b86dc PELROCK: Implements menu buttons
0a5f0560a5 PELROCK: Refactor button resource reading
f5a3133cb4 PELROCK: Properly place all menu buttons
ce313b2056 PELROCK: Plays entire intro video
33afb68090 PELROCK: Fix missing alfred frame on queued actions
4f3db80507 PELROCK: Switch to using AudioCDManager
62b56b6c36 PELROCK: Renders subtitles on intro video
291103f79b PELROCK: Proper colors in video subtitles
07706d3261 PELROCK: Word wraps subtitles
476564d982 PELROCK: Improvements on text positioning
0a3c9e5e6e PELROCK: Improvements on text positioning
a527041b51 PELROCK: Checks mouse hover against sprite pixel masks
a13abbc63c PELROCK: Refactor sprite data to preprocess animations
65a66c21d0 PELROCK: Reads voice files
22ff6faa73 PELROCK: Plays intro speech
828e6d9b84 PELROCK: Read metadata parameters from byte buffer
bd027e3ecf PELROCK: Reset room states before loading
67b294ed5b PELROCK: Video timing
fc798c1b53 PELROCK: Saves room changes in memory gamestate
6da8f84da2 PELROCK: Refactor dialog handler, reset and skip disabled conversations
40001b9ba5 PELROCK: Saves state of disabled conversation choices
8b077e1e02 PELROCK: Selects correct conversation when multiple npcs exist
89d26fe2a9 PELROCK: Fixes descriptions not being cleared
3622f07b3d PELROCK: Disables conversation roots
19c6f7bc1a PELROCK: Fixes dialog lines being read incorrectly
eafad6d16d PELROCK: Fix text positioning for NPCs
b1034e8745 PELROCK: Initial save/load implementation
b0044c74d6 PELROCK: Fix extra Alfred being show when clicking on target object
33b640a79e PELROCK: Swap palette remaps
7701be0918 PELROCK: Walk to center of hotspots
c80e1a25bc PELROCK: Use inventory item with hotspot
d8d5873eee PELROCK: Wrong combination responses
1481f31147 PELROCK: Pickup sauces
dec2e69b73 PELROCK: open/close doors
16ebb316a2 PELROCK: Triggers side effects in other rooms
5140ab1362 PELROCK: Shows action popup over alfred, fixes hover priority, mouse position capture
d3db3eff48 PELROCK: Use stuff with Alfred
8f89c446f2 PELROCK: Reading recipe
e78e9488e0 PELROCK: Fixes timing issues in sprites, walking speed and palette animations
b4f94c59fc PELROCK: Scaling improvements
8e6c8b01a4 PELROCK: Additional debugging capabilities
37add58b12 PELROCK: Implement all palette animations
7f740aff3c PELROCK: Better sound management
c8c2995f9e PELROCK: Add actions for room 3
b3e454c6ff PELROCK: Implements Room 15
0078df575b PELROCK: Implements actions on Room 4
fa5ee4b021 PELROCK: Improvements on dialog handling
aeae414a3e PELROCK: Room 4 conversation marker
bc0118e171 PELROCK: Implements computer on room 9
463759106f PELROCK: Cleanup
451919142a PELROCK: Finish room 4
9379385a8e PELROCK: Adds forced terminator to conversations
acdc0fd107 PELROCK: Dialog in room 5
08d7b47d4d PELROCK: Fixes dialog in room 7, fixes zorder
c6066f1707 PELROCK: WIP mouse movement in room 9
8fde0bac53 PELROCK: Implements library mouse (pass-by animations)
83b8fb17e9 PELROCK: Computer-book logic
dd28828c1a PELROCK: implement reading of books
a6ed3a2764 PELROCK: Implements room 14
bdad1b18ac PELROCK: Room changes refactor
f22d5018c2 PELROCK: Enable animations when museum is open
2e9a0fb46e PELROCK: Room 17
97258764ff PELROCK: Implements passer-by animations
d369e67fd7 PELROCK: Model mouse animation in room9 as passer-by
8d94e680e8 PELROCK: Implements palette animation on statue
fb9ae7561f PELROCK: Adds side effect to reading recipe
a0f37b533d PELROCK: Fixes animations in room 40
6480e47ea4 PELROCK: Implements water reflection effect
ea33ffd967 PELROCK: Restrict reflection effect to y position
2a510cdcec PELROCK: Disable branches in travel agency
7002cdd213 PELROCK: Map animation
73d9d97fb9 PELROCK: Fix walking algorithm so it doesnt exit the room when interacting with doors
14dfa22ef4 PELROCK: implements fade to black
d65032835f PELROCK: Room 21 (map)
562e221780 PELROCK: show entire inventory when picking object
4d4ff456f0 PERLOCK: adjust mouse hover over Alfred
398957a124 PELROCK: Improve action selection
433b1eb364 PELROCK: Dialog refactor into functions
c27335f362 PELROCK: Implements cascade disable of choice trees in conversations
90c330d345 PELROCK: Room 22 actions
876544feb3 PELROCK: Fixes shading bug
104e61136d PELROCK: Palette effect on room 28
99d7edabea PELROCK: Room 23
1c366f311f PELROCK: Implements crocodile animation
186db8d250 PELROCK: Sfx in menu
ae81d1088f PELROCK: Fixes conversation root model to be setRoot
77340b2dde PELROCK: Room 25
8067a173d1 PELROCK: Implements picking up objects in room 28
1f929725d2 PELROCK: Spell book
496946f4b2 PELROCK: Implements Egyptian museum
87d5093bfd PELROCK: Play sound when opening secret door
556703dc94 PELROCK: Implements Rooms 31 and 32
426763e30a PELROCK: Implements Room 33
92cb18e7b7 PELROCK: Fixes animation always playing when entering room 38
9519dd6c93 PELROCK: Tunnel exit animation
d89bbc6eb9 PELROCK: Fixes conversations in room 27
d8f0c53e7f PELROCK: Activate room 27's new conversations when opening Safe
a3d3a1778e PELROCK: Fix conversation advancement on previously done tirggers
d7e147ed85 PELROCK: Trigger police after going to jail
f2af94bf4c PELROCK: Doll animation in prison cell
2d39406a85 PELROCK: Disables interaction during cutscenes
d385e8a507 PELROCK: Implements using glue + patches to build inflated doll
cdb379e9ad PELROCK: Fixes resetting palette in room 28
7d7038667e PELROCK: Fixes statue never giving object 8 (note)
44a1836877 PELROCK: "Running" sprites in rooms 34, 36
19605d40a5 PELROCK: Implements room 37
1e225caa41 PELROCK: Implemented time travel animation
63056fbb62 PELROCK: Swimming pool cutscene (partial implementation)
bcf440271c PELROCK: Implement police showing up after raiding tomb
e71aa5b8d1 PELROCK: Full animations in swimming pool cutscene
bd9ee4a407 PELROCK: Implements rest of swimming cutscene
afa4597b81 PELROCK: Fixes bug in walkAndAction where alfred wouldnt end up looking in the right direction
892a4db753 PELROCK: Stone pass skeleton
82bcbdcdac PELROCK: Implements cutscenes in room 41
831355caa2 PELROCK: Implements rooms 43, 44, 45
4043d1ffc2 PELROCK: Crawl animation for room 55, fixes palette remappings
e71946da22 PELROCK: Room 47
c5a47052d4 PELROCK: Wrong door cutscene in Room 48
91be0ceead PELROCK: Correct choice of door in room 48 (hatch)
f26411b289 PELROCK: Room 49
20202772b6 PELROCK: Trigger newspaper scene also in room 0
39cca76119 PELROCK: Implements timing and spells in rooms 51-54
943271a5e7 PELROCK: Stop frame counter during dialog
8c0062a954 PELROCK: Play sound when using amulet and during electric shock
ced34be28f PELROCK: Gods/Sorcerer scenes in rooms 51, 52, 53, 54
f26de9e5e1 PELROCK: Implements missing useless item combinations
63179eff5e PELROCK: Loads CD Player screen
14945d2f9c PELROCK: CD Player (except pause)
3f7f9b75f0 PELROCK: Implements background book
6f69e96103 PELROCK: Improves styling in Library Computer
ccc3065030 PELROCK: Implements ending cutscene on room 52
27d21a0a07 PELROCK: Implements scene with girl in the outro
7fe5550209 PELROCK: Enables final hotspot after fight sequence
78a126017c PELROCK: Loads inventory paging arrows
e197092b5e PELROCK: Implements inventory overlay when using items
8ccb1b06a2 PELROCK: Implements inventory overlay scroll and click
1f188c55f8 PELROCK: Scroll dialog choices
d574cf97d0 PELROCK: add correct sizing to dialogue surface
3d1eb5b388 PELROCK: Implements missing actions
121b734d34 PELROCK: Credits in main menu
87769a6d09 PELROCK: Refactor alfred special anim code into its own function
c491840579 PELROCK: Fixes positioning action balloon and inventory scroll
d0997de296 PELROCK: Pickup bush in room 2, door in room 47
2510f3061b PELROCK: Implements action and continue trigger in conversations
3b03b4ba43 PELROCK: Implements credits in the ending
923774a099 PELROCK: Implements pyramid shaking scene
919f61d3f8 PELROCK: Pyramid collapsing animation on room 36
e53ffc7a6d PELROCK: Adjusts text in credits slideshow
24970d5b90 PELROCK: Post intro cutscene
fc84b9a894 PELROCK: Fix warnings
849b58d6a5 PELROCK: Fixes fall-through with salesman
0a34967556 PELROCK: Fall through room 15
692e16d654 PELROCK: Implements dog pee animation in room 19 and pigeons scene in 24
2f0ac42165 PELROCK: Implements sound effects and music in intro video
0e07504d16 PELROCK: Fixes animation in room 3 and computer text
60acdb4c8d PELROCK: Fixes menu descriptions
c2cbca370c PELROCK: Fixes walkboxes in room 14
197f9e676d PELROCK: Fixes music in travel scene
acae2fa733 PELROCK: Fixes using pumpkin with river
3ec216e9e9 PELROCK: Migrate to using ManagedSurfaces
133fe537c9 PELROCK: Fixes original bug in statue
d94c6dc379 PELROCK: Fixes intro timing
a4c9db112d PELROCK: Fixes inventory overlay glitches (autoscrolls on last icon)
cef88966e5 PELROCK: Fixes stone delivery sequence in rooms 41 & 42
b0fe4b04a9 PELROCK: Fixes credits not restarting
1f06f64278 PELROCK: Fixes credits changing color upon repeat
aa3c4639b8 PELROCK: Fixes removal of letter x in ingame texts
e482a187e1 PELROCK: Fixes for end cutscene
23093df6e0 PELROCK: General code cleanup
b7057581cb PELROCK: Disable looping for incidental music
f1b6d3a7ed PELROCK: Fixes stale item staying in action popup after use
ad219ccbbd PELROCK: Fixes book titles not having new lines
8577942222 PELROCK: Implement loading of background book
719de81bfb PELROCK: Removes all items when moving to Egypt
8962a72e54 PELROCK: Fixes zorder
0aa4161923 PELROCK: Adds extra stickers with glowy eyes to statues in final fight
d9d8e6cac0 PELROCK: Implements background viewer
cbf01025e9 PELROCK: Refactor cdplayer, background book, spellbook
8767c0a4bc PELROCK: Turn all offsets and sizes into constants
0171a5e989 PELROCK: Prevent sticker flicker on room change
58677e4489 PELROCK: Closes main menu after loading/saving
3fb39ab5c7 PELROCK: Implement TTL for dialogue lines
0e5168ea3a PELROCK: Move pertinent code into graphics
57a84acae1 PELROCK: Fixes mouse hover breaking if positioned where action popup used to be
4736dc2ad8 PELROCK: Implements original timing when walking/talking (halving the speed)
f0fdccb1d8 PELROCK: Respect original timing too for NPC talking animations
266df65e9a PELROCK: Fixes idle animation of talking character in room 39
1a9467777c PELROCK: Icons and scaling for volume control
cb596d4fbe PELROCK: Volume icons scale up/down with left button rather than click
4fefe69117 PELROCK: Implements volume controls
9333118fe9 PELROCK: Sounds in menu
7aff19c6ea sve thumbnail
cf6c76d0f5 PELROCK: Saves games with the right thumbnail
48b3c9d8d9 PELROCK: First implementation of sliding screensaver
9e1917d9f0 PELROCK: Enables fake teeth animation in room 3, increases amount of random responses
a7ded25994 PELROCK: Fixes for interaction with the statue in room 7
c507068bb2 PELROCK: Refactors main menu and adds confirmation upon exit
1d6826d392 PELROCK: Implements original save/load screens
7f8b161088 PELROCK: Missing actions
9954449942 PELROCK: Fixes conversation bug (present in the original) in room 45
e35f47c289 PELROCK: Fixes exit confirmation
5708dae5d5 PELROCK: Fixes styling of saving name input
2b1bcc34dc PELROCK: Fixes spells in spellbook having extraneous characters
9ede09dccf PELROCK: Amend checkConversation signature
6b269d2203 PELROCK: Fixes bug (present in the original) when handing books to librarian or using them on alfred
b74947e4da PELROCK: Allows loading from ScummVM's main menu
a5964bf279 PELROCK: Fixes extraneous characters on intro's subtitles
c1c9772c50 PELROCK: Fixes bug (present in the original) where endless loop can happen during conversation
67735982b7 PELROCK: Fixes not being able to use stuff with Alfred from inventory overlay
780c84181b PELROCK: Fix sounds in inventory to leave click as default sound
13834000a9 PELROCK: Scale tile shuffling with tile count in sliding puzzle minigame
e31fce39dd PELROCK: Improves anti-piracy joke
b6f5b1bd5a PELROCK: Fixes bug that would make travel agency inaccesible
d3e3cb2d7d PELROCK: Fix extraneous idle Alfred showing up after being eaten by croc
027a91d16b PELROCK: Ensure same ambient sound doesnt overlap with itself
ecd269a800 PELROCK: Clear inventory before time travelling
dd1ab34e35 PELROCK: Cleanup video, fonts and actions
ffd5b9652e PELROCK: Cleanup of dialog and computer
89189c032b PELROCK: Cleanup of menu and graphics
cf7004677e PELROCK: Cleanup of pathfinding and room
9afa4e1c63 PELROCK: Cleanup of sound and spellbook
903b6e4d22 PELROCK: Cleanup utils
ce2b01f4fa PELROCK: Types cleanup
34129b6bff PELROCK: Cleanup of offsets
cb81706019 PELROCK: Fix warnings
13330521f9 PELROCK: Move inventory item sounds lookup table into implementation
97c26c5ec3 PELROCK: Rename size constants for custom fonts
5977e6a2f3 PELROCK: Removal of warnings due to incompatible types
c5b87203f0 PELROCK: Fix mismatched free/deletes
98b3285bae PELROCK: Fixes leak on Dialog surface
1e2346f201 PELROCK: Fixes leak with passer-by-animations and palette animations
d81b5918a5 PELROCK: Make sure to clear animations before loading new ones
ee7fa87c94 PELROCK: Fix menu buttons not being cleared
b827ac0f9b PELROCK: Fixes leak in room reset data
693a07ddec PELROCK: Fix savegame data leak
fefda1a14e PELROCK: Avoid leaking choice list
a9f9e974d0 PELROCK: Make sure to free all pointers in room, pelrock and resources
1653e473e9 PELROCK: Moves sticker pixel data into its own structure
6c8e9d1496 PELROCK: Fix leak when loading game, and on conversation exit
5a73a5c07a PELROCK: Fix error when converting offsets
fab16d9824 PELROCK: Consolidate drawColoredTexts functions in graphics
c1e9301f6b PELROCK: Make crocodile sprite stick in crocodile sequence
920635b2f4 PELROCK: Improvements on idle animation and screensaver minigame
c2cf7a3d40 PELROCK: Fix scaling algorithm so feet arent cropped
c128562144 PELROCK: Add option to disable screensaver
c849d9789c PELROCK: Cleanup of pelrock.cpp
d7b1739e88 PELROCK: Fix sticker in egyptian museum door
9c2bfd6fb0 PELROCK: Set extra flags in case they are needed
e37bc9064d PELROCK: Translate flag names into English (from the original Spanish)
97706b2f17 PELROCK: Introduce getBoolFlag to prevent unsafe comparison on Windows
d4878a08f9 PELROCK: Normalize types for chrono
ebb2a7be7c PELROCK: Remove extraneous debug lines
244339e0fb PELROCK: Fix double-free after swimming pool cutscene
16fae188a6 PELROCK: Credits
7707b16203 PELROCK: Remove unnecessary files
f968301d6f PELROCK: Make sure to free textSurface in intro video
23a9a6592b PELROCK: Move video to root of the engine
a89f9d1c53 PELROCK: Header cleanup
a25a581670 PELROCK: Cleans up sliding puzzle
e84cbd9ff2 PELROCK: Decorate errors with function signature
9e1d34ce2e PELROCK: Simplify pixel setting in fonts
c83cb780a1 PELROCK: Fixes on syntax according to code reviews
1dfb93703d PELROCK: Syntax fixes from code reviews
a51d5b0afb PELROCK: Added ALFRED.2 to the detection tables
e87dd28ce1 PELROCK: Replace usage of char with byte where platform sensitive
963da1bbe5 PELROCK: Remove statics from room header
828574c3f5 PELROCK: Create menu sfx utility functions
b3bfc1776e PELROCK: Refactor Room metadata loading so it doesnt return by value
8534001165 PELROCK: Fix warnings on overflowing chars
d44fc4aaa5 PELROCK: Remove duplicate logic into resetPasserByAnim
e71a30c895 PELROCK: Create constructor for PasserBy animations
5c953ada38 PELROCK: Creates macro for deleting button sprites in menu
fa6b927ca8 PELROCK: Fixes talking animation occasionally not advancing frames on original timing


Commit: d698c4885d76c9e5c20c62485b6f4ad4afd66a09
    https://github.com/scummvm/scummvm/commit/d698c4885d76c9e5c20c62485b6f4ad4afd66a09
Author: gsanmartin (gabriel.sanmartin at dna.inc)
Date: 2026-05-05T12:58:57+02:00

Commit Message:
PELROCK: Initial engine skeleton

Changed paths:
  A engines/pelrock/POTFILES
  A engines/pelrock/configure.engine
  A engines/pelrock/console.cpp
  A engines/pelrock/console.h
  A engines/pelrock/credits.pl
  A engines/pelrock/detection.cpp
  A engines/pelrock/detection.h
  A engines/pelrock/detection_tables.h
  A engines/pelrock/metaengine.cpp
  A engines/pelrock/metaengine.h
  A engines/pelrock/module.mk
  A engines/pelrock/pelrock.cpp
  A engines/pelrock/pelrock.h


diff --git a/engines/pelrock/POTFILES b/engines/pelrock/POTFILES
new file mode 100644
index 00000000000..c09b6bf0735
--- /dev/null
+++ b/engines/pelrock/POTFILES
@@ -0,0 +1 @@
+engines/pelrock/metaengine.cpp
diff --git a/engines/pelrock/configure.engine b/engines/pelrock/configure.engine
new file mode 100644
index 00000000000..a4ac40cb140
--- /dev/null
+++ b/engines/pelrock/configure.engine
@@ -0,0 +1,3 @@
+# This file is included from the main "configure" script
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
+add_engine pelrock "Pelrock" no "" "" "" ""
diff --git a/engines/pelrock/console.cpp b/engines/pelrock/console.cpp
new file mode 100644
index 00000000000..1964cb5e3f6
--- /dev/null
+++ b/engines/pelrock/console.cpp
@@ -0,0 +1,38 @@
+/* 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 "pelrock/console.h"
+
+namespace Pelrock {
+
+Console::Console() : GUI::Debugger() {
+	registerCmd("test",   WRAP_METHOD(Console, Cmd_test));
+}
+
+Console::~Console() {
+}
+
+bool Console::Cmd_test(int argc, const char **argv) {
+	debugPrintf("Test\n");
+	return true;
+}
+
+} // End of namespace Pelrock
diff --git a/engines/pelrock/console.h b/engines/pelrock/console.h
new file mode 100644
index 00000000000..f17d71e587e
--- /dev/null
+++ b/engines/pelrock/console.h
@@ -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/>.
+ *
+ */
+
+#ifndef PELROCK_CONSOLE_H
+#define PELROCK_CONSOLE_H
+
+#include "gui/debugger.h"
+
+namespace Pelrock {
+
+class Console : public GUI::Debugger {
+private:
+	bool Cmd_test(int argc, const char **argv);
+public:
+	Console();
+	~Console() override;
+};
+
+} // End of namespace Pelrock
+
+#endif // PELROCK_CONSOLE_H
diff --git a/engines/pelrock/credits.pl b/engines/pelrock/credits.pl
new file mode 100644
index 00000000000..3f3ab6a82f2
--- /dev/null
+++ b/engines/pelrock/credits.pl
@@ -0,0 +1,3 @@
+begin_section("Pelrock");
+	add_person("Name 1", "Handle 1", "");
+end_section();
diff --git a/engines/pelrock/detection.cpp b/engines/pelrock/detection.cpp
new file mode 100644
index 00000000000..d0b46ac7502
--- /dev/null
+++ b/engines/pelrock/detection.cpp
@@ -0,0 +1,45 @@
+/* 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 "base/plugins.h"
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/md5.h"
+#include "common/str-array.h"
+#include "common/translation.h"
+#include "common/util.h"
+#include "pelrock/detection.h"
+#include "pelrock/detection_tables.h"
+
+const DebugChannelDef PelrockMetaEngineDetection::debugFlagList[] = {
+	{ Pelrock::kDebugGraphics, "Graphics", "Graphics debug level" },
+	{ Pelrock::kDebugPath, "Path", "Pathfinding debug level" },
+	{ Pelrock::kDebugFilePath, "FilePath", "File path debug level" },
+	{ Pelrock::kDebugScan, "Scan", "Scan for unrecognised games" },
+	{ Pelrock::kDebugScript, "Script", "Enable debug script dump" },
+	DEBUG_CHANNEL_END
+};
+
+PelrockMetaEngineDetection::PelrockMetaEngineDetection() : AdvancedMetaEngineDetection(
+	Pelrock::gameDescriptions, Pelrock::pelrockGames) {
+}
+
+REGISTER_PLUGIN_STATIC(PELROCK_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, PelrockMetaEngineDetection);
diff --git a/engines/pelrock/detection.h b/engines/pelrock/detection.h
new file mode 100644
index 00000000000..263ea2e9ac3
--- /dev/null
+++ b/engines/pelrock/detection.h
@@ -0,0 +1,69 @@
+/* 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 PELROCK_DETECTION_H
+#define PELROCK_DETECTION_H
+
+#include "engines/advancedDetector.h"
+
+namespace Pelrock {
+
+enum PelrockDebugChannels {
+	kDebugGraphics = 1,
+	kDebugPath,
+	kDebugScan,
+	kDebugFilePath,
+	kDebugScript,
+};
+
+extern const PlainGameDescriptor pelrockGames[];
+
+extern const ADGameDescription gameDescriptions[];
+
+#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1
+
+} // End of namespace Pelrock
+
+class PelrockMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
+	static const DebugChannelDef debugFlagList[];
+
+public:
+	PelrockMetaEngineDetection();
+	~PelrockMetaEngineDetection() override {}
+
+	const char *getName() const override {
+		return "pelrock";
+	}
+
+	const char *getEngineName() const override {
+		return "Pelrock";
+	}
+
+	const char *getOriginalCopyright() const override {
+		return "Pelrock (C)";
+	}
+
+	const DebugChannelDef *getDebugChannels() const override {
+		return debugFlagList;
+	}
+};
+
+#endif // PELROCK_DETECTION_H
diff --git a/engines/pelrock/detection_tables.h b/engines/pelrock/detection_tables.h
new file mode 100644
index 00000000000..3faf9ddbcf5
--- /dev/null
+++ b/engines/pelrock/detection_tables.h
@@ -0,0 +1,43 @@
+/* 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/>.
+ *
+ */
+
+namespace Pelrock {
+
+const PlainGameDescriptor pelrockGames[] = {
+	{ "pelrock", "Pelrock" },
+	{ 0, 0 }
+};
+
+const ADGameDescription gameDescriptions[] = {
+	{
+		"pelrock",
+		nullptr,
+		AD_ENTRY1s("file1.bin", "00000000000000000000000000000000", 11111),
+		Common::EN_ANY,
+		Common::kPlatformDOS,
+		ADGF_UNSTABLE,
+		GUIO1(GUIO_NONE)
+	},
+
+	AD_TABLE_END_MARKER
+};
+
+} // End of namespace Pelrock
diff --git a/engines/pelrock/metaengine.cpp b/engines/pelrock/metaengine.cpp
new file mode 100644
index 00000000000..a6268bedeaf
--- /dev/null
+++ b/engines/pelrock/metaengine.cpp
@@ -0,0 +1,69 @@
+/* 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 "common/translation.h"
+
+#include "pelrock/metaengine.h"
+#include "pelrock/detection.h"
+#include "pelrock/pelrock.h"
+
+namespace Pelrock {
+
+static const ADExtraGuiOptionsMap optionsList[] = {
+	{
+		GAMEOPTION_ORIGINAL_SAVELOAD,
+		{
+			_s("Use original save/load screens"),
+			_s("Use the original save/load screens instead of the ScummVM ones"),
+			"original_menus",
+			false,
+			0,
+			0
+		}
+	},
+	AD_EXTRA_GUI_OPTIONS_TERMINATOR
+};
+
+} // End of namespace Pelrock
+
+const char *PelrockMetaEngine::getName() const {
+	return "pelrock";
+}
+
+const ADExtraGuiOptionsMap *PelrockMetaEngine::getAdvancedExtraGuiOptions() const {
+	return Pelrock::optionsList;
+}
+
+Common::Error PelrockMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+	*engine = new Pelrock::PelrockEngine(syst, desc);
+	return Common::kNoError;
+}
+
+bool PelrockMetaEngine::hasFeature(MetaEngineFeature f) const {
+	return checkExtendedSaves(f) ||
+		(f == kSupportsLoadingDuringStartup);
+}
+
+#if PLUGIN_ENABLED_DYNAMIC(PELROCK)
+REGISTER_PLUGIN_DYNAMIC(PELROCK, PLUGIN_TYPE_ENGINE, PelrockMetaEngine);
+#else
+REGISTER_PLUGIN_STATIC(PELROCK, PLUGIN_TYPE_ENGINE, PelrockMetaEngine);
+#endif
diff --git a/engines/pelrock/metaengine.h b/engines/pelrock/metaengine.h
new file mode 100644
index 00000000000..d1649be567f
--- /dev/null
+++ b/engines/pelrock/metaengine.h
@@ -0,0 +1,43 @@
+/* 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 PELROCK_METAENGINE_H
+#define PELROCK_METAENGINE_H
+
+#include "engines/advancedDetector.h"
+
+class PelrockMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
+public:
+	const char *getName() const override;
+
+	Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
+
+	/**
+	 * Determine whether the engine supports the specified MetaEngine feature.
+	 *
+	 * Used by e.g. the launcher to determine whether to enable the Load button.
+	 */
+	bool hasFeature(MetaEngineFeature f) const override;
+
+	const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override;
+};
+
+#endif // PELROCK_METAENGINE_H
diff --git a/engines/pelrock/module.mk b/engines/pelrock/module.mk
new file mode 100644
index 00000000000..5a2724a9990
--- /dev/null
+++ b/engines/pelrock/module.mk
@@ -0,0 +1,17 @@
+MODULE := engines/pelrock
+
+MODULE_OBJS = \
+	pelrock.o \
+	console.o \
+	metaengine.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_PELROCK), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
+
+# Detection objects
+DETECT_OBJS += $(MODULE)/detection.o
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
new file mode 100644
index 00000000000..85bb1a67cc6
--- /dev/null
+++ b/engines/pelrock/pelrock.cpp
@@ -0,0 +1,109 @@
+/* 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 "pelrock/pelrock.h"
+#include "graphics/framelimiter.h"
+#include "pelrock/detection.h"
+#include "pelrock/console.h"
+#include "common/scummsys.h"
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "common/events.h"
+#include "common/system.h"
+#include "engines/util.h"
+#include "graphics/paletteman.h"
+
+namespace Pelrock {
+
+PelrockEngine *g_engine;
+
+PelrockEngine::PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engine(syst),
+	_gameDescription(gameDesc), _randomSource("Pelrock") {
+	g_engine = this;
+}
+
+PelrockEngine::~PelrockEngine() {
+	delete _screen;
+}
+
+uint32 PelrockEngine::getFeatures() const {
+	return _gameDescription->flags;
+}
+
+Common::String PelrockEngine::getGameId() const {
+	return _gameDescription->gameId;
+}
+
+Common::Error PelrockEngine::run() {
+	// Initialize 320x200 paletted graphics mode
+	initGraphics(320, 200);
+	_screen = new Graphics::Screen();
+
+	// Set the engine's debugger console
+	setDebugger(new Console());
+
+	// If a savegame was selected from the launcher, load it
+	int saveSlot = ConfMan.getInt("save_slot");
+	if (saveSlot != -1)
+		(void)loadGameState(saveSlot);
+
+	// Draw a series of boxes on screen as a sample
+	for (int i = 0; i < 100; ++i)
+		_screen->frameRect(Common::Rect(i, i, 320 - i, 200 - i), i);
+	_screen->update();
+
+	// Simple event handling loop
+	byte pal[256 * 3] = { 0 };
+	Common::Event e;
+	int offset = 0;
+
+	Graphics::FrameLimiter limiter(g_system, 60);
+	while (!shouldQuit()) {
+		while (g_system->getEventManager()->pollEvent(e)) {
+		}
+
+		// Cycle through a simple palette
+		++offset;
+		for (int i = 0; i < 256; ++i)
+			pal[i * 3 + 1] = (i + offset) % 256;
+		g_system->getPaletteManager()->setPalette(pal, 0, 256);
+		// Delay for a bit. All events loops should have a delay
+		// to prevent the system being unduly loaded
+		limiter.delayBeforeSwap();
+		_screen->update();
+		limiter.startFrame();
+	}
+
+	return Common::kNoError;
+}
+
+Common::Error PelrockEngine::syncGame(Common::Serializer &s) {
+	// The Serializer has methods isLoading() and isSaving()
+	// if you need to specific steps; for example setting
+	// an array size after reading it's length, whereas
+	// for saving it would write the existing array's length
+	int dummy = 0;
+	s.syncAsUint32LE(dummy);
+
+	return Common::kNoError;
+}
+
+} // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
new file mode 100644
index 00000000000..4ce714f965d
--- /dev/null
+++ b/engines/pelrock/pelrock.h
@@ -0,0 +1,105 @@
+/* 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 PELROCK_H
+#define PELROCK_H
+
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "common/error.h"
+#include "common/fs.h"
+#include "common/hash-str.h"
+#include "common/random.h"
+#include "common/serializer.h"
+#include "common/util.h"
+#include "engines/engine.h"
+#include "engines/savestate.h"
+#include "graphics/screen.h"
+
+#include "pelrock/detection.h"
+
+namespace Pelrock {
+
+struct PelrockGameDescription;
+
+class PelrockEngine : public Engine {
+private:
+	const ADGameDescription *_gameDescription;
+	Common::RandomSource _randomSource;
+protected:
+	// Engine APIs
+	Common::Error run() override;
+public:
+	Graphics::Screen *_screen = nullptr;
+public:
+	PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc);
+	~PelrockEngine() override;
+
+	uint32 getFeatures() const;
+
+	/**
+	 * Returns the game Id
+	 */
+	Common::String getGameId() const;
+
+	/**
+	 * Gets a random number
+	 */
+	uint32 getRandomNumber(uint maxNum) {
+		return _randomSource.getRandomNumber(maxNum);
+	}
+
+	bool hasFeature(EngineFeature f) const override {
+		return
+		    (f == kSupportsLoadingDuringRuntime) ||
+		    (f == kSupportsSavingDuringRuntime) ||
+		    (f == kSupportsReturnToLauncher);
+	};
+
+	bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override {
+		return true;
+	}
+	bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override {
+		return true;
+	}
+
+	/**
+	 * Uses a serializer to allow implementing savegame
+	 * loading and saving using a single method
+	 */
+	Common::Error syncGame(Common::Serializer &s);
+
+	Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override {
+		Common::Serializer s(nullptr, stream);
+		return syncGame(s);
+	}
+	Common::Error loadGameStream(Common::SeekableReadStream *stream) override {
+		Common::Serializer s(stream, nullptr);
+		return syncGame(s);
+	}
+};
+
+extern PelrockEngine *g_engine;
+#define SHOULD_QUIT ::Pelrock::g_engine->shouldQuit()
+
+} // End of namespace Pelrock
+
+#endif // PELROCK_H


Commit: 8583a0febd726a12eb7f5d425374066ee25a66fd
    https://github.com/scummvm/scummvm/commit/8583a0febd726a12eb7f5d425374066ee25a66fd
Author: gsanmartin (gabriel.sanmartin at dna.inc)
Date: 2026-05-05T12:58:58+02:00

Commit Message:
PELROCK: Loads scene and main character

Changed paths:
  A engines/pelrock/.gitignore
  A engines/pelrock/types.h
    engines/pelrock/detection_tables.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/.gitignore b/engines/pelrock/.gitignore
new file mode 100644
index 00000000000..7e97ef32309
--- /dev/null
+++ b/engines/pelrock/.gitignore
@@ -0,0 +1,3 @@
+*.o
+*.a
+.deps
diff --git a/engines/pelrock/detection_tables.h b/engines/pelrock/detection_tables.h
index 3faf9ddbcf5..f4c05cbab9e 100644
--- a/engines/pelrock/detection_tables.h
+++ b/engines/pelrock/detection_tables.h
@@ -30,7 +30,7 @@ const ADGameDescription gameDescriptions[] = {
 	{
 		"pelrock",
 		nullptr,
-		AD_ENTRY1s("file1.bin", "00000000000000000000000000000000", 11111),
+		AD_ENTRY1s("ALFRED.1", "ee0047cfcceece9c4f6a426245b6f449", 12915352),
 		Common::EN_ANY,
 		Common::kPlatformDOS,
 		ADGF_UNSTABLE,
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 85bb1a67cc6..5aaf7421da7 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -19,24 +19,30 @@
  *
  */
 
-#include "pelrock/pelrock.h"
-#include "graphics/framelimiter.h"
-#include "pelrock/detection.h"
-#include "pelrock/console.h"
-#include "common/scummsys.h"
 #include "common/config-manager.h"
 #include "common/debug-channels.h"
 #include "common/events.h"
+#include "common/file.h"
+#include "common/scummsys.h"
 #include "common/system.h"
 #include "engines/util.h"
+#include "graphics/cursorman.h"
+#include "graphics/framelimiter.h"
 #include "graphics/paletteman.h"
+#include "image/pcx.h"
+#include "image/png.h"
+
+#include "pelrock.h"
+#include "pelrock/console.h"
+#include "pelrock/detection.h"
+#include "pelrock/pelrock.h"
 
 namespace Pelrock {
 
 PelrockEngine *g_engine;
 
 PelrockEngine::PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engine(syst),
-	_gameDescription(gameDesc), _randomSource("Pelrock") {
+																				 _gameDescription(gameDesc), _randomSource("Pelrock") {
 	g_engine = this;
 }
 
@@ -54,7 +60,7 @@ Common::String PelrockEngine::getGameId() const {
 
 Common::Error PelrockEngine::run() {
 	// Initialize 320x200 paletted graphics mode
-	initGraphics(320, 200);
+	initGraphics(640, 400);
 	_screen = new Graphics::Screen();
 
 	// Set the engine's debugger console
@@ -65,28 +71,23 @@ Common::Error PelrockEngine::run() {
 	if (saveSlot != -1)
 		(void)loadGameState(saveSlot);
 
-	// Draw a series of boxes on screen as a sample
-	for (int i = 0; i < 100; ++i)
-		_screen->frameRect(Common::Rect(i, i, 320 - i, 200 - i), i);
-	_screen->update();
-
 	// Simple event handling loop
-	byte pal[256 * 3] = { 0 };
 	Common::Event e;
-	int offset = 0;
-
 	Graphics::FrameLimiter limiter(g_system, 60);
+
+	init();
+	if (shouldPlayIntro == false) {
+		stateGame = GAME;
+	} else {
+		stateGame = INTRO;
+		playIntro();
+	}
+
 	while (!shouldQuit()) {
 		while (g_system->getEventManager()->pollEvent(e)) {
 		}
-
-		// Cycle through a simple palette
-		++offset;
-		for (int i = 0; i < 256; ++i)
-			pal[i * 3 + 1] = (i + offset) % 256;
-		g_system->getPaletteManager()->setPalette(pal, 0, 256);
-		// Delay for a bit. All events loops should have a delay
-		// to prevent the system being unduly loaded
+		frames();
+		_screen->update();
 		limiter.delayBeforeSwap();
 		_screen->update();
 		limiter.startFrame();
@@ -95,6 +96,1823 @@ Common::Error PelrockEngine::run() {
 	return Common::kNoError;
 }
 
+void PelrockEngine::init() {
+	CursorMan.setDefaultArrowCursor();
+	CursorMan.showMouse(true);
+	if (gameInitialized == false) {
+		pixelsShadows = new byte[640 * 400];
+		// 		sabeUsarElLibroMagico = 0;
+		// 		magicWords = false;
+		// 		apagaLaLuz = false;
+		// 		showUsingObject = -1;
+		// 		prevWhichScreen = -1;
+		// 		whichScreen = -1;
+		gameInitialized = true;
+
+		// 		tutorial.init();
+
+		prevDirX = 0;
+		prevDirY = 0;
+		dirAlfred = 2;
+
+		objectToShow = "";
+
+		// 		factX = (float) widthScreen / (float) 640.0f;
+		// 		factY = (float) heightScreen / (float) 400.0f;
+
+		// xAlfred = (186 * factX);
+		// yAlfred = (307 * factY);
+		// xAlfred = 186;
+		// yAlfred = 307;
+
+		// 		for (int i = 0; i < 14; i++)
+		// 		{
+		// 			myPestanas[i].x = (int) ((float) myPestanas[i].x * factX);
+		// 			myPestanas[i].y = (int) ((float) myPestanas[i].y * factY);
+		// 			myPestanas[i].w = (int) ((float) myPestanas[i].w * factX);
+		// 			myPestanas[i].h = (int) ((float) myPestanas[i].h * factY);
+		// 		}
+
+		// 		movingAlfred = ALFRED_STOPPED;
+
+		// 		listUsedBranchs = new LinkedList<Integer>();
+		// 		listUsedBranchs.clear();
+
+		// 		listRects = new LinkedList<Integer>();
+		// 		listRects.clear();
+
+		// 		timeIddle = SystemClock.uptimeMillis();
+
+		// 		myListLibros = new LinkedList<libros>();
+		// 		myListLibros.clear();
+
+		// 		myListDesactAnims = new LinkedList<dactAnims>();
+		// 		myListDesactAnims.clear();
+
+		// 		myListChangesConvs = new LinkedList<changesConvs>();
+		// 		myListChangesConvs.clear();
+
+		// 		myListPegas = new LinkedList<pegas>();
+		// 		myListPegas.clear();
+
+		// 		myListObjects = new LinkedList<objects>();
+		// 		myListObjects.clear();
+
+		// 		myListUsingObjects = new LinkedList<Integer>();
+		// 		myListUsingObjects.clear();
+
+		// 		isPersonajeTalking = -1;
+
+		// 		stanteriaABuscar = -1;
+
+		// 		selectedUsingObject = false;
+
+		// 		currentTrack = "";
+
+		// 		mainVolumen = 50;
+		// 		mainTextSpeed = 50;
+
+		// 		extraDataToSave = new byte[98];
+
+		// 		leeLibros();
+		// 		myoverlay.setCredits();
+		loadAnims();
+		// 		loadOtherBitmaps();
+		setScreen(0, 2);
+		// setScreen(0, 2);
+		// 		valSound1 = 0;
+		// 		valSound2 = 0;
+		// 		valSound3 = 0;
+
+		// 		loadExtraTextsToTranslate();
+
+		// 		loadTutorialText();
+
+		// 		if (myMainActivity == null)
+		// 			myMainActivity = this;// new AlfredActivity();
+
+		// 		this.addContentView(myoverlay,
+		// 				new android.view.ViewGroup.LayoutParams(
+		// 						android.view.ViewGroup.LayoutParams.FILL_PARENT,
+		// 						android.view.ViewGroup.LayoutParams.FILL_PARENT));
+
+		// 		settingsLayout = new RelativeLayout(myContext);
+		// 		settingsLayout
+		// 				.setLayoutParams(new android.view.ViewGroup.LayoutParams(
+		// 						android.view.ViewGroup.LayoutParams.FILL_PARENT,
+		// 						android.view.ViewGroup.LayoutParams.FILL_PARENT));
+
+		// 		settingsElem1 = new SeekBar(myContext);
+		// 		settingsElem2 = new SeekBar(myContext);
+		// 		settingsElem3 = new Spinner(myContext);
+		// 		settingsElem4 = new Button(myContext);
+
+		// 		String[] items = new String[myLanguages.length];// {"English",
+		// 														// "Spanish",
+		// 														// "Three"};
+		// 		for (i = 0; i < myLanguages.length; i++)
+		// 		{
+		// 			items[i] = new String();
+		// 			items[i] = myLanguages[i].nShow;
+		// 		}
+
+		// 		mySpinAdapter<String> adapter = new mySpinAdapter<String>(myContext,
+		// 				items);
+
+		// 		settingsElem3.setAdapter(adapter);
+
+		// 		settingsElem3.setSelection(whichLanguageIndex);
+
+		// 		String stringTutorial = "";
+		// 		String namet = AlfredActivity.destinationFolder + "/tutorial.kk";
+		// 		File ft = new File(namet);
+
+		// 		if (ft.exists() == true)
+		// 		{
+		// 			byte b[] = tools.readFile(namet);
+		// 			b[0]++;
+
+		// 			if (b[0] >= 3)
+		// 			{
+		// 				b[0] = 3;
+		// 				playTutorial = false;
+		// 				stringTutorial = "OFF";
+		// 			} else
+		// 			{
+		// 				playTutorial = true;
+		// 				stringTutorial = "ON";
+		// 			}
+
+		// 			tools.createFile(namet, b);
+
+		// 		} else
+		// 		{
+		// 			byte b[] = new byte[1];
+
+		// 			b[0] = 0;
+
+		// 			tools.createFile(namet, b);
+		// 			playTutorial = true;
+		// 			stringTutorial = "ON";
+		// 		}
+
+		// 		String finalText = "Tutorial " + stringTutorial;
+
+		// 		settingsElem4.setText(finalText);
+		// 		settingsElem4.postInvalidate();
+		// 		settingsElem4.setBackgroundColor(0x00000000);
+
+		// 		settingsElem1.setOnSeekBarChangeListener(
+		// 				new SeekBar.OnSeekBarChangeListener()
+		// 				{
+
+		// 					public void onProgressChanged(SeekBar seekBar,
+		// 							int progress, boolean fromTouch)
+		// 					{
+		// 						setVolumen(progress);
+		// 					}
+
+		// 					public void onStartTrackingTouch(SeekBar seekBar)
+		// 					{
+		// 					}
+
+		// 					public void onStopTrackingTouch(SeekBar seekBar)
+		// 					{
+		// 					}
+		// 				});
+		// 		settingsElem2.setOnSeekBarChangeListener(
+		// 				new SeekBar.OnSeekBarChangeListener()
+		// 				{
+
+		// 					public void onProgressChanged(SeekBar seekBar,
+		// 							int progress, boolean fromTouch)
+		// 					{
+		// 						// mainTextSpeed=progress;
+		// 						factTimeToShow = 10 + (100 - progress);
+		// 					}
+
+		// 					public void onStartTrackingTouch(SeekBar seekBar)
+		// 					{
+		// 					}
+
+		// 					public void onStopTrackingTouch(SeekBar seekBar)
+		// 					{
+		// 					}
+		// 				});
+
+		// 		settingsElem3.setOnItemSelectedListener(new OnItemSelectedListener()
+		// 		{
+
+		// 			@Override
+		// 			public void onItemSelected(AdapterView<?> arg0, View arg1,
+		// 					int arg2, long arg3)
+		// 			{
+
+		// 				// trying to avoid undesired spinner selection changed
+		// 				// event, a known problem
+		// 				if (m_intSpinnerInitiCount < NO_OF_EVENTS)
+		// 				{
+		// 					m_intSpinnerInitiCount++;
+		// 				} else
+		// 				{
+		// 					whichLanguage = myLanguages[arg2].nInternal;
+		// 					whichLanguageInt = arg2;
+		// 					loadExtraTextsToTranslate();
+		// 					setScreen(whichScreen, dirAlfred);
+		// 					leeLibros();
+		// 					myoverlay.setCredits();
+		// 					loadTutorialText();
+		// 				}
+		// 			}
+
+		// 			@Override
+		// 			public void onNothingSelected(AdapterView<?> arg0)
+		// 			{
+
+		// 			}
+
+		// 		});
+
+		// 		// set up list view
+		// 		//////////////////
+		// 		saveLoadLayout = new LinearLayout(myContext);
+		// 		saveLoadLayout
+		// 				.setLayoutParams(new android.view.ViewGroup.LayoutParams(
+		// 						android.view.ViewGroup.LayoutParams.FILL_PARENT,
+		// 						android.view.ViewGroup.LayoutParams.FILL_PARENT));
+
+		// 		saveLoadListView = new ListView(myContext);
+
+		// 		saveLoadListView.setOnItemClickListener(new OnItemClickListener()
+		// 		{
+
+		// 			@Override
+		// 			public void onItemClick(AdapterView<?> arg0, View arg1,
+		// 					final int arg2, long arg3)
+		// 			{
+
+		// 				if (saveOrLoad == 0)// save
+		// 				{
+
+		// 					AlertDialog.Builder alert = new AlertDialog.Builder(
+		// 							myContext);
+
+		// 					alert.setTitle("Dreamtripper");
+		// 					alert.setMessage(extraThingsToTranslate[4]);
+		// 					alert.setIcon(R.drawable.alfred);
+		// 					final EditText input = new EditText(myContext);
+		// 					String pp = nombrePartidas[arg2];
+		// 					if (pp.compareTo(extraThingsToTranslate[7]) == 0)
+		// 						pp = "";
+		// 					input.setText(pp);
+		// 					alert.setView(input);
+
+		// 					alert.setPositiveButton("Ok",
+		// 							new DialogInterface.OnClickListener()
+		// 							{
+		// 								public void onClick(DialogInterface dialog,
+		// 										int whichButton)
+		// 								{
+		// 									grabaPartida(arg2,
+		// 											input.getText().toString());
+		// 									tools.showMessage(myContext,
+		// 											"Dreamtripper",
+		// 											extraThingsToTranslate[6],
+		// 											"OK");
+		// 									getNamesPartidas();
+		// 								}
+		// 							});
+
+		// 					alert.setNegativeButton("Cancel",
+		// 							new DialogInterface.OnClickListener()
+		// 							{
+		// 								public void onClick(DialogInterface dialog,
+		// 										int whichButton)
+		// 								{
+		// 								}
+		// 							});
+
+		// 					alert.show();
+
+		// 				} else
+		// 				{// load
+
+		// 					cargaPartida(arg2);
+
+		// 				}
+
+		// 			}
+
+		// 		});
+
+		// 		saveLoadLayout.addView(saveLoadListView,
+		// 				new android.view.ViewGroup.LayoutParams(
+		// 						android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
+		// 						android.view.ViewGroup.LayoutParams.WRAP_CONTENT));
+
+		// 		settingsLayout.addView(settingsElem1,
+		// 				new android.view.ViewGroup.LayoutParams(
+		// 						(int) ((float) 180 * factX),
+		// 						(int) ((float) 50 * factY)));
+
+		// 		settingsLayout.addView(settingsElem2,
+		// 				new android.view.ViewGroup.LayoutParams(
+		// 						(int) ((float) 180 * factX),
+		// 						(int) ((float) 50 * factY)));
+
+		// 		settingsLayout.addView(settingsElem3,
+		// 				new android.view.ViewGroup.LayoutParams(
+		// 						(int) ((float) 120 * factX),
+		// 						(int) ((float) 50 * factY)));
+
+		// 		settingsLayout.addView(settingsElem4,
+		// 				new android.view.ViewGroup.LayoutParams(
+		// 						(int) ((float) 130 * factX),
+		// 						(int) ((float) 50 * factY)));
+
+		// 		ViewGroup.MarginLayoutParams mlp2 = (ViewGroup.MarginLayoutParams) settingsElem1
+		// 				.getLayoutParams();
+		// 		mlp2.setMargins((int) ((float) 300 * factX),
+		// 				(int) ((float) 195 * factY),
+		// 				(int) ((float) (640 - 460) * factX),
+		// 				(int) ((float) (400 - 245) * factY));
+
+		// 		ViewGroup.MarginLayoutParams mlp3 = (ViewGroup.MarginLayoutParams) settingsElem2
+		// 				.getLayoutParams();
+		// 		mlp3.setMargins((int) ((float) 300 * factX),
+		// 				(int) ((float) 235 * factY),
+		// 				(int) ((float) (640 - 460) * factX),
+		// 				(int) ((float) (400 - 285) * factY));
+
+		// 		ViewGroup.MarginLayoutParams mlp4 = (ViewGroup.MarginLayoutParams) settingsElem3
+		// 				.getLayoutParams();
+		// 		mlp4.setMargins((int) ((float) 340 * factX),
+		// 				(int) ((float) 285 * factY),
+		// 				(int) ((float) (640 - 460) * factX),
+		// 				(int) ((float) (400 - 325) * factY));
+
+		// 		ViewGroup.MarginLayoutParams mlp5 = (ViewGroup.MarginLayoutParams) settingsElem4
+		// 				.getLayoutParams();
+		// 		mlp5.setMargins((int) ((float) 210 * factX),
+		// 				(int) ((float) 285 * factY),
+		// 				(int) ((float) (640 - 360) * factX),
+		// 				(int) ((float) (400 - 325) * factY));
+
+		// 		ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) saveLoadListView
+		// 				.getLayoutParams();
+		// 		mlp.setMargins((int) ((float) 223 * factX),
+		// 				(int) ((float) 200 * factY),
+		// 				(int) ((float) (640 - 466) * factX),
+		// 				(int) ((float) (400 - 312) * factY));
+
+		// 		settingsElem1.setProgress(mainVolumen);
+		// 		settingsElem2.setProgress(mainTextSpeed);
+
+		// 		this.addContentView(saveLoadLayout,
+		// 				new android.view.ViewGroup.LayoutParams(
+		// 						android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
+		// 						android.view.ViewGroup.LayoutParams.WRAP_CONTENT));
+
+		// 		this.addContentView(settingsLayout,
+		// 				new android.view.ViewGroup.LayoutParams(
+		// 						android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
+		// 						android.view.ViewGroup.LayoutParams.WRAP_CONTENT));
+
+		// 		settingsElem4.setOnClickListener(new OnClickListener()
+		// 		{
+
+		// 			@Override
+		// 			public void onClick(View arg0)
+		// 			{
+
+		// 				String stringTutorial = "";
+		// 				String namet = AlfredActivity.destinationFolder
+		// 						+ "/tutorial.kk";
+
+		// 				byte b[] = tools.readFile(namet);
+		// 				if (playTutorial == false)// (b[0]==0)
+		// 				{
+		// 					b[0] = 0;
+		// 					playTutorial = true;
+		// 					stringTutorial = "ON";
+
+		// 					tutorial.init();
+
+		// 				} else
+		// 				{
+		// 					b[0] = 4;
+		// 					playTutorial = false;
+		// 					stringTutorial = "OFF";
+		// 				}
+
+		// 				tools.createFile(namet, b);
+
+		// 				String fiinalText = "Tutorial " + stringTutorial;
+
+		// 				settingsElem4.setText(fiinalText);
+		// 				settingsElem4.postInvalidate();
+
+		// 			}
+
+		// 		});
+
+		// 		hideList();
+		// 		hideSettings();
+
+		// 		myExtraText = new extraText();
+
+		// 		String name = AlfredActivity.destinationFolder + "/test.kk";
+		// 		File f = new File(name);
+		// 		if (f.exists() == true)
+		// 		{
+		// 			byte b[] = tools.readFile(name);
+
+		// 			if (b[0] < 3)
+		// 			{
+		// 				b[0]++;
+		// 				playIntro = true;
+		// 			} else
+		// 				playIntro = false;
+
+		// 			tools.createFile(name, b);
+
+		// 		} else
+		// 		{
+		// 			byte b[] = new byte[1];
+
+		// 			b[0] = 0;
+		// 			tools.createFile(name, b);
+		// 			playIntro = true;
+		// 		}
+
+		// 		// playIntro=true;
+		// 		if (playIntro == false)
+		// 		{
+		// 			stateGame = GAME;
+		// 		} else
+		// 		{
+
+		// 			stateGame = INTRO;
+		// 			varCheckRealTime = 0;
+		// 			getWindow().setFormat(PixelFormat.TRANSLUCENT);
+		// 			videoHolder = new myVideoView(this);
+
+		// 			videoHolder.setWH(widthScreen, heightScreen);
+
+		// 			// videoHolder.setLayoutParams(params)
+
+		// 			Uri video = Uri.parse("android.resource://" + getPackageName()
+		// 					+ "/" + R.raw.intro2); // do not add any extension
+		// 			videoHolder.setVideoURI(video);
+
+		// 			LinearLayout.LayoutParams paramsVideo = new LinearLayout.LayoutParams(
+		// 					widthScreen, heightScreen);
+
+		// 			videoOverlay vo = new videoOverlay(myContext, assetManager,
+		// 					videoHolder);
+
+		// 			this.addContentView(videoHolder, paramsVideo);// new
+		// 															// android.view.ViewGroup.LayoutParams(paramsVideo));
+
+		// 			this.addContentView(vo, paramsVideo);// new
+		// 													// android.view.ViewGroup.LayoutParams(paramsVideo));
+
+		// 			// superTime=SystemClock.uptimeMillis();
+		// 			videoHolder.start();
+
+		// 			AlfredActivity.playTrack(22, false);
+
+		// 			videoHolder.setOnTouchListener(new OnTouchListener()
+		// 			{
+		// 				@Override
+		// 				public boolean onTouch(View arg0, MotionEvent arg1)
+		// 				{
+		// 					videoHolder.setVisibility(View.GONE);
+		// 					videoHolder.stopPlayback();
+
+		// 					AlfredActivity.myHandler
+		// 							.sendEmptyMessage(AlfredActivity.INTRO_END);
+
+		// 					return false;
+		// 				}
+
+		// 			});
+
+		// 			videoHolder.setOnCompletionListener(new OnCompletionListener()
+		// 			{
+
+		// 				@Override
+		// 				public void onCompletion(MediaPlayer arg0)
+		// 				{
+		// 					wait2(4000);
+		// 				}
+
+		// 			});
+
+		// 		}
+	}
+}
+
+void PelrockEngine::playIntro() {
+}
+
+void PelrockEngine::loadAnims() {
+	loadMainCharacterAnims();
+	// try
+	// {
+
+	// 	// andar, acciones y hablar...
+	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred1.png");
+
+	// 	for (int i = 0; i < 60; i++)
+	// 	{
+	// 		alfredBitmap[i] = Bitmap.createBitmap(mainAlfredAnim, i * 51, 0,
+	// 				51, 102);
+	// 	}
+
+	// 	mainAlfredAnim.recycle();
+
+	// 	// andar por el tunel
+	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred13.png");
+
+	// 	for (int i = 0; i < 18; i++)
+	// 	{
+	// 		alfredBitmap2[i] = Bitmap.createBitmap(mainAlfredAnim, i * 130,
+	// 				0, 130, 55);
+	// 	}
+
+	// 	mainAlfredAnim.recycle();
+
+	// 	// animacion de peinarse mirando a la izquierda...
+	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred2.png");
+
+	// 	for (int i = 0; i < 11; i++)
+	// 		alfredBitmapExtra1[i] = Bitmap.createBitmap(mainAlfredAnim,
+	// 				i * 51, 0, 51, 102);
+
+	// 	mainAlfredAnim.recycle();
+
+	// 	// animacion de peinarse mirando a la derecha...
+	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred3.png");
+
+	// 	for (int i = 0; i < 11; i++)
+	// 		alfredBitmapExtra2[i] = Bitmap.createBitmap(mainAlfredAnim,
+	// 				i * 51, 0, 51, 102);
+
+	// 	mainAlfredAnim.recycle();
+
+	// 	// animacion de leer...
+	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred4.png");
+
+	// 	for (int i = 0; i < 10; i++)
+	// 		alfredBitmapExtra3[i] = Bitmap.createBitmap(mainAlfredAnim,
+	// 				i * 51, 0, 51, 102);
+
+	// 	// animacion de electrocutarse...
+	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred5.png");
+
+	// 	for (int i = 0; i < 8; i++)
+	// 		alfredBitmapExtra4[i] = Bitmap.createBitmap(mainAlfredAnim,
+	// 				i * 82, 0, 82, 58);
+
+	// 	// animacion de cocodrilo...
+	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/crocodillo.png");
+
+	// 	for (int i = 0; i < 14; i++)
+	// 		alfredBitmapExtra5[i] = Bitmap.createBitmap(mainAlfredAnim,
+	// 				i * 171, 0, 171, 109);
+
+	// 	// animacion de escondite...
+	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred6.png");
+
+	// 	for (int i = 0; i < 12; i++)
+	// 		alfredBitmapExtra6[i] = Bitmap.createBitmap(mainAlfredAnim,
+	// 				i * 113, 0, 113, 103);
+
+	// 	// animacion de bajada al tunel...
+	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred7.png");
+
+	// 	for (int i = 0; i < 11; i++)
+	// 		alfredBitmapExtra7[i] = Bitmap.createBitmap(mainAlfredAnim,
+	// 				i * 33, 0, 33, 72);
+
+	// 	// animacion de subida del tunel...
+	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred8.png");
+
+	// 	for (int i = 0; i < 9; i++)
+	// 		alfredBitmapExtra8[i] = Bitmap.createBitmap(mainAlfredAnim,
+	// 				i * 33, 0, 33, 72);
+
+	// 	// animacion de escapada del tunel (llega al zoco)
+	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred9.png");
+
+	// 	for (int i = 0; i < 16; i++)
+	// 		alfredBitmapExtra9[i] = Bitmap.createBitmap(mainAlfredAnim,
+	// 				i * 158, 0, 158, 115);
+
+	// 	// animacion de puesta del muneco hinchable
+	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred10.png");
+
+	// 	for (int i = 0; i < 17; i++)
+	// 		alfredBitmapExtra10[i] = Bitmap.createBitmap(mainAlfredAnim,
+	// 				i * 177, 0, 177, 124);
+
+	// 	// animacion de magia (1)
+	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/magia1.png");
+
+	// 	for (int i = 0; i < 11; i++)
+	// 		alfredBitmapExtra11[i] = Bitmap.createBitmap(mainAlfredAnim,
+	// 				i * 98, 0, 98, 138);
+
+	// 	// animacion de magia (2)
+	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/magia2.png");
+
+	// 	for (int i = 0; i < 12; i++)
+	// 		alfredBitmapExtra12[i] = Bitmap.createBitmap(mainAlfredAnim,
+	// 				i * 98, 0, 98, 138);
+
+	// 	// animacion de pedras
+	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred11.png");
+
+	// 	for (int i = 0; i < 7; i++)
+	// 		alfredBitmapExtra13[i] = Bitmap.createBitmap(mainAlfredAnim,
+	// 				i * 208, 0, 208, 102);
+
+	// 	// animacion de desnudo
+	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred12.png");
+
+	// 	for (int i = 0; i < 4; i++)
+	// 		alfredBitmapExtra14[i] = Bitmap.createBitmap(mainAlfredAnim,
+	// 				i * 51, 0, 51, 102);
+
+	// 	// animacion de despertarse
+	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred14.png");
+
+	// 	for (int i = 0; i < 14; i++)
+	// 		alfredBitmapExtra15[i] = Bitmap.createBitmap(mainAlfredAnim,
+	// 				i * 71, 0, 71, 66);
+
+	// 	// animacion de tirar piedra
+	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred15.png");
+
+	// 	for (int i = 0; i < 4; i++)
+	// 		alfredBitmapExtra16[i] = Bitmap.createBitmap(mainAlfredAnim,
+	// 				i * 71, 0, 71, 101);
+
+	// 	mainAlfredAnim.recycle();
+
+	// } catch (IOException e)
+	// {
+	// }
+}
+
+const int EXPECTED_SIZE = 640 * 400;
+size_t decompress_rle_block(const uint8_t *data, size_t data_size, uint32_t offset, uint32_t size, uint8_t **out_data) {
+	// Check for uncompressed markers
+	if (size == 0x8000 || size == 0x6800) {
+		*out_data = (uint8_t *)malloc(size);
+		memcpy(*out_data, data + offset, size);
+		return size;
+	}
+
+	// RLE compressed
+	*out_data = (uint8_t *)malloc(EXPECTED_SIZE * 2); // Allocate enough space
+	size_t result_size = 0;
+
+	uint32_t pos = offset;
+	uint32_t end = offset + size;
+
+	while (pos + 2 <= end && pos + 2 <= data_size) {
+		// Check for BUDA marker
+		if (pos + 4 <= data_size &&
+			data[pos] == 'B' && data[pos + 1] == 'U' &&
+			data[pos + 2] == 'D' && data[pos + 3] == 'A') {
+			break;
+		}
+
+		uint8_t count = data[pos];
+		uint8_t value = data[pos + 1];
+
+		for (int i = 0; i < count; i++) {
+			(*out_data)[result_size++] = value;
+		}
+
+		pos += 2;
+	}
+
+	return result_size;
+}
+
+void PelrockEngine::getPalette(Common::File *roomFile, int roomOffset, byte *palette) {
+	// get palette
+	int paletteOffset = roomOffset + (11 * 8);
+	roomFile->seek(paletteOffset, SEEK_SET);
+	uint32 offset = roomFile->readUint32LE();
+	uint32 size = roomFile->readUint32LE();
+
+	roomFile->seek(offset, SEEK_SET);
+
+	roomFile->read(palette, size);
+	for (int i = 0; i < 256; i++) {
+		palette[i * 3] = palette[i * 3] << 2;
+		palette[i * 3 + 1] = palette[i * 3 + 1] << 2;
+		palette[i * 3 + 2] = palette[i * 3 + 2] << 2;
+	}
+
+}
+
+void PelrockEngine::getBackground(Common::File *roomFile, int roomOffset, byte *background) {
+	roomFile->seek(0, SEEK_SET);
+	// get screen
+	size_t combined_size = 0;
+	size_t uncompressed_size = 0;
+	for (int pair_idx = 0; pair_idx < 8; pair_idx++) {
+		uint32_t pair_offset = roomOffset + (pair_idx * 8);
+		if (pair_offset + 8 > roomFile->size())
+			continue;
+
+		roomFile->seek(pair_offset, SEEK_SET);
+		uint32_t offset = roomFile->readUint32LE();
+		uint32_t size = roomFile->readUint32LE();
+		uncompressed_size += size;
+
+		if (offset > 0 && size > 0 && offset < roomFile->size()) {
+			byte *data = new byte[size];
+			roomFile->seek(offset, SEEK_SET);
+			roomFile->read(data, size);
+			uint8_t *block_data = NULL;
+			size_t block_size = decompress_rle_block(data, size, 0, size, &block_data);
+
+			memcpy(background + combined_size, block_data, block_size);
+			combined_size += block_size + 1;
+			free(block_data);
+			delete[] data;
+		}
+	}
+}
+
+void PelrockEngine::loadMainCharacterAnims() {
+	Common::File alfred3;
+	if (!alfred3.open(Common::Path("ALFRED.3"))) {
+		error("Could not open ALFRED.3");
+		return;
+	}
+	int alfred3Size = alfred3.size();
+	unsigned char *bufferFile = (unsigned char *)malloc(alfred3Size);
+	alfred3.seek(0, SEEK_SET);
+	alfred3.read(bufferFile, alfred3Size);
+	alfred3.close();
+
+	int index = 0;
+	int index3 = 0;
+	uint32_t capacity = 3060 * 102;
+	unsigned char *pic = new unsigned char[capacity];
+	decompress_rle_block(bufferFile, alfred3Size, 0, alfred3Size, &pic);
+	memcpy(standingAnim, pic, 3060 * 102);
+}
+
+void PelrockEngine::frames() {
+	for (uint32_t y = 0; y < kAlfredFrameHeight; y++) {
+		for (uint32_t x = 0; x < kAlfredFrameWidth; x++) {
+			unsigned int src_pos = (curAlfredFrame * kAlfredFrameHeight * kAlfredFrameWidth) + (y * kAlfredFrameWidth) + x;
+			if(standingAnim[src_pos] != 255)
+				_screen->setPixel(x + xAlfred, y + yAlfred, standingAnim[src_pos]);
+		}
+	}
+	_screen->markAllDirty();
+	_screen->update();
+}
+
+void PelrockEngine::setScreen(int number, int dir) {
+
+	Common::File roomFile;
+	if (!roomFile.open(Common::Path("ALFRED.1"))) {
+		error("Could not open ALFRED.1");
+		return;
+	}
+
+
+	int roomOffset = number * kRoomStructSize;
+
+	byte *palette = new byte[256 * 3];
+	getPalette(&roomFile, roomOffset, palette);
+
+	int paletteOffset = roomOffset + (11 * 8);
+	roomFile.seek(paletteOffset, SEEK_SET);
+	uint32 offset = roomFile.readUint32LE();
+
+	g_system->getPaletteManager()->setPalette(palette, 0, 256);
+
+	byte *background = new byte[640 * 400];
+	getBackground(&roomFile, roomOffset, background);
+	for (int i = 0; i < 640; i++) {
+		for (int j = 0; j < 400; j++) {
+			_screen->setPixel(i, j, background[j * 640 + i]);
+		}
+	}
+	_screen->markAllDirty();
+	roomFile.close();
+	delete[] background;
+	delete[] palette;
+}
+
+void PelrockEngine::setScreenJava(int s, int dir) {
+	screenReady = false;
+	dirAlfred = dir;
+
+	// 		vueltaALaCarcel = false;
+	// 		timeToGoToPiss = -1;
+	// 		myoverlay.shakeScreen = false;
+	// 		walkingAgain = false;
+
+	// 		myoverlay.indexCicleColores = 0;
+
+	// 		stringToShowOnTutorial = "";
+
+	// 		magicWords = false;
+
+	// 		myMovingAlfredThread.isOtherTalking = false;
+	// 		isPersonajeTalking = -1;
+	// 		myMovingAlfredThread.showTextNowOtros = false;
+
+	// 		// pantalla................
+	// 		if (screenBitmap != null)
+	// 		{
+	// 			screenBitmap.recycle();
+	// 			screenBitmap = null;
+	// 		}
+
+	prevWhichScreen = whichScreen;
+
+	whichScreen = s;
+
+	// 		objetos.getObjectList(assetManager, s);
+	// 		objectToShow = "";
+
+	Common::String nameScreen = Common::String::format("pantallas/pantalla%d.png", s);
+	Common::String nameCamino = Common::String::format("caminos/pantalla%d.txt", s);
+
+	Common::File screen;
+	if (!screen.open(Common::Path(nameScreen))) {
+		error("Could not find pantalla!");
+	}
+	decoder->loadStream(screen);
+	Graphics::Palette palette = decoder->getPalette();
+	g_system->getPaletteManager()->setPalette(palette);
+	const Graphics::Surface *surf = decoder->getSurface();
+	if (!surf)
+		error("No surface");
+
+	g_engine->_screen->blitFrom(*surf);
+
+	_screen->markAllDirty();
+
+	// 		Config c = null;
+	// 		try
+	// 		{
+	// 			screenBitmap = getBitmapFromAsset(nameScreen);
+
+	// 			c = screenBitmap.getConfig();
+	// 			screenBitmap = screenBitmap.copy(c, true);
+	// 		} catch (IOException e)
+	// 		{
+	// 		}
+
+	Common::String nameColorsx = Common::String::format("colors%d.png", s);
+	Common::String nameColors = "colors/" + nameColorsx;
+	// 		try
+	// 		{
+	// 			screenColors = getBitmapFromAsset(nameColors);
+	// 		} catch (IOException e)
+	// 		{
+	// 			screenColors = null;
+	// 		}
+	// 		if (screenColors != null)
+	// 			setFakePalette(nameColorsx, screenColors);
+
+	Common::String nameShadows = Common::String::format("shadows/shadows_%d.png", s); // <-- fixed: include s
+	Common::File shadows;
+	if (shadows.open(Common::Path(nameShadows)) != Common::kNoError) {
+		error("Error opening shadows: %s", nameShadows.c_str());
+		return;
+	}
+	if (decoder->loadStream(shadows) != Common::kNoError) {
+		error("Decoder failed loading shadows: %s", nameShadows.c_str());
+		return;
+	}
+	const Graphics::Surface *shadowSurf = decoder->getSurface();
+	if (!shadowSurf) {
+		error("No shadow surface for %s", nameShadows.c_str());
+		return;
+	}
+	Common::copy((byte *)shadowSurf->getPixels(), (byte *)shadowSurf->getPixels() + 640 * 400, pixelsShadows);
+
+	// 		try
+	// 		{
+	// 			shadowColors = getBitmapFromAsset(nameShadows);
+	// 		} catch (IOException e)
+	// 		{
+	// 			shadowColors = null;
+	// 		}
+
+	// 		shadowColors.getPixels(pixelsShadows, 0, 640, 0, 0, 640, 400);
+
+	Common::File caminos;
+	if (!caminos.open(Common::Path(nameCamino))) {
+		error("Cant find camino");
+	}
+
+	// 		// caminos................
+	// 		BufferedReader br = null;
+	// 		List<String> wordList = new ArrayList<String>();
+
+	// 		try
+	// 		{
+	// 			br = new BufferedReader(
+	// 					new InputStreamReader(assetManager.open(nameCamino)));
+	// 			String word;
+	// 			while ((word = br.readLine()) != null)
+	// 				wordList.add(word);
+	// 		} catch (IOException e)
+	// 		{
+	// 		}
+
+	// 		paths.caminos = new defCam();
+
+	// 		int k = wordList.size();
+
+	// 		List<rectCam> provList = new LinkedList<rectCam>();
+	// 		provList.clear();
+
+	// 		List<conex> provListCon = new LinkedList<conex>();
+	// 		provListCon.clear();
+
+	// 		List<anims> provListAnims = new LinkedList<anims>();
+	// 		provListAnims.clear();
+
+	// 		myScale = null;
+
+	// 		scale aux4 = null;
+
+	// 		String line;
+	// 		int ind = -1;
+	// 		int prevInd = 0;
+	// 		int which = 0;
+
+	// 		for (int i = 0; i < k; i++)
+	// 		{
+
+	// 			line = wordList.get(i);
+
+	// 			if (line.compareTo("paths") == 0)
+	// 				which = 0;
+	// 			else if (line.compareTo("conexions") == 0)
+	// 				which = 1;
+	// 			else if (line.compareTo("anims") == 0)
+	// 				which = 2;
+	// 			else if (line.compareTo("scale") == 0)
+	// 				which = 3;
+	// 			else
+	// 			{
+
+	// 				switch (which)
+	// 				{
+	// 				case 0:
+
+	// 					rectCam aux = new rectCam();
+
+	// 					ind = line.indexOf(',', ind + 1);
+	// 					aux.x = (int) (Float
+	// 							.parseFloat(line.substring(prevInd, ind)) * factX);
+	// 					prevInd = ind + 1;
+
+	// 					ind = line.indexOf(',', ind + 1);
+	// 					aux.y = (int) (Float
+	// 							.parseFloat(line.substring(prevInd, ind)) * factY);
+	// 					prevInd = ind + 1;
+
+	// 					ind = line.indexOf(',', ind + 1);
+	// 					aux.w = (int) (Float
+	// 							.parseFloat(line.substring(prevInd, ind)) * factX);
+	// 					prevInd = ind + 1;
+
+	// 					ind = line.indexOf(',', ind + 1);
+	// 					aux.h = (int) (Float.parseFloat(line.substring(prevInd))
+	// 							* factY);
+	// 					prevInd = ind + 1;
+
+	// 					ind = -1;
+	// 					prevInd = 0;
+
+	// 					provList.add(aux);
+
+	// 				break;
+
+	// 				case 1:
+
+	// 					conex aux2 = new conex();
+
+	// 					ind = line.indexOf(',', ind + 1);
+	// 					aux2.x = (int) (Float
+	// 							.parseFloat(line.substring(prevInd, ind)) * factX);
+	// 					prevInd = ind + 1;
+
+	// 					ind = line.indexOf(',', ind + 1);
+	// 					aux2.y = (int) (Float
+	// 							.parseFloat(line.substring(prevInd, ind)) * factY);
+	// 					prevInd = ind + 1;
+
+	// 					ind = line.indexOf(',', ind + 1);
+	// 					aux2.which = (int) (Integer
+	// 							.parseInt(line.substring(prevInd, ind)));
+	// 					prevInd = ind + 1;
+
+	// 					ind = line.indexOf(',', ind + 1);
+	// 					aux2.ready = (int) (Integer
+	// 							.parseInt(line.substring(prevInd, ind)));
+	// 					prevInd = ind + 1;
+
+	// 					ind = line.indexOf(',', ind + 1);
+	// 					aux2.nx = (int) (Float
+	// 							.parseFloat(line.substring(prevInd, ind)) * factX);
+	// 					prevInd = ind + 1;
+
+	// 					ind = line.indexOf(',', ind + 1);
+	// 					aux2.ny = (int) (Float
+	// 							.parseFloat(line.substring(prevInd, ind)) * factY);
+	// 					prevInd = ind + 1;
+
+	// 					ind = line.indexOf(',', ind + 1);
+	// 					aux2.ndir = (int) (Integer
+	// 							.parseInt(line.substring(prevInd)));
+	// 					prevInd = ind + 1;
+
+	// 					ind = -1;
+	// 					prevInd = 0;
+
+	// 					provListCon.add(aux2);
+	// 				break;
+
+	// 				case 2:
+
+	// 					anims aux3 = new anims();
+
+	// 					aux3.activated = true;
+
+	// 					ind = line.indexOf(',', ind + 1);
+	// 					aux3.x = (int) (Integer
+	// 							.parseInt(line.substring(prevInd, ind)) * factX);
+	// 					aux3.backupX = aux3.x;
+	// 					prevInd = ind + 1;
+
+	// 					ind = line.indexOf(',', ind + 1);
+	// 					aux3.y = (int) (Integer
+	// 							.parseInt(line.substring(prevInd, ind)) * factY);
+	// 					aux3.backupY = aux3.y;
+	// 					prevInd = ind + 1;
+
+	// 					ind = line.indexOf(',', ind + 1);
+	// 					aux3.moving = (boolean) (Boolean
+	// 							.parseBoolean((line.substring(prevInd, ind))));
+	// 					prevInd = ind + 1;
+
+	// 					ind = line.indexOf(',', ind + 1);
+	// 					aux3.frames = (int) (Integer
+	// 							.parseInt(line.substring(prevInd, ind)));
+	// 					prevInd = ind + 1;
+
+	// 					ind = line.indexOf(',', ind + 1);
+	// 					if (ind == -1)
+	// 						aux3.speed = (int) (Integer
+	// 								.parseInt(line.substring(prevInd)));
+	// 					else
+	// 						aux3.speed = (int) (Integer
+	// 								.parseInt(line.substring(prevInd, ind)));
+	// 					prevInd = ind + 1;
+
+	// 					aux3.whichFrame = 0;
+	// 					aux3.nSpeed = 0;
+
+	// 					if (ind != -1)// otros datos en las animaciones...
+	// 					{
+	// 						int kk;
+	// 						if (aux3.moving == false)// los datos extras son de
+	// 													// animaciones 'combinadas'
+	// 						{
+
+	// 							ind = line.indexOf(',', ind + 1);
+	// 							kk = (int) (Integer
+	// 									.parseInt(line.substring(prevInd, ind)));
+	// 							prevInd = ind + 1;
+
+	// 							aux3.animParts = new parts[kk];
+	// 							aux3.time = 0;
+	// 							aux3.whichPart = 0;
+
+	// 							for (int j = 0; j < kk; j++)
+	// 							{
+	// 								aux3.animParts[j] = new parts();
+
+	// 								ind = line.indexOf(',', ind + 1);
+	// 								aux3.animParts[j].nframes = (int) (Integer
+	// 										.parseInt(
+	// 												line.substring(prevInd, ind)));
+	// 								prevInd = ind + 1;
+
+	// 								ind = line.indexOf(',', ind + 1);
+	// 								if (ind == -1)
+	// 									aux3.animParts[j].duration = (int) (Integer
+	// 											.parseInt(line.substring(prevInd)));
+	// 								else
+	// 									aux3.animParts[j].duration = (int) (Integer
+	// 											.parseInt(line.substring(prevInd,
+	// 													ind)));
+
+	// 								prevInd = ind + 1;
+
+	// 							}
+
+	// 						} else
+	// 						{
+
+	// 							ind = line.indexOf(',', ind + 1);
+	// 							kk = (int) (Integer
+	// 									.parseInt(line.substring(prevInd, ind)));
+	// 							prevInd = ind + 1;
+
+	// 							aux3.incAnims = new movements[kk];
+
+	// 							aux3.whichMovement = 0;
+	// 							aux3.isMovingAlready = false;
+
+	// 							ind = line.indexOf(',', ind + 1);
+	// 							aux3.startMoving = (int) (Integer
+	// 									.parseInt(line.substring(prevInd, ind)));
+	// 							prevInd = ind + 1;
+
+	// 							for (int j = 0; j < kk; j++)
+	// 							{
+	// 								aux3.incAnims[j] = new movements();
+
+	// 								ind = line.indexOf(',', ind + 1);
+	// 								aux3.incAnims[j].ix = (float) (Float.parseFloat(
+	// 										line.substring(prevInd, ind)));
+	// 								prevInd = ind + 1;
+
+	// 								ind = line.indexOf(',', ind + 1);
+	// 								aux3.incAnims[j].iy = (float) (Float.parseFloat(
+	// 										line.substring(prevInd, ind)));
+	// 								prevInd = ind + 1;
+
+	// 								ind = line.indexOf(',', ind + 1);
+	// 								if (ind == -1)
+	// 									aux3.incAnims[j].duration = (int) (Integer
+	// 											.parseInt(line.substring(prevInd)));
+	// 								else
+	// 									aux3.incAnims[j].duration = (int) (Integer
+	// 											.parseInt(line.substring(prevInd,
+	// 													ind)));
+	// 								prevInd = ind + 1;
+
+	// 								aux3.incAnims[j].nduration = 0;
+
+	// 							}
+
+	// 						}
+
+	// 					}
+
+	// 					ind = -1;
+	// 					prevInd = 0;
+
+	// 					provListAnims.add(aux3);
+
+	// 				break;
+
+	// 				case 3:
+
+	// 					aux4 = new scale();
+
+	// 					ind = line.indexOf(',', ind + 1);
+	// 					aux4.miny = (int) (Float
+	// 							.parseFloat(line.substring(prevInd, ind)) * factY);
+	// 					prevInd = ind + 1;
+
+	// 					ind = line.indexOf(',', ind + 1);
+	// 					aux4.factmin = (int) (Integer
+	// 							.parseInt(line.substring(prevInd, ind)));
+	// 					prevInd = ind + 1;
+
+	// 					ind = line.indexOf(',', ind + 1);
+	// 					aux4.maxy = (int) (Float
+	// 							.parseFloat(line.substring(prevInd, ind)) * factY);
+	// 					prevInd = ind + 1;
+
+	// 					ind = line.indexOf(',', ind + 1);
+	// 					aux4.factmax = (int) (Integer
+	// 							.parseInt(line.substring(prevInd)));
+	// 					prevInd = ind + 1;
+
+	// 					prevInd += 0;
+	// 				break;
+	// 				}
+
+	// 			}
+
+	// 		}
+
+	// 		// myAnims
+
+	// 		int realPaths = provList.size();
+
+	// 		paths.caminos.cams = new rectCam[realPaths];
+
+	// 		int t;
+	// 		for (t = 0; t < realPaths; t++)
+	// 		{
+	// 			paths.caminos.cams[t] = new rectCam();
+	// 			paths.caminos.cams[t] = provList.get(t);
+	// 		}
+
+	// 		int realConex = provListCon.size();
+
+	// 		myConex = new conex[realConex];
+	// 		for (t = 0; t < realConex; t++)
+	// 		{
+	// 			myConex[t] = new conex();
+	// 			myConex[t] = provListCon.get(t);
+	// 		}
+
+	// 		int realAnims = provListAnims.size();
+
+	// 		bitmapAnims = new Bitmap[realAnims];
+
+	// 		try
+	// 		{
+	// 			myAnims = new anims[realAnims];
+	// 			for (t = 0; t < realAnims; t++)
+	// 			{
+
+	// 				myAnims[t] = new anims();
+	// 				myAnims[t] = provListAnims.get(t);
+
+	// 				bitmapAnims[t] = getBitmapFromAsset(
+	// 						"animaciones/anim" + String.valueOf(whichScreen)
+	// 								+ String.valueOf(t) + ".png");
+
+	// 				myAnims[t].realWFrame = bitmapAnims[t].getWidth();
+	// 				myAnims[t].realHFrame = bitmapAnims[t].getHeight();
+
+	// 				myAnims[t].w = (int) ((float) bitmapAnims[t].getWidth()
+	// 						* factX);
+	// 				myAnims[t].h = (int) ((float) bitmapAnims[t].getHeight()
+	// 						* factY);
+
+	// 				myAnims[t].predOrder = t;
+	// 			}
+	// 		} catch (Exception e)
+	// 		{
+	// 		}
+
+	// 		for (int i = 0; i < realPaths; i++)
+	// 		{
+	// 			paths.caminos.cams[i].vecinos = new LinkedList<Integer>();
+	// 			paths.caminos.cams[i].index = i;
+	// 			paths.caminos.cams[i].marked = false;
+
+	// 			for (int j = 0; j < realPaths; j++)
+	// 			{
+	// 				if ((i != j) && (paths.esVecino(i, j)))
+	// 					paths.caminos.cams[i].vecinos.add(j);
+	// 			}
+	// 		}
+
+	// 		if (aux4 != null)
+	// 		{
+	// 			myScale = aux4;
+	// 		}
+
+	// 		checkPegatinas(whichScreen);
+	// 		checkObjects(whichScreen);
+
+	// 		myConversaciones.loadConversations(whichScreen);
+
+	// 		// a ver si alguna conversacion esta cambiada
+	// 		if (myConversaciones.mytalkingAnims[whichScreen].myPersonajes != null)
+	// 		{
+
+	// 			int x = myConversaciones.mytalkingAnims[whichScreen].myPersonajes.length;
+
+	// 			for (int i = 0; i < x; i++)
+	// 				myConversaciones.mytalkingAnims[whichScreen].myPersonajes[i].currentConversationEach = 0;
+
+	// 		}
+
+	// 		int n = myListChangesConvs.size();
+	// 		for (int i = 0; i < n; i++)
+	// 		{
+	// 			changesConvs aux = new changesConvs();
+	// 			aux = myListChangesConvs.get(i);
+
+	// 			if (whichScreen == aux.screen)
+	// 			{
+	// 				myConversaciones.mytalkingAnims[whichScreen].myPersonajes[aux.which].currentConversationEach = aux.n;// .setConversation(whichScreen,
+	// 																														// aux.which,
+	// 																														// aux.n);
+	// 			}
+	// 		}
+
+	// 		myoverlay.isFadedOut = false;
+
+	// 		// a ver si se ha desactivado alguna animacion
+	// 		n = myListDesactAnims.size();
+
+	// 		for (int i = 0; i < n; i++)
+	// 		{
+	// 			dactAnims aux = new dactAnims();
+	// 			aux = myListDesactAnims.get(i);
+
+	// 			if (whichScreen == aux.screen)
+	// 				myAnims[aux.which].activated = false;
+	// 		}
+
+	// 		// personajes pe[];
+	// 		personajes pe[] = AlfredActivity.myConversaciones.mytalkingAnims[whichScreen].myPersonajes;
+	// 		// lista de cosas sueltas a hacer...
+
+	// 		pintaPeriodicoDandoVueltas = false;
+	// 		int j;
+	// 		switch (whichScreen)
+	// 		{
+
+	// 		case 51:
+	// 			desactivaAnims(51, 0);
+	// 			desactivaAnims(51, myAnims.length - 1);
+	// 			resetDiosesTime();
+	// 		break;
+
+	// 		case 52:
+	// 			desactivaAnims(52, 0);
+	// 			desactivaAnims(52, 1);
+	// 			desactivaAnims(52, 2);
+	// 			desactivaAnims(52, 3);
+	// 			desactivaAnims(52, 4);
+	// 			desactivaAnims(52, 5);
+	// 			ponPegatina(52, 145);
+	// 			desactivaAnims(52, myAnims.length - 1);
+	// 			resetDiosesTime();
+	// 		break;
+
+	// 		case 53:
+	// 			desactivaAnims(53, 0);
+	// 			desactivaAnims(53, myAnims.length - 1);
+	// 			resetDiosesTime();
+	// 		break;
+
+	// 		case 54:
+	// 			desactivaAnims(54, 0);
+	// 			desactivaAnims(54, myAnims.length - 1);
+	// 			resetDiosesTime();
+	// 		break;
+
+	// 		case 0:
+	// 			if ((readExtraVariable(VUELTA_A_EMPEZAR) == 1)
+	// 					&& (readExtraVariable(FROM_INTRO) == 0))
+	// 			{
+	// 				setExtraVariables(AlfredActivity.VUELTA_A_EMPEZAR, 2);
+	// 				myoverlay.setAnimExtra(14, 15, 21, 71, 66, 0, 0);
+	// 				myMovingAlfredThread.showTextNow = true;
+	// 				whichObject = 0;
+	// 				textToShow = extraThingsToTranslate[117];
+
+	// 			}
+
+	// 		break;
+
+	// 		case 48:
+
+	// 			AlfredActivity.myMovingAlfredThread.saySomethingOther(
+	// 					AlfredActivity.extraThingsToTranslate[115], 0);
+	// 			setExtraVariables(AlfredActivity.A_POR_LA_PRINCESA, 1);
+
+	// 			desactivaAnims(48, 7);
+
+	// 		break;
+
+	// 		case 44:
+
+	// 			if (readExtraVariable(AlfredActivity.PIEDRA_FAKE_MOJADA) == 3)
+	// 				updateConexion(55, 1);
+
+	// 		break;
+
+	// 		case 41:
+	// 			j = getOrderAnims(2);
+	// 			myAnims[j].activated = false;
+
+	// 			j = getOrderAnims(3);
+	// 			myAnims[j].activated = false;
+
+	// 			if (readExtraVariable(AlfredActivity.GUARDIAS_BORRACHOS) == 1)
+	// 				updateConexion(43, 1);
+
+	// 		break;
+
+	// 		case 40:
+
+	// 			finalObject = 0;
+	// 			whichObject = 0;
+	// 			actionToDo = 1;
+	// 			myMovingAlfredThread.stateOfTalking = 0;
+	// 			gotoPos(290, 370, 3);
+	// 			whichAction = objetos.HABLAR;
+	// 			afterMovingAlfred = ALFRED_TALKING;
+	// 			posibilityOfFakeAnswer = false;
+	// 			varCheckRealTime = -1;
+
+	// 		break;
+
+	// 		case 39:
+
+	// 			j = getOrderAnims(0);
+	// 			myAnims[j].activated = false;
+
+	// 			j = getOrderAnims(7);
+	// 			myAnims[j].activated = false;
+
+	// 			j = getOrderAnims(8);
+	// 			myAnims[j].activated = false;
+
+	// 			j = getOrderAnims(9);
+	// 			myAnims[j].activated = false;
+
+	// 			j = getOrderAnims(10);
+	// 			myAnims[j].activated = false;
+
+	// 		break;
+
+	// 		case 34:
+	// 			if (readExtraVariable(AlfredActivity.VIGILANTE_PAJEANDOSE) == 1)
+	// 			{
+	// 				int i = getOrderAnims(0);
+	// 				myAnims[i].activated = false;
+	// 				updateConexion(35, 1);
+
+	// 			}
+	// 		break;
+
+	// 		case 21:
+
+	// 			if ((prevWhichScreen == 36)
+	// 					&& (readExtraVariable(AlfredActivity.PIRAMIDE_JODIDA) == 1))
+	// 			{
+	// 				updateConexion(36, 0);
+	// 				AlfredActivity.ponPegatina(21, 79);
+	// 				AlfredActivity.addPegatina(21, 79);
+	// 			}
+
+	// 		break;
+
+	// 		case 36:
+
+	// 			byte z = readExtraVariable(AlfredActivity.PIRAMIDE_JODIDA);
+	// 			if (z >= 3)
+	// 				setExtraVariables(AlfredActivity.PIRAMIDE_JODIDA, 1);
+
+	// 			int i;
+
+	// 			if (readExtraVariable(AlfredActivity.PIRAMIDE_JODIDA) == 1) // se ha
+	// 																		// cargado
+	// 																		// la
+	// 																		// piramide
+	// 			{
+
+	// 				i = getOrderAnims(0);
+	// 				myAnims[i].x = (int) (328.0f * factX);
+	// 				myAnims[i].y = (int) (209.0f * factY);
+	// 				myAnims[i].moving = false;
+	// 				myAnims[i].incAnims = null;// new movements[1];
+
+	// 				myAnims[i].speed = 1000000;
+
+	// 				i = getOrderAnims(2);
+	// 				myAnims[i].activated = true;
+	// 				myAnims[i].speed = 16;
+	// 			} else// if (prevWhichScreen==21)//no ha hecho nada
+	// 			{
+	// 				i = getOrderAnims(0);
+
+	// 				if (prevWhichScreen == 21)
+	// 				{
+	// 					myAnims[i].activated = true;
+	// 					myAnims[i].speed = 1000000;
+	// 					updateConexion(37, 0);
+	// 				} else
+	// 				{
+	// 					myAnims[i].activated = false;
+	// 					updateConexion(37, 1);
+	// 				}
+
+	// 				i = getOrderAnims(2);
+	// 				myAnims[i].activated = false;
+
+	// 			}
+
+	// 		break;
+
+	// 		case 26:
+
+	// 			mypersonajes = pe[0];
+
+	// 			if (myListUsingObjects.contains(82) == false)// no tiene dinero
+	// 				mypersonajes.setConversation(26, 0, 0);
+	// 			else
+	// 				mypersonajes.setConversation(26, 0, 1);
+
+	// 			mypersonajes = pe[1];
+	// 			mypersonajes.setConversation(26, 1, 0);
+
+	// 			if ((AlfredActivity
+	// 					.readExtraVariable(AlfredActivity.A_LA_CARCEL) == 1)
+	// 					&& (prevWhichScreen == 27)
+	// 					&& (AlfredActivity.readExtraVariable(
+	// 							AlfredActivity.SE_HA_PUESTO_EL_MUNECO) == 0))
+	// 			{
+	// 				mypersonajes = pe[1];
+	// 				mypersonajes.setConversation(26, 1, 1);
+	// 				// myAnims[0].activated=true;
+	// 				finalObject = 1;
+	// 				whichObject = 1;
+	// 				actionToDo = 1;
+	// 				myMovingAlfredThread.stateOfTalking = 0;
+	// 				gotoPos(314, 240, 3);
+	// 				whichAction = objetos.HABLAR;// .h
+	// 				afterMovingAlfred = ALFRED_TALKING;
+	// 				posibilityOfFakeAnswer = false;
+	// 				varCheckRealTime = -1;
+	// 			}
+	// 			if ((AlfredActivity
+	// 					.readExtraVariable(AlfredActivity.A_LA_CARCEL) == 1)
+	// 					&& (prevWhichScreen == 27)
+	// 					&& (AlfredActivity.readExtraVariable(
+	// 							AlfredActivity.SE_HA_PUESTO_EL_MUNECO) == 1))
+	// 			{
+	// 				mypersonajes = pe[1];
+	// 				mypersonajes.setConversation(26, 1, 2);
+	// 			}
+
+	// 		break;
+
+	// 		case 27:
+	// 			if (prevWhichScreen == 33)// viene del tunel
+	// 			{
+	// 				xAlfred = 90.0f * factX;
+	// 				yAlfred = 380.0f * factY;
+	// 				myoverlay.setAnimExtra(16, 9, 12, 158, 115, 0, 0);
+	// 			}
+
+	// 			// mypersonajes = pe[0];
+
+	// 			if (myListUsingObjects.contains(82) == false)
+	// 			{
+	// 				mypersonajes = pe[0];
+	// 				mypersonajes.setConversation(27, 0, 0);
+	// 				mypersonajes = pe[1];
+	// 				mypersonajes.setConversation(27, 1, 0);
+	// 			} else
+	// 			{
+	// 				mypersonajes = pe[0];
+	// 				mypersonajes.setConversation(27, 0, 1);
+	// 				mypersonajes = pe[1];
+	// 				mypersonajes.setConversation(27, 1, 1);
+	// 			}
+
+	// 		break;
+
+	// 		case 32:
+	// 			if (prevWhichScreen == 31)// viene de la carcel
+	// 			{
+	// 				xAlfred = 321.0f * factX;
+	// 				yAlfred = 125.0f * factY;
+	// 				myoverlay.setAnimExtra(11, 7, 9, 33, 72, 0, 0);
+	// 				AlfredActivity.myConversaciones.mytalkingAnims[31].myPersonajes[0]
+	// 						.setConversation(31, 0, 3);
+	// 				// mypersonajes = pe[0];
+	// 				// mypersonajes.setConversation(31, 0, 3);
+	// 			}
+
+	// 		break;
+
+	// 		case 38:
+	// 			if (AlfredActivity
+	// 					.readExtraVariable(AlfredActivity.ROBA_PELO_PRINCESA) == 0)
+	// 			{
+	// 				xAlfred = 230.0f * factX;
+	// 				yAlfred = 283.0f * factY;
+	// 				myoverlay.setAnimExtra(12, 6, 8, 113, 103, -32, 15);
+	// 			}
+	// 		break;
+	// 		case 30:
+	// 			myAnims[0].activated = false;
+	// 			myAnims[4].activated = false;
+
+	// 			if ((readExtraVariable(PUERTA_SECRETA_ABIERTA) == 1)
+	// 					&& (AlfredActivity.readExtraVariable(
+	// 							AlfredActivity.ROBA_PELO_PRINCESA) != 2))
+	// 				updateConexion(38, 1);
+
+	// 			if (AlfredActivity
+	// 					.readExtraVariable(AlfredActivity.ROBA_PELO_PRINCESA) == 1)
+	// 			{
+
+	// 				setExtraVariables(AlfredActivity.ROBA_PELO_PRINCESA, 2);
+	// 				myAnims[0].activated = true;
+	// 				finalObject = 0;
+	// 				actionToDo = 0;
+	// 				myMovingAlfredThread.stateOfTalking = 0;
+	// 				gotoPos(391, 344, 1);
+	// 				whichAction = objetos.HABLAR;// .h
+	// 				afterMovingAlfred = ALFRED_TALKING;
+	// 				posibilityOfFakeAnswer = false;
+	// 				varCheckRealTime = -1;
+	// 			}
+	// 		break;
+
+	// 		case 4:
+	// 			if (readExtraVariable(ELECTROCUTACION) == 0)
+	// 			{
+	// 				setExtraVariables(AlfredActivity.ELECTROCUTACION, 1);
+	// 				hideObject(7);
+	// 			}
+	// 			if (readExtraVariable(AlfredActivity.CABLES_PUESTOS) == 3)
+	// 				showObject(8);
+	// 			else
+	// 				hideObject(8);
+	// 		case 8:
+	// 		case 14:
+	// 			if (AlfredActivity.readExtraVariable(
+	// 					AlfredActivity.VENDEDOR_DEJA_DE_JODER) == 1)
+	// 			{
+	// 				paths.caminos.cams[0].w = (int) (599.0f * AlfredActivity.factX);
+	// 				AlfredActivity.updateConexion(16, 1);
+	// 			}
+	// 		case 16:
+	// 		case 19:
+
+	// 			if ((readExtraVariable(PUESTA_SALSA_PICANTE_EN_MENU) == 1)
+	// 					&& (readExtraVariable(JEFE_ENCARCELADO) == 0)
+	// 					&& (Math.random() < 0.25f))
+	// 			{
+	// 				// muestra periodico, quita al jefe, etc...
+	// 				setExtraVariables(AlfredActivity.JEFE_ENCARCELADO, 1);
+	// 				AlfredActivity.addObject(13, 0, false);// anula de que se pueda
+	// 														// clickar al jefe
+	// 				AlfredActivity.desactivaAnims(13, 0);
+	// 				AlfredActivity.desactivaAnims(13, 1);
+
+	// 				myConversaciones.mytalkingAnims[12].myPersonajes[0]
+	// 						.setConversation(12, 0, 1);// .currentConversationEach[aux.which]=aux.n;//.setConversation(whichScreen,
+	// 													// aux.which, aux.n);
+
+	// 				String x = "extrascreens/"
+	// 						+ extraScreens[(1 * myLanguages.length)
+	// 								+ whichLanguageInt]
+	// 						+ ".png";
+
+	// 				try
+	// 				{
+	// 					periodico = getBitmapFromAsset(x);
+	// 				} catch (IOException e)
+	// 				{
+	// 				}
+
+	// 				Config cx = periodico.getConfig();
+	// 				periodico = periodico.copy(cx, true);
+
+	// 				pintaPeriodicoDandoVueltas = true;
+
+	// 			}
+
+	// 		break;
+	// 		case 9:
+	// 			// personajes pe[] =
+	// 			// AlfredActivity.myConversaciones.mytalkingAnims[whichScreen].myPersonajes;
+	// 			mypersonajes = pe[0];
+
+	// 			if (myListUsingObjects.contains(10) == false)
+	// 				mypersonajes.setConversation(9, 0, 0);
+	// 			else
+	// 				mypersonajes.setConversation(9, 0, 3);
+	// 		break;
+	// 		case 20:
+	// 			// personajes pe[] =
+	// 			// AlfredActivity.myConversaciones.mytalkingAnims[whichScreen].myPersonajes;
+	// 			mypersonajes = pe[0];
+	// 			if (myListUsingObjects.contains(75) == true)
+	// 				mypersonajes.setConversation(20, 0, 2);
+	// 			else
+	// 			{
+	// 				if (myListUsingObjects.contains(59) == true)// &&
+	// 															// (myListUsingObjects.contains(59)==true))
+	// 					mypersonajes.setConversation(20, 0, 1);
+	// 				else
+	// 					mypersonajes.setConversation(20, 0, 0);
+	// 			}
+	// 		break;
+	// 		case 28:
+	// 			if (AlfredActivity.readExtraVariable(
+	// 					AlfredActivity.CROCODILLO_ENCENDIDO) == 1)
+	// 			{
+	// 				hideObject(0);
+	// 				showObject(1);
+	// 				showObject(2);
+	// 				showObject(3);
+	// 				showObject(4);
+	// 				myoverlay.isFadedOut = false;
+	// 			} else
+	// 			{
+	// 				hideObject(1);
+	// 				hideObject(2);
+	// 				hideObject(3);
+	// 				hideObject(4);
+	// 				myoverlay.isFadedOut = true;
+	// 			}
+	// 		break;
+
+	// 		}
+
+	// 		if (whichScreen == 55)
+	// 		{
+	// 			if (prevWhichScreen == 44)
+	// 				myMovingAlfredThread.alfredFrame = 0;
+	// 			else
+	// 				myMovingAlfredThread.alfredFrame = 17;
+	// 		} else
+	// 		{
+	// 			switch (dirAlfred)
+	// 			{
+	// 			case 0:
+	// 				myMovingAlfredThread.alfredFrame = 0;
+	// 			break;
+	// 			case 1:
+	// 				myMovingAlfredThread.alfredFrame = 9;
+	// 			break;
+	// 			case 2:
+	// 				myMovingAlfredThread.alfredFrame = 18;
+	// 			break;
+	// 			case 3:
+	// 				myMovingAlfredThread.alfredFrame = 23;
+	// 			break;
+	// 			}
+
+	// 		}
+
+	// 		BufferedReader brfx;
+	// 		String word;
+	// 		List<String> wordListfx = new ArrayList<String>();
+	// 		wordListfx.clear();
+	// 		String name = "fx/screen" + String.valueOf(whichScreen) + ".txt";
+
+	// 		try
+	// 		{
+	// 			brfx = new BufferedReader(
+	// 					new InputStreamReader(assetManager.open(name)));
+	// 			while ((word = brfx.readLine()) != null)
+	// 				wordListfx.add(word);
+	// 		} catch (IOException e)
+	// 		{
+	// 		}
+
+	// 		int size = wordListfx.size();
+	// 		filelistFX = new String[size];
+
+	// 		for (int i = 0; i < size; i++)
+	// 		{
+	// 			filelistFX[i] = new String();
+	// 			filelistFX[i] = wordListfx.get(i) + ".mp3";
+	// 		}
+
+	// 		if (filelistFX.length > 0)
+	// 		{
+	// 			int off = (int) (10000.0f + (Math.random() * 5000.0f));
+	// 			timeFXtoPlay = SystemClock.uptimeMillis() + off;
+	// 		}
+
+	// 		boolean loop;
+	// 		if (whichScreen != 48)
+	// 			loop = true;
+	// 		else
+	// 			loop = false;
+
+	// 		playTrack(whichScreen, loop);
+
+	// 		fundidoTime = false;
+
+	// 		timeStartScreen = SystemClock.uptimeMillis();
+
+	// 		screenReady = true;
+}
+
 Common::Error PelrockEngine::syncGame(Common::Serializer &s) {
 	// The Serializer has methods isLoading() and isSaving()
 	// if you need to specific steps; for example setting
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 4ce714f965d..e8589a7e34a 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -33,17 +33,49 @@
 #include "engines/engine.h"
 #include "engines/savestate.h"
 #include "graphics/screen.h"
+#include "image/png.h"
 
 #include "pelrock/detection.h"
+#include "pelrock/types.h"
 
 namespace Pelrock {
 
 struct PelrockGameDescription;
 
+const int kAlfredFrameWidth = 51;
+const int kAlfredFrameHeight = 102;
+
 class PelrockEngine : public Engine {
 private:
 	const ADGameDescription *_gameDescription;
 	Common::RandomSource _randomSource;
+	Image::PNGDecoder *decoder = new Image::PNGDecoder();
+	void init();
+	void playIntro();
+	void setScreen(int s, int dir);
+	void setScreenJava(int s, int dir);
+	void loadAnims();
+	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
+	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
+	void loadMainCharacterAnims();
+	void frames();
+
+	int xAlfred = 200;
+	int yAlfred = 200;
+	bool shouldPlayIntro = false;
+	GameState stateGame = GAME;
+	bool gameInitialized = false;
+	bool screenReady = false;
+	int dirAlfred = 0;
+	int prevDirX = 0;
+	int prevDirY = 0;
+	Common::String objectToShow = "";
+	int prevWhichScreen = 0;
+	int whichScreen = 0;
+	byte *pixelsShadows;// =new int[640*400];
+	byte *standingAnim = new byte[3060 * 102];
+
+	int curAlfredFrame = 9;
 protected:
 	// Engine APIs
 	Common::Error run() override;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
new file mode 100644
index 00000000000..84f39ef3542
--- /dev/null
+++ b/engines/pelrock/types.h
@@ -0,0 +1,36 @@
+
+namespace Pelrock {
+
+    const int kRoomStructSize = 104;
+    const int kNumRooms = 56;
+
+
+    enum GameState {
+        GAME = 100,
+        MENU = 101,
+        CREDITS = 102,
+        SAVELOAD = 103,
+        SETTINGS = 104,
+        EXTRA_SCREEN = 105,
+        INTRO = 106,
+        PROMOTE = 107,
+	};
+
+    // struct rectCam
+    // {
+    //     Common::List<int> vecinos;
+    //     bool marked;
+    //     int index;
+    //     int x;
+    //     int y;
+    //     int w;
+    //     int h;
+    // };
+
+    // struct defCam
+    // {
+    //     rectCam cams[];
+    // };
+
+
+} // End of namespace Pelrock


Commit: afac30a24c6b30104fdb13d7826de99f34dcdc47
    https://github.com/scummvm/scummvm/commit/afac30a24c6b30104fdb13d7826de99f34dcdc47
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:58:58+02:00

Commit Message:
PELROCK: Reads room animations and renders on screen

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 5aaf7421da7..18310095298 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -21,6 +21,7 @@
 
 #include "common/config-manager.h"
 #include "common/debug-channels.h"
+#include "common/endian.h"
 #include "common/events.h"
 #include "common/file.h"
 #include "common/scummsys.h"
@@ -178,7 +179,7 @@ void PelrockEngine::init() {
 		// 		myoverlay.setCredits();
 		loadAnims();
 		// 		loadOtherBitmaps();
-		setScreen(0, 2);
+		setScreen(1, 2);
 		// setScreen(0, 2);
 		// 		valSound1 = 0;
 		// 		valSound2 = 0;
@@ -851,6 +852,59 @@ void PelrockEngine::getBackground(Common::File *roomFile, int roomOffset, byte *
 	}
 }
 
+Common::List<Anim> PelrockEngine::getRoomAnimations(Common::File *roomFile, int roomOffset) {
+	uint32_t pair_offset = roomOffset + (8 * 8);
+	roomFile->seek(pair_offset, SEEK_SET);
+    uint32_t offset = roomFile->readUint32LE();
+    uint32_t size = roomFile->readUint32LE();
+
+	byte *data = new byte[size];
+	roomFile->seek(offset, SEEK_SET);
+	roomFile->read(data, size);
+
+	unsigned char *pic = new byte[10000 * 10000];
+    if (offset > 0 && size > 0) {
+        decompress_rle_block(data, size, 0, size, &pic);
+	} else {
+		return Common::List<Anim>();
+	}
+	Common::List<Anim> anims = Common::List<Anim>();
+	uint32_t spriteEnd = offset + size;
+	uint32_t metadata_start = spriteEnd + 108;
+	uint32_t picOffset = 0;
+	for(int i = 0; i < 10; i++) {
+ 		uint32_t animOffset = metadata_start + (i * 44);
+		roomFile->seek(animOffset, SEEK_SET);
+
+		int16 x = roomFile->readSint16LE();
+		int16 y = roomFile->readSint16LE();
+		byte w = roomFile->readByte();
+        byte h = roomFile->readByte();
+		roomFile->skip(2); // reserved
+        int secAnimCount = roomFile->readByte();
+		roomFile->skip(1);
+        byte frames = 0;
+		for( int i =0; i < secAnimCount; i++) {
+            frames += roomFile->readByte();
+		}
+		if (w > 0 && h > 0 && frames > 0) {
+			Anim anim;
+			anim.x = x;
+			anim.y = y;
+			anim.w = w;
+			anim.h = h;
+			anim.nframes = frames;
+			uint32_t needed = anim.w * anim.h * anim.nframes;
+			anim.animData = new byte[needed];
+			Common::copy(pic + picOffset, pic + picOffset + needed, anim.animData);
+			picOffset += needed;
+			debug("Anim %d: x=%d y=%d w=%d h=%d nframes=%d", i, anim.x, anim.y, anim.w, anim.h, anim.nframes);
+			anims.push_back(anim);
+		}
+	}
+	return anims;
+}
+
 void PelrockEngine::loadMainCharacterAnims() {
 	Common::File alfred3;
 	if (!alfred3.open(Common::Path("ALFRED.3"))) {
@@ -891,7 +945,6 @@ void PelrockEngine::setScreen(int number, int dir) {
 		return;
 	}
 
-
 	int roomOffset = number * kRoomStructSize;
 
 	byte *palette = new byte[256 * 3];
@@ -910,6 +963,23 @@ void PelrockEngine::setScreen(int number, int dir) {
 			_screen->setPixel(i, j, background[j * 640 + i]);
 		}
 	}
+	Common::List<Anim> anims = getRoomAnimations(&roomFile, roomOffset);
+	int num = 0;
+	for (Common::List<Anim>::iterator i = anims.begin(); i != anims.end(); i++) {
+		byte *frame = new byte[i->w * i->h];
+
+		Common::copy(i->animData, i->animData + (i->w * i->h), frame);
+
+		for (int y = 0; y < i->h; y++) {
+			for (int x = 0; x < i->w; x++) {
+				unsigned int src_pos = (y * i->w) + x;
+				int xPos = i->x + x;
+				int yPos = i->y + y;
+				if(frame[src_pos] != 255 && xPos > 0 && yPos > 0 && xPos < 640 && yPos < 400)
+					_screen->setPixel(xPos, yPos, frame[src_pos]);
+			}
+		}
+	}
 	_screen->markAllDirty();
 	roomFile.close();
 	delete[] background;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index e8589a7e34a..2028ea96faf 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -57,6 +57,7 @@ private:
 	void loadAnims();
 	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
+	Common::List<Anim> getRoomAnimations(Common::File *roomFile, int roomOffset);
 	void loadMainCharacterAnims();
 	void frames();
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 84f39ef3542..4e6ed97c133 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -4,6 +4,15 @@ namespace Pelrock {
     const int kRoomStructSize = 104;
     const int kNumRooms = 56;
 
+    struct Anim {
+        int x;
+        int y;
+        int w;
+        int h;
+        int nframes;
+        int speed;
+        byte *animData;
+    };
 
     enum GameState {
         GAME = 100,


Commit: 91575276ca2ed9fbdc3de512e4b1ec131eed23b4
    https://github.com/scummvm/scummvm/commit/91575276ca2ed9fbdc3de512e4b1ec131eed23b4
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:58:58+02:00

Commit Message:
PELROCK: Reads hotspots and walkboxes

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 18310095298..7230963e247 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -86,7 +86,13 @@ Common::Error PelrockEngine::run() {
 
 	while (!shouldQuit()) {
 		while (g_system->getEventManager()->pollEvent(e)) {
+			if (e.type == Common::EVENT_MOUSEMOVE) {
+				mouseX = e.mouse.x;
+				mouseY = e.mouse.y;
+				debug(3, "Mouse moved to (%d,%d)", mouseX, mouseY);
+			}
 		}
+		checkMouseHover();
 		frames();
 		_screen->update();
 		limiter.delayBeforeSwap();
@@ -179,7 +185,7 @@ void PelrockEngine::init() {
 		// 		myoverlay.setCredits();
 		loadAnims();
 		// 		loadOtherBitmaps();
-		setScreen(1, 2);
+		setScreen(2, 2);
 		// setScreen(0, 2);
 		// 		valSound1 = 0;
 		// 		valSound2 = 0;
@@ -819,7 +825,6 @@ void PelrockEngine::getPalette(Common::File *roomFile, int roomOffset, byte *pal
 		palette[i * 3 + 1] = palette[i * 3 + 1] << 2;
 		palette[i * 3 + 2] = palette[i * 3 + 2] << 2;
 	}
-
 }
 
 void PelrockEngine::getBackground(Common::File *roomFile, int roomOffset, byte *background) {
@@ -852,43 +857,43 @@ void PelrockEngine::getBackground(Common::File *roomFile, int roomOffset, byte *
 	}
 }
 
-Common::List<Anim> PelrockEngine::getRoomAnimations(Common::File *roomFile, int roomOffset) {
+Common::List<AnimSet> PelrockEngine::getRoomAnimations(Common::File *roomFile, int roomOffset) {
 	uint32_t pair_offset = roomOffset + (8 * 8);
 	roomFile->seek(pair_offset, SEEK_SET);
-    uint32_t offset = roomFile->readUint32LE();
-    uint32_t size = roomFile->readUint32LE();
+	uint32_t offset = roomFile->readUint32LE();
+	uint32_t size = roomFile->readUint32LE();
 
 	byte *data = new byte[size];
 	roomFile->seek(offset, SEEK_SET);
 	roomFile->read(data, size);
 
 	unsigned char *pic = new byte[10000 * 10000];
-    if (offset > 0 && size > 0) {
-        decompress_rle_block(data, size, 0, size, &pic);
+	if (offset > 0 && size > 0) {
+		decompress_rle_block(data, size, 0, size, &pic);
 	} else {
-		return Common::List<Anim>();
+		return Common::List<AnimSet>();
 	}
-	Common::List<Anim> anims = Common::List<Anim>();
+	Common::List<AnimSet> anims = Common::List<AnimSet>();
 	uint32_t spriteEnd = offset + size;
 	uint32_t metadata_start = spriteEnd + 108;
 	uint32_t picOffset = 0;
-	for(int i = 0; i < 10; i++) {
- 		uint32_t animOffset = metadata_start + (i * 44);
+	for (int i = 0; i < 10; i++) {
+		uint32_t animOffset = metadata_start + (i * 44);
 		roomFile->seek(animOffset, SEEK_SET);
 
 		int16 x = roomFile->readSint16LE();
 		int16 y = roomFile->readSint16LE();
 		byte w = roomFile->readByte();
-        byte h = roomFile->readByte();
+		byte h = roomFile->readByte();
 		roomFile->skip(2); // reserved
-        int secAnimCount = roomFile->readByte();
+		int secAnimCount = roomFile->readByte();
 		roomFile->skip(1);
-        byte frames = 0;
-		for( int i =0; i < secAnimCount; i++) {
-            frames += roomFile->readByte();
+		byte frames = 0;
+		for (int i = 0; i < secAnimCount; i++) {
+			frames += roomFile->readByte();
 		}
 		if (w > 0 && h > 0 && frames > 0) {
-			Anim anim;
+			AnimSet anim;
 			anim.x = x;
 			anim.y = y;
 			anim.w = w;
@@ -905,6 +910,68 @@ Common::List<Anim> PelrockEngine::getRoomAnimations(Common::File *roomFile, int
 	return anims;
 }
 
+Common::List<WalkBox> PelrockEngine::loadWalkboxes(Common::File *roomFile, int roomOffset) {
+	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
+	roomFile->seek(pair10_offset_pos, SEEK_SET);
+	// roomFile->skip(4);
+	uint32_t pair10_data_offset = roomFile->readUint32LE();
+	uint32_t pair10_size = roomFile->readUint32LE();
+
+	uint32_t walkbox_countOffset = pair10_data_offset + 0x213;
+	roomFile->seek(walkbox_countOffset, SEEK_SET);
+	byte walkbox_count = roomFile->readByte();
+	debug("Walkbox count: %d", walkbox_count);
+	uint32_t walkbox_offset = pair10_data_offset + 0x218;
+	Common::List<WalkBox> walkboxes;
+	for (int i = 0; i < walkbox_count; i++) {
+		uint32_t box_offset = walkbox_offset + i * 9;
+		roomFile->seek(box_offset, SEEK_SET);
+		int16 x1 = roomFile->readSint16LE();
+		int16 y1 = roomFile->readSint16LE();
+		int16 w = roomFile->readSint16LE();
+		int16 h = roomFile->readSint16LE();
+		byte flags = roomFile->readByte();
+		debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
+		WalkBox box;
+		box.x = x1;
+		box.y = y1;
+		box.w = w;
+		box.h = h;
+		box.flags = flags;
+		walkboxes.push_back(box);
+	}
+	return walkboxes;
+}
+
+void PelrockEngine::loadHotspots(Common::File *roomFile, int roomOffset) {
+	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
+	roomFile->seek(pair10_offset_pos, SEEK_SET);
+	// roomFile->skip(4);
+	uint32_t pair10_data_offset = roomFile->readUint32LE();
+	uint32_t pair10_size = roomFile->readUint32LE();
+	uint32_t count_offset = pair10_data_offset + 0x47a;
+	roomFile->seek(count_offset, SEEK_SET);
+	byte hotspot_count = roomFile->readByte();
+	uint32_t hotspot_data_start = pair10_data_offset + 0x47c;
+	Common::List<HotSpot> hotspots;
+	for (int i = 0; i < hotspot_count; i++) {
+		uint32_t obj_offset = hotspot_data_start + i * 9;
+		roomFile->seek(obj_offset, SEEK_SET);
+		byte obj_bytes[9];
+		roomFile->read(obj_bytes, 9);
+		byte type_byte = obj_bytes[0];
+		HotSpot spot;
+		spot.x = obj_bytes[1] | (obj_bytes[2] << 8);
+		spot.y = obj_bytes[3] | (obj_bytes[4] << 8);
+		spot.w = obj_bytes[5];
+		spot.h = obj_bytes[6];
+		spot.extra = obj_bytes[7] | (obj_bytes[8] << 8);
+		// debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, type_byte, spot.x, spot.y, spot.w, spot.h, spot.extra);
+		hotspots.push_back(spot);
+	}
+	_hotspots = hotspots;
+}
+
 void PelrockEngine::loadMainCharacterAnims() {
 	Common::File alfred3;
 	if (!alfred3.open(Common::Path("ALFRED.3"))) {
@@ -929,7 +996,7 @@ void PelrockEngine::frames() {
 	for (uint32_t y = 0; y < kAlfredFrameHeight; y++) {
 		for (uint32_t x = 0; x < kAlfredFrameWidth; x++) {
 			unsigned int src_pos = (curAlfredFrame * kAlfredFrameHeight * kAlfredFrameWidth) + (y * kAlfredFrameWidth) + x;
-			if(standingAnim[src_pos] != 255)
+			if (standingAnim[src_pos] != 255)
 				_screen->setPixel(x + xAlfred, y + yAlfred, standingAnim[src_pos]);
 		}
 	}
@@ -937,6 +1004,17 @@ void PelrockEngine::frames() {
 	_screen->update();
 }
 
+void PelrockEngine::checkMouseHover() {
+	for (Common::List<HotSpot>::iterator i = _hotspots.begin(); i != _hotspots.end(); i++) {
+		if (mouseX >= i->x && mouseX <= (i->x + i->w) &&
+			mouseY >= i->y && mouseY <= (i->y + i->h)) {
+			// _currentHotspot = &(*i);
+			debug("Hotspot at (%d,%d) size (%d,%d) extra %d", i->x, i->y, i->w, i->h, i->extra);
+			return;
+		}
+	}
+}
+
 void PelrockEngine::setScreen(int number, int dir) {
 
 	Common::File roomFile;
@@ -963,9 +1041,9 @@ void PelrockEngine::setScreen(int number, int dir) {
 			_screen->setPixel(i, j, background[j * 640 + i]);
 		}
 	}
-	Common::List<Anim> anims = getRoomAnimations(&roomFile, roomOffset);
+	Common::List<AnimSet> anims = getRoomAnimations(&roomFile, roomOffset);
 	int num = 0;
-	for (Common::List<Anim>::iterator i = anims.begin(); i != anims.end(); i++) {
+	for (Common::List<AnimSet>::iterator i = anims.begin(); i != anims.end(); i++) {
 		byte *frame = new byte[i->w * i->h];
 
 		Common::copy(i->animData, i->animData + (i->w * i->h), frame);
@@ -975,11 +1053,22 @@ void PelrockEngine::setScreen(int number, int dir) {
 				unsigned int src_pos = (y * i->w) + x;
 				int xPos = i->x + x;
 				int yPos = i->y + y;
-				if(frame[src_pos] != 255 && xPos > 0 && yPos > 0 && xPos < 640 && yPos < 400)
+				if (frame[src_pos] != 255 && xPos > 0 && yPos > 0 && xPos < 640 && yPos < 400)
 					_screen->setPixel(xPos, yPos, frame[src_pos]);
 			}
 		}
 	}
+	loadHotspots(&roomFile, roomOffset);
+	Common::List<WalkBox> walkboxes = loadWalkboxes(&roomFile, roomOffset);
+	int walkboxCount = 0;
+	for (Common::List<WalkBox>::iterator i = walkboxes.begin(); i != walkboxes.end(); i++) {
+		// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
+		_screen->drawLine(i->x, i->y, i->x + i->w, i->y, 0 + walkboxCount);
+		_screen->drawLine(i->x, i->y + i->h, i->x + i->w, i->y + i->h, 0 + walkboxCount);
+		_screen->drawLine(i->x, i->y, i->x, i->y + i->h, 0 + walkboxCount);
+		_screen->drawLine(i->x + i->w, i->y, i->x + i->w, i->y + i->h, 0 + walkboxCount);
+		walkboxCount++;
+	}
 	_screen->markAllDirty();
 	roomFile.close();
 	delete[] background;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 2028ea96faf..25943a7563c 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -22,13 +22,13 @@
 #ifndef PELROCK_H
 #define PELROCK_H
 
-#include "common/scummsys.h"
-#include "common/system.h"
 #include "common/error.h"
 #include "common/fs.h"
 #include "common/hash-str.h"
 #include "common/random.h"
+#include "common/scummsys.h"
 #include "common/serializer.h"
+#include "common/system.h"
 #include "common/util.h"
 #include "engines/engine.h"
 #include "engines/savestate.h"
@@ -55,12 +55,25 @@ private:
 	void setScreen(int s, int dir);
 	void setScreenJava(int s, int dir);
 	void loadAnims();
+	// Room data
 	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
-	Common::List<Anim> getRoomAnimations(Common::File *roomFile, int roomOffset);
+	Common::List<AnimSet> getRoomAnimations(Common::File *roomFile, int roomOffset);
+	void loadHotspots(Common::File *roomFile, int roomOffset);
 	void loadMainCharacterAnims();
+	Common::List<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
+
+	// render loop
 	void frames();
+	void checkMouseHover();
 
+	byte *standingAnim = new byte[3060 * 102];
+	Common::List<HotSpot> _hotspots;
+	int curAlfredFrame = 9;
+	uint16 mouseX = 0;
+	uint16 mouseY = 0;
+
+	// From the original code
 	int xAlfred = 200;
 	int yAlfred = 200;
 	bool shouldPlayIntro = false;
@@ -73,15 +86,14 @@ private:
 	Common::String objectToShow = "";
 	int prevWhichScreen = 0;
 	int whichScreen = 0;
-	byte *pixelsShadows;// =new int[640*400];
-	byte *standingAnim = new byte[3060 * 102];
-
-	int curAlfredFrame = 9;
+	byte *pixelsShadows; // =new int[640*400];
 protected:
 	// Engine APIs
 	Common::Error run() override;
+
 public:
 	Graphics::Screen *_screen = nullptr;
+
 public:
 	PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc);
 	~PelrockEngine() override;
@@ -101,10 +113,9 @@ public:
 	}
 
 	bool hasFeature(EngineFeature f) const override {
-		return
-		    (f == kSupportsLoadingDuringRuntime) ||
-		    (f == kSupportsSavingDuringRuntime) ||
-		    (f == kSupportsReturnToLauncher);
+		return (f == kSupportsLoadingDuringRuntime) ||
+			   (f == kSupportsSavingDuringRuntime) ||
+			   (f == kSupportsReturnToLauncher);
 	};
 
 	bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override {
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 4e6ed97c133..f6deee0d174 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -1,45 +1,70 @@
 
 namespace Pelrock {
 
-    const int kRoomStructSize = 104;
-    const int kNumRooms = 56;
-
-    struct Anim {
-        int x;
-        int y;
-        int w;
-        int h;
-        int nframes;
-        int speed;
-        byte *animData;
-    };
-
-    enum GameState {
-        GAME = 100,
-        MENU = 101,
-        CREDITS = 102,
-        SAVELOAD = 103,
-        SETTINGS = 104,
-        EXTRA_SCREEN = 105,
-        INTRO = 106,
-        PROMOTE = 107,
-	};
-
-    // struct rectCam
-    // {
-    //     Common::List<int> vecinos;
-    //     bool marked;
-    //     int index;
-    //     int x;
-    //     int y;
-    //     int w;
-    //     int h;
-    // };
-
-    // struct defCam
-    // {
-    //     rectCam cams[];
-    // };
+const int kRoomStructSize = 104;
+const int kNumRooms = 56;
 
+struct Anim {
+	int x;
+	int y;
+	int w;
+	int h;
+	int nframes;
+	byte *animData;
+};
+
+struct AnimSet {
+	int x;
+	int y;
+	int w;
+	int h;
+	int nframes;
+	int speed;
+	byte *animData;
+};
+
+struct HotSpot {
+	int x;
+	int y;
+	int id;
+	int w;
+	int h;
+	int extra;
+};
+
+struct WalkBox {
+	int16 x;
+	int16 y;
+	int16 w;
+	int16 h;
+	byte flags;
+};
+
+enum GameState {
+	GAME = 100,
+	MENU = 101,
+	CREDITS = 102,
+	SAVELOAD = 103,
+	SETTINGS = 104,
+	EXTRA_SCREEN = 105,
+	INTRO = 106,
+	PROMOTE = 107,
+};
+
+// struct rectCam
+// {
+//     Common::List<int> vecinos;
+//     bool marked;
+//     int index;
+//     int x;
+//     int y;
+//     int w;
+//     int h;
+// };
+
+// struct defCam
+// {
+//     rectCam cams[];
+// };
 
 } // End of namespace Pelrock


Commit: 94e520c79741df41f715712fff4dcaa2a689b791
    https://github.com/scummvm/scummvm/commit/94e520c79741df41f715712fff4dcaa2a689b791
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:58:58+02:00

Commit Message:
PELROCK: Separate animation sets

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 7230963e247..d3736b1f2cd 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -89,7 +89,7 @@ Common::Error PelrockEngine::run() {
 			if (e.type == Common::EVENT_MOUSEMOVE) {
 				mouseX = e.mouse.x;
 				mouseY = e.mouse.y;
-				debug(3, "Mouse moved to (%d,%d)", mouseX, mouseY);
+				// debug(3, "Mouse moved to (%d,%d)", mouseX, mouseY);
 			}
 		}
 		checkMouseHover();
@@ -877,35 +877,57 @@ Common::List<AnimSet> PelrockEngine::getRoomAnimations(Common::File *roomFile, i
 	uint32_t spriteEnd = offset + size;
 	uint32_t metadata_start = spriteEnd + 108;
 	uint32_t picOffset = 0;
-	for (int i = 0; i < 10; i++) {
+	for (int i = 0; i < 7; i++) {
 		uint32_t animOffset = metadata_start + (i * 44);
 		roomFile->seek(animOffset, SEEK_SET);
 
-		int16 x = roomFile->readSint16LE();
-		int16 y = roomFile->readSint16LE();
-		byte w = roomFile->readByte();
-		byte h = roomFile->readByte();
+		AnimSet animSet;
+		animSet.x = roomFile->readSint16LE();
+		animSet.y = roomFile->readSint16LE();
+		animSet.w = roomFile->readByte();
+		animSet.h = roomFile->readByte();
 		roomFile->skip(2); // reserved
-		int secAnimCount = roomFile->readByte();
+		animSet.numAnims = roomFile->readByte();
+		debug("AnimSet %d: x=%d y=%d w=%d h=%d numAnims=%d", i, animSet.x, animSet.y, animSet.w, animSet.h, animSet.numAnims);
+		animSet.animData = new Anim[animSet.numAnims];
 		roomFile->skip(1);
-		byte frames = 0;
-		for (int i = 0; i < secAnimCount; i++) {
-			frames += roomFile->readByte();
-		}
-		if (w > 0 && h > 0 && frames > 0) {
-			AnimSet anim;
-			anim.x = x;
-			anim.y = y;
-			anim.w = w;
-			anim.h = h;
+
+		for (int j = 0; j < animSet.numAnims; j++) {
+			byte frames = roomFile->readByte();
+			Anim anim;
+			anim.x = animSet.x;
+			anim.y = animSet.y;
+			anim.w = animSet.w;
+			anim.h = animSet.h;
 			anim.nframes = frames;
-			uint32_t needed = anim.w * anim.h * anim.nframes;
-			anim.animData = new byte[needed];
-			Common::copy(pic + picOffset, pic + picOffset + needed, anim.animData);
-			picOffset += needed;
-			debug("Anim %d: x=%d y=%d w=%d h=%d nframes=%d", i, anim.x, anim.y, anim.w, anim.h, anim.nframes);
-			anims.push_back(anim);
+			anim.animData = new byte[anim.nframes];
+			if (anim.w > 0 && anim.h > 0 && anim.nframes > 0) {
+				uint32_t needed = anim.w * anim.h * anim.nframes;
+				anim.animData = new byte[needed];
+				Common::copy(pic + picOffset, pic + picOffset + needed, anim.animData);
+				animSet.animData[j] = anim;
+				debug("Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes);
+				picOffset += needed;
+			} else {
+				debug("Anim %d-%d: invalid dimensions, skipping", i, j);
+			}
+			animSet.animData[j] = anim;
 		}
+		anims.push_back(animSet);
+		// if (w > 0 && h > 0 && frames > 0) {
+		// 	AnimSet anim;
+		// 	anim.x = x;
+		// 	anim.y = y;
+		// 	anim.w = w;
+		// 	anim.h = h;
+		// 	anim.numAnims = frames;
+		// 	uint32_t needed = anim.w * anim.h * anim.nframes;
+		// 	anim.animData = new byte[needed];
+		// 	Common::copy(pic + picOffset, pic + picOffset + needed, anim.animData);
+		// 	picOffset += needed;
+		// 	debug("Anim %d: x=%d y=%d w=%d h=%d nframes=%d", i, anim.x, anim.y, anim.w, anim.h, anim.nframes);
+		// 	anims.push_back(anim);
+		// }
 	}
 	return anims;
 }
@@ -931,7 +953,7 @@ Common::List<WalkBox> PelrockEngine::loadWalkboxes(Common::File *roomFile, int r
 		int16 w = roomFile->readSint16LE();
 		int16 h = roomFile->readSint16LE();
 		byte flags = roomFile->readByte();
-		debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
+		// debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
 		WalkBox box;
 		box.x = x1;
 		box.y = y1;
@@ -1009,7 +1031,7 @@ void PelrockEngine::checkMouseHover() {
 		if (mouseX >= i->x && mouseX <= (i->x + i->w) &&
 			mouseY >= i->y && mouseY <= (i->y + i->h)) {
 			// _currentHotspot = &(*i);
-			debug("Hotspot at (%d,%d) size (%d,%d) extra %d", i->x, i->y, i->w, i->h, i->extra);
+			// debug("Hotspot at (%d,%d) size (%d,%d) extra %d", i->x, i->y, i->w, i->h, i->extra);
 			return;
 		}
 	}
@@ -1044,19 +1066,29 @@ void PelrockEngine::setScreen(int number, int dir) {
 	Common::List<AnimSet> anims = getRoomAnimations(&roomFile, roomOffset);
 	int num = 0;
 	for (Common::List<AnimSet>::iterator i = anims.begin(); i != anims.end(); i++) {
-		byte *frame = new byte[i->w * i->h];
-
-		Common::copy(i->animData, i->animData + (i->w * i->h), frame);
+		debug("Processing animation set %d, numAnims %d", num, i->numAnims);
+		if (i->numAnims == 0) {
+			num++;
+			continue;
+		}
 
-		for (int y = 0; y < i->h; y++) {
-			for (int x = 0; x < i->w; x++) {
-				unsigned int src_pos = (y * i->w) + x;
-				int xPos = i->x + x;
-				int yPos = i->y + y;
-				if (frame[src_pos] != 255 && xPos > 0 && yPos > 0 && xPos < 640 && yPos < 400)
-					_screen->setPixel(xPos, yPos, frame[src_pos]);
+		for (int j = 0; j < i->numAnims; j++) {
+			debug("Drawing animation %d of set %d at (%d,%d) size (%d,%d) nframes %d", j, num, i->x, i->y, i->w, i->h, i->animData[j].nframes);
+			int frameSize = i->animData[j].w * i->animData[j].h;
+			byte *frame = new byte[frameSize];
+			Common::copy(i->animData[j].animData, i->animData[j].animData + (frameSize), frame);
+
+			for (int y = 0; y < i->h; y++) {
+				for (int x = 0; x < i->w; x++) {
+					unsigned int src_pos = (y * i->w) + x;
+					int xPos = i->x + x;
+					int yPos = i->y + y;
+					if (frame[src_pos] != 255 && xPos > 0 && yPos > 0 && xPos < 640 && yPos < 400)
+						_screen->setPixel(xPos, yPos, frame[src_pos]);
+				}
 			}
 		}
+		num++;
 	}
 	loadHotspots(&roomFile, roomOffset);
 	Common::List<WalkBox> walkboxes = loadWalkboxes(&roomFile, roomOffset);
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index f6deee0d174..55fd78550fe 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -18,9 +18,9 @@ struct AnimSet {
 	int y;
 	int w;
 	int h;
-	int nframes;
 	int speed;
-	byte *animData;
+	int numAnims;
+	Anim *animData;
 };
 
 struct HotSpot {


Commit: 09d049a337ffbe64c592acfd70d4106b72a50bef
    https://github.com/scummvm/scummvm/commit/09d049a337ffbe64c592acfd70d4106b72a50bef
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:58:59+02:00

Commit Message:
PELROCK: Display animations

Changed paths:
  A engines/pelrock/chrono.cpp
  A engines/pelrock/chrono.h
    engines/pelrock/module.mk
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/chrono.cpp b/engines/pelrock/chrono.cpp
new file mode 100644
index 00000000000..226b09807bc
--- /dev/null
+++ b/engines/pelrock/chrono.cpp
@@ -0,0 +1,60 @@
+/* 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 "common/system.h"
+
+#include "pelrock/chrono.h"
+#include "pelrock/pelrock.h"
+
+namespace Pelrock {
+
+ChronoManager::ChronoManager(/* args */) : _lastTick(0) {
+}
+
+ChronoManager::~ChronoManager() {
+}
+
+void ChronoManager::updateChrono() {
+	uint32 currentTime = g_system->getMillis();
+
+	if ((currentTime - _lastTick) >= kTickMs / _speedMultiplier) {
+		_gameTick = true;
+		_tickCount++;
+		if (_tickCount == kHalfTickMultiplier) {
+			_tickCount = 0;
+			_gameTickHalfSpeed = true;
+		} else {
+			_gameTickHalfSpeed = false;
+		}
+		_lastTick = currentTime;
+	} else {
+		_gameTick = false;
+	}
+}
+
+void ChronoManager::changeSpeed() {
+	if (_speedMultiplier == 1)
+		_speedMultiplier = 4;
+	else
+		_speedMultiplier = 1;
+}
+
+} // End of namespace Pelrock
diff --git a/engines/pelrock/chrono.h b/engines/pelrock/chrono.h
new file mode 100644
index 00000000000..5257563225f
--- /dev/null
+++ b/engines/pelrock/chrono.h
@@ -0,0 +1,49 @@
+/* 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 PELROCK_CHRONO_H
+#define PELROCK_CHRONO_H
+
+#include "common/scummsys.h"
+
+namespace Pelrock {
+
+// const int kTickMs = 20;
+const int kTickMs = 50;
+const int kHalfTickMultiplier = 2;
+
+class ChronoManager {
+private:
+	uint32 _lastTick = 0;
+	byte _tickCount = 0;
+	byte _speedMultiplier = 1;
+
+public:
+	ChronoManager();
+	~ChronoManager();
+	void updateChrono();
+	void changeSpeed();
+
+	bool _gameTick = false;
+	bool _gameTickHalfSpeed = false;
+};
+
+} // End of namespace Pelrock
+#endif
diff --git a/engines/pelrock/module.mk b/engines/pelrock/module.mk
index 5a2724a9990..bc1a263acdd 100644
--- a/engines/pelrock/module.mk
+++ b/engines/pelrock/module.mk
@@ -2,6 +2,7 @@ MODULE := engines/pelrock
 
 MODULE_OBJS = \
 	pelrock.o \
+	chrono.o \
 	console.o \
 	metaengine.o
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index d3736b1f2cd..86f8c122760 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -45,10 +45,12 @@ PelrockEngine *g_engine;
 PelrockEngine::PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engine(syst),
 																				 _gameDescription(gameDesc), _randomSource("Pelrock") {
 	g_engine = this;
+	_chronoManager = new ChronoManager();
 }
 
 PelrockEngine::~PelrockEngine() {
 	delete _screen;
+	delete _chronoManager;
 }
 
 uint32 PelrockEngine::getFeatures() const {
@@ -85,6 +87,7 @@ Common::Error PelrockEngine::run() {
 	}
 
 	while (!shouldQuit()) {
+		_chronoManager->updateChrono();
 		while (g_system->getEventManager()->pollEvent(e)) {
 			if (e.type == Common::EVENT_MOUSEMOVE) {
 				mouseX = e.mouse.x;
@@ -774,7 +777,7 @@ void PelrockEngine::loadAnims() {
 }
 
 const int EXPECTED_SIZE = 640 * 400;
-size_t decompress_rle_block(const uint8_t *data, size_t data_size, uint32_t offset, uint32_t size, uint8_t **out_data) {
+size_t rleDecompress(const uint8_t *data, size_t data_size, uint32_t offset, uint32_t size, uint8_t **out_data) {
 	// Check for uncompressed markers
 	if (size == 0x8000 || size == 0x6800) {
 		*out_data = (uint8_t *)malloc(size);
@@ -847,7 +850,7 @@ void PelrockEngine::getBackground(Common::File *roomFile, int roomOffset, byte *
 			roomFile->seek(offset, SEEK_SET);
 			roomFile->read(data, size);
 			uint8_t *block_data = NULL;
-			size_t block_size = decompress_rle_block(data, size, 0, size, &block_data);
+			size_t block_size = rleDecompress(data, size, 0, size, &block_data);
 
 			memcpy(background + combined_size, block_data, block_size);
 			combined_size += block_size + 1;
@@ -869,7 +872,7 @@ Common::List<AnimSet> PelrockEngine::getRoomAnimations(Common::File *roomFile, i
 
 	unsigned char *pic = new byte[10000 * 10000];
 	if (offset > 0 && size > 0) {
-		decompress_rle_block(data, size, 0, size, &pic);
+		rleDecompress(data, size, 0, size, &pic);
 	} else {
 		return Common::List<AnimSet>();
 	}
@@ -899,6 +902,7 @@ Common::List<AnimSet> PelrockEngine::getRoomAnimations(Common::File *roomFile, i
 			anim.y = animSet.y;
 			anim.w = animSet.w;
 			anim.h = animSet.h;
+			anim.curFrame = 0;
 			anim.nframes = frames;
 			anim.animData = new byte[anim.nframes];
 			if (anim.w > 0 && anim.h > 0 && anim.nframes > 0) {
@@ -909,11 +913,13 @@ Common::List<AnimSet> PelrockEngine::getRoomAnimations(Common::File *roomFile, i
 				debug("Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes);
 				picOffset += needed;
 			} else {
+				continue;
 				debug("Anim %d-%d: invalid dimensions, skipping", i, j);
 			}
 			animSet.animData[j] = anim;
 		}
 		anims.push_back(animSet);
+
 		// if (w > 0 && h > 0 && frames > 0) {
 		// 	AnimSet anim;
 		// 	anim.x = x;
@@ -1010,20 +1016,77 @@ void PelrockEngine::loadMainCharacterAnims() {
 	int index3 = 0;
 	uint32_t capacity = 3060 * 102;
 	unsigned char *pic = new unsigned char[capacity];
-	decompress_rle_block(bufferFile, alfred3Size, 0, alfred3Size, &pic);
+	rleDecompress(bufferFile, alfred3Size, 0, alfred3Size, &pic);
 	memcpy(standingAnim, pic, 3060 * 102);
 }
 
 void PelrockEngine::frames() {
-	for (uint32_t y = 0; y < kAlfredFrameHeight; y++) {
-		for (uint32_t x = 0; x < kAlfredFrameWidth; x++) {
-			unsigned int src_pos = (curAlfredFrame * kAlfredFrameHeight * kAlfredFrameWidth) + (y * kAlfredFrameWidth) + x;
-			if (standingAnim[src_pos] != 255)
-				_screen->setPixel(x + xAlfred, y + yAlfred, standingAnim[src_pos]);
+	if (_chronoManager->_gameTick) {
+		for (uint32_t y = 0; y < kAlfredFrameHeight; y++) {
+			for (uint32_t x = 0; x < kAlfredFrameWidth; x++) {
+				unsigned int src_pos = (curAlfredFrame * kAlfredFrameHeight * kAlfredFrameWidth) + (y * kAlfredFrameWidth) + x;
+				if (standingAnim[src_pos] != 255)
+					_screen->setPixel(x + xAlfred, y + yAlfred, standingAnim[src_pos]);
+			}
+		}
+		int num = 0;
+
+		for (Common::List<AnimSet>::iterator i = _currentRoomAnims.begin(); i != _currentRoomAnims.end(); i++) {
+			debug("Processing animation set %d, numAnims %d", num, i->numAnims);
+			if (i->numAnims == 0) {
+				num++;
+				continue;
+			}
+
+			for (int j = 0; j < i->numAnims; j++) {
+				debug("Drawing animation %d of set %d at (%d,%d) size (%d,%d) nframes %d", j, num, i->x, i->y, i->w, i->h, i->animData[j].nframes);
+				int x = i->animData[j].x;
+				int y = i->animData[j].y;
+				int w = i->animData[j].w;
+				int h = i->animData[j].h;
+
+				int frameSize = i->animData[j].w * i->animData[j].h;
+				int curFrame = i->animData[j].curFrame;
+				byte *frame = new byte[frameSize];
+				Common::copy(i->animData[j].animData + (curFrame * i->h * i->w), i->animData[j].animData + (curFrame * i->h * i->w) + (frameSize), frame);
+				debug("Current frame %d of %d", curFrame, i->animData[j].nframes);
+
+				byte *bg = new byte[frameSize];
+				for (int j = 0; j < w; j++) {
+					for (int i = 0; i < h; i++) {
+						int idx = i * w + j;
+						*(bg + idx) = _currentBackground[(y + i) * 640 + (x + j)];
+					}
+				}
+
+				for (int i = 0; i < w; i++) {
+					for (int j = 0; j < h; j++) {
+						int index = (j * w + i);
+							*(byte *)g_engine->_screen->getBasePtr(x + i, y + j) = bg[index];
+					}
+				}
+
+				for (int y = 0; y < i->h; y++) {
+					for (int x = 0; x < i->w; x++) {
+
+						unsigned int src_pos = (y * i->w) + x;
+						int xPos = i->x + x;
+						int yPos = i->y + y;
+						if (frame[src_pos] != 255 && xPos > 0 && yPos > 0 && xPos < 640 && yPos < 400)
+							_screen->setPixel(xPos, yPos, frame[src_pos]);
+					}
+				}
+				if (i->animData[j].curFrame < i->animData[j].nframes - 1) {
+					i->animData[j].curFrame++;
+				} else {
+					i->animData[j].curFrame = 0;
+				}
+			}
+			num++;
 		}
+		_screen->markAllDirty();
+		_screen->update();
 	}
-	_screen->markAllDirty();
-	_screen->update();
 }
 
 void PelrockEngine::checkMouseHover() {
@@ -1058,38 +1121,17 @@ void PelrockEngine::setScreen(int number, int dir) {
 
 	byte *background = new byte[640 * 400];
 	getBackground(&roomFile, roomOffset, background);
+	if(_currentBackground != nullptr)
+		delete[] _currentBackground;
+	_currentBackground = new byte[640 * 400];
+	Common::copy(background, background + 640 * 400, _currentBackground);
 	for (int i = 0; i < 640; i++) {
 		for (int j = 0; j < 400; j++) {
 			_screen->setPixel(i, j, background[j * 640 + i]);
 		}
 	}
-	Common::List<AnimSet> anims = getRoomAnimations(&roomFile, roomOffset);
-	int num = 0;
-	for (Common::List<AnimSet>::iterator i = anims.begin(); i != anims.end(); i++) {
-		debug("Processing animation set %d, numAnims %d", num, i->numAnims);
-		if (i->numAnims == 0) {
-			num++;
-			continue;
-		}
+	_currentRoomAnims = getRoomAnimations(&roomFile, roomOffset);
 
-		for (int j = 0; j < i->numAnims; j++) {
-			debug("Drawing animation %d of set %d at (%d,%d) size (%d,%d) nframes %d", j, num, i->x, i->y, i->w, i->h, i->animData[j].nframes);
-			int frameSize = i->animData[j].w * i->animData[j].h;
-			byte *frame = new byte[frameSize];
-			Common::copy(i->animData[j].animData, i->animData[j].animData + (frameSize), frame);
-
-			for (int y = 0; y < i->h; y++) {
-				for (int x = 0; x < i->w; x++) {
-					unsigned int src_pos = (y * i->w) + x;
-					int xPos = i->x + x;
-					int yPos = i->y + y;
-					if (frame[src_pos] != 255 && xPos > 0 && yPos > 0 && xPos < 640 && yPos < 400)
-						_screen->setPixel(xPos, yPos, frame[src_pos]);
-				}
-			}
-		}
-		num++;
-	}
 	loadHotspots(&roomFile, roomOffset);
 	Common::List<WalkBox> walkboxes = loadWalkboxes(&roomFile, roomOffset);
 	int walkboxCount = 0;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 25943a7563c..026bc2eb928 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -24,6 +24,7 @@
 
 #include "common/error.h"
 #include "common/fs.h"
+#include "common/file.h"
 #include "common/hash-str.h"
 #include "common/random.h"
 #include "common/scummsys.h"
@@ -35,6 +36,7 @@
 #include "graphics/screen.h"
 #include "image/png.h"
 
+#include "pelrock/chrono.h"
 #include "pelrock/detection.h"
 #include "pelrock/types.h"
 
@@ -67,11 +69,15 @@ private:
 	void frames();
 	void checkMouseHover();
 
+	ChronoManager *_chronoManager = nullptr;
 	byte *standingAnim = new byte[3060 * 102];
 	Common::List<HotSpot> _hotspots;
+	Common::List<AnimSet> _currentRoomAnims;
+	int *_currentAnimFrames = nullptr;
 	int curAlfredFrame = 9;
 	uint16 mouseX = 0;
 	uint16 mouseY = 0;
+	byte *_currentBackground = nullptr;
 
 	// From the original code
 	int xAlfred = 200;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 55fd78550fe..842df26840c 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -10,6 +10,7 @@ struct Anim {
 	int w;
 	int h;
 	int nframes;
+    int curFrame;
 	byte *animData;
 };
 


Commit: 1c9ef9c65bb465bfc2a03a3f5d26ba56e65a7893
    https://github.com/scummvm/scummvm/commit/1c9ef9c65bb465bfc2a03a3f5d26ba56e65a7893
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:58:59+02:00

Commit Message:
PELROCK: Implements Room exits

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 86f8c122760..48715e0d6ad 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -93,6 +93,8 @@ Common::Error PelrockEngine::run() {
 				mouseX = e.mouse.x;
 				mouseY = e.mouse.y;
 				// debug(3, "Mouse moved to (%d,%d)", mouseX, mouseY);
+			} else if (e.type == Common::EVENT_LBUTTONUP) {
+				checkMouseClick(e.mouse.x, e.mouse.y);
 			}
 		}
 		checkMouseHover();
@@ -188,7 +190,7 @@ void PelrockEngine::init() {
 		// 		myoverlay.setCredits();
 		loadAnims();
 		// 		loadOtherBitmaps();
-		setScreen(2, 2);
+		setScreen(1, 2);
 		// setScreen(0, 2);
 		// 		valSound1 = 0;
 		// 		valSound2 = 0;
@@ -971,6 +973,33 @@ Common::List<WalkBox> PelrockEngine::loadWalkboxes(Common::File *roomFile, int r
 	return walkboxes;
 }
 
+Common::List<Exit> PelrockEngine::loadExits(Common::File *roomFile, int roomOffset) {
+	Common::List<Exit> exits;
+	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
+	roomFile->seek(pair10_offset_pos, SEEK_SET);
+	uint32_t pair10_data_offset = roomFile->readUint32LE();
+	uint32_t pair10_size = roomFile->readUint32LE();
+	roomFile->seek(pair10_data_offset + 0x1BE, SEEK_SET);
+	int exit_count = roomFile->readByte();
+	roomFile->seek(pair10_data_offset + 0x1BF, SEEK_SET);
+	for (int i = 0; i < exit_count; i++) {
+		Exit exit;
+		exit.targetRoom = roomFile->readUint16LE();
+		exit.flags = roomFile->readByte();
+		exit.x = roomFile->readUint16LE();
+		exit.y = roomFile->readUint16LE();
+		exit.w = roomFile->readByte();
+		exit.h = roomFile->readByte();
+
+		exit.targetX = roomFile->readUint16LE();
+		exit.targetY = roomFile->readUint16LE();
+		exit.dir = roomFile->readByte();
+		debug("Exit %d: x=%d y=%d w=%d h=%d targetRoom=%d targetX=%d targetY=%d", i, exit.x, exit.y, exit.w, exit.h, exit.targetRoom, exit.targetX, exit.targetY);
+		exits.push_back(exit);
+	}
+	return exits;
+}
+
 void PelrockEngine::loadHotspots(Common::File *roomFile, int roomOffset) {
 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
 	roomFile->seek(pair10_offset_pos, SEEK_SET);
@@ -1032,14 +1061,14 @@ void PelrockEngine::frames() {
 		int num = 0;
 
 		for (Common::List<AnimSet>::iterator i = _currentRoomAnims.begin(); i != _currentRoomAnims.end(); i++) {
-			debug("Processing animation set %d, numAnims %d", num, i->numAnims);
+			// debug("Processing animation set %d, numAnims %d", num, i->numAnims);
 			if (i->numAnims == 0) {
 				num++;
 				continue;
 			}
 
 			for (int j = 0; j < i->numAnims; j++) {
-				debug("Drawing animation %d of set %d at (%d,%d) size (%d,%d) nframes %d", j, num, i->x, i->y, i->w, i->h, i->animData[j].nframes);
+				// debug("Drawing animation %d of set %d at (%d,%d) size (%d,%d) nframes %d", j, num, i->x, i->y, i->w, i->h, i->animData[j].nframes);
 				int x = i->animData[j].x;
 				int y = i->animData[j].y;
 				int w = i->animData[j].w;
@@ -1049,22 +1078,22 @@ void PelrockEngine::frames() {
 				int curFrame = i->animData[j].curFrame;
 				byte *frame = new byte[frameSize];
 				Common::copy(i->animData[j].animData + (curFrame * i->h * i->w), i->animData[j].animData + (curFrame * i->h * i->w) + (frameSize), frame);
-				debug("Current frame %d of %d", curFrame, i->animData[j].nframes);
-
-				byte *bg = new byte[frameSize];
-				for (int j = 0; j < w; j++) {
-					for (int i = 0; i < h; i++) {
-						int idx = i * w + j;
-						*(bg + idx) = _currentBackground[(y + i) * 640 + (x + j)];
-					}
-				}
-
-				for (int i = 0; i < w; i++) {
-					for (int j = 0; j < h; j++) {
-						int index = (j * w + i);
-							*(byte *)g_engine->_screen->getBasePtr(x + i, y + j) = bg[index];
-					}
-				}
+				// debug("Current frame %d of %d", curFrame, i->animData[j].nframes);
+
+				// byte *bg = new byte[frameSize];
+				// for (int j = 0; j < w; j++) {
+				// 	for (int i = 0; i < h; i++) {
+				// 		int idx = i * w + j;
+				// 		*(bg + idx) = _currentBackground[(y + i) * 640 + (x + j)];
+				// 	}
+				// }
+
+				// for (int i = 0; i < w; i++) {
+				// 	for (int j = 0; j < h; j++) {
+				// 		int index = (j * w + i);
+				// 			*(byte *)g_engine->_screen->getBasePtr(x + i, y + j) = bg[index];
+				// 	}
+				// }
 
 				for (int y = 0; y < i->h; y++) {
 					for (int x = 0; x < i->w; x++) {
@@ -1089,6 +1118,25 @@ void PelrockEngine::frames() {
 	}
 }
 
+void PelrockEngine::checkMouseClick(int x, int y) {
+	for (Common::List<Exit>::iterator i = _currentRoomExits.begin(); i != _currentRoomExits.end(); i++) {
+		if (x >= i->x && x <= (i->x + i->w) &&
+			y >= i->y && y <= (i->y + i->h)) {
+			debug("Clicked Exit at (%d,%d) size (%d,%d) to room %d", i->x, i->y, i->w, i->h, i->targetRoom);
+			setScreen(i->targetRoom, i->dir);
+			return;
+		}
+	}
+	for (Common::List<HotSpot>::iterator i = _hotspots.begin(); i != _hotspots.end(); i++) {
+		if (x >= i->x && x <= (i->x + i->w) &&
+			y >= i->y && y <= (i->y + i->h)) {
+			debug("Clicked Hotspot at (%d,%d) size (%d,%d) extra %d", i->x, i->y, i->w, i->h, i->extra);
+			// process hotspot action based on i->extra or other properties
+			return;
+		}
+	}
+}
+
 void PelrockEngine::checkMouseHover() {
 	for (Common::List<HotSpot>::iterator i = _hotspots.begin(); i != _hotspots.end(); i++) {
 		if (mouseX >= i->x && mouseX <= (i->x + i->w) &&
@@ -1121,7 +1169,7 @@ void PelrockEngine::setScreen(int number, int dir) {
 
 	byte *background = new byte[640 * 400];
 	getBackground(&roomFile, roomOffset, background);
-	if(_currentBackground != nullptr)
+	if (_currentBackground != nullptr)
 		delete[] _currentBackground;
 	_currentBackground = new byte[640 * 400];
 	Common::copy(background, background + 640 * 400, _currentBackground);
@@ -1143,6 +1191,18 @@ void PelrockEngine::setScreen(int number, int dir) {
 		_screen->drawLine(i->x + i->w, i->y, i->x + i->w, i->y + i->h, 0 + walkboxCount);
 		walkboxCount++;
 	}
+
+	_currentRoomExits = loadExits(&roomFile, roomOffset);
+
+	for (Common::List<Exit>::iterator i = _currentRoomExits.begin(); i != _currentRoomExits.end(); i++) {
+		// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
+		_screen->drawLine(i->x, i->y, i->x + i->w, i->y, 0 + walkboxCount);
+		_screen->drawLine(i->x, i->y + i->h, i->x + i->w, i->y + i->h, 0 + walkboxCount);
+		_screen->drawLine(i->x, i->y, i->x, i->y + i->h, 0 + walkboxCount);
+		_screen->drawLine(i->x + i->w, i->y, i->x + i->w, i->y + i->h, 0 + walkboxCount);
+		walkboxCount++;
+	}
+
 	_screen->markAllDirty();
 	roomFile.close();
 	delete[] background;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 026bc2eb928..62b3cdeeea1 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -23,8 +23,8 @@
 #define PELROCK_H
 
 #include "common/error.h"
-#include "common/fs.h"
 #include "common/file.h"
+#include "common/fs.h"
 #include "common/hash-str.h"
 #include "common/random.h"
 #include "common/scummsys.h"
@@ -63,16 +63,19 @@ private:
 	Common::List<AnimSet> getRoomAnimations(Common::File *roomFile, int roomOffset);
 	void loadHotspots(Common::File *roomFile, int roomOffset);
 	void loadMainCharacterAnims();
+	Common::List<Exit> loadExits(Common::File *roomFile, int roomOffset);
 	Common::List<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
 
 	// render loop
 	void frames();
 	void checkMouseHover();
+	void checkMouseClick(int x, int y);
 
 	ChronoManager *_chronoManager = nullptr;
 	byte *standingAnim = new byte[3060 * 102];
 	Common::List<HotSpot> _hotspots;
 	Common::List<AnimSet> _currentRoomAnims;
+	Common::List<Exit> _currentRoomExits;
 	int *_currentAnimFrames = nullptr;
 	int curAlfredFrame = 9;
 	uint16 mouseX = 0;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 842df26840c..9b776ee65ef 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -10,10 +10,23 @@ struct Anim {
 	int w;
 	int h;
 	int nframes;
-    int curFrame;
+	int curFrame;
 	byte *animData;
 };
 
+struct Exit {
+	uint16 x;
+	uint16 y;
+	byte w;
+	byte h;
+	uint16 targetRoom;
+	uint16 targetX;
+	uint16 targetY;
+	uint16 targetDir;
+	byte dir;
+	byte flags;
+};
+
 struct AnimSet {
 	int x;
 	int y;


Commit: 4aec798219ee64285a342ed5fc2863fc20b0e1a3
    https://github.com/scummvm/scummvm/commit/4aec798219ee64285a342ed5fc2863fc20b0e1a3
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:58:59+02:00

Commit Message:
PELROCK: Load cursors, move alfred when changing room

Changed paths:
  A engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
new file mode 100644
index 00000000000..1312a564db7
--- /dev/null
+++ b/engines/pelrock/offsets.h
@@ -0,0 +1,38 @@
+/* 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 PELROCK_OFFSETS_H
+#define PELROCK_OFFSETS_H
+
+#include "common/scummsys.h"
+
+namespace Pelrock {
+
+    uint32_t cursor_offsets[5] = {
+        0x0FDDFD,
+        0x0FDCDD,
+        0x0FDF1D,
+        0x0FE33D,
+        0x367EF0
+    };
+
+} // End of namespace Pelrock
+#endif
+
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 48715e0d6ad..41ad51cf1a4 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -37,6 +37,7 @@
 #include "pelrock/console.h"
 #include "pelrock/detection.h"
 #include "pelrock/pelrock.h"
+#include "pelrock/offsets.h"
 
 namespace Pelrock {
 
@@ -51,6 +52,9 @@ PelrockEngine::PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc) :
 PelrockEngine::~PelrockEngine() {
 	delete _screen;
 	delete _chronoManager;
+	for(int i =0; i<5; i++) {
+		delete[] _cursorMasks[i];
+	}
 }
 
 uint32 PelrockEngine::getFeatures() const {
@@ -109,519 +113,14 @@ Common::Error PelrockEngine::run() {
 }
 
 void PelrockEngine::init() {
-	CursorMan.setDefaultArrowCursor();
+	loadCursors();
+
+	changeCursor(DEFAULT);
 	CursorMan.showMouse(true);
 	if (gameInitialized == false) {
-		pixelsShadows = new byte[640 * 400];
-		// 		sabeUsarElLibroMagico = 0;
-		// 		magicWords = false;
-		// 		apagaLaLuz = false;
-		// 		showUsingObject = -1;
-		// 		prevWhichScreen = -1;
-		// 		whichScreen = -1;
 		gameInitialized = true;
-
-		// 		tutorial.init();
-
-		prevDirX = 0;
-		prevDirY = 0;
-		dirAlfred = 2;
-
-		objectToShow = "";
-
-		// 		factX = (float) widthScreen / (float) 640.0f;
-		// 		factY = (float) heightScreen / (float) 400.0f;
-
-		// xAlfred = (186 * factX);
-		// yAlfred = (307 * factY);
-		// xAlfred = 186;
-		// yAlfred = 307;
-
-		// 		for (int i = 0; i < 14; i++)
-		// 		{
-		// 			myPestanas[i].x = (int) ((float) myPestanas[i].x * factX);
-		// 			myPestanas[i].y = (int) ((float) myPestanas[i].y * factY);
-		// 			myPestanas[i].w = (int) ((float) myPestanas[i].w * factX);
-		// 			myPestanas[i].h = (int) ((float) myPestanas[i].h * factY);
-		// 		}
-
-		// 		movingAlfred = ALFRED_STOPPED;
-
-		// 		listUsedBranchs = new LinkedList<Integer>();
-		// 		listUsedBranchs.clear();
-
-		// 		listRects = new LinkedList<Integer>();
-		// 		listRects.clear();
-
-		// 		timeIddle = SystemClock.uptimeMillis();
-
-		// 		myListLibros = new LinkedList<libros>();
-		// 		myListLibros.clear();
-
-		// 		myListDesactAnims = new LinkedList<dactAnims>();
-		// 		myListDesactAnims.clear();
-
-		// 		myListChangesConvs = new LinkedList<changesConvs>();
-		// 		myListChangesConvs.clear();
-
-		// 		myListPegas = new LinkedList<pegas>();
-		// 		myListPegas.clear();
-
-		// 		myListObjects = new LinkedList<objects>();
-		// 		myListObjects.clear();
-
-		// 		myListUsingObjects = new LinkedList<Integer>();
-		// 		myListUsingObjects.clear();
-
-		// 		isPersonajeTalking = -1;
-
-		// 		stanteriaABuscar = -1;
-
-		// 		selectedUsingObject = false;
-
-		// 		currentTrack = "";
-
-		// 		mainVolumen = 50;
-		// 		mainTextSpeed = 50;
-
-		// 		extraDataToSave = new byte[98];
-
-		// 		leeLibros();
-		// 		myoverlay.setCredits();
 		loadAnims();
-		// 		loadOtherBitmaps();
-		setScreen(1, 2);
-		// setScreen(0, 2);
-		// 		valSound1 = 0;
-		// 		valSound2 = 0;
-		// 		valSound3 = 0;
-
-		// 		loadExtraTextsToTranslate();
-
-		// 		loadTutorialText();
-
-		// 		if (myMainActivity == null)
-		// 			myMainActivity = this;// new AlfredActivity();
-
-		// 		this.addContentView(myoverlay,
-		// 				new android.view.ViewGroup.LayoutParams(
-		// 						android.view.ViewGroup.LayoutParams.FILL_PARENT,
-		// 						android.view.ViewGroup.LayoutParams.FILL_PARENT));
-
-		// 		settingsLayout = new RelativeLayout(myContext);
-		// 		settingsLayout
-		// 				.setLayoutParams(new android.view.ViewGroup.LayoutParams(
-		// 						android.view.ViewGroup.LayoutParams.FILL_PARENT,
-		// 						android.view.ViewGroup.LayoutParams.FILL_PARENT));
-
-		// 		settingsElem1 = new SeekBar(myContext);
-		// 		settingsElem2 = new SeekBar(myContext);
-		// 		settingsElem3 = new Spinner(myContext);
-		// 		settingsElem4 = new Button(myContext);
-
-		// 		String[] items = new String[myLanguages.length];// {"English",
-		// 														// "Spanish",
-		// 														// "Three"};
-		// 		for (i = 0; i < myLanguages.length; i++)
-		// 		{
-		// 			items[i] = new String();
-		// 			items[i] = myLanguages[i].nShow;
-		// 		}
-
-		// 		mySpinAdapter<String> adapter = new mySpinAdapter<String>(myContext,
-		// 				items);
-
-		// 		settingsElem3.setAdapter(adapter);
-
-		// 		settingsElem3.setSelection(whichLanguageIndex);
-
-		// 		String stringTutorial = "";
-		// 		String namet = AlfredActivity.destinationFolder + "/tutorial.kk";
-		// 		File ft = new File(namet);
-
-		// 		if (ft.exists() == true)
-		// 		{
-		// 			byte b[] = tools.readFile(namet);
-		// 			b[0]++;
-
-		// 			if (b[0] >= 3)
-		// 			{
-		// 				b[0] = 3;
-		// 				playTutorial = false;
-		// 				stringTutorial = "OFF";
-		// 			} else
-		// 			{
-		// 				playTutorial = true;
-		// 				stringTutorial = "ON";
-		// 			}
-
-		// 			tools.createFile(namet, b);
-
-		// 		} else
-		// 		{
-		// 			byte b[] = new byte[1];
-
-		// 			b[0] = 0;
-
-		// 			tools.createFile(namet, b);
-		// 			playTutorial = true;
-		// 			stringTutorial = "ON";
-		// 		}
-
-		// 		String finalText = "Tutorial " + stringTutorial;
-
-		// 		settingsElem4.setText(finalText);
-		// 		settingsElem4.postInvalidate();
-		// 		settingsElem4.setBackgroundColor(0x00000000);
-
-		// 		settingsElem1.setOnSeekBarChangeListener(
-		// 				new SeekBar.OnSeekBarChangeListener()
-		// 				{
-
-		// 					public void onProgressChanged(SeekBar seekBar,
-		// 							int progress, boolean fromTouch)
-		// 					{
-		// 						setVolumen(progress);
-		// 					}
-
-		// 					public void onStartTrackingTouch(SeekBar seekBar)
-		// 					{
-		// 					}
-
-		// 					public void onStopTrackingTouch(SeekBar seekBar)
-		// 					{
-		// 					}
-		// 				});
-		// 		settingsElem2.setOnSeekBarChangeListener(
-		// 				new SeekBar.OnSeekBarChangeListener()
-		// 				{
-
-		// 					public void onProgressChanged(SeekBar seekBar,
-		// 							int progress, boolean fromTouch)
-		// 					{
-		// 						// mainTextSpeed=progress;
-		// 						factTimeToShow = 10 + (100 - progress);
-		// 					}
-
-		// 					public void onStartTrackingTouch(SeekBar seekBar)
-		// 					{
-		// 					}
-
-		// 					public void onStopTrackingTouch(SeekBar seekBar)
-		// 					{
-		// 					}
-		// 				});
-
-		// 		settingsElem3.setOnItemSelectedListener(new OnItemSelectedListener()
-		// 		{
-
-		// 			@Override
-		// 			public void onItemSelected(AdapterView<?> arg0, View arg1,
-		// 					int arg2, long arg3)
-		// 			{
-
-		// 				// trying to avoid undesired spinner selection changed
-		// 				// event, a known problem
-		// 				if (m_intSpinnerInitiCount < NO_OF_EVENTS)
-		// 				{
-		// 					m_intSpinnerInitiCount++;
-		// 				} else
-		// 				{
-		// 					whichLanguage = myLanguages[arg2].nInternal;
-		// 					whichLanguageInt = arg2;
-		// 					loadExtraTextsToTranslate();
-		// 					setScreen(whichScreen, dirAlfred);
-		// 					leeLibros();
-		// 					myoverlay.setCredits();
-		// 					loadTutorialText();
-		// 				}
-		// 			}
-
-		// 			@Override
-		// 			public void onNothingSelected(AdapterView<?> arg0)
-		// 			{
-
-		// 			}
-
-		// 		});
-
-		// 		// set up list view
-		// 		//////////////////
-		// 		saveLoadLayout = new LinearLayout(myContext);
-		// 		saveLoadLayout
-		// 				.setLayoutParams(new android.view.ViewGroup.LayoutParams(
-		// 						android.view.ViewGroup.LayoutParams.FILL_PARENT,
-		// 						android.view.ViewGroup.LayoutParams.FILL_PARENT));
-
-		// 		saveLoadListView = new ListView(myContext);
-
-		// 		saveLoadListView.setOnItemClickListener(new OnItemClickListener()
-		// 		{
-
-		// 			@Override
-		// 			public void onItemClick(AdapterView<?> arg0, View arg1,
-		// 					final int arg2, long arg3)
-		// 			{
-
-		// 				if (saveOrLoad == 0)// save
-		// 				{
-
-		// 					AlertDialog.Builder alert = new AlertDialog.Builder(
-		// 							myContext);
-
-		// 					alert.setTitle("Dreamtripper");
-		// 					alert.setMessage(extraThingsToTranslate[4]);
-		// 					alert.setIcon(R.drawable.alfred);
-		// 					final EditText input = new EditText(myContext);
-		// 					String pp = nombrePartidas[arg2];
-		// 					if (pp.compareTo(extraThingsToTranslate[7]) == 0)
-		// 						pp = "";
-		// 					input.setText(pp);
-		// 					alert.setView(input);
-
-		// 					alert.setPositiveButton("Ok",
-		// 							new DialogInterface.OnClickListener()
-		// 							{
-		// 								public void onClick(DialogInterface dialog,
-		// 										int whichButton)
-		// 								{
-		// 									grabaPartida(arg2,
-		// 											input.getText().toString());
-		// 									tools.showMessage(myContext,
-		// 											"Dreamtripper",
-		// 											extraThingsToTranslate[6],
-		// 											"OK");
-		// 									getNamesPartidas();
-		// 								}
-		// 							});
-
-		// 					alert.setNegativeButton("Cancel",
-		// 							new DialogInterface.OnClickListener()
-		// 							{
-		// 								public void onClick(DialogInterface dialog,
-		// 										int whichButton)
-		// 								{
-		// 								}
-		// 							});
-
-		// 					alert.show();
-
-		// 				} else
-		// 				{// load
-
-		// 					cargaPartida(arg2);
-
-		// 				}
-
-		// 			}
-
-		// 		});
-
-		// 		saveLoadLayout.addView(saveLoadListView,
-		// 				new android.view.ViewGroup.LayoutParams(
-		// 						android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
-		// 						android.view.ViewGroup.LayoutParams.WRAP_CONTENT));
-
-		// 		settingsLayout.addView(settingsElem1,
-		// 				new android.view.ViewGroup.LayoutParams(
-		// 						(int) ((float) 180 * factX),
-		// 						(int) ((float) 50 * factY)));
-
-		// 		settingsLayout.addView(settingsElem2,
-		// 				new android.view.ViewGroup.LayoutParams(
-		// 						(int) ((float) 180 * factX),
-		// 						(int) ((float) 50 * factY)));
-
-		// 		settingsLayout.addView(settingsElem3,
-		// 				new android.view.ViewGroup.LayoutParams(
-		// 						(int) ((float) 120 * factX),
-		// 						(int) ((float) 50 * factY)));
-
-		// 		settingsLayout.addView(settingsElem4,
-		// 				new android.view.ViewGroup.LayoutParams(
-		// 						(int) ((float) 130 * factX),
-		// 						(int) ((float) 50 * factY)));
-
-		// 		ViewGroup.MarginLayoutParams mlp2 = (ViewGroup.MarginLayoutParams) settingsElem1
-		// 				.getLayoutParams();
-		// 		mlp2.setMargins((int) ((float) 300 * factX),
-		// 				(int) ((float) 195 * factY),
-		// 				(int) ((float) (640 - 460) * factX),
-		// 				(int) ((float) (400 - 245) * factY));
-
-		// 		ViewGroup.MarginLayoutParams mlp3 = (ViewGroup.MarginLayoutParams) settingsElem2
-		// 				.getLayoutParams();
-		// 		mlp3.setMargins((int) ((float) 300 * factX),
-		// 				(int) ((float) 235 * factY),
-		// 				(int) ((float) (640 - 460) * factX),
-		// 				(int) ((float) (400 - 285) * factY));
-
-		// 		ViewGroup.MarginLayoutParams mlp4 = (ViewGroup.MarginLayoutParams) settingsElem3
-		// 				.getLayoutParams();
-		// 		mlp4.setMargins((int) ((float) 340 * factX),
-		// 				(int) ((float) 285 * factY),
-		// 				(int) ((float) (640 - 460) * factX),
-		// 				(int) ((float) (400 - 325) * factY));
-
-		// 		ViewGroup.MarginLayoutParams mlp5 = (ViewGroup.MarginLayoutParams) settingsElem4
-		// 				.getLayoutParams();
-		// 		mlp5.setMargins((int) ((float) 210 * factX),
-		// 				(int) ((float) 285 * factY),
-		// 				(int) ((float) (640 - 360) * factX),
-		// 				(int) ((float) (400 - 325) * factY));
-
-		// 		ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) saveLoadListView
-		// 				.getLayoutParams();
-		// 		mlp.setMargins((int) ((float) 223 * factX),
-		// 				(int) ((float) 200 * factY),
-		// 				(int) ((float) (640 - 466) * factX),
-		// 				(int) ((float) (400 - 312) * factY));
-
-		// 		settingsElem1.setProgress(mainVolumen);
-		// 		settingsElem2.setProgress(mainTextSpeed);
-
-		// 		this.addContentView(saveLoadLayout,
-		// 				new android.view.ViewGroup.LayoutParams(
-		// 						android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
-		// 						android.view.ViewGroup.LayoutParams.WRAP_CONTENT));
-
-		// 		this.addContentView(settingsLayout,
-		// 				new android.view.ViewGroup.LayoutParams(
-		// 						android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
-		// 						android.view.ViewGroup.LayoutParams.WRAP_CONTENT));
-
-		// 		settingsElem4.setOnClickListener(new OnClickListener()
-		// 		{
-
-		// 			@Override
-		// 			public void onClick(View arg0)
-		// 			{
-
-		// 				String stringTutorial = "";
-		// 				String namet = AlfredActivity.destinationFolder
-		// 						+ "/tutorial.kk";
-
-		// 				byte b[] = tools.readFile(namet);
-		// 				if (playTutorial == false)// (b[0]==0)
-		// 				{
-		// 					b[0] = 0;
-		// 					playTutorial = true;
-		// 					stringTutorial = "ON";
-
-		// 					tutorial.init();
-
-		// 				} else
-		// 				{
-		// 					b[0] = 4;
-		// 					playTutorial = false;
-		// 					stringTutorial = "OFF";
-		// 				}
-
-		// 				tools.createFile(namet, b);
-
-		// 				String fiinalText = "Tutorial " + stringTutorial;
-
-		// 				settingsElem4.setText(fiinalText);
-		// 				settingsElem4.postInvalidate();
-
-		// 			}
-
-		// 		});
-
-		// 		hideList();
-		// 		hideSettings();
-
-		// 		myExtraText = new extraText();
-
-		// 		String name = AlfredActivity.destinationFolder + "/test.kk";
-		// 		File f = new File(name);
-		// 		if (f.exists() == true)
-		// 		{
-		// 			byte b[] = tools.readFile(name);
-
-		// 			if (b[0] < 3)
-		// 			{
-		// 				b[0]++;
-		// 				playIntro = true;
-		// 			} else
-		// 				playIntro = false;
-
-		// 			tools.createFile(name, b);
-
-		// 		} else
-		// 		{
-		// 			byte b[] = new byte[1];
-
-		// 			b[0] = 0;
-		// 			tools.createFile(name, b);
-		// 			playIntro = true;
-		// 		}
-
-		// 		// playIntro=true;
-		// 		if (playIntro == false)
-		// 		{
-		// 			stateGame = GAME;
-		// 		} else
-		// 		{
-
-		// 			stateGame = INTRO;
-		// 			varCheckRealTime = 0;
-		// 			getWindow().setFormat(PixelFormat.TRANSLUCENT);
-		// 			videoHolder = new myVideoView(this);
-
-		// 			videoHolder.setWH(widthScreen, heightScreen);
-
-		// 			// videoHolder.setLayoutParams(params)
-
-		// 			Uri video = Uri.parse("android.resource://" + getPackageName()
-		// 					+ "/" + R.raw.intro2); // do not add any extension
-		// 			videoHolder.setVideoURI(video);
-
-		// 			LinearLayout.LayoutParams paramsVideo = new LinearLayout.LayoutParams(
-		// 					widthScreen, heightScreen);
-
-		// 			videoOverlay vo = new videoOverlay(myContext, assetManager,
-		// 					videoHolder);
-
-		// 			this.addContentView(videoHolder, paramsVideo);// new
-		// 															// android.view.ViewGroup.LayoutParams(paramsVideo));
-
-		// 			this.addContentView(vo, paramsVideo);// new
-		// 													// android.view.ViewGroup.LayoutParams(paramsVideo));
-
-		// 			// superTime=SystemClock.uptimeMillis();
-		// 			videoHolder.start();
-
-		// 			AlfredActivity.playTrack(22, false);
-
-		// 			videoHolder.setOnTouchListener(new OnTouchListener()
-		// 			{
-		// 				@Override
-		// 				public boolean onTouch(View arg0, MotionEvent arg1)
-		// 				{
-		// 					videoHolder.setVisibility(View.GONE);
-		// 					videoHolder.stopPlayback();
-
-		// 					AlfredActivity.myHandler
-		// 							.sendEmptyMessage(AlfredActivity.INTRO_END);
-
-		// 					return false;
-		// 				}
-
-		// 			});
-
-		// 			videoHolder.setOnCompletionListener(new OnCompletionListener()
-		// 			{
-
-		// 				@Override
-		// 				public void onCompletion(MediaPlayer arg0)
-		// 				{
-		// 					wait2(4000);
-		// 				}
-
-		// 			});
-
-		// 		}
+		setScreen(0, 2);
 	}
 }
 
@@ -630,152 +129,6 @@ void PelrockEngine::playIntro() {
 
 void PelrockEngine::loadAnims() {
 	loadMainCharacterAnims();
-	// try
-	// {
-
-	// 	// andar, acciones y hablar...
-	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred1.png");
-
-	// 	for (int i = 0; i < 60; i++)
-	// 	{
-	// 		alfredBitmap[i] = Bitmap.createBitmap(mainAlfredAnim, i * 51, 0,
-	// 				51, 102);
-	// 	}
-
-	// 	mainAlfredAnim.recycle();
-
-	// 	// andar por el tunel
-	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred13.png");
-
-	// 	for (int i = 0; i < 18; i++)
-	// 	{
-	// 		alfredBitmap2[i] = Bitmap.createBitmap(mainAlfredAnim, i * 130,
-	// 				0, 130, 55);
-	// 	}
-
-	// 	mainAlfredAnim.recycle();
-
-	// 	// animacion de peinarse mirando a la izquierda...
-	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred2.png");
-
-	// 	for (int i = 0; i < 11; i++)
-	// 		alfredBitmapExtra1[i] = Bitmap.createBitmap(mainAlfredAnim,
-	// 				i * 51, 0, 51, 102);
-
-	// 	mainAlfredAnim.recycle();
-
-	// 	// animacion de peinarse mirando a la derecha...
-	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred3.png");
-
-	// 	for (int i = 0; i < 11; i++)
-	// 		alfredBitmapExtra2[i] = Bitmap.createBitmap(mainAlfredAnim,
-	// 				i * 51, 0, 51, 102);
-
-	// 	mainAlfredAnim.recycle();
-
-	// 	// animacion de leer...
-	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred4.png");
-
-	// 	for (int i = 0; i < 10; i++)
-	// 		alfredBitmapExtra3[i] = Bitmap.createBitmap(mainAlfredAnim,
-	// 				i * 51, 0, 51, 102);
-
-	// 	// animacion de electrocutarse...
-	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred5.png");
-
-	// 	for (int i = 0; i < 8; i++)
-	// 		alfredBitmapExtra4[i] = Bitmap.createBitmap(mainAlfredAnim,
-	// 				i * 82, 0, 82, 58);
-
-	// 	// animacion de cocodrilo...
-	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/crocodillo.png");
-
-	// 	for (int i = 0; i < 14; i++)
-	// 		alfredBitmapExtra5[i] = Bitmap.createBitmap(mainAlfredAnim,
-	// 				i * 171, 0, 171, 109);
-
-	// 	// animacion de escondite...
-	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred6.png");
-
-	// 	for (int i = 0; i < 12; i++)
-	// 		alfredBitmapExtra6[i] = Bitmap.createBitmap(mainAlfredAnim,
-	// 				i * 113, 0, 113, 103);
-
-	// 	// animacion de bajada al tunel...
-	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred7.png");
-
-	// 	for (int i = 0; i < 11; i++)
-	// 		alfredBitmapExtra7[i] = Bitmap.createBitmap(mainAlfredAnim,
-	// 				i * 33, 0, 33, 72);
-
-	// 	// animacion de subida del tunel...
-	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred8.png");
-
-	// 	for (int i = 0; i < 9; i++)
-	// 		alfredBitmapExtra8[i] = Bitmap.createBitmap(mainAlfredAnim,
-	// 				i * 33, 0, 33, 72);
-
-	// 	// animacion de escapada del tunel (llega al zoco)
-	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred9.png");
-
-	// 	for (int i = 0; i < 16; i++)
-	// 		alfredBitmapExtra9[i] = Bitmap.createBitmap(mainAlfredAnim,
-	// 				i * 158, 0, 158, 115);
-
-	// 	// animacion de puesta del muneco hinchable
-	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred10.png");
-
-	// 	for (int i = 0; i < 17; i++)
-	// 		alfredBitmapExtra10[i] = Bitmap.createBitmap(mainAlfredAnim,
-	// 				i * 177, 0, 177, 124);
-
-	// 	// animacion de magia (1)
-	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/magia1.png");
-
-	// 	for (int i = 0; i < 11; i++)
-	// 		alfredBitmapExtra11[i] = Bitmap.createBitmap(mainAlfredAnim,
-	// 				i * 98, 0, 98, 138);
-
-	// 	// animacion de magia (2)
-	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/magia2.png");
-
-	// 	for (int i = 0; i < 12; i++)
-	// 		alfredBitmapExtra12[i] = Bitmap.createBitmap(mainAlfredAnim,
-	// 				i * 98, 0, 98, 138);
-
-	// 	// animacion de pedras
-	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred11.png");
-
-	// 	for (int i = 0; i < 7; i++)
-	// 		alfredBitmapExtra13[i] = Bitmap.createBitmap(mainAlfredAnim,
-	// 				i * 208, 0, 208, 102);
-
-	// 	// animacion de desnudo
-	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred12.png");
-
-	// 	for (int i = 0; i < 4; i++)
-	// 		alfredBitmapExtra14[i] = Bitmap.createBitmap(mainAlfredAnim,
-	// 				i * 51, 0, 51, 102);
-
-	// 	// animacion de despertarse
-	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred14.png");
-
-	// 	for (int i = 0; i < 14; i++)
-	// 		alfredBitmapExtra15[i] = Bitmap.createBitmap(mainAlfredAnim,
-	// 				i * 71, 0, 71, 66);
-
-	// 	// animacion de tirar piedra
-	// 	mainAlfredAnim = getBitmapFromAsset("animaciones/alfred15.png");
-
-	// 	for (int i = 0; i < 4; i++)
-	// 		alfredBitmapExtra16[i] = Bitmap.createBitmap(mainAlfredAnim,
-	// 				i * 71, 0, 71, 101);
-
-	// 	mainAlfredAnim.recycle();
-
-	// } catch (IOException e)
-	// {
-	// }
 }
 
 const int EXPECTED_SIZE = 640 * 400;
@@ -973,6 +326,24 @@ Common::List<WalkBox> PelrockEngine::loadWalkboxes(Common::File *roomFile, int r
 	return walkboxes;
 }
 
+void PelrockEngine::loadCursors() {
+	Common::File alfred7File;
+	if(!alfred7File.open("ALFRED.7")) {
+		error("Couldnt find file ALFRED.7");
+	}
+	for (int i= 0; i < 5; i++) {
+		uint32_t cursorOffset = cursor_offsets[i];
+		alfred7File.seek(cursorOffset);
+		_cursorMasks[i] = new byte[kCursorSize];
+		alfred7File.read(_cursorMasks[i], kCursorSize);
+		for(int j = 0; j < kCursorSize; j++) {
+			byte *cursorMask = _cursorMasks[i];
+			if(cursorMask[j] == 255) cursorMask[j] = 0;
+		}
+	}
+	alfred7File.close();
+}
+
 Common::List<Exit> PelrockEngine::loadExits(Common::File *roomFile, int roomOffset) {
 	Common::List<Exit> exits;
 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
@@ -1050,14 +421,9 @@ void PelrockEngine::loadMainCharacterAnims() {
 }
 
 void PelrockEngine::frames() {
+
 	if (_chronoManager->_gameTick) {
-		for (uint32_t y = 0; y < kAlfredFrameHeight; y++) {
-			for (uint32_t x = 0; x < kAlfredFrameWidth; x++) {
-				unsigned int src_pos = (curAlfredFrame * kAlfredFrameHeight * kAlfredFrameWidth) + (y * kAlfredFrameWidth) + x;
-				if (standingAnim[src_pos] != 255)
-					_screen->setPixel(x + xAlfred, y + yAlfred, standingAnim[src_pos]);
-			}
-		}
+
 		int num = 0;
 
 		for (Common::List<AnimSet>::iterator i = _currentRoomAnims.begin(); i != _currentRoomAnims.end(); i++) {
@@ -1068,7 +434,6 @@ void PelrockEngine::frames() {
 			}
 
 			for (int j = 0; j < i->numAnims; j++) {
-				// debug("Drawing animation %d of set %d at (%d,%d) size (%d,%d) nframes %d", j, num, i->x, i->y, i->w, i->h, i->animData[j].nframes);
 				int x = i->animData[j].x;
 				int y = i->animData[j].y;
 				int w = i->animData[j].w;
@@ -1080,20 +445,23 @@ void PelrockEngine::frames() {
 				Common::copy(i->animData[j].animData + (curFrame * i->h * i->w), i->animData[j].animData + (curFrame * i->h * i->w) + (frameSize), frame);
 				// debug("Current frame %d of %d", curFrame, i->animData[j].nframes);
 
-				// byte *bg = new byte[frameSize];
-				// for (int j = 0; j < w; j++) {
-				// 	for (int i = 0; i < h; i++) {
-				// 		int idx = i * w + j;
-				// 		*(bg + idx) = _currentBackground[(y + i) * 640 + (x + j)];
-				// 	}
-				// }
-
-				// for (int i = 0; i < w; i++) {
-				// 	for (int j = 0; j < h; j++) {
-				// 		int index = (j * w + i);
-				// 			*(byte *)g_engine->_screen->getBasePtr(x + i, y + j) = bg[index];
-				// 	}
-				// }
+				byte *bg = new byte[frameSize];
+				for (int j = 0; j < w; j++) {
+					for (int i = 0; i < h; i++) {
+						int idx = i * w + j;
+						if(y + i < 400 && x + j < 640) {
+							*(bg + idx) = _currentBackground[(y + i) * 640 + (x + j)];
+						}
+					}
+				}
+
+				for (int i = 0; i < w; i++) {
+					for (int j = 0; j < h; j++) {
+						int index = (j * w + i);
+						if(x + i < 640 && y + j < 400)
+							*(byte *)g_engine->_screen->getBasePtr(x + i, y + j) = bg[index];
+					}
+				}
 
 				for (int y = 0; y < i->h; y++) {
 					for (int x = 0; x < i->w; x++) {
@@ -1112,6 +480,15 @@ void PelrockEngine::frames() {
 				}
 			}
 			num++;
+
+			for (uint32_t y = 0; y < kAlfredFrameHeight; y++) {
+			for (uint32_t x = 0; x < kAlfredFrameWidth; x++) {
+				unsigned int src_pos = (curAlfredFrame * kAlfredFrameHeight * kAlfredFrameWidth) + (y * kAlfredFrameWidth) + x;
+				// debug("Xpos = %d, yPos=%d", x + xAlfred, y + yAlfred);
+				if (standingAnim[src_pos] != 255)
+					_screen->setPixel(x + xAlfred, y + yAlfred, standingAnim[src_pos]);
+			}
+		}
 		}
 		_screen->markAllDirty();
 		_screen->update();
@@ -1123,6 +500,8 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 		if (x >= i->x && x <= (i->x + i->w) &&
 			y >= i->y && y <= (i->y + i->h)) {
 			debug("Clicked Exit at (%d,%d) size (%d,%d) to room %d", i->x, i->y, i->w, i->h, i->targetRoom);
+			xAlfred = i->targetX;
+			yAlfred = i->targetY - kAlfredFrameHeight;
 			setScreen(i->targetRoom, i->dir);
 			return;
 		}
@@ -1137,12 +516,16 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 	}
 }
 
+void PelrockEngine::changeCursor(Cursor cursor) {
+	CursorMan.replaceCursor(_cursorMasks[cursor], kCursorWidth, kCursorHeight, 0, 0, 0);
+}
+
 void PelrockEngine::checkMouseHover() {
 	for (Common::List<HotSpot>::iterator i = _hotspots.begin(); i != _hotspots.end(); i++) {
 		if (mouseX >= i->x && mouseX <= (i->x + i->w) &&
 			mouseY >= i->y && mouseY <= (i->y + i->h)) {
 			// _currentHotspot = &(*i);
-			// debug("Hotspot at (%d,%d) size (%d,%d) extra %d", i->x, i->y, i->w, i->h, i->extra);
+			debug("Hotspot at (%d,%d) size (%d,%d) extra %d", i->x, i->y, i->w, i->h, i->extra);
 			return;
 		}
 	}
@@ -1181,6 +564,16 @@ void PelrockEngine::setScreen(int number, int dir) {
 	_currentRoomAnims = getRoomAnimations(&roomFile, roomOffset);
 
 	loadHotspots(&roomFile, roomOffset);
+	int hotsPotCount = 0;
+	for (Common::List<HotSpot>::iterator i = _hotspots.begin(); i != _hotspots.end(); i++) {
+		// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
+		_screen->drawLine(i->x, i->y, i->x + i->w, i->y, 100 + hotsPotCount);
+		_screen->drawLine(i->x, i->y + i->h, i->x + i->w, i->y + i->h, 100 + hotsPotCount);
+		_screen->drawLine(i->x, i->y, i->x, i->y + i->h, 100 + hotsPotCount);
+		_screen->drawLine(i->x + i->w, i->y, i->x + i->w, i->y + i->h, 100 + hotsPotCount);
+		hotsPotCount++;
+	}
+
 	Common::List<WalkBox> walkboxes = loadWalkboxes(&roomFile, roomOffset);
 	int walkboxCount = 0;
 	for (Common::List<WalkBox>::iterator i = walkboxes.begin(); i != walkboxes.end(); i++) {
@@ -1209,1003 +602,6 @@ void PelrockEngine::setScreen(int number, int dir) {
 	delete[] palette;
 }
 
-void PelrockEngine::setScreenJava(int s, int dir) {
-	screenReady = false;
-	dirAlfred = dir;
-
-	// 		vueltaALaCarcel = false;
-	// 		timeToGoToPiss = -1;
-	// 		myoverlay.shakeScreen = false;
-	// 		walkingAgain = false;
-
-	// 		myoverlay.indexCicleColores = 0;
-
-	// 		stringToShowOnTutorial = "";
-
-	// 		magicWords = false;
-
-	// 		myMovingAlfredThread.isOtherTalking = false;
-	// 		isPersonajeTalking = -1;
-	// 		myMovingAlfredThread.showTextNowOtros = false;
-
-	// 		// pantalla................
-	// 		if (screenBitmap != null)
-	// 		{
-	// 			screenBitmap.recycle();
-	// 			screenBitmap = null;
-	// 		}
-
-	prevWhichScreen = whichScreen;
-
-	whichScreen = s;
-
-	// 		objetos.getObjectList(assetManager, s);
-	// 		objectToShow = "";
-
-	Common::String nameScreen = Common::String::format("pantallas/pantalla%d.png", s);
-	Common::String nameCamino = Common::String::format("caminos/pantalla%d.txt", s);
-
-	Common::File screen;
-	if (!screen.open(Common::Path(nameScreen))) {
-		error("Could not find pantalla!");
-	}
-	decoder->loadStream(screen);
-	Graphics::Palette palette = decoder->getPalette();
-	g_system->getPaletteManager()->setPalette(palette);
-	const Graphics::Surface *surf = decoder->getSurface();
-	if (!surf)
-		error("No surface");
-
-	g_engine->_screen->blitFrom(*surf);
-
-	_screen->markAllDirty();
-
-	// 		Config c = null;
-	// 		try
-	// 		{
-	// 			screenBitmap = getBitmapFromAsset(nameScreen);
-
-	// 			c = screenBitmap.getConfig();
-	// 			screenBitmap = screenBitmap.copy(c, true);
-	// 		} catch (IOException e)
-	// 		{
-	// 		}
-
-	Common::String nameColorsx = Common::String::format("colors%d.png", s);
-	Common::String nameColors = "colors/" + nameColorsx;
-	// 		try
-	// 		{
-	// 			screenColors = getBitmapFromAsset(nameColors);
-	// 		} catch (IOException e)
-	// 		{
-	// 			screenColors = null;
-	// 		}
-	// 		if (screenColors != null)
-	// 			setFakePalette(nameColorsx, screenColors);
-
-	Common::String nameShadows = Common::String::format("shadows/shadows_%d.png", s); // <-- fixed: include s
-	Common::File shadows;
-	if (shadows.open(Common::Path(nameShadows)) != Common::kNoError) {
-		error("Error opening shadows: %s", nameShadows.c_str());
-		return;
-	}
-	if (decoder->loadStream(shadows) != Common::kNoError) {
-		error("Decoder failed loading shadows: %s", nameShadows.c_str());
-		return;
-	}
-	const Graphics::Surface *shadowSurf = decoder->getSurface();
-	if (!shadowSurf) {
-		error("No shadow surface for %s", nameShadows.c_str());
-		return;
-	}
-	Common::copy((byte *)shadowSurf->getPixels(), (byte *)shadowSurf->getPixels() + 640 * 400, pixelsShadows);
-
-	// 		try
-	// 		{
-	// 			shadowColors = getBitmapFromAsset(nameShadows);
-	// 		} catch (IOException e)
-	// 		{
-	// 			shadowColors = null;
-	// 		}
-
-	// 		shadowColors.getPixels(pixelsShadows, 0, 640, 0, 0, 640, 400);
-
-	Common::File caminos;
-	if (!caminos.open(Common::Path(nameCamino))) {
-		error("Cant find camino");
-	}
-
-	// 		// caminos................
-	// 		BufferedReader br = null;
-	// 		List<String> wordList = new ArrayList<String>();
-
-	// 		try
-	// 		{
-	// 			br = new BufferedReader(
-	// 					new InputStreamReader(assetManager.open(nameCamino)));
-	// 			String word;
-	// 			while ((word = br.readLine()) != null)
-	// 				wordList.add(word);
-	// 		} catch (IOException e)
-	// 		{
-	// 		}
-
-	// 		paths.caminos = new defCam();
-
-	// 		int k = wordList.size();
-
-	// 		List<rectCam> provList = new LinkedList<rectCam>();
-	// 		provList.clear();
-
-	// 		List<conex> provListCon = new LinkedList<conex>();
-	// 		provListCon.clear();
-
-	// 		List<anims> provListAnims = new LinkedList<anims>();
-	// 		provListAnims.clear();
-
-	// 		myScale = null;
-
-	// 		scale aux4 = null;
-
-	// 		String line;
-	// 		int ind = -1;
-	// 		int prevInd = 0;
-	// 		int which = 0;
-
-	// 		for (int i = 0; i < k; i++)
-	// 		{
-
-	// 			line = wordList.get(i);
-
-	// 			if (line.compareTo("paths") == 0)
-	// 				which = 0;
-	// 			else if (line.compareTo("conexions") == 0)
-	// 				which = 1;
-	// 			else if (line.compareTo("anims") == 0)
-	// 				which = 2;
-	// 			else if (line.compareTo("scale") == 0)
-	// 				which = 3;
-	// 			else
-	// 			{
-
-	// 				switch (which)
-	// 				{
-	// 				case 0:
-
-	// 					rectCam aux = new rectCam();
-
-	// 					ind = line.indexOf(',', ind + 1);
-	// 					aux.x = (int) (Float
-	// 							.parseFloat(line.substring(prevInd, ind)) * factX);
-	// 					prevInd = ind + 1;
-
-	// 					ind = line.indexOf(',', ind + 1);
-	// 					aux.y = (int) (Float
-	// 							.parseFloat(line.substring(prevInd, ind)) * factY);
-	// 					prevInd = ind + 1;
-
-	// 					ind = line.indexOf(',', ind + 1);
-	// 					aux.w = (int) (Float
-	// 							.parseFloat(line.substring(prevInd, ind)) * factX);
-	// 					prevInd = ind + 1;
-
-	// 					ind = line.indexOf(',', ind + 1);
-	// 					aux.h = (int) (Float.parseFloat(line.substring(prevInd))
-	// 							* factY);
-	// 					prevInd = ind + 1;
-
-	// 					ind = -1;
-	// 					prevInd = 0;
-
-	// 					provList.add(aux);
-
-	// 				break;
-
-	// 				case 1:
-
-	// 					conex aux2 = new conex();
-
-	// 					ind = line.indexOf(',', ind + 1);
-	// 					aux2.x = (int) (Float
-	// 							.parseFloat(line.substring(prevInd, ind)) * factX);
-	// 					prevInd = ind + 1;
-
-	// 					ind = line.indexOf(',', ind + 1);
-	// 					aux2.y = (int) (Float
-	// 							.parseFloat(line.substring(prevInd, ind)) * factY);
-	// 					prevInd = ind + 1;
-
-	// 					ind = line.indexOf(',', ind + 1);
-	// 					aux2.which = (int) (Integer
-	// 							.parseInt(line.substring(prevInd, ind)));
-	// 					prevInd = ind + 1;
-
-	// 					ind = line.indexOf(',', ind + 1);
-	// 					aux2.ready = (int) (Integer
-	// 							.parseInt(line.substring(prevInd, ind)));
-	// 					prevInd = ind + 1;
-
-	// 					ind = line.indexOf(',', ind + 1);
-	// 					aux2.nx = (int) (Float
-	// 							.parseFloat(line.substring(prevInd, ind)) * factX);
-	// 					prevInd = ind + 1;
-
-	// 					ind = line.indexOf(',', ind + 1);
-	// 					aux2.ny = (int) (Float
-	// 							.parseFloat(line.substring(prevInd, ind)) * factY);
-	// 					prevInd = ind + 1;
-
-	// 					ind = line.indexOf(',', ind + 1);
-	// 					aux2.ndir = (int) (Integer
-	// 							.parseInt(line.substring(prevInd)));
-	// 					prevInd = ind + 1;
-
-	// 					ind = -1;
-	// 					prevInd = 0;
-
-	// 					provListCon.add(aux2);
-	// 				break;
-
-	// 				case 2:
-
-	// 					anims aux3 = new anims();
-
-	// 					aux3.activated = true;
-
-	// 					ind = line.indexOf(',', ind + 1);
-	// 					aux3.x = (int) (Integer
-	// 							.parseInt(line.substring(prevInd, ind)) * factX);
-	// 					aux3.backupX = aux3.x;
-	// 					prevInd = ind + 1;
-
-	// 					ind = line.indexOf(',', ind + 1);
-	// 					aux3.y = (int) (Integer
-	// 							.parseInt(line.substring(prevInd, ind)) * factY);
-	// 					aux3.backupY = aux3.y;
-	// 					prevInd = ind + 1;
-
-	// 					ind = line.indexOf(',', ind + 1);
-	// 					aux3.moving = (boolean) (Boolean
-	// 							.parseBoolean((line.substring(prevInd, ind))));
-	// 					prevInd = ind + 1;
-
-	// 					ind = line.indexOf(',', ind + 1);
-	// 					aux3.frames = (int) (Integer
-	// 							.parseInt(line.substring(prevInd, ind)));
-	// 					prevInd = ind + 1;
-
-	// 					ind = line.indexOf(',', ind + 1);
-	// 					if (ind == -1)
-	// 						aux3.speed = (int) (Integer
-	// 								.parseInt(line.substring(prevInd)));
-	// 					else
-	// 						aux3.speed = (int) (Integer
-	// 								.parseInt(line.substring(prevInd, ind)));
-	// 					prevInd = ind + 1;
-
-	// 					aux3.whichFrame = 0;
-	// 					aux3.nSpeed = 0;
-
-	// 					if (ind != -1)// otros datos en las animaciones...
-	// 					{
-	// 						int kk;
-	// 						if (aux3.moving == false)// los datos extras son de
-	// 													// animaciones 'combinadas'
-	// 						{
-
-	// 							ind = line.indexOf(',', ind + 1);
-	// 							kk = (int) (Integer
-	// 									.parseInt(line.substring(prevInd, ind)));
-	// 							prevInd = ind + 1;
-
-	// 							aux3.animParts = new parts[kk];
-	// 							aux3.time = 0;
-	// 							aux3.whichPart = 0;
-
-	// 							for (int j = 0; j < kk; j++)
-	// 							{
-	// 								aux3.animParts[j] = new parts();
-
-	// 								ind = line.indexOf(',', ind + 1);
-	// 								aux3.animParts[j].nframes = (int) (Integer
-	// 										.parseInt(
-	// 												line.substring(prevInd, ind)));
-	// 								prevInd = ind + 1;
-
-	// 								ind = line.indexOf(',', ind + 1);
-	// 								if (ind == -1)
-	// 									aux3.animParts[j].duration = (int) (Integer
-	// 											.parseInt(line.substring(prevInd)));
-	// 								else
-	// 									aux3.animParts[j].duration = (int) (Integer
-	// 											.parseInt(line.substring(prevInd,
-	// 													ind)));
-
-	// 								prevInd = ind + 1;
-
-	// 							}
-
-	// 						} else
-	// 						{
-
-	// 							ind = line.indexOf(',', ind + 1);
-	// 							kk = (int) (Integer
-	// 									.parseInt(line.substring(prevInd, ind)));
-	// 							prevInd = ind + 1;
-
-	// 							aux3.incAnims = new movements[kk];
-
-	// 							aux3.whichMovement = 0;
-	// 							aux3.isMovingAlready = false;
-
-	// 							ind = line.indexOf(',', ind + 1);
-	// 							aux3.startMoving = (int) (Integer
-	// 									.parseInt(line.substring(prevInd, ind)));
-	// 							prevInd = ind + 1;
-
-	// 							for (int j = 0; j < kk; j++)
-	// 							{
-	// 								aux3.incAnims[j] = new movements();
-
-	// 								ind = line.indexOf(',', ind + 1);
-	// 								aux3.incAnims[j].ix = (float) (Float.parseFloat(
-	// 										line.substring(prevInd, ind)));
-	// 								prevInd = ind + 1;
-
-	// 								ind = line.indexOf(',', ind + 1);
-	// 								aux3.incAnims[j].iy = (float) (Float.parseFloat(
-	// 										line.substring(prevInd, ind)));
-	// 								prevInd = ind + 1;
-
-	// 								ind = line.indexOf(',', ind + 1);
-	// 								if (ind == -1)
-	// 									aux3.incAnims[j].duration = (int) (Integer
-	// 											.parseInt(line.substring(prevInd)));
-	// 								else
-	// 									aux3.incAnims[j].duration = (int) (Integer
-	// 											.parseInt(line.substring(prevInd,
-	// 													ind)));
-	// 								prevInd = ind + 1;
-
-	// 								aux3.incAnims[j].nduration = 0;
-
-	// 							}
-
-	// 						}
-
-	// 					}
-
-	// 					ind = -1;
-	// 					prevInd = 0;
-
-	// 					provListAnims.add(aux3);
-
-	// 				break;
-
-	// 				case 3:
-
-	// 					aux4 = new scale();
-
-	// 					ind = line.indexOf(',', ind + 1);
-	// 					aux4.miny = (int) (Float
-	// 							.parseFloat(line.substring(prevInd, ind)) * factY);
-	// 					prevInd = ind + 1;
-
-	// 					ind = line.indexOf(',', ind + 1);
-	// 					aux4.factmin = (int) (Integer
-	// 							.parseInt(line.substring(prevInd, ind)));
-	// 					prevInd = ind + 1;
-
-	// 					ind = line.indexOf(',', ind + 1);
-	// 					aux4.maxy = (int) (Float
-	// 							.parseFloat(line.substring(prevInd, ind)) * factY);
-	// 					prevInd = ind + 1;
-
-	// 					ind = line.indexOf(',', ind + 1);
-	// 					aux4.factmax = (int) (Integer
-	// 							.parseInt(line.substring(prevInd)));
-	// 					prevInd = ind + 1;
-
-	// 					prevInd += 0;
-	// 				break;
-	// 				}
-
-	// 			}
-
-	// 		}
-
-	// 		// myAnims
-
-	// 		int realPaths = provList.size();
-
-	// 		paths.caminos.cams = new rectCam[realPaths];
-
-	// 		int t;
-	// 		for (t = 0; t < realPaths; t++)
-	// 		{
-	// 			paths.caminos.cams[t] = new rectCam();
-	// 			paths.caminos.cams[t] = provList.get(t);
-	// 		}
-
-	// 		int realConex = provListCon.size();
-
-	// 		myConex = new conex[realConex];
-	// 		for (t = 0; t < realConex; t++)
-	// 		{
-	// 			myConex[t] = new conex();
-	// 			myConex[t] = provListCon.get(t);
-	// 		}
-
-	// 		int realAnims = provListAnims.size();
-
-	// 		bitmapAnims = new Bitmap[realAnims];
-
-	// 		try
-	// 		{
-	// 			myAnims = new anims[realAnims];
-	// 			for (t = 0; t < realAnims; t++)
-	// 			{
-
-	// 				myAnims[t] = new anims();
-	// 				myAnims[t] = provListAnims.get(t);
-
-	// 				bitmapAnims[t] = getBitmapFromAsset(
-	// 						"animaciones/anim" + String.valueOf(whichScreen)
-	// 								+ String.valueOf(t) + ".png");
-
-	// 				myAnims[t].realWFrame = bitmapAnims[t].getWidth();
-	// 				myAnims[t].realHFrame = bitmapAnims[t].getHeight();
-
-	// 				myAnims[t].w = (int) ((float) bitmapAnims[t].getWidth()
-	// 						* factX);
-	// 				myAnims[t].h = (int) ((float) bitmapAnims[t].getHeight()
-	// 						* factY);
-
-	// 				myAnims[t].predOrder = t;
-	// 			}
-	// 		} catch (Exception e)
-	// 		{
-	// 		}
-
-	// 		for (int i = 0; i < realPaths; i++)
-	// 		{
-	// 			paths.caminos.cams[i].vecinos = new LinkedList<Integer>();
-	// 			paths.caminos.cams[i].index = i;
-	// 			paths.caminos.cams[i].marked = false;
-
-	// 			for (int j = 0; j < realPaths; j++)
-	// 			{
-	// 				if ((i != j) && (paths.esVecino(i, j)))
-	// 					paths.caminos.cams[i].vecinos.add(j);
-	// 			}
-	// 		}
-
-	// 		if (aux4 != null)
-	// 		{
-	// 			myScale = aux4;
-	// 		}
-
-	// 		checkPegatinas(whichScreen);
-	// 		checkObjects(whichScreen);
-
-	// 		myConversaciones.loadConversations(whichScreen);
-
-	// 		// a ver si alguna conversacion esta cambiada
-	// 		if (myConversaciones.mytalkingAnims[whichScreen].myPersonajes != null)
-	// 		{
-
-	// 			int x = myConversaciones.mytalkingAnims[whichScreen].myPersonajes.length;
-
-	// 			for (int i = 0; i < x; i++)
-	// 				myConversaciones.mytalkingAnims[whichScreen].myPersonajes[i].currentConversationEach = 0;
-
-	// 		}
-
-	// 		int n = myListChangesConvs.size();
-	// 		for (int i = 0; i < n; i++)
-	// 		{
-	// 			changesConvs aux = new changesConvs();
-	// 			aux = myListChangesConvs.get(i);
-
-	// 			if (whichScreen == aux.screen)
-	// 			{
-	// 				myConversaciones.mytalkingAnims[whichScreen].myPersonajes[aux.which].currentConversationEach = aux.n;// .setConversation(whichScreen,
-	// 																														// aux.which,
-	// 																														// aux.n);
-	// 			}
-	// 		}
-
-	// 		myoverlay.isFadedOut = false;
-
-	// 		// a ver si se ha desactivado alguna animacion
-	// 		n = myListDesactAnims.size();
-
-	// 		for (int i = 0; i < n; i++)
-	// 		{
-	// 			dactAnims aux = new dactAnims();
-	// 			aux = myListDesactAnims.get(i);
-
-	// 			if (whichScreen == aux.screen)
-	// 				myAnims[aux.which].activated = false;
-	// 		}
-
-	// 		// personajes pe[];
-	// 		personajes pe[] = AlfredActivity.myConversaciones.mytalkingAnims[whichScreen].myPersonajes;
-	// 		// lista de cosas sueltas a hacer...
-
-	// 		pintaPeriodicoDandoVueltas = false;
-	// 		int j;
-	// 		switch (whichScreen)
-	// 		{
-
-	// 		case 51:
-	// 			desactivaAnims(51, 0);
-	// 			desactivaAnims(51, myAnims.length - 1);
-	// 			resetDiosesTime();
-	// 		break;
-
-	// 		case 52:
-	// 			desactivaAnims(52, 0);
-	// 			desactivaAnims(52, 1);
-	// 			desactivaAnims(52, 2);
-	// 			desactivaAnims(52, 3);
-	// 			desactivaAnims(52, 4);
-	// 			desactivaAnims(52, 5);
-	// 			ponPegatina(52, 145);
-	// 			desactivaAnims(52, myAnims.length - 1);
-	// 			resetDiosesTime();
-	// 		break;
-
-	// 		case 53:
-	// 			desactivaAnims(53, 0);
-	// 			desactivaAnims(53, myAnims.length - 1);
-	// 			resetDiosesTime();
-	// 		break;
-
-	// 		case 54:
-	// 			desactivaAnims(54, 0);
-	// 			desactivaAnims(54, myAnims.length - 1);
-	// 			resetDiosesTime();
-	// 		break;
-
-	// 		case 0:
-	// 			if ((readExtraVariable(VUELTA_A_EMPEZAR) == 1)
-	// 					&& (readExtraVariable(FROM_INTRO) == 0))
-	// 			{
-	// 				setExtraVariables(AlfredActivity.VUELTA_A_EMPEZAR, 2);
-	// 				myoverlay.setAnimExtra(14, 15, 21, 71, 66, 0, 0);
-	// 				myMovingAlfredThread.showTextNow = true;
-	// 				whichObject = 0;
-	// 				textToShow = extraThingsToTranslate[117];
-
-	// 			}
-
-	// 		break;
-
-	// 		case 48:
-
-	// 			AlfredActivity.myMovingAlfredThread.saySomethingOther(
-	// 					AlfredActivity.extraThingsToTranslate[115], 0);
-	// 			setExtraVariables(AlfredActivity.A_POR_LA_PRINCESA, 1);
-
-	// 			desactivaAnims(48, 7);
-
-	// 		break;
-
-	// 		case 44:
-
-	// 			if (readExtraVariable(AlfredActivity.PIEDRA_FAKE_MOJADA) == 3)
-	// 				updateConexion(55, 1);
-
-	// 		break;
-
-	// 		case 41:
-	// 			j = getOrderAnims(2);
-	// 			myAnims[j].activated = false;
-
-	// 			j = getOrderAnims(3);
-	// 			myAnims[j].activated = false;
-
-	// 			if (readExtraVariable(AlfredActivity.GUARDIAS_BORRACHOS) == 1)
-	// 				updateConexion(43, 1);
-
-	// 		break;
-
-	// 		case 40:
-
-	// 			finalObject = 0;
-	// 			whichObject = 0;
-	// 			actionToDo = 1;
-	// 			myMovingAlfredThread.stateOfTalking = 0;
-	// 			gotoPos(290, 370, 3);
-	// 			whichAction = objetos.HABLAR;
-	// 			afterMovingAlfred = ALFRED_TALKING;
-	// 			posibilityOfFakeAnswer = false;
-	// 			varCheckRealTime = -1;
-
-	// 		break;
-
-	// 		case 39:
-
-	// 			j = getOrderAnims(0);
-	// 			myAnims[j].activated = false;
-
-	// 			j = getOrderAnims(7);
-	// 			myAnims[j].activated = false;
-
-	// 			j = getOrderAnims(8);
-	// 			myAnims[j].activated = false;
-
-	// 			j = getOrderAnims(9);
-	// 			myAnims[j].activated = false;
-
-	// 			j = getOrderAnims(10);
-	// 			myAnims[j].activated = false;
-
-	// 		break;
-
-	// 		case 34:
-	// 			if (readExtraVariable(AlfredActivity.VIGILANTE_PAJEANDOSE) == 1)
-	// 			{
-	// 				int i = getOrderAnims(0);
-	// 				myAnims[i].activated = false;
-	// 				updateConexion(35, 1);
-
-	// 			}
-	// 		break;
-
-	// 		case 21:
-
-	// 			if ((prevWhichScreen == 36)
-	// 					&& (readExtraVariable(AlfredActivity.PIRAMIDE_JODIDA) == 1))
-	// 			{
-	// 				updateConexion(36, 0);
-	// 				AlfredActivity.ponPegatina(21, 79);
-	// 				AlfredActivity.addPegatina(21, 79);
-	// 			}
-
-	// 		break;
-
-	// 		case 36:
-
-	// 			byte z = readExtraVariable(AlfredActivity.PIRAMIDE_JODIDA);
-	// 			if (z >= 3)
-	// 				setExtraVariables(AlfredActivity.PIRAMIDE_JODIDA, 1);
-
-	// 			int i;
-
-	// 			if (readExtraVariable(AlfredActivity.PIRAMIDE_JODIDA) == 1) // se ha
-	// 																		// cargado
-	// 																		// la
-	// 																		// piramide
-	// 			{
-
-	// 				i = getOrderAnims(0);
-	// 				myAnims[i].x = (int) (328.0f * factX);
-	// 				myAnims[i].y = (int) (209.0f * factY);
-	// 				myAnims[i].moving = false;
-	// 				myAnims[i].incAnims = null;// new movements[1];
-
-	// 				myAnims[i].speed = 1000000;
-
-	// 				i = getOrderAnims(2);
-	// 				myAnims[i].activated = true;
-	// 				myAnims[i].speed = 16;
-	// 			} else// if (prevWhichScreen==21)//no ha hecho nada
-	// 			{
-	// 				i = getOrderAnims(0);
-
-	// 				if (prevWhichScreen == 21)
-	// 				{
-	// 					myAnims[i].activated = true;
-	// 					myAnims[i].speed = 1000000;
-	// 					updateConexion(37, 0);
-	// 				} else
-	// 				{
-	// 					myAnims[i].activated = false;
-	// 					updateConexion(37, 1);
-	// 				}
-
-	// 				i = getOrderAnims(2);
-	// 				myAnims[i].activated = false;
-
-	// 			}
-
-	// 		break;
-
-	// 		case 26:
-
-	// 			mypersonajes = pe[0];
-
-	// 			if (myListUsingObjects.contains(82) == false)// no tiene dinero
-	// 				mypersonajes.setConversation(26, 0, 0);
-	// 			else
-	// 				mypersonajes.setConversation(26, 0, 1);
-
-	// 			mypersonajes = pe[1];
-	// 			mypersonajes.setConversation(26, 1, 0);
-
-	// 			if ((AlfredActivity
-	// 					.readExtraVariable(AlfredActivity.A_LA_CARCEL) == 1)
-	// 					&& (prevWhichScreen == 27)
-	// 					&& (AlfredActivity.readExtraVariable(
-	// 							AlfredActivity.SE_HA_PUESTO_EL_MUNECO) == 0))
-	// 			{
-	// 				mypersonajes = pe[1];
-	// 				mypersonajes.setConversation(26, 1, 1);
-	// 				// myAnims[0].activated=true;
-	// 				finalObject = 1;
-	// 				whichObject = 1;
-	// 				actionToDo = 1;
-	// 				myMovingAlfredThread.stateOfTalking = 0;
-	// 				gotoPos(314, 240, 3);
-	// 				whichAction = objetos.HABLAR;// .h
-	// 				afterMovingAlfred = ALFRED_TALKING;
-	// 				posibilityOfFakeAnswer = false;
-	// 				varCheckRealTime = -1;
-	// 			}
-	// 			if ((AlfredActivity
-	// 					.readExtraVariable(AlfredActivity.A_LA_CARCEL) == 1)
-	// 					&& (prevWhichScreen == 27)
-	// 					&& (AlfredActivity.readExtraVariable(
-	// 							AlfredActivity.SE_HA_PUESTO_EL_MUNECO) == 1))
-	// 			{
-	// 				mypersonajes = pe[1];
-	// 				mypersonajes.setConversation(26, 1, 2);
-	// 			}
-
-	// 		break;
-
-	// 		case 27:
-	// 			if (prevWhichScreen == 33)// viene del tunel
-	// 			{
-	// 				xAlfred = 90.0f * factX;
-	// 				yAlfred = 380.0f * factY;
-	// 				myoverlay.setAnimExtra(16, 9, 12, 158, 115, 0, 0);
-	// 			}
-
-	// 			// mypersonajes = pe[0];
-
-	// 			if (myListUsingObjects.contains(82) == false)
-	// 			{
-	// 				mypersonajes = pe[0];
-	// 				mypersonajes.setConversation(27, 0, 0);
-	// 				mypersonajes = pe[1];
-	// 				mypersonajes.setConversation(27, 1, 0);
-	// 			} else
-	// 			{
-	// 				mypersonajes = pe[0];
-	// 				mypersonajes.setConversation(27, 0, 1);
-	// 				mypersonajes = pe[1];
-	// 				mypersonajes.setConversation(27, 1, 1);
-	// 			}
-
-	// 		break;
-
-	// 		case 32:
-	// 			if (prevWhichScreen == 31)// viene de la carcel
-	// 			{
-	// 				xAlfred = 321.0f * factX;
-	// 				yAlfred = 125.0f * factY;
-	// 				myoverlay.setAnimExtra(11, 7, 9, 33, 72, 0, 0);
-	// 				AlfredActivity.myConversaciones.mytalkingAnims[31].myPersonajes[0]
-	// 						.setConversation(31, 0, 3);
-	// 				// mypersonajes = pe[0];
-	// 				// mypersonajes.setConversation(31, 0, 3);
-	// 			}
-
-	// 		break;
-
-	// 		case 38:
-	// 			if (AlfredActivity
-	// 					.readExtraVariable(AlfredActivity.ROBA_PELO_PRINCESA) == 0)
-	// 			{
-	// 				xAlfred = 230.0f * factX;
-	// 				yAlfred = 283.0f * factY;
-	// 				myoverlay.setAnimExtra(12, 6, 8, 113, 103, -32, 15);
-	// 			}
-	// 		break;
-	// 		case 30:
-	// 			myAnims[0].activated = false;
-	// 			myAnims[4].activated = false;
-
-	// 			if ((readExtraVariable(PUERTA_SECRETA_ABIERTA) == 1)
-	// 					&& (AlfredActivity.readExtraVariable(
-	// 							AlfredActivity.ROBA_PELO_PRINCESA) != 2))
-	// 				updateConexion(38, 1);
-
-	// 			if (AlfredActivity
-	// 					.readExtraVariable(AlfredActivity.ROBA_PELO_PRINCESA) == 1)
-	// 			{
-
-	// 				setExtraVariables(AlfredActivity.ROBA_PELO_PRINCESA, 2);
-	// 				myAnims[0].activated = true;
-	// 				finalObject = 0;
-	// 				actionToDo = 0;
-	// 				myMovingAlfredThread.stateOfTalking = 0;
-	// 				gotoPos(391, 344, 1);
-	// 				whichAction = objetos.HABLAR;// .h
-	// 				afterMovingAlfred = ALFRED_TALKING;
-	// 				posibilityOfFakeAnswer = false;
-	// 				varCheckRealTime = -1;
-	// 			}
-	// 		break;
-
-	// 		case 4:
-	// 			if (readExtraVariable(ELECTROCUTACION) == 0)
-	// 			{
-	// 				setExtraVariables(AlfredActivity.ELECTROCUTACION, 1);
-	// 				hideObject(7);
-	// 			}
-	// 			if (readExtraVariable(AlfredActivity.CABLES_PUESTOS) == 3)
-	// 				showObject(8);
-	// 			else
-	// 				hideObject(8);
-	// 		case 8:
-	// 		case 14:
-	// 			if (AlfredActivity.readExtraVariable(
-	// 					AlfredActivity.VENDEDOR_DEJA_DE_JODER) == 1)
-	// 			{
-	// 				paths.caminos.cams[0].w = (int) (599.0f * AlfredActivity.factX);
-	// 				AlfredActivity.updateConexion(16, 1);
-	// 			}
-	// 		case 16:
-	// 		case 19:
-
-	// 			if ((readExtraVariable(PUESTA_SALSA_PICANTE_EN_MENU) == 1)
-	// 					&& (readExtraVariable(JEFE_ENCARCELADO) == 0)
-	// 					&& (Math.random() < 0.25f))
-	// 			{
-	// 				// muestra periodico, quita al jefe, etc...
-	// 				setExtraVariables(AlfredActivity.JEFE_ENCARCELADO, 1);
-	// 				AlfredActivity.addObject(13, 0, false);// anula de que se pueda
-	// 														// clickar al jefe
-	// 				AlfredActivity.desactivaAnims(13, 0);
-	// 				AlfredActivity.desactivaAnims(13, 1);
-
-	// 				myConversaciones.mytalkingAnims[12].myPersonajes[0]
-	// 						.setConversation(12, 0, 1);// .currentConversationEach[aux.which]=aux.n;//.setConversation(whichScreen,
-	// 													// aux.which, aux.n);
-
-	// 				String x = "extrascreens/"
-	// 						+ extraScreens[(1 * myLanguages.length)
-	// 								+ whichLanguageInt]
-	// 						+ ".png";
-
-	// 				try
-	// 				{
-	// 					periodico = getBitmapFromAsset(x);
-	// 				} catch (IOException e)
-	// 				{
-	// 				}
-
-	// 				Config cx = periodico.getConfig();
-	// 				periodico = periodico.copy(cx, true);
-
-	// 				pintaPeriodicoDandoVueltas = true;
-
-	// 			}
-
-	// 		break;
-	// 		case 9:
-	// 			// personajes pe[] =
-	// 			// AlfredActivity.myConversaciones.mytalkingAnims[whichScreen].myPersonajes;
-	// 			mypersonajes = pe[0];
-
-	// 			if (myListUsingObjects.contains(10) == false)
-	// 				mypersonajes.setConversation(9, 0, 0);
-	// 			else
-	// 				mypersonajes.setConversation(9, 0, 3);
-	// 		break;
-	// 		case 20:
-	// 			// personajes pe[] =
-	// 			// AlfredActivity.myConversaciones.mytalkingAnims[whichScreen].myPersonajes;
-	// 			mypersonajes = pe[0];
-	// 			if (myListUsingObjects.contains(75) == true)
-	// 				mypersonajes.setConversation(20, 0, 2);
-	// 			else
-	// 			{
-	// 				if (myListUsingObjects.contains(59) == true)// &&
-	// 															// (myListUsingObjects.contains(59)==true))
-	// 					mypersonajes.setConversation(20, 0, 1);
-	// 				else
-	// 					mypersonajes.setConversation(20, 0, 0);
-	// 			}
-	// 		break;
-	// 		case 28:
-	// 			if (AlfredActivity.readExtraVariable(
-	// 					AlfredActivity.CROCODILLO_ENCENDIDO) == 1)
-	// 			{
-	// 				hideObject(0);
-	// 				showObject(1);
-	// 				showObject(2);
-	// 				showObject(3);
-	// 				showObject(4);
-	// 				myoverlay.isFadedOut = false;
-	// 			} else
-	// 			{
-	// 				hideObject(1);
-	// 				hideObject(2);
-	// 				hideObject(3);
-	// 				hideObject(4);
-	// 				myoverlay.isFadedOut = true;
-	// 			}
-	// 		break;
-
-	// 		}
-
-	// 		if (whichScreen == 55)
-	// 		{
-	// 			if (prevWhichScreen == 44)
-	// 				myMovingAlfredThread.alfredFrame = 0;
-	// 			else
-	// 				myMovingAlfredThread.alfredFrame = 17;
-	// 		} else
-	// 		{
-	// 			switch (dirAlfred)
-	// 			{
-	// 			case 0:
-	// 				myMovingAlfredThread.alfredFrame = 0;
-	// 			break;
-	// 			case 1:
-	// 				myMovingAlfredThread.alfredFrame = 9;
-	// 			break;
-	// 			case 2:
-	// 				myMovingAlfredThread.alfredFrame = 18;
-	// 			break;
-	// 			case 3:
-	// 				myMovingAlfredThread.alfredFrame = 23;
-	// 			break;
-	// 			}
-
-	// 		}
-
-	// 		BufferedReader brfx;
-	// 		String word;
-	// 		List<String> wordListfx = new ArrayList<String>();
-	// 		wordListfx.clear();
-	// 		String name = "fx/screen" + String.valueOf(whichScreen) + ".txt";
-
-	// 		try
-	// 		{
-	// 			brfx = new BufferedReader(
-	// 					new InputStreamReader(assetManager.open(name)));
-	// 			while ((word = brfx.readLine()) != null)
-	// 				wordListfx.add(word);
-	// 		} catch (IOException e)
-	// 		{
-	// 		}
-
-	// 		int size = wordListfx.size();
-	// 		filelistFX = new String[size];
-
-	// 		for (int i = 0; i < size; i++)
-	// 		{
-	// 			filelistFX[i] = new String();
-	// 			filelistFX[i] = wordListfx.get(i) + ".mp3";
-	// 		}
-
-	// 		if (filelistFX.length > 0)
-	// 		{
-	// 			int off = (int) (10000.0f + (Math.random() * 5000.0f));
-	// 			timeFXtoPlay = SystemClock.uptimeMillis() + off;
-	// 		}
-
-	// 		boolean loop;
-	// 		if (whichScreen != 48)
-	// 			loop = true;
-	// 		else
-	// 			loop = false;
-
-	// 		playTrack(whichScreen, loop);
-
-	// 		fundidoTime = false;
-
-	// 		timeStartScreen = SystemClock.uptimeMillis();
-
-	// 		screenReady = true;
-}
-
 Common::Error PelrockEngine::syncGame(Common::Serializer &s) {
 	// The Serializer has methods isLoading() and isSaving()
 	// if you need to specific steps; for example setting
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 62b3cdeeea1..cf0c5c80bb4 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -57,6 +57,7 @@ private:
 	void setScreen(int s, int dir);
 	void setScreenJava(int s, int dir);
 	void loadAnims();
+
 	// Room data
 	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
@@ -65,11 +66,14 @@ private:
 	void loadMainCharacterAnims();
 	Common::List<Exit> loadExits(Common::File *roomFile, int roomOffset);
 	Common::List<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
+	void loadCursors();
+
 
 	// render loop
 	void frames();
 	void checkMouseHover();
 	void checkMouseClick(int x, int y);
+	void changeCursor(Cursor cursor);
 
 	ChronoManager *_chronoManager = nullptr;
 	byte *standingAnim = new byte[3060 * 102];
@@ -81,6 +85,7 @@ private:
 	uint16 mouseX = 0;
 	uint16 mouseY = 0;
 	byte *_currentBackground = nullptr;
+	byte *_cursorMasks[5] = { nullptr };
 
 	// From the original code
 	int xAlfred = 200;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 9b776ee65ef..3f94a55293e 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -1,6 +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 PELROCK_TYPES_H
+#define PELROCK_TYPES_H
 
 namespace Pelrock {
 
+enum Cursor {
+	DEFAULT,
+	HOTSPOT,
+	EXIT,
+	ANIMATION,
+	COMBINATION
+};
+
+// enum HoverState {
+// 	NONE,
+// 	INTERACTIVE,
+// 	HOSTPOT,
+// 	SPECIAl;
+
+
+// };
+
+const int kCursorWidth = 16;
+const int kCursorHeight = 18;
+const int kCursorSize = 288;  // 16 * 18
 const int kRoomStructSize = 104;
 const int kNumRooms = 56;
 
@@ -82,3 +124,5 @@ enum GameState {
 // };
 
 } // End of namespace Pelrock
+
+#endif


Commit: a20b8fde588e3194f521a2bac17c8347d49d3f74
    https://github.com/scummvm/scummvm/commit/a20b8fde588e3194f521a2bac17c8347d49d3f74
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:00+02:00

Commit Message:
PELROCK: Reads popup balloon animation and icons

Changed paths:
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 1312a564db7..3444f3edbd6 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -33,6 +33,9 @@ namespace Pelrock {
         0x367EF0
     };
 
+    uint32_t kBalloonFramesOffset = 2176936;
+    uint32_t kBalloonFramesSize = 24950;
+
 } // End of namespace Pelrock
 #endif
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 41ad51cf1a4..a94a11c264b 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -36,8 +36,8 @@
 #include "pelrock.h"
 #include "pelrock/console.h"
 #include "pelrock/detection.h"
-#include "pelrock/pelrock.h"
 #include "pelrock/offsets.h"
+#include "pelrock/pelrock.h"
 
 namespace Pelrock {
 
@@ -52,9 +52,14 @@ PelrockEngine::PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc) :
 PelrockEngine::~PelrockEngine() {
 	delete _screen;
 	delete _chronoManager;
-	for(int i =0; i<5; i++) {
+	for (int i = 0; i < 5; i++) {
 		delete[] _cursorMasks[i];
 	}
+	for (int i = 0; i < kNumVerbIcons; i++) {
+		delete[] _verbIcons[i];
+	}
+
+	delete[] _popUpBalloon;
 }
 
 uint32 PelrockEngine::getFeatures() const {
@@ -97,8 +102,19 @@ Common::Error PelrockEngine::run() {
 				mouseX = e.mouse.x;
 				mouseY = e.mouse.y;
 				// debug(3, "Mouse moved to (%d,%d)", mouseX, mouseY);
+			} else if (e.type == Common::EVENT_LBUTTONDOWN) {
+				_mouseDownTime = g_system->getMillis();
+				_isMouseDown = true;
 			} else if (e.type == Common::EVENT_LBUTTONUP) {
-				checkMouseClick(e.mouse.x, e.mouse.y);
+				if (_isMouseDown) {
+					uint32 clickDuration = g_system->getMillis() - _mouseDownTime;
+					if (clickDuration >= kLongClickDuration) {
+						checkLongMouseClick(e.mouse.x, e.mouse.y);
+					} else {
+						checkMouseClick(e.mouse.x, e.mouse.y);
+					}
+					_isMouseDown = false;
+				}
 			}
 		}
 		checkMouseHover();
@@ -114,6 +130,7 @@ Common::Error PelrockEngine::run() {
 
 void PelrockEngine::init() {
 	loadCursors();
+	loadInteractionIcons();
 
 	changeCursor(DEFAULT);
 	CursorMan.showMouse(true);
@@ -328,22 +345,52 @@ Common::List<WalkBox> PelrockEngine::loadWalkboxes(Common::File *roomFile, int r
 
 void PelrockEngine::loadCursors() {
 	Common::File alfred7File;
-	if(!alfred7File.open("ALFRED.7")) {
+	if (!alfred7File.open("ALFRED.7")) {
 		error("Couldnt find file ALFRED.7");
 	}
-	for (int i= 0; i < 5; i++) {
+	for (int i = 0; i < 5; i++) {
 		uint32_t cursorOffset = cursor_offsets[i];
 		alfred7File.seek(cursorOffset);
 		_cursorMasks[i] = new byte[kCursorSize];
 		alfred7File.read(_cursorMasks[i], kCursorSize);
-		for(int j = 0; j < kCursorSize; j++) {
-			byte *cursorMask = _cursorMasks[i];
-			if(cursorMask[j] == 255) cursorMask[j] = 0;
-		}
 	}
 	alfred7File.close();
 }
 
+void PelrockEngine::loadInteractionIcons() {
+	Common::File alfred7File;
+	if (!alfred7File.open("ALFRED.7")) {
+		error("Couldnt find file ALFRED.7");
+	}
+
+	alfred7File.seek(kBalloonFramesOffset, SEEK_SET);
+
+	uint32_t totalBalloonSize = kBalloonWidth * kBalloonHeight * kBalloonFrames;
+	_popUpBalloon = new byte[totalBalloonSize];
+
+	uint32_t compressedSize = kBalloonFramesSize;
+
+	byte *raw = new byte[compressedSize];
+	alfred7File.read(raw, compressedSize);
+	rleDecompress(raw, compressedSize, 0, compressedSize, &_popUpBalloon);
+
+	delete[] raw;
+
+	alfred7File.close();
+	Common::File alfred4File;
+	if (!alfred4File.open("ALFRED.4")) {
+		error("Couldnt find file ALFRED.4");
+	}
+
+	int iconSize = kVerbIconHeight * kVerbIconWidth;
+	for (int i = 0; i < kNumVerbIcons; i++) {
+		uint32_t iconOffset = i * iconSize;
+		_verbIcons[i] = new byte[iconSize];
+		alfred4File.read(_verbIcons[i], iconSize);
+	}
+	alfred4File.close();
+}
+
 Common::List<Exit> PelrockEngine::loadExits(Common::File *roomFile, int roomOffset) {
 	Common::List<Exit> exits;
 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
@@ -449,7 +496,7 @@ void PelrockEngine::frames() {
 				for (int j = 0; j < w; j++) {
 					for (int i = 0; i < h; i++) {
 						int idx = i * w + j;
-						if(y + i < 400 && x + j < 640) {
+						if (y + i < 400 && x + j < 640) {
 							*(bg + idx) = _currentBackground[(y + i) * 640 + (x + j)];
 						}
 					}
@@ -458,7 +505,7 @@ void PelrockEngine::frames() {
 				for (int i = 0; i < w; i++) {
 					for (int j = 0; j < h; j++) {
 						int index = (j * w + i);
-						if(x + i < 640 && y + j < 400)
+						if (x + i < 640 && y + j < 400)
 							*(byte *)g_engine->_screen->getBasePtr(x + i, y + j) = bg[index];
 					}
 				}
@@ -482,52 +529,121 @@ void PelrockEngine::frames() {
 			num++;
 
 			for (uint32_t y = 0; y < kAlfredFrameHeight; y++) {
-			for (uint32_t x = 0; x < kAlfredFrameWidth; x++) {
-				unsigned int src_pos = (curAlfredFrame * kAlfredFrameHeight * kAlfredFrameWidth) + (y * kAlfredFrameWidth) + x;
-				// debug("Xpos = %d, yPos=%d", x + xAlfred, y + yAlfred);
-				if (standingAnim[src_pos] != 255)
-					_screen->setPixel(x + xAlfred, y + yAlfred, standingAnim[src_pos]);
+				for (uint32_t x = 0; x < kAlfredFrameWidth; x++) {
+					unsigned int src_pos = (curAlfredFrame * kAlfredFrameHeight * kAlfredFrameWidth) + (y * kAlfredFrameWidth) + x;
+					// debug("Xpos = %d, yPos=%d", x + xAlfred, y + yAlfred);
+					if (standingAnim[src_pos] != 255)
+						_screen->setPixel(x + xAlfred, y + yAlfred, standingAnim[src_pos]);
+				}
 			}
 		}
-		}
 		_screen->markAllDirty();
 		_screen->update();
 	}
 }
 
-void PelrockEngine::checkMouseClick(int x, int y) {
+void PelrockEngine::checkLongMouseClick(int x, int y) {
+	HotSpot *hotspot = isHotspotUnder(mouseX, mouseY);
+	if (hotspot != nullptr) {
+		showActionBalloon(hotspot->x, hotspot->y);
+	}
+}
+
+HotSpot *PelrockEngine::isHotspotUnder(int x, int y) {
+
+	for (Common::List<HotSpot>::iterator i = _hotspots.begin(); i != _hotspots.end(); i++) {
+		if (mouseX >= i->x && mouseX <= (i->x + i->w) &&
+			mouseY >= i->y && mouseY <= (i->y + i->h)) {
+			debug("Hotspot at (%d,%d) size (%d,%d) extra %d", i->x, i->y, i->w, i->h, i->extra);
+			return &(*i);
+		}
+	}
+	return nullptr;
+}
+
+Exit *PelrockEngine::isExitUnder(int x, int y) {
 	for (Common::List<Exit>::iterator i = _currentRoomExits.begin(); i != _currentRoomExits.end(); i++) {
 		if (x >= i->x && x <= (i->x + i->w) &&
 			y >= i->y && y <= (i->y + i->h)) {
-			debug("Clicked Exit at (%d,%d) size (%d,%d) to room %d", i->x, i->y, i->w, i->h, i->targetRoom);
-			xAlfred = i->targetX;
-			yAlfred = i->targetY - kAlfredFrameHeight;
-			setScreen(i->targetRoom, i->dir);
-			return;
+			return &(*i);
 		}
 	}
-	for (Common::List<HotSpot>::iterator i = _hotspots.begin(); i != _hotspots.end(); i++) {
-		if (x >= i->x && x <= (i->x + i->w) &&
-			y >= i->y && y <= (i->y + i->h)) {
-			debug("Clicked Hotspot at (%d,%d) size (%d,%d) extra %d", i->x, i->y, i->w, i->h, i->extra);
-			// process hotspot action based on i->extra or other properties
-			return;
+	return nullptr;
+}
+
+AnimSet *PelrockEngine::isSpriteUnder(int x, int y) {
+	for (Common::List<AnimSet>::iterator i = _currentRoomAnims.begin(); i != _currentRoomAnims.end(); i++) {
+		if (mouseX >= i->x && mouseX <= (i->x + i->w) &&
+			mouseY >= i->y && mouseY <= (i->y + i->h)) {
+			debug("Sprite at (%d,%d) size (%d,%d)", i->x, i->y, i->w, i->h);
+			return &(*i);
+		}
+	}
+	return nullptr;
+}
+
+void PelrockEngine::showActionBalloon(int posx, int posy) {
+	int curFrame = 0;
+
+	for (uint32_t y = 0; y < kBalloonHeight; y++) {
+		for (uint32_t x = 0; x < kBalloonWidth; x++) {
+			unsigned int src_pos = (curFrame * kBalloonHeight * kBalloonWidth) + (y * kBalloonWidth) + x;
+			if (_popUpBalloon[src_pos] != 255)
+				_screen->setPixel(x + posx, y + posy, _popUpBalloon[src_pos]);
+		}
+	}
+
+	for (uint32_t y = 0; y < kVerbIconHeight; y++) {
+		for (uint32_t x = 0; x < kVerbIconWidth; x++) {
+			unsigned int src_pos = y * kVerbIconWidth + x;
+			debug("Color = %d", _verbIcons[LOOK][src_pos]);
+			if (_verbIcons[LOOK][src_pos] != 1)
+				_screen->setPixel(x + posx + 10, y + posy + 10, _verbIcons[LOOK][src_pos]);
 		}
 	}
 }
 
+void PelrockEngine::checkMouseClick(int x, int y) {
+
+	Exit *exit = isExitUnder(mouseX, mouseY);
+	if (exit != nullptr) {
+		xAlfred = exit->targetX;
+		yAlfred = exit->targetY - kAlfredFrameHeight;
+		setScreen(exit->targetRoom, exit->dir);
+	}
+
+	HotSpot *hotspot = isHotspotUnder(mouseX, mouseY);
+	if (hotspot != nullptr) {
+		debug("Clicked Hotspot at (%d,%d) size (%d,%d) extra %d", hotspot->x, hotspot->y, hotspot->w, hotspot->h, hotspot->extra);
+		changeCursor(HOTSPOT);
+	}
+}
+
 void PelrockEngine::changeCursor(Cursor cursor) {
-	CursorMan.replaceCursor(_cursorMasks[cursor], kCursorWidth, kCursorHeight, 0, 0, 0);
+	CursorMan.replaceCursor(_cursorMasks[cursor], kCursorWidth, kCursorHeight, 0, 0, 255);
 }
 
 void PelrockEngine::checkMouseHover() {
-	for (Common::List<HotSpot>::iterator i = _hotspots.begin(); i != _hotspots.end(); i++) {
-		if (mouseX >= i->x && mouseX <= (i->x + i->w) &&
-			mouseY >= i->y && mouseY <= (i->y + i->h)) {
-			// _currentHotspot = &(*i);
-			debug("Hotspot at (%d,%d) size (%d,%d) extra %d", i->x, i->y, i->w, i->h, i->extra);
-			return;
-		}
+	bool isSomethingUnder = false;
+
+	AnimSet *sprite = isSpriteUnder(mouseX, mouseY);
+	if (sprite != nullptr) {
+		isSomethingUnder = true;
+		changeCursor(HOTSPOT);
+	}
+
+	HotSpot *hotspot = isHotspotUnder(mouseX, mouseY);
+	if (hotspot != nullptr) {
+		isSomethingUnder = true;
+		changeCursor(HOTSPOT);
+	}
+	Exit *exit = isExitUnder(mouseX, mouseY);
+	if (exit != nullptr) {
+		isSomethingUnder = true;
+		changeCursor(EXIT);
+	}
+	if (!isSomethingUnder) {
+		changeCursor(DEFAULT);
 	}
 }
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index cf0c5c80bb4..e5461be2b81 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -67,13 +67,18 @@ private:
 	Common::List<Exit> loadExits(Common::File *roomFile, int roomOffset);
 	Common::List<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
 	void loadCursors();
-
+	void loadInteractionIcons();
 
 	// render loop
 	void frames();
 	void checkMouseHover();
 	void checkMouseClick(int x, int y);
+	void checkLongMouseClick(int x, int y);
 	void changeCursor(Cursor cursor);
+	HotSpot* isHotspotUnder(int x, int y);
+	Exit* isExitUnder(int x, int y);
+	AnimSet* isSpriteUnder(int x, int y);
+	void showActionBalloon(int posx, int posy);
 
 	ChronoManager *_chronoManager = nullptr;
 	byte *standingAnim = new byte[3060 * 102];
@@ -87,6 +92,12 @@ private:
 	byte *_currentBackground = nullptr;
 	byte *_cursorMasks[5] = { nullptr };
 
+	uint32 _mouseDownTime;
+    bool _isMouseDown;
+
+	byte *_verbIcons[9] = { nullptr };
+	byte *_popUpBalloon = nullptr;
+
 	// From the original code
 	int xAlfred = 200;
 	int yAlfred = 200;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 3f94a55293e..a5e4e81ce1c 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -27,10 +27,22 @@ enum Cursor {
 	DEFAULT,
 	HOTSPOT,
 	EXIT,
-	ANIMATION,
+	ALFRED,
 	COMBINATION
 };
 
+enum VerbIcons {
+	PICKUP,
+	TALK,
+	WALK,
+	LOOK,
+	PUSH,
+	PULL,
+	OPEN,
+	CLOSE,
+	UNKNOWN
+};
+
 // enum HoverState {
 // 	NONE,
 // 	INTERACTIVE,
@@ -40,11 +52,18 @@ enum Cursor {
 
 // };
 
+static const uint32 kLongClickDuration = 500; // 500ms for long click
 const int kCursorWidth = 16;
 const int kCursorHeight = 18;
 const int kCursorSize = 288;  // 16 * 18
 const int kRoomStructSize = 104;
 const int kNumRooms = 56;
+const int kVerbIconWidth = 60;
+const int kVerbIconHeight = 60;
+const int kNumVerbIcons = 9;
+const int kBalloonWidth = 247;
+const int kBalloonHeight = 112;
+const int kBalloonFrames = 4;
 
 struct Anim {
 	int x;


Commit: faa428a45e5695c49afdc1cde2b1bab334006c3d
    https://github.com/scummvm/scummvm/commit/faa428a45e5695c49afdc1cde2b1bab334006c3d
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:00+02:00

Commit Message:
PELROCK: Generalize bg copy and restoration

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index a94a11c264b..6f23066c9d0 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -467,6 +467,27 @@ void PelrockEngine::loadMainCharacterAnims() {
 	memcpy(standingAnim, pic, 3060 * 102);
 }
 
+byte *PelrockEngine::grabBackgroundSlice(int x, int y, int w, int h) {
+	byte *bg = new byte[w * h];
+	for (int j = 0; j < w; j++) {
+		for (int i = 0; i < h; i++) {
+			int idx = i * w + j;
+			if (y + i < 400 && x + j < 640) {
+				*(bg + idx) = _currentBackground[(y + i) * 640 + (x + j)];
+			}
+		}
+	}
+	return bg;
+}
+void PelrockEngine::putBackgroundSlice(int x, int y, int w, int h, byte *slice) {
+	for (int i = 0; i < w; i++) {
+		for (int j = 0; j < h; j++) {
+			int index = (j * w + i);
+			if (x + i < 640 && y + j < 400)
+				*(byte *)g_engine->_screen->getBasePtr(x + i, y + j) = slice[index];
+		}
+	}
+}
 void PelrockEngine::frames() {
 
 	if (_chronoManager->_gameTick) {
@@ -492,24 +513,8 @@ void PelrockEngine::frames() {
 				Common::copy(i->animData[j].animData + (curFrame * i->h * i->w), i->animData[j].animData + (curFrame * i->h * i->w) + (frameSize), frame);
 				// debug("Current frame %d of %d", curFrame, i->animData[j].nframes);
 
-				byte *bg = new byte[frameSize];
-				for (int j = 0; j < w; j++) {
-					for (int i = 0; i < h; i++) {
-						int idx = i * w + j;
-						if (y + i < 400 && x + j < 640) {
-							*(bg + idx) = _currentBackground[(y + i) * 640 + (x + j)];
-						}
-					}
-				}
-
-				for (int i = 0; i < w; i++) {
-					for (int j = 0; j < h; j++) {
-						int index = (j * w + i);
-						if (x + i < 640 && y + j < 400)
-							*(byte *)g_engine->_screen->getBasePtr(x + i, y + j) = bg[index];
-					}
-				}
-
+				byte *bg = grabBackgroundSlice(x, y, w, h);
+				putBackgroundSlice(x, y, w, h, bg);
 				for (int y = 0; y < i->h; y++) {
 					for (int x = 0; x < i->w; x++) {
 
@@ -536,6 +541,24 @@ void PelrockEngine::frames() {
 						_screen->setPixel(x + xAlfred, y + yAlfred, standingAnim[src_pos]);
 				}
 			}
+
+			if (_displayPopup) {
+
+				// byte *bgDialog = new byte[kBalloonWidth * kBalloonHeight];
+				// for (int j = 0; j < kBalloonWidth; j++) {
+				// 	for (int i = 0; i < kBalloonHeight; i++) {
+				// 		int idx = i * kBalloonWidth + j;
+				// 		if (_popupY + i < 400 && _popupX + j < 640) {
+				// 			*(bgDialog + idx) = _currentBackground[(_popupY + i) * 640 + (_popupX + j)];
+				// 		}
+				// 	}
+				// }
+				showActionBalloon(_popupX, _popupY, _currentPopupFrame);
+				if (_currentPopupFrame < 3) {
+					_currentPopupFrame++;
+				} else
+					_currentPopupFrame = 0;
+			}
 		}
 		_screen->markAllDirty();
 		_screen->update();
@@ -545,7 +568,11 @@ void PelrockEngine::frames() {
 void PelrockEngine::checkLongMouseClick(int x, int y) {
 	HotSpot *hotspot = isHotspotUnder(mouseX, mouseY);
 	if (hotspot != nullptr) {
-		showActionBalloon(hotspot->x, hotspot->y);
+		_popupX = hotspot->x;
+		_popupY = hotspot->y;
+		_displayPopup = true;
+		_currentPopupFrame = 0;
+		// _bgPopupBalloon =
 	}
 }
 
@@ -582,8 +609,7 @@ AnimSet *PelrockEngine::isSpriteUnder(int x, int y) {
 	return nullptr;
 }
 
-void PelrockEngine::showActionBalloon(int posx, int posy) {
-	int curFrame = 0;
+void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 
 	for (uint32_t y = 0; y < kBalloonHeight; y++) {
 		for (uint32_t x = 0; x < kBalloonWidth; x++) {
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index e5461be2b81..c383e212594 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -68,6 +68,8 @@ private:
 	Common::List<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
 	void loadCursors();
 	void loadInteractionIcons();
+	byte *grabBackgroundSlice(int x, int y, int w, int h);
+	void putBackgroundSlice(int x, int y, int w, int h, byte *slice);
 
 	// render loop
 	void frames();
@@ -78,7 +80,7 @@ private:
 	HotSpot* isHotspotUnder(int x, int y);
 	Exit* isExitUnder(int x, int y);
 	AnimSet* isSpriteUnder(int x, int y);
-	void showActionBalloon(int posx, int posy);
+	void showActionBalloon(int posx, int posy, int curFrame);
 
 	ChronoManager *_chronoManager = nullptr;
 	byte *standingAnim = new byte[3060 * 102];
@@ -97,6 +99,15 @@ private:
 
 	byte *_verbIcons[9] = { nullptr };
 	byte *_popUpBalloon = nullptr;
+	byte *_bgPopupBalloon = nullptr;
+
+
+	bool _displayPopup = false;
+	int _popupX = 0;
+	int _popupY = 0;
+	int _currentPopupFrame = 0;
+
+
 
 	// From the original code
 	int xAlfred = 200;


Commit: a09737f03cbb39bf3daa5f6c47a4c6b66fecaf3f
    https://github.com/scummvm/scummvm/commit/a09737f03cbb39bf3daa5f6c47a4c6b66fecaf3f
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:00+02:00

Commit Message:
PELROCK: Positioning of action balloon

Changed paths:
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 6f23066c9d0..aee6269b941 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -553,6 +553,9 @@ void PelrockEngine::frames() {
 				// 		}
 				// 	}
 				// }
+				if(_bgPopupBalloon!=nullptr) {
+					putBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight, _bgPopupBalloon);
+				}
 				showActionBalloon(_popupX, _popupY, _currentPopupFrame);
 				if (_currentPopupFrame < 3) {
 					_currentPopupFrame++;
@@ -568,11 +571,26 @@ void PelrockEngine::frames() {
 void PelrockEngine::checkLongMouseClick(int x, int y) {
 	HotSpot *hotspot = isHotspotUnder(mouseX, mouseY);
 	if (hotspot != nullptr) {
-		_popupX = hotspot->x;
-		_popupY = hotspot->y;
+		// _popupX = hotspot->x;
+		// _popupY = hotspot->y;
+		if(_bgPopupBalloon != nullptr) {
+			putBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight, _bgPopupBalloon);
+			delete[] _bgPopupBalloon;
+		}
+		_popupX = x - kBalloonWidth / 2;
+		if(_popupX < 0) _popupX = 0;
+		if(_popupX + kBalloonWidth > 640) {
+			_popupX -= 640 - (_popupX + kBalloonWidth);
+		}
+
+		_popupY = y - kBalloonHeight;
+		if(_popupY < 0) {
+			_popupY = 0;
+		}
 		_displayPopup = true;
 		_currentPopupFrame = 0;
-		// _bgPopupBalloon =
+
+		_bgPopupBalloon = grabBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight);
 	}
 }
 
@@ -622,7 +640,6 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	for (uint32_t y = 0; y < kVerbIconHeight; y++) {
 		for (uint32_t x = 0; x < kVerbIconWidth; x++) {
 			unsigned int src_pos = y * kVerbIconWidth + x;
-			debug("Color = %d", _verbIcons[LOOK][src_pos]);
 			if (_verbIcons[LOOK][src_pos] != 1)
 				_screen->setPixel(x + posx + 10, y + posy + 10, _verbIcons[LOOK][src_pos]);
 		}
@@ -631,6 +648,13 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 
 void PelrockEngine::checkMouseClick(int x, int y) {
 
+	_displayPopup = false;
+	if(_bgPopupBalloon != nullptr) {
+		putBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight, _bgPopupBalloon);
+		delete[] _bgPopupBalloon;
+		_bgPopupBalloon = nullptr;
+	}
+
 	Exit *exit = isExitUnder(mouseX, mouseY);
 	if (exit != nullptr) {
 		xAlfred = exit->targetX;


Commit: 47ddfa060bcbbcee3bd8f02139a093a294c1deb7
    https://github.com/scummvm/scummvm/commit/47ddfa060bcbbcee3bd8f02139a093a294c1deb7
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:01+02:00

Commit Message:
PELROCK: Font rendering

Changed paths:
  A engines/pelrock/fonts/large_font.cpp
  A engines/pelrock/fonts/large_font.h
  A engines/pelrock/fonts/small_font.cpp
  A engines/pelrock/fonts/small_font.h
    engines/pelrock/module.mk
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/fonts/large_font.cpp b/engines/pelrock/fonts/large_font.cpp
new file mode 100644
index 00000000000..07f41a0e63f
--- /dev/null
+++ b/engines/pelrock/fonts/large_font.cpp
@@ -0,0 +1,104 @@
+/* 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 "pelrock/fonts/large_font.h"
+
+namespace Pelrock {
+
+LargeFont::LargeFont() : _fontData(nullptr) {
+}
+
+LargeFont::~LargeFont() {
+	delete[] _fontData;
+}
+
+bool LargeFont::load(const Common::String &filename) {
+	Common::File file;
+	if (!file.open(Common::Path(filename))) {
+		return false;
+	}
+
+	file.seek(0x7DC8, SEEK_SET);
+	const int dataSize = 96 * 48; // 96 characters × 48 bytes
+	_fontData = new byte[dataSize];
+	file.read(_fontData, dataSize);
+	debug("LargeFont::load: Loading large font data from %s, size %d bytes", filename.c_str(), dataSize);
+	file.close();
+
+	return true;
+}
+
+int LargeFont::getCharWidth(uint32 chr) const {
+	return CHAR_WIDTH;
+}
+
+void LargeFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
+
+	// """Extract a single character from the large font data (12x24 pixels)
+	// Each character is 48 bytes (24 rows × 2 bytes per row)"""
+	// offset = char_index * 0x30  # 48 bytes per character
+	// char_data = data[offset:offset + 0x30]
+
+	// # Create 12x24 pixel array
+	// pixels = np.zeros((24, 12), dtype=np.uint8)
+
+	// # Process each row (2 bytes per row)
+	// for row in range(24):
+	//     byte1 = char_data[row * 2]
+	//     byte2 = char_data[row * 2 + 1]
+
+	//     # Process 12 bits (12 pixels) from the two bytes
+	//     for bit in range(8):
+	//         pixels[row, bit] = 255 if (byte1 & (0x80 >> bit)) else 0
+	//     for bit in range(4):
+	//         pixels[row, bit + 8] = 255 if (byte2 & (0x80 >> bit)) else 0
+
+	// return pixels
+
+	if (!_fontData || chr > 255) {
+		return;
+	}
+	chr -= 32; // Adjust for font starting at ASCII 32
+	int charOffset = chr * 0x30;
+	debug("LargeFont::drawChar: Drawing char %d at offset %d", chr, charOffset);
+	for (int i = 0; i < 24; i++) {
+		byte rowByte1 = _fontData[charOffset + i * 2];
+		byte rowByte2 = _fontData[charOffset + i * 2 + 1];
+		for (int bit = 0; bit < 8; bit++) {
+			bool pixelOn = (rowByte1 & (0x80 >> bit)) != 0;
+			if (pixelOn) {
+				if ((x + bit) < dst->w && (y + i) < dst->h) {
+					*((byte *)dst->getBasePtr(x + bit, y + i)) = color;
+				}
+			}
+		}
+		for (int bit = 0; bit < 4; bit++) {
+			bool pixelOn = (rowByte2 & (0x80 >> bit)) != 0;
+			if (pixelOn) {
+				if ((x + bit + 8) < dst->w && (y + i) < dst->h) {
+					*((byte *)dst->getBasePtr(x + bit + 8, y + i)) = color;
+				}
+			}
+		}
+	}
+}
+
+} // namespace Pelrock
diff --git a/engines/pelrock/fonts/large_font.h b/engines/pelrock/fonts/large_font.h
new file mode 100644
index 00000000000..bd87b9012d2
--- /dev/null
+++ b/engines/pelrock/fonts/large_font.h
@@ -0,0 +1,49 @@
+/* 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 PELROCK_LARGEFONT_H
+#define PELROCK_LARGEFONT_H
+
+#include "common/file.h"
+#include "common/str.h"
+#include "graphics/font.h"
+#include "graphics/surface.h"
+
+namespace Pelrock {
+class LargeFont : public Graphics::Font {
+public:
+	LargeFont();
+	~LargeFont();
+
+	bool load(const Common::String &filename);
+
+	// Required Font interface methods
+	int getFontHeight() const override { return CHAR_HEIGHT; }
+	int getMaxCharWidth() const override { return CHAR_WIDTH; }
+	int getCharWidth(uint32 chr) const override;
+	void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
+
+private:
+	static const int CHAR_WIDTH = 12;
+	static const int CHAR_HEIGHT = 24;
+	byte *_fontData;
+};
+} // End of namespace Pelrock
+#endif
diff --git a/engines/pelrock/fonts/small_font.cpp b/engines/pelrock/fonts/small_font.cpp
new file mode 100644
index 00000000000..9ab959e3bcb
--- /dev/null
+++ b/engines/pelrock/fonts/small_font.cpp
@@ -0,0 +1,74 @@
+/* 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 "pelrock/fonts/small_font.h"
+
+namespace Pelrock {
+
+SmallFont::SmallFont() : _fontData(nullptr) {
+}
+
+SmallFont::~SmallFont() {
+	delete[] _fontData;
+}
+
+bool SmallFont::load(const Common::String &filename) {
+	Common::File file;
+	if (!file.open(Common::Path(filename))) {
+		return false;
+	}
+
+	file.seek(0x8F32, SEEK_SET);
+
+	// const int dataSize = 256 * 8 * 8; // 256 characters, 8x8 pixels
+	const int dataSize = 2048; // 256 characters, 8x8 pixels
+	_fontData = new byte[dataSize];
+	file.read(_fontData, dataSize);
+	file.close();
+
+	return true;
+}
+
+int SmallFont::getCharWidth(uint32 chr) const {
+	return CHAR_WIDTH;
+}
+
+void SmallFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
+	if (!_fontData || chr > 255) {
+		return;
+	}
+
+	int charOffset = chr * 8;
+
+	for (int i = 0; i < 8; i++) {
+		byte rowByte = _fontData[charOffset + i];
+		for (int bit = 0; bit < 8; bit++) {
+			bool pixelOn = (rowByte & (0x80 >> bit)) != 0;
+			if (pixelOn) {
+				if ((x + bit) < dst->w && (y + i) < dst->h) {
+					*((byte *)dst->getBasePtr(x + bit, y + i)) = color;
+				}
+			}
+		}
+	}
+}
+
+} // namespace Pelrock
diff --git a/engines/pelrock/fonts/small_font.h b/engines/pelrock/fonts/small_font.h
new file mode 100644
index 00000000000..f1fb165503a
--- /dev/null
+++ b/engines/pelrock/fonts/small_font.h
@@ -0,0 +1,50 @@
+/* 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 PELROCK_SMALLFONT_H
+#define PELROCK_SMALLFONT_H
+
+#include "common/file.h"
+#include "common/str.h"
+#include "graphics/font.h"
+#include "graphics/surface.h"
+
+namespace Pelrock {
+class SmallFont : public Graphics::Font {
+public:
+	SmallFont();
+	~SmallFont();
+
+	bool load(const Common::String &filename);
+
+	// Required Font interface methods
+	int getFontHeight() const override { return CHAR_HEIGHT; }
+	int getMaxCharWidth() const override { return CHAR_WIDTH; }
+	int getCharWidth(uint32 chr) const override;
+	void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
+
+private:
+	static const int CHAR_WIDTH = 8;
+	static const int CHAR_HEIGHT = 8;
+	byte *_fontData;
+};
+
+} // End of namespace Pelrock
+#endif
diff --git a/engines/pelrock/module.mk b/engines/pelrock/module.mk
index bc1a263acdd..0c1e387371a 100644
--- a/engines/pelrock/module.mk
+++ b/engines/pelrock/module.mk
@@ -4,7 +4,9 @@ MODULE_OBJS = \
 	pelrock.o \
 	chrono.o \
 	console.o \
-	metaengine.o
+	metaengine.o \
+	fonts/small_font.o \
+	fonts/large_font.o \
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_PELROCK), DYNAMIC_PLUGIN)
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index aee6269b941..595fc4855d8 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -36,6 +36,7 @@
 #include "pelrock.h"
 #include "pelrock/console.h"
 #include "pelrock/detection.h"
+#include "pelrock/fonts/small_font.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
 
@@ -60,6 +61,9 @@ PelrockEngine::~PelrockEngine() {
 	}
 
 	delete[] _popUpBalloon;
+	if (_bgPopupBalloon)
+		delete[] _bgPopupBalloon;
+	delete _smallFont;
 }
 
 uint32 PelrockEngine::getFeatures() const {
@@ -131,6 +135,10 @@ Common::Error PelrockEngine::run() {
 void PelrockEngine::init() {
 	loadCursors();
 	loadInteractionIcons();
+	_smallFont = new SmallFont();
+	_smallFont->load("ALFRED.4");
+	_largeFont = new LargeFont();
+	_largeFont->load("ALFRED.7");
 
 	changeCursor(DEFAULT);
 	CursorMan.showMouse(true);
@@ -138,6 +146,9 @@ void PelrockEngine::init() {
 		gameInitialized = true;
 		loadAnims();
 		setScreen(0, 2);
+		_smallFont->drawString(g_engine->_screen, Common::String("Welcome to Pelrock!"), 0, 0, 400, 102);
+		_largeFont->drawString(g_engine->_screen, Common::String("Click to start"), 200, 180, 400, 102);
+		_screen->update();
 	}
 }
 
@@ -434,8 +445,8 @@ void PelrockEngine::loadHotspots(Common::File *roomFile, int roomOffset) {
 		roomFile->seek(obj_offset, SEEK_SET);
 		byte obj_bytes[9];
 		roomFile->read(obj_bytes, 9);
-		byte type_byte = obj_bytes[0];
 		HotSpot spot;
+		spot.type = obj_bytes[0];
 		spot.x = obj_bytes[1] | (obj_bytes[2] << 8);
 		spot.y = obj_bytes[3] | (obj_bytes[4] << 8);
 		spot.w = obj_bytes[5];
@@ -553,7 +564,7 @@ void PelrockEngine::frames() {
 				// 		}
 				// 	}
 				// }
-				if(_bgPopupBalloon!=nullptr) {
+				if (_bgPopupBalloon != nullptr) {
 					putBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight, _bgPopupBalloon);
 				}
 				showActionBalloon(_popupX, _popupY, _currentPopupFrame);
@@ -573,18 +584,19 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 	if (hotspot != nullptr) {
 		// _popupX = hotspot->x;
 		// _popupY = hotspot->y;
-		if(_bgPopupBalloon != nullptr) {
+		if (_bgPopupBalloon != nullptr) {
 			putBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight, _bgPopupBalloon);
 			delete[] _bgPopupBalloon;
 		}
 		_popupX = x - kBalloonWidth / 2;
-		if(_popupX < 0) _popupX = 0;
-		if(_popupX + kBalloonWidth > 640) {
+		if (_popupX < 0)
+			_popupX = 0;
+		if (_popupX + kBalloonWidth > 640) {
 			_popupX -= 640 - (_popupX + kBalloonWidth);
 		}
 
 		_popupY = y - kBalloonHeight;
-		if(_popupY < 0) {
+		if (_popupY < 0) {
 			_popupY = 0;
 		}
 		_displayPopup = true;
@@ -599,7 +611,20 @@ HotSpot *PelrockEngine::isHotspotUnder(int x, int y) {
 	for (Common::List<HotSpot>::iterator i = _hotspots.begin(); i != _hotspots.end(); i++) {
 		if (mouseX >= i->x && mouseX <= (i->x + i->w) &&
 			mouseY >= i->y && mouseY <= (i->y + i->h)) {
-			debug("Hotspot at (%d,%d) size (%d,%d) extra %d", i->x, i->y, i->w, i->h, i->extra);
+			int lookable = i->extra & 0x0B;
+			int openable = i->extra & 0x0F;
+			debug("Hotspot at (%d,%d) size (%d,%d) extra %d, type = %d, lookable = %d, openable=%d", i->x, i->y, i->w, i->h, i->extra, i->type, lookable, openable);
+			/*
+			LOOK = 0x0B      # Look at
+	TAKE = 0x0C      # Take/Pick up
+	USE = 0x0D       # Use
+	TALK = 0x0E      # Talk to
+	OPEN = 0x0F      # Open
+	CLOSE = 0x10     # Close
+	PUSH = 0x11      # Push/Move
+	PULL = 0x12      # Pull
+	GIVE = 0x13      # Give
+	*/
 			return &(*i);
 		}
 	}
@@ -649,7 +674,7 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 void PelrockEngine::checkMouseClick(int x, int y) {
 
 	_displayPopup = false;
-	if(_bgPopupBalloon != nullptr) {
+	if (_bgPopupBalloon != nullptr) {
 		putBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight, _bgPopupBalloon);
 		delete[] _bgPopupBalloon;
 		_bgPopupBalloon = nullptr;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index c383e212594..427b4218254 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -38,6 +38,8 @@
 
 #include "pelrock/chrono.h"
 #include "pelrock/detection.h"
+#include "pelrock/fonts/large_font.h"
+#include "pelrock/fonts/small_font.h"
 #include "pelrock/types.h"
 
 namespace Pelrock {
@@ -77,9 +79,9 @@ private:
 	void checkMouseClick(int x, int y);
 	void checkLongMouseClick(int x, int y);
 	void changeCursor(Cursor cursor);
-	HotSpot* isHotspotUnder(int x, int y);
-	Exit* isExitUnder(int x, int y);
-	AnimSet* isSpriteUnder(int x, int y);
+	HotSpot *isHotspotUnder(int x, int y);
+	Exit *isExitUnder(int x, int y);
+	AnimSet *isSpriteUnder(int x, int y);
 	void showActionBalloon(int posx, int posy, int curFrame);
 
 	ChronoManager *_chronoManager = nullptr;
@@ -92,22 +94,22 @@ private:
 	uint16 mouseX = 0;
 	uint16 mouseY = 0;
 	byte *_currentBackground = nullptr;
-	byte *_cursorMasks[5] = { nullptr };
+	byte *_cursorMasks[5] = {nullptr};
 
 	uint32 _mouseDownTime;
-    bool _isMouseDown;
+	bool _isMouseDown;
 
-	byte *_verbIcons[9] = { nullptr };
+	byte *_verbIcons[9] = {nullptr};
 	byte *_popUpBalloon = nullptr;
 	byte *_bgPopupBalloon = nullptr;
 
-
 	bool _displayPopup = false;
 	int _popupX = 0;
 	int _popupY = 0;
 	int _currentPopupFrame = 0;
 
-
+	SmallFont *_smallFont = nullptr;
+	LargeFont *_largeFont = nullptr;
 
 	// From the original code
 	int xAlfred = 200;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index a5e4e81ce1c..4a97adf12c4 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -99,6 +99,7 @@ struct AnimSet {
 };
 
 struct HotSpot {
+	byte type;
 	int x;
 	int y;
 	int id;


Commit: 8febd828eb4093a3d5df37caad0151ca54fca021
    https://github.com/scummvm/scummvm/commit/8febd828eb4093a3d5df37caad0151ca54fca021
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:01+02:00

Commit Message:
PELROCK: identifies valid actions associated with hotspots

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 595fc4855d8..985e3fd1728 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -146,9 +146,6 @@ void PelrockEngine::init() {
 		gameInitialized = true;
 		loadAnims();
 		setScreen(0, 2);
-		_smallFont->drawString(g_engine->_screen, Common::String("Welcome to Pelrock!"), 0, 0, 400, 102);
-		_largeFont->drawString(g_engine->_screen, Common::String("Click to start"), 200, 180, 400, 102);
-		_screen->update();
 	}
 }
 
@@ -243,7 +240,7 @@ void PelrockEngine::getBackground(Common::File *roomFile, int roomOffset, byte *
 	}
 }
 
-Common::List<AnimSet> PelrockEngine::getRoomAnimations(Common::File *roomFile, int roomOffset) {
+Common::List<AnimSet> PelrockEngine::loadRoomAnimations(Common::File *roomFile, int roomOffset) {
 	uint32_t pair_offset = roomOffset + (8 * 8);
 	roomFile->seek(pair_offset, SEEK_SET);
 	uint32_t offset = roomFile->readUint32LE();
@@ -265,9 +262,8 @@ Common::List<AnimSet> PelrockEngine::getRoomAnimations(Common::File *roomFile, i
 	uint32_t picOffset = 0;
 	for (int i = 0; i < 7; i++) {
 		uint32_t animOffset = metadata_start + (i * 44);
-		roomFile->seek(animOffset, SEEK_SET);
-
 		AnimSet animSet;
+		roomFile->seek(animOffset, SEEK_SET);
 		animSet.x = roomFile->readSint16LE();
 		animSet.y = roomFile->readSint16LE();
 		animSet.w = roomFile->readByte();
@@ -354,6 +350,7 @@ Common::List<WalkBox> PelrockEngine::loadWalkboxes(Common::File *roomFile, int r
 	return walkboxes;
 }
 
+
 void PelrockEngine::loadCursors() {
 	Common::File alfred7File;
 	if (!alfred7File.open("ALFRED.7")) {
@@ -431,8 +428,8 @@ Common::List<Exit> PelrockEngine::loadExits(Common::File *roomFile, int roomOffs
 
 void PelrockEngine::loadHotspots(Common::File *roomFile, int roomOffset) {
 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
+
 	roomFile->seek(pair10_offset_pos, SEEK_SET);
-	// roomFile->skip(4);
 	uint32_t pair10_data_offset = roomFile->readUint32LE();
 	uint32_t pair10_size = roomFile->readUint32LE();
 	uint32_t count_offset = pair10_data_offset + 0x47a;
@@ -456,6 +453,9 @@ void PelrockEngine::loadHotspots(Common::File *roomFile, int roomOffset) {
 		hotspots.push_back(spot);
 	}
 	_hotspots = hotspots;
+	// uint32_t hover_areas_start = pair10_data_offset + 0x1BE;
+	// roomFile->seek(hover_areas_start, SEEK_SET);
+
 }
 
 void PelrockEngine::loadMainCharacterAnims() {
@@ -499,6 +499,42 @@ void PelrockEngine::putBackgroundSlice(int x, int y, int w, int h, byte *slice)
 		}
 	}
 }
+Common::List<VerbIcons> PelrockEngine::populateActionsMenu(HotSpot hotspot) {
+	Common::List<VerbIcons> verbs;
+	verbs.push_back(LOOK);
+
+	if(hotspot.type & 1) {
+		debug("Hotspot allows OPEN action");
+		verbs.push_back(OPEN);
+	}
+	if(hotspot.type & 2) {
+		debug("Hotspot allows CLOSE action");
+		verbs.push_back(CLOSE);
+	}
+	if(hotspot.type & 4) {
+		debug("Hotspot allows UNKNOWN action");
+		verbs.push_back(UNKNOWN);
+	}
+	if(hotspot.type & 8) {
+		debug("Hotspot allows PICKUP action");
+		verbs.push_back(PICKUP);
+	}
+	if(hotspot.type & 16) {
+		debug("Hotspot allows TALK action");
+		verbs.push_back(TALK);
+	}
+	if(hotspot.type & 32) {
+		debug("Hotspot allows WALK action");
+		verbs.push_back(PUSH);
+	}
+	if(hotspot.type & 128) {
+		debug("Hotspot allows PULL action");
+		verbs.push_back(PULL);
+	}
+	return verbs;
+}
+
+
 void PelrockEngine::frames() {
 
 	if (_chronoManager->_gameTick) {
@@ -601,7 +637,8 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 		}
 		_displayPopup = true;
 		_currentPopupFrame = 0;
-
+		_currentHotspot = hotspot;
+		populateActionsMenu(*hotspot);
 		_bgPopupBalloon = grabBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight);
 	}
 }
@@ -613,18 +650,7 @@ HotSpot *PelrockEngine::isHotspotUnder(int x, int y) {
 			mouseY >= i->y && mouseY <= (i->y + i->h)) {
 			int lookable = i->extra & 0x0B;
 			int openable = i->extra & 0x0F;
-			debug("Hotspot at (%d,%d) size (%d,%d) extra %d, type = %d, lookable = %d, openable=%d", i->x, i->y, i->w, i->h, i->extra, i->type, lookable, openable);
-			/*
-			LOOK = 0x0B      # Look at
-	TAKE = 0x0C      # Take/Pick up
-	USE = 0x0D       # Use
-	TALK = 0x0E      # Talk to
-	OPEN = 0x0F      # Open
-	CLOSE = 0x10     # Close
-	PUSH = 0x11      # Push/Move
-	PULL = 0x12      # Pull
-	GIVE = 0x13      # Give
-	*/
+			// debug("Hotspot at (%d,%d) size (%d,%d) extra %d, type = %d, lookable = %d, openable=%d", i->x, i->y, i->w, i->h, i->extra, i->type, lookable, openable);
 			return &(*i);
 		}
 	}
@@ -645,7 +671,7 @@ AnimSet *PelrockEngine::isSpriteUnder(int x, int y) {
 	for (Common::List<AnimSet>::iterator i = _currentRoomAnims.begin(); i != _currentRoomAnims.end(); i++) {
 		if (mouseX >= i->x && mouseX <= (i->x + i->w) &&
 			mouseY >= i->y && mouseY <= (i->y + i->h)) {
-			debug("Sprite at (%d,%d) size (%d,%d)", i->x, i->y, i->w, i->h);
+			debug("Sprite at (%d,%d) size (%d,%d), type = %d, extra = %d, enabled=%d", i->x, i->y, i->w, i->h, i->type, i->extra, i->enabled);
 			return &(*i);
 		}
 	}
@@ -662,6 +688,10 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 		}
 	}
 
+	// for (Common::List<VerbIcons>::iterator i = verbList.begin(); i != verbList.end(); i++) {
+	// 	debug("Verb icon to show: %d", *i);
+	// }
+
 	for (uint32_t y = 0; y < kVerbIconHeight; y++) {
 		for (uint32_t x = 0; x < kVerbIconWidth; x++) {
 			unsigned int src_pos = y * kVerbIconWidth + x;
@@ -752,7 +782,7 @@ void PelrockEngine::setScreen(int number, int dir) {
 			_screen->setPixel(i, j, background[j * 640 + i]);
 		}
 	}
-	_currentRoomAnims = getRoomAnimations(&roomFile, roomOffset);
+	_currentRoomAnims = loadRoomAnimations(&roomFile, roomOffset);
 
 	loadHotspots(&roomFile, roomOffset);
 	int hotsPotCount = 0;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 427b4218254..3aff9bcd841 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -63,15 +63,17 @@ private:
 	// Room data
 	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
-	Common::List<AnimSet> getRoomAnimations(Common::File *roomFile, int roomOffset);
-	void loadHotspots(Common::File *roomFile, int roomOffset);
+	Common::List<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
 	void loadMainCharacterAnims();
+	void loadHotspots(Common::File *roomFile, int roomOffset);
 	Common::List<Exit> loadExits(Common::File *roomFile, int roomOffset);
 	Common::List<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
+	void loadRoomMetadata(Common::File *roomFile, int roomOffset);
 	void loadCursors();
 	void loadInteractionIcons();
 	byte *grabBackgroundSlice(int x, int y, int w, int h);
 	void putBackgroundSlice(int x, int y, int w, int h, byte *slice);
+	Common::List<VerbIcons> populateActionsMenu(HotSpot hotspot);
 
 	// render loop
 	void frames();
@@ -87,6 +89,7 @@ private:
 	ChronoManager *_chronoManager = nullptr;
 	byte *standingAnim = new byte[3060 * 102];
 	Common::List<HotSpot> _hotspots;
+	Common::List<HoverArea> _hoverAreas;
 	Common::List<AnimSet> _currentRoomAnims;
 	Common::List<Exit> _currentRoomExits;
 	int *_currentAnimFrames = nullptr;
@@ -107,6 +110,7 @@ private:
 	int _popupX = 0;
 	int _popupY = 0;
 	int _currentPopupFrame = 0;
+	HotSpot *_currentHotspot = nullptr;
 
 	SmallFont *_smallFont = nullptr;
 	LargeFont *_largeFont = nullptr;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 4a97adf12c4..f30cb37c2f0 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -89,6 +89,7 @@ struct Exit {
 };
 
 struct AnimSet {
+	byte type;
 	int x;
 	int y;
 	int w;
@@ -96,6 +97,15 @@ struct AnimSet {
 	int speed;
 	int numAnims;
 	Anim *animData;
+	byte extra;
+	bool enabled;
+};
+
+struct HoverArea {
+	int x;
+	int y;
+	int w;
+	int h;
 };
 
 struct HotSpot {


Commit: f131a5bad9d1d85fae9c8adcaa5b42d5413c07ab
    https://github.com/scummvm/scummvm/commit/f131a5bad9d1d85fae9c8adcaa5b42d5413c07ab
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:01+02:00

Commit Message:
PELROCK: Calculates clickable areas

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 985e3fd1728..bed6c3a3d89 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -146,6 +146,7 @@ void PelrockEngine::init() {
 		gameInitialized = true;
 		loadAnims();
 		setScreen(0, 2);
+		// setScreen(24, 2);
 	}
 }
 
@@ -258,6 +259,8 @@ Common::List<AnimSet> PelrockEngine::loadRoomAnimations(Common::File *roomFile,
 	}
 	Common::List<AnimSet> anims = Common::List<AnimSet>();
 	uint32_t spriteEnd = offset + size;
+
+	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
 	uint32_t metadata_start = spriteEnd + 108;
 	uint32_t picOffset = 0;
 	for (int i = 0; i < 7; i++) {
@@ -268,7 +271,8 @@ Common::List<AnimSet> PelrockEngine::loadRoomAnimations(Common::File *roomFile,
 		animSet.y = roomFile->readSint16LE();
 		animSet.w = roomFile->readByte();
 		animSet.h = roomFile->readByte();
-		roomFile->skip(2); // reserved
+		animSet.extra = roomFile->readByte();
+		roomFile->skip(1); // reserved
 		animSet.numAnims = roomFile->readByte();
 		debug("AnimSet %d: x=%d y=%d w=%d h=%d numAnims=%d", i, animSet.x, animSet.y, animSet.w, animSet.h, animSet.numAnims);
 		animSet.animData = new Anim[animSet.numAnims];
@@ -338,7 +342,7 @@ Common::List<WalkBox> PelrockEngine::loadWalkboxes(Common::File *roomFile, int r
 		int16 w = roomFile->readSint16LE();
 		int16 h = roomFile->readSint16LE();
 		byte flags = roomFile->readByte();
-		// debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
+		debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
 		WalkBox box;
 		box.x = x1;
 		box.y = y1;
@@ -449,7 +453,7 @@ void PelrockEngine::loadHotspots(Common::File *roomFile, int roomOffset) {
 		spot.w = obj_bytes[5];
 		spot.h = obj_bytes[6];
 		spot.extra = obj_bytes[7] | (obj_bytes[8] << 8);
-		// debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, type_byte, spot.x, spot.y, spot.w, spot.h, spot.extra);
+		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, spot.type, spot.x, spot.y, spot.w, spot.h, spot.extra);
 		hotspots.push_back(spot);
 	}
 	_hotspots = hotspots;
@@ -610,6 +614,16 @@ void PelrockEngine::frames() {
 					_currentPopupFrame = 0;
 			}
 		}
+
+		int walkboxCount = 0;
+		for (Common::List<WalkBox>::iterator i = _currentRoomWalkboxes.begin(); i != _currentRoomWalkboxes.end(); i++) {
+			// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
+			_screen->drawLine(i->x, i->y, i->x + i->w, i->y, 0 + walkboxCount);
+			_screen->drawLine(i->x, i->y + i->h, i->x + i->w, i->y + i->h, 0 + walkboxCount);
+			_screen->drawLine(i->x, i->y, i->x, i->y + i->h, 0 + walkboxCount);
+			_screen->drawLine(i->x + i->w, i->y, i->x + i->w, i->y + i->h, 0 + walkboxCount);
+			walkboxCount++;
+		}
 		_screen->markAllDirty();
 		_screen->update();
 	}
@@ -671,7 +685,7 @@ AnimSet *PelrockEngine::isSpriteUnder(int x, int y) {
 	for (Common::List<AnimSet>::iterator i = _currentRoomAnims.begin(); i != _currentRoomAnims.end(); i++) {
 		if (mouseX >= i->x && mouseX <= (i->x + i->w) &&
 			mouseY >= i->y && mouseY <= (i->y + i->h)) {
-			debug("Sprite at (%d,%d) size (%d,%d), type = %d, extra = %d, enabled=%d", i->x, i->y, i->w, i->h, i->type, i->extra, i->enabled);
+			// debug("Sprite at (%d,%d) size (%d,%d), type = %d, extra = %d, enabled=%d", i->x, i->y, i->w, i->h, i->type, i->extra, i->enabled);
 			return &(*i);
 		}
 	}
@@ -731,25 +745,119 @@ void PelrockEngine::changeCursor(Cursor cursor) {
 void PelrockEngine::checkMouseHover() {
 	bool isSomethingUnder = false;
 
+
+	 // Calculate walk target first (before checking anything else)
+    Common::Point walkTarget = calculateWalkTarget(mouseX, mouseY);
+
+    // Check if walk target hits any exit
+    bool exitDetected = false;
+    Exit *exit = isExitAtPoint(walkTarget.x, walkTarget.y);
+    if (exit != nullptr) {
+        exitDetected = true;
+    }
+
 	AnimSet *sprite = isSpriteUnder(mouseX, mouseY);
 	if (sprite != nullptr) {
 		isSomethingUnder = true;
-		changeCursor(HOTSPOT);
 	}
 
 	HotSpot *hotspot = isHotspotUnder(mouseX, mouseY);
 	if (hotspot != nullptr) {
 		isSomethingUnder = true;
+	}
+	// Exit *exit = isExitUnder(mouseX, mouseY);
+	// if (exit != nullptr) {
+	// 	isSomethingUnder = true;
+	// 	changeCursor(EXIT);
+	// }
+	if (isSomethingUnder && exitDetected) {
+		changeCursor(COMBINATION);
+	}
+	else if (isSomethingUnder) {
 		changeCursor(HOTSPOT);
 	}
-	Exit *exit = isExitUnder(mouseX, mouseY);
-	if (exit != nullptr) {
-		isSomethingUnder = true;
+	else if (exitDetected) {
 		changeCursor(EXIT);
-	}
-	if (!isSomethingUnder) {
+	} else {
 		changeCursor(DEFAULT);
 	}
+
+}
+
+Common::Point PelrockEngine::calculateWalkTarget(int mouseX, int mouseY) {
+    // Starting point for pathfinding
+    int sourceX = mouseX;
+    int sourceY = mouseY;
+
+    // TODO: If hovering over a sprite/hotspot, adjust source point to sprite center
+    // For now, just use mouse position
+
+    // Find nearest walkable point in walkboxes
+    uint32 minDistance = 0xFFFFFFFF;
+    Common::Point bestTarget(sourceX, sourceY);
+
+    for (Common::List<WalkBox>::iterator it = _currentRoomWalkboxes.begin();
+         it != _currentRoomWalkboxes.end(); ++it) {
+
+        // Calculate distance from source point to this walkbox (Manhattan distance)
+        int dx = 0;
+        int dy = 0;
+
+        // Calculate horizontal distance
+        if (sourceX < it->x) {
+            dx = it->x - sourceX;
+        } else if (sourceX > it->x + it->w) {
+            dx = sourceX - (it->x + it->w);
+        }
+        // else: sourceX is inside walkbox horizontally, dx = 0
+
+        // Calculate vertical distance
+        if (sourceY < it->y) {
+            dy = it->y - sourceY;
+        } else if (sourceY > it->y + it->h) {
+            dy = sourceY - (it->y + it->h);
+        }
+        // else: sourceY is inside walkbox vertically, dy = 0
+
+        uint32 distance = dx + dy;
+
+        if (distance < minDistance) {
+            minDistance = distance;
+
+            // Calculate target point (nearest point on walkbox to source)
+            int targetX = sourceX;
+            int targetY = sourceY;
+
+            if (sourceX < it->x) {
+                targetX = it->x;
+            } else if (sourceX > it->x + it->w) {
+                targetX = it->x + it->w;
+            }
+
+            if (sourceY < it->y) {
+                targetY = it->y;
+            } else if (sourceY > it->y + it->h) {
+                targetY = it->y + it->h;
+            }
+
+            bestTarget.x = targetX;
+            bestTarget.y = targetY;
+        }
+    }
+
+    return bestTarget;
+}
+
+Exit *PelrockEngine::isExitAtPoint(int x, int y) {
+    for (Common::List<Exit>::iterator i = _currentRoomExits.begin();
+         i != _currentRoomExits.end(); ++i) {
+        // Check if point is inside exit trigger rectangle
+        if (x >= i->x && x <= (i->x + i->w) &&
+            y >= i->y && y <= (i->y + i->h)) {
+            return &(*i);
+        }
+    }
+    return nullptr;
 }
 
 void PelrockEngine::setScreen(int number, int dir) {
@@ -795,19 +903,11 @@ void PelrockEngine::setScreen(int number, int dir) {
 		hotsPotCount++;
 	}
 
-	Common::List<WalkBox> walkboxes = loadWalkboxes(&roomFile, roomOffset);
-	int walkboxCount = 0;
-	for (Common::List<WalkBox>::iterator i = walkboxes.begin(); i != walkboxes.end(); i++) {
-		// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
-		_screen->drawLine(i->x, i->y, i->x + i->w, i->y, 0 + walkboxCount);
-		_screen->drawLine(i->x, i->y + i->h, i->x + i->w, i->y + i->h, 0 + walkboxCount);
-		_screen->drawLine(i->x, i->y, i->x, i->y + i->h, 0 + walkboxCount);
-		_screen->drawLine(i->x + i->w, i->y, i->x + i->w, i->y + i->h, 0 + walkboxCount);
-		walkboxCount++;
-	}
+	_currentRoomWalkboxes = loadWalkboxes(&roomFile, roomOffset);
 
 	_currentRoomExits = loadExits(&roomFile, roomOffset);
 
+	int walkboxCount = 0;
 	for (Common::List<Exit>::iterator i = _currentRoomExits.begin(); i != _currentRoomExits.end(); i++) {
 		// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
 		_screen->drawLine(i->x, i->y, i->x + i->w, i->y, 0 + walkboxCount);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 3aff9bcd841..b37df6e9ce7 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -74,7 +74,8 @@ private:
 	byte *grabBackgroundSlice(int x, int y, int w, int h);
 	void putBackgroundSlice(int x, int y, int w, int h, byte *slice);
 	Common::List<VerbIcons> populateActionsMenu(HotSpot hotspot);
-
+ 	Common::Point calculateWalkTarget(int mouseX, int mouseY);
+    Exit *isExitAtPoint(int x, int y);
 	// render loop
 	void frames();
 	void checkMouseHover();
@@ -92,6 +93,7 @@ private:
 	Common::List<HoverArea> _hoverAreas;
 	Common::List<AnimSet> _currentRoomAnims;
 	Common::List<Exit> _currentRoomExits;
+	Common::List<WalkBox> _currentRoomWalkboxes;
 	int *_currentAnimFrames = nullptr;
 	int curAlfredFrame = 9;
 	uint16 mouseX = 0;


Commit: f0a541069d5e21b93933a5cdd9163e77b1d8e1ce
    https://github.com/scummvm/scummvm/commit/f0a541069d5e21b93933a5cdd9163e77b1d8e1ce
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:01+02:00

Commit Message:
PELROCK: Selects sprites that are candidates for hotspots

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index bed6c3a3d89..de827a6a239 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -145,8 +145,8 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		setScreen(0, 2);
-		// setScreen(24, 2);
+		// setScreen(0, 2);
+		setScreen(2, 2);
 	}
 }
 
@@ -265,42 +265,54 @@ Common::List<AnimSet> PelrockEngine::loadRoomAnimations(Common::File *roomFile,
 	uint32_t picOffset = 0;
 	for (int i = 0; i < 7; i++) {
 		uint32_t animOffset = metadata_start + (i * 44);
-		AnimSet animSet;
+		byte *animData = new byte[44];
 		roomFile->seek(animOffset, SEEK_SET);
-		animSet.x = roomFile->readSint16LE();
-		animSet.y = roomFile->readSint16LE();
-		animSet.w = roomFile->readByte();
-		animSet.h = roomFile->readByte();
-		animSet.extra = roomFile->readByte();
-		roomFile->skip(1); // reserved
-		animSet.numAnims = roomFile->readByte();
-		debug("AnimSet %d: x=%d y=%d w=%d h=%d numAnims=%d", i, animSet.x, animSet.y, animSet.w, animSet.h, animSet.numAnims);
-		animSet.animData = new Anim[animSet.numAnims];
-		roomFile->skip(1);
+		roomFile->read(animData, 44);
+		AnimSet animSet;
+		animSet.x = animData[0] | (animData[1] << 8);
+		animSet.y = animData[2] | (animData[3] << 8);
+		animSet.w = animData[4];
+		animSet.h = animData[5];
+		animSet.extra = animData[6];
+		// roomFile->skip(1); // reserved
+		animSet.numAnims = animData[8];
+		animSet.spriteType = animData[33];
+		animSet.actionFlags = animData[34];
+		animSet.isDisabled = animData[38];
 
+
+		animSet.animData = new Anim[animSet.numAnims];
+		debug("AnimSet %d has %d sub-anims, type %d, actionFlags %d, isDisabled? %d", i, animSet.numAnims, animSet.spriteType, animSet.actionFlags, animSet.isDisabled);
+		// roomFile->skip(1);
+		int subAnimOffset = 10;
 		for (int j = 0; j < animSet.numAnims; j++) {
-			byte frames = roomFile->readByte();
+
 			Anim anim;
 			anim.x = animSet.x;
 			anim.y = animSet.y;
 			anim.w = animSet.w;
 			anim.h = animSet.h;
 			anim.curFrame = 0;
-			anim.nframes = frames;
+
+			anim.nframes = animData[subAnimOffset + j];
+			anim.loopCount = animData[subAnimOffset + 4 + j];
+			anim.speed = animData[subAnimOffset + 8 + j];
 			anim.animData = new byte[anim.nframes];
 			if (anim.w > 0 && anim.h > 0 && anim.nframes > 0) {
 				uint32_t needed = anim.w * anim.h * anim.nframes;
 				anim.animData = new byte[needed];
 				Common::copy(pic + picOffset, pic + picOffset + needed, anim.animData);
 				animSet.animData[j] = anim;
-				debug("Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes);
+				debug("  Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d loopCount=%d speed=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes, anim.loopCount, anim.speed);
 				picOffset += needed;
 			} else {
 				continue;
 				debug("Anim %d-%d: invalid dimensions, skipping", i, j);
 			}
 			animSet.animData[j] = anim;
+
 		}
+
 		anims.push_back(animSet);
 
 		// if (w > 0 && h > 0 && frames > 0) {
@@ -354,6 +366,62 @@ Common::List<WalkBox> PelrockEngine::loadWalkboxes(Common::File *roomFile, int r
 	return walkboxes;
 }
 
+void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
+
+	Common::List<AnimSet> anims = loadRoomAnimations(roomFile, roomOffset);
+
+	Common::List<HotSpot> hotspots;
+
+
+
+	for (Common::List<AnimSet>::iterator i = anims.begin(); i != anims.end(); i++) {
+		if(!i->isDisabled) {
+			hotspots.push_back(
+				HotSpot{
+					i->actionFlags,
+					i->x,
+					i->y,
+					i->w,
+					i->h,
+					i->extra,
+				}
+			);
+		}
+	}
+
+	Common::List<HotSpot> staticHotspots = loadHotspots(roomFile, roomOffset);
+	Common::List<Exit> exits = loadExits(roomFile, roomOffset);
+	Common::List<WalkBox> walkboxes = loadWalkboxes(roomFile, roomOffset);
+
+
+
+	int hotsPotCount = 0;
+	for (Common::List<HotSpot>::iterator i = staticHotspots.begin(); i != staticHotspots.end(); i++) {
+		// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
+		_screen->drawLine(i->x, i->y, i->x + i->w, i->y, 100 + hotsPotCount);
+		_screen->drawLine(i->x, i->y + i->h, i->x + i->w, i->y + i->h, 100 + hotsPotCount);
+		_screen->drawLine(i->x, i->y, i->x, i->y + i->h, 100 + hotsPotCount);
+		_screen->drawLine(i->x + i->w, i->y, i->x + i->w, i->y + i->h, 100 + hotsPotCount);
+		hotsPotCount++;
+		hotspots.push_back(*i);
+	}
+
+	int walkboxCount = 0;
+	for (Common::List<Exit>::iterator i = _currentRoomExits.begin(); i != _currentRoomExits.end(); i++) {
+		// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
+		_screen->drawLine(i->x, i->y, i->x + i->w, i->y, 0 + walkboxCount);
+		_screen->drawLine(i->x, i->y + i->h, i->x + i->w, i->y + i->h, 0 + walkboxCount);
+		_screen->drawLine(i->x, i->y, i->x, i->y + i->h, 0 + walkboxCount);
+		_screen->drawLine(i->x + i->w, i->y, i->x + i->w, i->y + i->h, 0 + walkboxCount);
+		walkboxCount++;
+	}
+
+
+	_currentRoomAnims = anims;
+	_currentRoomHotspots = hotspots;
+	_currentRoomExits = exits;
+	_currentRoomWalkboxes = walkboxes;
+}
 
 void PelrockEngine::loadCursors() {
 	Common::File alfred7File;
@@ -424,13 +492,12 @@ Common::List<Exit> PelrockEngine::loadExits(Common::File *roomFile, int roomOffs
 		exit.targetX = roomFile->readUint16LE();
 		exit.targetY = roomFile->readUint16LE();
 		exit.dir = roomFile->readByte();
-		debug("Exit %d: x=%d y=%d w=%d h=%d targetRoom=%d targetX=%d targetY=%d", i, exit.x, exit.y, exit.w, exit.h, exit.targetRoom, exit.targetX, exit.targetY);
 		exits.push_back(exit);
 	}
 	return exits;
 }
 
-void PelrockEngine::loadHotspots(Common::File *roomFile, int roomOffset) {
+Common::List<HotSpot>PelrockEngine::loadHotspots(Common::File *roomFile, int roomOffset) {
 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
 
 	roomFile->seek(pair10_offset_pos, SEEK_SET);
@@ -456,7 +523,7 @@ void PelrockEngine::loadHotspots(Common::File *roomFile, int roomOffset) {
 		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, spot.type, spot.x, spot.y, spot.w, spot.h, spot.extra);
 		hotspots.push_back(spot);
 	}
-	_hotspots = hotspots;
+	return hotspots;
 	// uint32_t hover_areas_start = pair10_data_offset + 0x1BE;
 	// roomFile->seek(hover_areas_start, SEEK_SET);
 
@@ -659,7 +726,7 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 
 HotSpot *PelrockEngine::isHotspotUnder(int x, int y) {
 
-	for (Common::List<HotSpot>::iterator i = _hotspots.begin(); i != _hotspots.end(); i++) {
+	for (Common::List<HotSpot>::iterator i = _currentRoomHotspots.begin(); i != _currentRoomHotspots.end(); i++) {
 		if (mouseX >= i->x && mouseX <= (i->x + i->w) &&
 			mouseY >= i->y && mouseY <= (i->y + i->h)) {
 			int lookable = i->extra & 0x0B;
@@ -724,7 +791,10 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 		_bgPopupBalloon = nullptr;
 	}
 
-	Exit *exit = isExitUnder(mouseX, mouseY);
+	Common::Point walkTarget = calculateWalkTarget(mouseX, mouseY);
+
+    Exit *exit = isExitAtPoint(walkTarget.x, walkTarget.y);
+
 	if (exit != nullptr) {
 		xAlfred = exit->targetX;
 		yAlfred = exit->targetY - kAlfredFrameHeight;
@@ -756,11 +826,6 @@ void PelrockEngine::checkMouseHover() {
         exitDetected = true;
     }
 
-	AnimSet *sprite = isSpriteUnder(mouseX, mouseY);
-	if (sprite != nullptr) {
-		isSomethingUnder = true;
-	}
-
 	HotSpot *hotspot = isHotspotUnder(mouseX, mouseY);
 	if (hotspot != nullptr) {
 		isSomethingUnder = true;
@@ -890,32 +955,9 @@ void PelrockEngine::setScreen(int number, int dir) {
 			_screen->setPixel(i, j, background[j * 640 + i]);
 		}
 	}
-	_currentRoomAnims = loadRoomAnimations(&roomFile, roomOffset);
-
-	loadHotspots(&roomFile, roomOffset);
-	int hotsPotCount = 0;
-	for (Common::List<HotSpot>::iterator i = _hotspots.begin(); i != _hotspots.end(); i++) {
-		// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
-		_screen->drawLine(i->x, i->y, i->x + i->w, i->y, 100 + hotsPotCount);
-		_screen->drawLine(i->x, i->y + i->h, i->x + i->w, i->y + i->h, 100 + hotsPotCount);
-		_screen->drawLine(i->x, i->y, i->x, i->y + i->h, 100 + hotsPotCount);
-		_screen->drawLine(i->x + i->w, i->y, i->x + i->w, i->y + i->h, 100 + hotsPotCount);
-		hotsPotCount++;
-	}
 
-	_currentRoomWalkboxes = loadWalkboxes(&roomFile, roomOffset);
 
-	_currentRoomExits = loadExits(&roomFile, roomOffset);
-
-	int walkboxCount = 0;
-	for (Common::List<Exit>::iterator i = _currentRoomExits.begin(); i != _currentRoomExits.end(); i++) {
-		// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
-		_screen->drawLine(i->x, i->y, i->x + i->w, i->y, 0 + walkboxCount);
-		_screen->drawLine(i->x, i->y + i->h, i->x + i->w, i->y + i->h, 0 + walkboxCount);
-		_screen->drawLine(i->x, i->y, i->x, i->y + i->h, 0 + walkboxCount);
-		_screen->drawLine(i->x + i->w, i->y, i->x + i->w, i->y + i->h, 0 + walkboxCount);
-		walkboxCount++;
-	}
+	loadRoomMetadata(&roomFile, roomOffset);
 
 	_screen->markAllDirty();
 	roomFile.close();
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index b37df6e9ce7..457e4ab4f8e 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -63,9 +63,9 @@ private:
 	// Room data
 	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
-	Common::List<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
 	void loadMainCharacterAnims();
-	void loadHotspots(Common::File *roomFile, int roomOffset);
+	Common::List<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
+	Common::List<HotSpot>loadHotspots(Common::File *roomFile, int roomOffset);
 	Common::List<Exit> loadExits(Common::File *roomFile, int roomOffset);
 	Common::List<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
 	void loadRoomMetadata(Common::File *roomFile, int roomOffset);
@@ -89,7 +89,7 @@ private:
 
 	ChronoManager *_chronoManager = nullptr;
 	byte *standingAnim = new byte[3060 * 102];
-	Common::List<HotSpot> _hotspots;
+	Common::List<HotSpot> _currentRoomHotspots;
 	Common::List<HoverArea> _hoverAreas;
 	Common::List<AnimSet> _currentRoomAnims;
 	Common::List<Exit> _currentRoomExits;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index f30cb37c2f0..25ec19546a8 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -73,6 +73,8 @@ struct Anim {
 	int nframes;
 	int curFrame;
 	byte *animData;
+	byte loopCount;
+	byte speed;
 };
 
 struct Exit {
@@ -90,15 +92,16 @@ struct Exit {
 
 struct AnimSet {
 	byte type;
-	int x;
-	int y;
-	int w;
-	int h;
-	int speed;
-	int numAnims;
+	int x; //0
+	int y;//2
+	int w;//4
+	int h;//5
+	byte extra; //6
+	int numAnims; //8
+	byte spriteType; //33
+	byte actionFlags;//34
+	bool isDisabled; //38
 	Anim *animData;
-	byte extra;
-	bool enabled;
 };
 
 struct HoverArea {


Commit: 98858463ec5b1ef6c27fc1795dc749ecbb9f0f08
    https://github.com/scummvm/scummvm/commit/98858463ec5b1ef6c27fc1795dc749ecbb9f0f08
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:02+02:00

Commit Message:
PELROCK: Display available actions

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index de827a6a239..b75db195894 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -280,7 +280,6 @@ Common::List<AnimSet> PelrockEngine::loadRoomAnimations(Common::File *roomFile,
 		animSet.actionFlags = animData[34];
 		animSet.isDisabled = animData[38];
 
-
 		animSet.animData = new Anim[animSet.numAnims];
 		debug("AnimSet %d has %d sub-anims, type %d, actionFlags %d, isDisabled? %d", i, animSet.numAnims, animSet.spriteType, animSet.actionFlags, animSet.isDisabled);
 		// roomFile->skip(1);
@@ -310,7 +309,6 @@ Common::List<AnimSet> PelrockEngine::loadRoomAnimations(Common::File *roomFile,
 				debug("Anim %d-%d: invalid dimensions, skipping", i, j);
 			}
 			animSet.animData[j] = anim;
-
 		}
 
 		anims.push_back(animSet);
@@ -372,10 +370,8 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 
 	Common::List<HotSpot> hotspots;
 
-
-
 	for (Common::List<AnimSet>::iterator i = anims.begin(); i != anims.end(); i++) {
-		if(!i->isDisabled) {
+		if (!i->isDisabled) {
 			hotspots.push_back(
 				HotSpot{
 					i->actionFlags,
@@ -384,8 +380,7 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 					i->w,
 					i->h,
 					i->extra,
-				}
-			);
+				});
 		}
 	}
 
@@ -393,8 +388,6 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	Common::List<Exit> exits = loadExits(roomFile, roomOffset);
 	Common::List<WalkBox> walkboxes = loadWalkboxes(roomFile, roomOffset);
 
-
-
 	int hotsPotCount = 0;
 	for (Common::List<HotSpot>::iterator i = staticHotspots.begin(); i != staticHotspots.end(); i++) {
 		// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
@@ -416,7 +409,6 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 		walkboxCount++;
 	}
 
-
 	_currentRoomAnims = anims;
 	_currentRoomHotspots = hotspots;
 	_currentRoomExits = exits;
@@ -497,7 +489,7 @@ Common::List<Exit> PelrockEngine::loadExits(Common::File *roomFile, int roomOffs
 	return exits;
 }
 
-Common::List<HotSpot>PelrockEngine::loadHotspots(Common::File *roomFile, int roomOffset) {
+Common::List<HotSpot> PelrockEngine::loadHotspots(Common::File *roomFile, int roomOffset) {
 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
 
 	roomFile->seek(pair10_offset_pos, SEEK_SET);
@@ -526,7 +518,6 @@ Common::List<HotSpot>PelrockEngine::loadHotspots(Common::File *roomFile, int roo
 	return hotspots;
 	// uint32_t hover_areas_start = pair10_data_offset + 0x1BE;
 	// roomFile->seek(hover_areas_start, SEEK_SET);
-
 }
 
 void PelrockEngine::loadMainCharacterAnims() {
@@ -574,38 +565,37 @@ Common::List<VerbIcons> PelrockEngine::populateActionsMenu(HotSpot hotspot) {
 	Common::List<VerbIcons> verbs;
 	verbs.push_back(LOOK);
 
-	if(hotspot.type & 1) {
+	if (hotspot.type & 1) {
 		debug("Hotspot allows OPEN action");
 		verbs.push_back(OPEN);
 	}
-	if(hotspot.type & 2) {
+	if (hotspot.type & 2) {
 		debug("Hotspot allows CLOSE action");
 		verbs.push_back(CLOSE);
 	}
-	if(hotspot.type & 4) {
+	if (hotspot.type & 4) {
 		debug("Hotspot allows UNKNOWN action");
 		verbs.push_back(UNKNOWN);
 	}
-	if(hotspot.type & 8) {
+	if (hotspot.type & 8) {
 		debug("Hotspot allows PICKUP action");
 		verbs.push_back(PICKUP);
 	}
-	if(hotspot.type & 16) {
+	if (hotspot.type & 16) {
 		debug("Hotspot allows TALK action");
 		verbs.push_back(TALK);
 	}
-	if(hotspot.type & 32) {
+	if (hotspot.type & 32) {
 		debug("Hotspot allows WALK action");
 		verbs.push_back(PUSH);
 	}
-	if(hotspot.type & 128) {
+	if (hotspot.type & 128) {
 		debug("Hotspot allows PULL action");
 		verbs.push_back(PULL);
 	}
 	return verbs;
 }
 
-
 void PelrockEngine::frames() {
 
 	if (_chronoManager->_gameTick) {
@@ -719,7 +709,6 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 		_displayPopup = true;
 		_currentPopupFrame = 0;
 		_currentHotspot = hotspot;
-		populateActionsMenu(*hotspot);
 		_bgPopupBalloon = grabBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight);
 	}
 }
@@ -769,15 +758,32 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 		}
 	}
 
-	// for (Common::List<VerbIcons>::iterator i = verbList.begin(); i != verbList.end(); i++) {
-	// 	debug("Verb icon to show: %d", *i);
-	// }
+	Common::List<VerbIcons> availableActions = populateActionsMenu(*_currentHotspot);
+
+	for (Common::List<VerbIcons>::iterator i = availableActions.begin(); i != availableActions.end(); i++) {
+		debug("Verb icon to show: %d", *i);
+	}
 
 	for (uint32_t y = 0; y < kVerbIconHeight; y++) {
 		for (uint32_t x = 0; x < kVerbIconWidth; x++) {
 			unsigned int src_pos = y * kVerbIconWidth + x;
 			if (_verbIcons[LOOK][src_pos] != 1)
-				_screen->setPixel(x + posx + 10, y + posy + 10, _verbIcons[LOOK][src_pos]);
+				_screen->setPixel(x + posx + 20, y + posy + 20, _verbIcons[LOOK][src_pos]);
+		}
+	}
+
+	for (Common::List<VerbIcons>::iterator i = availableActions.begin(); i != availableActions.end(); i++) {
+		VerbIcons verb = *i;
+		int index = 0;
+		for (Common::List<VerbIcons>::iterator j = availableActions.begin(); j != i; j++) {
+			index++;
+		}
+		for (uint32_t y = 0; y < kVerbIconHeight; y++) {
+			for (uint32_t x = 0; x < kVerbIconWidth; x++) {
+				unsigned int src_pos = y * kVerbIconWidth + x;
+				if (_verbIcons[verb][src_pos] != 1)
+					_screen->setPixel(x + posx + 20 + (index * (kVerbIconWidth + 2)), y + posy + 20, _verbIcons[verb][src_pos]);
+			}
 		}
 	}
 }
@@ -793,7 +799,7 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 
 	Common::Point walkTarget = calculateWalkTarget(mouseX, mouseY);
 
-    Exit *exit = isExitAtPoint(walkTarget.x, walkTarget.y);
+	Exit *exit = isExitAtPoint(walkTarget.x, walkTarget.y);
 
 	if (exit != nullptr) {
 		xAlfred = exit->targetX;
@@ -815,16 +821,15 @@ void PelrockEngine::changeCursor(Cursor cursor) {
 void PelrockEngine::checkMouseHover() {
 	bool isSomethingUnder = false;
 
+	// Calculate walk target first (before checking anything else)
+	Common::Point walkTarget = calculateWalkTarget(mouseX, mouseY);
 
-	 // Calculate walk target first (before checking anything else)
-    Common::Point walkTarget = calculateWalkTarget(mouseX, mouseY);
-
-    // Check if walk target hits any exit
-    bool exitDetected = false;
-    Exit *exit = isExitAtPoint(walkTarget.x, walkTarget.y);
-    if (exit != nullptr) {
-        exitDetected = true;
-    }
+	// Check if walk target hits any exit
+	bool exitDetected = false;
+	Exit *exit = isExitAtPoint(walkTarget.x, walkTarget.y);
+	if (exit != nullptr) {
+		exitDetected = true;
+	}
 
 	HotSpot *hotspot = isHotspotUnder(mouseX, mouseY);
 	if (hotspot != nullptr) {
@@ -837,92 +842,89 @@ void PelrockEngine::checkMouseHover() {
 	// }
 	if (isSomethingUnder && exitDetected) {
 		changeCursor(COMBINATION);
-	}
-	else if (isSomethingUnder) {
+	} else if (isSomethingUnder) {
 		changeCursor(HOTSPOT);
-	}
-	else if (exitDetected) {
+	} else if (exitDetected) {
 		changeCursor(EXIT);
 	} else {
 		changeCursor(DEFAULT);
 	}
-
 }
 
 Common::Point PelrockEngine::calculateWalkTarget(int mouseX, int mouseY) {
-    // Starting point for pathfinding
-    int sourceX = mouseX;
-    int sourceY = mouseY;
+	// Starting point for pathfinding
+	int sourceX = mouseX;
+	int sourceY = mouseY;
 
-    // TODO: If hovering over a sprite/hotspot, adjust source point to sprite center
-    // For now, just use mouse position
+	// TODO: If hovering over a sprite/hotspot, adjust source point to sprite center
+	// For now, just use mouse position
 
-    // Find nearest walkable point in walkboxes
-    uint32 minDistance = 0xFFFFFFFF;
-    Common::Point bestTarget(sourceX, sourceY);
+	// Find nearest walkable point in walkboxes
+	uint32 minDistance = 0xFFFFFFFF;
+	Common::Point bestTarget(sourceX, sourceY);
 
-    for (Common::List<WalkBox>::iterator it = _currentRoomWalkboxes.begin();
-         it != _currentRoomWalkboxes.end(); ++it) {
+	for (Common::List<WalkBox>::iterator it = _currentRoomWalkboxes.begin();
+		 it != _currentRoomWalkboxes.end(); ++it) {
 
-        // Calculate distance from source point to this walkbox (Manhattan distance)
-        int dx = 0;
-        int dy = 0;
+		// Calculate distance from source point to this walkbox (Manhattan distance)
+		int dx = 0;
+		int dy = 0;
 
-        // Calculate horizontal distance
-        if (sourceX < it->x) {
-            dx = it->x - sourceX;
-        } else if (sourceX > it->x + it->w) {
-            dx = sourceX - (it->x + it->w);
-        }
-        // else: sourceX is inside walkbox horizontally, dx = 0
+		// Calculate horizontal distance
+		if (sourceX < it->x) {
+			dx = it->x - sourceX;
+		} else if (sourceX > it->x + it->w) {
+			dx = sourceX - (it->x + it->w);
+		}
+		// else: sourceX is inside walkbox horizontally, dx = 0
 
-        // Calculate vertical distance
-        if (sourceY < it->y) {
-            dy = it->y - sourceY;
-        } else if (sourceY > it->y + it->h) {
-            dy = sourceY - (it->y + it->h);
-        }
-        // else: sourceY is inside walkbox vertically, dy = 0
+		// Calculate vertical distance
+		if (sourceY < it->y) {
+			dy = it->y - sourceY;
+		} else if (sourceY > it->y + it->h) {
+			dy = sourceY - (it->y + it->h);
+		}
+		// else: sourceY is inside walkbox vertically, dy = 0
 
-        uint32 distance = dx + dy;
+		uint32 distance = dx + dy;
 
-        if (distance < minDistance) {
-            minDistance = distance;
+		if (distance < minDistance) {
+			minDistance = distance;
 
-            // Calculate target point (nearest point on walkbox to source)
-            int targetX = sourceX;
-            int targetY = sourceY;
+			// Calculate target point (nearest point on walkbox to source)
+			int targetX = sourceX;
+			int targetY = sourceY;
 
-            if (sourceX < it->x) {
-                targetX = it->x;
-            } else if (sourceX > it->x + it->w) {
-                targetX = it->x + it->w;
-            }
+			if (sourceX < it->x) {
+				targetX = it->x;
+			} else if (sourceX > it->x + it->w) {
+				targetX = it->x + it->w;
+			}
 
-            if (sourceY < it->y) {
-                targetY = it->y;
-            } else if (sourceY > it->y + it->h) {
-                targetY = it->y + it->h;
-            }
+			if (sourceY < it->y) {
+				targetY = it->y;
+			} else if (sourceY > it->y + it->h) {
+				targetY = it->y + it->h;
+			}
 
-            bestTarget.x = targetX;
-            bestTarget.y = targetY;
-        }
-    }
+			bestTarget.x = targetX;
+			bestTarget.y = targetY;
+		}
+	}
 
-    return bestTarget;
+	return bestTarget;
 }
 
 Exit *PelrockEngine::isExitAtPoint(int x, int y) {
-    for (Common::List<Exit>::iterator i = _currentRoomExits.begin();
-         i != _currentRoomExits.end(); ++i) {
-        // Check if point is inside exit trigger rectangle
-        if (x >= i->x && x <= (i->x + i->w) &&
-            y >= i->y && y <= (i->y + i->h)) {
-            return &(*i);
-        }
-    }
-    return nullptr;
+	for (Common::List<Exit>::iterator i = _currentRoomExits.begin();
+		 i != _currentRoomExits.end(); ++i) {
+		// Check if point is inside exit trigger rectangle
+		if (x >= i->x && x <= (i->x + i->w) &&
+			y >= i->y && y <= (i->y + i->h)) {
+			return &(*i);
+		}
+	}
+	return nullptr;
 }
 
 void PelrockEngine::setScreen(int number, int dir) {
@@ -956,7 +958,6 @@ void PelrockEngine::setScreen(int number, int dir) {
 		}
 	}
 
-
 	loadRoomMetadata(&roomFile, roomOffset);
 
 	_screen->markAllDirty();
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 457e4ab4f8e..45d751e42e1 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -65,7 +65,7 @@ private:
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
 	void loadMainCharacterAnims();
 	Common::List<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
-	Common::List<HotSpot>loadHotspots(Common::File *roomFile, int roomOffset);
+	Common::List<HotSpot> loadHotspots(Common::File *roomFile, int roomOffset);
 	Common::List<Exit> loadExits(Common::File *roomFile, int roomOffset);
 	Common::List<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
 	void loadRoomMetadata(Common::File *roomFile, int roomOffset);
@@ -74,8 +74,8 @@ private:
 	byte *grabBackgroundSlice(int x, int y, int w, int h);
 	void putBackgroundSlice(int x, int y, int w, int h, byte *slice);
 	Common::List<VerbIcons> populateActionsMenu(HotSpot hotspot);
- 	Common::Point calculateWalkTarget(int mouseX, int mouseY);
-    Exit *isExitAtPoint(int x, int y);
+	Common::Point calculateWalkTarget(int mouseX, int mouseY);
+	Exit *isExitAtPoint(int x, int y);
 	// render loop
 	void frames();
 	void checkMouseHover();


Commit: 2efc7a1a41591703b6e41478bcca4636fd25fe49
    https://github.com/scummvm/scummvm/commit/2efc7a1a41591703b6e41478bcca4636fd25fe49
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:02+02:00

Commit Message:
PELROCK: Renders descriptions

Changed paths:
    engines/pelrock/chrono.cpp
    engines/pelrock/chrono.h
    engines/pelrock/fonts/large_font.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/chrono.cpp b/engines/pelrock/chrono.cpp
index 226b09807bc..355df50a828 100644
--- a/engines/pelrock/chrono.cpp
+++ b/engines/pelrock/chrono.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "common/events.h"
 #include "common/system.h"
 
 #include "pelrock/chrono.h"
@@ -57,4 +58,17 @@ void ChronoManager::changeSpeed() {
 		_speedMultiplier = 1;
 }
 
+void ChronoManager::delay(uint32 ms) {
+	uint32 delayStart = g_system->getMillis();
+
+	ms = ms / _speedMultiplier;
+	Common::Event e;
+	while ((g_system->getMillis() - delayStart) < ms && !g_engine->shouldQuit()) {
+		while (g_system->getEventManager()->pollEvent(e)) {
+
+		}
+		g_engine->_screen->update();
+	}
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/chrono.h b/engines/pelrock/chrono.h
index 5257563225f..0adce498a89 100644
--- a/engines/pelrock/chrono.h
+++ b/engines/pelrock/chrono.h
@@ -40,6 +40,7 @@ public:
 	~ChronoManager();
 	void updateChrono();
 	void changeSpeed();
+	void delay(uint32 ms);
 
 	bool _gameTick = false;
 	bool _gameTickHalfSpeed = false;
diff --git a/engines/pelrock/fonts/large_font.cpp b/engines/pelrock/fonts/large_font.cpp
index 07f41a0e63f..0e41a4dd812 100644
--- a/engines/pelrock/fonts/large_font.cpp
+++ b/engines/pelrock/fonts/large_font.cpp
@@ -51,34 +51,12 @@ int LargeFont::getCharWidth(uint32 chr) const {
 }
 
 void LargeFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
-
-	// """Extract a single character from the large font data (12x24 pixels)
-	// Each character is 48 bytes (24 rows × 2 bytes per row)"""
-	// offset = char_index * 0x30  # 48 bytes per character
-	// char_data = data[offset:offset + 0x30]
-
-	// # Create 12x24 pixel array
-	// pixels = np.zeros((24, 12), dtype=np.uint8)
-
-	// # Process each row (2 bytes per row)
-	// for row in range(24):
-	//     byte1 = char_data[row * 2]
-	//     byte2 = char_data[row * 2 + 1]
-
-	//     # Process 12 bits (12 pixels) from the two bytes
-	//     for bit in range(8):
-	//         pixels[row, bit] = 255 if (byte1 & (0x80 >> bit)) else 0
-	//     for bit in range(4):
-	//         pixels[row, bit + 8] = 255 if (byte2 & (0x80 >> bit)) else 0
-
-	// return pixels
-
-	if (!_fontData || chr > 255) {
+	chr -= 32; // Adjust for font starting at ASCII 32
+	if (!_fontData || chr > 255 || chr < 0) {
 		return;
 	}
-	chr -= 32; // Adjust for font starting at ASCII 32
 	int charOffset = chr * 0x30;
-	debug("LargeFont::drawChar: Drawing char %d at offset %d", chr, charOffset);
+
 	for (int i = 0; i < 24; i++) {
 		byte rowByte1 = _fontData[charOffset + i * 2];
 		byte rowByte2 = _fontData[charOffset + i * 2 + 1];
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index b75db195894..748071a4d46 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -243,6 +243,7 @@ void PelrockEngine::getBackground(Common::File *roomFile, int roomOffset, byte *
 
 Common::List<AnimSet> PelrockEngine::loadRoomAnimations(Common::File *roomFile, int roomOffset) {
 	uint32_t pair_offset = roomOffset + (8 * 8);
+	debug("Sprite pair offset position: %d", pair_offset);
 	roomFile->seek(pair_offset, SEEK_SET);
 	uint32_t offset = roomFile->readUint32LE();
 	uint32_t size = roomFile->readUint32LE();
@@ -279,7 +280,9 @@ Common::List<AnimSet> PelrockEngine::loadRoomAnimations(Common::File *roomFile,
 		animSet.spriteType = animData[33];
 		animSet.actionFlags = animData[34];
 		animSet.isDisabled = animData[38];
-
+		if(animSet.numAnims == 0) {
+			break;
+		}
 		animSet.animData = new Anim[animSet.numAnims];
 		debug("AnimSet %d has %d sub-anims, type %d, actionFlags %d, isDisabled? %d", i, animSet.numAnims, animSet.spriteType, animSet.actionFlags, animSet.isDisabled);
 		// roomFile->skip(1);
@@ -352,7 +355,7 @@ Common::List<WalkBox> PelrockEngine::loadWalkboxes(Common::File *roomFile, int r
 		int16 w = roomFile->readSint16LE();
 		int16 h = roomFile->readSint16LE();
 		byte flags = roomFile->readByte();
-		debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
+		// debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
 		WalkBox box;
 		box.x = x1;
 		box.y = y1;
@@ -364,39 +367,83 @@ Common::List<WalkBox> PelrockEngine::loadWalkboxes(Common::File *roomFile, int r
 	return walkboxes;
 }
 
+Common::Array<Description> PelrockEngine::loadRoomDescriptions(Common::File *roomFile, int roomOffset) {
+	uint32_t pair12_offset_pos = roomOffset + (12 * 8);
+	roomFile->seek(pair12_offset_pos, SEEK_SET);
+	// roomFile->skip(4);
+	uint32_t pair12_data_offset = roomFile->readUint32LE();
+	uint32_t pair12_size = roomFile->readUint32LE();
+
+	roomFile->seek(pair12_data_offset, SEEK_SET);
+	byte *data = new byte[pair12_size];
+	roomFile->read(data, pair12_size);
+	Common::Array<Description> descriptions;
+	uint32_t pos = 0;
+	while (pos < (pair12_size)) {
+		// char *desc = new char[256];
+		int desc_pos = 0;
+		if(data[pos] == 0xFF) {
+			Description description;
+			description.itemId = data[++pos];
+			pos += 2;
+			description.index = data[pos++];
+			description.text = "";
+			// debug("Found description terminator");
+			while (pos < (pair12_size) && data[pos] != 0xFD && pos < (pair12_size)) {
+				// debug(" char: %c", data[pos]);
+				if(data[pos] != 0x00) {
+					description.text.append(1, (char)data[pos]);
+				}
+				// desc[desc_pos++] = (char)data[pos];
+				// debug("Current desc: %s", desc);
+				pos++;
+			}
+			debug("Found description for item %d index %d, text: %s", description.itemId, description.index, description.text.c_str());
+
+			descriptions.push_back(description);
+		}
+		pos++;
+	}
+
+	delete[] data;
+	// for (Common::List<Common::String>::iterator i = descriptions.begin(); i != descriptions.end(); i++) {
+	// 	debug("Room description: %s", i->c_str());
+	// }
+	return descriptions;
+}
+
 void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 
-	Common::List<AnimSet> anims = loadRoomAnimations(roomFile, roomOffset);
+	Common::Array<Description> descriptions = loadRoomDescriptions(roomFile, roomOffset);
 
-	Common::List<HotSpot> hotspots;
+	Common::List<AnimSet> anims = loadRoomAnimations(roomFile, roomOffset);
 
+	Common::Array<HotSpot> hotspots;
+	int count = 0;
 	for (Common::List<AnimSet>::iterator i = anims.begin(); i != anims.end(); i++) {
-		if (!i->isDisabled) {
-			hotspots.push_back(
-				HotSpot{
-					i->actionFlags,
-					i->x,
-					i->y,
-					i->w,
-					i->h,
-					i->extra,
-				});
-		}
+
+		HotSpot thisHotspot;
+		thisHotspot.x = i->x;
+		thisHotspot.y = i->y;
+		thisHotspot.w = i->w;
+		thisHotspot.h = i->h;
+		thisHotspot.extra = i->extra;
+		thisHotspot.type = i->actionFlags;
+		thisHotspot.isEnabled = !i->isDisabled;
+		thisHotspot.description = descriptions[count].text;
+		hotspots.push_back(thisHotspot);
+		count++;
 	}
 
-	Common::List<HotSpot> staticHotspots = loadHotspots(roomFile, roomOffset);
+	Common::Array<HotSpot> staticHotspots = loadHotspots(roomFile, roomOffset);
 	Common::List<Exit> exits = loadExits(roomFile, roomOffset);
 	Common::List<WalkBox> walkboxes = loadWalkboxes(roomFile, roomOffset);
 
-	int hotsPotCount = 0;
-	for (Common::List<HotSpot>::iterator i = staticHotspots.begin(); i != staticHotspots.end(); i++) {
-		// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
-		_screen->drawLine(i->x, i->y, i->x + i->w, i->y, 100 + hotsPotCount);
-		_screen->drawLine(i->x, i->y + i->h, i->x + i->w, i->y + i->h, 100 + hotsPotCount);
-		_screen->drawLine(i->x, i->y, i->x, i->y + i->h, 100 + hotsPotCount);
-		_screen->drawLine(i->x + i->w, i->y, i->x + i->w, i->y + i->h, 100 + hotsPotCount);
-		hotsPotCount++;
-		hotspots.push_back(*i);
+	debug("total descriptions = %d, anims = %d, hotspots = %d", descriptions.size(), anims.size(), staticHotspots.size());
+	for (int i = 0; i < staticHotspots.size(); i++) {
+		HotSpot hotspot = staticHotspots[i];
+		hotspot.description =  descriptions[anims.size() + i].text;
+		hotspots.push_back(hotspot);
 	}
 
 	int walkboxCount = 0;
@@ -413,6 +460,17 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	_currentRoomHotspots = hotspots;
 	_currentRoomExits = exits;
 	_currentRoomWalkboxes = walkboxes;
+	_currentRoomDescriptions = descriptions;
+
+
+	for(int i=0; i < _currentRoomHotspots.size(); i++) {
+		HotSpot hotspot = _currentRoomHotspots[i];
+		debug("Hotspot %d: x=%d y=%d w=%d h=%d type=%d enabled? %d desc=%s", i, hotspot.x, hotspot.y, hotspot.w, hotspot.h, hotspot.type, hotspot.isEnabled, hotspot.description.c_str());
+		_screen->drawLine(hotspot.x, hotspot.y, hotspot.x + hotspot.w, hotspot.y, 200 + i);
+		_screen->drawLine(hotspot.x, hotspot.y + hotspot.h, hotspot.x + hotspot.w, hotspot.y + hotspot.h, 200 + i);
+		_screen->drawLine(hotspot.x, hotspot.y, hotspot.x, hotspot.y + hotspot.h, 200 + i);
+		_screen->drawLine(hotspot.x + hotspot.w, hotspot.y, hotspot.x + hotspot.w, hotspot.y + hotspot.h, 200 + i);
+	}
 }
 
 void PelrockEngine::loadCursors() {
@@ -489,9 +547,9 @@ Common::List<Exit> PelrockEngine::loadExits(Common::File *roomFile, int roomOffs
 	return exits;
 }
 
-Common::List<HotSpot> PelrockEngine::loadHotspots(Common::File *roomFile, int roomOffset) {
+Common::Array<HotSpot> PelrockEngine::loadHotspots(Common::File *roomFile, int roomOffset) {
 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
-
+	debug("Hotspot(10)  pair offset position: %d", pair10_offset_pos);
 	roomFile->seek(pair10_offset_pos, SEEK_SET);
 	uint32_t pair10_data_offset = roomFile->readUint32LE();
 	uint32_t pair10_size = roomFile->readUint32LE();
@@ -499,7 +557,7 @@ Common::List<HotSpot> PelrockEngine::loadHotspots(Common::File *roomFile, int ro
 	roomFile->seek(count_offset, SEEK_SET);
 	byte hotspot_count = roomFile->readByte();
 	uint32_t hotspot_data_start = pair10_data_offset + 0x47c;
-	Common::List<HotSpot> hotspots;
+	Common::Array<HotSpot> hotspots;
 	for (int i = 0; i < hotspot_count; i++) {
 		uint32_t obj_offset = hotspot_data_start + i * 9;
 		roomFile->seek(obj_offset, SEEK_SET);
@@ -715,13 +773,15 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 
 HotSpot *PelrockEngine::isHotspotUnder(int x, int y) {
 
-	for (Common::List<HotSpot>::iterator i = _currentRoomHotspots.begin(); i != _currentRoomHotspots.end(); i++) {
-		if (mouseX >= i->x && mouseX <= (i->x + i->w) &&
-			mouseY >= i->y && mouseY <= (i->y + i->h)) {
-			int lookable = i->extra & 0x0B;
-			int openable = i->extra & 0x0F;
+	for (size_t i = 0; i < _currentRoomHotspots.size(); i++) {
+		HotSpot hotspot = _currentRoomHotspots[i];
+		if (hotspot.isEnabled &&
+			mouseX >= hotspot.x && mouseX <= (hotspot.x + hotspot.w) &&
+			mouseY >= hotspot.y && mouseY <= (hotspot.y + hotspot.h)) {
+			int lookable = hotspot.extra & 0x0B;
+			int openable = hotspot.extra & 0x0F;
 			// debug("Hotspot at (%d,%d) size (%d,%d) extra %d, type = %d, lookable = %d, openable=%d", i->x, i->y, i->w, i->h, i->extra, i->type, lookable, openable);
-			return &(*i);
+			return &hotspot;
 		}
 	}
 	return nullptr;
@@ -809,7 +869,9 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 
 	HotSpot *hotspot = isHotspotUnder(mouseX, mouseY);
 	if (hotspot != nullptr) {
-		debug("Clicked Hotspot at (%d,%d) size (%d,%d) extra %d", hotspot->x, hotspot->y, hotspot->w, hotspot->h, hotspot->extra);
+
+		showDescription(hotspot->description.c_str(), xAlfred, yAlfred, 13);
+
 		changeCursor(HOTSPOT);
 	}
 }
@@ -927,6 +989,35 @@ Exit *PelrockEngine::isExitAtPoint(int x, int y) {
 	return nullptr;
 }
 
+void PelrockEngine::showDescription(Common::String text, int x, int y, byte color) {
+	Common::Rect rect = _largeFont->getBoundingBox(text.c_str());
+	if(x + rect.width() > 640) {
+		x = 640 - rect.width();
+	}
+	if(y + rect.height() > 400) {
+		y = 400 - rect.height();
+	}
+	x = 0;
+	y = 0;
+	if(_bgText != nullptr) {
+		putBackgroundSlice(x, y, 640, 400, _bgText);
+		delete[] _bgText;
+	}
+	int16 w = MIN(rect.width(), (int16)(640 - x));
+	int16 h = MIN(rect.height(), (int16)(400 - y));
+	debug("grabbing bg slice at (%d,%d) w=%d h=%d", x, y, w, h);
+
+	_bgText = grabBackgroundSlice(x, y, 640, 400);
+	_largeFont->drawString(_screen, text.c_str(), x - 1, y, 640, 0);     // Left
+    _largeFont->drawString(_screen, text.c_str(), x + 1, y, 640, 0);     // Right
+    _largeFont->drawString(_screen, text.c_str(), x, y - 1, 640, 0);     // Top
+    _largeFont->drawString(_screen, text.c_str(), x, y + 1, 640, 0);     // Bottom
+
+    // Draw main text on top
+    _largeFont->drawString(_screen, text.c_str(), x, y, 640, color);
+
+}
+
 void PelrockEngine::setScreen(int number, int dir) {
 
 	Common::File roomFile;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 45d751e42e1..e461ed006d2 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -65,9 +65,10 @@ private:
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
 	void loadMainCharacterAnims();
 	Common::List<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
-	Common::List<HotSpot> loadHotspots(Common::File *roomFile, int roomOffset);
+	Common::Array<HotSpot> loadHotspots(Common::File *roomFile, int roomOffset);
 	Common::List<Exit> loadExits(Common::File *roomFile, int roomOffset);
 	Common::List<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
+	Common::Array<Description> loadRoomDescriptions(Common::File *roomFile, int roomOffset);
 	void loadRoomMetadata(Common::File *roomFile, int roomOffset);
 	void loadCursors();
 	void loadInteractionIcons();
@@ -76,6 +77,8 @@ private:
 	Common::List<VerbIcons> populateActionsMenu(HotSpot hotspot);
 	Common::Point calculateWalkTarget(int mouseX, int mouseY);
 	Exit *isExitAtPoint(int x, int y);
+	void showDescription(Common::String text, int x, int y, byte color);
+
 	// render loop
 	void frames();
 	void checkMouseHover();
@@ -89,11 +92,12 @@ private:
 
 	ChronoManager *_chronoManager = nullptr;
 	byte *standingAnim = new byte[3060 * 102];
-	Common::List<HotSpot> _currentRoomHotspots;
+	Common::Array<HotSpot> _currentRoomHotspots;
 	Common::List<HoverArea> _hoverAreas;
 	Common::List<AnimSet> _currentRoomAnims;
 	Common::List<Exit> _currentRoomExits;
 	Common::List<WalkBox> _currentRoomWalkboxes;
+	Common::Array<Description> _currentRoomDescriptions;
 	int *_currentAnimFrames = nullptr;
 	int curAlfredFrame = 9;
 	uint16 mouseX = 0;
@@ -107,6 +111,7 @@ private:
 	byte *_verbIcons[9] = {nullptr};
 	byte *_popUpBalloon = nullptr;
 	byte *_bgPopupBalloon = nullptr;
+	byte *_bgText = nullptr;
 
 	bool _displayPopup = false;
 	int _popupX = 0;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 25ec19546a8..b909eb4ca86 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -112,13 +112,21 @@ struct HoverArea {
 };
 
 struct HotSpot {
-	byte type;
+	int id;
 	int x;
 	int y;
-	int id;
 	int w;
 	int h;
+	byte type;
 	int extra;
+	bool isEnabled = true;
+	Common::String description;
+};
+
+struct Description {
+	byte itemId;
+	byte index;
+	Common::String text;
 };
 
 struct WalkBox {


Commit: 7c6d67b89ee2b4a77057a572fa00d56cc991f894
    https://github.com/scummvm/scummvm/commit/7c6d67b89ee2b4a77057a572fa00d56cc991f894
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:02+02:00

Commit Message:
PELROCK: Fixes verb action selection

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 748071a4d46..aed4e05ac88 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -280,7 +280,7 @@ Common::List<AnimSet> PelrockEngine::loadRoomAnimations(Common::File *roomFile,
 		animSet.spriteType = animData[33];
 		animSet.actionFlags = animData[34];
 		animSet.isDisabled = animData[38];
-		if(animSet.numAnims == 0) {
+		if (animSet.numAnims == 0) {
 			break;
 		}
 		animSet.animData = new Anim[animSet.numAnims];
@@ -382,7 +382,7 @@ Common::Array<Description> PelrockEngine::loadRoomDescriptions(Common::File *roo
 	while (pos < (pair12_size)) {
 		// char *desc = new char[256];
 		int desc_pos = 0;
-		if(data[pos] == 0xFF) {
+		if (data[pos] == 0xFF) {
 			Description description;
 			description.itemId = data[++pos];
 			pos += 2;
@@ -391,7 +391,7 @@ Common::Array<Description> PelrockEngine::loadRoomDescriptions(Common::File *roo
 			// debug("Found description terminator");
 			while (pos < (pair12_size) && data[pos] != 0xFD && pos < (pair12_size)) {
 				// debug(" char: %c", data[pos]);
-				if(data[pos] != 0x00) {
+				if (data[pos] != 0x00) {
 					description.text.append(1, (char)data[pos]);
 				}
 				// desc[desc_pos++] = (char)data[pos];
@@ -430,7 +430,6 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 		thisHotspot.extra = i->extra;
 		thisHotspot.type = i->actionFlags;
 		thisHotspot.isEnabled = !i->isDisabled;
-		thisHotspot.description = descriptions[count].text;
 		hotspots.push_back(thisHotspot);
 		count++;
 	}
@@ -442,7 +441,6 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	debug("total descriptions = %d, anims = %d, hotspots = %d", descriptions.size(), anims.size(), staticHotspots.size());
 	for (int i = 0; i < staticHotspots.size(); i++) {
 		HotSpot hotspot = staticHotspots[i];
-		hotspot.description =  descriptions[anims.size() + i].text;
 		hotspots.push_back(hotspot);
 	}
 
@@ -462,10 +460,9 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	_currentRoomWalkboxes = walkboxes;
 	_currentRoomDescriptions = descriptions;
 
-
-	for(int i=0; i < _currentRoomHotspots.size(); i++) {
+	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
 		HotSpot hotspot = _currentRoomHotspots[i];
-		debug("Hotspot %d: x=%d y=%d w=%d h=%d type=%d enabled? %d desc=%s", i, hotspot.x, hotspot.y, hotspot.w, hotspot.h, hotspot.type, hotspot.isEnabled, hotspot.description.c_str());
+		debug("Hotspot %d: x=%d y=%d w=%d h=%d type=%d enabled? %d desc=%s", i, hotspot.x, hotspot.y, hotspot.w, hotspot.h, hotspot.type, hotspot.isEnabled, _currentRoomDescriptions[i].text.c_str());
 		_screen->drawLine(hotspot.x, hotspot.y, hotspot.x + hotspot.w, hotspot.y, 200 + i);
 		_screen->drawLine(hotspot.x, hotspot.y + hotspot.h, hotspot.x + hotspot.w, hotspot.y + hotspot.h, 200 + i);
 		_screen->drawLine(hotspot.x, hotspot.y, hotspot.x, hotspot.y + hotspot.h, 200 + i);
@@ -619,35 +616,36 @@ void PelrockEngine::putBackgroundSlice(int x, int y, int w, int h, byte *slice)
 		}
 	}
 }
-Common::List<VerbIcons> PelrockEngine::populateActionsMenu(HotSpot hotspot) {
+Common::List<VerbIcons> PelrockEngine::populateActionsMenu(HotSpot *hotspot) {
 	Common::List<VerbIcons> verbs;
+	debug("Populating actions menu for hotspot type %d", hotspot->type);
 	verbs.push_back(LOOK);
 
-	if (hotspot.type & 1) {
+	if (hotspot->type & 1) {
 		debug("Hotspot allows OPEN action");
 		verbs.push_back(OPEN);
 	}
-	if (hotspot.type & 2) {
+	if (hotspot->type & 2) {
 		debug("Hotspot allows CLOSE action");
 		verbs.push_back(CLOSE);
 	}
-	if (hotspot.type & 4) {
+	if (hotspot->type & 4) {
 		debug("Hotspot allows UNKNOWN action");
 		verbs.push_back(UNKNOWN);
 	}
-	if (hotspot.type & 8) {
+	if (hotspot->type & 8) {
 		debug("Hotspot allows PICKUP action");
 		verbs.push_back(PICKUP);
 	}
-	if (hotspot.type & 16) {
+	if (hotspot->type & 16) {
 		debug("Hotspot allows TALK action");
 		verbs.push_back(TALK);
 	}
-	if (hotspot.type & 32) {
+	if (hotspot->type & 32) {
 		debug("Hotspot allows WALK action");
 		verbs.push_back(PUSH);
 	}
-	if (hotspot.type & 128) {
+	if (hotspot->type & 128) {
 		debug("Hotspot allows PULL action");
 		verbs.push_back(PULL);
 	}
@@ -745,8 +743,8 @@ void PelrockEngine::frames() {
 }
 
 void PelrockEngine::checkLongMouseClick(int x, int y) {
-	HotSpot *hotspot = isHotspotUnder(mouseX, mouseY);
-	if (hotspot != nullptr) {
+	int hotspotIndex = isHotspotUnder(mouseX, mouseY);
+	if (hotspotIndex != -1) {
 		// _popupX = hotspot->x;
 		// _popupY = hotspot->y;
 		if (_bgPopupBalloon != nullptr) {
@@ -766,25 +764,23 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 		}
 		_displayPopup = true;
 		_currentPopupFrame = 0;
-		_currentHotspot = hotspot;
+		_currentHotspot = &_currentRoomHotspots[hotspotIndex];
+		debug("Current hotspot type: %d", _currentHotspot->type);
 		_bgPopupBalloon = grabBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight);
 	}
 }
 
-HotSpot *PelrockEngine::isHotspotUnder(int x, int y) {
+int PelrockEngine::isHotspotUnder(int x, int y) {
 
 	for (size_t i = 0; i < _currentRoomHotspots.size(); i++) {
 		HotSpot hotspot = _currentRoomHotspots[i];
 		if (hotspot.isEnabled &&
 			mouseX >= hotspot.x && mouseX <= (hotspot.x + hotspot.w) &&
 			mouseY >= hotspot.y && mouseY <= (hotspot.y + hotspot.h)) {
-			int lookable = hotspot.extra & 0x0B;
-			int openable = hotspot.extra & 0x0F;
-			// debug("Hotspot at (%d,%d) size (%d,%d) extra %d, type = %d, lookable = %d, openable=%d", i->x, i->y, i->w, i->h, i->extra, i->type, lookable, openable);
-			return &hotspot;
+			return i;
 		}
 	}
-	return nullptr;
+	return -1;
 }
 
 Exit *PelrockEngine::isExitUnder(int x, int y) {
@@ -818,11 +814,11 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 		}
 	}
 
-	Common::List<VerbIcons> availableActions = populateActionsMenu(*_currentHotspot);
+	Common::List<VerbIcons> availableActions = populateActionsMenu(_currentHotspot);
 
-	for (Common::List<VerbIcons>::iterator i = availableActions.begin(); i != availableActions.end(); i++) {
-		debug("Verb icon to show: %d", *i);
-	}
+	// for (Common::List<VerbIcons>::iterator i = availableActions.begin(); i != availableActions.end(); i++) {
+	// 	debug("Verb icon to show: %d", *i);
+	// }
 
 	for (uint32_t y = 0; y < kVerbIconHeight; y++) {
 		for (uint32_t x = 0; x < kVerbIconWidth; x++) {
@@ -851,6 +847,7 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 void PelrockEngine::checkMouseClick(int x, int y) {
 
 	_displayPopup = false;
+	_currentHotspot = nullptr;
 	if (_bgPopupBalloon != nullptr) {
 		putBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight, _bgPopupBalloon);
 		delete[] _bgPopupBalloon;
@@ -867,10 +864,9 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 		setScreen(exit->targetRoom, exit->dir);
 	}
 
-	HotSpot *hotspot = isHotspotUnder(mouseX, mouseY);
-	if (hotspot != nullptr) {
-
-		showDescription(hotspot->description.c_str(), xAlfred, yAlfred, 13);
+	int hotspotIndex = isHotspotUnder(mouseX, mouseY);
+	if (hotspotIndex != -1) {
+		showDescription(_currentRoomDescriptions[hotspotIndex].text.c_str(), xAlfred, yAlfred, 13);
 
 		changeCursor(HOTSPOT);
 	}
@@ -893,8 +889,8 @@ void PelrockEngine::checkMouseHover() {
 		exitDetected = true;
 	}
 
-	HotSpot *hotspot = isHotspotUnder(mouseX, mouseY);
-	if (hotspot != nullptr) {
+	int hotspotIndex = isHotspotUnder(mouseX, mouseY);
+	if (hotspotIndex != -1) {
 		isSomethingUnder = true;
 	}
 	// Exit *exit = isExitUnder(mouseX, mouseY);
@@ -991,15 +987,15 @@ Exit *PelrockEngine::isExitAtPoint(int x, int y) {
 
 void PelrockEngine::showDescription(Common::String text, int x, int y, byte color) {
 	Common::Rect rect = _largeFont->getBoundingBox(text.c_str());
-	if(x + rect.width() > 640) {
+	if (x + rect.width() > 640) {
 		x = 640 - rect.width();
 	}
-	if(y + rect.height() > 400) {
+	if (y + rect.height() > 400) {
 		y = 400 - rect.height();
 	}
 	x = 0;
 	y = 0;
-	if(_bgText != nullptr) {
+	if (_bgText != nullptr) {
 		putBackgroundSlice(x, y, 640, 400, _bgText);
 		delete[] _bgText;
 	}
@@ -1008,14 +1004,13 @@ void PelrockEngine::showDescription(Common::String text, int x, int y, byte colo
 	debug("grabbing bg slice at (%d,%d) w=%d h=%d", x, y, w, h);
 
 	_bgText = grabBackgroundSlice(x, y, 640, 400);
-	_largeFont->drawString(_screen, text.c_str(), x - 1, y, 640, 0);     // Left
-    _largeFont->drawString(_screen, text.c_str(), x + 1, y, 640, 0);     // Right
-    _largeFont->drawString(_screen, text.c_str(), x, y - 1, 640, 0);     // Top
-    _largeFont->drawString(_screen, text.c_str(), x, y + 1, 640, 0);     // Bottom
-
-    // Draw main text on top
-    _largeFont->drawString(_screen, text.c_str(), x, y, 640, color);
+	_largeFont->drawString(_screen, text.c_str(), x - 1, y, 640, 0); // Left
+	_largeFont->drawString(_screen, text.c_str(), x + 1, y, 640, 0); // Right
+	_largeFont->drawString(_screen, text.c_str(), x, y - 1, 640, 0); // Top
+	_largeFont->drawString(_screen, text.c_str(), x, y + 1, 640, 0); // Bottom
 
+	// Draw main text on top
+	_largeFont->drawString(_screen, text.c_str(), x, y, 640, color);
 }
 
 void PelrockEngine::setScreen(int number, int dir) {
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index e461ed006d2..2886cb59c4f 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -74,7 +74,7 @@ private:
 	void loadInteractionIcons();
 	byte *grabBackgroundSlice(int x, int y, int w, int h);
 	void putBackgroundSlice(int x, int y, int w, int h, byte *slice);
-	Common::List<VerbIcons> populateActionsMenu(HotSpot hotspot);
+	Common::List<VerbIcons> populateActionsMenu(HotSpot *hotspot);
 	Common::Point calculateWalkTarget(int mouseX, int mouseY);
 	Exit *isExitAtPoint(int x, int y);
 	void showDescription(Common::String text, int x, int y, byte color);
@@ -85,7 +85,7 @@ private:
 	void checkMouseClick(int x, int y);
 	void checkLongMouseClick(int x, int y);
 	void changeCursor(Cursor cursor);
-	HotSpot *isHotspotUnder(int x, int y);
+	int isHotspotUnder(int x, int y);
 	Exit *isExitUnder(int x, int y);
 	AnimSet *isSpriteUnder(int x, int y);
 	void showActionBalloon(int posx, int posy, int curFrame);
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index b909eb4ca86..3e6b3b780db 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -120,7 +120,6 @@ struct HotSpot {
 	byte type;
 	int extra;
 	bool isEnabled = true;
-	Common::String description;
 };
 
 struct Description {


Commit: a75cf60c5ca6015a8367989adffeb6644b3eb5c1
    https://github.com/scummvm/scummvm/commit/a75cf60c5ca6015a8367989adffeb6644b3eb5c1
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:03+02:00

Commit Message:
PELROCK: Load conversations

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index aed4e05ac88..4dca90ae336 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -243,7 +243,7 @@ void PelrockEngine::getBackground(Common::File *roomFile, int roomOffset, byte *
 
 Common::List<AnimSet> PelrockEngine::loadRoomAnimations(Common::File *roomFile, int roomOffset) {
 	uint32_t pair_offset = roomOffset + (8 * 8);
-	debug("Sprite pair offset position: %d", pair_offset);
+	// debug("Sprite pair offset position: %d", pair_offset);
 	roomFile->seek(pair_offset, SEEK_SET);
 	uint32_t offset = roomFile->readUint32LE();
 	uint32_t size = roomFile->readUint32LE();
@@ -284,8 +284,7 @@ Common::List<AnimSet> PelrockEngine::loadRoomAnimations(Common::File *roomFile,
 			break;
 		}
 		animSet.animData = new Anim[animSet.numAnims];
-		debug("AnimSet %d has %d sub-anims, type %d, actionFlags %d, isDisabled? %d", i, animSet.numAnims, animSet.spriteType, animSet.actionFlags, animSet.isDisabled);
-		// roomFile->skip(1);
+		// debug("AnimSet %d has %d sub-anims, type %d, actionFlags %d, isDisabled? %d", i, animSet.numAnims, animSet.spriteType, animSet.actionFlags, animSet.isDisabled);
 		int subAnimOffset = 10;
 		for (int j = 0; j < animSet.numAnims; j++) {
 
@@ -305,7 +304,7 @@ Common::List<AnimSet> PelrockEngine::loadRoomAnimations(Common::File *roomFile,
 				anim.animData = new byte[needed];
 				Common::copy(pic + picOffset, pic + picOffset + needed, anim.animData);
 				animSet.animData[j] = anim;
-				debug("  Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d loopCount=%d speed=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes, anim.loopCount, anim.speed);
+				// debug("  Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d loopCount=%d speed=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes, anim.loopCount, anim.speed);
 				picOffset += needed;
 			} else {
 				continue;
@@ -367,7 +366,7 @@ Common::List<WalkBox> PelrockEngine::loadWalkboxes(Common::File *roomFile, int r
 	return walkboxes;
 }
 
-Common::Array<Description> PelrockEngine::loadRoomDescriptions(Common::File *roomFile, int roomOffset) {
+Common::Array<Description> PelrockEngine::loadRoomDescriptions(Common::File *roomFile, int roomOffset, uint32_t &outPos) {
 	uint32_t pair12_offset_pos = roomOffset + (12 * 8);
 	roomFile->seek(pair12_offset_pos, SEEK_SET);
 	// roomFile->skip(4);
@@ -379,6 +378,7 @@ Common::Array<Description> PelrockEngine::loadRoomDescriptions(Common::File *roo
 	roomFile->read(data, pair12_size);
 	Common::Array<Description> descriptions;
 	uint32_t pos = 0;
+	uint32_t lastDescPos = 0;
 	while (pos < (pair12_size)) {
 		// char *desc = new char[256];
 		int desc_pos = 0;
@@ -401,10 +401,12 @@ Common::Array<Description> PelrockEngine::loadRoomDescriptions(Common::File *roo
 			debug("Found description for item %d index %d, text: %s", description.itemId, description.index, description.text.c_str());
 
 			descriptions.push_back(description);
+			lastDescPos = pos;
 		}
 		pos++;
 	}
-
+	debug("End of descriptions at position %d", pos);
+	outPos = lastDescPos+1;
 	delete[] data;
 	// for (Common::List<Common::String>::iterator i = descriptions.begin(); i != descriptions.end(); i++) {
 	// 	debug("Room description: %s", i->c_str());
@@ -412,9 +414,199 @@ Common::Array<Description> PelrockEngine::loadRoomDescriptions(Common::File *roo
 	return descriptions;
 }
 
+bool isControlByte(byte b) {
+	//  0xFD: 'END_LINE',
+    // 0xFC: 'TEXT_TERM',
+    // 0xFB: 'CHOICE',
+    // 0xFA: 'SKIP',
+    // 0xF9: 'PAGE_BREAK',
+    // 0xF8: 'ACTION',
+    // 0xF7: 'END_BRANCH',
+    // 0xF6: 'LINE_CONT',
+    // 0xF5: 'END_BRANCH_2',
+    // 0xF4: 'END_CONV',
+    // 0xF1: 'CHOICE_ALT',
+    // 0xF0: 'GO_BACK',
+    // 0xFE: 'END_BRANCH_3',
+    // 0xEB: 'END_ALT',
+    // 0xFF: 'DESC_START',
+    // 0x08: 'SPEAKER',
+	if (b == 0xFD || b == 0xFC || b == 0xFB || b == 0xFA || b == 0xF9 || b == 0xF8 ||
+		b == 0xF7 || b == 0xF6 || b == 0xF5 || b == 0xF4 || b == 0xF1 ||
+		b == 0xF0 || b == 0xFE || b == 0xEB || b == 0xFF || b == 0x08) {
+		return true;
+	}
+	return false;
+}
+
+bool isTerminalByte(byte b) {
+	return (b == 0xFD || b == 0xF4 || b == 0xEB || b == 0xF7 || b == 0xF5 || b == 0xFE || b == 0xFC);
+}
+
+
+/**
+ * def decode_byte(b):
+    """Decode a byte to character"""
+    special = {
+        0x80: 'ñ', 0x81: 'í', 0x82: '¡', 0x83: '¿', 0x84: 'ú',
+        0x7B: 'á', 0x7C: 'é', 0x7D: 'í', 0x7E: 'ó', 0x7F: 'ú',
+    }
+
+    if b in special:
+        return special[b]
+    elif 0x20 <= b <= 0x7A:
+        return chr(b)
+    else:
+        return f'[{b:02X}]'
+ */
+char32_t decodeByte(byte b) {
+	if ( b == 0x80) {
+		return 'n';
+	} else if (b == 0x81) {
+		return 'i';
+	} else if (b == 0x82) {
+		return 'X';
+	} else if (b == 0x83) {
+		return 'X';
+	} else if (b == 0x84) {
+		return 'u';
+	} else if (b == 0x7B) {
+		return 'a';
+	} else if (b == 0x7C) {
+		return 'e';
+	} else if (b == 0x7D) {
+		return 'i';
+	} else if (b == 0x7E) {
+		return 'o';
+	} else if (b == 0x7F) {
+		return 'u';
+	} else if (b >= 0x20 && b <= 0x7A) {
+		return (char)b;
+	} else {
+		// return string in format [XX]
+		return '.';
+	}
+
+}
+
+Common::String PelrockEngine::getControlName(byte b) {
+    switch (b) {
+        case 0xFD: return "END_LINE";
+        case 0xFC: return "TEXT_TERM";
+        case 0xFB: return "CHOICE";
+        case 0xFA: return "SKIP";
+        case 0xF9: return "PAGE_BREAK";
+        case 0xF8: return "ACTION";
+        case 0xF7: return "END_BRANCH";
+        case 0xF6: return "LINE_CONT";
+        case 0xF5: return "END_BRANCH_2";
+        case 0xF4: return "END_CONV";
+        case 0xF1: return "CHOICE_ALT";
+        case 0xF0: return "GO_BACK";
+        case 0xFE: return "END_BRANCH_3";
+        case 0xEB: return "END_ALT";
+        case 0xFF: return "DESC_START";
+        case 0x08: return "SPEAKER";
+        default: return Common::String::format("UNKNOWN(0x%02X)", b);
+    }
+}
+
+Common::Array<ConversationLine> PelrockEngine::loadConversations(Common::File *roomFile, int roomOffset, uint32_t startPos) {
+    Common::Array<ConversationLine> conversations;
+
+    debug("Loading conversations starting at position %d", startPos);
+
+    uint32_t pair12_offset_pos = roomOffset + (12 * 8);
+    roomFile->seek(pair12_offset_pos, SEEK_SET);
+    uint32_t pair12_data_offset = roomFile->readUint32LE();
+    uint32_t pair12_size = roomFile->readUint32LE();
+
+	startPos += 2;
+    uint32_t conversation_start = pair12_data_offset + startPos;
+    uint32_t conversation_size = pair12_size - startPos;
+
+    roomFile->seek(conversation_start, SEEK_SET);
+    byte *data = new byte[conversation_size];
+    roomFile->read(data, conversation_size);
+
+    uint32_t i = 0;
+    int lineNum = 1;
+
+    while (i < conversation_size) {
+        uint32_t lineStart = i;
+        ConversationLine line;
+        line.offset = startPos + lineStart;
+        line.speaker = 0;
+
+        // Read until we hit an end marker
+        while (i < conversation_size) {
+            byte b = data[i];
+
+            if (b == 0x08) { // SPEAKER
+                i++;
+                if (i < conversation_size) {
+                    line.speaker = data[i];
+                    line.controlBytes.push_back(Common::String::format("SPEAKER(0x%02X)", line.speaker));
+                    i++;
+                }
+                continue;
+            } else if (isControlByte(b)) {
+                line.controlBytes.push_back(Common::String::format("%s(0x%02X)",
+                                                                    getControlName(b).c_str(), b));
+                i++;
+
+                // Check for end markers
+                if (isTerminalByte(b)) {
+                    break;
+                }
+            } else {
+                // Regular text character
+                if (b != 0x00) {
+                    line.text += decodeByte(b);
+                }
+                i++;
+            }
+        }
+
+        // Store raw bytes for this line
+        for (uint32_t j = lineStart; j < i && j < conversation_size; j++) {
+            line.rawBytes.push_back(data[j]);
+        }
+
+        // Only add line if it has content
+        if (!line.text.empty() || line.controlBytes.size() > 0) {
+            // debug("Line %d (offset %d): %s", lineNum, line.offset, line.text.c_str());
+            if (line.controlBytes.size() > 0) {
+                Common::String controls;
+                for (uint k = 0; k < line.controlBytes.size(); k++) {
+                    if (k > 0) controls += ", ";
+                    controls += line.controlBytes[k];
+                }
+                // debug("  Controls: %s", controls.c_str());
+            }
+            conversations.push_back(line);
+            lineNum++;
+        }
+    }
+
+    delete[] data;
+    debug("Loaded %d conversation lines", conversations.size());
+    return conversations;
+}
+
 void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
+	uint32_t outPos = 0;
+
+	Common::Array<Description> descriptions = loadRoomDescriptions(roomFile, roomOffset, outPos);
+	debug("After decsriptions, position is %d", outPos);
+	Common::Array<ConversationLine> conversations = loadConversations(roomFile, roomOffset, outPos);
+	for(int i = 0; i < conversations.size(); i++) {
+		if( conversations[i].text.empty()) {
+			continue;
+		}
+		debug("Conversation %d: %s", i, conversations[i].text.c_str());
+	}
 
-	Common::Array<Description> descriptions = loadRoomDescriptions(roomFile, roomOffset);
 
 	Common::List<AnimSet> anims = loadRoomAnimations(roomFile, roomOffset);
 
@@ -438,6 +630,7 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	Common::List<Exit> exits = loadExits(roomFile, roomOffset);
 	Common::List<WalkBox> walkboxes = loadWalkboxes(roomFile, roomOffset);
 
+
 	debug("total descriptions = %d, anims = %d, hotspots = %d", descriptions.size(), anims.size(), staticHotspots.size());
 	for (int i = 0; i < staticHotspots.size(); i++) {
 		HotSpot hotspot = staticHotspots[i];
@@ -462,7 +655,7 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 
 	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
 		HotSpot hotspot = _currentRoomHotspots[i];
-		debug("Hotspot %d: x=%d y=%d w=%d h=%d type=%d enabled? %d desc=%s", i, hotspot.x, hotspot.y, hotspot.w, hotspot.h, hotspot.type, hotspot.isEnabled, _currentRoomDescriptions[i].text.c_str());
+		// debug("Hotspot %d: x=%d y=%d w=%d h=%d type=%d enabled? %d desc=%s", i, hotspot.x, hotspot.y, hotspot.w, hotspot.h, hotspot.type, hotspot.isEnabled, _currentRoomDescriptions[i].text.c_str());
 		_screen->drawLine(hotspot.x, hotspot.y, hotspot.x + hotspot.w, hotspot.y, 200 + i);
 		_screen->drawLine(hotspot.x, hotspot.y + hotspot.h, hotspot.x + hotspot.w, hotspot.y + hotspot.h, 200 + i);
 		_screen->drawLine(hotspot.x, hotspot.y, hotspot.x, hotspot.y + hotspot.h, 200 + i);
@@ -567,7 +760,7 @@ Common::Array<HotSpot> PelrockEngine::loadHotspots(Common::File *roomFile, int r
 		spot.w = obj_bytes[5];
 		spot.h = obj_bytes[6];
 		spot.extra = obj_bytes[7] | (obj_bytes[8] << 8);
-		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, spot.type, spot.x, spot.y, spot.w, spot.h, spot.extra);
+		// debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, spot.type, spot.x, spot.y, spot.w, spot.h, spot.extra);
 		hotspots.push_back(spot);
 	}
 	return hotspots;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 2886cb59c4f..332090cf5f0 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -68,7 +68,9 @@ private:
 	Common::Array<HotSpot> loadHotspots(Common::File *roomFile, int roomOffset);
 	Common::List<Exit> loadExits(Common::File *roomFile, int roomOffset);
 	Common::List<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
-	Common::Array<Description> loadRoomDescriptions(Common::File *roomFile, int roomOffset);
+	Common::Array<Description> loadRoomDescriptions(Common::File *roomFile, int roomOffset, uint32_t &outPos);
+	Common::Array<ConversationLine> loadConversations(Common::File *roomFile, int roomOffset, uint32_t startPos);
+	Common::String getControlName(byte b);
 	void loadRoomMetadata(Common::File *roomFile, int roomOffset);
 	void loadCursors();
 	void loadInteractionIcons();
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 3e6b3b780db..9e648bef778 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -21,6 +21,10 @@
 #ifndef PELROCK_TYPES_H
 #define PELROCK_TYPES_H
 
+#include "common/types.h"
+#include "common/scummsys.h"
+#include "common/system.h"
+
 namespace Pelrock {
 
 enum Cursor {
@@ -122,6 +126,16 @@ struct HotSpot {
 	bool isEnabled = true;
 };
 
+struct ConversationLine {
+    Common::String text;
+    Common::Array<Common::String> controlBytes;
+    byte speaker;
+    uint32 offset;
+    Common::Array<byte> rawBytes;
+
+    ConversationLine() : speaker(0), offset(0) {}
+};
+
 struct Description {
 	byte itemId;
 	byte index;
@@ -147,20 +161,22 @@ enum GameState {
 	PROMOTE = 107,
 };
 
-// struct rectCam
-// {
-//     Common::List<int> vecinos;
-//     bool marked;
-//     int index;
-//     int x;
-//     int y;
-//     int w;
-//     int h;
-// };
-
-// struct defCam
-// {
-//     rectCam cams[];
+// enum ConversationMarkers : byte {
+//     END_LINE(0xFD),
+//     TEXT_TERM(0xFC),
+// 	CHOICE(0xFB),
+// 	SKIP(0xFA),
+// 	PAGE_BREAK(0xF9),
+// 	ACTION(0xF8),
+// 	END_BRANCH(0xF7),
+// 	LINE_CONT(0xF6),
+// 	END_BRANCH_2(0xF5),
+// 	END_CONV(0xF4),
+// 	GO_BACK(0xF0),
+// 	END_BRANCH_3(0xFE),
+// 	END_ALT(0xEB),
+// 	DESC_START(0xFF),
+// 	SPEAKER(0x08)
 // };
 
 } // End of namespace Pelrock


Commit: 1647b7d852a993cc3b12a35174192c16d5bd0ea2
    https://github.com/scummvm/scummvm/commit/1647b7d852a993cc3b12a35174192c16d5bd0ea2
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:03+02:00

Commit Message:
PELROCK: Reads conversations correctly into a tree

Changed paths:
    engines/pelrock/fonts/large_font.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/fonts/large_font.cpp b/engines/pelrock/fonts/large_font.cpp
index 0e41a4dd812..9f94f855921 100644
--- a/engines/pelrock/fonts/large_font.cpp
+++ b/engines/pelrock/fonts/large_font.cpp
@@ -52,11 +52,10 @@ int LargeFont::getCharWidth(uint32 chr) const {
 
 void LargeFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
 	chr -= 32; // Adjust for font starting at ASCII 32
-	if (!_fontData || chr > 255 || chr < 0) {
+	if (!_fontData || chr > 96 || chr < 0) {
 		return;
 	}
 	int charOffset = chr * 0x30;
-
 	for (int i = 0; i < 24; i++) {
 		byte rowByte1 = _fontData[charOffset + i * 2];
 		byte rowByte2 = _fontData[charOffset + i * 2 + 1];
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 4dca90ae336..28c8aa3bc8c 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -414,35 +414,6 @@ Common::Array<Description> PelrockEngine::loadRoomDescriptions(Common::File *roo
 	return descriptions;
 }
 
-bool isControlByte(byte b) {
-	//  0xFD: 'END_LINE',
-    // 0xFC: 'TEXT_TERM',
-    // 0xFB: 'CHOICE',
-    // 0xFA: 'SKIP',
-    // 0xF9: 'PAGE_BREAK',
-    // 0xF8: 'ACTION',
-    // 0xF7: 'END_BRANCH',
-    // 0xF6: 'LINE_CONT',
-    // 0xF5: 'END_BRANCH_2',
-    // 0xF4: 'END_CONV',
-    // 0xF1: 'CHOICE_ALT',
-    // 0xF0: 'GO_BACK',
-    // 0xFE: 'END_BRANCH_3',
-    // 0xEB: 'END_ALT',
-    // 0xFF: 'DESC_START',
-    // 0x08: 'SPEAKER',
-	if (b == 0xFD || b == 0xFC || b == 0xFB || b == 0xFA || b == 0xF9 || b == 0xF8 ||
-		b == 0xF7 || b == 0xF6 || b == 0xF5 || b == 0xF4 || b == 0xF1 ||
-		b == 0xF0 || b == 0xFE || b == 0xEB || b == 0xFF || b == 0x08) {
-		return true;
-	}
-	return false;
-}
-
-bool isTerminalByte(byte b) {
-	return (b == 0xFD || b == 0xF4 || b == 0xEB || b == 0xF7 || b == 0xF5 || b == 0xFE || b == 0xFC);
-}
-
 
 /**
  * def decode_byte(b):
@@ -461,25 +432,25 @@ bool isTerminalByte(byte b) {
  */
 char32_t decodeByte(byte b) {
 	if ( b == 0x80) {
-		return 'n';
+		return '\xA4';
 	} else if (b == 0x81) {
-		return 'i';
+		return '\xA1';
 	} else if (b == 0x82) {
-		return 'X';
+		return '\xAD';
 	} else if (b == 0x83) {
-		return 'X';
+		return '\xA8';
 	} else if (b == 0x84) {
-		return 'u';
+		return '\xA3';
 	} else if (b == 0x7B) {
-		return 'a';
+		return '\xA0';
 	} else if (b == 0x7C) {
-		return 'e';
+		return '\x82';
 	} else if (b == 0x7D) {
-		return 'i';
+		return '\xA1';
 	} else if (b == 0x7E) {
-		return 'o';
+		return '\xA2';
 	} else if (b == 0x7F) {
-		return 'u';
+		return '\xA3';
 	} else if (b >= 0x20 && b <= 0x7A) {
 		return (char)b;
 	} else {
@@ -489,8 +460,23 @@ char32_t decodeByte(byte b) {
 
 }
 
+void PelrockEngine::talk() {
+	if(_currentRoomConversations.size() == 0)
+		return;
+	int x = _currentRoomHotspots[0].x;
+	int y = _currentRoomHotspots[0].y;
+	debug("Say %s", _currentRoomConversations[0].text.c_str());
+	showDescription(_currentRoomConversations[0].text, x, y, _currentRoomConversations[0].speakerId);
+	for(int i = 0; i < _currentRoomConversations[0].choices.size(); i++) {
+		int idx = _currentRoomConversations.size() - 1 - i;
+		_smallFont->drawString(_screen, _currentRoomConversations[0].choices[idx].text.c_str(), 0, 400 - ((i + 1) * 12), 640, 14);
+	}
+
+
+}
+
 Common::String PelrockEngine::getControlName(byte b) {
-    switch (b) {
+	switch (b) {
         case 0xFD: return "END_LINE";
         case 0xFC: return "TEXT_TERM";
         case 0xFB: return "CHOICE";
@@ -511,87 +497,378 @@ Common::String PelrockEngine::getControlName(byte b) {
     }
 }
 
-Common::Array<ConversationLine> PelrockEngine::loadConversations(Common::File *roomFile, int roomOffset, uint32_t startPos) {
-    Common::Array<ConversationLine> conversations;
+Common::String PelrockEngine::cleanText(const Common::String &text) {
+    Common::String cleaned = text;
 
-    debug("Loading conversations starting at position %d", startPos);
+    // Trim leading/trailing whitespace
+    while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
+        cleaned.deleteChar(0);
+    }
+    while (!cleaned.empty() && Common::isSpace(cleaned.lastChar())) {
+        cleaned.deleteLastChar();
+    }
 
-    uint32_t pair12_offset_pos = roomOffset + (12 * 8);
-    roomFile->seek(pair12_offset_pos, SEEK_SET);
-    uint32_t pair12_data_offset = roomFile->readUint32LE();
-    uint32_t pair12_size = roomFile->readUint32LE();
+    // Remove leading [XX][00] patterns
+    while (!cleaned.empty() && cleaned.contains('[')) {
+        uint idx = 0;
+        for (uint i = 0; i < cleaned.size() && i < 15; i++) {
+            if (cleaned[i] == '[') {
+                idx = i;
+                break;
+            }
+        }
 
-	startPos += 2;
-    uint32_t conversation_start = pair12_data_offset + startPos;
-    uint32_t conversation_size = pair12_size - startPos;
+        if (idx < 10) {
+            int endIdx = -1;
+            for (uint i = idx; i < cleaned.size() && i < idx + 10; i++) {
+                if (cleaned[i] == ']') {
+                    endIdx = i;
+                    break;
+                }
+            }
 
-    roomFile->seek(conversation_start, SEEK_SET);
-    byte *data = new byte[conversation_size];
-    roomFile->read(data, conversation_size);
+            if (endIdx > (int)idx && endIdx < (int)idx + 10) {
+                cleaned = cleaned.c_str() + endIdx + 1;
+                // Trim leading whitespace again
+                while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
+                    cleaned.deleteChar(0);
+                }
+            } else {
+                break;
+            }
+        } else {
+            break;
+        }
+    }
 
-    uint32_t i = 0;
-    int lineNum = 1;
-
-    while (i < conversation_size) {
-        uint32_t lineStart = i;
-        ConversationLine line;
-        line.offset = startPos + lineStart;
-        line.speaker = 0;
-
-        // Read until we hit an end marker
-        while (i < conversation_size) {
-            byte b = data[i];
-
-            if (b == 0x08) { // SPEAKER
-                i++;
-                if (i < conversation_size) {
-                    line.speaker = data[i];
-                    line.controlBytes.push_back(Common::String::format("SPEAKER(0x%02X)", line.speaker));
-                    i++;
+    // Remove single leading control characters
+    if (cleaned.size() > 1) {
+        byte first = (byte)cleaned[0];
+        byte second = (byte)cleaned[1];
+
+        if ((first == 'A' || first == 'H') &&
+            (Common::isUpper(second) || second == 0x83 || second == 0x82 || second == '[')) {
+            cleaned.deleteChar(0);
+            while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
+                cleaned.deleteChar(0);
+            }
+        } else if (strchr("#%')!+,.-\"*&$(/", first)) {
+            cleaned.deleteChar(0);
+            while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
+                cleaned.deleteChar(0);
+            }
+        }
+    }
+
+    return cleaned;
+}
+
+Common::Array<ConversationElement> PelrockEngine::parseConversationElements(const byte *convData, uint32 size) {
+    Common::Array<ConversationElement> elements;
+    Common::HashMap<int, int> choiceIndices; // Track choice index occurrences
+    uint32 pos = 0;
+
+    // First pass: parse elements and track choice indices
+    while (pos < size) {
+        byte b = convData[pos];
+
+        if (b == 0x08) { // SPEAKER
+            pos++;
+            if (pos < size) {
+                byte speakerId = convData[pos];
+                Common::String speaker = (speakerId == 0x0D) ? "ALFRED" : "NPC";
+                pos++;
+
+                // Read text
+                Common::String text;
+                while (pos < size && convData[pos] != 0x08 && convData[pos] != 0xFB &&
+                       convData[pos] != 0xF1 && convData[pos] != 0xF8 && convData[pos] != 0xFD &&
+                       convData[pos] != 0xFC && convData[pos] != 0xF4 && convData[pos] != 0xF7 &&
+                       convData[pos] != 0xF5 && convData[pos] != 0xFE && convData[pos] != 0xEB &&
+                       convData[pos] != 0xF0) {
+                    char32_t ch = decodeByte(convData[pos]);
+                    if (ch != '.') {
+                        text += ch;
+                    }
+                    pos++;
                 }
-                continue;
-            } else if (isControlByte(b)) {
-                line.controlBytes.push_back(Common::String::format("%s(0x%02X)",
-                                                                    getControlName(b).c_str(), b));
-                i++;
-
-                // Check for end markers
-                if (isTerminalByte(b)) {
-                    break;
+
+                text = cleanText(text);
+                if (!text.empty()) {
+                    ConversationElement elem;
+                    elem.type = ConversationElement::DIALOGUE;
+					elem.speakerId = speakerId;
+                    elem.speaker = speaker;
+                    elem.text = text;
+                    elem.choiceIndex = -1;
+                    elements.push_back(elem);
                 }
-            } else {
-                // Regular text character
-                if (b != 0x00) {
-                    line.text += decodeByte(b);
+            }
+        } else if (b == 0xFB || b == 0xF1) { // CHOICE marker
+            pos++;
+            int choiceIndex = -1;
+            if (pos < size) {
+                choiceIndex = convData[pos];
+                // Track this choice index
+                if (choiceIndices.contains(choiceIndex)) {
+                    choiceIndices[choiceIndex]++;
+                } else {
+                    choiceIndices[choiceIndex] = 1;
+                }
+                pos++;
+            }
+
+            // Skip next 2 bytes (speaker marker)
+            if (pos < size) pos++;
+            if (pos < size) pos++;
+
+            // Read text
+            Common::String text;
+            while (pos < size && convData[pos] != 0x08 && convData[pos] != 0xFB &&
+                   convData[pos] != 0xF1 && convData[pos] != 0xF8 && convData[pos] != 0xFD &&
+                   convData[pos] != 0xFC && convData[pos] != 0xF4 && convData[pos] != 0xF7 &&
+                   convData[pos] != 0xF5 && convData[pos] != 0xFE && convData[pos] != 0xEB &&
+                   convData[pos] != 0xF0) {
+                char32_t ch = decodeByte(convData[pos]);
+                if (ch != '.') {
+                    text += ch;
                 }
-                i++;
+                pos++;
+            }
+
+            text = cleanText(text);
+            if (!text.empty()) {
+                ConversationElement elem;
+                elem.type = ConversationElement::CHOICE_MARKER;
+                elem.text = text;
+                elem.choiceIndex = choiceIndex;
+                elements.push_back(elem);
             }
+        } else if (b == 0xF8) { // ACTION
+            pos += 3;
+        } else if (b == 0xF4) { // END_CONV
+            ConversationElement elem;
+            elem.type = ConversationElement::END_CONV;
+            elements.push_back(elem);
+            pos++;
+        } else if (b == 0xF7) { // END_BRANCH
+            ConversationElement elem;
+            elem.type = ConversationElement::END_BRANCH;
+            elements.push_back(elem);
+            pos++;
+        } else if (b == 0xFD || b == 0xFC || b == 0xF5 || b == 0xFE || b == 0xEB || b == 0xF0) {
+            pos++;
+        } else {
+            pos++;
         }
+    }
 
-        // Store raw bytes for this line
-        for (uint32_t j = lineStart; j < i && j < conversation_size; j++) {
-            line.rawBytes.push_back(data[j]);
+    // Second pass: mark which indices are actual choices (appear multiple times)
+    for (uint i = 0; i < elements.size(); i++) {
+        if (elements[i].choiceIndex >= 0) {
+            elements[i].isRealChoice = (choiceIndices[elements[i].choiceIndex] > 1);
         }
+    }
+
+    return elements;
+}
+
+Common::Array<ConversationNode> PelrockEngine::buildTreeStructure(const Common::Array<ConversationElement> &elements) {
+    Common::Array<ConversationNode> roots;
+    Common::Array<StackEntry> stack;
+    ConversationNode *currentRoot = nullptr;
+    uint i = 0;
+
+    while (i < elements.size()) {
+        const ConversationElement &elem = elements[i];
+
+        if (elem.type == ConversationElement::DIALOGUE && elem.speaker == "NPC") {
+            if (stack.empty()) {
+                // New root conversation
+                ConversationNode root;
+                root.type = ConversationNode::ROOT;
+                root.text = elem.text;
+                roots.push_back(root);
+                currentRoot = &roots[roots.size() - 1];
+            } else {
+                // NPC response within a branch
+                ConversationNode *parent = stack[stack.size() - 1].node;
+                ConversationNode response;
+                response.type = ConversationNode::RESPONSE;
+                response.speaker = "NPC";
+				response.speakerId = elem.speakerId;
+                response.text = elem.text;
+                parent->responses.push_back(response);
+            }
+            i++;
+
+        } else if (elem.type == ConversationElement::CHOICE_MARKER) {
+            if (elem.isRealChoice) {
+                // Real choice - player selects from menu
+                ConversationNode choiceNode;
+                choiceNode.type = ConversationNode::CHOICE;
+                choiceNode.text = elem.text;
+                choiceNode.choiceIndex = elem.choiceIndex;
+
+                // Find where to attach this choice
+                while (!stack.empty() && stack[stack.size() - 1].index >= elem.choiceIndex) {
+                    stack.pop_back();
+                }
 
-        // Only add line if it has content
-        if (!line.text.empty() || line.controlBytes.size() > 0) {
-            // debug("Line %d (offset %d): %s", lineNum, line.offset, line.text.c_str());
-            if (line.controlBytes.size() > 0) {
-                Common::String controls;
-                for (uint k = 0; k < line.controlBytes.size(); k++) {
-                    if (k > 0) controls += ", ";
-                    controls += line.controlBytes[k];
+                if (!stack.empty()) {
+                    ConversationNode *parent = stack[stack.size() - 1].node;
+                    parent->subchoices.push_back(choiceNode);
+
+                    // Get pointer to the newly added choice
+                    ConversationNode *newChoice = &parent->subchoices[parent->subchoices.size() - 1];
+
+                    StackEntry entry;
+                    entry.node = newChoice;
+                    entry.index = elem.choiceIndex;
+                    stack.push_back(entry);
+                } else {
+                    if (currentRoot) {
+                        currentRoot->choices.push_back(choiceNode);
+
+                        // Get pointer to the newly added choice
+                        ConversationNode *newChoice = &currentRoot->choices[currentRoot->choices.size() - 1];
+
+                        StackEntry entry;
+                        entry.node = newChoice;
+                        entry.index = elem.choiceIndex;
+                        stack.push_back(entry);
+                    }
                 }
-                // debug("  Controls: %s", controls.c_str());
+            } else {
+                // Auto-dialogue - ALFRED just speaks
+                if (!stack.empty()) {
+                    ConversationNode *parent = stack[stack.size() - 1].node;
+                    ConversationNode response;
+                    response.type = ConversationNode::RESPONSE;
+                    response.speaker = "ALFRED";
+					response.speakerId = 0x0D;
+                    response.text = elem.text;
+                    parent->responses.push_back(response);
+                }
+            }
+            i++;
+
+        } else if (elem.type == ConversationElement::DIALOGUE && elem.speaker == "ALFRED") {
+            if (!stack.empty()) {
+                ConversationNode *parent = stack[stack.size() - 1].node;
+                ConversationNode response;
+                response.type = ConversationNode::RESPONSE;
+                response.speaker = "ALFRED";
+                response.text = elem.text;
+					response.speakerId = 0x0D;
+                parent->responses.push_back(response);
             }
-            conversations.push_back(line);
-            lineNum++;
+            i++;
+
+        } else if (elem.type == ConversationElement::END_CONV) {
+            if (!stack.empty()) {
+                stack[stack.size() - 1].node->terminated = true;
+                stack.pop_back();
+            }
+            i++;
+
+        } else if (elem.type == ConversationElement::END_BRANCH) {
+            stack.clear();
+            currentRoot = nullptr;
+            i++;
+
+        } else {
+            i++;
         }
     }
 
-    delete[] data;
-    debug("Loaded %d conversation lines", conversations.size());
-    return conversations;
+    return roots;
+}
+
+Common::Array<ConversationNode> PelrockEngine::loadConversations(Common::File *roomFile, int roomOffset, uint32_t startPos) {
+
+    debug("Loading conversations starting at position %d", startPos);
+
+    uint32_t pair12_offset_pos = roomOffset + (12 * 8);
+    roomFile->seek(pair12_offset_pos, SEEK_SET);
+    uint32_t pair12_data_offset = roomFile->readUint32LE();
+    uint32_t pair12_size = roomFile->readUint32LE();
+
+	// startPos += 2;
+    uint32_t conversation_start = pair12_data_offset + startPos;
+    uint32_t conversation_size = pair12_size - startPos;
+
+    roomFile->seek(conversation_start, SEEK_SET);
+    byte *data = new byte[conversation_size];
+    roomFile->read(data, conversation_size);
+
+	Common::Array<ConversationElement> elements = parseConversationElements(data, conversation_size);
+	Common::Array<ConversationNode> roots = buildTreeStructure(elements);
+	return roots;
+
+    // uint32_t i = 0;
+    // int lineNum = 1;
+
+    // while (i < conversation_size) {
+    //     uint32_t lineStart = i;
+    //     ConversationLine line;
+    //     line.offset = startPos + lineStart;
+    //     line.speaker = 0;
+
+    //     // Read until we hit an end marker
+    //     while (i < conversation_size) {
+    //         byte b = data[i];
+
+    //         if (b == 0x08) { // SPEAKER
+    //             i++;
+    //             if (i < conversation_size) {
+    //                 line.speaker = data[i];
+    //                 line.controlBytes.push_back(Common::String::format("SPEAKER(0x%02X)", line.speaker));
+    //                 i++;
+    //             }
+    //             continue;
+    //         } else if (isControlByte(b)) {
+    //             line.controlBytes.push_back(Common::String::format("%s(0x%02X)",
+    //                                                                 getControlName(b).c_str(), b));
+    //             i++;
+
+    //             // Check for end markers
+    //             if (isTerminalByte(b)) {
+    //                 break;
+    //             }
+    //         } else {
+    //             // Regular text character
+    //             if (b != 0x00) {
+    //                 line.text += decodeByte(b);
+    //             }
+    //             i++;
+    //         }
+    //     }
+
+    //     // Store raw bytes for this line
+    //     for (uint32_t j = lineStart; j < i && j < conversation_size; j++) {
+    //         line.rawBytes.push_back(data[j]);
+    //     }
+
+    //     // Only add line if it has content
+    //     if (!line.text.empty() || line.controlBytes.size() > 0) {
+    //         // debug("Line %d (offset %d): %s", lineNum, line.offset, line.text.c_str());
+    //         if (line.controlBytes.size() > 0) {
+    //             Common::String controls;
+    //             for (uint k = 0; k < line.controlBytes.size(); k++) {
+    //                 if (k > 0) controls += ", ";
+    //                 controls += line.controlBytes[k];
+    //             }
+    //             // debug("  Controls: %s", controls.c_str());
+    //         }
+    //         conversations.push_back(line);
+    //         lineNum++;
+    //     }
+    // }
+
+    // delete[] data;
+    // debug("Loaded %d conversation lines", conversations.size());
+    // return conversations;
 }
 
 void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
@@ -599,14 +876,14 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 
 	Common::Array<Description> descriptions = loadRoomDescriptions(roomFile, roomOffset, outPos);
 	debug("After decsriptions, position is %d", outPos);
-	Common::Array<ConversationLine> conversations = loadConversations(roomFile, roomOffset, outPos);
-	for(int i = 0; i < conversations.size(); i++) {
-		if( conversations[i].text.empty()) {
+	Common::Array<ConversationNode> roots = loadConversations(roomFile, roomOffset, outPos);
+	for(int i = 0; i < roots.size(); i++) {
+		if( roots[i].text.empty()) {
 			continue;
 		}
-		debug("Conversation %d: %s", i, conversations[i].text.c_str());
+		debug("Conversation %d: %s", i, roots[i].text.c_str());
 	}
-
+	_currentRoomConversations = roots;
 
 	Common::List<AnimSet> anims = loadRoomAnimations(roomFile, roomOffset);
 
@@ -1059,9 +1336,9 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 
 	int hotspotIndex = isHotspotUnder(mouseX, mouseY);
 	if (hotspotIndex != -1) {
-		showDescription(_currentRoomDescriptions[hotspotIndex].text.c_str(), xAlfred, yAlfred, 13);
-
-		changeCursor(HOTSPOT);
+		talk();
+		// showDescription(_currentRoomDescriptions[hotspotIndex].text.c_str(), xAlfred, yAlfred, 13);
+		// changeCursor(HOTSPOT);
 	}
 }
 
@@ -1180,14 +1457,21 @@ Exit *PelrockEngine::isExitAtPoint(int x, int y) {
 
 void PelrockEngine::showDescription(Common::String text, int x, int y, byte color) {
 	Common::Rect rect = _largeFont->getBoundingBox(text.c_str());
-	if (x + rect.width() > 640) {
-		x = 640 - rect.width();
+	if (x + 2 + rect.width() > 640) {
+		x = 640 - rect.width() - 2;
 	}
-	if (y + rect.height() > 400) {
+	if (y + 2 + rect.height() > 400) {
 		y = 400 - rect.height();
 	}
-	x = 0;
-	y = 0;
+	if(x - 2 < 0) {
+		x = 2;
+	}
+	if(y - 2 < 0) {
+		y = 2;
+	}
+
+	x = 2;
+	y = 2;
 	if (_bgText != nullptr) {
 		putBackgroundSlice(x, y, 640, 400, _bgText);
 		delete[] _bgText;
@@ -1198,9 +1482,13 @@ void PelrockEngine::showDescription(Common::String text, int x, int y, byte colo
 
 	_bgText = grabBackgroundSlice(x, y, 640, 400);
 	_largeFont->drawString(_screen, text.c_str(), x - 1, y, 640, 0); // Left
+	_largeFont->drawString(_screen, text.c_str(), x - 2, y, 640, 0); // Left
 	_largeFont->drawString(_screen, text.c_str(), x + 1, y, 640, 0); // Right
+	_largeFont->drawString(_screen, text.c_str(), x + 2, y, 640, 0); // Right
 	_largeFont->drawString(_screen, text.c_str(), x, y - 1, 640, 0); // Top
+	_largeFont->drawString(_screen, text.c_str(), x, y - 2, 640, 0); // Top
 	_largeFont->drawString(_screen, text.c_str(), x, y + 1, 640, 0); // Bottom
+	_largeFont->drawString(_screen, text.c_str(), x, y + 2, 640, 0); // Bottom
 
 	// Draw main text on top
 	_largeFont->drawString(_screen, text.c_str(), x, y, 640, color);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 332090cf5f0..09c0a7b3587 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -69,7 +69,13 @@ private:
 	Common::List<Exit> loadExits(Common::File *roomFile, int roomOffset);
 	Common::List<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
 	Common::Array<Description> loadRoomDescriptions(Common::File *roomFile, int roomOffset, uint32_t &outPos);
-	Common::Array<ConversationLine> loadConversations(Common::File *roomFile, int roomOffset, uint32_t startPos);
+
+	Common::String cleanText(const Common::String &text);
+    Common::Array<ConversationElement> parseConversationElements(const byte *convData, uint32 size);
+	Common::Array<ConversationNode> buildTreeStructure(const Common::Array<ConversationElement> &elements);
+	Common::Array<ConversationNode> loadConversations(Common::File *roomFile, int roomOffset, uint32_t startPos);
+
+	void talk();
 	Common::String getControlName(byte b);
 	void loadRoomMetadata(Common::File *roomFile, int roomOffset);
 	void loadCursors();
@@ -100,6 +106,8 @@ private:
 	Common::List<Exit> _currentRoomExits;
 	Common::List<WalkBox> _currentRoomWalkboxes;
 	Common::Array<Description> _currentRoomDescriptions;
+	Common::Array<ConversationNode> _currentRoomConversations;
+
 	int *_currentAnimFrames = nullptr;
 	int curAlfredFrame = 9;
 	uint16 mouseX = 0;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 9e648bef778..35514e2a7c5 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -126,14 +126,47 @@ struct HotSpot {
 	bool isEnabled = true;
 };
 
-struct ConversationLine {
+struct ConversationElement {
+    enum Type {
+        DIALOGUE,
+        CHOICE_MARKER,
+        END_CONV,
+        END_BRANCH
+    } type;
+
+    Common::String speaker;
+	byte speakerId;
     Common::String text;
-    Common::Array<Common::String> controlBytes;
-    byte speaker;
-    uint32 offset;
-    Common::Array<byte> rawBytes;
+    int choiceIndex;
+    bool isRealChoice;
 
-    ConversationLine() : speaker(0), offset(0) {}
+    ConversationElement() : type(DIALOGUE), choiceIndex(-1), isRealChoice(false) {}
+};
+
+
+struct ConversationNode {
+    enum NodeType {
+        ROOT,
+        CHOICE,
+        RESPONSE
+    } type;
+
+    Common::String text;
+    Common::String speaker;
+	byte speakerId;
+    int choiceIndex;
+    bool terminated;
+
+    Common::Array<ConversationNode> choices;
+    Common::Array<ConversationNode> responses;
+    Common::Array<ConversationNode> subchoices;
+
+    ConversationNode() : type(ROOT), choiceIndex(-1), terminated(false) {}
+};
+
+struct StackEntry {
+    ConversationNode *node;
+    int index;
 };
 
 struct Description {


Commit: ae3266b3e1fc5666c020883a6458ae6ea7a6e9e8
    https://github.com/scummvm/scummvm/commit/ae3266b3e1fc5666c020883a6458ae6ea7a6e9e8
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:03+02:00

Commit Message:
PELROCK: Split alfred frames

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 28c8aa3bc8c..c8014a6d8f2 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -154,7 +154,7 @@ void PelrockEngine::playIntro() {
 }
 
 void PelrockEngine::loadAnims() {
-	loadMainCharacterAnims();
+	loadAlfredAnims();
 }
 
 const int EXPECTED_SIZE = 640 * 400;
@@ -406,7 +406,7 @@ Common::Array<Description> PelrockEngine::loadRoomDescriptions(Common::File *roo
 		pos++;
 	}
 	debug("End of descriptions at position %d", pos);
-	outPos = lastDescPos+1;
+	outPos = lastDescPos + 1;
 	delete[] data;
 	// for (Common::List<Common::String>::iterator i = descriptions.begin(); i != descriptions.end(); i++) {
 	// 	debug("Room description: %s", i->c_str());
@@ -414,24 +414,23 @@ Common::Array<Description> PelrockEngine::loadRoomDescriptions(Common::File *roo
 	return descriptions;
 }
 
-
 /**
  * def decode_byte(b):
-    """Decode a byte to character"""
-    special = {
-        0x80: 'ñ', 0x81: 'í', 0x82: '¡', 0x83: '¿', 0x84: 'ú',
-        0x7B: 'á', 0x7C: 'é', 0x7D: 'í', 0x7E: 'ó', 0x7F: 'ú',
-    }
-
-    if b in special:
-        return special[b]
-    elif 0x20 <= b <= 0x7A:
-        return chr(b)
-    else:
-        return f'[{b:02X}]'
+	"""Decode a byte to character"""
+	special = {
+		0x80: 'ñ', 0x81: 'í', 0x82: '¡', 0x83: '¿', 0x84: 'ú',
+		0x7B: 'á', 0x7C: 'é', 0x7D: 'í', 0x7E: 'ó', 0x7F: 'ú',
+	}
+
+	if b in special:
+		return special[b]
+	elif 0x20 <= b <= 0x7A:
+		return chr(b)
+	else:
+		return f'[{b:02X}]'
  */
 char32_t decodeByte(byte b) {
-	if ( b == 0x80) {
+	if (b == 0x80) {
 		return '\xA4';
 	} else if (b == 0x81) {
 		return '\xA1';
@@ -457,418 +456,434 @@ char32_t decodeByte(byte b) {
 		// return string in format [XX]
 		return '.';
 	}
-
 }
 
 void PelrockEngine::talk() {
-	if(_currentRoomConversations.size() == 0)
+	if (_currentRoomConversations.size() == 0)
 		return;
 	int x = _currentRoomHotspots[0].x;
 	int y = _currentRoomHotspots[0].y;
 	debug("Say %s", _currentRoomConversations[0].text.c_str());
-	showDescription(_currentRoomConversations[0].text, x, y, _currentRoomConversations[0].speakerId);
-	for(int i = 0; i < _currentRoomConversations[0].choices.size(); i++) {
-		int idx = _currentRoomConversations.size() - 1 - i;
-		_smallFont->drawString(_screen, _currentRoomConversations[0].choices[idx].text.c_str(), 0, 400 - ((i + 1) * 12), 640, 14);
-	}
-
-
+	// showDescription(_currentRoomConversations[0].text, x, y, _currentRoomConversations[0].speakerId);
+	// for(int i = 0; i < _currentRoomConversations[0].choices.size(); i++) {
+	// 	int idx = _currentRoomConversations.size() - 1 - i;
+	// 	_smallFont->drawString(_screen, _currentRoomConversations[0].choices[idx].text.c_str(), 0, 400 - ((i + 1) * 12), 640, 14);
+	// }
 }
 
 Common::String PelrockEngine::getControlName(byte b) {
 	switch (b) {
-        case 0xFD: return "END_LINE";
-        case 0xFC: return "TEXT_TERM";
-        case 0xFB: return "CHOICE";
-        case 0xFA: return "SKIP";
-        case 0xF9: return "PAGE_BREAK";
-        case 0xF8: return "ACTION";
-        case 0xF7: return "END_BRANCH";
-        case 0xF6: return "LINE_CONT";
-        case 0xF5: return "END_BRANCH_2";
-        case 0xF4: return "END_CONV";
-        case 0xF1: return "CHOICE_ALT";
-        case 0xF0: return "GO_BACK";
-        case 0xFE: return "END_BRANCH_3";
-        case 0xEB: return "END_ALT";
-        case 0xFF: return "DESC_START";
-        case 0x08: return "SPEAKER";
-        default: return Common::String::format("UNKNOWN(0x%02X)", b);
-    }
+	case 0xFD:
+		return "END_LINE";
+	case 0xFC:
+		return "TEXT_TERM";
+	case 0xFB:
+		return "CHOICE";
+	case 0xFA:
+		return "SKIP";
+	case 0xF9:
+		return "PAGE_BREAK";
+	case 0xF8:
+		return "ACTION";
+	case 0xF7:
+		return "END_BRANCH";
+	case 0xF6:
+		return "LINE_CONT";
+	case 0xF5:
+		return "END_BRANCH_2";
+	case 0xF4:
+		return "END_CONV";
+	case 0xF1:
+		return "CHOICE_ALT";
+	case 0xF0:
+		return "GO_BACK";
+	case 0xFE:
+		return "END_BRANCH_3";
+	case 0xEB:
+		return "END_ALT";
+	case 0xFF:
+		return "DESC_START";
+	case 0x08:
+		return "SPEAKER";
+	default:
+		return Common::String::format("UNKNOWN(0x%02X)", b);
+	}
 }
 
 Common::String PelrockEngine::cleanText(const Common::String &text) {
-    Common::String cleaned = text;
-
-    // Trim leading/trailing whitespace
-    while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
-        cleaned.deleteChar(0);
-    }
-    while (!cleaned.empty() && Common::isSpace(cleaned.lastChar())) {
-        cleaned.deleteLastChar();
-    }
-
-    // Remove leading [XX][00] patterns
-    while (!cleaned.empty() && cleaned.contains('[')) {
-        uint idx = 0;
-        for (uint i = 0; i < cleaned.size() && i < 15; i++) {
-            if (cleaned[i] == '[') {
-                idx = i;
-                break;
-            }
-        }
-
-        if (idx < 10) {
-            int endIdx = -1;
-            for (uint i = idx; i < cleaned.size() && i < idx + 10; i++) {
-                if (cleaned[i] == ']') {
-                    endIdx = i;
-                    break;
-                }
-            }
-
-            if (endIdx > (int)idx && endIdx < (int)idx + 10) {
-                cleaned = cleaned.c_str() + endIdx + 1;
-                // Trim leading whitespace again
-                while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
-                    cleaned.deleteChar(0);
-                }
-            } else {
-                break;
-            }
-        } else {
-            break;
-        }
-    }
-
-    // Remove single leading control characters
-    if (cleaned.size() > 1) {
-        byte first = (byte)cleaned[0];
-        byte second = (byte)cleaned[1];
-
-        if ((first == 'A' || first == 'H') &&
-            (Common::isUpper(second) || second == 0x83 || second == 0x82 || second == '[')) {
-            cleaned.deleteChar(0);
-            while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
-                cleaned.deleteChar(0);
-            }
-        } else if (strchr("#%')!+,.-\"*&$(/", first)) {
-            cleaned.deleteChar(0);
-            while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
-                cleaned.deleteChar(0);
-            }
-        }
-    }
-
-    return cleaned;
+	Common::String cleaned = text;
+
+	// Trim leading/trailing whitespace
+	while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
+		cleaned.deleteChar(0);
+	}
+	while (!cleaned.empty() && Common::isSpace(cleaned.lastChar())) {
+		cleaned.deleteLastChar();
+	}
+
+	// Remove leading [XX][00] patterns
+	while (!cleaned.empty() && cleaned.contains('[')) {
+		uint idx = 0;
+		for (uint i = 0; i < cleaned.size() && i < 15; i++) {
+			if (cleaned[i] == '[') {
+				idx = i;
+				break;
+			}
+		}
+
+		if (idx < 10) {
+			int endIdx = -1;
+			for (uint i = idx; i < cleaned.size() && i < idx + 10; i++) {
+				if (cleaned[i] == ']') {
+					endIdx = i;
+					break;
+				}
+			}
+
+			if (endIdx > (int)idx && endIdx < (int)idx + 10) {
+				cleaned = cleaned.c_str() + endIdx + 1;
+				// Trim leading whitespace again
+				while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
+					cleaned.deleteChar(0);
+				}
+			} else {
+				break;
+			}
+		} else {
+			break;
+		}
+	}
+
+	// Remove single leading control characters
+	if (cleaned.size() > 1) {
+		byte first = (byte)cleaned[0];
+		byte second = (byte)cleaned[1];
+
+		if ((first == 'A' || first == 'H') &&
+			(Common::isUpper(second) || second == 0x83 || second == 0x82 || second == '[')) {
+			cleaned.deleteChar(0);
+			while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
+				cleaned.deleteChar(0);
+			}
+		} else if (strchr("#%')!+,.-\"*&$(/", first)) {
+			cleaned.deleteChar(0);
+			while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
+				cleaned.deleteChar(0);
+			}
+		}
+	}
+
+	return cleaned;
 }
 
 Common::Array<ConversationElement> PelrockEngine::parseConversationElements(const byte *convData, uint32 size) {
-    Common::Array<ConversationElement> elements;
-    Common::HashMap<int, int> choiceIndices; // Track choice index occurrences
-    uint32 pos = 0;
-
-    // First pass: parse elements and track choice indices
-    while (pos < size) {
-        byte b = convData[pos];
-
-        if (b == 0x08) { // SPEAKER
-            pos++;
-            if (pos < size) {
-                byte speakerId = convData[pos];
-                Common::String speaker = (speakerId == 0x0D) ? "ALFRED" : "NPC";
-                pos++;
-
-                // Read text
-                Common::String text;
-                while (pos < size && convData[pos] != 0x08 && convData[pos] != 0xFB &&
-                       convData[pos] != 0xF1 && convData[pos] != 0xF8 && convData[pos] != 0xFD &&
-                       convData[pos] != 0xFC && convData[pos] != 0xF4 && convData[pos] != 0xF7 &&
-                       convData[pos] != 0xF5 && convData[pos] != 0xFE && convData[pos] != 0xEB &&
-                       convData[pos] != 0xF0) {
-                    char32_t ch = decodeByte(convData[pos]);
-                    if (ch != '.') {
-                        text += ch;
-                    }
-                    pos++;
-                }
-
-                text = cleanText(text);
-                if (!text.empty()) {
-                    ConversationElement elem;
-                    elem.type = ConversationElement::DIALOGUE;
+	Common::Array<ConversationElement> elements;
+	Common::HashMap<int, int> choiceIndices; // Track choice index occurrences
+	uint32 pos = 0;
+
+	// First pass: parse elements and track choice indices
+	while (pos < size) {
+		byte b = convData[pos];
+
+		if (b == 0x08) { // SPEAKER
+			pos++;
+			if (pos < size) {
+				byte speakerId = convData[pos];
+				Common::String speaker = (speakerId == 0x0D) ? "ALFRED" : "NPC";
+				pos++;
+
+				// Read text
+				Common::String text;
+				while (pos < size && convData[pos] != 0x08 && convData[pos] != 0xFB &&
+					   convData[pos] != 0xF1 && convData[pos] != 0xF8 && convData[pos] != 0xFD &&
+					   convData[pos] != 0xFC && convData[pos] != 0xF4 && convData[pos] != 0xF7 &&
+					   convData[pos] != 0xF5 && convData[pos] != 0xFE && convData[pos] != 0xEB &&
+					   convData[pos] != 0xF0) {
+					char32_t ch = decodeByte(convData[pos]);
+					if (ch != '.') {
+						text += ch;
+					}
+					pos++;
+				}
+
+				text = cleanText(text);
+				if (!text.empty()) {
+					ConversationElement elem;
+					elem.type = ConversationElement::DIALOGUE;
 					elem.speakerId = speakerId;
-                    elem.speaker = speaker;
-                    elem.text = text;
-                    elem.choiceIndex = -1;
-                    elements.push_back(elem);
-                }
-            }
-        } else if (b == 0xFB || b == 0xF1) { // CHOICE marker
-            pos++;
-            int choiceIndex = -1;
-            if (pos < size) {
-                choiceIndex = convData[pos];
-                // Track this choice index
-                if (choiceIndices.contains(choiceIndex)) {
-                    choiceIndices[choiceIndex]++;
-                } else {
-                    choiceIndices[choiceIndex] = 1;
-                }
-                pos++;
-            }
-
-            // Skip next 2 bytes (speaker marker)
-            if (pos < size) pos++;
-            if (pos < size) pos++;
-
-            // Read text
-            Common::String text;
-            while (pos < size && convData[pos] != 0x08 && convData[pos] != 0xFB &&
-                   convData[pos] != 0xF1 && convData[pos] != 0xF8 && convData[pos] != 0xFD &&
-                   convData[pos] != 0xFC && convData[pos] != 0xF4 && convData[pos] != 0xF7 &&
-                   convData[pos] != 0xF5 && convData[pos] != 0xFE && convData[pos] != 0xEB &&
-                   convData[pos] != 0xF0) {
-                char32_t ch = decodeByte(convData[pos]);
-                if (ch != '.') {
-                    text += ch;
-                }
-                pos++;
-            }
-
-            text = cleanText(text);
-            if (!text.empty()) {
-                ConversationElement elem;
-                elem.type = ConversationElement::CHOICE_MARKER;
-                elem.text = text;
-                elem.choiceIndex = choiceIndex;
-                elements.push_back(elem);
-            }
-        } else if (b == 0xF8) { // ACTION
-            pos += 3;
-        } else if (b == 0xF4) { // END_CONV
-            ConversationElement elem;
-            elem.type = ConversationElement::END_CONV;
-            elements.push_back(elem);
-            pos++;
-        } else if (b == 0xF7) { // END_BRANCH
-            ConversationElement elem;
-            elem.type = ConversationElement::END_BRANCH;
-            elements.push_back(elem);
-            pos++;
-        } else if (b == 0xFD || b == 0xFC || b == 0xF5 || b == 0xFE || b == 0xEB || b == 0xF0) {
-            pos++;
-        } else {
-            pos++;
-        }
-    }
-
-    // Second pass: mark which indices are actual choices (appear multiple times)
-    for (uint i = 0; i < elements.size(); i++) {
-        if (elements[i].choiceIndex >= 0) {
-            elements[i].isRealChoice = (choiceIndices[elements[i].choiceIndex] > 1);
-        }
-    }
-
-    return elements;
+					elem.speaker = speaker;
+					elem.text = text;
+					elem.choiceIndex = -1;
+					elements.push_back(elem);
+				}
+			}
+		} else if (b == 0xFB || b == 0xF1) { // CHOICE marker
+			pos++;
+			int choiceIndex = -1;
+			if (pos < size) {
+				choiceIndex = convData[pos];
+				// Track this choice index
+				if (choiceIndices.contains(choiceIndex)) {
+					choiceIndices[choiceIndex]++;
+				} else {
+					choiceIndices[choiceIndex] = 1;
+				}
+				pos++;
+			}
+
+			// Skip next 2 bytes (speaker marker)
+			if (pos < size)
+				pos++;
+			if (pos < size)
+				pos++;
+
+			// Read text
+			Common::String text;
+			while (pos < size && convData[pos] != 0x08 && convData[pos] != 0xFB &&
+				   convData[pos] != 0xF1 && convData[pos] != 0xF8 && convData[pos] != 0xFD &&
+				   convData[pos] != 0xFC && convData[pos] != 0xF4 && convData[pos] != 0xF7 &&
+				   convData[pos] != 0xF5 && convData[pos] != 0xFE && convData[pos] != 0xEB &&
+				   convData[pos] != 0xF0) {
+				char32_t ch = decodeByte(convData[pos]);
+				if (ch != '.') {
+					text += ch;
+				}
+				pos++;
+			}
+
+			text = cleanText(text);
+			if (!text.empty()) {
+				ConversationElement elem;
+				elem.type = ConversationElement::CHOICE_MARKER;
+				elem.text = text;
+				elem.choiceIndex = choiceIndex;
+				elements.push_back(elem);
+			}
+		} else if (b == 0xF8) { // ACTION
+			pos += 3;
+		} else if (b == 0xF4) { // END_CONV
+			ConversationElement elem;
+			elem.type = ConversationElement::END_CONV;
+			elements.push_back(elem);
+			pos++;
+		} else if (b == 0xF7) { // END_BRANCH
+			ConversationElement elem;
+			elem.type = ConversationElement::END_BRANCH;
+			elements.push_back(elem);
+			pos++;
+		} else if (b == 0xFD || b == 0xFC || b == 0xF5 || b == 0xFE || b == 0xEB || b == 0xF0) {
+			pos++;
+		} else {
+			pos++;
+		}
+	}
+
+	// Second pass: mark which indices are actual choices (appear multiple times)
+	for (uint i = 0; i < elements.size(); i++) {
+		if (elements[i].choiceIndex >= 0) {
+			elements[i].isRealChoice = (choiceIndices[elements[i].choiceIndex] > 1);
+		}
+	}
+
+	return elements;
 }
 
 Common::Array<ConversationNode> PelrockEngine::buildTreeStructure(const Common::Array<ConversationElement> &elements) {
-    Common::Array<ConversationNode> roots;
-    Common::Array<StackEntry> stack;
-    ConversationNode *currentRoot = nullptr;
-    uint i = 0;
-
-    while (i < elements.size()) {
-        const ConversationElement &elem = elements[i];
-
-        if (elem.type == ConversationElement::DIALOGUE && elem.speaker == "NPC") {
-            if (stack.empty()) {
-                // New root conversation
-                ConversationNode root;
-                root.type = ConversationNode::ROOT;
-                root.text = elem.text;
-                roots.push_back(root);
-                currentRoot = &roots[roots.size() - 1];
-            } else {
-                // NPC response within a branch
-                ConversationNode *parent = stack[stack.size() - 1].node;
-                ConversationNode response;
-                response.type = ConversationNode::RESPONSE;
-                response.speaker = "NPC";
+	Common::Array<ConversationNode> roots;
+	Common::Array<StackEntry> stack;
+	ConversationNode *currentRoot = nullptr;
+	uint i = 0;
+
+	while (i < elements.size()) {
+		const ConversationElement &elem = elements[i];
+
+		if (elem.type == ConversationElement::DIALOGUE && elem.speaker == "NPC") {
+			if (stack.empty()) {
+				// New root conversation
+				ConversationNode root;
+				root.type = ConversationNode::ROOT;
+				root.text = elem.text;
+				roots.push_back(root);
+				currentRoot = &roots[roots.size() - 1];
+			} else {
+				// NPC response within a branch
+				ConversationNode *parent = stack[stack.size() - 1].node;
+				ConversationNode response;
+				response.type = ConversationNode::RESPONSE;
+				response.speaker = "NPC";
 				response.speakerId = elem.speakerId;
-                response.text = elem.text;
-                parent->responses.push_back(response);
-            }
-            i++;
-
-        } else if (elem.type == ConversationElement::CHOICE_MARKER) {
-            if (elem.isRealChoice) {
-                // Real choice - player selects from menu
-                ConversationNode choiceNode;
-                choiceNode.type = ConversationNode::CHOICE;
-                choiceNode.text = elem.text;
-                choiceNode.choiceIndex = elem.choiceIndex;
-
-                // Find where to attach this choice
-                while (!stack.empty() && stack[stack.size() - 1].index >= elem.choiceIndex) {
-                    stack.pop_back();
-                }
-
-                if (!stack.empty()) {
-                    ConversationNode *parent = stack[stack.size() - 1].node;
-                    parent->subchoices.push_back(choiceNode);
-
-                    // Get pointer to the newly added choice
-                    ConversationNode *newChoice = &parent->subchoices[parent->subchoices.size() - 1];
-
-                    StackEntry entry;
-                    entry.node = newChoice;
-                    entry.index = elem.choiceIndex;
-                    stack.push_back(entry);
-                } else {
-                    if (currentRoot) {
-                        currentRoot->choices.push_back(choiceNode);
-
-                        // Get pointer to the newly added choice
-                        ConversationNode *newChoice = &currentRoot->choices[currentRoot->choices.size() - 1];
-
-                        StackEntry entry;
-                        entry.node = newChoice;
-                        entry.index = elem.choiceIndex;
-                        stack.push_back(entry);
-                    }
-                }
-            } else {
-                // Auto-dialogue - ALFRED just speaks
-                if (!stack.empty()) {
-                    ConversationNode *parent = stack[stack.size() - 1].node;
-                    ConversationNode response;
-                    response.type = ConversationNode::RESPONSE;
-                    response.speaker = "ALFRED";
-					response.speakerId = 0x0D;
-                    response.text = elem.text;
-                    parent->responses.push_back(response);
-                }
-            }
-            i++;
-
-        } else if (elem.type == ConversationElement::DIALOGUE && elem.speaker == "ALFRED") {
-            if (!stack.empty()) {
-                ConversationNode *parent = stack[stack.size() - 1].node;
-                ConversationNode response;
-                response.type = ConversationNode::RESPONSE;
-                response.speaker = "ALFRED";
-                response.text = elem.text;
+				response.text = elem.text;
+				parent->responses.push_back(response);
+			}
+			i++;
+
+		} else if (elem.type == ConversationElement::CHOICE_MARKER) {
+			if (elem.isRealChoice) {
+				// Real choice - player selects from menu
+				ConversationNode choiceNode;
+				choiceNode.type = ConversationNode::CHOICE;
+				choiceNode.text = elem.text;
+				choiceNode.choiceIndex = elem.choiceIndex;
+
+				// Find where to attach this choice
+				while (!stack.empty() && stack[stack.size() - 1].index >= elem.choiceIndex) {
+					stack.pop_back();
+				}
+
+				if (!stack.empty()) {
+					ConversationNode *parent = stack[stack.size() - 1].node;
+					parent->subchoices.push_back(choiceNode);
+
+					// Get pointer to the newly added choice
+					ConversationNode *newChoice = &parent->subchoices[parent->subchoices.size() - 1];
+
+					StackEntry entry;
+					entry.node = newChoice;
+					entry.index = elem.choiceIndex;
+					stack.push_back(entry);
+				} else {
+					if (currentRoot) {
+						currentRoot->choices.push_back(choiceNode);
+
+						// Get pointer to the newly added choice
+						ConversationNode *newChoice = &currentRoot->choices[currentRoot->choices.size() - 1];
+
+						StackEntry entry;
+						entry.node = newChoice;
+						entry.index = elem.choiceIndex;
+						stack.push_back(entry);
+					}
+				}
+			} else {
+				// Auto-dialogue - ALFRED just speaks
+				if (!stack.empty()) {
+					ConversationNode *parent = stack[stack.size() - 1].node;
+					ConversationNode response;
+					response.type = ConversationNode::RESPONSE;
+					response.speaker = "ALFRED";
 					response.speakerId = 0x0D;
-                parent->responses.push_back(response);
-            }
-            i++;
-
-        } else if (elem.type == ConversationElement::END_CONV) {
-            if (!stack.empty()) {
-                stack[stack.size() - 1].node->terminated = true;
-                stack.pop_back();
-            }
-            i++;
-
-        } else if (elem.type == ConversationElement::END_BRANCH) {
-            stack.clear();
-            currentRoot = nullptr;
-            i++;
-
-        } else {
-            i++;
-        }
-    }
-
-    return roots;
+					response.text = elem.text;
+					parent->responses.push_back(response);
+				}
+			}
+			i++;
+
+		} else if (elem.type == ConversationElement::DIALOGUE && elem.speaker == "ALFRED") {
+			if (!stack.empty()) {
+				ConversationNode *parent = stack[stack.size() - 1].node;
+				ConversationNode response;
+				response.type = ConversationNode::RESPONSE;
+				response.speaker = "ALFRED";
+				response.text = elem.text;
+				response.speakerId = 0x0D;
+				parent->responses.push_back(response);
+			}
+			i++;
+
+		} else if (elem.type == ConversationElement::END_CONV) {
+			if (!stack.empty()) {
+				stack[stack.size() - 1].node->terminated = true;
+				stack.pop_back();
+			}
+			i++;
+
+		} else if (elem.type == ConversationElement::END_BRANCH) {
+			stack.clear();
+			currentRoot = nullptr;
+			i++;
+
+		} else {
+			i++;
+		}
+	}
+
+	return roots;
 }
 
 Common::Array<ConversationNode> PelrockEngine::loadConversations(Common::File *roomFile, int roomOffset, uint32_t startPos) {
 
-    debug("Loading conversations starting at position %d", startPos);
+	debug("Loading conversations starting at position %d", startPos);
 
-    uint32_t pair12_offset_pos = roomOffset + (12 * 8);
-    roomFile->seek(pair12_offset_pos, SEEK_SET);
-    uint32_t pair12_data_offset = roomFile->readUint32LE();
-    uint32_t pair12_size = roomFile->readUint32LE();
+	uint32_t pair12_offset_pos = roomOffset + (12 * 8);
+	roomFile->seek(pair12_offset_pos, SEEK_SET);
+	uint32_t pair12_data_offset = roomFile->readUint32LE();
+	uint32_t pair12_size = roomFile->readUint32LE();
 
 	// startPos += 2;
-    uint32_t conversation_start = pair12_data_offset + startPos;
-    uint32_t conversation_size = pair12_size - startPos;
+	uint32_t conversation_start = pair12_data_offset + startPos;
+	uint32_t conversation_size = pair12_size - startPos;
 
-    roomFile->seek(conversation_start, SEEK_SET);
-    byte *data = new byte[conversation_size];
-    roomFile->read(data, conversation_size);
+	roomFile->seek(conversation_start, SEEK_SET);
+	byte *data = new byte[conversation_size];
+	roomFile->read(data, conversation_size);
 
 	Common::Array<ConversationElement> elements = parseConversationElements(data, conversation_size);
 	Common::Array<ConversationNode> roots = buildTreeStructure(elements);
 	return roots;
 
-    // uint32_t i = 0;
-    // int lineNum = 1;
-
-    // while (i < conversation_size) {
-    //     uint32_t lineStart = i;
-    //     ConversationLine line;
-    //     line.offset = startPos + lineStart;
-    //     line.speaker = 0;
-
-    //     // Read until we hit an end marker
-    //     while (i < conversation_size) {
-    //         byte b = data[i];
-
-    //         if (b == 0x08) { // SPEAKER
-    //             i++;
-    //             if (i < conversation_size) {
-    //                 line.speaker = data[i];
-    //                 line.controlBytes.push_back(Common::String::format("SPEAKER(0x%02X)", line.speaker));
-    //                 i++;
-    //             }
-    //             continue;
-    //         } else if (isControlByte(b)) {
-    //             line.controlBytes.push_back(Common::String::format("%s(0x%02X)",
-    //                                                                 getControlName(b).c_str(), b));
-    //             i++;
-
-    //             // Check for end markers
-    //             if (isTerminalByte(b)) {
-    //                 break;
-    //             }
-    //         } else {
-    //             // Regular text character
-    //             if (b != 0x00) {
-    //                 line.text += decodeByte(b);
-    //             }
-    //             i++;
-    //         }
-    //     }
-
-    //     // Store raw bytes for this line
-    //     for (uint32_t j = lineStart; j < i && j < conversation_size; j++) {
-    //         line.rawBytes.push_back(data[j]);
-    //     }
-
-    //     // Only add line if it has content
-    //     if (!line.text.empty() || line.controlBytes.size() > 0) {
-    //         // debug("Line %d (offset %d): %s", lineNum, line.offset, line.text.c_str());
-    //         if (line.controlBytes.size() > 0) {
-    //             Common::String controls;
-    //             for (uint k = 0; k < line.controlBytes.size(); k++) {
-    //                 if (k > 0) controls += ", ";
-    //                 controls += line.controlBytes[k];
-    //             }
-    //             // debug("  Controls: %s", controls.c_str());
-    //         }
-    //         conversations.push_back(line);
-    //         lineNum++;
-    //     }
-    // }
-
-    // delete[] data;
-    // debug("Loaded %d conversation lines", conversations.size());
-    // return conversations;
+	// uint32_t i = 0;
+	// int lineNum = 1;
+
+	// while (i < conversation_size) {
+	//     uint32_t lineStart = i;
+	//     ConversationLine line;
+	//     line.offset = startPos + lineStart;
+	//     line.speaker = 0;
+
+	//     // Read until we hit an end marker
+	//     while (i < conversation_size) {
+	//         byte b = data[i];
+
+	//         if (b == 0x08) { // SPEAKER
+	//             i++;
+	//             if (i < conversation_size) {
+	//                 line.speaker = data[i];
+	//                 line.controlBytes.push_back(Common::String::format("SPEAKER(0x%02X)", line.speaker));
+	//                 i++;
+	//             }
+	//             continue;
+	//         } else if (isControlByte(b)) {
+	//             line.controlBytes.push_back(Common::String::format("%s(0x%02X)",
+	//                                                                 getControlName(b).c_str(), b));
+	//             i++;
+
+	//             // Check for end markers
+	//             if (isTerminalByte(b)) {
+	//                 break;
+	//             }
+	//         } else {
+	//             // Regular text character
+	//             if (b != 0x00) {
+	//                 line.text += decodeByte(b);
+	//             }
+	//             i++;
+	//         }
+	//     }
+
+	//     // Store raw bytes for this line
+	//     for (uint32_t j = lineStart; j < i && j < conversation_size; j++) {
+	//         line.rawBytes.push_back(data[j]);
+	//     }
+
+	//     // Only add line if it has content
+	//     if (!line.text.empty() || line.controlBytes.size() > 0) {
+	//         // debug("Line %d (offset %d): %s", lineNum, line.offset, line.text.c_str());
+	//         if (line.controlBytes.size() > 0) {
+	//             Common::String controls;
+	//             for (uint k = 0; k < line.controlBytes.size(); k++) {
+	//                 if (k > 0) controls += ", ";
+	//                 controls += line.controlBytes[k];
+	//             }
+	//             // debug("  Controls: %s", controls.c_str());
+	//         }
+	//         conversations.push_back(line);
+	//         lineNum++;
+	//     }
+	// }
+
+	// delete[] data;
+	// debug("Loaded %d conversation lines", conversations.size());
+	// return conversations;
 }
 
 void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
@@ -877,8 +892,8 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	Common::Array<Description> descriptions = loadRoomDescriptions(roomFile, roomOffset, outPos);
 	debug("After decsriptions, position is %d", outPos);
 	Common::Array<ConversationNode> roots = loadConversations(roomFile, roomOffset, outPos);
-	for(int i = 0; i < roots.size(); i++) {
-		if( roots[i].text.empty()) {
+	for (int i = 0; i < roots.size(); i++) {
+		if (roots[i].text.empty()) {
 			continue;
 		}
 		debug("Conversation %d: %s", i, roots[i].text.c_str());
@@ -907,7 +922,6 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	Common::List<Exit> exits = loadExits(roomFile, roomOffset);
 	Common::List<WalkBox> walkboxes = loadWalkboxes(roomFile, roomOffset);
 
-
 	debug("total descriptions = %d, anims = %d, hotspots = %d", descriptions.size(), anims.size(), staticHotspots.size());
 	for (int i = 0; i < staticHotspots.size(); i++) {
 		HotSpot hotspot = staticHotspots[i];
@@ -915,14 +929,14 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	}
 
 	int walkboxCount = 0;
-	for (Common::List<Exit>::iterator i = _currentRoomExits.begin(); i != _currentRoomExits.end(); i++) {
-		// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
-		_screen->drawLine(i->x, i->y, i->x + i->w, i->y, 0 + walkboxCount);
-		_screen->drawLine(i->x, i->y + i->h, i->x + i->w, i->y + i->h, 0 + walkboxCount);
-		_screen->drawLine(i->x, i->y, i->x, i->y + i->h, 0 + walkboxCount);
-		_screen->drawLine(i->x + i->w, i->y, i->x + i->w, i->y + i->h, 0 + walkboxCount);
-		walkboxCount++;
-	}
+	// for (Common::List<Exit>::iterator i = _currentRoomExits.begin(); i != _currentRoomExits.end(); i++) {
+	// 	// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
+	// 	_screen->drawLine(i->x, i->y, i->x + i->w, i->y, 0 + walkboxCount);
+	// 	_screen->drawLine(i->x, i->y + i->h, i->x + i->w, i->y + i->h, 0 + walkboxCount);
+	// 	_screen->drawLine(i->x, i->y, i->x, i->y + i->h, 0 + walkboxCount);
+	// 	_screen->drawLine(i->x + i->w, i->y, i->x + i->w, i->y + i->h, 0 + walkboxCount);
+	// 	walkboxCount++;
+	// }
 
 	_currentRoomAnims = anims;
 	_currentRoomHotspots = hotspots;
@@ -930,14 +944,14 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	_currentRoomWalkboxes = walkboxes;
 	_currentRoomDescriptions = descriptions;
 
-	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
-		HotSpot hotspot = _currentRoomHotspots[i];
-		// debug("Hotspot %d: x=%d y=%d w=%d h=%d type=%d enabled? %d desc=%s", i, hotspot.x, hotspot.y, hotspot.w, hotspot.h, hotspot.type, hotspot.isEnabled, _currentRoomDescriptions[i].text.c_str());
-		_screen->drawLine(hotspot.x, hotspot.y, hotspot.x + hotspot.w, hotspot.y, 200 + i);
-		_screen->drawLine(hotspot.x, hotspot.y + hotspot.h, hotspot.x + hotspot.w, hotspot.y + hotspot.h, 200 + i);
-		_screen->drawLine(hotspot.x, hotspot.y, hotspot.x, hotspot.y + hotspot.h, 200 + i);
-		_screen->drawLine(hotspot.x + hotspot.w, hotspot.y, hotspot.x + hotspot.w, hotspot.y + hotspot.h, 200 + i);
-	}
+	// for (int i = 0; i < _currentRoomHotspots.size(); i++) {
+	// 	HotSpot hotspot = _currentRoomHotspots[i];
+	// 	// debug("Hotspot %d: x=%d y=%d w=%d h=%d type=%d enabled? %d desc=%s", i, hotspot.x, hotspot.y, hotspot.w, hotspot.h, hotspot.type, hotspot.isEnabled, _currentRoomDescriptions[i].text.c_str());
+	// 	_screen->drawLine(hotspot.x, hotspot.y, hotspot.x + hotspot.w, hotspot.y, 200 + i);
+	// 	_screen->drawLine(hotspot.x, hotspot.y + hotspot.h, hotspot.x + hotspot.w, hotspot.y + hotspot.h, 200 + i);
+	// 	_screen->drawLine(hotspot.x, hotspot.y, hotspot.x, hotspot.y + hotspot.h, 200 + i);
+	// 	_screen->drawLine(hotspot.x + hotspot.w, hotspot.y, hotspot.x + hotspot.w, hotspot.y + hotspot.h, 200 + i);
+	// }
 }
 
 void PelrockEngine::loadCursors() {
@@ -1045,7 +1059,16 @@ Common::Array<HotSpot> PelrockEngine::loadHotspots(Common::File *roomFile, int r
 	// roomFile->seek(hover_areas_start, SEEK_SET);
 }
 
-void PelrockEngine::loadMainCharacterAnims() {
+void extractSingleFrame(byte *source, byte *dest, int frameIndex, int frameWidth, int frameHeight) {
+	for (int y = 0; y < frameHeight; y++) {
+		for (int x = 0; x < frameWidth; x++) {
+			unsigned int src_pos = (frameIndex * frameHeight * frameWidth) + (y * frameWidth) + x;
+			dest[y * frameWidth + x] = source[src_pos];
+		}
+	}
+}
+
+void PelrockEngine::loadAlfredAnims() {
 	Common::File alfred3;
 	if (!alfred3.open(Common::Path("ALFRED.3"))) {
 		error("Could not open ALFRED.3");
@@ -1062,7 +1085,38 @@ void PelrockEngine::loadMainCharacterAnims() {
 	uint32_t capacity = 3060 * 102;
 	unsigned char *pic = new unsigned char[capacity];
 	rleDecompress(bufferFile, alfred3Size, 0, alfred3Size, &pic);
-	memcpy(standingAnim, pic, 3060 * 102);
+
+	for (int i = 0; i < 4; i++) {
+		standingAnimFrames[i] = new byte[kAlfredFrameWidth * kAlfredFrameHeight];
+
+		int prevWalkingFrames = 0;
+		int talkingFramesOffset = walkingAnimLengths[0] + walkingAnimLengths[1] + walkingAnimLengths[2] + walkingAnimLengths[3];
+		int prevTalkingFrames = 0;
+		for (int j = 0; j < i; j++) {
+			prevWalkingFrames += walkingAnimLengths[j] + 1;
+			prevTalkingFrames += talkingFramesOffset + talkingAnimLengths[j];
+		}
+		walkingAnimFrames[i] = new byte *[walkingAnimLengths[i]];
+
+		int standingFrame = prevWalkingFrames;
+		debug("Loading standing frame %d at index %d", i, standingFrame);
+		extractSingleFrame(pic, standingAnimFrames[i], standingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
+		for (int j = 0; j < walkingAnimLengths[i]; j++) {
+
+			walkingAnimFrames[i][j] = new byte[kAlfredFrameWidth * kAlfredFrameHeight];
+			int walkingFrame = prevWalkingFrames + 1 + j;
+			extractSingleFrame(pic, walkingAnimFrames[i][j], walkingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
+		}
+
+		talkingAnimFrames[i] = new byte *[talkingAnimLengths[i]];
+
+		int talkingStartFrame = prevWalkingFrames + 1 + walkingAnimLengths[i];
+		for (int j = 0; j < talkingAnimLengths[i]; j++) {
+			talkingAnimFrames[i][j] = new byte[kAlfredFrameWidth * kAlfredFrameHeight];
+			int talkingFrame = talkingStartFrame + j;
+			extractSingleFrame(pic, talkingAnimFrames[i][j], talkingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
+		}
+	}
 }
 
 byte *PelrockEngine::grabBackgroundSlice(int x, int y, int w, int h) {
@@ -1169,10 +1223,10 @@ void PelrockEngine::frames() {
 
 			for (uint32_t y = 0; y < kAlfredFrameHeight; y++) {
 				for (uint32_t x = 0; x < kAlfredFrameWidth; x++) {
-					unsigned int src_pos = (curAlfredFrame * kAlfredFrameHeight * kAlfredFrameWidth) + (y * kAlfredFrameWidth) + x;
+					unsigned int src_pos = (y * kAlfredFrameWidth) + x;
 					// debug("Xpos = %d, yPos=%d", x + xAlfred, y + yAlfred);
-					if (standingAnim[src_pos] != 255)
-						_screen->setPixel(x + xAlfred, y + yAlfred, standingAnim[src_pos]);
+					if (standingAnimFrames[dirAlfred][src_pos] != 255)
+						_screen->setPixel(x + xAlfred, y + yAlfred, standingAnimFrames[dirAlfred][src_pos]);
 				}
 			}
 
@@ -1463,10 +1517,10 @@ void PelrockEngine::showDescription(Common::String text, int x, int y, byte colo
 	if (y + 2 + rect.height() > 400) {
 		y = 400 - rect.height();
 	}
-	if(x - 2 < 0) {
+	if (x - 2 < 0) {
 		x = 2;
 	}
-	if(y - 2 < 0) {
+	if (y - 2 < 0) {
 		y = 2;
 	}
 
@@ -1501,6 +1555,7 @@ void PelrockEngine::setScreen(int number, int dir) {
 		error("Could not open ALFRED.1");
 		return;
 	}
+	dirAlfred = dir;
 
 	int roomOffset = number * kRoomStructSize;
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 09c0a7b3587..c26c380e890 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -63,7 +63,7 @@ private:
 	// Room data
 	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
-	void loadMainCharacterAnims();
+	void loadAlfredAnims();
 	Common::List<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
 	Common::Array<HotSpot> loadHotspots(Common::File *roomFile, int roomOffset);
 	Common::List<Exit> loadExits(Common::File *roomFile, int roomOffset);
@@ -71,7 +71,7 @@ private:
 	Common::Array<Description> loadRoomDescriptions(Common::File *roomFile, int roomOffset, uint32_t &outPos);
 
 	Common::String cleanText(const Common::String &text);
-    Common::Array<ConversationElement> parseConversationElements(const byte *convData, uint32 size);
+	Common::Array<ConversationElement> parseConversationElements(const byte *convData, uint32 size);
 	Common::Array<ConversationNode> buildTreeStructure(const Common::Array<ConversationElement> &elements);
 	Common::Array<ConversationNode> loadConversations(Common::File *roomFile, int roomOffset, uint32_t startPos);
 
@@ -99,7 +99,14 @@ private:
 	void showActionBalloon(int posx, int posy, int curFrame);
 
 	ChronoManager *_chronoManager = nullptr;
-	byte *standingAnim = new byte[3060 * 102];
+
+	// byte *standingAnim = new byte[3060 * 102];
+
+	byte **walkingAnimFrames[4];              // 4 arrays of arrays
+	byte *standingAnimFrames[4];              // 4 directions
+	int walkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
+	byte **talkingAnimFrames[4];              // 4 arrays of arrays
+	int talkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
 	Common::Array<HotSpot> _currentRoomHotspots;
 	Common::List<HoverArea> _hoverAreas;
 	Common::List<AnimSet> _currentRoomAnims;
@@ -109,6 +116,10 @@ private:
 	Common::Array<ConversationNode> _currentRoomConversations;
 
 	int *_currentAnimFrames = nullptr;
+	// From the original code
+	int xAlfred = 200;
+	int yAlfred = 200;
+	int dirAlfred = 0;
 	int curAlfredFrame = 9;
 	uint16 mouseX = 0;
 	uint16 mouseY = 0;
@@ -132,14 +143,10 @@ private:
 	SmallFont *_smallFont = nullptr;
 	LargeFont *_largeFont = nullptr;
 
-	// From the original code
-	int xAlfred = 200;
-	int yAlfred = 200;
 	bool shouldPlayIntro = false;
 	GameState stateGame = GAME;
 	bool gameInitialized = false;
 	bool screenReady = false;
-	int dirAlfred = 0;
 	int prevDirX = 0;
 	int prevDirY = 0;
 	Common::String objectToShow = "";


Commit: 6850b9a42bb726ced827014c4d7a592cb230a9f3
    https://github.com/scummvm/scummvm/commit/6850b9a42bb726ced827014c4d7a592cb230a9f3
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:04+02:00

Commit Message:
PELROCK: Displays walking and talking animations

Changed paths:
    engines/pelrock/chrono.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/chrono.h b/engines/pelrock/chrono.h
index 0adce498a89..1a5c902bced 100644
--- a/engines/pelrock/chrono.h
+++ b/engines/pelrock/chrono.h
@@ -26,7 +26,7 @@
 namespace Pelrock {
 
 // const int kTickMs = 20;
-const int kTickMs = 50;
+const int kTickMs = 200;
 const int kHalfTickMultiplier = 2;
 
 class ChronoManager {
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index c8014a6d8f2..a58a333d374 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -64,6 +64,16 @@ PelrockEngine::~PelrockEngine() {
 	if (_bgPopupBalloon)
 		delete[] _bgPopupBalloon;
 	delete _smallFont;
+	for (int i = 0; i < 4; i++) {
+
+		// free all frame buffers
+		for (int j = 0; j < walkingAnimLengths[i]; j++) {
+			delete[] walkingAnimFrames[i][j];
+		}
+
+		// free the array of pointers
+		delete[] walkingAnimFrames[i];
+	}
 }
 
 uint32 PelrockEngine::getFeatures() const {
@@ -102,7 +112,18 @@ Common::Error PelrockEngine::run() {
 	while (!shouldQuit()) {
 		_chronoManager->updateChrono();
 		while (g_system->getEventManager()->pollEvent(e)) {
-			if (e.type == Common::EVENT_MOUSEMOVE) {
+			if (e.type == Common::EVENT_KEYDOWN) {
+				if (e.kbd.keycode == Common::KEYCODE_w) {
+					isAlfredWalking = true;
+					isAlfredTalking = false;
+				} else if (e.kbd.keycode == Common::KEYCODE_t) {
+					isAlfredWalking = false;
+					isAlfredTalking = true;
+				} else if (e.kbd.keycode == Common::KEYCODE_s) {
+					isAlfredWalking = false;
+					isAlfredTalking = false;
+				}
+			} else if (e.type == Common::EVENT_MOUSEMOVE) {
 				mouseX = e.mouse.x;
 				mouseY = e.mouse.y;
 				// debug(3, "Mouse moved to (%d,%d)", mouseX, mouseY);
@@ -124,9 +145,8 @@ Common::Error PelrockEngine::run() {
 		checkMouseHover();
 		frames();
 		_screen->update();
-		limiter.delayBeforeSwap();
-		_screen->update();
-		limiter.startFrame();
+		// limiter.delayBeforeSwap();
+		// limiter.startFrame();
 	}
 
 	return Common::kNoError;
@@ -354,7 +374,7 @@ Common::List<WalkBox> PelrockEngine::loadWalkboxes(Common::File *roomFile, int r
 		int16 w = roomFile->readSint16LE();
 		int16 h = roomFile->readSint16LE();
 		byte flags = roomFile->readByte();
-		// debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
+		debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
 		WalkBox box;
 		box.x = x1;
 		box.y = y1;
@@ -920,6 +940,7 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 
 	Common::Array<HotSpot> staticHotspots = loadHotspots(roomFile, roomOffset);
 	Common::List<Exit> exits = loadExits(roomFile, roomOffset);
+
 	Common::List<WalkBox> walkboxes = loadWalkboxes(roomFile, roomOffset);
 
 	debug("total descriptions = %d, anims = %d, hotspots = %d", descriptions.size(), anims.size(), staticHotspots.size());
@@ -929,14 +950,6 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	}
 
 	int walkboxCount = 0;
-	// for (Common::List<Exit>::iterator i = _currentRoomExits.begin(); i != _currentRoomExits.end(); i++) {
-	// 	// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
-	// 	_screen->drawLine(i->x, i->y, i->x + i->w, i->y, 0 + walkboxCount);
-	// 	_screen->drawLine(i->x, i->y + i->h, i->x + i->w, i->y + i->h, 0 + walkboxCount);
-	// 	_screen->drawLine(i->x, i->y, i->x, i->y + i->h, 0 + walkboxCount);
-	// 	_screen->drawLine(i->x + i->w, i->y, i->x + i->w, i->y + i->h, 0 + walkboxCount);
-	// 	walkboxCount++;
-	// }
 
 	_currentRoomAnims = anims;
 	_currentRoomHotspots = hotspots;
@@ -952,6 +965,15 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	// 	_screen->drawLine(hotspot.x, hotspot.y, hotspot.x, hotspot.y + hotspot.h, 200 + i);
 	// 	_screen->drawLine(hotspot.x + hotspot.w, hotspot.y, hotspot.x + hotspot.w, hotspot.y + hotspot.h, 200 + i);
 	// }
+
+	for (Common::List<Exit>::iterator i = _currentRoomExits.begin(); i != _currentRoomExits.end(); i++) {
+		debug("Exit: x=%d y=%d w=%d h=%d to room %d", i->x, i->y, i->w, i->h, i->targetRoom);
+		// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
+		// _screen->drawLine(i->x, i->y, i->x + i->w, i->y, 0);
+		// _screen->drawLine(i->x, i->y + i->h, i->x + i->w, i->y + i->h, 0);
+		// _screen->drawLine(i->x, i->y, i->x, i->y + i->h, 0);
+		// _screen->drawLine(i->x + i->w, i->y, i->x + i->w, i->y + i->h);
+	}
 }
 
 void PelrockEngine::loadCursors() {
@@ -1088,14 +1110,16 @@ void PelrockEngine::loadAlfredAnims() {
 
 	for (int i = 0; i < 4; i++) {
 		standingAnimFrames[i] = new byte[kAlfredFrameWidth * kAlfredFrameHeight];
+		int talkingFramesOffset = walkingAnimLengths[0] + walkingAnimLengths[1] + walkingAnimLengths[2] + walkingAnimLengths[3] + 4;
 
 		int prevWalkingFrames = 0;
-		int talkingFramesOffset = walkingAnimLengths[0] + walkingAnimLengths[1] + walkingAnimLengths[2] + walkingAnimLengths[3];
 		int prevTalkingFrames = 0;
+
 		for (int j = 0; j < i; j++) {
 			prevWalkingFrames += walkingAnimLengths[j] + 1;
-			prevTalkingFrames += talkingFramesOffset + talkingAnimLengths[j];
+			prevTalkingFrames += talkingAnimLengths[j];
 		}
+
 		walkingAnimFrames[i] = new byte *[walkingAnimLengths[i]];
 
 		int standingFrame = prevWalkingFrames;
@@ -1110,7 +1134,7 @@ void PelrockEngine::loadAlfredAnims() {
 
 		talkingAnimFrames[i] = new byte *[talkingAnimLengths[i]];
 
-		int talkingStartFrame = prevWalkingFrames + 1 + walkingAnimLengths[i];
+		int talkingStartFrame = talkingFramesOffset + prevTalkingFrames;
 		for (int j = 0; j < talkingAnimLengths[i]; j++) {
 			talkingAnimFrames[i][j] = new byte[kAlfredFrameWidth * kAlfredFrameHeight];
 			int talkingFrame = talkingStartFrame + j;
@@ -1179,9 +1203,8 @@ Common::List<VerbIcons> PelrockEngine::populateActionsMenu(HotSpot *hotspot) {
 void PelrockEngine::frames() {
 
 	if (_chronoManager->_gameTick) {
-
+		debug("Game tick!");
 		int num = 0;
-
 		for (Common::List<AnimSet>::iterator i = _currentRoomAnims.begin(); i != _currentRoomAnims.end(); i++) {
 			// debug("Processing animation set %d, numAnims %d", num, i->numAnims);
 			if (i->numAnims == 0) {
@@ -1220,38 +1243,71 @@ void PelrockEngine::frames() {
 				}
 			}
 			num++;
+		}
 
-			for (uint32_t y = 0; y < kAlfredFrameHeight; y++) {
-				for (uint32_t x = 0; x < kAlfredFrameWidth; x++) {
-					unsigned int src_pos = (y * kAlfredFrameWidth) + x;
-					// debug("Xpos = %d, yPos=%d", x + xAlfred, y + yAlfred);
-					if (standingAnimFrames[dirAlfred][src_pos] != 255)
-						_screen->setPixel(x + xAlfred, y + yAlfred, standingAnimFrames[dirAlfred][src_pos]);
-				}
+		if (_bgAlfred != nullptr) {
+			putBackgroundSlice(xAlfred, yAlfred, kAlfredFrameWidth, kAlfredFrameHeight, _bgAlfred);
+			delete[] _bgAlfred;
+			_bgAlfred = nullptr;
+		}
+		_bgAlfred = grabBackgroundSlice(xAlfred, yAlfred, kAlfredFrameWidth, kAlfredFrameHeight);
+
+		if (isAlfredWalking) {
+			debug("Drawing walking frame %d for direction %d", curAlfredFrame, dirAlfred);
+			drawAlfred(walkingAnimFrames[dirAlfred][curAlfredFrame]);
+			// for (uint32_t y = 0; y < kAlfredFrameHeight; y++) {
+			// 	for (uint32_t x = 0; x < kAlfredFrameWidth; x++) {
+			// 		unsigned int src_pos = (y * kAlfredFrameWidth) + x;
+			// 		// debug("Xpos = %d, yPos=%d", x + xAlfred, y + yAlfred);
+			// 		if (walkingAnimFrames[dirAlfred][curAlfredFrame][src_pos] != 255)
+			// 			_screen->setPixel(x + xAlfred, y + yAlfred, standingAnimFrames[dirAlfred][src_pos]);
+			// 	}
+			// }
+			if (curAlfredFrame < walkingAnimLengths[dirAlfred] - 1) {
+				curAlfredFrame++;
+			} else {
+				curAlfredFrame = 0;
 			}
-
-			if (_displayPopup) {
-
-				// byte *bgDialog = new byte[kBalloonWidth * kBalloonHeight];
-				// for (int j = 0; j < kBalloonWidth; j++) {
-				// 	for (int i = 0; i < kBalloonHeight; i++) {
-				// 		int idx = i * kBalloonWidth + j;
-				// 		if (_popupY + i < 400 && _popupX + j < 640) {
-				// 			*(bgDialog + idx) = _currentBackground[(_popupY + i) * 640 + (_popupX + j)];
-				// 		}
-				// 	}
-				// }
-				if (_bgPopupBalloon != nullptr) {
-					putBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight, _bgPopupBalloon);
-				}
-				showActionBalloon(_popupX, _popupY, _currentPopupFrame);
-				if (_currentPopupFrame < 3) {
-					_currentPopupFrame++;
-				} else
-					_currentPopupFrame = 0;
+			debug("CurAlfredFrame from walking is now %d", curAlfredFrame);
+		} else if (isAlfredTalking) {
+			drawAlfred(talkingAnimFrames[dirAlfred][curAlfredFrame]);
+			if (curAlfredFrame < talkingAnimLengths[dirAlfred] - 1) {
+				curAlfredFrame++;
+			} else {
+				curAlfredFrame = 0;
+			}
+			debug("CurAlfredFrame from talking is now %d", curAlfredFrame);
+		} else {
+			drawAlfred(standingAnimFrames[dirAlfred]);
+			// for (uint32_t y = 0; y < kAlfredFrameHeight; y++) {
+			// 	for (uint32_t x = 0; x < kAlfredFrameWidth; x++) {
+			// 		unsigned int src_pos = (y * kAlfredFrameWidth) + x;
+			// 		// debug("Xpos = %d, yPos=%d", x + xAlfred, y + yAlfred);
+			// 		if (standingAnimFrames[dirAlfred][src_pos] != 255)
+			// 			_screen->setPixel(x + xAlfred, y + yAlfred, standingAnimFrames[dirAlfred][src_pos]);
+			// 	}
+			// }
+		}
+		if (_displayPopup) {
+
+			// byte *bgDialog = new byte[kBalloonWidth * kBalloonHeight];
+			// for (int j = 0; j < kBalloonWidth; j++) {
+			// 	for (int i = 0; i < kBalloonHeight; i++) {
+			// 		int idx = i * kBalloonWidth + j;
+			// 		if (_popupY + i < 400 && _popupX + j < 640) {
+			// 			*(bgDialog + idx) = _currentBackground[(_popupY + i) * 640 + (_popupX + j)];
+			// 		}
+			// 	}
+			// }
+			if (_bgPopupBalloon != nullptr) {
+				putBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight, _bgPopupBalloon);
 			}
+			showActionBalloon(_popupX, _popupY, _currentPopupFrame);
+			if (_currentPopupFrame < 3) {
+				_currentPopupFrame++;
+			} else
+				_currentPopupFrame = 0;
 		}
-
 		int walkboxCount = 0;
 		for (Common::List<WalkBox>::iterator i = _currentRoomWalkboxes.begin(); i != _currentRoomWalkboxes.end(); i++) {
 			// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
@@ -1266,6 +1322,17 @@ void PelrockEngine::frames() {
 	}
 }
 
+void PelrockEngine::drawAlfred(byte *buf) {
+	for (uint32_t y = 0; y < kAlfredFrameHeight; y++) {
+		for (uint32_t x = 0; x < kAlfredFrameWidth; x++) {
+			unsigned int src_pos = (y * kAlfredFrameWidth) + x;
+			// debug("Xpos = %d, yPos=%d", x + xAlfred, y + yAlfred);
+			if (buf[src_pos] != 255 && x + xAlfred >= 0 && y + yAlfred >= 0 && x + xAlfred < 640 && y + yAlfred < 400)
+				_screen->setPixel(x + xAlfred, y + yAlfred, buf[src_pos]);
+		}
+	}
+}
+
 void PelrockEngine::checkLongMouseClick(int x, int y) {
 	int hotspotIndex = isHotspotUnder(mouseX, mouseY);
 	if (hotspotIndex != -1) {
@@ -1558,7 +1625,7 @@ void PelrockEngine::setScreen(int number, int dir) {
 	dirAlfred = dir;
 
 	int roomOffset = number * kRoomStructSize;
-
+	curAlfredFrame = 0;
 	byte *palette = new byte[256 * 3];
 	getPalette(&roomFile, roomOffset, palette);
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index c26c380e890..94761a47665 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -89,6 +89,7 @@ private:
 
 	// render loop
 	void frames();
+	void drawAlfred(byte *buf);
 	void checkMouseHover();
 	void checkMouseClick(int x, int y);
 	void checkLongMouseClick(int x, int y);
@@ -108,7 +109,6 @@ private:
 	byte **talkingAnimFrames[4];              // 4 arrays of arrays
 	int talkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
 	Common::Array<HotSpot> _currentRoomHotspots;
-	Common::List<HoverArea> _hoverAreas;
 	Common::List<AnimSet> _currentRoomAnims;
 	Common::List<Exit> _currentRoomExits;
 	Common::List<WalkBox> _currentRoomWalkboxes;
@@ -120,7 +120,9 @@ private:
 	int xAlfred = 200;
 	int yAlfred = 200;
 	int dirAlfred = 0;
-	int curAlfredFrame = 9;
+	int curAlfredFrame = 0;
+	bool isAlfredWalking = false;
+	bool isAlfredTalking = false;
 	uint16 mouseX = 0;
 	uint16 mouseY = 0;
 	byte *_currentBackground = nullptr;
@@ -133,6 +135,7 @@ private:
 	byte *_popUpBalloon = nullptr;
 	byte *_bgPopupBalloon = nullptr;
 	byte *_bgText = nullptr;
+	byte *_bgAlfred = nullptr;
 
 	bool _displayPopup = false;
 	int _popupX = 0;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 35514e2a7c5..d1bc429980c 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -108,12 +108,6 @@ struct AnimSet {
 	Anim *animData;
 };
 
-struct HoverArea {
-	int x;
-	int y;
-	int w;
-	int h;
-};
 
 struct HotSpot {
 	int id;


Commit: 0535b469a44adcbd44ef93a75227704a23edbb7b
    https://github.com/scummvm/scummvm/commit/0535b469a44adcbd44ef93a75227704a23edbb7b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:04+02:00

Commit Message:
PELROCK: Properly refreshes scenes

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index a58a333d374..8933a301f11 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -61,8 +61,8 @@ PelrockEngine::~PelrockEngine() {
 	}
 
 	delete[] _popUpBalloon;
-	if (_bgPopupBalloon)
-		delete[] _bgPopupBalloon;
+	// if (_bgPopupBalloon)
+	// 	delete[] _bgPopupBalloon;
 	delete _smallFont;
 	for (int i = 0; i < 4; i++) {
 
@@ -89,6 +89,7 @@ Common::Error PelrockEngine::run() {
 	initGraphics(640, 400);
 	_screen = new Graphics::Screen();
 
+
 	// Set the engine's debugger console
 	setDebugger(new Console());
 
@@ -155,6 +156,10 @@ Common::Error PelrockEngine::run() {
 void PelrockEngine::init() {
 	loadCursors();
 	loadInteractionIcons();
+
+	_compositeBuffer = new byte[640 * 400];
+    _currentBackground = new byte[640 * 400];
+
 	_smallFont = new SmallFont();
 	_smallFont->load("ALFRED.4");
 	_largeFont = new LargeFont();
@@ -165,8 +170,9 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		// setScreen(0, 2);
-		setScreen(2, 2);
+		setScreen(0, 2);
+		// setScreen(2, 2);
+		// setScreen(28, 0);
 	}
 }
 
@@ -957,17 +963,17 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	_currentRoomWalkboxes = walkboxes;
 	_currentRoomDescriptions = descriptions;
 
-	// for (int i = 0; i < _currentRoomHotspots.size(); i++) {
-	// 	HotSpot hotspot = _currentRoomHotspots[i];
-	// 	// debug("Hotspot %d: x=%d y=%d w=%d h=%d type=%d enabled? %d desc=%s", i, hotspot.x, hotspot.y, hotspot.w, hotspot.h, hotspot.type, hotspot.isEnabled, _currentRoomDescriptions[i].text.c_str());
-	// 	_screen->drawLine(hotspot.x, hotspot.y, hotspot.x + hotspot.w, hotspot.y, 200 + i);
-	// 	_screen->drawLine(hotspot.x, hotspot.y + hotspot.h, hotspot.x + hotspot.w, hotspot.y + hotspot.h, 200 + i);
-	// 	_screen->drawLine(hotspot.x, hotspot.y, hotspot.x, hotspot.y + hotspot.h, 200 + i);
-	// 	_screen->drawLine(hotspot.x + hotspot.w, hotspot.y, hotspot.x + hotspot.w, hotspot.y + hotspot.h, 200 + i);
-	// }
+	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
+		HotSpot hotspot = _currentRoomHotspots[i];
+		// debug("Hotspot %d: x=%d y=%d w=%d h=%d type=%d enabled? %d extra=%d, desc=%s", i, hotspot.x, hotspot.y, hotspot.w, hotspot.h, hotspot.type, hotspot.isEnabled, hotspot.extra, _currentRoomDescriptions[i].text.c_str());
+		_screen->drawLine(hotspot.x, hotspot.y, hotspot.x + hotspot.w, hotspot.y, 200 + i);
+		_screen->drawLine(hotspot.x, hotspot.y + hotspot.h, hotspot.x + hotspot.w, hotspot.y + hotspot.h, 200 + i);
+		_screen->drawLine(hotspot.x, hotspot.y, hotspot.x, hotspot.y + hotspot.h, 200 + i);
+		_screen->drawLine(hotspot.x + hotspot.w, hotspot.y, hotspot.x + hotspot.w, hotspot.y + hotspot.h, 200 + i);
+	}
 
 	for (Common::List<Exit>::iterator i = _currentRoomExits.begin(); i != _currentRoomExits.end(); i++) {
-		debug("Exit: x=%d y=%d w=%d h=%d to room %d", i->x, i->y, i->w, i->h, i->targetRoom);
+		// debug("Exit: x=%d y=%d w=%d h=%d to room %d", i->x, i->y, i->w, i->h, i->targetRoom);
 		// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
 		// _screen->drawLine(i->x, i->y, i->x + i->w, i->y, 0);
 		// _screen->drawLine(i->x, i->y + i->h, i->x + i->w, i->y + i->h, 0);
@@ -1164,6 +1170,30 @@ void PelrockEngine::putBackgroundSlice(int x, int y, int w, int h, byte *slice)
 		}
 	}
 }
+
+// Helper function for transparent blitting
+void drawSpriteToBuffer(byte *buffer, int bufferWidth,
+						byte *sprite, int x, int y,
+						int width, int height,
+						int transparentColor) {
+	for (int py = 0; py < height; py++) {
+		for (int px = 0; px < width; px++) {
+			int srcIdx = py * width + px;
+			byte pixel = sprite[srcIdx];
+
+			if (pixel != transparentColor) {
+				int destX = x + px;
+				int destY = y + py;
+
+				if (destX >= 0 && destX < 640 &&
+					destY >= 0 && destY < 400) {
+					buffer[destY * bufferWidth + destX] = pixel;
+				}
+			}
+		}
+	}
+}
+
 Common::List<VerbIcons> PelrockEngine::populateActionsMenu(HotSpot *hotspot) {
 	Common::List<VerbIcons> verbs;
 	debug("Populating actions menu for hotspot type %d", hotspot->type);
@@ -1203,16 +1233,15 @@ Common::List<VerbIcons> PelrockEngine::populateActionsMenu(HotSpot *hotspot) {
 void PelrockEngine::frames() {
 
 	if (_chronoManager->_gameTick) {
+
+    	memcpy(_compositeBuffer, _currentBackground, 640 * 400);
+
 		debug("Game tick!");
-		int num = 0;
 		for (Common::List<AnimSet>::iterator i = _currentRoomAnims.begin(); i != _currentRoomAnims.end(); i++) {
 			// debug("Processing animation set %d, numAnims %d", num, i->numAnims);
-			if (i->numAnims == 0) {
-				num++;
-				continue;
-			}
 
-			for (int j = 0; j < i->numAnims; j++) {
+			int j = 0;
+			// for (int j = 0; j < i->numAnims; j++) {
 				int x = i->animData[j].x;
 				int y = i->animData[j].y;
 				int w = i->animData[j].w;
@@ -1224,45 +1253,38 @@ void PelrockEngine::frames() {
 				Common::copy(i->animData[j].animData + (curFrame * i->h * i->w), i->animData[j].animData + (curFrame * i->h * i->w) + (frameSize), frame);
 				// debug("Current frame %d of %d", curFrame, i->animData[j].nframes);
 
-				byte *bg = grabBackgroundSlice(x, y, w, h);
-				putBackgroundSlice(x, y, w, h, bg);
-				for (int y = 0; y < i->h; y++) {
-					for (int x = 0; x < i->w; x++) {
-
-						unsigned int src_pos = (y * i->w) + x;
-						int xPos = i->x + x;
-						int yPos = i->y + y;
-						if (frame[src_pos] != 255 && xPos > 0 && yPos > 0 && xPos < 640 && yPos < 400)
-							_screen->setPixel(xPos, yPos, frame[src_pos]);
-					}
-				}
+				// byte *bg = grabBackgroundSlice(x, y, w, h);
+				// putBackgroundSlice(x, y, w, h, bg);
+				drawSpriteToBuffer(_compositeBuffer, 640, frame, i->x, i->y, i->w, i->h, 255);
+				// for (int y = 0; y < i->h; y++) {
+				// 	for (int x = 0; x < i->w; x++) {
+
+				// 		unsigned int src_pos = (y * i->w) + x;
+				// 		int xPos = i->x + x;
+				// 		int yPos = i->y + y;
+				// 		if (frame[src_pos] != 255 && xPos > 0 && yPos > 0 && xPos < 640 && yPos < 400)
+				// 			_screen->setPixel(xPos, yPos, frame[src_pos]);
+				// 	}
+				// }
 				if (i->animData[j].curFrame < i->animData[j].nframes - 1) {
 					i->animData[j].curFrame++;
 				} else {
 					i->animData[j].curFrame = 0;
 				}
-			}
-			num++;
+			// }
 		}
 
-		if (_bgAlfred != nullptr) {
-			putBackgroundSlice(xAlfred, yAlfred, kAlfredFrameWidth, kAlfredFrameHeight, _bgAlfred);
-			delete[] _bgAlfred;
-			_bgAlfred = nullptr;
-		}
-		_bgAlfred = grabBackgroundSlice(xAlfred, yAlfred, kAlfredFrameWidth, kAlfredFrameHeight);
+		// if (_bgAlfred != nullptr) {
+		// 	putBackgroundSlice(xAlfred, yAlfred, kAlfredFrameWidth, kAlfredFrameHeight, _bgAlfred);
+		// 	delete[] _bgAlfred;
+		// 	_bgAlfred = nullptr;
+		// }
+		// _bgAlfred = grabBackgroundSlice(xAlfred, yAlfred, kAlfredFrameWidth, kAlfredFrameHeight);
 
 		if (isAlfredWalking) {
 			debug("Drawing walking frame %d for direction %d", curAlfredFrame, dirAlfred);
 			drawAlfred(walkingAnimFrames[dirAlfred][curAlfredFrame]);
-			// for (uint32_t y = 0; y < kAlfredFrameHeight; y++) {
-			// 	for (uint32_t x = 0; x < kAlfredFrameWidth; x++) {
-			// 		unsigned int src_pos = (y * kAlfredFrameWidth) + x;
-			// 		// debug("Xpos = %d, yPos=%d", x + xAlfred, y + yAlfred);
-			// 		if (walkingAnimFrames[dirAlfred][curAlfredFrame][src_pos] != 255)
-			// 			_screen->setPixel(x + xAlfred, y + yAlfred, standingAnimFrames[dirAlfred][src_pos]);
-			// 	}
-			// }
+
 			if (curAlfredFrame < walkingAnimLengths[dirAlfred] - 1) {
 				curAlfredFrame++;
 			} else {
@@ -1279,14 +1301,6 @@ void PelrockEngine::frames() {
 			debug("CurAlfredFrame from talking is now %d", curAlfredFrame);
 		} else {
 			drawAlfred(standingAnimFrames[dirAlfred]);
-			// for (uint32_t y = 0; y < kAlfredFrameHeight; y++) {
-			// 	for (uint32_t x = 0; x < kAlfredFrameWidth; x++) {
-			// 		unsigned int src_pos = (y * kAlfredFrameWidth) + x;
-			// 		// debug("Xpos = %d, yPos=%d", x + xAlfred, y + yAlfred);
-			// 		if (standingAnimFrames[dirAlfred][src_pos] != 255)
-			// 			_screen->setPixel(x + xAlfred, y + yAlfred, standingAnimFrames[dirAlfred][src_pos]);
-			// 	}
-			// }
 		}
 		if (_displayPopup) {
 
@@ -1299,38 +1313,40 @@ void PelrockEngine::frames() {
 			// 		}
 			// 	}
 			// }
-			if (_bgPopupBalloon != nullptr) {
-				putBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight, _bgPopupBalloon);
-			}
+			// if (_bgPopupBalloon != nullptr) {
+			// 	putBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight, _bgPopupBalloon);
+			// }
 			showActionBalloon(_popupX, _popupY, _currentPopupFrame);
 			if (_currentPopupFrame < 3) {
 				_currentPopupFrame++;
 			} else
 				_currentPopupFrame = 0;
 		}
-		int walkboxCount = 0;
-		for (Common::List<WalkBox>::iterator i = _currentRoomWalkboxes.begin(); i != _currentRoomWalkboxes.end(); i++) {
-			// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
-			_screen->drawLine(i->x, i->y, i->x + i->w, i->y, 0 + walkboxCount);
-			_screen->drawLine(i->x, i->y + i->h, i->x + i->w, i->y + i->h, 0 + walkboxCount);
-			_screen->drawLine(i->x, i->y, i->x, i->y + i->h, 0 + walkboxCount);
-			_screen->drawLine(i->x + i->w, i->y, i->x + i->w, i->y + i->h, 0 + walkboxCount);
-			walkboxCount++;
-		}
+		// int walkboxCount = 0;
+		// for (Common::List<WalkBox>::iterator i = _currentRoomWalkboxes.begin(); i != _currentRoomWalkboxes.end(); i++) {
+		// 	// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
+		// 	_screen->drawLine(i->x, i->y, i->x + i->w, i->y, 0 + walkboxCount);
+		// 	_screen->drawLine(i->x, i->y + i->h, i->x + i->w, i->y + i->h, 0 + walkboxCount);
+		// 	_screen->drawLine(i->x, i->y, i->x, i->y + i->h, 0 + walkboxCount);
+		// 	_screen->drawLine(i->x + i->w, i->y, i->x + i->w, i->y + i->h, 0 + walkboxCount);
+		// 	walkboxCount++;
+		// }
+		memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
 		_screen->markAllDirty();
-		_screen->update();
+		// _screen->update();
 	}
 }
 
 void PelrockEngine::drawAlfred(byte *buf) {
-	for (uint32_t y = 0; y < kAlfredFrameHeight; y++) {
-		for (uint32_t x = 0; x < kAlfredFrameWidth; x++) {
-			unsigned int src_pos = (y * kAlfredFrameWidth) + x;
-			// debug("Xpos = %d, yPos=%d", x + xAlfred, y + yAlfred);
-			if (buf[src_pos] != 255 && x + xAlfred >= 0 && y + yAlfred >= 0 && x + xAlfred < 640 && y + yAlfred < 400)
-				_screen->setPixel(x + xAlfred, y + yAlfred, buf[src_pos]);
-		}
-	}
+	// for (uint32_t y = 0; y < kAlfredFrameHeight; y++) {
+	// 	for (uint32_t x = 0; x < kAlfredFrameWidth; x++) {
+	// 		unsigned int src_pos = (y * kAlfredFrameWidth) + x;
+	// 		// debug("Xpos = %d, yPos=%d", x + xAlfred, y + yAlfred);
+	// 		if (buf[src_pos] != 255 && x + xAlfred >= 0 && y + yAlfred >= 0 && x + xAlfred < 640 && y + yAlfred < 400)
+	// 			_screen->setPixel(x + xAlfred, y + yAlfred, buf[src_pos]);
+	// 	}
+	// }
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, xAlfred, yAlfred, kAlfredFrameWidth, kAlfredFrameHeight, 255);
 }
 
 void PelrockEngine::checkLongMouseClick(int x, int y) {
@@ -1338,10 +1354,10 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 	if (hotspotIndex != -1) {
 		// _popupX = hotspot->x;
 		// _popupY = hotspot->y;
-		if (_bgPopupBalloon != nullptr) {
-			putBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight, _bgPopupBalloon);
-			delete[] _bgPopupBalloon;
-		}
+		// if (_bgPopupBalloon != nullptr) {
+		// 	putBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight, _bgPopupBalloon);
+		// 	delete[] _bgPopupBalloon;
+		// }
 		_popupX = x - kBalloonWidth / 2;
 		if (_popupX < 0)
 			_popupX = 0;
@@ -1357,7 +1373,7 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 		_currentPopupFrame = 0;
 		_currentHotspot = &_currentRoomHotspots[hotspotIndex];
 		debug("Current hotspot type: %d", _currentHotspot->type);
-		_bgPopupBalloon = grabBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight);
+		// _bgPopupBalloon = grabBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight);
 	}
 }
 
@@ -1397,41 +1413,22 @@ AnimSet *PelrockEngine::isSpriteUnder(int x, int y) {
 
 void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 
-	for (uint32_t y = 0; y < kBalloonHeight; y++) {
-		for (uint32_t x = 0; x < kBalloonWidth; x++) {
-			unsigned int src_pos = (curFrame * kBalloonHeight * kBalloonWidth) + (y * kBalloonWidth) + x;
-			if (_popUpBalloon[src_pos] != 255)
-				_screen->setPixel(x + posx, y + posy, _popUpBalloon[src_pos]);
-		}
-	}
-
+	drawSpriteToBuffer(_compositeBuffer, 640, _popUpBalloon + (curFrame * kBalloonHeight * kBalloonWidth), posx, posy, kBalloonWidth, kBalloonHeight, 255);
 	Common::List<VerbIcons> availableActions = populateActionsMenu(_currentHotspot);
 
 	// for (Common::List<VerbIcons>::iterator i = availableActions.begin(); i != availableActions.end(); i++) {
 	// 	debug("Verb icon to show: %d", *i);
 	// }
 
-	for (uint32_t y = 0; y < kVerbIconHeight; y++) {
-		for (uint32_t x = 0; x < kVerbIconWidth; x++) {
-			unsigned int src_pos = y * kVerbIconWidth + x;
-			if (_verbIcons[LOOK][src_pos] != 1)
-				_screen->setPixel(x + posx + 20, y + posy + 20, _verbIcons[LOOK][src_pos]);
-		}
-	}
-
+	drawSpriteToBuffer(_compositeBuffer, 640, _verbIcons[LOOK], posx + 20, posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
 	for (Common::List<VerbIcons>::iterator i = availableActions.begin(); i != availableActions.end(); i++) {
 		VerbIcons verb = *i;
 		int index = 0;
 		for (Common::List<VerbIcons>::iterator j = availableActions.begin(); j != i; j++) {
 			index++;
 		}
-		for (uint32_t y = 0; y < kVerbIconHeight; y++) {
-			for (uint32_t x = 0; x < kVerbIconWidth; x++) {
-				unsigned int src_pos = y * kVerbIconWidth + x;
-				if (_verbIcons[verb][src_pos] != 1)
-					_screen->setPixel(x + posx + 20 + (index * (kVerbIconWidth + 2)), y + posy + 20, _verbIcons[verb][src_pos]);
-			}
-		}
+		drawSpriteToBuffer(_compositeBuffer, 640, _verbIcons[verb], posx + 20 + (index * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
+
 	}
 }
 
@@ -1439,11 +1436,11 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 
 	_displayPopup = false;
 	_currentHotspot = nullptr;
-	if (_bgPopupBalloon != nullptr) {
-		putBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight, _bgPopupBalloon);
-		delete[] _bgPopupBalloon;
-		_bgPopupBalloon = nullptr;
-	}
+	// if (_bgPopupBalloon != nullptr) {
+	// 	putBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight, _bgPopupBalloon);
+	// 	delete[] _bgPopupBalloon;
+	// 	_bgPopupBalloon = nullptr;
+	// }
 
 	Common::Point walkTarget = calculateWalkTarget(mouseX, mouseY);
 
@@ -1458,6 +1455,7 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 	int hotspotIndex = isHotspotUnder(mouseX, mouseY);
 	if (hotspotIndex != -1) {
 		talk();
+		debug("Hotspot clicked: %d", _currentRoomHotspots[hotspotIndex].extra);
 		// showDescription(_currentRoomDescriptions[hotspotIndex].text.c_str(), xAlfred, yAlfred, 13);
 		// changeCursor(HOTSPOT);
 	}
@@ -1593,15 +1591,15 @@ void PelrockEngine::showDescription(Common::String text, int x, int y, byte colo
 
 	x = 2;
 	y = 2;
-	if (_bgText != nullptr) {
-		putBackgroundSlice(x, y, 640, 400, _bgText);
-		delete[] _bgText;
-	}
+	// if (_bgText != nullptr) {
+	// 	putBackgroundSlice(x, y, 640, 400, _bgText);
+	// 	delete[] _bgText;
+	// }
 	int16 w = MIN(rect.width(), (int16)(640 - x));
 	int16 h = MIN(rect.height(), (int16)(400 - y));
 	debug("grabbing bg slice at (%d,%d) w=%d h=%d", x, y, w, h);
 
-	_bgText = grabBackgroundSlice(x, y, 640, 400);
+	// _bgText = grabBackgroundSlice(x, y, 640, 400);
 	_largeFont->drawString(_screen, text.c_str(), x - 1, y, 640, 0); // Left
 	_largeFont->drawString(_screen, text.c_str(), x - 2, y, 640, 0); // Left
 	_largeFont->drawString(_screen, text.c_str(), x + 1, y, 640, 0); // Right
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 94761a47665..e9bbf720bb5 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -82,6 +82,8 @@ private:
 	void loadInteractionIcons();
 	byte *grabBackgroundSlice(int x, int y, int w, int h);
 	void putBackgroundSlice(int x, int y, int w, int h, byte *slice);
+
+
 	Common::List<VerbIcons> populateActionsMenu(HotSpot *hotspot);
 	Common::Point calculateWalkTarget(int mouseX, int mouseY);
 	Exit *isExitAtPoint(int x, int y);
@@ -108,6 +110,7 @@ private:
 	int walkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
 	byte **talkingAnimFrames[4];              // 4 arrays of arrays
 	int talkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
+
 	Common::Array<HotSpot> _currentRoomHotspots;
 	Common::List<AnimSet> _currentRoomAnims;
 	Common::List<Exit> _currentRoomExits;
@@ -125,7 +128,6 @@ private:
 	bool isAlfredTalking = false;
 	uint16 mouseX = 0;
 	uint16 mouseY = 0;
-	byte *_currentBackground = nullptr;
 	byte *_cursorMasks[5] = {nullptr};
 
 	uint32 _mouseDownTime;
@@ -133,9 +135,14 @@ private:
 
 	byte *_verbIcons[9] = {nullptr};
 	byte *_popUpBalloon = nullptr;
-	byte *_bgPopupBalloon = nullptr;
-	byte *_bgText = nullptr;
-	byte *_bgAlfred = nullptr;
+
+	byte *_currentBackground;      // Clean background - NEVER modified
+    byte *_compositeBuffer;        // Working composition buffer
+
+	// byte *_currentBackground = nullptr;
+	// byte *_bgPopupBalloon = nullptr;
+	// byte *_bgText = nullptr;
+	// byte *_bgAlfred = nullptr;
 
 	bool _displayPopup = false;
 	int _popupX = 0;


Commit: 0a07291d3431a35d00f41a91c6fcecc426e0712b
    https://github.com/scummvm/scummvm/commit/0a07291d3431a35d00f41a91c6fcecc426e0712b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:04+02:00

Commit Message:
PELROCK: Change list to array

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 8933a301f11..3a10a865267 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -89,7 +89,6 @@ Common::Error PelrockEngine::run() {
 	initGraphics(640, 400);
 	_screen = new Graphics::Screen();
 
-
 	// Set the engine's debugger console
 	setDebugger(new Console());
 
@@ -158,7 +157,7 @@ void PelrockEngine::init() {
 	loadInteractionIcons();
 
 	_compositeBuffer = new byte[640 * 400];
-    _currentBackground = new byte[640 * 400];
+	_currentBackground = new byte[640 * 400];
 
 	_smallFont = new SmallFont();
 	_smallFont->load("ALFRED.4");
@@ -267,7 +266,7 @@ void PelrockEngine::getBackground(Common::File *roomFile, int roomOffset, byte *
 	}
 }
 
-Common::List<AnimSet> PelrockEngine::loadRoomAnimations(Common::File *roomFile, int roomOffset) {
+Common::Array<AnimSet> PelrockEngine::loadRoomAnimations(Common::File *roomFile, int roomOffset) {
 	uint32_t pair_offset = roomOffset + (8 * 8);
 	// debug("Sprite pair offset position: %d", pair_offset);
 	roomFile->seek(pair_offset, SEEK_SET);
@@ -282,9 +281,9 @@ Common::List<AnimSet> PelrockEngine::loadRoomAnimations(Common::File *roomFile,
 	if (offset > 0 && size > 0) {
 		rleDecompress(data, size, 0, size, &pic);
 	} else {
-		return Common::List<AnimSet>();
+		return Common::Array<AnimSet>();
 	}
-	Common::List<AnimSet> anims = Common::List<AnimSet>();
+	Common::Array<AnimSet> anims = Common::Array<AnimSet>();
 	uint32_t spriteEnd = offset + size;
 
 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
@@ -926,20 +925,20 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	}
 	_currentRoomConversations = roots;
 
-	Common::List<AnimSet> anims = loadRoomAnimations(roomFile, roomOffset);
+	Common::Array<AnimSet> anims = loadRoomAnimations(roomFile, roomOffset);
 
 	Common::Array<HotSpot> hotspots;
 	int count = 0;
-	for (Common::List<AnimSet>::iterator i = anims.begin(); i != anims.end(); i++) {
+	for (int i = 0; i < anims.size(); i++) {
 
 		HotSpot thisHotspot;
-		thisHotspot.x = i->x;
-		thisHotspot.y = i->y;
-		thisHotspot.w = i->w;
-		thisHotspot.h = i->h;
-		thisHotspot.extra = i->extra;
-		thisHotspot.type = i->actionFlags;
-		thisHotspot.isEnabled = !i->isDisabled;
+		thisHotspot.x = anims[i].x;
+		thisHotspot.y = anims[i].y;
+		thisHotspot.w = anims[i].w;
+		thisHotspot.h = anims[i].h;
+		thisHotspot.extra = anims[i].extra;
+		thisHotspot.type = anims[i].actionFlags;
+		thisHotspot.isEnabled = !anims[i].isDisabled;
 		hotspots.push_back(thisHotspot);
 		count++;
 	}
@@ -1234,44 +1233,43 @@ void PelrockEngine::frames() {
 
 	if (_chronoManager->_gameTick) {
 
-    	memcpy(_compositeBuffer, _currentBackground, 640 * 400);
+		memcpy(_compositeBuffer, _currentBackground, 640 * 400);
 
 		debug("Game tick!");
-		for (Common::List<AnimSet>::iterator i = _currentRoomAnims.begin(); i != _currentRoomAnims.end(); i++) {
+		for (int i = 0; i < _currentRoomAnims.size(); i++) {
 			// debug("Processing animation set %d, numAnims %d", num, i->numAnims);
 
 			int j = 0;
-			// for (int j = 0; j < i->numAnims; j++) {
-				int x = i->animData[j].x;
-				int y = i->animData[j].y;
-				int w = i->animData[j].w;
-				int h = i->animData[j].h;
-
-				int frameSize = i->animData[j].w * i->animData[j].h;
-				int curFrame = i->animData[j].curFrame;
-				byte *frame = new byte[frameSize];
-				Common::copy(i->animData[j].animData + (curFrame * i->h * i->w), i->animData[j].animData + (curFrame * i->h * i->w) + (frameSize), frame);
-				// debug("Current frame %d of %d", curFrame, i->animData[j].nframes);
-
-				// byte *bg = grabBackgroundSlice(x, y, w, h);
-				// putBackgroundSlice(x, y, w, h, bg);
-				drawSpriteToBuffer(_compositeBuffer, 640, frame, i->x, i->y, i->w, i->h, 255);
-				// for (int y = 0; y < i->h; y++) {
-				// 	for (int x = 0; x < i->w; x++) {
-
-				// 		unsigned int src_pos = (y * i->w) + x;
-				// 		int xPos = i->x + x;
-				// 		int yPos = i->y + y;
-				// 		if (frame[src_pos] != 255 && xPos > 0 && yPos > 0 && xPos < 640 && yPos < 400)
-				// 			_screen->setPixel(xPos, yPos, frame[src_pos]);
-				// 	}
-				// }
-				if (i->animData[j].curFrame < i->animData[j].nframes - 1) {
-					i->animData[j].curFrame++;
-				} else {
-					i->animData[j].curFrame = 0;
-				}
+
+			int x = _currentRoomAnims[i].animData[j].x;
+			int y = _currentRoomAnims[i].animData[j].y;
+			int w = _currentRoomAnims[i].animData[j].w;
+			int h = _currentRoomAnims[i].animData[j].h;
+
+			int frameSize = _currentRoomAnims[i].animData[j].w * _currentRoomAnims[i].animData[j].h;
+			int curFrame = _currentRoomAnims[i].animData[j].curFrame;
+			byte *frame = new byte[frameSize];
+			Common::copy(_currentRoomAnims[i].animData[j].animData + (curFrame * _currentRoomAnims[i].animData[j].h * _currentRoomAnims[i].animData[j].w), _currentRoomAnims[i].animData[j].animData + (curFrame * _currentRoomAnims[i].animData[j].h * _currentRoomAnims[i].animData[j].w) + (frameSize), frame);
+			// debug("Current frame %d of %d", curFrame, i->animData[j].nframes);
+
+			// byte *bg = grabBackgroundSlice(x, y, w, h);
+			// putBackgroundSlice(x, y, w, h, bg);
+			drawSpriteToBuffer(_compositeBuffer, 640, frame, _currentRoomAnims[i].x, _currentRoomAnims[i].y, _currentRoomAnims[i].w, _currentRoomAnims[i].h, 255);
+			// for (int y = 0; y < i->h; y++) {
+			// 	for (int x = 0; x < i->w; x++) {
+
+			// 		unsigned int src_pos = (y * i->w) + x;
+			// 		int xPos = i->x + x;
+			// 		int yPos = i->y + y;
+			// 		if (frame[src_pos] != 255 && xPos > 0 && yPos > 0 && xPos < 640 && yPos < 400)
+			// 			_screen->setPixel(xPos, yPos, frame[src_pos]);
+			// 	}
 			// }
+			if (_currentRoomAnims[i].animData[j].curFrame < _currentRoomAnims[i].animData[j].nframes - 1) {
+				_currentRoomAnims[i].animData[j].curFrame++;
+			} else {
+				_currentRoomAnims[i].animData[j].curFrame = 0;
+			}
 		}
 
 		// if (_bgAlfred != nullptr) {
@@ -1400,17 +1398,6 @@ Exit *PelrockEngine::isExitUnder(int x, int y) {
 	return nullptr;
 }
 
-AnimSet *PelrockEngine::isSpriteUnder(int x, int y) {
-	for (Common::List<AnimSet>::iterator i = _currentRoomAnims.begin(); i != _currentRoomAnims.end(); i++) {
-		if (mouseX >= i->x && mouseX <= (i->x + i->w) &&
-			mouseY >= i->y && mouseY <= (i->y + i->h)) {
-			// debug("Sprite at (%d,%d) size (%d,%d), type = %d, extra = %d, enabled=%d", i->x, i->y, i->w, i->h, i->type, i->extra, i->enabled);
-			return &(*i);
-		}
-	}
-	return nullptr;
-}
-
 void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 
 	drawSpriteToBuffer(_compositeBuffer, 640, _popUpBalloon + (curFrame * kBalloonHeight * kBalloonWidth), posx, posy, kBalloonWidth, kBalloonHeight, 255);
@@ -1428,7 +1415,6 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 			index++;
 		}
 		drawSpriteToBuffer(_compositeBuffer, 640, _verbIcons[verb], posx + 20 + (index * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
-
 	}
 }
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index e9bbf720bb5..2cb9fe8393b 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -64,7 +64,7 @@ private:
 	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
 	void loadAlfredAnims();
-	Common::List<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
+	Common::Array<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
 	Common::Array<HotSpot> loadHotspots(Common::File *roomFile, int roomOffset);
 	Common::List<Exit> loadExits(Common::File *roomFile, int roomOffset);
 	Common::List<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
@@ -83,7 +83,6 @@ private:
 	byte *grabBackgroundSlice(int x, int y, int w, int h);
 	void putBackgroundSlice(int x, int y, int w, int h, byte *slice);
 
-
 	Common::List<VerbIcons> populateActionsMenu(HotSpot *hotspot);
 	Common::Point calculateWalkTarget(int mouseX, int mouseY);
 	Exit *isExitAtPoint(int x, int y);
@@ -112,7 +111,7 @@ private:
 	int talkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
 
 	Common::Array<HotSpot> _currentRoomHotspots;
-	Common::List<AnimSet> _currentRoomAnims;
+	Common::Array<AnimSet> _currentRoomAnims;
 	Common::List<Exit> _currentRoomExits;
 	Common::List<WalkBox> _currentRoomWalkboxes;
 	Common::Array<Description> _currentRoomDescriptions;
@@ -136,8 +135,8 @@ private:
 	byte *_verbIcons[9] = {nullptr};
 	byte *_popUpBalloon = nullptr;
 
-	byte *_currentBackground;      // Clean background - NEVER modified
-    byte *_compositeBuffer;        // Working composition buffer
+	byte *_currentBackground; // Clean background - NEVER modified
+	byte *_compositeBuffer;   // Working composition buffer
 
 	// byte *_currentBackground = nullptr;
 	// byte *_bgPopupBalloon = nullptr;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index d1bc429980c..a9b6c632e08 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -102,6 +102,7 @@ struct AnimSet {
 	int h;//5
 	byte extra; //6
 	int numAnims; //8
+	int curAnimIndex;
 	byte spriteType; //33
 	byte actionFlags;//34
 	bool isDisabled; //38


Commit: 5ce6183800745b5d9052a52b76e26596bd86b27b
    https://github.com/scummvm/scummvm/commit/5ce6183800745b5d9052a52b76e26596bd86b27b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:04+02:00

Commit Message:
PELROCK: Plays subanimations in order following control bytes

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 3a10a865267..7caabf4dcab 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1239,17 +1239,15 @@ void PelrockEngine::frames() {
 		for (int i = 0; i < _currentRoomAnims.size(); i++) {
 			// debug("Processing animation set %d, numAnims %d", num, i->numAnims);
 
-			int j = 0;
+			int x = _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].x;
+			int y = _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].y;
+			int w = _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].w;
+			int h = _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].h;
 
-			int x = _currentRoomAnims[i].animData[j].x;
-			int y = _currentRoomAnims[i].animData[j].y;
-			int w = _currentRoomAnims[i].animData[j].w;
-			int h = _currentRoomAnims[i].animData[j].h;
-
-			int frameSize = _currentRoomAnims[i].animData[j].w * _currentRoomAnims[i].animData[j].h;
-			int curFrame = _currentRoomAnims[i].animData[j].curFrame;
+			int frameSize = _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].w * _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].h;
+			int curFrame = _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curFrame;
 			byte *frame = new byte[frameSize];
-			Common::copy(_currentRoomAnims[i].animData[j].animData + (curFrame * _currentRoomAnims[i].animData[j].h * _currentRoomAnims[i].animData[j].w), _currentRoomAnims[i].animData[j].animData + (curFrame * _currentRoomAnims[i].animData[j].h * _currentRoomAnims[i].animData[j].w) + (frameSize), frame);
+			Common::copy(_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].animData + (curFrame * _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].h * _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].w), _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].animData + (curFrame * _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].h * _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].w) + (frameSize), frame);
 			// debug("Current frame %d of %d", curFrame, i->animData[j].nframes);
 
 			// byte *bg = grabBackgroundSlice(x, y, w, h);
@@ -1265,10 +1263,22 @@ void PelrockEngine::frames() {
 			// 			_screen->setPixel(xPos, yPos, frame[src_pos]);
 			// 	}
 			// }
-			if (_currentRoomAnims[i].animData[j].curFrame < _currentRoomAnims[i].animData[j].nframes - 1) {
-				_currentRoomAnims[i].animData[j].curFrame++;
+			if (_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curFrame < _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].nframes - 1) {
+				_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curFrame++;
 			} else {
-				_currentRoomAnims[i].animData[j].curFrame = 0;
+				if(_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curLoop < _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].loopCount - 1){
+					_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curFrame = 0;
+					_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curLoop++;
+				} else {
+					_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curFrame = 0;
+					_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curLoop = 0;
+					if(_currentRoomAnims[i].curAnimIndex < _currentRoomAnims[i].numAnims - 1){
+						_currentRoomAnims[i].curAnimIndex++;
+					}
+					else {
+						_currentRoomAnims[i].curAnimIndex = 0;
+					}
+				}
 			}
 		}
 
@@ -1291,6 +1301,7 @@ void PelrockEngine::frames() {
 			debug("CurAlfredFrame from walking is now %d", curAlfredFrame);
 		} else if (isAlfredTalking) {
 			drawAlfred(talkingAnimFrames[dirAlfred][curAlfredFrame]);
+
 			if (curAlfredFrame < talkingAnimLengths[dirAlfred] - 1) {
 				curAlfredFrame++;
 			} else {
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index a9b6c632e08..2552fe5f975 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -75,7 +75,8 @@ struct Anim {
 	int w;
 	int h;
 	int nframes;
-	int curFrame;
+	int curFrame = 0;
+	int curLoop = 0;
 	byte *animData;
 	byte loopCount;
 	byte speed;
@@ -102,7 +103,7 @@ struct AnimSet {
 	int h;//5
 	byte extra; //6
 	int numAnims; //8
-	int curAnimIndex;
+	int curAnimIndex = 0;
 	byte spriteType; //33
 	byte actionFlags;//34
 	bool isDisabled; //38


Commit: c3c6f36c951cff99ece66fbcae492cd06ac0bab6
    https://github.com/scummvm/scummvm/commit/c3c6f36c951cff99ece66fbcae492cd06ac0bab6
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:05+02:00

Commit Message:
PELROCK: Animation timing

Changed paths:
    engines/pelrock/chrono.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/chrono.h b/engines/pelrock/chrono.h
index 1a5c902bced..9dab952fadd 100644
--- a/engines/pelrock/chrono.h
+++ b/engines/pelrock/chrono.h
@@ -26,7 +26,7 @@
 namespace Pelrock {
 
 // const int kTickMs = 20;
-const int kTickMs = 200;
+const int kTickMs = 55;
 const int kHalfTickMultiplier = 2;
 
 class ChronoManager {
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 7caabf4dcab..b49a2e55ffd 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1252,34 +1252,40 @@ void PelrockEngine::frames() {
 
 			// byte *bg = grabBackgroundSlice(x, y, w, h);
 			// putBackgroundSlice(x, y, w, h, bg);
-			drawSpriteToBuffer(_compositeBuffer, 640, frame, _currentRoomAnims[i].x, _currentRoomAnims[i].y, _currentRoomAnims[i].w, _currentRoomAnims[i].h, 255);
-			// for (int y = 0; y < i->h; y++) {
-			// 	for (int x = 0; x < i->w; x++) {
-
-			// 		unsigned int src_pos = (y * i->w) + x;
-			// 		int xPos = i->x + x;
-			// 		int yPos = i->y + y;
-			// 		if (frame[src_pos] != 255 && xPos > 0 && yPos > 0 && xPos < 640 && yPos < 400)
-			// 			_screen->setPixel(xPos, yPos, frame[src_pos]);
-			// 	}
-			// }
-			if (_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curFrame < _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].nframes - 1) {
-				_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curFrame++;
-			} else {
-				if(_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curLoop < _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].loopCount - 1){
-					_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curFrame = 0;
-					_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curLoop++;
+
+				drawSpriteToBuffer(_compositeBuffer, 640, frame, _currentRoomAnims[i].x, _currentRoomAnims[i].y, _currentRoomAnims[i].w, _currentRoomAnims[i].h, 255);
+				// for (int y = 0; y < i->h; y++) {
+				// 	for (int x = 0; x < i->w; x++) {
+
+				// 		unsigned int src_pos = (y * i->w) + x;
+				// 		int xPos = i->x + x;
+				// 		int yPos = i->y + y;
+				// 		if (frame[src_pos] != 255 && xPos > 0 && yPos > 0 && xPos < 640 && yPos < 400)
+				// 			_screen->setPixel(xPos, yPos, frame[src_pos]);
+				// 	}
+				// }
+			if (_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].elpapsedFrames == _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].speed) {
+				_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].elpapsedFrames = 0;
+				if (_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curFrame < _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].nframes - 1) {
+					_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curFrame++;
 				} else {
-					_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curFrame = 0;
-					_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curLoop = 0;
-					if(_currentRoomAnims[i].curAnimIndex < _currentRoomAnims[i].numAnims - 1){
-						_currentRoomAnims[i].curAnimIndex++;
-					}
-					else {
-						_currentRoomAnims[i].curAnimIndex = 0;
+					if (_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curLoop < _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].loopCount - 1) {
+						_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curFrame = 0;
+						_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curLoop++;
+					} else {
+						_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curFrame = 0;
+						_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curLoop = 0;
+						if (_currentRoomAnims[i].curAnimIndex < _currentRoomAnims[i].numAnims - 1) {
+							_currentRoomAnims[i].curAnimIndex++;
+						} else {
+							_currentRoomAnims[i].curAnimIndex = 0;
+						}
 					}
 				}
 			}
+			else {
+				_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].elpapsedFrames++;
+			}
 		}
 
 		// if (_bgAlfred != nullptr) {
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 2552fe5f975..4f56d8d5393 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -80,6 +80,7 @@ struct Anim {
 	byte *animData;
 	byte loopCount;
 	byte speed;
+	byte elpapsedFrames = 0;
 };
 
 struct Exit {


Commit: 7e0d3a6bad0427f0a5fee894fb0e7695f074c683
    https://github.com/scummvm/scummvm/commit/7e0d3a6bad0427f0a5fee894fb0e7695f074c683
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:05+02:00

Commit Message:
PELROCK: Walk target calculation

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index b49a2e55ffd..0b1990ac05e 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -845,70 +845,6 @@ Common::Array<ConversationNode> PelrockEngine::loadConversations(Common::File *r
 	Common::Array<ConversationElement> elements = parseConversationElements(data, conversation_size);
 	Common::Array<ConversationNode> roots = buildTreeStructure(elements);
 	return roots;
-
-	// uint32_t i = 0;
-	// int lineNum = 1;
-
-	// while (i < conversation_size) {
-	//     uint32_t lineStart = i;
-	//     ConversationLine line;
-	//     line.offset = startPos + lineStart;
-	//     line.speaker = 0;
-
-	//     // Read until we hit an end marker
-	//     while (i < conversation_size) {
-	//         byte b = data[i];
-
-	//         if (b == 0x08) { // SPEAKER
-	//             i++;
-	//             if (i < conversation_size) {
-	//                 line.speaker = data[i];
-	//                 line.controlBytes.push_back(Common::String::format("SPEAKER(0x%02X)", line.speaker));
-	//                 i++;
-	//             }
-	//             continue;
-	//         } else if (isControlByte(b)) {
-	//             line.controlBytes.push_back(Common::String::format("%s(0x%02X)",
-	//                                                                 getControlName(b).c_str(), b));
-	//             i++;
-
-	//             // Check for end markers
-	//             if (isTerminalByte(b)) {
-	//                 break;
-	//             }
-	//         } else {
-	//             // Regular text character
-	//             if (b != 0x00) {
-	//                 line.text += decodeByte(b);
-	//             }
-	//             i++;
-	//         }
-	//     }
-
-	//     // Store raw bytes for this line
-	//     for (uint32_t j = lineStart; j < i && j < conversation_size; j++) {
-	//         line.rawBytes.push_back(data[j]);
-	//     }
-
-	//     // Only add line if it has content
-	//     if (!line.text.empty() || line.controlBytes.size() > 0) {
-	//         // debug("Line %d (offset %d): %s", lineNum, line.offset, line.text.c_str());
-	//         if (line.controlBytes.size() > 0) {
-	//             Common::String controls;
-	//             for (uint k = 0; k < line.controlBytes.size(); k++) {
-	//                 if (k > 0) controls += ", ";
-	//                 controls += line.controlBytes[k];
-	//             }
-	//             // debug("  Controls: %s", controls.c_str());
-	//         }
-	//         conversations.push_back(line);
-	//         lineNum++;
-	//     }
-	// }
-
-	// delete[] data;
-	// debug("Loaded %d conversation lines", conversations.size());
-	// return conversations;
 }
 
 void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
@@ -1248,22 +1184,9 @@ void PelrockEngine::frames() {
 			int curFrame = _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curFrame;
 			byte *frame = new byte[frameSize];
 			Common::copy(_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].animData + (curFrame * _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].h * _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].w), _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].animData + (curFrame * _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].h * _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].w) + (frameSize), frame);
-			// debug("Current frame %d of %d", curFrame, i->animData[j].nframes);
-
-			// byte *bg = grabBackgroundSlice(x, y, w, h);
-			// putBackgroundSlice(x, y, w, h, bg);
-
-				drawSpriteToBuffer(_compositeBuffer, 640, frame, _currentRoomAnims[i].x, _currentRoomAnims[i].y, _currentRoomAnims[i].w, _currentRoomAnims[i].h, 255);
-				// for (int y = 0; y < i->h; y++) {
-				// 	for (int x = 0; x < i->w; x++) {
-
-				// 		unsigned int src_pos = (y * i->w) + x;
-				// 		int xPos = i->x + x;
-				// 		int yPos = i->y + y;
-				// 		if (frame[src_pos] != 255 && xPos > 0 && yPos > 0 && xPos < 640 && yPos < 400)
-				// 			_screen->setPixel(xPos, yPos, frame[src_pos]);
-				// 	}
-				// }
+
+			drawSpriteToBuffer(_compositeBuffer, 640, frame, _currentRoomAnims[i].x, _currentRoomAnims[i].y, _currentRoomAnims[i].w, _currentRoomAnims[i].h, 255);
+
 			if (_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].elpapsedFrames == _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].speed) {
 				_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].elpapsedFrames = 0;
 				if (_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curFrame < _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].nframes - 1) {
@@ -1288,13 +1211,6 @@ void PelrockEngine::frames() {
 			}
 		}
 
-		// if (_bgAlfred != nullptr) {
-		// 	putBackgroundSlice(xAlfred, yAlfred, kAlfredFrameWidth, kAlfredFrameHeight, _bgAlfred);
-		// 	delete[] _bgAlfred;
-		// 	_bgAlfred = nullptr;
-		// }
-		// _bgAlfred = grabBackgroundSlice(xAlfred, yAlfred, kAlfredFrameWidth, kAlfredFrameHeight);
-
 		if (isAlfredWalking) {
 			debug("Drawing walking frame %d for direction %d", curAlfredFrame, dirAlfred);
 			drawAlfred(walkingAnimFrames[dirAlfred][curAlfredFrame]);
@@ -1337,42 +1253,37 @@ void PelrockEngine::frames() {
 			} else
 				_currentPopupFrame = 0;
 		}
-		// int walkboxCount = 0;
-		// for (Common::List<WalkBox>::iterator i = _currentRoomWalkboxes.begin(); i != _currentRoomWalkboxes.end(); i++) {
-		// 	// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
-		// 	_screen->drawLine(i->x, i->y, i->x + i->w, i->y, 0 + walkboxCount);
-		// 	_screen->drawLine(i->x, i->y + i->h, i->x + i->w, i->y + i->h, 0 + walkboxCount);
-		// 	_screen->drawLine(i->x, i->y, i->x, i->y + i->h, 0 + walkboxCount);
-		// 	_screen->drawLine(i->x + i->w, i->y, i->x + i->w, i->y + i->h, 0 + walkboxCount);
-		// 	walkboxCount++;
-		// }
+
+
+
 		memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
+
+				if(_curWalkTarget.x < 640 && _curWalkTarget.y < 400 && _curWalkTarget.x >=0 && _curWalkTarget.y >=0) {
+			_screen->setPixel(_curWalkTarget.x, _curWalkTarget.y, 100);
+			if(_curWalkTarget.x - 1 > 0 && _curWalkTarget.y - 1 > 0) {
+				_screen->setPixel(_curWalkTarget.x - 1, _curWalkTarget.y - 1, 100);
+			}
+			if(_curWalkTarget.x - 1 > 0 && _curWalkTarget.y + 1 < 400)
+				_screen->setPixel(_curWalkTarget.x - 1, _curWalkTarget.y + 1, 100);
+			if(_curWalkTarget.x + 1 < 640 && _curWalkTarget.y - 1 > 0)
+				_screen->setPixel(_curWalkTarget.x + 1, _curWalkTarget.y - 1, 100);
+			if(_curWalkTarget.x + 1 < 640 && _curWalkTarget.y + 1 < 400)
+				_screen->setPixel(_curWalkTarget.x + 1, _curWalkTarget.y + 1, 100);
+		}
 		_screen->markAllDirty();
 		// _screen->update();
 	}
 }
 
 void PelrockEngine::drawAlfred(byte *buf) {
-	// for (uint32_t y = 0; y < kAlfredFrameHeight; y++) {
-	// 	for (uint32_t x = 0; x < kAlfredFrameWidth; x++) {
-	// 		unsigned int src_pos = (y * kAlfredFrameWidth) + x;
-	// 		// debug("Xpos = %d, yPos=%d", x + xAlfred, y + yAlfred);
-	// 		if (buf[src_pos] != 255 && x + xAlfred >= 0 && y + yAlfred >= 0 && x + xAlfred < 640 && y + yAlfred < 400)
-	// 			_screen->setPixel(x + xAlfred, y + yAlfred, buf[src_pos]);
-	// 	}
-	// }
+
 	drawSpriteToBuffer(_compositeBuffer, 640, buf, xAlfred, yAlfred, kAlfredFrameWidth, kAlfredFrameHeight, 255);
 }
 
 void PelrockEngine::checkLongMouseClick(int x, int y) {
 	int hotspotIndex = isHotspotUnder(mouseX, mouseY);
 	if (hotspotIndex != -1) {
-		// _popupX = hotspot->x;
-		// _popupY = hotspot->y;
-		// if (_bgPopupBalloon != nullptr) {
-		// 	putBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight, _bgPopupBalloon);
-		// 	delete[] _bgPopupBalloon;
-		// }
+
 		_popupX = x - kBalloonWidth / 2;
 		if (_popupX < 0)
 			_popupX = 0;
@@ -1388,7 +1299,6 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 		_currentPopupFrame = 0;
 		_currentHotspot = &_currentRoomHotspots[hotspotIndex];
 		debug("Current hotspot type: %d", _currentHotspot->type);
-		// _bgPopupBalloon = grabBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight);
 	}
 }
 
@@ -1420,9 +1330,6 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	drawSpriteToBuffer(_compositeBuffer, 640, _popUpBalloon + (curFrame * kBalloonHeight * kBalloonWidth), posx, posy, kBalloonWidth, kBalloonHeight, 255);
 	Common::List<VerbIcons> availableActions = populateActionsMenu(_currentHotspot);
 
-	// for (Common::List<VerbIcons>::iterator i = availableActions.begin(); i != availableActions.end(); i++) {
-	// 	debug("Verb icon to show: %d", *i);
-	// }
 
 	drawSpriteToBuffer(_compositeBuffer, 640, _verbIcons[LOOK], posx + 20, posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
 	for (Common::List<VerbIcons>::iterator i = availableActions.begin(); i != availableActions.end(); i++) {
@@ -1435,18 +1342,32 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	}
 }
 
+void PelrockEngine::walkTo(int x, int y) {
+	isAlfredWalking = true;
+	curAlfredFrame = 0;
+
+	// if (x > xAlfred) {
+	// 	dirAlfred = RIGHT;
+	// } else if (x < xAlfred) {
+	// 	dirAlfred = LEFT;
+	// } else if (y < yAlfred) {
+	// 	dirAlfred = UP;
+	// } else if (y > yAlfred) {
+	// 	dirAlfred = DOWN;
+	// }
+	debug("Setting Alfred to walk towards (%d, %d) from (%d, %d) in direction %d", x, y, xAlfred, yAlfred, dirAlfred);
+}
+
 void PelrockEngine::checkMouseClick(int x, int y) {
 
 	_displayPopup = false;
 	_currentHotspot = nullptr;
-	// if (_bgPopupBalloon != nullptr) {
-	// 	putBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight, _bgPopupBalloon);
-	// 	delete[] _bgPopupBalloon;
-	// 	_bgPopupBalloon = nullptr;
-	// }
 
-	Common::Point walkTarget = calculateWalkTarget(mouseX, mouseY);
 
+	Common::Point walkTarget = calculateWalkTarget(mouseX, mouseY);
+	walkTo(walkTarget.x, walkTarget.y);
+	_curWalkTarget = walkTarget;
+	debug("Calculated walk target at (%d, %d)", walkTarget.x, walkTarget.y);
 	Exit *exit = isExitAtPoint(walkTarget.x, walkTarget.y);
 
 	if (exit != nullptr) {
@@ -1459,9 +1380,9 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 	if (hotspotIndex != -1) {
 		talk();
 		debug("Hotspot clicked: %d", _currentRoomHotspots[hotspotIndex].extra);
-		// showDescription(_currentRoomDescriptions[hotspotIndex].text.c_str(), xAlfred, yAlfred, 13);
-		// changeCursor(HOTSPOT);
 	}
+
+
 }
 
 void PelrockEngine::changeCursor(Cursor cursor) {
@@ -1485,11 +1406,7 @@ void PelrockEngine::checkMouseHover() {
 	if (hotspotIndex != -1) {
 		isSomethingUnder = true;
 	}
-	// Exit *exit = isExitUnder(mouseX, mouseY);
-	// if (exit != nullptr) {
-	// 	isSomethingUnder = true;
-	// 	changeCursor(EXIT);
-	// }
+
 	if (isSomethingUnder && exitDetected) {
 		changeCursor(COMBINATION);
 	} else if (isSomethingUnder) {
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 2cb9fe8393b..89f2475ed6b 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -75,6 +75,8 @@ private:
 	Common::Array<ConversationNode> buildTreeStructure(const Common::Array<ConversationElement> &elements);
 	Common::Array<ConversationNode> loadConversations(Common::File *roomFile, int roomOffset, uint32_t startPos);
 
+	void walkTo(int x, int y);
+
 	void talk();
 	Common::String getControlName(byte b);
 	void loadRoomMetadata(Common::File *roomFile, int roomOffset);
@@ -152,6 +154,10 @@ private:
 	SmallFont *_smallFont = nullptr;
 	LargeFont *_largeFont = nullptr;
 
+
+	Common::Point _curWalkTarget;
+
+
 	bool shouldPlayIntro = false;
 	GameState stateGame = GAME;
 	bool gameInitialized = false;


Commit: 1623209a0bcb388f995027d86385f9e3ee22383b
    https://github.com/scummvm/scummvm/commit/1623209a0bcb388f995027d86385f9e3ee22383b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:05+02:00

Commit Message:
PELROCK: Quick and dirty walking algorithm

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 0b1990ac05e..1af81195bf9 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -84,6 +84,14 @@ Common::String PelrockEngine::getGameId() const {
 	return _gameDescription->gameId;
 }
 
+void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byte color) {
+	// debug("Drawing rect at (%d,%d) w=%d h=%d color=%d", x, y, w, h, color);
+	surface->drawLine(x, y, x + w, y, color);
+	surface->drawLine(x, y + h, x + w, y + h, color);
+	surface->drawLine(x, y, x, y + h, color);
+	surface->drawLine(x + w, y, x + w, y + h, color);
+}
+
 Common::Error PelrockEngine::run() {
 	// Initialize 320x200 paletted graphics mode
 	initGraphics(640, 400);
@@ -358,7 +366,7 @@ Common::Array<AnimSet> PelrockEngine::loadRoomAnimations(Common::File *roomFile,
 	return anims;
 }
 
-Common::List<WalkBox> PelrockEngine::loadWalkboxes(Common::File *roomFile, int roomOffset) {
+Common::Array<WalkBox> PelrockEngine::loadWalkboxes(Common::File *roomFile, int roomOffset) {
 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
 	roomFile->seek(pair10_offset_pos, SEEK_SET);
 	// roomFile->skip(4);
@@ -370,7 +378,7 @@ Common::List<WalkBox> PelrockEngine::loadWalkboxes(Common::File *roomFile, int r
 	byte walkbox_count = roomFile->readByte();
 	debug("Walkbox count: %d", walkbox_count);
 	uint32_t walkbox_offset = pair10_data_offset + 0x218;
-	Common::List<WalkBox> walkboxes;
+	Common::Array<WalkBox> walkboxes;
 	for (int i = 0; i < walkbox_count; i++) {
 		uint32_t box_offset = walkbox_offset + i * 9;
 		roomFile->seek(box_offset, SEEK_SET);
@@ -882,7 +890,7 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	Common::Array<HotSpot> staticHotspots = loadHotspots(roomFile, roomOffset);
 	Common::List<Exit> exits = loadExits(roomFile, roomOffset);
 
-	Common::List<WalkBox> walkboxes = loadWalkboxes(roomFile, roomOffset);
+	Common::Array<WalkBox> walkboxes = loadWalkboxes(roomFile, roomOffset);
 
 	debug("total descriptions = %d, anims = %d, hotspots = %d", descriptions.size(), anims.size(), staticHotspots.size());
 	for (int i = 0; i < staticHotspots.size(); i++) {
@@ -901,10 +909,7 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
 		HotSpot hotspot = _currentRoomHotspots[i];
 		// debug("Hotspot %d: x=%d y=%d w=%d h=%d type=%d enabled? %d extra=%d, desc=%s", i, hotspot.x, hotspot.y, hotspot.w, hotspot.h, hotspot.type, hotspot.isEnabled, hotspot.extra, _currentRoomDescriptions[i].text.c_str());
-		_screen->drawLine(hotspot.x, hotspot.y, hotspot.x + hotspot.w, hotspot.y, 200 + i);
-		_screen->drawLine(hotspot.x, hotspot.y + hotspot.h, hotspot.x + hotspot.w, hotspot.y + hotspot.h, 200 + i);
-		_screen->drawLine(hotspot.x, hotspot.y, hotspot.x, hotspot.y + hotspot.h, 200 + i);
-		_screen->drawLine(hotspot.x + hotspot.w, hotspot.y, hotspot.x + hotspot.w, hotspot.y + hotspot.h, 200 + i);
+		// drawRect(_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 200 + i);
 	}
 
 	for (Common::List<Exit>::iterator i = _currentRoomExits.begin(); i != _currentRoomExits.end(); i++) {
@@ -1171,7 +1176,7 @@ void PelrockEngine::frames() {
 
 		memcpy(_compositeBuffer, _currentBackground, 640 * 400);
 
-		debug("Game tick!");
+		// debug("Game tick!");
 		for (int i = 0; i < _currentRoomAnims.size(); i++) {
 			// debug("Processing animation set %d, numAnims %d", num, i->numAnims);
 
@@ -1205,14 +1210,66 @@ void PelrockEngine::frames() {
 						}
 					}
 				}
-			}
-			else {
+			} else {
 				_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].elpapsedFrames++;
 			}
 		}
 
 		if (isAlfredWalking) {
-			debug("Drawing walking frame %d for direction %d", curAlfredFrame, dirAlfred);
+			debug("Alfred is walking, current step %d of %d", _current_step, _currentContext.movement_count);
+			MovementStep step = _currentContext.movement_buffer[_current_step];
+			debug("Alfred step: distance_x=%d, distance_y=%d", step.distance_x, step.distance_y);
+
+			if (step.distance_x > 0) {
+				if (step.flags & MOVE_RIGHT) {
+					dirAlfred = 0;
+					xAlfred += MIN((uint16_t)6, step.distance_x);
+				}
+				if (step.flags & MOVE_LEFT) {
+					dirAlfred = 1;
+					xAlfred -= MIN((uint16_t)6, step.distance_x);
+				}
+			}
+			if (step.distance_y > 0) {
+				if (step.flags & MOVE_DOWN) {
+					dirAlfred = 2;
+					yAlfred += MIN((uint16_t)6, step.distance_y);
+				}
+				if (step.flags & MOVE_UP) {
+					dirAlfred = 3;
+					yAlfred -= MIN((uint16_t)6, step.distance_y);
+				}
+			}
+
+			if (step.distance_x > 0)
+				step.distance_x -= MIN((uint16_t)6, step.distance_x);
+
+			if (step.distance_y > 0)
+				step.distance_y -= MIN((uint16_t)6, step.distance_y);
+			debug("Alfred position after step: x=%d, y=%d, step distance_x=%d, step distance_y=%d", xAlfred, yAlfred, step.distance_x, step.distance_y);
+			if (step.distance_x <= 0 && step.distance_y <= 0) {
+				debug("Alfred completed step %d", _current_step);
+				_current_step++;
+				if (_current_step >= _currentContext.movement_count) {
+					debug("Alfred reached his walk target.");
+					_current_step = 0;
+					isAlfredWalking = false;
+				}
+			} else {
+				_currentContext.movement_buffer[_current_step] = step;
+			}
+			// _current_step++;
+			// if(_current_step >= _currentContext.movement_count) {
+			// 	debug("Alfred reached his walk target.");
+			// 	_current_step = 0;
+			// 	isAlfredWalking = false;
+			// }
+			// if (step->flags & MOVE_RIGHT) printf("RIGHT ");
+			// if (step->flags & MOVE_LEFT) printf("LEFT ");
+			// if (step->flags & MOVE_DOWN) printf("DOWN ");
+			// if (step->flags & MOVE_UP) printf("UP ");
+
+			// debug("Drawing walking frame %d for direction %d", curAlfredFrame, dirAlfred);
 			drawAlfred(walkingAnimFrames[dirAlfred][curAlfredFrame]);
 
 			if (curAlfredFrame < walkingAnimLengths[dirAlfred] - 1) {
@@ -1220,7 +1277,7 @@ void PelrockEngine::frames() {
 			} else {
 				curAlfredFrame = 0;
 			}
-			debug("CurAlfredFrame from walking is now %d", curAlfredFrame);
+			// debug("CurAlfredFrame from walking is now %d", curAlfredFrame);
 		} else if (isAlfredTalking) {
 			drawAlfred(talkingAnimFrames[dirAlfred][curAlfredFrame]);
 
@@ -1254,21 +1311,31 @@ void PelrockEngine::frames() {
 				_currentPopupFrame = 0;
 		}
 
-
-
 		memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
-
-				if(_curWalkTarget.x < 640 && _curWalkTarget.y < 400 && _curWalkTarget.x >=0 && _curWalkTarget.y >=0) {
+		// debug("Drawing walkboxes..., %d, _currentRoomWalkboxes.size()=%d",  _currentRoomWalkboxes.size(), _currentRoomWalkboxes.size());
+		for (int i = 0; i < _currentRoomWalkboxes.size(); i++) {
+			// debug("Drawing walkbox %d", i);
+			WalkBox box = _currentRoomWalkboxes[i];
+			drawRect(_screen, box.x, box.y, box.w, box.h, 150 + i);
+		}
+		if (_curWalkTarget.x < 640 && _curWalkTarget.y < 400 && _curWalkTarget.x >= 0 && _curWalkTarget.y >= 0) {
 			_screen->setPixel(_curWalkTarget.x, _curWalkTarget.y, 100);
-			if(_curWalkTarget.x - 1 > 0 && _curWalkTarget.y - 1 > 0) {
+			if (_curWalkTarget.x - 1 > 0 && _curWalkTarget.y - 1 > 0)
 				_screen->setPixel(_curWalkTarget.x - 1, _curWalkTarget.y - 1, 100);
-			}
-			if(_curWalkTarget.x - 1 > 0 && _curWalkTarget.y + 1 < 400)
+			if (_curWalkTarget.x - 1 > 0 && _curWalkTarget.y + 1 < 400)
 				_screen->setPixel(_curWalkTarget.x - 1, _curWalkTarget.y + 1, 100);
-			if(_curWalkTarget.x + 1 < 640 && _curWalkTarget.y - 1 > 0)
+			if (_curWalkTarget.x + 1 < 640 && _curWalkTarget.y - 1 > 0)
 				_screen->setPixel(_curWalkTarget.x + 1, _curWalkTarget.y - 1, 100);
-			if(_curWalkTarget.x + 1 < 640 && _curWalkTarget.y + 1 < 400)
+			if (_curWalkTarget.x + 1 < 640 && _curWalkTarget.y + 1 < 400)
 				_screen->setPixel(_curWalkTarget.x + 1, _curWalkTarget.y + 1, 100);
+			if (_curWalkTarget.x - 2 > 0)
+				_screen->setPixel(_curWalkTarget.x - 2, _curWalkTarget.y, 100);
+			if (_curWalkTarget.x + 2 < 640)
+				_screen->setPixel(_curWalkTarget.x + 2, _curWalkTarget.y, 100);
+			if (_curWalkTarget.y - 2 > 0)
+				_screen->setPixel(_curWalkTarget.x, _curWalkTarget.y - 2, 100);
+			if (_curWalkTarget.y + 2 < 400)
+				_screen->setPixel(_curWalkTarget.x, _curWalkTarget.y + 2, 100);
 		}
 		_screen->markAllDirty();
 		// _screen->update();
@@ -1277,7 +1344,7 @@ void PelrockEngine::frames() {
 
 void PelrockEngine::drawAlfred(byte *buf) {
 
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, xAlfred, yAlfred, kAlfredFrameWidth, kAlfredFrameHeight, 255);
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, xAlfred, yAlfred - kAlfredFrameHeight, kAlfredFrameWidth, kAlfredFrameHeight, 255);
 }
 
 void PelrockEngine::checkLongMouseClick(int x, int y) {
@@ -1330,7 +1397,6 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	drawSpriteToBuffer(_compositeBuffer, 640, _popUpBalloon + (curFrame * kBalloonHeight * kBalloonWidth), posx, posy, kBalloonWidth, kBalloonHeight, 255);
 	Common::List<VerbIcons> availableActions = populateActionsMenu(_currentHotspot);
 
-
 	drawSpriteToBuffer(_compositeBuffer, 640, _verbIcons[LOOK], posx + 20, posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
 	for (Common::List<VerbIcons>::iterator i = availableActions.begin(); i != availableActions.end(); i++) {
 		VerbIcons verb = *i;
@@ -1346,6 +1412,39 @@ void PelrockEngine::walkTo(int x, int y) {
 	isAlfredWalking = true;
 	curAlfredFrame = 0;
 
+	PathContext context = {NULL, NULL, NULL, 0, 0, 0};
+
+	pathFind(x, y, &context);
+	debug("\nPath Information:\n");
+	debug("================\n");
+
+	debug("Walkbox path (%d boxes): ", context.path_length);
+	for (int i = 0; i < context.path_length && context.path_buffer[i] != PATH_END; i++) {
+		debug("%d ", context.path_buffer[i]);
+	}
+
+	debug("Movement steps (%d steps):\n", context.movement_count);
+	for (int i = 0; i < context.movement_count; i++) {
+		MovementStep *step = &context.movement_buffer[i];
+		debug("  Step %d: ", i);
+
+		if (step->flags & MOVE_RIGHT)
+			debug("RIGHT ");
+		if (step->flags & MOVE_LEFT)
+			debug("LEFT ");
+		if (step->flags & MOVE_DOWN)
+			debug("DOWN ");
+		if (step->flags & MOVE_UP)
+			debug("UP ");
+
+		debug("(dx=%d, dy=%d)\n", step->distance_x, step->distance_y);
+	}
+
+	debug("\nCompressed path (%d bytes): ", context.compressed_length);
+	for (int i = 0; i < context.compressed_length; i++) {
+		debug("%02X ", context.compressed_path[i]);
+	}
+
 	// if (x > xAlfred) {
 	// 	dirAlfred = RIGHT;
 	// } else if (x < xAlfred) {
@@ -1356,6 +1455,298 @@ void PelrockEngine::walkTo(int x, int y) {
 	// 	dirAlfred = DOWN;
 	// }
 	debug("Setting Alfred to walk towards (%d, %d) from (%d, %d) in direction %d", x, y, xAlfred, yAlfred, dirAlfred);
+	_currentContext = context;
+	debug("Path find complete, movement count: %d", _currentContext.movement_count);
+}
+
+bool PelrockEngine::pathFind(int x, int y, PathContext *context) {
+
+	if (context->path_buffer == NULL) {
+		context->path_buffer = (uint8_t *)malloc(MAX_PATH_LENGTH);
+	}
+	if (context->movement_buffer == NULL) {
+		context->movement_buffer = (MovementStep *)malloc(MAX_MOVEMENT_STEPS * sizeof(MovementStep));
+	}
+	// if (context->compressed_path == NULL) {
+	//     context->compressed_path = (uint8_t*)malloc(MAX_COMPRESSED_PATH);
+	// }
+
+	int startX = xAlfred;
+	int startY = yAlfred;
+	Common::Point target = calculateWalkTarget(x, y);
+	x = target.x;
+	y = target.y;
+	debug("Startx= %d, starty= %d, destx= %d, desty= %d", startX, startY, x, y);
+
+	uint8_t start_box = find_walkbox_for_point(startX, startY);
+	uint8_t dest_box = find_walkbox_for_point(x, y);
+
+	debug("Pathfinding from (%d, %d) in box %d to (%d, %d) in box %d\n",
+		  startX, startY, start_box,
+		  x, y, dest_box);
+	// Check if both points are in valid walkboxes
+	if (start_box == 0xFF || dest_box == 0xFF) {
+		debug("Error: Start or destination not in any walkbox\n");
+		return false;
+	}
+	// Special case: same walkbox
+	if (start_box == dest_box) {
+		// Generate direct movement
+		MovementStep direct_step;
+		direct_step.flags = 0;
+		if (startX < x) {
+			direct_step.distance_x = x - startX;
+			direct_step.flags |= MOVE_RIGHT;
+		} else {
+			direct_step.distance_x = startX - x;
+			direct_step.flags |= MOVE_LEFT;
+		}
+
+		if (startY < y) {
+			direct_step.distance_y = y - startY;
+			direct_step.flags |= MOVE_DOWN;
+		} else {
+			direct_step.distance_y = startY - y;
+			direct_step.flags |= MOVE_UP;
+		}
+
+		context->movement_buffer[0] = direct_step;
+		context->movement_count = 1;
+	} else {
+		// Build walkbox path
+		context->path_length = build_walkbox_path(start_box, dest_box,
+												  context->path_buffer);
+
+		if (context->path_length == 0) {
+			debug("Error: No path found\n");
+			return false;
+		}
+
+		// Generate movement steps
+		context->movement_count = generate_movement_steps(
+			context->path_buffer,
+			context->path_length,
+			startX, startY,
+			x, y,
+			context->movement_buffer);
+	}
+	return true;
+}
+
+/**
+ * Calculate movement needed to reach a target within a walkbox
+ */
+void calculate_movement_to_target(uint16_t current_x, uint16_t current_y,
+								  uint16_t target_x, uint16_t target_y,
+								  WalkBox *box,
+								  MovementStep *step) {
+	step->flags = 0;
+	step->distance_x = 0;
+	step->distance_y = 0;
+
+	// Calculate horizontal movement
+	if (current_x < box->x) {
+		// Need to move right to enter walkbox
+		step->distance_x = box->x - current_x;
+		step->flags |= MOVE_RIGHT;
+	} else if (current_x > box->x + box->w) {
+		// Need to move left to enter walkbox
+		step->distance_x = current_x - (box->x + box->w);
+		step->flags |= MOVE_LEFT;
+	}
+
+	// Calculate vertical movement
+	if (current_y < box->y) {
+		// Need to move down to enter walkbox
+		step->distance_y = box->y - current_y;
+		step->flags |= MOVE_DOWN;
+	} else if (current_y > box->y + box->h) {
+		// Need to move up to enter walkbox
+		step->distance_y = current_y - (box->y + box->h);
+		step->flags |= MOVE_UP;
+	}
+}
+
+/**
+ * Generate movement steps from walkbox path
+ * Returns: number of movement steps generated
+ */
+uint16_t PelrockEngine::generate_movement_steps(uint8_t *path_buffer,
+												uint16_t path_length,
+												uint16_t start_x, uint16_t start_y,
+												uint16_t dest_x, uint16_t dest_y,
+												MovementStep *movement_buffer) {
+	uint16_t current_x = start_x;
+	uint16_t current_y = start_y;
+	uint16_t movement_index = 0;
+
+	// Generate movements for each walkbox in path
+	for (uint16_t i = 0; i < path_length && path_buffer[i] != PATH_END; i++) {
+		uint8_t box_index = path_buffer[i];
+		WalkBox *box = &_currentRoomWalkboxes[box_index];
+
+		MovementStep step;
+		calculate_movement_to_target(current_x, current_y,
+									 dest_x, dest_y,
+									 box, &step);
+
+		if (step.distance_x > 0 || step.distance_y > 0) {
+			movement_buffer[movement_index++] = step;
+
+			// Update current position
+			if (step.flags & MOVE_RIGHT) {
+				current_x = box->x;
+			} else if (step.flags & MOVE_LEFT) {
+				current_x = box->x + box->w;
+			}
+
+			if (step.flags & MOVE_DOWN) {
+				current_y = box->y;
+			} else if (step.flags & MOVE_UP) {
+				current_y = box->y + box->h;
+			}
+		}
+	}
+
+	// Final movement to exact destination
+	MovementStep final_step;
+	final_step.flags = 0;
+
+	if (current_x < dest_x) {
+		final_step.distance_x = dest_x - current_x;
+		final_step.flags |= MOVE_RIGHT;
+	} else if (current_x > dest_x) {
+		final_step.distance_x = current_x - dest_x;
+		final_step.flags |= MOVE_LEFT;
+	} else {
+		final_step.distance_x = 0;
+	}
+
+	if (current_y < dest_y) {
+		final_step.distance_y = dest_y - current_y;
+		final_step.flags |= MOVE_DOWN;
+	} else if (current_y > dest_y) {
+		final_step.distance_y = current_y - dest_y;
+		final_step.flags |= MOVE_UP;
+	} else {
+		final_step.distance_y = 0;
+	}
+
+	if (final_step.distance_x > 0 || final_step.distance_y > 0) {
+		movement_buffer[movement_index++] = final_step;
+	}
+
+	return movement_index;
+}
+
+uint16_t PelrockEngine::build_walkbox_path(
+	uint8_t start_box,
+	uint8_t dest_box,
+	uint8_t *path_buffer) {
+	uint16_t path_index = 0;
+	uint8_t current_box = start_box;
+
+	// Initialize path with start walkbox
+	path_buffer[path_index++] = start_box;
+
+	// Clear visited flags
+	clear_visited_flags();
+
+	// Breadth-first search through walkboxes
+	while (current_box != dest_box && path_index < MAX_PATH_LENGTH - 1) {
+		uint8_t next_box = get_adjacent_walkbox(current_box);
+
+		if (next_box == 0xFF) {
+			// Dead end - backtrack
+			if (path_index > 1) {
+				path_index--;
+				current_box = path_buffer[path_index - 1];
+			} else {
+				// No path exists
+				return 0;
+			}
+		} else if (next_box == dest_box) {
+			// Found destination
+			path_buffer[path_index++] = dest_box;
+			break;
+		} else {
+			// Continue searching
+			path_buffer[path_index++] = next_box;
+			current_box = next_box;
+		}
+	}
+
+	// Terminate path
+	path_buffer[path_index] = PATH_END;
+
+	return path_index;
+}
+
+void PelrockEngine::clear_visited_flags() {
+	for (int i = 0; i < _currentRoomWalkboxes.size(); i++) {
+		_currentRoomWalkboxes[i].flags = 0;
+	}
+}
+
+/**
+ * Check if two walkboxes overlap or touch (are adjacent)
+ */
+bool walkboxes_adjacent(WalkBox *box1, WalkBox *box2) {
+	uint16_t box1_x_max = box1->x + box1->w;
+	uint16_t box1_y_max = box1->y + box1->h;
+	uint16_t box2_x_max = box2->x + box2->w;
+	uint16_t box2_y_max = box2->y + box2->h;
+
+	// Check if X ranges overlap
+	bool x_overlap = (box1->x <= box2_x_max) && (box2->x <= box1_x_max);
+
+	// Check if Y ranges overlap
+	bool y_overlap = (box1->y <= box2_y_max) && (box2->y <= box1_y_max);
+
+	return x_overlap && y_overlap;
+}
+
+uint8_t PelrockEngine::get_adjacent_walkbox(uint8_t current_box_index) {
+	WalkBox *current_box = &_currentRoomWalkboxes[current_box_index];
+
+	// Mark current walkbox as visited
+	current_box->flags = 0x01;
+
+	// Search for adjacent unvisited walkbox
+	for (uint8_t i = 0; i < _currentRoomWalkboxes.size(); i++) {
+		// Skip current walkbox
+		if (i == current_box_index) {
+			continue;
+		}
+
+		// Skip already visited walkboxes
+		if (_currentRoomWalkboxes[i].flags == 0x01) {
+			continue;
+		}
+
+		// Check if walkboxes are adjacent
+		if (walkboxes_adjacent(current_box, &_currentRoomWalkboxes[i])) {
+			return i;
+		}
+	}
+
+	return 0xFF; // No adjacent walkbox found
+}
+
+bool PelrockEngine::point_in_walkbox(WalkBox *box, uint16_t x, uint16_t y) {
+	return (x >= box->x &&
+			x <= box->x + box->w &&
+			y >= box->y &&
+			y <= box->y + box->h);
+}
+
+uint8_t PelrockEngine::find_walkbox_for_point(uint16_t x, uint16_t y) {
+	for (uint8_t i = 0; i < _currentRoomWalkboxes.size(); i++) {
+		if (point_in_walkbox(&_currentRoomWalkboxes[i], x, y)) {
+			return i;
+		}
+	}
+	return 0xFF; // Not found
 }
 
 void PelrockEngine::checkMouseClick(int x, int y) {
@@ -1363,17 +1754,17 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 	_displayPopup = false;
 	_currentHotspot = nullptr;
 
-
 	Common::Point walkTarget = calculateWalkTarget(mouseX, mouseY);
-	walkTo(walkTarget.x, walkTarget.y);
 	_curWalkTarget = walkTarget;
 	debug("Calculated walk target at (%d, %d)", walkTarget.x, walkTarget.y);
 	Exit *exit = isExitAtPoint(walkTarget.x, walkTarget.y);
 
 	if (exit != nullptr) {
 		xAlfred = exit->targetX;
-		yAlfred = exit->targetY - kAlfredFrameHeight;
+		yAlfred = exit->targetY;
 		setScreen(exit->targetRoom, exit->dir);
+	} else {
+		walkTo(walkTarget.x, walkTarget.y);
 	}
 
 	int hotspotIndex = isHotspotUnder(mouseX, mouseY);
@@ -1381,8 +1772,6 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 		talk();
 		debug("Hotspot clicked: %d", _currentRoomHotspots[hotspotIndex].extra);
 	}
-
-
 }
 
 void PelrockEngine::changeCursor(Cursor cursor) {
@@ -1430,26 +1819,27 @@ Common::Point PelrockEngine::calculateWalkTarget(int mouseX, int mouseY) {
 	uint32 minDistance = 0xFFFFFFFF;
 	Common::Point bestTarget(sourceX, sourceY);
 
-	for (Common::List<WalkBox>::iterator it = _currentRoomWalkboxes.begin();
-		 it != _currentRoomWalkboxes.end(); ++it) {
+	// for (Common::List<WalkBox>::iterator it = _currentRoomWalkboxes.begin();
+	//  it != _currentRoomWalkboxes.end(); ++it) {
+	for (size_t i = 0; i < _currentRoomWalkboxes.size(); i++) {
 
 		// Calculate distance from source point to this walkbox (Manhattan distance)
 		int dx = 0;
 		int dy = 0;
 
 		// Calculate horizontal distance
-		if (sourceX < it->x) {
-			dx = it->x - sourceX;
-		} else if (sourceX > it->x + it->w) {
-			dx = sourceX - (it->x + it->w);
+		if (sourceX < _currentRoomWalkboxes[i].x) {
+			dx = _currentRoomWalkboxes[i].x - sourceX;
+		} else if (sourceX > _currentRoomWalkboxes[i].x + _currentRoomWalkboxes[i].w) {
+			dx = sourceX - (_currentRoomWalkboxes[i].x + _currentRoomWalkboxes[i].w);
 		}
 		// else: sourceX is inside walkbox horizontally, dx = 0
 
 		// Calculate vertical distance
-		if (sourceY < it->y) {
-			dy = it->y - sourceY;
-		} else if (sourceY > it->y + it->h) {
-			dy = sourceY - (it->y + it->h);
+		if (sourceY < _currentRoomWalkboxes[i].y) {
+			dy = _currentRoomWalkboxes[i].y - sourceY;
+		} else if (sourceY > _currentRoomWalkboxes[i].y + _currentRoomWalkboxes[i].h) {
+			dy = sourceY - (_currentRoomWalkboxes[i].y + _currentRoomWalkboxes[i].h);
 		}
 		// else: sourceY is inside walkbox vertically, dy = 0
 
@@ -1462,16 +1852,16 @@ Common::Point PelrockEngine::calculateWalkTarget(int mouseX, int mouseY) {
 			int targetX = sourceX;
 			int targetY = sourceY;
 
-			if (sourceX < it->x) {
-				targetX = it->x;
-			} else if (sourceX > it->x + it->w) {
-				targetX = it->x + it->w;
+			if (sourceX < _currentRoomWalkboxes[i].x) {
+				targetX = _currentRoomWalkboxes[i].x;
+			} else if (sourceX > _currentRoomWalkboxes[i].x + _currentRoomWalkboxes[i].w) {
+				targetX = _currentRoomWalkboxes[i].x + _currentRoomWalkboxes[i].w;
 			}
 
-			if (sourceY < it->y) {
-				targetY = it->y;
-			} else if (sourceY > it->y + it->h) {
-				targetY = it->y + it->h;
+			if (sourceY < _currentRoomWalkboxes[i].y) {
+				targetY = _currentRoomWalkboxes[i].y;
+			} else if (sourceY > _currentRoomWalkboxes[i].y + _currentRoomWalkboxes[i].h) {
+				targetY = _currentRoomWalkboxes[i].y + _currentRoomWalkboxes[i].h;
 			}
 
 			bestTarget.x = targetX;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 89f2475ed6b..34b767a0dfd 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -67,7 +67,7 @@ private:
 	Common::Array<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
 	Common::Array<HotSpot> loadHotspots(Common::File *roomFile, int roomOffset);
 	Common::List<Exit> loadExits(Common::File *roomFile, int roomOffset);
-	Common::List<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
+	Common::Array<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
 	Common::Array<Description> loadRoomDescriptions(Common::File *roomFile, int roomOffset, uint32_t &outPos);
 
 	Common::String cleanText(const Common::String &text);
@@ -76,6 +76,17 @@ private:
 	Common::Array<ConversationNode> loadConversations(Common::File *roomFile, int roomOffset, uint32_t startPos);
 
 	void walkTo(int x, int y);
+	bool pathFind(int x, int y, PathContext *context);
+	uint8_t find_walkbox_for_point(uint16_t x, uint16_t y);
+	bool point_in_walkbox(WalkBox *box, uint16_t x, uint16_t y);
+	uint16_t build_walkbox_path(uint8_t start_box, uint8_t dest_box, uint8_t *path_buffer);
+	uint8_t get_adjacent_walkbox(uint8_t current_box_index);
+	void clear_visited_flags();
+	uint16_t generate_movement_steps(uint8_t *path_buffer,
+									 uint16_t path_length,
+									 uint16_t start_x, uint16_t start_y,
+									 uint16_t dest_x, uint16_t dest_y,
+									 MovementStep *movement_buffer);
 
 	void talk();
 	Common::String getControlName(byte b);
@@ -112,10 +123,13 @@ private:
 	byte **talkingAnimFrames[4];              // 4 arrays of arrays
 	int talkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
 
+	PathContext _currentContext;
+	int _current_step = 0;
+
 	Common::Array<HotSpot> _currentRoomHotspots;
 	Common::Array<AnimSet> _currentRoomAnims;
 	Common::List<Exit> _currentRoomExits;
-	Common::List<WalkBox> _currentRoomWalkboxes;
+	Common::Array<WalkBox> _currentRoomWalkboxes;
 	Common::Array<Description> _currentRoomDescriptions;
 	Common::Array<ConversationNode> _currentRoomConversations;
 
@@ -154,10 +168,8 @@ private:
 	SmallFont *_smallFont = nullptr;
 	LargeFont *_largeFont = nullptr;
 
-
 	Common::Point _curWalkTarget;
 
-
 	bool shouldPlayIntro = false;
 	GameState stateGame = GAME;
 	bool gameInitialized = false;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 4f56d8d5393..065d773f4b7 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -21,9 +21,9 @@
 #ifndef PELROCK_TYPES_H
 #define PELROCK_TYPES_H
 
-#include "common/types.h"
 #include "common/scummsys.h"
 #include "common/system.h"
+#include "common/types.h"
 
 namespace Pelrock {
 
@@ -53,13 +53,12 @@ enum VerbIcons {
 // 	HOSTPOT,
 // 	SPECIAl;
 
-
 // };
 
 static const uint32 kLongClickDuration = 500; // 500ms for long click
 const int kCursorWidth = 16;
 const int kCursorHeight = 18;
-const int kCursorSize = 288;  // 16 * 18
+const int kCursorSize = 288; // 16 * 18
 const int kRoomStructSize = 104;
 const int kNumRooms = 56;
 const int kVerbIconWidth = 60;
@@ -69,6 +68,35 @@ const int kBalloonWidth = 247;
 const int kBalloonHeight = 112;
 const int kBalloonFrames = 4;
 
+// Direction flags (bit-packed)
+#define MOVE_RIGHT 0x01 // Move right (positive X)
+#define MOVE_LEFT 0x02  // Move left (negative X)
+#define MOVE_HORIZ 0x03 // Horizontal movement mask
+#define MOVE_DOWN 0x04  // Move down (positive Y)
+#define MOVE_UP 0x08    // Move up (negative Y)
+#define MOVE_VERT 0x0C  // Vertical movement mask
+#define MAX_PATH_LENGTH 100
+#define MAX_MOVEMENT_STEPS 100 // 500 bytes / 5 bytes per step
+#define PATH_END 0xFF          // End of path marker
+
+typedef struct {
+	uint8_t flags;       // Direction flags (see MOVE_* constants)
+	uint16_t distance_x; // Horizontal distance to move
+	uint16_t distance_y; // Vertical distance to move
+} MovementStep;
+
+/**
+ * Pathfinding context
+ */
+typedef struct {
+	uint8_t *path_buffer;          // Sequence of walkbox indices
+	MovementStep *movement_buffer; // Array of movement steps
+	uint8_t *compressed_path;      // Final compressed path
+	uint16_t path_length;
+	uint16_t movement_count;
+	uint16_t compressed_length;
+} PathContext;
+
 struct Anim {
 	int x;
 	int y;
@@ -98,20 +126,19 @@ struct Exit {
 
 struct AnimSet {
 	byte type;
-	int x; //0
-	int y;//2
-	int w;//4
-	int h;//5
-	byte extra; //6
-	int numAnims; //8
+	int x;        // 0
+	int y;        // 2
+	int w;        // 4
+	int h;        // 5
+	byte extra;   // 6
+	int numAnims; // 8
 	int curAnimIndex = 0;
-	byte spriteType; //33
-	byte actionFlags;//34
-	bool isDisabled; //38
+	byte spriteType;  // 33
+	byte actionFlags; // 34
+	bool isDisabled;  // 38
 	Anim *animData;
 };
 
-
 struct HotSpot {
 	int id;
 	int x;
@@ -124,46 +151,45 @@ struct HotSpot {
 };
 
 struct ConversationElement {
-    enum Type {
-        DIALOGUE,
-        CHOICE_MARKER,
-        END_CONV,
-        END_BRANCH
-    } type;
-
-    Common::String speaker;
+	enum Type {
+		DIALOGUE,
+		CHOICE_MARKER,
+		END_CONV,
+		END_BRANCH
+	} type;
+
+	Common::String speaker;
 	byte speakerId;
-    Common::String text;
-    int choiceIndex;
-    bool isRealChoice;
+	Common::String text;
+	int choiceIndex;
+	bool isRealChoice;
 
-    ConversationElement() : type(DIALOGUE), choiceIndex(-1), isRealChoice(false) {}
+	ConversationElement() : type(DIALOGUE), choiceIndex(-1), isRealChoice(false) {}
 };
 
-
 struct ConversationNode {
-    enum NodeType {
-        ROOT,
-        CHOICE,
-        RESPONSE
-    } type;
-
-    Common::String text;
-    Common::String speaker;
+	enum NodeType {
+		ROOT,
+		CHOICE,
+		RESPONSE
+	} type;
+
+	Common::String text;
+	Common::String speaker;
 	byte speakerId;
-    int choiceIndex;
-    bool terminated;
+	int choiceIndex;
+	bool terminated;
 
-    Common::Array<ConversationNode> choices;
-    Common::Array<ConversationNode> responses;
-    Common::Array<ConversationNode> subchoices;
+	Common::Array<ConversationNode> choices;
+	Common::Array<ConversationNode> responses;
+	Common::Array<ConversationNode> subchoices;
 
-    ConversationNode() : type(ROOT), choiceIndex(-1), terminated(false) {}
+	ConversationNode() : type(ROOT), choiceIndex(-1), terminated(false) {}
 };
 
 struct StackEntry {
-    ConversationNode *node;
-    int index;
+	ConversationNode *node;
+	int index;
 };
 
 struct Description {


Commit: 65b577da752a9b4562d16af6e48572e3e2960e1f
    https://github.com/scummvm/scummvm/commit/65b577da752a9b4562d16af6e48572e3e2960e1f
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:06+02:00

Commit Message:
PELROCK: Improve walk algorithm

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 1af81195bf9..ff3b418667b 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -152,6 +152,7 @@ Common::Error PelrockEngine::run() {
 		}
 		checkMouseHover();
 		frames();
+
 		_screen->update();
 		// limiter.delayBeforeSwap();
 		// limiter.startFrame();
@@ -177,7 +178,7 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		setScreen(0, 2);
+		setScreen(0, 1);
 		// setScreen(2, 2);
 		// setScreen(28, 0);
 	}
@@ -888,7 +889,7 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	}
 
 	Common::Array<HotSpot> staticHotspots = loadHotspots(roomFile, roomOffset);
-	Common::List<Exit> exits = loadExits(roomFile, roomOffset);
+	Common::Array<Exit> exits = loadExits(roomFile, roomOffset);
 
 	Common::Array<WalkBox> walkboxes = loadWalkboxes(roomFile, roomOffset);
 
@@ -908,17 +909,12 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 
 	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
 		HotSpot hotspot = _currentRoomHotspots[i];
-		// debug("Hotspot %d: x=%d y=%d w=%d h=%d type=%d enabled? %d extra=%d, desc=%s", i, hotspot.x, hotspot.y, hotspot.w, hotspot.h, hotspot.type, hotspot.isEnabled, hotspot.extra, _currentRoomDescriptions[i].text.c_str());
-		// drawRect(_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 200 + i);
+		drawRect(_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 200 + i);
 	}
 
-	for (Common::List<Exit>::iterator i = _currentRoomExits.begin(); i != _currentRoomExits.end(); i++) {
-		// debug("Exit: x=%d y=%d w=%d h=%d to room %d", i->x, i->y, i->w, i->h, i->targetRoom);
-		// _screen->fillRect(Common::Rect(i->x, i->y, i->x + i->w, i->y + i->h), 255);
-		// _screen->drawLine(i->x, i->y, i->x + i->w, i->y, 0);
-		// _screen->drawLine(i->x, i->y + i->h, i->x + i->w, i->y + i->h, 0);
-		// _screen->drawLine(i->x, i->y, i->x, i->y + i->h, 0);
-		// _screen->drawLine(i->x + i->w, i->y, i->x + i->w, i->y + i->h);
+	for (int i = 0; i < _currentRoomExits.size(); i++) {
+		Exit exit = _currentRoomExits[i];
+		drawRect(_screen, exit.x, exit.y, exit.w, exit.h, 100 + i);
 	}
 }
 
@@ -970,8 +966,8 @@ void PelrockEngine::loadInteractionIcons() {
 	alfred4File.close();
 }
 
-Common::List<Exit> PelrockEngine::loadExits(Common::File *roomFile, int roomOffset) {
-	Common::List<Exit> exits;
+Common::Array<Exit> PelrockEngine::loadExits(Common::File *roomFile, int roomOffset) {
+	Common::Array<Exit> exits;
 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
 	roomFile->seek(pair10_offset_pos, SEEK_SET);
 	uint32_t pair10_data_offset = roomFile->readUint32LE();
@@ -1216,7 +1212,7 @@ void PelrockEngine::frames() {
 		}
 
 		if (isAlfredWalking) {
-			debug("Alfred is walking, current step %d of %d", _current_step, _currentContext.movement_count);
+
 			MovementStep step = _currentContext.movement_buffer[_current_step];
 			debug("Alfred step: distance_x=%d, distance_y=%d", step.distance_x, step.distance_y);
 
@@ -1258,25 +1254,24 @@ void PelrockEngine::frames() {
 			} else {
 				_currentContext.movement_buffer[_current_step] = step;
 			}
-			// _current_step++;
-			// if(_current_step >= _currentContext.movement_count) {
-			// 	debug("Alfred reached his walk target.");
-			// 	_current_step = 0;
-			// 	isAlfredWalking = false;
-			// }
-			// if (step->flags & MOVE_RIGHT) printf("RIGHT ");
-			// if (step->flags & MOVE_LEFT) printf("LEFT ");
-			// if (step->flags & MOVE_DOWN) printf("DOWN ");
-			// if (step->flags & MOVE_UP) printf("UP ");
 
-			// debug("Drawing walking frame %d for direction %d", curAlfredFrame, dirAlfred);
-			drawAlfred(walkingAnimFrames[dirAlfred][curAlfredFrame]);
+			Exit *exit = isExitUnder(xAlfred, yAlfred);
 
-			if (curAlfredFrame < walkingAnimLengths[dirAlfred] - 1) {
-				curAlfredFrame++;
-			} else {
+			if (exit != nullptr) {
+				xAlfred = exit->targetX;
+				yAlfred = exit->targetY;
+				setScreen(exit->targetRoom, exit->dir);
+			}
+
+			debug("Drawing walking frame %d for direction %d", curAlfredFrame, dirAlfred);
+
+			if (curAlfredFrame >= walkingAnimLengths[dirAlfred]) {
 				curAlfredFrame = 0;
 			}
+
+			drawAlfred(walkingAnimFrames[dirAlfred][curAlfredFrame]);
+			curAlfredFrame++;
+
 			// debug("CurAlfredFrame from walking is now %d", curAlfredFrame);
 		} else if (isAlfredTalking) {
 			drawAlfred(talkingAnimFrames[dirAlfred][curAlfredFrame]);
@@ -1383,10 +1378,11 @@ int PelrockEngine::isHotspotUnder(int x, int y) {
 }
 
 Exit *PelrockEngine::isExitUnder(int x, int y) {
-	for (Common::List<Exit>::iterator i = _currentRoomExits.begin(); i != _currentRoomExits.end(); i++) {
-		if (x >= i->x && x <= (i->x + i->w) &&
-			y >= i->y && y <= (i->y + i->h)) {
-			return &(*i);
+	for (int i = 0; i < _currentRoomExits.size(); i++) {
+		Exit exit = _currentRoomExits[i];
+		if (x >= exit.x && x <= (exit.x + exit.w) &&
+			y >= exit.y && y <= (exit.y + exit.h)) {
+			return &(_currentRoomExits[i]);
 		}
 	}
 	return nullptr;
@@ -1756,22 +1752,22 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 
 	Common::Point walkTarget = calculateWalkTarget(mouseX, mouseY);
 	_curWalkTarget = walkTarget;
-	debug("Calculated walk target at (%d, %d)", walkTarget.x, walkTarget.y);
-	Exit *exit = isExitAtPoint(walkTarget.x, walkTarget.y);
+	// debug("Calculated walk target at (%d, %d)", walkTarget.x, walkTarget.y);
+	// Exit *exit = isExitUnder(walkTarget.x, walkTarget.y);
 
-	if (exit != nullptr) {
+	/*if (exit != nullptr) {
 		xAlfred = exit->targetX;
 		yAlfred = exit->targetY;
 		setScreen(exit->targetRoom, exit->dir);
-	} else {
-		walkTo(walkTarget.x, walkTarget.y);
-	}
-
-	int hotspotIndex = isHotspotUnder(mouseX, mouseY);
-	if (hotspotIndex != -1) {
-		talk();
-		debug("Hotspot clicked: %d", _currentRoomHotspots[hotspotIndex].extra);
-	}
+	} else {*/
+	walkTo(walkTarget.x, walkTarget.y);
+	/*	} */
+
+	// int hotspotIndex = isHotspotUnder(mouseX, mouseY);
+	// if (hotspotIndex != -1) {
+	// 	talk();
+	// 	debug("Hotspot clicked: %d", _currentRoomHotspots[hotspotIndex].extra);
+	// }
 }
 
 void PelrockEngine::changeCursor(Cursor cursor) {
@@ -1786,7 +1782,7 @@ void PelrockEngine::checkMouseHover() {
 
 	// Check if walk target hits any exit
 	bool exitDetected = false;
-	Exit *exit = isExitAtPoint(walkTarget.x, walkTarget.y);
+	Exit *exit = isExitUnder(walkTarget.x, walkTarget.y);
 	if (exit != nullptr) {
 		exitDetected = true;
 	}
@@ -1872,18 +1868,6 @@ Common::Point PelrockEngine::calculateWalkTarget(int mouseX, int mouseY) {
 	return bestTarget;
 }
 
-Exit *PelrockEngine::isExitAtPoint(int x, int y) {
-	for (Common::List<Exit>::iterator i = _currentRoomExits.begin();
-		 i != _currentRoomExits.end(); ++i) {
-		// Check if point is inside exit trigger rectangle
-		if (x >= i->x && x <= (i->x + i->w) &&
-			y >= i->y && y <= (i->y + i->h)) {
-			return &(*i);
-		}
-	}
-	return nullptr;
-}
-
 void PelrockEngine::showDescription(Common::String text, int x, int y, byte color) {
 	Common::Rect rect = _largeFont->getBoundingBox(text.c_str());
 	if (x + 2 + rect.width() > 640) {
@@ -1931,7 +1915,9 @@ void PelrockEngine::setScreen(int number, int dir) {
 		return;
 	}
 	dirAlfred = dir;
-
+	isAlfredWalking = false;
+	isAlfredTalking = false;
+	_current_step = 0;
 	int roomOffset = number * kRoomStructSize;
 	curAlfredFrame = 0;
 	byte *palette = new byte[256 * 3];
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 34b767a0dfd..0d9fb5d617c 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -66,7 +66,7 @@ private:
 	void loadAlfredAnims();
 	Common::Array<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
 	Common::Array<HotSpot> loadHotspots(Common::File *roomFile, int roomOffset);
-	Common::List<Exit> loadExits(Common::File *roomFile, int roomOffset);
+	Common::Array<Exit> loadExits(Common::File *roomFile, int roomOffset);
 	Common::Array<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
 	Common::Array<Description> loadRoomDescriptions(Common::File *roomFile, int roomOffset, uint32_t &outPos);
 
@@ -98,7 +98,6 @@ private:
 
 	Common::List<VerbIcons> populateActionsMenu(HotSpot *hotspot);
 	Common::Point calculateWalkTarget(int mouseX, int mouseY);
-	Exit *isExitAtPoint(int x, int y);
 	void showDescription(Common::String text, int x, int y, byte color);
 
 	// render loop
@@ -128,15 +127,15 @@ private:
 
 	Common::Array<HotSpot> _currentRoomHotspots;
 	Common::Array<AnimSet> _currentRoomAnims;
-	Common::List<Exit> _currentRoomExits;
+	Common::Array<Exit> _currentRoomExits;
 	Common::Array<WalkBox> _currentRoomWalkboxes;
 	Common::Array<Description> _currentRoomDescriptions;
 	Common::Array<ConversationNode> _currentRoomConversations;
 
 	int *_currentAnimFrames = nullptr;
 	// From the original code
-	int xAlfred = 200;
-	int yAlfred = 200;
+	int xAlfred = 319;
+	int yAlfred = 302;
 	int dirAlfred = 0;
 	int curAlfredFrame = 0;
 	bool isAlfredWalking = false;


Commit: 92489bb33eefded0a9d2b69688947aaf3d84264b
    https://github.com/scummvm/scummvm/commit/92489bb33eefded0a9d2b69688947aaf3d84264b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:06+02:00

Commit Message:
PELROCK: Improve text display

Changed paths:
    engines/pelrock/chrono.cpp
    engines/pelrock/chrono.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/chrono.cpp b/engines/pelrock/chrono.cpp
index 355df50a828..9bcbd6a8873 100644
--- a/engines/pelrock/chrono.cpp
+++ b/engines/pelrock/chrono.cpp
@@ -36,6 +36,11 @@ ChronoManager::~ChronoManager() {
 void ChronoManager::updateChrono() {
 	uint32 currentTime = g_system->getMillis();
 
+	if(_textTtl > 0) {
+		_textTtl -= (currentTime - _lastTick);
+		if(_textTtl < 0)
+			_textTtl = 0;
+	}
 	if ((currentTime - _lastTick) >= kTickMs / _speedMultiplier) {
 		_gameTick = true;
 		_tickCount++;
diff --git a/engines/pelrock/chrono.h b/engines/pelrock/chrono.h
index 9dab952fadd..de19a626789 100644
--- a/engines/pelrock/chrono.h
+++ b/engines/pelrock/chrono.h
@@ -44,6 +44,7 @@ public:
 
 	bool _gameTick = false;
 	bool _gameTickHalfSpeed = false;
+	long _textTtl = 0;
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index ff3b418667b..a2cc8946ef3 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -91,7 +91,7 @@ void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byt
 	surface->drawLine(x, y, x, y + h, color);
 	surface->drawLine(x + w, y, x + w, y + h, color);
 }
-
+Common::Array<Common::Array<Common::String> > wordWrap(Common::String text);
 Common::Error PelrockEngine::run() {
 	// Initialize 320x200 paletted graphics mode
 	initGraphics(640, 400);
@@ -348,21 +348,6 @@ Common::Array<AnimSet> PelrockEngine::loadRoomAnimations(Common::File *roomFile,
 		}
 
 		anims.push_back(animSet);
-
-		// if (w > 0 && h > 0 && frames > 0) {
-		// 	AnimSet anim;
-		// 	anim.x = x;
-		// 	anim.y = y;
-		// 	anim.w = w;
-		// 	anim.h = h;
-		// 	anim.numAnims = frames;
-		// 	uint32_t needed = anim.w * anim.h * anim.nframes;
-		// 	anim.animData = new byte[needed];
-		// 	Common::copy(pic + picOffset, pic + picOffset + needed, anim.animData);
-		// 	picOffset += needed;
-		// 	debug("Anim %d: x=%d y=%d w=%d h=%d nframes=%d", i, anim.x, anim.y, anim.w, anim.h, anim.nframes);
-		// 	anims.push_back(anim);
-		// }
 	}
 	return anims;
 }
@@ -448,21 +433,6 @@ Common::Array<Description> PelrockEngine::loadRoomDescriptions(Common::File *roo
 	return descriptions;
 }
 
-/**
- * def decode_byte(b):
-	"""Decode a byte to character"""
-	special = {
-		0x80: 'ñ', 0x81: 'í', 0x82: '¡', 0x83: '¿', 0x84: 'ú',
-		0x7B: 'á', 0x7C: 'é', 0x7D: 'í', 0x7E: 'ó', 0x7F: 'ú',
-	}
-
-	if b in special:
-		return special[b]
-	elif 0x20 <= b <= 0x7A:
-		return chr(b)
-	else:
-		return f'[{b:02X}]'
- */
 char32_t decodeByte(byte b) {
 	if (b == 0x80) {
 		return '\xA4';
@@ -1242,6 +1212,7 @@ void PelrockEngine::frames() {
 
 			if (step.distance_y > 0)
 				step.distance_y -= MIN((uint16_t)6, step.distance_y);
+
 			debug("Alfred position after step: x=%d, y=%d, step distance_x=%d, step distance_y=%d", xAlfred, yAlfred, step.distance_x, step.distance_y);
 			if (step.distance_x <= 0 && step.distance_y <= 0) {
 				debug("Alfred completed step %d", _current_step);
@@ -1272,16 +1243,12 @@ void PelrockEngine::frames() {
 			drawAlfred(walkingAnimFrames[dirAlfred][curAlfredFrame]);
 			curAlfredFrame++;
 
-			// debug("CurAlfredFrame from walking is now %d", curAlfredFrame);
 		} else if (isAlfredTalking) {
-			drawAlfred(talkingAnimFrames[dirAlfred][curAlfredFrame]);
-
-			if (curAlfredFrame < talkingAnimLengths[dirAlfred] - 1) {
-				curAlfredFrame++;
-			} else {
+			if (curAlfredFrame >= talkingAnimLengths[dirAlfred] - 1) {
 				curAlfredFrame = 0;
 			}
-			debug("CurAlfredFrame from talking is now %d", curAlfredFrame);
+			drawAlfred(talkingAnimFrames[dirAlfred][curAlfredFrame]);
+			curAlfredFrame++;
 		} else {
 			drawAlfred(standingAnimFrames[dirAlfred]);
 		}
@@ -1307,6 +1274,26 @@ void PelrockEngine::frames() {
 		}
 
 		memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
+
+		if (!_currentTextPages.empty()) {
+			// debug("Will render text, _chronoManager->_textTtl=%d", _chronoManager->_textTtl);
+			if (_chronoManager->_textTtl > 0) {
+				renderText(_currentTextPages[_currentTextPageIndex], _textColor);
+			} else if (_currentTextPageIndex < _currentTextPages.size() - 1) {
+				_currentTextPageIndex++;
+
+				int totalChars = 0;
+				for (int i = 0; i < _currentTextPages[_currentTextPageIndex].size(); i++) {
+					totalChars += _currentTextPages[_currentTextPageIndex][i].size();
+				}
+				_chronoManager->_textTtl = totalChars * kTextCharDisplayTime;
+			} else {
+				_currentTextPages.clear();
+				_currentTextPageIndex = 0;
+				isAlfredTalking = false;
+			}
+		}
+
 		// debug("Drawing walkboxes..., %d, _currentRoomWalkboxes.size()=%d",  _currentRoomWalkboxes.size(), _currentRoomWalkboxes.size());
 		for (int i = 0; i < _currentRoomWalkboxes.size(); i++) {
 			// debug("Drawing walkbox %d", i);
@@ -1337,6 +1324,26 @@ void PelrockEngine::frames() {
 	}
 }
 
+void PelrockEngine::renderText(Common::Array<Common::String> lines, int color) {
+	if (color == ALFRED_COLOR) {
+		int baseX = xAlfred;
+		int baseY = yAlfred - kAlfredFrameHeight - 10;
+		int maxW = 0;
+		for (size_t i = 0; i < lines.size(); i++) {
+			Common::Rect r = _largeFont->getBoundingBox(lines[i]);
+			if (r.width() > maxW) {
+				maxW = r.width();
+			}
+		}
+		int lineSize = lines.size();
+		for (size_t i = 0; i < lines.size(); i++) {
+			int textX = baseX - (maxW / 2);
+			int textY = baseY - (lineSize * 20) + (i * 20);
+			drawText(lines[i], textX, textY, maxW, color);
+		}
+	}
+}
+
 void PelrockEngine::drawAlfred(byte *buf) {
 
 	drawSpriteToBuffer(_compositeBuffer, 640, buf, xAlfred, yAlfred - kAlfredFrameHeight, kAlfredFrameWidth, kAlfredFrameHeight, 255);
@@ -1760,14 +1767,15 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 		yAlfred = exit->targetY;
 		setScreen(exit->targetRoom, exit->dir);
 	} else {*/
-	walkTo(walkTarget.x, walkTarget.y);
 	/*	} */
 
-	// int hotspotIndex = isHotspotUnder(mouseX, mouseY);
-	// if (hotspotIndex != -1) {
-	// 	talk();
-	// 	debug("Hotspot clicked: %d", _currentRoomHotspots[hotspotIndex].extra);
-	// }
+	int hotspotIndex = isHotspotUnder(mouseX, mouseY);
+	if (hotspotIndex != -1) {
+		sayAlfred(_currentRoomDescriptions[hotspotIndex].text);
+		debug("Hotspot clicked: %d", _currentRoomHotspots[hotspotIndex].extra);
+	} else {
+		walkTo(walkTarget.x, walkTarget.y);
+	}
 }
 
 void PelrockEngine::changeCursor(Cursor cursor) {
@@ -1868,7 +1876,7 @@ Common::Point PelrockEngine::calculateWalkTarget(int mouseX, int mouseY) {
 	return bestTarget;
 }
 
-void PelrockEngine::showDescription(Common::String text, int x, int y, byte color) {
+void PelrockEngine::drawText(Common::String text, int x, int y, int w, byte color) {
 	Common::Rect rect = _largeFont->getBoundingBox(text.c_str());
 	if (x + 2 + rect.width() > 640) {
 		x = 640 - rect.width() - 2;
@@ -1883,28 +1891,153 @@ void PelrockEngine::showDescription(Common::String text, int x, int y, byte colo
 		y = 2;
 	}
 
-	x = 2;
-	y = 2;
-	// if (_bgText != nullptr) {
-	// 	putBackgroundSlice(x, y, 640, 400, _bgText);
-	// 	delete[] _bgText;
-	// }
-	int16 w = MIN(rect.width(), (int16)(640 - x));
-	int16 h = MIN(rect.height(), (int16)(400 - y));
-	debug("grabbing bg slice at (%d,%d) w=%d h=%d", x, y, w, h);
-
-	// _bgText = grabBackgroundSlice(x, y, 640, 400);
-	_largeFont->drawString(_screen, text.c_str(), x - 1, y, 640, 0); // Left
-	_largeFont->drawString(_screen, text.c_str(), x - 2, y, 640, 0); // Left
-	_largeFont->drawString(_screen, text.c_str(), x + 1, y, 640, 0); // Right
-	_largeFont->drawString(_screen, text.c_str(), x + 2, y, 640, 0); // Right
-	_largeFont->drawString(_screen, text.c_str(), x, y - 1, 640, 0); // Top
-	_largeFont->drawString(_screen, text.c_str(), x, y - 2, 640, 0); // Top
-	_largeFont->drawString(_screen, text.c_str(), x, y + 1, 640, 0); // Bottom
-	_largeFont->drawString(_screen, text.c_str(), x, y + 2, 640, 0); // Bottom
+	_largeFont->drawString(_screen, text.c_str(), x - 1, y, w, 0, Graphics::kTextAlignCenter); // Left
+	// _largeFont->drawString(_screen, text.c_str(), x - 2, y, 640, 0); // Left
+	_largeFont->drawString(_screen, text.c_str(), x + 1, y, w, 0, Graphics::kTextAlignCenter); // Right
+	// _largeFont->drawString(_screen, text.c_str(), x + 2, y, 640, 0); // Right
+	_largeFont->drawString(_screen, text.c_str(), x, y - 1, w, 0, Graphics::kTextAlignCenter); // Top
+	// _largeFont->drawString(_screen, text.c_str(), x, y - 2, 640, 0); // Top
+	_largeFont->drawString(_screen, text.c_str(), x, y + 1, w, 0, Graphics::kTextAlignCenter); // Bottom
+	// _largeFont->drawString(_screen, text.c_str(), x, y + 2, 640, 0); // Bottom
 
 	// Draw main text on top
-	_largeFont->drawString(_screen, text.c_str(), x, y, 640, color);
+	_largeFont->drawString(_screen, text.c_str(), x, y, w, color, Graphics::kTextAlignCenter);
+}
+
+void PelrockEngine::sayAlfred(Common::String text) {
+	isAlfredTalking = true;
+	curAlfredFrame = 0;
+	debug("Alfred says: %s", text.c_str());
+	_currentTextPages = wordWrap(text);
+	_textColor = 13;
+	int totalChars = 0;
+	for (int i = 0; i < _currentTextPages[0].size(); i++) {
+		totalChars += _currentTextPages[0][i].size();
+	}
+	_chronoManager->_textTtl = totalChars * kTextCharDisplayTime;
+}
+bool isEndMarker(char char_byte) {
+	return char_byte == CHAR_END_MARKER_1 || char_byte == CHAR_END_MARKER_2 || char_byte == CHAR_END_MARKER_3 || char_byte == CHAR_END_MARKER_4;
+}
+
+int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
+	// return word_length, is_end
+	int wordLength = 0;
+	int pos = startPos;
+	while (pos < text.size()) {
+		char char_byte = text[pos];
+		if (char_byte == CHAR_SPACE || isEndMarker(char_byte)) {
+			break;
+		}
+		wordLength++;
+		pos++;
+	}
+	// Check if we hit an end marker
+	if (pos < text.size() && isEndMarker(text[pos])) {
+		isEnd = true;
+	}
+	// Count ALL trailing spaces as part of this word
+	if (pos < text.size() && !isEnd) {
+		if (text[pos] == CHAR_END_MARKER_3) { // 0xF8 (-8) special case
+			wordLength += 3;
+		} else {
+			// Count all consecutive spaces
+			while (pos < text.size() && text[pos] == CHAR_SPACE) {
+				wordLength++;
+				pos++;
+			}
+		}
+	}
+	return wordLength;
+}
+
+Common::String joinStrings(const Common::Array<Common::String> &strings, const Common::String &separator) {
+	Common::String result;
+	for (uint i = 0; i < strings.size(); i++) {
+		result += strings[i];
+		if (i < strings.size() - 1)
+			result += separator;
+	}
+	return result;
+}
+
+Common::Array<Common::Array<Common::String> > wordWrap(Common::String text) {
+
+	Common::Array<Common::Array<Common::String> > pages;
+	Common::Array<Common::String> currentPage;
+	Common::Array<Common::String> currentLine;
+	int charsRemaining = MAX_CHARS_PER_LINE;
+	int position = 0;
+	int currentLineNum = 0;
+	while (position < text.size()) {
+		bool isEnd = false;
+		int wordLength = calculateWordLength(text, position, isEnd);
+		// # Extract the word (including trailing spaces)
+		// word = text[position:position + word_length].decode('latin-1', errors='replace')
+		Common::String word = text.substr(position, wordLength).decode(Common::kLatin1);
+		// # Key decision: if word_length > chars_remaining, wrap to next line
+		if (wordLength > charsRemaining) {
+			// Word is longer than the entire line - need to split
+			currentPage.push_back(joinStrings(currentLine, " "));
+			currentLine.clear();
+			charsRemaining = MAX_CHARS_PER_LINE;
+			currentLineNum++;
+
+			if (currentLineNum >= MAX_LINES) {
+				pages.push_back(currentPage);
+				currentPage.clear();
+				currentLineNum = 0;
+			}
+		}
+		// Add word to current line
+		currentLine.push_back(word);
+		charsRemaining -= wordLength;
+
+		if (charsRemaining == 0 && isEnd) {
+			Common::String lineText = joinStrings(currentLine, "");
+			while (lineText.lastChar() == CHAR_SPACE) {
+				lineText = lineText.substr(0, lineText.size() - 1);
+			}
+			int trailingSpaces = currentLine.size() - lineText.size();
+			if (trailingSpaces > 0) {
+				currentPage.push_back(lineText);
+				//  current_line = [' ' * trailing_spaces]
+				Common::String currentLine(trailingSpaces, ' ');
+				charsRemaining = MAX_CHARS_PER_LINE - trailingSpaces;
+				currentLineNum += 1;
+
+				if (currentLineNum >= MAX_LINES) {
+					pages.push_back(currentPage);
+					currentPage.clear();
+					currentLineNum = 0;
+				}
+			}
+		}
+
+		position += wordLength;
+		if (isEnd) {
+			// End of sentence/paragraph/page
+			break;
+		}
+	}
+	if (currentLine.empty() == false) {
+		Common::String lineText = joinStrings(currentLine, "");
+		while (lineText.lastChar() == CHAR_SPACE) {
+			lineText = lineText.substr(0, lineText.size() - 1);
+		}
+		currentPage.push_back(lineText);
+	}
+	if (currentPage.empty() == false) {
+		pages.push_back(currentPage);
+	}
+	debug("Word wrap produced %d pages", pages.size());
+	for (int i = 0; i < pages.size(); i++) {
+		debug(" Page %d:", i);
+		for (int j = 0; j < pages[i].size(); j++) {
+			debug("   Line %d: %s", j, pages[i][j].c_str());
+		}
+	}
+	return pages;
 }
 
 void PelrockEngine::setScreen(int number, int dir) {
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 0d9fb5d617c..6e744c3761e 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -98,10 +98,12 @@ private:
 
 	Common::List<VerbIcons> populateActionsMenu(HotSpot *hotspot);
 	Common::Point calculateWalkTarget(int mouseX, int mouseY);
-	void showDescription(Common::String text, int x, int y, byte color);
+	void drawText(Common::String text, int x, int y, int w, byte color);
 
+	void sayAlfred(Common::String text);
 	// render loop
 	void frames();
+	void renderText(Common::Array<Common::String> lines, int color);
 	void drawAlfred(byte *buf);
 	void checkMouseHover();
 	void checkMouseClick(int x, int y);
@@ -124,7 +126,9 @@ private:
 
 	PathContext _currentContext;
 	int _current_step = 0;
-
+	byte _textColor = 0;
+	Common::Array<Common::Array<Common::String> > _currentTextPages = Common::Array<Common::Array<Common::String> >();
+	int _currentTextPageIndex = 0;
 	Common::Array<HotSpot> _currentRoomHotspots;
 	Common::Array<AnimSet> _currentRoomAnims;
 	Common::Array<Exit> _currentRoomExits;
@@ -153,11 +157,6 @@ private:
 	byte *_currentBackground; // Clean background - NEVER modified
 	byte *_compositeBuffer;   // Working composition buffer
 
-	// byte *_currentBackground = nullptr;
-	// byte *_bgPopupBalloon = nullptr;
-	// byte *_bgText = nullptr;
-	// byte *_bgAlfred = nullptr;
-
 	bool _displayPopup = false;
 	int _popupX = 0;
 	int _popupY = 0;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 065d773f4b7..766df2dc150 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -47,14 +47,6 @@ enum VerbIcons {
 	UNKNOWN
 };
 
-// enum HoverState {
-// 	NONE,
-// 	INTERACTIVE,
-// 	HOSTPOT,
-// 	SPECIAl;
-
-// };
-
 static const uint32 kLongClickDuration = 500; // 500ms for long click
 const int kCursorWidth = 16;
 const int kCursorHeight = 18;
@@ -67,6 +59,7 @@ const int kNumVerbIcons = 9;
 const int kBalloonWidth = 247;
 const int kBalloonHeight = 112;
 const int kBalloonFrames = 4;
+const int kTextCharDisplayTime = 100; // 10ms per character
 
 // Direction flags (bit-packed)
 #define MOVE_RIGHT 0x01 // Move right (positive X)
@@ -79,8 +72,22 @@ const int kBalloonFrames = 4;
 #define MAX_MOVEMENT_STEPS 100 // 500 bytes / 5 bytes per step
 #define PATH_END 0xFF          // End of path marker
 
+#define MAX_CHARS_PER_LINE 0x2F // 47 characters
+#define MAX_LINES 5             // Maximum number of lines per page (0-indexed check against 4)
+
+// Control character codes (negative values in signed char)
+#define CHAR_SPACE 0x20        /* ' ' */
+#define CHAR_END_MARKER_1 0xFD /* -3 (end of text marker) */
+#define CHAR_END_MARKER_2 0xF4 /* -0xC (alternate end marker) */
+#define CHAR_END_MARKER_3 0xF8 /* -8 (another end marker) */
+#define CHAR_END_MARKER_4 0xF0 /* -0x10 (another end marker) */
+#define CHAR_NEWLINE 0xF6      /* -10 (newline marker) */
+#define CHAR_PAGE_BREAK 0xF9   /* marker inserted when switching pages */
+
+#define ALFRED_COLOR 0x0D
+
 typedef struct {
-	uint8_t flags;       // Direction flags (see MOVE_* constants)
+	uint8_t flags;       /* Direction flags (see MOVE_* constants) */
 	uint16_t distance_x; // Horizontal distance to move
 	uint16_t distance_y; // Vertical distance to move
 } MovementStep;
@@ -217,24 +224,6 @@ enum GameState {
 	PROMOTE = 107,
 };
 
-// enum ConversationMarkers : byte {
-//     END_LINE(0xFD),
-//     TEXT_TERM(0xFC),
-// 	CHOICE(0xFB),
-// 	SKIP(0xFA),
-// 	PAGE_BREAK(0xF9),
-// 	ACTION(0xF8),
-// 	END_BRANCH(0xF7),
-// 	LINE_CONT(0xF6),
-// 	END_BRANCH_2(0xF5),
-// 	END_CONV(0xF4),
-// 	GO_BACK(0xF0),
-// 	END_BRANCH_3(0xFE),
-// 	END_ALT(0xEB),
-// 	DESC_START(0xFF),
-// 	SPEAKER(0x08)
-// };
-
 } // End of namespace Pelrock
 
 #endif


Commit: 18060a6d5fa7b1eaf7e27f80efc811f29572266f
    https://github.com/scummvm/scummvm/commit/18060a6d5fa7b1eaf7e27f80efc811f29572266f
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:06+02:00

Commit Message:
PELROCK: Adds extra pixel char width on font

Changed paths:
    engines/pelrock/fonts/large_font.cpp


diff --git a/engines/pelrock/fonts/large_font.cpp b/engines/pelrock/fonts/large_font.cpp
index 9f94f855921..c27b7458c0c 100644
--- a/engines/pelrock/fonts/large_font.cpp
+++ b/engines/pelrock/fonts/large_font.cpp
@@ -47,7 +47,7 @@ bool LargeFont::load(const Common::String &filename) {
 }
 
 int LargeFont::getCharWidth(uint32 chr) const {
-	return CHAR_WIDTH;
+	return CHAR_WIDTH + 1;
 }
 
 void LargeFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {


Commit: cbe2b9958d2017062f41c8be01cf54ed775e1ff1
    https://github.com/scummvm/scummvm/commit/cbe2b9958d2017062f41c8be01cf54ed775e1ff1
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:07+02:00

Commit Message:
PELROCK: Font rendering

Changed paths:
    engines/pelrock/chrono.cpp
    engines/pelrock/fonts/large_font.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/chrono.cpp b/engines/pelrock/chrono.cpp
index 9bcbd6a8873..8201dc15427 100644
--- a/engines/pelrock/chrono.cpp
+++ b/engines/pelrock/chrono.cpp
@@ -36,11 +36,12 @@ ChronoManager::~ChronoManager() {
 void ChronoManager::updateChrono() {
 	uint32 currentTime = g_system->getMillis();
 
-	if(_textTtl > 0) {
+	if(_textTtl > 0 && g_engine->isAlfredTalking && !g_engine->isAlfredWalking) {
 		_textTtl -= (currentTime - _lastTick);
 		if(_textTtl < 0)
 			_textTtl = 0;
 	}
+
 	if ((currentTime - _lastTick) >= kTickMs / _speedMultiplier) {
 		_gameTick = true;
 		_tickCount++;
diff --git a/engines/pelrock/fonts/large_font.cpp b/engines/pelrock/fonts/large_font.cpp
index c27b7458c0c..9a1dc5894e0 100644
--- a/engines/pelrock/fonts/large_font.cpp
+++ b/engines/pelrock/fonts/large_font.cpp
@@ -37,12 +37,75 @@ bool LargeFont::load(const Common::String &filename) {
 	}
 
 	file.seek(0x7DC8, SEEK_SET);
-	const int dataSize = 96 * 48; // 96 characters × 48 bytes
-	_fontData = new byte[dataSize];
-	file.read(_fontData, dataSize);
+	const int numChars = 96;
+	const int charWidth = 12;
+	const int charHeight = 24;
+	const int pad = 1;
+
+	const int paddedWidth = charWidth + 2 * pad;   // 14
+	const int paddedHeight = charHeight + 2 * pad; // 26
+
+	const int dataSize = numChars * paddedHeight * paddedWidth; // 96 characters × 14 × 26 bytes
+	byte *rawFontData = new byte[numChars * 48];                // original format: 96 × 48 bytes
+	file.read(rawFontData, numChars * 48);
 	debug("LargeFont::load: Loading large font data from %s, size %d bytes", filename.c_str(), dataSize);
 	file.close();
 
+	delete[] _fontData;
+
+	_fontData = new byte[dataSize];
+	memset(_fontData, 0, dataSize);
+	for (int c = 0; c < numChars; c++) {
+		// Temporary bitmap for character + border
+		bool mask[paddedHeight][paddedWidth] = {false};
+		// Decode character pixels from rawFontData
+		int charOffset = c * 0x30;
+		for (int i = 0; i < charHeight; i++) {
+			byte rowByte1 = rawFontData[charOffset + i * 2];
+			byte rowByte2 = rawFontData[charOffset + i * 2 + 1];
+			for (int bit = 0; bit < 8; bit++) {
+				mask[i + pad][bit + pad] = (rowByte1 & (0x80 >> bit)) != 0;
+			}
+			for (int bit = 0; bit < 4; bit++) {
+				mask[i + pad][bit + 8 + pad] = (rowByte2 & (0x80 >> bit)) != 0;
+			}
+		}
+
+		bool borderMask[paddedHeight][paddedWidth] = {false};
+
+		for (int y = 0; y < paddedHeight; y++) {
+			for (int x = 0; x < paddedWidth; x++) {
+				if (mask[y][x]) {
+					// Mark 3x3 area around character pixel
+					for (int dy = -1; dy <= 1; dy++) {
+						for (int dx = -1; dx <= 1; dx++) {
+							int ny = y + dy;
+							int nx = x + dx;
+							if (ny >= 0 && ny < paddedHeight && nx >= 0 && nx < paddedWidth) {
+								if (!mask[ny][nx]) {
+									borderMask[ny][nx] = true;
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+
+		int outOffset = c * paddedHeight * paddedWidth;
+		for (int y = 0; y < paddedHeight; y++) {
+			for (int x = 0; x < paddedWidth; x++) {
+				if (mask[y][x]) {
+					_fontData[outOffset + y * paddedWidth + x] = 2;
+				} else if (borderMask[y][x]) {
+					_fontData[outOffset + y * paddedWidth + x] = 1;
+				} else {
+					_fontData[outOffset + y * paddedWidth + x] = 0;
+				}
+			}
+		}
+	}
+	delete[] rawFontData;
 	return true;
 }
 
@@ -55,24 +118,22 @@ void LargeFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint3
 	if (!_fontData || chr > 96 || chr < 0) {
 		return;
 	}
-	int charOffset = chr * 0x30;
-	for (int i = 0; i < 24; i++) {
-		byte rowByte1 = _fontData[charOffset + i * 2];
-		byte rowByte2 = _fontData[charOffset + i * 2 + 1];
-		for (int bit = 0; bit < 8; bit++) {
-			bool pixelOn = (rowByte1 & (0x80 >> bit)) != 0;
-			if (pixelOn) {
-				if ((x + bit) < dst->w && (y + i) < dst->h) {
-					*((byte *)dst->getBasePtr(x + bit, y + i)) = color;
-				}
-			}
-		}
-		for (int bit = 0; bit < 4; bit++) {
-			bool pixelOn = (rowByte2 & (0x80 >> bit)) != 0;
-			if (pixelOn) {
-				if ((x + bit + 8) < dst->w && (y + i) < dst->h) {
-					*((byte *)dst->getBasePtr(x + bit + 8, y + i)) = color;
-				}
+
+	const int paddedWidth = 14;
+	const int paddedHeight = 26;
+	int charOffset = chr * paddedWidth * paddedHeight;
+
+	for (int cy = 0; cy < paddedHeight; cy++) {
+		for (int cx = 0; cx < paddedWidth; cx++) {
+			byte val = _fontData[charOffset + cy * paddedWidth + cx];
+			int px = x + cx;
+			int py = y + cy;
+			if (px < 0 || px >= dst->w || py < 0 || py >= dst->h)
+				continue;
+			if (val == 1) {
+				*((byte *)dst->getBasePtr(px, py)) = 0;
+			} else if (val == 2) {
+				*((byte *)dst->getBasePtr(px, py)) = color;
 			}
 		}
 	}
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index a2cc8946ef3..5300ae37dae 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1275,7 +1275,7 @@ void PelrockEngine::frames() {
 
 		memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
 
-		if (!_currentTextPages.empty()) {
+		if (!isAlfredWalking && !_currentTextPages.empty()) {
 			// debug("Will render text, _chronoManager->_textTtl=%d", _chronoManager->_textTtl);
 			if (_chronoManager->_textTtl > 0) {
 				renderText(_currentTextPages[_currentTextPageIndex], _textColor);
@@ -1773,9 +1773,9 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 	if (hotspotIndex != -1) {
 		sayAlfred(_currentRoomDescriptions[hotspotIndex].text);
 		debug("Hotspot clicked: %d", _currentRoomHotspots[hotspotIndex].extra);
-	} else {
-		walkTo(walkTarget.x, walkTarget.y);
 	}
+
+	walkTo(walkTarget.x, walkTarget.y);
 }
 
 void PelrockEngine::changeCursor(Cursor cursor) {
@@ -1891,14 +1891,14 @@ void PelrockEngine::drawText(Common::String text, int x, int y, int w, byte colo
 		y = 2;
 	}
 
-	_largeFont->drawString(_screen, text.c_str(), x - 1, y, w, 0, Graphics::kTextAlignCenter); // Left
-	// _largeFont->drawString(_screen, text.c_str(), x - 2, y, 640, 0); // Left
-	_largeFont->drawString(_screen, text.c_str(), x + 1, y, w, 0, Graphics::kTextAlignCenter); // Right
-	// _largeFont->drawString(_screen, text.c_str(), x + 2, y, 640, 0); // Right
-	_largeFont->drawString(_screen, text.c_str(), x, y - 1, w, 0, Graphics::kTextAlignCenter); // Top
-	// _largeFont->drawString(_screen, text.c_str(), x, y - 2, 640, 0); // Top
-	_largeFont->drawString(_screen, text.c_str(), x, y + 1, w, 0, Graphics::kTextAlignCenter); // Bottom
-	// _largeFont->drawString(_screen, text.c_str(), x, y + 2, 640, 0); // Bottom
+	// _largeFont->drawString(_screen, text.c_str(), x - 1, y, w, 0, Graphics::kTextAlignCenter); // Left
+	// // _largeFont->drawString(_screen, text.c_str(), x - 2, y, w, 0, Graphics::kTextAlignCenter); // Left
+	// _largeFont->drawString(_screen, text.c_str(), x + 1, y, w, 0, Graphics::kTextAlignCenter); // Right
+	// // _largeFont->drawString(_screen, text.c_str(), x + 2, y, w, 0, Graphics::kTextAlignCenter); // Right
+	// _largeFont->drawString(_screen, text.c_str(), x, y - 1, w, 0, Graphics::kTextAlignCenter); // Top
+	// // _largeFont->drawString(_screen, text.c_str(), x, y - 2, w, 0, Graphics::kTextAlignCenter); // Top
+	// _largeFont->drawString(_screen, text.c_str(), x, y + 1, w, 0, Graphics::kTextAlignCenter); // Bottom
+	// // _largeFont->drawString(_screen, text.c_str(), x, y + 2, w, 0, Graphics::kTextAlignCenter); // Bottom
 
 	// Draw main text on top
 	_largeFont->drawString(_screen, text.c_str(), x, y, w, color, Graphics::kTextAlignCenter);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 6e744c3761e..875f38380b7 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -142,8 +142,6 @@ private:
 	int yAlfred = 302;
 	int dirAlfred = 0;
 	int curAlfredFrame = 0;
-	bool isAlfredWalking = false;
-	bool isAlfredTalking = false;
 	uint16 mouseX = 0;
 	uint16 mouseY = 0;
 	byte *_cursorMasks[5] = {nullptr};
@@ -184,6 +182,8 @@ protected:
 
 public:
 	Graphics::Screen *_screen = nullptr;
+	bool isAlfredWalking = false;
+	bool isAlfredTalking = false;
 
 public:
 	PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc);
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 766df2dc150..10f9bdaf2a6 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -47,6 +47,8 @@ enum VerbIcons {
 	UNKNOWN
 };
 
+
+
 static const uint32 kLongClickDuration = 500; // 500ms for long click
 const int kCursorWidth = 16;
 const int kCursorHeight = 18;
@@ -72,20 +74,21 @@ const int kTextCharDisplayTime = 100; // 10ms per character
 #define MAX_MOVEMENT_STEPS 100 // 500 bytes / 5 bytes per step
 #define PATH_END 0xFF          // End of path marker
 
-#define MAX_CHARS_PER_LINE 0x2F // 47 characters
-#define MAX_LINES 5             // Maximum number of lines per page (0-indexed check against 4)
+#define MAX_CHARS_PER_LINE  0x2F  // 47 characters
+#define MAX_LINES 5  // Maximum number of lines per page (0-indexed check against 4)
 
 // Control character codes (negative values in signed char)
-#define CHAR_SPACE 0x20        /* ' ' */
-#define CHAR_END_MARKER_1 0xFD /* -3 (end of text marker) */
-#define CHAR_END_MARKER_2 0xF4 /* -0xC (alternate end marker) */
-#define CHAR_END_MARKER_3 0xF8 /* -8 (another end marker) */
-#define CHAR_END_MARKER_4 0xF0 /* -0x10 (another end marker) */
-#define CHAR_NEWLINE 0xF6      /* -10 (newline marker) */
-#define CHAR_PAGE_BREAK 0xF9   /* marker inserted when switching pages */
+#define CHAR_SPACE 0x20           /* ' ' */
+#define CHAR_END_MARKER_1 0xFD    /* -3 (end of text marker) */
+#define CHAR_END_MARKER_2 0xF4    /* -0xC (alternate end marker) */
+#define CHAR_END_MARKER_3 0xF8    /* -8 (another end marker) */
+#define CHAR_END_MARKER_4 0xF0    /* -0x10 (another end marker) */
+#define CHAR_NEWLINE 0xF6         /* -10 (newline marker) */
+#define CHAR_PAGE_BREAK 0xF9      /* marker inserted when switching pages */
 
 #define ALFRED_COLOR 0x0D
 
+
 typedef struct {
 	uint8_t flags;       /* Direction flags (see MOVE_* constants) */
 	uint16_t distance_x; // Horizontal distance to move


Commit: 7148baeb8704be2a2dce2d8bbee701fddae27533
    https://github.com/scummvm/scummvm/commit/7148baeb8704be2a2dce2d8bbee701fddae27533
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:07+02:00

Commit Message:
PELROCK: Eliminates extra space in text dialog

Changed paths:
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 5300ae37dae..2b4bc07ea21 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1338,7 +1338,7 @@ void PelrockEngine::renderText(Common::Array<Common::String> lines, int color) {
 		int lineSize = lines.size();
 		for (size_t i = 0; i < lines.size(); i++) {
 			int textX = baseX - (maxW / 2);
-			int textY = baseY - (lineSize * 20) + (i * 20);
+			int textY = baseY - (lineSize * 25) + (i * 25);
 			drawText(lines[i], textX, textY, maxW, color);
 		}
 	}
@@ -1978,7 +1978,7 @@ Common::Array<Common::Array<Common::String> > wordWrap(Common::String text) {
 		// # Key decision: if word_length > chars_remaining, wrap to next line
 		if (wordLength > charsRemaining) {
 			// Word is longer than the entire line - need to split
-			currentPage.push_back(joinStrings(currentLine, " "));
+			currentPage.push_back(joinStrings(currentLine, ""));
 			currentLine.clear();
 			charsRemaining = MAX_CHARS_PER_LINE;
 			currentLineNum++;


Commit: b007fac21bb17e26e9375fe52f113204644ead49
    https://github.com/scummvm/scummvm/commit/b007fac21bb17e26e9375fe52f113204644ead49
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:07+02:00

Commit Message:
PELROCK: Click on action

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 2b4bc07ea21..a9201c69717 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -91,6 +91,15 @@ void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byt
 	surface->drawLine(x, y, x, y + h, color);
 	surface->drawLine(x + w, y, x + w, y + h, color);
 }
+void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color) {
+	// debug("Drawing rect at (%d,%d) w=%d h=%d color=%d", x, y, w, h, color);
+	surface->drawLine(x, y, x + w, y, color);
+	surface->drawLine(x, y + h, x + w, y + h, color);
+	surface->drawLine(x, y, x, y + h, color);
+	surface->drawLine(x + w, y, x + w, y + h, color);
+}
+
+
 Common::Array<Common::Array<Common::String> > wordWrap(Common::String text);
 Common::Error PelrockEngine::run() {
 	// Initialize 320x200 paletted graphics mode
@@ -465,9 +474,7 @@ char32_t decodeByte(byte b) {
 void PelrockEngine::talk() {
 	if (_currentRoomConversations.size() == 0)
 		return;
-	int x = _currentRoomHotspots[0].x;
-	int y = _currentRoomHotspots[0].y;
-	debug("Say %s", _currentRoomConversations[0].text.c_str());
+
 	// showDescription(_currentRoomConversations[0].text, x, y, _currentRoomConversations[0].speakerId);
 	// for(int i = 0; i < _currentRoomConversations[0].choices.size(); i++) {
 	// 	int idx = _currentRoomConversations.size() - 1 - i;
@@ -843,10 +850,10 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	Common::Array<AnimSet> anims = loadRoomAnimations(roomFile, roomOffset);
 
 	Common::Array<HotSpot> hotspots;
-	int count = 0;
 	for (int i = 0; i < anims.size(); i++) {
 
 		HotSpot thisHotspot;
+		thisHotspot.index = i;
 		thisHotspot.x = anims[i].x;
 		thisHotspot.y = anims[i].y;
 		thisHotspot.w = anims[i].w;
@@ -855,7 +862,6 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 		thisHotspot.type = anims[i].actionFlags;
 		thisHotspot.isEnabled = !anims[i].isDisabled;
 		hotspots.push_back(thisHotspot);
-		count++;
 	}
 
 	Common::Array<HotSpot> staticHotspots = loadHotspots(roomFile, roomOffset);
@@ -866,6 +872,7 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	debug("total descriptions = %d, anims = %d, hotspots = %d", descriptions.size(), anims.size(), staticHotspots.size());
 	for (int i = 0; i < staticHotspots.size(); i++) {
 		HotSpot hotspot = staticHotspots[i];
+		hotspot.index = anims.size() + i;
 		hotspots.push_back(hotspot);
 	}
 
@@ -879,12 +886,12 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 
 	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
 		HotSpot hotspot = _currentRoomHotspots[i];
-		drawRect(_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 200 + i);
+		// drawRect(_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 200 + i);
 	}
 
 	for (int i = 0; i < _currentRoomExits.size(); i++) {
 		Exit exit = _currentRoomExits[i];
-		drawRect(_screen, exit.x, exit.y, exit.w, exit.h, 100 + i);
+		// drawRect(_screen, exit.x, exit.y, exit.w, exit.h, 100 + i);
 	}
 }
 
@@ -1100,8 +1107,8 @@ void drawSpriteToBuffer(byte *buffer, int bufferWidth,
 	}
 }
 
-Common::List<VerbIcons> PelrockEngine::populateActionsMenu(HotSpot *hotspot) {
-	Common::List<VerbIcons> verbs;
+Common::Array<VerbIcons> PelrockEngine::availableActions(HotSpot *hotspot) {
+	Common::Array<VerbIcons> verbs;
 	debug("Populating actions menu for hotspot type %d", hotspot->type);
 	verbs.push_back(LOOK);
 
@@ -1184,7 +1191,7 @@ void PelrockEngine::frames() {
 		if (isAlfredWalking) {
 
 			MovementStep step = _currentContext.movement_buffer[_current_step];
-			debug("Alfred step: distance_x=%d, distance_y=%d", step.distance_x, step.distance_y);
+			// debug("Alfred step: distance_x=%d, distance_y=%d", step.distance_x, step.distance_y);
 
 			if (step.distance_x > 0) {
 				if (step.flags & MOVE_RIGHT) {
@@ -1213,12 +1220,12 @@ void PelrockEngine::frames() {
 			if (step.distance_y > 0)
 				step.distance_y -= MIN((uint16_t)6, step.distance_y);
 
-			debug("Alfred position after step: x=%d, y=%d, step distance_x=%d, step distance_y=%d", xAlfred, yAlfred, step.distance_x, step.distance_y);
+			// debug("Alfred position after step: x=%d, y=%d, step distance_x=%d, step distance_y=%d", xAlfred, yAlfred, step.distance_x, step.distance_y);
 			if (step.distance_x <= 0 && step.distance_y <= 0) {
-				debug("Alfred completed step %d", _current_step);
+				// debug("Alfred completed step %d", _current_step);
 				_current_step++;
 				if (_current_step >= _currentContext.movement_count) {
-					debug("Alfred reached his walk target.");
+					// debug("Alfred reached his walk target.");
 					_current_step = 0;
 					isAlfredWalking = false;
 				}
@@ -1234,7 +1241,7 @@ void PelrockEngine::frames() {
 				setScreen(exit->targetRoom, exit->dir);
 			}
 
-			debug("Drawing walking frame %d for direction %d", curAlfredFrame, dirAlfred);
+			// debug("Drawing walking frame %d for direction %d", curAlfredFrame, dirAlfred);
 
 			if (curAlfredFrame >= walkingAnimLengths[dirAlfred]) {
 				curAlfredFrame = 0;
@@ -1298,7 +1305,7 @@ void PelrockEngine::frames() {
 		for (int i = 0; i < _currentRoomWalkboxes.size(); i++) {
 			// debug("Drawing walkbox %d", i);
 			WalkBox box = _currentRoomWalkboxes[i];
-			drawRect(_screen, box.x, box.y, box.w, box.h, 150 + i);
+			// drawRect(_screen, box.x, box.y, box.w, box.h, 150 + i);
 		}
 		if (_curWalkTarget.x < 640 && _curWalkTarget.y < 400 && _curWalkTarget.x >= 0 && _curWalkTarget.y >= 0) {
 			_screen->setPixel(_curWalkTarget.x, _curWalkTarget.y, 100);
@@ -1324,6 +1331,9 @@ void PelrockEngine::frames() {
 	}
 }
 
+void PelrockEngine::doAction(byte object, byte action) {
+}
+
 void PelrockEngine::renderText(Common::Array<Common::String> lines, int color) {
 	if (color == ALFRED_COLOR) {
 		int baseX = xAlfred;
@@ -1395,19 +1405,33 @@ Exit *PelrockEngine::isExitUnder(int x, int y) {
 	return nullptr;
 }
 
+void blitSurfaceToBuffer(Graphics::Surface *surface, byte *buffer, int bufferWidth, int bufferHeight, int destX, int destY) {
+    for (int y = 0; y < surface->h; y++) {
+        for (int x = 0; x < surface->w; x++) {
+            int px = destX + x;
+            int py = destY + y;
+            if (px >= 0 && px < bufferWidth && py >= 0 && py < bufferHeight) {
+                byte pixel = *((byte *)surface->getBasePtr(x, y));
+                buffer[py * bufferWidth + px] = pixel;
+            }
+        }
+    }
+}
+
 void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 
 	drawSpriteToBuffer(_compositeBuffer, 640, _popUpBalloon + (curFrame * kBalloonHeight * kBalloonWidth), posx, posy, kBalloonWidth, kBalloonHeight, 255);
-	Common::List<VerbIcons> availableActions = populateActionsMenu(_currentHotspot);
+	Common::Array<VerbIcons> actions = availableActions(_currentHotspot);
 
 	drawSpriteToBuffer(_compositeBuffer, 640, _verbIcons[LOOK], posx + 20, posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
-	for (Common::List<VerbIcons>::iterator i = availableActions.begin(); i != availableActions.end(); i++) {
-		VerbIcons verb = *i;
-		int index = 0;
-		for (Common::List<VerbIcons>::iterator j = availableActions.begin(); j != i; j++) {
-			index++;
-		}
-		drawSpriteToBuffer(_compositeBuffer, 640, _verbIcons[verb], posx + 20 + (index * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
+	// Graphics::Surface rects;
+	// rects.create(kVerbIconWidth, kVerbIconHeight, Graphics::PixelFormat::createFormatCLUT8());
+	// drawRect(&rects, 0, 0, kVerbIconWidth, kVerbIconHeight, 255);
+
+	// blitSurfaceToBuffer(&rects, _compositeBuffer, 640, 480, posx + ver, posy + 20);
+
+	for(int i = 0; i < actions.size(); i++) {
+		drawSpriteToBuffer(_compositeBuffer, 640, _verbIcons[actions[i]], posx + 20 + (i * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
 	}
 }
 
@@ -1418,32 +1442,32 @@ void PelrockEngine::walkTo(int x, int y) {
 	PathContext context = {NULL, NULL, NULL, 0, 0, 0};
 
 	pathFind(x, y, &context);
-	debug("\nPath Information:\n");
-	debug("================\n");
+	// debug("\nPath Information:\n");
+	// debug("================\n");
 
-	debug("Walkbox path (%d boxes): ", context.path_length);
+	// debug("Walkbox path (%d boxes): ", context.path_length);
 	for (int i = 0; i < context.path_length && context.path_buffer[i] != PATH_END; i++) {
 		debug("%d ", context.path_buffer[i]);
 	}
 
-	debug("Movement steps (%d steps):\n", context.movement_count);
+	// debug("Movement steps (%d steps):\n", context.movement_count);
 	for (int i = 0; i < context.movement_count; i++) {
 		MovementStep *step = &context.movement_buffer[i];
-		debug("  Step %d: ", i);
+		// debug("  Step %d: ", i);
 
-		if (step->flags & MOVE_RIGHT)
-			debug("RIGHT ");
-		if (step->flags & MOVE_LEFT)
-			debug("LEFT ");
-		if (step->flags & MOVE_DOWN)
-			debug("DOWN ");
-		if (step->flags & MOVE_UP)
-			debug("UP ");
+		// if (step->flags & MOVE_RIGHT)
+		// 	debug("RIGHT ");
+		// if (step->flags & MOVE_LEFT)
+		// 	debug("LEFT ");
+		// if (step->flags & MOVE_DOWN)
+		// 	debug("DOWN ");
+		// if (step->flags & MOVE_UP)
+		// 	debug("UP ");
 
-		debug("(dx=%d, dy=%d)\n", step->distance_x, step->distance_y);
+		// debug("(dx=%d, dy=%d)\n", step->distance_x, step->distance_y);
 	}
 
-	debug("\nCompressed path (%d bytes): ", context.compressed_length);
+	// debug("\nCompressed path (%d bytes): ", context.compressed_length);
 	for (int i = 0; i < context.compressed_length; i++) {
 		debug("%02X ", context.compressed_path[i]);
 	}
@@ -1457,9 +1481,9 @@ void PelrockEngine::walkTo(int x, int y) {
 	// } else if (y > yAlfred) {
 	// 	dirAlfred = DOWN;
 	// }
-	debug("Setting Alfred to walk towards (%d, %d) from (%d, %d) in direction %d", x, y, xAlfred, yAlfred, dirAlfred);
+	// debug("Setting Alfred to walk towards (%d, %d) from (%d, %d) in direction %d", x, y, xAlfred, yAlfred, dirAlfred);
 	_currentContext = context;
-	debug("Path find complete, movement count: %d", _currentContext.movement_count);
+	// debug("Path find complete, movement count: %d", _currentContext.movement_count);
 }
 
 bool PelrockEngine::pathFind(int x, int y, PathContext *context) {
@@ -1754,9 +1778,34 @@ uint8_t PelrockEngine::find_walkbox_for_point(uint16_t x, uint16_t y) {
 
 void PelrockEngine::checkMouseClick(int x, int y) {
 
+
+	if(_displayPopup) {
+		Common::Array<VerbIcons> actions = availableActions(_currentHotspot);
+
+		Common::Rect lookRect = Common::Rect(_popupX + 20, _popupY + 20, _popupX + 20 + kVerbIconWidth, _popupY + 20 +kVerbIconHeight);
+		// debug("Look rect: x=%d, y=%d, w=%d, h=%d", lookRect.left, lookRect.top, lookRect, lookRect.h);
+		if(lookRect.contains(x, y)) {
+			debug("Look action clicked");
+			walkTo(_currentHotspot->x, _currentHotspot->y);
+			sayAlfred(_currentRoomDescriptions[_currentHotspot->index].text);
+			_displayPopup = false;
+			return;
+		}
+		for(int i = 0; i < actions.size(); i++) {
+			Common::Rect actionRect = Common::Rect(_popupX + 20 + (i * (kVerbIconWidth + 2)), _popupY + 20, kVerbIconWidth, kVerbIconHeight);
+			if(actionRect.contains(x, y)) {
+				doAction(actions[i], _currentHotspot->extra);
+				_displayPopup = false;
+				return;
+			}
+		}
+	}
+
 	_displayPopup = false;
 	_currentHotspot = nullptr;
 
+
+
 	Common::Point walkTarget = calculateWalkTarget(mouseX, mouseY);
 	_curWalkTarget = walkTarget;
 	// debug("Calculated walk target at (%d, %d)", walkTarget.x, walkTarget.y);
@@ -1769,11 +1818,11 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 	} else {*/
 	/*	} */
 
-	int hotspotIndex = isHotspotUnder(mouseX, mouseY);
-	if (hotspotIndex != -1) {
-		sayAlfred(_currentRoomDescriptions[hotspotIndex].text);
-		debug("Hotspot clicked: %d", _currentRoomHotspots[hotspotIndex].extra);
-	}
+	// int hotspotIndex = isHotspotUnder(mouseX, mouseY);
+	// if (hotspotIndex != -1) {
+	// 	sayAlfred(_currentRoomDescriptions[hotspotIndex].text);
+	// 	debug("Hotspot clicked: %d", _currentRoomHotspots[hotspotIndex].extra);
+	// }
 
 	walkTo(walkTarget.x, walkTarget.y);
 }
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 875f38380b7..ba3716dbb72 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -96,13 +96,14 @@ private:
 	byte *grabBackgroundSlice(int x, int y, int w, int h);
 	void putBackgroundSlice(int x, int y, int w, int h, byte *slice);
 
-	Common::List<VerbIcons> populateActionsMenu(HotSpot *hotspot);
+	Common::Array<VerbIcons> availableActions(HotSpot *hotspot);
 	Common::Point calculateWalkTarget(int mouseX, int mouseY);
 	void drawText(Common::String text, int x, int y, int w, byte color);
 
 	void sayAlfred(Common::String text);
 	// render loop
 	void frames();
+	void doAction(byte object, byte action);
 	void renderText(Common::Array<Common::String> lines, int color);
 	void drawAlfred(byte *buf);
 	void checkMouseHover();
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 10f9bdaf2a6..f1487f09cca 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -62,6 +62,7 @@ const int kBalloonWidth = 247;
 const int kBalloonHeight = 112;
 const int kBalloonFrames = 4;
 const int kTextCharDisplayTime = 100; // 10ms per character
+const int kVerbIconPadding = 20;
 
 // Direction flags (bit-packed)
 #define MOVE_RIGHT 0x01 // Move right (positive X)
@@ -150,6 +151,7 @@ struct AnimSet {
 };
 
 struct HotSpot {
+	int index;
 	int id;
 	int x;
 	int y;


Commit: 75f2713f77cad7748925bafd4809b66b9ebe8daa
    https://github.com/scummvm/scummvm/commit/75f2713f77cad7748925bafd4809b66b9ebe8daa
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:08+02:00

Commit Message:
PELROCK: Better long click handling

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index a9201c69717..b59d4bf9ae8 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -99,7 +99,6 @@ void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color
 	surface->drawLine(x + w, y, x + w, y + h, color);
 }
 
-
 Common::Array<Common::Array<Common::String> > wordWrap(Common::String text);
 Common::Error PelrockEngine::run() {
 	// Initialize 320x200 paletted graphics mode
@@ -145,18 +144,26 @@ Common::Error PelrockEngine::run() {
 				mouseY = e.mouse.y;
 				// debug(3, "Mouse moved to (%d,%d)", mouseX, mouseY);
 			} else if (e.type == Common::EVENT_LBUTTONDOWN) {
-				_mouseDownTime = g_system->getMillis();
-				_isMouseDown = true;
-			} else if (e.type == Common::EVENT_LBUTTONUP) {
-				if (_isMouseDown) {
-					uint32 clickDuration = g_system->getMillis() - _mouseDownTime;
-					if (clickDuration >= kLongClickDuration) {
-						checkLongMouseClick(e.mouse.x, e.mouse.y);
-					} else {
-						checkMouseClick(e.mouse.x, e.mouse.y);
-					}
-					_isMouseDown = false;
+				debug("long button down");
+				if (!_isMouseDown) {
+					_mouseClickTime = g_system->getMillis();
+					_isMouseDown = true;
 				}
+			} else if (e.type == Common::EVENT_LBUTTONUP) {
+				_isMouseDown = false;
+				// if (!_longClick) {
+				checkMouseClick(e.mouse.x, e.mouse.y);
+				_displayPopup = false;
+				// }
+				_longClick = false;
+			}
+		}
+		if (_isMouseDown) {
+			if (g_system->getMillis() - _mouseClickTime >= kLongClickDuration) {
+				debug("long click!");
+				_longClick = true;
+				_isMouseDown = false;
+				checkLongMouseClick(e.mouse.x, e.mouse.y);
 			}
 		}
 		checkMouseHover();
@@ -408,12 +415,11 @@ Common::Array<Description> PelrockEngine::loadRoomDescriptions(Common::File *roo
 	uint32_t pos = 0;
 	uint32_t lastDescPos = 0;
 	while (pos < (pair12_size)) {
-		// char *desc = new char[256];
 		int desc_pos = 0;
 		if (data[pos] == 0xFF) {
 			Description description;
-			description.itemId = data[++pos];
-			pos += 2;
+			description.itemId = data[pos + 1];
+			pos += 3;
 			description.index = data[pos++];
 			description.text = "";
 			// debug("Found description terminator");
@@ -422,6 +428,12 @@ Common::Array<Description> PelrockEngine::loadRoomDescriptions(Common::File *roo
 				if (data[pos] != 0x00) {
 					description.text.append(1, (char)data[pos]);
 				}
+				if (data[pos] == 0xF8) {
+
+					description.actionTrigger = data[pos + 1] | data[pos + 2] << 8;
+					pos += 2;
+					continue;
+				}
 				// desc[desc_pos++] = (char)data[pos];
 				// debug("Current desc: %s", desc);
 				pos++;
@@ -471,10 +483,17 @@ char32_t decodeByte(byte b) {
 	}
 }
 
-void PelrockEngine::talk() {
+void PelrockEngine::talk(byte object) {
 	if (_currentRoomConversations.size() == 0)
 		return;
 
+	AnimSet *animSet;
+	for (int i = 0; i < _currentRoomAnims.size(); i++) {
+		if (_currentRoomAnims[i].extra == object) {
+			AnimSet *animSet = &_currentRoomAnims[i];
+		}
+	}
+
 	// showDescription(_currentRoomConversations[0].text, x, y, _currentRoomConversations[0].speakerId);
 	// for(int i = 0; i < _currentRoomConversations[0].choices.size(); i++) {
 	// 	int idx = _currentRoomConversations.size() - 1 - i;
@@ -886,7 +905,7 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 
 	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
 		HotSpot hotspot = _currentRoomHotspots[i];
-		// drawRect(_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 200 + i);
+		drawRect(_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 200 + i);
 	}
 
 	for (int i = 0; i < _currentRoomExits.size(); i++) {
@@ -1109,7 +1128,6 @@ void drawSpriteToBuffer(byte *buffer, int bufferWidth,
 
 Common::Array<VerbIcons> PelrockEngine::availableActions(HotSpot *hotspot) {
 	Common::Array<VerbIcons> verbs;
-	debug("Populating actions menu for hotspot type %d", hotspot->type);
 	verbs.push_back(LOOK);
 
 	if (hotspot->type & 1) {
@@ -1332,6 +1350,9 @@ void PelrockEngine::frames() {
 }
 
 void PelrockEngine::doAction(byte object, byte action) {
+	if (action == TALK) {
+		talk(object);
+	}
 }
 
 void PelrockEngine::renderText(Common::Array<Common::String> lines, int color) {
@@ -1406,16 +1427,16 @@ Exit *PelrockEngine::isExitUnder(int x, int y) {
 }
 
 void blitSurfaceToBuffer(Graphics::Surface *surface, byte *buffer, int bufferWidth, int bufferHeight, int destX, int destY) {
-    for (int y = 0; y < surface->h; y++) {
-        for (int x = 0; x < surface->w; x++) {
-            int px = destX + x;
-            int py = destY + y;
-            if (px >= 0 && px < bufferWidth && py >= 0 && py < bufferHeight) {
-                byte pixel = *((byte *)surface->getBasePtr(x, y));
-                buffer[py * bufferWidth + px] = pixel;
-            }
-        }
-    }
+	for (int y = 0; y < surface->h; y++) {
+		for (int x = 0; x < surface->w; x++) {
+			int px = destX + x;
+			int py = destY + y;
+			if (px >= 0 && px < bufferWidth && py >= 0 && py < bufferHeight) {
+				byte pixel = *((byte *)surface->getBasePtr(x, y));
+				buffer[py * bufferWidth + px] = pixel;
+			}
+		}
+	}
 }
 
 void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
@@ -1430,7 +1451,7 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 
 	// blitSurfaceToBuffer(&rects, _compositeBuffer, 640, 480, posx + ver, posy + 20);
 
-	for(int i = 0; i < actions.size(); i++) {
+	for (int i = 0; i < actions.size(); i++) {
 		drawSpriteToBuffer(_compositeBuffer, 640, _verbIcons[actions[i]], posx + 20 + (i * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
 	}
 }
@@ -1778,22 +1799,24 @@ uint8_t PelrockEngine::find_walkbox_for_point(uint16_t x, uint16_t y) {
 
 void PelrockEngine::checkMouseClick(int x, int y) {
 
-
-	if(_displayPopup) {
+	if (_displayPopup) {
 		Common::Array<VerbIcons> actions = availableActions(_currentHotspot);
 
-		Common::Rect lookRect = Common::Rect(_popupX + 20, _popupY + 20, _popupX + 20 + kVerbIconWidth, _popupY + 20 +kVerbIconHeight);
+		Common::Rect lookRect = Common::Rect(_popupX + 20, _popupY + 20, _popupX + 20 + kVerbIconWidth, _popupY + 20 + kVerbIconHeight);
 		// debug("Look rect: x=%d, y=%d, w=%d, h=%d", lookRect.left, lookRect.top, lookRect, lookRect.h);
-		if(lookRect.contains(x, y)) {
+		if (lookRect.contains(x, y)) {
 			debug("Look action clicked");
 			walkTo(_currentHotspot->x, _currentHotspot->y);
 			sayAlfred(_currentRoomDescriptions[_currentHotspot->index].text);
 			_displayPopup = false;
 			return;
 		}
-		for(int i = 0; i < actions.size(); i++) {
-			Common::Rect actionRect = Common::Rect(_popupX + 20 + (i * (kVerbIconWidth + 2)), _popupY + 20, kVerbIconWidth, kVerbIconHeight);
-			if(actionRect.contains(x, y)) {
+		for (int i = 0; i < actions.size(); i++) {
+			debug("Checking action %d at index %d for mouse click = %d, %d", actions[i], i, x, y);
+			int x = _popupX + 20 + (i * (kVerbIconWidth + 2));
+			int y = _popupY + 20;
+			Common::Rect actionRect = Common::Rect(x, y, x + kVerbIconWidth, y + kVerbIconHeight);
+			if (actionRect.contains(x, y)) {
 				doAction(actions[i], _currentHotspot->extra);
 				_displayPopup = false;
 				return;
@@ -1804,11 +1827,9 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 	_displayPopup = false;
 	_currentHotspot = nullptr;
 
-
-
 	Common::Point walkTarget = calculateWalkTarget(mouseX, mouseY);
 	_curWalkTarget = walkTarget;
-	// debug("Calculated walk target at (%d, %d)", walkTarget.x, walkTarget.y);
+
 	// Exit *exit = isExitUnder(walkTarget.x, walkTarget.y);
 
 	/*if (exit != nullptr) {
@@ -1818,12 +1839,6 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 	} else {*/
 	/*	} */
 
-	// int hotspotIndex = isHotspotUnder(mouseX, mouseY);
-	// if (hotspotIndex != -1) {
-	// 	sayAlfred(_currentRoomDescriptions[hotspotIndex].text);
-	// 	debug("Hotspot clicked: %d", _currentRoomHotspots[hotspotIndex].extra);
-	// }
-
 	walkTo(walkTarget.x, walkTarget.y);
 }
 
@@ -1845,7 +1860,9 @@ void PelrockEngine::checkMouseHover() {
 	}
 
 	int hotspotIndex = isHotspotUnder(mouseX, mouseY);
+
 	if (hotspotIndex != -1) {
+		debug("Hotspot under mouse: %d, (%d,%d)", hotspotIndex, _currentRoomHotspots[hotspotIndex].x, _currentRoomHotspots[hotspotIndex].y);
 		isSomethingUnder = true;
 	}
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index ba3716dbb72..712138ccc8b 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -88,7 +88,7 @@ private:
 									 uint16_t dest_x, uint16_t dest_y,
 									 MovementStep *movement_buffer);
 
-	void talk();
+	void talk(byte object);
 	Common::String getControlName(byte b);
 	void loadRoomMetadata(Common::File *roomFile, int roomOffset);
 	void loadCursors();
@@ -147,8 +147,9 @@ private:
 	uint16 mouseY = 0;
 	byte *_cursorMasks[5] = {nullptr};
 
-	uint32 _mouseDownTime;
-	bool _isMouseDown;
+	uint32 _mouseClickTime;
+	bool _isMouseDown = false;
+	bool _longClick = false;
 
 	byte *_verbIcons[9] = {nullptr};
 	byte *_popUpBalloon = nullptr;
@@ -156,6 +157,7 @@ private:
 	byte *_currentBackground; // Clean background - NEVER modified
 	byte *_compositeBuffer;   // Working composition buffer
 
+	bool _lMouseDown = false;
 	bool _displayPopup = false;
 	int _popupX = 0;
 	int _popupY = 0;
@@ -171,12 +173,12 @@ private:
 	GameState stateGame = GAME;
 	bool gameInitialized = false;
 	bool screenReady = false;
-	int prevDirX = 0;
-	int prevDirY = 0;
-	Common::String objectToShow = "";
-	int prevWhichScreen = 0;
-	int whichScreen = 0;
-	byte *pixelsShadows; // =new int[640*400];
+	// int prevDirX = 0;
+	// int prevDirY = 0;
+	// Common::String objectToShow = "";
+	// int prevWhichScreen = 0;
+	// int whichScreen = 0;
+	// byte *pixelsShadows; // =new int[640*400];
 protected:
 	// Engine APIs
 	Common::Error run() override;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index f1487f09cca..1ba82902c2a 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -207,6 +207,8 @@ struct StackEntry {
 struct Description {
 	byte itemId;
 	byte index;
+	bool isAction = false;
+	uint16 actionTrigger;
 	Common::String text;
 };
 


Commit: 262ff0cfb353d9205192e76234f1876de09cb24c
    https://github.com/scummvm/scummvm/commit/262ff0cfb353d9205192e76234f1876de09cb24c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:08+02:00

Commit Message:
PELROCK: Fixes wrong description loading

Changed paths:
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index b59d4bf9ae8..ad8d91652d8 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -429,10 +429,10 @@ Common::Array<Description> PelrockEngine::loadRoomDescriptions(Common::File *roo
 					description.text.append(1, (char)data[pos]);
 				}
 				if (data[pos] == 0xF8) {
-
 					description.actionTrigger = data[pos + 1] | data[pos + 2] << 8;
+					debug("Found action trigger: %d", description.actionTrigger);
 					pos += 2;
-					continue;
+					break;
 				}
 				// desc[desc_pos++] = (char)data[pos];
 				// debug("Current desc: %s", desc);
@@ -1862,7 +1862,6 @@ void PelrockEngine::checkMouseHover() {
 	int hotspotIndex = isHotspotUnder(mouseX, mouseY);
 
 	if (hotspotIndex != -1) {
-		debug("Hotspot under mouse: %d, (%d,%d)", hotspotIndex, _currentRoomHotspots[hotspotIndex].x, _currentRoomHotspots[hotspotIndex].y);
 		isSomethingUnder = true;
 	}
 


Commit: ac8813f39d0717512f011393904b74f06e527759
    https://github.com/scummvm/scummvm/commit/ac8813f39d0717512f011393904b74f06e527759
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:08+02:00

Commit Message:
PELROCK: Loads NPCs Talking anims

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index ad8d91652d8..1adafea6b37 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -484,15 +484,19 @@ char32_t decodeByte(byte b) {
 }
 
 void PelrockEngine::talk(byte object) {
+	debug("Talking to object %d", object);
 	if (_currentRoomConversations.size() == 0)
 		return;
 
 	AnimSet *animSet;
 	for (int i = 0; i < _currentRoomAnims.size(); i++) {
 		if (_currentRoomAnims[i].extra == object) {
-			AnimSet *animSet = &_currentRoomAnims[i];
+			animSet = &_currentRoomAnims[i];
 		}
 	}
+	isNPCATalking = true;
+	NPCTalking = animSet->extra;
+
 
 	// showDescription(_currentRoomConversations[0].text, x, y, _currentRoomConversations[0].speakerId);
 	// for(int i = 0; i < _currentRoomConversations[0].choices.size(); i++) {
@@ -914,6 +918,118 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	}
 }
 
+void readUntilBuda(Common::SeekableReadStream *stream, byte *&buffer, size_t &outSize) {
+ const char marker[] = "BUDA";
+    const int markerLen = 4;
+    size_t bufferSize = 4096;
+    size_t pos = 0;
+
+    buffer = (byte *)malloc(bufferSize);
+
+    while (!stream->eos()) {
+        byte b = stream->readByte();
+        if (pos + 1 > bufferSize) {
+            bufferSize *= 2;
+            buffer = (byte *)realloc(buffer, bufferSize);
+        }
+        buffer[pos++] = b;
+
+        // Check for marker at the end of buffer
+        if (pos >= markerLen &&
+            buffer[pos - 4] == 'B' &&
+            buffer[pos - 3] == 'U' &&
+            buffer[pos - 2] == 'D' &&
+            buffer[pos - 1] == 'A') {
+            break;
+        }
+    }
+    outSize = pos;
+}
+
+void PelrockEngine::loadRoomTalkingAnimations(int roomNumber) {
+
+	int headerIndex = roomNumber;
+	uint32 offset = kTalkingAnimHeaderSize * headerIndex;
+
+	TalkinAnimHeader talkHeader;
+	Common::File talkFile;
+	if (!talkFile.open("ALFRED.2")) {
+		error("Couldnt find file ALFRED.2");
+	}
+
+	talkFile.seek(offset, SEEK_SET);
+
+	talkHeader.spritePointer = talkFile.readUint16LE();
+	talkHeader.unknown1 = talkFile.readByte();
+	talkFile.read(&talkHeader.unknown2, 4);
+	talkHeader.offsetX = talkFile.readByte();
+	talkHeader.offsetY = talkFile.readByte();
+	talkHeader.wAnimA = talkFile.readByte();
+	talkHeader.hAnimA = talkFile.readByte();
+	talkFile.read(&talkHeader.unknown3, 2);
+	talkHeader.numFramesAnimA = talkFile.readByte();
+	talkFile.read(&talkHeader.unknown4, 7);
+	talkHeader.wAnimB = talkFile.readByte();
+	talkHeader.hAnimB = talkFile.readByte();
+	talkHeader.unknown5 = talkFile.readByte();
+	talkHeader.numFramesAnimB = talkFile.readByte();
+	talkFile.read(&talkHeader.unknown6, 29);
+
+	if(talkHeader.spritePointer == 0) {
+		debug("No talking animation for room %d", roomNumber);
+		talkFile.close();
+		return;
+	}
+
+	if(talkHeader.animA != nullptr) {
+		delete[] talkHeader.animA;
+		talkHeader.animA = nullptr;
+	}
+	talkHeader.animA = new byte *[talkHeader.numFramesAnimA];
+
+
+	talkFile.seek(talkHeader.spritePointer, SEEK_SET);
+
+	byte *data = nullptr;
+	byte *decompressed = new byte[talkHeader.wAnimA * talkHeader.hAnimA * talkHeader.numFramesAnimA];
+	size_t dataSize = 0;
+	readUntilBuda(&talkFile, data, dataSize);
+	rleDecompress(data, dataSize, 0, dataSize, &decompressed);
+	debug("Decompressed talking anim A size: %zu", dataSize);
+	for(int i = 0; i < talkHeader.numFramesAnimA; i++) {
+		talkHeader.animA[i] = new byte[talkHeader.wAnimA * talkHeader.hAnimA];
+		Common::copy(decompressed + (i * talkHeader.wAnimA * talkHeader.hAnimA), decompressed + ((i + 1) * talkHeader.wAnimA * talkHeader.hAnimA), talkHeader.animA[i]);
+	}
+	free(data);
+	free(decompressed);
+
+
+	if(talkHeader.numFramesAnimB > 0) {
+		if(talkHeader.animB != nullptr) {
+			delete[] talkHeader.animB;
+			talkHeader.animB = nullptr;
+		}
+		talkHeader.animB = new byte *[talkHeader.numFramesAnimB];
+		// Read second animation frames
+		size_t offsetB = talkHeader.spritePointer + dataSize;
+		talkFile.seek(offsetB, SEEK_SET);
+		byte *dataB = nullptr;
+		byte *decompressedB = new byte[talkHeader.wAnimB * talkHeader.hAnimB * talkHeader.numFramesAnimB];
+		size_t dataBSize = 0;
+		readUntilBuda(&talkFile, dataB, dataBSize);
+		rleDecompress(dataB, 0, dataBSize, dataBSize, &decompressedB);
+		for(int i = 0; i < talkHeader.numFramesAnimB; i++) {
+			talkHeader.animB[i] = new byte[talkHeader.wAnimB * talkHeader.hAnimB];
+			Common::copy(decompressedB + (i * talkHeader.wAnimB * talkHeader.hAnimB), decompressedB + ((i + 1) * talkHeader.wAnimB * talkHeader.hAnimB), talkHeader.animB[i]);
+		}
+		free(dataB);
+		free(decompressedB);
+	}
+	_talkingAnimHeader = talkHeader;
+
+	talkFile.close();
+}
+
 void PelrockEngine::loadCursors() {
 	Common::File alfred7File;
 	if (!alfred7File.open("ALFRED.7")) {
@@ -1002,16 +1118,14 @@ Common::Array<HotSpot> PelrockEngine::loadHotspots(Common::File *roomFile, int r
 	for (int i = 0; i < hotspot_count; i++) {
 		uint32_t obj_offset = hotspot_data_start + i * 9;
 		roomFile->seek(obj_offset, SEEK_SET);
-		byte obj_bytes[9];
-		roomFile->read(obj_bytes, 9);
 		HotSpot spot;
-		spot.type = obj_bytes[0];
-		spot.x = obj_bytes[1] | (obj_bytes[2] << 8);
-		spot.y = obj_bytes[3] | (obj_bytes[4] << 8);
-		spot.w = obj_bytes[5];
-		spot.h = obj_bytes[6];
-		spot.extra = obj_bytes[7] | (obj_bytes[8] << 8);
-		// debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, spot.type, spot.x, spot.y, spot.w, spot.h, spot.extra);
+		spot.type = roomFile->readByte();
+		spot.x = roomFile->readUint16LE();
+		spot.y = roomFile->readUint16LE();
+		spot.w = roomFile->readByte();
+		spot.h = roomFile->readByte();
+		spot.extra = roomFile->readUint16LE();
+		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, spot.type, spot.x, spot.y, spot.w, spot.h, spot.extra);
 		hotspots.push_back(spot);
 	}
 	return hotspots;
@@ -1175,6 +1289,13 @@ void PelrockEngine::frames() {
 			int y = _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].y;
 			int w = _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].w;
 			int h = _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].h;
+			int extra = _currentRoomAnims[i].extra;
+
+			if(NPCTalking == extra) {
+				debug("Skipping anim set %d because NPC is talking", i);
+				talkNPC(&_currentRoomAnims[i]);
+				continue;
+			}
 
 			int frameSize = _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].w * _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].h;
 			int curFrame = _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curFrame;
@@ -1206,6 +1327,7 @@ void PelrockEngine::frames() {
 			}
 		}
 
+
 		if (isAlfredWalking) {
 
 			MovementStep step = _currentContext.movement_buffer[_current_step];
@@ -1349,7 +1471,7 @@ void PelrockEngine::frames() {
 	}
 }
 
-void PelrockEngine::doAction(byte object, byte action) {
+void PelrockEngine::doAction(byte action, byte object) {
 	if (action == TALK) {
 		talk(object);
 	}
@@ -1456,6 +1578,23 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	}
 }
 
+void PelrockEngine::talkNPC(AnimSet *animSet) {
+	//Change with the right index
+	int x = animSet->x + _talkingAnimHeader.offsetX;
+	int y = animSet->y + _talkingAnimHeader.offsetY;
+	int w = _talkingAnimHeader.wAnimA;
+	int h = _talkingAnimHeader.hAnimA;
+	int numFrames = _talkingAnimHeader.numFramesAnimA;
+	int curFrame = _talkingAnimHeader.currentFrameAnimA++;
+	if (curFrame >= numFrames) {
+		_talkingAnimHeader.currentFrameAnimA = 0;
+		curFrame = 0;
+	}
+	debug("Talking NPC frame %d/%d, x=%d, y=%d, w=%d, h=%d", curFrame, numFrames, x, y, w, h);
+
+	drawSpriteToBuffer(_compositeBuffer, 640, _talkingAnimHeader.animA[curFrame], x, y, w, h, 255);
+}
+
 void PelrockEngine::walkTo(int x, int y) {
 	isAlfredWalking = true;
 	curAlfredFrame = 0;
@@ -1799,9 +1938,13 @@ uint8_t PelrockEngine::find_walkbox_for_point(uint16_t x, uint16_t y) {
 
 void PelrockEngine::checkMouseClick(int x, int y) {
 
+	if(NPCTalking) NPCTalking = false;
+
 	if (_displayPopup) {
 		Common::Array<VerbIcons> actions = availableActions(_currentHotspot);
-
+		for (int i = 0; i < actions.size(); i++) {
+			debug("Available action %d at index %d", actions[i], i);
+		}
 		Common::Rect lookRect = Common::Rect(_popupX + 20, _popupY + 20, _popupX + 20 + kVerbIconWidth, _popupY + 20 + kVerbIconHeight);
 		// debug("Look rect: x=%d, y=%d, w=%d, h=%d", lookRect.left, lookRect.top, lookRect, lookRect.h);
 		if (lookRect.contains(x, y)) {
@@ -1811,12 +1954,15 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 			_displayPopup = false;
 			return;
 		}
-		for (int i = 0; i < actions.size(); i++) {
-			debug("Checking action %d at index %d for mouse click = %d, %d", actions[i], i, x, y);
+		for (int i = 1; i < actions.size(); i++) {
+
+			// debug("Checking action %d at index %d for mouse click = %d, %d", actions[i], i, x, y);
 			int x = _popupX + 20 + (i * (kVerbIconWidth + 2));
 			int y = _popupY + 20;
 			Common::Rect actionRect = Common::Rect(x, y, x + kVerbIconWidth, y + kVerbIconHeight);
+
 			if (actionRect.contains(x, y)) {
+				debug("Action %d clicked", actions[i]);
 				doAction(actions[i], _currentHotspot->extra);
 				_displayPopup = false;
 				return;
@@ -2140,6 +2286,7 @@ void PelrockEngine::setScreen(int number, int dir) {
 	}
 
 	loadRoomMetadata(&roomFile, roomOffset);
+	loadRoomTalkingAnimations(number);
 
 	_screen->markAllDirty();
 	roomFile.close();
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 712138ccc8b..89017ab9a08 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -91,6 +91,7 @@ private:
 	void talk(byte object);
 	Common::String getControlName(byte b);
 	void loadRoomMetadata(Common::File *roomFile, int roomOffset);
+	void loadRoomTalkingAnimations(int roomNumber);
 	void loadCursors();
 	void loadInteractionIcons();
 	byte *grabBackgroundSlice(int x, int y, int w, int h);
@@ -103,7 +104,7 @@ private:
 	void sayAlfred(Common::String text);
 	// render loop
 	void frames();
-	void doAction(byte object, byte action);
+	void doAction(byte action, byte object);
 	void renderText(Common::Array<Common::String> lines, int color);
 	void drawAlfred(byte *buf);
 	void checkMouseHover();
@@ -114,16 +115,19 @@ private:
 	Exit *isExitUnder(int x, int y);
 	AnimSet *isSpriteUnder(int x, int y);
 	void showActionBalloon(int posx, int posy, int curFrame);
+	void talkNPC(AnimSet *animSet);
 
 	ChronoManager *_chronoManager = nullptr;
 
 	// byte *standingAnim = new byte[3060 * 102];
 
-	byte **walkingAnimFrames[4];              // 4 arrays of arrays
-	byte *standingAnimFrames[4];              // 4 directions
-	int walkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
-	byte **talkingAnimFrames[4];              // 4 arrays of arrays
-	int talkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
+	byte **walkingAnimFrames[4];                // 4 arrays of arrays
+	byte *standingAnimFrames[4] = {nullptr};    // 4 directions
+	int walkingAnimLengths[4] = {8, 8, 4, 4};   // size of each inner array
+	byte **talkingAnimFrames[4];                // 4 arrays of arrays
+	int talkingAnimLengths[4] = {8, 8, 4, 4};   // size of each inner array
+
+	TalkinAnimHeader _talkingAnimHeader;
 
 	PathContext _currentContext;
 	int _current_step = 0;
@@ -168,7 +172,12 @@ private:
 	LargeFont *_largeFont = nullptr;
 
 	Common::Point _curWalkTarget;
+	bool isNPCATalking = false;
+	uint16 NPCTalking = 0;
+	bool isNPCBTalking = false;
+
 
+	//JAVA
 	bool shouldPlayIntro = false;
 	GameState stateGame = GAME;
 	bool gameInitialized = false;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 1ba82902c2a..803ab5f6cbb 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -54,6 +54,7 @@ const int kCursorWidth = 16;
 const int kCursorHeight = 18;
 const int kCursorSize = 288; // 16 * 18
 const int kRoomStructSize = 104;
+const int kTalkingAnimHeaderSize = 55;
 const int kNumRooms = 56;
 const int kVerbIconWidth = 60;
 const int kVerbIconHeight = 60;
@@ -162,6 +163,33 @@ struct HotSpot {
 	bool isEnabled = true;
 };
 
+struct TalkinAnimHeader {
+	uint16 spritePointer;
+	byte unknown1;
+	byte unknown2[4];
+
+	int8 offsetX;
+	int8 offsetY;
+
+	byte wAnimA;
+	byte hAnimA;
+	byte unknown3[2];
+	byte numFramesAnimA;
+	byte unknown4[7];
+
+	byte currentFrameAnimA;
+
+	byte wAnimB;
+	byte hAnimB;
+	byte unknown5;
+	byte numFramesAnimB;
+	byte unknown6[29];
+	byte currentFrameAnimB;
+
+	byte **animA = nullptr;
+	byte **animB = nullptr;
+};
+
 struct ConversationElement {
 	enum Type {
 		DIALOGUE,


Commit: e65b34ec6c46560c2c3cd125aa48edea645e5c0e
    https://github.com/scummvm/scummvm/commit/e65b34ec6c46560c2c3cd125aa48edea645e5c0e
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:09+02:00

Commit Message:
PELROCK: Reads second talking animation properly

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 1adafea6b37..1d8bf432a70 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -194,7 +194,8 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		setScreen(0, 1);
+		setScreen(5, 0);
+		// setScreen(13, 1);
 		// setScreen(2, 2);
 		// setScreen(28, 0);
 	}
@@ -497,7 +498,6 @@ void PelrockEngine::talk(byte object) {
 	isNPCATalking = true;
 	NPCTalking = animSet->extra;
 
-
 	// showDescription(_currentRoomConversations[0].text, x, y, _currentRoomConversations[0].speakerId);
 	// for(int i = 0; i < _currentRoomConversations[0].choices.size(); i++) {
 	// 	int idx = _currentRoomConversations.size() - 1 - i;
@@ -918,32 +918,32 @@ void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	}
 }
 
-void readUntilBuda(Common::SeekableReadStream *stream, byte *&buffer, size_t &outSize) {
- const char marker[] = "BUDA";
-    const int markerLen = 4;
-    size_t bufferSize = 4096;
-    size_t pos = 0;
-
-    buffer = (byte *)malloc(bufferSize);
+void readUntilBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *&buffer, size_t &outSize) {
+	const char marker[] = "BUDA";
+	const int markerLen = 4;
+	size_t bufferSize = 4096;
+	size_t pos = 0;
 
-    while (!stream->eos()) {
-        byte b = stream->readByte();
-        if (pos + 1 > bufferSize) {
-            bufferSize *= 2;
-            buffer = (byte *)realloc(buffer, bufferSize);
-        }
-        buffer[pos++] = b;
-
-        // Check for marker at the end of buffer
-        if (pos >= markerLen &&
-            buffer[pos - 4] == 'B' &&
-            buffer[pos - 3] == 'U' &&
-            buffer[pos - 2] == 'D' &&
-            buffer[pos - 1] == 'A') {
-            break;
-        }
-    }
-    outSize = pos;
+	buffer = (byte *)malloc(bufferSize);
+	stream->seek(startPos, SEEK_SET);
+	while (!stream->eos()) {
+		byte b = stream->readByte();
+		if (pos + 1 > bufferSize) {
+			bufferSize *= 2;
+			buffer = (byte *)realloc(buffer, bufferSize);
+		}
+		buffer[pos++] = b;
+
+		// Check for marker at the end of buffer
+		if (pos >= markerLen &&
+			buffer[pos - 4] == 'B' &&
+			buffer[pos - 3] == 'U' &&
+			buffer[pos - 2] == 'D' &&
+			buffer[pos - 1] == 'A') {
+			break;
+		}
+	}
+	outSize = pos;
 }
 
 void PelrockEngine::loadRoomTalkingAnimations(int roomNumber) {
@@ -962,69 +962,60 @@ void PelrockEngine::loadRoomTalkingAnimations(int roomNumber) {
 	talkHeader.spritePointer = talkFile.readUint16LE();
 	talkHeader.unknown1 = talkFile.readByte();
 	talkFile.read(&talkHeader.unknown2, 4);
-	talkHeader.offsetX = talkFile.readByte();
-	talkHeader.offsetY = talkFile.readByte();
+	talkHeader.offsetXAnimA = talkFile.readByte();
+	talkHeader.offsetYAnimA = talkFile.readByte();
 	talkHeader.wAnimA = talkFile.readByte();
 	talkHeader.hAnimA = talkFile.readByte();
 	talkFile.read(&talkHeader.unknown3, 2);
 	talkHeader.numFramesAnimA = talkFile.readByte();
-	talkFile.read(&talkHeader.unknown4, 7);
+	talkFile.read(&talkHeader.unknown4, 5);
+
+	talkHeader.offsetXAnimB = talkFile.readByte();
+	talkHeader.offsetYAnimB = talkFile.readByte();
 	talkHeader.wAnimB = talkFile.readByte();
 	talkHeader.hAnimB = talkFile.readByte();
-	talkHeader.unknown5 = talkFile.readByte();
+	talkFile.read(&talkHeader.unknown5, 2);
 	talkHeader.numFramesAnimB = talkFile.readByte();
 	talkFile.read(&talkHeader.unknown6, 29);
+	debug("Talking anim header for room %d: spritePointer=%d, wA=%d, hA=%d, framesA=%d, wB=%d, hB=%d, framesB=%d", roomNumber, talkHeader.spritePointer, talkHeader.wAnimA, talkHeader.hAnimA, talkHeader.numFramesAnimA, talkHeader.wAnimB, talkHeader.hAnimB, talkHeader.numFramesAnimB);
 
-	if(talkHeader.spritePointer == 0) {
+	if (talkHeader.spritePointer == 0) {
 		debug("No talking animation for room %d", roomNumber);
 		talkFile.close();
 		return;
 	}
 
-	if(talkHeader.animA != nullptr) {
-		delete[] talkHeader.animA;
-		talkHeader.animA = nullptr;
-	}
+	// if(talkHeader.animA != nullptr) {
+	// 	delete[] talkHeader.animA;
+	// 	talkHeader.animA = nullptr;
+	// }
 	talkHeader.animA = new byte *[talkHeader.numFramesAnimA];
 
-
-	talkFile.seek(talkHeader.spritePointer, SEEK_SET);
-
 	byte *data = nullptr;
-	byte *decompressed = new byte[talkHeader.wAnimA * talkHeader.hAnimA * talkHeader.numFramesAnimA];
+	int animASize = talkHeader.wAnimA * talkHeader.hAnimA * talkHeader.numFramesAnimA;
+	byte *decompressed = nullptr;
 	size_t dataSize = 0;
-	readUntilBuda(&talkFile, data, dataSize);
-	rleDecompress(data, dataSize, 0, dataSize, &decompressed);
-	debug("Decompressed talking anim A size: %zu", dataSize);
-	for(int i = 0; i < talkHeader.numFramesAnimA; i++) {
+	readUntilBuda(&talkFile, talkHeader.spritePointer, data, dataSize);
+	size_t decompressedSize = rleDecompress(data, dataSize, 0, dataSize, &decompressed);
+	free(data);
+	debug("Decompressed talking anim A size: %zu, decompressed size: %zu", dataSize, decompressedSize);
+	for (int i = 0; i < talkHeader.numFramesAnimA; i++) {
 		talkHeader.animA[i] = new byte[talkHeader.wAnimA * talkHeader.hAnimA];
 		Common::copy(decompressed + (i * talkHeader.wAnimA * talkHeader.hAnimA), decompressed + ((i + 1) * talkHeader.wAnimA * talkHeader.hAnimA), talkHeader.animA[i]);
 	}
-	free(data);
-	free(decompressed);
-
 
-	if(talkHeader.numFramesAnimB > 0) {
-		if(talkHeader.animB != nullptr) {
-			delete[] talkHeader.animB;
-			talkHeader.animB = nullptr;
-		}
+	if (talkHeader.numFramesAnimB > 0) {
+		// if(talkHeader.animA != nullptr) {
+		// 	delete[] talkHeader.animA;
+		// 	talkHeader.animA = nullptr;
+		// }
 		talkHeader.animB = new byte *[talkHeader.numFramesAnimB];
-		// Read second animation frames
-		size_t offsetB = talkHeader.spritePointer + dataSize;
-		talkFile.seek(offsetB, SEEK_SET);
-		byte *dataB = nullptr;
-		byte *decompressedB = new byte[talkHeader.wAnimB * talkHeader.hAnimB * talkHeader.numFramesAnimB];
-		size_t dataBSize = 0;
-		readUntilBuda(&talkFile, dataB, dataBSize);
-		rleDecompress(dataB, 0, dataBSize, dataBSize, &decompressedB);
-		for(int i = 0; i < talkHeader.numFramesAnimB; i++) {
+		for (int i = 0; i < talkHeader.numFramesAnimB; i++) {
 			talkHeader.animB[i] = new byte[talkHeader.wAnimB * talkHeader.hAnimB];
-			Common::copy(decompressedB + (i * talkHeader.wAnimB * talkHeader.hAnimB), decompressedB + ((i + 1) * talkHeader.wAnimB * talkHeader.hAnimB), talkHeader.animB[i]);
+			Common::copy(decompressed + animASize + (i * talkHeader.wAnimB * talkHeader.hAnimB), decompressed + animASize + ((i + 1) * talkHeader.wAnimB * talkHeader.hAnimB), talkHeader.animB[i]);
 		}
-		free(dataB);
-		free(decompressedB);
 	}
+	free(decompressed);
 	_talkingAnimHeader = talkHeader;
 
 	talkFile.close();
@@ -1291,9 +1282,9 @@ void PelrockEngine::frames() {
 			int h = _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].h;
 			int extra = _currentRoomAnims[i].extra;
 
-			if(NPCTalking == extra) {
-				debug("Skipping anim set %d because NPC is talking", i);
-				talkNPC(&_currentRoomAnims[i]);
+			if (NPCTalking == extra) {
+				// debug("Skipping anim set %d because NPC is talking", i);
+				talkNPC(&_currentRoomAnims[i], i);
 				continue;
 			}
 
@@ -1327,7 +1318,6 @@ void PelrockEngine::frames() {
 			}
 		}
 
-
 		if (isAlfredWalking) {
 
 			MovementStep step = _currentContext.movement_buffer[_current_step];
@@ -1578,21 +1568,29 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	}
 }
 
-void PelrockEngine::talkNPC(AnimSet *animSet) {
-	//Change with the right index
-	int x = animSet->x + _talkingAnimHeader.offsetX;
-	int y = animSet->y + _talkingAnimHeader.offsetY;
-	int w = _talkingAnimHeader.wAnimA;
-	int h = _talkingAnimHeader.hAnimA;
-	int numFrames = _talkingAnimHeader.numFramesAnimA;
-	int curFrame = _talkingAnimHeader.currentFrameAnimA++;
+void PelrockEngine::talkNPC(AnimSet *animSet, int index) {
+	// Change with the right index
+
+	int x = animSet->x + (index ? _talkingAnimHeader.offsetXAnimB : _talkingAnimHeader.offsetXAnimA);
+	int y = animSet->y + (index ? _talkingAnimHeader.offsetYAnimB : _talkingAnimHeader.offsetYAnimA);
+
+	int w = index ? _talkingAnimHeader.wAnimB : _talkingAnimHeader.wAnimA;
+	int h = index ? _talkingAnimHeader.hAnimB : _talkingAnimHeader.hAnimA;
+	int numFrames = index ? _talkingAnimHeader.numFramesAnimB : _talkingAnimHeader.numFramesAnimA;
+	int curFrame = index ? _talkingAnimHeader.currentFrameAnimB++ : _talkingAnimHeader.currentFrameAnimA++;
 	if (curFrame >= numFrames) {
-		_talkingAnimHeader.currentFrameAnimA = 0;
+		if (index) {
+			_talkingAnimHeader.currentFrameAnimB = 0;
+		} else {
+			_talkingAnimHeader.currentFrameAnimA = 0;
+		}
 		curFrame = 0;
 	}
+	byte *frame = index ? _talkingAnimHeader.animB[curFrame] : _talkingAnimHeader.animA[curFrame];
+
 	debug("Talking NPC frame %d/%d, x=%d, y=%d, w=%d, h=%d", curFrame, numFrames, x, y, w, h);
 
-	drawSpriteToBuffer(_compositeBuffer, 640, _talkingAnimHeader.animA[curFrame], x, y, w, h, 255);
+	drawSpriteToBuffer(_compositeBuffer, 640, frame, x, y, w, h, 255);
 }
 
 void PelrockEngine::walkTo(int x, int y) {
@@ -1938,7 +1936,8 @@ uint8_t PelrockEngine::find_walkbox_for_point(uint16_t x, uint16_t y) {
 
 void PelrockEngine::checkMouseClick(int x, int y) {
 
-	if(NPCTalking) NPCTalking = false;
+	if (NPCTalking)
+		NPCTalking = false;
 
 	if (_displayPopup) {
 		Common::Array<VerbIcons> actions = availableActions(_currentHotspot);
@@ -1976,16 +1975,17 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 	Common::Point walkTarget = calculateWalkTarget(mouseX, mouseY);
 	_curWalkTarget = walkTarget;
 
-	// Exit *exit = isExitUnder(walkTarget.x, walkTarget.y);
-
-	/*if (exit != nullptr) {
-		xAlfred = exit->targetX;
-		yAlfred = exit->targetY;
-		setScreen(exit->targetRoom, exit->dir);
-	} else {*/
-	/*	} */
+	{ // For quick room navigation
+		Exit *exit = isExitUnder(walkTarget.x, walkTarget.y);
 
-	walkTo(walkTarget.x, walkTarget.y);
+		if (exit != nullptr) {
+			xAlfred = exit->targetX;
+			yAlfred = exit->targetY;
+			setScreen(exit->targetRoom, exit->dir);
+		} else {
+			walkTo(walkTarget.x, walkTarget.y);
+		}
+	}
 }
 
 void PelrockEngine::changeCursor(Cursor cursor) {
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 89017ab9a08..65003ca8edb 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -115,7 +115,7 @@ private:
 	Exit *isExitUnder(int x, int y);
 	AnimSet *isSpriteUnder(int x, int y);
 	void showActionBalloon(int posx, int posy, int curFrame);
-	void talkNPC(AnimSet *animSet);
+	void talkNPC(AnimSet *animSet, int index);
 
 	ChronoManager *_chronoManager = nullptr;
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 803ab5f6cbb..6b5d033f84a 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -168,15 +168,17 @@ struct TalkinAnimHeader {
 	byte unknown1;
 	byte unknown2[4];
 
-	int8 offsetX;
-	int8 offsetY;
+	int8 offsetXAnimA;
+	int8 offsetYAnimA;
 
 	byte wAnimA;
 	byte hAnimA;
 	byte unknown3[2];
 	byte numFramesAnimA;
-	byte unknown4[7];
+	byte unknown4[5];
 
+	byte offsetXAnimB;
+	byte offsetYAnimB;
 	byte currentFrameAnimA;
 
 	byte wAnimB;


Commit: b79204d594043e5fd61926fede1ee33fd777c7dc
    https://github.com/scummvm/scummvm/commit/b79204d594043e5fd61926fede1ee33fd777c7dc
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:09+02:00

Commit Message:
PELROCK: Fixes talking animations offset

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 1d8bf432a70..1f38c9de8bb 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -194,10 +194,9 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		setScreen(5, 0);
-		// setScreen(13, 1);
-		// setScreen(2, 2);
-		// setScreen(28, 0);
+		// setScreen(5, 0); //museum entrance
+		// setScreen(13, 1); // restaurants kitchen
+		setScreen(2, 2); // hooker
 	}
 }
 
@@ -959,9 +958,8 @@ void PelrockEngine::loadRoomTalkingAnimations(int roomNumber) {
 
 	talkFile.seek(offset, SEEK_SET);
 
-	talkHeader.spritePointer = talkFile.readUint16LE();
-	talkHeader.unknown1 = talkFile.readByte();
-	talkFile.read(&talkHeader.unknown2, 4);
+	talkHeader.spritePointer = talkFile.readUint32LE();
+	talkFile.read(&talkHeader.unknown2, 3);
 	talkHeader.offsetXAnimA = talkFile.readByte();
 	talkHeader.offsetYAnimA = talkFile.readByte();
 	talkHeader.wAnimA = talkFile.readByte();
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 6b5d033f84a..08e9a30471c 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -164,9 +164,9 @@ struct HotSpot {
 };
 
 struct TalkinAnimHeader {
-	uint16 spritePointer;
-	byte unknown1;
-	byte unknown2[4];
+	uint32 spritePointer;
+
+	byte unknown2[3];
 
 	int8 offsetXAnimA;
 	int8 offsetYAnimA;


Commit: 397b1d1fe8b42fd85a7d85806ebe3a6017f1a631
    https://github.com/scummvm/scummvm/commit/397b1d1fe8b42fd85a7d85806ebe3a6017f1a631
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:09+02:00

Commit Message:
PELROCK: Fixes text color selection

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 1f38c9de8bb..ca348e2d804 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -494,8 +494,16 @@ void PelrockEngine::talk(byte object) {
 			animSet = &_currentRoomAnims[i];
 		}
 	}
-	isNPCATalking = true;
-	NPCTalking = animSet->extra;
+
+	ConversationNode selectedNode = _currentRoomConversations[0];
+
+	bool isNPC = selectedNode.speakerId != 13;
+	if (isNPC) {
+		sayNPC(animSet, selectedNode.text, selectedNode.speakerId);
+	}
+	// for(int i= 0; i< _currentRoomConversations.size(); i++) {
+	// _currentRoomConversations
+	// }
 
 	// showDescription(_currentRoomConversations[0].text, x, y, _currentRoomConversations[0].speakerId);
 	// for(int i = 0; i < _currentRoomConversations[0].choices.size(); i++) {
@@ -736,6 +744,8 @@ Common::Array<ConversationNode> PelrockEngine::buildTreeStructure(const Common::
 				ConversationNode root;
 				root.type = ConversationNode::ROOT;
 				root.text = elem.text;
+				root.speaker = "NPC";
+				root.speakerId = elem.speakerId;
 				roots.push_back(root);
 				currentRoot = &roots[roots.size() - 1];
 			} else {
@@ -756,6 +766,8 @@ Common::Array<ConversationNode> PelrockEngine::buildTreeStructure(const Common::
 				ConversationNode choiceNode;
 				choiceNode.type = ConversationNode::CHOICE;
 				choiceNode.text = elem.text;
+				choiceNode.speaker = "ALFRED";
+				choiceNode.speakerId = 0x0D; // Player
 				choiceNode.choiceIndex = elem.choiceIndex;
 
 				// Find where to attach this choice
@@ -1280,7 +1292,7 @@ void PelrockEngine::frames() {
 			int h = _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].h;
 			int extra = _currentRoomAnims[i].extra;
 
-			if (NPCTalking == extra) {
+			if (whichNPCTalking == extra) {
 				// debug("Skipping anim set %d because NPC is talking", i);
 				talkNPC(&_currentRoomAnims[i], i);
 				continue;
@@ -1411,9 +1423,9 @@ void PelrockEngine::frames() {
 		memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
 
 		if (!isAlfredWalking && !_currentTextPages.empty()) {
-			// debug("Will render text, _chronoManager->_textTtl=%d", _chronoManager->_textTtl);
 			if (_chronoManager->_textTtl > 0) {
-				renderText(_currentTextPages[_currentTextPageIndex], _textColor);
+				debug("Will render text, _chronoManager->_textTtl=%d", _chronoManager->_textTtl);
+				renderText(_currentTextPages[_currentTextPageIndex], _textColor, _textPos.x, _textPos.y);
 			} else if (_currentTextPageIndex < _currentTextPages.size() - 1) {
 				_currentTextPageIndex++;
 
@@ -1426,6 +1438,8 @@ void PelrockEngine::frames() {
 				_currentTextPages.clear();
 				_currentTextPageIndex = 0;
 				isAlfredTalking = false;
+				isNPCATalking = false;
+				isNPCBTalking = false;
 			}
 		}
 
@@ -1465,24 +1479,21 @@ void PelrockEngine::doAction(byte action, byte object) {
 	}
 }
 
-void PelrockEngine::renderText(Common::Array<Common::String> lines, int color) {
-	if (color == ALFRED_COLOR) {
-		int baseX = xAlfred;
-		int baseY = yAlfred - kAlfredFrameHeight - 10;
-		int maxW = 0;
-		for (size_t i = 0; i < lines.size(); i++) {
-			Common::Rect r = _largeFont->getBoundingBox(lines[i]);
-			if (r.width() > maxW) {
-				maxW = r.width();
-			}
-		}
-		int lineSize = lines.size();
-		for (size_t i = 0; i < lines.size(); i++) {
-			int textX = baseX - (maxW / 2);
-			int textY = baseY - (lineSize * 25) + (i * 25);
-			drawText(lines[i], textX, textY, maxW, color);
+void PelrockEngine::renderText(Common::Array<Common::String> lines, int color, int baseX, int baseY) {
+
+	int maxW = 0;
+	for (size_t i = 0; i < lines.size(); i++) {
+		Common::Rect r = _largeFont->getBoundingBox(lines[i]);
+		if (r.width() > maxW) {
+			maxW = r.width();
 		}
 	}
+	int lineSize = lines.size();
+	for (size_t i = 0; i < lines.size(); i++) {
+		int textX = baseX - (maxW / 2);
+		int textY = baseY - (lineSize * 25) + (i * 25);
+		drawText(lines[i], textX, textY, maxW, color);
+	}
 }
 
 void PelrockEngine::drawAlfred(byte *buf) {
@@ -1934,8 +1945,8 @@ uint8_t PelrockEngine::find_walkbox_for_point(uint16_t x, uint16_t y) {
 
 void PelrockEngine::checkMouseClick(int x, int y) {
 
-	if (NPCTalking)
-		NPCTalking = false;
+	if (whichNPCTalking)
+		whichNPCTalking = false;
 
 	if (_displayPopup) {
 		Common::Array<VerbIcons> actions = availableActions(_currentHotspot);
@@ -2087,32 +2098,36 @@ Common::Point PelrockEngine::calculateWalkTarget(int mouseX, int mouseY) {
 
 void PelrockEngine::drawText(Common::String text, int x, int y, int w, byte color) {
 	Common::Rect rect = _largeFont->getBoundingBox(text.c_str());
-	if (x + 2 + rect.width() > 640) {
+	if (x + rect.width() > 640) {
 		x = 640 - rect.width() - 2;
 	}
-	if (y + 2 + rect.height() > 400) {
+	if (y + rect.height() > 400) {
 		y = 400 - rect.height();
 	}
-	if (x - 2 < 0) {
-		x = 2;
+	if (x < 0) {
+		x = 0;
 	}
-	if (y - 2 < 0) {
-		y = 2;
+	if (y < 0) {
+		y = 0;
 	}
-
-	// _largeFont->drawString(_screen, text.c_str(), x - 1, y, w, 0, Graphics::kTextAlignCenter); // Left
-	// // _largeFont->drawString(_screen, text.c_str(), x - 2, y, w, 0, Graphics::kTextAlignCenter); // Left
-	// _largeFont->drawString(_screen, text.c_str(), x + 1, y, w, 0, Graphics::kTextAlignCenter); // Right
-	// // _largeFont->drawString(_screen, text.c_str(), x + 2, y, w, 0, Graphics::kTextAlignCenter); // Right
-	// _largeFont->drawString(_screen, text.c_str(), x, y - 1, w, 0, Graphics::kTextAlignCenter); // Top
-	// // _largeFont->drawString(_screen, text.c_str(), x, y - 2, w, 0, Graphics::kTextAlignCenter); // Top
-	// _largeFont->drawString(_screen, text.c_str(), x, y + 1, w, 0, Graphics::kTextAlignCenter); // Bottom
-	// // _largeFont->drawString(_screen, text.c_str(), x, y + 2, w, 0, Graphics::kTextAlignCenter); // Bottom
-
 	// Draw main text on top
 	_largeFont->drawString(_screen, text.c_str(), x, y, w, color, Graphics::kTextAlignCenter);
 }
 
+void PelrockEngine::sayNPC(AnimSet *anim, Common::String text, byte color) {
+	isNPCATalking = true;
+	whichNPCTalking = anim->extra;
+	debug("NPC says %s, color = %d", text.c_str(), color);
+	_currentTextPages = wordWrap(text);
+	_textColor = color;
+	int totalChars = 0;
+	for (int i = 0; i < _currentTextPages[0].size(); i++) {
+		totalChars += _currentTextPages[0][i].size();
+	}
+	_textPos = Common::Point(anim->x, anim->y - anim->h - 10);
+	_chronoManager->_textTtl = totalChars * kTextCharDisplayTime;
+}
+
 void PelrockEngine::sayAlfred(Common::String text) {
 	isAlfredTalking = true;
 	curAlfredFrame = 0;
@@ -2123,6 +2138,7 @@ void PelrockEngine::sayAlfred(Common::String text) {
 	for (int i = 0; i < _currentTextPages[0].size(); i++) {
 		totalChars += _currentTextPages[0][i].size();
 	}
+	_textPos = Common::Point(xAlfred, yAlfred - kAlfredFrameHeight - 10);
 	_chronoManager->_textTtl = totalChars * kTextCharDisplayTime;
 }
 bool isEndMarker(char char_byte) {
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 65003ca8edb..1128d9bd5e8 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -102,10 +102,11 @@ private:
 	void drawText(Common::String text, int x, int y, int w, byte color);
 
 	void sayAlfred(Common::String text);
+	void sayNPC(AnimSet *anim, Common::String text, byte color);
 	// render loop
 	void frames();
 	void doAction(byte action, byte object);
-	void renderText(Common::Array<Common::String> lines, int color);
+	void renderText(Common::Array<Common::String> lines, int color, int x, int y);
 	void drawAlfred(byte *buf);
 	void checkMouseHover();
 	void checkMouseClick(int x, int y);
@@ -131,9 +132,14 @@ private:
 
 	PathContext _currentContext;
 	int _current_step = 0;
+
+
 	byte _textColor = 0;
+	Common::Point _textPos;
 	Common::Array<Common::Array<Common::String> > _currentTextPages = Common::Array<Common::Array<Common::String> >();
 	int _currentTextPageIndex = 0;
+
+
 	Common::Array<HotSpot> _currentRoomHotspots;
 	Common::Array<AnimSet> _currentRoomAnims;
 	Common::Array<Exit> _currentRoomExits;
@@ -173,7 +179,7 @@ private:
 
 	Common::Point _curWalkTarget;
 	bool isNPCATalking = false;
-	uint16 NPCTalking = 0;
+	uint16 whichNPCTalking = 0;
 	bool isNPCBTalking = false;
 
 


Commit: 1ecefd962b11ef03290e6b1e1828f615531aeb61
    https://github.com/scummvm/scummvm/commit/1ecefd962b11ef03290e6b1e1828f615531aeb61
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:09+02:00

Commit Message:
PELROCK: Creates ResourceManager

Changed paths:
  A engines/pelrock/resources.cpp
  A engines/pelrock/resources.h
  A engines/pelrock/util.cpp
  A engines/pelrock/util.h
    engines/pelrock/module.mk
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/module.mk b/engines/pelrock/module.mk
index 0c1e387371a..458143f1b40 100644
--- a/engines/pelrock/module.mk
+++ b/engines/pelrock/module.mk
@@ -5,8 +5,10 @@ MODULE_OBJS = \
 	chrono.o \
 	console.o \
 	metaengine.o \
+	resources.o \
 	fonts/small_font.o \
 	fonts/large_font.o \
+	util.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_PELROCK), DYNAMIC_PLUGIN)
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index ca348e2d804..fe611f99409 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -39,6 +39,7 @@
 #include "pelrock/fonts/small_font.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
+#include "pelrock/util.h"
 
 namespace Pelrock {
 
@@ -48,9 +49,14 @@ PelrockEngine::PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc) :
 																				 _gameDescription(gameDesc), _randomSource("Pelrock") {
 	g_engine = this;
 	_chronoManager = new ChronoManager();
+	_resourceManager = new ResourceManager();
 }
 
 PelrockEngine::~PelrockEngine() {
+	delete _resourceManager;
+	delete[] _compositeBuffer;
+	delete[] _currentBackground;
+	delete _largeFont;
 	delete _screen;
 	delete _chronoManager;
 	for (int i = 0; i < 5; i++) {
@@ -84,21 +90,6 @@ Common::String PelrockEngine::getGameId() const {
 	return _gameDescription->gameId;
 }
 
-void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byte color) {
-	// debug("Drawing rect at (%d,%d) w=%d h=%d color=%d", x, y, w, h, color);
-	surface->drawLine(x, y, x + w, y, color);
-	surface->drawLine(x, y + h, x + w, y + h, color);
-	surface->drawLine(x, y, x, y + h, color);
-	surface->drawLine(x + w, y, x + w, y + h, color);
-}
-void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color) {
-	// debug("Drawing rect at (%d,%d) w=%d h=%d color=%d", x, y, w, h, color);
-	surface->drawLine(x, y, x + w, y, color);
-	surface->drawLine(x, y + h, x + w, y + h, color);
-	surface->drawLine(x, y, x, y + h, color);
-	surface->drawLine(x + w, y, x + w, y + h, color);
-}
-
 Common::Array<Common::Array<Common::String> > wordWrap(Common::String text);
 Common::Error PelrockEngine::run() {
 	// Initialize 320x200 paletted graphics mode
@@ -207,43 +198,6 @@ void PelrockEngine::loadAnims() {
 	loadAlfredAnims();
 }
 
-const int EXPECTED_SIZE = 640 * 400;
-size_t rleDecompress(const uint8_t *data, size_t data_size, uint32_t offset, uint32_t size, uint8_t **out_data) {
-	// Check for uncompressed markers
-	if (size == 0x8000 || size == 0x6800) {
-		*out_data = (uint8_t *)malloc(size);
-		memcpy(*out_data, data + offset, size);
-		return size;
-	}
-
-	// RLE compressed
-	*out_data = (uint8_t *)malloc(EXPECTED_SIZE * 2); // Allocate enough space
-	size_t result_size = 0;
-
-	uint32_t pos = offset;
-	uint32_t end = offset + size;
-
-	while (pos + 2 <= end && pos + 2 <= data_size) {
-		// Check for BUDA marker
-		if (pos + 4 <= data_size &&
-			data[pos] == 'B' && data[pos + 1] == 'U' &&
-			data[pos + 2] == 'D' && data[pos + 3] == 'A') {
-			break;
-		}
-
-		uint8_t count = data[pos];
-		uint8_t value = data[pos + 1];
-
-		for (int i = 0; i < count; i++) {
-			(*out_data)[result_size++] = value;
-		}
-
-		pos += 2;
-	}
-
-	return result_size;
-}
-
 void PelrockEngine::getPalette(Common::File *roomFile, int roomOffset, byte *palette) {
 	// get palette
 	int paletteOffset = roomOffset + (11 * 8);
@@ -291,168 +245,168 @@ void PelrockEngine::getBackground(Common::File *roomFile, int roomOffset, byte *
 	}
 }
 
-Common::Array<AnimSet> PelrockEngine::loadRoomAnimations(Common::File *roomFile, int roomOffset) {
-	uint32_t pair_offset = roomOffset + (8 * 8);
-	// debug("Sprite pair offset position: %d", pair_offset);
-	roomFile->seek(pair_offset, SEEK_SET);
-	uint32_t offset = roomFile->readUint32LE();
-	uint32_t size = roomFile->readUint32LE();
-
-	byte *data = new byte[size];
-	roomFile->seek(offset, SEEK_SET);
-	roomFile->read(data, size);
-
-	unsigned char *pic = new byte[10000 * 10000];
-	if (offset > 0 && size > 0) {
-		rleDecompress(data, size, 0, size, &pic);
-	} else {
-		return Common::Array<AnimSet>();
-	}
-	Common::Array<AnimSet> anims = Common::Array<AnimSet>();
-	uint32_t spriteEnd = offset + size;
-
-	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
-	uint32_t metadata_start = spriteEnd + 108;
-	uint32_t picOffset = 0;
-	for (int i = 0; i < 7; i++) {
-		uint32_t animOffset = metadata_start + (i * 44);
-		byte *animData = new byte[44];
-		roomFile->seek(animOffset, SEEK_SET);
-		roomFile->read(animData, 44);
-		AnimSet animSet;
-		animSet.x = animData[0] | (animData[1] << 8);
-		animSet.y = animData[2] | (animData[3] << 8);
-		animSet.w = animData[4];
-		animSet.h = animData[5];
-		animSet.extra = animData[6];
-		// roomFile->skip(1); // reserved
-		animSet.numAnims = animData[8];
-		animSet.spriteType = animData[33];
-		animSet.actionFlags = animData[34];
-		animSet.isDisabled = animData[38];
-		if (animSet.numAnims == 0) {
-			break;
-		}
-		animSet.animData = new Anim[animSet.numAnims];
-		// debug("AnimSet %d has %d sub-anims, type %d, actionFlags %d, isDisabled? %d", i, animSet.numAnims, animSet.spriteType, animSet.actionFlags, animSet.isDisabled);
-		int subAnimOffset = 10;
-		for (int j = 0; j < animSet.numAnims; j++) {
-
-			Anim anim;
-			anim.x = animSet.x;
-			anim.y = animSet.y;
-			anim.w = animSet.w;
-			anim.h = animSet.h;
-			anim.curFrame = 0;
-
-			anim.nframes = animData[subAnimOffset + j];
-			anim.loopCount = animData[subAnimOffset + 4 + j];
-			anim.speed = animData[subAnimOffset + 8 + j];
-			anim.animData = new byte[anim.nframes];
-			if (anim.w > 0 && anim.h > 0 && anim.nframes > 0) {
-				uint32_t needed = anim.w * anim.h * anim.nframes;
-				anim.animData = new byte[needed];
-				Common::copy(pic + picOffset, pic + picOffset + needed, anim.animData);
-				animSet.animData[j] = anim;
-				// debug("  Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d loopCount=%d speed=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes, anim.loopCount, anim.speed);
-				picOffset += needed;
-			} else {
-				continue;
-				debug("Anim %d-%d: invalid dimensions, skipping", i, j);
-			}
-			animSet.animData[j] = anim;
-		}
-
-		anims.push_back(animSet);
-	}
-	return anims;
-}
-
-Common::Array<WalkBox> PelrockEngine::loadWalkboxes(Common::File *roomFile, int roomOffset) {
-	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
-	roomFile->seek(pair10_offset_pos, SEEK_SET);
-	// roomFile->skip(4);
-	uint32_t pair10_data_offset = roomFile->readUint32LE();
-	uint32_t pair10_size = roomFile->readUint32LE();
-
-	uint32_t walkbox_countOffset = pair10_data_offset + 0x213;
-	roomFile->seek(walkbox_countOffset, SEEK_SET);
-	byte walkbox_count = roomFile->readByte();
-	debug("Walkbox count: %d", walkbox_count);
-	uint32_t walkbox_offset = pair10_data_offset + 0x218;
-	Common::Array<WalkBox> walkboxes;
-	for (int i = 0; i < walkbox_count; i++) {
-		uint32_t box_offset = walkbox_offset + i * 9;
-		roomFile->seek(box_offset, SEEK_SET);
-		int16 x1 = roomFile->readSint16LE();
-		int16 y1 = roomFile->readSint16LE();
-		int16 w = roomFile->readSint16LE();
-		int16 h = roomFile->readSint16LE();
-		byte flags = roomFile->readByte();
-		debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
-		WalkBox box;
-		box.x = x1;
-		box.y = y1;
-		box.w = w;
-		box.h = h;
-		box.flags = flags;
-		walkboxes.push_back(box);
-	}
-	return walkboxes;
-}
-
-Common::Array<Description> PelrockEngine::loadRoomDescriptions(Common::File *roomFile, int roomOffset, uint32_t &outPos) {
-	uint32_t pair12_offset_pos = roomOffset + (12 * 8);
-	roomFile->seek(pair12_offset_pos, SEEK_SET);
-	// roomFile->skip(4);
-	uint32_t pair12_data_offset = roomFile->readUint32LE();
-	uint32_t pair12_size = roomFile->readUint32LE();
-
-	roomFile->seek(pair12_data_offset, SEEK_SET);
-	byte *data = new byte[pair12_size];
-	roomFile->read(data, pair12_size);
-	Common::Array<Description> descriptions;
-	uint32_t pos = 0;
-	uint32_t lastDescPos = 0;
-	while (pos < (pair12_size)) {
-		int desc_pos = 0;
-		if (data[pos] == 0xFF) {
-			Description description;
-			description.itemId = data[pos + 1];
-			pos += 3;
-			description.index = data[pos++];
-			description.text = "";
-			// debug("Found description terminator");
-			while (pos < (pair12_size) && data[pos] != 0xFD && pos < (pair12_size)) {
-				// debug(" char: %c", data[pos]);
-				if (data[pos] != 0x00) {
-					description.text.append(1, (char)data[pos]);
-				}
-				if (data[pos] == 0xF8) {
-					description.actionTrigger = data[pos + 1] | data[pos + 2] << 8;
-					debug("Found action trigger: %d", description.actionTrigger);
-					pos += 2;
-					break;
-				}
-				// desc[desc_pos++] = (char)data[pos];
-				// debug("Current desc: %s", desc);
-				pos++;
-			}
-			debug("Found description for item %d index %d, text: %s", description.itemId, description.index, description.text.c_str());
-
-			descriptions.push_back(description);
-			lastDescPos = pos;
-		}
-		pos++;
-	}
-	debug("End of descriptions at position %d", pos);
-	outPos = lastDescPos + 1;
-	delete[] data;
-	// for (Common::List<Common::String>::iterator i = descriptions.begin(); i != descriptions.end(); i++) {
-	// 	debug("Room description: %s", i->c_str());
-	// }
-	return descriptions;
-}
+// Common::Array<AnimSet> PelrockEngine::loadRoomAnimations(Common::File *roomFile, int roomOffset) {
+// 	uint32_t pair_offset = roomOffset + (8 * 8);
+// 	// debug("Sprite pair offset position: %d", pair_offset);
+// 	roomFile->seek(pair_offset, SEEK_SET);
+// 	uint32_t offset = roomFile->readUint32LE();
+// 	uint32_t size = roomFile->readUint32LE();
+
+// 	byte *data = new byte[size];
+// 	roomFile->seek(offset, SEEK_SET);
+// 	roomFile->read(data, size);
+
+// 	unsigned char *pic = new byte[10000 * 10000];
+// 	if (offset > 0 && size > 0) {
+// 		rleDecompress(data, size, 0, size, &pic);
+// 	} else {
+// 		return Common::Array<AnimSet>();
+// 	}
+// 	Common::Array<AnimSet> anims = Common::Array<AnimSet>();
+// 	uint32_t spriteEnd = offset + size;
+
+// 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
+// 	uint32_t metadata_start = spriteEnd + 108;
+// 	uint32_t picOffset = 0;
+// 	for (int i = 0; i < 7; i++) {
+// 		uint32_t animOffset = metadata_start + (i * 44);
+// 		byte *animData = new byte[44];
+// 		roomFile->seek(animOffset, SEEK_SET);
+// 		roomFile->read(animData, 44);
+// 		AnimSet animSet;
+// 		animSet.x = animData[0] | (animData[1] << 8);
+// 		animSet.y = animData[2] | (animData[3] << 8);
+// 		animSet.w = animData[4];
+// 		animSet.h = animData[5];
+// 		animSet.extra = animData[6];
+// 		// roomFile->skip(1); // reserved
+// 		animSet.numAnims = animData[8];
+// 		animSet.spriteType = animData[33];
+// 		animSet.actionFlags = animData[34];
+// 		animSet.isDisabled = animData[38];
+// 		if (animSet.numAnims == 0) {
+// 			break;
+// 		}
+// 		animSet.animData = new Anim[animSet.numAnims];
+// 		// debug("AnimSet %d has %d sub-anims, type %d, actionFlags %d, isDisabled? %d", i, animSet.numAnims, animSet.spriteType, animSet.actionFlags, animSet.isDisabled);
+// 		int subAnimOffset = 10;
+// 		for (int j = 0; j < animSet.numAnims; j++) {
+
+// 			Anim anim;
+// 			anim.x = animSet.x;
+// 			anim.y = animSet.y;
+// 			anim.w = animSet.w;
+// 			anim.h = animSet.h;
+// 			anim.curFrame = 0;
+
+// 			anim.nframes = animData[subAnimOffset + j];
+// 			anim.loopCount = animData[subAnimOffset + 4 + j];
+// 			anim.speed = animData[subAnimOffset + 8 + j];
+// 			anim.animData = new byte[anim.nframes];
+// 			if (anim.w > 0 && anim.h > 0 && anim.nframes > 0) {
+// 				uint32_t needed = anim.w * anim.h * anim.nframes;
+// 				anim.animData = new byte[needed];
+// 				Common::copy(pic + picOffset, pic + picOffset + needed, anim.animData);
+// 				animSet.animData[j] = anim;
+// 				// debug("  Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d loopCount=%d speed=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes, anim.loopCount, anim.speed);
+// 				picOffset += needed;
+// 			} else {
+// 				continue;
+// 				debug("Anim %d-%d: invalid dimensions, skipping", i, j);
+// 			}
+// 			animSet.animData[j] = anim;
+// 		}
+
+// 		anims.push_back(animSet);
+// 	}
+// 	return anims;
+// }
+
+// Common::Array<WalkBox> PelrockEngine::loadWalkboxes(Common::File *roomFile, int roomOffset) {
+// 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
+// 	roomFile->seek(pair10_offset_pos, SEEK_SET);
+// 	// roomFile->skip(4);
+// 	uint32_t pair10_data_offset = roomFile->readUint32LE();
+// 	uint32_t pair10_size = roomFile->readUint32LE();
+
+// 	uint32_t walkbox_countOffset = pair10_data_offset + 0x213;
+// 	roomFile->seek(walkbox_countOffset, SEEK_SET);
+// 	byte walkbox_count = roomFile->readByte();
+// 	debug("Walkbox count: %d", walkbox_count);
+// 	uint32_t walkbox_offset = pair10_data_offset + 0x218;
+// 	Common::Array<WalkBox> walkboxes;
+// 	for (int i = 0; i < walkbox_count; i++) {
+// 		uint32_t box_offset = walkbox_offset + i * 9;
+// 		roomFile->seek(box_offset, SEEK_SET);
+// 		int16 x1 = roomFile->readSint16LE();
+// 		int16 y1 = roomFile->readSint16LE();
+// 		int16 w = roomFile->readSint16LE();
+// 		int16 h = roomFile->readSint16LE();
+// 		byte flags = roomFile->readByte();
+// 		debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
+// 		WalkBox box;
+// 		box.x = x1;
+// 		box.y = y1;
+// 		box.w = w;
+// 		box.h = h;
+// 		box.flags = flags;
+// 		walkboxes.push_back(box);
+// 	}
+// 	return walkboxes;
+// }
+
+// Common::Array<Description> PelrockEngine::loadRoomDescriptions(Common::File *roomFile, int roomOffset, uint32_t &outPos) {
+// 	uint32_t pair12_offset_pos = roomOffset + (12 * 8);
+// 	roomFile->seek(pair12_offset_pos, SEEK_SET);
+// 	// roomFile->skip(4);
+// 	uint32_t pair12_data_offset = roomFile->readUint32LE();
+// 	uint32_t pair12_size = roomFile->readUint32LE();
+
+// 	roomFile->seek(pair12_data_offset, SEEK_SET);
+// 	byte *data = new byte[pair12_size];
+// 	roomFile->read(data, pair12_size);
+// 	Common::Array<Description> descriptions;
+// 	uint32_t pos = 0;
+// 	uint32_t lastDescPos = 0;
+// 	while (pos < (pair12_size)) {
+// 		int desc_pos = 0;
+// 		if (data[pos] == 0xFF) {
+// 			Description description;
+// 			description.itemId = data[pos + 1];
+// 			pos += 3;
+// 			description.index = data[pos++];
+// 			description.text = "";
+// 			// debug("Found description terminator");
+// 			while (pos < (pair12_size) && data[pos] != 0xFD && pos < (pair12_size)) {
+// 				// debug(" char: %c", data[pos]);
+// 				if (data[pos] != 0x00) {
+// 					description.text.append(1, (char)data[pos]);
+// 				}
+// 				if (data[pos] == 0xF8) {
+// 					description.actionTrigger = data[pos + 1] | data[pos + 2] << 8;
+// 					debug("Found action trigger: %d", description.actionTrigger);
+// 					pos += 2;
+// 					break;
+// 				}
+// 				// desc[desc_pos++] = (char)data[pos];
+// 				// debug("Current desc: %s", desc);
+// 				pos++;
+// 			}
+// 			debug("Found description for item %d index %d, text: %s", description.itemId, description.index, description.text.c_str());
+
+// 			descriptions.push_back(description);
+// 			lastDescPos = pos;
+// 		}
+// 		pos++;
+// 	}
+// 	debug("End of descriptions at position %d", pos);
+// 	outPos = lastDescPos + 1;
+// 	delete[] data;
+// 	// for (Common::List<Common::String>::iterator i = descriptions.begin(); i != descriptions.end(); i++) {
+// 	// 	debug("Room description: %s", i->c_str());
+// 	// }
+// 	return descriptions;
+// }
 
 char32_t decodeByte(byte b) {
 	if (b == 0x80) {
@@ -867,67 +821,67 @@ Common::Array<ConversationNode> PelrockEngine::loadConversations(Common::File *r
 	return roots;
 }
 
-void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
-	uint32_t outPos = 0;
-
-	Common::Array<Description> descriptions = loadRoomDescriptions(roomFile, roomOffset, outPos);
-	debug("After decsriptions, position is %d", outPos);
-	Common::Array<ConversationNode> roots = loadConversations(roomFile, roomOffset, outPos);
-	for (int i = 0; i < roots.size(); i++) {
-		if (roots[i].text.empty()) {
-			continue;
-		}
-		debug("Conversation %d: %s", i, roots[i].text.c_str());
-	}
-	_currentRoomConversations = roots;
-
-	Common::Array<AnimSet> anims = loadRoomAnimations(roomFile, roomOffset);
-
-	Common::Array<HotSpot> hotspots;
-	for (int i = 0; i < anims.size(); i++) {
-
-		HotSpot thisHotspot;
-		thisHotspot.index = i;
-		thisHotspot.x = anims[i].x;
-		thisHotspot.y = anims[i].y;
-		thisHotspot.w = anims[i].w;
-		thisHotspot.h = anims[i].h;
-		thisHotspot.extra = anims[i].extra;
-		thisHotspot.type = anims[i].actionFlags;
-		thisHotspot.isEnabled = !anims[i].isDisabled;
-		hotspots.push_back(thisHotspot);
-	}
-
-	Common::Array<HotSpot> staticHotspots = loadHotspots(roomFile, roomOffset);
-	Common::Array<Exit> exits = loadExits(roomFile, roomOffset);
-
-	Common::Array<WalkBox> walkboxes = loadWalkboxes(roomFile, roomOffset);
-
-	debug("total descriptions = %d, anims = %d, hotspots = %d", descriptions.size(), anims.size(), staticHotspots.size());
-	for (int i = 0; i < staticHotspots.size(); i++) {
-		HotSpot hotspot = staticHotspots[i];
-		hotspot.index = anims.size() + i;
-		hotspots.push_back(hotspot);
-	}
-
-	int walkboxCount = 0;
-
-	_currentRoomAnims = anims;
-	_currentRoomHotspots = hotspots;
-	_currentRoomExits = exits;
-	_currentRoomWalkboxes = walkboxes;
-	_currentRoomDescriptions = descriptions;
-
-	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
-		HotSpot hotspot = _currentRoomHotspots[i];
-		drawRect(_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 200 + i);
-	}
-
-	for (int i = 0; i < _currentRoomExits.size(); i++) {
-		Exit exit = _currentRoomExits[i];
-		// drawRect(_screen, exit.x, exit.y, exit.w, exit.h, 100 + i);
-	}
-}
+// void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
+// 	uint32_t outPos = 0;
+
+// 	Common::Array<Description> descriptions = loadRoomDescriptions(roomFile, roomOffset, outPos);
+// 	debug("After decsriptions, position is %d", outPos);
+// 	Common::Array<ConversationNode> roots = loadConversations(roomFile, roomOffset, outPos);
+// 	for (int i = 0; i < roots.size(); i++) {
+// 		if (roots[i].text.empty()) {
+// 			continue;
+// 		}
+// 		debug("Conversation %d: %s", i, roots[i].text.c_str());
+// 	}
+// 	_currentRoomConversations = roots;
+
+// 	Common::Array<AnimSet> anims = loadRoomAnimations(roomFile, roomOffset);
+
+// 	Common::Array<HotSpot> hotspots;
+// 	for (int i = 0; i < anims.size(); i++) {
+
+// 		HotSpot thisHotspot;
+// 		thisHotspot.index = i;
+// 		thisHotspot.x = anims[i].x;
+// 		thisHotspot.y = anims[i].y;
+// 		thisHotspot.w = anims[i].w;
+// 		thisHotspot.h = anims[i].h;
+// 		thisHotspot.extra = anims[i].extra;
+// 		thisHotspot.type = anims[i].actionFlags;
+// 		thisHotspot.isEnabled = !anims[i].isDisabled;
+// 		hotspots.push_back(thisHotspot);
+// 	}
+
+// 	Common::Array<HotSpot> staticHotspots = loadHotspots(roomFile, roomOffset);
+// 	Common::Array<Exit> exits = loadExits(roomFile, roomOffset);
+
+// 	Common::Array<WalkBox> walkboxes = loadWalkboxes(roomFile, roomOffset);
+
+// 	debug("total descriptions = %d, anims = %d, hotspots = %d", descriptions.size(), anims.size(), staticHotspots.size());
+// 	for (int i = 0; i < staticHotspots.size(); i++) {
+// 		HotSpot hotspot = staticHotspots[i];
+// 		hotspot.index = anims.size() + i;
+// 		hotspots.push_back(hotspot);
+// 	}
+
+// 	int walkboxCount = 0;
+
+// 	_currentRoomAnims = anims;
+// 	_currentRoomHotspots = hotspots;
+// 	_currentRoomExits = exits;
+// 	_currentRoomWalkboxes = walkboxes;
+// 	_currentRoomDescriptions = descriptions;
+
+// 	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
+// 		HotSpot hotspot = _currentRoomHotspots[i];
+// 		drawRect(_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 200 + i);
+// 	}
+
+// 	for (int i = 0; i < _currentRoomExits.size(); i++) {
+// 		Exit exit = _currentRoomExits[i];
+// 		// drawRect(_screen, exit.x, exit.y, exit.w, exit.h, 100 + i);
+// 	}
+// }
 
 void readUntilBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *&buffer, size_t &outSize) {
 	const char marker[] = "BUDA";
@@ -1079,60 +1033,60 @@ void PelrockEngine::loadInteractionIcons() {
 	alfred4File.close();
 }
 
-Common::Array<Exit> PelrockEngine::loadExits(Common::File *roomFile, int roomOffset) {
-	Common::Array<Exit> exits;
-	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
-	roomFile->seek(pair10_offset_pos, SEEK_SET);
-	uint32_t pair10_data_offset = roomFile->readUint32LE();
-	uint32_t pair10_size = roomFile->readUint32LE();
-	roomFile->seek(pair10_data_offset + 0x1BE, SEEK_SET);
-	int exit_count = roomFile->readByte();
-	roomFile->seek(pair10_data_offset + 0x1BF, SEEK_SET);
-	for (int i = 0; i < exit_count; i++) {
-		Exit exit;
-		exit.targetRoom = roomFile->readUint16LE();
-		exit.flags = roomFile->readByte();
-		exit.x = roomFile->readUint16LE();
-		exit.y = roomFile->readUint16LE();
-		exit.w = roomFile->readByte();
-		exit.h = roomFile->readByte();
-
-		exit.targetX = roomFile->readUint16LE();
-		exit.targetY = roomFile->readUint16LE();
-		exit.dir = roomFile->readByte();
-		exits.push_back(exit);
-	}
-	return exits;
-}
-
-Common::Array<HotSpot> PelrockEngine::loadHotspots(Common::File *roomFile, int roomOffset) {
-	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
-	debug("Hotspot(10)  pair offset position: %d", pair10_offset_pos);
-	roomFile->seek(pair10_offset_pos, SEEK_SET);
-	uint32_t pair10_data_offset = roomFile->readUint32LE();
-	uint32_t pair10_size = roomFile->readUint32LE();
-	uint32_t count_offset = pair10_data_offset + 0x47a;
-	roomFile->seek(count_offset, SEEK_SET);
-	byte hotspot_count = roomFile->readByte();
-	uint32_t hotspot_data_start = pair10_data_offset + 0x47c;
-	Common::Array<HotSpot> hotspots;
-	for (int i = 0; i < hotspot_count; i++) {
-		uint32_t obj_offset = hotspot_data_start + i * 9;
-		roomFile->seek(obj_offset, SEEK_SET);
-		HotSpot spot;
-		spot.type = roomFile->readByte();
-		spot.x = roomFile->readUint16LE();
-		spot.y = roomFile->readUint16LE();
-		spot.w = roomFile->readByte();
-		spot.h = roomFile->readByte();
-		spot.extra = roomFile->readUint16LE();
-		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, spot.type, spot.x, spot.y, spot.w, spot.h, spot.extra);
-		hotspots.push_back(spot);
-	}
-	return hotspots;
-	// uint32_t hover_areas_start = pair10_data_offset + 0x1BE;
-	// roomFile->seek(hover_areas_start, SEEK_SET);
-}
+// Common::Array<Exit> PelrockEngine::loadExits(Common::File *roomFile, int roomOffset) {
+// 	Common::Array<Exit> exits;
+// 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
+// 	roomFile->seek(pair10_offset_pos, SEEK_SET);
+// 	uint32_t pair10_data_offset = roomFile->readUint32LE();
+// 	uint32_t pair10_size = roomFile->readUint32LE();
+// 	roomFile->seek(pair10_data_offset + 0x1BE, SEEK_SET);
+// 	int exit_count = roomFile->readByte();
+// 	roomFile->seek(pair10_data_offset + 0x1BF, SEEK_SET);
+// 	for (int i = 0; i < exit_count; i++) {
+// 		Exit exit;
+// 		exit.targetRoom = roomFile->readUint16LE();
+// 		exit.flags = roomFile->readByte();
+// 		exit.x = roomFile->readUint16LE();
+// 		exit.y = roomFile->readUint16LE();
+// 		exit.w = roomFile->readByte();
+// 		exit.h = roomFile->readByte();
+
+// 		exit.targetX = roomFile->readUint16LE();
+// 		exit.targetY = roomFile->readUint16LE();
+// 		exit.dir = roomFile->readByte();
+// 		exits.push_back(exit);
+// 	}
+// 	return exits;
+// }
+
+// Common::Array<HotSpot> PelrockEngine::loadHotspots(Common::File *roomFile, int roomOffset) {
+// 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
+// 	debug("Hotspot(10)  pair offset position: %d", pair10_offset_pos);
+// 	roomFile->seek(pair10_offset_pos, SEEK_SET);
+// 	uint32_t pair10_data_offset = roomFile->readUint32LE();
+// 	uint32_t pair10_size = roomFile->readUint32LE();
+// 	uint32_t count_offset = pair10_data_offset + 0x47a;
+// 	roomFile->seek(count_offset, SEEK_SET);
+// 	byte hotspot_count = roomFile->readByte();
+// 	uint32_t hotspot_data_start = pair10_data_offset + 0x47c;
+// 	Common::Array<HotSpot> hotspots;
+// 	for (int i = 0; i < hotspot_count; i++) {
+// 		uint32_t obj_offset = hotspot_data_start + i * 9;
+// 		roomFile->seek(obj_offset, SEEK_SET);
+// 		HotSpot spot;
+// 		spot.type = roomFile->readByte();
+// 		spot.x = roomFile->readUint16LE();
+// 		spot.y = roomFile->readUint16LE();
+// 		spot.w = roomFile->readByte();
+// 		spot.h = roomFile->readByte();
+// 		spot.extra = roomFile->readUint16LE();
+// 		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, spot.type, spot.x, spot.y, spot.w, spot.h, spot.extra);
+// 		hotspots.push_back(spot);
+// 	}
+// 	return hotspots;
+// 	// uint32_t hover_areas_start = pair10_data_offset + 0x1BE;
+// 	// roomFile->seek(hover_areas_start, SEEK_SET);
+// }
 
 void extractSingleFrame(byte *source, byte *dest, int frameIndex, int frameWidth, int frameHeight) {
 	for (int y = 0; y < frameHeight; y++) {
@@ -2299,7 +2253,7 @@ void PelrockEngine::setScreen(int number, int dir) {
 		}
 	}
 
-	loadRoomMetadata(&roomFile, roomOffset);
+	_resourceManager->loadRoomMetadata(&roomFile, roomOffset);
 	loadRoomTalkingAnimations(number);
 
 	_screen->markAllDirty();
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 1128d9bd5e8..feef1bf91bc 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -40,6 +40,7 @@
 #include "pelrock/detection.h"
 #include "pelrock/fonts/large_font.h"
 #include "pelrock/fonts/small_font.h"
+#include "pelrock/resources.h"
 #include "pelrock/types.h"
 
 namespace Pelrock {
@@ -53,7 +54,7 @@ class PelrockEngine : public Engine {
 private:
 	const ADGameDescription *_gameDescription;
 	Common::RandomSource _randomSource;
-	Image::PNGDecoder *decoder = new Image::PNGDecoder();
+
 	void init();
 	void playIntro();
 	void setScreen(int s, int dir);
@@ -64,11 +65,11 @@ private:
 	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
 	void loadAlfredAnims();
-	Common::Array<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
-	Common::Array<HotSpot> loadHotspots(Common::File *roomFile, int roomOffset);
-	Common::Array<Exit> loadExits(Common::File *roomFile, int roomOffset);
-	Common::Array<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
-	Common::Array<Description> loadRoomDescriptions(Common::File *roomFile, int roomOffset, uint32_t &outPos);
+	// Common::Array<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
+	// Common::Array<HotSpot> loadHotspots(Common::File *roomFile, int roomOffset);
+	// Common::Array<Exit> loadExits(Common::File *roomFile, int roomOffset);
+	// Common::Array<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
+	// Common::Array<Description> loadRoomDescriptions(Common::File *roomFile, int roomOffset, uint32_t &outPos);
 
 	Common::String cleanText(const Common::String &text);
 	Common::Array<ConversationElement> parseConversationElements(const byte *convData, uint32 size);
@@ -90,7 +91,7 @@ private:
 
 	void talk(byte object);
 	Common::String getControlName(byte b);
-	void loadRoomMetadata(Common::File *roomFile, int roomOffset);
+	// void loadRoomMetadata(Common::File *roomFile, int roomOffset);
 	void loadRoomTalkingAnimations(int roomNumber);
 	void loadCursors();
 	void loadInteractionIcons();
@@ -122,31 +123,22 @@ private:
 
 	// byte *standingAnim = new byte[3060 * 102];
 
-	byte **walkingAnimFrames[4];                // 4 arrays of arrays
-	byte *standingAnimFrames[4] = {nullptr};    // 4 directions
-	int walkingAnimLengths[4] = {8, 8, 4, 4};   // size of each inner array
-	byte **talkingAnimFrames[4];                // 4 arrays of arrays
-	int talkingAnimLengths[4] = {8, 8, 4, 4};   // size of each inner array
+	byte **walkingAnimFrames[4];              // 4 arrays of arrays
+	byte *standingAnimFrames[4] = {nullptr};  // 4 directions
+	int walkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
+	byte **talkingAnimFrames[4];              // 4 arrays of arrays
+	int talkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
 
 	TalkinAnimHeader _talkingAnimHeader;
 
 	PathContext _currentContext;
 	int _current_step = 0;
 
-
 	byte _textColor = 0;
 	Common::Point _textPos;
 	Common::Array<Common::Array<Common::String> > _currentTextPages = Common::Array<Common::Array<Common::String> >();
 	int _currentTextPageIndex = 0;
 
-
-	Common::Array<HotSpot> _currentRoomHotspots;
-	Common::Array<AnimSet> _currentRoomAnims;
-	Common::Array<Exit> _currentRoomExits;
-	Common::Array<WalkBox> _currentRoomWalkboxes;
-	Common::Array<Description> _currentRoomDescriptions;
-	Common::Array<ConversationNode> _currentRoomConversations;
-
 	int *_currentAnimFrames = nullptr;
 	// From the original code
 	int xAlfred = 319;
@@ -182,8 +174,7 @@ private:
 	uint16 whichNPCTalking = 0;
 	bool isNPCBTalking = false;
 
-
-	//JAVA
+	// JAVA
 	bool shouldPlayIntro = false;
 	GameState stateGame = GAME;
 	bool gameInitialized = false;
@@ -194,6 +185,9 @@ private:
 	// int prevWhichScreen = 0;
 	// int whichScreen = 0;
 	// byte *pixelsShadows; // =new int[640*400];
+
+	ResourceManager *_resourceManager = nullptr;
+
 protected:
 	// Engine APIs
 	Common::Error run() override;
@@ -248,6 +242,13 @@ public:
 		Common::Serializer s(stream, nullptr);
 		return syncGame(s);
 	}
+
+	Common::Array<HotSpot> _currentRoomHotspots;
+	Common::Array<AnimSet> _currentRoomAnims;
+	Common::Array<Exit> _currentRoomExits;
+	Common::Array<WalkBox> _currentRoomWalkboxes;
+	Common::Array<Description> _currentRoomDescriptions;
+	Common::Array<ConversationNode> _currentRoomConversations;
 };
 
 extern PelrockEngine *g_engine;
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
new file mode 100644
index 00000000000..dac515a606d
--- /dev/null
+++ b/engines/pelrock/resources.cpp
@@ -0,0 +1,312 @@
+/* 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 "pelrock/resources.h"
+#include "pelrock/pelrock.h"
+#include "pelrock/util.h"
+
+namespace Pelrock {
+
+ResourceManager::ResourceManager() {
+}
+
+ResourceManager::~ResourceManager() {
+}
+
+Common::Array<Exit> ResourceManager::loadExits(Common::File *roomFile, int roomOffset) {
+	Common::Array<Exit> exits;
+	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
+	roomFile->seek(pair10_offset_pos, SEEK_SET);
+	uint32_t pair10_data_offset = roomFile->readUint32LE();
+	uint32_t pair10_size = roomFile->readUint32LE();
+	roomFile->seek(pair10_data_offset + 0x1BE, SEEK_SET);
+	int exit_count = roomFile->readByte();
+	roomFile->seek(pair10_data_offset + 0x1BF, SEEK_SET);
+	for (int i = 0; i < exit_count; i++) {
+		Exit exit;
+		exit.targetRoom = roomFile->readUint16LE();
+		exit.flags = roomFile->readByte();
+		exit.x = roomFile->readUint16LE();
+		exit.y = roomFile->readUint16LE();
+		exit.w = roomFile->readByte();
+		exit.h = roomFile->readByte();
+
+		exit.targetX = roomFile->readUint16LE();
+		exit.targetY = roomFile->readUint16LE();
+		exit.dir = roomFile->readByte();
+		exits.push_back(exit);
+	}
+	return exits;
+}
+
+Common::Array<HotSpot> ResourceManager::loadHotspots(Common::File *roomFile, int roomOffset) {
+	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
+	debug("Hotspot(10)  pair offset position: %d", pair10_offset_pos);
+	roomFile->seek(pair10_offset_pos, SEEK_SET);
+	uint32_t pair10_data_offset = roomFile->readUint32LE();
+	uint32_t pair10_size = roomFile->readUint32LE();
+	uint32_t count_offset = pair10_data_offset + 0x47a;
+	roomFile->seek(count_offset, SEEK_SET);
+	byte hotspot_count = roomFile->readByte();
+	uint32_t hotspot_data_start = pair10_data_offset + 0x47c;
+	Common::Array<HotSpot> hotspots;
+	for (int i = 0; i < hotspot_count; i++) {
+		uint32_t obj_offset = hotspot_data_start + i * 9;
+		roomFile->seek(obj_offset, SEEK_SET);
+		HotSpot spot;
+		spot.type = roomFile->readByte();
+		spot.x = roomFile->readUint16LE();
+		spot.y = roomFile->readUint16LE();
+		spot.w = roomFile->readByte();
+		spot.h = roomFile->readByte();
+		spot.extra = roomFile->readUint16LE();
+		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, spot.type, spot.x, spot.y, spot.w, spot.h, spot.extra);
+		hotspots.push_back(spot);
+	}
+	return hotspots;
+	// uint32_t hover_areas_start = pair10_data_offset + 0x1BE;
+	// roomFile->seek(hover_areas_start, SEEK_SET);
+}
+
+void ResourceManager::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
+	uint32_t outPos = 0;
+
+	Common::Array<Description> descriptions = loadRoomDescriptions(roomFile, roomOffset, outPos);
+	debug("After decsriptions, position is %d", outPos);
+	// Common::Array<ConversationNode> roots = loadConversations(roomFile, roomOffset, outPos);
+	// for (int i = 0; i < roots.size(); i++) {
+	// 	if (roots[i].text.empty()) {
+	// 		continue;
+	// 	}
+	// 	debug("Conversation %d: %s", i, roots[i].text.c_str());
+	// }
+	// g_engine->_currentRoomConversations = roots;
+
+	Common::Array<AnimSet> anims = loadRoomAnimations(roomFile, roomOffset);
+
+	Common::Array<HotSpot> hotspots;
+	for (int i = 0; i < anims.size(); i++) {
+
+		HotSpot thisHotspot;
+		thisHotspot.index = i;
+		thisHotspot.x = anims[i].x;
+		thisHotspot.y = anims[i].y;
+		thisHotspot.w = anims[i].w;
+		thisHotspot.h = anims[i].h;
+		thisHotspot.extra = anims[i].extra;
+		thisHotspot.type = anims[i].actionFlags;
+		thisHotspot.isEnabled = !anims[i].isDisabled;
+		hotspots.push_back(thisHotspot);
+	}
+
+	Common::Array<HotSpot> staticHotspots = loadHotspots(roomFile, roomOffset);
+	Common::Array<Exit> exits = loadExits(roomFile, roomOffset);
+
+	Common::Array<WalkBox> walkboxes = loadWalkboxes(roomFile, roomOffset);
+
+	debug("total descriptions = %d, anims = %d, hotspots = %d", descriptions.size(), anims.size(), staticHotspots.size());
+	for (int i = 0; i < staticHotspots.size(); i++) {
+		HotSpot hotspot = staticHotspots[i];
+		hotspot.index = anims.size() + i;
+		hotspots.push_back(hotspot);
+	}
+
+	int walkboxCount = 0;
+
+	g_engine->_currentRoomAnims = anims;
+	g_engine->_currentRoomHotspots = hotspots;
+	g_engine->_currentRoomExits = exits;
+	g_engine->_currentRoomWalkboxes = walkboxes;
+	g_engine->_currentRoomDescriptions = descriptions;
+	for (int i = 0; i < g_engine->_currentRoomHotspots.size(); i++) {
+		HotSpot hotspot = g_engine->_currentRoomHotspots[i];
+		drawRect(g_engine->_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 200 + i);
+	}
+
+	for (int i = 0; i < g_engine->_currentRoomExits.size(); i++) {
+		Exit exit = g_engine->_currentRoomExits[i];
+		// drawRect(_screen, exit.x, exit.y, exit.w, exit.h, 100 + i);
+	}
+}
+Common::Array<AnimSet> ResourceManager::loadRoomAnimations(Common::File *roomFile, int roomOffset) {
+	uint32_t pair_offset = roomOffset + (8 * 8);
+	// debug("Sprite pair offset position: %d", pair_offset);
+	roomFile->seek(pair_offset, SEEK_SET);
+	uint32_t offset = roomFile->readUint32LE();
+	uint32_t size = roomFile->readUint32LE();
+
+	byte *data = new byte[size];
+	roomFile->seek(offset, SEEK_SET);
+	roomFile->read(data, size);
+
+	unsigned char *pic = new byte[10000 * 10000];
+	if (offset > 0 && size > 0) {
+		rleDecompress(data, size, 0, size, &pic);
+	} else {
+		return Common::Array<AnimSet>();
+	}
+	Common::Array<AnimSet> anims = Common::Array<AnimSet>();
+	uint32_t spriteEnd = offset + size;
+
+	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
+	uint32_t metadata_start = spriteEnd + 108;
+	uint32_t picOffset = 0;
+	for (int i = 0; i < 7; i++) {
+		uint32_t animOffset = metadata_start + (i * 44);
+		byte *animData = new byte[44];
+		roomFile->seek(animOffset, SEEK_SET);
+		roomFile->read(animData, 44);
+		AnimSet animSet;
+		animSet.x = animData[0] | (animData[1] << 8);
+		animSet.y = animData[2] | (animData[3] << 8);
+		animSet.w = animData[4];
+		animSet.h = animData[5];
+		animSet.extra = animData[6];
+		// roomFile->skip(1); // reserved
+		animSet.numAnims = animData[8];
+		animSet.spriteType = animData[33];
+		animSet.actionFlags = animData[34];
+		animSet.isDisabled = animData[38];
+		if (animSet.numAnims == 0) {
+			break;
+		}
+		animSet.animData = new Anim[animSet.numAnims];
+		// debug("AnimSet %d has %d sub-anims, type %d, actionFlags %d, isDisabled? %d", i, animSet.numAnims, animSet.spriteType, animSet.actionFlags, animSet.isDisabled);
+		int subAnimOffset = 10;
+		for (int j = 0; j < animSet.numAnims; j++) {
+
+			Anim anim;
+			anim.x = animSet.x;
+			anim.y = animSet.y;
+			anim.w = animSet.w;
+			anim.h = animSet.h;
+			anim.curFrame = 0;
+
+			anim.nframes = animData[subAnimOffset + j];
+			anim.loopCount = animData[subAnimOffset + 4 + j];
+			anim.speed = animData[subAnimOffset + 8 + j];
+			anim.animData = new byte[anim.nframes];
+			if (anim.w > 0 && anim.h > 0 && anim.nframes > 0) {
+				uint32_t needed = anim.w * anim.h * anim.nframes;
+				anim.animData = new byte[needed];
+				Common::copy(pic + picOffset, pic + picOffset + needed, anim.animData);
+				animSet.animData[j] = anim;
+				// debug("  Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d loopCount=%d speed=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes, anim.loopCount, anim.speed);
+				picOffset += needed;
+			} else {
+				continue;
+				debug("Anim %d-%d: invalid dimensions, skipping", i, j);
+			}
+			animSet.animData[j] = anim;
+		}
+
+		anims.push_back(animSet);
+	}
+	return anims;
+}
+
+Common::Array<WalkBox> ResourceManager::loadWalkboxes(Common::File *roomFile, int roomOffset) {
+	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
+	roomFile->seek(pair10_offset_pos, SEEK_SET);
+	// roomFile->skip(4);
+	uint32_t pair10_data_offset = roomFile->readUint32LE();
+	uint32_t pair10_size = roomFile->readUint32LE();
+
+	uint32_t walkbox_countOffset = pair10_data_offset + 0x213;
+	roomFile->seek(walkbox_countOffset, SEEK_SET);
+	byte walkbox_count = roomFile->readByte();
+	debug("Walkbox count: %d", walkbox_count);
+	uint32_t walkbox_offset = pair10_data_offset + 0x218;
+	Common::Array<WalkBox> walkboxes;
+	for (int i = 0; i < walkbox_count; i++) {
+		uint32_t box_offset = walkbox_offset + i * 9;
+		roomFile->seek(box_offset, SEEK_SET);
+		int16 x1 = roomFile->readSint16LE();
+		int16 y1 = roomFile->readSint16LE();
+		int16 w = roomFile->readSint16LE();
+		int16 h = roomFile->readSint16LE();
+		byte flags = roomFile->readByte();
+		debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
+		WalkBox box;
+		box.x = x1;
+		box.y = y1;
+		box.w = w;
+		box.h = h;
+		box.flags = flags;
+		walkboxes.push_back(box);
+	}
+	return walkboxes;
+}
+
+Common::Array<Description> ResourceManager::loadRoomDescriptions(Common::File *roomFile, int roomOffset, uint32_t &outPos) {
+	uint32_t pair12_offset_pos = roomOffset + (12 * 8);
+	roomFile->seek(pair12_offset_pos, SEEK_SET);
+	// roomFile->skip(4);
+	uint32_t pair12_data_offset = roomFile->readUint32LE();
+	uint32_t pair12_size = roomFile->readUint32LE();
+
+	roomFile->seek(pair12_data_offset, SEEK_SET);
+	byte *data = new byte[pair12_size];
+	roomFile->read(data, pair12_size);
+	Common::Array<Description> descriptions;
+	uint32_t pos = 0;
+	uint32_t lastDescPos = 0;
+	while (pos < (pair12_size)) {
+		int desc_pos = 0;
+		if (data[pos] == 0xFF) {
+			Description description;
+			description.itemId = data[pos + 1];
+			pos += 3;
+			description.index = data[pos++];
+			description.text = "";
+			// debug("Found description terminator");
+			while (pos < (pair12_size) && data[pos] != 0xFD && pos < (pair12_size)) {
+				// debug(" char: %c", data[pos]);
+				if (data[pos] != 0x00) {
+					description.text.append(1, (char)data[pos]);
+				}
+				if (data[pos] == 0xF8) {
+					description.actionTrigger = data[pos + 1] | data[pos + 2] << 8;
+					debug("Found action trigger: %d", description.actionTrigger);
+					pos += 2;
+					break;
+				}
+				// desc[desc_pos++] = (char)data[pos];
+				// debug("Current desc: %s", desc);
+				pos++;
+			}
+			debug("Found description for item %d index %d, text: %s", description.itemId, description.index, description.text.c_str());
+
+			descriptions.push_back(description);
+			lastDescPos = pos;
+		}
+		pos++;
+	}
+	debug("End of descriptions at position %d", pos);
+	outPos = lastDescPos + 1;
+	delete[] data;
+	// for (Common::List<Common::String>::iterator i = descriptions.begin(); i != descriptions.end(); i++) {
+	// 	debug("Room description: %s", i->c_str());
+	// }
+	return descriptions;
+}
+
+} // End of namespace Pelrock
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
new file mode 100644
index 00000000000..45fcd7b3c31
--- /dev/null
+++ b/engines/pelrock/resources.h
@@ -0,0 +1,49 @@
+/* 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 PELROCK_RESOURCES_H
+#define PELROCK_RESOURCES_H
+
+#include "common/array.h"
+#include "common/file.h"
+#include "common/scummsys.h"
+
+#include "pelrock/types.h"
+
+namespace Pelrock {
+
+class ResourceManager {
+public:
+	ResourceManager();
+	~ResourceManager();
+	void loadRoomMetadata(Common::File *roomFile, int roomOffset);
+	void loadRoomTalkingAnimations(int roomNumber);
+
+private:
+	Common::Array<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
+	Common::Array<HotSpot> loadHotspots(Common::File *roomFile, int roomOffset);
+	Common::Array<Exit> loadExits(Common::File *roomFile, int roomOffset);
+	Common::Array<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
+	Common::Array<Description> loadRoomDescriptions(Common::File *roomFile, int roomOffset, uint32_t &outPos);
+};
+
+} // End of namespace Pelrock
+
+#endif
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 08e9a30471c..0bb527b3469 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -47,8 +47,6 @@ enum VerbIcons {
 	UNKNOWN
 };
 
-
-
 static const uint32 kLongClickDuration = 500; // 500ms for long click
 const int kCursorWidth = 16;
 const int kCursorHeight = 18;
@@ -76,21 +74,20 @@ const int kVerbIconPadding = 20;
 #define MAX_MOVEMENT_STEPS 100 // 500 bytes / 5 bytes per step
 #define PATH_END 0xFF          // End of path marker
 
-#define MAX_CHARS_PER_LINE  0x2F  // 47 characters
-#define MAX_LINES 5  // Maximum number of lines per page (0-indexed check against 4)
+#define MAX_CHARS_PER_LINE 0x2F // 47 characters
+#define MAX_LINES 5             // Maximum number of lines per page (0-indexed check against 4)
 
 // Control character codes (negative values in signed char)
-#define CHAR_SPACE 0x20           /* ' ' */
-#define CHAR_END_MARKER_1 0xFD    /* -3 (end of text marker) */
-#define CHAR_END_MARKER_2 0xF4    /* -0xC (alternate end marker) */
-#define CHAR_END_MARKER_3 0xF8    /* -8 (another end marker) */
-#define CHAR_END_MARKER_4 0xF0    /* -0x10 (another end marker) */
-#define CHAR_NEWLINE 0xF6         /* -10 (newline marker) */
-#define CHAR_PAGE_BREAK 0xF9      /* marker inserted when switching pages */
+#define CHAR_SPACE 0x20        /* ' ' */
+#define CHAR_END_MARKER_1 0xFD /* -3 (end of text marker) */
+#define CHAR_END_MARKER_2 0xF4 /* -0xC (alternate end marker) */
+#define CHAR_END_MARKER_3 0xF8 /* -8 (another end marker) */
+#define CHAR_END_MARKER_4 0xF0 /* -0x10 (another end marker) */
+#define CHAR_NEWLINE 0xF6      /* -10 (newline marker) */
+#define CHAR_PAGE_BREAK 0xF9   /* marker inserted when switching pages */
 
 #define ALFRED_COLOR 0x0D
 
-
 typedef struct {
 	uint8_t flags;       /* Direction flags (see MOVE_* constants) */
 	uint16_t distance_x; // Horizontal distance to move
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
new file mode 100644
index 00000000000..7a1ab388d0a
--- /dev/null
+++ b/engines/pelrock/util.cpp
@@ -0,0 +1,79 @@
+/* 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 "pelrock/util.h"
+#include "pelrock/types.h"
+
+namespace Pelrock {
+
+void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byte color) {
+	// debug("Drawing rect at (%d,%d) w=%d h=%d color=%d", x, y, w, h, color);
+	surface->drawLine(x, y, x + w, y, color);
+	surface->drawLine(x, y + h, x + w, y + h, color);
+	surface->drawLine(x, y, x, y + h, color);
+	surface->drawLine(x + w, y, x + w, y + h, color);
+}
+
+void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color) {
+	// debug("Drawing rect at (%d,%d) w=%d h=%d color=%d", x, y, w, h, color);
+	surface->drawLine(x, y, x + w, y, color);
+	surface->drawLine(x, y + h, x + w, y + h, color);
+	surface->drawLine(x, y, x, y + h, color);
+	surface->drawLine(x + w, y, x + w, y + h, color);
+}
+
+size_t rleDecompress(const uint8_t *data, size_t data_size, uint32_t offset, uint32_t size, uint8_t **out_data) {
+	// Check for uncompressed markers
+	if (size == 0x8000 || size == 0x6800) {
+		*out_data = (uint8_t *)malloc(size);
+		memcpy(*out_data, data + offset, size);
+		return size;
+	}
+
+	// RLE compressed
+	*out_data = (uint8_t *)malloc(EXPECTED_SIZE * 2); // Allocate enough space
+	size_t result_size = 0;
+
+	uint32_t pos = offset;
+	uint32_t end = offset + size;
+
+	while (pos + 2 <= end && pos + 2 <= data_size) {
+		// Check for BUDA marker
+		if (pos + 4 <= data_size &&
+			data[pos] == 'B' && data[pos + 1] == 'U' &&
+			data[pos + 2] == 'D' && data[pos + 3] == 'A') {
+			break;
+		}
+
+		uint8_t count = data[pos];
+		uint8_t value = data[pos + 1];
+
+		for (int i = 0; i < count; i++) {
+			(*out_data)[result_size++] = value;
+		}
+
+		pos += 2;
+	}
+
+	return result_size;
+}
+
+} // End of namespace Pelrock
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
new file mode 100644
index 00000000000..d5cfa3cc3cf
--- /dev/null
+++ b/engines/pelrock/util.h
@@ -0,0 +1,34 @@
+/* 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 PELROCK_UTIL_H
+#define PELROCK_UTIL_H
+
+#include "common/types.h"
+#include "graphics/managed_surface.h"
+#include "graphics/surface.h"
+namespace Pelrock {
+
+const int EXPECTED_SIZE = 640 * 400;
+size_t rleDecompress(const uint8_t *data, size_t data_size, uint32_t offset, uint32_t size, uint8_t **out_data);
+void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byte color);
+void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color);
+} // End of namespace Pelrock
+#endif // PELROCK_UTIL_H


Commit: 36661bdceee3b33c28437611e027ae6e696c3f24
    https://github.com/scummvm/scummvm/commit/36661bdceee3b33c28437611e027ae6e696c3f24
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:10+02:00

Commit Message:
PELROCK: Refactors room resource loading into its own class

Changed paths:
  A engines/pelrock/room.cpp
  A engines/pelrock/room.h
  R engines/pelrock/resources.cpp
  R engines/pelrock/resources.h
    engines/pelrock/module.mk
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/util.cpp
    engines/pelrock/util.h


diff --git a/engines/pelrock/module.mk b/engines/pelrock/module.mk
index 458143f1b40..fdaf98bc630 100644
--- a/engines/pelrock/module.mk
+++ b/engines/pelrock/module.mk
@@ -5,7 +5,7 @@ MODULE_OBJS = \
 	chrono.o \
 	console.o \
 	metaengine.o \
-	resources.o \
+	room.o \
 	fonts/small_font.o \
 	fonts/large_font.o \
 	util.o
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index fe611f99409..b66d2836d6e 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -49,11 +49,11 @@ PelrockEngine::PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc) :
 																				 _gameDescription(gameDesc), _randomSource("Pelrock") {
 	g_engine = this;
 	_chronoManager = new ChronoManager();
-	_resourceManager = new ResourceManager();
+	_room = new RoomManager();
 }
 
 PelrockEngine::~PelrockEngine() {
-	delete _resourceManager;
+	delete _room;
 	delete[] _compositeBuffer;
 	delete[] _currentBackground;
 	delete _largeFont;
@@ -198,258 +198,19 @@ void PelrockEngine::loadAnims() {
 	loadAlfredAnims();
 }
 
-void PelrockEngine::getPalette(Common::File *roomFile, int roomOffset, byte *palette) {
-	// get palette
-	int paletteOffset = roomOffset + (11 * 8);
-	roomFile->seek(paletteOffset, SEEK_SET);
-	uint32 offset = roomFile->readUint32LE();
-	uint32 size = roomFile->readUint32LE();
-
-	roomFile->seek(offset, SEEK_SET);
-
-	roomFile->read(palette, size);
-	for (int i = 0; i < 256; i++) {
-		palette[i * 3] = palette[i * 3] << 2;
-		palette[i * 3 + 1] = palette[i * 3 + 1] << 2;
-		palette[i * 3 + 2] = palette[i * 3 + 2] << 2;
-	}
-}
-
-void PelrockEngine::getBackground(Common::File *roomFile, int roomOffset, byte *background) {
-	roomFile->seek(0, SEEK_SET);
-	// get screen
-	size_t combined_size = 0;
-	size_t uncompressed_size = 0;
-	for (int pair_idx = 0; pair_idx < 8; pair_idx++) {
-		uint32_t pair_offset = roomOffset + (pair_idx * 8);
-		if (pair_offset + 8 > roomFile->size())
-			continue;
-
-		roomFile->seek(pair_offset, SEEK_SET);
-		uint32_t offset = roomFile->readUint32LE();
-		uint32_t size = roomFile->readUint32LE();
-		uncompressed_size += size;
-
-		if (offset > 0 && size > 0 && offset < roomFile->size()) {
-			byte *data = new byte[size];
-			roomFile->seek(offset, SEEK_SET);
-			roomFile->read(data, size);
-			uint8_t *block_data = NULL;
-			size_t block_size = rleDecompress(data, size, 0, size, &block_data);
-
-			memcpy(background + combined_size, block_data, block_size);
-			combined_size += block_size + 1;
-			free(block_data);
-			delete[] data;
-		}
-	}
-}
-
-// Common::Array<AnimSet> PelrockEngine::loadRoomAnimations(Common::File *roomFile, int roomOffset) {
-// 	uint32_t pair_offset = roomOffset + (8 * 8);
-// 	// debug("Sprite pair offset position: %d", pair_offset);
-// 	roomFile->seek(pair_offset, SEEK_SET);
-// 	uint32_t offset = roomFile->readUint32LE();
-// 	uint32_t size = roomFile->readUint32LE();
-
-// 	byte *data = new byte[size];
-// 	roomFile->seek(offset, SEEK_SET);
-// 	roomFile->read(data, size);
-
-// 	unsigned char *pic = new byte[10000 * 10000];
-// 	if (offset > 0 && size > 0) {
-// 		rleDecompress(data, size, 0, size, &pic);
-// 	} else {
-// 		return Common::Array<AnimSet>();
-// 	}
-// 	Common::Array<AnimSet> anims = Common::Array<AnimSet>();
-// 	uint32_t spriteEnd = offset + size;
-
-// 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
-// 	uint32_t metadata_start = spriteEnd + 108;
-// 	uint32_t picOffset = 0;
-// 	for (int i = 0; i < 7; i++) {
-// 		uint32_t animOffset = metadata_start + (i * 44);
-// 		byte *animData = new byte[44];
-// 		roomFile->seek(animOffset, SEEK_SET);
-// 		roomFile->read(animData, 44);
-// 		AnimSet animSet;
-// 		animSet.x = animData[0] | (animData[1] << 8);
-// 		animSet.y = animData[2] | (animData[3] << 8);
-// 		animSet.w = animData[4];
-// 		animSet.h = animData[5];
-// 		animSet.extra = animData[6];
-// 		// roomFile->skip(1); // reserved
-// 		animSet.numAnims = animData[8];
-// 		animSet.spriteType = animData[33];
-// 		animSet.actionFlags = animData[34];
-// 		animSet.isDisabled = animData[38];
-// 		if (animSet.numAnims == 0) {
-// 			break;
-// 		}
-// 		animSet.animData = new Anim[animSet.numAnims];
-// 		// debug("AnimSet %d has %d sub-anims, type %d, actionFlags %d, isDisabled? %d", i, animSet.numAnims, animSet.spriteType, animSet.actionFlags, animSet.isDisabled);
-// 		int subAnimOffset = 10;
-// 		for (int j = 0; j < animSet.numAnims; j++) {
-
-// 			Anim anim;
-// 			anim.x = animSet.x;
-// 			anim.y = animSet.y;
-// 			anim.w = animSet.w;
-// 			anim.h = animSet.h;
-// 			anim.curFrame = 0;
-
-// 			anim.nframes = animData[subAnimOffset + j];
-// 			anim.loopCount = animData[subAnimOffset + 4 + j];
-// 			anim.speed = animData[subAnimOffset + 8 + j];
-// 			anim.animData = new byte[anim.nframes];
-// 			if (anim.w > 0 && anim.h > 0 && anim.nframes > 0) {
-// 				uint32_t needed = anim.w * anim.h * anim.nframes;
-// 				anim.animData = new byte[needed];
-// 				Common::copy(pic + picOffset, pic + picOffset + needed, anim.animData);
-// 				animSet.animData[j] = anim;
-// 				// debug("  Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d loopCount=%d speed=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes, anim.loopCount, anim.speed);
-// 				picOffset += needed;
-// 			} else {
-// 				continue;
-// 				debug("Anim %d-%d: invalid dimensions, skipping", i, j);
-// 			}
-// 			animSet.animData[j] = anim;
-// 		}
-
-// 		anims.push_back(animSet);
-// 	}
-// 	return anims;
-// }
-
-// Common::Array<WalkBox> PelrockEngine::loadWalkboxes(Common::File *roomFile, int roomOffset) {
-// 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
-// 	roomFile->seek(pair10_offset_pos, SEEK_SET);
-// 	// roomFile->skip(4);
-// 	uint32_t pair10_data_offset = roomFile->readUint32LE();
-// 	uint32_t pair10_size = roomFile->readUint32LE();
-
-// 	uint32_t walkbox_countOffset = pair10_data_offset + 0x213;
-// 	roomFile->seek(walkbox_countOffset, SEEK_SET);
-// 	byte walkbox_count = roomFile->readByte();
-// 	debug("Walkbox count: %d", walkbox_count);
-// 	uint32_t walkbox_offset = pair10_data_offset + 0x218;
-// 	Common::Array<WalkBox> walkboxes;
-// 	for (int i = 0; i < walkbox_count; i++) {
-// 		uint32_t box_offset = walkbox_offset + i * 9;
-// 		roomFile->seek(box_offset, SEEK_SET);
-// 		int16 x1 = roomFile->readSint16LE();
-// 		int16 y1 = roomFile->readSint16LE();
-// 		int16 w = roomFile->readSint16LE();
-// 		int16 h = roomFile->readSint16LE();
-// 		byte flags = roomFile->readByte();
-// 		debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
-// 		WalkBox box;
-// 		box.x = x1;
-// 		box.y = y1;
-// 		box.w = w;
-// 		box.h = h;
-// 		box.flags = flags;
-// 		walkboxes.push_back(box);
-// 	}
-// 	return walkboxes;
-// }
-
-// Common::Array<Description> PelrockEngine::loadRoomDescriptions(Common::File *roomFile, int roomOffset, uint32_t &outPos) {
-// 	uint32_t pair12_offset_pos = roomOffset + (12 * 8);
-// 	roomFile->seek(pair12_offset_pos, SEEK_SET);
-// 	// roomFile->skip(4);
-// 	uint32_t pair12_data_offset = roomFile->readUint32LE();
-// 	uint32_t pair12_size = roomFile->readUint32LE();
-
-// 	roomFile->seek(pair12_data_offset, SEEK_SET);
-// 	byte *data = new byte[pair12_size];
-// 	roomFile->read(data, pair12_size);
-// 	Common::Array<Description> descriptions;
-// 	uint32_t pos = 0;
-// 	uint32_t lastDescPos = 0;
-// 	while (pos < (pair12_size)) {
-// 		int desc_pos = 0;
-// 		if (data[pos] == 0xFF) {
-// 			Description description;
-// 			description.itemId = data[pos + 1];
-// 			pos += 3;
-// 			description.index = data[pos++];
-// 			description.text = "";
-// 			// debug("Found description terminator");
-// 			while (pos < (pair12_size) && data[pos] != 0xFD && pos < (pair12_size)) {
-// 				// debug(" char: %c", data[pos]);
-// 				if (data[pos] != 0x00) {
-// 					description.text.append(1, (char)data[pos]);
-// 				}
-// 				if (data[pos] == 0xF8) {
-// 					description.actionTrigger = data[pos + 1] | data[pos + 2] << 8;
-// 					debug("Found action trigger: %d", description.actionTrigger);
-// 					pos += 2;
-// 					break;
-// 				}
-// 				// desc[desc_pos++] = (char)data[pos];
-// 				// debug("Current desc: %s", desc);
-// 				pos++;
-// 			}
-// 			debug("Found description for item %d index %d, text: %s", description.itemId, description.index, description.text.c_str());
-
-// 			descriptions.push_back(description);
-// 			lastDescPos = pos;
-// 		}
-// 		pos++;
-// 	}
-// 	debug("End of descriptions at position %d", pos);
-// 	outPos = lastDescPos + 1;
-// 	delete[] data;
-// 	// for (Common::List<Common::String>::iterator i = descriptions.begin(); i != descriptions.end(); i++) {
-// 	// 	debug("Room description: %s", i->c_str());
-// 	// }
-// 	return descriptions;
-// }
-
-char32_t decodeByte(byte b) {
-	if (b == 0x80) {
-		return '\xA4';
-	} else if (b == 0x81) {
-		return '\xA1';
-	} else if (b == 0x82) {
-		return '\xAD';
-	} else if (b == 0x83) {
-		return '\xA8';
-	} else if (b == 0x84) {
-		return '\xA3';
-	} else if (b == 0x7B) {
-		return '\xA0';
-	} else if (b == 0x7C) {
-		return '\x82';
-	} else if (b == 0x7D) {
-		return '\xA1';
-	} else if (b == 0x7E) {
-		return '\xA2';
-	} else if (b == 0x7F) {
-		return '\xA3';
-	} else if (b >= 0x20 && b <= 0x7A) {
-		return (char)b;
-	} else {
-		// return string in format [XX]
-		return '.';
-	}
-}
-
 void PelrockEngine::talk(byte object) {
 	debug("Talking to object %d", object);
-	if (_currentRoomConversations.size() == 0)
+	if (_room->_currentRoomConversations.size() == 0)
 		return;
 
 	AnimSet *animSet;
-	for (int i = 0; i < _currentRoomAnims.size(); i++) {
-		if (_currentRoomAnims[i].extra == object) {
-			animSet = &_currentRoomAnims[i];
+	for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
+		if (_room->_currentRoomAnims[i].extra == object) {
+			animSet = &_room->_currentRoomAnims[i];
 		}
 	}
 
-	ConversationNode selectedNode = _currentRoomConversations[0];
+	ConversationNode selectedNode = _room->_currentRoomConversations[0];
 
 	bool isNPC = selectedNode.speakerId != 13;
 	if (isNPC) {
@@ -466,525 +227,6 @@ void PelrockEngine::talk(byte object) {
 	// }
 }
 
-Common::String PelrockEngine::getControlName(byte b) {
-	switch (b) {
-	case 0xFD:
-		return "END_LINE";
-	case 0xFC:
-		return "TEXT_TERM";
-	case 0xFB:
-		return "CHOICE";
-	case 0xFA:
-		return "SKIP";
-	case 0xF9:
-		return "PAGE_BREAK";
-	case 0xF8:
-		return "ACTION";
-	case 0xF7:
-		return "END_BRANCH";
-	case 0xF6:
-		return "LINE_CONT";
-	case 0xF5:
-		return "END_BRANCH_2";
-	case 0xF4:
-		return "END_CONV";
-	case 0xF1:
-		return "CHOICE_ALT";
-	case 0xF0:
-		return "GO_BACK";
-	case 0xFE:
-		return "END_BRANCH_3";
-	case 0xEB:
-		return "END_ALT";
-	case 0xFF:
-		return "DESC_START";
-	case 0x08:
-		return "SPEAKER";
-	default:
-		return Common::String::format("UNKNOWN(0x%02X)", b);
-	}
-}
-
-Common::String PelrockEngine::cleanText(const Common::String &text) {
-	Common::String cleaned = text;
-
-	// Trim leading/trailing whitespace
-	while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
-		cleaned.deleteChar(0);
-	}
-	while (!cleaned.empty() && Common::isSpace(cleaned.lastChar())) {
-		cleaned.deleteLastChar();
-	}
-
-	// Remove leading [XX][00] patterns
-	while (!cleaned.empty() && cleaned.contains('[')) {
-		uint idx = 0;
-		for (uint i = 0; i < cleaned.size() && i < 15; i++) {
-			if (cleaned[i] == '[') {
-				idx = i;
-				break;
-			}
-		}
-
-		if (idx < 10) {
-			int endIdx = -1;
-			for (uint i = idx; i < cleaned.size() && i < idx + 10; i++) {
-				if (cleaned[i] == ']') {
-					endIdx = i;
-					break;
-				}
-			}
-
-			if (endIdx > (int)idx && endIdx < (int)idx + 10) {
-				cleaned = cleaned.c_str() + endIdx + 1;
-				// Trim leading whitespace again
-				while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
-					cleaned.deleteChar(0);
-				}
-			} else {
-				break;
-			}
-		} else {
-			break;
-		}
-	}
-
-	// Remove single leading control characters
-	if (cleaned.size() > 1) {
-		byte first = (byte)cleaned[0];
-		byte second = (byte)cleaned[1];
-
-		if ((first == 'A' || first == 'H') &&
-			(Common::isUpper(second) || second == 0x83 || second == 0x82 || second == '[')) {
-			cleaned.deleteChar(0);
-			while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
-				cleaned.deleteChar(0);
-			}
-		} else if (strchr("#%')!+,.-\"*&$(/", first)) {
-			cleaned.deleteChar(0);
-			while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
-				cleaned.deleteChar(0);
-			}
-		}
-	}
-
-	return cleaned;
-}
-
-Common::Array<ConversationElement> PelrockEngine::parseConversationElements(const byte *convData, uint32 size) {
-	Common::Array<ConversationElement> elements;
-	Common::HashMap<int, int> choiceIndices; // Track choice index occurrences
-	uint32 pos = 0;
-
-	// First pass: parse elements and track choice indices
-	while (pos < size) {
-		byte b = convData[pos];
-
-		if (b == 0x08) { // SPEAKER
-			pos++;
-			if (pos < size) {
-				byte speakerId = convData[pos];
-				Common::String speaker = (speakerId == 0x0D) ? "ALFRED" : "NPC";
-				pos++;
-
-				// Read text
-				Common::String text;
-				while (pos < size && convData[pos] != 0x08 && convData[pos] != 0xFB &&
-					   convData[pos] != 0xF1 && convData[pos] != 0xF8 && convData[pos] != 0xFD &&
-					   convData[pos] != 0xFC && convData[pos] != 0xF4 && convData[pos] != 0xF7 &&
-					   convData[pos] != 0xF5 && convData[pos] != 0xFE && convData[pos] != 0xEB &&
-					   convData[pos] != 0xF0) {
-					char32_t ch = decodeByte(convData[pos]);
-					if (ch != '.') {
-						text += ch;
-					}
-					pos++;
-				}
-
-				text = cleanText(text);
-				if (!text.empty()) {
-					ConversationElement elem;
-					elem.type = ConversationElement::DIALOGUE;
-					elem.speakerId = speakerId;
-					elem.speaker = speaker;
-					elem.text = text;
-					elem.choiceIndex = -1;
-					elements.push_back(elem);
-				}
-			}
-		} else if (b == 0xFB || b == 0xF1) { // CHOICE marker
-			pos++;
-			int choiceIndex = -1;
-			if (pos < size) {
-				choiceIndex = convData[pos];
-				// Track this choice index
-				if (choiceIndices.contains(choiceIndex)) {
-					choiceIndices[choiceIndex]++;
-				} else {
-					choiceIndices[choiceIndex] = 1;
-				}
-				pos++;
-			}
-
-			// Skip next 2 bytes (speaker marker)
-			if (pos < size)
-				pos++;
-			if (pos < size)
-				pos++;
-
-			// Read text
-			Common::String text;
-			while (pos < size && convData[pos] != 0x08 && convData[pos] != 0xFB &&
-				   convData[pos] != 0xF1 && convData[pos] != 0xF8 && convData[pos] != 0xFD &&
-				   convData[pos] != 0xFC && convData[pos] != 0xF4 && convData[pos] != 0xF7 &&
-				   convData[pos] != 0xF5 && convData[pos] != 0xFE && convData[pos] != 0xEB &&
-				   convData[pos] != 0xF0) {
-				char32_t ch = decodeByte(convData[pos]);
-				if (ch != '.') {
-					text += ch;
-				}
-				pos++;
-			}
-
-			text = cleanText(text);
-			if (!text.empty()) {
-				ConversationElement elem;
-				elem.type = ConversationElement::CHOICE_MARKER;
-				elem.text = text;
-				elem.choiceIndex = choiceIndex;
-				elements.push_back(elem);
-			}
-		} else if (b == 0xF8) { // ACTION
-			pos += 3;
-		} else if (b == 0xF4) { // END_CONV
-			ConversationElement elem;
-			elem.type = ConversationElement::END_CONV;
-			elements.push_back(elem);
-			pos++;
-		} else if (b == 0xF7) { // END_BRANCH
-			ConversationElement elem;
-			elem.type = ConversationElement::END_BRANCH;
-			elements.push_back(elem);
-			pos++;
-		} else if (b == 0xFD || b == 0xFC || b == 0xF5 || b == 0xFE || b == 0xEB || b == 0xF0) {
-			pos++;
-		} else {
-			pos++;
-		}
-	}
-
-	// Second pass: mark which indices are actual choices (appear multiple times)
-	for (uint i = 0; i < elements.size(); i++) {
-		if (elements[i].choiceIndex >= 0) {
-			elements[i].isRealChoice = (choiceIndices[elements[i].choiceIndex] > 1);
-		}
-	}
-
-	return elements;
-}
-
-Common::Array<ConversationNode> PelrockEngine::buildTreeStructure(const Common::Array<ConversationElement> &elements) {
-	Common::Array<ConversationNode> roots;
-	Common::Array<StackEntry> stack;
-	ConversationNode *currentRoot = nullptr;
-	uint i = 0;
-
-	while (i < elements.size()) {
-		const ConversationElement &elem = elements[i];
-
-		if (elem.type == ConversationElement::DIALOGUE && elem.speaker == "NPC") {
-			if (stack.empty()) {
-				// New root conversation
-				ConversationNode root;
-				root.type = ConversationNode::ROOT;
-				root.text = elem.text;
-				root.speaker = "NPC";
-				root.speakerId = elem.speakerId;
-				roots.push_back(root);
-				currentRoot = &roots[roots.size() - 1];
-			} else {
-				// NPC response within a branch
-				ConversationNode *parent = stack[stack.size() - 1].node;
-				ConversationNode response;
-				response.type = ConversationNode::RESPONSE;
-				response.speaker = "NPC";
-				response.speakerId = elem.speakerId;
-				response.text = elem.text;
-				parent->responses.push_back(response);
-			}
-			i++;
-
-		} else if (elem.type == ConversationElement::CHOICE_MARKER) {
-			if (elem.isRealChoice) {
-				// Real choice - player selects from menu
-				ConversationNode choiceNode;
-				choiceNode.type = ConversationNode::CHOICE;
-				choiceNode.text = elem.text;
-				choiceNode.speaker = "ALFRED";
-				choiceNode.speakerId = 0x0D; // Player
-				choiceNode.choiceIndex = elem.choiceIndex;
-
-				// Find where to attach this choice
-				while (!stack.empty() && stack[stack.size() - 1].index >= elem.choiceIndex) {
-					stack.pop_back();
-				}
-
-				if (!stack.empty()) {
-					ConversationNode *parent = stack[stack.size() - 1].node;
-					parent->subchoices.push_back(choiceNode);
-
-					// Get pointer to the newly added choice
-					ConversationNode *newChoice = &parent->subchoices[parent->subchoices.size() - 1];
-
-					StackEntry entry;
-					entry.node = newChoice;
-					entry.index = elem.choiceIndex;
-					stack.push_back(entry);
-				} else {
-					if (currentRoot) {
-						currentRoot->choices.push_back(choiceNode);
-
-						// Get pointer to the newly added choice
-						ConversationNode *newChoice = &currentRoot->choices[currentRoot->choices.size() - 1];
-
-						StackEntry entry;
-						entry.node = newChoice;
-						entry.index = elem.choiceIndex;
-						stack.push_back(entry);
-					}
-				}
-			} else {
-				// Auto-dialogue - ALFRED just speaks
-				if (!stack.empty()) {
-					ConversationNode *parent = stack[stack.size() - 1].node;
-					ConversationNode response;
-					response.type = ConversationNode::RESPONSE;
-					response.speaker = "ALFRED";
-					response.speakerId = 0x0D;
-					response.text = elem.text;
-					parent->responses.push_back(response);
-				}
-			}
-			i++;
-
-		} else if (elem.type == ConversationElement::DIALOGUE && elem.speaker == "ALFRED") {
-			if (!stack.empty()) {
-				ConversationNode *parent = stack[stack.size() - 1].node;
-				ConversationNode response;
-				response.type = ConversationNode::RESPONSE;
-				response.speaker = "ALFRED";
-				response.text = elem.text;
-				response.speakerId = 0x0D;
-				parent->responses.push_back(response);
-			}
-			i++;
-
-		} else if (elem.type == ConversationElement::END_CONV) {
-			if (!stack.empty()) {
-				stack[stack.size() - 1].node->terminated = true;
-				stack.pop_back();
-			}
-			i++;
-
-		} else if (elem.type == ConversationElement::END_BRANCH) {
-			stack.clear();
-			currentRoot = nullptr;
-			i++;
-
-		} else {
-			i++;
-		}
-	}
-
-	return roots;
-}
-
-Common::Array<ConversationNode> PelrockEngine::loadConversations(Common::File *roomFile, int roomOffset, uint32_t startPos) {
-
-	debug("Loading conversations starting at position %d", startPos);
-
-	uint32_t pair12_offset_pos = roomOffset + (12 * 8);
-	roomFile->seek(pair12_offset_pos, SEEK_SET);
-	uint32_t pair12_data_offset = roomFile->readUint32LE();
-	uint32_t pair12_size = roomFile->readUint32LE();
-
-	// startPos += 2;
-	uint32_t conversation_start = pair12_data_offset + startPos;
-	uint32_t conversation_size = pair12_size - startPos;
-
-	roomFile->seek(conversation_start, SEEK_SET);
-	byte *data = new byte[conversation_size];
-	roomFile->read(data, conversation_size);
-
-	Common::Array<ConversationElement> elements = parseConversationElements(data, conversation_size);
-	Common::Array<ConversationNode> roots = buildTreeStructure(elements);
-	return roots;
-}
-
-// void PelrockEngine::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
-// 	uint32_t outPos = 0;
-
-// 	Common::Array<Description> descriptions = loadRoomDescriptions(roomFile, roomOffset, outPos);
-// 	debug("After decsriptions, position is %d", outPos);
-// 	Common::Array<ConversationNode> roots = loadConversations(roomFile, roomOffset, outPos);
-// 	for (int i = 0; i < roots.size(); i++) {
-// 		if (roots[i].text.empty()) {
-// 			continue;
-// 		}
-// 		debug("Conversation %d: %s", i, roots[i].text.c_str());
-// 	}
-// 	_currentRoomConversations = roots;
-
-// 	Common::Array<AnimSet> anims = loadRoomAnimations(roomFile, roomOffset);
-
-// 	Common::Array<HotSpot> hotspots;
-// 	for (int i = 0; i < anims.size(); i++) {
-
-// 		HotSpot thisHotspot;
-// 		thisHotspot.index = i;
-// 		thisHotspot.x = anims[i].x;
-// 		thisHotspot.y = anims[i].y;
-// 		thisHotspot.w = anims[i].w;
-// 		thisHotspot.h = anims[i].h;
-// 		thisHotspot.extra = anims[i].extra;
-// 		thisHotspot.type = anims[i].actionFlags;
-// 		thisHotspot.isEnabled = !anims[i].isDisabled;
-// 		hotspots.push_back(thisHotspot);
-// 	}
-
-// 	Common::Array<HotSpot> staticHotspots = loadHotspots(roomFile, roomOffset);
-// 	Common::Array<Exit> exits = loadExits(roomFile, roomOffset);
-
-// 	Common::Array<WalkBox> walkboxes = loadWalkboxes(roomFile, roomOffset);
-
-// 	debug("total descriptions = %d, anims = %d, hotspots = %d", descriptions.size(), anims.size(), staticHotspots.size());
-// 	for (int i = 0; i < staticHotspots.size(); i++) {
-// 		HotSpot hotspot = staticHotspots[i];
-// 		hotspot.index = anims.size() + i;
-// 		hotspots.push_back(hotspot);
-// 	}
-
-// 	int walkboxCount = 0;
-
-// 	_currentRoomAnims = anims;
-// 	_currentRoomHotspots = hotspots;
-// 	_currentRoomExits = exits;
-// 	_currentRoomWalkboxes = walkboxes;
-// 	_currentRoomDescriptions = descriptions;
-
-// 	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
-// 		HotSpot hotspot = _currentRoomHotspots[i];
-// 		drawRect(_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 200 + i);
-// 	}
-
-// 	for (int i = 0; i < _currentRoomExits.size(); i++) {
-// 		Exit exit = _currentRoomExits[i];
-// 		// drawRect(_screen, exit.x, exit.y, exit.w, exit.h, 100 + i);
-// 	}
-// }
-
-void readUntilBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *&buffer, size_t &outSize) {
-	const char marker[] = "BUDA";
-	const int markerLen = 4;
-	size_t bufferSize = 4096;
-	size_t pos = 0;
-
-	buffer = (byte *)malloc(bufferSize);
-	stream->seek(startPos, SEEK_SET);
-	while (!stream->eos()) {
-		byte b = stream->readByte();
-		if (pos + 1 > bufferSize) {
-			bufferSize *= 2;
-			buffer = (byte *)realloc(buffer, bufferSize);
-		}
-		buffer[pos++] = b;
-
-		// Check for marker at the end of buffer
-		if (pos >= markerLen &&
-			buffer[pos - 4] == 'B' &&
-			buffer[pos - 3] == 'U' &&
-			buffer[pos - 2] == 'D' &&
-			buffer[pos - 1] == 'A') {
-			break;
-		}
-	}
-	outSize = pos;
-}
-
-void PelrockEngine::loadRoomTalkingAnimations(int roomNumber) {
-
-	int headerIndex = roomNumber;
-	uint32 offset = kTalkingAnimHeaderSize * headerIndex;
-
-	TalkinAnimHeader talkHeader;
-	Common::File talkFile;
-	if (!talkFile.open("ALFRED.2")) {
-		error("Couldnt find file ALFRED.2");
-	}
-
-	talkFile.seek(offset, SEEK_SET);
-
-	talkHeader.spritePointer = talkFile.readUint32LE();
-	talkFile.read(&talkHeader.unknown2, 3);
-	talkHeader.offsetXAnimA = talkFile.readByte();
-	talkHeader.offsetYAnimA = talkFile.readByte();
-	talkHeader.wAnimA = talkFile.readByte();
-	talkHeader.hAnimA = talkFile.readByte();
-	talkFile.read(&talkHeader.unknown3, 2);
-	talkHeader.numFramesAnimA = talkFile.readByte();
-	talkFile.read(&talkHeader.unknown4, 5);
-
-	talkHeader.offsetXAnimB = talkFile.readByte();
-	talkHeader.offsetYAnimB = talkFile.readByte();
-	talkHeader.wAnimB = talkFile.readByte();
-	talkHeader.hAnimB = talkFile.readByte();
-	talkFile.read(&talkHeader.unknown5, 2);
-	talkHeader.numFramesAnimB = talkFile.readByte();
-	talkFile.read(&talkHeader.unknown6, 29);
-	debug("Talking anim header for room %d: spritePointer=%d, wA=%d, hA=%d, framesA=%d, wB=%d, hB=%d, framesB=%d", roomNumber, talkHeader.spritePointer, talkHeader.wAnimA, talkHeader.hAnimA, talkHeader.numFramesAnimA, talkHeader.wAnimB, talkHeader.hAnimB, talkHeader.numFramesAnimB);
-
-	if (talkHeader.spritePointer == 0) {
-		debug("No talking animation for room %d", roomNumber);
-		talkFile.close();
-		return;
-	}
-
-	// if(talkHeader.animA != nullptr) {
-	// 	delete[] talkHeader.animA;
-	// 	talkHeader.animA = nullptr;
-	// }
-	talkHeader.animA = new byte *[talkHeader.numFramesAnimA];
-
-	byte *data = nullptr;
-	int animASize = talkHeader.wAnimA * talkHeader.hAnimA * talkHeader.numFramesAnimA;
-	byte *decompressed = nullptr;
-	size_t dataSize = 0;
-	readUntilBuda(&talkFile, talkHeader.spritePointer, data, dataSize);
-	size_t decompressedSize = rleDecompress(data, dataSize, 0, dataSize, &decompressed);
-	free(data);
-	debug("Decompressed talking anim A size: %zu, decompressed size: %zu", dataSize, decompressedSize);
-	for (int i = 0; i < talkHeader.numFramesAnimA; i++) {
-		talkHeader.animA[i] = new byte[talkHeader.wAnimA * talkHeader.hAnimA];
-		Common::copy(decompressed + (i * talkHeader.wAnimA * talkHeader.hAnimA), decompressed + ((i + 1) * talkHeader.wAnimA * talkHeader.hAnimA), talkHeader.animA[i]);
-	}
-
-	if (talkHeader.numFramesAnimB > 0) {
-		// if(talkHeader.animA != nullptr) {
-		// 	delete[] talkHeader.animA;
-		// 	talkHeader.animA = nullptr;
-		// }
-		talkHeader.animB = new byte *[talkHeader.numFramesAnimB];
-		for (int i = 0; i < talkHeader.numFramesAnimB; i++) {
-			talkHeader.animB[i] = new byte[talkHeader.wAnimB * talkHeader.hAnimB];
-			Common::copy(decompressed + animASize + (i * talkHeader.wAnimB * talkHeader.hAnimB), decompressed + animASize + ((i + 1) * talkHeader.wAnimB * talkHeader.hAnimB), talkHeader.animB[i]);
-		}
-	}
-	free(decompressed);
-	_talkingAnimHeader = talkHeader;
-
-	talkFile.close();
-}
-
 void PelrockEngine::loadCursors() {
 	Common::File alfred7File;
 	if (!alfred7File.open("ALFRED.7")) {
@@ -1033,61 +275,6 @@ void PelrockEngine::loadInteractionIcons() {
 	alfred4File.close();
 }
 
-// Common::Array<Exit> PelrockEngine::loadExits(Common::File *roomFile, int roomOffset) {
-// 	Common::Array<Exit> exits;
-// 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
-// 	roomFile->seek(pair10_offset_pos, SEEK_SET);
-// 	uint32_t pair10_data_offset = roomFile->readUint32LE();
-// 	uint32_t pair10_size = roomFile->readUint32LE();
-// 	roomFile->seek(pair10_data_offset + 0x1BE, SEEK_SET);
-// 	int exit_count = roomFile->readByte();
-// 	roomFile->seek(pair10_data_offset + 0x1BF, SEEK_SET);
-// 	for (int i = 0; i < exit_count; i++) {
-// 		Exit exit;
-// 		exit.targetRoom = roomFile->readUint16LE();
-// 		exit.flags = roomFile->readByte();
-// 		exit.x = roomFile->readUint16LE();
-// 		exit.y = roomFile->readUint16LE();
-// 		exit.w = roomFile->readByte();
-// 		exit.h = roomFile->readByte();
-
-// 		exit.targetX = roomFile->readUint16LE();
-// 		exit.targetY = roomFile->readUint16LE();
-// 		exit.dir = roomFile->readByte();
-// 		exits.push_back(exit);
-// 	}
-// 	return exits;
-// }
-
-// Common::Array<HotSpot> PelrockEngine::loadHotspots(Common::File *roomFile, int roomOffset) {
-// 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
-// 	debug("Hotspot(10)  pair offset position: %d", pair10_offset_pos);
-// 	roomFile->seek(pair10_offset_pos, SEEK_SET);
-// 	uint32_t pair10_data_offset = roomFile->readUint32LE();
-// 	uint32_t pair10_size = roomFile->readUint32LE();
-// 	uint32_t count_offset = pair10_data_offset + 0x47a;
-// 	roomFile->seek(count_offset, SEEK_SET);
-// 	byte hotspot_count = roomFile->readByte();
-// 	uint32_t hotspot_data_start = pair10_data_offset + 0x47c;
-// 	Common::Array<HotSpot> hotspots;
-// 	for (int i = 0; i < hotspot_count; i++) {
-// 		uint32_t obj_offset = hotspot_data_start + i * 9;
-// 		roomFile->seek(obj_offset, SEEK_SET);
-// 		HotSpot spot;
-// 		spot.type = roomFile->readByte();
-// 		spot.x = roomFile->readUint16LE();
-// 		spot.y = roomFile->readUint16LE();
-// 		spot.w = roomFile->readByte();
-// 		spot.h = roomFile->readByte();
-// 		spot.extra = roomFile->readUint16LE();
-// 		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, spot.type, spot.x, spot.y, spot.w, spot.h, spot.extra);
-// 		hotspots.push_back(spot);
-// 	}
-// 	return hotspots;
-// 	// uint32_t hover_areas_start = pair10_data_offset + 0x1BE;
-// 	// roomFile->seek(hover_areas_start, SEEK_SET);
-// }
-
 void extractSingleFrame(byte *source, byte *dest, int frameIndex, int frameWidth, int frameHeight) {
 	for (int y = 0; y < frameHeight; y++) {
 		for (int x = 0; x < frameWidth; x++) {
@@ -1237,48 +424,48 @@ void PelrockEngine::frames() {
 		memcpy(_compositeBuffer, _currentBackground, 640 * 400);
 
 		// debug("Game tick!");
-		for (int i = 0; i < _currentRoomAnims.size(); i++) {
+		for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
 			// debug("Processing animation set %d, numAnims %d", num, i->numAnims);
-
-			int x = _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].x;
-			int y = _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].y;
-			int w = _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].w;
-			int h = _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].h;
-			int extra = _currentRoomAnims[i].extra;
+			AnimSet &animSet = _room->_currentRoomAnims[i];
+			int x = animSet.animData[animSet.curAnimIndex].x;
+			int y = animSet.animData[animSet.curAnimIndex].y;
+			int w = animSet.animData[animSet.curAnimIndex].w;
+			int h = animSet.animData[animSet.curAnimIndex].h;
+			int extra = animSet.extra;
 
 			if (whichNPCTalking == extra) {
 				// debug("Skipping anim set %d because NPC is talking", i);
-				talkNPC(&_currentRoomAnims[i], i);
+				talkNPC(&animSet, i);
 				continue;
 			}
 
-			int frameSize = _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].w * _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].h;
-			int curFrame = _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curFrame;
+			int frameSize = animSet.animData[animSet.curAnimIndex].w * animSet.animData[animSet.curAnimIndex].h;
+			int curFrame = animSet.animData[animSet.curAnimIndex].curFrame;
 			byte *frame = new byte[frameSize];
-			Common::copy(_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].animData + (curFrame * _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].h * _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].w), _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].animData + (curFrame * _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].h * _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].w) + (frameSize), frame);
+			Common::copy(animSet.animData[animSet.curAnimIndex].animData + (curFrame * animSet.animData[animSet.curAnimIndex].h * animSet.animData[animSet.curAnimIndex].w), animSet.animData[animSet.curAnimIndex].animData + (curFrame * animSet.animData[animSet.curAnimIndex].h * animSet.animData[animSet.curAnimIndex].w) + (frameSize), frame);
 
-			drawSpriteToBuffer(_compositeBuffer, 640, frame, _currentRoomAnims[i].x, _currentRoomAnims[i].y, _currentRoomAnims[i].w, _currentRoomAnims[i].h, 255);
+			drawSpriteToBuffer(_compositeBuffer, 640, frame, animSet.x, animSet.y, animSet.w, animSet.h, 255);
 
-			if (_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].elpapsedFrames == _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].speed) {
-				_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].elpapsedFrames = 0;
-				if (_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curFrame < _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].nframes - 1) {
-					_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curFrame++;
+			if (animSet.animData[animSet.curAnimIndex].elpapsedFrames == animSet.animData[animSet.curAnimIndex].speed) {
+				animSet.animData[animSet.curAnimIndex].elpapsedFrames = 0;
+				if (animSet.animData[animSet.curAnimIndex].curFrame < animSet.animData[animSet.curAnimIndex].nframes - 1) {
+					animSet.animData[animSet.curAnimIndex].curFrame++;
 				} else {
-					if (_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curLoop < _currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].loopCount - 1) {
-						_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curFrame = 0;
-						_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curLoop++;
+					if (animSet.animData[animSet.curAnimIndex].curLoop < animSet.animData[animSet.curAnimIndex].loopCount - 1) {
+						animSet.animData[animSet.curAnimIndex].curFrame = 0;
+						animSet.animData[animSet.curAnimIndex].curLoop++;
 					} else {
-						_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curFrame = 0;
-						_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].curLoop = 0;
-						if (_currentRoomAnims[i].curAnimIndex < _currentRoomAnims[i].numAnims - 1) {
-							_currentRoomAnims[i].curAnimIndex++;
+						animSet.animData[animSet.curAnimIndex].curFrame = 0;
+						animSet.animData[animSet.curAnimIndex].curLoop = 0;
+						if (animSet.curAnimIndex < animSet.numAnims - 1) {
+							animSet.curAnimIndex++;
 						} else {
-							_currentRoomAnims[i].curAnimIndex = 0;
+							animSet.curAnimIndex = 0;
 						}
 					}
 				}
 			} else {
-				_currentRoomAnims[i].animData[_currentRoomAnims[i].curAnimIndex].elpapsedFrames++;
+				animSet.animData[animSet.curAnimIndex].elpapsedFrames++;
 			}
 		}
 
@@ -1398,9 +585,9 @@ void PelrockEngine::frames() {
 		}
 
 		// debug("Drawing walkboxes..., %d, _currentRoomWalkboxes.size()=%d",  _currentRoomWalkboxes.size(), _currentRoomWalkboxes.size());
-		for (int i = 0; i < _currentRoomWalkboxes.size(); i++) {
+		for (int i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
 			// debug("Drawing walkbox %d", i);
-			WalkBox box = _currentRoomWalkboxes[i];
+			WalkBox box = _room->_currentRoomWalkboxes[i];
 			// drawRect(_screen, box.x, box.y, box.w, box.h, 150 + i);
 		}
 		if (_curWalkTarget.x < 640 && _curWalkTarget.y < 400 && _curWalkTarget.x >= 0 && _curWalkTarget.y >= 0) {
@@ -1472,15 +659,15 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 		}
 		_displayPopup = true;
 		_currentPopupFrame = 0;
-		_currentHotspot = &_currentRoomHotspots[hotspotIndex];
+		_currentHotspot = &_room->_currentRoomHotspots[hotspotIndex];
 		debug("Current hotspot type: %d", _currentHotspot->type);
 	}
 }
 
 int PelrockEngine::isHotspotUnder(int x, int y) {
 
-	for (size_t i = 0; i < _currentRoomHotspots.size(); i++) {
-		HotSpot hotspot = _currentRoomHotspots[i];
+	for (size_t i = 0; i < _room->_currentRoomHotspots.size(); i++) {
+		HotSpot hotspot = _room->_currentRoomHotspots[i];
 		if (hotspot.isEnabled &&
 			mouseX >= hotspot.x && mouseX <= (hotspot.x + hotspot.w) &&
 			mouseY >= hotspot.y && mouseY <= (hotspot.y + hotspot.h)) {
@@ -1491,11 +678,11 @@ int PelrockEngine::isHotspotUnder(int x, int y) {
 }
 
 Exit *PelrockEngine::isExitUnder(int x, int y) {
-	for (int i = 0; i < _currentRoomExits.size(); i++) {
-		Exit exit = _currentRoomExits[i];
+	for (int i = 0; i < _room->_currentRoomExits.size(); i++) {
+		Exit exit = _room->_currentRoomExits[i];
 		if (x >= exit.x && x <= (exit.x + exit.w) &&
 			y >= exit.y && y <= (exit.y + exit.h)) {
-			return &(_currentRoomExits[i]);
+			return &(_room->_currentRoomExits[i]);
 		}
 	}
 	return nullptr;
@@ -1534,22 +721,24 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 void PelrockEngine::talkNPC(AnimSet *animSet, int index) {
 	// Change with the right index
 
-	int x = animSet->x + (index ? _talkingAnimHeader.offsetXAnimB : _talkingAnimHeader.offsetXAnimA);
-	int y = animSet->y + (index ? _talkingAnimHeader.offsetYAnimB : _talkingAnimHeader.offsetYAnimA);
+	TalkinAnimHeader animHeader = _room->_talkingAnimHeader;
+
+	int x = animSet->x + (index ? animHeader.offsetXAnimB : animHeader.offsetXAnimA);
+	int y = animSet->y + (index ? animHeader.offsetYAnimB : animHeader.offsetYAnimA);
 
-	int w = index ? _talkingAnimHeader.wAnimB : _talkingAnimHeader.wAnimA;
-	int h = index ? _talkingAnimHeader.hAnimB : _talkingAnimHeader.hAnimA;
-	int numFrames = index ? _talkingAnimHeader.numFramesAnimB : _talkingAnimHeader.numFramesAnimA;
-	int curFrame = index ? _talkingAnimHeader.currentFrameAnimB++ : _talkingAnimHeader.currentFrameAnimA++;
+	int w = index ? animHeader.wAnimB : animHeader.wAnimA;
+	int h = index ? animHeader.hAnimB : animHeader.hAnimA;
+	int numFrames = index ? animHeader.numFramesAnimB : animHeader.numFramesAnimA;
+	int curFrame = index ? animHeader.currentFrameAnimB++ : animHeader.currentFrameAnimA++;
 	if (curFrame >= numFrames) {
 		if (index) {
-			_talkingAnimHeader.currentFrameAnimB = 0;
+			animHeader.currentFrameAnimB = 0;
 		} else {
-			_talkingAnimHeader.currentFrameAnimA = 0;
+			animHeader.currentFrameAnimA = 0;
 		}
 		curFrame = 0;
 	}
-	byte *frame = index ? _talkingAnimHeader.animB[curFrame] : _talkingAnimHeader.animA[curFrame];
+	byte *frame = index ? animHeader.animB[curFrame] : animHeader.animA[curFrame];
 
 	debug("Talking NPC frame %d/%d, x=%d, y=%d, w=%d, h=%d", curFrame, numFrames, x, y, w, h);
 
@@ -1731,7 +920,7 @@ uint16_t PelrockEngine::generate_movement_steps(uint8_t *path_buffer,
 	// Generate movements for each walkbox in path
 	for (uint16_t i = 0; i < path_length && path_buffer[i] != PATH_END; i++) {
 		uint8_t box_index = path_buffer[i];
-		WalkBox *box = &_currentRoomWalkboxes[box_index];
+		WalkBox *box = &_room->_currentRoomWalkboxes[box_index];
 
 		MovementStep step;
 		calculate_movement_to_target(current_x, current_y,
@@ -1831,8 +1020,8 @@ uint16_t PelrockEngine::build_walkbox_path(
 }
 
 void PelrockEngine::clear_visited_flags() {
-	for (int i = 0; i < _currentRoomWalkboxes.size(); i++) {
-		_currentRoomWalkboxes[i].flags = 0;
+	for (int i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
+		_room->_currentRoomWalkboxes[i].flags = 0;
 	}
 }
 
@@ -1855,25 +1044,25 @@ bool walkboxes_adjacent(WalkBox *box1, WalkBox *box2) {
 }
 
 uint8_t PelrockEngine::get_adjacent_walkbox(uint8_t current_box_index) {
-	WalkBox *current_box = &_currentRoomWalkboxes[current_box_index];
+	WalkBox *current_box = &_room->_currentRoomWalkboxes[current_box_index];
 
 	// Mark current walkbox as visited
 	current_box->flags = 0x01;
 
 	// Search for adjacent unvisited walkbox
-	for (uint8_t i = 0; i < _currentRoomWalkboxes.size(); i++) {
+	for (uint8_t i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
 		// Skip current walkbox
 		if (i == current_box_index) {
 			continue;
 		}
 
 		// Skip already visited walkboxes
-		if (_currentRoomWalkboxes[i].flags == 0x01) {
+		if (_room->_currentRoomWalkboxes[i].flags == 0x01) {
 			continue;
 		}
 
 		// Check if walkboxes are adjacent
-		if (walkboxes_adjacent(current_box, &_currentRoomWalkboxes[i])) {
+		if (walkboxes_adjacent(current_box, &_room->_currentRoomWalkboxes[i])) {
 			return i;
 		}
 	}
@@ -1889,8 +1078,8 @@ bool PelrockEngine::point_in_walkbox(WalkBox *box, uint16_t x, uint16_t y) {
 }
 
 uint8_t PelrockEngine::find_walkbox_for_point(uint16_t x, uint16_t y) {
-	for (uint8_t i = 0; i < _currentRoomWalkboxes.size(); i++) {
-		if (point_in_walkbox(&_currentRoomWalkboxes[i], x, y)) {
+	for (uint8_t i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
+		if (point_in_walkbox(&_room->_currentRoomWalkboxes[i], x, y)) {
 			return i;
 		}
 	}
@@ -1912,7 +1101,7 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 		if (lookRect.contains(x, y)) {
 			debug("Look action clicked");
 			walkTo(_currentHotspot->x, _currentHotspot->y);
-			sayAlfred(_currentRoomDescriptions[_currentHotspot->index].text);
+			sayAlfred(_room->_currentRoomDescriptions[_currentHotspot->index].text);
 			_displayPopup = false;
 			return;
 		}
@@ -1999,25 +1188,25 @@ Common::Point PelrockEngine::calculateWalkTarget(int mouseX, int mouseY) {
 
 	// for (Common::List<WalkBox>::iterator it = _currentRoomWalkboxes.begin();
 	//  it != _currentRoomWalkboxes.end(); ++it) {
-	for (size_t i = 0; i < _currentRoomWalkboxes.size(); i++) {
+	for (size_t i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
 
 		// Calculate distance from source point to this walkbox (Manhattan distance)
 		int dx = 0;
 		int dy = 0;
 
 		// Calculate horizontal distance
-		if (sourceX < _currentRoomWalkboxes[i].x) {
-			dx = _currentRoomWalkboxes[i].x - sourceX;
-		} else if (sourceX > _currentRoomWalkboxes[i].x + _currentRoomWalkboxes[i].w) {
-			dx = sourceX - (_currentRoomWalkboxes[i].x + _currentRoomWalkboxes[i].w);
+		if (sourceX < _room->_currentRoomWalkboxes[i].x) {
+			dx = _room->_currentRoomWalkboxes[i].x - sourceX;
+		} else if (sourceX > _room->_currentRoomWalkboxes[i].x + _room->_currentRoomWalkboxes[i].w) {
+			dx = sourceX - (_room->_currentRoomWalkboxes[i].x + _room->_currentRoomWalkboxes[i].w);
 		}
 		// else: sourceX is inside walkbox horizontally, dx = 0
 
 		// Calculate vertical distance
-		if (sourceY < _currentRoomWalkboxes[i].y) {
-			dy = _currentRoomWalkboxes[i].y - sourceY;
-		} else if (sourceY > _currentRoomWalkboxes[i].y + _currentRoomWalkboxes[i].h) {
-			dy = sourceY - (_currentRoomWalkboxes[i].y + _currentRoomWalkboxes[i].h);
+		if (sourceY < _room->_currentRoomWalkboxes[i].y) {
+			dy = _room->_currentRoomWalkboxes[i].y - sourceY;
+		} else if (sourceY > _room->_currentRoomWalkboxes[i].y + _room->_currentRoomWalkboxes[i].h) {
+			dy = sourceY - (_room->_currentRoomWalkboxes[i].y + _room->_currentRoomWalkboxes[i].h);
 		}
 		// else: sourceY is inside walkbox vertically, dy = 0
 
@@ -2030,16 +1219,16 @@ Common::Point PelrockEngine::calculateWalkTarget(int mouseX, int mouseY) {
 			int targetX = sourceX;
 			int targetY = sourceY;
 
-			if (sourceX < _currentRoomWalkboxes[i].x) {
-				targetX = _currentRoomWalkboxes[i].x;
-			} else if (sourceX > _currentRoomWalkboxes[i].x + _currentRoomWalkboxes[i].w) {
-				targetX = _currentRoomWalkboxes[i].x + _currentRoomWalkboxes[i].w;
+			if (sourceX < _room->_currentRoomWalkboxes[i].x) {
+				targetX = _room->_currentRoomWalkboxes[i].x;
+			} else if (sourceX > _room->_currentRoomWalkboxes[i].x + _room->_currentRoomWalkboxes[i].w) {
+				targetX = _room->_currentRoomWalkboxes[i].x + _room->_currentRoomWalkboxes[i].w;
 			}
 
-			if (sourceY < _currentRoomWalkboxes[i].y) {
-				targetY = _currentRoomWalkboxes[i].y;
-			} else if (sourceY > _currentRoomWalkboxes[i].y + _currentRoomWalkboxes[i].h) {
-				targetY = _currentRoomWalkboxes[i].y + _currentRoomWalkboxes[i].h;
+			if (sourceY < _room->_currentRoomWalkboxes[i].y) {
+				targetY = _room->_currentRoomWalkboxes[i].y;
+			} else if (sourceY > _room->_currentRoomWalkboxes[i].y + _room->_currentRoomWalkboxes[i].h) {
+				targetY = _room->_currentRoomWalkboxes[i].y + _room->_currentRoomWalkboxes[i].h;
 			}
 
 			bestTarget.x = targetX;
@@ -2233,7 +1422,7 @@ void PelrockEngine::setScreen(int number, int dir) {
 	int roomOffset = number * kRoomStructSize;
 	curAlfredFrame = 0;
 	byte *palette = new byte[256 * 3];
-	getPalette(&roomFile, roomOffset, palette);
+	_room->getPalette(&roomFile, roomOffset, palette);
 
 	int paletteOffset = roomOffset + (11 * 8);
 	roomFile.seek(paletteOffset, SEEK_SET);
@@ -2242,7 +1431,7 @@ void PelrockEngine::setScreen(int number, int dir) {
 	g_system->getPaletteManager()->setPalette(palette, 0, 256);
 
 	byte *background = new byte[640 * 400];
-	getBackground(&roomFile, roomOffset, background);
+	_room->getBackground(&roomFile, roomOffset, background);
 	if (_currentBackground != nullptr)
 		delete[] _currentBackground;
 	_currentBackground = new byte[640 * 400];
@@ -2253,8 +1442,8 @@ void PelrockEngine::setScreen(int number, int dir) {
 		}
 	}
 
-	_resourceManager->loadRoomMetadata(&roomFile, roomOffset);
-	loadRoomTalkingAnimations(number);
+	_room->loadRoomMetadata(&roomFile, roomOffset);
+	_room->loadRoomTalkingAnimations(number);
 
 	_screen->markAllDirty();
 	roomFile.close();
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index feef1bf91bc..4ef14515529 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -40,7 +40,7 @@
 #include "pelrock/detection.h"
 #include "pelrock/fonts/large_font.h"
 #include "pelrock/fonts/small_font.h"
-#include "pelrock/resources.h"
+#include "pelrock/room.h"
 #include "pelrock/types.h"
 
 namespace Pelrock {
@@ -61,20 +61,7 @@ private:
 	void setScreenJava(int s, int dir);
 	void loadAnims();
 
-	// Room data
-	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
-	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
 	void loadAlfredAnims();
-	// Common::Array<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
-	// Common::Array<HotSpot> loadHotspots(Common::File *roomFile, int roomOffset);
-	// Common::Array<Exit> loadExits(Common::File *roomFile, int roomOffset);
-	// Common::Array<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
-	// Common::Array<Description> loadRoomDescriptions(Common::File *roomFile, int roomOffset, uint32_t &outPos);
-
-	Common::String cleanText(const Common::String &text);
-	Common::Array<ConversationElement> parseConversationElements(const byte *convData, uint32 size);
-	Common::Array<ConversationNode> buildTreeStructure(const Common::Array<ConversationElement> &elements);
-	Common::Array<ConversationNode> loadConversations(Common::File *roomFile, int roomOffset, uint32_t startPos);
 
 	void walkTo(int x, int y);
 	bool pathFind(int x, int y, PathContext *context);
@@ -90,9 +77,6 @@ private:
 									 MovementStep *movement_buffer);
 
 	void talk(byte object);
-	Common::String getControlName(byte b);
-	// void loadRoomMetadata(Common::File *roomFile, int roomOffset);
-	void loadRoomTalkingAnimations(int roomNumber);
 	void loadCursors();
 	void loadInteractionIcons();
 	byte *grabBackgroundSlice(int x, int y, int w, int h);
@@ -121,15 +105,12 @@ private:
 
 	ChronoManager *_chronoManager = nullptr;
 
-	// byte *standingAnim = new byte[3060 * 102];
-
 	byte **walkingAnimFrames[4];              // 4 arrays of arrays
 	byte *standingAnimFrames[4] = {nullptr};  // 4 directions
 	int walkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
 	byte **talkingAnimFrames[4];              // 4 arrays of arrays
 	int talkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
 
-	TalkinAnimHeader _talkingAnimHeader;
 
 	PathContext _currentContext;
 	int _current_step = 0;
@@ -186,7 +167,7 @@ private:
 	// int whichScreen = 0;
 	// byte *pixelsShadows; // =new int[640*400];
 
-	ResourceManager *_resourceManager = nullptr;
+	RoomManager *_room = nullptr;
 
 protected:
 	// Engine APIs
@@ -242,13 +223,6 @@ public:
 		Common::Serializer s(stream, nullptr);
 		return syncGame(s);
 	}
-
-	Common::Array<HotSpot> _currentRoomHotspots;
-	Common::Array<AnimSet> _currentRoomAnims;
-	Common::Array<Exit> _currentRoomExits;
-	Common::Array<WalkBox> _currentRoomWalkboxes;
-	Common::Array<Description> _currentRoomDescriptions;
-	Common::Array<ConversationNode> _currentRoomConversations;
 };
 
 extern PelrockEngine *g_engine;
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
deleted file mode 100644
index dac515a606d..00000000000
--- a/engines/pelrock/resources.cpp
+++ /dev/null
@@ -1,312 +0,0 @@
-/* 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 "pelrock/resources.h"
-#include "pelrock/pelrock.h"
-#include "pelrock/util.h"
-
-namespace Pelrock {
-
-ResourceManager::ResourceManager() {
-}
-
-ResourceManager::~ResourceManager() {
-}
-
-Common::Array<Exit> ResourceManager::loadExits(Common::File *roomFile, int roomOffset) {
-	Common::Array<Exit> exits;
-	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
-	roomFile->seek(pair10_offset_pos, SEEK_SET);
-	uint32_t pair10_data_offset = roomFile->readUint32LE();
-	uint32_t pair10_size = roomFile->readUint32LE();
-	roomFile->seek(pair10_data_offset + 0x1BE, SEEK_SET);
-	int exit_count = roomFile->readByte();
-	roomFile->seek(pair10_data_offset + 0x1BF, SEEK_SET);
-	for (int i = 0; i < exit_count; i++) {
-		Exit exit;
-		exit.targetRoom = roomFile->readUint16LE();
-		exit.flags = roomFile->readByte();
-		exit.x = roomFile->readUint16LE();
-		exit.y = roomFile->readUint16LE();
-		exit.w = roomFile->readByte();
-		exit.h = roomFile->readByte();
-
-		exit.targetX = roomFile->readUint16LE();
-		exit.targetY = roomFile->readUint16LE();
-		exit.dir = roomFile->readByte();
-		exits.push_back(exit);
-	}
-	return exits;
-}
-
-Common::Array<HotSpot> ResourceManager::loadHotspots(Common::File *roomFile, int roomOffset) {
-	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
-	debug("Hotspot(10)  pair offset position: %d", pair10_offset_pos);
-	roomFile->seek(pair10_offset_pos, SEEK_SET);
-	uint32_t pair10_data_offset = roomFile->readUint32LE();
-	uint32_t pair10_size = roomFile->readUint32LE();
-	uint32_t count_offset = pair10_data_offset + 0x47a;
-	roomFile->seek(count_offset, SEEK_SET);
-	byte hotspot_count = roomFile->readByte();
-	uint32_t hotspot_data_start = pair10_data_offset + 0x47c;
-	Common::Array<HotSpot> hotspots;
-	for (int i = 0; i < hotspot_count; i++) {
-		uint32_t obj_offset = hotspot_data_start + i * 9;
-		roomFile->seek(obj_offset, SEEK_SET);
-		HotSpot spot;
-		spot.type = roomFile->readByte();
-		spot.x = roomFile->readUint16LE();
-		spot.y = roomFile->readUint16LE();
-		spot.w = roomFile->readByte();
-		spot.h = roomFile->readByte();
-		spot.extra = roomFile->readUint16LE();
-		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, spot.type, spot.x, spot.y, spot.w, spot.h, spot.extra);
-		hotspots.push_back(spot);
-	}
-	return hotspots;
-	// uint32_t hover_areas_start = pair10_data_offset + 0x1BE;
-	// roomFile->seek(hover_areas_start, SEEK_SET);
-}
-
-void ResourceManager::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
-	uint32_t outPos = 0;
-
-	Common::Array<Description> descriptions = loadRoomDescriptions(roomFile, roomOffset, outPos);
-	debug("After decsriptions, position is %d", outPos);
-	// Common::Array<ConversationNode> roots = loadConversations(roomFile, roomOffset, outPos);
-	// for (int i = 0; i < roots.size(); i++) {
-	// 	if (roots[i].text.empty()) {
-	// 		continue;
-	// 	}
-	// 	debug("Conversation %d: %s", i, roots[i].text.c_str());
-	// }
-	// g_engine->_currentRoomConversations = roots;
-
-	Common::Array<AnimSet> anims = loadRoomAnimations(roomFile, roomOffset);
-
-	Common::Array<HotSpot> hotspots;
-	for (int i = 0; i < anims.size(); i++) {
-
-		HotSpot thisHotspot;
-		thisHotspot.index = i;
-		thisHotspot.x = anims[i].x;
-		thisHotspot.y = anims[i].y;
-		thisHotspot.w = anims[i].w;
-		thisHotspot.h = anims[i].h;
-		thisHotspot.extra = anims[i].extra;
-		thisHotspot.type = anims[i].actionFlags;
-		thisHotspot.isEnabled = !anims[i].isDisabled;
-		hotspots.push_back(thisHotspot);
-	}
-
-	Common::Array<HotSpot> staticHotspots = loadHotspots(roomFile, roomOffset);
-	Common::Array<Exit> exits = loadExits(roomFile, roomOffset);
-
-	Common::Array<WalkBox> walkboxes = loadWalkboxes(roomFile, roomOffset);
-
-	debug("total descriptions = %d, anims = %d, hotspots = %d", descriptions.size(), anims.size(), staticHotspots.size());
-	for (int i = 0; i < staticHotspots.size(); i++) {
-		HotSpot hotspot = staticHotspots[i];
-		hotspot.index = anims.size() + i;
-		hotspots.push_back(hotspot);
-	}
-
-	int walkboxCount = 0;
-
-	g_engine->_currentRoomAnims = anims;
-	g_engine->_currentRoomHotspots = hotspots;
-	g_engine->_currentRoomExits = exits;
-	g_engine->_currentRoomWalkboxes = walkboxes;
-	g_engine->_currentRoomDescriptions = descriptions;
-	for (int i = 0; i < g_engine->_currentRoomHotspots.size(); i++) {
-		HotSpot hotspot = g_engine->_currentRoomHotspots[i];
-		drawRect(g_engine->_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 200 + i);
-	}
-
-	for (int i = 0; i < g_engine->_currentRoomExits.size(); i++) {
-		Exit exit = g_engine->_currentRoomExits[i];
-		// drawRect(_screen, exit.x, exit.y, exit.w, exit.h, 100 + i);
-	}
-}
-Common::Array<AnimSet> ResourceManager::loadRoomAnimations(Common::File *roomFile, int roomOffset) {
-	uint32_t pair_offset = roomOffset + (8 * 8);
-	// debug("Sprite pair offset position: %d", pair_offset);
-	roomFile->seek(pair_offset, SEEK_SET);
-	uint32_t offset = roomFile->readUint32LE();
-	uint32_t size = roomFile->readUint32LE();
-
-	byte *data = new byte[size];
-	roomFile->seek(offset, SEEK_SET);
-	roomFile->read(data, size);
-
-	unsigned char *pic = new byte[10000 * 10000];
-	if (offset > 0 && size > 0) {
-		rleDecompress(data, size, 0, size, &pic);
-	} else {
-		return Common::Array<AnimSet>();
-	}
-	Common::Array<AnimSet> anims = Common::Array<AnimSet>();
-	uint32_t spriteEnd = offset + size;
-
-	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
-	uint32_t metadata_start = spriteEnd + 108;
-	uint32_t picOffset = 0;
-	for (int i = 0; i < 7; i++) {
-		uint32_t animOffset = metadata_start + (i * 44);
-		byte *animData = new byte[44];
-		roomFile->seek(animOffset, SEEK_SET);
-		roomFile->read(animData, 44);
-		AnimSet animSet;
-		animSet.x = animData[0] | (animData[1] << 8);
-		animSet.y = animData[2] | (animData[3] << 8);
-		animSet.w = animData[4];
-		animSet.h = animData[5];
-		animSet.extra = animData[6];
-		// roomFile->skip(1); // reserved
-		animSet.numAnims = animData[8];
-		animSet.spriteType = animData[33];
-		animSet.actionFlags = animData[34];
-		animSet.isDisabled = animData[38];
-		if (animSet.numAnims == 0) {
-			break;
-		}
-		animSet.animData = new Anim[animSet.numAnims];
-		// debug("AnimSet %d has %d sub-anims, type %d, actionFlags %d, isDisabled? %d", i, animSet.numAnims, animSet.spriteType, animSet.actionFlags, animSet.isDisabled);
-		int subAnimOffset = 10;
-		for (int j = 0; j < animSet.numAnims; j++) {
-
-			Anim anim;
-			anim.x = animSet.x;
-			anim.y = animSet.y;
-			anim.w = animSet.w;
-			anim.h = animSet.h;
-			anim.curFrame = 0;
-
-			anim.nframes = animData[subAnimOffset + j];
-			anim.loopCount = animData[subAnimOffset + 4 + j];
-			anim.speed = animData[subAnimOffset + 8 + j];
-			anim.animData = new byte[anim.nframes];
-			if (anim.w > 0 && anim.h > 0 && anim.nframes > 0) {
-				uint32_t needed = anim.w * anim.h * anim.nframes;
-				anim.animData = new byte[needed];
-				Common::copy(pic + picOffset, pic + picOffset + needed, anim.animData);
-				animSet.animData[j] = anim;
-				// debug("  Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d loopCount=%d speed=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes, anim.loopCount, anim.speed);
-				picOffset += needed;
-			} else {
-				continue;
-				debug("Anim %d-%d: invalid dimensions, skipping", i, j);
-			}
-			animSet.animData[j] = anim;
-		}
-
-		anims.push_back(animSet);
-	}
-	return anims;
-}
-
-Common::Array<WalkBox> ResourceManager::loadWalkboxes(Common::File *roomFile, int roomOffset) {
-	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
-	roomFile->seek(pair10_offset_pos, SEEK_SET);
-	// roomFile->skip(4);
-	uint32_t pair10_data_offset = roomFile->readUint32LE();
-	uint32_t pair10_size = roomFile->readUint32LE();
-
-	uint32_t walkbox_countOffset = pair10_data_offset + 0x213;
-	roomFile->seek(walkbox_countOffset, SEEK_SET);
-	byte walkbox_count = roomFile->readByte();
-	debug("Walkbox count: %d", walkbox_count);
-	uint32_t walkbox_offset = pair10_data_offset + 0x218;
-	Common::Array<WalkBox> walkboxes;
-	for (int i = 0; i < walkbox_count; i++) {
-		uint32_t box_offset = walkbox_offset + i * 9;
-		roomFile->seek(box_offset, SEEK_SET);
-		int16 x1 = roomFile->readSint16LE();
-		int16 y1 = roomFile->readSint16LE();
-		int16 w = roomFile->readSint16LE();
-		int16 h = roomFile->readSint16LE();
-		byte flags = roomFile->readByte();
-		debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
-		WalkBox box;
-		box.x = x1;
-		box.y = y1;
-		box.w = w;
-		box.h = h;
-		box.flags = flags;
-		walkboxes.push_back(box);
-	}
-	return walkboxes;
-}
-
-Common::Array<Description> ResourceManager::loadRoomDescriptions(Common::File *roomFile, int roomOffset, uint32_t &outPos) {
-	uint32_t pair12_offset_pos = roomOffset + (12 * 8);
-	roomFile->seek(pair12_offset_pos, SEEK_SET);
-	// roomFile->skip(4);
-	uint32_t pair12_data_offset = roomFile->readUint32LE();
-	uint32_t pair12_size = roomFile->readUint32LE();
-
-	roomFile->seek(pair12_data_offset, SEEK_SET);
-	byte *data = new byte[pair12_size];
-	roomFile->read(data, pair12_size);
-	Common::Array<Description> descriptions;
-	uint32_t pos = 0;
-	uint32_t lastDescPos = 0;
-	while (pos < (pair12_size)) {
-		int desc_pos = 0;
-		if (data[pos] == 0xFF) {
-			Description description;
-			description.itemId = data[pos + 1];
-			pos += 3;
-			description.index = data[pos++];
-			description.text = "";
-			// debug("Found description terminator");
-			while (pos < (pair12_size) && data[pos] != 0xFD && pos < (pair12_size)) {
-				// debug(" char: %c", data[pos]);
-				if (data[pos] != 0x00) {
-					description.text.append(1, (char)data[pos]);
-				}
-				if (data[pos] == 0xF8) {
-					description.actionTrigger = data[pos + 1] | data[pos + 2] << 8;
-					debug("Found action trigger: %d", description.actionTrigger);
-					pos += 2;
-					break;
-				}
-				// desc[desc_pos++] = (char)data[pos];
-				// debug("Current desc: %s", desc);
-				pos++;
-			}
-			debug("Found description for item %d index %d, text: %s", description.itemId, description.index, description.text.c_str());
-
-			descriptions.push_back(description);
-			lastDescPos = pos;
-		}
-		pos++;
-	}
-	debug("End of descriptions at position %d", pos);
-	outPos = lastDescPos + 1;
-	delete[] data;
-	// for (Common::List<Common::String>::iterator i = descriptions.begin(); i != descriptions.end(); i++) {
-	// 	debug("Room description: %s", i->c_str());
-	// }
-	return descriptions;
-}
-
-} // End of namespace Pelrock
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
new file mode 100644
index 00000000000..fa5a029d59a
--- /dev/null
+++ b/engines/pelrock/room.cpp
@@ -0,0 +1,825 @@
+/* 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 "pelrock/room.h"
+#include "pelrock/pelrock.h"
+#include "pelrock/util.h"
+
+namespace Pelrock {
+
+RoomManager::RoomManager() {
+}
+
+RoomManager::~RoomManager() {
+	// delete[] _currentRoomHotspots;
+	// delete[] _currentRoomAnims;
+	// delete[] _currentRoomExits;
+	// delete[] _currentRoomWalkboxes;
+	// delete[] _currentRoomDescriptions;
+	// delete[] _currentRoomConversations;
+}
+
+void RoomManager::getPalette(Common::File *roomFile, int roomOffset, byte *palette) {
+	// get palette
+	int paletteOffset = roomOffset + (11 * 8);
+	roomFile->seek(paletteOffset, SEEK_SET);
+	uint32 offset = roomFile->readUint32LE();
+	uint32 size = roomFile->readUint32LE();
+
+	roomFile->seek(offset, SEEK_SET);
+
+	roomFile->read(palette, size);
+	for (int i = 0; i < 256; i++) {
+		palette[i * 3] = palette[i * 3] << 2;
+		palette[i * 3 + 1] = palette[i * 3 + 1] << 2;
+		palette[i * 3 + 2] = palette[i * 3 + 2] << 2;
+	}
+}
+
+void RoomManager::getBackground(Common::File *roomFile, int roomOffset, byte *background) {
+	roomFile->seek(0, SEEK_SET);
+	// get screen
+	size_t combined_size = 0;
+	size_t uncompressed_size = 0;
+	for (int pair_idx = 0; pair_idx < 8; pair_idx++) {
+		uint32_t pair_offset = roomOffset + (pair_idx * 8);
+		if (pair_offset + 8 > roomFile->size())
+			continue;
+
+		roomFile->seek(pair_offset, SEEK_SET);
+		uint32_t offset = roomFile->readUint32LE();
+		uint32_t size = roomFile->readUint32LE();
+		uncompressed_size += size;
+
+		if (offset > 0 && size > 0 && offset < roomFile->size()) {
+			byte *data = new byte[size];
+			roomFile->seek(offset, SEEK_SET);
+			roomFile->read(data, size);
+			uint8_t *block_data = NULL;
+			size_t block_size = rleDecompress(data, size, 0, size, &block_data);
+
+			memcpy(background + combined_size, block_data, block_size);
+			combined_size += block_size + 1;
+			free(block_data);
+			delete[] data;
+		}
+	}
+}
+
+Common::Array<Exit> RoomManager::loadExits(Common::File *roomFile, int roomOffset) {
+	Common::Array<Exit> exits;
+	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
+	roomFile->seek(pair10_offset_pos, SEEK_SET);
+	uint32_t pair10_data_offset = roomFile->readUint32LE();
+	uint32_t pair10_size = roomFile->readUint32LE();
+	roomFile->seek(pair10_data_offset + 0x1BE, SEEK_SET);
+	int exit_count = roomFile->readByte();
+	roomFile->seek(pair10_data_offset + 0x1BF, SEEK_SET);
+	for (int i = 0; i < exit_count; i++) {
+		Exit exit;
+		exit.targetRoom = roomFile->readUint16LE();
+		exit.flags = roomFile->readByte();
+		exit.x = roomFile->readUint16LE();
+		exit.y = roomFile->readUint16LE();
+		exit.w = roomFile->readByte();
+		exit.h = roomFile->readByte();
+
+		exit.targetX = roomFile->readUint16LE();
+		exit.targetY = roomFile->readUint16LE();
+		exit.dir = roomFile->readByte();
+		exits.push_back(exit);
+	}
+	return exits;
+}
+
+Common::Array<HotSpot> RoomManager::loadHotspots(Common::File *roomFile, int roomOffset) {
+	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
+	debug("Hotspot(10)  pair offset position: %d", pair10_offset_pos);
+	roomFile->seek(pair10_offset_pos, SEEK_SET);
+	uint32_t pair10_data_offset = roomFile->readUint32LE();
+	uint32_t pair10_size = roomFile->readUint32LE();
+	uint32_t count_offset = pair10_data_offset + 0x47a;
+	roomFile->seek(count_offset, SEEK_SET);
+	byte hotspot_count = roomFile->readByte();
+	uint32_t hotspot_data_start = pair10_data_offset + 0x47c;
+	Common::Array<HotSpot> hotspots;
+	for (int i = 0; i < hotspot_count; i++) {
+		uint32_t obj_offset = hotspot_data_start + i * 9;
+		roomFile->seek(obj_offset, SEEK_SET);
+		HotSpot spot;
+		spot.type = roomFile->readByte();
+		spot.x = roomFile->readUint16LE();
+		spot.y = roomFile->readUint16LE();
+		spot.w = roomFile->readByte();
+		spot.h = roomFile->readByte();
+		spot.extra = roomFile->readUint16LE();
+		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, spot.type, spot.x, spot.y, spot.w, spot.h, spot.extra);
+		hotspots.push_back(spot);
+	}
+	return hotspots;
+	// uint32_t hover_areas_start = pair10_data_offset + 0x1BE;
+	// roomFile->seek(hover_areas_start, SEEK_SET);
+}
+
+void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
+	uint32_t outPos = 0;
+
+	Common::Array<Description> descriptions = loadRoomDescriptions(roomFile, roomOffset, outPos);
+	debug("After decsriptions, position is %d", outPos);
+	Common::Array<ConversationNode> roots = loadConversations(roomFile, roomOffset, outPos);
+	for (int i = 0; i < roots.size(); i++) {
+		if (roots[i].text.empty()) {
+			continue;
+		}
+		debug("Conversation %d: %s", i, roots[i].text.c_str());
+	}
+	_currentRoomConversations = roots;
+
+	Common::Array<AnimSet> anims = loadRoomAnimations(roomFile, roomOffset);
+
+	Common::Array<HotSpot> hotspots;
+	for (int i = 0; i < anims.size(); i++) {
+
+		HotSpot thisHotspot;
+		thisHotspot.index = i;
+		thisHotspot.x = anims[i].x;
+		thisHotspot.y = anims[i].y;
+		thisHotspot.w = anims[i].w;
+		thisHotspot.h = anims[i].h;
+		thisHotspot.extra = anims[i].extra;
+		thisHotspot.type = anims[i].actionFlags;
+		thisHotspot.isEnabled = !anims[i].isDisabled;
+		hotspots.push_back(thisHotspot);
+	}
+
+	Common::Array<HotSpot> staticHotspots = loadHotspots(roomFile, roomOffset);
+	Common::Array<Exit> exits = loadExits(roomFile, roomOffset);
+
+	Common::Array<WalkBox> walkboxes = loadWalkboxes(roomFile, roomOffset);
+
+	debug("total descriptions = %d, anims = %d, hotspots = %d", descriptions.size(), anims.size(), staticHotspots.size());
+	for (int i = 0; i < staticHotspots.size(); i++) {
+		HotSpot hotspot = staticHotspots[i];
+		hotspot.index = anims.size() + i;
+		hotspots.push_back(hotspot);
+	}
+
+	int walkboxCount = 0;
+
+	_currentRoomAnims = anims;
+	_currentRoomHotspots = hotspots;
+	_currentRoomExits = exits;
+	_currentRoomWalkboxes = walkboxes;
+	_currentRoomDescriptions = descriptions;
+	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
+		HotSpot hotspot = _currentRoomHotspots[i];
+		drawRect(g_engine->_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 200 + i);
+	}
+
+	for (int i = 0; i < _currentRoomExits.size(); i++) {
+		Exit exit = _currentRoomExits[i];
+		// drawRect(_screen, exit.x, exit.y, exit.w, exit.h, 100 + i);
+	}
+}
+Common::Array<AnimSet> RoomManager::loadRoomAnimations(Common::File *roomFile, int roomOffset) {
+	uint32_t pair_offset = roomOffset + (8 * 8);
+	// debug("Sprite pair offset position: %d", pair_offset);
+	roomFile->seek(pair_offset, SEEK_SET);
+	uint32_t offset = roomFile->readUint32LE();
+	uint32_t size = roomFile->readUint32LE();
+
+	byte *data = new byte[size];
+	roomFile->seek(offset, SEEK_SET);
+	roomFile->read(data, size);
+
+	unsigned char *pic = new byte[10000 * 10000];
+	if (offset > 0 && size > 0) {
+		rleDecompress(data, size, 0, size, &pic);
+	} else {
+		return Common::Array<AnimSet>();
+	}
+	Common::Array<AnimSet> anims = Common::Array<AnimSet>();
+	uint32_t spriteEnd = offset + size;
+
+	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
+	uint32_t metadata_start = spriteEnd + 108;
+	uint32_t picOffset = 0;
+	for (int i = 0; i < 7; i++) {
+		uint32_t animOffset = metadata_start + (i * 44);
+		byte *animData = new byte[44];
+		roomFile->seek(animOffset, SEEK_SET);
+		roomFile->read(animData, 44);
+		AnimSet animSet;
+		animSet.x = animData[0] | (animData[1] << 8);
+		animSet.y = animData[2] | (animData[3] << 8);
+		animSet.w = animData[4];
+		animSet.h = animData[5];
+		animSet.extra = animData[6];
+		// roomFile->skip(1); // reserved
+		animSet.numAnims = animData[8];
+		animSet.spriteType = animData[33];
+		animSet.actionFlags = animData[34];
+		animSet.isDisabled = animData[38];
+		if (animSet.numAnims == 0) {
+			break;
+		}
+		animSet.animData = new Anim[animSet.numAnims];
+		// debug("AnimSet %d has %d sub-anims, type %d, actionFlags %d, isDisabled? %d", i, animSet.numAnims, animSet.spriteType, animSet.actionFlags, animSet.isDisabled);
+		int subAnimOffset = 10;
+		for (int j = 0; j < animSet.numAnims; j++) {
+
+			Anim anim;
+			anim.x = animSet.x;
+			anim.y = animSet.y;
+			anim.w = animSet.w;
+			anim.h = animSet.h;
+			anim.curFrame = 0;
+
+			anim.nframes = animData[subAnimOffset + j];
+			anim.loopCount = animData[subAnimOffset + 4 + j];
+			anim.speed = animData[subAnimOffset + 8 + j];
+			anim.animData = new byte[anim.nframes];
+			if (anim.w > 0 && anim.h > 0 && anim.nframes > 0) {
+				uint32_t needed = anim.w * anim.h * anim.nframes;
+				anim.animData = new byte[needed];
+				Common::copy(pic + picOffset, pic + picOffset + needed, anim.animData);
+				animSet.animData[j] = anim;
+				// debug("  Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d loopCount=%d speed=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes, anim.loopCount, anim.speed);
+				picOffset += needed;
+			} else {
+				continue;
+				debug("Anim %d-%d: invalid dimensions, skipping", i, j);
+			}
+			animSet.animData[j] = anim;
+		}
+
+		anims.push_back(animSet);
+	}
+	return anims;
+}
+
+Common::Array<WalkBox> RoomManager::loadWalkboxes(Common::File *roomFile, int roomOffset) {
+	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
+	roomFile->seek(pair10_offset_pos, SEEK_SET);
+	// roomFile->skip(4);
+	uint32_t pair10_data_offset = roomFile->readUint32LE();
+	uint32_t pair10_size = roomFile->readUint32LE();
+
+	uint32_t walkbox_countOffset = pair10_data_offset + 0x213;
+	roomFile->seek(walkbox_countOffset, SEEK_SET);
+	byte walkbox_count = roomFile->readByte();
+	debug("Walkbox count: %d", walkbox_count);
+	uint32_t walkbox_offset = pair10_data_offset + 0x218;
+	Common::Array<WalkBox> walkboxes;
+	for (int i = 0; i < walkbox_count; i++) {
+		uint32_t box_offset = walkbox_offset + i * 9;
+		roomFile->seek(box_offset, SEEK_SET);
+		int16 x1 = roomFile->readSint16LE();
+		int16 y1 = roomFile->readSint16LE();
+		int16 w = roomFile->readSint16LE();
+		int16 h = roomFile->readSint16LE();
+		byte flags = roomFile->readByte();
+		debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
+		WalkBox box;
+		box.x = x1;
+		box.y = y1;
+		box.w = w;
+		box.h = h;
+		box.flags = flags;
+		walkboxes.push_back(box);
+	}
+	return walkboxes;
+}
+
+Common::Array<Description> RoomManager::loadRoomDescriptions(Common::File *roomFile, int roomOffset, uint32_t &outPos) {
+	uint32_t pair12_offset_pos = roomOffset + (12 * 8);
+	roomFile->seek(pair12_offset_pos, SEEK_SET);
+	// roomFile->skip(4);
+	uint32_t pair12_data_offset = roomFile->readUint32LE();
+	uint32_t pair12_size = roomFile->readUint32LE();
+
+	roomFile->seek(pair12_data_offset, SEEK_SET);
+	byte *data = new byte[pair12_size];
+	roomFile->read(data, pair12_size);
+	Common::Array<Description> descriptions;
+	uint32_t pos = 0;
+	uint32_t lastDescPos = 0;
+	while (pos < (pair12_size)) {
+		int desc_pos = 0;
+		if (data[pos] == 0xFF) {
+			Description description;
+			description.itemId = data[pos + 1];
+			pos += 3;
+			description.index = data[pos++];
+			description.text = "";
+			// debug("Found description terminator");
+			while (pos < (pair12_size) && data[pos] != 0xFD && pos < (pair12_size)) {
+				// debug(" char: %c", data[pos]);
+				if (data[pos] != 0x00) {
+					description.text.append(1, (char)data[pos]);
+				}
+				if (data[pos] == 0xF8) {
+					description.actionTrigger = data[pos + 1] | data[pos + 2] << 8;
+					debug("Found action trigger: %d", description.actionTrigger);
+					pos += 2;
+					break;
+				}
+				// desc[desc_pos++] = (char)data[pos];
+				// debug("Current desc: %s", desc);
+				pos++;
+			}
+			debug("Found description for item %d index %d, text: %s", description.itemId, description.index, description.text.c_str());
+
+			descriptions.push_back(description);
+			lastDescPos = pos;
+		}
+		pos++;
+	}
+	debug("End of descriptions at position %d", pos);
+	outPos = lastDescPos + 1;
+	delete[] data;
+	// for (Common::List<Common::String>::iterator i = descriptions.begin(); i != descriptions.end(); i++) {
+	// 	debug("Room description: %s", i->c_str());
+	// }
+	return descriptions;
+}
+
+
+char32_t decodeByte(byte b) {
+	if (b == 0x80) {
+		return '\xA4';
+	} else if (b == 0x81) {
+		return '\xA1';
+	} else if (b == 0x82) {
+		return '\xAD';
+	} else if (b == 0x83) {
+		return '\xA8';
+	} else if (b == 0x84) {
+		return '\xA3';
+	} else if (b == 0x7B) {
+		return '\xA0';
+	} else if (b == 0x7C) {
+		return '\x82';
+	} else if (b == 0x7D) {
+		return '\xA1';
+	} else if (b == 0x7E) {
+		return '\xA2';
+	} else if (b == 0x7F) {
+		return '\xA3';
+	} else if (b >= 0x20 && b <= 0x7A) {
+		return (char)b;
+	} else {
+		// return string in format [XX]
+		return '.';
+	}
+}
+
+void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
+
+	int headerIndex = roomNumber;
+	uint32 offset = kTalkingAnimHeaderSize * headerIndex;
+
+	TalkinAnimHeader talkHeader;
+	Common::File talkFile;
+	if (!talkFile.open("ALFRED.2")) {
+		error("Couldnt find file ALFRED.2");
+	}
+
+	talkFile.seek(offset, SEEK_SET);
+
+	talkHeader.spritePointer = talkFile.readUint32LE();
+	talkFile.read(&talkHeader.unknown2, 3);
+	talkHeader.offsetXAnimA = talkFile.readByte();
+	talkHeader.offsetYAnimA = talkFile.readByte();
+	talkHeader.wAnimA = talkFile.readByte();
+	talkHeader.hAnimA = talkFile.readByte();
+	talkFile.read(&talkHeader.unknown3, 2);
+	talkHeader.numFramesAnimA = talkFile.readByte();
+	talkFile.read(&talkHeader.unknown4, 5);
+
+	talkHeader.offsetXAnimB = talkFile.readByte();
+	talkHeader.offsetYAnimB = talkFile.readByte();
+	talkHeader.wAnimB = talkFile.readByte();
+	talkHeader.hAnimB = talkFile.readByte();
+	talkFile.read(&talkHeader.unknown5, 2);
+	talkHeader.numFramesAnimB = talkFile.readByte();
+	talkFile.read(&talkHeader.unknown6, 29);
+	debug("Talking anim header for room %d: spritePointer=%d, wA=%d, hA=%d, framesA=%d, wB=%d, hB=%d, framesB=%d", roomNumber, talkHeader.spritePointer, talkHeader.wAnimA, talkHeader.hAnimA, talkHeader.numFramesAnimA, talkHeader.wAnimB, talkHeader.hAnimB, talkHeader.numFramesAnimB);
+
+	if (talkHeader.spritePointer == 0) {
+		debug("No talking animation for room %d", roomNumber);
+		talkFile.close();
+		return;
+	}
+
+	// if(talkHeader.animA != nullptr) {
+	// 	delete[] talkHeader.animA;
+	// 	talkHeader.animA = nullptr;
+	// }
+	talkHeader.animA = new byte *[talkHeader.numFramesAnimA];
+
+	byte *data = nullptr;
+	int animASize = talkHeader.wAnimA * talkHeader.hAnimA * talkHeader.numFramesAnimA;
+	byte *decompressed = nullptr;
+	size_t dataSize = 0;
+	readUntilBuda(&talkFile, talkHeader.spritePointer, data, dataSize);
+	size_t decompressedSize = rleDecompress(data, dataSize, 0, dataSize, &decompressed);
+	free(data);
+	debug("Decompressed talking anim A size: %zu, decompressed size: %zu", dataSize, decompressedSize);
+	for (int i = 0; i < talkHeader.numFramesAnimA; i++) {
+		talkHeader.animA[i] = new byte[talkHeader.wAnimA * talkHeader.hAnimA];
+		Common::copy(decompressed + (i * talkHeader.wAnimA * talkHeader.hAnimA), decompressed + ((i + 1) * talkHeader.wAnimA * talkHeader.hAnimA), talkHeader.animA[i]);
+	}
+
+	if (talkHeader.numFramesAnimB > 0) {
+		// if(talkHeader.animA != nullptr) {
+		// 	delete[] talkHeader.animA;
+		// 	talkHeader.animA = nullptr;
+		// }
+		talkHeader.animB = new byte *[talkHeader.numFramesAnimB];
+		for (int i = 0; i < talkHeader.numFramesAnimB; i++) {
+			talkHeader.animB[i] = new byte[talkHeader.wAnimB * talkHeader.hAnimB];
+			Common::copy(decompressed + animASize + (i * talkHeader.wAnimB * talkHeader.hAnimB), decompressed + animASize + ((i + 1) * talkHeader.wAnimB * talkHeader.hAnimB), talkHeader.animB[i]);
+		}
+	}
+	free(decompressed);
+	_talkingAnimHeader = talkHeader;
+
+	talkFile.close();
+}
+
+Common::String RoomManager::getControlName(byte b) {
+	switch (b) {
+	case 0xFD:
+		return "END_LINE";
+	case 0xFC:
+		return "TEXT_TERM";
+	case 0xFB:
+		return "CHOICE";
+	case 0xFA:
+		return "SKIP";
+	case 0xF9:
+		return "PAGE_BREAK";
+	case 0xF8:
+		return "ACTION";
+	case 0xF7:
+		return "END_BRANCH";
+	case 0xF6:
+		return "LINE_CONT";
+	case 0xF5:
+		return "END_BRANCH_2";
+	case 0xF4:
+		return "END_CONV";
+	case 0xF1:
+		return "CHOICE_ALT";
+	case 0xF0:
+		return "GO_BACK";
+	case 0xFE:
+		return "END_BRANCH_3";
+	case 0xEB:
+		return "END_ALT";
+	case 0xFF:
+		return "DESC_START";
+	case 0x08:
+		return "SPEAKER";
+	default:
+		return Common::String::format("UNKNOWN(0x%02X)", b);
+	}
+}
+
+Common::String RoomManager::cleanText(const Common::String &text) {
+	Common::String cleaned = text;
+
+	// Trim leading/trailing whitespace
+	while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
+		cleaned.deleteChar(0);
+	}
+	while (!cleaned.empty() && Common::isSpace(cleaned.lastChar())) {
+		cleaned.deleteLastChar();
+	}
+
+	// Remove leading [XX][00] patterns
+	while (!cleaned.empty() && cleaned.contains('[')) {
+		uint idx = 0;
+		for (uint i = 0; i < cleaned.size() && i < 15; i++) {
+			if (cleaned[i] == '[') {
+				idx = i;
+				break;
+			}
+		}
+
+		if (idx < 10) {
+			int endIdx = -1;
+			for (uint i = idx; i < cleaned.size() && i < idx + 10; i++) {
+				if (cleaned[i] == ']') {
+					endIdx = i;
+					break;
+				}
+			}
+
+			if (endIdx > (int)idx && endIdx < (int)idx + 10) {
+				cleaned = cleaned.c_str() + endIdx + 1;
+				// Trim leading whitespace again
+				while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
+					cleaned.deleteChar(0);
+				}
+			} else {
+				break;
+			}
+		} else {
+			break;
+		}
+	}
+
+	// Remove single leading control characters
+	if (cleaned.size() > 1) {
+		byte first = (byte)cleaned[0];
+		byte second = (byte)cleaned[1];
+
+		if ((first == 'A' || first == 'H') &&
+			(Common::isUpper(second) || second == 0x83 || second == 0x82 || second == '[')) {
+			cleaned.deleteChar(0);
+			while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
+				cleaned.deleteChar(0);
+			}
+		} else if (strchr("#%')!+,.-\"*&$(/", first)) {
+			cleaned.deleteChar(0);
+			while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
+				cleaned.deleteChar(0);
+			}
+		}
+	}
+
+	return cleaned;
+}
+
+Common::Array<ConversationElement> RoomManager::parseConversationElements(const byte *convData, uint32 size) {
+	Common::Array<ConversationElement> elements;
+	Common::HashMap<int, int> choiceIndices; // Track choice index occurrences
+	uint32 pos = 0;
+
+	// First pass: parse elements and track choice indices
+	while (pos < size) {
+		byte b = convData[pos];
+
+		if (b == 0x08) { // SPEAKER
+			pos++;
+			if (pos < size) {
+				byte speakerId = convData[pos];
+				Common::String speaker = (speakerId == 0x0D) ? "ALFRED" : "NPC";
+				pos++;
+
+				// Read text
+				Common::String text;
+				while (pos < size && convData[pos] != 0x08 && convData[pos] != 0xFB &&
+					   convData[pos] != 0xF1 && convData[pos] != 0xF8 && convData[pos] != 0xFD &&
+					   convData[pos] != 0xFC && convData[pos] != 0xF4 && convData[pos] != 0xF7 &&
+					   convData[pos] != 0xF5 && convData[pos] != 0xFE && convData[pos] != 0xEB &&
+					   convData[pos] != 0xF0) {
+					char32_t ch = decodeByte(convData[pos]);
+					if (ch != '.') {
+						text += ch;
+					}
+					pos++;
+				}
+
+				text = cleanText(text);
+				if (!text.empty()) {
+					ConversationElement elem;
+					elem.type = ConversationElement::DIALOGUE;
+					elem.speakerId = speakerId;
+					elem.speaker = speaker;
+					elem.text = text;
+					elem.choiceIndex = -1;
+					elements.push_back(elem);
+				}
+			}
+		} else if (b == 0xFB || b == 0xF1) { // CHOICE marker
+			pos++;
+			int choiceIndex = -1;
+			if (pos < size) {
+				choiceIndex = convData[pos];
+				// Track this choice index
+				if (choiceIndices.contains(choiceIndex)) {
+					choiceIndices[choiceIndex]++;
+				} else {
+					choiceIndices[choiceIndex] = 1;
+				}
+				pos++;
+			}
+
+			// Skip next 2 bytes (speaker marker)
+			if (pos < size)
+				pos++;
+			if (pos < size)
+				pos++;
+
+			// Read text
+			Common::String text;
+			while (pos < size && convData[pos] != 0x08 && convData[pos] != 0xFB &&
+				   convData[pos] != 0xF1 && convData[pos] != 0xF8 && convData[pos] != 0xFD &&
+				   convData[pos] != 0xFC && convData[pos] != 0xF4 && convData[pos] != 0xF7 &&
+				   convData[pos] != 0xF5 && convData[pos] != 0xFE && convData[pos] != 0xEB &&
+				   convData[pos] != 0xF0) {
+				char32_t ch = decodeByte(convData[pos]);
+				if (ch != '.') {
+					text += ch;
+				}
+				pos++;
+			}
+
+			text = cleanText(text);
+			if (!text.empty()) {
+				ConversationElement elem;
+				elem.type = ConversationElement::CHOICE_MARKER;
+				elem.text = text;
+				elem.choiceIndex = choiceIndex;
+				elements.push_back(elem);
+			}
+		} else if (b == 0xF8) { // ACTION
+			pos += 3;
+		} else if (b == 0xF4) { // END_CONV
+			ConversationElement elem;
+			elem.type = ConversationElement::END_CONV;
+			elements.push_back(elem);
+			pos++;
+		} else if (b == 0xF7) { // END_BRANCH
+			ConversationElement elem;
+			elem.type = ConversationElement::END_BRANCH;
+			elements.push_back(elem);
+			pos++;
+		} else if (b == 0xFD || b == 0xFC || b == 0xF5 || b == 0xFE || b == 0xEB || b == 0xF0) {
+			pos++;
+		} else {
+			pos++;
+		}
+	}
+
+	// Second pass: mark which indices are actual choices (appear multiple times)
+	for (uint i = 0; i < elements.size(); i++) {
+		if (elements[i].choiceIndex >= 0) {
+			elements[i].isRealChoice = (choiceIndices[elements[i].choiceIndex] > 1);
+		}
+	}
+
+	return elements;
+}
+
+Common::Array<ConversationNode> RoomManager::buildTreeStructure(const Common::Array<ConversationElement> &elements) {
+	Common::Array<ConversationNode> roots;
+	Common::Array<StackEntry> stack;
+	ConversationNode *currentRoot = nullptr;
+	uint i = 0;
+
+	while (i < elements.size()) {
+		const ConversationElement &elem = elements[i];
+
+		if (elem.type == ConversationElement::DIALOGUE && elem.speaker == "NPC") {
+			if (stack.empty()) {
+				// New root conversation
+				ConversationNode root;
+				root.type = ConversationNode::ROOT;
+				root.text = elem.text;
+				root.speaker = "NPC";
+				root.speakerId = elem.speakerId;
+				roots.push_back(root);
+				currentRoot = &roots[roots.size() - 1];
+			} else {
+				// NPC response within a branch
+				ConversationNode *parent = stack[stack.size() - 1].node;
+				ConversationNode response;
+				response.type = ConversationNode::RESPONSE;
+				response.speaker = "NPC";
+				response.speakerId = elem.speakerId;
+				response.text = elem.text;
+				parent->responses.push_back(response);
+			}
+			i++;
+
+		} else if (elem.type == ConversationElement::CHOICE_MARKER) {
+			if (elem.isRealChoice) {
+				// Real choice - player selects from menu
+				ConversationNode choiceNode;
+				choiceNode.type = ConversationNode::CHOICE;
+				choiceNode.text = elem.text;
+				choiceNode.speaker = "ALFRED";
+				choiceNode.speakerId = 0x0D; // Player
+				choiceNode.choiceIndex = elem.choiceIndex;
+
+				// Find where to attach this choice
+				while (!stack.empty() && stack[stack.size() - 1].index >= elem.choiceIndex) {
+					stack.pop_back();
+				}
+
+				if (!stack.empty()) {
+					ConversationNode *parent = stack[stack.size() - 1].node;
+					parent->subchoices.push_back(choiceNode);
+
+					// Get pointer to the newly added choice
+					ConversationNode *newChoice = &parent->subchoices[parent->subchoices.size() - 1];
+
+					StackEntry entry;
+					entry.node = newChoice;
+					entry.index = elem.choiceIndex;
+					stack.push_back(entry);
+				} else {
+					if (currentRoot) {
+						currentRoot->choices.push_back(choiceNode);
+
+						// Get pointer to the newly added choice
+						ConversationNode *newChoice = &currentRoot->choices[currentRoot->choices.size() - 1];
+
+						StackEntry entry;
+						entry.node = newChoice;
+						entry.index = elem.choiceIndex;
+						stack.push_back(entry);
+					}
+				}
+			} else {
+				// Auto-dialogue - ALFRED just speaks
+				if (!stack.empty()) {
+					ConversationNode *parent = stack[stack.size() - 1].node;
+					ConversationNode response;
+					response.type = ConversationNode::RESPONSE;
+					response.speaker = "ALFRED";
+					response.speakerId = 0x0D;
+					response.text = elem.text;
+					parent->responses.push_back(response);
+				}
+			}
+			i++;
+
+		} else if (elem.type == ConversationElement::DIALOGUE && elem.speaker == "ALFRED") {
+			if (!stack.empty()) {
+				ConversationNode *parent = stack[stack.size() - 1].node;
+				ConversationNode response;
+				response.type = ConversationNode::RESPONSE;
+				response.speaker = "ALFRED";
+				response.text = elem.text;
+				response.speakerId = 0x0D;
+				parent->responses.push_back(response);
+			}
+			i++;
+
+		} else if (elem.type == ConversationElement::END_CONV) {
+			if (!stack.empty()) {
+				stack[stack.size() - 1].node->terminated = true;
+				stack.pop_back();
+			}
+			i++;
+
+		} else if (elem.type == ConversationElement::END_BRANCH) {
+			stack.clear();
+			currentRoot = nullptr;
+			i++;
+
+		} else {
+			i++;
+		}
+	}
+
+	return roots;
+}
+
+Common::Array<ConversationNode> RoomManager::loadConversations(Common::File *roomFile, int roomOffset, uint32_t startPos) {
+
+	debug("Loading conversations starting at position %d", startPos);
+
+	uint32_t pair12_offset_pos = roomOffset + (12 * 8);
+	roomFile->seek(pair12_offset_pos, SEEK_SET);
+	uint32_t pair12_data_offset = roomFile->readUint32LE();
+	uint32_t pair12_size = roomFile->readUint32LE();
+
+	// startPos += 2;
+	uint32_t conversation_start = pair12_data_offset + startPos;
+	uint32_t conversation_size = pair12_size - startPos;
+
+	roomFile->seek(conversation_start, SEEK_SET);
+	byte *data = new byte[conversation_size];
+	roomFile->read(data, conversation_size);
+
+	Common::Array<ConversationElement> elements = parseConversationElements(data, conversation_size);
+	Common::Array<ConversationNode> roots = buildTreeStructure(elements);
+	return roots;
+}
+
+
+} // End of namespace Pelrock
diff --git a/engines/pelrock/resources.h b/engines/pelrock/room.h
similarity index 63%
rename from engines/pelrock/resources.h
rename to engines/pelrock/room.h
index 45fcd7b3c31..311c7bc66b0 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/room.h
@@ -29,12 +29,22 @@
 
 namespace Pelrock {
 
-class ResourceManager {
+class RoomManager {
 public:
-	ResourceManager();
-	~ResourceManager();
+	RoomManager();
+	~RoomManager();
 	void loadRoomMetadata(Common::File *roomFile, int roomOffset);
 	void loadRoomTalkingAnimations(int roomNumber);
+	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
+	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
+
+	Common::Array<HotSpot> _currentRoomHotspots;
+	Common::Array<AnimSet> _currentRoomAnims;
+	Common::Array<Exit> _currentRoomExits;
+	Common::Array<WalkBox> _currentRoomWalkboxes;
+	Common::Array<Description> _currentRoomDescriptions;
+	Common::Array<ConversationNode> _currentRoomConversations;
+	TalkinAnimHeader _talkingAnimHeader;
 
 private:
 	Common::Array<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
@@ -42,6 +52,12 @@ private:
 	Common::Array<Exit> loadExits(Common::File *roomFile, int roomOffset);
 	Common::Array<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
 	Common::Array<Description> loadRoomDescriptions(Common::File *roomFile, int roomOffset, uint32_t &outPos);
+
+	Common::String getControlName(byte b);
+	Common::String cleanText(const Common::String &text);
+	Common::Array<ConversationElement> parseConversationElements(const byte *convData, uint32 size);
+	Common::Array<ConversationNode> buildTreeStructure(const Common::Array<ConversationElement> &elements);
+	Common::Array<ConversationNode> loadConversations(Common::File *roomFile, int roomOffset, uint32_t startPos);
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 7a1ab388d0a..5f2720d59bb 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -18,6 +18,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
+#include "common/stream.h"
 
 #include "pelrock/util.h"
 #include "pelrock/types.h"
@@ -76,4 +77,32 @@ size_t rleDecompress(const uint8_t *data, size_t data_size, uint32_t offset, uin
 	return result_size;
 }
 
+void readUntilBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *&buffer, size_t &outSize) {
+	const char marker[] = "BUDA";
+	const int markerLen = 4;
+	size_t bufferSize = 4096;
+	size_t pos = 0;
+
+	buffer = (byte *)malloc(bufferSize);
+	stream->seek(startPos, SEEK_SET);
+	while (!stream->eos()) {
+		byte b = stream->readByte();
+		if (pos + 1 > bufferSize) {
+			bufferSize *= 2;
+			buffer = (byte *)realloc(buffer, bufferSize);
+		}
+		buffer[pos++] = b;
+
+		// Check for marker at the end of buffer
+		if (pos >= markerLen &&
+			buffer[pos - 4] == 'B' &&
+			buffer[pos - 3] == 'U' &&
+			buffer[pos - 2] == 'D' &&
+			buffer[pos - 1] == 'A') {
+			break;
+		}
+	}
+	outSize = pos;
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index d5cfa3cc3cf..1f333132958 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -28,6 +28,8 @@ namespace Pelrock {
 
 const int EXPECTED_SIZE = 640 * 400;
 size_t rleDecompress(const uint8_t *data, size_t data_size, uint32_t offset, uint32_t size, uint8_t **out_data);
+void readUntilBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *&buffer, size_t &outSize);
+
 void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byte color);
 void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color);
 } // End of namespace Pelrock


Commit: 977976673989fbb2ff622442e1ce718880d316aa
    https://github.com/scummvm/scummvm/commit/977976673989fbb2ff622442e1ce718880d316aa
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:10+02:00

Commit Message:
PELROCK: Refactor to use common extractSingleFrame function

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp
    engines/pelrock/util.cpp
    engines/pelrock/util.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index b66d2836d6e..94573b6bf24 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -91,6 +91,7 @@ Common::String PelrockEngine::getGameId() const {
 }
 
 Common::Array<Common::Array<Common::String> > wordWrap(Common::String text);
+
 Common::Error PelrockEngine::run() {
 	// Initialize 320x200 paletted graphics mode
 	initGraphics(640, 400);
@@ -275,14 +276,6 @@ void PelrockEngine::loadInteractionIcons() {
 	alfred4File.close();
 }
 
-void extractSingleFrame(byte *source, byte *dest, int frameIndex, int frameWidth, int frameHeight) {
-	for (int y = 0; y < frameHeight; y++) {
-		for (int x = 0; x < frameWidth; x++) {
-			unsigned int src_pos = (frameIndex * frameHeight * frameWidth) + (y * frameWidth) + x;
-			dest[y * frameWidth + x] = source[src_pos];
-		}
-	}
-}
 
 void PelrockEngine::loadAlfredAnims() {
 	Common::File alfred3;
@@ -349,6 +342,7 @@ byte *PelrockEngine::grabBackgroundSlice(int x, int y, int w, int h) {
 	}
 	return bg;
 }
+
 void PelrockEngine::putBackgroundSlice(int x, int y, int w, int h, byte *slice) {
 	for (int i = 0; i < w; i++) {
 		for (int j = 0; j < h; j++) {
@@ -359,28 +353,6 @@ void PelrockEngine::putBackgroundSlice(int x, int y, int w, int h, byte *slice)
 	}
 }
 
-// Helper function for transparent blitting
-void drawSpriteToBuffer(byte *buffer, int bufferWidth,
-						byte *sprite, int x, int y,
-						int width, int height,
-						int transparentColor) {
-	for (int py = 0; py < height; py++) {
-		for (int px = 0; px < width; px++) {
-			int srcIdx = py * width + px;
-			byte pixel = sprite[srcIdx];
-
-			if (pixel != transparentColor) {
-				int destX = x + px;
-				int destY = y + py;
-
-				if (destX >= 0 && destX < 640 &&
-					destY >= 0 && destY < 400) {
-					buffer[destY * bufferWidth + destX] = pixel;
-				}
-			}
-		}
-	}
-}
 
 Common::Array<VerbIcons> PelrockEngine::availableActions(HotSpot *hotspot) {
 	Common::Array<VerbIcons> verbs;
@@ -442,7 +414,7 @@ void PelrockEngine::frames() {
 			int frameSize = animSet.animData[animSet.curAnimIndex].w * animSet.animData[animSet.curAnimIndex].h;
 			int curFrame = animSet.animData[animSet.curAnimIndex].curFrame;
 			byte *frame = new byte[frameSize];
-			Common::copy(animSet.animData[animSet.curAnimIndex].animData + (curFrame * animSet.animData[animSet.curAnimIndex].h * animSet.animData[animSet.curAnimIndex].w), animSet.animData[animSet.curAnimIndex].animData + (curFrame * animSet.animData[animSet.curAnimIndex].h * animSet.animData[animSet.curAnimIndex].w) + (frameSize), frame);
+			extractSingleFrame(animSet.animData[animSet.curAnimIndex].animData, frame, curFrame, animSet.animData[animSet.curAnimIndex].w, animSet.animData[animSet.curAnimIndex].h);
 
 			drawSpriteToBuffer(_compositeBuffer, 640, frame, animSet.x, animSet.y, animSet.w, animSet.h, 255);
 
@@ -565,7 +537,6 @@ void PelrockEngine::frames() {
 
 		if (!isAlfredWalking && !_currentTextPages.empty()) {
 			if (_chronoManager->_textTtl > 0) {
-				debug("Will render text, _chronoManager->_textTtl=%d", _chronoManager->_textTtl);
 				renderText(_currentTextPages[_currentTextPageIndex], _textColor, _textPos.x, _textPos.y);
 			} else if (_currentTextPageIndex < _currentTextPages.size() - 1) {
 				_currentTextPageIndex++;
@@ -721,25 +692,24 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 void PelrockEngine::talkNPC(AnimSet *animSet, int index) {
 	// Change with the right index
 
-	TalkinAnimHeader animHeader = _room->_talkingAnimHeader;
+	TalkinAnimHeader *animHeader = &_room->_talkingAnimHeader;
 
-	int x = animSet->x + (index ? animHeader.offsetXAnimB : animHeader.offsetXAnimA);
-	int y = animSet->y + (index ? animHeader.offsetYAnimB : animHeader.offsetYAnimA);
+	int x = animSet->x + (index ? animHeader->offsetXAnimB : animHeader->offsetXAnimA);
+	int y = animSet->y + (index ? animHeader->offsetYAnimB : animHeader->offsetYAnimA);
 
-	int w = index ? animHeader.wAnimB : animHeader.wAnimA;
-	int h = index ? animHeader.hAnimB : animHeader.hAnimA;
-	int numFrames = index ? animHeader.numFramesAnimB : animHeader.numFramesAnimA;
-	int curFrame = index ? animHeader.currentFrameAnimB++ : animHeader.currentFrameAnimA++;
+	int w = index ? animHeader->wAnimB : animHeader->wAnimA;
+	int h = index ? animHeader->hAnimB : animHeader->hAnimA;
+	int numFrames = index ? animHeader->numFramesAnimB : animHeader->numFramesAnimA;
+	int curFrame = index ? animHeader->currentFrameAnimB++ : animHeader->currentFrameAnimA++;
 	if (curFrame >= numFrames) {
 		if (index) {
-			animHeader.currentFrameAnimB = 0;
+			animHeader->currentFrameAnimB = 0;
 		} else {
-			animHeader.currentFrameAnimA = 0;
+			animHeader->currentFrameAnimA = 0;
 		}
 		curFrame = 0;
 	}
-	byte *frame = index ? animHeader.animB[curFrame] : animHeader.animA[curFrame];
-
+	byte *frame = index ? animHeader->animB[curFrame] : animHeader->animA[curFrame];
 	debug("Talking NPC frame %d/%d, x=%d, y=%d, w=%d, h=%d", curFrame, numFrames, x, y, w, h);
 
 	drawSpriteToBuffer(_compositeBuffer, 640, frame, x, y, w, h, 255);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index fa5a029d59a..0321fc19112 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -446,7 +446,8 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 	debug("Decompressed talking anim A size: %zu, decompressed size: %zu", dataSize, decompressedSize);
 	for (int i = 0; i < talkHeader.numFramesAnimA; i++) {
 		talkHeader.animA[i] = new byte[talkHeader.wAnimA * talkHeader.hAnimA];
-		Common::copy(decompressed + (i * talkHeader.wAnimA * talkHeader.hAnimA), decompressed + ((i + 1) * talkHeader.wAnimA * talkHeader.hAnimA), talkHeader.animA[i]);
+		extractSingleFrame(decompressed, talkHeader.animA[i], i, talkHeader.wAnimA, talkHeader.hAnimA);
+
 	}
 
 	if (talkHeader.numFramesAnimB > 0) {
@@ -457,7 +458,7 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 		talkHeader.animB = new byte *[talkHeader.numFramesAnimB];
 		for (int i = 0; i < talkHeader.numFramesAnimB; i++) {
 			talkHeader.animB[i] = new byte[talkHeader.wAnimB * talkHeader.hAnimB];
-			Common::copy(decompressed + animASize + (i * talkHeader.wAnimB * talkHeader.hAnimB), decompressed + animASize + ((i + 1) * talkHeader.wAnimB * talkHeader.hAnimB), talkHeader.animB[i]);
+			extractSingleFrame(decompressed + animASize, talkHeader.animB[i], i, talkHeader.wAnimB, talkHeader.hAnimB);
 		}
 	}
 	free(decompressed);
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 5f2720d59bb..c05d0e4a2a2 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -105,4 +105,37 @@ void readUntilBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *
 	outSize = pos;
 }
 
+// Helper function for transparent blitting
+void drawSpriteToBuffer(byte *buffer, int bufferWidth,
+						byte *sprite, int x, int y,
+						int width, int height,
+						int transparentColor) {
+	for (int py = 0; py < height; py++) {
+		for (int px = 0; px < width; px++) {
+			int srcIdx = py * width + px;
+			byte pixel = sprite[srcIdx];
+
+			if (pixel != transparentColor) {
+				int destX = x + px;
+				int destY = y + py;
+
+				if (destX >= 0 && destX < 640 &&
+					destY >= 0 && destY < 400) {
+					buffer[destY * bufferWidth + destX] = pixel;
+				}
+			}
+		}
+	}
+}
+
+
+void extractSingleFrame(byte *source, byte *dest, int frameIndex, int frameWidth, int frameHeight) {
+	for (int y = 0; y < frameHeight; y++) {
+		for (int x = 0; x < frameWidth; x++) {
+			unsigned int src_pos = (frameIndex * frameHeight * frameWidth) + (y * frameWidth) + x;
+			dest[y * frameWidth + x] = source[src_pos];
+		}
+	}
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index 1f333132958..1bda8ed6c8b 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -29,7 +29,12 @@ namespace Pelrock {
 const int EXPECTED_SIZE = 640 * 400;
 size_t rleDecompress(const uint8_t *data, size_t data_size, uint32_t offset, uint32_t size, uint8_t **out_data);
 void readUntilBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *&buffer, size_t &outSize);
-
+void drawSpriteToBuffer(byte *buffer, int bufferWidth,
+						byte *sprite, int x, int y,
+						int width, int height,
+						int transparentColor);
+void blitSurfaceToBuffer(Graphics::Surface *surface, byte *buffer, int bufferWidth, int bufferHeight, int destX, int destY);
+void extractSingleFrame(byte *source, byte *dest, int frameIndex, int frameWidth, int frameHeight);
 void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byte color);
 void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color);
 } // End of namespace Pelrock


Commit: b25ff75330d7328baeda49dfc9796c3ca8ff04d7
    https://github.com/scummvm/scummvm/commit/b25ff75330d7328baeda49dfc9796c3ca8ff04d7
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:10+02:00

Commit Message:
PELROCK: refactor animations

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 94573b6bf24..a680f6e56cb 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -276,7 +276,6 @@ void PelrockEngine::loadInteractionIcons() {
 	alfred4File.close();
 }
 
-
 void PelrockEngine::loadAlfredAnims() {
 	Common::File alfred3;
 	if (!alfred3.open(Common::Path("ALFRED.3"))) {
@@ -353,7 +352,6 @@ void PelrockEngine::putBackgroundSlice(int x, int y, int w, int h, byte *slice)
 	}
 }
 
-
 Common::Array<VerbIcons> PelrockEngine::availableActions(HotSpot *hotspot) {
 	Common::Array<VerbIcons> verbs;
 	verbs.push_back(LOOK);
@@ -399,46 +397,7 @@ void PelrockEngine::frames() {
 		for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
 			// debug("Processing animation set %d, numAnims %d", num, i->numAnims);
 			AnimSet &animSet = _room->_currentRoomAnims[i];
-			int x = animSet.animData[animSet.curAnimIndex].x;
-			int y = animSet.animData[animSet.curAnimIndex].y;
-			int w = animSet.animData[animSet.curAnimIndex].w;
-			int h = animSet.animData[animSet.curAnimIndex].h;
-			int extra = animSet.extra;
-
-			if (whichNPCTalking == extra) {
-				// debug("Skipping anim set %d because NPC is talking", i);
-				talkNPC(&animSet, i);
-				continue;
-			}
-
-			int frameSize = animSet.animData[animSet.curAnimIndex].w * animSet.animData[animSet.curAnimIndex].h;
-			int curFrame = animSet.animData[animSet.curAnimIndex].curFrame;
-			byte *frame = new byte[frameSize];
-			extractSingleFrame(animSet.animData[animSet.curAnimIndex].animData, frame, curFrame, animSet.animData[animSet.curAnimIndex].w, animSet.animData[animSet.curAnimIndex].h);
-
-			drawSpriteToBuffer(_compositeBuffer, 640, frame, animSet.x, animSet.y, animSet.w, animSet.h, 255);
-
-			if (animSet.animData[animSet.curAnimIndex].elpapsedFrames == animSet.animData[animSet.curAnimIndex].speed) {
-				animSet.animData[animSet.curAnimIndex].elpapsedFrames = 0;
-				if (animSet.animData[animSet.curAnimIndex].curFrame < animSet.animData[animSet.curAnimIndex].nframes - 1) {
-					animSet.animData[animSet.curAnimIndex].curFrame++;
-				} else {
-					if (animSet.animData[animSet.curAnimIndex].curLoop < animSet.animData[animSet.curAnimIndex].loopCount - 1) {
-						animSet.animData[animSet.curAnimIndex].curFrame = 0;
-						animSet.animData[animSet.curAnimIndex].curLoop++;
-					} else {
-						animSet.animData[animSet.curAnimIndex].curFrame = 0;
-						animSet.animData[animSet.curAnimIndex].curLoop = 0;
-						if (animSet.curAnimIndex < animSet.numAnims - 1) {
-							animSet.curAnimIndex++;
-						} else {
-							animSet.curAnimIndex = 0;
-						}
-					}
-				}
-			} else {
-				animSet.animData[animSet.curAnimIndex].elpapsedFrames++;
-			}
+			drawNextFrame(&animSet);
 		}
 
 		if (isAlfredWalking) {
@@ -609,10 +568,52 @@ void PelrockEngine::renderText(Common::Array<Common::String> lines, int color, i
 }
 
 void PelrockEngine::drawAlfred(byte *buf) {
-
 	drawSpriteToBuffer(_compositeBuffer, 640, buf, xAlfred, yAlfred - kAlfredFrameHeight, kAlfredFrameWidth, kAlfredFrameHeight, 255);
 }
 
+void PelrockEngine::drawNextFrame(AnimSet *animSet) {
+	Anim &animData = animSet->animData[animSet->curAnimIndex];
+	int x = animData.x;
+	int y = animData.y;
+	int w = animData.w;
+	int h = animData.h;
+	int extra = animSet->extra;
+
+	if (whichNPCTalking == extra) {
+		talkNPC(animSet);
+		return;
+	}
+
+	int frameSize = animData.w * animData.h;
+	int curFrame = animData.curFrame;
+	byte *frame = new byte[frameSize];
+	extractSingleFrame(animData.animData, frame, curFrame, animData.w, animData.h);
+
+	drawSpriteToBuffer(_compositeBuffer, 640, frame, animSet->x, animSet->y, animSet->w, animSet->h, 255);
+
+	if (animData.elpapsedFrames == animData.speed) {
+		animData.elpapsedFrames = 0;
+		if (animData.curFrame < animData.nframes - 1) {
+			animData.curFrame++;
+		} else {
+			if (animData.curLoop < animData.loopCount - 1) {
+				animData.curFrame = 0;
+				animData.curLoop++;
+			} else {
+				animData.curFrame = 0;
+				animData.curLoop = 0;
+				if (animSet->curAnimIndex < animSet->numAnims - 1) {
+					animSet->curAnimIndex++;
+				} else {
+					animSet->curAnimIndex = 0;
+				}
+			}
+		}
+	} else {
+		animData.elpapsedFrames++;
+	}
+}
+
 void PelrockEngine::checkLongMouseClick(int x, int y) {
 	int hotspotIndex = isHotspotUnder(mouseX, mouseY);
 	if (hotspotIndex != -1) {
@@ -689,9 +690,10 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	}
 }
 
-void PelrockEngine::talkNPC(AnimSet *animSet, int index) {
+void PelrockEngine::talkNPC(AnimSet *animSet) {
 	// Change with the right index
 
+	int index = animSet->index;
 	TalkinAnimHeader *animHeader = &_room->_talkingAnimHeader;
 
 	int x = animSet->x + (index ? animHeader->offsetXAnimB : animHeader->offsetXAnimA);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 4ef14515529..e88afc7063c 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -93,6 +93,7 @@ private:
 	void doAction(byte action, byte object);
 	void renderText(Common::Array<Common::String> lines, int color, int x, int y);
 	void drawAlfred(byte *buf);
+	void drawNextFrame(AnimSet *animSet);
 	void checkMouseHover();
 	void checkMouseClick(int x, int y);
 	void checkLongMouseClick(int x, int y);
@@ -101,7 +102,7 @@ private:
 	Exit *isExitUnder(int x, int y);
 	AnimSet *isSpriteUnder(int x, int y);
 	void showActionBalloon(int posx, int posy, int curFrame);
-	void talkNPC(AnimSet *animSet, int index);
+	void talkNPC(AnimSet *animSet);
 
 	ChronoManager *_chronoManager = nullptr;
 
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 0321fc19112..b201886d231 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -228,6 +228,7 @@ Common::Array<AnimSet> RoomManager::loadRoomAnimations(Common::File *roomFile, i
 		roomFile->seek(animOffset, SEEK_SET);
 		roomFile->read(animData, 44);
 		AnimSet animSet;
+		animSet.index = i;
 		animSet.x = animData[0] | (animData[1] << 8);
 		animSet.y = animData[2] | (animData[3] << 8);
 		animSet.w = animData[4];
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 0bb527b3469..b88e5f19e66 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -134,6 +134,7 @@ struct Exit {
 };
 
 struct AnimSet {
+	int index; // number of the animation in the rooms
 	byte type;
 	int x;        // 0
 	int y;        // 2


Commit: 89c6caf126b59fe2d5600120822d02c36895a88e
    https://github.com/scummvm/scummvm/commit/89c6caf126b59fe2d5600120822d02c36895a88e
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:11+02:00

Commit Message:
PELROCK: Refactor other resource loading

Changed paths:
  A engines/pelrock/resources.cpp
    engines/pelrock/module.mk
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/util.cpp
    engines/pelrock/util.h


diff --git a/engines/pelrock/module.mk b/engines/pelrock/module.mk
index fdaf98bc630..a30d1632dd7 100644
--- a/engines/pelrock/module.mk
+++ b/engines/pelrock/module.mk
@@ -8,7 +8,8 @@ MODULE_OBJS = \
 	room.o \
 	fonts/small_font.o \
 	fonts/large_font.o \
-	util.o
+	util.o \
+	resources.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_PELROCK), DYNAMIC_PLUGIN)
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 3444f3edbd6..e6bdc94d920 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -25,7 +25,7 @@
 
 namespace Pelrock {
 
-    uint32_t cursor_offsets[5] = {
+    static const uint32_t cursor_offsets[5] = {
         0x0FDDFD,
         0x0FDCDD,
         0x0FDF1D,
@@ -33,8 +33,8 @@ namespace Pelrock {
         0x367EF0
     };
 
-    uint32_t kBalloonFramesOffset = 2176936;
-    uint32_t kBalloonFramesSize = 24950;
+    static const uint32_t kBalloonFramesOffset = 2176936;
+    static const uint32_t kBalloonFramesSize = 24950;
 
 } // End of namespace Pelrock
 #endif
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index a680f6e56cb..a517c9f828f 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -228,107 +228,6 @@ void PelrockEngine::talk(byte object) {
 	// }
 }
 
-void PelrockEngine::loadCursors() {
-	Common::File alfred7File;
-	if (!alfred7File.open("ALFRED.7")) {
-		error("Couldnt find file ALFRED.7");
-	}
-	for (int i = 0; i < 5; i++) {
-		uint32_t cursorOffset = cursor_offsets[i];
-		alfred7File.seek(cursorOffset);
-		_cursorMasks[i] = new byte[kCursorSize];
-		alfred7File.read(_cursorMasks[i], kCursorSize);
-	}
-	alfred7File.close();
-}
-
-void PelrockEngine::loadInteractionIcons() {
-	Common::File alfred7File;
-	if (!alfred7File.open("ALFRED.7")) {
-		error("Couldnt find file ALFRED.7");
-	}
-
-	alfred7File.seek(kBalloonFramesOffset, SEEK_SET);
-
-	uint32_t totalBalloonSize = kBalloonWidth * kBalloonHeight * kBalloonFrames;
-	_popUpBalloon = new byte[totalBalloonSize];
-
-	uint32_t compressedSize = kBalloonFramesSize;
-
-	byte *raw = new byte[compressedSize];
-	alfred7File.read(raw, compressedSize);
-	rleDecompress(raw, compressedSize, 0, compressedSize, &_popUpBalloon);
-
-	delete[] raw;
-
-	alfred7File.close();
-	Common::File alfred4File;
-	if (!alfred4File.open("ALFRED.4")) {
-		error("Couldnt find file ALFRED.4");
-	}
-
-	int iconSize = kVerbIconHeight * kVerbIconWidth;
-	for (int i = 0; i < kNumVerbIcons; i++) {
-		uint32_t iconOffset = i * iconSize;
-		_verbIcons[i] = new byte[iconSize];
-		alfred4File.read(_verbIcons[i], iconSize);
-	}
-	alfred4File.close();
-}
-
-void PelrockEngine::loadAlfredAnims() {
-	Common::File alfred3;
-	if (!alfred3.open(Common::Path("ALFRED.3"))) {
-		error("Could not open ALFRED.3");
-		return;
-	}
-	int alfred3Size = alfred3.size();
-	unsigned char *bufferFile = (unsigned char *)malloc(alfred3Size);
-	alfred3.seek(0, SEEK_SET);
-	alfred3.read(bufferFile, alfred3Size);
-	alfred3.close();
-
-	int index = 0;
-	int index3 = 0;
-	uint32_t capacity = 3060 * 102;
-	unsigned char *pic = new unsigned char[capacity];
-	rleDecompress(bufferFile, alfred3Size, 0, alfred3Size, &pic);
-
-	for (int i = 0; i < 4; i++) {
-		standingAnimFrames[i] = new byte[kAlfredFrameWidth * kAlfredFrameHeight];
-		int talkingFramesOffset = walkingAnimLengths[0] + walkingAnimLengths[1] + walkingAnimLengths[2] + walkingAnimLengths[3] + 4;
-
-		int prevWalkingFrames = 0;
-		int prevTalkingFrames = 0;
-
-		for (int j = 0; j < i; j++) {
-			prevWalkingFrames += walkingAnimLengths[j] + 1;
-			prevTalkingFrames += talkingAnimLengths[j];
-		}
-
-		walkingAnimFrames[i] = new byte *[walkingAnimLengths[i]];
-
-		int standingFrame = prevWalkingFrames;
-		debug("Loading standing frame %d at index %d", i, standingFrame);
-		extractSingleFrame(pic, standingAnimFrames[i], standingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
-		for (int j = 0; j < walkingAnimLengths[i]; j++) {
-
-			walkingAnimFrames[i][j] = new byte[kAlfredFrameWidth * kAlfredFrameHeight];
-			int walkingFrame = prevWalkingFrames + 1 + j;
-			extractSingleFrame(pic, walkingAnimFrames[i][j], walkingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
-		}
-
-		talkingAnimFrames[i] = new byte *[talkingAnimLengths[i]];
-
-		int talkingStartFrame = talkingFramesOffset + prevTalkingFrames;
-		for (int j = 0; j < talkingAnimLengths[i]; j++) {
-			talkingAnimFrames[i][j] = new byte[kAlfredFrameWidth * kAlfredFrameHeight];
-			int talkingFrame = talkingStartFrame + j;
-			extractSingleFrame(pic, talkingAnimFrames[i][j], talkingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
-		}
-	}
-}
-
 byte *PelrockEngine::grabBackgroundSlice(int x, int y, int w, int h) {
 	byte *bg = new byte[w * h];
 	for (int j = 0; j < w; j++) {
@@ -660,19 +559,6 @@ Exit *PelrockEngine::isExitUnder(int x, int y) {
 	return nullptr;
 }
 
-void blitSurfaceToBuffer(Graphics::Surface *surface, byte *buffer, int bufferWidth, int bufferHeight, int destX, int destY) {
-	for (int y = 0; y < surface->h; y++) {
-		for (int x = 0; x < surface->w; x++) {
-			int px = destX + x;
-			int py = destY + y;
-			if (px >= 0 && px < bufferWidth && py >= 0 && py < bufferHeight) {
-				byte pixel = *((byte *)surface->getBasePtr(x, y));
-				buffer[py * bufferWidth + px] = pixel;
-			}
-		}
-	}
-}
-
 void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 
 	drawSpriteToBuffer(_compositeBuffer, 640, _popUpBalloon + (curFrame * kBalloonHeight * kBalloonWidth), posx, posy, kBalloonWidth, kBalloonHeight, 255);
@@ -1256,6 +1142,7 @@ void PelrockEngine::sayAlfred(Common::String text) {
 	_textPos = Common::Point(xAlfred, yAlfred - kAlfredFrameHeight - 10);
 	_chronoManager->_textTtl = totalChars * kTextCharDisplayTime;
 }
+
 bool isEndMarker(char char_byte) {
 	return char_byte == CHAR_END_MARKER_1 || char_byte == CHAR_END_MARKER_2 || char_byte == CHAR_END_MARKER_3 || char_byte == CHAR_END_MARKER_4;
 }
@@ -1291,16 +1178,6 @@ int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
 	return wordLength;
 }
 
-Common::String joinStrings(const Common::Array<Common::String> &strings, const Common::String &separator) {
-	Common::String result;
-	for (uint i = 0; i < strings.size(); i++) {
-		result += strings[i];
-		if (i < strings.size() - 1)
-			result += separator;
-	}
-	return result;
-}
-
 Common::Array<Common::Array<Common::String> > wordWrap(Common::String text) {
 
 	Common::Array<Common::Array<Common::String> > pages;
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
new file mode 100644
index 00000000000..163e307e88b
--- /dev/null
+++ b/engines/pelrock/resources.cpp
@@ -0,0 +1,127 @@
+/* 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 "pelrock/pelrock.h"
+#include "pelrock/offsets.h"
+#include "pelrock/util.h"
+
+namespace Pelrock {
+void PelrockEngine::loadCursors() {
+	Common::File alfred7File;
+	if (!alfred7File.open("ALFRED.7")) {
+		error("Couldnt find file ALFRED.7");
+	}
+	for (int i = 0; i < 5; i++) {
+		uint32_t cursorOffset = cursor_offsets[i];
+		alfred7File.seek(cursorOffset);
+		_cursorMasks[i] = new byte[kCursorSize];
+		alfred7File.read(_cursorMasks[i], kCursorSize);
+	}
+	alfred7File.close();
+}
+void PelrockEngine::loadInteractionIcons() {
+	Common::File alfred7File;
+	if (!alfred7File.open("ALFRED.7")) {
+		error("Couldnt find file ALFRED.7");
+	}
+
+	alfred7File.seek(kBalloonFramesOffset, SEEK_SET);
+
+	uint32_t totalBalloonSize = kBalloonWidth * kBalloonHeight * kBalloonFrames;
+	_popUpBalloon = new byte[totalBalloonSize];
+
+	uint32_t compressedSize = kBalloonFramesSize;
+
+	byte *raw = new byte[compressedSize];
+	alfred7File.read(raw, compressedSize);
+	rleDecompress(raw, compressedSize, 0, compressedSize, &_popUpBalloon);
+
+	delete[] raw;
+
+	alfred7File.close();
+	Common::File alfred4File;
+	if (!alfred4File.open("ALFRED.4")) {
+		error("Couldnt find file ALFRED.4");
+	}
+
+	int iconSize = kVerbIconHeight * kVerbIconWidth;
+	for (int i = 0; i < kNumVerbIcons; i++) {
+		uint32_t iconOffset = i * iconSize;
+		_verbIcons[i] = new byte[iconSize];
+		alfred4File.read(_verbIcons[i], iconSize);
+	}
+	alfred4File.close();
+}
+
+void PelrockEngine::loadAlfredAnims() {
+	Common::File alfred3;
+	if (!alfred3.open(Common::Path("ALFRED.3"))) {
+		error("Could not open ALFRED.3");
+		return;
+	}
+	int alfred3Size = alfred3.size();
+	unsigned char *bufferFile = (unsigned char *)malloc(alfred3Size);
+	alfred3.seek(0, SEEK_SET);
+	alfred3.read(bufferFile, alfred3Size);
+	alfred3.close();
+
+	int index = 0;
+	int index3 = 0;
+	uint32_t capacity = 3060 * 102;
+	unsigned char *pic = new unsigned char[capacity];
+	rleDecompress(bufferFile, alfred3Size, 0, alfred3Size, &pic);
+
+	for (int i = 0; i < 4; i++) {
+		standingAnimFrames[i] = new byte[kAlfredFrameWidth * kAlfredFrameHeight];
+		int talkingFramesOffset = walkingAnimLengths[0] + walkingAnimLengths[1] + walkingAnimLengths[2] + walkingAnimLengths[3] + 4;
+
+		int prevWalkingFrames = 0;
+		int prevTalkingFrames = 0;
+
+		for (int j = 0; j < i; j++) {
+			prevWalkingFrames += walkingAnimLengths[j] + 1;
+			prevTalkingFrames += talkingAnimLengths[j];
+		}
+
+		walkingAnimFrames[i] = new byte *[walkingAnimLengths[i]];
+
+		int standingFrame = prevWalkingFrames;
+		debug("Loading standing frame %d at index %d", i, standingFrame);
+		extractSingleFrame(pic, standingAnimFrames[i], standingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
+		for (int j = 0; j < walkingAnimLengths[i]; j++) {
+
+			walkingAnimFrames[i][j] = new byte[kAlfredFrameWidth * kAlfredFrameHeight];
+			int walkingFrame = prevWalkingFrames + 1 + j;
+			extractSingleFrame(pic, walkingAnimFrames[i][j], walkingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
+		}
+
+		talkingAnimFrames[i] = new byte *[talkingAnimLengths[i]];
+
+		int talkingStartFrame = talkingFramesOffset + prevTalkingFrames;
+		for (int j = 0; j < talkingAnimLengths[i]; j++) {
+			talkingAnimFrames[i][j] = new byte[kAlfredFrameWidth * kAlfredFrameHeight];
+			int talkingFrame = talkingStartFrame + j;
+			extractSingleFrame(pic, talkingAnimFrames[i][j], talkingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
+		}
+	}
+}
+
+} // End of namespace Pelrock
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index c05d0e4a2a2..e59e7e54468 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -128,7 +128,6 @@ void drawSpriteToBuffer(byte *buffer, int bufferWidth,
 	}
 }
 
-
 void extractSingleFrame(byte *source, byte *dest, int frameIndex, int frameWidth, int frameHeight) {
 	for (int y = 0; y < frameHeight; y++) {
 		for (int x = 0; x < frameWidth; x++) {
@@ -138,4 +137,13 @@ void extractSingleFrame(byte *source, byte *dest, int frameIndex, int frameWidth
 	}
 }
 
+Common::String joinStrings(const Common::Array<Common::String> &strings, const Common::String &separator) {
+	Common::String result;
+	for (uint i = 0; i < strings.size(); i++) {
+		result += strings[i];
+		if (i < strings.size() - 1)
+			result += separator;
+	}
+	return result;
+}
 } // End of namespace Pelrock
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index 1bda8ed6c8b..ee91d2e394e 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -21,9 +21,13 @@
 #ifndef PELROCK_UTIL_H
 #define PELROCK_UTIL_H
 
+#include "common/array.h"
+#include "common/stream.h"
 #include "common/types.h"
 #include "graphics/managed_surface.h"
 #include "graphics/surface.h"
+
+
 namespace Pelrock {
 
 const int EXPECTED_SIZE = 640 * 400;
@@ -37,5 +41,6 @@ void blitSurfaceToBuffer(Graphics::Surface *surface, byte *buffer, int bufferWid
 void extractSingleFrame(byte *source, byte *dest, int frameIndex, int frameWidth, int frameHeight);
 void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byte color);
 void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color);
+Common::String joinStrings(const Common::Array<Common::String> &strings, const Common::String &separator);
 } // End of namespace Pelrock
 #endif // PELROCK_UTIL_H


Commit: 07471b2029762b82cb3da43f6126850e4175ccf7
    https://github.com/scummvm/scummvm/commit/07471b2029762b82cb3da43f6126850e4175ccf7
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:11+02:00

Commit Message:
PELROCK: Creates ResourceManager

Changed paths:
  A engines/pelrock/resources.h
    engines/pelrock/chrono.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/chrono.cpp b/engines/pelrock/chrono.cpp
index 8201dc15427..2b2eba5903a 100644
--- a/engines/pelrock/chrono.cpp
+++ b/engines/pelrock/chrono.cpp
@@ -36,7 +36,7 @@ ChronoManager::~ChronoManager() {
 void ChronoManager::updateChrono() {
 	uint32 currentTime = g_system->getMillis();
 
-	if(_textTtl > 0 && g_engine->isAlfredTalking && !g_engine->isAlfredWalking) {
+	if(_textTtl > 0 && g_engine->alfredState == ALFRED_TALKING && g_engine->alfredState != ALFRED_WALKING) {
 		_textTtl -= (currentTime - _lastTick);
 		if(_textTtl < 0)
 			_textTtl = 0;
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index a517c9f828f..9e35172d0e2 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -50,6 +50,7 @@ PelrockEngine::PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc) :
 	g_engine = this;
 	_chronoManager = new ChronoManager();
 	_room = new RoomManager();
+	_res = new ResourceManager();
 }
 
 PelrockEngine::~PelrockEngine() {
@@ -59,27 +60,11 @@ PelrockEngine::~PelrockEngine() {
 	delete _largeFont;
 	delete _screen;
 	delete _chronoManager;
-	for (int i = 0; i < 5; i++) {
-		delete[] _cursorMasks[i];
-	}
-	for (int i = 0; i < kNumVerbIcons; i++) {
-		delete[] _verbIcons[i];
-	}
 
-	delete[] _popUpBalloon;
 	// if (_bgPopupBalloon)
 	// 	delete[] _bgPopupBalloon;
 	delete _smallFont;
-	for (int i = 0; i < 4; i++) {
-
-		// free all frame buffers
-		for (int j = 0; j < walkingAnimLengths[i]; j++) {
-			delete[] walkingAnimFrames[i][j];
-		}
 
-		// free the array of pointers
-		delete[] walkingAnimFrames[i];
-	}
 }
 
 uint32 PelrockEngine::getFeatures() const {
@@ -122,31 +107,25 @@ Common::Error PelrockEngine::run() {
 		while (g_system->getEventManager()->pollEvent(e)) {
 			if (e.type == Common::EVENT_KEYDOWN) {
 				if (e.kbd.keycode == Common::KEYCODE_w) {
-					isAlfredWalking = true;
-					isAlfredTalking = false;
+					alfredState = ALFRED_WALKING;
 				} else if (e.kbd.keycode == Common::KEYCODE_t) {
-					isAlfredWalking = false;
-					isAlfredTalking = true;
+					alfredState = ALFRED_TALKING;
 				} else if (e.kbd.keycode == Common::KEYCODE_s) {
-					isAlfredWalking = false;
-					isAlfredTalking = false;
+					alfredState = ALFRED_IDLE;
 				}
 			} else if (e.type == Common::EVENT_MOUSEMOVE) {
 				mouseX = e.mouse.x;
 				mouseY = e.mouse.y;
 				// debug(3, "Mouse moved to (%d,%d)", mouseX, mouseY);
 			} else if (e.type == Common::EVENT_LBUTTONDOWN) {
-				debug("long button down");
 				if (!_isMouseDown) {
 					_mouseClickTime = g_system->getMillis();
 					_isMouseDown = true;
 				}
 			} else if (e.type == Common::EVENT_LBUTTONUP) {
 				_isMouseDown = false;
-				// if (!_longClick) {
 				checkMouseClick(e.mouse.x, e.mouse.y);
 				_displayPopup = false;
-				// }
 				_longClick = false;
 			}
 		}
@@ -170,8 +149,8 @@ Common::Error PelrockEngine::run() {
 }
 
 void PelrockEngine::init() {
-	loadCursors();
-	loadInteractionIcons();
+	_res->loadCursors();
+	_res->loadInteractionIcons();
 
 	_compositeBuffer = new byte[640 * 400];
 	_currentBackground = new byte[640 * 400];
@@ -183,6 +162,7 @@ void PelrockEngine::init() {
 
 	changeCursor(DEFAULT);
 	CursorMan.showMouse(true);
+
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
@@ -196,7 +176,7 @@ void PelrockEngine::playIntro() {
 }
 
 void PelrockEngine::loadAnims() {
-	loadAlfredAnims();
+	_res->loadAlfredAnims();
 }
 
 void PelrockEngine::talk(byte object) {
@@ -299,7 +279,7 @@ void PelrockEngine::frames() {
 			drawNextFrame(&animSet);
 		}
 
-		if (isAlfredWalking) {
+		if (alfredState == ALFRED_WALKING) {
 
 			MovementStep step = _currentContext.movement_buffer[_current_step];
 			// debug("Alfred step: distance_x=%d, distance_y=%d", step.distance_x, step.distance_y);
@@ -338,7 +318,7 @@ void PelrockEngine::frames() {
 				if (_current_step >= _currentContext.movement_count) {
 					// debug("Alfred reached his walk target.");
 					_current_step = 0;
-					isAlfredWalking = false;
+					alfredState = ALFRED_IDLE;
 				}
 			} else {
 				_currentContext.movement_buffer[_current_step] = step;
@@ -354,21 +334,21 @@ void PelrockEngine::frames() {
 
 			// debug("Drawing walking frame %d for direction %d", curAlfredFrame, dirAlfred);
 
-			if (curAlfredFrame >= walkingAnimLengths[dirAlfred]) {
+			if (curAlfredFrame >= _res->walkingAnimLengths[dirAlfred]) {
 				curAlfredFrame = 0;
 			}
 
-			drawAlfred(walkingAnimFrames[dirAlfred][curAlfredFrame]);
+			drawAlfred(_res->walkingAnimFrames[dirAlfred][curAlfredFrame]);
 			curAlfredFrame++;
 
-		} else if (isAlfredTalking) {
-			if (curAlfredFrame >= talkingAnimLengths[dirAlfred] - 1) {
+		} else if (alfredState == ALFRED_TALKING) {
+			if (curAlfredFrame >= _res->talkingAnimLengths[dirAlfred] - 1) {
 				curAlfredFrame = 0;
 			}
-			drawAlfred(talkingAnimFrames[dirAlfred][curAlfredFrame]);
+			drawAlfred(_res->talkingAnimFrames[dirAlfred][curAlfredFrame]);
 			curAlfredFrame++;
 		} else {
-			drawAlfred(standingAnimFrames[dirAlfred]);
+			drawAlfred(_res->standingAnimFrames[dirAlfred]);
 		}
 		if (_displayPopup) {
 
@@ -393,7 +373,7 @@ void PelrockEngine::frames() {
 
 		memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
 
-		if (!isAlfredWalking && !_currentTextPages.empty()) {
+		if (alfredState != ALFRED_WALKING && !_currentTextPages.empty()) {
 			if (_chronoManager->_textTtl > 0) {
 				renderText(_currentTextPages[_currentTextPageIndex], _textColor, _textPos.x, _textPos.y);
 			} else if (_currentTextPageIndex < _currentTextPages.size() - 1) {
@@ -407,7 +387,7 @@ void PelrockEngine::frames() {
 			} else {
 				_currentTextPages.clear();
 				_currentTextPageIndex = 0;
-				isAlfredTalking = false;
+				alfredState = ALFRED_IDLE;
 				isNPCATalking = false;
 				isNPCBTalking = false;
 			}
@@ -479,7 +459,7 @@ void PelrockEngine::drawNextFrame(AnimSet *animSet) {
 	int extra = animSet->extra;
 
 	if (whichNPCTalking == extra) {
-		talkNPC(animSet);
+		drawTalkNPC(animSet);
 		return;
 	}
 
@@ -561,10 +541,10 @@ Exit *PelrockEngine::isExitUnder(int x, int y) {
 
 void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 
-	drawSpriteToBuffer(_compositeBuffer, 640, _popUpBalloon + (curFrame * kBalloonHeight * kBalloonWidth), posx, posy, kBalloonWidth, kBalloonHeight, 255);
+	drawSpriteToBuffer(_compositeBuffer, 640, _res->_popUpBalloon + (curFrame * kBalloonHeight * kBalloonWidth), posx, posy, kBalloonWidth, kBalloonHeight, 255);
 	Common::Array<VerbIcons> actions = availableActions(_currentHotspot);
 
-	drawSpriteToBuffer(_compositeBuffer, 640, _verbIcons[LOOK], posx + 20, posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
+	drawSpriteToBuffer(_compositeBuffer, 640, _res->_verbIcons[LOOK], posx + 20, posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
 	// Graphics::Surface rects;
 	// rects.create(kVerbIconWidth, kVerbIconHeight, Graphics::PixelFormat::createFormatCLUT8());
 	// drawRect(&rects, 0, 0, kVerbIconWidth, kVerbIconHeight, 255);
@@ -572,15 +552,15 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	// blitSurfaceToBuffer(&rects, _compositeBuffer, 640, 480, posx + ver, posy + 20);
 
 	for (int i = 0; i < actions.size(); i++) {
-		drawSpriteToBuffer(_compositeBuffer, 640, _verbIcons[actions[i]], posx + 20 + (i * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
+		drawSpriteToBuffer(_compositeBuffer, 640, _res->_verbIcons[actions[i]], posx + 20 + (i * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
 	}
 }
 
-void PelrockEngine::talkNPC(AnimSet *animSet) {
+void PelrockEngine::drawTalkNPC(AnimSet *animSet) {
 	// Change with the right index
 
 	int index = animSet->index;
-	TalkinAnimHeader *animHeader = &_room->_talkingAnimHeader;
+	TalkingAnimHeader *animHeader = &_room->_talkingAnimHeader;
 
 	int x = animSet->x + (index ? animHeader->offsetXAnimB : animHeader->offsetXAnimA);
 	int y = animSet->y + (index ? animHeader->offsetYAnimB : animHeader->offsetYAnimA);
@@ -604,7 +584,7 @@ void PelrockEngine::talkNPC(AnimSet *animSet) {
 }
 
 void PelrockEngine::walkTo(int x, int y) {
-	isAlfredWalking = true;
+	alfredState = ALFRED_WALKING;
 	curAlfredFrame = 0;
 
 	PathContext context = {NULL, NULL, NULL, 0, 0, 0};
@@ -999,7 +979,7 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 }
 
 void PelrockEngine::changeCursor(Cursor cursor) {
-	CursorMan.replaceCursor(_cursorMasks[cursor], kCursorWidth, kCursorHeight, 0, 0, 255);
+	CursorMan.replaceCursor(_res->_cursorMasks[cursor], kCursorWidth, kCursorHeight, 0, 0, 255);
 }
 
 void PelrockEngine::checkMouseHover() {
@@ -1130,7 +1110,7 @@ void PelrockEngine::sayNPC(AnimSet *anim, Common::String text, byte color) {
 }
 
 void PelrockEngine::sayAlfred(Common::String text) {
-	isAlfredTalking = true;
+	alfredState = ALFRED_TALKING;
 	curAlfredFrame = 0;
 	debug("Alfred says: %s", text.c_str());
 	_currentTextPages = wordWrap(text);
@@ -1265,8 +1245,7 @@ void PelrockEngine::setScreen(int number, int dir) {
 		return;
 	}
 	dirAlfred = dir;
-	isAlfredWalking = false;
-	isAlfredTalking = false;
+	alfredState = ALFRED_IDLE;
 	_current_step = 0;
 	int roomOffset = number * kRoomStructSize;
 	curAlfredFrame = 0;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index e88afc7063c..c27d002104e 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -40,6 +40,7 @@
 #include "pelrock/detection.h"
 #include "pelrock/fonts/large_font.h"
 #include "pelrock/fonts/small_font.h"
+#include "pelrock/resources.h"
 #include "pelrock/room.h"
 #include "pelrock/types.h"
 
@@ -54,6 +55,9 @@ class PelrockEngine : public Engine {
 private:
 	const ADGameDescription *_gameDescription;
 	Common::RandomSource _randomSource;
+	ChronoManager *_chronoManager = nullptr;
+	RoomManager *_room = nullptr;
+	ResourceManager *_res = nullptr;
 
 	void init();
 	void playIntro();
@@ -61,8 +65,6 @@ private:
 	void setScreenJava(int s, int dir);
 	void loadAnims();
 
-	void loadAlfredAnims();
-
 	void walkTo(int x, int y);
 	bool pathFind(int x, int y, PathContext *context);
 	uint8_t find_walkbox_for_point(uint16_t x, uint16_t y);
@@ -77,8 +79,6 @@ private:
 									 MovementStep *movement_buffer);
 
 	void talk(byte object);
-	void loadCursors();
-	void loadInteractionIcons();
 	byte *grabBackgroundSlice(int x, int y, int w, int h);
 	void putBackgroundSlice(int x, int y, int w, int h, byte *slice);
 
@@ -88,64 +88,57 @@ private:
 
 	void sayAlfred(Common::String text);
 	void sayNPC(AnimSet *anim, Common::String text, byte color);
-	// render loop
+
 	void frames();
 	void doAction(byte action, byte object);
 	void renderText(Common::Array<Common::String> lines, int color, int x, int y);
 	void drawAlfred(byte *buf);
 	void drawNextFrame(AnimSet *animSet);
-	void checkMouseHover();
-	void checkMouseClick(int x, int y);
-	void checkLongMouseClick(int x, int y);
 	void changeCursor(Cursor cursor);
 	int isHotspotUnder(int x, int y);
 	Exit *isExitUnder(int x, int y);
 	AnimSet *isSpriteUnder(int x, int y);
 	void showActionBalloon(int posx, int posy, int curFrame);
-	void talkNPC(AnimSet *animSet);
-
-	ChronoManager *_chronoManager = nullptr;
+	void drawTalkNPC(AnimSet *animSet);
 
-	byte **walkingAnimFrames[4];              // 4 arrays of arrays
-	byte *standingAnimFrames[4] = {nullptr};  // 4 directions
-	int walkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
-	byte **talkingAnimFrames[4];              // 4 arrays of arrays
-	int talkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
+	void checkMouseHover();
+	void checkMouseClick(int x, int y);
+	void checkLongMouseClick(int x, int y);
 
 
-	PathContext _currentContext;
+	//walking
 	int _current_step = 0;
+	PathContext _currentContext;
 
+	//text display
 	byte _textColor = 0;
 	Common::Point _textPos;
 	Common::Array<Common::Array<Common::String> > _currentTextPages = Common::Array<Common::Array<Common::String> >();
 	int _currentTextPageIndex = 0;
 
-	int *_currentAnimFrames = nullptr;
-	// From the original code
+
+	// Alfred
 	int xAlfred = 319;
 	int yAlfred = 302;
 	int dirAlfred = 0;
 	int curAlfredFrame = 0;
+
 	uint16 mouseX = 0;
 	uint16 mouseY = 0;
-	byte *_cursorMasks[5] = {nullptr};
-
+	bool _lMouseDown = false;
 	uint32 _mouseClickTime;
 	bool _isMouseDown = false;
 	bool _longClick = false;
 
-	byte *_verbIcons[9] = {nullptr};
-	byte *_popUpBalloon = nullptr;
 
-	byte *_currentBackground; // Clean background - NEVER modified
+	byte *_currentBackground = nullptr; // Clean background - NEVER modified
 	byte *_compositeBuffer;   // Working composition buffer
 
-	bool _lMouseDown = false;
 	bool _displayPopup = false;
 	int _popupX = 0;
 	int _popupY = 0;
 	int _currentPopupFrame = 0;
+
 	HotSpot *_currentHotspot = nullptr;
 
 	SmallFont *_smallFont = nullptr;
@@ -168,7 +161,6 @@ private:
 	// int whichScreen = 0;
 	// byte *pixelsShadows; // =new int[640*400];
 
-	RoomManager *_room = nullptr;
 
 protected:
 	// Engine APIs
@@ -176,9 +168,7 @@ protected:
 
 public:
 	Graphics::Screen *_screen = nullptr;
-	bool isAlfredWalking = false;
-	bool isAlfredTalking = false;
-
+	AlfredState alfredState = ALFRED_IDLE;
 public:
 	PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc);
 	~PelrockEngine() override;
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 163e307e88b..1cb5ebdf8b2 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -19,12 +19,37 @@
  *
  */
 
-#include "pelrock/pelrock.h"
+#include "pelrock/resources.h"
 #include "pelrock/offsets.h"
+#include "pelrock/pelrock.h"
 #include "pelrock/util.h"
 
 namespace Pelrock {
-void PelrockEngine::loadCursors() {
+
+ResourceManager::ResourceManager(/* args */) {
+}
+
+ResourceManager::~ResourceManager() {
+	for (int i = 0; i < 5; i++) {
+		delete[] _cursorMasks[i];
+	}
+	for (int i = 0; i < kNumVerbIcons; i++) {
+		delete[] _verbIcons[i];
+	}
+	delete[] _popUpBalloon;
+	for (int i = 0; i < 4; i++) {
+
+		// free all frame buffers
+		for (int j = 0; j < walkingAnimLengths[i]; j++) {
+			delete[] walkingAnimFrames[i][j];
+		}
+
+		// free the array of pointers
+		delete[] walkingAnimFrames[i];
+	}
+}
+
+void ResourceManager::loadCursors() {
 	Common::File alfred7File;
 	if (!alfred7File.open("ALFRED.7")) {
 		error("Couldnt find file ALFRED.7");
@@ -37,7 +62,7 @@ void PelrockEngine::loadCursors() {
 	}
 	alfred7File.close();
 }
-void PelrockEngine::loadInteractionIcons() {
+void ResourceManager::loadInteractionIcons() {
 	Common::File alfred7File;
 	if (!alfred7File.open("ALFRED.7")) {
 		error("Couldnt find file ALFRED.7");
@@ -71,7 +96,7 @@ void PelrockEngine::loadInteractionIcons() {
 	alfred4File.close();
 }
 
-void PelrockEngine::loadAlfredAnims() {
+void ResourceManager::loadAlfredAnims() {
 	Common::File alfred3;
 	if (!alfred3.open(Common::Path("ALFRED.3"))) {
 		error("Could not open ALFRED.3");
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
new file mode 100644
index 00000000000..a1e240055da
--- /dev/null
+++ b/engines/pelrock/resources.h
@@ -0,0 +1,51 @@
+/* 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 PELROCK_RESOURCES_H
+#define PELROCK_RESOURCES_H
+
+ #include "common/scummsys.h"
+
+namespace Pelrock {
+
+class ResourceManager {
+private:
+	/* data */
+public:
+	ResourceManager(/* args */);
+	~ResourceManager();
+
+	void loadCursors();
+	void loadInteractionIcons();
+	void loadAlfredAnims();
+
+	byte **walkingAnimFrames[4];              // 4 arrays of arrays
+	byte *standingAnimFrames[4] = {nullptr};  // 4 directions
+	int walkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
+	byte **talkingAnimFrames[4];              // 4 arrays of arrays
+	int talkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
+    byte *_cursorMasks[5] = {nullptr};
+	byte *_verbIcons[9] = {nullptr};
+	byte *_popUpBalloon = nullptr;
+};
+
+
+} // End of namespace Pelrock
+#endif
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index b201886d231..3b5a3c84deb 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -398,7 +398,7 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 	int headerIndex = roomNumber;
 	uint32 offset = kTalkingAnimHeaderSize * headerIndex;
 
-	TalkinAnimHeader talkHeader;
+	TalkingAnimHeader talkHeader;
 	Common::File talkFile;
 	if (!talkFile.open("ALFRED.2")) {
 		error("Couldnt find file ALFRED.2");
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 311c7bc66b0..10553071ea3 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -18,8 +18,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
-#ifndef PELROCK_RESOURCES_H
-#define PELROCK_RESOURCES_H
+#ifndef PELROCK_ROOM_H
+#define PELROCK_ROOM_H
 
 #include "common/array.h"
 #include "common/file.h"
@@ -44,7 +44,7 @@ public:
 	Common::Array<WalkBox> _currentRoomWalkboxes;
 	Common::Array<Description> _currentRoomDescriptions;
 	Common::Array<ConversationNode> _currentRoomConversations;
-	TalkinAnimHeader _talkingAnimHeader;
+	TalkingAnimHeader _talkingAnimHeader;
 
 private:
 	Common::Array<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index b88e5f19e66..8c02104b280 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -63,6 +63,15 @@ const int kBalloonFrames = 4;
 const int kTextCharDisplayTime = 100; // 10ms per character
 const int kVerbIconPadding = 20;
 
+
+enum AlfredState {
+	ALFRED_IDLE,
+	ALFRED_WALKING,
+	ALFRED_TALKING,
+	ALFRED_INTERACTING,
+};
+
+
 // Direction flags (bit-packed)
 #define MOVE_RIGHT 0x01 // Move right (positive X)
 #define MOVE_LEFT 0x02  // Move left (negative X)
@@ -161,7 +170,7 @@ struct HotSpot {
 	bool isEnabled = true;
 };
 
-struct TalkinAnimHeader {
+struct TalkingAnimHeader {
 	uint32 spritePointer;
 
 	byte unknown2[3];


Commit: 321e4012568784061893b047b03dca2c3c92bfc3
    https://github.com/scummvm/scummvm/commit/321e4012568784061893b047b03dca2c3c92bfc3
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:11+02:00

Commit Message:
PELROCK: Comb animation

Changed paths:
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/types.h
    engines/pelrock/util.cpp


diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index e6bdc94d920..577613b2d31 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -36,6 +36,11 @@ namespace Pelrock {
     static const uint32_t kBalloonFramesOffset = 2176936;
     static const uint32_t kBalloonFramesSize = 24950;
 
+    static const uint32_t ALFRED7_ALFRED_COMB_R = 67764;
+    static const uint32_t ALFRED7_ALFRED_COMB_L = 88404;
+
+
+
 } // End of namespace Pelrock
 #endif
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 9e35172d0e2..58abc31d2c4 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -112,6 +112,8 @@ Common::Error PelrockEngine::run() {
 					alfredState = ALFRED_TALKING;
 				} else if (e.kbd.keycode == Common::KEYCODE_s) {
 					alfredState = ALFRED_IDLE;
+				} else if (e.kbd.keycode == Common::KEYCODE_c) {
+					alfredState = ALFRED_COMB;
 				}
 			} else if (e.type == Common::EVENT_MOUSEMOVE) {
 				mouseX = e.mouse.x;
@@ -338,17 +340,24 @@ void PelrockEngine::frames() {
 				curAlfredFrame = 0;
 			}
 
-			drawAlfred(_res->walkingAnimFrames[dirAlfred][curAlfredFrame]);
+			drawAlfred(_res->alfredWalkFrames[dirAlfred][curAlfredFrame]);
 			curAlfredFrame++;
 
 		} else if (alfredState == ALFRED_TALKING) {
 			if (curAlfredFrame >= _res->talkingAnimLengths[dirAlfred] - 1) {
 				curAlfredFrame = 0;
 			}
-			drawAlfred(_res->talkingAnimFrames[dirAlfred][curAlfredFrame]);
+			drawAlfred(_res->alfredTalkFrames[dirAlfred][curAlfredFrame]);
 			curAlfredFrame++;
+		} else if (alfredState == ALFRED_COMB) {
+			if (curAlfredFrame >= 11) {
+				curAlfredFrame = 0;
+			}
+			drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCombFrames[0][curAlfredFrame], xAlfred, yAlfred - kAlfredFrameHeight, 51, 102, 255);
+			curAlfredFrame++;
+
 		} else {
-			drawAlfred(_res->standingAnimFrames[dirAlfred]);
+			drawAlfred(_res->alfredIdle[dirAlfred]);
 		}
 		if (_displayPopup) {
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index c27d002104e..abef2134ece 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -48,9 +48,6 @@ namespace Pelrock {
 
 struct PelrockGameDescription;
 
-const int kAlfredFrameWidth = 51;
-const int kAlfredFrameHeight = 102;
-
 class PelrockEngine : public Engine {
 private:
 	const ADGameDescription *_gameDescription;
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 1cb5ebdf8b2..60e12f573ef 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -41,11 +41,11 @@ ResourceManager::~ResourceManager() {
 
 		// free all frame buffers
 		for (int j = 0; j < walkingAnimLengths[i]; j++) {
-			delete[] walkingAnimFrames[i][j];
+			delete[] alfredWalkFrames[i][j];
 		}
 
 		// free the array of pointers
-		delete[] walkingAnimFrames[i];
+		delete[] alfredWalkFrames[i];
 	}
 }
 
@@ -114,8 +114,9 @@ void ResourceManager::loadAlfredAnims() {
 	unsigned char *pic = new unsigned char[capacity];
 	rleDecompress(bufferFile, alfred3Size, 0, alfred3Size, &pic);
 
+	int frameSize = kAlfredFrameHeight * kAlfredFrameWidth;
 	for (int i = 0; i < 4; i++) {
-		standingAnimFrames[i] = new byte[kAlfredFrameWidth * kAlfredFrameHeight];
+		alfredIdle[i] = new byte[frameSize];
 		int talkingFramesOffset = walkingAnimLengths[0] + walkingAnimLengths[1] + walkingAnimLengths[2] + walkingAnimLengths[3] + 4;
 
 		int prevWalkingFrames = 0;
@@ -126,27 +127,66 @@ void ResourceManager::loadAlfredAnims() {
 			prevTalkingFrames += talkingAnimLengths[j];
 		}
 
-		walkingAnimFrames[i] = new byte *[walkingAnimLengths[i]];
+		alfredWalkFrames[i] = new byte *[walkingAnimLengths[i]];
 
 		int standingFrame = prevWalkingFrames;
 		debug("Loading standing frame %d at index %d", i, standingFrame);
-		extractSingleFrame(pic, standingAnimFrames[i], standingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
+		extractSingleFrame(pic, alfredIdle[i], standingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
 		for (int j = 0; j < walkingAnimLengths[i]; j++) {
 
-			walkingAnimFrames[i][j] = new byte[kAlfredFrameWidth * kAlfredFrameHeight];
+			alfredWalkFrames[i][j] = new byte[frameSize];
 			int walkingFrame = prevWalkingFrames + 1 + j;
-			extractSingleFrame(pic, walkingAnimFrames[i][j], walkingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
+			extractSingleFrame(pic, alfredWalkFrames[i][j], walkingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
 		}
 
-		talkingAnimFrames[i] = new byte *[talkingAnimLengths[i]];
+		alfredTalkFrames[i] = new byte *[talkingAnimLengths[i]];
 
 		int talkingStartFrame = talkingFramesOffset + prevTalkingFrames;
 		for (int j = 0; j < talkingAnimLengths[i]; j++) {
-			talkingAnimFrames[i][j] = new byte[kAlfredFrameWidth * kAlfredFrameHeight];
+			alfredTalkFrames[i][j] = new byte[frameSize];
 			int talkingFrame = talkingStartFrame + j;
-			extractSingleFrame(pic, talkingAnimFrames[i][j], talkingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
+			extractSingleFrame(pic, alfredTalkFrames[i][j], talkingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
 		}
 	}
+
+	free(bufferFile);
+
+	Common::File alfred7;
+	if (!alfred7.open(Common::Path("ALFRED.7"))) {
+		error("Could not open ALFRED.7");
+		return;
+	}
+	int spriteMapSize = frameSize * 11;
+	byte *alfredCombRightRaw;
+	size_t alfredCombRightSize;
+
+	readUntilBuda(&alfred7, ALFRED7_ALFRED_COMB_R, alfredCombRightRaw, alfredCombRightSize);
+	byte *alfredCombRight = nullptr;
+	rleDecompress(alfredCombRightRaw, alfredCombRightSize, 0, alfredCombRightSize, &alfredCombRight);
+
+	alfredCombFrames[0] = new byte *[11];
+	alfredCombFrames[1] = new byte *[11];
+
+	for (int i = 0; i < 11; i++) {
+
+		alfredCombFrames[0][i] = new byte[frameSize];
+		extractSingleFrame(alfredCombRight, alfredCombFrames[0][i], i, kAlfredFrameWidth, kAlfredFrameHeight);
+	}
+
+	byte *alfredCombLeftRaw;
+	size_t alfredCombLeftSize;
+	readUntilBuda(&alfred7, ALFRED7_ALFRED_COMB_L, alfredCombLeftRaw, alfredCombLeftSize);
+	byte *alfredCombLeft = nullptr;
+	rleDecompress(alfredCombLeftRaw, alfredCombLeftSize, 0, spriteMapSize, &alfredCombLeft);
+
+	for (int i = 0; i < 11; i++) {
+		alfredCombFrames[1][i] = new byte[frameSize];
+		extractSingleFrame(alfredCombLeft, alfredCombFrames[1][i], i, kAlfredFrameWidth, kAlfredFrameHeight);
+	}
+
+	alfred7.close();
+	free(alfredCombRightRaw);
+	free(alfredCombLeftRaw);
 }
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index a1e240055da..cab57604def 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -36,11 +36,14 @@ public:
 	void loadInteractionIcons();
 	void loadAlfredAnims();
 
-	byte **walkingAnimFrames[4];              // 4 arrays of arrays
-	byte *standingAnimFrames[4] = {nullptr};  // 4 directions
+	byte **alfredWalkFrames[4];              // 4 arrays of arrays
+	byte *alfredIdle[4] = {nullptr};  // 4 directions
 	int walkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
-	byte **talkingAnimFrames[4];              // 4 arrays of arrays
+	byte **alfredTalkFrames[4];              // 4 arrays of arrays
 	int talkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
+	byte **alfredCombFrames[2];
+
+
     byte *_cursorMasks[5] = {nullptr};
 	byte *_verbIcons[9] = {nullptr};
 	byte *_popUpBalloon = nullptr;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 8c02104b280..6fd46cfddfe 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -63,12 +63,17 @@ const int kBalloonFrames = 4;
 const int kTextCharDisplayTime = 100; // 10ms per character
 const int kVerbIconPadding = 20;
 
+const int kAlfredFrameWidth = 51;
+const int kAlfredFrameHeight = 102;
+
+
 
 enum AlfredState {
 	ALFRED_IDLE,
 	ALFRED_WALKING,
 	ALFRED_TALKING,
 	ALFRED_INTERACTING,
+	ALFRED_COMB
 };
 
 
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index e59e7e54468..b3633c300ea 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -68,6 +68,7 @@ size_t rleDecompress(const uint8_t *data, size_t data_size, uint32_t offset, uin
 		uint8_t value = data[pos + 1];
 
 		for (int i = 0; i < count; i++) {
+			// debug("Pos = %zu, writing value %02X", result_size, value);
 			(*out_data)[result_size++] = value;
 		}
 
@@ -102,6 +103,7 @@ void readUntilBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *
 			break;
 		}
 	}
+	debug("Read %zu bytes until BUDA marker", pos);
 	outSize = pos;
 }
 


Commit: 04a7b8c2c4818dc46e8b54778b31dd48cb8dd93a
    https://github.com/scummvm/scummvm/commit/04a7b8c2c4818dc46e8b54778b31dd48cb8dd93a
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:12+02:00

Commit Message:
PELROCK: Alfred interacting animation

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 58abc31d2c4..f2a723086cd 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -64,7 +64,6 @@ PelrockEngine::~PelrockEngine() {
 	// if (_bgPopupBalloon)
 	// 	delete[] _bgPopupBalloon;
 	delete _smallFont;
-
 }
 
 uint32 PelrockEngine::getFeatures() const {
@@ -114,6 +113,8 @@ Common::Error PelrockEngine::run() {
 					alfredState = ALFRED_IDLE;
 				} else if (e.kbd.keycode == Common::KEYCODE_c) {
 					alfredState = ALFRED_COMB;
+				} else if (e.kbd.keycode == Common::KEYCODE_i) {
+					alfredState = ALFRED_INTERACTING;
 				}
 			} else if (e.type == Common::EVENT_MOUSEMOVE) {
 				mouseX = e.mouse.x;
@@ -336,7 +337,7 @@ void PelrockEngine::frames() {
 
 			// debug("Drawing walking frame %d for direction %d", curAlfredFrame, dirAlfred);
 
-			if (curAlfredFrame >= _res->walkingAnimLengths[dirAlfred]) {
+			if (curAlfredFrame >= walkingAnimLengths[dirAlfred]) {
 				curAlfredFrame = 0;
 			}
 
@@ -344,7 +345,7 @@ void PelrockEngine::frames() {
 			curAlfredFrame++;
 
 		} else if (alfredState == ALFRED_TALKING) {
-			if (curAlfredFrame >= _res->talkingAnimLengths[dirAlfred] - 1) {
+			if (curAlfredFrame >= talkingAnimLengths[dirAlfred] - 1) {
 				curAlfredFrame = 0;
 			}
 			drawAlfred(_res->alfredTalkFrames[dirAlfred][curAlfredFrame]);
@@ -356,7 +357,14 @@ void PelrockEngine::frames() {
 			drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCombFrames[0][curAlfredFrame], xAlfred, yAlfred - kAlfredFrameHeight, 51, 102, 255);
 			curAlfredFrame++;
 
-		} else {
+		} else if(alfredState == ALFRED_INTERACTING) {
+			if (curAlfredFrame >= interactingAnimLength) {
+				curAlfredFrame = 0;
+			}
+			drawAlfred(_res->alfredInteractFrames[dirAlfred][curAlfredFrame]);
+			curAlfredFrame++;
+		}
+		else {
 			drawAlfred(_res->alfredIdle[dirAlfred]);
 		}
 		if (_displayPopup) {
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 60e12f573ef..c131921fc27 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -38,15 +38,25 @@ ResourceManager::~ResourceManager() {
 	}
 	delete[] _popUpBalloon;
 	for (int i = 0; i < 4; i++) {
-
 		// free all frame buffers
 		for (int j = 0; j < walkingAnimLengths[i]; j++) {
 			delete[] alfredWalkFrames[i][j];
+			delete[] alfredTalkFrames[i][j];
+		}
+
+		for(int j = 0; j < 4; j ++) {
+			delete[] alfredInteractFrames[i][j];
 		}
 
 		// free the array of pointers
 		delete[] alfredWalkFrames[i];
+		delete[] alfredTalkFrames[i];
+		delete[] alfredInteractFrames[i];
+		delete[] alfredIdle[i];
 	}
+
+		delete[] alfredCombFrames[0];
+		delete[] alfredCombFrames[1];
 }
 
 void ResourceManager::loadCursors() {
@@ -118,13 +128,15 @@ void ResourceManager::loadAlfredAnims() {
 	for (int i = 0; i < 4; i++) {
 		alfredIdle[i] = new byte[frameSize];
 		int talkingFramesOffset = walkingAnimLengths[0] + walkingAnimLengths[1] + walkingAnimLengths[2] + walkingAnimLengths[3] + 4;
-
+		int interactingFramesOffset = talkingFramesOffset + talkingAnimLengths[0] + talkingAnimLengths[1] + talkingAnimLengths[2] + talkingAnimLengths[3];
 		int prevWalkingFrames = 0;
 		int prevTalkingFrames = 0;
+		int prevInteractingFrames = 0;
 
 		for (int j = 0; j < i; j++) {
 			prevWalkingFrames += walkingAnimLengths[j] + 1;
 			prevTalkingFrames += talkingAnimLengths[j];
+			prevInteractingFrames += interactingAnimLength;
 		}
 
 		alfredWalkFrames[i] = new byte *[walkingAnimLengths[i]];
@@ -147,6 +159,15 @@ void ResourceManager::loadAlfredAnims() {
 			int talkingFrame = talkingStartFrame + j;
 			extractSingleFrame(pic, alfredTalkFrames[i][j], talkingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
 		}
+
+		alfredInteractFrames[i] = new byte *[interactingAnimLength];
+		int interactingStartFrame = interactingFramesOffset + prevInteractingFrames;
+		for (int j = 0; j < interactingAnimLength; j++) {
+			alfredInteractFrames[i][j] = new byte[frameSize];
+			int interactingFrame = interactingStartFrame + j;
+			extractSingleFrame(pic, alfredInteractFrames[i][j], interactingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
+		}
+
 	}
 
 	free(bufferFile);
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index cab57604def..bfa3b60efd1 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -25,6 +25,11 @@
 
 namespace Pelrock {
 
+
+static const int walkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
+static const int talkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
+static const int interactingAnimLength = 2;
+
 class ResourceManager {
 private:
 	/* data */
@@ -36,13 +41,14 @@ public:
 	void loadInteractionIcons();
 	void loadAlfredAnims();
 
-	byte **alfredWalkFrames[4];              // 4 arrays of arrays
 	byte *alfredIdle[4] = {nullptr};  // 4 directions
-	int walkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
+
+	byte **alfredWalkFrames[4];              // 4 arrays of arrays
+
 	byte **alfredTalkFrames[4];              // 4 arrays of arrays
-	int talkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
-	byte **alfredCombFrames[2];
 
+	byte **alfredCombFrames[2];
+	byte **alfredInteractFrames[4];
 
     byte *_cursorMasks[5] = {nullptr};
 	byte *_verbIcons[9] = {nullptr};


Commit: aaeac0248fb759859ccc8444377cb7a819431c52
    https://github.com/scummvm/scummvm/commit/aaeac0248fb759859ccc8444377cb7a819431c52
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:12+02:00

Commit Message:
PELROCK: Turn if into switch when handling alfred state

Changed paths:
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index f2a723086cd..0f0e6516ac9 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -105,18 +105,26 @@ Common::Error PelrockEngine::run() {
 		_chronoManager->updateChrono();
 		while (g_system->getEventManager()->pollEvent(e)) {
 			if (e.type == Common::EVENT_KEYDOWN) {
-				if (e.kbd.keycode == Common::KEYCODE_w) {
-					alfredState = ALFRED_WALKING;
-				} else if (e.kbd.keycode == Common::KEYCODE_t) {
-					alfredState = ALFRED_TALKING;
-				} else if (e.kbd.keycode == Common::KEYCODE_s) {
-					alfredState = ALFRED_IDLE;
-				} else if (e.kbd.keycode == Common::KEYCODE_c) {
-					alfredState = ALFRED_COMB;
-				} else if (e.kbd.keycode == Common::KEYCODE_i) {
-					alfredState = ALFRED_INTERACTING;
-				}
-			} else if (e.type == Common::EVENT_MOUSEMOVE) {
+            switch (e.kbd.keycode) {
+                case Common::KEYCODE_w:
+                    alfredState = ALFRED_WALKING;
+                    break;
+                case Common::KEYCODE_t:
+                    alfredState = ALFRED_TALKING;
+                    break;
+                case Common::KEYCODE_s:
+                    alfredState = ALFRED_IDLE;
+                    break;
+                case Common::KEYCODE_c:
+                    alfredState = ALFRED_COMB;
+                    break;
+                case Common::KEYCODE_i:
+                    alfredState = ALFRED_INTERACTING;
+                    break;
+                default:
+                    break;
+            }
+        } else if (e.type == Common::EVENT_MOUSEMOVE) {
 				mouseX = e.mouse.x;
 				mouseY = e.mouse.y;
 				// debug(3, "Mouse moved to (%d,%d)", mouseX, mouseY);
@@ -282,91 +290,88 @@ void PelrockEngine::frames() {
 			drawNextFrame(&animSet);
 		}
 
-		if (alfredState == ALFRED_WALKING) {
-
-			MovementStep step = _currentContext.movement_buffer[_current_step];
-			// debug("Alfred step: distance_x=%d, distance_y=%d", step.distance_x, step.distance_y);
-
-			if (step.distance_x > 0) {
-				if (step.flags & MOVE_RIGHT) {
-					dirAlfred = 0;
-					xAlfred += MIN((uint16_t)6, step.distance_x);
-				}
-				if (step.flags & MOVE_LEFT) {
-					dirAlfred = 1;
-					xAlfred -= MIN((uint16_t)6, step.distance_x);
-				}
-			}
-			if (step.distance_y > 0) {
-				if (step.flags & MOVE_DOWN) {
-					dirAlfred = 2;
-					yAlfred += MIN((uint16_t)6, step.distance_y);
-				}
-				if (step.flags & MOVE_UP) {
-					dirAlfred = 3;
-					yAlfred -= MIN((uint16_t)6, step.distance_y);
-				}
-			}
-
-			if (step.distance_x > 0)
-				step.distance_x -= MIN((uint16_t)6, step.distance_x);
-
-			if (step.distance_y > 0)
-				step.distance_y -= MIN((uint16_t)6, step.distance_y);
-
-			// debug("Alfred position after step: x=%d, y=%d, step distance_x=%d, step distance_y=%d", xAlfred, yAlfred, step.distance_x, step.distance_y);
-			if (step.distance_x <= 0 && step.distance_y <= 0) {
-				// debug("Alfred completed step %d", _current_step);
-				_current_step++;
-				if (_current_step >= _currentContext.movement_count) {
-					// debug("Alfred reached his walk target.");
-					_current_step = 0;
-					alfredState = ALFRED_IDLE;
-				}
-			} else {
-				_currentContext.movement_buffer[_current_step] = step;
-			}
-
-			Exit *exit = isExitUnder(xAlfred, yAlfred);
-
-			if (exit != nullptr) {
-				xAlfred = exit->targetX;
-				yAlfred = exit->targetY;
-				setScreen(exit->targetRoom, exit->dir);
-			}
-
-			// debug("Drawing walking frame %d for direction %d", curAlfredFrame, dirAlfred);
-
-			if (curAlfredFrame >= walkingAnimLengths[dirAlfred]) {
-				curAlfredFrame = 0;
-			}
-
-			drawAlfred(_res->alfredWalkFrames[dirAlfred][curAlfredFrame]);
-			curAlfredFrame++;
-
-		} else if (alfredState == ALFRED_TALKING) {
-			if (curAlfredFrame >= talkingAnimLengths[dirAlfred] - 1) {
-				curAlfredFrame = 0;
-			}
-			drawAlfred(_res->alfredTalkFrames[dirAlfred][curAlfredFrame]);
-			curAlfredFrame++;
-		} else if (alfredState == ALFRED_COMB) {
-			if (curAlfredFrame >= 11) {
-				curAlfredFrame = 0;
-			}
-			drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCombFrames[0][curAlfredFrame], xAlfred, yAlfred - kAlfredFrameHeight, 51, 102, 255);
-			curAlfredFrame++;
-
-		} else if(alfredState == ALFRED_INTERACTING) {
-			if (curAlfredFrame >= interactingAnimLength) {
-				curAlfredFrame = 0;
-			}
-			drawAlfred(_res->alfredInteractFrames[dirAlfred][curAlfredFrame]);
-			curAlfredFrame++;
-		}
-		else {
-			drawAlfred(_res->alfredIdle[dirAlfred]);
-		}
+		switch (alfredState) {
+            case ALFRED_WALKING: {
+                MovementStep step = _currentContext.movement_buffer[_current_step];
+
+                if (step.distance_x > 0) {
+                    if (step.flags & MOVE_RIGHT) {
+                        dirAlfred = 0;
+                        xAlfred += MIN((uint16_t)6, step.distance_x);
+                    }
+                    if (step.flags & MOVE_LEFT) {
+                        dirAlfred = 1;
+                        xAlfred -= MIN((uint16_t)6, step.distance_x);
+                    }
+                }
+                if (step.distance_y > 0) {
+                    if (step.flags & MOVE_DOWN) {
+                        dirAlfred = 2;
+                        yAlfred += MIN((uint16_t)6, step.distance_y);
+                    }
+                    if (step.flags & MOVE_UP) {
+                        dirAlfred = 3;
+                        yAlfred -= MIN((uint16_t)6, step.distance_y);
+                    }
+                }
+
+                if (step.distance_x > 0)
+                    step.distance_x -= MIN((uint16_t)6, step.distance_x);
+
+                if (step.distance_y > 0)
+                    step.distance_y -= MIN((uint16_t)6, step.distance_y);
+
+                if (step.distance_x <= 0 && step.distance_y <= 0) {
+                    _current_step++;
+                    if (_current_step >= _currentContext.movement_count) {
+                        _current_step = 0;
+                        alfredState = ALFRED_IDLE;
+                    }
+                } else {
+                    _currentContext.movement_buffer[_current_step] = step;
+                }
+
+                Exit *exit = isExitUnder(xAlfred, yAlfred);
+
+                if (exit != nullptr) {
+                    xAlfred = exit->targetX;
+                    yAlfred = exit->targetY;
+                    setScreen(exit->targetRoom, exit->dir);
+                }
+
+                if (curAlfredFrame >= walkingAnimLengths[dirAlfred]) {
+                    curAlfredFrame = 0;
+                }
+
+                drawAlfred(_res->alfredWalkFrames[dirAlfred][curAlfredFrame]);
+                curAlfredFrame++;
+                break;
+            }
+            case ALFRED_TALKING:
+                if (curAlfredFrame >= talkingAnimLengths[dirAlfred] - 1) {
+                    curAlfredFrame = 0;
+                }
+                drawAlfred(_res->alfredTalkFrames[dirAlfred][curAlfredFrame]);
+                curAlfredFrame++;
+                break;
+            case ALFRED_COMB:
+                if (curAlfredFrame >= 11) {
+                    curAlfredFrame = 0;
+                }
+                drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCombFrames[0][curAlfredFrame], xAlfred, yAlfred - kAlfredFrameHeight, 51, 102, 255);
+                curAlfredFrame++;
+                break;
+            case ALFRED_INTERACTING:
+                if (curAlfredFrame >= interactingAnimLength) {
+                    curAlfredFrame = 0;
+                }
+                drawAlfred(_res->alfredInteractFrames[dirAlfred][curAlfredFrame]);
+                curAlfredFrame++;
+                break;
+            default:
+                drawAlfred(_res->alfredIdle[dirAlfred]);
+                break;
+        }
 		if (_displayPopup) {
 
 			// byte *bgDialog = new byte[kBalloonWidth * kBalloonHeight];
@@ -414,7 +419,7 @@ void PelrockEngine::frames() {
 		for (int i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
 			// debug("Drawing walkbox %d", i);
 			WalkBox box = _room->_currentRoomWalkboxes[i];
-			// drawRect(_screen, box.x, box.y, box.w, box.h, 150 + i);
+			drawRect(_screen, box.x, box.y, box.w, box.h, 150 + i);
 		}
 		if (_curWalkTarget.x < 640 && _curWalkTarget.y < 400 && _curWalkTarget.x >= 0 && _curWalkTarget.y >= 0) {
 			_screen->setPixel(_curWalkTarget.x, _curWalkTarget.y, 100);


Commit: 3226b5217a173fc6a2cac54a384b0263382c9e14
    https://github.com/scummvm/scummvm/commit/3226b5217a173fc6a2cac54a384b0263382c9e14
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:12+02:00

Commit Message:
PELROCK: Initial Intro video playback

Changed paths:
  A engines/pelrock/video/video.cpp
  A engines/pelrock/video/video.h
    engines/pelrock/chrono.cpp
    engines/pelrock/chrono.h
    engines/pelrock/module.mk
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/chrono.cpp b/engines/pelrock/chrono.cpp
index 2b2eba5903a..306e38c1ca9 100644
--- a/engines/pelrock/chrono.cpp
+++ b/engines/pelrock/chrono.cpp
@@ -22,6 +22,7 @@
 #include "common/events.h"
 #include "common/system.h"
 
+#include "chrono.h"
 #include "pelrock/chrono.h"
 #include "pelrock/pelrock.h"
 
@@ -36,9 +37,9 @@ ChronoManager::~ChronoManager() {
 void ChronoManager::updateChrono() {
 	uint32 currentTime = g_system->getMillis();
 
-	if(_textTtl > 0 && g_engine->alfredState == ALFRED_TALKING && g_engine->alfredState != ALFRED_WALKING) {
+	if (_textTtl > 0 && g_engine->alfredState == ALFRED_TALKING && g_engine->alfredState != ALFRED_WALKING) {
 		_textTtl -= (currentTime - _lastTick);
-		if(_textTtl < 0)
+		if (_textTtl < 0)
 			_textTtl = 0;
 	}
 
@@ -71,10 +72,24 @@ void ChronoManager::delay(uint32 ms) {
 	Common::Event e;
 	while ((g_system->getMillis() - delayStart) < ms && !g_engine->shouldQuit()) {
 		while (g_system->getEventManager()->pollEvent(e)) {
-
 		}
 		g_engine->_screen->update();
 	}
 }
 
+void ChronoManager::waitForKey() {
+	bool waitForKey = false;
+	Common::Event e;
+	debug("Waiting for key!");
+	while (!waitForKey && !g_engine->shouldQuit()) {
+		while (g_system->getEventManager()->pollEvent(e)) {
+			if (e.type == Common::EVENT_KEYDOWN) {
+				waitForKey = true;
+			}
+		}
+
+		g_engine->_screen->update();
+		g_system->delayMillis(10);
+	}
+}
 } // End of namespace Pelrock
diff --git a/engines/pelrock/chrono.h b/engines/pelrock/chrono.h
index de19a626789..ffdf43f3b3e 100644
--- a/engines/pelrock/chrono.h
+++ b/engines/pelrock/chrono.h
@@ -41,6 +41,7 @@ public:
 	void updateChrono();
 	void changeSpeed();
 	void delay(uint32 ms);
+	void waitForKey();
 
 	bool _gameTick = false;
 	bool _gameTickHalfSpeed = false;
diff --git a/engines/pelrock/module.mk b/engines/pelrock/module.mk
index a30d1632dd7..88894769896 100644
--- a/engines/pelrock/module.mk
+++ b/engines/pelrock/module.mk
@@ -9,7 +9,8 @@ MODULE_OBJS = \
 	fonts/small_font.o \
 	fonts/large_font.o \
 	util.o \
-	resources.o
+	resources.o\
+	video/video.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_PELROCK), DYNAMIC_PLUGIN)
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 0f0e6516ac9..b1a68231810 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -60,6 +60,7 @@ PelrockEngine::~PelrockEngine() {
 	delete _largeFont;
 	delete _screen;
 	delete _chronoManager;
+	delete _videoManager;
 
 	// if (_bgPopupBalloon)
 	// 	delete[] _bgPopupBalloon;
@@ -80,6 +81,7 @@ Common::Error PelrockEngine::run() {
 	// Initialize 320x200 paletted graphics mode
 	initGraphics(640, 400);
 	_screen = new Graphics::Screen();
+	_videoManager = new VideoManager(_screen);
 
 	// Set the engine's debugger console
 	setDebugger(new Console());
@@ -98,33 +100,33 @@ Common::Error PelrockEngine::run() {
 		stateGame = GAME;
 	} else {
 		stateGame = INTRO;
-		playIntro();
+		_videoManager->playIntro();
 	}
 
 	while (!shouldQuit()) {
 		_chronoManager->updateChrono();
 		while (g_system->getEventManager()->pollEvent(e)) {
 			if (e.type == Common::EVENT_KEYDOWN) {
-            switch (e.kbd.keycode) {
-                case Common::KEYCODE_w:
-                    alfredState = ALFRED_WALKING;
-                    break;
-                case Common::KEYCODE_t:
-                    alfredState = ALFRED_TALKING;
-                    break;
-                case Common::KEYCODE_s:
-                    alfredState = ALFRED_IDLE;
-                    break;
-                case Common::KEYCODE_c:
-                    alfredState = ALFRED_COMB;
-                    break;
-                case Common::KEYCODE_i:
-                    alfredState = ALFRED_INTERACTING;
-                    break;
-                default:
-                    break;
-            }
-        } else if (e.type == Common::EVENT_MOUSEMOVE) {
+				switch (e.kbd.keycode) {
+				case Common::KEYCODE_w:
+					alfredState = ALFRED_WALKING;
+					break;
+				case Common::KEYCODE_t:
+					alfredState = ALFRED_TALKING;
+					break;
+				case Common::KEYCODE_s:
+					alfredState = ALFRED_IDLE;
+					break;
+				case Common::KEYCODE_c:
+					alfredState = ALFRED_COMB;
+					break;
+				case Common::KEYCODE_i:
+					alfredState = ALFRED_INTERACTING;
+					break;
+				default:
+					break;
+				}
+			} else if (e.type == Common::EVENT_MOUSEMOVE) {
 				mouseX = e.mouse.x;
 				mouseY = e.mouse.y;
 				// debug(3, "Mouse moved to (%d,%d)", mouseX, mouseY);
@@ -183,9 +185,6 @@ void PelrockEngine::init() {
 	}
 }
 
-void PelrockEngine::playIntro() {
-}
-
 void PelrockEngine::loadAnims() {
 	_res->loadAlfredAnims();
 }
@@ -291,87 +290,87 @@ void PelrockEngine::frames() {
 		}
 
 		switch (alfredState) {
-            case ALFRED_WALKING: {
-                MovementStep step = _currentContext.movement_buffer[_current_step];
-
-                if (step.distance_x > 0) {
-                    if (step.flags & MOVE_RIGHT) {
-                        dirAlfred = 0;
-                        xAlfred += MIN((uint16_t)6, step.distance_x);
-                    }
-                    if (step.flags & MOVE_LEFT) {
-                        dirAlfred = 1;
-                        xAlfred -= MIN((uint16_t)6, step.distance_x);
-                    }
-                }
-                if (step.distance_y > 0) {
-                    if (step.flags & MOVE_DOWN) {
-                        dirAlfred = 2;
-                        yAlfred += MIN((uint16_t)6, step.distance_y);
-                    }
-                    if (step.flags & MOVE_UP) {
-                        dirAlfred = 3;
-                        yAlfred -= MIN((uint16_t)6, step.distance_y);
-                    }
-                }
-
-                if (step.distance_x > 0)
-                    step.distance_x -= MIN((uint16_t)6, step.distance_x);
-
-                if (step.distance_y > 0)
-                    step.distance_y -= MIN((uint16_t)6, step.distance_y);
-
-                if (step.distance_x <= 0 && step.distance_y <= 0) {
-                    _current_step++;
-                    if (_current_step >= _currentContext.movement_count) {
-                        _current_step = 0;
-                        alfredState = ALFRED_IDLE;
-                    }
-                } else {
-                    _currentContext.movement_buffer[_current_step] = step;
-                }
-
-                Exit *exit = isExitUnder(xAlfred, yAlfred);
-
-                if (exit != nullptr) {
-                    xAlfred = exit->targetX;
-                    yAlfred = exit->targetY;
-                    setScreen(exit->targetRoom, exit->dir);
-                }
-
-                if (curAlfredFrame >= walkingAnimLengths[dirAlfred]) {
-                    curAlfredFrame = 0;
-                }
-
-                drawAlfred(_res->alfredWalkFrames[dirAlfred][curAlfredFrame]);
-                curAlfredFrame++;
-                break;
-            }
-            case ALFRED_TALKING:
-                if (curAlfredFrame >= talkingAnimLengths[dirAlfred] - 1) {
-                    curAlfredFrame = 0;
-                }
-                drawAlfred(_res->alfredTalkFrames[dirAlfred][curAlfredFrame]);
-                curAlfredFrame++;
-                break;
-            case ALFRED_COMB:
-                if (curAlfredFrame >= 11) {
-                    curAlfredFrame = 0;
-                }
-                drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCombFrames[0][curAlfredFrame], xAlfred, yAlfred - kAlfredFrameHeight, 51, 102, 255);
-                curAlfredFrame++;
-                break;
-            case ALFRED_INTERACTING:
-                if (curAlfredFrame >= interactingAnimLength) {
-                    curAlfredFrame = 0;
-                }
-                drawAlfred(_res->alfredInteractFrames[dirAlfred][curAlfredFrame]);
-                curAlfredFrame++;
-                break;
-            default:
-                drawAlfred(_res->alfredIdle[dirAlfred]);
-                break;
-        }
+		case ALFRED_WALKING: {
+			MovementStep step = _currentContext.movement_buffer[_current_step];
+
+			if (step.distance_x > 0) {
+				if (step.flags & MOVE_RIGHT) {
+					dirAlfred = 0;
+					xAlfred += MIN((uint16_t)6, step.distance_x);
+				}
+				if (step.flags & MOVE_LEFT) {
+					dirAlfred = 1;
+					xAlfred -= MIN((uint16_t)6, step.distance_x);
+				}
+			}
+			if (step.distance_y > 0) {
+				if (step.flags & MOVE_DOWN) {
+					dirAlfred = 2;
+					yAlfred += MIN((uint16_t)6, step.distance_y);
+				}
+				if (step.flags & MOVE_UP) {
+					dirAlfred = 3;
+					yAlfred -= MIN((uint16_t)6, step.distance_y);
+				}
+			}
+
+			if (step.distance_x > 0)
+				step.distance_x -= MIN((uint16_t)6, step.distance_x);
+
+			if (step.distance_y > 0)
+				step.distance_y -= MIN((uint16_t)6, step.distance_y);
+
+			if (step.distance_x <= 0 && step.distance_y <= 0) {
+				_current_step++;
+				if (_current_step >= _currentContext.movement_count) {
+					_current_step = 0;
+					alfredState = ALFRED_IDLE;
+				}
+			} else {
+				_currentContext.movement_buffer[_current_step] = step;
+			}
+
+			Exit *exit = isExitUnder(xAlfred, yAlfred);
+
+			if (exit != nullptr) {
+				xAlfred = exit->targetX;
+				yAlfred = exit->targetY;
+				setScreen(exit->targetRoom, exit->dir);
+			}
+
+			if (curAlfredFrame >= walkingAnimLengths[dirAlfred]) {
+				curAlfredFrame = 0;
+			}
+
+			drawAlfred(_res->alfredWalkFrames[dirAlfred][curAlfredFrame]);
+			curAlfredFrame++;
+			break;
+		}
+		case ALFRED_TALKING:
+			if (curAlfredFrame >= talkingAnimLengths[dirAlfred] - 1) {
+				curAlfredFrame = 0;
+			}
+			drawAlfred(_res->alfredTalkFrames[dirAlfred][curAlfredFrame]);
+			curAlfredFrame++;
+			break;
+		case ALFRED_COMB:
+			if (curAlfredFrame >= 11) {
+				curAlfredFrame = 0;
+			}
+			drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCombFrames[0][curAlfredFrame], xAlfred, yAlfred - kAlfredFrameHeight, 51, 102, 255);
+			curAlfredFrame++;
+			break;
+		case ALFRED_INTERACTING:
+			if (curAlfredFrame >= interactingAnimLength) {
+				curAlfredFrame = 0;
+			}
+			drawAlfred(_res->alfredInteractFrames[dirAlfred][curAlfredFrame]);
+			curAlfredFrame++;
+			break;
+		default:
+			drawAlfred(_res->alfredIdle[dirAlfred]);
+			break;
+		}
 		if (_displayPopup) {
 
 			// byte *bgDialog = new byte[kBalloonWidth * kBalloonHeight];
@@ -993,6 +992,8 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 		if (exit != nullptr) {
 			xAlfred = exit->targetX;
 			yAlfred = exit->targetY;
+
+			debug("Placing character at %d, %d", exit->targetX, exit->targetY);
 			setScreen(exit->targetRoom, exit->dir);
 		} else {
 			walkTo(walkTarget.x, walkTarget.y);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index abef2134ece..1ec613533ad 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -43,6 +43,7 @@
 #include "pelrock/resources.h"
 #include "pelrock/room.h"
 #include "pelrock/types.h"
+#include "pelrock/video/video.h"
 
 namespace Pelrock {
 
@@ -52,12 +53,10 @@ class PelrockEngine : public Engine {
 private:
 	const ADGameDescription *_gameDescription;
 	Common::RandomSource _randomSource;
-	ChronoManager *_chronoManager = nullptr;
 	RoomManager *_room = nullptr;
 	ResourceManager *_res = nullptr;
 
 	void init();
-	void playIntro();
 	void setScreen(int s, int dir);
 	void setScreenJava(int s, int dir);
 	void loadAnims();
@@ -147,8 +146,8 @@ private:
 	bool isNPCBTalking = false;
 
 	// JAVA
-	bool shouldPlayIntro = false;
-	GameState stateGame = GAME;
+	bool shouldPlayIntro = true;
+	GameState stateGame = INTRO;
 	bool gameInitialized = false;
 	bool screenReady = false;
 	// int prevDirX = 0;
@@ -166,6 +165,8 @@ protected:
 public:
 	Graphics::Screen *_screen = nullptr;
 	AlfredState alfredState = ALFRED_IDLE;
+	ChronoManager *_chronoManager = nullptr;
+	VideoManager *_videoManager = nullptr;
 public:
 	PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc);
 	~PelrockEngine() override;
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
new file mode 100644
index 00000000000..03d33b3af42
--- /dev/null
+++ b/engines/pelrock/video/video.cpp
@@ -0,0 +1,113 @@
+/* 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 "common/file.h"
+#include "graphics/screen.h"
+
+#include "pelrock/chrono.h"
+#include "pelrock/pelrock.h"
+#include "pelrock/video/video.h"
+#include "video.h"
+
+namespace Pelrock {
+
+VideoManager::VideoManager(Graphics::Screen *screen) : _screen(screen) {
+}
+
+VideoManager::~VideoManager() {
+}
+
+void VideoManager::playIntro() {
+	Common::File videoFile;
+	if (!videoFile.open("ESCENAX.SSN")) {
+		error("Could not open ESCENAX.SSN");
+		return;
+	}
+	loadPalette(videoFile);
+	videoFile.seek(0x5000, SEEK_SET);
+	size_t firstChunkSize = 0x5000 * 13;
+	byte *chunk0 = new byte[firstChunkSize];
+	videoFile.read(chunk0, firstChunkSize);
+	byte *background = decodeCopyBlock(chunk0, firstChunkSize, 0);
+
+	for (int i = 0; i < 640; i++) {
+		for (int j = 0; j < 400; j++) {
+			_screen->setPixel(i, j, background[j * 640 + i]);
+		}
+	}
+	_screen->markAllDirty();
+	_screen->update();
+	g_engine->_chronoManager->waitForKey();
+	videoFile.close();
+}
+void VideoManager::loadPalette(Common::SeekableReadStream &stream) {
+
+	byte paletteData[768];
+	stream.seek(0x0009, SEEK_SET);
+	stream.read(paletteData, 768);
+	byte palette[768];
+	for (int i = 0; i < 256; i++) {
+		palette[i * 3 + 0] = paletteData[i * 3 + 0] << 2;
+		palette[i * 3 + 1] = paletteData[i * 3 + 1] << 2;
+		palette[i * 3 + 2] = paletteData[i * 3 + 2] << 2;
+	}
+	_screen->setPalette(palette);
+	// def extract_palette(data):
+	// """Extract and convert VGA palette to 8-bit RGB"""
+	// file_palette = data[0x0009:0x0009 + 768]
+	// palette = []
+	// for i in range(256):
+	//     r = file_palette[i * 3 + 0] * 4
+	//     g = file_palette[i * 3 + 1] * 4
+	//     b = file_palette[i * 3 + 2] * 4
+	//     palette.extend([r, g, b])
+	// return palette
+}
+
+byte *VideoManager::decodeCopyBlock(byte *data, size_t size, uint32 offset) {
+
+	byte *buf = new byte[256000];
+	memset(buf, 0, 256000);
+	uint32 pos = offset + 0x0D;
+	// frames are encoded so that each block copy has a 5-byte header
+	// the first 3 bytes are the offset within the screen to which to
+	// copy the bytes. The 5th byte is the length of the block to copy.
+	while (pos + 5 < size) {
+		byte dest_lo = data[pos];
+		byte dest_mid = data[pos + 1];
+		byte dest_hi = data[pos + 2];
+		byte length = data[pos + 4];
+		if (length == 0) {
+			break;
+		}
+		uint32 dest_offset = dest_lo | (dest_mid << 8) | (dest_hi << 16);
+
+		if (dest_offset + length > 256000) {
+			break;
+		}
+		pos += 5;
+		Common::copy(data + pos, data + pos + length, buf + dest_offset);
+		pos += length;
+	}
+
+	return buf; // Placeholder
+}
+} // End of namespace Pelrock
diff --git a/engines/pelrock/video/video.h b/engines/pelrock/video/video.h
new file mode 100644
index 00000000000..af4b23fe9dc
--- /dev/null
+++ b/engines/pelrock/video/video.h
@@ -0,0 +1,50 @@
+/* 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 PELROCK_VIDEO_H
+#define PELROCK_VIDEO_H
+
+namespace Pelrock {
+
+static const uint32 offsets[] = {
+	0x64000,
+	0x69000,
+	0x6E000,
+	0x73000,
+	0x78000,
+	0x7D000,
+	0x82000,
+	0x87000};
+
+class VideoManager {
+public:
+	VideoManager(Graphics::Screen *screen);
+	~VideoManager();
+	void playIntro();
+
+private:
+	Graphics::Screen *_screen;
+	void loadPalette(Common::SeekableReadStream &stream);
+	byte *decodeCopyBlock(byte *data, size_t size, uint32 offset);
+};
+
+} // End of namespace Pelrock
+#endif


Commit: 45624b59540b367a5716d9f283af0dd28624d1d5
    https://github.com/scummvm/scummvm/commit/45624b59540b367a5716d9f283af0dd28624d1d5
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:13+02:00

Commit Message:
PELROCK: Renders first video sequence

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/video/video.cpp
    engines/pelrock/video/video.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index b1a68231810..f21d8d436c0 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -95,13 +95,14 @@ Common::Error PelrockEngine::run() {
 	Common::Event e;
 	Graphics::FrameLimiter limiter(g_system, 60);
 
-	init();
 	if (shouldPlayIntro == false) {
 		stateGame = GAME;
 	} else {
 		stateGame = INTRO;
 		_videoManager->playIntro();
+		stateGame = GAME;
 	}
+	init();
 
 	while (!shouldQuit()) {
 		_chronoManager->updateChrono();
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index 03d33b3af42..9ebcc585608 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -42,11 +42,11 @@ void VideoManager::playIntro() {
 		return;
 	}
 	loadPalette(videoFile);
-	videoFile.seek(0x5000, SEEK_SET);
-	size_t firstChunkSize = 0x5000 * 13;
+	videoFile.seek(frame0offset, SEEK_SET);
+	size_t firstChunkSize = chunkSize * 13;
 	byte *chunk0 = new byte[firstChunkSize];
 	videoFile.read(chunk0, firstChunkSize);
-	byte *background = decodeCopyBlock(chunk0, firstChunkSize, 0);
+	byte *background = decodeCopyBlock(chunk0, 0);
 
 	for (int i = 0; i < 640; i++) {
 		for (int j = 0; j < 400; j++) {
@@ -55,6 +55,47 @@ void VideoManager::playIntro() {
 	}
 	_screen->markAllDirty();
 	_screen->update();
+	delete[] chunk0;
+	g_engine->_chronoManager->waitForKey();
+
+	size_t chunk1Size = chunkSize * 6;
+	byte chunk1_data[chunk1Size];
+	videoFile.seek(frame1offset, SEEK_SET);
+	videoFile.read(chunk1_data, chunk1Size);
+
+	byte *delta1 = decodeRLE(chunk1_data, chunk1Size, 0x0D);
+	for (int j = 0; j < 256000; j++) {
+		background[j] ^= delta1[j];
+	}
+	delete[] delta1;
+	for (int x = 0; x < 640; x++) {
+		for (int y = 0; y < 400; y++) {
+			_screen->setPixel(x, y, background[y * 640 + x]);
+		}
+	}
+	_screen->markAllDirty();
+	_screen->update();
+	g_engine->_chronoManager->waitForKey();
+
+	for (size_t i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) {
+		byte *chunk = new byte[chunkSize];
+		videoFile.seek(offsets[i], SEEK_SET);
+		videoFile.read(chunk, chunkSize);
+		byte *delta = decodeCopyBlock(chunk, 0);
+		for (int j = 0; j < 256000; j++) {
+			background[j] ^= delta[j];
+		}
+		delete[] delta;
+		for (int x = 0; x < 640; x++) {
+			for (int y = 0; y < 400; y++) {
+				_screen->setPixel(x, y, background[y * 640 + x]);
+			}
+		}
+		delete[] chunk;
+		_screen->markAllDirty();
+		_screen->update();
+		g_engine->_chronoManager->waitForKey();
+	}
 	g_engine->_chronoManager->waitForKey();
 	videoFile.close();
 }
@@ -70,19 +111,9 @@ void VideoManager::loadPalette(Common::SeekableReadStream &stream) {
 		palette[i * 3 + 2] = paletteData[i * 3 + 2] << 2;
 	}
 	_screen->setPalette(palette);
-	// def extract_palette(data):
-	// """Extract and convert VGA palette to 8-bit RGB"""
-	// file_palette = data[0x0009:0x0009 + 768]
-	// palette = []
-	// for i in range(256):
-	//     r = file_palette[i * 3 + 0] * 4
-	//     g = file_palette[i * 3 + 1] * 4
-	//     b = file_palette[i * 3 + 2] * 4
-	//     palette.extend([r, g, b])
-	// return palette
 }
 
-byte *VideoManager::decodeCopyBlock(byte *data, size_t size, uint32 offset) {
+byte *VideoManager::decodeCopyBlock(byte *data, uint32 offset) {
 
 	byte *buf = new byte[256000];
 	memset(buf, 0, 256000);
@@ -90,7 +121,7 @@ byte *VideoManager::decodeCopyBlock(byte *data, size_t size, uint32 offset) {
 	// frames are encoded so that each block copy has a 5-byte header
 	// the first 3 bytes are the offset within the screen to which to
 	// copy the bytes. The 5th byte is the length of the block to copy.
-	while (pos + 5 < size) {
+	while (true) {
 		byte dest_lo = data[pos];
 		byte dest_mid = data[pos + 1];
 		byte dest_hi = data[pos + 2];
@@ -110,4 +141,57 @@ byte *VideoManager::decodeCopyBlock(byte *data, size_t size, uint32 offset) {
 
 	return buf; // Placeholder
 }
+
+byte *VideoManager::decodeRLE(byte *data, size_t size, uint32 offset) {
+	byte *buf = new byte[256000];
+	memset(buf, 0, 256000);
+	uint32 pos = offset;
+	// result = bytearray()
+	// pos = start_pos
+
+	uint32 outPos = 0;
+	while (outPos < 256000 && pos < size) {
+		byte countByte = data[pos];
+		pos += 1;
+
+		if ((countByte & 0xC0) == 0xC0) {
+			// RLE: count in lower 6 bits, next byte is value
+			uint32 count = countByte & 0x3F;
+			if (pos >= size) {
+				break;
+			}
+			byte value = data[pos];
+			pos += 1;
+			for (uint32 i = 0; i < count && outPos < 256000; i++) {
+				buf[outPos++] = value;
+			}
+		} else {
+			// Literal: count is 1, this byte is the value
+			buf[outPos++] = countByte;
+		}
+	}
+	return buf;
+
+	// while len(result) < max_size and pos < len(data):
+	//     count_byte = data[pos]
+	//     pos += 1
+
+	//     if (count_byte & 0xC0) == 0xC0:
+	//         # RLE: count in lower 6 bits, next byte is value
+	//         count = count_byte & 0x3F
+	//         if pos >= len(data):
+	//             break
+	//         value = data[pos]
+	//         pos += 1
+	//         result.extend([value] * count)
+	//     else:
+	//         # Literal: count is 1, this byte is the value
+	//         result.append(count_byte)
+
+	// # Pad to exact size
+	// if len(result) < max_size:
+	//     result.extend([0] * (max_size - len(result)))
+
+	// return bytes(result[:max_size])
+}
 } // End of namespace Pelrock
diff --git a/engines/pelrock/video/video.h b/engines/pelrock/video/video.h
index af4b23fe9dc..f9ac45b3a2e 100644
--- a/engines/pelrock/video/video.h
+++ b/engines/pelrock/video/video.h
@@ -24,6 +24,9 @@
 
 namespace Pelrock {
 
+static const uint32 frame0offset = 0x5000;
+static const uint32 frame1offset = 0x46000;
+static const uint32 chunkSize = 0x5000;
 static const uint32 offsets[] = {
 	0x64000,
 	0x69000,
@@ -43,7 +46,8 @@ public:
 private:
 	Graphics::Screen *_screen;
 	void loadPalette(Common::SeekableReadStream &stream);
-	byte *decodeCopyBlock(byte *data, size_t size, uint32 offset);
+	byte *decodeCopyBlock(byte *data, uint32 offset);
+    byte *decodeRLE(byte *data, size_t size, uint32 offset);
 };
 
 } // End of namespace Pelrock


Commit: 8e3b5c552687625f541555e61e7b7ef8cb5644f3
    https://github.com/scummvm/scummvm/commit/8e3b5c552687625f541555e61e7b7ef8cb5644f3
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:13+02:00

Commit Message:
PELROCK: Initial scaling algorithm

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/types.h
    engines/pelrock/video/video.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index f21d8d436c0..cef91e013e3 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -75,7 +75,7 @@ Common::String PelrockEngine::getGameId() const {
 	return _gameDescription->gameId;
 }
 
-Common::Array<Common::Array<Common::String> > wordWrap(Common::String text);
+Common::Array<Common::Array<Common::String>> wordWrap(Common::String text);
 
 Common::Error PelrockEngine::run() {
 	// Initialize 320x200 paletted graphics mode
@@ -165,6 +165,7 @@ Common::Error PelrockEngine::run() {
 void PelrockEngine::init() {
 	_res->loadCursors();
 	_res->loadInteractionIcons();
+	calculateScalingMasks();
 
 	_compositeBuffer = new byte[640 * 400];
 	_currentBackground = new byte[640 * 400];
@@ -180,9 +181,9 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		// setScreen(5, 0); //museum entrance
+		setScreen(6, 0); // museum entrance
 		// setScreen(13, 1); // restaurants kitchen
-		setScreen(2, 2); // hooker
+		// setScreen(2, 2); // hooker
 	}
 }
 
@@ -469,7 +470,108 @@ void PelrockEngine::renderText(Common::Array<Common::String> lines, int color, i
 }
 
 void PelrockEngine::drawAlfred(byte *buf) {
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, xAlfred, yAlfred - kAlfredFrameHeight, kAlfredFrameWidth, kAlfredFrameHeight, 255);
+
+	ScaleCalculation scale = calculateScaling(yAlfred, _room->_scaleParams);
+
+	int finalHeight = kAlfredFrameHeight - scale.scaleDown + scale.scaleUp;
+	if (finalHeight <= 0) {
+		finalHeight = 1;
+	}
+	float scaleFactor = static_cast<float>(finalHeight) / static_cast<float>(kAlfredFrameHeight);
+	int finalWidth = static_cast<int>(kAlfredFrameWidth * scaleFactor);
+	if (finalWidth <= 0) {
+		finalWidth = 1;
+	}
+	int scaleIndex = finalHeight - 1;
+	if (scaleIndex >= _heightScalingTable.size()) {
+		scaleIndex = _heightScalingTable.size() - 1;
+	}
+	if (scaleIndex < 0) {
+		scaleIndex = 0;
+	}
+	debug("Scaling Alfred frame to final size (%d x %d) from scale factor %.2f", finalWidth, finalHeight, scaleFactor);
+	int linesToSkip = kAlfredFrameHeight - finalHeight;
+
+	debug("lines to skip = %d, finalHeight = %d, finalWidth = %d for position (%d, %d)", linesToSkip, finalHeight, finalWidth, xAlfred, yAlfred);
+
+	if (linesToSkip <= 0) {
+		// No skipping needed, output all lines
+		drawSpriteToBuffer(_compositeBuffer, 640, buf, xAlfred, yAlfred - kAlfredFrameHeight, kAlfredFrameWidth, kAlfredFrameHeight, 255);
+	} else {
+
+		byte *scaledData = new byte[finalWidth * finalHeight];
+		int skipInterval = kAlfredFrameHeight / linesToSkip;
+		Common::Array<float> idealSkipPositions;
+		for (int i = 0; i < linesToSkip; i++) {
+			float idealPos = (i + 0.5f) * skipInterval;
+			idealSkipPositions.push_back(idealPos);
+		}
+
+		debug("Ideal skip positions:");
+		for (size_t i = 0; i < idealSkipPositions.size(); i++) {
+			debug("  %.2f", idealSkipPositions[i]);
+		}
+
+		debug("Height scaling table size =%d", _heightScalingTable.size());
+		Common::Array<int> tableSkipPositions;
+		for (int scanline = 0; scanline < kAlfredFrameHeight; scanline++) {
+			if (_heightScalingTable[scaleIndex][scanline] != 0) {
+				tableSkipPositions.push_back(scanline);
+			}
+		}
+
+		debug("Table skip positions:");
+		for (size_t i = 0; i < tableSkipPositions.size(); i++) {
+			debug("  %d", tableSkipPositions[i]);
+		}
+
+		Common::Array<int> skipTheseLines;
+		for (size_t i = 0; i < idealSkipPositions.size(); i++) {
+			float idealPos = idealSkipPositions[i];
+			int closest = -1;
+			int minDiff = INT32_MAX;
+			for (size_t j = 0; j < tableSkipPositions.size(); j++) {
+				int candidate = tableSkipPositions[j];
+				int diff = static_cast<int>(abs(candidate - idealPos));
+				if (diff < minDiff) {
+					minDiff = diff;
+					closest = candidate;
+				}
+			}
+			if (closest != -1) {
+				skipTheseLines.push_back(closest);
+			}
+			if (skipTheseLines.size() >= static_cast<size_t>(linesToSkip)) {
+				break;
+			}
+		}
+
+		int outY = 0;
+		for (int srcY = 0; srcY < kAlfredFrameHeight; srcY++) {
+			bool skipLine = false;
+			for (size_t skipIdx = 0; skipIdx < skipTheseLines.size(); ++skipIdx) {
+				if (skipTheseLines[skipIdx] == srcY) {
+					skipLine = true;
+					break;
+				}
+			}
+			if (!skipLine) {
+				for (int outX = 0; outX < finalWidth; outX++) {
+					int srcX = static_cast<int>(outX * kAlfredFrameWidth / finalWidth);
+					if (srcX >= kAlfredFrameWidth) {
+						srcX = kAlfredFrameWidth - 1;
+					}
+					int srcIndex = srcY * kAlfredFrameWidth + srcX;
+					int outIndex = outY * finalWidth + outX;
+					scaledData[outIndex] = buf[srcIndex];
+				}
+				outY++;
+			}
+		}
+		drawSpriteToBuffer(_compositeBuffer, 640, scaledData, xAlfred, yAlfred - finalHeight, finalWidth, finalHeight, 255);
+
+		delete[] scaledData;
+	}
 }
 
 void PelrockEngine::drawNextFrame(AnimSet *animSet) {
@@ -537,6 +639,126 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 	}
 }
 
+void PelrockEngine::calculateScalingMasks() {
+
+	//    for scale_factor in range(CHAR_WIDTH):
+	//     step = CHAR_WIDTH / (scale_factor + 1.0)
+	//     row = []
+	//     index = 0.0
+	//     source_pixel = 0
+
+	//     while index < CHAR_WIDTH:
+	//         row.append(source_pixel)
+	//         index += step
+	//         source_pixel += 1
+	//         if source_pixel >= CHAR_WIDTH:
+	//             source_pixel = CHAR_WIDTH - 1
+
+	//     # Pad to exactly CHAR_WIDTH entries
+	//     while len(row) < CHAR_WIDTH:
+	//         row.append(row[-1] if row else 0)
+	//     width_table.append(row[:CHAR_WIDTH])
+
+	for (int scaleFactor = 0; scaleFactor < kAlfredFrameWidth; scaleFactor++) {
+		float step = kAlfredFrameWidth / (scaleFactor + 1.0f);
+		Common::Array<int> row;
+		float index = 0.0f;
+		int sourcePixel = 0;
+
+		while (index < kAlfredFrameWidth) {
+			row.push_back(sourcePixel);
+			index += step;
+			sourcePixel += 1;
+			if (sourcePixel >= kAlfredFrameWidth) {
+				sourcePixel = kAlfredFrameWidth - 1;
+			}
+		}
+
+		// Pad to exactly CHAR_WIDTH entries
+		while (row.size() < kAlfredFrameWidth) {
+			row.push_back(row.empty() ? 0 : row[row.size() - 1]);
+		}
+
+		_widthScalingTable.push_back(row);
+	}
+
+	//  height_table = []
+	// for scale_factor in range(CHAR_HEIGHT):
+	//     step = CHAR_HEIGHT / (scale_factor + 1.0)
+	//     row = [0] * CHAR_HEIGHT  # Initialize all to 0
+
+	//     # Mark positions where we should keep/duplicate the scanline
+	//     position = step
+	//     counter = 1
+	//     while position < CHAR_HEIGHT:
+	//         idx = round(position)
+	//         if idx < CHAR_HEIGHT:
+	//             row[idx] = counter
+	//             counter += 1
+	//         position += step
+
+	//     height_table.append(row)
+	for (int scaleFactor = 0; scaleFactor < kAlfredFrameHeight; scaleFactor++) {
+		float step = kAlfredFrameHeight / (scaleFactor + 1.0f);
+		Common::Array<int> row;
+		row.resize(kAlfredFrameHeight, 0);
+		float position = step;
+		int counter = 1;
+		while (position < kAlfredFrameHeight) {
+			int idx = static_cast<int>(round(position));
+			if (idx < kAlfredFrameHeight) {
+				row[idx] = counter;
+				counter++;
+			}
+			position += step;
+		}
+		_heightScalingTable.push_back(row);
+	}
+}
+
+ScaleCalculation PelrockEngine::calculateScaling(int yPos, ScalingParams scalingParams) {
+	int scaleDown = 0;
+	int scaleUp = 0;
+	if (scalingParams.scaleMode == 0xFF) {
+		scaleDown = 0x5e;
+		scaleUp = 0x2f;
+	} else if (scalingParams.scaleMode == 0xFE) {
+		scaleDown = 0;
+		scaleUp = 0;
+	} else if (scalingParams.scaleMode == 0) {
+		if (scalingParams.yThreshold < yPos) {
+			scaleDown = 0;
+			scaleUp = 0;
+		} else {
+			if (scalingParams.scaleDivisor != 0) {
+				scaleDown = (scalingParams.yThreshold - yPos) / scalingParams.scaleDivisor;
+				scaleUp = scaleDown / 2;
+			} else {
+				scaleDown = 0;
+				scaleUp = 0;
+			}
+		}
+	} else {
+		scaleDown = 0;
+		scaleUp = 0;
+	}
+
+	int finalHeight = kAlfredFrameHeight - scaleDown + scaleUp;
+	if (finalHeight < 1)
+		finalHeight = 1;
+
+	int finalWidth = kAlfredFrameWidth * (finalHeight / kAlfredFrameHeight);
+	if (finalWidth < 1)
+		finalWidth = 1;
+
+	ScaleCalculation scaleCalc;
+	scaleCalc.scaledHeight = finalHeight;
+	scaleCalc.scaledWidth = finalWidth;
+	scaleCalc.scaleDown = scaleDown;
+	scaleCalc.scaleUp = scaleUp;
+	return scaleCalc;
+}
+
 int PelrockEngine::isHotspotUnder(int x, int y) {
 
 	for (size_t i = 0; i < _room->_currentRoomHotspots.size(); i++) {
@@ -1182,9 +1404,9 @@ int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
 	return wordLength;
 }
 
-Common::Array<Common::Array<Common::String> > wordWrap(Common::String text) {
+Common::Array<Common::Array<Common::String>> wordWrap(Common::String text) {
 
-	Common::Array<Common::Array<Common::String> > pages;
+	Common::Array<Common::Array<Common::String>> pages;
 	Common::Array<Common::String> currentPage;
 	Common::Array<Common::String> currentLine;
 	int charsRemaining = MAX_CHARS_PER_LINE;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 1ec613533ad..425b073bb9a 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -101,21 +101,27 @@ private:
 	void checkMouseClick(int x, int y);
 	void checkLongMouseClick(int x, int y);
 
+	void calculateScalingMasks();
+	ScaleCalculation calculateScaling(int yPos, ScalingParams scalingParams);
 
-	//walking
+	Common::Array<Common::Array<int>> _widthScalingTable;
+	Common::Array<Common::Array<int>> _heightScalingTable;
+
+	// walking
 	int _current_step = 0;
 	PathContext _currentContext;
 
-	//text display
+	// text display
 	byte _textColor = 0;
 	Common::Point _textPos;
-	Common::Array<Common::Array<Common::String> > _currentTextPages = Common::Array<Common::Array<Common::String> >();
+	Common::Array<Common::Array<Common::String>> _currentTextPages = Common::Array<Common::Array<Common::String>>();
 	int _currentTextPageIndex = 0;
 
-
 	// Alfred
-	int xAlfred = 319;
-	int yAlfred = 302;
+	// int xAlfred = 319;
+	// int yAlfred = 302;
+	int xAlfred = 264;
+	int yAlfred = 394;
 	int dirAlfred = 0;
 	int curAlfredFrame = 0;
 
@@ -126,9 +132,8 @@ private:
 	bool _isMouseDown = false;
 	bool _longClick = false;
 
-
 	byte *_currentBackground = nullptr; // Clean background - NEVER modified
-	byte *_compositeBuffer;   // Working composition buffer
+	byte *_compositeBuffer;             // Working composition buffer
 
 	bool _displayPopup = false;
 	int _popupX = 0;
@@ -146,7 +151,7 @@ private:
 	bool isNPCBTalking = false;
 
 	// JAVA
-	bool shouldPlayIntro = true;
+	bool shouldPlayIntro = false;
 	GameState stateGame = INTRO;
 	bool gameInitialized = false;
 	bool screenReady = false;
@@ -157,7 +162,6 @@ private:
 	// int whichScreen = 0;
 	// byte *pixelsShadows; // =new int[640*400];
 
-
 protected:
 	// Engine APIs
 	Common::Error run() override;
@@ -167,6 +171,7 @@ public:
 	AlfredState alfredState = ALFRED_IDLE;
 	ChronoManager *_chronoManager = nullptr;
 	VideoManager *_videoManager = nullptr;
+
 public:
 	PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc);
 	~PelrockEngine() override;
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 3b5a3c84deb..23b1e0beefa 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -22,6 +22,7 @@
 #include "pelrock/room.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/util.h"
+#include "room.h"
 
 namespace Pelrock {
 
@@ -172,7 +173,7 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 
 	Common::Array<HotSpot> staticHotspots = loadHotspots(roomFile, roomOffset);
 	Common::Array<Exit> exits = loadExits(roomFile, roomOffset);
-
+	ScalingParams scalingParams = loadScalingParams(roomFile, roomOffset);
 	Common::Array<WalkBox> walkboxes = loadWalkboxes(roomFile, roomOffset);
 
 	debug("total descriptions = %d, anims = %d, hotspots = %d", descriptions.size(), anims.size(), staticHotspots.size());
@@ -189,6 +190,7 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	_currentRoomExits = exits;
 	_currentRoomWalkboxes = walkboxes;
 	_currentRoomDescriptions = descriptions;
+	_scaleParams = scalingParams;
 	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
 		HotSpot hotspot = _currentRoomHotspots[i];
 		drawRect(g_engine->_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 200 + i);
@@ -287,6 +289,8 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(Common::File *roomFile, int ro
 	uint32_t walkbox_countOffset = pair10_data_offset + 0x213;
 	roomFile->seek(walkbox_countOffset, SEEK_SET);
 	byte walkbox_count = roomFile->readByte();
+
+
 	debug("Walkbox count: %d", walkbox_count);
 	uint32_t walkbox_offset = pair10_data_offset + 0x218;
 	Common::Array<WalkBox> walkboxes;
@@ -823,5 +827,19 @@ Common::Array<ConversationNode> RoomManager::loadConversations(Common::File *roo
 	return roots;
 }
 
-
+ScalingParams RoomManager::loadScalingParams(Common::File *roomFile, int roomOffset) {
+	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
+	roomFile->seek(pair10_offset_pos, SEEK_SET);
+	// roomFile->skip(4);
+	uint32_t pair10_data_offset = roomFile->readUint32LE();
+	uint32_t pair10_size = roomFile->readUint32LE();
+	uint32_t scalingParamsOffset = pair10_data_offset + 0x214;
+
+	roomFile->seek(scalingParamsOffset, SEEK_SET);
+	ScalingParams scalingParams;
+	scalingParams.yThreshold = roomFile->readSint16LE();
+	scalingParams.scaleDivisor = roomFile->readByte();
+	scalingParams.scaleMode = roomFile->readByte();
+	return scalingParams;
+}
 } // End of namespace Pelrock
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 10553071ea3..db12aceb27b 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -45,6 +45,7 @@ public:
 	Common::Array<Description> _currentRoomDescriptions;
 	Common::Array<ConversationNode> _currentRoomConversations;
 	TalkingAnimHeader _talkingAnimHeader;
+	ScalingParams _scaleParams;
 
 private:
 	Common::Array<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
@@ -58,6 +59,7 @@ private:
 	Common::Array<ConversationElement> parseConversationElements(const byte *convData, uint32 size);
 	Common::Array<ConversationNode> buildTreeStructure(const Common::Array<ConversationElement> &elements);
 	Common::Array<ConversationNode> loadConversations(Common::File *roomFile, int roomOffset, uint32_t startPos);
+	ScalingParams loadScalingParams(Common::File *roomFile, int roomOffset);
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 6fd46cfddfe..99ded11b46e 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -262,6 +262,19 @@ struct WalkBox {
 	byte flags;
 };
 
+struct ScalingParams {
+	int16 yThreshold;
+	byte scaleDivisor;
+	byte scaleMode;
+};
+
+struct ScaleCalculation {
+	int scaledWidth;
+	int scaledHeight;
+	int scaleUp;
+	int scaleDown;
+};
+
 enum GameState {
 	GAME = 100,
 	MENU = 101,
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index 9ebcc585608..93c5fd4a17e 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -146,9 +146,6 @@ byte *VideoManager::decodeRLE(byte *data, size_t size, uint32 offset) {
 	byte *buf = new byte[256000];
 	memset(buf, 0, 256000);
 	uint32 pos = offset;
-	// result = bytearray()
-	// pos = start_pos
-
 	uint32 outPos = 0;
 	while (outPos < 256000 && pos < size) {
 		byte countByte = data[pos];
@@ -171,27 +168,5 @@ byte *VideoManager::decodeRLE(byte *data, size_t size, uint32 offset) {
 		}
 	}
 	return buf;
-
-	// while len(result) < max_size and pos < len(data):
-	//     count_byte = data[pos]
-	//     pos += 1
-
-	//     if (count_byte & 0xC0) == 0xC0:
-	//         # RLE: count in lower 6 bits, next byte is value
-	//         count = count_byte & 0x3F
-	//         if pos >= len(data):
-	//             break
-	//         value = data[pos]
-	//         pos += 1
-	//         result.extend([value] * count)
-	//     else:
-	//         # Literal: count is 1, this byte is the value
-	//         result.append(count_byte)
-
-	// # Pad to exact size
-	// if len(result) < max_size:
-	//     result.extend([0] * (max_size - len(result)))
-
-	// return bytes(result[:max_size])
 }
 } // End of namespace Pelrock


Commit: 79fdb25fcf772bc9404f6606e2980e777ee00fef
    https://github.com/scummvm/scummvm/commit/79fdb25fcf772bc9404f6606e2980e777ee00fef
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:13+02:00

Commit Message:
PELROCK: Initial scaling algorithm

Changed paths:
    engines/pelrock/offsets.h
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/util.cpp
    engines/pelrock/util.h


diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 577613b2d31..15dc427f85e 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -36,8 +36,8 @@ namespace Pelrock {
     static const uint32_t kBalloonFramesOffset = 2176936;
     static const uint32_t kBalloonFramesSize = 24950;
 
-    static const uint32_t ALFRED7_ALFRED_COMB_R = 67764;
-    static const uint32_t ALFRED7_ALFRED_COMB_L = 88404;
+    static const uint32_t ALFRED7_ALFRED_COMB_R = 67768;
+    static const uint32_t ALFRED7_ALFRED_COMB_L = 88408;
 
 
 
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index c131921fc27..117fa5f5a5e 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -87,7 +87,7 @@ void ResourceManager::loadInteractionIcons() {
 
 	byte *raw = new byte[compressedSize];
 	alfred7File.read(raw, compressedSize);
-	rleDecompress(raw, compressedSize, 0, compressedSize, &_popUpBalloon);
+	rleDecompress(raw, compressedSize, 0, totalBalloonSize, &_popUpBalloon);
 
 	delete[] raw;
 
@@ -122,7 +122,7 @@ void ResourceManager::loadAlfredAnims() {
 	int index3 = 0;
 	uint32_t capacity = 3060 * 102;
 	unsigned char *pic = new unsigned char[capacity];
-	rleDecompress(bufferFile, alfred3Size, 0, alfred3Size, &pic);
+	rleDecompress(bufferFile, alfred3Size, 0, capacity, &pic);
 
 	int frameSize = kAlfredFrameHeight * kAlfredFrameWidth;
 	for (int i = 0; i < 4; i++) {
@@ -178,18 +178,18 @@ void ResourceManager::loadAlfredAnims() {
 		return;
 	}
 	int spriteMapSize = frameSize * 11;
+
 	byte *alfredCombRightRaw;
 	size_t alfredCombRightSize;
 
 	readUntilBuda(&alfred7, ALFRED7_ALFRED_COMB_R, alfredCombRightRaw, alfredCombRightSize);
 	byte *alfredCombRight = nullptr;
-	rleDecompress(alfredCombRightRaw, alfredCombRightSize, 0, alfredCombRightSize, &alfredCombRight);
+	rleDecompress(alfredCombRightRaw, alfredCombRightSize, 0, spriteMapSize, &alfredCombRight);
 
 	alfredCombFrames[0] = new byte *[11];
 	alfredCombFrames[1] = new byte *[11];
 
 	for (int i = 0; i < 11; i++) {
-
 		alfredCombFrames[0][i] = new byte[frameSize];
 		extractSingleFrame(alfredCombRight, alfredCombFrames[0][i], i, kAlfredFrameWidth, kAlfredFrameHeight);
 	}
@@ -198,8 +198,12 @@ void ResourceManager::loadAlfredAnims() {
 	size_t alfredCombLeftSize;
 	readUntilBuda(&alfred7, ALFRED7_ALFRED_COMB_L, alfredCombLeftRaw, alfredCombLeftSize);
 	byte *alfredCombLeft = nullptr;
-	rleDecompress(alfredCombLeftRaw, alfredCombLeftSize, 0, spriteMapSize, &alfredCombLeft);
+	size_t outSize = rleDecompress(alfredCombLeftRaw, alfredCombLeftSize, 0, spriteMapSize, &alfredCombLeft);
+	debug("Sprite map size: %d, %d, %d", spriteMapSize, alfredCombLeftSize, outSize);
 
+	for (int i = 0; i < 11; i++) {
+		debug("Extracting comb left frame %d", i);
+	}
 	for (int i = 0; i < 11; i++) {
 		alfredCombFrames[1][i] = new byte[frameSize];
 		extractSingleFrame(alfredCombLeft, alfredCombFrames[1][i], i, kAlfredFrameWidth, kAlfredFrameHeight);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 23b1e0beefa..f79e999bf59 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -27,6 +27,7 @@
 namespace Pelrock {
 
 RoomManager::RoomManager() {
+	pixelsShadows = new byte[640*400] { 0 };
 }
 
 RoomManager::~RoomManager() {
@@ -36,6 +37,7 @@ RoomManager::~RoomManager() {
 	// delete[] _currentRoomWalkboxes;
 	// delete[] _currentRoomDescriptions;
 	// delete[] _currentRoomConversations;
+	delete[] pixelsShadows;
 }
 
 void RoomManager::getPalette(Common::File *roomFile, int roomOffset, byte *palette) {
@@ -75,7 +77,7 @@ void RoomManager::getBackground(Common::File *roomFile, int roomOffset, byte *ba
 			roomFile->seek(offset, SEEK_SET);
 			roomFile->read(data, size);
 			uint8_t *block_data = NULL;
-			size_t block_size = rleDecompress(data, size, 0, size, &block_data);
+			size_t block_size = rleDecompress(data, size, 0, 640 * 400, &block_data);
 
 			memcpy(background + combined_size, block_data, block_size);
 			combined_size += block_size + 1;
@@ -212,9 +214,9 @@ Common::Array<AnimSet> RoomManager::loadRoomAnimations(Common::File *roomFile, i
 	roomFile->seek(offset, SEEK_SET);
 	roomFile->read(data, size);
 
-	unsigned char *pic = new byte[10000 * 10000];
+	unsigned char *pic = nullptr;
 	if (offset > 0 && size > 0) {
-		rleDecompress(data, size, 0, size, &pic);
+		rleDecompress(data, size, 0, size, &pic, true);
 	} else {
 		return Common::Array<AnimSet>();
 	}
@@ -842,4 +844,29 @@ ScalingParams RoomManager::loadScalingParams(Common::File *roomFile, int roomOff
 	scalingParams.scaleMode = roomFile->readByte();
 	return scalingParams;
 }
+
+static uint32 readUint24(Common::ReadStream &stream) {
+	uint32 value = stream.readUint16LE();
+	value |= stream.readByte() << 16;
+	return value;
+}
+
+byte *RoomManager::loadShadowMap(int roomNumber) {
+	Common::File shadowMapFile;
+	if (!shadowMapFile.open("ALFRED.5")) {
+		error("Couldnt find file ALFRED.5");
+	}
+
+	uint32 entryOffset = roomNumber * 6;
+
+	shadowMapFile.seek(entryOffset, SEEK_SET);
+	uint32 shadowOffset = readUint24(shadowMapFile);
+
+	shadowMapFile.seek(shadowOffset, SEEK_SET);
+
+	// pixelsShadows = rleDecompress(
+
+
+	return nullptr;
+}
 } // End of namespace Pelrock
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index db12aceb27b..04237eb3c84 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -46,6 +46,7 @@ public:
 	Common::Array<ConversationNode> _currentRoomConversations;
 	TalkingAnimHeader _talkingAnimHeader;
 	ScalingParams _scaleParams;
+	byte *pixelsShadows = nullptr;
 
 private:
 	Common::Array<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
@@ -60,6 +61,7 @@ private:
 	Common::Array<ConversationNode> buildTreeStructure(const Common::Array<ConversationElement> &elements);
 	Common::Array<ConversationNode> loadConversations(Common::File *roomFile, int roomOffset, uint32_t startPos);
 	ScalingParams loadScalingParams(Common::File *roomFile, int roomOffset);
+	byte *loadShadowMap(int roomNumber);
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index b3633c300ea..d37fa8fe089 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -41,38 +41,72 @@ void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color
 	surface->drawLine(x + w, y, x + w, y + h, color);
 }
 
-size_t rleDecompress(const uint8_t *data, size_t data_size, uint32_t offset, uint32_t size, uint8_t **out_data) {
-	// Check for uncompressed markers
-	if (size == 0x8000 || size == 0x6800) {
-		*out_data = (uint8_t *)malloc(size);
-		memcpy(*out_data, data + offset, size);
-		return size;
+size_t rleDecompress(
+	const uint8_t *input,
+	size_t inputSize,
+	uint32_t offset,
+	uint32_t expectedSize,
+	uint8_t **out_data,
+    bool untilBuda
+) {
+	// // Check for uncompressed markers
+	if (inputSize == 0x8000 || inputSize == 0x6800) {
+		*out_data = (uint8_t *)malloc(inputSize);
+		memcpy(*out_data, input + offset, inputSize);
+		return inputSize;
 	}
 
 	// RLE compressed
-	*out_data = (uint8_t *)malloc(EXPECTED_SIZE * 2); // Allocate enough space
+	size_t bufferCapacity;
 	size_t result_size = 0;
-
 	uint32_t pos = offset;
-	uint32_t end = offset + size;
 
-	while (pos + 2 <= end && pos + 2 <= data_size) {
+	if (untilBuda) {
+		// Dynamic allocation mode - grow buffer as needed
+		bufferCapacity = 4096;
+		*out_data = (uint8_t *)malloc(bufferCapacity);
+		if (!*out_data)
+			return 0;
+	} else {
+		// Fixed size mode
+		bufferCapacity = expectedSize;
+		*out_data = (uint8_t *)malloc(expectedSize);
+		if (!*out_data)
+			return 0;
+	}
+
+	while (pos + 2 <= inputSize) {
 		// Check for BUDA marker
-		if (pos + 4 <= data_size &&
-			data[pos] == 'B' && data[pos + 1] == 'U' &&
-			data[pos + 2] == 'D' && data[pos + 3] == 'A') {
+		if (pos + 4 <= inputSize &&
+			input[pos] == 'B' && input[pos + 1] == 'U' &&
+			input[pos + 2] == 'D' && input[pos + 3] == 'A') {
 			break;
 		}
 
-		uint8_t count = data[pos];
-		uint8_t value = data[pos + 1];
+		uint8_t count = input[pos];
+		uint8_t value = input[pos + 1];
 
 		for (int i = 0; i < count; i++) {
+			 // If in untilBuda mode, grow buffer as needed
+            if (untilBuda && result_size >= bufferCapacity) {
+                bufferCapacity *= 2;
+                uint8_t *newBuf = (uint8_t *)realloc(*out_data, bufferCapacity);
+                if (!newBuf) {
+                    free(*out_data);
+                    *out_data = nullptr;
+                    return 0;
+                }
+                *out_data = newBuf;
+            }
 			// debug("Pos = %zu, writing value %02X", result_size, value);
 			(*out_data)[result_size++] = value;
 		}
 
 		pos += 2;
+    	// In fixed size mode, stop when we reach expected size
+        if (!untilBuda && result_size >= expectedSize) {
+            break;
+        }
 	}
 
 	return result_size;
@@ -134,6 +168,7 @@ void extractSingleFrame(byte *source, byte *dest, int frameIndex, int frameWidth
 	for (int y = 0; y < frameHeight; y++) {
 		for (int x = 0; x < frameWidth; x++) {
 			unsigned int src_pos = (frameIndex * frameHeight * frameWidth) + (y * frameWidth) + x;
+			// debug("Copying pixel from source pos %u to dest pos %d", src_pos, y * frameWidth + x);
 			dest[y * frameWidth + x] = source[src_pos];
 		}
 	}
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index ee91d2e394e..44b61c094c5 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -31,7 +31,7 @@
 namespace Pelrock {
 
 const int EXPECTED_SIZE = 640 * 400;
-size_t rleDecompress(const uint8_t *data, size_t data_size, uint32_t offset, uint32_t size, uint8_t **out_data);
+size_t rleDecompress(const uint8_t *data, size_t data_size, uint32_t offset, uint32_t size, uint8_t **out_data, bool untilBuda = true);
 void readUntilBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *&buffer, size_t &outSize);
 void drawSpriteToBuffer(byte *buffer, int bufferWidth,
 						byte *sprite, int x, int y,


Commit: 2df4ff4791bb646355af01432631116a758b6b27
    https://github.com/scummvm/scummvm/commit/2df4ff4791bb646355af01432631116a758b6b27
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:14+02:00

Commit Message:
PELROCK: Loads shadow map

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index cef91e013e3..0592b3e05d2 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -124,6 +124,9 @@ Common::Error PelrockEngine::run() {
 				case Common::KEYCODE_i:
 					alfredState = ALFRED_INTERACTING;
 					break;
+				case Common::KEYCODE_z:
+					showShadows = !showShadows;
+					break;
 				default:
 					break;
 				}
@@ -441,7 +444,13 @@ void PelrockEngine::frames() {
 			if (_curWalkTarget.y + 2 < 400)
 				_screen->setPixel(_curWalkTarget.x, _curWalkTarget.y + 2, 100);
 		}
+
+		if(showShadows) {
+			memcpy(_screen->getPixels(), _room->_pixelsShadows, 640 * 400);
+		}
+
 		_screen->markAllDirty();
+
 		// _screen->update();
 	}
 }
@@ -1516,7 +1525,7 @@ void PelrockEngine::setScreen(int number, int dir) {
 		}
 	}
 
-	_room->loadRoomMetadata(&roomFile, roomOffset);
+	_room->loadRoomMetadata(&roomFile, number);
 	_room->loadRoomTalkingAnimations(number);
 
 	_screen->markAllDirty();
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 425b073bb9a..a3c93bb0036 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -150,6 +150,8 @@ private:
 	uint16 whichNPCTalking = 0;
 	bool isNPCBTalking = false;
 
+	bool showShadows = false;
+
 	// JAVA
 	bool shouldPlayIntro = false;
 	GameState stateGame = INTRO;
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index f79e999bf59..35b2f1c45a9 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -27,7 +27,7 @@
 namespace Pelrock {
 
 RoomManager::RoomManager() {
-	pixelsShadows = new byte[640*400] { 0 };
+	_pixelsShadows = new byte[640*400] { 0 };
 }
 
 RoomManager::~RoomManager() {
@@ -37,7 +37,7 @@ RoomManager::~RoomManager() {
 	// delete[] _currentRoomWalkboxes;
 	// delete[] _currentRoomDescriptions;
 	// delete[] _currentRoomConversations;
-	delete[] pixelsShadows;
+	delete[] _pixelsShadows;
 }
 
 void RoomManager::getPalette(Common::File *roomFile, int roomOffset, byte *palette) {
@@ -142,9 +142,10 @@ Common::Array<HotSpot> RoomManager::loadHotspots(Common::File *roomFile, int roo
 	// roomFile->seek(hover_areas_start, SEEK_SET);
 }
 
-void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
+void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	uint32_t outPos = 0;
 
+	int roomOffset = roomNumber * kRoomStructSize;
 	Common::Array<Description> descriptions = loadRoomDescriptions(roomFile, roomOffset, outPos);
 	debug("After decsriptions, position is %d", outPos);
 	Common::Array<ConversationNode> roots = loadConversations(roomFile, roomOffset, outPos);
@@ -185,6 +186,9 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 		hotspots.push_back(hotspot);
 	}
 
+
+	byte *shadows = loadShadowMap(roomNumber);
+
 	int walkboxCount = 0;
 
 	_currentRoomAnims = anims;
@@ -193,6 +197,7 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 	_currentRoomWalkboxes = walkboxes;
 	_currentRoomDescriptions = descriptions;
 	_scaleParams = scalingParams;
+	_pixelsShadows = shadows;
 	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
 		HotSpot hotspot = _currentRoomHotspots[i];
 		drawRect(g_engine->_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 200 + i);
@@ -203,6 +208,7 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomOffset) {
 		// drawRect(_screen, exit.x, exit.y, exit.w, exit.h, 100 + i);
 	}
 }
+
 Common::Array<AnimSet> RoomManager::loadRoomAnimations(Common::File *roomFile, int roomOffset) {
 	uint32_t pair_offset = roomOffset + (8 * 8);
 	// debug("Sprite pair offset position: %d", pair_offset);
@@ -859,14 +865,20 @@ byte *RoomManager::loadShadowMap(int roomNumber) {
 
 	uint32 entryOffset = roomNumber * 6;
 
+	debug("Loading shadow map for room %d at offset %d", roomNumber, entryOffset);
+
 	shadowMapFile.seek(entryOffset, SEEK_SET);
 	uint32 shadowOffset = readUint24(shadowMapFile);
 
-	shadowMapFile.seek(shadowOffset, SEEK_SET);
+	byte *compressed = nullptr;
+	size_t compressedSize = 0;
+	readUntilBuda(&shadowMapFile, shadowOffset, compressed, compressedSize);
 
-	// pixelsShadows = rleDecompress(
-
-
-	return nullptr;
+	debug("Shadow map compressed size: %zu", compressedSize);
+	byte *shadows = nullptr;
+	size_t output = rleDecompress(compressed, compressedSize, 0, 640 * 400, &shadows);
+	free(compressed);
+	return shadows;
 }
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 04237eb3c84..8044a194ba7 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -33,7 +33,7 @@ class RoomManager {
 public:
 	RoomManager();
 	~RoomManager();
-	void loadRoomMetadata(Common::File *roomFile, int roomOffset);
+	void loadRoomMetadata(Common::File *roomFile, int roomNumber);
 	void loadRoomTalkingAnimations(int roomNumber);
 	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
@@ -46,7 +46,7 @@ public:
 	Common::Array<ConversationNode> _currentRoomConversations;
 	TalkingAnimHeader _talkingAnimHeader;
 	ScalingParams _scaleParams;
-	byte *pixelsShadows = nullptr;
+	byte *_pixelsShadows = nullptr;
 
 private:
 	Common::Array<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);


Commit: 3240f59bd457ba7f68ec50a24ad20bf034f5d3ef
    https://github.com/scummvm/scummvm/commit/3240f59bd457ba7f68ec50a24ad20bf034f5d3ef
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:14+02:00

Commit Message:
PELROCK: Character shading

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp
    engines/pelrock/room.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 0592b3e05d2..dd96f27459a 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -185,8 +185,8 @@ void PelrockEngine::init() {
 		gameInitialized = true;
 		loadAnims();
 		setScreen(6, 0); // museum entrance
-		// setScreen(13, 1); // restaurants kitchen
-		// setScreen(2, 2); // hooker
+						 // setScreen(13, 1); // restaurants kitchen
+						 // setScreen(2, 2); // hooker
 	}
 }
 
@@ -445,7 +445,7 @@ void PelrockEngine::frames() {
 				_screen->setPixel(_curWalkTarget.x, _curWalkTarget.y + 2, 100);
 		}
 
-		if(showShadows) {
+		if (showShadows) {
 			memcpy(_screen->getPixels(), _room->_pixelsShadows, 640 * 400);
 		}
 
@@ -503,12 +503,12 @@ void PelrockEngine::drawAlfred(byte *buf) {
 
 	debug("lines to skip = %d, finalHeight = %d, finalWidth = %d for position (%d, %d)", linesToSkip, finalHeight, finalWidth, xAlfred, yAlfred);
 
-	if (linesToSkip <= 0) {
-		// No skipping needed, output all lines
-		drawSpriteToBuffer(_compositeBuffer, 640, buf, xAlfred, yAlfred - kAlfredFrameHeight, kAlfredFrameWidth, kAlfredFrameHeight, 255);
-	} else {
+	int shadowPos = yAlfred; // - finalHeight;
+	bool shadeCharacter = _room->_pixelsShadows[shadowPos * 640 + xAlfred] != 0xFF;
+
+	byte *finalBuf = new byte[finalWidth * finalHeight];
 
-		byte *scaledData = new byte[finalWidth * finalHeight];
+	if (linesToSkip > 0) {
 		int skipInterval = kAlfredFrameHeight / linesToSkip;
 		Common::Array<float> idealSkipPositions;
 		for (int i = 0; i < linesToSkip; i++) {
@@ -572,15 +572,25 @@ void PelrockEngine::drawAlfred(byte *buf) {
 					}
 					int srcIndex = srcY * kAlfredFrameWidth + srcX;
 					int outIndex = outY * finalWidth + outX;
-					scaledData[outIndex] = buf[srcIndex];
+					finalBuf[outIndex] = buf[srcIndex];
 				}
 				outY++;
 			}
 		}
-		drawSpriteToBuffer(_compositeBuffer, 640, scaledData, xAlfred, yAlfred - finalHeight, finalWidth, finalHeight, 255);
+	} else {
+		Common::copy(buf, buf + (kAlfredFrameWidth * kAlfredFrameHeight), finalBuf);
+	}
 
-		delete[] scaledData;
+	if (shadeCharacter) {
+		for (int i = 0; i < finalWidth * finalHeight; i++) {
+			if (finalBuf[i] != 255) {
+				finalBuf[i] = _room->alfredRemap[finalBuf[i]];
+			}
+		}
 	}
+
+	drawSpriteToBuffer(_compositeBuffer, 640, finalBuf, xAlfred, yAlfred - finalHeight, finalWidth, finalHeight, 255);
+	delete[] finalBuf;
 }
 
 void PelrockEngine::drawNextFrame(AnimSet *animSet) {
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 35b2f1c45a9..6ab3b08acae 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -188,6 +188,7 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 
 
 	byte *shadows = loadShadowMap(roomNumber);
+	loadRemaps(roomNumber);
 
 	int walkboxCount = 0;
 
@@ -878,7 +879,23 @@ byte *RoomManager::loadShadowMap(int roomNumber) {
 	byte *shadows = nullptr;
 	size_t output = rleDecompress(compressed, compressedSize, 0, 640 * 400, &shadows);
 	free(compressed);
+	shadowMapFile.close();
 	return shadows;
 }
 
+void RoomManager::loadRemaps(int roomNumber) {
+
+	Common::File remapFile;
+	if (!remapFile.open("ALFRED.9")) {
+		error("Couldnt find file ALFRED.9");
+	}
+
+	uint32 remapOffset = 0x200 + (roomNumber * 1024);
+
+	remapFile.seek(remapOffset, SEEK_SET);
+	remapFile.read(alfredRemap, 256);
+	remapFile.read(overlayRemap, 256);
+	remapFile.close();
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 8044a194ba7..688357a92a3 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -47,6 +47,8 @@ public:
 	TalkingAnimHeader _talkingAnimHeader;
 	ScalingParams _scaleParams;
 	byte *_pixelsShadows = nullptr;
+	byte alfredRemap[256];
+	byte overlayRemap[256];
 
 private:
 	Common::Array<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
@@ -62,6 +64,7 @@ private:
 	Common::Array<ConversationNode> loadConversations(Common::File *roomFile, int roomOffset, uint32_t startPos);
 	ScalingParams loadScalingParams(Common::File *roomFile, int roomOffset);
 	byte *loadShadowMap(int roomNumber);
+	void loadRemaps(int roomNumber);
 };
 
 } // End of namespace Pelrock


Commit: 58faeb41d2f033a425677b110ebe20d8a1efee61
    https://github.com/scummvm/scummvm/commit/58faeb41d2f033a425677b110ebe20d8a1efee61
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:14+02:00

Commit Message:
PELROCK: Dialog choice overlay

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index dd96f27459a..98830523ea1 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -223,6 +223,18 @@ void PelrockEngine::talk(byte object) {
 	// }
 }
 
+void PelrockEngine::displayChoices(Common::Array<Common::String> choices, byte *compositeBuffer) {
+	int overlayHeight = choices.size() * kChoiceHeight + 2;
+	int overlayY = 400 - overlayHeight;
+	debug("Displaying choices overlay at y=%d, height=%d", overlayY, overlayHeight);
+	for (int x = 0; x < 640; x++) {
+		for (int y = overlayY; y < 400; y++) {
+			int index = y * 640 + x;
+			compositeBuffer[index] = _room->overlayRemap[compositeBuffer[index]];
+		}
+	}
+}
+
 byte *PelrockEngine::grabBackgroundSlice(int x, int y, int w, int h) {
 	byte *bg = new byte[w * h];
 	for (int j = 0; j < w; j++) {
@@ -397,6 +409,12 @@ void PelrockEngine::frames() {
 				_currentPopupFrame = 0;
 		}
 
+		Common::Array<Common::String> testChoices;
+		testChoices.push_back("First choice");
+		testChoices.push_back("Second choice");
+		testChoices.push_back("Third choice");
+		displayChoices(testChoices, _compositeBuffer);
+
 		memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
 
 		if (alfredState != ALFRED_WALKING && !_currentTextPages.empty()) {
@@ -516,12 +534,12 @@ void PelrockEngine::drawAlfred(byte *buf) {
 			idealSkipPositions.push_back(idealPos);
 		}
 
-		debug("Ideal skip positions:");
+		// debug("Ideal skip positions:");
 		for (size_t i = 0; i < idealSkipPositions.size(); i++) {
 			debug("  %.2f", idealSkipPositions[i]);
 		}
 
-		debug("Height scaling table size =%d", _heightScalingTable.size());
+		// debug("Height scaling table size =%d", _heightScalingTable.size());
 		Common::Array<int> tableSkipPositions;
 		for (int scanline = 0; scanline < kAlfredFrameHeight; scanline++) {
 			if (_heightScalingTable[scaleIndex][scanline] != 0) {
@@ -529,10 +547,10 @@ void PelrockEngine::drawAlfred(byte *buf) {
 			}
 		}
 
-		debug("Table skip positions:");
-		for (size_t i = 0; i < tableSkipPositions.size(); i++) {
-			debug("  %d", tableSkipPositions[i]);
-		}
+		// debug("Table skip positions:");
+		// for (size_t i = 0; i < tableSkipPositions.size(); i++) {
+		// 	debug("  %d", tableSkipPositions[i]);
+		// }
 
 		Common::Array<int> skipTheseLines;
 		for (size_t i = 0; i < idealSkipPositions.size(); i++) {
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index a3c93bb0036..a99f60a6e5e 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -75,6 +75,8 @@ private:
 									 MovementStep *movement_buffer);
 
 	void talk(byte object);
+	void displayChoices(Common::Array<Common::String> choices, byte *compositeBuffer);
+
 	byte *grabBackgroundSlice(int x, int y, int w, int h);
 	void putBackgroundSlice(int x, int y, int w, int h, byte *slice);
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 99ded11b46e..587bcbf0301 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -66,7 +66,7 @@ const int kVerbIconPadding = 20;
 const int kAlfredFrameWidth = 51;
 const int kAlfredFrameHeight = 102;
 
-
+const int kChoiceHeight = 16; // Height of each choice line in pixels
 
 enum AlfredState {
 	ALFRED_IDLE,


Commit: a7e5c9cf2ad5ebc3da3411949f5f24e374974d27
    https://github.com/scummvm/scummvm/commit/a7e5c9cf2ad5ebc3da3411949f5f24e374974d27
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:15+02:00

Commit Message:
PELROCK: Loads room names

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/util.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 98830523ea1..653ce77042c 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -49,8 +49,6 @@ PelrockEngine::PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc) :
 																				 _gameDescription(gameDesc), _randomSource("Pelrock") {
 	g_engine = this;
 	_chronoManager = new ChronoManager();
-	_room = new RoomManager();
-	_res = new ResourceManager();
 }
 
 PelrockEngine::~PelrockEngine() {
@@ -82,6 +80,8 @@ Common::Error PelrockEngine::run() {
 	initGraphics(640, 400);
 	_screen = new Graphics::Screen();
 	_videoManager = new VideoManager(_screen);
+	_room = new RoomManager();
+	_res = new ResourceManager();
 
 	// Set the engine's debugger console
 	setDebugger(new Console());
@@ -226,7 +226,7 @@ void PelrockEngine::talk(byte object) {
 void PelrockEngine::displayChoices(Common::Array<Common::String> choices, byte *compositeBuffer) {
 	int overlayHeight = choices.size() * kChoiceHeight + 2;
 	int overlayY = 400 - overlayHeight;
-	debug("Displaying choices overlay at y=%d, height=%d", overlayY, overlayHeight);
+	// debug("Displaying choices overlay at y=%d, height=%d", overlayY, overlayHeight);
 	for (int x = 0; x < 640; x++) {
 		for (int y = overlayY; y < 400; y++) {
 			int index = y * 640 + x;
@@ -516,10 +516,10 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	if (scaleIndex < 0) {
 		scaleIndex = 0;
 	}
-	debug("Scaling Alfred frame to final size (%d x %d) from scale factor %.2f", finalWidth, finalHeight, scaleFactor);
+	// debug("Scaling Alfred frame to final size (%d x %d) from scale factor %.2f", finalWidth, finalHeight, scaleFactor);
 	int linesToSkip = kAlfredFrameHeight - finalHeight;
 
-	debug("lines to skip = %d, finalHeight = %d, finalWidth = %d for position (%d, %d)", linesToSkip, finalHeight, finalWidth, xAlfred, yAlfred);
+	// debug("lines to skip = %d, finalHeight = %d, finalWidth = %d for position (%d, %d)", linesToSkip, finalHeight, finalWidth, xAlfred, yAlfred);
 
 	int shadowPos = yAlfred; // - finalHeight;
 	bool shadeCharacter = _room->_pixelsShadows[shadowPos * 640 + xAlfred] != 0xFF;
@@ -534,11 +534,6 @@ void PelrockEngine::drawAlfred(byte *buf) {
 			idealSkipPositions.push_back(idealPos);
 		}
 
-		// debug("Ideal skip positions:");
-		for (size_t i = 0; i < idealSkipPositions.size(); i++) {
-			debug("  %.2f", idealSkipPositions[i]);
-		}
-
 		// debug("Height scaling table size =%d", _heightScalingTable.size());
 		Common::Array<int> tableSkipPositions;
 		for (int scanline = 0; scanline < kAlfredFrameHeight; scanline++) {
@@ -875,9 +870,6 @@ void PelrockEngine::walkTo(int x, int y) {
 	// debug("================\n");
 
 	// debug("Walkbox path (%d boxes): ", context.path_length);
-	for (int i = 0; i < context.path_length && context.path_buffer[i] != PATH_END; i++) {
-		debug("%d ", context.path_buffer[i]);
-	}
 
 	// debug("Movement steps (%d steps):\n", context.movement_count);
 	for (int i = 0; i < context.movement_count; i++) {
@@ -897,9 +889,6 @@ void PelrockEngine::walkTo(int x, int y) {
 	}
 
 	// debug("\nCompressed path (%d bytes): ", context.compressed_length);
-	for (int i = 0; i < context.compressed_length; i++) {
-		debug("%02X ", context.compressed_path[i]);
-	}
 
 	// if (x > xAlfred) {
 	// 	dirAlfred = RIGHT;
@@ -923,9 +912,6 @@ bool PelrockEngine::pathFind(int x, int y, PathContext *context) {
 	if (context->movement_buffer == NULL) {
 		context->movement_buffer = (MovementStep *)malloc(MAX_MOVEMENT_STEPS * sizeof(MovementStep));
 	}
-	// if (context->compressed_path == NULL) {
-	//     context->compressed_path = (uint8_t*)malloc(MAX_COMPRESSED_PATH);
-	// }
 
 	int startX = xAlfred;
 	int startY = yAlfred;
@@ -1212,11 +1198,8 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 
 	if (_displayPopup) {
 		Common::Array<VerbIcons> actions = availableActions(_currentHotspot);
-		for (int i = 0; i < actions.size(); i++) {
-			debug("Available action %d at index %d", actions[i], i);
-		}
+
 		Common::Rect lookRect = Common::Rect(_popupX + 20, _popupY + 20, _popupX + 20 + kVerbIconWidth, _popupY + 20 + kVerbIconHeight);
-		// debug("Look rect: x=%d, y=%d, w=%d, h=%d", lookRect.left, lookRect.top, lookRect, lookRect.h);
 		if (lookRect.contains(x, y)) {
 			debug("Look action clicked");
 			walkTo(_currentHotspot->x, _currentHotspot->y);
@@ -1226,7 +1209,6 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 		}
 		for (int i = 1; i < actions.size(); i++) {
 
-			// debug("Checking action %d at index %d for mouse click = %d, %d", actions[i], i, x, y);
 			int x = _popupX + 20 + (i * (kVerbIconWidth + 2));
 			int y = _popupY + 20;
 			Common::Rect actionRect = Common::Rect(x, y, x + kVerbIconWidth, y + kVerbIconHeight);
@@ -1523,6 +1505,7 @@ Common::Array<Common::Array<Common::String>> wordWrap(Common::String text) {
 void PelrockEngine::setScreen(int number, int dir) {
 
 	Common::File roomFile;
+	debug("Loading room %s number %d", _room->getRoomName(number).c_str(), number);
 	if (!roomFile.open(Common::Path("ALFRED.1"))) {
 		error("Could not open ALFRED.1");
 		return;
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 117fa5f5a5e..d451a8bc859 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -142,7 +142,7 @@ void ResourceManager::loadAlfredAnims() {
 		alfredWalkFrames[i] = new byte *[walkingAnimLengths[i]];
 
 		int standingFrame = prevWalkingFrames;
-		debug("Loading standing frame %d at index %d", i, standingFrame);
+
 		extractSingleFrame(pic, alfredIdle[i], standingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
 		for (int j = 0; j < walkingAnimLengths[i]; j++) {
 
@@ -199,11 +199,7 @@ void ResourceManager::loadAlfredAnims() {
 	readUntilBuda(&alfred7, ALFRED7_ALFRED_COMB_L, alfredCombLeftRaw, alfredCombLeftSize);
 	byte *alfredCombLeft = nullptr;
 	size_t outSize = rleDecompress(alfredCombLeftRaw, alfredCombLeftSize, 0, spriteMapSize, &alfredCombLeft);
-	debug("Sprite map size: %d, %d, %d", spriteMapSize, alfredCombLeftSize, outSize);
 
-	for (int i = 0; i < 11; i++) {
-		debug("Extracting comb left frame %d", i);
-	}
 	for (int i = 0; i < 11; i++) {
 		alfredCombFrames[1][i] = new byte[frameSize];
 		extractSingleFrame(alfredCombLeft, alfredCombFrames[1][i], i, kAlfredFrameWidth, kAlfredFrameHeight);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 6ab3b08acae..4aa4ad0c31a 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -18,6 +18,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
+#include "common/scummsys.h"
 
 #include "pelrock/room.h"
 #include "pelrock/pelrock.h"
@@ -27,7 +28,11 @@
 namespace Pelrock {
 
 RoomManager::RoomManager() {
-	_pixelsShadows = new byte[640*400] { 0 };
+	_pixelsShadows = new byte[640 * 400]{0};
+	_roomNames = loadRoomNames();
+	for (int i = 0; i < _roomNames.size(); i++) {
+		debug("Room %d name: %s", i, _roomNames[i].c_str());
+	}
 }
 
 RoomManager::~RoomManager() {
@@ -115,7 +120,7 @@ Common::Array<Exit> RoomManager::loadExits(Common::File *roomFile, int roomOffse
 
 Common::Array<HotSpot> RoomManager::loadHotspots(Common::File *roomFile, int roomOffset) {
 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
-	debug("Hotspot(10)  pair offset position: %d", pair10_offset_pos);
+	// debug("Hotspot(10)  pair offset position: %d", pair10_offset_pos);
 	roomFile->seek(pair10_offset_pos, SEEK_SET);
 	uint32_t pair10_data_offset = roomFile->readUint32LE();
 	uint32_t pair10_size = roomFile->readUint32LE();
@@ -134,7 +139,7 @@ Common::Array<HotSpot> RoomManager::loadHotspots(Common::File *roomFile, int roo
 		spot.w = roomFile->readByte();
 		spot.h = roomFile->readByte();
 		spot.extra = roomFile->readUint16LE();
-		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, spot.type, spot.x, spot.y, spot.w, spot.h, spot.extra);
+		// debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, spot.type, spot.x, spot.y, spot.w, spot.h, spot.extra);
 		hotspots.push_back(spot);
 	}
 	return hotspots;
@@ -186,7 +191,6 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 		hotspots.push_back(hotspot);
 	}
 
-
 	byte *shadows = loadShadowMap(roomNumber);
 	loadRemaps(roomNumber);
 
@@ -299,8 +303,7 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(Common::File *roomFile, int ro
 	roomFile->seek(walkbox_countOffset, SEEK_SET);
 	byte walkbox_count = roomFile->readByte();
 
-
-	debug("Walkbox count: %d", walkbox_count);
+	// debug("Walkbox count: %d", walkbox_count);
 	uint32_t walkbox_offset = pair10_data_offset + 0x218;
 	Common::Array<WalkBox> walkboxes;
 	for (int i = 0; i < walkbox_count; i++) {
@@ -311,7 +314,7 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(Common::File *roomFile, int ro
 		int16 w = roomFile->readSint16LE();
 		int16 h = roomFile->readSint16LE();
 		byte flags = roomFile->readByte();
-		debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
+		// debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
 		WalkBox box;
 		box.x = x1;
 		box.y = y1;
@@ -352,7 +355,7 @@ Common::Array<Description> RoomManager::loadRoomDescriptions(Common::File *roomF
 				}
 				if (data[pos] == 0xF8) {
 					description.actionTrigger = data[pos + 1] | data[pos + 2] << 8;
-					debug("Found action trigger: %d", description.actionTrigger);
+					// debug("Found action trigger: %d", description.actionTrigger);
 					pos += 2;
 					break;
 				}
@@ -360,14 +363,14 @@ Common::Array<Description> RoomManager::loadRoomDescriptions(Common::File *roomF
 				// debug("Current desc: %s", desc);
 				pos++;
 			}
-			debug("Found description for item %d index %d, text: %s", description.itemId, description.index, description.text.c_str());
+			// debug("Found description for item %d index %d, text: %s", description.itemId, description.index, description.text.c_str());
 
 			descriptions.push_back(description);
 			lastDescPos = pos;
 		}
 		pos++;
 	}
-	debug("End of descriptions at position %d", pos);
+	// debug("End of descriptions at position %d", pos);
 	outPos = lastDescPos + 1;
 	delete[] data;
 	// for (Common::List<Common::String>::iterator i = descriptions.begin(); i != descriptions.end(); i++) {
@@ -376,7 +379,6 @@ Common::Array<Description> RoomManager::loadRoomDescriptions(Common::File *roomF
 	return descriptions;
 }
 
-
 char32_t decodeByte(byte b) {
 	if (b == 0x80) {
 		return '\xA4';
@@ -457,11 +459,10 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 	readUntilBuda(&talkFile, talkHeader.spritePointer, data, dataSize);
 	size_t decompressedSize = rleDecompress(data, dataSize, 0, dataSize, &decompressed);
 	free(data);
-	debug("Decompressed talking anim A size: %zu, decompressed size: %zu", dataSize, decompressedSize);
+	// debug("Decompressed talking anim A size: %zu, decompressed size: %zu", dataSize, decompressedSize);
 	for (int i = 0; i < talkHeader.numFramesAnimA; i++) {
 		talkHeader.animA[i] = new byte[talkHeader.wAnimA * talkHeader.hAnimA];
 		extractSingleFrame(decompressed, talkHeader.animA[i], i, talkHeader.wAnimA, talkHeader.hAnimA);
-
 	}
 
 	if (talkHeader.numFramesAnimB > 0) {
@@ -866,8 +867,6 @@ byte *RoomManager::loadShadowMap(int roomNumber) {
 
 	uint32 entryOffset = roomNumber * 6;
 
-	debug("Loading shadow map for room %d at offset %d", roomNumber, entryOffset);
-
 	shadowMapFile.seek(entryOffset, SEEK_SET);
 	uint32 shadowOffset = readUint24(shadowMapFile);
 
@@ -875,7 +874,6 @@ byte *RoomManager::loadShadowMap(int roomNumber) {
 	size_t compressedSize = 0;
 	readUntilBuda(&shadowMapFile, shadowOffset, compressed, compressedSize);
 
-	debug("Shadow map compressed size: %zu", compressedSize);
 	byte *shadows = nullptr;
 	size_t output = rleDecompress(compressed, compressedSize, 0, 640 * 400, &shadows);
 	free(compressed);
@@ -898,4 +896,38 @@ void RoomManager::loadRemaps(int roomNumber) {
 	remapFile.close();
 }
 
+Common::Array<Common::String> RoomManager::loadRoomNames() {
+	Common::Array<Common::String> roomNames;
+	Common::File juegoExe;
+	if (!juegoExe.open(Common::Path("JUEGO.EXE"))) {
+		error("Couldnt find file JUEGO.EXE");
+	}
+
+	size_t namesSize = 1335;
+	juegoExe.seek(0x49315, SEEK_SET);
+	byte *namesData = new byte[namesSize];
+	juegoExe.read(namesData, namesSize);
+	uint32 pos = 0;
+	Common::String currentName = "";
+	while (pos < namesSize) {
+		if (namesData[pos] == 0xFD &&
+			namesData[pos + 1] == 0x00 &&
+			namesData[pos + 2] == 0x08 &&
+			namesData[pos + 3] == 0x02) {
+			if (currentName.size() > 0	) {
+				debug("Found room name: %s", currentName.c_str());
+				roomNames.push_back(currentName);
+			}
+			currentName = "";
+			pos += 4;
+			continue;
+		}
+		currentName += (char)namesData[pos];
+		pos++;
+	}
+	delete[] namesData;
+	juegoExe.close();
+	return roomNames;
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 688357a92a3..97c72dcd8bb 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -37,6 +37,12 @@ public:
 	void loadRoomTalkingAnimations(int roomNumber);
 	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
+	Common::String getRoomName(int roomNumber) {
+		if (roomNumber >= 0 && roomNumber < _roomNames.size()) {
+			return _roomNames[roomNumber];
+		}
+		return "Unknown Room";
+	}
 
 	Common::Array<HotSpot> _currentRoomHotspots;
 	Common::Array<AnimSet> _currentRoomAnims;
@@ -49,6 +55,7 @@ public:
 	byte *_pixelsShadows = nullptr;
 	byte alfredRemap[256];
 	byte overlayRemap[256];
+	Common::Array<Common::String> _roomNames;
 
 private:
 	Common::Array<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
@@ -65,6 +72,8 @@ private:
 	ScalingParams loadScalingParams(Common::File *roomFile, int roomOffset);
 	byte *loadShadowMap(int roomNumber);
 	void loadRemaps(int roomNumber);
+	Common::Array<Common::String> loadRoomNames();
+
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index d37fa8fe089..46e3e532e4a 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -137,7 +137,6 @@ void readUntilBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *
 			break;
 		}
 	}
-	debug("Read %zu bytes until BUDA marker", pos);
 	outSize = pos;
 }
 


Commit: 10986e7bb073cd7b849f1f1c3685f06e735699ec
    https://github.com/scummvm/scummvm/commit/10986e7bb073cd7b849f1f1c3685f06e735699ec
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:15+02:00

Commit Message:
PELROCK: Music playback (from mp3)

Changed paths:
  A engines/pelrock/sound.cpp
  A engines/pelrock/sound.h
    engines/pelrock/module.mk
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/room.h


diff --git a/engines/pelrock/module.mk b/engines/pelrock/module.mk
index 88894769896..f6b1579cff6 100644
--- a/engines/pelrock/module.mk
+++ b/engines/pelrock/module.mk
@@ -10,6 +10,7 @@ MODULE_OBJS = \
 	fonts/large_font.o \
 	util.o \
 	resources.o\
+	sound.o \
 	video/video.o
 
 # This module can be built as a plugin
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 653ce77042c..474b10a4f69 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -52,14 +52,15 @@ PelrockEngine::PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc) :
 }
 
 PelrockEngine::~PelrockEngine() {
-	delete _room;
 	delete[] _compositeBuffer;
 	delete[] _currentBackground;
 	delete _largeFont;
 	delete _screen;
 	delete _chronoManager;
 	delete _videoManager;
-
+	delete _soundManager;
+	delete _room;
+	delete _res;
 	// if (_bgPopupBalloon)
 	// 	delete[] _bgPopupBalloon;
 	delete _smallFont;
@@ -82,6 +83,7 @@ Common::Error PelrockEngine::run() {
 	_videoManager = new VideoManager(_screen);
 	_room = new RoomManager();
 	_res = new ResourceManager();
+	_soundManager = new SoundManager(_mixer);
 
 	// Set the engine's debugger console
 	setDebugger(new Console());
@@ -184,7 +186,8 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		setScreen(6, 0); // museum entrance
+		setScreen(0, 0);
+		// setScreen(6, 0); // museum entrance
 						 // setScreen(13, 1); // restaurants kitchen
 						 // setScreen(2, 2); // hooker
 	}
@@ -1538,7 +1541,11 @@ void PelrockEngine::setScreen(int number, int dir) {
 
 	_room->loadRoomMetadata(&roomFile, number);
 	_room->loadRoomTalkingAnimations(number);
-
+	if(_room->_musicTrack > 0)
+		_soundManager->playMusicTrack(_room->_musicTrack);
+	else {
+		_soundManager->stopMusic();
+	}
 	_screen->markAllDirty();
 	roomFile.close();
 	delete[] background;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index a99f60a6e5e..6b1ac2a4e02 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -42,6 +42,7 @@
 #include "pelrock/fonts/small_font.h"
 #include "pelrock/resources.h"
 #include "pelrock/room.h"
+#include "pelrock/sound.h"
 #include "pelrock/types.h"
 #include "pelrock/video/video.h"
 
@@ -175,6 +176,7 @@ public:
 	AlfredState alfredState = ALFRED_IDLE;
 	ChronoManager *_chronoManager = nullptr;
 	VideoManager *_videoManager = nullptr;
+	SoundManager *_soundManager = nullptr;
 
 public:
 	PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc);
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index d451a8bc859..02a952a46b1 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -44,7 +44,7 @@ ResourceManager::~ResourceManager() {
 			delete[] alfredTalkFrames[i][j];
 		}
 
-		for(int j = 0; j < 4; j ++) {
+		for(int j = 0; j < 2; j ++) {
 			delete[] alfredInteractFrames[i][j];
 		}
 
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 4aa4ad0c31a..27fc9d7601c 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -28,7 +28,7 @@
 namespace Pelrock {
 
 RoomManager::RoomManager() {
-	_pixelsShadows = new byte[640 * 400]{0};
+	_pixelsShadows = new byte[640 * 400];
 	_roomNames = loadRoomNames();
 	for (int i = 0; i < _roomNames.size(); i++) {
 		debug("Room %d name: %s", i, _roomNames[i].c_str());
@@ -42,7 +42,10 @@ RoomManager::~RoomManager() {
 	// delete[] _currentRoomWalkboxes;
 	// delete[] _currentRoomDescriptions;
 	// delete[] _currentRoomConversations;
-	delete[] _pixelsShadows;
+	if(_pixelsShadows != nullptr) {
+		delete[] _pixelsShadows;
+		_pixelsShadows = nullptr;
+	}
 }
 
 void RoomManager::getPalette(Common::File *roomFile, int roomOffset, byte *palette) {
@@ -194,15 +197,18 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	byte *shadows = loadShadowMap(roomNumber);
 	loadRemaps(roomNumber);
 
-	int walkboxCount = 0;
+	Common::Array<int> sfx = loadRoomSfx(roomFile, roomOffset);
 
+	int walkboxCount = 0;
 	_currentRoomAnims = anims;
 	_currentRoomHotspots = hotspots;
 	_currentRoomExits = exits;
 	_currentRoomWalkboxes = walkboxes;
 	_currentRoomDescriptions = descriptions;
 	_scaleParams = scalingParams;
-	_pixelsShadows = shadows;
+	Common::copy(shadows, shadows + (640 * 400), _pixelsShadows);
+	_musicTrack = loadMusicTrackForRoom(roomFile, roomOffset);
+	_roomSfx = sfx;
 	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
 		HotSpot hotspot = _currentRoomHotspots[i];
 		drawRect(g_engine->_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 200 + i);
@@ -930,4 +936,33 @@ Common::Array<Common::String> RoomManager::loadRoomNames() {
 	return roomNames;
 }
 
+byte RoomManager::loadMusicTrackForRoom(Common::File *roomFile, int roomOffset) {
+	uint32_t pair9offset = roomOffset + (9 * 8);
+	roomFile->seek(pair9offset, SEEK_SET);
+	uint32_t pair9_data_offset = roomFile->readUint32LE();
+	uint32_t pair9_size = roomFile->readUint32LE();
+
+	roomFile->seek(pair9_data_offset, SEEK_SET);
+	byte musicTrack = roomFile->readByte();
+	debug("Music track for room at offset %d is %d", roomOffset, musicTrack);
+	return musicTrack + 1;
+}
+
+Common::Array<int> RoomManager::loadRoomSfx(Common::File *roomFile, int roomOffset) {
+	uint32_t pair9offset = roomOffset + (9 * 8);
+	roomFile->seek(pair9offset, SEEK_SET);
+	uint32_t pair9_data_offset = roomFile->readUint32LE();
+	uint32_t pair9_size = roomFile->readUint32LE();
+
+	roomFile->seek(pair9_data_offset, SEEK_SET);
+	roomFile->skip(1); // skip music track byte
+	Common::Array<int> roomSfx;
+	for(int i=0; i< 9; i++) {
+		byte sfx = roomFile->readByte();
+		roomSfx.push_back((int)sfx);
+		debug("SFX %d for room at offset %d is %d", i, roomOffset, sfx);
+	}
+	return roomSfx;
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 97c72dcd8bb..c5cc6f209f6 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -56,6 +56,8 @@ public:
 	byte alfredRemap[256];
 	byte overlayRemap[256];
 	Common::Array<Common::String> _roomNames;
+	byte _musicTrack = 0;
+	Common::Array<int> _roomSfx;
 
 private:
 	Common::Array<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
@@ -73,7 +75,8 @@ private:
 	byte *loadShadowMap(int roomNumber);
 	void loadRemaps(int roomNumber);
 	Common::Array<Common::String> loadRoomNames();
-
+	byte loadMusicTrackForRoom(Common::File *roomFile, int roomOffset);
+	Common::Array<int> loadRoomSfx(Common::File *roomFile, int roomOffset);
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
new file mode 100644
index 00000000000..3733aaa76a1
--- /dev/null
+++ b/engines/pelrock/sound.cpp
@@ -0,0 +1,97 @@
+/* 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 "audio/audiostream.h"
+#include "audio/decoders/mp3.h"
+#include "audio/mixer.h"
+#include "common/file.h"
+#include "common/scummsys.h"
+
+#include "pelrock/sound.h"
+
+namespace Pelrock {
+
+SoundManager::SoundManager(Audio::Mixer *mixer)
+    : _mixer(mixer), _currentVolume(255), _musicFile(nullptr) {
+	// TODO: Initialize sound manager
+}
+
+SoundManager::~SoundManager() {
+	stopSound();
+    stopMusic();
+}
+
+void SoundManager::playSound(const Common::String &filename, int volume) {
+	// TODO: Play sound file
+}
+
+void SoundManager::stopSound() {
+	// _mixer->stopHandle(_soundHandle);
+	// TODO: Stop currently playing sound
+}
+
+void SoundManager::setVolume(int volume) {
+	// TODO: Set sound volume
+}
+
+bool SoundManager::isPlaying() const {
+	// TODO: Return whether a sound is playing
+	return false;
+}
+
+void SoundManager::stopMusic() {
+    if(_isMusicPlaying) {
+        _mixer->stopHandle(_musicHandle);
+        _isMusicPlaying = false;
+    }
+}
+
+void SoundManager::playMusicTrack(int trackNumber) {
+    if(_currentMusicTrack == trackNumber && _isMusicPlaying) {
+        // Already playing this track
+        return;
+    }
+    _currentMusicTrack = trackNumber;
+	stopSound();
+	// Open the file
+	_musicFile = new Common::File();
+	Common::String filename = Common::String::format("music/track%d.mp3", trackNumber);
+
+	if (!_musicFile->open(Common::Path(filename))) {
+		delete _musicFile;
+        _musicFile = nullptr;
+        return;
+	}
+#ifdef USE_MAD
+	Audio::SeekableAudioStream *stream = Audio::makeMP3Stream(_musicFile, DisposeAfterUse::YES);
+	if (!stream) {
+		_musicFile->close();
+        delete _musicFile;
+        _musicFile = nullptr;
+		return;
+	}
+	Audio::AudioStream *loopStream = Audio::makeLoopingAudioStream(stream, 0);
+	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, loopStream, -1, _currentVolume);
+    _isMusicPlaying = true;
+    _musicFile = nullptr;
+#endif
+}
+} // End of namespace Pelrock
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
new file mode 100644
index 00000000000..6b7a0d67d8f
--- /dev/null
+++ b/engines/pelrock/sound.h
@@ -0,0 +1,59 @@
+/* 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 PELROCK_SOUND_H
+#define PELROCK_SOUND_H
+
+#include "common/file.h"
+#include "common/scummsys.h"
+#include "common/str.h"
+#include "audio/mixer.h"
+
+namespace Pelrock {
+
+class SoundManager {
+public:
+    SoundManager(Audio::Mixer *mixer);
+    ~SoundManager();
+
+    void playSound(const Common::String &filename, int volume = 255);
+    void stopSound();
+    void stopMusic();
+    void setVolume(int volume);
+    bool isPlaying() const;
+    void playMusicTrack(int trackNumber);
+    bool isMusicPlaying() const {
+        return _isMusicPlaying;
+    }
+
+private:
+    Audio::Mixer *_mixer;
+    Audio::SoundHandle _soundHandle;
+    Audio::SoundHandle _musicHandle;
+    bool _isMusicPlaying = false;
+    int _currentVolume;
+    Common::File *_musicFile;
+    byte _currentMusicTrack = 0;
+};
+
+} // End of namespace Pelrock
+
+#endif // PELROCK_SOUND_H


Commit: d2fa47a1150836545daab5492b48765099d9a7e9
    https://github.com/scummvm/scummvm/commit/d2fa47a1150836545daab5492b48765099d9a7e9
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:15+02:00

Commit Message:
PELROCK: Fix music stopage

Changed paths:
    engines/pelrock/room.cpp
    engines/pelrock/sound.cpp


diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 27fc9d7601c..647b6470c3d 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -945,7 +945,7 @@ byte RoomManager::loadMusicTrackForRoom(Common::File *roomFile, int roomOffset)
 	roomFile->seek(pair9_data_offset, SEEK_SET);
 	byte musicTrack = roomFile->readByte();
 	debug("Music track for room at offset %d is %d", roomOffset, musicTrack);
-	return musicTrack + 1;
+	return musicTrack > 0 ? musicTrack + 1 : 0;
 }
 
 Common::Array<int> RoomManager::loadRoomSfx(Common::File *roomFile, int roomOffset) {
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 3733aaa76a1..538cd52fc16 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -23,6 +23,7 @@
 #include "audio/decoders/mp3.h"
 #include "audio/mixer.h"
 #include "common/file.h"
+#include "common/debug.h"
 #include "common/scummsys.h"
 
 #include "pelrock/sound.h"
@@ -59,6 +60,7 @@ bool SoundManager::isPlaying() const {
 
 void SoundManager::stopMusic() {
     if(_isMusicPlaying) {
+        debug("Stopping music");
         _mixer->stopHandle(_musicHandle);
         _isMusicPlaying = false;
     }
@@ -70,7 +72,7 @@ void SoundManager::playMusicTrack(int trackNumber) {
         return;
     }
     _currentMusicTrack = trackNumber;
-	stopSound();
+	stopMusic();
 	// Open the file
 	_musicFile = new Common::File();
 	Common::String filename = Common::String::format("music/track%d.mp3", trackNumber);
@@ -89,7 +91,7 @@ void SoundManager::playMusicTrack(int trackNumber) {
 		return;
 	}
 	Audio::AudioStream *loopStream = Audio::makeLoopingAudioStream(stream, 0);
-	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, loopStream, -1, _currentVolume);
+	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, loopStream, -1, _currentVolume);
     _isMusicPlaying = true;
     _musicFile = nullptr;
 #endif


Commit: f1a3cd07d25ad973aeaae7c7f900ae54fd5f0b2e
    https://github.com/scummvm/scummvm/commit/f1a3cd07d25ad973aeaae7c7f900ae54fd5f0b2e
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:15+02:00

Commit Message:
PELROCK: Renders settings screen

Changed paths:
    engines/pelrock/chrono.h
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/sound.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/chrono.h b/engines/pelrock/chrono.h
index ffdf43f3b3e..aa389db418d 100644
--- a/engines/pelrock/chrono.h
+++ b/engines/pelrock/chrono.h
@@ -25,8 +25,9 @@
 
 namespace Pelrock {
 
-// const int kTickMs = 20;
-const int kTickMs = 55;
+const int kTickMs = 18;
+// const int kTickMs = 55;
+// const int kTickMs = 15;
 const int kHalfTickMultiplier = 2;
 
 class ChronoManager {
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 15dc427f85e..e69c4a34e87 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -39,6 +39,9 @@ namespace Pelrock {
     static const uint32_t ALFRED7_ALFRED_COMB_R = 67768;
     static const uint32_t ALFRED7_ALFRED_COMB_L = 88408;
 
+    static const uint32_t kSettingsMenuOffset = 910097; // Placeholder offset
+    static const uint32_t kSettingsPaletteOffset = 1038141; // 640 * 480
+
 
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 474b10a4f69..b217520a886 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -146,6 +146,13 @@ Common::Error PelrockEngine::run() {
 				checkMouseClick(e.mouse.x, e.mouse.y);
 				_displayPopup = false;
 				_longClick = false;
+			} else if (e.type == Common::EVENT_RBUTTONUP) {
+				if (stateGame != SETTINGS)
+					stateGame = SETTINGS;
+				else {
+					g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
+					stateGame = GAME;
+				}
 			}
 		}
 		if (_isMouseDown) {
@@ -157,7 +164,16 @@ Common::Error PelrockEngine::run() {
 			}
 		}
 		checkMouseHover();
-		frames();
+		if (stateGame == SETTINGS) {
+
+			memcpy(_screen->getPixels(), _res->_mainMenu, 640 * 400);
+			g_system->getPaletteManager()->setPalette(_res->_mainMenuPalette, 0, 256);
+			g_engine->_screen->markAllDirty();
+			g_engine->_screen->update();
+
+		} else if (stateGame == GAME) {
+			frames();
+		}
 
 		_screen->update();
 		// limiter.delayBeforeSwap();
@@ -171,7 +187,7 @@ void PelrockEngine::init() {
 	_res->loadCursors();
 	_res->loadInteractionIcons();
 	calculateScalingMasks();
-
+	_res->loadSettingsMenu();
 	_compositeBuffer = new byte[640 * 400];
 	_currentBackground = new byte[640 * 400];
 
@@ -188,8 +204,8 @@ void PelrockEngine::init() {
 		loadAnims();
 		setScreen(0, 0);
 		// setScreen(6, 0); // museum entrance
-						 // setScreen(13, 1); // restaurants kitchen
-						 // setScreen(2, 2); // hooker
+		// setScreen(13, 1); // restaurants kitchen
+		// setScreen(2, 2); // hooker
 	}
 }
 
@@ -1541,7 +1557,7 @@ void PelrockEngine::setScreen(int number, int dir) {
 
 	_room->loadRoomMetadata(&roomFile, number);
 	_room->loadRoomTalkingAnimations(number);
-	if(_room->_musicTrack > 0)
+	if (_room->_musicTrack > 0)
 		_soundManager->playMusicTrack(_room->_musicTrack);
 	else {
 		_soundManager->stopMusic();
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 02a952a46b1..193133af40c 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -23,6 +23,8 @@
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/util.h"
+#include "resources.h"
+#include "room.h"
 
 namespace Pelrock {
 
@@ -44,7 +46,7 @@ ResourceManager::~ResourceManager() {
 			delete[] alfredTalkFrames[i][j];
 		}
 
-		for(int j = 0; j < 2; j ++) {
+		for (int j = 0; j < 2; j++) {
 			delete[] alfredInteractFrames[i][j];
 		}
 
@@ -55,8 +57,9 @@ ResourceManager::~ResourceManager() {
 		delete[] alfredIdle[i];
 	}
 
-		delete[] alfredCombFrames[0];
-		delete[] alfredCombFrames[1];
+	delete[] alfredCombFrames[0];
+	delete[] alfredCombFrames[1];
+	delete _mainMenu;
 }
 
 void ResourceManager::loadCursors() {
@@ -72,6 +75,7 @@ void ResourceManager::loadCursors() {
 	}
 	alfred7File.close();
 }
+
 void ResourceManager::loadInteractionIcons() {
 	Common::File alfred7File;
 	if (!alfred7File.open("ALFRED.7")) {
@@ -167,7 +171,6 @@ void ResourceManager::loadAlfredAnims() {
 			int interactingFrame = interactingStartFrame + j;
 			extractSingleFrame(pic, alfredInteractFrames[i][j], interactingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
 		}
-
 	}
 
 	free(bufferFile);
@@ -210,4 +213,44 @@ void ResourceManager::loadAlfredAnims() {
 	free(alfredCombLeftRaw);
 }
 
+
+void ResourceManager::mergeRleBlocks(Common::SeekableReadStream *stream, uint32 offset, int numBlocks, byte *outputBuffer) {
+	stream->seek(offset, SEEK_SET);
+	// get screen
+	size_t combined_size = 0;
+	for (int i = 0; i < numBlocks; i++) {
+		byte *thisBlock = nullptr;
+		size_t blockSize = 0;
+		readUntilBuda(stream, stream->pos(), thisBlock, blockSize);
+		uint8_t *block_data = nullptr;
+		size_t decompressedSize = rleDecompress(thisBlock, blockSize, 0, 640 * 400, &block_data, true);
+		memcpy(outputBuffer + combined_size, block_data, decompressedSize);
+		combined_size += decompressedSize + 1;
+		free(block_data);
+		free(thisBlock);
+	}
+}
+
+void ResourceManager::loadSettingsMenu() {
+	Common::File alfred7;
+	if (!alfred7.open(Common::Path("ALFRED.7"))) {
+		error("Could not open ALFRED.7");
+		return;
+	}
+
+	_mainMenu = new byte[640 * 400];
+
+	alfred7.seek(kSettingsPaletteOffset, SEEK_SET);
+	alfred7.read(_mainMenuPalette, 768);
+	for (int i = 0; i < 256; i++) {
+		_mainMenuPalette[i * 3] = _mainMenuPalette[i * 3] << 2;
+		_mainMenuPalette[i * 3 + 1] = _mainMenuPalette[i * 3 + 1] << 2;
+		_mainMenuPalette[i * 3 + 2] = _mainMenuPalette[i * 3 + 2] << 2;
+	}
+
+	mergeRleBlocks(&alfred7, kSettingsMenuOffset, 8, _mainMenu);
+	alfred7.close();
+}
+
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index bfa3b60efd1..6bed0e95310 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -21,40 +21,44 @@
 #ifndef PELROCK_RESOURCES_H
 #define PELROCK_RESOURCES_H
 
- #include "common/scummsys.h"
+#include "common/scummsys.h"
+#include "common/stream.h"
 
 namespace Pelrock {
 
-
 static const int walkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
 static const int talkingAnimLengths[4] = {8, 8, 4, 4}; // size of each inner array
 static const int interactingAnimLength = 2;
 
 class ResourceManager {
 private:
-	/* data */
+	void mergeRleBlocks(Common::SeekableReadStream *stream, uint32 offset, int numBlocks, byte *outputBuffer);
 public:
 	ResourceManager(/* args */);
 	~ResourceManager();
 
+	void loadSettingsMenu();
 	void loadCursors();
 	void loadInteractionIcons();
 	void loadAlfredAnims();
+	byte *loadExtra();
 
-	byte *alfredIdle[4] = {nullptr};  // 4 directions
+	byte *alfredIdle[4] = {nullptr}; // 4 directions
 
-	byte **alfredWalkFrames[4];              // 4 arrays of arrays
+	byte **alfredWalkFrames[4]; // 4 arrays of arrays
 
-	byte **alfredTalkFrames[4];              // 4 arrays of arrays
+	byte **alfredTalkFrames[4]; // 4 arrays of arrays
 
 	byte **alfredCombFrames[2];
 	byte **alfredInteractFrames[4];
 
-    byte *_cursorMasks[5] = {nullptr};
+	byte *_cursorMasks[5] = {nullptr};
 	byte *_verbIcons[9] = {nullptr};
 	byte *_popUpBalloon = nullptr;
-};
 
+	byte *_mainMenu = nullptr;
+	byte _mainMenuPalette[768] = {0};
+};
 
 } // End of namespace Pelrock
 #endif
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 647b6470c3d..0f84e8c6e2b 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -63,13 +63,13 @@ void RoomManager::getPalette(Common::File *roomFile, int roomOffset, byte *palet
 		palette[i * 3 + 1] = palette[i * 3 + 1] << 2;
 		palette[i * 3 + 2] = palette[i * 3 + 2] << 2;
 	}
+	memcpy(_roomPalette, palette, 768);
 }
 
 void RoomManager::getBackground(Common::File *roomFile, int roomOffset, byte *background) {
 	roomFile->seek(0, SEEK_SET);
 	// get screen
 	size_t combined_size = 0;
-	size_t uncompressed_size = 0;
 	for (int pair_idx = 0; pair_idx < 8; pair_idx++) {
 		uint32_t pair_offset = roomOffset + (pair_idx * 8);
 		if (pair_offset + 8 > roomFile->size())
@@ -78,7 +78,6 @@ void RoomManager::getBackground(Common::File *roomFile, int roomOffset, byte *ba
 		roomFile->seek(pair_offset, SEEK_SET);
 		uint32_t offset = roomFile->readUint32LE();
 		uint32_t size = roomFile->readUint32LE();
-		uncompressed_size += size;
 
 		if (offset > 0 && size > 0 && offset < roomFile->size()) {
 			byte *data = new byte[size];
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index c5cc6f209f6..245a38c198d 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -53,6 +53,7 @@ public:
 	TalkingAnimHeader _talkingAnimHeader;
 	ScalingParams _scaleParams;
 	byte *_pixelsShadows = nullptr;
+	byte _roomPalette[768];
 	byte alfredRemap[256];
 	byte overlayRemap[256];
 	Common::Array<Common::String> _roomNames;
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index 6b7a0d67d8f..75f36dc0a0e 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -52,6 +52,7 @@ private:
     int _currentVolume;
     Common::File *_musicFile;
     byte _currentMusicTrack = 0;
+    Audio::SoundHandle _sfxHandles[8];
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 587bcbf0301..df49d413a5b 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -283,9 +283,9 @@ enum GameState {
 	SETTINGS = 104,
 	EXTRA_SCREEN = 105,
 	INTRO = 106,
-	PROMOTE = 107,
 };
 
+
 } // End of namespace Pelrock
 
 #endif


Commit: a0bc558ee022f2c0e97be5709ed4cde511896c67
    https://github.com/scummvm/scummvm/commit/a0bc558ee022f2c0e97be5709ed4cde511896c67
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:16+02:00

Commit Message:
PELROCK: Plays Sfx

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index b217520a886..3e749204dc2 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -186,8 +186,10 @@ Common::Error PelrockEngine::run() {
 void PelrockEngine::init() {
 	_res->loadCursors();
 	_res->loadInteractionIcons();
-	calculateScalingMasks();
 	_res->loadSettingsMenu();
+	_soundManager->loadSoundIndex();
+
+	calculateScalingMasks();
 	_compositeBuffer = new byte[640 * 400];
 	_currentBackground = new byte[640 * 400];
 
@@ -1562,6 +1564,11 @@ void PelrockEngine::setScreen(int number, int dir) {
 	else {
 		_soundManager->stopMusic();
 	}
+	for (int i = 0; i < kNumSfxPerRoom; i++) {
+		if (_room->_roomSfx[i])
+			_soundManager->playSound(_room->_roomSfx[i]);
+	}
+
 	_screen->markAllDirty();
 	roomFile.close();
 	delete[] background;
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 0f84e8c6e2b..f8ffb32b25f 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -20,8 +20,8 @@
  */
 #include "common/scummsys.h"
 
-#include "pelrock/room.h"
 #include "pelrock/pelrock.h"
+#include "pelrock/room.h"
 #include "pelrock/util.h"
 #include "room.h"
 
@@ -30,9 +30,6 @@ namespace Pelrock {
 RoomManager::RoomManager() {
 	_pixelsShadows = new byte[640 * 400];
 	_roomNames = loadRoomNames();
-	for (int i = 0; i < _roomNames.size(); i++) {
-		debug("Room %d name: %s", i, _roomNames[i].c_str());
-	}
 }
 
 RoomManager::~RoomManager() {
@@ -42,7 +39,7 @@ RoomManager::~RoomManager() {
 	// delete[] _currentRoomWalkboxes;
 	// delete[] _currentRoomDescriptions;
 	// delete[] _currentRoomConversations;
-	if(_pixelsShadows != nullptr) {
+	if (_pixelsShadows != nullptr) {
 		delete[] _pixelsShadows;
 		_pixelsShadows = nullptr;
 	}
@@ -196,8 +193,6 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	byte *shadows = loadShadowMap(roomNumber);
 	loadRemaps(roomNumber);
 
-	Common::Array<int> sfx = loadRoomSfx(roomFile, roomOffset);
-
 	int walkboxCount = 0;
 	_currentRoomAnims = anims;
 	_currentRoomHotspots = hotspots;
@@ -207,7 +202,8 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	_scaleParams = scalingParams;
 	Common::copy(shadows, shadows + (640 * 400), _pixelsShadows);
 	_musicTrack = loadMusicTrackForRoom(roomFile, roomOffset);
-	_roomSfx = sfx;
+	_roomSfx = loadRoomSfx(roomFile, roomOffset);
+
 	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
 		HotSpot hotspot = _currentRoomHotspots[i];
 		drawRect(g_engine->_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 200 + i);
@@ -901,8 +897,8 @@ void RoomManager::loadRemaps(int roomNumber) {
 	remapFile.close();
 }
 
-Common::Array<Common::String> RoomManager::loadRoomNames() {
-	Common::Array<Common::String> roomNames;
+Common::StringArray RoomManager::loadRoomNames() {
+	Common::StringArray roomNames;
 	Common::File juegoExe;
 	if (!juegoExe.open(Common::Path("JUEGO.EXE"))) {
 		error("Couldnt find file JUEGO.EXE");
@@ -919,8 +915,7 @@ Common::Array<Common::String> RoomManager::loadRoomNames() {
 			namesData[pos + 1] == 0x00 &&
 			namesData[pos + 2] == 0x08 &&
 			namesData[pos + 3] == 0x02) {
-			if (currentName.size() > 0	) {
-				debug("Found room name: %s", currentName.c_str());
+			if (currentName.size() > 0) {
 				roomNames.push_back(currentName);
 			}
 			currentName = "";
@@ -947,7 +942,7 @@ byte RoomManager::loadMusicTrackForRoom(Common::File *roomFile, int roomOffset)
 	return musicTrack > 0 ? musicTrack + 1 : 0;
 }
 
-Common::Array<int> RoomManager::loadRoomSfx(Common::File *roomFile, int roomOffset) {
+Common::Array<byte> RoomManager::loadRoomSfx(Common::File *roomFile, int roomOffset) {
 	uint32_t pair9offset = roomOffset + (9 * 8);
 	roomFile->seek(pair9offset, SEEK_SET);
 	uint32_t pair9_data_offset = roomFile->readUint32LE();
@@ -955,11 +950,11 @@ Common::Array<int> RoomManager::loadRoomSfx(Common::File *roomFile, int roomOffs
 
 	roomFile->seek(pair9_data_offset, SEEK_SET);
 	roomFile->skip(1); // skip music track byte
-	Common::Array<int> roomSfx;
-	for(int i=0; i< 9; i++) {
+	Common::Array<byte> roomSfx(kNumSfxPerRoom);
+	for (int i = 0; i < kNumSfxPerRoom; i++) {
 		byte sfx = roomFile->readByte();
-		roomSfx.push_back((int)sfx);
-		debug("SFX %d for room at offset %d is %d", i, roomOffset, sfx);
+		roomSfx[i] = sfx;
+		debug("SFX %d for room at offset %d is %d (%s)", i, roomOffset, sfx, SOUND_FILENAMES[sfx]);
 	}
 	return roomSfx;
 }
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 245a38c198d..3ec5fbf4ad1 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -29,6 +29,8 @@
 
 namespace Pelrock {
 
+static const int kNumSfxPerRoom = 8;
+
 class RoomManager {
 public:
 	RoomManager();
@@ -56,9 +58,9 @@ public:
 	byte _roomPalette[768];
 	byte alfredRemap[256];
 	byte overlayRemap[256];
-	Common::Array<Common::String> _roomNames;
+	Common::StringArray _roomNames;
 	byte _musicTrack = 0;
-	Common::Array<int> _roomSfx;
+	Common::Array<byte> _roomSfx;
 
 private:
 	Common::Array<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
@@ -75,9 +77,9 @@ private:
 	ScalingParams loadScalingParams(Common::File *roomFile, int roomOffset);
 	byte *loadShadowMap(int roomNumber);
 	void loadRemaps(int roomNumber);
-	Common::Array<Common::String> loadRoomNames();
+	Common::StringArray loadRoomNames();
 	byte loadMusicTrackForRoom(Common::File *roomFile, int roomOffset);
-	Common::Array<int> loadRoomSfx(Common::File *roomFile, int roomOffset);
+	Common::Array<byte> loadRoomSfx(Common::File *roomFile, int roomOffset);
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 538cd52fc16..4501b869086 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -21,32 +21,144 @@
 
 #include "audio/audiostream.h"
 #include "audio/decoders/mp3.h"
+#include "audio/decoders/raw.h"
+#include "audio/decoders/wave.h"
+
 #include "audio/mixer.h"
-#include "common/file.h"
 #include "common/debug.h"
+#include "common/endian.h"
+#include "common/file.h"
+#include "common/memstream.h"
 #include "common/scummsys.h"
 
 #include "pelrock/sound.h"
+#include "sound.h"
 
 namespace Pelrock {
 
 SoundManager::SoundManager(Audio::Mixer *mixer)
-    : _mixer(mixer), _currentVolume(255), _musicFile(nullptr) {
+	: _mixer(mixer), _currentVolume(255), _musicFile(nullptr) {
 	// TODO: Initialize sound manager
 }
 
 SoundManager::~SoundManager() {
-	stopSound();
-    stopMusic();
+	stopAllSounds();
+	stopMusic();
 }
 
-void SoundManager::playSound(const Common::String &filename, int volume) {
+void SoundManager::playSound(byte index, int volume) {
+	debug("Playing sound index %d (%s)", index, SOUND_FILENAMES[index]);
+	auto it = _soundMap.find(SOUND_FILENAMES[index]);
+	if (it != _soundMap.end()) {
+		playSound(it->_value, volume);
+	} else {
+		debug("Sound file %s not found in sound map", SOUND_FILENAMES[index]);
+	}
+}
+
+void SoundManager::playSound(SonidoFile sound, int volume) {
+	Common::File sonidosFile;
+	if (!sonidosFile.open(Common::Path("SONIDOS.DAT"))) {
+		debug("Failed to open SONIDOS.DAT");
+		return;
+	}
+
+	sonidosFile.seek(sound.offset, SEEK_SET);
+	byte *data = new byte[sound.size];
+	sonidosFile.read(data, sound.size);
+	sonidosFile.close();
+
+	SoundFormat format = detectFormat(data, sound.size);
+	uint32_t sampleRate = getSampleRate(data, format);
+	Audio::AudioStream *stream = nullptr;
+
+	if (format == SOUND_FORMAT_RIFF) {
+		// For WAV/RIFF files, use the wave decoder
+		Common::MemoryReadStream *memStream = new Common::MemoryReadStream(data, sound.size, DisposeAfterUse::YES);
+		stream = Audio::makeWAVStream(memStream, DisposeAfterUse::YES);
+	} else if (format == SOUND_FORMAT_RAWPCM || format == SOUND_FORMAT_MILES || format == SOUND_FORMAT_MILES2) {
+		// Determine the offset to skip the header
+		uint32 headerSize = 0;
+		if (format == SOUND_FORMAT_MILES || format == SOUND_FORMAT_MILES2) {
+			headerSize = 80;
+		}
+
+		uint32 pcmSize = sound.size - headerSize;
+		byte *pcmData = (byte *)malloc(pcmSize);
+		memcpy(pcmData, data + headerSize, pcmSize);
+		delete[] data;
+
+		// Create raw audio stream (8-bit unsigned mono is common for old games)
+		stream = Audio::makeRawStream(pcmData, pcmSize, sampleRate,
+									  Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
+	} else {
+		debug("Unknown sound format");
+		delete[] data;
+		return;
+	}
+
+	if (stream) {
+		int channel = findFreeChannel();
+		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, volume, 0, DisposeAfterUse::YES);
+	}
 	// TODO: Play sound file
 }
 
-void SoundManager::stopSound() {
-	// _mixer->stopHandle(_soundHandle);
-	// TODO: Stop currently playing sound
+SoundFormat SoundManager::detectFormat(byte *data, uint32 size) {
+
+	if (size < 16) {
+		return SOUND_FORMAT_INVALID;
+	}
+	byte byte0 = data[0];
+	byte byte1 = data[1];
+
+	if (data[0] == 'R' && data[1] == 'I' && data[2] == 'F' && data[3] == 'F') {
+		return SOUND_FORMAT_RIFF;
+	}
+	if (byte0 == 0x01 && byte1 == 0x2e) {
+		return SOUND_FORMAT_MILES;
+	}
+	if (byte0 == 0x01 && (byte1 >= 0x40 && byte1 <= 0x7f)) {
+		return SOUND_FORMAT_MILES2;
+	}
+	if (size <= 100) {
+		return SOUND_FORMAT_INVALID;
+	}
+	return SOUND_FORMAT_RAWPCM;
+}
+
+int SoundManager::getSampleRate(byte *data, SoundFormat format) {
+
+	uint32 sampleRate = 11025; // Default sample rate
+	if (format == SOUND_FORMAT_RIFF) {
+		sampleRate = READ_LE_UINT32(data + 0x18);
+	} else if (format == SOUND_FORMAT_MILES) {
+		sampleRate = READ_LE_UINT32(data + 0x1C);
+	} else if (format == SOUND_FORMAT_MILES2) {
+		sampleRate = READ_LE_UINT32(data + 0x10);
+	}
+	return sampleRate;
+}
+
+int SoundManager::findFreeChannel() {
+	for (int i = 0; i < kMaxChannels; i++) {
+		if (!_mixer->isSoundHandleActive(_sfxHandles[i])) {
+			return i;
+		}
+	}
+	return 0;
+}
+
+void SoundManager::stopAllSounds() {
+	for (int i = 0; i < kMaxChannels; i++) {
+		_mixer->stopHandle(_sfxHandles[i]);
+	}
+}
+
+void SoundManager::stopSound(int channel) {
+	if (channel >= 0 && channel < kMaxChannels) {
+		_mixer->stopHandle(_sfxHandles[channel]);
+	}
 }
 
 void SoundManager::setVolume(int volume) {
@@ -54,24 +166,35 @@ void SoundManager::setVolume(int volume) {
 }
 
 bool SoundManager::isPlaying() const {
-	// TODO: Return whether a sound is playing
+	for (int i = 0; i < 8; i++) {
+		if (_mixer->isSoundHandleActive(_sfxHandles[i])) {
+			return true;
+		}
+	}
+	return false;
+}
+
+bool SoundManager::isPlaying(int channel) const {
+	if (channel >= 0 && channel < kMaxChannels) {
+		return _mixer->isSoundHandleActive(_sfxHandles[channel]);
+	}
 	return false;
 }
 
 void SoundManager::stopMusic() {
-    if(_isMusicPlaying) {
-        debug("Stopping music");
-        _mixer->stopHandle(_musicHandle);
-        _isMusicPlaying = false;
-    }
+	if (_isMusicPlaying) {
+		debug("Stopping music");
+		_mixer->stopHandle(_musicHandle);
+		_isMusicPlaying = false;
+	}
 }
 
 void SoundManager::playMusicTrack(int trackNumber) {
-    if(_currentMusicTrack == trackNumber && _isMusicPlaying) {
-        // Already playing this track
-        return;
-    }
-    _currentMusicTrack = trackNumber;
+	if (_currentMusicTrack == trackNumber && _isMusicPlaying) {
+		// Already playing this track
+		return;
+	}
+	_currentMusicTrack = trackNumber;
 	stopMusic();
 	// Open the file
 	_musicFile = new Common::File();
@@ -79,21 +202,51 @@ void SoundManager::playMusicTrack(int trackNumber) {
 
 	if (!_musicFile->open(Common::Path(filename))) {
 		delete _musicFile;
-        _musicFile = nullptr;
-        return;
+		_musicFile = nullptr;
+		return;
 	}
 #ifdef USE_MAD
 	Audio::SeekableAudioStream *stream = Audio::makeMP3Stream(_musicFile, DisposeAfterUse::YES);
 	if (!stream) {
 		_musicFile->close();
-        delete _musicFile;
-        _musicFile = nullptr;
+		delete _musicFile;
+		_musicFile = nullptr;
 		return;
 	}
 	Audio::AudioStream *loopStream = Audio::makeLoopingAudioStream(stream, 0);
 	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, loopStream, -1, _currentVolume);
-    _isMusicPlaying = true;
-    _musicFile = nullptr;
+	_isMusicPlaying = true;
+	_musicFile = nullptr;
 #endif
 }
+
+void Pelrock::SoundManager::loadSoundIndex() {
+
+	Common::File sonidosFile;
+	if (!sonidosFile.open(Common::Path("SONIDOS.DAT"))) {
+		debug("Failed to open SONIDOS.DAT");
+		return;
+	}
+	// Read header
+	char magic[4];
+	sonidosFile.read(magic, 4);
+	if (strncmp(magic, "PACK", 4) != 0) {
+		debug("SONIDOS.DAT has invalid magic");
+		return;
+	}
+	byte fileCount = sonidosFile.readByte();
+	debug("SONIDOS.DAT contains %u files", fileCount);
+	sonidosFile.skip(3); // Padding bytes
+
+	for (uint32_t i = 0; i < fileCount; i++) {
+		SonidoFile sonido;
+		sonido.filename = sonidosFile.readString('\0', 12);
+		sonidosFile.skip(1);
+		sonido.offset = sonidosFile.readUint32LE();
+		sonido.size = sonidosFile.readUint32LE();
+		_soundMap[sonido.filename] = sonido;
+	}
+	sonidosFile.close();
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index 75f36dc0a0e..362b22e511d 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -22,37 +22,169 @@
 #ifndef PELROCK_SOUND_H
 #define PELROCK_SOUND_H
 
+#include "audio/mixer.h"
 #include "common/file.h"
 #include "common/scummsys.h"
 #include "common/str.h"
-#include "audio/mixer.h"
 
 namespace Pelrock {
 
+typedef struct {
+	Common::String filename;
+	uint32_t offset;
+	uint32_t size;
+	unsigned char *data;
+} SonidoFile;
+
+static const char *SOUND_FILENAMES[] = {
+	"NO_SOUND.SMP", // 0 - Silence/disabled
+	"BUHO_ZZZ.SMP", // 1 - Owl
+	"BIRD_1_1.SMP", // 2 - Bird variant 1
+	"BIRD_1_2.SMP", // 3 - Bird variant 2
+	"BIRD_1_3.SMP", // 4 - Bird variant 3
+	"DESPERZZ.SMP", // 5 - Yawn/stretch
+	"HORN_5ZZ.SMP", // 6 - Car horn 5
+	"HORN_6ZZ.SMP", // 7 - Car horn 6
+	"HORN_8ZZ.SMP", // 8 - Car horn 8
+	"SUZIPASS.SMP", // 9 - Suzi passing
+	"CAT_1ZZZ.SMP", // 10 - Cat
+	"DOG_01ZZ.SMP", // 11 - Dog bark 1
+	"DOG_02ZZ.SMP", // 12 - Dog bark 2
+	"DOG_04ZZ.SMP", // 13 - Dog bark 4
+	"DOG_05ZZ.SMP", // 14 - Dog bark 5
+	"DOG_06ZZ.SMP", // 15 - Dog bark 6
+	"DOG_07ZZ.SMP", // 16 - Dog bark 7
+	"DOG_09ZZ.SMP", // 17 - Dog bark 9
+	"ALARMZZZ.SMP", // 18 - Alarm
+	"AMBULAN1.SMP", // 19 - Ambulance
+	"FOUNTAIN.SMP", // 20 - Fountain
+	"GRILLOSZ.SMP", // 21 - Crickets
+	"HOJASZZZ.SMP", // 22 - Leaves rustling
+	"FLASHZZZ.SMP", // 23 - Flash/camera
+	"CUCHI1ZZ.SMP", // 24 - Knife 1
+	"KNRRRRRZ.SMP", // 25 - Snoring
+	"PHONE_02.SMP", // 26 - Phone ring 2
+	"PHONE_03.SMP", // 27 - Phone ring 3
+	"SSSHTZZZ.SMP", // 28 - Shush/quiet
+	"BURGUER1.SMP", // 29 - Burger sizzle
+	"FLIES_2Z.SMP", // 30 - Flies buzzing
+	"PARRILLA.SMP", // 31 - Grill
+	"WATER_2Z.SMP", // 32 - Water
+	"XIQUETZZ.SMP", // 33 - Whistle
+	"RONQUIZZ.SMP", // 34 - Snoring
+	"MOCO1ZZZ.SMP", // 35 - Snot/mucus 1
+	"MOCO2ZZZ.SMP", // 36 - Snot/mucus 2
+	"SPRINGZZ.SMP", // 37 - Spring bounce
+	"MARUJASZ.SMP", // 38 - Gossip/chatter
+	"ELECTROZ.SMP", // 39 - Electric shock
+	"GLASS1ZZ.SMP", // 40 - Glass clink
+	"OPDOORZZ.SMP", // 41 - Door open
+	"CLDOORZZ.SMP", // 42 - Door close
+	"FXH2ZZZZ.SMP", // 43 - Effect 2
+	"BOTEZZZZ.SMP", // 44 - Bottle
+	"ELEC3ZZZ.SMP", // 45 - Electric 3
+	"AJARLZZZ.SMP", // 46 - Ajar/creak
+	"BELCHZZZ.SMP", // 47 - Belch/burp
+	"64ZZZZZZ.SMP", // 48 - Sound effect 64
+	"BIRDOWL2.SMP", // 49 - Bird/owl 2
+	"BUBBLE2Z.SMP", // 50 - Bubbles
+	"BURGUER1.SMP", // 51 - Burger (duplicate)
+	"CACKLEZZ.SMP", // 52 - Cackle/laugh
+	"CERAMIC1.SMP", // 53 - Ceramic break
+	"CLANG5ZZ.SMP", // 54 - Metal clang
+	"CUCHI2ZZ.SMP", // 55 - Knife 2
+	"CUCHI3ZZ.SMP", // 56 - Knife 3
+	"ELEC3ZZZ.SMP", // 57 - Electric 3 (duplicate)
+	"HOJASZZZ.SMP", // 58 - Leaves (duplicate)
+	"LIMA1ZZZ.SMP", // 59 - File/rasp
+	"MOROSZZZ.SMP", // 60 - Moors/crowd
+	"MOROZZZZ.SMP", // 61 - Moor/crowd
+	"MUD1ZZZZ.SMP", // 62 - Mud squelch
+	"PICOZZZZ.SMP", // 63 - Pickaxe
+	"PICO1XZZ.SMP", // 64 - Pickaxe 1
+	"PICO2XZZ.SMP", // 65 - Pickaxe 2
+	"PICO3XZZ.SMP", // 66 - Pickaxe 3
+	"RIMSHOTZ.SMP", // 67 - Rimshot drum
+	"RONCOZZZ.SMP", // 68 - Snoring
+	"SORBOZZZ.SMP", // 69 - Slurp/sip
+	"VIENTO1Z.SMP", // 70 - Wind
+	"2ZZZZZZZ.SMP", // 71 - Sound 2
+	"20ZZZZZZ.SMP", // 72 - Sound 20
+	"21ZZZZZZ.SMP", // 73 - Sound 21
+	"23ZZZZZZ.SMP", // 74 - Sound 23
+	"107ZZZZZ.SMP", // 75 - Sound 107
+	"39ZZZZZZ.SMP", // 76 - Sound 39
+	"81ZZZZZZ.SMP", // 77 - Sound 81
+	"88ZZZZZZ.SMP", // 78 - Sound 88
+	"92ZZZZZZ.SMP", // 79 - Sound 92
+	"SAW_2ZZZ.SMP", // 80 - Saw
+	"QUAKE2ZZ.SMP", // 81 - Earthquake
+	"ROCKSZZZ.SMP", // 82 - Rocks falling
+	"IN_FIREZ.SMP", // 83 - Fire
+	"BEAMZZZZ.SMP", // 84 - Beam/ray
+	"GLISSDWN.SMP", // 85 - Glissando down
+	"REMATERL.SMP", // 86 - Rematerialize
+	"FXH1ZZZZ.SMP", // 87 - Effect 1
+	"FXH3ZZZZ.SMP", // 88 - Effect 3
+	"FXH4ZZZZ.SMP", // 89 - Effect 4
+	"MATCHZZZ.SMP", // 90 - Match strike
+	"SURF_01Z.SMP", // 91 - Surf wave 1
+	"SURF_02Z.SMP", // 92 - Surf wave 2
+	"SURF_04Z.SMP", // 93 - Surf wave 4
+	"TWANGZZZ.SMP", // 94 - Twang
+	"LANDCRAS.SMP", // 95 - Crash landing
+};
+
+enum SoundFormat {
+	SOUND_FORMAT_RAWPCM,
+	SOUND_FORMAT_MILES,
+	SOUND_FORMAT_MILES2,
+	SOUND_FORMAT_RIFF,
+	SOUND_FORMAT_INVALID
+};
+
+struct SoundData {
+	SoundFormat format;
+	int sampleRate;
+	byte *data;
+	uint32 size;
+};
+
+const int kMaxChannels = 15;
+
 class SoundManager {
 public:
-    SoundManager(Audio::Mixer *mixer);
-    ~SoundManager();
-
-    void playSound(const Common::String &filename, int volume = 255);
-    void stopSound();
-    void stopMusic();
-    void setVolume(int volume);
-    bool isPlaying() const;
-    void playMusicTrack(int trackNumber);
-    bool isMusicPlaying() const {
-        return _isMusicPlaying;
-    }
+	SoundManager(Audio::Mixer *mixer);
+	~SoundManager();
+	void playSound(byte index, int volume = 255);
+	void stopAllSounds();
+	void stopSound(int channel);
+	void stopMusic();
+	void setVolume(int volume);
+	bool isPlaying() const;
+	bool isPlaying(int channel) const;
+	void playMusicTrack(int trackNumber);
+	bool isMusicPlaying() const {
+		return _isMusicPlaying;
+	}
+	void loadSoundIndex();
 
 private:
-    Audio::Mixer *_mixer;
-    Audio::SoundHandle _soundHandle;
-    Audio::SoundHandle _musicHandle;
-    bool _isMusicPlaying = false;
-    int _currentVolume;
-    Common::File *_musicFile;
-    byte _currentMusicTrack = 0;
-    Audio::SoundHandle _sfxHandles[8];
+	void playSound(SonidoFile sound, int volume = 255);
+	SoundFormat detectFormat(byte *data, uint32 size);
+	int getSampleRate(byte *data, SoundFormat format);
+	int findFreeChannel();
+
+private:
+	Audio::Mixer *_mixer;
+	bool _isMusicPlaying = false;
+	int _currentVolume;
+	Common::File *_musicFile;
+	byte _currentMusicTrack = 0;
+	Audio::SoundHandle _musicHandle;
+	Audio::SoundHandle _sfxHandles[kMaxChannels];
+
+	Common::HashMap<Common::String, SonidoFile> _soundMap;
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index df49d413a5b..8f99d8917d1 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -76,7 +76,6 @@ enum AlfredState {
 	ALFRED_COMB
 };
 
-
 // Direction flags (bit-packed)
 #define MOVE_RIGHT 0x01 // Move right (positive X)
 #define MOVE_LEFT 0x02  // Move left (negative X)


Commit: b899645c4b616f87fbbf6a21fa9753b94f3f272c
    https://github.com/scummvm/scummvm/commit/b899645c4b616f87fbbf6a21fa9753b94f3f272c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:16+02:00

Commit Message:
PELROCK: Condense Alfred state

Changed paths:
    engines/pelrock/chrono.cpp
    engines/pelrock/chrono.h
    engines/pelrock/fonts/large_font.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/chrono.cpp b/engines/pelrock/chrono.cpp
index 306e38c1ca9..131fd80ce85 100644
--- a/engines/pelrock/chrono.cpp
+++ b/engines/pelrock/chrono.cpp
@@ -37,7 +37,7 @@ ChronoManager::~ChronoManager() {
 void ChronoManager::updateChrono() {
 	uint32 currentTime = g_system->getMillis();
 
-	if (_textTtl > 0 && g_engine->alfredState == ALFRED_TALKING && g_engine->alfredState != ALFRED_WALKING) {
+	if (_textTtl > 0 && g_engine->alfredState.animState == ALFRED_TALKING && g_engine->alfredState.animState != ALFRED_WALKING) {
 		_textTtl -= (currentTime - _lastTick);
 		if (_textTtl < 0)
 			_textTtl = 0;
@@ -46,12 +46,6 @@ void ChronoManager::updateChrono() {
 	if ((currentTime - _lastTick) >= kTickMs / _speedMultiplier) {
 		_gameTick = true;
 		_tickCount++;
-		if (_tickCount == kHalfTickMultiplier) {
-			_tickCount = 0;
-			_gameTickHalfSpeed = true;
-		} else {
-			_gameTickHalfSpeed = false;
-		}
 		_lastTick = currentTime;
 	} else {
 		_gameTick = false;
diff --git a/engines/pelrock/chrono.h b/engines/pelrock/chrono.h
index aa389db418d..03f10b010cf 100644
--- a/engines/pelrock/chrono.h
+++ b/engines/pelrock/chrono.h
@@ -25,9 +25,13 @@
 
 namespace Pelrock {
 
-const int kTickMs = 18;
-// const int kTickMs = 55;
+// const int kTickMs = 18;
+// const int kTickMs = 100;
 // const int kTickMs = 15;
+// const int kTickMs = 33;
+// const int kTickMs = 38;
+const int kTickMs = 55;
+// const int kTickMs = 60;
 const int kHalfTickMultiplier = 2;
 
 class ChronoManager {
@@ -45,7 +49,6 @@ public:
 	void waitForKey();
 
 	bool _gameTick = false;
-	bool _gameTickHalfSpeed = false;
 	long _textTtl = 0;
 };
 
diff --git a/engines/pelrock/fonts/large_font.cpp b/engines/pelrock/fonts/large_font.cpp
index 9a1dc5894e0..fa431089d42 100644
--- a/engines/pelrock/fonts/large_font.cpp
+++ b/engines/pelrock/fonts/large_font.cpp
@@ -115,7 +115,7 @@ int LargeFont::getCharWidth(uint32 chr) const {
 
 void LargeFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
 	chr -= 32; // Adjust for font starting at ASCII 32
-	if (!_fontData || chr > 96 || chr < 0) {
+	if (!_fontData || chr >= 96 || chr < 0) {
 		return;
 	}
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 3e749204dc2..e26b4b157e7 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -112,19 +112,19 @@ Common::Error PelrockEngine::run() {
 			if (e.type == Common::EVENT_KEYDOWN) {
 				switch (e.kbd.keycode) {
 				case Common::KEYCODE_w:
-					alfredState = ALFRED_WALKING;
+					alfredState.animState = ALFRED_WALKING;
 					break;
 				case Common::KEYCODE_t:
-					alfredState = ALFRED_TALKING;
+					alfredState.animState = ALFRED_TALKING;
 					break;
 				case Common::KEYCODE_s:
-					alfredState = ALFRED_IDLE;
+					alfredState.animState = ALFRED_IDLE;
 					break;
 				case Common::KEYCODE_c:
-					alfredState = ALFRED_COMB;
+					alfredState.animState = ALFRED_COMB;
 					break;
 				case Common::KEYCODE_i:
-					alfredState = ALFRED_INTERACTING;
+					alfredState.animState = ALFRED_INTERACTING;
 					break;
 				case Common::KEYCODE_z:
 					showShadows = !showShadows;
@@ -204,7 +204,7 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		setScreen(0, 0);
+		setScreen(0, ALFRED_DOWN);
 		// setScreen(6, 0); // museum entrance
 		// setScreen(13, 1); // restaurants kitchen
 		// setScreen(2, 2); // hooker
@@ -326,89 +326,91 @@ void PelrockEngine::frames() {
 			AnimSet &animSet = _room->_currentRoomAnims[i];
 			drawNextFrame(&animSet);
 		}
-
-		switch (alfredState) {
+		switch (alfredState.animState) {
 		case ALFRED_WALKING: {
 			MovementStep step = _currentContext.movement_buffer[_current_step];
 
 			if (step.distance_x > 0) {
 				if (step.flags & MOVE_RIGHT) {
-					dirAlfred = 0;
-					xAlfred += MIN((uint16_t)6, step.distance_x);
+					alfredState.direction = ALFRED_RIGHT;
+					alfredState.x += MIN(alfredState.movementSpeed, step.distance_x);
 				}
 				if (step.flags & MOVE_LEFT) {
-					dirAlfred = 1;
-					xAlfred -= MIN((uint16_t)6, step.distance_x);
+					alfredState.direction = ALFRED_LEFT;
+					alfredState.x -= MIN(alfredState.movementSpeed, step.distance_x);
 				}
 			}
 			if (step.distance_y > 0) {
 				if (step.flags & MOVE_DOWN) {
-					dirAlfred = 2;
-					yAlfred += MIN((uint16_t)6, step.distance_y);
+					alfredState.direction = ALFRED_DOWN;
+					alfredState.y += MIN(alfredState.movementSpeed, step.distance_y);
 				}
 				if (step.flags & MOVE_UP) {
-					dirAlfred = 3;
-					yAlfred -= MIN((uint16_t)6, step.distance_y);
+					alfredState.direction = ALFRED_UP;
+					alfredState.y -= MIN(alfredState.movementSpeed, step.distance_y);
 				}
 			}
 
 			if (step.distance_x > 0)
-				step.distance_x -= MIN((uint16_t)6, step.distance_x);
+				step.distance_x -= MIN(alfredState.movementSpeed, step.distance_x);
 
 			if (step.distance_y > 0)
-				step.distance_y -= MIN((uint16_t)6, step.distance_y);
+				step.distance_y -= MIN(alfredState.movementSpeed, step.distance_y);
 
 			if (step.distance_x <= 0 && step.distance_y <= 0) {
 				_current_step++;
 				if (_current_step >= _currentContext.movement_count) {
 					_current_step = 0;
-					alfredState = ALFRED_IDLE;
+					alfredState.animState = ALFRED_IDLE;
 				}
 			} else {
 				_currentContext.movement_buffer[_current_step] = step;
 			}
 
-			Exit *exit = isExitUnder(xAlfred, yAlfred);
+			Exit *exit = isExitUnder(alfredState.x, alfredState.y);
 
 			if (exit != nullptr) {
-				xAlfred = exit->targetX;
-				yAlfred = exit->targetY;
+				alfredState.x = exit->targetX;
+				alfredState.y = exit->targetY;
 				setScreen(exit->targetRoom, exit->dir);
 			}
 
-			if (curAlfredFrame >= walkingAnimLengths[dirAlfred]) {
-				curAlfredFrame = 0;
+			if (alfredState.curFrame >= walkingAnimLengths[alfredState.direction]) {
+				alfredState.curFrame = 0;
 			}
 
-			drawAlfred(_res->alfredWalkFrames[dirAlfred][curAlfredFrame]);
-			curAlfredFrame++;
+			drawAlfred(_res->alfredWalkFrames[alfredState.direction][alfredState.curFrame]);
+			// if(alfredFrameSkip) alfredState.curFrame++;
+			// alfredFrameSkip = !alfredFrameSkip;
+			alfredState.curFrame++;
 			break;
 		}
 		case ALFRED_TALKING:
-			if (curAlfredFrame >= talkingAnimLengths[dirAlfred] - 1) {
-				curAlfredFrame = 0;
+			if (alfredState.curFrame >= talkingAnimLengths[alfredState.direction] - 1) {
+				alfredState.curFrame = 0;
 			}
-			drawAlfred(_res->alfredTalkFrames[dirAlfred][curAlfredFrame]);
-			curAlfredFrame++;
+			drawAlfred(_res->alfredTalkFrames[alfredState.direction][alfredState.curFrame]);
+			alfredState.curFrame++;
 			break;
 		case ALFRED_COMB:
-			if (curAlfredFrame >= 11) {
-				curAlfredFrame = 0;
+			if (alfredState.curFrame >= 11) {
+				alfredState.curFrame = 0;
 			}
-			drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCombFrames[0][curAlfredFrame], xAlfred, yAlfred - kAlfredFrameHeight, 51, 102, 255);
-			curAlfredFrame++;
+			drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCombFrames[0][alfredState.curFrame], alfredState.x, alfredState.y - kAlfredFrameHeight, 51, 102, 255);
+			alfredState.curFrame++;
 			break;
 		case ALFRED_INTERACTING:
-			if (curAlfredFrame >= interactingAnimLength) {
-				curAlfredFrame = 0;
+			if (alfredState.curFrame >= interactingAnimLength) {
+				alfredState.curFrame = 0;
 			}
-			drawAlfred(_res->alfredInteractFrames[dirAlfred][curAlfredFrame]);
-			curAlfredFrame++;
+			drawAlfred(_res->alfredInteractFrames[alfredState.direction][alfredState.curFrame]);
+			alfredState.curFrame++;
 			break;
 		default:
-			drawAlfred(_res->alfredIdle[dirAlfred]);
+			drawAlfred(_res->alfredIdle[alfredState.direction]);
 			break;
 		}
+
 		if (_displayPopup) {
 
 			// byte *bgDialog = new byte[kBalloonWidth * kBalloonHeight];
@@ -438,7 +440,7 @@ void PelrockEngine::frames() {
 
 		memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
 
-		if (alfredState != ALFRED_WALKING && !_currentTextPages.empty()) {
+		if (alfredState.animState != ALFRED_WALKING && !_currentTextPages.empty()) {
 			if (_chronoManager->_textTtl > 0) {
 				renderText(_currentTextPages[_currentTextPageIndex], _textColor, _textPos.x, _textPos.y);
 			} else if (_currentTextPageIndex < _currentTextPages.size() - 1) {
@@ -452,7 +454,7 @@ void PelrockEngine::frames() {
 			} else {
 				_currentTextPages.clear();
 				_currentTextPageIndex = 0;
-				alfredState = ALFRED_IDLE;
+				alfredState.animState = ALFRED_IDLE;
 				isNPCATalking = false;
 				isNPCBTalking = false;
 			}
@@ -519,7 +521,7 @@ void PelrockEngine::renderText(Common::Array<Common::String> lines, int color, i
 
 void PelrockEngine::drawAlfred(byte *buf) {
 
-	ScaleCalculation scale = calculateScaling(yAlfred, _room->_scaleParams);
+	ScaleCalculation scale = calculateScaling(alfredState.y, _room->_scaleParams);
 
 	int finalHeight = kAlfredFrameHeight - scale.scaleDown + scale.scaleUp;
 	if (finalHeight <= 0) {
@@ -542,8 +544,8 @@ void PelrockEngine::drawAlfred(byte *buf) {
 
 	// debug("lines to skip = %d, finalHeight = %d, finalWidth = %d for position (%d, %d)", linesToSkip, finalHeight, finalWidth, xAlfred, yAlfred);
 
-	int shadowPos = yAlfred; // - finalHeight;
-	bool shadeCharacter = _room->_pixelsShadows[shadowPos * 640 + xAlfred] != 0xFF;
+	int shadowPos = alfredState.y; // - finalHeight;
+	bool shadeCharacter = _room->_pixelsShadows[shadowPos * 640 + alfredState.x] != 0xFF;
 
 	byte *finalBuf = new byte[finalWidth * finalHeight];
 
@@ -623,7 +625,7 @@ void PelrockEngine::drawAlfred(byte *buf) {
 		}
 	}
 
-	drawSpriteToBuffer(_compositeBuffer, 640, finalBuf, xAlfred, yAlfred - finalHeight, finalWidth, finalHeight, 255);
+	drawSpriteToBuffer(_compositeBuffer, 640, finalBuf, alfredState.x, alfredState.y - finalHeight, finalWidth, finalHeight, 255);
 	delete[] finalBuf;
 }
 
@@ -881,8 +883,8 @@ void PelrockEngine::drawTalkNPC(AnimSet *animSet) {
 }
 
 void PelrockEngine::walkTo(int x, int y) {
-	alfredState = ALFRED_WALKING;
-	curAlfredFrame = 0;
+	alfredState.animState = ALFRED_WALKING;
+	alfredState.curFrame = 0;
 
 	PathContext context = {NULL, NULL, NULL, 0, 0, 0};
 
@@ -934,8 +936,8 @@ bool PelrockEngine::pathFind(int x, int y, PathContext *context) {
 		context->movement_buffer = (MovementStep *)malloc(MAX_MOVEMENT_STEPS * sizeof(MovementStep));
 	}
 
-	int startX = xAlfred;
-	int startY = yAlfred;
+	int startX = alfredState.x;
+	int startY = alfredState.y;
 	Common::Point target = calculateWalkTarget(x, y);
 	x = target.x;
 	y = target.y;
@@ -1253,8 +1255,8 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 		Exit *exit = isExitUnder(walkTarget.x, walkTarget.y);
 
 		if (exit != nullptr) {
-			xAlfred = exit->targetX;
-			yAlfred = exit->targetY;
+			alfredState.x = exit->targetX;
+			alfredState.y = exit->targetY;
 
 			debug("Placing character at %d, %d", exit->targetX, exit->targetY);
 			setScreen(exit->targetRoom, exit->dir);
@@ -1396,8 +1398,8 @@ void PelrockEngine::sayNPC(AnimSet *anim, Common::String text, byte color) {
 }
 
 void PelrockEngine::sayAlfred(Common::String text) {
-	alfredState = ALFRED_TALKING;
-	curAlfredFrame = 0;
+	alfredState.animState = ALFRED_TALKING;
+	alfredState.curFrame = 0;
 	debug("Alfred says: %s", text.c_str());
 	_currentTextPages = wordWrap(text);
 	_textColor = 13;
@@ -1405,7 +1407,7 @@ void PelrockEngine::sayAlfred(Common::String text) {
 	for (int i = 0; i < _currentTextPages[0].size(); i++) {
 		totalChars += _currentTextPages[0][i].size();
 	}
-	_textPos = Common::Point(xAlfred, yAlfred - kAlfredFrameHeight - 10);
+	_textPos = Common::Point(alfredState.x, alfredState.y - kAlfredFrameHeight - 10);
 	_chronoManager->_textTtl = totalChars * kTextCharDisplayTime;
 }
 
@@ -1523,7 +1525,7 @@ Common::Array<Common::Array<Common::String>> wordWrap(Common::String text) {
 	return pages;
 }
 
-void PelrockEngine::setScreen(int number, int dir) {
+void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 
 	Common::File roomFile;
 	debug("Loading room %s number %d", _room->getRoomName(number).c_str(), number);
@@ -1531,11 +1533,11 @@ void PelrockEngine::setScreen(int number, int dir) {
 		error("Could not open ALFRED.1");
 		return;
 	}
-	dirAlfred = dir;
-	alfredState = ALFRED_IDLE;
+	alfredState.direction = dir;
+	alfredState.animState = ALFRED_IDLE;
 	_current_step = 0;
 	int roomOffset = number * kRoomStructSize;
-	curAlfredFrame = 0;
+	alfredState.curFrame = 0;
 	byte *palette = new byte[256 * 3];
 	_room->getPalette(&roomFile, roomOffset, palette);
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 6b1ac2a4e02..6c46e2c8686 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -58,8 +58,7 @@ private:
 	ResourceManager *_res = nullptr;
 
 	void init();
-	void setScreen(int s, int dir);
-	void setScreenJava(int s, int dir);
+	void setScreen(int s, AlfredDirection dir);
 	void loadAnims();
 
 	void walkTo(int x, int y);
@@ -121,12 +120,7 @@ private:
 	int _currentTextPageIndex = 0;
 
 	// Alfred
-	// int xAlfred = 319;
-	// int yAlfred = 302;
-	int xAlfred = 264;
-	int yAlfred = 394;
-	int dirAlfred = 0;
-	int curAlfredFrame = 0;
+	bool alfredFrameSkip = false;
 
 	uint16 mouseX = 0;
 	uint16 mouseY = 0;
@@ -173,7 +167,7 @@ protected:
 
 public:
 	Graphics::Screen *_screen = nullptr;
-	AlfredState alfredState = ALFRED_IDLE;
+	AlfredState alfredState;
 	ChronoManager *_chronoManager = nullptr;
 	VideoManager *_videoManager = nullptr;
 	SoundManager *_soundManager = nullptr;
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index f8ffb32b25f..ef15b1258ae 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -111,7 +111,26 @@ Common::Array<Exit> RoomManager::loadExits(Common::File *roomFile, int roomOffse
 
 		exit.targetX = roomFile->readUint16LE();
 		exit.targetY = roomFile->readUint16LE();
-		exit.dir = roomFile->readByte();
+		byte dir = roomFile->readByte();
+		switch (dir)
+		{
+		case ALFRED_RIGHT:
+			exit.dir = ALFRED_RIGHT;
+			break;
+		case ALFRED_LEFT:
+			exit.dir = ALFRED_LEFT;
+			break;
+		case ALFRED_DOWN:
+			exit.dir = ALFRED_DOWN;
+			break;
+		case ALFRED_UP:
+			exit.dir = ALFRED_UP;
+			break;
+		default:
+			exit.dir = ALFRED_DOWN;
+			break;
+		}
+
 		exits.push_back(exit);
 	}
 	return exits;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 8f99d8917d1..83fe1f8676d 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -68,13 +68,6 @@ const int kAlfredFrameHeight = 102;
 
 const int kChoiceHeight = 16; // Height of each choice line in pixels
 
-enum AlfredState {
-	ALFRED_IDLE,
-	ALFRED_WALKING,
-	ALFRED_TALKING,
-	ALFRED_INTERACTING,
-	ALFRED_COMB
-};
 
 // Direction flags (bit-packed)
 #define MOVE_RIGHT 0x01 // Move right (positive X)
@@ -101,6 +94,32 @@ enum AlfredState {
 
 #define ALFRED_COLOR 0x0D
 
+
+
+enum AlfredAnimState {
+	ALFRED_IDLE,
+	ALFRED_WALKING,
+	ALFRED_TALKING,
+	ALFRED_INTERACTING,
+	ALFRED_COMB
+};
+
+enum AlfredDirection {
+	ALFRED_RIGHT = 0,
+	ALFRED_LEFT = 1,
+	ALFRED_DOWN = 2,
+	ALFRED_UP = 3
+};
+
+struct AlfredState {
+	AlfredAnimState animState = ALFRED_IDLE;
+	AlfredDirection direction = ALFRED_DOWN;
+	int curFrame = 0;
+	uint16 movementSpeed = 6; // pixels per frame
+	uint16 x = 319;
+	uint16 y = 302;
+};
+
 typedef struct {
 	uint8_t flags;       /* Direction flags (see MOVE_* constants) */
 	uint16_t distance_x; // Horizontal distance to move
@@ -142,7 +161,7 @@ struct Exit {
 	uint16 targetX;
 	uint16 targetY;
 	uint16 targetDir;
-	byte dir;
+	AlfredDirection dir;
 	byte flags;
 };
 
@@ -284,7 +303,6 @@ enum GameState {
 	INTRO = 106,
 };
 
-
 } // End of namespace Pelrock
 
 #endif


Commit: 4e21a273c66309872ccd4223ddbfe5abedc4c802
    https://github.com/scummvm/scummvm/commit/4e21a273c66309872ccd4223ddbfe5abedc4c802
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:16+02:00

Commit Message:
PELROCK: Rudimentary ambient sound generation

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index e26b4b157e7..ede51df431b 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -318,6 +318,12 @@ void PelrockEngine::frames() {
 
 	if (_chronoManager->_gameTick) {
 
+		int soundIndex = _soundManager->tick();
+		if(soundIndex >= 0 && soundIndex < _room->_roomSfx.size()) {
+			debug("Playing SFX index %d", soundIndex);
+			_soundManager->playSound(_room->_roomSfx[3 + soundIndex]);
+		}
+
 		memcpy(_compositeBuffer, _currentBackground, 640 * 400);
 
 		// debug("Game tick!");
@@ -1526,7 +1532,7 @@ Common::Array<Common::Array<Common::String>> wordWrap(Common::String text) {
 }
 
 void PelrockEngine::setScreen(int number, AlfredDirection dir) {
-
+	_soundManager->stopAllSounds();
 	Common::File roomFile;
 	debug("Loading room %s number %d", _room->getRoomName(number).c_str(), number);
 	if (!roomFile.open(Common::Path("ALFRED.1"))) {
@@ -1566,10 +1572,10 @@ void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 	else {
 		_soundManager->stopMusic();
 	}
-	for (int i = 0; i < kNumSfxPerRoom; i++) {
-		if (_room->_roomSfx[i])
-			_soundManager->playSound(_room->_roomSfx[i]);
-	}
+	// for (int i = 0; i < kNumSfxPerRoom; i++) {
+	// 	if (_room->_roomSfx[i])
+	// 		_soundManager->playSound(_room->_roomSfx[i]);
+	// }
 
 	_screen->markAllDirty();
 	roomFile.close();
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 4501b869086..52bb901e52b 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -31,6 +31,7 @@
 #include "common/memstream.h"
 #include "common/scummsys.h"
 
+#include "pelrock/pelrock.h"
 #include "pelrock/sound.h"
 #include "sound.h"
 
@@ -220,7 +221,7 @@ void SoundManager::playMusicTrack(int trackNumber) {
 #endif
 }
 
-void Pelrock::SoundManager::loadSoundIndex() {
+void SoundManager::loadSoundIndex() {
 
 	Common::File sonidosFile;
 	if (!sonidosFile.open(Common::Path("SONIDOS.DAT"))) {
@@ -249,4 +250,26 @@ void Pelrock::SoundManager::loadSoundIndex() {
 	sonidosFile.close();
 }
 
+
+int gerRandomNumber() {
+
+
+}
+
+int SoundManager::tick() {
+
+	soundFrameCounter++;
+	uint32 random = g_engine->getRandomNumber(1);
+	if(!random) {
+		return -1;
+	}
+	if((soundFrameCounter & COUNTER_MASK) != COUNTER_MASK){
+
+		return -1;
+	}
+	soundFrameCounter = 0;
+	uint32 slot = g_engine->getRandomNumber(4);
+	return slot;
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index 362b22e511d..c2a4372a35f 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -150,6 +150,8 @@ struct SoundData {
 	uint32 size;
 };
 
+static const uint COUNTER_MASK = 0x1F;
+
 const int kMaxChannels = 15;
 
 class SoundManager {
@@ -169,6 +171,8 @@ public:
 	}
 	void loadSoundIndex();
 
+	int tick();
+
 private:
 	void playSound(SonidoFile sound, int volume = 255);
 	SoundFormat detectFormat(byte *data, uint32 size);
@@ -183,7 +187,7 @@ private:
 	byte _currentMusicTrack = 0;
 	Audio::SoundHandle _musicHandle;
 	Audio::SoundHandle _sfxHandles[kMaxChannels];
-
+	int soundFrameCounter = 0;
 	Common::HashMap<Common::String, SonidoFile> _soundMap;
 };
 


Commit: 9c95c808593920d13c72d0c550db8cf76196a5af
    https://github.com/scummvm/scummvm/commit/9c95c808593920d13c72d0c550db8cf76196a5af
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:17+02:00

Commit Message:
PELROCK: Sprite movements (left/right and top/down)

Changed paths:
    engines/pelrock/console.cpp
    engines/pelrock/console.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/console.cpp b/engines/pelrock/console.cpp
index 1964cb5e3f6..cfa96283d8b 100644
--- a/engines/pelrock/console.cpp
+++ b/engines/pelrock/console.cpp
@@ -19,20 +19,32 @@
  *
  */
 
+#include "console.h"
+
 #include "pelrock/console.h"
+#include "pelrock/types.h"
 
 namespace Pelrock {
 
-Console::Console() : GUI::Debugger() {
-	registerCmd("test",   WRAP_METHOD(Console, Cmd_test));
+PelrockConsole::PelrockConsole(PelrockEngine *engine) : GUI::Debugger(), _engine(engine) {
+	registerCmd("setScreen", WRAP_METHOD(PelrockConsole, cmdLoadRoom));
 }
 
-Console::~Console() {
+PelrockConsole::~PelrockConsole() {
 }
 
-bool Console::Cmd_test(int argc, const char **argv) {
-	debugPrintf("Test\n");
+
+bool PelrockConsole::cmdLoadRoom(int argc, const char **argv) {
+	if (argc < 2) {
+		debugPrintf("Usage: setScreen <roomNumber>");
+		return true;
+	}
+
+	int roomNumber = atoi(argv[1]);
+	g_engine->setScreen(roomNumber, ALFRED_DOWN);
+	debugPrintf("Loaded room %d", roomNumber);
 	return true;
 }
 
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/console.h b/engines/pelrock/console.h
index f17d71e587e..29bb09747c0 100644
--- a/engines/pelrock/console.h
+++ b/engines/pelrock/console.h
@@ -24,15 +24,19 @@
 #define PELROCK_CONSOLE_H
 
 #include "gui/debugger.h"
+#include "pelrock/pelrock.h"
 
 namespace Pelrock {
 
-class Console : public GUI::Debugger {
+class PelrockConsole : public GUI::Debugger {
 private:
-	bool Cmd_test(int argc, const char **argv);
+	PelrockEngine *_engine;
+	bool cmdLoadRoom(int argc, const char **argv);
+	bool cmdTest(int argc, const char **argv);
+
 public:
-	Console();
-	~Console() override;
+	PelrockConsole(PelrockEngine *engine);
+	~PelrockConsole() override;
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index ede51df431b..4095abd9f89 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -86,7 +86,7 @@ Common::Error PelrockEngine::run() {
 	_soundManager = new SoundManager(_mixer);
 
 	// Set the engine's debugger console
-	setDebugger(new Console());
+	setDebugger(new PelrockConsole(this));
 
 	// If a savegame was selected from the launcher, load it
 	int saveSlot = ConfMan.getInt("save_slot");
@@ -220,7 +220,7 @@ void PelrockEngine::talk(byte object) {
 	if (_room->_currentRoomConversations.size() == 0)
 		return;
 
-	AnimSet *animSet;
+	Sprite *animSet;
 	for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
 		if (_room->_currentRoomAnims[i].extra == object) {
 			animSet = &_room->_currentRoomAnims[i];
@@ -329,7 +329,7 @@ void PelrockEngine::frames() {
 		// debug("Game tick!");
 		for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
 			// debug("Processing animation set %d, numAnims %d", num, i->numAnims);
-			AnimSet &animSet = _room->_currentRoomAnims[i];
+			Sprite &animSet = _room->_currentRoomAnims[i];
 			drawNextFrame(&animSet);
 		}
 		switch (alfredState.animState) {
@@ -495,7 +495,7 @@ void PelrockEngine::frames() {
 		if (showShadows) {
 			memcpy(_screen->getPixels(), _room->_pixelsShadows, 640 * 400);
 		}
-
+		_smallFont->drawString(_screen, Common::String::format("Room number: %d",_room->_currentRoomNumber), 0, 4, 640, 13);
 		_screen->markAllDirty();
 
 		// _screen->update();
@@ -635,16 +635,52 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	delete[] finalBuf;
 }
 
-void PelrockEngine::drawNextFrame(AnimSet *animSet) {
-	Anim &animData = animSet->animData[animSet->curAnimIndex];
-	int x = animData.x;
-	int y = animData.y;
+
+void applyMovement(int16_t *x, int16_t *y, /*int8_t *z,*/ uint16_t flags) {
+    // X-axis movement
+    if (flags & 0x10) {  // Bit 4: X movement enabled
+        int amount = flags & 0x07;  // Bits 0-2: pixels per frame
+        if (flags & 0x08) {  // Bit 3: direction
+            *x += amount;   // 1 = right (add)
+        } else {
+            *x -= amount;   // 0 = left (subtract)
+        }
+    }
+
+    // Y-axis movement
+    if (flags & 0x200) {  // Bit 9: Y movement enabled
+        int amount = (flags >> 5) & 0x07;  // Bits 5-7: pixels per frame
+        if (flags & 0x100) {  // Bit 8: direction
+            *y += amount;   // 1 = down (add)
+        } else {
+            *y -= amount;   // 0 = up (subtract)
+        }
+    }
+
+    // // Z-axis movement
+    // if (flags & 0x4000) {  // Bit 14: Z movement enabled
+    //     int amount = (flags >> 10) & 0x07;  // Bits 10-12: amount
+    //     if (flags & 0x2000) {  // Bit 13: direction
+    //         *z += amount;   // 1 = forward (add)
+    //     } else {
+    //         *z -= amount;   // 0 = back (subtract)
+    //     }
+    // }
+}
+
+
+void PelrockEngine::drawNextFrame(Sprite *sprite) {
+	Anim &animData = sprite->animData[sprite->curAnimIndex];
+
+
+	applyMovement(&(sprite->x), &(sprite->y), animData.movementFlags);
+	int x = sprite->x;
+	int y = sprite->y;
 	int w = animData.w;
 	int h = animData.h;
-	int extra = animSet->extra;
-
+	int extra = sprite->extra;
 	if (whichNPCTalking == extra) {
-		drawTalkNPC(animSet);
+		drawTalkNPC(sprite);
 		return;
 	}
 
@@ -653,7 +689,7 @@ void PelrockEngine::drawNextFrame(AnimSet *animSet) {
 	byte *frame = new byte[frameSize];
 	extractSingleFrame(animData.animData, frame, curFrame, animData.w, animData.h);
 
-	drawSpriteToBuffer(_compositeBuffer, 640, frame, animSet->x, animSet->y, animSet->w, animSet->h, 255);
+	drawSpriteToBuffer(_compositeBuffer, 640, frame, sprite->x, sprite->y, sprite->w, sprite->h, 255);
 
 	if (animData.elpapsedFrames == animData.speed) {
 		animData.elpapsedFrames = 0;
@@ -666,10 +702,10 @@ void PelrockEngine::drawNextFrame(AnimSet *animSet) {
 			} else {
 				animData.curFrame = 0;
 				animData.curLoop = 0;
-				if (animSet->curAnimIndex < animSet->numAnims - 1) {
-					animSet->curAnimIndex++;
+				if (sprite->curAnimIndex < sprite->numAnims - 1) {
+					sprite->curAnimIndex++;
 				} else {
-					animSet->curAnimIndex = 0;
+					sprite->curAnimIndex = 0;
 				}
 			}
 		}
@@ -861,7 +897,7 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	}
 }
 
-void PelrockEngine::drawTalkNPC(AnimSet *animSet) {
+void PelrockEngine::drawTalkNPC(Sprite *animSet) {
 	// Change with the right index
 
 	int index = animSet->index;
@@ -893,44 +929,8 @@ void PelrockEngine::walkTo(int x, int y) {
 	alfredState.curFrame = 0;
 
 	PathContext context = {NULL, NULL, NULL, 0, 0, 0};
-
 	pathFind(x, y, &context);
-	// debug("\nPath Information:\n");
-	// debug("================\n");
-
-	// debug("Walkbox path (%d boxes): ", context.path_length);
-
-	// debug("Movement steps (%d steps):\n", context.movement_count);
-	for (int i = 0; i < context.movement_count; i++) {
-		MovementStep *step = &context.movement_buffer[i];
-		// debug("  Step %d: ", i);
-
-		// if (step->flags & MOVE_RIGHT)
-		// 	debug("RIGHT ");
-		// if (step->flags & MOVE_LEFT)
-		// 	debug("LEFT ");
-		// if (step->flags & MOVE_DOWN)
-		// 	debug("DOWN ");
-		// if (step->flags & MOVE_UP)
-		// 	debug("UP ");
-
-		// debug("(dx=%d, dy=%d)\n", step->distance_x, step->distance_y);
-	}
-
-	// debug("\nCompressed path (%d bytes): ", context.compressed_length);
-
-	// if (x > xAlfred) {
-	// 	dirAlfred = RIGHT;
-	// } else if (x < xAlfred) {
-	// 	dirAlfred = LEFT;
-	// } else if (y < yAlfred) {
-	// 	dirAlfred = UP;
-	// } else if (y > yAlfred) {
-	// 	dirAlfred = DOWN;
-	// }
-	// debug("Setting Alfred to walk towards (%d, %d) from (%d, %d) in direction %d", x, y, xAlfred, yAlfred, dirAlfred);
 	_currentContext = context;
-	// debug("Path find complete, movement count: %d", _currentContext.movement_count);
 }
 
 bool PelrockEngine::pathFind(int x, int y, PathContext *context) {
@@ -1389,7 +1389,7 @@ void PelrockEngine::drawText(Common::String text, int x, int y, int w, byte colo
 	_largeFont->drawString(_screen, text.c_str(), x, y, w, color, Graphics::kTextAlignCenter);
 }
 
-void PelrockEngine::sayNPC(AnimSet *anim, Common::String text, byte color) {
+void PelrockEngine::sayNPC(Sprite *anim, Common::String text, byte color) {
 	isNPCATalking = true;
 	whichNPCTalking = anim->extra;
 	debug("NPC says %s, color = %d", text.c_str(), color);
@@ -1577,6 +1577,7 @@ void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 	// 		_soundManager->playSound(_room->_roomSfx[i]);
 	// }
 
+	_room->_currentRoomNumber = number;
 	_screen->markAllDirty();
 	roomFile.close();
 	delete[] background;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 6c46e2c8686..0529e446045 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -58,7 +58,6 @@ private:
 	ResourceManager *_res = nullptr;
 
 	void init();
-	void setScreen(int s, AlfredDirection dir);
 	void loadAnims();
 
 	void walkTo(int x, int y);
@@ -85,19 +84,19 @@ private:
 	void drawText(Common::String text, int x, int y, int w, byte color);
 
 	void sayAlfred(Common::String text);
-	void sayNPC(AnimSet *anim, Common::String text, byte color);
+	void sayNPC(Sprite *anim, Common::String text, byte color);
 
 	void frames();
 	void doAction(byte action, byte object);
 	void renderText(Common::Array<Common::String> lines, int color, int x, int y);
 	void drawAlfred(byte *buf);
-	void drawNextFrame(AnimSet *animSet);
+	void drawNextFrame(Sprite *animSet);
 	void changeCursor(Cursor cursor);
 	int isHotspotUnder(int x, int y);
 	Exit *isExitUnder(int x, int y);
-	AnimSet *isSpriteUnder(int x, int y);
+	Sprite *isSpriteUnder(int x, int y);
 	void showActionBalloon(int posx, int posy, int curFrame);
-	void drawTalkNPC(AnimSet *animSet);
+	void drawTalkNPC(Sprite *animSet);
 
 	void checkMouseHover();
 	void checkMouseClick(int x, int y);
@@ -217,6 +216,9 @@ public:
 		Common::Serializer s(stream, nullptr);
 		return syncGame(s);
 	}
+
+
+	void setScreen(int s, AlfredDirection dir);
 };
 
 extern PelrockEngine *g_engine;
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index ef15b1258ae..ba3372913a5 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -180,7 +180,7 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	}
 	_currentRoomConversations = roots;
 
-	Common::Array<AnimSet> anims = loadRoomAnimations(roomFile, roomOffset);
+	Common::Array<Sprite> anims = loadRoomAnimations(roomFile, roomOffset);
 
 	Common::Array<HotSpot> hotspots;
 	for (int i = 0; i < anims.size(); i++) {
@@ -234,7 +234,7 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	}
 }
 
-Common::Array<AnimSet> RoomManager::loadRoomAnimations(Common::File *roomFile, int roomOffset) {
+Common::Array<Sprite> RoomManager::loadRoomAnimations(Common::File *roomFile, int roomOffset) {
 	uint32_t pair_offset = roomOffset + (8 * 8);
 	// debug("Sprite pair offset position: %d", pair_offset);
 	roomFile->seek(pair_offset, SEEK_SET);
@@ -249,9 +249,9 @@ Common::Array<AnimSet> RoomManager::loadRoomAnimations(Common::File *roomFile, i
 	if (offset > 0 && size > 0) {
 		rleDecompress(data, size, 0, size, &pic, true);
 	} else {
-		return Common::Array<AnimSet>();
+		return Common::Array<Sprite>();
 	}
-	Common::Array<AnimSet> anims = Common::Array<AnimSet>();
+	Common::Array<Sprite> anims = Common::Array<Sprite>();
 	uint32_t spriteEnd = offset + size;
 
 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
@@ -262,52 +262,55 @@ Common::Array<AnimSet> RoomManager::loadRoomAnimations(Common::File *roomFile, i
 		byte *animData = new byte[44];
 		roomFile->seek(animOffset, SEEK_SET);
 		roomFile->read(animData, 44);
-		AnimSet animSet;
-		animSet.index = i;
-		animSet.x = animData[0] | (animData[1] << 8);
-		animSet.y = animData[2] | (animData[3] << 8);
-		animSet.w = animData[4];
-		animSet.h = animData[5];
-		animSet.extra = animData[6];
+		Sprite sprite;
+		sprite.index = i;
+		sprite.x = animData[0] | (animData[1] << 8);
+		sprite.y = animData[2] | (animData[3] << 8);
+		sprite.w = animData[4];
+		sprite.h = animData[5];
+		sprite.extra = animData[6];
 		// roomFile->skip(1); // reserved
-		animSet.numAnims = animData[8];
-		animSet.spriteType = animData[33];
-		animSet.actionFlags = animData[34];
-		animSet.isDisabled = animData[38];
-		if (animSet.numAnims == 0) {
+		sprite.numAnims = animData[8];
+		sprite.zOrder = animData[23];
+		sprite.spriteType = animData[33];
+		sprite.actionFlags = animData[34];
+		sprite.isDisabled = animData[38];
+		if (sprite.numAnims == 0) {
 			break;
 		}
-		animSet.animData = new Anim[animSet.numAnims];
+		sprite.animData = new Anim[sprite.numAnims];
 		// debug("AnimSet %d has %d sub-anims, type %d, actionFlags %d, isDisabled? %d", i, animSet.numAnims, animSet.spriteType, animSet.actionFlags, animSet.isDisabled);
 		int subAnimOffset = 10;
-		for (int j = 0; j < animSet.numAnims; j++) {
+		for (int j = 0; j < sprite.numAnims; j++) {
 
 			Anim anim;
-			anim.x = animSet.x;
-			anim.y = animSet.y;
-			anim.w = animSet.w;
-			anim.h = animSet.h;
+			anim.x = sprite.x;
+			anim.y = sprite.y;
+			anim.w = sprite.w;
+			anim.h = sprite.h;
 			anim.curFrame = 0;
 
 			anim.nframes = animData[subAnimOffset + j];
 			anim.loopCount = animData[subAnimOffset + 4 + j];
 			anim.speed = animData[subAnimOffset + 8 + j];
+			anim.movementFlags = animData[subAnimOffset + 14 + (j*2)] | (animData[subAnimOffset + 14 + (j*2) + 1] << 8);
 			anim.animData = new byte[anim.nframes];
 			if (anim.w > 0 && anim.h > 0 && anim.nframes > 0) {
 				uint32_t needed = anim.w * anim.h * anim.nframes;
 				anim.animData = new byte[needed];
 				Common::copy(pic + picOffset, pic + picOffset + needed, anim.animData);
-				animSet.animData[j] = anim;
-				// debug("  Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d loopCount=%d speed=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes, anim.loopCount, anim.speed);
+				sprite.animData[j] = anim;
+				debug("  Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d loopCount=%d speed=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes, anim.loopCount, anim.speed);
+				debug("  Movement flags: 0x%04X", anim.movementFlags);
 				picOffset += needed;
 			} else {
 				continue;
 				debug("Anim %d-%d: invalid dimensions, skipping", i, j);
 			}
-			animSet.animData[j] = anim;
+			sprite.animData[j] = anim;
 		}
 
-		anims.push_back(animSet);
+		anims.push_back(sprite);
 	}
 	return anims;
 }
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 3ec5fbf4ad1..2d081c21e2e 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -47,7 +47,7 @@ public:
 	}
 
 	Common::Array<HotSpot> _currentRoomHotspots;
-	Common::Array<AnimSet> _currentRoomAnims;
+	Common::Array<Sprite> _currentRoomAnims;
 	Common::Array<Exit> _currentRoomExits;
 	Common::Array<WalkBox> _currentRoomWalkboxes;
 	Common::Array<Description> _currentRoomDescriptions;
@@ -61,9 +61,10 @@ public:
 	Common::StringArray _roomNames;
 	byte _musicTrack = 0;
 	Common::Array<byte> _roomSfx;
+	byte _currentRoomNumber = 0;
 
 private:
-	Common::Array<AnimSet> loadRoomAnimations(Common::File *roomFile, int roomOffset);
+	Common::Array<Sprite> loadRoomAnimations(Common::File *roomFile, int roomOffset);
 	Common::Array<HotSpot> loadHotspots(Common::File *roomFile, int roomOffset);
 	Common::Array<Exit> loadExits(Common::File *roomFile, int roomOffset);
 	Common::Array<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 52bb901e52b..f5531cd98de 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -37,6 +37,8 @@
 
 namespace Pelrock {
 
+
+
 SoundManager::SoundManager(Audio::Mixer *mixer)
 	: _mixer(mixer), _currentVolume(255), _musicFile(nullptr) {
 	// TODO: Initialize sound manager
@@ -250,26 +252,31 @@ void SoundManager::loadSoundIndex() {
 	sonidosFile.close();
 }
 
-
-int gerRandomNumber() {
-
-
-}
+int RANDOM_THRESHOLD = 0x4000;
 
 int SoundManager::tick() {
 
 	soundFrameCounter++;
-	uint32 random = g_engine->getRandomNumber(1);
-	if(!random) {
+
+	uint16 rand1 = _rng.nextRandom();
+	// uint32 random = g_engine->getRandomNumber(1);
+	if(rand1 <= RANDOM_THRESHOLD){
+		// debug("No SFX this tick due to 50% random");
 		return -1;
 	}
-	if((soundFrameCounter & COUNTER_MASK) != COUNTER_MASK){
 
+	if((soundFrameCounter & COUNTER_MASK) != COUNTER_MASK){
+		// debug("No SFX this tick due to counter mask (counter = %d)", soundFrameCounter);
 		return -1;
 	}
+
+	uint16 rand2 = _rng.nextRandom();
+	int slot = rand2 & 3;
+	debug("Slot = %d (rand2 = %u)", slot, rand2);
+
 	soundFrameCounter = 0;
-	uint32 slot = g_engine->getRandomNumber(4);
-	return slot;
+	// uint32 slot = g_engine->getRandomNumber(4);
+	return slot + 1;
 }
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index c2a4372a35f..3be968743ea 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -154,6 +154,36 @@ static const uint COUNTER_MASK = 0x1F;
 
 const int kMaxChannels = 15;
 
+
+
+	class GameRNG {
+
+private:
+    uint32_t _state;
+public:
+    // LCG constants (from JUEGO.EXE @ 0x0002b12f)
+    static constexpr uint32_t MULTIPLIER = 0x41C64E6D; // 1103515245
+    static constexpr uint32_t INCREMENT  = 0x3039;     // 12345
+
+    GameRNG(uint32_t seed = 0) {
+        _state = seed & 0xFFFFFFFF;
+    }
+
+    // Generate next random number (0-32767)
+    uint16_t nextRandom() {
+        _state = (_state * MULTIPLIER + INCREMENT) & 0xFFFFFFFF;
+        return static_cast<uint16_t>((_state >> 16) & 0x7FFF);
+    }
+
+    uint32_t getState() const {
+        return _state;
+    }
+
+    void setState(uint32_t state) {
+        _state = state & 0xFFFFFFFF;
+    }
+};
+
 class SoundManager {
 public:
 	SoundManager(Audio::Mixer *mixer);
@@ -189,8 +219,12 @@ private:
 	Audio::SoundHandle _sfxHandles[kMaxChannels];
 	int soundFrameCounter = 0;
 	Common::HashMap<Common::String, SonidoFile> _soundMap;
+	GameRNG _rng = GameRNG(0);
 };
 
+
+
+
 } // End of namespace Pelrock
 
 #endif // PELROCK_SOUND_H
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 83fe1f8676d..c832ba20afe 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -139,8 +139,8 @@ typedef struct {
 } PathContext;
 
 struct Anim {
-	int x;
-	int y;
+	int16 x;
+	int16 y;
 	int w;
 	int h;
 	int nframes;
@@ -150,31 +150,33 @@ struct Anim {
 	byte loopCount;
 	byte speed;
 	byte elpapsedFrames = 0;
+	uint16 movementFlags = 0;
 };
 
 struct Exit {
-	uint16 x;
-	uint16 y;
+	int16 x;
+	int16 y;
 	byte w;
 	byte h;
 	uint16 targetRoom;
-	uint16 targetX;
-	uint16 targetY;
+	int16 targetX;
+	int16 targetY;
 	uint16 targetDir;
 	AlfredDirection dir;
 	byte flags;
 };
 
-struct AnimSet {
+struct Sprite {
 	int index; // number of the animation in the rooms
 	byte type;
-	int x;        // 0
-	int y;        // 2
+	int16 x;        // 0
+	int16 y;        // 2
 	int w;        // 4
 	int h;        // 5
 	byte extra;   // 6
 	int numAnims; // 8
 	int curAnimIndex = 0;
+	byte zOrder;
 	byte spriteType;  // 33
 	byte actionFlags; // 34
 	bool isDisabled;  // 38


Commit: ea20c30125dbd44e78ea12ddc62a4323f0f43e5c
    https://github.com/scummvm/scummvm/commit/ea20c30125dbd44e78ea12ddc62a4323f0f43e5c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:17+02:00

Commit Message:
PELROCK: Fixes walking algorithm

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/types.h
    engines/pelrock/util.cpp
    engines/pelrock/util.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 4095abd9f89..871ee128675 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -129,6 +129,11 @@ Common::Error PelrockEngine::run() {
 				case Common::KEYCODE_z:
 					showShadows = !showShadows;
 					break;
+				case Common::KEYCODE_y:
+					alfredState.x = 193;
+					alfredState.y = 382;
+					walkTo(377, 318);
+					break;
 				default:
 					break;
 				}
@@ -320,7 +325,7 @@ void PelrockEngine::frames() {
 
 		int soundIndex = _soundManager->tick();
 		if(soundIndex >= 0 && soundIndex < _room->_roomSfx.size()) {
-			debug("Playing SFX index %d", soundIndex);
+			// debug("Playing SFX index %d", soundIndex);
 			_soundManager->playSound(_room->_roomSfx[3 + soundIndex]);
 		}
 
@@ -334,7 +339,8 @@ void PelrockEngine::frames() {
 		}
 		switch (alfredState.animState) {
 		case ALFRED_WALKING: {
-			MovementStep step = _currentContext.movement_buffer[_current_step];
+
+			MovementStep step = _currentContext.movementBuffer[_currentStep];
 
 			if (step.distance_x > 0) {
 				if (step.flags & MOVE_RIGHT) {
@@ -364,13 +370,13 @@ void PelrockEngine::frames() {
 				step.distance_y -= MIN(alfredState.movementSpeed, step.distance_y);
 
 			if (step.distance_x <= 0 && step.distance_y <= 0) {
-				_current_step++;
-				if (_current_step >= _currentContext.movement_count) {
-					_current_step = 0;
+				_currentStep++;
+				if (_currentStep >= _currentContext.movementCount) {
+					_currentStep = 0;
 					alfredState.animState = ALFRED_IDLE;
 				}
 			} else {
-				_currentContext.movement_buffer[_current_step] = step;
+				_currentContext.movementBuffer[_currentStep] = step;
 			}
 
 			Exit *exit = isExitUnder(alfredState.x, alfredState.y);
@@ -471,6 +477,7 @@ void PelrockEngine::frames() {
 			// debug("Drawing walkbox %d", i);
 			WalkBox box = _room->_currentRoomWalkboxes[i];
 			drawRect(_screen, box.x, box.y, box.w, box.h, 150 + i);
+			_smallFont->drawString(_screen, Common::String::format("%d", i), box.x + 2, box.y + 2, 640, 14);
 		}
 		if (_curWalkTarget.x < 640 && _curWalkTarget.y < 400 && _curWalkTarget.x >= 0 && _curWalkTarget.y >= 0) {
 			_screen->setPixel(_curWalkTarget.x, _curWalkTarget.y, 100);
@@ -671,7 +678,10 @@ void applyMovement(int16_t *x, int16_t *y, /*int8_t *z,*/ uint16_t flags) {
 
 void PelrockEngine::drawNextFrame(Sprite *sprite) {
 	Anim &animData = sprite->animData[sprite->curAnimIndex];
-
+	if(sprite->zOrder == -1) {
+		// skips z0rder -1 sprites
+		return;
+	}
 
 	applyMovement(&(sprite->x), &(sprite->y), animData.movementFlags);
 	int x = sprite->x;
@@ -885,15 +895,15 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	drawSpriteToBuffer(_compositeBuffer, 640, _res->_popUpBalloon + (curFrame * kBalloonHeight * kBalloonWidth), posx, posy, kBalloonWidth, kBalloonHeight, 255);
 	Common::Array<VerbIcons> actions = availableActions(_currentHotspot);
 
-	drawSpriteToBuffer(_compositeBuffer, 640, _res->_verbIcons[LOOK], posx + 20, posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
+	// drawSpriteToBuffer(_compositeBuffer, 640, _res->_verbIcons[LOOK], posx + 20, posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
 	// Graphics::Surface rects;
 	// rects.create(kVerbIconWidth, kVerbIconHeight, Graphics::PixelFormat::createFormatCLUT8());
-	// drawRect(&rects, 0, 0, kVerbIconWidth, kVerbIconHeight, 255);
+	// drawRect(&rects, 0, 0, kVerbIconWidth, kVerbIconHeight, 1);
 
-	// blitSurfaceToBuffer(&rects, _compositeBuffer, 640, 480, posx + ver, posy + 20);
 
 	for (int i = 0; i < actions.size(); i++) {
 		drawSpriteToBuffer(_compositeBuffer, 640, _res->_verbIcons[actions[i]], posx + 20 + (i * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
+		// blitSurfaceToBuffer(&rects, _compositeBuffer, 640, 480, posx + 20 + (i * (kVerbIconWidth + 2)), posy + 20);
 	}
 }
 
@@ -925,81 +935,79 @@ void PelrockEngine::drawTalkNPC(Sprite *animSet) {
 }
 
 void PelrockEngine::walkTo(int x, int y) {
-	alfredState.animState = ALFRED_WALKING;
-	alfredState.curFrame = 0;
-
-	PathContext context = {NULL, NULL, NULL, 0, 0, 0};
+	_currentStep = 0;
+	PathContext context = {nullptr, nullptr, nullptr, 0, 0, 0};
 	pathFind(x, y, &context);
 	_currentContext = context;
+	alfredState.animState = ALFRED_WALKING;
+	alfredState.curFrame = 0;
 }
 
-bool PelrockEngine::pathFind(int x, int y, PathContext *context) {
+bool PelrockEngine::pathFind(int targetX, int targetY, PathContext *context) {
 
-	if (context->path_buffer == NULL) {
-		context->path_buffer = (uint8_t *)malloc(MAX_PATH_LENGTH);
+	if (context->pathBuffer == NULL) {
+		context->pathBuffer = (uint8_t *)malloc(MAX_PATH_LENGTH);
 	}
-	if (context->movement_buffer == NULL) {
-		context->movement_buffer = (MovementStep *)malloc(MAX_MOVEMENT_STEPS * sizeof(MovementStep));
+	if (context->movementBuffer == NULL) {
+		context->movementBuffer = (MovementStep *)malloc(MAX_MOVEMENT_STEPS * sizeof(MovementStep));
 	}
 
 	int startX = alfredState.x;
 	int startY = alfredState.y;
-	Common::Point target = calculateWalkTarget(x, y);
-	x = target.x;
-	y = target.y;
-	debug("Startx= %d, starty= %d, destx= %d, desty= %d", startX, startY, x, y);
+	Common::Point target = calculateWalkTarget(targetX, targetY);
+	targetX = target.x;
+	targetY = target.y;
+	debug("Startx= %d, starty= %d, destx= %d, desty= %d", startX, startY, targetX, targetY);
 
-	uint8_t start_box = find_walkbox_for_point(startX, startY);
-	uint8_t dest_box = find_walkbox_for_point(x, y);
+	uint8_t startBox = findWalkboxForPoint(startX, startY);
+	uint8_t destBox = findWalkboxForPoint(targetX, targetY);
 
-	debug("Pathfinding from (%d, %d) in box %d to (%d, %d) in box %d\n",
-		  startX, startY, start_box,
-		  x, y, dest_box);
+	debug("Pathfinding from (%d, %d) in box %d to (%d, %d) in box %d\n", startX, startY, startBox, targetX, targetY, destBox);
 	// Check if both points are in valid walkboxes
-	if (start_box == 0xFF || dest_box == 0xFF) {
+	if (startBox == 0xFF || destBox == 0xFF) {
 		debug("Error: Start or destination not in any walkbox\n");
 		return false;
 	}
 	// Special case: same walkbox
-	if (start_box == dest_box) {
+	if (startBox == destBox) {
 		// Generate direct movement
 		MovementStep direct_step;
 		direct_step.flags = 0;
-		if (startX < x) {
-			direct_step.distance_x = x - startX;
+		if (startX < targetX) {
+			direct_step.distance_x = targetX - startX;
 			direct_step.flags |= MOVE_RIGHT;
 		} else {
-			direct_step.distance_x = startX - x;
+			direct_step.distance_x = startX - targetX;
 			direct_step.flags |= MOVE_LEFT;
 		}
 
-		if (startY < y) {
-			direct_step.distance_y = y - startY;
+		if (startY < targetY) {
+			direct_step.distance_y = targetY - startY;
 			direct_step.flags |= MOVE_DOWN;
 		} else {
-			direct_step.distance_y = startY - y;
+			direct_step.distance_y = startY - targetY;
 			direct_step.flags |= MOVE_UP;
 		}
 
-		context->movement_buffer[0] = direct_step;
-		context->movement_count = 1;
+		context->movementBuffer[0] = direct_step;
+		context->movementCount = 1;
 	} else {
 		// Build walkbox path
-		context->path_length = build_walkbox_path(start_box, dest_box,
-												  context->path_buffer);
-
-		if (context->path_length == 0) {
+		context->pathLength = buildWalkboxPath(startBox, destBox, context->pathBuffer);
+		debug("Walkbox path to point");
+		for(int i = 0; i < context->pathLength; i++) {
+			debug("Walkbox %d: %d", i, context->pathBuffer[i]);
+		}
+		if (context->pathLength == 0) {
 			debug("Error: No path found\n");
 			return false;
 		}
 
 		// Generate movement steps
-		context->movement_count = generate_movement_steps(
-			context->path_buffer,
-			context->path_length,
-			startX, startY,
-			x, y,
-			context->movement_buffer);
+		context->movementCount = generateMovementSteps(context->pathBuffer, context->pathLength, startX, startY, targetX, targetY, context->movementBuffer);
+		for(int i = 0; i < context->movementCount; i++) {
+			debug("Movement step %d: flags=\"%s\", dx=%d, dy=%d", i, printMovementFlags(context->movementBuffer[i].flags).c_str(), context->movementBuffer[i].distance_x, context->movementBuffer[i].distance_y);
+		}
 	}
 	return true;
 }
@@ -1007,7 +1015,7 @@ bool PelrockEngine::pathFind(int x, int y, PathContext *context) {
 /**
  * Calculate movement needed to reach a target within a walkbox
  */
-void calculate_movement_to_target(uint16_t current_x, uint16_t current_y,
+void calculateMovementToTarget(uint16_t current_x, uint16_t current_y,
 								  uint16_t target_x, uint16_t target_y,
 								  WalkBox *box,
 								  MovementStep *step) {
@@ -1042,39 +1050,37 @@ void calculate_movement_to_target(uint16_t current_x, uint16_t current_y,
  * Generate movement steps from walkbox path
  * Returns: number of movement steps generated
  */
-uint16_t PelrockEngine::generate_movement_steps(uint8_t *path_buffer,
-												uint16_t path_length,
-												uint16_t start_x, uint16_t start_y,
-												uint16_t dest_x, uint16_t dest_y,
-												MovementStep *movement_buffer) {
-	uint16_t current_x = start_x;
-	uint16_t current_y = start_y;
-	uint16_t movement_index = 0;
+uint16_t PelrockEngine::generateMovementSteps(uint8_t *pathBuffer,
+												uint16_t pathLength,
+												uint16_t startX, uint16_t startY,
+												uint16_t destX, uint16_t destY,
+												MovementStep *movementBuffer) {
+	uint16_t currentX = startX;
+	uint16_t currentY = startY;
+	uint16_t movementIndex = 0;
 
 	// Generate movements for each walkbox in path
-	for (uint16_t i = 0; i < path_length && path_buffer[i] != PATH_END; i++) {
-		uint8_t box_index = path_buffer[i];
-		WalkBox *box = &_room->_currentRoomWalkboxes[box_index];
+	for (uint16_t i = 0; i < pathLength && pathBuffer[i] != PATH_END; i++) {
+		uint8_t boxIndex = pathBuffer[i];
+		WalkBox *box = &_room->_currentRoomWalkboxes[boxIndex];
 
 		MovementStep step;
-		calculate_movement_to_target(current_x, current_y,
-									 dest_x, dest_y,
-									 box, &step);
+		calculateMovementToTarget(currentX, currentY, destX, destY, box, &step);
 
 		if (step.distance_x > 0 || step.distance_y > 0) {
-			movement_buffer[movement_index++] = step;
+			movementBuffer[movementIndex++] = step;
 
 			// Update current position
 			if (step.flags & MOVE_RIGHT) {
-				current_x = box->x;
+				currentX = box->x;
 			} else if (step.flags & MOVE_LEFT) {
-				current_x = box->x + box->w;
+				currentX = box->x + box->w;
 			}
 
 			if (step.flags & MOVE_DOWN) {
-				current_y = box->y;
+				currentY = box->y;
 			} else if (step.flags & MOVE_UP) {
-				current_y = box->y + box->h;
+				currentY = box->y + box->h;
 			}
 		}
 	}
@@ -1083,77 +1089,75 @@ uint16_t PelrockEngine::generate_movement_steps(uint8_t *path_buffer,
 	MovementStep final_step;
 	final_step.flags = 0;
 
-	if (current_x < dest_x) {
-		final_step.distance_x = dest_x - current_x;
+	if (currentX < destX) {
+		final_step.distance_x = destX - currentX;
 		final_step.flags |= MOVE_RIGHT;
-	} else if (current_x > dest_x) {
-		final_step.distance_x = current_x - dest_x;
+	} else if (currentX > destX) {
+		final_step.distance_x = currentX - destX;
 		final_step.flags |= MOVE_LEFT;
 	} else {
 		final_step.distance_x = 0;
 	}
 
-	if (current_y < dest_y) {
-		final_step.distance_y = dest_y - current_y;
+	if (currentY < destY) {
+		final_step.distance_y = destY - currentY;
 		final_step.flags |= MOVE_DOWN;
-	} else if (current_y > dest_y) {
-		final_step.distance_y = current_y - dest_y;
+	} else if (currentY > destY) {
+		final_step.distance_y = currentY - destY;
 		final_step.flags |= MOVE_UP;
 	} else {
 		final_step.distance_y = 0;
 	}
 
 	if (final_step.distance_x > 0 || final_step.distance_y > 0) {
-		movement_buffer[movement_index++] = final_step;
+		movementBuffer[movementIndex++] = final_step;
 	}
 
-	return movement_index;
+	return movementIndex;
 }
 
-uint16_t PelrockEngine::build_walkbox_path(
-	uint8_t start_box,
-	uint8_t dest_box,
-	uint8_t *path_buffer) {
-	uint16_t path_index = 0;
-	uint8_t current_box = start_box;
+uint16_t PelrockEngine::buildWalkboxPath(uint8_t startBox, uint8_t destBox, uint8_t *pathBuffer) {
+
+	uint16_t pathIndex = 0;
+	uint8_t currentBox = startBox;
 
 	// Initialize path with start walkbox
-	path_buffer[path_index++] = start_box;
+	pathBuffer[pathIndex++] = startBox;
 
 	// Clear visited flags
-	clear_visited_flags();
+	clearVisitedFlags();
 
 	// Breadth-first search through walkboxes
-	while (current_box != dest_box && path_index < MAX_PATH_LENGTH - 1) {
-		uint8_t next_box = get_adjacent_walkbox(current_box);
+	while (currentBox != destBox && pathIndex < MAX_PATH_LENGTH - 1) {
+		uint8_t nextBox = getAdjacentWalkbox(currentBox);
 
-		if (next_box == 0xFF) {
+		if (nextBox == 0xFF) {
 			// Dead end - backtrack
-			if (path_index > 1) {
-				path_index--;
-				current_box = path_buffer[path_index - 1];
+			if (pathIndex > 1) {
+				pathIndex--;
+				currentBox = pathBuffer[pathIndex - 1];
 			} else {
 				// No path exists
 				return 0;
 			}
-		} else if (next_box == dest_box) {
+		} else if (nextBox == destBox) {
 			// Found destination
-			path_buffer[path_index++] = dest_box;
+			pathBuffer[pathIndex++] = destBox;
 			break;
 		} else {
 			// Continue searching
-			path_buffer[path_index++] = next_box;
-			current_box = next_box;
+			pathBuffer[pathIndex++] = nextBox;
+			currentBox = nextBox;
 		}
 	}
 
 	// Terminate path
-	path_buffer[path_index] = PATH_END;
+	pathBuffer[pathIndex] = PATH_END;
 
-	return path_index;
+	return pathIndex;
 }
 
-void PelrockEngine::clear_visited_flags() {
+void PelrockEngine::clearVisitedFlags() {
 	for (int i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
 		_room->_currentRoomWalkboxes[i].flags = 0;
 	}
@@ -1162,31 +1166,31 @@ void PelrockEngine::clear_visited_flags() {
 /**
  * Check if two walkboxes overlap or touch (are adjacent)
  */
-bool walkboxes_adjacent(WalkBox *box1, WalkBox *box2) {
+bool areWalkboxesAdjacent(WalkBox *box1, WalkBox *box2) {
 	uint16_t box1_x_max = box1->x + box1->w;
 	uint16_t box1_y_max = box1->y + box1->h;
 	uint16_t box2_x_max = box2->x + box2->w;
 	uint16_t box2_y_max = box2->y + box2->h;
 
 	// Check if X ranges overlap
-	bool x_overlap = (box1->x <= box2_x_max) && (box2->x <= box1_x_max);
+	bool xOverlap = (box1->x <= box2_x_max) && (box2->x <= box1_x_max);
 
 	// Check if Y ranges overlap
-	bool y_overlap = (box1->y <= box2_y_max) && (box2->y <= box1_y_max);
+	bool yOverlap = (box1->y <= box2_y_max) && (box2->y <= box1_y_max);
 
-	return x_overlap && y_overlap;
+	return xOverlap && yOverlap;
 }
 
-uint8_t PelrockEngine::get_adjacent_walkbox(uint8_t current_box_index) {
-	WalkBox *current_box = &_room->_currentRoomWalkboxes[current_box_index];
+uint8_t PelrockEngine::getAdjacentWalkbox(uint8_t currentBoxIndex) {
+	WalkBox *currentBox = &_room->_currentRoomWalkboxes[currentBoxIndex];
 
 	// Mark current walkbox as visited
-	current_box->flags = 0x01;
+	currentBox->flags = 0x01;
 
 	// Search for adjacent unvisited walkbox
 	for (uint8_t i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
 		// Skip current walkbox
-		if (i == current_box_index) {
+		if (i == currentBoxIndex) {
 			continue;
 		}
 
@@ -1196,7 +1200,7 @@ uint8_t PelrockEngine::get_adjacent_walkbox(uint8_t current_box_index) {
 		}
 
 		// Check if walkboxes are adjacent
-		if (walkboxes_adjacent(current_box, &_room->_currentRoomWalkboxes[i])) {
+		if (areWalkboxesAdjacent(currentBox, &_room->_currentRoomWalkboxes[i])) {
 			return i;
 		}
 	}
@@ -1204,16 +1208,16 @@ uint8_t PelrockEngine::get_adjacent_walkbox(uint8_t current_box_index) {
 	return 0xFF; // No adjacent walkbox found
 }
 
-bool PelrockEngine::point_in_walkbox(WalkBox *box, uint16_t x, uint16_t y) {
+bool PelrockEngine::isPointInWalkbox(WalkBox *box, uint16_t x, uint16_t y) {
 	return (x >= box->x &&
 			x <= box->x + box->w &&
 			y >= box->y &&
 			y <= box->y + box->h);
 }
 
-uint8_t PelrockEngine::find_walkbox_for_point(uint16_t x, uint16_t y) {
+uint8_t PelrockEngine::findWalkboxForPoint(uint16_t x, uint16_t y) {
 	for (uint8_t i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
-		if (point_in_walkbox(&_room->_currentRoomWalkboxes[i], x, y)) {
+		if (isPointInWalkbox(&_room->_currentRoomWalkboxes[i], x, y)) {
 			return i;
 		}
 	}
@@ -1238,10 +1242,9 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 		}
 		for (int i = 1; i < actions.size(); i++) {
 
-			int x = _popupX + 20 + (i * (kVerbIconWidth + 2));
-			int y = _popupY + 20;
-			Common::Rect actionRect = Common::Rect(x, y, x + kVerbIconWidth, y + kVerbIconHeight);
-
+			int actionX = _popupX + 20 + (i * (kVerbIconWidth + 2));
+			int actionY = _popupY + 20;
+			Common::Rect actionRect = Common::Rect(actionX, actionY, actionX + kVerbIconWidth, actionY + kVerbIconHeight);
 			if (actionRect.contains(x, y)) {
 				debug("Action %d clicked", actions[i]);
 				doAction(actions[i], _currentHotspot->extra);
@@ -1541,7 +1544,7 @@ void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 	}
 	alfredState.direction = dir;
 	alfredState.animState = ALFRED_IDLE;
-	_current_step = 0;
+	_currentStep = 0;
 	int roomOffset = number * kRoomStructSize;
 	alfredState.curFrame = 0;
 	byte *palette = new byte[256 * 3];
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 0529e446045..c2121a17301 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -62,12 +62,12 @@ private:
 
 	void walkTo(int x, int y);
 	bool pathFind(int x, int y, PathContext *context);
-	uint8_t find_walkbox_for_point(uint16_t x, uint16_t y);
-	bool point_in_walkbox(WalkBox *box, uint16_t x, uint16_t y);
-	uint16_t build_walkbox_path(uint8_t start_box, uint8_t dest_box, uint8_t *path_buffer);
-	uint8_t get_adjacent_walkbox(uint8_t current_box_index);
-	void clear_visited_flags();
-	uint16_t generate_movement_steps(uint8_t *path_buffer,
+	uint8_t findWalkboxForPoint(uint16_t x, uint16_t y);
+	bool isPointInWalkbox(WalkBox *box, uint16_t x, uint16_t y);
+	uint16_t buildWalkboxPath(uint8_t start_box, uint8_t dest_box, uint8_t *path_buffer);
+	uint8_t getAdjacentWalkbox(uint8_t current_box_index);
+	void clearVisitedFlags();
+	uint16_t generateMovementSteps(uint8_t *path_buffer,
 									 uint16_t path_length,
 									 uint16_t start_x, uint16_t start_y,
 									 uint16_t dest_x, uint16_t dest_y,
@@ -109,7 +109,7 @@ private:
 	Common::Array<Common::Array<int>> _heightScalingTable;
 
 	// walking
-	int _current_step = 0;
+	int _currentStep = 0;
 	PathContext _currentContext;
 
 	// text display
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index ba3372913a5..2c316012176 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -170,13 +170,13 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 
 	int roomOffset = roomNumber * kRoomStructSize;
 	Common::Array<Description> descriptions = loadRoomDescriptions(roomFile, roomOffset, outPos);
-	debug("After decsriptions, position is %d", outPos);
+	// debug("After decsriptions, position is %d", outPos);
 	Common::Array<ConversationNode> roots = loadConversations(roomFile, roomOffset, outPos);
 	for (int i = 0; i < roots.size(); i++) {
 		if (roots[i].text.empty()) {
 			continue;
 		}
-		debug("Conversation %d: %s", i, roots[i].text.c_str());
+		// debug("Conversation %d: %s", i, roots[i].text.c_str());
 	}
 	_currentRoomConversations = roots;
 
@@ -202,7 +202,7 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	ScalingParams scalingParams = loadScalingParams(roomFile, roomOffset);
 	Common::Array<WalkBox> walkboxes = loadWalkboxes(roomFile, roomOffset);
 
-	debug("total descriptions = %d, anims = %d, hotspots = %d", descriptions.size(), anims.size(), staticHotspots.size());
+	// debug("total descriptions = %d, anims = %d, hotspots = %d", descriptions.size(), anims.size(), staticHotspots.size());
 	for (int i = 0; i < staticHotspots.size(); i++) {
 		HotSpot hotspot = staticHotspots[i];
 		hotspot.index = anims.size() + i;
@@ -300,12 +300,12 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(Common::File *roomFile, in
 				anim.animData = new byte[needed];
 				Common::copy(pic + picOffset, pic + picOffset + needed, anim.animData);
 				sprite.animData[j] = anim;
-				debug("  Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d loopCount=%d speed=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes, anim.loopCount, anim.speed);
-				debug("  Movement flags: 0x%04X", anim.movementFlags);
+				// debug("  Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d loopCount=%d speed=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes, anim.loopCount, anim.speed);
+				// debug("  Movement flags: 0x%04X", anim.movementFlags);
 				picOffset += needed;
 			} else {
 				continue;
-				debug("Anim %d-%d: invalid dimensions, skipping", i, j);
+				// debug("Anim %d-%d: invalid dimensions, skipping", i, j);
 			}
 			sprite.animData[j] = anim;
 		}
@@ -461,7 +461,7 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 	talkFile.read(&talkHeader.unknown5, 2);
 	talkHeader.numFramesAnimB = talkFile.readByte();
 	talkFile.read(&talkHeader.unknown6, 29);
-	debug("Talking anim header for room %d: spritePointer=%d, wA=%d, hA=%d, framesA=%d, wB=%d, hB=%d, framesB=%d", roomNumber, talkHeader.spritePointer, talkHeader.wAnimA, talkHeader.hAnimA, talkHeader.numFramesAnimA, talkHeader.wAnimB, talkHeader.hAnimB, talkHeader.numFramesAnimB);
+	// debug("Talking anim header for room %d: spritePointer=%d, wA=%d, hA=%d, framesA=%d, wB=%d, hB=%d, framesB=%d", roomNumber, talkHeader.spritePointer, talkHeader.wAnimA, talkHeader.hAnimA, talkHeader.numFramesAnimA, talkHeader.wAnimB, talkHeader.hAnimB, talkHeader.numFramesAnimB);
 
 	if (talkHeader.spritePointer == 0) {
 		debug("No talking animation for room %d", roomNumber);
@@ -960,7 +960,7 @@ byte RoomManager::loadMusicTrackForRoom(Common::File *roomFile, int roomOffset)
 
 	roomFile->seek(pair9_data_offset, SEEK_SET);
 	byte musicTrack = roomFile->readByte();
-	debug("Music track for room at offset %d is %d", roomOffset, musicTrack);
+	// debug("Music track for room at offset %d is %d", roomOffset, musicTrack);
 	return musicTrack > 0 ? musicTrack + 1 : 0;
 }
 
@@ -976,7 +976,7 @@ Common::Array<byte> RoomManager::loadRoomSfx(Common::File *roomFile, int roomOff
 	for (int i = 0; i < kNumSfxPerRoom; i++) {
 		byte sfx = roomFile->readByte();
 		roomSfx[i] = sfx;
-		debug("SFX %d for room at offset %d is %d (%s)", i, roomOffset, sfx, SOUND_FILENAMES[sfx]);
+		// debug("SFX %d for room at offset %d is %d (%s)", i, roomOffset, sfx, SOUND_FILENAMES[sfx]);
 	}
 	return roomSfx;
 }
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index f5531cd98de..22e41377808 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -50,7 +50,7 @@ SoundManager::~SoundManager() {
 }
 
 void SoundManager::playSound(byte index, int volume) {
-	debug("Playing sound index %d (%s)", index, SOUND_FILENAMES[index]);
+	// debug("Playing sound index %d (%s)", index, SOUND_FILENAMES[index]);
 	auto it = _soundMap.find(SOUND_FILENAMES[index]);
 	if (it != _soundMap.end()) {
 		playSound(it->_value, volume);
@@ -272,7 +272,7 @@ int SoundManager::tick() {
 
 	uint16 rand2 = _rng.nextRandom();
 	int slot = rand2 & 3;
-	debug("Slot = %d (rand2 = %u)", slot, rand2);
+	// debug("Slot = %d (rand2 = %u)", slot, rand2);
 
 	soundFrameCounter = 0;
 	// uint32 slot = g_engine->getRandomNumber(4);
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index c832ba20afe..e212ae31c3a 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -130,11 +130,11 @@ typedef struct {
  * Pathfinding context
  */
 typedef struct {
-	uint8_t *path_buffer;          // Sequence of walkbox indices
-	MovementStep *movement_buffer; // Array of movement steps
+	uint8_t *pathBuffer;          // Sequence of walkbox indices
+	MovementStep *movementBuffer; // Array of movement steps
 	uint8_t *compressed_path;      // Final compressed path
-	uint16_t path_length;
-	uint16_t movement_count;
+	uint16_t pathLength;
+	uint16_t movementCount;
 	uint16_t compressed_length;
 } PathContext;
 
@@ -176,7 +176,7 @@ struct Sprite {
 	byte extra;   // 6
 	int numAnims; // 8
 	int curAnimIndex = 0;
-	byte zOrder;
+	int8 zOrder;
 	byte spriteType;  // 33
 	byte actionFlags; // 34
 	bool isDisabled;  // 38
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 46e3e532e4a..72e071f3256 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -20,8 +20,9 @@
  */
 #include "common/stream.h"
 
-#include "pelrock/util.h"
 #include "pelrock/types.h"
+#include "pelrock/util.h"
+#include "util.h"
 
 namespace Pelrock {
 
@@ -34,21 +35,42 @@ void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byt
 }
 
 void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color) {
-	// debug("Drawing rect at (%d,%d) w=%d h=%d color=%d", x, y, w, h, color);
 	surface->drawLine(x, y, x + w, y, color);
 	surface->drawLine(x, y + h, x + w, y + h, color);
 	surface->drawLine(x, y, x, y + h, color);
 	surface->drawLine(x + w, y, x + w, y + h, color);
 }
 
+Common::String printMovementFlags(uint8_t flags) {
+	Common::String result;
+	if (flags & MOVE_HORIZ) {
+		result += "HORIZ ";
+	}
+	if (flags & MOVE_VERT) {
+		result += "VERT ";
+	}
+	if (flags & MOVE_DOWN) {
+		result += "DOWN ";
+	}
+	if (flags & MOVE_LEFT) {
+		result += "LEFT ";
+	}
+	if (flags & MOVE_UP) {
+		result += "UP ";
+	}
+	if (flags & MOVE_RIGHT) {
+		result += "RIGHT ";
+	}
+	return result;
+}
+
 size_t rleDecompress(
 	const uint8_t *input,
 	size_t inputSize,
 	uint32_t offset,
 	uint32_t expectedSize,
 	uint8_t **out_data,
-    bool untilBuda
-) {
+	bool untilBuda) {
 	// // Check for uncompressed markers
 	if (inputSize == 0x8000 || inputSize == 0x6800) {
 		*out_data = (uint8_t *)malloc(inputSize);
@@ -87,26 +109,26 @@ size_t rleDecompress(
 		uint8_t value = input[pos + 1];
 
 		for (int i = 0; i < count; i++) {
-			 // If in untilBuda mode, grow buffer as needed
-            if (untilBuda && result_size >= bufferCapacity) {
-                bufferCapacity *= 2;
-                uint8_t *newBuf = (uint8_t *)realloc(*out_data, bufferCapacity);
-                if (!newBuf) {
-                    free(*out_data);
-                    *out_data = nullptr;
-                    return 0;
-                }
-                *out_data = newBuf;
-            }
+			// If in untilBuda mode, grow buffer as needed
+			if (untilBuda && result_size >= bufferCapacity) {
+				bufferCapacity *= 2;
+				uint8_t *newBuf = (uint8_t *)realloc(*out_data, bufferCapacity);
+				if (!newBuf) {
+					free(*out_data);
+					*out_data = nullptr;
+					return 0;
+				}
+				*out_data = newBuf;
+			}
 			// debug("Pos = %zu, writing value %02X", result_size, value);
 			(*out_data)[result_size++] = value;
 		}
 
 		pos += 2;
-    	// In fixed size mode, stop when we reach expected size
-        if (!untilBuda && result_size >= expectedSize) {
-            break;
-        }
+		// In fixed size mode, stop when we reach expected size
+		if (!untilBuda && result_size >= expectedSize) {
+			break;
+		}
 	}
 
 	return result_size;
@@ -163,6 +185,22 @@ void drawSpriteToBuffer(byte *buffer, int bufferWidth,
 	}
 }
 
+void blitSurfaceToBuffer(Graphics::Surface *surface, byte *buffer, int bufferWidth, int bufferHeight, int destX, int destY) {
+	for (int y = 0; y < surface->h; y++) {
+		for (int x = 0; x < surface->w; x++) {
+			int px = destX + x;
+			int py = destY + y;
+			if (px >= 0 && px < bufferWidth && py >= 0 && py < bufferHeight) {
+
+				byte pixel = *((byte *)surface->getBasePtr(x, y));
+				if (pixel != 0) {
+					buffer[py * bufferWidth + px] = pixel;
+				}
+			}
+		}
+	}
+}
+
 void extractSingleFrame(byte *source, byte *dest, int frameIndex, int frameWidth, int frameHeight) {
 	for (int y = 0; y < frameHeight; y++) {
 		for (int x = 0; x < frameWidth; x++) {
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index 44b61c094c5..8e3de21718f 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -41,6 +41,7 @@ void blitSurfaceToBuffer(Graphics::Surface *surface, byte *buffer, int bufferWid
 void extractSingleFrame(byte *source, byte *dest, int frameIndex, int frameWidth, int frameHeight);
 void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byte color);
 void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color);
+Common::String printMovementFlags(uint8_t flags);
 Common::String joinStrings(const Common::Array<Common::String> &strings, const Common::String &separator);
 } // End of namespace Pelrock
 #endif // PELROCK_UTIL_H


Commit: ac84939fbbb0149a4bfec740a71ee2599565377b
    https://github.com/scummvm/scummvm/commit/ac84939fbbb0149a4bfec740a71ee2599565377b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:17+02:00

Commit Message:
PELROCK: Refactor actions

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 871ee128675..07daaf682f4 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -220,35 +220,6 @@ void PelrockEngine::loadAnims() {
 	_res->loadAlfredAnims();
 }
 
-void PelrockEngine::talk(byte object) {
-	debug("Talking to object %d", object);
-	if (_room->_currentRoomConversations.size() == 0)
-		return;
-
-	Sprite *animSet;
-	for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
-		if (_room->_currentRoomAnims[i].extra == object) {
-			animSet = &_room->_currentRoomAnims[i];
-		}
-	}
-
-	ConversationNode selectedNode = _room->_currentRoomConversations[0];
-
-	bool isNPC = selectedNode.speakerId != 13;
-	if (isNPC) {
-		sayNPC(animSet, selectedNode.text, selectedNode.speakerId);
-	}
-	// for(int i= 0; i< _currentRoomConversations.size(); i++) {
-	// _currentRoomConversations
-	// }
-
-	// showDescription(_currentRoomConversations[0].text, x, y, _currentRoomConversations[0].speakerId);
-	// for(int i = 0; i < _currentRoomConversations[0].choices.size(); i++) {
-	// 	int idx = _currentRoomConversations.size() - 1 - i;
-	// 	_smallFont->drawString(_screen, _currentRoomConversations[0].choices[idx].text.c_str(), 0, 400 - ((i + 1) * 12), 640, 14);
-	// }
-}
-
 void PelrockEngine::displayChoices(Common::Array<Common::String> choices, byte *compositeBuffer) {
 	int overlayHeight = choices.size() * kChoiceHeight + 2;
 	int overlayY = 400 - overlayHeight;
@@ -284,36 +255,32 @@ void PelrockEngine::putBackgroundSlice(int x, int y, int w, int h, byte *slice)
 	}
 }
 
-Common::Array<VerbIcons> PelrockEngine::availableActions(HotSpot *hotspot) {
-	Common::Array<VerbIcons> verbs;
+Common::Array<VerbIcon> PelrockEngine::availableActions(HotSpot *hotspot) {
+	if(hotspot == nullptr) {
+		return Common::Array<VerbIcon>();
+	}
+	Common::Array<VerbIcon> verbs;
 	verbs.push_back(LOOK);
 
 	if (hotspot->type & 1) {
-		debug("Hotspot allows OPEN action");
 		verbs.push_back(OPEN);
 	}
 	if (hotspot->type & 2) {
-		debug("Hotspot allows CLOSE action");
 		verbs.push_back(CLOSE);
 	}
 	if (hotspot->type & 4) {
-		debug("Hotspot allows UNKNOWN action");
 		verbs.push_back(UNKNOWN);
 	}
 	if (hotspot->type & 8) {
-		debug("Hotspot allows PICKUP action");
 		verbs.push_back(PICKUP);
 	}
 	if (hotspot->type & 16) {
-		debug("Hotspot allows TALK action");
 		verbs.push_back(TALK);
 	}
 	if (hotspot->type & 32) {
-		debug("Hotspot allows WALK action");
 		verbs.push_back(PUSH);
 	}
 	if (hotspot->type & 128) {
-		debug("Hotspot allows PULL action");
 		verbs.push_back(PULL);
 	}
 	return verbs;
@@ -324,7 +291,7 @@ void PelrockEngine::frames() {
 	if (_chronoManager->_gameTick) {
 
 		int soundIndex = _soundManager->tick();
-		if(soundIndex >= 0 && soundIndex < _room->_roomSfx.size()) {
+		if (soundIndex >= 0 && soundIndex < _room->_roomSfx.size()) {
 			// debug("Playing SFX index %d", soundIndex);
 			_soundManager->playSound(_room->_roomSfx[3 + soundIndex]);
 		}
@@ -337,6 +304,12 @@ void PelrockEngine::frames() {
 			Sprite &animSet = _room->_currentRoomAnims[i];
 			drawNextFrame(&animSet);
 		}
+		// if(alfredState.animState == ALFRED_IDLE && alfredState.nextState != ALFRED_IDLE) {
+		// 	alfredState.animState = alfredState.nextState;
+		// 	alfredState.nextState = ALFRED_IDLE;
+		// 	alfredState.curFrame = 0;
+		// }
+
 		switch (alfredState.animState) {
 		case ALFRED_WALKING: {
 
@@ -424,19 +397,6 @@ void PelrockEngine::frames() {
 		}
 
 		if (_displayPopup) {
-
-			// byte *bgDialog = new byte[kBalloonWidth * kBalloonHeight];
-			// for (int j = 0; j < kBalloonWidth; j++) {
-			// 	for (int i = 0; i < kBalloonHeight; i++) {
-			// 		int idx = i * kBalloonWidth + j;
-			// 		if (_popupY + i < 400 && _popupX + j < 640) {
-			// 			*(bgDialog + idx) = _currentBackground[(_popupY + i) * 640 + (_popupX + j)];
-			// 		}
-			// 	}
-			// }
-			// if (_bgPopupBalloon != nullptr) {
-			// 	putBackgroundSlice(_popupX, _popupY, kBalloonWidth, kBalloonHeight, _bgPopupBalloon);
-			// }
 			showActionBalloon(_popupX, _popupY, _currentPopupFrame);
 			if (_currentPopupFrame < 3) {
 				_currentPopupFrame++;
@@ -452,8 +412,9 @@ void PelrockEngine::frames() {
 
 		memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
 
-		if (alfredState.animState != ALFRED_WALKING && !_currentTextPages.empty()) {
+		if (alfredState.animState == ALFRED_TALKING && !_currentTextPages.empty()) {
 			if (_chronoManager->_textTtl > 0) {
+				_textPos = Common::Point(alfredState.x, alfredState.y - kAlfredFrameHeight - 10);
 				renderText(_currentTextPages[_currentTextPageIndex], _textColor, _textPos.x, _textPos.y);
 			} else if (_currentTextPageIndex < _currentTextPages.size() - 1) {
 				_currentTextPageIndex++;
@@ -472,6 +433,13 @@ void PelrockEngine::frames() {
 			}
 		}
 
+		if(alfredState.animState == ALFRED_IDLE && alfredState.nextState != ALFRED_IDLE) {
+			// debug("Switching Alfred state from IDLE to %d", alfredState.nextState);
+			alfredState.animState = alfredState.nextState;
+			alfredState.nextState = ALFRED_IDLE;
+			alfredState.curFrame = 0;
+		}
+
 		// debug("Drawing walkboxes..., %d, _currentRoomWalkboxes.size()=%d",  _currentRoomWalkboxes.size(), _currentRoomWalkboxes.size());
 		for (int i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
 			// debug("Drawing walkbox %d", i);
@@ -502,17 +470,60 @@ void PelrockEngine::frames() {
 		if (showShadows) {
 			memcpy(_screen->getPixels(), _room->_pixelsShadows, 640 * 400);
 		}
-		_smallFont->drawString(_screen, Common::String::format("Room number: %d",_room->_currentRoomNumber), 0, 4, 640, 13);
+		_smallFont->drawString(_screen, Common::String::format("Room number: %d", _room->_currentRoomNumber), 0, 4, 640, 13);
 		_screen->markAllDirty();
 
 		// _screen->update();
 	}
 }
 
-void PelrockEngine::doAction(byte action, byte object) {
-	if (action == TALK) {
-		talk(object);
+void PelrockEngine::doAction(byte action, HotSpot *hotspot) {
+	switch (action) {
+	case LOOK:
+		lookAtHotspot(_currentHotspot);
+		break;
+	case TALK:
+		talkTo(_currentHotspot);
+		break;
+	default:
+		break;
+	}
+}
+
+void PelrockEngine::talkTo(HotSpot *hotspot) {
+	debug("Talking to object %d", hotspot->index);
+	if (_room->_currentRoomConversations.size() == 0)
+		return;
+
+	Sprite *animSet;
+	for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
+		if (i == hotspot->index) {
+			animSet = &_room->_currentRoomAnims[i];
+		}
+	}
+
+	ConversationNode selectedNode = _room->_currentRoomConversations[0];
+
+	bool isNPC = selectedNode.speakerId != 13;
+	if (isNPC) {
+		sayNPC(animSet, selectedNode.text, selectedNode.speakerId);
 	}
+	// for(int i= 0; i< _currentRoomConversations.size(); i++) {
+	// _currentRoomConversations
+	// }
+
+	// showDescription(_currentRoomConversations[0].text, x, y, _currentRoomConversations[0].speakerId);
+	// for(int i = 0; i < _currentRoomConversations[0].choices.size(); i++) {
+	// 	int idx = _currentRoomConversations.size() - 1 - i;
+	// 	_smallFont->drawString(_screen, _currentRoomConversations[0].choices[idx].text.c_str(), 0, 400 - ((i + 1) * 12), 640, 14);
+	// }
+}
+
+void PelrockEngine::lookAtHotspot(HotSpot *hotspot) {
+	debug("Look action clicked");
+	walkTo(_currentHotspot->x, _currentHotspot->y);
+	sayAlfred(_room->_currentRoomDescriptions[_currentHotspot->index].text);
+	_displayPopup = false;
 }
 
 void PelrockEngine::renderText(Common::Array<Common::String> lines, int color, int baseX, int baseY) {
@@ -642,43 +653,41 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	delete[] finalBuf;
 }
 
-
 void applyMovement(int16_t *x, int16_t *y, /*int8_t *z,*/ uint16_t flags) {
-    // X-axis movement
-    if (flags & 0x10) {  // Bit 4: X movement enabled
-        int amount = flags & 0x07;  // Bits 0-2: pixels per frame
-        if (flags & 0x08) {  // Bit 3: direction
-            *x += amount;   // 1 = right (add)
-        } else {
-            *x -= amount;   // 0 = left (subtract)
-        }
-    }
-
-    // Y-axis movement
-    if (flags & 0x200) {  // Bit 9: Y movement enabled
-        int amount = (flags >> 5) & 0x07;  // Bits 5-7: pixels per frame
-        if (flags & 0x100) {  // Bit 8: direction
-            *y += amount;   // 1 = down (add)
-        } else {
-            *y -= amount;   // 0 = up (subtract)
-        }
-    }
-
-    // // Z-axis movement
-    // if (flags & 0x4000) {  // Bit 14: Z movement enabled
-    //     int amount = (flags >> 10) & 0x07;  // Bits 10-12: amount
-    //     if (flags & 0x2000) {  // Bit 13: direction
-    //         *z += amount;   // 1 = forward (add)
-    //     } else {
-    //         *z -= amount;   // 0 = back (subtract)
-    //     }
-    // }
-}
+	// X-axis movement
+	if (flags & 0x10) {            // Bit 4: X movement enabled
+		int amount = flags & 0x07; // Bits 0-2: pixels per frame
+		if (flags & 0x08) {        // Bit 3: direction
+			*x += amount;          // 1 = right (add)
+		} else {
+			*x -= amount; // 0 = left (subtract)
+		}
+	}
 
+	// Y-axis movement
+	if (flags & 0x200) {                  // Bit 9: Y movement enabled
+		int amount = (flags >> 5) & 0x07; // Bits 5-7: pixels per frame
+		if (flags & 0x100) {              // Bit 8: direction
+			*y += amount;                 // 1 = down (add)
+		} else {
+			*y -= amount; // 0 = up (subtract)
+		}
+	}
+
+	// // Z-axis movement
+	// if (flags & 0x4000) {  // Bit 14: Z movement enabled
+	//     int amount = (flags >> 10) & 0x07;  // Bits 10-12: amount
+	//     if (flags & 0x2000) {  // Bit 13: direction
+	//         *z += amount;   // 1 = forward (add)
+	//     } else {
+	//         *z -= amount;   // 0 = back (subtract)
+	//     }
+	// }
+}
 
 void PelrockEngine::drawNextFrame(Sprite *sprite) {
 	Anim &animData = sprite->animData[sprite->curAnimIndex];
-	if(sprite->zOrder == -1) {
+	if (sprite->zOrder == -1) {
 		// skips z0rder -1 sprites
 		return;
 	}
@@ -893,17 +902,16 @@ Exit *PelrockEngine::isExitUnder(int x, int y) {
 void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 
 	drawSpriteToBuffer(_compositeBuffer, 640, _res->_popUpBalloon + (curFrame * kBalloonHeight * kBalloonWidth), posx, posy, kBalloonWidth, kBalloonHeight, 255);
-	Common::Array<VerbIcons> actions = availableActions(_currentHotspot);
-
-	// drawSpriteToBuffer(_compositeBuffer, 640, _res->_verbIcons[LOOK], posx + 20, posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
-	// Graphics::Surface rects;
-	// rects.create(kVerbIconWidth, kVerbIconHeight, Graphics::PixelFormat::createFormatCLUT8());
-	// drawRect(&rects, 0, 0, kVerbIconWidth, kVerbIconHeight, 1);
-
-
+	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
+	VerbIcon icon = isActionUnder(mouseX, mouseY);
 	for (int i = 0; i < actions.size(); i++) {
+		if(icon == actions[i] && _iconBlink++ < kIconBlinkPeriod/2) {
+			continue;
+		}
+		if (_iconBlink > kIconBlinkPeriod) {
+			_iconBlink = 0;
+		}
 		drawSpriteToBuffer(_compositeBuffer, 640, _res->_verbIcons[actions[i]], posx + 20 + (i * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
-		// blitSurfaceToBuffer(&rects, _compositeBuffer, 640, 480, posx + 20 + (i * (kVerbIconWidth + 2)), posy + 20);
 	}
 }
 
@@ -995,7 +1003,7 @@ bool PelrockEngine::pathFind(int targetX, int targetY, PathContext *context) {
 		// Build walkbox path
 		context->pathLength = buildWalkboxPath(startBox, destBox, context->pathBuffer);
 		debug("Walkbox path to point");
-		for(int i = 0; i < context->pathLength; i++) {
+		for (int i = 0; i < context->pathLength; i++) {
 			debug("Walkbox %d: %d", i, context->pathBuffer[i]);
 		}
 		if (context->pathLength == 0) {
@@ -1005,7 +1013,7 @@ bool PelrockEngine::pathFind(int targetX, int targetY, PathContext *context) {
 
 		// Generate movement steps
 		context->movementCount = generateMovementSteps(context->pathBuffer, context->pathLength, startX, startY, targetX, targetY, context->movementBuffer);
-		for(int i = 0; i < context->movementCount; i++) {
+		for (int i = 0; i < context->movementCount; i++) {
 			debug("Movement step %d: flags=\"%s\", dx=%d, dy=%d", i, printMovementFlags(context->movementBuffer[i].flags).c_str(), context->movementBuffer[i].distance_x, context->movementBuffer[i].distance_y);
 		}
 	}
@@ -1016,9 +1024,9 @@ bool PelrockEngine::pathFind(int targetX, int targetY, PathContext *context) {
  * Calculate movement needed to reach a target within a walkbox
  */
 void calculateMovementToTarget(uint16_t current_x, uint16_t current_y,
-								  uint16_t target_x, uint16_t target_y,
-								  WalkBox *box,
-								  MovementStep *step) {
+							   uint16_t target_x, uint16_t target_y,
+							   WalkBox *box,
+							   MovementStep *step) {
 	step->flags = 0;
 	step->distance_x = 0;
 	step->distance_y = 0;
@@ -1051,10 +1059,10 @@ void calculateMovementToTarget(uint16_t current_x, uint16_t current_y,
  * Returns: number of movement steps generated
  */
 uint16_t PelrockEngine::generateMovementSteps(uint8_t *pathBuffer,
-												uint16_t pathLength,
-												uint16_t startX, uint16_t startY,
-												uint16_t destX, uint16_t destY,
-												MovementStep *movementBuffer) {
+											  uint16_t pathLength,
+											  uint16_t startX, uint16_t startY,
+											  uint16_t destX, uint16_t destY,
+											  MovementStep *movementBuffer) {
 	uint16_t currentX = startX;
 	uint16_t currentY = startY;
 	uint16_t movementIndex = 0;
@@ -1224,34 +1232,34 @@ uint8_t PelrockEngine::findWalkboxForPoint(uint16_t x, uint16_t y) {
 	return 0xFF; // Not found
 }
 
+VerbIcon PelrockEngine::isActionUnder(int x, int y) {
+	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
+	for (int i = 0; i < actions.size(); i++) {
+		int actionX = _popupX + 20 + (i * (kVerbIconWidth + 2));
+		int actionY = _popupY + 20;
+		Common::Rect actionRect = Common::Rect(actionX, actionY, actionX + kVerbIconWidth, actionY + kVerbIconHeight);
+		if (actionRect.contains(x, y)) {
+
+			return actions[i];
+		}
+	}
+	return NO_ACTION;
+}
+
 void PelrockEngine::checkMouseClick(int x, int y) {
 
 	if (whichNPCTalking)
 		whichNPCTalking = false;
 
 	if (_displayPopup) {
-		Common::Array<VerbIcons> actions = availableActions(_currentHotspot);
-
-		Common::Rect lookRect = Common::Rect(_popupX + 20, _popupY + 20, _popupX + 20 + kVerbIconWidth, _popupY + 20 + kVerbIconHeight);
-		if (lookRect.contains(x, y)) {
-			debug("Look action clicked");
-			walkTo(_currentHotspot->x, _currentHotspot->y);
-			sayAlfred(_room->_currentRoomDescriptions[_currentHotspot->index].text);
+		// Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
+		VerbIcon actionClicked = isActionUnder(x, y);
+		if (actionClicked != NO_ACTION) {
+			debug("Action %d clicked", actionClicked);
+			doAction(actionClicked, _currentHotspot);
 			_displayPopup = false;
 			return;
 		}
-		for (int i = 1; i < actions.size(); i++) {
-
-			int actionX = _popupX + 20 + (i * (kVerbIconWidth + 2));
-			int actionY = _popupY + 20;
-			Common::Rect actionRect = Common::Rect(actionX, actionY, actionX + kVerbIconWidth, actionY + kVerbIconHeight);
-			if (actionRect.contains(x, y)) {
-				debug("Action %d clicked", actions[i]);
-				doAction(actions[i], _currentHotspot->extra);
-				_displayPopup = false;
-				return;
-			}
-		}
 	}
 
 	_displayPopup = false;
@@ -1298,6 +1306,10 @@ void PelrockEngine::checkMouseHover() {
 		isSomethingUnder = true;
 	}
 
+	if(isActionUnder(mouseX, mouseY) != NO_ACTION) {
+		isSomethingUnder = false;
+	}
+
 	if (isSomethingUnder && exitDetected) {
 		changeCursor(COMBINATION);
 	} else if (isSomethingUnder) {
@@ -1407,7 +1419,7 @@ void PelrockEngine::sayNPC(Sprite *anim, Common::String text, byte color) {
 }
 
 void PelrockEngine::sayAlfred(Common::String text) {
-	alfredState.animState = ALFRED_TALKING;
+	alfredState.nextState = ALFRED_TALKING;
 	alfredState.curFrame = 0;
 	debug("Alfred says: %s", text.c_str());
 	_currentTextPages = wordWrap(text);
@@ -1416,7 +1428,6 @@ void PelrockEngine::sayAlfred(Common::String text) {
 	for (int i = 0; i < _currentTextPages[0].size(); i++) {
 		totalChars += _currentTextPages[0][i].size();
 	}
-	_textPos = Common::Point(alfredState.x, alfredState.y - kAlfredFrameHeight - 10);
 	_chronoManager->_textTtl = totalChars * kTextCharDisplayTime;
 }
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index c2121a17301..a96242a7709 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -60,42 +60,44 @@ private:
 	void init();
 	void loadAnims();
 
+	/*
+		Walking alforithm
+	*/
 	void walkTo(int x, int y);
 	bool pathFind(int x, int y, PathContext *context);
+	Common::Point calculateWalkTarget(int mouseX, int mouseY);
 	uint8_t findWalkboxForPoint(uint16_t x, uint16_t y);
 	bool isPointInWalkbox(WalkBox *box, uint16_t x, uint16_t y);
 	uint16_t buildWalkboxPath(uint8_t start_box, uint8_t dest_box, uint8_t *path_buffer);
 	uint8_t getAdjacentWalkbox(uint8_t current_box_index);
 	void clearVisitedFlags();
-	uint16_t generateMovementSteps(uint8_t *path_buffer,
-									 uint16_t path_length,
-									 uint16_t start_x, uint16_t start_y,
-									 uint16_t dest_x, uint16_t dest_y,
-									 MovementStep *movement_buffer);
+	uint16_t generateMovementSteps(uint8_t *path_buffer, uint16_t path_length, uint16_t start_x, uint16_t start_y, uint16_t dest_x, uint16_t dest_y, MovementStep *movement_buffer);
 
 	void talk(byte object);
 	void displayChoices(Common::Array<Common::String> choices, byte *compositeBuffer);
+	void sayAlfred(Common::String text);
+	void sayNPC(Sprite *anim, Common::String text, byte color);
 
 	byte *grabBackgroundSlice(int x, int y, int w, int h);
 	void putBackgroundSlice(int x, int y, int w, int h, byte *slice);
 
-	Common::Array<VerbIcons> availableActions(HotSpot *hotspot);
-	Common::Point calculateWalkTarget(int mouseX, int mouseY);
-	void drawText(Common::String text, int x, int y, int w, byte color);
+	Common::Array<VerbIcon> availableActions(HotSpot *hotspot);
+	VerbIcon isActionUnder(int x, int y);
+	int isHotspotUnder(int x, int y);
+	Exit *isExitUnder(int x, int y);
+	Sprite *isSpriteUnder(int x, int y);
+	void showActionBalloon(int posx, int posy, int curFrame);
 
-	void sayAlfred(Common::String text);
-	void sayNPC(Sprite *anim, Common::String text, byte color);
+	void drawText(Common::String text, int x, int y, int w, byte color);
 
 	void frames();
-	void doAction(byte action, byte object);
+	void doAction(byte action, HotSpot *hotspot);
+	void talkTo(HotSpot *hotspot);
+	void lookAtHotspot(HotSpot *hotspot);
 	void renderText(Common::Array<Common::String> lines, int color, int x, int y);
 	void drawAlfred(byte *buf);
 	void drawNextFrame(Sprite *animSet);
 	void changeCursor(Cursor cursor);
-	int isHotspotUnder(int x, int y);
-	Exit *isExitUnder(int x, int y);
-	Sprite *isSpriteUnder(int x, int y);
-	void showActionBalloon(int posx, int posy, int curFrame);
 	void drawTalkNPC(Sprite *animSet);
 
 	void checkMouseHover();
@@ -120,6 +122,7 @@ private:
 
 	// Alfred
 	bool alfredFrameSkip = false;
+	bool isAlkfredWalking = false;
 
 	uint16 mouseX = 0;
 	uint16 mouseY = 0;
@@ -132,6 +135,7 @@ private:
 	byte *_compositeBuffer;             // Working composition buffer
 
 	bool _displayPopup = false;
+	byte _iconBlink = 0;
 	int _popupX = 0;
 	int _popupY = 0;
 	int _currentPopupFrame = 0;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index e212ae31c3a..865ecb3ab1a 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -35,7 +35,7 @@ enum Cursor {
 	COMBINATION
 };
 
-enum VerbIcons {
+enum VerbIcon {
 	PICKUP,
 	TALK,
 	WALK,
@@ -44,7 +44,8 @@ enum VerbIcons {
 	PULL,
 	OPEN,
 	CLOSE,
-	UNKNOWN
+	UNKNOWN,
+	NO_ACTION
 };
 
 static const uint32 kLongClickDuration = 500; // 500ms for long click
@@ -94,7 +95,7 @@ const int kChoiceHeight = 16; // Height of each choice line in pixels
 
 #define ALFRED_COLOR 0x0D
 
-
+const byte kIconBlinkPeriod = 4;
 
 enum AlfredAnimState {
 	ALFRED_IDLE,
@@ -113,6 +114,7 @@ enum AlfredDirection {
 
 struct AlfredState {
 	AlfredAnimState animState = ALFRED_IDLE;
+	AlfredAnimState nextState = ALFRED_IDLE;
 	AlfredDirection direction = ALFRED_DOWN;
 	int curFrame = 0;
 	uint16 movementSpeed = 6; // pixels per frame


Commit: a74e8c29047ace9ffa9964d18510bed32fba5e91
    https://github.com/scummvm/scummvm/commit/a74e8c29047ace9ffa9964d18510bed32fba5e91
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:17+02:00

Commit Message:
PELROCK: Fixed walk before talk

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp
    engines/pelrock/util.cpp
    engines/pelrock/util.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 07daaf682f4..e634eee11c0 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -412,9 +412,11 @@ void PelrockEngine::frames() {
 
 		memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
 
-		if (alfredState.animState == ALFRED_TALKING && !_currentTextPages.empty()) {
+		if (alfredState.animState != ALFRED_WALKING && !_currentTextPages.empty()) {
 			if (_chronoManager->_textTtl > 0) {
-				_textPos = Common::Point(alfredState.x, alfredState.y - kAlfredFrameHeight - 10);
+				if(alfredState.animState == ALFRED_TALKING) {
+					_textPos = Common::Point(alfredState.x, alfredState.y - kAlfredFrameHeight - 10);
+				}
 				renderText(_currentTextPages[_currentTextPageIndex], _textColor, _textPos.x, _textPos.y);
 			} else if (_currentTextPageIndex < _currentTextPages.size() - 1) {
 				_currentTextPageIndex++;
@@ -937,7 +939,7 @@ void PelrockEngine::drawTalkNPC(Sprite *animSet) {
 		curFrame = 0;
 	}
 	byte *frame = index ? animHeader->animB[curFrame] : animHeader->animA[curFrame];
-	debug("Talking NPC frame %d/%d, x=%d, y=%d, w=%d, h=%d", curFrame, numFrames, x, y, w, h);
+	// debug("Talking NPC frame %d/%d, x=%d, y=%d, w=%d, h=%d", curFrame, numFrames, x, y, w, h);
 
 	drawSpriteToBuffer(_compositeBuffer, 640, frame, x, y, w, h, 255);
 }
@@ -1414,7 +1416,8 @@ void PelrockEngine::sayNPC(Sprite *anim, Common::String text, byte color) {
 	for (int i = 0; i < _currentTextPages[0].size(); i++) {
 		totalChars += _currentTextPages[0][i].size();
 	}
-	_textPos = Common::Point(anim->x, anim->y - anim->h - 10);
+	debug("Settijng textpos to %d, %d", anim->x, anim->y - 10);
+	_textPos = Common::Point(anim->x, anim->y - 10);
 	_chronoManager->_textTtl = totalChars * kTextCharDisplayTime;
 }
 
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 2c316012176..b66578481f2 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -370,24 +370,20 @@ Common::Array<Description> RoomManager::loadRoomDescriptions(Common::File *roomF
 			pos += 3;
 			description.index = data[pos++];
 			description.text = "";
-			// debug("Found description terminator");
+
 			while (pos < (pair12_size) && data[pos] != 0xFD && pos < (pair12_size)) {
-				// debug(" char: %c", data[pos]);
+
 				if (data[pos] != 0x00) {
-					description.text.append(1, (char)data[pos]);
+					// debug("Description char byte: 0x%02X", data[pos]);
+					description.text.append(1, decodeCPByte((byte)data[pos]));
 				}
 				if (data[pos] == 0xF8) {
 					description.actionTrigger = data[pos + 1] | data[pos + 2] << 8;
-					// debug("Found action trigger: %d", description.actionTrigger);
 					pos += 2;
 					break;
 				}
-				// desc[desc_pos++] = (char)data[pos];
-				// debug("Current desc: %s", desc);
 				pos++;
 			}
-			// debug("Found description for item %d index %d, text: %s", description.itemId, description.index, description.text.c_str());
-
 			descriptions.push_back(description);
 			lastDescPos = pos;
 		}
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 72e071f3256..b42c8b58cb9 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -220,4 +220,7 @@ Common::String joinStrings(const Common::Array<Common::String> &strings, const C
 	}
 	return result;
 }
+char32_t decodeCPByte(byte b) {
+	return cp437_to_unicode[b];
+}
 } // End of namespace Pelrock
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index 8e3de21718f..b5c2ba328fc 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -43,5 +43,43 @@ void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byt
 void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color);
 Common::String printMovementFlags(uint8_t flags);
 Common::String joinStrings(const Common::Array<Common::String> &strings, const Common::String &separator);
+static const char32_t cp437_to_unicode[256] = {
+    // 0x00 - 0x7F: ASCII (unchanged)
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+    0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+    0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+    0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+    0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+    0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+    0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+    0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+    0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+    // 0x80 - 0xFF: CP437 extended characters
+    0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
+    0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+    0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
+    0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
+    0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
+    0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+    0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
+    0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+    0x03B1, 0x03B2, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
+    0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+    0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
+    0x00B0, 0x00B7, 0x2022, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+
+char32_t decodeCPByte(byte b);
 } // End of namespace Pelrock
 #endif // PELROCK_UTIL_H


Commit: f345388fbc1c9ceab8c2978e649afff2052831fd
    https://github.com/scummvm/scummvm/commit/f345388fbc1c9ceab8c2978e649afff2052831fd
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:18+02:00

Commit Message:
PELROCK: Debug position markers

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/util.cpp
    engines/pelrock/util.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index e634eee11c0..89c661e58c1 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -223,13 +223,15 @@ void PelrockEngine::loadAnims() {
 void PelrockEngine::displayChoices(Common::Array<Common::String> choices, byte *compositeBuffer) {
 	int overlayHeight = choices.size() * kChoiceHeight + 2;
 	int overlayY = 400 - overlayHeight;
-	// debug("Displaying choices overlay at y=%d, height=%d", overlayY, overlayHeight);
 	for (int x = 0; x < 640; x++) {
 		for (int y = overlayY; y < 400; y++) {
 			int index = y * 640 + x;
 			compositeBuffer[index] = _room->overlayRemap[compositeBuffer[index]];
 		}
 	}
+	for (int i = 0; i < choices.size(); i++) {
+		drawText(choices[i], 10, overlayY + 2 + i * kChoiceHeight, 620, 15);
+	}
 }
 
 byte *PelrockEngine::grabBackgroundSlice(int x, int y, int w, int h) {
@@ -304,11 +306,6 @@ void PelrockEngine::frames() {
 			Sprite &animSet = _room->_currentRoomAnims[i];
 			drawNextFrame(&animSet);
 		}
-		// if(alfredState.animState == ALFRED_IDLE && alfredState.nextState != ALFRED_IDLE) {
-		// 	alfredState.animState = alfredState.nextState;
-		// 	alfredState.nextState = ALFRED_IDLE;
-		// 	alfredState.curFrame = 0;
-		// }
 
 		switch (alfredState.animState) {
 		case ALFRED_WALKING: {
@@ -449,25 +446,10 @@ void PelrockEngine::frames() {
 			drawRect(_screen, box.x, box.y, box.w, box.h, 150 + i);
 			_smallFont->drawString(_screen, Common::String::format("%d", i), box.x + 2, box.y + 2, 640, 14);
 		}
-		if (_curWalkTarget.x < 640 && _curWalkTarget.y < 400 && _curWalkTarget.x >= 0 && _curWalkTarget.y >= 0) {
-			_screen->setPixel(_curWalkTarget.x, _curWalkTarget.y, 100);
-			if (_curWalkTarget.x - 1 > 0 && _curWalkTarget.y - 1 > 0)
-				_screen->setPixel(_curWalkTarget.x - 1, _curWalkTarget.y - 1, 100);
-			if (_curWalkTarget.x - 1 > 0 && _curWalkTarget.y + 1 < 400)
-				_screen->setPixel(_curWalkTarget.x - 1, _curWalkTarget.y + 1, 100);
-			if (_curWalkTarget.x + 1 < 640 && _curWalkTarget.y - 1 > 0)
-				_screen->setPixel(_curWalkTarget.x + 1, _curWalkTarget.y - 1, 100);
-			if (_curWalkTarget.x + 1 < 640 && _curWalkTarget.y + 1 < 400)
-				_screen->setPixel(_curWalkTarget.x + 1, _curWalkTarget.y + 1, 100);
-			if (_curWalkTarget.x - 2 > 0)
-				_screen->setPixel(_curWalkTarget.x - 2, _curWalkTarget.y, 100);
-			if (_curWalkTarget.x + 2 < 640)
-				_screen->setPixel(_curWalkTarget.x + 2, _curWalkTarget.y, 100);
-			if (_curWalkTarget.y - 2 > 0)
-				_screen->setPixel(_curWalkTarget.x, _curWalkTarget.y - 2, 100);
-			if (_curWalkTarget.y + 2 < 400)
-				_screen->setPixel(_curWalkTarget.x, _curWalkTarget.y + 2, 100);
-		}
+
+		drawPos(_screen, alfredState.x, alfredState.y, 13);
+		drawPos(_screen, _curWalkTarget.x, _curWalkTarget.y, 100);
+
 
 		if (showShadows) {
 			memcpy(_screen->getPixels(), _room->_pixelsShadows, 640 * 400);
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index b42c8b58cb9..5a3d2a5e64a 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -220,6 +220,14 @@ Common::String joinStrings(const Common::Array<Common::String> &strings, const C
 	}
 	return result;
 }
+void drawPos(Graphics::ManagedSurface *surface, int x, int y, byte color) {
+	if (x < 640 && y < 400 && x >= 0 && y >= 0) {
+		surface->setPixel(x, y, 100);
+
+		surface->drawEllipse(x - 3, y - 3, x+3, y+3, color, true);
+	}
+}
+
 char32_t decodeCPByte(byte b) {
 	return cp437_to_unicode[b];
 }
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index b5c2ba328fc..38783d96ac4 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -43,6 +43,7 @@ void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byt
 void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color);
 Common::String printMovementFlags(uint8_t flags);
 Common::String joinStrings(const Common::Array<Common::String> &strings, const Common::String &separator);
+void drawPos(Graphics::ManagedSurface *surface, int x, int y, byte color);
 static const char32_t cp437_to_unicode[256] = {
     // 0x00 - 0x7F: ASCII (unchanged)
     0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,


Commit: 5751f8c2d58747ebe8c7a85590df01b5eaa78de5
    https://github.com/scummvm/scummvm/commit/5751f8c2d58747ebe8c7a85590df01b5eaa78de5
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:18+02:00

Commit Message:
PELROCK: Sprite order

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 89c661e58c1..08dc243de5b 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -258,7 +258,7 @@ void PelrockEngine::putBackgroundSlice(int x, int y, int w, int h, byte *slice)
 }
 
 Common::Array<VerbIcon> PelrockEngine::availableActions(HotSpot *hotspot) {
-	if(hotspot == nullptr) {
+	if (hotspot == nullptr) {
 		return Common::Array<VerbIcon>();
 	}
 	Common::Array<VerbIcon> verbs;
@@ -288,6 +288,20 @@ Common::Array<VerbIcon> PelrockEngine::availableActions(HotSpot *hotspot) {
 	return verbs;
 }
 
+// Sort sprites by zOrder in-place using insertion sort (efficient for nearly-sorted data)
+void sortAnimsByZOrder(Common::Array<Sprite> &anims) {
+	for (size_t i = 1; i < anims.size(); ++i) {
+		Sprite key = anims[i];
+		int z = key.zOrder;
+		size_t j = i;
+		while (j > 0 && anims[j - 1].zOrder > z) {
+			anims[j] = anims[j - 1];
+			--j;
+		}
+		anims[j] = key;
+	}
+}
+
 void PelrockEngine::frames() {
 
 	if (_chronoManager->_gameTick) {
@@ -298,101 +312,42 @@ void PelrockEngine::frames() {
 			_soundManager->playSound(_room->_roomSfx[3 + soundIndex]);
 		}
 
-		memcpy(_compositeBuffer, _currentBackground, 640 * 400);
+		// Sort sprites by zOrder (persists in the array)
+		sortAnimsByZOrder(_room->_currentRoomAnims);
 
-		// debug("Game tick!");
-		for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
-			// debug("Processing animation set %d, numAnims %d", num, i->numAnims);
-			Sprite &animSet = _room->_currentRoomAnims[i];
-			drawNextFrame(&animSet);
-		}
-
-		switch (alfredState.animState) {
-		case ALFRED_WALKING: {
-
-			MovementStep step = _currentContext.movementBuffer[_currentStep];
-
-			if (step.distance_x > 0) {
-				if (step.flags & MOVE_RIGHT) {
-					alfredState.direction = ALFRED_RIGHT;
-					alfredState.x += MIN(alfredState.movementSpeed, step.distance_x);
-				}
-				if (step.flags & MOVE_LEFT) {
-					alfredState.direction = ALFRED_LEFT;
-					alfredState.x -= MIN(alfredState.movementSpeed, step.distance_x);
-				}
-			}
-			if (step.distance_y > 0) {
-				if (step.flags & MOVE_DOWN) {
-					alfredState.direction = ALFRED_DOWN;
-					alfredState.y += MIN(alfredState.movementSpeed, step.distance_y);
-				}
-				if (step.flags & MOVE_UP) {
-					alfredState.direction = ALFRED_UP;
-					alfredState.y -= MIN(alfredState.movementSpeed, step.distance_y);
-				}
-			}
-
-			if (step.distance_x > 0)
-				step.distance_x -= MIN(alfredState.movementSpeed, step.distance_x);
+		memcpy(_compositeBuffer, _currentBackground, 640 * 400);
 
-			if (step.distance_y > 0)
-				step.distance_y -= MIN(alfredState.movementSpeed, step.distance_y);
+		// Create temporary render order partitioned by Alfred's Y position
+		Common::Array<Sprite *> renderOrder;
+		int alfredY = alfredState.y;
 
-			if (step.distance_x <= 0 && step.distance_y <= 0) {
-				_currentStep++;
-				if (_currentStep >= _currentContext.movementCount) {
-					_currentStep = 0;
-					alfredState.animState = ALFRED_IDLE;
-				}
-			} else {
-				_currentContext.movementBuffer[_currentStep] = step;
-			}
-
-			Exit *exit = isExitUnder(alfredState.x, alfredState.y);
+		// First pass: sprites behind Alfred (y <= alfredY)
+		for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
+			if (_room->_currentRoomAnims[i].y < (alfredY - kAlfredFrameHeight)) {
+				debug("Drawing sprite %d in front of Alfred at zOrder %d, pos (%d, %d)", i, _room->_currentRoomAnims[i].zOrder, _room->_currentRoomAnims[i].x, _room->_currentRoomAnims[i].y);
 
-			if (exit != nullptr) {
-				alfredState.x = exit->targetX;
-				alfredState.y = exit->targetY;
-				setScreen(exit->targetRoom, exit->dir);
+				// renderOrder.push_back(&_room->_currentRoomAnims[i]);
+				drawNextFrame(&_room->_currentRoomAnims[i]);
 			}
+		}
 
-			if (alfredState.curFrame >= walkingAnimLengths[alfredState.direction]) {
-				alfredState.curFrame = 0;
-			}
+		// Draw Alfred here (you'll need to add this)
+		chooseAlfredStateAndDraw();
 
-			drawAlfred(_res->alfredWalkFrames[alfredState.direction][alfredState.curFrame]);
-			// if(alfredFrameSkip) alfredState.curFrame++;
-			// alfredFrameSkip = !alfredFrameSkip;
-			alfredState.curFrame++;
-			break;
-		}
-		case ALFRED_TALKING:
-			if (alfredState.curFrame >= talkingAnimLengths[alfredState.direction] - 1) {
-				alfredState.curFrame = 0;
-			}
-			drawAlfred(_res->alfredTalkFrames[alfredState.direction][alfredState.curFrame]);
-			alfredState.curFrame++;
-			break;
-		case ALFRED_COMB:
-			if (alfredState.curFrame >= 11) {
-				alfredState.curFrame = 0;
-			}
-			drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCombFrames[0][alfredState.curFrame], alfredState.x, alfredState.y - kAlfredFrameHeight, 51, 102, 255);
-			alfredState.curFrame++;
-			break;
-		case ALFRED_INTERACTING:
-			if (alfredState.curFrame >= interactingAnimLength) {
-				alfredState.curFrame = 0;
+		// Second pass: sprites in front of Alfred (y > alfredY)
+		for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
+			if (_room->_currentRoomAnims[i].y > (alfredY - kAlfredFrameHeight)) {
+				debug("Drawing sprite %d behind Alfred at zOrder %d, pos (%d, %d)", i, _room->_currentRoomAnims[i].zOrder, _room->_currentRoomAnims[i].x, _room->_currentRoomAnims[i].y);
+				drawNextFrame(&_room->_currentRoomAnims[i]);
+				// renderOrder.push_back(&_room->_currentRoomAnims[i]);
 			}
-			drawAlfred(_res->alfredInteractFrames[alfredState.direction][alfredState.curFrame]);
-			alfredState.curFrame++;
-			break;
-		default:
-			drawAlfred(_res->alfredIdle[alfredState.direction]);
-			break;
 		}
 
+		// // Render in the computed order
+		// for (int i = 0; i < renderOrder.size(); i++) {
+		// 	drawNextFrame(renderOrder[i]);
+		// }
+
 		if (_displayPopup) {
 			showActionBalloon(_popupX, _popupY, _currentPopupFrame);
 			if (_currentPopupFrame < 3) {
@@ -411,7 +366,7 @@ void PelrockEngine::frames() {
 
 		if (alfredState.animState != ALFRED_WALKING && !_currentTextPages.empty()) {
 			if (_chronoManager->_textTtl > 0) {
-				if(alfredState.animState == ALFRED_TALKING) {
+				if (alfredState.animState == ALFRED_TALKING) {
 					_textPos = Common::Point(alfredState.x, alfredState.y - kAlfredFrameHeight - 10);
 				}
 				renderText(_currentTextPages[_currentTextPageIndex], _textColor, _textPos.x, _textPos.y);
@@ -432,7 +387,7 @@ void PelrockEngine::frames() {
 			}
 		}
 
-		if(alfredState.animState == ALFRED_IDLE && alfredState.nextState != ALFRED_IDLE) {
+		if (alfredState.animState == ALFRED_IDLE && alfredState.nextState != ALFRED_IDLE) {
 			// debug("Switching Alfred state from IDLE to %d", alfredState.nextState);
 			alfredState.animState = alfredState.nextState;
 			alfredState.nextState = ALFRED_IDLE;
@@ -448,13 +403,14 @@ void PelrockEngine::frames() {
 		}
 
 		drawPos(_screen, alfredState.x, alfredState.y, 13);
+		drawPos(_screen, alfredState.x, alfredState.y - kAlfredFrameHeight, 13);
 		drawPos(_screen, _curWalkTarget.x, _curWalkTarget.y, 100);
 
-
 		if (showShadows) {
 			memcpy(_screen->getPixels(), _room->_pixelsShadows, 640 * 400);
 		}
 		_smallFont->drawString(_screen, Common::String::format("Room number: %d", _room->_currentRoomNumber), 0, 4, 640, 13);
+		_smallFont->drawString(_screen, Common::String::format("Alfred pos: %d, %d (%d)", alfredState.x, alfredState.y, alfredState.y - kAlfredFrameHeight), 0, 18, 640, 13);
 		_screen->markAllDirty();
 
 		// _screen->update();
@@ -527,6 +483,94 @@ void PelrockEngine::renderText(Common::Array<Common::String> lines, int color, i
 	}
 }
 
+void PelrockEngine::chooseAlfredStateAndDraw() {
+	switch (alfredState.animState) {
+	case ALFRED_WALKING: {
+
+		MovementStep step = _currentContext.movementBuffer[_currentStep];
+
+		if (step.distance_x > 0) {
+			if (step.flags & MOVE_RIGHT) {
+				alfredState.direction = ALFRED_RIGHT;
+				alfredState.x += MIN(alfredState.movementSpeed, step.distance_x);
+			}
+			if (step.flags & MOVE_LEFT) {
+				alfredState.direction = ALFRED_LEFT;
+				alfredState.x -= MIN(alfredState.movementSpeed, step.distance_x);
+			}
+		}
+		if (step.distance_y > 0) {
+			if (step.flags & MOVE_DOWN) {
+				alfredState.direction = ALFRED_DOWN;
+				alfredState.y += MIN(alfredState.movementSpeed, step.distance_y);
+			}
+			if (step.flags & MOVE_UP) {
+				alfredState.direction = ALFRED_UP;
+				alfredState.y -= MIN(alfredState.movementSpeed, step.distance_y);
+			}
+		}
+
+		if (step.distance_x > 0)
+			step.distance_x -= MIN(alfredState.movementSpeed, step.distance_x);
+
+		if (step.distance_y > 0)
+			step.distance_y -= MIN(alfredState.movementSpeed, step.distance_y);
+
+		if (step.distance_x <= 0 && step.distance_y <= 0) {
+			_currentStep++;
+			if (_currentStep >= _currentContext.movementCount) {
+				_currentStep = 0;
+				alfredState.animState = ALFRED_IDLE;
+			}
+		} else {
+			_currentContext.movementBuffer[_currentStep] = step;
+		}
+
+		Exit *exit = isExitUnder(alfredState.x, alfredState.y);
+
+		if (exit != nullptr) {
+			alfredState.x = exit->targetX;
+			alfredState.y = exit->targetY;
+			setScreen(exit->targetRoom, exit->dir);
+		}
+
+		if (alfredState.curFrame >= walkingAnimLengths[alfredState.direction]) {
+			alfredState.curFrame = 0;
+		}
+
+		drawAlfred(_res->alfredWalkFrames[alfredState.direction][alfredState.curFrame]);
+		// if(alfredFrameSkip) alfredState.curFrame++;
+		// alfredFrameSkip = !alfredFrameSkip;
+		alfredState.curFrame++;
+		break;
+	}
+	case ALFRED_TALKING:
+		if (alfredState.curFrame >= talkingAnimLengths[alfredState.direction] - 1) {
+			alfredState.curFrame = 0;
+		}
+		drawAlfred(_res->alfredTalkFrames[alfredState.direction][alfredState.curFrame]);
+		alfredState.curFrame++;
+		break;
+	case ALFRED_COMB:
+		if (alfredState.curFrame >= 11) {
+			alfredState.curFrame = 0;
+		}
+		drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCombFrames[0][alfredState.curFrame], alfredState.x, alfredState.y - kAlfredFrameHeight, 51, 102, 255);
+		alfredState.curFrame++;
+		break;
+	case ALFRED_INTERACTING:
+		if (alfredState.curFrame >= interactingAnimLength) {
+			alfredState.curFrame = 0;
+		}
+		drawAlfred(_res->alfredInteractFrames[alfredState.direction][alfredState.curFrame]);
+		alfredState.curFrame++;
+		break;
+	default:
+		drawAlfred(_res->alfredIdle[alfredState.direction]);
+		break;
+	}
+}
+
 void PelrockEngine::drawAlfred(byte *buf) {
 
 	ScaleCalculation scale = calculateScaling(alfredState.y, _room->_scaleParams);
@@ -889,7 +933,7 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
 	VerbIcon icon = isActionUnder(mouseX, mouseY);
 	for (int i = 0; i < actions.size(); i++) {
-		if(icon == actions[i] && _iconBlink++ < kIconBlinkPeriod/2) {
+		if (icon == actions[i] && _iconBlink++ < kIconBlinkPeriod / 2) {
 			continue;
 		}
 		if (_iconBlink > kIconBlinkPeriod) {
@@ -1290,7 +1334,7 @@ void PelrockEngine::checkMouseHover() {
 		isSomethingUnder = true;
 	}
 
-	if(isActionUnder(mouseX, mouseY) != NO_ACTION) {
+	if (isActionUnder(mouseX, mouseY) != NO_ACTION) {
 		isSomethingUnder = false;
 	}
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index a96242a7709..37a530f65b4 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -95,6 +95,7 @@ private:
 	void talkTo(HotSpot *hotspot);
 	void lookAtHotspot(HotSpot *hotspot);
 	void renderText(Common::Array<Common::String> lines, int color, int x, int y);
+	void chooseAlfredStateAndDraw();
 	void drawAlfred(byte *buf);
 	void drawNextFrame(Sprite *animSet);
 	void changeCursor(Cursor cursor);


Commit: d64304311f1ec1262bb6fb76d9a620ed0522afd5
    https://github.com/scummvm/scummvm/commit/d64304311f1ec1262bb6fb76d9a620ed0522afd5
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:18+02:00

Commit Message:
PELROCK: Sets temporary Alfred zorder

Changed paths:
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 08dc243de5b..7ada651578d 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -323,7 +323,7 @@ void PelrockEngine::frames() {
 
 		// First pass: sprites behind Alfred (y <= alfredY)
 		for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
-			if (_room->_currentRoomAnims[i].y < (alfredY - kAlfredFrameHeight)) {
+			if (_room->_currentRoomAnims[i].zOrder > 10) {
 				debug("Drawing sprite %d in front of Alfred at zOrder %d, pos (%d, %d)", i, _room->_currentRoomAnims[i].zOrder, _room->_currentRoomAnims[i].x, _room->_currentRoomAnims[i].y);
 
 				// renderOrder.push_back(&_room->_currentRoomAnims[i]);
@@ -336,7 +336,7 @@ void PelrockEngine::frames() {
 
 		// Second pass: sprites in front of Alfred (y > alfredY)
 		for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
-			if (_room->_currentRoomAnims[i].y > (alfredY - kAlfredFrameHeight)) {
+			if (_room->_currentRoomAnims[i].zOrder <= 10) {
 				debug("Drawing sprite %d behind Alfred at zOrder %d, pos (%d, %d)", i, _room->_currentRoomAnims[i].zOrder, _room->_currentRoomAnims[i].x, _room->_currentRoomAnims[i].y);
 				drawNextFrame(&_room->_currentRoomAnims[i]);
 				// renderOrder.push_back(&_room->_currentRoomAnims[i]);


Commit: df8045d555a2966ebea3ba99e992a3facf91b9db
    https://github.com/scummvm/scummvm/commit/df8045d555a2966ebea3ba99e992a3facf91b9db
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:19+02:00

Commit Message:
PELROCK: Fixes decompression script

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp
    engines/pelrock/util.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 7ada651578d..f7abb794a5c 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -191,7 +191,7 @@ Common::Error PelrockEngine::run() {
 void PelrockEngine::init() {
 	_res->loadCursors();
 	_res->loadInteractionIcons();
-	_res->loadSettingsMenu();
+	// _res->loadSettingsMenu();
 	_soundManager->loadSoundIndex();
 
 	calculateScalingMasks();
@@ -324,9 +324,6 @@ void PelrockEngine::frames() {
 		// First pass: sprites behind Alfred (y <= alfredY)
 		for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
 			if (_room->_currentRoomAnims[i].zOrder > 10) {
-				debug("Drawing sprite %d in front of Alfred at zOrder %d, pos (%d, %d)", i, _room->_currentRoomAnims[i].zOrder, _room->_currentRoomAnims[i].x, _room->_currentRoomAnims[i].y);
-
-				// renderOrder.push_back(&_room->_currentRoomAnims[i]);
 				drawNextFrame(&_room->_currentRoomAnims[i]);
 			}
 		}
@@ -337,17 +334,10 @@ void PelrockEngine::frames() {
 		// Second pass: sprites in front of Alfred (y > alfredY)
 		for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
 			if (_room->_currentRoomAnims[i].zOrder <= 10) {
-				debug("Drawing sprite %d behind Alfred at zOrder %d, pos (%d, %d)", i, _room->_currentRoomAnims[i].zOrder, _room->_currentRoomAnims[i].x, _room->_currentRoomAnims[i].y);
 				drawNextFrame(&_room->_currentRoomAnims[i]);
-				// renderOrder.push_back(&_room->_currentRoomAnims[i]);
 			}
 		}
 
-		// // Render in the computed order
-		// for (int i = 0; i < renderOrder.size(); i++) {
-		// 	drawNextFrame(renderOrder[i]);
-		// }
-
 		if (_displayPopup) {
 			showActionBalloon(_popupX, _popupY, _currentPopupFrame);
 			if (_currentPopupFrame < 3) {
@@ -356,11 +346,11 @@ void PelrockEngine::frames() {
 				_currentPopupFrame = 0;
 		}
 
-		Common::Array<Common::String> testChoices;
-		testChoices.push_back("First choice");
-		testChoices.push_back("Second choice");
-		testChoices.push_back("Third choice");
-		displayChoices(testChoices, _compositeBuffer);
+		// Common::Array<Common::String> testChoices;
+		// testChoices.push_back("First choice");
+		// testChoices.push_back("Second choice");
+		// testChoices.push_back("Third choice");
+		// displayChoices(testChoices, _compositeBuffer);
 
 		memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
 
@@ -566,6 +556,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		alfredState.curFrame++;
 		break;
 	default:
+		debug("Drawing Alfred idle frame for direction %d", alfredState.direction);
 		drawAlfred(_res->alfredIdle[alfredState.direction]);
 		break;
 	}
@@ -591,7 +582,7 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	if (scaleIndex < 0) {
 		scaleIndex = 0;
 	}
-	// debug("Scaling Alfred frame to final size (%d x %d) from scale factor %.2f", finalWidth, finalHeight, scaleFactor);
+	debug("Scaling Alfred frame to final size (%d x %d) from scale factor %.2f", finalWidth, finalHeight, scaleFactor);
 	int linesToSkip = kAlfredFrameHeight - finalHeight;
 
 	// debug("lines to skip = %d, finalHeight = %d, finalWidth = %d for position (%d, %d)", linesToSkip, finalHeight, finalWidth, xAlfred, yAlfred);
@@ -660,7 +651,12 @@ void PelrockEngine::drawAlfred(byte *buf) {
 					}
 					int srcIndex = srcY * kAlfredFrameWidth + srcX;
 					int outIndex = outY * finalWidth + outX;
-					finalBuf[outIndex] = buf[srcIndex];
+					debug("srcIndex = %d, outIndex = %d, original size = %d, outsize = %d", srcIndex, outIndex, kAlfredFrameWidth * kAlfredFrameHeight, finalWidth * finalHeight);
+					if(outIndex >= finalWidth * finalHeight || srcIndex >= kAlfredFrameWidth * kAlfredFrameHeight) {
+						debug("Index out of bounds!");
+					}
+					else
+						finalBuf[outIndex] = buf[srcIndex];
 				}
 				outY++;
 			}
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index b66578481f2..fa00202554b 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -75,16 +75,20 @@ void RoomManager::getBackground(Common::File *roomFile, int roomOffset, byte *ba
 		roomFile->seek(pair_offset, SEEK_SET);
 		uint32_t offset = roomFile->readUint32LE();
 		uint32_t size = roomFile->readUint32LE();
-
+		debug("Background pair %d: offset=%d size=%d", pair_idx, offset, size);
 		if (offset > 0 && size > 0 && offset < roomFile->size()) {
 			byte *data = new byte[size];
 			roomFile->seek(offset, SEEK_SET);
 			roomFile->read(data, size);
 			uint8_t *block_data = NULL;
 			size_t block_size = rleDecompress(data, size, 0, 640 * 400, &block_data);
-
+			debug(" Decompressed block size: %d, combined size: %d", block_size, combined_size + block_size);
+			if (block_size + combined_size > 640 * 400) {
+				debug(" Warning: decompressed background size exceeds buffer size!");
+				block_size = 640 * 400 - combined_size;
+			}
 			memcpy(background + combined_size, block_data, block_size);
-			combined_size += block_size + 1;
+			combined_size += block_size;
 			free(block_data);
 			delete[] data;
 		}
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 5a3d2a5e64a..76091bf1943 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -65,73 +65,99 @@ Common::String printMovementFlags(uint8_t flags) {
 }
 
 size_t rleDecompress(
-	const uint8_t *input,
-	size_t inputSize,
-	uint32_t offset,
-	uint32_t expectedSize,
-	uint8_t **out_data,
-	bool untilBuda) {
-	// // Check for uncompressed markers
-	if (inputSize == 0x8000 || inputSize == 0x6800) {
-		*out_data = (uint8_t *)malloc(inputSize);
-		memcpy(*out_data, input + offset, inputSize);
-		return inputSize;
-	}
+    const uint8_t *input,
+    size_t inputSize,
+    uint32_t offset,
+    uint32_t expectedSize,
+    uint8_t **out_data,
+    bool untilBuda) {
+    // Check for uncompressed markers
+    if (inputSize == 0x8000 || inputSize == 0x6800) {
+        *out_data = (uint8_t *)malloc(inputSize);
+        if (!*out_data)
+            return 0;
+        memcpy(*out_data, input + offset, inputSize);
+        return inputSize;
+    }
 
-	// RLE compressed
-	size_t bufferCapacity;
-	size_t result_size = 0;
-	uint32_t pos = offset;
-
-	if (untilBuda) {
-		// Dynamic allocation mode - grow buffer as needed
-		bufferCapacity = 4096;
-		*out_data = (uint8_t *)malloc(bufferCapacity);
-		if (!*out_data)
-			return 0;
-	} else {
-		// Fixed size mode
-		bufferCapacity = expectedSize;
-		*out_data = (uint8_t *)malloc(expectedSize);
-		if (!*out_data)
-			return 0;
-	}
+    // RLE compressed
+    size_t bufferCapacity;
+    size_t result_size = 0;
+    uint32_t pos = offset;
+    uint8_t last_value = 0;
 
-	while (pos + 2 <= inputSize) {
-		// Check for BUDA marker
-		if (pos + 4 <= inputSize &&
-			input[pos] == 'B' && input[pos + 1] == 'U' &&
-			input[pos + 2] == 'D' && input[pos + 3] == 'A') {
-			break;
-		}
+    if (untilBuda || expectedSize == 0) {
+        // Dynamic allocation mode - grow buffer as needed
+        bufferCapacity = 4096;
+        *out_data = (uint8_t *)malloc(bufferCapacity);
+        if (!*out_data)
+            return 0;
+    } else {
+        // Fixed size mode
+        bufferCapacity = expectedSize;
+        *out_data = (uint8_t *)malloc(bufferCapacity);
+        if (!*out_data)
+            return 0;
+    }
 
-		uint8_t count = input[pos];
-		uint8_t value = input[pos + 1];
-
-		for (int i = 0; i < count; i++) {
-			// If in untilBuda mode, grow buffer as needed
-			if (untilBuda && result_size >= bufferCapacity) {
-				bufferCapacity *= 2;
-				uint8_t *newBuf = (uint8_t *)realloc(*out_data, bufferCapacity);
-				if (!newBuf) {
-					free(*out_data);
-					*out_data = nullptr;
-					return 0;
-				}
-				*out_data = newBuf;
-			}
-			// debug("Pos = %zu, writing value %02X", result_size, value);
-			(*out_data)[result_size++] = value;
-		}
+    while (pos + 2 <= inputSize) {
+        // Read the RLE pair
+        uint8_t count = input[pos];
+        uint8_t value = input[pos + 1];
+        last_value = value;
 
-		pos += 2;
-		// In fixed size mode, stop when we reach expected size
-		if (!untilBuda && result_size >= expectedSize) {
-			break;
-		}
-	}
+        // Write pixels for this pair
+        for (int i = 0; i < count; i++) {
+            // Grow buffer if needed
+            if (result_size >= bufferCapacity) {
+                if (untilBuda || expectedSize == 0) {
+                    bufferCapacity *= 2;
+                } else {
+                    // In fixed mode, we've hit the limit - something is wrong
+                    // but grow minimally to avoid crash
+                    bufferCapacity += 256;
+                }
+                uint8_t *newBuf = (uint8_t *)realloc(*out_data, bufferCapacity);
+                if (!newBuf) {
+                    free(*out_data);
+                    *out_data = nullptr;
+                    return 0;
+                }
+                *out_data = newBuf;
+            }
+            (*out_data)[result_size++] = value;
+        }
+
+        // Advance to next pair
+        pos += 2;
+
+        // Check for BUDA marker at new position
+        if (untilBuda && pos + 4 <= inputSize &&
+            input[pos] == 'B' && input[pos + 1] == 'U' &&
+            input[pos + 2] == 'D' && input[pos + 3] == 'A') {
+            // Game writes one final pixel after BUDA marker
+            // Grow buffer if needed
+            if (result_size >= bufferCapacity) {
+                bufferCapacity++;
+                uint8_t *newBuf = (uint8_t *)realloc(*out_data, bufferCapacity);
+                if (!newBuf) {
+                    free(*out_data);
+                    *out_data = nullptr;
+                    return 0;
+                }
+                *out_data = newBuf;
+            }
+            (*out_data)[result_size++] = last_value;
+            break;
+        }
+
+        // In fixed size mode, stop when we reach expected size
+        if (!untilBuda && expectedSize > 0 && result_size >= expectedSize) {
+            break;
+        }
+    }
 
-	return result_size;
+    return result_size;
 }
 
 void readUntilBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *&buffer, size_t &outSize) {


Commit: 9f89aabd1af755af11320a5b77fa5be25474a9e9
    https://github.com/scummvm/scummvm/commit/9f89aabd1af755af11320a5b77fa5be25474a9e9
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:19+02:00

Commit Message:
PELROCK: Loads and renders inventory icons

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/types.h
    engines/pelrock/util.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index f7abb794a5c..21cd123dee1 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -170,9 +170,16 @@ Common::Error PelrockEngine::run() {
 		}
 		checkMouseHover();
 		if (stateGame == SETTINGS) {
-
-			memcpy(_screen->getPixels(), _res->_mainMenu, 640 * 400);
 			g_system->getPaletteManager()->setPalette(_res->_mainMenuPalette, 0, 256);
+
+			memcpy(_compositeBuffer, _res->_mainMenu, 640 * 400);
+
+			for (int i = 0; i < 4; i++) {
+				InventoryObject item = _res->getInventoryObject(i);
+				drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 140 + i * 85, 110 - i * 5, 60, 60, 1);
+			}
+			memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
+
 			g_engine->_screen->markAllDirty();
 			g_engine->_screen->update();
 
@@ -191,7 +198,8 @@ Common::Error PelrockEngine::run() {
 void PelrockEngine::init() {
 	_res->loadCursors();
 	_res->loadInteractionIcons();
-	// _res->loadSettingsMenu();
+	_res->loadInventoryIcons();
+	_res->loadSettingsMenu();
 	_soundManager->loadSoundIndex();
 
 	calculateScalingMasks();
@@ -556,7 +564,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		alfredState.curFrame++;
 		break;
 	default:
-		debug("Drawing Alfred idle frame for direction %d", alfredState.direction);
+		// debug("Drawing Alfred idle frame for direction %d", alfredState.direction);
 		drawAlfred(_res->alfredIdle[alfredState.direction]);
 		break;
 	}
@@ -582,7 +590,7 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	if (scaleIndex < 0) {
 		scaleIndex = 0;
 	}
-	debug("Scaling Alfred frame to final size (%d x %d) from scale factor %.2f", finalWidth, finalHeight, scaleFactor);
+	// debug("Scaling Alfred frame to final size (%d x %d) from scale factor %.2f", finalWidth, finalHeight, scaleFactor);
 	int linesToSkip = kAlfredFrameHeight - finalHeight;
 
 	// debug("lines to skip = %d, finalHeight = %d, finalWidth = %d for position (%d, %d)", linesToSkip, finalHeight, finalWidth, xAlfred, yAlfred);
@@ -652,10 +660,9 @@ void PelrockEngine::drawAlfred(byte *buf) {
 					int srcIndex = srcY * kAlfredFrameWidth + srcX;
 					int outIndex = outY * finalWidth + outX;
 					debug("srcIndex = %d, outIndex = %d, original size = %d, outsize = %d", srcIndex, outIndex, kAlfredFrameWidth * kAlfredFrameHeight, finalWidth * finalHeight);
-					if(outIndex >= finalWidth * finalHeight || srcIndex >= kAlfredFrameWidth * kAlfredFrameHeight) {
+					if (outIndex >= finalWidth * finalHeight || srcIndex >= kAlfredFrameWidth * kAlfredFrameHeight) {
 						debug("Index out of bounds!");
-					}
-					else
+					} else
 						finalBuf[outIndex] = buf[srcIndex];
 				}
 				outY++;
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 193133af40c..0364fe8a04b 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -29,6 +29,7 @@
 namespace Pelrock {
 
 ResourceManager::ResourceManager(/* args */) {
+	_inventoryIcons = new InventoryObject[69];
 }
 
 ResourceManager::~ResourceManager() {
@@ -60,6 +61,7 @@ ResourceManager::~ResourceManager() {
 	delete[] alfredCombFrames[0];
 	delete[] alfredCombFrames[1];
 	delete _mainMenu;
+	delete[] _inventoryIcons;
 }
 
 void ResourceManager::loadCursors() {
@@ -213,6 +215,27 @@ void ResourceManager::loadAlfredAnims() {
 	free(alfredCombLeftRaw);
 }
 
+void ResourceManager::loadInventoryIcons() {
+	Common::File alfred4File;
+	if (!alfred4File.open("ALFRED.4")) {
+		error("Couldnt find file ALFRED.4");
+	}
+	uint32 iconsSize = alfred4File.size() - 423656;
+	byte *iconData = new byte[iconsSize];
+	alfred4File.seek(42366, SEEK_SET);
+	alfred4File.read(iconData, iconsSize);
+
+	int iconSize = 60 * 60; // each icon has 30 bytes of header
+	for (int i = 0; i < 69; i++) {
+		_inventoryIcons[i].index = i;
+		extractSingleFrame(iconData, _inventoryIcons[i].iconData, i, 60, 60);
+	}
+	delete[] iconData;
+}
+
+InventoryObject ResourceManager::getInventoryObject(byte index) {
+	return _inventoryIcons[index];
+}
 
 void ResourceManager::mergeRleBlocks(Common::SeekableReadStream *stream, uint32 offset, int numBlocks, byte *outputBuffer) {
 	stream->seek(offset, SEEK_SET);
@@ -225,7 +248,7 @@ void ResourceManager::mergeRleBlocks(Common::SeekableReadStream *stream, uint32
 		uint8_t *block_data = nullptr;
 		size_t decompressedSize = rleDecompress(thisBlock, blockSize, 0, 640 * 400, &block_data, true);
 		memcpy(outputBuffer + combined_size, block_data, decompressedSize);
-		combined_size += decompressedSize + 1;
+		combined_size += decompressedSize;
 		free(block_data);
 		free(thisBlock);
 	}
@@ -252,5 +275,4 @@ void ResourceManager::loadSettingsMenu() {
 	alfred7.close();
 }
 
-
 } // End of namespace Pelrock
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index 6bed0e95310..4561e78e799 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -23,6 +23,7 @@
 
 #include "common/scummsys.h"
 #include "common/stream.h"
+#include "pelrock/types.h"
 
 namespace Pelrock {
 
@@ -33,6 +34,7 @@ static const int interactingAnimLength = 2;
 class ResourceManager {
 private:
 	void mergeRleBlocks(Common::SeekableReadStream *stream, uint32 offset, int numBlocks, byte *outputBuffer);
+	InventoryObject *_inventoryIcons = nullptr;
 public:
 	ResourceManager(/* args */);
 	~ResourceManager();
@@ -41,6 +43,8 @@ public:
 	void loadCursors();
 	void loadInteractionIcons();
 	void loadAlfredAnims();
+	void loadInventoryIcons();
+	InventoryObject getInventoryObject(byte index);
 	byte *loadExtra();
 
 	byte *alfredIdle[4] = {nullptr}; // 4 directions
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 865ecb3ab1a..31969c3f898 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -307,6 +307,12 @@ enum GameState {
 	INTRO = 106,
 };
 
+struct InventoryObject {
+	byte index;
+	Common::String description;
+	byte iconData[60 * 60];
+};
+
 } // End of namespace Pelrock
 
 #endif
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 76091bf1943..f204188003b 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -65,99 +65,99 @@ Common::String printMovementFlags(uint8_t flags) {
 }
 
 size_t rleDecompress(
-    const uint8_t *input,
-    size_t inputSize,
-    uint32_t offset,
-    uint32_t expectedSize,
-    uint8_t **out_data,
-    bool untilBuda) {
-    // Check for uncompressed markers
-    if (inputSize == 0x8000 || inputSize == 0x6800) {
-        *out_data = (uint8_t *)malloc(inputSize);
-        if (!*out_data)
-            return 0;
-        memcpy(*out_data, input + offset, inputSize);
-        return inputSize;
-    }
+	const uint8_t *input,
+	size_t inputSize,
+	uint32_t offset,
+	uint32_t expectedSize,
+	uint8_t **out_data,
+	bool untilBuda) {
+	// Check for uncompressed markers
+	if (inputSize == 0x8000 || inputSize == 0x6800) {
+		*out_data = (uint8_t *)malloc(inputSize);
+		if (!*out_data)
+			return 0;
+		memcpy(*out_data, input + offset, inputSize);
+		return inputSize;
+	}
 
-    // RLE compressed
-    size_t bufferCapacity;
-    size_t result_size = 0;
-    uint32_t pos = offset;
-    uint8_t last_value = 0;
+	// RLE compressed
+	size_t bufferCapacity;
+	size_t result_size = 0;
+	uint32_t pos = offset;
+	uint8_t last_value = 0;
 
-    if (untilBuda || expectedSize == 0) {
-        // Dynamic allocation mode - grow buffer as needed
-        bufferCapacity = 4096;
-        *out_data = (uint8_t *)malloc(bufferCapacity);
-        if (!*out_data)
-            return 0;
-    } else {
-        // Fixed size mode
-        bufferCapacity = expectedSize;
-        *out_data = (uint8_t *)malloc(bufferCapacity);
-        if (!*out_data)
-            return 0;
-    }
+	if (untilBuda || expectedSize == 0) {
+		// Dynamic allocation mode - grow buffer as needed
+		bufferCapacity = 4096;
+		*out_data = (uint8_t *)malloc(bufferCapacity);
+		if (!*out_data)
+			return 0;
+	} else {
+		// Fixed size mode
+		bufferCapacity = expectedSize;
+		*out_data = (uint8_t *)malloc(bufferCapacity);
+		if (!*out_data)
+			return 0;
+	}
 
-    while (pos + 2 <= inputSize) {
-        // Read the RLE pair
-        uint8_t count = input[pos];
-        uint8_t value = input[pos + 1];
-        last_value = value;
+	while (pos + 2 <= inputSize) {
+		// Read the RLE pair
+		uint8_t count = input[pos];
+		uint8_t value = input[pos + 1];
+		last_value = value;
 
-        // Write pixels for this pair
-        for (int i = 0; i < count; i++) {
-            // Grow buffer if needed
-            if (result_size >= bufferCapacity) {
-                if (untilBuda || expectedSize == 0) {
-                    bufferCapacity *= 2;
-                } else {
-                    // In fixed mode, we've hit the limit - something is wrong
-                    // but grow minimally to avoid crash
-                    bufferCapacity += 256;
-                }
-                uint8_t *newBuf = (uint8_t *)realloc(*out_data, bufferCapacity);
-                if (!newBuf) {
-                    free(*out_data);
-                    *out_data = nullptr;
-                    return 0;
-                }
-                *out_data = newBuf;
-            }
-            (*out_data)[result_size++] = value;
-        }
+		// Write pixels for this pair
+		for (int i = 0; i < count; i++) {
+			// Grow buffer if needed
+			if (result_size >= bufferCapacity) {
+				if (untilBuda || expectedSize == 0) {
+					bufferCapacity *= 2;
+				} else {
+					// In fixed mode, we've hit the limit - something is wrong
+					// but grow minimally to avoid crash
+					bufferCapacity += 256;
+				}
+				uint8_t *newBuf = (uint8_t *)realloc(*out_data, bufferCapacity);
+				if (!newBuf) {
+					free(*out_data);
+					*out_data = nullptr;
+					return 0;
+				}
+				*out_data = newBuf;
+			}
+			(*out_data)[result_size++] = value;
+		}
 
-        // Advance to next pair
-        pos += 2;
+		// Advance to next pair
+		pos += 2;
 
-        // Check for BUDA marker at new position
-        if (untilBuda && pos + 4 <= inputSize &&
-            input[pos] == 'B' && input[pos + 1] == 'U' &&
-            input[pos + 2] == 'D' && input[pos + 3] == 'A') {
-            // Game writes one final pixel after BUDA marker
-            // Grow buffer if needed
-            if (result_size >= bufferCapacity) {
-                bufferCapacity++;
-                uint8_t *newBuf = (uint8_t *)realloc(*out_data, bufferCapacity);
-                if (!newBuf) {
-                    free(*out_data);
-                    *out_data = nullptr;
-                    return 0;
-                }
-                *out_data = newBuf;
-            }
-            (*out_data)[result_size++] = last_value;
-            break;
-        }
+		// Check for BUDA marker at new position
+		if (untilBuda && pos + 4 <= inputSize &&
+			input[pos] == 'B' && input[pos + 1] == 'U' &&
+			input[pos + 2] == 'D' && input[pos + 3] == 'A') {
+			// Game writes one final pixel after BUDA marker
+			// Grow buffer if needed
+			if (result_size >= bufferCapacity) {
+				bufferCapacity++;
+				uint8_t *newBuf = (uint8_t *)realloc(*out_data, bufferCapacity);
+				if (!newBuf) {
+					free(*out_data);
+					*out_data = nullptr;
+					return 0;
+				}
+				*out_data = newBuf;
+			}
+			(*out_data)[result_size++] = last_value;
+			break;
+		}
 
-        // In fixed size mode, stop when we reach expected size
-        if (!untilBuda && expectedSize > 0 && result_size >= expectedSize) {
-            break;
-        }
-    }
+		// In fixed size mode, stop when we reach expected size
+		if (!untilBuda && expectedSize > 0 && result_size >= expectedSize) {
+			break;
+		}
+	}
 
-    return result_size;
+	return result_size;
 }
 
 void readUntilBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *&buffer, size_t &outSize) {
@@ -189,10 +189,8 @@ void readUntilBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *
 }
 
 // Helper function for transparent blitting
-void drawSpriteToBuffer(byte *buffer, int bufferWidth,
-						byte *sprite, int x, int y,
-						int width, int height,
-						int transparentColor) {
+void drawSpriteToBuffer(byte *buffer, int bufferWidth, byte *sprite, int x, int y, int width, int height, int transparentColor) {
+
 	for (int py = 0; py < height; py++) {
 		for (int px = 0; px < width; px++) {
 			int srcIdx = py * width + px;
@@ -250,7 +248,7 @@ void drawPos(Graphics::ManagedSurface *surface, int x, int y, byte color) {
 	if (x < 640 && y < 400 && x >= 0 && y >= 0) {
 		surface->setPixel(x, y, 100);
 
-		surface->drawEllipse(x - 3, y - 3, x+3, y+3, color, true);
+		surface->drawEllipse(x - 3, y - 3, x + 3, y + 3, color, true);
 	}
 }
 


Commit: 8463665fa1e8daf7de0511df1240d502f80666b5
    https://github.com/scummvm/scummvm/commit/8463665fa1e8daf7de0511df1240d502f80666b5
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:19+02:00

Commit Message:
PELROCK: Reads the right settings screen

Changed paths:
    engines/pelrock/resources.cpp
    engines/pelrock/util.h


diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 0364fe8a04b..e01d52d552c 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -262,16 +262,50 @@ void ResourceManager::loadSettingsMenu() {
 	}
 
 	_mainMenu = new byte[640 * 400];
-
-	alfred7.seek(kSettingsPaletteOffset, SEEK_SET);
-	alfred7.read(_mainMenuPalette, 768);
-	for (int i = 0; i < 256; i++) {
-		_mainMenuPalette[i * 3] = _mainMenuPalette[i * 3] << 2;
-		_mainMenuPalette[i * 3 + 1] = _mainMenuPalette[i * 3 + 1] << 2;
-		_mainMenuPalette[i * 3 + 2] = _mainMenuPalette[i * 3 + 2] << 2;
+	for (int i = 0; i < 640 * 400; i++) {
+		_mainMenu[i] = 13;
 	}
 
-	mergeRleBlocks(&alfred7, kSettingsMenuOffset, 8, _mainMenu);
+	alfred7.seek(kSettingsPaletteOffset, SEEK_SET);
+	// alfred7.read(_mainMenuPalette, 768);
+	// for (int i = 0; i < 256; i++) {
+	// 	_mainMenuPalette[i * 3] = _mainMenuPalette[i * 3] << 2;
+	// 	_mainMenuPalette[i * 3 + 1] = _mainMenuPalette[i * 3 + 1] << 2;
+	// 	_mainMenuPalette[i * 3 + 2] = _mainMenuPalette[i * 3 + 2] << 2;
+	// }
+
+	uint32 curPos = 0;
+	alfred7.seek(2405266, SEEK_SET);
+	alfred7.read(_mainMenu, 65536);
+
+	curPos += 65536;
+
+	byte *compressedPart1 = new byte[29418];
+	alfred7.read(compressedPart1, 29418);
+	byte *decompressedPart1 = nullptr;
+	size_t decompressedSize = rleDecompress(compressedPart1, 29418, 0, 0, &decompressedPart1, true);
+
+	memcpy(_mainMenu + curPos, decompressedPart1, decompressedSize);
+	curPos += decompressedSize;
+
+
+	delete[] compressedPart1;
+	delete[] decompressedPart1;
+	alfred7.seek(2500220, SEEK_SET);
+	alfred7.read(_mainMenu + curPos, 32768);
+	curPos += 32768;
+	byte *compressedPart2 = new byte[30288];
+	alfred7.read(compressedPart2, 30288);
+	byte *decompressedPart2 = nullptr;
+	decompressedSize = rleDecompress(compressedPart2, 30288, 0, 0, &decompressedPart2, true);
+
+	memcpy(_mainMenu + curPos, decompressedPart2, decompressedSize);
+	curPos += decompressedSize;
+	debug("Settings menu size loaded: %d, with last block %d", curPos, curPos + 92160);
+	delete[] compressedPart2;
+	delete[] decompressedPart2;
+	alfred7.seek(2563266, SEEK_SET);
+	alfred7.read(_mainMenu + curPos, 92160);
 	alfred7.close();
 }
 
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index 38783d96ac4..593aac4e2df 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -33,10 +33,7 @@ namespace Pelrock {
 const int EXPECTED_SIZE = 640 * 400;
 size_t rleDecompress(const uint8_t *data, size_t data_size, uint32_t offset, uint32_t size, uint8_t **out_data, bool untilBuda = true);
 void readUntilBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *&buffer, size_t &outSize);
-void drawSpriteToBuffer(byte *buffer, int bufferWidth,
-						byte *sprite, int x, int y,
-						int width, int height,
-						int transparentColor);
+void drawSpriteToBuffer(byte *buffer, int bufferWidth, byte *sprite, int x, int y, int width, int height, int transparentColor);
 void blitSurfaceToBuffer(Graphics::Surface *surface, byte *buffer, int bufferWidth, int bufferHeight, int destX, int destY);
 void extractSingleFrame(byte *source, byte *dest, int frameIndex, int frameWidth, int frameHeight);
 void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byte color);


Commit: 4c4a20c93cd918b0d11b62ab5428f1f60565c935
    https://github.com/scummvm/scummvm/commit/4c4a20c93cd918b0d11b62ab5428f1f60565c935
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:19+02:00

Commit Message:
PELROCK: Loads right palette for settings menu

Changed paths:
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp


diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index e69c4a34e87..0fafede4be0 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -39,8 +39,8 @@ namespace Pelrock {
     static const uint32_t ALFRED7_ALFRED_COMB_R = 67768;
     static const uint32_t ALFRED7_ALFRED_COMB_L = 88408;
 
-    static const uint32_t kSettingsMenuOffset = 910097; // Placeholder offset
-    static const uint32_t kSettingsPaletteOffset = 1038141; // 640 * 480
+
+    static const uint32_t kSettingsPaletteOffset = 0x2884c2; // 640 * 480
 
 
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 21cd123dee1..e9137e1cd23 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -152,8 +152,10 @@ Common::Error PelrockEngine::run() {
 				_displayPopup = false;
 				_longClick = false;
 			} else if (e.type == Common::EVENT_RBUTTONUP) {
-				if (stateGame != SETTINGS)
+				if (stateGame != SETTINGS){
+					g_system->getPaletteManager()->setPalette(_res->_mainMenuPalette, 0, 256);
 					stateGame = SETTINGS;
+				}
 				else {
 					g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
 					stateGame = GAME;
@@ -170,8 +172,6 @@ Common::Error PelrockEngine::run() {
 		}
 		checkMouseHover();
 		if (stateGame == SETTINGS) {
-			g_system->getPaletteManager()->setPalette(_res->_mainMenuPalette, 0, 256);
-
 			memcpy(_compositeBuffer, _res->_mainMenu, 640 * 400);
 
 			for (int i = 0; i < 4; i++) {
@@ -339,6 +339,7 @@ void PelrockEngine::frames() {
 		// Draw Alfred here (you'll need to add this)
 		chooseAlfredStateAndDraw();
 
+
 		// Second pass: sprites in front of Alfred (y > alfredY)
 		for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
 			if (_room->_currentRoomAnims[i].zOrder <= 10) {
@@ -766,6 +767,7 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 
 void PelrockEngine::checkLongMouseClick(int x, int y) {
 	int hotspotIndex = isHotspotUnder(mouseX, mouseY);
+
 	if (hotspotIndex != -1) {
 
 		_popupX = x - kBalloonWidth / 2;
@@ -1192,7 +1194,7 @@ uint16_t PelrockEngine::buildWalkboxPath(uint8_t startBox, uint8_t destBox, uint
 
 	// Terminate path
 	pathBuffer[pathIndex] = PATH_END;
-
+	debug("Built walkbox path of length %d", pathIndex);
 	return pathIndex;
 }
 
@@ -1332,7 +1334,11 @@ void PelrockEngine::checkMouseHover() {
 	}
 
 	int hotspotIndex = isHotspotUnder(mouseX, mouseY);
-
+	if(hotspotIndex != -1) {
+	debug("Hotspot under mouse: %d, %d (extra = %d)", _room->_currentRoomHotspots[hotspotIndex].x,
+		  _room->_currentRoomHotspots[hotspotIndex].y,
+		  hotspotIndex != -1 ? _room->_currentRoomHotspots[hotspotIndex].extra : -1);
+	}
 	if (hotspotIndex != -1) {
 		isSomethingUnder = true;
 	}
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index e01d52d552c..9bddb612599 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -267,12 +267,12 @@ void ResourceManager::loadSettingsMenu() {
 	}
 
 	alfred7.seek(kSettingsPaletteOffset, SEEK_SET);
-	// alfred7.read(_mainMenuPalette, 768);
-	// for (int i = 0; i < 256; i++) {
-	// 	_mainMenuPalette[i * 3] = _mainMenuPalette[i * 3] << 2;
-	// 	_mainMenuPalette[i * 3 + 1] = _mainMenuPalette[i * 3 + 1] << 2;
-	// 	_mainMenuPalette[i * 3 + 2] = _mainMenuPalette[i * 3 + 2] << 2;
-	// }
+	alfred7.read(_mainMenuPalette, 768);
+	for (int i = 0; i < 256; i++) {
+		_mainMenuPalette[i * 3] = _mainMenuPalette[i * 3] << 2;
+		_mainMenuPalette[i * 3 + 1] = _mainMenuPalette[i * 3 + 1] << 2;
+		_mainMenuPalette[i * 3 + 2] = _mainMenuPalette[i * 3 + 2] << 2;
+	}
 
 	uint32 curPos = 0;
 	alfred7.seek(2405266, SEEK_SET);


Commit: c7ec5a2917a1f61c070f24b82a748f09df7c69b1
    https://github.com/scummvm/scummvm/commit/c7ec5a2917a1f61c070f24b82a748f09df7c69b1
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:20+02:00

Commit Message:
PELROCK: Splits menu and game event handling

Changed paths:
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp


diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 0fafede4be0..4719d02d333 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -39,8 +39,8 @@ namespace Pelrock {
     static const uint32_t ALFRED7_ALFRED_COMB_R = 67768;
     static const uint32_t ALFRED7_ALFRED_COMB_L = 88408;
 
-
-    static const uint32_t kSettingsPaletteOffset = 0x2884c2; // 640 * 480
+    static const uint32_t kAlternateSettingsMenuOffset = 910097; // Placeholder offset
+    static const uint32_t kSettingsPaletteOffset = 1038141; // 640 * 480
 
 
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index e9137e1cd23..6a0c3344ec9 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -107,86 +107,11 @@ Common::Error PelrockEngine::run() {
 	init();
 
 	while (!shouldQuit()) {
-		_chronoManager->updateChrono();
-		while (g_system->getEventManager()->pollEvent(e)) {
-			if (e.type == Common::EVENT_KEYDOWN) {
-				switch (e.kbd.keycode) {
-				case Common::KEYCODE_w:
-					alfredState.animState = ALFRED_WALKING;
-					break;
-				case Common::KEYCODE_t:
-					alfredState.animState = ALFRED_TALKING;
-					break;
-				case Common::KEYCODE_s:
-					alfredState.animState = ALFRED_IDLE;
-					break;
-				case Common::KEYCODE_c:
-					alfredState.animState = ALFRED_COMB;
-					break;
-				case Common::KEYCODE_i:
-					alfredState.animState = ALFRED_INTERACTING;
-					break;
-				case Common::KEYCODE_z:
-					showShadows = !showShadows;
-					break;
-				case Common::KEYCODE_y:
-					alfredState.x = 193;
-					alfredState.y = 382;
-					walkTo(377, 318);
-					break;
-				default:
-					break;
-				}
-			} else if (e.type == Common::EVENT_MOUSEMOVE) {
-				mouseX = e.mouse.x;
-				mouseY = e.mouse.y;
-				// debug(3, "Mouse moved to (%d,%d)", mouseX, mouseY);
-			} else if (e.type == Common::EVENT_LBUTTONDOWN) {
-				if (!_isMouseDown) {
-					_mouseClickTime = g_system->getMillis();
-					_isMouseDown = true;
-				}
-			} else if (e.type == Common::EVENT_LBUTTONUP) {
-				_isMouseDown = false;
-				checkMouseClick(e.mouse.x, e.mouse.y);
-				_displayPopup = false;
-				_longClick = false;
-			} else if (e.type == Common::EVENT_RBUTTONUP) {
-				if (stateGame != SETTINGS){
-					g_system->getPaletteManager()->setPalette(_res->_mainMenuPalette, 0, 256);
-					stateGame = SETTINGS;
-				}
-				else {
-					g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
-					stateGame = GAME;
-				}
-			}
-		}
-		if (_isMouseDown) {
-			if (g_system->getMillis() - _mouseClickTime >= kLongClickDuration) {
-				debug("long click!");
-				_longClick = true;
-				_isMouseDown = false;
-				checkLongMouseClick(e.mouse.x, e.mouse.y);
-			}
-		}
-		checkMouseHover();
 		if (stateGame == SETTINGS) {
-			memcpy(_compositeBuffer, _res->_mainMenu, 640 * 400);
-
-			for (int i = 0; i < 4; i++) {
-				InventoryObject item = _res->getInventoryObject(i);
-				drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 140 + i * 85, 110 - i * 5, 60, 60, 1);
-			}
-			memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
-
-			g_engine->_screen->markAllDirty();
-			g_engine->_screen->update();
-
+			menuLoop();
 		} else if (stateGame == GAME) {
-			frames();
+			gameLoop();
 		}
-
 		_screen->update();
 		// limiter.delayBeforeSwap();
 		// limiter.startFrame();
@@ -339,7 +264,6 @@ void PelrockEngine::frames() {
 		// Draw Alfred here (you'll need to add this)
 		chooseAlfredStateAndDraw();
 
-
 		// Second pass: sprites in front of Alfred (y > alfredY)
 		for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
 			if (_room->_currentRoomAnims[i].zOrder <= 10) {
@@ -975,6 +899,96 @@ void PelrockEngine::drawTalkNPC(Sprite *animSet) {
 	drawSpriteToBuffer(_compositeBuffer, 640, frame, x, y, w, h, 255);
 }
 
+void PelrockEngine::gameLoop() {
+	_chronoManager->updateChrono();
+	Common::Event e;
+	while (g_system->getEventManager()->pollEvent(e)) {
+		if (e.type == Common::EVENT_KEYDOWN) {
+			switch (e.kbd.keycode) {
+			case Common::KEYCODE_w:
+				alfredState.animState = ALFRED_WALKING;
+				break;
+			case Common::KEYCODE_t:
+				alfredState.animState = ALFRED_TALKING;
+				break;
+			case Common::KEYCODE_s:
+				alfredState.animState = ALFRED_IDLE;
+				break;
+			case Common::KEYCODE_c:
+				alfredState.animState = ALFRED_COMB;
+				break;
+			case Common::KEYCODE_i:
+				alfredState.animState = ALFRED_INTERACTING;
+				break;
+			case Common::KEYCODE_z:
+				showShadows = !showShadows;
+				break;
+			case Common::KEYCODE_y:
+				alfredState.x = 193;
+				alfredState.y = 382;
+				walkTo(377, 318);
+				break;
+			default:
+				break;
+			}
+		} else if (e.type == Common::EVENT_MOUSEMOVE) {
+			mouseX = e.mouse.x;
+			mouseY = e.mouse.y;
+			// debug(3, "Mouse moved to (%d,%d)", mouseX, mouseY);
+		} else if (e.type == Common::EVENT_LBUTTONDOWN) {
+			if (!_isMouseDown) {
+				_mouseClickTime = g_system->getMillis();
+				_isMouseDown = true;
+			}
+		} else if (e.type == Common::EVENT_LBUTTONUP) {
+			_isMouseDown = false;
+			checkMouseClick(e.mouse.x, e.mouse.y);
+			_displayPopup = false;
+			_longClick = false;
+		} else if (e.type == Common::EVENT_RBUTTONUP) {
+			g_system->getPaletteManager()->setPalette(_res->_mainMenuPalette, 0, 256);
+			stateGame = SETTINGS;
+		}
+	}
+	if (_isMouseDown) {
+		if (g_system->getMillis() - _mouseClickTime >= kLongClickDuration) {
+			debug("long click!");
+			_longClick = true;
+			_isMouseDown = false;
+			checkLongMouseClick(e.mouse.x, e.mouse.y);
+		}
+	}
+	checkMouseHover();
+	frames();
+}
+
+void PelrockEngine::menuLoop() {
+	Common::Event e;
+	while (g_system->getEventManager()->pollEvent(e)) {
+		if (e.type == Common::EVENT_LBUTTONUP) {
+			// _isMouseDown = false;
+			// checkMouseClick(e.mouse.x, e.mouse.y);
+			// _displayPopup = false;
+			// _longClick = false;
+		} else if (e.type == Common::EVENT_RBUTTONUP) {
+
+			g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
+			stateGame = GAME;
+		}
+	}
+
+	memcpy(_compositeBuffer, _res->_mainMenu, 640 * 400);
+
+	for (int i = 0; i < 4; i++) {
+		InventoryObject item = _res->getInventoryObject(i);
+		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 140 + i * 85, 110 - i * 5, 60, 60, 1);
+	}
+	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
+
+	g_engine->_screen->markAllDirty();
+	g_engine->_screen->update();
+}
+
 void PelrockEngine::walkTo(int x, int y) {
 	_currentStep = 0;
 	PathContext context = {nullptr, nullptr, nullptr, 0, 0, 0};
@@ -1334,10 +1348,10 @@ void PelrockEngine::checkMouseHover() {
 	}
 
 	int hotspotIndex = isHotspotUnder(mouseX, mouseY);
-	if(hotspotIndex != -1) {
-	debug("Hotspot under mouse: %d, %d (extra = %d)", _room->_currentRoomHotspots[hotspotIndex].x,
-		  _room->_currentRoomHotspots[hotspotIndex].y,
-		  hotspotIndex != -1 ? _room->_currentRoomHotspots[hotspotIndex].extra : -1);
+	if (hotspotIndex != -1) {
+		debug("Hotspot under mouse: %d, %d (extra = %d)", _room->_currentRoomHotspots[hotspotIndex].x,
+			  _room->_currentRoomHotspots[hotspotIndex].y,
+			  hotspotIndex != -1 ? _room->_currentRoomHotspots[hotspotIndex].extra : -1);
 	}
 	if (hotspotIndex != -1) {
 		isSomethingUnder = true;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 37a530f65b4..f346eee6b8c 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -101,6 +101,9 @@ private:
 	void changeCursor(Cursor cursor);
 	void drawTalkNPC(Sprite *animSet);
 
+	void gameLoop();
+	void menuLoop();
+
 	void checkMouseHover();
 	void checkMouseClick(int x, int y);
 	void checkLongMouseClick(int x, int y);
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 9bddb612599..67bd6ba1bbb 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -255,6 +255,8 @@ void ResourceManager::mergeRleBlocks(Common::SeekableReadStream *stream, uint32
 }
 
 void ResourceManager::loadSettingsMenu() {
+
+	bool alternateMenu = true;
 	Common::File alfred7;
 	if (!alfred7.open(Common::Path("ALFRED.7"))) {
 		error("Could not open ALFRED.7");
@@ -262,51 +264,68 @@ void ResourceManager::loadSettingsMenu() {
 	}
 
 	_mainMenu = new byte[640 * 400];
-	for (int i = 0; i < 640 * 400; i++) {
-		_mainMenu[i] = 13;
-	}
 
-	alfred7.seek(kSettingsPaletteOffset, SEEK_SET);
-	alfred7.read(_mainMenuPalette, 768);
-	for (int i = 0; i < 256; i++) {
-		_mainMenuPalette[i * 3] = _mainMenuPalette[i * 3] << 2;
-		_mainMenuPalette[i * 3 + 1] = _mainMenuPalette[i * 3 + 1] << 2;
-		_mainMenuPalette[i * 3 + 2] = _mainMenuPalette[i * 3 + 2] << 2;
-	}
+	if (!alternateMenu) {
+		alfred7.seek(kSettingsPaletteOffset, SEEK_SET);
+		alfred7.read(_mainMenuPalette, 768);
+		for (int i = 0; i < 256; i++) {
+			_mainMenuPalette[i * 3] = _mainMenuPalette[i * 3] << 2;
+			_mainMenuPalette[i * 3 + 1] = _mainMenuPalette[i * 3 + 1] << 2;
+			_mainMenuPalette[i * 3 + 2] = _mainMenuPalette[i * 3 + 2] << 2;
+		}
 
-	uint32 curPos = 0;
-	alfred7.seek(2405266, SEEK_SET);
-	alfred7.read(_mainMenu, 65536);
-
-	curPos += 65536;
-
-	byte *compressedPart1 = new byte[29418];
-	alfred7.read(compressedPart1, 29418);
-	byte *decompressedPart1 = nullptr;
-	size_t decompressedSize = rleDecompress(compressedPart1, 29418, 0, 0, &decompressedPart1, true);
-
-	memcpy(_mainMenu + curPos, decompressedPart1, decompressedSize);
-	curPos += decompressedSize;
-
-
-	delete[] compressedPart1;
-	delete[] decompressedPart1;
-	alfred7.seek(2500220, SEEK_SET);
-	alfred7.read(_mainMenu + curPos, 32768);
-	curPos += 32768;
-	byte *compressedPart2 = new byte[30288];
-	alfred7.read(compressedPart2, 30288);
-	byte *decompressedPart2 = nullptr;
-	decompressedSize = rleDecompress(compressedPart2, 30288, 0, 0, &decompressedPart2, true);
-
-	memcpy(_mainMenu + curPos, decompressedPart2, decompressedSize);
-	curPos += decompressedSize;
-	debug("Settings menu size loaded: %d, with last block %d", curPos, curPos + 92160);
-	delete[] compressedPart2;
-	delete[] decompressedPart2;
-	alfred7.seek(2563266, SEEK_SET);
-	alfred7.read(_mainMenu + curPos, 92160);
-	alfred7.close();
+		uint32 curPos = 0;
+		alfred7.seek(2405266, SEEK_SET);
+		alfred7.read(_mainMenu, 65536);
+
+		curPos += 65536;
+
+		byte *compressedPart1 = new byte[29418];
+		alfred7.read(compressedPart1, 29418);
+		byte *decompressedPart1 = nullptr;
+		size_t decompressedSize = rleDecompress(compressedPart1, 29418, 0, 0, &decompressedPart1, true);
+
+		memcpy(_mainMenu + curPos, decompressedPart1, decompressedSize);
+		curPos += decompressedSize;
+
+		delete[] compressedPart1;
+		delete[] decompressedPart1;
+		alfred7.seek(2500220, SEEK_SET);
+		alfred7.read(_mainMenu + curPos, 32768);
+		curPos += 32768;
+		byte *compressedPart2 = new byte[30288];
+		alfred7.read(compressedPart2, 30288);
+		byte *decompressedPart2 = nullptr;
+		decompressedSize = rleDecompress(compressedPart2, 30288, 0, 0, &decompressedPart2, true);
+
+		memcpy(_mainMenu + curPos, decompressedPart2, decompressedSize);
+		curPos += decompressedSize;
+		debug("Settings menu size loaded: %d, with last block %d", curPos, curPos + 92160);
+		delete[] compressedPart2;
+		delete[] decompressedPart2;
+		alfred7.seek(2563266, SEEK_SET);
+		alfred7.read(_mainMenu + curPos, 92160);
+		alfred7.close();
+	} else {
+		Common::File alfred7;
+		if (!alfred7.open(Common::Path("ALFRED.7"))) {
+			error("Could not open ALFRED.7");
+			return;
+		}
+
+		_mainMenu = new byte[640 * 400];
+
+		alfred7.seek(kSettingsPaletteOffset, SEEK_SET);
+		alfred7.read(_mainMenuPalette, 768);
+		for (int i = 0; i < 256; i++) {
+			_mainMenuPalette[i * 3] = _mainMenuPalette[i * 3] << 2;
+			_mainMenuPalette[i * 3 + 1] = _mainMenuPalette[i * 3 + 1] << 2;
+			_mainMenuPalette[i * 3 + 2] = _mainMenuPalette[i * 3 + 2] << 2;
+		}
+
+		mergeRleBlocks(&alfred7, kAlternateSettingsMenuOffset, 8, _mainMenu);
+		alfred7.close();
+	}
 }
 
 } // End of namespace Pelrock


Commit: 15b0077a61493db6202e0ccfda8a56f6a6f9b71f
    https://github.com/scummvm/scummvm/commit/15b0077a61493db6202e0ccfda8a56f6a6f9b71f
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:20+02:00

Commit Message:
PELROCK: Remove g_engine references from engine

Changed paths:
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 6a0c3344ec9..f229527dcda 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -185,7 +185,7 @@ void PelrockEngine::putBackgroundSlice(int x, int y, int w, int h, byte *slice)
 		for (int j = 0; j < h; j++) {
 			int index = (j * w + i);
 			if (x + i < 640 && y + j < 400)
-				*(byte *)g_engine->_screen->getBasePtr(x + i, y + j) = slice[index];
+				*(byte *)_screen->getBasePtr(x + i, y + j) = slice[index];
 		}
 	}
 }
@@ -985,8 +985,8 @@ void PelrockEngine::menuLoop() {
 	}
 	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
 
-	g_engine->_screen->markAllDirty();
-	g_engine->_screen->update();
+	_screen->markAllDirty();
+	_screen->update();
 }
 
 void PelrockEngine::walkTo(int x, int y) {


Commit: 9a2c56321be123d02869dc19ba8bb8c1f87e109a
    https://github.com/scummvm/scummvm/commit/9a2c56321be123d02869dc19ba8bb8c1f87e109a
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:20+02:00

Commit Message:
PELROCK: Loads inventory descriptions

Changed paths:
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h


diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 4719d02d333..6719c50478b 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -25,25 +25,145 @@
 
 namespace Pelrock {
 
-    static const uint32_t cursor_offsets[5] = {
-        0x0FDDFD,
-        0x0FDCDD,
-        0x0FDF1D,
-        0x0FE33D,
-        0x367EF0
-    };
+static const uint32_t cursor_offsets[5] = {
+	0x0FDDFD,
+	0x0FDCDD,
+	0x0FDF1D,
+	0x0FE33D,
+	0x367EF0};
 
-    static const uint32_t kBalloonFramesOffset = 2176936;
-    static const uint32_t kBalloonFramesSize = 24950;
+static const uint32_t kBalloonFramesOffset = 2176936;
+static const uint32_t kBalloonFramesSize = 24950;
 
-    static const uint32_t ALFRED7_ALFRED_COMB_R = 67768;
-    static const uint32_t ALFRED7_ALFRED_COMB_L = 88408;
+static const uint32_t ALFRED7_ALFRED_COMB_R = 67768;
+static const uint32_t ALFRED7_ALFRED_COMB_L = 88408;
 
-    static const uint32_t kAlternateSettingsMenuOffset = 910097; // Placeholder offset
-    static const uint32_t kSettingsPaletteOffset = 1038141; // 640 * 480
+static const uint32_t kAlternateSettingsMenuOffset = 910097; // Placeholder offset
+static const uint32_t kSettingsPaletteOffset = 1038141;      // 640 * 480
 
+#define DESCRIPTION_BASE_OFFSET 0x4715D
+#define NUM_DESCRIPTIONS 113
 
+static const uint32 kInventoryDescriptionsOffset = 0x4715D;
+static const uint32 kInventoryDescriptionsSize = 7868;
+
+
+// Description offsets relative to DESCRIPTION_BASE_OFFSET
+const uint16_t description_offsets[NUM_DESCRIPTIONS] = {
+	0x0000, // Object 0: Historia de la Princesa Zenna y su amante insatisfecho
+	0x0058, // Object 1: Nombre: Alfred Pelrock
+	0x00C4, // Object 2: La tipica tarjeta por la que te sacan commisiones
+	0x010E, // Object 3: Una pequeña foto de Alfred
+	0x012C, // Object 4: Un ladrillo
+	0x013B, // Object 5: 1000 pesetas
+	0x014B, // Object 6: Una alargadera con un extremo suelto
+	0x0173, // Object 7: Un amuleto egipcio con forma de escarabajo alado
+	0x01A7, // Object 8: Dice: OM OM RASKAMAMOM
+	0x01C1, // Object 9: Es una carta de la Asociacion Ra-Amoniana
+	0x020A, // Object 10: Un carnet de biblioteca
+	0x0247, // Object 11: Titulo: Canticos espirituales en formato *.zip
+	0x02B3, // Object 12: Titulo: Pasion Flagrante
+	0x0327, // Object 13: Titulo: El Valenciano en los comienzos del siglo XXI
+	0x039E, // Object 14: Titulo: El sistema inmunologico de los cefalopodos (v.I)
+	0x0412, // Object 15: Titulo: Dos y dos son 5
+	0x0493, // Object 16: Titulo: La parte creativa
+	0x057B, // Object 17: Titulo: 10 maneras de preparar fideos chinos
+	0x065C, // Object 18: Titulo: Los Peces Gato del Rio Tajo
+	0x06D8, // Object 19: Titulo: Gato por liebre
+	0x0753, // Object 20: Titulo: Hiper-cocina para solteros
+	0x07CE, // Object 21: Titulo: El camaleon humano
+	0x084E, // Object 22: Titulo: Psiquiatria Avanzada (vol. 8)
+	0x08CA, // Object 23: Titulo: Sistemas de alcantarillado en el siglo XV
+	0x0949, // Object 24: Titulo: Cartas de amor de Pol Pot a su novia
+	0x09CC, // Object 25: Titulo: El gran libro de las preposiciones
+	0x0A50, // Object 26: Titulo: Corazon, vida y muerte de un tenista
+	0x0ACC, // Object 27: Titulo: Analisis de la vida de los funcionarios
+	0x0B4D, // Object 28: Titulo: Ensayos sobre la putrefaccion
+	0x0BC9, // Object 29: Titulo: Cocinar bien es imposible
+	0x0C49, // Object 30: Titulo: 1000 formas de hacer sonar un claxon
+	0x0CC8, // Object 31: Titulo: El arte de la peluqueria
+	0x0D3B, // Object 32: Titulo: Analisis de las tramas de las mejores telecomedias
+	0x0DC7, // Object 33: Titulo: Tratado de las empanadillas
+	0x0E40, // Object 34: Titulo: Misterios de los numeros
+	0x0EBA, // Object 35: Titulo: Como vender mas
+	0x0F31, // Object 36: Titulo: Todos podemos estar de moda
+	0x0FAD, // Object 37: Titulo: La economia capitalista (Tomo VI)
+	0x102E, // Object 38: Titulo: Aventuras con mis hemorrides
+	0x10AB, // Object 39: Titulo: Automate. Tomo IV: Suicidio
+	0x1128, // Object 40: Titulo: El cienpies azul
+	0x11A1, // Object 41: Titulo: Guia sexual de la mosca
+	0x121E, // Object 42: Titulo: La Oveja. El gran misterio
+	0x1297, // Object 43: Titulo: Mi libro de cocina
+	0x1309, // Object 44: Titulo: Ariel
+	0x1377, // Object 45: Titulo: Matar cucarachas con la mirada
+	0x13F4, // Object 46: Titulo: Telepatia: Caso practico
+	0x1476, // Object 47: Titulo: Vida y obra de Paquirrin
+	0x14F4, // Object 48: Titulo: Odas para aliviar el estrenimiento
+	0x1577, // Object 49: Titulo: Mi vida en el gran mercado
+	0x15F4, // Object 50: Titulo: Oda al tocino
+	0x1669, // Object 51: Titulo: Como escribir una novela
+	0x16E0, // Object 52: Titulo: Recogiendo oro en las cloacas
+	0x175B, // Object 53: Titulo: Como comer bien. Tomo XXI. Entrantes
+	0x17DD, // Object 54: Titulo: No tengo nada mejor que hacer
+	0x185D, // Object 55: Titulo: Los Heraldos Negros
+	0x18D1, // Object 56: Titulo: La Piedra Rosetta
+	0x194A, // Object 57: Titulo: Fabulas de Ciencia Ficcion
+	0x19C3, // Object 58: Titulo: Elogio de la pereza
+	0x1A35, // Object 59: Un pequeño altavoz
+	0x1A52, // Object 60: Un altavoz mediano
+	0x1A70, // Object 61: Un altavoz grande
+	0x1A8C, // Object 62: Un extintor
+	0x1AA3, // Object 63: Parece un extintor, pero es un termo de cafe. De los de antes, de los grandes
+	0x1B2D, // Object 64: Un termo con cafe
+	0x1B53, // Object 65: Una cafetera expres para una taza
+	0x1B8D, // Object 66: La tarjeta de acceso de Erika
+	0x1BBE, // Object 67: Billetes de avion para salir por patas hacia Valencia
+	0x1C26, // Object 68: Una bolsa de patatas fritas
+	0x1C52, // Object 69: Llave de la habitacion de Lucy
+	0x1C82, // Object 70: Llave de la habitacion de Erika
+	0x1CB3, // Object 71: Un ordenador de sobremesa
+	0x1CE1, // Object 72: Un cuadro
+	0x1CF9, // Object 73: Una moto mega-retuneada
+	0x1D24, // Object 74: Una lamparita
+	0x1D3F, // Object 75: Un libro gordo
+	0x1D5C, // Object 76: Un libro finito
+	0x1D7A, // Object 77: Un poco de escayola vieja
+	0x1DA3, // Object 78: Un cacho de ladrillo
+	0x1DCC, // Object 79: Un boligrafo
+	0x1DE5, // Object 80: Un radiocasete
+	0x1E00, // Object 81: Es una pistola. (Real como la vida misma)
+	0x1E49, // Object 82: Una pieza de fruta
+	0x1E6B, // Object 83: Un frasco de pastillas para dormir
+	0x1EA0, // Object 84: Una pulsera
+	0x1EB9, // Object 85: Una estatua pequeña
+	0x1EE0, // Object 86: Una jodida (disculpen las molestias) cinta de video
+	0x1F4F, // Object 87: Una jodida (disculpen las molestias) cadena hifi
+	0x1FBE, // Object 88: Una magdalena
+	0x1FD8, // Object 89: Un poco de cecina
+	0x1FFC, // Object 90: Un televisor portatil con forma de Mickey Mouse
+	0x2053, // Object 91: Un destornillador
+	0x2071, // Object 92: Alicates de electricista
+	0x2097, // Object 93: Un cable
+	0x20AE, // Object 94: Una linterna
+	0x20C6, // Object 95: Unas pilas gigantes
+	0x20E7, // Object 96: La bolsa de basura negra
+	0x2116, // Object 97: Foto de un tal Gerardo (desconocido)
+	0x2158, // Object 98: Una cinta de casete
+	0x2179, // Object 99: Un walkman
+	0x218F, // Object 100: Un papel con un telefono
+	0x21BE, // Object 101: Una llave grande de metacrilato
+	0x21F1, // Object 102: Una pequeña llave
+	0x220E, // Object 103: Autenticas naranjas de Nules
+	0x2236, // Object 104: No se haga el loco: Llameme !!!
+	0x2285, // Object 105: Folletos explicativos sobre el SIDA
+	0x22BE, // Object 106: Un pin que acredita mi sabiduria
+	0x22E2, // Object 107: Una bayeta para frotar lamparas magicas
+	0x2316, // Object 108: Parches ultra-fuertes
+	0x2337, // Object 109: Pegamento que te cagas
+	0x235A, // Object 110: Una replica de Alfred pinchada
+	0x2383, // Object 111: Una cinta del Rey Elvis
+	0x23A4, // Object 112: Una caja de condone
+};
 
 } // End of namespace Pelrock
 #endif
-
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index f229527dcda..2ee4d77cc90 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -124,6 +124,7 @@ void PelrockEngine::init() {
 	_res->loadCursors();
 	_res->loadInteractionIcons();
 	_res->loadInventoryIcons();
+	_res->loadInventoryDescriptions();
 	_res->loadSettingsMenu();
 	_soundManager->loadSoundIndex();
 
@@ -1348,11 +1349,6 @@ void PelrockEngine::checkMouseHover() {
 	}
 
 	int hotspotIndex = isHotspotUnder(mouseX, mouseY);
-	if (hotspotIndex != -1) {
-		debug("Hotspot under mouse: %d, %d (extra = %d)", _room->_currentRoomHotspots[hotspotIndex].x,
-			  _room->_currentRoomHotspots[hotspotIndex].y,
-			  hotspotIndex != -1 ? _room->_currentRoomHotspots[hotspotIndex].extra : -1);
-	}
 	if (hotspotIndex != -1) {
 		isSomethingUnder = true;
 	}
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 67bd6ba1bbb..67916788d49 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -233,6 +233,44 @@ void ResourceManager::loadInventoryIcons() {
 	delete[] iconData;
 }
 
+void ResourceManager::loadInventoryDescriptions() {
+	Common::File exe;
+	if (!exe.open("JUEGO.EXE")) {
+		error("Couldnt find file JUEGO.EXE");
+	}
+	byte *descBuffer = new byte[kInventoryDescriptionsSize];
+	exe.seek(kInventoryDescriptionsOffset, SEEK_SET);
+	exe.read(descBuffer, kInventoryDescriptionsSize);
+	int pos = 0;
+	Common::String desc = "";
+	while (pos < kInventoryDescriptionsSize) {
+		if (descBuffer[pos] == 0xFD) {
+			if (!desc.empty()) {
+				_inventoryDescriptions.push_back(desc);
+				desc = Common::String();
+			}
+			pos++;
+			continue;
+		}
+		if (descBuffer[pos] == 0x00) {
+			pos++;
+			continue;
+		}
+		if (descBuffer[pos] == 0x08) {
+			pos += 2;
+			continue;
+		}
+
+		desc.append(1, descBuffer[pos]);
+		if (pos + 1 == kInventoryDescriptionsSize) {
+			_inventoryDescriptions.push_back(desc);
+		}
+		pos++;
+	}
+	delete[] descBuffer;
+	exe.close();
+}
+
 InventoryObject ResourceManager::getInventoryObject(byte index) {
 	return _inventoryIcons[index];
 }
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index 4561e78e799..dd7c0d0c148 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -44,6 +44,7 @@ public:
 	void loadInteractionIcons();
 	void loadAlfredAnims();
 	void loadInventoryIcons();
+	void loadInventoryDescriptions();
 	InventoryObject getInventoryObject(byte index);
 	byte *loadExtra();
 
@@ -62,6 +63,7 @@ public:
 
 	byte *_mainMenu = nullptr;
 	byte _mainMenuPalette[768] = {0};
+	Common::Array<Common::String> _inventoryDescriptions;
 };
 
 } // End of namespace Pelrock


Commit: 4ebec869e3ee6b5e1514a81fd51e63b659d6cc50
    https://github.com/scummvm/scummvm/commit/4ebec869e3ee6b5e1514a81fd51e63b659d6cc50
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:21+02:00

Commit Message:
PELROCK: Associates descriptions to objects

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 2ee4d77cc90..17e1ba67501 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -123,8 +123,7 @@ Common::Error PelrockEngine::run() {
 void PelrockEngine::init() {
 	_res->loadCursors();
 	_res->loadInteractionIcons();
-	_res->loadInventoryIcons();
-	_res->loadInventoryDescriptions();
+	_res->loadInventoryItems();
 	_res->loadSettingsMenu();
 	_soundManager->loadSoundIndex();
 
@@ -967,6 +966,9 @@ void PelrockEngine::menuLoop() {
 	Common::Event e;
 	while (g_system->getEventManager()->pollEvent(e)) {
 		if (e.type == Common::EVENT_LBUTTONUP) {
+
+			_menuText = _res->getInventoryObject(selectedInvIndex++).description;
+
 			// _isMouseDown = false;
 			// checkMouseClick(e.mouse.x, e.mouse.y);
 			// _displayPopup = false;
@@ -985,7 +987,7 @@ void PelrockEngine::menuLoop() {
 		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 140 + i * 85, 110 - i * 5, 60, 60, 1);
 	}
 	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
-
+	_smallFont->drawString(_screen, _menuText, 0, 0, 640, 0);
 	_screen->markAllDirty();
 	_screen->update();
 }
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index f346eee6b8c..bf00459347b 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -156,6 +156,10 @@ private:
 
 	bool showShadows = false;
 
+	//Temporary
+	int selectedInvIndex = 0;
+	Common::String _menuText;
+
 	// JAVA
 	bool shouldPlayIntro = false;
 	GameState stateGame = INTRO;
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 67916788d49..d33576d2deb 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -215,7 +215,8 @@ void ResourceManager::loadAlfredAnims() {
 	free(alfredCombLeftRaw);
 }
 
-void ResourceManager::loadInventoryIcons() {
+void ResourceManager::loadInventoryItems() {
+	loadInventoryDescriptions();
 	Common::File alfred4File;
 	if (!alfred4File.open("ALFRED.4")) {
 		error("Couldnt find file ALFRED.4");
@@ -229,6 +230,7 @@ void ResourceManager::loadInventoryIcons() {
 	for (int i = 0; i < 69; i++) {
 		_inventoryIcons[i].index = i;
 		extractSingleFrame(iconData, _inventoryIcons[i].iconData, i, 60, 60);
+		_inventoryIcons[i].description = _inventoryDescriptions[i];
 	}
 	delete[] iconData;
 }
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index dd7c0d0c148..9b54652f76f 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -34,6 +34,7 @@ static const int interactingAnimLength = 2;
 class ResourceManager {
 private:
 	void mergeRleBlocks(Common::SeekableReadStream *stream, uint32 offset, int numBlocks, byte *outputBuffer);
+	void loadInventoryDescriptions();
 	InventoryObject *_inventoryIcons = nullptr;
 public:
 	ResourceManager(/* args */);
@@ -43,8 +44,7 @@ public:
 	void loadCursors();
 	void loadInteractionIcons();
 	void loadAlfredAnims();
-	void loadInventoryIcons();
-	void loadInventoryDescriptions();
+	void loadInventoryItems();
 	InventoryObject getInventoryObject(byte index);
 	byte *loadExtra();
 


Commit: c3300ba0c62e4ee1fbbe85c9aeb1e44edc78b98f
    https://github.com/scummvm/scummvm/commit/c3300ba0c62e4ee1fbbe85c9aeb1e44edc78b98f
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:21+02:00

Commit Message:
PELROCK: Fixes persistent text

Changed paths:
    engines/pelrock/chrono.cpp
    engines/pelrock/chrono.h
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/chrono.cpp b/engines/pelrock/chrono.cpp
index 131fd80ce85..cb1d1b33848 100644
--- a/engines/pelrock/chrono.cpp
+++ b/engines/pelrock/chrono.cpp
@@ -37,7 +37,7 @@ ChronoManager::~ChronoManager() {
 void ChronoManager::updateChrono() {
 	uint32 currentTime = g_system->getMillis();
 
-	if (_textTtl > 0 && g_engine->alfredState.animState == ALFRED_TALKING && g_engine->alfredState.animState != ALFRED_WALKING) {
+	if (_textTtl > 0 && countTextDown) {
 		_textTtl -= (currentTime - _lastTick);
 		if (_textTtl < 0)
 			_textTtl = 0;
diff --git a/engines/pelrock/chrono.h b/engines/pelrock/chrono.h
index 03f10b010cf..37d096f4dbd 100644
--- a/engines/pelrock/chrono.h
+++ b/engines/pelrock/chrono.h
@@ -50,6 +50,7 @@ public:
 
 	bool _gameTick = false;
 	long _textTtl = 0;
+	bool countTextDown = false;
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 17e1ba67501..86efba1e75e 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -288,6 +288,7 @@ void PelrockEngine::frames() {
 		memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
 
 		if (alfredState.animState != ALFRED_WALKING && !_currentTextPages.empty()) {
+			_chronoManager->countTextDown = true;
 			if (_chronoManager->_textTtl > 0) {
 				if (alfredState.animState == ALFRED_TALKING) {
 					_textPos = Common::Point(alfredState.x, alfredState.y - kAlfredFrameHeight - 10);
@@ -307,6 +308,7 @@ void PelrockEngine::frames() {
 				alfredState.animState = ALFRED_IDLE;
 				isNPCATalking = false;
 				isNPCBTalking = false;
+				_chronoManager->countTextDown = false;
 			}
 		}
 
@@ -694,14 +696,14 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 
 	if (hotspotIndex != -1) {
 
-		_popupX = x - kBalloonWidth / 2;
+		_popupX = alfredState.x - kBalloonWidth / 2;
 		if (_popupX < 0)
 			_popupX = 0;
 		if (_popupX + kBalloonWidth > 640) {
 			_popupX -= 640 - (_popupX + kBalloonWidth);
 		}
 
-		_popupY = y - kBalloonHeight;
+		_popupY = alfredState.y - kAlfredFrameHeight - kBalloonHeight;
 		if (_popupY < 0) {
 			_popupY = 0;
 		}
@@ -986,6 +988,7 @@ void PelrockEngine::menuLoop() {
 		InventoryObject item = _res->getInventoryObject(i);
 		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 140 + i * 85, 110 - i * 5, 60, 60, 1);
 	}
+
 	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
 	_smallFont->drawString(_screen, _menuText, 0, 0, 640, 0);
 	_screen->markAllDirty();


Commit: 47ba1000c9ff704c6a7eee1e2b334de40ab97426
    https://github.com/scummvm/scummvm/commit/47ba1000c9ff704c6a7eee1e2b334de40ab97426
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:21+02:00

Commit Message:
PELROCK: Navigate through inventory icons

Changed paths:
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/util.cpp
    engines/pelrock/util.h


diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 6719c50478b..66a2578af49 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -38,8 +38,10 @@ static const uint32_t kBalloonFramesSize = 24950;
 static const uint32_t ALFRED7_ALFRED_COMB_R = 67768;
 static const uint32_t ALFRED7_ALFRED_COMB_L = 88408;
 
-static const uint32_t kAlternateSettingsMenuOffset = 910097; // Placeholder offset
-static const uint32_t kSettingsPaletteOffset = 1038141;      // 640 * 480
+
+static const uint32_t kAlternateSettingsMenuOffset = 910097;
+static const uint32_t kAlternateSettingsPaletteOffset = 1038141;      // 640 * 480
+static const uint32_t kSettingsPaletteOffset = 0x2884c2;      // 640 * 480
 
 #define DESCRIPTION_BASE_OFFSET 0x4715D
 #define NUM_DESCRIPTIONS 113
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 86efba1e75e..7d5e49704c8 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -108,6 +108,7 @@ Common::Error PelrockEngine::run() {
 
 	while (!shouldQuit()) {
 		if (stateGame == SETTINGS) {
+			changeCursor(DEFAULT);
 			menuLoop();
 		} else if (stateGame == GAME) {
 			gameLoop();
@@ -714,6 +715,32 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 	}
 }
 
+void PelrockEngine::checkMouseClickOnSettings(int x, int y) {
+
+	bool selectedItem = false;
+	for(int i = 0; i < 4; i++) {
+		if(x >= 140 + (82 * i) && x <= 140 + (82 * i) + 64 &&
+		   y >= 115 - (8 * i) && y <= 115 - (8 * i) + 64) {
+			selectedInvIndex = curInventoryPage * 4 + i;
+			_menuText = _res->getInventoryObject(selectedInvIndex).description;
+			debug("Selected inventory index: %d", selectedInvIndex);
+			selectedItem = true;
+			return;
+		}
+	}
+	if(!selectedItem) {
+		selectedInvIndex = -1;
+		_menuText = "";
+	}
+
+	if(x >= 471 && x <= 471 + 23 &&
+	   y >= 87 && y <= 87 + 33) {
+		curInventoryPage++;
+	}
+
+
+}
+
 void PelrockEngine::calculateScalingMasks() {
 
 	//    for scale_factor in range(CHAR_WIDTH):
@@ -968,13 +995,8 @@ void PelrockEngine::menuLoop() {
 	Common::Event e;
 	while (g_system->getEventManager()->pollEvent(e)) {
 		if (e.type == Common::EVENT_LBUTTONUP) {
+			checkMouseClickOnSettings(e.mouse.x, e.mouse.y);
 
-			_menuText = _res->getInventoryObject(selectedInvIndex++).description;
-
-			// _isMouseDown = false;
-			// checkMouseClick(e.mouse.x, e.mouse.y);
-			// _displayPopup = false;
-			// _longClick = false;
 		} else if (e.type == Common::EVENT_RBUTTONUP) {
 
 			g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
@@ -984,13 +1006,15 @@ void PelrockEngine::menuLoop() {
 
 	memcpy(_compositeBuffer, _res->_mainMenu, 640 * 400);
 
-	for (int i = 0; i < 4; i++) {
-		InventoryObject item = _res->getInventoryObject(i);
-		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 140 + i * 85, 110 - i * 5, 60, 60, 1);
+	for(int i = 0; i < 4; i++) {
+		int itemIndex = curInventoryPage * 4 + i;
+		InventoryObject item = _res->getInventoryObject(itemIndex);
+		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 140 + (82 * i), 115 - (8 * i), 60, 60, 1);
+		drawRect(_compositeBuffer, 140 + (82 * i) - 2, 115 - (8 * i) - 2, 64, 64, 255); // Draw border
 	}
 
 	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
-	_smallFont->drawString(_screen, _menuText, 0, 0, 640, 0);
+	_smallFont->drawString(_screen, _menuText, 230, 200, 200, 0);
 	_screen->markAllDirty();
 	_screen->update();
 }
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index bf00459347b..5aa74d3fa86 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -107,6 +107,7 @@ private:
 	void checkMouseHover();
 	void checkMouseClick(int x, int y);
 	void checkLongMouseClick(int x, int y);
+	void checkMouseClickOnSettings(int x, int y);
 
 	void calculateScalingMasks();
 	ScaleCalculation calculateScaling(int yPos, ScalingParams scalingParams);
@@ -158,6 +159,7 @@ private:
 
 	//Temporary
 	int selectedInvIndex = 0;
+	int curInventoryPage = 0;
 	Common::String _menuText;
 
 	// JAVA
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index d33576d2deb..63834fc61bd 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -296,7 +296,7 @@ void ResourceManager::mergeRleBlocks(Common::SeekableReadStream *stream, uint32
 
 void ResourceManager::loadSettingsMenu() {
 
-	bool alternateMenu = true;
+	bool alternateMenu = false;
 	Common::File alfred7;
 	if (!alfred7.open(Common::Path("ALFRED.7"))) {
 		error("Could not open ALFRED.7");
@@ -355,7 +355,7 @@ void ResourceManager::loadSettingsMenu() {
 
 		_mainMenu = new byte[640 * 400];
 
-		alfred7.seek(kSettingsPaletteOffset, SEEK_SET);
+		alfred7.seek(kAlternateSettingsPaletteOffset, SEEK_SET);
 		alfred7.read(_mainMenuPalette, 768);
 		for (int i = 0; i < 256; i++) {
 			_mainMenuPalette[i * 3] = _mainMenuPalette[i * 3] << 2;
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index fa00202554b..3f8fc4436a9 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -75,14 +75,12 @@ void RoomManager::getBackground(Common::File *roomFile, int roomOffset, byte *ba
 		roomFile->seek(pair_offset, SEEK_SET);
 		uint32_t offset = roomFile->readUint32LE();
 		uint32_t size = roomFile->readUint32LE();
-		debug("Background pair %d: offset=%d size=%d", pair_idx, offset, size);
 		if (offset > 0 && size > 0 && offset < roomFile->size()) {
 			byte *data = new byte[size];
 			roomFile->seek(offset, SEEK_SET);
 			roomFile->read(data, size);
 			uint8_t *block_data = NULL;
 			size_t block_size = rleDecompress(data, size, 0, 640 * 400, &block_data);
-			debug(" Decompressed block size: %d, combined size: %d", block_size, combined_size + block_size);
 			if (block_size + combined_size > 640 * 400) {
 				debug(" Warning: decompressed background size exceeds buffer size!");
 				block_size = 640 * 400 - combined_size;
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index f204188003b..bc302a8ae7d 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -41,6 +41,22 @@ void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color
 	surface->drawLine(x + w, y, x + w, y + h, color);
 }
 
+void drawRect(byte *screenBuffer, int x, int y, int w, int h, byte color) {
+	Graphics::Surface *surface = new Graphics::Surface();
+	surface->create(w, h, Graphics::PixelFormat::createFormatCLUT8());
+	drawRect(surface, 0, 0, w, h, color);
+
+	for (int py = 0; py < h; py++) {
+		for (int px = 0; px < w; px++) {
+			int destIdx = (y + py) * 640 + (x + px);
+			int srcIdx = py * w + px;
+			int color  = *((byte *)surface->getBasePtr(px, py));
+			if(color != 0)
+			screenBuffer[destIdx] = color;
+		}
+	}
+}
+
 Common::String printMovementFlags(uint8_t flags) {
 	Common::String result;
 	if (flags & MOVE_HORIZ) {
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index 593aac4e2df..88fc9c67093 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -38,6 +38,7 @@ void blitSurfaceToBuffer(Graphics::Surface *surface, byte *buffer, int bufferWid
 void extractSingleFrame(byte *source, byte *dest, int frameIndex, int frameWidth, int frameHeight);
 void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byte color);
 void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color);
+void drawRect(byte *screenBuffer, int x, int y, int w, int h, byte color);
 Common::String printMovementFlags(uint8_t flags);
 Common::String joinStrings(const Common::Array<Common::String> &strings, const Common::String &separator);
 void drawPos(Graphics::ManagedSurface *surface, int x, int y, byte color);


Commit: e3517058e65add29333806130dbcd11db8dc925c
    https://github.com/scummvm/scummvm/commit/e3517058e65add29333806130dbcd11db8dc925c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:21+02:00

Commit Message:
PELROCK: Fixes decoding issues

Changed paths:
    engines/pelrock/fonts/large_font.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/util.cpp
    engines/pelrock/util.h


diff --git a/engines/pelrock/fonts/large_font.cpp b/engines/pelrock/fonts/large_font.cpp
index fa431089d42..f6aa4f12e7f 100644
--- a/engines/pelrock/fonts/large_font.cpp
+++ b/engines/pelrock/fonts/large_font.cpp
@@ -37,7 +37,7 @@ bool LargeFont::load(const Common::String &filename) {
 	}
 
 	file.seek(0x7DC8, SEEK_SET);
-	const int numChars = 96;
+	const int numChars = 100;
 	const int charWidth = 12;
 	const int charHeight = 24;
 	const int pad = 1;
@@ -115,7 +115,7 @@ int LargeFont::getCharWidth(uint32 chr) const {
 
 void LargeFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
 	chr -= 32; // Adjust for font starting at ASCII 32
-	if (!_fontData || chr >= 96 || chr < 0) {
+	if (!_fontData || chr >= 100 || chr < 0) {
 		return;
 	}
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 7d5e49704c8..54c925df455 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -587,7 +587,6 @@ void PelrockEngine::drawAlfred(byte *buf) {
 					}
 					int srcIndex = srcY * kAlfredFrameWidth + srcX;
 					int outIndex = outY * finalWidth + outX;
-					debug("srcIndex = %d, outIndex = %d, original size = %d, outsize = %d", srcIndex, outIndex, kAlfredFrameWidth * kAlfredFrameHeight, finalWidth * finalHeight);
 					if (outIndex >= finalWidth * finalHeight || srcIndex >= kAlfredFrameWidth * kAlfredFrameHeight) {
 						debug("Index out of bounds!");
 					} else
@@ -701,7 +700,7 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 		if (_popupX < 0)
 			_popupX = 0;
 		if (_popupX + kBalloonWidth > 640) {
-			_popupX -= 640 - (_popupX + kBalloonWidth);
+			_popupX = 640 - kBalloonWidth;
 		}
 
 		_popupY = alfredState.y - kAlfredFrameHeight - kBalloonHeight;
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 63834fc61bd..c5da7da9312 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -262,8 +262,13 @@ void ResourceManager::loadInventoryDescriptions() {
 			pos += 2;
 			continue;
 		}
+		if(descBuffer[pos] == 0xC8) {
+			desc.append(1, '\n');
+			pos++;
+			continue;
+		}
 
-		desc.append(1, descBuffer[pos]);
+		desc.append(1, decodeChar(descBuffer[pos]));
 		if (pos + 1 == kInventoryDescriptionsSize) {
 			_inventoryDescriptions.push_back(desc);
 		}
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 3f8fc4436a9..df4910da95c 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -376,8 +376,8 @@ Common::Array<Description> RoomManager::loadRoomDescriptions(Common::File *roomF
 			while (pos < (pair12_size) && data[pos] != 0xFD && pos < (pair12_size)) {
 
 				if (data[pos] != 0x00) {
-					// debug("Description char byte: 0x%02X", data[pos]);
-					description.text.append(1, decodeCPByte((byte)data[pos]));
+					// debug("Adding char 0x%02X to description, decoded as %lc", data[pos], decodeChar((byte)data[pos]));
+					description.text.append(1, decodeChar((byte)data[pos]));
 				}
 				if (data[pos] == 0xF8) {
 					description.actionTrigger = data[pos + 1] | data[pos + 2] << 8;
@@ -400,35 +400,6 @@ Common::Array<Description> RoomManager::loadRoomDescriptions(Common::File *roomF
 	return descriptions;
 }
 
-char32_t decodeByte(byte b) {
-	if (b == 0x80) {
-		return '\xA4';
-	} else if (b == 0x81) {
-		return '\xA1';
-	} else if (b == 0x82) {
-		return '\xAD';
-	} else if (b == 0x83) {
-		return '\xA8';
-	} else if (b == 0x84) {
-		return '\xA3';
-	} else if (b == 0x7B) {
-		return '\xA0';
-	} else if (b == 0x7C) {
-		return '\x82';
-	} else if (b == 0x7D) {
-		return '\xA1';
-	} else if (b == 0x7E) {
-		return '\xA2';
-	} else if (b == 0x7F) {
-		return '\xA3';
-	} else if (b >= 0x20 && b <= 0x7A) {
-		return (char)b;
-	} else {
-		// return string in format [XX]
-		return '.';
-	}
-}
-
 void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 
 	int headerIndex = roomNumber;
@@ -631,7 +602,7 @@ Common::Array<ConversationElement> RoomManager::parseConversationElements(const
 					   convData[pos] != 0xFC && convData[pos] != 0xF4 && convData[pos] != 0xF7 &&
 					   convData[pos] != 0xF5 && convData[pos] != 0xFE && convData[pos] != 0xEB &&
 					   convData[pos] != 0xF0) {
-					char32_t ch = decodeByte(convData[pos]);
+					char32_t ch = decodeChar(convData[pos]);
 					if (ch != '.') {
 						text += ch;
 					}
@@ -676,7 +647,7 @@ Common::Array<ConversationElement> RoomManager::parseConversationElements(const
 				   convData[pos] != 0xFC && convData[pos] != 0xF4 && convData[pos] != 0xF7 &&
 				   convData[pos] != 0xF5 && convData[pos] != 0xFE && convData[pos] != 0xEB &&
 				   convData[pos] != 0xF0) {
-				char32_t ch = decodeByte(convData[pos]);
+				char32_t ch = decodeChar(convData[pos]);
 				if (ch != '.') {
 					text += ch;
 				}
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index bc302a8ae7d..ac0efab9cf0 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -268,7 +268,20 @@ void drawPos(Graphics::ManagedSurface *surface, int x, int y, byte color) {
 	}
 }
 
-char32_t decodeCPByte(byte b) {
-	return cp437_to_unicode[b];
+byte decodeChar(byte b) {
+	switch (b) {
+		case 0x82: return special_chars[1];
+		case 0x83: return special_chars[0];
+
+		case 0x80: return special_chars[3]; // n tilde
+		case 0x7F: return special_chars[4];
+		case 0x7E: return special_chars[5];
+		case 0x7D: return special_chars[6];
+		case 0x7C: return special_chars[7];
+		case 0x7B: return special_chars[8];
+		default:
+    		return b;
+	}
 }
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index 88fc9c67093..82e78cc368c 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -42,43 +42,21 @@ void drawRect(byte *screenBuffer, int x, int y, int w, int h, byte color);
 Common::String printMovementFlags(uint8_t flags);
 Common::String joinStrings(const Common::Array<Common::String> &strings, const Common::String &separator);
 void drawPos(Graphics::ManagedSurface *surface, int x, int y, byte color);
-static const char32_t cp437_to_unicode[256] = {
-    // 0x00 - 0x7F: ASCII (unchanged)
-    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
-    0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
-    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
-    0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
-    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
-    0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
-    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
-    0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
-    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
-    0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
-    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
-    0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
-    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
-    0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
-    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
-    0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
-    // 0x80 - 0xFF: CP437 extended characters
-    0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
-    0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
-    0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
-    0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
-    0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
-    0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
-    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
-    0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
-    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
-    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
-    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
-    0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
-    0x03B1, 0x03B2, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
-    0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
-    0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
-    0x00B0, 0x00B7, 0x2022, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+byte decodeChar(byte b);
+
+
+static const int special_chars[] = {
+    131, // inverted ?
+    130, // inverted !
+    129, // capital N tilde
+    128, // small n tilde
+    127, // small u tilde
+    126, // small o tilde
+    125, // small i tilde
+    124, // small e tilde
+    123, // small a tilde
 };
 
-char32_t decodeCPByte(byte b);
+
 } // End of namespace Pelrock
 #endif // PELROCK_UTIL_H


Commit: 9b14c946816ddddc7fdf5f0f1f4b66102e5b18b2
    https://github.com/scummvm/scummvm/commit/9b14c946816ddddc7fdf5f0f1f4b66102e5b18b2
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:22+02:00

Commit Message:
PELROCK: Room 2 palette anim

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/sound.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 54c925df455..b53375245d9 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -337,6 +337,53 @@ void PelrockEngine::frames() {
 		}
 		_smallFont->drawString(_screen, Common::String::format("Room number: %d", _room->_currentRoomNumber), 0, 4, 640, 13);
 		_smallFont->drawString(_screen, Common::String::format("Alfred pos: %d, %d (%d)", alfredState.x, alfredState.y, alfredState.y - kAlfredFrameHeight), 0, 18, 640, 13);
+
+		if (_paletteAnim != nullptr) {
+
+			// if (_paletteAnim->curFrameCount >= _paletteAnim->speed) {
+			_paletteAnim->curFrameCount = 0;
+			if (_paletteAnim->currentR >= _paletteAnim->maxR &&
+				_paletteAnim->currentG >= _paletteAnim->maxG &&
+				_paletteAnim->currentB >= _paletteAnim->maxB) {
+				_paletteAnim->downDirection = 0;
+			} else if (_paletteAnim->currentR <= _paletteAnim->minR &&
+					   _paletteAnim->currentG <= _paletteAnim->minG &&
+					   _paletteAnim->currentB <= _paletteAnim->minB) {
+				_paletteAnim->downDirection = 1;
+			}
+
+			if (_paletteAnim->downDirection) {
+				if (_paletteAnim->currentR < _paletteAnim->maxR) {
+					_paletteAnim->currentR += _paletteAnim->speed;
+				}
+				if (_paletteAnim->currentG < _paletteAnim->maxG) {
+					_paletteAnim->currentG += _paletteAnim->speed;
+				}
+				if (_paletteAnim->currentB < _paletteAnim->maxB) {
+					_paletteAnim->currentB += _paletteAnim->speed;
+				}
+			} else {
+				if (_paletteAnim->currentR > _paletteAnim->minR) {
+					_paletteAnim->currentR -= _paletteAnim->speed;
+				}
+				if (_paletteAnim->currentG > _paletteAnim->minG) {
+					_paletteAnim->currentG -= _paletteAnim->speed;
+				}
+				if (_paletteAnim->currentB > _paletteAnim->minB) {
+					_paletteAnim->currentB -= _paletteAnim->speed;
+				}
+			}
+
+			_room->_roomPalette[_paletteAnim->paletteIndex * 3] = _paletteAnim->currentR;
+			_room->_roomPalette[_paletteAnim->paletteIndex * 3 + 1] = _paletteAnim->currentG;
+			_room->_roomPalette[_paletteAnim->paletteIndex * 3 + 2] = _paletteAnim->currentB;
+			g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
+
+			// } else {
+			// 	_paletteAnim->curFrameCount++;
+			// }
+		}
+
 		_screen->markAllDirty();
 
 		// _screen->update();
@@ -717,9 +764,9 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 void PelrockEngine::checkMouseClickOnSettings(int x, int y) {
 
 	bool selectedItem = false;
-	for(int i = 0; i < 4; i++) {
-		if(x >= 140 + (82 * i) && x <= 140 + (82 * i) + 64 &&
-		   y >= 115 - (8 * i) && y <= 115 - (8 * i) + 64) {
+	for (int i = 0; i < 4; i++) {
+		if (x >= 140 + (82 * i) && x <= 140 + (82 * i) + 64 &&
+			y >= 115 - (8 * i) && y <= 115 - (8 * i) + 64) {
 			selectedInvIndex = curInventoryPage * 4 + i;
 			_menuText = _res->getInventoryObject(selectedInvIndex).description;
 			debug("Selected inventory index: %d", selectedInvIndex);
@@ -727,17 +774,15 @@ void PelrockEngine::checkMouseClickOnSettings(int x, int y) {
 			return;
 		}
 	}
-	if(!selectedItem) {
+	if (!selectedItem) {
 		selectedInvIndex = -1;
 		_menuText = "";
 	}
 
-	if(x >= 471 && x <= 471 + 23 &&
-	   y >= 87 && y <= 87 + 33) {
+	if (x >= 471 && x <= 471 + 23 &&
+		y >= 87 && y <= 87 + 33) {
 		curInventoryPage++;
 	}
-
-
 }
 
 void PelrockEngine::calculateScalingMasks() {
@@ -1005,7 +1050,7 @@ void PelrockEngine::menuLoop() {
 
 	memcpy(_compositeBuffer, _res->_mainMenu, 640 * 400);
 
-	for(int i = 0; i < 4; i++) {
+	for (int i = 0; i < 4; i++) {
 		int itemIndex = curInventoryPage * 4 + i;
 		InventoryObject item = _res->getInventoryObject(itemIndex);
 		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 140 + (82 * i), 115 - (8 * i), 60, 60, 1);
@@ -1668,6 +1713,15 @@ void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 	// }
 
 	_room->_currentRoomNumber = number;
+
+	if (number == 2 && _paletteAnim == nullptr) { // Pelrock Mansion
+		_paletteAnim = _room->paletteAnimRoom2();
+	} else {
+		if (_paletteAnim != nullptr)
+			free(_paletteAnim);
+		_paletteAnim = nullptr;
+	}
+
 	_screen->markAllDirty();
 	roomFile.close();
 	delete[] background;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 5aa74d3fa86..13dde45f0cd 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -162,11 +162,15 @@ private:
 	int curInventoryPage = 0;
 	Common::String _menuText;
 
+	PaletteAnimFade *_paletteAnim = nullptr;
+
 	// JAVA
 	bool shouldPlayIntro = false;
 	GameState stateGame = INTRO;
 	bool gameInitialized = false;
 	bool screenReady = false;
+
+
 	// int prevDirX = 0;
 	// int prevDirY = 0;
 	// Common::String objectToShow = "";
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index df4910da95c..96d9bad83ee 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -93,6 +93,40 @@ void RoomManager::getBackground(Common::File *roomFile, int roomOffset, byte *ba
 	}
 }
 
+void RoomManager::paletteAnimRoom0() {
+
+}
+
+PaletteAnimFade *RoomManager::paletteAnimRoom2() {
+	Common::File exeFile;
+
+	if (!exeFile.open("JUEGO.EXE")) {
+		debug("Could not open JUEGO.EXE for palette animation!");
+		return nullptr;
+	}
+	exeFile.seek(0x0004B860, SEEK_SET);
+	PaletteAnimFade *anim = new PaletteAnimFade();
+	anim->paletteIndex = exeFile.readByte();
+	anim->paletteMode = exeFile.readByte();
+	anim->currentR = exeFile.readByte() << 2;
+	anim->currentG = exeFile.readByte() << 2;
+	anim->currentB = exeFile.readByte() << 2;
+	anim->minR = exeFile.readByte() << 2;
+	anim->minG = exeFile.readByte() << 2;
+	anim->minB = exeFile.readByte() << 2;
+	anim->maxR = exeFile.readByte() << 2;
+	anim->maxG = exeFile.readByte() << 2;
+	anim->maxB = exeFile.readByte() << 2;
+	byte flags = exeFile.readByte();
+	anim->curFrameCount = 0;
+
+
+	// lower 5 bits are speed, bit 6 is direction
+	anim->speed = flags & 0x3F;
+	anim->downDirection = (flags & 0x40);
+	return anim;
+}
+
 Common::Array<Exit> RoomManager::loadExits(Common::File *roomFile, int roomOffset) {
 	Common::Array<Exit> exits;
 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 2d081c21e2e..573d18292f1 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -39,6 +39,9 @@ public:
 	void loadRoomTalkingAnimations(int roomNumber);
 	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
+	void paletteAnimRoom0();
+	PaletteAnimFade *paletteAnimRoom2();
+
 	Common::String getRoomName(int roomNumber) {
 		if (roomNumber >= 0 && roomNumber < _roomNames.size()) {
 			return _roomNames[roomNumber];
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 22e41377808..bbea25006be 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -100,10 +100,10 @@ void SoundManager::playSound(SonidoFile sound, int volume) {
 		return;
 	}
 
-	if (stream) {
-		int channel = findFreeChannel();
-		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, volume, 0, DisposeAfterUse::YES);
-	}
+	// if (stream) {
+	// 	int channel = findFreeChannel();
+	// 	_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, volume, 0, DisposeAfterUse::YES);
+	// }
 	// TODO: Play sound file
 }
 
@@ -193,34 +193,34 @@ void SoundManager::stopMusic() {
 }
 
 void SoundManager::playMusicTrack(int trackNumber) {
-	if (_currentMusicTrack == trackNumber && _isMusicPlaying) {
-		// Already playing this track
-		return;
-	}
-	_currentMusicTrack = trackNumber;
-	stopMusic();
-	// Open the file
-	_musicFile = new Common::File();
-	Common::String filename = Common::String::format("music/track%d.mp3", trackNumber);
-
-	if (!_musicFile->open(Common::Path(filename))) {
-		delete _musicFile;
-		_musicFile = nullptr;
-		return;
-	}
-#ifdef USE_MAD
-	Audio::SeekableAudioStream *stream = Audio::makeMP3Stream(_musicFile, DisposeAfterUse::YES);
-	if (!stream) {
-		_musicFile->close();
-		delete _musicFile;
-		_musicFile = nullptr;
-		return;
-	}
-	Audio::AudioStream *loopStream = Audio::makeLoopingAudioStream(stream, 0);
-	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, loopStream, -1, _currentVolume);
-	_isMusicPlaying = true;
-	_musicFile = nullptr;
-#endif
+// 	if (_currentMusicTrack == trackNumber && _isMusicPlaying) {
+// 		// Already playing this track
+// 		return;
+// 	}
+// 	_currentMusicTrack = trackNumber;
+// 	stopMusic();
+// 	// Open the file
+// 	_musicFile = new Common::File();
+// 	Common::String filename = Common::String::format("music/track%d.mp3", trackNumber);
+
+// 	if (!_musicFile->open(Common::Path(filename))) {
+// 		delete _musicFile;
+// 		_musicFile = nullptr;
+// 		return;
+// 	}
+// #ifdef USE_MAD
+// 	Audio::SeekableAudioStream *stream = Audio::makeMP3Stream(_musicFile, DisposeAfterUse::YES);
+// 	if (!stream) {
+// 		_musicFile->close();
+// 		delete _musicFile;
+// 		_musicFile = nullptr;
+// 		return;
+// 	}
+// 	Audio::AudioStream *loopStream = Audio::makeLoopingAudioStream(stream, 0);
+// 	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, loopStream, -1, _currentVolume);
+// 	_isMusicPlaying = true;
+// 	_musicFile = nullptr;
+// #endif
 }
 
 void SoundManager::loadSoundIndex() {
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 31969c3f898..c5ae3d7911c 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -313,6 +313,23 @@ struct InventoryObject {
 	byte iconData[60 * 60];
 };
 
+struct PaletteAnimFade {
+	byte paletteIndex;
+	byte paletteMode;
+	byte currentR;
+	byte currentG;
+	byte currentB;
+	byte minR;
+	byte minG;
+	byte minB;
+	byte maxR;
+	byte maxG;
+	byte maxB;
+	byte speed;
+	bool downDirection;
+	byte curFrameCount = 0;
+};
+
 } // End of namespace Pelrock
 
 #endif


Commit: 7dd082958e8b9e92c460d5594c4fdb432e2bd5d9
    https://github.com/scummvm/scummvm/commit/7dd082958e8b9e92c460d5594c4fdb432e2bd5d9
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:22+02:00

Commit Message:
PELROCK: Palette anim from room 0

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index b53375245d9..9f564bfa9ac 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -338,45 +338,45 @@ void PelrockEngine::frames() {
 		_smallFont->drawString(_screen, Common::String::format("Room number: %d", _room->_currentRoomNumber), 0, 4, 640, 13);
 		_smallFont->drawString(_screen, Common::String::format("Alfred pos: %d, %d (%d)", alfredState.x, alfredState.y, alfredState.y - kAlfredFrameHeight), 0, 18, 640, 13);
 
-		if (_paletteAnim != nullptr) {
+		if (_paletteFadeAnim != nullptr) {
 
 			// if (_paletteAnim->curFrameCount >= _paletteAnim->speed) {
-			_paletteAnim->curFrameCount = 0;
-			if (_paletteAnim->currentR >= _paletteAnim->maxR &&
-				_paletteAnim->currentG >= _paletteAnim->maxG &&
-				_paletteAnim->currentB >= _paletteAnim->maxB) {
-				_paletteAnim->downDirection = 0;
-			} else if (_paletteAnim->currentR <= _paletteAnim->minR &&
-					   _paletteAnim->currentG <= _paletteAnim->minG &&
-					   _paletteAnim->currentB <= _paletteAnim->minB) {
-				_paletteAnim->downDirection = 1;
+			_paletteFadeAnim->curFrameCount = 0;
+			if (_paletteFadeAnim->currentR >= _paletteFadeAnim->maxR &&
+				_paletteFadeAnim->currentG >= _paletteFadeAnim->maxG &&
+				_paletteFadeAnim->currentB >= _paletteFadeAnim->maxB) {
+				_paletteFadeAnim->downDirection = 0;
+			} else if (_paletteFadeAnim->currentR <= _paletteFadeAnim->minR &&
+					   _paletteFadeAnim->currentG <= _paletteFadeAnim->minG &&
+					   _paletteFadeAnim->currentB <= _paletteFadeAnim->minB) {
+				_paletteFadeAnim->downDirection = 1;
 			}
 
-			if (_paletteAnim->downDirection) {
-				if (_paletteAnim->currentR < _paletteAnim->maxR) {
-					_paletteAnim->currentR += _paletteAnim->speed;
+			if (_paletteFadeAnim->downDirection) {
+				if (_paletteFadeAnim->currentR < _paletteFadeAnim->maxR) {
+					_paletteFadeAnim->currentR += _paletteFadeAnim->speed;
 				}
-				if (_paletteAnim->currentG < _paletteAnim->maxG) {
-					_paletteAnim->currentG += _paletteAnim->speed;
+				if (_paletteFadeAnim->currentG < _paletteFadeAnim->maxG) {
+					_paletteFadeAnim->currentG += _paletteFadeAnim->speed;
 				}
-				if (_paletteAnim->currentB < _paletteAnim->maxB) {
-					_paletteAnim->currentB += _paletteAnim->speed;
+				if (_paletteFadeAnim->currentB < _paletteFadeAnim->maxB) {
+					_paletteFadeAnim->currentB += _paletteFadeAnim->speed;
 				}
 			} else {
-				if (_paletteAnim->currentR > _paletteAnim->minR) {
-					_paletteAnim->currentR -= _paletteAnim->speed;
+				if (_paletteFadeAnim->currentR > _paletteFadeAnim->minR) {
+					_paletteFadeAnim->currentR -= _paletteFadeAnim->speed;
 				}
-				if (_paletteAnim->currentG > _paletteAnim->minG) {
-					_paletteAnim->currentG -= _paletteAnim->speed;
+				if (_paletteFadeAnim->currentG > _paletteFadeAnim->minG) {
+					_paletteFadeAnim->currentG -= _paletteFadeAnim->speed;
 				}
-				if (_paletteAnim->currentB > _paletteAnim->minB) {
-					_paletteAnim->currentB -= _paletteAnim->speed;
+				if (_paletteFadeAnim->currentB > _paletteFadeAnim->minB) {
+					_paletteFadeAnim->currentB -= _paletteFadeAnim->speed;
 				}
 			}
 
-			_room->_roomPalette[_paletteAnim->paletteIndex * 3] = _paletteAnim->currentR;
-			_room->_roomPalette[_paletteAnim->paletteIndex * 3 + 1] = _paletteAnim->currentG;
-			_room->_roomPalette[_paletteAnim->paletteIndex * 3 + 2] = _paletteAnim->currentB;
+			_room->_roomPalette[_paletteFadeAnim->paletteIndex * 3] = _paletteFadeAnim->currentR;
+			_room->_roomPalette[_paletteFadeAnim->paletteIndex * 3 + 1] = _paletteFadeAnim->currentG;
+			_room->_roomPalette[_paletteFadeAnim->paletteIndex * 3 + 2] = _paletteFadeAnim->currentB;
 			g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
 
 			// } else {
@@ -384,6 +384,29 @@ void PelrockEngine::frames() {
 			// }
 		}
 
+		if (_paletteRotateAnim != nullptr) {
+			if (_paletteRotateAnim->curFrameCount >= _paletteRotateAnim->delay) {
+				_paletteRotateAnim->curFrameCount = 0;
+				int colors = _paletteRotateAnim->paletteMode;
+				byte *paletteValues = new byte[colors * 3];
+				for (int i = 0; i < colors; i++) {
+					paletteValues[i * 3] = _room->_roomPalette[(_paletteRotateAnim->paletteStartIndex + i) * 3];
+					paletteValues[i * 3 + 1] = _room->_roomPalette[(_paletteRotateAnim->paletteStartIndex + i) * 3 + 1];
+					paletteValues[i * 3 + 2] = _room->_roomPalette[(_paletteRotateAnim->paletteStartIndex + i) * 3 + 2];
+				}
+				for (int i = 0; i < colors; i++) {
+					int srcIndex = (i + 1) % colors;
+					_room->_roomPalette[(_paletteRotateAnim->paletteStartIndex + i) * 3] = paletteValues[srcIndex * 3];
+					_room->_roomPalette[(_paletteRotateAnim->paletteStartIndex + i) * 3 + 1] = paletteValues[srcIndex * 3 + 1];
+					_room->_roomPalette[(_paletteRotateAnim->paletteStartIndex + i) * 3 + 2] = paletteValues[srcIndex * 3 + 2];
+				}
+
+				g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
+
+			} else {
+				_paletteRotateAnim->curFrameCount++;
+			}
+		}
 		_screen->markAllDirty();
 
 		// _screen->update();
@@ -1714,12 +1737,17 @@ void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 
 	_room->_currentRoomNumber = number;
 
-	if (number == 2 && _paletteAnim == nullptr) { // Pelrock Mansion
-		_paletteAnim = _room->paletteAnimRoom2();
+	if (number == 2 && _paletteFadeAnim == nullptr) { // Pelrock Mansion
+		_paletteFadeAnim = _room->paletteAnimRoom2();
+	} else if (number == 0 && _paletteRotateAnim == nullptr) {
+		_paletteRotateAnim = _room->paletteAnimRoom0();
 	} else {
-		if (_paletteAnim != nullptr)
-			free(_paletteAnim);
-		_paletteAnim = nullptr;
+		if (_paletteFadeAnim != nullptr)
+			free(_paletteFadeAnim);
+		_paletteFadeAnim = nullptr;
+		if (_paletteRotateAnim != nullptr)
+			free(_paletteRotateAnim);
+		_paletteRotateAnim = nullptr;
 	}
 
 	_screen->markAllDirty();
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 13dde45f0cd..d3ecbbd95e8 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -162,8 +162,8 @@ private:
 	int curInventoryPage = 0;
 	Common::String _menuText;
 
-	PaletteAnimFade *_paletteAnim = nullptr;
-
+	PaletteAnimFade *_paletteFadeAnim = nullptr;
+	PaletteAnimRotate *_paletteRotateAnim = nullptr;
 	// JAVA
 	bool shouldPlayIntro = false;
 	GameState stateGame = INTRO;
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 96d9bad83ee..75a2014e2fd 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -93,8 +93,23 @@ void RoomManager::getBackground(Common::File *roomFile, int roomOffset, byte *ba
 	}
 }
 
-void RoomManager::paletteAnimRoom0() {
+PaletteAnimRotate *RoomManager::paletteAnimRoom0() {
+	Common::File exeFile;
 
+	if (!exeFile.open("JUEGO.EXE")) {
+		debug("Could not open JUEGO.EXE for palette animation!");
+		return nullptr;
+	}
+	exeFile.seek(0x0004B88C, SEEK_SET);
+	PaletteAnimRotate *anim = new PaletteAnimRotate();
+	anim->paletteStartIndex = exeFile.readByte();
+	anim->paletteMode = exeFile.readByte();
+	anim->unknown = exeFile.readByte();
+	anim->delay = exeFile.readByte();
+	exeFile.read(anim->unknownBytes, 7);
+	anim->flags = exeFile.readByte();
+	exeFile.close();
+	return anim;
 }
 
 PaletteAnimFade *RoomManager::paletteAnimRoom2() {
@@ -124,6 +139,7 @@ PaletteAnimFade *RoomManager::paletteAnimRoom2() {
 	// lower 5 bits are speed, bit 6 is direction
 	anim->speed = flags & 0x3F;
 	anim->downDirection = (flags & 0x40);
+	exeFile.close();
 	return anim;
 }
 
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 573d18292f1..688038f8247 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -39,7 +39,7 @@ public:
 	void loadRoomTalkingAnimations(int roomNumber);
 	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
-	void paletteAnimRoom0();
+	PaletteAnimRotate *paletteAnimRoom0();
 	PaletteAnimFade *paletteAnimRoom2();
 
 	Common::String getRoomName(int roomNumber) {
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index c5ae3d7911c..d4d89c46bac 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -330,6 +330,17 @@ struct PaletteAnimFade {
 	byte curFrameCount = 0;
 };
 
+
+struct PaletteAnimRotate {
+	byte paletteStartIndex;
+	byte paletteMode;
+	byte unknown;
+	byte delay;
+	byte unknownBytes[7];
+	byte flags;
+	byte curFrameCount = 0;
+};
+
 } // End of namespace Pelrock
 
 #endif


Commit: 9fc45ddbca79db240dd203e6a194fef6c3f5a96b
    https://github.com/scummvm/scummvm/commit/9fc45ddbca79db240dd203e6a194fef6c3f5a96b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:22+02:00

Commit Message:
PELROCK: Consolidate palette animations

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 9f564bfa9ac..2a0f2fc8fe3 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -338,78 +338,80 @@ void PelrockEngine::frames() {
 		_smallFont->drawString(_screen, Common::String::format("Room number: %d", _room->_currentRoomNumber), 0, 4, 640, 13);
 		_smallFont->drawString(_screen, Common::String::format("Alfred pos: %d, %d (%d)", alfredState.x, alfredState.y, alfredState.y - kAlfredFrameHeight), 0, 18, 640, 13);
 
-		if (_paletteFadeAnim != nullptr) {
-
-			// if (_paletteAnim->curFrameCount >= _paletteAnim->speed) {
-			_paletteFadeAnim->curFrameCount = 0;
-			if (_paletteFadeAnim->currentR >= _paletteFadeAnim->maxR &&
-				_paletteFadeAnim->currentG >= _paletteFadeAnim->maxG &&
-				_paletteFadeAnim->currentB >= _paletteFadeAnim->maxB) {
-				_paletteFadeAnim->downDirection = 0;
-			} else if (_paletteFadeAnim->currentR <= _paletteFadeAnim->minR &&
-					   _paletteFadeAnim->currentG <= _paletteFadeAnim->minG &&
-					   _paletteFadeAnim->currentB <= _paletteFadeAnim->minB) {
-				_paletteFadeAnim->downDirection = 1;
-			}
-
-			if (_paletteFadeAnim->downDirection) {
-				if (_paletteFadeAnim->currentR < _paletteFadeAnim->maxR) {
-					_paletteFadeAnim->currentR += _paletteFadeAnim->speed;
-				}
-				if (_paletteFadeAnim->currentG < _paletteFadeAnim->maxG) {
-					_paletteFadeAnim->currentG += _paletteFadeAnim->speed;
-				}
-				if (_paletteFadeAnim->currentB < _paletteFadeAnim->maxB) {
-					_paletteFadeAnim->currentB += _paletteFadeAnim->speed;
-				}
+		if (_room->_currentPaletteAnim != nullptr) {
+			if (_room->_currentPaletteAnim->paletteMode == 1) {
+				animateFadePalette(_room->_currentPaletteAnim);
 			} else {
-				if (_paletteFadeAnim->currentR > _paletteFadeAnim->minR) {
-					_paletteFadeAnim->currentR -= _paletteFadeAnim->speed;
-				}
-				if (_paletteFadeAnim->currentG > _paletteFadeAnim->minG) {
-					_paletteFadeAnim->currentG -= _paletteFadeAnim->speed;
-				}
-				if (_paletteFadeAnim->currentB > _paletteFadeAnim->minB) {
-					_paletteFadeAnim->currentB -= _paletteFadeAnim->speed;
-				}
+				animateRotatePalette(_room->_currentPaletteAnim);
 			}
+		}
 
-			_room->_roomPalette[_paletteFadeAnim->paletteIndex * 3] = _paletteFadeAnim->currentR;
-			_room->_roomPalette[_paletteFadeAnim->paletteIndex * 3 + 1] = _paletteFadeAnim->currentG;
-			_room->_roomPalette[_paletteFadeAnim->paletteIndex * 3 + 2] = _paletteFadeAnim->currentB;
-			g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
+		_screen->markAllDirty();
+	}
+}
 
-			// } else {
-			// 	_paletteAnim->curFrameCount++;
-			// }
-		}
+void PelrockEngine::animateFadePalette(PaletteAnim *anim) {
 
-		if (_paletteRotateAnim != nullptr) {
-			if (_paletteRotateAnim->curFrameCount >= _paletteRotateAnim->delay) {
-				_paletteRotateAnim->curFrameCount = 0;
-				int colors = _paletteRotateAnim->paletteMode;
-				byte *paletteValues = new byte[colors * 3];
-				for (int i = 0; i < colors; i++) {
-					paletteValues[i * 3] = _room->_roomPalette[(_paletteRotateAnim->paletteStartIndex + i) * 3];
-					paletteValues[i * 3 + 1] = _room->_roomPalette[(_paletteRotateAnim->paletteStartIndex + i) * 3 + 1];
-					paletteValues[i * 3 + 2] = _room->_roomPalette[(_paletteRotateAnim->paletteStartIndex + i) * 3 + 2];
-				}
-				for (int i = 0; i < colors; i++) {
-					int srcIndex = (i + 1) % colors;
-					_room->_roomPalette[(_paletteRotateAnim->paletteStartIndex + i) * 3] = paletteValues[srcIndex * 3];
-					_room->_roomPalette[(_paletteRotateAnim->paletteStartIndex + i) * 3 + 1] = paletteValues[srcIndex * 3 + 1];
-					_room->_roomPalette[(_paletteRotateAnim->paletteStartIndex + i) * 3 + 2] = paletteValues[srcIndex * 3 + 2];
-				}
+	// if (_paletteAnim->curFrameCount >= _paletteAnim->speed) {
+	if (anim->data[0] >= anim->data[6] &&
+		anim->data[1] >= anim->data[7] &&
+		anim->data[2] >= anim->data[8]) {
+		anim->data[10] = 0;
+	} else if (anim->data[0] <= anim->data[3] &&
+			   anim->data[1] <= anim->data[4] &&
+			   anim->data[2] <= anim->data[5]) {
+		anim->data[10] = 1;
+	}
+
+	if (anim->data[10]) {
+		if (anim->data[0] < anim->data[6]) {
+			anim->data[0] += anim->data[9];
+		}
+		if (anim->data[1] < anim->data[7]) {
+			anim->data[1] += anim->data[9];
+		}
+		if (anim->data[2] < anim->data[8]) {
+			anim->data[2] += anim->data[9];
+		}
+	} else {
+		if (anim->data[0] > anim->data[3]) {
+			anim->data[0] -= anim->data[9];
+		}
+		if (anim->data[1] > anim->data[4]) {
+			anim->data[1] -= anim->data[9];
+		}
+		if (anim->data[2] > anim->data[5]) {
+			anim->data[2] -= anim->data[9];
+		}
+	}
 
-				g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
+	_room->_roomPalette[anim->startIndex * 3] = anim->data[0];
+	_room->_roomPalette[anim->startIndex * 3 + 1] = anim->data[1];
+	_room->_roomPalette[anim->startIndex * 3 + 2] = anim->data[2];
+	g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
+}
 
-			} else {
-				_paletteRotateAnim->curFrameCount++;
-			}
+void PelrockEngine::animateRotatePalette(PaletteAnim *anim) {
+	if (anim->curFrameCount >= anim->data[1]) {
+		anim->curFrameCount = 0;
+		int colors = anim->paletteMode;
+		byte *paletteValues = new byte[colors * 3];
+		for (int i = 0; i < colors; i++) {
+			paletteValues[i * 3] = _room->_roomPalette[(anim->startIndex + i) * 3];
+			paletteValues[i * 3 + 1] = _room->_roomPalette[(anim->startIndex + i) * 3 + 1];
+			paletteValues[i * 3 + 2] = _room->_roomPalette[(anim->startIndex + i) * 3 + 2];
 		}
-		_screen->markAllDirty();
+		for (int i = 0; i < colors; i++) {
+			int srcIndex = (i + 1) % colors;
+			_room->_roomPalette[(anim->startIndex + i) * 3] = paletteValues[srcIndex * 3];
+			_room->_roomPalette[(anim->startIndex + i) * 3 + 1] = paletteValues[srcIndex * 3 + 1];
+			_room->_roomPalette[(anim->startIndex + i) * 3 + 2] = paletteValues[srcIndex * 3 + 2];
+		}
+
+		g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
 
-		// _screen->update();
+	} else {
+		anim->curFrameCount++;
 	}
 }
 
@@ -1737,19 +1739,6 @@ void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 
 	_room->_currentRoomNumber = number;
 
-	if (number == 2 && _paletteFadeAnim == nullptr) { // Pelrock Mansion
-		_paletteFadeAnim = _room->paletteAnimRoom2();
-	} else if (number == 0 && _paletteRotateAnim == nullptr) {
-		_paletteRotateAnim = _room->paletteAnimRoom0();
-	} else {
-		if (_paletteFadeAnim != nullptr)
-			free(_paletteFadeAnim);
-		_paletteFadeAnim = nullptr;
-		if (_paletteRotateAnim != nullptr)
-			free(_paletteRotateAnim);
-		_paletteRotateAnim = nullptr;
-	}
-
 	_screen->markAllDirty();
 	roomFile.close();
 	delete[] background;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index d3ecbbd95e8..30c6c3816d8 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -91,6 +91,8 @@ private:
 	void drawText(Common::String text, int x, int y, int w, byte color);
 
 	void frames();
+	void animateFadePalette(PaletteAnim *anim);
+	void animateRotatePalette(PaletteAnim *anim);
 	void doAction(byte action, HotSpot *hotspot);
 	void talkTo(HotSpot *hotspot);
 	void lookAtHotspot(HotSpot *hotspot);
@@ -157,20 +159,17 @@ private:
 
 	bool showShadows = false;
 
-	//Temporary
+	// Temporary
 	int selectedInvIndex = 0;
 	int curInventoryPage = 0;
 	Common::String _menuText;
 
-	PaletteAnimFade *_paletteFadeAnim = nullptr;
-	PaletteAnimRotate *_paletteRotateAnim = nullptr;
 	// JAVA
 	bool shouldPlayIntro = false;
 	GameState stateGame = INTRO;
 	bool gameInitialized = false;
 	bool screenReady = false;
 
-
 	// int prevDirX = 0;
 	// int prevDirY = 0;
 	// Common::String objectToShow = "";
@@ -235,7 +234,6 @@ public:
 		return syncGame(s);
 	}
 
-
 	void setScreen(int s, AlfredDirection dir);
 };
 
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 75a2014e2fd..f261859bca8 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -93,52 +93,37 @@ void RoomManager::getBackground(Common::File *roomFile, int roomOffset, byte *ba
 	}
 }
 
-PaletteAnimRotate *RoomManager::paletteAnimRoom0() {
+PaletteAnim *RoomManager::getPaletteAnimForRoom(int roomNumber) {
 	Common::File exeFile;
 
 	if (!exeFile.open("JUEGO.EXE")) {
 		debug("Could not open JUEGO.EXE for palette animation!");
 		return nullptr;
 	}
-	exeFile.seek(0x0004B88C, SEEK_SET);
-	PaletteAnimRotate *anim = new PaletteAnimRotate();
-	anim->paletteStartIndex = exeFile.readByte();
-	anim->paletteMode = exeFile.readByte();
-	anim->unknown = exeFile.readByte();
-	anim->delay = exeFile.readByte();
-	exeFile.read(anim->unknownBytes, 7);
-	anim->flags = exeFile.readByte();
-	exeFile.close();
-	return anim;
-}
-
-PaletteAnimFade *RoomManager::paletteAnimRoom2() {
-	Common::File exeFile;
-
-	if (!exeFile.open("JUEGO.EXE")) {
-		debug("Could not open JUEGO.EXE for palette animation!");
+	uint32_t offset = 0;
+	switch (roomNumber) {
+	case 0:
+		offset = 0x0004B88C;
+		break;
+	case 2:
+		offset = 0x0004B860;
+		break;
+	default:
+		exeFile.close();
 		return nullptr;
 	}
-	exeFile.seek(0x0004B860, SEEK_SET);
-	PaletteAnimFade *anim = new PaletteAnimFade();
-	anim->paletteIndex = exeFile.readByte();
+
+	exeFile.seek(offset, SEEK_SET);
+	PaletteAnim *anim = new PaletteAnim();
+	anim->startIndex = exeFile.readByte();
 	anim->paletteMode = exeFile.readByte();
-	anim->currentR = exeFile.readByte() << 2;
-	anim->currentG = exeFile.readByte() << 2;
-	anim->currentB = exeFile.readByte() << 2;
-	anim->minR = exeFile.readByte() << 2;
-	anim->minG = exeFile.readByte() << 2;
-	anim->minB = exeFile.readByte() << 2;
-	anim->maxR = exeFile.readByte() << 2;
-	anim->maxG = exeFile.readByte() << 2;
-	anim->maxB = exeFile.readByte() << 2;
-	byte flags = exeFile.readByte();
-	anim->curFrameCount = 0;
-
-
-	// lower 5 bits are speed, bit 6 is direction
-	anim->speed = flags & 0x3F;
-	anim->downDirection = (flags & 0x40);
+	exeFile.read(anim->data, 10);
+	if (anim->paletteMode == 1) {
+		for (int i = 2; i < 10; i++) {
+			anim->data[i] = anim->data[i] << 2;
+		}
+	}
+
 	exeFile.close();
 	return anim;
 }
@@ -164,8 +149,7 @@ Common::Array<Exit> RoomManager::loadExits(Common::File *roomFile, int roomOffse
 		exit.targetX = roomFile->readUint16LE();
 		exit.targetY = roomFile->readUint16LE();
 		byte dir = roomFile->readByte();
-		switch (dir)
-		{
+		switch (dir) {
 		case ALFRED_RIGHT:
 			exit.dir = ALFRED_RIGHT;
 			break;
@@ -284,6 +268,15 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 		Exit exit = _currentRoomExits[i];
 		// drawRect(_screen, exit.x, exit.y, exit.w, exit.h, 100 + i);
 	}
+	PaletteAnim *anim = getPaletteAnimForRoom(roomNumber);
+	if (anim != nullptr) {
+		if (_currentPaletteAnim != nullptr) {
+			delete _currentPaletteAnim;
+		}
+		_currentPaletteAnim = anim;
+	} else {
+		_currentPaletteAnim = nullptr;
+	}
 }
 
 Common::Array<Sprite> RoomManager::loadRoomAnimations(Common::File *roomFile, int roomOffset) {
@@ -345,7 +338,7 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(Common::File *roomFile, in
 			anim.nframes = animData[subAnimOffset + j];
 			anim.loopCount = animData[subAnimOffset + 4 + j];
 			anim.speed = animData[subAnimOffset + 8 + j];
-			anim.movementFlags = animData[subAnimOffset + 14 + (j*2)] | (animData[subAnimOffset + 14 + (j*2) + 1] << 8);
+			anim.movementFlags = animData[subAnimOffset + 14 + (j * 2)] | (animData[subAnimOffset + 14 + (j * 2) + 1] << 8);
 			anim.animData = new byte[anim.nframes];
 			if (anim.w > 0 && anim.h > 0 && anim.nframes > 0) {
 				uint32_t needed = anim.w * anim.h * anim.nframes;
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 688038f8247..a73b94add24 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -39,8 +39,8 @@ public:
 	void loadRoomTalkingAnimations(int roomNumber);
 	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
-	PaletteAnimRotate *paletteAnimRoom0();
-	PaletteAnimFade *paletteAnimRoom2();
+
+	PaletteAnim *getPaletteAnimForRoom(int roomNumber);
 
 	Common::String getRoomName(int roomNumber) {
 		if (roomNumber >= 0 && roomNumber < _roomNames.size()) {
@@ -65,6 +65,7 @@ public:
 	byte _musicTrack = 0;
 	Common::Array<byte> _roomSfx;
 	byte _currentRoomNumber = 0;
+	PaletteAnim *_currentPaletteAnim = nullptr;
 
 private:
 	Common::Array<Sprite> loadRoomAnimations(Common::File *roomFile, int roomOffset);
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index d4d89c46bac..3d98e38a4be 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -314,7 +314,7 @@ struct InventoryObject {
 };
 
 struct PaletteAnimFade {
-	byte paletteIndex;
+	byte startIndex;
 	byte paletteMode;
 	byte currentR;
 	byte currentG;
@@ -332,7 +332,7 @@ struct PaletteAnimFade {
 
 
 struct PaletteAnimRotate {
-	byte paletteStartIndex;
+	byte startIndex;
 	byte paletteMode;
 	byte unknown;
 	byte delay;
@@ -341,6 +341,13 @@ struct PaletteAnimRotate {
 	byte curFrameCount = 0;
 };
 
+struct PaletteAnim {
+	byte startIndex;
+	byte paletteMode;
+	byte data[10]; // Based on mode its a rotate or fade
+	byte curFrameCount = 0;
+};
+
 } // End of namespace Pelrock
 
 #endif


Commit: e20590b5bd01ca5f159a030688c1ca5da7f5a956
    https://github.com/scummvm/scummvm/commit/e20590b5bd01ca5f159a030688c1ca5da7f5a956
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:23+02:00

Commit Message:
PELROCK: Loads stickers

Changed paths:
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 66a2578af49..03311b8295c 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -50,6 +50,27 @@ static const uint32 kInventoryDescriptionsOffset = 0x4715D;
 static const uint32 kInventoryDescriptionsSize = 7868;
 
 
+const uint32_t pegatina_offsets[137] = {
+	0x000000, 0x00005B, 0x0000B6, 0x000298, 0x00047A, 0x0023C8, 0x004316, 0x004376,
+    0x005119, 0x005EBC, 0x0083ED, 0x008529, 0x0092C4, 0x00A3AA, 0x00B490, 0x00B6A6,
+    0x00C05A, 0x00CA0E, 0x00D3D0, 0x00D46E, 0x00F036, 0x00FB8F, 0x00FC55, 0x0119D7,
+    0x013759, 0x01391F, 0x014A9D, 0x015C1B, 0x017601, 0x018FE7, 0x019048, 0x0190A9,
+    0x01910A, 0x0197F4, 0x019EDE, 0x01A7EC, 0x01B0FA, 0x01B8C4, 0x01C644, 0x01D83A,
+    0x01E104, 0x01E8C6, 0x01F45D, 0x01FBBB, 0x02011D, 0x02052F, 0x020A95, 0x020E5B,
+    0x0210B3, 0x0216E6, 0x021D5E, 0x0233A3, 0x0249E8, 0x025777, 0x026506, 0x028E2B,
+    0x02B82F, 0x02C9D7, 0x02E4CA, 0x02FFBD, 0x03234A, 0x0346D7, 0x036A83, 0x038E2F,
+    0x03B18D, 0x03D4EB, 0x03DEC9, 0x03F813, 0x04115D, 0x045303, 0x0494A9, 0x04955F,
+    0x049615, 0x0496CB, 0x0499E1, 0x049EC7, 0x04A023, 0x04A447, 0x04BA6D, 0x04BFA1,
+    0x04CE33, 0x04CF09, 0x04DB3B, 0x052885, 0x0575CF, 0x05775B, 0x057D79, 0x058397,
+    0x058969, 0x058F50, 0x05A9DB, 0x05C561, 0x05C72E, 0x05C8FB, 0x05EAC1, 0x060C87,
+    0x060D19, 0x060E62, 0x061039, 0x0613C2, 0x061764, 0x061847, 0x062535, 0x062D4B,
+    0x064F11, 0x0670D7, 0x067381, 0x0675A9, 0x0677EF, 0x067A98, 0x067DDE, 0x068115,
+    0x0684E3, 0x068A76, 0x068F30, 0x0693C8, 0x0696AD, 0x06C2C9, 0x06C84D, 0x07095D,
+    0x071854, 0x07274B, 0x073642, 0x074539, 0x075454, 0x0791DA, 0x07CF60, 0x07E4AB,
+    0x07ECED, 0x07F52F, 0x07FD71, 0x080591, 0x080B24, 0x080B84, 0x080F39, 0x0812F5,
+    0x0816B1
+};
+
 // Description offsets relative to DESCRIPTION_BASE_OFFSET
 const uint16_t description_offsets[NUM_DESCRIPTIONS] = {
 	0x0000, // Object 0: Historia de la Princesa Zenna y su amante insatisfecho
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 2a0f2fc8fe3..93c03eb230b 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -418,10 +418,13 @@ void PelrockEngine::animateRotatePalette(PaletteAnim *anim) {
 void PelrockEngine::doAction(byte action, HotSpot *hotspot) {
 	switch (action) {
 	case LOOK:
-		lookAtHotspot(_currentHotspot);
+		lookAt(hotspot);
 		break;
 	case TALK:
-		talkTo(_currentHotspot);
+		talkTo(hotspot);
+		break;
+	case OPEN:
+		open(hotspot);
 		break;
 	default:
 		break;
@@ -457,13 +460,25 @@ void PelrockEngine::talkTo(HotSpot *hotspot) {
 	// }
 }
 
-void PelrockEngine::lookAtHotspot(HotSpot *hotspot) {
+void PelrockEngine::lookAt(HotSpot *hotspot) {
 	debug("Look action clicked");
 	walkTo(_currentHotspot->x, _currentHotspot->y);
 	sayAlfred(_room->_currentRoomDescriptions[_currentHotspot->index].text);
 	_displayPopup = false;
 }
 
+void PelrockEngine::open(HotSpot *hotspot) {
+	switch (hotspot->extra)
+	{
+	case 261:
+		_room->placeSticker(_res->getSticker(91), _currentBackground);
+		break;
+
+	default:
+		break;
+	}
+}
+
 void PelrockEngine::renderText(Common::Array<Common::String> lines, int color, int baseX, int baseY) {
 
 	int maxW = 0;
@@ -782,7 +797,7 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 		_displayPopup = true;
 		_currentPopupFrame = 0;
 		_currentHotspot = &_room->_currentRoomHotspots[hotspotIndex];
-		debug("Current hotspot type: %d", _currentHotspot->type);
+		debug("Current hotspot (x=%d, y=%d) with extra = %d type: %d, desc= %s", _currentHotspot->x, _currentHotspot->y, _currentHotspot->extra, _currentHotspot->type, _room->_currentRoomDescriptions[_currentHotspot->index].text.c_str());
 	}
 }
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 30c6c3816d8..83f23136b46 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -95,7 +95,8 @@ private:
 	void animateRotatePalette(PaletteAnim *anim);
 	void doAction(byte action, HotSpot *hotspot);
 	void talkTo(HotSpot *hotspot);
-	void lookAtHotspot(HotSpot *hotspot);
+	void lookAt(HotSpot *hotspot);
+	void open(HotSpot *hotspot);
 	void renderText(Common::Array<Common::String> lines, int color, int x, int y);
 	void chooseAlfredStateAndDraw();
 	void drawAlfred(byte *buf);
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index c5da7da9312..b79af08c68d 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -235,6 +235,25 @@ void ResourceManager::loadInventoryItems() {
 	delete[] iconData;
 }
 
+Pelrock::Sticker ResourceManager::getSticker(int stickerIndex) {
+	Common::File alfred6File;
+	if (!alfred6File.open("ALFRED.6")) {
+		error("Couldnt find file ALFRED.6");
+	}
+
+	uint32 stickerOffset = pegatina_offsets[stickerIndex];
+	alfred6File.seek(stickerOffset, SEEK_SET);
+	Sticker sticker;
+	sticker.x = alfred6File.readUint16LE();
+	sticker.y = alfred6File.readUint16LE();
+	sticker.w = alfred6File.readByte();
+	sticker.h = alfred6File.readByte();
+	sticker.stickerData = new byte[sticker.w * sticker.h];
+	alfred6File.read(sticker.stickerData, sticker.w * sticker.h);
+	alfred6File.close();
+	return sticker;
+}
+
 void ResourceManager::loadInventoryDescriptions() {
 	Common::File exe;
 	if (!exe.open("JUEGO.EXE")) {
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index 9b54652f76f..dc7d70c7602 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -45,6 +45,7 @@ public:
 	void loadInteractionIcons();
 	void loadAlfredAnims();
 	void loadInventoryItems();
+	Sticker getSticker(int stickerIndex);
 	InventoryObject getInventoryObject(byte index);
 	byte *loadExtra();
 
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index f261859bca8..103d4189ee5 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -93,6 +93,21 @@ void RoomManager::getBackground(Common::File *roomFile, int roomOffset, byte *ba
 	}
 }
 
+void RoomManager::placeSticker(Sticker sticker, byte *background) {
+	for (int y = 0; y < sticker.h; y++) {
+		for (int x = 0; x < sticker.w; x++) {
+			byte pixel = sticker.stickerData[y * sticker.w + x];
+			if (pixel != 0) {
+				int bgX = sticker.x + x;
+				int bgY = sticker.y + y;
+				if (bgX >= 0 && bgX < 640 && bgY >= 0 && bgY < 400) {
+					background[bgY * 640 + bgX] = pixel;
+				}
+			}
+		}
+	}
+}
+
 PaletteAnim *RoomManager::getPaletteAnimForRoom(int roomNumber) {
 	Common::File exeFile;
 
@@ -174,7 +189,7 @@ Common::Array<Exit> RoomManager::loadExits(Common::File *roomFile, int roomOffse
 
 Common::Array<HotSpot> RoomManager::loadHotspots(Common::File *roomFile, int roomOffset) {
 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
-	// debug("Hotspot(10)  pair offset position: %d", pair10_offset_pos);
+
 	roomFile->seek(pair10_offset_pos, SEEK_SET);
 	uint32_t pair10_data_offset = roomFile->readUint32LE();
 	uint32_t pair10_size = roomFile->readUint32LE();
@@ -193,17 +208,15 @@ Common::Array<HotSpot> RoomManager::loadHotspots(Common::File *roomFile, int roo
 		spot.w = roomFile->readByte();
 		spot.h = roomFile->readByte();
 		spot.extra = roomFile->readUint16LE();
-		// debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, spot.type, spot.x, spot.y, spot.w, spot.h, spot.extra);
+		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, spot.type, spot.x, spot.y, spot.w, spot.h, spot.extra);
 		hotspots.push_back(spot);
 	}
 	return hotspots;
-	// uint32_t hover_areas_start = pair10_data_offset + 0x1BE;
-	// roomFile->seek(hover_areas_start, SEEK_SET);
 }
 
 void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	uint32_t outPos = 0;
-
+	_currentRoomStickers.clear();
 	int roomOffset = roomNumber * kRoomStructSize;
 	Common::Array<Description> descriptions = loadRoomDescriptions(roomFile, roomOffset, outPos);
 	// debug("After decsriptions, position is %d", outPos);
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index a73b94add24..c2552e87866 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -39,7 +39,7 @@ public:
 	void loadRoomTalkingAnimations(int roomNumber);
 	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
-
+	void placeSticker(Sticker sticker, byte *background);
 	PaletteAnim *getPaletteAnimForRoom(int roomNumber);
 
 	Common::String getRoomName(int roomNumber) {
@@ -66,6 +66,7 @@ public:
 	Common::Array<byte> _roomSfx;
 	byte _currentRoomNumber = 0;
 	PaletteAnim *_currentPaletteAnim = nullptr;
+	Common::Array<Sticker> _currentRoomStickers;
 
 private:
 	Common::Array<Sprite> loadRoomAnimations(Common::File *roomFile, int roomOffset);
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 3d98e38a4be..4bc5ffce333 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -330,6 +330,15 @@ struct PaletteAnimFade {
 	byte curFrameCount = 0;
 };
 
+struct Sticker
+{
+	uint16 x;
+	uint16 y;
+	byte w;
+	byte h;
+	byte *stickerData;
+};
+
 
 struct PaletteAnimRotate {
 	byte startIndex;


Commit: a3fb1e4e1cf47390a3d3f198d25e18ad63e85956
    https://github.com/scummvm/scummvm/commit/a3fb1e4e1cf47390a3d3f198d25e18ad63e85956
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:23+02:00

Commit Message:
PELROCK: Refactor pathfinding into its own module

Changed paths:
  A engines/pelrock/pathfinding.cpp
  A engines/pelrock/pathfinding.h
    engines/pelrock/chrono.cpp
    engines/pelrock/chrono.h
    engines/pelrock/module.mk
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h
    engines/pelrock/util.cpp
    engines/pelrock/util.h


diff --git a/engines/pelrock/chrono.cpp b/engines/pelrock/chrono.cpp
index cb1d1b33848..1538240f90b 100644
--- a/engines/pelrock/chrono.cpp
+++ b/engines/pelrock/chrono.cpp
@@ -37,12 +37,6 @@ ChronoManager::~ChronoManager() {
 void ChronoManager::updateChrono() {
 	uint32 currentTime = g_system->getMillis();
 
-	if (_textTtl > 0 && countTextDown) {
-		_textTtl -= (currentTime - _lastTick);
-		if (_textTtl < 0)
-			_textTtl = 0;
-	}
-
 	if ((currentTime - _lastTick) >= kTickMs / _speedMultiplier) {
 		_gameTick = true;
 		_tickCount++;
diff --git a/engines/pelrock/chrono.h b/engines/pelrock/chrono.h
index 37d096f4dbd..649ab9a0391 100644
--- a/engines/pelrock/chrono.h
+++ b/engines/pelrock/chrono.h
@@ -49,7 +49,6 @@ public:
 	void waitForKey();
 
 	bool _gameTick = false;
-	long _textTtl = 0;
 	bool countTextDown = false;
 };
 
diff --git a/engines/pelrock/module.mk b/engines/pelrock/module.mk
index f6b1579cff6..f919cdf363f 100644
--- a/engines/pelrock/module.mk
+++ b/engines/pelrock/module.mk
@@ -11,7 +11,8 @@ MODULE_OBJS = \
 	util.o \
 	resources.o\
 	sound.o \
-	video/video.o
+	video/video.o \
+	pathfinding.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_PELROCK), DYNAMIC_PLUGIN)
diff --git a/engines/pelrock/pathfinding.cpp b/engines/pelrock/pathfinding.cpp
new file mode 100644
index 00000000000..32254c551d3
--- /dev/null
+++ b/engines/pelrock/pathfinding.cpp
@@ -0,0 +1,394 @@
+/* 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 "common/debug.h"
+#include "common/scummsys.h"
+
+#include "pelrock/pathfinding.h"
+#include "pelrock/util.h"
+
+namespace Pelrock {
+
+Common::String printMovementFlags(uint8_t flags) {
+	Common::String result;
+	if (flags & MOVE_HORIZ) {
+		result += "HORIZ ";
+	}
+	if (flags & MOVE_VERT) {
+		result += "VERT ";
+	}
+	if (flags & MOVE_DOWN) {
+		result += "DOWN ";
+	}
+	if (flags & MOVE_LEFT) {
+		result += "LEFT ";
+	}
+	if (flags & MOVE_UP) {
+		result += "UP ";
+	}
+	if (flags & MOVE_RIGHT) {
+		result += "RIGHT ";
+	}
+	return result;
+}
+
+bool findPath(int sourceX, int sourceY, int targetX, int targetY, Common::Array<WalkBox> &walkboxes, PathContext *context) {
+
+	if (context->pathBuffer == NULL) {
+		context->pathBuffer = (uint8_t *)malloc(MAX_PATH_LENGTH);
+	}
+	if (context->movementBuffer == NULL) {
+		context->movementBuffer = (MovementStep *)malloc(MAX_MOVEMENT_STEPS * sizeof(MovementStep));
+	}
+
+	int startX = sourceX;
+	int startY = sourceY;
+	Common::Point target = calculateWalkTarget(walkboxes, targetX, targetY);
+	targetX = target.x;
+	targetY = target.y;
+	debug("Startx= %d, starty= %d, destx= %d, desty= %d", startX, startY, targetX, targetY);
+
+	uint8_t startBox = findWalkboxForPoint(walkboxes, startX, startY);
+	uint8_t destBox = findWalkboxForPoint(walkboxes, targetX, targetY);
+
+	debug("Pathfinding from (%d, %d) in box %d to (%d, %d) in box %d\n", startX, startY, startBox, targetX, targetY, destBox);
+	// Check if both points are in valid walkboxes
+	if (startBox == 0xFF || destBox == 0xFF) {
+		debug("Error: Start or destination not in any walkbox\n");
+		return false;
+	}
+	// Special case: same walkbox
+	if (startBox == destBox) {
+		// Generate direct movement
+		MovementStep direct_step;
+		direct_step.flags = 0;
+		if (startX < targetX) {
+			direct_step.distanceX = targetX - startX;
+			direct_step.flags |= MOVE_RIGHT;
+		} else {
+			direct_step.distanceX = startX - targetX;
+			direct_step.flags |= MOVE_LEFT;
+		}
+
+		if (startY < targetY) {
+			direct_step.distanceY = targetY - startY;
+			direct_step.flags |= MOVE_DOWN;
+		} else {
+			direct_step.distanceY = startY - targetY;
+			direct_step.flags |= MOVE_UP;
+		}
+
+		context->movementBuffer[0] = direct_step;
+		context->movementCount = 1;
+	} else {
+		// Build walkbox path
+		context->pathLength = buildWalkboxPath(walkboxes, startBox, destBox, context->pathBuffer);
+		debug("Walkbox path to point");
+		for (int i = 0; i < context->pathLength; i++) {
+			debug("Walkbox %d: %d", i, context->pathBuffer[i]);
+		}
+		if (context->pathLength == 0) {
+			debug("Error: No path found\n");
+			return false;
+		}
+
+		// Generate movement steps
+		context->movementCount = generateMovementSteps(walkboxes, context->pathBuffer, context->pathLength, startX, startY, targetX, targetY, context->movementBuffer);
+		// for (int i = 0; i < context->movementCount; i++) {
+		// 	debug("Movement step %d: flags=\"%s\", dx=%d, dy=%d", i, printMovementFlags(context->movementBuffer[i].flags).c_str(), context->movementBuffer[i].distanceX, context->movementBuffer[i].distanceY);
+		// }
+	}
+	return true;
+}
+
+Common::Point calculateWalkTarget(Common::Array<WalkBox> &walkboxes, int x, int y) {
+	// Starting point for pathfinding
+	int sourceX = x;
+	int sourceY = y;
+
+	// TODO: If hovering over a sprite/hotspot, adjust source point to sprite center
+	// For now, just use mouse position
+
+	// Find nearest walkable point in walkboxes
+	uint32 minDistance = 0xFFFFFFFF;
+	Common::Point bestTarget(sourceX, sourceY);
+
+	for (size_t i = 0; i < walkboxes.size(); i++) {
+
+		// Calculate distance from source point to this walkbox (Manhattan distance)
+		int dx = 0;
+		int dy = 0;
+
+		// Calculate horizontal distance
+		if (sourceX < walkboxes[i].x) {
+			dx = walkboxes[i].x - sourceX;
+		} else if (sourceX > walkboxes[i].x + walkboxes[i].w) {
+			dx = sourceX - (walkboxes[i].x + walkboxes[i].w);
+		}
+		// else: sourceX is inside walkbox horizontally, dx = 0
+
+		// Calculate vertical distance
+		if (sourceY < walkboxes[i].y) {
+			dy = walkboxes[i].y - sourceY;
+		} else if (sourceY > walkboxes[i].y + walkboxes[i].h) {
+			dy = sourceY - (walkboxes[i].y + walkboxes[i].h);
+		}
+		// else: sourceY is inside walkbox vertically, dy = 0
+
+		uint32 distance = dx + dy;
+
+		if (distance < minDistance) {
+			minDistance = distance;
+
+			// Calculate target point (nearest point on walkbox to source)
+			int targetX = sourceX;
+			int targetY = sourceY;
+
+			if (sourceX < walkboxes[i].x) {
+				targetX = walkboxes[i].x;
+			} else if (sourceX > walkboxes[i].x + walkboxes[i].w) {
+				targetX = walkboxes[i].x + walkboxes[i].w;
+			}
+
+			if (sourceY < walkboxes[i].y) {
+				targetY = walkboxes[i].y;
+			} else if (sourceY > walkboxes[i].y + walkboxes[i].h) {
+				targetY = walkboxes[i].y + walkboxes[i].h;
+			}
+
+			bestTarget.x = targetX;
+			bestTarget.y = targetY;
+		}
+	}
+
+	return bestTarget;
+}
+
+uint8_t findWalkboxForPoint(Common::Array<WalkBox> &walkboxes, uint16_t x, uint16_t y) {
+	for (uint8_t i = 0; i < walkboxes.size(); i++) {
+		if (isPointInWalkbox(&walkboxes[i], x, y)) {
+			return i;
+		}
+	}
+	return 0xFF; // Not found
+}
+
+bool isPointInWalkbox(WalkBox *box, uint16_t x, uint16_t y) {
+	return (x >= box->x &&
+			x <= box->x + box->w &&
+			y >= box->y &&
+			y <= box->y + box->h);
+}
+
+/**
+ * Check if two walkboxes overlap or touch (are adjacent)
+ */
+bool areWalkboxesAdjacent(WalkBox *box1, WalkBox *box2) {
+	uint16_t box1_x_max = box1->x + box1->w;
+	uint16_t box1_y_max = box1->y + box1->h;
+	uint16_t box2_x_max = box2->x + box2->w;
+	uint16_t box2_y_max = box2->y + box2->h;
+
+	// Check if X ranges overlap
+	bool xOverlap = (box1->x <= box2_x_max) && (box2->x <= box1_x_max);
+
+	// Check if Y ranges overlap
+	bool yOverlap = (box1->y <= box2_y_max) && (box2->y <= box1_y_max);
+
+	return xOverlap && yOverlap;
+}
+
+uint8_t getAdjacentWalkbox(Common::Array<WalkBox> &walkboxes, uint8_t currentBoxIndex) {
+	WalkBox *currentBox = &walkboxes[currentBoxIndex];
+
+	// Mark current walkbox as visited
+	currentBox->flags = 0x01;
+
+	// Search for adjacent unvisited walkbox
+	for (uint8_t i = 0; i < walkboxes.size(); i++) {
+		// Skip current walkbox
+		if (i == currentBoxIndex) {
+			continue;
+		}
+
+		// Skip already visited walkboxes
+		if (walkboxes[i].flags == 0x01) {
+			continue;
+		}
+
+		// Check if walkboxes are adjacent
+		if (areWalkboxesAdjacent(currentBox, &walkboxes[i])) {
+			return i;
+		}
+	}
+
+	return 0xFF; // No adjacent walkbox found
+}
+
+void clearVisitedFlags(Common::Array<WalkBox> &walkboxes) {
+	for (int i = 0; i < walkboxes.size(); i++) {
+		walkboxes[i].flags = 0;
+	}
+}
+
+uint16_t buildWalkboxPath(Common::Array<WalkBox> &walkboxes, uint8_t startBox, uint8_t destBox, uint8_t *pathBuffer) {
+
+	uint16_t pathIndex = 0;
+	uint8_t currentBox = startBox;
+
+	// Initialize path with start walkbox
+	pathBuffer[pathIndex++] = startBox;
+
+	// Clear visited flags
+	clearVisitedFlags(walkboxes);
+
+	// Breadth-first search through walkboxes
+	while (currentBox != destBox && pathIndex < MAX_PATH_LENGTH - 1) {
+		uint8_t nextBox = getAdjacentWalkbox(walkboxes, currentBox);
+
+		if (nextBox == 0xFF) {
+			// Dead end - backtrack
+			if (pathIndex > 1) {
+				pathIndex--;
+				currentBox = pathBuffer[pathIndex - 1];
+			} else {
+				// No path exists
+				return 0;
+			}
+		} else if (nextBox == destBox) {
+			// Found destination
+			pathBuffer[pathIndex++] = destBox;
+			break;
+		} else {
+			// Continue searching
+			pathBuffer[pathIndex++] = nextBox;
+			currentBox = nextBox;
+		}
+	}
+
+	// Terminate path
+	pathBuffer[pathIndex] = PATH_END;
+	debug("Built walkbox path of length %d", pathIndex);
+	return pathIndex;
+}
+
+/**
+ * Calculate movement needed to reach a target within a walkbox
+ */
+void calculateMovementToTarget(uint16_t currentX, uint16_t currentY, uint16_t targetX, uint16_t targetY, WalkBox *box, MovementStep *step) {
+	step->flags = 0;
+	step->distanceX = 0;
+	step->distanceY = 0;
+
+	// Calculate horizontal movement
+	if (currentX < box->x) {
+		// Need to move right to enter walkbox
+		step->distanceX = box->x - currentX;
+		step->flags |= MOVE_RIGHT;
+	} else if (currentX > box->x + box->w) {
+		// Need to move left to enter walkbox
+		step->distanceX = currentX - (box->x + box->w);
+		step->flags |= MOVE_LEFT;
+	}
+
+	// Calculate vertical movement
+	if (currentY < box->y) {
+		// Need to move down to enter walkbox
+		step->distanceY = box->y - currentY;
+		step->flags |= MOVE_DOWN;
+	} else if (currentY > box->y + box->h) {
+		// Need to move up to enter walkbox
+		step->distanceY = currentY - (box->y + box->h);
+		step->flags |= MOVE_UP;
+	}
+}
+
+/**
+ * Generate movement steps from walkbox path
+ * Returns: number of movement steps generated
+ */
+uint16_t generateMovementSteps(Common::Array<WalkBox> &walkboxes,
+							   uint8_t *pathBuffer,
+							   uint16_t pathLength,
+							   uint16_t startX, uint16_t startY,
+							   uint16_t destX, uint16_t destY,
+							   MovementStep *movementBuffer) {
+	uint16_t currentX = startX;
+	uint16_t currentY = startY;
+	uint16_t movementIndex = 0;
+
+	// Generate movements for each walkbox in path
+	for (uint16_t i = 0; i < pathLength && pathBuffer[i] != PATH_END; i++) {
+		uint8_t boxIndex = pathBuffer[i];
+		WalkBox *box = &walkboxes[boxIndex];
+
+		MovementStep step;
+		calculateMovementToTarget(currentX, currentY, destX, destY, box, &step);
+
+		if (step.distanceX > 0 || step.distanceY > 0) {
+			movementBuffer[movementIndex++] = step;
+
+			// Update current position
+			if (step.flags & MOVE_RIGHT) {
+				currentX = box->x;
+			} else if (step.flags & MOVE_LEFT) {
+				currentX = box->x + box->w;
+			}
+
+			if (step.flags & MOVE_DOWN) {
+				currentY = box->y;
+			} else if (step.flags & MOVE_UP) {
+				currentY = box->y + box->h;
+			}
+		}
+	}
+
+	// Final movement to exact destination
+	MovementStep final_step;
+	final_step.flags = 0;
+
+	if (currentX < destX) {
+		final_step.distanceX = destX - currentX;
+		final_step.flags |= MOVE_RIGHT;
+	} else if (currentX > destX) {
+		final_step.distanceX = currentX - destX;
+		final_step.flags |= MOVE_LEFT;
+	} else {
+		final_step.distanceX = 0;
+	}
+
+	if (currentY < destY) {
+		final_step.distanceY = destY - currentY;
+		final_step.flags |= MOVE_DOWN;
+	} else if (currentY > destY) {
+		final_step.distanceY = currentY - destY;
+		final_step.flags |= MOVE_UP;
+	} else {
+		final_step.distanceY = 0;
+	}
+
+	if (final_step.distanceX > 0 || final_step.distanceY > 0) {
+		movementBuffer[movementIndex++] = final_step;
+	}
+
+	return movementIndex;
+}
+
+} // End of namespace Pelrock
diff --git a/engines/pelrock/pathfinding.h b/engines/pelrock/pathfinding.h
new file mode 100644
index 00000000000..cb33de23d29
--- /dev/null
+++ b/engines/pelrock/pathfinding.h
@@ -0,0 +1,42 @@
+/* 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 PELROCK_PATHFINDING_H
+#define PELROCK_PATHFINDING_H
+
+#include "common/scummsys.h"
+#include "graphics/screen.h"
+
+#include "pelrock/types.h"
+
+namespace Pelrock {
+bool findPath(int sourceX, int sourceY, int targetX, int targetY, Common::Array<WalkBox> &walkboxes, PathContext *context);
+
+Common::Point calculateWalkTarget(Common::Array<WalkBox> &walkboxes, int x, int y);
+uint8_t findWalkboxForPoint(Common::Array<WalkBox> &walkboxes, uint16_t x, uint16_t y);
+uint8_t getAdjacentWalkbox(Common::Array<WalkBox> &walkboxes, uint8_t current_box_index);
+uint16_t buildWalkboxPath(Common::Array<WalkBox> &walkboxes, uint8_t start_box, uint8_t dest_box, uint8_t *path_buffer);
+uint16_t generateMovementSteps(Common::Array<WalkBox> &walkboxes, uint8_t *path_buffer, uint16_t path_length, uint16_t start_x, uint16_t start_y, uint16_t dest_x, uint16_t dest_y, MovementStep *movement_buffer);
+bool isPointInWalkbox(WalkBox *box, uint16_t x, uint16_t y);
+void clearVisitedFlags(Common::Array<WalkBox> &walkboxes);
+
+} // End of namespace Pelrock
+
+#endif // PELROCK_PATHFINDING_H
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 93c03eb230b..099a8108744 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -40,6 +40,7 @@
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/util.h"
+#include "pelrock/pathfinding.h"
 
 namespace Pelrock {
 
@@ -107,6 +108,7 @@ Common::Error PelrockEngine::run() {
 	init();
 
 	while (!shouldQuit()) {
+
 		if (stateGame == SETTINGS) {
 			changeCursor(DEFAULT);
 			menuLoop();
@@ -236,7 +238,7 @@ void sortAnimsByZOrder(Common::Array<Sprite> &anims) {
 	}
 }
 
-void PelrockEngine::frames() {
+void PelrockEngine::renderScene() {
 
 	if (_chronoManager->_gameTick) {
 
@@ -290,7 +292,7 @@ void PelrockEngine::frames() {
 
 		if (alfredState.animState != ALFRED_WALKING && !_currentTextPages.empty()) {
 			_chronoManager->countTextDown = true;
-			if (_chronoManager->_textTtl > 0) {
+			if (_textDurationFrames-- > 0) {
 				if (alfredState.animState == ALFRED_TALKING) {
 					_textPos = Common::Point(alfredState.x, alfredState.y - kAlfredFrameHeight - 10);
 				}
@@ -302,7 +304,7 @@ void PelrockEngine::frames() {
 				for (int i = 0; i < _currentTextPages[_currentTextPageIndex].size(); i++) {
 					totalChars += _currentTextPages[_currentTextPageIndex][i].size();
 				}
-				_chronoManager->_textTtl = totalChars * kTextCharDisplayTime;
+				_textDurationFrames = totalChars / 2;
 			} else {
 				_currentTextPages.clear();
 				_currentTextPageIndex = 0;
@@ -502,34 +504,34 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 
 		MovementStep step = _currentContext.movementBuffer[_currentStep];
 
-		if (step.distance_x > 0) {
+		if (step.distanceX > 0) {
 			if (step.flags & MOVE_RIGHT) {
 				alfredState.direction = ALFRED_RIGHT;
-				alfredState.x += MIN(alfredState.movementSpeed, step.distance_x);
+				alfredState.x += MIN(alfredState.movementSpeed, step.distanceX);
 			}
 			if (step.flags & MOVE_LEFT) {
 				alfredState.direction = ALFRED_LEFT;
-				alfredState.x -= MIN(alfredState.movementSpeed, step.distance_x);
+				alfredState.x -= MIN(alfredState.movementSpeed, step.distanceX);
 			}
 		}
-		if (step.distance_y > 0) {
+		if (step.distanceY > 0) {
 			if (step.flags & MOVE_DOWN) {
 				alfredState.direction = ALFRED_DOWN;
-				alfredState.y += MIN(alfredState.movementSpeed, step.distance_y);
+				alfredState.y += MIN(alfredState.movementSpeed, step.distanceY);
 			}
 			if (step.flags & MOVE_UP) {
 				alfredState.direction = ALFRED_UP;
-				alfredState.y -= MIN(alfredState.movementSpeed, step.distance_y);
+				alfredState.y -= MIN(alfredState.movementSpeed, step.distanceY);
 			}
 		}
 
-		if (step.distance_x > 0)
-			step.distance_x -= MIN(alfredState.movementSpeed, step.distance_x);
+		if (step.distanceX > 0)
+			step.distanceX -= MIN(alfredState.movementSpeed, step.distanceX);
 
-		if (step.distance_y > 0)
-			step.distance_y -= MIN(alfredState.movementSpeed, step.distance_y);
+		if (step.distanceY > 0)
+			step.distanceY -= MIN(alfredState.movementSpeed, step.distanceY);
 
-		if (step.distance_x <= 0 && step.distance_y <= 0) {
+		if (step.distanceX <= 0 && step.distanceY <= 0) {
 			_currentStep++;
 			if (_currentStep >= _currentContext.movementCount) {
 				_currentStep = 0;
@@ -1012,6 +1014,12 @@ void PelrockEngine::drawTalkNPC(Sprite *animSet) {
 	drawSpriteToBuffer(_compositeBuffer, 640, frame, x, y, w, h, 255);
 }
 
+void PelrockEngine::conversationLoop() {
+	while(inConversation) {
+
+	}
+}
+
 void PelrockEngine::gameLoop() {
 	_chronoManager->updateChrono();
 	Common::Event e;
@@ -1072,7 +1080,12 @@ void PelrockEngine::gameLoop() {
 		}
 	}
 	checkMouseHover();
-	frames();
+
+	if(inConversation) {
+		conversationLoop();
+	} else {
+		renderScene();
+	}
 }
 
 void PelrockEngine::menuLoop() {
@@ -1106,293 +1119,12 @@ void PelrockEngine::menuLoop() {
 void PelrockEngine::walkTo(int x, int y) {
 	_currentStep = 0;
 	PathContext context = {nullptr, nullptr, nullptr, 0, 0, 0};
-	pathFind(x, y, &context);
+	findPath(alfredState.x, alfredState.y, x, y, _room->_currentRoomWalkboxes, &context);
 	_currentContext = context;
 	alfredState.animState = ALFRED_WALKING;
 	alfredState.curFrame = 0;
 }
 
-bool PelrockEngine::pathFind(int targetX, int targetY, PathContext *context) {
-
-	if (context->pathBuffer == NULL) {
-		context->pathBuffer = (uint8_t *)malloc(MAX_PATH_LENGTH);
-	}
-	if (context->movementBuffer == NULL) {
-		context->movementBuffer = (MovementStep *)malloc(MAX_MOVEMENT_STEPS * sizeof(MovementStep));
-	}
-
-	int startX = alfredState.x;
-	int startY = alfredState.y;
-	Common::Point target = calculateWalkTarget(targetX, targetY);
-	targetX = target.x;
-	targetY = target.y;
-	debug("Startx= %d, starty= %d, destx= %d, desty= %d", startX, startY, targetX, targetY);
-
-	uint8_t startBox = findWalkboxForPoint(startX, startY);
-	uint8_t destBox = findWalkboxForPoint(targetX, targetY);
-
-	debug("Pathfinding from (%d, %d) in box %d to (%d, %d) in box %d\n", startX, startY, startBox, targetX, targetY, destBox);
-	// Check if both points are in valid walkboxes
-	if (startBox == 0xFF || destBox == 0xFF) {
-		debug("Error: Start or destination not in any walkbox\n");
-		return false;
-	}
-	// Special case: same walkbox
-	if (startBox == destBox) {
-		// Generate direct movement
-		MovementStep direct_step;
-		direct_step.flags = 0;
-		if (startX < targetX) {
-			direct_step.distance_x = targetX - startX;
-			direct_step.flags |= MOVE_RIGHT;
-		} else {
-			direct_step.distance_x = startX - targetX;
-			direct_step.flags |= MOVE_LEFT;
-		}
-
-		if (startY < targetY) {
-			direct_step.distance_y = targetY - startY;
-			direct_step.flags |= MOVE_DOWN;
-		} else {
-			direct_step.distance_y = startY - targetY;
-			direct_step.flags |= MOVE_UP;
-		}
-
-		context->movementBuffer[0] = direct_step;
-		context->movementCount = 1;
-	} else {
-		// Build walkbox path
-		context->pathLength = buildWalkboxPath(startBox, destBox, context->pathBuffer);
-		debug("Walkbox path to point");
-		for (int i = 0; i < context->pathLength; i++) {
-			debug("Walkbox %d: %d", i, context->pathBuffer[i]);
-		}
-		if (context->pathLength == 0) {
-			debug("Error: No path found\n");
-			return false;
-		}
-
-		// Generate movement steps
-		context->movementCount = generateMovementSteps(context->pathBuffer, context->pathLength, startX, startY, targetX, targetY, context->movementBuffer);
-		for (int i = 0; i < context->movementCount; i++) {
-			debug("Movement step %d: flags=\"%s\", dx=%d, dy=%d", i, printMovementFlags(context->movementBuffer[i].flags).c_str(), context->movementBuffer[i].distance_x, context->movementBuffer[i].distance_y);
-		}
-	}
-	return true;
-}
-
-/**
- * Calculate movement needed to reach a target within a walkbox
- */
-void calculateMovementToTarget(uint16_t current_x, uint16_t current_y,
-							   uint16_t target_x, uint16_t target_y,
-							   WalkBox *box,
-							   MovementStep *step) {
-	step->flags = 0;
-	step->distance_x = 0;
-	step->distance_y = 0;
-
-	// Calculate horizontal movement
-	if (current_x < box->x) {
-		// Need to move right to enter walkbox
-		step->distance_x = box->x - current_x;
-		step->flags |= MOVE_RIGHT;
-	} else if (current_x > box->x + box->w) {
-		// Need to move left to enter walkbox
-		step->distance_x = current_x - (box->x + box->w);
-		step->flags |= MOVE_LEFT;
-	}
-
-	// Calculate vertical movement
-	if (current_y < box->y) {
-		// Need to move down to enter walkbox
-		step->distance_y = box->y - current_y;
-		step->flags |= MOVE_DOWN;
-	} else if (current_y > box->y + box->h) {
-		// Need to move up to enter walkbox
-		step->distance_y = current_y - (box->y + box->h);
-		step->flags |= MOVE_UP;
-	}
-}
-
-/**
- * Generate movement steps from walkbox path
- * Returns: number of movement steps generated
- */
-uint16_t PelrockEngine::generateMovementSteps(uint8_t *pathBuffer,
-											  uint16_t pathLength,
-											  uint16_t startX, uint16_t startY,
-											  uint16_t destX, uint16_t destY,
-											  MovementStep *movementBuffer) {
-	uint16_t currentX = startX;
-	uint16_t currentY = startY;
-	uint16_t movementIndex = 0;
-
-	// Generate movements for each walkbox in path
-	for (uint16_t i = 0; i < pathLength && pathBuffer[i] != PATH_END; i++) {
-		uint8_t boxIndex = pathBuffer[i];
-		WalkBox *box = &_room->_currentRoomWalkboxes[boxIndex];
-
-		MovementStep step;
-		calculateMovementToTarget(currentX, currentY, destX, destY, box, &step);
-
-		if (step.distance_x > 0 || step.distance_y > 0) {
-			movementBuffer[movementIndex++] = step;
-
-			// Update current position
-			if (step.flags & MOVE_RIGHT) {
-				currentX = box->x;
-			} else if (step.flags & MOVE_LEFT) {
-				currentX = box->x + box->w;
-			}
-
-			if (step.flags & MOVE_DOWN) {
-				currentY = box->y;
-			} else if (step.flags & MOVE_UP) {
-				currentY = box->y + box->h;
-			}
-		}
-	}
-
-	// Final movement to exact destination
-	MovementStep final_step;
-	final_step.flags = 0;
-
-	if (currentX < destX) {
-		final_step.distance_x = destX - currentX;
-		final_step.flags |= MOVE_RIGHT;
-	} else if (currentX > destX) {
-		final_step.distance_x = currentX - destX;
-		final_step.flags |= MOVE_LEFT;
-	} else {
-		final_step.distance_x = 0;
-	}
-
-	if (currentY < destY) {
-		final_step.distance_y = destY - currentY;
-		final_step.flags |= MOVE_DOWN;
-	} else if (currentY > destY) {
-		final_step.distance_y = currentY - destY;
-		final_step.flags |= MOVE_UP;
-	} else {
-		final_step.distance_y = 0;
-	}
-
-	if (final_step.distance_x > 0 || final_step.distance_y > 0) {
-		movementBuffer[movementIndex++] = final_step;
-	}
-
-	return movementIndex;
-}
-
-uint16_t PelrockEngine::buildWalkboxPath(uint8_t startBox, uint8_t destBox, uint8_t *pathBuffer) {
-
-	uint16_t pathIndex = 0;
-	uint8_t currentBox = startBox;
-
-	// Initialize path with start walkbox
-	pathBuffer[pathIndex++] = startBox;
-
-	// Clear visited flags
-	clearVisitedFlags();
-
-	// Breadth-first search through walkboxes
-	while (currentBox != destBox && pathIndex < MAX_PATH_LENGTH - 1) {
-		uint8_t nextBox = getAdjacentWalkbox(currentBox);
-
-		if (nextBox == 0xFF) {
-			// Dead end - backtrack
-			if (pathIndex > 1) {
-				pathIndex--;
-				currentBox = pathBuffer[pathIndex - 1];
-			} else {
-				// No path exists
-				return 0;
-			}
-		} else if (nextBox == destBox) {
-			// Found destination
-			pathBuffer[pathIndex++] = destBox;
-			break;
-		} else {
-			// Continue searching
-			pathBuffer[pathIndex++] = nextBox;
-			currentBox = nextBox;
-		}
-	}
-
-	// Terminate path
-	pathBuffer[pathIndex] = PATH_END;
-	debug("Built walkbox path of length %d", pathIndex);
-	return pathIndex;
-}
-
-void PelrockEngine::clearVisitedFlags() {
-	for (int i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
-		_room->_currentRoomWalkboxes[i].flags = 0;
-	}
-}
-
-/**
- * Check if two walkboxes overlap or touch (are adjacent)
- */
-bool areWalkboxesAdjacent(WalkBox *box1, WalkBox *box2) {
-	uint16_t box1_x_max = box1->x + box1->w;
-	uint16_t box1_y_max = box1->y + box1->h;
-	uint16_t box2_x_max = box2->x + box2->w;
-	uint16_t box2_y_max = box2->y + box2->h;
-
-	// Check if X ranges overlap
-	bool xOverlap = (box1->x <= box2_x_max) && (box2->x <= box1_x_max);
-
-	// Check if Y ranges overlap
-	bool yOverlap = (box1->y <= box2_y_max) && (box2->y <= box1_y_max);
-
-	return xOverlap && yOverlap;
-}
-
-uint8_t PelrockEngine::getAdjacentWalkbox(uint8_t currentBoxIndex) {
-	WalkBox *currentBox = &_room->_currentRoomWalkboxes[currentBoxIndex];
-
-	// Mark current walkbox as visited
-	currentBox->flags = 0x01;
-
-	// Search for adjacent unvisited walkbox
-	for (uint8_t i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
-		// Skip current walkbox
-		if (i == currentBoxIndex) {
-			continue;
-		}
-
-		// Skip already visited walkboxes
-		if (_room->_currentRoomWalkboxes[i].flags == 0x01) {
-			continue;
-		}
-
-		// Check if walkboxes are adjacent
-		if (areWalkboxesAdjacent(currentBox, &_room->_currentRoomWalkboxes[i])) {
-			return i;
-		}
-	}
-
-	return 0xFF; // No adjacent walkbox found
-}
-
-bool PelrockEngine::isPointInWalkbox(WalkBox *box, uint16_t x, uint16_t y) {
-	return (x >= box->x &&
-			x <= box->x + box->w &&
-			y >= box->y &&
-			y <= box->y + box->h);
-}
-
-uint8_t PelrockEngine::findWalkboxForPoint(uint16_t x, uint16_t y) {
-	for (uint8_t i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
-		if (isPointInWalkbox(&_room->_currentRoomWalkboxes[i], x, y)) {
-			return i;
-		}
-	}
-	return 0xFF; // Not found
-}
-
 VerbIcon PelrockEngine::isActionUnder(int x, int y) {
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
 	for (int i = 0; i < actions.size(); i++) {
@@ -1407,6 +1139,20 @@ VerbIcon PelrockEngine::isActionUnder(int x, int y) {
 	return NO_ACTION;
 }
 
+bool PelrockEngine::isAlfredUnder(int x, int y) {
+	//TODO: Account for scaling
+	int alfredX = alfredState.x;
+	int alfredY = alfredState.y;
+	int alfredW = kAlfredFrameWidth;
+	int alfredH = kAlfredFrameHeight;
+
+	if(alfredY - alfredH > y || alfredY < y || alfredX > x || alfredX + alfredW < x) {
+		return false;
+	}
+	return true;
+}
+
+
 void PelrockEngine::checkMouseClick(int x, int y) {
 
 	if (whichNPCTalking)
@@ -1426,7 +1172,7 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 	_displayPopup = false;
 	_currentHotspot = nullptr;
 
-	Common::Point walkTarget = calculateWalkTarget(mouseX, mouseY);
+	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, mouseX, mouseY);
 	_curWalkTarget = walkTarget;
 
 	{ // For quick room navigation
@@ -1449,10 +1195,10 @@ void PelrockEngine::changeCursor(Cursor cursor) {
 }
 
 void PelrockEngine::checkMouseHover() {
-	bool isSomethingUnder = false;
+	bool hotspotDetected = false;
 
 	// Calculate walk target first (before checking anything else)
-	Common::Point walkTarget = calculateWalkTarget(mouseX, mouseY);
+	Common::Point walkTarget = calculateWalkTarget( _room->_currentRoomWalkboxes, mouseX, mouseY);
 
 	// Check if walk target hits any exit
 	bool exitDetected = false;
@@ -1463,88 +1209,96 @@ void PelrockEngine::checkMouseHover() {
 
 	int hotspotIndex = isHotspotUnder(mouseX, mouseY);
 	if (hotspotIndex != -1) {
-		isSomethingUnder = true;
+		hotspotDetected = true;
 	}
 
 	if (isActionUnder(mouseX, mouseY) != NO_ACTION) {
-		isSomethingUnder = false;
+		hotspotDetected = false;
+	}
+
+	bool alfredDetected = false;
+	if(isAlfredUnder(mouseX, mouseY)) {
+		alfredDetected = true;
 	}
 
-	if (isSomethingUnder && exitDetected) {
+	if(alfredDetected) {
+		changeCursor(ALFRED);
+	} else if (hotspotDetected && exitDetected) {
 		changeCursor(COMBINATION);
-	} else if (isSomethingUnder) {
+	} else if (hotspotDetected) {
 		changeCursor(HOTSPOT);
 	} else if (exitDetected) {
 		changeCursor(EXIT);
-	} else {
+	}
+	else {
 		changeCursor(DEFAULT);
 	}
 }
 
-Common::Point PelrockEngine::calculateWalkTarget(int mouseX, int mouseY) {
-	// Starting point for pathfinding
-	int sourceX = mouseX;
-	int sourceY = mouseY;
-
-	// TODO: If hovering over a sprite/hotspot, adjust source point to sprite center
-	// For now, just use mouse position
-
-	// Find nearest walkable point in walkboxes
-	uint32 minDistance = 0xFFFFFFFF;
-	Common::Point bestTarget(sourceX, sourceY);
-
-	// for (Common::List<WalkBox>::iterator it = _currentRoomWalkboxes.begin();
-	//  it != _currentRoomWalkboxes.end(); ++it) {
-	for (size_t i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
-
-		// Calculate distance from source point to this walkbox (Manhattan distance)
-		int dx = 0;
-		int dy = 0;
-
-		// Calculate horizontal distance
-		if (sourceX < _room->_currentRoomWalkboxes[i].x) {
-			dx = _room->_currentRoomWalkboxes[i].x - sourceX;
-		} else if (sourceX > _room->_currentRoomWalkboxes[i].x + _room->_currentRoomWalkboxes[i].w) {
-			dx = sourceX - (_room->_currentRoomWalkboxes[i].x + _room->_currentRoomWalkboxes[i].w);
-		}
-		// else: sourceX is inside walkbox horizontally, dx = 0
-
-		// Calculate vertical distance
-		if (sourceY < _room->_currentRoomWalkboxes[i].y) {
-			dy = _room->_currentRoomWalkboxes[i].y - sourceY;
-		} else if (sourceY > _room->_currentRoomWalkboxes[i].y + _room->_currentRoomWalkboxes[i].h) {
-			dy = sourceY - (_room->_currentRoomWalkboxes[i].y + _room->_currentRoomWalkboxes[i].h);
-		}
-		// else: sourceY is inside walkbox vertically, dy = 0
-
-		uint32 distance = dx + dy;
-
-		if (distance < minDistance) {
-			minDistance = distance;
-
-			// Calculate target point (nearest point on walkbox to source)
-			int targetX = sourceX;
-			int targetY = sourceY;
-
-			if (sourceX < _room->_currentRoomWalkboxes[i].x) {
-				targetX = _room->_currentRoomWalkboxes[i].x;
-			} else if (sourceX > _room->_currentRoomWalkboxes[i].x + _room->_currentRoomWalkboxes[i].w) {
-				targetX = _room->_currentRoomWalkboxes[i].x + _room->_currentRoomWalkboxes[i].w;
-			}
-
-			if (sourceY < _room->_currentRoomWalkboxes[i].y) {
-				targetY = _room->_currentRoomWalkboxes[i].y;
-			} else if (sourceY > _room->_currentRoomWalkboxes[i].y + _room->_currentRoomWalkboxes[i].h) {
-				targetY = _room->_currentRoomWalkboxes[i].y + _room->_currentRoomWalkboxes[i].h;
-			}
-
-			bestTarget.x = targetX;
-			bestTarget.y = targetY;
-		}
-	}
-
-	return bestTarget;
-}
+// Common::Point PelrockEngine::calculateWalkTarget(int mouseX, int mouseY) {
+// 	// Starting point for pathfinding
+// 	int sourceX = mouseX;
+// 	int sourceY = mouseY;
+
+// 	// TODO: If hovering over a sprite/hotspot, adjust source point to sprite center
+// 	// For now, just use mouse position
+
+// 	// Find nearest walkable point in walkboxes
+// 	uint32 minDistance = 0xFFFFFFFF;
+// 	Common::Point bestTarget(sourceX, sourceY);
+
+// 	// for (Common::List<WalkBox>::iterator it = _currentRoomWalkboxes.begin();
+// 	//  it != _currentRoomWalkboxes.end(); ++it) {
+// 	for (size_t i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
+
+// 		// Calculate distance from source point to this walkbox (Manhattan distance)
+// 		int dx = 0;
+// 		int dy = 0;
+
+// 		// Calculate horizontal distance
+// 		if (sourceX < _room->_currentRoomWalkboxes[i].x) {
+// 			dx = _room->_currentRoomWalkboxes[i].x - sourceX;
+// 		} else if (sourceX > _room->_currentRoomWalkboxes[i].x + _room->_currentRoomWalkboxes[i].w) {
+// 			dx = sourceX - (_room->_currentRoomWalkboxes[i].x + _room->_currentRoomWalkboxes[i].w);
+// 		}
+// 		// else: sourceX is inside walkbox horizontally, dx = 0
+
+// 		// Calculate vertical distance
+// 		if (sourceY < _room->_currentRoomWalkboxes[i].y) {
+// 			dy = _room->_currentRoomWalkboxes[i].y - sourceY;
+// 		} else if (sourceY > _room->_currentRoomWalkboxes[i].y + _room->_currentRoomWalkboxes[i].h) {
+// 			dy = sourceY - (_room->_currentRoomWalkboxes[i].y + _room->_currentRoomWalkboxes[i].h);
+// 		}
+// 		// else: sourceY is inside walkbox vertically, dy = 0
+
+// 		uint32 distance = dx + dy;
+
+// 		if (distance < minDistance) {
+// 			minDistance = distance;
+
+// 			// Calculate target point (nearest point on walkbox to source)
+// 			int targetX = sourceX;
+// 			int targetY = sourceY;
+
+// 			if (sourceX < _room->_currentRoomWalkboxes[i].x) {
+// 				targetX = _room->_currentRoomWalkboxes[i].x;
+// 			} else if (sourceX > _room->_currentRoomWalkboxes[i].x + _room->_currentRoomWalkboxes[i].w) {
+// 				targetX = _room->_currentRoomWalkboxes[i].x + _room->_currentRoomWalkboxes[i].w;
+// 			}
+
+// 			if (sourceY < _room->_currentRoomWalkboxes[i].y) {
+// 				targetY = _room->_currentRoomWalkboxes[i].y;
+// 			} else if (sourceY > _room->_currentRoomWalkboxes[i].y + _room->_currentRoomWalkboxes[i].h) {
+// 				targetY = _room->_currentRoomWalkboxes[i].y + _room->_currentRoomWalkboxes[i].h;
+// 			}
+
+// 			bestTarget.x = targetX;
+// 			bestTarget.y = targetY;
+// 		}
+// 	}
+
+// 	return bestTarget;
+// }
 
 void PelrockEngine::drawText(Common::String text, int x, int y, int w, byte color) {
 	Common::Rect rect = _largeFont->getBoundingBox(text.c_str());
@@ -1576,7 +1330,7 @@ void PelrockEngine::sayNPC(Sprite *anim, Common::String text, byte color) {
 	}
 	debug("Settijng textpos to %d, %d", anim->x, anim->y - 10);
 	_textPos = Common::Point(anim->x, anim->y - 10);
-	_chronoManager->_textTtl = totalChars * kTextCharDisplayTime;
+	_textDurationFrames = totalChars / 2;
 }
 
 void PelrockEngine::sayAlfred(Common::String text) {
@@ -1589,7 +1343,7 @@ void PelrockEngine::sayAlfred(Common::String text) {
 	for (int i = 0; i < _currentTextPages[0].size(); i++) {
 		totalChars += _currentTextPages[0][i].size();
 	}
-	_chronoManager->_textTtl = totalChars * kTextCharDisplayTime;
+	_textDurationFrames = totalChars / 2;
 }
 
 bool isEndMarker(char char_byte) {
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 83f23136b46..0fbf0148874 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -64,14 +64,6 @@ private:
 		Walking alforithm
 	*/
 	void walkTo(int x, int y);
-	bool pathFind(int x, int y, PathContext *context);
-	Common::Point calculateWalkTarget(int mouseX, int mouseY);
-	uint8_t findWalkboxForPoint(uint16_t x, uint16_t y);
-	bool isPointInWalkbox(WalkBox *box, uint16_t x, uint16_t y);
-	uint16_t buildWalkboxPath(uint8_t start_box, uint8_t dest_box, uint8_t *path_buffer);
-	uint8_t getAdjacentWalkbox(uint8_t current_box_index);
-	void clearVisitedFlags();
-	uint16_t generateMovementSteps(uint8_t *path_buffer, uint16_t path_length, uint16_t start_x, uint16_t start_y, uint16_t dest_x, uint16_t dest_y, MovementStep *movement_buffer);
 
 	void talk(byte object);
 	void displayChoices(Common::Array<Common::String> choices, byte *compositeBuffer);
@@ -83,6 +75,7 @@ private:
 
 	Common::Array<VerbIcon> availableActions(HotSpot *hotspot);
 	VerbIcon isActionUnder(int x, int y);
+	bool isAlfredUnder(int x, int y);
 	int isHotspotUnder(int x, int y);
 	Exit *isExitUnder(int x, int y);
 	Sprite *isSpriteUnder(int x, int y);
@@ -90,7 +83,7 @@ private:
 
 	void drawText(Common::String text, int x, int y, int w, byte color);
 
-	void frames();
+	void renderScene();
 	void animateFadePalette(PaletteAnim *anim);
 	void animateRotatePalette(PaletteAnim *anim);
 	void doAction(byte action, HotSpot *hotspot);
@@ -103,6 +96,7 @@ private:
 	void drawNextFrame(Sprite *animSet);
 	void changeCursor(Cursor cursor);
 	void drawTalkNPC(Sprite *animSet);
+	void conversationLoop();
 
 	void gameLoop();
 	void menuLoop();
@@ -125,6 +119,7 @@ private:
 	// text display
 	byte _textColor = 0;
 	Common::Point _textPos;
+	int _textDurationFrames = 0;
 	Common::Array<Common::Array<Common::String>> _currentTextPages = Common::Array<Common::Array<Common::String>>();
 	int _currentTextPageIndex = 0;
 
@@ -168,6 +163,7 @@ private:
 	// JAVA
 	bool shouldPlayIntro = false;
 	GameState stateGame = INTRO;
+	bool inConversation = false;
 	bool gameInitialized = false;
 	bool screenReady = false;
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 4bc5ffce333..aa8e4f9edb8 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -124,8 +124,8 @@ struct AlfredState {
 
 typedef struct {
 	uint8_t flags;       /* Direction flags (see MOVE_* constants) */
-	uint16_t distance_x; // Horizontal distance to move
-	uint16_t distance_y; // Vertical distance to move
+	uint16_t distanceX; // Horizontal distance to move
+	uint16_t distanceY; // Vertical distance to move
 } MovementStep;
 
 /**
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index ac0efab9cf0..33713fbce43 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -57,29 +57,6 @@ void drawRect(byte *screenBuffer, int x, int y, int w, int h, byte color) {
 	}
 }
 
-Common::String printMovementFlags(uint8_t flags) {
-	Common::String result;
-	if (flags & MOVE_HORIZ) {
-		result += "HORIZ ";
-	}
-	if (flags & MOVE_VERT) {
-		result += "VERT ";
-	}
-	if (flags & MOVE_DOWN) {
-		result += "DOWN ";
-	}
-	if (flags & MOVE_LEFT) {
-		result += "LEFT ";
-	}
-	if (flags & MOVE_UP) {
-		result += "UP ";
-	}
-	if (flags & MOVE_RIGHT) {
-		result += "RIGHT ";
-	}
-	return result;
-}
-
 size_t rleDecompress(
 	const uint8_t *input,
 	size_t inputSize,
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index 82e78cc368c..1d0aec0a740 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -39,7 +39,6 @@ void extractSingleFrame(byte *source, byte *dest, int frameIndex, int frameWidth
 void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byte color);
 void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color);
 void drawRect(byte *screenBuffer, int x, int y, int w, int h, byte color);
-Common::String printMovementFlags(uint8_t flags);
 Common::String joinStrings(const Common::Array<Common::String> &strings, const Common::String &separator);
 void drawPos(Graphics::ManagedSurface *surface, int x, int y, byte color);
 byte decodeChar(byte b);


Commit: 7bc6fce6fc6f49ac5a3a4a294c58f29b8e93f941
    https://github.com/scummvm/scummvm/commit/7bc6fce6fc6f49ac5a3a4a294c58f29b8e93f941
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:23+02:00

Commit Message:
PELROCK: Restructuring of renderloop

Changed paths:
  A engines/pelrock/dialog.cpp
  A engines/pelrock/dialog.h
  A engines/pelrock/graphics.cpp
  A engines/pelrock/graphics.h
    engines/pelrock/chrono.cpp
    engines/pelrock/chrono.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h


diff --git a/engines/pelrock/chrono.cpp b/engines/pelrock/chrono.cpp
index 1538240f90b..d5c3e3655e5 100644
--- a/engines/pelrock/chrono.cpp
+++ b/engines/pelrock/chrono.cpp
@@ -39,7 +39,7 @@ void ChronoManager::updateChrono() {
 
 	if ((currentTime - _lastTick) >= kTickMs / _speedMultiplier) {
 		_gameTick = true;
-		_tickCount++;
+		_frameCount++;
 		_lastTick = currentTime;
 	} else {
 		_gameTick = false;
diff --git a/engines/pelrock/chrono.h b/engines/pelrock/chrono.h
index 649ab9a0391..67f8ced785f 100644
--- a/engines/pelrock/chrono.h
+++ b/engines/pelrock/chrono.h
@@ -37,8 +37,8 @@ const int kHalfTickMultiplier = 2;
 class ChronoManager {
 private:
 	uint32 _lastTick = 0;
-	byte _tickCount = 0;
 	byte _speedMultiplier = 1;
+	uint32 _frameCount = 0;
 
 public:
 	ChronoManager();
@@ -47,6 +47,9 @@ public:
 	void changeSpeed();
 	void delay(uint32 ms);
 	void waitForKey();
+	uint32 getFrameCount() const {
+		return _frameCount;
+	}
 
 	bool _gameTick = false;
 	bool countTextDown = false;
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
new file mode 100644
index 00000000000..fb5db72f960
--- /dev/null
+++ b/engines/pelrock/dialog.cpp
@@ -0,0 +1,36 @@
+/* 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 "pelrock/dialog.h"
+#include "dialog.h"
+
+namespace Pelrock {
+
+DialogManager::DialogManager(/* args */) {
+}
+
+DialogManager::~DialogManager() {
+}
+
+void DialogManager::startConversation(ConversationNode &root) {
+
+}
+} // namespace Pelrock
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
new file mode 100644
index 00000000000..ae1b032c04f
--- /dev/null
+++ b/engines/pelrock/dialog.h
@@ -0,0 +1,43 @@
+/* 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 PELROCK_DIALOG_H
+#define PELROCK_DIALOG_H
+
+#include "common/array.h"
+#include "common/scummsys.h"
+
+#include "pelrock/pelrock.h"
+#include "pelrock/types.h"
+namespace Pelrock {
+
+class DialogManager
+{
+private:
+    /* data */
+public:
+    DialogManager(/* args */);
+    ~DialogManager();
+
+    void startConversation(ConversationNode &root);
+};
+
+} // End of namespace Pelrock
+#endif // PELROCK_DIALOG_H
diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
new file mode 100644
index 00000000000..69c05b16b4f
--- /dev/null
+++ b/engines/pelrock/graphics.cpp
@@ -0,0 +1,35 @@
+/* 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 "common/scummsys.h"
+
+#include "pelrock/graphics.h"
+
+namespace Pelrock {
+
+GraphicsManager::GraphicsManager() {
+}
+
+GraphicsManager::~GraphicsManager() {
+}
+
+} // End of namespace Pelrock
+
diff --git a/engines/pelrock/graphics.h b/engines/pelrock/graphics.h
new file mode 100644
index 00000000000..a4718e50624
--- /dev/null
+++ b/engines/pelrock/graphics.h
@@ -0,0 +1,38 @@
+/* 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 PELROCK_GRAPHICS_H
+#define PELROCK_GRAPHICS_H
+
+#include "common/scummsys.h"
+
+namespace Pelrock {
+
+class GraphicsManager {
+
+public:
+	GraphicsManager();
+	~GraphicsManager();
+
+    void renderScene(bool showDialogOverlay);
+};
+
+} // End of namespace Pelrock
+#endif // PELROCK_GRAPHICS_H
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 099a8108744..b5cb852c6cb 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -38,9 +38,9 @@
 #include "pelrock/detection.h"
 #include "pelrock/fonts/small_font.h"
 #include "pelrock/offsets.h"
+#include "pelrock/pathfinding.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/util.h"
-#include "pelrock/pathfinding.h"
 
 namespace Pelrock {
 
@@ -156,6 +156,30 @@ void PelrockEngine::loadAnims() {
 	_res->loadAlfredAnims();
 }
 
+void PelrockEngine::talkLoop() {
+	while (inConversation) {
+
+		// // Display and advance through text
+		// while (/*moresegments*/ 1) {
+
+		// 	// Show each segment with animation
+		// 	while (!time_expired && !click) {
+		// 		← INNER LOOP
+
+		// 		wait_or_process_input();
+		// 		setup_alfred_frame_from_state();
+
+		// 		render_scene(0);
+		// 		← CALLS FULL ANIMATION CODE
+		//             ↓ update_npc_sprite_animations()  ← HERE !-Increment frame counters - Advance animation frames - Process movement - Apply sprite changes
+
+		// 																																	 process_game_state(1);
+
+		// 	} // End animation loop
+		// }
+	}
+}
+
 void PelrockEngine::displayChoices(Common::Array<Common::String> choices, byte *compositeBuffer) {
 	int overlayHeight = choices.size() * kChoiceHeight + 2;
 	int overlayY = 400 - overlayHeight;
@@ -238,118 +262,141 @@ void sortAnimsByZOrder(Common::Array<Sprite> &anims) {
 	}
 }
 
-void PelrockEngine::renderScene() {
-
-	if (_chronoManager->_gameTick) {
-
-		int soundIndex = _soundManager->tick();
-		if (soundIndex >= 0 && soundIndex < _room->_roomSfx.size()) {
-			// debug("Playing SFX index %d", soundIndex);
-			_soundManager->playSound(_room->_roomSfx[3 + soundIndex]);
-		}
-
-		// Sort sprites by zOrder (persists in the array)
-		sortAnimsByZOrder(_room->_currentRoomAnims);
+void PelrockEngine::playSoundIfNeeded() {
 
-		memcpy(_compositeBuffer, _currentBackground, 640 * 400);
+	int soundIndex = _soundManager->tick(_chronoManager->getFrameCount());
+	if (soundIndex >= 0 && soundIndex < _room->_roomSfx.size()) {
+		debug("Playing SFX index %d", soundIndex);
+		_soundManager->playSound(_room->_roomSfx[3 + soundIndex]);
+	}
+}
 
-		// Create temporary render order partitioned by Alfred's Y position
-		Common::Array<Sprite *> renderOrder;
-		int alfredY = alfredState.y;
+void PelrockEngine::renderScene(bool showTextOverlay) {
 
-		// First pass: sprites behind Alfred (y <= alfredY)
-		for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
-			if (_room->_currentRoomAnims[i].zOrder > 10) {
-				drawNextFrame(&_room->_currentRoomAnims[i]);
-			}
-		}
+	if (_chronoManager->_gameTick) {
+		playSoundIfNeeded();
+
+		copyBackgroundToBuffer();
+		updateAnimations();
+
+		if (showTextOverlay) {
+			displayChoices(_currentTextPages[_currentTextPageIndex], _compositeBuffer);
+		}
+
+		presentFrame();
+		updatePaletteAnimations();
+
+		// if (alfredState.animState != ALFRED_WALKING && !_currentTextPages.empty()) {
+		// 	_chronoManager->countTextDown = true;
+		// 	if (_textDurationFrames-- > 0) {
+		// 		if (alfredState.animState == ALFRED_TALKING) {
+		// 			_textPos = Common::Point(alfredState.x, alfredState.y - kAlfredFrameHeight - 10);
+		// 		}
+		// 		renderText(_currentTextPages[_currentTextPageIndex], _textColor, _textPos.x, _textPos.y);
+		// 	} else if (_currentTextPageIndex < _currentTextPages.size() - 1) {
+		// 		_currentTextPageIndex++;
+
+		// 		int totalChars = 0;
+		// 		for (int i = 0; i < _currentTextPages[_currentTextPageIndex].size(); i++) {
+		// 			totalChars += _currentTextPages[_currentTextPageIndex][i].size();
+		// 		}
+		// 		_textDurationFrames = totalChars / 2;
+		// 	} else {
+		// 		_currentTextPages.clear();
+		// 		_currentTextPageIndex = 0;
+		// 		alfredState.animState = ALFRED_IDLE;
+		// 		isNPCATalking = false;
+		// 		isNPCBTalking = false;
+		// 		_chronoManager->countTextDown = false;
+		// 	}
+		// }
 
-		// Draw Alfred here (you'll need to add this)
-		chooseAlfredStateAndDraw();
+		// if (alfredState.animState == ALFRED_IDLE && alfredState.nextState != ALFRED_IDLE) {
+		// 	// debug("Switching Alfred state from IDLE to %d", alfredState.nextState);
+		// 	alfredState.animState = alfredState.nextState;
+		// 	alfredState.nextState = ALFRED_IDLE;
+		// 	alfredState.curFrame = 0;
+		// }
 
-		// Second pass: sprites in front of Alfred (y > alfredY)
-		for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
-			if (_room->_currentRoomAnims[i].zOrder <= 10) {
-				drawNextFrame(&_room->_currentRoomAnims[i]);
-			}
-		}
+		// debug("Drawing walkboxes..., %d, _currentRoomWalkboxes.size()=%d",  _currentRoomWalkboxes.size(), _currentRoomWalkboxes.size());
 
-		if (_displayPopup) {
-			showActionBalloon(_popupX, _popupY, _currentPopupFrame);
-			if (_currentPopupFrame < 3) {
-				_currentPopupFrame++;
-			} else
-				_currentPopupFrame = 0;
-		}
+		_screen->markAllDirty();
+	}
+}
 
-		// Common::Array<Common::String> testChoices;
-		// testChoices.push_back("First choice");
-		// testChoices.push_back("Second choice");
-		// testChoices.push_back("Third choice");
-		// displayChoices(testChoices, _compositeBuffer);
+void PelrockEngine::copyBackgroundToBuffer() {
+	// copy background to buffer
+	memcpy(_compositeBuffer, _currentBackground, 640 * 400);
+}
 
-		memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
+void PelrockEngine::updateAnimations() {
+	// Sort sprites by zOrder (persists in the array)
+	sortAnimsByZOrder(_room->_currentRoomAnims);
 
-		if (alfredState.animState != ALFRED_WALKING && !_currentTextPages.empty()) {
-			_chronoManager->countTextDown = true;
-			if (_textDurationFrames-- > 0) {
-				if (alfredState.animState == ALFRED_TALKING) {
-					_textPos = Common::Point(alfredState.x, alfredState.y - kAlfredFrameHeight - 10);
-				}
-				renderText(_currentTextPages[_currentTextPageIndex], _textColor, _textPos.x, _textPos.y);
-			} else if (_currentTextPageIndex < _currentTextPages.size() - 1) {
-				_currentTextPageIndex++;
+	// Create temporary render order partitioned by Alfred's Y position
+	Common::Array<Sprite *> renderOrder;
+	int alfredY = alfredState.y;
 
-				int totalChars = 0;
-				for (int i = 0; i < _currentTextPages[_currentTextPageIndex].size(); i++) {
-					totalChars += _currentTextPages[_currentTextPageIndex][i].size();
-				}
-				_textDurationFrames = totalChars / 2;
-			} else {
-				_currentTextPages.clear();
-				_currentTextPageIndex = 0;
-				alfredState.animState = ALFRED_IDLE;
-				isNPCATalking = false;
-				isNPCBTalking = false;
-				_chronoManager->countTextDown = false;
-			}
+	// First pass: sprites behind Alfred (y <= alfredY)
+	for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
+		if (_room->_currentRoomAnims[i].zOrder > 10) {
+			drawNextFrame(&_room->_currentRoomAnims[i]);
 		}
+	}
 
-		if (alfredState.animState == ALFRED_IDLE && alfredState.nextState != ALFRED_IDLE) {
-			// debug("Switching Alfred state from IDLE to %d", alfredState.nextState);
-			alfredState.animState = alfredState.nextState;
-			alfredState.nextState = ALFRED_IDLE;
-			alfredState.curFrame = 0;
-		}
+	// Draw Alfred here (you'll need to add this)
+	chooseAlfredStateAndDraw();
 
-		// debug("Drawing walkboxes..., %d, _currentRoomWalkboxes.size()=%d",  _currentRoomWalkboxes.size(), _currentRoomWalkboxes.size());
-		for (int i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
-			// debug("Drawing walkbox %d", i);
-			WalkBox box = _room->_currentRoomWalkboxes[i];
-			drawRect(_screen, box.x, box.y, box.w, box.h, 150 + i);
-			_smallFont->drawString(_screen, Common::String::format("%d", i), box.x + 2, box.y + 2, 640, 14);
+	// Second pass: sprites in front of Alfred (y > alfredY)
+	for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
+		if (_room->_currentRoomAnims[i].zOrder <= 10) {
+			drawNextFrame(&_room->_currentRoomAnims[i]);
 		}
+	}
 
-		drawPos(_screen, alfredState.x, alfredState.y, 13);
-		drawPos(_screen, alfredState.x, alfredState.y - kAlfredFrameHeight, 13);
-		drawPos(_screen, _curWalkTarget.x, _curWalkTarget.y, 100);
+	if (_displayPopup) {
+		showActionBalloon(_popupX, _popupY, _currentPopupFrame);
+		if (_currentPopupFrame < 3) {
+			_currentPopupFrame++;
+		} else
+			_currentPopupFrame = 0;
+	}
+}
 
-		if (showShadows) {
-			memcpy(_screen->getPixels(), _room->_pixelsShadows, 640 * 400);
-		}
-		_smallFont->drawString(_screen, Common::String::format("Room number: %d", _room->_currentRoomNumber), 0, 4, 640, 13);
-		_smallFont->drawString(_screen, Common::String::format("Alfred pos: %d, %d (%d)", alfredState.x, alfredState.y, alfredState.y - kAlfredFrameHeight), 0, 18, 640, 13);
+void PelrockEngine::presentFrame() {
+	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
+	paintDebugLayer();
+	_screen->markAllDirty();
+}
 
-		if (_room->_currentPaletteAnim != nullptr) {
-			if (_room->_currentPaletteAnim->paletteMode == 1) {
-				animateFadePalette(_room->_currentPaletteAnim);
-			} else {
-				animateRotatePalette(_room->_currentPaletteAnim);
-			}
+void PelrockEngine::updatePaletteAnimations() {
+	if (_room->_currentPaletteAnim != nullptr) {
+		if (_room->_currentPaletteAnim->paletteMode == 1) {
+			animateFadePalette(_room->_currentPaletteAnim);
+		} else {
+			animateRotatePalette(_room->_currentPaletteAnim);
 		}
+	}
+}
 
-		_screen->markAllDirty();
+void PelrockEngine::paintDebugLayer() {
+	for (int i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
+		// debug("Drawing walkbox %d", i);
+		WalkBox box = _room->_currentRoomWalkboxes[i];
+		drawRect(_screen, box.x, box.y, box.w, box.h, 150 + i);
+		_smallFont->drawString(_screen, Common::String::format("%d", i), box.x + 2, box.y + 2, 640, 14);
+	}
+
+	drawPos(_screen, alfredState.x, alfredState.y, 13);
+	drawPos(_screen, alfredState.x, alfredState.y - kAlfredFrameHeight, 13);
+	drawPos(_screen, _curWalkTarget.x, _curWalkTarget.y, 100);
+
+	if (showShadows) {
+		memcpy(_screen->getPixels(), _room->_pixelsShadows, 640 * 400);
 	}
+	_smallFont->drawString(_screen, Common::String::format("Room number: %d", _room->_currentRoomNumber), 0, 4, 640, 13);
+	_smallFont->drawString(_screen, Common::String::format("Alfred pos: %d, %d (%d)", alfredState.x, alfredState.y, alfredState.y - kAlfredFrameHeight), 0, 18, 640, 13);
+	_smallFont->drawString(_screen, Common::String::format("Frame number: %d", _chronoManager->getFrameCount()), 0, 30, 640, 13);
 }
 
 void PelrockEngine::animateFadePalette(PaletteAnim *anim) {
@@ -470,8 +517,7 @@ void PelrockEngine::lookAt(HotSpot *hotspot) {
 }
 
 void PelrockEngine::open(HotSpot *hotspot) {
-	switch (hotspot->extra)
-	{
+	switch (hotspot->extra) {
 	case 261:
 		_room->placeSticker(_res->getSticker(91), _currentBackground);
 		break;
@@ -1015,8 +1061,7 @@ void PelrockEngine::drawTalkNPC(Sprite *animSet) {
 }
 
 void PelrockEngine::conversationLoop() {
-	while(inConversation) {
-
+	while (inConversation) {
 	}
 }
 
@@ -1081,7 +1126,7 @@ void PelrockEngine::gameLoop() {
 	}
 	checkMouseHover();
 
-	if(inConversation) {
+	if (inConversation) {
 		conversationLoop();
 	} else {
 		renderScene();
@@ -1140,19 +1185,18 @@ VerbIcon PelrockEngine::isActionUnder(int x, int y) {
 }
 
 bool PelrockEngine::isAlfredUnder(int x, int y) {
-	//TODO: Account for scaling
+	// TODO: Account for scaling
 	int alfredX = alfredState.x;
 	int alfredY = alfredState.y;
 	int alfredW = kAlfredFrameWidth;
 	int alfredH = kAlfredFrameHeight;
 
-	if(alfredY - alfredH > y || alfredY < y || alfredX > x || alfredX + alfredW < x) {
+	if (alfredY - alfredH > y || alfredY < y || alfredX > x || alfredX + alfredW < x) {
 		return false;
 	}
 	return true;
 }
 
-
 void PelrockEngine::checkMouseClick(int x, int y) {
 
 	if (whichNPCTalking)
@@ -1198,7 +1242,7 @@ void PelrockEngine::checkMouseHover() {
 	bool hotspotDetected = false;
 
 	// Calculate walk target first (before checking anything else)
-	Common::Point walkTarget = calculateWalkTarget( _room->_currentRoomWalkboxes, mouseX, mouseY);
+	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, mouseX, mouseY);
 
 	// Check if walk target hits any exit
 	bool exitDetected = false;
@@ -1217,11 +1261,11 @@ void PelrockEngine::checkMouseHover() {
 	}
 
 	bool alfredDetected = false;
-	if(isAlfredUnder(mouseX, mouseY)) {
+	if (isAlfredUnder(mouseX, mouseY)) {
 		alfredDetected = true;
 	}
 
-	if(alfredDetected) {
+	if (alfredDetected) {
 		changeCursor(ALFRED);
 	} else if (hotspotDetected && exitDetected) {
 		changeCursor(COMBINATION);
@@ -1229,8 +1273,7 @@ void PelrockEngine::checkMouseHover() {
 		changeCursor(HOTSPOT);
 	} else if (exitDetected) {
 		changeCursor(EXIT);
-	}
-	else {
+	} else {
 		changeCursor(DEFAULT);
 	}
 }
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 0fbf0148874..109e9f07793 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -66,6 +66,7 @@ private:
 	void walkTo(int x, int y);
 
 	void talk(byte object);
+	void talkLoop();
 	void displayChoices(Common::Array<Common::String> choices, byte *compositeBuffer);
 	void sayAlfred(Common::String text);
 	void sayNPC(Sprite *anim, Common::String text, byte color);
@@ -83,7 +84,14 @@ private:
 
 	void drawText(Common::String text, int x, int y, int w, byte color);
 
-	void renderScene();
+	void renderScene(bool showTextOverlay = false);
+	void copyBackgroundToBuffer();
+	void updateAnimations();
+	void presentFrame();
+	void updatePaletteAnimations();
+	void paintDebugLayer();
+
+
 	void animateFadePalette(PaletteAnim *anim);
 	void animateRotatePalette(PaletteAnim *anim);
 	void doAction(byte action, HotSpot *hotspot);
@@ -96,8 +104,9 @@ private:
 	void drawNextFrame(Sprite *animSet);
 	void changeCursor(Cursor cursor);
 	void drawTalkNPC(Sprite *animSet);
-	void conversationLoop();
+	void playSoundIfNeeded();
 
+	void conversationLoop();
 	void gameLoop();
 	void menuLoop();
 
@@ -180,10 +189,10 @@ protected:
 
 public:
 	Graphics::Screen *_screen = nullptr;
-	AlfredState alfredState;
 	ChronoManager *_chronoManager = nullptr;
 	VideoManager *_videoManager = nullptr;
 	SoundManager *_soundManager = nullptr;
+	AlfredState alfredState;
 
 public:
 	PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc);
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index bbea25006be..1c08df8579e 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -104,7 +104,6 @@ void SoundManager::playSound(SonidoFile sound, int volume) {
 	// 	int channel = findFreeChannel();
 	// 	_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, volume, 0, DisposeAfterUse::YES);
 	// }
-	// TODO: Play sound file
 }
 
 SoundFormat SoundManager::detectFormat(byte *data, uint32 size) {
@@ -254,9 +253,8 @@ void SoundManager::loadSoundIndex() {
 
 int RANDOM_THRESHOLD = 0x4000;
 
-int SoundManager::tick() {
+int SoundManager::tick(uint32 frameCount) {
 
-	soundFrameCounter++;
 
 	uint16 rand1 = _rng.nextRandom();
 	// uint32 random = g_engine->getRandomNumber(1);
@@ -265,7 +263,7 @@ int SoundManager::tick() {
 		return -1;
 	}
 
-	if((soundFrameCounter & COUNTER_MASK) != COUNTER_MASK){
+	if((frameCount & COUNTER_MASK) != COUNTER_MASK){
 		// debug("No SFX this tick due to counter mask (counter = %d)", soundFrameCounter);
 		return -1;
 	}
@@ -273,8 +271,6 @@ int SoundManager::tick() {
 	uint16 rand2 = _rng.nextRandom();
 	int slot = rand2 & 3;
 	// debug("Slot = %d (rand2 = %u)", slot, rand2);
-
-	soundFrameCounter = 0;
 	// uint32 slot = g_engine->getRandomNumber(4);
 	return slot + 1;
 }
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index 3be968743ea..9a22bdfa099 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -201,7 +201,7 @@ public:
 	}
 	void loadSoundIndex();
 
-	int tick();
+	int tick(uint32 frameCount);
 
 private:
 	void playSound(SonidoFile sound, int volume = 255);
@@ -217,7 +217,6 @@ private:
 	byte _currentMusicTrack = 0;
 	Audio::SoundHandle _musicHandle;
 	Audio::SoundHandle _sfxHandles[kMaxChannels];
-	int soundFrameCounter = 0;
 	Common::HashMap<Common::String, SonidoFile> _soundMap;
 	GameRNG _rng = GameRNG(0);
 };


Commit: b58ca4248cd62962c4c7633146530283f366b8ac
    https://github.com/scummvm/scummvm/commit/b58ca4248cd62962c4c7633146530283f366b8ac
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:23+02:00

Commit Message:
PELROCK: Create EventManager

Changed paths:
  A engines/pelrock/events.cpp
  A engines/pelrock/events.h
    engines/pelrock/chrono.cpp
    engines/pelrock/chrono.h
    engines/pelrock/module.mk
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/video/video.cpp
    engines/pelrock/video/video.h


diff --git a/engines/pelrock/chrono.cpp b/engines/pelrock/chrono.cpp
index d5c3e3655e5..db0f7d8c10a 100644
--- a/engines/pelrock/chrono.cpp
+++ b/engines/pelrock/chrono.cpp
@@ -65,19 +65,4 @@ void ChronoManager::delay(uint32 ms) {
 	}
 }
 
-void ChronoManager::waitForKey() {
-	bool waitForKey = false;
-	Common::Event e;
-	debug("Waiting for key!");
-	while (!waitForKey && !g_engine->shouldQuit()) {
-		while (g_system->getEventManager()->pollEvent(e)) {
-			if (e.type == Common::EVENT_KEYDOWN) {
-				waitForKey = true;
-			}
-		}
-
-		g_engine->_screen->update();
-		g_system->delayMillis(10);
-	}
-}
 } // End of namespace Pelrock
diff --git a/engines/pelrock/chrono.h b/engines/pelrock/chrono.h
index 67f8ced785f..f7f67229545 100644
--- a/engines/pelrock/chrono.h
+++ b/engines/pelrock/chrono.h
@@ -46,7 +46,6 @@ public:
 	void updateChrono();
 	void changeSpeed();
 	void delay(uint32 ms);
-	void waitForKey();
 	uint32 getFrameCount() const {
 		return _frameCount;
 	}
diff --git a/engines/pelrock/events.cpp b/engines/pelrock/events.cpp
new file mode 100644
index 00000000000..bc81829f64b
--- /dev/null
+++ b/engines/pelrock/events.cpp
@@ -0,0 +1,120 @@
+/* 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 "common/events.h"
+
+#include "pelrock/events.h"
+#include "pelrock/pelrock.h"
+
+namespace Pelrock {
+
+PelrockEventManager::PelrockEventManager() {
+}
+
+void PelrockEventManager::pollEvent() {
+
+	Common::EventManager *eventMan = g_engine->_system->getEventManager();
+	while (eventMan->pollEvent(_event)) {
+		if (isMouseEvent(_event)) {
+			_mouseX = _event.mouse.x;
+			_mouseY = _event.mouse.y;
+		}
+		switch (_event.type) {
+		case Common::EVENT_QUIT:
+		case Common::EVENT_RETURN_TO_LAUNCHER:
+			return;
+
+		// case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
+		// 	// handle action
+		// 	handleKey(_event);
+		// 	break;
+
+		// case Common::EVENT_CUSTOM_ENGINE_ACTION_END:
+		// 	break;
+		// case Common::EVENT_KEYDOWN:
+		// 	changeGameSpeed(_event);
+		// 	_keyPressed = true;
+		// 	_lastKeyEvent = _event;
+		// 	return;
+		// case Common::EVENT_KEYUP:
+		// 	return;
+		case Common::EVENT_LBUTTONDOWN:
+			if (_leftMouseButton == 0) {
+				_clickTime = g_system->getMillis();
+			}
+			_leftMouseButton = 1;
+			break;
+		case Common::EVENT_LBUTTONUP:
+			if (_leftMouseButton == 1) {
+				_leftMouseClicked = true;
+				_mouseClickX = _event.mouse.x;
+				_mouseClickY = _event.mouse.y;
+				_longClicked = false;
+			} else {
+				_leftMouseClicked = false;
+			}
+			_longClicked = false;
+			_leftMouseButton = 0;
+			_clickTime = 0;
+			break;
+		case Common::EVENT_RBUTTONDOWN:
+			_rightMouseButton = 1;
+			debug("Right mouse button down");
+			break;
+		case Common::EVENT_RBUTTONUP:
+			if (_rightMouseButton == 1) {
+				debug("Right mouse clicked");
+				_rightMouseClicked = true;
+			} else {
+				_rightMouseClicked = false;
+			}
+			_rightMouseButton = 0;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (_leftMouseButton) {
+		uint32 elapsedLongClick = g_system->getMillis() - _clickTime;
+		if (elapsedLongClick >= kDoubleClickDelay) {
+			elapsedLongClick = 0;
+			_longClicked = true;
+			_leftMouseButton = 0;
+		}
+	}
+}
+
+void PelrockEventManager::waitForKey() {
+	bool waitForKey = false;
+	Common::Event e;
+	debug("Waiting for key!");
+	while (!waitForKey && !g_engine->shouldQuit()) {
+		while (g_system->getEventManager()->pollEvent(e)) {
+			if (e.type == Common::EVENT_KEYDOWN) {
+				waitForKey = true;
+			}
+		}
+
+		g_engine->_screen->update();
+		g_system->delayMillis(10);
+	}
+}
+} // namespace Pelrock
diff --git a/engines/pelrock/events.h b/engines/pelrock/events.h
new file mode 100644
index 00000000000..ddea8935c8b
--- /dev/null
+++ b/engines/pelrock/events.h
@@ -0,0 +1,50 @@
+/* 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 PELROCK_EVENTS_H
+#define PELROCK_EVENTS_H
+
+#include "common/events.h"
+#include "common/scummsys.h"
+
+namespace Pelrock {
+static const int kDoubleClickDelay = 300; // in milliseconds
+class PelrockEventManager {
+private:
+	Common::Event _event;
+	bool _leftMouseButton = 0;
+	bool _rightMouseButton = 0;
+	uint32 _clickTime = 0;
+public:
+	int16 _mouseX = 0;
+	int16 _mouseY = 0;
+	int16 _mouseClickX = 0;
+	int16 _mouseClickY = 0;
+	bool _leftMouseClicked = false;
+	bool _longClicked = false;
+	bool _rightMouseClicked = false;
+	Common::Event _lastKeyEvent;
+	PelrockEventManager();
+	void pollEvent();
+	void waitForKey();
+};
+
+} // End of namespace Pelrock
+#endif
diff --git a/engines/pelrock/module.mk b/engines/pelrock/module.mk
index f919cdf363f..d935e5fa27f 100644
--- a/engines/pelrock/module.mk
+++ b/engines/pelrock/module.mk
@@ -12,7 +12,8 @@ MODULE_OBJS = \
 	resources.o\
 	sound.o \
 	video/video.o \
-	pathfinding.o
+	pathfinding.o \
+	events.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_PELROCK), DYNAMIC_PLUGIN)
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index b5cb852c6cb..fc471ab0c36 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -49,22 +49,24 @@ PelrockEngine *g_engine;
 PelrockEngine::PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engine(syst),
 																				 _gameDescription(gameDesc), _randomSource("Pelrock") {
 	g_engine = this;
-	_chronoManager = new ChronoManager();
+	_chrono = new ChronoManager();
+	_events = new PelrockEventManager();
 }
 
 PelrockEngine::~PelrockEngine() {
 	delete[] _compositeBuffer;
 	delete[] _currentBackground;
 	delete _largeFont;
+	delete _smallFont;
 	delete _screen;
-	delete _chronoManager;
+	delete _chrono;
 	delete _videoManager;
-	delete _soundManager;
+	delete _sound;
 	delete _room;
 	delete _res;
+	delete _events;
 	// if (_bgPopupBalloon)
 	// 	delete[] _bgPopupBalloon;
-	delete _smallFont;
 }
 
 uint32 PelrockEngine::getFeatures() const {
@@ -81,10 +83,10 @@ Common::Error PelrockEngine::run() {
 	// Initialize 320x200 paletted graphics mode
 	initGraphics(640, 400);
 	_screen = new Graphics::Screen();
-	_videoManager = new VideoManager(_screen);
+	_videoManager = new VideoManager(_screen, _events);
 	_room = new RoomManager();
 	_res = new ResourceManager();
-	_soundManager = new SoundManager(_mixer);
+	_sound = new SoundManager(_mixer);
 
 	// Set the engine's debugger console
 	setDebugger(new PelrockConsole(this));
@@ -95,7 +97,6 @@ Common::Error PelrockEngine::run() {
 		(void)loadGameState(saveSlot);
 
 	// Simple event handling loop
-	Common::Event e;
 	Graphics::FrameLimiter limiter(g_system, 60);
 
 	if (shouldPlayIntro == false) {
@@ -128,7 +129,7 @@ void PelrockEngine::init() {
 	_res->loadInteractionIcons();
 	_res->loadInventoryItems();
 	_res->loadSettingsMenu();
-	_soundManager->loadSoundIndex();
+	_sound->loadSoundIndex();
 
 	calculateScalingMasks();
 	_compositeBuffer = new byte[640 * 400];
@@ -146,9 +147,6 @@ void PelrockEngine::init() {
 		gameInitialized = true;
 		loadAnims();
 		setScreen(0, ALFRED_DOWN);
-		// setScreen(6, 0); // museum entrance
-		// setScreen(13, 1); // restaurants kitchen
-		// setScreen(2, 2); // hooker
 	}
 }
 
@@ -156,30 +154,6 @@ void PelrockEngine::loadAnims() {
 	_res->loadAlfredAnims();
 }
 
-void PelrockEngine::talkLoop() {
-	while (inConversation) {
-
-		// // Display and advance through text
-		// while (/*moresegments*/ 1) {
-
-		// 	// Show each segment with animation
-		// 	while (!time_expired && !click) {
-		// 		← INNER LOOP
-
-		// 		wait_or_process_input();
-		// 		setup_alfred_frame_from_state();
-
-		// 		render_scene(0);
-		// 		← CALLS FULL ANIMATION CODE
-		//             ↓ update_npc_sprite_animations()  ← HERE !-Increment frame counters - Advance animation frames - Process movement - Apply sprite changes
-
-		// 																																	 process_game_state(1);
-
-		// 	} // End animation loop
-		// }
-	}
-}
-
 void PelrockEngine::displayChoices(Common::Array<Common::String> choices, byte *compositeBuffer) {
 	int overlayHeight = choices.size() * kChoiceHeight + 2;
 	int overlayY = 400 - overlayHeight;
@@ -264,16 +238,16 @@ void sortAnimsByZOrder(Common::Array<Sprite> &anims) {
 
 void PelrockEngine::playSoundIfNeeded() {
 
-	int soundIndex = _soundManager->tick(_chronoManager->getFrameCount());
+	int soundIndex = _sound->tick(_chrono->getFrameCount());
 	if (soundIndex >= 0 && soundIndex < _room->_roomSfx.size()) {
 		debug("Playing SFX index %d", soundIndex);
-		_soundManager->playSound(_room->_roomSfx[3 + soundIndex]);
+		_sound->playSound(_room->_roomSfx[3 + soundIndex]);
 	}
 }
 
 void PelrockEngine::renderScene(bool showTextOverlay) {
 
-	if (_chronoManager->_gameTick) {
+	if (_chrono->_gameTick) {
 		playSoundIfNeeded();
 
 		copyBackgroundToBuffer();
@@ -283,6 +257,8 @@ void PelrockEngine::renderScene(bool showTextOverlay) {
 			displayChoices(_currentTextPages[_currentTextPageIndex], _compositeBuffer);
 		}
 
+		checkMouse();
+
 		presentFrame();
 		updatePaletteAnimations();
 
@@ -324,6 +300,55 @@ void PelrockEngine::renderScene(bool showTextOverlay) {
 	}
 }
 
+void PelrockEngine::checkMouse() {
+	if(_events->_leftMouseClicked) {
+		checkMouseClick(_events->_mouseClickX, _events->_mouseClickY);
+		_events->_leftMouseClicked = false;
+		_displayPopup = false;
+	}
+	else if(_events->_longClicked) {
+		checkLongMouseClick(_events->_mouseClickX, _events->_mouseClickY);
+		_events->_longClicked = false;
+	}
+	else if(_events->_rightMouseClicked) {
+		debug("Right mouse clicked - entering settings menu");
+		g_system->getPaletteManager()->setPalette(_res->_mainMenuPalette, 0, 256);
+		_events->_rightMouseClicked = false;
+		stateGame = SETTINGS;
+	}
+	checkMouseHover();
+
+//else if (e.type == Common::EVENT_MOUSEMOVE) {
+	// 		mouseX = e.mouse.x;
+	// 		mouseY = e.mouse.y;
+	// 		// debug(3, "Mouse moved to (%d,%d)", mouseX, mouseY);
+	// 	} else if (e.type == Common::EVENT_LBUTTONDOWN) {
+	// 		if (!_isMouseDown) {
+	// 			_mouseClickTime = g_system->getMillis();
+	// 			_isMouseDown = true;
+	// 		}
+	// 	} else if (e.type == Common::EVENT_LBUTTONUP) {
+	// 		_isMouseDown = false;
+	// 		checkMouseClick(e.mouse.x, e.mouse.y);
+	// 		_displayPopup = false;
+	// 		_longClick = false;
+	// 	} else if (e.type == Common::EVENT_RBUTTONUP) {
+	// 		g_system->getPaletteManager()->setPalette(_res->_mainMenuPalette, 0, 256);
+	// 		stateGame = SETTINGS;
+	// 	}
+	// }
+	// if (_isMouseDown) {
+	// 	if (g_system->getMillis() - _mouseClickTime >= kLongClickDuration) {
+	// 		debug("long click!");
+	// 		_longClick = true;
+	// 		_isMouseDown = false;
+	// 		checkLongMouseClick(e.mouse.x, e.mouse.y);
+	// 	}
+	// }
+	// checkMouseHover();
+
+}
+
 void PelrockEngine::copyBackgroundToBuffer() {
 	// copy background to buffer
 	memcpy(_compositeBuffer, _currentBackground, 640 * 400);
@@ -396,12 +421,12 @@ void PelrockEngine::paintDebugLayer() {
 	}
 	_smallFont->drawString(_screen, Common::String::format("Room number: %d", _room->_currentRoomNumber), 0, 4, 640, 13);
 	_smallFont->drawString(_screen, Common::String::format("Alfred pos: %d, %d (%d)", alfredState.x, alfredState.y, alfredState.y - kAlfredFrameHeight), 0, 18, 640, 13);
-	_smallFont->drawString(_screen, Common::String::format("Frame number: %d", _chronoManager->getFrameCount()), 0, 30, 640, 13);
+	_smallFont->drawString(_screen, Common::String::format("Frame number: %d", _chrono->getFrameCount()), 0, 30, 640, 13);
 }
 
 void PelrockEngine::animateFadePalette(PaletteAnim *anim) {
 
-	// if (_paletteAnim->curFrameCount >= _paletteAnim->speed) {
+
 	if (anim->data[0] >= anim->data[6] &&
 		anim->data[1] >= anim->data[7] &&
 		anim->data[2] >= anim->data[8]) {
@@ -800,7 +825,6 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 	int curFrame = animData.curFrame;
 	byte *frame = new byte[frameSize];
 	extractSingleFrame(animData.animData, frame, curFrame, animData.w, animData.h);
-
 	drawSpriteToBuffer(_compositeBuffer, 640, frame, sprite->x, sprite->y, sprite->w, sprite->h, 255);
 
 	if (animData.elpapsedFrames == animData.speed) {
@@ -827,7 +851,7 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 }
 
 void PelrockEngine::checkLongMouseClick(int x, int y) {
-	int hotspotIndex = isHotspotUnder(mouseX, mouseY);
+	int hotspotIndex = isHotspotUnder(_events->_mouseX, _events->_mouseY);
 
 	if (hotspotIndex != -1) {
 
@@ -998,8 +1022,8 @@ int PelrockEngine::isHotspotUnder(int x, int y) {
 	for (size_t i = 0; i < _room->_currentRoomHotspots.size(); i++) {
 		HotSpot hotspot = _room->_currentRoomHotspots[i];
 		if (hotspot.isEnabled &&
-			mouseX >= hotspot.x && mouseX <= (hotspot.x + hotspot.w) &&
-			mouseY >= hotspot.y && mouseY <= (hotspot.y + hotspot.h)) {
+			x >= hotspot.x && x <= (hotspot.x + hotspot.w) &&
+			y >= hotspot.y && y <= (hotspot.y + hotspot.h)) {
 			return i;
 		}
 	}
@@ -1021,7 +1045,8 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 
 	drawSpriteToBuffer(_compositeBuffer, 640, _res->_popUpBalloon + (curFrame * kBalloonHeight * kBalloonWidth), posx, posy, kBalloonWidth, kBalloonHeight, 255);
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
-	VerbIcon icon = isActionUnder(mouseX, mouseY);
+
+	VerbIcon icon = isActionUnder(_events->_mouseX, _events->_mouseY);
 	for (int i = 0; i < actions.size(); i++) {
 		if (icon == actions[i] && _iconBlink++ < kIconBlinkPeriod / 2) {
 			continue;
@@ -1062,69 +1087,70 @@ void PelrockEngine::drawTalkNPC(Sprite *animSet) {
 
 void PelrockEngine::conversationLoop() {
 	while (inConversation) {
+
 	}
 }
 
 void PelrockEngine::gameLoop() {
-	_chronoManager->updateChrono();
-	Common::Event e;
-	while (g_system->getEventManager()->pollEvent(e)) {
-		if (e.type == Common::EVENT_KEYDOWN) {
-			switch (e.kbd.keycode) {
-			case Common::KEYCODE_w:
-				alfredState.animState = ALFRED_WALKING;
-				break;
-			case Common::KEYCODE_t:
-				alfredState.animState = ALFRED_TALKING;
-				break;
-			case Common::KEYCODE_s:
-				alfredState.animState = ALFRED_IDLE;
-				break;
-			case Common::KEYCODE_c:
-				alfredState.animState = ALFRED_COMB;
-				break;
-			case Common::KEYCODE_i:
-				alfredState.animState = ALFRED_INTERACTING;
-				break;
-			case Common::KEYCODE_z:
-				showShadows = !showShadows;
-				break;
-			case Common::KEYCODE_y:
-				alfredState.x = 193;
-				alfredState.y = 382;
-				walkTo(377, 318);
-				break;
-			default:
-				break;
-			}
-		} else if (e.type == Common::EVENT_MOUSEMOVE) {
-			mouseX = e.mouse.x;
-			mouseY = e.mouse.y;
-			// debug(3, "Mouse moved to (%d,%d)", mouseX, mouseY);
-		} else if (e.type == Common::EVENT_LBUTTONDOWN) {
-			if (!_isMouseDown) {
-				_mouseClickTime = g_system->getMillis();
-				_isMouseDown = true;
-			}
-		} else if (e.type == Common::EVENT_LBUTTONUP) {
-			_isMouseDown = false;
-			checkMouseClick(e.mouse.x, e.mouse.y);
-			_displayPopup = false;
-			_longClick = false;
-		} else if (e.type == Common::EVENT_RBUTTONUP) {
-			g_system->getPaletteManager()->setPalette(_res->_mainMenuPalette, 0, 256);
-			stateGame = SETTINGS;
-		}
-	}
-	if (_isMouseDown) {
-		if (g_system->getMillis() - _mouseClickTime >= kLongClickDuration) {
-			debug("long click!");
-			_longClick = true;
-			_isMouseDown = false;
-			checkLongMouseClick(e.mouse.x, e.mouse.y);
-		}
-	}
-	checkMouseHover();
+	_chrono->updateChrono();
+	_events->pollEvent();
+	// while (g_system->getEventManager()->pollEvent(e)) {
+	// 	if (e.type == Common::EVENT_KEYDOWN) {
+	// 		switch (e.kbd.keycode) {
+	// 		case Common::KEYCODE_w:
+	// 			alfredState.animState = ALFRED_WALKING;
+	// 			break;
+	// 		case Common::KEYCODE_t:
+	// 			alfredState.animState = ALFRED_TALKING;
+	// 			break;
+	// 		case Common::KEYCODE_s:
+	// 			alfredState.animState = ALFRED_IDLE;
+	// 			break;
+	// 		case Common::KEYCODE_c:
+	// 			alfredState.animState = ALFRED_COMB;
+	// 			break;
+	// 		case Common::KEYCODE_i:
+	// 			alfredState.animState = ALFRED_INTERACTING;
+	// 			break;
+	// 		case Common::KEYCODE_z:
+	// 			showShadows = !showShadows;
+	// 			break;
+	// 		case Common::KEYCODE_y:
+	// 			alfredState.x = 193;
+	// 			alfredState.y = 382;
+	// 			walkTo(377, 318);
+	// 			break;
+	// 		default:
+	// 			break;
+	// 		}
+	// 	} else if (e.type == Common::EVENT_MOUSEMOVE) {
+	// 		mouseX = e.mouse.x;
+	// 		mouseY = e.mouse.y;
+	// 		// debug(3, "Mouse moved to (%d,%d)", mouseX, mouseY);
+	// 	} else if (e.type == Common::EVENT_LBUTTONDOWN) {
+	// 		if (!_isMouseDown) {
+	// 			_mouseClickTime = g_system->getMillis();
+	// 			_isMouseDown = true;
+	// 		}
+	// 	} else if (e.type == Common::EVENT_LBUTTONUP) {
+	// 		_isMouseDown = false;
+	// 		checkMouseClick(e.mouse.x, e.mouse.y);
+	// 		_displayPopup = false;
+	// 		_longClick = false;
+	// 	} else if (e.type == Common::EVENT_RBUTTONUP) {
+	// 		g_system->getPaletteManager()->setPalette(_res->_mainMenuPalette, 0, 256);
+	// 		stateGame = SETTINGS;
+	// 	}
+	// }
+	// if (_isMouseDown) {
+	// 	if (g_system->getMillis() - _mouseClickTime >= kLongClickDuration) {
+	// 		debug("long click!");
+	// 		_longClick = true;
+	// 		_isMouseDown = false;
+	// 		checkLongMouseClick(e.mouse.x, e.mouse.y);
+	// 	}
+	// }
+	// checkMouseHover();
 
 	if (inConversation) {
 		conversationLoop();
@@ -1134,16 +1160,16 @@ void PelrockEngine::gameLoop() {
 }
 
 void PelrockEngine::menuLoop() {
-	Common::Event e;
-	while (g_system->getEventManager()->pollEvent(e)) {
-		if (e.type == Common::EVENT_LBUTTONUP) {
-			checkMouseClickOnSettings(e.mouse.x, e.mouse.y);
+	_events->pollEvent();
 
-		} else if (e.type == Common::EVENT_RBUTTONUP) {
-
-			g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
-			stateGame = GAME;
-		}
+	if(_events->_leftMouseClicked) {
+		_events->_leftMouseClicked = false;
+		checkMouseClickOnSettings(_events->_mouseX, _events->_mouseY);
+	}
+	else if (_events->_rightMouseClicked) {
+		_events->_rightMouseClicked = false;
+		g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
+		stateGame = GAME;
 	}
 
 	memcpy(_compositeBuffer, _res->_mainMenu, 640 * 400);
@@ -1216,7 +1242,7 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 	_displayPopup = false;
 	_currentHotspot = nullptr;
 
-	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, mouseX, mouseY);
+	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, _events->_mouseX, _events->_mouseY);
 	_curWalkTarget = walkTarget;
 
 	{ // For quick room navigation
@@ -1242,7 +1268,7 @@ void PelrockEngine::checkMouseHover() {
 	bool hotspotDetected = false;
 
 	// Calculate walk target first (before checking anything else)
-	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, mouseX, mouseY);
+	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, _events->_mouseX, _events->_mouseY);
 
 	// Check if walk target hits any exit
 	bool exitDetected = false;
@@ -1251,17 +1277,17 @@ void PelrockEngine::checkMouseHover() {
 		exitDetected = true;
 	}
 
-	int hotspotIndex = isHotspotUnder(mouseX, mouseY);
+	int hotspotIndex = isHotspotUnder(_events->_mouseX, _events->_mouseY);
 	if (hotspotIndex != -1) {
 		hotspotDetected = true;
 	}
 
-	if (isActionUnder(mouseX, mouseY) != NO_ACTION) {
+	if (isActionUnder(_events->_mouseX, _events->_mouseY) != NO_ACTION) {
 		hotspotDetected = false;
 	}
 
 	bool alfredDetected = false;
-	if (isAlfredUnder(mouseX, mouseY)) {
+	if (isAlfredUnder(_events->_mouseX, _events->_mouseY)) {
 		alfredDetected = true;
 	}
 
@@ -1504,7 +1530,7 @@ Common::Array<Common::Array<Common::String>> wordWrap(Common::String text) {
 }
 
 void PelrockEngine::setScreen(int number, AlfredDirection dir) {
-	_soundManager->stopAllSounds();
+	_sound->stopAllSounds();
 	Common::File roomFile;
 	debug("Loading room %s number %d", _room->getRoomName(number).c_str(), number);
 	if (!roomFile.open(Common::Path("ALFRED.1"))) {
@@ -1540,14 +1566,10 @@ void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 	_room->loadRoomMetadata(&roomFile, number);
 	_room->loadRoomTalkingAnimations(number);
 	if (_room->_musicTrack > 0)
-		_soundManager->playMusicTrack(_room->_musicTrack);
+		_sound->playMusicTrack(_room->_musicTrack);
 	else {
-		_soundManager->stopMusic();
+		_sound->stopMusic();
 	}
-	// for (int i = 0; i < kNumSfxPerRoom; i++) {
-	// 	if (_room->_roomSfx[i])
-	// 		_soundManager->playSound(_room->_roomSfx[i]);
-	// }
 
 	_room->_currentRoomNumber = number;
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 109e9f07793..b19d94498c1 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -38,6 +38,7 @@
 
 #include "pelrock/chrono.h"
 #include "pelrock/detection.h"
+#include "pelrock/events.h"
 #include "pelrock/fonts/large_font.h"
 #include "pelrock/fonts/small_font.h"
 #include "pelrock/resources.h"
@@ -56,6 +57,10 @@ private:
 	Common::RandomSource _randomSource;
 	RoomManager *_room = nullptr;
 	ResourceManager *_res = nullptr;
+	ChronoManager *_chrono = nullptr;
+	VideoManager *_videoManager = nullptr;
+	SoundManager *_sound = nullptr;
+	PelrockEventManager *_events = nullptr;
 
 	void init();
 	void loadAnims();
@@ -66,7 +71,6 @@ private:
 	void walkTo(int x, int y);
 
 	void talk(byte object);
-	void talkLoop();
 	void displayChoices(Common::Array<Common::String> choices, byte *compositeBuffer);
 	void sayAlfred(Common::String text);
 	void sayNPC(Sprite *anim, Common::String text, byte color);
@@ -85,6 +89,7 @@ private:
 	void drawText(Common::String text, int x, int y, int w, byte color);
 
 	void renderScene(bool showTextOverlay = false);
+	void checkMouse();
 	void copyBackgroundToBuffer();
 	void updateAnimations();
 	void presentFrame();
@@ -136,13 +141,6 @@ private:
 	bool alfredFrameSkip = false;
 	bool isAlkfredWalking = false;
 
-	uint16 mouseX = 0;
-	uint16 mouseY = 0;
-	bool _lMouseDown = false;
-	uint32 _mouseClickTime;
-	bool _isMouseDown = false;
-	bool _longClick = false;
-
 	byte *_currentBackground = nullptr; // Clean background - NEVER modified
 	byte *_compositeBuffer;             // Working composition buffer
 
@@ -189,9 +187,6 @@ protected:
 
 public:
 	Graphics::Screen *_screen = nullptr;
-	ChronoManager *_chronoManager = nullptr;
-	VideoManager *_videoManager = nullptr;
-	SoundManager *_soundManager = nullptr;
 	AlfredState alfredState;
 
 public:
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index 93c5fd4a17e..03b78936d1b 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -29,7 +29,7 @@
 
 namespace Pelrock {
 
-VideoManager::VideoManager(Graphics::Screen *screen) : _screen(screen) {
+VideoManager::VideoManager(Graphics::Screen *screen, PelrockEventManager *events) : _screen(screen), _events(events) {
 }
 
 VideoManager::~VideoManager() {
@@ -56,7 +56,7 @@ void VideoManager::playIntro() {
 	_screen->markAllDirty();
 	_screen->update();
 	delete[] chunk0;
-	g_engine->_chronoManager->waitForKey();
+	_events->waitForKey();
 
 	size_t chunk1Size = chunkSize * 6;
 	byte chunk1_data[chunk1Size];
@@ -75,7 +75,7 @@ void VideoManager::playIntro() {
 	}
 	_screen->markAllDirty();
 	_screen->update();
-	g_engine->_chronoManager->waitForKey();
+	_events->waitForKey();
 
 	for (size_t i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) {
 		byte *chunk = new byte[chunkSize];
@@ -94,9 +94,9 @@ void VideoManager::playIntro() {
 		delete[] chunk;
 		_screen->markAllDirty();
 		_screen->update();
-		g_engine->_chronoManager->waitForKey();
+		_events->waitForKey();
 	}
-	g_engine->_chronoManager->waitForKey();
+	_events->waitForKey();
 	videoFile.close();
 }
 void VideoManager::loadPalette(Common::SeekableReadStream &stream) {
diff --git a/engines/pelrock/video/video.h b/engines/pelrock/video/video.h
index f9ac45b3a2e..405c52b06b2 100644
--- a/engines/pelrock/video/video.h
+++ b/engines/pelrock/video/video.h
@@ -22,6 +22,8 @@
 #ifndef PELROCK_VIDEO_H
 #define PELROCK_VIDEO_H
 
+#include "pelrock/events.h"
+
 namespace Pelrock {
 
 static const uint32 frame0offset = 0x5000;
@@ -39,12 +41,13 @@ static const uint32 offsets[] = {
 
 class VideoManager {
 public:
-	VideoManager(Graphics::Screen *screen);
+	VideoManager(Graphics::Screen *screen, PelrockEventManager *events);
 	~VideoManager();
 	void playIntro();
 
 private:
 	Graphics::Screen *_screen;
+	PelrockEventManager *_events;
 	void loadPalette(Common::SeekableReadStream &stream);
 	byte *decodeCopyBlock(byte *data, uint32 offset);
     byte *decodeRLE(byte *data, size_t size, uint32 offset);


Commit: e6ad7d5ed4119626befdd26a1271d5b82aeaa569
    https://github.com/scummvm/scummvm/commit/e6ad7d5ed4119626befdd26a1271d5b82aeaa569
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:24+02:00

Commit Message:
PELROCK: First attempt at conversation flow

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/module.mk
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/types.h
    engines/pelrock/util.cpp
    engines/pelrock/util.h


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index fb5db72f960..99bb129661f 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -21,16 +21,517 @@
 
 #include "pelrock/dialog.h"
 #include "dialog.h"
+#include "pelrock/pelrock.h"
+#include "pelrock/util.h"
+
+// Control character codes (negative values in signed char)
+#define CHAR_SPACE 0x20        /* ' ' */
+#define CHAR_END_MARKER_1 0xFD /* -3 (end of text marker) */
+#define CHAR_END_MARKER_2 0xF4 /* -0xC (alternate end marker) */
+#define CHAR_END_MARKER_3 0xF8 /* -8 (another end marker) */
+#define CHAR_END_MARKER_4 0xF0 /* -0x10 (another end marker) */
+#define CHAR_NEWLINE 0xF6      /* -10 (newline marker) */
+#define CHAR_PAGE_BREAK 0xF9   /* marker inserted when switching pages */
+
+// Conversation control bytes
+#define CTRL_SPEAKER_ID 0x08        /* Next byte is speaker ID (color) */
+#define CTRL_END_TEXT 0xFD          /* End of text segment */
+#define CTRL_TEXT_TERMINATOR 0xFC   /* Text terminator */
+#define CTRL_DIALOGUE_MARKER 0xFB   /* Choice marker */
+#define CTRL_DISABLED_CHOICE 0xFA   /* Disabled choice marker */
+#define CTRL_PAGE_BREAK_CONV 0xF9   /* Page break in conversation */
+#define CTRL_ACTION_TRIGGER 0xF8    /* Action trigger */
+#define CTRL_END_BRANCH 0xF7        /* End of branch */
+#define CTRL_LINE_CONTINUE 0xF6     /* Line continue/newline */
+#define CTRL_ALT_END_MARKER_1 0xF5  /* Alt end marker */
+#define CTRL_END_CONVERSATION 0xF4  /* End conversation */
+#define CTRL_DIALOGUE_MARKER_2 0xF1 /* Alt choice marker */
+#define CTRL_GO_BACK 0xF0           /* Go back in conversation */
+#define CTRL_ALT_END_MARKER_2 0xEB  /* Alt end marker 2 */
+#define CTRL_ALT_END_MARKER_3 0xFE  /* Alt end marker 3 */
 
 namespace Pelrock {
 
-DialogManager::DialogManager(/* args */) {
+DialogManager::DialogManager(Graphics::Screen *screen, PelrockEventManager *events,
+							 LargeFont *largeFont, SmallFont *smallFont)
+	: _screen(screen), _events(events),
+	  _largeFont(largeFont), _smallFont(smallFont) {
 }
 
 DialogManager::~DialogManager() {
+	delete _currentChoices;
+	_currentChoices = nullptr;
+}
+
+uint32 DialogManager::readTextBlock(
+	const byte *data,
+	uint32 dataSize,
+	uint32 startPos,
+	Common::String &outText,
+	byte &outSpeakerId) {
+	uint32 pos = startPos;
+	outSpeakerId = ALFRED_COLOR; // Default to Alfred's color
+	outText = "";
+
+	// Skip control bytes at start
+	while (pos < dataSize &&
+		   (data[pos] == CTRL_ALT_END_MARKER_1 || data[pos] == CTRL_ALT_END_MARKER_2 ||
+			data[pos] == CTRL_ALT_END_MARKER_3 || data[pos] == CTRL_TEXT_TERMINATOR ||
+			data[pos] == CTRL_GO_BACK)) {
+		pos++;
+	}
+
+	if (pos >= dataSize) {
+		return pos;
+	}
+
+	// Check for speaker ID marker
+	if (data[pos] == CTRL_SPEAKER_ID) {
+		pos++;
+		if (pos < dataSize) {
+			outSpeakerId = data[pos];
+			pos++;
+		}
+	}
+	// Check for dialogue marker (choice text)
+	else if (data[pos] == CTRL_DIALOGUE_MARKER || data[pos] == CTRL_DIALOGUE_MARKER_2) {
+		pos++; // Skip marker
+
+		// Skip choice index
+		if (pos < dataSize) {
+			pos++;
+		}
+
+		// Skip 2 bytes after choice index (speaker marker bytes)
+		if (pos < dataSize) {
+			pos++;
+		}
+		if (pos < dataSize) {
+			pos++;
+		}
+
+		// Choice text is always spoken by ALFRED
+		outSpeakerId = ALFRED_COLOR;
+	}
+
+	// Read text until control byte
+	while (pos < dataSize) {
+		byte b = data[pos];
+
+		// End markers - stop reading text
+		if (b == CTRL_END_TEXT || b == CTRL_END_CONVERSATION || b == CTRL_ACTION_TRIGGER ||
+			b == CTRL_END_BRANCH || b == CTRL_DIALOGUE_MARKER || b == CTRL_DIALOGUE_MARKER_2 ||
+			b == CTRL_TEXT_TERMINATOR || b == CTRL_ALT_END_MARKER_1 || b == CTRL_ALT_END_MARKER_2 ||
+			b == CTRL_ALT_END_MARKER_3 || b == CTRL_GO_BACK || b == CTRL_SPEAKER_ID) {
+			break;
+		}
+
+		if (b == CTRL_LINE_CONTINUE || b == CTRL_PAGE_BREAK_CONV) {
+			outText += ' ';
+			pos++;
+			continue;
+		}
+
+		if (b == 0x0A || b == 0x0B || b == 0x00) {
+			debug("Skipping byte 0x%02X at pos %u", b, pos);
+			pos++; // Skip nulls and line feeds
+			continue;
+		}
+
+		// Regular text - decode the character
+		if (b >= 0x20 && b <= 0x7A) {
+			outText += decodeChar(b);
+		} else {
+			// Try to decode special characters
+			byte decoded = decodeChar(b);
+			if (decoded != b || (decoded >= 0x20 && decoded <= 0x83)) {
+				outText += (char)decoded;
+			}
+		}
+		pos++;
+	}
+
+	return pos;
+}
+
+void DialogManager::displayChoices(Common::Array<ChoiceOption> *choices, byte *compositeBuffer) {
+	int overlayHeight = choices->size() * kChoiceHeight + 2;
+	int overlayY = 400 - overlayHeight;
+	for (int x = 0; x < 640; x++) {
+		for (int y = overlayY; y < 400; y++) {
+			int index = y * 640 + x;
+			compositeBuffer[index] = g_engine->_room->overlayRemap[compositeBuffer[index]];
+		}
+	}
+	for (int i = 0; i < choices->size(); i++) {
+		drawText(compositeBuffer, g_engine->_smallFont, (*choices)[i].text, 10, overlayY + 2 + i * kChoiceHeight, 620, 9);
+	}
+}
+
+/**
+ * Display dialogue text and wait for click to advance
+ * @param text The text to display
+ * @param speakerId The speaker ID which is used as color
+ */
+void DialogManager::displayDialogue(const Common::String &text, byte speakerId) {
+	if (text.empty() || text.size() <= 1) {
+		return;
+	}
+
+	// Clear any existing click state
+	_events->_leftMouseClicked = false;
+
+	// Render loop - display text and wait for click
+	while (!g_engine->shouldQuit()) {
+		_events->pollEvent();
+
+		// Render the scene (keeps animations going)
+		g_engine->renderScene(false);
+
+		// Draw the dialogue text on top using speaker ID as color
+		drawText(g_engine->_largeFont, text, _curSprite->x, _curSprite->y - 10, 640, speakerId);
+		// drawText(g_engine->_largeFont, "Hola", 10, 10, 640, speakerId);
+
+		// Present to screen
+		_screen->markAllDirty();
+		_screen->update();
+
+		if (_events->_leftMouseClicked) {
+			_events->_leftMouseClicked = false;
+			break;
+		}
+		g_system->delayMillis(10);
+	}
+}
+
+/**
+ * Select a choice from displayed options
+ * Returns the index of the selected choice in the choices array
+ */
+int DialogManager::selectChoice(Common::Array<Common::String> &choices, byte *compositeBuffer) {
+	// Clear any existing click state
+	_events->_leftMouseClicked = false;
+
+	int overlayHeight = choices.size() * kChoiceHeight + 2;
+	int overlayY = 400 - overlayHeight;
+
+	while (!g_engine->shouldQuit()) {
+		_events->pollEvent();
+
+		// Render the scene with choices overlay
+		g_engine->renderScene(true);
+
+		if (_events->_leftMouseClicked) {
+			_events->_leftMouseClicked = false;
+
+			// Check if click is in the choices area
+			if (_events->_mouseClickY >= overlayY) {
+				int selectedIndex = (_events->_mouseClickY - overlayY - 2) / kChoiceHeight;
+				if (selectedIndex >= 0 && selectedIndex < (int)choices.size()) {
+					return selectedIndex;
+				}
+			}
+		}
+		g_engine->_screen->update();
+		g_system->delayMillis(10);
+	}
+
+	return 0;
+}
+
+/**
+ * Parse conversation choices from the data
+ * @param data The conversation data
+ * @param dataSize Size of data
+ * @param startPos Starting position
+ * @param outChoices Output: array of choice options
+ * @return The position after parsing choices
+ */
+uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 startPos,
+								   Common::Array<ChoiceOption> &outChoices) {
+	uint32 pos = startPos;
+	outChoices.clear();
+	int firstChoiceIndex = -1;
+	int choiceCount = 0;
+
+	// Find first choice marker
+	while (pos < dataSize) {
+		byte b = data[pos];
+
+		// Stop at end markers
+		if (b == CTRL_ALT_END_MARKER_1 || b == CTRL_END_BRANCH || b == CTRL_ALT_END_MARKER_3) {
+			break;
+		}
+
+		// Found first choice marker
+		if (b == CTRL_DIALOGUE_MARKER || b == CTRL_DIALOGUE_MARKER_2) {
+			if (pos + 1 < dataSize) {
+				firstChoiceIndex = data[pos + 1];
+				ChoiceOption opt;
+				opt.index = firstChoiceIndex;
+				opt.dataOffset = pos;
+				opt.isDisabled = false;
+
+				// Parse the choice text
+				uint32 textPos = pos + 4; // Skip marker + index + 2 speaker bytes
+				while (textPos < dataSize) {
+					byte tb = data[textPos];
+					if (tb == CTRL_END_TEXT || tb == CTRL_DIALOGUE_MARKER ||
+						tb == CTRL_DIALOGUE_MARKER_2 || tb == CTRL_END_BRANCH ||
+						tb == CTRL_ALT_END_MARKER_1) {
+						break;
+					}
+
+					if (b == 0x0A || b == 0x0B || b == 0x00) {
+						debug("Skipping byte 0x%02X at pos %u", b, pos);
+						pos++; // Skip nulls and line feeds
+						continue;
+					}
+
+					if (tb >= 0x20 && tb <= 0x7A) {
+						opt.text += (char)tb;
+					} else {
+						byte decoded = decodeChar(tb);
+						if (decoded != tb || (decoded >= 0x20 && decoded <= 0x83)) {
+							opt.text += (char)decoded;
+						}
+					}
+					textPos++;
+				}
+
+				outChoices.push_back(opt);
+				choiceCount = 1;
+				pos++;
+				break;
+			}
+		}
+
+		pos++;
+	}
+
+	// Scan for additional choices with SAME index
+	while (pos < dataSize) {
+		byte b = data[pos];
+
+		// Stop at end markers
+		if (b == CTRL_ALT_END_MARKER_1 || b == CTRL_END_BRANCH || b == CTRL_ALT_END_MARKER_3) {
+			break;
+		}
+
+		// Found a dialogue marker
+		if (b == CTRL_DIALOGUE_MARKER || b == CTRL_DIALOGUE_MARKER_2) {
+			if (pos + 1 < dataSize) {
+				int choiceIndex = data[pos + 1];
+
+				// Only collect choices with same index
+				if (choiceIndex == firstChoiceIndex) {
+					// Check if disabled
+					bool isDisabled = (b == CTRL_DISABLED_CHOICE);
+
+					ChoiceOption opt;
+					opt.index = choiceIndex;
+					opt.dataOffset = pos;
+					opt.isDisabled = isDisabled;
+
+					// Parse the choice text
+					uint32 textPos = pos + 4;
+					while (textPos < dataSize) {
+						byte tb = data[textPos];
+						if (tb == CTRL_END_TEXT || tb == CTRL_DIALOGUE_MARKER ||
+							tb == CTRL_DIALOGUE_MARKER_2 || tb == CTRL_END_BRANCH ||
+							tb == CTRL_ALT_END_MARKER_1) {
+							break;
+						}
+
+						if (b == 0x0A || b == 0x0B || b == 0x00) {
+							debug("Skipping byte 0x%02X at pos %u", b, pos);
+							pos++; // Skip nulls and line feeds
+							continue;
+						}
+
+						if (tb >= 0x20 && tb <= 0x7A) {
+							opt.text += (char)tb;
+						} else {
+							byte decoded = decodeChar(tb);
+							if (decoded != tb || (decoded >= 0x20 && decoded <= 0x83)) {
+								opt.text += (char)decoded;
+							}
+						}
+						textPos++;
+					}
+
+					outChoices.push_back(opt);
+					choiceCount++;
+				} else if (choiceIndex < firstChoiceIndex) {
+					// Different choice index - stop scanning
+					break;
+				}
+			}
+		}
+
+		pos++;
+	}
+
+	return pos;
 }
 
-void DialogManager::startConversation(ConversationNode &root) {
+void DialogManager::startConversation(const byte *conversationData, uint32 dataSize, Sprite *animSet) {
+	if (!conversationData || dataSize == 0) {
+		debug("startConversation: No conversation data");
+		return;
+	}
+	_curSprite = animSet;
+
+	debug("Starting conversation with %u bytes of data", dataSize);
+
+	uint32 position = 0;
+
+	// Skip any junk at start until we find a speaker marker or choice marker
+	while (position < dataSize &&
+		   conversationData[position] != CTRL_SPEAKER_ID &&
+		   conversationData[position] != CTRL_DIALOGUE_MARKER &&
+		   conversationData[position] != CTRL_DIALOGUE_MARKER_2) {
+		position++;
+	}
+
+	// OUTER LOOP: Continue until conversation ends
+	while (position < dataSize && !g_engine->shouldQuit()) {
+		// Skip control bytes that should be ignored
+		while (position < dataSize &&
+			   (conversationData[position] == CTRL_ALT_END_MARKER_1 ||
+				conversationData[position] == CTRL_ALT_END_MARKER_2 ||
+				conversationData[position] == CTRL_ALT_END_MARKER_3 ||
+				conversationData[position] == CTRL_TEXT_TERMINATOR ||
+				conversationData[position] == CTRL_GO_BACK)) {
+			position++;
+		}
+
+		if (position >= dataSize) {
+			debug("Reached end of data while skipping control bytes");
+			break;
+		}
+
+		// 1. Read and display current dialogue
+		Common::String text;
+		byte speakerId;
+		uint32 endPos = readTextBlock(conversationData, dataSize, position, text, speakerId);
+
+		// Skip spurious single character artifacts
+		if (!text.empty() && text.size() > 1) {
+			debug("Dialogue: \"%s\" (Speaker ID: %u)", text.c_str(), speakerId);
+			displayDialogue(text, speakerId);
+		}
+
+		// Move to end of text
+		position = endPos;
+
+		// 2. Check for end of conversation
+		if (position >= dataSize) {
+			debug("Reached end of data after reading dialogue");
+			break;
+		}
+
+		byte controlByte = conversationData[position];
+
+		if (controlByte == CTRL_END_CONVERSATION) {
+			debug("End of conversation marker found");
+			break;
+		}
+
+		// Move past control byte
+		if (controlByte == CTRL_END_TEXT || controlByte == CTRL_ACTION_TRIGGER) {
+			position++;
+			if (position >= dataSize) {
+				debug("Reached end of data after moving past control byte");
+				break;
+			}
+		}
+
+		// 3. Before parsing choices, check if we're at a choice marker
+		// Skip control bytes to peek at next meaningful byte
+		uint32 peekPos = position;
+		while (peekPos < dataSize &&
+			   (conversationData[peekPos] == CTRL_ALT_END_MARKER_1 ||
+				conversationData[peekPos] == CTRL_ALT_END_MARKER_2 ||
+				conversationData[peekPos] == CTRL_ALT_END_MARKER_3 ||
+				conversationData[peekPos] == CTRL_TEXT_TERMINATOR ||
+				conversationData[peekPos] == CTRL_GO_BACK)) {
+			peekPos++;
+		}
+
+		// If not at a choice marker, there's more dialogue to read
+		if (peekPos < dataSize &&
+			conversationData[peekPos] != CTRL_DIALOGUE_MARKER &&
+			conversationData[peekPos] != CTRL_DIALOGUE_MARKER_2 &&
+			conversationData[peekPos] != CTRL_END_CONVERSATION) {
+			continue;
+		}
+
+		// 4. Parse choices
+		debug("Parsing choices at pos %u", position);
+		Common::Array<ChoiceOption> choices;
+		parseChoices(conversationData, dataSize, position, choices);
+		debug("Parsed %u choices", choices.size());
+		for (uint i = 0; i < choices.size(); i++) {
+			debug(" Choice %u: \"%s\" (Disabled: %s)", i, choices[i].text.c_str(),
+				  choices[i].isDisabled ? "Yes" : "No");
+		}
+		if (choices.empty()) {
+			// No choices, continue reading dialogue
+			position = peekPos;
+			continue;
+		}
+
+		// 5. Display choices and get selection
+		int selectedIndex = 0;
+
+		// Check if this is auto-dialogue (only one choice)
+		if (choices.size() == 1) {
+			// Auto-dialogue: display it automatically
+			displayDialogue(choices[0].text, ALFRED_COLOR);
+			selectedIndex = 0;
+		} else {
+			// Real choice: show menu and wait for selection
+			Common::Array<Common::String> choiceTexts;
+			for (uint i = 0; i < choices.size(); i++) {
+				if (choices[i].isDisabled) {
+					choiceTexts.push_back("[DISABLED] " + choices[i].text);
+				} else {
+					choiceTexts.push_back(choices[i].text);
+				}
+			}
+			_currentChoices = &choices;
+			// Use displayChoices to show and select
+			selectedIndex = selectChoice(choiceTexts, g_engine->_compositeBuffer);
+		}
+
+		// 6. Move position to after the selected choice
+		if (selectedIndex >= 0 && selectedIndex < (int)choices.size()) {
+			position = choices[selectedIndex].dataOffset;
+
+			// Read and display the selected choice as dialogue
+			Common::String choiceText;
+			byte choiceSpeakerId;
+			endPos = readTextBlock(conversationData, dataSize, position, choiceText, choiceSpeakerId);
+
+			if (!choiceText.empty() && choiceText.size() > 1) {
+				displayDialogue(choiceText, ALFRED_COLOR);
+			}
+
+			position = endPos;
+
+			// Skip past end marker
+			if (position < dataSize) {
+				byte endByte = conversationData[position];
+				if (endByte == CTRL_END_TEXT || endByte == CTRL_END_BRANCH ||
+					endByte == CTRL_ACTION_TRIGGER) {
+					position++;
+				}
+			}
+		}
+	}
 
+	debug("Conversation ended");
+	// Note: The caller should set inConversation = false after this returns
 }
 } // namespace Pelrock
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index ae1b032c04f..816d9f32dab 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -21,22 +21,53 @@
 #ifndef PELROCK_DIALOG_H
 #define PELROCK_DIALOG_H
 
-#include "common/array.h"
 #include "common/scummsys.h"
+#include "graphics/screen.h"
 
-#include "pelrock/pelrock.h"
+#include "pelrock/events.h"
+#include "pelrock/fonts/large_font.h"
+#include "pelrock/fonts/small_font.h"
 #include "pelrock/types.h"
+
 namespace Pelrock {
+/**
+ * Structure to hold a parsed choice option
+ */
+struct ChoiceOption {
+	int index;
+	Common::String text;
+	bool isDisabled;
+	uint32 dataOffset;
+
+	ChoiceOption() : index(-1), isDisabled(false), dataOffset(0) {}
+};
 
-class DialogManager
-{
+class DialogManager {
 private:
-    /* data */
+	Graphics::Screen *_screen = nullptr;
+	PelrockEventManager *_events = nullptr;
+	LargeFont *_largeFont = nullptr;
+	SmallFont *_smallFont = nullptr;
+	Sprite *_curSprite = nullptr;
+
+	// Private helper functions for conversation parsing
+	void displayDialogue(const Common::String &text, byte speakerId);
+	uint32 readTextBlock(const byte *data, uint32 dataSize, uint32 startPos,
+						 Common::String &outText, byte &outSpeakerId);
+	uint32 parseChoices(const byte *data, uint32 dataSize, uint32 startPos, Common::Array<ChoiceOption> &outChoices);
+
+	void checkMouse();
+
 public:
-    DialogManager(/* args */);
-    ~DialogManager();
+	DialogManager(Graphics::Screen *screen, PelrockEventManager *events,
+				  LargeFont *largeFont, SmallFont *smallFont);
+	~DialogManager();
+
+	void displayChoices(Common::Array<ChoiceOption> *choices, byte *compositeBuffer);
+	int selectChoice(Common::Array<Common::String> &choices, byte *compositeBuffer);
+	void startConversation(const byte *conversationData, uint32 dataSize, Sprite *alfredAnimSet = nullptr);
 
-    void startConversation(ConversationNode &root);
+	Common::Array<ChoiceOption> *_currentChoices = nullptr;
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/module.mk b/engines/pelrock/module.mk
index d935e5fa27f..942fcb31b5e 100644
--- a/engines/pelrock/module.mk
+++ b/engines/pelrock/module.mk
@@ -13,7 +13,8 @@ MODULE_OBJS = \
 	sound.o \
 	video/video.o \
 	pathfinding.o \
-	events.o
+	events.o \
+	dialog.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_PELROCK), DYNAMIC_PLUGIN)
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index fc471ab0c36..d9a7c8da098 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -20,7 +20,6 @@
  */
 
 #include "common/config-manager.h"
-#include "common/debug-channels.h"
 #include "common/endian.h"
 #include "common/events.h"
 #include "common/file.h"
@@ -77,7 +76,7 @@ Common::String PelrockEngine::getGameId() const {
 	return _gameDescription->gameId;
 }
 
-Common::Array<Common::Array<Common::String>> wordWrap(Common::String text);
+// Common::Array<Common::Array<Common::String>> wordWrap(Common::String text);
 
 Common::Error PelrockEngine::run() {
 	// Initialize 320x200 paletted graphics mode
@@ -87,6 +86,7 @@ Common::Error PelrockEngine::run() {
 	_room = new RoomManager();
 	_res = new ResourceManager();
 	_sound = new SoundManager(_mixer);
+	_dialog = new DialogManager(_screen, _events, _largeFont, _smallFont);
 
 	// Set the engine's debugger console
 	setDebugger(new PelrockConsole(this));
@@ -154,20 +154,6 @@ void PelrockEngine::loadAnims() {
 	_res->loadAlfredAnims();
 }
 
-void PelrockEngine::displayChoices(Common::Array<Common::String> choices, byte *compositeBuffer) {
-	int overlayHeight = choices.size() * kChoiceHeight + 2;
-	int overlayY = 400 - overlayHeight;
-	for (int x = 0; x < 640; x++) {
-		for (int y = overlayY; y < 400; y++) {
-			int index = y * 640 + x;
-			compositeBuffer[index] = _room->overlayRemap[compositeBuffer[index]];
-		}
-	}
-	for (int i = 0; i < choices.size(); i++) {
-		drawText(choices[i], 10, overlayY + 2 + i * kChoiceHeight, 620, 15);
-	}
-}
-
 byte *PelrockEngine::grabBackgroundSlice(int x, int y, int w, int h) {
 	byte *bg = new byte[w * h];
 	for (int j = 0; j < w; j++) {
@@ -240,13 +226,13 @@ void PelrockEngine::playSoundIfNeeded() {
 
 	int soundIndex = _sound->tick(_chrono->getFrameCount());
 	if (soundIndex >= 0 && soundIndex < _room->_roomSfx.size()) {
-		debug("Playing SFX index %d", soundIndex);
 		_sound->playSound(_room->_roomSfx[3 + soundIndex]);
 	}
 }
 
 void PelrockEngine::renderScene(bool showTextOverlay) {
 
+	_chrono->updateChrono();
 	if (_chrono->_gameTick) {
 		playSoundIfNeeded();
 
@@ -254,11 +240,9 @@ void PelrockEngine::renderScene(bool showTextOverlay) {
 		updateAnimations();
 
 		if (showTextOverlay) {
-			displayChoices(_currentTextPages[_currentTextPageIndex], _compositeBuffer);
+			_dialog->displayChoices(_dialog->_currentChoices, _compositeBuffer);
 		}
 
-		checkMouse();
-
 		presentFrame();
 		updatePaletteAnimations();
 
@@ -288,13 +272,11 @@ void PelrockEngine::renderScene(bool showTextOverlay) {
 		// }
 
 		// if (alfredState.animState == ALFRED_IDLE && alfredState.nextState != ALFRED_IDLE) {
-		// 	// debug("Switching Alfred state from IDLE to %d", alfredState.nextState);
 		// 	alfredState.animState = alfredState.nextState;
 		// 	alfredState.nextState = ALFRED_IDLE;
 		// 	alfredState.curFrame = 0;
 		// }
 
-		// debug("Drawing walkboxes..., %d, _currentRoomWalkboxes.size()=%d",  _currentRoomWalkboxes.size(), _currentRoomWalkboxes.size());
 
 		_screen->markAllDirty();
 	}
@@ -311,7 +293,6 @@ void PelrockEngine::checkMouse() {
 		_events->_longClicked = false;
 	}
 	else if(_events->_rightMouseClicked) {
-		debug("Right mouse clicked - entering settings menu");
 		g_system->getPaletteManager()->setPalette(_res->_mainMenuPalette, 0, 256);
 		_events->_rightMouseClicked = false;
 		stateGame = SETTINGS;
@@ -321,7 +302,6 @@ void PelrockEngine::checkMouse() {
 //else if (e.type == Common::EVENT_MOUSEMOVE) {
 	// 		mouseX = e.mouse.x;
 	// 		mouseY = e.mouse.y;
-	// 		// debug(3, "Mouse moved to (%d,%d)", mouseX, mouseY);
 	// 	} else if (e.type == Common::EVENT_LBUTTONDOWN) {
 	// 		if (!_isMouseDown) {
 	// 			_mouseClickTime = g_system->getMillis();
@@ -339,7 +319,6 @@ void PelrockEngine::checkMouse() {
 	// }
 	// if (_isMouseDown) {
 	// 	if (g_system->getMillis() - _mouseClickTime >= kLongClickDuration) {
-	// 		debug("long click!");
 	// 		_longClick = true;
 	// 		_isMouseDown = false;
 	// 		checkLongMouseClick(e.mouse.x, e.mouse.y);
@@ -406,7 +385,6 @@ void PelrockEngine::updatePaletteAnimations() {
 
 void PelrockEngine::paintDebugLayer() {
 	for (int i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
-		// debug("Drawing walkbox %d", i);
 		WalkBox box = _room->_currentRoomWalkboxes[i];
 		drawRect(_screen, box.x, box.y, box.w, box.h, 150 + i);
 		_smallFont->drawString(_screen, Common::String::format("%d", i), box.x + 2, box.y + 2, 640, 14);
@@ -506,38 +484,20 @@ void PelrockEngine::doAction(byte action, HotSpot *hotspot) {
 }
 
 void PelrockEngine::talkTo(HotSpot *hotspot) {
-	debug("Talking to object %d", hotspot->index);
-	if (_room->_currentRoomConversations.size() == 0)
-		return;
-
 	Sprite *animSet;
 	for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
 		if (i == hotspot->index) {
 			animSet = &_room->_currentRoomAnims[i];
+			break;
 		}
 	}
-
-	ConversationNode selectedNode = _room->_currentRoomConversations[0];
-
-	bool isNPC = selectedNode.speakerId != 13;
-	if (isNPC) {
-		sayNPC(animSet, selectedNode.text, selectedNode.speakerId);
-	}
-	// for(int i= 0; i< _currentRoomConversations.size(); i++) {
-	// _currentRoomConversations
-	// }
-
-	// showDescription(_currentRoomConversations[0].text, x, y, _currentRoomConversations[0].speakerId);
-	// for(int i = 0; i < _currentRoomConversations[0].choices.size(); i++) {
-	// 	int idx = _currentRoomConversations.size() - 1 - i;
-	// 	_smallFont->drawString(_screen, _currentRoomConversations[0].choices[idx].text.c_str(), 0, 400 - ((i + 1) * 12), 640, 14);
-	// }
+	debug("Starting conversation with hotspot %d, animSet pos %d", hotspot->index, animSet->x);
+	_dialog->startConversation(_room->_conversationData, _room->_conversationDataSize, animSet);
 }
 
 void PelrockEngine::lookAt(HotSpot *hotspot) {
-	debug("Look action clicked");
 	walkTo(_currentHotspot->x, _currentHotspot->y);
-	sayAlfred(_room->_currentRoomDescriptions[_currentHotspot->index].text);
+	// sayAlfred(_room->_currentRoomDescriptions[_currentHotspot->index].text);
 	_displayPopup = false;
 }
 
@@ -565,7 +525,7 @@ void PelrockEngine::renderText(Common::Array<Common::String> lines, int color, i
 	for (size_t i = 0; i < lines.size(); i++) {
 		int textX = baseX - (maxW / 2);
 		int textY = baseY - (lineSize * 25) + (i * 25);
-		drawText(lines[i], textX, textY, maxW, color);
+		drawText(_largeFont, lines[i], textX, textY, maxW, color);
 	}
 }
 
@@ -652,7 +612,6 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		alfredState.curFrame++;
 		break;
 	default:
-		// debug("Drawing Alfred idle frame for direction %d", alfredState.direction);
 		drawAlfred(_res->alfredIdle[alfredState.direction]);
 		break;
 	}
@@ -678,10 +637,8 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	if (scaleIndex < 0) {
 		scaleIndex = 0;
 	}
-	// debug("Scaling Alfred frame to final size (%d x %d) from scale factor %.2f", finalWidth, finalHeight, scaleFactor);
 	int linesToSkip = kAlfredFrameHeight - finalHeight;
 
-	// debug("lines to skip = %d, finalHeight = %d, finalWidth = %d for position (%d, %d)", linesToSkip, finalHeight, finalWidth, xAlfred, yAlfred);
 
 	int shadowPos = alfredState.y; // - finalHeight;
 	bool shadeCharacter = _room->_pixelsShadows[shadowPos * 640 + alfredState.x] != 0xFF;
@@ -696,7 +653,6 @@ void PelrockEngine::drawAlfred(byte *buf) {
 			idealSkipPositions.push_back(idealPos);
 		}
 
-		// debug("Height scaling table size =%d", _heightScalingTable.size());
 		Common::Array<int> tableSkipPositions;
 		for (int scanline = 0; scanline < kAlfredFrameHeight; scanline++) {
 			if (_heightScalingTable[scaleIndex][scanline] != 0) {
@@ -704,9 +660,7 @@ void PelrockEngine::drawAlfred(byte *buf) {
 			}
 		}
 
-		// debug("Table skip positions:");
 		// for (size_t i = 0; i < tableSkipPositions.size(); i++) {
-		// 	debug("  %d", tableSkipPositions[i]);
 		// }
 
 		Common::Array<int> skipTheseLines;
@@ -748,7 +702,6 @@ void PelrockEngine::drawAlfred(byte *buf) {
 					int srcIndex = srcY * kAlfredFrameWidth + srcX;
 					int outIndex = outY * finalWidth + outX;
 					if (outIndex >= finalWidth * finalHeight || srcIndex >= kAlfredFrameWidth * kAlfredFrameHeight) {
-						debug("Index out of bounds!");
 					} else
 						finalBuf[outIndex] = buf[srcIndex];
 				}
@@ -869,7 +822,6 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 		_displayPopup = true;
 		_currentPopupFrame = 0;
 		_currentHotspot = &_room->_currentRoomHotspots[hotspotIndex];
-		debug("Current hotspot (x=%d, y=%d) with extra = %d type: %d, desc= %s", _currentHotspot->x, _currentHotspot->y, _currentHotspot->extra, _currentHotspot->type, _room->_currentRoomDescriptions[_currentHotspot->index].text.c_str());
 	}
 }
 
@@ -881,7 +833,6 @@ void PelrockEngine::checkMouseClickOnSettings(int x, int y) {
 			y >= 115 - (8 * i) && y <= 115 - (8 * i) + 64) {
 			selectedInvIndex = curInventoryPage * 4 + i;
 			_menuText = _res->getInventoryObject(selectedInvIndex).description;
-			debug("Selected inventory index: %d", selectedInvIndex);
 			selectedItem = true;
 			return;
 		}
@@ -1080,19 +1031,11 @@ void PelrockEngine::drawTalkNPC(Sprite *animSet) {
 		curFrame = 0;
 	}
 	byte *frame = index ? animHeader->animB[curFrame] : animHeader->animA[curFrame];
-	// debug("Talking NPC frame %d/%d, x=%d, y=%d, w=%d, h=%d", curFrame, numFrames, x, y, w, h);
 
 	drawSpriteToBuffer(_compositeBuffer, 640, frame, x, y, w, h, 255);
 }
 
-void PelrockEngine::conversationLoop() {
-	while (inConversation) {
-
-	}
-}
-
 void PelrockEngine::gameLoop() {
-	_chrono->updateChrono();
 	_events->pollEvent();
 	// while (g_system->getEventManager()->pollEvent(e)) {
 	// 	if (e.type == Common::EVENT_KEYDOWN) {
@@ -1115,46 +1058,18 @@ void PelrockEngine::gameLoop() {
 	// 		case Common::KEYCODE_z:
 	// 			showShadows = !showShadows;
 	// 			break;
-	// 		case Common::KEYCODE_y:
-	// 			alfredState.x = 193;
-	// 			alfredState.y = 382;
-	// 			walkTo(377, 318);
-	// 			break;
 	// 		default:
 	// 			break;
 	// 		}
-	// 	} else if (e.type == Common::EVENT_MOUSEMOVE) {
-	// 		mouseX = e.mouse.x;
-	// 		mouseY = e.mouse.y;
-	// 		// debug(3, "Mouse moved to (%d,%d)", mouseX, mouseY);
-	// 	} else if (e.type == Common::EVENT_LBUTTONDOWN) {
-	// 		if (!_isMouseDown) {
-	// 			_mouseClickTime = g_system->getMillis();
-	// 			_isMouseDown = true;
-	// 		}
-	// 	} else if (e.type == Common::EVENT_LBUTTONUP) {
-	// 		_isMouseDown = false;
-	// 		checkMouseClick(e.mouse.x, e.mouse.y);
-	// 		_displayPopup = false;
-	// 		_longClick = false;
-	// 	} else if (e.type == Common::EVENT_RBUTTONUP) {
-	// 		g_system->getPaletteManager()->setPalette(_res->_mainMenuPalette, 0, 256);
-	// 		stateGame = SETTINGS;
-	// 	}
-	// }
-	// if (_isMouseDown) {
-	// 	if (g_system->getMillis() - _mouseClickTime >= kLongClickDuration) {
-	// 		debug("long click!");
-	// 		_longClick = true;
-	// 		_isMouseDown = false;
-	// 		checkLongMouseClick(e.mouse.x, e.mouse.y);
 	// 	}
-	// }
-	// checkMouseHover();
 
 	if (inConversation) {
-		conversationLoop();
+		// TODO: Pass actual conversation data from room
+		// For now, using nullptr to disable - actual data needs to be loaded
+		_dialog->startConversation(nullptr, 0);
+		inConversation = false;
 	} else {
+		checkMouse();
 		renderScene();
 	}
 }
@@ -1232,7 +1147,6 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 		// Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
 		VerbIcon actionClicked = isActionUnder(x, y);
 		if (actionClicked != NO_ACTION) {
-			debug("Action %d clicked", actionClicked);
 			doAction(actionClicked, _currentHotspot);
 			_displayPopup = false;
 			return;
@@ -1252,7 +1166,6 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 			alfredState.x = exit->targetX;
 			alfredState.y = exit->targetY;
 
-			debug("Placing character at %d, %d", exit->targetX, exit->targetY);
 			setScreen(exit->targetRoom, exit->dir);
 		} else {
 			walkTo(walkTarget.x, walkTarget.y);
@@ -1304,235 +1217,141 @@ void PelrockEngine::checkMouseHover() {
 	}
 }
 
-// Common::Point PelrockEngine::calculateWalkTarget(int mouseX, int mouseY) {
-// 	// Starting point for pathfinding
-// 	int sourceX = mouseX;
-// 	int sourceY = mouseY;
-
-// 	// TODO: If hovering over a sprite/hotspot, adjust source point to sprite center
-// 	// For now, just use mouse position
-
-// 	// Find nearest walkable point in walkboxes
-// 	uint32 minDistance = 0xFFFFFFFF;
-// 	Common::Point bestTarget(sourceX, sourceY);
-
-// 	// for (Common::List<WalkBox>::iterator it = _currentRoomWalkboxes.begin();
-// 	//  it != _currentRoomWalkboxes.end(); ++it) {
-// 	for (size_t i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
+// void PelrockEngine::sayNPC(Sprite *anim, Common::String text, byte color) {
+// 	isNPCATalking = true;
+// 	whichNPCTalking = anim->extra;
+// 	_currentTextPages = wordWrap(text);
+// 	_textColor = color;
+// 	int totalChars = 0;
+// 	for (int i = 0; i < _currentTextPages[0].size(); i++) {
+// 		totalChars += _currentTextPages[0][i].size();
+// 	}
+// 	_textPos = Common::Point(anim->x, anim->y - 10);
+// 	_textDurationFrames = totalChars / 2;
+// }
 
-// 		// Calculate distance from source point to this walkbox (Manhattan distance)
-// 		int dx = 0;
-// 		int dy = 0;
+// void PelrockEngine::sayAlfred(Common::String text) {
+// 	alfredState.nextState = ALFRED_TALKING;
+// 	alfredState.curFrame = 0;
+// 	_currentTextPages = wordWrap(text);
+// 	_textColor = 13;
+// 	int totalChars = 0;
+// 	for (int i = 0; i < _currentTextPages[0].size(); i++) {
+// 		totalChars += _currentTextPages[0][i].size();
+// 	}
+// 	_textDurationFrames = totalChars / 2;
+// }
 
-// 		// Calculate horizontal distance
-// 		if (sourceX < _room->_currentRoomWalkboxes[i].x) {
-// 			dx = _room->_currentRoomWalkboxes[i].x - sourceX;
-// 		} else if (sourceX > _room->_currentRoomWalkboxes[i].x + _room->_currentRoomWalkboxes[i].w) {
-// 			dx = sourceX - (_room->_currentRoomWalkboxes[i].x + _room->_currentRoomWalkboxes[i].w);
+// int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
+// 	// return word_length, is_end
+// 	int wordLength = 0;
+// 	int pos = startPos;
+// 	while (pos < text.size()) {
+// 		char char_byte = text[pos];
+// 		if (char_byte == CHAR_SPACE || isEndMarker(char_byte)) {
+// 			break;
 // 		}
-// 		// else: sourceX is inside walkbox horizontally, dx = 0
-
-// 		// Calculate vertical distance
-// 		if (sourceY < _room->_currentRoomWalkboxes[i].y) {
-// 			dy = _room->_currentRoomWalkboxes[i].y - sourceY;
-// 		} else if (sourceY > _room->_currentRoomWalkboxes[i].y + _room->_currentRoomWalkboxes[i].h) {
-// 			dy = sourceY - (_room->_currentRoomWalkboxes[i].y + _room->_currentRoomWalkboxes[i].h);
+// 		wordLength++;
+// 		pos++;
+// 	}
+// 	// Check if we hit an end marker
+// 	if (pos < text.size() && isEndMarker(text[pos])) {
+// 		isEnd = true;
+// 	}
+// 	// Count ALL trailing spaces as part of this word
+// 	if (pos < text.size() && !isEnd) {
+// 		if (text[pos] == CHAR_END_MARKER_3) { // 0xF8 (-8) special case
+// 			wordLength += 3;
+// 		} else {
+// 			// Count all consecutive spaces
+// 			while (pos < text.size() && text[pos] == CHAR_SPACE) {
+// 				wordLength++;
+// 				pos++;
+// 			}
 // 		}
-// 		// else: sourceY is inside walkbox vertically, dy = 0
-
-// 		uint32 distance = dx + dy;
-
-// 		if (distance < minDistance) {
-// 			minDistance = distance;
-
-// 			// Calculate target point (nearest point on walkbox to source)
-// 			int targetX = sourceX;
-// 			int targetY = sourceY;
+// 	}
+// 	return wordLength;
+// }
 
-// 			if (sourceX < _room->_currentRoomWalkboxes[i].x) {
-// 				targetX = _room->_currentRoomWalkboxes[i].x;
-// 			} else if (sourceX > _room->_currentRoomWalkboxes[i].x + _room->_currentRoomWalkboxes[i].w) {
-// 				targetX = _room->_currentRoomWalkboxes[i].x + _room->_currentRoomWalkboxes[i].w;
+// Common::Array<Common::Array<Common::String>> wordWrap(Common::String text) {
+
+// 	Common::Array<Common::Array<Common::String>> pages;
+// 	Common::Array<Common::String> currentPage;
+// 	Common::Array<Common::String> currentLine;
+// 	int charsRemaining = MAX_CHARS_PER_LINE;
+// 	int position = 0;
+// 	int currentLineNum = 0;
+// 	while (position < text.size()) {
+// 		bool isEnd = false;
+// 		int wordLength = calculateWordLength(text, position, isEnd);
+// 		// # Extract the word (including trailing spaces)
+// 		// word = text[position:position + word_length].decode('latin-1', errors='replace')
+// 		Common::String word = text.substr(position, wordLength).decode(Common::kLatin1);
+// 		// # Key decision: if word_length > chars_remaining, wrap to next line
+// 		if (wordLength > charsRemaining) {
+// 			// Word is longer than the entire line - need to split
+// 			currentPage.push_back(joinStrings(currentLine, ""));
+// 			currentLine.clear();
+// 			charsRemaining = MAX_CHARS_PER_LINE;
+// 			currentLineNum++;
+
+// 			if (currentLineNum >= MAX_LINES) {
+// 				pages.push_back(currentPage);
+// 				currentPage.clear();
+// 				currentLineNum = 0;
 // 			}
-
-// 			if (sourceY < _room->_currentRoomWalkboxes[i].y) {
-// 				targetY = _room->_currentRoomWalkboxes[i].y;
-// 			} else if (sourceY > _room->_currentRoomWalkboxes[i].y + _room->_currentRoomWalkboxes[i].h) {
-// 				targetY = _room->_currentRoomWalkboxes[i].y + _room->_currentRoomWalkboxes[i].h;
+// 		}
+// 		// Add word to current line
+// 		currentLine.push_back(word);
+// 		charsRemaining -= wordLength;
+
+// 		if (charsRemaining == 0 && isEnd) {
+// 			Common::String lineText = joinStrings(currentLine, "");
+// 			while (lineText.lastChar() == CHAR_SPACE) {
+// 				lineText = lineText.substr(0, lineText.size() - 1);
+// 			}
+// 			int trailingSpaces = currentLine.size() - lineText.size();
+// 			if (trailingSpaces > 0) {
+// 				currentPage.push_back(lineText);
+// 				//  current_line = [' ' * trailing_spaces]
+// 				Common::String currentLine(trailingSpaces, ' ');
+// 				charsRemaining = MAX_CHARS_PER_LINE - trailingSpaces;
+// 				currentLineNum += 1;
+
+// 				if (currentLineNum >= MAX_LINES) {
+// 					pages.push_back(currentPage);
+// 					currentPage.clear();
+// 					currentLineNum = 0;
+// 				}
 // 			}
+// 		}
 
-// 			bestTarget.x = targetX;
-// 			bestTarget.y = targetY;
+// 		position += wordLength;
+// 		if (isEnd) {
+// 			// End of sentence/paragraph/page
+// 			break;
 // 		}
 // 	}
-
-// 	return bestTarget;
+// 	if (currentLine.empty() == false) {
+// 		Common::String lineText = joinStrings(currentLine, "");
+// 		while (lineText.lastChar() == CHAR_SPACE) {
+// 			lineText = lineText.substr(0, lineText.size() - 1);
+// 		}
+// 		currentPage.push_back(lineText);
+// 	}
+// 	if (currentPage.empty() == false) {
+// 		pages.push_back(currentPage);
+// 	}
+// 	for (int i = 0; i < pages.size(); i++) {
+// 		for (int j = 0; j < pages[i].size(); j++) {
+// 		}
+// 	}
+// 	return pages;
 // }
 
-void PelrockEngine::drawText(Common::String text, int x, int y, int w, byte color) {
-	Common::Rect rect = _largeFont->getBoundingBox(text.c_str());
-	if (x + rect.width() > 640) {
-		x = 640 - rect.width() - 2;
-	}
-	if (y + rect.height() > 400) {
-		y = 400 - rect.height();
-	}
-	if (x < 0) {
-		x = 0;
-	}
-	if (y < 0) {
-		y = 0;
-	}
-	// Draw main text on top
-	_largeFont->drawString(_screen, text.c_str(), x, y, w, color, Graphics::kTextAlignCenter);
-}
-
-void PelrockEngine::sayNPC(Sprite *anim, Common::String text, byte color) {
-	isNPCATalking = true;
-	whichNPCTalking = anim->extra;
-	debug("NPC says %s, color = %d", text.c_str(), color);
-	_currentTextPages = wordWrap(text);
-	_textColor = color;
-	int totalChars = 0;
-	for (int i = 0; i < _currentTextPages[0].size(); i++) {
-		totalChars += _currentTextPages[0][i].size();
-	}
-	debug("Settijng textpos to %d, %d", anim->x, anim->y - 10);
-	_textPos = Common::Point(anim->x, anim->y - 10);
-	_textDurationFrames = totalChars / 2;
-}
-
-void PelrockEngine::sayAlfred(Common::String text) {
-	alfredState.nextState = ALFRED_TALKING;
-	alfredState.curFrame = 0;
-	debug("Alfred says: %s", text.c_str());
-	_currentTextPages = wordWrap(text);
-	_textColor = 13;
-	int totalChars = 0;
-	for (int i = 0; i < _currentTextPages[0].size(); i++) {
-		totalChars += _currentTextPages[0][i].size();
-	}
-	_textDurationFrames = totalChars / 2;
-}
-
-bool isEndMarker(char char_byte) {
-	return char_byte == CHAR_END_MARKER_1 || char_byte == CHAR_END_MARKER_2 || char_byte == CHAR_END_MARKER_3 || char_byte == CHAR_END_MARKER_4;
-}
-
-int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
-	// return word_length, is_end
-	int wordLength = 0;
-	int pos = startPos;
-	while (pos < text.size()) {
-		char char_byte = text[pos];
-		if (char_byte == CHAR_SPACE || isEndMarker(char_byte)) {
-			break;
-		}
-		wordLength++;
-		pos++;
-	}
-	// Check if we hit an end marker
-	if (pos < text.size() && isEndMarker(text[pos])) {
-		isEnd = true;
-	}
-	// Count ALL trailing spaces as part of this word
-	if (pos < text.size() && !isEnd) {
-		if (text[pos] == CHAR_END_MARKER_3) { // 0xF8 (-8) special case
-			wordLength += 3;
-		} else {
-			// Count all consecutive spaces
-			while (pos < text.size() && text[pos] == CHAR_SPACE) {
-				wordLength++;
-				pos++;
-			}
-		}
-	}
-	return wordLength;
-}
-
-Common::Array<Common::Array<Common::String>> wordWrap(Common::String text) {
-
-	Common::Array<Common::Array<Common::String>> pages;
-	Common::Array<Common::String> currentPage;
-	Common::Array<Common::String> currentLine;
-	int charsRemaining = MAX_CHARS_PER_LINE;
-	int position = 0;
-	int currentLineNum = 0;
-	while (position < text.size()) {
-		bool isEnd = false;
-		int wordLength = calculateWordLength(text, position, isEnd);
-		// # Extract the word (including trailing spaces)
-		// word = text[position:position + word_length].decode('latin-1', errors='replace')
-		Common::String word = text.substr(position, wordLength).decode(Common::kLatin1);
-		// # Key decision: if word_length > chars_remaining, wrap to next line
-		if (wordLength > charsRemaining) {
-			// Word is longer than the entire line - need to split
-			currentPage.push_back(joinStrings(currentLine, ""));
-			currentLine.clear();
-			charsRemaining = MAX_CHARS_PER_LINE;
-			currentLineNum++;
-
-			if (currentLineNum >= MAX_LINES) {
-				pages.push_back(currentPage);
-				currentPage.clear();
-				currentLineNum = 0;
-			}
-		}
-		// Add word to current line
-		currentLine.push_back(word);
-		charsRemaining -= wordLength;
-
-		if (charsRemaining == 0 && isEnd) {
-			Common::String lineText = joinStrings(currentLine, "");
-			while (lineText.lastChar() == CHAR_SPACE) {
-				lineText = lineText.substr(0, lineText.size() - 1);
-			}
-			int trailingSpaces = currentLine.size() - lineText.size();
-			if (trailingSpaces > 0) {
-				currentPage.push_back(lineText);
-				//  current_line = [' ' * trailing_spaces]
-				Common::String currentLine(trailingSpaces, ' ');
-				charsRemaining = MAX_CHARS_PER_LINE - trailingSpaces;
-				currentLineNum += 1;
-
-				if (currentLineNum >= MAX_LINES) {
-					pages.push_back(currentPage);
-					currentPage.clear();
-					currentLineNum = 0;
-				}
-			}
-		}
-
-		position += wordLength;
-		if (isEnd) {
-			// End of sentence/paragraph/page
-			break;
-		}
-	}
-	if (currentLine.empty() == false) {
-		Common::String lineText = joinStrings(currentLine, "");
-		while (lineText.lastChar() == CHAR_SPACE) {
-			lineText = lineText.substr(0, lineText.size() - 1);
-		}
-		currentPage.push_back(lineText);
-	}
-	if (currentPage.empty() == false) {
-		pages.push_back(currentPage);
-	}
-	debug("Word wrap produced %d pages", pages.size());
-	for (int i = 0; i < pages.size(); i++) {
-		debug(" Page %d:", i);
-		for (int j = 0; j < pages[i].size(); j++) {
-			debug("   Line %d: %s", j, pages[i][j].c_str());
-		}
-	}
-	return pages;
-}
-
 void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 	_sound->stopAllSounds();
 	Common::File roomFile;
-	debug("Loading room %s number %d", _room->getRoomName(number).c_str(), number);
 	if (!roomFile.open(Common::Path("ALFRED.1"))) {
 		error("Could not open ALFRED.1");
 		return;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index b19d94498c1..8fdf33ee2a4 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -38,6 +38,7 @@
 
 #include "pelrock/chrono.h"
 #include "pelrock/detection.h"
+#include "pelrock/dialog.h"
 #include "pelrock/events.h"
 #include "pelrock/fonts/large_font.h"
 #include "pelrock/fonts/small_font.h"
@@ -55,12 +56,12 @@ class PelrockEngine : public Engine {
 private:
 	const ADGameDescription *_gameDescription;
 	Common::RandomSource _randomSource;
-	RoomManager *_room = nullptr;
 	ResourceManager *_res = nullptr;
 	ChronoManager *_chrono = nullptr;
 	VideoManager *_videoManager = nullptr;
 	SoundManager *_sound = nullptr;
 	PelrockEventManager *_events = nullptr;
+	DialogManager *_dialog = nullptr;
 
 	void init();
 	void loadAnims();
@@ -71,7 +72,6 @@ private:
 	void walkTo(int x, int y);
 
 	void talk(byte object);
-	void displayChoices(Common::Array<Common::String> choices, byte *compositeBuffer);
 	void sayAlfred(Common::String text);
 	void sayNPC(Sprite *anim, Common::String text, byte color);
 
@@ -86,9 +86,6 @@ private:
 	Sprite *isSpriteUnder(int x, int y);
 	void showActionBalloon(int posx, int posy, int curFrame);
 
-	void drawText(Common::String text, int x, int y, int w, byte color);
-
-	void renderScene(bool showTextOverlay = false);
 	void checkMouse();
 	void copyBackgroundToBuffer();
 	void updateAnimations();
@@ -111,7 +108,6 @@ private:
 	void drawTalkNPC(Sprite *animSet);
 	void playSoundIfNeeded();
 
-	void conversationLoop();
 	void gameLoop();
 	void menuLoop();
 
@@ -142,7 +138,6 @@ private:
 	bool isAlkfredWalking = false;
 
 	byte *_currentBackground = nullptr; // Clean background - NEVER modified
-	byte *_compositeBuffer;             // Working composition buffer
 
 	bool _displayPopup = false;
 	byte _iconBlink = 0;
@@ -152,8 +147,6 @@ private:
 
 	HotSpot *_currentHotspot = nullptr;
 
-	SmallFont *_smallFont = nullptr;
-	LargeFont *_largeFont = nullptr;
 
 	Common::Point _curWalkTarget;
 	bool isNPCATalking = false;
@@ -187,7 +180,13 @@ protected:
 
 public:
 	Graphics::Screen *_screen = nullptr;
+	RoomManager *_room = nullptr;
 	AlfredState alfredState;
+	byte *_compositeBuffer;             // Working composition buffer
+
+	SmallFont *_smallFont = nullptr;
+	LargeFont *_largeFont = nullptr;
+	void renderScene(bool showTextOverlay = false);
 
 public:
 	PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 103d4189ee5..a705bd009b5 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -218,17 +218,7 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	uint32_t outPos = 0;
 	_currentRoomStickers.clear();
 	int roomOffset = roomNumber * kRoomStructSize;
-	Common::Array<Description> descriptions = loadRoomDescriptions(roomFile, roomOffset, outPos);
-	// debug("After decsriptions, position is %d", outPos);
-	Common::Array<ConversationNode> roots = loadConversations(roomFile, roomOffset, outPos);
-	for (int i = 0; i < roots.size(); i++) {
-		if (roots[i].text.empty()) {
-			continue;
-		}
-		// debug("Conversation %d: %s", i, roots[i].text.c_str());
-	}
-	_currentRoomConversations = roots;
-
+	Common::Array<Description> descriptions = loadRoomTexts(roomFile, roomOffset);
 	Common::Array<Sprite> anims = loadRoomAnimations(roomFile, roomOffset);
 
 	Common::Array<HotSpot> hotspots;
@@ -407,7 +397,7 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(Common::File *roomFile, int ro
 	return walkboxes;
 }
 
-Common::Array<Description> RoomManager::loadRoomDescriptions(Common::File *roomFile, int roomOffset, uint32_t &outPos) {
+Common::Array<Description> RoomManager::loadRoomTexts(Common::File *roomFile, int roomOffset) {
 	uint32_t pair12_offset_pos = roomOffset + (12 * 8);
 	roomFile->seek(pair12_offset_pos, SEEK_SET);
 	// roomFile->skip(4);
@@ -448,7 +438,14 @@ Common::Array<Description> RoomManager::loadRoomDescriptions(Common::File *roomF
 		pos++;
 	}
 	// debug("End of descriptions at position %d", pos);
-	outPos = lastDescPos + 1;
+	size_t conversationStart = lastDescPos + 1;
+	_conversationDataSize = pair12_size - conversationStart;
+	if(_conversationData != nullptr) {
+		delete[] _conversationData;
+	}
+	_conversationData = new byte[_conversationDataSize];
+	Common::copy(data + conversationStart, data + conversationStart + _conversationDataSize, _conversationData);
+
 	delete[] data;
 	// for (Common::List<Common::String>::iterator i = descriptions.begin(); i != descriptions.end(); i++) {
 	// 	debug("Room description: %s", i->c_str());
@@ -530,361 +527,6 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 	talkFile.close();
 }
 
-Common::String RoomManager::getControlName(byte b) {
-	switch (b) {
-	case 0xFD:
-		return "END_LINE";
-	case 0xFC:
-		return "TEXT_TERM";
-	case 0xFB:
-		return "CHOICE";
-	case 0xFA:
-		return "SKIP";
-	case 0xF9:
-		return "PAGE_BREAK";
-	case 0xF8:
-		return "ACTION";
-	case 0xF7:
-		return "END_BRANCH";
-	case 0xF6:
-		return "LINE_CONT";
-	case 0xF5:
-		return "END_BRANCH_2";
-	case 0xF4:
-		return "END_CONV";
-	case 0xF1:
-		return "CHOICE_ALT";
-	case 0xF0:
-		return "GO_BACK";
-	case 0xFE:
-		return "END_BRANCH_3";
-	case 0xEB:
-		return "END_ALT";
-	case 0xFF:
-		return "DESC_START";
-	case 0x08:
-		return "SPEAKER";
-	default:
-		return Common::String::format("UNKNOWN(0x%02X)", b);
-	}
-}
-
-Common::String RoomManager::cleanText(const Common::String &text) {
-	Common::String cleaned = text;
-
-	// Trim leading/trailing whitespace
-	while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
-		cleaned.deleteChar(0);
-	}
-	while (!cleaned.empty() && Common::isSpace(cleaned.lastChar())) {
-		cleaned.deleteLastChar();
-	}
-
-	// Remove leading [XX][00] patterns
-	while (!cleaned.empty() && cleaned.contains('[')) {
-		uint idx = 0;
-		for (uint i = 0; i < cleaned.size() && i < 15; i++) {
-			if (cleaned[i] == '[') {
-				idx = i;
-				break;
-			}
-		}
-
-		if (idx < 10) {
-			int endIdx = -1;
-			for (uint i = idx; i < cleaned.size() && i < idx + 10; i++) {
-				if (cleaned[i] == ']') {
-					endIdx = i;
-					break;
-				}
-			}
-
-			if (endIdx > (int)idx && endIdx < (int)idx + 10) {
-				cleaned = cleaned.c_str() + endIdx + 1;
-				// Trim leading whitespace again
-				while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
-					cleaned.deleteChar(0);
-				}
-			} else {
-				break;
-			}
-		} else {
-			break;
-		}
-	}
-
-	// Remove single leading control characters
-	if (cleaned.size() > 1) {
-		byte first = (byte)cleaned[0];
-		byte second = (byte)cleaned[1];
-
-		if ((first == 'A' || first == 'H') &&
-			(Common::isUpper(second) || second == 0x83 || second == 0x82 || second == '[')) {
-			cleaned.deleteChar(0);
-			while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
-				cleaned.deleteChar(0);
-			}
-		} else if (strchr("#%')!+,.-\"*&$(/", first)) {
-			cleaned.deleteChar(0);
-			while (!cleaned.empty() && Common::isSpace(cleaned.firstChar())) {
-				cleaned.deleteChar(0);
-			}
-		}
-	}
-
-	return cleaned;
-}
-
-Common::Array<ConversationElement> RoomManager::parseConversationElements(const byte *convData, uint32 size) {
-	Common::Array<ConversationElement> elements;
-	Common::HashMap<int, int> choiceIndices; // Track choice index occurrences
-	uint32 pos = 0;
-
-	// First pass: parse elements and track choice indices
-	while (pos < size) {
-		byte b = convData[pos];
-
-		if (b == 0x08) { // SPEAKER
-			pos++;
-			if (pos < size) {
-				byte speakerId = convData[pos];
-				Common::String speaker = (speakerId == 0x0D) ? "ALFRED" : "NPC";
-				pos++;
-
-				// Read text
-				Common::String text;
-				while (pos < size && convData[pos] != 0x08 && convData[pos] != 0xFB &&
-					   convData[pos] != 0xF1 && convData[pos] != 0xF8 && convData[pos] != 0xFD &&
-					   convData[pos] != 0xFC && convData[pos] != 0xF4 && convData[pos] != 0xF7 &&
-					   convData[pos] != 0xF5 && convData[pos] != 0xFE && convData[pos] != 0xEB &&
-					   convData[pos] != 0xF0) {
-					char32_t ch = decodeChar(convData[pos]);
-					if (ch != '.') {
-						text += ch;
-					}
-					pos++;
-				}
-
-				text = cleanText(text);
-				if (!text.empty()) {
-					ConversationElement elem;
-					elem.type = ConversationElement::DIALOGUE;
-					elem.speakerId = speakerId;
-					elem.speaker = speaker;
-					elem.text = text;
-					elem.choiceIndex = -1;
-					elements.push_back(elem);
-				}
-			}
-		} else if (b == 0xFB || b == 0xF1) { // CHOICE marker
-			pos++;
-			int choiceIndex = -1;
-			if (pos < size) {
-				choiceIndex = convData[pos];
-				// Track this choice index
-				if (choiceIndices.contains(choiceIndex)) {
-					choiceIndices[choiceIndex]++;
-				} else {
-					choiceIndices[choiceIndex] = 1;
-				}
-				pos++;
-			}
-
-			// Skip next 2 bytes (speaker marker)
-			if (pos < size)
-				pos++;
-			if (pos < size)
-				pos++;
-
-			// Read text
-			Common::String text;
-			while (pos < size && convData[pos] != 0x08 && convData[pos] != 0xFB &&
-				   convData[pos] != 0xF1 && convData[pos] != 0xF8 && convData[pos] != 0xFD &&
-				   convData[pos] != 0xFC && convData[pos] != 0xF4 && convData[pos] != 0xF7 &&
-				   convData[pos] != 0xF5 && convData[pos] != 0xFE && convData[pos] != 0xEB &&
-				   convData[pos] != 0xF0) {
-				char32_t ch = decodeChar(convData[pos]);
-				if (ch != '.') {
-					text += ch;
-				}
-				pos++;
-			}
-
-			text = cleanText(text);
-			if (!text.empty()) {
-				ConversationElement elem;
-				elem.type = ConversationElement::CHOICE_MARKER;
-				elem.text = text;
-				elem.choiceIndex = choiceIndex;
-				elements.push_back(elem);
-			}
-		} else if (b == 0xF8) { // ACTION
-			pos += 3;
-		} else if (b == 0xF4) { // END_CONV
-			ConversationElement elem;
-			elem.type = ConversationElement::END_CONV;
-			elements.push_back(elem);
-			pos++;
-		} else if (b == 0xF7) { // END_BRANCH
-			ConversationElement elem;
-			elem.type = ConversationElement::END_BRANCH;
-			elements.push_back(elem);
-			pos++;
-		} else if (b == 0xFD || b == 0xFC || b == 0xF5 || b == 0xFE || b == 0xEB || b == 0xF0) {
-			pos++;
-		} else {
-			pos++;
-		}
-	}
-
-	// Second pass: mark which indices are actual choices (appear multiple times)
-	for (uint i = 0; i < elements.size(); i++) {
-		if (elements[i].choiceIndex >= 0) {
-			elements[i].isRealChoice = (choiceIndices[elements[i].choiceIndex] > 1);
-		}
-	}
-
-	return elements;
-}
-
-Common::Array<ConversationNode> RoomManager::buildTreeStructure(const Common::Array<ConversationElement> &elements) {
-	Common::Array<ConversationNode> roots;
-	Common::Array<StackEntry> stack;
-	ConversationNode *currentRoot = nullptr;
-	uint i = 0;
-
-	while (i < elements.size()) {
-		const ConversationElement &elem = elements[i];
-
-		if (elem.type == ConversationElement::DIALOGUE && elem.speaker == "NPC") {
-			if (stack.empty()) {
-				// New root conversation
-				ConversationNode root;
-				root.type = ConversationNode::ROOT;
-				root.text = elem.text;
-				root.speaker = "NPC";
-				root.speakerId = elem.speakerId;
-				roots.push_back(root);
-				currentRoot = &roots[roots.size() - 1];
-			} else {
-				// NPC response within a branch
-				ConversationNode *parent = stack[stack.size() - 1].node;
-				ConversationNode response;
-				response.type = ConversationNode::RESPONSE;
-				response.speaker = "NPC";
-				response.speakerId = elem.speakerId;
-				response.text = elem.text;
-				parent->responses.push_back(response);
-			}
-			i++;
-
-		} else if (elem.type == ConversationElement::CHOICE_MARKER) {
-			if (elem.isRealChoice) {
-				// Real choice - player selects from menu
-				ConversationNode choiceNode;
-				choiceNode.type = ConversationNode::CHOICE;
-				choiceNode.text = elem.text;
-				choiceNode.speaker = "ALFRED";
-				choiceNode.speakerId = 0x0D; // Player
-				choiceNode.choiceIndex = elem.choiceIndex;
-
-				// Find where to attach this choice
-				while (!stack.empty() && stack[stack.size() - 1].index >= elem.choiceIndex) {
-					stack.pop_back();
-				}
-
-				if (!stack.empty()) {
-					ConversationNode *parent = stack[stack.size() - 1].node;
-					parent->subchoices.push_back(choiceNode);
-
-					// Get pointer to the newly added choice
-					ConversationNode *newChoice = &parent->subchoices[parent->subchoices.size() - 1];
-
-					StackEntry entry;
-					entry.node = newChoice;
-					entry.index = elem.choiceIndex;
-					stack.push_back(entry);
-				} else {
-					if (currentRoot) {
-						currentRoot->choices.push_back(choiceNode);
-
-						// Get pointer to the newly added choice
-						ConversationNode *newChoice = &currentRoot->choices[currentRoot->choices.size() - 1];
-
-						StackEntry entry;
-						entry.node = newChoice;
-						entry.index = elem.choiceIndex;
-						stack.push_back(entry);
-					}
-				}
-			} else {
-				// Auto-dialogue - ALFRED just speaks
-				if (!stack.empty()) {
-					ConversationNode *parent = stack[stack.size() - 1].node;
-					ConversationNode response;
-					response.type = ConversationNode::RESPONSE;
-					response.speaker = "ALFRED";
-					response.speakerId = 0x0D;
-					response.text = elem.text;
-					parent->responses.push_back(response);
-				}
-			}
-			i++;
-
-		} else if (elem.type == ConversationElement::DIALOGUE && elem.speaker == "ALFRED") {
-			if (!stack.empty()) {
-				ConversationNode *parent = stack[stack.size() - 1].node;
-				ConversationNode response;
-				response.type = ConversationNode::RESPONSE;
-				response.speaker = "ALFRED";
-				response.text = elem.text;
-				response.speakerId = 0x0D;
-				parent->responses.push_back(response);
-			}
-			i++;
-
-		} else if (elem.type == ConversationElement::END_CONV) {
-			if (!stack.empty()) {
-				stack[stack.size() - 1].node->terminated = true;
-				stack.pop_back();
-			}
-			i++;
-
-		} else if (elem.type == ConversationElement::END_BRANCH) {
-			stack.clear();
-			currentRoot = nullptr;
-			i++;
-
-		} else {
-			i++;
-		}
-	}
-
-	return roots;
-}
-
-Common::Array<ConversationNode> RoomManager::loadConversations(Common::File *roomFile, int roomOffset, uint32_t startPos) {
-
-	debug("Loading conversations starting at position %d", startPos);
-
-	uint32_t pair12_offset_pos = roomOffset + (12 * 8);
-	roomFile->seek(pair12_offset_pos, SEEK_SET);
-	uint32_t pair12_data_offset = roomFile->readUint32LE();
-	uint32_t pair12_size = roomFile->readUint32LE();
-
-	// startPos += 2;
-	uint32_t conversation_start = pair12_data_offset + startPos;
-	uint32_t conversation_size = pair12_size - startPos;
-
-	roomFile->seek(conversation_start, SEEK_SET);
-	byte *data = new byte[conversation_size];
-	roomFile->read(data, conversation_size);
-
-	Common::Array<ConversationElement> elements = parseConversationElements(data, conversation_size);
-	Common::Array<ConversationNode> roots = buildTreeStructure(elements);
-	return roots;
-}
-
 ScalingParams RoomManager::loadScalingParams(Common::File *roomFile, int roomOffset) {
 	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
 	roomFile->seek(pair10_offset_pos, SEEK_SET);
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index c2552e87866..c0a3c0f0419 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -49,43 +49,41 @@ public:
 		return "Unknown Room";
 	}
 
+	byte _currentRoomNumber = 0;
 	Common::Array<HotSpot> _currentRoomHotspots;
 	Common::Array<Sprite> _currentRoomAnims;
 	Common::Array<Exit> _currentRoomExits;
 	Common::Array<WalkBox> _currentRoomWalkboxes;
 	Common::Array<Description> _currentRoomDescriptions;
-	Common::Array<ConversationNode> _currentRoomConversations;
+
 	TalkingAnimHeader _talkingAnimHeader;
 	ScalingParams _scaleParams;
 	byte *_pixelsShadows = nullptr;
 	byte _roomPalette[768];
 	byte alfredRemap[256];
 	byte overlayRemap[256];
-	Common::StringArray _roomNames;
 	byte _musicTrack = 0;
 	Common::Array<byte> _roomSfx;
-	byte _currentRoomNumber = 0;
 	PaletteAnim *_currentPaletteAnim = nullptr;
 	Common::Array<Sticker> _currentRoomStickers;
+	byte *_conversationData = nullptr;
+	size_t _conversationDataSize = 0;
 
 private:
 	Common::Array<Sprite> loadRoomAnimations(Common::File *roomFile, int roomOffset);
 	Common::Array<HotSpot> loadHotspots(Common::File *roomFile, int roomOffset);
 	Common::Array<Exit> loadExits(Common::File *roomFile, int roomOffset);
 	Common::Array<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
-	Common::Array<Description> loadRoomDescriptions(Common::File *roomFile, int roomOffset, uint32_t &outPos);
+	Common::Array<Description> loadRoomTexts(Common::File *roomFile, int roomOffset);
 
-	Common::String getControlName(byte b);
-	Common::String cleanText(const Common::String &text);
-	Common::Array<ConversationElement> parseConversationElements(const byte *convData, uint32 size);
-	Common::Array<ConversationNode> buildTreeStructure(const Common::Array<ConversationElement> &elements);
-	Common::Array<ConversationNode> loadConversations(Common::File *roomFile, int roomOffset, uint32_t startPos);
 	ScalingParams loadScalingParams(Common::File *roomFile, int roomOffset);
 	byte *loadShadowMap(int roomNumber);
 	void loadRemaps(int roomNumber);
 	Common::StringArray loadRoomNames();
 	byte loadMusicTrackForRoom(Common::File *roomFile, int roomOffset);
 	Common::Array<byte> loadRoomSfx(Common::File *roomFile, int roomOffset);
+
+	Common::StringArray _roomNames;
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index aa8e4f9edb8..ae9fec2be54 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -84,15 +84,6 @@ const int kChoiceHeight = 16; // Height of each choice line in pixels
 #define MAX_CHARS_PER_LINE 0x2F // 47 characters
 #define MAX_LINES 5             // Maximum number of lines per page (0-indexed check against 4)
 
-// Control character codes (negative values in signed char)
-#define CHAR_SPACE 0x20        /* ' ' */
-#define CHAR_END_MARKER_1 0xFD /* -3 (end of text marker) */
-#define CHAR_END_MARKER_2 0xF4 /* -0xC (alternate end marker) */
-#define CHAR_END_MARKER_3 0xF8 /* -8 (another end marker) */
-#define CHAR_END_MARKER_4 0xF0 /* -0x10 (another end marker) */
-#define CHAR_NEWLINE 0xF6      /* -10 (newline marker) */
-#define CHAR_PAGE_BREAK 0xF9   /* marker inserted when switching pages */
-
 #define ALFRED_COLOR 0x0D
 
 const byte kIconBlinkPeriod = 4;
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 33713fbce43..87fb010963b 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -19,7 +19,9 @@
  *
  */
 #include "common/stream.h"
+#include "graphics/font.h"
 
+#include "pelrock/pelrock.h"
 #include "pelrock/types.h"
 #include "pelrock/util.h"
 #include "util.h"
@@ -50,13 +52,66 @@ void drawRect(byte *screenBuffer, int x, int y, int w, int h, byte color) {
 		for (int px = 0; px < w; px++) {
 			int destIdx = (y + py) * 640 + (x + px);
 			int srcIdx = py * w + px;
-			int color  = *((byte *)surface->getBasePtr(px, py));
-			if(color != 0)
-			screenBuffer[destIdx] = color;
+			int color = *((byte *)surface->getBasePtr(px, py));
+			if (color != 0)
+				screenBuffer[destIdx] = color;
 		}
 	}
 }
 
+void drawText(byte *screenBuffer, Graphics::Font *font, Common::String text, int x, int y, int w, byte color, Graphics::TextAlign align) {
+	Common::Rect rect = font->getBoundingBox(text.c_str());
+	Graphics::Surface *surface = new Graphics::Surface();
+	int bboxW = rect.width();
+	int bboxH = rect.height();
+
+	surface->create(bboxW, bboxH, Graphics::PixelFormat::createFormatCLUT8());
+
+	if (x + bboxW > 640) {
+		x = 640 - bboxW - 2;
+	}
+	if (y + bboxH > 400) {
+		y = 400 - bboxH - 2;
+	}
+	if (x < 0) {
+		x = 0;
+	}
+	if (y < 0) {
+		y = 0;
+	}
+
+	// Draw main text on top
+	font->drawString(surface, text.c_str(), 0, 0, bboxW, color, align);
+	drawRect(surface, 0, 0, bboxW - 1, bboxH - 1, color);
+	for (int py = 0; py < bboxH; py++) {
+		for (int px = 0; px < bboxW; px++) {
+			int destIdx = (y + py) * 640 + (x + px);
+			int srcIdx = py * bboxW + px;
+			int color = *((byte *)surface->getBasePtr(px, py));
+			if (color != 0)
+				screenBuffer[destIdx] = color;
+		}
+	}
+}
+
+void drawText(Graphics::Font *font, Common::String text, int x, int y, int w, byte color) {
+	Common::Rect rect = font->getBoundingBox(text.c_str());
+	if (x + rect.width() > 640) {
+		x = 640 - rect.width() - 2;
+	}
+	if (y + rect.height() > 400) {
+		y = 400 - rect.height();
+	}
+	if (x < 0) {
+		x = 0;
+	}
+	if (y < 0) {
+		y = 0;
+	}
+	// Draw main text on top
+	font->drawString(g_engine->_screen, text.c_str(), x, y, w, color, Graphics::kTextAlignCenter);
+}
+
 size_t rleDecompress(
 	const uint8_t *input,
 	size_t inputSize,
@@ -246,19 +301,29 @@ void drawPos(Graphics::ManagedSurface *surface, int x, int y, byte color) {
 }
 
 byte decodeChar(byte b) {
+	byte returnedChar = 0;
 	switch (b) {
-		case 0x82: return special_chars[1];
-		case 0x83: return special_chars[0];
-
-		case 0x80: return special_chars[3]; // n tilde
-		case 0x7F: return special_chars[4];
-		case 0x7E: return special_chars[5];
-		case 0x7D: return special_chars[6];
-		case 0x7C: return special_chars[7];
-		case 0x7B: return special_chars[8];
-		default:
-    		return b;
+	case 0x82:
+		returnedChar = special_chars[1];
+	case 0x83:
+		returnedChar = special_chars[0];
+
+	case 0x80:
+		returnedChar = special_chars[3]; // n tilde
+	case 0x7F:
+		returnedChar = special_chars[4];
+	case 0x7E:
+		returnedChar = special_chars[5];
+	case 0x7D:
+		returnedChar = special_chars[6];
+	case 0x7C:
+		returnedChar = special_chars[7];
+	case 0x7B:
+		returnedChar = special_chars[8];
+	default:
+		return returnedChar = b;
 	}
+	return returnedChar;
 }
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index 1d0aec0a740..aaa7d96af16 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -21,13 +21,12 @@
 #ifndef PELROCK_UTIL_H
 #define PELROCK_UTIL_H
 
-#include "common/array.h"
 #include "common/stream.h"
 #include "common/types.h"
+#include "graphics/font.h"
 #include "graphics/managed_surface.h"
 #include "graphics/surface.h"
 
-
 namespace Pelrock {
 
 const int EXPECTED_SIZE = 640 * 400;
@@ -39,23 +38,23 @@ void extractSingleFrame(byte *source, byte *dest, int frameIndex, int frameWidth
 void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byte color);
 void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color);
 void drawRect(byte *screenBuffer, int x, int y, int w, int h, byte color);
+void drawText(byte *screenBuffer, Graphics::Font *font, Common::String text, int x, int y, int w, byte color, Graphics::TextAlign align = Graphics::kTextAlignLeft);
+void drawText(Graphics::Font *font, Common::String text, int x, int y, int w, byte color);
 Common::String joinStrings(const Common::Array<Common::String> &strings, const Common::String &separator);
 void drawPos(Graphics::ManagedSurface *surface, int x, int y, byte color);
 byte decodeChar(byte b);
 
-
 static const int special_chars[] = {
-    131, // inverted ?
-    130, // inverted !
-    129, // capital N tilde
-    128, // small n tilde
-    127, // small u tilde
-    126, // small o tilde
-    125, // small i tilde
-    124, // small e tilde
-    123, // small a tilde
+	131, // inverted ?
+	130, // inverted !
+	129, // capital N tilde
+	128, // small n tilde
+	127, // small u tilde
+	126, // small o tilde
+	125, // small i tilde
+	124, // small e tilde
+	123, // small a tilde
 };
 
-
 } // End of namespace Pelrock
 #endif // PELROCK_UTIL_H


Commit: 4a16eee238c654f20d73cc054d214a2f4a5d075e
    https://github.com/scummvm/scummvm/commit/4a16eee238c654f20d73cc054d214a2f4a5d075e
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:24+02:00

Commit Message:
PELROCK: Creates double font

Changed paths:
  A engines/pelrock/fonts/small_font_double.cpp
  A engines/pelrock/fonts/small_font_double.h
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/fonts/small_font.h
    engines/pelrock/module.mk
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/util.cpp


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 99bb129661f..55ab3822263 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -52,10 +52,8 @@
 
 namespace Pelrock {
 
-DialogManager::DialogManager(Graphics::Screen *screen, PelrockEventManager *events,
-							 LargeFont *largeFont, SmallFont *smallFont)
-	: _screen(screen), _events(events),
-	  _largeFont(largeFont), _smallFont(smallFont) {
+DialogManager::DialogManager(Graphics::Screen *screen, PelrockEventManager *events)
+	: _screen(screen), _events(events) {
 }
 
 DialogManager::~DialogManager() {
@@ -164,7 +162,7 @@ void DialogManager::displayChoices(Common::Array<ChoiceOption> *choices, byte *c
 		}
 	}
 	for (int i = 0; i < choices->size(); i++) {
-		drawText(compositeBuffer, g_engine->_smallFont, (*choices)[i].text, 10, overlayY + 2 + i * kChoiceHeight, 620, 9);
+		drawText(compositeBuffer, g_engine->_doubleSmallFont, (*choices)[i].text, 10, overlayY + 2 + i * kChoiceHeight, 620, 9);
 	}
 }
 
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 816d9f32dab..bf78fb658b6 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -46,8 +46,6 @@ class DialogManager {
 private:
 	Graphics::Screen *_screen = nullptr;
 	PelrockEventManager *_events = nullptr;
-	LargeFont *_largeFont = nullptr;
-	SmallFont *_smallFont = nullptr;
 	Sprite *_curSprite = nullptr;
 
 	// Private helper functions for conversation parsing
@@ -59,8 +57,7 @@ private:
 	void checkMouse();
 
 public:
-	DialogManager(Graphics::Screen *screen, PelrockEventManager *events,
-				  LargeFont *largeFont, SmallFont *smallFont);
+	DialogManager(Graphics::Screen *screen, PelrockEventManager *events);
 	~DialogManager();
 
 	void displayChoices(Common::Array<ChoiceOption> *choices, byte *compositeBuffer);
diff --git a/engines/pelrock/fonts/small_font.h b/engines/pelrock/fonts/small_font.h
index f1fb165503a..08432320f98 100644
--- a/engines/pelrock/fonts/small_font.h
+++ b/engines/pelrock/fonts/small_font.h
@@ -39,11 +39,12 @@ public:
 	int getMaxCharWidth() const override { return CHAR_WIDTH; }
 	int getCharWidth(uint32 chr) const override;
 	void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
+protected:
+	byte *_fontData;
 
 private:
 	static const int CHAR_WIDTH = 8;
 	static const int CHAR_HEIGHT = 8;
-	byte *_fontData;
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/fonts/small_font_double.cpp b/engines/pelrock/fonts/small_font_double.cpp
new file mode 100644
index 00000000000..ef483eaf9c0
--- /dev/null
+++ b/engines/pelrock/fonts/small_font_double.cpp
@@ -0,0 +1,89 @@
+/* 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 "pelrock/fonts/small_font_double.h"
+
+namespace Pelrock {
+
+DoubleSmallFont::DoubleSmallFont() : SmallFont() {
+}
+
+DoubleSmallFont::~DoubleSmallFont() {
+}
+
+// void DoubleSmallFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
+// 	if (!_fontData || chr > 255) {
+// 		return;
+// 	}
+
+// 	int charOffset = chr * 8;
+
+// 	for (int i = 0; i < 8; i++) {
+// 		byte rowByte = _fontData[charOffset + i];
+// 		for (int bit = 0; bit < 8; bit++) {
+// 			bool pixelOn = (rowByte & (0x80 >> bit)) != 0;
+// 			if (pixelOn) {
+// 				if ((x + bit) < dst->w && (y + i) < dst->h) {
+// 					*((byte *)dst->getBasePtr(x + bit, y + i)) = color;
+// 				}
+// 			}
+// 		}
+// 	}
+// }
+
+void DoubleSmallFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
+	if (!_fontData || chr > 255) {
+		return;
+	}
+
+	int charOffset = chr * 8;
+
+	for (int i = 0; i < 8; i++) {
+		byte rowByte = _fontData[charOffset + i];
+		for (int bit = 0; bit < 8; bit++) {
+			bool pixelOn = (rowByte & (0x80 >> bit)) != 0;
+            int yPos = y + (i * 2);
+            // int yPos
+			if (pixelOn) {
+				if ((x + bit) < dst->w && (y + yPos + 1) < dst->h) {
+					*((byte *)dst->getBasePtr(x + bit, yPos)) = color;
+                    *((byte *)dst->getBasePtr(x + bit, yPos + 1)) = color;
+					// *((byte *)dst->getBasePtr(x + bit, y + yPos + 1)) = color;
+				}
+			}
+		}
+	}
+
+	// for (int i = 0; i < 8; i++) {
+	// 	byte rowByte = _fontData[charOffset + i];
+	// 	for (int bit = 0; bit < 8; bit++) {
+	// 		bool pixelOn = (rowByte & (0x80 >> bit)) != 0;
+	// 		if (pixelOn) {
+	// 			if ((x + bit) < dst->w && (y + i) < dst->h) {
+	// 				*((byte *)dst->getBasePtr(x + bit, y + i)) = color;
+	// 			}
+	// 		}
+	// 	}
+	// }
+
+}
+
+} // namespace Pelrock
diff --git a/engines/pelrock/fonts/small_font_double.h b/engines/pelrock/fonts/small_font_double.h
new file mode 100644
index 00000000000..6cbe30d4884
--- /dev/null
+++ b/engines/pelrock/fonts/small_font_double.h
@@ -0,0 +1,47 @@
+/* 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 PELROCK_SMALLFONTDOUBLE_H
+#define PELROCK_SMALLFONTDOUBLE_H
+
+#include "common/file.h"
+#include "common/str.h"
+#include "graphics/font.h"
+#include "graphics/surface.h"
+
+#include "pelrock/fonts/small_font.h"
+
+namespace Pelrock {
+class DoubleSmallFont : public SmallFont {
+public:
+	DoubleSmallFont();
+	~DoubleSmallFont();
+
+	// Required Font interface methods
+	int getFontHeight() const override { return CHAR_HEIGHT; };
+	void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
+
+private:
+	static const int CHAR_HEIGHT = 16;
+};
+
+} // End of namespace Pelrock
+#endif
+
diff --git a/engines/pelrock/module.mk b/engines/pelrock/module.mk
index 942fcb31b5e..427fcc99c03 100644
--- a/engines/pelrock/module.mk
+++ b/engines/pelrock/module.mk
@@ -8,6 +8,7 @@ MODULE_OBJS = \
 	room.o \
 	fonts/small_font.o \
 	fonts/large_font.o \
+	fonts/small_font_double.o \
 	util.o \
 	resources.o\
 	sound.o \
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index d9a7c8da098..2a82af31a9c 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -57,6 +57,7 @@ PelrockEngine::~PelrockEngine() {
 	delete[] _currentBackground;
 	delete _largeFont;
 	delete _smallFont;
+	delete _doubleSmallFont;
 	delete _screen;
 	delete _chrono;
 	delete _videoManager;
@@ -86,7 +87,7 @@ Common::Error PelrockEngine::run() {
 	_room = new RoomManager();
 	_res = new ResourceManager();
 	_sound = new SoundManager(_mixer);
-	_dialog = new DialogManager(_screen, _events, _largeFont, _smallFont);
+	_dialog = new DialogManager(_screen, _events);
 
 	// Set the engine's debugger console
 	setDebugger(new PelrockConsole(this));
@@ -139,6 +140,8 @@ void PelrockEngine::init() {
 	_smallFont->load("ALFRED.4");
 	_largeFont = new LargeFont();
 	_largeFont->load("ALFRED.7");
+	_doubleSmallFont = new DoubleSmallFont();
+	_doubleSmallFont->load("ALFRED.4");
 
 	changeCursor(DEFAULT);
 	CursorMan.showMouse(true);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 8fdf33ee2a4..407afeed780 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -42,6 +42,7 @@
 #include "pelrock/events.h"
 #include "pelrock/fonts/large_font.h"
 #include "pelrock/fonts/small_font.h"
+#include "pelrock/fonts/small_font_double.h"
 #include "pelrock/resources.h"
 #include "pelrock/room.h"
 #include "pelrock/sound.h"
@@ -186,6 +187,7 @@ public:
 
 	SmallFont *_smallFont = nullptr;
 	LargeFont *_largeFont = nullptr;
+	DoubleSmallFont *_doubleSmallFont = nullptr;
 	void renderScene(bool showTextOverlay = false);
 
 public:
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 87fb010963b..5725ab54109 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -82,7 +82,7 @@ void drawText(byte *screenBuffer, Graphics::Font *font, Common::String text, int
 
 	// Draw main text on top
 	font->drawString(surface, text.c_str(), 0, 0, bboxW, color, align);
-	drawRect(surface, 0, 0, bboxW - 1, bboxH - 1, color);
+	// drawRect(surface, 0, 0, bboxW - 1, bboxH - 1, color);
 	for (int py = 0; py < bboxH; py++) {
 		for (int px = 0; px < bboxW; px++) {
 			int destIdx = (y + py) * 640 + (x + px);


Commit: 105ea475b050794e410ebca753ebdcb4502e0bc1
    https://github.com/scummvm/scummvm/commit/105ea475b050794e410ebca753ebdcb4502e0bc1
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:24+02:00

Commit Message:
PELROCK: Implements double height font

Changed paths:
    engines/pelrock/fonts/small_font.cpp
    engines/pelrock/fonts/small_font.h
    engines/pelrock/fonts/small_font_double.cpp
    engines/pelrock/fonts/small_font_double.h


diff --git a/engines/pelrock/fonts/small_font.cpp b/engines/pelrock/fonts/small_font.cpp
index 9ab959e3bcb..9dff0d694e2 100644
--- a/engines/pelrock/fonts/small_font.cpp
+++ b/engines/pelrock/fonts/small_font.cpp
@@ -38,8 +38,8 @@ bool SmallFont::load(const Common::String &filename) {
 
 	file.seek(0x8F32, SEEK_SET);
 
-	// const int dataSize = 256 * 8 * 8; // 256 characters, 8x8 pixels
-	const int dataSize = 2048; // 256 characters, 8x8 pixels
+	const int dataSize = kNumChars * 8; // 256 characters, 8x8 pixels
+	debug("SmallFont::load: Loading font data of size %d from %s", dataSize, filename.c_str());
 	_fontData = new byte[dataSize];
 	file.read(_fontData, dataSize);
 	file.close();
@@ -52,7 +52,7 @@ int SmallFont::getCharWidth(uint32 chr) const {
 }
 
 void SmallFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
-	if (!_fontData || chr > 255) {
+	if (!_fontData || chr > kNumChars - 1) {
 		return;
 	}
 
diff --git a/engines/pelrock/fonts/small_font.h b/engines/pelrock/fonts/small_font.h
index 08432320f98..cd80c690a1b 100644
--- a/engines/pelrock/fonts/small_font.h
+++ b/engines/pelrock/fonts/small_font.h
@@ -27,6 +27,9 @@
 #include "graphics/surface.h"
 
 namespace Pelrock {
+
+static const int kNumChars = 256;
+
 class SmallFont : public Graphics::Font {
 public:
 	SmallFont();
@@ -39,8 +42,9 @@ public:
 	int getMaxCharWidth() const override { return CHAR_WIDTH; }
 	int getCharWidth(uint32 chr) const override;
 	void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
-protected:
+
 	byte *_fontData;
+protected:
 
 private:
 	static const int CHAR_WIDTH = 8;
diff --git a/engines/pelrock/fonts/small_font_double.cpp b/engines/pelrock/fonts/small_font_double.cpp
index ef483eaf9c0..19fb92645cf 100644
--- a/engines/pelrock/fonts/small_font_double.cpp
+++ b/engines/pelrock/fonts/small_font_double.cpp
@@ -18,7 +18,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
-
+#include "common/debug.h"
 #include "pelrock/fonts/small_font_double.h"
 
 namespace Pelrock {
@@ -29,61 +29,26 @@ DoubleSmallFont::DoubleSmallFont() : SmallFont() {
 DoubleSmallFont::~DoubleSmallFont() {
 }
 
-// void DoubleSmallFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
-// 	if (!_fontData || chr > 255) {
-// 		return;
-// 	}
-
-// 	int charOffset = chr * 8;
-
-// 	for (int i = 0; i < 8; i++) {
-// 		byte rowByte = _fontData[charOffset + i];
-// 		for (int bit = 0; bit < 8; bit++) {
-// 			bool pixelOn = (rowByte & (0x80 >> bit)) != 0;
-// 			if (pixelOn) {
-// 				if ((x + bit) < dst->w && (y + i) < dst->h) {
-// 					*((byte *)dst->getBasePtr(x + bit, y + i)) = color;
-// 				}
-// 			}
-// 		}
-// 	}
-// }
-
 void DoubleSmallFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
-	if (!_fontData || chr > 255) {
+	if (!_fontData || chr > kNumChars - 1) {
+		// debug("DoubleSmallFont::drawChar: Invalid char %d", chr);
 		return;
 	}
-
 	int charOffset = chr * 8;
 
 	for (int i = 0; i < 8; i++) {
 		byte rowByte = _fontData[charOffset + i];
 		for (int bit = 0; bit < 8; bit++) {
 			bool pixelOn = (rowByte & (0x80 >> bit)) != 0;
-            int yPos = y + (i * 2);
-            // int yPos
+			int yPos = y + (i * 2);
 			if (pixelOn) {
-				if ((x + bit) < dst->w && (y + yPos + 1) < dst->h) {
+				if ((x + bit) < dst->w && (yPos + 1) < dst->h) {
 					*((byte *)dst->getBasePtr(x + bit, yPos)) = color;
-                    *((byte *)dst->getBasePtr(x + bit, yPos + 1)) = color;
-					// *((byte *)dst->getBasePtr(x + bit, y + yPos + 1)) = color;
+					*((byte *)dst->getBasePtr(x + bit, yPos + 1)) = color;
 				}
 			}
 		}
 	}
-
-	// for (int i = 0; i < 8; i++) {
-	// 	byte rowByte = _fontData[charOffset + i];
-	// 	for (int bit = 0; bit < 8; bit++) {
-	// 		bool pixelOn = (rowByte & (0x80 >> bit)) != 0;
-	// 		if (pixelOn) {
-	// 			if ((x + bit) < dst->w && (y + i) < dst->h) {
-	// 				*((byte *)dst->getBasePtr(x + bit, y + i)) = color;
-	// 			}
-	// 		}
-	// 	}
-	// }
-
 }
 
 } // namespace Pelrock
diff --git a/engines/pelrock/fonts/small_font_double.h b/engines/pelrock/fonts/small_font_double.h
index 6cbe30d4884..5bf265ca0fa 100644
--- a/engines/pelrock/fonts/small_font_double.h
+++ b/engines/pelrock/fonts/small_font_double.h
@@ -34,7 +34,7 @@ public:
 	DoubleSmallFont();
 	~DoubleSmallFont();
 
-	// Required Font interface methods
+
 	int getFontHeight() const override { return CHAR_HEIGHT; };
 	void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
 


Commit: f5a9d2bcf28d159daeb7e1fdc9bc0c63329a132f
    https://github.com/scummvm/scummvm/commit/f5a9d2bcf28d159daeb7e1fdc9bc0c63329a132f
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:25+02:00

Commit Message:
PELROCK: Decodes text bytes properly

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/util.cpp
    engines/pelrock/util.h


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 55ab3822263..8bf85f49a2f 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -112,6 +112,7 @@ uint32 DialogManager::readTextBlock(
 		outSpeakerId = ALFRED_COLOR;
 	}
 
+	pos += 2; // Skip line count and blank
 	// Read text until control byte
 	while (pos < dataSize) {
 		byte b = data[pos];
@@ -130,22 +131,8 @@ uint32 DialogManager::readTextBlock(
 			continue;
 		}
 
-		if (b == 0x0A || b == 0x0B || b == 0x00) {
-			debug("Skipping byte 0x%02X at pos %u", b, pos);
-			pos++; // Skip nulls and line feeds
-			continue;
-		}
-
-		// Regular text - decode the character
-		if (b >= 0x20 && b <= 0x7A) {
-			outText += decodeChar(b);
-		} else {
-			// Try to decode special characters
-			byte decoded = decodeChar(b);
-			if (decoded != b || (decoded >= 0x20 && decoded <= 0x83)) {
-				outText += (char)decoded;
-			}
-		}
+		// Regular text - does not need decoding
+		outText += b;
 		pos++;
 	}
 
@@ -272,6 +259,7 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 
 				// Parse the choice text
 				uint32 textPos = pos + 4; // Skip marker + index + 2 speaker bytes
+				textPos += 2;
 				while (textPos < dataSize) {
 					byte tb = data[textPos];
 					if (tb == CTRL_END_TEXT || tb == CTRL_DIALOGUE_MARKER ||
@@ -280,17 +268,12 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 						break;
 					}
 
-					if (b == 0x0A || b == 0x0B || b == 0x00) {
-						debug("Skipping byte 0x%02X at pos %u", b, pos);
-						pos++; // Skip nulls and line feeds
-						continue;
-					}
-
-					if (tb >= 0x20 && tb <= 0x7A) {
+					if (tb >= 0x20 && tb < 0x7A) {
 						opt.text += (char)tb;
 					} else {
 						byte decoded = decodeChar(tb);
-						if (decoded != tb || (decoded >= 0x20 && decoded <= 0x83)) {
+						debug("Parsing choice char: 0x%02X, decoded: 0x%02X", tb, decoded);
+						if (decoded != tb || (decoded >= 0x20 && decoded <= 0xB4)) {
 							opt.text += (char)decoded;
 						}
 					}
@@ -333,6 +316,7 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 
 					// Parse the choice text
 					uint32 textPos = pos + 4;
+					textPos += 2; // Skip marker + index + 2 speaker bytes
 					while (textPos < dataSize) {
 						byte tb = data[textPos];
 						if (tb == CTRL_END_TEXT || tb == CTRL_DIALOGUE_MARKER ||
@@ -341,17 +325,12 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 							break;
 						}
 
-						if (b == 0x0A || b == 0x0B || b == 0x00) {
-							debug("Skipping byte 0x%02X at pos %u", b, pos);
-							pos++; // Skip nulls and line feeds
-							continue;
-						}
-
 						if (tb >= 0x20 && tb <= 0x7A) {
 							opt.text += (char)tb;
 						} else {
 							byte decoded = decodeChar(tb);
-							if (decoded != tb || (decoded >= 0x20 && decoded <= 0x83)) {
+							debug("Parsing choice char: 0x%02X, decoded: 0x%02X", tb, decoded);
+							if (decoded != tb || (decoded >= 0x20 && decoded <= 0xB4)) {
 								opt.text += (char)decoded;
 							}
 						}
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 5725ab54109..de6c4f3b2b6 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -301,29 +301,27 @@ void drawPos(Graphics::ManagedSurface *surface, int x, int y, byte color) {
 }
 
 byte decodeChar(byte b) {
-	byte returnedChar = 0;
+
 	switch (b) {
 	case 0x82:
-		returnedChar = special_chars[1];
+		return special_chars[1];
 	case 0x83:
-		returnedChar = special_chars[0];
-
+		return special_chars[0];
 	case 0x80:
-		returnedChar = special_chars[3]; // n tilde
+		return  special_chars[3]; // n tilde
 	case 0x7F:
-		returnedChar = special_chars[4];
+		return special_chars[4];
 	case 0x7E:
-		returnedChar = special_chars[5];
+		return special_chars[5];
 	case 0x7D:
-		returnedChar = special_chars[6];
+		return special_chars[6];
 	case 0x7C:
-		returnedChar = special_chars[7];
+		return special_chars[7];
 	case 0x7B:
-		returnedChar = special_chars[8];
+		return special_chars[8];
 	default:
-		return returnedChar = b;
+		return b;
 	}
-	return returnedChar;
 }
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index aaa7d96af16..2a31bfbcd81 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -45,15 +45,15 @@ void drawPos(Graphics::ManagedSurface *surface, int x, int y, byte color);
 byte decodeChar(byte b);
 
 static const int special_chars[] = {
-	131, // inverted ?
-	130, // inverted !
-	129, // capital N tilde
-	128, // small n tilde
-	127, // small u tilde
-	126, // small o tilde
-	125, // small i tilde
-	124, // small e tilde
-	123, // small a tilde
+	168, // inverted ?
+	173, // inverted !
+	165, // capital N tilde
+	164, // small n tilde
+	163, // small u tilde
+	162, // small o tilde
+	161, // small i tilde
+	132, // small e tilde
+	160, // small a tilde
 };
 
 } // End of namespace Pelrock


Commit: deb08538828e0c6ecf31a1def5092a376e75a1ad
    https://github.com/scummvm/scummvm/commit/deb08538828e0c6ecf31a1def5092a376e75a1ad
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:25+02:00

Commit Message:
PELROCK: Fixes responses during conversations

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 8bf85f49a2f..41a02d418ac 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -112,7 +112,7 @@ uint32 DialogManager::readTextBlock(
 		outSpeakerId = ALFRED_COLOR;
 	}
 
-	pos += 2; // Skip line count and blank
+	// pos += 2; // Skip line count and blank
 	// Read text until control byte
 	while (pos < dataSize) {
 		byte b = data[pos];
@@ -132,7 +132,9 @@ uint32 DialogManager::readTextBlock(
 		}
 
 		// Regular text - does not need decoding
-		outText += b;
+		if (b >= 0x20 && b <= 0x83) {
+			outText += b;
+		}
 		pos++;
 	}
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 2a82af31a9c..44ccb7788ec 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -302,33 +302,6 @@ void PelrockEngine::checkMouse() {
 	}
 	checkMouseHover();
 
-//else if (e.type == Common::EVENT_MOUSEMOVE) {
-	// 		mouseX = e.mouse.x;
-	// 		mouseY = e.mouse.y;
-	// 	} else if (e.type == Common::EVENT_LBUTTONDOWN) {
-	// 		if (!_isMouseDown) {
-	// 			_mouseClickTime = g_system->getMillis();
-	// 			_isMouseDown = true;
-	// 		}
-	// 	} else if (e.type == Common::EVENT_LBUTTONUP) {
-	// 		_isMouseDown = false;
-	// 		checkMouseClick(e.mouse.x, e.mouse.y);
-	// 		_displayPopup = false;
-	// 		_longClick = false;
-	// 	} else if (e.type == Common::EVENT_RBUTTONUP) {
-	// 		g_system->getPaletteManager()->setPalette(_res->_mainMenuPalette, 0, 256);
-	// 		stateGame = SETTINGS;
-	// 	}
-	// }
-	// if (_isMouseDown) {
-	// 	if (g_system->getMillis() - _mouseClickTime >= kLongClickDuration) {
-	// 		_longClick = true;
-	// 		_isMouseDown = false;
-	// 		checkLongMouseClick(e.mouse.x, e.mouse.y);
-	// 	}
-	// }
-	// checkMouseHover();
-
 }
 
 void PelrockEngine::copyBackgroundToBuffer() {


Commit: 318b7f345f858133bd0a6b0d8c7d994143c8e7c4
    https://github.com/scummvm/scummvm/commit/318b7f345f858133bd0a6b0d8c7d994143c8e7c4
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:25+02:00

Commit Message:
PELROCK: Initial attempt at word wrapping algorithm

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 41a02d418ac..c4accf5dea1 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -160,14 +160,14 @@ void DialogManager::displayChoices(Common::Array<ChoiceOption> *choices, byte *c
  * @param text The text to display
  * @param speakerId The speaker ID which is used as color
  */
-void DialogManager::displayDialogue(const Common::String &text, byte speakerId) {
-	if (text.empty() || text.size() <= 1) {
+void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>> dialogueLines, byte speakerId) {
+	if (dialogueLines.empty()) {
 		return;
 	}
 
 	// Clear any existing click state
 	_events->_leftMouseClicked = false;
-
+	int curPage = 0;
 	// Render loop - display text and wait for click
 	while (!g_engine->shouldQuit()) {
 		_events->pollEvent();
@@ -175,9 +175,13 @@ void DialogManager::displayDialogue(const Common::String &text, byte speakerId)
 		// Render the scene (keeps animations going)
 		g_engine->renderScene(false);
 
+
 		// Draw the dialogue text on top using speaker ID as color
-		drawText(g_engine->_largeFont, text, _curSprite->x, _curSprite->y - 10, 640, speakerId);
-		// drawText(g_engine->_largeFont, "Hola", 10, 10, 640, speakerId);
+		Common::Array<Common::String> textLines = dialogueLines[curPage];
+		for(int i = 0; i < textLines.size(); i++) {
+			int yPos = 400 - (textLines.size() - i) * 20 - 10; // Adjust Y position based on line count
+			drawText(g_engine->_largeFont, textLines[i], 10, yPos, 620, speakerId);
+		}
 
 		// Present to screen
 		_screen->markAllDirty();
@@ -185,12 +189,21 @@ void DialogManager::displayDialogue(const Common::String &text, byte speakerId)
 
 		if (_events->_leftMouseClicked) {
 			_events->_leftMouseClicked = false;
+			if(curPage < (int)dialogueLines.size() - 1) {
+				curPage++;
+			} else {
+				break; // Exit dialogue on last page click
+			}
 			break;
 		}
 		g_system->delayMillis(10);
 	}
 }
 
+void DialogManager::displayDialogue(Common::String text, byte speakerId) {
+	displayDialogue(wordWrap(text), speakerId);
+}
+
 /**
  * Select a choice from displayed options
  * Returns the index of the selected choice in the choices array
@@ -395,10 +408,13 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		byte speakerId;
 		uint32 endPos = readTextBlock(conversationData, dataSize, position, text, speakerId);
 
+
+		Common::Array<Common::Array<Common::String>> wrappedText = wordWrap(text);
+
 		// Skip spurious single character artifacts
 		if (!text.empty() && text.size() > 1) {
 			debug("Dialogue: \"%s\" (Speaker ID: %u)", text.c_str(), speakerId);
-			displayDialogue(text, speakerId);
+			displayDialogue(wrappedText, speakerId);
 		}
 
 		// Move to end of text
@@ -513,4 +529,117 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 	debug("Conversation ended");
 	// Note: The caller should set inConversation = false after this returns
 }
+
+bool isEndMarker(char char_byte) {
+	return char_byte == CHAR_END_MARKER_1 || char_byte == CHAR_END_MARKER_2 || char_byte == CHAR_END_MARKER_3 || char_byte == CHAR_END_MARKER_4;
+}
+
+
+int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
+	// return word_length, is_end
+	int wordLength = 0;
+	int pos = startPos;
+	while (pos < text.size()) {
+		char char_byte = text[pos];
+		if (char_byte == CHAR_SPACE || isEndMarker(char_byte)) {
+			break;
+		}
+		wordLength++;
+		pos++;
+	}
+	// Check if we hit an end marker
+	if (pos < text.size() && isEndMarker(text[pos])) {
+		isEnd = true;
+	}
+	// Count ALL trailing spaces as part of this word
+	if (pos < text.size() && !isEnd) {
+		if (text[pos] == CHAR_END_MARKER_3) { // 0xF8 (-8) special case
+			wordLength += 3;
+		} else {
+			// Count all consecutive spaces
+			while (pos < text.size() && text[pos] == CHAR_SPACE) {
+				wordLength++;
+				pos++;
+			}
+		}
+	}
+	return wordLength;
+}
+
+Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::String text) {
+
+	Common::Array<Common::Array<Common::String>> pages;
+	Common::Array<Common::String> currentPage;
+	Common::Array<Common::String> currentLine;
+	int charsRemaining = MAX_CHARS_PER_LINE;
+	int position = 0;
+	int currentLineNum = 0;
+	while (position < text.size()) {
+		bool isEnd = false;
+		int wordLength = calculateWordLength(text, position, isEnd);
+		// # Extract the word (including trailing spaces)
+		// word = text[position:position + word_length].decode('latin-1', errors='replace')
+		Common::String word = text.substr(position, wordLength).decode(Common::kLatin1);
+		// # Key decision: if word_length > chars_remaining, wrap to next line
+		if (wordLength > charsRemaining) {
+			// Word is longer than the entire line - need to split
+			currentPage.push_back(joinStrings(currentLine, ""));
+			currentLine.clear();
+			charsRemaining = MAX_CHARS_PER_LINE;
+			currentLineNum++;
+
+			if (currentLineNum >= MAX_LINES) {
+				pages.push_back(currentPage);
+				currentPage.clear();
+				currentLineNum = 0;
+			}
+		}
+		// Add word to current line
+		currentLine.push_back(word);
+		charsRemaining -= wordLength;
+
+		if (charsRemaining == 0 && isEnd) {
+			Common::String lineText = joinStrings(currentLine, "");
+			while (lineText.lastChar() == CHAR_SPACE) {
+				lineText = lineText.substr(0, lineText.size() - 1);
+			}
+			int trailingSpaces = currentLine.size() - lineText.size();
+			if (trailingSpaces > 0) {
+				currentPage.push_back(lineText);
+				//  current_line = [' ' * trailing_spaces]
+				Common::String currentLine(trailingSpaces, ' ');
+				charsRemaining = MAX_CHARS_PER_LINE - trailingSpaces;
+				currentLineNum += 1;
+
+				if (currentLineNum >= MAX_LINES) {
+					pages.push_back(currentPage);
+					currentPage.clear();
+					currentLineNum = 0;
+				}
+			}
+		}
+
+		position += wordLength;
+		if (isEnd) {
+			// End of sentence/paragraph/page
+			break;
+		}
+	}
+	if (currentLine.empty() == false) {
+		Common::String lineText = joinStrings(currentLine, "");
+		while (lineText.lastChar() == CHAR_SPACE) {
+			lineText = lineText.substr(0, lineText.size() - 1);
+		}
+		currentPage.push_back(lineText);
+	}
+	if (currentPage.empty() == false) {
+		pages.push_back(currentPage);
+	}
+	for (int i = 0; i < pages.size(); i++) {
+		for (int j = 0; j < pages[i].size(); j++) {
+		}
+	}
+	return pages;
+}
+
 } // namespace Pelrock
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index bf78fb658b6..f3b7e055ebe 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -49,7 +49,8 @@ private:
 	Sprite *_curSprite = nullptr;
 
 	// Private helper functions for conversation parsing
-	void displayDialogue(const Common::String &text, byte speakerId);
+	void displayDialogue(Common::Array<Common::Array<Common::String>> dialogueLines, byte speakerId);
+	void displayDialogue(Common::String text, byte speakerId);
 	uint32 readTextBlock(const byte *data, uint32 dataSize, uint32 startPos,
 						 Common::String &outText, byte &outSpeakerId);
 	uint32 parseChoices(const byte *data, uint32 dataSize, uint32 startPos, Common::Array<ChoiceOption> &outChoices);
@@ -63,7 +64,7 @@ public:
 	void displayChoices(Common::Array<ChoiceOption> *choices, byte *compositeBuffer);
 	int selectChoice(Common::Array<Common::String> &choices, byte *compositeBuffer);
 	void startConversation(const byte *conversationData, uint32 dataSize, Sprite *alfredAnimSet = nullptr);
-
+	Common::Array<Common::Array<Common::String>> wordWrap(Common::String text);
 	Common::Array<ChoiceOption> *_currentChoices = nullptr;
 };
 


Commit: 570547df397964c7c49fb3b343092b2a25b49230
    https://github.com/scummvm/scummvm/commit/570547df397964c7c49fb3b343092b2a25b49230
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:26+02:00

Commit Message:
PELROCK: Center text above character

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index c4accf5dea1..6ca9bfaea52 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -175,21 +175,61 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		// Render the scene (keeps animations going)
 		g_engine->renderScene(false);
 
-
 		// Draw the dialogue text on top using speaker ID as color
 		Common::Array<Common::String> textLines = dialogueLines[curPage];
-		for(int i = 0; i < textLines.size(); i++) {
-			int yPos = 400 - (textLines.size() - i) * 20 - 10; // Adjust Y position based on line count
-			drawText(g_engine->_largeFont, textLines[i], 10, yPos, 620, speakerId);
+
+		int maxWidth = 0;
+		int height = textLines.size() * 20;
+		for (int i = 0; i < textLines.size(); i++) {
+			maxWidth = MAX(maxWidth, g_engine->_largeFont->getStringWidth(textLines[i]));
+		}
+
+		Graphics::Surface s;
+		s.create(maxWidth, height, Graphics::PixelFormat::createFormatCLUT8());
+		s.drawRoundRect(Common::Rect(0, 0, s.getRect().width(), s.getRect().height()), 2, 13, false);
+		int xPos = 0;
+		int yPos = 0;
+
+		if (speakerId == ALFRED_COLOR) {
+			// Offset X position for Alfred to avoid overlapping with his sprite
+			xPos = g_engine->alfredState.x + kAlfredFrameWidth / 2 - maxWidth / 2;
+			yPos = g_engine->alfredState.y - kAlfredFrameHeight - 30; // Above sprite, adjust for line
+		} else {
+			xPos = _curSprite->x + _curSprite->w / 2 - maxWidth / 2;
+			yPos = _curSprite->y - 30 - height; // Above sprite, adjust for line
+		}
+
+		for (int i = 0; i < textLines.size(); i++) {
+
+			int xPos = 0;
+			int yPos = i * 20; // Above sprite, adjust for line
+
+			debug("Drawing dialogue line %s at (%d, %d), speaker =%d, maxWidth = %d", textLines[i].c_str(), xPos, yPos, speakerId, maxWidth);
+			g_engine->_largeFont->drawString(&s, textLines[i], xPos, yPos, maxWidth, speakerId, Graphics::kTextAlignCenter);
+
 		}
 
+		if (xPos + s.getRect().width() > 640) {
+			xPos = 640 - s.getRect().width() - 2;
+		}
+		if (yPos + s.getRect().height() > 400) {
+			yPos = 400 - s.getRect().height();
+		}
+		if (xPos < 0) {
+			xPos = 0;
+		}
+		if (yPos < 0) {
+			yPos = 0;
+		}
+
+		_screen->transBlitFrom(s, s.getRect(), Common::Point(xPos, yPos));
 		// Present to screen
 		_screen->markAllDirty();
 		_screen->update();
 
 		if (_events->_leftMouseClicked) {
 			_events->_leftMouseClicked = false;
-			if(curPage < (int)dialogueLines.size() - 1) {
+			if (curPage < (int)dialogueLines.size() - 1) {
 				curPage++;
 			} else {
 				break; // Exit dialogue on last page click
@@ -408,7 +448,6 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		byte speakerId;
 		uint32 endPos = readTextBlock(conversationData, dataSize, position, text, speakerId);
 
-
 		Common::Array<Common::Array<Common::String>> wrappedText = wordWrap(text);
 
 		// Skip spurious single character artifacts
@@ -534,7 +573,6 @@ bool isEndMarker(char char_byte) {
 	return char_byte == CHAR_END_MARKER_1 || char_byte == CHAR_END_MARKER_2 || char_byte == CHAR_END_MARKER_3 || char_byte == CHAR_END_MARKER_4;
 }
 
-
 int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
 	// return word_length, is_end
 	int wordLength = 0;
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 44ccb7788ec..51859bc238f 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -462,12 +462,11 @@ void PelrockEngine::doAction(byte action, HotSpot *hotspot) {
 void PelrockEngine::talkTo(HotSpot *hotspot) {
 	Sprite *animSet;
 	for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
-		if (i == hotspot->index) {
+		if (_room->_currentRoomAnims[i].index == hotspot->index) {
 			animSet = &_room->_currentRoomAnims[i];
 			break;
 		}
 	}
-	debug("Starting conversation with hotspot %d, animSet pos %d", hotspot->index, animSet->x);
 	_dialog->startConversation(_room->_conversationData, _room->_conversationDataSize, animSet);
 }
 


Commit: dce198bd6e3472486179872589af1c6f2e141a44
    https://github.com/scummvm/scummvm/commit/dce198bd6e3472486179872589af1c6f2e141a44
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:26+02:00

Commit Message:
PELROCK: Enable talking animations

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 6ca9bfaea52..e4300e1347e 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -191,12 +191,16 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		int yPos = 0;
 
 		if (speakerId == ALFRED_COLOR) {
+			g_engine->alfredState.animState = ALFRED_TALKING;
+			_curSprite->isTalking = false;
 			// Offset X position for Alfred to avoid overlapping with his sprite
 			xPos = g_engine->alfredState.x + kAlfredFrameWidth / 2 - maxWidth / 2;
-			yPos = g_engine->alfredState.y - kAlfredFrameHeight - 30; // Above sprite, adjust for line
+			yPos = g_engine->alfredState.y - kAlfredFrameHeight - height; // Above sprite, adjust for line
 		} else {
+			g_engine->alfredState.animState = ALFRED_IDLE;
+			_curSprite->isTalking = true;
 			xPos = _curSprite->x + _curSprite->w / 2 - maxWidth / 2;
-			yPos = _curSprite->y - 30 - height; // Above sprite, adjust for line
+			yPos = _curSprite->y - height; // Above sprite, adjust for line
 		}
 
 		for (int i = 0; i < textLines.size(); i++) {
@@ -204,7 +208,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 			int xPos = 0;
 			int yPos = i * 20; // Above sprite, adjust for line
 
-			debug("Drawing dialogue line %s at (%d, %d), speaker =%d, maxWidth = %d", textLines[i].c_str(), xPos, yPos, speakerId, maxWidth);
+			debug("Drawing dialogue line %s at (%d, %d), speaker =%d, maxWidth = %d, isNPC talking=%d", textLines[i].c_str(), xPos, yPos, speakerId, maxWidth, _curSprite->isTalking ? 1 : 0);
 			g_engine->_largeFont->drawString(&s, textLines[i], xPos, yPos, maxWidth, speakerId, Graphics::kTextAlignCenter);
 
 		}
@@ -238,6 +242,8 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		}
 		g_system->delayMillis(10);
 	}
+	_curSprite->isTalking = false;
+	g_engine->alfredState.animState = ALFRED_IDLE;
 }
 
 void DialogManager::displayDialogue(Common::String text, byte speakerId) {
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 51859bc238f..977d44fe5e6 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -464,6 +464,7 @@ void PelrockEngine::talkTo(HotSpot *hotspot) {
 	for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
 		if (_room->_currentRoomAnims[i].index == hotspot->index) {
 			animSet = &_room->_currentRoomAnims[i];
+			animSet->isTalking = true;
 			break;
 		}
 	}
@@ -743,8 +744,7 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 	int y = sprite->y;
 	int w = animData.w;
 	int h = animData.h;
-	int extra = sprite->extra;
-	if (whichNPCTalking == extra) {
+	if (sprite->isTalking) {
 		drawTalkNPC(sprite);
 		return;
 	}
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index ae9fec2be54..3aa7fe81cfc 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -173,6 +173,7 @@ struct Sprite {
 	byte spriteType;  // 33
 	byte actionFlags; // 34
 	bool isDisabled;  // 38
+	bool isTalking = false;
 	Anim *animData;
 };
 


Commit: 97e87afd0978de7c2edb11a1830e553af9d6606f
    https://github.com/scummvm/scummvm/commit/97e87afd0978de7c2edb11a1830e553af9d6606f
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:26+02:00

Commit Message:
PELROCK: Highlighting of conversation choices

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index e4300e1347e..3ef603438e1 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -151,7 +151,15 @@ void DialogManager::displayChoices(Common::Array<ChoiceOption> *choices, byte *c
 		}
 	}
 	for (int i = 0; i < choices->size(); i++) {
-		drawText(compositeBuffer, g_engine->_doubleSmallFont, (*choices)[i].text, 10, overlayY + 2 + i * kChoiceHeight, 620, 9);
+		ChoiceOption choice = (*choices)[i];
+		int choicePadding = 32;
+		int width = g_engine->_doubleSmallFont->getStringWidth(choice.text);
+		Common::Rect bbox(0, overlayY + i * kChoiceHeight, width + choicePadding * 2, overlayY + i * kChoiceHeight + kChoiceHeight);
+		int color = 14;
+		if (bbox.contains(_events->_mouseX, _events->_mouseY)) {
+			color = 15;
+		}
+		drawText(compositeBuffer, g_engine->_doubleSmallFont, choice.text, choicePadding, overlayY + 2 + i * kChoiceHeight, 620, color);
 	}
 }
 
@@ -179,13 +187,14 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		Common::Array<Common::String> textLines = dialogueLines[curPage];
 
 		int maxWidth = 0;
-		int height = textLines.size() * 20;
+		int height = textLines.size() * 24;
 		for (int i = 0; i < textLines.size(); i++) {
 			maxWidth = MAX(maxWidth, g_engine->_largeFont->getStringWidth(textLines[i]));
 		}
 
 		Graphics::Surface s;
 		s.create(maxWidth, height, Graphics::PixelFormat::createFormatCLUT8());
+		s.fillRect(s.getRect(), 255); // Clear surface
 		s.drawRoundRect(Common::Rect(0, 0, s.getRect().width(), s.getRect().height()), 2, 13, false);
 		int xPos = 0;
 		int yPos = 0;
@@ -208,9 +217,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 			int xPos = 0;
 			int yPos = i * 20; // Above sprite, adjust for line
 
-			debug("Drawing dialogue line %s at (%d, %d), speaker =%d, maxWidth = %d, isNPC talking=%d", textLines[i].c_str(), xPos, yPos, speakerId, maxWidth, _curSprite->isTalking ? 1 : 0);
 			g_engine->_largeFont->drawString(&s, textLines[i], xPos, yPos, maxWidth, speakerId, Graphics::kTextAlignCenter);
-
 		}
 
 		if (xPos + s.getRect().width() > 640) {
@@ -226,7 +233,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 			yPos = 0;
 		}
 
-		_screen->transBlitFrom(s, s.getRect(), Common::Point(xPos, yPos));
+		_screen->transBlitFrom(s, s.getRect(), Common::Point(xPos, yPos), 255);
 		// Present to screen
 		_screen->markAllDirty();
 		_screen->update();
@@ -453,12 +460,10 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		Common::String text;
 		byte speakerId;
 		uint32 endPos = readTextBlock(conversationData, dataSize, position, text, speakerId);
-
 		Common::Array<Common::Array<Common::String>> wrappedText = wordWrap(text);
 
 		// Skip spurious single character artifacts
 		if (!text.empty() && text.size() > 1) {
-			debug("Dialogue: \"%s\" (Speaker ID: %u)", text.c_str(), speakerId);
 			displayDialogue(wrappedText, speakerId);
 		}
 
@@ -508,7 +513,6 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		}
 
 		// 4. Parse choices
-		debug("Parsing choices at pos %u", position);
 		Common::Array<ChoiceOption> choices;
 		parseChoices(conversationData, dataSize, position, choices);
 		debug("Parsed %u choices", choices.size());
@@ -528,7 +532,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		// Check if this is auto-dialogue (only one choice)
 		if (choices.size() == 1) {
 			// Auto-dialogue: display it automatically
-			displayDialogue(choices[0].text, ALFRED_COLOR);
+			debug("Auto-selecting single choice: \"%s\"", choices[0].text.c_str());
 			selectedIndex = 0;
 		} else {
 			// Real choice: show menu and wait for selection
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 977d44fe5e6..6d0a94103cd 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -149,7 +149,10 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		setScreen(0, ALFRED_DOWN);
+		// setScreen(0, ALFRED_DOWN);
+		setScreen(2, ALFRED_LEFT);
+		alfredState.x = 576;
+		alfredState.y = 374;
 	}
 }
 
@@ -508,9 +511,7 @@ void PelrockEngine::renderText(Common::Array<Common::String> lines, int color, i
 void PelrockEngine::chooseAlfredStateAndDraw() {
 	switch (alfredState.animState) {
 	case ALFRED_WALKING: {
-
 		MovementStep step = _currentContext.movementBuffer[_currentStep];
-
 		if (step.distanceX > 0) {
 			if (step.flags & MOVE_RIGHT) {
 				alfredState.direction = ALFRED_RIGHT;
@@ -571,7 +572,9 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			alfredState.curFrame = 0;
 		}
 		drawAlfred(_res->alfredTalkFrames[alfredState.direction][alfredState.curFrame]);
-		alfredState.curFrame++;
+		if(_chrono->getFrameCount() % 2 == 0) {
+			alfredState.curFrame++;
+		}
 		break;
 	case ALFRED_COMB:
 		if (alfredState.curFrame >= 11) {


Commit: 2c4d6eb2908d224b6fd94282eb7edbcbbc03860c
    https://github.com/scummvm/scummvm/commit/2c4d6eb2908d224b6fd94282eb7edbcbbc03860c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:26+02:00

Commit Message:
PELROCK: Fixes word wrap rendering

Changed paths:
    engines/pelrock/dialog.cpp


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 3ef603438e1..e61661ab0db 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -240,12 +240,12 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 
 		if (_events->_leftMouseClicked) {
 			_events->_leftMouseClicked = false;
+			debug("Dialogue click to advance, current page: %d, totalPages: %d", curPage, (int)dialogueLines.size());
 			if (curPage < (int)dialogueLines.size() - 1) {
 				curPage++;
 			} else {
 				break; // Exit dialogue on last page click
 			}
-			break;
 		}
 		g_system->delayMillis(10);
 	}
@@ -461,7 +461,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		byte speakerId;
 		uint32 endPos = readTextBlock(conversationData, dataSize, position, text, speakerId);
 		Common::Array<Common::Array<Common::String>> wrappedText = wordWrap(text);
-
+		debug("Word wrapping %s produces %d pages", text.c_str(), wrappedText.size());
 		// Skip spurious single character artifacts
 		if (!text.empty() && text.size() > 1) {
 			displayDialogue(wrappedText, speakerId);


Commit: d5fd802816b0731b7e703669496bd642eceb15b1
    https://github.com/scummvm/scummvm/commit/d5fd802816b0731b7e703669496bd642eceb15b1
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:27+02:00

Commit Message:
PELROCK: Stop conversation when no more choices are available.

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index e61661ab0db..da445446bc9 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -24,32 +24,6 @@
 #include "pelrock/pelrock.h"
 #include "pelrock/util.h"
 
-// Control character codes (negative values in signed char)
-#define CHAR_SPACE 0x20        /* ' ' */
-#define CHAR_END_MARKER_1 0xFD /* -3 (end of text marker) */
-#define CHAR_END_MARKER_2 0xF4 /* -0xC (alternate end marker) */
-#define CHAR_END_MARKER_3 0xF8 /* -8 (another end marker) */
-#define CHAR_END_MARKER_4 0xF0 /* -0x10 (another end marker) */
-#define CHAR_NEWLINE 0xF6      /* -10 (newline marker) */
-#define CHAR_PAGE_BREAK 0xF9   /* marker inserted when switching pages */
-
-// Conversation control bytes
-#define CTRL_SPEAKER_ID 0x08        /* Next byte is speaker ID (color) */
-#define CTRL_END_TEXT 0xFD          /* End of text segment */
-#define CTRL_TEXT_TERMINATOR 0xFC   /* Text terminator */
-#define CTRL_DIALOGUE_MARKER 0xFB   /* Choice marker */
-#define CTRL_DISABLED_CHOICE 0xFA   /* Disabled choice marker */
-#define CTRL_PAGE_BREAK_CONV 0xF9   /* Page break in conversation */
-#define CTRL_ACTION_TRIGGER 0xF8    /* Action trigger */
-#define CTRL_END_BRANCH 0xF7        /* End of branch */
-#define CTRL_LINE_CONTINUE 0xF6     /* Line continue/newline */
-#define CTRL_ALT_END_MARKER_1 0xF5  /* Alt end marker */
-#define CTRL_END_CONVERSATION 0xF4  /* End conversation */
-#define CTRL_DIALOGUE_MARKER_2 0xF1 /* Alt choice marker */
-#define CTRL_GO_BACK 0xF0           /* Go back in conversation */
-#define CTRL_ALT_END_MARKER_2 0xEB  /* Alt end marker 2 */
-#define CTRL_ALT_END_MARKER_3 0xFE  /* Alt end marker 3 */
-
 namespace Pelrock {
 
 DialogManager::DialogManager(Graphics::Screen *screen, PelrockEventManager *events)
@@ -430,6 +404,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 	debug("Starting conversation with %u bytes of data", dataSize);
 
 	uint32 position = 0;
+	int currentChoiceLevel = -1; // Track the current choice level
 
 	// Skip any junk at start until we find a speaker marker or choice marker
 	while (position < dataSize &&
@@ -517,7 +492,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		parseChoices(conversationData, dataSize, position, choices);
 		debug("Parsed %u choices", choices.size());
 		for (uint i = 0; i < choices.size(); i++) {
-			debug(" Choice %u: \"%s\" (Disabled: %s)", i, choices[i].text.c_str(),
+			debug(" Choice %u (index %d): \"%s\" (Disabled: %s)", i, choices[i].index, choices[i].text.c_str(),
 				  choices[i].isDisabled ? "Yes" : "No");
 		}
 		if (choices.empty()) {
@@ -526,6 +501,23 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 			continue;
 		}
 
+		// Check if we have a currentChoiceLevel and if these choices are at the next level
+		if (currentChoiceLevel >= 0) {
+			// We've already made a choice, check if the current choices are at the next level
+			bool foundNextLevel = false;
+			for (uint i = 0; i < choices.size(); i++) {
+				if (choices[i].index == currentChoiceLevel + 1) {
+					foundNextLevel = true;
+					break;
+				}
+			}
+
+			if (!foundNextLevel) {
+				debug("No choices found at level %d (current is %d), ending conversation", currentChoiceLevel + 1, currentChoiceLevel);
+				break;
+			}
+		}
+
 		// 5. Display choices and get selection
 		int selectedIndex = 0;
 
@@ -552,6 +544,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		// 6. Move position to after the selected choice
 		if (selectedIndex >= 0 && selectedIndex < (int)choices.size()) {
 			position = choices[selectedIndex].dataOffset;
+			currentChoiceLevel = choices[selectedIndex].index;
 
 			// Read and display the selected choice as dialogue
 			Common::String choiceText;
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index f3b7e055ebe..68dddfe8941 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -30,6 +30,33 @@
 #include "pelrock/types.h"
 
 namespace Pelrock {
+
+// Control character codes (negative values in signed char)
+#define CHAR_SPACE 0x20        /* ' ' */
+#define CHAR_END_MARKER_1 0xFD /* -3 (end of text marker) */
+#define CHAR_END_MARKER_2 0xF4 /* -0xC (alternate end marker) */
+#define CHAR_END_MARKER_3 0xF8 /* -8 (another end marker) */
+#define CHAR_END_MARKER_4 0xF0 /* -0x10 (another end marker) */
+#define CHAR_NEWLINE 0xF6      /* -10 (newline marker) */
+#define CHAR_PAGE_BREAK 0xF9   /* marker inserted when switching pages */
+
+// Conversation control bytes
+#define CTRL_SPEAKER_ID 0x08        /* Next byte is speaker ID (color) */
+#define CTRL_END_TEXT 0xFD          /* End of text segment */
+#define CTRL_TEXT_TERMINATOR 0xFC   /* Text terminator */
+#define CTRL_DIALOGUE_MARKER 0xFB   /* Choice marker */
+#define CTRL_DISABLED_CHOICE 0xFA   /* Disabled choice marker */
+#define CTRL_PAGE_BREAK_CONV 0xF9   /* Page break in conversation */
+#define CTRL_ACTION_TRIGGER 0xF8    /* Action trigger */
+#define CTRL_END_BRANCH 0xF7        /* End of branch */
+#define CTRL_LINE_CONTINUE 0xF6     /* Line continue/newline */
+#define CTRL_ALT_END_MARKER_1 0xF5  /* Alt end marker - do nothing */
+#define CTRL_END_CONVERSATION 0xF4  /* End conversation and disable option */
+#define CTRL_DIALOGUE_MARKER_2 0xF1 /* Alt choice marker that disappears */
+#define CTRL_GO_BACK 0xF0           /* Go back in conversation */
+#define CTRL_ALT_END_MARKER_2 0xEB  /* Alt end marker 2 */
+#define CTRL_ALT_END_MARKER_3 0xFE  /* Alt end marker 3 */
+
 /**
  * Structure to hold a parsed choice option
  */
@@ -51,8 +78,7 @@ private:
 	// Private helper functions for conversation parsing
 	void displayDialogue(Common::Array<Common::Array<Common::String>> dialogueLines, byte speakerId);
 	void displayDialogue(Common::String text, byte speakerId);
-	uint32 readTextBlock(const byte *data, uint32 dataSize, uint32 startPos,
-						 Common::String &outText, byte &outSpeakerId);
+	uint32 readTextBlock(const byte *data, uint32 dataSize, uint32 startPos, Common::String &outText, byte &outSpeakerId);
 	uint32 parseChoices(const byte *data, uint32 dataSize, uint32 startPos, Common::Array<ChoiceOption> &outChoices);
 
 	void checkMouse();
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 6d0a94103cd..445f90ed1c2 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1125,8 +1125,8 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 		// Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
 		VerbIcon actionClicked = isActionUnder(x, y);
 		if (actionClicked != NO_ACTION) {
-			doAction(actionClicked, _currentHotspot);
 			_displayPopup = false;
+			doAction(actionClicked, _currentHotspot);
 			return;
 		}
 	}


Commit: 1d9b7dc63b2ceb713b75d0e35a09b096d07dde3e
    https://github.com/scummvm/scummvm/commit/1d9b7dc63b2ceb713b75d0e35a09b096d07dde3e
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:27+02:00

Commit Message:
PELROCK: Applies z-movement to sprites

Changed paths:
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 445f90ed1c2..700ce52f4f0 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -703,7 +703,7 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	delete[] finalBuf;
 }
 
-void applyMovement(int16_t *x, int16_t *y, /*int8_t *z,*/ uint16_t flags) {
+void applyMovement(int16_t *x, int16_t *y, int8_t *z, uint16_t flags) {
 	// X-axis movement
 	if (flags & 0x10) {            // Bit 4: X movement enabled
 		int amount = flags & 0x07; // Bits 0-2: pixels per frame
@@ -724,15 +724,15 @@ void applyMovement(int16_t *x, int16_t *y, /*int8_t *z,*/ uint16_t flags) {
 		}
 	}
 
-	// // Z-axis movement
-	// if (flags & 0x4000) {  // Bit 14: Z movement enabled
-	//     int amount = (flags >> 10) & 0x07;  // Bits 10-12: amount
-	//     if (flags & 0x2000) {  // Bit 13: direction
-	//         *z += amount;   // 1 = forward (add)
-	//     } else {
-	//         *z -= amount;   // 0 = back (subtract)
-	//     }
-	// }
+	// Z-axis movement
+	if (flags & 0x4000) {  // Bit 14: Z movement enabled
+	    int amount = (flags >> 10) & 0x07;  // Bits 10-12: amount
+	    if (flags & 0x2000) {  // Bit 13: direction
+	        *z += amount;   // 1 = forward (add)
+	    } else {
+	        *z -= amount;   // 0 = back (subtract)
+	    }
+	}
 }
 
 void PelrockEngine::drawNextFrame(Sprite *sprite) {
@@ -742,7 +742,7 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 		return;
 	}
 
-	applyMovement(&(sprite->x), &(sprite->y), animData.movementFlags);
+	applyMovement(&(sprite->x), &(sprite->y), &(sprite->zOrder), animData.movementFlags);
 	int x = sprite->x;
 	int y = sprite->y;
 	int w = animData.w;
@@ -758,7 +758,8 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 	extractSingleFrame(animData.animData, frame, curFrame, animData.w, animData.h);
 	drawSpriteToBuffer(_compositeBuffer, 640, frame, sprite->x, sprite->y, sprite->w, sprite->h, 255);
 
-	if (animData.elpapsedFrames == animData.speed) {
+	// if (animData.elpapsedFrames == animData.speed) {
+	if(_chrono->getFrameCount() % animData.speed == 0) {
 		animData.elpapsedFrames = 0;
 		if (animData.curFrame < animData.nframes - 1) {
 			animData.curFrame++;


Commit: d0f1be8645d909ddc21d10e5d01c4ca1cd8b0c3d
    https://github.com/scummvm/scummvm/commit/d0f1be8645d909ddc21d10e5d01c4ca1cd8b0c3d
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:27+02:00

Commit Message:
PELROCK: Animation speeds for talking

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 700ce52f4f0..edc3733c2ef 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -562,8 +562,6 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		}
 
 		drawAlfred(_res->alfredWalkFrames[alfredState.direction][alfredState.curFrame]);
-		// if(alfredFrameSkip) alfredState.curFrame++;
-		// alfredFrameSkip = !alfredFrameSkip;
 		alfredState.curFrame++;
 		break;
 	}
@@ -572,7 +570,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			alfredState.curFrame = 0;
 		}
 		drawAlfred(_res->alfredTalkFrames[alfredState.direction][alfredState.curFrame]);
-		if(_chrono->getFrameCount() % 2 == 0) {
+		if(_chrono->getFrameCount() % kAlfredAnimationSpeed == 0) {
 			alfredState.curFrame++;
 		}
 		break;
@@ -1000,7 +998,17 @@ void PelrockEngine::drawTalkNPC(Sprite *animSet) {
 	int w = index ? animHeader->wAnimB : animHeader->wAnimA;
 	int h = index ? animHeader->hAnimB : animHeader->hAnimA;
 	int numFrames = index ? animHeader->numFramesAnimB : animHeader->numFramesAnimA;
-	int curFrame = index ? animHeader->currentFrameAnimB++ : animHeader->currentFrameAnimA++;
+
+	if(_chrono->getFrameCount() % kTalkAnimationSpeed == 0) {
+		if (index) {
+			animHeader->currentFrameAnimB++;
+		} else {
+			animHeader->currentFrameAnimA++;
+		}
+	}
+
+	byte curFrame = index ? animHeader->currentFrameAnimB : animHeader->currentFrameAnimA;
+
 	if (curFrame >= numFrames) {
 		if (index) {
 			animHeader->currentFrameAnimB = 0;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 3aa7fe81cfc..e2529d55dd4 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -69,6 +69,9 @@ const int kAlfredFrameHeight = 102;
 
 const int kChoiceHeight = 16; // Height of each choice line in pixels
 
+const int kTalkAnimationSpeed = 2; // Frames per update
+const int kAlfredAnimationSpeed = 2; // Frames per update
+
 
 // Direction flags (bit-packed)
 #define MOVE_RIGHT 0x01 // Move right (positive X)
@@ -111,6 +114,7 @@ struct AlfredState {
 	uint16 movementSpeed = 6; // pixels per frame
 	uint16 x = 319;
 	uint16 y = 302;
+	float currentScale = 1.0f;
 };
 
 typedef struct {


Commit: 60d73a5f1c1332cc6d8b0363333eeeac4036e313
    https://github.com/scummvm/scummvm/commit/60d73a5f1c1332cc6d8b0363333eeeac4036e313
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:28+02:00

Commit Message:
PELROCK: Fixes incorrect zorder drawing

Changed paths:
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index edc3733c2ef..ab2ea96cafd 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -314,15 +314,11 @@ void PelrockEngine::copyBackgroundToBuffer() {
 
 void PelrockEngine::updateAnimations() {
 	// Sort sprites by zOrder (persists in the array)
-	sortAnimsByZOrder(_room->_currentRoomAnims);
-
-	// Create temporary render order partitioned by Alfred's Y position
-	Common::Array<Sprite *> renderOrder;
-	int alfredY = alfredState.y;
 
+	sortAnimsByZOrder(_room->_currentRoomAnims);
 	// First pass: sprites behind Alfred (y <= alfredY)
 	for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
-		if (_room->_currentRoomAnims[i].zOrder > 10) {
+		if (_room->_currentRoomAnims[i].zOrder > 10 || _room->_currentRoomAnims[i].zOrder < 0) {
 			drawNextFrame(&_room->_currentRoomAnims[i]);
 		}
 	}
@@ -332,7 +328,7 @@ void PelrockEngine::updateAnimations() {
 
 	// Second pass: sprites in front of Alfred (y > alfredY)
 	for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
-		if (_room->_currentRoomAnims[i].zOrder <= 10) {
+		if (_room->_currentRoomAnims[i].zOrder <= 10 && _room->_currentRoomAnims[i].zOrder >= 0) {
 			drawNextFrame(&_room->_currentRoomAnims[i]);
 		}
 	}


Commit: 2326250c3d599713ee0efcb5300317e0282f8b15
    https://github.com/scummvm/scummvm/commit/2326250c3d599713ee0efcb5300317e0282f8b15
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:28+02:00

Commit Message:
PELROCK: Moves menu handling into its own class

Changed paths:
  A engines/pelrock/menu.cpp
  A engines/pelrock/menu.h
    engines/pelrock/module.mk
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
new file mode 100644
index 00000000000..5d51fc887d8
--- /dev/null
+++ b/engines/pelrock/menu.cpp
@@ -0,0 +1,226 @@
+/* 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 "common/debug.h"
+#include "common/file.h"
+#include "graphics/paletteman.h"
+
+#include "pelrock/menu.h"
+#include "pelrock/offsets.h"
+#include "pelrock/pelrock.h"
+#include "pelrock/util.h"
+#include "menu.h"
+
+namespace Pelrock {
+
+Pelrock::MenuManager::MenuManager(Graphics::Screen *screen, PelrockEventManager *events, ResourceManager *res) : _screen(screen), _events(events), _res(res) {
+
+}
+
+
+void MenuManager::checkMouseClick(int x, int y) {
+
+	bool selectedItem = false;
+	for (int i = 0; i < 4; i++) {
+		if (x >= 140 + (82 * i) && x <= 140 + (82 * i) + 64 &&
+			y >= 115 - (8 * i) && y <= 115 - (8 * i) + 64) {
+			_selectedInvIndex = _curInventoryPage * 4 + i;
+			_menuText = _inventoryDescriptions[_selectedInvIndex];
+			selectedItem = true;
+			return;
+		}
+	}
+	if (!selectedItem) {
+		_selectedInvIndex = -1;
+		_menuText = "";
+	}
+
+	if (x >= 471 && x <= 471 + 23 &&
+		y >= 87 && y <= 87 + 33) {
+		_curInventoryPage++;
+	}
+}
+
+
+void MenuManager::menuLoop() {
+    _events->pollEvent();
+
+	if(_events->_leftMouseClicked) {
+		_events->_leftMouseClicked = false;
+		checkMouseClick(_events->_mouseX, _events->_mouseY);
+	}
+	else if (_events->_rightMouseClicked) {
+		_events->_rightMouseClicked = false;
+		g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
+		g_engine->stateGame = GAME;
+        tearDown();
+	}
+
+	memcpy(_compositeBuffer, _mainMenu, 640 * 400);
+
+	for (int i = 0; i < 4; i++) {
+		int itemIndex = _curInventoryPage * 4 + i;
+		InventoryObject item = _res->getInventoryObject(itemIndex);
+		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 140 + (82 * i), 115 - (8 * i), 60, 60, 1);
+		drawRect(_compositeBuffer, 140 + (82 * i) - 2, 115 - (8 * i) - 2, 64, 64, 255); // Draw border
+	}
+
+	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
+	g_engine->_smallFont->drawString(_screen, _menuText, 230, 200, 200, 255);
+	_screen->markAllDirty();
+	_screen->update();
+}
+
+void MenuManager::loadMenu() {
+
+	bool alternateMenu = false;
+	Common::File alfred7;
+	if (!alfred7.open(Common::Path("ALFRED.7"))) {
+		error("Could not open ALFRED.7");
+		return;
+	}
+
+	_compositeBuffer = new byte[640 * 400];
+	_mainMenu = new byte[640 * 400];
+    loadInventoryDescriptions();
+	if (!alternateMenu) {
+		alfred7.seek(kSettingsPaletteOffset, SEEK_SET);
+		alfred7.read(_mainMenuPalette, 768);
+		for (int i = 0; i < 256; i++) {
+			_mainMenuPalette[i * 3] = _mainMenuPalette[i * 3] << 2;
+			_mainMenuPalette[i * 3 + 1] = _mainMenuPalette[i * 3 + 1] << 2;
+			_mainMenuPalette[i * 3 + 2] = _mainMenuPalette[i * 3 + 2] << 2;
+		}
+
+		uint32 curPos = 0;
+		alfred7.seek(2405266, SEEK_SET);
+		alfred7.read(_mainMenu, 65536);
+
+		curPos += 65536;
+
+		byte *compressedPart1 = new byte[29418];
+		alfred7.read(compressedPart1, 29418);
+		byte *decompressedPart1 = nullptr;
+		size_t decompressedSize = rleDecompress(compressedPart1, 29418, 0, 0, &decompressedPart1, true);
+
+		memcpy(_mainMenu + curPos, decompressedPart1, decompressedSize);
+		curPos += decompressedSize;
+
+		delete[] compressedPart1;
+		delete[] decompressedPart1;
+		alfred7.seek(2500220, SEEK_SET);
+		alfred7.read(_mainMenu + curPos, 32768);
+		curPos += 32768;
+		byte *compressedPart2 = new byte[30288];
+		alfred7.read(compressedPart2, 30288);
+		byte *decompressedPart2 = nullptr;
+		decompressedSize = rleDecompress(compressedPart2, 30288, 0, 0, &decompressedPart2, true);
+
+		memcpy(_mainMenu + curPos, decompressedPart2, decompressedSize);
+		curPos += decompressedSize;
+		debug("Settings menu size loaded: %d, with last block %d", curPos, curPos + 92160);
+		delete[] compressedPart2;
+		delete[] decompressedPart2;
+		alfred7.seek(2563266, SEEK_SET);
+		alfred7.read(_mainMenu + curPos, 92160);
+		alfred7.close();
+	} else {
+		Common::File alfred7;
+		if (!alfred7.open(Common::Path("ALFRED.7"))) {
+			error("Could not open ALFRED.7");
+			return;
+		}
+
+		_mainMenu = new byte[640 * 400];
+
+		alfred7.seek(kAlternateSettingsPaletteOffset, SEEK_SET);
+		alfred7.read(_mainMenuPalette, 768);
+		for (int i = 0; i < 256; i++) {
+			_mainMenuPalette[i * 3] = _mainMenuPalette[i * 3] << 2;
+			_mainMenuPalette[i * 3 + 1] = _mainMenuPalette[i * 3 + 1] << 2;
+			_mainMenuPalette[i * 3 + 2] = _mainMenuPalette[i * 3 + 2] << 2;
+		}
+
+		g_engine->_res->mergeRleBlocks(&alfred7, kAlternateSettingsMenuOffset, 8, _mainMenu);
+		alfred7.close();
+	}
+}
+
+void MenuManager::loadInventoryDescriptions() {
+
+	Common::File exe;
+	if (!exe.open("JUEGO.EXE")) {
+		error("Couldnt find file JUEGO.EXE");
+	}
+	byte *descBuffer = new byte[kInventoryDescriptionsSize];
+	exe.seek(kInventoryDescriptionsOffset, SEEK_SET);
+	exe.read(descBuffer, kInventoryDescriptionsSize);
+	int pos = 0;
+	Common::String desc = "";
+	while (pos < kInventoryDescriptionsSize) {
+		if (descBuffer[pos] == 0xFD) {
+			if (!desc.empty()) {
+				_inventoryDescriptions.push_back(desc);
+				desc = Common::String();
+			}
+			pos++;
+			continue;
+		}
+		if (descBuffer[pos] == 0x00) {
+			pos++;
+			continue;
+		}
+		if (descBuffer[pos] == 0x08) {
+			pos += 2;
+			continue;
+		}
+		if (descBuffer[pos] == 0xC8) {
+			desc.append(1, '\n');
+			pos++;
+			continue;
+		}
+
+		desc.append(1, decodeChar(descBuffer[pos]));
+		if (pos + 1 == kInventoryDescriptionsSize) {
+			_inventoryDescriptions.push_back(desc);
+		}
+		pos++;
+	}
+	delete[] descBuffer;
+
+    exe.seek(0x49209, SEEK_SET);
+
+
+    _defaultText = exe.readString(0xFD, 35);
+    _menuText = _defaultText;
+
+	exe.close();
+}
+
+void MenuManager::tearDown() {
+
+}
+
+Pelrock::MenuManager::~MenuManager() {
+	delete[] _mainMenu;
+}
+
+} // End of namespace Pelrock
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
new file mode 100644
index 00000000000..ba7f44e8859
--- /dev/null
+++ b/engines/pelrock/menu.h
@@ -0,0 +1,56 @@
+/* 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 PELROCK_MENU_H
+#define PELROCK_MENU_H
+
+#include "graphics/screen.h"
+
+#include "pelrock/events.h"
+#include "pelrock/resources.h"
+
+namespace Pelrock {
+
+class MenuManager {
+public:
+    MenuManager(Graphics::Screen *screen, PelrockEventManager *events, ResourceManager *res);
+    ~MenuManager();
+    void menuLoop();
+    void loadMenu();
+	byte _mainMenuPalette[768] = {0};
+private:
+	void checkMouseClick(int x, int y);
+    void loadInventoryDescriptions();
+    void tearDown();
+    Graphics::Screen *_screen = nullptr;
+    PelrockEventManager *_events = nullptr;
+    ResourceManager *_res = nullptr;
+	byte *_mainMenu = nullptr;
+    byte *_compositeBuffer = nullptr;
+    Common::String _defaultText;
+	// Temporary
+	int _selectedInvIndex = 0;
+	int _curInventoryPage = 0;
+	Common::String _menuText;
+	Common::Array<Common::String> _inventoryDescriptions;
+};
+
+} // End of namespace Pelrock
+#endif // PELROCK_MENU_H
diff --git a/engines/pelrock/module.mk b/engines/pelrock/module.mk
index 427fcc99c03..0fd85abbf06 100644
--- a/engines/pelrock/module.mk
+++ b/engines/pelrock/module.mk
@@ -15,7 +15,8 @@ MODULE_OBJS = \
 	video/video.o \
 	pathfinding.o \
 	events.o \
-	dialog.o
+	dialog.o \
+	menu.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_PELROCK), DYNAMIC_PLUGIN)
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index ab2ea96cafd..f94fa27cc51 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -65,6 +65,8 @@ PelrockEngine::~PelrockEngine() {
 	delete _room;
 	delete _res;
 	delete _events;
+	delete _dialog;
+	delete _menu;
 	// if (_bgPopupBalloon)
 	// 	delete[] _bgPopupBalloon;
 }
@@ -88,6 +90,7 @@ Common::Error PelrockEngine::run() {
 	_res = new ResourceManager();
 	_sound = new SoundManager(_mixer);
 	_dialog = new DialogManager(_screen, _events);
+	_menu = new MenuManager(_screen, _events, _res);
 
 	// Set the engine's debugger console
 	setDebugger(new PelrockConsole(this));
@@ -113,7 +116,7 @@ Common::Error PelrockEngine::run() {
 
 		if (stateGame == SETTINGS) {
 			changeCursor(DEFAULT);
-			menuLoop();
+			_menu->menuLoop();
 		} else if (stateGame == GAME) {
 			gameLoop();
 		}
@@ -129,8 +132,8 @@ void PelrockEngine::init() {
 	_res->loadCursors();
 	_res->loadInteractionIcons();
 	_res->loadInventoryItems();
-	_res->loadSettingsMenu();
 	_sound->loadSoundIndex();
+	_menu->loadMenu();
 
 	calculateScalingMasks();
 	_compositeBuffer = new byte[640 * 400];
@@ -299,7 +302,8 @@ void PelrockEngine::checkMouse() {
 		_events->_longClicked = false;
 	}
 	else if(_events->_rightMouseClicked) {
-		g_system->getPaletteManager()->setPalette(_res->_mainMenuPalette, 0, 256);
+
+		g_system->getPaletteManager()->setPalette(_menu->_mainMenuPalette, 0, 256);
 		_events->_rightMouseClicked = false;
 		stateGame = SETTINGS;
 	}
@@ -798,29 +802,6 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 	}
 }
 
-void PelrockEngine::checkMouseClickOnSettings(int x, int y) {
-
-	bool selectedItem = false;
-	for (int i = 0; i < 4; i++) {
-		if (x >= 140 + (82 * i) && x <= 140 + (82 * i) + 64 &&
-			y >= 115 - (8 * i) && y <= 115 - (8 * i) + 64) {
-			selectedInvIndex = curInventoryPage * 4 + i;
-			_menuText = _res->getInventoryObject(selectedInvIndex).description;
-			selectedItem = true;
-			return;
-		}
-	}
-	if (!selectedItem) {
-		selectedInvIndex = -1;
-		_menuText = "";
-	}
-
-	if (x >= 471 && x <= 471 + 23 &&
-		y >= 87 && y <= 87 + 33) {
-		curInventoryPage++;
-	}
-}
-
 void PelrockEngine::calculateScalingMasks() {
 
 	//    for scale_factor in range(CHAR_WIDTH):
@@ -1057,33 +1038,33 @@ void PelrockEngine::gameLoop() {
 	}
 }
 
-void PelrockEngine::menuLoop() {
-	_events->pollEvent();
+// void PelrockEngine::menuLoop() {
+// 	_events->pollEvent();
 
-	if(_events->_leftMouseClicked) {
-		_events->_leftMouseClicked = false;
-		checkMouseClickOnSettings(_events->_mouseX, _events->_mouseY);
-	}
-	else if (_events->_rightMouseClicked) {
-		_events->_rightMouseClicked = false;
-		g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
-		stateGame = GAME;
-	}
+// 	if(_events->_leftMouseClicked) {
+// 		_events->_leftMouseClicked = false;
+// 		checkMouseClickOnSettings(_events->_mouseX, _events->_mouseY);
+// 	}
+// 	else if (_events->_rightMouseClicked) {
+// 		_events->_rightMouseClicked = false;
+// 		g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
+// 		stateGame = GAME;
+// 	}
 
-	memcpy(_compositeBuffer, _res->_mainMenu, 640 * 400);
+// 	memcpy(_compositeBuffer, _res->_mainMenu, 640 * 400);
 
-	for (int i = 0; i < 4; i++) {
-		int itemIndex = curInventoryPage * 4 + i;
-		InventoryObject item = _res->getInventoryObject(itemIndex);
-		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 140 + (82 * i), 115 - (8 * i), 60, 60, 1);
-		drawRect(_compositeBuffer, 140 + (82 * i) - 2, 115 - (8 * i) - 2, 64, 64, 255); // Draw border
-	}
+// 	for (int i = 0; i < 4; i++) {
+// 		int itemIndex = curInventoryPage * 4 + i;
+// 		InventoryObject item = _res->getInventoryObject(itemIndex);
+// 		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 140 + (82 * i), 115 - (8 * i), 60, 60, 1);
+// 		drawRect(_compositeBuffer, 140 + (82 * i) - 2, 115 - (8 * i) - 2, 64, 64, 255); // Draw border
+// 	}
 
-	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
-	_smallFont->drawString(_screen, _menuText, 230, 200, 200, 0);
-	_screen->markAllDirty();
-	_screen->update();
-}
+// 	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
+// 	_smallFont->drawString(_screen, _menuText, 230, 200, 200, 0);
+// 	_screen->markAllDirty();
+// 	_screen->update();
+// }
 
 void PelrockEngine::walkTo(int x, int y) {
 	_currentStep = 0;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 407afeed780..a4a0ef2508a 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -48,6 +48,7 @@
 #include "pelrock/sound.h"
 #include "pelrock/types.h"
 #include "pelrock/video/video.h"
+#include "pelrock/menu.h"
 
 namespace Pelrock {
 
@@ -57,12 +58,12 @@ class PelrockEngine : public Engine {
 private:
 	const ADGameDescription *_gameDescription;
 	Common::RandomSource _randomSource;
-	ResourceManager *_res = nullptr;
 	ChronoManager *_chrono = nullptr;
 	VideoManager *_videoManager = nullptr;
 	SoundManager *_sound = nullptr;
 	PelrockEventManager *_events = nullptr;
 	DialogManager *_dialog = nullptr;
+	MenuManager *_menu = nullptr;
 
 	void init();
 	void loadAnims();
@@ -115,7 +116,6 @@ private:
 	void checkMouseHover();
 	void checkMouseClick(int x, int y);
 	void checkLongMouseClick(int x, int y);
-	void checkMouseClickOnSettings(int x, int y);
 
 	void calculateScalingMasks();
 	ScaleCalculation calculateScaling(int yPos, ScalingParams scalingParams);
@@ -156,14 +156,8 @@ private:
 
 	bool showShadows = false;
 
-	// Temporary
-	int selectedInvIndex = 0;
-	int curInventoryPage = 0;
-	Common::String _menuText;
-
 	// JAVA
 	bool shouldPlayIntro = false;
-	GameState stateGame = INTRO;
 	bool inConversation = false;
 	bool gameInitialized = false;
 	bool screenReady = false;
@@ -181,9 +175,11 @@ protected:
 
 public:
 	Graphics::Screen *_screen = nullptr;
+	ResourceManager *_res = nullptr;
 	RoomManager *_room = nullptr;
 	AlfredState alfredState;
 	byte *_compositeBuffer;             // Working composition buffer
+	GameState stateGame = INTRO;
 
 	SmallFont *_smallFont = nullptr;
 	LargeFont *_largeFont = nullptr;
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index b79af08c68d..3c61c31c45e 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -60,7 +60,6 @@ ResourceManager::~ResourceManager() {
 
 	delete[] alfredCombFrames[0];
 	delete[] alfredCombFrames[1];
-	delete _mainMenu;
 	delete[] _inventoryIcons;
 }
 
@@ -216,7 +215,7 @@ void ResourceManager::loadAlfredAnims() {
 }
 
 void ResourceManager::loadInventoryItems() {
-	loadInventoryDescriptions();
+	// loadInventoryDescriptions();
 	Common::File alfred4File;
 	if (!alfred4File.open("ALFRED.4")) {
 		error("Couldnt find file ALFRED.4");
@@ -230,7 +229,7 @@ void ResourceManager::loadInventoryItems() {
 	for (int i = 0; i < 69; i++) {
 		_inventoryIcons[i].index = i;
 		extractSingleFrame(iconData, _inventoryIcons[i].iconData, i, 60, 60);
-		_inventoryIcons[i].description = _inventoryDescriptions[i];
+		// _inventoryIcons[i].description = _inventoryDescriptions[i];
 	}
 	delete[] iconData;
 }
@@ -254,49 +253,6 @@ Pelrock::Sticker ResourceManager::getSticker(int stickerIndex) {
 	return sticker;
 }
 
-void ResourceManager::loadInventoryDescriptions() {
-	Common::File exe;
-	if (!exe.open("JUEGO.EXE")) {
-		error("Couldnt find file JUEGO.EXE");
-	}
-	byte *descBuffer = new byte[kInventoryDescriptionsSize];
-	exe.seek(kInventoryDescriptionsOffset, SEEK_SET);
-	exe.read(descBuffer, kInventoryDescriptionsSize);
-	int pos = 0;
-	Common::String desc = "";
-	while (pos < kInventoryDescriptionsSize) {
-		if (descBuffer[pos] == 0xFD) {
-			if (!desc.empty()) {
-				_inventoryDescriptions.push_back(desc);
-				desc = Common::String();
-			}
-			pos++;
-			continue;
-		}
-		if (descBuffer[pos] == 0x00) {
-			pos++;
-			continue;
-		}
-		if (descBuffer[pos] == 0x08) {
-			pos += 2;
-			continue;
-		}
-		if(descBuffer[pos] == 0xC8) {
-			desc.append(1, '\n');
-			pos++;
-			continue;
-		}
-
-		desc.append(1, decodeChar(descBuffer[pos]));
-		if (pos + 1 == kInventoryDescriptionsSize) {
-			_inventoryDescriptions.push_back(desc);
-		}
-		pos++;
-	}
-	delete[] descBuffer;
-	exe.close();
-}
-
 InventoryObject ResourceManager::getInventoryObject(byte index) {
 	return _inventoryIcons[index];
 }
@@ -318,78 +274,4 @@ void ResourceManager::mergeRleBlocks(Common::SeekableReadStream *stream, uint32
 	}
 }
 
-void ResourceManager::loadSettingsMenu() {
-
-	bool alternateMenu = false;
-	Common::File alfred7;
-	if (!alfred7.open(Common::Path("ALFRED.7"))) {
-		error("Could not open ALFRED.7");
-		return;
-	}
-
-	_mainMenu = new byte[640 * 400];
-
-	if (!alternateMenu) {
-		alfred7.seek(kSettingsPaletteOffset, SEEK_SET);
-		alfred7.read(_mainMenuPalette, 768);
-		for (int i = 0; i < 256; i++) {
-			_mainMenuPalette[i * 3] = _mainMenuPalette[i * 3] << 2;
-			_mainMenuPalette[i * 3 + 1] = _mainMenuPalette[i * 3 + 1] << 2;
-			_mainMenuPalette[i * 3 + 2] = _mainMenuPalette[i * 3 + 2] << 2;
-		}
-
-		uint32 curPos = 0;
-		alfred7.seek(2405266, SEEK_SET);
-		alfred7.read(_mainMenu, 65536);
-
-		curPos += 65536;
-
-		byte *compressedPart1 = new byte[29418];
-		alfred7.read(compressedPart1, 29418);
-		byte *decompressedPart1 = nullptr;
-		size_t decompressedSize = rleDecompress(compressedPart1, 29418, 0, 0, &decompressedPart1, true);
-
-		memcpy(_mainMenu + curPos, decompressedPart1, decompressedSize);
-		curPos += decompressedSize;
-
-		delete[] compressedPart1;
-		delete[] decompressedPart1;
-		alfred7.seek(2500220, SEEK_SET);
-		alfred7.read(_mainMenu + curPos, 32768);
-		curPos += 32768;
-		byte *compressedPart2 = new byte[30288];
-		alfred7.read(compressedPart2, 30288);
-		byte *decompressedPart2 = nullptr;
-		decompressedSize = rleDecompress(compressedPart2, 30288, 0, 0, &decompressedPart2, true);
-
-		memcpy(_mainMenu + curPos, decompressedPart2, decompressedSize);
-		curPos += decompressedSize;
-		debug("Settings menu size loaded: %d, with last block %d", curPos, curPos + 92160);
-		delete[] compressedPart2;
-		delete[] decompressedPart2;
-		alfred7.seek(2563266, SEEK_SET);
-		alfred7.read(_mainMenu + curPos, 92160);
-		alfred7.close();
-	} else {
-		Common::File alfred7;
-		if (!alfred7.open(Common::Path("ALFRED.7"))) {
-			error("Could not open ALFRED.7");
-			return;
-		}
-
-		_mainMenu = new byte[640 * 400];
-
-		alfred7.seek(kAlternateSettingsPaletteOffset, SEEK_SET);
-		alfred7.read(_mainMenuPalette, 768);
-		for (int i = 0; i < 256; i++) {
-			_mainMenuPalette[i * 3] = _mainMenuPalette[i * 3] << 2;
-			_mainMenuPalette[i * 3 + 1] = _mainMenuPalette[i * 3 + 1] << 2;
-			_mainMenuPalette[i * 3 + 2] = _mainMenuPalette[i * 3 + 2] << 2;
-		}
-
-		mergeRleBlocks(&alfred7, kAlternateSettingsMenuOffset, 8, _mainMenu);
-		alfred7.close();
-	}
-}
-
 } // End of namespace Pelrock
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index dc7d70c7602..2f774dee09e 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -33,13 +33,13 @@ static const int interactingAnimLength = 2;
 
 class ResourceManager {
 private:
-	void mergeRleBlocks(Common::SeekableReadStream *stream, uint32 offset, int numBlocks, byte *outputBuffer);
-	void loadInventoryDescriptions();
 	InventoryObject *_inventoryIcons = nullptr;
+
 public:
 	ResourceManager(/* args */);
 	~ResourceManager();
 
+	void mergeRleBlocks(Common::SeekableReadStream *stream, uint32 offset, int numBlocks, byte *outputBuffer);
 	void loadSettingsMenu();
 	void loadCursors();
 	void loadInteractionIcons();
@@ -61,10 +61,6 @@ public:
 	byte *_cursorMasks[5] = {nullptr};
 	byte *_verbIcons[9] = {nullptr};
 	byte *_popUpBalloon = nullptr;
-
-	byte *_mainMenu = nullptr;
-	byte _mainMenuPalette[768] = {0};
-	Common::Array<Common::String> _inventoryDescriptions;
 };
 
 } // End of namespace Pelrock


Commit: 567ff2ce419769bd67f7044647b7c938deb2d7f6
    https://github.com/scummvm/scummvm/commit/567ff2ce419769bd67f7044647b7c938deb2d7f6
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:28+02:00

Commit Message:
PELROCK: Loads menu texts

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 5d51fc887d8..84a86b3c4f8 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -205,12 +205,43 @@ void MenuManager::loadInventoryDescriptions() {
 		pos++;
 	}
 	delete[] descBuffer;
+    desc = "";
+    byte *textBuffer = new byte[230];
+    exe.seek(0x49203, SEEK_SET);
+	exe.read(textBuffer, 230);
+    pos = 0;
+    while (pos < 230) {
+		if (textBuffer[pos] == 0xFD) {
+			if (!desc.empty()) {
+				_menuTexts.push_back(desc);
+				desc = Common::String();
+			}
+			pos++;
+			continue;
+		}
+		if (textBuffer[pos] == 0x00) {
+			pos++;
+			continue;
+		}
+		if (textBuffer[pos] == 0x08) {
+			pos += 2;
+			continue;
+		}
+		if (textBuffer[pos] == 0xC8) {
+			desc.append(1, '\n');
+			pos++;
+			continue;
+		}
 
-    exe.seek(0x49209, SEEK_SET);
-
+		desc.append(1, decodeChar(textBuffer[pos]));
+		if (pos + 1 == 230) {
+			_menuTexts.push_back(desc);
+		}
+		pos++;
+	}
 
-    _defaultText = exe.readString(0xFD, 35);
-    _menuText = _defaultText;
+    _menuText = _menuTexts[0];
+    delete[] textBuffer;
 
 	exe.close();
 }
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index ba7f44e8859..f4a162b8731 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -38,18 +38,19 @@ public:
 private:
 	void checkMouseClick(int x, int y);
     void loadInventoryDescriptions();
+    void loadMenuTexts();
     void tearDown();
     Graphics::Screen *_screen = nullptr;
     PelrockEventManager *_events = nullptr;
     ResourceManager *_res = nullptr;
 	byte *_mainMenu = nullptr;
     byte *_compositeBuffer = nullptr;
-    Common::String _defaultText;
+    Common::StringArray _menuTexts;
 	// Temporary
 	int _selectedInvIndex = 0;
 	int _curInventoryPage = 0;
 	Common::String _menuText;
-	Common::Array<Common::String> _inventoryDescriptions;
+	Common::StringArray _inventoryDescriptions;
 };
 
 } // End of namespace Pelrock


Commit: ae7169c6f8806b08cb5f1d912a8504d48f286bd7
    https://github.com/scummvm/scummvm/commit/ae7169c6f8806b08cb5f1d912a8504d48f286bd7
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:28+02:00

Commit Message:
PELROCK: Processes new line commands

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 84a86b3c4f8..9dffd67665d 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -50,7 +50,7 @@ void MenuManager::checkMouseClick(int x, int y) {
 	}
 	if (!selectedItem) {
 		_selectedInvIndex = -1;
-		_menuText = "";
+		_menuText = _menuTexts[0];
 	}
 
 	if (x >= 471 && x <= 471 + 23 &&
@@ -84,7 +84,10 @@ void MenuManager::menuLoop() {
 	}
 
 	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
-	g_engine->_smallFont->drawString(_screen, _menuText, 230, 200, 200, 255);
+    for(int i = 0; _menuText.size() > i; i++) {
+        g_engine->_smallFont->drawString(_screen, _menuText[i], 230, 200 + (i * 10), 200, 255);
+    }
+
 	_screen->markAllDirty();
 	_screen->update();
 }
@@ -175,10 +178,14 @@ void MenuManager::loadInventoryDescriptions() {
 	exe.read(descBuffer, kInventoryDescriptionsSize);
 	int pos = 0;
 	Common::String desc = "";
+    Common::StringArray objLines;
 	while (pos < kInventoryDescriptionsSize) {
 		if (descBuffer[pos] == 0xFD) {
 			if (!desc.empty()) {
-				_inventoryDescriptions.push_back(desc);
+
+				objLines.push_back(desc);
+                _inventoryDescriptions.push_back(objLines);
+                objLines.clear();
 				desc = Common::String();
 			}
 			pos++;
@@ -193,14 +200,16 @@ void MenuManager::loadInventoryDescriptions() {
 			continue;
 		}
 		if (descBuffer[pos] == 0xC8) {
-			desc.append(1, '\n');
+            objLines.push_back(desc);
+            desc = Common::String();
 			pos++;
 			continue;
 		}
 
 		desc.append(1, decodeChar(descBuffer[pos]));
 		if (pos + 1 == kInventoryDescriptionsSize) {
-			_inventoryDescriptions.push_back(desc);
+            objLines.push_back(desc);
+			_inventoryDescriptions.push_back(objLines);
 		}
 		pos++;
 	}
@@ -210,10 +219,13 @@ void MenuManager::loadInventoryDescriptions() {
     exe.seek(0x49203, SEEK_SET);
 	exe.read(textBuffer, 230);
     pos = 0;
+    Common::StringArray textLines;
     while (pos < 230) {
 		if (textBuffer[pos] == 0xFD) {
 			if (!desc.empty()) {
-				_menuTexts.push_back(desc);
+                textLines.push_back(desc);
+				_menuTexts.push_back(textLines);
+                textLines.clear();
 				desc = Common::String();
 			}
 			pos++;
@@ -228,14 +240,16 @@ void MenuManager::loadInventoryDescriptions() {
 			continue;
 		}
 		if (textBuffer[pos] == 0xC8) {
-			desc.append(1, '\n');
+            textLines.push_back(desc);
+            desc = Common::String();
 			pos++;
 			continue;
 		}
 
 		desc.append(1, decodeChar(textBuffer[pos]));
 		if (pos + 1 == 230) {
-			_menuTexts.push_back(desc);
+            textLines.push_back(desc);
+			_menuTexts.push_back(textLines);
 		}
 		pos++;
 	}
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index f4a162b8731..1a429f5b7fe 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -45,12 +45,12 @@ private:
     ResourceManager *_res = nullptr;
 	byte *_mainMenu = nullptr;
     byte *_compositeBuffer = nullptr;
-    Common::StringArray _menuTexts;
+    Common::Array<Common::StringArray> _menuTexts;
 	// Temporary
 	int _selectedInvIndex = 0;
 	int _curInventoryPage = 0;
-	Common::String _menuText;
-	Common::StringArray _inventoryDescriptions;
+	Common::StringArray _menuText;
+	Common::Array<Common::StringArray> _inventoryDescriptions;
 };
 
 } // End of namespace Pelrock


Commit: fcb23865fd95e000017cdd617909eaed30976150
    https://github.com/scummvm/scummvm/commit/fcb23865fd95e000017cdd617909eaed30976150
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:29+02:00

Commit Message:
PELROCK: Process colored text

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 9dffd67665d..43576882dc5 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -35,6 +35,31 @@ Pelrock::MenuManager::MenuManager(Graphics::Screen *screen, PelrockEventManager
 
 }
 
+void MenuManager::drawColoredText(Graphics::ManagedSurface *screen, const Common::String &text, int x, int y, int w, Graphics::Font *font) {
+	int currentX = x;
+	uint32 currentColor = 255;
+
+	Common::String segment;
+	for (uint i = 0; i < text.size(); i++) {
+		if (text[i] == '@' && i + 1 < text.size()) {
+			// Draw accumulated segment
+			if (!segment.empty()) {
+				font->drawString(screen, segment, currentX, y, w, currentColor);
+				currentX += font->getStringWidth(segment);
+				segment.clear();
+			}
+			currentColor = text[i + 1];
+			i++; // skip color code
+		} else {
+			segment += text[i];
+		}
+	}
+
+	// Draw remaining segment
+	if (!segment.empty()) {
+		font->drawString(screen, segment, currentX, y, w, currentColor);
+	}
+}
 
 void MenuManager::checkMouseClick(int x, int y) {
 
@@ -61,7 +86,7 @@ void MenuManager::checkMouseClick(int x, int y) {
 
 
 void MenuManager::menuLoop() {
-    _events->pollEvent();
+	_events->pollEvent();
 
 	if(_events->_leftMouseClicked) {
 		_events->_leftMouseClicked = false;
@@ -71,7 +96,7 @@ void MenuManager::menuLoop() {
 		_events->_rightMouseClicked = false;
 		g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
 		g_engine->stateGame = GAME;
-        tearDown();
+		tearDown();
 	}
 
 	memcpy(_compositeBuffer, _mainMenu, 640 * 400);
@@ -80,13 +105,12 @@ void MenuManager::menuLoop() {
 		int itemIndex = _curInventoryPage * 4 + i;
 		InventoryObject item = _res->getInventoryObject(itemIndex);
 		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 140 + (82 * i), 115 - (8 * i), 60, 60, 1);
-		drawRect(_compositeBuffer, 140 + (82 * i) - 2, 115 - (8 * i) - 2, 64, 64, 255); // Draw border
 	}
 
 	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
-    for(int i = 0; _menuText.size() > i; i++) {
-        g_engine->_smallFont->drawString(_screen, _menuText[i], 230, 200 + (i * 10), 200, 255);
-    }
+	for(int i = 0; _menuText.size() > i; i++) {
+		drawColoredText(_screen, _menuText[i], 230, 200 + (i * 10), 200, g_engine->_smallFont);
+	}
 
 	_screen->markAllDirty();
 	_screen->update();
@@ -103,7 +127,7 @@ void MenuManager::loadMenu() {
 
 	_compositeBuffer = new byte[640 * 400];
 	_mainMenu = new byte[640 * 400];
-    loadInventoryDescriptions();
+	loadMenuTexts();
 	if (!alternateMenu) {
 		alfred7.seek(kSettingsPaletteOffset, SEEK_SET);
 		alfred7.read(_mainMenuPalette, 768);
@@ -167,95 +191,71 @@ void MenuManager::loadMenu() {
 	}
 }
 
-void MenuManager::loadInventoryDescriptions() {
-
-	Common::File exe;
-	if (!exe.open("JUEGO.EXE")) {
-		error("Couldnt find file JUEGO.EXE");
-	}
-	byte *descBuffer = new byte[kInventoryDescriptionsSize];
-	exe.seek(kInventoryDescriptionsOffset, SEEK_SET);
-	exe.read(descBuffer, kInventoryDescriptionsSize);
+Common::Array<Common::Array<Common::String>> processTextData(byte *data, size_t size) {
 	int pos = 0;
 	Common::String desc = "";
-    Common::StringArray objLines;
-	while (pos < kInventoryDescriptionsSize) {
-		if (descBuffer[pos] == 0xFD) {
+	Common::StringArray lines;
+	Common::Array<Common::Array<Common::String>> texts;
+	while (pos < size) {
+		if (data[pos] == 0xFD) {
 			if (!desc.empty()) {
 
-				objLines.push_back(desc);
-                _inventoryDescriptions.push_back(objLines);
-                objLines.clear();
+				lines.push_back(desc);
+				texts.push_back(lines);
+				lines.clear();
 				desc = Common::String();
 			}
 			pos++;
 			continue;
 		}
-		if (descBuffer[pos] == 0x00) {
+		if (data[pos] == 0x00) {
 			pos++;
 			continue;
 		}
-		if (descBuffer[pos] == 0x08) {
+		if (data[pos] == 0x08) {
+			byte color = data[pos + 1];
+			desc.append(1, '@');
+			desc.append(1, color);
 			pos += 2;
 			continue;
 		}
-		if (descBuffer[pos] == 0xC8) {
-            objLines.push_back(desc);
-            desc = Common::String();
+		if (data[pos] == 0xC8) {
+			lines.push_back(desc);
+			desc = Common::String();
 			pos++;
 			continue;
 		}
 
-		desc.append(1, decodeChar(descBuffer[pos]));
-		if (pos + 1 == kInventoryDescriptionsSize) {
-            objLines.push_back(desc);
-			_inventoryDescriptions.push_back(objLines);
+		desc.append(1, decodeChar(data[pos]));
+		if (pos + 1 == size) {
+			lines.push_back(desc);
+			texts.push_back(lines);
 		}
 		pos++;
 	}
-	delete[] descBuffer;
-    desc = "";
-    byte *textBuffer = new byte[230];
-    exe.seek(0x49203, SEEK_SET);
-	exe.read(textBuffer, 230);
-    pos = 0;
-    Common::StringArray textLines;
-    while (pos < 230) {
-		if (textBuffer[pos] == 0xFD) {
-			if (!desc.empty()) {
-                textLines.push_back(desc);
-				_menuTexts.push_back(textLines);
-                textLines.clear();
-				desc = Common::String();
-			}
-			pos++;
-			continue;
-		}
-		if (textBuffer[pos] == 0x00) {
-			pos++;
-			continue;
-		}
-		if (textBuffer[pos] == 0x08) {
-			pos += 2;
-			continue;
-		}
-		if (textBuffer[pos] == 0xC8) {
-            textLines.push_back(desc);
-            desc = Common::String();
-			pos++;
-			continue;
-		}
+	return texts;
+}
 
-		desc.append(1, decodeChar(textBuffer[pos]));
-		if (pos + 1 == 230) {
-            textLines.push_back(desc);
-			_menuTexts.push_back(textLines);
-		}
-		pos++;
+void MenuManager::loadMenuTexts() {
+
+	Common::File exe;
+	if (!exe.open("JUEGO.EXE")) {
+		error("Couldnt find file JUEGO.EXE");
 	}
+	byte *descBuffer = new byte[kInventoryDescriptionsSize];
+	exe.seek(kInventoryDescriptionsOffset, SEEK_SET);
+	exe.read(descBuffer, kInventoryDescriptionsSize);
+	_inventoryDescriptions = processTextData(descBuffer, kInventoryDescriptionsSize);
+	delete[] descBuffer;
+
+	Common::String desc = "";
+	byte *textBuffer = new byte[230];
+	exe.seek(0x49203, SEEK_SET);
+	exe.read(textBuffer, 230);
+	_menuTexts = processTextData(textBuffer, 230);
 
-    _menuText = _menuTexts[0];
-    delete[] textBuffer;
+	_menuText = _menuTexts[0];
+	delete[] textBuffer;
 
 	exe.close();
 }
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 1a429f5b7fe..77d5d39b5b6 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -22,6 +22,7 @@
 #define PELROCK_MENU_H
 
 #include "graphics/screen.h"
+#include "graphics/font.h"
 
 #include "pelrock/events.h"
 #include "pelrock/resources.h"
@@ -30,22 +31,22 @@ namespace Pelrock {
 
 class MenuManager {
 public:
-    MenuManager(Graphics::Screen *screen, PelrockEventManager *events, ResourceManager *res);
-    ~MenuManager();
-    void menuLoop();
-    void loadMenu();
+	MenuManager(Graphics::Screen *screen, PelrockEventManager *events, ResourceManager *res);
+	~MenuManager();
+	void menuLoop();
+	void loadMenu();
 	byte _mainMenuPalette[768] = {0};
 private:
 	void checkMouseClick(int x, int y);
-    void loadInventoryDescriptions();
-    void loadMenuTexts();
-    void tearDown();
-    Graphics::Screen *_screen = nullptr;
-    PelrockEventManager *_events = nullptr;
-    ResourceManager *_res = nullptr;
+	void loadMenuTexts();
+	void tearDown();
+	void drawColoredText(Graphics::ManagedSurface *surface, const Common::String &text, int x, int y, int w, Graphics::Font *font);
+	Graphics::Screen *_screen = nullptr;
+	PelrockEventManager *_events = nullptr;
+	ResourceManager *_res = nullptr;
 	byte *_mainMenu = nullptr;
-    byte *_compositeBuffer = nullptr;
-    Common::Array<Common::StringArray> _menuTexts;
+	byte *_compositeBuffer = nullptr;
+	Common::Array<Common::StringArray> _menuTexts;
 	// Temporary
 	int _selectedInvIndex = 0;
 	int _curInventoryPage = 0;


Commit: c45bf2369c5219f9157a5a633cbc7a86b26c5fb9
    https://github.com/scummvm/scummvm/commit/c45bf2369c5219f9157a5a633cbc7a86b26c5fb9
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:29+02:00

Commit Message:
PELROCK: Add and remove Stickers

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 43576882dc5..497f7db4a92 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -191,51 +191,6 @@ void MenuManager::loadMenu() {
 	}
 }
 
-Common::Array<Common::Array<Common::String>> processTextData(byte *data, size_t size) {
-	int pos = 0;
-	Common::String desc = "";
-	Common::StringArray lines;
-	Common::Array<Common::Array<Common::String>> texts;
-	while (pos < size) {
-		if (data[pos] == 0xFD) {
-			if (!desc.empty()) {
-
-				lines.push_back(desc);
-				texts.push_back(lines);
-				lines.clear();
-				desc = Common::String();
-			}
-			pos++;
-			continue;
-		}
-		if (data[pos] == 0x00) {
-			pos++;
-			continue;
-		}
-		if (data[pos] == 0x08) {
-			byte color = data[pos + 1];
-			desc.append(1, '@');
-			desc.append(1, color);
-			pos += 2;
-			continue;
-		}
-		if (data[pos] == 0xC8) {
-			lines.push_back(desc);
-			desc = Common::String();
-			pos++;
-			continue;
-		}
-
-		desc.append(1, decodeChar(data[pos]));
-		if (pos + 1 == size) {
-			lines.push_back(desc);
-			texts.push_back(lines);
-		}
-		pos++;
-	}
-	return texts;
-}
-
 void MenuManager::loadMenuTexts() {
 
 	Common::File exe;
@@ -245,14 +200,14 @@ void MenuManager::loadMenuTexts() {
 	byte *descBuffer = new byte[kInventoryDescriptionsSize];
 	exe.seek(kInventoryDescriptionsOffset, SEEK_SET);
 	exe.read(descBuffer, kInventoryDescriptionsSize);
-	_inventoryDescriptions = processTextData(descBuffer, kInventoryDescriptionsSize);
+	_inventoryDescriptions = _res->processTextData(descBuffer, kInventoryDescriptionsSize);
 	delete[] descBuffer;
 
 	Common::String desc = "";
-	byte *textBuffer = new byte[230];
-	exe.seek(0x49203, SEEK_SET);
-	exe.read(textBuffer, 230);
-	_menuTexts = processTextData(textBuffer, 230);
+	byte *textBuffer = new byte[kMenuTextSize];
+	exe.seek(kMenuTextOffset, SEEK_SET);
+	exe.read(textBuffer, kMenuTextSize);
+	_menuTexts = _res->processTextData(textBuffer, kMenuTextSize);
 
 	_menuText = _menuTexts[0];
 	delete[] textBuffer;
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 03311b8295c..6b939b13e28 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -38,37 +38,85 @@ static const uint32_t kBalloonFramesSize = 24950;
 static const uint32_t ALFRED7_ALFRED_COMB_R = 67768;
 static const uint32_t ALFRED7_ALFRED_COMB_L = 88408;
 
-
 static const uint32_t kAlternateSettingsMenuOffset = 910097;
-static const uint32_t kAlternateSettingsPaletteOffset = 1038141;      // 640 * 480
-static const uint32_t kSettingsPaletteOffset = 0x2884c2;      // 640 * 480
+static const uint32_t kAlternateSettingsPaletteOffset = 1038141; // 640 * 480
+static const uint32_t kSettingsPaletteOffset = 0x2884c2;         // 640 * 480
 
 #define DESCRIPTION_BASE_OFFSET 0x4715D
 #define NUM_DESCRIPTIONS 113
 
 static const uint32 kInventoryDescriptionsOffset = 0x4715D;
 static const uint32 kInventoryDescriptionsSize = 7868;
-
+static const uint32 kMenuTextOffset = 0x49203;
+static const uint32 kMenuTextSize = 230;
+static const uint32 kAlfredResponsesOffset = 0x441DC;
+static const uint32 kAlfredResponsesSize = 12163;
+static const uint32 kCreditsOffset = 0x49F60;
+static const uint32 kCreditsSize = 2540;
 
 const uint32_t pegatina_offsets[137] = {
 	0x000000, 0x00005B, 0x0000B6, 0x000298, 0x00047A, 0x0023C8, 0x004316, 0x004376,
-    0x005119, 0x005EBC, 0x0083ED, 0x008529, 0x0092C4, 0x00A3AA, 0x00B490, 0x00B6A6,
-    0x00C05A, 0x00CA0E, 0x00D3D0, 0x00D46E, 0x00F036, 0x00FB8F, 0x00FC55, 0x0119D7,
-    0x013759, 0x01391F, 0x014A9D, 0x015C1B, 0x017601, 0x018FE7, 0x019048, 0x0190A9,
-    0x01910A, 0x0197F4, 0x019EDE, 0x01A7EC, 0x01B0FA, 0x01B8C4, 0x01C644, 0x01D83A,
-    0x01E104, 0x01E8C6, 0x01F45D, 0x01FBBB, 0x02011D, 0x02052F, 0x020A95, 0x020E5B,
-    0x0210B3, 0x0216E6, 0x021D5E, 0x0233A3, 0x0249E8, 0x025777, 0x026506, 0x028E2B,
-    0x02B82F, 0x02C9D7, 0x02E4CA, 0x02FFBD, 0x03234A, 0x0346D7, 0x036A83, 0x038E2F,
-    0x03B18D, 0x03D4EB, 0x03DEC9, 0x03F813, 0x04115D, 0x045303, 0x0494A9, 0x04955F,
-    0x049615, 0x0496CB, 0x0499E1, 0x049EC7, 0x04A023, 0x04A447, 0x04BA6D, 0x04BFA1,
-    0x04CE33, 0x04CF09, 0x04DB3B, 0x052885, 0x0575CF, 0x05775B, 0x057D79, 0x058397,
-    0x058969, 0x058F50, 0x05A9DB, 0x05C561, 0x05C72E, 0x05C8FB, 0x05EAC1, 0x060C87,
-    0x060D19, 0x060E62, 0x061039, 0x0613C2, 0x061764, 0x061847, 0x062535, 0x062D4B,
-    0x064F11, 0x0670D7, 0x067381, 0x0675A9, 0x0677EF, 0x067A98, 0x067DDE, 0x068115,
-    0x0684E3, 0x068A76, 0x068F30, 0x0693C8, 0x0696AD, 0x06C2C9, 0x06C84D, 0x07095D,
-    0x071854, 0x07274B, 0x073642, 0x074539, 0x075454, 0x0791DA, 0x07CF60, 0x07E4AB,
-    0x07ECED, 0x07F52F, 0x07FD71, 0x080591, 0x080B24, 0x080B84, 0x080F39, 0x0812F5,
-    0x0816B1
+	0x005119, 0x005EBC, 0x0083ED, 0x008529, 0x0092C4, 0x00A3AA, 0x00B490, 0x00B6A6,
+	0x00C05A, 0x00CA0E, 0x00D3D0, 0x00D46E, 0x00F036, 0x00FB8F, 0x00FC55, 0x0119D7,
+	0x013759, 0x01391F, 0x014A9D, 0x015C1B, 0x017601, 0x018FE7, 0x019048, 0x0190A9,
+	0x01910A, 0x0197F4, 0x019EDE, 0x01A7EC, 0x01B0FA, 0x01B8C4, 0x01C644, 0x01D83A,
+	0x01E104, 0x01E8C6, 0x01F45D, 0x01FBBB, 0x02011D, 0x02052F, 0x020A95, 0x020E5B,
+	0x0210B3, 0x0216E6, 0x021D5E, 0x0233A3, 0x0249E8, 0x025777, 0x026506, 0x028E2B,
+	0x02B82F, 0x02C9D7, 0x02E4CA, 0x02FFBD, 0x03234A, 0x0346D7, 0x036A83, 0x038E2F,
+	0x03B18D, 0x03D4EB, 0x03DEC9, 0x03F813, 0x04115D, 0x045303, 0x0494A9, 0x04955F,
+	0x049615, 0x0496CB, 0x0499E1, 0x049EC7, 0x04A023, 0x04A447, 0x04BA6D, 0x04BFA1,
+	0x04CE33, 0x04CF09, 0x04DB3B, 0x052885, 0x0575CF, 0x05775B, 0x057D79, 0x058397,
+	0x058969, 0x058F50, 0x05A9DB, 0x05C561, 0x05C72E, 0x05C8FB, 0x05EAC1, 0x060C87,
+	0x060D19, 0x060E62, 0x061039, 0x0613C2, 0x061764, 0x061847, 0x062535, 0x062D4B,
+	0x064F11, 0x0670D7, 0x067381, 0x0675A9, 0x0677EF, 0x067A98, 0x067DDE, 0x068115,
+	0x0684E3, 0x068A76, 0x068F30, 0x0693C8, 0x0696AD, 0x06C2C9, 0x06C84D, 0x07095D,
+	0x071854, 0x07274B, 0x073642, 0x074539, 0x075454, 0x0791DA, 0x07CF60, 0x07E4AB,
+	0x07ECED, 0x07F52F, 0x07FD71, 0x080591, 0x080B24, 0x080B84, 0x080F39, 0x0812F5,
+	0x0816B1};
+
+const byte pegatina_rooms[140] = {
+	0, 0, 0, 0, 0, 0, 0,                            // Sprites 0-6: Room 0
+	2, 2,                                           // Sprites 7-8: Room 2
+	3, 3, 3, 3, 3, 3, 3, 3,                         // Sprites 9-16: Room 3
+	4, 4, 4, 4, 4,                                  // Sprites 17-21: Room 4
+	5, 5,                                           // Sprites 22-23: Room 5
+	7,                                              // Sprite 24: Room 7
+	8, 8,                                           // Sprites 25-26: Room 8
+	9, 9, 9, 9, 9,                                  // Sprites 27-31: Room 9
+	12, 12,                                         // Sprites 32-33: Room 12
+	13, 13, 13,                                     // Sprites 34-36: Room 13
+	12,                                             // Sprite 37: Room 12
+	15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, // Sprites 38-49: Room 15
+	16, 16,                                         // Sprites 50-51: Room 16
+	17, 17,                                         // Sprites 52-53: Room 17
+	19, 19, 19, 19, 19,                             // Sprites 54-58: Room 19
+
+	0, 0, 0, 0, 0, 0, 0, // Sprites 59-65: Room 0
+	33, 33,              // Sprites 66-67: Room 33
+	29, 29,              // Sprites 68-69: Room 29
+	0, 0, 0,             // Sprites 70-72: Room 0
+
+	34, 35, 31, 25,         // Sprites 73-76: Various rooms
+	31,                     // Sprite 77: Room 31
+	32,                     // Sprite 78: Room 32
+	21, 25,                 // Sprites 79-80: Rooms 21, 25
+	0,                      // Sprite 81: Room 0
+	0, 0, 0, 0, 0,          // Sprites 82-86: Room 0
+	4, 4, 4, 4,             // Sprites 87-90: Room 4
+	0, 0, 0, 0,             // Sprites 91-94: Room 0
+	0, 0, 0, 0, 0, 0,       // Sprites 95-100: Room 0
+	33, 33,                 // Sprites 101-102: Room 33
+	47, 47,                 // Sprites 103-104: Room 47
+	52, 52, 52, 52, 52,     // Sprites 105-109: Room 52
+	52, 52, 52, 52, 52, 52, // Sprites 110-115: Room 52
+	41,                     // Sprite 116: Room 41
+	0,                      // Sprite 117: Room 0
+	30,                     // Sprite 118: Room 30
+	44, 44, 44, 44,         // Sprites 119-122: Room 44
+	31,                     // Sprite 123: Room 31
+	46, 46,                 // Sprites 124-125: Room 46
+	31,                     // Sprite 126: Room 31
+	51, 52, 53, 54          // Sprites 127-130: Various rooms
 };
 
 // Description offsets relative to DESCRIPTION_BASE_OFFSET
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index f94fa27cc51..826b4cd3c2e 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -132,6 +132,8 @@ void PelrockEngine::init() {
 	_res->loadCursors();
 	_res->loadInteractionIcons();
 	_res->loadInventoryItems();
+	_res->loadAlfredResponses();
+
 	_sound->loadSoundIndex();
 	_menu->loadMenu();
 
@@ -246,6 +248,8 @@ void PelrockEngine::renderScene(bool showTextOverlay) {
 		playSoundIfNeeded();
 
 		copyBackgroundToBuffer();
+
+		placeStickers();
 		updateAnimations();
 
 		if (showTextOverlay) {
@@ -286,29 +290,25 @@ void PelrockEngine::renderScene(bool showTextOverlay) {
 		// 	alfredState.curFrame = 0;
 		// }
 
-
 		_screen->markAllDirty();
 	}
 }
 
 void PelrockEngine::checkMouse() {
-	if(_events->_leftMouseClicked) {
+	if (_events->_leftMouseClicked) {
 		checkMouseClick(_events->_mouseClickX, _events->_mouseClickY);
 		_events->_leftMouseClicked = false;
 		_displayPopup = false;
-	}
-	else if(_events->_longClicked) {
+	} else if (_events->_longClicked) {
 		checkLongMouseClick(_events->_mouseClickX, _events->_mouseClickY);
 		_events->_longClicked = false;
-	}
-	else if(_events->_rightMouseClicked) {
+	} else if (_events->_rightMouseClicked) {
 
 		g_system->getPaletteManager()->setPalette(_menu->_mainMenuPalette, 0, 256);
 		_events->_rightMouseClicked = false;
 		stateGame = SETTINGS;
 	}
 	checkMouseHover();
-
 }
 
 void PelrockEngine::copyBackgroundToBuffer() {
@@ -381,8 +381,31 @@ void PelrockEngine::paintDebugLayer() {
 	_smallFont->drawString(_screen, Common::String::format("Frame number: %d", _chrono->getFrameCount()), 0, 30, 640, 13);
 }
 
-void PelrockEngine::animateFadePalette(PaletteAnim *anim) {
+void PelrockEngine::placeStickers() {
+	for (int i = 0; i < _room->_currentRoomStickers.size(); i++) {
+		Sticker sticker = _room->_currentRoomStickers[i];
+		if (sticker.roomNumber == _room->_currentRoomNumber) {
+			placeSticker(sticker);
+		}
+	}
+}
+
+void PelrockEngine::placeSticker(Sticker sticker) {
+	for (int y = 0; y < sticker.h; y++) {
+		for (int x = 0; x < sticker.w; x++) {
+			byte pixel = sticker.stickerData[y * sticker.w + x];
+			if (pixel != 0) {
+				int bgX = sticker.x + x;
+				int bgY = sticker.y + y;
+				if (bgX >= 0 && bgX < 640 && bgY >= 0 && bgY < 400) {
+					_compositeBuffer[bgY * 640 + bgX] = pixel;
+				}
+			}
+		}
+	}
+}
 
+void PelrockEngine::animateFadePalette(PaletteAnim *anim) {
 
 	if (anim->data[0] >= anim->data[6] &&
 		anim->data[1] >= anim->data[7] &&
@@ -457,6 +480,9 @@ void PelrockEngine::doAction(byte action, HotSpot *hotspot) {
 	case OPEN:
 		open(hotspot);
 		break;
+	case CLOSE:
+		close(hotspot);
+		break;
 	default:
 		break;
 	}
@@ -483,10 +509,23 @@ void PelrockEngine::lookAt(HotSpot *hotspot) {
 void PelrockEngine::open(HotSpot *hotspot) {
 	switch (hotspot->extra) {
 	case 261:
-		_room->placeSticker(_res->getSticker(91), _currentBackground);
+		_room->addSticker(_res->getSticker(91));
+		break;
+
+	default:
+
+		break;
+	}
+}
+
+void PelrockEngine::close(HotSpot *hotspot) {
+	switch (hotspot->extra) {
+	case 261:
+		_room->removeSticker(91);
 		break;
 
 	default:
+
 		break;
 	}
 }
@@ -570,7 +609,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			alfredState.curFrame = 0;
 		}
 		drawAlfred(_res->alfredTalkFrames[alfredState.direction][alfredState.curFrame]);
-		if(_chrono->getFrameCount() % kAlfredAnimationSpeed == 0) {
+		if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0) {
 			alfredState.curFrame++;
 		}
 		break;
@@ -616,7 +655,6 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	}
 	int linesToSkip = kAlfredFrameHeight - finalHeight;
 
-
 	int shadowPos = alfredState.y; // - finalHeight;
 	bool shadeCharacter = _room->_pixelsShadows[shadowPos * 640 + alfredState.x] != 0xFF;
 
@@ -723,13 +761,13 @@ void applyMovement(int16_t *x, int16_t *y, int8_t *z, uint16_t flags) {
 	}
 
 	// Z-axis movement
-	if (flags & 0x4000) {  // Bit 14: Z movement enabled
-	    int amount = (flags >> 10) & 0x07;  // Bits 10-12: amount
-	    if (flags & 0x2000) {  // Bit 13: direction
-	        *z += amount;   // 1 = forward (add)
-	    } else {
-	        *z -= amount;   // 0 = back (subtract)
-	    }
+	if (flags & 0x4000) {                  // Bit 14: Z movement enabled
+		int amount = (flags >> 10) & 0x07; // Bits 10-12: amount
+		if (flags & 0x2000) {              // Bit 13: direction
+			*z += amount;                  // 1 = forward (add)
+		} else {
+			*z -= amount; // 0 = back (subtract)
+		}
 	}
 }
 
@@ -757,7 +795,7 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 	drawSpriteToBuffer(_compositeBuffer, 640, frame, sprite->x, sprite->y, sprite->w, sprite->h, 255);
 
 	// if (animData.elpapsedFrames == animData.speed) {
-	if(_chrono->getFrameCount() % animData.speed == 0) {
+	if (_chrono->getFrameCount() % animData.speed == 0) {
 		animData.elpapsedFrames = 0;
 		if (animData.curFrame < animData.nframes - 1) {
 			animData.curFrame++;
@@ -953,12 +991,9 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 
 	VerbIcon icon = isActionUnder(_events->_mouseX, _events->_mouseY);
 	for (int i = 0; i < actions.size(); i++) {
-		if (icon == actions[i] && _iconBlink++ < kIconBlinkPeriod / 2) {
+		if (icon == actions[i] && _chrono->getFrameCount() % kIconBlinkPeriod == 0) {
 			continue;
 		}
-		if (_iconBlink > kIconBlinkPeriod) {
-			_iconBlink = 0;
-		}
 		drawSpriteToBuffer(_compositeBuffer, 640, _res->_verbIcons[actions[i]], posx + 20 + (i * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
 	}
 }
@@ -976,7 +1011,7 @@ void PelrockEngine::drawTalkNPC(Sprite *animSet) {
 	int h = index ? animHeader->hAnimB : animHeader->hAnimA;
 	int numFrames = index ? animHeader->numFramesAnimB : animHeader->numFramesAnimA;
 
-	if(_chrono->getFrameCount() % kTalkAnimationSpeed == 0) {
+	if (_chrono->getFrameCount() % kTalkAnimationSpeed == 0) {
 		if (index) {
 			animHeader->currentFrameAnimB++;
 		} else {
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index a4a0ef2508a..476809f6e49 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -43,12 +43,12 @@
 #include "pelrock/fonts/large_font.h"
 #include "pelrock/fonts/small_font.h"
 #include "pelrock/fonts/small_font_double.h"
+#include "pelrock/menu.h"
 #include "pelrock/resources.h"
 #include "pelrock/room.h"
 #include "pelrock/sound.h"
 #include "pelrock/types.h"
 #include "pelrock/video/video.h"
-#include "pelrock/menu.h"
 
 namespace Pelrock {
 
@@ -94,7 +94,8 @@ private:
 	void presentFrame();
 	void updatePaletteAnimations();
 	void paintDebugLayer();
-
+	void placeStickers();
+	void placeSticker(Sticker sticker);
 
 	void animateFadePalette(PaletteAnim *anim);
 	void animateRotatePalette(PaletteAnim *anim);
@@ -102,6 +103,7 @@ private:
 	void talkTo(HotSpot *hotspot);
 	void lookAt(HotSpot *hotspot);
 	void open(HotSpot *hotspot);
+	void close(HotSpot *hotspot);
 	void renderText(Common::Array<Common::String> lines, int color, int x, int y);
 	void chooseAlfredStateAndDraw();
 	void drawAlfred(byte *buf);
@@ -141,14 +143,12 @@ private:
 	byte *_currentBackground = nullptr; // Clean background - NEVER modified
 
 	bool _displayPopup = false;
-	byte _iconBlink = 0;
 	int _popupX = 0;
 	int _popupY = 0;
 	int _currentPopupFrame = 0;
 
 	HotSpot *_currentHotspot = nullptr;
 
-
 	Common::Point _curWalkTarget;
 	bool isNPCATalking = false;
 	uint16 whichNPCTalking = 0;
@@ -178,7 +178,7 @@ public:
 	ResourceManager *_res = nullptr;
 	RoomManager *_room = nullptr;
 	AlfredState alfredState;
-	byte *_compositeBuffer;             // Working composition buffer
+	byte *_compositeBuffer; // Working composition buffer
 	GameState stateGame = INTRO;
 
 	SmallFont *_smallFont = nullptr;
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 3c61c31c45e..7262f3028fe 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -234,6 +234,79 @@ void ResourceManager::loadInventoryItems() {
 	delete[] iconData;
 }
 
+void ResourceManager::loadAlfredResponses() {
+
+	Common::File exe;
+	if (!exe.open("JUEGO.EXE")) {
+		error("Couldnt find file JUEGO.EXE");
+	}
+	byte *descBuffer = new byte[kAlfredResponsesSize];
+	exe.seek(kAlfredResponsesOffset, SEEK_SET);
+	exe.read(descBuffer, kAlfredResponsesSize);
+	_alfredResponses = processTextData(descBuffer, kAlfredResponsesSize);
+	delete[] descBuffer;
+	exe.close();
+}
+
+Common::Array<Common::StringArray> ResourceManager::getCredits() {
+	Common::File exe;
+	if (!exe.open("JUEGO.EXE")) {
+		error("Couldnt find file JUEGO.EXE");
+	}
+	byte *descBuffer = new byte[kCreditsSize];
+	exe.seek(kCreditsOffset, SEEK_SET);
+	exe.read(descBuffer, kCreditsSize);
+	Common::Array<Common::StringArray> credits = processTextData(descBuffer, kCreditsSize);
+	delete[] descBuffer;
+	exe.close();
+	return credits;
+}
+
+Common::Array<Common::Array<Common::String>> ResourceManager::processTextData(byte *data, size_t size) {
+	int pos = 0;
+	Common::String desc = "";
+	Common::StringArray lines;
+	Common::Array<Common::Array<Common::String>> texts;
+	while (pos < size) {
+		if (data[pos] == 0xFD) {
+			if (!desc.empty()) {
+
+				lines.push_back(desc);
+				texts.push_back(lines);
+				lines.clear();
+				desc = Common::String();
+			}
+			pos++;
+			continue;
+		}
+		if (data[pos] == 0x00) {
+			pos++;
+			continue;
+		}
+		if (data[pos] == 0x08) {
+			byte color = data[pos + 1];
+			desc.append(1, '@');
+			desc.append(1, color);
+			pos += 2;
+			continue;
+		}
+		if (data[pos] == 0xC8) {
+			lines.push_back(desc);
+			desc = Common::String();
+			pos++;
+			continue;
+		}
+
+		desc.append(1, decodeChar(data[pos]));
+		if (pos + 1 == size) {
+			lines.push_back(desc);
+			texts.push_back(lines);
+		}
+		pos++;
+	}
+	return texts;
+}
+
 Pelrock::Sticker ResourceManager::getSticker(int stickerIndex) {
 	Common::File alfred6File;
 	if (!alfred6File.open("ALFRED.6")) {
@@ -247,6 +320,8 @@ Pelrock::Sticker ResourceManager::getSticker(int stickerIndex) {
 	sticker.y = alfred6File.readUint16LE();
 	sticker.w = alfred6File.readByte();
 	sticker.h = alfred6File.readByte();
+	sticker.roomNumber = pegatina_rooms[stickerIndex];
+	sticker.stickerIndex = stickerIndex;
 	sticker.stickerData = new byte[sticker.w * sticker.h];
 	alfred6File.read(sticker.stickerData, sticker.w * sticker.h);
 	alfred6File.close();
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index 2f774dee09e..77535265e62 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -45,6 +45,9 @@ public:
 	void loadInteractionIcons();
 	void loadAlfredAnims();
 	void loadInventoryItems();
+	void loadAlfredResponses();
+	Common::Array<Common::StringArray> getCredits();
+	Common::Array<Common::Array<Common::String>> processTextData(byte *data, size_t size);
 	Sticker getSticker(int stickerIndex);
 	InventoryObject getInventoryObject(byte index);
 	byte *loadExtra();
@@ -61,6 +64,7 @@ public:
 	byte *_cursorMasks[5] = {nullptr};
 	byte *_verbIcons[9] = {nullptr};
 	byte *_popUpBalloon = nullptr;
+	Common::Array<Common::StringArray> _alfredResponses;
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index a705bd009b5..f7cedd90832 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -93,19 +93,20 @@ void RoomManager::getBackground(Common::File *roomFile, int roomOffset, byte *ba
 	}
 }
 
-void RoomManager::placeSticker(Sticker sticker, byte *background) {
-	for (int y = 0; y < sticker.h; y++) {
-		for (int x = 0; x < sticker.w; x++) {
-			byte pixel = sticker.stickerData[y * sticker.w + x];
-			if (pixel != 0) {
-				int bgX = sticker.x + x;
-				int bgY = sticker.y + y;
-				if (bgX >= 0 && bgX < 640 && bgY >= 0 && bgY < 400) {
-					background[bgY * 640 + bgX] = pixel;
-				}
-			}
+void RoomManager::addSticker(Sticker sticker) {
+	_currentRoomStickers.push_back(sticker);
+}
+
+void RoomManager::removeSticker(int stickerIndex) {
+	int index = -1;
+	for (int i = 0; i < _currentRoomStickers.size(); i++) {
+		if (_currentRoomStickers[i].stickerIndex == stickerIndex) {
+			index = i;
+			break;
 		}
 	}
+	if (index != -1 && index < _currentRoomStickers.size())
+		_currentRoomStickers.remove_at(index);
 }
 
 PaletteAnim *RoomManager::getPaletteAnimForRoom(int roomNumber) {
@@ -440,7 +441,7 @@ Common::Array<Description> RoomManager::loadRoomTexts(Common::File *roomFile, in
 	// debug("End of descriptions at position %d", pos);
 	size_t conversationStart = lastDescPos + 1;
 	_conversationDataSize = pair12_size - conversationStart;
-	if(_conversationData != nullptr) {
+	if (_conversationData != nullptr) {
 		delete[] _conversationData;
 	}
 	_conversationData = new byte[_conversationDataSize];
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index c0a3c0f0419..f9018d89d84 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -39,7 +39,8 @@ public:
 	void loadRoomTalkingAnimations(int roomNumber);
 	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
-	void placeSticker(Sticker sticker, byte *background);
+	void addSticker(Sticker sticker);
+	void removeSticker(int index);
 	PaletteAnim *getPaletteAnimForRoom(int roomNumber);
 
 	Common::String getRoomName(int roomNumber) {
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index e2529d55dd4..0ca599f4ffe 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -69,10 +69,9 @@ const int kAlfredFrameHeight = 102;
 
 const int kChoiceHeight = 16; // Height of each choice line in pixels
 
-const int kTalkAnimationSpeed = 2; // Frames per update
+const int kTalkAnimationSpeed = 2;   // Frames per update
 const int kAlfredAnimationSpeed = 2; // Frames per update
 
-
 // Direction flags (bit-packed)
 #define MOVE_RIGHT 0x01 // Move right (positive X)
 #define MOVE_LEFT 0x02  // Move left (negative X)
@@ -118,7 +117,7 @@ struct AlfredState {
 };
 
 typedef struct {
-	uint8_t flags;       /* Direction flags (see MOVE_* constants) */
+	uint8_t flags;      /* Direction flags (see MOVE_* constants) */
 	uint16_t distanceX; // Horizontal distance to move
 	uint16_t distanceY; // Vertical distance to move
 } MovementStep;
@@ -129,7 +128,7 @@ typedef struct {
 typedef struct {
 	uint8_t *pathBuffer;          // Sequence of walkbox indices
 	MovementStep *movementBuffer; // Array of movement steps
-	uint8_t *compressed_path;      // Final compressed path
+	uint8_t *compressed_path;     // Final compressed path
 	uint16_t pathLength;
 	uint16_t movementCount;
 	uint16_t compressed_length;
@@ -166,8 +165,8 @@ struct Exit {
 struct Sprite {
 	int index; // number of the animation in the rooms
 	byte type;
-	int16 x;        // 0
-	int16 y;        // 2
+	int16 x;      // 0
+	int16 y;      // 2
 	int w;        // 4
 	int h;        // 5
 	byte extra;   // 6
@@ -326,8 +325,9 @@ struct PaletteAnimFade {
 	byte curFrameCount = 0;
 };
 
-struct Sticker
-{
+struct Sticker {
+	int roomNumber;
+	int stickerIndex;
 	uint16 x;
 	uint16 y;
 	byte w;
@@ -335,7 +335,6 @@ struct Sticker
 	byte *stickerData;
 };
 
-
 struct PaletteAnimRotate {
 	byte startIndex;
 	byte paletteMode;


Commit: 69fc6068593e8914c6eeb78625f8d4dc6927a310
    https://github.com/scummvm/scummvm/commit/69fc6068593e8914c6eeb78625f8d4dc6927a310
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:29+02:00

Commit Message:
PELROCK: Opens/Closes door and enables exit

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 826b4cd3c2e..22e2834a108 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -511,7 +511,10 @@ void PelrockEngine::open(HotSpot *hotspot) {
 	case 261:
 		_room->addSticker(_res->getSticker(91));
 		break;
-
+	case 268:
+		_room->addSticker(_res->getSticker(93));
+		_room->_currentRoomExits[0].isEnabled = true;
+		break;
 	default:
 
 		break;
@@ -523,7 +526,10 @@ void PelrockEngine::close(HotSpot *hotspot) {
 	case 261:
 		_room->removeSticker(91);
 		break;
-
+	case 268:
+		_room->removeSticker(93);
+		_room->_currentRoomExits[0].isEnabled = false;
+		break;
 	default:
 
 		break;
@@ -977,7 +983,9 @@ Exit *PelrockEngine::isExitUnder(int x, int y) {
 	for (int i = 0; i < _room->_currentRoomExits.size(); i++) {
 		Exit exit = _room->_currentRoomExits[i];
 		if (x >= exit.x && x <= (exit.x + exit.w) &&
-			y >= exit.y && y <= (exit.y + exit.h)) {
+			y >= exit.y && y <= (exit.y + exit.h)
+			// && exit.isEnabled
+		) {
 			return &(_room->_currentRoomExits[i]);
 		}
 	}
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index f7cedd90832..6e3a8db6ba9 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -156,7 +156,7 @@ Common::Array<Exit> RoomManager::loadExits(Common::File *roomFile, int roomOffse
 	for (int i = 0; i < exit_count; i++) {
 		Exit exit;
 		exit.targetRoom = roomFile->readUint16LE();
-		exit.flags = roomFile->readByte();
+		exit.isEnabled = roomFile->readByte();
 		exit.x = roomFile->readUint16LE();
 		exit.y = roomFile->readUint16LE();
 		exit.w = roomFile->readByte();
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 0ca599f4ffe..24179d1cabb 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -159,7 +159,7 @@ struct Exit {
 	int16 targetY;
 	uint16 targetDir;
 	AlfredDirection dir;
-	byte flags;
+	byte isEnabled;
 };
 
 struct Sprite {


Commit: 1ea95983098c9f25b0cd1b6f0ab20d6158e42b58
    https://github.com/scummvm/scummvm/commit/1ea95983098c9f25b0cd1b6f0ab20d6158e42b58
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:30+02:00

Commit Message:
PELROCK: Refactor popup state

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index da445446bc9..00893a9041a 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -31,8 +31,10 @@ DialogManager::DialogManager(Graphics::Screen *screen, PelrockEventManager *even
 }
 
 DialogManager::~DialogManager() {
-	delete _currentChoices;
-	_currentChoices = nullptr;
+	if( _currentChoices ) {
+		delete _currentChoices;
+		_currentChoices = nullptr;
+	}
 }
 
 uint32 DialogManager::readTextBlock(
@@ -274,10 +276,9 @@ int DialogManager::selectChoice(Common::Array<Common::String> &choices, byte *co
  * @param outChoices Output: array of choice options
  * @return The position after parsing choices
  */
-uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 startPos,
-								   Common::Array<ChoiceOption> &outChoices) {
+uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 startPos, Common::Array<ChoiceOption> *outChoices) {
 	uint32 pos = startPos;
-	outChoices.clear();
+	outChoices->clear();
 	int firstChoiceIndex = -1;
 	int choiceCount = 0;
 
@@ -322,7 +323,7 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 					textPos++;
 				}
 
-				outChoices.push_back(opt);
+				outChoices->push_back(opt);
 				choiceCount = 1;
 				pos++;
 				break;
@@ -379,7 +380,7 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 						textPos++;
 					}
 
-					outChoices.push_back(opt);
+					outChoices->push_back(opt);
 					choiceCount++;
 				} else if (choiceIndex < firstChoiceIndex) {
 					// Different choice index - stop scanning
@@ -488,14 +489,14 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		}
 
 		// 4. Parse choices
-		Common::Array<ChoiceOption> choices;
+		Common::Array<ChoiceOption> *choices = new Common::Array<ChoiceOption>();
 		parseChoices(conversationData, dataSize, position, choices);
-		debug("Parsed %u choices", choices.size());
-		for (uint i = 0; i < choices.size(); i++) {
-			debug(" Choice %u (index %d): \"%s\" (Disabled: %s)", i, choices[i].index, choices[i].text.c_str(),
-				  choices[i].isDisabled ? "Yes" : "No");
+		debug("Parsed %u choices", choices->size());
+		for (uint i = 0; i < choices->size(); i++) {
+			debug(" Choice %u (index %d): \"%s\" (Disabled: %s)", i, (*choices)[i].index, (*choices)[i].text.c_str(),
+				  (*choices)[i].isDisabled ? "Yes" : "No");
 		}
-		if (choices.empty()) {
+		if (choices->empty()) {
 			// No choices, continue reading dialogue
 			position = peekPos;
 			continue;
@@ -505,8 +506,8 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		if (currentChoiceLevel >= 0) {
 			// We've already made a choice, check if the current choices are at the next level
 			bool foundNextLevel = false;
-			for (uint i = 0; i < choices.size(); i++) {
-				if (choices[i].index == currentChoiceLevel + 1) {
+			for (uint i = 0; i < choices->size(); i++) {
+				if ((*choices)[i].index == currentChoiceLevel + 1) {
 					foundNextLevel = true;
 					break;
 				}
@@ -522,29 +523,34 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		int selectedIndex = 0;
 
 		// Check if this is auto-dialogue (only one choice)
-		if (choices.size() == 1) {
+		if (choices->size() == 1) {
 			// Auto-dialogue: display it automatically
-			debug("Auto-selecting single choice: \"%s\"", choices[0].text.c_str());
+			debug("Auto-selecting single choice: \"%s\"", (*choices)[0].text.c_str());
 			selectedIndex = 0;
 		} else {
 			// Real choice: show menu and wait for selection
 			Common::Array<Common::String> choiceTexts;
-			for (uint i = 0; i < choices.size(); i++) {
-				if (choices[i].isDisabled) {
-					choiceTexts.push_back("[DISABLED] " + choices[i].text);
+			for (uint i = 0; i < choices->size(); i++) {
+				if ((*choices)[i].isDisabled) {
+					choiceTexts.push_back("[DISABLED] " + (*choices)[i].text);
 				} else {
-					choiceTexts.push_back(choices[i].text);
+					choiceTexts.push_back((*choices)[i].text);
 				}
 			}
-			_currentChoices = &choices;
+
+			if(_currentChoices) {
+				delete _currentChoices;
+				_currentChoices = nullptr;
+			}
+			_currentChoices = choices;
 			// Use displayChoices to show and select
 			selectedIndex = selectChoice(choiceTexts, g_engine->_compositeBuffer);
 		}
 
 		// 6. Move position to after the selected choice
-		if (selectedIndex >= 0 && selectedIndex < (int)choices.size()) {
-			position = choices[selectedIndex].dataOffset;
-			currentChoiceLevel = choices[selectedIndex].index;
+		if (selectedIndex >= 0 && selectedIndex < (int)choices->size()) {
+			position = (*choices)[selectedIndex].dataOffset;
+			currentChoiceLevel = (*choices)[selectedIndex].index;
 
 			// Read and display the selected choice as dialogue
 			Common::String choiceText;
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 68dddfe8941..55111b95f01 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -79,7 +79,7 @@ private:
 	void displayDialogue(Common::Array<Common::Array<Common::String>> dialogueLines, byte speakerId);
 	void displayDialogue(Common::String text, byte speakerId);
 	uint32 readTextBlock(const byte *data, uint32 dataSize, uint32 startPos, Common::String &outText, byte &outSpeakerId);
-	uint32 parseChoices(const byte *data, uint32 dataSize, uint32 startPos, Common::Array<ChoiceOption> &outChoices);
+	uint32 parseChoices(const byte *data, uint32 dataSize, uint32 startPos, Common::Array<ChoiceOption> *outChoices);
 
 	void checkMouse();
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 22e2834a108..ca19c39b00e 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -298,7 +298,7 @@ void PelrockEngine::checkMouse() {
 	if (_events->_leftMouseClicked) {
 		checkMouseClick(_events->_mouseClickX, _events->_mouseClickY);
 		_events->_leftMouseClicked = false;
-		_displayPopup = false;
+		_actionPopupState.isActive = false;
 	} else if (_events->_longClicked) {
 		checkLongMouseClick(_events->_mouseClickX, _events->_mouseClickY);
 		_events->_longClicked = false;
@@ -337,12 +337,12 @@ void PelrockEngine::updateAnimations() {
 		}
 	}
 
-	if (_displayPopup) {
-		showActionBalloon(_popupX, _popupY, _currentPopupFrame);
-		if (_currentPopupFrame < 3) {
-			_currentPopupFrame++;
+	if (_actionPopupState.isActive) {
+		showActionBalloon(_actionPopupState.x, _actionPopupState.y, _actionPopupState.curFrame);
+		if (_actionPopupState.curFrame < 3) {
+			_actionPopupState.curFrame++;
 		} else
-			_currentPopupFrame = 0;
+			_actionPopupState.curFrame = 0;
 	}
 }
 
@@ -503,7 +503,7 @@ void PelrockEngine::talkTo(HotSpot *hotspot) {
 void PelrockEngine::lookAt(HotSpot *hotspot) {
 	walkTo(_currentHotspot->x, _currentHotspot->y);
 	// sayAlfred(_room->_currentRoomDescriptions[_currentHotspot->index].text);
-	_displayPopup = false;
+	_actionPopupState.isActive = false;
 }
 
 void PelrockEngine::open(HotSpot *hotspot) {
@@ -790,7 +790,7 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 	int w = animData.w;
 	int h = animData.h;
 	if (sprite->isTalking) {
-		drawTalkNPC(sprite);
+		animateTalkingNPC(sprite);
 		return;
 	}
 
@@ -829,19 +829,19 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 
 	if (hotspotIndex != -1) {
 
-		_popupX = alfredState.x - kBalloonWidth / 2;
-		if (_popupX < 0)
-			_popupX = 0;
-		if (_popupX + kBalloonWidth > 640) {
-			_popupX = 640 - kBalloonWidth;
+		_actionPopupState.x = alfredState.x - kBalloonWidth / 2;
+		if (_actionPopupState.x < 0)
+			_actionPopupState.x = 0;
+		if (_actionPopupState.x + kBalloonWidth > 640) {
+			_actionPopupState.x = 640 - kBalloonWidth;
 		}
 
-		_popupY = alfredState.y - kAlfredFrameHeight - kBalloonHeight;
-		if (_popupY < 0) {
-			_popupY = 0;
+		_actionPopupState.y = alfredState.y - kAlfredFrameHeight - kBalloonHeight;
+		if (_actionPopupState.y < 0) {
+			_actionPopupState.y = 0;
 		}
-		_displayPopup = true;
-		_currentPopupFrame = 0;
+		_actionPopupState.isActive = true;
+		_actionPopupState.curFrame = 0;
 		_currentHotspot = &_room->_currentRoomHotspots[hotspotIndex];
 	}
 }
@@ -1006,7 +1006,7 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	}
 }
 
-void PelrockEngine::drawTalkNPC(Sprite *animSet) {
+void PelrockEngine::animateTalkingNPC(Sprite *animSet) {
 	// Change with the right index
 
 	int index = animSet->index;
@@ -1121,8 +1121,8 @@ void PelrockEngine::walkTo(int x, int y) {
 VerbIcon PelrockEngine::isActionUnder(int x, int y) {
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
 	for (int i = 0; i < actions.size(); i++) {
-		int actionX = _popupX + 20 + (i * (kVerbIconWidth + 2));
-		int actionY = _popupY + 20;
+		int actionX = _actionPopupState.x + 20 + (i * (kVerbIconWidth + 2));
+		int actionY = _actionPopupState.y + 20;
 		Common::Rect actionRect = Common::Rect(actionX, actionY, actionX + kVerbIconWidth, actionY + kVerbIconHeight);
 		if (actionRect.contains(x, y)) {
 
@@ -1150,17 +1150,17 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 	if (whichNPCTalking)
 		whichNPCTalking = false;
 
-	if (_displayPopup) {
+	if (_actionPopupState.isActive) {
 		// Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
 		VerbIcon actionClicked = isActionUnder(x, y);
 		if (actionClicked != NO_ACTION) {
-			_displayPopup = false;
+			_actionPopupState.isActive = false;
 			doAction(actionClicked, _currentHotspot);
 			return;
 		}
 	}
 
-	_displayPopup = false;
+	_actionPopupState.isActive = false;
 	_currentHotspot = nullptr;
 
 	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, _events->_mouseX, _events->_mouseY);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 476809f6e49..8e2201d200d 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -109,7 +109,7 @@ private:
 	void drawAlfred(byte *buf);
 	void drawNextFrame(Sprite *animSet);
 	void changeCursor(Cursor cursor);
-	void drawTalkNPC(Sprite *animSet);
+	void animateTalkingNPC(Sprite *animSet);
 	void playSoundIfNeeded();
 
 	void gameLoop();
@@ -142,10 +142,7 @@ private:
 
 	byte *_currentBackground = nullptr; // Clean background - NEVER modified
 
-	bool _displayPopup = false;
-	int _popupX = 0;
-	int _popupY = 0;
-	int _currentPopupFrame = 0;
+	ActionPopupState _actionPopupState;
 
 	HotSpot *_currentHotspot = nullptr;
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 24179d1cabb..1e782187187 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -105,6 +105,14 @@ enum AlfredDirection {
 	ALFRED_UP = 3
 };
 
+struct ActionPopupState {
+	bool isActive = false;
+	int curFrame = 0;
+	int x = 0;
+	int y = 0;
+	int displayTime = 0;
+};
+
 struct AlfredState {
 	AlfredAnimState animState = ALFRED_IDLE;
 	AlfredAnimState nextState = ALFRED_IDLE;


Commit: 0d4a8eee0a0cf9ffd155049cc2ad652bae045704
    https://github.com/scummvm/scummvm/commit/0d4a8eee0a0cf9ffd155049cc2ad652bae045704
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:30+02:00

Commit Message:
PELROCK: Loads extra screens

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 00893a9041a..8321f6a03e5 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -177,7 +177,9 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 
 		if (speakerId == ALFRED_COLOR) {
 			g_engine->alfredState.animState = ALFRED_TALKING;
-			_curSprite->isTalking = false;
+			if(_curSprite != nullptr) {
+				_curSprite->isTalking = false;
+			}
 			// Offset X position for Alfred to avoid overlapping with his sprite
 			xPos = g_engine->alfredState.x + kAlfredFrameWidth / 2 - maxWidth / 2;
 			yPos = g_engine->alfredState.y - kAlfredFrameHeight - height; // Above sprite, adjust for line
@@ -225,7 +227,9 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		}
 		g_system->delayMillis(10);
 	}
-	_curSprite->isTalking = false;
+	if(_curSprite != nullptr) {
+		_curSprite->isTalking = false;
+	}
 	g_engine->alfredState.animState = ALFRED_IDLE;
 }
 
@@ -578,6 +582,23 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 	// Note: The caller should set inConversation = false after this returns
 }
 
+void DialogManager::sayAlfred(Common::String text) {
+	g_engine->alfredState.nextState = ALFRED_TALKING;
+	g_engine->alfredState.curFrame = 0;
+
+	Common::Array<Common::Array<Common::String>> textLines = wordWrap(text);
+
+	displayDialogue(textLines, ALFRED_COLOR);
+
+}
+
+void DialogManager::sayAlfred(Description description) {
+	sayAlfred(description.text);
+	if( description.isAction) {
+		g_engine->performActionTrigger(description.actionTrigger);
+	}
+}
+
 bool isEndMarker(char char_byte) {
 	return char_byte == CHAR_END_MARKER_1 || char_byte == CHAR_END_MARKER_2 || char_byte == CHAR_END_MARKER_3 || char_byte == CHAR_END_MARKER_4;
 }
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 55111b95f01..ffadb6ab073 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -90,6 +90,9 @@ public:
 	void displayChoices(Common::Array<ChoiceOption> *choices, byte *compositeBuffer);
 	int selectChoice(Common::Array<Common::String> &choices, byte *compositeBuffer);
 	void startConversation(const byte *conversationData, uint32 dataSize, Sprite *alfredAnimSet = nullptr);
+	void sayAlfred(Common::String text);
+	void sayAlfred(Description description);
+
 	Common::Array<Common::Array<Common::String>> wordWrap(Common::String text);
 	Common::Array<ChoiceOption> *_currentChoices = nullptr;
 };
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 6b939b13e28..fa80338d70f 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -236,5 +236,64 @@ const uint16_t description_offsets[NUM_DESCRIPTIONS] = {
 	0x23A4, // Object 112: Una caja de condone
 };
 
+struct ExtraImages {
+	uint32 offset;
+	uint32 paletteOffset;
+	byte numChunks;
+};
+
+const ExtraImages extraScreens[] = {
+	{
+		0x00, // Portrait above bed
+		0x7984,
+		8
+	},
+	{
+		0x1A9EE, // Computer screen
+		0x305A2,
+		8
+	},
+	{
+		0x647C3, // Alfred circle
+		0x7B6B1,
+		4
+	},
+	{
+		0x6FBC9, // Recipe
+		0x7B6B1,
+		8
+	},
+	{
+		0x7BA11, // Newspaper
+		0x88745,
+		8
+	},
+	{
+		0x9237B, // tablet
+		0xB0EE7,
+		8
+	},
+	{
+		0xB11ED, // map
+		0xDE011,
+		8
+	},
+	{
+		0xFFC47, // girl book
+		0x1180C9,
+		8
+	},
+	{
+		0x1183C5, // book
+		0x1358F3,
+		8
+	},
+	{
+		0x152A88, // portrait
+		0x15BFC8,
+		8
+	},
+};
+
 } // End of namespace Pelrock
 #endif
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index ca19c39b00e..5cbfca2a5f9 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -294,6 +294,26 @@ void PelrockEngine::renderScene(bool showTextOverlay) {
 	}
 }
 
+void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
+	debug("Performing action trigger: %d", actionTrigger);
+	switch (actionTrigger) {
+	case 257:
+		byte *palette = new byte[768];
+		if (_extraScreen == nullptr) {
+			_extraScreen = new byte[640 * 400];
+		}
+		_res->getExtraScreen(9, _extraScreen, palette);
+
+		g_system->getPaletteManager()->setPalette(palette, 0, 256);
+		showExtraScreen();
+
+		_dialog->sayAlfred(_res->_alfredResponses[0][0]); // "I found something interesting!");
+
+		delete[] palette;
+		break;
+	}
+}
+
 void PelrockEngine::checkMouse() {
 	if (_events->_leftMouseClicked) {
 		checkMouseClick(_events->_mouseClickX, _events->_mouseClickY);
@@ -502,7 +522,7 @@ void PelrockEngine::talkTo(HotSpot *hotspot) {
 
 void PelrockEngine::lookAt(HotSpot *hotspot) {
 	walkTo(_currentHotspot->x, _currentHotspot->y);
-	// sayAlfred(_room->_currentRoomDescriptions[_currentHotspot->index].text);
+	_dialog->sayAlfred(_room->_currentRoomDescriptions[_currentHotspot->index]);
 	_actionPopupState.isActive = false;
 }
 
@@ -1081,33 +1101,24 @@ void PelrockEngine::gameLoop() {
 	}
 }
 
-// void PelrockEngine::menuLoop() {
-// 	_events->pollEvent();
-
-// 	if(_events->_leftMouseClicked) {
-// 		_events->_leftMouseClicked = false;
-// 		checkMouseClickOnSettings(_events->_mouseX, _events->_mouseY);
-// 	}
-// 	else if (_events->_rightMouseClicked) {
-// 		_events->_rightMouseClicked = false;
-// 		g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
-// 		stateGame = GAME;
-// 	}
-
-// 	memcpy(_compositeBuffer, _res->_mainMenu, 640 * 400);
+void PelrockEngine::showExtraScreen() {
+	memcpy(_screen->getPixels(), _extraScreen, 640 * 400);
+	_screen->markAllDirty();
+	_screen->update();
+	while (!shouldQuit()) {
+		_events->pollEvent();
 
-// 	for (int i = 0; i < 4; i++) {
-// 		int itemIndex = curInventoryPage * 4 + i;
-// 		InventoryObject item = _res->getInventoryObject(itemIndex);
-// 		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 140 + (82 * i), 115 - (8 * i), 60, 60, 1);
-// 		drawRect(_compositeBuffer, 140 + (82 * i) - 2, 115 - (8 * i) - 2, 64, 64, 255); // Draw border
-// 	}
+		if (_events->_leftMouseClicked) {
+			_events->_leftMouseClicked = false;
+			break;
+		}
+		g_system->delayMillis(10);
+	}
 
-// 	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
-// 	_smallFont->drawString(_screen, _menuText, 230, 200, 200, 0);
-// 	_screen->markAllDirty();
-// 	_screen->update();
-// }
+	g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
+	free(_extraScreen);
+	_extraScreen = nullptr;
+}
 
 void PelrockEngine::walkTo(int x, int y) {
 	_currentStep = 0;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 8e2201d200d..13668978b52 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -74,7 +74,6 @@ private:
 	void walkTo(int x, int y);
 
 	void talk(byte object);
-	void sayAlfred(Common::String text);
 	void sayNPC(Sprite *anim, Common::String text, byte color);
 
 	byte *grabBackgroundSlice(int x, int y, int w, int h);
@@ -114,6 +113,7 @@ private:
 
 	void gameLoop();
 	void menuLoop();
+	void showExtraScreen();
 
 	void checkMouseHover();
 	void checkMouseClick(int x, int y);
@@ -141,7 +141,7 @@ private:
 	bool isAlkfredWalking = false;
 
 	byte *_currentBackground = nullptr; // Clean background - NEVER modified
-
+	byte *_extraScreen = nullptr;
 	ActionPopupState _actionPopupState;
 
 	HotSpot *_currentHotspot = nullptr;
@@ -182,6 +182,7 @@ public:
 	LargeFont *_largeFont = nullptr;
 	DoubleSmallFont *_doubleSmallFont = nullptr;
 	void renderScene(bool showTextOverlay = false);
+	void performActionTrigger(uint16 actionTrigger);
 
 public:
 	PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc);
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 7262f3028fe..95123581d55 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -248,6 +248,23 @@ void ResourceManager::loadAlfredResponses() {
 	exe.close();
 }
 
+void ResourceManager::getExtraScreen(int screenIndex, byte *screenBuf, byte *palette) {
+	Common::File alfred7;
+	if (!alfred7.open("ALFRED.7")) {
+		error("Couldnt find file ALFRED.7");
+	}
+	ExtraImages screen = extraScreens[screenIndex];
+	mergeRleBlocks(&alfred7, screen.offset, 8, screenBuf);
+	alfred7.seek(screen.paletteOffset, SEEK_SET);
+	alfred7.read(palette, 768);
+	for (int i = 0; i < 256; i++) {
+		palette[i * 3] = palette[i * 3] << 2;
+		palette[i * 3 + 1] = palette[i * 3 + 1] << 2;
+		palette[i * 3 + 2] = palette[i * 3 + 2] << 2;
+	}
+	alfred7.close();
+}
+
 Common::Array<Common::StringArray> ResourceManager::getCredits() {
 	Common::File exe;
 	if (!exe.open("JUEGO.EXE")) {
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index 77535265e62..e026f15813f 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -46,6 +46,7 @@ public:
 	void loadAlfredAnims();
 	void loadInventoryItems();
 	void loadAlfredResponses();
+	void getExtraScreen(int screenIndex, byte *screenBuf, byte *palette);
 	Common::Array<Common::StringArray> getCredits();
 	Common::Array<Common::Array<Common::String>> processTextData(byte *data, size_t size);
 	Sticker getSticker(int stickerIndex);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 6e3a8db6ba9..1dc41fdbe3a 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -428,6 +428,9 @@ Common::Array<Description> RoomManager::loadRoomTexts(Common::File *roomFile, in
 				}
 				if (data[pos] == 0xF8) {
 					description.actionTrigger = data[pos + 1] | data[pos + 2] << 8;
+					if( description.actionTrigger != 0 ) {
+						description.isAction = true;
+					}
 					pos += 2;
 					break;
 				}


Commit: eb5618572cb745969e8ce81738468fc792d6d064
    https://github.com/scummvm/scummvm/commit/eb5618572cb745969e8ce81738468fc792d6d064
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:30+02:00

Commit Message:
PELROCK: Queued actions

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 5cbfca2a5f9..f2c6dc3e25d 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -521,7 +521,6 @@ void PelrockEngine::talkTo(HotSpot *hotspot) {
 }
 
 void PelrockEngine::lookAt(HotSpot *hotspot) {
-	walkTo(_currentHotspot->x, _currentHotspot->y);
 	_dialog->sayAlfred(_room->_currentRoomDescriptions[_currentHotspot->index]);
 	_actionPopupState.isActive = false;
 }
@@ -608,6 +607,10 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			_currentStep++;
 			if (_currentStep >= _currentContext.movementCount) {
 				_currentStep = 0;
+				if(_queuedAction.isQueued) {
+					doAction(_queuedAction.verb, &_room->_currentRoomHotspots[_queuedAction.hotspotIndex]);
+					_queuedAction.isQueued = false;
+				}
 				alfredState.animState = ALFRED_IDLE;
 			}
 		} else {
@@ -1136,7 +1139,6 @@ VerbIcon PelrockEngine::isActionUnder(int x, int y) {
 		int actionY = _actionPopupState.y + 20;
 		Common::Rect actionRect = Common::Rect(actionX, actionY, actionX + kVerbIconWidth, actionY + kVerbIconHeight);
 		if (actionRect.contains(x, y)) {
-
 			return actions[i];
 		}
 	}
@@ -1158,19 +1160,21 @@ bool PelrockEngine::isAlfredUnder(int x, int y) {
 
 void PelrockEngine::checkMouseClick(int x, int y) {
 
-	if (whichNPCTalking)
-		whichNPCTalking = false;
-
 	if (_actionPopupState.isActive) {
 		// Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
 		VerbIcon actionClicked = isActionUnder(x, y);
 		if (actionClicked != NO_ACTION) {
 			_actionPopupState.isActive = false;
-			doAction(actionClicked, _currentHotspot);
-			return;
+			if(_currentHotspot != nullptr) {
+				walkTo(_currentHotspot->x + _currentHotspot->w / 2, _currentHotspot->y + _currentHotspot->h);
+				_queuedAction = QueuedAction{actionClicked, _currentHotspot->index, true};
+				return;
+			}
 		}
 	}
 
+	_queuedAction = QueuedAction{NO_ACTION, -1, false};
+
 	_actionPopupState.isActive = false;
 	_currentHotspot = nullptr;
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 13668978b52..8c0cc2def13 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -147,9 +147,7 @@ private:
 	HotSpot *_currentHotspot = nullptr;
 
 	Common::Point _curWalkTarget;
-	bool isNPCATalking = false;
-	uint16 whichNPCTalking = 0;
-	bool isNPCBTalking = false;
+	QueuedAction _queuedAction;
 
 	bool showShadows = false;
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 1e782187187..330a5696b69 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -287,6 +287,12 @@ struct WalkBox {
 	byte flags;
 };
 
+struct QueuedAction {
+	VerbIcon verb;
+	int hotspotIndex;
+	bool isQueued;
+};
+
 struct ScalingParams {
 	int16 yThreshold;
 	byte scaleDivisor;


Commit: 7728e3ea128699585f80485ce4d05f6e3b297dee
    https://github.com/scummvm/scummvm/commit/7728e3ea128699585f80485ce4d05f6e3b297dee
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:30+02:00

Commit Message:
PELROCK: Improve exit calculation

Changed paths:
    engines/pelrock/pathfinding.cpp
    engines/pelrock/pathfinding.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/pathfinding.cpp b/engines/pelrock/pathfinding.cpp
index 32254c551d3..bc78b4c68ea 100644
--- a/engines/pelrock/pathfinding.cpp
+++ b/engines/pelrock/pathfinding.cpp
@@ -22,6 +22,7 @@
 #include "common/scummsys.h"
 
 #include "pelrock/pathfinding.h"
+#include "pelrock/types.h"
 #include "pelrock/util.h"
 
 namespace Pelrock {
@@ -60,7 +61,7 @@ bool findPath(int sourceX, int sourceY, int targetX, int targetY, Common::Array<
 
 	int startX = sourceX;
 	int startY = sourceY;
-	Common::Point target = calculateWalkTarget(walkboxes, targetX, targetY);
+	Common::Point target = calculateWalkTarget(walkboxes, targetX, targetY, false, nullptr);
 	targetX = target.x;
 	targetY = target.y;
 	debug("Startx= %d, starty= %d, destx= %d, desty= %d", startX, startY, targetX, targetY);
@@ -118,67 +119,97 @@ bool findPath(int sourceX, int sourceY, int targetX, int targetY, Common::Array<
 	return true;
 }
 
-Common::Point calculateWalkTarget(Common::Array<WalkBox> &walkboxes, int x, int y) {
-	// Starting point for pathfinding
-	int sourceX = x;
-	int sourceY = y;
+Common::Point calculateWalkTarget(Common::Array<WalkBox> &walkboxes,
+								  int sourceX, int sourceY,
+								  bool mouseHoverState,
+								  HotSpot *hotspot) {
+
+	// // Step 1: Determine actual source point
+	// if (mouseHoverState == 1) {
+	//     // Hovering over sprite - check if it has action flags or is animated
+	//     Sprite *sprite = getSprite(hotspotSpriteIndex);
+	//     if (sprite->actionFlags != 0 || sprite->frameCount != 1) {
+	//         sourceX = sprite->x + sprite->width / 2;
+	//         sourceY = sprite->y + sprite->height;
+	//     }
+	// }
+	// else if (mouseHoverState == 2) {
+	//     // Hovering over hotspot - use hotspot center-bottom
+	//     Hotspot *hotspot = getHotspot(hotspotSpriteIndex);
+	//     sourceX = hotspot->x + hotspot->width / 2;
+	//     sourceY = hotspot->y + hotspot->height;
+	// }
+
+	if (mouseHoverState == 1) {
+		// Hovering over hotspot - use hotspot center-bottom
+		sourceX = hotspot->x + hotspot->w / 2;
+		sourceY = hotspot->y + hotspot->h;
+	}
 
-	// TODO: If hovering over a sprite/hotspot, adjust source point to sprite center
-	// For now, just use mouse position
+	// else: use sourceX, sourceY as passed (mouse position)
 
-	// Find nearest walkable point in walkboxes
-	uint32 minDistance = 0xFFFFFFFF;
-	Common::Point bestTarget(sourceX, sourceY);
+	// Step 2: Find nearest walkbox
+	uint32 minDistance = 0xFFFF;
+	int bestXDistance = 0;
+	int bestYDistance = 0;
+	int bestXDirection = 0; // 0 = left/subtract, 1 = right/add
+	int bestYDirection = 0; // 0 = up/subtract, 1 = down/add
 
 	for (size_t i = 0; i < walkboxes.size(); i++) {
+		int xDistance = 0;
+		int xDirection = 0;
+		int yDistance = 0;
+		int yDirection = 0;
 
-		// Calculate distance from source point to this walkbox (Manhattan distance)
-		int dx = 0;
-		int dy = 0;
-
-		// Calculate horizontal distance
+		// Calculate X distance with direction
 		if (sourceX < walkboxes[i].x) {
-			dx = walkboxes[i].x - sourceX;
+			xDistance = walkboxes[i].x - sourceX;
+			xDirection = 1; // RIGHT
 		} else if (sourceX > walkboxes[i].x + walkboxes[i].w) {
-			dx = sourceX - (walkboxes[i].x + walkboxes[i].w);
+			// KEY: subtract 1 from right edge
+			xDistance = sourceX - (walkboxes[i].x + walkboxes[i].w - 1);
+			xDirection = 0; // LEFT
 		}
-		// else: sourceX is inside walkbox horizontally, dx = 0
+		// else: sourceX is inside, xDistance = 0
 
-		// Calculate vertical distance
+		// Calculate Y distance with direction
 		if (sourceY < walkboxes[i].y) {
-			dy = walkboxes[i].y - sourceY;
+			yDistance = walkboxes[i].y - sourceY;
+			yDirection = 1; // DOWN
 		} else if (sourceY > walkboxes[i].y + walkboxes[i].h) {
-			dy = sourceY - (walkboxes[i].y + walkboxes[i].h);
+			// KEY: subtract 1 from bottom edge
+			yDistance = sourceY - (walkboxes[i].y + walkboxes[i].h - 1);
+			yDirection = 0; // UP
 		}
-		// else: sourceY is inside walkbox vertically, dy = 0
-
-		uint32 distance = dx + dy;
+		// else: sourceY is inside, yDistance = 0
 
-		if (distance < minDistance) {
-			minDistance = distance;
+		uint32 totalDistance = xDistance + yDistance;
 
-			// Calculate target point (nearest point on walkbox to source)
-			int targetX = sourceX;
-			int targetY = sourceY;
+		if (totalDistance < minDistance) {
+			minDistance = totalDistance;
+			bestXDistance = xDistance;
+			bestYDistance = yDistance;
+			bestXDirection = xDirection;
+			bestYDirection = yDirection;
+		}
+	}
 
-			if (sourceX < walkboxes[i].x) {
-				targetX = walkboxes[i].x;
-			} else if (sourceX > walkboxes[i].x + walkboxes[i].w) {
-				targetX = walkboxes[i].x + walkboxes[i].w;
-			}
+	// Step 3: Calculate final target point
+	Common::Point target;
 
-			if (sourceY < walkboxes[i].y) {
-				targetY = walkboxes[i].y;
-			} else if (sourceY > walkboxes[i].y + walkboxes[i].h) {
-				targetY = walkboxes[i].y + walkboxes[i].h;
-			}
+	if (bestXDirection == 1) {
+		target.x = sourceX + bestXDistance;
+	} else {
+		target.x = sourceX - bestXDistance;
+	}
 
-			bestTarget.x = targetX;
-			bestTarget.y = targetY;
-		}
+	if (bestYDirection == 1) {
+		target.y = sourceY + bestYDistance;
+	} else {
+		target.y = sourceY - bestYDistance;
 	}
 
-	return bestTarget;
+	return target;
 }
 
 uint8_t findWalkboxForPoint(Common::Array<WalkBox> &walkboxes, uint16_t x, uint16_t y) {
diff --git a/engines/pelrock/pathfinding.h b/engines/pelrock/pathfinding.h
index cb33de23d29..8f02b0e814b 100644
--- a/engines/pelrock/pathfinding.h
+++ b/engines/pelrock/pathfinding.h
@@ -29,7 +29,7 @@
 namespace Pelrock {
 bool findPath(int sourceX, int sourceY, int targetX, int targetY, Common::Array<WalkBox> &walkboxes, PathContext *context);
 
-Common::Point calculateWalkTarget(Common::Array<WalkBox> &walkboxes, int x, int y);
+Common::Point calculateWalkTarget(Common::Array<WalkBox> &walkboxes, int sourceX, int sourceY, bool mouseHoverState, HotSpot *hotspot);
 uint8_t findWalkboxForPoint(Common::Array<WalkBox> &walkboxes, uint16_t x, uint16_t y);
 uint8_t getAdjacentWalkbox(Common::Array<WalkBox> &walkboxes, uint8_t current_box_index);
 uint16_t buildWalkboxPath(Common::Array<WalkBox> &walkboxes, uint8_t start_box, uint8_t dest_box, uint8_t *path_buffer);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index f2c6dc3e25d..90510cc501e 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -607,7 +607,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			_currentStep++;
 			if (_currentStep >= _currentContext.movementCount) {
 				_currentStep = 0;
-				if(_queuedAction.isQueued) {
+				if (_queuedAction.isQueued) {
 					doAction(_queuedAction.verb, &_room->_currentRoomHotspots[_queuedAction.hotspotIndex]);
 					_queuedAction.isQueued = false;
 				}
@@ -1165,7 +1165,7 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 		VerbIcon actionClicked = isActionUnder(x, y);
 		if (actionClicked != NO_ACTION) {
 			_actionPopupState.isActive = false;
-			if(_currentHotspot != nullptr) {
+			if (_currentHotspot != nullptr) {
 				walkTo(_currentHotspot->x + _currentHotspot->w / 2, _currentHotspot->y + _currentHotspot->h);
 				_queuedAction = QueuedAction{actionClicked, _currentHotspot->index, true};
 				return;
@@ -1178,21 +1178,34 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 	_actionPopupState.isActive = false;
 	_currentHotspot = nullptr;
 
-	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, _events->_mouseX, _events->_mouseY);
+	int hotspotIndex = isHotspotUnder(_events->_mouseX, _events->_mouseY);
+	bool isHotspotUnder = false;
+	if(hotspotIndex != -1) {
+		isHotspotUnder = true;
+	}
+
+	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, _events->_mouseX, _events->_mouseY, isHotspotUnder, isHotspotUnder ? &_room->_currentRoomHotspots[hotspotIndex] : nullptr);
 	_curWalkTarget = walkTarget;
 
-	{ // For quick room navigation
-		Exit *exit = isExitUnder(walkTarget.x, walkTarget.y);
+	// if (hotspotIndex != -1) {
+	// 	_currentHotspot = &_room->_currentRoomHotspots[hotspotIndex];
+	// 	walkTarget.x = _currentHotspot->x + _currentHotspot->w / 2;
+	// 	walkTarget.y = _currentHotspot->y + _currentHotspot->h;
+	// }
 
-		if (exit != nullptr) {
-			alfredState.x = exit->targetX;
-			alfredState.y = exit->targetY;
+	walkTo(walkTarget.x, walkTarget.y);
 
-			setScreen(exit->targetRoom, exit->dir);
-		} else {
-			walkTo(walkTarget.x, walkTarget.y);
-		}
-	}
+	// { // For quick room navigation
+	// 	Exit *exit = isExitUnder(walkTarget.x, walkTarget.y);
+
+	// 	if (exit != nullptr) {
+	// 		alfredState.x = exit->targetX;
+	// 		alfredState.y = exit->targetY;
+
+	// 		setScreen(exit->targetRoom, exit->dir);
+	// 	} else {
+	// 	}
+	// }
 }
 
 void PelrockEngine::changeCursor(Cursor cursor) {
@@ -1202,16 +1215,6 @@ void PelrockEngine::changeCursor(Cursor cursor) {
 void PelrockEngine::checkMouseHover() {
 	bool hotspotDetected = false;
 
-	// Calculate walk target first (before checking anything else)
-	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, _events->_mouseX, _events->_mouseY);
-
-	// Check if walk target hits any exit
-	bool exitDetected = false;
-	Exit *exit = isExitUnder(walkTarget.x, walkTarget.y);
-	if (exit != nullptr) {
-		exitDetected = true;
-	}
-
 	int hotspotIndex = isHotspotUnder(_events->_mouseX, _events->_mouseY);
 	if (hotspotIndex != -1) {
 		hotspotDetected = true;
@@ -1225,6 +1228,15 @@ void PelrockEngine::checkMouseHover() {
 	if (isAlfredUnder(_events->_mouseX, _events->_mouseY)) {
 		alfredDetected = true;
 	}
+	// Calculate walk target first (before checking anything else)
+	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, _events->_mouseX, _events->_mouseY, hotspotDetected, hotspotDetected ? &_room->_currentRoomHotspots[hotspotIndex] : nullptr);
+
+	// Check if walk target hits any exit
+	bool exitDetected = false;
+	Exit *exit = isExitUnder(walkTarget.x, walkTarget.y);
+	if (exit != nullptr) {
+		exitDetected = true;
+	}
 
 	if (alfredDetected) {
 		changeCursor(ALFRED);
@@ -1239,138 +1251,6 @@ void PelrockEngine::checkMouseHover() {
 	}
 }
 
-// void PelrockEngine::sayNPC(Sprite *anim, Common::String text, byte color) {
-// 	isNPCATalking = true;
-// 	whichNPCTalking = anim->extra;
-// 	_currentTextPages = wordWrap(text);
-// 	_textColor = color;
-// 	int totalChars = 0;
-// 	for (int i = 0; i < _currentTextPages[0].size(); i++) {
-// 		totalChars += _currentTextPages[0][i].size();
-// 	}
-// 	_textPos = Common::Point(anim->x, anim->y - 10);
-// 	_textDurationFrames = totalChars / 2;
-// }
-
-// void PelrockEngine::sayAlfred(Common::String text) {
-// 	alfredState.nextState = ALFRED_TALKING;
-// 	alfredState.curFrame = 0;
-// 	_currentTextPages = wordWrap(text);
-// 	_textColor = 13;
-// 	int totalChars = 0;
-// 	for (int i = 0; i < _currentTextPages[0].size(); i++) {
-// 		totalChars += _currentTextPages[0][i].size();
-// 	}
-// 	_textDurationFrames = totalChars / 2;
-// }
-
-// int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
-// 	// return word_length, is_end
-// 	int wordLength = 0;
-// 	int pos = startPos;
-// 	while (pos < text.size()) {
-// 		char char_byte = text[pos];
-// 		if (char_byte == CHAR_SPACE || isEndMarker(char_byte)) {
-// 			break;
-// 		}
-// 		wordLength++;
-// 		pos++;
-// 	}
-// 	// Check if we hit an end marker
-// 	if (pos < text.size() && isEndMarker(text[pos])) {
-// 		isEnd = true;
-// 	}
-// 	// Count ALL trailing spaces as part of this word
-// 	if (pos < text.size() && !isEnd) {
-// 		if (text[pos] == CHAR_END_MARKER_3) { // 0xF8 (-8) special case
-// 			wordLength += 3;
-// 		} else {
-// 			// Count all consecutive spaces
-// 			while (pos < text.size() && text[pos] == CHAR_SPACE) {
-// 				wordLength++;
-// 				pos++;
-// 			}
-// 		}
-// 	}
-// 	return wordLength;
-// }
-
-// Common::Array<Common::Array<Common::String>> wordWrap(Common::String text) {
-
-// 	Common::Array<Common::Array<Common::String>> pages;
-// 	Common::Array<Common::String> currentPage;
-// 	Common::Array<Common::String> currentLine;
-// 	int charsRemaining = MAX_CHARS_PER_LINE;
-// 	int position = 0;
-// 	int currentLineNum = 0;
-// 	while (position < text.size()) {
-// 		bool isEnd = false;
-// 		int wordLength = calculateWordLength(text, position, isEnd);
-// 		// # Extract the word (including trailing spaces)
-// 		// word = text[position:position + word_length].decode('latin-1', errors='replace')
-// 		Common::String word = text.substr(position, wordLength).decode(Common::kLatin1);
-// 		// # Key decision: if word_length > chars_remaining, wrap to next line
-// 		if (wordLength > charsRemaining) {
-// 			// Word is longer than the entire line - need to split
-// 			currentPage.push_back(joinStrings(currentLine, ""));
-// 			currentLine.clear();
-// 			charsRemaining = MAX_CHARS_PER_LINE;
-// 			currentLineNum++;
-
-// 			if (currentLineNum >= MAX_LINES) {
-// 				pages.push_back(currentPage);
-// 				currentPage.clear();
-// 				currentLineNum = 0;
-// 			}
-// 		}
-// 		// Add word to current line
-// 		currentLine.push_back(word);
-// 		charsRemaining -= wordLength;
-
-// 		if (charsRemaining == 0 && isEnd) {
-// 			Common::String lineText = joinStrings(currentLine, "");
-// 			while (lineText.lastChar() == CHAR_SPACE) {
-// 				lineText = lineText.substr(0, lineText.size() - 1);
-// 			}
-// 			int trailingSpaces = currentLine.size() - lineText.size();
-// 			if (trailingSpaces > 0) {
-// 				currentPage.push_back(lineText);
-// 				//  current_line = [' ' * trailing_spaces]
-// 				Common::String currentLine(trailingSpaces, ' ');
-// 				charsRemaining = MAX_CHARS_PER_LINE - trailingSpaces;
-// 				currentLineNum += 1;
-
-// 				if (currentLineNum >= MAX_LINES) {
-// 					pages.push_back(currentPage);
-// 					currentPage.clear();
-// 					currentLineNum = 0;
-// 				}
-// 			}
-// 		}
-
-// 		position += wordLength;
-// 		if (isEnd) {
-// 			// End of sentence/paragraph/page
-// 			break;
-// 		}
-// 	}
-// 	if (currentLine.empty() == false) {
-// 		Common::String lineText = joinStrings(currentLine, "");
-// 		while (lineText.lastChar() == CHAR_SPACE) {
-// 			lineText = lineText.substr(0, lineText.size() - 1);
-// 		}
-// 		currentPage.push_back(lineText);
-// 	}
-// 	if (currentPage.empty() == false) {
-// 		pages.push_back(currentPage);
-// 	}
-// 	for (int i = 0; i < pages.size(); i++) {
-// 		for (int j = 0; j < pages[i].size(); j++) {
-// 		}
-// 	}
-// 	return pages;
-// }
-
 void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 	_sound->stopAllSounds();
 	Common::File roomFile;
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 1dc41fdbe3a..9f85bdc1906 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -386,7 +386,7 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(Common::File *roomFile, int ro
 		int16 w = roomFile->readSint16LE();
 		int16 h = roomFile->readSint16LE();
 		byte flags = roomFile->readByte();
-		// debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
+		debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
 		WalkBox box;
 		box.x = x1;
 		box.y = y1;


Commit: ed2e14da575a2d2c208b221fb019080b657f39a1
    https://github.com/scummvm/scummvm/commit/ed2e14da575a2d2c208b221fb019080b657f39a1
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:31+02:00

Commit Message:
PELROCK: Simple inventory management

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 497f7db4a92..6384c358000 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -67,7 +67,7 @@ void MenuManager::checkMouseClick(int x, int y) {
 	for (int i = 0; i < 4; i++) {
 		if (x >= 140 + (82 * i) && x <= 140 + (82 * i) + 64 &&
 			y >= 115 - (8 * i) && y <= 115 - (8 * i) + 64) {
-			_selectedInvIndex = _curInventoryPage * 4 + i;
+			_selectedInvIndex = g_engine->_inventoryItems[_curInventoryPage * 4 + i];
 			_menuText = _inventoryDescriptions[_selectedInvIndex];
 			selectedItem = true;
 			return;
@@ -101,9 +101,12 @@ void MenuManager::menuLoop() {
 
 	memcpy(_compositeBuffer, _mainMenu, 640 * 400);
 
+
 	for (int i = 0; i < 4; i++) {
 		int itemIndex = _curInventoryPage * 4 + i;
-		InventoryObject item = _res->getInventoryObject(itemIndex);
+		if(g_engine->_inventoryItems.size() <= itemIndex)
+			continue;
+		InventoryObject item = g_engine->_res->getInventoryObject(g_engine->_inventoryItems[itemIndex]);
 		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 140 + (82 * i), 115 - (8 * i), 60, 60, 1);
 	}
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 90510cc501e..2dcbaf73857 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -503,6 +503,9 @@ void PelrockEngine::doAction(byte action, HotSpot *hotspot) {
 	case CLOSE:
 		close(hotspot);
 		break;
+	case PICKUP:
+		pick(hotspot);
+		break;
 	default:
 		break;
 	}
@@ -529,6 +532,7 @@ void PelrockEngine::open(HotSpot *hotspot) {
 	switch (hotspot->extra) {
 	case 261:
 		_room->addSticker(_res->getSticker(91));
+		_room->_currentRoomHotspots[hotspot->index].isEnabled = false;
 		break;
 	case 268:
 		_room->addSticker(_res->getSticker(93));
@@ -555,6 +559,25 @@ void PelrockEngine::close(HotSpot *hotspot) {
 	}
 }
 
+void PelrockEngine::pick(HotSpot *hotspot) {
+	_inventoryItems.push_back(hotspot->extra);
+	switch (hotspot->extra)
+	{
+	case 0:
+	case 1:
+	case 2:
+		_room->_currentRoomHotspots[hotspot->index].isEnabled = false;
+		break;
+	case 4:
+		_room->addSticker(_res->getSticker(95));
+		/* code */
+		break;
+
+	default:
+		break;
+	}
+}
+
 void PelrockEngine::renderText(Common::Array<Common::String> lines, int color, int baseX, int baseY) {
 
 	int maxW = 0;
@@ -1158,6 +1181,7 @@ bool PelrockEngine::isAlfredUnder(int x, int y) {
 	return true;
 }
 
+
 void PelrockEngine::checkMouseClick(int x, int y) {
 
 	if (_actionPopupState.isActive) {
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 8c0cc2def13..b9a47c1beef 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -103,6 +103,7 @@ private:
 	void lookAt(HotSpot *hotspot);
 	void open(HotSpot *hotspot);
 	void close(HotSpot *hotspot);
+	void pick(HotSpot *hotspot);
 	void renderText(Common::Array<Common::String> lines, int color, int x, int y);
 	void chooseAlfredStateAndDraw();
 	void drawAlfred(byte *buf);
@@ -179,8 +180,10 @@ public:
 	SmallFont *_smallFont = nullptr;
 	LargeFont *_largeFont = nullptr;
 	DoubleSmallFont *_doubleSmallFont = nullptr;
-	void renderScene(bool showTextOverlay = false);
-	void performActionTrigger(uint16 actionTrigger);
+
+	Common::Array<int> _inventoryItems;
+	int _selectedInventoryItem = -1;
+
 
 public:
 	PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc);
@@ -229,6 +232,8 @@ public:
 	}
 
 	void setScreen(int s, AlfredDirection dir);
+	void renderScene(bool showTextOverlay = false);
+	void performActionTrigger(uint16 actionTrigger);
 };
 
 extern PelrockEngine *g_engine;
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 9f85bdc1906..3fd7bf5faf3 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -424,7 +424,7 @@ Common::Array<Description> RoomManager::loadRoomTexts(Common::File *roomFile, in
 
 				if (data[pos] != 0x00) {
 					// debug("Adding char 0x%02X to description, decoded as %lc", data[pos], decodeChar((byte)data[pos]));
-					description.text.append(1, decodeChar((byte)data[pos]));
+					description.text.append(1, (char)data[pos]);
 				}
 				if (data[pos] == 0xF8) {
 					description.actionTrigger = data[pos + 1] | data[pos + 2] << 8;
@@ -449,11 +449,7 @@ Common::Array<Description> RoomManager::loadRoomTexts(Common::File *roomFile, in
 	}
 	_conversationData = new byte[_conversationDataSize];
 	Common::copy(data + conversationStart, data + conversationStart + _conversationDataSize, _conversationData);
-
 	delete[] data;
-	// for (Common::List<Common::String>::iterator i = descriptions.begin(); i != descriptions.end(); i++) {
-	// 	debug("Room description: %s", i->c_str());
-	// }
 	return descriptions;
 }
 


Commit: b533f0ac91b90db89b9e97a352bf56f48f39c55c
    https://github.com/scummvm/scummvm/commit/b533f0ac91b90db89b9e97a352bf56f48f39c55c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:31+02:00

Commit Message:
PELROCK: Remove debug artifacts

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 8321f6a03e5..cf52a246d5e 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -171,7 +171,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		Graphics::Surface s;
 		s.create(maxWidth, height, Graphics::PixelFormat::createFormatCLUT8());
 		s.fillRect(s.getRect(), 255); // Clear surface
-		s.drawRoundRect(Common::Rect(0, 0, s.getRect().width(), s.getRect().height()), 2, 13, false);
+		// s.drawRoundRect(Common::Rect(0, 0, s.getRect().width(), s.getRect().height()), 2, 13, false);
 		int xPos = 0;
 		int yPos = 0;
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 2dcbaf73857..7495aa9c7d7 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -368,7 +368,7 @@ void PelrockEngine::updateAnimations() {
 
 void PelrockEngine::presentFrame() {
 	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
-	paintDebugLayer();
+	// paintDebugLayer();
 	_screen->markAllDirty();
 }
 


Commit: 558c76b6d58b5ff5bfbba6f578fb39583e2c21a9
    https://github.com/scummvm/scummvm/commit/558c76b6d58b5ff5bfbba6f578fb39583e2c21a9
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:31+02:00

Commit Message:
PELROCK: Idle animation

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 7495aa9c7d7..774796d04df 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -317,13 +317,15 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 void PelrockEngine::checkMouse() {
 	if (_events->_leftMouseClicked) {
 		checkMouseClick(_events->_mouseClickX, _events->_mouseClickY);
+		alfredState.idleFrameCounter = 0;
 		_events->_leftMouseClicked = false;
 		_actionPopupState.isActive = false;
 	} else if (_events->_longClicked) {
+		alfredState.idleFrameCounter = 0;
 		checkLongMouseClick(_events->_mouseClickX, _events->_mouseClickY);
 		_events->_longClicked = false;
 	} else if (_events->_rightMouseClicked) {
-
+		alfredState.idleFrameCounter = 0;
 		g_system->getPaletteManager()->setPalette(_menu->_mainMenuPalette, 0, 256);
 		_events->_rightMouseClicked = false;
 		stateGame = SETTINGS;
@@ -596,6 +598,14 @@ void PelrockEngine::renderText(Common::Array<Common::String> lines, int color, i
 }
 
 void PelrockEngine::chooseAlfredStateAndDraw() {
+	if(alfredState.idleFrameCounter++ >= kAlfredIdleAnimationFrameCount &&
+		 alfredState.animState == ALFRED_IDLE &&
+		 (alfredState.direction == ALFRED_LEFT || alfredState.direction == ALFRED_RIGHT)
+	) {
+		alfredState.idleFrameCounter = 0;
+		alfredState.curFrame = 0;
+		alfredState.animState = ALFRED_COMB;
+	}
 	switch (alfredState.animState) {
 	case ALFRED_WALKING: {
 		MovementStep step = _currentContext.movementBuffer[_currentStep];
@@ -667,9 +677,12 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		break;
 	case ALFRED_COMB:
 		if (alfredState.curFrame >= 11) {
+			alfredState.animState = ALFRED_IDLE;
 			alfredState.curFrame = 0;
+			drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredIdle[alfredState.direction], alfredState.x, alfredState.y - kAlfredFrameHeight, 51, 102, 255);
+			break;
 		}
-		drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCombFrames[0][alfredState.curFrame], alfredState.x, alfredState.y - kAlfredFrameHeight, 51, 102, 255);
+		drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCombFrames[alfredState.direction][alfredState.curFrame], alfredState.x, alfredState.y - kAlfredFrameHeight, 51, 102, 255);
 		alfredState.curFrame++;
 		break;
 	case ALFRED_INTERACTING:
@@ -1090,31 +1103,6 @@ void PelrockEngine::animateTalkingNPC(Sprite *animSet) {
 
 void PelrockEngine::gameLoop() {
 	_events->pollEvent();
-	// while (g_system->getEventManager()->pollEvent(e)) {
-	// 	if (e.type == Common::EVENT_KEYDOWN) {
-	// 		switch (e.kbd.keycode) {
-	// 		case Common::KEYCODE_w:
-	// 			alfredState.animState = ALFRED_WALKING;
-	// 			break;
-	// 		case Common::KEYCODE_t:
-	// 			alfredState.animState = ALFRED_TALKING;
-	// 			break;
-	// 		case Common::KEYCODE_s:
-	// 			alfredState.animState = ALFRED_IDLE;
-	// 			break;
-	// 		case Common::KEYCODE_c:
-	// 			alfredState.animState = ALFRED_COMB;
-	// 			break;
-	// 		case Common::KEYCODE_i:
-	// 			alfredState.animState = ALFRED_INTERACTING;
-	// 			break;
-	// 		case Common::KEYCODE_z:
-	// 			showShadows = !showShadows;
-	// 			break;
-	// 		default:
-	// 			break;
-	// 		}
-	// 	}
 
 	if (inConversation) {
 		// TODO: Pass actual conversation data from room
@@ -1276,37 +1264,34 @@ void PelrockEngine::checkMouseHover() {
 }
 
 void PelrockEngine::setScreen(int number, AlfredDirection dir) {
-	_sound->stopAllSounds();
+
 	Common::File roomFile;
 	if (!roomFile.open(Common::Path("ALFRED.1"))) {
 		error("Could not open ALFRED.1");
 		return;
 	}
+	_sound->stopAllSounds();
 	alfredState.direction = dir;
 	alfredState.animState = ALFRED_IDLE;
 	_currentStep = 0;
 	int roomOffset = number * kRoomStructSize;
 	alfredState.curFrame = 0;
+
 	byte *palette = new byte[256 * 3];
 	_room->getPalette(&roomFile, roomOffset, palette);
 
-	int paletteOffset = roomOffset + (11 * 8);
-	roomFile.seek(paletteOffset, SEEK_SET);
-	uint32 offset = roomFile.readUint32LE();
-
-	g_system->getPaletteManager()->setPalette(palette, 0, 256);
 
 	byte *background = new byte[640 * 400];
 	_room->getBackground(&roomFile, roomOffset, background);
-	if (_currentBackground != nullptr)
-		delete[] _currentBackground;
-	_currentBackground = new byte[640 * 400];
+
+	_screen->clear();
+	_screen->markAllDirty();
+	_screen->update();
+
+
 	Common::copy(background, background + 640 * 400, _currentBackground);
-	for (int i = 0; i < 640; i++) {
-		for (int j = 0; j < 400; j++) {
-			_screen->setPixel(i, j, background[j * 640 + i]);
-		}
-	}
+	copyBackgroundToBuffer();
+	g_system->getPaletteManager()->setPalette(palette, 0, 256);
 
 	_room->loadRoomMetadata(&roomFile, number);
 	_room->loadRoomTalkingAnimations(number);
@@ -1319,6 +1304,7 @@ void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 	_room->_currentRoomNumber = number;
 
 	_screen->markAllDirty();
+	_screen->update();
 	roomFile.close();
 	delete[] background;
 	delete[] palette;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 330a5696b69..0a738eaa141 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -71,6 +71,7 @@ const int kChoiceHeight = 16; // Height of each choice line in pixels
 
 const int kTalkAnimationSpeed = 2;   // Frames per update
 const int kAlfredAnimationSpeed = 2; // Frames per update
+const int kAlfredIdleAnimationFrameCount = 300;
 
 // Direction flags (bit-packed)
 #define MOVE_RIGHT 0x01 // Move right (positive X)
@@ -122,6 +123,7 @@ struct AlfredState {
 	uint16 x = 319;
 	uint16 y = 302;
 	float currentScale = 1.0f;
+	int idleFrameCounter = 0;
 };
 
 typedef struct {


Commit: cf4d609cdaea99fd4289ef2639f230938887fed0
    https://github.com/scummvm/scummvm/commit/cf4d609cdaea99fd4289ef2639f230938887fed0
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:32+02:00

Commit Message:
PELROCK: Fixes text processing

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 6384c358000..8398d028e70 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -203,14 +203,14 @@ void MenuManager::loadMenuTexts() {
 	byte *descBuffer = new byte[kInventoryDescriptionsSize];
 	exe.seek(kInventoryDescriptionsOffset, SEEK_SET);
 	exe.read(descBuffer, kInventoryDescriptionsSize);
-	_inventoryDescriptions = _res->processTextData(descBuffer, kInventoryDescriptionsSize);
+	_inventoryDescriptions = _res->processTextData(descBuffer, kInventoryDescriptionsSize, true);
 	delete[] descBuffer;
 
 	Common::String desc = "";
 	byte *textBuffer = new byte[kMenuTextSize];
 	exe.seek(kMenuTextOffset, SEEK_SET);
 	exe.read(textBuffer, kMenuTextSize);
-	_menuTexts = _res->processTextData(textBuffer, kMenuTextSize);
+	_menuTexts = _res->processTextData(textBuffer, kMenuTextSize, true);
 
 	_menuText = _menuTexts[0];
 	delete[] textBuffer;
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 95123581d55..98757d03414 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -279,7 +279,7 @@ Common::Array<Common::StringArray> ResourceManager::getCredits() {
 	return credits;
 }
 
-Common::Array<Common::Array<Common::String>> ResourceManager::processTextData(byte *data, size_t size) {
+Common::Array<Common::Array<Common::String>> ResourceManager::processTextData(byte *data, size_t size, bool decode) {
 	int pos = 0;
 	Common::String desc = "";
 	Common::StringArray lines;
@@ -313,8 +313,10 @@ Common::Array<Common::Array<Common::String>> ResourceManager::processTextData(by
 			pos++;
 			continue;
 		}
-
-		desc.append(1, decodeChar(data[pos]));
+		if( decode )
+			desc.append(1, decodeChar(data[pos]));
+		else
+			desc.append(1, data[pos]);
 		if (pos + 1 == size) {
 			lines.push_back(desc);
 			texts.push_back(lines);
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index e026f15813f..042c9bf82a4 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -48,7 +48,7 @@ public:
 	void loadAlfredResponses();
 	void getExtraScreen(int screenIndex, byte *screenBuf, byte *palette);
 	Common::Array<Common::StringArray> getCredits();
-	Common::Array<Common::Array<Common::String>> processTextData(byte *data, size_t size);
+	Common::Array<Common::Array<Common::String>> processTextData(byte *data, size_t size, bool decode = false);
 	Sticker getSticker(int stickerIndex);
 	InventoryObject getInventoryObject(byte index);
 	byte *loadExtra();


Commit: 441230ed99a0b499471325b05aea1d8b94206328
    https://github.com/scummvm/scummvm/commit/441230ed99a0b499471325b05aea1d8b94206328
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:32+02:00

Commit Message:
PELROCK: Action Handler

Changed paths:
  A engines/pelrock/actions.cpp
  A engines/pelrock/actions.h
    engines/pelrock/dialog.cpp
    engines/pelrock/module.mk
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
new file mode 100644
index 00000000000..b5efbf0709a
--- /dev/null
+++ b/engines/pelrock/actions.cpp
@@ -0,0 +1,82 @@
+/* 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 "pelrock/actions.h"
+#include "pelrock/pelrock.h"
+
+namespace Pelrock {
+
+const ActionEntry actionTable[] = {
+    // Room 0
+    { 261, OPEN,   &PelrockEngine::openDrawer },
+    { 261, CLOSE,  &PelrockEngine::closeDrawer },
+    { 268, OPEN,   &PelrockEngine::openDoor },
+    { 268, CLOSE,  &PelrockEngine::closeDoor },
+    { 3, PICKUP,   &PelrockEngine::pickUpPhoto },
+
+    // Generic handlers
+    { WILDCARD, PICKUP, &PelrockEngine::noOp }, // Generic pickup action
+    { WILDCARD, TALK,   &PelrockEngine::noOp }, // Generic talk action
+    { WILDCARD, WALK,   &PelrockEngine::noOp }, // Generic walk action
+    { WILDCARD, LOOK,   &PelrockEngine::noOp }, // Generic look action
+    { WILDCARD, PUSH,   &PelrockEngine::noOp }, // Generic push action
+    { WILDCARD, PULL,   &PelrockEngine::noOp }, // Generic pull action
+    { WILDCARD, OPEN,   &PelrockEngine::noOp }, // Generic open action
+    { WILDCARD, CLOSE,  &PelrockEngine::noOp }, // Generic close action
+
+    // End marker
+    { WILDCARD, NO_ACTION, nullptr }
+};
+
+// Handler implementations
+void PelrockEngine::openDrawer(HotSpot *hotspot) {
+    _room->addSticker(_res->getSticker(91));
+    hotspot->isEnabled = false;
+}
+
+void PelrockEngine::closeDrawer(HotSpot *hotspot) {
+    _room->removeSticker(91);
+    hotspot->isEnabled = true;
+}
+
+void PelrockEngine::openDoor(HotSpot *hotspot) {
+    _room->addSticker(_res->getSticker(93));
+    _room->_currentRoomExits[0].isEnabled = true;
+}
+
+void PelrockEngine::closeDoor(HotSpot *hotspot) {
+    _room->removeSticker(93);
+    _room->_currentRoomExits[0].isEnabled = false;
+}
+
+void PelrockEngine::pickUpAndDisable(HotSpot *hotspot) {
+    _inventoryItems.push_back(hotspot->extra);
+    hotspot->isEnabled = false;
+}
+
+void PelrockEngine::pickUpPhoto(HotSpot *hotspot) {
+    _room->findHotspotByExtra(261)->isEnabled = true;
+}
+void PelrockEngine::noOp(HotSpot *hotspot) {
+	// Do nothing
+}
+
+} // End of namespace Pelrock
diff --git a/engines/pelrock/actions.h b/engines/pelrock/actions.h
new file mode 100644
index 00000000000..73305bc21de
--- /dev/null
+++ b/engines/pelrock/actions.h
@@ -0,0 +1,41 @@
+/* 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 PELROCK_ACTIONS_H
+#define PELROCK_ACTIONS_H
+
+#include "pelrock/types.h"
+#include "pelrock/pelrock.h"
+
+namespace Pelrock {
+
+const int WILDCARD = -1;
+
+struct ActionEntry {
+    int hotspotExtra;
+    VerbIcon action;
+    void (PelrockEngine::*handler)(HotSpot *);
+};
+
+// Action table for all rooms
+extern const ActionEntry actionTable[];
+
+} // End of namespace Pelrock
+#endif // PELROCK_ACTIONS_H
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index cf52a246d5e..0c9e688f39e 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -586,10 +586,9 @@ void DialogManager::sayAlfred(Common::String text) {
 	g_engine->alfredState.nextState = ALFRED_TALKING;
 	g_engine->alfredState.curFrame = 0;
 
+	_curSprite = nullptr;
 	Common::Array<Common::Array<Common::String>> textLines = wordWrap(text);
-
 	displayDialogue(textLines, ALFRED_COLOR);
-
 }
 
 void DialogManager::sayAlfred(Description description) {
@@ -604,7 +603,6 @@ bool isEndMarker(char char_byte) {
 }
 
 int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
-	// return word_length, is_end
 	int wordLength = 0;
 	int pos = startPos;
 	while (pos < text.size()) {
diff --git a/engines/pelrock/module.mk b/engines/pelrock/module.mk
index 0fd85abbf06..c577cd7abe3 100644
--- a/engines/pelrock/module.mk
+++ b/engines/pelrock/module.mk
@@ -2,6 +2,7 @@ MODULE := engines/pelrock
 
 MODULE_OBJS = \
 	pelrock.o \
+	actions.o \
 	chrono.o \
 	console.o \
 	metaengine.o \
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 774796d04df..0c9c36659ed 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -33,6 +33,7 @@
 #include "image/png.h"
 
 #include "pelrock.h"
+#include "pelrock/actions.h"
 #include "pelrock/console.h"
 #include "pelrock/detection.h"
 #include "pelrock/fonts/small_font.h"
@@ -67,8 +68,6 @@ PelrockEngine::~PelrockEngine() {
 	delete _events;
 	delete _dialog;
 	delete _menu;
-	// if (_bgPopupBalloon)
-	// 	delete[] _bgPopupBalloon;
 }
 
 uint32 PelrockEngine::getFeatures() const {
@@ -195,25 +194,25 @@ Common::Array<VerbIcon> PelrockEngine::availableActions(HotSpot *hotspot) {
 	Common::Array<VerbIcon> verbs;
 	verbs.push_back(LOOK);
 
-	if (hotspot->type & 1) {
+	if (hotspot->actionFlags & 1) {
 		verbs.push_back(OPEN);
 	}
-	if (hotspot->type & 2) {
+	if (hotspot->actionFlags & 2) {
 		verbs.push_back(CLOSE);
 	}
-	if (hotspot->type & 4) {
+	if (hotspot->actionFlags & 4) {
 		verbs.push_back(UNKNOWN);
 	}
-	if (hotspot->type & 8) {
+	if (hotspot->actionFlags & 8) {
 		verbs.push_back(PICKUP);
 	}
-	if (hotspot->type & 16) {
+	if (hotspot->actionFlags & 16) {
 		verbs.push_back(TALK);
 	}
-	if (hotspot->type & 32) {
+	if (hotspot->actionFlags & 32) {
 		verbs.push_back(PUSH);
 	}
-	if (hotspot->type & 128) {
+	if (hotspot->actionFlags & 128) {
 		verbs.push_back(PULL);
 	}
 	return verbs;
@@ -259,37 +258,6 @@ void PelrockEngine::renderScene(bool showTextOverlay) {
 		presentFrame();
 		updatePaletteAnimations();
 
-		// if (alfredState.animState != ALFRED_WALKING && !_currentTextPages.empty()) {
-		// 	_chronoManager->countTextDown = true;
-		// 	if (_textDurationFrames-- > 0) {
-		// 		if (alfredState.animState == ALFRED_TALKING) {
-		// 			_textPos = Common::Point(alfredState.x, alfredState.y - kAlfredFrameHeight - 10);
-		// 		}
-		// 		renderText(_currentTextPages[_currentTextPageIndex], _textColor, _textPos.x, _textPos.y);
-		// 	} else if (_currentTextPageIndex < _currentTextPages.size() - 1) {
-		// 		_currentTextPageIndex++;
-
-		// 		int totalChars = 0;
-		// 		for (int i = 0; i < _currentTextPages[_currentTextPageIndex].size(); i++) {
-		// 			totalChars += _currentTextPages[_currentTextPageIndex][i].size();
-		// 		}
-		// 		_textDurationFrames = totalChars / 2;
-		// 	} else {
-		// 		_currentTextPages.clear();
-		// 		_currentTextPageIndex = 0;
-		// 		alfredState.animState = ALFRED_IDLE;
-		// 		isNPCATalking = false;
-		// 		isNPCBTalking = false;
-		// 		_chronoManager->countTextDown = false;
-		// 	}
-		// }
-
-		// if (alfredState.animState == ALFRED_IDLE && alfredState.nextState != ALFRED_IDLE) {
-		// 	alfredState.animState = alfredState.nextState;
-		// 	alfredState.nextState = ALFRED_IDLE;
-		// 	alfredState.curFrame = 0;
-		// }
-
 		_screen->markAllDirty();
 	}
 }
@@ -314,6 +282,27 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	}
 }
 
+void PelrockEngine::executeAction(VerbIcon action, HotSpot *hotspot) {
+    for (const ActionEntry *entry = actionTable; entry->handler != nullptr; entry++) {
+        if (entry->action == action && entry->hotspotExtra == hotspot->extra) {
+            // Found exact match - call the handler
+            (this->*(entry->handler))(hotspot);
+            return;
+        }
+    }
+
+    // Try wildcard match (hotspotExtra == 0 means "any hotspot")
+    for (const ActionEntry *entry = actionTable; entry->handler != nullptr; ++entry) {
+        if (entry->action == action && entry->hotspotExtra == WILDCARD) {
+            (this->*(entry->handler))(hotspot);
+            return;
+        }
+    }
+
+    // No handler found
+    warning("No handler for hotspot %d with action %d", hotspot->extra, action);
+}
+
 void PelrockEngine::checkMouse() {
 	if (_events->_leftMouseClicked) {
 		checkMouseClick(_events->_mouseClickX, _events->_mouseClickY);
@@ -491,7 +480,7 @@ void PelrockEngine::animateRotatePalette(PaletteAnim *anim) {
 	}
 }
 
-void PelrockEngine::doAction(byte action, HotSpot *hotspot) {
+void PelrockEngine::doAction(VerbIcon action, HotSpot *hotspot) {
 	switch (action) {
 	case LOOK:
 		lookAt(hotspot);
@@ -499,16 +488,12 @@ void PelrockEngine::doAction(byte action, HotSpot *hotspot) {
 	case TALK:
 		talkTo(hotspot);
 		break;
-	case OPEN:
-		open(hotspot);
-		break;
-	case CLOSE:
-		close(hotspot);
-		break;
 	case PICKUP:
-		pick(hotspot);
+		pickUpAndDisable(hotspot);
+		executeAction(PICKUP, hotspot);
 		break;
 	default:
+		executeAction(action, hotspot);
 		break;
 	}
 }
@@ -530,56 +515,6 @@ void PelrockEngine::lookAt(HotSpot *hotspot) {
 	_actionPopupState.isActive = false;
 }
 
-void PelrockEngine::open(HotSpot *hotspot) {
-	switch (hotspot->extra) {
-	case 261:
-		_room->addSticker(_res->getSticker(91));
-		_room->_currentRoomHotspots[hotspot->index].isEnabled = false;
-		break;
-	case 268:
-		_room->addSticker(_res->getSticker(93));
-		_room->_currentRoomExits[0].isEnabled = true;
-		break;
-	default:
-
-		break;
-	}
-}
-
-void PelrockEngine::close(HotSpot *hotspot) {
-	switch (hotspot->extra) {
-	case 261:
-		_room->removeSticker(91);
-		break;
-	case 268:
-		_room->removeSticker(93);
-		_room->_currentRoomExits[0].isEnabled = false;
-		break;
-	default:
-
-		break;
-	}
-}
-
-void PelrockEngine::pick(HotSpot *hotspot) {
-	_inventoryItems.push_back(hotspot->extra);
-	switch (hotspot->extra)
-	{
-	case 0:
-	case 1:
-	case 2:
-		_room->_currentRoomHotspots[hotspot->index].isEnabled = false;
-		break;
-	case 4:
-		_room->addSticker(_res->getSticker(95));
-		/* code */
-		break;
-
-	default:
-		break;
-	}
-}
-
 void PelrockEngine::renderText(Common::Array<Common::String> lines, int color, int baseX, int baseY) {
 
 	int maxW = 0;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index b9a47c1beef..d73b420734a 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -98,12 +98,10 @@ private:
 
 	void animateFadePalette(PaletteAnim *anim);
 	void animateRotatePalette(PaletteAnim *anim);
-	void doAction(byte action, HotSpot *hotspot);
+	void doAction(VerbIcon action, HotSpot *hotspot);
 	void talkTo(HotSpot *hotspot);
 	void lookAt(HotSpot *hotspot);
-	void open(HotSpot *hotspot);
-	void close(HotSpot *hotspot);
-	void pick(HotSpot *hotspot);
+
 	void renderText(Common::Array<Common::String> lines, int color, int x, int y);
 	void chooseAlfredStateAndDraw();
 	void drawAlfred(byte *buf);
@@ -234,6 +232,17 @@ public:
 	void setScreen(int s, AlfredDirection dir);
 	void renderScene(bool showTextOverlay = false);
 	void performActionTrigger(uint16 actionTrigger);
+
+    void executeAction(VerbIcon action, HotSpot *hotspot);
+
+    void openDrawer(HotSpot *hotspot);
+    void closeDrawer(HotSpot *hotspot);
+    void openDoor(HotSpot *hotspot);
+    void closeDoor(HotSpot *hotspot);
+    void pickUpAndDisable(HotSpot *hotspot);
+	void pickUpPhoto(HotSpot *hotspot);
+	void noOp(HotSpot *hotspot);
+
 };
 
 extern PelrockEngine *g_engine;
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 3fd7bf5faf3..f7a78196fcc 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -109,6 +109,15 @@ void RoomManager::removeSticker(int stickerIndex) {
 		_currentRoomStickers.remove_at(index);
 }
 
+HotSpot *RoomManager::findHotspotByExtra(uint16 extra) {
+	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
+		if (_currentRoomHotspots[i].extra == extra) {
+			return &_currentRoomHotspots[i];
+		}
+	}
+	return nullptr;
+}
+
 PaletteAnim *RoomManager::getPaletteAnimForRoom(int roomNumber) {
 	Common::File exeFile;
 
@@ -203,13 +212,13 @@ Common::Array<HotSpot> RoomManager::loadHotspots(Common::File *roomFile, int roo
 		uint32_t obj_offset = hotspot_data_start + i * 9;
 		roomFile->seek(obj_offset, SEEK_SET);
 		HotSpot spot;
-		spot.type = roomFile->readByte();
-		spot.x = roomFile->readUint16LE();
-		spot.y = roomFile->readUint16LE();
+		spot.actionFlags = roomFile->readByte();
+		spot.x = roomFile->readSint16LE();
+		spot.y = roomFile->readSint16LE();
 		spot.w = roomFile->readByte();
 		spot.h = roomFile->readByte();
-		spot.extra = roomFile->readUint16LE();
-		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, spot.type, spot.x, spot.y, spot.w, spot.h, spot.extra);
+		spot.extra = roomFile->readSint16LE();
+		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, spot.actionFlags, spot.x, spot.y, spot.w, spot.h, spot.extra);
 		hotspots.push_back(spot);
 	}
 	return hotspots;
@@ -232,7 +241,7 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 		thisHotspot.w = anims[i].w;
 		thisHotspot.h = anims[i].h;
 		thisHotspot.extra = anims[i].extra;
-		thisHotspot.type = anims[i].actionFlags;
+		thisHotspot.actionFlags = anims[i].actionFlags;
 		thisHotspot.isEnabled = !anims[i].isDisabled;
 		hotspots.push_back(thisHotspot);
 	}
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index f9018d89d84..0f2e78fb400 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -41,6 +41,7 @@ public:
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
 	void addSticker(Sticker sticker);
 	void removeSticker(int index);
+	HotSpot * findHotspotByExtra(uint16 extra);
 	PaletteAnim *getPaletteAnimForRoom(int roomNumber);
 
 	Common::String getRoomName(int roomNumber) {
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 0a738eaa141..e1a11da6dd7 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -193,12 +193,12 @@ struct Sprite {
 struct HotSpot {
 	int index;
 	int id;
-	int x;
-	int y;
+	int16 x;
+	int16 y;
 	int w;
 	int h;
-	byte type;
-	int extra;
+	byte actionFlags;
+	int16 extra;
 	bool isEnabled = true;
 };
 


Commit: 3566f40b75dcc2d0f0e8507be9832c576b5ef8ac
    https://github.com/scummvm/scummvm/commit/3566f40b75dcc2d0f0e8507be9832c576b5ef8ac
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:32+02:00

Commit Message:
PELROCK: Adds item to action popup

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index b5efbf0709a..aeaa5727aa6 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -68,7 +68,11 @@ void PelrockEngine::closeDoor(HotSpot *hotspot) {
 }
 
 void PelrockEngine::pickUpAndDisable(HotSpot *hotspot) {
+    if(_inventoryItems.size() == 0) {
+        _selectedInventoryItem = hotspot->extra;
+    }
     _inventoryItems.push_back(hotspot->extra);
+
     hotspot->isEnabled = false;
 }
 
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 0c9e688f39e..88f0043a266 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -118,6 +118,7 @@ uint32 DialogManager::readTextBlock(
 }
 
 void DialogManager::displayChoices(Common::Array<ChoiceOption> *choices, byte *compositeBuffer) {
+
 	int overlayHeight = choices->size() * kChoiceHeight + 2;
 	int overlayY = 400 - overlayHeight;
 	for (int x = 0; x < 640; x++) {
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 0c9c36659ed..8d583aceb2d 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -153,10 +153,10 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		// setScreen(0, ALFRED_DOWN);
-		setScreen(2, ALFRED_LEFT);
-		alfredState.x = 576;
-		alfredState.y = 374;
+		setScreen(0, ALFRED_DOWN);
+		// setScreen(2, ALFRED_LEFT);
+		// alfredState.x = 576;
+		// alfredState.y = 374;
 	}
 }
 
@@ -992,12 +992,22 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
 
 	VerbIcon icon = isActionUnder(_events->_mouseX, _events->_mouseY);
+	bool shouldBlink = _chrono->getFrameCount() % kIconBlinkPeriod == 0;
 	for (int i = 0; i < actions.size(); i++) {
-		if (icon == actions[i] && _chrono->getFrameCount() % kIconBlinkPeriod == 0) {
+		if (icon == actions[i] && shouldBlink) {
 			continue;
 		}
 		drawSpriteToBuffer(_compositeBuffer, 640, _res->_verbIcons[actions[i]], posx + 20 + (i * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
+
+	}
+	bool itemUnder = isItemUnder(_events->_mouseX, _events->_mouseY);
+	if(_selectedInventoryItem != -1) {
+		if(itemUnder && shouldBlink) {
+			return;
+		}
+		drawSpriteToBuffer(_compositeBuffer, 640, _res->getInventoryObject(_selectedInventoryItem).iconData, posx + 20 + (actions.size() * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
 	}
+
 }
 
 void PelrockEngine::animateTalkingNPC(Sprite *animSet) {
@@ -1091,6 +1101,17 @@ VerbIcon PelrockEngine::isActionUnder(int x, int y) {
 	return NO_ACTION;
 }
 
+bool PelrockEngine::isItemUnder(int x, int y) {
+	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
+	Common::Rect itemRect = Common::Rect(_actionPopupState.x + 20 + (actions.size() * (kVerbIconWidth + 2)), _actionPopupState.y + 20,
+		_actionPopupState.x + 20 + (actions.size() * (kVerbIconWidth + 2)) + kVerbIconWidth,
+		_actionPopupState.y + 20 + kVerbIconHeight);
+	if (itemRect.contains(x, y)) {
+		return true;
+	}
+	return false;
+}
+
 bool PelrockEngine::isAlfredUnder(int x, int y) {
 	// TODO: Account for scaling
 	int alfredX = alfredState.x;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index d73b420734a..77578522f2e 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -81,6 +81,7 @@ private:
 
 	Common::Array<VerbIcon> availableActions(HotSpot *hotspot);
 	VerbIcon isActionUnder(int x, int y);
+	bool isItemUnder(int x, int y);
 	bool isAlfredUnder(int x, int y);
 	int isHotspotUnder(int x, int y);
 	Exit *isExitUnder(int x, int y);
@@ -108,8 +109,8 @@ private:
 	void drawNextFrame(Sprite *animSet);
 	void changeCursor(Cursor cursor);
 	void animateTalkingNPC(Sprite *animSet);
-	void playSoundIfNeeded();
 
+	void playSoundIfNeeded();
 	void gameLoop();
 	void menuLoop();
 	void showExtraScreen();


Commit: b4f04b9518fd99dd9e2f52fa9186145ab97a0b25
    https://github.com/scummvm/scummvm/commit/b4f04b9518fd99dd9e2f52fa9186145ab97a0b25
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:32+02:00

Commit Message:
PELROCK: Additional text processing

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index aeaa5727aa6..f6a90bd9893 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -21,64 +21,74 @@
 
 #include "pelrock/actions.h"
 #include "pelrock/pelrock.h"
+#include "pelrock/offsets.h"
 
 namespace Pelrock {
 
 const ActionEntry actionTable[] = {
-    // Room 0
-    { 261, OPEN,   &PelrockEngine::openDrawer },
-    { 261, CLOSE,  &PelrockEngine::closeDrawer },
-    { 268, OPEN,   &PelrockEngine::openDoor },
-    { 268, CLOSE,  &PelrockEngine::closeDoor },
-    { 3, PICKUP,   &PelrockEngine::pickUpPhoto },
+	// Room 0
+	{261, OPEN, &PelrockEngine::openDrawer},
+	{261, CLOSE, &PelrockEngine::closeDrawer},
+	{268, OPEN, &PelrockEngine::openDoor},
+	{268, CLOSE, &PelrockEngine::closeDoor},
+	{3, PICKUP, &PelrockEngine::pickUpPhoto},
+	{0, PICKUP, &PelrockEngine::pickYellowBook}, // Generic pickup for other items
 
-    // Generic handlers
-    { WILDCARD, PICKUP, &PelrockEngine::noOp }, // Generic pickup action
-    { WILDCARD, TALK,   &PelrockEngine::noOp }, // Generic talk action
-    { WILDCARD, WALK,   &PelrockEngine::noOp }, // Generic walk action
-    { WILDCARD, LOOK,   &PelrockEngine::noOp }, // Generic look action
-    { WILDCARD, PUSH,   &PelrockEngine::noOp }, // Generic push action
-    { WILDCARD, PULL,   &PelrockEngine::noOp }, // Generic pull action
-    { WILDCARD, OPEN,   &PelrockEngine::noOp }, // Generic open action
-    { WILDCARD, CLOSE,  &PelrockEngine::noOp }, // Generic close action
+	// Generic handlers
+	{WILDCARD, PICKUP, &PelrockEngine::noOp}, // Generic pickup action
+	{WILDCARD, TALK, &PelrockEngine::noOp},   // Generic talk action
+	{WILDCARD, WALK, &PelrockEngine::noOp},   // Generic walk action
+	{WILDCARD, LOOK, &PelrockEngine::noOp},   // Generic look action
+	{WILDCARD, PUSH, &PelrockEngine::noOp},   // Generic push action
+	{WILDCARD, PULL, &PelrockEngine::noOp},   // Generic pull action
+	{WILDCARD, OPEN, &PelrockEngine::noOp},   // Generic open action
+	{WILDCARD, CLOSE, &PelrockEngine::noOp},  // Generic close action
 
-    // End marker
-    { WILDCARD, NO_ACTION, nullptr }
-};
+	// End marker
+	{WILDCARD, NO_ACTION, nullptr}};
 
 // Handler implementations
 void PelrockEngine::openDrawer(HotSpot *hotspot) {
-    _room->addSticker(_res->getSticker(91));
-    hotspot->isEnabled = false;
+	if (_room->hasSticker(91)) {
+        _dialog->say(_res->_ingameTexts[ALREADY_OPENED_M]);
+		return;
+	}
+	_room->addSticker(_res->getSticker(91));
+	hotspot->isEnabled = false;
 }
 
 void PelrockEngine::closeDrawer(HotSpot *hotspot) {
-    _room->removeSticker(91);
-    hotspot->isEnabled = true;
+	_room->removeSticker(91);
+	hotspot->isEnabled = true;
 }
 
 void PelrockEngine::openDoor(HotSpot *hotspot) {
-    _room->addSticker(_res->getSticker(93));
-    _room->_currentRoomExits[0].isEnabled = true;
+	_room->addSticker(_res->getSticker(93));
+	_room->_currentRoomExits[0].isEnabled = true;
 }
 
 void PelrockEngine::closeDoor(HotSpot *hotspot) {
-    _room->removeSticker(93);
-    _room->_currentRoomExits[0].isEnabled = false;
+	_room->removeSticker(93);
+	_room->_currentRoomExits[0].isEnabled = false;
 }
 
 void PelrockEngine::pickUpAndDisable(HotSpot *hotspot) {
-    if(_inventoryItems.size() == 0) {
-        _selectedInventoryItem = hotspot->extra;
-    }
-    _inventoryItems.push_back(hotspot->extra);
+	if (_inventoryItems.size() == 0) {
+		_selectedInventoryItem = hotspot->extra;
+	}
+	_inventoryItems.push_back(hotspot->extra);
 
-    hotspot->isEnabled = false;
+	hotspot->isEnabled = false;
 }
 
 void PelrockEngine::pickUpPhoto(HotSpot *hotspot) {
-    _room->findHotspotByExtra(261)->isEnabled = true;
+	_room->findHotspotByExtra(261)->isEnabled = true;
 }
+
+void PelrockEngine::pickYellowBook(HotSpot *hotspot) {
+	_room->addSticker(_res->getSticker(95));
+}
+
 void PelrockEngine::noOp(HotSpot *hotspot) {
 	// Do nothing
 }
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 88f0043a266..ad33710ce02 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -400,12 +400,31 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 	return pos;
 }
 
+void DialogManager::setCurSprite(int index) {
+	// Set current sprite based on index
+	if (g_engine->_room == nullptr) {
+		_curSprite = nullptr;
+		return;
+	}
+
+	for (uint i = 0; i < g_engine->_room->_currentRoomAnims.size(); i++) {
+		Sprite *sprite = &g_engine->_room->_currentRoomAnims[i];
+		if (sprite->index == index) {
+			_curSprite = sprite;
+			return;
+		}
+	}
+
+	_curSprite = nullptr;
+}
+
 void DialogManager::startConversation(const byte *conversationData, uint32 dataSize, Sprite *animSet) {
 	if (!conversationData || dataSize == 0) {
 		debug("startConversation: No conversation data");
 		return;
 	}
-	_curSprite = animSet;
+	setCurSprite(animSet ? animSet->index : -1);
+	// _curSprite = animSet;
 
 	debug("Starting conversation with %u bytes of data", dataSize);
 
@@ -583,22 +602,61 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 	// Note: The caller should set inConversation = false after this returns
 }
 
-void DialogManager::sayAlfred(Common::String text) {
+void DialogManager::sayAlfred(Common::StringArray texts) {
 	g_engine->alfredState.nextState = ALFRED_TALKING;
 	g_engine->alfredState.curFrame = 0;
 
 	_curSprite = nullptr;
-	Common::Array<Common::Array<Common::String>> textLines = wordWrap(text);
+	Common::Array<Common::StringArray> textLines = wordWrap(texts);
 	displayDialogue(textLines, ALFRED_COLOR);
 }
 
 void DialogManager::sayAlfred(Description description) {
-	sayAlfred(description.text);
+	Common::StringArray texts;
+	texts.push_back(description.text);
+
+	sayAlfred(texts);
 	if( description.isAction) {
 		g_engine->performActionTrigger(description.actionTrigger);
 	}
 }
 
+void DialogManager::say(Common::StringArray texts) {
+	if(texts.empty()) {
+		return;
+	}
+	int speakerMarker = texts[0][0];
+	int color = texts[0][1];
+
+	if(speakerMarker == '@') {
+
+		for(int i = 0; i < texts.size(); i++) {
+			// Remove first two marker bytes
+			if(texts[i].size() > 2) {
+				texts[i] = texts[i].substr(2);
+				if(texts[i][0] == 0x78 && texts[i][1] == 0x78) { // Remove additional control chars
+					texts[i] = texts[i].substr(2);
+				}
+			} else {
+				texts[i] = "";
+			}
+		}
+
+		if(color == ALFRED_COLOR) {
+			sayAlfred(texts);
+			return;
+		}
+		else {
+			setCurSprite(0);
+			Common::Array<Common::StringArray> textLines = wordWrap(texts);
+			displayDialogue(textLines, color);
+		}
+	}
+	else {
+		sayAlfred(texts);
+	}
+}
+
 bool isEndMarker(char char_byte) {
 	return char_byte == CHAR_END_MARKER_1 || char_byte == CHAR_END_MARKER_2 || char_byte == CHAR_END_MARKER_3 || char_byte == CHAR_END_MARKER_4;
 }
@@ -709,4 +767,15 @@ Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::Str
 	return pages;
 }
 
+Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::StringArray texts) {
+	Common::Array<Common::Array<Common::String>> allWrappedLines;
+	for(int i = 0; i < texts.size(); i++) {
+		Common::Array<Common::Array<Common::String>> wrapped = wordWrap(texts[i]);
+		for(int j = 0; j < wrapped.size(); j++) {
+			allWrappedLines.push_back(wrapped[j]);
+		}
+	}
+	return allWrappedLines;
+}
+
 } // namespace Pelrock
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index ffadb6ab073..104d319bc0c 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -80,8 +80,9 @@ private:
 	void displayDialogue(Common::String text, byte speakerId);
 	uint32 readTextBlock(const byte *data, uint32 dataSize, uint32 startPos, Common::String &outText, byte &outSpeakerId);
 	uint32 parseChoices(const byte *data, uint32 dataSize, uint32 startPos, Common::Array<ChoiceOption> *outChoices);
-
+	void setCurSprite(int index);
 	void checkMouse();
+	void sayAlfred(Common::StringArray texts);
 
 public:
 	DialogManager(Graphics::Screen *screen, PelrockEventManager *events);
@@ -90,10 +91,11 @@ public:
 	void displayChoices(Common::Array<ChoiceOption> *choices, byte *compositeBuffer);
 	int selectChoice(Common::Array<Common::String> &choices, byte *compositeBuffer);
 	void startConversation(const byte *conversationData, uint32 dataSize, Sprite *alfredAnimSet = nullptr);
-	void sayAlfred(Common::String text);
 	void sayAlfred(Description description);
+	void say(Common::StringArray texts);
 
 	Common::Array<Common::Array<Common::String>> wordWrap(Common::String text);
+	Common::Array<Common::Array<Common::String>> wordWrap(Common::StringArray texts);
 	Common::Array<ChoiceOption> *_currentChoices = nullptr;
 };
 
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index fa80338d70f..aed6086615e 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -119,6 +119,43 @@ const byte pegatina_rooms[140] = {
 	51, 52, 53, 54          // Sprites 127-130: Various rooms
 };
 
+enum TextIndices {
+	THEYRE_CLOSED,
+	NOTAVAILABLE_TODAY,
+	ALREADY_OPENED_M,
+	ALREADY_CLOSED_M,
+	ALREADY_OPENED_F,
+	ALREADY_CLOSED_F,
+	ICECREAM_CLOSED,
+	IMPOOR,
+	SOHOT,
+	GREENBUTTON_REDBUTTON,
+	ENTERCARD,
+	NOMONEY_LEFT,
+	STUNGAGAIN,
+	WHATWASTHAT,
+	WHOS_THERE,
+	IMOFF,
+	SHOP_CLOSED,
+	SHE_WOULDNT_NOTICE,
+	GOTTA_OPEN_FIRST,
+	LETHISFATHER_PICKTHEM,
+	BRIBEME,
+	VERYGOOD,
+	WHENHEASKS,
+	OKAY,
+	I_NEED_ID,
+	WHAT_DO_I_GET_IN_RETURN,
+	THATSNOTENOUGH,
+	STOP,
+	THATSOBVIOUSLYNOTENOUGH,
+	WHATFOR,
+	NOTSTONE_ICE,
+	HEY_DONTSTART,
+	HOTONE_MOCKING,
+	SHUTUP,
+};
+
 // Description offsets relative to DESCRIPTION_BASE_OFFSET
 const uint16_t description_offsets[NUM_DESCRIPTIONS] = {
 	0x0000, // Object 0: Historia de la Princesa Zenna y su amante insatisfecho
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 8d583aceb2d..dc7c0b9f2cb 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -273,9 +273,11 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 		_res->getExtraScreen(9, _extraScreen, palette);
 
 		g_system->getPaletteManager()->setPalette(palette, 0, 256);
-		showExtraScreen();
+		extraScreenLoop();
 
-		_dialog->sayAlfred(_res->_alfredResponses[0][0]); // "I found something interesting!");
+		_dialog->say(_res->_ingameTexts[SOHOT]);
+		_screen->markAllDirty();
+		_screen->update();
 
 		delete[] palette;
 		break;
@@ -283,24 +285,24 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 }
 
 void PelrockEngine::executeAction(VerbIcon action, HotSpot *hotspot) {
-    for (const ActionEntry *entry = actionTable; entry->handler != nullptr; entry++) {
-        if (entry->action == action && entry->hotspotExtra == hotspot->extra) {
-            // Found exact match - call the handler
-            (this->*(entry->handler))(hotspot);
-            return;
-        }
-    }
-
-    // Try wildcard match (hotspotExtra == 0 means "any hotspot")
-    for (const ActionEntry *entry = actionTable; entry->handler != nullptr; ++entry) {
-        if (entry->action == action && entry->hotspotExtra == WILDCARD) {
-            (this->*(entry->handler))(hotspot);
-            return;
-        }
-    }
-
-    // No handler found
-    warning("No handler for hotspot %d with action %d", hotspot->extra, action);
+	for (const ActionEntry *entry = actionTable; entry->handler != nullptr; entry++) {
+		if (entry->action == action && entry->hotspotExtra == hotspot->extra) {
+			// Found exact match - call the handler
+			(this->*(entry->handler))(hotspot);
+			return;
+		}
+	}
+
+	// Try wildcard match (hotspotExtra == 0 means "any hotspot")
+	for (const ActionEntry *entry = actionTable; entry->handler != nullptr; ++entry) {
+		if (entry->action == action && entry->hotspotExtra == WILDCARD) {
+			(this->*(entry->handler))(hotspot);
+			return;
+		}
+	}
+
+	// No handler found
+	warning("No handler for hotspot %d with action %d", hotspot->extra, action);
 }
 
 void PelrockEngine::checkMouse() {
@@ -533,10 +535,9 @@ void PelrockEngine::renderText(Common::Array<Common::String> lines, int color, i
 }
 
 void PelrockEngine::chooseAlfredStateAndDraw() {
-	if(alfredState.idleFrameCounter++ >= kAlfredIdleAnimationFrameCount &&
-		 alfredState.animState == ALFRED_IDLE &&
-		 (alfredState.direction == ALFRED_LEFT || alfredState.direction == ALFRED_RIGHT)
-	) {
+	if (alfredState.idleFrameCounter++ >= kAlfredIdleAnimationFrameCount &&
+		alfredState.animState == ALFRED_IDLE &&
+		(alfredState.direction == ALFRED_LEFT || alfredState.direction == ALFRED_RIGHT)) {
 		alfredState.idleFrameCounter = 0;
 		alfredState.curFrame = 0;
 		alfredState.animState = ALFRED_COMB;
@@ -998,16 +999,14 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 			continue;
 		}
 		drawSpriteToBuffer(_compositeBuffer, 640, _res->_verbIcons[actions[i]], posx + 20 + (i * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
-
 	}
 	bool itemUnder = isItemUnder(_events->_mouseX, _events->_mouseY);
-	if(_selectedInventoryItem != -1) {
-		if(itemUnder && shouldBlink) {
+	if (_selectedInventoryItem != -1) {
+		if (itemUnder && shouldBlink) {
 			return;
 		}
 		drawSpriteToBuffer(_compositeBuffer, 640, _res->getInventoryObject(_selectedInventoryItem).iconData, posx + 20 + (actions.size() * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
 	}
-
 }
 
 void PelrockEngine::animateTalkingNPC(Sprite *animSet) {
@@ -1060,10 +1059,9 @@ void PelrockEngine::gameLoop() {
 	}
 }
 
-void PelrockEngine::showExtraScreen() {
+void PelrockEngine::extraScreenLoop() {
 	memcpy(_screen->getPixels(), _extraScreen, 640 * 400);
-	_screen->markAllDirty();
-	_screen->update();
+
 	while (!shouldQuit()) {
 		_events->pollEvent();
 
@@ -1072,6 +1070,8 @@ void PelrockEngine::showExtraScreen() {
 			break;
 		}
 		g_system->delayMillis(10);
+		_screen->markAllDirty();
+		_screen->update();
 	}
 
 	g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
@@ -1104,8 +1104,8 @@ VerbIcon PelrockEngine::isActionUnder(int x, int y) {
 bool PelrockEngine::isItemUnder(int x, int y) {
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
 	Common::Rect itemRect = Common::Rect(_actionPopupState.x + 20 + (actions.size() * (kVerbIconWidth + 2)), _actionPopupState.y + 20,
-		_actionPopupState.x + 20 + (actions.size() * (kVerbIconWidth + 2)) + kVerbIconWidth,
-		_actionPopupState.y + 20 + kVerbIconHeight);
+										 _actionPopupState.x + 20 + (actions.size() * (kVerbIconWidth + 2)) + kVerbIconWidth,
+										 _actionPopupState.y + 20 + kVerbIconHeight);
 	if (itemRect.contains(x, y)) {
 		return true;
 	}
@@ -1125,7 +1125,6 @@ bool PelrockEngine::isAlfredUnder(int x, int y) {
 	return true;
 }
 
-
 void PelrockEngine::checkMouseClick(int x, int y) {
 
 	if (_actionPopupState.isActive) {
@@ -1148,7 +1147,7 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 
 	int hotspotIndex = isHotspotUnder(_events->_mouseX, _events->_mouseY);
 	bool isHotspotUnder = false;
-	if(hotspotIndex != -1) {
+	if (hotspotIndex != -1) {
 		isHotspotUnder = true;
 	}
 
@@ -1236,7 +1235,6 @@ void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 	byte *palette = new byte[256 * 3];
 	_room->getPalette(&roomFile, roomOffset, palette);
 
-
 	byte *background = new byte[640 * 400];
 	_room->getBackground(&roomFile, roomOffset, background);
 
@@ -1244,7 +1242,6 @@ void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 	_screen->markAllDirty();
 	_screen->update();
 
-
 	Common::copy(background, background + 640 * 400, _currentBackground);
 	copyBackgroundToBuffer();
 	g_system->getPaletteManager()->setPalette(palette, 0, 256);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 77578522f2e..cbb0b9dd401 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -113,7 +113,7 @@ private:
 	void playSoundIfNeeded();
 	void gameLoop();
 	void menuLoop();
-	void showExtraScreen();
+	void extraScreenLoop();
 
 	void checkMouseHover();
 	void checkMouseClick(int x, int y);
@@ -242,6 +242,7 @@ public:
     void closeDoor(HotSpot *hotspot);
     void pickUpAndDisable(HotSpot *hotspot);
 	void pickUpPhoto(HotSpot *hotspot);
+	void pickYellowBook(HotSpot *hotspot);
 	void noOp(HotSpot *hotspot);
 
 };
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 98757d03414..18c79aac5d9 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -243,7 +243,7 @@ void ResourceManager::loadAlfredResponses() {
 	byte *descBuffer = new byte[kAlfredResponsesSize];
 	exe.seek(kAlfredResponsesOffset, SEEK_SET);
 	exe.read(descBuffer, kAlfredResponsesSize);
-	_alfredResponses = processTextData(descBuffer, kAlfredResponsesSize);
+	_ingameTexts = processTextData(descBuffer, kAlfredResponsesSize);
 	delete[] descBuffer;
 	exe.close();
 }
@@ -305,6 +305,7 @@ Common::Array<Common::Array<Common::String>> ResourceManager::processTextData(by
 			desc.append(1, '@');
 			desc.append(1, color);
 			pos += 2;
+
 			continue;
 		}
 		if (data[pos] == 0xC8) {
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index 042c9bf82a4..19faa3bdd63 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -65,7 +65,7 @@ public:
 	byte *_cursorMasks[5] = {nullptr};
 	byte *_verbIcons[9] = {nullptr};
 	byte *_popUpBalloon = nullptr;
-	Common::Array<Common::StringArray> _alfredResponses;
+	Common::Array<Common::StringArray> _ingameTexts;
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index f7a78196fcc..25aed624a85 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -109,6 +109,15 @@ void RoomManager::removeSticker(int stickerIndex) {
 		_currentRoomStickers.remove_at(index);
 }
 
+bool RoomManager::hasSticker(int index) {
+	for(int i = 0; i < _currentRoomStickers.size(); i++) {
+		if (_currentRoomStickers[i].stickerIndex == index) {
+			return true;
+		}
+	}
+	return false;
+}
+
 HotSpot *RoomManager::findHotspotByExtra(uint16 extra) {
 	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
 		if (_currentRoomHotspots[i].extra == extra) {
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 0f2e78fb400..b529cf83fac 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -41,6 +41,7 @@ public:
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
 	void addSticker(Sticker sticker);
 	void removeSticker(int index);
+	bool hasSticker(int index);
 	HotSpot * findHotspotByExtra(uint16 extra);
 	PaletteAnim *getPaletteAnimForRoom(int roomNumber);
 


Commit: fedc0a9a10604b6f04684ff3f1b2ff2054c3a567
    https://github.com/scummvm/scummvm/commit/fedc0a9a10604b6f04684ff3f1b2ff2054c3a567
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:33+02:00

Commit Message:
PELROCK: Inventory icon flashes when picked up

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/actions.h
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/events.h
    engines/pelrock/graphics.cpp
    engines/pelrock/graphics.h
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h
    engines/pelrock/module.mk
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/sound.h
    engines/pelrock/types.h
    engines/pelrock/util.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index f6a90bd9893..3693cb5daf5 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -20,8 +20,9 @@
  */
 
 #include "pelrock/actions.h"
-#include "pelrock/pelrock.h"
 #include "pelrock/offsets.h"
+#include "pelrock/pelrock.h"
+#include "pelrock/util.h"
 
 namespace Pelrock {
 
@@ -50,7 +51,7 @@ const ActionEntry actionTable[] = {
 // Handler implementations
 void PelrockEngine::openDrawer(HotSpot *hotspot) {
 	if (_room->hasSticker(91)) {
-        _dialog->say(_res->_ingameTexts[ALREADY_OPENED_M]);
+		_dialog->say(_res->_ingameTexts[ALREADY_OPENED_M]);
 		return;
 	}
 	_room->addSticker(_res->getSticker(91));
@@ -76,8 +77,18 @@ void PelrockEngine::pickUpAndDisable(HotSpot *hotspot) {
 	if (_inventoryItems.size() == 0) {
 		_selectedInventoryItem = hotspot->extra;
 	}
-	_inventoryItems.push_back(hotspot->extra);
+	int frameCounter = 0;
+	while (frameCounter < kIconFlashDuration) {
+		_events->pollEvent();
 
+		bool didRender = renderScene(OVERLAY_PICKUP_ICON);
+		_screen->update();
+		if (didRender) {
+			frameCounter++;
+		}
+		g_system->delayMillis(10);
+	}
+	_inventoryItems.push_back(hotspot->extra);
 	hotspot->isEnabled = false;
 }
 
diff --git a/engines/pelrock/actions.h b/engines/pelrock/actions.h
index 73305bc21de..5bd70abe8e4 100644
--- a/engines/pelrock/actions.h
+++ b/engines/pelrock/actions.h
@@ -21,17 +21,18 @@
 #ifndef PELROCK_ACTIONS_H
 #define PELROCK_ACTIONS_H
 
-#include "pelrock/types.h"
 #include "pelrock/pelrock.h"
+#include "pelrock/types.h"
 
 namespace Pelrock {
 
 const int WILDCARD = -1;
+const int kIconFlashDuration = 15; // frames
 
 struct ActionEntry {
-    int hotspotExtra;
-    VerbIcon action;
-    void (PelrockEngine::*handler)(HotSpot *);
+	int hotspotExtra;
+	VerbIcon action;
+	void (PelrockEngine::*handler)(HotSpot *);
 };
 
 // Action table for all rooms
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index ad33710ce02..c1cbc8a3fdf 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -26,12 +26,12 @@
 
 namespace Pelrock {
 
-DialogManager::DialogManager(Graphics::Screen *screen, PelrockEventManager *events)
-	: _screen(screen), _events(events) {
+DialogManager::DialogManager(Graphics::Screen *screen, PelrockEventManager *events, GraphicsManager *graphics)
+	: _screen(screen), _events(events), _graphics(graphics) {
 }
 
 DialogManager::~DialogManager() {
-	if( _currentChoices ) {
+	if (_currentChoices) {
 		delete _currentChoices;
 		_currentChoices = nullptr;
 	}
@@ -120,23 +120,17 @@ uint32 DialogManager::readTextBlock(
 void DialogManager::displayChoices(Common::Array<ChoiceOption> *choices, byte *compositeBuffer) {
 
 	int overlayHeight = choices->size() * kChoiceHeight + 2;
-	int overlayY = 400 - overlayHeight;
-	for (int x = 0; x < 640; x++) {
-		for (int y = overlayY; y < 400; y++) {
-			int index = y * 640 + x;
-			compositeBuffer[index] = g_engine->_room->overlayRemap[compositeBuffer[index]];
-		}
-	}
+	Common::Point overlayPos = _graphics->showOverlay(overlayHeight, compositeBuffer);
 	for (int i = 0; i < choices->size(); i++) {
 		ChoiceOption choice = (*choices)[i];
 		int choicePadding = 32;
 		int width = g_engine->_doubleSmallFont->getStringWidth(choice.text);
-		Common::Rect bbox(0, overlayY + i * kChoiceHeight, width + choicePadding * 2, overlayY + i * kChoiceHeight + kChoiceHeight);
+		Common::Rect bbox(0, overlayPos.y + i * kChoiceHeight, width + choicePadding * 2, overlayPos.y + i * kChoiceHeight + kChoiceHeight);
 		int color = 14;
 		if (bbox.contains(_events->_mouseX, _events->_mouseY)) {
 			color = 15;
 		}
-		drawText(compositeBuffer, g_engine->_doubleSmallFont, choice.text, choicePadding, overlayY + 2 + i * kChoiceHeight, 620, color);
+		drawText(compositeBuffer, g_engine->_doubleSmallFont, choice.text, choicePadding, overlayPos.y + 2 + i * kChoiceHeight, 620, color);
 	}
 }
 
@@ -158,7 +152,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		_events->pollEvent();
 
 		// Render the scene (keeps animations going)
-		g_engine->renderScene(false);
+		g_engine->renderScene(OVERLAY_NONE);
 
 		// Draw the dialogue text on top using speaker ID as color
 		Common::Array<Common::String> textLines = dialogueLines[curPage];
@@ -178,7 +172,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 
 		if (speakerId == ALFRED_COLOR) {
 			g_engine->alfredState.animState = ALFRED_TALKING;
-			if(_curSprite != nullptr) {
+			if (_curSprite != nullptr) {
 				_curSprite->isTalking = false;
 			}
 			// Offset X position for Alfred to avoid overlapping with his sprite
@@ -228,7 +222,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		}
 		g_system->delayMillis(10);
 	}
-	if(_curSprite != nullptr) {
+	if (_curSprite != nullptr) {
 		_curSprite->isTalking = false;
 	}
 	g_engine->alfredState.animState = ALFRED_IDLE;
@@ -253,7 +247,7 @@ int DialogManager::selectChoice(Common::Array<Common::String> &choices, byte *co
 		_events->pollEvent();
 
 		// Render the scene with choices overlay
-		g_engine->renderScene(true);
+		g_engine->renderScene(OVERLAY_CHOICES);
 
 		if (_events->_leftMouseClicked) {
 			_events->_leftMouseClicked = false;
@@ -562,7 +556,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 				}
 			}
 
-			if(_currentChoices) {
+			if (_currentChoices) {
 				delete _currentChoices;
 				_currentChoices = nullptr;
 			}
@@ -616,25 +610,25 @@ void DialogManager::sayAlfred(Description description) {
 	texts.push_back(description.text);
 
 	sayAlfred(texts);
-	if( description.isAction) {
+	if (description.isAction) {
 		g_engine->performActionTrigger(description.actionTrigger);
 	}
 }
 
 void DialogManager::say(Common::StringArray texts) {
-	if(texts.empty()) {
+	if (texts.empty()) {
 		return;
 	}
 	int speakerMarker = texts[0][0];
 	int color = texts[0][1];
 
-	if(speakerMarker == '@') {
+	if (speakerMarker == '@') {
 
-		for(int i = 0; i < texts.size(); i++) {
+		for (int i = 0; i < texts.size(); i++) {
 			// Remove first two marker bytes
-			if(texts[i].size() > 2) {
+			if (texts[i].size() > 2) {
 				texts[i] = texts[i].substr(2);
-				if(texts[i][0] == 0x78 && texts[i][1] == 0x78) { // Remove additional control chars
+				if (texts[i][0] == 0x78 && texts[i][1] == 0x78) { // Remove additional control chars
 					texts[i] = texts[i].substr(2);
 				}
 			} else {
@@ -642,17 +636,15 @@ void DialogManager::say(Common::StringArray texts) {
 			}
 		}
 
-		if(color == ALFRED_COLOR) {
+		if (color == ALFRED_COLOR) {
 			sayAlfred(texts);
 			return;
-		}
-		else {
+		} else {
 			setCurSprite(0);
 			Common::Array<Common::StringArray> textLines = wordWrap(texts);
 			displayDialogue(textLines, color);
 		}
-	}
-	else {
+	} else {
 		sayAlfred(texts);
 	}
 }
@@ -769,9 +761,9 @@ Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::Str
 
 Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::StringArray texts) {
 	Common::Array<Common::Array<Common::String>> allWrappedLines;
-	for(int i = 0; i < texts.size(); i++) {
+	for (int i = 0; i < texts.size(); i++) {
 		Common::Array<Common::Array<Common::String>> wrapped = wordWrap(texts[i]);
-		for(int j = 0; j < wrapped.size(); j++) {
+		for (int j = 0; j < wrapped.size(); j++) {
 			allWrappedLines.push_back(wrapped[j]);
 		}
 	}
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 104d319bc0c..d9b4e82a192 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -27,6 +27,7 @@
 #include "pelrock/events.h"
 #include "pelrock/fonts/large_font.h"
 #include "pelrock/fonts/small_font.h"
+#include "pelrock/graphics.h"
 #include "pelrock/types.h"
 
 namespace Pelrock {
@@ -73,6 +74,7 @@ class DialogManager {
 private:
 	Graphics::Screen *_screen = nullptr;
 	PelrockEventManager *_events = nullptr;
+	GraphicsManager *_graphics = nullptr;
 	Sprite *_curSprite = nullptr;
 
 	// Private helper functions for conversation parsing
@@ -85,7 +87,7 @@ private:
 	void sayAlfred(Common::StringArray texts);
 
 public:
-	DialogManager(Graphics::Screen *screen, PelrockEventManager *events);
+	DialogManager(Graphics::Screen *screen, PelrockEventManager *events, GraphicsManager *graphics);
 	~DialogManager();
 
 	void displayChoices(Common::Array<ChoiceOption> *choices, byte *compositeBuffer);
diff --git a/engines/pelrock/events.h b/engines/pelrock/events.h
index ddea8935c8b..d25fb456cdd 100644
--- a/engines/pelrock/events.h
+++ b/engines/pelrock/events.h
@@ -32,6 +32,7 @@ private:
 	bool _leftMouseButton = 0;
 	bool _rightMouseButton = 0;
 	uint32 _clickTime = 0;
+
 public:
 	int16 _mouseX = 0;
 	int16 _mouseY = 0;
diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index 69c05b16b4f..8a1743bbff7 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -22,6 +22,7 @@
 #include "common/scummsys.h"
 
 #include "pelrock/graphics.h"
+#include "pelrock/pelrock.h"
 
 namespace Pelrock {
 
@@ -31,5 +32,16 @@ GraphicsManager::GraphicsManager() {
 GraphicsManager::~GraphicsManager() {
 }
 
-} // End of namespace Pelrock
+Common::Point GraphicsManager::showOverlay(int height, byte *buf) {
+	int overlayY = 400 - height;
+	int overlayX = 0;
+	for (int x = 0; x < 640; x++) {
+		for (int y = overlayY; y < 400; y++) {
+			int index = y * 640 + x;
+			buf[index] = g_engine->_room->overlayRemap[buf[index]];
+		}
+	}
+	return Common::Point(overlayX, overlayY);
+}
 
+} // End of namespace Pelrock
diff --git a/engines/pelrock/graphics.h b/engines/pelrock/graphics.h
index a4718e50624..a6c607706a3 100644
--- a/engines/pelrock/graphics.h
+++ b/engines/pelrock/graphics.h
@@ -22,16 +22,16 @@
 #define PELROCK_GRAPHICS_H
 
 #include "common/scummsys.h"
+#include "graphics/screen.h"
 
 namespace Pelrock {
 
 class GraphicsManager {
-
 public:
 	GraphicsManager();
 	~GraphicsManager();
 
-    void renderScene(bool showDialogOverlay);
+	Common::Point showOverlay(int height, byte *buf);
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 8398d028e70..da6d1cdc761 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -23,16 +23,15 @@
 #include "common/file.h"
 #include "graphics/paletteman.h"
 
+#include "menu.h"
 #include "pelrock/menu.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/util.h"
-#include "menu.h"
 
 namespace Pelrock {
 
 Pelrock::MenuManager::MenuManager(Graphics::Screen *screen, PelrockEventManager *events, ResourceManager *res) : _screen(screen), _events(events), _res(res) {
-
 }
 
 void MenuManager::drawColoredText(Graphics::ManagedSurface *screen, const Common::String &text, int x, int y, int w, Graphics::Font *font) {
@@ -84,15 +83,13 @@ void MenuManager::checkMouseClick(int x, int y) {
 	}
 }
 
-
 void MenuManager::menuLoop() {
 	_events->pollEvent();
 
-	if(_events->_leftMouseClicked) {
+	if (_events->_leftMouseClicked) {
 		_events->_leftMouseClicked = false;
 		checkMouseClick(_events->_mouseX, _events->_mouseY);
-	}
-	else if (_events->_rightMouseClicked) {
+	} else if (_events->_rightMouseClicked) {
 		_events->_rightMouseClicked = false;
 		g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
 		g_engine->stateGame = GAME;
@@ -101,17 +98,16 @@ void MenuManager::menuLoop() {
 
 	memcpy(_compositeBuffer, _mainMenu, 640 * 400);
 
-
 	for (int i = 0; i < 4; i++) {
 		int itemIndex = _curInventoryPage * 4 + i;
-		if(g_engine->_inventoryItems.size() <= itemIndex)
+		if (g_engine->_inventoryItems.size() <= itemIndex)
 			continue;
 		InventoryObject item = g_engine->_res->getInventoryObject(g_engine->_inventoryItems[itemIndex]);
 		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 140 + (82 * i), 115 - (8 * i), 60, 60, 1);
 	}
 
 	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
-	for(int i = 0; _menuText.size() > i; i++) {
+	for (int i = 0; _menuText.size() > i; i++) {
 		drawColoredText(_screen, _menuText[i], 230, 200 + (i * 10), 200, g_engine->_smallFont);
 	}
 
@@ -219,7 +215,6 @@ void MenuManager::loadMenuTexts() {
 }
 
 void MenuManager::tearDown() {
-
 }
 
 Pelrock::MenuManager::~MenuManager() {
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 77d5d39b5b6..b9d3e87f463 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -21,8 +21,8 @@
 #ifndef PELROCK_MENU_H
 #define PELROCK_MENU_H
 
-#include "graphics/screen.h"
 #include "graphics/font.h"
+#include "graphics/screen.h"
 
 #include "pelrock/events.h"
 #include "pelrock/resources.h"
@@ -36,6 +36,7 @@ public:
 	void menuLoop();
 	void loadMenu();
 	byte _mainMenuPalette[768] = {0};
+
 private:
 	void checkMouseClick(int x, int y);
 	void loadMenuTexts();
diff --git a/engines/pelrock/module.mk b/engines/pelrock/module.mk
index c577cd7abe3..a01a7d58cc9 100644
--- a/engines/pelrock/module.mk
+++ b/engines/pelrock/module.mk
@@ -17,7 +17,8 @@ MODULE_OBJS = \
 	pathfinding.o \
 	events.o \
 	dialog.o \
-	menu.o
+	menu.o \
+	graphics.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_PELROCK), DYNAMIC_PLUGIN)
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index dc7c0b9f2cb..c4c888786b4 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -68,6 +68,7 @@ PelrockEngine::~PelrockEngine() {
 	delete _events;
 	delete _dialog;
 	delete _menu;
+	delete _graphics;
 }
 
 uint32 PelrockEngine::getFeatures() const {
@@ -85,12 +86,12 @@ Common::Error PelrockEngine::run() {
 	initGraphics(640, 400);
 	_screen = new Graphics::Screen();
 	_videoManager = new VideoManager(_screen, _events);
+	_graphics = new GraphicsManager();
 	_room = new RoomManager();
 	_res = new ResourceManager();
 	_sound = new SoundManager(_mixer);
-	_dialog = new DialogManager(_screen, _events);
+	_dialog = new DialogManager(_screen, _events, _graphics);
 	_menu = new MenuManager(_screen, _events, _res);
-
 	// Set the engine's debugger console
 	setDebugger(new PelrockConsole(this));
 
@@ -240,7 +241,7 @@ void PelrockEngine::playSoundIfNeeded() {
 	}
 }
 
-void PelrockEngine::renderScene(bool showTextOverlay) {
+bool PelrockEngine::renderScene(int overlayMode) {
 
 	_chrono->updateChrono();
 	if (_chrono->_gameTick) {
@@ -251,15 +252,19 @@ void PelrockEngine::renderScene(bool showTextOverlay) {
 		placeStickers();
 		updateAnimations();
 
-		if (showTextOverlay) {
+		if (overlayMode == 1) {
 			_dialog->displayChoices(_dialog->_currentChoices, _compositeBuffer);
+		} else if (overlayMode == 2) {
+			pickupIconFlash();
 		}
 
 		presentFrame();
 		updatePaletteAnimations();
 
 		_screen->markAllDirty();
+		return true;
 	}
+	return false;
 }
 
 void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
@@ -576,11 +581,11 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			_currentStep++;
 			if (_currentStep >= _currentContext.movementCount) {
 				_currentStep = 0;
+				alfredState.animState = ALFRED_IDLE;
 				if (_queuedAction.isQueued) {
 					doAction(_queuedAction.verb, &_room->_currentRoomHotspots[_queuedAction.hotspotIndex]);
 					_queuedAction.isQueued = false;
 				}
-				alfredState.animState = ALFRED_IDLE;
 			}
 		} else {
 			_currentContext.movementBuffer[_currentStep] = step;
@@ -1045,6 +1050,14 @@ void PelrockEngine::animateTalkingNPC(Sprite *animSet) {
 	drawSpriteToBuffer(_compositeBuffer, 640, frame, x, y, w, h, 255);
 }
 
+void PelrockEngine::pickupIconFlash() {
+	_graphics->showOverlay(65, _compositeBuffer);
+	InventoryObject item = _res->getInventoryObject(_currentHotspot->extra);
+	if (_chrono->getFrameCount() % kIconBlinkPeriod == 0) {
+		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 5, 400 - 60 - 5, 60, 60, 1);
+	}
+}
+
 void PelrockEngine::gameLoop() {
 	_events->pollEvent();
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index cbb0b9dd401..86b11252531 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -43,6 +43,7 @@
 #include "pelrock/fonts/large_font.h"
 #include "pelrock/fonts/small_font.h"
 #include "pelrock/fonts/small_font_double.h"
+#include "pelrock/graphics.h"
 #include "pelrock/menu.h"
 #include "pelrock/resources.h"
 #include "pelrock/room.h"
@@ -64,6 +65,7 @@ private:
 	PelrockEventManager *_events = nullptr;
 	DialogManager *_dialog = nullptr;
 	MenuManager *_menu = nullptr;
+	GraphicsManager *_graphics = nullptr;
 
 	void init();
 	void loadAnims();
@@ -109,6 +111,7 @@ private:
 	void drawNextFrame(Sprite *animSet);
 	void changeCursor(Cursor cursor);
 	void animateTalkingNPC(Sprite *animSet);
+	void pickupIconFlash();
 
 	void playSoundIfNeeded();
 	void gameLoop();
@@ -183,7 +186,6 @@ public:
 	Common::Array<int> _inventoryItems;
 	int _selectedInventoryItem = -1;
 
-
 public:
 	PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc);
 	~PelrockEngine() override;
@@ -231,20 +233,19 @@ public:
 	}
 
 	void setScreen(int s, AlfredDirection dir);
-	void renderScene(bool showTextOverlay = false);
+	bool renderScene(int overlayMode = OVERLAY_NONE);
 	void performActionTrigger(uint16 actionTrigger);
 
-    void executeAction(VerbIcon action, HotSpot *hotspot);
+	void executeAction(VerbIcon action, HotSpot *hotspot);
 
-    void openDrawer(HotSpot *hotspot);
-    void closeDrawer(HotSpot *hotspot);
-    void openDoor(HotSpot *hotspot);
-    void closeDoor(HotSpot *hotspot);
-    void pickUpAndDisable(HotSpot *hotspot);
+	void openDrawer(HotSpot *hotspot);
+	void closeDrawer(HotSpot *hotspot);
+	void openDoor(HotSpot *hotspot);
+	void closeDoor(HotSpot *hotspot);
+	void pickUpAndDisable(HotSpot *hotspot);
 	void pickUpPhoto(HotSpot *hotspot);
 	void pickYellowBook(HotSpot *hotspot);
 	void noOp(HotSpot *hotspot);
-
 };
 
 extern PelrockEngine *g_engine;
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 18c79aac5d9..4a9f1000d2c 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -314,7 +314,7 @@ Common::Array<Common::Array<Common::String>> ResourceManager::processTextData(by
 			pos++;
 			continue;
 		}
-		if( decode )
+		if (decode)
 			desc.append(1, decodeChar(data[pos]));
 		else
 			desc.append(1, data[pos]);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 25aed624a85..79d46904824 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -110,7 +110,7 @@ void RoomManager::removeSticker(int stickerIndex) {
 }
 
 bool RoomManager::hasSticker(int index) {
-	for(int i = 0; i < _currentRoomStickers.size(); i++) {
+	for (int i = 0; i < _currentRoomStickers.size(); i++) {
 		if (_currentRoomStickers[i].stickerIndex == index) {
 			return true;
 		}
@@ -446,7 +446,7 @@ Common::Array<Description> RoomManager::loadRoomTexts(Common::File *roomFile, in
 				}
 				if (data[pos] == 0xF8) {
 					description.actionTrigger = data[pos + 1] | data[pos + 2] << 8;
-					if( description.actionTrigger != 0 ) {
+					if (description.actionTrigger != 0) {
 						description.isAction = true;
 					}
 					pos += 2;
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index b529cf83fac..74b86f15d0b 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -42,7 +42,7 @@ public:
 	void addSticker(Sticker sticker);
 	void removeSticker(int index);
 	bool hasSticker(int index);
-	HotSpot * findHotspotByExtra(uint16 extra);
+	HotSpot *findHotspotByExtra(uint16 extra);
 	PaletteAnim *getPaletteAnimForRoom(int roomNumber);
 
 	Common::String getRoomName(int roomNumber) {
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index 9a22bdfa099..e354a8d452e 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -154,34 +154,33 @@ static const uint COUNTER_MASK = 0x1F;
 
 const int kMaxChannels = 15;
 
-
-
-	class GameRNG {
+class GameRNG {
 
 private:
-    uint32_t _state;
+	uint32_t _state;
+
 public:
-    // LCG constants (from JUEGO.EXE @ 0x0002b12f)
-    static constexpr uint32_t MULTIPLIER = 0x41C64E6D; // 1103515245
-    static constexpr uint32_t INCREMENT  = 0x3039;     // 12345
-
-    GameRNG(uint32_t seed = 0) {
-        _state = seed & 0xFFFFFFFF;
-    }
-
-    // Generate next random number (0-32767)
-    uint16_t nextRandom() {
-        _state = (_state * MULTIPLIER + INCREMENT) & 0xFFFFFFFF;
-        return static_cast<uint16_t>((_state >> 16) & 0x7FFF);
-    }
-
-    uint32_t getState() const {
-        return _state;
-    }
-
-    void setState(uint32_t state) {
-        _state = state & 0xFFFFFFFF;
-    }
+	// LCG constants (from JUEGO.EXE @ 0x0002b12f)
+	static constexpr uint32_t MULTIPLIER = 0x41C64E6D; // 1103515245
+	static constexpr uint32_t INCREMENT = 0x3039;      // 12345
+
+	GameRNG(uint32_t seed = 0) {
+		_state = seed & 0xFFFFFFFF;
+	}
+
+	// Generate next random number (0-32767)
+	uint16_t nextRandom() {
+		_state = (_state * MULTIPLIER + INCREMENT) & 0xFFFFFFFF;
+		return static_cast<uint16_t>((_state >> 16) & 0x7FFF);
+	}
+
+	uint32_t getState() const {
+		return _state;
+	}
+
+	void setState(uint32_t state) {
+		_state = state & 0xFFFFFFFF;
+	}
 };
 
 class SoundManager {
@@ -221,9 +220,6 @@ private:
 	GameRNG _rng = GameRNG(0);
 };
 
-
-
-
 } // End of namespace Pelrock
 
 #endif // PELROCK_SOUND_H
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index e1a11da6dd7..b99c1aa5eb9 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -89,6 +89,10 @@ const int kAlfredIdleAnimationFrameCount = 300;
 
 #define ALFRED_COLOR 0x0D
 
+#define OVERLAY_NONE 0
+#define OVERLAY_CHOICES 1
+#define OVERLAY_PICKUP_ICON 2
+
 const byte kIconBlinkPeriod = 4;
 
 enum AlfredAnimState {
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index de6c4f3b2b6..5fa4e5ad68d 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -308,7 +308,7 @@ byte decodeChar(byte b) {
 	case 0x83:
 		return special_chars[0];
 	case 0x80:
-		return  special_chars[3]; // n tilde
+		return special_chars[3]; // n tilde
 	case 0x7F:
 		return special_chars[4];
 	case 0x7E:


Commit: 2e4bbe82e602964acf73837f68e6de2ebf5ac768
    https://github.com/scummvm/scummvm/commit/2e4bbe82e602964acf73837f68e6de2ebf5ac768
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:33+02:00

Commit Message:
PELROCK: Cleanup of pelrock engine class

Changed paths:
    engines/pelrock/graphics.cpp
    engines/pelrock/graphics.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index 8a1743bbff7..355b773e5f9 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -44,4 +44,29 @@ Common::Point GraphicsManager::showOverlay(int height, byte *buf) {
 	return Common::Point(overlayX, overlayY);
 }
 
+byte *GraphicsManager::grabBackgroundSlice(byte *buf, int x, int y, int w, int h) {
+	byte *bg = new byte[w * h];
+	for (int j = 0; j < w; j++) {
+		for (int i = 0; i < h; i++) {
+			int idx = i * w + j;
+			if (y + i < 400 && x + j < 640) {
+				*(bg + idx) = buf[(y + i) * 640 + (x + j)];
+			}
+		}
+	}
+	return bg;
+}
+
+void GraphicsManager::putBackgroundSlice(byte *buf, int x, int y, int w, int h, byte *slice) {
+	for (int i = 0; i < w; i++) {
+		for (int j = 0; j < h; j++) {
+			int index = (j * w + i);
+			if (x + i < 640 && y + j < 400) {
+				buf[(y + j) * 640 + (x + i)] = slice[index];
+				// *(byte *)_screen->getBasePtr(x + i, y + j) = slice[index];
+			}
+		}
+	}
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/graphics.h b/engines/pelrock/graphics.h
index a6c607706a3..ab604a63d56 100644
--- a/engines/pelrock/graphics.h
+++ b/engines/pelrock/graphics.h
@@ -32,6 +32,9 @@ public:
 	~GraphicsManager();
 
 	Common::Point showOverlay(int height, byte *buf);
+	byte *grabBackgroundSlice(byte *buf, int x, int y, int w, int h);
+	void putBackgroundSlice(byte *buf, int x, int y, int w, int h, byte *slice);
+
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index c4c888786b4..dc43e6ab911 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -165,28 +165,6 @@ void PelrockEngine::loadAnims() {
 	_res->loadAlfredAnims();
 }
 
-byte *PelrockEngine::grabBackgroundSlice(int x, int y, int w, int h) {
-	byte *bg = new byte[w * h];
-	for (int j = 0; j < w; j++) {
-		for (int i = 0; i < h; i++) {
-			int idx = i * w + j;
-			if (y + i < 400 && x + j < 640) {
-				*(bg + idx) = _currentBackground[(y + i) * 640 + (x + j)];
-			}
-		}
-	}
-	return bg;
-}
-
-void PelrockEngine::putBackgroundSlice(int x, int y, int w, int h, byte *slice) {
-	for (int i = 0; i < w; i++) {
-		for (int j = 0; j < h; j++) {
-			int index = (j * w + i);
-			if (x + i < 640 && y + j < 400)
-				*(byte *)_screen->getBasePtr(x + i, y + j) = slice[index];
-		}
-	}
-}
 
 Common::Array<VerbIcon> PelrockEngine::availableActions(HotSpot *hotspot) {
 	if (hotspot == nullptr) {
@@ -522,23 +500,6 @@ void PelrockEngine::lookAt(HotSpot *hotspot) {
 	_actionPopupState.isActive = false;
 }
 
-void PelrockEngine::renderText(Common::Array<Common::String> lines, int color, int baseX, int baseY) {
-
-	int maxW = 0;
-	for (size_t i = 0; i < lines.size(); i++) {
-		Common::Rect r = _largeFont->getBoundingBox(lines[i]);
-		if (r.width() > maxW) {
-			maxW = r.width();
-		}
-	}
-	int lineSize = lines.size();
-	for (size_t i = 0; i < lines.size(); i++) {
-		int textX = baseX - (maxW / 2);
-		int textY = baseY - (lineSize * 25) + (i * 25);
-		drawText(_largeFont, lines[i], textX, textY, maxW, color);
-	}
-}
-
 void PelrockEngine::chooseAlfredStateAndDraw() {
 	if (alfredState.idleFrameCounter++ >= kAlfredIdleAnimationFrameCount &&
 		alfredState.animState == ALFRED_IDLE &&
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 86b11252531..797f6ce40ca 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -75,12 +75,6 @@ private:
 	*/
 	void walkTo(int x, int y);
 
-	void talk(byte object);
-	void sayNPC(Sprite *anim, Common::String text, byte color);
-
-	byte *grabBackgroundSlice(int x, int y, int w, int h);
-	void putBackgroundSlice(int x, int y, int w, int h, byte *slice);
-
 	Common::Array<VerbIcon> availableActions(HotSpot *hotspot);
 	VerbIcon isActionUnder(int x, int y);
 	bool isItemUnder(int x, int y);
@@ -88,6 +82,7 @@ private:
 	int isHotspotUnder(int x, int y);
 	Exit *isExitUnder(int x, int y);
 	Sprite *isSpriteUnder(int x, int y);
+
 	void showActionBalloon(int posx, int posy, int curFrame);
 
 	void checkMouse();
@@ -105,7 +100,6 @@ private:
 	void talkTo(HotSpot *hotspot);
 	void lookAt(HotSpot *hotspot);
 
-	void renderText(Common::Array<Common::String> lines, int color, int x, int y);
 	void chooseAlfredStateAndDraw();
 	void drawAlfred(byte *buf);
 	void drawNextFrame(Sprite *animSet);
@@ -114,8 +108,8 @@ private:
 	void pickupIconFlash();
 
 	void playSoundIfNeeded();
+
 	void gameLoop();
-	void menuLoop();
 	void extraScreenLoop();
 
 	void checkMouseHover();
@@ -125,6 +119,7 @@ private:
 	void calculateScalingMasks();
 	ScaleCalculation calculateScaling(int yPos, ScalingParams scalingParams);
 
+
 	Common::Array<Common::Array<int>> _widthScalingTable;
 	Common::Array<Common::Array<int>> _heightScalingTable;
 
@@ -132,24 +127,15 @@ private:
 	int _currentStep = 0;
 	PathContext _currentContext;
 
-	// text display
-	byte _textColor = 0;
-	Common::Point _textPos;
-	int _textDurationFrames = 0;
-	Common::Array<Common::Array<Common::String>> _currentTextPages = Common::Array<Common::Array<Common::String>>();
-	int _currentTextPageIndex = 0;
-
-	// Alfred
-	bool alfredFrameSkip = false;
-	bool isAlkfredWalking = false;
-
 	byte *_currentBackground = nullptr; // Clean background - NEVER modified
 	byte *_extraScreen = nullptr;
+
 	ActionPopupState _actionPopupState;
 
 	HotSpot *_currentHotspot = nullptr;
 
 	Common::Point _curWalkTarget;
+
 	QueuedAction _queuedAction;
 
 	bool showShadows = false;
@@ -186,6 +172,7 @@ public:
 	Common::Array<int> _inventoryItems;
 	int _selectedInventoryItem = -1;
 
+
 public:
 	PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc);
 	~PelrockEngine() override;
@@ -234,15 +221,16 @@ public:
 
 	void setScreen(int s, AlfredDirection dir);
 	bool renderScene(int overlayMode = OVERLAY_NONE);
+
+	// Actions
 	void performActionTrigger(uint16 actionTrigger);
 
 	void executeAction(VerbIcon action, HotSpot *hotspot);
-
-	void openDrawer(HotSpot *hotspot);
-	void closeDrawer(HotSpot *hotspot);
-	void openDoor(HotSpot *hotspot);
-	void closeDoor(HotSpot *hotspot);
-	void pickUpAndDisable(HotSpot *hotspot);
+    void openDrawer(HotSpot *hotspot);
+    void closeDrawer(HotSpot *hotspot);
+    void openDoor(HotSpot *hotspot);
+    void closeDoor(HotSpot *hotspot);
+    void pickUpAndDisable(HotSpot *hotspot);
 	void pickUpPhoto(HotSpot *hotspot);
 	void pickYellowBook(HotSpot *hotspot);
 	void noOp(HotSpot *hotspot);


Commit: 149fd0a54e1def62ce14d67ecd4ca309db4d572b
    https://github.com/scummvm/scummvm/commit/149fd0a54e1def62ce14d67ecd4ca309db4d572b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:33+02:00

Commit Message:
PELROCK: Better handling of mouse events

Changed paths:
    engines/pelrock/events.cpp
    engines/pelrock/events.h
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/events.cpp b/engines/pelrock/events.cpp
index bc81829f64b..766dfccc1f8 100644
--- a/engines/pelrock/events.cpp
+++ b/engines/pelrock/events.cpp
@@ -63,7 +63,10 @@ void PelrockEventManager::pollEvent() {
 			break;
 		case Common::EVENT_LBUTTONUP:
 			if (_leftMouseButton == 1) {
-				_leftMouseClicked = true;
+				// Don't treat as regular click if we're in popup selection mode
+				if (!_popupSelectionMode) {
+					_leftMouseClicked = true;
+				}
 				_mouseClickX = _event.mouse.x;
 				_mouseClickY = _event.mouse.y;
 				_longClicked = false;
@@ -97,7 +100,7 @@ void PelrockEventManager::pollEvent() {
 		if (elapsedLongClick >= kDoubleClickDelay) {
 			elapsedLongClick = 0;
 			_longClicked = true;
-			_leftMouseButton = 0;
+			_popupSelectionMode = true;
 		}
 	}
 }
diff --git a/engines/pelrock/events.h b/engines/pelrock/events.h
index d25fb456cdd..f39ab56cfb5 100644
--- a/engines/pelrock/events.h
+++ b/engines/pelrock/events.h
@@ -29,8 +29,6 @@ static const int kDoubleClickDelay = 300; // in milliseconds
 class PelrockEventManager {
 private:
 	Common::Event _event;
-	bool _leftMouseButton = 0;
-	bool _rightMouseButton = 0;
 	uint32 _clickTime = 0;
 
 public:
@@ -41,6 +39,9 @@ public:
 	bool _leftMouseClicked = false;
 	bool _longClicked = false;
 	bool _rightMouseClicked = false;
+	bool _popupSelectionMode = false;
+	bool _leftMouseButton = 0;
+	bool _rightMouseButton = 0;
 	Common::Event _lastKeyEvent;
 	PelrockEventManager();
 	void pollEvent();
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index dc43e6ab911..1c27602a347 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -165,7 +165,6 @@ void PelrockEngine::loadAnims() {
 	_res->loadAlfredAnims();
 }
 
-
 Common::Array<VerbIcon> PelrockEngine::availableActions(HotSpot *hotspot) {
 	if (hotspot == nullptr) {
 		return Common::Array<VerbIcon>();
@@ -230,9 +229,9 @@ bool PelrockEngine::renderScene(int overlayMode) {
 		placeStickers();
 		updateAnimations();
 
-		if (overlayMode == 1) {
+		if (overlayMode == OVERLAY_CHOICES) {
 			_dialog->displayChoices(_dialog->_currentChoices, _compositeBuffer);
-		} else if (overlayMode == 2) {
+		} else if (overlayMode == OVERLAY_PICKUP_ICON) {
 			pickupIconFlash();
 		}
 
@@ -289,7 +288,31 @@ void PelrockEngine::executeAction(VerbIcon action, HotSpot *hotspot) {
 }
 
 void PelrockEngine::checkMouse() {
-	if (_events->_leftMouseClicked) {
+
+	// Cancel walking animation on mouse click
+	if(_events->_leftMouseButton) {
+		alfredState.curFrame = 0;
+		alfredState.animState = ALFRED_IDLE;
+	}
+
+	// Handle mouse release after long press (popup selection mode)
+	if (_events->_popupSelectionMode && !_events->_leftMouseButton) {
+		// Mouse was released while popup is active
+		VerbIcon actionClicked = isActionUnder(_events->_mouseX, _events->_mouseY);
+		if (actionClicked != NO_ACTION && _currentHotspot != nullptr) {
+			// Action was selected - queue it
+			walkTo(_currentHotspot->x + _currentHotspot->w / 2, _currentHotspot->y + _currentHotspot->h);
+			_queuedAction = QueuedAction{actionClicked, _currentHotspot->index, true};
+		} else {
+			// Released outside popup - just close it
+			_queuedAction = QueuedAction{NO_ACTION, -1, false};
+			_currentHotspot = nullptr;
+		}
+		_actionPopupState.isActive = false;
+		_events->_popupSelectionMode = false;
+		alfredState.idleFrameCounter = 0;
+	} else if (_events->_leftMouseClicked) {
+		// Regular click (not during popup mode)
 		checkMouseClick(_events->_mouseClickX, _events->_mouseClickY);
 		alfredState.idleFrameCounter = 0;
 		_events->_leftMouseClicked = false;
@@ -788,9 +811,9 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 void PelrockEngine::checkLongMouseClick(int x, int y) {
 	int hotspotIndex = isHotspotUnder(_events->_mouseX, _events->_mouseY);
 
-	if (hotspotIndex != -1) {
+	if (hotspotIndex != -1 && !_actionPopupState.isActive) {
 
-		_actionPopupState.x = alfredState.x - kBalloonWidth / 2;
+		_actionPopupState.x = alfredState.x + kAlfredFrameWidth/2 - kBalloonWidth / 2;
 		if (_actionPopupState.x < 0)
 			_actionPopupState.x = 0;
 		if (_actionPopupState.x + kBalloonWidth > 640) {
@@ -1100,22 +1123,8 @@ bool PelrockEngine::isAlfredUnder(int x, int y) {
 }
 
 void PelrockEngine::checkMouseClick(int x, int y) {
-
-	if (_actionPopupState.isActive) {
-		// Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
-		VerbIcon actionClicked = isActionUnder(x, y);
-		if (actionClicked != NO_ACTION) {
-			_actionPopupState.isActive = false;
-			if (_currentHotspot != nullptr) {
-				walkTo(_currentHotspot->x + _currentHotspot->w / 2, _currentHotspot->y + _currentHotspot->h);
-				_queuedAction = QueuedAction{actionClicked, _currentHotspot->index, true};
-				return;
-			}
-		}
-	}
-
+	// This handles regular clicks (not popup selection)
 	_queuedAction = QueuedAction{NO_ACTION, -1, false};
-
 	_actionPopupState.isActive = false;
 	_currentHotspot = nullptr;
 


Commit: 70b784d2d0c0b4a740dbacecfe4432f798afebe4
    https://github.com/scummvm/scummvm/commit/70b784d2d0c0b4a740dbacecfe4432f798afebe4
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:34+02:00

Commit Message:
PELROCK: Calculate facing direction

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 1c27602a347..13189d3da44 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -233,6 +233,8 @@ bool PelrockEngine::renderScene(int overlayMode) {
 			_dialog->displayChoices(_dialog->_currentChoices, _compositeBuffer);
 		} else if (overlayMode == OVERLAY_PICKUP_ICON) {
 			pickupIconFlash();
+		} else if (overlayMode == OVERLAY_ACTION) {
+			showActionBalloon(_actionPopupState.x, _actionPopupState.y, _actionPopupState.curFrame);
 		}
 
 		presentFrame();
@@ -290,8 +292,9 @@ void PelrockEngine::executeAction(VerbIcon action, HotSpot *hotspot) {
 void PelrockEngine::checkMouse() {
 
 	// Cancel walking animation on mouse click
-	if(_events->_leftMouseButton) {
+	if (_events->_leftMouseButton) {
 		alfredState.curFrame = 0;
+		alfredState.idleFrameCounter = 0;
 		alfredState.animState = ALFRED_IDLE;
 	}
 
@@ -310,19 +313,15 @@ void PelrockEngine::checkMouse() {
 		}
 		_actionPopupState.isActive = false;
 		_events->_popupSelectionMode = false;
-		alfredState.idleFrameCounter = 0;
 	} else if (_events->_leftMouseClicked) {
 		// Regular click (not during popup mode)
 		checkMouseClick(_events->_mouseClickX, _events->_mouseClickY);
-		alfredState.idleFrameCounter = 0;
 		_events->_leftMouseClicked = false;
 		_actionPopupState.isActive = false;
 	} else if (_events->_longClicked) {
-		alfredState.idleFrameCounter = 0;
 		checkLongMouseClick(_events->_mouseClickX, _events->_mouseClickY);
 		_events->_longClicked = false;
 	} else if (_events->_rightMouseClicked) {
-		alfredState.idleFrameCounter = 0;
 		g_system->getPaletteManager()->setPalette(_menu->_mainMenuPalette, 0, 256);
 		_events->_rightMouseClicked = false;
 		stateGame = SETTINGS;
@@ -367,7 +366,7 @@ void PelrockEngine::updateAnimations() {
 
 void PelrockEngine::presentFrame() {
 	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
-	// paintDebugLayer();
+	paintDebugLayer();
 	_screen->markAllDirty();
 }
 
@@ -566,6 +565,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			if (_currentStep >= _currentContext.movementCount) {
 				_currentStep = 0;
 				alfredState.animState = ALFRED_IDLE;
+				alfredState.direction = calculateAlfredsDirection(_currentHotspot);
 				if (_queuedAction.isQueued) {
 					doAction(_queuedAction.verb, &_room->_currentRoomHotspots[_queuedAction.hotspotIndex]);
 					_queuedAction.isQueued = false;
@@ -608,7 +608,8 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			break;
 		}
 		drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCombFrames[alfredState.direction][alfredState.curFrame], alfredState.x, alfredState.y - kAlfredFrameHeight, 51, 102, 255);
-		alfredState.curFrame++;
+		if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0)
+			alfredState.curFrame++;
 		break;
 	case ALFRED_INTERACTING:
 		if (alfredState.curFrame >= interactingAnimLength) {
@@ -813,7 +814,7 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 
 	if (hotspotIndex != -1 && !_actionPopupState.isActive) {
 
-		_actionPopupState.x = alfredState.x + kAlfredFrameWidth/2 - kBalloonWidth / 2;
+		_actionPopupState.x = alfredState.x + kAlfredFrameWidth / 2 - kBalloonWidth / 2;
 		if (_actionPopupState.x < 0)
 			_actionPopupState.x = 0;
 		if (_actionPopupState.x + kBalloonWidth > 640) {
@@ -977,7 +978,6 @@ Exit *PelrockEngine::isExitUnder(int x, int y) {
 }
 
 void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
-
 	drawSpriteToBuffer(_compositeBuffer, 640, _res->_popUpBalloon + (curFrame * kBalloonHeight * kBalloonWidth), posx, posy, kBalloonWidth, kBalloonHeight, 255);
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
 
@@ -996,6 +996,11 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 		}
 		drawSpriteToBuffer(_compositeBuffer, 640, _res->getInventoryObject(_selectedInventoryItem).iconData, posx + 20 + (actions.size() * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
 	}
+	if (_actionPopupState.curFrame < 3) {
+		_actionPopupState.curFrame++;
+	} else {
+		_actionPopupState.curFrame = 0;
+	}
 }
 
 void PelrockEngine::animateTalkingNPC(Sprite *animSet) {
@@ -1044,16 +1049,8 @@ void PelrockEngine::pickupIconFlash() {
 
 void PelrockEngine::gameLoop() {
 	_events->pollEvent();
-
-	if (inConversation) {
-		// TODO: Pass actual conversation data from room
-		// For now, using nullptr to disable - actual data needs to be loaded
-		_dialog->startConversation(nullptr, 0);
-		inConversation = false;
-	} else {
-		checkMouse();
-		renderScene();
-	}
+	checkMouse();
+	renderScene();
 }
 
 void PelrockEngine::extraScreenLoop() {
@@ -1085,6 +1082,50 @@ void PelrockEngine::walkTo(int x, int y) {
 	alfredState.curFrame = 0;
 }
 
+AlfredDirection PelrockEngine::calculateAlfredsDirection(HotSpot *hotspot) {
+
+	AlfredDirection calculatedDirection = ALFRED_DOWN;
+	if (hotspot->isSprite) {
+		// Check if Alfred's left edge is past sprite's right edge
+		if (hotspot->x + hotspot->w < alfredState.x) {
+			calculatedDirection = ALFRED_LEFT; // Face LEFT
+		}
+		// Check if Alfred's right edge is before sprite's left edge
+		else if ((alfredState.x + kAlfredFrameWidth - alfredState.scaledX) < hotspot->x) {
+			calculatedDirection = ALFRED_RIGHT; // Face RIGHT
+		}
+		// Alfred is horizontally overlapping with sprite
+		else {
+			// Check if Alfred's top is above sprite's bottom OR Alfred is within sprite's Y range
+			if (((alfredState.y + kAlfredFrameHeight - alfredState.scaledY) < hotspot->y) ||
+				(alfredState.y <= hotspot->y + hotspot->h &&
+				 hotspot->zOrder <= ((399 - alfredState.y) / 2) + 10)) {
+				calculatedDirection = ALFRED_DOWN; // Face DOWN
+			} else {
+				calculatedDirection = ALFRED_UP; // Face UP
+			}
+		}
+	} else {
+		// Check if Alfred's left edge is past hotspot's right edge
+		if (hotspot->x + hotspot->w < alfredState.x) {
+			calculatedDirection = ALFRED_LEFT; // Face LEFT
+		}
+		// Check if Alfred's right edge is before hotspot's left edge
+		else if ((alfredState.x + kAlfredFrameWidth - alfredState.scaledX) < hotspot->x) {
+			calculatedDirection = ALFRED_RIGHT; // Face RIGHT
+		}
+		// Check vertical positioning
+		else if (((alfredState.y + kAlfredFrameHeight - alfredState.scaledY) < hotspot->y) ||
+				 (alfredState.y <= hotspot->y + hotspot->h &&
+				  (hotspot->actionFlags & 0x80) == 0x80)) {
+			calculatedDirection = ALFRED_DOWN; // Face DOWN
+		} else {
+			calculatedDirection = ALFRED_UP; // Face UP
+		}
+	}
+	return calculatedDirection;
+}
+
 VerbIcon PelrockEngine::isActionUnder(int x, int y) {
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
 	for (int i = 0; i < actions.size(); i++) {
@@ -1137,11 +1178,14 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, _events->_mouseX, _events->_mouseY, isHotspotUnder, isHotspotUnder ? &_room->_currentRoomHotspots[hotspotIndex] : nullptr);
 	_curWalkTarget = walkTarget;
 
-	// if (hotspotIndex != -1) {
-	// 	_currentHotspot = &_room->_currentRoomHotspots[hotspotIndex];
-	// 	walkTarget.x = _currentHotspot->x + _currentHotspot->w / 2;
-	// 	walkTarget.y = _currentHotspot->y + _currentHotspot->h;
-	// }
+	if (hotspotIndex != -1) {
+		_currentHotspot = &_room->_currentRoomHotspots[hotspotIndex];
+		walkTarget.x = _currentHotspot->x + _currentHotspot->w / 2;
+		walkTarget.y = _currentHotspot->y + _currentHotspot->h;
+	}
+	else {
+		_currentHotspot = nullptr;
+	}
 
 	walkTo(walkTarget.x, walkTarget.y);
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 797f6ce40ca..a700fdd9719 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -74,6 +74,7 @@ private:
 		Walking alforithm
 	*/
 	void walkTo(int x, int y);
+	AlfredDirection calculateAlfredsDirection(HotSpot *hotspot);
 
 	Common::Array<VerbIcon> availableActions(HotSpot *hotspot);
 	VerbIcon isActionUnder(int x, int y);
@@ -142,7 +143,6 @@ private:
 
 	// JAVA
 	bool shouldPlayIntro = false;
-	bool inConversation = false;
 	bool gameInitialized = false;
 	bool screenReady = false;
 
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 79d46904824..4d5065a42f2 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -226,6 +226,7 @@ Common::Array<HotSpot> RoomManager::loadHotspots(Common::File *roomFile, int roo
 		spot.y = roomFile->readSint16LE();
 		spot.w = roomFile->readByte();
 		spot.h = roomFile->readByte();
+		spot.isSprite = false;
 		spot.extra = roomFile->readSint16LE();
 		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, spot.actionFlags, spot.x, spot.y, spot.w, spot.h, spot.extra);
 		hotspots.push_back(spot);
@@ -252,6 +253,8 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 		thisHotspot.extra = anims[i].extra;
 		thisHotspot.actionFlags = anims[i].actionFlags;
 		thisHotspot.isEnabled = !anims[i].isDisabled;
+		thisHotspot.isSprite = true;
+		thisHotspot.zOrder = anims[i].zOrder;
 		hotspots.push_back(thisHotspot);
 	}
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index b99c1aa5eb9..e2266ec0ac6 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -92,6 +92,7 @@ const int kAlfredIdleAnimationFrameCount = 300;
 #define OVERLAY_NONE 0
 #define OVERLAY_CHOICES 1
 #define OVERLAY_PICKUP_ICON 2
+#define OVERLAY_ACTION 3
 
 const byte kIconBlinkPeriod = 4;
 
@@ -126,7 +127,8 @@ struct AlfredState {
 	uint16 movementSpeed = 6; // pixels per frame
 	uint16 x = 319;
 	uint16 y = 302;
-	float currentScale = 1.0f;
+	uint16 scaledX = 0;
+	uint16 scaledY = 0;
 	int idleFrameCounter = 0;
 };
 
@@ -204,6 +206,8 @@ struct HotSpot {
 	byte actionFlags;
 	int16 extra;
 	bool isEnabled = true;
+	bool isSprite = false;
+	byte zOrder = 0;
 };
 
 struct TalkingAnimHeader {


Commit: 262ec696c5f9b4bd84394f4af63f6a9f059945a0
    https://github.com/scummvm/scummvm/commit/262ec696c5f9b4bd84394f4af63f6a9f059945a0
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:34+02:00

Commit Message:
PELROCK: Fixes crash with non-existing hotspot

Changed paths:
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 13189d3da44..b59af0a0d04 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -565,7 +565,8 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			if (_currentStep >= _currentContext.movementCount) {
 				_currentStep = 0;
 				alfredState.animState = ALFRED_IDLE;
-				alfredState.direction = calculateAlfredsDirection(_currentHotspot);
+				if(_currentHotspot != nullptr)
+					alfredState.direction = calculateAlfredsDirection(_currentHotspot);
 				if (_queuedAction.isQueued) {
 					doAction(_queuedAction.verb, &_room->_currentRoomHotspots[_queuedAction.hotspotIndex]);
 					_queuedAction.isQueued = false;
@@ -1127,6 +1128,9 @@ AlfredDirection PelrockEngine::calculateAlfredsDirection(HotSpot *hotspot) {
 }
 
 VerbIcon PelrockEngine::isActionUnder(int x, int y) {
+	if (_currentHotspot == nullptr) {
+		return NO_ACTION;
+	}
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
 	for (int i = 0; i < actions.size(); i++) {
 		int actionX = _actionPopupState.x + 20 + (i * (kVerbIconWidth + 2));
@@ -1174,19 +1178,10 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 	if (hotspotIndex != -1) {
 		isHotspotUnder = true;
 	}
-
-	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, _events->_mouseX, _events->_mouseY, isHotspotUnder, isHotspotUnder ? &_room->_currentRoomHotspots[hotspotIndex] : nullptr);
+	_currentHotspot = isHotspotUnder ? &_room->_currentRoomHotspots[hotspotIndex] : nullptr;
+	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, _events->_mouseX, _events->_mouseY, isHotspotUnder, _currentHotspot);
 	_curWalkTarget = walkTarget;
 
-	if (hotspotIndex != -1) {
-		_currentHotspot = &_room->_currentRoomHotspots[hotspotIndex];
-		walkTarget.x = _currentHotspot->x + _currentHotspot->w / 2;
-		walkTarget.y = _currentHotspot->y + _currentHotspot->h;
-	}
-	else {
-		_currentHotspot = nullptr;
-	}
-
 	walkTo(walkTarget.x, walkTarget.y);
 
 	// { // For quick room navigation
@@ -1253,6 +1248,7 @@ void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 		return;
 	}
 	_sound->stopAllSounds();
+	_currentHotspot = nullptr;
 	alfredState.direction = dir;
 	alfredState.animState = ALFRED_IDLE;
 	_currentStep = 0;


Commit: 86f6b84e837694f74a01aedf8f1273ed160a13ac
    https://github.com/scummvm/scummvm/commit/86f6b84e837694f74a01aedf8f1273ed160a13ac
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:34+02:00

Commit Message:
PELROCK: Pickup animation

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pathfinding.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 3693cb5daf5..69724377a5f 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -23,6 +23,7 @@
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/util.h"
+#include "pelrock.h"
 
 namespace Pelrock {
 
@@ -34,6 +35,7 @@ const ActionEntry actionTable[] = {
 	{268, CLOSE, &PelrockEngine::closeDoor},
 	{3, PICKUP, &PelrockEngine::pickUpPhoto},
 	{0, PICKUP, &PelrockEngine::pickYellowBook}, // Generic pickup for other items
+    {4, PICKUP, &PelrockEngine::pickUpBrick}, // Brick
 
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOp}, // Generic pickup action
@@ -100,6 +102,10 @@ void PelrockEngine::pickYellowBook(HotSpot *hotspot) {
 	_room->addSticker(_res->getSticker(95));
 }
 
+void PelrockEngine::pickUpBrick(HotSpot *hotspot) {
+    _room->addSticker(_res->getSticker(133));
+}
+
 void PelrockEngine::noOp(HotSpot *hotspot) {
 	// Do nothing
 }
diff --git a/engines/pelrock/pathfinding.cpp b/engines/pelrock/pathfinding.cpp
index bc78b4c68ea..c6538d91273 100644
--- a/engines/pelrock/pathfinding.cpp
+++ b/engines/pelrock/pathfinding.cpp
@@ -144,6 +144,7 @@ Common::Point calculateWalkTarget(Common::Array<WalkBox> &walkboxes,
 		// Hovering over hotspot - use hotspot center-bottom
 		sourceX = hotspot->x + hotspot->w / 2;
 		sourceY = hotspot->y + hotspot->h;
+
 	}
 
 	// else: use sourceX, sourceY as passed (mouse position)
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index b59af0a0d04..b710156c738 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -387,6 +387,12 @@ void PelrockEngine::paintDebugLayer() {
 		_smallFont->drawString(_screen, Common::String::format("%d", i), box.x + 2, box.y + 2, 640, 14);
 	}
 
+	for(int i = 0; i< _room->_currentRoomExits.size(); i++) {
+		Exit exit = _room->_currentRoomExits[i];
+		drawRect(_screen, exit.x, exit.y, exit.w, exit.h, 200 + i);
+		_smallFont->drawString(_screen, Common::String::format("Exit %d -> Room %d", i, exit.targetRoom), exit.x + 2, exit.y + 2, 640, 14);
+	}
+
 	drawPos(_screen, alfredState.x, alfredState.y, 13);
 	drawPos(_screen, alfredState.x, alfredState.y - kAlfredFrameHeight, 13);
 	drawPos(_screen, _curWalkTarget.x, _curWalkTarget.y, 100);
@@ -402,9 +408,7 @@ void PelrockEngine::paintDebugLayer() {
 void PelrockEngine::placeStickers() {
 	for (int i = 0; i < _room->_currentRoomStickers.size(); i++) {
 		Sticker sticker = _room->_currentRoomStickers[i];
-		if (sticker.roomNumber == _room->_currentRoomNumber) {
-			placeSticker(sticker);
-		}
+		placeSticker(sticker);
 	}
 }
 
@@ -496,6 +500,8 @@ void PelrockEngine::doAction(VerbIcon action, HotSpot *hotspot) {
 		talkTo(hotspot);
 		break;
 	case PICKUP:
+		alfredState.animState = ALFRED_INTERACTING;
+		alfredState.curFrame = 0;
 		pickUpAndDisable(hotspot);
 		executeAction(PICKUP, hotspot);
 		break;
@@ -530,6 +536,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		alfredState.curFrame = 0;
 		alfredState.animState = ALFRED_COMB;
 	}
+	debug("Alfred state: %d, pos (%d, %d) curFrame %d", alfredState.animState, alfredState.x, alfredState.y, alfredState.curFrame);
 	switch (alfredState.animState) {
 	case ALFRED_WALKING: {
 		MovementStep step = _currentContext.movementBuffer[_currentStep];
@@ -565,11 +572,14 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			if (_currentStep >= _currentContext.movementCount) {
 				_currentStep = 0;
 				alfredState.animState = ALFRED_IDLE;
+				alfredState.curFrame = 0;
 				if(_currentHotspot != nullptr)
 					alfredState.direction = calculateAlfredsDirection(_currentHotspot);
+
 				if (_queuedAction.isQueued) {
 					doAction(_queuedAction.verb, &_room->_currentRoomHotspots[_queuedAction.hotspotIndex]);
 					_queuedAction.isQueued = false;
+					break;
 				}
 			}
 		} else {
@@ -615,9 +625,11 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 	case ALFRED_INTERACTING:
 		if (alfredState.curFrame >= interactingAnimLength) {
 			alfredState.curFrame = 0;
+			alfredState.animState = ALFRED_IDLE;
 		}
 		drawAlfred(_res->alfredInteractFrames[alfredState.direction][alfredState.curFrame]);
-		alfredState.curFrame++;
+		if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0)
+			alfredState.curFrame++;
 		break;
 	default:
 		drawAlfred(_res->alfredIdle[alfredState.direction]);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index a700fdd9719..77ecb0cb53f 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -233,6 +233,7 @@ public:
     void pickUpAndDisable(HotSpot *hotspot);
 	void pickUpPhoto(HotSpot *hotspot);
 	void pickYellowBook(HotSpot *hotspot);
+	void pickUpBrick(HotSpot *hotspot);
 	void noOp(HotSpot *hotspot);
 };
 


Commit: e960d138d9a73eea979373bf76d60b9eec13b2d8
    https://github.com/scummvm/scummvm/commit/e960d138d9a73eea979373bf76d60b9eec13b2d8
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:34+02:00

Commit Message:
PELROCK: State change through function

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index c1cbc8a3fdf..566d9e1b667 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -171,7 +171,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		int yPos = 0;
 
 		if (speakerId == ALFRED_COLOR) {
-			g_engine->alfredState.animState = ALFRED_TALKING;
+			g_engine->alfredState.setState(ALFRED_TALKING);
 			if (_curSprite != nullptr) {
 				_curSprite->isTalking = false;
 			}
@@ -179,7 +179,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 			xPos = g_engine->alfredState.x + kAlfredFrameWidth / 2 - maxWidth / 2;
 			yPos = g_engine->alfredState.y - kAlfredFrameHeight - height; // Above sprite, adjust for line
 		} else {
-			g_engine->alfredState.animState = ALFRED_IDLE;
+			g_engine->alfredState.setState(ALFRED_IDLE);
 			_curSprite->isTalking = true;
 			xPos = _curSprite->x + _curSprite->w / 2 - maxWidth / 2;
 			yPos = _curSprite->y - height; // Above sprite, adjust for line
@@ -225,7 +225,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 	if (_curSprite != nullptr) {
 		_curSprite->isTalking = false;
 	}
-	g_engine->alfredState.animState = ALFRED_IDLE;
+	g_engine->alfredState.setState(ALFRED_IDLE);
 }
 
 void DialogManager::displayDialogue(Common::String text, byte speakerId) {
@@ -597,8 +597,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 }
 
 void DialogManager::sayAlfred(Common::StringArray texts) {
-	g_engine->alfredState.nextState = ALFRED_TALKING;
-	g_engine->alfredState.curFrame = 0;
+	g_engine->alfredState.setState(ALFRED_TALKING);
 
 	_curSprite = nullptr;
 	Common::Array<Common::StringArray> textLines = wordWrap(texts);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index b710156c738..130f156749c 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -294,8 +294,7 @@ void PelrockEngine::checkMouse() {
 	// Cancel walking animation on mouse click
 	if (_events->_leftMouseButton) {
 		alfredState.curFrame = 0;
-		alfredState.idleFrameCounter = 0;
-		alfredState.animState = ALFRED_IDLE;
+		alfredState.setState(ALFRED_IDLE);
 	}
 
 	// Handle mouse release after long press (popup selection mode)
@@ -387,7 +386,7 @@ void PelrockEngine::paintDebugLayer() {
 		_smallFont->drawString(_screen, Common::String::format("%d", i), box.x + 2, box.y + 2, 640, 14);
 	}
 
-	for(int i = 0; i< _room->_currentRoomExits.size(); i++) {
+	for (int i = 0; i < _room->_currentRoomExits.size(); i++) {
 		Exit exit = _room->_currentRoomExits[i];
 		drawRect(_screen, exit.x, exit.y, exit.w, exit.h, 200 + i);
 		_smallFont->drawString(_screen, Common::String::format("Exit %d -> Room %d", i, exit.targetRoom), exit.x + 2, exit.y + 2, 640, 14);
@@ -500,8 +499,7 @@ void PelrockEngine::doAction(VerbIcon action, HotSpot *hotspot) {
 		talkTo(hotspot);
 		break;
 	case PICKUP:
-		alfredState.animState = ALFRED_INTERACTING;
-		alfredState.curFrame = 0;
+		alfredState.setState(ALFRED_INTERACTING);
 		pickUpAndDisable(hotspot);
 		executeAction(PICKUP, hotspot);
 		break;
@@ -533,8 +531,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		alfredState.animState == ALFRED_IDLE &&
 		(alfredState.direction == ALFRED_LEFT || alfredState.direction == ALFRED_RIGHT)) {
 		alfredState.idleFrameCounter = 0;
-		alfredState.curFrame = 0;
-		alfredState.animState = ALFRED_COMB;
+		alfredState.setState(ALFRED_COMB);
 	}
 	debug("Alfred state: %d, pos (%d, %d) curFrame %d", alfredState.animState, alfredState.x, alfredState.y, alfredState.curFrame);
 	switch (alfredState.animState) {
@@ -571,9 +568,8 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			_currentStep++;
 			if (_currentStep >= _currentContext.movementCount) {
 				_currentStep = 0;
-				alfredState.animState = ALFRED_IDLE;
-				alfredState.curFrame = 0;
-				if(_currentHotspot != nullptr)
+				alfredState.setState(ALFRED_IDLE);
+				if (_currentHotspot != nullptr)
 					alfredState.direction = calculateAlfredsDirection(_currentHotspot);
 
 				if (_queuedAction.isQueued) {
@@ -613,8 +609,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		break;
 	case ALFRED_COMB:
 		if (alfredState.curFrame >= 11) {
-			alfredState.animState = ALFRED_IDLE;
-			alfredState.curFrame = 0;
+			alfredState.setState(ALFRED_IDLE);
 			drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredIdle[alfredState.direction], alfredState.x, alfredState.y - kAlfredFrameHeight, 51, 102, 255);
 			break;
 		}
@@ -623,13 +618,15 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			alfredState.curFrame++;
 		break;
 	case ALFRED_INTERACTING:
-		if (alfredState.curFrame >= interactingAnimLength) {
-			alfredState.curFrame = 0;
-			alfredState.animState = ALFRED_IDLE;
+		if (alfredState.curFrame > interactingAnimLength) {
+			alfredState.setState(ALFRED_IDLE);
+			drawAlfred(_res->alfredInteractFrames[alfredState.direction][alfredState.curFrame]);
+			break;
 		}
 		drawAlfred(_res->alfredInteractFrames[alfredState.direction][alfredState.curFrame]);
-		if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0)
+		if (_chrono->getFrameCount() % 15 == 0) {
 			alfredState.curFrame++;
+		}
 		break;
 	default:
 		drawAlfred(_res->alfredIdle[alfredState.direction]);
@@ -1091,8 +1088,7 @@ void PelrockEngine::walkTo(int x, int y) {
 	PathContext context = {nullptr, nullptr, nullptr, 0, 0, 0};
 	findPath(alfredState.x, alfredState.y, x, y, _room->_currentRoomWalkboxes, &context);
 	_currentContext = context;
-	alfredState.animState = ALFRED_WALKING;
-	alfredState.curFrame = 0;
+	alfredState.setState(ALFRED_WALKING);
 }
 
 AlfredDirection PelrockEngine::calculateAlfredsDirection(HotSpot *hotspot) {
@@ -1262,7 +1258,7 @@ void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 	_sound->stopAllSounds();
 	_currentHotspot = nullptr;
 	alfredState.direction = dir;
-	alfredState.animState = ALFRED_IDLE;
+	alfredState.setState(ALFRED_IDLE);
 	_currentStep = 0;
 	int roomOffset = number * kRoomStructSize;
 	alfredState.curFrame = 0;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index e2266ec0ac6..4a6767ebc38 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -121,7 +121,6 @@ struct ActionPopupState {
 
 struct AlfredState {
 	AlfredAnimState animState = ALFRED_IDLE;
-	AlfredAnimState nextState = ALFRED_IDLE;
 	AlfredDirection direction = ALFRED_DOWN;
 	int curFrame = 0;
 	uint16 movementSpeed = 6; // pixels per frame
@@ -130,6 +129,11 @@ struct AlfredState {
 	uint16 scaledX = 0;
 	uint16 scaledY = 0;
 	int idleFrameCounter = 0;
+
+	void setState(AlfredAnimState nextState) {
+		animState = nextState;
+		curFrame = 0;
+	}
 };
 
 typedef struct {


Commit: ae72e1d66ee19901e76432b988536b0647947b0a
    https://github.com/scummvm/scummvm/commit/ae72e1d66ee19901e76432b988536b0647947b0a
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:35+02:00

Commit Message:
PELROCK: Fixes redundant frame when changing alfred state

Changed paths:
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 130f156749c..5e491352366 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -611,26 +611,26 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		if (alfredState.curFrame >= 11) {
 			alfredState.setState(ALFRED_IDLE);
 			drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredIdle[alfredState.direction], alfredState.x, alfredState.y - kAlfredFrameHeight, 51, 102, 255);
-			break;
+		} else {
+			drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCombFrames[alfredState.direction][alfredState.curFrame], alfredState.x, alfredState.y - kAlfredFrameHeight, 51, 102, 255);
+			if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0)
+				alfredState.curFrame++;
 		}
-		drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCombFrames[alfredState.direction][alfredState.curFrame], alfredState.x, alfredState.y - kAlfredFrameHeight, 51, 102, 255);
-		if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0)
-			alfredState.curFrame++;
 		break;
 	case ALFRED_INTERACTING:
-		if (alfredState.curFrame > interactingAnimLength) {
+		if (alfredState.curFrame >= interactingAnimLength) {
 			alfredState.setState(ALFRED_IDLE);
+		} else {
 			drawAlfred(_res->alfredInteractFrames[alfredState.direction][alfredState.curFrame]);
-			break;
-		}
-		drawAlfred(_res->alfredInteractFrames[alfredState.direction][alfredState.curFrame]);
-		if (_chrono->getFrameCount() % 15 == 0) {
-			alfredState.curFrame++;
+			if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0) {
+				alfredState.curFrame++;
+			}
 		}
 		break;
-	default:
+	}
+	// This if is needed to draw Alfred when idle, when the switch case results in a state change
+	if (alfredState.animState == ALFRED_IDLE) {
 		drawAlfred(_res->alfredIdle[alfredState.direction]);
-		break;
 	}
 }
 


Commit: b8398b86dc9f6745bd27e17fc2b29cdab0c95e27
    https://github.com/scummvm/scummvm/commit/b8398b86dc9f6745bd27e17fc2b29cdab0c95e27
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:35+02:00

Commit Message:
PELROCK: Implements menu buttons

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index da6d1cdc761..becf7ca81a3 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -60,6 +60,24 @@ void MenuManager::drawColoredText(Graphics::ManagedSurface *screen, const Common
 	}
 }
 
+MenuButton MenuManager::isButtonClicked(int x, int y) {
+	Common::Rect questionMarkRect(Common::Point(kQuestionMarkPosX, kQuestionMarkPosY), kQuestionMarkWidth, kQuestionMarkHeight);
+	if (questionMarkRect.contains(x, y)) {
+		return QUESTION_MARK_BUTTON;
+	}
+	Common::Rect invPrevRect(Common::Point(469, 87), kInventoryArrowWidth, kInventoryArrowHeight);
+	if (invPrevRect.contains(x, y)) {
+		return INVENTORY_PREV_BUTTON;
+	}
+	Common::Rect invNextRect(Common::Point(459, 140), kInventoryArrowWidth, kInventoryArrowHeight);
+	if (invNextRect.contains(x, y)) {
+		return INVENTORY_NEXT_BUTTON;
+	}
+
+	return NO_BUTTON; // Default fallback
+
+}
+
 void MenuManager::checkMouseClick(int x, int y) {
 
 	bool selectedItem = false;
@@ -77,26 +95,42 @@ void MenuManager::checkMouseClick(int x, int y) {
 		_menuText = _menuTexts[0];
 	}
 
-	if (x >= 471 && x <= 471 + 23 &&
-		y >= 87 && y <= 87 + 33) {
-		_curInventoryPage++;
+	MenuButton button = isButtonClicked(x, y);
+	switch (button) {
+	case QUESTION_MARK_BUTTON:
+		debug("Show credits");
+		break;
+	case INVENTORY_PREV_BUTTON:
+		if (_curInventoryPage > 0)
+			_curInventoryPage--;
+		break;
+	case INVENTORY_NEXT_BUTTON:
+		if ((_curInventoryPage + 1) * 4 < g_engine->_inventoryItems.size())
+			_curInventoryPage++;
+		break;
+	default:
+		break;
 	}
+
 }
 
+
+
 void MenuManager::menuLoop() {
 	_events->pollEvent();
 
 	if (_events->_leftMouseClicked) {
-		_events->_leftMouseClicked = false;
 		checkMouseClick(_events->_mouseX, _events->_mouseY);
+		_events->_leftMouseClicked = false;
 	} else if (_events->_rightMouseClicked) {
-		_events->_rightMouseClicked = false;
 		g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
 		g_engine->stateGame = GAME;
+		_events->_rightMouseClicked = false;
 		tearDown();
 	}
 
 	memcpy(_compositeBuffer, _mainMenu, 640 * 400);
+	drawButtons();
 
 	for (int i = 0; i < 4; i++) {
 		int itemIndex = _curInventoryPage * 4 + i;
@@ -111,6 +145,7 @@ void MenuManager::menuLoop() {
 		drawColoredText(_screen, _menuText[i], 230, 200 + (i * 10), 200, g_engine->_smallFont);
 	}
 
+	drawText(g_engine->_smallFont, Common::String::format("%d,%d", _events->_mouseX, _events->_mouseY), 0, 0, 640, 13);
 	_screen->markAllDirty();
 	_screen->update();
 }
@@ -167,7 +202,6 @@ void MenuManager::loadMenu() {
 		delete[] decompressedPart2;
 		alfred7.seek(2563266, SEEK_SET);
 		alfred7.read(_mainMenu + curPos, 92160);
-		alfred7.close();
 	} else {
 		Common::File alfred7;
 		if (!alfred7.open(Common::Path("ALFRED.7"))) {
@@ -186,8 +220,41 @@ void MenuManager::loadMenu() {
 		}
 
 		g_engine->_res->mergeRleBlocks(&alfred7, kAlternateSettingsMenuOffset, 8, _mainMenu);
-		alfred7.close();
 	}
+
+
+	// question mark
+	byte *questionMarks = new byte[kQuestionMarkStride * 2];
+
+	alfred7.seek(3214046, SEEK_SET);
+	alfred7.read(questionMarks, kQuestionMarkStride * 2);
+
+	_questionMark[0] = new byte[kQuestionMarkStride];
+	_questionMark[1] = new byte[kQuestionMarkStride];
+	extractSingleFrame(questionMarks, _questionMark[0], 0, kQuestionMarkWidth, kQuestionMarkHeight);
+	extractSingleFrame(questionMarks, _questionMark[1], 1, kQuestionMarkWidth, kQuestionMarkHeight);
+	delete[] questionMarks;
+
+	//Inventory arrows
+	byte *arrows = new byte[kInventoryArrowStride * 2];
+	alfred7.seek(kInvLeftArrowOffset, SEEK_SET);
+	alfred7.read(arrows, kInventoryArrowStride * 2);
+	_inventoryLeftArrow[0] = new byte[kInventoryArrowStride];
+	_inventoryLeftArrow[1] = new byte[kInventoryArrowStride];
+	extractSingleFrame(arrows, _inventoryLeftArrow[0], 0, kInventoryArrowWidth, kInventoryArrowHeight);
+	extractSingleFrame(arrows, _inventoryLeftArrow[1], 1, kInventoryArrowWidth, kInventoryArrowHeight);
+
+	alfred7.seek(kInvLeftArrowOffset + (kInventoryArrowStride * 2), SEEK_SET);
+	alfred7.read(arrows, kInventoryArrowStride * 2);
+	_inventoryRightArrow[0] = new byte[kInventoryArrowStride];
+	_inventoryRightArrow[1] = new byte[kInventoryArrowStride];
+	extractSingleFrame(arrows, _inventoryRightArrow[0], 0, kInventoryArrowWidth, kInventoryArrowHeight);
+	extractSingleFrame(arrows, _inventoryRightArrow[1], 1, kInventoryArrowWidth, kInventoryArrowHeight);
+
+	delete[] arrows;
+
+
+	alfred7.close();
 }
 
 void MenuManager::loadMenuTexts() {
@@ -217,8 +284,32 @@ void MenuManager::loadMenuTexts() {
 void MenuManager::tearDown() {
 }
 
+void MenuManager::drawButtons() {
+	MenuButton button = NO_BUTTON;
+	if(_events->_leftMouseButton != 0) {
+		button = isButtonClicked(_events->_mouseX, _events->_mouseY);
+	}
+	//Question mark
+	byte *buf = button == QUESTION_MARK_BUTTON ? _questionMark[1] : _questionMark[0];
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, kQuestionMarkPosX, kQuestionMarkPosY, kQuestionMarkWidth, kQuestionMarkHeight, 255);
+
+	buf = button == INVENTORY_PREV_BUTTON ? _inventoryLeftArrow[1] : _inventoryLeftArrow[0];
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, 469, 87, kInventoryArrowWidth, kInventoryArrowHeight, 255);
+
+	buf = button == INVENTORY_NEXT_BUTTON ? _inventoryRightArrow[1] : _inventoryRightArrow[0];
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, 463, 133, kInventoryArrowWidth, kInventoryArrowHeight, 255);
+
+}
+
 Pelrock::MenuManager::~MenuManager() {
 	delete[] _mainMenu;
+	delete[] _compositeBuffer;
+	delete[] _questionMark[0];
+	delete[] _questionMark[1];
+	delete[] _inventoryLeftArrow[0];
+	delete[] _inventoryLeftArrow[1];
+	delete[] _inventoryRightArrow[0];
+	delete[] _inventoryRightArrow[1];
 }
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index b9d3e87f463..edcd02ee101 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -29,6 +29,32 @@
 
 namespace Pelrock {
 
+const int kQuestionMarkOffset = 3214046;
+const int kInvLeftArrowOffset = 3215906;
+
+const int kQuestionMarkPosX = 217;
+const int kQuestionMarkPosY = 293;
+const int kQuestionMarkWidth = 31;
+const int kQuestionMarkHeight = 30;
+const int kQuestionMarkStride = 30 * 31;
+
+const int kInventoryArrowWidth = 26;
+const int kInventoryArrowHeight = 37;
+const int kInventoryArrowStride = kInventoryArrowWidth * kInventoryArrowHeight;
+
+enum MenuButton {
+	QUESTION_MARK_BUTTON,
+	INVENTORY_PREV_BUTTON,
+	INVENTORY_NEXT_BUTTON,
+	SAVEGAME_PREV_BUTTON,
+	SAVEGAME_NEXT_BUTTON,
+	EXIT_MENU_BUTTON,
+	SAVE_GAME_BUTTON,
+	LOAD_GAME_BUTTON,
+	SOUNDS_BUTTON,
+	NO_BUTTON
+};
+
 class MenuManager {
 public:
 	MenuManager(Graphics::Screen *screen, PelrockEventManager *events, ResourceManager *res);
@@ -41,12 +67,18 @@ private:
 	void checkMouseClick(int x, int y);
 	void loadMenuTexts();
 	void tearDown();
+	void drawButtons();
 	void drawColoredText(Graphics::ManagedSurface *surface, const Common::String &text, int x, int y, int w, Graphics::Font *font);
+	MenuButton isButtonClicked(int x, int y);
 	Graphics::Screen *_screen = nullptr;
 	PelrockEventManager *_events = nullptr;
 	ResourceManager *_res = nullptr;
 	byte *_mainMenu = nullptr;
 	byte *_compositeBuffer = nullptr;
+	byte *_inventoryLeftArrow[2] = { nullptr };
+	byte *_inventoryRightArrow[2] = { nullptr };
+	byte *_savesArrows[2] = { nullptr };
+	byte *_questionMark[2] = {nullptr};
 	Common::Array<Common::StringArray> _menuTexts;
 	// Temporary
 	int _selectedInvIndex = 0;


Commit: 0a5f0560a5f6c0c72a9853bb0a97b10226c00c57
    https://github.com/scummvm/scummvm/commit/0a5f0560a5f6c0c72a9853bb0a97b10226c00c57
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:35+02:00

Commit Message:
PELROCK: Refactor button resource reading

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index becf7ca81a3..6d669408866 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -61,19 +61,27 @@ void MenuManager::drawColoredText(Graphics::ManagedSurface *screen, const Common
 }
 
 MenuButton MenuManager::isButtonClicked(int x, int y) {
-	Common::Rect questionMarkRect(Common::Point(kQuestionMarkPosX, kQuestionMarkPosY), kQuestionMarkWidth, kQuestionMarkHeight);
-	if (questionMarkRect.contains(x, y)) {
+	if (_questionMarkRect.contains(x, y)) {
 		return QUESTION_MARK_BUTTON;
 	}
-	Common::Rect invPrevRect(Common::Point(469, 87), kInventoryArrowWidth, kInventoryArrowHeight);
-	if (invPrevRect.contains(x, y)) {
+	if (_invLeft.contains(x, y)) {
 		return INVENTORY_PREV_BUTTON;
 	}
-	Common::Rect invNextRect(Common::Point(459, 140), kInventoryArrowWidth, kInventoryArrowHeight);
-	if (invNextRect.contains(x, y)) {
+	if (_invRight.contains(x, y)) {
 		return INVENTORY_NEXT_BUTTON;
 	}
-
+	if( _saveGameRect.contains(x, y)) {
+		return SAVE_GAME_BUTTON;
+	}
+	if( _loadGameRect.contains(x, y)) {
+		return LOAD_GAME_BUTTON;
+	}
+	if( _soundsRect.contains(x, y)) {
+		return SOUNDS_BUTTON;
+	}
+	if( _exitToDosRect.contains(x, y)) {
+		return EXIT_MENU_BUTTON;
+	}
 	return NO_BUTTON; // Default fallback
 
 }
@@ -222,41 +230,30 @@ void MenuManager::loadMenu() {
 		g_engine->_res->mergeRleBlocks(&alfred7, kAlternateSettingsMenuOffset, 8, _mainMenu);
 	}
 
+	readButton(alfred7, 3193376, _saveButtons, _saveGameRect);
+	readButton(alfred7, alfred7.pos(), _loadButtons, _loadGameRect);
+	readButton(alfred7, alfred7.pos(), _soundsButtons, _soundsRect);
+	readButton(alfred7, kInvLeftArrowOffset, _inventoryLeftArrow, _invLeft);
+	readButton(alfred7, alfred7.pos(), _inventoryRightArrow, _invRight);
+	readButton(alfred7, 3214046, _questionMark, _questionMarkRect);
 
-	// question mark
-	byte *questionMarks = new byte[kQuestionMarkStride * 2];
-
-	alfred7.seek(3214046, SEEK_SET);
-	alfred7.read(questionMarks, kQuestionMarkStride * 2);
-
-	_questionMark[0] = new byte[kQuestionMarkStride];
-	_questionMark[1] = new byte[kQuestionMarkStride];
-	extractSingleFrame(questionMarks, _questionMark[0], 0, kQuestionMarkWidth, kQuestionMarkHeight);
-	extractSingleFrame(questionMarks, _questionMark[1], 1, kQuestionMarkWidth, kQuestionMarkHeight);
-	delete[] questionMarks;
-
-	//Inventory arrows
-	byte *arrows = new byte[kInventoryArrowStride * 2];
-	alfred7.seek(kInvLeftArrowOffset, SEEK_SET);
-	alfred7.read(arrows, kInventoryArrowStride * 2);
-	_inventoryLeftArrow[0] = new byte[kInventoryArrowStride];
-	_inventoryLeftArrow[1] = new byte[kInventoryArrowStride];
-	extractSingleFrame(arrows, _inventoryLeftArrow[0], 0, kInventoryArrowWidth, kInventoryArrowHeight);
-	extractSingleFrame(arrows, _inventoryLeftArrow[1], 1, kInventoryArrowWidth, kInventoryArrowHeight);
-
-	alfred7.seek(kInvLeftArrowOffset + (kInventoryArrowStride * 2), SEEK_SET);
-	alfred7.read(arrows, kInventoryArrowStride * 2);
-	_inventoryRightArrow[0] = new byte[kInventoryArrowStride];
-	_inventoryRightArrow[1] = new byte[kInventoryArrowStride];
-	extractSingleFrame(arrows, _inventoryRightArrow[0], 0, kInventoryArrowWidth, kInventoryArrowHeight);
-	extractSingleFrame(arrows, _inventoryRightArrow[1], 1, kInventoryArrowWidth, kInventoryArrowHeight);
+	alfred7.close();
+}
 
-	delete[] arrows;
 
 
-	alfred7.close();
+void MenuManager::readButton(Common::File &alfred7, uint32 offset, byte *outBuffer[2], Common::Rect rect) {
+	alfred7.seek(offset, SEEK_SET);
+	byte *buttonData = new byte[rect.width() * rect.height() * 2];
+	alfred7.read(buttonData, rect.width() * rect.height() * 2);
+	outBuffer[0] = new byte[rect.width() * rect.height()];
+	outBuffer[1] = new byte[rect.width() * rect.height()];
+	extractSingleFrame(buttonData, outBuffer[0], 0, rect.width(), rect.height());
+	extractSingleFrame(buttonData, outBuffer[1], 1	, rect.width(), rect.height());
+	delete[] buttonData;
 }
 
+
 void MenuManager::loadMenuTexts() {
 
 	Common::File exe;
@@ -289,16 +286,26 @@ void MenuManager::drawButtons() {
 	if(_events->_leftMouseButton != 0) {
 		button = isButtonClicked(_events->_mouseX, _events->_mouseY);
 	}
-	//Question mark
 	byte *buf = button == QUESTION_MARK_BUTTON ? _questionMark[1] : _questionMark[0];
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, kQuestionMarkPosX, kQuestionMarkPosY, kQuestionMarkWidth, kQuestionMarkHeight, 255);
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, _questionMarkRect.left, _questionMarkRect.top, _questionMarkRect.width(), _questionMarkRect.height(), 255);
 
 	buf = button == INVENTORY_PREV_BUTTON ? _inventoryLeftArrow[1] : _inventoryLeftArrow[0];
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, 469, 87, kInventoryArrowWidth, kInventoryArrowHeight, 255);
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, _invLeft.left, _invLeft.top, _invLeft.width(), _invLeft.height(), 255);
 
 	buf = button == INVENTORY_NEXT_BUTTON ? _inventoryRightArrow[1] : _inventoryRightArrow[0];
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, 463, 133, kInventoryArrowWidth, kInventoryArrowHeight, 255);
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, _invRight.left, _invRight.top, _invRight.width(), _invRight.height(), 255);
+
+	buf = button == SAVE_GAME_BUTTON ? _saveButtons[1] : _saveButtons[0];
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, _saveGameRect.left, _saveGameRect.top, _saveGameRect.width(), _saveGameRect.height(), 255);
+
+	buf = button == LOAD_GAME_BUTTON ? _loadButtons[1] : _loadButtons[0];
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, _loadGameRect.left, _loadGameRect.top, _loadGameRect.width(), _loadGameRect.height(), 255);
+
+	buf = button == LOAD_GAME_BUTTON ? _loadButtons[1] : _loadButtons[0];
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, _loadGameRect.left, _loadGameRect.top, _loadGameRect.width(), _loadGameRect.height(), 255);
 
+	buf = button == SOUNDS_BUTTON ? _soundsButtons[1] : _soundsButtons[0];
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, _soundsRect.left, _soundsRect.top, _soundsRect.width(), _soundsRect.height(), 255);
 }
 
 Pelrock::MenuManager::~MenuManager() {
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index edcd02ee101..099b8897018 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -32,16 +32,6 @@ namespace Pelrock {
 const int kQuestionMarkOffset = 3214046;
 const int kInvLeftArrowOffset = 3215906;
 
-const int kQuestionMarkPosX = 217;
-const int kQuestionMarkPosY = 293;
-const int kQuestionMarkWidth = 31;
-const int kQuestionMarkHeight = 30;
-const int kQuestionMarkStride = 30 * 31;
-
-const int kInventoryArrowWidth = 26;
-const int kInventoryArrowHeight = 37;
-const int kInventoryArrowStride = kInventoryArrowWidth * kInventoryArrowHeight;
-
 enum MenuButton {
 	QUESTION_MARK_BUTTON,
 	INVENTORY_PREV_BUTTON,
@@ -69,16 +59,42 @@ private:
 	void tearDown();
 	void drawButtons();
 	void drawColoredText(Graphics::ManagedSurface *surface, const Common::String &text, int x, int y, int w, Graphics::Font *font);
+	void readButton(Common::File &alfred7, uint32 offset, byte *outBuffer[2], Common::Rect rect);
 	MenuButton isButtonClicked(int x, int y);
 	Graphics::Screen *_screen = nullptr;
 	PelrockEventManager *_events = nullptr;
 	ResourceManager *_res = nullptr;
 	byte *_mainMenu = nullptr;
 	byte *_compositeBuffer = nullptr;
+
+	Common::Rect _saveGameRect = Common::Rect(Common::Point(130, 184), 81, 34);
+	byte *_saveButtons[2] = { nullptr };
+
+	Common::Rect _loadGameRect = Common::Rect(Common::Point(130, 221), 80, 31);
+	byte *_loadButtons[2] = { nullptr };
+
+	Common::Rect _soundsRect = Common::Rect(Common::Point(137, 260), 77, 30);
+	byte *_soundsButtons[2] = { nullptr };
+
+	Common::Rect _exitToDosRect = Common::Rect(Common::Point(220, 270), 81, 30);
+	byte *_exitToDosButtons[2] = { nullptr };
+
+
+	Common::Rect _invLeft = Common::Rect(Common::Point(469, 87), 26, 37);
 	byte *_inventoryLeftArrow[2] = { nullptr };
+
+	Common::Rect _invRight = Common::Rect(Common::Point(463, 130), 26, 37);
 	byte *_inventoryRightArrow[2] = { nullptr };
-	byte *_savesArrows[2] = { nullptr };
+
+	Common::Rect _savesUp = Common::Rect(Common::Point(320, 150), 16, 16);
+	byte *_savesUpArrows[2] = { nullptr };
+
+	Common::Rect _savesDown = Common::Rect(Common::Point(385, 150), 16, 16);
+	byte *_savesDownArrows[2] = { nullptr };
+
+	Common::Rect _questionMarkRect = Common::Rect(Common::Point(217, 293), 31, 30);
 	byte *_questionMark[2] = {nullptr};
+
 	Common::Array<Common::StringArray> _menuTexts;
 	// Temporary
 	int _selectedInvIndex = 0;


Commit: f5a3133cb41bd1320453a1dfbf77176506b47879
    https://github.com/scummvm/scummvm/commit/f5a3133cb41bd1320453a1dfbf77176506b47879
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:36+02:00

Commit Message:
PELROCK: Properly place all menu buttons

Changed paths:
    engines/pelrock/events.cpp
    engines/pelrock/events.h
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/events.cpp b/engines/pelrock/events.cpp
index 766dfccc1f8..37f46927342 100644
--- a/engines/pelrock/events.cpp
+++ b/engines/pelrock/events.cpp
@@ -48,10 +48,11 @@ void PelrockEventManager::pollEvent() {
 
 		// case Common::EVENT_CUSTOM_ENGINE_ACTION_END:
 		// 	break;
-		// case Common::EVENT_KEYDOWN:
+		case Common::EVENT_KEYDOWN:
 		// 	changeGameSpeed(_event);
-		// 	_keyPressed = true;
-		// 	_lastKeyEvent = _event;
+			// _keyPressed = true;
+			_lastKeyEvent = _event.kbd.keycode;
+			break;
 		// 	return;
 		// case Common::EVENT_KEYUP:
 		// 	return;
diff --git a/engines/pelrock/events.h b/engines/pelrock/events.h
index f39ab56cfb5..defe1392f9c 100644
--- a/engines/pelrock/events.h
+++ b/engines/pelrock/events.h
@@ -42,7 +42,7 @@ public:
 	bool _popupSelectionMode = false;
 	bool _leftMouseButton = 0;
 	bool _rightMouseButton = 0;
-	Common::Event _lastKeyEvent;
+	Common::KeyCode _lastKeyEvent = Common::KEYCODE_INVALID;
 	PelrockEventManager();
 	void pollEvent();
 	void waitForKey();
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 6d669408866..a04c840df59 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -70,20 +70,25 @@ MenuButton MenuManager::isButtonClicked(int x, int y) {
 	if (_invRight.contains(x, y)) {
 		return INVENTORY_NEXT_BUTTON;
 	}
-	if( _saveGameRect.contains(x, y)) {
+	if (_saveGameRect.contains(x, y)) {
 		return SAVE_GAME_BUTTON;
 	}
-	if( _loadGameRect.contains(x, y)) {
+	if (_loadGameRect.contains(x, y)) {
 		return LOAD_GAME_BUTTON;
 	}
-	if( _soundsRect.contains(x, y)) {
+	if (_soundsRect.contains(x, y)) {
 		return SOUNDS_BUTTON;
 	}
-	if( _exitToDosRect.contains(x, y)) {
+	if (_exitToDosRect.contains(x, y)) {
 		return EXIT_MENU_BUTTON;
 	}
+	if (_savesUp.contains(x, y)) {
+		return SAVEGAME_PREV_BUTTON;
+	}
+	if (_savesDown.contains(x, y)) {
+		return SAVEGAME_NEXT_BUTTON;
+	}
 	return NO_BUTTON; // Default fallback
-
 }
 
 void MenuManager::checkMouseClick(int x, int y) {
@@ -119,11 +124,8 @@ void MenuManager::checkMouseClick(int x, int y) {
 	default:
 		break;
 	}
-
 }
 
-
-
 void MenuManager::menuLoop() {
 	_events->pollEvent();
 
@@ -135,10 +137,20 @@ void MenuManager::menuLoop() {
 		g_engine->stateGame = GAME;
 		_events->_rightMouseClicked = false;
 		tearDown();
+	} else {
+		if (_events->_lastKeyEvent == Common::KEYCODE_b) {
+			// g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
+			// g_engine->stateGame = GAME;
+			showButtons = !showButtons;
+			_events->_lastKeyEvent = Common::KEYCODE_INVALID;
+			// tearDown();
+		}
 	}
 
 	memcpy(_compositeBuffer, _mainMenu, 640 * 400);
-	drawButtons();
+	// memset(_compositeBuffer, 0, 640 * 400);
+	if (showButtons)
+		drawButtons();
 
 	for (int i = 0; i < 4; i++) {
 		int itemIndex = _curInventoryPage * 4 + i;
@@ -233,15 +245,16 @@ void MenuManager::loadMenu() {
 	readButton(alfred7, 3193376, _saveButtons, _saveGameRect);
 	readButton(alfred7, alfred7.pos(), _loadButtons, _loadGameRect);
 	readButton(alfred7, alfred7.pos(), _soundsButtons, _soundsRect);
+	readButton(alfred7, alfred7.pos(), _exitToDosButtons, _exitToDosRect);
 	readButton(alfred7, kInvLeftArrowOffset, _inventoryLeftArrow, _invLeft);
 	readButton(alfred7, alfred7.pos(), _inventoryRightArrow, _invRight);
+	readButton(alfred7, alfred7.pos(), _savesUpArrows, _savesUp);
+	readButton(alfred7, alfred7.pos(), _savesDownArrows, _savesDown);
 	readButton(alfred7, 3214046, _questionMark, _questionMarkRect);
 
 	alfred7.close();
 }
 
-
-
 void MenuManager::readButton(Common::File &alfred7, uint32 offset, byte *outBuffer[2], Common::Rect rect) {
 	alfred7.seek(offset, SEEK_SET);
 	byte *buttonData = new byte[rect.width() * rect.height() * 2];
@@ -249,11 +262,10 @@ void MenuManager::readButton(Common::File &alfred7, uint32 offset, byte *outBuff
 	outBuffer[0] = new byte[rect.width() * rect.height()];
 	outBuffer[1] = new byte[rect.width() * rect.height()];
 	extractSingleFrame(buttonData, outBuffer[0], 0, rect.width(), rect.height());
-	extractSingleFrame(buttonData, outBuffer[1], 1	, rect.width(), rect.height());
+	extractSingleFrame(buttonData, outBuffer[1], 1, rect.width(), rect.height());
 	delete[] buttonData;
 }
 
-
 void MenuManager::loadMenuTexts() {
 
 	Common::File exe;
@@ -283,29 +295,38 @@ void MenuManager::tearDown() {
 
 void MenuManager::drawButtons() {
 	MenuButton button = NO_BUTTON;
-	if(_events->_leftMouseButton != 0) {
+	if (_events->_leftMouseButton != 0) {
 		button = isButtonClicked(_events->_mouseX, _events->_mouseY);
 	}
 	byte *buf = button == QUESTION_MARK_BUTTON ? _questionMark[1] : _questionMark[0];
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, _questionMarkRect.left, _questionMarkRect.top, _questionMarkRect.width(), _questionMarkRect.height(), 255);
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, _questionMarkRect.left, _questionMarkRect.top, _questionMarkRect.width(), _questionMarkRect.height(), kTransparentColor);
 
 	buf = button == INVENTORY_PREV_BUTTON ? _inventoryLeftArrow[1] : _inventoryLeftArrow[0];
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, _invLeft.left, _invLeft.top, _invLeft.width(), _invLeft.height(), 255);
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, _invLeft.left, _invLeft.top, _invLeft.width(), _invLeft.height(), kTransparentColor);
 
 	buf = button == INVENTORY_NEXT_BUTTON ? _inventoryRightArrow[1] : _inventoryRightArrow[0];
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, _invRight.left, _invRight.top, _invRight.width(), _invRight.height(), 255);
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, _invRight.left, _invRight.top, _invRight.width(), _invRight.height(), kTransparentColor);
 
 	buf = button == SAVE_GAME_BUTTON ? _saveButtons[1] : _saveButtons[0];
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, _saveGameRect.left, _saveGameRect.top, _saveGameRect.width(), _saveGameRect.height(), 255);
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, _saveGameRect.left, _saveGameRect.top, _saveGameRect.width(), _saveGameRect.height(), kTransparentColor);
 
 	buf = button == LOAD_GAME_BUTTON ? _loadButtons[1] : _loadButtons[0];
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, _loadGameRect.left, _loadGameRect.top, _loadGameRect.width(), _loadGameRect.height(), 255);
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, _loadGameRect.left, _loadGameRect.top, _loadGameRect.width(), _loadGameRect.height(), kTransparentColor);
 
 	buf = button == LOAD_GAME_BUTTON ? _loadButtons[1] : _loadButtons[0];
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, _loadGameRect.left, _loadGameRect.top, _loadGameRect.width(), _loadGameRect.height(), 255);
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, _loadGameRect.left, _loadGameRect.top, _loadGameRect.width(), _loadGameRect.height(), kTransparentColor);
 
 	buf = button == SOUNDS_BUTTON ? _soundsButtons[1] : _soundsButtons[0];
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, _soundsRect.left, _soundsRect.top, _soundsRect.width(), _soundsRect.height(), 255);
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, _soundsRect.left, _soundsRect.top, _soundsRect.width(), _soundsRect.height(), kTransparentColor);
+
+	buf = button == EXIT_MENU_BUTTON ? _exitToDosButtons[1] : _exitToDosButtons[0];
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, _exitToDosRect.left, _exitToDosRect.top, _exitToDosRect.width(), _exitToDosRect.height(), kTransparentColor);
+
+	buf = button == SAVEGAME_PREV_BUTTON ? _savesUpArrows[1] : _savesUpArrows[0];
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, _savesUp.left, _savesUp.top, _savesUp.width(), _savesUp.height(), kTransparentColor);
+
+	buf = button == SAVEGAME_NEXT_BUTTON ? _savesDownArrows[1] : _savesDownArrows[0];
+	drawSpriteToBuffer(_compositeBuffer, 640, buf, _savesDown.left, _savesDown.top, _savesDown.width(), _savesDown.height(), kTransparentColor);
 }
 
 Pelrock::MenuManager::~MenuManager() {
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 099b8897018..9d1fae248dd 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -31,6 +31,7 @@ namespace Pelrock {
 
 const int kQuestionMarkOffset = 3214046;
 const int kInvLeftArrowOffset = 3215906;
+const int kTransparentColor = 15;
 
 enum MenuButton {
 	QUESTION_MARK_BUTTON,
@@ -67,30 +68,29 @@ private:
 	byte *_mainMenu = nullptr;
 	byte *_compositeBuffer = nullptr;
 
-	Common::Rect _saveGameRect = Common::Rect(Common::Point(130, 184), 81, 34);
-	byte *_saveButtons[2] = { nullptr };
+	Common::Rect _saveGameRect = Common::Rect(Common::Point(132, 186), 81, 34);
+	byte *_saveButtons[2] = {nullptr};
 
-	Common::Rect _loadGameRect = Common::Rect(Common::Point(130, 221), 80, 31);
-	byte *_loadButtons[2] = { nullptr };
+	Common::Rect _loadGameRect = Common::Rect(Common::Point(133, 222), 80, 33);
+	byte *_loadButtons[2] = {nullptr};
 
-	Common::Rect _soundsRect = Common::Rect(Common::Point(137, 260), 77, 30);
-	byte *_soundsButtons[2] = { nullptr };
+	Common::Rect _soundsRect = Common::Rect(Common::Point(134, 258), 77, 33);
+	byte *_soundsButtons[2] = {nullptr};
 
-	Common::Rect _exitToDosRect = Common::Rect(Common::Point(220, 270), 81, 30);
-	byte *_exitToDosButtons[2] = { nullptr };
+	Common::Rect _exitToDosRect = Common::Rect(Common::Point(134, 293), 75, 30);
+	byte *_exitToDosButtons[2] = {nullptr};
 
+	Common::Rect _invLeft = Common::Rect(Common::Point(469, 88), 26, 37);
+	byte *_inventoryLeftArrow[2] = {nullptr};
 
-	Common::Rect _invLeft = Common::Rect(Common::Point(469, 87), 26, 37);
-	byte *_inventoryLeftArrow[2] = { nullptr };
+	Common::Rect _invRight = Common::Rect(Common::Point(463, 132), 26, 37);
+	byte *_inventoryRightArrow[2] = {nullptr};
 
-	Common::Rect _invRight = Common::Rect(Common::Point(463, 130), 26, 37);
-	byte *_inventoryRightArrow[2] = { nullptr };
+	Common::Rect _savesUp = Common::Rect(Common::Point(457, 189), 26, 24);
+	byte *_savesUpArrows[2] = {nullptr};
 
-	Common::Rect _savesUp = Common::Rect(Common::Point(320, 150), 16, 16);
-	byte *_savesUpArrows[2] = { nullptr };
-
-	Common::Rect _savesDown = Common::Rect(Common::Point(385, 150), 16, 16);
-	byte *_savesDownArrows[2] = { nullptr };
+	Common::Rect _savesDown = Common::Rect(Common::Point(450, 278), 26, 24);
+	byte *_savesDownArrows[2] = {nullptr};
 
 	Common::Rect _questionMarkRect = Common::Rect(Common::Point(217, 293), 31, 30);
 	byte *_questionMark[2] = {nullptr};
@@ -101,6 +101,8 @@ private:
 	int _curInventoryPage = 0;
 	Common::StringArray _menuText;
 	Common::Array<Common::StringArray> _inventoryDescriptions;
+
+	bool showButtons = true;
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 5e491352366..563789e4f89 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -105,6 +105,7 @@ Common::Error PelrockEngine::run() {
 
 	if (shouldPlayIntro == false) {
 		stateGame = GAME;
+		// stateGame = SETTINGS;
 	} else {
 		stateGame = INTRO;
 		_videoManager->playIntro();


Commit: ce313b205643b446101e0da3362c2a59ae2e2ac7
    https://github.com/scummvm/scummvm/commit/ce313b205643b446101e0da3362c2a59ae2e2ac7
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:36+02:00

Commit Message:
PELROCK: Plays entire intro video

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/video/video.cpp
    engines/pelrock/video/video.h


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index a04c840df59..6d1aa8f7b3e 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -99,6 +99,7 @@ void MenuManager::checkMouseClick(int x, int y) {
 			y >= 115 - (8 * i) && y <= 115 - (8 * i) + 64) {
 			_selectedInvIndex = g_engine->_inventoryItems[_curInventoryPage * 4 + i];
 			_menuText = _inventoryDescriptions[_selectedInvIndex];
+			g_engine->_selectedInventoryItem = _selectedInvIndex;
 			selectedItem = true;
 			return;
 		}
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 563789e4f89..ec35dfc84d2 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -54,8 +54,10 @@ PelrockEngine::PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc) :
 }
 
 PelrockEngine::~PelrockEngine() {
-	delete[] _compositeBuffer;
-	delete[] _currentBackground;
+	if(_compositeBuffer)
+		delete[] _compositeBuffer;
+	if(_currentBackground)
+		delete[] _currentBackground;
 	delete _largeFont;
 	delete _smallFont;
 	delete _doubleSmallFont;
@@ -85,13 +87,13 @@ Common::Error PelrockEngine::run() {
 	// Initialize 320x200 paletted graphics mode
 	initGraphics(640, 400);
 	_screen = new Graphics::Screen();
-	_videoManager = new VideoManager(_screen, _events);
 	_graphics = new GraphicsManager();
 	_room = new RoomManager();
 	_res = new ResourceManager();
 	_sound = new SoundManager(_mixer);
 	_dialog = new DialogManager(_screen, _events, _graphics);
 	_menu = new MenuManager(_screen, _events, _res);
+	_videoManager = new VideoManager(_screen, _events, _chrono);
 	// Set the engine's debugger console
 	setDebugger(new PelrockConsole(this));
 
@@ -111,7 +113,8 @@ Common::Error PelrockEngine::run() {
 		_videoManager->playIntro();
 		stateGame = GAME;
 	}
-	init();
+	if(!shouldQuit())
+		init();
 
 	while (!shouldQuit()) {
 
@@ -534,7 +537,6 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		alfredState.idleFrameCounter = 0;
 		alfredState.setState(ALFRED_COMB);
 	}
-	debug("Alfred state: %d, pos (%d, %d) curFrame %d", alfredState.animState, alfredState.x, alfredState.y, alfredState.curFrame);
 	switch (alfredState.animState) {
 	case ALFRED_WALKING: {
 		MovementStep step = _currentContext.movementBuffer[_currentStep];
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 77ecb0cb53f..63c46b11290 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -141,8 +141,7 @@ private:
 
 	bool showShadows = false;
 
-	// JAVA
-	bool shouldPlayIntro = false;
+	bool shouldPlayIntro = true;
 	bool gameInitialized = false;
 	bool screenReady = false;
 
@@ -162,7 +161,7 @@ public:
 	ResourceManager *_res = nullptr;
 	RoomManager *_room = nullptr;
 	AlfredState alfredState;
-	byte *_compositeBuffer; // Working composition buffer
+	byte *_compositeBuffer = nullptr; // Working composition buffer
 	GameState stateGame = INTRO;
 
 	SmallFont *_smallFont = nullptr;
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index 03b78936d1b..3bb59706f49 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -21,6 +21,7 @@
 
 #include "common/file.h"
 #include "graphics/screen.h"
+#include "graphics/paletteman.h"
 
 #include "pelrock/chrono.h"
 #include "pelrock/pelrock.h"
@@ -29,7 +30,7 @@
 
 namespace Pelrock {
 
-VideoManager::VideoManager(Graphics::Screen *screen, PelrockEventManager *events) : _screen(screen), _events(events) {
+VideoManager::VideoManager(Graphics::Screen *screen, PelrockEventManager *events, ChronoManager *chrono) : _screen(screen), _events(events), _chrono(chrono) {
 }
 
 VideoManager::~VideoManager() {
@@ -42,63 +43,46 @@ void VideoManager::playIntro() {
 		return;
 	}
 	loadPalette(videoFile);
-	videoFile.seek(frame0offset, SEEK_SET);
-	size_t firstChunkSize = chunkSize * 13;
-	byte *chunk0 = new byte[firstChunkSize];
-	videoFile.read(chunk0, firstChunkSize);
-	byte *background = decodeCopyBlock(chunk0, 0);
-
-	for (int i = 0; i < 640; i++) {
-		for (int j = 0; j < 400; j++) {
-			_screen->setPixel(i, j, background[j * 640 + i]);
-		}
-	}
-	_screen->markAllDirty();
-	_screen->update();
-	delete[] chunk0;
-	_events->waitForKey();
-
-	size_t chunk1Size = chunkSize * 6;
-	byte chunk1_data[chunk1Size];
-	videoFile.seek(frame1offset, SEEK_SET);
-	videoFile.read(chunk1_data, chunk1Size);
-
-	byte *delta1 = decodeRLE(chunk1_data, chunk1Size, 0x0D);
-	for (int j = 0; j < 256000; j++) {
-		background[j] ^= delta1[j];
-	}
-	delete[] delta1;
-	for (int x = 0; x < 640; x++) {
-		for (int y = 0; y < 400; y++) {
-			_screen->setPixel(x, y, background[y * 640 + x]);
-		}
-	}
-	_screen->markAllDirty();
-	_screen->update();
-	_events->waitForKey();
-
-	for (size_t i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) {
-		byte *chunk = new byte[chunkSize];
-		videoFile.seek(offsets[i], SEEK_SET);
-		videoFile.read(chunk, chunkSize);
-		byte *delta = decodeCopyBlock(chunk, 0);
-		for (int j = 0; j < 256000; j++) {
-			background[j] ^= delta[j];
-		}
-		delete[] delta;
-		for (int x = 0; x < 640; x++) {
-			for (int y = 0; y < 400; y++) {
-				_screen->setPixel(x, y, background[y * 640 + x]);
+	videoFile.seek(0, SEEK_SET);
+
+	memset(_currentKeyFrame, 0, 256000);
+	for (int sequence = 0; sequence < 1; sequence++) {
+		int frameCounter = 0;
+		int chunksInBuffer = 0;
+		bool videoExitFlag = false;
+
+		while (!videoExitFlag && !g_engine->shouldQuit()) {
+			_chrono->updateChrono();
+			_events->pollEvent();
+
+			if(_chrono->_gameTick) {
+				ChunkHeader chunk;
+				readChunk(videoFile, chunk);
+
+				switch (chunk.chunkType) {
+				case 1:
+				case 2:
+					processFrame(chunk, frameCounter++);
+					break;
+				case 3:
+					videoExitFlag = true;
+					break;
+				case 4:
+					loadPalette(chunk);
+					break;
+				default:
+					debug("Unknown chunk type %d encountered", chunk.chunkType);
+					break;
+				}
+				presentFrame();
 			}
+			g_system->delayMillis(10);
 		}
-		delete[] chunk;
-		_screen->markAllDirty();
-		_screen->update();
-		_events->waitForKey();
 	}
-	_events->waitForKey();
+
 	videoFile.close();
 }
+
 void VideoManager::loadPalette(Common::SeekableReadStream &stream) {
 
 	byte paletteData[768];
@@ -113,11 +97,21 @@ void VideoManager::loadPalette(Common::SeekableReadStream &stream) {
 	_screen->setPalette(palette);
 }
 
+void VideoManager::loadPalette(ChunkHeader &chunk) {
+	byte palette[768];
+	for (int i = 0; i < 256; i++) {
+		palette[i * 3 + 0] = chunk.data[i * 3 + 0] << 2;
+		palette[i * 3 + 1] = chunk.data[i * 3 + 1] << 2;
+		palette[i * 3 + 2] = chunk.data[i * 3 + 2] << 2;
+	}
+	g_system->getPaletteManager()->setPalette(palette, 0, 256);
+}
+
 byte *VideoManager::decodeCopyBlock(byte *data, uint32 offset) {
 
 	byte *buf = new byte[256000];
 	memset(buf, 0, 256000);
-	uint32 pos = offset + 0x0D;
+	uint32 pos = offset + 0x04;
 	// frames are encoded so that each block copy has a 5-byte header
 	// the first 3 bytes are the offset within the screen to which to
 	// copy the bytes. The 5th byte is the length of the block to copy.
@@ -139,7 +133,7 @@ byte *VideoManager::decodeCopyBlock(byte *data, uint32 offset) {
 		pos += length;
 	}
 
-	return buf; // Placeholder
+	return buf;
 }
 
 byte *VideoManager::decodeRLE(byte *data, size_t size, uint32 offset) {
@@ -169,4 +163,42 @@ byte *VideoManager::decodeRLE(byte *data, size_t size, uint32 offset) {
 	}
 	return buf;
 }
+
+void VideoManager::readChunk(Common::SeekableReadStream &stream, ChunkHeader &chunk) {
+	chunk.blockCount = stream.readUint32LE();
+	chunk.dataOffset = stream.readUint32LE();
+	chunk.chunkType = stream.readByte();
+
+	chunk.data = new byte[chunk.blockCount * chunkSize + 9];
+	stream.read(chunk.data, chunk.blockCount * chunkSize - 9);
+}
+
+void VideoManager::processFrame(ChunkHeader &chunk, const int frameCount) {
+	byte *frameData = nullptr;
+	if(chunk.chunkType == 1) {
+		// Video data chunk
+		frameData = decodeRLE(chunk.data, chunk.blockCount * chunkSize, 0x04);
+	} else if (chunk.chunkType == 2) {
+		// Block copy chunk
+		frameData = decodeCopyBlock(chunk.data, 0);
+	}
+
+	if(frameCount == 0) {
+		memcpy(_currentKeyFrame, frameData, 256000);
+	} else {
+		// Subsequent frames, XOR with previous frame
+		for (int i = 0; i < 256000; i++) {
+			_currentKeyFrame[i] ^= frameData[i];
+		}
+	}
+	delete[] frameData;
+
+}
+
+void VideoManager::presentFrame() {
+	memcpy(_screen->getPixels(), _currentKeyFrame, 640 * 400);
+	_screen->markAllDirty();
+	_screen->update();
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/video/video.h b/engines/pelrock/video/video.h
index 405c52b06b2..82354cbb833 100644
--- a/engines/pelrock/video/video.h
+++ b/engines/pelrock/video/video.h
@@ -26,31 +26,35 @@
 
 namespace Pelrock {
 
-static const uint32 frame0offset = 0x5000;
-static const uint32 frame1offset = 0x46000;
+struct ChunkHeader {
+    uint32_t blockCount;      // +0x00: Number of 0x5000-byte blocks
+    uint32_t dataOffset;      // +0x04: Varies by chunk type
+    uint8_t  chunkType;       // +0x08: 1=RLE, 2=BlockCopy, 3=End, 4=Palette, 6=Special
+    // +0x0D: Frame data begins
+	byte *data;
+};
+
 static const uint32 chunkSize = 0x5000;
-static const uint32 offsets[] = {
-	0x64000,
-	0x69000,
-	0x6E000,
-	0x73000,
-	0x78000,
-	0x7D000,
-	0x82000,
-	0x87000};
 
 class VideoManager {
 public:
-	VideoManager(Graphics::Screen *screen, PelrockEventManager *events);
+	VideoManager(Graphics::Screen *screen, PelrockEventManager *events, ChronoManager *chrono);
 	~VideoManager();
 	void playIntro();
 
 private:
 	Graphics::Screen *_screen;
 	PelrockEventManager *_events;
+	ChronoManager *_chrono;
 	void loadPalette(Common::SeekableReadStream &stream);
+	void loadPalette(ChunkHeader &chunk);
 	byte *decodeCopyBlock(byte *data, uint32 offset);
     byte *decodeRLE(byte *data, size_t size, uint32 offset);
+	void readChunk(Common::SeekableReadStream &stream, ChunkHeader &chunk);
+	void processFrame(ChunkHeader &chunk, const int frameCount);
+	void presentFrame();
+	byte *_currentKeyFrame = new byte[640 * 400];
+	Common::Array<ChunkHeader> _chunkBuffer;
 };
 
 } // End of namespace Pelrock


Commit: 33afb68090be03f4ab70dfca1d8c79424e56e25c
    https://github.com/scummvm/scummvm/commit/33afb68090be03f4ab70dfca1d8c79424e56e25c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:36+02:00

Commit Message:
PELROCK: Fix missing alfred frame on queued actions

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/video/video.cpp
    engines/pelrock/video/video.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index ec35dfc84d2..0391b38a0c9 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -507,6 +507,9 @@ void PelrockEngine::doAction(VerbIcon action, HotSpot *hotspot) {
 		pickUpAndDisable(hotspot);
 		executeAction(PICKUP, hotspot);
 		break;
+	case OPEN:
+	case CLOSE:
+		alfredState.setState(ALFRED_INTERACTING);
 	default:
 		executeAction(action, hotspot);
 		break;
@@ -572,8 +575,10 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			if (_currentStep >= _currentContext.movementCount) {
 				_currentStep = 0;
 				alfredState.setState(ALFRED_IDLE);
+
 				if (_currentHotspot != nullptr)
 					alfredState.direction = calculateAlfredsDirection(_currentHotspot);
+				drawAlfred(_res->alfredIdle[alfredState.direction]);
 
 				if (_queuedAction.isQueued) {
 					doAction(_queuedAction.verb, &_room->_currentRoomHotspots[_queuedAction.hotspotIndex]);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 63c46b11290..691b6808d08 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -141,7 +141,7 @@ private:
 
 	bool showShadows = false;
 
-	bool shouldPlayIntro = true;
+	bool shouldPlayIntro = false;
 	bool gameInitialized = false;
 	bool screenReady = false;
 
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index 3bb59706f49..2eac7bc4f4a 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -31,9 +31,11 @@
 namespace Pelrock {
 
 VideoManager::VideoManager(Graphics::Screen *screen, PelrockEventManager *events, ChronoManager *chrono) : _screen(screen), _events(events), _chrono(chrono) {
+	_screenSurface.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 }
 
 VideoManager::~VideoManager() {
+	_screenSurface.free();
 }
 
 void VideoManager::playIntro() {
@@ -42,10 +44,9 @@ void VideoManager::playIntro() {
 		error("Could not open ESCENAX.SSN");
 		return;
 	}
-	loadPalette(videoFile);
 	videoFile.seek(0, SEEK_SET);
 
-	memset(_currentKeyFrame, 0, 256000);
+	_screenSurface.fillRect(Common::Rect(0, 0, 640, 400), 0);
 	for (int sequence = 0; sequence < 1; sequence++) {
 		int frameCounter = 0;
 		int chunksInBuffer = 0;
@@ -83,20 +84,6 @@ void VideoManager::playIntro() {
 	videoFile.close();
 }
 
-void VideoManager::loadPalette(Common::SeekableReadStream &stream) {
-
-	byte paletteData[768];
-	stream.seek(0x0009, SEEK_SET);
-	stream.read(paletteData, 768);
-	byte palette[768];
-	for (int i = 0; i < 256; i++) {
-		palette[i * 3 + 0] = paletteData[i * 3 + 0] << 2;
-		palette[i * 3 + 1] = paletteData[i * 3 + 1] << 2;
-		palette[i * 3 + 2] = paletteData[i * 3 + 2] << 2;
-	}
-	_screen->setPalette(palette);
-}
-
 void VideoManager::loadPalette(ChunkHeader &chunk) {
 	byte palette[768];
 	for (int i = 0; i < 256; i++) {
@@ -183,12 +170,13 @@ void VideoManager::processFrame(ChunkHeader &chunk, const int frameCount) {
 		frameData = decodeCopyBlock(chunk.data, 0);
 	}
 
+	byte *surfacePixels = (byte *)_screenSurface.getPixels();
 	if(frameCount == 0) {
-		memcpy(_currentKeyFrame, frameData, 256000);
+		memcpy(surfacePixels, frameData, 256000);
 	} else {
 		// Subsequent frames, XOR with previous frame
 		for (int i = 0; i < 256000; i++) {
-			_currentKeyFrame[i] ^= frameData[i];
+			surfacePixels[i] ^= frameData[i];
 		}
 	}
 	delete[] frameData;
@@ -196,7 +184,7 @@ void VideoManager::processFrame(ChunkHeader &chunk, const int frameCount) {
 }
 
 void VideoManager::presentFrame() {
-	memcpy(_screen->getPixels(), _currentKeyFrame, 640 * 400);
+	_screen->blitFrom(_screenSurface);
 	_screen->markAllDirty();
 	_screen->update();
 }
diff --git a/engines/pelrock/video/video.h b/engines/pelrock/video/video.h
index 82354cbb833..f46c8068229 100644
--- a/engines/pelrock/video/video.h
+++ b/engines/pelrock/video/video.h
@@ -22,6 +22,8 @@
 #ifndef PELROCK_VIDEO_H
 #define PELROCK_VIDEO_H
 
+#include "graphics/surface.h"
+
 #include "pelrock/events.h"
 
 namespace Pelrock {
@@ -46,14 +48,13 @@ private:
 	Graphics::Screen *_screen;
 	PelrockEventManager *_events;
 	ChronoManager *_chrono;
-	void loadPalette(Common::SeekableReadStream &stream);
 	void loadPalette(ChunkHeader &chunk);
 	byte *decodeCopyBlock(byte *data, uint32 offset);
     byte *decodeRLE(byte *data, size_t size, uint32 offset);
 	void readChunk(Common::SeekableReadStream &stream, ChunkHeader &chunk);
 	void processFrame(ChunkHeader &chunk, const int frameCount);
 	void presentFrame();
-	byte *_currentKeyFrame = new byte[640 * 400];
+	Graphics::Surface _screenSurface = Graphics::Surface();
 	Common::Array<ChunkHeader> _chunkBuffer;
 };
 


Commit: 4f3db805075f3acd338c02ccef054975533f27e4
    https://github.com/scummvm/scummvm/commit/4f3db805075f3acd338c02ccef054975533f27e4
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:36+02:00

Commit Message:
PELROCK: Switch to using AudioCDManager

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 0391b38a0c9..fda6a07e787 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -32,6 +32,8 @@
 #include "image/pcx.h"
 #include "image/png.h"
 
+#include "backends/audiocd/audiocd.h"
+
 #include "pelrock.h"
 #include "pelrock/actions.h"
 #include "pelrock/console.h"
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 1c08df8579e..47c82d82cb8 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -20,28 +20,28 @@
  */
 
 #include "audio/audiostream.h"
-#include "audio/decoders/mp3.h"
 #include "audio/decoders/raw.h"
 #include "audio/decoders/wave.h"
-
 #include "audio/mixer.h"
+
 #include "common/debug.h"
 #include "common/endian.h"
 #include "common/file.h"
 #include "common/memstream.h"
 #include "common/scummsys.h"
 
+#include "backends/audiocd/audiocd.h"
+
 #include "pelrock/pelrock.h"
 #include "pelrock/sound.h"
 #include "sound.h"
 
 namespace Pelrock {
 
-
-
 SoundManager::SoundManager(Audio::Mixer *mixer)
 	: _mixer(mixer), _currentVolume(255), _musicFile(nullptr) {
 	// TODO: Initialize sound manager
+	g_system->getAudioCDManager()->open();
 }
 
 SoundManager::~SoundManager() {
@@ -184,42 +184,21 @@ bool SoundManager::isPlaying(int channel) const {
 }
 
 void SoundManager::stopMusic() {
-	if (_isMusicPlaying) {
-		debug("Stopping music");
-		_mixer->stopHandle(_musicHandle);
-		_isMusicPlaying = false;
-	}
+	g_system->getAudioCDManager()->stop();
+}
+
+bool SoundManager::isMusicPlaying() {
+	return g_system->getAudioCDManager()->isPlaying();
 }
 
 void SoundManager::playMusicTrack(int trackNumber) {
-// 	if (_currentMusicTrack == trackNumber && _isMusicPlaying) {
-// 		// Already playing this track
-// 		return;
-// 	}
-// 	_currentMusicTrack = trackNumber;
-// 	stopMusic();
-// 	// Open the file
-// 	_musicFile = new Common::File();
-// 	Common::String filename = Common::String::format("music/track%d.mp3", trackNumber);
-
-// 	if (!_musicFile->open(Common::Path(filename))) {
-// 		delete _musicFile;
-// 		_musicFile = nullptr;
-// 		return;
-// 	}
-// #ifdef USE_MAD
-// 	Audio::SeekableAudioStream *stream = Audio::makeMP3Stream(_musicFile, DisposeAfterUse::YES);
-// 	if (!stream) {
-// 		_musicFile->close();
-// 		delete _musicFile;
-// 		_musicFile = nullptr;
-// 		return;
-// 	}
-// 	Audio::AudioStream *loopStream = Audio::makeLoopingAudioStream(stream, 0);
-// 	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, loopStream, -1, _currentVolume);
-// 	_isMusicPlaying = true;
-// 	_musicFile = nullptr;
-// #endif
+	if (_currentMusicTrack == trackNumber && isMusicPlaying()) {
+		// Already playing this track
+		return;
+	}
+	_currentMusicTrack = trackNumber;
+	g_system->getAudioCDManager()->stop();
+	g_system->getAudioCDManager()->play(trackNumber, -1, 0, 0);
 }
 
 void SoundManager::loadSoundIndex() {
@@ -255,15 +234,14 @@ int RANDOM_THRESHOLD = 0x4000;
 
 int SoundManager::tick(uint32 frameCount) {
 
-
 	uint16 rand1 = _rng.nextRandom();
 	// uint32 random = g_engine->getRandomNumber(1);
-	if(rand1 <= RANDOM_THRESHOLD){
+	if (rand1 <= RANDOM_THRESHOLD) {
 		// debug("No SFX this tick due to 50% random");
 		return -1;
 	}
 
-	if((frameCount & COUNTER_MASK) != COUNTER_MASK){
+	if ((frameCount & COUNTER_MASK) != COUNTER_MASK) {
 		// debug("No SFX this tick due to counter mask (counter = %d)", soundFrameCounter);
 		return -1;
 	}
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index e354a8d452e..d47d39559a8 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -191,6 +191,7 @@ public:
 	void stopAllSounds();
 	void stopSound(int channel);
 	void stopMusic();
+	bool isMusicPlaying();
 	void setVolume(int volume);
 	bool isPlaying() const;
 	bool isPlaying(int channel) const;


Commit: 62b56b6c36b87b6cd9ce570024327827c35b931e
    https://github.com/scummvm/scummvm/commit/62b56b6c36b87b6cd9ce570024327827c35b931e
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:37+02:00

Commit Message:
PELROCK: Renders subtitles on intro video

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/video/video.cpp
    engines/pelrock/video/video.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index fda6a07e787..5e3b6e3391d 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -95,7 +95,14 @@ Common::Error PelrockEngine::run() {
 	_sound = new SoundManager(_mixer);
 	_dialog = new DialogManager(_screen, _events, _graphics);
 	_menu = new MenuManager(_screen, _events, _res);
-	_videoManager = new VideoManager(_screen, _events, _chrono);
+	_smallFont = new SmallFont();
+	_smallFont->load("ALFRED.4");
+	_largeFont = new LargeFont();
+	_largeFont->load("ALFRED.7");
+	_doubleSmallFont = new DoubleSmallFont();
+	_doubleSmallFont->load("ALFRED.4");
+	_videoManager = new VideoManager(_screen, _events, _chrono, _largeFont);
+
 	// Set the engine's debugger console
 	setDebugger(new PelrockConsole(this));
 
@@ -147,13 +154,6 @@ void PelrockEngine::init() {
 	_compositeBuffer = new byte[640 * 400];
 	_currentBackground = new byte[640 * 400];
 
-	_smallFont = new SmallFont();
-	_smallFont->load("ALFRED.4");
-	_largeFont = new LargeFont();
-	_largeFont->load("ALFRED.7");
-	_doubleSmallFont = new DoubleSmallFont();
-	_doubleSmallFont->load("ALFRED.4");
-
 	changeCursor(DEFAULT);
 	CursorMan.showMouse(true);
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 691b6808d08..63c46b11290 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -141,7 +141,7 @@ private:
 
 	bool showShadows = false;
 
-	bool shouldPlayIntro = false;
+	bool shouldPlayIntro = true;
 	bool gameInitialized = false;
 	bool screenReady = false;
 
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index 2eac7bc4f4a..f25a3e71820 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -20,8 +20,8 @@
  */
 
 #include "common/file.h"
-#include "graphics/screen.h"
 #include "graphics/paletteman.h"
+#include "graphics/screen.h"
 
 #include "pelrock/chrono.h"
 #include "pelrock/pelrock.h"
@@ -30,15 +30,17 @@
 
 namespace Pelrock {
 
-VideoManager::VideoManager(Graphics::Screen *screen, PelrockEventManager *events, ChronoManager *chrono) : _screen(screen), _events(events), _chrono(chrono) {
-	_screenSurface.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
+VideoManager::VideoManager(Graphics::Screen *screen, PelrockEventManager *events, ChronoManager *chrono, LargeFont *largeFont) : _screen(screen), _events(events), _chrono(chrono), _largeFont(largeFont) {
+	_videoSurface.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
+	_textSurface.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 }
 
 VideoManager::~VideoManager() {
-	_screenSurface.free();
+	_videoSurface.free();
 }
 
 void VideoManager::playIntro() {
+	initMetadata();
 	Common::File videoFile;
 	if (!videoFile.open("ESCENAX.SSN")) {
 		error("Could not open ESCENAX.SSN");
@@ -46,7 +48,8 @@ void VideoManager::playIntro() {
 	}
 	videoFile.seek(0, SEEK_SET);
 
-	_screenSurface.fillRect(Common::Rect(0, 0, 640, 400), 0);
+	_videoSurface.fillRect(Common::Rect(0, 0, 640, 400), 0);
+	_textSurface.fillRect(Common::Rect(0, 0, 640, 400), 255);
 	for (int sequence = 0; sequence < 1; sequence++) {
 		int frameCounter = 0;
 		int chunksInBuffer = 0;
@@ -56,7 +59,7 @@ void VideoManager::playIntro() {
 			_chrono->updateChrono();
 			_events->pollEvent();
 
-			if(_chrono->_gameTick) {
+			if (_chrono->_gameTick && _chrono->getFrameCount() % 2 == 0) {
 				ChunkHeader chunk;
 				readChunk(videoFile, chunk);
 
@@ -75,12 +78,18 @@ void VideoManager::playIntro() {
 					debug("Unknown chunk type %d encountered", chunk.chunkType);
 					break;
 				}
+
+				Subtitle *subtitle = getSubtitleForFrame(frameCounter);
+				if (subtitle != nullptr) {
+					_largeFont->drawString(&_textSurface, subtitle->text, subtitle->x, subtitle->y, 640, 13);
+				}
+
 				presentFrame();
 			}
 			g_system->delayMillis(10);
 		}
+		debug("Total frames played: %d", frameCounter);
 	}
-
 	videoFile.close();
 }
 
@@ -162,7 +171,7 @@ void VideoManager::readChunk(Common::SeekableReadStream &stream, ChunkHeader &ch
 
 void VideoManager::processFrame(ChunkHeader &chunk, const int frameCount) {
 	byte *frameData = nullptr;
-	if(chunk.chunkType == 1) {
+	if (chunk.chunkType == 1) {
 		// Video data chunk
 		frameData = decodeRLE(chunk.data, chunk.blockCount * chunkSize, 0x04);
 	} else if (chunk.chunkType == 2) {
@@ -170,8 +179,8 @@ void VideoManager::processFrame(ChunkHeader &chunk, const int frameCount) {
 		frameData = decodeCopyBlock(chunk.data, 0);
 	}
 
-	byte *surfacePixels = (byte *)_screenSurface.getPixels();
-	if(frameCount == 0) {
+	byte *surfacePixels = (byte *)_videoSurface.getPixels();
+	if (frameCount == 0) {
 		memcpy(surfacePixels, frameData, 256000);
 	} else {
 		// Subsequent frames, XOR with previous frame
@@ -180,13 +189,115 @@ void VideoManager::processFrame(ChunkHeader &chunk, const int frameCount) {
 		}
 	}
 	delete[] frameData;
-
 }
 
 void VideoManager::presentFrame() {
-	_screen->blitFrom(_screenSurface);
+	_screen->blitFrom(_videoSurface);
+	_screen->transBlitFrom(_textSurface, 255);
 	_screen->markAllDirty();
 	_screen->update();
 }
 
+void VideoManager::initMetadata() {
+	Common::File metadataFile;
+	if (!metadataFile.open("ESCENAX.SCR")) {
+		error("Could not open ESCENAX.SCR");
+		return;
+	}
+
+	while (metadataFile.eos() == false) {
+		char curChar = metadataFile.readByte();
+		if (curChar == '/') {
+			char nextChar = metadataFile.readByte();
+			if (nextChar == 't') { // subtitle
+				Subtitle subtitle;
+				Common::String buffer;
+				int values[4];
+				int valueIndex = 0;
+
+				// Skip spaces after "/t"
+				while (!metadataFile.eos() && metadataFile.readByte() == ' ')
+					;
+				metadataFile.seek(-1, SEEK_CUR); // Step back one byte
+
+				// Parse 4 space-delimited numbers
+				while (!metadataFile.eos() && valueIndex < 4) {
+					char c = metadataFile.readByte();
+
+					if (c == ' ') {
+						if (!buffer.empty()) {
+							values[valueIndex++] = atoi(buffer.c_str());
+							buffer.clear();
+						}
+					} else if (c >= '0' && c <= '9') {
+						buffer += c;
+					} else if (c == 0x08) {
+						// End of numbers, start of text
+						if (!buffer.empty()) {
+							values[valueIndex++] = atoi(buffer.c_str());
+						}
+						break;
+					}
+				}
+
+				if (valueIndex == 4) {
+					subtitle.startFrame = values[0];
+					subtitle.endFrame = values[1];
+					subtitle.x = values[2];
+					subtitle.y = values[3];
+
+					// Read text until CRLF (0x0D 0x0A)
+					subtitle.text.clear();
+					while (!metadataFile.eos()) {
+						char c = metadataFile.readByte();
+
+						if (c == 0x0D) {
+							char next = metadataFile.readByte();
+							if (next == 0x0A) {
+								break;
+							} else {
+								subtitle.text += c;
+								subtitle.text += next;
+							}
+						} else {
+							subtitle.text += c;
+						}
+					}
+					_subtitles.push_back(subtitle);
+				}
+			}
+		}
+	}
+
+	debug("Loaded %d subtitles", _subtitles.size());
+	debug("Loaded %d audio effects", _audioEffect.size());
+
+	metadataFile.close();
+}
+
+Subtitle *VideoManager::getSubtitleForFrame(uint16 frameCounter) {
+	// Check if current subtitle is still active
+	if (_currentSubtitleIndex < _subtitles.size()) {
+		Subtitle &sub = _subtitles[_currentSubtitleIndex];
+
+		if (frameCounter >= sub.startFrame && frameCounter <= sub.endFrame) {
+			return ⊂ // Still showing this subtitle
+		}
+
+		if (frameCounter > sub.endFrame) {
+			_currentSubtitleIndex++; // Move to next subtitle
+			_textSurface.fillRect(Common::Rect(0, 0, 640, 400), 255);
+			// Check if new subtitle should be active
+			if (_currentSubtitleIndex < _subtitles.size()) {
+				Subtitle &nextSub = _subtitles[_currentSubtitleIndex];
+				if (frameCounter >= nextSub.startFrame && frameCounter <= nextSub.endFrame) {
+					return &nextSub;
+				}
+			}
+		}
+	}
+
+	return nullptr; // No active subtitle
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/video/video.h b/engines/pelrock/video/video.h
index f46c8068229..61ee9256e3a 100644
--- a/engines/pelrock/video/video.h
+++ b/engines/pelrock/video/video.h
@@ -25,6 +25,7 @@
 #include "graphics/surface.h"
 
 #include "pelrock/events.h"
+#include "pelrock/fonts/large_font.h"
 
 namespace Pelrock {
 
@@ -36,11 +37,38 @@ struct ChunkHeader {
 	byte *data;
 };
 
+struct Effect {
+	uint16 startFrame;
+};
+
+struct AudioEffect : Effect {
+};
+
+struct Subtitle : Effect {
+	uint16 startFrame;
+	uint16 endFrame;
+	uint16 x;
+	uint16 y;
+	Common::String text;
+};
+
+struct Voice : AudioEffect {
+	char filename[12];
+};
+
+struct Sfx : AudioEffect {
+	uint32 soundId;
+};
+
+struct ExtraSound : AudioEffect {
+	char filename[12];
+};
+
 static const uint32 chunkSize = 0x5000;
 
 class VideoManager {
 public:
-	VideoManager(Graphics::Screen *screen, PelrockEventManager *events, ChronoManager *chrono);
+	VideoManager(Graphics::Screen *screen, PelrockEventManager *events, ChronoManager *chrono, LargeFont *largeFont);
 	~VideoManager();
 	void playIntro();
 
@@ -48,14 +76,21 @@ private:
 	Graphics::Screen *_screen;
 	PelrockEventManager *_events;
 	ChronoManager *_chrono;
+	LargeFont *_largeFont;
 	void loadPalette(ChunkHeader &chunk);
 	byte *decodeCopyBlock(byte *data, uint32 offset);
     byte *decodeRLE(byte *data, size_t size, uint32 offset);
 	void readChunk(Common::SeekableReadStream &stream, ChunkHeader &chunk);
 	void processFrame(ChunkHeader &chunk, const int frameCount);
 	void presentFrame();
-	Graphics::Surface _screenSurface = Graphics::Surface();
+	void initMetadata();
+	Subtitle *getSubtitleForFrame(uint16 frameNumber);
+	int _currentSubtitleIndex = 0;
+	Graphics::Surface _videoSurface = Graphics::Surface();
+	Graphics::Surface _textSurface = Graphics::Surface();
 	Common::Array<ChunkHeader> _chunkBuffer;
+	Common::Array<Subtitle> _subtitles;
+	Common::Array<AudioEffect> _audioEffect;
 };
 
 } // End of namespace Pelrock


Commit: 291103f79bec1ca43a3f5c7d9c797ac701860098
    https://github.com/scummvm/scummvm/commit/291103f79bec1ca43a3f5c7d9c797ac701860098
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:37+02:00

Commit Message:
PELROCK: Proper colors in video subtitles

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/video/video.cpp
    engines/pelrock/video/video.h


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 566d9e1b667..80601605cdd 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -618,36 +618,44 @@ void DialogManager::say(Common::StringArray texts) {
 	if (texts.empty()) {
 		return;
 	}
-	int speakerMarker = texts[0][0];
-	int color = texts[0][1];
+	byte speakerId;
+	bool wasProcessed = processColorAndTrim(texts, speakerId);
 
-	if (speakerMarker == '@') {
-
-		for (int i = 0; i < texts.size(); i++) {
-			// Remove first two marker bytes
-			if (texts[i].size() > 2) {
-				texts[i] = texts[i].substr(2);
-				if (texts[i][0] == 0x78 && texts[i][1] == 0x78) { // Remove additional control chars
-					texts[i] = texts[i].substr(2);
-				}
-			} else {
-				texts[i] = "";
-			}
-		}
-
-		if (color == ALFRED_COLOR) {
+	if (wasProcessed) {
+		if (speakerId == ALFRED_COLOR) {
 			sayAlfred(texts);
 			return;
 		} else {
 			setCurSprite(0);
 			Common::Array<Common::StringArray> textLines = wordWrap(texts);
-			displayDialogue(textLines, color);
+			displayDialogue(textLines, speakerId);
 		}
 	} else {
 		sayAlfred(texts);
 	}
 }
 
+bool DialogManager::processColorAndTrim(Common::StringArray &lines, byte &speakerId) {
+	int speakerMarker = lines[0][0];
+	speakerId = lines[0][1];
+	if (speakerMarker == '@') {
+
+		for (int i = 0; i < lines.size(); i++) {
+			// Remove first two marker bytes
+			if (lines[i].size() > 2) {
+				lines[i] = lines[i].substr(2);
+				if (lines[i][0] == 0x78 && lines[i][1] == 0x78) { // Remove additional control chars
+					lines[i] = lines[i].substr(2);
+				}
+			} else {
+				lines[i] = "";
+			}
+		}
+		return true;
+	}
+	return false;
+}
+
 bool isEndMarker(char char_byte) {
 	return char_byte == CHAR_END_MARKER_1 || char_byte == CHAR_END_MARKER_2 || char_byte == CHAR_END_MARKER_3 || char_byte == CHAR_END_MARKER_4;
 }
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index d9b4e82a192..0b9b59504ae 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -95,6 +95,7 @@ public:
 	void startConversation(const byte *conversationData, uint32 dataSize, Sprite *alfredAnimSet = nullptr);
 	void sayAlfred(Description description);
 	void say(Common::StringArray texts);
+	bool processColorAndTrim(Common::StringArray &lines, byte &speakerId);
 
 	Common::Array<Common::Array<Common::String>> wordWrap(Common::String text);
 	Common::Array<Common::Array<Common::String>> wordWrap(Common::StringArray texts);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 5e3b6e3391d..d1623192ffc 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -101,7 +101,7 @@ Common::Error PelrockEngine::run() {
 	_largeFont->load("ALFRED.7");
 	_doubleSmallFont = new DoubleSmallFont();
 	_doubleSmallFont->load("ALFRED.4");
-	_videoManager = new VideoManager(_screen, _events, _chrono, _largeFont);
+	_videoManager = new VideoManager(_screen, _events, _chrono, _largeFont, _dialog);
 
 	// Set the engine's debugger console
 	setDebugger(new PelrockConsole(this));
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index f25a3e71820..4552c1a799d 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -30,7 +30,10 @@
 
 namespace Pelrock {
 
-VideoManager::VideoManager(Graphics::Screen *screen, PelrockEventManager *events, ChronoManager *chrono, LargeFont *largeFont) : _screen(screen), _events(events), _chrono(chrono), _largeFont(largeFont) {
+VideoManager::VideoManager(
+	Graphics::Screen *screen,
+	PelrockEventManager *events,
+	ChronoManager *chrono, LargeFont *largeFont, DialogManager *dialog) : _screen(screen), _events(events), _chrono(chrono), _largeFont(largeFont), _dialog(dialog) {
 	_videoSurface.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 	_textSurface.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 }
@@ -79,9 +82,15 @@ void VideoManager::playIntro() {
 					break;
 				}
 
+
 				Subtitle *subtitle = getSubtitleForFrame(frameCounter);
 				if (subtitle != nullptr) {
-					_largeFont->drawString(&_textSurface, subtitle->text, subtitle->x, subtitle->y, 640, 13);
+					Common::StringArray lines;
+					lines.push_back(subtitle->text);
+					byte color;
+					_dialog->processColorAndTrim(lines, color);
+					debug("Displaying subtitle: %s with color %d", subtitle->text.c_str(), color);
+					_largeFont->drawString(&_textSurface, subtitle->text, subtitle->x, subtitle->y, 640, color);
 				}
 
 				presentFrame();
@@ -239,7 +248,7 @@ void VideoManager::initMetadata() {
 						break;
 					}
 				}
-
+				metadataFile.skip(1); // Skip the extra space
 				if (valueIndex == 4) {
 					subtitle.startFrame = values[0];
 					subtitle.endFrame = values[1];
@@ -260,7 +269,10 @@ void VideoManager::initMetadata() {
 								subtitle.text += next;
 							}
 						} else {
-							subtitle.text += c;
+							if(c == 0x08)
+								subtitle.text += '@';
+							else
+								subtitle.text += c;
 						}
 					}
 					_subtitles.push_back(subtitle);
diff --git a/engines/pelrock/video/video.h b/engines/pelrock/video/video.h
index 61ee9256e3a..64e618b309f 100644
--- a/engines/pelrock/video/video.h
+++ b/engines/pelrock/video/video.h
@@ -68,7 +68,13 @@ static const uint32 chunkSize = 0x5000;
 
 class VideoManager {
 public:
-	VideoManager(Graphics::Screen *screen, PelrockEventManager *events, ChronoManager *chrono, LargeFont *largeFont);
+	VideoManager(
+		Graphics::Screen *screen,
+		PelrockEventManager *events,
+		ChronoManager *chrono,
+		LargeFont *largeFont,
+		DialogManager *dialog
+	);
 	~VideoManager();
 	void playIntro();
 
@@ -77,6 +83,7 @@ private:
 	PelrockEventManager *_events;
 	ChronoManager *_chrono;
 	LargeFont *_largeFont;
+	DialogManager *_dialog;
 	void loadPalette(ChunkHeader &chunk);
 	byte *decodeCopyBlock(byte *data, uint32 offset);
     byte *decodeRLE(byte *data, size_t size, uint32 offset);


Commit: 07706d32616f565a74be9f37a642f64c9bf389e2
    https://github.com/scummvm/scummvm/commit/07706d32616f565a74be9f37a642f64c9bf389e2
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:37+02:00

Commit Message:
PELROCK: Word wraps subtitles

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/video/video.cpp
    engines/pelrock/video/video.h


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 80601605cdd..e90f09189dc 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -134,6 +134,28 @@ void DialogManager::displayChoices(Common::Array<ChoiceOption> *choices, byte *c
 	}
 }
 
+Graphics::Surface DialogManager::getDialogueSurface(Common::Array<Common::String> dialogueLines, byte speakerId) {
+
+	int maxWidth = 0;
+	int height = dialogueLines.size() * 24;
+	for (int i = 0; i < dialogueLines.size(); i++) {
+		maxWidth = MAX(maxWidth, g_engine->_largeFont->getStringWidth(dialogueLines[i]));
+	}
+
+	Graphics::Surface s;
+	s.create(maxWidth, height, Graphics::PixelFormat::createFormatCLUT8());
+	s.fillRect(s.getRect(), 255); // Clear surface
+
+	for (int i = 0; i < dialogueLines.size(); i++) {
+
+		int xPos = 0;
+		int yPos = i * 20; // Above sprite, adjust for line
+
+		g_engine->_largeFont->drawString(&s, dialogueLines[i], xPos, yPos, maxWidth, speakerId, Graphics::kTextAlignCenter);
+	}
+
+	return s;
+}
 /**
  * Display dialogue text and wait for click to advance
  * @param text The text to display
@@ -162,11 +184,6 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		for (int i = 0; i < textLines.size(); i++) {
 			maxWidth = MAX(maxWidth, g_engine->_largeFont->getStringWidth(textLines[i]));
 		}
-
-		Graphics::Surface s;
-		s.create(maxWidth, height, Graphics::PixelFormat::createFormatCLUT8());
-		s.fillRect(s.getRect(), 255); // Clear surface
-		// s.drawRoundRect(Common::Rect(0, 0, s.getRect().width(), s.getRect().height()), 2, 13, false);
 		int xPos = 0;
 		int yPos = 0;
 
@@ -185,13 +202,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 			yPos = _curSprite->y - height; // Above sprite, adjust for line
 		}
 
-		for (int i = 0; i < textLines.size(); i++) {
-
-			int xPos = 0;
-			int yPos = i * 20; // Above sprite, adjust for line
-
-			g_engine->_largeFont->drawString(&s, textLines[i], xPos, yPos, maxWidth, speakerId, Graphics::kTextAlignCenter);
-		}
+		Graphics::Surface s = getDialogueSurface(textLines, speakerId);
 
 		if (xPos + s.getRect().width() > 640) {
 			xPos = 640 - s.getRect().width() - 2;
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 0b9b59504ae..0812eddef57 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -96,6 +96,7 @@ public:
 	void sayAlfred(Description description);
 	void say(Common::StringArray texts);
 	bool processColorAndTrim(Common::StringArray &lines, byte &speakerId);
+	Graphics::Surface getDialogueSurface(Common::Array<Common::String> dialogueLines, byte speakerId);
 
 	Common::Array<Common::Array<Common::String>> wordWrap(Common::String text);
 	Common::Array<Common::Array<Common::String>> wordWrap(Common::StringArray texts);
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index 4552c1a799d..67f19ea5d99 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -58,7 +58,7 @@ void VideoManager::playIntro() {
 		int chunksInBuffer = 0;
 		bool videoExitFlag = false;
 
-		while (!videoExitFlag && !g_engine->shouldQuit()) {
+		while (!videoExitFlag && !g_engine->shouldQuit() && _events->_lastKeyEvent != Common::KEYCODE_ESCAPE) {
 			_chrono->updateChrono();
 			_events->pollEvent();
 
@@ -82,15 +82,13 @@ void VideoManager::playIntro() {
 					break;
 				}
 
-
 				Subtitle *subtitle = getSubtitleForFrame(frameCounter);
 				if (subtitle != nullptr) {
-					Common::StringArray lines;
-					lines.push_back(subtitle->text);
+					Common::StringArray lines = _dialog->wordWrap(subtitle->text)[0];
+
 					byte color;
 					_dialog->processColorAndTrim(lines, color);
-					debug("Displaying subtitle: %s with color %d", subtitle->text.c_str(), color);
-					_largeFont->drawString(&_textSurface, subtitle->text, subtitle->x, subtitle->y, 640, color);
+					_textSurface.transBlitFrom(_dialog->getDialogueSurface(lines, color), Common::Point(subtitle->x, subtitle->y), 255);
 				}
 
 				presentFrame();
diff --git a/engines/pelrock/video/video.h b/engines/pelrock/video/video.h
index 64e618b309f..801e720b00a 100644
--- a/engines/pelrock/video/video.h
+++ b/engines/pelrock/video/video.h
@@ -94,7 +94,7 @@ private:
 	Subtitle *getSubtitleForFrame(uint16 frameNumber);
 	int _currentSubtitleIndex = 0;
 	Graphics::Surface _videoSurface = Graphics::Surface();
-	Graphics::Surface _textSurface = Graphics::Surface();
+	Graphics::ManagedSurface _textSurface = Graphics::ManagedSurface();
 	Common::Array<ChunkHeader> _chunkBuffer;
 	Common::Array<Subtitle> _subtitles;
 	Common::Array<AudioEffect> _audioEffect;


Commit: 476564d9826583b2200d12b0e19f021d1cf026fd
    https://github.com/scummvm/scummvm/commit/476564d9826583b2200d12b0e19f021d1cf026fd
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:38+02:00

Commit Message:
PELROCK: Improvements on text positioning

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/util.cpp
    engines/pelrock/video/video.cpp
    engines/pelrock/video/video.h


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index e90f09189dc..c739a04932b 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -149,8 +149,7 @@ Graphics::Surface DialogManager::getDialogueSurface(Common::Array<Common::String
 	for (int i = 0; i < dialogueLines.size(); i++) {
 
 		int xPos = 0;
-		int yPos = i * 20; // Above sprite, adjust for line
-
+		int yPos = i * 25; // Above sprite, adjust for line
 		g_engine->_largeFont->drawString(&s, dialogueLines[i], xPos, yPos, maxWidth, speakerId, Graphics::kTextAlignCenter);
 	}
 
@@ -188,7 +187,9 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		int yPos = 0;
 
 		if (speakerId == ALFRED_COLOR) {
-			g_engine->alfredState.setState(ALFRED_TALKING);
+			if(g_engine->alfredState.animState != ALFRED_TALKING) {
+				g_engine->alfredState.setState(ALFRED_TALKING);
+			}
 			if (_curSprite != nullptr) {
 				_curSprite->isTalking = false;
 			}
@@ -198,14 +199,14 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		} else {
 			g_engine->alfredState.setState(ALFRED_IDLE);
 			_curSprite->isTalking = true;
-			xPos = _curSprite->x + _curSprite->w / 2 - maxWidth / 2;
+			xPos = _curSprite->x + _curSprite->w / 2;
 			yPos = _curSprite->y - height; // Above sprite, adjust for line
 		}
 
 		Graphics::Surface s = getDialogueSurface(textLines, speakerId);
 
 		if (xPos + s.getRect().width() > 640) {
-			xPos = 640 - s.getRect().width() - 2;
+			xPos = 640 - s.getRect().width();
 		}
 		if (yPos + s.getRect().height() > 400) {
 			yPos = 400 - s.getRect().height();
@@ -216,8 +217,11 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		if (yPos < 0) {
 			yPos = 0;
 		}
-
 		_screen->transBlitFrom(s, s.getRect(), Common::Point(xPos, yPos), 255);
+		drawPos(_screen, xPos, yPos, speakerId);
+		drawRect(_screen, xPos, yPos,
+			s.getRect().width(),
+			s.getRect().height(), speakerId);
 		// Present to screen
 		_screen->markAllDirty();
 		_screen->update();
@@ -617,8 +621,8 @@ void DialogManager::sayAlfred(Common::StringArray texts) {
 
 void DialogManager::sayAlfred(Description description) {
 	Common::StringArray texts;
-	texts.push_back(description.text);
 
+	texts.push_back(description.text);
 	sayAlfred(texts);
 	if (description.isAction) {
 		g_engine->performActionTrigger(description.actionTrigger);
@@ -649,12 +653,14 @@ void DialogManager::say(Common::StringArray texts) {
 bool DialogManager::processColorAndTrim(Common::StringArray &lines, byte &speakerId) {
 	int speakerMarker = lines[0][0];
 	speakerId = lines[0][1];
+
 	if (speakerMarker == '@') {
 
 		for (int i = 0; i < lines.size(); i++) {
 			// Remove first two marker bytes
 			if (lines[i].size() > 2) {
 				lines[i] = lines[i].substr(2);
+
 				if (lines[i][0] == 0x78 && lines[i][1] == 0x78) { // Remove additional control chars
 					lines[i] = lines[i].substr(2);
 				}
@@ -713,8 +719,7 @@ Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::Str
 		bool isEnd = false;
 		int wordLength = calculateWordLength(text, position, isEnd);
 		// # Extract the word (including trailing spaces)
-		// word = text[position:position + word_length].decode('latin-1', errors='replace')
-		Common::String word = text.substr(position, wordLength).decode(Common::kLatin1);
+		Common::String word = text.substr(position, wordLength);
 		// # Key decision: if word_length > chars_remaining, wrap to next line
 		if (wordLength > charsRemaining) {
 			// Word is longer than the entire line - need to split
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 0812eddef57..230dfa135ae 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -70,6 +70,18 @@ struct ChoiceOption {
 	ChoiceOption() : index(-1), isDisabled(false), dataOffset(0) {}
 };
 
+static void debugHexString(const Common::String &str, const char *label = nullptr) {
+	if (label) {
+		debug("%s:", label);
+	}
+
+	Common::String hexOutput;
+	for (uint i = 0; i < str.size(); i++) {
+		hexOutput += Common::String::format("%02X ", (unsigned char)str[i]);
+	}
+	debug("%s", hexOutput.c_str());
+}
+
 class DialogManager {
 private:
 	Graphics::Screen *_screen = nullptr;
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index d1623192ffc..1e13810181a 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -594,7 +594,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 
 		Exit *exit = isExitUnder(alfredState.x, alfredState.y);
 
-		if (exit != nullptr) {
+		if (exit != nullptr && exit->isEnabled) {
 			alfredState.x = exit->targetX;
 			alfredState.y = exit->targetY;
 			setScreen(exit->targetRoom, exit->dir);
@@ -604,7 +604,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			alfredState.curFrame = 0;
 		}
 
-		drawAlfred(_res->alfredWalkFrames[alfredState.direction][alfredState.curFrame]);
+	drawAlfred(_res->alfredWalkFrames[alfredState.direction][alfredState.curFrame]);
 		alfredState.curFrame++;
 		break;
 	}
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 63c46b11290..691b6808d08 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -141,7 +141,7 @@ private:
 
 	bool showShadows = false;
 
-	bool shouldPlayIntro = true;
+	bool shouldPlayIntro = false;
 	bool gameInitialized = false;
 	bool screenReady = false;
 
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 4d5065a42f2..e06a144542f 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -436,15 +436,15 @@ Common::Array<Description> RoomManager::loadRoomTexts(Common::File *roomFile, in
 		int desc_pos = 0;
 		if (data[pos] == 0xFF) {
 			Description description;
+
 			description.itemId = data[pos + 1];
-			pos += 3;
+			pos += 4;
 			description.index = data[pos++];
 			description.text = "";
 
 			while (pos < (pair12_size) && data[pos] != 0xFD && pos < (pair12_size)) {
 
 				if (data[pos] != 0x00) {
-					// debug("Adding char 0x%02X to description, decoded as %lc", data[pos], decodeChar((byte)data[pos]));
 					description.text.append(1, (char)data[pos]);
 				}
 				if (data[pos] == 0xF8) {
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 5fa4e5ad68d..cf2503cd5aa 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -295,7 +295,6 @@ Common::String joinStrings(const Common::Array<Common::String> &strings, const C
 void drawPos(Graphics::ManagedSurface *surface, int x, int y, byte color) {
 	if (x < 640 && y < 400 && x >= 0 && y >= 0) {
 		surface->setPixel(x, y, 100);
-
 		surface->drawEllipse(x - 3, y - 3, x + 3, y + 3, color, true);
 	}
 }
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index 67f19ea5d99..46e7bc7f7da 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -26,7 +26,7 @@
 #include "pelrock/chrono.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/video/video.h"
-#include "video.h"
+#include "pelrock/util.h"
 
 namespace Pelrock {
 
@@ -88,7 +88,13 @@ void VideoManager::playIntro() {
 
 					byte color;
 					_dialog->processColorAndTrim(lines, color);
-					_textSurface.transBlitFrom(_dialog->getDialogueSurface(lines, color), Common::Point(subtitle->x, subtitle->y), 255);
+					Graphics::Surface s = _dialog->getDialogueSurface(lines, color);
+					_textSurface.transBlitFrom(s, Common::Point(subtitle->x, subtitle->y), 255);
+
+					drawPos(&_textSurface, subtitle->x, subtitle->y, color);
+					drawRect(&_textSurface, subtitle->x, subtitle->y,
+						s.getRect().width(),
+						s.getRect().height(), color);
 				}
 
 				presentFrame();
@@ -270,7 +276,7 @@ void VideoManager::initMetadata() {
 							if(c == 0x08)
 								subtitle.text += '@';
 							else
-								subtitle.text += c;
+								subtitle.text += decodeChar(c);
 						}
 					}
 					_subtitles.push_back(subtitle);
@@ -285,6 +291,30 @@ void VideoManager::initMetadata() {
 	metadataFile.close();
 }
 
+char VideoManager::decodeChar(byte c) {
+
+	switch (c) {
+	case 0xAD:
+		return video_special_chars[1];
+	case 0xA8:
+		return video_special_chars[0];
+	case 0xA4:
+		return video_special_chars[3]; // n tilde
+	case 0xA3:
+		return video_special_chars[4];
+	case 0xA2:
+		return video_special_chars[5];
+	case 0xA1:
+		return video_special_chars[6];
+	case 0x82:
+		return video_special_chars[7];
+	case 0xA0:
+		return video_special_chars[8];
+	default:
+		return c;
+	}
+}
+
 Subtitle *VideoManager::getSubtitleForFrame(uint16 frameCounter) {
 	// Check if current subtitle is still active
 	if (_currentSubtitleIndex < _subtitles.size()) {
diff --git a/engines/pelrock/video/video.h b/engines/pelrock/video/video.h
index 801e720b00a..37ca542c6a0 100644
--- a/engines/pelrock/video/video.h
+++ b/engines/pelrock/video/video.h
@@ -66,6 +66,18 @@ struct ExtraSound : AudioEffect {
 
 static const uint32 chunkSize = 0x5000;
 
+static const int video_special_chars[] = {
+	0x83, // inverted ?
+	0x82, // inverted !
+	165, // capital N tilde
+	0x80, // small n tilde
+	0x7F, // small u tilde
+	0x7E, // small o tilde
+	0x7D, // small i tilde
+	0x7C, // small e tilde
+	0x7B, // small a tilde
+};
+
 class VideoManager {
 public:
 	VideoManager(
@@ -91,6 +103,7 @@ private:
 	void processFrame(ChunkHeader &chunk, const int frameCount);
 	void presentFrame();
 	void initMetadata();
+	char decodeChar(byte c);
 	Subtitle *getSubtitleForFrame(uint16 frameNumber);
 	int _currentSubtitleIndex = 0;
 	Graphics::Surface _videoSurface = Graphics::Surface();


Commit: 0a3c9e5e6e003b53290d036af7b9b02b9a9cd176
    https://github.com/scummvm/scummvm/commit/0a3c9e5e6e003b53290d036af7b9b02b9a9cd176
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:38+02:00

Commit Message:
PELROCK: Improvements on text positioning

Changed paths:
    engines/pelrock/dialog.cpp


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index c739a04932b..e5429d4c61f 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -194,7 +194,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 				_curSprite->isTalking = false;
 			}
 			// Offset X position for Alfred to avoid overlapping with his sprite
-			xPos = g_engine->alfredState.x + kAlfredFrameWidth / 2 - maxWidth / 2;
+			xPos = g_engine->alfredState.x - maxWidth / 2; //+ kAlfredFrameWidth / 2 - maxWidth / 2;
 			yPos = g_engine->alfredState.y - kAlfredFrameHeight - height; // Above sprite, adjust for line
 		} else {
 			g_engine->alfredState.setState(ALFRED_IDLE);


Commit: a527041b51cfd2ba428a440ef481d7f6ba8e4fe6
    https://github.com/scummvm/scummvm/commit/a527041b51cfd2ba428a440ef481d7f6ba8e4fe6
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:38+02:00

Commit Message:
PELROCK: Checks mouse hover against sprite pixel masks

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 1e13810181a..09d72217160 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -56,9 +56,9 @@ PelrockEngine::PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc) :
 }
 
 PelrockEngine::~PelrockEngine() {
-	if(_compositeBuffer)
+	if (_compositeBuffer)
 		delete[] _compositeBuffer;
-	if(_currentBackground)
+	if (_currentBackground)
 		delete[] _currentBackground;
 	delete _largeFont;
 	delete _smallFont;
@@ -122,7 +122,7 @@ Common::Error PelrockEngine::run() {
 		_videoManager->playIntro();
 		stateGame = GAME;
 	}
-	if(!shouldQuit())
+	if (!shouldQuit())
 		init();
 
 	while (!shouldQuit()) {
@@ -604,7 +604,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			alfredState.curFrame = 0;
 		}
 
-	drawAlfred(_res->alfredWalkFrames[alfredState.direction][alfredState.curFrame]);
+		drawAlfred(_res->alfredWalkFrames[alfredState.direction][alfredState.curFrame]);
 		alfredState.curFrame++;
 		break;
 	}
@@ -978,6 +978,19 @@ int PelrockEngine::isHotspotUnder(int x, int y) {
 		if (hotspot.isEnabled &&
 			x >= hotspot.x && x <= (hotspot.x + hotspot.w) &&
 			y >= hotspot.y && y <= (hotspot.y + hotspot.h)) {
+			// Check against sprite frame
+			if (hotspot.isSprite) {
+				Sprite *sprite = nullptr;
+				for (size_t j = 0; j < _room->_currentRoomAnims.size(); j++) {
+					if (_room->_currentRoomAnims[j].index == hotspot.index) {
+						sprite = &(_room->_currentRoomAnims[j]);
+						break;
+					}
+				}
+				bool spriteUnder = isSpriteUnder(sprite, x, y);
+				return spriteUnder ? i : -1;
+			}
+
 			return i;
 		}
 	}
@@ -997,6 +1010,28 @@ Exit *PelrockEngine::isExitUnder(int x, int y) {
 	return nullptr;
 }
 
+/**
+ * Checks if the given position is actually frame data or transparent pixel
+ */
+bool PelrockEngine::isSpriteUnder(Sprite *sprite, int x, int y) {
+	Anim &animData = sprite->animData[sprite->curAnimIndex];
+	int frameSize = animData.w * animData.h;
+	int curFrame = animData.curFrame;
+	byte *frame = new byte[frameSize];
+	extractSingleFrame(animData.animData, frame, curFrame, animData.w, animData.h);
+	int localX = x - animData.x;
+	int localY = y - animData.y;
+	if (localX >= 0 && localX < animData.w && localY >= 0 && localY < animData.h) {
+		byte pixel = frame[localY * animData.w + localX];
+		if (pixel != 255) {
+			delete[] frame;
+			return true;
+		}
+	}
+	delete[] frame;
+	return false;
+}
+
 void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	drawSpriteToBuffer(_compositeBuffer, 640, _res->_popUpBalloon + (curFrame * kBalloonHeight * kBalloonWidth), posx, posy, kBalloonWidth, kBalloonHeight, 255);
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 691b6808d08..9484d292a29 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -82,7 +82,7 @@ private:
 	bool isAlfredUnder(int x, int y);
 	int isHotspotUnder(int x, int y);
 	Exit *isExitUnder(int x, int y);
-	Sprite *isSpriteUnder(int x, int y);
+	bool isSpriteUnder(Sprite *sprite, int x, int y);
 
 	void showActionBalloon(int posx, int posy, int curFrame);
 


Commit: a13abbc63c4041323cbc8cf1df750d6ddaa0ce9c
    https://github.com/scummvm/scummvm/commit/a13abbc63c4041323cbc8cf1df750d6ddaa0ce9c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:38+02:00

Commit Message:
PELROCK: Refactor sprite data to preprocess animations

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 09d72217160..1ca4f04a638 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -802,8 +802,7 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 	int frameSize = animData.w * animData.h;
 	int curFrame = animData.curFrame;
 	byte *frame = new byte[frameSize];
-	extractSingleFrame(animData.animData, frame, curFrame, animData.w, animData.h);
-	drawSpriteToBuffer(_compositeBuffer, 640, frame, sprite->x, sprite->y, sprite->w, sprite->h, 255);
+	drawSpriteToBuffer(_compositeBuffer, 640, animData.animData[curFrame], sprite->x, sprite->y, sprite->w, sprite->h, 255);
 
 	// if (animData.elpapsedFrames == animData.speed) {
 	if (_chrono->getFrameCount() % animData.speed == 0) {
@@ -1002,7 +1001,7 @@ Exit *PelrockEngine::isExitUnder(int x, int y) {
 		Exit exit = _room->_currentRoomExits[i];
 		if (x >= exit.x && x <= (exit.x + exit.w) &&
 			y >= exit.y && y <= (exit.y + exit.h)
-			// && exit.isEnabled
+			&& exit.isEnabled
 		) {
 			return &(_room->_currentRoomExits[i]);
 		}
@@ -1017,18 +1016,15 @@ bool PelrockEngine::isSpriteUnder(Sprite *sprite, int x, int y) {
 	Anim &animData = sprite->animData[sprite->curAnimIndex];
 	int frameSize = animData.w * animData.h;
 	int curFrame = animData.curFrame;
-	byte *frame = new byte[frameSize];
-	extractSingleFrame(animData.animData, frame, curFrame, animData.w, animData.h);
+
 	int localX = x - animData.x;
 	int localY = y - animData.y;
 	if (localX >= 0 && localX < animData.w && localY >= 0 && localY < animData.h) {
-		byte pixel = frame[localY * animData.w + localX];
+		byte pixel = animData.animData[curFrame][localY * animData.w + localX];
 		if (pixel != 255) {
-			delete[] frame;
 			return true;
 		}
 	}
-	delete[] frame;
 	return false;
 }
 
@@ -1062,7 +1058,7 @@ void PelrockEngine::animateTalkingNPC(Sprite *animSet) {
 	// Change with the right index
 
 	int index = animSet->index;
-	TalkingAnimHeader *animHeader = &_room->_talkingAnimHeader;
+	TalkingAnims *animHeader = &_room->_talkingAnimHeader;
 
 	int x = animSet->x + (index ? animHeader->offsetXAnimB : animHeader->offsetXAnimA);
 	int y = animSet->y + (index ? animHeader->offsetYAnimB : animHeader->offsetYAnimA);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index e06a144542f..c682155bd5c 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -339,7 +339,6 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(Common::File *roomFile, in
 		sprite.w = animData[4];
 		sprite.h = animData[5];
 		sprite.extra = animData[6];
-		// roomFile->skip(1); // reserved
 		sprite.numAnims = animData[8];
 		sprite.zOrder = animData[23];
 		sprite.spriteType = animData[33];
@@ -364,11 +363,14 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(Common::File *roomFile, in
 			anim.loopCount = animData[subAnimOffset + 4 + j];
 			anim.speed = animData[subAnimOffset + 8 + j];
 			anim.movementFlags = animData[subAnimOffset + 14 + (j * 2)] | (animData[subAnimOffset + 14 + (j * 2) + 1] << 8);
-			anim.animData = new byte[anim.nframes];
+
+			anim.animData = new byte *[anim.nframes];
 			if (anim.w > 0 && anim.h > 0 && anim.nframes > 0) {
 				uint32_t needed = anim.w * anim.h * anim.nframes;
-				anim.animData = new byte[needed];
-				Common::copy(pic + picOffset, pic + picOffset + needed, anim.animData);
+				for(int i = 0; i < anim.nframes; i++) {
+					anim.animData[i] = new byte[anim.w * anim.h];
+					extractSingleFrame(pic + picOffset, anim.animData[i], i, anim.w, anim.h);
+				}
 				sprite.animData[j] = anim;
 				// debug("  Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d loopCount=%d speed=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes, anim.loopCount, anim.speed);
 				// debug("  Movement flags: 0x%04X", anim.movementFlags);
@@ -479,7 +481,7 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 	int headerIndex = roomNumber;
 	uint32 offset = kTalkingAnimHeaderSize * headerIndex;
 
-	TalkingAnimHeader talkHeader;
+	TalkingAnims talkHeader;
 	Common::File talkFile;
 	if (!talkFile.open("ALFRED.2")) {
 		error("Couldnt find file ALFRED.2");
@@ -512,10 +514,6 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 		return;
 	}
 
-	// if(talkHeader.animA != nullptr) {
-	// 	delete[] talkHeader.animA;
-	// 	talkHeader.animA = nullptr;
-	// }
 	talkHeader.animA = new byte *[talkHeader.numFramesAnimA];
 
 	byte *data = nullptr;
@@ -532,10 +530,6 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 	}
 
 	if (talkHeader.numFramesAnimB > 0) {
-		// if(talkHeader.animA != nullptr) {
-		// 	delete[] talkHeader.animA;
-		// 	talkHeader.animA = nullptr;
-		// }
 		talkHeader.animB = new byte *[talkHeader.numFramesAnimB];
 		for (int i = 0; i < talkHeader.numFramesAnimB; i++) {
 			talkHeader.animB[i] = new byte[talkHeader.wAnimB * talkHeader.hAnimB];
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 74b86f15d0b..8809aaeddb2 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -59,7 +59,7 @@ public:
 	Common::Array<WalkBox> _currentRoomWalkboxes;
 	Common::Array<Description> _currentRoomDescriptions;
 
-	TalkingAnimHeader _talkingAnimHeader;
+	TalkingAnims _talkingAnimHeader;
 	ScalingParams _scaleParams;
 	byte *_pixelsShadows = nullptr;
 	byte _roomPalette[768];
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 4a6767ebc38..d53fa540694 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -162,7 +162,7 @@ struct Anim {
 	int nframes;
 	int curFrame = 0;
 	int curLoop = 0;
-	byte *animData;
+	byte **animData;
 	byte loopCount;
 	byte speed;
 	byte elpapsedFrames = 0;
@@ -214,7 +214,7 @@ struct HotSpot {
 	byte zOrder = 0;
 };
 
-struct TalkingAnimHeader {
+struct TalkingAnims {
 	uint32 spritePointer;
 
 	byte unknown2[3];


Commit: 65a66c21d00a8feaed5ff50d5128eb842d7103ab
    https://github.com/scummvm/scummvm/commit/65a66c21d00a8feaed5ff50d5128eb842d7103ab
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:39+02:00

Commit Message:
PELROCK: Reads voice files

Changed paths:
    engines/pelrock/pelrock.h
    engines/pelrock/video/video.cpp
    engines/pelrock/video/video.h


diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 9484d292a29..a4a87c51f1f 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -141,7 +141,7 @@ private:
 
 	bool showShadows = false;
 
-	bool shouldPlayIntro = false;
+	bool shouldPlayIntro = true;
 	bool gameInitialized = false;
 	bool screenReady = false;
 
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index 46e7bc7f7da..97d55bd2c16 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -25,8 +25,9 @@
 
 #include "pelrock/chrono.h"
 #include "pelrock/pelrock.h"
-#include "pelrock/video/video.h"
 #include "pelrock/util.h"
+#include "pelrock/video/video.h"
+#include "video.h"
 
 namespace Pelrock {
 
@@ -36,10 +37,14 @@ VideoManager::VideoManager(
 	ChronoManager *chrono, LargeFont *largeFont, DialogManager *dialog) : _screen(screen), _events(events), _chrono(chrono), _largeFont(largeFont), _dialog(dialog) {
 	_videoSurface.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 	_textSurface.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
+	if (!_introSndFile.open("introsnd.dat")) {
+		error("Could not open introsnd.dat");
+	}
 }
 
 VideoManager::~VideoManager() {
 	_videoSurface.free();
+	_introSndFile.close();
 }
 
 void VideoManager::playIntro() {
@@ -93,8 +98,8 @@ void VideoManager::playIntro() {
 
 					drawPos(&_textSurface, subtitle->x, subtitle->y, color);
 					drawRect(&_textSurface, subtitle->x, subtitle->y,
-						s.getRect().width(),
-						s.getRect().height(), color);
+							 s.getRect().width(),
+							 s.getRect().height(), color);
 				}
 
 				presentFrame();
@@ -218,69 +223,36 @@ void VideoManager::initMetadata() {
 		return;
 	}
 
+	// 1. Read the file allocation table from introsnd.dat
+	if (_introSndFile.isOpen()) {
+		_introSndFile.seek(0, SEEK_SET);
+		char signature[5] = {0};
+		_introSndFile.read(signature, 4);
+		if (strcmp(signature, "PACK") == 0) {
+			uint32_t numFiles = _introSndFile.readUint32LE();
+			for (uint32_t i = 0; i < numFiles; ++i) {
+				VoiceData sound;
+				Common::String filename = _introSndFile.readString();
+				// _introSndFile.skip(1);
+				sound.offset = _introSndFile.readUint32LE();
+				sound.length = _introSndFile.readUint32LE();
+				_sounds[filename] = sound;
+			}
+		}
+		debug("Loaded %d sound entries", _sounds.size());
+	}
+
 	while (metadataFile.eos() == false) {
 		char curChar = metadataFile.readByte();
 		if (curChar == '/') {
 			char nextChar = metadataFile.readByte();
 			if (nextChar == 't') { // subtitle
-				Subtitle subtitle;
-				Common::String buffer;
-				int values[4];
-				int valueIndex = 0;
-
-				// Skip spaces after "/t"
-				while (!metadataFile.eos() && metadataFile.readByte() == ' ')
-					;
-				metadataFile.seek(-1, SEEK_CUR); // Step back one byte
-
-				// Parse 4 space-delimited numbers
-				while (!metadataFile.eos() && valueIndex < 4) {
-					char c = metadataFile.readByte();
-
-					if (c == ' ') {
-						if (!buffer.empty()) {
-							values[valueIndex++] = atoi(buffer.c_str());
-							buffer.clear();
-						}
-					} else if (c >= '0' && c <= '9') {
-						buffer += c;
-					} else if (c == 0x08) {
-						// End of numbers, start of text
-						if (!buffer.empty()) {
-							values[valueIndex++] = atoi(buffer.c_str());
-						}
-						break;
-					}
-				}
-				metadataFile.skip(1); // Skip the extra space
-				if (valueIndex == 4) {
-					subtitle.startFrame = values[0];
-					subtitle.endFrame = values[1];
-					subtitle.x = values[2];
-					subtitle.y = values[3];
-
-					// Read text until CRLF (0x0D 0x0A)
-					subtitle.text.clear();
-					while (!metadataFile.eos()) {
-						char c = metadataFile.readByte();
-
-						if (c == 0x0D) {
-							char next = metadataFile.readByte();
-							if (next == 0x0A) {
-								break;
-							} else {
-								subtitle.text += c;
-								subtitle.text += next;
-							}
-						} else {
-							if(c == 0x08)
-								subtitle.text += '@';
-							else
-								subtitle.text += decodeChar(c);
-						}
-					}
-					_subtitles.push_back(subtitle);
-				}
+				Subtitle subtitle = readSubtitle(metadataFile);
+				_subtitles.push_back(subtitle);
+			} else if (nextChar == 'x') {
+				Voice voice = readVoice(metadataFile);
+				// Read filename (up to 12 bytes, null-terminated)
+				_audioEffect.push_back(voice);
 			}
 		}
 	}
@@ -291,6 +263,95 @@ void VideoManager::initMetadata() {
 	metadataFile.close();
 }
 
+Voice VideoManager::readVoice(Common::File &metadataFile) {
+	Voice voice;
+	Common::String buffer;
+
+	// Skip spaces after "/x"
+	while (!metadataFile.eos() && metadataFile.readByte() == ' ')
+		;
+	metadataFile.seek(-1, SEEK_CUR); // Step back one byte
+
+	bool frameCountRead = false;
+	while (!metadataFile.eos()) {
+		char c = metadataFile.readByte();
+		if (c == ' ') {
+			if (!buffer.empty() && !frameCountRead) {
+				voice.startFrame = atoi(buffer.c_str());
+				buffer.clear();
+				frameCountRead = true;
+			}
+		} else if (c == 0x0D || c == 0x0A) {
+			break;
+		} else {
+			buffer += c;
+		}
+	}
+	voice.filename = buffer;
+	debug("Loaded voice: frame %d, file '%s'", voice.startFrame, voice.filename.c_str());
+	return voice;
+}
+
+Subtitle VideoManager::readSubtitle(Common::File &metadataFile) {
+	Subtitle subtitle;
+	Common::String buffer;
+	int values[4];
+	int valueIndex = 0;
+
+	// Skip spaces after "/t"
+	while (!metadataFile.eos() && metadataFile.readByte() == ' ')
+		;
+	metadataFile.seek(-1, SEEK_CUR); // Step back one byte
+
+	// Parse 4 space-delimited numbers
+	while (!metadataFile.eos() && valueIndex < 4) {
+		char c = metadataFile.readByte();
+
+		if (c == ' ') {
+			if (!buffer.empty()) {
+				values[valueIndex++] = atoi(buffer.c_str());
+				buffer.clear();
+			}
+		} else if (c >= '0' && c <= '9') {
+			buffer += c;
+		} else if (c == 0x08) {
+			// End of numbers, start of text
+			if (!buffer.empty()) {
+				values[valueIndex++] = atoi(buffer.c_str());
+			}
+			break;
+		}
+	}
+	metadataFile.skip(1); // Skip the extra space
+
+	subtitle.startFrame = values[0];
+	subtitle.endFrame = values[1];
+	subtitle.x = values[2];
+	subtitle.y = values[3];
+
+	// Read text until CRLF (0x0D 0x0A)
+	subtitle.text.clear();
+	while (!metadataFile.eos()) {
+		char c = metadataFile.readByte();
+
+		if (c == 0x0D) {
+			char next = metadataFile.readByte();
+			if (next == 0x0A) {
+				break;
+			} else {
+				subtitle.text += c;
+				subtitle.text += next;
+			}
+		} else {
+			if (c == 0x08)
+				subtitle.text += '@';
+			else
+				subtitle.text += decodeChar(c);
+		}
+	}
+	return subtitle;
+}
+
 char VideoManager::decodeChar(byte c) {
 
 	switch (c) {
diff --git a/engines/pelrock/video/video.h b/engines/pelrock/video/video.h
index 37ca542c6a0..a3597ee71c3 100644
--- a/engines/pelrock/video/video.h
+++ b/engines/pelrock/video/video.h
@@ -53,7 +53,7 @@ struct Subtitle : Effect {
 };
 
 struct Voice : AudioEffect {
-	char filename[12];
+	Common::String filename;
 };
 
 struct Sfx : AudioEffect {
@@ -61,7 +61,12 @@ struct Sfx : AudioEffect {
 };
 
 struct ExtraSound : AudioEffect {
-	char filename[12];
+	Common::String filename;
+};
+
+struct VoiceData {
+	uint32 offset;
+	uint32 length;
 };
 
 static const uint32 chunkSize = 0x5000;
@@ -103,6 +108,9 @@ private:
 	void processFrame(ChunkHeader &chunk, const int frameCount);
 	void presentFrame();
 	void initMetadata();
+	void readSubtitle(Common::File &metadataFile, Pelrock::Subtitle &subtitle);
+	Subtitle readSubtitle(Common::File &metadataFile);
+	Voice readVoice(Common::File &metadataFile);
 	char decodeChar(byte c);
 	Subtitle *getSubtitleForFrame(uint16 frameNumber);
 	int _currentSubtitleIndex = 0;
@@ -111,6 +119,8 @@ private:
 	Common::Array<ChunkHeader> _chunkBuffer;
 	Common::Array<Subtitle> _subtitles;
 	Common::Array<AudioEffect> _audioEffect;
+	Common::HashMap<Common::String, VoiceData> _sounds;
+	Common::File _introSndFile;
 };
 
 } // End of namespace Pelrock


Commit: 22ff6faa73d3290864dabd5e30f7bc10664bbf19
    https://github.com/scummvm/scummvm/commit/22ff6faa73d3290864dabd5e30f7bc10664bbf19
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:39+02:00

Commit Message:
PELROCK: Plays intro speech

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h
    engines/pelrock/video/video.cpp
    engines/pelrock/video/video.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 1ca4f04a638..b6bcfeccc17 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -101,7 +101,7 @@ Common::Error PelrockEngine::run() {
 	_largeFont->load("ALFRED.7");
 	_doubleSmallFont = new DoubleSmallFont();
 	_doubleSmallFont->load("ALFRED.4");
-	_videoManager = new VideoManager(_screen, _events, _chrono, _largeFont, _dialog);
+	_videoManager = new VideoManager(_screen, _events, _chrono, _largeFont, _dialog, _sound);
 
 	// Set the engine's debugger console
 	setDebugger(new PelrockConsole(this));
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 47c82d82cb8..281ba6d9304 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -100,10 +100,14 @@ void SoundManager::playSound(SonidoFile sound, int volume) {
 		return;
 	}
 
-	// if (stream) {
-	// 	int channel = findFreeChannel();
-	// 	_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, volume, 0, DisposeAfterUse::YES);
-	// }
+}
+
+void SoundManager::playSound(byte *soundData, uint32 size,int volume) {
+	Audio::AudioStream *stream = Audio::makeRawStream(soundData, size, 11025, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
+	 if (stream) {
+		int channel = findFreeChannel();
+		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, volume, 0, DisposeAfterUse::YES);
+	}
 }
 
 SoundFormat SoundManager::detectFormat(byte *data, uint32 size) {
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index d47d39559a8..ee305f6a20d 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -188,6 +188,7 @@ public:
 	SoundManager(Audio::Mixer *mixer);
 	~SoundManager();
 	void playSound(byte index, int volume = 255);
+	void playSound(byte *soundData, uint32 size, int volume = 255);
 	void stopAllSounds();
 	void stopSound(int channel);
 	void stopMusic();
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index 97d55bd2c16..a0ad426faa9 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -34,7 +34,10 @@ namespace Pelrock {
 VideoManager::VideoManager(
 	Graphics::Screen *screen,
 	PelrockEventManager *events,
-	ChronoManager *chrono, LargeFont *largeFont, DialogManager *dialog) : _screen(screen), _events(events), _chrono(chrono), _largeFont(largeFont), _dialog(dialog) {
+	ChronoManager *chrono,
+	LargeFont *largeFont,
+	DialogManager *dialog,
+	SoundManager *sound) : _screen(screen), _events(events), _chrono(chrono), _largeFont(largeFont), _dialog(dialog), _sound(sound) {
 	_videoSurface.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 	_textSurface.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 	if (!_introSndFile.open("introsnd.dat")) {
@@ -59,7 +62,7 @@ void VideoManager::playIntro() {
 	_videoSurface.fillRect(Common::Rect(0, 0, 640, 400), 0);
 	_textSurface.fillRect(Common::Rect(0, 0, 640, 400), 255);
 	for (int sequence = 0; sequence < 1; sequence++) {
-		int frameCounter = 0;
+		uint16 frameCounter = 0;
 		int chunksInBuffer = 0;
 		bool videoExitFlag = false;
 
@@ -87,6 +90,18 @@ void VideoManager::playIntro() {
 					break;
 				}
 
+				if (_voiceEffect.contains(frameCounter)) {
+					Voice voice = _voiceEffect[frameCounter];
+					debug("Playing voice effect: '%s'", voice.filename.c_str());
+					VoiceData voiceData = _sounds[voice.filename];
+					_introSndFile.seek(voiceData.offset, SEEK_SET);
+					byte *voiceBuffer = new byte[voiceData.length];
+					_introSndFile.read(voiceBuffer, voiceData.length);
+					_sound->playSound(voiceBuffer, voiceData.length);
+					// g_system->getSoundManager()->playSoundBuffer(voiceBuffer, voiceData.length, SOUND_FORMAT_PCM8, 11025);
+					// delete[] voiceBuffer;
+				}
+
 				Subtitle *subtitle = getSubtitleForFrame(frameCounter);
 				if (subtitle != nullptr) {
 					Common::StringArray lines = _dialog->wordWrap(subtitle->text)[0];
@@ -223,7 +238,6 @@ void VideoManager::initMetadata() {
 		return;
 	}
 
-	// 1. Read the file allocation table from introsnd.dat
 	if (_introSndFile.isOpen()) {
 		_introSndFile.seek(0, SEEK_SET);
 		char signature[5] = {0};
@@ -237,8 +251,10 @@ void VideoManager::initMetadata() {
 				sound.offset = _introSndFile.readUint32LE();
 				sound.length = _introSndFile.readUint32LE();
 				_sounds[filename] = sound;
+				debug("Loaded sound: '%s' (offset: %u, length: %u)", filename.c_str(), sound.offset, sound.length);
 			}
 		}
+
 		debug("Loaded %d sound entries", _sounds.size());
 	}
 
@@ -252,13 +268,13 @@ void VideoManager::initMetadata() {
 			} else if (nextChar == 'x') {
 				Voice voice = readVoice(metadataFile);
 				// Read filename (up to 12 bytes, null-terminated)
-				_audioEffect.push_back(voice);
+				_voiceEffect[voice.startFrame] = voice;
 			}
 		}
 	}
 
 	debug("Loaded %d subtitles", _subtitles.size());
-	debug("Loaded %d audio effects", _audioEffect.size());
+	debug("Loaded %d audio effects", _voiceEffect.size());
 
 	metadataFile.close();
 }
diff --git a/engines/pelrock/video/video.h b/engines/pelrock/video/video.h
index a3597ee71c3..064a0c7b7ae 100644
--- a/engines/pelrock/video/video.h
+++ b/engines/pelrock/video/video.h
@@ -90,7 +90,8 @@ public:
 		PelrockEventManager *events,
 		ChronoManager *chrono,
 		LargeFont *largeFont,
-		DialogManager *dialog
+		DialogManager *dialog,
+		SoundManager *sound
 	);
 	~VideoManager();
 	void playIntro();
@@ -101,6 +102,7 @@ private:
 	ChronoManager *_chrono;
 	LargeFont *_largeFont;
 	DialogManager *_dialog;
+	SoundManager *_sound;
 	void loadPalette(ChunkHeader &chunk);
 	byte *decodeCopyBlock(byte *data, uint32 offset);
     byte *decodeRLE(byte *data, size_t size, uint32 offset);
@@ -118,7 +120,7 @@ private:
 	Graphics::ManagedSurface _textSurface = Graphics::ManagedSurface();
 	Common::Array<ChunkHeader> _chunkBuffer;
 	Common::Array<Subtitle> _subtitles;
-	Common::Array<AudioEffect> _audioEffect;
+	Common::HashMap<uint16, Voice> _voiceEffect;
 	Common::HashMap<Common::String, VoiceData> _sounds;
 	Common::File _introSndFile;
 };


Commit: 828e6d9b84e85cfa3ec904c0b0bbf6a680e7999e
    https://github.com/scummvm/scummvm/commit/828e6d9b84e85cfa3ec904c0b0bbf6a680e7999e
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:39+02:00

Commit Message:
PELROCK: Read metadata parameters from byte buffer

Changed paths:
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/sound.cpp
    engines/pelrock/video/video.cpp


diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index a4a87c51f1f..9484d292a29 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -141,7 +141,7 @@ private:
 
 	bool showShadows = false;
 
-	bool shouldPlayIntro = true;
+	bool shouldPlayIntro = false;
 	bool gameInitialized = false;
 	bool screenReady = false;
 
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index c682155bd5c..4d1397a181e 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -162,27 +162,24 @@ PaletteAnim *RoomManager::getPaletteAnimForRoom(int roomNumber) {
 	return anim;
 }
 
-Common::Array<Exit> RoomManager::loadExits(Common::File *roomFile, int roomOffset) {
+Common::Array<Exit> RoomManager::loadExits(byte *data, size_t size) {
 	Common::Array<Exit> exits;
-	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
-	roomFile->seek(pair10_offset_pos, SEEK_SET);
-	uint32_t pair10_data_offset = roomFile->readUint32LE();
-	uint32_t pair10_size = roomFile->readUint32LE();
-	roomFile->seek(pair10_data_offset + 0x1BE, SEEK_SET);
-	int exit_count = roomFile->readByte();
-	roomFile->seek(pair10_data_offset + 0x1BF, SEEK_SET);
-	for (int i = 0; i < exit_count; i++) {
+	int exitCountOffset = 0x1BE;
+	byte exitCount = data[exitCountOffset];
+	int exitDataOffset = 0x1BF;
+	for (int i = 0; i < exitCount; i++) {
+		int exitOffset = exitDataOffset + i * 14;
 		Exit exit;
-		exit.targetRoom = roomFile->readUint16LE();
-		exit.isEnabled = roomFile->readByte();
-		exit.x = roomFile->readUint16LE();
-		exit.y = roomFile->readUint16LE();
-		exit.w = roomFile->readByte();
-		exit.h = roomFile->readByte();
-
-		exit.targetX = roomFile->readUint16LE();
-		exit.targetY = roomFile->readUint16LE();
-		byte dir = roomFile->readByte();
+		exit.targetRoom = READ_LE_INT16(data + exitOffset);
+		exit.isEnabled = data[exitOffset + 2];
+		exit.x = READ_LE_INT16(data + exitOffset + 3);
+		exit.y = READ_LE_INT16(data + exitOffset + 5);
+		exit.w = data[exitOffset + 7];
+		exit.h = data[exitOffset + 8];
+
+		exit.targetX = READ_LE_INT16(data + exitOffset + 9);
+		exit.targetY = READ_LE_INT16(data + exitOffset + 11);
+		byte dir = data[exitOffset + 13];
 		switch (dir) {
 		case ALFRED_RIGHT:
 			exit.dir = ALFRED_RIGHT;
@@ -202,32 +199,30 @@ Common::Array<Exit> RoomManager::loadExits(Common::File *roomFile, int roomOffse
 		}
 
 		exits.push_back(exit);
+		debug("Exit %d: targetRoom=%d isEnabled=%d x=%d y=%d w=%d h=%d targetX=%d targetY=%d dir=%d",
+			  i, exit.targetRoom, exit.isEnabled, exit.x, exit.y, exit.w, exit.h,
+			  exit.targetX, exit.targetY, exit.dir);
 	}
 	return exits;
 }
 
-Common::Array<HotSpot> RoomManager::loadHotspots(Common::File *roomFile, int roomOffset) {
-	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
+Common::Array<HotSpot> RoomManager::loadHotspots(byte *data, size_t size) {
+	int pair10StartingPos = 0x47a;
 
-	roomFile->seek(pair10_offset_pos, SEEK_SET);
-	uint32_t pair10_data_offset = roomFile->readUint32LE();
-	uint32_t pair10_size = roomFile->readUint32LE();
-	uint32_t count_offset = pair10_data_offset + 0x47a;
-	roomFile->seek(count_offset, SEEK_SET);
-	byte hotspot_count = roomFile->readByte();
-	uint32_t hotspot_data_start = pair10_data_offset + 0x47c;
+
+	byte hotspot_count = data[pair10StartingPos];
+	int hotspotsDataStart = pair10StartingPos + 2;
 	Common::Array<HotSpot> hotspots;
 	for (int i = 0; i < hotspot_count; i++) {
-		uint32_t obj_offset = hotspot_data_start + i * 9;
-		roomFile->seek(obj_offset, SEEK_SET);
+		int hotspotOffset = hotspotsDataStart + i * 9;
 		HotSpot spot;
-		spot.actionFlags = roomFile->readByte();
-		spot.x = roomFile->readSint16LE();
-		spot.y = roomFile->readSint16LE();
-		spot.w = roomFile->readByte();
-		spot.h = roomFile->readByte();
+		spot.actionFlags = data[hotspotOffset];
+		spot.x = READ_LE_INT16(data + hotspotOffset + 1);
+		spot.y = READ_LE_INT16(data + hotspotOffset + 3);
+		spot.w = data[hotspotOffset + 5];
+		spot.h = data[hotspotOffset + 6];
 		spot.isSprite = false;
-		spot.extra = roomFile->readSint16LE();
+		spot.extra = READ_LE_INT16(data + hotspotOffset + 7);
 		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, spot.actionFlags, spot.x, spot.y, spot.w, spot.h, spot.extra);
 		hotspots.push_back(spot);
 	}
@@ -238,9 +233,33 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	uint32_t outPos = 0;
 	_currentRoomStickers.clear();
 	int roomOffset = roomNumber * kRoomStructSize;
-	Common::Array<Description> descriptions = loadRoomTexts(roomFile, roomOffset);
-	Common::Array<Sprite> anims = loadRoomAnimations(roomFile, roomOffset);
 
+	uint32_t pair10offset = roomOffset + (10 * 8);
+	roomFile->seek(pair10offset, SEEK_SET);
+	// roomFile->skip(4);
+	uint32_t pair10dataOffset = roomFile->readUint32LE();
+	uint32_t pair10size = roomFile->readUint32LE();
+
+	byte *pair10 = new byte[pair10size];
+	roomFile->seek(pair10dataOffset, SEEK_SET);
+	roomFile->read(pair10, pair10size);
+
+	// The user's game can be in any state so we reset to defaults first
+	resetRoomDefaults(pair10, pair10size);
+
+
+	byte *pic = nullptr;
+	size_t pixelDataSize = 0;
+	loadAnimationPixelData(roomFile, roomOffset, pic, pixelDataSize);
+
+	Common::Array<Sprite> anims = loadRoomAnimations(pic, pixelDataSize, pair10, pair10size);
+	Common::Array<HotSpot> staticHotspots = loadHotspots(pair10, pair10size);
+	Common::Array<WalkBox> walkboxes = loadWalkboxes(pair10, pair10size);
+	Common::Array<Exit> exits = loadExits(pair10, pair10size);
+	ScalingParams scalingParams = loadScalingParams(pair10, pair10size);
+
+
+	Common::Array<Description> descriptions = loadRoomTexts(roomFile, roomOffset);
 	Common::Array<HotSpot> hotspots;
 	for (int i = 0; i < anims.size(); i++) {
 
@@ -258,11 +277,6 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 		hotspots.push_back(thisHotspot);
 	}
 
-	Common::Array<HotSpot> staticHotspots = loadHotspots(roomFile, roomOffset);
-	Common::Array<Exit> exits = loadExits(roomFile, roomOffset);
-	ScalingParams scalingParams = loadScalingParams(roomFile, roomOffset);
-	Common::Array<WalkBox> walkboxes = loadWalkboxes(roomFile, roomOffset);
-
 	// debug("total descriptions = %d, anims = %d, hotspots = %d", descriptions.size(), anims.size(), staticHotspots.size());
 	for (int i = 0; i < staticHotspots.size(); i++) {
 		HotSpot hotspot = staticHotspots[i];
@@ -302,54 +316,52 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	} else {
 		_currentPaletteAnim = nullptr;
 	}
+
+	delete[] pair10;
 }
 
-Common::Array<Sprite> RoomManager::loadRoomAnimations(Common::File *roomFile, int roomOffset) {
+void RoomManager::loadAnimationPixelData(Common::File *roomFile, int roomOffset, byte *&buffer, size_t &outSize) {
 	uint32_t pair_offset = roomOffset + (8 * 8);
-	// debug("Sprite pair offset position: %d", pair_offset);
 	roomFile->seek(pair_offset, SEEK_SET);
 	uint32_t offset = roomFile->readUint32LE();
 	uint32_t size = roomFile->readUint32LE();
 
-	byte *data = new byte[size];
+	byte  *pixelData = new byte[size];
 	roomFile->seek(offset, SEEK_SET);
-	roomFile->read(data, size);
-
-	unsigned char *pic = nullptr;
+	roomFile->read(pixelData, size);
 	if (offset > 0 && size > 0) {
-		rleDecompress(data, size, 0, size, &pic, true);
-	} else {
-		return Common::Array<Sprite>();
+		outSize = rleDecompress(pixelData, size, 0, size, &buffer, true);
 	}
-	Common::Array<Sprite> anims = Common::Array<Sprite>();
-	uint32_t spriteEnd = offset + size;
+}
 
-	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
-	uint32_t metadata_start = spriteEnd + 108;
+Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pixelDataSize, byte *data, size_t size) {
+
+	Common::Array<Sprite> anims = Common::Array<Sprite>();
+	uint32_t spriteCountPos = 5;
+	byte spriteCount = data[spriteCountPos] - 2;
+	debug("Sprite count: %d", spriteCount);
+	uint32_t metadata_start = spriteCountPos + (44 * 2 + 5);
 	uint32_t picOffset = 0;
-	for (int i = 0; i < 7; i++) {
+	for (int i = 0; i < spriteCount; i++) {
 		uint32_t animOffset = metadata_start + (i * 44);
-		byte *animData = new byte[44];
-		roomFile->seek(animOffset, SEEK_SET);
-		roomFile->read(animData, 44);
 		Sprite sprite;
 		sprite.index = i;
-		sprite.x = animData[0] | (animData[1] << 8);
-		sprite.y = animData[2] | (animData[3] << 8);
-		sprite.w = animData[4];
-		sprite.h = animData[5];
-		sprite.extra = animData[6];
-		sprite.numAnims = animData[8];
-		sprite.zOrder = animData[23];
-		sprite.spriteType = animData[33];
-		sprite.actionFlags = animData[34];
-		sprite.isDisabled = animData[38];
+		sprite.x = READ_LE_INT16(data + animOffset + 0);
+		sprite.y = READ_LE_INT16(data + animOffset + 2);
+		sprite.w = data[animOffset + 4];
+		sprite.h = data[animOffset + 5];
+		sprite.extra = data[animOffset + 6];
+		sprite.numAnims = data[animOffset + 8];
+		sprite.zOrder = data[animOffset + 23];
+		sprite.spriteType = data[animOffset + 33];
+		sprite.actionFlags = data[animOffset + 34];
+		sprite.isDisabled = data[animOffset + 38];
 		if (sprite.numAnims == 0) {
 			break;
 		}
 		sprite.animData = new Anim[sprite.numAnims];
 		// debug("AnimSet %d has %d sub-anims, type %d, actionFlags %d, isDisabled? %d", i, animSet.numAnims, animSet.spriteType, animSet.actionFlags, animSet.isDisabled);
-		int subAnimOffset = 10;
+		int subAnimOffset = animOffset + 10;
 		for (int j = 0; j < sprite.numAnims; j++) {
 
 			Anim anim;
@@ -359,17 +371,17 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(Common::File *roomFile, in
 			anim.h = sprite.h;
 			anim.curFrame = 0;
 
-			anim.nframes = animData[subAnimOffset + j];
-			anim.loopCount = animData[subAnimOffset + 4 + j];
-			anim.speed = animData[subAnimOffset + 8 + j];
-			anim.movementFlags = animData[subAnimOffset + 14 + (j * 2)] | (animData[subAnimOffset + 14 + (j * 2) + 1] << 8);
+			anim.nframes = data[subAnimOffset + j];
+			anim.loopCount = data[subAnimOffset + 4 + j];
+			anim.speed = data[subAnimOffset + 8 + j];
+			anim.movementFlags = data[subAnimOffset + 14 + (j * 2)] | (data[subAnimOffset + 14 + (j * 2) + 1] << 8);
 
 			anim.animData = new byte *[anim.nframes];
 			if (anim.w > 0 && anim.h > 0 && anim.nframes > 0) {
 				uint32_t needed = anim.w * anim.h * anim.nframes;
 				for(int i = 0; i < anim.nframes; i++) {
 					anim.animData[i] = new byte[anim.w * anim.h];
-					extractSingleFrame(pic + picOffset, anim.animData[i], i, anim.w, anim.h);
+					extractSingleFrame(pixelData + picOffset, anim.animData[i], i, anim.w, anim.h);
 				}
 				sprite.animData[j] = anim;
 				// debug("  Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d loopCount=%d speed=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes, anim.loopCount, anim.speed);
@@ -387,28 +399,21 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(Common::File *roomFile, in
 	return anims;
 }
 
-Common::Array<WalkBox> RoomManager::loadWalkboxes(Common::File *roomFile, int roomOffset) {
-	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
-	roomFile->seek(pair10_offset_pos, SEEK_SET);
-	// roomFile->skip(4);
-	uint32_t pair10_data_offset = roomFile->readUint32LE();
-	uint32_t pair10_size = roomFile->readUint32LE();
+Common::Array<WalkBox> RoomManager::loadWalkboxes(byte *data, size_t size) {
 
-	uint32_t walkbox_countOffset = pair10_data_offset + 0x213;
-	roomFile->seek(walkbox_countOffset, SEEK_SET);
-	byte walkbox_count = roomFile->readByte();
+	int walkboxCountOffset = 0x213;
+	byte walkboxCount = data[walkboxCountOffset];
 
 	// debug("Walkbox count: %d", walkbox_count);
-	uint32_t walkbox_offset = pair10_data_offset + 0x218;
+	uint32_t walkboxOffset = 0x218;
 	Common::Array<WalkBox> walkboxes;
-	for (int i = 0; i < walkbox_count; i++) {
-		uint32_t box_offset = walkbox_offset + i * 9;
-		roomFile->seek(box_offset, SEEK_SET);
-		int16 x1 = roomFile->readSint16LE();
-		int16 y1 = roomFile->readSint16LE();
-		int16 w = roomFile->readSint16LE();
-		int16 h = roomFile->readSint16LE();
-		byte flags = roomFile->readByte();
+	for (int i = 0; i < walkboxCount; i++) {
+		uint32_t boxOffset = walkboxOffset + i * 9;
+		int16 x1 = READ_LE_INT16(data + boxOffset);
+		int16 y1 = READ_LE_INT16(data + boxOffset + 2);
+		int16 w = READ_LE_INT16(data + boxOffset + 4);
+		int16 h = READ_LE_INT16(data + boxOffset + 6);
+		byte flags = data[boxOffset + 8];
 		debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
 		WalkBox box;
 		box.x = x1;
@@ -424,7 +429,6 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(Common::File *roomFile, int ro
 Common::Array<Description> RoomManager::loadRoomTexts(Common::File *roomFile, int roomOffset) {
 	uint32_t pair12_offset_pos = roomOffset + (12 * 8);
 	roomFile->seek(pair12_offset_pos, SEEK_SET);
-	// roomFile->skip(4);
 	uint32_t pair12_data_offset = roomFile->readUint32LE();
 	uint32_t pair12_size = roomFile->readUint32LE();
 
@@ -476,6 +480,10 @@ Common::Array<Description> RoomManager::loadRoomTexts(Common::File *roomFile, in
 	return descriptions;
 }
 
+void RoomManager::resetRoomDefaults(byte *data, size_t size) {
+
+}
+
 void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 
 	int headerIndex = roomNumber;
@@ -542,19 +550,14 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 	talkFile.close();
 }
 
-ScalingParams RoomManager::loadScalingParams(Common::File *roomFile, int roomOffset) {
-	uint32_t pair10_offset_pos = roomOffset + (10 * 8);
-	roomFile->seek(pair10_offset_pos, SEEK_SET);
-	// roomFile->skip(4);
-	uint32_t pair10_data_offset = roomFile->readUint32LE();
-	uint32_t pair10_size = roomFile->readUint32LE();
-	uint32_t scalingParamsOffset = pair10_data_offset + 0x214;
+ScalingParams RoomManager::loadScalingParams(byte *data, size_t size) {
+
+	uint32_t scalingParamsOffset = 0x214;
 
-	roomFile->seek(scalingParamsOffset, SEEK_SET);
 	ScalingParams scalingParams;
-	scalingParams.yThreshold = roomFile->readSint16LE();
-	scalingParams.scaleDivisor = roomFile->readByte();
-	scalingParams.scaleMode = roomFile->readByte();
+	scalingParams.yThreshold = READ_LE_INT16(data + scalingParamsOffset);
+	scalingParams.scaleDivisor = data[scalingParamsOffset + 2];
+	scalingParams.scaleMode = data[scalingParamsOffset + 3];
 	return scalingParams;
 }
 
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 8809aaeddb2..462f0e96279 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -73,13 +73,16 @@ public:
 	size_t _conversationDataSize = 0;
 
 private:
-	Common::Array<Sprite> loadRoomAnimations(Common::File *roomFile, int roomOffset);
-	Common::Array<HotSpot> loadHotspots(Common::File *roomFile, int roomOffset);
-	Common::Array<Exit> loadExits(Common::File *roomFile, int roomOffset);
-	Common::Array<WalkBox> loadWalkboxes(Common::File *roomFile, int roomOffset);
+	void loadAnimationPixelData(Common::File *roomFile,  int roomOffset, byte *&buffer, size_t &outSize);
+	Common::Array<Sprite> loadRoomAnimations(byte *pixelData, size_t pixelDataSize, byte *data, size_t size);
+	Common::Array<HotSpot> loadHotspots(byte *data, size_t size);
+	Common::Array<Exit> loadExits(byte *data, size_t size);
+	ScalingParams loadScalingParams(byte *data, size_t size);
+	Common::Array<WalkBox> loadWalkboxes(byte *data, size_t size);
 	Common::Array<Description> loadRoomTexts(Common::File *roomFile, int roomOffset);
 
-	ScalingParams loadScalingParams(Common::File *roomFile, int roomOffset);
+	void resetRoomDefaults(byte *data, size_t size);
+
 	byte *loadShadowMap(int roomNumber);
 	void loadRemaps(int roomNumber);
 	Common::StringArray loadRoomNames();
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 281ba6d9304..604dae90c47 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -99,12 +99,11 @@ void SoundManager::playSound(SonidoFile sound, int volume) {
 		delete[] data;
 		return;
 	}
-
 }
 
-void SoundManager::playSound(byte *soundData, uint32 size,int volume) {
+void SoundManager::playSound(byte *soundData, uint32 size, int volume) {
 	Audio::AudioStream *stream = Audio::makeRawStream(soundData, size, 11025, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
-	 if (stream) {
+	if (stream) {
 		int channel = findFreeChannel();
 		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, volume, 0, DisposeAfterUse::YES);
 	}
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index a0ad426faa9..8564be3c1ee 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -73,7 +73,7 @@ void VideoManager::playIntro() {
 			if (_chrono->_gameTick && _chrono->getFrameCount() % 2 == 0) {
 				ChunkHeader chunk;
 				readChunk(videoFile, chunk);
-
+				debug("Read chunk type %d at frame %d", chunk.chunkType, frameCounter);
 				switch (chunk.chunkType) {
 				case 1:
 				case 2:
@@ -98,8 +98,6 @@ void VideoManager::playIntro() {
 					byte *voiceBuffer = new byte[voiceData.length];
 					_introSndFile.read(voiceBuffer, voiceData.length);
 					_sound->playSound(voiceBuffer, voiceData.length);
-					// g_system->getSoundManager()->playSoundBuffer(voiceBuffer, voiceData.length, SOUND_FORMAT_PCM8, 11025);
-					// delete[] voiceBuffer;
 				}
 
 				Subtitle *subtitle = getSubtitleForFrame(frameCounter);


Commit: bd027e3ecf98f75c8456553de6ca128a0b0ac41c
    https://github.com/scummvm/scummvm/commit/bd027e3ecf98f75c8456553de6ca128a0b0ac41c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:40+02:00

Commit Message:
PELROCK: Reset room states before loading

Changed paths:
    engines/pelrock/room.cpp
    engines/pelrock/room.h


diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 4d1397a181e..fccc5533045 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -43,6 +43,7 @@ RoomManager::~RoomManager() {
 		delete[] _pixelsShadows;
 		_pixelsShadows = nullptr;
 	}
+	delete[] _resetData;
 }
 
 void RoomManager::getPalette(Common::File *roomFile, int roomOffset, byte *palette) {
@@ -209,7 +210,6 @@ Common::Array<Exit> RoomManager::loadExits(byte *data, size_t size) {
 Common::Array<HotSpot> RoomManager::loadHotspots(byte *data, size_t size) {
 	int pair10StartingPos = 0x47a;
 
-
 	byte hotspot_count = data[pair10StartingPos];
 	int hotspotsDataStart = pair10StartingPos + 2;
 	Common::Array<HotSpot> hotspots;
@@ -245,8 +245,7 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	roomFile->read(pair10, pair10size);
 
 	// The user's game can be in any state so we reset to defaults first
-	resetRoomDefaults(pair10, pair10size);
-
+	resetRoomDefaults(roomNumber, pair10, pair10size);
 
 	byte *pic = nullptr;
 	size_t pixelDataSize = 0;
@@ -258,7 +257,6 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	Common::Array<Exit> exits = loadExits(pair10, pair10size);
 	ScalingParams scalingParams = loadScalingParams(pair10, pair10size);
 
-
 	Common::Array<Description> descriptions = loadRoomTexts(roomFile, roomOffset);
 	Common::Array<HotSpot> hotspots;
 	for (int i = 0; i < anims.size(); i++) {
@@ -320,13 +318,24 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	delete[] pair10;
 }
 
+void RoomManager::init() {
+	Common::File alfred8;
+	if (!alfred8.open("ALFRED.8")) {
+		error("Couldnt find file ALFRED.8");
+	}
+	// _resetDataSize = alfred8.size();
+	// _resetData = new byte[_resetDataSize];
+	// alfred8.read(_resetData, _resetDataSize);
+	// alfred8.close();
+}
+
 void RoomManager::loadAnimationPixelData(Common::File *roomFile, int roomOffset, byte *&buffer, size_t &outSize) {
 	uint32_t pair_offset = roomOffset + (8 * 8);
 	roomFile->seek(pair_offset, SEEK_SET);
 	uint32_t offset = roomFile->readUint32LE();
 	uint32_t size = roomFile->readUint32LE();
 
-	byte  *pixelData = new byte[size];
+	byte *pixelData = new byte[size];
 	roomFile->seek(offset, SEEK_SET);
 	roomFile->read(pixelData, size);
 	if (offset > 0 && size > 0) {
@@ -379,7 +388,7 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 			anim.animData = new byte *[anim.nframes];
 			if (anim.w > 0 && anim.h > 0 && anim.nframes > 0) {
 				uint32_t needed = anim.w * anim.h * anim.nframes;
-				for(int i = 0; i < anim.nframes; i++) {
+				for (int i = 0; i < anim.nframes; i++) {
 					anim.animData[i] = new byte[anim.w * anim.h];
 					extractSingleFrame(pixelData + picOffset, anim.animData[i], i, anim.w, anim.h);
 				}
@@ -480,8 +489,33 @@ Common::Array<Description> RoomManager::loadRoomTexts(Common::File *roomFile, in
 	return descriptions;
 }
 
-void RoomManager::resetRoomDefaults(byte *data, size_t size) {
-
+void RoomManager::resetRoomDefaults(byte room, byte *&data, size_t size) {
+	Common::File alfred8;
+	if (!alfred8.open("ALFRED.8")) {
+		error("Couldnt find file ALFRED.8");
+	}
+	bool roomDone = false;
+	while (!alfred8.eos() && !roomDone) {
+		ResetEntry entry;
+		entry.room = alfred8.readUint16LE();
+		entry.offset = alfred8.readUint16LE();
+		entry.dataSize = alfred8.readByte();
+		entry.data = new byte[entry.dataSize];
+		alfred8.read(entry.data, entry.dataSize);
+		if (room < entry.room) {
+			// We've passed the room we care about
+			roomDone = true;
+			break;
+		}
+		if (room > entry.room) {
+			// Not the room we care about, skip
+			continue;
+		}
+		debug("Resetting room %d data at offset %d, size %d", entry.room, entry.offset, entry.dataSize);
+		Common::copy(entry.data, entry.data + entry.dataSize, data + entry.offset);
+		// delete[] entry.data;
+	}
+	alfred8.close();
 }
 
 void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 462f0e96279..5da0a7b0659 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -31,6 +31,20 @@ namespace Pelrock {
 
 static const int kNumSfxPerRoom = 8;
 
+struct ResetEntry {
+	uint16 room;
+	uint16 offset;
+	byte dataSize;
+	byte *data = nullptr;
+
+	~ResetEntry() {
+		if (data != nullptr) {
+			delete[] data;
+			data = nullptr;
+		}
+	}
+};
+
 class RoomManager {
 public:
 	RoomManager();
@@ -73,6 +87,7 @@ public:
 	size_t _conversationDataSize = 0;
 
 private:
+	void init();
 	void loadAnimationPixelData(Common::File *roomFile,  int roomOffset, byte *&buffer, size_t &outSize);
 	Common::Array<Sprite> loadRoomAnimations(byte *pixelData, size_t pixelDataSize, byte *data, size_t size);
 	Common::Array<HotSpot> loadHotspots(byte *data, size_t size);
@@ -81,7 +96,7 @@ private:
 	Common::Array<WalkBox> loadWalkboxes(byte *data, size_t size);
 	Common::Array<Description> loadRoomTexts(Common::File *roomFile, int roomOffset);
 
-	void resetRoomDefaults(byte *data, size_t size);
+	void resetRoomDefaults(byte room, byte *&data, size_t size);
 
 	byte *loadShadowMap(int roomNumber);
 	void loadRemaps(int roomNumber);
@@ -90,6 +105,7 @@ private:
 	Common::Array<byte> loadRoomSfx(Common::File *roomFile, int roomOffset);
 
 	Common::StringArray _roomNames;
+	byte *_resetData = nullptr;
 };
 
 } // End of namespace Pelrock


Commit: 67b294ed5b2a2d45417eb71c7fa9c261dab5585e
    https://github.com/scummvm/scummvm/commit/67b294ed5b2a2d45417eb71c7fa9c261dab5585e
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:40+02:00

Commit Message:
PELROCK: Video timing

Changed paths:
    engines/pelrock/video/video.cpp


diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index 8564be3c1ee..a4f726a8478 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -69,8 +69,9 @@ void VideoManager::playIntro() {
 		while (!videoExitFlag && !g_engine->shouldQuit() && _events->_lastKeyEvent != Common::KEYCODE_ESCAPE) {
 			_chrono->updateChrono();
 			_events->pollEvent();
-
-			if (_chrono->_gameTick && _chrono->getFrameCount() % 2 == 0) {
+			Subtitle *subtitle = getSubtitleForFrame(frameCounter);
+			int frameSkip = subtitle != nullptr ? 4 : 2;
+			if (_chrono->_gameTick && _chrono->getFrameCount() % frameSkip == 0) {
 				ChunkHeader chunk;
 				readChunk(videoFile, chunk);
 				debug("Read chunk type %d at frame %d", chunk.chunkType, frameCounter);
@@ -91,6 +92,14 @@ void VideoManager::playIntro() {
 				}
 
 				if (_voiceEffect.contains(frameCounter)) {
+					// Wait for any playing voice to finish before starting new one
+					while (_sound->isPlaying()) {
+						_events->pollEvent();
+						g_system->delayMillis(10);
+						if (g_engine->shouldQuit() || _events->_lastKeyEvent == Common::KEYCODE_ESCAPE) {
+							break;
+						}
+					}
 					Voice voice = _voiceEffect[frameCounter];
 					debug("Playing voice effect: '%s'", voice.filename.c_str());
 					VoiceData voiceData = _sounds[voice.filename];
@@ -100,7 +109,7 @@ void VideoManager::playIntro() {
 					_sound->playSound(voiceBuffer, voiceData.length);
 				}
 
-				Subtitle *subtitle = getSubtitleForFrame(frameCounter);
+
 				if (subtitle != nullptr) {
 					Common::StringArray lines = _dialog->wordWrap(subtitle->text)[0];
 


Commit: fc798c1b53046915de74bdd3ff7bf415aee9fc1b
    https://github.com/scummvm/scummvm/commit/fc798c1b53046915de74bdd3ff7bf415aee9fc1b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:40+02:00

Commit Message:
PELROCK: Saves room changes in memory gamestate

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/menu.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 69724377a5f..5582eef7204 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -20,10 +20,10 @@
  */
 
 #include "pelrock/actions.h"
+#include "pelrock.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/util.h"
-#include "pelrock.h"
 
 namespace Pelrock {
 
@@ -35,7 +35,7 @@ const ActionEntry actionTable[] = {
 	{268, CLOSE, &PelrockEngine::closeDoor},
 	{3, PICKUP, &PelrockEngine::pickUpPhoto},
 	{0, PICKUP, &PelrockEngine::pickYellowBook}, // Generic pickup for other items
-    {4, PICKUP, &PelrockEngine::pickUpBrick}, // Brick
+	{4, PICKUP, &PelrockEngine::pickUpBrick},    // Brick
 
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOp}, // Generic pickup action
@@ -57,16 +57,17 @@ void PelrockEngine::openDrawer(HotSpot *hotspot) {
 		return;
 	}
 	_room->addSticker(_res->getSticker(91));
-	hotspot->isEnabled = false;
+	// TODO: Check if we need to disable the hotspot
+	_room->disableHotspot(hotspot);
 }
 
 void PelrockEngine::closeDrawer(HotSpot *hotspot) {
 	_room->removeSticker(91);
-	hotspot->isEnabled = true;
+	_room->enableHotspot(hotspot);
 }
 
 void PelrockEngine::openDoor(HotSpot *hotspot) {
-	_room->addSticker(_res->getSticker(93));
+	_room->addSticker(_res->getSticker(93), false);
 	_room->_currentRoomExits[0].isEnabled = true;
 }
 
@@ -91,11 +92,11 @@ void PelrockEngine::pickUpAndDisable(HotSpot *hotspot) {
 		g_system->delayMillis(10);
 	}
 	_inventoryItems.push_back(hotspot->extra);
-	hotspot->isEnabled = false;
+	_room->disableHotspot(hotspot);
 }
 
 void PelrockEngine::pickUpPhoto(HotSpot *hotspot) {
-	_room->findHotspotByExtra(261)->isEnabled = true;
+	_room->enableHotspot(_room->findHotspotByExtra(261));
 }
 
 void PelrockEngine::pickYellowBook(HotSpot *hotspot) {
@@ -103,7 +104,7 @@ void PelrockEngine::pickYellowBook(HotSpot *hotspot) {
 }
 
 void PelrockEngine::pickUpBrick(HotSpot *hotspot) {
-    _room->addSticker(_res->getSticker(133));
+	_room->addSticker(_res->getSticker(133));
 }
 
 void PelrockEngine::noOp(HotSpot *hotspot) {
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 6d1aa8f7b3e..78db638a13d 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -135,7 +135,7 @@ void MenuManager::menuLoop() {
 		_events->_leftMouseClicked = false;
 	} else if (_events->_rightMouseClicked) {
 		g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
-		g_engine->stateGame = GAME;
+		g_engine->_state.stateGame = GAME;
 		_events->_rightMouseClicked = false;
 		tearDown();
 	} else {
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index b6bcfeccc17..a2f259e6424 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -115,22 +115,22 @@ Common::Error PelrockEngine::run() {
 	Graphics::FrameLimiter limiter(g_system, 60);
 
 	if (shouldPlayIntro == false) {
-		stateGame = GAME;
+		_state.stateGame = GAME;
 		// stateGame = SETTINGS;
 	} else {
-		stateGame = INTRO;
+		_state.stateGame = INTRO;
 		_videoManager->playIntro();
-		stateGame = GAME;
+		_state.stateGame = GAME;
 	}
 	if (!shouldQuit())
 		init();
 
 	while (!shouldQuit()) {
 
-		if (stateGame == SETTINGS) {
+		if (_state.stateGame == SETTINGS) {
 			changeCursor(DEFAULT);
 			_menu->menuLoop();
-		} else if (stateGame == GAME) {
+		} else if (_state.stateGame == GAME) {
 			gameLoop();
 		}
 		_screen->update();
@@ -329,7 +329,7 @@ void PelrockEngine::checkMouse() {
 	} else if (_events->_rightMouseClicked) {
 		g_system->getPaletteManager()->setPalette(_menu->_mainMenuPalette, 0, 256);
 		_events->_rightMouseClicked = false;
-		stateGame = SETTINGS;
+		_state.stateGame = SETTINGS;
 	}
 	checkMouseHover();
 }
@@ -411,8 +411,13 @@ void PelrockEngine::paintDebugLayer() {
 }
 
 void PelrockEngine::placeStickers() {
-	for (int i = 0; i < _room->_currentRoomStickers.size(); i++) {
-		Sticker sticker = _room->_currentRoomStickers[i];
+	for (int i = 0; i < _state.roomStickers[_room->_currentRoomNumber].size(); i++) {
+		Sticker sticker = _state.roomStickers[_room->_currentRoomNumber][i];
+		placeSticker(sticker);
+	}
+	// also place temporary stickers
+	for (int i = 0; i < _room->_transientStickers.size(); i++) {
+		Sticker sticker = _room->_transientStickers[i];
 		placeSticker(sticker);
 	}
 }
@@ -830,7 +835,6 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 
 void PelrockEngine::checkLongMouseClick(int x, int y) {
 	int hotspotIndex = isHotspotUnder(_events->_mouseX, _events->_mouseY);
-
 	if (hotspotIndex != -1 && !_actionPopupState.isActive) {
 
 		_actionPopupState.x = alfredState.x + kAlfredFrameWidth / 2 - kBalloonWidth / 2;
@@ -990,7 +994,7 @@ int PelrockEngine::isHotspotUnder(int x, int y) {
 				return spriteUnder ? i : -1;
 			}
 
-			return i;
+			return hotspot.index;
 		}
 	}
 	return -1;
@@ -1000,9 +1004,7 @@ Exit *PelrockEngine::isExitUnder(int x, int y) {
 	for (int i = 0; i < _room->_currentRoomExits.size(); i++) {
 		Exit exit = _room->_currentRoomExits[i];
 		if (x >= exit.x && x <= (exit.x + exit.w) &&
-			y >= exit.y && y <= (exit.y + exit.h)
-			&& exit.isEnabled
-		) {
+			y >= exit.y && y <= (exit.y + exit.h) && exit.isEnabled) {
 			return &(_room->_currentRoomExits[i]);
 		}
 	}
@@ -1326,8 +1328,6 @@ void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 		_sound->stopMusic();
 	}
 
-	_room->_currentRoomNumber = number;
-
 	_screen->markAllDirty();
 	_screen->update();
 	roomFile.close();
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 9484d292a29..74ac6c087ea 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -145,6 +145,7 @@ private:
 	bool gameInitialized = false;
 	bool screenReady = false;
 
+
 	// int prevDirX = 0;
 	// int prevDirY = 0;
 	// Common::String objectToShow = "";
@@ -162,7 +163,8 @@ public:
 	RoomManager *_room = nullptr;
 	AlfredState alfredState;
 	byte *_compositeBuffer = nullptr; // Working composition buffer
-	GameState stateGame = INTRO;
+
+	GameStateData _state;
 
 	SmallFont *_smallFont = nullptr;
 	LargeFont *_largeFont = nullptr;
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index fccc5533045..c64d7394728 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -94,31 +94,64 @@ void RoomManager::getBackground(Common::File *roomFile, int roomOffset, byte *ba
 	}
 }
 
-void RoomManager::addSticker(Sticker sticker) {
-	_currentRoomStickers.push_back(sticker);
+void RoomManager::addSticker(Sticker sticker, bool persist) {
+	if (persist)
+		g_engine->_state.roomStickers[_currentRoomNumber].push_back(sticker);
+	else
+		_transientStickers.push_back(sticker);
 }
 
 void RoomManager::removeSticker(int stickerIndex) {
 	int index = -1;
-	for (int i = 0; i < _currentRoomStickers.size(); i++) {
-		if (_currentRoomStickers[i].stickerIndex == stickerIndex) {
+	for (int i = 0; i < g_engine->_state.roomStickers[_currentRoomNumber].size(); i++) {
+		if (g_engine->_state.roomStickers[_currentRoomNumber][i].stickerIndex == stickerIndex) {
 			index = i;
 			break;
 		}
 	}
-	if (index != -1 && index < _currentRoomStickers.size())
-		_currentRoomStickers.remove_at(index);
+	if (index != -1 && index < g_engine->_state.roomStickers[_currentRoomNumber].size())
+		g_engine->_state.roomStickers[_currentRoomNumber].remove_at(index);
 }
 
 bool RoomManager::hasSticker(int index) {
-	for (int i = 0; i < _currentRoomStickers.size(); i++) {
-		if (_currentRoomStickers[i].stickerIndex == index) {
+	for (int i = 0; i < g_engine->_state.roomStickers[_currentRoomNumber].size(); i++) {
+		if (g_engine->_state.roomStickers[_currentRoomNumber][i].stickerIndex == index) {
 			return true;
 		}
 	}
 	return false;
 }
 
+void RoomManager::changeExit(Exit exit) {
+	g_engine->_state.roomExitChanges[_currentRoomNumber].push_back({_currentRoomNumber, exit.index, exit});
+}
+
+void RoomManager::changeWalkBox(WalkBox walkbox) {
+	g_engine->_state.roomWalkBoxChanges[_currentRoomNumber].push_back({_currentRoomNumber, walkbox.index, walkbox});
+}
+
+void RoomManager::changeHotSpot(HotSpot hotspot) {
+	g_engine->_state.roomHotSpotChanges[_currentRoomNumber].push_back({_currentRoomNumber, hotspot.innerIndex, hotspot});
+}
+
+void RoomManager::enableHotspot(HotSpot *hotspot, bool persist) {
+	hotspot->isEnabled = true;
+	if (persist) {
+		changeHotSpot(*hotspot);
+	}
+}
+
+void RoomManager::disableHotspot(HotSpot *hotspot, bool persist) {
+	hotspot->isEnabled = false;
+	if (persist) {
+		changeHotSpot(*hotspot);
+	}
+}
+
+void RoomManager::addWalkbox(WalkBox walkbox) {
+	g_engine->_state.roomWalkBoxChanges[_currentRoomNumber].push_back({_currentRoomNumber, walkbox.index, walkbox});
+}
+
 HotSpot *RoomManager::findHotspotByExtra(uint16 extra) {
 	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
 		if (_currentRoomHotspots[i].extra == extra) {
@@ -170,7 +203,23 @@ Common::Array<Exit> RoomManager::loadExits(byte *data, size_t size) {
 	int exitDataOffset = 0x1BF;
 	for (int i = 0; i < exitCount; i++) {
 		int exitOffset = exitDataOffset + i * 14;
+		bool isChanged = false;
+		if (g_engine->_state.roomExitChanges.contains(_currentRoomNumber)) {
+			// if the exit has been changed, load the changed version
+			for (int j = 0; j < g_engine->_state.roomExitChanges[_currentRoomNumber].size(); j++) {
+				if (g_engine->_state.roomExitChanges[_currentRoomNumber][j].exitIndex == i) {
+					exits.push_back(g_engine->_state.roomExitChanges[_currentRoomNumber][j].exit);
+					isChanged = true;
+					break;
+				}
+			}
+		}
+
+		if (isChanged)
+			continue;
+
 		Exit exit;
+		exit.index = i;
 		exit.targetRoom = READ_LE_INT16(data + exitOffset);
 		exit.isEnabled = data[exitOffset + 2];
 		exit.x = READ_LE_INT16(data + exitOffset + 3);
@@ -200,9 +249,9 @@ Common::Array<Exit> RoomManager::loadExits(byte *data, size_t size) {
 		}
 
 		exits.push_back(exit);
-		debug("Exit %d: targetRoom=%d isEnabled=%d x=%d y=%d w=%d h=%d targetX=%d targetY=%d dir=%d",
-			  i, exit.targetRoom, exit.isEnabled, exit.x, exit.y, exit.w, exit.h,
-			  exit.targetX, exit.targetY, exit.dir);
+		// debug("Exit %d: targetRoom=%d isEnabled=%d x=%d y=%d w=%d h=%d targetX=%d targetY=%d dir=%d",
+		// 	  i, exit.targetRoom, exit.isEnabled, exit.x, exit.y, exit.w, exit.h,
+		// 	  exit.targetX, exit.targetY, exit.dir);
 	}
 	return exits;
 }
@@ -216,6 +265,21 @@ Common::Array<HotSpot> RoomManager::loadHotspots(byte *data, size_t size) {
 	for (int i = 0; i < hotspot_count; i++) {
 		int hotspotOffset = hotspotsDataStart + i * 9;
 		HotSpot spot;
+		spot.innerIndex = i;
+		spot.index = i;
+		bool isChanged = false;
+		if (g_engine->_state.roomHotSpotChanges.contains(_currentRoomNumber)) {
+			// if the hotspot has been changed, load the changed version
+			for (int j = 0; j < g_engine->_state.roomHotSpotChanges[_currentRoomNumber].size(); j++) {
+				if (g_engine->_state.roomHotSpotChanges[_currentRoomNumber][j].hotspotIndex == spot.innerIndex) {
+					hotspots.push_back(g_engine->_state.roomHotSpotChanges[_currentRoomNumber][j].hotspot);
+					isChanged = true;
+					break;
+				}
+			}
+		}
+		if (isChanged)
+			continue;
 		spot.actionFlags = data[hotspotOffset];
 		spot.x = READ_LE_INT16(data + hotspotOffset + 1);
 		spot.y = READ_LE_INT16(data + hotspotOffset + 3);
@@ -223,15 +287,18 @@ Common::Array<HotSpot> RoomManager::loadHotspots(byte *data, size_t size) {
 		spot.h = data[hotspotOffset + 6];
 		spot.isSprite = false;
 		spot.extra = READ_LE_INT16(data + hotspotOffset + 7);
-		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d", i, spot.actionFlags, spot.x, spot.y, spot.w, spot.h, spot.extra);
+		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d, isEnabled=%d", spot.innerIndex, spot.actionFlags, spot.x, spot.y, spot.w, spot.h, spot.extra, spot.isEnabled);
 		hotspots.push_back(spot);
 	}
+
 	return hotspots;
 }
 
 void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
+
 	uint32_t outPos = 0;
-	_currentRoomStickers.clear();
+	_transientStickers.clear();
+	_currentRoomNumber = roomNumber;
 	int roomOffset = roomNumber * kRoomStructSize;
 
 	uint32_t pair10offset = roomOffset + (10 * 8);
@@ -423,8 +490,22 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(byte *data, size_t size) {
 		int16 w = READ_LE_INT16(data + boxOffset + 4);
 		int16 h = READ_LE_INT16(data + boxOffset + 6);
 		byte flags = data[boxOffset + 8];
-		debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
+		// debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
 		WalkBox box;
+		box.index = i;
+		bool isChanged = false;
+		if (g_engine->_state.roomWalkBoxChanges.contains(_currentRoomNumber)) {
+			// if the walkbox has been changed, load the changed version
+			for (int j = 0; j < g_engine->_state.roomWalkBoxChanges[_currentRoomNumber].size(); j++) {
+				if (g_engine->_state.roomWalkBoxChanges[_currentRoomNumber][j].walkboxIndex == i) {
+					walkboxes.push_back(g_engine->_state.roomWalkBoxChanges[_currentRoomNumber][j].walkbox);
+					isChanged = true;
+					break;
+				}
+			}
+		}
+		if (isChanged)
+			continue;
 		box.x = x1;
 		box.y = y1;
 		box.w = w;
@@ -432,6 +513,23 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(byte *data, size_t size) {
 		box.flags = flags;
 		walkboxes.push_back(box);
 	}
+
+	if (g_engine->_state.roomWalkBoxChanges.contains(_currentRoomNumber)) {
+		// Add any new walkboxes that were added
+		for (int j = 0; j < g_engine->_state.roomWalkBoxChanges[_currentRoomNumber].size(); j++) {
+			bool found = false;
+			for (int i = 0; i < walkboxes.size(); i++) {
+				if (g_engine->_state.roomWalkBoxChanges[_currentRoomNumber][j].walkboxIndex == walkboxes[i].index) {
+					found = true;
+					break;
+				}
+			}
+			if (!found) {
+				walkboxes.push_back(g_engine->_state.roomWalkBoxChanges[_currentRoomNumber][j].walkbox);
+			}
+		}
+	}
+
 	return walkboxes;
 }
 
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 5da0a7b0659..c3f2b9ed8e8 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -53,9 +53,19 @@ public:
 	void loadRoomTalkingAnimations(int roomNumber);
 	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
-	void addSticker(Sticker sticker);
+	void addSticker(Sticker sticker, bool persist = true);
 	void removeSticker(int index);
 	bool hasSticker(int index);
+	void changeExit(Exit exit);
+	void changeWalkBox(WalkBox walkbox);
+	void changeHotSpot(HotSpot hotspot);
+	/**
+	 * Utility function to enable or disable a hotspot, with an option to persist the change.
+	 */
+	void enableHotspot(HotSpot *hotspot, bool persist = true);
+	void disableHotspot(HotSpot *hotspot, bool persist = true);
+	void addWalkbox(WalkBox walkbox);
+
 	HotSpot *findHotspotByExtra(uint16 extra);
 	PaletteAnim *getPaletteAnimForRoom(int roomNumber);
 
@@ -82,13 +92,13 @@ public:
 	byte _musicTrack = 0;
 	Common::Array<byte> _roomSfx;
 	PaletteAnim *_currentPaletteAnim = nullptr;
-	Common::Array<Sticker> _currentRoomStickers;
 	byte *_conversationData = nullptr;
 	size_t _conversationDataSize = 0;
+	Common::Array<Sticker> _transientStickers;
 
 private:
 	void init();
-	void loadAnimationPixelData(Common::File *roomFile,  int roomOffset, byte *&buffer, size_t &outSize);
+	void loadAnimationPixelData(Common::File *roomFile, int roomOffset, byte *&buffer, size_t &outSize);
 	Common::Array<Sprite> loadRoomAnimations(byte *pixelData, size_t pixelDataSize, byte *data, size_t size);
 	Common::Array<HotSpot> loadHotspots(byte *data, size_t size);
 	Common::Array<Exit> loadExits(byte *data, size_t size);
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index d53fa540694..dcea3d58394 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -170,6 +170,7 @@ struct Anim {
 };
 
 struct Exit {
+	byte index;
 	int16 x;
 	int16 y;
 	byte w;
@@ -183,7 +184,7 @@ struct Exit {
 };
 
 struct Sprite {
-	int index; // number of the animation in the rooms
+	byte index; // number of the animation in the rooms
 	byte type;
 	int16 x;      // 0
 	int16 y;      // 2
@@ -201,7 +202,8 @@ struct Sprite {
 };
 
 struct HotSpot {
-	int index;
+	byte index;
+	byte innerIndex;
 	int id;
 	int16 x;
 	int16 y;
@@ -243,48 +245,6 @@ struct TalkingAnims {
 	byte **animB = nullptr;
 };
 
-struct ConversationElement {
-	enum Type {
-		DIALOGUE,
-		CHOICE_MARKER,
-		END_CONV,
-		END_BRANCH
-	} type;
-
-	Common::String speaker;
-	byte speakerId;
-	Common::String text;
-	int choiceIndex;
-	bool isRealChoice;
-
-	ConversationElement() : type(DIALOGUE), choiceIndex(-1), isRealChoice(false) {}
-};
-
-struct ConversationNode {
-	enum NodeType {
-		ROOT,
-		CHOICE,
-		RESPONSE
-	} type;
-
-	Common::String text;
-	Common::String speaker;
-	byte speakerId;
-	int choiceIndex;
-	bool terminated;
-
-	Common::Array<ConversationNode> choices;
-	Common::Array<ConversationNode> responses;
-	Common::Array<ConversationNode> subchoices;
-
-	ConversationNode() : type(ROOT), choiceIndex(-1), terminated(false) {}
-};
-
-struct StackEntry {
-	ConversationNode *node;
-	int index;
-};
-
 struct Description {
 	byte itemId;
 	byte index;
@@ -294,6 +254,7 @@ struct Description {
 };
 
 struct WalkBox {
+	byte index;
 	int16 x;
 	int16 y;
 	int16 w;
@@ -330,6 +291,24 @@ enum GameState {
 	INTRO = 106,
 };
 
+struct HotSpotChange {
+	byte roomNumber;
+	byte hotspotIndex;
+	HotSpot hotspot;
+};
+
+struct ExitChange {
+	byte roomNumber;
+	byte exitIndex;
+	Exit exit;
+};
+
+struct WalkBoxChange {
+	byte roomNumber;
+	byte walkboxIndex;
+	WalkBox walkbox;
+};
+
 struct InventoryObject {
 	byte index;
 	Common::String description;
@@ -380,6 +359,15 @@ struct PaletteAnim {
 	byte curFrameCount = 0;
 };
 
+
+struct GameStateData {
+	GameState stateGame = INTRO;
+	Common::HashMap<byte, Common::Array<Sticker>> roomStickers;
+	Common::HashMap<byte, Common::Array<ExitChange>> roomExitChanges;
+	Common::HashMap<byte, Common::Array<WalkBoxChange>> roomWalkBoxChanges;
+	Common::HashMap<byte, Common::Array<HotSpotChange>> roomHotSpotChanges;
+};
+
 } // End of namespace Pelrock
 
 #endif


Commit: 6da8f84da294b7de54eb99bdd7bdc049f439bf38
    https://github.com/scummvm/scummvm/commit/6da8f84da294b7de54eb99bdd7bdc049f439bf38
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:41+02:00

Commit Message:
PELROCK: Refactor dialog handler, reset and skip disabled conversations

Changed paths:
  A engines/pelrock/saveload.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/graphics.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index e5429d4c61f..046bf05eac2 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -108,7 +108,7 @@ uint32 DialogManager::readTextBlock(
 		}
 
 		// Regular text - does not need decoding
-		if (b >= 0x20 && b <= 0x83) {
+		if (b >= CHAR_SPACE && b <= 0x83) {
 			outText += b;
 		}
 		pos++;
@@ -187,14 +187,14 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		int yPos = 0;
 
 		if (speakerId == ALFRED_COLOR) {
-			if(g_engine->alfredState.animState != ALFRED_TALKING) {
+			if (g_engine->alfredState.animState != ALFRED_TALKING) {
 				g_engine->alfredState.setState(ALFRED_TALKING);
 			}
 			if (_curSprite != nullptr) {
 				_curSprite->isTalking = false;
 			}
 			// Offset X position for Alfred to avoid overlapping with his sprite
-			xPos = g_engine->alfredState.x - maxWidth / 2; //+ kAlfredFrameWidth / 2 - maxWidth / 2;
+			xPos = g_engine->alfredState.x - maxWidth / 2;                //+ kAlfredFrameWidth / 2 - maxWidth / 2;
 			yPos = g_engine->alfredState.y - kAlfredFrameHeight - height; // Above sprite, adjust for line
 		} else {
 			g_engine->alfredState.setState(ALFRED_IDLE);
@@ -220,8 +220,8 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		_screen->transBlitFrom(s, s.getRect(), Common::Point(xPos, yPos), 255);
 		drawPos(_screen, xPos, yPos, speakerId);
 		drawRect(_screen, xPos, yPos,
-			s.getRect().width(),
-			s.getRect().height(), speakerId);
+				 s.getRect().width(),
+				 s.getRect().height(), speakerId);
 		// Present to screen
 		_screen->markAllDirty();
 		_screen->update();
@@ -294,58 +294,6 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 	uint32 pos = startPos;
 	outChoices->clear();
 	int firstChoiceIndex = -1;
-	int choiceCount = 0;
-
-	// Find first choice marker
-	while (pos < dataSize) {
-		byte b = data[pos];
-
-		// Stop at end markers
-		if (b == CTRL_ALT_END_MARKER_1 || b == CTRL_END_BRANCH || b == CTRL_ALT_END_MARKER_3) {
-			break;
-		}
-
-		// Found first choice marker
-		if (b == CTRL_DIALOGUE_MARKER || b == CTRL_DIALOGUE_MARKER_2) {
-			if (pos + 1 < dataSize) {
-				firstChoiceIndex = data[pos + 1];
-				ChoiceOption opt;
-				opt.index = firstChoiceIndex;
-				opt.dataOffset = pos;
-				opt.isDisabled = false;
-
-				// Parse the choice text
-				uint32 textPos = pos + 4; // Skip marker + index + 2 speaker bytes
-				textPos += 2;
-				while (textPos < dataSize) {
-					byte tb = data[textPos];
-					if (tb == CTRL_END_TEXT || tb == CTRL_DIALOGUE_MARKER ||
-						tb == CTRL_DIALOGUE_MARKER_2 || tb == CTRL_END_BRANCH ||
-						tb == CTRL_ALT_END_MARKER_1) {
-						break;
-					}
-
-					if (tb >= 0x20 && tb < 0x7A) {
-						opt.text += (char)tb;
-					} else {
-						byte decoded = decodeChar(tb);
-						debug("Parsing choice char: 0x%02X, decoded: 0x%02X", tb, decoded);
-						if (decoded != tb || (decoded >= 0x20 && decoded <= 0xB4)) {
-							opt.text += (char)decoded;
-						}
-					}
-					textPos++;
-				}
-
-				outChoices->push_back(opt);
-				choiceCount = 1;
-				pos++;
-				break;
-			}
-		}
-
-		pos++;
-	}
 
 	// Scan for additional choices with SAME index
 	while (pos < dataSize) {
@@ -359,21 +307,25 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 		// Found a dialogue marker
 		if (b == CTRL_DIALOGUE_MARKER || b == CTRL_DIALOGUE_MARKER_2) {
 			if (pos + 1 < dataSize) {
+				if (firstChoiceIndex == -1) {
+					firstChoiceIndex = data[pos + 1];
+				}
 				int choiceIndex = data[pos + 1];
 
 				// Only collect choices with same index
 				if (choiceIndex == firstChoiceIndex) {
 					// Check if disabled
-					bool isDisabled = (b == CTRL_DISABLED_CHOICE);
 
 					ChoiceOption opt;
 					opt.index = choiceIndex;
 					opt.dataOffset = pos;
-					opt.isDisabled = isDisabled;
-
+					pos += 2; // Move past marker + index
+					if (data[pos] == CTRL_DISABLED_CHOICE) {
+						opt.isDisabled = true;
+					}
 					// Parse the choice text
 					uint32 textPos = pos + 4;
-					textPos += 2; // Skip marker + index + 2 speaker bytes
+					// textPos += 2; // Skip marker + index + 2 speaker bytes
 					while (textPos < dataSize) {
 						byte tb = data[textPos];
 						if (tb == CTRL_END_TEXT || tb == CTRL_DIALOGUE_MARKER ||
@@ -393,9 +345,8 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 						}
 						textPos++;
 					}
-
-					outChoices->push_back(opt);
-					choiceCount++;
+					if (!opt.isDisabled)
+						outChoices->push_back(opt);
 				} else if (choiceIndex < firstChoiceIndex) {
 					// Different choice index - stop scanning
 					break;
@@ -674,7 +625,7 @@ bool DialogManager::processColorAndTrim(Common::StringArray &lines, byte &speake
 }
 
 bool isEndMarker(char char_byte) {
-	return char_byte == CHAR_END_MARKER_1 || char_byte == CHAR_END_MARKER_2 || char_byte == CHAR_END_MARKER_3 || char_byte == CHAR_END_MARKER_4;
+	return char_byte == CTRL_END_TEXT || char_byte == CTRL_END_CONVERSATION || char_byte == CTRL_ACTION_TRIGGER || char_byte == CTRL_GO_BACK;
 }
 
 int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
@@ -694,7 +645,7 @@ int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
 	}
 	// Count ALL trailing spaces as part of this word
 	if (pos < text.size() && !isEnd) {
-		if (text[pos] == CHAR_END_MARKER_3) { // 0xF8 (-8) special case
+		if (text[pos] == CTRL_ACTION_TRIGGER) { // 0xF8 (-8) special case
 			wordLength += 3;
 		} else {
 			// Count all consecutive spaces
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 230dfa135ae..3113a1c6a68 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -34,18 +34,10 @@ namespace Pelrock {
 
 // Control character codes (negative values in signed char)
 #define CHAR_SPACE 0x20        /* ' ' */
-#define CHAR_END_MARKER_1 0xFD /* -3 (end of text marker) */
-#define CHAR_END_MARKER_2 0xF4 /* -0xC (alternate end marker) */
-#define CHAR_END_MARKER_3 0xF8 /* -8 (another end marker) */
-#define CHAR_END_MARKER_4 0xF0 /* -0x10 (another end marker) */
-#define CHAR_NEWLINE 0xF6      /* -10 (newline marker) */
-#define CHAR_PAGE_BREAK 0xF9   /* marker inserted when switching pages */
-
-// Conversation control bytes
 #define CTRL_SPEAKER_ID 0x08        /* Next byte is speaker ID (color) */
 #define CTRL_END_TEXT 0xFD          /* End of text segment */
 #define CTRL_TEXT_TERMINATOR 0xFC   /* Text terminator */
-#define CTRL_DIALOGUE_MARKER 0xFB   /* Choice marker */
+#define CTRL_DIALOGUE_MARKER 0xF1   /* Choice marker that sticks */
 #define CTRL_DISABLED_CHOICE 0xFA   /* Disabled choice marker */
 #define CTRL_PAGE_BREAK_CONV 0xF9   /* Page break in conversation */
 #define CTRL_ACTION_TRIGGER 0xF8    /* Action trigger */
@@ -53,7 +45,7 @@ namespace Pelrock {
 #define CTRL_LINE_CONTINUE 0xF6     /* Line continue/newline */
 #define CTRL_ALT_END_MARKER_1 0xF5  /* Alt end marker - do nothing */
 #define CTRL_END_CONVERSATION 0xF4  /* End conversation and disable option */
-#define CTRL_DIALOGUE_MARKER_2 0xF1 /* Alt choice marker that disappears */
+#define CTRL_DIALOGUE_MARKER_2 0xFB /* Alt choice marker that disappears */
 #define CTRL_GO_BACK 0xF0           /* Go back in conversation */
 #define CTRL_ALT_END_MARKER_2 0xEB  /* Alt end marker 2 */
 #define CTRL_ALT_END_MARKER_3 0xFE  /* Alt end marker 3 */
@@ -66,6 +58,7 @@ struct ChoiceOption {
 	Common::String text;
 	bool isDisabled;
 	uint32 dataOffset;
+	bool shouldDisableOnSelect = false;
 
 	ChoiceOption() : index(-1), isDisabled(false), dataOffset(0) {}
 };
diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index 355b773e5f9..218e52b9e39 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -38,7 +38,7 @@ Common::Point GraphicsManager::showOverlay(int height, byte *buf) {
 	for (int x = 0; x < 640; x++) {
 		for (int y = overlayY; y < 400; y++) {
 			int index = y * 640 + x;
-			buf[index] = g_engine->_room->overlayRemap[buf[index]];
+			buf[index] = g_engine->_room->paletteRemaps[1][buf[index]];
 		}
 	}
 	return Common::Point(overlayX, overlayY);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index a2f259e6424..bb0548d69b7 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -746,7 +746,7 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	if (shadeCharacter) {
 		for (int i = 0; i < finalWidth * finalHeight; i++) {
 			if (finalBuf[i] != 255) {
-				finalBuf[i] = _room->alfredRemap[finalBuf[i]];
+				finalBuf[i] = _room->paletteRemaps[0][finalBuf[i]];
 			}
 		}
 	}
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 4a9f1000d2c..781ecd6ea59 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -285,7 +285,7 @@ Common::Array<Common::Array<Common::String>> ResourceManager::processTextData(by
 	Common::StringArray lines;
 	Common::Array<Common::Array<Common::String>> texts;
 	while (pos < size) {
-		if (data[pos] == 0xFD) {
+		if (data[pos] == CTRL_END_TEXT) {
 			if (!desc.empty()) {
 
 				lines.push_back(desc);
@@ -300,7 +300,7 @@ Common::Array<Common::Array<Common::String>> ResourceManager::processTextData(by
 			pos++;
 			continue;
 		}
-		if (data[pos] == 0x08) {
+		if (data[pos] == CTRL_SPEAKER_ID) {
 			byte color = data[pos + 1];
 			desc.append(1, '@');
 			desc.append(1, color);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index c64d7394728..478d7d15089 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -33,12 +33,6 @@ RoomManager::RoomManager() {
 }
 
 RoomManager::~RoomManager() {
-	// delete[] _currentRoomHotspots;
-	// delete[] _currentRoomAnims;
-	// delete[] _currentRoomExits;
-	// delete[] _currentRoomWalkboxes;
-	// delete[] _currentRoomDescriptions;
-	// delete[] _currentRoomConversations;
 	if (_pixelsShadows != nullptr) {
 		delete[] _pixelsShadows;
 		_pixelsShadows = nullptr;
@@ -294,6 +288,36 @@ Common::Array<HotSpot> RoomManager::loadHotspots(byte *data, size_t size) {
 	return hotspots;
 }
 
+void RoomManager::resetConversationStates(byte roomNumber, byte *conversationData, size_t conversationDataSize) {
+	Common::File alfredB;
+	if (!alfredB.open("ALFRED.B")) {
+		debug("Could not open ALFRED.B to reset conversation states!");
+		return;
+	}
+	bool roomDone = false;
+	while (!alfredB.eos() && !roomDone) {
+		ResetEntry entry;
+		entry.room = alfredB.readUint16LE();
+		entry.offset = alfredB.readUint16LE();
+		entry.dataSize = alfredB.readByte();
+		entry.data = new byte[entry.dataSize];
+		alfredB.read(entry.data, entry.dataSize);
+		if (roomNumber < entry.room) {
+			// We've passed the room we care about
+			roomDone = true;
+			break;
+		}
+		if (roomNumber > entry.room) {
+			// Not the room we care about, skip
+			continue;
+		}
+		debug("Resetting room %d conversation data at offset %d, size %d", entry.room, entry.offset, entry.dataSize);
+		Common::copy(entry.data, entry.data + entry.dataSize, conversationData + entry.offset);
+		// delete[] entry.data;
+	}
+	alfredB.close();
+}
+
 void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 
 	uint32_t outPos = 0;
@@ -301,9 +325,20 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	_currentRoomNumber = roomNumber;
 	int roomOffset = roomNumber * kRoomStructSize;
 
+	// Pairs 0-7 are background data, already loaded
+
+	// Pair 8 - Animation Pixel Data
+	byte *pic = nullptr;
+	size_t pixelDataSize = 0;
+	loadAnimationPixelData(roomFile, roomOffset, pic, pixelDataSize);
+
+	// Pair 9 - Music and sound
+	_musicTrack = loadMusicTrackForRoom(roomFile, roomOffset);
+	_roomSfx = loadRoomSfx(roomFile, roomOffset);
+
+	// Pair 10
 	uint32_t pair10offset = roomOffset + (10 * 8);
 	roomFile->seek(pair10offset, SEEK_SET);
-	// roomFile->skip(4);
 	uint32_t pair10dataOffset = roomFile->readUint32LE();
 	uint32_t pair10size = roomFile->readUint32LE();
 
@@ -312,56 +347,38 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	roomFile->read(pair10, pair10size);
 
 	// The user's game can be in any state so we reset to defaults first
-	resetRoomDefaults(roomNumber, pair10, pair10size);
-
-	byte *pic = nullptr;
-	size_t pixelDataSize = 0;
-	loadAnimationPixelData(roomFile, roomOffset, pic, pixelDataSize);
+	resetMetadataDefaults(roomNumber, pair10, pair10size);
 
-	Common::Array<Sprite> anims = loadRoomAnimations(pic, pixelDataSize, pair10, pair10size);
+	Common::Array<Sprite> sprites = loadRoomAnimations(pic, pixelDataSize, pair10, pair10size);
 	Common::Array<HotSpot> staticHotspots = loadHotspots(pair10, pair10size);
-	Common::Array<WalkBox> walkboxes = loadWalkboxes(pair10, pair10size);
-	Common::Array<Exit> exits = loadExits(pair10, pair10size);
-	ScalingParams scalingParams = loadScalingParams(pair10, pair10size);
 
-	Common::Array<Description> descriptions = loadRoomTexts(roomFile, roomOffset);
-	Common::Array<HotSpot> hotspots;
-	for (int i = 0; i < anims.size(); i++) {
+	_currentRoomAnims = sprites;
+	_currentRoomHotspots = unifyHotspots(sprites, staticHotspots);
+	_currentRoomExits = loadExits(pair10, pair10size);
+	_currentRoomWalkboxes = loadWalkboxes(pair10, pair10size);
+	_scaleParams = loadScalingParams(pair10, pair10size);
 
-		HotSpot thisHotspot;
-		thisHotspot.index = i;
-		thisHotspot.x = anims[i].x;
-		thisHotspot.y = anims[i].y;
-		thisHotspot.w = anims[i].w;
-		thisHotspot.h = anims[i].h;
-		thisHotspot.extra = anims[i].extra;
-		thisHotspot.actionFlags = anims[i].actionFlags;
-		thisHotspot.isEnabled = !anims[i].isDisabled;
-		thisHotspot.isSprite = true;
-		thisHotspot.zOrder = anims[i].zOrder;
-		hotspots.push_back(thisHotspot);
-	}
+	// Pair 11 is the palette, already loaded
 
-	// debug("total descriptions = %d, anims = %d, hotspots = %d", descriptions.size(), anims.size(), staticHotspots.size());
-	for (int i = 0; i < staticHotspots.size(); i++) {
-		HotSpot hotspot = staticHotspots[i];
-		hotspot.index = anims.size() + i;
-		hotspots.push_back(hotspot);
-	}
+	// Pair 12 - Room Texts
+	uint32_t pair12offset = roomOffset + (12 * 8);
+	roomFile->seek(pair12offset, SEEK_SET);
+	uint32_t pair12dataOffset = roomFile->readUint32LE();
+	uint32_t pair12size = roomFile->readUint32LE();
 
-	byte *shadows = loadShadowMap(roomNumber);
-	loadRemaps(roomNumber);
+	byte *pair12 = new byte[pair12size];
+	roomFile->seek(pair12dataOffset, SEEK_SET);
+	roomFile->read(pair12, pair12size);
 
-	int walkboxCount = 0;
-	_currentRoomAnims = anims;
-	_currentRoomHotspots = hotspots;
-	_currentRoomExits = exits;
-	_currentRoomWalkboxes = walkboxes;
-	_currentRoomDescriptions = descriptions;
-	_scaleParams = scalingParams;
-	Common::copy(shadows, shadows + (640 * 400), _pixelsShadows);
-	_musicTrack = loadMusicTrackForRoom(roomFile, roomOffset);
-	_roomSfx = loadRoomSfx(roomFile, roomOffset);
+	resetConversationStates(roomNumber, pair12, pair12size);
+	uint32 lastDescPos = loadDescriptions(pair12, pair12size, _currentRoomDescriptions);
+	loadConversationData(pair12, pair12size, lastDescPos, _conversationDataSize, _conversationData);
+
+	if (_pixelsShadows != nullptr)
+		delete[] _pixelsShadows;
+	_pixelsShadows = loadShadowMap(roomNumber);
+
+	loadRemaps(roomNumber);
 
 	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
 		HotSpot hotspot = _currentRoomHotspots[i];
@@ -370,8 +387,8 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 
 	for (int i = 0; i < _currentRoomExits.size(); i++) {
 		Exit exit = _currentRoomExits[i];
-		// drawRect(_screen, exit.x, exit.y, exit.w, exit.h, 100 + i);
 	}
+
 	PaletteAnim *anim = getPaletteAnimForRoom(roomNumber);
 	if (anim != nullptr) {
 		if (_currentPaletteAnim != nullptr) {
@@ -383,6 +400,33 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	}
 
 	delete[] pair10;
+	delete[] pair12;
+}
+
+Common::Array<HotSpot> RoomManager::unifyHotspots(Common::Array<Pelrock::Sprite> &anims, Common::Array<Pelrock::HotSpot> &staticHotspots) {
+	Common::Array<HotSpot> unifiedHotspots;
+	for (int i = 0; i < anims.size(); i++) {
+		HotSpot thisHotspot;
+		thisHotspot.index = i;
+		thisHotspot.x = anims[i].x;
+		thisHotspot.y = anims[i].y;
+		thisHotspot.w = anims[i].w;
+		thisHotspot.h = anims[i].h;
+		thisHotspot.extra = anims[i].extra;
+		thisHotspot.actionFlags = anims[i].actionFlags;
+		thisHotspot.isEnabled = !anims[i].isDisabled;
+		thisHotspot.isSprite = true;
+		thisHotspot.zOrder = anims[i].zOrder;
+		unifiedHotspots.push_back(thisHotspot);
+	}
+
+	// debug("total descriptions = %d, anims = %d, hotspots = %d", descriptions.size(), anims.size(), staticHotspots.size());
+	for (int i = 0; i < staticHotspots.size(); i++) {
+		HotSpot hotspot = staticHotspots[i];
+		hotspot.index = anims.size() + i;
+		unifiedHotspots.push_back(hotspot);
+	}
+	return unifiedHotspots;
 }
 
 void RoomManager::init() {
@@ -533,35 +577,26 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(byte *data, size_t size) {
 	return walkboxes;
 }
 
-Common::Array<Description> RoomManager::loadRoomTexts(Common::File *roomFile, int roomOffset) {
-	uint32_t pair12_offset_pos = roomOffset + (12 * 8);
-	roomFile->seek(pair12_offset_pos, SEEK_SET);
-	uint32_t pair12_data_offset = roomFile->readUint32LE();
-	uint32_t pair12_size = roomFile->readUint32LE();
-
-	roomFile->seek(pair12_data_offset, SEEK_SET);
-	byte *data = new byte[pair12_size];
-	roomFile->read(data, pair12_size);
-	Common::Array<Description> descriptions;
+uint32 RoomManager::loadDescriptions(byte *pair12data, size_t pair12size, Common::Array<Description> &outDescriptions) {
 	uint32_t pos = 0;
 	uint32_t lastDescPos = 0;
-	while (pos < (pair12_size)) {
+	while (pos < (pair12size)) {
 		int desc_pos = 0;
-		if (data[pos] == 0xFF) {
+		if (pair12data[pos] == 0xFF) {
 			Description description;
 
-			description.itemId = data[pos + 1];
+			description.itemId = pair12data[pos + 1];
 			pos += 4;
-			description.index = data[pos++];
+			description.index = pair12data[pos++];
 			description.text = "";
 
-			while (pos < (pair12_size) && data[pos] != 0xFD && pos < (pair12_size)) {
+			while (pos < (pair12size) && pair12data[pos] != 0xFD && pos < (pair12size)) {
 
-				if (data[pos] != 0x00) {
-					description.text.append(1, (char)data[pos]);
+				if (pair12data[pos] != 0x00) {
+					description.text.append(1, (char)pair12data[pos]);
 				}
-				if (data[pos] == 0xF8) {
-					description.actionTrigger = data[pos + 1] | data[pos + 2] << 8;
+				if (pair12data[pos] == 0xF8) {
+					description.actionTrigger = pair12data[pos + 1] | pair12data[pos + 2] << 8;
 					if (description.actionTrigger != 0) {
 						description.isAction = true;
 					}
@@ -570,24 +605,25 @@ Common::Array<Description> RoomManager::loadRoomTexts(Common::File *roomFile, in
 				}
 				pos++;
 			}
-			descriptions.push_back(description);
+			outDescriptions.push_back(description);
 			lastDescPos = pos;
 		}
 		pos++;
 	}
-	// debug("End of descriptions at position %d", pos);
-	size_t conversationStart = lastDescPos + 1;
-	_conversationDataSize = pair12_size - conversationStart;
-	if (_conversationData != nullptr) {
-		delete[] _conversationData;
+	return lastDescPos + 1;
+}
+
+void RoomManager::loadConversationData(byte *pair12data, size_t pair12size, uint32 startPos, size_t &outConversationDataSize, byte *&outConversationData) {
+	size_t conversationStart = startPos;
+	outConversationDataSize = pair12size - conversationStart;
+	if (outConversationData != nullptr) {
+		delete[] outConversationData;
 	}
-	_conversationData = new byte[_conversationDataSize];
-	Common::copy(data + conversationStart, data + conversationStart + _conversationDataSize, _conversationData);
-	delete[] data;
-	return descriptions;
+	outConversationData = new byte[outConversationDataSize];
+	Common::copy(pair12data + conversationStart, pair12data + conversationStart + outConversationDataSize, outConversationData);
 }
 
-void RoomManager::resetRoomDefaults(byte room, byte *&data, size_t size) {
+void RoomManager::resetMetadataDefaults(byte room, byte *&data, size_t size) {
 	Common::File alfred8;
 	if (!alfred8.open("ALFRED.8")) {
 		error("Couldnt find file ALFRED.8");
@@ -609,7 +645,7 @@ void RoomManager::resetRoomDefaults(byte room, byte *&data, size_t size) {
 			// Not the room we care about, skip
 			continue;
 		}
-		debug("Resetting room %d data at offset %d, size %d", entry.room, entry.offset, entry.dataSize);
+		debug("Resetting room %d metadata at offset %d, size %d", entry.room, entry.offset, entry.dataSize);
 		Common::copy(entry.data, entry.data + entry.dataSize, data + entry.offset);
 		// delete[] entry.data;
 	}
@@ -731,8 +767,10 @@ void RoomManager::loadRemaps(int roomNumber) {
 	uint32 remapOffset = 0x200 + (roomNumber * 1024);
 
 	remapFile.seek(remapOffset, SEEK_SET);
-	remapFile.read(alfredRemap, 256);
-	remapFile.read(overlayRemap, 256);
+	remapFile.read(paletteRemaps[0], 256);
+	remapFile.read(paletteRemaps[1], 256);
+	remapFile.read(paletteRemaps[2], 256);
+	remapFile.read(paletteRemaps[3], 256);
 	remapFile.close();
 }
 
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index c3f2b9ed8e8..15d4b5c59c5 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -50,6 +50,7 @@ public:
 	RoomManager();
 	~RoomManager();
 	void loadRoomMetadata(Common::File *roomFile, int roomNumber);
+	Common::Array<HotSpot> unifyHotspots(Common::Array<Pelrock::Sprite> &anims, Common::Array<Pelrock::HotSpot> &staticHotspots);
 	void loadRoomTalkingAnimations(int roomNumber);
 	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
@@ -87,8 +88,7 @@ public:
 	ScalingParams _scaleParams;
 	byte *_pixelsShadows = nullptr;
 	byte _roomPalette[768];
-	byte alfredRemap[256];
-	byte overlayRemap[256];
+	byte paletteRemaps[4][256];
 	byte _musicTrack = 0;
 	Common::Array<byte> _roomSfx;
 	PaletteAnim *_currentPaletteAnim = nullptr;
@@ -104,9 +104,10 @@ private:
 	Common::Array<Exit> loadExits(byte *data, size_t size);
 	ScalingParams loadScalingParams(byte *data, size_t size);
 	Common::Array<WalkBox> loadWalkboxes(byte *data, size_t size);
-	Common::Array<Description> loadRoomTexts(Common::File *roomFile, int roomOffset);
-
-	void resetRoomDefaults(byte room, byte *&data, size_t size);
+	uint32 loadDescriptions(byte *pair12data, size_t pair12size, Common::Array<Description> &outDescriptions);
+	void loadConversationData(byte *pair12data, size_t pair12size, uint32 startPos, size_t &outConversationDataSize, byte *&outConversationData);
+	void resetConversationStates(byte roomNumber, byte *conversationData, size_t conversationDataSize);
+	void resetMetadataDefaults(byte room, byte *&data, size_t size);
 
 	byte *loadShadowMap(int roomNumber);
 	void loadRemaps(int roomNumber);
diff --git a/engines/pelrock/saveload.cpp b/engines/pelrock/saveload.cpp
new file mode 100644
index 00000000000..87e3e6cabff
--- /dev/null
+++ b/engines/pelrock/saveload.cpp
@@ -0,0 +1,25 @@
+/* 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/>.
+ *
+ */
+
+ namespace Pelrock {
+
+
+ } // End of namespace Pelrock
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index dcea3d58394..6467a007c0b 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -366,6 +366,15 @@ struct GameStateData {
 	Common::HashMap<byte, Common::Array<ExitChange>> roomExitChanges;
 	Common::HashMap<byte, Common::Array<WalkBoxChange>> roomWalkBoxChanges;
 	Common::HashMap<byte, Common::Array<HotSpotChange>> roomHotSpotChanges;
+	byte *conversationBranchState = new byte[4 * 56];
+
+	bool getBranchDisabledState(byte room, byte branch) const {
+		return (conversationBranchState[room * 4 + branch] != 0);
+	}
+
+	void setBranchDisabledState(byte room, byte branch, bool disabled) {
+		conversationBranchState[room * 4 + branch] = disabled ? 1 : 0;
+	}
 };
 
 } // End of namespace Pelrock


Commit: 40001b9ba5f96232b5e0437328fcfa1494c7ad30
    https://github.com/scummvm/scummvm/commit/40001b9ba5f96232b5e0437328fcfa1494c7ad30
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:41+02:00

Commit Message:
PELROCK: Saves state of disabled conversation choices

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 5582eef7204..e8d76a2aaf3 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -111,4 +111,11 @@ void PelrockEngine::noOp(HotSpot *hotspot) {
 	// Do nothing
 }
 
+void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte rootIndex) {
+	if(actionTrigger == 328) {
+		debug("Disabling root %d in room %d", rootIndex, room);
+		_state.setRootDisabledState(room, rootIndex, true);
+	}
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 046bf05eac2..d8de2caa6f3 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -68,7 +68,7 @@ uint32 DialogManager::readTextBlock(
 		}
 	}
 	// Check for dialogue marker (choice text)
-	else if (data[pos] == CTRL_DIALOGUE_MARKER || data[pos] == CTRL_DIALOGUE_MARKER_2) {
+	else if (data[pos] == CTRL_DIALOGUE_MARKER || data[pos] == CTRL_DIALOGUE_MARKER_ONEOFF) {
 		pos++; // Skip marker
 
 		// Skip choice index
@@ -95,7 +95,7 @@ uint32 DialogManager::readTextBlock(
 
 		// End markers - stop reading text
 		if (b == CTRL_END_TEXT || b == CTRL_END_CONVERSATION || b == CTRL_ACTION_TRIGGER ||
-			b == CTRL_END_BRANCH || b == CTRL_DIALOGUE_MARKER || b == CTRL_DIALOGUE_MARKER_2 ||
+			b == CTRL_END_BRANCH || b == CTRL_DIALOGUE_MARKER || b == CTRL_DIALOGUE_MARKER_ONEOFF ||
 			b == CTRL_TEXT_TERMINATOR || b == CTRL_ALT_END_MARKER_1 || b == CTRL_ALT_END_MARKER_2 ||
 			b == CTRL_ALT_END_MARKER_3 || b == CTRL_GO_BACK || b == CTRL_SPEAKER_ID) {
 			break;
@@ -305,7 +305,7 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 		}
 
 		// Found a dialogue marker
-		if (b == CTRL_DIALOGUE_MARKER || b == CTRL_DIALOGUE_MARKER_2) {
+		if (b == CTRL_DIALOGUE_MARKER || b == CTRL_DIALOGUE_MARKER_ONEOFF) {
 			if (pos + 1 < dataSize) {
 				if (firstChoiceIndex == -1) {
 					firstChoiceIndex = data[pos + 1];
@@ -317,7 +317,9 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 					// Check if disabled
 
 					ChoiceOption opt;
-					opt.index = choiceIndex;
+					opt.room = g_engine->_room->_currentRoomNumber;
+					opt.shouldDisableOnSelect = b == CTRL_DIALOGUE_MARKER_ONEOFF;
+					opt.choiceIndex = choiceIndex;
 					opt.dataOffset = pos;
 					pos += 2; // Move past marker + index
 					if (data[pos] == CTRL_DISABLED_CHOICE) {
@@ -329,7 +331,7 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 					while (textPos < dataSize) {
 						byte tb = data[textPos];
 						if (tb == CTRL_END_TEXT || tb == CTRL_DIALOGUE_MARKER ||
-							tb == CTRL_DIALOGUE_MARKER_2 || tb == CTRL_END_BRANCH ||
+							tb == CTRL_DIALOGUE_MARKER_ONEOFF || tb == CTRL_END_BRANCH ||
 							tb == CTRL_ALT_END_MARKER_1) {
 							break;
 						}
@@ -338,7 +340,6 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 							opt.text += (char)tb;
 						} else {
 							byte decoded = decodeChar(tb);
-							debug("Parsing choice char: 0x%02X, decoded: 0x%02X", tb, decoded);
 							if (decoded != tb || (decoded >= 0x20 && decoded <= 0xB4)) {
 								opt.text += (char)decoded;
 							}
@@ -395,7 +396,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 	while (position < dataSize &&
 		   conversationData[position] != CTRL_SPEAKER_ID &&
 		   conversationData[position] != CTRL_DIALOGUE_MARKER &&
-		   conversationData[position] != CTRL_DIALOGUE_MARKER_2) {
+		   conversationData[position] != CTRL_DIALOGUE_MARKER_ONEOFF) {
 		position++;
 	}
 
@@ -443,6 +444,16 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 			break;
 		}
 
+		if(controlByte == CTRL_ACTION_TRIGGER) {
+			uint16 actionCode = conversationData[position + 1] | (conversationData[position + 2] << 8);
+			debug("Action trigger %d encountered!", actionCode);
+			g_engine->dialogActionTrigger(
+				actionCode,
+				g_engine->_room->_currentRoomNumber,
+				g_engine->_room->getCurrentConversationRootIndex()
+			);
+		}
+
 		// Move past control byte
 		if (controlByte == CTRL_END_TEXT || controlByte == CTRL_ACTION_TRIGGER) {
 			position++;
@@ -467,7 +478,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		// If not at a choice marker, there's more dialogue to read
 		if (peekPos < dataSize &&
 			conversationData[peekPos] != CTRL_DIALOGUE_MARKER &&
-			conversationData[peekPos] != CTRL_DIALOGUE_MARKER_2 &&
+			conversationData[peekPos] != CTRL_DIALOGUE_MARKER_ONEOFF &&
 			conversationData[peekPos] != CTRL_END_CONVERSATION) {
 			continue;
 		}
@@ -477,7 +488,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		parseChoices(conversationData, dataSize, position, choices);
 		debug("Parsed %u choices", choices->size());
 		for (uint i = 0; i < choices->size(); i++) {
-			debug(" Choice %u (index %d): \"%s\" (Disabled: %s)", i, (*choices)[i].index, (*choices)[i].text.c_str(),
+			debug(" Choice %u (index %d): \"%s\" (Disabled: %s)", i, (*choices)[i].choiceIndex, (*choices)[i].text.c_str(),
 				  (*choices)[i].isDisabled ? "Yes" : "No");
 		}
 		if (choices->empty()) {
@@ -491,7 +502,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 			// We've already made a choice, check if the current choices are at the next level
 			bool foundNextLevel = false;
 			for (uint i = 0; i < choices->size(); i++) {
-				if ((*choices)[i].index == currentChoiceLevel + 1) {
+				if ((*choices)[i].choiceIndex == currentChoiceLevel + 1) {
 					foundNextLevel = true;
 					break;
 				}
@@ -515,11 +526,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 			// Real choice: show menu and wait for selection
 			Common::Array<Common::String> choiceTexts;
 			for (uint i = 0; i < choices->size(); i++) {
-				if ((*choices)[i].isDisabled) {
-					choiceTexts.push_back("[DISABLED] " + (*choices)[i].text);
-				} else {
-					choiceTexts.push_back((*choices)[i].text);
-				}
+				choiceTexts.push_back((*choices)[i].text);
 			}
 
 			if (_currentChoices) {
@@ -534,7 +541,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		// 6. Move position to after the selected choice
 		if (selectedIndex >= 0 && selectedIndex < (int)choices->size()) {
 			position = (*choices)[selectedIndex].dataOffset;
-			currentChoiceLevel = (*choices)[selectedIndex].index;
+			currentChoiceLevel = (*choices)[selectedIndex].choiceIndex;
 
 			// Read and display the selected choice as dialogue
 			Common::String choiceText;
@@ -543,6 +550,9 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 
 			if (!choiceText.empty() && choiceText.size() > 1) {
 				displayDialogue(choiceText, ALFRED_COLOR);
+				if ((*choices)[selectedIndex].shouldDisableOnSelect) {
+					g_engine->_room->addDisabledChoice((*choices)[selectedIndex]);
+				}
 			}
 
 			position = endPos;
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 3113a1c6a68..16df8d811d1 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -33,35 +33,22 @@
 namespace Pelrock {
 
 // Control character codes (negative values in signed char)
-#define CHAR_SPACE 0x20        /* ' ' */
-#define CTRL_SPEAKER_ID 0x08        /* Next byte is speaker ID (color) */
-#define CTRL_END_TEXT 0xFD          /* End of text segment */
-#define CTRL_TEXT_TERMINATOR 0xFC   /* Text terminator */
-#define CTRL_DIALOGUE_MARKER 0xF1   /* Choice marker that sticks */
-#define CTRL_DISABLED_CHOICE 0xFA   /* Disabled choice marker */
-#define CTRL_PAGE_BREAK_CONV 0xF9   /* Page break in conversation */
-#define CTRL_ACTION_TRIGGER 0xF8    /* Action trigger */
-#define CTRL_END_BRANCH 0xF7        /* End of branch */
-#define CTRL_LINE_CONTINUE 0xF6     /* Line continue/newline */
-#define CTRL_ALT_END_MARKER_1 0xF5  /* Alt end marker - do nothing */
-#define CTRL_END_CONVERSATION 0xF4  /* End conversation and disable option */
-#define CTRL_DIALOGUE_MARKER_2 0xFB /* Alt choice marker that disappears */
-#define CTRL_GO_BACK 0xF0           /* Go back in conversation */
-#define CTRL_ALT_END_MARKER_2 0xEB  /* Alt end marker 2 */
-#define CTRL_ALT_END_MARKER_3 0xFE  /* Alt end marker 3 */
-
-/**
- * Structure to hold a parsed choice option
- */
-struct ChoiceOption {
-	int index;
-	Common::String text;
-	bool isDisabled;
-	uint32 dataOffset;
-	bool shouldDisableOnSelect = false;
-
-	ChoiceOption() : index(-1), isDisabled(false), dataOffset(0) {}
-};
+#define CHAR_SPACE 0x20                  /* ' ' */
+#define CTRL_SPEAKER_ID 0x08             /* Next byte is speaker ID (color) */
+#define CTRL_END_TEXT 0xFD               /* End of text segment */
+#define CTRL_TEXT_TERMINATOR 0xFC        /* Text terminator */
+#define CTRL_DIALOGUE_MARKER 0xF1        /* Choice marker that sticks */
+#define CTRL_DISABLED_CHOICE 0xFA        /* Disabled choice marker */
+#define CTRL_PAGE_BREAK_CONV 0xF9        /* Page break in conversation */
+#define CTRL_ACTION_TRIGGER 0xF8         /* Action trigger */
+#define CTRL_END_BRANCH 0xF7             /* End of branch */
+#define CTRL_LINE_CONTINUE 0xF6          /* Line continue/newline */
+#define CTRL_ALT_END_MARKER_1 0xF5       /* Alt end marker - do nothing */
+#define CTRL_END_CONVERSATION 0xF4       /* End conversation and disable option */
+#define CTRL_DIALOGUE_MARKER_ONEOFF 0xFB /* Alt choice marker that disappears */
+#define CTRL_GO_BACK 0xF0                /* Go back in conversation */
+#define CTRL_ALT_END_MARKER_2 0xEB       /* Alt end marker 2 */
+#define CTRL_ALT_END_MARKER_3 0xFE       /* Alt end marker 3 */
 
 static void debugHexString(const Common::String &str, const char *label = nullptr) {
 	if (label) {
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index bb0548d69b7..994b6b4d7fb 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -274,6 +274,7 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	}
 }
 
+
 void PelrockEngine::executeAction(VerbIcon action, HotSpot *hotspot) {
 	for (const ActionEntry *entry = actionTable; entry->handler != nullptr; entry++) {
 		if (entry->action == action && entry->hotspotExtra == hotspot->extra) {
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 74ac6c087ea..4d60d09973a 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -120,7 +120,6 @@ private:
 	void calculateScalingMasks();
 	ScaleCalculation calculateScaling(int yPos, ScalingParams scalingParams);
 
-
 	Common::Array<Common::Array<int>> _widthScalingTable;
 	Common::Array<Common::Array<int>> _heightScalingTable;
 
@@ -145,7 +144,6 @@ private:
 	bool gameInitialized = false;
 	bool screenReady = false;
 
-
 	// int prevDirX = 0;
 	// int prevDirY = 0;
 	// Common::String objectToShow = "";
@@ -173,7 +171,6 @@ public:
 	Common::Array<int> _inventoryItems;
 	int _selectedInventoryItem = -1;
 
-
 public:
 	PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc);
 	~PelrockEngine() override;
@@ -225,13 +222,14 @@ public:
 
 	// Actions
 	void performActionTrigger(uint16 actionTrigger);
+	void dialogActionTrigger(uint16 actionTrigger, byte room, byte rootIndex);
 
 	void executeAction(VerbIcon action, HotSpot *hotspot);
-    void openDrawer(HotSpot *hotspot);
-    void closeDrawer(HotSpot *hotspot);
-    void openDoor(HotSpot *hotspot);
-    void closeDoor(HotSpot *hotspot);
-    void pickUpAndDisable(HotSpot *hotspot);
+	void openDrawer(HotSpot *hotspot);
+	void closeDrawer(HotSpot *hotspot);
+	void openDoor(HotSpot *hotspot);
+	void closeDoor(HotSpot *hotspot);
+	void pickUpAndDisable(HotSpot *hotspot);
 	void pickUpPhoto(HotSpot *hotspot);
 	void pickYellowBook(HotSpot *hotspot);
 	void pickUpBrick(HotSpot *hotspot);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 478d7d15089..3a6e1a67bda 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -371,8 +371,8 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	roomFile->read(pair12, pair12size);
 
 	resetConversationStates(roomNumber, pair12, pair12size);
-	uint32 lastDescPos = loadDescriptions(pair12, pair12size, _currentRoomDescriptions);
-	loadConversationData(pair12, pair12size, lastDescPos, _conversationDataSize, _conversationData);
+	_conversationOffset = loadDescriptions(pair12, pair12size, _currentRoomDescriptions);
+	loadConversationData(pair12, pair12size, _conversationOffset, _conversationDataSize, _conversationData);
 
 	if (_pixelsShadows != nullptr)
 		delete[] _pixelsShadows;
@@ -621,6 +621,39 @@ void RoomManager::loadConversationData(byte *pair12data, size_t pair12size, uint
 	}
 	outConversationData = new byte[outConversationDataSize];
 	Common::copy(pair12data + conversationStart, pair12data + conversationStart + outConversationDataSize, outConversationData);
+	if (g_engine->_state.disabledBranches.contains(_currentRoomNumber)) {
+		applyDisabledChoices(_currentRoomNumber, outConversationData, outConversationDataSize);
+	}
+}
+
+void RoomManager::applyDisabledChoices(int roomNumber, byte *conversationData, size_t conversationDataSize) {
+	Common::Array<ResetEntry> disabledBranches = g_engine->_state.disabledBranches[roomNumber];
+	if (disabledBranches.size() == 0) {
+		return;
+	}
+	debug("Disabling %d conversation branches for room %d", disabledBranches.size(), roomNumber);
+	for (int i = 0; i < disabledBranches.size(); i++) {
+		ResetEntry resetEntry = disabledBranches[i];
+		applyDisabledChoice(resetEntry, conversationData, conversationDataSize);
+	}
+}
+
+void RoomManager::applyDisabledChoice(ResetEntry entry, byte *conversationData, size_t conversationDataSize) {
+	Common::copy(entry.data, entry.data + entry.dataSize, conversationData + entry.offset);
+}
+
+void RoomManager::addDisabledChoice(ChoiceOption choice) {
+	debug("Adding disabled branch for room %d at offset %d", choice.room, choice.dataOffset);
+	ResetEntry resetEntry = ResetEntry();
+	resetEntry.room = choice.room;
+	resetEntry.offset = choice.dataOffset;
+	resetEntry.dataSize = 1;
+	resetEntry.data = new byte[1];
+	resetEntry.data[0] = 0xFA; // Disabled
+	// Apply immediately
+	applyDisabledChoice(resetEntry, _conversationData, _conversationDataSize);
+	// Store for future loads
+	g_engine->_state.addDisabledBranch(resetEntry);
 }
 
 void RoomManager::resetMetadataDefaults(byte room, byte *&data, size_t size) {
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 15d4b5c59c5..2a3ace33404 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -31,20 +31,6 @@ namespace Pelrock {
 
 static const int kNumSfxPerRoom = 8;
 
-struct ResetEntry {
-	uint16 room;
-	uint16 offset;
-	byte dataSize;
-	byte *data = nullptr;
-
-	~ResetEntry() {
-		if (data != nullptr) {
-			delete[] data;
-			data = nullptr;
-		}
-	}
-};
-
 class RoomManager {
 public:
 	RoomManager();
@@ -54,6 +40,8 @@ public:
 	void loadRoomTalkingAnimations(int roomNumber);
 	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
+
+	/** Methods to modify room data at runtime **/
 	void addSticker(Sticker sticker, bool persist = true);
 	void removeSticker(int index);
 	bool hasSticker(int index);
@@ -66,6 +54,9 @@ public:
 	void enableHotspot(HotSpot *hotspot, bool persist = true);
 	void disableHotspot(HotSpot *hotspot, bool persist = true);
 	void addWalkbox(WalkBox walkbox);
+	void applyDisabledChoices(int roomNumber, byte *conversationData, size_t conversationDataSize);
+	void applyDisabledChoice(ResetEntry entry, byte *conversationData, size_t conversationDataSize);
+	void addDisabledChoice(ChoiceOption choice);
 
 	HotSpot *findHotspotByExtra(uint16 extra);
 	PaletteAnim *getPaletteAnimForRoom(int roomNumber);
@@ -95,6 +86,7 @@ public:
 	byte *_conversationData = nullptr;
 	size_t _conversationDataSize = 0;
 	Common::Array<Sticker> _transientStickers;
+	uint32 _conversationOffset;
 
 private:
 	void init();
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 6467a007c0b..4e5f5b44e19 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -359,6 +359,26 @@ struct PaletteAnim {
 	byte curFrameCount = 0;
 };
 
+/**
+ * Structure to hold a parsed choice option
+ */
+struct ChoiceOption {
+	byte room;
+	int choiceIndex;
+	Common::String text;
+	uint32 dataOffset;
+	bool isDisabled;
+	bool shouldDisableOnSelect = false;
+
+	ChoiceOption() : choiceIndex(-1), isDisabled(false), dataOffset(0) {}
+};
+
+struct ResetEntry {
+	uint16 room;
+	uint16 offset;
+	byte dataSize;
+	byte *data = nullptr;
+};
 
 struct GameStateData {
 	GameState stateGame = INTRO;
@@ -366,14 +386,21 @@ struct GameStateData {
 	Common::HashMap<byte, Common::Array<ExitChange>> roomExitChanges;
 	Common::HashMap<byte, Common::Array<WalkBoxChange>> roomWalkBoxChanges;
 	Common::HashMap<byte, Common::Array<HotSpotChange>> roomHotSpotChanges;
-	byte *conversationBranchState = new byte[4 * 56];
 
-	bool getBranchDisabledState(byte room, byte branch) const {
-		return (conversationBranchState[room * 4 + branch] != 0);
+	Common::HashMap<byte, Common::Array<ResetEntry>> disabledBranches;
+
+	void addDisabledBranch(ResetEntry entry) {
+		disabledBranches[entry.room].push_back(entry);
+	}
+
+	byte *conversationRootsState = new byte[4 * 56];
+
+	bool getRootDisabledState(byte room, byte root) const {
+		return (conversationRootsState[room * 4 + root] != 0);
 	}
 
-	void setBranchDisabledState(byte room, byte branch, bool disabled) {
-		conversationBranchState[room * 4 + branch] = disabled ? 1 : 0;
+	void setRootDisabledState(byte room, byte root, bool disabled) {
+		conversationRootsState[room * 4 + root] = disabled ? 1 : 0;
 	}
 };
 


Commit: 8b077e1e0215397c5eb9488a7c7334064643ec47
    https://github.com/scummvm/scummvm/commit/8b077e1e0215397c5eb9488a7c7334064643ec47
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:41+02:00

Commit Message:
PELROCK: Selects correct conversation when multiple npcs exist

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index d8de2caa6f3..2c9cc81730e 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -50,7 +50,7 @@ uint32 DialogManager::readTextBlock(
 	// Skip control bytes at start
 	while (pos < dataSize &&
 		   (data[pos] == CTRL_ALT_END_MARKER_1 || data[pos] == CTRL_ALT_END_MARKER_2 ||
-			data[pos] == CTRL_ALT_END_MARKER_3 || data[pos] == CTRL_TEXT_TERMINATOR ||
+			data[pos] == CTRL_TEXT_TERMINATOR ||
 			data[pos] == CTRL_GO_BACK)) {
 		pos++;
 	}
@@ -97,7 +97,7 @@ uint32 DialogManager::readTextBlock(
 		if (b == CTRL_END_TEXT || b == CTRL_END_CONVERSATION || b == CTRL_ACTION_TRIGGER ||
 			b == CTRL_END_BRANCH || b == CTRL_DIALOGUE_MARKER || b == CTRL_DIALOGUE_MARKER_ONEOFF ||
 			b == CTRL_TEXT_TERMINATOR || b == CTRL_ALT_END_MARKER_1 || b == CTRL_ALT_END_MARKER_2 ||
-			b == CTRL_ALT_END_MARKER_3 || b == CTRL_GO_BACK || b == CTRL_SPEAKER_ID) {
+			b == CTRL_GO_BACK || b == CTRL_SPEAKER_ID) {
 			break;
 		}
 
@@ -300,7 +300,7 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 		byte b = data[pos];
 
 		// Stop at end markers
-		if (b == CTRL_ALT_END_MARKER_1 || b == CTRL_END_BRANCH || b == CTRL_ALT_END_MARKER_3) {
+		if (b == CTRL_ALT_END_MARKER_1 || b == CTRL_END_BRANCH) {
 			break;
 		}
 
@@ -379,7 +379,7 @@ void DialogManager::setCurSprite(int index) {
 	_curSprite = nullptr;
 }
 
-void DialogManager::startConversation(const byte *conversationData, uint32 dataSize, Sprite *animSet) {
+void DialogManager::startConversation(const byte *conversationData, uint32 dataSize, byte npcIndex, Sprite *animSet) {
 	if (!conversationData || dataSize == 0) {
 		debug("startConversation: No conversation data");
 		return;
@@ -387,11 +387,24 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 	setCurSprite(animSet ? animSet->index : -1);
 	// _curSprite = animSet;
 
-	debug("Starting conversation with %u bytes of data", dataSize);
+	debug("Starting conversation with %u bytes of data, for npc %u", dataSize, npcIndex);
 
 	uint32 position = 0;
 	int currentChoiceLevel = -1; // Track the current choice level
 
+	bool speakerRootOffsetFound = false;
+	int currentRoot = npcIndex + 1;
+	while(position < dataSize && !speakerRootOffsetFound) {
+		if(conversationData[position] == CTRL_ALT_SPEAKER_ROOT && conversationData[position + 1] == currentRoot) {
+			speakerRootOffsetFound = true;
+			position += 2; // Move past the speaker root marker and npc index
+		} else {
+			position++;
+		}
+	}
+
+
+
 	// Skip any junk at start until we find a speaker marker or choice marker
 	while (position < dataSize &&
 		   conversationData[position] != CTRL_SPEAKER_ID &&
@@ -406,7 +419,6 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		while (position < dataSize &&
 			   (conversationData[position] == CTRL_ALT_END_MARKER_1 ||
 				conversationData[position] == CTRL_ALT_END_MARKER_2 ||
-				conversationData[position] == CTRL_ALT_END_MARKER_3 ||
 				conversationData[position] == CTRL_TEXT_TERMINATOR ||
 				conversationData[position] == CTRL_GO_BACK)) {
 			position++;
@@ -450,7 +462,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 			g_engine->dialogActionTrigger(
 				actionCode,
 				g_engine->_room->_currentRoomNumber,
-				g_engine->_room->getCurrentConversationRootIndex()
+				0 //FIXME: Pass NPC index?
 			);
 		}
 
@@ -469,7 +481,6 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		while (peekPos < dataSize &&
 			   (conversationData[peekPos] == CTRL_ALT_END_MARKER_1 ||
 				conversationData[peekPos] == CTRL_ALT_END_MARKER_2 ||
-				conversationData[peekPos] == CTRL_ALT_END_MARKER_3 ||
 				conversationData[peekPos] == CTRL_TEXT_TERMINATOR ||
 				conversationData[peekPos] == CTRL_GO_BACK)) {
 			peekPos++;
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 16df8d811d1..66a34b94c8b 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -48,7 +48,7 @@ namespace Pelrock {
 #define CTRL_DIALOGUE_MARKER_ONEOFF 0xFB /* Alt choice marker that disappears */
 #define CTRL_GO_BACK 0xF0                /* Go back in conversation */
 #define CTRL_ALT_END_MARKER_2 0xEB       /* Alt end marker 2 */
-#define CTRL_ALT_END_MARKER_3 0xFE       /* Alt end marker 3 */
+#define CTRL_ALT_SPEAKER_ROOT 0xFE       /* Separates conversations from different speakers */
 
 static void debugHexString(const Common::String &str, const char *label = nullptr) {
 	if (label) {
@@ -84,7 +84,7 @@ public:
 
 	void displayChoices(Common::Array<ChoiceOption> *choices, byte *compositeBuffer);
 	int selectChoice(Common::Array<Common::String> &choices, byte *compositeBuffer);
-	void startConversation(const byte *conversationData, uint32 dataSize, Sprite *alfredAnimSet = nullptr);
+	void startConversation(const byte *conversationData, uint32 dataSize, byte npcIndex, Sprite *alfredAnimSet = nullptr);
 	void sayAlfred(Description description);
 	void say(Common::StringArray texts);
 	bool processColorAndTrim(Common::StringArray &lines, byte &speakerId);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 994b6b4d7fb..e8ea2498395 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -533,7 +533,7 @@ void PelrockEngine::talkTo(HotSpot *hotspot) {
 			break;
 		}
 	}
-	_dialog->startConversation(_room->_conversationData, _room->_conversationDataSize, animSet);
+	_dialog->startConversation(_room->_conversationData, _room->_conversationDataSize, hotspot->index, animSet);
 }
 
 void PelrockEngine::lookAt(HotSpot *hotspot) {


Commit: 89d26fe2a93fcf8ee5371dc712d0f65d117ef69b
    https://github.com/scummvm/scummvm/commit/89d26fe2a93fcf8ee5371dc712d0f65d117ef69b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:41+02:00

Commit Message:
PELROCK: Fixes descriptions not being cleared

Changed paths:
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 3a6e1a67bda..9cc6a11d670 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -371,6 +371,7 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	roomFile->read(pair12, pair12size);
 
 	resetConversationStates(roomNumber, pair12, pair12size);
+
 	_conversationOffset = loadDescriptions(pair12, pair12size, _currentRoomDescriptions);
 	loadConversationData(pair12, pair12size, _conversationOffset, _conversationDataSize, _conversationData);
 
@@ -580,6 +581,7 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(byte *data, size_t size) {
 uint32 RoomManager::loadDescriptions(byte *pair12data, size_t pair12size, Common::Array<Description> &outDescriptions) {
 	uint32_t pos = 0;
 	uint32_t lastDescPos = 0;
+	outDescriptions.clear();
 	while (pos < (pair12size)) {
 		int desc_pos = 0;
 		if (pair12data[pos] == 0xFF) {


Commit: 3622f07b3df0d399fb277c9e2c01b93a63259d29
    https://github.com/scummvm/scummvm/commit/3622f07b3df0d399fb277c9e2c01b93a63259d29
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:42+02:00

Commit Message:
PELROCK: Disables conversation roots

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 2c9cc81730e..a433e5378ca 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -392,18 +392,30 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 	uint32 position = 0;
 	int currentChoiceLevel = -1; // Track the current choice level
 
-	bool speakerRootOffsetFound = false;
-	int currentRoot = npcIndex + 1;
-	while(position < dataSize && !speakerRootOffsetFound) {
-		if(conversationData[position] == CTRL_ALT_SPEAKER_ROOT && conversationData[position + 1] == currentRoot) {
-			speakerRootOffsetFound = true;
-			position += 2; // Move past the speaker root marker and npc index
+	// Find the speaker tree for this NPC; they are marked by 0xFE 0xXX where XX is NPC index + 1
+	bool speakerTreeOffsetFound = false;
+	int currentConversationTree = npcIndex + 1;
+	while(position < dataSize && !speakerTreeOffsetFound) {
+		if(conversationData[position] == CTRL_ALT_SPEAKER_ROOT && conversationData[position + 1] == currentConversationTree) {
+			speakerTreeOffsetFound = true;
+			position += 2; // Move past the speaker tree marker and npc index
 		} else {
 			position++;
 		}
 	}
-
-
+	// Right after the speaker conversation tree, we are in branch 0
+	int currentRoot = 0;
+	while(g_engine->_state.getRootDisabledState(g_engine->_room->_currentRoomNumber, currentRoot)) {
+		// This root is disabled, skip to next
+		while(position < dataSize) {
+			if(conversationData[position] == CTRL_END_BRANCH) {
+				position++; // Move past end branch marker
+				currentRoot++;
+				break;
+			}
+			position++;
+		}
+	}
 
 	// Skip any junk at start until we find a speaker marker or choice marker
 	while (position < dataSize &&
@@ -462,7 +474,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 			g_engine->dialogActionTrigger(
 				actionCode,
 				g_engine->_room->_currentRoomNumber,
-				0 //FIXME: Pass NPC index?
+				currentRoot
 			);
 		}
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index e8ea2498395..523797ae087 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -160,10 +160,10 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		setScreen(0, ALFRED_DOWN);
-		// setScreen(2, ALFRED_LEFT);
-		// alfredState.x = 576;
-		// alfredState.y = 374;
+		// setScreen(0, ALFRED_DOWN);
+		setScreen(2, ALFRED_LEFT);
+		alfredState.x = 576;
+		alfredState.y = 374;
 	}
 }
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 4e5f5b44e19..4df597b3717 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -389,6 +389,15 @@ struct GameStateData {
 
 	Common::HashMap<byte, Common::Array<ResetEntry>> disabledBranches;
 
+	GameStateData() {
+		memset(conversationRootsState, 0, 4 * 56);
+	}
+
+	~GameStateData() {
+		delete[] conversationRootsState;
+		conversationRootsState = nullptr;
+	}
+
 	void addDisabledBranch(ResetEntry entry) {
 		disabledBranches[entry.room].push_back(entry);
 	}


Commit: 19c6f7bc1a7a81c9b6185e79c3516658a004790d
    https://github.com/scummvm/scummvm/commit/19c6f7bc1a7a81c9b6185e79c3516658a004790d
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:42+02:00

Commit Message:
PELROCK: Fixes dialog lines being read incorrectly

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/util.h


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index a433e5378ca..6e4b20352f1 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -43,16 +43,14 @@ uint32 DialogManager::readTextBlock(
 	uint32 startPos,
 	Common::String &outText,
 	byte &outSpeakerId) {
+
 	uint32 pos = startPos;
 	outSpeakerId = ALFRED_COLOR; // Default to Alfred's color
 	outText = "";
 
 	// Skip control bytes at start
-	while (pos < dataSize &&
-		   (data[pos] == CTRL_ALT_END_MARKER_1 || data[pos] == CTRL_ALT_END_MARKER_2 ||
-			data[pos] == CTRL_TEXT_TERMINATOR ||
-			data[pos] == CTRL_GO_BACK)) {
-		pos++;
+	if(data[pos] == CTRL_TEXT_TERMINATOR) {
+		pos+=2;
 	}
 
 	if (pos >= dataSize) {
@@ -64,7 +62,6 @@ uint32 DialogManager::readTextBlock(
 		pos++;
 		if (pos < dataSize) {
 			outSpeakerId = data[pos];
-			pos++;
 		}
 	}
 	// Check for dialogue marker (choice text)
@@ -75,20 +72,14 @@ uint32 DialogManager::readTextBlock(
 		if (pos < dataSize) {
 			pos++;
 		}
-
-		// Skip 2 bytes after choice index (speaker marker bytes)
-		if (pos < dataSize) {
-			pos++;
-		}
-		if (pos < dataSize) {
-			pos++;
-		}
-
 		// Choice text is always spoken by ALFRED
 		outSpeakerId = ALFRED_COLOR;
+		pos+=2;
 	}
 
-	// pos += 2; // Skip line count and blank
+	int lineIndex = data[++pos];
+	pos++; //blank
+	debug("Reading text block starting at pos %u, line index %d, speaker ID %d", startPos, lineIndex, outSpeakerId);
 	// Read text until control byte
 	while (pos < dataSize) {
 		byte b = data[pos];
@@ -102,6 +93,7 @@ uint32 DialogManager::readTextBlock(
 		}
 
 		if (b == CTRL_LINE_CONTINUE || b == CTRL_PAGE_BREAK_CONV) {
+			warning("Found unexpected line/page break control code in readTextBlock at pos %u", pos);
 			outText += ' ';
 			pos++;
 			continue;
@@ -431,7 +423,6 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		while (position < dataSize &&
 			   (conversationData[position] == CTRL_ALT_END_MARKER_1 ||
 				conversationData[position] == CTRL_ALT_END_MARKER_2 ||
-				conversationData[position] == CTRL_TEXT_TERMINATOR ||
 				conversationData[position] == CTRL_GO_BACK)) {
 			position++;
 		}
@@ -476,6 +467,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 				g_engine->_room->_currentRoomNumber,
 				currentRoot
 			);
+			break;
 		}
 
 		// Move past control byte
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index 2a31bfbcd81..59ae1fb4687 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -52,7 +52,7 @@ static const int special_chars[] = {
 	163, // small u tilde
 	162, // small o tilde
 	161, // small i tilde
-	132, // small e tilde
+	130, // small e tilde
 	160, // small a tilde
 };
 


Commit: eafad6d16d1a5a23247f1bcea5bceaf562805bcc
    https://github.com/scummvm/scummvm/commit/eafad6d16d1a5a23247f1bcea5bceaf562805bcc
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:42+02:00

Commit Message:
PELROCK: Fix text positioning for NPCs

Changed paths:
    engines/pelrock/dialog.cpp


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 6e4b20352f1..b3313729fe5 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -191,24 +191,18 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		} else {
 			g_engine->alfredState.setState(ALFRED_IDLE);
 			_curSprite->isTalking = true;
-			xPos = _curSprite->x + _curSprite->w / 2;
+			xPos = _curSprite->x + _curSprite->w / 2 - maxWidth / 2;
 			yPos = _curSprite->y - height; // Above sprite, adjust for line
 		}
 
 		Graphics::Surface s = getDialogueSurface(textLines, speakerId);
 
-		if (xPos + s.getRect().width() > 640) {
-			xPos = 640 - s.getRect().width();
-		}
-		if (yPos + s.getRect().height() > 400) {
-			yPos = 400 - s.getRect().height();
-		}
-		if (xPos < 0) {
-			xPos = 0;
-		}
-		if (yPos < 0) {
-			yPos = 0;
-		}
+
+
+		// Clamp to screen bounds
+		xPos = CLIP(xPos, 0, 640 - maxWidth);
+		yPos = CLIP(yPos, 0, 400 - s.getRect().height());
+
 		_screen->transBlitFrom(s, s.getRect(), Common::Point(xPos, yPos), 255);
 		drawPos(_screen, xPos, yPos, speakerId);
 		drawRect(_screen, xPos, yPos,


Commit: b1034e8745a1ee6e42ac16382aa5f89b60f1b91c
    https://github.com/scummvm/scummvm/commit/b1034e8745a1ee6e42ac16382aa5f89b60f1b91c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:43+02:00

Commit Message:
PELROCK: Initial save/load implementation

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/menu.cpp
    engines/pelrock/module.mk
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/saveload.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index e8d76a2aaf3..30127b279fe 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -77,8 +77,8 @@ void PelrockEngine::closeDoor(HotSpot *hotspot) {
 }
 
 void PelrockEngine::pickUpAndDisable(HotSpot *hotspot) {
-	if (_inventoryItems.size() == 0) {
-		_selectedInventoryItem = hotspot->extra;
+	if (_state.inventoryItems.size() == 0) {
+		_state.selectedInventoryItem = hotspot->extra;
 	}
 	int frameCounter = 0;
 	while (frameCounter < kIconFlashDuration) {
@@ -91,7 +91,7 @@ void PelrockEngine::pickUpAndDisable(HotSpot *hotspot) {
 		}
 		g_system->delayMillis(10);
 	}
-	_inventoryItems.push_back(hotspot->extra);
+	_state.inventoryItems.push_back(hotspot->extra);
 	_room->disableHotspot(hotspot);
 }
 
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index b3313729fe5..110a1c16632 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -179,17 +179,17 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		int yPos = 0;
 
 		if (speakerId == ALFRED_COLOR) {
-			if (g_engine->alfredState.animState != ALFRED_TALKING) {
-				g_engine->alfredState.setState(ALFRED_TALKING);
+			if (g_engine->_alfredState.animState != ALFRED_TALKING) {
+				g_engine->_alfredState.setState(ALFRED_TALKING);
 			}
 			if (_curSprite != nullptr) {
 				_curSprite->isTalking = false;
 			}
 			// Offset X position for Alfred to avoid overlapping with his sprite
-			xPos = g_engine->alfredState.x - maxWidth / 2;                //+ kAlfredFrameWidth / 2 - maxWidth / 2;
-			yPos = g_engine->alfredState.y - kAlfredFrameHeight - height; // Above sprite, adjust for line
+			xPos = g_engine->_alfredState.x - maxWidth / 2;                //+ kAlfredFrameWidth / 2 - maxWidth / 2;
+			yPos = g_engine->_alfredState.y - kAlfredFrameHeight - height; // Above sprite, adjust for line
 		} else {
-			g_engine->alfredState.setState(ALFRED_IDLE);
+			g_engine->_alfredState.setState(ALFRED_IDLE);
 			_curSprite->isTalking = true;
 			xPos = _curSprite->x + _curSprite->w / 2 - maxWidth / 2;
 			yPos = _curSprite->y - height; // Above sprite, adjust for line
@@ -226,7 +226,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 	if (_curSprite != nullptr) {
 		_curSprite->isTalking = false;
 	}
-	g_engine->alfredState.setState(ALFRED_IDLE);
+	g_engine->_alfredState.setState(ALFRED_IDLE);
 }
 
 void DialogManager::displayDialogue(Common::String text, byte speakerId) {
@@ -582,7 +582,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 }
 
 void DialogManager::sayAlfred(Common::StringArray texts) {
-	g_engine->alfredState.setState(ALFRED_TALKING);
+	g_engine->_alfredState.setState(ALFRED_TALKING);
 
 	_curSprite = nullptr;
 	Common::Array<Common::StringArray> textLines = wordWrap(texts);
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 78db638a13d..39442d50c0c 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -97,9 +97,9 @@ void MenuManager::checkMouseClick(int x, int y) {
 	for (int i = 0; i < 4; i++) {
 		if (x >= 140 + (82 * i) && x <= 140 + (82 * i) + 64 &&
 			y >= 115 - (8 * i) && y <= 115 - (8 * i) + 64) {
-			_selectedInvIndex = g_engine->_inventoryItems[_curInventoryPage * 4 + i];
+			_selectedInvIndex = g_engine->_state.inventoryItems[_curInventoryPage * 4 + i];
 			_menuText = _inventoryDescriptions[_selectedInvIndex];
-			g_engine->_selectedInventoryItem = _selectedInvIndex;
+			g_engine->_state.selectedInventoryItem = _selectedInvIndex;
 			selectedItem = true;
 			return;
 		}
@@ -119,9 +119,15 @@ void MenuManager::checkMouseClick(int x, int y) {
 			_curInventoryPage--;
 		break;
 	case INVENTORY_NEXT_BUTTON:
-		if ((_curInventoryPage + 1) * 4 < g_engine->_inventoryItems.size())
+		if ((_curInventoryPage + 1) * 4 < g_engine->_state.inventoryItems.size())
 			_curInventoryPage++;
 		break;
+	case SAVE_GAME_BUTTON:
+		g_engine->saveGameDialog();
+		break;
+	case LOAD_GAME_BUTTON:
+		g_engine->loadGameDialog();
+		break;
 	default:
 		break;
 	}
@@ -155,9 +161,9 @@ void MenuManager::menuLoop() {
 
 	for (int i = 0; i < 4; i++) {
 		int itemIndex = _curInventoryPage * 4 + i;
-		if (g_engine->_inventoryItems.size() <= itemIndex)
+		if (g_engine->_state.inventoryItems.size() <= itemIndex)
 			continue;
-		InventoryObject item = g_engine->_res->getInventoryObject(g_engine->_inventoryItems[itemIndex]);
+		InventoryObject item = g_engine->_res->getInventoryObject(g_engine->_state.inventoryItems[itemIndex]);
 		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 140 + (82 * i), 115 - (8 * i), 60, 60, 1);
 	}
 
diff --git a/engines/pelrock/module.mk b/engines/pelrock/module.mk
index a01a7d58cc9..fc5b879566f 100644
--- a/engines/pelrock/module.mk
+++ b/engines/pelrock/module.mk
@@ -18,7 +18,8 @@ MODULE_OBJS = \
 	events.o \
 	dialog.o \
 	menu.o \
-	graphics.o
+	graphics.o \
+	saveload.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_PELROCK), DYNAMIC_PLUGIN)
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 523797ae087..bebdd59fd74 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -160,10 +160,10 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		// setScreen(0, ALFRED_DOWN);
-		setScreen(2, ALFRED_LEFT);
-		alfredState.x = 576;
-		alfredState.y = 374;
+		setScreen(0, ALFRED_DOWN);
+		// setScreen(2, ALFRED_LEFT);
+		// alfredState.x = 576;
+		// alfredState.y = 374;
 	}
 }
 
@@ -300,8 +300,8 @@ void PelrockEngine::checkMouse() {
 
 	// Cancel walking animation on mouse click
 	if (_events->_leftMouseButton) {
-		alfredState.curFrame = 0;
-		alfredState.setState(ALFRED_IDLE);
+		_alfredState.curFrame = 0;
+		_alfredState.setState(ALFRED_IDLE);
 	}
 
 	// Handle mouse release after long press (popup selection mode)
@@ -399,15 +399,15 @@ void PelrockEngine::paintDebugLayer() {
 		_smallFont->drawString(_screen, Common::String::format("Exit %d -> Room %d", i, exit.targetRoom), exit.x + 2, exit.y + 2, 640, 14);
 	}
 
-	drawPos(_screen, alfredState.x, alfredState.y, 13);
-	drawPos(_screen, alfredState.x, alfredState.y - kAlfredFrameHeight, 13);
+	drawPos(_screen, _alfredState.x, _alfredState.y, 13);
+	drawPos(_screen, _alfredState.x, _alfredState.y - kAlfredFrameHeight, 13);
 	drawPos(_screen, _curWalkTarget.x, _curWalkTarget.y, 100);
 
 	if (showShadows) {
 		memcpy(_screen->getPixels(), _room->_pixelsShadows, 640 * 400);
 	}
 	_smallFont->drawString(_screen, Common::String::format("Room number: %d", _room->_currentRoomNumber), 0, 4, 640, 13);
-	_smallFont->drawString(_screen, Common::String::format("Alfred pos: %d, %d (%d)", alfredState.x, alfredState.y, alfredState.y - kAlfredFrameHeight), 0, 18, 640, 13);
+	_smallFont->drawString(_screen, Common::String::format("Alfred pos: %d, %d (%d)", _alfredState.x, _alfredState.y, _alfredState.y - kAlfredFrameHeight), 0, 18, 640, 13);
 	_smallFont->drawString(_screen, Common::String::format("Frame number: %d", _chrono->getFrameCount()), 0, 30, 640, 13);
 }
 
@@ -511,13 +511,13 @@ void PelrockEngine::doAction(VerbIcon action, HotSpot *hotspot) {
 		talkTo(hotspot);
 		break;
 	case PICKUP:
-		alfredState.setState(ALFRED_INTERACTING);
+		_alfredState.setState(ALFRED_INTERACTING);
 		pickUpAndDisable(hotspot);
 		executeAction(PICKUP, hotspot);
 		break;
 	case OPEN:
 	case CLOSE:
-		alfredState.setState(ALFRED_INTERACTING);
+		_alfredState.setState(ALFRED_INTERACTING);
 	default:
 		executeAction(action, hotspot);
 		break;
@@ -542,51 +542,51 @@ void PelrockEngine::lookAt(HotSpot *hotspot) {
 }
 
 void PelrockEngine::chooseAlfredStateAndDraw() {
-	if (alfredState.idleFrameCounter++ >= kAlfredIdleAnimationFrameCount &&
-		alfredState.animState == ALFRED_IDLE &&
-		(alfredState.direction == ALFRED_LEFT || alfredState.direction == ALFRED_RIGHT)) {
-		alfredState.idleFrameCounter = 0;
-		alfredState.setState(ALFRED_COMB);
+	if (_alfredState.idleFrameCounter++ >= kAlfredIdleAnimationFrameCount &&
+		_alfredState.animState == ALFRED_IDLE &&
+		(_alfredState.direction == ALFRED_LEFT || _alfredState.direction == ALFRED_RIGHT)) {
+		_alfredState.idleFrameCounter = 0;
+		_alfredState.setState(ALFRED_COMB);
 	}
-	switch (alfredState.animState) {
+	switch (_alfredState.animState) {
 	case ALFRED_WALKING: {
 		MovementStep step = _currentContext.movementBuffer[_currentStep];
 		if (step.distanceX > 0) {
 			if (step.flags & MOVE_RIGHT) {
-				alfredState.direction = ALFRED_RIGHT;
-				alfredState.x += MIN(alfredState.movementSpeed, step.distanceX);
+				_alfredState.direction = ALFRED_RIGHT;
+				_alfredState.x += MIN(_alfredState.movementSpeed, step.distanceX);
 			}
 			if (step.flags & MOVE_LEFT) {
-				alfredState.direction = ALFRED_LEFT;
-				alfredState.x -= MIN(alfredState.movementSpeed, step.distanceX);
+				_alfredState.direction = ALFRED_LEFT;
+				_alfredState.x -= MIN(_alfredState.movementSpeed, step.distanceX);
 			}
 		}
 		if (step.distanceY > 0) {
 			if (step.flags & MOVE_DOWN) {
-				alfredState.direction = ALFRED_DOWN;
-				alfredState.y += MIN(alfredState.movementSpeed, step.distanceY);
+				_alfredState.direction = ALFRED_DOWN;
+				_alfredState.y += MIN(_alfredState.movementSpeed, step.distanceY);
 			}
 			if (step.flags & MOVE_UP) {
-				alfredState.direction = ALFRED_UP;
-				alfredState.y -= MIN(alfredState.movementSpeed, step.distanceY);
+				_alfredState.direction = ALFRED_UP;
+				_alfredState.y -= MIN(_alfredState.movementSpeed, step.distanceY);
 			}
 		}
 
 		if (step.distanceX > 0)
-			step.distanceX -= MIN(alfredState.movementSpeed, step.distanceX);
+			step.distanceX -= MIN(_alfredState.movementSpeed, step.distanceX);
 
 		if (step.distanceY > 0)
-			step.distanceY -= MIN(alfredState.movementSpeed, step.distanceY);
+			step.distanceY -= MIN(_alfredState.movementSpeed, step.distanceY);
 
 		if (step.distanceX <= 0 && step.distanceY <= 0) {
 			_currentStep++;
 			if (_currentStep >= _currentContext.movementCount) {
 				_currentStep = 0;
-				alfredState.setState(ALFRED_IDLE);
+				_alfredState.setState(ALFRED_IDLE);
 
 				if (_currentHotspot != nullptr)
-					alfredState.direction = calculateAlfredsDirection(_currentHotspot);
-				drawAlfred(_res->alfredIdle[alfredState.direction]);
+					_alfredState.direction = calculateAlfredsDirection(_currentHotspot);
+				drawAlfred(_res->alfredIdle[_alfredState.direction]);
 
 				if (_queuedAction.isQueued) {
 					doAction(_queuedAction.verb, &_room->_currentRoomHotspots[_queuedAction.hotspotIndex]);
@@ -598,61 +598,61 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			_currentContext.movementBuffer[_currentStep] = step;
 		}
 
-		Exit *exit = isExitUnder(alfredState.x, alfredState.y);
+		Exit *exit = isExitUnder(_alfredState.x, _alfredState.y);
 
 		if (exit != nullptr && exit->isEnabled) {
-			alfredState.x = exit->targetX;
-			alfredState.y = exit->targetY;
+			_alfredState.x = exit->targetX;
+			_alfredState.y = exit->targetY;
 			setScreen(exit->targetRoom, exit->dir);
 		}
 
-		if (alfredState.curFrame >= walkingAnimLengths[alfredState.direction]) {
-			alfredState.curFrame = 0;
+		if (_alfredState.curFrame >= walkingAnimLengths[_alfredState.direction]) {
+			_alfredState.curFrame = 0;
 		}
 
-		drawAlfred(_res->alfredWalkFrames[alfredState.direction][alfredState.curFrame]);
-		alfredState.curFrame++;
+		drawAlfred(_res->alfredWalkFrames[_alfredState.direction][_alfredState.curFrame]);
+		_alfredState.curFrame++;
 		break;
 	}
 	case ALFRED_TALKING:
-		if (alfredState.curFrame >= talkingAnimLengths[alfredState.direction] - 1) {
-			alfredState.curFrame = 0;
+		if (_alfredState.curFrame >= talkingAnimLengths[_alfredState.direction] - 1) {
+			_alfredState.curFrame = 0;
 		}
-		drawAlfred(_res->alfredTalkFrames[alfredState.direction][alfredState.curFrame]);
+		drawAlfred(_res->alfredTalkFrames[_alfredState.direction][_alfredState.curFrame]);
 		if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0) {
-			alfredState.curFrame++;
+			_alfredState.curFrame++;
 		}
 		break;
 	case ALFRED_COMB:
-		if (alfredState.curFrame >= 11) {
-			alfredState.setState(ALFRED_IDLE);
-			drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredIdle[alfredState.direction], alfredState.x, alfredState.y - kAlfredFrameHeight, 51, 102, 255);
+		if (_alfredState.curFrame >= 11) {
+			_alfredState.setState(ALFRED_IDLE);
+			drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredIdle[_alfredState.direction], _alfredState.x, _alfredState.y - kAlfredFrameHeight, 51, 102, 255);
 		} else {
-			drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCombFrames[alfredState.direction][alfredState.curFrame], alfredState.x, alfredState.y - kAlfredFrameHeight, 51, 102, 255);
+			drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCombFrames[_alfredState.direction][_alfredState.curFrame], _alfredState.x, _alfredState.y - kAlfredFrameHeight, 51, 102, 255);
 			if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0)
-				alfredState.curFrame++;
+				_alfredState.curFrame++;
 		}
 		break;
 	case ALFRED_INTERACTING:
-		if (alfredState.curFrame >= interactingAnimLength) {
-			alfredState.setState(ALFRED_IDLE);
+		if (_alfredState.curFrame >= interactingAnimLength) {
+			_alfredState.setState(ALFRED_IDLE);
 		} else {
-			drawAlfred(_res->alfredInteractFrames[alfredState.direction][alfredState.curFrame]);
+			drawAlfred(_res->alfredInteractFrames[_alfredState.direction][_alfredState.curFrame]);
 			if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0) {
-				alfredState.curFrame++;
+				_alfredState.curFrame++;
 			}
 		}
 		break;
 	}
 	// This if is needed to draw Alfred when idle, when the switch case results in a state change
-	if (alfredState.animState == ALFRED_IDLE) {
-		drawAlfred(_res->alfredIdle[alfredState.direction]);
+	if (_alfredState.animState == ALFRED_IDLE) {
+		drawAlfred(_res->alfredIdle[_alfredState.direction]);
 	}
 }
 
 void PelrockEngine::drawAlfred(byte *buf) {
 
-	ScaleCalculation scale = calculateScaling(alfredState.y, _room->_scaleParams);
+	ScaleCalculation scale = calculateScaling(_alfredState.y, _room->_scaleParams);
 
 	int finalHeight = kAlfredFrameHeight - scale.scaleDown + scale.scaleUp;
 	if (finalHeight <= 0) {
@@ -672,8 +672,8 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	}
 	int linesToSkip = kAlfredFrameHeight - finalHeight;
 
-	int shadowPos = alfredState.y; // - finalHeight;
-	bool shadeCharacter = _room->_pixelsShadows[shadowPos * 640 + alfredState.x] != 0xFF;
+	int shadowPos = _alfredState.y; // - finalHeight;
+	bool shadeCharacter = _room->_pixelsShadows[shadowPos * 640 + _alfredState.x] != 0xFF;
 
 	byte *finalBuf = new byte[finalWidth * finalHeight];
 
@@ -752,7 +752,7 @@ void PelrockEngine::drawAlfred(byte *buf) {
 		}
 	}
 
-	drawSpriteToBuffer(_compositeBuffer, 640, finalBuf, alfredState.x, alfredState.y - finalHeight, finalWidth, finalHeight, 255);
+	drawSpriteToBuffer(_compositeBuffer, 640, finalBuf, _alfredState.x, _alfredState.y - finalHeight, finalWidth, finalHeight, 255);
 	delete[] finalBuf;
 }
 
@@ -838,14 +838,14 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 	int hotspotIndex = isHotspotUnder(_events->_mouseX, _events->_mouseY);
 	if (hotspotIndex != -1 && !_actionPopupState.isActive) {
 
-		_actionPopupState.x = alfredState.x + kAlfredFrameWidth / 2 - kBalloonWidth / 2;
+		_actionPopupState.x = _alfredState.x + kAlfredFrameWidth / 2 - kBalloonWidth / 2;
 		if (_actionPopupState.x < 0)
 			_actionPopupState.x = 0;
 		if (_actionPopupState.x + kBalloonWidth > 640) {
 			_actionPopupState.x = 640 - kBalloonWidth;
 		}
 
-		_actionPopupState.y = alfredState.y - kAlfredFrameHeight - kBalloonHeight;
+		_actionPopupState.y = _alfredState.y - kAlfredFrameHeight - kBalloonHeight;
 		if (_actionPopupState.y < 0) {
 			_actionPopupState.y = 0;
 		}
@@ -1044,11 +1044,11 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 		drawSpriteToBuffer(_compositeBuffer, 640, _res->_verbIcons[actions[i]], posx + 20 + (i * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
 	}
 	bool itemUnder = isItemUnder(_events->_mouseX, _events->_mouseY);
-	if (_selectedInventoryItem != -1) {
+	if (_state.selectedInventoryItem != -1) {
 		if (itemUnder && shouldBlink) {
 			return;
 		}
-		drawSpriteToBuffer(_compositeBuffer, 640, _res->getInventoryObject(_selectedInventoryItem).iconData, posx + 20 + (actions.size() * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
+		drawSpriteToBuffer(_compositeBuffer, 640, _res->getInventoryObject(_state.selectedInventoryItem).iconData, posx + 20 + (actions.size() * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
 	}
 	if (_actionPopupState.curFrame < 3) {
 		_actionPopupState.curFrame++;
@@ -1130,9 +1130,9 @@ void PelrockEngine::extraScreenLoop() {
 void PelrockEngine::walkTo(int x, int y) {
 	_currentStep = 0;
 	PathContext context = {nullptr, nullptr, nullptr, 0, 0, 0};
-	findPath(alfredState.x, alfredState.y, x, y, _room->_currentRoomWalkboxes, &context);
+	findPath(_alfredState.x, _alfredState.y, x, y, _room->_currentRoomWalkboxes, &context);
 	_currentContext = context;
-	alfredState.setState(ALFRED_WALKING);
+	_alfredState.setState(ALFRED_WALKING);
 }
 
 AlfredDirection PelrockEngine::calculateAlfredsDirection(HotSpot *hotspot) {
@@ -1140,19 +1140,19 @@ AlfredDirection PelrockEngine::calculateAlfredsDirection(HotSpot *hotspot) {
 	AlfredDirection calculatedDirection = ALFRED_DOWN;
 	if (hotspot->isSprite) {
 		// Check if Alfred's left edge is past sprite's right edge
-		if (hotspot->x + hotspot->w < alfredState.x) {
+		if (hotspot->x + hotspot->w < _alfredState.x) {
 			calculatedDirection = ALFRED_LEFT; // Face LEFT
 		}
 		// Check if Alfred's right edge is before sprite's left edge
-		else if ((alfredState.x + kAlfredFrameWidth - alfredState.scaledX) < hotspot->x) {
+		else if ((_alfredState.x + kAlfredFrameWidth - _alfredState.scaledX) < hotspot->x) {
 			calculatedDirection = ALFRED_RIGHT; // Face RIGHT
 		}
 		// Alfred is horizontally overlapping with sprite
 		else {
 			// Check if Alfred's top is above sprite's bottom OR Alfred is within sprite's Y range
-			if (((alfredState.y + kAlfredFrameHeight - alfredState.scaledY) < hotspot->y) ||
-				(alfredState.y <= hotspot->y + hotspot->h &&
-				 hotspot->zOrder <= ((399 - alfredState.y) / 2) + 10)) {
+			if (((_alfredState.y + kAlfredFrameHeight - _alfredState.scaledY) < hotspot->y) ||
+				(_alfredState.y <= hotspot->y + hotspot->h &&
+				 hotspot->zOrder <= ((399 - _alfredState.y) / 2) + 10)) {
 				calculatedDirection = ALFRED_DOWN; // Face DOWN
 			} else {
 				calculatedDirection = ALFRED_UP; // Face UP
@@ -1160,16 +1160,16 @@ AlfredDirection PelrockEngine::calculateAlfredsDirection(HotSpot *hotspot) {
 		}
 	} else {
 		// Check if Alfred's left edge is past hotspot's right edge
-		if (hotspot->x + hotspot->w < alfredState.x) {
+		if (hotspot->x + hotspot->w < _alfredState.x) {
 			calculatedDirection = ALFRED_LEFT; // Face LEFT
 		}
 		// Check if Alfred's right edge is before hotspot's left edge
-		else if ((alfredState.x + kAlfredFrameWidth - alfredState.scaledX) < hotspot->x) {
+		else if ((_alfredState.x + kAlfredFrameWidth - _alfredState.scaledX) < hotspot->x) {
 			calculatedDirection = ALFRED_RIGHT; // Face RIGHT
 		}
 		// Check vertical positioning
-		else if (((alfredState.y + kAlfredFrameHeight - alfredState.scaledY) < hotspot->y) ||
-				 (alfredState.y <= hotspot->y + hotspot->h &&
+		else if (((_alfredState.y + kAlfredFrameHeight - _alfredState.scaledY) < hotspot->y) ||
+				 (_alfredState.y <= hotspot->y + hotspot->h &&
 				  (hotspot->actionFlags & 0x80) == 0x80)) {
 			calculatedDirection = ALFRED_DOWN; // Face DOWN
 		} else {
@@ -1208,8 +1208,8 @@ bool PelrockEngine::isItemUnder(int x, int y) {
 
 bool PelrockEngine::isAlfredUnder(int x, int y) {
 	// TODO: Account for scaling
-	int alfredX = alfredState.x;
-	int alfredY = alfredState.y;
+	int alfredX = _alfredState.x;
+	int alfredY = _alfredState.y;
 	int alfredW = kAlfredFrameWidth;
 	int alfredH = kAlfredFrameHeight;
 
@@ -1301,11 +1301,11 @@ void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 	}
 	_sound->stopAllSounds();
 	_currentHotspot = nullptr;
-	alfredState.direction = dir;
-	alfredState.setState(ALFRED_IDLE);
+	_alfredState.direction = dir;
+	_alfredState.setState(ALFRED_IDLE);
 	_currentStep = 0;
 	int roomOffset = number * kRoomStructSize;
-	alfredState.curFrame = 0;
+	_alfredState.curFrame = 0;
 
 	byte *palette = new byte[256 * 3];
 	_room->getPalette(&roomFile, roomOffset, palette);
@@ -1336,15 +1336,4 @@ void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 	delete[] palette;
 }
 
-Common::Error PelrockEngine::syncGame(Common::Serializer &s) {
-	// The Serializer has methods isLoading() and isSaving()
-	// if you need to specific steps; for example setting
-	// an array size after reading it's length, whereas
-	// for saving it would write the existing array's length
-	int dummy = 0;
-	s.syncAsUint32LE(dummy);
-
-	return Common::kNoError;
-}
-
 } // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 4d60d09973a..50d1affcc74 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -159,7 +159,7 @@ public:
 	Graphics::Screen *_screen = nullptr;
 	ResourceManager *_res = nullptr;
 	RoomManager *_room = nullptr;
-	AlfredState alfredState;
+	AlfredState _alfredState;
 	byte *_compositeBuffer = nullptr; // Working composition buffer
 
 	GameStateData _state;
@@ -168,8 +168,6 @@ public:
 	LargeFont *_largeFont = nullptr;
 	DoubleSmallFont *_doubleSmallFont = nullptr;
 
-	Common::Array<int> _inventoryItems;
-	int _selectedInventoryItem = -1;
 
 public:
 	PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc);
@@ -208,6 +206,10 @@ public:
 	 */
 	Common::Error syncGame(Common::Serializer &s);
 
+	void loadGame(SaveGameData &saveGame);
+
+	SaveGameData *createSaveGameData() const;
+
 	Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override {
 		Common::Serializer s(nullptr, stream);
 		return syncGame(s);
diff --git a/engines/pelrock/saveload.cpp b/engines/pelrock/saveload.cpp
index 87e3e6cabff..dff98e24e5b 100644
--- a/engines/pelrock/saveload.cpp
+++ b/engines/pelrock/saveload.cpp
@@ -18,8 +18,338 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
+#include "common/savefile.h"
 
- namespace Pelrock {
+#include "pelrock.h"
+#include "pelrock/pelrock.h"
+#include "pelrock/types.h"
 
+namespace Pelrock {
 
- } // End of namespace Pelrock
+#define SAVEGAME_CURRENT_VERSION 1
+
+// Helper functions for syncing structs
+void syncSticker(Common::Serializer &s, Sticker &sticker) {
+	s.syncAsSint32LE(sticker.stickerIndex);
+	// if(s.isLoading()) {
+
+	// }
+	// s.syncAsSint32LE(sticker.roomNumber);
+
+	// if(s.isLoading()) {
+	//     sticker.stickerData = new byte[sticker.w * sticker.h];
+	// }
+	// s.syncAsUint16LE(sticker.x);
+	// s.syncAsUint16LE(sticker.y);
+	// s.syncAsByte(sticker.w);
+	// s.syncAsByte(sticker.h);
+	// // Note: stickerData pointer not serialized - must be reconstructed on load
+}
+
+void syncExit(Common::Serializer &s, Exit &exit) {
+	s.syncAsByte(exit.index);
+	s.syncAsSint16LE(exit.x);
+	s.syncAsSint16LE(exit.y);
+	s.syncAsByte(exit.w);
+	s.syncAsByte(exit.h);
+	s.syncAsUint16LE(exit.targetRoom);
+	s.syncAsSint16LE(exit.targetX);
+	s.syncAsSint16LE(exit.targetY);
+	s.syncAsUint16LE(exit.targetDir);
+	s.syncAsByte((byte &)exit.dir);
+	s.syncAsByte(exit.isEnabled);
+}
+
+void syncExitChange(Common::Serializer &s, ExitChange &change) {
+	s.syncAsByte(change.roomNumber);
+	s.syncAsByte(change.exitIndex);
+	syncExit(s, change.exit);
+}
+
+void syncWalkBox(Common::Serializer &s, WalkBox &walkbox) {
+	s.syncAsSint16LE(walkbox.x);
+	s.syncAsSint16LE(walkbox.y);
+	s.syncAsSint16LE(walkbox.w);
+	s.syncAsSint16LE(walkbox.h);
+	s.syncAsByte(walkbox.flags);
+}
+
+void syncWalkBoxChange(Common::Serializer &s, WalkBoxChange &change) {
+	s.syncAsByte(change.roomNumber);
+	s.syncAsByte(change.walkboxIndex);
+	syncWalkBox(s, change.walkbox);
+}
+
+void syncHotSpot(Common::Serializer &s, HotSpot &hotspot) {
+	s.syncAsByte(hotspot.index);
+	s.syncAsByte(hotspot.innerIndex);
+	s.syncAsSint32LE(hotspot.id);
+	s.syncAsSint16LE(hotspot.x);
+	s.syncAsSint16LE(hotspot.y);
+	s.syncAsSint16LE(hotspot.w);
+	s.syncAsSint16LE(hotspot.h);
+	s.syncAsByte(hotspot.actionFlags);
+	s.syncAsByte(hotspot.extra);
+	s.syncAsByte((byte &)hotspot.isEnabled);
+	s.syncAsByte((byte &)hotspot.isSprite);
+	s.syncAsByte(hotspot.zOrder);
+}
+
+void syncHotSpotChange(Common::Serializer &s, HotSpotChange &change) {
+	s.syncAsByte(change.roomNumber);
+	s.syncAsByte(change.hotspotIndex);
+	syncHotSpot(s, change.hotspot);
+}
+
+void syncResetEntry(Common::Serializer &s, ResetEntry &entry) {
+	s.syncAsUint16LE(entry.room);
+	s.syncAsUint16LE(entry.offset);
+	s.syncAsByte(entry.dataSize);
+	if (s.isLoading()) {
+		entry.data = new byte[entry.dataSize];
+	}
+	for (int j = 0; j < entry.dataSize; ++j) {
+		s.syncAsByte(entry.data[j]);
+	}
+}
+
+bool syncGeneralData(Common::Serializer &s, SaveGameData *game) {
+	// Byte
+	s.syncAsByte(game->currentRoom);
+	// Uint16
+	s.syncAsUint16LE(game->alfredX);
+	s.syncAsUint16LE(game->alfredY);
+	s.syncAsByte(game->alfredDir);
+
+	return !s.err();
+}
+
+bool syncGameStateData(Common::Serializer &s, GameStateData *gameState) {
+	// GameState
+	s.syncAsUint32LE((uint32 &)gameState->stateGame);
+
+	// Inventory items
+	uint16 inventorySize = (uint16)gameState->inventoryItems.size();
+	s.syncAsUint16LE(inventorySize);
+	if (s.isLoading()) {
+		gameState->inventoryItems.resize(inventorySize);
+	}
+	for (uint16 i = 0; i < inventorySize; ++i) {
+		s.syncAsByte(gameState->inventoryItems[i]);
+	}
+	// Selected inventory item
+	s.syncAsSint16LE(gameState->selectedInventoryItem);
+
+	// Room stickers
+	uint16 stickersSize = (uint16)gameState->roomStickers.size();
+	s.syncAsUint16LE(stickersSize);
+	if (s.isSaving()) {
+		for (const auto &pair : gameState->roomStickers) {
+			byte roomNumber = pair._key;
+			s.syncAsByte(roomNumber);
+			const Common::Array<Sticker> &stickers = pair._value;
+			uint16 numStickers = (uint16)stickers.size();
+			s.syncAsUint16LE(numStickers);
+			for (uint16 i = 0; i < numStickers; ++i) {
+				Sticker sticker = stickers[i];
+				syncSticker(s, sticker);
+			}
+		}
+	} else {
+		gameState->roomStickers.clear();
+		for (uint16 idx = 0; idx < stickersSize; ++idx) {
+			byte roomNumber;
+			s.syncAsByte(roomNumber);
+			uint16 numStickers;
+			s.syncAsUint16LE(numStickers);
+			Common::Array<Sticker> stickers;
+			for (uint16 i = 0; i < numStickers; ++i) {
+				int stickerIndex = 0;
+				s.syncAsSint32LE(stickerIndex);
+				stickers.push_back(g_engine->_res->getSticker(stickerIndex));
+			}
+			gameState->roomStickers[roomNumber] = stickers;
+		}
+	}
+
+	// Room exit changes
+	uint16 numExits = (uint16)gameState->roomExitChanges.size();
+	s.syncAsUint16LE(numExits);
+	if (s.isSaving()) {
+		for (const auto &exitPair : gameState->roomExitChanges) {
+			byte roomNumber = exitPair._key;
+			s.syncAsByte(roomNumber);
+			const Common::Array<ExitChange> &exits = exitPair._value;
+			uint16 numExitsInRoom = (uint16)exits.size();
+			s.syncAsUint16LE(numExitsInRoom);
+			for (uint16 i = 0; i < numExitsInRoom; ++i) {
+				ExitChange change = exits[i];
+				syncExitChange(s, change);
+			}
+		}
+	} else {
+		gameState->roomExitChanges.clear();
+		for (uint16 idx = 0; idx < numExits; ++idx) {
+			byte roomNumber;
+			s.syncAsByte(roomNumber);
+			uint16 numExitsInRoom;
+			s.syncAsUint16LE(numExitsInRoom);
+			Common::Array<ExitChange> exits;
+			for (uint16 i = 0; i < numExitsInRoom; ++i) {
+				ExitChange change;
+				syncExitChange(s, change);
+				exits.push_back(change);
+			}
+			gameState->roomExitChanges[roomNumber] = exits;
+		}
+	}
+
+	// Room walkbox changes
+	uint16 numWalkBoxes = (uint16)gameState->roomWalkBoxChanges.size();
+	s.syncAsUint16LE(numWalkBoxes);
+	if (s.isSaving()) {
+		for (const auto &walkBoxPair : gameState->roomWalkBoxChanges) {
+			byte roomNumber = walkBoxPair._key;
+			s.syncAsByte(roomNumber);
+			const Common::Array<WalkBoxChange> &walkBoxes = walkBoxPair._value;
+			uint16 numWalkBoxesInRoom = (uint16)walkBoxes.size();
+			s.syncAsUint16LE(numWalkBoxesInRoom);
+			for (uint16 i = 0; i < numWalkBoxesInRoom; ++i) {
+				WalkBoxChange change = walkBoxes[i];
+				syncWalkBoxChange(s, change);
+			}
+		}
+	} else {
+		gameState->roomWalkBoxChanges.clear();
+		for (uint16 idx = 0; idx < numWalkBoxes; ++idx) {
+			byte roomNumber;
+			s.syncAsByte(roomNumber);
+			uint16 numWalkBoxesInRoom;
+			s.syncAsUint16LE(numWalkBoxesInRoom);
+			Common::Array<WalkBoxChange> walkBoxes;
+			for (uint16 i = 0; i < numWalkBoxesInRoom; ++i) {
+				WalkBoxChange change;
+				syncWalkBoxChange(s, change);
+				walkBoxes.push_back(change);
+			}
+			gameState->roomWalkBoxChanges[roomNumber] = walkBoxes;
+		}
+	}
+
+	// Room hotspot changes
+	uint16 hotSpotChangesSize = (uint16)gameState->roomHotSpotChanges.size();
+	s.syncAsUint16LE(hotSpotChangesSize);
+	if (s.isSaving()) {
+		for (const auto &hotSpotPair : gameState->roomHotSpotChanges) {
+			byte roomNumber = hotSpotPair._key;
+			s.syncAsByte(roomNumber);
+			const Common::Array<HotSpotChange> &hotSpots = hotSpotPair._value;
+			uint16 numHotSpots = (uint16)hotSpots.size();
+			s.syncAsUint16LE(numHotSpots);
+			for (uint16 i = 0; i < numHotSpots; ++i) {
+				HotSpotChange change = hotSpots[i];
+				syncHotSpotChange(s, change);
+			}
+		}
+	} else {
+		gameState->roomHotSpotChanges.clear();
+		for (uint16 idx = 0; idx < hotSpotChangesSize; ++idx) {
+			byte roomNumber;
+			s.syncAsByte(roomNumber);
+			uint16 numHotSpots;
+			s.syncAsUint16LE(numHotSpots);
+			Common::Array<HotSpotChange> hotSpots;
+			for (uint16 i = 0; i < numHotSpots; ++i) {
+				HotSpotChange change;
+				syncHotSpotChange(s, change);
+				hotSpots.push_back(change);
+			}
+			gameState->roomHotSpotChanges[roomNumber] = hotSpots;
+		}
+	}
+
+	// Disabled branches
+	uint16 disabledBranchesSize = (uint16)gameState->disabledBranches.size();
+	s.syncAsUint16LE(disabledBranchesSize);
+	if (s.isSaving()) {
+		for (const auto &branchPair : gameState->disabledBranches) {
+			byte roomNumber = branchPair._key;
+			s.syncAsByte(roomNumber);
+			const Common::Array<ResetEntry> &branches = branchPair._value;
+			uint16 numBranches = (uint16)branches.size();
+			s.syncAsUint16LE(numBranches);
+			for (uint16 i = 0; i < numBranches; ++i) {
+				ResetEntry entry = branches[i];
+				syncResetEntry(s, entry);
+			}
+		}
+	} else {
+		gameState->disabledBranches.clear();
+		for (uint16 idx = 0; idx < disabledBranchesSize; ++idx) {
+			byte roomNumber;
+			s.syncAsByte(roomNumber);
+			uint16 numBranches;
+			s.syncAsUint16LE(numBranches);
+			Common::Array<ResetEntry> branches;
+			for (uint16 i = 0; i < numBranches; ++i) {
+				ResetEntry entry;
+				syncResetEntry(s, entry);
+				branches.push_back(entry);
+			}
+			gameState->disabledBranches[roomNumber] = branches;
+		}
+	}
+
+	// Conversation roots state
+	s.syncBytes(gameState->conversationRootsState, 4 * 56);
+	return !s.err();
+}
+
+Common::Error syncSaveData(Common::Serializer &s, SaveGameData *gameState) {
+	if (!syncGeneralData(s, gameState))
+		return Common::Error(Common::kUnknownError, "Failed to sync general save game data.");
+
+	if (!syncGameStateData(s, gameState->gameState))
+		return Common::Error(Common::kUnknownError, "Failed to sync game state data.");
+
+	return Common::kNoError;
+}
+
+Common::Error PelrockEngine::syncGame(Common::Serializer &s) {
+	Common::Error result;
+
+	if (s.isLoading()) {
+		SaveGameData saveGame;
+		if (saveGame.gameState != nullptr)
+			delete saveGame.gameState;
+		saveGame.gameState = new GameStateData();
+		result = syncSaveData(s, &(saveGame));
+		loadGame(saveGame);
+	} else {
+		SaveGameData *saveGame = createSaveGameData();
+		result = syncSaveData(s, saveGame);
+	}
+	return result;
+}
+
+void PelrockEngine::loadGame(SaveGameData &saveGame) {
+	_alfredState.x = saveGame.alfredX;
+	_alfredState.y = saveGame.alfredY;
+	_alfredState.direction = (AlfredDirection)saveGame.alfredDir;
+	_state = *(saveGame.gameState);
+
+	setScreen(saveGame.currentRoom, _alfredState.direction);
+	_state.stateGame = GAME;
+}
+
+SaveGameData *PelrockEngine::createSaveGameData() const {
+	SaveGameData *saveGame = new SaveGameData();
+	saveGame->gameState = &g_engine->_state;
+	saveGame->currentRoom = _room->_currentRoomNumber;
+	saveGame->alfredX = _alfredState.x;
+	saveGame->alfredY = _alfredState.y;
+	saveGame->alfredDir = _alfredState.direction;
+	return saveGame;
+}
+
+} // End of namespace Pelrock
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 4df597b3717..a3e54267b8a 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -380,13 +380,16 @@ struct ResetEntry {
 	byte *data = nullptr;
 };
 
+
 struct GameStateData {
 	GameState stateGame = INTRO;
+
+	Common::Array<byte> inventoryItems;
+	int16 selectedInventoryItem = -1;
 	Common::HashMap<byte, Common::Array<Sticker>> roomStickers;
 	Common::HashMap<byte, Common::Array<ExitChange>> roomExitChanges;
 	Common::HashMap<byte, Common::Array<WalkBoxChange>> roomWalkBoxChanges;
 	Common::HashMap<byte, Common::Array<HotSpotChange>> roomHotSpotChanges;
-
 	Common::HashMap<byte, Common::Array<ResetEntry>> disabledBranches;
 
 	GameStateData() {
@@ -413,6 +416,14 @@ struct GameStateData {
 	}
 };
 
+struct SaveGameData {
+	byte currentRoom = 0;
+	uint16 alfredX = 0;
+	uint16 alfredY = 0;
+	AlfredDirection alfredDir = ALFRED_DOWN;
+	GameStateData *gameState = nullptr;
+};
+
 } // End of namespace Pelrock
 
 #endif


Commit: b0044c74d6ee1da608624ab3f20d99fe31dee5ab
    https://github.com/scummvm/scummvm/commit/b0044c74d6ee1da608624ab3f20d99fe31dee5ab
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:43+02:00

Commit Message:
PELROCK: Fix extra Alfred being show when clicking on target object

Changed paths:
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index bebdd59fd74..04b9c98ef08 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -609,9 +609,10 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		if (_alfredState.curFrame >= walkingAnimLengths[_alfredState.direction]) {
 			_alfredState.curFrame = 0;
 		}
-
-		drawAlfred(_res->alfredWalkFrames[_alfredState.direction][_alfredState.curFrame]);
-		_alfredState.curFrame++;
+		if(_alfredState.animState == ALFRED_WALKING) {// in case it changed to idle above
+			drawAlfred(_res->alfredWalkFrames[_alfredState.direction][_alfredState.curFrame]);
+			_alfredState.curFrame++;
+		}
 		break;
 	}
 	case ALFRED_TALKING:


Commit: 33b640a79e1c3f2442135bd0f5aa7112703110ec
    https://github.com/scummvm/scummvm/commit/33b640a79e1c3f2442135bd0f5aa7112703110ec
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:43+02:00

Commit Message:
PELROCK: Swap palette remaps

Changed paths:
    engines/pelrock/graphics.cpp
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index 218e52b9e39..e52fbf92f29 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -38,7 +38,7 @@ Common::Point GraphicsManager::showOverlay(int height, byte *buf) {
 	for (int x = 0; x < 640; x++) {
 		for (int y = overlayY; y < 400; y++) {
 			int index = y * 640 + x;
-			buf[index] = g_engine->_room->paletteRemaps[1][buf[index]];
+			buf[index] = g_engine->_room->paletteRemaps[0][buf[index]];
 		}
 	}
 	return Common::Point(overlayX, overlayY);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 04b9c98ef08..54277f017a6 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -748,7 +748,7 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	if (shadeCharacter) {
 		for (int i = 0; i < finalWidth * finalHeight; i++) {
 			if (finalBuf[i] != 255) {
-				finalBuf[i] = _room->paletteRemaps[0][finalBuf[i]];
+				finalBuf[i] = _room->paletteRemaps[1][finalBuf[i]];
 			}
 		}
 	}


Commit: 7701be0918f256afb67d8a58319a6691ca1d500d
    https://github.com/scummvm/scummvm/commit/7701be0918f256afb67d8a58319a6691ca1d500d
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:43+02:00

Commit Message:
PELROCK: Walk to center of hotspots

Changed paths:
    engines/pelrock/pathfinding.cpp
    engines/pelrock/pathfinding.h
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/pathfinding.cpp b/engines/pelrock/pathfinding.cpp
index c6538d91273..af0bfb8681c 100644
--- a/engines/pelrock/pathfinding.cpp
+++ b/engines/pelrock/pathfinding.cpp
@@ -50,7 +50,7 @@ Common::String printMovementFlags(uint8_t flags) {
 	return result;
 }
 
-bool findPath(int sourceX, int sourceY, int targetX, int targetY, Common::Array<WalkBox> &walkboxes, PathContext *context) {
+bool findPath(int sourceX, int sourceY, int targetX, int targetY, Common::Array<WalkBox> &walkboxes, PathContext *context, HotSpot *hotspot) {
 
 	if (context->pathBuffer == NULL) {
 		context->pathBuffer = (uint8_t *)malloc(MAX_PATH_LENGTH);
@@ -61,7 +61,7 @@ bool findPath(int sourceX, int sourceY, int targetX, int targetY, Common::Array<
 
 	int startX = sourceX;
 	int startY = sourceY;
-	Common::Point target = calculateWalkTarget(walkboxes, targetX, targetY, false, nullptr);
+	Common::Point target = calculateWalkTarget(walkboxes, targetX, targetY, 2, nullptr);
 	targetX = target.x;
 	targetY = target.y;
 	debug("Startx= %d, starty= %d, destx= %d, desty= %d", startX, startY, targetX, targetY);
@@ -140,13 +140,15 @@ Common::Point calculateWalkTarget(Common::Array<WalkBox> &walkboxes,
 	//     sourceY = hotspot->y + hotspot->height;
 	// }
 
-	if (mouseHoverState == 1) {
+	// if (mouseHoverState == 1) {
 		// Hovering over hotspot - use hotspot center-bottom
+	if(hotspot != nullptr) {
 		sourceX = hotspot->x + hotspot->w / 2;
 		sourceY = hotspot->y + hotspot->h;
-
 	}
 
+	// }
+
 	// else: use sourceX, sourceY as passed (mouse position)
 
 	// Step 2: Find nearest walkbox
diff --git a/engines/pelrock/pathfinding.h b/engines/pelrock/pathfinding.h
index 8f02b0e814b..6b7a3a628ef 100644
--- a/engines/pelrock/pathfinding.h
+++ b/engines/pelrock/pathfinding.h
@@ -27,8 +27,17 @@
 #include "pelrock/types.h"
 
 namespace Pelrock {
-bool findPath(int sourceX, int sourceY, int targetX, int targetY, Common::Array<WalkBox> &walkboxes, PathContext *context);
+bool findPath(int sourceX, int sourceY, int targetX, int targetY, Common::Array<WalkBox> &walkboxes, PathContext *context, HotSpot *hotspot = nullptr);
 
+/**
+ * Calculate the walk target point based on source coordinates and mouse hover state.
+ * @param walkboxes         Array of walkboxes in the current room.
+ * @param sourceX           X coordinate of the source point (e.g., mouse position).
+ * @param sourceY           Y coordinate of the source point (e.g., mouse position).
+ * @param mouseHoverState   State indicating what the mouse is hovering over (0 = nothing, 1 = hotspot hover, 2 = hotspot click).
+ * @param hotspot           Pointer to the hotspot being hovered over (if applicable).
+ * @return                  Calculated walk target point.
+ */
 Common::Point calculateWalkTarget(Common::Array<WalkBox> &walkboxes, int sourceX, int sourceY, bool mouseHoverState, HotSpot *hotspot);
 uint8_t findWalkboxForPoint(Common::Array<WalkBox> &walkboxes, uint16_t x, uint16_t y);
 uint8_t getAdjacentWalkbox(Common::Array<WalkBox> &walkboxes, uint8_t current_box_index);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 54277f017a6..aab309f1e0e 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1131,7 +1131,7 @@ void PelrockEngine::extraScreenLoop() {
 void PelrockEngine::walkTo(int x, int y) {
 	_currentStep = 0;
 	PathContext context = {nullptr, nullptr, nullptr, 0, 0, 0};
-	findPath(_alfredState.x, _alfredState.y, x, y, _room->_currentRoomWalkboxes, &context);
+	findPath(_alfredState.x, _alfredState.y, x, y, _room->_currentRoomWalkboxes, &context, _currentHotspot);
 	_currentContext = context;
 	_alfredState.setState(ALFRED_WALKING);
 }


Commit: c80e1a25bc49f46143c7b061be5f1542cf861390
    https://github.com/scummvm/scummvm/commit/c80e1a25bc49f46143c7b061be5f1542cf861390
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:44+02:00

Commit Message:
PELROCK: Use inventory item with hotspot

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/actions.h
    engines/pelrock/offsets.h
    engines/pelrock/pathfinding.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 30127b279fe..b61f620fca4 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -19,6 +19,8 @@
  *
  */
 
+#include "graphics/paletteman.h"
+
 #include "pelrock/actions.h"
 #include "pelrock.h"
 #include "pelrock/offsets.h"
@@ -38,27 +40,60 @@ const ActionEntry actionTable[] = {
 	{4, PICKUP, &PelrockEngine::pickUpBrick},    // Brick
 
 	// Generic handlers
-	{WILDCARD, PICKUP, &PelrockEngine::noOp}, // Generic pickup action
-	{WILDCARD, TALK, &PelrockEngine::noOp},   // Generic talk action
-	{WILDCARD, WALK, &PelrockEngine::noOp},   // Generic walk action
-	{WILDCARD, LOOK, &PelrockEngine::noOp},   // Generic look action
-	{WILDCARD, PUSH, &PelrockEngine::noOp},   // Generic push action
-	{WILDCARD, PULL, &PelrockEngine::noOp},   // Generic pull action
-	{WILDCARD, OPEN, &PelrockEngine::noOp},   // Generic open action
-	{WILDCARD, CLOSE, &PelrockEngine::noOp},  // Generic close action
+	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
+	{WILDCARD, TALK, &PelrockEngine::noOpAction},   // Generic talk action
+	{WILDCARD, WALK, &PelrockEngine::noOpAction},   // Generic walk action
+	{WILDCARD, LOOK, &PelrockEngine::noOpAction},   // Generic look action
+	{WILDCARD, PUSH, &PelrockEngine::noOpAction},   // Generic push action
+	{WILDCARD, PULL, &PelrockEngine::noOpAction},   // Generic pull action
+	{WILDCARD, OPEN, &PelrockEngine::noOpAction},   // Generic open action
+	{WILDCARD, CLOSE, &PelrockEngine::noOpAction},  // Generic close action
 
 	// End marker
 	{WILDCARD, NO_ACTION, nullptr}};
 
-// Handler implementations
+
+const CombinationEntry combinationTable[] = {
+	{2, 281, &PelrockEngine::useCardWithATM}, // Use ATM Card with ATM
+	// End marker
+	{WILDCARD, WILDCARD, nullptr}
+};
+
+	// Handler implementations
+void PelrockEngine::openDoor(HotSpot *hotspot) {
+	_room->addSticker(_res->getSticker(93), false);
+	_room->enableExit(0, false);
+}
+
 void PelrockEngine::openDrawer(HotSpot *hotspot) {
 	if (_room->hasSticker(91)) {
-		_dialog->say(_res->_ingameTexts[ALREADY_OPENED_M]);
+		_dialog->say(_res->_ingameTexts[YA_ABIERTO_M]);
 		return;
 	}
 	_room->addSticker(_res->getSticker(91));
 	// TODO: Check if we need to disable the hotspot
-	_room->disableHotspot(hotspot);
+	if(_room->findHotspotByExtra(1)->isEnabled &&
+	   _room->findHotspotByExtra(2)->isEnabled &&
+	   _room->findHotspotByExtra(3)->isEnabled) {
+	   _room->disableHotspot(hotspot);
+	}
+}
+
+void PelrockEngine::closeDoor(HotSpot *hotspot) {
+	_room->removeSticker(93);
+	_room->disableExit(0, false);
+}
+
+void PelrockEngine::pickUpPhoto(HotSpot *hotspot) {
+	_room->enableHotspot(_room->findHotspotByExtra(261));
+}
+
+void PelrockEngine::pickYellowBook(HotSpot *hotspot) {
+	_room->addSticker(_res->getSticker(95));
+}
+
+void PelrockEngine::pickUpBrick(HotSpot *hotspot) {
+	_room->addSticker(_res->getSticker(133));
 }
 
 void PelrockEngine::closeDrawer(HotSpot *hotspot) {
@@ -66,20 +101,41 @@ void PelrockEngine::closeDrawer(HotSpot *hotspot) {
 	_room->enableHotspot(hotspot);
 }
 
-void PelrockEngine::openDoor(HotSpot *hotspot) {
-	_room->addSticker(_res->getSticker(93), false);
-	_room->_currentRoomExits[0].isEnabled = true;
-}
 
-void PelrockEngine::closeDoor(HotSpot *hotspot) {
-	_room->removeSticker(93);
-	_room->_currentRoomExits[0].isEnabled = false;
+void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
+	debug("Withdrawing money from ATM using card (inv obj %d)", inventoryObject);
+	if(_state.JEFE_INGRESA_PASTA) {
+		_state.JEFE_INGRESA_PASTA = 0;
+		addInventoryItem(75);
+	}
+	else {
+		int billCount = 0;
+		for(int i = 0; i < _state.inventoryItems.size(); i++) {
+			if(_state.inventoryItems[i] == 5) {
+				billCount++;
+			}
+		}
+		if(billCount < 13) {
+			addInventoryItem(5); // 1000 pesetas bill
+			_dialog->say(_res->_ingameTexts[109]);
+		}
+		else {
+			_dialog->say(_res->_ingameTexts[NOMONEY_LEFT]);
+		}
+
+	}
 }
 
 void PelrockEngine::pickUpAndDisable(HotSpot *hotspot) {
+	addInventoryItem(hotspot->extra);
+	_room->disableHotspot(hotspot);
+}
+
+void PelrockEngine::addInventoryItem(int item) {
 	if (_state.inventoryItems.size() == 0) {
-		_state.selectedInventoryItem = hotspot->extra;
+		_state.selectedInventoryItem = item;
 	}
+	_flashingIcon = item;
 	int frameCounter = 0;
 	while (frameCounter < kIconFlashDuration) {
 		_events->pollEvent();
@@ -91,31 +147,47 @@ void PelrockEngine::pickUpAndDisable(HotSpot *hotspot) {
 		}
 		g_system->delayMillis(10);
 	}
-	_state.inventoryItems.push_back(hotspot->extra);
-	_room->disableHotspot(hotspot);
+	_state.addInventoryItem(item);
 }
 
-void PelrockEngine::pickUpPhoto(HotSpot *hotspot) {
-	_room->enableHotspot(_room->findHotspotByExtra(261));
-}
 
-void PelrockEngine::pickYellowBook(HotSpot *hotspot) {
-	_room->addSticker(_res->getSticker(95));
+void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte rootIndex) {
+	if(actionTrigger == 328) {
+		debug("Disabling root %d in room %d", rootIndex, room);
+		_state.setRootDisabledState(room, rootIndex, true);
+	}
 }
 
-void PelrockEngine::pickUpBrick(HotSpot *hotspot) {
-	_room->addSticker(_res->getSticker(133));
+void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
+	debug("Performing action trigger: %d", actionTrigger);
+	switch (actionTrigger) {
+	case 257:
+		byte *palette = new byte[768];
+		if (_extraScreen == nullptr) {
+			_extraScreen = new byte[640 * 400];
+		}
+		_res->getExtraScreen(9, _extraScreen, palette);
+
+		g_system->getPaletteManager()->setPalette(palette, 0, 256);
+		extraScreenLoop();
+
+		_dialog->say(_res->_ingameTexts[SOHOT]);
+		_screen->markAllDirty();
+		_screen->update();
+
+		delete[] palette;
+		break;
+	}
 }
 
-void PelrockEngine::noOp(HotSpot *hotspot) {
+
+void PelrockEngine::noOpAction(HotSpot *hotspot) {
 	// Do nothing
 }
 
-void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte rootIndex) {
-	if(actionTrigger == 328) {
-		debug("Disabling root %d in room %d", rootIndex, room);
-		_state.setRootDisabledState(room, rootIndex, true);
-	}
+
+void PelrockEngine::noOpItem(int item, HotSpot *hotspot) {
+	// Do nothing
 }
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/actions.h b/engines/pelrock/actions.h
index 5bd70abe8e4..a5eb7a62804 100644
--- a/engines/pelrock/actions.h
+++ b/engines/pelrock/actions.h
@@ -35,8 +35,15 @@ struct ActionEntry {
 	void (PelrockEngine::*handler)(HotSpot *);
 };
 
+struct CombinationEntry {
+	int inventoryObject;
+	int hotspotExtra;
+	void (PelrockEngine::*handler)(int, HotSpot *);
+};
+
 // Action table for all rooms
 extern const ActionEntry actionTable[];
+extern const CombinationEntry combinationTable[];
 
 } // End of namespace Pelrock
 #endif // PELROCK_ACTIONS_H
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index aed6086615e..a76ad6cb5f5 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -120,9 +120,9 @@ const byte pegatina_rooms[140] = {
 };
 
 enum TextIndices {
-	THEYRE_CLOSED,
-	NOTAVAILABLE_TODAY,
-	ALREADY_OPENED_M,
+	ESTAN_CERRADOS,
+	HOY_NO_DISPONIBLES,
+	YA_ABIERTO_M,
 	ALREADY_CLOSED_M,
 	ALREADY_OPENED_F,
 	ALREADY_CLOSED_F,
@@ -154,6 +154,29 @@ enum TextIndices {
 	HEY_DONTSTART,
 	HOTONE_MOCKING,
 	SHUTUP,
+	NO_YOU,
+	TOO_MUCH_CANT_THINK,
+	A_LITTLE_RESPECT,
+	NO_THEY_MAKEYOU_FAT,
+	CLOCK_CHANGED,
+	CORRESPONDENCIA_AJENA,
+	ANDA,
+	TUCREES,
+	NOESAMIAQUIENDEBES,
+	AQUIENENTONCES,
+	LIBROSSECRETOS,
+	VENGA_ACA,
+	TODOS,
+	EL_LIBRO_NOESTA_AQUI,
+	TENDRE_DEJAR_LIBRO,
+	TRABAJARIA_MEJOR_SI_NO_ME_MOLESTARA,
+	REGALO_LIBRO_RECETAS,
+	YSI_METIRA_MAQUINA,
+	QUITA_ESAS_MANOS,
+	QUEASCO,
+	QUESESTO_RECETA,
+	YAESTA_ABIERTO,
+
 };
 
 // Description offsets relative to DESCRIPTION_BASE_OFFSET
diff --git a/engines/pelrock/pathfinding.cpp b/engines/pelrock/pathfinding.cpp
index af0bfb8681c..efe1a28e600 100644
--- a/engines/pelrock/pathfinding.cpp
+++ b/engines/pelrock/pathfinding.cpp
@@ -142,10 +142,10 @@ Common::Point calculateWalkTarget(Common::Array<WalkBox> &walkboxes,
 
 	// if (mouseHoverState == 1) {
 		// Hovering over hotspot - use hotspot center-bottom
-	if(hotspot != nullptr) {
-		sourceX = hotspot->x + hotspot->w / 2;
-		sourceY = hotspot->y + hotspot->h;
-	}
+	// if(hotspot != nullptr) {
+	// 	sourceX = hotspot->x + hotspot->w / 2;
+	// 	sourceY = hotspot->y + hotspot->h;
+	// }
 
 	// }
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index aab309f1e0e..19f6a47d676 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -252,30 +252,22 @@ bool PelrockEngine::renderScene(int overlayMode) {
 	return false;
 }
 
-void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
-	debug("Performing action trigger: %d", actionTrigger);
-	switch (actionTrigger) {
-	case 257:
-		byte *palette = new byte[768];
-		if (_extraScreen == nullptr) {
-			_extraScreen = new byte[640 * 400];
-		}
-		_res->getExtraScreen(9, _extraScreen, palette);
-
-		g_system->getPaletteManager()->setPalette(palette, 0, 256);
-		extraScreenLoop();
-
-		_dialog->say(_res->_ingameTexts[SOHOT]);
-		_screen->markAllDirty();
-		_screen->update();
+void PelrockEngine::executeAction(VerbIcon action, HotSpot *hotspot) {
 
-		delete[] palette;
-		break;
+	if(action == ITEM) {
+		int inventoryObject = _state.selectedInventoryItem;
+		for (const CombinationEntry *entry = combinationTable; entry->handler != nullptr; entry++) {
+			if (entry->inventoryObject == inventoryObject && entry->hotspotExtra == hotspot->extra) {
+				(this->*(entry->handler))(inventoryObject, hotspot);
+				return;
+			}
+		}
+		warning("No handler for using inventory object %d with hotspot %d", inventoryObject, hotspot->extra);
+		return;
 	}
-}
 
 
-void PelrockEngine::executeAction(VerbIcon action, HotSpot *hotspot) {
+
 	for (const ActionEntry *entry = actionTable; entry->handler != nullptr; entry++) {
 		if (entry->action == action && entry->hotspotExtra == hotspot->extra) {
 			// Found exact match - call the handler
@@ -284,7 +276,7 @@ void PelrockEngine::executeAction(VerbIcon action, HotSpot *hotspot) {
 		}
 	}
 
-	// Try wildcard match (hotspotExtra == 0 means "any hotspot")
+	// Try wildcard match (hotspotExtra == -1 means "any hotspot")
 	for (const ActionEntry *entry = actionTable; entry->handler != nullptr; ++entry) {
 		if (entry->action == action && entry->hotspotExtra == WILDCARD) {
 			(this->*(entry->handler))(hotspot);
@@ -1096,7 +1088,7 @@ void PelrockEngine::animateTalkingNPC(Sprite *animSet) {
 
 void PelrockEngine::pickupIconFlash() {
 	_graphics->showOverlay(65, _compositeBuffer);
-	InventoryObject item = _res->getInventoryObject(_currentHotspot->extra);
+	InventoryObject item = _res->getInventoryObject(_flashingIcon);
 	if (_chrono->getFrameCount() % kIconBlinkPeriod == 0) {
 		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 5, 400 - 60 - 5, 60, 60, 1);
 	}
@@ -1185,11 +1177,19 @@ VerbIcon PelrockEngine::isActionUnder(int x, int y) {
 		return NO_ACTION;
 	}
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
-	for (int i = 0; i < actions.size(); i++) {
+	int loopEnd = _state.selectedInventoryItem != -1 ? actions.size() + 1 : actions.size();
+	for (int i = 0; i < loopEnd; i++) {
 		int actionX = _actionPopupState.x + 20 + (i * (kVerbIconWidth + 2));
 		int actionY = _actionPopupState.y + 20;
 		Common::Rect actionRect = Common::Rect(actionX, actionY, actionX + kVerbIconWidth, actionY + kVerbIconHeight);
-		if (actionRect.contains(x, y)) {
+
+		if(i == actions.size()) {
+			// Check inventory item
+			if (actionRect.contains(x, y)) {
+				return ITEM;
+			}
+		}
+		else if (actionRect.contains(x, y)) {
 			return actions[i];
 		}
 	}
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 50d1affcc74..b1427cf4677 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -133,9 +133,9 @@ private:
 	ActionPopupState _actionPopupState;
 
 	HotSpot *_currentHotspot = nullptr;
+	int _flashingIcon = -1;
 
 	Common::Point _curWalkTarget;
-
 	QueuedAction _queuedAction;
 
 	bool showShadows = false;
@@ -222,6 +222,7 @@ public:
 	void setScreen(int s, AlfredDirection dir);
 	bool renderScene(int overlayMode = OVERLAY_NONE);
 
+	void addInventoryItem(int item);
 	// Actions
 	void performActionTrigger(uint16 actionTrigger);
 	void dialogActionTrigger(uint16 actionTrigger, byte room, byte rootIndex);
@@ -235,7 +236,9 @@ public:
 	void pickUpPhoto(HotSpot *hotspot);
 	void pickYellowBook(HotSpot *hotspot);
 	void pickUpBrick(HotSpot *hotspot);
-	void noOp(HotSpot *hotspot);
+	void noOpAction(HotSpot *hotspot);
+	void noOpItem(int item, HotSpot *hotspot);
+	void useCardWithATM(int inventoryObject, HotSpot *hotspot);
 };
 
 extern PelrockEngine *g_engine;
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 9cc6a11d670..555cdabe24c 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -116,8 +116,18 @@ bool RoomManager::hasSticker(int index) {
 	return false;
 }
 
-void RoomManager::changeExit(Exit exit) {
-	g_engine->_state.roomExitChanges[_currentRoomNumber].push_back({_currentRoomNumber, exit.index, exit});
+void RoomManager::changeExit(int index, bool enabled, bool persist) {
+	_currentRoomExits[index].isEnabled = enabled;
+	if(persist)
+		g_engine->_state.roomExitChanges[_currentRoomNumber].push_back({_currentRoomNumber, _currentRoomExits[index].index, _currentRoomExits[index]});
+}
+
+void RoomManager::disableExit(int index, bool persist) {
+	changeExit(index, false, persist);
+}
+
+void RoomManager::enableExit(int index, bool persist) {
+	changeExit(index, true, persist);
 }
 
 void RoomManager::changeWalkBox(WalkBox walkbox) {
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 2a3ace33404..6814dad01aa 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -45,7 +45,9 @@ public:
 	void addSticker(Sticker sticker, bool persist = true);
 	void removeSticker(int index);
 	bool hasSticker(int index);
-	void changeExit(Exit exit);
+	void changeExit(int index, bool enabled, bool persist = true);
+	void disableExit(int index, bool persist);
+	void enableExit(int index, bool persist);
 	void changeWalkBox(WalkBox walkbox);
 	void changeHotSpot(HotSpot hotspot);
 	/**
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index a3e54267b8a..bdfbf28aeaf 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -44,6 +44,7 @@ enum VerbIcon {
 	PULL,
 	OPEN,
 	CLOSE,
+	ITEM,
 	UNKNOWN,
 	NO_ACTION
 };
@@ -382,6 +383,8 @@ struct ResetEntry {
 
 
 struct GameStateData {
+	bool JEFE_INGRESA_PASTA = false;
+
 	GameState stateGame = INTRO;
 
 	Common::Array<byte> inventoryItems;
@@ -405,6 +408,18 @@ struct GameStateData {
 		disabledBranches[entry.room].push_back(entry);
 	}
 
+	void addInventoryItem(int id) {
+		inventoryItems.push_back(id);
+	}
+	void removeInventoyItem(int id) {
+		for(int i = 0; i < inventoryItems.size(); i++) {
+			if (inventoryItems[i] == id) {
+				inventoryItems.remove_at(i);
+				return;
+			}
+		}
+	}
+
 	byte *conversationRootsState = new byte[4 * 56];
 
 	bool getRootDisabledState(byte room, byte root) const {


Commit: d8d5873eee038cc505826cad57fcd8fb00ebcec5
    https://github.com/scummvm/scummvm/commit/d8d5873eee038cc505826cad57fcd8fb00ebcec5
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:44+02:00

Commit Message:
PELROCK: Wrong combination responses

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/util.cpp
    engines/pelrock/util.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index b61f620fca4..abbcb8f1825 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -59,7 +59,6 @@ const CombinationEntry combinationTable[] = {
 	{WILDCARD, WILDCARD, nullptr}
 };
 
-	// Handler implementations
 void PelrockEngine::openDoor(HotSpot *hotspot) {
 	_room->addSticker(_res->getSticker(93), false);
 	_room->enableExit(0, false);
@@ -117,12 +116,11 @@ void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
 		}
 		if(billCount < 13) {
 			addInventoryItem(5); // 1000 pesetas bill
-			_dialog->say(_res->_ingameTexts[109]);
+			_dialog->say(_res->_ingameTexts[TEAPETECE_BUENRATO]);
 		}
 		else {
 			_dialog->say(_res->_ingameTexts[NOMONEY_LEFT]);
 		}
-
 	}
 }
 
@@ -182,12 +180,14 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 
 
 void PelrockEngine::noOpAction(HotSpot *hotspot) {
-	// Do nothing
 }
 
 
 void PelrockEngine::noOpItem(int item, HotSpot *hotspot) {
-	// Do nothing
+	//154 to 169
+	debug("No-op item %d with hotspot %d", item, hotspot->extra);
+	byte response = (byte)getRandomNumber(12);
+	_dialog->say(_res->_ingameTexts[154 + response]);
 }
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index a76ad6cb5f5..f6343fa48d1 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -176,7 +176,145 @@ enum TextIndices {
 	QUEASCO,
 	QUESESTO_RECETA,
 	YAESTA_ABIERTO,
-
+	VAESTAR_POCOFUERTE,
+	CUENTOPARECIDO,
+	COSASAPRENDIDO,
+	PERIODICOSENSACIONALISTA,
+	HOJAENTREPAGINAS,
+	NOENTIENDONADA,
+	NOTENGODINERO,
+	CUESTA1000,
+	AQUITIENE,
+	MUYBIEN,
+	YASEEGIPCIO,
+	QUELASTIMA_NOSEEGIPCIO,
+	FORMULAVIAJETIEMPO,
+	PARECECERRADO,
+	NOVIO2METROS,
+	GRANIDEA,
+	SELORECOMIENDO,
+	OIGAUSTED,
+	ESAMI,
+	VENGAAHORAMISMO,
+	CUIDADOIMPRUDENTE,
+	QUEOSCUROESTAESTO,
+	MENUDAAVENTURA,
+	NECESITOGASOLINA,
+	YANOSEHACEONCOMOANTES,
+	NADIELOHAVISTO,
+	AYAYAY,
+	OIGAUSTED2,
+	LEESTOYVIGILANDO,
+	OIGA,
+	CAPITULOPARADOJAS,
+	HAYQUECELEBRARLO,
+	PESADEMASIADO,
+	NINGUNATEMAAPROPIADO,
+	PARAQUECOGERBARRO,
+	BUENOCOGEREUNPOCO,
+	ABSOLUTAMENTECERRADO,
+	NOSETEOCURRAACERCARTE,
+	PUERTAAUTENTICA_IZQUIERDA,
+	OHMISALVADOR,
+	VOYPORTI_PRINCESA,
+	AMISBRAZOS,
+	DIOSMIOQUEESESTO,
+	QUEPASA,
+	OLVIDECERRARTRAMPILLA,
+	NOTEPREOCUPES_VOLVERE,
+	ALACONUSTED,
+	MEMEO,
+	POR5MINUTOS,
+	TALUEGOLUCAS,
+	SISUPIERA_COMBINACION,
+	PARECE_COMBINACION_CAJAFUERTE,
+	GRANCANTIDAD_DINERO,
+	TEAPETECE_BUENRATO,
+	YLOSCONDONES,
+	QUEASCO_CASIMEMEA,
+	HECHOELPRIMO,
+	MEHANTOMADO_EL_PELO,
+	PESADO_UNRATO,
+	TRAIDOR,
+	TUTIA,
+	LATUYA,
+	GORDO,
+	FIDEO,
+	LIMPIACULO,
+	CONTUTURBANTE,
+	OSO,
+	COMADREJA,
+	CABEZON,
+	TUABUELO,
+	TUMUJER,
+	PERDEDOR,
+	SOYMEJORQUETU,
+	TRAMPOSO,
+	MALPERDEDOR,
+	PARAUNAVEZ,
+	MEJORMELARGO,
+	NOTENGOPARCHES,
+	NOTENGOPEGAMENTO,
+	MUNECO_ARREGLADO,
+	MAREDEDEU,
+	PROBARLIBRO,
+	PRACTICAR_MAS,
+	AQUI_NO_NECESITO,
+	DIOSHALCON,
+	OHGRANOSIRIS,
+	HEMEAQUI,
+	OHSOBEK,
+	OHTOTH,
+	TODOSLASCOSAS,
+	HELLEGADOPURO,
+	DIOSDELATURBULENCIA,
+	OHANUBIS,
+	HEVENIDO,
+	HELLEGADOATI,
+	OHPTHA,
+	LASPUERTASDELCIELO,
+	VAYASUENHO,
+	PARAQUE,
+	YESO,
+	UNPOCODESESPERADO,
+	COMBINACIONESMEJORES,
+	NOSEQUEPRETENDES_CONESO,
+	COMO,
+	MUCHOSENTIDO,
+	PORPROBAR,
+	NOLOENTIENDO,
+	PARAESONOSIRVE,
+	PRUEBAOTRACOSA,
+	SIHOMBREQUEMAS,
+	NOSEQUEPRETENDES,
+	COSASRARAS,
+	ARTE_O_LOCURA,
+	UTILIDADES,
+	TITULOJUEGO,
+	MENSAJEOTRAEPOCA,
+	NOERAAUTENTICO,
+	PRIMERINGREDIENTE,
+	DOSINGREDIENTES,
+	TRESINGREDIENTES,
+	CUATROINGREDIENTES,
+	DEACUERDO,
+	GAMBERROS,
+	QUIENYO,
+	PINTA_BUENAPERSONA,
+	DEMO_FINAL,
+	DIOSHALCON_2,
+	GRANOSIRIS,
+	HEMEAQUI_ISIS,
+	OHSOBEK_2,
+	OHTOTH_2,
+	PROTEGEN_MI_CUERPO,
+	HELLEGADO_PURO,
+	DIOSDELATURBULENCIA_2,
+	OHANUBIS_2,
+	HEVENIDO_2,
+	HELLEGADO_ATI,
+	OHPTHA_2,
+	LASPUERTAS_DELCIELO,
 };
 
 // Description offsets relative to DESCRIPTION_BASE_OFFSET
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 19f6a47d676..80b49229074 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -262,6 +262,7 @@ void PelrockEngine::executeAction(VerbIcon action, HotSpot *hotspot) {
 				return;
 			}
 		}
+		noOpItem(inventoryObject, hotspot);
 		warning("No handler for using inventory object %d with hotspot %d", inventoryObject, hotspot->extra);
 		return;
 	}
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index cf2503cd5aa..7ead6a86730 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -323,4 +323,8 @@ byte decodeChar(byte b) {
 	}
 }
 
+Common::StringArray arrayOf(Common::String str) {
+	return Common::StringArray(1, str);
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index 59ae1fb4687..62a70260476 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -43,6 +43,7 @@ void drawText(Graphics::Font *font, Common::String text, int x, int y, int w, by
 Common::String joinStrings(const Common::Array<Common::String> &strings, const Common::String &separator);
 void drawPos(Graphics::ManagedSurface *surface, int x, int y, byte color);
 byte decodeChar(byte b);
+Common::StringArray arrayOf(Common::String str);
 
 static const int special_chars[] = {
 	168, // inverted ?


Commit: 1481f311477a2bb44b9b6d4a518254e927784a8c
    https://github.com/scummvm/scummvm/commit/1481f311477a2bb44b9b6d4a518254e927784a8c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:44+02:00

Commit Message:
PELROCK: Pickup sauces

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index abbcb8f1825..2a391eae874 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -37,7 +37,14 @@ const ActionEntry actionTable[] = {
 	{268, CLOSE, &PelrockEngine::closeDoor},
 	{3, PICKUP, &PelrockEngine::pickUpPhoto},
 	{0, PICKUP, &PelrockEngine::pickYellowBook}, // Generic pickup for other items
+	// Room 1
 	{4, PICKUP, &PelrockEngine::pickUpBrick},    // Brick
+	// Room 2
+	{282, OPEN, &PelrockEngine::openMcDoor},
+	{282, CLOSE, &PelrockEngine::closeMcDoor},
+	{60, PICKUP, &PelrockEngine::grabKetchup},
+	{61, PICKUP, &PelrockEngine::grabMustard},
+	{62, PICKUP, &PelrockEngine::grabSpicey},
 
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
@@ -60,7 +67,7 @@ const CombinationEntry combinationTable[] = {
 };
 
 void PelrockEngine::openDoor(HotSpot *hotspot) {
-	_room->addSticker(_res->getSticker(93), false);
+	_room->addSticker(93, false);
 	_room->enableExit(0, false);
 }
 
@@ -69,7 +76,7 @@ void PelrockEngine::openDrawer(HotSpot *hotspot) {
 		_dialog->say(_res->_ingameTexts[YA_ABIERTO_M]);
 		return;
 	}
-	_room->addSticker(_res->getSticker(91));
+	_room->addSticker(91);
 	// TODO: Check if we need to disable the hotspot
 	if(_room->findHotspotByExtra(1)->isEnabled &&
 	   _room->findHotspotByExtra(2)->isEnabled &&
@@ -88,11 +95,11 @@ void PelrockEngine::pickUpPhoto(HotSpot *hotspot) {
 }
 
 void PelrockEngine::pickYellowBook(HotSpot *hotspot) {
-	_room->addSticker(_res->getSticker(95));
+	_room->addSticker(95);
 }
 
 void PelrockEngine::pickUpBrick(HotSpot *hotspot) {
-	_room->addSticker(_res->getSticker(133));
+	_room->addSticker(133);
 }
 
 void PelrockEngine::closeDrawer(HotSpot *hotspot) {
@@ -100,7 +107,6 @@ void PelrockEngine::closeDrawer(HotSpot *hotspot) {
 	_room->enableHotspot(hotspot);
 }
 
-
 void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
 	debug("Withdrawing money from ATM using card (inv obj %d)", inventoryObject);
 	if(_state.JEFE_INGRESA_PASTA) {
@@ -124,11 +130,41 @@ void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
 	}
 }
 
+void PelrockEngine::openMcDoor(HotSpot *hotspot) {
+	if( _room->hasSticker(7)) {
+		_dialog->say(_res->_ingameTexts[ALREADY_OPENED_F]);
+		return;
+	}
+	_room->enableExit(2);
+	_room->addSticker(7);
+}
+
+void PelrockEngine::closeMcDoor(HotSpot *hotspot) {
+	if( !_room->hasSticker(7)) {
+		_dialog->say(_res->_ingameTexts[ALREADY_CLOSED_F]);
+		return;
+	}
+	_room->disableExit(2);
+	_room->removeSticker(7);
+}
+
 void PelrockEngine::pickUpAndDisable(HotSpot *hotspot) {
 	addInventoryItem(hotspot->extra);
 	_room->disableHotspot(hotspot);
 }
 
+void PelrockEngine::grabKetchup(HotSpot *hotspot) {
+	_room->addSticker(70);
+}
+
+void PelrockEngine::grabMustard(HotSpot *hotspot) {
+	_room->addSticker(72);
+}
+
+void PelrockEngine::grabSpicey(HotSpot *hotspot) {
+	_room->addSticker(71);
+}
+
 void PelrockEngine::addInventoryItem(int item) {
 	if (_state.inventoryItems.size() == 0) {
 		_state.selectedInventoryItem = item;
@@ -186,6 +222,7 @@ void PelrockEngine::noOpAction(HotSpot *hotspot) {
 void PelrockEngine::noOpItem(int item, HotSpot *hotspot) {
 	//154 to 169
 	debug("No-op item %d with hotspot %d", item, hotspot->extra);
+	_alfredState.direction = ALFRED_DOWN;
 	byte response = (byte)getRandomNumber(12);
 	_dialog->say(_res->_ingameTexts[154 + response]);
 }
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 80b49229074..796bbaf4723 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -530,6 +530,7 @@ void PelrockEngine::talkTo(HotSpot *hotspot) {
 }
 
 void PelrockEngine::lookAt(HotSpot *hotspot) {
+	debug("Looking at hotspot %d with extra %d", hotspot->index, hotspot->extra);
 	_dialog->sayAlfred(_room->_currentRoomDescriptions[_currentHotspot->index]);
 	_actionPopupState.isActive = false;
 }
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index b1427cf4677..29cbab2fd12 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -233,12 +233,17 @@ public:
 	void openDoor(HotSpot *hotspot);
 	void closeDoor(HotSpot *hotspot);
 	void pickUpAndDisable(HotSpot *hotspot);
+	void grabKetchup(HotSpot *hotspot);
+	void grabMustard(HotSpot *hotspot);
+	void grabSpicey(HotSpot *hotspot);
 	void pickUpPhoto(HotSpot *hotspot);
 	void pickYellowBook(HotSpot *hotspot);
 	void pickUpBrick(HotSpot *hotspot);
 	void noOpAction(HotSpot *hotspot);
 	void noOpItem(int item, HotSpot *hotspot);
 	void useCardWithATM(int inventoryObject, HotSpot *hotspot);
+	void openMcDoor(HotSpot *hotspot);
+	void closeMcDoor(HotSpot *hotspot);
 };
 
 extern PelrockEngine *g_engine;
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 555cdabe24c..6eef9f6742c 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -88,7 +88,8 @@ void RoomManager::getBackground(Common::File *roomFile, int roomOffset, byte *ba
 	}
 }
 
-void RoomManager::addSticker(Sticker sticker, bool persist) {
+void RoomManager::addSticker(int stickerId, bool persist) {
+	Sticker sticker = g_engine->_res->getSticker(stickerId);
 	if (persist)
 		g_engine->_state.roomStickers[_currentRoomNumber].push_back(sticker);
 	else
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 6814dad01aa..719a251db63 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -42,12 +42,12 @@ public:
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
 
 	/** Methods to modify room data at runtime **/
-	void addSticker(Sticker sticker, bool persist = true);
+	void addSticker(int stickerId, bool persist = true);
 	void removeSticker(int index);
 	bool hasSticker(int index);
 	void changeExit(int index, bool enabled, bool persist = true);
-	void disableExit(int index, bool persist);
-	void enableExit(int index, bool persist);
+	void disableExit(int index, bool persist = true);
+	void enableExit(int index, bool persist = true);
 	void changeWalkBox(WalkBox walkbox);
 	void changeHotSpot(HotSpot hotspot);
 	/**


Commit: dec2e69b73fc0dbc19aaa2f97f0b7d424af54122
    https://github.com/scummvm/scummvm/commit/dec2e69b73fc0dbc19aaa2f97f0b7d424af54122
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:45+02:00

Commit Message:
PELROCK: open/close doors

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 2a391eae874..77d5aeac3cf 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -21,14 +21,17 @@
 
 #include "graphics/paletteman.h"
 
-#include "pelrock/actions.h"
 #include "pelrock.h"
+#include "pelrock/actions.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/util.h"
 
 namespace Pelrock {
 
+#define MASCULINE true
+#define FEMININE false
+
 const ActionEntry actionTable[] = {
 	// Room 0
 	{261, OPEN, &PelrockEngine::openDrawer},
@@ -38,13 +41,17 @@ const ActionEntry actionTable[] = {
 	{3, PICKUP, &PelrockEngine::pickUpPhoto},
 	{0, PICKUP, &PelrockEngine::pickYellowBook}, // Generic pickup for other items
 	// Room 1
-	{4, PICKUP, &PelrockEngine::pickUpBrick},    // Brick
+	{4, PICKUP, &PelrockEngine::pickUpBrick}, // Brick
 	// Room 2
 	{282, OPEN, &PelrockEngine::openMcDoor},
 	{282, CLOSE, &PelrockEngine::closeMcDoor},
+
+	// Room 12
 	{60, PICKUP, &PelrockEngine::grabKetchup},
 	{61, PICKUP, &PelrockEngine::grabMustard},
 	{62, PICKUP, &PelrockEngine::grabSpicey},
+	{370, OPEN, &PelrockEngine::openKitchenDoor},
+	{370, CLOSE, &PelrockEngine::closeKitchenDoor},
 
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
@@ -59,16 +66,13 @@ const ActionEntry actionTable[] = {
 	// End marker
 	{WILDCARD, NO_ACTION, nullptr}};
 
-
 const CombinationEntry combinationTable[] = {
 	{2, 281, &PelrockEngine::useCardWithATM}, // Use ATM Card with ATM
 	// End marker
-	{WILDCARD, WILDCARD, nullptr}
-};
+	{WILDCARD, WILDCARD, nullptr}};
 
 void PelrockEngine::openDoor(HotSpot *hotspot) {
-	_room->addSticker(93, false);
-	_room->enableExit(0, false);
+	openDoor(hotspot, 0, 93, FEMININE, true);
 }
 
 void PelrockEngine::openDrawer(HotSpot *hotspot) {
@@ -78,16 +82,15 @@ void PelrockEngine::openDrawer(HotSpot *hotspot) {
 	}
 	_room->addSticker(91);
 	// TODO: Check if we need to disable the hotspot
-	if(_room->findHotspotByExtra(1)->isEnabled &&
-	   _room->findHotspotByExtra(2)->isEnabled &&
-	   _room->findHotspotByExtra(3)->isEnabled) {
-	   _room->disableHotspot(hotspot);
+	if (_room->findHotspotByExtra(1)->isEnabled &&
+		_room->findHotspotByExtra(2)->isEnabled &&
+		_room->findHotspotByExtra(3)->isEnabled) {
+		_room->disableHotspot(hotspot);
 	}
 }
 
 void PelrockEngine::closeDoor(HotSpot *hotspot) {
-	_room->removeSticker(93);
-	_room->disableExit(0, false);
+	closeDoor(hotspot, 0, 93, FEMININE, true);
 }
 
 void PelrockEngine::pickUpPhoto(HotSpot *hotspot) {
@@ -109,29 +112,27 @@ void PelrockEngine::closeDrawer(HotSpot *hotspot) {
 
 void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
 	debug("Withdrawing money from ATM using card (inv obj %d)", inventoryObject);
-	if(_state.JEFE_INGRESA_PASTA) {
+	if (_state.JEFE_INGRESA_PASTA) {
 		_state.JEFE_INGRESA_PASTA = 0;
 		addInventoryItem(75);
-	}
-	else {
+	} else {
 		int billCount = 0;
-		for(int i = 0; i < _state.inventoryItems.size(); i++) {
-			if(_state.inventoryItems[i] == 5) {
+		for (int i = 0; i < _state.inventoryItems.size(); i++) {
+			if (_state.inventoryItems[i] == 5) {
 				billCount++;
 			}
 		}
-		if(billCount < 13) {
+		if (billCount < 13) {
 			addInventoryItem(5); // 1000 pesetas bill
 			_dialog->say(_res->_ingameTexts[TEAPETECE_BUENRATO]);
-		}
-		else {
+		} else {
 			_dialog->say(_res->_ingameTexts[NOMONEY_LEFT]);
 		}
 	}
 }
 
 void PelrockEngine::openMcDoor(HotSpot *hotspot) {
-	if( _room->hasSticker(7)) {
+	if (_room->hasSticker(7)) {
 		_dialog->say(_res->_ingameTexts[ALREADY_OPENED_F]);
 		return;
 	}
@@ -140,7 +141,8 @@ void PelrockEngine::openMcDoor(HotSpot *hotspot) {
 }
 
 void PelrockEngine::closeMcDoor(HotSpot *hotspot) {
-	if( !_room->hasSticker(7)) {
+	closeDoor(hotspot, 2, 7, FEMININE, true);
+	if (!_room->hasSticker(7)) {
 		_dialog->say(_res->_ingameTexts[ALREADY_CLOSED_F]);
 		return;
 	}
@@ -162,9 +164,38 @@ void PelrockEngine::grabMustard(HotSpot *hotspot) {
 }
 
 void PelrockEngine::grabSpicey(HotSpot *hotspot) {
+
 	_room->addSticker(71);
 }
 
+void PelrockEngine::openKitchenDoor(HotSpot *hotspot) {
+	openDoor(hotspot, 1, 32, MASCULINE, true);
+}
+
+void PelrockEngine::closeKitchenDoor(HotSpot *HotSpot) {
+	closeDoor(HotSpot, 1, 32, MASCULINE, true);
+}
+
+void PelrockEngine::openDoor(HotSpot *hotspot, int doorIndex, int sticker, bool masculine, bool stayClosed) {
+	if (_room->hasSticker(sticker)) {
+		int text = masculine == MASCULINE ? YA_ABIERTO_M : ALREADY_OPENED_F;
+		_dialog->say(_res->_ingameTexts[text]);
+		return;
+	}
+	_room->enableExit(doorIndex, !stayClosed);
+	_room->addSticker(sticker, !stayClosed);
+}
+
+void PelrockEngine::closeDoor(HotSpot *hotspot, int doorIndex, int sticker, bool masculine, bool stayOpen) {
+	if (!_room->hasSticker(sticker)) {
+		int text = masculine == MASCULINE ? ALREADY_CLOSED_M : ALREADY_CLOSED_F;
+		_dialog->say(_res->_ingameTexts[text]);
+		return;
+	}
+	_room->disableExit(doorIndex, !stayOpen);
+	_room->removeSticker(sticker);
+}
+
 void PelrockEngine::addInventoryItem(int item) {
 	if (_state.inventoryItems.size() == 0) {
 		_state.selectedInventoryItem = item;
@@ -184,9 +215,8 @@ void PelrockEngine::addInventoryItem(int item) {
 	_state.addInventoryItem(item);
 }
 
-
 void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte rootIndex) {
-	if(actionTrigger == 328) {
+	if (actionTrigger == 328) {
 		debug("Disabling root %d in room %d", rootIndex, room);
 		_state.setRootDisabledState(room, rootIndex, true);
 	}
@@ -214,13 +244,11 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	}
 }
 
-
 void PelrockEngine::noOpAction(HotSpot *hotspot) {
 }
 
-
 void PelrockEngine::noOpItem(int item, HotSpot *hotspot) {
-	//154 to 169
+	// 154 to 169
 	debug("No-op item %d with hotspot %d", item, hotspot->extra);
 	_alfredState.direction = ALFRED_DOWN;
 	byte response = (byte)getRandomNumber(12);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 29cbab2fd12..bdc641ed837 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -236,6 +236,10 @@ public:
 	void grabKetchup(HotSpot *hotspot);
 	void grabMustard(HotSpot *hotspot);
 	void grabSpicey(HotSpot *hotspot);
+	void openKitchenDoor(HotSpot *hotspot);
+	void closeKitchenDoor(HotSpot *HotSpot);
+	void openDoor(HotSpot *hotspot, int doorIndex, int sticker, bool masculine, bool stayClosed);
+	void closeDoor(HotSpot *hotspot, int doorIndex, int sticker, bool masculine, bool stayOpen);
 	void pickUpPhoto(HotSpot *hotspot);
 	void pickYellowBook(HotSpot *hotspot);
 	void pickUpBrick(HotSpot *hotspot);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 6eef9f6742c..bad8be22e6d 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -98,12 +98,24 @@ void RoomManager::addSticker(int stickerId, bool persist) {
 
 void RoomManager::removeSticker(int stickerIndex) {
 	int index = -1;
+	if (index == -1) {
+		for (int i = 0; i < _transientStickers.size(); i++) {
+			if (_transientStickers[i].stickerIndex == stickerIndex) {
+				index = i;
+				_transientStickers.remove_at(index);
+				return;
+			}
+		}
+	}
+
 	for (int i = 0; i < g_engine->_state.roomStickers[_currentRoomNumber].size(); i++) {
 		if (g_engine->_state.roomStickers[_currentRoomNumber][i].stickerIndex == stickerIndex) {
 			index = i;
+			g_engine->_state.roomStickers[_currentRoomNumber].remove_at(index);
 			break;
 		}
 	}
+
 	if (index != -1 && index < g_engine->_state.roomStickers[_currentRoomNumber].size())
 		g_engine->_state.roomStickers[_currentRoomNumber].remove_at(index);
 }
@@ -114,12 +126,19 @@ bool RoomManager::hasSticker(int index) {
 			return true;
 		}
 	}
+
+	for (int i = 0; i < _transientStickers.size(); i++) {
+		if (_transientStickers[i].stickerIndex == index) {
+			return true;
+		}
+	}
+
 	return false;
 }
 
 void RoomManager::changeExit(int index, bool enabled, bool persist) {
 	_currentRoomExits[index].isEnabled = enabled;
-	if(persist)
+	if (persist)
 		g_engine->_state.roomExitChanges[_currentRoomNumber].push_back({_currentRoomNumber, _currentRoomExits[index].index, _currentRoomExits[index]});
 }
 


Commit: 16ebb316a209547f5f39625236a60ae4fe64279d
    https://github.com/scummvm/scummvm/commit/16ebb316a209547f5f39625236a60ae4fe64279d
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:45+02:00

Commit Message:
PELROCK: Triggers side effects in other rooms

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/menu.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/saveload.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 77d5aeac3cf..d71120b29eb 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -34,10 +34,10 @@ namespace Pelrock {
 
 const ActionEntry actionTable[] = {
 	// Room 0
-	{261, OPEN, &PelrockEngine::openDrawer},
-	{261, CLOSE, &PelrockEngine::closeDrawer},
-	{268, OPEN, &PelrockEngine::openDoor},
-	{268, CLOSE, &PelrockEngine::closeDoor},
+	{261, OPEN, &PelrockEngine::openRoomDrawer},
+	{261, CLOSE, &PelrockEngine::closeRoomDrawer},
+	{268, OPEN, &PelrockEngine::openRoomDoor},
+	{268, CLOSE, &PelrockEngine::closeRoomDoor},
 	{3, PICKUP, &PelrockEngine::pickUpPhoto},
 	{0, PICKUP, &PelrockEngine::pickYellowBook}, // Generic pickup for other items
 	// Room 1
@@ -53,6 +53,10 @@ const ActionEntry actionTable[] = {
 	{370, OPEN, &PelrockEngine::openKitchenDoor},
 	{370, CLOSE, &PelrockEngine::closeKitchenDoor},
 
+	// Room 13
+	{375, OPEN, &PelrockEngine::openKitchenDrawer},
+	{374, OPEN, &PelrockEngine::openKitchenDoorFromInside},
+
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
 	{WILDCARD, TALK, &PelrockEngine::noOpAction},   // Generic talk action
@@ -68,14 +72,15 @@ const ActionEntry actionTable[] = {
 
 const CombinationEntry combinationTable[] = {
 	{2, 281, &PelrockEngine::useCardWithATM}, // Use ATM Card with ATM
+	{62, 185, &PelrockEngine::useSpicySauceWithBurger}, // Use Spicy Sauce with Burger
 	// End marker
 	{WILDCARD, WILDCARD, nullptr}};
 
-void PelrockEngine::openDoor(HotSpot *hotspot) {
+void PelrockEngine::openRoomDoor(HotSpot *hotspot) {
 	openDoor(hotspot, 0, 93, FEMININE, true);
 }
 
-void PelrockEngine::openDrawer(HotSpot *hotspot) {
+void PelrockEngine::openRoomDrawer(HotSpot *hotspot) {
 	if (_room->hasSticker(91)) {
 		_dialog->say(_res->_ingameTexts[YA_ABIERTO_M]);
 		return;
@@ -89,7 +94,7 @@ void PelrockEngine::openDrawer(HotSpot *hotspot) {
 	}
 }
 
-void PelrockEngine::closeDoor(HotSpot *hotspot) {
+void PelrockEngine::closeRoomDoor(HotSpot *hotspot) {
 	closeDoor(hotspot, 0, 93, FEMININE, true);
 }
 
@@ -105,7 +110,7 @@ void PelrockEngine::pickUpBrick(HotSpot *hotspot) {
 	_room->addSticker(133);
 }
 
-void PelrockEngine::closeDrawer(HotSpot *hotspot) {
+void PelrockEngine::closeRoomDrawer(HotSpot *hotspot) {
 	_room->removeSticker(91);
 	_room->enableHotspot(hotspot);
 }
@@ -132,22 +137,12 @@ void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
 }
 
 void PelrockEngine::openMcDoor(HotSpot *hotspot) {
-	if (_room->hasSticker(7)) {
-		_dialog->say(_res->_ingameTexts[ALREADY_OPENED_F]);
-		return;
-	}
-	_room->enableExit(2);
-	_room->addSticker(7);
+	openDoor(hotspot, 2, 7, FEMININE, false);
 }
 
 void PelrockEngine::closeMcDoor(HotSpot *hotspot) {
-	closeDoor(hotspot, 2, 7, FEMININE, true);
-	if (!_room->hasSticker(7)) {
-		_dialog->say(_res->_ingameTexts[ALREADY_CLOSED_F]);
-		return;
-	}
-	_room->disableExit(2);
-	_room->removeSticker(7);
+	//FIXME: Impossible to close right now
+	closeDoor(hotspot, 2, 7, FEMININE, false);
 }
 
 void PelrockEngine::pickUpAndDisable(HotSpot *hotspot) {
@@ -164,7 +159,6 @@ void PelrockEngine::grabMustard(HotSpot *hotspot) {
 }
 
 void PelrockEngine::grabSpicey(HotSpot *hotspot) {
-
 	_room->addSticker(71);
 }
 
@@ -176,23 +170,42 @@ void PelrockEngine::closeKitchenDoor(HotSpot *HotSpot) {
 	closeDoor(HotSpot, 1, 32, MASCULINE, true);
 }
 
-void PelrockEngine::openDoor(HotSpot *hotspot, int doorIndex, int sticker, bool masculine, bool stayClosed) {
+void PelrockEngine::openKitchenDrawer(HotSpot *hotspot) {
+	if(_state.JEFE_ENCARCELADO == false) {
+		_dialog->say(_res->_ingameTexts[QUITA_ESAS_MANOS]);
+	}
+	else {
+		_room->addSticker(36);
+		_room->disableHotspot(hotspot);
+	}
+}
+
+void PelrockEngine::openKitchenDoorFromInside(HotSpot *hotspot) {
+	openDoor(hotspot, 0, 34, MASCULINE, true);
+}
+
+void PelrockEngine::useSpicySauceWithBurger(int inventoryObject, HotSpot *hotspot) {
+	_state.PUESTA_SALSA_PICANTE = true;
+	_dialog->say(_res->_ingameTexts[VAESTAR_POCOFUERTE]);
+}
+
+void PelrockEngine::openDoor(HotSpot *hotspot, int exitIndex, int sticker, bool masculine, bool stayClosed) {
 	if (_room->hasSticker(sticker)) {
 		int text = masculine == MASCULINE ? YA_ABIERTO_M : ALREADY_OPENED_F;
 		_dialog->say(_res->_ingameTexts[text]);
 		return;
 	}
-	_room->enableExit(doorIndex, !stayClosed);
+	_room->enableExit(exitIndex, !stayClosed);
 	_room->addSticker(sticker, !stayClosed);
 }
 
-void PelrockEngine::closeDoor(HotSpot *hotspot, int doorIndex, int sticker, bool masculine, bool stayOpen) {
+void PelrockEngine::closeDoor(HotSpot *hotspot, int exitIndex, int sticker, bool masculine, bool stayOpen) {
 	if (!_room->hasSticker(sticker)) {
 		int text = masculine == MASCULINE ? ALREADY_CLOSED_M : ALREADY_CLOSED_F;
 		_dialog->say(_res->_ingameTexts[text]);
 		return;
 	}
-	_room->disableExit(doorIndex, !stayOpen);
+	_room->disableExit(exitIndex, !stayOpen);
 	_room->removeSticker(sticker);
 }
 
@@ -233,6 +246,7 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 		_res->getExtraScreen(9, _extraScreen, palette);
 
 		g_system->getPaletteManager()->setPalette(palette, 0, 256);
+		_sound->playMusicTrack(25);
 		extraScreenLoop();
 
 		_dialog->say(_res->_ingameTexts[SOHOT]);
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 39442d50c0c..4dec3ef8cd5 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -101,6 +101,7 @@ void MenuManager::checkMouseClick(int x, int y) {
 			_menuText = _inventoryDescriptions[_selectedInvIndex];
 			g_engine->_state.selectedInventoryItem = _selectedInvIndex;
 			selectedItem = true;
+			debug("Selected inventory item %d", _selectedInvIndex);
 			return;
 		}
 	}
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 796bbaf4723..063812a4681 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -641,6 +641,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 	}
 	// This if is needed to draw Alfred when idle, when the switch case results in a state change
 	if (_alfredState.animState == ALFRED_IDLE) {
+		debug("Drawing Alfred idle frame, direction %d", _alfredState.direction);
 		drawAlfred(_res->alfredIdle[_alfredState.direction]);
 	}
 }
@@ -1331,7 +1332,7 @@ void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 	else {
 		_sound->stopMusic();
 	}
-
+	doExtraActions(number);
 	_screen->markAllDirty();
 	_screen->update();
 	roomFile.close();
@@ -1339,4 +1340,32 @@ void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 	delete[] palette;
 }
 
+void PelrockEngine::doExtraActions(int roomNumber) {
+switch (roomNumber)
+{
+case 4:
+	if(_state.PUESTA_SALSA_PICANTE && !_state.JEFE_ENCARCELADO) {
+		_state.JEFE_ENCARCELADO = true;
+		_room->disableSprite(13, 0, true); // Disable Jefe hotspot
+		byte *palette = new byte[768];
+		if (_extraScreen == nullptr) {
+			_extraScreen = new byte[640 * 400];
+		}
+		_res->getExtraScreen(4, _extraScreen, palette);
+
+		g_system->getPaletteManager()->setPalette(palette, 0, 256);
+		extraScreenLoop();
+
+		_screen->markAllDirty();
+		_screen->update();
+
+	}
+	break;
+
+default:
+	break;
+}
+
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index bdc641ed837..267b64e1e12 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -220,6 +220,7 @@ public:
 	}
 
 	void setScreen(int s, AlfredDirection dir);
+	void doExtraActions(int roomNumber);
 	bool renderScene(int overlayMode = OVERLAY_NONE);
 
 	void addInventoryItem(int item);
@@ -228,16 +229,19 @@ public:
 	void dialogActionTrigger(uint16 actionTrigger, byte room, byte rootIndex);
 
 	void executeAction(VerbIcon action, HotSpot *hotspot);
-	void openDrawer(HotSpot *hotspot);
-	void closeDrawer(HotSpot *hotspot);
-	void openDoor(HotSpot *hotspot);
-	void closeDoor(HotSpot *hotspot);
+	void openRoomDrawer(HotSpot *hotspot);
+	void closeRoomDrawer(HotSpot *hotspot);
+	void openRoomDoor(HotSpot *hotspot);
+	void closeRoomDoor(HotSpot *hotspot);
 	void pickUpAndDisable(HotSpot *hotspot);
 	void grabKetchup(HotSpot *hotspot);
 	void grabMustard(HotSpot *hotspot);
 	void grabSpicey(HotSpot *hotspot);
 	void openKitchenDoor(HotSpot *hotspot);
 	void closeKitchenDoor(HotSpot *HotSpot);
+	void openKitchenDrawer(HotSpot *hotspot);
+	void openKitchenDoorFromInside(HotSpot *hotspot);
+	void useSpicySauceWithBurger(int inventoryObject, HotSpot *hotspot);
 	void openDoor(HotSpot *hotspot, int doorIndex, int sticker, bool masculine, bool stayClosed);
 	void closeDoor(HotSpot *hotspot, int doorIndex, int sticker, bool masculine, bool stayOpen);
 	void pickUpPhoto(HotSpot *hotspot);
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 781ecd6ea59..d9ddd65087e 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -30,6 +30,9 @@ namespace Pelrock {
 
 ResourceManager::ResourceManager(/* args */) {
 	_inventoryIcons = new InventoryObject[69];
+	for (int i = 0; i < 4; i++) {
+		alfredIdle[i] = nullptr;
+	}
 }
 
 ResourceManager::~ResourceManager() {
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index 19faa3bdd63..852dc741c9a 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -53,7 +53,7 @@ public:
 	InventoryObject getInventoryObject(byte index);
 	byte *loadExtra();
 
-	byte *alfredIdle[4] = {nullptr}; // 4 directions
+	byte *alfredIdle[4]; // 4 directions
 
 	byte **alfredWalkFrames[4]; // 4 arrays of arrays
 
@@ -62,8 +62,8 @@ public:
 	byte **alfredCombFrames[2];
 	byte **alfredInteractFrames[4];
 
-	byte *_cursorMasks[5] = {nullptr};
-	byte *_verbIcons[9] = {nullptr};
+	byte *_cursorMasks[5];
+	byte *_verbIcons[9];
 	byte *_popUpBalloon = nullptr;
 	Common::Array<Common::StringArray> _ingameTexts;
 };
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index bad8be22e6d..00f16d8af42 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -158,6 +158,17 @@ void RoomManager::changeHotSpot(HotSpot hotspot) {
 	g_engine->_state.roomHotSpotChanges[_currentRoomNumber].push_back({_currentRoomNumber, hotspot.innerIndex, hotspot});
 }
 
+void RoomManager::disableSprite(int roomNumber, int spriteIndex, bool persist) {
+	if(roomNumber == _currentRoomNumber) {
+		_currentRoomAnims[spriteIndex].zOrder = 255;
+	}
+	g_engine->_state.disabledSprites[roomNumber].push_back(spriteIndex);
+}
+
+void RoomManager::enableSprite(int spriteIndex, int zOrder, bool persist) {
+	// _currentRoomAnims[spriteIndex].zOrder = zOrder;
+}
+
 void RoomManager::enableHotspot(HotSpot *hotspot, bool persist) {
 	hotspot->isEnabled = true;
 	if (persist) {
@@ -445,7 +456,7 @@ Common::Array<HotSpot> RoomManager::unifyHotspots(Common::Array<Pelrock::Sprite>
 		thisHotspot.h = anims[i].h;
 		thisHotspot.extra = anims[i].extra;
 		thisHotspot.actionFlags = anims[i].actionFlags;
-		thisHotspot.isEnabled = !anims[i].isDisabled;
+		thisHotspot.isEnabled = !anims[i].isHotspotDisabled;
 		thisHotspot.isSprite = true;
 		thisHotspot.zOrder = anims[i].zOrder;
 		unifiedHotspots.push_back(thisHotspot);
@@ -493,6 +504,9 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 	debug("Sprite count: %d", spriteCount);
 	uint32_t metadata_start = spriteCountPos + (44 * 2 + 5);
 	uint32_t picOffset = 0;
+
+	Common::Array<int> disabledSprites = g_engine->_state.disabledSprites[_currentRoomNumber];
+
 	for (int i = 0; i < spriteCount; i++) {
 		uint32_t animOffset = metadata_start + (i * 44);
 		Sprite sprite;
@@ -504,9 +518,15 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 		sprite.extra = data[animOffset + 6];
 		sprite.numAnims = data[animOffset + 8];
 		sprite.zOrder = data[animOffset + 23];
+		for(int i = 0; i < disabledSprites.size(); i++) {
+			if (disabledSprites[i] == sprite.index) {
+				sprite.zOrder = 255;
+				break;
+			}
+		}
 		sprite.spriteType = data[animOffset + 33];
 		sprite.actionFlags = data[animOffset + 34];
-		sprite.isDisabled = data[animOffset + 38];
+		sprite.isHotspotDisabled = data[animOffset + 38];
 		if (sprite.numAnims == 0) {
 			break;
 		}
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 719a251db63..cd0354871e4 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -50,6 +50,8 @@ public:
 	void enableExit(int index, bool persist = true);
 	void changeWalkBox(WalkBox walkbox);
 	void changeHotSpot(HotSpot hotspot);
+	void disableSprite(int roomNumber, int spriteIndex, bool persist = true);
+	void enableSprite(int spriteIndex, int zOrder, bool persist = true);
 	/**
 	 * Utility function to enable or disable a hotspot, with an option to persist the change.
 	 */
diff --git a/engines/pelrock/saveload.cpp b/engines/pelrock/saveload.cpp
index dff98e24e5b..259a1c74735 100644
--- a/engines/pelrock/saveload.cpp
+++ b/engines/pelrock/saveload.cpp
@@ -268,6 +268,37 @@ bool syncGameStateData(Common::Serializer &s, GameStateData *gameState) {
 		}
 	}
 
+	uint16 disabledSpritesSize = (uint16)gameState->disabledSprites.size();
+	s.syncAsUint16LE(disabledSpritesSize);
+	if (s.isSaving()) {
+		for (const auto &spritePair : gameState->disabledSprites) {
+			byte roomNumber = spritePair._key;
+			s.syncAsByte(roomNumber);
+			const Common::Array<int> &sprites = spritePair._value;
+			uint16 numSprites = (uint16)sprites.size();
+			s.syncAsUint16LE(numSprites);
+			for (uint16 i = 0; i < numSprites; ++i) {
+				int spriteIndex = sprites[i];
+				s.syncAsSint32LE(spriteIndex);
+			}
+		}
+	} else {
+		gameState->disabledSprites.clear();
+		for (uint16 idx = 0; idx < disabledSpritesSize; ++idx) {
+			byte roomNumber;
+			s.syncAsByte(roomNumber);
+			uint16 numSprites;
+			s.syncAsUint16LE(numSprites);
+			Common::Array<int> sprites;
+			for (uint16 i = 0; i < numSprites; ++i) {
+				int spriteIndex;
+				s.syncAsSint32LE(spriteIndex);
+				sprites.push_back(spriteIndex);
+			}
+			gameState->disabledSprites[roomNumber] = sprites;
+		}
+	}
+
 	// Disabled branches
 	uint16 disabledBranchesSize = (uint16)gameState->disabledBranches.size();
 	s.syncAsUint16LE(disabledBranchesSize);
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 604dae90c47..cba53b03780 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -92,13 +92,17 @@ void SoundManager::playSound(SonidoFile sound, int volume) {
 		delete[] data;
 
 		// Create raw audio stream (8-bit unsigned mono is common for old games)
-		stream = Audio::makeRawStream(pcmData, pcmSize, sampleRate,
-									  Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
+		stream = Audio::makeRawStream(pcmData, pcmSize, sampleRate, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
 	} else {
 		debug("Unknown sound format");
 		delete[] data;
 		return;
 	}
+
+	if (stream) {
+		int channel = findFreeChannel();
+		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, volume, 0, DisposeAfterUse::YES);
+	}
 }
 
 void SoundManager::playSound(byte *soundData, uint32 size, int volume) {
@@ -201,7 +205,7 @@ void SoundManager::playMusicTrack(int trackNumber) {
 	}
 	_currentMusicTrack = trackNumber;
 	g_system->getAudioCDManager()->stop();
-	g_system->getAudioCDManager()->play(trackNumber, -1, 0, 0);
+	// g_system->getAudioCDManager()->play(trackNumber, -1, 0, 0);
 }
 
 void SoundManager::loadSoundIndex() {
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index bdfbf28aeaf..3175c3080ca 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -197,7 +197,7 @@ struct Sprite {
 	int8 zOrder;
 	byte spriteType;  // 33
 	byte actionFlags; // 34
-	bool isDisabled;  // 38
+	bool isHotspotDisabled;  // 38
 	bool isTalking = false;
 	Anim *animData;
 };
@@ -384,6 +384,8 @@ struct ResetEntry {
 
 struct GameStateData {
 	bool JEFE_INGRESA_PASTA = false;
+	bool JEFE_ENCARCELADO = false;
+	bool PUESTA_SALSA_PICANTE = false;
 
 	GameState stateGame = INTRO;
 
@@ -394,6 +396,7 @@ struct GameStateData {
 	Common::HashMap<byte, Common::Array<WalkBoxChange>> roomWalkBoxChanges;
 	Common::HashMap<byte, Common::Array<HotSpotChange>> roomHotSpotChanges;
 	Common::HashMap<byte, Common::Array<ResetEntry>> disabledBranches;
+	Common::HashMap<byte, Common::Array<int>> disabledSprites;
 
 	GameStateData() {
 		memset(conversationRootsState, 0, 4 * 56);


Commit: 5140ab1362da08bfc565bf39f851886fb9b00f76
    https://github.com/scummvm/scummvm/commit/5140ab1362da08bfc565bf39f851886fb9b00f76
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:45+02:00

Commit Message:
PELROCK: Shows action popup over alfred, fixes hover priority, mouse position capture

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/events.cpp
    engines/pelrock/events.h
    engines/pelrock/menu.cpp
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/room.cpp
    engines/pelrock/saveload.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h
    engines/pelrock/types.h
    engines/pelrock/util.cpp
    engines/pelrock/util.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index d71120b29eb..17d229c94cb 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -72,7 +72,7 @@ const ActionEntry actionTable[] = {
 
 const CombinationEntry combinationTable[] = {
 	{2, 281, &PelrockEngine::useCardWithATM}, // Use ATM Card with ATM
-	{62, 185, &PelrockEngine::useSpicySauceWithBurger}, // Use Spicy Sauce with Burger
+	{62, 117, &PelrockEngine::useSpicySauceWithBurger}, // Use Spicy Sauce with Burger
 	// End marker
 	{WILDCARD, WILDCARD, nullptr}};
 
@@ -117,13 +117,13 @@ void PelrockEngine::closeRoomDrawer(HotSpot *hotspot) {
 
 void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
 	debug("Withdrawing money from ATM using card (inv obj %d)", inventoryObject);
-	if (_state.JEFE_INGRESA_PASTA) {
-		_state.JEFE_INGRESA_PASTA = 0;
+	if (_state->JEFE_INGRESA_PASTA) {
+		_state->JEFE_INGRESA_PASTA = 0;
 		addInventoryItem(75);
 	} else {
 		int billCount = 0;
-		for (int i = 0; i < _state.inventoryItems.size(); i++) {
-			if (_state.inventoryItems[i] == 5) {
+		for (int i = 0; i < _state->inventoryItems.size(); i++) {
+			if (_state->inventoryItems[i] == 5) {
 				billCount++;
 			}
 		}
@@ -171,12 +171,13 @@ void PelrockEngine::closeKitchenDoor(HotSpot *HotSpot) {
 }
 
 void PelrockEngine::openKitchenDrawer(HotSpot *hotspot) {
-	if(_state.JEFE_ENCARCELADO == false) {
+	if(_state->JEFE_ENCARCELADO == false) {
 		_dialog->say(_res->_ingameTexts[QUITA_ESAS_MANOS]);
 	}
 	else {
 		_room->addSticker(36);
-		_room->disableHotspot(hotspot);
+		addInventoryItem(73); // Add recipe
+		_dialog->say(_res->_ingameTexts[QUESESTO_RECETA]);
 	}
 }
 
@@ -185,7 +186,7 @@ void PelrockEngine::openKitchenDoorFromInside(HotSpot *hotspot) {
 }
 
 void PelrockEngine::useSpicySauceWithBurger(int inventoryObject, HotSpot *hotspot) {
-	_state.PUESTA_SALSA_PICANTE = true;
+	_state->PUESTA_SALSA_PICANTE = true;
 	_dialog->say(_res->_ingameTexts[VAESTAR_POCOFUERTE]);
 }
 
@@ -210,8 +211,8 @@ void PelrockEngine::closeDoor(HotSpot *hotspot, int exitIndex, int sticker, bool
 }
 
 void PelrockEngine::addInventoryItem(int item) {
-	if (_state.inventoryItems.size() == 0) {
-		_state.selectedInventoryItem = item;
+	if (_state->inventoryItems.size() == 0) {
+		_state->selectedInventoryItem = item;
 	}
 	_flashingIcon = item;
 	int frameCounter = 0;
@@ -225,13 +226,13 @@ void PelrockEngine::addInventoryItem(int item) {
 		}
 		g_system->delayMillis(10);
 	}
-	_state.addInventoryItem(item);
+	_state->addInventoryItem(item);
 }
 
 void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte rootIndex) {
 	if (actionTrigger == 328) {
 		debug("Disabling root %d in room %d", rootIndex, room);
-		_state.setRootDisabledState(room, rootIndex, true);
+		_state->setRootDisabledState(room, rootIndex, true);
 	}
 }
 
@@ -239,21 +240,11 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	debug("Performing action trigger: %d", actionTrigger);
 	switch (actionTrigger) {
 	case 257:
-		byte *palette = new byte[768];
-		if (_extraScreen == nullptr) {
-			_extraScreen = new byte[640 * 400];
-		}
-		_res->getExtraScreen(9, _extraScreen, palette);
-
-		g_system->getPaletteManager()->setPalette(palette, 0, 256);
 		_sound->playMusicTrack(25);
-		extraScreenLoop();
-
+		loadExtraScreenAndPresent(9);
 		_dialog->say(_res->_ingameTexts[SOHOT]);
 		_screen->markAllDirty();
 		_screen->update();
-
-		delete[] palette;
 		break;
 	}
 }
@@ -269,4 +260,21 @@ void PelrockEngine::noOpItem(int item, HotSpot *hotspot) {
 	_dialog->say(_res->_ingameTexts[154 + response]);
 }
 
+void PelrockEngine::useOnAlfred(int inventoryObject) {
+
+	debug("Using item %d on Alfred", inventoryObject);
+	switch (inventoryObject)
+	{
+	case 73: // Recipe book
+		_res->loadAlfredSpecialAnim(0);
+		_alfredState.animState = ALFRED_SPECIAL_ANIM;
+		loadExtraScreenAndPresent(3);
+		_dialog->say(_res->_ingameTexts[QUEASCO]);
+		break;
+
+	default:
+		break;
+	}
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 110a1c16632..1abead0b84a 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -391,7 +391,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 	}
 	// Right after the speaker conversation tree, we are in branch 0
 	int currentRoot = 0;
-	while(g_engine->_state.getRootDisabledState(g_engine->_room->_currentRoomNumber, currentRoot)) {
+	while(g_engine->_state->getRootDisabledState(g_engine->_room->_currentRoomNumber, currentRoot)) {
 		// This root is disabled, skip to next
 		while(position < dataSize) {
 			if(conversationData[position] == CTRL_END_BRANCH) {
diff --git a/engines/pelrock/events.cpp b/engines/pelrock/events.cpp
index 37f46927342..1b0f4c4ff8a 100644
--- a/engines/pelrock/events.cpp
+++ b/engines/pelrock/events.cpp
@@ -22,6 +22,7 @@
 
 #include "pelrock/events.h"
 #include "pelrock/pelrock.h"
+#include "pelrock/util.h"
 
 namespace Pelrock {
 
@@ -49,7 +50,7 @@ void PelrockEventManager::pollEvent() {
 		// case Common::EVENT_CUSTOM_ENGINE_ACTION_END:
 		// 	break;
 		case Common::EVENT_KEYDOWN:
-		// 	changeGameSpeed(_event);
+			changeGameSpeed(_event);
 			// _keyPressed = true;
 			_lastKeyEvent = _event.kbd.keycode;
 			break;
@@ -61,6 +62,8 @@ void PelrockEventManager::pollEvent() {
 				_clickTime = g_system->getMillis();
 			}
 			_leftMouseButton = 1;
+			_mouseClickX = _event.mouse.x;
+			_mouseClickY = _event.mouse.y;
 			break;
 		case Common::EVENT_LBUTTONUP:
 			if (_leftMouseButton == 1) {
@@ -68,8 +71,8 @@ void PelrockEventManager::pollEvent() {
 				if (!_popupSelectionMode) {
 					_leftMouseClicked = true;
 				}
-				_mouseClickX = _event.mouse.x;
-				_mouseClickY = _event.mouse.y;
+				_releaseX = _event.mouse.x;
+				_releaseY = _event.mouse.y;
 				_longClicked = false;
 			} else {
 				_leftMouseClicked = false;
diff --git a/engines/pelrock/events.h b/engines/pelrock/events.h
index defe1392f9c..046e38dc8ec 100644
--- a/engines/pelrock/events.h
+++ b/engines/pelrock/events.h
@@ -36,6 +36,8 @@ public:
 	int16 _mouseY = 0;
 	int16 _mouseClickX = 0;
 	int16 _mouseClickY = 0;
+	int16 _releaseX = 0;
+	int16 _releaseY = 0;
 	bool _leftMouseClicked = false;
 	bool _longClicked = false;
 	bool _rightMouseClicked = false;
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 4dec3ef8cd5..ad74822092b 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -97,9 +97,9 @@ void MenuManager::checkMouseClick(int x, int y) {
 	for (int i = 0; i < 4; i++) {
 		if (x >= 140 + (82 * i) && x <= 140 + (82 * i) + 64 &&
 			y >= 115 - (8 * i) && y <= 115 - (8 * i) + 64) {
-			_selectedInvIndex = g_engine->_state.inventoryItems[_curInventoryPage * 4 + i];
+			_selectedInvIndex = g_engine->_state->inventoryItems[_curInventoryPage * 4 + i];
 			_menuText = _inventoryDescriptions[_selectedInvIndex];
-			g_engine->_state.selectedInventoryItem = _selectedInvIndex;
+			g_engine->_state->selectedInventoryItem = _selectedInvIndex;
 			selectedItem = true;
 			debug("Selected inventory item %d", _selectedInvIndex);
 			return;
@@ -120,7 +120,7 @@ void MenuManager::checkMouseClick(int x, int y) {
 			_curInventoryPage--;
 		break;
 	case INVENTORY_NEXT_BUTTON:
-		if ((_curInventoryPage + 1) * 4 < g_engine->_state.inventoryItems.size())
+		if ((_curInventoryPage + 1) * 4 < g_engine->_state->inventoryItems.size())
 			_curInventoryPage++;
 		break;
 	case SAVE_GAME_BUTTON:
@@ -142,7 +142,7 @@ void MenuManager::menuLoop() {
 		_events->_leftMouseClicked = false;
 	} else if (_events->_rightMouseClicked) {
 		g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
-		g_engine->_state.stateGame = GAME;
+		g_engine->_state->stateGame = GAME;
 		_events->_rightMouseClicked = false;
 		tearDown();
 	} else {
@@ -162,9 +162,9 @@ void MenuManager::menuLoop() {
 
 	for (int i = 0; i < 4; i++) {
 		int itemIndex = _curInventoryPage * 4 + i;
-		if (g_engine->_state.inventoryItems.size() <= itemIndex)
+		if (g_engine->_state->inventoryItems.size() <= itemIndex)
 			continue;
-		InventoryObject item = g_engine->_res->getInventoryObject(g_engine->_state.inventoryItems[itemIndex]);
+		InventoryObject item = g_engine->_res->getIconForObject(g_engine->_state->inventoryItems[itemIndex]);
 		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 140 + (82 * i), 115 - (8 * i), 60, 60, 1);
 	}
 
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index f6343fa48d1..b63745f9d22 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -441,56 +441,56 @@ struct ExtraImages {
 };
 
 const ExtraImages extraScreens[] = {
-	{
-		0x00, // Portrait above bed
-		0x7984,
-		8
-	},
-	{
-		0x1A9EE, // Computer screen
-		0x305A2,
-		8
-	},
-	{
-		0x647C3, // Alfred circle
-		0x7B6B1,
-		4
-	},
-	{
-		0x6FBC9, // Recipe
-		0x7B6B1,
-		8
-	},
-	{
-		0x7BA11, // Newspaper
-		0x88745,
-		8
-	},
-	{
-		0x9237B, // tablet
-		0xB0EE7,
-		8
-	},
-	{
-		0xB11ED, // map
-		0xDE011,
-		8
-	},
-	{
-		0xFFC47, // girl book
-		0x1180C9,
-		8
-	},
-	{
-		0x1183C5, // book
-		0x1358F3,
-		8
-	},
-	{
-		0x152A88, // portrait
-		0x15BFC8,
-		8
-	},
+	{0x00, // Portrait above bed
+	 0x7984,
+	 8},
+	{0x1A9EE, // Computer screen
+	 0x305A2,
+	 8},
+	{0x647C3, // Alfred circle
+	 0x7B6B1,
+	 4},
+	{0x6FBC9, // Recipe
+	 0x7B6B1,
+	 8},
+	{0x7BA11, // Newspaper
+	 0x88745,
+	 8},
+	{0x9237B, // tablet
+	 0xB0EE7,
+	 8},
+	{0xB11ED, // map
+	 0xDE011,
+	 8},
+	{0xFFC47, // girl book
+	 0x1180C9,
+	 8},
+	{0x1183C5, // book
+	 0x1358F3,
+	 8},
+	{0x152A88, // portrait
+	 0x15BFC8,
+	 8},
+};
+
+struct AlfredSpecialAnimOffset {
+	int numFrames = 0;
+	int w = 0;
+	int h = 0;
+	int numBudas;
+	uint32 offset;
+	int stride = 0;
+
+	AlfredSpecialAnimOffset(int nF, int width, int height, int nBudas, uint32 off)
+		: numFrames(nF), w(width), h(height), numBudas(nBudas), offset(off) {
+		stride = w * h;
+	}
+	AlfredSpecialAnimOffset() {
+	}
+};
+
+const AlfredSpecialAnimOffset alfredSpecialAnims[] = {
+	{20, 51, 102, 2, 559681}, // READ
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 063812a4681..b367960b160 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -115,22 +115,22 @@ Common::Error PelrockEngine::run() {
 	Graphics::FrameLimiter limiter(g_system, 60);
 
 	if (shouldPlayIntro == false) {
-		_state.stateGame = GAME;
+		_state->stateGame = GAME;
 		// stateGame = SETTINGS;
 	} else {
-		_state.stateGame = INTRO;
+		_state->stateGame = INTRO;
 		_videoManager->playIntro();
-		_state.stateGame = GAME;
+		_state->stateGame = GAME;
 	}
 	if (!shouldQuit())
 		init();
 
 	while (!shouldQuit()) {
 
-		if (_state.stateGame == SETTINGS) {
+		if (_state->stateGame == SETTINGS) {
 			changeCursor(DEFAULT);
 			_menu->menuLoop();
-		} else if (_state.stateGame == GAME) {
+		} else if (_state->stateGame == GAME) {
 			gameLoop();
 		}
 		_screen->update();
@@ -254,8 +254,8 @@ bool PelrockEngine::renderScene(int overlayMode) {
 
 void PelrockEngine::executeAction(VerbIcon action, HotSpot *hotspot) {
 
-	if(action == ITEM) {
-		int inventoryObject = _state.selectedInventoryItem;
+	if (action == ITEM) {
+		int inventoryObject = _state->selectedInventoryItem;
 		for (const CombinationEntry *entry = combinationTable; entry->handler != nullptr; entry++) {
 			if (entry->inventoryObject == inventoryObject && entry->hotspotExtra == hotspot->extra) {
 				(this->*(entry->handler))(inventoryObject, hotspot);
@@ -267,8 +267,6 @@ void PelrockEngine::executeAction(VerbIcon action, HotSpot *hotspot) {
 		return;
 	}
 
-
-
 	for (const ActionEntry *entry = actionTable; entry->handler != nullptr; entry++) {
 		if (entry->action == action && entry->hotspotExtra == hotspot->extra) {
 			// Found exact match - call the handler
@@ -300,8 +298,11 @@ void PelrockEngine::checkMouse() {
 	// Handle mouse release after long press (popup selection mode)
 	if (_events->_popupSelectionMode && !_events->_leftMouseButton) {
 		// Mouse was released while popup is active
-		VerbIcon actionClicked = isActionUnder(_events->_mouseX, _events->_mouseY);
-		if (actionClicked != NO_ACTION && _currentHotspot != nullptr) {
+		VerbIcon actionClicked = isActionUnder(_events->_releaseX, _events->_releaseY);
+		if (_actionPopupState.isAlfredUnder) {
+			debug("Using item on Alfred");
+			useOnAlfred(_state->selectedInventoryItem);
+		} else if (actionClicked != NO_ACTION && _currentHotspot != nullptr) {
 			// Action was selected - queue it
 			walkTo(_currentHotspot->x + _currentHotspot->w / 2, _currentHotspot->y + _currentHotspot->h);
 			_queuedAction = QueuedAction{actionClicked, _currentHotspot->index, true};
@@ -323,7 +324,7 @@ void PelrockEngine::checkMouse() {
 	} else if (_events->_rightMouseClicked) {
 		g_system->getPaletteManager()->setPalette(_menu->_mainMenuPalette, 0, 256);
 		_events->_rightMouseClicked = false;
-		_state.stateGame = SETTINGS;
+		_state->stateGame = SETTINGS;
 	}
 	checkMouseHover();
 }
@@ -405,8 +406,8 @@ void PelrockEngine::paintDebugLayer() {
 }
 
 void PelrockEngine::placeStickers() {
-	for (int i = 0; i < _state.roomStickers[_room->_currentRoomNumber].size(); i++) {
-		Sticker sticker = _state.roomStickers[_room->_currentRoomNumber][i];
+	for (int i = 0; i < _state->roomStickers[_room->_currentRoomNumber].size(); i++) {
+		Sticker sticker = _state->roomStickers[_room->_currentRoomNumber][i];
 		placeSticker(sticker);
 	}
 	// also place temporary stickers
@@ -603,13 +604,13 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		if (_alfredState.curFrame >= walkingAnimLengths[_alfredState.direction]) {
 			_alfredState.curFrame = 0;
 		}
-		if(_alfredState.animState == ALFRED_WALKING) {// in case it changed to idle above
+		if (_alfredState.animState == ALFRED_WALKING) { // in case it changed to idle above
 			drawAlfred(_res->alfredWalkFrames[_alfredState.direction][_alfredState.curFrame]);
 			_alfredState.curFrame++;
 		}
 		break;
 	}
-	case ALFRED_TALKING:
+	case ALFRED_TALKING: {
 		if (_alfredState.curFrame >= talkingAnimLengths[_alfredState.direction] - 1) {
 			_alfredState.curFrame = 0;
 		}
@@ -618,7 +619,8 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			_alfredState.curFrame++;
 		}
 		break;
-	case ALFRED_COMB:
+	}
+	case ALFRED_COMB: {
 		if (_alfredState.curFrame >= 11) {
 			_alfredState.setState(ALFRED_IDLE);
 			drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredIdle[_alfredState.direction], _alfredState.x, _alfredState.y - kAlfredFrameHeight, 51, 102, 255);
@@ -628,7 +630,8 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 				_alfredState.curFrame++;
 		}
 		break;
-	case ALFRED_INTERACTING:
+	}
+	case ALFRED_INTERACTING: {
 		if (_alfredState.curFrame >= interactingAnimLength) {
 			_alfredState.setState(ALFRED_IDLE);
 		} else {
@@ -639,9 +642,34 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		}
 		break;
 	}
+	case ALFRED_SPECIAL_ANIM: {
+		if (_res->_specialAnimCurFrame > _res->_curSpecialAnim.numFrames) {
+			_res->clearSpecialAnim();
+			_alfredState.setState(ALFRED_IDLE);
+		} else {
+			byte *frame = new byte[_res->_curSpecialAnim.stride * _res->_curSpecialAnim.numFrames];
+			extractSingleFrame(_res->_specialAnimData,
+							   frame,
+							   _res->_specialAnimCurFrame,
+							   _res->_curSpecialAnim.w,
+							   _res->_curSpecialAnim.h);
+			drawSpriteToBuffer(_compositeBuffer,
+							   640,
+							   frame,
+							   _alfredState.x,
+							   _alfredState.y - _res->_curSpecialAnim.h,
+							   _res->_curSpecialAnim.w,
+							   _res->_curSpecialAnim.h,
+							   255);
+			if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0) {
+				_res->_specialAnimCurFrame++;
+			}
+			delete[] frame;
+		}
+	}
+	}
 	// This if is needed to draw Alfred when idle, when the switch case results in a state change
 	if (_alfredState.animState == ALFRED_IDLE) {
-		debug("Drawing Alfred idle frame, direction %d", _alfredState.direction);
 		drawAlfred(_res->alfredIdle[_alfredState.direction]);
 	}
 }
@@ -801,10 +829,10 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 		return;
 	}
 
-	int frameSize = animData.w * animData.h;
+	int frameSize = sprite->stride;
 	int curFrame = animData.curFrame;
 	byte *frame = new byte[frameSize];
-	drawSpriteToBuffer(_compositeBuffer, 640, animData.animData[curFrame], sprite->x, sprite->y, sprite->w, sprite->h, 255);
+	drawSpriteToBuffer(_compositeBuffer, 640, animData.animData[curFrame], x, y, w, h, 255);
 
 	// if (animData.elpapsedFrames == animData.speed) {
 	if (_chrono->getFrameCount() % animData.speed == 0) {
@@ -831,8 +859,9 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 }
 
 void PelrockEngine::checkLongMouseClick(int x, int y) {
-	int hotspotIndex = isHotspotUnder(_events->_mouseX, _events->_mouseY);
-	if (hotspotIndex != -1 && !_actionPopupState.isActive) {
+	int hotspotIndex = isHotspotUnder(x, y);
+	bool alfredUnder = isAlfredUnder(x, y);
+	if ((hotspotIndex != -1 || alfredUnder) && !_actionPopupState.isActive) {
 
 		_actionPopupState.x = _alfredState.x + kAlfredFrameWidth / 2 - kBalloonWidth / 2;
 		if (_actionPopupState.x < 0)
@@ -847,7 +876,11 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 		}
 		_actionPopupState.isActive = true;
 		_actionPopupState.curFrame = 0;
-		_currentHotspot = &_room->_currentRoomHotspots[hotspotIndex];
+
+		if (hotspotIndex != -1) {
+			_actionPopupState.isAlfredUnder = alfredUnder;
+			_currentHotspot = &_room->_currentRoomHotspots[hotspotIndex];
+		}
 	}
 }
 
@@ -1040,11 +1073,11 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 		drawSpriteToBuffer(_compositeBuffer, 640, _res->_verbIcons[actions[i]], posx + 20 + (i * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
 	}
 	bool itemUnder = isItemUnder(_events->_mouseX, _events->_mouseY);
-	if (_state.selectedInventoryItem != -1) {
+	if (_state->selectedInventoryItem != -1) {
 		if (itemUnder && shouldBlink) {
 			return;
 		}
-		drawSpriteToBuffer(_compositeBuffer, 640, _res->getInventoryObject(_state.selectedInventoryItem).iconData, posx + 20 + (actions.size() * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
+		drawSpriteToBuffer(_compositeBuffer, 640, _res->getIconForObject(_state->selectedInventoryItem).iconData, posx + 20 + (actions.size() * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
 	}
 	if (_actionPopupState.curFrame < 3) {
 		_actionPopupState.curFrame++;
@@ -1091,7 +1124,10 @@ void PelrockEngine::animateTalkingNPC(Sprite *animSet) {
 
 void PelrockEngine::pickupIconFlash() {
 	_graphics->showOverlay(65, _compositeBuffer);
-	InventoryObject item = _res->getInventoryObject(_flashingIcon);
+	if(_flashingIcon == -1)
+		return;
+	debug("Flashing icon %d", _flashingIcon);
+	InventoryObject item = _res->getIconForObject(_flashingIcon);
 	if (_chrono->getFrameCount() % kIconBlinkPeriod == 0) {
 		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 5, 400 - 60 - 5, 60, 60, 1);
 	}
@@ -1180,19 +1216,18 @@ VerbIcon PelrockEngine::isActionUnder(int x, int y) {
 		return NO_ACTION;
 	}
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
-	int loopEnd = _state.selectedInventoryItem != -1 ? actions.size() + 1 : actions.size();
+	int loopEnd = _state->selectedInventoryItem != -1 ? actions.size() + 1 : actions.size();
 	for (int i = 0; i < loopEnd; i++) {
 		int actionX = _actionPopupState.x + 20 + (i * (kVerbIconWidth + 2));
 		int actionY = _actionPopupState.y + 20;
 		Common::Rect actionRect = Common::Rect(actionX, actionY, actionX + kVerbIconWidth, actionY + kVerbIconHeight);
 
-		if(i == actions.size()) {
+		if (i == actions.size()) {
 			// Check inventory item
 			if (actionRect.contains(x, y)) {
 				return ITEM;
 			}
-		}
-		else if (actionRect.contains(x, y)) {
+		} else if (actionRect.contains(x, y)) {
 			return actions[i];
 		}
 	}
@@ -1283,14 +1318,14 @@ void PelrockEngine::checkMouseHover() {
 		exitDetected = true;
 	}
 
-	if (alfredDetected) {
-		changeCursor(ALFRED);
-	} else if (hotspotDetected && exitDetected) {
+	if (hotspotDetected && exitDetected) {
 		changeCursor(COMBINATION);
 	} else if (hotspotDetected) {
 		changeCursor(HOTSPOT);
 	} else if (exitDetected) {
 		changeCursor(EXIT);
+	} else if (alfredDetected) {
+		changeCursor(ALFRED);
 	} else {
 		changeCursor(DEFAULT);
 	}
@@ -1340,32 +1375,36 @@ void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 	delete[] palette;
 }
 
-void PelrockEngine::doExtraActions(int roomNumber) {
-switch (roomNumber)
-{
-case 4:
-	if(_state.PUESTA_SALSA_PICANTE && !_state.JEFE_ENCARCELADO) {
-		_state.JEFE_ENCARCELADO = true;
-		_room->disableSprite(13, 0, true); // Disable Jefe hotspot
-		byte *palette = new byte[768];
-		if (_extraScreen == nullptr) {
-			_extraScreen = new byte[640 * 400];
-		}
-		_res->getExtraScreen(4, _extraScreen, palette);
-
-		g_system->getPaletteManager()->setPalette(palette, 0, 256);
-		extraScreenLoop();
-
-		_screen->markAllDirty();
-		_screen->update();
-
+void PelrockEngine::loadExtraScreenAndPresent(int screenIndex) {
+	byte *palette = new byte[768];
+	if (_extraScreen == nullptr) {
+		_extraScreen = new byte[640 * 400];
 	}
-	break;
+	_res->getExtraScreen(screenIndex, _extraScreen, palette);
 
-default:
-	break;
+	g_system->getPaletteManager()->setPalette(palette, 0, 256);
+	extraScreenLoop();
+	delete[] _extraScreen;
+	delete[] palette;
+	_screen->markAllDirty();
+	_screen->update();
 }
 
+void PelrockEngine::doExtraActions(int roomNumber) {
+	switch (roomNumber) {
+	case 4:
+		if (_state->PUESTA_SALSA_PICANTE && !_state->JEFE_ENCARCELADO) {
+			_state->JEFE_ENCARCELADO = true;
+			_room->disableSprite(13, 0, true);
+			loadExtraScreenAndPresent(4);
+			_screen->markAllDirty();
+			_screen->update();
+		}
+		break;
+
+	default:
+		break;
+	}
 }
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 267b64e1e12..b38ba69a017 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -59,7 +59,6 @@ class PelrockEngine : public Engine {
 private:
 	const ADGameDescription *_gameDescription;
 	Common::RandomSource _randomSource;
-	ChronoManager *_chrono = nullptr;
 	VideoManager *_videoManager = nullptr;
 	SoundManager *_sound = nullptr;
 	PelrockEventManager *_events = nullptr;
@@ -159,10 +158,11 @@ public:
 	Graphics::Screen *_screen = nullptr;
 	ResourceManager *_res = nullptr;
 	RoomManager *_room = nullptr;
+	ChronoManager *_chrono = nullptr;
 	AlfredState _alfredState;
 	byte *_compositeBuffer = nullptr; // Working composition buffer
 
-	GameStateData _state;
+	GameStateData *_state = new GameStateData();
 
 	SmallFont *_smallFont = nullptr;
 	LargeFont *_largeFont = nullptr;
@@ -220,6 +220,7 @@ public:
 	}
 
 	void setScreen(int s, AlfredDirection dir);
+	void loadExtraScreenAndPresent(int screenIndex);
 	void doExtraActions(int roomNumber);
 	bool renderScene(int overlayMode = OVERLAY_NONE);
 
@@ -249,6 +250,7 @@ public:
 	void pickUpBrick(HotSpot *hotspot);
 	void noOpAction(HotSpot *hotspot);
 	void noOpItem(int item, HotSpot *hotspot);
+	void useOnAlfred(int inventoryObject);
 	void useCardWithATM(int inventoryObject, HotSpot *hotspot);
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index d9ddd65087e..1c95c4abf24 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -217,6 +217,27 @@ void ResourceManager::loadAlfredAnims() {
 	free(alfredCombLeftRaw);
 }
 
+void ResourceManager::loadAlfredSpecialAnim(int numAnim) {
+	_curSpecialAnim = alfredSpecialAnims[numAnim];
+	Common::File alfred7;
+	if (!alfred7.open(Common::Path("ALFRED.7"))) {
+		error("Could not open ALFRED.7");
+		return;
+	}
+
+	alfred7.seek(_curSpecialAnim.offset, SEEK_SET);
+	_specialAnimData = new byte[_curSpecialAnim.numFrames * _curSpecialAnim.w * _curSpecialAnim.h];
+	mergeRleBlocks(&alfred7, _curSpecialAnim.offset, 2, _specialAnimData);
+	_specialAnimCurFrame = 0;
+	alfred7.close();
+}
+
+void ResourceManager::clearSpecialAnim() {
+	delete[] _specialAnimData;
+	_specialAnimData = nullptr;
+	_specialAnimCurFrame = 0;
+}
+
 void ResourceManager::loadInventoryItems() {
 	// loadInventoryDescriptions();
 	Common::File alfred4File;
@@ -351,8 +372,18 @@ Pelrock::Sticker ResourceManager::getSticker(int stickerIndex) {
 	return sticker;
 }
 
-InventoryObject ResourceManager::getInventoryObject(byte index) {
-	return _inventoryIcons[index];
+InventoryObject ResourceManager::getIconForObject(byte objectIndex) {
+	byte iconIndex = 0;
+	if (objectIndex < 59) {
+		if (11 < objectIndex < 59) {
+			iconIndex = ((objectIndex - 11) & 3) + 11; // Books cycle through icons 11-14
+		} else {
+			iconIndex = objectIndex; // Direct mapping for IDs 0-11
+		}
+	} else {
+		iconIndex = objectIndex - 44; // Offset for high IDs (59+)
+	}
+	return _inventoryIcons[iconIndex];
 }
 
 void ResourceManager::mergeRleBlocks(Common::SeekableReadStream *stream, uint32 offset, int numBlocks, byte *outputBuffer) {
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index 852dc741c9a..1288d3d6af7 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -24,6 +24,7 @@
 #include "common/scummsys.h"
 #include "common/stream.h"
 #include "pelrock/types.h"
+#include "pelrock/offsets.h"
 
 namespace Pelrock {
 
@@ -44,13 +45,15 @@ public:
 	void loadCursors();
 	void loadInteractionIcons();
 	void loadAlfredAnims();
+	void loadAlfredSpecialAnim(int numAnim);
+	void clearSpecialAnim();
 	void loadInventoryItems();
 	void loadAlfredResponses();
 	void getExtraScreen(int screenIndex, byte *screenBuf, byte *palette);
 	Common::Array<Common::StringArray> getCredits();
 	Common::Array<Common::Array<Common::String>> processTextData(byte *data, size_t size, bool decode = false);
 	Sticker getSticker(int stickerIndex);
-	InventoryObject getInventoryObject(byte index);
+	InventoryObject getIconForObject(byte index);
 	byte *loadExtra();
 
 	byte *alfredIdle[4]; // 4 directions
@@ -66,6 +69,11 @@ public:
 	byte *_verbIcons[9];
 	byte *_popUpBalloon = nullptr;
 	Common::Array<Common::StringArray> _ingameTexts;
+
+	//Special anims
+	byte *_specialAnimData = nullptr;
+	int _specialAnimCurFrame = 0;
+	AlfredSpecialAnimOffset _curSpecialAnim;
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 00f16d8af42..265396014b4 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -91,7 +91,7 @@ void RoomManager::getBackground(Common::File *roomFile, int roomOffset, byte *ba
 void RoomManager::addSticker(int stickerId, bool persist) {
 	Sticker sticker = g_engine->_res->getSticker(stickerId);
 	if (persist)
-		g_engine->_state.roomStickers[_currentRoomNumber].push_back(sticker);
+		g_engine->_state->roomStickers[_currentRoomNumber].push_back(sticker);
 	else
 		_transientStickers.push_back(sticker);
 }
@@ -108,21 +108,21 @@ void RoomManager::removeSticker(int stickerIndex) {
 		}
 	}
 
-	for (int i = 0; i < g_engine->_state.roomStickers[_currentRoomNumber].size(); i++) {
-		if (g_engine->_state.roomStickers[_currentRoomNumber][i].stickerIndex == stickerIndex) {
+	for (int i = 0; i < g_engine->_state->roomStickers[_currentRoomNumber].size(); i++) {
+		if (g_engine->_state->roomStickers[_currentRoomNumber][i].stickerIndex == stickerIndex) {
 			index = i;
-			g_engine->_state.roomStickers[_currentRoomNumber].remove_at(index);
+			g_engine->_state->roomStickers[_currentRoomNumber].remove_at(index);
 			break;
 		}
 	}
 
-	if (index != -1 && index < g_engine->_state.roomStickers[_currentRoomNumber].size())
-		g_engine->_state.roomStickers[_currentRoomNumber].remove_at(index);
+	if (index != -1 && index < g_engine->_state->roomStickers[_currentRoomNumber].size())
+		g_engine->_state->roomStickers[_currentRoomNumber].remove_at(index);
 }
 
 bool RoomManager::hasSticker(int index) {
-	for (int i = 0; i < g_engine->_state.roomStickers[_currentRoomNumber].size(); i++) {
-		if (g_engine->_state.roomStickers[_currentRoomNumber][i].stickerIndex == index) {
+	for (int i = 0; i < g_engine->_state->roomStickers[_currentRoomNumber].size(); i++) {
+		if (g_engine->_state->roomStickers[_currentRoomNumber][i].stickerIndex == index) {
 			return true;
 		}
 	}
@@ -139,7 +139,7 @@ bool RoomManager::hasSticker(int index) {
 void RoomManager::changeExit(int index, bool enabled, bool persist) {
 	_currentRoomExits[index].isEnabled = enabled;
 	if (persist)
-		g_engine->_state.roomExitChanges[_currentRoomNumber].push_back({_currentRoomNumber, _currentRoomExits[index].index, _currentRoomExits[index]});
+		g_engine->_state->roomExitChanges[_currentRoomNumber].push_back({_currentRoomNumber, _currentRoomExits[index].index, _currentRoomExits[index]});
 }
 
 void RoomManager::disableExit(int index, bool persist) {
@@ -151,18 +151,18 @@ void RoomManager::enableExit(int index, bool persist) {
 }
 
 void RoomManager::changeWalkBox(WalkBox walkbox) {
-	g_engine->_state.roomWalkBoxChanges[_currentRoomNumber].push_back({_currentRoomNumber, walkbox.index, walkbox});
+	g_engine->_state->roomWalkBoxChanges[_currentRoomNumber].push_back({_currentRoomNumber, walkbox.index, walkbox});
 }
 
 void RoomManager::changeHotSpot(HotSpot hotspot) {
-	g_engine->_state.roomHotSpotChanges[_currentRoomNumber].push_back({_currentRoomNumber, hotspot.innerIndex, hotspot});
+	g_engine->_state->roomHotSpotChanges[_currentRoomNumber].push_back({_currentRoomNumber, hotspot.innerIndex, hotspot});
 }
 
 void RoomManager::disableSprite(int roomNumber, int spriteIndex, bool persist) {
 	if(roomNumber == _currentRoomNumber) {
 		_currentRoomAnims[spriteIndex].zOrder = 255;
 	}
-	g_engine->_state.disabledSprites[roomNumber].push_back(spriteIndex);
+	g_engine->_state->disabledSprites[roomNumber].push_back(spriteIndex);
 }
 
 void RoomManager::enableSprite(int spriteIndex, int zOrder, bool persist) {
@@ -184,7 +184,7 @@ void RoomManager::disableHotspot(HotSpot *hotspot, bool persist) {
 }
 
 void RoomManager::addWalkbox(WalkBox walkbox) {
-	g_engine->_state.roomWalkBoxChanges[_currentRoomNumber].push_back({_currentRoomNumber, walkbox.index, walkbox});
+	g_engine->_state->roomWalkBoxChanges[_currentRoomNumber].push_back({_currentRoomNumber, walkbox.index, walkbox});
 }
 
 HotSpot *RoomManager::findHotspotByExtra(uint16 extra) {
@@ -239,11 +239,11 @@ Common::Array<Exit> RoomManager::loadExits(byte *data, size_t size) {
 	for (int i = 0; i < exitCount; i++) {
 		int exitOffset = exitDataOffset + i * 14;
 		bool isChanged = false;
-		if (g_engine->_state.roomExitChanges.contains(_currentRoomNumber)) {
+		if (g_engine->_state->roomExitChanges.contains(_currentRoomNumber)) {
 			// if the exit has been changed, load the changed version
-			for (int j = 0; j < g_engine->_state.roomExitChanges[_currentRoomNumber].size(); j++) {
-				if (g_engine->_state.roomExitChanges[_currentRoomNumber][j].exitIndex == i) {
-					exits.push_back(g_engine->_state.roomExitChanges[_currentRoomNumber][j].exit);
+			for (int j = 0; j < g_engine->_state->roomExitChanges[_currentRoomNumber].size(); j++) {
+				if (g_engine->_state->roomExitChanges[_currentRoomNumber][j].exitIndex == i) {
+					exits.push_back(g_engine->_state->roomExitChanges[_currentRoomNumber][j].exit);
 					isChanged = true;
 					break;
 				}
@@ -303,11 +303,11 @@ Common::Array<HotSpot> RoomManager::loadHotspots(byte *data, size_t size) {
 		spot.innerIndex = i;
 		spot.index = i;
 		bool isChanged = false;
-		if (g_engine->_state.roomHotSpotChanges.contains(_currentRoomNumber)) {
+		if (g_engine->_state->roomHotSpotChanges.contains(_currentRoomNumber)) {
 			// if the hotspot has been changed, load the changed version
-			for (int j = 0; j < g_engine->_state.roomHotSpotChanges[_currentRoomNumber].size(); j++) {
-				if (g_engine->_state.roomHotSpotChanges[_currentRoomNumber][j].hotspotIndex == spot.innerIndex) {
-					hotspots.push_back(g_engine->_state.roomHotSpotChanges[_currentRoomNumber][j].hotspot);
+			for (int j = 0; j < g_engine->_state->roomHotSpotChanges[_currentRoomNumber].size(); j++) {
+				if (g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspotIndex == spot.innerIndex) {
+					hotspots.push_back(g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot);
 					isChanged = true;
 					break;
 				}
@@ -352,7 +352,6 @@ void RoomManager::resetConversationStates(byte roomNumber, byte *conversationDat
 			// Not the room we care about, skip
 			continue;
 		}
-		debug("Resetting room %d conversation data at offset %d, size %d", entry.room, entry.offset, entry.dataSize);
 		Common::copy(entry.data, entry.data + entry.dataSize, conversationData + entry.offset);
 		// delete[] entry.data;
 	}
@@ -505,7 +504,7 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 	uint32_t metadata_start = spriteCountPos + (44 * 2 + 5);
 	uint32_t picOffset = 0;
 
-	Common::Array<int> disabledSprites = g_engine->_state.disabledSprites[_currentRoomNumber];
+	Common::Array<int> disabledSprites = g_engine->_state->disabledSprites[_currentRoomNumber];
 
 	for (int i = 0; i < spriteCount; i++) {
 		uint32_t animOffset = metadata_start + (i * 44);
@@ -515,18 +514,20 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 		sprite.y = READ_LE_INT16(data + animOffset + 2);
 		sprite.w = data[animOffset + 4];
 		sprite.h = data[animOffset + 5];
-		sprite.extra = data[animOffset + 6];
+		sprite.stride = READ_LE_INT16(data + animOffset + 6);
 		sprite.numAnims = data[animOffset + 8];
 		sprite.zOrder = data[animOffset + 23];
+		sprite.extra = data[animOffset + 32];
+		sprite.spriteType = data[animOffset + 33];
+		sprite.actionFlags = data[animOffset + 34];
+		sprite.isHotspotDisabled = data[animOffset + 38];
 		for(int i = 0; i < disabledSprites.size(); i++) {
 			if (disabledSprites[i] == sprite.index) {
 				sprite.zOrder = 255;
+				sprite.isHotspotDisabled = 1;
 				break;
 			}
 		}
-		sprite.spriteType = data[animOffset + 33];
-		sprite.actionFlags = data[animOffset + 34];
-		sprite.isHotspotDisabled = data[animOffset + 38];
 		if (sprite.numAnims == 0) {
 			break;
 		}
@@ -589,11 +590,11 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(byte *data, size_t size) {
 		WalkBox box;
 		box.index = i;
 		bool isChanged = false;
-		if (g_engine->_state.roomWalkBoxChanges.contains(_currentRoomNumber)) {
+		if (g_engine->_state->roomWalkBoxChanges.contains(_currentRoomNumber)) {
 			// if the walkbox has been changed, load the changed version
-			for (int j = 0; j < g_engine->_state.roomWalkBoxChanges[_currentRoomNumber].size(); j++) {
-				if (g_engine->_state.roomWalkBoxChanges[_currentRoomNumber][j].walkboxIndex == i) {
-					walkboxes.push_back(g_engine->_state.roomWalkBoxChanges[_currentRoomNumber][j].walkbox);
+			for (int j = 0; j < g_engine->_state->roomWalkBoxChanges[_currentRoomNumber].size(); j++) {
+				if (g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkboxIndex == i) {
+					walkboxes.push_back(g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkbox);
 					isChanged = true;
 					break;
 				}
@@ -609,18 +610,18 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(byte *data, size_t size) {
 		walkboxes.push_back(box);
 	}
 
-	if (g_engine->_state.roomWalkBoxChanges.contains(_currentRoomNumber)) {
+	if (g_engine->_state->roomWalkBoxChanges.contains(_currentRoomNumber)) {
 		// Add any new walkboxes that were added
-		for (int j = 0; j < g_engine->_state.roomWalkBoxChanges[_currentRoomNumber].size(); j++) {
+		for (int j = 0; j < g_engine->_state->roomWalkBoxChanges[_currentRoomNumber].size(); j++) {
 			bool found = false;
 			for (int i = 0; i < walkboxes.size(); i++) {
-				if (g_engine->_state.roomWalkBoxChanges[_currentRoomNumber][j].walkboxIndex == walkboxes[i].index) {
+				if (g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkboxIndex == walkboxes[i].index) {
 					found = true;
 					break;
 				}
 			}
 			if (!found) {
-				walkboxes.push_back(g_engine->_state.roomWalkBoxChanges[_currentRoomNumber][j].walkbox);
+				walkboxes.push_back(g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkbox);
 			}
 		}
 	}
@@ -673,13 +674,13 @@ void RoomManager::loadConversationData(byte *pair12data, size_t pair12size, uint
 	}
 	outConversationData = new byte[outConversationDataSize];
 	Common::copy(pair12data + conversationStart, pair12data + conversationStart + outConversationDataSize, outConversationData);
-	if (g_engine->_state.disabledBranches.contains(_currentRoomNumber)) {
+	if (g_engine->_state->disabledBranches.contains(_currentRoomNumber)) {
 		applyDisabledChoices(_currentRoomNumber, outConversationData, outConversationDataSize);
 	}
 }
 
 void RoomManager::applyDisabledChoices(int roomNumber, byte *conversationData, size_t conversationDataSize) {
-	Common::Array<ResetEntry> disabledBranches = g_engine->_state.disabledBranches[roomNumber];
+	Common::Array<ResetEntry> disabledBranches = g_engine->_state->disabledBranches[roomNumber];
 	if (disabledBranches.size() == 0) {
 		return;
 	}
@@ -705,7 +706,7 @@ void RoomManager::addDisabledChoice(ChoiceOption choice) {
 	// Apply immediately
 	applyDisabledChoice(resetEntry, _conversationData, _conversationDataSize);
 	// Store for future loads
-	g_engine->_state.addDisabledBranch(resetEntry);
+	g_engine->_state->addDisabledBranch(resetEntry);
 }
 
 void RoomManager::resetMetadataDefaults(byte room, byte *&data, size_t size) {
@@ -730,7 +731,6 @@ void RoomManager::resetMetadataDefaults(byte room, byte *&data, size_t size) {
 			// Not the room we care about, skip
 			continue;
 		}
-		debug("Resetting room %d metadata at offset %d, size %d", entry.room, entry.offset, entry.dataSize);
 		Common::copy(entry.data, entry.data + entry.dataSize, data + entry.offset);
 		// delete[] entry.data;
 	}
diff --git a/engines/pelrock/saveload.cpp b/engines/pelrock/saveload.cpp
index 259a1c74735..63184033f7f 100644
--- a/engines/pelrock/saveload.cpp
+++ b/engines/pelrock/saveload.cpp
@@ -31,19 +31,6 @@ namespace Pelrock {
 // Helper functions for syncing structs
 void syncSticker(Common::Serializer &s, Sticker &sticker) {
 	s.syncAsSint32LE(sticker.stickerIndex);
-	// if(s.isLoading()) {
-
-	// }
-	// s.syncAsSint32LE(sticker.roomNumber);
-
-	// if(s.isLoading()) {
-	//     sticker.stickerData = new byte[sticker.w * sticker.h];
-	// }
-	// s.syncAsUint16LE(sticker.x);
-	// s.syncAsUint16LE(sticker.y);
-	// s.syncAsByte(sticker.w);
-	// s.syncAsByte(sticker.h);
-	// // Note: stickerData pointer not serialized - must be reconstructed on load
 }
 
 void syncExit(Common::Serializer &s, Exit &exit) {
@@ -119,7 +106,13 @@ bool syncGeneralData(Common::Serializer &s, SaveGameData *game) {
 	// Uint16
 	s.syncAsUint16LE(game->alfredX);
 	s.syncAsUint16LE(game->alfredY);
-	s.syncAsByte(game->alfredDir);
+	s.syncAsByte((byte &)game->alfredDir);
+
+	if (s.isLoading()) {
+		debug("LOAD: room=%d, x=%d, y=%d, dir=%d", game->currentRoom, game->alfredX, game->alfredY, game->alfredDir);
+	} else {
+		debug("SAVE: room=%d, x=%d, y=%d, dir=%d", game->currentRoom, game->alfredX, game->alfredY, game->alfredDir);
+	}
 
 	return !s.err();
 }
@@ -279,7 +272,7 @@ bool syncGameStateData(Common::Serializer &s, GameStateData *gameState) {
 			s.syncAsUint16LE(numSprites);
 			for (uint16 i = 0; i < numSprites; ++i) {
 				int spriteIndex = sprites[i];
-				s.syncAsSint32LE(spriteIndex);
+				s.syncAsByte(spriteIndex);
 			}
 		}
 	} else {
@@ -292,7 +285,7 @@ bool syncGameStateData(Common::Serializer &s, GameStateData *gameState) {
 			Common::Array<int> sprites;
 			for (uint16 i = 0; i < numSprites; ++i) {
 				int spriteIndex;
-				s.syncAsSint32LE(spriteIndex);
+				s.syncAsByte(spriteIndex);
 				sprites.push_back(spriteIndex);
 			}
 			gameState->disabledSprites[roomNumber] = sprites;
@@ -367,19 +360,19 @@ void PelrockEngine::loadGame(SaveGameData &saveGame) {
 	_alfredState.x = saveGame.alfredX;
 	_alfredState.y = saveGame.alfredY;
 	_alfredState.direction = (AlfredDirection)saveGame.alfredDir;
-	_state = *(saveGame.gameState);
+	_state = saveGame.gameState;
 
 	setScreen(saveGame.currentRoom, _alfredState.direction);
-	_state.stateGame = GAME;
+	_state->stateGame = GAME;
 }
 
 SaveGameData *PelrockEngine::createSaveGameData() const {
 	SaveGameData *saveGame = new SaveGameData();
-	saveGame->gameState = &g_engine->_state;
 	saveGame->currentRoom = _room->_currentRoomNumber;
 	saveGame->alfredX = _alfredState.x;
 	saveGame->alfredY = _alfredState.y;
 	saveGame->alfredDir = _alfredState.direction;
+	saveGame->gameState = g_engine->_state;
 	return saveGame;
 }
 
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index cba53b03780..31bfa59f5ad 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -198,14 +198,14 @@ bool SoundManager::isMusicPlaying() {
 	return g_system->getAudioCDManager()->isPlaying();
 }
 
-void SoundManager::playMusicTrack(int trackNumber) {
+void SoundManager::playMusicTrack(int trackNumber, bool loop) {
 	if (_currentMusicTrack == trackNumber && isMusicPlaying()) {
 		// Already playing this track
 		return;
 	}
 	_currentMusicTrack = trackNumber;
 	g_system->getAudioCDManager()->stop();
-	// g_system->getAudioCDManager()->play(trackNumber, -1, 0, 0);
+	// g_system->getAudioCDManager()->play(trackNumber, loop ? -1 : 0, 0, 0);
 }
 
 void SoundManager::loadSoundIndex() {
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index ee305f6a20d..d2733e83e7a 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -196,7 +196,7 @@ public:
 	void setVolume(int volume);
 	bool isPlaying() const;
 	bool isPlaying(int channel) const;
-	void playMusicTrack(int trackNumber);
+	void playMusicTrack(int trackNumber, bool loop = true);
 	bool isMusicPlaying() const {
 		return _isMusicPlaying;
 	}
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 3175c3080ca..e2d0a2bf59c 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -102,7 +102,8 @@ enum AlfredAnimState {
 	ALFRED_WALKING,
 	ALFRED_TALKING,
 	ALFRED_INTERACTING,
-	ALFRED_COMB
+	ALFRED_COMB,
+	ALFRED_SPECIAL_ANIM
 };
 
 enum AlfredDirection {
@@ -118,6 +119,7 @@ struct ActionPopupState {
 	int x = 0;
 	int y = 0;
 	int displayTime = 0;
+	bool isAlfredUnder = false;
 };
 
 struct AlfredState {
@@ -191,7 +193,7 @@ struct Sprite {
 	int16 y;      // 2
 	int w;        // 4
 	int h;        // 5
-	byte extra;   // 6
+	uint16 stride; // 6-7
 	int numAnims; // 8
 	int curAnimIndex = 0;
 	int8 zOrder;
@@ -200,6 +202,7 @@ struct Sprite {
 	bool isHotspotDisabled;  // 38
 	bool isTalking = false;
 	Anim *animData;
+	byte extra;
 };
 
 struct HotSpot {
@@ -381,17 +384,63 @@ struct ResetEntry {
 	byte *data = nullptr;
 };
 
-
 struct GameStateData {
 	bool JEFE_INGRESA_PASTA = false;
 	bool JEFE_ENCARCELADO = false;
 	bool PUESTA_SALSA_PICANTE = false;
+	bool CRISTAL_ROTO = false;
+	bool ENTRA_EN_TIENDA_PRIMERA_VEZ = false;
+	bool ELECTROCUTACION = false;
+	bool CABLES_PUESTOS = false;
+	bool SOBORNO_PORTERO = false;
+	bool MEMORIZA_LIBRO = false;
+	bool ALFRED_INTELIGENTE = false;
+	bool ALFRED_SABE_EGIPCIO = false;
+	bool VENDEDOR_DEJA_DE_JODER = false;
+	bool VIAJE_A_EGIPTO = false;
+	bool PARADOJA_RESUELTA = false;
+	bool CROCODILLO_ENCENDIDO = false;
+	bool MIRA_SIMBOLO_FUERA_MUSEO = false;
+	bool PUERTA_SECRETA_ABIERTA = false;
+	bool ROBA_PELO_PRINCESA = false;
+	bool A_LA_CARCEL = false;
+	bool CLAVE_CAJA_FUERTE = false;
+	bool SE_HA_PUESTO_EL_MUNECO = false;
+	bool VIGILANTE_BEBE_AGUA = false;
+	bool VIGILANTE_MEANDO = false;
+	bool PIRAMIDE_JODIDA = false;
+	bool PIRAMIDE_JODIDA2 = false;
+	bool VIGILANTE_PAJEANDOSE = false;
+	bool FORMULA_MAGICA = false;
+	bool VIAJA_AL_PASADO = false;
+	bool APARECE_EUNUCO = false;
+	bool AL_FARAON = false;
+	bool A_CURRAR = false;
+	bool DA_PIEDRA = false;
+	byte NUMERO_DE_COPAS = false;
+	bool PIEDRAS_COGIDAS = false;
+	bool GUARDIAS_BORRACHOS = false;
+	bool PIEDRA_FAKE_MOJADA = false;
+	bool PUERTA_BUENA = false;
+	bool TRAMPILLA_ABIERTA = false;
+	bool HABITACION_PRINCESA = false;
+	bool A_POR_LA_PRINCESA = false;
+	bool VUELTA_A_EMPEZAR = false;
+	bool A_LOS_PASILLOS = false;
+	bool COMO_ESTAN_LOS_DIOSES = false;
+	bool END_OF_GAME = false;
+	bool FROM_INTRO = false;
+	byte INGREDIENTES_CONSEGUIDOS = 0;
+	bool HE_TIRADO_PIEDRA = false;
+	bool HA_USADO_AGUA = false;
 
 	GameState stateGame = INTRO;
 
 	Common::Array<byte> inventoryItems;
 	int16 selectedInventoryItem = -1;
+
 	Common::HashMap<byte, Common::Array<Sticker>> roomStickers;
+	// Common::HashMap<byte, ResetEntry> roomExitChanges;
 	Common::HashMap<byte, Common::Array<ExitChange>> roomExitChanges;
 	Common::HashMap<byte, Common::Array<WalkBoxChange>> roomWalkBoxChanges;
 	Common::HashMap<byte, Common::Array<HotSpotChange>> roomHotSpotChanges;
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 7ead6a86730..05f9d9e231d 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -323,6 +323,17 @@ byte decodeChar(byte b) {
 	}
 }
 
+
+void changeGameSpeed(Common::Event e) {
+	if (e.type == Common::EVENT_KEYDOWN) {
+		if (e.kbd.hasFlags(Common::KBD_CTRL)) {
+			if (e.kbd.keycode == Common::KEYCODE_f) {
+				g_engine->_chrono->changeSpeed();
+			}
+		}
+	}
+}
+
 Common::StringArray arrayOf(Common::String str) {
 	return Common::StringArray(1, str);
 }
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index 62a70260476..9af486dbe51 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -21,8 +21,10 @@
 #ifndef PELROCK_UTIL_H
 #define PELROCK_UTIL_H
 
+#include "common/events.h"
 #include "common/stream.h"
 #include "common/types.h"
+
 #include "graphics/font.h"
 #include "graphics/managed_surface.h"
 #include "graphics/surface.h"
@@ -43,6 +45,7 @@ void drawText(Graphics::Font *font, Common::String text, int x, int y, int w, by
 Common::String joinStrings(const Common::Array<Common::String> &strings, const Common::String &separator);
 void drawPos(Graphics::ManagedSurface *surface, int x, int y, byte color);
 byte decodeChar(byte b);
+void changeGameSpeed(Common::Event e);
 Common::StringArray arrayOf(Common::String str);
 
 static const int special_chars[] = {


Commit: d3db3eff4809f43408fae182b8413183bbc42951
    https://github.com/scummvm/scummvm/commit/d3db3eff4809f43408fae182b8413183bbc42951
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:45+02:00

Commit Message:
PELROCK: Use stuff with Alfred

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/saveload.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 17d229c94cb..0db963320e8 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -117,8 +117,8 @@ void PelrockEngine::closeRoomDrawer(HotSpot *hotspot) {
 
 void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
 	debug("Withdrawing money from ATM using card (inv obj %d)", inventoryObject);
-	if (_state->JEFE_INGRESA_PASTA) {
-		_state->JEFE_INGRESA_PASTA = 0;
+	if (_state->flagIsSet(FLAG_JEFE_INGRESA_PASTA)) {
+		_state->setFlag(FLAG_JEFE_INGRESA_PASTA, false);
 		addInventoryItem(75);
 	} else {
 		int billCount = 0;
@@ -171,12 +171,12 @@ void PelrockEngine::closeKitchenDoor(HotSpot *HotSpot) {
 }
 
 void PelrockEngine::openKitchenDrawer(HotSpot *hotspot) {
-	if(_state->JEFE_ENCARCELADO == false) {
+	if(!_state->flagIsSet(FLAG_JEFE_ENCARCELADO)) {
 		_dialog->say(_res->_ingameTexts[QUITA_ESAS_MANOS]);
 	}
 	else {
 		_room->addSticker(36);
-		addInventoryItem(73); // Add recipe
+		addInventoryItem(63); // Add recipe
 		_dialog->say(_res->_ingameTexts[QUESESTO_RECETA]);
 	}
 }
@@ -186,7 +186,7 @@ void PelrockEngine::openKitchenDoorFromInside(HotSpot *hotspot) {
 }
 
 void PelrockEngine::useSpicySauceWithBurger(int inventoryObject, HotSpot *hotspot) {
-	_state->PUESTA_SALSA_PICANTE = true;
+	_state->setFlag(FLAG_PUESTA_SALSA_PICANTE, true);
 	_dialog->say(_res->_ingameTexts[VAESTAR_POCOFUERTE]);
 }
 
@@ -265,11 +265,11 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 	debug("Using item %d on Alfred", inventoryObject);
 	switch (inventoryObject)
 	{
-	case 73: // Recipe book
+	case 63: // Recipe book
 		_res->loadAlfredSpecialAnim(0);
 		_alfredState.animState = ALFRED_SPECIAL_ANIM;
 		loadExtraScreenAndPresent(3);
-		_dialog->say(_res->_ingameTexts[QUEASCO]);
+		// _dialog->say(_res->_ingameTexts[QUEASCO]);
 		break;
 
 	default:
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index b367960b160..7438fdbc8f6 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -877,8 +877,8 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 		_actionPopupState.isActive = true;
 		_actionPopupState.curFrame = 0;
 
+		_actionPopupState.isAlfredUnder = alfredUnder;
 		if (hotspotIndex != -1) {
-			_actionPopupState.isAlfredUnder = alfredUnder;
 			_currentHotspot = &_room->_currentRoomHotspots[hotspotIndex];
 		}
 	}
@@ -1381,9 +1381,10 @@ void PelrockEngine::loadExtraScreenAndPresent(int screenIndex) {
 		_extraScreen = new byte[640 * 400];
 	}
 	_res->getExtraScreen(screenIndex, _extraScreen, palette);
-
+	CursorMan.showMouse(false);
 	g_system->getPaletteManager()->setPalette(palette, 0, 256);
 	extraScreenLoop();
+	CursorMan.showMouse(true);
 	delete[] _extraScreen;
 	delete[] palette;
 	_screen->markAllDirty();
@@ -1393,8 +1394,8 @@ void PelrockEngine::loadExtraScreenAndPresent(int screenIndex) {
 void PelrockEngine::doExtraActions(int roomNumber) {
 	switch (roomNumber) {
 	case 4:
-		if (_state->PUESTA_SALSA_PICANTE && !_state->JEFE_ENCARCELADO) {
-			_state->JEFE_ENCARCELADO = true;
+		if (_state->flagIsSet(FLAG_PUESTA_SALSA_PICANTE) && !_state->flagIsSet(FLAG_JEFE_ENCARCELADO)) {
+			_state->setFlag(FLAG_JEFE_ENCARCELADO, true);
 			_room->disableSprite(13, 0, true);
 			loadExtraScreenAndPresent(4);
 			_screen->markAllDirty();
diff --git a/engines/pelrock/saveload.cpp b/engines/pelrock/saveload.cpp
index 63184033f7f..4f92d03b297 100644
--- a/engines/pelrock/saveload.cpp
+++ b/engines/pelrock/saveload.cpp
@@ -121,6 +121,10 @@ bool syncGameStateData(Common::Serializer &s, GameStateData *gameState) {
 	// GameState
 	s.syncAsUint32LE((uint32 &)gameState->stateGame);
 
+	// Flags
+	for (int i = 0; i < 46; ++i) {
+		s.syncAsByte((byte &)gameState->flags[i]);
+	}
 	// Inventory items
 	uint16 inventorySize = (uint16)gameState->inventoryItems.size();
 	s.syncAsUint16LE(inventorySize);
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index e2d0a2bf59c..93d477a02d7 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -189,17 +189,17 @@ struct Exit {
 struct Sprite {
 	byte index; // number of the animation in the rooms
 	byte type;
-	int16 x;      // 0
-	int16 y;      // 2
-	int w;        // 4
-	int h;        // 5
+	int16 x;       // 0
+	int16 y;       // 2
+	int w;         // 4
+	int h;         // 5
 	uint16 stride; // 6-7
-	int numAnims; // 8
+	int numAnims;  // 8
 	int curAnimIndex = 0;
 	int8 zOrder;
-	byte spriteType;  // 33
-	byte actionFlags; // 34
-	bool isHotspotDisabled;  // 38
+	byte spriteType;        // 33
+	byte actionFlags;       // 34
+	bool isHotspotDisabled; // 38
 	bool isTalking = false;
 	Anim *animData;
 	byte extra;
@@ -384,55 +384,58 @@ struct ResetEntry {
 	byte *data = nullptr;
 };
 
+#define FLAG_JEFE_INGRESA_PASTA 0
+#define FLAG_JEFE_ENCARCELADO 1
+#define FLAG_PUESTA_SALSA_PICANTE 2
+#define FLAG_CRISTAL_ROTO 3
+#define FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ 4
+#define FLAG_ELECTROCUTACION 5
+#define FLAG_CABLES_PUESTOS 6
+#define FLAG_SOBORNO_PORTERO 7
+#define FLAG_MEMORIZA_LIBRO 8
+#define FLAG_ALFRED_INTELIGENTE 9
+#define FLAG_ALFRED_SABE_EGIPCIO 10
+#define FLAG_VENDEDOR_DEJA_DE_JODER 11
+#define FLAG_VIAJE_A_EGIPTO 12
+#define FLAG_PARADOJA_RESUELTA 13
+#define FLAG_CROCODILLO_ENCENDIDO 14
+#define FLAG_MIRA_SIMBOLO_FUERA_MUSEO 15
+#define FLAG_PUERTA_SECRETA_ABIERTA 16
+#define FLAG_ROBA_PELO_PRINCESA 17
+#define FLAG_A_LA_CARCEL 18
+#define FLAG_CLAVE_CAJA_FUERTE 19
+#define FLAG_SE_HA_PUESTO_EL_MUNECO 20
+#define FLAG_VIGILANTE_BEBE_AGUA 21
+#define FLAG_VIGILANTE_MEANDO 22
+#define FLAG_PIRAMIDE_JODIDA 23
+#define FLAG_PIRAMIDE_JODIDA2 24
+#define FLAG_VIGILANTE_PAJEANDOSE 25
+#define FLAG_FORMULA_MAGICA 26
+#define FLAG_VIAJA_AL_PASADO 27
+#define FLAG_APARECE_EUNUCO 28
+#define FLAG_AL_FARAON 29
+#define FLAG_A_CURRAR 30
+#define FLAG_DA_PIEDRA 31
+#define FLAG_PIEDRAS_COGIDAS 32
+#define FLAG_GUARDIAS_BORRACHOS 33
+#define FLAG_PIEDRA_FAKE_MOJADA 34
+#define FLAG_PUERTA_BUENA 35
+#define FLAG_TRAMPILLA_ABIERTA 36
+#define FLAG_HABITACION_PRINCESA 37
+#define FLAG_A_POR_LA_PRINCESA 38
+#define FLAG_VUELTA_A_EMPEZAR 39
+#define FLAG_A_LOS_PASILLOS 40
+#define FLAG_COMO_ESTAN_LOS_DIOSES 41
+#define FLAG_END_OF_GAME 42
+#define FLAG_FROM_INTRO 43
+#define FLAG_HE_TIRADO_PIEDRA 44
+#define FLAG_HA_USADO_AGUA 45
+
 struct GameStateData {
-	bool JEFE_INGRESA_PASTA = false;
-	bool JEFE_ENCARCELADO = false;
-	bool PUESTA_SALSA_PICANTE = false;
-	bool CRISTAL_ROTO = false;
-	bool ENTRA_EN_TIENDA_PRIMERA_VEZ = false;
-	bool ELECTROCUTACION = false;
-	bool CABLES_PUESTOS = false;
-	bool SOBORNO_PORTERO = false;
-	bool MEMORIZA_LIBRO = false;
-	bool ALFRED_INTELIGENTE = false;
-	bool ALFRED_SABE_EGIPCIO = false;
-	bool VENDEDOR_DEJA_DE_JODER = false;
-	bool VIAJE_A_EGIPTO = false;
-	bool PARADOJA_RESUELTA = false;
-	bool CROCODILLO_ENCENDIDO = false;
-	bool MIRA_SIMBOLO_FUERA_MUSEO = false;
-	bool PUERTA_SECRETA_ABIERTA = false;
-	bool ROBA_PELO_PRINCESA = false;
-	bool A_LA_CARCEL = false;
-	bool CLAVE_CAJA_FUERTE = false;
-	bool SE_HA_PUESTO_EL_MUNECO = false;
-	bool VIGILANTE_BEBE_AGUA = false;
-	bool VIGILANTE_MEANDO = false;
-	bool PIRAMIDE_JODIDA = false;
-	bool PIRAMIDE_JODIDA2 = false;
-	bool VIGILANTE_PAJEANDOSE = false;
-	bool FORMULA_MAGICA = false;
-	bool VIAJA_AL_PASADO = false;
-	bool APARECE_EUNUCO = false;
-	bool AL_FARAON = false;
-	bool A_CURRAR = false;
-	bool DA_PIEDRA = false;
+	bool flags[46];
+
 	byte NUMERO_DE_COPAS = false;
-	bool PIEDRAS_COGIDAS = false;
-	bool GUARDIAS_BORRACHOS = false;
-	bool PIEDRA_FAKE_MOJADA = false;
-	bool PUERTA_BUENA = false;
-	bool TRAMPILLA_ABIERTA = false;
-	bool HABITACION_PRINCESA = false;
-	bool A_POR_LA_PRINCESA = false;
-	bool VUELTA_A_EMPEZAR = false;
-	bool A_LOS_PASILLOS = false;
-	bool COMO_ESTAN_LOS_DIOSES = false;
-	bool END_OF_GAME = false;
-	bool FROM_INTRO = false;
 	byte INGREDIENTES_CONSEGUIDOS = 0;
-	bool HE_TIRADO_PIEDRA = false;
-	bool HA_USADO_AGUA = false;
 
 	GameState stateGame = INTRO;
 
@@ -460,11 +463,23 @@ struct GameStateData {
 		disabledBranches[entry.room].push_back(entry);
 	}
 
+	bool flagIsSet(int flagIndex) const {
+		if (flagIndex < 0 || flagIndex >= 46)
+			return false;
+		return flags[flagIndex];
+	}
+
+	void setFlag(int flagIndex, bool value) {
+		if (flagIndex < 0 || flagIndex >= 46)
+			return;
+		flags[flagIndex] = value;
+	}
+
 	void addInventoryItem(int id) {
 		inventoryItems.push_back(id);
 	}
 	void removeInventoyItem(int id) {
-		for(int i = 0; i < inventoryItems.size(); i++) {
+		for (int i = 0; i < inventoryItems.size(); i++) {
 			if (inventoryItems[i] == id) {
 				inventoryItems.remove_at(i);
 				return;


Commit: 8f89c446f2062c7dcdbcda5102a75934395ed38c
    https://github.com/scummvm/scummvm/commit/8f89c446f2062c7dcdbcda5102a75934395ed38c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:46+02:00

Commit Message:
PELROCK: Reading recipe

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/events.cpp
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 0db963320e8..cf0dc9853a4 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -71,7 +71,7 @@ const ActionEntry actionTable[] = {
 	{WILDCARD, NO_ACTION, nullptr}};
 
 const CombinationEntry combinationTable[] = {
-	{2, 281, &PelrockEngine::useCardWithATM}, // Use ATM Card with ATM
+	{2, 281, &PelrockEngine::useCardWithATM},           // Use ATM Card with ATM
 	{62, 117, &PelrockEngine::useSpicySauceWithBurger}, // Use Spicy Sauce with Burger
 	// End marker
 	{WILDCARD, WILDCARD, nullptr}};
@@ -141,7 +141,7 @@ void PelrockEngine::openMcDoor(HotSpot *hotspot) {
 }
 
 void PelrockEngine::closeMcDoor(HotSpot *hotspot) {
-	//FIXME: Impossible to close right now
+	// FIXME: Impossible to close right now
 	closeDoor(hotspot, 2, 7, FEMININE, false);
 }
 
@@ -171,10 +171,9 @@ void PelrockEngine::closeKitchenDoor(HotSpot *HotSpot) {
 }
 
 void PelrockEngine::openKitchenDrawer(HotSpot *hotspot) {
-	if(!_state->flagIsSet(FLAG_JEFE_ENCARCELADO)) {
+	if (!_state->flagIsSet(FLAG_JEFE_ENCARCELADO)) {
 		_dialog->say(_res->_ingameTexts[QUITA_ESAS_MANOS]);
-	}
-	else {
+	} else {
 		_room->addSticker(36);
 		addInventoryItem(63); // Add recipe
 		_dialog->say(_res->_ingameTexts[QUESESTO_RECETA]);
@@ -263,13 +262,15 @@ void PelrockEngine::noOpItem(int item, HotSpot *hotspot) {
 void PelrockEngine::useOnAlfred(int inventoryObject) {
 
 	debug("Using item %d on Alfred", inventoryObject);
-	switch (inventoryObject)
-	{
+	switch (inventoryObject) {
 	case 63: // Recipe book
-		_res->loadAlfredSpecialAnim(0);
+		_res->loadAlfredSpecialAnim(1);
 		_alfredState.animState = ALFRED_SPECIAL_ANIM;
+		waitForSpecialAnimation();
+		debug("After special anim");
 		loadExtraScreenAndPresent(3);
-		// _dialog->say(_res->_ingameTexts[QUEASCO]);
+		debug("After extra screen");
+		_dialog->say(_res->_ingameTexts[QUEASCO]);
 		break;
 
 	default:
diff --git a/engines/pelrock/events.cpp b/engines/pelrock/events.cpp
index 1b0f4c4ff8a..63bf81876ef 100644
--- a/engines/pelrock/events.cpp
+++ b/engines/pelrock/events.cpp
@@ -83,11 +83,9 @@ void PelrockEventManager::pollEvent() {
 			break;
 		case Common::EVENT_RBUTTONDOWN:
 			_rightMouseButton = 1;
-			debug("Right mouse button down");
 			break;
 		case Common::EVENT_RBUTTONUP:
 			if (_rightMouseButton == 1) {
-				debug("Right mouse clicked");
 				_rightMouseClicked = true;
 			} else {
 				_rightMouseClicked = false;
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index b63745f9d22..03cb71fcdab 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -450,7 +450,7 @@ const ExtraImages extraScreens[] = {
 	{0x647C3, // Alfred circle
 	 0x7B6B1,
 	 4},
-	{0x6FBC9, // Recipe
+	{0x6FBCD, // Recipe
 	 0x7B6B1,
 	 8},
 	{0x7BA11, // Newspaper
@@ -478,11 +478,12 @@ struct AlfredSpecialAnimOffset {
 	int w = 0;
 	int h = 0;
 	int numBudas;
+	int loops;
 	uint32 offset;
 	int stride = 0;
 
-	AlfredSpecialAnimOffset(int nF, int width, int height, int nBudas, uint32 off)
-		: numFrames(nF), w(width), h(height), numBudas(nBudas), offset(off) {
+	AlfredSpecialAnimOffset(int nF, int width, int height, int nBudas, uint32 off, int loops)
+		: numFrames(nF), w(width), h(height), numBudas(nBudas), offset(off), loops(loops) {
 		stride = w * h;
 	}
 	AlfredSpecialAnimOffset() {
@@ -490,7 +491,8 @@ struct AlfredSpecialAnimOffset {
 };
 
 const AlfredSpecialAnimOffset alfredSpecialAnims[] = {
-	{20, 51, 102, 2, 559681}, // READ
+	{10, 51, 102, 1, 559685, 1}, // READ BOOK
+	{10, 51, 102, 1, 578943, 1}, // READ RECIPE
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 7438fdbc8f6..e2794bf23fb 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -297,6 +297,10 @@ void PelrockEngine::checkMouse() {
 
 	// Handle mouse release after long press (popup selection mode)
 	if (_events->_popupSelectionMode && !_events->_leftMouseButton) {
+		_events->_leftMouseButton = false;
+		_events->_leftMouseClicked = false;
+		_events->_popupSelectionMode = false;
+		_actionPopupState.isActive = false;
 		// Mouse was released while popup is active
 		VerbIcon actionClicked = isActionUnder(_events->_releaseX, _events->_releaseY);
 		if (_actionPopupState.isAlfredUnder) {
@@ -311,8 +315,6 @@ void PelrockEngine::checkMouse() {
 			_queuedAction = QueuedAction{NO_ACTION, -1, false};
 			_currentHotspot = nullptr;
 		}
-		_actionPopupState.isActive = false;
-		_events->_popupSelectionMode = false;
 	} else if (_events->_leftMouseClicked) {
 		// Regular click (not during popup mode)
 		checkMouseClick(_events->_mouseClickX, _events->_mouseClickY);
@@ -643,9 +645,15 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		break;
 	}
 	case ALFRED_SPECIAL_ANIM: {
-		if (_res->_specialAnimCurFrame > _res->_curSpecialAnim.numFrames) {
-			_res->clearSpecialAnim();
-			_alfredState.setState(ALFRED_IDLE);
+		if (_res->_specialAnimCurFrame >= _res->_curSpecialAnim.numFrames) {
+			if (_res->_speciaAnimLoopCount < _res->_curSpecialAnim.loops) {
+				_res->_speciaAnimLoopCount++;
+				_res->_specialAnimCurFrame = 0;
+			} else {
+				_res->clearSpecialAnim();
+				_alfredState.setState(ALFRED_IDLE);
+				_res->_isSpecialAnimFinished = true;
+			}
 		} else {
 			byte *frame = new byte[_res->_curSpecialAnim.stride * _res->_curSpecialAnim.numFrames];
 			extractSingleFrame(_res->_specialAnimData,
@@ -1124,9 +1132,8 @@ void PelrockEngine::animateTalkingNPC(Sprite *animSet) {
 
 void PelrockEngine::pickupIconFlash() {
 	_graphics->showOverlay(65, _compositeBuffer);
-	if(_flashingIcon == -1)
+	if (_flashingIcon == -1)
 		return;
-	debug("Flashing icon %d", _flashingIcon);
 	InventoryObject item = _res->getIconForObject(_flashingIcon);
 	if (_chrono->getFrameCount() % kIconBlinkPeriod == 0) {
 		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 5, 400 - 60 - 5, 60, 60, 1);
@@ -1141,7 +1148,6 @@ void PelrockEngine::gameLoop() {
 
 void PelrockEngine::extraScreenLoop() {
 	memcpy(_screen->getPixels(), _extraScreen, 640 * 400);
-
 	while (!shouldQuit()) {
 		_events->pollEvent();
 
@@ -1391,6 +1397,15 @@ void PelrockEngine::loadExtraScreenAndPresent(int screenIndex) {
 	_screen->update();
 }
 
+void PelrockEngine::waitForSpecialAnimation() {
+	while (!g_engine->shouldQuit() && !_res->_isSpecialAnimFinished) {
+		_events->pollEvent();
+		renderScene(OVERLAY_NONE);
+		_screen->update();
+		g_system->delayMillis(10);
+	}
+}
+
 void PelrockEngine::doExtraActions(int roomNumber) {
 	switch (roomNumber) {
 	case 4:
@@ -1398,8 +1413,6 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			_state->setFlag(FLAG_JEFE_ENCARCELADO, true);
 			_room->disableSprite(13, 0, true);
 			loadExtraScreenAndPresent(4);
-			_screen->markAllDirty();
-			_screen->update();
 		}
 		break;
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index b38ba69a017..4e0eeb9f5a5 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -221,6 +221,7 @@ public:
 
 	void setScreen(int s, AlfredDirection dir);
 	void loadExtraScreenAndPresent(int screenIndex);
+	void waitForSpecialAnimation();
 	void doExtraActions(int roomNumber);
 	bool renderScene(int overlayMode = OVERLAY_NONE);
 
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 1c95c4abf24..d951cca7771 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -227,8 +227,10 @@ void ResourceManager::loadAlfredSpecialAnim(int numAnim) {
 
 	alfred7.seek(_curSpecialAnim.offset, SEEK_SET);
 	_specialAnimData = new byte[_curSpecialAnim.numFrames * _curSpecialAnim.w * _curSpecialAnim.h];
-	mergeRleBlocks(&alfred7, _curSpecialAnim.offset, 2, _specialAnimData);
+	debug("Special anim buffer size: %d", _curSpecialAnim.numFrames * _curSpecialAnim.w * _curSpecialAnim.h);
+	mergeRleBlocks(&alfred7, _curSpecialAnim.offset, _curSpecialAnim.numBudas, _specialAnimData);
 	_specialAnimCurFrame = 0;
+	_isSpecialAnimFinished = false;
 	alfred7.close();
 }
 
@@ -236,6 +238,7 @@ void ResourceManager::clearSpecialAnim() {
 	delete[] _specialAnimData;
 	_specialAnimData = nullptr;
 	_specialAnimCurFrame = 0;
+	_speciaAnimLoopCount = 0;
 }
 
 void ResourceManager::loadInventoryItems() {
@@ -393,6 +396,7 @@ void ResourceManager::mergeRleBlocks(Common::SeekableReadStream *stream, uint32
 	for (int i = 0; i < numBlocks; i++) {
 		byte *thisBlock = nullptr;
 		size_t blockSize = 0;
+		uint32 pos = stream->pos();
 		readUntilBuda(stream, stream->pos(), thisBlock, blockSize);
 		uint8_t *block_data = nullptr;
 		size_t decompressedSize = rleDecompress(thisBlock, blockSize, 0, 640 * 400, &block_data, true);
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index 1288d3d6af7..3b1690ed34e 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -23,8 +23,8 @@
 
 #include "common/scummsys.h"
 #include "common/stream.h"
-#include "pelrock/types.h"
 #include "pelrock/offsets.h"
+#include "pelrock/types.h"
 
 namespace Pelrock {
 
@@ -70,10 +70,12 @@ public:
 	byte *_popUpBalloon = nullptr;
 	Common::Array<Common::StringArray> _ingameTexts;
 
-	//Special anims
+	// Special anims
 	byte *_specialAnimData = nullptr;
 	int _specialAnimCurFrame = 0;
+	int _speciaAnimLoopCount = 0;
 	AlfredSpecialAnimOffset _curSpecialAnim;
+	bool _isSpecialAnimFinished = false;
 };
 
 } // End of namespace Pelrock


Commit: e78e9488e07982b57c909cbd1c0ee2ef4f38422e
    https://github.com/scummvm/scummvm/commit/e78e9488e07982b57c909cbd1c0ee2ef4f38422e
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:46+02:00

Commit Message:
PELROCK: Fixes timing issues in sprites, walking speed and palette animations

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index e2794bf23fb..4cb895350c3 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -435,36 +435,47 @@ void PelrockEngine::placeSticker(Sticker sticker) {
 }
 
 void PelrockEngine::animateFadePalette(PaletteAnim *anim) {
-
-	if (anim->data[0] >= anim->data[6] &&
-		anim->data[1] >= anim->data[7] &&
-		anim->data[2] >= anim->data[8]) {
-		anim->data[10] = 0;
-	} else if (anim->data[0] <= anim->data[3] &&
-			   anim->data[1] <= anim->data[4] &&
-			   anim->data[2] <= anim->data[5]) {
-		anim->data[10] = 1;
-	}
-
-	if (anim->data[10]) {
-		if (anim->data[0] < anim->data[6]) {
-			anim->data[0] += anim->data[9];
-		}
-		if (anim->data[1] < anim->data[7]) {
-			anim->data[1] += anim->data[9];
-		}
-		if (anim->data[2] < anim->data[8]) {
-			anim->data[2] += anim->data[9];
+	// FADE palette animation - cycles a single palette entry between min/max RGB values
+	// Data layout (after loading from EXE):
+	//   data[0] = current R
+	//   data[1] = current G
+	//   data[2] = current B
+	//   data[3] = min R
+	//   data[4] = min G
+	//   data[5] = min B
+	//   data[6] = max R
+	//   data[7] = max G
+	//   data[8] = max B
+	//   data[9] = flags byte:
+	//             bits 0-1: R increment
+	//             bits 2-3: G increment
+	//             bits 4-5: B increment
+	//             bit 6: direction (0=decreasing toward min, 1=increasing toward max)
+
+	byte flags = anim->data[9];
+	// Increments are scaled by 4 (<<2) to match the shifted RGB values
+	byte rInc = (flags & 0x03) << 2;
+	byte gInc = ((flags >> 2) & 0x03) << 2;
+	byte bInc = ((flags >> 4) & 0x03) << 2;
+	bool increasing = (flags & 0x40) != 0;
+
+	if (increasing) {
+		// Increasing toward max values
+		anim->data[0] += rInc;
+		anim->data[1] += gInc;
+		anim->data[2] += bInc;
+		// Check if R reached max, then reverse direction
+		if (anim->data[0] >= anim->data[6]) {
+			anim->data[9] &= ~0x40; // Clear direction bit
 		}
 	} else {
-		if (anim->data[0] > anim->data[3]) {
-			anim->data[0] -= anim->data[9];
-		}
-		if (anim->data[1] > anim->data[4]) {
-			anim->data[1] -= anim->data[9];
-		}
-		if (anim->data[2] > anim->data[5]) {
-			anim->data[2] -= anim->data[9];
+		// Decreasing toward min values
+		anim->data[0] -= rInc;
+		anim->data[1] -= gInc;
+		anim->data[2] -= bInc;
+		// Check if R reached min, then reverse direction
+		if (anim->data[0] <= anim->data[3]) {
+			anim->data[9] |= 0x40; // Set direction bit
 		}
 	}
 
@@ -475,8 +486,9 @@ void PelrockEngine::animateFadePalette(PaletteAnim *anim) {
 }
 
 void PelrockEngine::animateRotatePalette(PaletteAnim *anim) {
-	if (anim->curFrameCount >= anim->data[1]) {
-		anim->curFrameCount = 0;
+
+	if (anim->curFrame >= anim->data[1]) {
+		anim->curFrame = 0;
 		int colors = anim->paletteMode;
 		byte *paletteValues = new byte[colors * 3];
 		for (int i = 0; i < colors; i++) {
@@ -494,7 +506,7 @@ void PelrockEngine::animateRotatePalette(PaletteAnim *anim) {
 		g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
 
 	} else {
-		anim->curFrameCount++;
+		anim->curFrame++;
 	}
 }
 
@@ -551,29 +563,29 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		if (step.distanceX > 0) {
 			if (step.flags & MOVE_RIGHT) {
 				_alfredState.direction = ALFRED_RIGHT;
-				_alfredState.x += MIN(_alfredState.movementSpeed, step.distanceX);
+				_alfredState.x += MIN(_alfredState.movementSpeedX, step.distanceX);
 			}
 			if (step.flags & MOVE_LEFT) {
 				_alfredState.direction = ALFRED_LEFT;
-				_alfredState.x -= MIN(_alfredState.movementSpeed, step.distanceX);
+				_alfredState.x -= MIN(_alfredState.movementSpeedX, step.distanceX);
 			}
 		}
 		if (step.distanceY > 0) {
 			if (step.flags & MOVE_DOWN) {
 				_alfredState.direction = ALFRED_DOWN;
-				_alfredState.y += MIN(_alfredState.movementSpeed, step.distanceY);
+				_alfredState.y += MIN(_alfredState.movementSpeedY, step.distanceY);
 			}
 			if (step.flags & MOVE_UP) {
 				_alfredState.direction = ALFRED_UP;
-				_alfredState.y -= MIN(_alfredState.movementSpeed, step.distanceY);
+				_alfredState.y -= MIN(_alfredState.movementSpeedY, step.distanceY);
 			}
 		}
 
 		if (step.distanceX > 0)
-			step.distanceX -= MIN(_alfredState.movementSpeed, step.distanceX);
+			step.distanceX -= MIN(_alfredState.movementSpeedX, step.distanceX);
 
 		if (step.distanceY > 0)
-			step.distanceY -= MIN(_alfredState.movementSpeed, step.distanceY);
+			step.distanceY -= MIN(_alfredState.movementSpeedY, step.distanceY);
 
 		if (step.distanceX <= 0 && step.distanceY <= 0) {
 			_currentStep++;
@@ -617,9 +629,9 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			_alfredState.curFrame = 0;
 		}
 		drawAlfred(_res->alfredTalkFrames[_alfredState.direction][_alfredState.curFrame]);
-		if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0) {
+		// if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0) {
 			_alfredState.curFrame++;
-		}
+		// }
 		break;
 	}
 	case ALFRED_COMB: {
@@ -628,7 +640,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredIdle[_alfredState.direction], _alfredState.x, _alfredState.y - kAlfredFrameHeight, 51, 102, 255);
 		} else {
 			drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCombFrames[_alfredState.direction][_alfredState.curFrame], _alfredState.x, _alfredState.y - kAlfredFrameHeight, 51, 102, 255);
-			if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0)
+			// if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0)
 				_alfredState.curFrame++;
 		}
 		break;
@@ -638,9 +650,9 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			_alfredState.setState(ALFRED_IDLE);
 		} else {
 			drawAlfred(_res->alfredInteractFrames[_alfredState.direction][_alfredState.curFrame]);
-			if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0) {
+			// if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0) {
 				_alfredState.curFrame++;
-			}
+			// }
 		}
 		break;
 	}
@@ -669,9 +681,9 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 							   _res->_curSpecialAnim.w,
 							   _res->_curSpecialAnim.h,
 							   255);
-			if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0) {
+			// if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0) {
 				_res->_specialAnimCurFrame++;
-			}
+			// }
 			delete[] frame;
 		}
 	}
@@ -842,8 +854,9 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 	byte *frame = new byte[frameSize];
 	drawSpriteToBuffer(_compositeBuffer, 640, animData.animData[curFrame], x, y, w, h, 255);
 
-	// if (animData.elpapsedFrames == animData.speed) {
-	if (_chrono->getFrameCount() % animData.speed == 0) {
+	// Original in the game: increment FIRST, then check (not check-then-increment)
+	animData.elpapsedFrames++;
+	if (animData.elpapsedFrames >= animData.speed) {
 		animData.elpapsedFrames = 0;
 		if (animData.curFrame < animData.nframes - 1) {
 			animData.curFrame++;
@@ -861,8 +874,6 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 				}
 			}
 		}
-	} else {
-		animData.elpapsedFrames++;
 	}
 }
 
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 265396014b4..8143c9938fb 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -222,7 +222,12 @@ PaletteAnim *RoomManager::getPaletteAnimForRoom(int roomNumber) {
 	anim->paletteMode = exeFile.readByte();
 	exeFile.read(anim->data, 10);
 	if (anim->paletteMode == 1) {
-		for (int i = 2; i < 10; i++) {
+		// FADE mode: shift RGB values to convert from 6-bit VGA to 8-bit
+		// data[0-2] = current R,G,B
+		// data[3-5] = min R,G,B
+		// data[6-8] = max R,G,B
+		// data[9] = flags (R/G/B increments + direction) - NOT shifted
+		for (int i = 0; i < 9; i++) {
 			anim->data[i] = anim->data[i] << 2;
 		}
 	}
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 93d477a02d7..09af2584325 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -71,7 +71,7 @@ const int kAlfredFrameHeight = 102;
 const int kChoiceHeight = 16; // Height of each choice line in pixels
 
 const int kTalkAnimationSpeed = 2;   // Frames per update
-const int kAlfredAnimationSpeed = 2; // Frames per update
+
 const int kAlfredIdleAnimationFrameCount = 300;
 
 // Direction flags (bit-packed)
@@ -126,7 +126,8 @@ struct AlfredState {
 	AlfredAnimState animState = ALFRED_IDLE;
 	AlfredDirection direction = ALFRED_DOWN;
 	int curFrame = 0;
-	uint16 movementSpeed = 6; // pixels per frame
+	uint16 movementSpeedX = 6; // pixels per frame
+	uint16 movementSpeedY = 5; // pixels per frame
 	uint16 x = 319;
 	uint16 y = 302;
 	uint16 scaledX = 0;
@@ -360,7 +361,8 @@ struct PaletteAnim {
 	byte startIndex;
 	byte paletteMode;
 	byte data[10]; // Based on mode its a rotate or fade
-	byte curFrameCount = 0;
+	byte curFrame = 0;
+	byte tickCount = 0;
 };
 
 /**


Commit: b4f94c59fc715c9d644ec764f657d5582de8954c
    https://github.com/scummvm/scummvm/commit/b4f94c59fc715c9d644ec764f657d5582de8954c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:46+02:00

Commit Message:
PELROCK: Scaling improvements

Changed paths:
    engines/pelrock/pathfinding.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/pathfinding.cpp b/engines/pelrock/pathfinding.cpp
index efe1a28e600..a89d9cee7ad 100644
--- a/engines/pelrock/pathfinding.cpp
+++ b/engines/pelrock/pathfinding.cpp
@@ -124,34 +124,12 @@ Common::Point calculateWalkTarget(Common::Array<WalkBox> &walkboxes,
 								  bool mouseHoverState,
 								  HotSpot *hotspot) {
 
-	// // Step 1: Determine actual source point
-	// if (mouseHoverState == 1) {
-	//     // Hovering over sprite - check if it has action flags or is animated
-	//     Sprite *sprite = getSprite(hotspotSpriteIndex);
-	//     if (sprite->actionFlags != 0 || sprite->frameCount != 1) {
-	//         sourceX = sprite->x + sprite->width / 2;
-	//         sourceY = sprite->y + sprite->height;
-	//     }
-	// }
-	// else if (mouseHoverState == 2) {
-	//     // Hovering over hotspot - use hotspot center-bottom
-	//     Hotspot *hotspot = getHotspot(hotspotSpriteIndex);
-	//     sourceX = hotspot->x + hotspot->width / 2;
-	//     sourceY = hotspot->y + hotspot->height;
-	// }
-
-	// if (mouseHoverState == 1) {
-		// Hovering over hotspot - use hotspot center-bottom
-	// if(hotspot != nullptr) {
-	// 	sourceX = hotspot->x + hotspot->w / 2;
-	// 	sourceY = hotspot->y + hotspot->h;
-	// }
-
-	// }
-
-	// else: use sourceX, sourceY as passed (mouse position)
-
-	// Step 2: Find nearest walkbox
+	if(hotspot != nullptr) {
+		sourceX = hotspot->x + hotspot->w / 2;
+		sourceY = hotspot->y + hotspot->h;
+	}
+
+	// Find nearest walkbox
 	uint32 minDistance = 0xFFFF;
 	int bestXDistance = 0;
 	int bestYDistance = 0;
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 4cb895350c3..18b3e968c36 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -629,19 +629,17 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			_alfredState.curFrame = 0;
 		}
 		drawAlfred(_res->alfredTalkFrames[_alfredState.direction][_alfredState.curFrame]);
-		// if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0) {
-			_alfredState.curFrame++;
+		_alfredState.curFrame++;
 		// }
 		break;
 	}
 	case ALFRED_COMB: {
 		if (_alfredState.curFrame >= 11) {
 			_alfredState.setState(ALFRED_IDLE);
-			drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredIdle[_alfredState.direction], _alfredState.x, _alfredState.y - kAlfredFrameHeight, 51, 102, 255);
+			drawAlfred(_res->alfredIdle[_alfredState.direction]);
 		} else {
-			drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCombFrames[_alfredState.direction][_alfredState.curFrame], _alfredState.x, _alfredState.y - kAlfredFrameHeight, 51, 102, 255);
-			// if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0)
-				_alfredState.curFrame++;
+			drawAlfred(_res->alfredCombFrames[_alfredState.direction][_alfredState.curFrame]);
+			_alfredState.curFrame++;
 		}
 		break;
 	}
@@ -650,9 +648,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			_alfredState.setState(ALFRED_IDLE);
 		} else {
 			drawAlfred(_res->alfredInteractFrames[_alfredState.direction][_alfredState.curFrame]);
-			// if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0) {
-				_alfredState.curFrame++;
-			// }
+			_alfredState.curFrame++;
 		}
 		break;
 	}
@@ -673,17 +669,16 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 							   _res->_specialAnimCurFrame,
 							   _res->_curSpecialAnim.w,
 							   _res->_curSpecialAnim.h);
-			drawSpriteToBuffer(_compositeBuffer,
-							   640,
-							   frame,
-							   _alfredState.x,
-							   _alfredState.y - _res->_curSpecialAnim.h,
-							   _res->_curSpecialAnim.w,
-							   _res->_curSpecialAnim.h,
-							   255);
-			// if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0) {
-				_res->_specialAnimCurFrame++;
-			// }
+			drawAlfred(frame);
+			// drawSpriteToBuffer(_compositeBuffer,
+			// 				   640,
+			// 				   frame,
+			// 				   _alfredState.x,
+			// 				   _alfredState.y - _res->_curSpecialAnim.h,
+			// 				   _res->_curSpecialAnim.w,
+			// 				   _res->_curSpecialAnim.h,
+			// 				   255);
+			_res->_specialAnimCurFrame++;
 			delete[] frame;
 		}
 	}
@@ -698,17 +693,19 @@ void PelrockEngine::drawAlfred(byte *buf) {
 
 	ScaleCalculation scale = calculateScaling(_alfredState.y, _room->_scaleParams);
 
-	int finalHeight = kAlfredFrameHeight - scale.scaleDown + scale.scaleUp;
+	// Use the pre-calculated scaled dimensions from calculateScaling
+	int finalHeight = scale.scaledHeight;
+	int finalWidth = scale.scaledWidth;
+
 	if (finalHeight <= 0) {
 		finalHeight = 1;
 	}
-	float scaleFactor = static_cast<float>(finalHeight) / static_cast<float>(kAlfredFrameHeight);
-	int finalWidth = static_cast<int>(kAlfredFrameWidth * scaleFactor);
 	if (finalWidth <= 0) {
 		finalWidth = 1;
 	}
+
 	int scaleIndex = finalHeight - 1;
-	if (scaleIndex >= _heightScalingTable.size()) {
+	if (scaleIndex >= (int)_heightScalingTable.size()) {
 		scaleIndex = _heightScalingTable.size() - 1;
 	}
 	if (scaleIndex < 0) {
@@ -981,45 +978,52 @@ void PelrockEngine::calculateScalingMasks() {
 }
 
 ScaleCalculation PelrockEngine::calculateScaling(int yPos, ScalingParams scalingParams) {
-	int scaleDown = 0;
-	int scaleUp = 0;
+	// scaleY = amount to subtract from height (94 max for 0xFF mode)
+	// scaleX = amount to subtract from width (47 max for 0xFF mode, = scaleY/2)
+	int scaleY = 0;
+	int scaleX = 0;
 	if (scalingParams.scaleMode == 0xFF) {
-		scaleDown = 0x5e;
-		scaleUp = 0x2f;
+		// Maximum scaling - character is very small (used for bird's eye view maps)
+		scaleY = 0x5e; // 94
+		scaleX = 0x2f; // 47
 	} else if (scalingParams.scaleMode == 0xFE) {
-		scaleDown = 0;
-		scaleUp = 0;
+		// No scaling - full size character
+		scaleY = 0;
+		scaleX = 0;
 	} else if (scalingParams.scaleMode == 0) {
+		// Dynamic scaling based on Y position
 		if (scalingParams.yThreshold < yPos) {
-			scaleDown = 0;
-			scaleUp = 0;
+			// Below threshold - no scaling
+			scaleY = 0;
+			scaleX = 0;
 		} else {
 			if (scalingParams.scaleDivisor != 0) {
-				scaleDown = (scalingParams.yThreshold - yPos) / scalingParams.scaleDivisor;
-				scaleUp = scaleDown / 2;
+				scaleY = (scalingParams.yThreshold - yPos) / scalingParams.scaleDivisor;
+				scaleX = scaleY / 2;
 			} else {
-				scaleDown = 0;
-				scaleUp = 0;
+				scaleY = 0;
+				scaleX = 0;
 			}
 		}
 	} else {
-		scaleDown = 0;
-		scaleUp = 0;
+		scaleY = 0;
+		scaleX = 0;
 	}
 
-	int finalHeight = kAlfredFrameHeight - scaleDown + scaleUp;
+	// Original game formula: actual dimensions = base - scale amount
+	int finalHeight = kAlfredFrameHeight - scaleY;
 	if (finalHeight < 1)
 		finalHeight = 1;
 
-	int finalWidth = kAlfredFrameWidth * (finalHeight / kAlfredFrameHeight);
+	int finalWidth = kAlfredFrameWidth - scaleX;
 	if (finalWidth < 1)
 		finalWidth = 1;
 
 	ScaleCalculation scaleCalc;
 	scaleCalc.scaledHeight = finalHeight;
 	scaleCalc.scaledWidth = finalWidth;
-	scaleCalc.scaleDown = scaleDown;
-	scaleCalc.scaleUp = scaleUp;
+	scaleCalc.scaleY = scaleY;
+	scaleCalc.scaleX = scaleX;
 	return scaleCalc;
 }
 
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 31bfa59f5ad..50ec43216a4 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -94,7 +94,7 @@ void SoundManager::playSound(SonidoFile sound, int volume) {
 		// Create raw audio stream (8-bit unsigned mono is common for old games)
 		stream = Audio::makeRawStream(pcmData, pcmSize, sampleRate, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
 	} else {
-		debug("Unknown sound format");
+		debug("Unknown sound format at offset %d, with size %d", sound.offset, sound.size);
 		delete[] data;
 		return;
 	}
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 09af2584325..75b32123f00 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -282,8 +282,8 @@ struct ScalingParams {
 struct ScaleCalculation {
 	int scaledWidth;
 	int scaledHeight;
-	int scaleUp;
-	int scaleDown;
+	int scaleX;  // Amount to subtract from width (was scaleUp)
+	int scaleY;  // Amount to subtract from height (was scaleDown)
 };
 
 enum GameState {


Commit: 8e6c8b01a45051e42992509d81d6495784b14cda
    https://github.com/scummvm/scummvm/commit/8e6c8b01a45051e42992509d81d6495784b14cda
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:47+02:00

Commit Message:
PELROCK: Additional debugging capabilities

Changed paths:
    engines/pelrock/console.cpp
    engines/pelrock/console.h
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/types.h
    engines/pelrock/util.cpp


diff --git a/engines/pelrock/console.cpp b/engines/pelrock/console.cpp
index cfa96283d8b..04cf59c1dea 100644
--- a/engines/pelrock/console.cpp
+++ b/engines/pelrock/console.cpp
@@ -28,12 +28,12 @@ namespace Pelrock {
 
 PelrockConsole::PelrockConsole(PelrockEngine *engine) : GUI::Debugger(), _engine(engine) {
 	registerCmd("setScreen", WRAP_METHOD(PelrockConsole, cmdLoadRoom));
+	registerCmd("give", WRAP_METHOD(PelrockConsole, cmdGiveItems));
 }
 
 PelrockConsole::~PelrockConsole() {
 }
 
-
 bool PelrockConsole::cmdLoadRoom(int argc, const char **argv) {
 	if (argc < 2) {
 		debugPrintf("Usage: setScreen <roomNumber>");
@@ -42,9 +42,24 @@ bool PelrockConsole::cmdLoadRoom(int argc, const char **argv) {
 
 	int roomNumber = atoi(argv[1]);
 	g_engine->setScreen(roomNumber, ALFRED_DOWN);
+	const WalkBox w = g_engine->_room->_currentRoomWalkboxes[0];
+	g_engine->_alfredState.x = w.x;
+	g_engine->_alfredState.y = w.y;
 	debugPrintf("Loaded room %d", roomNumber);
 	return true;
 }
 
+bool PelrockConsole::cmdGiveItems(int argc, const char **argv) {
+	if (argc < 2) {
+		debugPrintf("Usage: giveItems <itemId> [itemId] ...");
+		return true;
+	}
+	for (int i = 1; i < argc; i++) {
+		int itemId = atoi(argv[i]);
+		g_engine->_state->addInventoryItem(itemId);
+		debugPrintf("Gave item %d\n", itemId);
+	}
+	return true;
+}
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/console.h b/engines/pelrock/console.h
index 29bb09747c0..41b007da25c 100644
--- a/engines/pelrock/console.h
+++ b/engines/pelrock/console.h
@@ -32,6 +32,7 @@ class PelrockConsole : public GUI::Debugger {
 private:
 	PelrockEngine *_engine;
 	bool cmdLoadRoom(int argc, const char **argv);
+	bool cmdGiveItems(int argc, const char **argv);
 	bool cmdTest(int argc, const char **argv);
 
 public:
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index ad74822092b..488255a310d 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -160,13 +160,7 @@ void MenuManager::menuLoop() {
 	if (showButtons)
 		drawButtons();
 
-	for (int i = 0; i < 4; i++) {
-		int itemIndex = _curInventoryPage * 4 + i;
-		if (g_engine->_state->inventoryItems.size() <= itemIndex)
-			continue;
-		InventoryObject item = g_engine->_res->getIconForObject(g_engine->_state->inventoryItems[itemIndex]);
-		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 140 + (82 * i), 115 - (8 * i), 60, 60, 1);
-	}
+	drawInventoryIcons();
 
 	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
 	for (int i = 0; _menuText.size() > i; i++) {
@@ -178,6 +172,21 @@ void MenuManager::menuLoop() {
 	_screen->update();
 }
 
+void MenuManager::drawInventoryIcons() {
+	bool debugIcons = true;
+	for (int i = 0; i < 4; i++) {
+		int itemIndex = _curInventoryPage * 4 + i;
+		if (g_engine->_state->inventoryItems.size() <= itemIndex)
+			continue;
+		InventoryObject item = g_engine->_res->getIconForObject(g_engine->_state->inventoryItems[itemIndex]);
+		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 140 + (82 * i), 115 - (8 * i), 60, 60, 1);
+		if (debugIcons) {
+			drawRect(_compositeBuffer, 140 + (82 * i), 115 - (8 * i), 60, 60, 13);
+			drawText(_compositeBuffer, g_engine->_smallFont, Common::String::format("ID %d", g_engine->_state->inventoryItems[itemIndex]), 140 + (82 * i) + 2, 115 - (8 * i) + 2, 640, 13);
+		}
+	}
+}
+
 void MenuManager::loadMenu() {
 
 	bool alternateMenu = false;
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 9d1fae248dd..3de2bc10f92 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -51,6 +51,7 @@ public:
 	MenuManager(Graphics::Screen *screen, PelrockEventManager *events, ResourceManager *res);
 	~MenuManager();
 	void menuLoop();
+	void drawInventoryIcons();
 	void loadMenu();
 	byte _mainMenuPalette[768] = {0};
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 18b3e968c36..aee08282f69 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -383,16 +383,35 @@ void PelrockEngine::updatePaletteAnimations() {
 }
 
 void PelrockEngine::paintDebugLayer() {
-	for (int i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
-		WalkBox box = _room->_currentRoomWalkboxes[i];
-		drawRect(_screen, box.x, box.y, box.w, box.h, 150 + i);
-		_smallFont->drawString(_screen, Common::String::format("%d", i), box.x + 2, box.y + 2, 640, 14);
+	bool showWalkboxes = false;
+
+	if (showWalkboxes) {
+		for (int i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
+			WalkBox box = _room->_currentRoomWalkboxes[i];
+			drawRect(_screen, box.x, box.y, box.w, box.h, 150 + i);
+			_smallFont->drawString(_screen, Common::String::format("%d", i), box.x + 2, box.y + 2, 640, 14);
+		}
 	}
 
-	for (int i = 0; i < _room->_currentRoomExits.size(); i++) {
-		Exit exit = _room->_currentRoomExits[i];
-		drawRect(_screen, exit.x, exit.y, exit.w, exit.h, 200 + i);
-		_smallFont->drawString(_screen, Common::String::format("Exit %d -> Room %d", i, exit.targetRoom), exit.x + 2, exit.y + 2, 640, 14);
+	bool showHotspots = true;
+	if (showHotspots) {
+		for (int i = 0; i < _room->_currentRoomHotspots.size(); i++) {
+			HotSpot hotspot = _room->_currentRoomHotspots[i];
+			if (!hotspot.isEnabled)
+				continue;
+			drawRect(_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 180 + i);
+			_smallFont->drawString(_screen, Common::String::format("HS %d", i), hotspot.x + 2, hotspot.y + 2, 640, 14);
+			_smallFont->drawString(_screen, Common::String::format("x=%d", hotspot.extra), hotspot.x + 2, hotspot.y + 2 + 14, 640, 14);
+		}
+	}
+
+	bool showExits = true;
+	if (showExits) {
+		for (int i = 0; i < _room->_currentRoomExits.size(); i++) {
+			Exit exit = _room->_currentRoomExits[i];
+			drawRect(_screen, exit.x, exit.y, exit.w, exit.h, 200 + i);
+			_smallFont->drawString(_screen, Common::String::format("Exit %d -> Room %d", i, exit.targetRoom), exit.x + 2, exit.y + 2, 640, 14);
+		}
 	}
 
 	drawPos(_screen, _alfredState.x, _alfredState.y, 13);
@@ -670,14 +689,6 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 							   _res->_curSpecialAnim.w,
 							   _res->_curSpecialAnim.h);
 			drawAlfred(frame);
-			// drawSpriteToBuffer(_compositeBuffer,
-			// 				   640,
-			// 				   frame,
-			// 				   _alfredState.x,
-			// 				   _alfredState.y - _res->_curSpecialAnim.h,
-			// 				   _res->_curSpecialAnim.w,
-			// 				   _res->_curSpecialAnim.h,
-			// 				   255);
 			_res->_specialAnimCurFrame++;
 			delete[] frame;
 		}
@@ -689,6 +700,9 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 	}
 }
 
+/**
+ * Scales and shades alfred sprite and draws it to the composite buffer
+ */
 void PelrockEngine::drawAlfred(byte *buf) {
 
 	ScaleCalculation scale = calculateScaling(_alfredState.y, _room->_scaleParams);
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index d951cca7771..eda93599d47 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -378,7 +378,7 @@ Pelrock::Sticker ResourceManager::getSticker(int stickerIndex) {
 InventoryObject ResourceManager::getIconForObject(byte objectIndex) {
 	byte iconIndex = 0;
 	if (objectIndex < 59) {
-		if (11 < objectIndex < 59) {
+		if (objectIndex >= 11 && objectIndex < 59) {
 			iconIndex = ((objectIndex - 11) & 3) + 11; // Books cycle through icons 11-14
 		} else {
 			iconIndex = objectIndex; // Direct mapping for IDs 0-11
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 8143c9938fb..24969dc6711 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -159,7 +159,7 @@ void RoomManager::changeHotSpot(HotSpot hotspot) {
 }
 
 void RoomManager::disableSprite(int roomNumber, int spriteIndex, bool persist) {
-	if(roomNumber == _currentRoomNumber) {
+	if (roomNumber == _currentRoomNumber) {
 		_currentRoomAnims[spriteIndex].zOrder = 255;
 	}
 	g_engine->_state->disabledSprites[roomNumber].push_back(spriteIndex);
@@ -526,7 +526,7 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 		sprite.spriteType = data[animOffset + 33];
 		sprite.actionFlags = data[animOffset + 34];
 		sprite.isHotspotDisabled = data[animOffset + 38];
-		for(int i = 0; i < disabledSprites.size(); i++) {
+		for (int i = 0; i < disabledSprites.size(); i++) {
 			if (disabledSprites[i] == sprite.index) {
 				sprite.zOrder = 255;
 				sprite.isHotspotDisabled = 1;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 75b32123f00..4970659abec 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -70,7 +70,7 @@ const int kAlfredFrameHeight = 102;
 
 const int kChoiceHeight = 16; // Height of each choice line in pixels
 
-const int kTalkAnimationSpeed = 2;   // Frames per update
+const int kTalkAnimationSpeed = 2; // Frames per update
 
 const int kAlfredIdleAnimationFrameCount = 300;
 
@@ -282,8 +282,8 @@ struct ScalingParams {
 struct ScaleCalculation {
 	int scaledWidth;
 	int scaledHeight;
-	int scaleX;  // Amount to subtract from width (was scaleUp)
-	int scaleY;  // Amount to subtract from height (was scaleDown)
+	int scaleX; // Amount to subtract from width (was scaleUp)
+	int scaleY; // Amount to subtract from height (was scaleDown)
 };
 
 enum GameState {
@@ -480,6 +480,7 @@ struct GameStateData {
 	void addInventoryItem(int id) {
 		inventoryItems.push_back(id);
 	}
+
 	void removeInventoyItem(int id) {
 		for (int i = 0; i < inventoryItems.size(); i++) {
 			if (inventoryItems[i] == id) {
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 05f9d9e231d..e4b8c5d370f 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -323,7 +323,6 @@ byte decodeChar(byte b) {
 	}
 }
 
-
 void changeGameSpeed(Common::Event e) {
 	if (e.type == Common::EVENT_KEYDOWN) {
 		if (e.kbd.hasFlags(Common::KBD_CTRL)) {


Commit: 37add58b128ca8e5497e89dd9ed90bd41626973c
    https://github.com/scummvm/scummvm/commit/37add58b128ca8e5497e89dd9ed90bd41626973c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:47+02:00

Commit Message:
PELROCK: Implement all palette animations

Changed paths:
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 24969dc6711..f858fac530c 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -211,6 +211,39 @@ PaletteAnim *RoomManager::getPaletteAnimForRoom(int roomNumber) {
 	case 2:
 		offset = 0x0004B860;
 		break;
+	case 9:
+		offset = 0x0004B874;
+		break;
+	case 17:
+		offset = 0x0004B86C;
+		break;
+	case 18:
+		offset = 0x0004B870;
+		break;
+	case 19:
+		offset = 0x0004B878;
+		break;
+	case 21:
+		offset = 0x0004B884;
+		break;
+	case 25:
+		offset = 0x0004B890;
+		break;
+	case 32:
+		offset = 0x0004B898;
+		break;
+	case 33:
+		offset = 0x0004B89C;
+		break;
+	case 38:
+		offset = 0x0004B894;
+		break;
+	case 39:
+		offset = 0x0004B888;
+		break;
+	case 46:
+		offset = 0x0004B8A0;
+		break;
 	default:
 		exeFile.close();
 		return nullptr;


Commit: 7f740aff3cdc355075a7915e9bafbee12a57312a
    https://github.com/scummvm/scummvm/commit/7f740aff3cdc355075a7915e9bafbee12a57312a
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:47+02:00

Commit Message:
PELROCK: Better sound management

Changed paths:
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h


diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 50ec43216a4..530a7c934c6 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -237,27 +237,23 @@ void SoundManager::loadSoundIndex() {
 	sonidosFile.close();
 }
 
-int RANDOM_THRESHOLD = 0x4000;
+static const uint kAmbientCounterMask = 0x1F;  // Trigger when (counter & mask) == mask
 
-int SoundManager::tick(uint32 frameCount) {
-
-	uint16 rand1 = _rng.nextRandom();
-	// uint32 random = g_engine->getRandomNumber(1);
-	if (rand1 <= RANDOM_THRESHOLD) {
-		// debug("No SFX this tick due to 50% random");
+int SoundManager::tickAmbientSound(uint32 frameCount) {
+	// Counter gate: only trigger every 32 frames when (counter & 0x1F) == 0x1F
+	if ((frameCount & kAmbientCounterMask) != kAmbientCounterMask) {
 		return -1;
 	}
 
-	if ((frameCount & COUNTER_MASK) != COUNTER_MASK) {
-		// debug("No SFX this tick due to counter mask (counter = %d)", soundFrameCounter);
+	// 50% probability gate using ScummVM's random source
+	if (g_engine->getRandomNumber(1) == 0) {
 		return -1;
 	}
 
-	uint16 rand2 = _rng.nextRandom();
-	int slot = rand2 & 3;
-	// debug("Slot = %d (rand2 = %u)", slot, rand2);
-	// uint32 slot = g_engine->getRandomNumber(4);
-	return slot + 1;
+	// Pick random ambient slot 0-3 (corresponds to room sound indices 4-7)
+	int ambientSlotOffset = g_engine->getRandomNumber(3);
+
+	return ambientSlotOffset;  // Caller adds 4 to get room sound index
 }
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index d2733e83e7a..45d882fa1a3 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -150,38 +150,9 @@ struct SoundData {
 	uint32 size;
 };
 
-static const uint COUNTER_MASK = 0x1F;
 
 const int kMaxChannels = 15;
-
-class GameRNG {
-
-private:
-	uint32_t _state;
-
-public:
-	// LCG constants (from JUEGO.EXE @ 0x0002b12f)
-	static constexpr uint32_t MULTIPLIER = 0x41C64E6D; // 1103515245
-	static constexpr uint32_t INCREMENT = 0x3039;      // 12345
-
-	GameRNG(uint32_t seed = 0) {
-		_state = seed & 0xFFFFFFFF;
-	}
-
-	// Generate next random number (0-32767)
-	uint16_t nextRandom() {
-		_state = (_state * MULTIPLIER + INCREMENT) & 0xFFFFFFFF;
-		return static_cast<uint16_t>((_state >> 16) & 0x7FFF);
-	}
-
-	uint32_t getState() const {
-		return _state;
-	}
-
-	void setState(uint32_t state) {
-		_state = state & 0xFFFFFFFF;
-	}
-};
+const int kAmbientSoundSlotBase = 4;  // Room sound indices 4-7 are ambient sounds
 
 class SoundManager {
 public:
@@ -202,7 +173,13 @@ public:
 	}
 	void loadSoundIndex();
 
-	int tick(uint32 frameCount);
+	/**
+	 * Check if ambient sound should play this frame.
+	 * @param frameCount Current game frame counter
+	 * @return Ambient slot offset (0-3) to play, or -1 if no sound this frame
+	 *         Add kAmbientSoundSlotBase (4) to get room sound index
+	 */
+	int tickAmbientSound(uint32 frameCount);
 
 private:
 	void playSound(SonidoFile sound, int volume = 255);
@@ -219,7 +196,6 @@ private:
 	Audio::SoundHandle _musicHandle;
 	Audio::SoundHandle _sfxHandles[kMaxChannels];
 	Common::HashMap<Common::String, SonidoFile> _soundMap;
-	GameRNG _rng = GameRNG(0);
 };
 
 } // End of namespace Pelrock


Commit: c8c2995f9efd2aaa19fbe5619d5534106936ff90
    https://github.com/scummvm/scummvm/commit/c8c2995f9efd2aaa19fbe5619d5534106936ff90
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:48+02:00

Commit Message:
PELROCK: Add actions for room 3

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/console.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/events.cpp
    engines/pelrock/events.h
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/saveload.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index cf0dc9853a4..aeb70e48c20 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -57,6 +57,11 @@ const ActionEntry actionTable[] = {
 	{375, OPEN, &PelrockEngine::openKitchenDrawer},
 	{374, OPEN, &PelrockEngine::openKitchenDoorFromInside},
 
+	// Room 3
+	{290, OPEN, &PelrockEngine::openShopDoor},
+	{290, CLOSE, &PelrockEngine::closeShopDoor},
+	{32, OPEN, &PelrockEngine::openLamppost},
+
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
 	{WILDCARD, TALK, &PelrockEngine::noOpAction},   // Generic talk action
@@ -73,6 +78,7 @@ const ActionEntry actionTable[] = {
 const CombinationEntry combinationTable[] = {
 	{2, 281, &PelrockEngine::useCardWithATM},           // Use ATM Card with ATM
 	{62, 117, &PelrockEngine::useSpicySauceWithBurger}, // Use Spicy Sauce with Burger
+	{4, 294, &PelrockEngine::useBrickWithWindow},       // Use Brick with Window (Room 3)
 	// End marker
 	{WILDCARD, WILDCARD, nullptr}};
 
@@ -131,7 +137,7 @@ void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
 			addInventoryItem(5); // 1000 pesetas bill
 			_dialog->say(_res->_ingameTexts[TEAPETECE_BUENRATO]);
 		} else {
-			_dialog->say(_res->_ingameTexts[NOMONEY_LEFT]);
+			_dialog->say(_res->_ingameTexts[NOTENGOMASDINERO]);
 		}
 	}
 }
@@ -189,9 +195,90 @@ void PelrockEngine::useSpicySauceWithBurger(int inventoryObject, HotSpot *hotspo
 	_dialog->say(_res->_ingameTexts[VAESTAR_POCOFUERTE]);
 }
 
+void PelrockEngine::openShopDoor(HotSpot *hotspot) {
+	if (!_state->flagIsSet(FLAG_TIENDA_ABIERTA)) {
+		_dialog->say(_res->_ingameTexts[TIENDA_CERRADA]);
+		return;
+	} else {
+		openDoor(hotspot, 0, 13, MASCULINE, false);
+	}
+}
+
+void PelrockEngine::closeShopDoor(HotSpot *hotspot) {
+	closeDoor(hotspot, 0, 13, MASCULINE, false);
+}
+
+void PelrockEngine::openLamppost(HotSpot *hotspot) {
+	debug("Opening lamppost");
+	_room->addSticker(14);
+}
+
+void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
+	debug("Using brick with window - sticker 11 will be added");
+
+	// Check if window is already broken
+	if (_room->hasSticker(11)) {
+		// Window already broken, say something generic
+		_alfredState.direction = ALFRED_UP;
+		_dialog->say(_res->_ingameTexts[YA_ABIERTO_M]); // "It's already open/broken"
+		return;
+	}
+
+	// TODO: Play Alfred's throwing animation
+	// This would require adding a new special animation entry
+	// _res->loadAlfredSpecialAnim(BRICK_THROW_ANIM);
+	// _alfredState.animState = ALFRED_SPECIAL_ANIM;
+	// waitForSpecialAnimation();
+
+	// TODO: Animate sprite 8 (brick projectile) moving to window
+	Sprite *brickSprite = _room->findSpriteByIndex(7);
+	HotSpot *windowHotspot = _room->findHotspotByExtra(294);
+	brickSprite->x = _alfredState.x - brickSprite->w / 2;
+	brickSprite->y = _alfredState.y - kAlfredFrameHeight;
+	brickSprite->zOrder = 20; // Make it visible
+	int target = windowHotspot->y + windowHotspot->h / 2;
+	while (!shouldQuit()) {
+		_events->pollEvent();
+		renderScene(OVERLAY_NONE);
+		if (_chrono->_gameTick) {
+			_room->findSpriteByIndex(7)->y -= 40;
+			if (_room->findSpriteByIndex(7)->y < target) {
+				_room->findSpriteByIndex(7)->zOrder = -1;
+				break;
+			}
+		}
+		_screen->update();
+		g_system->delayMillis(10);
+	}
+	// This would involve loading and animating the room sprite
+
+	// Add the broken window sticker
+	_room->addSticker(11);
+	_sound->playSound(_room->_roomSfx[2]); // Play glass breaking sound
+
+	// Remove brick from inventory
+	_state->removeInventoryItem(4);
+
+	int16 x = 639; // put at the very edge of the screen
+	int16 y = windowHotspot->y;
+	// Play the NPC dialog sequence
+	int16 dialog1y = y + 22;
+	int16 dialog2y = dialog1y + 10 + _largeFont->getFontHeight();
+	_dialog->say(_res->_ingameTexts[QUEHASIDOESO], x, dialog1y);
+	_dialog->say(_res->_ingameTexts[QUIENANDAAHI], x, dialog2y);
+	_dialog->say(_res->_ingameTexts[YOMEVOY]);
+
+	_state->setFlag(FLAG_TIENDA_ABIERTA, true);
+	_room->onlyPersistSticker(_room->_currentRoomNumber, 9);
+	_room->onlyPersistSticker(_room->_currentRoomNumber, 10);
+	_room->disableHotspot(_room->findHotspotByExtra(295)); // Disable storefront hotspot
+	_room->disableHotspot(_room->findHotspotByExtra(294)); // Disable window hotspot
+	walkTo(639, _alfredState.y);
+}
+
 void PelrockEngine::openDoor(HotSpot *hotspot, int exitIndex, int sticker, bool masculine, bool stayClosed) {
 	if (_room->hasSticker(sticker)) {
-		int text = masculine == MASCULINE ? YA_ABIERTO_M : ALREADY_OPENED_F;
+		int text = masculine == MASCULINE ? YA_ABIERTO_M : YA_ABIERTA_F;
 		_dialog->say(_res->_ingameTexts[text]);
 		return;
 	}
@@ -201,7 +288,7 @@ void PelrockEngine::openDoor(HotSpot *hotspot, int exitIndex, int sticker, bool
 
 void PelrockEngine::closeDoor(HotSpot *hotspot, int exitIndex, int sticker, bool masculine, bool stayOpen) {
 	if (!_room->hasSticker(sticker)) {
-		int text = masculine == MASCULINE ? ALREADY_CLOSED_M : ALREADY_CLOSED_F;
+		int text = masculine == MASCULINE ? YA_CERRADO_M : YA_CERRADA_F;
 		_dialog->say(_res->_ingameTexts[text]);
 		return;
 	}
@@ -241,7 +328,7 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	case 257:
 		_sound->playMusicTrack(25);
 		loadExtraScreenAndPresent(9);
-		_dialog->say(_res->_ingameTexts[SOHOT]);
+		_dialog->say(_res->_ingameTexts[QUEBUENA_ESTA]);
 		_screen->markAllDirty();
 		_screen->update();
 		break;
@@ -267,12 +354,17 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		_res->loadAlfredSpecialAnim(1);
 		_alfredState.animState = ALFRED_SPECIAL_ANIM;
 		waitForSpecialAnimation();
-		debug("After special anim");
+
 		loadExtraScreenAndPresent(3);
 		debug("After extra screen");
 		_dialog->say(_res->_ingameTexts[QUEASCO]);
 		break;
-
+	case 0: // yellow book
+		_res->loadAlfredSpecialAnim(2);
+		_alfredState.animState = ALFRED_SPECIAL_ANIM;
+		waitForSpecialAnimation();
+		_dialog->say(_res->_ingameTexts[CUENTOPARECIDO]);
+		break;
 	default:
 		break;
 	}
diff --git a/engines/pelrock/console.cpp b/engines/pelrock/console.cpp
index 04cf59c1dea..0f1b2f0f967 100644
--- a/engines/pelrock/console.cpp
+++ b/engines/pelrock/console.cpp
@@ -42,9 +42,6 @@ bool PelrockConsole::cmdLoadRoom(int argc, const char **argv) {
 
 	int roomNumber = atoi(argv[1]);
 	g_engine->setScreen(roomNumber, ALFRED_DOWN);
-	const WalkBox w = g_engine->_room->_currentRoomWalkboxes[0];
-	g_engine->_alfredState.x = w.x;
-	g_engine->_alfredState.y = w.y;
 	debugPrintf("Loaded room %d", roomNumber);
 	return true;
 }
@@ -56,7 +53,11 @@ bool PelrockConsole::cmdGiveItems(int argc, const char **argv) {
 	}
 	for (int i = 1; i < argc; i++) {
 		int itemId = atoi(argv[i]);
+		bool markAsSelected = g_engine->_state->inventoryItems.empty();
 		g_engine->_state->addInventoryItem(itemId);
+		if (markAsSelected)
+			g_engine->_state->selectedInventoryItem = itemId;
+
 		debugPrintf("Gave item %d\n", itemId);
 	}
 	return true;
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 1abead0b84a..c5233f6275d 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -49,8 +49,8 @@ uint32 DialogManager::readTextBlock(
 	outText = "";
 
 	// Skip control bytes at start
-	if(data[pos] == CTRL_TEXT_TERMINATOR) {
-		pos+=2;
+	if (data[pos] == CTRL_TEXT_TERMINATOR) {
+		pos += 2;
 	}
 
 	if (pos >= dataSize) {
@@ -74,11 +74,11 @@ uint32 DialogManager::readTextBlock(
 		}
 		// Choice text is always spoken by ALFRED
 		outSpeakerId = ALFRED_COLOR;
-		pos+=2;
+		pos += 2;
 	}
 
 	int lineIndex = data[++pos];
-	pos++; //blank
+	pos++; // blank
 	debug("Reading text block starting at pos %u, line index %d, speaker ID %d", startPos, lineIndex, outSpeakerId);
 	// Read text until control byte
 	while (pos < dataSize) {
@@ -147,12 +147,37 @@ Graphics::Surface DialogManager::getDialogueSurface(Common::Array<Common::String
 
 	return s;
 }
+
+void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>> dialogueLines, byte speakerId) {
+	int16 xBasePos = 0;
+	int16 yBasePos = 0;
+	if (speakerId == ALFRED_COLOR) {
+		if (g_engine->_alfredState.animState != ALFRED_TALKING) {
+			g_engine->_alfredState.setState(ALFRED_TALKING);
+		}
+		if (_curSprite != nullptr) {
+			_curSprite->isTalking = false;
+		}
+		// Offset X position for Alfred to avoid overlapping with his sprite
+		xBasePos = g_engine->_alfredState.x;                      //+ kAlfredFrameWidth / 2 - maxWidth / 2;
+		yBasePos = g_engine->_alfredState.y - kAlfredFrameHeight; // Above sprite, adjust for line
+	} else {
+		g_engine->_alfredState.setState(ALFRED_IDLE);
+		if (_curSprite != nullptr) {
+			_curSprite->isTalking = true;
+			xBasePos = _curSprite->x + _curSprite->w / 2;
+			yBasePos = _curSprite->y; // Above sprite, adjust for line
+		}
+	}
+	displayDialogue(dialogueLines, speakerId, xBasePos, yBasePos); // Default position
+}
+
 /**
  * Display dialogue text and wait for click to advance
  * @param text The text to display
  * @param speakerId The speaker ID which is used as color
  */
-void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>> dialogueLines, byte speakerId) {
+void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>> dialogueLines, byte speakerId, int xBasePos, int yBasePos) {
 	if (dialogueLines.empty()) {
 		return;
 	}
@@ -160,6 +185,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 	// Clear any existing click state
 	_events->_leftMouseClicked = false;
 	int curPage = 0;
+
 	// Render loop - display text and wait for click
 	while (!g_engine->shouldQuit()) {
 		_events->pollEvent();
@@ -175,30 +201,12 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		for (int i = 0; i < textLines.size(); i++) {
 			maxWidth = MAX(maxWidth, g_engine->_largeFont->getStringWidth(textLines[i]));
 		}
-		int xPos = 0;
-		int yPos = 0;
 
-		if (speakerId == ALFRED_COLOR) {
-			if (g_engine->_alfredState.animState != ALFRED_TALKING) {
-				g_engine->_alfredState.setState(ALFRED_TALKING);
-			}
-			if (_curSprite != nullptr) {
-				_curSprite->isTalking = false;
-			}
-			// Offset X position for Alfred to avoid overlapping with his sprite
-			xPos = g_engine->_alfredState.x - maxWidth / 2;                //+ kAlfredFrameWidth / 2 - maxWidth / 2;
-			yPos = g_engine->_alfredState.y - kAlfredFrameHeight - height; // Above sprite, adjust for line
-		} else {
-			g_engine->_alfredState.setState(ALFRED_IDLE);
-			_curSprite->isTalking = true;
-			xPos = _curSprite->x + _curSprite->w / 2 - maxWidth / 2;
-			yPos = _curSprite->y - height; // Above sprite, adjust for line
-		}
+		int xPos = xBasePos - maxWidth / 2;
+		int yPos = yBasePos - height;
 
 		Graphics::Surface s = getDialogueSurface(textLines, speakerId);
 
-
-
 		// Clamp to screen bounds
 		xPos = CLIP(xPos, 0, 640 - maxWidth);
 		yPos = CLIP(yPos, 0, 400 - s.getRect().height());
@@ -356,7 +364,7 @@ void DialogManager::setCurSprite(int index) {
 
 	for (uint i = 0; i < g_engine->_room->_currentRoomAnims.size(); i++) {
 		Sprite *sprite = &g_engine->_room->_currentRoomAnims[i];
-		if (sprite->index == index) {
+		if (sprite->index == index && (sprite->actionFlags & 16)) {
 			_curSprite = sprite;
 			return;
 		}
@@ -381,8 +389,8 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 	// Find the speaker tree for this NPC; they are marked by 0xFE 0xXX where XX is NPC index + 1
 	bool speakerTreeOffsetFound = false;
 	int currentConversationTree = npcIndex + 1;
-	while(position < dataSize && !speakerTreeOffsetFound) {
-		if(conversationData[position] == CTRL_ALT_SPEAKER_ROOT && conversationData[position + 1] == currentConversationTree) {
+	while (position < dataSize && !speakerTreeOffsetFound) {
+		if (conversationData[position] == CTRL_ALT_SPEAKER_ROOT && conversationData[position + 1] == currentConversationTree) {
 			speakerTreeOffsetFound = true;
 			position += 2; // Move past the speaker tree marker and npc index
 		} else {
@@ -391,10 +399,10 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 	}
 	// Right after the speaker conversation tree, we are in branch 0
 	int currentRoot = 0;
-	while(g_engine->_state->getRootDisabledState(g_engine->_room->_currentRoomNumber, currentRoot)) {
+	while (g_engine->_state->getRootDisabledState(g_engine->_room->_currentRoomNumber, currentRoot)) {
 		// This root is disabled, skip to next
-		while(position < dataSize) {
-			if(conversationData[position] == CTRL_END_BRANCH) {
+		while (position < dataSize) {
+			if (conversationData[position] == CTRL_END_BRANCH) {
 				position++; // Move past end branch marker
 				currentRoot++;
 				break;
@@ -453,14 +461,13 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 			break;
 		}
 
-		if(controlByte == CTRL_ACTION_TRIGGER) {
+		if (controlByte == CTRL_ACTION_TRIGGER) {
 			uint16 actionCode = conversationData[position + 1] | (conversationData[position + 2] << 8);
 			debug("Action trigger %d encountered!", actionCode);
 			g_engine->dialogActionTrigger(
 				actionCode,
 				g_engine->_room->_currentRoomNumber,
-				currentRoot
-			);
+				currentRoot);
 			break;
 		}
 
@@ -620,6 +627,23 @@ void DialogManager::say(Common::StringArray texts) {
 	}
 }
 
+void DialogManager::say(Common::StringArray texts, int16 x, int16 y) {
+	if (texts.empty()) {
+		return;
+	}
+	byte speakerId;
+	bool wasProcessed = processColorAndTrim(texts, speakerId);
+
+	if (wasProcessed) {
+		// Create a temporary sprite at the specified position
+		Common::Array<Common::StringArray> textLines = wordWrap(texts);
+		displayDialogue(textLines, speakerId, x, y);
+	} else {
+		sayAlfred(texts);
+	}
+
+}
+
 bool DialogManager::processColorAndTrim(Common::StringArray &lines, byte &speakerId) {
 	int speakerMarker = lines[0][0];
 	speakerId = lines[0][1];
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 66a34b94c8b..b543de68855 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -71,6 +71,7 @@ private:
 
 	// Private helper functions for conversation parsing
 	void displayDialogue(Common::Array<Common::Array<Common::String>> dialogueLines, byte speakerId);
+	void displayDialogue(Common::Array<Common::Array<Common::String>> dialogueLines, byte speakerId, int xBasePos, int yBasePos);
 	void displayDialogue(Common::String text, byte speakerId);
 	uint32 readTextBlock(const byte *data, uint32 dataSize, uint32 startPos, Common::String &outText, byte &outSpeakerId);
 	uint32 parseChoices(const byte *data, uint32 dataSize, uint32 startPos, Common::Array<ChoiceOption> *outChoices);
@@ -87,6 +88,7 @@ public:
 	void startConversation(const byte *conversationData, uint32 dataSize, byte npcIndex, Sprite *alfredAnimSet = nullptr);
 	void sayAlfred(Description description);
 	void say(Common::StringArray texts);
+	void say(Common::StringArray texts, int16 x, int16 y);
 	bool processColorAndTrim(Common::StringArray &lines, byte &speakerId);
 	Graphics::Surface getDialogueSurface(Common::Array<Common::String> dialogueLines, byte speakerId);
 
diff --git a/engines/pelrock/events.cpp b/engines/pelrock/events.cpp
index 63bf81876ef..da4b41a7e87 100644
--- a/engines/pelrock/events.cpp
+++ b/engines/pelrock/events.cpp
@@ -23,6 +23,7 @@
 #include "pelrock/events.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/util.h"
+#include "events.h"
 
 namespace Pelrock {
 
@@ -58,6 +59,7 @@ void PelrockEventManager::pollEvent() {
 		// case Common::EVENT_KEYUP:
 		// 	return;
 		case Common::EVENT_LBUTTONDOWN:
+
 			if (_leftMouseButton == 0) {
 				_clickTime = g_system->getMillis();
 			}
@@ -122,4 +124,5 @@ void PelrockEventManager::waitForKey() {
 		g_system->delayMillis(10);
 	}
 }
+
 } // namespace Pelrock
diff --git a/engines/pelrock/events.h b/engines/pelrock/events.h
index 046e38dc8ec..fe25ba04382 100644
--- a/engines/pelrock/events.h
+++ b/engines/pelrock/events.h
@@ -30,7 +30,6 @@ class PelrockEventManager {
 private:
 	Common::Event _event;
 	uint32 _clickTime = 0;
-
 public:
 	int16 _mouseX = 0;
 	int16 _mouseY = 0;
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 03cb71fcdab..9f14fc77506 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -123,34 +123,34 @@ enum TextIndices {
 	ESTAN_CERRADOS,
 	HOY_NO_DISPONIBLES,
 	YA_ABIERTO_M,
-	ALREADY_CLOSED_M,
-	ALREADY_OPENED_F,
-	ALREADY_CLOSED_F,
-	ICECREAM_CLOSED,
-	IMPOOR,
-	SOHOT,
-	GREENBUTTON_REDBUTTON,
-	ENTERCARD,
-	NOMONEY_LEFT,
-	STUNGAGAIN,
-	WHATWASTHAT,
-	WHOS_THERE,
-	IMOFF,
-	SHOP_CLOSED,
-	SHE_WOULDNT_NOTICE,
-	GOTTA_OPEN_FIRST,
-	LETHISFATHER_PICKTHEM,
-	BRIBEME,
-	VERYGOOD,
-	WHENHEASKS,
-	OKAY,
-	I_NEED_ID,
-	WHAT_DO_I_GET_IN_RETURN,
-	THATSNOTENOUGH,
-	STOP,
-	THATSOBVIOUSLYNOTENOUGH,
-	WHATFOR,
-	NOTSTONE_ICE,
+	YA_CERRADO_M,
+	YA_ABIERTA_F,
+	YA_CERRADA_F,
+	HELADERIA_CERRADA,
+	POBRE_PERO_NO_HE_LLEGADO_A_ESO,
+	QUEBUENA_ESTA,
+	BOTONVERDEPARASACAR_BOTONVERDEPARACANCELAR,
+	PRIMEROMETA_TARJETA,
+	NOTENGOMASDINERO,
+	MEHEVUELTOAPINCHAR,
+	QUEHASIDOESO,
+	QUIENANDAAHI,
+	YOMEVOY,
+	TIENDA_CERRADA,
+	NOSE_ENTERARIA,
+	PRIMERO_ABRIRLO,
+	QUELOSCOJA_SUPADRE,
+	PRETENDEUSTED_SOBORNARME,
+	MUYBIEN_1,
+	CUANDOMELOPIDA,
+	DEACUERDO,
+	NECESITODNI,
+	QUE_RECIBO_ACAMBIO,
+	ESPOCO,
+	ALTO,
+	NIPARAEMPEZAR,
+	PARAQUE,
+	DEPIEDRANO_DEHIELO,
 	HEY_DONTSTART,
 	HOTONE_MOCKING,
 	SHUTUP,
@@ -274,7 +274,7 @@ enum TextIndices {
 	OHPTHA,
 	LASPUERTASDELCIELO,
 	VAYASUENHO,
-	PARAQUE,
+	PARAQUE_2,
 	YESO,
 	UNPOCODESESPERADO,
 	COMBINACIONESMEJORES,
@@ -297,7 +297,7 @@ enum TextIndices {
 	DOSINGREDIENTES,
 	TRESINGREDIENTES,
 	CUATROINGREDIENTES,
-	DEACUERDO,
+	DEACUERDO_2,
 	GAMBERROS,
 	QUIENYO,
 	PINTA_BUENAPERSONA,
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index aee08282f69..bb8f7e68ec1 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -160,7 +160,9 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		setScreen(0, ALFRED_DOWN);
+		// setScreen(0, ALFRED_DOWN);
+		setScreen(3, ALFRED_RIGHT);
+		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
 		// alfredState.y = 374;
@@ -217,10 +219,17 @@ void sortAnimsByZOrder(Common::Array<Sprite> &anims) {
 }
 
 void PelrockEngine::playSoundIfNeeded() {
-
-	int soundIndex = _sound->tick(_chrono->getFrameCount());
-	if (soundIndex >= 0 && soundIndex < _room->_roomSfx.size()) {
-		_sound->playSound(_room->_roomSfx[3 + soundIndex]);
+	// Get ambient slot offset (0-3) or -1 if no sound this frame
+	int ambientSlotOffset = _sound->tickAmbientSound(_chrono->getFrameCount());
+	if (ambientSlotOffset >= 0) {
+		// Convert to room sound index: slots 12-15 = room indices 4-7
+		int roomSoundIndex = kAmbientSoundSlotBase + ambientSlotOffset;
+		if (roomSoundIndex < _room->_roomSfx.size()) {
+			byte soundFileIndex = _room->_roomSfx[roomSoundIndex];
+			if (soundFileIndex != 0) { // 0 = NO_SOUND.SMP (disabled)
+				_sound->playSound(soundFileIndex);
+			}
+		}
 	}
 }
 
@@ -234,6 +243,15 @@ bool PelrockEngine::renderScene(int overlayMode) {
 
 		placeStickers();
 		updateAnimations();
+		// Some stickers need to be placed AFTER sprites, hardcoded in the original
+		if (_room->_currentRoomNumber == 3) {
+			for (int i = 0; i < _state->stickersPerRoom[3].size(); i++) {
+				if (_state->stickersPerRoom[3][i].stickerIndex == 14) {
+					placeSticker(_state->stickersPerRoom[3][i]);
+					break;
+				}
+			}
+		}
 
 		if (overlayMode == OVERLAY_CHOICES) {
 			_dialog->displayChoices(_dialog->_currentChoices, _compositeBuffer);
@@ -253,7 +271,7 @@ bool PelrockEngine::renderScene(int overlayMode) {
 }
 
 void PelrockEngine::executeAction(VerbIcon action, HotSpot *hotspot) {
-
+	debug("Executing action %d on hotspot %d", action, hotspot->extra);
 	if (action == ITEM) {
 		int inventoryObject = _state->selectedInventoryItem;
 		for (const CombinationEntry *entry = combinationTable; entry->handler != nullptr; entry++) {
@@ -389,7 +407,16 @@ void PelrockEngine::paintDebugLayer() {
 		for (int i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
 			WalkBox box = _room->_currentRoomWalkboxes[i];
 			drawRect(_screen, box.x, box.y, box.w, box.h, 150 + i);
-			_smallFont->drawString(_screen, Common::String::format("%d", i), box.x + 2, box.y + 2, 640, 14);
+			// _smallFont->drawString(_screen, Common::String::format("%d", i), box.x + 2, box.y + 2, 640, 14);
+		}
+	}
+
+	bool showSprites = true;
+	if (showSprites) {
+		for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
+			Sprite sprite = _room->_currentRoomAnims[i];
+			drawRect(_screen, sprite.x, sprite.y, sprite.animData->w, sprite.animData->h, 14);
+			_smallFont->drawString(_screen, Common::String::format("S %d", sprite.index), sprite.x + 2, sprite.y, 640, 14);
 		}
 	}
 
@@ -397,11 +424,11 @@ void PelrockEngine::paintDebugLayer() {
 	if (showHotspots) {
 		for (int i = 0; i < _room->_currentRoomHotspots.size(); i++) {
 			HotSpot hotspot = _room->_currentRoomHotspots[i];
-			if (!hotspot.isEnabled)
+			if (!hotspot.isEnabled || hotspot.isSprite)
 				continue;
-			drawRect(_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 180 + i);
-			_smallFont->drawString(_screen, Common::String::format("HS %d", i), hotspot.x + 2, hotspot.y + 2, 640, 14);
-			_smallFont->drawString(_screen, Common::String::format("x=%d", hotspot.extra), hotspot.x + 2, hotspot.y + 2 + 14, 640, 14);
+			drawRect(_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 12);
+			_smallFont->drawString(_screen, Common::String::format("HS %d", hotspot.index - _room->_currentRoomAnims.size()), hotspot.x + 2, hotspot.y + 2, 640, 12);
+			// _smallFont->drawString(_screen, Common::String::format("x=%d", hotspot.extra), hotspot.x + 2, hotspot.y + 2 + 14, 640, 14);
 		}
 	}
 
@@ -427,13 +454,9 @@ void PelrockEngine::paintDebugLayer() {
 }
 
 void PelrockEngine::placeStickers() {
-	for (int i = 0; i < _state->roomStickers[_room->_currentRoomNumber].size(); i++) {
-		Sticker sticker = _state->roomStickers[_room->_currentRoomNumber][i];
-		placeSticker(sticker);
-	}
 	// also place temporary stickers
-	for (int i = 0; i < _room->_transientStickers.size(); i++) {
-		Sticker sticker = _room->_transientStickers[i];
+	for (int i = 0; i < _room->_roomStickers.size(); i++) {
+		Sticker sticker = _room->_roomStickers[i];
 		placeSticker(sticker);
 	}
 }
@@ -564,7 +587,7 @@ void PelrockEngine::talkTo(HotSpot *hotspot) {
 }
 
 void PelrockEngine::lookAt(HotSpot *hotspot) {
-	debug("Looking at hotspot %d with extra %d", hotspot->index, hotspot->extra);
+	debug("Looking at hotspot %d (%d) with extra %d", hotspot->index, hotspot->isSprite? hotspot->index : hotspot->index - _room->_currentRoomAnims.size(), hotspot->extra);
 	_dialog->sayAlfred(_room->_currentRoomDescriptions[_currentHotspot->index]);
 	_actionPopupState.isActive = false;
 }
@@ -628,7 +651,8 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 
 		Exit *exit = isExitUnder(_alfredState.x, _alfredState.y);
 
-		if (exit != nullptr && exit->isEnabled) {
+		if (exit != nullptr /*&& exit->isEnabled*/) {
+			debug("Using exit to room %d", exit->targetRoom);
 			_alfredState.x = exit->targetX;
 			_alfredState.y = exit->targetY;
 			setScreen(exit->targetRoom, exit->dir);
@@ -1049,7 +1073,9 @@ int PelrockEngine::isHotspotUnder(int x, int y) {
 			x >= hotspot.x && x <= (hotspot.x + hotspot.w) &&
 			y >= hotspot.y && y <= (hotspot.y + hotspot.h)) {
 			// Check against sprite frame
-			if (hotspot.isSprite) {
+			if (!hotspot.isSprite) {
+				return hotspot.index;
+			} else {
 				Sprite *sprite = nullptr;
 				for (size_t j = 0; j < _room->_currentRoomAnims.size(); j++) {
 					if (_room->_currentRoomAnims[j].index == hotspot.index) {
@@ -1058,10 +1084,10 @@ int PelrockEngine::isHotspotUnder(int x, int y) {
 					}
 				}
 				bool spriteUnder = isSpriteUnder(sprite, x, y);
-				return spriteUnder ? i : -1;
+				if(spriteUnder)
+					return i;
+				else continue;
 			}
-
-			return hotspot.index;
 		}
 	}
 	return -1;
@@ -1110,7 +1136,7 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 		drawSpriteToBuffer(_compositeBuffer, 640, _res->_verbIcons[actions[i]], posx + 20 + (i * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
 	}
 	bool itemUnder = isItemUnder(_events->_mouseX, _events->_mouseY);
-	if (_state->selectedInventoryItem != -1) {
+	if (_state->selectedInventoryItem >= 0 && !_state->inventoryItems.empty()) {
 		if (itemUnder && shouldBlink) {
 			return;
 		}
@@ -1366,7 +1392,7 @@ void PelrockEngine::checkMouseHover() {
 	}
 }
 
-void PelrockEngine::setScreen(int number, AlfredDirection dir) {
+void PelrockEngine::setScreen(int roomNumber, AlfredDirection dir) {
 
 	Common::File roomFile;
 	if (!roomFile.open(Common::Path("ALFRED.1"))) {
@@ -1378,7 +1404,7 @@ void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 	_alfredState.direction = dir;
 	_alfredState.setState(ALFRED_IDLE);
 	_currentStep = 0;
-	int roomOffset = number * kRoomStructSize;
+	int roomOffset = roomNumber * kRoomStructSize;
 	_alfredState.curFrame = 0;
 
 	byte *palette = new byte[256 * 3];
@@ -1395,16 +1421,25 @@ void PelrockEngine::setScreen(int number, AlfredDirection dir) {
 	copyBackgroundToBuffer();
 	g_system->getPaletteManager()->setPalette(palette, 0, 256);
 
-	_room->loadRoomMetadata(&roomFile, number);
-	_room->loadRoomTalkingAnimations(number);
+	_room->loadRoomMetadata(&roomFile, roomNumber);
+	_room->loadRoomTalkingAnimations(roomNumber);
 	if (_room->_musicTrack > 0)
 		_sound->playMusicTrack(_room->_musicTrack);
 	else {
 		_sound->stopMusic();
 	}
-	doExtraActions(number);
+
+	if (findWalkboxForPoint(_room->_currentRoomWalkboxes, _alfredState.x, _alfredState.y) == 0xFF) {
+		const WalkBox w = _room->_currentRoomWalkboxes[0];
+		_alfredState.x = w.x;
+		_alfredState.y = w.y;
+	}
+
 	_screen->markAllDirty();
 	_screen->update();
+
+
+	doExtraActions(roomNumber);
 	roomFile.close();
 	delete[] background;
 	delete[] palette;
@@ -1444,6 +1479,13 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			loadExtraScreenAndPresent(4);
 		}
 		break;
+	case 15:
+		if(_state->flagIsSet(FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ)) {
+			_state->setFlag(FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ, false);
+			_dialog->say(_res->_ingameTexts[GAMBERROS]);
+			_dialog->say(_res->_ingameTexts[QUIENYO]);
+			_dialog->say(_res->_ingameTexts[PINTA_BUENAPERSONA]);
+		}
 
 	default:
 		break;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 4e0eeb9f5a5..7b0a8452448 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -244,6 +244,9 @@ public:
 	void openKitchenDrawer(HotSpot *hotspot);
 	void openKitchenDoorFromInside(HotSpot *hotspot);
 	void useSpicySauceWithBurger(int inventoryObject, HotSpot *hotspot);
+	void openShopDoor(HotSpot *hotspot);
+	void closeShopDoor(HotSpot *hotspot);
+	void openLamppost(HotSpot *hotspot);
 	void openDoor(HotSpot *hotspot, int doorIndex, int sticker, bool masculine, bool stayClosed);
 	void closeDoor(HotSpot *hotspot, int doorIndex, int sticker, bool masculine, bool stayOpen);
 	void pickUpPhoto(HotSpot *hotspot);
@@ -253,6 +256,7 @@ public:
 	void noOpItem(int item, HotSpot *hotspot);
 	void useOnAlfred(int inventoryObject);
 	void useCardWithATM(int inventoryObject, HotSpot *hotspot);
+	void useBrickWithWindow(int inventoryObject, HotSpot *hotspot);
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
 };
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index f858fac530c..5211fea94d8 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -90,45 +90,49 @@ void RoomManager::getBackground(Common::File *roomFile, int roomOffset, byte *ba
 
 void RoomManager::addSticker(int stickerId, bool persist) {
 	Sticker sticker = g_engine->_res->getSticker(stickerId);
+	_roomStickers.push_back(sticker);
 	if (persist)
-		g_engine->_state->roomStickers[_currentRoomNumber].push_back(sticker);
-	else
-		_transientStickers.push_back(sticker);
+		g_engine->_state->stickersPerRoom[_currentRoomNumber].push_back(sticker);
+}
+
+void RoomManager::onlyPersistSticker(byte room, int stickerId) {
+	Sticker sticker = g_engine->_res->getSticker(stickerId);
+	g_engine->_state->stickersPerRoom[room].push_back(sticker);
 }
 
 void RoomManager::removeSticker(int stickerIndex) {
 	int index = -1;
 	if (index == -1) {
-		for (int i = 0; i < _transientStickers.size(); i++) {
-			if (_transientStickers[i].stickerIndex == stickerIndex) {
+		for (int i = 0; i < _roomStickers.size(); i++) {
+			if (_roomStickers[i].stickerIndex == stickerIndex) {
 				index = i;
-				_transientStickers.remove_at(index);
+				_roomStickers.remove_at(index);
 				return;
 			}
 		}
 	}
 
-	for (int i = 0; i < g_engine->_state->roomStickers[_currentRoomNumber].size(); i++) {
-		if (g_engine->_state->roomStickers[_currentRoomNumber][i].stickerIndex == stickerIndex) {
+	for (int i = 0; i < g_engine->_state->stickersPerRoom[_currentRoomNumber].size(); i++) {
+		if (g_engine->_state->stickersPerRoom[_currentRoomNumber][i].stickerIndex == stickerIndex) {
 			index = i;
-			g_engine->_state->roomStickers[_currentRoomNumber].remove_at(index);
+			g_engine->_state->stickersPerRoom[_currentRoomNumber].remove_at(index);
 			break;
 		}
 	}
 
-	if (index != -1 && index < g_engine->_state->roomStickers[_currentRoomNumber].size())
-		g_engine->_state->roomStickers[_currentRoomNumber].remove_at(index);
+	if (index != -1 && index < g_engine->_state->stickersPerRoom[_currentRoomNumber].size())
+		g_engine->_state->stickersPerRoom[_currentRoomNumber].remove_at(index);
 }
 
 bool RoomManager::hasSticker(int index) {
-	for (int i = 0; i < g_engine->_state->roomStickers[_currentRoomNumber].size(); i++) {
-		if (g_engine->_state->roomStickers[_currentRoomNumber][i].stickerIndex == index) {
+	for (int i = 0; i < g_engine->_state->stickersPerRoom[_currentRoomNumber].size(); i++) {
+		if (g_engine->_state->stickersPerRoom[_currentRoomNumber][i].stickerIndex == index) {
 			return true;
 		}
 	}
 
-	for (int i = 0; i < _transientStickers.size(); i++) {
-		if (_transientStickers[i].stickerIndex == index) {
+	for (int i = 0; i < _roomStickers.size(); i++) {
+		if (_roomStickers[i].stickerIndex == index) {
 			return true;
 		}
 	}
@@ -166,7 +170,7 @@ void RoomManager::disableSprite(int roomNumber, int spriteIndex, bool persist) {
 }
 
 void RoomManager::enableSprite(int spriteIndex, int zOrder, bool persist) {
-	// _currentRoomAnims[spriteIndex].zOrder = zOrder;
+	_currentRoomAnims[spriteIndex].zOrder = zOrder;
 }
 
 void RoomManager::enableHotspot(HotSpot *hotspot, bool persist) {
@@ -187,6 +191,24 @@ void RoomManager::addWalkbox(WalkBox walkbox) {
 	g_engine->_state->roomWalkBoxChanges[_currentRoomNumber].push_back({_currentRoomNumber, walkbox.index, walkbox});
 }
 
+Sprite *RoomManager::findSpriteByIndex(byte index) {
+	for (int i = 0; i < _currentRoomAnims.size(); i++) {
+		if (_currentRoomAnims[i].index == index) {
+			return &_currentRoomAnims[i];
+		}
+	}
+	return nullptr;
+}
+
+HotSpot *RoomManager::findHotspotByIndex(byte index) {
+	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
+		if (!_currentRoomHotspots[i].isSprite && _currentRoomHotspots[i].innerIndex == index) {
+			return &_currentRoomHotspots[i];
+		}
+	}
+	return nullptr;
+}
+
 HotSpot *RoomManager::findHotspotByExtra(uint16 extra) {
 	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
 		if (_currentRoomHotspots[i].extra == extra) {
@@ -360,7 +382,7 @@ Common::Array<HotSpot> RoomManager::loadHotspots(byte *data, size_t size) {
 		spot.h = data[hotspotOffset + 6];
 		spot.isSprite = false;
 		spot.extra = READ_LE_INT16(data + hotspotOffset + 7);
-		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d, isEnabled=%d", spot.innerIndex, spot.actionFlags, spot.x, spot.y, spot.w, spot.h, spot.extra, spot.isEnabled);
+		// debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d, isEnabled=%d", spot.innerIndex, spot.actionFlags, spot.x, spot.y, spot.w, spot.h, spot.extra, spot.isEnabled);
 		hotspots.push_back(spot);
 	}
 
@@ -399,7 +421,7 @@ void RoomManager::resetConversationStates(byte roomNumber, byte *conversationDat
 void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 
 	uint32_t outPos = 0;
-	_transientStickers.clear();
+	_roomStickers.clear();
 	_currentRoomNumber = roomNumber;
 	int roomOffset = roomNumber * kRoomStructSize;
 
@@ -435,7 +457,7 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	_currentRoomExits = loadExits(pair10, pair10size);
 	_currentRoomWalkboxes = loadWalkboxes(pair10, pair10size);
 	_scaleParams = loadScalingParams(pair10, pair10size);
-
+	_roomStickers = g_engine->_state->stickersPerRoom[roomNumber];
 	// Pair 11 is the palette, already loaded
 
 	// Pair 12 - Room Texts
@@ -570,7 +592,7 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 			break;
 		}
 		sprite.animData = new Anim[sprite.numAnims];
-		// debug("AnimSet %d has %d sub-anims, type %d, actionFlags %d, isDisabled? %d", i, animSet.numAnims, animSet.spriteType, animSet.actionFlags, animSet.isDisabled);
+		// debug("Sprite %d has %d sub-anims, type %d, actionFlags %d, isDisabled? %d", i, sprite.numAnims, sprite.spriteType, sprite.actionFlags, sprite.isHotspotDisabled);
 		int subAnimOffset = animOffset + 10;
 		for (int j = 0; j < sprite.numAnims; j++) {
 
@@ -696,11 +718,16 @@ uint32 RoomManager::loadDescriptions(byte *pair12data, size_t pair12size, Common
 				}
 				pos++;
 			}
+			// Hardcoded fix in the original!
+			if(_currentRoomNumber == 3 && description.text.size() == 1 && description.text[0] == 0x2D) {
+				outDescriptions.push_back(description);
+			}
 			outDescriptions.push_back(description);
 			lastDescPos = pos;
 		}
 		pos++;
 	}
+
 	return lastDescPos + 1;
 }
 
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index cd0354871e4..7f5d18184cc 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -43,6 +43,7 @@ public:
 
 	/** Methods to modify room data at runtime **/
 	void addSticker(int stickerId, bool persist = true);
+	void onlyPersistSticker(byte room, int stickerId);
 	void removeSticker(int index);
 	bool hasSticker(int index);
 	void changeExit(int index, bool enabled, bool persist = true);
@@ -62,6 +63,8 @@ public:
 	void applyDisabledChoice(ResetEntry entry, byte *conversationData, size_t conversationDataSize);
 	void addDisabledChoice(ChoiceOption choice);
 
+	Sprite *findSpriteByIndex(byte index);
+	HotSpot *findHotspotByIndex(byte index);
 	HotSpot *findHotspotByExtra(uint16 extra);
 	PaletteAnim *getPaletteAnimForRoom(int roomNumber);
 
@@ -89,7 +92,7 @@ public:
 	PaletteAnim *_currentPaletteAnim = nullptr;
 	byte *_conversationData = nullptr;
 	size_t _conversationDataSize = 0;
-	Common::Array<Sticker> _transientStickers;
+	Common::Array<Sticker> _roomStickers;
 	uint32 _conversationOffset;
 
 private:
diff --git a/engines/pelrock/saveload.cpp b/engines/pelrock/saveload.cpp
index 4f92d03b297..51066a03da5 100644
--- a/engines/pelrock/saveload.cpp
+++ b/engines/pelrock/saveload.cpp
@@ -122,7 +122,7 @@ bool syncGameStateData(Common::Serializer &s, GameStateData *gameState) {
 	s.syncAsUint32LE((uint32 &)gameState->stateGame);
 
 	// Flags
-	for (int i = 0; i < 46; ++i) {
+	for (int i = 0; i < kNumGameFlags; ++i) {
 		s.syncAsByte((byte &)gameState->flags[i]);
 	}
 	// Inventory items
@@ -138,10 +138,10 @@ bool syncGameStateData(Common::Serializer &s, GameStateData *gameState) {
 	s.syncAsSint16LE(gameState->selectedInventoryItem);
 
 	// Room stickers
-	uint16 stickersSize = (uint16)gameState->roomStickers.size();
+	uint16 stickersSize = (uint16)gameState->stickersPerRoom.size();
 	s.syncAsUint16LE(stickersSize);
 	if (s.isSaving()) {
-		for (const auto &pair : gameState->roomStickers) {
+		for (const auto &pair : gameState->stickersPerRoom) {
 			byte roomNumber = pair._key;
 			s.syncAsByte(roomNumber);
 			const Common::Array<Sticker> &stickers = pair._value;
@@ -153,7 +153,7 @@ bool syncGameStateData(Common::Serializer &s, GameStateData *gameState) {
 			}
 		}
 	} else {
-		gameState->roomStickers.clear();
+		gameState->stickersPerRoom.clear();
 		for (uint16 idx = 0; idx < stickersSize; ++idx) {
 			byte roomNumber;
 			s.syncAsByte(roomNumber);
@@ -165,7 +165,7 @@ bool syncGameStateData(Common::Serializer &s, GameStateData *gameState) {
 				s.syncAsSint32LE(stickerIndex);
 				stickers.push_back(g_engine->_res->getSticker(stickerIndex));
 			}
-			gameState->roomStickers[roomNumber] = stickers;
+			gameState->stickersPerRoom[roomNumber] = stickers;
 		}
 	}
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 4970659abec..9714e9ae6ad 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -432,9 +432,12 @@ struct ResetEntry {
 #define FLAG_FROM_INTRO 43
 #define FLAG_HE_TIRADO_PIEDRA 44
 #define FLAG_HA_USADO_AGUA 45
+#define FLAG_TIENDA_ABIERTA 46
+
+const int kNumGameFlags = 47;
 
 struct GameStateData {
-	bool flags[46];
+	bool flags[kNumGameFlags];
 
 	byte NUMERO_DE_COPAS = false;
 	byte INGREDIENTES_CONSEGUIDOS = 0;
@@ -442,9 +445,9 @@ struct GameStateData {
 	GameState stateGame = INTRO;
 
 	Common::Array<byte> inventoryItems;
-	int16 selectedInventoryItem = -1;
+	int16 selectedInventoryItem = 0;
 
-	Common::HashMap<byte, Common::Array<Sticker>> roomStickers;
+	Common::HashMap<byte, Common::Array<Sticker>> stickersPerRoom;
 	// Common::HashMap<byte, ResetEntry> roomExitChanges;
 	Common::HashMap<byte, Common::Array<ExitChange>> roomExitChanges;
 	Common::HashMap<byte, Common::Array<WalkBoxChange>> roomWalkBoxChanges;
@@ -454,6 +457,7 @@ struct GameStateData {
 
 	GameStateData() {
 		memset(conversationRootsState, 0, 4 * 56);
+		flags[FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ] = true;
 	}
 
 	~GameStateData() {
@@ -481,7 +485,7 @@ struct GameStateData {
 		inventoryItems.push_back(id);
 	}
 
-	void removeInventoyItem(int id) {
+	void removeInventoryItem(int id) {
 		for (int i = 0; i < inventoryItems.size(); i++) {
 			if (inventoryItems[i] == id) {
 				inventoryItems.remove_at(i);


Commit: b3e454c6ff98d176a0182a3af67da23507d2dfc8
    https://github.com/scummvm/scummvm/commit/b3e454c6ff98d176a0182a3af67da23507d2dfc8
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:48+02:00

Commit Message:
PELROCK: Implements Room 15

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/offsets.h
    engines/pelrock/pathfinding.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index aeb70e48c20..cbcf6042c0c 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -61,6 +61,21 @@ const ActionEntry actionTable[] = {
 	{290, OPEN, &PelrockEngine::openShopDoor},
 	{290, CLOSE, &PelrockEngine::closeShopDoor},
 	{32, OPEN, &PelrockEngine::openLamppost},
+	{308, PICKUP, &PelrockEngine::moveCable}, // Lamppost cable
+
+	// Room 15
+	{65, PICKUP, &PelrockEngine::pickGuitar},
+	{66, PICKUP, &PelrockEngine::pickFish},
+	{67, PICKUP, &PelrockEngine::pickTeddyBear},
+	{68, PICKUP, &PelrockEngine::pickDiscs},
+	{69, PICKUP, &PelrockEngine::pickMonkeyBrain},
+	{70, PICKUP, &PelrockEngine::pickBooks},
+	{71, PICKUP, &PelrockEngine::pickPalette},
+	{72, PICKUP, &PelrockEngine::pickCandy},
+	{73, PICKUP, &PelrockEngine::pickConch},
+	{74, PICKUP, &PelrockEngine::pickHat},
+	{6, PICKUP, &PelrockEngine::pickCord},
+	{7, PICKUP, &PelrockEngine::pickAmulet},
 
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
@@ -79,6 +94,7 @@ const CombinationEntry combinationTable[] = {
 	{2, 281, &PelrockEngine::useCardWithATM},           // Use ATM Card with ATM
 	{62, 117, &PelrockEngine::useSpicySauceWithBurger}, // Use Spicy Sauce with Burger
 	{4, 294, &PelrockEngine::useBrickWithWindow},       // Use Brick with Window (Room 3)
+	{4, 295, &PelrockEngine::useBrickWithShopWindow},
 	// End marker
 	{WILDCARD, WILDCARD, nullptr}};
 
@@ -211,18 +227,10 @@ void PelrockEngine::closeShopDoor(HotSpot *hotspot) {
 void PelrockEngine::openLamppost(HotSpot *hotspot) {
 	debug("Opening lamppost");
 	_room->addSticker(14);
+	_room->moveHotspot(_room->findHotspotByExtra(308), 519, 363);
 }
 
 void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
-	debug("Using brick with window - sticker 11 will be added");
-
-	// Check if window is already broken
-	if (_room->hasSticker(11)) {
-		// Window already broken, say something generic
-		_alfredState.direction = ALFRED_UP;
-		_dialog->say(_res->_ingameTexts[YA_ABIERTO_M]); // "It's already open/broken"
-		return;
-	}
 
 	// TODO: Play Alfred's throwing animation
 	// This would require adding a new special animation entry
@@ -259,7 +267,7 @@ void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
 	// Remove brick from inventory
 	_state->removeInventoryItem(4);
 
-	int16 x = 639; // put at the very edge of the screen
+	int16 x = 639; // put at the very edge of the screen so it gets adjusted
 	int16 y = windowHotspot->y;
 	// Play the NPC dialog sequence
 	int16 dialog1y = y + 22;
@@ -276,6 +284,66 @@ void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
 	walkTo(639, _alfredState.y);
 }
 
+void PelrockEngine::moveCable(HotSpot *hotspot) {
+	_room->addSticker(15);
+	_room->addSticker(16);
+	_room->addSticker(17);
+	_room->addStickerToRoom(4, 20); // Room 4, sticker 20
+	_state->setFlag(FLAG_CABLES_PUESTOS, true);
+}
+
+void PelrockEngine::useBrickWithShopWindow(int inventoryObject, HotSpot *hotspot) {
+	_dialog->say(_res->_ingameTexts[NOSE_ENTERARIA]);
+}
+
+void PelrockEngine::pickGuitar(HotSpot *hotspot) {
+	buyFromStore(hotspot, 38);
+}
+
+void PelrockEngine::pickFish(HotSpot *hotspot) {
+	buyFromStore(hotspot, 39);
+}
+
+void PelrockEngine::pickTeddyBear(HotSpot *hotspot) {
+	buyFromStore(hotspot, 40);
+}
+
+void PelrockEngine::pickDiscs(HotSpot *hotspot) {
+	buyFromStore(hotspot, 41);
+}
+
+void PelrockEngine::pickMonkeyBrain(HotSpot *hotspot) {
+	buyFromStore(hotspot, 42);
+}
+
+void PelrockEngine::pickBooks(HotSpot *hotspot) {
+	buyFromStore(hotspot, 43);
+}
+
+void PelrockEngine::pickPalette(HotSpot *hotspot) {
+	buyFromStore(hotspot, 44);
+}
+
+void PelrockEngine::pickCandy(HotSpot *hotspot) {
+	buyFromStore(hotspot, 45);
+}
+
+void PelrockEngine::pickConch(HotSpot *hotspot) {
+	buyFromStore(hotspot, 46);
+}
+
+void PelrockEngine::pickHat(HotSpot *hotspot) {
+	buyFromStore(hotspot, 47);
+}
+
+void PelrockEngine::pickCord(HotSpot *hotspot) {
+	buyFromStore(hotspot, 48);
+}
+
+void PelrockEngine::pickAmulet(HotSpot *hotspot) {
+	buyFromStore(hotspot, 49);
+}
+
 void PelrockEngine::openDoor(HotSpot *hotspot, int exitIndex, int sticker, bool masculine, bool stayClosed) {
 	if (_room->hasSticker(sticker)) {
 		int text = masculine == MASCULINE ? YA_ABIERTO_M : YA_ABIERTA_F;
@@ -284,6 +352,7 @@ void PelrockEngine::openDoor(HotSpot *hotspot, int exitIndex, int sticker, bool
 	}
 	_room->enableExit(exitIndex, !stayClosed);
 	_room->addSticker(sticker, !stayClosed);
+	_sound->playSound(_room->_roomSfx[0]);
 }
 
 void PelrockEngine::closeDoor(HotSpot *hotspot, int exitIndex, int sticker, bool masculine, bool stayOpen) {
@@ -294,6 +363,7 @@ void PelrockEngine::closeDoor(HotSpot *hotspot, int exitIndex, int sticker, bool
 	}
 	_room->disableExit(exitIndex, !stayOpen);
 	_room->removeSticker(sticker);
+	_sound->playSound(_room->_roomSfx[1]);
 }
 
 void PelrockEngine::addInventoryItem(int item) {
@@ -315,6 +385,27 @@ void PelrockEngine::addInventoryItem(int item) {
 	_state->addInventoryItem(item);
 }
 
+void PelrockEngine::buyFromStore(HotSpot *hotspot, int stickerId) {
+	if(_state->hasInventoryItem(5) == false)
+	{
+		_dialog->say(_res->_ingameTexts[NOTENGODINERO]);
+		return;
+	}
+	else {
+		_room->addSticker(stickerId);
+		_room->disableHotspot(hotspot);
+		_state->addInventoryItem(hotspot->extra);
+		_currentHotspot = nullptr;
+		walkLoop(224, 283, ALFRED_LEFT);
+		_dialog->say(_res->_ingameTexts[CUESTA1000]);
+		_dialog->say(_res->_ingameTexts[AQUITIENE]);
+		_dialog->say(_res->_ingameTexts[MUYBIEN]);
+		_state->removeInventoryItem(5); // Remove 1000 pesetas bill
+	}
+}
+
+
+
 void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte rootIndex) {
 	if (actionTrigger == 328) {
 		debug("Disabling root %d in room %d", rootIndex, room);
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 9f14fc77506..85268139a93 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -297,6 +297,7 @@ enum TextIndices {
 	DOSINGREDIENTES,
 	TRESINGREDIENTES,
 	CUATROINGREDIENTES,
+	LIBRO_ABURRIDO,
 	DEACUERDO_2,
 	GAMBERROS,
 	QUIENYO,
diff --git a/engines/pelrock/pathfinding.cpp b/engines/pelrock/pathfinding.cpp
index a89d9cee7ad..d141bb3af21 100644
--- a/engines/pelrock/pathfinding.cpp
+++ b/engines/pelrock/pathfinding.cpp
@@ -64,12 +64,12 @@ bool findPath(int sourceX, int sourceY, int targetX, int targetY, Common::Array<
 	Common::Point target = calculateWalkTarget(walkboxes, targetX, targetY, 2, nullptr);
 	targetX = target.x;
 	targetY = target.y;
-	debug("Startx= %d, starty= %d, destx= %d, desty= %d", startX, startY, targetX, targetY);
+	// debug("Startx= %d, starty= %d, destx= %d, desty= %d", startX, startY, targetX, targetY);
 
 	uint8_t startBox = findWalkboxForPoint(walkboxes, startX, startY);
 	uint8_t destBox = findWalkboxForPoint(walkboxes, targetX, targetY);
 
-	debug("Pathfinding from (%d, %d) in box %d to (%d, %d) in box %d\n", startX, startY, startBox, targetX, targetY, destBox);
+	// debug("Pathfinding from (%d, %d) in box %d to (%d, %d) in box %d\n", startX, startY, startBox, targetX, targetY, destBox);
 	// Check if both points are in valid walkboxes
 	if (startBox == 0xFF || destBox == 0xFF) {
 		debug("Error: Start or destination not in any walkbox\n");
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index bb8f7e68ec1..0193577b6f6 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -161,7 +161,7 @@ void PelrockEngine::init() {
 		gameInitialized = true;
 		loadAnims();
 		// setScreen(0, ALFRED_DOWN);
-		setScreen(3, ALFRED_RIGHT);
+		setScreen(15, ALFRED_RIGHT);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
@@ -237,7 +237,7 @@ bool PelrockEngine::renderScene(int overlayMode) {
 
 	_chrono->updateChrono();
 	if (_chrono->_gameTick) {
-		playSoundIfNeeded();
+		// playSoundIfNeeded();
 
 		copyBackgroundToBuffer();
 
@@ -427,7 +427,7 @@ void PelrockEngine::paintDebugLayer() {
 			if (!hotspot.isEnabled || hotspot.isSprite)
 				continue;
 			drawRect(_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 12);
-			_smallFont->drawString(_screen, Common::String::format("HS %d", hotspot.index - _room->_currentRoomAnims.size()), hotspot.x + 2, hotspot.y + 2, 640, 12);
+			_smallFont->drawString(_screen, Common::String::format("HS %d - %d", hotspot.index - _room->_currentRoomAnims.size(), hotspot.extra), hotspot.x + 2, hotspot.y + 2, 640, 12);
 			// _smallFont->drawString(_screen, Common::String::format("x=%d", hotspot.extra), hotspot.x + 2, hotspot.y + 2 + 14, 640, 14);
 		}
 	}
@@ -562,7 +562,9 @@ void PelrockEngine::doAction(VerbIcon action, HotSpot *hotspot) {
 		break;
 	case PICKUP:
 		_alfredState.setState(ALFRED_INTERACTING);
-		pickUpAndDisable(hotspot);
+		if(_room->isPickableByExtra(hotspot->extra)) {
+			pickUpAndDisable(hotspot);
+		}
 		executeAction(PICKUP, hotspot);
 		break;
 	case OPEN:
@@ -633,6 +635,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			_currentStep++;
 			if (_currentStep >= _currentContext.movementCount) {
 				_currentStep = 0;
+				debug("Finished walking to target");
 				_alfredState.setState(ALFRED_IDLE);
 
 				if (_currentHotspot != nullptr)
@@ -640,8 +643,8 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 				drawAlfred(_res->alfredIdle[_alfredState.direction]);
 
 				if (_queuedAction.isQueued) {
-					doAction(_queuedAction.verb, &_room->_currentRoomHotspots[_queuedAction.hotspotIndex]);
 					_queuedAction.isQueued = false;
+					doAction(_queuedAction.verb, &_room->_currentRoomHotspots[_queuedAction.hotspotIndex]);
 					break;
 				}
 			}
@@ -931,10 +934,13 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 		_actionPopupState.isActive = true;
 		_actionPopupState.curFrame = 0;
 
-		_actionPopupState.isAlfredUnder = alfredUnder;
 		if (hotspotIndex != -1) {
 			_currentHotspot = &_room->_currentRoomHotspots[hotspotIndex];
 		}
+		else {
+			_actionPopupState.isAlfredUnder = alfredUnder;
+			_currentHotspot = nullptr;
+		}
 	}
 }
 
@@ -1220,6 +1226,19 @@ void PelrockEngine::extraScreenLoop() {
 	_extraScreen = nullptr;
 }
 
+void PelrockEngine::walkLoop(int16 x, int16 y, AlfredDirection direction) {
+
+	_alfredState.direction = direction;
+	walkTo(x, y);
+	while(!shouldQuit() && _alfredState.animState == ALFRED_WALKING) {
+		_events->pollEvent();
+		renderScene();
+		_screen->update();
+		g_system->delayMillis(10);
+	}
+	debug("Walk loop ended");
+}
+
 void PelrockEngine::walkTo(int x, int y) {
 	_currentStep = 0;
 	PathContext context = {nullptr, nullptr, nullptr, 0, 0, 0};
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 7b0a8452448..8350fb76227 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -111,6 +111,7 @@ private:
 
 	void gameLoop();
 	void extraScreenLoop();
+	void walkLoop(int16 x, int16 y, AlfredDirection direction);
 
 	void checkMouseHover();
 	void checkMouseClick(int x, int y);
@@ -226,6 +227,7 @@ public:
 	bool renderScene(int overlayMode = OVERLAY_NONE);
 
 	void addInventoryItem(int item);
+	void buyFromStore(HotSpot *hotspot, int stickerId);
 	// Actions
 	void performActionTrigger(uint16 actionTrigger);
 	void dialogActionTrigger(uint16 actionTrigger, byte room, byte rootIndex);
@@ -257,6 +259,20 @@ public:
 	void useOnAlfred(int inventoryObject);
 	void useCardWithATM(int inventoryObject, HotSpot *hotspot);
 	void useBrickWithWindow(int inventoryObject, HotSpot *hotspot);
+	void moveCable(HotSpot *hotspot);
+	void useBrickWithShopWindow(int inventoryObject, HotSpot *hotspot);
+	void pickGuitar(HotSpot *hotspot);
+	void pickFish(HotSpot *hotspot);
+	void pickTeddyBear(HotSpot *hotspot);
+	void pickDiscs(HotSpot *hotspot);
+	void pickMonkeyBrain(HotSpot *hotspot);
+	void pickBooks(HotSpot *hotspot);
+	void pickPalette(HotSpot *hotspot);
+	void pickCandy(HotSpot *hotspot);
+	void pickConch(HotSpot *hotspot);
+	void pickHat(HotSpot *hotspot);
+	void pickCord(HotSpot *hotspot);
+	void pickAmulet(HotSpot *hotspot);
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
 };
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 5211fea94d8..04d86740812 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -91,8 +91,12 @@ void RoomManager::getBackground(Common::File *roomFile, int roomOffset, byte *ba
 void RoomManager::addSticker(int stickerId, bool persist) {
 	Sticker sticker = g_engine->_res->getSticker(stickerId);
 	_roomStickers.push_back(sticker);
-	if (persist)
-		g_engine->_state->stickersPerRoom[_currentRoomNumber].push_back(sticker);
+	addStickerToRoom(_currentRoomNumber, stickerId);
+}
+
+void RoomManager::addStickerToRoom(byte room, int stickerId) {
+	Sticker sticker = g_engine->_res->getSticker(stickerId);
+	g_engine->_state->stickersPerRoom[room].push_back(sticker);
 }
 
 void RoomManager::onlyPersistSticker(byte room, int stickerId) {
@@ -162,7 +166,7 @@ void RoomManager::changeHotSpot(HotSpot hotspot) {
 	g_engine->_state->roomHotSpotChanges[_currentRoomNumber].push_back({_currentRoomNumber, hotspot.innerIndex, hotspot});
 }
 
-void RoomManager::disableSprite(int roomNumber, int spriteIndex, bool persist) {
+void RoomManager::disableSprite(byte roomNumber, int spriteIndex, bool persist) {
 	if (roomNumber == _currentRoomNumber) {
 		_currentRoomAnims[spriteIndex].zOrder = 255;
 	}
@@ -187,6 +191,14 @@ void RoomManager::disableHotspot(HotSpot *hotspot, bool persist) {
 	}
 }
 
+void RoomManager::moveHotspot(HotSpot *hotspot, int16 newX, int16 newY, bool persist) {
+	hotspot->x = newX;
+	hotspot->y = newY;
+	if (persist) {
+		changeHotSpot(*hotspot);
+	}
+}
+
 void RoomManager::addWalkbox(WalkBox walkbox) {
 	g_engine->_state->roomWalkBoxChanges[_currentRoomNumber].push_back({_currentRoomNumber, walkbox.index, walkbox});
 }
@@ -382,7 +394,7 @@ Common::Array<HotSpot> RoomManager::loadHotspots(byte *data, size_t size) {
 		spot.h = data[hotspotOffset + 6];
 		spot.isSprite = false;
 		spot.extra = READ_LE_INT16(data + hotspotOffset + 7);
-		// debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d, isEnabled=%d", spot.innerIndex, spot.actionFlags, spot.x, spot.y, spot.w, spot.h, spot.extra, spot.isEnabled);
+		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d, isEnabled=%d", spot.innerIndex, spot.actionFlags, spot.x, spot.y, spot.w, spot.h, spot.extra, spot.isEnabled);
 		hotspots.push_back(spot);
 	}
 
@@ -744,7 +756,7 @@ void RoomManager::loadConversationData(byte *pair12data, size_t pair12size, uint
 	}
 }
 
-void RoomManager::applyDisabledChoices(int roomNumber, byte *conversationData, size_t conversationDataSize) {
+void RoomManager::applyDisabledChoices(byte roomNumber, byte *conversationData, size_t conversationDataSize) {
 	Common::Array<ResetEntry> disabledBranches = g_engine->_state->disabledBranches[roomNumber];
 	if (disabledBranches.size() == 0) {
 		return;
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 7f5d18184cc..eb624d32b35 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -30,6 +30,21 @@
 namespace Pelrock {
 
 static const int kNumSfxPerRoom = 8;
+static const int unpickableHotspotExtras[] = {
+	308, // lamppost cable
+	65, // objects in shop
+	66,
+	67,
+	68,
+	69,
+	70,
+	71,
+	72,
+	73,
+	74,
+	6,
+	7
+};
 
 class RoomManager {
 public:
@@ -43,26 +58,46 @@ public:
 
 	/** Methods to modify room data at runtime **/
 	void addSticker(int stickerId, bool persist = true);
+	void addStickerToRoom(byte room, int stickerId);
 	void onlyPersistSticker(byte room, int stickerId);
 	void removeSticker(int index);
+	void removeSticker(byte room, int index);
 	bool hasSticker(int index);
+	bool hasSticker(byte room, int index);
 	void changeExit(int index, bool enabled, bool persist = true);
+	void changeExit(byte room, int index, bool enabled, bool persist = true);
 	void disableExit(int index, bool persist = true);
+	void disableExit(byte room, int index, bool persist = true);
 	void enableExit(int index, bool persist = true);
+	void enableExit(byte room, int index, bool persist = true);
 	void changeWalkBox(WalkBox walkbox);
+	void changeWalkbox(byte room, WalkBox walkbox);
 	void changeHotSpot(HotSpot hotspot);
-	void disableSprite(int roomNumber, int spriteIndex, bool persist = true);
+	void changeHotspot(byte room, HotSpot hotspot);
+	void disableSprite(byte roomNumber, int spriteIndex, bool persist = true);
 	void enableSprite(int spriteIndex, int zOrder, bool persist = true);
+	void enableSprite(byte roomNumber,int spriteIndex, int zOrder, bool persist = true);
 	/**
 	 * Utility function to enable or disable a hotspot, with an option to persist the change.
 	 */
 	void enableHotspot(HotSpot *hotspot, bool persist = true);
+	void enableHotspot(byte room, HotSpot *hotspot, bool persist = true);
 	void disableHotspot(HotSpot *hotspot, bool persist = true);
+	void disableHotspot(byte room, HotSpot *hotspot, bool persist = true);
+	void moveHotspot(HotSpot *hotspot, int16 newX, int16 newY, bool persist = true);
+	void moveHotspot(byte room, HotSpot *hotspot, int16 newX, int16 newY, bool persist = true);
 	void addWalkbox(WalkBox walkbox);
-	void applyDisabledChoices(int roomNumber, byte *conversationData, size_t conversationDataSize);
+	void addWalkbox(byte room, WalkBox walkbox);
+	void applyDisabledChoices(byte roomNumber, byte *conversationData, size_t conversationDataSize);
 	void applyDisabledChoice(ResetEntry entry, byte *conversationData, size_t conversationDataSize);
 	void addDisabledChoice(ChoiceOption choice);
-
+	bool isPickableByExtra(uint16 extra)  {
+		for(int i = 0; i < sizeof(unpickableHotspotExtras); i++) {
+			if (extra == unpickableHotspotExtras[i])
+				return false;
+		}
+		return true;
+	}
 	Sprite *findSpriteByIndex(byte index);
 	HotSpot *findHotspotByIndex(byte index);
 	HotSpot *findHotspotByExtra(uint16 extra);
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 9714e9ae6ad..a307e46697e 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -470,13 +470,13 @@ struct GameStateData {
 	}
 
 	bool flagIsSet(int flagIndex) const {
-		if (flagIndex < 0 || flagIndex >= 46)
+		if (flagIndex < 0 || flagIndex >= kNumGameFlags)
 			return false;
 		return flags[flagIndex];
 	}
 
 	void setFlag(int flagIndex, bool value) {
-		if (flagIndex < 0 || flagIndex >= 46)
+		if (flagIndex < 0 || flagIndex >= kNumGameFlags)
 			return;
 		flags[flagIndex] = value;
 	}
@@ -494,6 +494,15 @@ struct GameStateData {
 		}
 	}
 
+	bool hasInventoryItem(int id) const {
+		for (int i = 0; i < inventoryItems.size(); i++) {
+			if (inventoryItems[i] == id) {
+				return true;
+			}
+		}
+		return false;
+	}
+
 	byte *conversationRootsState = new byte[4 * 56];
 
 	bool getRootDisabledState(byte room, byte root) const {


Commit: 0078df575bbd9e91e4de12efffc68bee09eece4e
    https://github.com/scummvm/scummvm/commit/0078df575bbd9e91e4de12efffc68bee09eece4e
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:48+02:00

Commit Message:
PELROCK: Implements actions on Room 4

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/menu.cpp
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index cbcf6042c0c..2af488824fa 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -77,6 +77,10 @@ const ActionEntry actionTable[] = {
 	{6, PICKUP, &PelrockEngine::pickCord},
 	{7, PICKUP, &PelrockEngine::pickAmulet},
 
+	// Room 4
+	{315, OPEN, &PelrockEngine::openPlug},
+	{316, PICKUP, &PelrockEngine::pickCables},
+
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
 	{WILDCARD, TALK, &PelrockEngine::noOpAction},   // Generic talk action
@@ -95,9 +99,89 @@ const CombinationEntry combinationTable[] = {
 	{62, 117, &PelrockEngine::useSpicySauceWithBurger}, // Use Spicy Sauce with Burger
 	{4, 294, &PelrockEngine::useBrickWithWindow},       // Use Brick with Window (Room 3)
 	{4, 295, &PelrockEngine::useBrickWithShopWindow},
+	{6, 315, &PelrockEngine::useCordWithPlug},
 	// End marker
 	{WILDCARD, WILDCARD, nullptr}};
 
+void PelrockEngine::openDoor(HotSpot *hotspot, int exitIndex, int sticker, bool masculine, bool stayClosed) {
+	if (_room->hasSticker(sticker)) {
+		int text = masculine == MASCULINE ? YA_ABIERTO_M : YA_ABIERTA_F;
+		_dialog->say(_res->_ingameTexts[text]);
+		return;
+	}
+	_room->enableExit(exitIndex, !stayClosed);
+	_room->addSticker(sticker, !stayClosed);
+	_sound->playSound(_room->_roomSfx[0]);
+}
+
+void PelrockEngine::closeDoor(HotSpot *hotspot, int exitIndex, int sticker, bool masculine, bool stayOpen) {
+	if (!_room->hasSticker(sticker)) {
+		int text = masculine == MASCULINE ? YA_CERRADO_M : YA_CERRADA_F;
+		_dialog->say(_res->_ingameTexts[text]);
+		return;
+	}
+	_room->disableExit(exitIndex, !stayOpen);
+	_room->removeSticker(sticker);
+	_sound->playSound(_room->_roomSfx[1]);
+}
+
+void PelrockEngine::addInventoryItem(int item) {
+	if (_state->inventoryItems.size() == 0) {
+		_state->selectedInventoryItem = item;
+	}
+	_flashingIcon = item;
+	int frameCounter = 0;
+	while (frameCounter < kIconFlashDuration) {
+		_events->pollEvent();
+
+		bool didRender = renderScene(OVERLAY_PICKUP_ICON);
+		_screen->update();
+		if (didRender) {
+			frameCounter++;
+		}
+		g_system->delayMillis(10);
+	}
+	_state->addInventoryItem(item);
+}
+
+void PelrockEngine::buyFromStore(HotSpot *hotspot, int stickerId) {
+	if (_state->hasInventoryItem(5) == false) {
+		_dialog->say(_res->_ingameTexts[NOTENGODINERO]);
+		return;
+	} else {
+		_room->addSticker(stickerId);
+		_room->disableHotspot(hotspot);
+		if (hotspot->extra == 69) {
+			_room->disableSprite(15, 3); // Disable monkey brain sprite
+		}
+		_state->addInventoryItem(hotspot->extra);
+		_currentHotspot = nullptr;
+		walkLoop(224, 283, ALFRED_LEFT);
+		_dialog->say(_res->_ingameTexts[CUESTA1000]);
+		_dialog->say(_res->_ingameTexts[AQUITIENE]);
+		_dialog->say(_res->_ingameTexts[MUYBIEN]);
+		_state->removeInventoryItem(5); // Remove 1000 pesetas bill
+	}
+}
+
+void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte rootIndex) {
+	if (actionTrigger == 328) {
+		debug("Disabling root %d in room %d", rootIndex, room);
+		_state->setRootDisabledState(room, rootIndex, true);
+	}
+}
+
+void PelrockEngine::noOpAction(HotSpot *hotspot) {
+}
+
+void PelrockEngine::noOpItem(int item, HotSpot *hotspot) {
+	// 154 to 169
+	debug("No-op item %d with hotspot %d", item, hotspot->extra);
+	_alfredState.direction = ALFRED_DOWN;
+	byte response = (byte)getRandomNumber(12);
+	_dialog->say(_res->_ingameTexts[154 + response]);
+}
+
 void PelrockEngine::openRoomDoor(HotSpot *hotspot) {
 	openDoor(hotspot, 0, 93, FEMININE, true);
 }
@@ -139,7 +223,7 @@ void PelrockEngine::closeRoomDrawer(HotSpot *hotspot) {
 
 void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
 	debug("Withdrawing money from ATM using card (inv obj %d)", inventoryObject);
-	if (_state->flagIsSet(FLAG_JEFE_INGRESA_PASTA)) {
+	if (_state->getFlag(FLAG_JEFE_INGRESA_PASTA)) {
 		_state->setFlag(FLAG_JEFE_INGRESA_PASTA, false);
 		addInventoryItem(75);
 	} else {
@@ -193,7 +277,7 @@ void PelrockEngine::closeKitchenDoor(HotSpot *HotSpot) {
 }
 
 void PelrockEngine::openKitchenDrawer(HotSpot *hotspot) {
-	if (!_state->flagIsSet(FLAG_JEFE_ENCARCELADO)) {
+	if (!_state->getFlag(FLAG_JEFE_ENCARCELADO)) {
 		_dialog->say(_res->_ingameTexts[QUITA_ESAS_MANOS]);
 	} else {
 		_room->addSticker(36);
@@ -212,7 +296,7 @@ void PelrockEngine::useSpicySauceWithBurger(int inventoryObject, HotSpot *hotspo
 }
 
 void PelrockEngine::openShopDoor(HotSpot *hotspot) {
-	if (!_state->flagIsSet(FLAG_TIENDA_ABIERTA)) {
+	if (!_state->getFlag(FLAG_TIENDA_ABIERTA)) {
 		_dialog->say(_res->_ingameTexts[TIENDA_CERRADA]);
 		return;
 	} else {
@@ -344,73 +428,43 @@ void PelrockEngine::pickAmulet(HotSpot *hotspot) {
 	buyFromStore(hotspot, 49);
 }
 
-void PelrockEngine::openDoor(HotSpot *hotspot, int exitIndex, int sticker, bool masculine, bool stayClosed) {
-	if (_room->hasSticker(sticker)) {
-		int text = masculine == MASCULINE ? YA_ABIERTO_M : YA_ABIERTA_F;
-		_dialog->say(_res->_ingameTexts[text]);
-		return;
-	}
-	_room->enableExit(exitIndex, !stayClosed);
-	_room->addSticker(sticker, !stayClosed);
-	_sound->playSound(_room->_roomSfx[0]);
-}
-
-void PelrockEngine::closeDoor(HotSpot *hotspot, int exitIndex, int sticker, bool masculine, bool stayOpen) {
-	if (!_room->hasSticker(sticker)) {
-		int text = masculine == MASCULINE ? YA_CERRADO_M : YA_CERRADA_F;
-		_dialog->say(_res->_ingameTexts[text]);
-		return;
-	}
-	_room->disableExit(exitIndex, !stayOpen);
-	_room->removeSticker(sticker);
-	_sound->playSound(_room->_roomSfx[1]);
+void PelrockEngine::openPlug(HotSpot *hotspot) {
+	_room->addSticker(18);
 }
 
-void PelrockEngine::addInventoryItem(int item) {
-	if (_state->inventoryItems.size() == 0) {
-		_state->selectedInventoryItem = item;
-	}
-	_flashingIcon = item;
-	int frameCounter = 0;
-	while (frameCounter < kIconFlashDuration) {
-		_events->pollEvent();
-
-		bool didRender = renderScene(OVERLAY_PICKUP_ICON);
-		_screen->update();
-		if (didRender) {
-			frameCounter++;
+void PelrockEngine::useCordWithPlug(int inventoryObject, HotSpot *hotspot) {
+	if (!_room->hasSticker(18)) {
+		_dialog->say(_res->_ingameTexts[PRIMERO_ABRIRLO]);
+	} else {
+		debug("Flag is %d", _state->getFlag(FLAG_CABLES_PUESTOS));
+		if (_state->getFlag(FLAG_CABLES_PUESTOS)) {
+			_room->addSticker(19);
+			_room->moveHotspot(_room->findHotspotByIndex(6), 391, 381);
 		}
-		g_system->delayMillis(10);
 	}
-	_state->addInventoryItem(item);
 }
 
-void PelrockEngine::buyFromStore(HotSpot *hotspot, int stickerId) {
-	if(_state->hasInventoryItem(5) == false)
-	{
-		_dialog->say(_res->_ingameTexts[NOTENGODINERO]);
-		return;
-	}
-	else {
-		_room->addSticker(stickerId);
-		_room->disableHotspot(hotspot);
-		_state->addInventoryItem(hotspot->extra);
-		_currentHotspot = nullptr;
-		walkLoop(224, 283, ALFRED_LEFT);
-		_dialog->say(_res->_ingameTexts[CUESTA1000]);
-		_dialog->say(_res->_ingameTexts[AQUITIENE]);
-		_dialog->say(_res->_ingameTexts[MUYBIEN]);
-		_state->removeInventoryItem(5); // Remove 1000 pesetas bill
-	}
-}
+void PelrockEngine::pickCables(HotSpot *hotspot) {
+	// Duck to pick cables
+	_res->loadAlfredSpecialAnim(2);
+	_alfredState.animState = ALFRED_SPECIAL_ANIM;
+	waitForSpecialAnimation();
 
+	// electric shock
+	int prevX = _alfredState.x;
+	_alfredState.x -= 20;
+	_res->loadAlfredSpecialAnim(3);
+	_alfredState.animState = ALFRED_SPECIAL_ANIM;
+	waitForSpecialAnimation();
+	_alfredState.x = prevX;
 
+	// Stand up (reverse of duck)
+	_res->loadAlfredSpecialAnim(2, true);
+	_alfredState.animState = ALFRED_SPECIAL_ANIM;
+	waitForSpecialAnimation();
+	_room->addSticker(21);
 
-void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte rootIndex) {
-	if (actionTrigger == 328) {
-		debug("Disabling root %d in room %d", rootIndex, room);
-		_state->setRootDisabledState(room, rootIndex, true);
-	}
+	_dialog->say(_res->_ingameTexts[RELOJ_HA_CAMBIADO]);
 }
 
 void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
@@ -426,17 +480,6 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	}
 }
 
-void PelrockEngine::noOpAction(HotSpot *hotspot) {
-}
-
-void PelrockEngine::noOpItem(int item, HotSpot *hotspot) {
-	// 154 to 169
-	debug("No-op item %d with hotspot %d", item, hotspot->extra);
-	_alfredState.direction = ALFRED_DOWN;
-	byte response = (byte)getRandomNumber(12);
-	_dialog->say(_res->_ingameTexts[154 + response]);
-}
-
 void PelrockEngine::useOnAlfred(int inventoryObject) {
 
 	debug("Using item %d on Alfred", inventoryObject);
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 488255a310d..b8da093fb03 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -269,6 +269,7 @@ void MenuManager::loadMenu() {
 	readButton(alfred7, alfred7.pos(), _savesDownArrows, _savesDown);
 	readButton(alfred7, 3214046, _questionMark, _questionMarkRect);
 
+	_menuText = _menuTexts[0];
 	alfred7.close();
 }
 
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 85268139a93..3e2a1d88b4a 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -158,7 +158,7 @@ enum TextIndices {
 	TOO_MUCH_CANT_THINK,
 	A_LITTLE_RESPECT,
 	NO_THEY_MAKEYOU_FAT,
-	CLOCK_CHANGED,
+	RELOJ_HA_CAMBIADO,
 	CORRESPONDENCIA_AJENA,
 	ANDA,
 	TUCREES,
@@ -494,6 +494,8 @@ struct AlfredSpecialAnimOffset {
 const AlfredSpecialAnimOffset alfredSpecialAnims[] = {
 	{10, 51, 102, 1, 559685, 1}, // READ BOOK
 	{10, 51, 102, 1, 578943, 1}, // READ RECIPE
+	{3, 45, 87, 0, 37000, 1}, // ELECTRIC SHOCK 1
+	{2, 82, 58, 0, 53106, 20}, // ELECTRIC SHOCK 3
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 0193577b6f6..20904e8fc80 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -161,7 +161,8 @@ void PelrockEngine::init() {
 		gameInitialized = true;
 		loadAnims();
 		// setScreen(0, ALFRED_DOWN);
-		setScreen(15, ALFRED_RIGHT);
+		// setScreen(3, ALFRED_RIGHT);
+		setScreen(4, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
@@ -561,15 +562,13 @@ void PelrockEngine::doAction(VerbIcon action, HotSpot *hotspot) {
 		talkTo(hotspot);
 		break;
 	case PICKUP:
-		_alfredState.setState(ALFRED_INTERACTING);
-		if(_room->isPickableByExtra(hotspot->extra)) {
+		if (_room->isPickableByExtra(hotspot->extra)) {
 			pickUpAndDisable(hotspot);
 		}
 		executeAction(PICKUP, hotspot);
 		break;
 	case OPEN:
 	case CLOSE:
-		_alfredState.setState(ALFRED_INTERACTING);
 	default:
 		executeAction(action, hotspot);
 		break;
@@ -589,7 +588,7 @@ void PelrockEngine::talkTo(HotSpot *hotspot) {
 }
 
 void PelrockEngine::lookAt(HotSpot *hotspot) {
-	debug("Looking at hotspot %d (%d) with extra %d", hotspot->index, hotspot->isSprite? hotspot->index : hotspot->index - _room->_currentRoomAnims.size(), hotspot->extra);
+	debug("Looking at hotspot %d (%d) with extra %d", hotspot->index, hotspot->isSprite ? hotspot->index : hotspot->index - _room->_currentRoomAnims.size(), hotspot->extra);
 	_dialog->sayAlfred(_room->_currentRoomDescriptions[_currentHotspot->index]);
 	_actionPopupState.isActive = false;
 }
@@ -601,7 +600,12 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		_alfredState.idleFrameCounter = 0;
 		_alfredState.setState(ALFRED_COMB);
 	}
+
 	switch (_alfredState.animState) {
+	case ALFRED_IDLE: {
+		drawAlfred(_res->alfredIdle[_alfredState.direction]);
+		break;
+	}
 	case ALFRED_WALKING: {
 		MovementStep step = _currentContext.movementBuffer[_currentStep];
 		if (step.distanceX > 0) {
@@ -641,11 +645,14 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 				if (_currentHotspot != nullptr)
 					_alfredState.direction = calculateAlfredsDirection(_currentHotspot);
 				drawAlfred(_res->alfredIdle[_alfredState.direction]);
-
 				if (_queuedAction.isQueued) {
-					_queuedAction.isQueued = false;
-					doAction(_queuedAction.verb, &_room->_currentRoomHotspots[_queuedAction.hotspotIndex]);
-					break;
+					// look and talk execute immediately, others need interaction animation first
+					if (_queuedAction.verb == TALK || _queuedAction.verb == LOOK) {
+						_queuedAction.isQueued = false;
+						doAction(_queuedAction.verb, &_room->_currentRoomHotspots[_queuedAction.hotspotIndex]);
+						break;
+					}
+					_alfredState.setState(ALFRED_INTERACTING);
 				}
 			}
 		} else {
@@ -671,60 +678,74 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		break;
 	}
 	case ALFRED_TALKING: {
+		drawAlfred(_res->alfredTalkFrames[_alfredState.direction][_alfredState.curFrame]);
+		if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0)
+			_alfredState.curFrame++;
 		if (_alfredState.curFrame >= talkingAnimLengths[_alfredState.direction] - 1) {
 			_alfredState.curFrame = 0;
 		}
-		drawAlfred(_res->alfredTalkFrames[_alfredState.direction][_alfredState.curFrame]);
-		_alfredState.curFrame++;
-		// }
 		break;
 	}
 	case ALFRED_COMB: {
+
+		drawAlfred(_res->alfredCombFrames[_alfredState.direction][_alfredState.curFrame]);
+		_alfredState.curFrame++;
 		if (_alfredState.curFrame >= 11) {
 			_alfredState.setState(ALFRED_IDLE);
-			drawAlfred(_res->alfredIdle[_alfredState.direction]);
-		} else {
-			drawAlfred(_res->alfredCombFrames[_alfredState.direction][_alfredState.curFrame]);
-			_alfredState.curFrame++;
 		}
 		break;
 	}
 	case ALFRED_INTERACTING: {
+		drawAlfred(_res->alfredInteractFrames[_alfredState.direction][_alfredState.curFrame]);
+		_alfredState.curFrame++;
 		if (_alfredState.curFrame >= interactingAnimLength) {
+			if (_queuedAction.isQueued) {
+				_queuedAction.isQueued = false;
+				doAction(_queuedAction.verb, &_room->_currentRoomHotspots[_queuedAction.hotspotIndex]);
+				break;
+			}
 			_alfredState.setState(ALFRED_IDLE);
-		} else {
-			drawAlfred(_res->alfredInteractFrames[_alfredState.direction][_alfredState.curFrame]);
-			_alfredState.curFrame++;
 		}
 		break;
 	}
 	case ALFRED_SPECIAL_ANIM: {
-		if (_res->_specialAnimCurFrame >= _res->_curSpecialAnim.numFrames) {
-			if (_res->_speciaAnimLoopCount < _res->_curSpecialAnim.loops) {
-				_res->_speciaAnimLoopCount++;
-				_res->_specialAnimCurFrame = 0;
-			} else {
-				_res->clearSpecialAnim();
-				_alfredState.setState(ALFRED_IDLE);
-				_res->_isSpecialAnimFinished = true;
-			}
-		} else {
-			byte *frame = new byte[_res->_curSpecialAnim.stride * _res->_curSpecialAnim.numFrames];
-			extractSingleFrame(_res->_specialAnimData,
-							   frame,
-							   _res->_specialAnimCurFrame,
-							   _res->_curSpecialAnim.w,
-							   _res->_curSpecialAnim.h);
+
+		byte *frame = new byte[_res->_currentSpecialAnim->stride * _res->_currentSpecialAnim->numFrames];
+		debug("Drawing special anim frame %d/%d", _res->_currentSpecialAnim->curFrame, _res->_currentSpecialAnim->numFrames);
+		extractSingleFrame(_res->_currentSpecialAnim->animData,
+						   frame,
+						   _res->_currentSpecialAnim->curFrame,
+						   _res->_currentSpecialAnim->w,
+						   _res->_currentSpecialAnim->h);
+		if (_res->_currentSpecialAnim->w == kAlfredFrameWidth && _res->_currentSpecialAnim->h == kAlfredFrameHeight) {
 			drawAlfred(frame);
-			_res->_specialAnimCurFrame++;
-			delete[] frame;
+		} else {
+			// Scale special anim frame to Alfred size before drawing
+			drawSpriteToBuffer(_compositeBuffer, 640, frame, _alfredState.x, _alfredState.y - _res->_currentSpecialAnim->h, _res->_currentSpecialAnim->w, _res->_currentSpecialAnim->h, 255);
 		}
+		if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0) {
+			_res->_currentSpecialAnim->curFrame++;
+
+			if (_res->_currentSpecialAnim->curFrame >= _res->_currentSpecialAnim->numFrames) {
+				if (_res->_currentSpecialAnim->curLoop < _res->_currentSpecialAnim->loopCount - 1) {
+					_res->_currentSpecialAnim->curLoop++;
+					_res->_currentSpecialAnim->curFrame = 0;
+				} else {
+					_alfredState.setState(ALFRED_IDLE);
+					_res->clearSpecialAnim();
+					_res->_isSpecialAnimFinished = true;
+				}
+			}
+		}
+
+		delete[] frame;
+		break;
 	}
 	}
-	// This if is needed to draw Alfred when idle, when the switch case results in a state change
-	if (_alfredState.animState == ALFRED_IDLE) {
-		drawAlfred(_res->alfredIdle[_alfredState.direction]);
-	}
+	// // This if is needed to draw Alfred when idle, when the switch case results in a state change
+	// if (_alfredState.animState == ALFRED_IDLE) {
+	// 	drawAlfred(_res->alfredIdle[_alfredState.direction]);
+	// }
 }
 
 /**
@@ -734,6 +755,10 @@ void PelrockEngine::drawAlfred(byte *buf) {
 
 	ScaleCalculation scale = calculateScaling(_alfredState.y, _room->_scaleParams);
 
+	// Update Alfred's scale state for use by other functions
+	_alfredState.scaledX = scale.scaleX;
+	_alfredState.scaledY = scale.scaleY;
+
 	// Use the pre-calculated scaled dimensions from calculateScaling
 	int finalHeight = scale.scaledHeight;
 	int finalWidth = scale.scaledWidth;
@@ -745,7 +770,8 @@ void PelrockEngine::drawAlfred(byte *buf) {
 		finalWidth = 1;
 	}
 
-	int scaleIndex = finalHeight - 1;
+	// The scaling table is indexed by how many scanlines to skip (scaleY), not by final height
+	int scaleIndex = scale.scaleY;
 	if (scaleIndex >= (int)_heightScalingTable.size()) {
 		scaleIndex = _heightScalingTable.size() - 1;
 	}
@@ -916,6 +942,7 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 }
 
 void PelrockEngine::checkLongMouseClick(int x, int y) {
+	_alfredState.idleFrameCounter = 0;
 	int hotspotIndex = isHotspotUnder(x, y);
 	bool alfredUnder = isAlfredUnder(x, y);
 	if ((hotspotIndex != -1 || alfredUnder) && !_actionPopupState.isActive) {
@@ -933,12 +960,11 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 		}
 		_actionPopupState.isActive = true;
 		_actionPopupState.curFrame = 0;
-
+		debug("Setting alfred under popup: %d", alfredUnder);
+		_actionPopupState.isAlfredUnder = alfredUnder;
 		if (hotspotIndex != -1) {
 			_currentHotspot = &_room->_currentRoomHotspots[hotspotIndex];
-		}
-		else {
-			_actionPopupState.isAlfredUnder = alfredUnder;
+		} else {
 			_currentHotspot = nullptr;
 		}
 	}
@@ -1090,9 +1116,10 @@ int PelrockEngine::isHotspotUnder(int x, int y) {
 					}
 				}
 				bool spriteUnder = isSpriteUnder(sprite, x, y);
-				if(spriteUnder)
+				if (spriteUnder)
 					return i;
-				else continue;
+				else
+					continue;
 			}
 		}
 	}
@@ -1230,7 +1257,7 @@ void PelrockEngine::walkLoop(int16 x, int16 y, AlfredDirection direction) {
 
 	_alfredState.direction = direction;
 	walkTo(x, y);
-	while(!shouldQuit() && _alfredState.animState == ALFRED_WALKING) {
+	while (!shouldQuit() && _alfredState.animState == ALFRED_WALKING) {
 		_events->pollEvent();
 		renderScene();
 		_screen->update();
@@ -1326,11 +1353,11 @@ bool PelrockEngine::isItemUnder(int x, int y) {
 }
 
 bool PelrockEngine::isAlfredUnder(int x, int y) {
-	// TODO: Account for scaling
 	int alfredX = _alfredState.x;
 	int alfredY = _alfredState.y;
-	int alfredW = kAlfredFrameWidth;
-	int alfredH = kAlfredFrameHeight;
+	// Use scaled dimensions (width - scaleX, height - scaleY)
+	int alfredW = kAlfredFrameWidth - _alfredState.scaledX;
+	int alfredH = kAlfredFrameHeight - _alfredState.scaledY;
 
 	if (alfredY - alfredH > y || alfredY < y || alfredX > x || alfredX + alfredW < x) {
 		return false;
@@ -1343,7 +1370,7 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 	_queuedAction = QueuedAction{NO_ACTION, -1, false};
 	_actionPopupState.isActive = false;
 	_currentHotspot = nullptr;
-
+	_alfredState.idleFrameCounter = 0;
 	int hotspotIndex = isHotspotUnder(_events->_mouseX, _events->_mouseY);
 	bool isHotspotUnder = false;
 	if (hotspotIndex != -1) {
@@ -1457,7 +1484,6 @@ void PelrockEngine::setScreen(int roomNumber, AlfredDirection dir) {
 	_screen->markAllDirty();
 	_screen->update();
 
-
 	doExtraActions(roomNumber);
 	roomFile.close();
 	delete[] background;
@@ -1492,14 +1518,14 @@ void PelrockEngine::waitForSpecialAnimation() {
 void PelrockEngine::doExtraActions(int roomNumber) {
 	switch (roomNumber) {
 	case 4:
-		if (_state->flagIsSet(FLAG_PUESTA_SALSA_PICANTE) && !_state->flagIsSet(FLAG_JEFE_ENCARCELADO)) {
+		if (_state->getFlag(FLAG_PUESTA_SALSA_PICANTE) && !_state->getFlag(FLAG_JEFE_ENCARCELADO)) {
 			_state->setFlag(FLAG_JEFE_ENCARCELADO, true);
 			_room->disableSprite(13, 0, true);
 			loadExtraScreenAndPresent(4);
 		}
 		break;
 	case 15:
-		if(_state->flagIsSet(FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ)) {
+		if (_state->getFlag(FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ)) {
 			_state->setFlag(FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ, false);
 			_dialog->say(_res->_ingameTexts[GAMBERROS]);
 			_dialog->say(_res->_ingameTexts[QUIENYO]);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 8350fb76227..1d9970cab01 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -273,6 +273,9 @@ public:
 	void pickHat(HotSpot *hotspot);
 	void pickCord(HotSpot *hotspot);
 	void pickAmulet(HotSpot *hotspot);
+	void openPlug(HotSpot *hotspot);
+	void useCordWithPlug(int inventoryObject, HotSpot *hotspot);
+	void pickCables(HotSpot *hotspot);
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
 };
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index eda93599d47..ff01637d04b 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -217,28 +217,46 @@ void ResourceManager::loadAlfredAnims() {
 	free(alfredCombLeftRaw);
 }
 
-void ResourceManager::loadAlfredSpecialAnim(int numAnim) {
-	_curSpecialAnim = alfredSpecialAnims[numAnim];
+void ResourceManager::loadAlfredSpecialAnim(int numAnim, bool reverse) {
+	AlfredSpecialAnimOffset offset = alfredSpecialAnims[numAnim];
 	Common::File alfred7;
 	if (!alfred7.open(Common::Path("ALFRED.7"))) {
 		error("Could not open ALFRED.7");
 		return;
 	}
 
-	alfred7.seek(_curSpecialAnim.offset, SEEK_SET);
-	_specialAnimData = new byte[_curSpecialAnim.numFrames * _curSpecialAnim.w * _curSpecialAnim.h];
-	debug("Special anim buffer size: %d", _curSpecialAnim.numFrames * _curSpecialAnim.w * _curSpecialAnim.h);
-	mergeRleBlocks(&alfred7, _curSpecialAnim.offset, _curSpecialAnim.numBudas, _specialAnimData);
-	_specialAnimCurFrame = 0;
+	alfred7.seek(offset.offset, SEEK_SET);
+	if(_currentSpecialAnim)
+		delete _currentSpecialAnim;
+	_currentSpecialAnim = new AlfredSpecialAnim(offset.numFrames, offset.w, offset.h, offset.numBudas, offset.offset, offset.loops);
+	_currentSpecialAnim->animData = new byte[offset.numFrames * offset.w * offset.h];
+	if(offset.numBudas > 0) {
+		mergeRleBlocks(&alfred7, offset.offset, offset.numBudas, _currentSpecialAnim->animData);
+	}
+	else {
+		alfred7.read(_currentSpecialAnim->animData, offset.numFrames * offset.w * offset.h);
+	}
+	if(reverse) {
+		// reverse frames for testing
+		byte *reversedData = new byte[offset.numFrames * offset.w * offset.h];
+		for(int i = 0; i < offset.numFrames; i++) {
+			extractSingleFrame(_currentSpecialAnim->animData,
+							   &reversedData[i * offset.w * offset.h],
+							   offset.numFrames - 1 - i,
+							   offset.w,
+							   offset.h);
+		}
+		delete[] _currentSpecialAnim->animData;
+		_currentSpecialAnim->animData = reversedData;
+	}
+
 	_isSpecialAnimFinished = false;
 	alfred7.close();
 }
 
 void ResourceManager::clearSpecialAnim() {
-	delete[] _specialAnimData;
-	_specialAnimData = nullptr;
-	_specialAnimCurFrame = 0;
-	_speciaAnimLoopCount = 0;
+	delete _currentSpecialAnim;
+	_currentSpecialAnim = nullptr;
 }
 
 void ResourceManager::loadInventoryItems() {
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index 3b1690ed34e..f91386e2f37 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -45,7 +45,7 @@ public:
 	void loadCursors();
 	void loadInteractionIcons();
 	void loadAlfredAnims();
-	void loadAlfredSpecialAnim(int numAnim);
+	void loadAlfredSpecialAnim(int numAnim, bool reverse = false);
 	void clearSpecialAnim();
 	void loadInventoryItems();
 	void loadAlfredResponses();
@@ -71,10 +71,11 @@ public:
 	Common::Array<Common::StringArray> _ingameTexts;
 
 	// Special anims
-	byte *_specialAnimData = nullptr;
-	int _specialAnimCurFrame = 0;
-	int _speciaAnimLoopCount = 0;
-	AlfredSpecialAnimOffset _curSpecialAnim;
+	AlfredSpecialAnim *_currentSpecialAnim = nullptr;
+	// byte *_specialAnimData = nullptr;
+	// int _specialAnimCurFrame = 0;
+	// int _speciaAnimLoopCount = 0;
+	// AlfredSpecialAnimOffset _curSpecialAnim;
 	bool _isSpecialAnimFinished = false;
 };
 
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 04d86740812..61981dac738 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -215,6 +215,7 @@ Sprite *RoomManager::findSpriteByIndex(byte index) {
 HotSpot *RoomManager::findHotspotByIndex(byte index) {
 	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
 		if (!_currentRoomHotspots[i].isSprite && _currentRoomHotspots[i].innerIndex == index) {
+			debug("Found hotspot %d at index %d, extra = %d", index, i, _currentRoomHotspots[i].extra);
 			return &_currentRoomHotspots[i];
 		}
 	}
@@ -379,6 +380,7 @@ Common::Array<HotSpot> RoomManager::loadHotspots(byte *data, size_t size) {
 			// if the hotspot has been changed, load the changed version
 			for (int j = 0; j < g_engine->_state->roomHotSpotChanges[_currentRoomNumber].size(); j++) {
 				if (g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspotIndex == spot.innerIndex) {
+					debug("Hotspot %d has been changed, loading changed version, Hotspot x=%d, y = %d, extra = %d", spot.innerIndex, g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot.x, g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot.y, g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot.extra);
 					hotspots.push_back(g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot);
 					isChanged = true;
 					break;
@@ -394,7 +396,7 @@ Common::Array<HotSpot> RoomManager::loadHotspots(byte *data, size_t size) {
 		spot.h = data[hotspotOffset + 6];
 		spot.isSprite = false;
 		spot.extra = READ_LE_INT16(data + hotspotOffset + 7);
-		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d, isEnabled=%d", spot.innerIndex, spot.actionFlags, spot.x, spot.y, spot.w, spot.h, spot.extra, spot.isEnabled);
+		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d, index =%d, isEnabled=%d", spot.innerIndex, spot.actionFlags, spot.x, spot.y, spot.w, spot.h, spot.extra, spot.innerIndex, spot.isEnabled);
 		hotspots.push_back(spot);
 	}
 
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index eb624d32b35..e16941f4d9a 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -92,7 +92,8 @@ public:
 	void applyDisabledChoice(ResetEntry entry, byte *conversationData, size_t conversationDataSize);
 	void addDisabledChoice(ChoiceOption choice);
 	bool isPickableByExtra(uint16 extra)  {
-		for(int i = 0; i < sizeof(unpickableHotspotExtras); i++) {
+		int size = sizeof(unpickableHotspotExtras) / sizeof(unpickableHotspotExtras[0]);
+		for(int i = 0; i < size; i++) {
 			if (extra == unpickableHotspotExtras[i])
 				return false;
 		}
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index a307e46697e..65b9ea944b4 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -71,6 +71,8 @@ const int kAlfredFrameHeight = 102;
 const int kChoiceHeight = 16; // Height of each choice line in pixels
 
 const int kTalkAnimationSpeed = 2; // Frames per update
+const int kAlfredAnimationSpeed = 2; // Frames per update
+
 
 const int kAlfredIdleAnimationFrameCount = 300;
 
@@ -113,6 +115,27 @@ enum AlfredDirection {
 	ALFRED_UP = 3
 };
 
+struct AlfredSpecialAnim {
+	byte *animData = nullptr;
+	int w = 0;
+	int h = 0;
+	int numFrames = 0;
+	int loopCount = 0;
+	uint32 stride = 0;
+	int curFrame = 0;
+	int curLoop = 0;
+	AlfredSpecialAnim(int nF, int width, int height, int nBudas, uint32 off, int loopCount)
+		: numFrames(nF), w(width), h(height), loopCount(loopCount) {
+		stride = w * h;
+	}
+	~AlfredSpecialAnim() {
+		if (animData) {
+			delete[] animData;
+			animData = nullptr;
+		}
+	}
+};
+
 struct ActionPopupState {
 	bool isActive = false;
 	int curFrame = 0;
@@ -433,19 +456,18 @@ struct ResetEntry {
 #define FLAG_HE_TIRADO_PIEDRA 44
 #define FLAG_HA_USADO_AGUA 45
 #define FLAG_TIENDA_ABIERTA 46
+#define FLAG_NUMERO_DE_COPAS 47
+#define FLAG_INGREDIENTES_CONSEGUIDOS 48
 
-const int kNumGameFlags = 47;
+const int kNumGameFlags = 49;
 
 struct GameStateData {
-	bool flags[kNumGameFlags];
-
-	byte NUMERO_DE_COPAS = false;
-	byte INGREDIENTES_CONSEGUIDOS = 0;
+	byte flags[kNumGameFlags];
 
 	GameState stateGame = INTRO;
 
 	Common::Array<byte> inventoryItems;
-	int16 selectedInventoryItem = 0;
+	int16 selectedInventoryItem = -1;
 
 	Common::HashMap<byte, Common::Array<Sticker>> stickersPerRoom;
 	// Common::HashMap<byte, ResetEntry> roomExitChanges;
@@ -457,6 +479,8 @@ struct GameStateData {
 
 	GameStateData() {
 		memset(conversationRootsState, 0, 4 * 56);
+		for (int i = 0; i < kNumGameFlags; i++)
+			flags[i] = 0;
 		flags[FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ] = true;
 	}
 
@@ -469,13 +493,13 @@ struct GameStateData {
 		disabledBranches[entry.room].push_back(entry);
 	}
 
-	bool flagIsSet(int flagIndex) const {
+	byte getFlag(int flagIndex) const {
 		if (flagIndex < 0 || flagIndex >= kNumGameFlags)
 			return false;
 		return flags[flagIndex];
 	}
 
-	void setFlag(int flagIndex, bool value) {
+	void setFlag(int flagIndex, byte value) {
 		if (flagIndex < 0 || flagIndex >= kNumGameFlags)
 			return;
 		flags[flagIndex] = value;


Commit: fa5ee4b021436bfa67df6dfc003504f06dfdfb97
    https://github.com/scummvm/scummvm/commit/fa5ee4b021436bfa67df6dfc003504f06dfdfb97
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:48+02:00

Commit Message:
PELROCK: Improvements on dialog handling

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index c5233f6275d..9342c10df73 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -355,6 +355,53 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 	return pos;
 }
 
+/**
+ * Check if all sub-branches of the current choice level are exhausted.
+ * Based on Ghidra code at LAB_00018c2d in handle_conversation_tree.
+ *
+ * Returns true if we should disable the current choice, which happens when:
+ * - There are no FB sub-branches at higher indices, OR
+ * - All FB sub-branches at higher indices already have FA (are disabled)
+ *
+ * F1 markers (repeatable) don't block disabling - they never get disabled.
+ */
+bool DialogManager::checkAllSubBranchesExhausted(const byte *data, uint32 dataSize, uint32 startPos, int currentChoiceLevel) {
+	uint32 pos = startPos;
+
+	while (pos < dataSize) {
+		byte b = data[pos];
+
+		// Stop at branch/conversation end markers (0xF5, 0xF7, 0xFE, 0xF4)
+		if (b == CTRL_ALT_END_MARKER_1 || b == CTRL_END_BRANCH ||
+			b == CTRL_ALT_SPEAKER_ROOT || b == CTRL_END_CONVERSATION) {
+			break;
+		}
+
+		// Found FB (one-time choice marker)
+		if (b == CTRL_DIALOGUE_MARKER_ONEOFF && pos + 2 < dataSize) {
+			byte choiceIdx = data[pos + 1];
+
+			// Only check sub-branches (higher index = deeper level)
+			if (choiceIdx > currentChoiceLevel) {
+				// Check if NOT disabled (no FA at pos+2)
+				if (data[pos + 2] != CTRL_DISABLED_CHOICE) {
+					debug("checkAllSubBranchesExhausted: Active FB at pos %u, idx %d (current %d) - NOT exhausted",
+						  pos, choiceIdx, currentChoiceLevel);
+					return false;  // Don't disable parent
+				}
+			} else if (choiceIdx <= currentChoiceLevel) {
+				// Hit choice at same or lower level - stop
+				break;
+			}
+		}
+
+		pos++;
+	}
+
+	debug("checkAllSubBranchesExhausted: All sub-branches exhausted at level %d", currentChoiceLevel);
+	return true;
+}
+
 void DialogManager::setCurSprite(int index) {
 	// Set current sprite based on index
 	if (g_engine->_room == nullptr) {
@@ -373,6 +420,14 @@ void DialogManager::setCurSprite(int index) {
 	_curSprite = nullptr;
 }
 
+bool isRootDisabled(byte room, int root) {
+
+	if(g_engine->_state->getRootDisabledState(room, root)) {
+		return true;
+	}
+	return false;
+}
+
 void DialogManager::startConversation(const byte *conversationData, uint32 dataSize, byte npcIndex, Sprite *animSet) {
 	if (!conversationData || dataSize == 0) {
 		debug("startConversation: No conversation data");
@@ -472,7 +527,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		}
 
 		// Move past control byte
-		if (controlByte == CTRL_END_TEXT || controlByte == CTRL_ACTION_TRIGGER) {
+		if (controlByte == CTRL_END_TEXT) {
 			position++;
 			if (position >= dataSize) {
 				debug("Reached end of data after moving past control byte");
@@ -495,7 +550,8 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		if (peekPos < dataSize &&
 			conversationData[peekPos] != CTRL_DIALOGUE_MARKER &&
 			conversationData[peekPos] != CTRL_DIALOGUE_MARKER_ONEOFF &&
-			conversationData[peekPos] != CTRL_END_CONVERSATION) {
+			conversationData[peekPos] != CTRL_END_CONVERSATION &&
+			conversationData[peekPos] != CTRL_DISABLED_CHOICE) {
 			continue;
 		}
 
@@ -566,8 +622,13 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 
 			if (!choiceText.empty() && choiceText.size() > 1) {
 				displayDialogue(choiceText, ALFRED_COLOR);
+				// Only disable FB choice if all sub-branches are exhausted (Ghidra LAB_00018c2d)
 				if ((*choices)[selectedIndex].shouldDisableOnSelect) {
-					g_engine->_room->addDisabledChoice((*choices)[selectedIndex]);
+					bool shouldDisable = checkAllSubBranchesExhausted(
+						conversationData, dataSize, endPos, currentChoiceLevel);
+					if (shouldDisable) {
+						g_engine->_room->addDisabledChoice((*choices)[selectedIndex]);
+					}
 				}
 			}
 
@@ -686,7 +747,6 @@ int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
 	if (pos < text.size() && isEndMarker(text[pos])) {
 		isEnd = true;
 	}
-	// Count ALL trailing spaces as part of this word
 	if (pos < text.size() && !isEnd) {
 		if (text[pos] == CTRL_ACTION_TRIGGER) { // 0xF8 (-8) special case
 			wordLength += 3;
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index b543de68855..8a47379ccec 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -78,6 +78,7 @@ private:
 	void setCurSprite(int index);
 	void checkMouse();
 	void sayAlfred(Common::StringArray texts);
+	bool checkAllSubBranchesExhausted(const byte *data, uint32 dataSize, uint32 startPos, int currentChoiceLevel);
 
 public:
 	DialogManager(Graphics::Screen *screen, PelrockEventManager *events, GraphicsManager *graphics);


Commit: aeae414a3e39ce76ab5957c47764d09facc9f653
    https://github.com/scummvm/scummvm/commit/aeae414a3e39ce76ab5957c47764d09facc9f653
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:49+02:00

Commit Message:
PELROCK: Room 4 conversation marker

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp
    engines/pelrock/room.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 2af488824fa..383e04ee7b8 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -445,6 +445,10 @@ void PelrockEngine::useCordWithPlug(int inventoryObject, HotSpot *hotspot) {
 }
 
 void PelrockEngine::pickCables(HotSpot *hotspot) {
+	if(_room->hasSticker(21)) {
+		_dialog->say(_res->_ingameTexts[QUELOSCOJA_SUPADRE]);
+		return;
+	}
 	// Duck to pick cables
 	_res->loadAlfredSpecialAnim(2);
 	_alfredState.animState = ALFRED_SPECIAL_ANIM;
@@ -465,6 +469,7 @@ void PelrockEngine::pickCables(HotSpot *hotspot) {
 	_room->addSticker(21);
 
 	_dialog->say(_res->_ingameTexts[RELOJ_HA_CAMBIADO]);
+	_state->setRootDisabledState(4, 0, true);
 }
 
 void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 9342c10df73..020414d7a5a 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -357,13 +357,12 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 
 /**
  * Check if all sub-branches of the current choice level are exhausted.
- * Based on Ghidra code at LAB_00018c2d in handle_conversation_tree.
  *
  * Returns true if we should disable the current choice, which happens when:
  * - There are no FB sub-branches at higher indices, OR
  * - All FB sub-branches at higher indices already have FA (are disabled)
  *
- * F1 markers (repeatable) don't block disabling - they never get disabled.
+ * F1 markers (repeatable) never get disabled.
  */
 bool DialogManager::checkAllSubBranchesExhausted(const byte *data, uint32 dataSize, uint32 startPos, int currentChoiceLevel) {
 	uint32 pos = startPos;
@@ -440,6 +439,9 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 
 	uint32 position = 0;
 	int currentChoiceLevel = -1; // Track the current choice level
+	uint32 lastChoiceMenuPosition = 0; // Track where we last showed a choice menu
+	ChoiceOption lastSelectedChoice; // Track the last choice we selected
+	bool skipToChoices = false; // After F0, skip directly to choice parsing
 
 	// Find the speaker tree for this NPC; they are marked by 0xFE 0xXX where XX is NPC index + 1
 	bool speakerTreeOffsetFound = false;
@@ -476,86 +478,114 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 
 	// OUTER LOOP: Continue until conversation ends
 	while (position < dataSize && !g_engine->shouldQuit()) {
-		// Skip control bytes that should be ignored
+		// Skip control bytes that should be ignored (but handle F0 specially)
 		while (position < dataSize &&
 			   (conversationData[position] == CTRL_ALT_END_MARKER_1 ||
-				conversationData[position] == CTRL_ALT_END_MARKER_2 ||
-				conversationData[position] == CTRL_GO_BACK)) {
+				conversationData[position] == CTRL_ALT_END_MARKER_2)) {
 			position++;
 		}
 
-		if (position >= dataSize) {
-			debug("Reached end of data while skipping control bytes");
-			break;
-		}
+		// Handle F0 "Go Back" - return to the last choice menu position
+		// Disable the choice that led us here before rewinding
+		if (position < dataSize && conversationData[position] == CTRL_GO_BACK) {
+			debug("F0 Go Back at position %u, rewinding to lastChoiceMenuPosition %u", position, lastChoiceMenuPosition);
 
-		// 1. Read and display current dialogue
-		Common::String text;
-		byte speakerId;
-		uint32 endPos = readTextBlock(conversationData, dataSize, position, text, speakerId);
-		Common::Array<Common::Array<Common::String>> wrappedText = wordWrap(text);
-		debug("Word wrapping %s produces %d pages", text.c_str(), wrappedText.size());
-		// Skip spurious single character artifacts
-		if (!text.empty() && text.size() > 1) {
-			displayDialogue(wrappedText, speakerId);
+			if (lastChoiceMenuPosition > 0) {
+				// Disable the choice that led to this F0
+				if (lastSelectedChoice.dataOffset > 0) {
+					debug("F0: Disabling choice that led here at offset %u", lastSelectedChoice.dataOffset);
+					g_engine->_room->addDisabledChoice(lastSelectedChoice);
+				}
+				position = lastChoiceMenuPosition;
+				skipToChoices = true; // Skip directly to choice parsing
+				// Jump directly to choice parsing section
+				// Don't continue - let it fall through
+			} else {
+				debug("F0: No previous choice menu, ending conversation");
+				break;
+			}
 		}
 
-		// Move to end of text
-		position = endPos;
-
-		// 2. Check for end of conversation
 		if (position >= dataSize) {
-			debug("Reached end of data after reading dialogue");
+			debug("Reached end of data while skipping control bytes");
 			break;
 		}
 
-		byte controlByte = conversationData[position];
-
-		if (controlByte == CTRL_END_CONVERSATION) {
-			debug("End of conversation marker found");
-			break;
-		}
+		// 1. Read and display current dialogue (unless skipping to choices)
+		uint32 endPos = position; // Declare outside the if block
+		if (!skipToChoices) {
+			Common::String text;
+			byte speakerId;
+			endPos = readTextBlock(conversationData, dataSize, position, text, speakerId);
+			Common::Array<Common::Array<Common::String>> wrappedText = wordWrap(text);
+			debug("Word wrapping %s produces %d pages", text.c_str(), wrappedText.size());
+			// Skip spurious single character artifacts
+			if (!text.empty() && text.size() > 1) {
+				displayDialogue(wrappedText, speakerId);
+			}
 
-		if (controlByte == CTRL_ACTION_TRIGGER) {
-			uint16 actionCode = conversationData[position + 1] | (conversationData[position + 2] << 8);
-			debug("Action trigger %d encountered!", actionCode);
-			g_engine->dialogActionTrigger(
-				actionCode,
-				g_engine->_room->_currentRoomNumber,
-				currentRoot);
-			break;
+			// Move to end of text
+			position = endPos;
 		}
 
-		// Move past control byte
-		if (controlByte == CTRL_END_TEXT) {
-			position++;
+		// 2. Check for end of conversation (skip if going directly to choices)
+		if (!skipToChoices) {
 			if (position >= dataSize) {
-				debug("Reached end of data after moving past control byte");
+				debug("Reached end of data after reading dialogue");
+				break;
+			}
+
+			byte controlByte = conversationData[position];
+
+			if (controlByte == CTRL_END_CONVERSATION) {
+				debug("End of conversation marker found");
+				break;
+			}
+
+			if (controlByte == CTRL_ACTION_TRIGGER) {
+				uint16 actionCode = conversationData[position + 1] | (conversationData[position + 2] << 8);
+				debug("Action trigger %d encountered!", actionCode);
+				g_engine->dialogActionTrigger(
+					actionCode,
+					g_engine->_room->_currentRoomNumber,
+					currentRoot);
 				break;
 			}
+
+			// Move past control byte
+			if (controlByte == CTRL_END_TEXT) {
+				position++;
+				if (position >= dataSize) {
+					debug("Reached end of data after moving past control byte");
+					break;
+				}
+			}
 		}
 
 		// 3. Before parsing choices, check if we're at a choice marker
 		// Skip control bytes to peek at next meaningful byte
 		uint32 peekPos = position;
-		while (peekPos < dataSize &&
-			   (conversationData[peekPos] == CTRL_ALT_END_MARKER_1 ||
-				conversationData[peekPos] == CTRL_ALT_END_MARKER_2 ||
-				conversationData[peekPos] == CTRL_TEXT_TERMINATOR ||
-				conversationData[peekPos] == CTRL_GO_BACK)) {
-			peekPos++;
-		}
-
-		// If not at a choice marker, there's more dialogue to read
-		if (peekPos < dataSize &&
-			conversationData[peekPos] != CTRL_DIALOGUE_MARKER &&
-			conversationData[peekPos] != CTRL_DIALOGUE_MARKER_ONEOFF &&
-			conversationData[peekPos] != CTRL_END_CONVERSATION &&
-			conversationData[peekPos] != CTRL_DISABLED_CHOICE) {
-			continue;
+		if (!skipToChoices) {
+			while (peekPos < dataSize &&
+				   (conversationData[peekPos] == CTRL_ALT_END_MARKER_1 ||
+					conversationData[peekPos] == CTRL_ALT_END_MARKER_2 ||
+					conversationData[peekPos] == CTRL_TEXT_TERMINATOR)) {
+				peekPos++;
+			}
+
+			// If not at a choice marker, there's more dialogue to read
+			if (peekPos < dataSize &&
+				conversationData[peekPos] != CTRL_DIALOGUE_MARKER &&
+				conversationData[peekPos] != CTRL_DIALOGUE_MARKER_ONEOFF &&
+				conversationData[peekPos] != CTRL_END_CONVERSATION &&
+				conversationData[peekPos] != CTRL_DISABLED_CHOICE) {
+				continue;
+			}
 		}
 
-		// 4. Parse choices
+		// 4. Parse choices - save position for F0 "go back"
+		skipToChoices = false; // Reset flag
+		lastChoiceMenuPosition = position;
 		Common::Array<ChoiceOption> *choices = new Common::Array<ChoiceOption>();
 		parseChoices(conversationData, dataSize, position, choices);
 		debug("Parsed %u choices", choices->size());
@@ -569,19 +599,23 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 			continue;
 		}
 
-		// Check if we have a currentChoiceLevel and if these choices are at the next level
+		// Check if we have a currentChoiceLevel and if these choices are at the expected level
 		if (currentChoiceLevel >= 0) {
-			// We've already made a choice, check if the current choices are at the next level
-			bool foundNextLevel = false;
+			// After F0, we expect choices at the SAME level (currentChoiceLevel)
+			// Otherwise, we expect choices at the NEXT level (currentChoiceLevel + 1)
+			bool foundExpectedLevel = false;
 			for (uint i = 0; i < choices->size(); i++) {
-				if ((*choices)[i].choiceIndex == currentChoiceLevel + 1) {
-					foundNextLevel = true;
+				if ((*choices)[i].choiceIndex == currentChoiceLevel ||
+					(*choices)[i].choiceIndex == currentChoiceLevel + 1) {
+					foundExpectedLevel = true;
+					// Update currentChoiceLevel to match what we found
+					currentChoiceLevel = (*choices)[i].choiceIndex;
 					break;
 				}
 			}
 
-			if (!foundNextLevel) {
-				debug("No choices found at level %d (current is %d), ending conversation", currentChoiceLevel + 1, currentChoiceLevel);
+			if (!foundExpectedLevel) {
+				debug("No choices found at level %d or %d, ending conversation", currentChoiceLevel, currentChoiceLevel + 1);
 				break;
 			}
 		}
@@ -612,6 +646,8 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 
 		// 6. Move position to after the selected choice
 		if (selectedIndex >= 0 && selectedIndex < (int)choices->size()) {
+			// Save this choice in case we hit F0 and need to disable it
+			lastSelectedChoice = (*choices)[selectedIndex];
 			position = (*choices)[selectedIndex].dataOffset;
 			currentChoiceLevel = (*choices)[selectedIndex].choiceIndex;
 
@@ -624,8 +660,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 				displayDialogue(choiceText, ALFRED_COLOR);
 				// Only disable FB choice if all sub-branches are exhausted (Ghidra LAB_00018c2d)
 				if ((*choices)[selectedIndex].shouldDisableOnSelect) {
-					bool shouldDisable = checkAllSubBranchesExhausted(
-						conversationData, dataSize, endPos, currentChoiceLevel);
+					bool shouldDisable = checkAllSubBranchesExhausted(conversationData, dataSize, endPos, currentChoiceLevel);
 					if (shouldDisable) {
 						g_engine->_room->addDisabledChoice((*choices)[selectedIndex]);
 					}
@@ -728,7 +763,7 @@ bool DialogManager::processColorAndTrim(Common::StringArray &lines, byte &speake
 	return false;
 }
 
-bool isEndMarker(char char_byte) {
+bool isEndMarker(unsigned char char_byte) {
 	return char_byte == CTRL_END_TEXT || char_byte == CTRL_END_CONVERSATION || char_byte == CTRL_ACTION_TRIGGER || char_byte == CTRL_GO_BACK;
 }
 
@@ -801,7 +836,7 @@ Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::Str
 			if (trailingSpaces > 0) {
 				currentPage.push_back(lineText);
 				//  current_line = [' ' * trailing_spaces]
-				Common::String currentLine(trailingSpaces, ' ');
+				Common::String current_line(trailingSpaces, ' ');
 				charsRemaining = MAX_CHARS_PER_LINE - trailingSpaces;
 				currentLineNum += 1;
 
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 8a47379ccec..0f8d078af3b 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -50,17 +50,17 @@ namespace Pelrock {
 #define CTRL_ALT_END_MARKER_2 0xEB       /* Alt end marker 2 */
 #define CTRL_ALT_SPEAKER_ROOT 0xFE       /* Separates conversations from different speakers */
 
-static void debugHexString(const Common::String &str, const char *label = nullptr) {
-	if (label) {
-		debug("%s:", label);
-	}
+// static void debugHexString(const Common::String &str, const char *label = nullptr) {
+// 	if (label) {
+// 		debug("%s:", label);
+// 	}
 
-	Common::String hexOutput;
-	for (uint i = 0; i < str.size(); i++) {
-		hexOutput += Common::String::format("%02X ", (unsigned char)str[i]);
-	}
-	debug("%s", hexOutput.c_str());
-}
+// 	Common::String hexOutput;
+// 	for (uint i = 0; i < str.size(); i++) {
+// 		hexOutput += Common::String::format("%02X ", (unsigned char)str[i]);
+// 	}
+// 	debug("%s", hexOutput.c_str());
+// }
 
 class DialogManager {
 private:
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 3e2a1d88b4a..bdd14778c95 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -491,7 +491,7 @@ struct AlfredSpecialAnimOffset {
 	}
 };
 
-const AlfredSpecialAnimOffset alfredSpecialAnims[] = {
+static const AlfredSpecialAnimOffset alfredSpecialAnims[] = {
 	{10, 51, 102, 1, 559685, 1}, // READ BOOK
 	{10, 51, 102, 1, 578943, 1}, // READ RECIPE
 	{3, 45, 87, 0, 37000, 1}, // ELECTRIC SHOCK 1
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 20904e8fc80..28ce2b527de 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -576,7 +576,7 @@ void PelrockEngine::doAction(VerbIcon action, HotSpot *hotspot) {
 }
 
 void PelrockEngine::talkTo(HotSpot *hotspot) {
-	Sprite *animSet;
+	Sprite *animSet = nullptr;
 	for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
 		if (_room->_currentRoomAnims[i].index == hotspot->index) {
 			animSet = &_room->_currentRoomAnims[i];
@@ -696,22 +696,22 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		break;
 	}
 	case ALFRED_INTERACTING: {
+		debug("Alfred interacting frame %d/%d, direction %d", _alfredState.curFrame, interactingAnimLength, _alfredState.direction);
 		drawAlfred(_res->alfredInteractFrames[_alfredState.direction][_alfredState.curFrame]);
 		_alfredState.curFrame++;
 		if (_alfredState.curFrame >= interactingAnimLength) {
 			if (_queuedAction.isQueued) {
 				_queuedAction.isQueued = false;
+				_alfredState.setState(ALFRED_IDLE);
 				doAction(_queuedAction.verb, &_room->_currentRoomHotspots[_queuedAction.hotspotIndex]);
 				break;
 			}
-			_alfredState.setState(ALFRED_IDLE);
 		}
 		break;
 	}
 	case ALFRED_SPECIAL_ANIM: {
 
 		byte *frame = new byte[_res->_currentSpecialAnim->stride * _res->_currentSpecialAnim->numFrames];
-		debug("Drawing special anim frame %d/%d", _res->_currentSpecialAnim->curFrame, _res->_currentSpecialAnim->numFrames);
 		extractSingleFrame(_res->_currentSpecialAnim->animData,
 						   frame,
 						   _res->_currentSpecialAnim->curFrame,
@@ -915,7 +915,6 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 
 	int frameSize = sprite->stride;
 	int curFrame = animData.curFrame;
-	byte *frame = new byte[frameSize];
 	drawSpriteToBuffer(_compositeBuffer, 640, animData.animData[curFrame], x, y, w, h, 255);
 
 	// Original in the game: increment FIRST, then check (not check-then-increment)
@@ -1142,7 +1141,6 @@ Exit *PelrockEngine::isExitUnder(int x, int y) {
  */
 bool PelrockEngine::isSpriteUnder(Sprite *sprite, int x, int y) {
 	Anim &animData = sprite->animData[sprite->curAnimIndex];
-	int frameSize = animData.w * animData.h;
 	int curFrame = animData.curFrame;
 
 	int localX = x - animData.x;
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 61981dac738..9e7a1e7efd5 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -434,7 +434,6 @@ void RoomManager::resetConversationStates(byte roomNumber, byte *conversationDat
 
 void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 
-	uint32_t outPos = 0;
 	_roomStickers.clear();
 	_currentRoomNumber = roomNumber;
 	int roomOffset = roomNumber * kRoomStructSize;
@@ -500,10 +499,6 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 		drawRect(g_engine->_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 200 + i);
 	}
 
-	for (int i = 0; i < _currentRoomExits.size(); i++) {
-		Exit exit = _currentRoomExits[i];
-	}
-
 	PaletteAnim *anim = getPaletteAnimForRoom(roomNumber);
 	if (anim != nullptr) {
 		if (_currentPaletteAnim != nullptr) {
@@ -595,8 +590,8 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 		sprite.spriteType = data[animOffset + 33];
 		sprite.actionFlags = data[animOffset + 34];
 		sprite.isHotspotDisabled = data[animOffset + 38];
-		for (int i = 0; i < disabledSprites.size(); i++) {
-			if (disabledSprites[i] == sprite.index) {
+		for (int j = 0; j < disabledSprites.size(); j++) {
+			if (disabledSprites[j] == sprite.index) {
 				sprite.zOrder = 255;
 				sprite.isHotspotDisabled = 1;
 				break;
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index e16941f4d9a..3feb2b25913 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -43,7 +43,8 @@ static const int unpickableHotspotExtras[] = {
 	73,
 	74,
 	6,
-	7
+	7,
+	316, // wires
 };
 
 class RoomManager {


Commit: bc0118e1717cca86458d71898dcadeb20687b480
    https://github.com/scummvm/scummvm/commit/bc0118e1717cca86458d71898dcadeb20687b480
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:49+02:00

Commit Message:
PELROCK: Implements computer on room 9

Changed paths:
  A engines/pelrock/computer.cpp
  A engines/pelrock/computer.h
  A engines/pelrock/library_books.h
    engines/pelrock/actions.cpp
    engines/pelrock/graphics.cpp
    engines/pelrock/graphics.h
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h
    engines/pelrock/module.mk
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h
    engines/pelrock/util.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 383e04ee7b8..1b18c8f46d1 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -482,6 +482,13 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 		_screen->markAllDirty();
 		_screen->update();
 		break;
+	case 271:
+		_dialog->say(_res->_ingameTexts[TRABAJARIA_MEJOR_SI_NO_ME_MOLESTARA]);
+		break;
+	case 270:
+		// loadExtraScreenAndPresent(9);
+		_state->stateGame = COMPUTER;
+		break;
 	}
 }
 
diff --git a/engines/pelrock/computer.cpp b/engines/pelrock/computer.cpp
new file mode 100644
index 00000000000..3b60c673edf
--- /dev/null
+++ b/engines/pelrock/computer.cpp
@@ -0,0 +1,294 @@
+/* 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 "common/events.h"
+#include "common/system.h"
+#include "graphics/paletteman.h"
+
+#include "pelrock/computer.h"
+#include "pelrock/library_books.h"
+#include "pelrock/pelrock.h"
+
+namespace Pelrock {
+
+Computer::Computer(PelrockEventManager *eventMan)
+	: _backgroundScreen(nullptr),
+	  _palette(nullptr),
+	  _state(STATE_MAIN_MENU),
+	  _searchLetter(0),
+	  _searchType(0),
+	  _currentResult(0),
+	  _memorizedBookIndex(-1),
+	  _events(eventMan) {
+
+	// Initialize UI strings (Spanish - original game language)
+	_menuTitle = "MENU PRINCIPAL";
+	_menuOption1 = "1    CONSULTAR POR TITULO";
+	_menuOption2 = "2    CONSULTAR POR AUTOR";
+	_menuOption3 = "3    CANCELAR";
+	_promptLetter = "Teclea una letra (A-Z):";
+	_labelTitle = "Titulo    : ";
+	_labelAuthor = "Autor     : ";
+	_labelGenre = "Genero    : ";
+	_labelSituacion = "Situacion : ";
+	_statusPhysical = "Estante %c, fila %d";
+	_statusCatalogOnly = "Solo en catalogo";
+	_optMemorizar = "(M)emorizar";
+	_optSeguir = "(S)eguir";
+	_optCancelar = "(C)ancelar";
+	_noResults = "No se encontraron libros";
+	_memorizedMsg = "Bueno... Tendre que buscar en la estanteria de la %c";
+}
+
+Computer::~Computer() {
+	cleanup();
+}
+
+void Computer::loadBackground() {
+	_palette = new byte[768];
+	_backgroundScreen = new byte[640 * 400];
+
+	g_engine->_res->getExtraScreen(1, _backgroundScreen, _palette);
+	g_system->getPaletteManager()->setPalette(_palette, 0, 256);
+}
+
+void Computer::cleanup() {
+	if (_backgroundScreen) {
+		delete[] _backgroundScreen;
+		_backgroundScreen = nullptr;
+	}
+	if (_palette) {
+		// Restore room palette
+		g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
+		delete[] _palette;
+		_palette = nullptr;
+	}
+	g_engine->_screen->markAllDirty();
+	g_engine->_screen->update();
+}
+
+int Computer::run() {
+	loadBackground();
+	_state = STATE_MAIN_MENU;
+
+	while (!g_engine->shouldQuit() && _state != STATE_EXIT) {
+		_events->pollEvent();
+		drawScreen();
+
+		switch (_state) {
+		case STATE_MAIN_MENU:
+			handleMainMenu();
+			break;
+
+		case STATE_SEARCH_BY_TITLE:
+		case STATE_SEARCH_BY_AUTHOR:
+			handleSearchInput();
+			break;
+
+		case STATE_SHOW_RESULTS:
+			handleResultsDisplay();
+			break;
+
+		case STATE_EXIT:
+			break;
+		}
+
+		g_engine->_screen->markAllDirty();
+		g_engine->_screen->update();
+		g_system->delayMillis(10);
+	}
+	cleanup();
+	return _memorizedBookIndex;
+}
+
+void Computer::drawScreen() {
+	// Clear to background
+	memcpy(g_engine->_screen->getPixels(), _backgroundScreen, 640 * 400);
+
+	int textY = 100;
+	int textX = 180;
+
+	switch (_state) {
+	case STATE_MAIN_MENU:
+		g_engine->_smallFont->drawString(g_engine->_screen, _menuTitle, textX, textY, 280, 15, Graphics::kTextAlignCenter);
+		g_engine->_smallFont->drawString(g_engine->_screen, _menuOption1, textX, textY + 40, 280, 14);
+		g_engine->_smallFont->drawString(g_engine->_screen, _menuOption2, textX, textY + 60, 280, 14);
+		g_engine->_smallFont->drawString(g_engine->_screen, _menuOption3, textX, textY + 80, 280, 14);
+		break;
+
+	case STATE_SEARCH_BY_TITLE:
+	case STATE_SEARCH_BY_AUTHOR:
+		g_engine->_smallFont->drawString(g_engine->_screen,
+			_searchType == 0 ? "CONSULTAR POR TITULO" : "CONSULTAR POR AUTOR",
+			textX, textY, 280, 15, Graphics::kTextAlignCenter);
+		g_engine->_smallFont->drawString(g_engine->_screen, _promptLetter, textX, textY + 40, 280, 14);
+		break;
+
+	case STATE_SHOW_RESULTS:
+		{
+			Common::String header = Common::String::format(
+				"Consulta de %s, letra %c",
+				_searchType == 0 ? "TITULO" : "AUTOR",
+				_searchLetter);
+			g_engine->_smallFont->drawString(g_engine->_screen, header, textX, textY, 280, 15, Graphics::kTextAlignCenter);
+
+			if (_searchResults.empty()) {
+				g_engine->_smallFont->drawString(g_engine->_screen, _noResults, textX, textY + 50, 280, 14);
+			} else {
+				// Display current book
+				int bookIdx = _searchResults[_currentResult];
+				const LibraryBook &book = kLibraryBooks[bookIdx];
+
+				// Title (may be long, truncate if needed)
+				Common::String titleLine = Common::String::format("%s%s", _labelTitle, book.title);
+				g_engine->_smallFont->drawString(g_engine->_screen, titleLine, textX - 50, textY + 40, 340, 14);
+
+				// Author
+				Common::String authorLine = Common::String::format("%s%s", _labelAuthor, book.author);
+				g_engine->_smallFont->drawString(g_engine->_screen, authorLine, textX - 50, textY + 60, 340, 14);
+
+				// Genre
+				Common::String genreLine = Common::String::format("%s%s", _labelGenre, book.genre);
+				g_engine->_smallFont->drawString(g_engine->_screen, genreLine, textX - 50, textY + 80, 340, 14);
+
+				// Situacion (location/availability)
+				Common::String situacionLine;
+				if (book.available) {
+					situacionLine = Common::String::format("%sEstante %c, fila %d",
+						_labelSituacion, book.shelfLetter, book.shelfRow);
+				} else {
+					situacionLine = Common::String::format("%s%s",
+						_labelSituacion, _statusCatalogOnly);
+				}
+				g_engine->_smallFont->drawString(g_engine->_screen, situacionLine, textX - 50, textY + 100, 340,
+					book.available ? 10 : 8); // Green if physical, gray if catalog-only
+
+				// Show result counter
+				Common::String counter = Common::String::format("Libro %d de %d",
+					_currentResult + 1, (int)_searchResults.size());
+				g_engine->_smallFont->drawString(g_engine->_screen, counter, textX, textY + 130, 280, 14, Graphics::kTextAlignCenter);
+
+				// Show navigation options
+				Common::String navOptions;
+				if (book.available) {
+					navOptions = Common::String::format("%s   %s   %s", _optMemorizar, _optSeguir, _optCancelar);
+				} else {
+					navOptions = Common::String::format("%s   %s", _optSeguir, _optCancelar);
+				}
+				g_engine->_smallFont->drawString(g_engine->_screen, navOptions, textX, textY + 160, 280, 8, Graphics::kTextAlignCenter);
+			}
+		}
+		break;
+
+	case STATE_EXIT:
+		break;
+	}
+}
+
+void Computer::handleMainMenu() {
+	if (_events->_lastKeyEvent == Common::KEYCODE_1) {
+		_searchType = 0;
+		_state = STATE_SEARCH_BY_TITLE;
+		_events->_lastKeyEvent = Common::KEYCODE_INVALID;
+	} else if (_events->_lastKeyEvent == Common::KEYCODE_2) {
+		_searchType = 1;
+		_state = STATE_SEARCH_BY_AUTHOR;
+		_events->_lastKeyEvent = Common::KEYCODE_INVALID;
+	} else if (_events->_lastKeyEvent == Common::KEYCODE_3) {
+		_state = STATE_EXIT;
+	}
+}
+
+void Computer::handleSearchInput() {
+	if (_events->_lastKeyEvent >= Common::KEYCODE_a &&
+	    _events->_lastKeyEvent <= Common::KEYCODE_z) {
+		_searchLetter = 'A' + (_events->_lastKeyEvent - Common::KEYCODE_a);
+		performSearch();
+		_currentResult = 0;
+		_state = STATE_SHOW_RESULTS;
+		_events->_lastKeyEvent = Common::KEYCODE_INVALID;
+	} else if (_events->_lastKeyEvent == Common::KEYCODE_ESCAPE) {
+		_state = STATE_MAIN_MENU;
+		_events->_lastKeyEvent = Common::KEYCODE_INVALID;
+	}
+}
+
+void Computer::handleResultsDisplay() {
+	if (_events->_lastKeyEvent == Common::KEYCODE_s) {
+		if (!_searchResults.empty()) {
+			_currentResult = (_currentResult + 1) % _searchResults.size();
+		}
+		_events->_lastKeyEvent = Common::KEYCODE_INVALID;
+	}
+	else if (_events->_lastKeyEvent == Common::KEYCODE_m) {
+		if (!_searchResults.empty()) {
+			int bookIdx = _searchResults[_currentResult];
+			const LibraryBook &book = kLibraryBooks[bookIdx];
+			if (book.available) {
+				memorizeBook(bookIdx);
+			}
+		}
+		_events->_lastKeyEvent = Common::KEYCODE_INVALID;
+	}
+	// C key or ESC - Cancel (return to main menu)
+	else if (_events->_lastKeyEvent == Common::KEYCODE_c ||
+	         _events->_lastKeyEvent == Common::KEYCODE_ESCAPE) {
+		_state = STATE_MAIN_MENU;
+		_events->_lastKeyEvent = Common::KEYCODE_INVALID;
+	}
+}
+
+void Computer::memorizeBook(int bookIndex) {
+	const LibraryBook &book = kLibraryBooks[bookIndex];
+
+	// Store the memorized book for later pickup from the shelf
+	_memorizedBookIndex = bookIndex;
+
+	// In the original game, Alfred says "Bueno... Tendre que buscar en la estanteria de la X"
+	// where X is the shelf letter
+	// TODO: Play the dialog and set a game flag so the book can be picked up from the shelf
+
+	// For now, just exit the computer interface
+	// The game state should be updated to allow picking up this book from shelf X
+	_state = STATE_EXIT;
+
+	debug(1, "Memorized book '%s' at shelf %c, row %d", book.title, book.shelfLetter, book.shelfRow);
+}
+
+void Computer::performSearch() {
+	_searchResults.clear();
+
+	for (int i = 0; i < kLibraryBookCount; i++) {
+		const char *searchField = _searchType == 0 ?
+			kLibraryBooks[i].title : kLibraryBooks[i].author;
+
+		// Check if first letter matches (case-insensitive)
+		char firstChar = searchField[0];
+		if (firstChar >= 'a' && firstChar <= 'z')
+			firstChar = firstChar - 'a' + 'A';
+
+		if (firstChar == _searchLetter) {
+			_searchResults.push_back(i);
+		}
+	}
+}
+
+} // End of namespace Pelrock
diff --git a/engines/pelrock/computer.h b/engines/pelrock/computer.h
new file mode 100644
index 00000000000..a9c3f7e1e00
--- /dev/null
+++ b/engines/pelrock/computer.h
@@ -0,0 +1,94 @@
+/* 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 PELROCK_COMPUTER_H
+#define PELROCK_COMPUTER_H
+
+#include "common/array.h"
+#include "common/str.h"
+
+#include "pelrock/events.h"
+
+namespace Pelrock {
+
+class PelrockEngine;
+
+class Computer {
+public:
+	Computer(PelrockEventManager *eventMan);
+	~Computer();
+
+	/**
+	 * @return Book index if a book was memorized, -1 otherwise
+	 */
+	int run();
+
+private:
+	enum ComputerState {
+		STATE_MAIN_MENU,
+		STATE_SEARCH_BY_TITLE,
+		STATE_SEARCH_BY_AUTHOR,
+		STATE_SHOW_RESULTS,
+		STATE_EXIT
+	};
+
+	PelrockEventManager *_events;
+	byte *_backgroundScreen;
+	byte *_palette;
+
+	// State variables
+	ComputerState _state;
+	char _searchLetter;
+	int _searchType;  // 0 = title, 1 = author
+	Common::Array<int> _searchResults;
+	int _currentResult;
+	int _memorizedBookIndex;  // Index of book that was memorized (-1 if none)
+
+	const char *_menuTitle;
+	const char *_menuOption1;       // "CONSULTAR POR TITULO"
+	const char *_menuOption2;       // "CONSULTAR POR AUTOR"
+	const char *_menuOption3;       // "CANCELAR"
+	const char *_promptLetter;      // "Teclea una letra (A-Z):"
+	const char *_labelTitle;        // "Titulo    : "
+	const char *_labelAuthor;       // "Autor     : "
+	const char *_labelGenre;        // "Genero    : "
+	const char *_labelSituacion;    // "Situacion : "
+	const char *_statusPhysical;    // "Estante %c, fila %d"
+	const char *_statusCatalogOnly; // "Solo en catalogo"
+	const char *_optMemorizar;      // "(M)emorizar"
+	const char *_optSeguir;         // "(S)eguir"
+	const char *_optCancelar;       // "(C)ancelar"
+	const char *_noResults;         // "No se encontraron libros"
+	const char *_memorizedMsg;      // "Bueno... Tendre que buscar en la estanteria de la %c"
+
+	void loadBackground();
+	void cleanup();
+	void handleMainMenu();
+	void handleSearchInput();
+	void handleResultsDisplay();
+	void performSearch();
+	void drawScreen();
+	void memorizeBook(int bookIndex);
+};
+
+} // End of namespace Pelrock
+
+#endif
diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index e52fbf92f29..59a540cf077 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -69,4 +69,8 @@ void GraphicsManager::putBackgroundSlice(byte *buf, int x, int y, int w, int h,
 	}
 }
 
+void GraphicsManager::clearScreen() {
+	memset(g_engine->_screen->getPixels(), 0, g_engine->_screen->pitch * g_engine->_screen->h);
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/graphics.h b/engines/pelrock/graphics.h
index ab604a63d56..caa28e18f4c 100644
--- a/engines/pelrock/graphics.h
+++ b/engines/pelrock/graphics.h
@@ -34,7 +34,7 @@ public:
 	Common::Point showOverlay(int height, byte *buf);
 	byte *grabBackgroundSlice(byte *buf, int x, int y, int w, int h);
 	void putBackgroundSlice(byte *buf, int x, int y, int w, int h, byte *slice);
-
+	void clearScreen();
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/library_books.h b/engines/pelrock/library_books.h
new file mode 100644
index 00000000000..a86a990948e
--- /dev/null
+++ b/engines/pelrock/library_books.h
@@ -0,0 +1,305 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * Alfred Pelrock Library Book Database
+ * Extracted from ALFRED.7 offset 0x309E0 to 0x33F05
+ *
+ * This file was auto-generated by extract_alfred7_books_full.py
+ * DO NOT EDIT MANUALLY
+ *
+ * Book format in ALFRED.7 (108 bytes per entry):
+ *   - Title:        55 bytes (space-padded)
+ *   - Author:       30 bytes (space-padded)
+ *   - Genre:        20 bytes (space-padded)
+ *   - Shelf Letter:  1 byte (A-Z or space if catalog-only)
+ *   - Shelf Row:     1 byte (1-3 or space)
+ *   - Status:        1 byte (0x01=catalog only, 0x02=physical copy)
+ */
+
+#ifndef PELROCK_LIBRARY_BOOKS_H
+#define PELROCK_LIBRARY_BOOKS_H
+
+#include "common/scummsys.h"
+
+namespace Pelrock {
+
+// Book data location in ALFRED.7
+static const uint32 kBookDataOffset = 0x309E0;
+static const uint32 kBookDataEnd = 0x33F05;
+static const int kBookEntrySize = 108;  // 55 + 30 + 20 + 1 + 1 + 1
+
+// Field sizes
+static const int kBookTitleSize = 55;
+static const int kBookAuthorSize = 30;
+static const int kBookGenreSize = 20;
+
+// Status byte values
+static const byte kBookStatusCatalogOnly = 0x01;  // No physical copy
+static const byte kBookStatusPhysical = 0x02;     // Has physical copy on shelf
+
+struct LibraryBook {
+    const char *title;
+    const char *author;
+    const char *genre;
+    char shelfLetter;    // A-Z for shelf location, space if catalog-only
+    byte shelfRow;       // 1-3 for row number, 0 if catalog-only
+    bool available;  // true = can be found on shelf, false = catalog only
+};
+
+static const int kLibraryBookCount = 125;
+
+static const LibraryBook kLibraryBooks[] = {
+    // Book 1: Los hombres: ¡ Como caparlos !
+    {"Los hombres: ¡ Como caparlos !", "Herminia Gutierrez", "Feminismo", ' ', 0, false},
+    // Book 2: Mujeres del mundo: ¡ No os depileis las ...
+    {"Mujeres del mundo: ¡ No os depileis las axilas !", "Herminia Gutierrez", "Feminismo", ' ', 0, false},
+    // Book 3: Gato por liebre
+    {"Gato por liebre", "Karlos Arguiñano", "Cocina", 'I', 1, true},
+    // Book 4: Hamlet
+    {"Hamlet", "William Shakespeare", "Teatro", ' ', 0, false},
+    // Book 5: Fausto
+    {"Fausto", "Goethe", "Novela", ' ', 0, false},
+    // Book 6: Enrique de Ofterdingen
+    {"Enrique de Ofterdingen", "Novalis", "Novela", 'J', 1, true},
+    // Book 7: Guia de desobediencia civica
+    {"Guia de desobediencia civica", "Azagra", "Ensayo", ' ', 0, false},
+    // Book 8: Literatura en la edad de piedra: cuestio...
+    {"Literatura en la edad de piedra: cuestion de fuerza", "Profesor Cebollo", "Ensayo", ' ', 0, false},
+    // Book 9: Turbo C ++ con Intratex
+    {"Turbo C ++ con Intratex", "Programadores reunidos", "Informatica", ' ', 0, false},
+    // Book 10: Codigo Maquina a pelo
+    {"Codigo Maquina a pelo", "Programadores reunidos", "Informatica", 'K', 1, true},
+    // Book 11: Asesino por vocacion
+    {"Asesino por vocacion", "Chema Ton", "Novela negra", ' ', 0, false},
+    // Book 12: Poemas rebuscados
+    {"Poemas rebuscados", "Ramon Rodriguez", "Poesia", ' ', 0, false},
+    // Book 13: El parto de las tortugas: Un proceso len...
+    {"El parto de las tortugas: Un proceso lento", "Profesor Lorin Colorado", "Biologia", ' ', 0, false},
+    // Book 14: Te parto la cara ¡ Capuyo !
+    {"Te parto la cara ¡ Capuyo !", "Jhonny Rapper", "Critica Social", 'L', 3, true},
+    // Book 15: En los tugurios del Himalaya
+    {"En los tugurios del Himalaya", "Coronel Tapioca", "Aventuras", ' ', 0, false},
+    // Book 16: En las callejuelas del amazonas
+    {"En las callejuelas del amazonas", "Coronel Tapioca", "Aventuras", ' ', 0, false},
+    // Book 17: En las selvas de Londres
+    {"En las selvas de Londres", "Coronel Tapioca", "Aventuras", 'M', 1, true},
+    // Book 18: Sueños binarios
+    {"Sueños binarios", "Doctor Chip", "Ciencia ficcion", ' ', 0, false},
+    // Book 19: Cien cuentos cortisimos
+    {"Cien cuentos cortisimos", "Billi el rapido", "Novela", ' ', 0, false},
+    // Book 20: Teoria de la relatividad
+    {"Teoria de la relatividad", "Alfred Einstein", "Ciencia", ' ', 0, false},
+    // Book 21: El ultimo paso para la cuadratura del ci...
+    {"El ultimo paso para la cuadratura del circulo", "Anonimo", "Filosofia", 'N', 1, true},
+    // Book 22: Correlacion entre el sentido de los colo...
+    {"Correlacion entre el sentido de los colores y sonidos", "Anonimo", "Filosofia", ' ', 0, false},
+    // Book 23: Los cuatro evangelios: Interpretados por...
+    {"Los cuatro evangelios: Interpretados por ordenador", "Doctor Chip", "Teologia", ' ', 0, false},
+    // Book 24: Enciclopedia de bolsillo
+    {"Enciclopedia de bolsillo", "Profesor Lumbreras", "Enciclopedia", 'O', 1, true},
+    // Book 25: Sigueme y revienta
+    {"Sigueme y revienta", "M. Indurain", "Deporte", 'P', 3, true},
+    // Book 26: Me duele too...
+    {"Me duele too...", "Carmen Opausica", "Ensayo", ' ', 0, false},
+    // Book 27: El Perro de Sam Rocker si tiene rabo
+    {"El Perro de Sam Rocker si tiene rabo", "El Perro de Sam Rocker", "Biografia", ' ', 0, false},
+    // Book 28: Donde estara mi carro ?
+    {"Donde estara mi carro ?", "Manolo Escobar", "Aventuras", ' ', 0, false},
+    // Book 29: Oh tu, bella flor del jardin
+    {"Oh tu, bella flor del jardin", "La abeja Maya", "Poesia", 'Q', 2, true},
+    // Book 30: Yogui, ¡ Bajate de esa motoneta !
+    {"Yogui, ¡ Bajate de esa motoneta !", "Bubu", "Filosofia", ' ', 0, false},
+    // Book 31: Odio a muerte a loh mardito rohedore !
+    {"Odio a muerte a loh mardito rohedore !", "Er gato Yin", "Zoologia", ' ', 0, false},
+    // Book 32: Pissi, Dissi, ¡ Sargan de su aguhero !
+    {"Pissi, Dissi, ¡ Sargan de su aguhero !", "Er gato Yin", "Zoologia", ' ', 0, false},
+    // Book 33: Mardito sea ... ¡ Er queso !
+    {"Mardito sea ... ¡ Er queso !", "Er gato Yin", "Zoologia", ' ', 0, false},
+    // Book 34: La mate porque era mia
+    {"La mate porque era mia", "Loquillo", "Novela", ' ', 0, false},
+    // Book 35: Los gallos: Esos desconocidos
+    {"Los gallos: Esos desconocidos", "Paco rico", "Zoologia", ' ', 0, false},
+    // Book 36: Cuentos corrientes
+    {"Cuentos corrientes", "Pepe Lopez", "Novela", 'R', 1, true},
+    // Book 37: Mas madera
+    {"Mas madera", "R. Gepetto", "Bricolage", 'S', 2, true},
+    // Book 38: Cuentos del Lejano Oriente
+    {"Cuentos del Lejano Oriente", "Jhonny Mc. Dowall", "Novela", ' ', 0, false},
+    // Book 39: Saca el guiski cheli
+    {"Saca el guiski cheli", "Manolo lailo", "Cronica Social", ' ', 0, false},
+    // Book 40: Ta totao !
+    {"Ta totao !", "Lao Tse", "Filosofia", ' ', 0, false},
+    // Book 41: Mis conversaciones con el señor Roca
+    {"Mis conversaciones con el señor Roca", "Francisca Gando", "Epistolar", 'T', 2, true},
+    // Book 42: Guia para la supervivencia
+    {"Guia para la supervivencia", "Robinson Crusoe", "Manual", ' ', 0, false},
+    // Book 43: No esperes a ser la segunda: ¡ Engaña a ...
+    {"No esperes a ser la segunda: ¡ Engaña a tu marido !", "Herminia Gutierrez", "Feminismo", ' ', 0, false},
+    // Book 44: Yoga Sutras
+    {"Yoga Sutras", "Patanjali", "Filosofia", 'X', 3, true},
+    // Book 45: El juego de los Abalorios
+    {"El juego de los Abalorios", "Herman Hesse", "Novela", ' ', 0, false},
+    // Book 46: Tienes suerte de ser tan pequeño, ¡ mard...
+    {"Tienes suerte de ser tan pequeño, ¡ mardito roedo !", "Er gato Yin", "Novela", ' ', 0, false},
+    // Book 47: Como hacerse rico en diez minutos
+    {"Como hacerse rico en diez minutos", "El Dioni", "Manual", 'Y', 1, true},
+    // Book 48: Te querre a pesar de tu madre
+    {"Te querre a pesar de tu madre", "Corin Tellado", "Novela rosa", ' ', 0, false},
+    // Book 49: Hasta que el mando a distancia nos separ...
+    {"Hasta que el mando a distancia nos separe", "Corin Tellado", "Novela rosa", ' ', 0, false},
+    // Book 50: Una pasion ostentorea
+    {"Una pasion ostentorea", "Corin Tellado y Jesus Gil", "Novela rosa", 'Z', 3, true},
+    // Book 51: Por mi, como si te la machacas
+    {"Por mi, como si te la machacas", "Seneca", "Filosofia", ' ', 0, false},
+    // Book 52: Conversaciones con mi caballo
+    {"Conversaciones con mi caballo", "Jesus Gil", "Humor", ' ', 0, false},
+    // Book 53: El poder curativo de la mierda comun
+    {"El poder curativo de la mierda comun", "Sri Ramachrinaraska", "Esoterismo", ' ', 1, true},
+    // Book 54: La liberacion mediante la eneriga de los...
+    {"La liberacion mediante la eneriga de los pedos", "Sri Ramachrinaraska", "Esoterismo", ' ', 0, false},
+    // Book 55: Sobre la imperceptibilidad de lo imperce...
+    {"Sobre la imperceptibilidad de lo imperceptible", "Perogrullo", "Ensayo", ' ', 0, false},
+    // Book 56: Piojos; como educarlos sin traumas
+    {"Piojos; como educarlos sin traumas", "Franz Franzfrenz", "Psicologia", ' ', 0, false},
+    // Book 57: I Ching
+    {"I Ching", "Richard Willem", "Filosofia", ' ', 0, false},
+    // Book 58: No se nada, ni me importa
+    {"No se nada, ni me importa", "Socrates", "Filosofia", 'U', 2, true},
+    // Book 59: No he sido yo, ¡ Lo juro !
+    {"No he sido yo, ¡ Lo juro !", "Juan Jose Gil", "Biografia", ' ', 0, false},
+    // Book 60: Relatos cortos
+    {"Relatos cortos", "Tachenko", "Novela de ficcion", 'V', 3, true},
+    // Book 61: El martillo como elemento cognitivo
+    {"El martillo como elemento cognitivo", "Friedrich Nietzsche", "Filosofia", ' ', 0, false},
+    // Book 62: La maravillosa vida del escarabajo pelot...
+    {"La maravillosa vida del escarabajo pelotero (v. I)", "Dr. Federico Ñazo", "Botanica", ' ', 0, false},
+    // Book 63: La maravillosa vida del escarabajo pelot...
+    {"La maravillosa vida del escarabajo pelotero (v. II)", "Dr. Federico Ñazo", "Botanica", ' ', 0, false},
+    // Book 64: La maravillosa vida del escarabajo pelot...
+    {"La maravillosa vida del escarabajo pelotero (v. III)", "Dr. Federico Ñazo", "Botanica", ' ', 0, false},
+    // Book 65: La maravillosa vida del escarabajo pelot...
+    {"La maravillosa vida del escarabajo pelotero (v. IV)", "Dr. Federico Ñazo", "Botanica", ' ', 0, false},
+    // Book 66: La maravillosa vida del escarabajo pelot...
+    {"La maravillosa vida del escarabajo pelotero (v. V)", "Dr. Federico Ñazo", "Botanica", ' ', 0, false},
+    // Book 67: Tu eliges: Tu madre o yo
+    {"Tu eliges: Tu madre o yo", "Anonimo", "Psicologia aplicada", 'W', 3, true},
+    // Book 68: Mi lucha
+    {"Mi lucha", "Adolf Hitler", "Humor negro", ' ', 0, false},
+    // Book 69: Cuentos de amor y desidia
+    {"Cuentos de amor y desidia", "Jardiel Poncela", "Teatro", ' ', 1, true},
+    // Book 70: Nuevas andanzas de Zaratustra
+    {"Nuevas andanzas de Zaratustra", "Anonimo", "Aventuras", ' ', 2, true},
+    // Book 71: Me se cuadre ¡ Coño !
+    {"Me se cuadre ¡ Coño !", "Sargento Cienfuegos", "Etica militar", ' ', 2, true},
+    // Book 72: Gatos: solos o con Ketchup
+    {"Gatos: solos o con Ketchup", "El perro de Sam Rocker", "Cocina", ' ', 1, true},
+    // Book 73: Querida Adelaida: mi marido NO ha dejado...
+    {"Querida Adelaida: mi marido NO ha dejado de roncar", "Maruja Mones", "Epistolar", ' ', 0, false},
+    // Book 74: Sobre el papel de El Lepe en el nuevo Or...
+    {"Sobre el papel de El Lepe en el nuevo Orden Mundial", "General Sintacha", "Estrategia", ' ', 0, false},
+    // Book 75: Aqui no hay nadie que se acueste sin cen...
+    {"Aqui no hay nadie que se acueste sin cenar (v. I)", "Fidel Castro", "Politica", ' ', 1, true},
+    // Book 76: Aqui no hay nadie que se acueste sin cen...
+    {"Aqui no hay nadie que se acueste sin cenar (v. II)", "Fidel Castro", "Politica", ' ', 0, false},
+    // Book 77: Aqui no hay nadie que se acueste sin cen...
+    {"Aqui no hay nadie que se acueste sin cenar (v. III)", "Fidel Castro", "Politica", ' ', 0, false},
+    // Book 78: Aqui no hay nadie que se acueste sin cen...
+    {"Aqui no hay nadie que se acueste sin cenar (v. IV)", "Fidel Castro", "Politica", ' ', 0, false},
+    // Book 79: Aqui no hay nadie que se acueste sin cen...
+    {"Aqui no hay nadie que se acueste sin cenar (v. V)", "Fidel Castro", "Politica", ' ', 0, false},
+    // Book 80: Domine la metafisica en 5 dias
+    {"Domine la metafisica en 5 dias", "Profesor Pinocho", "Manual", ' ', 1, true},
+    // Book 81: Domine el ensamblador en 5 dias
+    {"Domine el ensamblador en 5 dias", "Profesor Pinocho", "Manual", ' ', 1, true},
+    // Book 82: Dominese a si mismo en 5 dias
+    {"Dominese a si mismo en 5 dias", "Profesor Pinocho", "Manual", ' ', 1, true},
+    // Book 83: Piernas de Ciertopelo
+    {"Piernas de Ciertopelo", "Chichi Mondongo", "Erotica", ' ', 2, true},
+    // Book 84: Otra vuelta de tuerca
+    {"Otra vuelta de tuerca", "Pepe, el fontanero", "Bricolage", ' ', 2, true},
+    // Book 85: La Tierra: ¡ Planeta limpio !
+    {"La Tierra: ¡ Planeta limpio !", "Juan, el basurero", "Ciencia Ficcion", ' ', 2, true},
+    // Book 86: Liberad a Brian !
+    {"Liberad a Brian !", "Roger Rabitt", "Historica", ' ', 2, true},
+    // Book 87: La vida es una mierda
+    {"La vida es una mierda", "Juanjo Dido", "Ensayo", ' ', 2, true},
+    // Book 88: No era eso lo que yo di a entender
+    {"No era eso lo que yo di a entender", "Jesus de Nazaret", "Religion", ' ', 2, true},
+    // Book 89: Castigos a Piratas Informaticos
+    {"Castigos a Piratas Informaticos", "Torquemada", "Inquisicion", ' ', 1, true},
+    // Book 90: Confiesa, bruja asquerosa
+    {"Confiesa, bruja asquerosa", "Troquemada", "Inquisicion", ' ', 1, true},
+    // Book 91: El cocherito lere
+    {"El cocherito lere", "Paco costas", "Automovilismo", ' ', 1, true},
+    // Book 92: La musica amansa a las fieras
+    {"La musica amansa a las fieras", "Wagner", "Musica", ' ', 2, true},
+    // Book 93: Pinocho en el Parlamento
+    {"Pinocho en el Parlamento", "Carmen Tirosa", "Cronica Social", ' ', 0, false},
+    // Book 94: Hagase famoso gracias a la energia de la...
+    {"Hagase famoso gracias a la energia de las petunias", "Carmelo Cuelo", "Esoterismo", ' ', 0, false},
+    // Book 95: Dios mio, ¡ Que cruz !
+    {"Dios mio, ¡ Que cruz !", "Jesus de Nazaret", "Autobiografia", ' ', 0, false},
+    // Book 96: Psicologia de la motivacion inmotivada
+    {"Psicologia de la motivacion inmotivada", "Dr. Chemi", "Psicologia", ' ', 2, true},
+    // Book 97: Magia rosa para iniciados
+    {"Magia rosa para iniciados", "Manolo Lailo", "Esoterismo", ' ', 0, false},
+    // Book 98: Un mundo Feliz
+    {"Un mundo Feliz", "Aldous Huxley", "Novela", ' ', 0, false},
+    // Book 99: Sexo oral y por escrito
+    {"Sexo oral y por escrito", "Franz Masturmann", "Sexologia", ' ', 3, true},
+    // Book 100: El contrato social de aprendizaje
+    {"El contrato social de aprendizaje", "Rousseau", "Ensayo", ' ', 1, true},
+    // Book 101: Vida sexual del escarabajo de la Patagon...
+    {"Vida sexual del escarabajo de la Patagonia", "Dr. Tedio Plomez", "Botanica", ' ', 3, true},
+    // Book 102: Manual del necrofago
+    {"Manual del necrofago", "Jesus Gil", "Manual", ' ', 0, false},
+    // Book 103: Canticos espirituales en formato *.ZIP
+    {"Canticos espirituales en formato *.ZIP", "Doctor Chip", "Poesia", 'B', 1, true},
+    // Book 104: Novela erotica en formato *.MAS
+    {"Novela erotica en formato *.MAS", "Doctor Chip", "Erotica", ' ', 0, false},
+    // Book 105: Plopuestas colelacionales en coyuntulas ...
+    {"Plopuestas colelacionales en coyuntulas bilatelales", "Senadol Chan Chu Yo", "Politica", ' ', 0, false},
+    // Book 106: Ereh un fistro
+    {"Ereh un fistro", "Chiquito de la casa", "Humor", ' ', 0, false},
+    // Book 107: El hacedor de la Lluvia
+    {"El hacedor de la Lluvia", "Herman Hesse", "Cuentos", ' ', 0, false},
+    // Book 108: Pasiones recalcitrantes
+    {"Pasiones recalcitrantes", "Corin Tellado", "Novela rosa", 'C', 2, true},
+    // Book 109: No me mates con tomate
+    {"No me mates con tomate", "Karlos Arguiñano", "Cocina", ' ', 0, false},
+    // Book 110: El valenciano en los albores del siglo X...
+    {"El valenciano en los albores del siglo XXI", "Jaume i Pascual", "Nacionalismo", 'D', 1, true},
+    // Book 111: El valenciano es la lengua del futuro
+    {"El valenciano es la lengua del futuro", "Jaume i Pascual", "Nacionalismo", ' ', 0, false},
+    // Book 112: Valencia: mes que mai
+    {"Valencia: mes que mai", "Jaume i Pascual", "Nacionalismo", ' ', 0, false},
+    // Book 113: Sistema inmunitario de los cefalopodos (...
+    {"Sistema inmunitario de los cefalopodos (v. I)", "Dr. Tedio Plomez", "Biologia", 'E', 3, true},
+    // Book 114: Sistema inmunitario de los cefalopodos (...
+    {"Sistema inmunitario de los cefalopodos (v. II)", "Dr. Tedio Plomez", "Biologia", ' ', 0, false},
+    // Book 115: Sistema inmunitario de los cefalopodos (...
+    {"Sistema inmunitario de los cefalopodos (v. III)", "Dr. Tedio Plomez", "Biologia", ' ', 0, false},
+    // Book 116: Sistema inmunitario de los cefalopodos (...
+    {"Sistema inmunitario de los cefalopodos (v. IV)", "Dr. Tedio Plomez", "Biologia", ' ', 0, false},
+    // Book 117: Sistema inmunitario de los cefalopodos (...
+    {"Sistema inmunitario de los cefalopodos (v. V)", "Dr. Tedio Plomez", "Biologia", ' ', 0, false},
+    // Book 118: Dos mas dos son cinco
+    {"Dos mas dos son cinco", "Joan Josep Climent Colomer", "Matematicas", 'F', 1, true},
+    // Book 119: El algebra es la base de la programacion
+    {"El algebra es la base de la programacion", "Joan Josep Climent Colomer", "Humor absurdo", ' ', 0, false},
+    // Book 120: Autobiografia de una miseria humana
+    {"Autobiografia de una miseria humana", "Joan Josep Climent Colomer", "Esperpento", ' ', 0, false},
+    // Book 121: El arte mundial antes y despues de mi
+    {"El arte mundial antes y despues de mi", "Nacho Taulet Perman", "Arte", ' ', 0, false},
+    // Book 122: La parte Creativa
+    {"La parte Creativa", "Nacho Taulet Perman", "Arte", 'G', 2, true},
+    // Book 123: Llamame cuando se muera tu abuelo
+    {"Llamame cuando se muera tu abuelo", "Jose Bart Carrion", "Teatro", ' ', 0, false},
+    // Book 124: Soy un incomprendido
+    {"Soy un incomprendido", "Jose Bart Carrion", "Autobiografia", ' ', 0, false},
+    // Book 125: El arte de limpiar botijos por dentro
+    {"El arte de limpiar botijos por dentro", "Varios autores", "Manualidades", ' ', 0, false},
+};
+
+} // namespace Pelrock
+
+#endif // PELROCK_LIBRARY_BOOKS_H
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index b8da093fb03..a2bea00e420 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -135,26 +135,29 @@ void MenuManager::checkMouseClick(int x, int y) {
 }
 
 void MenuManager::menuLoop() {
-	_events->pollEvent();
-
-	if (_events->_leftMouseClicked) {
-		checkMouseClick(_events->_mouseX, _events->_mouseY);
-		_events->_leftMouseClicked = false;
-	} else if (_events->_rightMouseClicked) {
-		g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
-		g_engine->_state->stateGame = GAME;
-		_events->_rightMouseClicked = false;
-		tearDown();
-	} else {
-		if (_events->_lastKeyEvent == Common::KEYCODE_b) {
-			// g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
-			// g_engine->stateGame = GAME;
-			showButtons = !showButtons;
-			_events->_lastKeyEvent = Common::KEYCODE_INVALID;
-			// tearDown();
+
+	g_engine->changeCursor(DEFAULT);
+	while (!g_engine->shouldQuit() && !_events->_rightMouseClicked) {
+
+		_events->pollEvent();
+
+		if (_events->_leftMouseClicked) {
+			checkMouseClick(_events->_mouseX, _events->_mouseY);
+			_events->_leftMouseClicked = false;
 		}
+
+		drawScreen();
+		_screen->markAllDirty();
+		_screen->update();
+		g_system->delayMillis(10);
 	}
+	g_engine->_graphics->clearScreen();
+	_events->_rightMouseClicked = false;
+	g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
+	cleanUp();
+}
 
+void MenuManager::drawScreen() {
 	memcpy(_compositeBuffer, _mainMenu, 640 * 400);
 	// memset(_compositeBuffer, 0, 640 * 400);
 	if (showButtons)
@@ -168,8 +171,6 @@ void MenuManager::menuLoop() {
 	}
 
 	drawText(g_engine->_smallFont, Common::String::format("%d,%d", _events->_mouseX, _events->_mouseY), 0, 0, 640, 13);
-	_screen->markAllDirty();
-	_screen->update();
 }
 
 void MenuManager::drawInventoryIcons() {
@@ -308,7 +309,7 @@ void MenuManager::loadMenuTexts() {
 	exe.close();
 }
 
-void MenuManager::tearDown() {
+void MenuManager::cleanUp() {
 }
 
 void MenuManager::drawButtons() {
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 3de2bc10f92..32b0b14505d 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -51,6 +51,7 @@ public:
 	MenuManager(Graphics::Screen *screen, PelrockEventManager *events, ResourceManager *res);
 	~MenuManager();
 	void menuLoop();
+	void drawScreen();
 	void drawInventoryIcons();
 	void loadMenu();
 	byte _mainMenuPalette[768] = {0};
@@ -58,7 +59,7 @@ public:
 private:
 	void checkMouseClick(int x, int y);
 	void loadMenuTexts();
-	void tearDown();
+	void cleanUp();
 	void drawButtons();
 	void drawColoredText(Graphics::ManagedSurface *surface, const Common::String &text, int x, int y, int w, Graphics::Font *font);
 	void readButton(Common::File &alfred7, uint32 offset, byte *outBuffer[2], Common::Rect rect);
diff --git a/engines/pelrock/module.mk b/engines/pelrock/module.mk
index fc5b879566f..5a295a952b0 100644
--- a/engines/pelrock/module.mk
+++ b/engines/pelrock/module.mk
@@ -4,6 +4,7 @@ MODULE_OBJS = \
 	pelrock.o \
 	actions.o \
 	chrono.o \
+	computer.o \
 	console.o \
 	metaengine.o \
 	room.o \
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 28ce2b527de..f9d1b13e358 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -36,6 +36,7 @@
 
 #include "pelrock.h"
 #include "pelrock/actions.h"
+#include "pelrock/computer.h"
 #include "pelrock/console.h"
 #include "pelrock/detection.h"
 #include "pelrock/fonts/small_font.h"
@@ -111,31 +112,24 @@ Common::Error PelrockEngine::run() {
 	if (saveSlot != -1)
 		(void)loadGameState(saveSlot);
 
-	// Simple event handling loop
-	Graphics::FrameLimiter limiter(g_system, 60);
-
-	if (shouldPlayIntro == false) {
-		_state->stateGame = GAME;
-		// stateGame = SETTINGS;
-	} else {
-		_state->stateGame = INTRO;
-		_videoManager->playIntro();
-		_state->stateGame = GAME;
-	}
-	if (!shouldQuit())
-		init();
+	_state->stateGame = shouldPlayIntro ? INTRO : GAME;
 
+	init();
 	while (!shouldQuit()) {
-
 		if (_state->stateGame == SETTINGS) {
-			changeCursor(DEFAULT);
 			_menu->menuLoop();
+			_state->stateGame = GAME;
 		} else if (_state->stateGame == GAME) {
 			gameLoop();
 		}
-		_screen->update();
-		// limiter.delayBeforeSwap();
-		// limiter.startFrame();
+		else if (_state->stateGame == INTRO) {
+			_videoManager->playIntro();
+			_state->stateGame = GAME;
+		}
+		else if (_state->stateGame == COMPUTER) {
+			computerLoop();
+			_state->stateGame = GAME;
+		}
 	}
 
 	return Common::kNoError;
@@ -1227,9 +1221,16 @@ void PelrockEngine::pickupIconFlash() {
 }
 
 void PelrockEngine::gameLoop() {
-	_events->pollEvent();
-	checkMouse();
-	renderScene();
+
+		_events->pollEvent();
+		checkMouse();
+		renderScene();
+		_screen->update();
+}
+
+void PelrockEngine::computerLoop() {
+	Computer computer(_events);
+	computer.run();
 }
 
 void PelrockEngine::extraScreenLoop() {
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 1d9970cab01..3edb3a915b8 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -61,10 +61,8 @@ private:
 	Common::RandomSource _randomSource;
 	VideoManager *_videoManager = nullptr;
 	SoundManager *_sound = nullptr;
-	PelrockEventManager *_events = nullptr;
 	DialogManager *_dialog = nullptr;
 	MenuManager *_menu = nullptr;
-	GraphicsManager *_graphics = nullptr;
 
 	void init();
 	void loadAnims();
@@ -103,13 +101,13 @@ private:
 	void chooseAlfredStateAndDraw();
 	void drawAlfred(byte *buf);
 	void drawNextFrame(Sprite *animSet);
-	void changeCursor(Cursor cursor);
 	void animateTalkingNPC(Sprite *animSet);
 	void pickupIconFlash();
 
 	void playSoundIfNeeded();
 
 	void gameLoop();
+	void computerLoop();
 	void extraScreenLoop();
 	void walkLoop(int16 x, int16 y, AlfredDirection direction);
 
@@ -156,10 +154,12 @@ protected:
 	Common::Error run() override;
 
 public:
+	GraphicsManager *_graphics = nullptr;
 	Graphics::Screen *_screen = nullptr;
 	ResourceManager *_res = nullptr;
 	RoomManager *_room = nullptr;
 	ChronoManager *_chrono = nullptr;
+	PelrockEventManager *_events = nullptr;
 	AlfredState _alfredState;
 	byte *_compositeBuffer = nullptr; // Working composition buffer
 
@@ -223,12 +223,14 @@ public:
 	void setScreen(int s, AlfredDirection dir);
 	void loadExtraScreenAndPresent(int screenIndex);
 	void waitForSpecialAnimation();
-	void doExtraActions(int roomNumber);
 	bool renderScene(int overlayMode = OVERLAY_NONE);
 
+	void changeCursor(Cursor cursor);
+
+	// Actions
+	void doExtraActions(int roomNumber);
 	void addInventoryItem(int item);
 	void buyFromStore(HotSpot *hotspot, int stickerId);
-	// Actions
 	void performActionTrigger(uint16 actionTrigger);
 	void dialogActionTrigger(uint16 actionTrigger, byte room, byte rootIndex);
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 65b9ea944b4..db0570ea3bd 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -311,12 +311,10 @@ struct ScaleCalculation {
 
 enum GameState {
 	GAME = 100,
-	MENU = 101,
-	CREDITS = 102,
-	SAVELOAD = 103,
-	SETTINGS = 104,
-	EXTRA_SCREEN = 105,
-	INTRO = 106,
+	CREDITS = 101,
+	SETTINGS = 102,
+	INTRO = 103,
+	COMPUTER = 104
 };
 
 struct HotSpotChange {
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index e4b8c5d370f..e105ca50e73 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -19,12 +19,13 @@
  *
  */
 #include "common/stream.h"
+
+#include "graphics/cursorman.h"
 #include "graphics/font.h"
 
 #include "pelrock/pelrock.h"
 #include "pelrock/types.h"
 #include "pelrock/util.h"
-#include "util.h"
 
 namespace Pelrock {
 


Commit: 463759106ffac067400fa8aee679a4a5bcb4f2a6
    https://github.com/scummvm/scummvm/commit/463759106ffac067400fa8aee679a4a5bcb4f2a6
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:49+02:00

Commit Message:
PELROCK: Cleanup

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/sound.cpp
    engines/pelrock/types.h
    engines/pelrock/util.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 1b18c8f46d1..633e2e1f318 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -21,7 +21,6 @@
 
 #include "graphics/paletteman.h"
 
-#include "pelrock.h"
 #include "pelrock/actions.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
@@ -126,7 +125,7 @@ void PelrockEngine::closeDoor(HotSpot *hotspot, int exitIndex, int sticker, bool
 }
 
 void PelrockEngine::addInventoryItem(int item) {
-	if (_state->inventoryItems.size() == 0) {
+	if (_state->inventoryItems.empty()) {
 		_state->selectedInventoryItem = item;
 	}
 	_flashingIcon = item;
@@ -228,7 +227,7 @@ void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
 		addInventoryItem(75);
 	} else {
 		int billCount = 0;
-		for (int i = 0; i < _state->inventoryItems.size(); i++) {
+		for (uint i = 0; i < _state->inventoryItems.size(); i++) {
 			if (_state->inventoryItems[i] == 5) {
 				billCount++;
 			}
@@ -445,7 +444,7 @@ void PelrockEngine::useCordWithPlug(int inventoryObject, HotSpot *hotspot) {
 }
 
 void PelrockEngine::pickCables(HotSpot *hotspot) {
-	if(_room->hasSticker(21)) {
+	if (_room->hasSticker(21)) {
 		_dialog->say(_res->_ingameTexts[QUELOSCOJA_SUPADRE]);
 		return;
 	}
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 020414d7a5a..3e2bebb996a 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -20,7 +20,6 @@
  */
 
 #include "pelrock/dialog.h"
-#include "dialog.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/util.h"
 
@@ -113,7 +112,7 @@ void DialogManager::displayChoices(Common::Array<ChoiceOption> *choices, byte *c
 
 	int overlayHeight = choices->size() * kChoiceHeight + 2;
 	Common::Point overlayPos = _graphics->showOverlay(overlayHeight, compositeBuffer);
-	for (int i = 0; i < choices->size(); i++) {
+	for (uint i = 0; i < choices->size(); i++) {
 		ChoiceOption choice = (*choices)[i];
 		int choicePadding = 32;
 		int width = g_engine->_doubleSmallFont->getStringWidth(choice.text);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index f9d1b13e358..4002e663f55 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -121,12 +121,10 @@ Common::Error PelrockEngine::run() {
 			_state->stateGame = GAME;
 		} else if (_state->stateGame == GAME) {
 			gameLoop();
-		}
-		else if (_state->stateGame == INTRO) {
+		} else if (_state->stateGame == INTRO) {
 			_videoManager->playIntro();
 			_state->stateGame = GAME;
-		}
-		else if (_state->stateGame == COMPUTER) {
+		} else if (_state->stateGame == COMPUTER) {
 			computerLoop();
 			_state->stateGame = GAME;
 		}
@@ -240,7 +238,7 @@ bool PelrockEngine::renderScene(int overlayMode) {
 		updateAnimations();
 		// Some stickers need to be placed AFTER sprites, hardcoded in the original
 		if (_room->_currentRoomNumber == 3) {
-			for (int i = 0; i < _state->stickersPerRoom[3].size(); i++) {
+			for (uint i = 0; i < _state->stickersPerRoom[3].size(); i++) {
 				if (_state->stickersPerRoom[3][i].stickerIndex == 14) {
 					placeSticker(_state->stickersPerRoom[3][i]);
 					break;
@@ -354,7 +352,7 @@ void PelrockEngine::updateAnimations() {
 
 	sortAnimsByZOrder(_room->_currentRoomAnims);
 	// First pass: sprites behind Alfred (y <= alfredY)
-	for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
+	for (uint i = 0; i < _room->_currentRoomAnims.size(); i++) {
 		if (_room->_currentRoomAnims[i].zOrder > 10 || _room->_currentRoomAnims[i].zOrder < 0) {
 			drawNextFrame(&_room->_currentRoomAnims[i]);
 		}
@@ -364,7 +362,7 @@ void PelrockEngine::updateAnimations() {
 	chooseAlfredStateAndDraw();
 
 	// Second pass: sprites in front of Alfred (y > alfredY)
-	for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
+	for (uint i = 0; i < _room->_currentRoomAnims.size(); i++) {
 		if (_room->_currentRoomAnims[i].zOrder <= 10 && _room->_currentRoomAnims[i].zOrder >= 0) {
 			drawNextFrame(&_room->_currentRoomAnims[i]);
 		}
@@ -399,7 +397,7 @@ void PelrockEngine::paintDebugLayer() {
 	bool showWalkboxes = false;
 
 	if (showWalkboxes) {
-		for (int i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
+		for (uint i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
 			WalkBox box = _room->_currentRoomWalkboxes[i];
 			drawRect(_screen, box.x, box.y, box.w, box.h, 150 + i);
 			// _smallFont->drawString(_screen, Common::String::format("%d", i), box.x + 2, box.y + 2, 640, 14);
@@ -408,7 +406,7 @@ void PelrockEngine::paintDebugLayer() {
 
 	bool showSprites = true;
 	if (showSprites) {
-		for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
+		for (uint i = 0; i < _room->_currentRoomAnims.size(); i++) {
 			Sprite sprite = _room->_currentRoomAnims[i];
 			drawRect(_screen, sprite.x, sprite.y, sprite.animData->w, sprite.animData->h, 14);
 			_smallFont->drawString(_screen, Common::String::format("S %d", sprite.index), sprite.x + 2, sprite.y, 640, 14);
@@ -417,7 +415,7 @@ void PelrockEngine::paintDebugLayer() {
 
 	bool showHotspots = true;
 	if (showHotspots) {
-		for (int i = 0; i < _room->_currentRoomHotspots.size(); i++) {
+		for (uint i = 0; i < _room->_currentRoomHotspots.size(); i++) {
 			HotSpot hotspot = _room->_currentRoomHotspots[i];
 			if (!hotspot.isEnabled || hotspot.isSprite)
 				continue;
@@ -429,7 +427,7 @@ void PelrockEngine::paintDebugLayer() {
 
 	bool showExits = true;
 	if (showExits) {
-		for (int i = 0; i < _room->_currentRoomExits.size(); i++) {
+		for (uint i = 0; i < _room->_currentRoomExits.size(); i++) {
 			Exit exit = _room->_currentRoomExits[i];
 			drawRect(_screen, exit.x, exit.y, exit.w, exit.h, 200 + i);
 			_smallFont->drawString(_screen, Common::String::format("Exit %d -> Room %d", i, exit.targetRoom), exit.x + 2, exit.y + 2, 640, 14);
@@ -450,7 +448,7 @@ void PelrockEngine::paintDebugLayer() {
 
 void PelrockEngine::placeStickers() {
 	// also place temporary stickers
-	for (int i = 0; i < _room->_roomStickers.size(); i++) {
+	for (uint i = 0; i < _room->_roomStickers.size(); i++) {
 		Sticker sticker = _room->_roomStickers[i];
 		placeSticker(sticker);
 	}
@@ -539,6 +537,7 @@ void PelrockEngine::animateRotatePalette(PaletteAnim *anim) {
 			_room->_roomPalette[(anim->startIndex + i) * 3 + 1] = paletteValues[srcIndex * 3 + 1];
 			_room->_roomPalette[(anim->startIndex + i) * 3 + 2] = paletteValues[srcIndex * 3 + 2];
 		}
+		delete[] paletteValues;
 
 		g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
 
@@ -571,7 +570,7 @@ void PelrockEngine::doAction(VerbIcon action, HotSpot *hotspot) {
 
 void PelrockEngine::talkTo(HotSpot *hotspot) {
 	Sprite *animSet = nullptr;
-	for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
+	for (uint i = 0; i < _room->_currentRoomAnims.size(); i++) {
 		if (_room->_currentRoomAnims[i].index == hotspot->index) {
 			animSet = &_room->_currentRoomAnims[i];
 			animSet->isTalking = true;
@@ -1120,7 +1119,7 @@ int PelrockEngine::isHotspotUnder(int x, int y) {
 }
 
 Exit *PelrockEngine::isExitUnder(int x, int y) {
-	for (int i = 0; i < _room->_currentRoomExits.size(); i++) {
+	for (uint i = 0; i < _room->_currentRoomExits.size(); i++) {
 		Exit exit = _room->_currentRoomExits[i];
 		if (x >= exit.x && x <= (exit.x + exit.w) &&
 			y >= exit.y && y <= (exit.y + exit.h) && exit.isEnabled) {
@@ -1134,6 +1133,9 @@ Exit *PelrockEngine::isExitUnder(int x, int y) {
  * Checks if the given position is actually frame data or transparent pixel
  */
 bool PelrockEngine::isSpriteUnder(Sprite *sprite, int x, int y) {
+	if (sprite == nullptr) {
+		return false;
+	}
 	Anim &animData = sprite->animData[sprite->curAnimIndex];
 	int curFrame = animData.curFrame;
 
@@ -1154,7 +1156,7 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 
 	VerbIcon icon = isActionUnder(_events->_mouseX, _events->_mouseY);
 	bool shouldBlink = _chrono->getFrameCount() % kIconBlinkPeriod == 0;
-	for (int i = 0; i < actions.size(); i++) {
+	for (uint i = 0; i < actions.size(); i++) {
 		if (icon == actions[i] && shouldBlink) {
 			continue;
 		}
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index ff01637d04b..8a47740db83 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -22,9 +22,8 @@
 #include "pelrock/resources.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
+#include "pelrock/room.h"
 #include "pelrock/util.h"
-#include "resources.h"
-#include "room.h"
 
 namespace Pelrock {
 
@@ -226,20 +225,19 @@ void ResourceManager::loadAlfredSpecialAnim(int numAnim, bool reverse) {
 	}
 
 	alfred7.seek(offset.offset, SEEK_SET);
-	if(_currentSpecialAnim)
+	if (_currentSpecialAnim)
 		delete _currentSpecialAnim;
 	_currentSpecialAnim = new AlfredSpecialAnim(offset.numFrames, offset.w, offset.h, offset.numBudas, offset.offset, offset.loops);
 	_currentSpecialAnim->animData = new byte[offset.numFrames * offset.w * offset.h];
-	if(offset.numBudas > 0) {
+	if (offset.numBudas > 0) {
 		mergeRleBlocks(&alfred7, offset.offset, offset.numBudas, _currentSpecialAnim->animData);
-	}
-	else {
+	} else {
 		alfred7.read(_currentSpecialAnim->animData, offset.numFrames * offset.w * offset.h);
 	}
-	if(reverse) {
+	if  (reverse) {
 		// reverse frames for testing
 		byte *reversedData = new byte[offset.numFrames * offset.w * offset.h];
-		for(int i = 0; i < offset.numFrames; i++) {
+		for (int i = 0; i < offset.numFrames; i++) {
 			extractSingleFrame(_currentSpecialAnim->animData,
 							   &reversedData[i * offset.w * offset.h],
 							   offset.numFrames - 1 - i,
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 9e7a1e7efd5..d0fb17c4754 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -23,7 +23,6 @@
 #include "pelrock/pelrock.h"
 #include "pelrock/room.h"
 #include "pelrock/util.h"
-#include "room.h"
 
 namespace Pelrock {
 
@@ -105,37 +104,31 @@ void RoomManager::onlyPersistSticker(byte room, int stickerId) {
 }
 
 void RoomManager::removeSticker(int stickerIndex) {
-	int index = -1;
-	if (index == -1) {
-		for (int i = 0; i < _roomStickers.size(); i++) {
-			if (_roomStickers[i].stickerIndex == stickerIndex) {
-				index = i;
-				_roomStickers.remove_at(index);
-				return;
-			}
+	// First check and remove from room stickers
+	for (uint i = 0; i < _roomStickers.size(); i++) {
+		if (_roomStickers[i].stickerIndex == stickerIndex) {
+			_roomStickers.remove_at(i);
+			return;
 		}
 	}
 
-	for (int i = 0; i < g_engine->_state->stickersPerRoom[_currentRoomNumber].size(); i++) {
+	// Then check and remove from persisted stickers
+	for (uint i = 0; i < g_engine->_state->stickersPerRoom[_currentRoomNumber].size(); i++) {
 		if (g_engine->_state->stickersPerRoom[_currentRoomNumber][i].stickerIndex == stickerIndex) {
-			index = i;
-			g_engine->_state->stickersPerRoom[_currentRoomNumber].remove_at(index);
-			break;
+			g_engine->_state->stickersPerRoom[_currentRoomNumber].remove_at(i);
+			return;
 		}
 	}
-
-	if (index != -1 && index < g_engine->_state->stickersPerRoom[_currentRoomNumber].size())
-		g_engine->_state->stickersPerRoom[_currentRoomNumber].remove_at(index);
 }
 
 bool RoomManager::hasSticker(int index) {
-	for (int i = 0; i < g_engine->_state->stickersPerRoom[_currentRoomNumber].size(); i++) {
+	for (uint i = 0; i < g_engine->_state->stickersPerRoom[_currentRoomNumber].size(); i++) {
 		if (g_engine->_state->stickersPerRoom[_currentRoomNumber][i].stickerIndex == index) {
 			return true;
 		}
 	}
 
-	for (int i = 0; i < _roomStickers.size(); i++) {
+	for (uint i = 0; i < _roomStickers.size(); i++) {
 		if (_roomStickers[i].stickerIndex == index) {
 			return true;
 		}
@@ -204,7 +197,7 @@ void RoomManager::addWalkbox(WalkBox walkbox) {
 }
 
 Sprite *RoomManager::findSpriteByIndex(byte index) {
-	for (int i = 0; i < _currentRoomAnims.size(); i++) {
+	for (uint i = 0; i < _currentRoomAnims.size(); i++) {
 		if (_currentRoomAnims[i].index == index) {
 			return &_currentRoomAnims[i];
 		}
@@ -213,7 +206,7 @@ Sprite *RoomManager::findSpriteByIndex(byte index) {
 }
 
 HotSpot *RoomManager::findHotspotByIndex(byte index) {
-	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
+	for (uint i = 0; i < _currentRoomHotspots.size(); i++) {
 		if (!_currentRoomHotspots[i].isSprite && _currentRoomHotspots[i].innerIndex == index) {
 			debug("Found hotspot %d at index %d, extra = %d", index, i, _currentRoomHotspots[i].extra);
 			return &_currentRoomHotspots[i];
@@ -223,7 +216,7 @@ HotSpot *RoomManager::findHotspotByIndex(byte index) {
 }
 
 HotSpot *RoomManager::findHotspotByExtra(uint16 extra) {
-	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
+	for (uint i = 0; i < _currentRoomHotspots.size(); i++) {
 		if (_currentRoomHotspots[i].extra == extra) {
 			return &_currentRoomHotspots[i];
 		}
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 3feb2b25913..cb676dbabd6 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -77,7 +77,7 @@ public:
 	void changeHotspot(byte room, HotSpot hotspot);
 	void disableSprite(byte roomNumber, int spriteIndex, bool persist = true);
 	void enableSprite(int spriteIndex, int zOrder, bool persist = true);
-	void enableSprite(byte roomNumber,int spriteIndex, int zOrder, bool persist = true);
+	void enableSprite(byte roomNumber, int spriteIndex, int zOrder, bool persist = true);
 	/**
 	 * Utility function to enable or disable a hotspot, with an option to persist the change.
 	 */
@@ -92,9 +92,9 @@ public:
 	void applyDisabledChoices(byte roomNumber, byte *conversationData, size_t conversationDataSize);
 	void applyDisabledChoice(ResetEntry entry, byte *conversationData, size_t conversationDataSize);
 	void addDisabledChoice(ChoiceOption choice);
-	bool isPickableByExtra(uint16 extra)  {
+	bool isPickableByExtra(uint16 extra) {
 		int size = sizeof(unpickableHotspotExtras) / sizeof(unpickableHotspotExtras[0]);
-		for(int i = 0; i < size; i++) {
+		for (int i = 0; i < size; i++) {
 			if (extra == unpickableHotspotExtras[i])
 				return false;
 		}
@@ -106,7 +106,7 @@ public:
 	PaletteAnim *getPaletteAnimForRoom(int roomNumber);
 
 	Common::String getRoomName(int roomNumber) {
-		if (roomNumber >= 0 && roomNumber < _roomNames.size()) {
+		if (roomNumber >= 0 && (uint)roomNumber < _roomNames.size()) {
 			return _roomNames[roomNumber];
 		}
 		return "Unknown Room";
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 530a7c934c6..deea925d318 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -34,7 +34,6 @@
 
 #include "pelrock/pelrock.h"
 #include "pelrock/sound.h"
-#include "sound.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index db0570ea3bd..72ecc35def2 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -508,7 +508,7 @@ struct GameStateData {
 	}
 
 	void removeInventoryItem(int id) {
-		for (int i = 0; i < inventoryItems.size(); i++) {
+		for (uint i = 0; i < inventoryItems.size(); i++) {
 			if (inventoryItems[i] == id) {
 				inventoryItems.remove_at(i);
 				return;
@@ -517,7 +517,7 @@ struct GameStateData {
 	}
 
 	bool hasInventoryItem(int id) const {
-		for (int i = 0; i < inventoryItems.size(); i++) {
+		for (uint i = 0; i < inventoryItems.size(); i++) {
 			if (inventoryItems[i] == id) {
 				return true;
 			}
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index e105ca50e73..e4c5aa39f10 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -45,28 +45,28 @@ void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color
 }
 
 void drawRect(byte *screenBuffer, int x, int y, int w, int h, byte color) {
-	Graphics::Surface *surface = new Graphics::Surface();
-	surface->create(w, h, Graphics::PixelFormat::createFormatCLUT8());
-	drawRect(surface, 0, 0, w, h, color);
+	Graphics::Surface surface;
+	surface.create(w, h, Graphics::PixelFormat::createFormatCLUT8());
+	drawRect(&surface, 0, 0, w, h, color);
 
 	for (int py = 0; py < h; py++) {
 		for (int px = 0; px < w; px++) {
 			int destIdx = (y + py) * 640 + (x + px);
-			int srcIdx = py * w + px;
-			int color = *((byte *)surface->getBasePtr(px, py));
-			if (color != 0)
-				screenBuffer[destIdx] = color;
+			int pixelColor = *((byte *)surface.getBasePtr(px, py));
+			if (pixelColor != 0)
+				screenBuffer[destIdx] = pixelColor;
 		}
 	}
+	surface.free();
 }
 
 void drawText(byte *screenBuffer, Graphics::Font *font, Common::String text, int x, int y, int w, byte color, Graphics::TextAlign align) {
 	Common::Rect rect = font->getBoundingBox(text.c_str());
-	Graphics::Surface *surface = new Graphics::Surface();
+	Graphics::Surface surface;
 	int bboxW = rect.width();
 	int bboxH = rect.height();
 
-	surface->create(bboxW, bboxH, Graphics::PixelFormat::createFormatCLUT8());
+	surface.create(bboxW, bboxH, Graphics::PixelFormat::createFormatCLUT8());
 
 	if (x + bboxW > 640) {
 		x = 640 - bboxW - 2;
@@ -82,17 +82,17 @@ void drawText(byte *screenBuffer, Graphics::Font *font, Common::String text, int
 	}
 
 	// Draw main text on top
-	font->drawString(surface, text.c_str(), 0, 0, bboxW, color, align);
+	font->drawString(&surface, text.c_str(), 0, 0, bboxW, color, align);
 	// drawRect(surface, 0, 0, bboxW - 1, bboxH - 1, color);
 	for (int py = 0; py < bboxH; py++) {
 		for (int px = 0; px < bboxW; px++) {
 			int destIdx = (y + py) * 640 + (x + px);
-			int srcIdx = py * bboxW + px;
-			int color = *((byte *)surface->getBasePtr(px, py));
-			if (color != 0)
-				screenBuffer[destIdx] = color;
+			int pixelColor = *((byte *)surface.getBasePtr(px, py));
+			if (pixelColor != 0)
+				screenBuffer[destIdx] = pixelColor;
 		}
 	}
+	surface.free();
 }
 
 void drawText(Graphics::Font *font, Common::String text, int x, int y, int w, byte color) {


Commit: 451919142ac6d28c15fbacb21dd672aa39b7223b
    https://github.com/scummvm/scummvm/commit/451919142ac6d28c15fbacb21dd672aa39b7223b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:50+02:00

Commit Message:
PELROCK: Finish room 4

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 633e2e1f318..6e48f7358c3 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -21,6 +21,7 @@
 
 #include "graphics/paletteman.h"
 
+#include "pelrock.h"
 #include "pelrock/actions.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
@@ -41,6 +42,7 @@ const ActionEntry actionTable[] = {
 	{0, PICKUP, &PelrockEngine::pickYellowBook}, // Generic pickup for other items
 	// Room 1
 	{4, PICKUP, &PelrockEngine::pickUpBrick}, // Brick
+	{277, OPEN, &PelrockEngine::openIceCreamShopDoor},
 	// Room 2
 	{282, OPEN, &PelrockEngine::openMcDoor},
 	{282, CLOSE, &PelrockEngine::closeMcDoor},
@@ -79,6 +81,7 @@ const ActionEntry actionTable[] = {
 	// Room 4
 	{315, OPEN, &PelrockEngine::openPlug},
 	{316, PICKUP, &PelrockEngine::pickCables},
+	{312, OPEN, &PelrockEngine::openMuseumDoor},
 
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
@@ -99,6 +102,8 @@ const CombinationEntry combinationTable[] = {
 	{4, 294, &PelrockEngine::useBrickWithWindow},       // Use Brick with Window (Room 3)
 	{4, 295, &PelrockEngine::useBrickWithShopWindow},
 	{6, 315, &PelrockEngine::useCordWithPlug},
+	{1, 53, &PelrockEngine::giveIdToGuard},    // Give ID to Guard
+	{5, 53, &PelrockEngine::giveMoneyToGuard}, // Give Money to Guard
 	// End marker
 	{WILDCARD, WILDCARD, nullptr}};
 
@@ -167,6 +172,11 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	if (actionTrigger == 328) {
 		debug("Disabling root %d in room %d", rootIndex, room);
 		_state->setRootDisabledState(room, rootIndex, true);
+	} else if (actionTrigger == 258) {
+		_state->setFlag(FLAG_GUARDIA_PIDECOSAS, true);
+		_state->setRootDisabledState(4, 1, true);
+	} else {
+		debug("Got actionTrigger %d in dialogActionTrigger, but no handler defined", actionTrigger);
 	}
 }
 
@@ -215,6 +225,10 @@ void PelrockEngine::pickUpBrick(HotSpot *hotspot) {
 	_room->addSticker(133);
 }
 
+void PelrockEngine::openIceCreamShopDoor(HotSpot *hotspot) {
+	_dialog->say(_res->_ingameTexts[HELADERIA_CERRADA]);
+}
+
 void PelrockEngine::closeRoomDrawer(HotSpot *hotspot) {
 	_room->removeSticker(91);
 	_room->enableHotspot(hotspot);
@@ -471,6 +485,52 @@ void PelrockEngine::pickCables(HotSpot *hotspot) {
 	_state->setRootDisabledState(4, 0, true);
 }
 
+void PelrockEngine::giveIdToGuard(int inventoryObject, HotSpot *hotspot) {
+	if (!_state->getFlag(FLAG_GUARDIA_PIDECOSAS)) {
+		_dialog->say(_res->_ingameTexts[CUANDOMELOPIDA]);
+		return;
+	}
+
+	if (!_state->getFlag(FLAG_GUARDIA_DNI_ENTREGADO)) {
+		_state->setFlag(FLAG_GUARDIA_DNI_ENTREGADO, true);
+		_dialog->say(_res->_ingameTexts[DEACUERDO]);
+		return;
+	}
+	if (_state->getFlag(FLAG_SOBORNO_PORTERO) && _state->getFlag(FLAG_GUARDIA_DNI_ENTREGADO)) {
+		_state->setRootDisabledState(4, 2, true);
+		return;
+	}
+}
+
+void PelrockEngine::giveMoneyToGuard(int inventoryObject, HotSpot *hotspot) {
+	if (!_state->getFlag(FLAG_GUARDIA_PIDECOSAS)) {
+		_dialog->say(_res->_ingameTexts[PRETENDEUSTED_SOBORNARME]);
+		return;
+	} else if (!_state->getFlag(FLAG_SOBORNO_PORTERO)) {
+		_state->setFlag(FLAG_SOBORNO_PORTERO, true);
+		_dialog->say(_res->_ingameTexts[MUYBIEN]);
+		_state->removeInventoryItem(5); // Remove 1000 pesetas bill
+		return;
+	}
+	if (_state->getFlag(FLAG_SOBORNO_PORTERO) && _state->getFlag(FLAG_GUARDIA_DNI_ENTREGADO)) {
+		_state->setRootDisabledState(4, 2, true);
+		return;
+	}
+}
+
+void PelrockEngine::openMuseumDoor(HotSpot *hotspot) {
+	if (!_state->getFlag(FLAG_GUARDIA_PIDECOSAS)) {
+		_dialog->say(_res->_ingameTexts[ALTO]);
+		return;
+	} else if (!_state->getFlag(FLAG_GUARDIA_DNI_ENTREGADO)) {
+		_dialog->say(_res->_ingameTexts[NECESITODNI]);
+	} else if (!_state->getFlag(FLAG_SOBORNO_PORTERO)) {
+		_dialog->say(_res->_ingameTexts[QUE_RECIBO_ACAMBIO]);
+	} else {
+		openDoor(hotspot, 1, 22, FEMININE, false);
+	}
+}
+
 void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	debug("Performing action trigger: %d", actionTrigger);
 	switch (actionTrigger) {
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 3edb3a915b8..915b68ed8a8 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -256,6 +256,7 @@ public:
 	void pickUpPhoto(HotSpot *hotspot);
 	void pickYellowBook(HotSpot *hotspot);
 	void pickUpBrick(HotSpot *hotspot);
+	void openIceCreamShopDoor(HotSpot *hotspot);
 	void noOpAction(HotSpot *hotspot);
 	void noOpItem(int item, HotSpot *hotspot);
 	void useOnAlfred(int inventoryObject);
@@ -278,6 +279,9 @@ public:
 	void openPlug(HotSpot *hotspot);
 	void useCordWithPlug(int inventoryObject, HotSpot *hotspot);
 	void pickCables(HotSpot *hotspot);
+	void giveIdToGuard(int inventoryObject, HotSpot *hotspot);
+	void giveMoneyToGuard(int inventoryObject, HotSpot *hotspot);
+	void openMuseumDoor(HotSpot *hotspot);
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
 };
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index d0fb17c4754..b78f88ec381 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -122,14 +122,13 @@ void RoomManager::removeSticker(int stickerIndex) {
 }
 
 bool RoomManager::hasSticker(int index) {
-	for (uint i = 0; i < g_engine->_state->stickersPerRoom[_currentRoomNumber].size(); i++) {
-		if (g_engine->_state->stickersPerRoom[_currentRoomNumber][i].stickerIndex == index) {
+	for (uint i = 0; i < _roomStickers.size(); i++) {
+		if (_roomStickers[i].stickerIndex == index) {
 			return true;
 		}
 	}
-
-	for (uint i = 0; i < _roomStickers.size(); i++) {
-		if (_roomStickers[i].stickerIndex == index) {
+	for (uint i = 0; i < g_engine->_state->stickersPerRoom[_currentRoomNumber].size(); i++) {
+		if (g_engine->_state->stickersPerRoom[_currentRoomNumber][i].stickerIndex == index) {
 			return true;
 		}
 	}
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 72ecc35def2..0bd87050257 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -456,8 +456,10 @@ struct ResetEntry {
 #define FLAG_TIENDA_ABIERTA 46
 #define FLAG_NUMERO_DE_COPAS 47
 #define FLAG_INGREDIENTES_CONSEGUIDOS 48
+#define FLAG_GUARDIA_PIDECOSAS 49
+#define FLAG_GUARDIA_DNI_ENTREGADO 50
 
-const int kNumGameFlags = 49;
+const int kNumGameFlags = 51;
 
 struct GameStateData {
 	byte flags[kNumGameFlags];


Commit: 9379385a8e1f649ab6863671edf33ec5825a42fe
    https://github.com/scummvm/scummvm/commit/9379385a8e1f649ab6863671edf33ec5825a42fe
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:50+02:00

Commit Message:
PELROCK: Adds forced terminator to conversations

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 3e2bebb996a..ce472641f2c 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "pelrock/dialog.h"
+#include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/util.h"
 
@@ -326,6 +327,24 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 						if (tb == CTRL_END_TEXT || tb == CTRL_DIALOGUE_MARKER ||
 							tb == CTRL_DIALOGUE_MARKER_ONEOFF || tb == CTRL_END_BRANCH ||
 							tb == CTRL_ALT_END_MARKER_1) {
+							// Check if there is a terminator (F4 or F8) at the end of this choice's response
+							// Scan forward but stop at another choice marker or branch end
+							uint32 scanPos = textPos;
+							while (scanPos < dataSize) {
+								byte sb = data[scanPos];
+								// Stop scanning at another choice marker or branch boundaries
+								if (sb == CTRL_DIALOGUE_MARKER || sb == CTRL_DIALOGUE_MARKER_ONEOFF ||
+									sb == CTRL_END_BRANCH || sb == CTRL_ALT_END_MARKER_1 ||
+									sb == CTRL_ALT_SPEAKER_ROOT) {
+									break;
+								}
+								// Found a conversation terminator - this choice ends the conversation
+								if (sb == CTRL_END_CONVERSATION || sb == CTRL_ACTION_TRIGGER) {
+									opt.hasConversationEndMarker = true;
+									break;
+								}
+								scanPos++;
+							}
 							break;
 						}
 
@@ -385,7 +404,7 @@ bool DialogManager::checkAllSubBranchesExhausted(const byte *data, uint32 dataSi
 				if (data[pos + 2] != CTRL_DISABLED_CHOICE) {
 					debug("checkAllSubBranchesExhausted: Active FB at pos %u, idx %d (current %d) - NOT exhausted",
 						  pos, choiceIdx, currentChoiceLevel);
-					return false;  // Don't disable parent
+					return false; // Don't disable parent
 				}
 			} else if (choiceIdx <= currentChoiceLevel) {
 				// Hit choice at same or lower level - stop
@@ -420,7 +439,7 @@ void DialogManager::setCurSprite(int index) {
 
 bool isRootDisabled(byte room, int root) {
 
-	if(g_engine->_state->getRootDisabledState(room, root)) {
+	if (g_engine->_state->getRootDisabledState(room, root)) {
 		return true;
 	}
 	return false;
@@ -437,10 +456,10 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 	debug("Starting conversation with %u bytes of data, for npc %u", dataSize, npcIndex);
 
 	uint32 position = 0;
-	int currentChoiceLevel = -1; // Track the current choice level
+	int currentChoiceLevel = -1;       // Track the current choice level
 	uint32 lastChoiceMenuPosition = 0; // Track where we last showed a choice menu
-	ChoiceOption lastSelectedChoice; // Track the last choice we selected
-	bool skipToChoices = false; // After F0, skip directly to choice parsing
+	ChoiceOption lastSelectedChoice;   // Track the last choice we selected
+	bool skipToChoices = false;        // After F0, skip directly to choice parsing
 
 	// Find the speaker tree for this NPC; they are marked by 0xFE 0xXX where XX is NPC index + 1
 	bool speakerTreeOffsetFound = false;
@@ -497,8 +516,8 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 				}
 				position = lastChoiceMenuPosition;
 				skipToChoices = true; // Skip directly to choice parsing
-				// Jump directly to choice parsing section
-				// Don't continue - let it fall through
+									  // Jump directly to choice parsing section
+									  // Don't continue - let it fall through
 			} else {
 				debug("F0: No previous choice menu, ending conversation");
 				break;
@@ -587,6 +606,28 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		lastChoiceMenuPosition = position;
 		Common::Array<ChoiceOption> *choices = new Common::Array<ChoiceOption>();
 		parseChoices(conversationData, dataSize, position, choices);
+
+		// Check if ANY choice has a conversation terminator (F4 or F8)
+		// If so, there's already a way to exit - don't add goodbye option
+		// Only add goodbye if NO choices terminate the conversation naturally
+		bool anyChoiceTerminatesConversation = false;
+		for (uint i = 0; i < choices->size(); i++) {
+			if ((*choices)[i].hasConversationEndMarker) {
+				anyChoiceTerminatesConversation = true;
+				break;
+			}
+		}
+		if (!anyChoiceTerminatesConversation && choices->size() > 0) {
+			// No choice ends the conversation, so add the goodbye option
+			ChoiceOption termChoice;
+			termChoice.choiceIndex = currentChoiceLevel;
+			termChoice.isTerminator = true;
+			termChoice.isDisabled = false;
+			termChoice.shouldDisableOnSelect = false;
+			termChoice.text = g_engine->_res->_conversationTerminator;
+			choices->push_back(termChoice);
+		}
+
 		debug("Parsed %u choices", choices->size());
 		for (uint i = 0; i < choices->size(); i++) {
 			debug(" Choice %u (index %d): \"%s\" (Disabled: %s)", i, (*choices)[i].choiceIndex, (*choices)[i].text.c_str(),
@@ -645,8 +686,13 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 
 		// 6. Move position to after the selected choice
 		if (selectedIndex >= 0 && selectedIndex < (int)choices->size()) {
+
 			// Save this choice in case we hit F0 and need to disable it
 			lastSelectedChoice = (*choices)[selectedIndex];
+			if (lastSelectedChoice.isTerminator) {
+				displayDialogue(lastSelectedChoice.text, ALFRED_COLOR);
+				break;
+			}
 			position = (*choices)[selectedIndex].dataOffset;
 			currentChoiceLevel = (*choices)[selectedIndex].choiceIndex;
 
@@ -657,7 +703,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 
 			if (!choiceText.empty() && choiceText.size() > 1) {
 				displayDialogue(choiceText, ALFRED_COLOR);
-				// Only disable FB choice if all sub-branches are exhausted (Ghidra LAB_00018c2d)
+				// Only disable FB choice if all sub-branches are exhausted
 				if ((*choices)[selectedIndex].shouldDisableOnSelect) {
 					bool shouldDisable = checkAllSubBranchesExhausted(conversationData, dataSize, endPos, currentChoiceLevel);
 					if (shouldDisable) {
@@ -736,7 +782,6 @@ void DialogManager::say(Common::StringArray texts, int16 x, int16 y) {
 	} else {
 		sayAlfred(texts);
 	}
-
 }
 
 bool DialogManager::processColorAndTrim(Common::StringArray &lines, byte &speakerId) {
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 0f8d078af3b..dc3e24dc03e 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -50,18 +50,6 @@ namespace Pelrock {
 #define CTRL_ALT_END_MARKER_2 0xEB       /* Alt end marker 2 */
 #define CTRL_ALT_SPEAKER_ROOT 0xFE       /* Separates conversations from different speakers */
 
-// static void debugHexString(const Common::String &str, const char *label = nullptr) {
-// 	if (label) {
-// 		debug("%s:", label);
-// 	}
-
-// 	Common::String hexOutput;
-// 	for (uint i = 0; i < str.size(); i++) {
-// 		hexOutput += Common::String::format("%02X ", (unsigned char)str[i]);
-// 	}
-// 	debug("%s", hexOutput.c_str());
-// }
-
 class DialogManager {
 private:
 	Graphics::Screen *_screen = nullptr;
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index bdd14778c95..9f53a168279 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -50,6 +50,7 @@ static const uint32 kInventoryDescriptionsSize = 7868;
 static const uint32 kMenuTextOffset = 0x49203;
 static const uint32 kMenuTextSize = 230;
 static const uint32 kAlfredResponsesOffset = 0x441DC;
+static const uint32 kConversationTerminatorOffset = 0x0492EE;
 static const uint32 kAlfredResponsesSize = 12163;
 static const uint32 kCreditsOffset = 0x49F60;
 static const uint32 kCreditsSize = 2540;
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 4002e663f55..cfeb4f748ee 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -137,7 +137,7 @@ void PelrockEngine::init() {
 	_res->loadCursors();
 	_res->loadInteractionIcons();
 	_res->loadInventoryItems();
-	_res->loadAlfredResponses();
+	_res->loadHardcodedText();
 
 	_sound->loadSoundIndex();
 	_menu->loadMenu();
@@ -314,6 +314,7 @@ void PelrockEngine::checkMouse() {
 		_actionPopupState.isActive = false;
 		// Mouse was released while popup is active
 		VerbIcon actionClicked = isActionUnder(_events->_releaseX, _events->_releaseY);
+		debug("Popup action clicked: %d, is alfredunder %d", actionClicked, _actionPopupState.isAlfredUnder);
 		if (_actionPopupState.isAlfredUnder) {
 			debug("Using item on Alfred");
 			useOnAlfred(_state->selectedInventoryItem);
@@ -937,6 +938,7 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 	_alfredState.idleFrameCounter = 0;
 	int hotspotIndex = isHotspotUnder(x, y);
 	bool alfredUnder = isAlfredUnder(x, y);
+	debug("Long click at %d,%d - hotspot %d, alfred under %d", x, y, hotspotIndex, alfredUnder);
 	if ((hotspotIndex != -1 || alfredUnder) && !_actionPopupState.isActive) {
 
 		_actionPopupState.x = _alfredState.x + kAlfredFrameWidth / 2 - kBalloonWidth / 2;
@@ -1224,10 +1226,10 @@ void PelrockEngine::pickupIconFlash() {
 
 void PelrockEngine::gameLoop() {
 
-		_events->pollEvent();
-		checkMouse();
-		renderScene();
-		_screen->update();
+	_events->pollEvent();
+	checkMouse();
+	renderScene();
+	_screen->update();
 }
 
 void PelrockEngine::computerLoop() {
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 8a47740db83..08af8c0331c 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -234,7 +234,7 @@ void ResourceManager::loadAlfredSpecialAnim(int numAnim, bool reverse) {
 	} else {
 		alfred7.read(_currentSpecialAnim->animData, offset.numFrames * offset.w * offset.h);
 	}
-	if  (reverse) {
+	if (reverse) {
 		// reverse frames for testing
 		byte *reversedData = new byte[offset.numFrames * offset.w * offset.h];
 		for (int i = 0; i < offset.numFrames; i++) {
@@ -277,7 +277,7 @@ void ResourceManager::loadInventoryItems() {
 	delete[] iconData;
 }
 
-void ResourceManager::loadAlfredResponses() {
+void ResourceManager::loadHardcodedText() {
 
 	Common::File exe;
 	if (!exe.open("JUEGO.EXE")) {
@@ -287,6 +287,10 @@ void ResourceManager::loadAlfredResponses() {
 	exe.seek(kAlfredResponsesOffset, SEEK_SET);
 	exe.read(descBuffer, kAlfredResponsesSize);
 	_ingameTexts = processTextData(descBuffer, kAlfredResponsesSize);
+	byte *terminatorBuffer = new byte[39];
+	exe.seek(kConversationTerminatorOffset, SEEK_SET);
+	exe.read(terminatorBuffer, 39);
+	_conversationTerminator = Common::String((const char *)terminatorBuffer, 39);
 	delete[] descBuffer;
 	exe.close();
 }
@@ -322,11 +326,11 @@ Common::Array<Common::StringArray> ResourceManager::getCredits() {
 	return credits;
 }
 
-Common::Array<Common::Array<Common::String>> ResourceManager::processTextData(byte *data, size_t size, bool decode) {
+Common::Array<Common::StringArray> ResourceManager::processTextData(byte *data, size_t size, bool decode) {
 	int pos = 0;
 	Common::String desc = "";
 	Common::StringArray lines;
-	Common::Array<Common::Array<Common::String>> texts;
+	Common::Array<Common::StringArray> texts;
 	while (pos < size) {
 		if (data[pos] == CTRL_END_TEXT) {
 			if (!desc.empty()) {
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index f91386e2f37..22aec4820cb 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -48,7 +48,7 @@ public:
 	void loadAlfredSpecialAnim(int numAnim, bool reverse = false);
 	void clearSpecialAnim();
 	void loadInventoryItems();
-	void loadAlfredResponses();
+	void loadHardcodedText();
 	void getExtraScreen(int screenIndex, byte *screenBuf, byte *palette);
 	Common::Array<Common::StringArray> getCredits();
 	Common::Array<Common::Array<Common::String>> processTextData(byte *data, size_t size, bool decode = false);
@@ -69,13 +69,10 @@ public:
 	byte *_verbIcons[9];
 	byte *_popUpBalloon = nullptr;
 	Common::Array<Common::StringArray> _ingameTexts;
+	Common::String _conversationTerminator;
 
 	// Special anims
 	AlfredSpecialAnim *_currentSpecialAnim = nullptr;
-	// byte *_specialAnimData = nullptr;
-	// int _specialAnimCurFrame = 0;
-	// int _speciaAnimLoopCount = 0;
-	// AlfredSpecialAnimOffset _curSpecialAnim;
 	bool _isSpecialAnimFinished = false;
 };
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 0bd87050257..6203cf64bdb 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -394,10 +394,12 @@ struct ChoiceOption {
 	int choiceIndex;
 	Common::String text;
 	uint32 dataOffset;
-	bool isDisabled;
+	bool isDisabled = false;
 	bool shouldDisableOnSelect = false;
+	bool hasConversationEndMarker = false;
+	bool isTerminator = false;
 
-	ChoiceOption() : choiceIndex(-1), isDisabled(false), dataOffset(0) {}
+	ChoiceOption() : choiceIndex(-1), dataOffset(0) {}
 };
 
 struct ResetEntry {


Commit: acdc0fd107646aac28f01a8e21d62f08b36b21cd
    https://github.com/scummvm/scummvm/commit/acdc0fd107646aac28f01a8e21d62f08b36b21cd
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:50+02:00

Commit Message:
PELROCK: Dialog in room 5

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/events.cpp
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 6e48f7358c3..761945ed49a 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -83,6 +83,9 @@ const ActionEntry actionTable[] = {
 	{316, PICKUP, &PelrockEngine::pickCables},
 	{312, OPEN, &PelrockEngine::openMuseumDoor},
 
+	// Room 5
+	{},
+
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
 	{WILDCARD, TALK, &PelrockEngine::noOpAction},   // Generic talk action
@@ -172,9 +175,22 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	if (actionTrigger == 328) {
 		debug("Disabling root %d in room %d", rootIndex, room);
 		_state->setRootDisabledState(room, rootIndex, true);
+	} else if (actionTrigger == 329) {
+		debug("Would now enable X easter egg");
 	} else if (actionTrigger == 258) {
 		_state->setFlag(FLAG_GUARDIA_PIDECOSAS, true);
 		_state->setRootDisabledState(4, 1, true);
+	} else if (actionTrigger == 259) {
+		_dialog->say(_res->_ingameTexts[NO_EMPECEMOS]);
+	} else if (actionTrigger == 260) {
+		_dialog->say(_res->_ingameTexts[CUERPO_DANONE], 1);
+		_dialog->say(_res->_ingameTexts[CABEZA_HUECA]);
+	} else if (actionTrigger == 261) {
+		_dialog->say(_res->_ingameTexts[ESO_LO_SERAS_TU], 1);
+	} else if (actionTrigger == 262) {
+		_dialog->say(_res->_ingameTexts[DEMASIADO_NO_PUEDO_PENSAR], 1);
+	} else if (actionTrigger == 263) {
+		_dialog->say(_res->_ingameTexts[UN_POCO_RESPETO]);
 	} else {
 		debug("Got actionTrigger %d in dialogActionTrigger, but no handler defined", actionTrigger);
 	}
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index ce472641f2c..2ce7ca94221 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -289,27 +289,30 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 	outChoices->clear();
 	int firstChoiceIndex = -1;
 
-	// Scan for additional choices with SAME index
+	// Scan for choices with SAME index
+	// The key insight: choices at the same level may be scattered throughout the data,
+	// separated by deeper-level sub-branches. We must scan past higher-index choices
+	// to find all choices at our level, but stop when we hit a LOWER index.
 	while (pos < dataSize) {
 		byte b = data[pos];
 
 		// Stop at end markers
-		if (b == CTRL_ALT_END_MARKER_1 || b == CTRL_END_BRANCH) {
+		if (b == CTRL_ALT_END_MARKER_1 || b == CTRL_END_BRANCH || b == CTRL_ALT_SPEAKER_ROOT) {
 			break;
 		}
 
 		// Found a dialogue marker
 		if (b == CTRL_DIALOGUE_MARKER || b == CTRL_DIALOGUE_MARKER_ONEOFF) {
 			if (pos + 1 < dataSize) {
+				int choiceIndex = data[pos + 1];
+				
+				// Set firstChoiceIndex from first non-disabled choice, or first choice if all disabled
 				if (firstChoiceIndex == -1) {
-					firstChoiceIndex = data[pos + 1];
+					firstChoiceIndex = choiceIndex;
 				}
-				int choiceIndex = data[pos + 1];
 
-				// Only collect choices with same index
+				// Only collect choices with same index as the first one we found
 				if (choiceIndex == firstChoiceIndex) {
-					// Check if disabled
-
 					ChoiceOption opt;
 					opt.room = g_engine->_room->_currentRoomNumber;
 					opt.shouldDisableOnSelect = b == CTRL_DIALOGUE_MARKER_ONEOFF;
@@ -321,7 +324,6 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 					}
 					// Parse the choice text
 					uint32 textPos = pos + 4;
-					// textPos += 2; // Skip marker + index + 2 speaker bytes
 					while (textPos < dataSize) {
 						byte tb = data[textPos];
 						if (tb == CTRL_END_TEXT || tb == CTRL_DIALOGUE_MARKER ||
@@ -361,9 +363,12 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 					if (!opt.isDisabled)
 						outChoices->push_back(opt);
 				} else if (choiceIndex < firstChoiceIndex) {
-					// Different choice index - stop scanning
+					// Hit a choice at a LOWER level - stop scanning
+					// This means we've gone past all choices at our level
 					break;
 				}
+				// If choiceIndex > firstChoiceIndex, we're in a deeper sub-branch
+				// Continue scanning to find more choices at our level
 			}
 		}
 
@@ -388,9 +393,12 @@ bool DialogManager::checkAllSubBranchesExhausted(const byte *data, uint32 dataSi
 	while (pos < dataSize) {
 		byte b = data[pos];
 
-		// Stop at branch/conversation end markers (0xF5, 0xF7, 0xFE, 0xF4)
+		// Stop at TRUE branch boundary markers (0xF5, 0xF7, 0xFE)
+		// NOTE: Do NOT stop at F4 (CTRL_END_CONVERSATION) - F4 markers appear between
+		// choices as terminators for each choice's response path. We need to scan
+		// past them to find all choices at the target level.
 		if (b == CTRL_ALT_END_MARKER_1 || b == CTRL_END_BRANCH ||
-			b == CTRL_ALT_SPEAKER_ROOT || b == CTRL_END_CONVERSATION) {
+			b == CTRL_ALT_SPEAKER_ROOT) {
 			break;
 		}
 
@@ -407,7 +415,8 @@ bool DialogManager::checkAllSubBranchesExhausted(const byte *data, uint32 dataSi
 					return false; // Don't disable parent
 				}
 			} else if (choiceIdx <= currentChoiceLevel) {
-				// Hit choice at same or lower level - stop
+				// Hit choice at same or lower level - stop scanning
+				// All choices at higher levels before this point have been checked
 				break;
 			}
 		}
@@ -536,7 +545,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 			byte speakerId;
 			endPos = readTextBlock(conversationData, dataSize, position, text, speakerId);
 			Common::Array<Common::Array<Common::String>> wrappedText = wordWrap(text);
-			debug("Word wrapping %s produces %d pages", text.c_str(), wrappedText.size());
+			// debug("Word wrapping %s produces %d pages", text.c_str(), wrappedText.size());
 			// Skip spurious single character artifacts
 			if (!text.empty() && text.size() > 1) {
 				displayDialogue(wrappedText, speakerId);
@@ -707,6 +716,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 				if ((*choices)[selectedIndex].shouldDisableOnSelect) {
 					bool shouldDisable = checkAllSubBranchesExhausted(conversationData, dataSize, endPos, currentChoiceLevel);
 					if (shouldDisable) {
+						debug("Disabling one-time choice at index %d after selection", selectedIndex);
 						g_engine->_room->addDisabledChoice((*choices)[selectedIndex]);
 					}
 				}
@@ -747,7 +757,7 @@ void DialogManager::sayAlfred(Description description) {
 	}
 }
 
-void DialogManager::say(Common::StringArray texts) {
+void DialogManager::say(Common::StringArray texts, byte spriteIndex) {
 	if (texts.empty()) {
 		return;
 	}
@@ -759,7 +769,7 @@ void DialogManager::say(Common::StringArray texts) {
 			sayAlfred(texts);
 			return;
 		} else {
-			setCurSprite(0);
+			setCurSprite(spriteIndex);
 			Common::Array<Common::StringArray> textLines = wordWrap(texts);
 			displayDialogue(textLines, speakerId);
 		}
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index dc3e24dc03e..7719110b719 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -76,7 +76,7 @@ public:
 	int selectChoice(Common::Array<Common::String> &choices, byte *compositeBuffer);
 	void startConversation(const byte *conversationData, uint32 dataSize, byte npcIndex, Sprite *alfredAnimSet = nullptr);
 	void sayAlfred(Description description);
-	void say(Common::StringArray texts);
+	void say(Common::StringArray texts, byte spriteIndex = 0);
 	void say(Common::StringArray texts, int16 x, int16 y);
 	bool processColorAndTrim(Common::StringArray &lines, byte &speakerId);
 	Graphics::Surface getDialogueSurface(Common::Array<Common::String> dialogueLines, byte speakerId);
diff --git a/engines/pelrock/events.cpp b/engines/pelrock/events.cpp
index da4b41a7e87..c90705e7c06 100644
--- a/engines/pelrock/events.cpp
+++ b/engines/pelrock/events.cpp
@@ -59,7 +59,6 @@ void PelrockEventManager::pollEvent() {
 		// case Common::EVENT_KEYUP:
 		// 	return;
 		case Common::EVENT_LBUTTONDOWN:
-
 			if (_leftMouseButton == 0) {
 				_clickTime = g_system->getMillis();
 			}
@@ -70,9 +69,9 @@ void PelrockEventManager::pollEvent() {
 		case Common::EVENT_LBUTTONUP:
 			if (_leftMouseButton == 1) {
 				// Don't treat as regular click if we're in popup selection mode
-				if (!_popupSelectionMode) {
-					_leftMouseClicked = true;
-				}
+				// if (!_popupSelectionMode) {
+				_leftMouseClicked = true;
+				// }
 				_releaseX = _event.mouse.x;
 				_releaseY = _event.mouse.y;
 				_longClicked = false;
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 9f53a168279..1d80751bdc3 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -152,12 +152,12 @@ enum TextIndices {
 	NIPARAEMPEZAR,
 	PARAQUE,
 	DEPIEDRANO_DEHIELO,
-	HEY_DONTSTART,
-	HOTONE_MOCKING,
-	SHUTUP,
-	NO_YOU,
-	TOO_MUCH_CANT_THINK,
-	A_LITTLE_RESPECT,
+	NO_EMPECEMOS,
+	CUERPO_DANONE,
+	CABEZA_HUECA,
+	ESO_LO_SERAS_TU,
+	DEMASIADO_NO_PUEDO_PENSAR,
+	UN_POCO_RESPETO,
 	NO_THEY_MAKEYOU_FAT,
 	RELOJ_HA_CAMBIADO,
 	CORRESPONDENCIA_AJENA,
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index cfeb4f748ee..5561005b206 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -154,7 +154,7 @@ void PelrockEngine::init() {
 		loadAnims();
 		// setScreen(0, ALFRED_DOWN);
 		// setScreen(3, ALFRED_RIGHT);
-		setScreen(4, ALFRED_DOWN);
+		setScreen(5, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
@@ -578,6 +578,7 @@ void PelrockEngine::talkTo(HotSpot *hotspot) {
 			break;
 		}
 	}
+	changeCursor(DEFAULT);
 	_dialog->startConversation(_room->_conversationData, _room->_conversationDataSize, hotspot->index, animSet);
 }
 
@@ -938,7 +939,6 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 	_alfredState.idleFrameCounter = 0;
 	int hotspotIndex = isHotspotUnder(x, y);
 	bool alfredUnder = isAlfredUnder(x, y);
-	debug("Long click at %d,%d - hotspot %d, alfred under %d", x, y, hotspotIndex, alfredUnder);
 	if ((hotspotIndex != -1 || alfredUnder) && !_actionPopupState.isActive) {
 
 		_actionPopupState.x = _alfredState.x + kAlfredFrameWidth / 2 - kBalloonWidth / 2;
@@ -1403,6 +1403,7 @@ void PelrockEngine::changeCursor(Cursor cursor) {
 }
 
 void PelrockEngine::checkMouseHover() {
+
 	bool hotspotDetected = false;
 
 	int hotspotIndex = isHotspotUnder(_events->_mouseX, _events->_mouseY);
@@ -1410,17 +1411,19 @@ void PelrockEngine::checkMouseHover() {
 		hotspotDetected = true;
 	}
 
+
 	if (isActionUnder(_events->_mouseX, _events->_mouseY) != NO_ACTION) {
 		hotspotDetected = false;
 	}
 
+	// Calculate walk target first (before checking anything else)
+	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, _events->_mouseX, _events->_mouseY, hotspotDetected, hotspotDetected ? &_room->_currentRoomHotspots[hotspotIndex] : nullptr);
+
+
 	bool alfredDetected = false;
 	if (isAlfredUnder(_events->_mouseX, _events->_mouseY)) {
 		alfredDetected = true;
 	}
-	// Calculate walk target first (before checking anything else)
-	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, _events->_mouseX, _events->_mouseY, hotspotDetected, hotspotDetected ? &_room->_currentRoomHotspots[hotspotIndex] : nullptr);
-
 	// Check if walk target hits any exit
 	bool exitDetected = false;
 	Exit *exit = isExitUnder(walkTarget.x, walkTarget.y);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index b78f88ec381..8917eb03092 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -762,13 +762,17 @@ void RoomManager::applyDisabledChoice(ResetEntry entry, byte *conversationData,
 }
 
 void RoomManager::addDisabledChoice(ChoiceOption choice) {
-	debug("Adding disabled branch for room %d at offset %d", choice.room, choice.dataOffset);
+	// Write 0xFA at offset+2 (after FB/F1 marker and level byte)
+	// This marks the choice as disabled without destroying the marker structure
+	uint32 disableOffset = choice.dataOffset + 2;
+	debug("Adding disabled branch for room %d at offset %d (FA written at %d)", 
+		  choice.room, choice.dataOffset, disableOffset);
 	ResetEntry resetEntry = ResetEntry();
 	resetEntry.room = choice.room;
-	resetEntry.offset = choice.dataOffset;
+	resetEntry.offset = disableOffset;
 	resetEntry.dataSize = 1;
 	resetEntry.data = new byte[1];
-	resetEntry.data[0] = 0xFA; // Disabled
+	resetEntry.data[0] = 0xFA; // Disabled marker
 	// Apply immediately
 	applyDisabledChoice(resetEntry, _conversationData, _conversationDataSize);
 	// Store for future loads


Commit: 08d7b47d4d4f669fe64effc452dc0197a574ed50
    https://github.com/scummvm/scummvm/commit/08d7b47d4d4f669fe64effc452dc0197a574ed50
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:51+02:00

Commit Message:
PELROCK: Fixes dialog in room 7, fixes zorder

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 761945ed49a..f24b3924856 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -191,6 +191,10 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_dialog->say(_res->_ingameTexts[DEMASIADO_NO_PUEDO_PENSAR], 1);
 	} else if (actionTrigger == 263) {
 		_dialog->say(_res->_ingameTexts[UN_POCO_RESPETO]);
+	} else if (actionTrigger == 264) {
+		//disables the two first roots, the second one will be enabled later!
+		_state->setRootDisabledState(room, rootIndex, true);
+		_state->setRootDisabledState(room, rootIndex + 1, true);
 	} else {
 		debug("Got actionTrigger %d in dialogActionTrigger, but no handler defined", actionTrigger);
 	}
@@ -550,14 +554,17 @@ void PelrockEngine::openMuseumDoor(HotSpot *hotspot) {
 void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	debug("Performing action trigger: %d", actionTrigger);
 	switch (actionTrigger) {
-	case 257:
+	case 257: // look portrait
 		_sound->playMusicTrack(25);
 		loadExtraScreenAndPresent(9);
 		_dialog->say(_res->_ingameTexts[QUEBUENA_ESTA]);
 		_screen->markAllDirty();
 		_screen->update();
 		break;
-	case 271:
+	case 268: // look at statue
+		_dialog->say(_res->_ingameTexts[TUCREES]);
+		break;
+	case 271: // look at librarian
 		_dialog->say(_res->_ingameTexts[TRABAJARIA_MEJOR_SI_NO_ME_MOLESTARA]);
 		break;
 	case 270:
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 2ce7ca94221..aa39d3e9116 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -616,25 +616,31 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		Common::Array<ChoiceOption> *choices = new Common::Array<ChoiceOption>();
 		parseChoices(conversationData, dataSize, position, choices);
 
-		// Check if ANY choice has a conversation terminator (F4 or F8)
-		// If so, there's already a way to exit - don't add goodbye option
-		// Only add goodbye if NO choices terminate the conversation naturally
-		bool anyChoiceTerminatesConversation = false;
-		for (uint i = 0; i < choices->size(); i++) {
-			if ((*choices)[i].hasConversationEndMarker) {
-				anyChoiceTerminatesConversation = true;
-				break;
+		// Store original choice count before potentially adding goodbye
+		uint originalChoiceCount = choices->size();
+
+		// Only consider adding goodbye if there are MULTIPLE choices
+		// If there's only 1 choice, it's auto-dialogue and should not have goodbye
+		if (originalChoiceCount > 1) {
+			// Check if ANY choice has a conversation terminator (F4 or F8)
+			// If so, there's already a way to exit - don't add goodbye option
+			bool anyChoiceTerminatesConversation = false;
+			for (uint i = 0; i < choices->size(); i++) {
+				if ((*choices)[i].hasConversationEndMarker) {
+					anyChoiceTerminatesConversation = true;
+					break;
+				}
+			}
+			if (!anyChoiceTerminatesConversation) {
+				// No choice ends the conversation, so add the goodbye option
+				ChoiceOption termChoice;
+				termChoice.choiceIndex = currentChoiceLevel;
+				termChoice.isTerminator = true;
+				termChoice.isDisabled = false;
+				termChoice.shouldDisableOnSelect = false;
+				termChoice.text = g_engine->_res->_conversationTerminator;
+				choices->push_back(termChoice);
 			}
-		}
-		if (!anyChoiceTerminatesConversation && choices->size() > 0) {
-			// No choice ends the conversation, so add the goodbye option
-			ChoiceOption termChoice;
-			termChoice.choiceIndex = currentChoiceLevel;
-			termChoice.isTerminator = true;
-			termChoice.isDisabled = false;
-			termChoice.shouldDisableOnSelect = false;
-			termChoice.text = g_engine->_res->_conversationTerminator;
-			choices->push_back(termChoice);
 		}
 
 		debug("Parsed %u choices", choices->size());
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 5561005b206..8084547e28f 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -348,23 +348,32 @@ void PelrockEngine::copyBackgroundToBuffer() {
 	memcpy(_compositeBuffer, _currentBackground, 640 * 400);
 }
 
-void PelrockEngine::updateAnimations() {
-	// Sort sprites by zOrder (persists in the array)
+// Calculate Alfred's z-order based on Y position
+// At Y=399 (bottom of screen): z = 10 (foreground)
+// At Y=0 (top of screen): z = 209 (background)
+static int calculateAlfredZOrder(int alfredY) {
+	return ((399 - alfredY) & 0xFFFE) / 2 + 10;
+}
 
+void PelrockEngine::updateAnimations() {
+	// Sort sprites by zOrder (ascending: low z = back, rendered first)
 	sortAnimsByZOrder(_room->_currentRoomAnims);
-	// First pass: sprites behind Alfred (y <= alfredY)
+
+	int alfredZOrder = calculateAlfredZOrder(_alfredState.y);
+
+	// First pass: sprites behind Alfred (sprite zOrder > alfredZOrder)
 	for (uint i = 0; i < _room->_currentRoomAnims.size(); i++) {
-		if (_room->_currentRoomAnims[i].zOrder > 10 || _room->_currentRoomAnims[i].zOrder < 0) {
+		if (_room->_currentRoomAnims[i].zOrder > alfredZOrder || _room->_currentRoomAnims[i].zOrder < 0) {
 			drawNextFrame(&_room->_currentRoomAnims[i]);
 		}
 	}
 
-	// Draw Alfred here (you'll need to add this)
+	// Draw Alfred
 	chooseAlfredStateAndDraw();
 
-	// Second pass: sprites in front of Alfred (y > alfredY)
+	// Second pass: sprites in front of Alfred (sprite zOrder <= alfredZOrder)
 	for (uint i = 0; i < _room->_currentRoomAnims.size(); i++) {
-		if (_room->_currentRoomAnims[i].zOrder <= 10 && _room->_currentRoomAnims[i].zOrder >= 0) {
+		if (_room->_currentRoomAnims[i].zOrder <= alfredZOrder && _room->_currentRoomAnims[i].zOrder >= 0) {
 			drawNextFrame(&_room->_currentRoomAnims[i]);
 		}
 	}


Commit: c6066f17073a4fcfede01176148b634c62c8083e
    https://github.com/scummvm/scummvm/commit/c6066f17073a4fcfede01176148b634c62c8083e
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:51+02:00

Commit Message:
PELROCK: WIP mouse movement in room 9

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/chrono.h
    engines/pelrock/console.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index f24b3924856..5cc6b2a9517 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -83,8 +83,20 @@ const ActionEntry actionTable[] = {
 	{316, PICKUP, &PelrockEngine::pickCables},
 	{312, OPEN, &PelrockEngine::openMuseumDoor},
 
-	// Room 5
-	{},
+	// // Room 5
+	// {},
+
+	// // Room 7
+	// {},
+
+	// Room 8
+	{355, OPEN, &PelrockEngine::openLibraryOutdoorsDoor},
+	{355, CLOSE, &PelrockEngine::closeLibraryOutdoorsDoor},
+	{357, PICKUP, &PelrockEngine::pickUpLetter},
+
+	// Room 9
+	{363, OPEN, &PelrockEngine::openLibraryIndoorsDoor},
+	{363, CLOSE, &PelrockEngine::closeLibraryIndoorsDoor},
 
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
@@ -100,13 +112,14 @@ const ActionEntry actionTable[] = {
 	{WILDCARD, NO_ACTION, nullptr}};
 
 const CombinationEntry combinationTable[] = {
-	{2, 281, &PelrockEngine::useCardWithATM},           // Use ATM Card with ATM
-	{62, 117, &PelrockEngine::useSpicySauceWithBurger}, // Use Spicy Sauce with Burger
-	{4, 294, &PelrockEngine::useBrickWithWindow},       // Use Brick with Window (Room 3)
+	{2, 281, &PelrockEngine::useCardWithATM},
+	{62, 117, &PelrockEngine::useSpicySauceWithBurger},
+	{4, 294, &PelrockEngine::useBrickWithWindow},
 	{4, 295, &PelrockEngine::useBrickWithShopWindow},
 	{6, 315, &PelrockEngine::useCordWithPlug},
-	{1, 53, &PelrockEngine::giveIdToGuard},    // Give ID to Guard
-	{5, 53, &PelrockEngine::giveMoneyToGuard}, // Give Money to Guard
+	{1, 53, &PelrockEngine::giveIdToGuard},
+	{5, 53, &PelrockEngine::giveMoneyToGuard},
+	{7, 353, &PelrockEngine::useAmuletWithStatue},
 	// End marker
 	{WILDCARD, WILDCARD, nullptr}};
 
@@ -551,6 +564,46 @@ void PelrockEngine::openMuseumDoor(HotSpot *hotspot) {
 	}
 }
 
+void PelrockEngine::useAmuletWithStatue(int inventoryObject, HotSpot *hotspot) {
+
+	if (!_room->hasSticker(24)) {
+		_room->addSticker(24);
+		_state->removeInventoryItem(7);
+		_state->setRootDisabledState(7, 0, true);
+		_state->setRootDisabledState(7, 1, false);
+		_state->setRootDisabledState(7, 2, true);
+		// TODO: Palette anim
+		HotSpot *statueHotspot = _room->findHotspotByExtra(91);
+		_currentHotspot = statueHotspot;
+
+		walkAndAction(statueHotspot, TALK);
+		_state->setRootDisabledState(7, 1, true);
+
+		// TODO: Undo palette anim!
+	}
+}
+
+void PelrockEngine::pickUpLetter(HotSpot *hotspot) {
+	addInventoryItem(9);
+	_room->setActionMask(hotspot, ACTION_MASK_NONE); // Disable hotspot
+}
+
+void PelrockEngine::openLibraryOutdoorsDoor(HotSpot *hotspot) {
+	openDoor(hotspot, 0, 26, FEMININE, false);
+}
+
+void PelrockEngine::closeLibraryOutdoorsDoor(HotSpot *hotspot) {
+	closeDoor(hotspot, 0, 26, FEMININE, false);
+}
+
+void PelrockEngine::openLibraryIndoorsDoor(HotSpot *hotspot) {
+	openDoor(hotspot, 0, 28, FEMININE, false);
+}
+
+void PelrockEngine::closeLibraryIndoorsDoor(HotSpot *hotspot) {
+	closeDoor(hotspot, 0, 28, FEMININE, false);
+}
+
 void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	debug("Performing action trigger: %d", actionTrigger);
 	switch (actionTrigger) {
diff --git a/engines/pelrock/chrono.h b/engines/pelrock/chrono.h
index f7f67229545..295157816f2 100644
--- a/engines/pelrock/chrono.h
+++ b/engines/pelrock/chrono.h
@@ -30,7 +30,8 @@ namespace Pelrock {
 // const int kTickMs = 15;
 // const int kTickMs = 33;
 // const int kTickMs = 38;
-const int kTickMs = 55;
+// const int kTickMs = 55;
+const int kTickMs = 200;
 // const int kTickMs = 60;
 const int kHalfTickMultiplier = 2;
 
diff --git a/engines/pelrock/console.cpp b/engines/pelrock/console.cpp
index 0f1b2f0f967..3b5a7c327b5 100644
--- a/engines/pelrock/console.cpp
+++ b/engines/pelrock/console.cpp
@@ -27,7 +27,7 @@
 namespace Pelrock {
 
 PelrockConsole::PelrockConsole(PelrockEngine *engine) : GUI::Debugger(), _engine(engine) {
-	registerCmd("setScreen", WRAP_METHOD(PelrockConsole, cmdLoadRoom));
+	registerCmd("room", WRAP_METHOD(PelrockConsole, cmdLoadRoom));
 	registerCmd("give", WRAP_METHOD(PelrockConsole, cmdGiveItems));
 }
 
@@ -36,7 +36,7 @@ PelrockConsole::~PelrockConsole() {
 
 bool PelrockConsole::cmdLoadRoom(int argc, const char **argv) {
 	if (argc < 2) {
-		debugPrintf("Usage: setScreen <roomNumber>");
+		debugPrintf("Usage: room <roomNumber>");
 		return true;
 	}
 
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index aa39d3e9116..7dc5629975f 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -305,7 +305,7 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 		if (b == CTRL_DIALOGUE_MARKER || b == CTRL_DIALOGUE_MARKER_ONEOFF) {
 			if (pos + 1 < dataSize) {
 				int choiceIndex = data[pos + 1];
-				
+
 				// Set firstChoiceIndex from first non-disabled choice, or first choice if all disabled
 				if (firstChoiceIndex == -1) {
 					firstChoiceIndex = choiceIndex;
@@ -462,7 +462,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 	setCurSprite(animSet ? animSet->index : -1);
 	// _curSprite = animSet;
 
-	debug("Starting conversation with %u bytes of data, for npc %u", dataSize, npcIndex);
+	debug("Starting conversation with %d bytes of data, for npc %d, hotspot %d", dataSize, npcIndex, animSet ? animSet->index : -1);
 
 	uint32 position = 0;
 	int currentChoiceLevel = -1;       // Track the current choice level
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 8084547e28f..5b45e3d5b31 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -154,7 +154,8 @@ void PelrockEngine::init() {
 		loadAnims();
 		// setScreen(0, ALFRED_DOWN);
 		// setScreen(3, ALFRED_RIGHT);
-		setScreen(5, ALFRED_DOWN);
+		// setScreen(5, ALFRED_DOWN);
+		setScreen(9, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
@@ -173,25 +174,25 @@ Common::Array<VerbIcon> PelrockEngine::availableActions(HotSpot *hotspot) {
 	Common::Array<VerbIcon> verbs;
 	verbs.push_back(LOOK);
 
-	if (hotspot->actionFlags & 1) {
+	if (hotspot->actionFlags & ACTION_MASK_OPEN) {
 		verbs.push_back(OPEN);
 	}
-	if (hotspot->actionFlags & 2) {
+	if (hotspot->actionFlags & ACTION_MASK_CLOSE) {
 		verbs.push_back(CLOSE);
 	}
-	if (hotspot->actionFlags & 4) {
+	if (hotspot->actionFlags & ACTION_MASK_UNKNOWN) {
 		verbs.push_back(UNKNOWN);
 	}
-	if (hotspot->actionFlags & 8) {
+	if (hotspot->actionFlags & ACTION_MASK_PICKUP) {
 		verbs.push_back(PICKUP);
 	}
-	if (hotspot->actionFlags & 16) {
+	if (hotspot->actionFlags & ACTION_MASK_TALK) {
 		verbs.push_back(TALK);
 	}
-	if (hotspot->actionFlags & 32) {
+	if (hotspot->actionFlags & ACTION_MASK_PUSH) {
 		verbs.push_back(PUSH);
 	}
-	if (hotspot->actionFlags & 128) {
+	if (hotspot->actionFlags & ACTION_MASK_PULL) {
 		verbs.push_back(PULL);
 	}
 	return verbs;
@@ -230,29 +231,16 @@ bool PelrockEngine::renderScene(int overlayMode) {
 
 	_chrono->updateChrono();
 	if (_chrono->_gameTick) {
-		// playSoundIfNeeded();
+		frameTriggers();
+
+		playSoundIfNeeded();
 
 		copyBackgroundToBuffer();
 
-		placeStickers();
+		placeStickersFirstPass();
 		updateAnimations();
-		// Some stickers need to be placed AFTER sprites, hardcoded in the original
-		if (_room->_currentRoomNumber == 3) {
-			for (uint i = 0; i < _state->stickersPerRoom[3].size(); i++) {
-				if (_state->stickersPerRoom[3][i].stickerIndex == 14) {
-					placeSticker(_state->stickersPerRoom[3][i]);
-					break;
-				}
-			}
-		}
 
-		if (overlayMode == OVERLAY_CHOICES) {
-			_dialog->displayChoices(_dialog->_currentChoices, _compositeBuffer);
-		} else if (overlayMode == OVERLAY_PICKUP_ICON) {
-			pickupIconFlash();
-		} else if (overlayMode == OVERLAY_ACTION) {
-			showActionBalloon(_actionPopupState.x, _actionPopupState.y, _actionPopupState.curFrame);
-		}
+		renderOverlay(overlayMode);
 
 		presentFrame();
 		updatePaletteAnimations();
@@ -263,6 +251,55 @@ bool PelrockEngine::renderScene(int overlayMode) {
 	return false;
 }
 
+const int kPasserbyTriggerFrameInterval = 30;
+
+void PelrockEngine::frameTriggers() {
+	if ((_chrono->getFrameCount() & kPasserbyTriggerFrameInterval) == kPasserbyTriggerFrameInterval) {
+		debug("Would trigger passer-by");
+		switch (_room->_currentRoomNumber)
+		{
+		case 9: {
+			Sprite *mouse = _room->findSpriteByIndex(2);
+			mouse->zOrder = 3;
+			break;
+		}
+		default:
+			break;
+		}
+	}
+
+	if(_room->_currentRoomNumber == 9) {
+		Sprite *mouse = _room->findSpriteByIndex(2);
+		if (mouse) {
+			if(mouse->x > 130 && mouse->x < 200) {
+				debug("Moving mouse right and down");
+				if(mouse->curAnimIndex == 0)
+					mouse->curAnimIndex = 1;
+				mouse->y += 2;
+				mouse->x += 6;
+			}
+			else if(mouse->x > 200 && mouse->x < 340) {
+				debug("Moving mouse right");
+				if(mouse->curAnimIndex == 1)
+					mouse->curAnimIndex = 2;
+				mouse->x += 6;
+			}
+			else if(mouse->x > 340) {
+				debug("Moving mouse down");
+				if(mouse->curAnimIndex == 2)
+					mouse->curAnimIndex = 1;
+				mouse->y += 2;
+			}
+			if(mouse->x > 355) {
+				mouse->x = 82;
+				mouse->y = 315;
+				mouse->zOrder = 255;
+				mouse->curAnimIndex = 0;
+			}
+		}
+	}
+}
+
 void PelrockEngine::executeAction(VerbIcon action, HotSpot *hotspot) {
 	debug("Executing action %d on hotspot %d", action, hotspot->extra);
 	if (action == ITEM) {
@@ -320,8 +357,7 @@ void PelrockEngine::checkMouse() {
 			useOnAlfred(_state->selectedInventoryItem);
 		} else if (actionClicked != NO_ACTION && _currentHotspot != nullptr) {
 			// Action was selected - queue it
-			walkTo(_currentHotspot->x + _currentHotspot->w / 2, _currentHotspot->y + _currentHotspot->h);
-			_queuedAction = QueuedAction{actionClicked, _currentHotspot->index, true};
+			walkAndAction(_currentHotspot, actionClicked);
 		} else {
 			// Released outside popup - just close it
 			_queuedAction = QueuedAction{NO_ACTION, -1, false};
@@ -456,7 +492,7 @@ void PelrockEngine::paintDebugLayer() {
 	_smallFont->drawString(_screen, Common::String::format("Frame number: %d", _chrono->getFrameCount()), 0, 30, 640, 13);
 }
 
-void PelrockEngine::placeStickers() {
+void PelrockEngine::placeStickersFirstPass() {
 	// also place temporary stickers
 	for (uint i = 0; i < _room->_roomStickers.size(); i++) {
 		Sticker sticker = _room->_roomStickers[i];
@@ -464,6 +500,18 @@ void PelrockEngine::placeStickers() {
 	}
 }
 
+void PelrockEngine::placeStickersSecondPass() {
+	// Some stickers need to be placed AFTER sprites, hardcoded in the original
+	if (_room->_currentRoomNumber == 3) {
+		for (uint i = 0; i < _state->stickersPerRoom[3].size(); i++) {
+			if (_state->stickersPerRoom[3][i].stickerIndex == 14) {
+				placeSticker(_state->stickersPerRoom[3][i]);
+				break;
+			}
+		}
+	}
+}
+
 void PelrockEngine::placeSticker(Sticker sticker) {
 	for (int y = 0; y < sticker.h; y++) {
 		for (int x = 0; x < sticker.w; x++) {
@@ -479,6 +527,16 @@ void PelrockEngine::placeSticker(Sticker sticker) {
 	}
 }
 
+void PelrockEngine::renderOverlay(int overlayMode) {
+	if (overlayMode == OVERLAY_CHOICES) {
+		_dialog->displayChoices(_dialog->_currentChoices, _compositeBuffer);
+	} else if (overlayMode == OVERLAY_PICKUP_ICON) {
+		pickupIconFlash();
+	} else if (overlayMode == OVERLAY_ACTION) {
+		showActionBalloon(_actionPopupState.x, _actionPopupState.y, _actionPopupState.curFrame);
+	}
+}
+
 void PelrockEngine::animateFadePalette(PaletteAnim *anim) {
 	// FADE palette animation - cycles a single palette entry between min/max RGB values
 	// Data layout (after loading from EXE):
@@ -588,6 +646,7 @@ void PelrockEngine::talkTo(HotSpot *hotspot) {
 		}
 	}
 	changeCursor(DEFAULT);
+	debug("Talking to hotspot %d (%d) with extra %d", hotspot->index, hotspot->isSprite ? hotspot->index : hotspot->index - _room->_currentRoomAnims.size(), hotspot->extra);
 	_dialog->startConversation(_room->_conversationData, _room->_conversationDataSize, hotspot->index, animSet);
 }
 
@@ -901,6 +960,8 @@ void applyMovement(int16_t *x, int16_t *y, int8_t *z, uint16_t flags) {
 }
 
 void PelrockEngine::drawNextFrame(Sprite *sprite) {
+	if(sprite->index == 2)
+		debug("Drawing sprite %d at (%d,%d) zOrder %d, anim %d frame %d, movementFlags %d", sprite->index, sprite->x, sprite->y, sprite->zOrder, sprite->curAnimIndex, sprite->animData[sprite->curAnimIndex].curFrame, sprite->animData[sprite->curAnimIndex].movementFlags);
 	Anim &animData = sprite->animData[sprite->curAnimIndex];
 	if (sprite->zOrder == -1) {
 		// skips z0rder -1 sprites
@@ -936,6 +997,14 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 				animData.curLoop = 0;
 				if (sprite->curAnimIndex < sprite->numAnims - 1) {
 					sprite->curAnimIndex++;
+
+					// Trigger ring on phone on every start of animation 2
+					if (_room->_currentRoomNumber == 9 && sprite->index == 3) {
+						if (sprite->curAnimIndex == 1 && animData.curLoop == 0) {
+							byte soundFileIndex = _room->_roomSfx[2];
+							_sound->playSound(soundFileIndex, 2);
+						}
+					}
 				} else {
 					sprite->curAnimIndex = 0;
 				}
@@ -1286,6 +1355,12 @@ void PelrockEngine::walkTo(int x, int y) {
 	_alfredState.setState(ALFRED_WALKING);
 }
 
+void PelrockEngine::walkAndAction(HotSpot *hotspot, VerbIcon action) {
+	walkTo(hotspot->x + hotspot->w / 2, hotspot->y + hotspot->h);
+
+	_queuedAction = QueuedAction{action, hotspot->index, true};
+}
+
 AlfredDirection PelrockEngine::calculateAlfredsDirection(HotSpot *hotspot) {
 
 	AlfredDirection calculatedDirection = ALFRED_DOWN;
@@ -1420,7 +1495,6 @@ void PelrockEngine::checkMouseHover() {
 		hotspotDetected = true;
 	}
 
-
 	if (isActionUnder(_events->_mouseX, _events->_mouseY) != NO_ACTION) {
 		hotspotDetected = false;
 	}
@@ -1428,7 +1502,6 @@ void PelrockEngine::checkMouseHover() {
 	// Calculate walk target first (before checking anything else)
 	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, _events->_mouseX, _events->_mouseY, hotspotDetected, hotspotDetected ? &_room->_currentRoomHotspots[hotspotIndex] : nullptr);
 
-
 	bool alfredDetected = false;
 	if (isAlfredUnder(_events->_mouseX, _events->_mouseY)) {
 		alfredDetected = true;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 915b68ed8a8..c5927739af8 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -71,6 +71,7 @@ private:
 		Walking alforithm
 	*/
 	void walkTo(int x, int y);
+	void walkAndAction(HotSpot *hotspot, VerbIcon action);
 	AlfredDirection calculateAlfredsDirection(HotSpot *hotspot);
 
 	Common::Array<VerbIcon> availableActions(HotSpot *hotspot);
@@ -89,8 +90,10 @@ private:
 	void presentFrame();
 	void updatePaletteAnimations();
 	void paintDebugLayer();
-	void placeStickers();
+	void placeStickersFirstPass();
+	void placeStickersSecondPass();
 	void placeSticker(Sticker sticker);
+	void renderOverlay(int overlayMode);
 
 	void animateFadePalette(PaletteAnim *anim);
 	void animateRotatePalette(PaletteAnim *anim);
@@ -224,6 +227,7 @@ public:
 	void loadExtraScreenAndPresent(int screenIndex);
 	void waitForSpecialAnimation();
 	bool renderScene(int overlayMode = OVERLAY_NONE);
+	void frameTriggers();
 
 	void changeCursor(Cursor cursor);
 
@@ -282,6 +286,12 @@ public:
 	void giveIdToGuard(int inventoryObject, HotSpot *hotspot);
 	void giveMoneyToGuard(int inventoryObject, HotSpot *hotspot);
 	void openMuseumDoor(HotSpot *hotspot);
+	void useAmuletWithStatue(int inventoryObject, HotSpot *hotspot);
+	void pickUpLetter(HotSpot *hotspot);
+	void openLibraryOutdoorsDoor(HotSpot *hotspot);
+	void closeLibraryOutdoorsDoor(HotSpot *hotspot);
+	void openLibraryIndoorsDoor(HotSpot *hotspot);
+	void closeLibraryIndoorsDoor(HotSpot *hotspot);
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
 };
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 8917eb03092..e6641fb8712 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -23,6 +23,7 @@
 #include "pelrock/pelrock.h"
 #include "pelrock/room.h"
 #include "pelrock/util.h"
+#include "room.h"
 
 namespace Pelrock {
 
@@ -191,6 +192,11 @@ void RoomManager::moveHotspot(HotSpot *hotspot, int16 newX, int16 newY, bool per
 	}
 }
 
+void RoomManager::setActionMask(HotSpot *hotspot, byte actionMask) {
+	hotspot->actionFlags = actionMask;
+	changeHotSpot(*hotspot);
+}
+
 void RoomManager::addWalkbox(WalkBox walkbox) {
 	g_engine->_state->roomWalkBoxChanges[_currentRoomNumber].push_back({_currentRoomNumber, walkbox.index, walkbox});
 }
@@ -765,7 +771,7 @@ void RoomManager::addDisabledChoice(ChoiceOption choice) {
 	// Write 0xFA at offset+2 (after FB/F1 marker and level byte)
 	// This marks the choice as disabled without destroying the marker structure
 	uint32 disableOffset = choice.dataOffset + 2;
-	debug("Adding disabled branch for room %d at offset %d (FA written at %d)", 
+	debug("Adding disabled branch for room %d at offset %d (FA written at %d)",
 		  choice.room, choice.dataOffset, disableOffset);
 	ResetEntry resetEntry = ResetEntry();
 	resetEntry.room = choice.room;
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index cb676dbabd6..e837fe45a2d 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -45,6 +45,7 @@ static const int unpickableHotspotExtras[] = {
 	6,
 	7,
 	316, // wires
+	357, // mailbox should pick a different hotspot
 };
 
 class RoomManager {
@@ -86,6 +87,7 @@ public:
 	void disableHotspot(HotSpot *hotspot, bool persist = true);
 	void disableHotspot(byte room, HotSpot *hotspot, bool persist = true);
 	void moveHotspot(HotSpot *hotspot, int16 newX, int16 newY, bool persist = true);
+	void setActionMask(HotSpot *hotspot, byte actionMask);
 	void moveHotspot(byte room, HotSpot *hotspot, int16 newX, int16 newY, bool persist = true);
 	void addWalkbox(WalkBox walkbox);
 	void addWalkbox(byte room, WalkBox walkbox);
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index deea925d318..98f57125cc9 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -48,7 +48,7 @@ SoundManager::~SoundManager() {
 	stopMusic();
 }
 
-void SoundManager::playSound(byte index, int volume) {
+void SoundManager::playSound(byte index, int volume, int channel) {
 	// debug("Playing sound index %d (%s)", index, SOUND_FILENAMES[index]);
 	auto it = _soundMap.find(SOUND_FILENAMES[index]);
 	if (it != _soundMap.end()) {
@@ -58,7 +58,7 @@ void SoundManager::playSound(byte index, int volume) {
 	}
 }
 
-void SoundManager::playSound(SonidoFile sound, int volume) {
+void SoundManager::playSound(SonidoFile sound, int volume, int channel) {
 	Common::File sonidosFile;
 	if (!sonidosFile.open(Common::Path("SONIDOS.DAT"))) {
 		debug("Failed to open SONIDOS.DAT");
@@ -99,7 +99,15 @@ void SoundManager::playSound(SonidoFile sound, int volume) {
 	}
 
 	if (stream) {
-		int channel = findFreeChannel();
+		if(channel == -1) {
+			// Find a free channel
+		 	channel = findFreeChannel();
+		}
+		else {
+			if(_mixer->isSoundHandleActive(_sfxHandles[channel])) {
+				_mixer->stopHandle(_sfxHandles[channel]);
+			}
+		}
 		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, volume, 0, DisposeAfterUse::YES);
 	}
 }
@@ -149,7 +157,8 @@ int SoundManager::getSampleRate(byte *data, SoundFormat format) {
 }
 
 int SoundManager::findFreeChannel() {
-	for (int i = 0; i < kMaxChannels; i++) {
+	//Reserve first 3 channels for one-off sounds
+	for (int i = 3; i < kMaxChannels; i++) {
 		if (!_mixer->isSoundHandleActive(_sfxHandles[i])) {
 			return i;
 		}
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index 45d882fa1a3..5f0f90c895e 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -158,7 +158,7 @@ class SoundManager {
 public:
 	SoundManager(Audio::Mixer *mixer);
 	~SoundManager();
-	void playSound(byte index, int volume = 255);
+	void playSound(byte index, int volume = 255, int channel = -1);
 	void playSound(byte *soundData, uint32 size, int volume = 255);
 	void stopAllSounds();
 	void stopSound(int channel);
@@ -182,7 +182,7 @@ public:
 	int tickAmbientSound(uint32 frameCount);
 
 private:
-	void playSound(SonidoFile sound, int volume = 255);
+	void playSound(SonidoFile sound, int volume = 255, int channel = -1);
 	SoundFormat detectFormat(byte *data, uint32 size);
 	int getSampleRate(byte *data, SoundFormat format);
 	int findFreeChannel();
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 6203cf64bdb..424c4f005d2 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -35,6 +35,15 @@ enum Cursor {
 	COMBINATION
 };
 
+#define ACTION_MASK_NONE 0
+#define ACTION_MASK_OPEN 1
+#define ACTION_MASK_CLOSE 2
+#define ACTION_MASK_UNKNOWN 4
+#define ACTION_MASK_PICKUP 8
+#define ACTION_MASK_TALK 16
+#define ACTION_MASK_PUSH 32
+#define ACTION_MASK_PULL 128
+
 enum VerbIcon {
 	PICKUP,
 	TALK,
@@ -471,8 +480,8 @@ struct GameStateData {
 	Common::Array<byte> inventoryItems;
 	int16 selectedInventoryItem = -1;
 
+	int libraryShelf = -1;
 	Common::HashMap<byte, Common::Array<Sticker>> stickersPerRoom;
-	// Common::HashMap<byte, ResetEntry> roomExitChanges;
 	Common::HashMap<byte, Common::Array<ExitChange>> roomExitChanges;
 	Common::HashMap<byte, Common::Array<WalkBoxChange>> roomWalkBoxChanges;
 	Common::HashMap<byte, Common::Array<HotSpotChange>> roomHotSpotChanges;


Commit: 8fde0bac53520e097456df6ddbcaa1076bfa2cbb
    https://github.com/scummvm/scummvm/commit/8fde0bac53520e097456df6ddbcaa1076bfa2cbb
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:51+02:00

Commit Message:
PELROCK: Implements library mouse (pass-by animations)

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/chrono.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 5cc6b2a9517..f43d0d57aa2 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -97,6 +97,9 @@ const ActionEntry actionTable[] = {
 	// Room 9
 	{363, OPEN, &PelrockEngine::openLibraryIndoorsDoor},
 	{363, CLOSE, &PelrockEngine::closeLibraryIndoorsDoor},
+	{360, PICKUP, &PelrockEngine::pickBooksFromShelf1},
+	{361, PICKUP, &PelrockEngine::pickBooksFromShelf2},
+	{362, PICKUP, &PelrockEngine::pickBooksFromShelf3},
 
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
@@ -205,7 +208,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	} else if (actionTrigger == 263) {
 		_dialog->say(_res->_ingameTexts[UN_POCO_RESPETO]);
 	} else if (actionTrigger == 264) {
-		//disables the two first roots, the second one will be enabled later!
+		// disables the two first roots, the second one will be enabled later!
 		_state->setRootDisabledState(room, rootIndex, true);
 		_state->setRootDisabledState(room, rootIndex + 1, true);
 	} else {
@@ -604,6 +607,67 @@ void PelrockEngine::closeLibraryIndoorsDoor(HotSpot *hotspot) {
 	closeDoor(hotspot, 0, 28, FEMININE, false);
 }
 
+void PelrockEngine::pickBooksFromShelf1(HotSpot *hotspot) {
+	pickUpBook(0);
+}
+
+void PelrockEngine::pickBooksFromShelf2(HotSpot *hotspot) {
+	pickUpBook(1);
+}
+
+void PelrockEngine::pickBooksFromShelf3(HotSpot *hotspot) {
+	pickUpBook(2);
+}
+
+void PelrockEngine::pickUpBook(int i) {
+	if (!_state->hasInventoryItem(10)) {
+		_dialog->say(_res->_ingameTexts[VENGA_ACA]);
+		_state->setRootDisabledState(9, 0, true);
+		_alfredState.isWalkingCancelable = false;
+		walkAndAction(_room->findHotspotByExtra(102), TALK);
+	} else {
+		if (_state->libraryShelf == -1) {
+			_dialog->say(_res->_ingameTexts[TODOS]);
+		} else if (_state->libraryShelf != i) {
+			_dialog->say(_res->_ingameTexts[EL_LIBRO_NOESTA_AQUI]);
+		} else {
+			_state->libraryShelf = -1;
+		}
+	}
+	// if (AlfredActivity.stanteriaABuscar == -1)// no ha memorizado
+	// 												// ninguno
+	// 		AlfredActivity.myMovingAlfredThread
+	// 				.saySomething(AlfredActivity.extraThingsToTranslate[54]);
+	// 	else
+	// 	{
+	// 		if (AlfredActivity.stanteriaABuscar != i)// estanteria
+	// 													// equivocada
+	// 			AlfredActivity.myMovingAlfredThread
+	// 					.saySomething(AlfredActivity.extraThingsToTranslate[66]);
+	// 		else
+	// 		// coge el libro
+	// 		{
+
+	// 			AlfredActivity.stanteriaABuscar = -1;
+
+	// 			int hml = howManyLibrosTengo();
+
+	// 			if (hml == 3)
+	// 			{
+	// 				int in = getFirstBook();
+	// 				if (in != -1)
+	// 					AlfredActivity.removeUsingObject(in);
+
+	// 				AlfredActivity.myMovingAlfredThread
+	// 						.saySomething(AlfredActivity.extraThingsToTranslate[67]);
+	// 			}
+
+	// 			AlfredActivity
+	// 					.addInventoryItem(11 + AlfredActivity.newShowLibro.index);
+	// 		}
+	// 	}
+}
+
 void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	debug("Performing action trigger: %d", actionTrigger);
 	switch (actionTrigger) {
diff --git a/engines/pelrock/chrono.h b/engines/pelrock/chrono.h
index 295157816f2..f7f67229545 100644
--- a/engines/pelrock/chrono.h
+++ b/engines/pelrock/chrono.h
@@ -30,8 +30,7 @@ namespace Pelrock {
 // const int kTickMs = 15;
 // const int kTickMs = 33;
 // const int kTickMs = 38;
-// const int kTickMs = 55;
-const int kTickMs = 200;
+const int kTickMs = 55;
 // const int kTickMs = 60;
 const int kHalfTickMultiplier = 2;
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 5b45e3d5b31..29676cdef92 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -251,16 +251,22 @@ bool PelrockEngine::renderScene(int overlayMode) {
 	return false;
 }
 
-const int kPasserbyTriggerFrameInterval = 30;
+const int kPasserbyTriggerFrameInterval = 0x3FF;
 
 void PelrockEngine::frameTriggers() {
 	if ((_chrono->getFrameCount() & kPasserbyTriggerFrameInterval) == kPasserbyTriggerFrameInterval) {
 		debug("Would trigger passer-by");
-		switch (_room->_currentRoomNumber)
-		{
+		switch (_room->_currentRoomNumber) {
 		case 9: {
 			Sprite *mouse = _room->findSpriteByIndex(2);
-			mouse->zOrder = 3;
+			mouse->zOrder = 1;
+			mouse->animData[0].loopCount = 3;
+			mouse->animData[1].loopCount = 1;
+			mouse->animData[1].movementFlags = 0x3FF;
+			mouse->animData[2].loopCount = 1;
+			mouse->animData[2].movementFlags = 0x801F;
+			mouse->animData[3].loopCount = 3;
+			mouse->animData[3].movementFlags = 0x3E0;
 			break;
 		}
 		default:
@@ -268,29 +274,11 @@ void PelrockEngine::frameTriggers() {
 		}
 	}
 
-	if(_room->_currentRoomNumber == 9) {
+	if (_room->_currentRoomNumber == 9) {
+		// Mouse animation on library
 		Sprite *mouse = _room->findSpriteByIndex(2);
 		if (mouse) {
-			if(mouse->x > 130 && mouse->x < 200) {
-				debug("Moving mouse right and down");
-				if(mouse->curAnimIndex == 0)
-					mouse->curAnimIndex = 1;
-				mouse->y += 2;
-				mouse->x += 6;
-			}
-			else if(mouse->x > 200 && mouse->x < 340) {
-				debug("Moving mouse right");
-				if(mouse->curAnimIndex == 1)
-					mouse->curAnimIndex = 2;
-				mouse->x += 6;
-			}
-			else if(mouse->x > 340) {
-				debug("Moving mouse down");
-				if(mouse->curAnimIndex == 2)
-					mouse->curAnimIndex = 1;
-				mouse->y += 2;
-			}
-			if(mouse->x > 355) {
+			if (mouse->y > 355) {
 				mouse->x = 82;
 				mouse->y = 315;
 				mouse->zOrder = 255;
@@ -337,6 +325,14 @@ void PelrockEngine::executeAction(VerbIcon action, HotSpot *hotspot) {
 
 void PelrockEngine::checkMouse() {
 
+	checkMouseHover();
+	if (_alfredState.animState == ALFRED_WALKING && !_alfredState.isWalkingCancelable) {
+		// Ignore clicks while Alfred is walking
+		_events->_leftMouseClicked = false;
+		debug("Ignoring mouse click while Alfred is walking");
+		return;
+	}
+
 	// Cancel walking animation on mouse click
 	if (_events->_leftMouseButton) {
 		_alfredState.curFrame = 0;
@@ -364,6 +360,7 @@ void PelrockEngine::checkMouse() {
 			_currentHotspot = nullptr;
 		}
 	} else if (_events->_leftMouseClicked) {
+
 		// Regular click (not during popup mode)
 		checkMouseClick(_events->_mouseClickX, _events->_mouseClickY);
 		_events->_leftMouseClicked = false;
@@ -376,7 +373,6 @@ void PelrockEngine::checkMouse() {
 		_events->_rightMouseClicked = false;
 		_state->stateGame = SETTINGS;
 	}
-	checkMouseHover();
 }
 
 void PelrockEngine::copyBackgroundToBuffer() {
@@ -704,6 +700,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 				_currentStep = 0;
 				debug("Finished walking to target");
 				_alfredState.setState(ALFRED_IDLE);
+				_alfredState.isWalkingCancelable = true;
 
 				if (_currentHotspot != nullptr)
 					_alfredState.direction = calculateAlfredsDirection(_currentHotspot);
@@ -960,8 +957,6 @@ void applyMovement(int16_t *x, int16_t *y, int8_t *z, uint16_t flags) {
 }
 
 void PelrockEngine::drawNextFrame(Sprite *sprite) {
-	if(sprite->index == 2)
-		debug("Drawing sprite %d at (%d,%d) zOrder %d, anim %d frame %d, movementFlags %d", sprite->index, sprite->x, sprite->y, sprite->zOrder, sprite->curAnimIndex, sprite->animData[sprite->curAnimIndex].curFrame, sprite->animData[sprite->curAnimIndex].movementFlags);
 	Anim &animData = sprite->animData[sprite->curAnimIndex];
 	if (sprite->zOrder == -1) {
 		// skips z0rder -1 sprites
@@ -1357,7 +1352,6 @@ void PelrockEngine::walkTo(int x, int y) {
 
 void PelrockEngine::walkAndAction(HotSpot *hotspot, VerbIcon action) {
 	walkTo(hotspot->x + hotspot->w / 2, hotspot->y + hotspot->h);
-
 	_queuedAction = QueuedAction{action, hotspot->index, true};
 }
 
@@ -1453,6 +1447,7 @@ bool PelrockEngine::isAlfredUnder(int x, int y) {
 }
 
 void PelrockEngine::checkMouseClick(int x, int y) {
+
 	// This handles regular clicks (not popup selection)
 	_queuedAction = QueuedAction{NO_ACTION, -1, false};
 	_actionPopupState.isActive = false;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index c5927739af8..052b418c668 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -166,6 +166,7 @@ public:
 	AlfredState _alfredState;
 	byte *_compositeBuffer = nullptr; // Working composition buffer
 
+	bool _mouseDisabled = false;
 	GameStateData *_state = new GameStateData();
 
 	SmallFont *_smallFont = nullptr;
@@ -292,6 +293,10 @@ public:
 	void closeLibraryOutdoorsDoor(HotSpot *hotspot);
 	void openLibraryIndoorsDoor(HotSpot *hotspot);
 	void closeLibraryIndoorsDoor(HotSpot *hotspot);
+	void pickBooksFromShelf1(HotSpot *hotspot);
+	void pickBooksFromShelf2(HotSpot *hotspot);
+	void pickBooksFromShelf3(HotSpot *hotspot);
+	void pickUpBook(int i);
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
 };
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index e837fe45a2d..00d231976f7 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -45,7 +45,10 @@ static const int unpickableHotspotExtras[] = {
 	6,
 	7,
 	316, // wires
-	357, // mailbox should pick a different hotspot
+	357, // mailbox should pick a different hotspot,
+	360,  // library shelves
+	361,
+	362,
 };
 
 class RoomManager {
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 424c4f005d2..329190948f9 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -165,6 +165,7 @@ struct AlfredState {
 	uint16 scaledX = 0;
 	uint16 scaledY = 0;
 	int idleFrameCounter = 0;
+	bool isWalkingCancelable = true;
 
 	void setState(AlfredAnimState nextState) {
 		animState = nextState;


Commit: 83b8fb17e9e678bf9c0019d6608349fbfdbb9165
    https://github.com/scummvm/scummvm/commit/83b8fb17e9e678bf9c0019d6608349fbfdbb9165
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:51+02:00

Commit Message:
PELROCK: Computer-book logic

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/computer.cpp
    engines/pelrock/computer.h
    engines/pelrock/dialog.h
    engines/pelrock/library_books.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index f43d0d57aa2..ab4c33c2d88 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -608,15 +608,15 @@ void PelrockEngine::closeLibraryIndoorsDoor(HotSpot *hotspot) {
 }
 
 void PelrockEngine::pickBooksFromShelf1(HotSpot *hotspot) {
-	pickUpBook(0);
+	pickUpBook(1);
 }
 
 void PelrockEngine::pickBooksFromShelf2(HotSpot *hotspot) {
-	pickUpBook(1);
+	pickUpBook(2);
 }
 
 void PelrockEngine::pickBooksFromShelf3(HotSpot *hotspot) {
-	pickUpBook(2);
+	pickUpBook(3);
 }
 
 void PelrockEngine::pickUpBook(int i) {
@@ -632,40 +632,17 @@ void PelrockEngine::pickUpBook(int i) {
 			_dialog->say(_res->_ingameTexts[EL_LIBRO_NOESTA_AQUI]);
 		} else {
 			_state->libraryShelf = -1;
+			int booksInInventory = _state->booksInInventory();
+			if (booksInInventory == 3) {
+				int firstBook = _state->findFirstBookIndex();
+				if(firstBook != -1)
+					_state->removeInventoryItem(firstBook);
+				_dialog->say(_res->_ingameTexts[TENDRE_DEJAR_LIBRO]);
+			}
+			_state->addInventoryItem(_state->selectedBookIndex);
+			_state->selectedBookIndex = -1;
 		}
 	}
-	// if (AlfredActivity.stanteriaABuscar == -1)// no ha memorizado
-	// 												// ninguno
-	// 		AlfredActivity.myMovingAlfredThread
-	// 				.saySomething(AlfredActivity.extraThingsToTranslate[54]);
-	// 	else
-	// 	{
-	// 		if (AlfredActivity.stanteriaABuscar != i)// estanteria
-	// 													// equivocada
-	// 			AlfredActivity.myMovingAlfredThread
-	// 					.saySomething(AlfredActivity.extraThingsToTranslate[66]);
-	// 		else
-	// 		// coge el libro
-	// 		{
-
-	// 			AlfredActivity.stanteriaABuscar = -1;
-
-	// 			int hml = howManyLibrosTengo();
-
-	// 			if (hml == 3)
-	// 			{
-	// 				int in = getFirstBook();
-	// 				if (in != -1)
-	// 					AlfredActivity.removeUsingObject(in);
-
-	// 				AlfredActivity.myMovingAlfredThread
-	// 						.saySomething(AlfredActivity.extraThingsToTranslate[67]);
-	// 			}
-
-	// 			AlfredActivity
-	// 					.addInventoryItem(11 + AlfredActivity.newShowLibro.index);
-	// 		}
-	// 	}
 }
 
 void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
diff --git a/engines/pelrock/computer.cpp b/engines/pelrock/computer.cpp
index 3b60c673edf..4b675b82dec 100644
--- a/engines/pelrock/computer.cpp
+++ b/engines/pelrock/computer.cpp
@@ -23,6 +23,7 @@
 #include "common/system.h"
 #include "graphics/paletteman.h"
 
+#include "computer.h"
 #include "pelrock/computer.h"
 #include "pelrock/library_books.h"
 #include "pelrock/pelrock.h"
@@ -56,8 +57,51 @@ Computer::Computer(PelrockEventManager *eventMan)
 	_optCancelar = "(C)ancelar";
 	_noResults = "No se encontraron libros";
 	_memorizedMsg = "Bueno... Tendre que buscar en la estanteria de la %c";
+
+	init();
 }
 
+void Computer::init() {
+	Common::File alfred7File;
+	if (!alfred7File.open("ALFRED.7")) {
+		error("Could not open ALFRED.7");
+		return;
+	}
+
+	alfred7File.seek(kBookDataOffset, SEEK_SET);
+	int index = 0;
+	int index2 = 0;
+	while (alfred7File.pos() < kBookDataEnd) {
+		LibraryBook book;
+
+		book.title = alfred7File.readString(0, kBookTitleSize);
+		book.author = alfred7File.readString(0, kBookAuthorSize);
+		book.genre = alfred7File.readString(0, kBookGenreSize);
+		book.title.trim();
+		book.author.trim();
+		book.genre.trim();
+		book.inventoryIndex = alfred7File.readByte() - 55;
+		book.shelf = alfred7File.readByte();
+		book.available = alfred7File.readByte() == 2;
+		// book.index = book.available ? index++ : -1;
+		// book.index2 = index2++;
+		_libraryBooks.push_back(book);
+	}
+
+	for(int i = 0; i < _libraryBooks.size(); i++) {
+		const LibraryBook &book = _libraryBooks[i];
+		debug("Loaded book: title='%s', author='%s', genre='%s', unknown=%d, shelf=%d, available=%d",
+			  book.title.c_str(), book.author.c_str(), book.genre.c_str(),
+			  book.inventoryIndex, book.shelf, book.available);
+	}
+
+	_searchResults.clear();
+	_currentResult = 0;
+	_searchLetter = 0;
+	_memorizedBookIndex = -1;
+}
+
+
 Computer::~Computer() {
 	cleanup();
 }
@@ -137,66 +181,64 @@ void Computer::drawScreen() {
 	case STATE_SEARCH_BY_TITLE:
 	case STATE_SEARCH_BY_AUTHOR:
 		g_engine->_smallFont->drawString(g_engine->_screen,
-			_searchType == 0 ? "CONSULTAR POR TITULO" : "CONSULTAR POR AUTOR",
-			textX, textY, 280, 15, Graphics::kTextAlignCenter);
+										 _searchType == 0 ? "CONSULTAR POR TITULO" : "CONSULTAR POR AUTOR",
+										 textX, textY, 280, 15, Graphics::kTextAlignCenter);
 		g_engine->_smallFont->drawString(g_engine->_screen, _promptLetter, textX, textY + 40, 280, 14);
 		break;
 
-	case STATE_SHOW_RESULTS:
-		{
-			Common::String header = Common::String::format(
-				"Consulta de %s, letra %c",
-				_searchType == 0 ? "TITULO" : "AUTOR",
-				_searchLetter);
-			g_engine->_smallFont->drawString(g_engine->_screen, header, textX, textY, 280, 15, Graphics::kTextAlignCenter);
+	case STATE_SHOW_RESULTS: {
+		Common::String header = Common::String::format(
+			"Consulta de %s, letra %c",
+			_searchType == 0 ? "TITULO" : "AUTOR",
+			_searchLetter);
+		g_engine->_smallFont->drawString(g_engine->_screen, header, textX, textY, 280, 15, Graphics::kTextAlignCenter);
+
+		if (_searchResults.empty()) {
+			g_engine->_smallFont->drawString(g_engine->_screen, _noResults, textX, textY + 50, 280, 14);
+		} else {
+			// Display current book
+			int bookIdx = _searchResults[_currentResult];
+			const LibraryBook &book = _libraryBooks[bookIdx];
+
+			// Title (may be long, truncate if needed)
+			Common::String titleLine = Common::String::format("%s%s", _labelTitle, book.title.c_str());
+			g_engine->_smallFont->drawString(g_engine->_screen, titleLine, textX - 50, textY + 40, 340, 14);
 
-			if (_searchResults.empty()) {
-				g_engine->_smallFont->drawString(g_engine->_screen, _noResults, textX, textY + 50, 280, 14);
+			// Author
+			Common::String authorLine = Common::String::format("%s%s", _labelAuthor, book.author.c_str());
+			g_engine->_smallFont->drawString(g_engine->_screen, authorLine, textX - 50, textY + 60, 340, 14);
+
+			// Genre
+			Common::String genreLine = Common::String::format("%s%s", _labelGenre, book.genre.c_str());
+			g_engine->_smallFont->drawString(g_engine->_screen, genreLine, textX - 50, textY + 80, 340, 14);
+
+			// Situacion (location/availability)
+			Common::String situacionLine;
+			if (book.available) {
+				situacionLine = Common::String::format("%s Estanteria %d",
+													   _labelSituacion, book.shelf);
+			} else {
+				situacionLine = Common::String::format("%s%s",
+													   _labelSituacion, _statusCatalogOnly);
+			}
+			g_engine->_smallFont->drawString(g_engine->_screen, situacionLine, textX - 50, textY + 100, 340,
+											 book.available ? 10 : 8); // Green if physical, gray if catalog-only
+
+			// Show result counter
+			Common::String counter = Common::String::format("Libro %d de %d",
+															_currentResult + 1, (int)_searchResults.size());
+			g_engine->_smallFont->drawString(g_engine->_screen, counter, textX, textY + 130, 280, 14, Graphics::kTextAlignCenter);
+
+			// Show navigation options
+			Common::String navOptions;
+			if (book.available) {
+				navOptions = Common::String::format("%s   %s   %s", _optMemorizar, _optSeguir, _optCancelar);
 			} else {
-				// Display current book
-				int bookIdx = _searchResults[_currentResult];
-				const LibraryBook &book = kLibraryBooks[bookIdx];
-
-				// Title (may be long, truncate if needed)
-				Common::String titleLine = Common::String::format("%s%s", _labelTitle, book.title);
-				g_engine->_smallFont->drawString(g_engine->_screen, titleLine, textX - 50, textY + 40, 340, 14);
-
-				// Author
-				Common::String authorLine = Common::String::format("%s%s", _labelAuthor, book.author);
-				g_engine->_smallFont->drawString(g_engine->_screen, authorLine, textX - 50, textY + 60, 340, 14);
-
-				// Genre
-				Common::String genreLine = Common::String::format("%s%s", _labelGenre, book.genre);
-				g_engine->_smallFont->drawString(g_engine->_screen, genreLine, textX - 50, textY + 80, 340, 14);
-
-				// Situacion (location/availability)
-				Common::String situacionLine;
-				if (book.available) {
-					situacionLine = Common::String::format("%sEstante %c, fila %d",
-						_labelSituacion, book.shelfLetter, book.shelfRow);
-				} else {
-					situacionLine = Common::String::format("%s%s",
-						_labelSituacion, _statusCatalogOnly);
-				}
-				g_engine->_smallFont->drawString(g_engine->_screen, situacionLine, textX - 50, textY + 100, 340,
-					book.available ? 10 : 8); // Green if physical, gray if catalog-only
-
-				// Show result counter
-				Common::String counter = Common::String::format("Libro %d de %d",
-					_currentResult + 1, (int)_searchResults.size());
-				g_engine->_smallFont->drawString(g_engine->_screen, counter, textX, textY + 130, 280, 14, Graphics::kTextAlignCenter);
-
-				// Show navigation options
-				Common::String navOptions;
-				if (book.available) {
-					navOptions = Common::String::format("%s   %s   %s", _optMemorizar, _optSeguir, _optCancelar);
-				} else {
-					navOptions = Common::String::format("%s   %s", _optSeguir, _optCancelar);
-				}
-				g_engine->_smallFont->drawString(g_engine->_screen, navOptions, textX, textY + 160, 280, 8, Graphics::kTextAlignCenter);
+				navOptions = Common::String::format("%s   %s", _optSeguir, _optCancelar);
 			}
+			g_engine->_smallFont->drawString(g_engine->_screen, navOptions, textX, textY + 160, 280, 8, Graphics::kTextAlignCenter);
 		}
-		break;
+	} break;
 
 	case STATE_EXIT:
 		break;
@@ -219,7 +261,7 @@ void Computer::handleMainMenu() {
 
 void Computer::handleSearchInput() {
 	if (_events->_lastKeyEvent >= Common::KEYCODE_a &&
-	    _events->_lastKeyEvent <= Common::KEYCODE_z) {
+		_events->_lastKeyEvent <= Common::KEYCODE_z) {
 		_searchLetter = 'A' + (_events->_lastKeyEvent - Common::KEYCODE_a);
 		performSearch();
 		_currentResult = 0;
@@ -237,11 +279,10 @@ void Computer::handleResultsDisplay() {
 			_currentResult = (_currentResult + 1) % _searchResults.size();
 		}
 		_events->_lastKeyEvent = Common::KEYCODE_INVALID;
-	}
-	else if (_events->_lastKeyEvent == Common::KEYCODE_m) {
+	} else if (_events->_lastKeyEvent == Common::KEYCODE_m) {
 		if (!_searchResults.empty()) {
 			int bookIdx = _searchResults[_currentResult];
-			const LibraryBook &book = kLibraryBooks[bookIdx];
+			const LibraryBook &book = _libraryBooks[bookIdx];
 			if (book.available) {
 				memorizeBook(bookIdx);
 			}
@@ -250,35 +291,35 @@ void Computer::handleResultsDisplay() {
 	}
 	// C key or ESC - Cancel (return to main menu)
 	else if (_events->_lastKeyEvent == Common::KEYCODE_c ||
-	         _events->_lastKeyEvent == Common::KEYCODE_ESCAPE) {
+			 _events->_lastKeyEvent == Common::KEYCODE_ESCAPE) {
 		_state = STATE_MAIN_MENU;
 		_events->_lastKeyEvent = Common::KEYCODE_INVALID;
+		g_engine->_state->libraryShelf = -1; // 0-based shelf index
+		g_engine->_state->selectedBookIndex = -1;
+		g_engine->_state->bookLetter = '\0';
 	}
 }
 
 void Computer::memorizeBook(int bookIndex) {
-	const LibraryBook &book = kLibraryBooks[bookIndex];
+	const LibraryBook &book = _libraryBooks[bookIndex];
 
 	// Store the memorized book for later pickup from the shelf
 	_memorizedBookIndex = bookIndex;
 
-	// In the original game, Alfred says "Bueno... Tendre que buscar en la estanteria de la X"
-	// where X is the shelf letter
-	// TODO: Play the dialog and set a game flag so the book can be picked up from the shelf
-
-	// For now, just exit the computer interface
-	// The game state should be updated to allow picking up this book from shelf X
 	_state = STATE_EXIT;
 
-	debug(1, "Memorized book '%s' at shelf %c, row %d", book.title, book.shelfLetter, book.shelfRow);
+	g_engine->_state->libraryShelf = book.shelf; // 0-based shelf index
+	g_engine->_state->selectedBookIndex = book.inventoryIndex;
+	g_engine->_state->bookLetter = book.title[0];
+
+	debug("Memorized book '%s' with index %d, shelf %d, letter %c", book.title.c_str(), g_engine->_state->selectedBookIndex, g_engine->_state->libraryShelf, g_engine->_state->bookLetter);
 }
 
 void Computer::performSearch() {
 	_searchResults.clear();
 
-	for (int i = 0; i < kLibraryBookCount; i++) {
-		const char *searchField = _searchType == 0 ?
-			kLibraryBooks[i].title : kLibraryBooks[i].author;
+	for (int i = 0; i < _libraryBooks.size(); i++) {
+		Common::String searchField = _searchType == 0 ? _libraryBooks[i].title : _libraryBooks[i].author;
 
 		// Check if first letter matches (case-insensitive)
 		char firstChar = searchField[0];
diff --git a/engines/pelrock/computer.h b/engines/pelrock/computer.h
index a9c3f7e1e00..7f0117c9f4c 100644
--- a/engines/pelrock/computer.h
+++ b/engines/pelrock/computer.h
@@ -26,6 +26,7 @@
 #include "common/str.h"
 
 #include "pelrock/events.h"
+#include "pelrock/library_books.h"
 
 namespace Pelrock {
 
@@ -34,12 +35,14 @@ class PelrockEngine;
 class Computer {
 public:
 	Computer(PelrockEventManager *eventMan);
+
 	~Computer();
 
 	/**
 	 * @return Book index if a book was memorized, -1 otherwise
 	 */
 	int run();
+	const char *_memorizedMsg;      // "Bueno... Tendre que buscar en la estanteria de la %c"
 
 private:
 	enum ComputerState {
@@ -59,6 +62,7 @@ private:
 	char _searchLetter;
 	int _searchType;  // 0 = title, 1 = author
 	Common::Array<int> _searchResults;
+	Common::Array<LibraryBook> _libraryBooks;
 	int _currentResult;
 	int _memorizedBookIndex;  // Index of book that was memorized (-1 if none)
 
@@ -77,7 +81,6 @@ private:
 	const char *_optSeguir;         // "(S)eguir"
 	const char *_optCancelar;       // "(C)ancelar"
 	const char *_noResults;         // "No se encontraron libros"
-	const char *_memorizedMsg;      // "Bueno... Tendre que buscar en la estanteria de la %c"
 
 	void loadBackground();
 	void cleanup();
@@ -87,6 +90,7 @@ private:
 	void performSearch();
 	void drawScreen();
 	void memorizeBook(int bookIndex);
+	void init();
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 7719110b719..03c578ca35e 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -65,7 +65,6 @@ private:
 	uint32 parseChoices(const byte *data, uint32 dataSize, uint32 startPos, Common::Array<ChoiceOption> *outChoices);
 	void setCurSprite(int index);
 	void checkMouse();
-	void sayAlfred(Common::StringArray texts);
 	bool checkAllSubBranchesExhausted(const byte *data, uint32 dataSize, uint32 startPos, int currentChoiceLevel);
 
 public:
@@ -76,6 +75,7 @@ public:
 	int selectChoice(Common::Array<Common::String> &choices, byte *compositeBuffer);
 	void startConversation(const byte *conversationData, uint32 dataSize, byte npcIndex, Sprite *alfredAnimSet = nullptr);
 	void sayAlfred(Description description);
+	void sayAlfred(Common::StringArray texts);
 	void say(Common::StringArray texts, byte spriteIndex = 0);
 	void say(Common::StringArray texts, int16 x, int16 y);
 	bool processColorAndTrim(Common::StringArray &lines, byte &speakerId);
diff --git a/engines/pelrock/library_books.h b/engines/pelrock/library_books.h
index a86a990948e..15a6491b98e 100644
--- a/engines/pelrock/library_books.h
+++ b/engines/pelrock/library_books.h
@@ -37,268 +37,271 @@ static const byte kBookStatusCatalogOnly = 0x01;  // No physical copy
 static const byte kBookStatusPhysical = 0x02;     // Has physical copy on shelf
 
 struct LibraryBook {
-    const char *title;
-    const char *author;
-    const char *genre;
-    char shelfLetter;    // A-Z for shelf location, space if catalog-only
-    byte shelfRow;       // 1-3 for row number, 0 if catalog-only
+    Common::String title; //55 bytes for title
+    Common::String author; //30 bytes for author
+    Common::String genre; //20 bytes for genre
+    byte inventoryIndex;
+    byte shelf;       // 1-3 for row number, 0 if catalog-only
     bool available;  // true = can be found on shelf, false = catalog only
 };
 
-static const int kLibraryBookCount = 125;
+const LibraryBook noBook = {"", "", "", 0, 0, false};
 
-static const LibraryBook kLibraryBooks[] = {
-    // Book 1: Los hombres: ¡ Como caparlos !
-    {"Los hombres: ¡ Como caparlos !", "Herminia Gutierrez", "Feminismo", ' ', 0, false},
-    // Book 2: Mujeres del mundo: ¡ No os depileis las ...
-    {"Mujeres del mundo: ¡ No os depileis las axilas !", "Herminia Gutierrez", "Feminismo", ' ', 0, false},
-    // Book 3: Gato por liebre
-    {"Gato por liebre", "Karlos Arguiñano", "Cocina", 'I', 1, true},
-    // Book 4: Hamlet
-    {"Hamlet", "William Shakespeare", "Teatro", ' ', 0, false},
-    // Book 5: Fausto
-    {"Fausto", "Goethe", "Novela", ' ', 0, false},
-    // Book 6: Enrique de Ofterdingen
-    {"Enrique de Ofterdingen", "Novalis", "Novela", 'J', 1, true},
-    // Book 7: Guia de desobediencia civica
-    {"Guia de desobediencia civica", "Azagra", "Ensayo", ' ', 0, false},
-    // Book 8: Literatura en la edad de piedra: cuestio...
-    {"Literatura en la edad de piedra: cuestion de fuerza", "Profesor Cebollo", "Ensayo", ' ', 0, false},
-    // Book 9: Turbo C ++ con Intratex
-    {"Turbo C ++ con Intratex", "Programadores reunidos", "Informatica", ' ', 0, false},
-    // Book 10: Codigo Maquina a pelo
-    {"Codigo Maquina a pelo", "Programadores reunidos", "Informatica", 'K', 1, true},
-    // Book 11: Asesino por vocacion
-    {"Asesino por vocacion", "Chema Ton", "Novela negra", ' ', 0, false},
-    // Book 12: Poemas rebuscados
-    {"Poemas rebuscados", "Ramon Rodriguez", "Poesia", ' ', 0, false},
-    // Book 13: El parto de las tortugas: Un proceso len...
-    {"El parto de las tortugas: Un proceso lento", "Profesor Lorin Colorado", "Biologia", ' ', 0, false},
-    // Book 14: Te parto la cara ¡ Capuyo !
-    {"Te parto la cara ¡ Capuyo !", "Jhonny Rapper", "Critica Social", 'L', 3, true},
-    // Book 15: En los tugurios del Himalaya
-    {"En los tugurios del Himalaya", "Coronel Tapioca", "Aventuras", ' ', 0, false},
-    // Book 16: En las callejuelas del amazonas
-    {"En las callejuelas del amazonas", "Coronel Tapioca", "Aventuras", ' ', 0, false},
-    // Book 17: En las selvas de Londres
-    {"En las selvas de Londres", "Coronel Tapioca", "Aventuras", 'M', 1, true},
-    // Book 18: Sueños binarios
-    {"Sueños binarios", "Doctor Chip", "Ciencia ficcion", ' ', 0, false},
-    // Book 19: Cien cuentos cortisimos
-    {"Cien cuentos cortisimos", "Billi el rapido", "Novela", ' ', 0, false},
-    // Book 20: Teoria de la relatividad
-    {"Teoria de la relatividad", "Alfred Einstein", "Ciencia", ' ', 0, false},
-    // Book 21: El ultimo paso para la cuadratura del ci...
-    {"El ultimo paso para la cuadratura del circulo", "Anonimo", "Filosofia", 'N', 1, true},
-    // Book 22: Correlacion entre el sentido de los colo...
-    {"Correlacion entre el sentido de los colores y sonidos", "Anonimo", "Filosofia", ' ', 0, false},
-    // Book 23: Los cuatro evangelios: Interpretados por...
-    {"Los cuatro evangelios: Interpretados por ordenador", "Doctor Chip", "Teologia", ' ', 0, false},
-    // Book 24: Enciclopedia de bolsillo
-    {"Enciclopedia de bolsillo", "Profesor Lumbreras", "Enciclopedia", 'O', 1, true},
-    // Book 25: Sigueme y revienta
-    {"Sigueme y revienta", "M. Indurain", "Deporte", 'P', 3, true},
-    // Book 26: Me duele too...
-    {"Me duele too...", "Carmen Opausica", "Ensayo", ' ', 0, false},
-    // Book 27: El Perro de Sam Rocker si tiene rabo
-    {"El Perro de Sam Rocker si tiene rabo", "El Perro de Sam Rocker", "Biografia", ' ', 0, false},
-    // Book 28: Donde estara mi carro ?
-    {"Donde estara mi carro ?", "Manolo Escobar", "Aventuras", ' ', 0, false},
-    // Book 29: Oh tu, bella flor del jardin
-    {"Oh tu, bella flor del jardin", "La abeja Maya", "Poesia", 'Q', 2, true},
-    // Book 30: Yogui, ¡ Bajate de esa motoneta !
-    {"Yogui, ¡ Bajate de esa motoneta !", "Bubu", "Filosofia", ' ', 0, false},
-    // Book 31: Odio a muerte a loh mardito rohedore !
-    {"Odio a muerte a loh mardito rohedore !", "Er gato Yin", "Zoologia", ' ', 0, false},
-    // Book 32: Pissi, Dissi, ¡ Sargan de su aguhero !
-    {"Pissi, Dissi, ¡ Sargan de su aguhero !", "Er gato Yin", "Zoologia", ' ', 0, false},
-    // Book 33: Mardito sea ... ¡ Er queso !
-    {"Mardito sea ... ¡ Er queso !", "Er gato Yin", "Zoologia", ' ', 0, false},
-    // Book 34: La mate porque era mia
-    {"La mate porque era mia", "Loquillo", "Novela", ' ', 0, false},
-    // Book 35: Los gallos: Esos desconocidos
-    {"Los gallos: Esos desconocidos", "Paco rico", "Zoologia", ' ', 0, false},
-    // Book 36: Cuentos corrientes
-    {"Cuentos corrientes", "Pepe Lopez", "Novela", 'R', 1, true},
-    // Book 37: Mas madera
-    {"Mas madera", "R. Gepetto", "Bricolage", 'S', 2, true},
-    // Book 38: Cuentos del Lejano Oriente
-    {"Cuentos del Lejano Oriente", "Jhonny Mc. Dowall", "Novela", ' ', 0, false},
-    // Book 39: Saca el guiski cheli
-    {"Saca el guiski cheli", "Manolo lailo", "Cronica Social", ' ', 0, false},
-    // Book 40: Ta totao !
-    {"Ta totao !", "Lao Tse", "Filosofia", ' ', 0, false},
-    // Book 41: Mis conversaciones con el señor Roca
-    {"Mis conversaciones con el señor Roca", "Francisca Gando", "Epistolar", 'T', 2, true},
-    // Book 42: Guia para la supervivencia
-    {"Guia para la supervivencia", "Robinson Crusoe", "Manual", ' ', 0, false},
-    // Book 43: No esperes a ser la segunda: ¡ Engaña a ...
-    {"No esperes a ser la segunda: ¡ Engaña a tu marido !", "Herminia Gutierrez", "Feminismo", ' ', 0, false},
-    // Book 44: Yoga Sutras
-    {"Yoga Sutras", "Patanjali", "Filosofia", 'X', 3, true},
-    // Book 45: El juego de los Abalorios
-    {"El juego de los Abalorios", "Herman Hesse", "Novela", ' ', 0, false},
-    // Book 46: Tienes suerte de ser tan pequeño, ¡ mard...
-    {"Tienes suerte de ser tan pequeño, ¡ mardito roedo !", "Er gato Yin", "Novela", ' ', 0, false},
-    // Book 47: Como hacerse rico en diez minutos
-    {"Como hacerse rico en diez minutos", "El Dioni", "Manual", 'Y', 1, true},
-    // Book 48: Te querre a pesar de tu madre
-    {"Te querre a pesar de tu madre", "Corin Tellado", "Novela rosa", ' ', 0, false},
-    // Book 49: Hasta que el mando a distancia nos separ...
-    {"Hasta que el mando a distancia nos separe", "Corin Tellado", "Novela rosa", ' ', 0, false},
-    // Book 50: Una pasion ostentorea
-    {"Una pasion ostentorea", "Corin Tellado y Jesus Gil", "Novela rosa", 'Z', 3, true},
-    // Book 51: Por mi, como si te la machacas
-    {"Por mi, como si te la machacas", "Seneca", "Filosofia", ' ', 0, false},
-    // Book 52: Conversaciones con mi caballo
-    {"Conversaciones con mi caballo", "Jesus Gil", "Humor", ' ', 0, false},
-    // Book 53: El poder curativo de la mierda comun
-    {"El poder curativo de la mierda comun", "Sri Ramachrinaraska", "Esoterismo", ' ', 1, true},
-    // Book 54: La liberacion mediante la eneriga de los...
-    {"La liberacion mediante la eneriga de los pedos", "Sri Ramachrinaraska", "Esoterismo", ' ', 0, false},
-    // Book 55: Sobre la imperceptibilidad de lo imperce...
-    {"Sobre la imperceptibilidad de lo imperceptible", "Perogrullo", "Ensayo", ' ', 0, false},
-    // Book 56: Piojos; como educarlos sin traumas
-    {"Piojos; como educarlos sin traumas", "Franz Franzfrenz", "Psicologia", ' ', 0, false},
-    // Book 57: I Ching
-    {"I Ching", "Richard Willem", "Filosofia", ' ', 0, false},
-    // Book 58: No se nada, ni me importa
-    {"No se nada, ni me importa", "Socrates", "Filosofia", 'U', 2, true},
-    // Book 59: No he sido yo, ¡ Lo juro !
-    {"No he sido yo, ¡ Lo juro !", "Juan Jose Gil", "Biografia", ' ', 0, false},
-    // Book 60: Relatos cortos
-    {"Relatos cortos", "Tachenko", "Novela de ficcion", 'V', 3, true},
-    // Book 61: El martillo como elemento cognitivo
-    {"El martillo como elemento cognitivo", "Friedrich Nietzsche", "Filosofia", ' ', 0, false},
-    // Book 62: La maravillosa vida del escarabajo pelot...
-    {"La maravillosa vida del escarabajo pelotero (v. I)", "Dr. Federico Ñazo", "Botanica", ' ', 0, false},
-    // Book 63: La maravillosa vida del escarabajo pelot...
-    {"La maravillosa vida del escarabajo pelotero (v. II)", "Dr. Federico Ñazo", "Botanica", ' ', 0, false},
-    // Book 64: La maravillosa vida del escarabajo pelot...
-    {"La maravillosa vida del escarabajo pelotero (v. III)", "Dr. Federico Ñazo", "Botanica", ' ', 0, false},
-    // Book 65: La maravillosa vida del escarabajo pelot...
-    {"La maravillosa vida del escarabajo pelotero (v. IV)", "Dr. Federico Ñazo", "Botanica", ' ', 0, false},
-    // Book 66: La maravillosa vida del escarabajo pelot...
-    {"La maravillosa vida del escarabajo pelotero (v. V)", "Dr. Federico Ñazo", "Botanica", ' ', 0, false},
-    // Book 67: Tu eliges: Tu madre o yo
-    {"Tu eliges: Tu madre o yo", "Anonimo", "Psicologia aplicada", 'W', 3, true},
-    // Book 68: Mi lucha
-    {"Mi lucha", "Adolf Hitler", "Humor negro", ' ', 0, false},
-    // Book 69: Cuentos de amor y desidia
-    {"Cuentos de amor y desidia", "Jardiel Poncela", "Teatro", ' ', 1, true},
-    // Book 70: Nuevas andanzas de Zaratustra
-    {"Nuevas andanzas de Zaratustra", "Anonimo", "Aventuras", ' ', 2, true},
-    // Book 71: Me se cuadre ¡ Coño !
-    {"Me se cuadre ¡ Coño !", "Sargento Cienfuegos", "Etica militar", ' ', 2, true},
-    // Book 72: Gatos: solos o con Ketchup
-    {"Gatos: solos o con Ketchup", "El perro de Sam Rocker", "Cocina", ' ', 1, true},
-    // Book 73: Querida Adelaida: mi marido NO ha dejado...
-    {"Querida Adelaida: mi marido NO ha dejado de roncar", "Maruja Mones", "Epistolar", ' ', 0, false},
-    // Book 74: Sobre el papel de El Lepe en el nuevo Or...
-    {"Sobre el papel de El Lepe en el nuevo Orden Mundial", "General Sintacha", "Estrategia", ' ', 0, false},
-    // Book 75: Aqui no hay nadie que se acueste sin cen...
-    {"Aqui no hay nadie que se acueste sin cenar (v. I)", "Fidel Castro", "Politica", ' ', 1, true},
-    // Book 76: Aqui no hay nadie que se acueste sin cen...
-    {"Aqui no hay nadie que se acueste sin cenar (v. II)", "Fidel Castro", "Politica", ' ', 0, false},
-    // Book 77: Aqui no hay nadie que se acueste sin cen...
-    {"Aqui no hay nadie que se acueste sin cenar (v. III)", "Fidel Castro", "Politica", ' ', 0, false},
-    // Book 78: Aqui no hay nadie que se acueste sin cen...
-    {"Aqui no hay nadie que se acueste sin cenar (v. IV)", "Fidel Castro", "Politica", ' ', 0, false},
-    // Book 79: Aqui no hay nadie que se acueste sin cen...
-    {"Aqui no hay nadie que se acueste sin cenar (v. V)", "Fidel Castro", "Politica", ' ', 0, false},
-    // Book 80: Domine la metafisica en 5 dias
-    {"Domine la metafisica en 5 dias", "Profesor Pinocho", "Manual", ' ', 1, true},
-    // Book 81: Domine el ensamblador en 5 dias
-    {"Domine el ensamblador en 5 dias", "Profesor Pinocho", "Manual", ' ', 1, true},
-    // Book 82: Dominese a si mismo en 5 dias
-    {"Dominese a si mismo en 5 dias", "Profesor Pinocho", "Manual", ' ', 1, true},
-    // Book 83: Piernas de Ciertopelo
-    {"Piernas de Ciertopelo", "Chichi Mondongo", "Erotica", ' ', 2, true},
-    // Book 84: Otra vuelta de tuerca
-    {"Otra vuelta de tuerca", "Pepe, el fontanero", "Bricolage", ' ', 2, true},
-    // Book 85: La Tierra: ¡ Planeta limpio !
-    {"La Tierra: ¡ Planeta limpio !", "Juan, el basurero", "Ciencia Ficcion", ' ', 2, true},
-    // Book 86: Liberad a Brian !
-    {"Liberad a Brian !", "Roger Rabitt", "Historica", ' ', 2, true},
-    // Book 87: La vida es una mierda
-    {"La vida es una mierda", "Juanjo Dido", "Ensayo", ' ', 2, true},
-    // Book 88: No era eso lo que yo di a entender
-    {"No era eso lo que yo di a entender", "Jesus de Nazaret", "Religion", ' ', 2, true},
-    // Book 89: Castigos a Piratas Informaticos
-    {"Castigos a Piratas Informaticos", "Torquemada", "Inquisicion", ' ', 1, true},
-    // Book 90: Confiesa, bruja asquerosa
-    {"Confiesa, bruja asquerosa", "Troquemada", "Inquisicion", ' ', 1, true},
-    // Book 91: El cocherito lere
-    {"El cocherito lere", "Paco costas", "Automovilismo", ' ', 1, true},
-    // Book 92: La musica amansa a las fieras
-    {"La musica amansa a las fieras", "Wagner", "Musica", ' ', 2, true},
-    // Book 93: Pinocho en el Parlamento
-    {"Pinocho en el Parlamento", "Carmen Tirosa", "Cronica Social", ' ', 0, false},
-    // Book 94: Hagase famoso gracias a la energia de la...
-    {"Hagase famoso gracias a la energia de las petunias", "Carmelo Cuelo", "Esoterismo", ' ', 0, false},
-    // Book 95: Dios mio, ¡ Que cruz !
-    {"Dios mio, ¡ Que cruz !", "Jesus de Nazaret", "Autobiografia", ' ', 0, false},
-    // Book 96: Psicologia de la motivacion inmotivada
-    {"Psicologia de la motivacion inmotivada", "Dr. Chemi", "Psicologia", ' ', 2, true},
-    // Book 97: Magia rosa para iniciados
-    {"Magia rosa para iniciados", "Manolo Lailo", "Esoterismo", ' ', 0, false},
-    // Book 98: Un mundo Feliz
-    {"Un mundo Feliz", "Aldous Huxley", "Novela", ' ', 0, false},
-    // Book 99: Sexo oral y por escrito
-    {"Sexo oral y por escrito", "Franz Masturmann", "Sexologia", ' ', 3, true},
-    // Book 100: El contrato social de aprendizaje
-    {"El contrato social de aprendizaje", "Rousseau", "Ensayo", ' ', 1, true},
-    // Book 101: Vida sexual del escarabajo de la Patagon...
-    {"Vida sexual del escarabajo de la Patagonia", "Dr. Tedio Plomez", "Botanica", ' ', 3, true},
-    // Book 102: Manual del necrofago
-    {"Manual del necrofago", "Jesus Gil", "Manual", ' ', 0, false},
-    // Book 103: Canticos espirituales en formato *.ZIP
-    {"Canticos espirituales en formato *.ZIP", "Doctor Chip", "Poesia", 'B', 1, true},
-    // Book 104: Novela erotica en formato *.MAS
-    {"Novela erotica en formato *.MAS", "Doctor Chip", "Erotica", ' ', 0, false},
-    // Book 105: Plopuestas colelacionales en coyuntulas ...
-    {"Plopuestas colelacionales en coyuntulas bilatelales", "Senadol Chan Chu Yo", "Politica", ' ', 0, false},
-    // Book 106: Ereh un fistro
-    {"Ereh un fistro", "Chiquito de la casa", "Humor", ' ', 0, false},
-    // Book 107: El hacedor de la Lluvia
-    {"El hacedor de la Lluvia", "Herman Hesse", "Cuentos", ' ', 0, false},
-    // Book 108: Pasiones recalcitrantes
-    {"Pasiones recalcitrantes", "Corin Tellado", "Novela rosa", 'C', 2, true},
-    // Book 109: No me mates con tomate
-    {"No me mates con tomate", "Karlos Arguiñano", "Cocina", ' ', 0, false},
-    // Book 110: El valenciano en los albores del siglo X...
-    {"El valenciano en los albores del siglo XXI", "Jaume i Pascual", "Nacionalismo", 'D', 1, true},
-    // Book 111: El valenciano es la lengua del futuro
-    {"El valenciano es la lengua del futuro", "Jaume i Pascual", "Nacionalismo", ' ', 0, false},
-    // Book 112: Valencia: mes que mai
-    {"Valencia: mes que mai", "Jaume i Pascual", "Nacionalismo", ' ', 0, false},
-    // Book 113: Sistema inmunitario de los cefalopodos (...
-    {"Sistema inmunitario de los cefalopodos (v. I)", "Dr. Tedio Plomez", "Biologia", 'E', 3, true},
-    // Book 114: Sistema inmunitario de los cefalopodos (...
-    {"Sistema inmunitario de los cefalopodos (v. II)", "Dr. Tedio Plomez", "Biologia", ' ', 0, false},
-    // Book 115: Sistema inmunitario de los cefalopodos (...
-    {"Sistema inmunitario de los cefalopodos (v. III)", "Dr. Tedio Plomez", "Biologia", ' ', 0, false},
-    // Book 116: Sistema inmunitario de los cefalopodos (...
-    {"Sistema inmunitario de los cefalopodos (v. IV)", "Dr. Tedio Plomez", "Biologia", ' ', 0, false},
-    // Book 117: Sistema inmunitario de los cefalopodos (...
-    {"Sistema inmunitario de los cefalopodos (v. V)", "Dr. Tedio Plomez", "Biologia", ' ', 0, false},
-    // Book 118: Dos mas dos son cinco
-    {"Dos mas dos son cinco", "Joan Josep Climent Colomer", "Matematicas", 'F', 1, true},
-    // Book 119: El algebra es la base de la programacion
-    {"El algebra es la base de la programacion", "Joan Josep Climent Colomer", "Humor absurdo", ' ', 0, false},
-    // Book 120: Autobiografia de una miseria humana
-    {"Autobiografia de una miseria humana", "Joan Josep Climent Colomer", "Esperpento", ' ', 0, false},
-    // Book 121: El arte mundial antes y despues de mi
-    {"El arte mundial antes y despues de mi", "Nacho Taulet Perman", "Arte", ' ', 0, false},
-    // Book 122: La parte Creativa
-    {"La parte Creativa", "Nacho Taulet Perman", "Arte", 'G', 2, true},
-    // Book 123: Llamame cuando se muera tu abuelo
-    {"Llamame cuando se muera tu abuelo", "Jose Bart Carrion", "Teatro", ' ', 0, false},
-    // Book 124: Soy un incomprendido
-    {"Soy un incomprendido", "Jose Bart Carrion", "Autobiografia", ' ', 0, false},
-    // Book 125: El arte de limpiar botijos por dentro
-    {"El arte de limpiar botijos por dentro", "Varios autores", "Manualidades", ' ', 0, false},
-};
+
+// static const int kLibraryBookCount = 125;
+
+// static const LibraryBook kLibraryBooks[] = {
+//     // Book 1: Los hombres: ¡ Como caparlos !
+//     {"Los hombres: ¡ Como caparlos !", "Herminia Gutierrez", "Feminismo", ' ', 0, false},
+//     // Book 2: Mujeres del mundo: ¡ No os depileis las ...
+//     {"Mujeres del mundo: ¡ No os depileis las axilas !", "Herminia Gutierrez", "Feminismo", ' ', 0, false},
+//     // Book 3: Gato por liebre
+//     {"Gato por liebre", "Karlos Arguiñano", "Cocina", 'I', 1, true},
+//     // Book 4: Hamlet
+//     {"Hamlet", "William Shakespeare", "Teatro", ' ', 0, false},
+//     // Book 5: Fausto
+//     {"Fausto", "Goethe", "Novela", ' ', 0, false},
+//     // Book 6: Enrique de Ofterdingen
+//     {"Enrique de Ofterdingen", "Novalis", "Novela", 'J', 1, true},
+//     // Book 7: Guia de desobediencia civica
+//     {"Guia de desobediencia civica", "Azagra", "Ensayo", ' ', 0, false},
+//     // Book 8: Literatura en la edad de piedra: cuestio...
+//     {"Literatura en la edad de piedra: cuestion de fuerza", "Profesor Cebollo", "Ensayo", ' ', 0, false},
+//     // Book 9: Turbo C ++ con Intratex
+//     {"Turbo C ++ con Intratex", "Programadores reunidos", "Informatica", ' ', 0, false},
+//     // Book 10: Codigo Maquina a pelo
+//     {"Codigo Maquina a pelo", "Programadores reunidos", "Informatica", 'K', 1, true},
+//     // Book 11: Asesino por vocacion
+//     {"Asesino por vocacion", "Chema Ton", "Novela negra", ' ', 0, false},
+//     // Book 12: Poemas rebuscados
+//     {"Poemas rebuscados", "Ramon Rodriguez", "Poesia", ' ', 0, false},
+//     // Book 13: El parto de las tortugas: Un proceso len...
+//     {"El parto de las tortugas: Un proceso lento", "Profesor Lorin Colorado", "Biologia", ' ', 0, false},
+//     // Book 14: Te parto la cara ¡ Capuyo !
+//     {"Te parto la cara ¡ Capuyo !", "Jhonny Rapper", "Critica Social", 'L', 3, true},
+//     // Book 15: En los tugurios del Himalaya
+//     {"En los tugurios del Himalaya", "Coronel Tapioca", "Aventuras", ' ', 0, false},
+//     // Book 16: En las callejuelas del amazonas
+//     {"En las callejuelas del amazonas", "Coronel Tapioca", "Aventuras", ' ', 0, false},
+//     // Book 17: En las selvas de Londres
+//     {"En las selvas de Londres", "Coronel Tapioca", "Aventuras", 'M', 1, true},
+//     // Book 18: Sueños binarios
+//     {"Sueños binarios", "Doctor Chip", "Ciencia ficcion", ' ', 0, false},
+//     // Book 19: Cien cuentos cortisimos
+//     {"Cien cuentos cortisimos", "Billi el rapido", "Novela", ' ', 0, false},
+//     // Book 20: Teoria de la relatividad
+//     {"Teoria de la relatividad", "Alfred Einstein", "Ciencia", ' ', 0, false},
+//     // Book 21: El ultimo paso para la cuadratura del ci...
+//     {"El ultimo paso para la cuadratura del circulo", "Anonimo", "Filosofia", 'N', 1, true},
+//     // Book 22: Correlacion entre el sentido de los colo...
+//     {"Correlacion entre el sentido de los colores y sonidos", "Anonimo", "Filosofia", ' ', 0, false},
+//     // Book 23: Los cuatro evangelios: Interpretados por...
+//     {"Los cuatro evangelios: Interpretados por ordenador", "Doctor Chip", "Teologia", ' ', 0, false},
+//     // Book 24: Enciclopedia de bolsillo
+//     {"Enciclopedia de bolsillo", "Profesor Lumbreras", "Enciclopedia", 'O', 1, true},
+//     // Book 25: Sigueme y revienta
+//     {"Sigueme y revienta", "M. Indurain", "Deporte", 'P', 3, true},
+//     // Book 26: Me duele too...
+//     {"Me duele too...", "Carmen Opausica", "Ensayo", ' ', 0, false},
+//     // Book 27: El Perro de Sam Rocker si tiene rabo
+//     {"El Perro de Sam Rocker si tiene rabo", "El Perro de Sam Rocker", "Biografia", ' ', 0, false},
+//     // Book 28: Donde estara mi carro ?
+//     {"Donde estara mi carro ?", "Manolo Escobar", "Aventuras", ' ', 0, false},
+//     // Book 29: Oh tu, bella flor del jardin
+//     {"Oh tu, bella flor del jardin", "La abeja Maya", "Poesia", 'Q', 2, true},
+//     // Book 30: Yogui, ¡ Bajate de esa motoneta !
+//     {"Yogui, ¡ Bajate de esa motoneta !", "Bubu", "Filosofia", ' ', 0, false},
+//     // Book 31: Odio a muerte a loh mardito rohedore !
+//     {"Odio a muerte a loh mardito rohedore !", "Er gato Yin", "Zoologia", ' ', 0, false},
+//     // Book 32: Pissi, Dissi, ¡ Sargan de su aguhero !
+//     {"Pissi, Dissi, ¡ Sargan de su aguhero !", "Er gato Yin", "Zoologia", ' ', 0, false},
+//     // Book 33: Mardito sea ... ¡ Er queso !
+//     {"Mardito sea ... ¡ Er queso !", "Er gato Yin", "Zoologia", ' ', 0, false},
+//     // Book 34: La mate porque era mia
+//     {"La mate porque era mia", "Loquillo", "Novela", ' ', 0, false},
+//     // Book 35: Los gallos: Esos desconocidos
+//     {"Los gallos: Esos desconocidos", "Paco rico", "Zoologia", ' ', 0, false},
+//     // Book 36: Cuentos corrientes
+//     {"Cuentos corrientes", "Pepe Lopez", "Novela", 'R', 1, true},
+//     // Book 37: Mas madera
+//     {"Mas madera", "R. Gepetto", "Bricolage", 'S', 2, true},
+//     // Book 38: Cuentos del Lejano Oriente
+//     {"Cuentos del Lejano Oriente", "Jhonny Mc. Dowall", "Novela", ' ', 0, false},
+//     // Book 39: Saca el guiski cheli
+//     {"Saca el guiski cheli", "Manolo lailo", "Cronica Social", ' ', 0, false},
+//     // Book 40: Ta totao !
+//     {"Ta totao !", "Lao Tse", "Filosofia", ' ', 0, false},
+//     // Book 41: Mis conversaciones con el señor Roca
+//     {"Mis conversaciones con el señor Roca", "Francisca Gando", "Epistolar", 'T', 2, true},
+//     // Book 42: Guia para la supervivencia
+//     {"Guia para la supervivencia", "Robinson Crusoe", "Manual", ' ', 0, false},
+//     // Book 43: No esperes a ser la segunda: ¡ Engaña a ...
+//     {"No esperes a ser la segunda: ¡ Engaña a tu marido !", "Herminia Gutierrez", "Feminismo", ' ', 0, false},
+//     // Book 44: Yoga Sutras
+//     {"Yoga Sutras", "Patanjali", "Filosofia", 'X', 3, true},
+//     // Book 45: El juego de los Abalorios
+//     {"El juego de los Abalorios", "Herman Hesse", "Novela", ' ', 0, false},
+//     // Book 46: Tienes suerte de ser tan pequeño, ¡ mard...
+//     {"Tienes suerte de ser tan pequeño, ¡ mardito roedo !", "Er gato Yin", "Novela", ' ', 0, false},
+//     // Book 47: Como hacerse rico en diez minutos
+//     {"Como hacerse rico en diez minutos", "El Dioni", "Manual", 'Y', 1, true},
+//     // Book 48: Te querre a pesar de tu madre
+//     {"Te querre a pesar de tu madre", "Corin Tellado", "Novela rosa", ' ', 0, false},
+//     // Book 49: Hasta que el mando a distancia nos separ...
+//     {"Hasta que el mando a distancia nos separe", "Corin Tellado", "Novela rosa", ' ', 0, false},
+//     // Book 50: Una pasion ostentorea
+//     {"Una pasion ostentorea", "Corin Tellado y Jesus Gil", "Novela rosa", 'Z', 3, true},
+//     // Book 51: Por mi, como si te la machacas
+//     {"Por mi, como si te la machacas", "Seneca", "Filosofia", ' ', 0, false},
+//     // Book 52: Conversaciones con mi caballo
+//     {"Conversaciones con mi caballo", "Jesus Gil", "Humor", ' ', 0, false},
+//     // Book 53: El poder curativo de la mierda comun
+//     {"El poder curativo de la mierda comun", "Sri Ramachrinaraska", "Esoterismo", ' ', 1, true},
+//     // Book 54: La liberacion mediante la eneriga de los...
+//     {"La liberacion mediante la eneriga de los pedos", "Sri Ramachrinaraska", "Esoterismo", ' ', 0, false},
+//     // Book 55: Sobre la imperceptibilidad de lo imperce...
+//     {"Sobre la imperceptibilidad de lo imperceptible", "Perogrullo", "Ensayo", ' ', 0, false},
+//     // Book 56: Piojos; como educarlos sin traumas
+//     {"Piojos; como educarlos sin traumas", "Franz Franzfrenz", "Psicologia", ' ', 0, false},
+//     // Book 57: I Ching
+//     {"I Ching", "Richard Willem", "Filosofia", ' ', 0, false},
+//     // Book 58: No se nada, ni me importa
+//     {"No se nada, ni me importa", "Socrates", "Filosofia", 'U', 2, true},
+//     // Book 59: No he sido yo, ¡ Lo juro !
+//     {"No he sido yo, ¡ Lo juro !", "Juan Jose Gil", "Biografia", ' ', 0, false},
+//     // Book 60: Relatos cortos
+//     {"Relatos cortos", "Tachenko", "Novela de ficcion", 'V', 3, true},
+//     // Book 61: El martillo como elemento cognitivo
+//     {"El martillo como elemento cognitivo", "Friedrich Nietzsche", "Filosofia", ' ', 0, false},
+//     // Book 62: La maravillosa vida del escarabajo pelot...
+//     {"La maravillosa vida del escarabajo pelotero (v. I)", "Dr. Federico Ñazo", "Botanica", ' ', 0, false},
+//     // Book 63: La maravillosa vida del escarabajo pelot...
+//     {"La maravillosa vida del escarabajo pelotero (v. II)", "Dr. Federico Ñazo", "Botanica", ' ', 0, false},
+//     // Book 64: La maravillosa vida del escarabajo pelot...
+//     {"La maravillosa vida del escarabajo pelotero (v. III)", "Dr. Federico Ñazo", "Botanica", ' ', 0, false},
+//     // Book 65: La maravillosa vida del escarabajo pelot...
+//     {"La maravillosa vida del escarabajo pelotero (v. IV)", "Dr. Federico Ñazo", "Botanica", ' ', 0, false},
+//     // Book 66: La maravillosa vida del escarabajo pelot...
+//     {"La maravillosa vida del escarabajo pelotero (v. V)", "Dr. Federico Ñazo", "Botanica", ' ', 0, false},
+//     // Book 67: Tu eliges: Tu madre o yo
+//     {"Tu eliges: Tu madre o yo", "Anonimo", "Psicologia aplicada", 'W', 3, true},
+//     // Book 68: Mi lucha
+//     {"Mi lucha", "Adolf Hitler", "Humor negro", ' ', 0, false},
+//     // Book 69: Cuentos de amor y desidia
+//     {"Cuentos de amor y desidia", "Jardiel Poncela", "Teatro", ' ', 1, true},
+//     // Book 70: Nuevas andanzas de Zaratustra
+//     {"Nuevas andanzas de Zaratustra", "Anonimo", "Aventuras", ' ', 2, true},
+//     // Book 71: Me se cuadre ¡ Coño !
+//     {"Me se cuadre ¡ Coño !", "Sargento Cienfuegos", "Etica militar", ' ', 2, true},
+//     // Book 72: Gatos: solos o con Ketchup
+//     {"Gatos: solos o con Ketchup", "El perro de Sam Rocker", "Cocina", ' ', 1, true},
+//     // Book 73: Querida Adelaida: mi marido NO ha dejado...
+//     {"Querida Adelaida: mi marido NO ha dejado de roncar", "Maruja Mones", "Epistolar", ' ', 0, false},
+//     // Book 74: Sobre el papel de El Lepe en el nuevo Or...
+//     {"Sobre el papel de El Lepe en el nuevo Orden Mundial", "General Sintacha", "Estrategia", ' ', 0, false},
+//     // Book 75: Aqui no hay nadie que se acueste sin cen...
+//     {"Aqui no hay nadie que se acueste sin cenar (v. I)", "Fidel Castro", "Politica", ' ', 1, true},
+//     // Book 76: Aqui no hay nadie que se acueste sin cen...
+//     {"Aqui no hay nadie que se acueste sin cenar (v. II)", "Fidel Castro", "Politica", ' ', 0, false},
+//     // Book 77: Aqui no hay nadie que se acueste sin cen...
+//     {"Aqui no hay nadie que se acueste sin cenar (v. III)", "Fidel Castro", "Politica", ' ', 0, false},
+//     // Book 78: Aqui no hay nadie que se acueste sin cen...
+//     {"Aqui no hay nadie que se acueste sin cenar (v. IV)", "Fidel Castro", "Politica", ' ', 0, false},
+//     // Book 79: Aqui no hay nadie que se acueste sin cen...
+//     {"Aqui no hay nadie que se acueste sin cenar (v. V)", "Fidel Castro", "Politica", ' ', 0, false},
+//     // Book 80: Domine la metafisica en 5 dias
+//     {"Domine la metafisica en 5 dias", "Profesor Pinocho", "Manual", ' ', 1, true},
+//     // Book 81: Domine el ensamblador en 5 dias
+//     {"Domine el ensamblador en 5 dias", "Profesor Pinocho", "Manual", ' ', 1, true},
+//     // Book 82: Dominese a si mismo en 5 dias
+//     {"Dominese a si mismo en 5 dias", "Profesor Pinocho", "Manual", ' ', 1, true},
+//     // Book 83: Piernas de Ciertopelo
+//     {"Piernas de Ciertopelo", "Chichi Mondongo", "Erotica", ' ', 2, true},
+//     // Book 84: Otra vuelta de tuerca
+//     {"Otra vuelta de tuerca", "Pepe, el fontanero", "Bricolage", ' ', 2, true},
+//     // Book 85: La Tierra: ¡ Planeta limpio !
+//     {"La Tierra: ¡ Planeta limpio !", "Juan, el basurero", "Ciencia Ficcion", ' ', 2, true},
+//     // Book 86: Liberad a Brian !
+//     {"Liberad a Brian !", "Roger Rabitt", "Historica", ' ', 2, true},
+//     // Book 87: La vida es una mierda
+//     {"La vida es una mierda", "Juanjo Dido", "Ensayo", ' ', 2, true},
+//     // Book 88: No era eso lo que yo di a entender
+//     {"No era eso lo que yo di a entender", "Jesus de Nazaret", "Religion", ' ', 2, true},
+//     // Book 89: Castigos a Piratas Informaticos
+//     {"Castigos a Piratas Informaticos", "Torquemada", "Inquisicion", ' ', 1, true},
+//     // Book 90: Confiesa, bruja asquerosa
+//     {"Confiesa, bruja asquerosa", "Troquemada", "Inquisicion", ' ', 1, true},
+//     // Book 91: El cocherito lere
+//     {"El cocherito lere", "Paco costas", "Automovilismo", ' ', 1, true},
+//     // Book 92: La musica amansa a las fieras
+//     {"La musica amansa a las fieras", "Wagner", "Musica", ' ', 2, true},
+//     // Book 93: Pinocho en el Parlamento
+//     {"Pinocho en el Parlamento", "Carmen Tirosa", "Cronica Social", ' ', 0, false},
+//     // Book 94: Hagase famoso gracias a la energia de la...
+//     {"Hagase famoso gracias a la energia de las petunias", "Carmelo Cuelo", "Esoterismo", ' ', 0, false},
+//     // Book 95: Dios mio, ¡ Que cruz !
+//     {"Dios mio, ¡ Que cruz !", "Jesus de Nazaret", "Autobiografia", ' ', 0, false},
+//     // Book 96: Psicologia de la motivacion inmotivada
+//     {"Psicologia de la motivacion inmotivada", "Dr. Chemi", "Psicologia", ' ', 2, true},
+//     // Book 97: Magia rosa para iniciados
+//     {"Magia rosa para iniciados", "Manolo Lailo", "Esoterismo", ' ', 0, false},
+//     // Book 98: Un mundo Feliz
+//     {"Un mundo Feliz", "Aldous Huxley", "Novela", ' ', 0, false},
+//     // Book 99: Sexo oral y por escrito
+//     {"Sexo oral y por escrito", "Franz Masturmann", "Sexologia", ' ', 3, true},
+//     // Book 100: El contrato social de aprendizaje
+//     {"El contrato social de aprendizaje", "Rousseau", "Ensayo", ' ', 1, true},
+//     // Book 101: Vida sexual del escarabajo de la Patagon...
+//     {"Vida sexual del escarabajo de la Patagonia", "Dr. Tedio Plomez", "Botanica", ' ', 3, true},
+//     // Book 102: Manual del necrofago
+//     {"Manual del necrofago", "Jesus Gil", "Manual", ' ', 0, false},
+//     // Book 103: Canticos espirituales en formato *.ZIP
+//     {"Canticos espirituales en formato *.ZIP", "Doctor Chip", "Poesia", 'B', 1, true},
+//     // Book 104: Novela erotica en formato *.MAS
+//     {"Novela erotica en formato *.MAS", "Doctor Chip", "Erotica", ' ', 0, false},
+//     // Book 105: Plopuestas colelacionales en coyuntulas ...
+//     {"Plopuestas colelacionales en coyuntulas bilatelales", "Senadol Chan Chu Yo", "Politica", ' ', 0, false},
+//     // Book 106: Ereh un fistro
+//     {"Ereh un fistro", "Chiquito de la casa", "Humor", ' ', 0, false},
+//     // Book 107: El hacedor de la Lluvia
+//     {"El hacedor de la Lluvia", "Herman Hesse", "Cuentos", ' ', 0, false},
+//     // Book 108: Pasiones recalcitrantes
+//     {"Pasiones recalcitrantes", "Corin Tellado", "Novela rosa", 'C', 2, true},
+//     // Book 109: No me mates con tomate
+//     {"No me mates con tomate", "Karlos Arguiñano", "Cocina", ' ', 0, false},
+//     // Book 110: El valenciano en los albores del siglo X...
+//     {"El valenciano en los albores del siglo XXI", "Jaume i Pascual", "Nacionalismo", 'D', 1, true},
+//     // Book 111: El valenciano es la lengua del futuro
+//     {"El valenciano es la lengua del futuro", "Jaume i Pascual", "Nacionalismo", ' ', 0, false},
+//     // Book 112: Valencia: mes que mai
+//     {"Valencia: mes que mai", "Jaume i Pascual", "Nacionalismo", ' ', 0, false},
+//     // Book 113: Sistema inmunitario de los cefalopodos (...
+//     {"Sistema inmunitario de los cefalopodos (v. I)", "Dr. Tedio Plomez", "Biologia", 'E', 3, true},
+//     // Book 114: Sistema inmunitario de los cefalopodos (...
+//     {"Sistema inmunitario de los cefalopodos (v. II)", "Dr. Tedio Plomez", "Biologia", ' ', 0, false},
+//     // Book 115: Sistema inmunitario de los cefalopodos (...
+//     {"Sistema inmunitario de los cefalopodos (v. III)", "Dr. Tedio Plomez", "Biologia", ' ', 0, false},
+//     // Book 116: Sistema inmunitario de los cefalopodos (...
+//     {"Sistema inmunitario de los cefalopodos (v. IV)", "Dr. Tedio Plomez", "Biologia", ' ', 0, false},
+//     // Book 117: Sistema inmunitario de los cefalopodos (...
+//     {"Sistema inmunitario de los cefalopodos (v. V)", "Dr. Tedio Plomez", "Biologia", ' ', 0, false},
+//     // Book 118: Dos mas dos son cinco
+//     {"Dos mas dos son cinco", "Joan Josep Climent Colomer", "Matematicas", 'F', 1, true},
+//     // Book 119: El algebra es la base de la programacion
+//     {"El algebra es la base de la programacion", "Joan Josep Climent Colomer", "Humor absurdo", ' ', 0, false},
+//     // Book 120: Autobiografia de una miseria humana
+//     {"Autobiografia de una miseria humana", "Joan Josep Climent Colomer", "Esperpento", ' ', 0, false},
+//     // Book 121: El arte mundial antes y despues de mi
+//     {"El arte mundial antes y despues de mi", "Nacho Taulet Perman", "Arte", ' ', 0, false},
+//     // Book 122: La parte Creativa
+//     {"La parte Creativa", "Nacho Taulet Perman", "Arte", 'G', 2, true},
+//     // Book 123: Llamame cuando se muera tu abuelo
+//     {"Llamame cuando se muera tu abuelo", "Jose Bart Carrion", "Teatro", ' ', 0, false},
+//     // Book 124: Soy un incomprendido
+//     {"Soy un incomprendido", "Jose Bart Carrion", "Autobiografia", ' ', 0, false},
+//     // Book 125: El arte de limpiar botijos por dentro
+//     {"El arte de limpiar botijos por dentro", "Varios autores", "Manualidades", ' ', 0, false},
+// };
 
 } // namespace Pelrock
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 29676cdef92..b3d2ff36546 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -329,7 +329,6 @@ void PelrockEngine::checkMouse() {
 	if (_alfredState.animState == ALFRED_WALKING && !_alfredState.isWalkingCancelable) {
 		// Ignore clicks while Alfred is walking
 		_events->_leftMouseClicked = false;
-		debug("Ignoring mouse click while Alfred is walking");
 		return;
 	}
 
@@ -1308,6 +1307,11 @@ void PelrockEngine::gameLoop() {
 void PelrockEngine::computerLoop() {
 	Computer computer(_events);
 	computer.run();
+	if(_state->selectedBookIndex != -1 && _state->bookLetter != '\0') {
+		Common::StringArray lines;
+		lines.push_back(Common::String::format(computer._memorizedMsg, _state->bookLetter));
+		_dialog->sayAlfred(lines);
+	}
 }
 
 void PelrockEngine::extraScreenLoop() {
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 08af8c0331c..26378f0079b 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -24,6 +24,7 @@
 #include "pelrock/pelrock.h"
 #include "pelrock/room.h"
 #include "pelrock/util.h"
+#include "resources.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index 22aec4820cb..4191fe5b2f7 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -49,6 +49,7 @@ public:
 	void clearSpecialAnim();
 	void loadInventoryItems();
 	void loadHardcodedText();
+	void loadComputerText();
 	void getExtraScreen(int screenIndex, byte *screenBuf, byte *palette);
 	Common::Array<Common::StringArray> getCredits();
 	Common::Array<Common::Array<Common::String>> processTextData(byte *data, size_t size, bool decode = false);
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 329190948f9..1fca82e42bf 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -481,7 +481,10 @@ struct GameStateData {
 	Common::Array<byte> inventoryItems;
 	int16 selectedInventoryItem = -1;
 
+
 	int libraryShelf = -1;
+	int selectedBookIndex = -1;
+	unsigned char bookLetter = '\0';
 	Common::HashMap<byte, Common::Array<Sticker>> stickersPerRoom;
 	Common::HashMap<byte, Common::Array<ExitChange>> roomExitChanges;
 	Common::HashMap<byte, Common::Array<WalkBoxChange>> roomWalkBoxChanges;
@@ -548,6 +551,28 @@ struct GameStateData {
 	void setRootDisabledState(byte room, byte root, bool disabled) {
 		conversationRootsState[room * 4 + root] = disabled ? 1 : 0;
 	}
+
+	int findFirstBookIndex() {
+		for (uint i = 0; i < inventoryItems.size(); i++) {
+			int x = inventoryItems[i];
+			if ((x >= 11) && (x <= 58))
+				return x;
+		}
+		return -1;
+	}
+
+
+	int booksInInventory() {
+		int l = inventoryItems.size();
+		int count = 0;
+		for (int i = 0; i < l; i++) {
+			int x = inventoryItems[i];
+			if ((x >= 11) && (x <= 58))
+				count++;
+		}
+		return count;
+	}
+
 };
 
 struct SaveGameData {


Commit: dd28828c1abe0e17eaf48975e5d2d850f53e9a64
    https://github.com/scummvm/scummvm/commit/dd28828c1abe0e17eaf48975e5d2d850f53e9a64
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:52+02:00

Commit Message:
PELROCK: implement reading of books

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index ab4c33c2d88..44d75bd57f0 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -123,6 +123,7 @@ const CombinationEntry combinationTable[] = {
 	{1, 53, &PelrockEngine::giveIdToGuard},
 	{5, 53, &PelrockEngine::giveMoneyToGuard},
 	{7, 353, &PelrockEngine::useAmuletWithStatue},
+	{8, 102, &PelrockEngine::giveFormulaToLibrarian},
 	// End marker
 	{WILDCARD, WILDCARD, nullptr}};
 
@@ -177,7 +178,7 @@ void PelrockEngine::buyFromStore(HotSpot *hotspot, int stickerId) {
 		if (hotspot->extra == 69) {
 			_room->disableSprite(15, 3); // Disable monkey brain sprite
 		}
-		_state->addInventoryItem(hotspot->extra);
+		addInventoryItem(hotspot->extra);
 		_currentHotspot = nullptr;
 		walkLoop(224, 283, ALFRED_LEFT);
 		_dialog->say(_res->_ingameTexts[CUESTA1000]);
@@ -619,6 +620,12 @@ void PelrockEngine::pickBooksFromShelf3(HotSpot *hotspot) {
 	pickUpBook(3);
 }
 
+void PelrockEngine::giveFormulaToLibrarian(int inventoryObject, HotSpot *hotspot) {
+	_dialog->say(_res->_ingameTexts[REGALO_LIBRO_RECETAS]);
+	_state->removeInventoryItem(8);
+	addInventoryItem(59);
+}
+
 void PelrockEngine::pickUpBook(int i) {
 	if (!_state->hasInventoryItem(10)) {
 		_dialog->say(_res->_ingameTexts[VENGA_ACA]);
@@ -635,11 +642,11 @@ void PelrockEngine::pickUpBook(int i) {
 			int booksInInventory = _state->booksInInventory();
 			if (booksInInventory == 3) {
 				int firstBook = _state->findFirstBookIndex();
-				if(firstBook != -1)
+				if (firstBook != -1)
 					_state->removeInventoryItem(firstBook);
 				_dialog->say(_res->_ingameTexts[TENDRE_DEJAR_LIBRO]);
 			}
-			_state->addInventoryItem(_state->selectedBookIndex);
+			addInventoryItem(_state->selectedBookIndex);
 			_state->selectedBookIndex = -1;
 		}
 	}
@@ -672,7 +679,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 
 	debug("Using item %d on Alfred", inventoryObject);
 	switch (inventoryObject) {
-	case 63: // Recipe book
+	case 63: // Recipe
 		_res->loadAlfredSpecialAnim(1);
 		_alfredState.animState = ALFRED_SPECIAL_ANIM;
 		waitForSpecialAnimation();
@@ -681,8 +688,48 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		debug("After extra screen");
 		_dialog->say(_res->_ingameTexts[QUEASCO]);
 		break;
+	case 59: // Recipe book
+		if (!_state->hasInventoryItem(64)) {
+			_res->loadAlfredSpecialAnim(0);
+			_alfredState.animState = ALFRED_SPECIAL_ANIM;
+			waitForSpecialAnimation();
+			_dialog->say(_res->_ingameTexts[HOJAENTREPAGINAS]);
+			addInventoryItem(64);
+		} else {
+			_res->loadAlfredSpecialAnim(0);
+			_alfredState.animState = ALFRED_SPECIAL_ANIM;
+			waitForSpecialAnimation();
+			_dialog->say(_res->_ingameTexts[NOENTIENDONADA]);
+		}
+		break;
+	case 17: // Egyptian book
+		_res->loadAlfredSpecialAnim(0);
+		_alfredState.animState = ALFRED_SPECIAL_ANIM;
+		waitForSpecialAnimation();
+		_dialog->say(_res->_ingameTexts[YASEEGIPCIO]);
+		_state->setFlag(FLAG_ALFRED_SABE_EGIPCIO, true);
+		break;
+	case 24:
+		_res->loadAlfredSpecialAnim(0);
+		_alfredState.animState = ALFRED_SPECIAL_ANIM;
+		waitForSpecialAnimation();
+		_dialog->say(_res->_ingameTexts[COSASAPRENDIDO]);
+		_state->setFlag(FLAG_ALFRED_INTELIGENTE, true);
+		break;
+	case 64:
+		_res->loadAlfredSpecialAnim(0);
+		_alfredState.animState = ALFRED_SPECIAL_ANIM;
+		waitForSpecialAnimation();
+		loadExtraScreenAndPresent(5);
+		if(_state->getFlag(FLAG_ALFRED_SABE_EGIPCIO)) {
+			_dialog->say(_res->_ingameTexts[FORMULAVIAJETIEMPO]);
+		}
+		else {
+			_dialog->say(_res->_ingameTexts[QUELASTIMA_NOSEEGIPCIO]);
+		}
+		break;
 	case 0: // yellow book
-		_res->loadAlfredSpecialAnim(2);
+		_res->loadAlfredSpecialAnim(0);
 		_alfredState.animState = ALFRED_SPECIAL_ANIM;
 		waitForSpecialAnimation();
 		_dialog->say(_res->_ingameTexts[CUENTOPARECIDO]);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 052b418c668..1b9d3bd8f67 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -296,6 +296,7 @@ public:
 	void pickBooksFromShelf1(HotSpot *hotspot);
 	void pickBooksFromShelf2(HotSpot *hotspot);
 	void pickBooksFromShelf3(HotSpot *hotspot);
+	void giveFormulaToLibrarian(int inventoryObject, HotSpot *hotspot);
 	void pickUpBook(int i);
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index 4191fe5b2f7..1dbda4171f4 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -49,7 +49,7 @@ public:
 	void clearSpecialAnim();
 	void loadInventoryItems();
 	void loadHardcodedText();
-	void loadComputerText();
+	Common::StringArray loadComputerText();
 	void getExtraScreen(int screenIndex, byte *screenBuf, byte *palette);
 	Common::Array<Common::StringArray> getCredits();
 	Common::Array<Common::Array<Common::String>> processTextData(byte *data, size_t size, bool decode = false);


Commit: a6ed3a2764017c08bace10b3da9a7f7d29a99d6b
    https://github.com/scummvm/scummvm/commit/a6ed3a2764017c08bace10b3da9a7f7d29a99d6b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:52+02:00

Commit Message:
PELROCK: Implements room 14

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/console.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp
    engines/pelrock/room.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 44d75bd57f0..515be30c5f1 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -189,31 +189,61 @@ void PelrockEngine::buyFromStore(HotSpot *hotspot, int stickerId) {
 }
 
 void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte rootIndex) {
-	if (actionTrigger == 328) {
+	switch (actionTrigger) {
+	case 328:
 		debug("Disabling root %d in room %d", rootIndex, room);
 		_state->setRootDisabledState(room, rootIndex, true);
-	} else if (actionTrigger == 329) {
+		break;
+	case 329:
 		debug("Would now enable X easter egg");
-	} else if (actionTrigger == 258) {
+		break;
+	case 258:
 		_state->setFlag(FLAG_GUARDIA_PIDECOSAS, true);
 		_state->setRootDisabledState(4, 1, true);
-	} else if (actionTrigger == 259) {
+		break;
+	case 259:
 		_dialog->say(_res->_ingameTexts[NO_EMPECEMOS]);
-	} else if (actionTrigger == 260) {
+		break;
+	case 260:
 		_dialog->say(_res->_ingameTexts[CUERPO_DANONE], 1);
 		_dialog->say(_res->_ingameTexts[CABEZA_HUECA]);
-	} else if (actionTrigger == 261) {
+		break;
+	case 261:
 		_dialog->say(_res->_ingameTexts[ESO_LO_SERAS_TU], 1);
-	} else if (actionTrigger == 262) {
+		break;
+	case 262:
 		_dialog->say(_res->_ingameTexts[DEMASIADO_NO_PUEDO_PENSAR], 1);
-	} else if (actionTrigger == 263) {
+		break;
+	case 263:
 		_dialog->say(_res->_ingameTexts[UN_POCO_RESPETO]);
-	} else if (actionTrigger == 264) {
+		break;
+	case 264:
 		// disables the two first roots, the second one will be enabled later!
 		_state->setRootDisabledState(room, rootIndex, true);
 		_state->setRootDisabledState(room, rootIndex + 1, true);
-	} else {
+		break;
+	case 272:
+		_state->setRootDisabledState(room, rootIndex, true);
+		break;
+	case 273:
+		WalkBox w1;
+		w1.x = 436;
+		w1.y = 356;
+		w1.w = 4;
+		w1.h = 14;
+		w1.flags = 0;
+		WalkBox w2;
+		w2.x = 440;
+		w2.y = 368;
+		w2.w = 148;
+		w2.h = 2;
+		w2.flags = 0;
+
+		_room->addWalkbox(w1);
+		_room->addWalkbox(w2);
+	default:
 		debug("Got actionTrigger %d in dialogActionTrigger, but no handler defined", actionTrigger);
+		break;
 	}
 }
 
@@ -535,6 +565,7 @@ void PelrockEngine::giveIdToGuard(int inventoryObject, HotSpot *hotspot) {
 	}
 	if (_state->getFlag(FLAG_SOBORNO_PORTERO) && _state->getFlag(FLAG_GUARDIA_DNI_ENTREGADO)) {
 		_state->setRootDisabledState(4, 2, true);
+		_room->enableSprite(4, 2, 100, true);
 		return;
 	}
 }
@@ -551,6 +582,8 @@ void PelrockEngine::giveMoneyToGuard(int inventoryObject, HotSpot *hotspot) {
 	}
 	if (_state->getFlag(FLAG_SOBORNO_PORTERO) && _state->getFlag(FLAG_GUARDIA_DNI_ENTREGADO)) {
 		_state->setRootDisabledState(4, 2, true);
+		_room->enableSprite(2, 100, true);
+		_room->enableSprite(3, 100, true);
 		return;
 	}
 }
@@ -715,6 +748,8 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		waitForSpecialAnimation();
 		_dialog->say(_res->_ingameTexts[COSASAPRENDIDO]);
 		_state->setFlag(FLAG_ALFRED_INTELIGENTE, true);
+		_state->setRootDisabledState(14, 0, true);
+		_state->setRootDisabledState(14, 1, true);
 		break;
 	case 64:
 		_res->loadAlfredSpecialAnim(0);
diff --git a/engines/pelrock/console.cpp b/engines/pelrock/console.cpp
index 3b5a7c327b5..a7a01679c8c 100644
--- a/engines/pelrock/console.cpp
+++ b/engines/pelrock/console.cpp
@@ -42,7 +42,7 @@ bool PelrockConsole::cmdLoadRoom(int argc, const char **argv) {
 
 	int roomNumber = atoi(argv[1]);
 	g_engine->setScreen(roomNumber, ALFRED_DOWN);
-	debugPrintf("Loaded room %d", roomNumber);
+	debugPrintf("Loaded room %d\n", roomNumber);
 	return true;
 }
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index b3d2ff36546..a6ac2a3ce5c 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -435,7 +435,7 @@ void PelrockEngine::updatePaletteAnimations() {
 }
 
 void PelrockEngine::paintDebugLayer() {
-	bool showWalkboxes = false;
+	bool showWalkboxes = true;
 
 	if (showWalkboxes) {
 		for (uint i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index e6641fb8712..71f1361224c 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -198,6 +198,7 @@ void RoomManager::setActionMask(HotSpot *hotspot, byte actionMask) {
 }
 
 void RoomManager::addWalkbox(WalkBox walkbox) {
+	_currentRoomWalkboxes.push_back(walkbox);
 	g_engine->_state->roomWalkBoxChanges[_currentRoomNumber].push_back({_currentRoomNumber, walkbox.index, walkbox});
 }
 
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 00d231976f7..0c796789a74 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -51,6 +51,12 @@ static const int unpickableHotspotExtras[] = {
 	362,
 };
 
+enum PersistType {
+	PERSIST_TEMP,
+	PERSIST_PERM,
+	PERSIST_BOTH
+};
+
 class RoomManager {
 public:
 	RoomManager();


Commit: bdad1b18ac5a5ecefb3df490147d7b4dd34bf3bf
    https://github.com/scummvm/scummvm/commit/bdad1b18ac5a5ecefb3df490147d7b4dd34bf3bf
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:52+02:00

Commit Message:
PELROCK: Room changes refactor

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/saveload.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 515be30c5f1..b317a6aa710 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -133,8 +133,8 @@ void PelrockEngine::openDoor(HotSpot *hotspot, int exitIndex, int sticker, bool
 		_dialog->say(_res->_ingameTexts[text]);
 		return;
 	}
-	_room->enableExit(exitIndex, !stayClosed);
-	_room->addSticker(sticker, !stayClosed);
+	_room->enableExit(exitIndex, stayClosed ? PERSIST_TEMP : PERSIST_BOTH);
+	_room->addSticker(sticker, stayClosed ? PERSIST_TEMP : PERSIST_BOTH);
 	_sound->playSound(_room->_roomSfx[0]);
 }
 
@@ -144,7 +144,7 @@ void PelrockEngine::closeDoor(HotSpot *hotspot, int exitIndex, int sticker, bool
 		_dialog->say(_res->_ingameTexts[text]);
 		return;
 	}
-	_room->disableExit(exitIndex, !stayOpen);
+	_room->disableExit(exitIndex, stayOpen ? PERSIST_TEMP : PERSIST_BOTH);
 	_room->removeSticker(sticker);
 	_sound->playSound(_room->_roomSfx[1]);
 }
@@ -297,6 +297,10 @@ void PelrockEngine::openIceCreamShopDoor(HotSpot *hotspot) {
 }
 
 void PelrockEngine::closeRoomDrawer(HotSpot *hotspot) {
+	if(!_room->hasSticker(91)) {
+		_dialog->say(_res->_ingameTexts[YA_CERRADO_M]);
+		return;
+	}
 	_room->removeSticker(91);
 	_room->enableHotspot(hotspot);
 }
@@ -441,8 +445,8 @@ void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
 	_dialog->say(_res->_ingameTexts[YOMEVOY]);
 
 	_state->setFlag(FLAG_TIENDA_ABIERTA, true);
-	_room->onlyPersistSticker(_room->_currentRoomNumber, 9);
-	_room->onlyPersistSticker(_room->_currentRoomNumber, 10);
+	_room->addStickerToRoom(_room->_currentRoomNumber, 9, PERSIST_PERM);
+	_room->addStickerToRoom(_room->_currentRoomNumber, 10, PERSIST_PERM);
 	_room->disableHotspot(_room->findHotspotByExtra(295)); // Disable storefront hotspot
 	_room->disableHotspot(_room->findHotspotByExtra(294)); // Disable window hotspot
 	walkTo(639, _alfredState.y);
@@ -565,7 +569,7 @@ void PelrockEngine::giveIdToGuard(int inventoryObject, HotSpot *hotspot) {
 	}
 	if (_state->getFlag(FLAG_SOBORNO_PORTERO) && _state->getFlag(FLAG_GUARDIA_DNI_ENTREGADO)) {
 		_state->setRootDisabledState(4, 2, true);
-		_room->enableSprite(4, 2, 100, true);
+		_room->enableSprite(4, 2, 100);
 		return;
 	}
 }
@@ -582,8 +586,8 @@ void PelrockEngine::giveMoneyToGuard(int inventoryObject, HotSpot *hotspot) {
 	}
 	if (_state->getFlag(FLAG_SOBORNO_PORTERO) && _state->getFlag(FLAG_GUARDIA_DNI_ENTREGADO)) {
 		_state->setRootDisabledState(4, 2, true);
-		_room->enableSprite(2, 100, true);
-		_room->enableSprite(3, 100, true);
+		_room->enableSprite(2, 100);
+		_room->enableSprite(3, 100);
 		return;
 	}
 }
@@ -659,6 +663,10 @@ void PelrockEngine::giveFormulaToLibrarian(int inventoryObject, HotSpot *hotspot
 	addInventoryItem(59);
 }
 
+void PelrockEngine::openNewspaperDoor(HotSpot *hotspot) {
+	openDoor(hotspot, 0, 90, FEMININE, true);
+}
+
 void PelrockEngine::pickUpBook(int i) {
 	if (!_state->hasInventoryItem(10)) {
 		_dialog->say(_res->_ingameTexts[VENGA_ACA]);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index a6ac2a3ce5c..994839b0fec 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -152,10 +152,10 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		// setScreen(0, ALFRED_DOWN);
+		setScreen(0, ALFRED_DOWN);
 		// setScreen(3, ALFRED_RIGHT);
 		// setScreen(5, ALFRED_DOWN);
-		setScreen(9, ALFRED_DOWN);
+		// setScreen(9, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 1b9d3bd8f67..345cc3407e6 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -297,6 +297,7 @@ public:
 	void pickBooksFromShelf2(HotSpot *hotspot);
 	void pickBooksFromShelf3(HotSpot *hotspot);
 	void giveFormulaToLibrarian(int inventoryObject, HotSpot *hotspot);
+	void openNewspaperDoor(HotSpot *hotspot);
 	void pickUpBook(int i);
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 71f1361224c..86d5e81bdab 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -88,48 +88,59 @@ void RoomManager::getBackground(Common::File *roomFile, int roomOffset, byte *ba
 	}
 }
 
-void RoomManager::addSticker(int stickerId, bool persist) {
-	Sticker sticker = g_engine->_res->getSticker(stickerId);
-	_roomStickers.push_back(sticker);
-	addStickerToRoom(_currentRoomNumber, stickerId);
+void RoomManager::addSticker(int stickerId, int persist) {
+	addStickerToRoom(_currentRoomNumber, stickerId, persist);
 }
 
-void RoomManager::addStickerToRoom(byte room, int stickerId) {
+void RoomManager::addStickerToRoom(byte room, int stickerId, int persist) {
 	Sticker sticker = g_engine->_res->getSticker(stickerId);
-	g_engine->_state->stickersPerRoom[room].push_back(sticker);
+	if (room == _currentRoomNumber && persist & PERSIST_TEMP) {
+		_roomStickers.push_back(sticker);
+	}
+	if (persist & PERSIST_PERM) {
+		g_engine->_state->stickersPerRoom[room].push_back(sticker);
+	}
 }
 
-void RoomManager::onlyPersistSticker(byte room, int stickerId) {
-	Sticker sticker = g_engine->_res->getSticker(stickerId);
-	g_engine->_state->stickersPerRoom[room].push_back(sticker);
+void RoomManager::removeSticker(int stickerId) {
+	removeStickerFromRoom(_currentRoomNumber, stickerId);
 }
 
-void RoomManager::removeSticker(int stickerIndex) {
+void RoomManager::removeStickerFromRoom(byte room, int stickerId) {
 	// First check and remove from room stickers
 	for (uint i = 0; i < _roomStickers.size(); i++) {
-		if (_roomStickers[i].stickerIndex == stickerIndex) {
+		if (_roomStickers[i].stickerIndex == stickerId) {
 			_roomStickers.remove_at(i);
 			return;
 		}
 	}
 
 	// Then check and remove from persisted stickers
-	for (uint i = 0; i < g_engine->_state->stickersPerRoom[_currentRoomNumber].size(); i++) {
-		if (g_engine->_state->stickersPerRoom[_currentRoomNumber][i].stickerIndex == stickerIndex) {
-			g_engine->_state->stickersPerRoom[_currentRoomNumber].remove_at(i);
+	for (uint i = 0; i < g_engine->_state->stickersPerRoom[room].size(); i++) {
+		if (g_engine->_state->stickersPerRoom[room][i].stickerIndex == stickerId) {
+			g_engine->_state->stickersPerRoom[room].remove_at(i);
 			return;
 		}
 	}
 }
 
 bool RoomManager::hasSticker(int index) {
+	return hasSticker(_currentRoomNumber, index);
+}
+
+bool RoomManager::hasSticker(byte room, int index) {
 	for (uint i = 0; i < _roomStickers.size(); i++) {
 		if (_roomStickers[i].stickerIndex == index) {
 			return true;
 		}
 	}
-	for (uint i = 0; i < g_engine->_state->stickersPerRoom[_currentRoomNumber].size(); i++) {
-		if (g_engine->_state->stickersPerRoom[_currentRoomNumber][i].stickerIndex == index) {
+
+	if (room != _currentRoomNumber) {
+		return false;
+	}
+
+	for (uint i = 0; i < g_engine->_state->stickersPerRoom[room].size(); i++) {
+		if (g_engine->_state->stickersPerRoom[room][i].stickerIndex == index) {
 			return true;
 		}
 	}
@@ -137,69 +148,144 @@ bool RoomManager::hasSticker(int index) {
 	return false;
 }
 
-void RoomManager::changeExit(int index, bool enabled, bool persist) {
-	_currentRoomExits[index].isEnabled = enabled;
-	if (persist)
-		g_engine->_state->roomExitChanges[_currentRoomNumber].push_back({_currentRoomNumber, _currentRoomExits[index].index, _currentRoomExits[index]});
+void RoomManager::changeExit(byte index, bool enabled, int persist) {
+	changeExit(_currentRoomNumber, index, enabled, persist);
 }
 
-void RoomManager::disableExit(int index, bool persist) {
+void RoomManager::changeExit(byte room, byte index, bool enabled, int persist) {
+	if (room == _currentRoomNumber & persist & PERSIST_TEMP) {
+		_currentRoomExits[index].isEnabled = enabled;
+	}
+	if (persist & PERSIST_PERM)
+		g_engine->_state->roomExitChanges[room].push_back({room, index, enabled});
+}
+
+void RoomManager::disableExit(byte index, int persist) {
 	changeExit(index, false, persist);
 }
 
-void RoomManager::enableExit(int index, bool persist) {
+void RoomManager::disableExit(byte room, byte index, int persist) {
+	changeExit(room, index, false, persist);
+}
+
+void RoomManager::enableExit(byte index, int persist) {
 	changeExit(index, true, persist);
 }
 
-void RoomManager::changeWalkBox(WalkBox walkbox) {
-	g_engine->_state->roomWalkBoxChanges[_currentRoomNumber].push_back({_currentRoomNumber, walkbox.index, walkbox});
+void RoomManager::enableExit(byte room, byte index, int persist) {
+	changeExit(room, index, true, persist);
+}
+
+void RoomManager::changeWalkBox(WalkBox walkbox, int persist) {
+	changeWalkbox(_currentRoomNumber, walkbox, persist);
+}
+
+void RoomManager::changeWalkbox(byte room, WalkBox walkbox, int persist) {
+	if (room == _currentRoomNumber & persist & PERSIST_TEMP) {
+		_currentRoomWalkboxes[walkbox.index] = walkbox;
+	}
+	if (persist & PERSIST_PERM) {
+		g_engine->_state->roomWalkBoxChanges[room].push_back({room, walkbox.index, walkbox});
+	}
 }
 
-void RoomManager::changeHotSpot(HotSpot hotspot) {
-	g_engine->_state->roomHotSpotChanges[_currentRoomNumber].push_back({_currentRoomNumber, hotspot.innerIndex, hotspot});
+void RoomManager::changeHotSpot(HotSpot hotspot, int persist) {
+	changeHotspot(_currentRoomNumber, hotspot, persist);
 }
 
-void RoomManager::disableSprite(byte roomNumber, int spriteIndex, bool persist) {
-	if (roomNumber == _currentRoomNumber) {
+void RoomManager::changeHotspot(byte room, HotSpot hotspot, int persist) {
+	if (room == _currentRoomNumber & persist & PERSIST_TEMP) {
+		for (uint i = 0; i < _currentRoomHotspots.size(); i++) {
+			if (!_currentRoomHotspots[i].isSprite && _currentRoomHotspots[i].innerIndex == hotspot.innerIndex) {
+				_currentRoomHotspots[i] = hotspot;
+				break;
+			}
+		}
+	}
+	if (persist & PERSIST_PERM) {
+		g_engine->_state->roomHotSpotChanges[room].push_back({_currentRoomNumber, hotspot.innerIndex, hotspot});
+	}
+}
+
+void RoomManager::disableSprite(byte spriteIndex, int persist) {
+	disableSprite(_currentRoomNumber, spriteIndex, persist);
+}
+
+void RoomManager::disableSprite(byte roomNumber, byte spriteIndex, int persist) {
+	if (roomNumber == _currentRoomNumber & persist & PERSIST_TEMP) {
 		_currentRoomAnims[spriteIndex].zOrder = 255;
+		_currentRoomAnims[spriteIndex].isHotspotDisabled = true;
+	}
+	if (persist & PERSIST_PERM) {
+		g_engine->_state->spriteChanges[roomNumber].push_back({roomNumber, spriteIndex, 255});
 	}
-	g_engine->_state->disabledSprites[roomNumber].push_back(spriteIndex);
 }
 
-void RoomManager::enableSprite(int spriteIndex, int zOrder, bool persist) {
-	_currentRoomAnims[spriteIndex].zOrder = zOrder;
+void RoomManager::enableSprite(byte spriteIndex, byte zOrder, int persist) {
+	enableSprite(_currentRoomNumber, spriteIndex, zOrder, persist);
 }
 
-void RoomManager::enableHotspot(HotSpot *hotspot, bool persist) {
-	hotspot->isEnabled = true;
-	if (persist) {
-		changeHotSpot(*hotspot);
+void RoomManager::enableSprite(byte roomNumber, byte spriteIndex, byte zOrder, int persist) {
+	if (roomNumber == _currentRoomNumber & persist & PERSIST_TEMP) {
+		_currentRoomAnims[spriteIndex].zOrder = zOrder;
 	}
+	if (persist & PERSIST_PERM) {
+		g_engine->_state->spriteChanges[roomNumber].push_back({roomNumber, spriteIndex, zOrder});
+	}
+}
+
+void RoomManager::enableHotspot(HotSpot *hotspot, int persist) {
+	enableHotspot(_currentRoomNumber, hotspot, persist);
 }
 
-void RoomManager::disableHotspot(HotSpot *hotspot, bool persist) {
-	hotspot->isEnabled = false;
-	if (persist) {
-		changeHotSpot(*hotspot);
+void RoomManager::enableHotspot(byte room, HotSpot *hotspot, int persist) {
+	if (persist & PERSIST_TEMP && room == _currentRoomNumber) {
+		hotspot->isEnabled = true;
+	}
+	if (persist & PERSIST_PERM) {
+		changeHotspot(room, *hotspot);
 	}
 }
 
-void RoomManager::moveHotspot(HotSpot *hotspot, int16 newX, int16 newY, bool persist) {
-	hotspot->x = newX;
-	hotspot->y = newY;
-	if (persist) {
-		changeHotSpot(*hotspot);
+void RoomManager::disableHotspot(HotSpot *hotspot, int persist) {
+	disableHotspot(_currentRoomNumber, hotspot, persist);
+}
+
+void RoomManager::disableHotspot(byte room, HotSpot *hotspot, int persist) {
+	if (persist & PERSIST_TEMP && room == _currentRoomNumber) {
+		hotspot->isEnabled = false;
+	}
+	if (persist & PERSIST_PERM) {
+		changeHotspot(room, *hotspot);
 	}
 }
 
-void RoomManager::setActionMask(HotSpot *hotspot, byte actionMask) {
-	hotspot->actionFlags = actionMask;
-	changeHotSpot(*hotspot);
+void RoomManager::moveHotspot(HotSpot *hotspot, int16 newX, int16 newY, int persist) {
+	if (persist & PERSIST_TEMP) {
+		hotspot->x = newX;
+		hotspot->y = newY;
+	}
+	if (persist & PERSIST_PERM) {
+		changeHotspot(_currentRoomNumber, *hotspot, persist);
+	}
 }
 
-void RoomManager::addWalkbox(WalkBox walkbox) {
-	_currentRoomWalkboxes.push_back(walkbox);
-	g_engine->_state->roomWalkBoxChanges[_currentRoomNumber].push_back({_currentRoomNumber, walkbox.index, walkbox});
+void RoomManager::setActionMask(HotSpot *hotspot, byte actionMask, int persist) {
+	if (persist & PERSIST_TEMP) {
+		hotspot->actionFlags = actionMask;
+	}
+	if (persist & PERSIST_PERM) {
+		changeHotspot(_currentRoomNumber, *hotspot, persist);
+	}
+}
+
+void RoomManager::addWalkbox(WalkBox walkbox, int persist) {
+	if (persist & PERSIST_TEMP) {
+		_currentRoomWalkboxes.push_back(walkbox);
+	}
+	if (persist & PERSIST_PERM) {
+		g_engine->_state->roomWalkBoxChanges[_currentRoomNumber].push_back({_currentRoomNumber, walkbox.index, walkbox});
+	}
 }
 
 Sprite *RoomManager::findSpriteByIndex(byte index) {
@@ -310,20 +396,6 @@ Common::Array<Exit> RoomManager::loadExits(byte *data, size_t size) {
 	int exitDataOffset = 0x1BF;
 	for (int i = 0; i < exitCount; i++) {
 		int exitOffset = exitDataOffset + i * 14;
-		bool isChanged = false;
-		if (g_engine->_state->roomExitChanges.contains(_currentRoomNumber)) {
-			// if the exit has been changed, load the changed version
-			for (int j = 0; j < g_engine->_state->roomExitChanges[_currentRoomNumber].size(); j++) {
-				if (g_engine->_state->roomExitChanges[_currentRoomNumber][j].exitIndex == i) {
-					exits.push_back(g_engine->_state->roomExitChanges[_currentRoomNumber][j].exit);
-					isChanged = true;
-					break;
-				}
-			}
-		}
-
-		if (isChanged)
-			continue;
 
 		Exit exit;
 		exit.index = i;
@@ -355,6 +427,15 @@ Common::Array<Exit> RoomManager::loadExits(byte *data, size_t size) {
 			break;
 		}
 
+		if (g_engine->_state->roomExitChanges.contains(_currentRoomNumber)) {
+			// if the exit has been changed, load the changed version
+			for (int j = 0; j < g_engine->_state->roomExitChanges[_currentRoomNumber].size(); j++) {
+				if (g_engine->_state->roomExitChanges[_currentRoomNumber][j].exitIndex == i) {
+					exit.isEnabled = g_engine->_state->roomExitChanges[_currentRoomNumber][j].enabled;
+					break;
+				}
+			}
+		}
 		exits.push_back(exit);
 		// debug("Exit %d: targetRoom=%d isEnabled=%d x=%d y=%d w=%d h=%d targetX=%d targetY=%d dir=%d",
 		// 	  i, exit.targetRoom, exit.isEnabled, exit.x, exit.y, exit.w, exit.h,
@@ -572,7 +653,7 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 	uint32_t metadata_start = spriteCountPos + (44 * 2 + 5);
 	uint32_t picOffset = 0;
 
-	Common::Array<int> disabledSprites = g_engine->_state->disabledSprites[_currentRoomNumber];
+	Common::Array<SpriteChange> spriteChanges = g_engine->_state->spriteChanges[_currentRoomNumber];
 
 	for (int i = 0; i < spriteCount; i++) {
 		uint32_t animOffset = metadata_start + (i * 44);
@@ -589,10 +670,12 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 		sprite.spriteType = data[animOffset + 33];
 		sprite.actionFlags = data[animOffset + 34];
 		sprite.isHotspotDisabled = data[animOffset + 38];
-		for (int j = 0; j < disabledSprites.size(); j++) {
-			if (disabledSprites[j] == sprite.index) {
-				sprite.zOrder = 255;
-				sprite.isHotspotDisabled = 1;
+		for (int j = 0; j < spriteChanges.size(); j++) {
+			if (spriteChanges[j].spriteIndex == sprite.index) {
+				sprite.zOrder = spriteChanges[j].zIndex;
+				if(sprite.zOrder == 255) {
+					sprite.isHotspotDisabled = 1;
+				}
 				break;
 			}
 		}
@@ -727,7 +810,7 @@ uint32 RoomManager::loadDescriptions(byte *pair12data, size_t pair12size, Common
 				pos++;
 			}
 			// Hardcoded fix in the original!
-			if(_currentRoomNumber == 3 && description.text.size() == 1 && description.text[0] == 0x2D) {
+			if (_currentRoomNumber == 3 && description.text.size() == 1 && description.text[0] == 0x2D) {
 				outDescriptions.push_back(description);
 			}
 			outDescriptions.push_back(description);
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 0c796789a74..96499e7ae1e 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -51,11 +51,10 @@ static const int unpickableHotspotExtras[] = {
 	362,
 };
 
-enum PersistType {
-	PERSIST_TEMP,
-	PERSIST_PERM,
-	PERSIST_BOTH
-};
+
+#define PERSIST_TEMP 1
+#define PERSIST_PERM 2
+#define PERSIST_BOTH 3
 
 class RoomManager {
 public:
@@ -68,41 +67,54 @@ public:
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
 
 	/** Methods to modify room data at runtime **/
-	void addSticker(int stickerId, bool persist = true);
-	void addStickerToRoom(byte room, int stickerId);
-	void onlyPersistSticker(byte room, int stickerId);
-	void removeSticker(int index);
-	void removeSticker(byte room, int index);
+	void addSticker(int stickerId, int persist = PERSIST_BOTH);
+	void addStickerToRoom(byte room, int stickerId, int persist = PERSIST_BOTH);
+
+	void removeSticker(int stickerId);
+	void removeStickerFromRoom(byte room, int stickerId);
+
 	bool hasSticker(int index);
 	bool hasSticker(byte room, int index);
-	void changeExit(int index, bool enabled, bool persist = true);
-	void changeExit(byte room, int index, bool enabled, bool persist = true);
-	void disableExit(int index, bool persist = true);
-	void disableExit(byte room, int index, bool persist = true);
-	void enableExit(int index, bool persist = true);
-	void enableExit(byte room, int index, bool persist = true);
-	void changeWalkBox(WalkBox walkbox);
-	void changeWalkbox(byte room, WalkBox walkbox);
-	void changeHotSpot(HotSpot hotspot);
-	void changeHotspot(byte room, HotSpot hotspot);
-	void disableSprite(byte roomNumber, int spriteIndex, bool persist = true);
-	void enableSprite(int spriteIndex, int zOrder, bool persist = true);
-	void enableSprite(byte roomNumber, int spriteIndex, int zOrder, bool persist = true);
+
+	void changeExit(byte index, bool enabled, int persist = PERSIST_BOTH);
+	void changeExit(byte room, byte index, bool enabled, int persist = PERSIST_BOTH);
+
+	void disableExit(byte index, int persist = PERSIST_BOTH);
+	void disableExit(byte room, byte index, int persist = PERSIST_BOTH);
+	void enableExit(byte index, int persist = PERSIST_BOTH);
+	void enableExit(byte room, byte index, int persist = PERSIST_BOTH);
+
+	void changeWalkBox(WalkBox walkbox, int persist = PERSIST_BOTH);
+	void changeWalkbox(byte room, WalkBox walkbox, int persist = PERSIST_BOTH);
+
+	void changeHotSpot(HotSpot hotspot, int persist = PERSIST_BOTH);
+	void changeHotspot(byte room, HotSpot hotspot, int persist = PERSIST_BOTH);
+
+	void disableSprite(byte spriteIndex, int persist = PERSIST_BOTH);
+	void disableSprite(byte roomNumber, byte spriteIndex, int persist = PERSIST_BOTH);
+	void enableSprite(byte spriteIndex, byte zOrder, int persist = PERSIST_BOTH);
+	void enableSprite(byte roomNumber, byte spriteIndex, byte zOrder, int persist = PERSIST_BOTH);
 	/**
 	 * Utility function to enable or disable a hotspot, with an option to persist the change.
 	 */
-	void enableHotspot(HotSpot *hotspot, bool persist = true);
-	void enableHotspot(byte room, HotSpot *hotspot, bool persist = true);
-	void disableHotspot(HotSpot *hotspot, bool persist = true);
-	void disableHotspot(byte room, HotSpot *hotspot, bool persist = true);
-	void moveHotspot(HotSpot *hotspot, int16 newX, int16 newY, bool persist = true);
-	void setActionMask(HotSpot *hotspot, byte actionMask);
-	void moveHotspot(byte room, HotSpot *hotspot, int16 newX, int16 newY, bool persist = true);
-	void addWalkbox(WalkBox walkbox);
-	void addWalkbox(byte room, WalkBox walkbox);
+	void enableHotspot(HotSpot *hotspot, int persist = PERSIST_BOTH);
+	void enableHotspot(byte room, HotSpot *hotspot, int persist = PERSIST_BOTH);
+
+	void disableHotspot(HotSpot *hotspot, int persist = PERSIST_BOTH);
+	void disableHotspot(byte room, HotSpot *hotspot, int persist = PERSIST_BOTH);
+
+	void moveHotspot(HotSpot *hotspot, int16 newX, int16 newY, int persist = PERSIST_BOTH);
+	void moveHotspot(byte room, HotSpot *hotspot, int16 newX, int16 newY, int persist = PERSIST_BOTH);
+	void setActionMask(HotSpot *hotspot, byte actionMask, int persist = PERSIST_BOTH);
+
+	void addWalkbox(WalkBox walkbox, int persist = PERSIST_BOTH);
+	void addWalkbox(byte room, WalkBox walkbox, int persist = PERSIST_BOTH);
+
 	void applyDisabledChoices(byte roomNumber, byte *conversationData, size_t conversationDataSize);
 	void applyDisabledChoice(ResetEntry entry, byte *conversationData, size_t conversationDataSize);
 	void addDisabledChoice(ChoiceOption choice);
+
+
 	bool isPickableByExtra(uint16 extra) {
 		int size = sizeof(unpickableHotspotExtras) / sizeof(unpickableHotspotExtras[0]);
 		for (int i = 0; i < size; i++) {
diff --git a/engines/pelrock/saveload.cpp b/engines/pelrock/saveload.cpp
index 51066a03da5..b665f9c244f 100644
--- a/engines/pelrock/saveload.cpp
+++ b/engines/pelrock/saveload.cpp
@@ -50,7 +50,8 @@ void syncExit(Common::Serializer &s, Exit &exit) {
 void syncExitChange(Common::Serializer &s, ExitChange &change) {
 	s.syncAsByte(change.roomNumber);
 	s.syncAsByte(change.exitIndex);
-	syncExit(s, change.exit);
+	s.syncAsByte((byte &)change.enabled);
+	// syncExit(s, change.exit);
 }
 
 void syncWalkBox(Common::Serializer &s, WalkBox &walkbox) {
@@ -82,6 +83,12 @@ void syncHotSpot(Common::Serializer &s, HotSpot &hotspot) {
 	s.syncAsByte(hotspot.zOrder);
 }
 
+void syncSpriteChange(Common::Serializer &s, SpriteChange &change) {
+	s.syncAsByte(change.roomNumber);
+	s.syncAsByte(change.spriteIndex);
+	s.syncAsByte(change.zIndex);
+}
+
 void syncHotSpotChange(Common::Serializer &s, HotSpotChange &change) {
 	s.syncAsByte(change.roomNumber);
 	s.syncAsByte(change.hotspotIndex);
@@ -265,34 +272,34 @@ bool syncGameStateData(Common::Serializer &s, GameStateData *gameState) {
 		}
 	}
 
-	uint16 disabledSpritesSize = (uint16)gameState->disabledSprites.size();
+	uint16 disabledSpritesSize = (uint16)gameState->spriteChanges.size();
 	s.syncAsUint16LE(disabledSpritesSize);
 	if (s.isSaving()) {
-		for (const auto &spritePair : gameState->disabledSprites) {
+		for (const auto &spritePair : gameState->spriteChanges) {
 			byte roomNumber = spritePair._key;
 			s.syncAsByte(roomNumber);
-			const Common::Array<int> &sprites = spritePair._value;
+			const Common::Array<SpriteChange> &sprites = spritePair._value;
 			uint16 numSprites = (uint16)sprites.size();
 			s.syncAsUint16LE(numSprites);
 			for (uint16 i = 0; i < numSprites; ++i) {
-				int spriteIndex = sprites[i];
-				s.syncAsByte(spriteIndex);
+				SpriteChange spriteChange = sprites[i];
+				syncSpriteChange(s, spriteChange);
 			}
 		}
 	} else {
-		gameState->disabledSprites.clear();
+		gameState->spriteChanges.clear();
 		for (uint16 idx = 0; idx < disabledSpritesSize; ++idx) {
 			byte roomNumber;
 			s.syncAsByte(roomNumber);
 			uint16 numSprites;
 			s.syncAsUint16LE(numSprites);
-			Common::Array<int> sprites;
+			Common::Array<SpriteChange> sprites;
 			for (uint16 i = 0; i < numSprites; ++i) {
-				int spriteIndex;
-				s.syncAsByte(spriteIndex);
-				sprites.push_back(spriteIndex);
+				SpriteChange spriteChange;
+				syncSpriteChange(s, spriteChange);
+				sprites.push_back(spriteChange);
 			}
-			gameState->disabledSprites[roomNumber] = sprites;
+			gameState->spriteChanges[roomNumber] = sprites;
 		}
 	}
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 1fca82e42bf..5e752d964de 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -327,6 +327,15 @@ enum GameState {
 	COMPUTER = 104
 };
 
+struct SpriteChange
+{
+	byte roomNumber;
+	byte spriteIndex;
+	byte zIndex;
+
+};
+
+
 struct HotSpotChange {
 	byte roomNumber;
 	byte hotspotIndex;
@@ -336,7 +345,7 @@ struct HotSpotChange {
 struct ExitChange {
 	byte roomNumber;
 	byte exitIndex;
-	Exit exit;
+	bool enabled;
 };
 
 struct WalkBoxChange {
@@ -481,7 +490,6 @@ struct GameStateData {
 	Common::Array<byte> inventoryItems;
 	int16 selectedInventoryItem = -1;
 
-
 	int libraryShelf = -1;
 	int selectedBookIndex = -1;
 	unsigned char bookLetter = '\0';
@@ -490,7 +498,7 @@ struct GameStateData {
 	Common::HashMap<byte, Common::Array<WalkBoxChange>> roomWalkBoxChanges;
 	Common::HashMap<byte, Common::Array<HotSpotChange>> roomHotSpotChanges;
 	Common::HashMap<byte, Common::Array<ResetEntry>> disabledBranches;
-	Common::HashMap<byte, Common::Array<int>> disabledSprites;
+	Common::HashMap<byte, Common::Array<SpriteChange>> spriteChanges;
 
 	GameStateData() {
 		memset(conversationRootsState, 0, 4 * 56);


Commit: f22d5018c2dc89379869e0c19647d27ce8886145
    https://github.com/scummvm/scummvm/commit/f22d5018c2dc89379869e0c19647d27ce8886145
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:53+02:00

Commit Message:
PELROCK: Enable animations when museum is open

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index b317a6aa710..c992cb14096 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -101,6 +101,10 @@ const ActionEntry actionTable[] = {
 	{361, PICKUP, &PelrockEngine::pickBooksFromShelf2},
 	{362, PICKUP, &PelrockEngine::pickBooksFromShelf3},
 
+	// Room 16
+	{388, OPEN, &PelrockEngine::openNewspaperDoor},
+	{388, CLOSE, &PelrockEngine::closeNewspaperDoor},
+
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
 	{WILDCARD, TALK, &PelrockEngine::noOpAction},   // Generic talk action
@@ -222,6 +226,9 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_state->setRootDisabledState(room, rootIndex, true);
 		_state->setRootDisabledState(room, rootIndex + 1, true);
 		break;
+	case 267:
+		_state->setRootDisabledState(7, 1, true);
+		break;
 	case 272:
 		_state->setRootDisabledState(room, rootIndex, true);
 		break;
@@ -297,7 +304,7 @@ void PelrockEngine::openIceCreamShopDoor(HotSpot *hotspot) {
 }
 
 void PelrockEngine::closeRoomDrawer(HotSpot *hotspot) {
-	if(!_room->hasSticker(91)) {
+	if (!_room->hasSticker(91)) {
 		_dialog->say(_res->_ingameTexts[YA_CERRADO_M]);
 		return;
 	}
@@ -565,15 +572,23 @@ void PelrockEngine::giveIdToGuard(int inventoryObject, HotSpot *hotspot) {
 	if (!_state->getFlag(FLAG_GUARDIA_DNI_ENTREGADO)) {
 		_state->setFlag(FLAG_GUARDIA_DNI_ENTREGADO, true);
 		_dialog->say(_res->_ingameTexts[DEACUERDO]);
-		return;
 	}
 	if (_state->getFlag(FLAG_SOBORNO_PORTERO) && _state->getFlag(FLAG_GUARDIA_DNI_ENTREGADO)) {
-		_state->setRootDisabledState(4, 2, true);
-		_room->enableSprite(4, 2, 100);
+		unlockMuseum();
 		return;
 	}
 }
 
+void PelrockEngine::unlockMuseum() {
+	_state->setRootDisabledState(4, 2, true);
+	_room->enableSprite(2, 100, PERSIST_PERM);
+	_room->enableSprite(3, 100, PERSIST_PERM);
+	_room->addStickerToRoom(4, 87, PERSIST_PERM);
+	_room->addStickerToRoom(4, 88, PERSIST_PERM);
+	_room->addStickerToRoom(4, 89, PERSIST_PERM);
+	_room->addStickerToRoom(4, 90, PERSIST_PERM);
+}
+
 void PelrockEngine::giveMoneyToGuard(int inventoryObject, HotSpot *hotspot) {
 	if (!_state->getFlag(FLAG_GUARDIA_PIDECOSAS)) {
 		_dialog->say(_res->_ingameTexts[PRETENDEUSTED_SOBORNARME]);
@@ -582,12 +597,9 @@ void PelrockEngine::giveMoneyToGuard(int inventoryObject, HotSpot *hotspot) {
 		_state->setFlag(FLAG_SOBORNO_PORTERO, true);
 		_dialog->say(_res->_ingameTexts[MUYBIEN]);
 		_state->removeInventoryItem(5); // Remove 1000 pesetas bill
-		return;
 	}
 	if (_state->getFlag(FLAG_SOBORNO_PORTERO) && _state->getFlag(FLAG_GUARDIA_DNI_ENTREGADO)) {
-		_state->setRootDisabledState(4, 2, true);
-		_room->enableSprite(2, 100);
-		_room->enableSprite(3, 100);
+		unlockMuseum();
 		return;
 	}
 }
@@ -618,7 +630,6 @@ void PelrockEngine::useAmuletWithStatue(int inventoryObject, HotSpot *hotspot) {
 		_currentHotspot = statueHotspot;
 
 		walkAndAction(statueHotspot, TALK);
-		_state->setRootDisabledState(7, 1, true);
 
 		// TODO: Undo palette anim!
 	}
@@ -664,15 +675,38 @@ void PelrockEngine::giveFormulaToLibrarian(int inventoryObject, HotSpot *hotspot
 }
 
 void PelrockEngine::openNewspaperDoor(HotSpot *hotspot) {
-	openDoor(hotspot, 0, 90, FEMININE, true);
+	openDoor(hotspot, 2, 50, MASCULINE, false);
+}
+
+void PelrockEngine::closeNewspaperDoor(HotSpot *hotspot) {
+	closeDoor(hotspot, 2, 50, MASCULINE, false);
 }
 
 void PelrockEngine::pickUpBook(int i) {
 	if (!_state->hasInventoryItem(10)) {
 		_dialog->say(_res->_ingameTexts[VENGA_ACA]);
 		_state->setRootDisabledState(9, 0, true);
+
+		if (_state->hasInventoryItem(3)) {
+			_state->setRootDisabledState(9, 1, true);
+			addInventoryItem(10);
+		}
+
 		_alfredState.isWalkingCancelable = false;
 		walkAndAction(_room->findHotspotByExtra(102), TALK);
+		// After dialog ends, reenable first dialog root if no photo in inventory
+		// Wait for dialog to end to reenable first dialog root
+		while (!shouldQuit() && _queuedAction.isQueued) {
+			_events->pollEvent();
+			renderScene(OVERLAY_NONE);
+			_screen->update();
+		}
+		if (!_state->hasInventoryItem(3)) {
+
+			_state->setRootDisabledState(9, 0, false);
+		} else {
+			_state->setRootDisabledState(9, 2, true);
+		}
 	} else {
 		if (_state->libraryShelf == -1) {
 			_dialog->say(_res->_ingameTexts[TODOS]);
@@ -764,10 +798,9 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		_alfredState.animState = ALFRED_SPECIAL_ANIM;
 		waitForSpecialAnimation();
 		loadExtraScreenAndPresent(5);
-		if(_state->getFlag(FLAG_ALFRED_SABE_EGIPCIO)) {
+		if (_state->getFlag(FLAG_ALFRED_SABE_EGIPCIO)) {
 			_dialog->say(_res->_ingameTexts[FORMULAVIAJETIEMPO]);
-		}
-		else {
+		} else {
 			_dialog->say(_res->_ingameTexts[QUELASTIMA_NOSEEGIPCIO]);
 		}
 		break;
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 994839b0fec..f2cafbb23e1 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -152,9 +152,9 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		setScreen(0, ALFRED_DOWN);
+		// setScreen(0, ALFRED_DOWN);
 		// setScreen(3, ALFRED_RIGHT);
-		// setScreen(5, ALFRED_DOWN);
+		setScreen(7, ALFRED_DOWN);
 		// setScreen(9, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 345cc3407e6..0ec596a1d25 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -285,6 +285,7 @@ public:
 	void useCordWithPlug(int inventoryObject, HotSpot *hotspot);
 	void pickCables(HotSpot *hotspot);
 	void giveIdToGuard(int inventoryObject, HotSpot *hotspot);
+	void unlockMuseum();
 	void giveMoneyToGuard(int inventoryObject, HotSpot *hotspot);
 	void openMuseumDoor(HotSpot *hotspot);
 	void useAmuletWithStatue(int inventoryObject, HotSpot *hotspot);
@@ -298,6 +299,7 @@ public:
 	void pickBooksFromShelf3(HotSpot *hotspot);
 	void giveFormulaToLibrarian(int inventoryObject, HotSpot *hotspot);
 	void openNewspaperDoor(HotSpot *hotspot);
+	void closeNewspaperDoor(HotSpot *hotspot);
 	void pickUpBook(int i);
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 86d5e81bdab..e00d9780776 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -153,7 +153,7 @@ void RoomManager::changeExit(byte index, bool enabled, int persist) {
 }
 
 void RoomManager::changeExit(byte room, byte index, bool enabled, int persist) {
-	if (room == _currentRoomNumber & persist & PERSIST_TEMP) {
+	if (room == _currentRoomNumber && persist & PERSIST_TEMP) {
 		_currentRoomExits[index].isEnabled = enabled;
 	}
 	if (persist & PERSIST_PERM)
@@ -181,7 +181,7 @@ void RoomManager::changeWalkBox(WalkBox walkbox, int persist) {
 }
 
 void RoomManager::changeWalkbox(byte room, WalkBox walkbox, int persist) {
-	if (room == _currentRoomNumber & persist & PERSIST_TEMP) {
+	if (room == _currentRoomNumber && persist & PERSIST_TEMP) {
 		_currentRoomWalkboxes[walkbox.index] = walkbox;
 	}
 	if (persist & PERSIST_PERM) {
@@ -194,7 +194,7 @@ void RoomManager::changeHotSpot(HotSpot hotspot, int persist) {
 }
 
 void RoomManager::changeHotspot(byte room, HotSpot hotspot, int persist) {
-	if (room == _currentRoomNumber & persist & PERSIST_TEMP) {
+	if (room == _currentRoomNumber && persist & PERSIST_TEMP) {
 		for (uint i = 0; i < _currentRoomHotspots.size(); i++) {
 			if (!_currentRoomHotspots[i].isSprite && _currentRoomHotspots[i].innerIndex == hotspot.innerIndex) {
 				_currentRoomHotspots[i] = hotspot;
@@ -212,7 +212,7 @@ void RoomManager::disableSprite(byte spriteIndex, int persist) {
 }
 
 void RoomManager::disableSprite(byte roomNumber, byte spriteIndex, int persist) {
-	if (roomNumber == _currentRoomNumber & persist & PERSIST_TEMP) {
+	if (roomNumber == _currentRoomNumber && persist & PERSIST_TEMP) {
 		_currentRoomAnims[spriteIndex].zOrder = 255;
 		_currentRoomAnims[spriteIndex].isHotspotDisabled = true;
 	}
@@ -226,7 +226,7 @@ void RoomManager::enableSprite(byte spriteIndex, byte zOrder, int persist) {
 }
 
 void RoomManager::enableSprite(byte roomNumber, byte spriteIndex, byte zOrder, int persist) {
-	if (roomNumber == _currentRoomNumber & persist & PERSIST_TEMP) {
+	if (roomNumber == _currentRoomNumber && persist & PERSIST_TEMP) {
 		_currentRoomAnims[spriteIndex].zOrder = zOrder;
 	}
 	if (persist & PERSIST_PERM) {


Commit: 2e9a0fb46e043e6890483b4404fab104fa776426
    https://github.com/scummvm/scummvm/commit/2e9a0fb46e043e6890483b4404fab104fa776426
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:53+02:00

Commit Message:
PELROCK: Room 17

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index c992cb14096..05d829c0c4e 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -105,6 +105,10 @@ const ActionEntry actionTable[] = {
 	{388, OPEN, &PelrockEngine::openNewspaperDoor},
 	{388, CLOSE, &PelrockEngine::closeNewspaperDoor},
 
+	// Room 17
+	{393, OPEN, &PelrockEngine::openNewspaperBossDor},
+	{393, CLOSE, &PelrockEngine::closeNewspaperBossDoor},
+
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
 	{WILDCARD, TALK, &PelrockEngine::noOpAction},   // Generic talk action
@@ -682,6 +686,14 @@ void PelrockEngine::closeNewspaperDoor(HotSpot *hotspot) {
 	closeDoor(hotspot, 2, 50, MASCULINE, false);
 }
 
+void PelrockEngine::openNewspaperBossDor(HotSpot *hotspot) {
+	openDoor(hotspot, 1, 52, MASCULINE, true);
+}
+
+void PelrockEngine::closeNewspaperBossDoor(HotSpot *hotspot) {
+	closeDoor(hotspot, 1, 52, MASCULINE, true);
+}
+
 void PelrockEngine::pickUpBook(int i) {
 	if (!_state->hasInventoryItem(10)) {
 		_dialog->say(_res->_ingameTexts[VENGA_ACA]);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 0ec596a1d25..236d21befe0 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -300,6 +300,8 @@ public:
 	void giveFormulaToLibrarian(int inventoryObject, HotSpot *hotspot);
 	void openNewspaperDoor(HotSpot *hotspot);
 	void closeNewspaperDoor(HotSpot *hotspot);
+	void openNewspaperBossDor(HotSpot *hotspot);
+	void closeNewspaperBossDoor(HotSpot *hotspot);
 	void pickUpBook(int i);
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index e00d9780776..f61b3d20e67 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -704,6 +704,7 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 				uint32_t needed = anim.w * anim.h * anim.nframes;
 				for (int i = 0; i < anim.nframes; i++) {
 					anim.animData[i] = new byte[anim.w * anim.h];
+					// debug("Extracting frame %d for anim %d-%d, w=%d h=%d, pixelDataSize=%d, current offset %d", i, j, anim.nframes, anim.w, anim.h, pixelDataSize, picOffset);
 					extractSingleFrame(pixelData + picOffset, anim.animData[i], i, anim.w, anim.h);
 				}
 				sprite.animData[j] = anim;


Commit: 97258764ff0c4ac1cc557d4ea5196f62581a9571
    https://github.com/scummvm/scummvm/commit/97258764ff0c4ac1cc557d4ea5196f62581a9571
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:53+02:00

Commit Message:
PELROCK: Implements passer-by animations

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index f2cafbb23e1..868cacbef3a 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -200,16 +200,20 @@ Common::Array<VerbIcon> PelrockEngine::availableActions(HotSpot *hotspot) {
 
 // Sort sprites by zOrder in-place using insertion sort (efficient for nearly-sorted data)
 void sortAnimsByZOrder(Common::Array<Sprite> &anims) {
-	for (size_t i = 1; i < anims.size(); ++i) {
+	for (size_t i = 0; i < anims.size(); ++i) {
 		Sprite key = anims[i];
 		int z = key.zOrder;
 		size_t j = i;
-		while (j > 0 && anims[j - 1].zOrder > z) {
+		while (j > 0 && anims[j - 1].zOrder < z) {
 			anims[j] = anims[j - 1];
 			--j;
 		}
 		anims[j] = key;
 	}
+	debug("Sorted anims by zOrder");
+	for (size_t i = 0; i < anims.size(); i++) {
+		debug("Anim %d, extra = %d: zOrder=%d", i, anims[i].extra, anims[i].zOrder);
+	}
 }
 
 void PelrockEngine::playSoundIfNeeded() {
@@ -251,11 +255,13 @@ bool PelrockEngine::renderScene(int overlayMode) {
 	return false;
 }
 
-const int kPasserbyTriggerFrameInterval = 0x3FF;
+// const int kPasserbyTriggerFrameInterval = 0x3FF;
 
+const int kPasserbyTriggerFrameInterval = 15;
 void PelrockEngine::frameTriggers() {
-	if ((_chrono->getFrameCount() & kPasserbyTriggerFrameInterval) == kPasserbyTriggerFrameInterval) {
-		debug("Would trigger passer-by");
+	uint32 frameCount = _chrono->getFrameCount();
+	// Passerby animations
+	if ((frameCount & kPasserbyTriggerFrameInterval) == kPasserbyTriggerFrameInterval) {
 		switch (_room->_currentRoomNumber) {
 		case 9: {
 			Sprite *mouse = _room->findSpriteByIndex(2);
@@ -269,11 +275,8 @@ void PelrockEngine::frameTriggers() {
 			mouse->animData[3].movementFlags = 0x3E0;
 			break;
 		}
-		default:
-			break;
 		}
 	}
-
 	if (_room->_currentRoomNumber == 9) {
 		// Mouse animation on library
 		Sprite *mouse = _room->findSpriteByIndex(2);
@@ -286,6 +289,54 @@ void PelrockEngine::frameTriggers() {
 			}
 		}
 	}
+	passerByAnim(frameCount);
+}
+
+void PelrockEngine::passerByAnim(uint32 frameCount) {
+	if (_room->_passerByAnims == nullptr) {
+		debug("No passerby anims for this room");
+		return;
+	}
+	if (_room->_passerByAnims->latch == false) {
+		int animIndex = _room->_passerByAnims->numAnims == 1 ? 0 : getRandomNumber(_room->_passerByAnims->numAnims - 1);
+		PasserByAnim anim = _room->_passerByAnims->passerByAnims[animIndex];
+		if ((frameCount & anim.frameTrigger) == anim.frameTrigger) {
+			Sprite *sprite = _room->findSpriteByIndex(anim.spriteIndex);
+			if (sprite && sprite->zOrder == -1) {
+				debug("Starting passerby anim for sprite %d at index %d", anim.spriteIndex, animIndex);
+				sprite->zOrder = anim.targetZIndex;
+				_room->_passerByAnims->latch = true;
+				_room->_passerByAnims->currentAnimIndex = animIndex;
+			}
+		}
+	} else {
+		PasserByAnim anim = _room->_passerByAnims->passerByAnims[_room->_passerByAnims->currentAnimIndex];
+		byte direction = anim.dir;
+		int spriteIndex = anim.spriteIndex;
+		int startX = anim.startX;
+		int startY = anim.startY;
+
+		Sprite *sprite = _room->findSpriteByIndex(spriteIndex);
+		if (direction == RIGHT) {
+			if (sprite->x >= anim.resetX) {
+				sprite->x = startX;
+				sprite->y = startY;
+				sprite->zOrder = -1;
+				sprite->curAnimIndex = 0;
+				sprite->animData[0].curFrame = 0;
+				_room->_passerByAnims->latch = false;
+			}
+		} else if (direction == LEFT) {
+			if (sprite->x <= anim.resetX) {
+				sprite->x = startX;
+				sprite->y = startY;
+				sprite->zOrder = -1;
+				sprite->curAnimIndex = 0;
+				sprite->animData[0].curFrame = 0;
+				_room->_passerByAnims->latch = false;
+			}
+		}
+	}
 }
 
 void PelrockEngine::executeAction(VerbIcon action, HotSpot *hotspot) {
@@ -1307,7 +1358,7 @@ void PelrockEngine::gameLoop() {
 void PelrockEngine::computerLoop() {
 	Computer computer(_events);
 	computer.run();
-	if(_state->selectedBookIndex != -1 && _state->bookLetter != '\0') {
+	if (_state->selectedBookIndex != -1 && _state->bookLetter != '\0') {
 		Common::StringArray lines;
 		lines.push_back(Common::String::format(computer._memorizedMsg, _state->bookLetter));
 		_dialog->sayAlfred(lines);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 236d21befe0..1ec970af8d2 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -230,6 +230,8 @@ public:
 	bool renderScene(int overlayMode = OVERLAY_NONE);
 	void frameTriggers();
 
+	void passerByAnim(uint32 frameCount);
+
 	void changeCursor(Cursor cursor);
 
 	// Actions
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index f61b3d20e67..8ecf72ff31d 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -589,10 +589,205 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 		_currentPaletteAnim = nullptr;
 	}
 
+	if (_passerByAnims != nullptr) {
+		delete _passerByAnims;
+	}
+	_passerByAnims = loadPasserByAnims(roomNumber);
+
 	delete[] pair10;
 	delete[] pair12;
 }
 
+int streetWalkerIndices[] = {
+	-1, // room 0,
+	5,  // room 1,
+	3,  // room 2,
+	6,  // room 3,
+	-1, // room 4,
+	-1, // room 5,
+	-1, // room 6,
+	-1, // room 7,
+	7,  // room 8,
+	-1, // room 9,
+	-1, // room 10,
+	-1, // room 11,
+	-1, // room 12,
+	-1, // room 13,
+	2,  // room 14,
+	-1, // room 15,
+	2
+
+};
+RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
+	RoomPasserBys *anims = nullptr;
+	switch (roomNumber) {
+		// case 9: {
+		// 		Sprite *mouse = findSpriteByIndex(2);
+		// 		mouse->zOrder = 1;
+		// 		mouse->animData[0].loopCount = 3;
+		// 		mouse->animData[1].loopCount = 1;
+		// 		mouse->animData[1].movementFlags = 0x3FF;
+		// 		mouse->animData[2].loopCount = 1;
+		// 		mouse->animData[2].movementFlags = 0x801F;
+		// 		mouse->animData[3].loopCount = 3;
+		// 		mouse->animData[3].movementFlags = 0x3E0;
+		// 		break;
+		// 	}
+
+	case 1:
+	case 2:
+	case 3:
+	case 8:
+	case 14:
+	case 16: {
+		anims = new RoomPasserBys(roomNumber, 1);
+		PasserByAnim anim;
+		anim.spriteIndex = streetWalkerIndices[roomNumber];
+		Sprite *camel = findSpriteByIndex(anim.spriteIndex);
+		anim.startX = camel->x;
+		anim.startY = camel->y;
+		anim.dir = RIGHT;
+		anim.frameTrigger = 0x1FFF;
+		anim.targetZIndex = 1;
+		anim.resetX = 639 + camel->w;
+
+		anims->passerByAnims[0] = anim;
+		break;
+	}
+	case 21: {
+		anims = new RoomPasserBys(roomNumber, 1);
+		PasserByAnim anim;
+		anim.spriteIndex = 3;
+		Sprite *camel = findSpriteByIndex(3);
+		anim.startX = camel->x;
+		anim.startY = camel->y;
+		anim.dir = LEFT;
+		anim.resetX = 0 - camel->w;
+		anim.targetZIndex = 1;
+
+		anims->passerByAnims[0] = anim;
+		break;
+	}
+	case 29: {
+		Sprite *carLeft = findSpriteByIndex(2);
+		Sprite *carRight = findSpriteByIndex(3);
+
+		anims = new RoomPasserBys(roomNumber, 2);
+		PasserByAnim animA;
+		animA.spriteIndex = 2;
+		animA.startX = carLeft->x;
+		animA.startY = carLeft->y;
+		animA.dir = LEFT;
+		animA.resetX = carRight->x + carRight->w - carLeft->w;
+		animA.targetZIndex = 100;
+
+		anims->passerByAnims[0] = animA;
+		PasserByAnim animB;
+		animB.spriteIndex = 3;
+		animB.startX = carRight->x;
+		animB.startY = carRight->y;
+		animB.dir = RIGHT;
+		animB.targetZIndex = 100;
+		animB.resetX = 639 + carRight->w;
+		anims->passerByAnims[1] = animB;
+		break;
+	}
+	case 31: {
+
+		anims = new RoomPasserBys(roomNumber, 1);
+		Sprite *walker = findSpriteByIndex(2);
+		Sprite *dark = findSpriteByIndex(5);
+		PasserByAnim anim;
+		anim.spriteIndex = 2;
+		anim.startX = walker->x;
+		anim.startY = walker->y;
+		anim.dir = RIGHT;
+		anim.resetX = dark->x;
+		anim.targetZIndex = dark->zOrder + 1;
+		anims->passerByAnims[0] = anim;
+		break;
+	}
+	case 46: {
+		Sprite *catRight = findSpriteByIndex(2);
+		Sprite *catLeft = findSpriteByIndex(3);
+		Sprite *blank = findSpriteByIndex(0);
+		anims = new RoomPasserBys(roomNumber, 2);
+		PasserByAnim animA;
+		animA.spriteIndex = 2;
+		animA.startX = catRight->x;
+		animA.startY = catRight->y;
+		animA.dir = RIGHT;
+		animA.resetX = catLeft->x;
+		animA.targetZIndex = blank->zOrder + 1;
+
+		anims->passerByAnims[0] = animA;
+		PasserByAnim animB;
+		animB.spriteIndex = 3;
+		animB.startX = catLeft->x;
+		animB.startY = catLeft->y;
+		animB.dir = LEFT;
+		animB.resetX = blank->x;
+		animB.targetZIndex = blank->zOrder + 1;
+		anims->passerByAnims[1] = animB;
+		break;
+	}
+	case 47: {
+		Sprite *mouseRight = findSpriteByIndex(3);
+		Sprite *mouseLeft = findSpriteByIndex(4);
+		Sprite *papers = findSpriteByIndex(1);
+
+		anims = new RoomPasserBys(roomNumber, 2);
+		PasserByAnim animA;
+		animA.spriteIndex = 3;
+		animA.startX = mouseRight->x;
+		animA.startY = mouseRight->y;
+		animA.dir = RIGHT;
+		animA.resetX = mouseLeft->x;
+		animA.targetZIndex = papers->zOrder + 1;
+		anims->passerByAnims[0] = animA;
+
+		PasserByAnim animB;
+		animB.spriteIndex = 4;
+		animB.startX = mouseLeft->x;
+		animB.startY = mouseLeft->y;
+		animB.dir = LEFT;
+		animB.resetX = mouseRight->x;
+		animB.targetZIndex = papers->zOrder + 1;
+		anims->passerByAnims[1] = animB;
+		break;
+	}
+	case 50: {
+		Sprite *mummyLeft = findSpriteByIndex(2);
+		Sprite *mummyRight = findSpriteByIndex(3);
+
+		anims = new RoomPasserBys(roomNumber, 2);
+		PasserByAnim animA;
+		animA.spriteIndex = 2;
+		animA.startX = mummyLeft->x;
+		animA.startY = mummyLeft->y;
+		animA.dir = LEFT;
+		animA.resetX = 0 - mummyLeft->w;
+		animA.targetZIndex = 1;
+
+		anims->passerByAnims[0] = animA;
+		PasserByAnim animB;
+		animB.spriteIndex = 3;
+		animB.startX = mummyRight->x;
+		animB.startY = mummyRight->y;
+		animB.dir = RIGHT;
+		animB.targetZIndex = 1;
+		animB.resetX = 639 + mummyRight->w;
+		anims->passerByAnims[1] = animB;
+		break;
+	}
+	default:
+		break;
+	}
+	if (anims != nullptr)
+		debug("Loaded passerby anims for room %d, count = %d", roomNumber, anims->numAnims);
+	return anims;
+}
+
 Common::Array<HotSpot> RoomManager::unifyHotspots(Common::Array<Pelrock::Sprite> &anims, Common::Array<Pelrock::HotSpot> &staticHotspots) {
 	Common::Array<HotSpot> unifiedHotspots;
 	for (int i = 0; i < anims.size(); i++) {
@@ -673,7 +868,7 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 		for (int j = 0; j < spriteChanges.size(); j++) {
 			if (spriteChanges[j].spriteIndex == sprite.index) {
 				sprite.zOrder = spriteChanges[j].zIndex;
-				if(sprite.zOrder == 255) {
+				if (sprite.zOrder == 255) {
 					sprite.isHotspotDisabled = 1;
 				}
 				break;
@@ -945,7 +1140,7 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 	readUntilBuda(&talkFile, talkHeader.spritePointer, data, dataSize);
 	size_t decompressedSize = rleDecompress(data, dataSize, 0, dataSize, &decompressed);
 	free(data);
-	// debug("Decompressed talking anim A size: %zu, decompressed size: %zu", dataSize, decompressedSize);
+	debug("Decompressed talking anim A size: %zu, decompressed size: %zu", dataSize, decompressedSize);
 	for (int i = 0; i < talkHeader.numFramesAnimA; i++) {
 		talkHeader.animA[i] = new byte[talkHeader.wAnimA * talkHeader.hAnimA];
 		extractSingleFrame(decompressed, talkHeader.animA[i], i, talkHeader.wAnimA, talkHeader.hAnimA);
@@ -955,7 +1150,14 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 		talkHeader.animB = new byte *[talkHeader.numFramesAnimB];
 		for (int i = 0; i < talkHeader.numFramesAnimB; i++) {
 			talkHeader.animB[i] = new byte[talkHeader.wAnimB * talkHeader.hAnimB];
-			extractSingleFrame(decompressed + animASize, talkHeader.animB[i], i, talkHeader.wAnimB, talkHeader.hAnimB);
+			uint32 offset = animASize + (i * talkHeader.wAnimB * talkHeader.hAnimB);
+			debug("Extracting talking anim B frame %d at offset %d, size = %d", i, animASize + (i * talkHeader.wAnimB * talkHeader.hAnimB), talkHeader.wAnimB * talkHeader.hAnimB);
+			if (offset + talkHeader.wAnimB * talkHeader.hAnimB >= decompressedSize) {
+				debug("Error: offset %d is beyond decompressed size %zu", offset, decompressedSize);
+				talkHeader.numFramesAnimB = 0;
+			} else {
+				extractSingleFrame(decompressed + animASize, talkHeader.animB[i], i, talkHeader.wAnimB, talkHeader.hAnimB);
+			}
 		}
 	}
 	free(decompressed);
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 96499e7ae1e..0ae126f14eb 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -61,6 +61,7 @@ public:
 	RoomManager();
 	~RoomManager();
 	void loadRoomMetadata(Common::File *roomFile, int roomNumber);
+	RoomPasserBys *loadPasserByAnims(int roomNumber);
 	Common::Array<HotSpot> unifyHotspots(Common::Array<Pelrock::Sprite> &anims, Common::Array<Pelrock::HotSpot> &staticHotspots);
 	void loadRoomTalkingAnimations(int roomNumber);
 	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
@@ -150,6 +151,7 @@ public:
 	byte _musicTrack = 0;
 	Common::Array<byte> _roomSfx;
 	PaletteAnim *_currentPaletteAnim = nullptr;
+	RoomPasserBys *_passerByAnims = nullptr;
 	byte *_conversationData = nullptr;
 	size_t _conversationDataSize = 0;
 	Common::Array<Sticker> _roomStickers;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 5e752d964de..a7b861aec4c 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -405,6 +405,30 @@ struct PaletteAnim {
 	byte tickCount = 0;
 };
 
+#define RIGHT 0
+#define LEFT 1
+
+struct PasserByAnim
+{
+	uint32 frameTrigger = 0x3FF;
+	int16 startX;
+	int16 startY;
+	int16 resetX;
+	bool dir;
+	byte spriteIndex;
+	byte targetZIndex;
+};
+
+struct RoomPasserBys {
+	byte roomNumber;
+	PasserByAnim passerByAnims[2];
+	byte currentAnimIndex = 0;
+	byte numAnims = 0;
+	bool latch = false;
+	RoomPasserBys(byte roomNum, byte numAnims) : roomNumber(roomNum), numAnims(numAnims) {}
+};
+
+
 /**
  * Structure to hold a parsed choice option
  */


Commit: d369e67fd78693d0580f158d5a60c911fa6ca4ae
    https://github.com/scummvm/scummvm/commit/d369e67fd78693d0580f158d5a60c911fa6ca4ae
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:54+02:00

Commit Message:
PELROCK: Model mouse animation in room9 as passer-by

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 868cacbef3a..275d622f846 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -210,10 +210,10 @@ void sortAnimsByZOrder(Common::Array<Sprite> &anims) {
 		}
 		anims[j] = key;
 	}
-	debug("Sorted anims by zOrder");
-	for (size_t i = 0; i < anims.size(); i++) {
-		debug("Anim %d, extra = %d: zOrder=%d", i, anims[i].extra, anims[i].zOrder);
-	}
+	// debug("Sorted anims by zOrder");
+	// for (size_t i = 0; i < anims.size(); i++) {
+	// debug("Anim %d, extra = %d: zOrder=%d", i, anims[i].extra, anims[i].zOrder);
+	// }
 }
 
 void PelrockEngine::playSoundIfNeeded() {
@@ -260,35 +260,6 @@ bool PelrockEngine::renderScene(int overlayMode) {
 const int kPasserbyTriggerFrameInterval = 15;
 void PelrockEngine::frameTriggers() {
 	uint32 frameCount = _chrono->getFrameCount();
-	// Passerby animations
-	if ((frameCount & kPasserbyTriggerFrameInterval) == kPasserbyTriggerFrameInterval) {
-		switch (_room->_currentRoomNumber) {
-		case 9: {
-			Sprite *mouse = _room->findSpriteByIndex(2);
-			mouse->zOrder = 1;
-			mouse->animData[0].loopCount = 3;
-			mouse->animData[1].loopCount = 1;
-			mouse->animData[1].movementFlags = 0x3FF;
-			mouse->animData[2].loopCount = 1;
-			mouse->animData[2].movementFlags = 0x801F;
-			mouse->animData[3].loopCount = 3;
-			mouse->animData[3].movementFlags = 0x3E0;
-			break;
-		}
-		}
-	}
-	if (_room->_currentRoomNumber == 9) {
-		// Mouse animation on library
-		Sprite *mouse = _room->findSpriteByIndex(2);
-		if (mouse) {
-			if (mouse->y > 355) {
-				mouse->x = 82;
-				mouse->y = 315;
-				mouse->zOrder = 255;
-				mouse->curAnimIndex = 0;
-			}
-		}
-	}
 	passerByAnim(frameCount);
 }
 
@@ -305,6 +276,9 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 			if (sprite && sprite->zOrder == -1) {
 				debug("Starting passerby anim for sprite %d at index %d", anim.spriteIndex, animIndex);
 				sprite->zOrder = anim.targetZIndex;
+				sprite->curAnimIndex = 0;
+				sprite->animData[0].curFrame = 0;
+				sprite->animData[0].curLoop = 0;
 				_room->_passerByAnims->latch = true;
 				_room->_passerByAnims->currentAnimIndex = animIndex;
 			}
@@ -312,13 +286,15 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 	} else {
 		PasserByAnim anim = _room->_passerByAnims->passerByAnims[_room->_passerByAnims->currentAnimIndex];
 		byte direction = anim.dir;
+
 		int spriteIndex = anim.spriteIndex;
 		int startX = anim.startX;
 		int startY = anim.startY;
-
+		debug("Checking passerby anim %d for sprite %d, direction %d", _room->_passerByAnims->currentAnimIndex, spriteIndex, direction);
 		Sprite *sprite = _room->findSpriteByIndex(spriteIndex);
-		if (direction == RIGHT) {
-			if (sprite->x >= anim.resetX) {
+		if (direction == PASSERBY_RIGHT) {
+			debug("Checking passerby anim for sprite %d moving RIGHT, curpos is %d", spriteIndex, sprite->x);
+			if (sprite->x >= anim.resetCoord) {
 				sprite->x = startX;
 				sprite->y = startY;
 				sprite->zOrder = -1;
@@ -326,8 +302,20 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 				sprite->animData[0].curFrame = 0;
 				_room->_passerByAnims->latch = false;
 			}
-		} else if (direction == LEFT) {
-			if (sprite->x <= anim.resetX) {
+		} else if (direction == PASSERBY_LEFT) {
+			debug("Checking passerby anim for sprite %d moving LEFT, curpos is %d", spriteIndex, sprite->x);
+
+			if (sprite->x <= anim.resetCoord) {
+				sprite->x = startX;
+				sprite->y = startY;
+				sprite->zOrder = -1;
+				sprite->curAnimIndex = 0;
+				sprite->animData[0].curFrame = 0;
+				_room->_passerByAnims->latch = false;
+			}
+		} else if (direction == PASSERBY_DOWN) {
+			debug("Checking passerby anim for sprite %d moving DOWN, curpos is %d, reset %d", spriteIndex, sprite->y, anim.resetCoord);
+			if (sprite->y >= anim.resetCoord) {
 				sprite->x = startX;
 				sprite->y = startY;
 				sprite->zOrder = -1;
@@ -1013,6 +1001,10 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 		return;
 	}
 
+	if(_room->_currentRoomNumber == 9 && sprite->index == 2) {
+		debug("Drawing sprite 2 in room 9, anim %d, frame %d/%d, loop %d/%d", sprite->curAnimIndex, animData.curFrame, animData.nframes, animData.curLoop, animData.loopCount);
+	}
+
 	applyMovement(&(sprite->x), &(sprite->y), &(sprite->zOrder), animData.movementFlags);
 	int x = sprite->x;
 	int y = sprite->y;
@@ -1352,6 +1344,7 @@ void PelrockEngine::gameLoop() {
 	_events->pollEvent();
 	checkMouse();
 	renderScene();
+	// _events->waitForKey();
 	_screen->update();
 }
 
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 8ecf72ff31d..473bd24fb27 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -621,18 +621,29 @@ int streetWalkerIndices[] = {
 RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 	RoomPasserBys *anims = nullptr;
 	switch (roomNumber) {
-		// case 9: {
-		// 		Sprite *mouse = findSpriteByIndex(2);
-		// 		mouse->zOrder = 1;
-		// 		mouse->animData[0].loopCount = 3;
-		// 		mouse->animData[1].loopCount = 1;
-		// 		mouse->animData[1].movementFlags = 0x3FF;
-		// 		mouse->animData[2].loopCount = 1;
-		// 		mouse->animData[2].movementFlags = 0x801F;
-		// 		mouse->animData[3].loopCount = 3;
-		// 		mouse->animData[3].movementFlags = 0x3E0;
-		// 		break;
-		// 	}
+	case 9: {
+		Sprite *mouse = findSpriteByIndex(2);
+		Sprite *blank = findSpriteByIndex(4);
+		mouse->animData[0].loopCount = 3;
+		mouse->animData[1].loopCount = 1;
+		mouse->animData[1].movementFlags = 0x3FF;
+		mouse->animData[2].loopCount = 1;
+		mouse->animData[2].movementFlags = 0x801F;
+		mouse->animData[3].loopCount = 4;
+		mouse->animData[3].movementFlags = 0x3C0;
+
+		anims = new RoomPasserBys(roomNumber, 1);
+		PasserByAnim anim;
+		anim.spriteIndex = 2;
+		anim.startX = mouse->x;
+		anim.startY = mouse->y;
+		anim.dir = PASSERBY_DOWN;
+		anim.targetZIndex = blank->zOrder + 1;
+		anim.resetCoord = blank->y;
+		anims->passerByAnims[0] = anim;
+		debug("Loaded passerby animation for room %d, direction = %d", roomNumber, anim.dir);
+		break;
+	}
 
 	case 1:
 	case 2:
@@ -646,10 +657,10 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		Sprite *camel = findSpriteByIndex(anim.spriteIndex);
 		anim.startX = camel->x;
 		anim.startY = camel->y;
-		anim.dir = RIGHT;
+		anim.dir = PASSERBY_RIGHT;
 		anim.frameTrigger = 0x1FFF;
 		anim.targetZIndex = 1;
-		anim.resetX = 639 + camel->w;
+		anim.resetCoord = 639 + camel->w;
 
 		anims->passerByAnims[0] = anim;
 		break;
@@ -661,8 +672,8 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		Sprite *camel = findSpriteByIndex(3);
 		anim.startX = camel->x;
 		anim.startY = camel->y;
-		anim.dir = LEFT;
-		anim.resetX = 0 - camel->w;
+		anim.dir = PASSERBY_LEFT;
+		anim.resetCoord = 0 - camel->w;
 		anim.targetZIndex = 1;
 
 		anims->passerByAnims[0] = anim;
@@ -677,8 +688,8 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		animA.spriteIndex = 2;
 		animA.startX = carLeft->x;
 		animA.startY = carLeft->y;
-		animA.dir = LEFT;
-		animA.resetX = carRight->x + carRight->w - carLeft->w;
+		animA.dir = PASSERBY_LEFT;
+		animA.resetCoord = carRight->x + carRight->w - carLeft->w;
 		animA.targetZIndex = 100;
 
 		anims->passerByAnims[0] = animA;
@@ -686,9 +697,9 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		animB.spriteIndex = 3;
 		animB.startX = carRight->x;
 		animB.startY = carRight->y;
-		animB.dir = RIGHT;
+		animB.dir = PASSERBY_RIGHT;
 		animB.targetZIndex = 100;
-		animB.resetX = 639 + carRight->w;
+		animB.resetCoord = 639 + carRight->w;
 		anims->passerByAnims[1] = animB;
 		break;
 	}
@@ -701,8 +712,8 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		anim.spriteIndex = 2;
 		anim.startX = walker->x;
 		anim.startY = walker->y;
-		anim.dir = RIGHT;
-		anim.resetX = dark->x;
+		anim.dir = PASSERBY_RIGHT;
+		anim.resetCoord = dark->x;
 		anim.targetZIndex = dark->zOrder + 1;
 		anims->passerByAnims[0] = anim;
 		break;
@@ -716,8 +727,8 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		animA.spriteIndex = 2;
 		animA.startX = catRight->x;
 		animA.startY = catRight->y;
-		animA.dir = RIGHT;
-		animA.resetX = catLeft->x;
+		animA.dir = PASSERBY_RIGHT;
+		animA.resetCoord = catLeft->x;
 		animA.targetZIndex = blank->zOrder + 1;
 
 		anims->passerByAnims[0] = animA;
@@ -725,8 +736,8 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		animB.spriteIndex = 3;
 		animB.startX = catLeft->x;
 		animB.startY = catLeft->y;
-		animB.dir = LEFT;
-		animB.resetX = blank->x;
+		animB.dir = PASSERBY_LEFT;
+		animB.resetCoord = blank->x;
 		animB.targetZIndex = blank->zOrder + 1;
 		anims->passerByAnims[1] = animB;
 		break;
@@ -741,8 +752,8 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		animA.spriteIndex = 3;
 		animA.startX = mouseRight->x;
 		animA.startY = mouseRight->y;
-		animA.dir = RIGHT;
-		animA.resetX = mouseLeft->x;
+		animA.dir = PASSERBY_RIGHT;
+		animA.resetCoord = mouseLeft->x;
 		animA.targetZIndex = papers->zOrder + 1;
 		anims->passerByAnims[0] = animA;
 
@@ -750,8 +761,8 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		animB.spriteIndex = 4;
 		animB.startX = mouseLeft->x;
 		animB.startY = mouseLeft->y;
-		animB.dir = LEFT;
-		animB.resetX = mouseRight->x;
+		animB.dir = PASSERBY_LEFT;
+		animB.resetCoord = mouseRight->x;
 		animB.targetZIndex = papers->zOrder + 1;
 		anims->passerByAnims[1] = animB;
 		break;
@@ -765,8 +776,8 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		animA.spriteIndex = 2;
 		animA.startX = mummyLeft->x;
 		animA.startY = mummyLeft->y;
-		animA.dir = LEFT;
-		animA.resetX = 0 - mummyLeft->w;
+		animA.dir = PASSERBY_LEFT;
+		animA.resetCoord = 0 - mummyLeft->w;
 		animA.targetZIndex = 1;
 
 		anims->passerByAnims[0] = animA;
@@ -774,9 +785,9 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		animB.spriteIndex = 3;
 		animB.startX = mummyRight->x;
 		animB.startY = mummyRight->y;
-		animB.dir = RIGHT;
+		animB.dir = PASSERBY_RIGHT;
 		animB.targetZIndex = 1;
-		animB.resetX = 639 + mummyRight->w;
+		animB.resetCoord = 639 + mummyRight->w;
 		anims->passerByAnims[1] = animB;
 		break;
 	}
@@ -868,9 +879,6 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 		for (int j = 0; j < spriteChanges.size(); j++) {
 			if (spriteChanges[j].spriteIndex == sprite.index) {
 				sprite.zOrder = spriteChanges[j].zIndex;
-				if (sprite.zOrder == 255) {
-					sprite.isHotspotDisabled = 1;
-				}
 				break;
 			}
 		}
@@ -981,7 +989,6 @@ uint32 RoomManager::loadDescriptions(byte *pair12data, size_t pair12size, Common
 	uint32_t lastDescPos = 0;
 	outDescriptions.clear();
 	while (pos < (pair12size)) {
-		int desc_pos = 0;
 		if (pair12data[pos] == 0xFF) {
 			Description description;
 
@@ -1199,7 +1206,7 @@ byte *RoomManager::loadShadowMap(int roomNumber) {
 	readUntilBuda(&shadowMapFile, shadowOffset, compressed, compressedSize);
 
 	byte *shadows = nullptr;
-	size_t output = rleDecompress(compressed, compressedSize, 0, 640 * 400, &shadows);
+	rleDecompress(compressed, compressedSize, 0, 640 * 400, &shadows);
 	free(compressed);
 	shadowMapFile.close();
 	return shadows;
@@ -1259,7 +1266,6 @@ byte RoomManager::loadMusicTrackForRoom(Common::File *roomFile, int roomOffset)
 	uint32_t pair9offset = roomOffset + (9 * 8);
 	roomFile->seek(pair9offset, SEEK_SET);
 	uint32_t pair9_data_offset = roomFile->readUint32LE();
-	uint32_t pair9_size = roomFile->readUint32LE();
 
 	roomFile->seek(pair9_data_offset, SEEK_SET);
 	byte musicTrack = roomFile->readByte();
@@ -1271,7 +1277,6 @@ Common::Array<byte> RoomManager::loadRoomSfx(Common::File *roomFile, int roomOff
 	uint32_t pair9offset = roomOffset + (9 * 8);
 	roomFile->seek(pair9offset, SEEK_SET);
 	uint32_t pair9_data_offset = roomFile->readUint32LE();
-	uint32_t pair9_size = roomFile->readUint32LE();
 
 	roomFile->seek(pair9_data_offset, SEEK_SET);
 	roomFile->skip(1); // skip music track byte
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index a7b861aec4c..f6022cfbca5 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -405,16 +405,16 @@ struct PaletteAnim {
 	byte tickCount = 0;
 };
 
-#define RIGHT 0
-#define LEFT 1
-
+#define PASSERBY_RIGHT 0
+#define PASSERBY_LEFT 1
+#define PASSERBY_DOWN 2
 struct PasserByAnim
 {
 	uint32 frameTrigger = 0x3FF;
 	int16 startX;
 	int16 startY;
-	int16 resetX;
-	bool dir;
+	int16 resetCoord;
+	byte dir;
 	byte spriteIndex;
 	byte targetZIndex;
 };


Commit: 8d94e680e89bde8363ba669a40c0cb5ef17879f0
    https://github.com/scummvm/scummvm/commit/8d94e680e89bde8363ba669a40c0cb5ef17879f0
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:54+02:00

Commit Message:
PELROCK: Implements palette animation on statue

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 05d829c0c4e..308758d9a8a 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -413,9 +413,9 @@ void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
 
 	// TODO: Play Alfred's throwing animation
 	// This would require adding a new special animation entry
-	// _res->loadAlfredSpecialAnim(BRICK_THROW_ANIM);
-	// _alfredState.animState = ALFRED_SPECIAL_ANIM;
-	// waitForSpecialAnimation();
+	_res->loadAlfredSpecialAnim(4);
+	_alfredState.animState = ALFRED_SPECIAL_ANIM;
+	waitForSpecialAnimation();
 
 	// TODO: Animate sprite 8 (brick projectile) moving to window
 	Sprite *brickSprite = _room->findSpriteByIndex(7);
@@ -629,13 +629,15 @@ void PelrockEngine::useAmuletWithStatue(int inventoryObject, HotSpot *hotspot) {
 		_state->setRootDisabledState(7, 0, true);
 		_state->setRootDisabledState(7, 1, false);
 		_state->setRootDisabledState(7, 2, true);
-		// TODO: Palette anim
+		_alfredState.direction = ALFRED_RIGHT;
+
 		HotSpot *statueHotspot = _room->findHotspotByExtra(91);
 		_currentHotspot = statueHotspot;
-
+		walkTo(statueHotspot->x + statueHotspot->w / 2, statueHotspot->y + statueHotspot->h);
+		animateStatuePaletteFade(false);
 		walkAndAction(statueHotspot, TALK);
-
-		// TODO: Undo palette anim!
+		waitForActionEnd();
+		animateStatuePaletteFade(true);
 	}
 }
 
@@ -708,11 +710,7 @@ void PelrockEngine::pickUpBook(int i) {
 		walkAndAction(_room->findHotspotByExtra(102), TALK);
 		// After dialog ends, reenable first dialog root if no photo in inventory
 		// Wait for dialog to end to reenable first dialog root
-		while (!shouldQuit() && _queuedAction.isQueued) {
-			_events->pollEvent();
-			renderScene(OVERLAY_NONE);
-			_screen->update();
-		}
+		waitForActionEnd();
 		if (!_state->hasInventoryItem(3)) {
 
 			_state->setRootDisabledState(9, 0, false);
@@ -827,4 +825,107 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 	}
 }
 
+void PelrockEngine::animateStatuePaletteFade(bool reverse) {
+	// Structure at JUEGO.EXE offset 0x4C700
+	struct StatuePaletteData {
+		uint16 x;           // 368
+		uint16 y;           // 148
+		uint16 type;        // 2
+		uint16 padding;     // 0
+		byte indices[16];   // Palette indices to modify
+		byte source[16][3]; // Source RGB values (6-bit VGA)
+		byte target[16][3]; // Target RGB values (6-bit VGA)
+	};
+
+	Common::File exeFile;
+	if (!exeFile.open("JUEGO.EXE")) {
+		warning("Could not open JUEGO.EXE for statue palette animation");
+		return;
+	}
+
+	// Read the palette data structure from JUEGO.EXE
+	exeFile.seek(0x4C700, SEEK_SET);
+
+	StatuePaletteData paletteData;
+	paletteData.x = exeFile.readUint16LE();
+	paletteData.y = exeFile.readUint16LE();
+	paletteData.type = exeFile.readUint16LE();
+	paletteData.padding = exeFile.readUint16LE();
+
+	exeFile.read(paletteData.indices, 16);
+
+	for (int i = 0; i < 16; i++) {
+		paletteData.source[i][0] = exeFile.readByte();
+		paletteData.source[i][1] = exeFile.readByte();
+		paletteData.source[i][2] = exeFile.readByte();
+	}
+
+	for (int i = 0; i < 16; i++) {
+		paletteData.target[i][0] = exeFile.readByte();
+		paletteData.target[i][1] = exeFile.readByte();
+		paletteData.target[i][2] = exeFile.readByte();
+	}
+
+	exeFile.close();
+
+	// Animation parameters
+	const int kNumFrames = 7; // 7 step updates total
+	const int kDelayMs = 200; // ~12 ticks at 60Hz (~200ms)
+
+	// Get current palette
+	byte currentPalette[768];
+	memcpy(currentPalette, _room->_roomPalette, 768);
+
+	// Perform the fade animation
+	int frame = 0;
+	while (!shouldQuit() && frame <= kNumFrames) {
+		_events->pollEvent();
+		_chrono->updateChrono();
+
+		if (_chrono->_gameTick) {
+			for (int i = 0; i < 16; i++) {
+				byte paletteIndex = paletteData.indices[i];
+
+				// Determine source and target based on direction
+				byte *srcColor = reverse ? paletteData.target[i] : paletteData.source[i];
+				byte *dstColor = reverse ? paletteData.source[i] : paletteData.target[i];
+
+				// Linear interpolation (6-bit VGA values)
+				byte r6 = srcColor[0] + ((dstColor[0] - srcColor[0]) * frame) / kNumFrames;
+				byte g6 = srcColor[1] + ((dstColor[1] - srcColor[1]) * frame) / kNumFrames;
+				byte b6 = srcColor[2] + ((dstColor[2] - srcColor[2]) * frame) / kNumFrames;
+
+				// Convert 6-bit VGA (0-63) to 8-bit (0-255) by shifting left 2 bits
+				currentPalette[paletteIndex * 3 + 0] = r6 << 2;
+				currentPalette[paletteIndex * 3 + 1] = g6 << 2;
+				currentPalette[paletteIndex * 3 + 2] = b6 << 2;
+			}
+
+			// Apply the palette
+			g_system->getPaletteManager()->setPalette(currentPalette, 0, 256);
+			// // Update the room's internal palette
+			// memcpy(_room->_roomPalette, currentPalette, 768);
+
+			// Redraw the scene with the new palette
+			copyBackgroundToBuffer();
+			placeStickersFirstPass();
+
+			chooseAlfredStateAndDraw();
+			placeStickersSecondPass();
+			presentFrame();
+
+			frame++;
+		}
+		_screen->update();
+		g_system->delayMillis(10);
+	}
+}
+
+void PelrockEngine::waitForActionEnd() {
+	while (!shouldQuit() && _queuedAction.isQueued) {
+		_events->pollEvent();
+		renderScene(OVERLAY_NONE);
+		_screen->update();
+	}
+}
 } // End of namespace Pelrock
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 1d80751bdc3..46ee8a57e47 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -481,11 +481,17 @@ struct AlfredSpecialAnimOffset {
 	int h = 0;
 	int numBudas;
 	int loops;
+	int numAlfred;
 	uint32 offset;
 	int stride = 0;
+	uint32 size;
 
-	AlfredSpecialAnimOffset(int nF, int width, int height, int nBudas, uint32 off, int loops)
-		: numFrames(nF), w(width), h(height), numBudas(nBudas), offset(off), loops(loops) {
+	AlfredSpecialAnimOffset(int nF, int width, int height, int nBudas, int numAlfred, uint32 off, int loops)
+		: numFrames(nF), w(width), h(height), numBudas(nBudas), numAlfred(numAlfred), offset(off), loops(loops) {
+		AlfredSpecialAnimOffset(nF, width, height, nBudas, numAlfred, off, loops, w * h * nF);
+	}
+	AlfredSpecialAnimOffset(int nF, int width, int height, int nBudas, int numAlfred, uint32 off, int loops, uint32 sz)
+		: numFrames(nF), w(width), h(height), numBudas(nBudas), numAlfred(numAlfred), offset(off), loops(loops), size(sz) {
 		stride = w * h;
 	}
 	AlfredSpecialAnimOffset() {
@@ -493,10 +499,11 @@ struct AlfredSpecialAnimOffset {
 };
 
 static const AlfredSpecialAnimOffset alfredSpecialAnims[] = {
-	{10, 51, 102, 1, 559685, 1}, // READ BOOK
-	{10, 51, 102, 1, 578943, 1}, // READ RECIPE
-	{3, 45, 87, 0, 37000, 1}, // ELECTRIC SHOCK 1
-	{2, 82, 58, 0, 53106, 20}, // ELECTRIC SHOCK 3
+	{10, 51, 102, 1, 7, 559685, 1}, // READ BOOK
+	{10, 51, 102, 1, 7, 578943, 1}, // READ RECIPE
+	{3, 45, 87, 0, 7, 37000, 1}, // ELECTRIC SHOCK 1
+	{2, 82, 58, 0, 7, 53106, 20}, // ELECTRIC SHOCK 3
+	{3, 71, 110, 1, 2, 20724, 1, 62480}, // Throw
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 275d622f846..c63444e1450 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -265,7 +265,6 @@ void PelrockEngine::frameTriggers() {
 
 void PelrockEngine::passerByAnim(uint32 frameCount) {
 	if (_room->_passerByAnims == nullptr) {
-		debug("No passerby anims for this room");
 		return;
 	}
 	if (_room->_passerByAnims->latch == false) {
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 1ec970af8d2..a8bc2d407ec 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -307,6 +307,9 @@ public:
 	void pickUpBook(int i);
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
+
+	void animateStatuePaletteFade(bool reverse = false);
+	void waitForActionEnd();
 };
 
 extern PelrockEngine *g_engine;
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 26378f0079b..1311c14fa32 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -218,39 +218,42 @@ void ResourceManager::loadAlfredAnims() {
 }
 
 void ResourceManager::loadAlfredSpecialAnim(int numAnim, bool reverse) {
-	AlfredSpecialAnimOffset offset = alfredSpecialAnims[numAnim];
-	Common::File alfred7;
-	if (!alfred7.open(Common::Path("ALFRED.7"))) {
-		error("Could not open ALFRED.7");
+	AlfredSpecialAnimOffset anim = alfredSpecialAnims[numAnim];
+
+	Common::String filename = Common::String::format("ALFRED.%d", anim.numAlfred);
+	Common::File alfredFile;
+	if (!alfredFile.open(Common::Path(filename))) {
+		error("Could not open %s", filename.c_str());
 		return;
 	}
 
-	alfred7.seek(offset.offset, SEEK_SET);
+	alfredFile.seek(anim.offset, SEEK_SET);
 	if (_currentSpecialAnim)
 		delete _currentSpecialAnim;
-	_currentSpecialAnim = new AlfredSpecialAnim(offset.numFrames, offset.w, offset.h, offset.numBudas, offset.offset, offset.loops);
-	_currentSpecialAnim->animData = new byte[offset.numFrames * offset.w * offset.h];
-	if (offset.numBudas > 0) {
-		mergeRleBlocks(&alfred7, offset.offset, offset.numBudas, _currentSpecialAnim->animData);
+	_currentSpecialAnim = new AlfredSpecialAnim(anim.numFrames, anim.w, anim.h, anim.numBudas, anim.offset, anim.loops, anim.size);
+	_currentSpecialAnim->animData = new byte[anim.size];
+	if (anim.numBudas > 0) {
+		debug("Loading special anim with budas: numBudas=%d, totalSize %d", anim.numBudas, anim.size);
+		mergeRleBlocks(&alfredFile, anim.offset, anim.numBudas, _currentSpecialAnim->animData);
 	} else {
-		alfred7.read(_currentSpecialAnim->animData, offset.numFrames * offset.w * offset.h);
+		alfredFile.read(_currentSpecialAnim->animData, anim.numFrames * anim.w * anim.h);
 	}
 	if (reverse) {
 		// reverse frames for testing
-		byte *reversedData = new byte[offset.numFrames * offset.w * offset.h];
-		for (int i = 0; i < offset.numFrames; i++) {
+		byte *reversedData = new byte[anim.numFrames * anim.w * anim.h];
+		for (int i = 0; i < anim.numFrames; i++) {
 			extractSingleFrame(_currentSpecialAnim->animData,
-							   &reversedData[i * offset.w * offset.h],
-							   offset.numFrames - 1 - i,
-							   offset.w,
-							   offset.h);
+							   &reversedData[i * anim.w * anim.h],
+							   anim.numFrames - 1 - i,
+							   anim.w,
+							   anim.h);
 		}
 		delete[] _currentSpecialAnim->animData;
 		_currentSpecialAnim->animData = reversedData;
 	}
 
 	_isSpecialAnimFinished = false;
-	alfred7.close();
+	alfredFile.close();
 }
 
 void ResourceManager::clearSpecialAnim() {
@@ -421,6 +424,7 @@ void ResourceManager::mergeRleBlocks(Common::SeekableReadStream *stream, uint32
 		readUntilBuda(stream, stream->pos(), thisBlock, blockSize);
 		uint8_t *block_data = nullptr;
 		size_t decompressedSize = rleDecompress(thisBlock, blockSize, 0, 640 * 400, &block_data, true);
+		debug("Decompressed block %d: %zu bytes", i, decompressedSize);
 		memcpy(outputBuffer + combined_size, block_data, decompressedSize);
 		combined_size += decompressedSize;
 		free(block_data);
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index f6022cfbca5..f76a904613f 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -133,8 +133,9 @@ struct AlfredSpecialAnim {
 	uint32 stride = 0;
 	int curFrame = 0;
 	int curLoop = 0;
-	AlfredSpecialAnim(int nF, int width, int height, int nBudas, uint32 off, int loopCount)
-		: numFrames(nF), w(width), h(height), loopCount(loopCount) {
+	uint32 size = 0;
+	AlfredSpecialAnim(int nF, int width, int height, int nBudas, uint32 off, int loopCount, uint32 sz)
+		: numFrames(nF), w(width), h(height), loopCount(loopCount), size(sz) {
 		stride = w * h;
 	}
 	~AlfredSpecialAnim() {


Commit: fb9ae7561f69dfcf99367b22608580000a97e371
    https://github.com/scummvm/scummvm/commit/fb9ae7561f69dfcf99367b22608580000a97e371
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:54+02:00

Commit Message:
PELROCK: Adds side effect to reading recipe

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/offsets.h
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 308758d9a8a..3f62d20b22e 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -770,6 +770,11 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		waitForSpecialAnimation();
 
 		loadExtraScreenAndPresent(3);
+		_state->setRootDisabledState(17, 0, true);
+		_state->setRootDisabledState(18, 0, true);
+		_state->setRootDisabledState(18, 1, true);
+		_state->setRootDisabledState(18, 2, true);
+		_state->setRootDisabledState(18, 3, true);
 		debug("After extra screen");
 		_dialog->say(_res->_ingameTexts[QUEASCO]);
 		break;
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 46ee8a57e47..b2e21f426d9 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -488,7 +488,7 @@ struct AlfredSpecialAnimOffset {
 
 	AlfredSpecialAnimOffset(int nF, int width, int height, int nBudas, int numAlfred, uint32 off, int loops)
 		: numFrames(nF), w(width), h(height), numBudas(nBudas), numAlfred(numAlfred), offset(off), loops(loops) {
-		AlfredSpecialAnimOffset(nF, width, height, nBudas, numAlfred, off, loops, w * h * nF);
+		AlfredSpecialAnimOffset(nF, width, height, nBudas, numAlfred, off, loops, width * height * nF);
 	}
 	AlfredSpecialAnimOffset(int nF, int width, int height, int nBudas, int numAlfred, uint32 off, int loops, uint32 sz)
 		: numFrames(nF), w(width), h(height), numBudas(nBudas), numAlfred(numAlfred), offset(off), loops(loops), size(sz) {
@@ -498,13 +498,5 @@ struct AlfredSpecialAnimOffset {
 	}
 };
 
-static const AlfredSpecialAnimOffset alfredSpecialAnims[] = {
-	{10, 51, 102, 1, 7, 559685, 1}, // READ BOOK
-	{10, 51, 102, 1, 7, 578943, 1}, // READ RECIPE
-	{3, 45, 87, 0, 7, 37000, 1}, // ELECTRIC SHOCK 1
-	{2, 82, 58, 0, 7, 53106, 20}, // ELECTRIC SHOCK 3
-	{3, 71, 110, 1, 2, 20724, 1, 62480}, // Throw
-};
-
 } // End of namespace Pelrock
 #endif
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 1311c14fa32..8d130872258 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -33,8 +33,17 @@ ResourceManager::ResourceManager(/* args */) {
 	for (int i = 0; i < 4; i++) {
 		alfredIdle[i] = nullptr;
 	}
+
 }
 
+const AlfredSpecialAnimOffset ResourceManager::alfredSpecialAnims[] = {
+	{10, 51, 102, 1, 7, 559685, 1, }, // READ BOOK
+	{10, 51, 102, 1, 7, 578943, 1}, // READ RECIPE
+	{3, 45, 87, 0, 7, 37000, 1}, // ELECTRIC SHOCK 1
+	{2, 82, 58, 0, 7, 53106, 20}, // ELECTRIC SHOCK 3
+	{3, 71, 110, 1, 2, 20724, 1, 62480}, // Throw
+};
+
 ResourceManager::~ResourceManager() {
 	for (int i = 0; i < 5; i++) {
 		delete[] _cursorMasks[i];
@@ -231,9 +240,10 @@ void ResourceManager::loadAlfredSpecialAnim(int numAnim, bool reverse) {
 	if (_currentSpecialAnim)
 		delete _currentSpecialAnim;
 	_currentSpecialAnim = new AlfredSpecialAnim(anim.numFrames, anim.w, anim.h, anim.numBudas, anim.offset, anim.loops, anim.size);
-	_currentSpecialAnim->animData = new byte[anim.size];
+	uint32 size = anim.size == 0 ? anim.numFrames * anim.w * anim.h : anim.size;
+	_currentSpecialAnim->animData = new byte[size];
 	if (anim.numBudas > 0) {
-		debug("Loading special anim with budas: numBudas=%d, totalSize %d", anim.numBudas, anim.size);
+		debug("Loading special anim with budas: numBudas=%d, totalSize %d", anim.numBudas, size);
 		mergeRleBlocks(&alfredFile, anim.offset, anim.numBudas, _currentSpecialAnim->animData);
 	} else {
 		alfredFile.read(_currentSpecialAnim->animData, anim.numFrames * anim.w * anim.h);
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index 1dbda4171f4..a8d99221f92 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -75,6 +75,8 @@ public:
 	// Special anims
 	AlfredSpecialAnim *_currentSpecialAnim = nullptr;
 	bool _isSpecialAnimFinished = false;
+	static const AlfredSpecialAnimOffset alfredSpecialAnims[];
+
 };
 
 } // End of namespace Pelrock


Commit: a0f37b533d96e0940abac68f41c835a8a02a08bc
    https://github.com/scummvm/scummvm/commit/a0f37b533d96e0940abac68f41c835a8a02a08bc
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:55+02:00

Commit Message:
PELROCK: Fixes animations in room 40

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 3f62d20b22e..9478d1f12b3 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -109,6 +109,10 @@ const ActionEntry actionTable[] = {
 	{393, OPEN, &PelrockEngine::openNewspaperBossDor},
 	{393, CLOSE, &PelrockEngine::closeNewspaperBossDoor},
 
+	// Room 19
+	{400, OPEN, &PelrockEngine::openTravelAgencyDoor},
+	{400, CLOSE, &PelrockEngine::closeTravelAgencyDoor},
+
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
 	{WILDCARD, TALK, &PelrockEngine::noOpAction},   // Generic talk action
@@ -131,7 +135,7 @@ const CombinationEntry combinationTable[] = {
 	{1, 53, &PelrockEngine::giveIdToGuard},
 	{5, 53, &PelrockEngine::giveMoneyToGuard},
 	{7, 353, &PelrockEngine::useAmuletWithStatue},
-	{8, 102, &PelrockEngine::giveFormulaToLibrarian},
+	{8, 102, &PelrockEngine::giveSecretCodeToLibrarian},
 	// End marker
 	{WILDCARD, WILDCARD, nullptr}};
 
@@ -252,6 +256,16 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 
 		_room->addWalkbox(w1);
 		_room->addWalkbox(w2);
+	case 274:
+	case 275:
+	case 276:
+		_state->setRootDisabledState(room, rootIndex, true);
+		break;
+	case 277:
+		_state->setRootDisabledState(room, rootIndex, true);
+		_state->setFlag(FLAG_JEFE_INGRESA_PASTA, true);
+
+		break;
 	default:
 		debug("Got actionTrigger %d in dialogActionTrigger, but no handler defined", actionTrigger);
 		break;
@@ -674,7 +688,7 @@ void PelrockEngine::pickBooksFromShelf3(HotSpot *hotspot) {
 	pickUpBook(3);
 }
 
-void PelrockEngine::giveFormulaToLibrarian(int inventoryObject, HotSpot *hotspot) {
+void PelrockEngine::giveSecretCodeToLibrarian(int inventoryObject, HotSpot *hotspot) {
 	_dialog->say(_res->_ingameTexts[REGALO_LIBRO_RECETAS]);
 	_state->removeInventoryItem(8);
 	addInventoryItem(59);
@@ -696,6 +710,22 @@ void PelrockEngine::closeNewspaperBossDoor(HotSpot *hotspot) {
 	closeDoor(hotspot, 1, 52, MASCULINE, true);
 }
 
+void PelrockEngine::openTravelAgencyDoor(HotSpot *hotspot) {
+	// In order to unlock the second part of the game, we need to ensure
+	// we have all we need to solve the game once there
+	if(
+		_state->hasInventoryItem(17) &&
+		_state->hasInventoryItem(59)
+	) {
+		openDoor(hotspot, 0, 55, FEMININE, false);
+	}
+	// The game originally did nothing here
+}
+
+void PelrockEngine::closeTravelAgencyDoor(HotSpot *hotspot) {
+	closeDoor(hotspot, 0, 55, FEMININE, false);
+}
+
 void PelrockEngine::pickUpBook(int i) {
 	if (!_state->hasInventoryItem(10)) {
 		_dialog->say(_res->_ingameTexts[VENGA_ACA]);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index a8bc2d407ec..45851f7c2e8 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -299,11 +299,13 @@ public:
 	void pickBooksFromShelf1(HotSpot *hotspot);
 	void pickBooksFromShelf2(HotSpot *hotspot);
 	void pickBooksFromShelf3(HotSpot *hotspot);
-	void giveFormulaToLibrarian(int inventoryObject, HotSpot *hotspot);
+	void giveSecretCodeToLibrarian(int inventoryObject, HotSpot *hotspot);
 	void openNewspaperDoor(HotSpot *hotspot);
 	void closeNewspaperDoor(HotSpot *hotspot);
 	void openNewspaperBossDor(HotSpot *hotspot);
 	void closeNewspaperBossDoor(HotSpot *hotspot);
+	void openTravelAgencyDoor(HotSpot *hotspot);
+	void closeTravelAgencyDoor(HotSpot *hotspot);
 	void pickUpBook(int i);
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 473bd24fb27..3504f3cac47 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -846,8 +846,18 @@ void RoomManager::loadAnimationPixelData(Common::File *roomFile, int roomOffset,
 	roomFile->seek(offset, SEEK_SET);
 	roomFile->read(pixelData, size);
 	if (offset > 0 && size > 0) {
-		outSize = rleDecompress(pixelData, size, 0, size, &buffer, true);
+		if(_currentRoomNumber != 40) {
+			outSize = rleDecompress(pixelData, size, 0, size, &buffer, true);
+		}
+		else {
+			// room 40 has uncompressed animation data for some reason
+			buffer = new byte[size];
+			Common::copy(pixelData, pixelData + size, buffer);
+			outSize = size;
+		}
 	}
+
+
 }
 
 Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pixelDataSize, byte *data, size_t size) {


Commit: 6480e47ea4199bbb868561e965ddbdfa46c953cf
    https://github.com/scummvm/scummvm/commit/6480e47ea4199bbb868561e965ddbdfa46c953cf
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:55+02:00

Commit Message:
PELROCK: Implements water reflection effect

Changed paths:
    engines/pelrock/graphics.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/util.cpp
    engines/pelrock/util.h


diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index 59a540cf077..15801ddd60f 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -38,7 +38,7 @@ Common::Point GraphicsManager::showOverlay(int height, byte *buf) {
 	for (int x = 0; x < 640; x++) {
 		for (int y = overlayY; y < 400; y++) {
 			int index = y * 640 + x;
-			buf[index] = g_engine->_room->paletteRemaps[0][buf[index]];
+			buf[index] = g_engine->_room->_paletteRemaps[0][buf[index]];
 		}
 	}
 	return Common::Point(overlayX, overlayY);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index c63444e1450..e2127b6cf78 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -154,7 +154,7 @@ void PelrockEngine::init() {
 		loadAnims();
 		// setScreen(0, ALFRED_DOWN);
 		// setScreen(3, ALFRED_RIGHT);
-		setScreen(7, ALFRED_DOWN);
+		setScreen(25, ALFRED_DOWN);
 		// setScreen(9, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
@@ -326,6 +326,28 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 	}
 }
 
+void PelrockEngine::reflectionEffect(byte *buf, int x, int y, int width, int height) {
+	// Water reflection - draws mirrored sprite on water pixels
+	// Only sprite pixels 0-15 are reflected (checked via pixel & 0xF0 == 0)
+	for (int row = 0; row < height; row++) {
+		int reflectY = y + row;
+
+		if (reflectY >= 400)
+			break; // Screen boundary
+
+		for (int col = 0; col < width; col++) {
+			byte pixel = buf[(height - 1 - row) * width + col]; // Read from bottom up for mirror
+			// Only reflect pixels 0-15 (high nibble must be 0)
+			if (pixel != 255 && (pixel & 0xF0) == 0) {
+				byte bgPixel = _compositeBuffer[reflectY * 640 + x + col];
+				if (bgPixel >= 223 && bgPixel < 228) { // Is water (0xDF-0xE3)
+					_compositeBuffer[reflectY * 640 + x + col] = _room->_paletteRemaps[4][pixel];
+				}
+			}
+		}
+	}
+}
+
 void PelrockEngine::executeAction(VerbIcon action, HotSpot *hotspot) {
 	debug("Executing action %d on hotspot %d", action, hotspot->extra);
 	if (action == ITEM) {
@@ -839,36 +861,12 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		break;
 	}
 	}
-	// // This if is needed to draw Alfred when idle, when the switch case results in a state change
-	// if (_alfredState.animState == ALFRED_IDLE) {
-	// 	drawAlfred(_res->alfredIdle[_alfredState.direction]);
-	// }
 }
 
-/**
- * Scales and shades alfred sprite and draws it to the composite buffer
- */
-void PelrockEngine::drawAlfred(byte *buf) {
-
-	ScaleCalculation scale = calculateScaling(_alfredState.y, _room->_scaleParams);
-
-	// Update Alfred's scale state for use by other functions
-	_alfredState.scaledX = scale.scaleX;
-	_alfredState.scaledY = scale.scaleY;
-
-	// Use the pre-calculated scaled dimensions from calculateScaling
-	int finalHeight = scale.scaledHeight;
-	int finalWidth = scale.scaledWidth;
-
-	if (finalHeight <= 0) {
-		finalHeight = 1;
-	}
-	if (finalWidth <= 0) {
-		finalWidth = 1;
-	}
+byte *PelrockEngine::scale(int scaleY, int finalWidth, int finalHeight, byte *buf) {
 
 	// The scaling table is indexed by how many scanlines to skip (scaleY), not by final height
-	int scaleIndex = scale.scaleY;
+	int scaleIndex = scaleY;
 	if (scaleIndex >= (int)_heightScalingTable.size()) {
 		scaleIndex = _heightScalingTable.size() - 1;
 	}
@@ -877,8 +875,6 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	}
 	int linesToSkip = kAlfredFrameHeight - finalHeight;
 
-	int shadowPos = _alfredState.y; // - finalHeight;
-	bool shadeCharacter = _room->_pixelsShadows[shadowPos * 640 + _alfredState.x] != 0xFF;
 
 	byte *finalBuf = new byte[finalWidth * finalHeight];
 
@@ -948,16 +944,52 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	} else {
 		Common::copy(buf, buf + (kAlfredFrameWidth * kAlfredFrameHeight), finalBuf);
 	}
+	return finalBuf;
+}
+
+/**
+ * Scales and shades alfred sprite and draws it to the composite buffer
+ */
+void PelrockEngine::drawAlfred(byte *buf) {
+
+	ScaleCalculation scaleCalc = calculateScaling(_alfredState.y, _room->_scaleParams);
+
+	// Update Alfred's scale state for use by other functions
+	_alfredState.scaledX = scaleCalc.scaleX;
+	_alfredState.scaledY = scaleCalc.scaleY;
+
+	// Use the pre-calculated scaled dimensions from calculateScaling
+	int finalHeight = scaleCalc.scaledHeight;
+	int finalWidth = scaleCalc.scaledWidth;
+
+	if (finalHeight <= 0) {
+		finalHeight = 1;
+	}
+	if (finalWidth <= 0) {
+		finalWidth = 1;
+	}
 
+	byte *finalBuf = scale(scaleCalc.scaleY, finalWidth, finalHeight, buf);
+
+	int shadowPos = _alfredState.y; // - finalHeight;
+	bool shadeCharacter = _room->_pixelsShadows[shadowPos * 640 + _alfredState.x] != 0xFF;
 	if (shadeCharacter) {
 		for (int i = 0; i < finalWidth * finalHeight; i++) {
 			if (finalBuf[i] != 255) {
-				finalBuf[i] = _room->paletteRemaps[1][finalBuf[i]];
+				finalBuf[i] = _room->_paletteRemaps[1][finalBuf[i]];
 			}
 		}
 	}
 
 	drawSpriteToBuffer(_compositeBuffer, 640, finalBuf, _alfredState.x, _alfredState.y - finalHeight, finalWidth, finalHeight, 255);
+
+	// Water reflection (rooms 25 and 45 only)
+	if (_room->_currentRoomNumber == 25 || _room->_currentRoomNumber == 45) {
+		// Offset from Alfred's feet to start of reflection
+		int yOffset = (_room->_currentRoomNumber == 45) ? 25 : 13;
+		reflectionEffect(finalBuf, _alfredState.x, _alfredState.y + yOffset, finalWidth, finalHeight);
+	}
+
 	delete[] finalBuf;
 }
 
@@ -1000,7 +1032,7 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 		return;
 	}
 
-	if(_room->_currentRoomNumber == 9 && sprite->index == 2) {
+	if (_room->_currentRoomNumber == 9 && sprite->index == 2) {
 		debug("Drawing sprite 2 in room 9, anim %d, frame %d/%d, loop %d/%d", sprite->curAnimIndex, animData.curFrame, animData.nframes, animData.curLoop, animData.loopCount);
 	}
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 45851f7c2e8..776569da328 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -102,6 +102,7 @@ private:
 	void lookAt(HotSpot *hotspot);
 
 	void chooseAlfredStateAndDraw();
+	byte *scale(int scaleY, int finalWidth, int finalHeight, byte *buf);
 	void drawAlfred(byte *buf);
 	void drawNextFrame(Sprite *animSet);
 	void animateTalkingNPC(Sprite *animSet);
@@ -231,7 +232,7 @@ public:
 	void frameTriggers();
 
 	void passerByAnim(uint32 frameCount);
-
+	void reflectionEffect(byte *buf, int x, int y, int width, int height);
 	void changeCursor(Cursor cursor);
 
 	// Actions
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 3504f3cac47..7e73944bc46 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -30,6 +30,7 @@ namespace Pelrock {
 RoomManager::RoomManager() {
 	_pixelsShadows = new byte[640 * 400];
 	_roomNames = loadRoomNames();
+	loadWaterPaletteRemap();
 }
 
 RoomManager::~RoomManager() {
@@ -40,6 +41,17 @@ RoomManager::~RoomManager() {
 	delete[] _resetData;
 }
 
+void RoomManager::loadWaterPaletteRemap() {
+	//Extra remap for water effect
+	Common::File exe;
+	if(!exe.open("JUEGO.EXE")) {
+		error("Couldnt find file JUEGO.EXE");
+	}
+	exe.seek(0x4C77C, SEEK_SET);
+	exe.read(_paletteRemaps[4], 256);
+	exe.close();
+}
+
 void RoomManager::getPalette(Common::File *roomFile, int roomOffset, byte *palette) {
 	// get palette
 	int paletteOffset = roomOffset + (11 * 8);
@@ -1232,11 +1244,12 @@ void RoomManager::loadRemaps(int roomNumber) {
 	uint32 remapOffset = 0x200 + (roomNumber * 1024);
 
 	remapFile.seek(remapOffset, SEEK_SET);
-	remapFile.read(paletteRemaps[0], 256);
-	remapFile.read(paletteRemaps[1], 256);
-	remapFile.read(paletteRemaps[2], 256);
-	remapFile.read(paletteRemaps[3], 256);
+	remapFile.read(_paletteRemaps[0], 256);
+	remapFile.read(_paletteRemaps[1], 256);
+	remapFile.read(_paletteRemaps[2], 256);
+	remapFile.read(_paletteRemaps[3], 256);
 	remapFile.close();
+
 }
 
 Common::StringArray RoomManager::loadRoomNames() {
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 0ae126f14eb..16fc12d2b86 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -66,6 +66,7 @@ public:
 	void loadRoomTalkingAnimations(int roomNumber);
 	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
 	void getBackground(Common::File *roomFile, int roomOffset, byte *background);
+	void loadWaterPaletteRemap();
 
 	/** Methods to modify room data at runtime **/
 	void addSticker(int stickerId, int persist = PERSIST_BOTH);
@@ -147,7 +148,7 @@ public:
 	ScalingParams _scaleParams;
 	byte *_pixelsShadows = nullptr;
 	byte _roomPalette[768];
-	byte paletteRemaps[4][256];
+	byte _paletteRemaps[5][256];
 	byte _musicTrack = 0;
 	Common::Array<byte> _roomSfx;
 	PaletteAnim *_currentPaletteAnim = nullptr;
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index e4c5aa39f10..29089cf6677 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -26,6 +26,7 @@
 #include "pelrock/pelrock.h"
 #include "pelrock/types.h"
 #include "pelrock/util.h"
+#include "util.h"
 
 namespace Pelrock {
 
@@ -338,4 +339,18 @@ Common::StringArray arrayOf(Common::String str) {
 	return Common::StringArray(1, str);
 }
 
+
+void invertSprite(byte *spriteBuf, int w, int h) {
+	// invert horizontal lines so character is upside down
+	for (int y = 0; y < h / 2; y++) {
+		for (int x = 0; x < w; x++) {
+			int topIndex = y * w + x;
+			int bottomIndex = (h - 1 - y) * w + x;
+			byte temp = spriteBuf[topIndex];
+			spriteBuf[topIndex] = spriteBuf[bottomIndex];
+			spriteBuf[bottomIndex] = temp;
+		}
+	}
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index 9af486dbe51..3b416b754b1 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -47,6 +47,7 @@ void drawPos(Graphics::ManagedSurface *surface, int x, int y, byte color);
 byte decodeChar(byte b);
 void changeGameSpeed(Common::Event e);
 Common::StringArray arrayOf(Common::String str);
+void invertSprite(byte *data, int w, int h);
 
 static const int special_chars[] = {
 	168, // inverted ?


Commit: ea33ffd9674e161648c2bc9e26205eae05c8724c
    https://github.com/scummvm/scummvm/commit/ea33ffd9674e161648c2bc9e26205eae05c8724c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:55+02:00

Commit Message:
PELROCK: Restrict reflection effect to y position

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index e2127b6cf78..5d4d74d70b8 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -327,6 +327,30 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 }
 
 void PelrockEngine::reflectionEffect(byte *buf, int x, int y, int width, int height) {
+
+
+	// ScaleCalculation scaleCalc = calculateScaling(y + 50, _room->_scaleParams);
+
+	// // Update Alfred's scale state for use by other functions
+	// _alfredState.scaledX = scaleCalc.scaleX;
+	// _alfredState.scaledY = scaleCalc.scaleY;
+
+	// // Use the pre-calculated scaled dimensions from calculateScaling
+	// int finalHeight = scaleCalc.scaledHeight;
+	// int finalWidth = scaleCalc.scaledWidth;
+
+	// if (finalHeight <= 0) {
+	// 	finalHeight = 1;
+	// }
+	// if (finalWidth <= 0) {
+	// 	finalWidth = 1;
+	// }
+
+	// byte *finalBuf = scale(scaleCalc.scaleY, finalWidth, finalHeight, buf);
+	// height = finalHeight;
+	// width = finalWidth;
+
+
 	// Water reflection - draws mirrored sprite on water pixels
 	// Only sprite pixels 0-15 are reflected (checked via pixel & 0xF0 == 0)
 	for (int row = 0; row < height; row++) {
@@ -336,6 +360,7 @@ void PelrockEngine::reflectionEffect(byte *buf, int x, int y, int width, int hei
 			break; // Screen boundary
 
 		for (int col = 0; col < width; col++) {
+			// byte pixel = finalBuf[(height - 1 - row) * width + col]; // Read from bottom up for mirror
 			byte pixel = buf[(height - 1 - row) * width + col]; // Read from bottom up for mirror
 			// Only reflect pixels 0-15 (high nibble must be 0)
 			if (pixel != 255 && (pixel & 0xF0) == 0) {
@@ -984,7 +1009,7 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	drawSpriteToBuffer(_compositeBuffer, 640, finalBuf, _alfredState.x, _alfredState.y - finalHeight, finalWidth, finalHeight, 255);
 
 	// Water reflection (rooms 25 and 45 only)
-	if (_room->_currentRoomNumber == 25 || _room->_currentRoomNumber == 45) {
+	if ((_room->_currentRoomNumber == 25 || _room->_currentRoomNumber == 45) && _alfredState.y >= 299) {
 		// Offset from Alfred's feet to start of reflection
 		int yOffset = (_room->_currentRoomNumber == 45) ? 25 : 13;
 		reflectionEffect(finalBuf, _alfredState.x, _alfredState.y + yOffset, finalWidth, finalHeight);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 776569da328..972769c9a55 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -102,7 +102,6 @@ private:
 	void lookAt(HotSpot *hotspot);
 
 	void chooseAlfredStateAndDraw();
-	byte *scale(int scaleY, int finalWidth, int finalHeight, byte *buf);
 	void drawAlfred(byte *buf);
 	void drawNextFrame(Sprite *animSet);
 	void animateTalkingNPC(Sprite *animSet);
@@ -121,6 +120,7 @@ private:
 
 	void calculateScalingMasks();
 	ScaleCalculation calculateScaling(int yPos, ScalingParams scalingParams);
+	byte *scale(int scaleY, int finalWidth, int finalHeight, byte *buf);
 
 	Common::Array<Common::Array<int>> _widthScalingTable;
 	Common::Array<Common::Array<int>> _heightScalingTable;


Commit: 2a510cdcec4578942d5ee0d7d0b403cded0343d2
    https://github.com/scummvm/scummvm/commit/2a510cdcec4578942d5ee0d7d0b403cded0343d2
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:56+02:00

Commit Message:
PELROCK: Disable branches in travel agency

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/console.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 9478d1f12b3..f5fd30a5f3f 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -178,6 +178,8 @@ void PelrockEngine::addInventoryItem(int item) {
 		g_system->delayMillis(10);
 	}
 	_state->addInventoryItem(item);
+
+	checkObjectsForPart2();
 }
 
 void PelrockEngine::buyFromStore(HotSpot *hotspot, int stickerId) {
@@ -264,7 +266,9 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 277:
 		_state->setRootDisabledState(room, rootIndex, true);
 		_state->setFlag(FLAG_JEFE_INGRESA_PASTA, true);
-
+		break;
+	case 278:
+		_state->setRootDisabledState(room, rootIndex, true);
 		break;
 	default:
 		debug("Got actionTrigger %d in dialogActionTrigger, but no handler defined", actionTrigger);
@@ -711,19 +715,15 @@ void PelrockEngine::closeNewspaperBossDoor(HotSpot *hotspot) {
 }
 
 void PelrockEngine::openTravelAgencyDoor(HotSpot *hotspot) {
-	// In order to unlock the second part of the game, we need to ensure
-	// we have all we need to solve the game once there
-	if(
-		_state->hasInventoryItem(17) &&
-		_state->hasInventoryItem(59)
-	) {
-		openDoor(hotspot, 0, 55, FEMININE, false);
+
+	if(_state->getFlag(FLAG_AGENCIA_ABIERTA)) {
+		openDoor(hotspot, 1, 57, FEMININE, false);
 	}
 	// The game originally did nothing here
 }
 
 void PelrockEngine::closeTravelAgencyDoor(HotSpot *hotspot) {
-	closeDoor(hotspot, 0, 55, FEMININE, false);
+	closeDoor(hotspot, 1, 57, FEMININE, false);
 }
 
 void PelrockEngine::pickUpBook(int i) {
@@ -765,6 +765,7 @@ void PelrockEngine::pickUpBook(int i) {
 			_state->selectedBookIndex = -1;
 		}
 	}
+
 }
 
 void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
@@ -915,9 +916,10 @@ void PelrockEngine::animateStatuePaletteFade(bool reverse) {
 	int frame = 0;
 	while (!shouldQuit() && frame <= kNumFrames) {
 		_events->pollEvent();
-		_chrono->updateChrono();
 
-		if (_chrono->_gameTick) {
+		bool didRender = renderScene(OVERLAY_NONE);
+		if(didRender) {
+
 			for (int i = 0; i < 16; i++) {
 				byte paletteIndex = paletteData.indices[i];
 
@@ -938,23 +940,30 @@ void PelrockEngine::animateStatuePaletteFade(bool reverse) {
 
 			// Apply the palette
 			g_system->getPaletteManager()->setPalette(currentPalette, 0, 256);
-			// // Update the room's internal palette
-			// memcpy(_room->_roomPalette, currentPalette, 768);
-
-			// Redraw the scene with the new palette
-			copyBackgroundToBuffer();
-			placeStickersFirstPass();
-
-			chooseAlfredStateAndDraw();
-			placeStickersSecondPass();
-			presentFrame();
-
 			frame++;
 		}
 		_screen->update();
 		g_system->delayMillis(10);
 	}
 }
+/**
+ * In order to unlock the second part of the game, we need to ensure
+ * we have all we need to solve the game once there
+ */
+void PelrockEngine::checkObjectsForPart2() {
+	if (_state->hasInventoryItem(17) &&
+		_state->hasInventoryItem(59) &&
+		_state->hasInventoryItem(24)
+	) {
+		_room->addStickerToRoom(19, 54, PERSIST_BOTH);
+		_room->addStickerToRoom(19, 55, PERSIST_BOTH);
+		_room->addStickerToRoom(19, 56, PERSIST_BOTH);
+		_room->addStickerToRoom(19, 58, PERSIST_BOTH);
+		_state->setFlag(FLAG_AGENCIA_ABIERTA, true);
+		// _state->setFlag(FLAG_PUEDE_VIAJAR_EN_EL_TIEMPO, true);
+		// _state->setRootDisabledState(19, 0, true);
+	}
+}
 
 void PelrockEngine::waitForActionEnd() {
 	while (!shouldQuit() && _queuedAction.isQueued) {
@@ -963,4 +972,7 @@ void PelrockEngine::waitForActionEnd() {
 		_screen->update();
 	}
 }
+
+// void checkOBjec
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/console.cpp b/engines/pelrock/console.cpp
index a7a01679c8c..0dff4c54566 100644
--- a/engines/pelrock/console.cpp
+++ b/engines/pelrock/console.cpp
@@ -54,7 +54,8 @@ bool PelrockConsole::cmdGiveItems(int argc, const char **argv) {
 	for (int i = 1; i < argc; i++) {
 		int itemId = atoi(argv[i]);
 		bool markAsSelected = g_engine->_state->inventoryItems.empty();
-		g_engine->_state->addInventoryItem(itemId);
+		g_engine->addInventoryItem(itemId);
+		// g_engine->_state->addInventoryItem(itemId);
 		if (markAsSelected)
 			g_engine->_state->selectedInventoryItem = itemId;
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 972769c9a55..2b5d1b1ff96 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -312,6 +312,7 @@ public:
 	void closeMcDoor(HotSpot *hotspot);
 
 	void animateStatuePaletteFade(bool reverse = false);
+	void checkObjectsForPart2();
 	void waitForActionEnd();
 };
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index f76a904613f..b9b3ac14b97 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -504,8 +504,9 @@ struct ResetEntry {
 #define FLAG_INGREDIENTES_CONSEGUIDOS 48
 #define FLAG_GUARDIA_PIDECOSAS 49
 #define FLAG_GUARDIA_DNI_ENTREGADO 50
+#define FLAG_AGENCIA_ABIERTA 51
 
-const int kNumGameFlags = 51;
+const int kNumGameFlags = 52;
 
 struct GameStateData {
 	byte flags[kNumGameFlags];


Commit: 7002cdd213a03fe7687ca2db9892f0f0e58b7de3
    https://github.com/scummvm/scummvm/commit/7002cdd213a03fe7687ca2db9892f0f0e58b7de3
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:56+02:00

Commit Message:
PELROCK: Map animation

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/console.cpp
    engines/pelrock/console.h
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/util.cpp
    engines/pelrock/util.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index f5fd30a5f3f..80838b59cb2 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -270,6 +270,9 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 278:
 		_state->setRootDisabledState(room, rootIndex, true);
 		break;
+	case 279:
+		travelToEgypt();
+		break;
 	default:
 		debug("Got actionTrigger %d in dialogActionTrigger, but no handler defined", actionTrigger);
 		break;
@@ -339,6 +342,8 @@ void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
 	if (_state->getFlag(FLAG_JEFE_INGRESA_PASTA)) {
 		_state->setFlag(FLAG_JEFE_INGRESA_PASTA, false);
 		addInventoryItem(75);
+		_state->setRootDisabledState(20, 0, true);
+		_state->setRootDisabledState(20, 1, true);
 	} else {
 		int billCount = 0;
 		for (uint i = 0; i < _state->inventoryItems.size(); i++) {
@@ -788,6 +793,15 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 		// loadExtraScreenAndPresent(9);
 		_state->stateGame = COMPUTER;
 		break;
+	case 145:
+		_dialog->say(_res->_ingameTexts[NOVIO2METROS]);
+		break;
+	case 281:
+		_dialog->say(_res->_ingameTexts[GRANIDEA]);
+		break;
+	case 282:
+		_dialog->say(_res->_ingameTexts[SELORECOMIENDO]);
+		break;
 	}
 }
 
diff --git a/engines/pelrock/console.cpp b/engines/pelrock/console.cpp
index 0dff4c54566..11a1f24031d 100644
--- a/engines/pelrock/console.cpp
+++ b/engines/pelrock/console.cpp
@@ -29,11 +29,38 @@ namespace Pelrock {
 PelrockConsole::PelrockConsole(PelrockEngine *engine) : GUI::Debugger(), _engine(engine) {
 	registerCmd("room", WRAP_METHOD(PelrockConsole, cmdLoadRoom));
 	registerCmd("give", WRAP_METHOD(PelrockConsole, cmdGiveItems));
+	registerCmd("disableRoot", WRAP_METHOD(PelrockConsole, disableRoot));
+	registerCmd("setFlag", WRAP_METHOD(PelrockConsole, setFlag));
 }
 
 PelrockConsole::~PelrockConsole() {
 }
 
+
+bool PelrockConsole::setFlag(int argc, const char **argv) {
+	if (argc < 3) {
+		debugPrintf("Usage: setFlag <flagIndex> <value>");
+		return true;
+	}
+	int flagIndex = atoi(argv[1]);
+	int value = atoi(argv[2]);
+	_engine->_state->setFlag(flagIndex, value);
+	debugPrintf("Set flag %d to %d\n", flagIndex, value);
+	return true;
+}
+
+bool PelrockConsole::disableRoot(int argc, const char **argv) {
+	if (argc < 3) {
+		debugPrintf("Usage: disableRoot <roomNumber> <rootIndex>");
+		return true;
+	}
+	int roomNumber = atoi(argv[1]);
+	int rootIndex = atoi(argv[2]);
+	_engine->_state->setRootDisabledState(roomNumber, rootIndex, true);
+	debugPrintf("Disabled root %d in room %d\n", rootIndex, roomNumber);
+	return true;
+}
+
 bool PelrockConsole::cmdLoadRoom(int argc, const char **argv) {
 	if (argc < 2) {
 		debugPrintf("Usage: room <roomNumber>");
diff --git a/engines/pelrock/console.h b/engines/pelrock/console.h
index 41b007da25c..f8728363680 100644
--- a/engines/pelrock/console.h
+++ b/engines/pelrock/console.h
@@ -34,6 +34,8 @@ private:
 	bool cmdLoadRoom(int argc, const char **argv);
 	bool cmdGiveItems(int argc, const char **argv);
 	bool cmdTest(int argc, const char **argv);
+	bool disableRoot(int argc, const char **argv);
+	bool setFlag(int argc, const char **argv);
 
 public:
 	PelrockConsole(PelrockEngine *engine);
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index b2e21f426d9..a187a70ebf4 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -461,7 +461,7 @@ const ExtraImages extraScreens[] = {
 	{0x9237B, // tablet
 	 0xB0EE7,
 	 8},
-	{0xB11ED, // map
+	{0x000B11F1, // map
 	 0xDE011,
 	 8},
 	{0xFFC47, // girl book
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 5d4d74d70b8..80b61729915 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -231,6 +231,48 @@ void PelrockEngine::playSoundIfNeeded() {
 	}
 }
 
+void PelrockEngine::travelToEgypt() {
+	_sound->playMusicTrack(26, false);
+	byte *palette = new byte[768];
+	if (_extraScreen == nullptr) {
+		_extraScreen = new byte[640 * 400];
+	}
+	_res->getExtraScreen(6, _extraScreen, palette);
+	CursorMan.showMouse(false);
+	g_system->getPaletteManager()->setPalette(palette, 0, 256);
+
+	memcpy(_screen->getPixels(), _extraScreen, 640 * 400);
+	int frameCount = 0;
+	while (!shouldQuit() && frameCount < 96) {
+		_events->pollEvent();
+		g_system->delayMillis(10);
+		_chrono->updateChrono();
+		if(_chrono->_gameTick && _chrono->getFrameCount() % 2 == 0) {
+			int colorIndex = 160 + frameCount;
+			int index = colorIndex * 3;
+			palette[index] = 255;     // Red
+			palette[index + 1] = 0;   // Green
+			palette[index + 2] = 0;   // Blue
+			g_system->getPaletteManager()->setPalette(palette, 0, 256);
+			frameCount++;
+		}
+		drawPaletteSquares((byte *)_screen->getPixels(), palette);
+
+		_screen->markAllDirty();
+		_screen->update();
+	}
+
+	g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
+	free(_extraScreen);
+	_extraScreen = nullptr;
+	CursorMan.showMouse(true);
+	delete[] _extraScreen;
+	delete[] palette;
+	_screen->markAllDirty();
+	_screen->update();
+
+}
+
 bool PelrockEngine::renderScene(int overlayMode) {
 
 	_chrono->updateChrono();
@@ -1399,6 +1441,9 @@ void PelrockEngine::gameLoop() {
 
 	_events->pollEvent();
 	checkMouse();
+	if(_events->_lastKeyEvent == Common::KeyCode::KEYCODE_m) {
+		travelToEgypt();
+	}
 	renderScene();
 	// _events->waitForKey();
 	_screen->update();
@@ -1678,6 +1723,7 @@ void PelrockEngine::setScreen(int roomNumber, AlfredDirection dir) {
 }
 
 void PelrockEngine::loadExtraScreenAndPresent(int screenIndex) {
+
 	byte *palette = new byte[768];
 	if (_extraScreen == nullptr) {
 		_extraScreen = new byte[640 * 400];
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 2b5d1b1ff96..1a6232cc55a 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -109,6 +109,7 @@ private:
 
 	void playSoundIfNeeded();
 
+
 	void gameLoop();
 	void computerLoop();
 	void extraScreenLoop();
@@ -235,6 +236,8 @@ public:
 	void reflectionEffect(byte *buf, int x, int y, int width, int height);
 	void changeCursor(Cursor cursor);
 
+	void travelToEgypt();
+
 	// Actions
 	void doExtraActions(int roomNumber);
 	void addInventoryItem(int item);
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 8d130872258..bc17308f07e 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -434,9 +434,14 @@ void ResourceManager::mergeRleBlocks(Common::SeekableReadStream *stream, uint32
 		readUntilBuda(stream, stream->pos(), thisBlock, blockSize);
 		uint8_t *block_data = nullptr;
 		size_t decompressedSize = rleDecompress(thisBlock, blockSize, 0, 640 * 400, &block_data, true);
-		debug("Decompressed block %d: %zu bytes", i, decompressedSize);
+		debug("Decompressed block %d: %zu bytes, total %zu", i, decompressedSize, combined_size + decompressedSize);
+		if (combined_size + decompressedSize > 640 * 400) {
+			debug("Warning: decompressed data exceeds output buffer size, truncating");
+			decompressedSize = 640 * 400 - combined_size;
+		}
 		memcpy(outputBuffer + combined_size, block_data, decompressedSize);
 		combined_size += decompressedSize;
+
 		free(block_data);
 		free(thisBlock);
 	}
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 98f57125cc9..b94f925b14f 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -213,7 +213,7 @@ void SoundManager::playMusicTrack(int trackNumber, bool loop) {
 	}
 	_currentMusicTrack = trackNumber;
 	g_system->getAudioCDManager()->stop();
-	// g_system->getAudioCDManager()->play(trackNumber, loop ? -1 : 0, 0, 0);
+	g_system->getAudioCDManager()->play(trackNumber, loop ? -1 : 1, 0, 0);
 }
 
 void SoundManager::loadSoundIndex() {
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 29089cf6677..6eb82d4f66c 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -165,7 +165,9 @@ size_t rleDecompress(
 				} else {
 					// In fixed mode, we've hit the limit - something is wrong
 					// but grow minimally to avoid crash
-					bufferCapacity += 256;
+					// bufferCapacity += 256;
+					break;
+
 				}
 				uint8_t *newBuf = (uint8_t *)realloc(*out_data, bufferCapacity);
 				if (!newBuf) {
@@ -353,4 +355,35 @@ void invertSprite(byte *spriteBuf, int w, int h) {
 	}
 }
 
+void drawPaletteSquares(byte *screenBuffer, byte *palette) {
+	// Draw 3x3 squares for all 256 palette colors
+	// Arrange them in a 16x16 grid (256 colors)
+	const int squareSize = 6;
+	const int colorsPerRow = 16;
+	const int startX = 10;  // Left margin
+	const int startY = 10;  // Top margin
+	const int spacing = 1;  // Space between squares
+
+	for (int colorIndex = 0; colorIndex < 256; colorIndex++) {
+		int row = colorIndex / colorsPerRow;
+		int col = colorIndex % colorsPerRow;
+
+		int x = startX + col * (squareSize + spacing);
+		int y = startY + row * (squareSize + spacing);
+
+		// Draw the 3x3 square with the current color
+		for (int py = 0; py < squareSize; py++) {
+			for (int px = 0; px < squareSize; px++) {
+				int destX = x + px;
+				int destY = y + py;
+
+				// Bounds check
+				if (destX >= 0 && destX < 640 && destY >= 0 && destY < 400) {
+					screenBuffer[destY * 640 + destX] = colorIndex;
+				}
+			}
+		}
+	}
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index 3b416b754b1..3425c7ef455 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -48,6 +48,7 @@ byte decodeChar(byte b);
 void changeGameSpeed(Common::Event e);
 Common::StringArray arrayOf(Common::String str);
 void invertSprite(byte *data, int w, int h);
+void drawPaletteSquares(byte *screenBuffer, byte *palette);
 
 static const int special_chars[] = {
 	168, // inverted ?


Commit: 73d9d97fb97626d6b88110e3e0533b71bf090676
    https://github.com/scummvm/scummvm/commit/73d9d97fb97626d6b88110e3e0533b71bf090676
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:56+02:00

Commit Message:
PELROCK: Fix walking algorithm so it doesnt exit the room when interacting with doors

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 80b61729915..cd84c318264 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -839,21 +839,23 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 						break;
 					}
 					_alfredState.setState(ALFRED_INTERACTING);
+					// Don't check exits when there's a queued action - action executes first
+					break;
+				}
+
+				// Only check exits after walking is complete AND no queued action
+				Exit *exit = isExitUnder(_alfredState.x, _alfredState.y);
+				if (exit != nullptr) {
+					debug("Using exit to room %d", exit->targetRoom);
+					_alfredState.x = exit->targetX;
+					_alfredState.y = exit->targetY;
+					setScreen(exit->targetRoom, exit->dir);
 				}
 			}
 		} else {
 			_currentContext.movementBuffer[_currentStep] = step;
 		}
 
-		Exit *exit = isExitUnder(_alfredState.x, _alfredState.y);
-
-		if (exit != nullptr /*&& exit->isEnabled*/) {
-			debug("Using exit to room %d", exit->targetRoom);
-			_alfredState.x = exit->targetX;
-			_alfredState.y = exit->targetY;
-			setScreen(exit->targetRoom, exit->dir);
-		}
-
 		if (_alfredState.curFrame >= walkingAnimLengths[_alfredState.direction]) {
 			_alfredState.curFrame = 0;
 		}
@@ -1336,8 +1338,9 @@ int PelrockEngine::isHotspotUnder(int x, int y) {
 Exit *PelrockEngine::isExitUnder(int x, int y) {
 	for (uint i = 0; i < _room->_currentRoomExits.size(); i++) {
 		Exit exit = _room->_currentRoomExits[i];
-		if (x >= exit.x && x <= (exit.x + exit.w) &&
-			y >= exit.y && y <= (exit.y + exit.h) && exit.isEnabled) {
+		// Original game uses: x <= exit.x + exit.w - 1 and y <= exit.y + exit.h - 1
+		if (x >= exit.x && x <= (exit.x + exit.w - 1) &&
+			y >= exit.y && y <= (exit.y + exit.h - 1) && exit.isEnabled) {
 			return &(_room->_currentRoomExits[i]);
 		}
 	}
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index b94f925b14f..55f0edf0a55 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -38,7 +38,7 @@
 namespace Pelrock {
 
 SoundManager::SoundManager(Audio::Mixer *mixer)
-	: _mixer(mixer), _currentVolume(255), _musicFile(nullptr) {
+	: _mixer(mixer), _currentVolume(128), _musicFile(nullptr) {
 	// TODO: Initialize sound manager
 	g_system->getAudioCDManager()->open();
 }
@@ -213,7 +213,7 @@ void SoundManager::playMusicTrack(int trackNumber, bool loop) {
 	}
 	_currentMusicTrack = trackNumber;
 	g_system->getAudioCDManager()->stop();
-	g_system->getAudioCDManager()->play(trackNumber, loop ? -1 : 1, 0, 0);
+	// g_system->getAudioCDManager()->play(trackNumber, loop ? -1 : 0, 0, 0);
 }
 
 void SoundManager::loadSoundIndex() {
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index 5f0f90c895e..604773622ab 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -158,8 +158,8 @@ class SoundManager {
 public:
 	SoundManager(Audio::Mixer *mixer);
 	~SoundManager();
-	void playSound(byte index, int volume = 255, int channel = -1);
-	void playSound(byte *soundData, uint32 size, int volume = 255);
+	void playSound(byte index, int volume = 128, int channel = -1);
+	void playSound(byte *soundData, uint32 size, int volume = 128);
 	void stopAllSounds();
 	void stopSound(int channel);
 	void stopMusic();


Commit: 14dfa22ef4d9945faf9ad21a2e2b21dbbbe1d81b
    https://github.com/scummvm/scummvm/commit/14dfa22ef4d9945faf9ad21a2e2b21dbbbe1d81b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:56+02:00

Commit Message:
PELROCK: implements fade to black

Changed paths:
    engines/pelrock/graphics.cpp
    engines/pelrock/graphics.h
    engines/pelrock/menu.cpp
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index 15801ddd60f..a9e9e2b1390 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -20,7 +20,10 @@
  */
 
 #include "common/scummsys.h"
+#include "engines/util.h"
+#include "graphics/paletteman.h"
 
+#include "graphics.h"
 #include "pelrock/graphics.h"
 #include "pelrock/pelrock.h"
 
@@ -69,6 +72,39 @@ void GraphicsManager::putBackgroundSlice(byte *buf, int x, int y, int w, int h,
 	}
 }
 
+void GraphicsManager::fadeToBlack() {
+	byte palette[768];
+	g_system->getPaletteManager()->grabPalette(palette, 0, 256);
+	while (!g_engine->shouldQuit()) {
+		g_engine->_events->pollEvent();
+		g_engine->_chrono->updateChrono();
+		if (g_engine->_chrono->_gameTick) {
+
+			for (int i = 0; i < 768; i++) {
+				if (palette[i] > 0) {
+					palette[i] = MAX(palette[i] / 2, 0);
+				}
+			}
+			g_system->getPaletteManager()->setPalette(palette, 0, 256);
+
+			bool allBlack = true;
+			for (int i = 0; i < 768; i++) {
+				if (palette[i] != 0) {
+					allBlack = false;
+				}
+			}
+
+			if (allBlack) {
+				break;
+			}
+
+			g_engine->_screen->markAllDirty();
+			g_engine->_screen->update();
+		}
+		g_system->delayMillis(10);
+	}
+}
+
 void GraphicsManager::clearScreen() {
 	memset(g_engine->_screen->getPixels(), 0, g_engine->_screen->pitch * g_engine->_screen->h);
 }
diff --git a/engines/pelrock/graphics.h b/engines/pelrock/graphics.h
index caa28e18f4c..a55e7749d73 100644
--- a/engines/pelrock/graphics.h
+++ b/engines/pelrock/graphics.h
@@ -34,6 +34,7 @@ public:
 	Common::Point showOverlay(int height, byte *buf);
 	byte *grabBackgroundSlice(byte *buf, int x, int y, int w, int h);
 	void putBackgroundSlice(byte *buf, int x, int y, int w, int h, byte *slice);
+	void fadeToBlack();
 	void clearScreen();
 };
 
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index a2bea00e420..9c3c1a9a1fb 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -136,6 +136,7 @@ void MenuManager::checkMouseClick(int x, int y) {
 
 void MenuManager::menuLoop() {
 
+	g_system->getPaletteManager()->setPalette(_mainMenuPalette, 0, 256);
 	g_engine->changeCursor(DEFAULT);
 	while (!g_engine->shouldQuit() && !_events->_rightMouseClicked) {
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index cd84c318264..babbf9fec74 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -117,6 +117,7 @@ Common::Error PelrockEngine::run() {
 	init();
 	while (!shouldQuit()) {
 		if (_state->stateGame == SETTINGS) {
+			_graphics->clearScreen();
 			_menu->menuLoop();
 			_state->stateGame = GAME;
 		} else if (_state->stateGame == GAME) {
@@ -232,6 +233,7 @@ void PelrockEngine::playSoundIfNeeded() {
 }
 
 void PelrockEngine::travelToEgypt() {
+	_graphics->fadeToBlack();
 	_sound->playMusicTrack(26, false);
 	byte *palette = new byte[768];
 	if (_extraScreen == nullptr) {
@@ -247,12 +249,12 @@ void PelrockEngine::travelToEgypt() {
 		_events->pollEvent();
 		g_system->delayMillis(10);
 		_chrono->updateChrono();
-		if(_chrono->_gameTick && _chrono->getFrameCount() % 2 == 0) {
+		if (_chrono->_gameTick && _chrono->getFrameCount() % 2 == 0) {
 			int colorIndex = 160 + frameCount;
 			int index = colorIndex * 3;
-			palette[index] = 255;     // Red
-			palette[index + 1] = 0;   // Green
-			palette[index + 2] = 0;   // Blue
+			palette[index] = 255;   // Red
+			palette[index + 1] = 0; // Green
+			palette[index + 2] = 0; // Blue
 			g_system->getPaletteManager()->setPalette(palette, 0, 256);
 			frameCount++;
 		}
@@ -261,6 +263,7 @@ void PelrockEngine::travelToEgypt() {
 		_screen->markAllDirty();
 		_screen->update();
 	}
+	_graphics->clearScreen();
 
 	g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
 	free(_extraScreen);
@@ -270,7 +273,6 @@ void PelrockEngine::travelToEgypt() {
 	delete[] palette;
 	_screen->markAllDirty();
 	_screen->update();
-
 }
 
 bool PelrockEngine::renderScene(int overlayMode) {
@@ -370,7 +372,6 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 
 void PelrockEngine::reflectionEffect(byte *buf, int x, int y, int width, int height) {
 
-
 	// ScaleCalculation scaleCalc = calculateScaling(y + 50, _room->_scaleParams);
 
 	// // Update Alfred's scale state for use by other functions
@@ -392,7 +393,6 @@ void PelrockEngine::reflectionEffect(byte *buf, int x, int y, int width, int hei
 	// height = finalHeight;
 	// width = finalWidth;
 
-
 	// Water reflection - draws mirrored sprite on water pixels
 	// Only sprite pixels 0-15 are reflected (checked via pixel & 0xF0 == 0)
 	for (int row = 0; row < height; row++) {
@@ -495,7 +495,6 @@ void PelrockEngine::checkMouse() {
 		checkLongMouseClick(_events->_mouseClickX, _events->_mouseClickY);
 		_events->_longClicked = false;
 	} else if (_events->_rightMouseClicked) {
-		g_system->getPaletteManager()->setPalette(_menu->_mainMenuPalette, 0, 256);
 		_events->_rightMouseClicked = false;
 		_state->stateGame = SETTINGS;
 	}
@@ -944,7 +943,6 @@ byte *PelrockEngine::scale(int scaleY, int finalWidth, int finalHeight, byte *bu
 	}
 	int linesToSkip = kAlfredFrameHeight - finalHeight;
 
-
 	byte *finalBuf = new byte[finalWidth * finalHeight];
 
 	if (linesToSkip > 0) {
@@ -1444,7 +1442,7 @@ void PelrockEngine::gameLoop() {
 
 	_events->pollEvent();
 	checkMouse();
-	if(_events->_lastKeyEvent == Common::KeyCode::KEYCODE_m) {
+	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_m) {
 		travelToEgypt();
 	}
 	renderScene();
@@ -1733,6 +1731,7 @@ void PelrockEngine::loadExtraScreenAndPresent(int screenIndex) {
 	}
 	_res->getExtraScreen(screenIndex, _extraScreen, palette);
 	CursorMan.showMouse(false);
+	_graphics->clearScreen();
 	g_system->getPaletteManager()->setPalette(palette, 0, 256);
 	extraScreenLoop();
 	CursorMan.showMouse(true);


Commit: d65032835f678f68f01013b94759050e92ef4407
    https://github.com/scummvm/scummvm/commit/d65032835f678f68f01013b94759050e92ef4407
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:57+02:00

Commit Message:
PELROCK: Room 21 (map)

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/util.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index babbf9fec74..cb1024f4e7a 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -286,11 +286,17 @@ bool PelrockEngine::renderScene(int overlayMode) {
 		copyBackgroundToBuffer();
 
 		placeStickersFirstPass();
+
 		updateAnimations();
 
+		placeStickersSecondPass();
+
 		renderOverlay(overlayMode);
 
+		mouseHoverForMap();
+
 		presentFrame();
+
 		updatePaletteAnimations();
 
 		_screen->markAllDirty();
@@ -299,6 +305,13 @@ bool PelrockEngine::renderScene(int overlayMode) {
 	return false;
 }
 
+void PelrockEngine::mouseHoverForMap() {
+	if (_room->_currentRoomNumber == 21 && !_hoveredMapLocation.empty()) {
+		Common::Rect r = _largeFont->getBoundingBox(_hoveredMapLocation.c_str());
+		drawText(_compositeBuffer, _largeFont, _hoveredMapLocation.c_str(), _events->_mouseX - r.width() / 2, _events->_mouseY - r.height(), 640, ALFRED_COLOR);
+	}
+}
+
 // const int kPasserbyTriggerFrameInterval = 0x3FF;
 
 const int kPasserbyTriggerFrameInterval = 15;
@@ -453,6 +466,7 @@ void PelrockEngine::executeAction(VerbIcon action, HotSpot *hotspot) {
 void PelrockEngine::checkMouse() {
 
 	checkMouseHover();
+
 	if (_alfredState.animState == ALFRED_WALKING && !_alfredState.isWalkingCancelable) {
 		// Ignore clicks while Alfred is walking
 		_events->_leftMouseClicked = false;
@@ -486,12 +500,11 @@ void PelrockEngine::checkMouse() {
 			_currentHotspot = nullptr;
 		}
 	} else if (_events->_leftMouseClicked) {
-
 		// Regular click (not during popup mode)
 		checkMouseClick(_events->_mouseClickX, _events->_mouseClickY);
 		_events->_leftMouseClicked = false;
 		_actionPopupState.isActive = false;
-	} else if (_events->_longClicked) {
+	} else if (_events->_longClicked && !_room->_currentRoomNumber == 21) {
 		checkLongMouseClick(_events->_mouseClickX, _events->_mouseClickY);
 		_events->_longClicked = false;
 	} else if (_events->_rightMouseClicked) {
@@ -624,6 +637,7 @@ void PelrockEngine::placeStickersFirstPass() {
 void PelrockEngine::placeStickersSecondPass() {
 	// Some stickers need to be placed AFTER sprites, hardcoded in the original
 	if (_room->_currentRoomNumber == 3) {
+		debug("Placing second-pass stickers for room 3");
 		for (uint i = 0; i < _state->stickersPerRoom[3].size(); i++) {
 			if (_state->stickersPerRoom[3][i].stickerIndex == 14) {
 				placeSticker(_state->stickersPerRoom[3][i]);
@@ -1634,10 +1648,24 @@ void PelrockEngine::changeCursor(Cursor cursor) {
 void PelrockEngine::checkMouseHover() {
 
 	bool hotspotDetected = false;
-
 	int hotspotIndex = isHotspotUnder(_events->_mouseX, _events->_mouseY);
+
+	bool alfredDetected = false;
+	if (isAlfredUnder(_events->_mouseX, _events->_mouseY)) {
+		alfredDetected = true;
+		_hoveredMapLocation = "Alfred";
+	}
+
 	if (hotspotIndex != -1) {
 		hotspotDetected = true;
+		_hoveredMapLocation = _room->_currentRoomDescriptions[hotspotIndex].text;
+	}
+	else if (!alfredDetected) {
+		_hoveredMapLocation = "";
+	}
+
+	if(_room->_currentRoomNumber == 21) {
+		return;
 	}
 
 	if (isActionUnder(_events->_mouseX, _events->_mouseY) != NO_ACTION) {
@@ -1647,10 +1675,7 @@ void PelrockEngine::checkMouseHover() {
 	// Calculate walk target first (before checking anything else)
 	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, _events->_mouseX, _events->_mouseY, hotspotDetected, hotspotDetected ? &_room->_currentRoomHotspots[hotspotIndex] : nullptr);
 
-	bool alfredDetected = false;
-	if (isAlfredUnder(_events->_mouseX, _events->_mouseY)) {
-		alfredDetected = true;
-	}
+
 	// Check if walk target hits any exit
 	bool exitDetected = false;
 	Exit *exit = isExitUnder(walkTarget.x, walkTarget.y);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 1a6232cc55a..359a1013034 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -147,6 +147,8 @@ private:
 	bool gameInitialized = false;
 	bool screenReady = false;
 
+	Common::String _hoveredMapLocation = "";
+
 	// int prevDirX = 0;
 	// int prevDirY = 0;
 	// Common::String objectToShow = "";
@@ -230,6 +232,7 @@ public:
 	void loadExtraScreenAndPresent(int screenIndex);
 	void waitForSpecialAnimation();
 	bool renderScene(int overlayMode = OVERLAY_NONE);
+void mouseHoverForMap();
 	void frameTriggers();
 
 	void passerByAnim(uint32 frameCount);
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 6eb82d4f66c..31bab7e10e0 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -68,7 +68,7 @@ void drawText(byte *screenBuffer, Graphics::Font *font, Common::String text, int
 	int bboxH = rect.height();
 
 	surface.create(bboxW, bboxH, Graphics::PixelFormat::createFormatCLUT8());
-
+	surface.fillRect(Common::Rect(0, 0, bboxW, bboxH), 255);
 	if (x + bboxW > 640) {
 		x = 640 - bboxW - 2;
 	}
@@ -89,7 +89,7 @@ void drawText(byte *screenBuffer, Graphics::Font *font, Common::String text, int
 		for (int px = 0; px < bboxW; px++) {
 			int destIdx = (y + py) * 640 + (x + px);
 			int pixelColor = *((byte *)surface.getBasePtr(px, py));
-			if (pixelColor != 0)
+			if(pixelColor != 255)
 				screenBuffer[destIdx] = pixelColor;
 		}
 	}


Commit: 562e22178055dd03c30722103e0f59fc959ca8d4
    https://github.com/scummvm/scummvm/commit/562e22178055dd03c30722103e0f59fc959ca8d4
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:57+02:00

Commit Message:
PELROCK: show entire inventory when picking object

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/console.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 80838b59cb2..eeff6471a5f 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -165,7 +165,7 @@ void PelrockEngine::addInventoryItem(int item) {
 	if (_state->inventoryItems.empty()) {
 		_state->selectedInventoryItem = item;
 	}
-	_flashingIcon = item;
+	_newItem = item;
 	int frameCounter = 0;
 	while (frameCounter < kIconFlashDuration) {
 		_events->pollEvent();
diff --git a/engines/pelrock/console.cpp b/engines/pelrock/console.cpp
index 11a1f24031d..7ffbb0c6154 100644
--- a/engines/pelrock/console.cpp
+++ b/engines/pelrock/console.cpp
@@ -81,8 +81,7 @@ bool PelrockConsole::cmdGiveItems(int argc, const char **argv) {
 	for (int i = 1; i < argc; i++) {
 		int itemId = atoi(argv[i]);
 		bool markAsSelected = g_engine->_state->inventoryItems.empty();
-		g_engine->addInventoryItem(itemId);
-		// g_engine->_state->addInventoryItem(itemId);
+		g_engine->_state->addInventoryItem(itemId);
 		if (markAsSelected)
 			g_engine->_state->selectedInventoryItem = itemId;
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index cb1024f4e7a..952dd2c6544 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -155,7 +155,7 @@ void PelrockEngine::init() {
 		loadAnims();
 		// setScreen(0, ALFRED_DOWN);
 		// setScreen(3, ALFRED_RIGHT);
-		setScreen(25, ALFRED_DOWN);
+		setScreen(23, ALFRED_DOWN);
 		// setScreen(9, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
@@ -199,6 +199,8 @@ Common::Array<VerbIcon> PelrockEngine::availableActions(HotSpot *hotspot) {
 	return verbs;
 }
 
+Common::Point getPositionInBallonForIndex(int i);
+
 // Sort sprites by zOrder in-place using insertion sort (efficient for nearly-sorted data)
 void sortAnimsByZOrder(Common::Array<Sprite> &anims) {
 	for (size_t i = 0; i < anims.size(); ++i) {
@@ -478,7 +480,6 @@ void PelrockEngine::checkMouse() {
 		_alfredState.curFrame = 0;
 		_alfredState.setState(ALFRED_IDLE);
 	}
-
 	// Handle mouse release after long press (popup selection mode)
 	if (_events->_popupSelectionMode && !_events->_leftMouseButton) {
 		_events->_leftMouseButton = false;
@@ -504,7 +505,7 @@ void PelrockEngine::checkMouse() {
 		checkMouseClick(_events->_mouseClickX, _events->_mouseClickY);
 		_events->_leftMouseClicked = false;
 		_actionPopupState.isActive = false;
-	} else if (_events->_longClicked && !_room->_currentRoomNumber == 21) {
+	} else if (_events->_longClicked && _room->_currentRoomNumber != 21) {
 		checkLongMouseClick(_events->_mouseClickX, _events->_mouseClickY);
 		_events->_longClicked = false;
 	} else if (_events->_rightMouseClicked) {
@@ -1193,24 +1194,6 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 
 void PelrockEngine::calculateScalingMasks() {
 
-	//    for scale_factor in range(CHAR_WIDTH):
-	//     step = CHAR_WIDTH / (scale_factor + 1.0)
-	//     row = []
-	//     index = 0.0
-	//     source_pixel = 0
-
-	//     while index < CHAR_WIDTH:
-	//         row.append(source_pixel)
-	//         index += step
-	//         source_pixel += 1
-	//         if source_pixel >= CHAR_WIDTH:
-	//             source_pixel = CHAR_WIDTH - 1
-
-	//     # Pad to exactly CHAR_WIDTH entries
-	//     while len(row) < CHAR_WIDTH:
-	//         row.append(row[-1] if row else 0)
-	//     width_table.append(row[:CHAR_WIDTH])
-
 	for (int scaleFactor = 0; scaleFactor < kAlfredFrameWidth; scaleFactor++) {
 		float step = kAlfredFrameWidth / (scaleFactor + 1.0f);
 		Common::Array<int> row;
@@ -1234,22 +1217,6 @@ void PelrockEngine::calculateScalingMasks() {
 		_widthScalingTable.push_back(row);
 	}
 
-	//  height_table = []
-	// for scale_factor in range(CHAR_HEIGHT):
-	//     step = CHAR_HEIGHT / (scale_factor + 1.0)
-	//     row = [0] * CHAR_HEIGHT  # Initialize all to 0
-
-	//     # Mark positions where we should keep/duplicate the scanline
-	//     position = step
-	//     counter = 1
-	//     while position < CHAR_HEIGHT:
-	//         idx = round(position)
-	//         if idx < CHAR_HEIGHT:
-	//             row[idx] = counter
-	//             counter += 1
-	//         position += step
-
-	//     height_table.append(row)
 	for (int scaleFactor = 0; scaleFactor < kAlfredFrameHeight; scaleFactor++) {
 		float step = kAlfredFrameHeight / (scaleFactor + 1.0f);
 		Common::Array<int> row;
@@ -1380,6 +1347,10 @@ bool PelrockEngine::isSpriteUnder(Sprite *sprite, int x, int y) {
 	return false;
 }
 
+Common::Point getPositionInBallonForIndex(int i, int x, int y) {
+	return Common::Point(x + 20 + (i * (kVerbIconWidth + 2)), y + 20);
+}
+
 void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	drawSpriteToBuffer(_compositeBuffer, 640, _res->_popUpBalloon + (curFrame * kBalloonHeight * kBalloonWidth), posx, posy, kBalloonWidth, kBalloonHeight, 255);
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
@@ -1390,11 +1361,11 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 		if (icon == actions[i] && shouldBlink) {
 			continue;
 		}
-		drawSpriteToBuffer(_compositeBuffer, 640, _res->_verbIcons[actions[i]], posx + 20 + (i * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
+		Common::Point p = getPositionInBallonForIndex(i, posx, posy);
+		drawSpriteToBuffer(_compositeBuffer, 640, _res->_verbIcons[actions[i]], p.x, p.y, kVerbIconWidth, kVerbIconHeight, 1);
 	}
-	bool itemUnder = isItemUnder(_events->_mouseX, _events->_mouseY);
 	if (_state->selectedInventoryItem >= 0 && !_state->inventoryItems.empty()) {
-		if (itemUnder && shouldBlink) {
+		if (icon == ITEM && shouldBlink) {
 			return;
 		}
 		drawSpriteToBuffer(_compositeBuffer, 640, _res->getIconForObject(_state->selectedInventoryItem).iconData, posx + 20 + (actions.size() * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
@@ -1442,13 +1413,26 @@ void PelrockEngine::animateTalkingNPC(Sprite *animSet) {
 	drawSpriteToBuffer(_compositeBuffer, 640, frame, x, y, w, h, 255);
 }
 
+Common::Point getPositionInOverlayForIndex(uint index) {
+	return Common::Point(5 + index * (60 + 2), 400 - 60 - 5);
+}
+
+
 void PelrockEngine::pickupIconFlash() {
 	_graphics->showOverlay(65, _compositeBuffer);
-	if (_flashingIcon == -1)
+	if (_newItem == -1)
 		return;
-	InventoryObject item = _res->getIconForObject(_flashingIcon);
+	uint invSize = _state->inventoryItems.size();
+	for( int i = 0; i < invSize; i++) {
+		Common::Point p = getPositionInOverlayForIndex(i);
+		drawSpriteToBuffer(_compositeBuffer, 640, _res->getIconForObject(_state->inventoryItems[i]).iconData, p.x, p.y, 60, 60, 1);
+	}
+
+	InventoryObject item = _res->getIconForObject(_newItem);
 	if (_chrono->getFrameCount() % kIconBlinkPeriod == 0) {
-		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 5, 400 - 60 - 5, 60, 60, 1);
+		Common::Point p = getPositionInOverlayForIndex(invSize);
+		debug("Drawing pickup icon for item %d at inventory index %d, position %d,%d", _newItem, invSize, p.x, p.y);
+		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, p.x, p.y, 60, 60, 1);
 	}
 }
 
@@ -1570,9 +1554,8 @@ VerbIcon PelrockEngine::isActionUnder(int x, int y) {
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
 	int loopEnd = _state->selectedInventoryItem != -1 ? actions.size() + 1 : actions.size();
 	for (int i = 0; i < loopEnd; i++) {
-		int actionX = _actionPopupState.x + 20 + (i * (kVerbIconWidth + 2));
-		int actionY = _actionPopupState.y + 20;
-		Common::Rect actionRect = Common::Rect(actionX, actionY, actionX + kVerbIconWidth, actionY + kVerbIconHeight);
+		Common::Point p = getPositionInBallonForIndex(i, _actionPopupState.x, _actionPopupState.y);
+		Common::Rect actionRect = Common::Rect(p.x, p.y, p.x + kVerbIconWidth, p.y + kVerbIconHeight);
 
 		if (i == actions.size()) {
 			// Check inventory item
@@ -1586,17 +1569,6 @@ VerbIcon PelrockEngine::isActionUnder(int x, int y) {
 	return NO_ACTION;
 }
 
-bool PelrockEngine::isItemUnder(int x, int y) {
-	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
-	Common::Rect itemRect = Common::Rect(_actionPopupState.x + 20 + (actions.size() * (kVerbIconWidth + 2)), _actionPopupState.y + 20,
-										 _actionPopupState.x + 20 + (actions.size() * (kVerbIconWidth + 2)) + kVerbIconWidth,
-										 _actionPopupState.y + 20 + kVerbIconHeight);
-	if (itemRect.contains(x, y)) {
-		return true;
-	}
-	return false;
-}
-
 bool PelrockEngine::isAlfredUnder(int x, int y) {
 	int alfredX = _alfredState.x;
 	int alfredY = _alfredState.y;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 359a1013034..061668f4eba 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -76,7 +76,6 @@ private:
 
 	Common::Array<VerbIcon> availableActions(HotSpot *hotspot);
 	VerbIcon isActionUnder(int x, int y);
-	bool isItemUnder(int x, int y);
 	bool isAlfredUnder(int x, int y);
 	int isHotspotUnder(int x, int y);
 	Exit *isExitUnder(int x, int y);
@@ -136,7 +135,7 @@ private:
 	ActionPopupState _actionPopupState;
 
 	HotSpot *_currentHotspot = nullptr;
-	int _flashingIcon = -1;
+	int _newItem = -1;
 
 	Common::Point _curWalkTarget;
 	QueuedAction _queuedAction;
@@ -232,7 +231,7 @@ public:
 	void loadExtraScreenAndPresent(int screenIndex);
 	void waitForSpecialAnimation();
 	bool renderScene(int overlayMode = OVERLAY_NONE);
-void mouseHoverForMap();
+	void mouseHoverForMap();
 	void frameTriggers();
 
 	void passerByAnim(uint32 frameCount);


Commit: 4d4ff456f058204f620f69026651aa7ecd7258a2
    https://github.com/scummvm/scummvm/commit/4d4ff456f058204f620f69026651aa7ecd7258a2
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:57+02:00

Commit Message:
PERLOCK: adjust mouse hover over Alfred

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 952dd2c6544..7245d7a133a 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1036,14 +1036,15 @@ void PelrockEngine::drawAlfred(byte *buf) {
 
 	ScaleCalculation scaleCalc = calculateScaling(_alfredState.y, _room->_scaleParams);
 
-	// Update Alfred's scale state for use by other functions
-	_alfredState.scaledX = scaleCalc.scaleX;
-	_alfredState.scaledY = scaleCalc.scaleY;
-
 	// Use the pre-calculated scaled dimensions from calculateScaling
 	int finalHeight = scaleCalc.scaledHeight;
 	int finalWidth = scaleCalc.scaledWidth;
 
+
+	// Update Alfred's scale state for use by other functions
+	_alfredState.w = finalWidth;
+	_alfredState.h = finalHeight;
+
 	if (finalHeight <= 0) {
 		finalHeight = 1;
 	}
@@ -1051,28 +1052,28 @@ void PelrockEngine::drawAlfred(byte *buf) {
 		finalWidth = 1;
 	}
 
-	byte *finalBuf = scale(scaleCalc.scaleY, finalWidth, finalHeight, buf);
+	byte *scaledBuf = scale(scaleCalc.scaleY, finalWidth, finalHeight, buf);
+	delete[] _alfredSprite;
+	_alfredSprite = scaledBuf;
 
 	int shadowPos = _alfredState.y; // - finalHeight;
 	bool shadeCharacter = _room->_pixelsShadows[shadowPos * 640 + _alfredState.x] != 0xFF;
 	if (shadeCharacter) {
 		for (int i = 0; i < finalWidth * finalHeight; i++) {
-			if (finalBuf[i] != 255) {
-				finalBuf[i] = _room->_paletteRemaps[1][finalBuf[i]];
+			if (_alfredSprite[i] != 255) {
+				_alfredSprite[i] = _room->_paletteRemaps[1][_alfredSprite[i]];
 			}
 		}
 	}
 
-	drawSpriteToBuffer(_compositeBuffer, 640, finalBuf, _alfredState.x, _alfredState.y - finalHeight, finalWidth, finalHeight, 255);
+	drawSpriteToBuffer(_compositeBuffer, 640, _alfredSprite, _alfredState.x, _alfredState.y - finalHeight, finalWidth, finalHeight, 255);
 
 	// Water reflection (rooms 25 and 45 only)
 	if ((_room->_currentRoomNumber == 25 || _room->_currentRoomNumber == 45) && _alfredState.y >= 299) {
 		// Offset from Alfred's feet to start of reflection
 		int yOffset = (_room->_currentRoomNumber == 45) ? 25 : 13;
-		reflectionEffect(finalBuf, _alfredState.x, _alfredState.y + yOffset, finalWidth, finalHeight);
+		reflectionEffect(_alfredSprite, _alfredState.x, _alfredState.y + yOffset, finalWidth, finalHeight);
 	}
-
-	delete[] finalBuf;
 }
 
 void applyMovement(int16_t *x, int16_t *y, int8_t *z, uint16_t flags) {
@@ -1512,13 +1513,13 @@ AlfredDirection PelrockEngine::calculateAlfredsDirection(HotSpot *hotspot) {
 			calculatedDirection = ALFRED_LEFT; // Face LEFT
 		}
 		// Check if Alfred's right edge is before sprite's left edge
-		else if ((_alfredState.x + kAlfredFrameWidth - _alfredState.scaledX) < hotspot->x) {
+		else if ((_alfredState.x + _alfredState.w) < hotspot->x) {
 			calculatedDirection = ALFRED_RIGHT; // Face RIGHT
 		}
 		// Alfred is horizontally overlapping with sprite
 		else {
 			// Check if Alfred's top is above sprite's bottom OR Alfred is within sprite's Y range
-			if (((_alfredState.y + kAlfredFrameHeight - _alfredState.scaledY) < hotspot->y) ||
+			if (((_alfredState.y + _alfredState.h) < hotspot->y) ||
 				(_alfredState.y <= hotspot->y + hotspot->h &&
 				 hotspot->zOrder <= ((399 - _alfredState.y) / 2) + 10)) {
 				calculatedDirection = ALFRED_DOWN; // Face DOWN
@@ -1532,11 +1533,11 @@ AlfredDirection PelrockEngine::calculateAlfredsDirection(HotSpot *hotspot) {
 			calculatedDirection = ALFRED_LEFT; // Face LEFT
 		}
 		// Check if Alfred's right edge is before hotspot's left edge
-		else if ((_alfredState.x + kAlfredFrameWidth - _alfredState.scaledX) < hotspot->x) {
+		else if ((_alfredState.x + _alfredState.w) < hotspot->x) {
 			calculatedDirection = ALFRED_RIGHT; // Face RIGHT
 		}
 		// Check vertical positioning
-		else if (((_alfredState.y + kAlfredFrameHeight - _alfredState.scaledY) < hotspot->y) ||
+		else if (((_alfredState.y + _alfredState.h) < hotspot->y) ||
 				 (_alfredState.y <= hotspot->y + hotspot->h &&
 				  (hotspot->actionFlags & 0x80) == 0x80)) {
 			calculatedDirection = ALFRED_DOWN; // Face DOWN
@@ -1570,16 +1571,15 @@ VerbIcon PelrockEngine::isActionUnder(int x, int y) {
 }
 
 bool PelrockEngine::isAlfredUnder(int x, int y) {
-	int alfredX = _alfredState.x;
-	int alfredY = _alfredState.y;
-	// Use scaled dimensions (width - scaleX, height - scaleY)
-	int alfredW = kAlfredFrameWidth - _alfredState.scaledX;
-	int alfredH = kAlfredFrameHeight - _alfredState.scaledY;
-
-	if (alfredY - alfredH > y || alfredY < y || alfredX > x || alfredX + alfredW < x) {
-		return false;
+	int localX = x - _alfredState.x;
+	int localY = y - _alfredState.y  + _alfredState.h; // Adjust for scaling (since Alfred's position is based on his feet, but sprite may be scaled from the top)
+	if (localX >= 0 && localX < _alfredState.w && localY >= 0 && localY < _alfredState.h) {
+		byte pixel = _alfredSprite[localY * _alfredState.w + localX];
+		if (pixel != 255) {
+			return true;
+		}
 	}
-	return true;
+	return false;
 }
 
 void PelrockEngine::checkMouseClick(int x, int y) {
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 061668f4eba..b470c431f24 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -147,6 +147,7 @@ private:
 	bool screenReady = false;
 
 	Common::String _hoveredMapLocation = "";
+	byte *_alfredSprite = nullptr;
 
 	// int prevDirX = 0;
 	// int prevDirY = 0;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index b9b3ac14b97..e6bff318382 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -163,8 +163,8 @@ struct AlfredState {
 	uint16 movementSpeedY = 5; // pixels per frame
 	uint16 x = 319;
 	uint16 y = 302;
-	uint16 scaledX = 0;
-	uint16 scaledY = 0;
+	byte w = kAlfredFrameWidth;
+	byte h = kAlfredFrameHeight;
 	int idleFrameCounter = 0;
 	bool isWalkingCancelable = true;
 


Commit: 398957a124dd40b09b1f00c542602bb9990d44c5
    https://github.com/scummvm/scummvm/commit/398957a124dd40b09b1f00c542602bb9990d44c5
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:58+02:00

Commit Message:
PELROCK: Improve action selection

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index eeff6471a5f..ccc0569c119 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -793,7 +793,7 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 		// loadExtraScreenAndPresent(9);
 		_state->stateGame = COMPUTER;
 		break;
-	case 145:
+	case 280:
 		_dialog->say(_res->_ingameTexts[NOVIO2METROS]);
 		break;
 	case 281:
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 7245d7a133a..50a72e96a7a 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -488,14 +488,16 @@ void PelrockEngine::checkMouse() {
 		_actionPopupState.isActive = false;
 		// Mouse was released while popup is active
 		VerbIcon actionClicked = isActionUnder(_events->_releaseX, _events->_releaseY);
-		debug("Popup action clicked: %d, is alfredunder %d", actionClicked, _actionPopupState.isAlfredUnder);
-		if (_actionPopupState.isAlfredUnder) {
-			debug("Using item on Alfred");
-			useOnAlfred(_state->selectedInventoryItem);
-		} else if (actionClicked != NO_ACTION && _currentHotspot != nullptr) {
+		if(actionClicked != NO_ACTION) {
+			debug("Popup action clicked: %d, is alfredunder %d", actionClicked, _actionPopupState.isAlfredUnder);
+		}
+		if (actionClicked != NO_ACTION && _currentHotspot != nullptr) {
 			// Action was selected - queue it
 			walkAndAction(_currentHotspot, actionClicked);
-		} else {
+		} else if (_actionPopupState.isAlfredUnder && actionClicked != NO_ACTION) {
+			debug("Using item on Alfred");
+			useOnAlfred(_state->selectedInventoryItem);
+		} else  {
 			// Released outside popup - just close it
 			_queuedAction = QueuedAction{NO_ACTION, -1, false};
 			_currentHotspot = nullptr;
@@ -1630,6 +1632,7 @@ void PelrockEngine::checkMouseHover() {
 
 	if (hotspotIndex != -1) {
 		hotspotDetected = true;
+		debug("Hotspot detected: %s", _room->_currentRoomDescriptions[hotspotIndex].text.c_str());
 		_hoveredMapLocation = _room->_currentRoomDescriptions[hotspotIndex].text;
 	}
 	else if (!alfredDetected) {


Commit: 433b1eb364dc40ebfe52900b14bbe6c0d4384591
    https://github.com/scummvm/scummvm/commit/433b1eb364dc40ebfe52900b14bbe6c0d4384591
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:58+02:00

Commit Message:
PELROCK: Dialog refactor into functions

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index ccc0569c119..d4e5d51e72d 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -273,6 +273,52 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 279:
 		travelToEgypt();
 		break;
+			// moros
+	case 330:
+		break;
+	case 331:
+		break;
+	case 332:
+		break;
+	case 333:
+		break;
+	case 334:
+		break;
+	case 335:
+		break;
+	case 336:
+		break;
+	case 337:
+		break;
+	case 338:
+		break;
+	case 339:
+		break;
+	case 340:
+		break;
+	case 341:
+		break;
+	case 342:
+		break;
+	case 343:
+		break;
+	case 344:
+		break;
+	case 345:
+		break;
+	case 346:
+		break;
+	case 347:
+		break;
+	case 348:
+		break;
+	case 349:
+		break;
+	case 350:
+		break;
+	case 351:
+		break;
+		// end moros
 	default:
 		debug("Got actionTrigger %d in dialogActionTrigger, but no handler defined", actionTrigger);
 		break;
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 7dc5629975f..828c500170b 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "pelrock/dialog.h"
+#include "dialog.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/util.h"
@@ -246,7 +247,6 @@ void DialogManager::displayDialogue(Common::String text, byte speakerId) {
  * Returns the index of the selected choice in the choices array
  */
 int DialogManager::selectChoice(Common::Array<Common::String> &choices, byte *compositeBuffer) {
-	// Clear any existing click state
 	_events->_leftMouseClicked = false;
 
 	int overlayHeight = choices.size() * kChoiceHeight + 2;
@@ -255,13 +255,11 @@ int DialogManager::selectChoice(Common::Array<Common::String> &choices, byte *co
 	while (!g_engine->shouldQuit()) {
 		_events->pollEvent();
 
-		// Render the scene with choices overlay
 		g_engine->renderScene(OVERLAY_CHOICES);
 
 		if (_events->_leftMouseClicked) {
 			_events->_leftMouseClicked = false;
 
-			// Check if click is in the choices area
 			if (_events->_mouseClickY >= overlayY) {
 				int selectedIndex = (_events->_mouseClickY - overlayY - 2) / kChoiceHeight;
 				if (selectedIndex >= 0 && selectedIndex < (int)choices.size()) {
@@ -460,145 +458,48 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		return;
 	}
 	setCurSprite(animSet ? animSet->index : -1);
-	// _curSprite = animSet;
 
 	debug("Starting conversation with %d bytes of data, for npc %d, hotspot %d", dataSize, npcIndex, animSet ? animSet->index : -1);
 
-	uint32 position = 0;
-	int currentChoiceLevel = -1;       // Track the current choice level
-	uint32 lastChoiceMenuPosition = 0; // Track where we last showed a choice menu
-	ChoiceOption lastSelectedChoice;   // Track the last choice we selected
-	bool skipToChoices = false;        // After F0, skip directly to choice parsing
-
-	// Find the speaker tree for this NPC; they are marked by 0xFE 0xXX where XX is NPC index + 1
-	bool speakerTreeOffsetFound = false;
-	int currentConversationTree = npcIndex + 1;
-	while (position < dataSize && !speakerTreeOffsetFound) {
-		if (conversationData[position] == CTRL_ALT_SPEAKER_ROOT && conversationData[position + 1] == currentConversationTree) {
-			speakerTreeOffsetFound = true;
-			position += 2; // Move past the speaker tree marker and npc index
-		} else {
-			position++;
-		}
-	}
-	// Right after the speaker conversation tree, we are in branch 0
-	int currentRoot = 0;
-	while (g_engine->_state->getRootDisabledState(g_engine->_room->_currentRoomNumber, currentRoot)) {
-		// This root is disabled, skip to next
-		while (position < dataSize) {
-			if (conversationData[position] == CTRL_END_BRANCH) {
-				position++; // Move past end branch marker
-				currentRoot++;
-				break;
-			}
-			position++;
-		}
-	}
-
-	// Skip any junk at start until we find a speaker marker or choice marker
-	while (position < dataSize &&
-		   conversationData[position] != CTRL_SPEAKER_ID &&
-		   conversationData[position] != CTRL_DIALOGUE_MARKER &&
-		   conversationData[position] != CTRL_DIALOGUE_MARKER_ONEOFF) {
-		position++;
-	}
-
-	// OUTER LOOP: Continue until conversation ends
-	while (position < dataSize && !g_engine->shouldQuit()) {
-		// Skip control bytes that should be ignored (but handle F0 specially)
-		while (position < dataSize &&
-			   (conversationData[position] == CTRL_ALT_END_MARKER_1 ||
-				conversationData[position] == CTRL_ALT_END_MARKER_2)) {
-			position++;
-		}
+	// Initialize conversation state
+	ConversationState state = initializeConversation(conversationData, dataSize, npcIndex);
+	bool skipToChoices = false;
 
-		// Handle F0 "Go Back" - return to the last choice menu position
-		// Disable the choice that led us here before rewinding
-		if (position < dataSize && conversationData[position] == CTRL_GO_BACK) {
-			debug("F0 Go Back at position %u, rewinding to lastChoiceMenuPosition %u", position, lastChoiceMenuPosition);
+	// Main conversation loop
+	while (state.position < dataSize && !g_engine->shouldQuit()) {
+		state.position = skipControlBytes(conversationData, dataSize, state.position);
 
-			if (lastChoiceMenuPosition > 0) {
-				// Disable the choice that led to this F0
-				if (lastSelectedChoice.dataOffset > 0) {
-					debug("F0: Disabling choice that led here at offset %u", lastSelectedChoice.dataOffset);
-					g_engine->_room->addDisabledChoice(lastSelectedChoice);
-				}
-				position = lastChoiceMenuPosition;
-				skipToChoices = true; // Skip directly to choice parsing
-									  // Jump directly to choice parsing section
-									  // Don't continue - let it fall through
+		if (state.position < dataSize && conversationData[state.position] == CTRL_GO_BACK) {
+			if (handleGoBack(conversationData, state.position, state)) {
+				skipToChoices = true;
 			} else {
-				debug("F0: No previous choice menu, ending conversation");
-				break;
+				break; // End conversation if no previous menu
 			}
 		}
 
-		if (position >= dataSize) {
+		if (state.position >= dataSize) {
 			debug("Reached end of data while skipping control bytes");
 			break;
 		}
 
-		// 1. Read and display current dialogue (unless skipping to choices)
-		uint32 endPos = position; // Declare outside the if block
 		if (!skipToChoices) {
-			Common::String text;
-			byte speakerId;
-			endPos = readTextBlock(conversationData, dataSize, position, text, speakerId);
-			Common::Array<Common::Array<Common::String>> wrappedText = wordWrap(text);
-			// debug("Word wrapping %s produces %d pages", text.c_str(), wrappedText.size());
-			// Skip spurious single character artifacts
-			if (!text.empty() && text.size() > 1) {
-				displayDialogue(wrappedText, speakerId);
-			}
-
-			// Move to end of text
-			position = endPos;
+			state.position = readAndDisplayDialogue(conversationData, dataSize, state.position);
 		}
 
-		// 2. Check for end of conversation (skip if going directly to choices)
 		if (!skipToChoices) {
-			if (position >= dataSize) {
-				debug("Reached end of data after reading dialogue");
-				break;
-			}
-
-			byte controlByte = conversationData[position];
-
-			if (controlByte == CTRL_END_CONVERSATION) {
-				debug("End of conversation marker found");
-				break;
-			}
-
-			if (controlByte == CTRL_ACTION_TRIGGER) {
-				uint16 actionCode = conversationData[position + 1] | (conversationData[position + 2] << 8);
-				debug("Action trigger %d encountered!", actionCode);
-				g_engine->dialogActionTrigger(
-					actionCode,
-					g_engine->_room->_currentRoomNumber,
-					currentRoot);
-				break;
-			}
-
-			// Move past control byte
-			if (controlByte == CTRL_END_TEXT) {
-				position++;
-				if (position >= dataSize) {
-					debug("Reached end of data after moving past control byte");
-					break;
+			ConversationEndResult endResult = checkConversationEnd(conversationData, dataSize, state.position);
+			if (endResult.shouldEnd) {
+				if (endResult.hasAction) {
+					g_engine->dialogActionTrigger(endResult.actionCode, g_engine->_room->_currentRoomNumber, state.currentRoot);
 				}
+				break;
 			}
+			state.position = endResult.nextPosition;
 		}
-
-		// 3. Before parsing choices, check if we're at a choice marker
-		// Skip control bytes to peek at next meaningful byte
-		uint32 peekPos = position;
+		// Check for choice markers
+		uint32 peekPos = state.position;
 		if (!skipToChoices) {
-			while (peekPos < dataSize &&
-				   (conversationData[peekPos] == CTRL_ALT_END_MARKER_1 ||
-					conversationData[peekPos] == CTRL_ALT_END_MARKER_2 ||
-					conversationData[peekPos] == CTRL_TEXT_TERMINATOR)) {
-				peekPos++;
-			}
+			peekPos = peekNextMeaningfulByte(conversationData, dataSize, state.position);
 
 			// If not at a choice marker, there's more dialogue to read
 			if (peekPos < dataSize &&
@@ -610,75 +511,47 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 			}
 		}
 
-		// 4. Parse choices - save position for F0 "go back"
-		skipToChoices = false; // Reset flag
-		lastChoiceMenuPosition = position;
+		// Parse choices
+		skipToChoices = false;
+		state.lastChoiceMenuPosition = state.position;
+
 		Common::Array<ChoiceOption> *choices = new Common::Array<ChoiceOption>();
-		parseChoices(conversationData, dataSize, position, choices);
+		parseChoices(conversationData, dataSize, state.position, choices);
 
-		// Store original choice count before potentially adding goodbye
 		uint originalChoiceCount = choices->size();
-
-		// Only consider adding goodbye if there are MULTIPLE choices
-		// If there's only 1 choice, it's auto-dialogue and should not have goodbye
-		if (originalChoiceCount > 1) {
-			// Check if ANY choice has a conversation terminator (F4 or F8)
-			// If so, there's already a way to exit - don't add goodbye option
-			bool anyChoiceTerminatesConversation = false;
-			for (uint i = 0; i < choices->size(); i++) {
-				if ((*choices)[i].hasConversationEndMarker) {
-					anyChoiceTerminatesConversation = true;
-					break;
-				}
-			}
-			if (!anyChoiceTerminatesConversation) {
-				// No choice ends the conversation, so add the goodbye option
-				ChoiceOption termChoice;
-				termChoice.choiceIndex = currentChoiceLevel;
-				termChoice.isTerminator = true;
-				termChoice.isDisabled = false;
-				termChoice.shouldDisableOnSelect = false;
-				termChoice.text = g_engine->_res->_conversationTerminator;
-				choices->push_back(termChoice);
-			}
-		}
+		addGoodbyeOptionIfNeeded(choices, state.currentChoiceLevel, originalChoiceCount);
 
 		debug("Parsed %u choices", choices->size());
 		for (uint i = 0; i < choices->size(); i++) {
 			debug(" Choice %u (index %d): \"%s\" (Disabled: %s)", i, (*choices)[i].choiceIndex, (*choices)[i].text.c_str(),
 				  (*choices)[i].isDisabled ? "Yes" : "No");
 		}
+
 		if (choices->empty()) {
-			// No choices, continue reading dialogue
-			position = peekPos;
+			state.position = peekPos;
 			continue;
 		}
 
-		// Check if we have a currentChoiceLevel and if these choices are at the expected level
-		if (currentChoiceLevel >= 0) {
-			// After F0, we expect choices at the SAME level (currentChoiceLevel)
-			// Otherwise, we expect choices at the NEXT level (currentChoiceLevel + 1)
+		// Validate choice level
+		if (state.currentChoiceLevel >= 0) {
 			bool foundExpectedLevel = false;
 			for (uint i = 0; i < choices->size(); i++) {
-				if ((*choices)[i].choiceIndex == currentChoiceLevel ||
-					(*choices)[i].choiceIndex == currentChoiceLevel + 1) {
+				if ((*choices)[i].choiceIndex == state.currentChoiceLevel ||
+					(*choices)[i].choiceIndex == state.currentChoiceLevel + 1) {
 					foundExpectedLevel = true;
-					// Update currentChoiceLevel to match what we found
-					currentChoiceLevel = (*choices)[i].choiceIndex;
+					state.currentChoiceLevel = (*choices)[i].choiceIndex;
 					break;
 				}
 			}
 
 			if (!foundExpectedLevel) {
-				debug("No choices found at level %d or %d, ending conversation", currentChoiceLevel, currentChoiceLevel + 1);
+				debug("No choices found at level %d or %d, ending conversation", state.currentChoiceLevel, state.currentChoiceLevel + 1);
 				break;
 			}
 		}
 
-		// 5. Display choices and get selection
+		// Process user selection
 		int selectedIndex = 0;
-
-		// Check if this is auto-dialogue (only one choice)
 		if (choices->size() == 1) {
 			// Auto-dialogue: display it automatically
 			debug("Auto-selecting single choice: \"%s\"", (*choices)[0].text.c_str());
@@ -695,56 +568,241 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 				_currentChoices = nullptr;
 			}
 			_currentChoices = choices;
-			// Use displayChoices to show and select
 			selectedIndex = selectChoice(choiceTexts, g_engine->_compositeBuffer);
 		}
 
-		// 6. Move position to after the selected choice
-		if (selectedIndex >= 0 && selectedIndex < (int)choices->size()) {
+		state.position = processChoiceSelection(conversationData, dataSize, choices, selectedIndex, state);
+	}
 
-			// Save this choice in case we hit F0 and need to disable it
-			lastSelectedChoice = (*choices)[selectedIndex];
-			if (lastSelectedChoice.isTerminator) {
-				displayDialogue(lastSelectedChoice.text, ALFRED_COLOR);
+	debug("Conversation ended");
+}
+
+uint32 DialogManager::findRoot(int &currentRoot, uint32 currentPosition, uint32 dataSize, const byte *conversationData) {
+	while (g_engine->_state->getRootDisabledState(g_engine->_room->_currentRoomNumber, currentRoot)) {
+		// This root is disabled, skip to next
+		while (currentPosition < dataSize) {
+			if (conversationData[currentPosition] == CTRL_END_BRANCH) {
+				currentPosition++; // Move past end branch marker
+				currentRoot++;
 				break;
 			}
-			position = (*choices)[selectedIndex].dataOffset;
-			currentChoiceLevel = (*choices)[selectedIndex].choiceIndex;
-
-			// Read and display the selected choice as dialogue
-			Common::String choiceText;
-			byte choiceSpeakerId;
-			endPos = readTextBlock(conversationData, dataSize, position, choiceText, choiceSpeakerId);
-
-			if (!choiceText.empty() && choiceText.size() > 1) {
-				displayDialogue(choiceText, ALFRED_COLOR);
-				// Only disable FB choice if all sub-branches are exhausted
-				if ((*choices)[selectedIndex].shouldDisableOnSelect) {
-					bool shouldDisable = checkAllSubBranchesExhausted(conversationData, dataSize, endPos, currentChoiceLevel);
-					if (shouldDisable) {
-						debug("Disabling one-time choice at index %d after selection", selectedIndex);
-						g_engine->_room->addDisabledChoice((*choices)[selectedIndex]);
-					}
-				}
-			}
+			currentPosition++;
+		}
+	}
+	return currentPosition;
+}
+
+uint32 DialogManager::findSpeaker(byte npcIndex, uint32 dataSize, const byte *conversationData) {
+	// Find the speaker tree for this NPC; they are marked by 0xFE 0xXX where XX is NPC index + 1
+	bool speakerTreeOffsetFound = false;
+	int currentConversationTree = npcIndex + 1;
+	uint32 position = 0;
+	while (position < dataSize && !speakerTreeOffsetFound) {
+		if (conversationData[position] == CTRL_ALT_SPEAKER_ROOT && conversationData[position + 1] == currentConversationTree) {
+			speakerTreeOffsetFound = true;
+			position += 2; // Move past the speaker tree marker and npc index
+		} else {
+			position++;
+		}
+	}
+	return position;
+}
 
-			position = endPos;
+// Skip control bytes that should be ignored
+uint32 DialogManager::skipControlBytes(const byte *data, uint32 dataSize, uint32 position) {
+	while (position < dataSize &&
+		   (data[position] == CTRL_ALT_END_MARKER_1 ||
+			data[position] == CTRL_ALT_END_MARKER_2)) {
+		position++;
+	}
+	return position;
+}
 
-			// Skip past end marker
-			if (position < dataSize) {
-				byte endByte = conversationData[position];
-				if (endByte == CTRL_END_TEXT || endByte == CTRL_END_BRANCH ||
-					endByte == CTRL_ACTION_TRIGGER) {
-					position++;
-				}
-			}
+// Peek at next meaningful byte after skipping control bytes
+uint32 DialogManager::peekNextMeaningfulByte(const byte *data, uint32 dataSize, uint32 position) {
+	uint32 peekPos = position;
+	while (peekPos < dataSize &&
+		   (data[peekPos] == CTRL_ALT_END_MARKER_1 ||
+			data[peekPos] == CTRL_ALT_END_MARKER_2 ||
+			data[peekPos] == CTRL_TEXT_TERMINATOR)) {
+		peekPos++;
+	}
+	return peekPos;
+}
+
+// Initialize conversation by finding speaker and root
+ConversationState DialogManager::initializeConversation(const byte *data, uint32 dataSize, byte npcIndex) {
+	ConversationState state;
+	state.position = findSpeaker(npcIndex, dataSize, data);
+	state.currentRoot = 0;
+	state.position = findRoot(state.currentRoot, state.position, dataSize, data);
+	state.currentChoiceLevel = -1;
+	state.lastChoiceMenuPosition = 0;
+	state.lastSelectedChoice = ChoiceOption();
+
+	// Skip any junk at start until we find a speaker marker
+	while (state.position < dataSize && data[state.position] != CTRL_SPEAKER_ID) {
+		state.position++;
+	}
+
+	return state;
+}
+
+// Handle F0 "Go Back" control byte
+bool DialogManager::handleGoBack(const byte *data, uint32 position, ConversationState &state) {
+	if (data[position] != CTRL_GO_BACK) {
+		return false;
+	}
+
+	debug("F0 Go Back at position %u, rewinding to lastChoiceMenuPosition %u", position, state.lastChoiceMenuPosition);
+
+	if (state.lastChoiceMenuPosition > 0) {
+		// Disable the choice that led to this F0
+		if (state.lastSelectedChoice.dataOffset > 0) {
+			debug("F0: Disabling choice that led here at offset %u", state.lastSelectedChoice.dataOffset);
+			g_engine->_room->addDisabledChoice(state.lastSelectedChoice);
 		}
+		state.position = state.lastChoiceMenuPosition;
+		return true; // Skip to choices
+	} else {
+		debug("F0: No previous choice menu, ending conversation");
+		return false; // End conversation
 	}
+}
 
-	debug("Conversation ended");
-	// Note: The caller should set inConversation = false after this returns
+uint32 DialogManager::readAndDisplayDialogue(const byte *data, uint32 dataSize, uint32 position) {
+	Common::String text;
+	byte speakerId;
+	uint32 endPos = readTextBlock(data, dataSize, position, text, speakerId);
+	Common::Array<Common::Array<Common::String>> wrappedText = wordWrap(text);
+
+	// Skip spurious single character artifacts
+	if (!text.empty() && text.size() > 1) {
+		displayDialogue(wrappedText, speakerId);
+	}
+
+	return endPos;
+}
+
+ConversationEndResult DialogManager::checkConversationEnd(const byte *data, uint32 dataSize, uint32 position) {
+	ConversationEndResult result;
+	result.shouldEnd = false;
+	result.nextPosition = position;
+	result.hasAction = false;
+	result.actionCode = 0;
+
+	if (position >= dataSize) {
+		debug("Reached end of data after reading dialogue");
+		result.shouldEnd = true;
+		return result;
+	}
+
+	byte controlByte = data[position];
+
+	if (controlByte == CTRL_END_CONVERSATION) {
+		debug("End of conversation marker found");
+		result.shouldEnd = true;
+		return result;
+	}
+
+	if (controlByte == CTRL_ACTION_TRIGGER) {
+		result.actionCode = data[position + 1] | (data[position + 2] << 8);
+		debug("Action trigger %d encountered!", result.actionCode);
+		result.shouldEnd = true;
+		result.hasAction = true;
+		return result;
+	}
+
+	// Move past control byte
+	if (controlByte == CTRL_END_TEXT) {
+		result.nextPosition = position + 1;
+	}
+
+	return result;
 }
 
+void DialogManager::addGoodbyeOptionIfNeeded(Common::Array<ChoiceOption> *choices, int currentChoiceLevel, uint originalChoiceCount) {
+	// Only consider adding goodbye if there are MULTIPLE choices
+	// If there's only 1 choice, it's auto-dialogue and should not have goodbye
+	if (originalChoiceCount <= 1) {
+		return;
+	}
+
+	// Check if ANY choice has a conversation terminator (F4 or F8)
+	// If so, there's already a way to exit - don't add goodbye option
+	bool anyChoiceTerminatesConversation = false;
+	for (uint i = 0; i < choices->size(); i++) {
+		if ((*choices)[i].hasConversationEndMarker) {
+			anyChoiceTerminatesConversation = true;
+			break;
+		}
+	}
+
+	if (!anyChoiceTerminatesConversation) {
+		ChoiceOption termChoice;
+		termChoice.choiceIndex = currentChoiceLevel;
+		termChoice.isTerminator = true;
+		termChoice.isDisabled = false;
+		termChoice.shouldDisableOnSelect = false;
+		termChoice.text = g_engine->_res->_conversationTerminator;
+		choices->push_back(termChoice);
+	}
+}
+
+// Handle choice selection and response
+uint32 DialogManager::processChoiceSelection(
+	const byte *data,
+	uint32 dataSize,
+	Common::Array<ChoiceOption> *choices,
+	int selectedIndex,
+	ConversationState &state) {
+
+	if (selectedIndex < 0 || selectedIndex >= (int)choices->size()) {
+		return state.position;
+	}
+
+	// Save this choice in case we hit F0 and need to disable it
+	state.lastSelectedChoice = (*choices)[selectedIndex];
+
+	if (state.lastSelectedChoice.isTerminator) {
+		displayDialogue(state.lastSelectedChoice.text, ALFRED_COLOR);
+		return dataSize; // End conversation
+	}
+
+	uint32 position = (*choices)[selectedIndex].dataOffset;
+	state.currentChoiceLevel = (*choices)[selectedIndex].choiceIndex;
+
+	// Read and display the selected choice as dialogue
+	Common::String choiceText;
+	byte choiceSpeakerId;
+	uint32 endPos = readTextBlock(data, dataSize, position, choiceText, choiceSpeakerId);
+
+	if (!choiceText.empty() && choiceText.size() > 1) {
+		displayDialogue(choiceText, ALFRED_COLOR);
+
+		// Only disable FB choice if all sub-branches are exhausted
+		if ((*choices)[selectedIndex].shouldDisableOnSelect) {
+			bool shouldDisable = checkAllSubBranchesExhausted(data, dataSize, endPos, state.currentChoiceLevel);
+			if (shouldDisable) {
+				debug("Disabling one-time choice at index %d after selection", selectedIndex);
+				g_engine->_room->addDisabledChoice((*choices)[selectedIndex]);
+			}
+		}
+	}
+
+	position = endPos;
+
+	// Skip past end marker
+	if (position < dataSize) {
+		byte endByte = data[position];
+		if (endByte == CTRL_END_TEXT || endByte == CTRL_END_BRANCH ||
+			endByte == CTRL_ACTION_TRIGGER) {
+			position++;
+		}
+	}
+
+	return position;
+}
 void DialogManager::sayAlfred(Common::StringArray texts) {
 	g_engine->_alfredState.setState(ALFRED_TALKING);
 
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 03c578ca35e..3c42ed7dff9 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -50,6 +50,22 @@ namespace Pelrock {
 #define CTRL_ALT_END_MARKER_2 0xEB       /* Alt end marker 2 */
 #define CTRL_ALT_SPEAKER_ROOT 0xFE       /* Separates conversations from different speakers */
 
+// Helper structures for conversation state management
+struct ConversationState {
+	uint32 position;
+	int currentChoiceLevel;
+	uint32 lastChoiceMenuPosition;
+	ChoiceOption lastSelectedChoice;
+	int currentRoot;
+};
+
+struct ConversationEndResult {
+	bool shouldEnd;
+	uint32 nextPosition;
+	uint16 actionCode;
+	bool hasAction;
+};
+
 class DialogManager {
 private:
 	Graphics::Screen *_screen = nullptr;
@@ -67,6 +83,16 @@ private:
 	void checkMouse();
 	bool checkAllSubBranchesExhausted(const byte *data, uint32 dataSize, uint32 startPos, int currentChoiceLevel);
 
+	// Refactored helper functions for startConversation
+	uint32 skipControlBytes(const byte *data, uint32 dataSize, uint32 position);
+	uint32 peekNextMeaningfulByte(const byte *data, uint32 dataSize, uint32 position);
+	ConversationState initializeConversation(const byte *data, uint32 dataSize, byte npcIndex);
+	bool handleGoBack(const byte *data, uint32 position, ConversationState &state);
+	uint32 readAndDisplayDialogue(const byte *data, uint32 dataSize, uint32 position);
+	ConversationEndResult checkConversationEnd(const byte *data, uint32 dataSize, uint32 position);
+	void addGoodbyeOptionIfNeeded(Common::Array<ChoiceOption> *choices, int currentChoiceLevel, uint originalChoiceCount);
+	uint32 processChoiceSelection(const byte *data, uint32 dataSize, Common::Array<ChoiceOption> *choices, int selectedIndex, ConversationState &state);
+
 public:
 	DialogManager(Graphics::Screen *screen, PelrockEventManager *events, GraphicsManager *graphics);
 	~DialogManager();
@@ -74,6 +100,8 @@ public:
 	void displayChoices(Common::Array<ChoiceOption> *choices, byte *compositeBuffer);
 	int selectChoice(Common::Array<Common::String> &choices, byte *compositeBuffer);
 	void startConversation(const byte *conversationData, uint32 dataSize, byte npcIndex, Sprite *alfredAnimSet = nullptr);
+	uint32 findRoot(int &currentRoot, uint32 position, uint32 dataSize, const byte *conversationData);
+	uint32 findSpeaker(byte npcIndex, uint32 dataSize, const byte *conversationData);
 	void sayAlfred(Description description);
 	void sayAlfred(Common::StringArray texts);
 	void say(Common::StringArray texts, byte spriteIndex = 0);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 50a72e96a7a..26bf9867463 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -155,7 +155,7 @@ void PelrockEngine::init() {
 		loadAnims();
 		// setScreen(0, ALFRED_DOWN);
 		// setScreen(3, ALFRED_RIGHT);
-		setScreen(23, ALFRED_DOWN);
+		setScreen(22, ALFRED_DOWN);
 		// setScreen(9, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
@@ -1632,7 +1632,6 @@ void PelrockEngine::checkMouseHover() {
 
 	if (hotspotIndex != -1) {
 		hotspotDetected = true;
-		debug("Hotspot detected: %s", _room->_currentRoomDescriptions[hotspotIndex].text.c_str());
 		_hoveredMapLocation = _room->_currentRoomDescriptions[hotspotIndex].text;
 	}
 	else if (!alfredDetected) {


Commit: c27335f3623e316f4621d9152ba4da9cade5e836
    https://github.com/scummvm/scummvm/commit/c27335f3623e316f4621d9152ba4da9cade5e836
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:58+02:00

Commit Message:
PELROCK: Implements cascade disable of choice trees in conversations

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/room.cpp
    engines/pelrock/sound.cpp


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 828c500170b..d99c2fec6dd 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -18,6 +18,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
+#include "common/stack.h"
 
 #include "pelrock/dialog.h"
 #include "dialog.h"
@@ -80,7 +81,7 @@ uint32 DialogManager::readTextBlock(
 
 	int lineIndex = data[++pos];
 	pos++; // blank
-	debug("Reading text block starting at pos %u, line index %d, speaker ID %d", startPos, lineIndex, outSpeakerId);
+	// debug("Reading text block starting at pos %u, line index %d, speaker ID %d", startPos, lineIndex, outSpeakerId);
 	// Read text until control byte
 	while (pos < dataSize) {
 		byte b = data[pos];
@@ -223,7 +224,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 
 		if (_events->_leftMouseClicked) {
 			_events->_leftMouseClicked = false;
-			debug("Dialogue click to advance, current page: %d, totalPages: %d", curPage, (int)dialogueLines.size());
+			// debug("Dialogue click to advance, current page: %d, totalPages: %d", curPage, (int)dialogueLines.size());
 			if (curPage < (int)dialogueLines.size() - 1) {
 				curPage++;
 			} else {
@@ -239,6 +240,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 }
 
 void DialogManager::displayDialogue(Common::String text, byte speakerId) {
+	debug("Displaying dialogue: \"%s\" (Speaker ID: %d)", text.c_str(), speakerId);
 	displayDialogue(wordWrap(text), speakerId);
 }
 
@@ -464,13 +466,13 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 	// Initialize conversation state
 	ConversationState state = initializeConversation(conversationData, dataSize, npcIndex);
 	bool skipToChoices = false;
-
+	Common::Stack<uint32> positionStack; // Stack to handle nested branches for "go back" functionality
 	// Main conversation loop
 	while (state.position < dataSize && !g_engine->shouldQuit()) {
 		state.position = skipControlBytes(conversationData, dataSize, state.position);
 
 		if (state.position < dataSize && conversationData[state.position] == CTRL_GO_BACK) {
-			if (handleGoBack(conversationData, state.position, state)) {
+			if (handleGoBack(conversationData, positionStack, state.position, state)) {
 				skipToChoices = true;
 			} else {
 				break; // End conversation if no previous menu
@@ -513,7 +515,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 
 		// Parse choices
 		skipToChoices = false;
-		state.lastChoiceMenuPosition = state.position;
+		uint32 positionAtChoices = state.position;
 
 		Common::Array<ChoiceOption> *choices = new Common::Array<ChoiceOption>();
 		parseChoices(conversationData, dataSize, state.position, choices);
@@ -526,12 +528,23 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 			debug(" Choice %u (index %d): \"%s\" (Disabled: %s)", i, (*choices)[i].choiceIndex, (*choices)[i].text.c_str(),
 				  (*choices)[i].isDisabled ? "Yes" : "No");
 		}
+		debug("-----------------------");
 
 		if (choices->empty()) {
-			state.position = peekPos;
+			state.position = positionStack.empty() ? 0 : positionStack.pop();
+			if(state.position == 0) {
+				debug("No choices and no previous position to go back to, ending conversation");
+				break;
+			}
+			checkAllSubBranchesExhausted(conversationData, dataSize, state.position, state.currentChoiceLevel-1);
+			debug("No choices found, popping back to previous choice menu, position %u", state.position);
+			skipToChoices = true;
+			// state.position = peekPos;
 			continue;
 		}
 
+		positionStack.push(positionAtChoices); // Push position of this choice menu onto stack for potential "go back"
+
 		// Validate choice level
 		if (state.currentChoiceLevel >= 0) {
 			bool foundExpectedLevel = false;
@@ -637,7 +650,6 @@ ConversationState DialogManager::initializeConversation(const byte *data, uint32
 	state.currentRoot = 0;
 	state.position = findRoot(state.currentRoot, state.position, dataSize, data);
 	state.currentChoiceLevel = -1;
-	state.lastChoiceMenuPosition = 0;
 	state.lastSelectedChoice = ChoiceOption();
 
 	// Skip any junk at start until we find a speaker marker
@@ -649,25 +661,30 @@ ConversationState DialogManager::initializeConversation(const byte *data, uint32
 }
 
 // Handle F0 "Go Back" control byte
-bool DialogManager::handleGoBack(const byte *data, uint32 position, ConversationState &state) {
+// When F0 is hit, all choices at the current level have been exhausted.
+// The cascading disable in disableChoiceIfNeeded should have already disabled
+// the choices that led here. We just need to go back to the parent level.
+bool DialogManager::handleGoBack(const byte *data, Common::Stack<uint32> &positionStack, uint32 position, ConversationState &state) {
 	if (data[position] != CTRL_GO_BACK) {
 		return false;
 	}
 
-	debug("F0 Go Back at position %u, rewinding to lastChoiceMenuPosition %u", position, state.lastChoiceMenuPosition);
+	debug("F0 Go Back hit at position %u, current level %d", position, state.currentChoiceLevel);
 
-	if (state.lastChoiceMenuPosition > 0) {
-		// Disable the choice that led to this F0
-		if (state.lastSelectedChoice.dataOffset > 0) {
-			debug("F0: Disabling choice that led here at offset %u", state.lastSelectedChoice.dataOffset);
-			g_engine->_room->addDisabledChoice(state.lastSelectedChoice);
-		}
-		state.position = state.lastChoiceMenuPosition;
-		return true; // Skip to choices
-	} else {
-		debug("F0: No previous choice menu, ending conversation");
-		return false; // End conversation
+	// Pop position stack - we're going back to parent level
+	uint32 parentPos = positionStack.empty() ? 0 : positionStack.pop();
+
+	if (parentPos == 0) {
+		debug("F0: No parent position on stack, ending conversation");
+		return false;
 	}
+
+	// Go up one level
+	state.currentChoiceLevel--;
+	state.position = parentPos;
+	debug("F0: Moved back to level %d, position %u", state.currentChoiceLevel, parentPos);
+
+	return true;
 }
 
 uint32 DialogManager::readAndDisplayDialogue(const byte *data, uint32 dataSize, uint32 position) {
@@ -779,15 +796,8 @@ uint32 DialogManager::processChoiceSelection(
 
 	if (!choiceText.empty() && choiceText.size() > 1) {
 		displayDialogue(choiceText, ALFRED_COLOR);
-
-		// Only disable FB choice if all sub-branches are exhausted
-		if ((*choices)[selectedIndex].shouldDisableOnSelect) {
-			bool shouldDisable = checkAllSubBranchesExhausted(data, dataSize, endPos, state.currentChoiceLevel);
-			if (shouldDisable) {
-				debug("Disabling one-time choice at index %d after selection", selectedIndex);
-				g_engine->_room->addDisabledChoice((*choices)[selectedIndex]);
-			}
-		}
+		debug("Will check if choice should be disabled after displaying dialogue");
+		disableChoiceIfNeeded(choices, selectedIndex, data, dataSize, endPos, state);
 	}
 
 	position = endPos;
@@ -803,6 +813,94 @@ uint32 DialogManager::processChoiceSelection(
 
 	return position;
 }
+void DialogManager::disableChoiceIfNeeded(Common::Array<Pelrock::ChoiceOption> *choices, int selectedIndex, const byte *data, uint32 dataSize, uint32 endPos, Pelrock::ConversationState &state) {
+	// Cascading parent disable:
+	// 1. Check if current choice's sub-branches are exhausted
+	// 2. If so AND it's 0xFB, disable the current choice
+	// 3. Go up to parent level, check if parent's sub-branches are exhausted
+	// 4. Continue until we find a level with active sub-branches or reach level 1
+
+	// Start with the currently selected choice
+	int currentLevel = state.currentChoiceLevel;
+	uint32 currentChoicePos = (*choices)[selectedIndex].dataOffset;
+	bool isCurrentFB = (*choices)[selectedIndex].shouldDisableOnSelect;
+
+	while (currentLevel >= 1) {
+		// Check if all sub-branches at this level are exhausted
+		bool allExhausted = checkAllSubBranchesExhausted(data, dataSize, currentChoicePos + 4, currentLevel);
+
+		if (!allExhausted) {
+			debug("Cascading disable stopped at level %d - active sub-branches found", currentLevel);
+			break;
+		}
+
+		// Check if this choice is F1 (repeatable) - don't disable
+		if (!isCurrentFB) {
+			debug("Choice at level %d is repeatable (F1), stopping cascade", currentLevel);
+			break;
+		}
+
+		// Disable this one-time choice
+		debug("Cascading disable: level %d, offset %u", currentLevel, currentChoicePos);
+		ChoiceOption choiceToDisable;
+		choiceToDisable.room = g_engine->_room->_currentRoomNumber;
+		choiceToDisable.dataOffset = currentChoicePos;
+		choiceToDisable.choiceIndex = currentLevel;
+		choiceToDisable.shouldDisableOnSelect = true;
+		g_engine->_room->addDisabledChoice(choiceToDisable);
+
+		// Stop if we've reached level 1
+		if (currentLevel <= 1) {
+			debug("Reached level 1, stopping cascading disable");
+			break;
+		}
+
+		// Go up one level - scan backwards to find the parent FB/F1 marker at (currentLevel - 1)
+		currentLevel--;
+		uint32 scanPos = currentChoicePos;
+		bool foundParent = false;
+
+		while (scanPos > 0) {
+			scanPos--;
+			byte b = data[scanPos];
+
+			// Found 0xFB marker
+			if (b == CTRL_DIALOGUE_MARKER_ONEOFF && scanPos + 1 < dataSize) {
+				byte idx = data[scanPos + 1];
+				if (idx == (byte)currentLevel) {
+					currentChoicePos = scanPos;
+					isCurrentFB = true;
+					foundParent = true;
+					debug("Found parent FB at level %d, pos %u", currentLevel, currentChoicePos);
+					break;
+				}
+			}
+			// Found 0xF1 marker (repeatable)
+			else if (b == CTRL_DIALOGUE_MARKER && scanPos + 1 < dataSize) {
+				byte idx = data[scanPos + 1];
+				if (idx == (byte)currentLevel) {
+					// Found 0xF1 parent - will stop cascade on next iteration
+					currentChoicePos = scanPos;
+					isCurrentFB = false;
+					foundParent = true;
+					debug("Found parent 0xF1 at level %d, pos %u - will stop cascade", currentLevel, currentChoicePos);
+					break;
+				}
+			}
+
+			// Hit boundary markers - stop searching
+			if (b == CTRL_ALT_SPEAKER_ROOT || b == CTRL_END_BRANCH || b == CTRL_ALT_END_MARKER_1) {
+				debug("Hit boundary at pos %u while looking for parent level %d", scanPos, currentLevel);
+				break;
+			}
+		}
+
+		if (!foundParent) {
+			debug("Could not find parent at level %d, stopping cascade", currentLevel);
+			break;
+		}
+	}
+}
 void DialogManager::sayAlfred(Common::StringArray texts) {
 	g_engine->_alfredState.setState(ALFRED_TALKING);
 
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 3c42ed7dff9..44361644696 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -22,6 +22,7 @@
 #define PELROCK_DIALOG_H
 
 #include "common/scummsys.h"
+#include "common/stack.h"
 #include "graphics/screen.h"
 
 #include "pelrock/events.h"
@@ -54,7 +55,6 @@ namespace Pelrock {
 struct ConversationState {
 	uint32 position;
 	int currentChoiceLevel;
-	uint32 lastChoiceMenuPosition;
 	ChoiceOption lastSelectedChoice;
 	int currentRoot;
 };
@@ -87,11 +87,12 @@ private:
 	uint32 skipControlBytes(const byte *data, uint32 dataSize, uint32 position);
 	uint32 peekNextMeaningfulByte(const byte *data, uint32 dataSize, uint32 position);
 	ConversationState initializeConversation(const byte *data, uint32 dataSize, byte npcIndex);
-	bool handleGoBack(const byte *data, uint32 position, ConversationState &state);
+	bool handleGoBack(const byte *data, Common::Stack<uint32> &positionStack,  uint32 position, ConversationState &state);
 	uint32 readAndDisplayDialogue(const byte *data, uint32 dataSize, uint32 position);
 	ConversationEndResult checkConversationEnd(const byte *data, uint32 dataSize, uint32 position);
 	void addGoodbyeOptionIfNeeded(Common::Array<ChoiceOption> *choices, int currentChoiceLevel, uint originalChoiceCount);
 	uint32 processChoiceSelection(const byte *data, uint32 dataSize, Common::Array<ChoiceOption> *choices, int selectedIndex, ConversationState &state);
+	void disableChoiceIfNeeded(Common::Array<Pelrock::ChoiceOption> * choices, int selectedIndex, const byte * data, uint32 dataSize, uint32 endPos, Pelrock::ConversationState & state);
 
 public:
 	DialogManager(Graphics::Screen *screen, PelrockEventManager *events, GraphicsManager *graphics);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 7e73944bc46..2433cf5e920 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -1082,6 +1082,8 @@ void RoomManager::addDisabledChoice(ChoiceOption choice) {
 	uint32 disableOffset = choice.dataOffset + 2;
 	debug("Adding disabled branch for room %d at offset %d (FA written at %d)",
 		  choice.room, choice.dataOffset, disableOffset);
+
+	debug("Disabled branch is: \"%s\"",  choice.text.c_str());
 	ResetEntry resetEntry = ResetEntry();
 	resetEntry.room = choice.room;
 	resetEntry.offset = disableOffset;
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 55f0edf0a55..be2d885d732 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -93,7 +93,7 @@ void SoundManager::playSound(SonidoFile sound, int volume, int channel) {
 		// Create raw audio stream (8-bit unsigned mono is common for old games)
 		stream = Audio::makeRawStream(pcmData, pcmSize, sampleRate, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
 	} else {
-		debug("Unknown sound format at offset %d, with size %d", sound.offset, sound.size);
+		debug("Unknown sound format on sound with name %s at offset %d, with size %d", sound.filename.c_str(), sound.offset, sound.size);
 		delete[] data;
 		return;
 	}


Commit: 90c330d3454939828606fbbc19dd03d6b8a3027d
    https://github.com/scummvm/scummvm/commit/90c330d3454939828606fbbc19dd03d6b8a3027d
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:59+02:00

Commit Message:
PELROCK: Room 22 actions

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/types.h
    engines/pelrock/util.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index d4e5d51e72d..83609979d8b 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -273,50 +273,85 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 279:
 		travelToEgypt();
 		break;
-			// moros
+	// moros
+	case 317:
+		addInventoryItem(95);
+		break;
 	case 330:
+		// Two oranges
+		addInventoryItem(103);
 		break;
 	case 331:
+		_dialog->say(_res->_ingameTexts[HECHOELPRIMO]);
 		break;
 	case 332:
+		//psychologist card
+		if(!_state->hasInventoryItem(104)) {
+			addInventoryItem(104);
+		}
 		break;
 	case 333:
+		_dialog->say(_res->_ingameTexts[MEHANTOMADO_EL_PELO]);
 		break;
 	case 334:
+		addInventoryItem(86);
+		_state->setRootDisabledState(room, rootIndex, true);
+
 		break;
 	case 335:
+		//many oranges
+		addInventoryItem(104);
 		break;
 	case 336:
+		_dialog->say(_res->_ingameTexts[PESADO_UNRATO]);
 		break;
 	case 337:
-		break;
 	case 338:
-		break;
 	case 339:
-		break;
 	case 340:
-		break;
 	case 341:
-		break;
 	case 342:
-		break;
 	case 343:
-		break;
 	case 344:
-		break;
 	case 345:
-		break;
 	case 346:
+		_state->setRootDisabledState(room, rootIndex, true);
 		break;
-	case 347:
-		break;
-	case 348:
-		break;
+	case 348: {
+		//game originally crashes here intentionally!
+		g_system->quit();
+	}
 	case 349:
+		_state->setFlag(FLAG_CONSIGNAS_VENDEDOR, _state->getFlag(FLAG_CONSIGNAS_VENDEDOR) + 1);
+		if (_state->getFlag(FLAG_CONSIGNAS_VENDEDOR) == 2) {
+			_state->setRootDisabledState(room, rootIndex, true);
+		}
 		break;
 	case 350:
+		_state->setFlag(FLAG_CONSIGNAS_VENDEDOR, _state->getFlag(FLAG_CONSIGNAS_VENDEDOR) + 1);
+		if (_state->getFlag(FLAG_CONSIGNAS_VENDEDOR) == 2) {
+			_state->setRootDisabledState(room, rootIndex, true);
+		}
 		break;
 	case 351:
+		_dialog->say(_res->_ingameTexts[TRAIDOR], 0);
+		_dialog->say(_res->_ingameTexts[TUTIA], 1);
+		_dialog->say(_res->_ingameTexts[LATUYA], 0);
+		_dialog->say(_res->_ingameTexts[GORDO], 1);
+		_dialog->say(_res->_ingameTexts[FIDEO], 0);
+		_dialog->say(_res->_ingameTexts[LIMPIACULO], 1);
+		_dialog->say(_res->_ingameTexts[CONTUTURBANTE], 0);
+		_dialog->say(_res->_ingameTexts[OSO], 1);
+		_dialog->say(_res->_ingameTexts[COMADREJA], 0);
+		_dialog->say(_res->_ingameTexts[CABEZON], 1);
+		_dialog->say(_res->_ingameTexts[TUABUELO], 0);
+		_dialog->say(_res->_ingameTexts[TUMUJER], 1);
+		_dialog->say(_res->_ingameTexts[PERDEDOR], 0);
+		_dialog->say(_res->_ingameTexts[SOYMEJORQUETU], 1);
+		_dialog->say(_res->_ingameTexts[TRAMPOSO], 0);
+		_dialog->say(_res->_ingameTexts[MALPERDEDOR], 1);
+		_dialog->say(_res->_ingameTexts[PARAUNAVEZ], 0);
+		_dialog->say(_res->_ingameTexts[MEJORMELARGO], 1);
 		break;
 		// end moros
 	default:
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index a187a70ebf4..9ff2fa9fd2e 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -473,6 +473,9 @@ const ExtraImages extraScreens[] = {
 	{0x152A88, // portrait
 	 0x15BFC8,
 	 8},
+	 {2727564, // CD
+	 2833276,
+	 8}
 };
 
 struct AlfredSpecialAnimOffset {
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 26bf9867463..3de1df77290 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -488,7 +488,7 @@ void PelrockEngine::checkMouse() {
 		_actionPopupState.isActive = false;
 		// Mouse was released while popup is active
 		VerbIcon actionClicked = isActionUnder(_events->_releaseX, _events->_releaseY);
-		if(actionClicked != NO_ACTION) {
+		if (actionClicked != NO_ACTION) {
 			debug("Popup action clicked: %d, is alfredunder %d", actionClicked, _actionPopupState.isAlfredUnder);
 		}
 		if (actionClicked != NO_ACTION && _currentHotspot != nullptr) {
@@ -497,7 +497,7 @@ void PelrockEngine::checkMouse() {
 		} else if (_actionPopupState.isAlfredUnder && actionClicked != NO_ACTION) {
 			debug("Using item on Alfred");
 			useOnAlfred(_state->selectedInventoryItem);
-		} else  {
+		} else {
 			// Released outside popup - just close it
 			_queuedAction = QueuedAction{NO_ACTION, -1, false};
 			_currentHotspot = nullptr;
@@ -1042,7 +1042,6 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	int finalHeight = scaleCalc.scaledHeight;
 	int finalWidth = scaleCalc.scaledWidth;
 
-
 	// Update Alfred's scale state for use by other functions
 	_alfredState.w = finalWidth;
 	_alfredState.h = finalHeight;
@@ -1420,13 +1419,12 @@ Common::Point getPositionInOverlayForIndex(uint index) {
 	return Common::Point(5 + index * (60 + 2), 400 - 60 - 5);
 }
 
-
 void PelrockEngine::pickupIconFlash() {
 	_graphics->showOverlay(65, _compositeBuffer);
 	if (_newItem == -1)
 		return;
 	uint invSize = _state->inventoryItems.size();
-	for( int i = 0; i < invSize; i++) {
+	for (int i = 0; i < invSize; i++) {
 		Common::Point p = getPositionInOverlayForIndex(i);
 		drawSpriteToBuffer(_compositeBuffer, 640, _res->getIconForObject(_state->inventoryItems[i]).iconData, p.x, p.y, 60, 60, 1);
 	}
@@ -1446,6 +1444,9 @@ void PelrockEngine::gameLoop() {
 	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_m) {
 		travelToEgypt();
 	}
+	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_n) {
+		loadExtraScreenAndPresent(10);
+	}
 	renderScene();
 	// _events->waitForKey();
 	_screen->update();
@@ -1574,8 +1575,8 @@ VerbIcon PelrockEngine::isActionUnder(int x, int y) {
 
 bool PelrockEngine::isAlfredUnder(int x, int y) {
 	int localX = x - _alfredState.x;
-	int localY = y - _alfredState.y  + _alfredState.h; // Adjust for scaling (since Alfred's position is based on his feet, but sprite may be scaled from the top)
-	if (localX >= 0 && localX < _alfredState.w && localY >= 0 && localY < _alfredState.h) {
+	int localY = y - _alfredState.y + _alfredState.h; // Adjust for scaling (since Alfred's position is based on his feet, but sprite may be scaled from the top)
+	if (_alfredSprite != nullptr && localX >= 0 && localX < _alfredState.w && localY >= 0 && localY < _alfredState.h) {
 		byte pixel = _alfredSprite[localY * _alfredState.w + localX];
 		if (pixel != 255) {
 			return true;
@@ -1633,12 +1634,11 @@ void PelrockEngine::checkMouseHover() {
 	if (hotspotIndex != -1) {
 		hotspotDetected = true;
 		_hoveredMapLocation = _room->_currentRoomDescriptions[hotspotIndex].text;
-	}
-	else if (!alfredDetected) {
+	} else if (!alfredDetected) {
 		_hoveredMapLocation = "";
 	}
 
-	if(_room->_currentRoomNumber == 21) {
+	if (_room->_currentRoomNumber == 21) {
 		return;
 	}
 
@@ -1649,7 +1649,6 @@ void PelrockEngine::checkMouseHover() {
 	// Calculate walk target first (before checking anything else)
 	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, _events->_mouseX, _events->_mouseY, hotspotDetected, hotspotDetected ? &_room->_currentRoomHotspots[hotspotIndex] : nullptr);
 
-
 	// Check if walk target hits any exit
 	bool exitDetected = false;
 	Exit *exit = isExitUnder(walkTarget.x, walkTarget.y);
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index be2d885d732..4738fe93744 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -108,7 +108,7 @@ void SoundManager::playSound(SonidoFile sound, int volume, int channel) {
 				_mixer->stopHandle(_sfxHandles[channel]);
 			}
 		}
-		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, volume, 0, DisposeAfterUse::YES);
+		// _mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, volume, 0, DisposeAfterUse::YES);
 	}
 }
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index e6bff318382..87dd315c631 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -505,8 +505,9 @@ struct ResetEntry {
 #define FLAG_GUARDIA_PIDECOSAS 49
 #define FLAG_GUARDIA_DNI_ENTREGADO 50
 #define FLAG_AGENCIA_ABIERTA 51
+#define FLAG_CONSIGNAS_VENDEDOR 52
 
-const int kNumGameFlags = 52;
+const int kNumGameFlags = 53;
 
 struct GameStateData {
 	byte flags[kNumGameFlags];
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 31bab7e10e0..709614e875d 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -89,7 +89,7 @@ void drawText(byte *screenBuffer, Graphics::Font *font, Common::String text, int
 		for (int px = 0; px < bboxW; px++) {
 			int destIdx = (y + py) * 640 + (x + px);
 			int pixelColor = *((byte *)surface.getBasePtr(px, py));
-			if(pixelColor != 255)
+			if(pixelColor != 255 && destIdx >= 0 && destIdx < 256000)
 				screenBuffer[destIdx] = pixelColor;
 		}
 	}


Commit: 876544feb332612c064bf3675aaa9ce1972c9f05
    https://github.com/scummvm/scummvm/commit/876544feb332612c064bf3675aaa9ce1972c9f05
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:59+02:00

Commit Message:
PELROCK: Fixes shading bug

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 83609979d8b..c8e998d48b2 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -354,6 +354,18 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_dialog->say(_res->_ingameTexts[MEJORMELARGO], 1);
 		break;
 		// end moros
+		//puta 2
+	case 352:
+		break;
+	case 353:
+		break;
+	case 354:
+		break;
+	case 355:
+		break;
+	case 356:
+		break;
+	//end puta
 	default:
 		debug("Got actionTrigger %d in dialogActionTrigger, but no handler defined", actionTrigger);
 		break;
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 3de1df77290..f971194c1d8 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1057,12 +1057,29 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	delete[] _alfredSprite;
 	_alfredSprite = scaledBuf;
 
-	int shadowPos = _alfredState.y; // - finalHeight;
-	bool shadeCharacter = _room->_pixelsShadows[shadowPos * 640 + _alfredState.x] != 0xFF;
-	if (shadeCharacter) {
+	// Shadow detection: scan across Alfred's width at feet line.
+	// Original game scans shadow buffer
+	// at (topY + 0x66) * 640 + X + col for col = 0..width, where topY + 0x66 = feetY.
+	// The shadow map value (0-3) indexes into the palette remap tables.
+	byte shadowLevel = 0xFF; // 0xFF = no shadow
+	int feetY = _alfredState.y;
+	if (feetY >= 0 && feetY < 400 && _room->_pixelsShadows != nullptr) {
+		for (int col = 0; col < finalWidth; col++) {
+			int checkX = _alfredState.x + col;
+			if (checkX >= 0 && checkX < 640) {
+				byte shadowVal = _room->_pixelsShadows[feetY * 640 + checkX];
+				if (shadowVal != 0xFF) {
+					shadowLevel = shadowVal;
+					break; // Original breaks on first shadow pixel found
+				}
+			}
+		}
+	}
+
+	if (shadowLevel != 0xFF && shadowLevel < 4) {
 		for (int i = 0; i < finalWidth * finalHeight; i++) {
 			if (_alfredSprite[i] != 255) {
-				_alfredSprite[i] = _room->_paletteRemaps[1][_alfredSprite[i]];
+				_alfredSprite[i] = _room->_paletteRemaps[3 - shadowLevel][_alfredSprite[i]];
 			}
 		}
 	}


Commit: 104e61136de7f226fd3e5259a16e6eda789c83c3
    https://github.com/scummvm/scummvm/commit/104e61136de7f226fd3e5259a16e6eda789c83c3
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T12:59:59+02:00

Commit Message:
PELROCK: Palette effect on room 28

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/graphics.cpp
    engines/pelrock/graphics.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index c8e998d48b2..7bf4000ad0b 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -113,6 +113,9 @@ const ActionEntry actionTable[] = {
 	{400, OPEN, &PelrockEngine::openTravelAgencyDoor},
 	{400, CLOSE, &PelrockEngine::closeTravelAgencyDoor},
 
+	// Room 28
+	{472, PICKUP, &PelrockEngine::pickUpRoom28Object},
+
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
 	{WILDCARD, TALK, &PelrockEngine::noOpAction},   // Generic talk action
@@ -318,8 +321,9 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_state->setRootDisabledState(room, rootIndex, true);
 		break;
 	case 348: {
-		//game originally crashes here intentionally!
-		g_system->quit();
+		// Anti-piracy punishment: corrupt screen + noise + crash
+		antiPiracyEffect();
+		break;
 	}
 	case 349:
 		_state->setFlag(FLAG_CONSIGNAS_VENDEDOR, _state->getFlag(FLAG_CONSIGNAS_VENDEDOR) + 1);
@@ -1080,6 +1084,130 @@ void PelrockEngine::waitForActionEnd() {
 	}
 }
 
-// void checkOBjec
+/**
+ * Handler for picking up object with extra_id 472 in Room 28.
+ * Loads a special palette from ALFRED.7 at offset 0x1610CE and
+ * fades to it using the step-wise palette transition.
+ *
+ * Original handler at Ghidra address 0x1FED8.
+ */
+void PelrockEngine::pickUpRoom28Object(HotSpot *hotspot) {
+	// Load the special palette from ALFRED.7 at offset 0x1610CE
+	static const uint32 kRoom28PaletteOffset = 0x1610CE;
+
+	Common::File alfred7;
+	if (!alfred7.open(Common::Path("ALFRED.7"))) {
+		warning("Could not open ALFRED.7 for room 28 palette");
+		return;
+	}
+
+	byte targetPalette[768];
+	alfred7.seek(kRoom28PaletteOffset, SEEK_SET);
+	alfred7.read(targetPalette, 768);
+	alfred7.close();
+
+	// Convert 6-bit VGA palette (0-63) to 8-bit (0-255)
+	for (int i = 0; i < 768; i++) {
+		targetPalette[i] = targetPalette[i] << 2;
+	}
+
+	// Fade from current palette to the new palette
+	_graphics->fadePaletteToTarget(targetPalette, 25);
+	debug("Finished palette fade for room 28 object pickup");
+	// Pick up the item
+	// addInventoryItem(hotspot->extra);
+	_room->disableHotspot(hotspot);
+}
+
+/**
+ * Original behavior:
+ * 1. Stop all sound
+ * 2. Loop: corrupt the background buffer pointer with random values,
+ *    copy garbage to screen, write sequential memory bytes to PC speaker
+ *    port 0x61 to produce noise
+ * 3. On keypress: divide by zero -> crash to DOS
+ *
+ * ScummVM behavior:
+ * 1. Stop all sound
+ * 2. Loop: fill screen with random pixels, play white noise
+ * 3. On keypress: return to launcher
+ */
+void PelrockEngine::antiPiracyEffect() {
+	_sound->stopAllSounds();
+	_sound->stopMusic();
+
+	// Generate a buffer of white noise for the PC speaker simulation
+	const int kNoiseLength = 16000; // 1 second at 8kHz
+	byte *noiseData = new byte[kNoiseLength + 44]; // WAV header + data
+
+	// Write a minimal WAV header
+	memcpy(noiseData, "RIFF", 4);
+	WRITE_LE_UINT32(noiseData + 4, kNoiseLength + 36);
+	memcpy(noiseData + 8, "WAVE", 4);
+	memcpy(noiseData + 12, "fmt ", 4);
+	WRITE_LE_UINT32(noiseData + 16, 16);      // chunk size
+	WRITE_LE_UINT16(noiseData + 20, 1);       // PCM format
+	WRITE_LE_UINT16(noiseData + 22, 1);       // mono
+	WRITE_LE_UINT32(noiseData + 24, 8000);    // sample rate
+	WRITE_LE_UINT32(noiseData + 28, 8000);    // byte rate
+	WRITE_LE_UINT16(noiseData + 32, 1);       // block align
+	WRITE_LE_UINT16(noiseData + 34, 8);       // bits per sample
+	memcpy(noiseData + 36, "data", 4);
+	WRITE_LE_UINT32(noiseData + 40, kNoiseLength);
+
+	// Fill with random noise (simulating garbage bytes written to port 0x61)
+	byte curNoise = (byte)getRandomNumber(255);
+	for (int i = 0; i < kNoiseLength; i++) {
+		noiseData[44 + i] = curNoise;
+		bool changeNoise = getRandomNumber(10) < 2; // 20% chance to change noise value
+		if (changeNoise) {
+			curNoise = (byte)getRandomNumber(255);
+		}
+	}
+
+	// Play the noise
+	_sound->playSound(noiseData, kNoiseLength + 44, 200);
+
+	byte *screenPixels = (byte *)_screen->getPixels();
+	int screenSize = _screen->pitch * _screen->h;
+
+	// Clear any pending key event before starting the loop
+	_events->_lastKeyEvent = Common::KEYCODE_INVALID;
+
+	while (!shouldQuit()) {
+		_events->pollEvent();
+
+		if (_events->_lastKeyEvent != Common::KEYCODE_INVALID) {
+			break;
+		}
+
+		//generate random pixels on the screen (simulating corrupted video memory)
+		for (int i = 0; i < screenSize; i++) {
+			screenPixels[i] = (byte)getRandomNumber(255);
+		}
+
+		// Regenerate noise periodically
+		for (int i = 0; i < kNoiseLength; i++) {
+			noiseData[44 + i] = curNoise;
+			bool changeNoise = getRandomNumber(10) < 2; // 20% chance to change noise value
+			if (changeNoise) {
+				curNoise = (byte)getRandomNumber(255);
+			}
+		}
+		if (!_sound->isPlaying()) {
+			_sound->playSound(noiseData, kNoiseLength + 44, 200);
+		}
+
+		_screen->markAllDirty();
+		_screen->update();
+		g_system->delayMillis(50);
+	}
+
+	_sound->stopAllSounds();
+	delete[] noiseData;
+
+	// Return to launcher
+	Engine::quitGame();
+}
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index a9e9e2b1390..edff6cb44e3 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -105,6 +105,44 @@ void GraphicsManager::fadeToBlack() {
 	}
 }
 
+/**
+ * Fades between two palettes by incrementally changing the current palette towards the target palette.
+ */
+void GraphicsManager::fadePaletteToTarget(byte *targetPalette, int stepSize) {
+	byte currentPalette[768];
+	memcpy(currentPalette, g_engine->_room->_roomPalette, 768);
+
+	while (!g_engine->shouldQuit()) {
+		g_engine->_events->pollEvent();
+
+		bool didRender = g_engine->renderScene(OVERLAY_NONE);
+		if (didRender) {
+			bool changed = false;
+
+			for (int i = 0; i < 768; i++) {
+				if (currentPalette[i] < targetPalette[i]) {
+					currentPalette[i] = MIN((int)currentPalette[i] + stepSize, (int)targetPalette[i]);
+					changed = true;
+				} else if (currentPalette[i] > targetPalette[i]) {
+					currentPalette[i] = MAX((int)currentPalette[i] - stepSize, (int)targetPalette[i]);
+					changed = true;
+				}
+			}
+
+			if (!changed)
+				break;
+
+			g_system->getPaletteManager()->setPalette(currentPalette, 0, 256);
+		}
+
+		g_engine->_screen->update();
+		g_system->delayMillis(10);
+	}
+
+	memcpy(g_engine->_room->_roomPalette, targetPalette, 768);
+	g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
+}
+
 void GraphicsManager::clearScreen() {
 	memset(g_engine->_screen->getPixels(), 0, g_engine->_screen->pitch * g_engine->_screen->h);
 }
diff --git a/engines/pelrock/graphics.h b/engines/pelrock/graphics.h
index a55e7749d73..8965a6b8464 100644
--- a/engines/pelrock/graphics.h
+++ b/engines/pelrock/graphics.h
@@ -35,6 +35,7 @@ public:
 	byte *grabBackgroundSlice(byte *buf, int x, int y, int w, int h);
 	void putBackgroundSlice(byte *buf, int x, int y, int w, int h, byte *slice);
 	void fadeToBlack();
+	void fadePaletteToTarget(byte *targetPalette, int stepSize);
 	void clearScreen();
 };
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index f971194c1d8..8238221944d 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1460,9 +1460,15 @@ void PelrockEngine::gameLoop() {
 	checkMouse();
 	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_m) {
 		travelToEgypt();
+		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
 	}
 	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_n) {
 		loadExtraScreenAndPresent(10);
+		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
+	}
+	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_p) {
+		antiPiracyEffect();
+		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
 	}
 	renderScene();
 	// _events->waitForKey();
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index b470c431f24..bb681c79bd9 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -318,6 +318,8 @@ public:
 	void closeMcDoor(HotSpot *hotspot);
 
 	void animateStatuePaletteFade(bool reverse = false);
+	void pickUpRoom28Object(HotSpot *hotspot);
+	void antiPiracyEffect();
 	void checkObjectsForPart2();
 	void waitForActionEnd();
 };
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 16fc12d2b86..1cf969b758f 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -49,6 +49,7 @@ static const int unpickableHotspotExtras[] = {
 	360,  // library shelves
 	361,
 	362,
+	472, // matches
 };
 
 


Commit: 99d7edabea19ba21dfc289b6a141f0cd133cc914
    https://github.com/scummvm/scummvm/commit/99d7edabea19ba21dfc289b6a141f0cd133cc914
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:00+02:00

Commit Message:
PELROCK: Room 23

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 7bf4000ad0b..a2ca82dc2da 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -114,7 +114,7 @@ const ActionEntry actionTable[] = {
 	{400, CLOSE, &PelrockEngine::closeTravelAgencyDoor},
 
 	// Room 28
-	{472, PICKUP, &PelrockEngine::pickUpRoom28Object},
+	{472, PICKUP, &PelrockEngine::pickUpMatches},
 
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
@@ -358,16 +358,26 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_dialog->say(_res->_ingameTexts[MEJORMELARGO], 1);
 		break;
 		// end moros
-		//puta 2
-	case 352:
-		break;
 	case 353:
+		_state->setRootDisabledState(room, rootIndex, true);
+		_state->setRootDisabledState(room, rootIndex + 1, true);
 		break;
 	case 354:
+		if(_state->hasInventoryItem(105)) {
+			addInventoryItem(105);
+		}
 		break;
+	case 352:
 	case 355:
+		_graphics->fadeToBlack();
+		_alfredState.x = 342;
+		_alfredState.y = 277;
+		setScreen(31, ALFRED_DOWN);
 		break;
 	case 356:
+		_state->setRootDisabledState(room, 0, true);
+		_state->setRootDisabledState(room, 1, true);
+		_state->setRootDisabledState(room, 2, true);
 		break;
 	//end puta
 	default:
@@ -1091,7 +1101,7 @@ void PelrockEngine::waitForActionEnd() {
  *
  * Original handler at Ghidra address 0x1FED8.
  */
-void PelrockEngine::pickUpRoom28Object(HotSpot *hotspot) {
+void PelrockEngine::pickUpMatches(HotSpot *hotspot) {
 	// Load the special palette from ALFRED.7 at offset 0x1610CE
 	static const uint32 kRoom28PaletteOffset = 0x1610CE;
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 8238221944d..85304337777 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1079,7 +1079,8 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	if (shadowLevel != 0xFF && shadowLevel < 4) {
 		for (int i = 0; i < finalWidth * finalHeight; i++) {
 			if (_alfredSprite[i] != 255) {
-				_alfredSprite[i] = _room->_paletteRemaps[3 - shadowLevel][_alfredSprite[i]];
+				// _alfredSprite[i] = _room->_paletteRemaps[3 - shadowLevel][_alfredSprite[i]];
+				_alfredSprite[i] = _room->_paletteRemaps[0][_alfredSprite[i]];
 			}
 		}
 	}
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index bb681c79bd9..482266a09d9 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -318,7 +318,7 @@ public:
 	void closeMcDoor(HotSpot *hotspot);
 
 	void animateStatuePaletteFade(bool reverse = false);
-	void pickUpRoom28Object(HotSpot *hotspot);
+	void pickUpMatches(HotSpot *hotspot);
 	void antiPiracyEffect();
 	void checkObjectsForPart2();
 	void waitForActionEnd();


Commit: 1c366f311fc5d82c80e853d322e0e2702717c9c9
    https://github.com/scummvm/scummvm/commit/1c366f311fc5d82c80e853d322e0e2702717c9c9
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:00+02:00

Commit Message:
PELROCK: Implements crocodile animation

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/graphics.cpp
    engines/pelrock/graphics.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index a2ca82dc2da..5bbb5010e5a 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -135,10 +135,13 @@ const CombinationEntry combinationTable[] = {
 	{4, 294, &PelrockEngine::useBrickWithWindow},
 	{4, 295, &PelrockEngine::useBrickWithShopWindow},
 	{6, 315, &PelrockEngine::useCordWithPlug},
-	{1, 53, &PelrockEngine::giveIdToGuard},
-	{5, 53, &PelrockEngine::giveMoneyToGuard},
+	{1, 309, &PelrockEngine::giveIdToGuard},
+	{5, 309, &PelrockEngine::giveMoneyToGuard},
 	{7, 353, &PelrockEngine::useAmuletWithStatue},
-	{8, 102, &PelrockEngine::giveSecretCodeToLibrarian},
+	{8, 353, &PelrockEngine::useSecretCodeWithStatue},
+	{8, 358, &PelrockEngine::giveSecretCodeToLibrarian},
+	{4, 358, &PelrockEngine::useBrickWithLibrarian}, // Any hotspot in the lamppost will work
+	{76, 469, &PelrockEngine::usePumpkinWithRiver},
 	// End marker
 	{WILDCARD, WILDCARD, nullptr}};
 
@@ -212,7 +215,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_state->setRootDisabledState(room, rootIndex, true);
 		break;
 	case 329:
-		debug("Would now enable X easter egg");
+		_state->setFlag(FLAG_PUTA_250_VECES, true);
 		break;
 	case 258:
 		_state->setFlag(FLAG_GUARDIA_PIDECOSAS, true);
@@ -280,6 +283,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 317:
 		addInventoryItem(95);
 		break;
+
 	case 330:
 		// Two oranges
 		addInventoryItem(103);
@@ -369,7 +373,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		break;
 	case 352:
 	case 355:
-		_graphics->fadeToBlack();
+		_graphics->fadeToBlack(10);
 		_alfredState.x = 342;
 		_alfredState.y = 277;
 		setScreen(31, ALFRED_DOWN);
@@ -550,20 +554,20 @@ void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
 	// TODO: Animate sprite 8 (brick projectile) moving to window
 	Sprite *brickSprite = _room->findSpriteByIndex(7);
 	HotSpot *windowHotspot = _room->findHotspotByExtra(294);
-	brickSprite->x = _alfredState.x - brickSprite->w / 2;
-	brickSprite->y = _alfredState.y - kAlfredFrameHeight;
-	brickSprite->zOrder = 20; // Make it visible
+	brickSprite->x = 420;
+	brickSprite->y = 241;
+	brickSprite->zOrder = 10; // Make it visible
 	int target = windowHotspot->y + windowHotspot->h / 2;
 	while (!shouldQuit()) {
 		_events->pollEvent();
 		renderScene(OVERLAY_NONE);
-		if (_chrono->_gameTick) {
-			_room->findSpriteByIndex(7)->y -= 40;
-			if (_room->findSpriteByIndex(7)->y < target) {
+		// if (_chrono->_gameTick) {
+			_room->findSpriteByIndex(7)->y -= 10;
+			if (_room->findSpriteByIndex(7)->y <= 70) {
 				_room->findSpriteByIndex(7)->zOrder = -1;
 				break;
 			}
-		}
+		// }
 		_screen->update();
 		g_system->delayMillis(10);
 	}
@@ -590,7 +594,7 @@ void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
 	_room->addStickerToRoom(_room->_currentRoomNumber, 10, PERSIST_PERM);
 	_room->disableHotspot(_room->findHotspotByExtra(295)); // Disable storefront hotspot
 	_room->disableHotspot(_room->findHotspotByExtra(294)); // Disable window hotspot
-	walkTo(639, _alfredState.y);
+	walkTo(630, _alfredState.y);
 }
 
 void PelrockEngine::moveCable(HotSpot *hotspot) {
@@ -771,6 +775,10 @@ void PelrockEngine::useAmuletWithStatue(int inventoryObject, HotSpot *hotspot) {
 	}
 }
 
+void PelrockEngine::useSecretCodeWithStatue(int inventoryObject, HotSpot *hotspot) {
+	_dialog->say(_res->_ingameTexts[NOESAMIAQUIENDEBES], 1);
+}
+
 void PelrockEngine::pickUpLetter(HotSpot *hotspot) {
 	addInventoryItem(9);
 	_room->setActionMask(hotspot, ACTION_MASK_NONE); // Disable hotspot
@@ -810,6 +818,10 @@ void PelrockEngine::giveSecretCodeToLibrarian(int inventoryObject, HotSpot *hots
 	addInventoryItem(59);
 }
 
+void PelrockEngine::useBrickWithLibrarian(int inventoryObject, HotSpot *hotspot) {
+	_dialog->say(_res->_ingameTexts[YSI_METIRA_MAQUINA]);
+}
+
 void PelrockEngine::openNewspaperDoor(HotSpot *hotspot) {
 	openDoor(hotspot, 2, 50, MASCULINE, false);
 }
@@ -838,6 +850,32 @@ void PelrockEngine::closeTravelAgencyDoor(HotSpot *hotspot) {
 	closeDoor(hotspot, 1, 57, FEMININE, false);
 }
 
+void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
+	_sound->playMusicTrack(27);
+	_dialog->say(_res->_ingameTexts[PRIMERINGREDIENTE]);
+	_dialog->say(_res->_ingameTexts[CUIDADOIMPRUDENTE]);
+	_res->loadAlfredSpecialAnim(5);
+	_alfredState.animState = ALFRED_SPECIAL_ANIM;
+	_alfredState.x -= 10;
+	_alfredState.y += 20;
+	waitForSpecialAnimation();
+	_sound->playSound(_room->_roomSfx[0], 100, 0); // Belch
+	bool isPlaying = true;
+	while (!shouldQuit() && isPlaying) {
+		_events->pollEvent();
+		isPlaying = _sound->isPlaying(0);
+		_screen->update();
+		g_system->delayMillis(10);
+	}
+	_graphics->fadeToBlack(10);
+	//update conversaton state
+	_alfredState.x = 300;
+	_alfredState.y = 238;
+	setScreen(28, ALFRED_DOWN);
+	_dialog->say(_res->_ingameTexts[QUEOSCUROESTAESTO]);
+
+}
+
 void PelrockEngine::pickUpBook(int i) {
 	if (!_state->hasInventoryItem(10)) {
 		_dialog->say(_res->_ingameTexts[VENGA_ACA]);
@@ -916,6 +954,12 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 
 	debug("Using item %d on Alfred", inventoryObject);
 	switch (inventoryObject) {
+	case 9: // carta
+		_dialog->say(_res->_ingameTexts[CORRESPONDENCIA_AJENA]);
+		break;
+	case 34: // Como hacerse rico...
+		_dialog->say(_res->_ingameTexts[PERIODICOSENSACIONALISTA], 1);
+		break;
 	case 63: // Recipe
 		_res->loadAlfredSpecialAnim(1);
 		_alfredState.animState = ALFRED_SPECIAL_ANIM;
diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index edff6cb44e3..bda96e13442 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -72,7 +72,7 @@ void GraphicsManager::putBackgroundSlice(byte *buf, int x, int y, int w, int h,
 	}
 }
 
-void GraphicsManager::fadeToBlack() {
+void GraphicsManager::fadeToBlack(int stepSize) {
 	byte palette[768];
 	g_system->getPaletteManager()->grabPalette(palette, 0, 256);
 	while (!g_engine->shouldQuit()) {
@@ -82,7 +82,7 @@ void GraphicsManager::fadeToBlack() {
 
 			for (int i = 0; i < 768; i++) {
 				if (palette[i] > 0) {
-					palette[i] = MAX(palette[i] / 2, 0);
+					palette[i] = MAX(palette[i] - stepSize, 0);
 				}
 			}
 			g_system->getPaletteManager()->setPalette(palette, 0, 256);
diff --git a/engines/pelrock/graphics.h b/engines/pelrock/graphics.h
index 8965a6b8464..be2230e10a9 100644
--- a/engines/pelrock/graphics.h
+++ b/engines/pelrock/graphics.h
@@ -34,7 +34,7 @@ public:
 	Common::Point showOverlay(int height, byte *buf);
 	byte *grabBackgroundSlice(byte *buf, int x, int y, int w, int h);
 	void putBackgroundSlice(byte *buf, int x, int y, int w, int h, byte *slice);
-	void fadeToBlack();
+	void fadeToBlack(int stepSize);
 	void fadePaletteToTarget(byte *targetPalette, int stepSize);
 	void clearScreen();
 };
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 85304337777..28cb289d895 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -155,8 +155,8 @@ void PelrockEngine::init() {
 		loadAnims();
 		// setScreen(0, ALFRED_DOWN);
 		// setScreen(3, ALFRED_RIGHT);
-		setScreen(22, ALFRED_DOWN);
-		// setScreen(9, ALFRED_DOWN);
+		// setScreen(22, ALFRED_DOWN);
+		setScreen(9, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
@@ -235,7 +235,7 @@ void PelrockEngine::playSoundIfNeeded() {
 }
 
 void PelrockEngine::travelToEgypt() {
-	_graphics->fadeToBlack();
+	_graphics->fadeToBlack(10);
 	_sound->playMusicTrack(26, false);
 	byte *palette = new byte[768];
 	if (_extraScreen == nullptr) {
@@ -304,6 +304,23 @@ bool PelrockEngine::renderScene(int overlayMode) {
 		_screen->markAllDirty();
 		return true;
 	}
+
+	switch (_room->_currentRoomNumber) {
+	case 2: {
+		if (_events->_lastKeyEvent == Common::KEYCODE_x) {
+			_events->_lastKeyEvent = Common::KEYCODE_INVALID;
+			debug("Pressed X in room 2, numPressedX is now %d, flag is %d", _numPressedX + 1, _state->getFlag(FLAG_PUTA_250_VECES));
+			if (_state->getFlag(FLAG_PUTA_250_VECES) == true) {
+				_numPressedX++;
+				if (_numPressedX == 250) {
+					_dialog->say(_res->_ingameTexts[YLOSCONDONES]);
+				}
+			}
+		}
+		break;
+	}
+	}
+
 	return false;
 }
 
@@ -640,7 +657,6 @@ void PelrockEngine::placeStickersFirstPass() {
 void PelrockEngine::placeStickersSecondPass() {
 	// Some stickers need to be placed AFTER sprites, hardcoded in the original
 	if (_room->_currentRoomNumber == 3) {
-		debug("Placing second-pass stickers for room 3");
 		for (uint i = 0; i < _state->stickersPerRoom[3].size(); i++) {
 			if (_state->stickersPerRoom[3][i].stickerIndex == 14) {
 				placeSticker(_state->stickersPerRoom[3][i]);
@@ -1576,15 +1592,15 @@ AlfredDirection PelrockEngine::calculateAlfredsDirection(HotSpot *hotspot) {
 }
 
 VerbIcon PelrockEngine::isActionUnder(int x, int y) {
-	if (_currentHotspot == nullptr) {
+	/*if (_currentHotspot == nullptr) {
 		return NO_ACTION;
-	}
+	}*/
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
 	int loopEnd = _state->selectedInventoryItem != -1 ? actions.size() + 1 : actions.size();
 	for (int i = 0; i < loopEnd; i++) {
 		Common::Point p = getPositionInBallonForIndex(i, _actionPopupState.x, _actionPopupState.y);
 		Common::Rect actionRect = Common::Rect(p.x, p.y, p.x + kVerbIconWidth, p.y + kVerbIconHeight);
-
+		debug("Checking action %d at rect (%d,%d) to (%d,%d) against mouse position %d,%d", i, actionRect.left, actionRect.top, actionRect.right, actionRect.bottom, x, y);
 		if (i == actions.size()) {
 			// Check inventory item
 			if (actionRect.contains(x, y)) {
@@ -1594,6 +1610,7 @@ VerbIcon PelrockEngine::isActionUnder(int x, int y) {
 			return actions[i];
 		}
 	}
+	debug("No action under mouse at position %d,%d", x, y);
 	return NO_ACTION;
 }
 
@@ -1668,6 +1685,7 @@ void PelrockEngine::checkMouseHover() {
 
 	if (isActionUnder(_events->_mouseX, _events->_mouseY) != NO_ACTION) {
 		hotspotDetected = false;
+		debug("Mouse is over action icon, not hotspot, so showing action cursor");
 	}
 
 	// Calculate walk target first (before checking anything else)
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 482266a09d9..90560b89ba0 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -149,6 +149,8 @@ private:
 	Common::String _hoveredMapLocation = "";
 	byte *_alfredSprite = nullptr;
 
+	int _numPressedX = 0;
+
 	// int prevDirX = 0;
 	// int prevDirY = 0;
 	// Common::String objectToShow = "";
@@ -298,6 +300,7 @@ public:
 	void giveMoneyToGuard(int inventoryObject, HotSpot *hotspot);
 	void openMuseumDoor(HotSpot *hotspot);
 	void useAmuletWithStatue(int inventoryObject, HotSpot *hotspot);
+	void useSecretCodeWithStatue(int inventoryObject, HotSpot *hotspot);
 	void pickUpLetter(HotSpot *hotspot);
 	void openLibraryOutdoorsDoor(HotSpot *hotspot);
 	void closeLibraryOutdoorsDoor(HotSpot *hotspot);
@@ -307,12 +310,14 @@ public:
 	void pickBooksFromShelf2(HotSpot *hotspot);
 	void pickBooksFromShelf3(HotSpot *hotspot);
 	void giveSecretCodeToLibrarian(int inventoryObject, HotSpot *hotspot);
+	void useBrickWithLibrarian(int inventoryObject, HotSpot *hotspot);
 	void openNewspaperDoor(HotSpot *hotspot);
 	void closeNewspaperDoor(HotSpot *hotspot);
 	void openNewspaperBossDor(HotSpot *hotspot);
 	void closeNewspaperBossDoor(HotSpot *hotspot);
 	void openTravelAgencyDoor(HotSpot *hotspot);
 	void closeTravelAgencyDoor(HotSpot *hotspot);
+	void usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot);
 	void pickUpBook(int i);
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index bc17308f07e..f7fa6e94803 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -42,6 +42,7 @@ const AlfredSpecialAnimOffset ResourceManager::alfredSpecialAnims[] = {
 	{3, 45, 87, 0, 7, 37000, 1}, // ELECTRIC SHOCK 1
 	{2, 82, 58, 0, 7, 53106, 20}, // ELECTRIC SHOCK 3
 	{3, 71, 110, 1, 2, 20724, 1, 62480}, // Throw
+	{14, 171, 107, 1, 7, 1556540, 1} , //crocodile
 };
 
 ResourceManager::~ResourceManager() {
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 2433cf5e920..495c43fce1e 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -894,8 +894,8 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 		sprite.stride = READ_LE_INT16(data + animOffset + 6);
 		sprite.numAnims = data[animOffset + 8];
 		sprite.zOrder = data[animOffset + 23];
-		sprite.extra = data[animOffset + 32];
-		sprite.spriteType = data[animOffset + 33];
+		sprite.extra = READ_LE_INT16(data + animOffset + 32);
+		debug("Sprite %d: x=%d y=%d w=%d h=%d stride=%d numAnims=%d zOrder=%d extra=%d", i, sprite.x, sprite.y, sprite.w, sprite.h, sprite.stride, sprite.numAnims, sprite.zOrder, sprite.extra);
 		sprite.actionFlags = data[animOffset + 34];
 		sprite.isHotspotDisabled = data[animOffset + 38];
 		for (int j = 0; j < spriteChanges.size(); j++) {
@@ -908,7 +908,6 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 			break;
 		}
 		sprite.animData = new Anim[sprite.numAnims];
-		// debug("Sprite %d has %d sub-anims, type %d, actionFlags %d, isDisabled? %d", i, sprite.numAnims, sprite.spriteType, sprite.actionFlags, sprite.isHotspotDisabled);
 		int subAnimOffset = animOffset + 10;
 		for (int j = 0; j < sprite.numAnims; j++) {
 
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 4738fe93744..3b4e32d4331 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -108,7 +108,7 @@ void SoundManager::playSound(SonidoFile sound, int volume, int channel) {
 				_mixer->stopHandle(_sfxHandles[channel]);
 			}
 		}
-		// _mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, volume, 0, DisposeAfterUse::YES);
+		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, volume, 0, DisposeAfterUse::YES);
 	}
 }
 
@@ -213,7 +213,7 @@ void SoundManager::playMusicTrack(int trackNumber, bool loop) {
 	}
 	_currentMusicTrack = trackNumber;
 	g_system->getAudioCDManager()->stop();
-	// g_system->getAudioCDManager()->play(trackNumber, loop ? -1 : 0, 0, 0);
+	g_system->getAudioCDManager()->play(trackNumber, loop ? -1 : 0, 0, 0);
 }
 
 void SoundManager::loadSoundIndex() {
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 87dd315c631..2028f549702 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -231,13 +231,13 @@ struct Sprite {
 	uint16 stride; // 6-7
 	int numAnims;  // 8
 	int curAnimIndex = 0;
-	int8 zOrder;
-	byte spriteType;        // 33
+	int8 zOrder; //32-33
+
 	byte actionFlags;       // 34
 	bool isHotspotDisabled; // 38
 	bool isTalking = false;
 	Anim *animData;
-	byte extra;
+	int16 extra;
 };
 
 struct HotSpot {
@@ -506,8 +506,9 @@ struct ResetEntry {
 #define FLAG_GUARDIA_DNI_ENTREGADO 50
 #define FLAG_AGENCIA_ABIERTA 51
 #define FLAG_CONSIGNAS_VENDEDOR 52
+#define FLAG_PUTA_250_VECES 53
 
-const int kNumGameFlags = 53;
+const int kNumGameFlags = 54;
 
 struct GameStateData {
 	byte flags[kNumGameFlags];


Commit: 186db8d250dcd87da683c07a129ebb264c0709b1
    https://github.com/scummvm/scummvm/commit/186db8d250dcd87da683c07a129ebb264c0709b1
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:00+02:00

Commit Message:
PELROCK: Sfx in menu

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 5bbb5010e5a..c5832e1c4d6 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -113,9 +113,13 @@ const ActionEntry actionTable[] = {
 	{400, OPEN, &PelrockEngine::openTravelAgencyDoor},
 	{400, CLOSE, &PelrockEngine::closeTravelAgencyDoor},
 
+	// Room 25
+	{609, PICKUP, &PelrockEngine::pickupSunflower},
+
 	// Room 28
 	{472, PICKUP, &PelrockEngine::pickUpMatches},
 
+
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
 	{WILDCARD, TALK, &PelrockEngine::noOpAction},   // Generic talk action
@@ -131,7 +135,7 @@ const ActionEntry actionTable[] = {
 
 const CombinationEntry combinationTable[] = {
 	{2, 281, &PelrockEngine::useCardWithATM},
-	{62, 117, &PelrockEngine::useSpicySauceWithBurger},
+	{62, 373, &PelrockEngine::useSpicySauceWithBurger},
 	{4, 294, &PelrockEngine::useBrickWithWindow},
 	{4, 295, &PelrockEngine::useBrickWithShopWindow},
 	{6, 315, &PelrockEngine::useCordWithPlug},
@@ -373,10 +377,8 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		break;
 	case 352:
 	case 355:
-		_graphics->fadeToBlack(10);
-		_alfredState.x = 342;
-		_alfredState.y = 277;
-		setScreen(31, ALFRED_DOWN);
+		//a la carcel
+		toJail();
 		break;
 	case 356:
 		_state->setRootDisabledState(room, 0, true);
@@ -384,12 +386,61 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_state->setRootDisabledState(room, 2, true);
 		break;
 	//end puta
+	// sabio
+	case 366:
+		_state->setRootDisabledState(room, rootIndex, true);
+		break;
+	case 363:
+		toJail();
+		break;
+	case 367: //accept riddle
+		_state->setRootDisabledState(room, 26, true);
+		walkAndAction(_room->findHotspotByExtra(467), TALK);
+		break;
+// hasta aqui
+
+	case 357: // mal
+		_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, _state->getFlag(FLAG_RESPUESTAS_ACERTADAS) - 1);
+		_state->setRootDisabledState(room, rootIndex, true);
+		break;
+	case 358: // muy mal
+		_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, _state->getFlag(FLAG_RESPUESTAS_ACERTADAS) - 2);
+		_state->setRootDisabledState(room, rootIndex, true);
+		break;
+	case 359: // bien
+		_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, _state->getFlag(FLAG_RESPUESTAS_ACERTADAS) + 1);
+		if(_state->getFlag(FLAG_RESPUESTAS_ACERTADAS) == 15) {
+			addInventoryItem(106); // pin
+			_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, 0);
+
+		}
+		_state->setRootDisabledState(room, rootIndex, true);
+		break;
+	case 360:
+
+	case 361:
+		_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, 0);
+		//back to conversation 27
+	case 362:
+//?
+	case 365:
+		//correct
+	case 364:
+		//wrong
+
 	default:
 		debug("Got actionTrigger %d in dialogActionTrigger, but no handler defined", actionTrigger);
 		break;
 	}
 }
 
+void PelrockEngine::toJail() {
+	_graphics->fadeToBlack(10);
+	_alfredState.x = 342;
+	_alfredState.y = 277;
+	setScreen(31, ALFRED_DOWN);
+}
+
 void PelrockEngine::noOpAction(HotSpot *hotspot) {
 }
 
@@ -521,6 +572,7 @@ void PelrockEngine::openKitchenDoorFromInside(HotSpot *hotspot) {
 
 void PelrockEngine::useSpicySauceWithBurger(int inventoryObject, HotSpot *hotspot) {
 	_state->setFlag(FLAG_PUESTA_SALSA_PICANTE, true);
+	_sound->playSound(_room->_roomSfx[2]);
 	_dialog->say(_res->_ingameTexts[VAESTAR_POCOFUERTE]);
 }
 
@@ -873,9 +925,23 @@ void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
 	_alfredState.y = 238;
 	setScreen(28, ALFRED_DOWN);
 	_dialog->say(_res->_ingameTexts[QUEOSCUROESTAESTO]);
+}
 
+void PelrockEngine::pickupSunflower(HotSpot *hotspot) {
+	if(_state->getFlag(FLAG_PARADOJA_RESUELTA) == false) {
+		_dialog->say(_res->_ingameTexts[OIGA]);
+		for(int i = 0; i < 26; i++) {
+			_state->setRootDisabledState(25, i, true);
+		}
+
+		walkAndAction(_room->findHotspotByExtra(467), TALK);
+	}
+	else {
+
+	}
 }
 
+
 void PelrockEngine::pickUpBook(int i) {
 	if (!_state->hasInventoryItem(10)) {
 		_dialog->say(_res->_ingameTexts[VENGA_ACA]);
@@ -1138,6 +1204,7 @@ void PelrockEngine::waitForActionEnd() {
 	}
 }
 
+
 /**
  * Handler for picking up object with extra_id 472 in Room 28.
  * Loads a special palette from ALFRED.7 at offset 0x1610CE and
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 9c3c1a9a1fb..69449dcc1f4 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -31,7 +31,7 @@
 
 namespace Pelrock {
 
-Pelrock::MenuManager::MenuManager(Graphics::Screen *screen, PelrockEventManager *events, ResourceManager *res) : _screen(screen), _events(events), _res(res) {
+Pelrock::MenuManager::MenuManager(Graphics::Screen *screen, PelrockEventManager *events, ResourceManager *res, SoundManager *sound) : _screen(screen), _events(events), _res(res), _sound(sound) {
 }
 
 void MenuManager::drawColoredText(Graphics::ManagedSurface *screen, const Common::String &text, int x, int y, int w, Graphics::Font *font) {
@@ -97,11 +97,7 @@ void MenuManager::checkMouseClick(int x, int y) {
 	for (int i = 0; i < 4; i++) {
 		if (x >= 140 + (82 * i) && x <= 140 + (82 * i) + 64 &&
 			y >= 115 - (8 * i) && y <= 115 - (8 * i) + 64) {
-			_selectedInvIndex = g_engine->_state->inventoryItems[_curInventoryPage * 4 + i];
-			_menuText = _inventoryDescriptions[_selectedInvIndex];
-			g_engine->_state->selectedInventoryItem = _selectedInvIndex;
-			selectedItem = true;
-			debug("Selected inventory item %d", _selectedInvIndex);
+			selectedItem = selectInventoryItem(i);
 			return;
 		}
 	}
@@ -134,6 +130,18 @@ void MenuManager::checkMouseClick(int x, int y) {
 	}
 }
 
+bool MenuManager::selectInventoryItem(int i) {
+	if (_curInventoryPage * 4 + i >= g_engine->_state->inventoryItems.size())
+		return false;
+
+	_selectedInvIndex = g_engine->_state->inventoryItems[_curInventoryPage * 4 + i];
+	_menuText = _inventoryDescriptions[_selectedInvIndex];
+	_sound->playSound(inventorySounds[_selectedInvIndex], 100, 0);
+	g_engine->_state->selectedInventoryItem = _selectedInvIndex;
+	debug("Selected inventory item %d", _selectedInvIndex);
+	return true;
+}
+
 void MenuManager::menuLoop() {
 
 	g_system->getPaletteManager()->setPalette(_mainMenuPalette, 0, 256);
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 32b0b14505d..781b721715d 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -26,6 +26,7 @@
 
 #include "pelrock/events.h"
 #include "pelrock/resources.h"
+#include "pelrock/sound.h"
 
 namespace Pelrock {
 
@@ -46,9 +47,126 @@ enum MenuButton {
 	NO_BUTTON
 };
 
+static const char *inventorySounds[113] = {
+
+	"HOJASZZZ.SMP", // 0 - Default leaf rustle
+	"11ZZZZZZ.SMP", // 1 -
+	"11ZZZZZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"GLASS1ZZ.SMP", // 4 - Glass clink (brick)
+	"11ZZZZZZ.SMP",
+	"ELEC3ZZZ.SMP", // 6 - Electric zap
+	"REMATERL.SMP", // 7 - Rematerialize
+	"81ZZZZZZ.SMP", // 8 - (numbered SFX)
+	"HOJASZZZ.SMP",
+	"SSSHTZZZ.SMP", // 10 - Shushing
+	"HOJASZZZ.SMP", // 11 - Default leaf rustle
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP", // 20
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP", // 30
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP", // 40
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP", // 50
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"BOTEZZZZ.SMP", // 60
+	"BOTEZZZZ.SMP", // 61
+	"BOTEZZZZ.SMP", // 62 - Bottle sound
+	"BELCHZZZ.SMP", // 63 - Belch
+	"BEAMZZZZ.SMP", // 64 - Beam/ray
+	"ELVIS1ZZ.SMP", // 65 - Elvis impression
+	"CAT_1ZZZ.SMP", // 66 - Cat sound
+	"BOOOOOIZ.SMP", // 67 - Boing
+	"DISCOSZZ.SMP", // 68 - Disco music
+	"MONORLZZ.SMP", // 69 - Monorail
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"CARACOLA.SMP", // 73 - Seashell
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"WATER_2Z.SMP", // 76 - Water splash
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"EEEEKZZZ.SMP", // 79 - Shriek
+	"REMATERL.SMP", // 80 - Rematerialize
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"ELVIS1ZZ.SMP", // 83 - Elvis impression
+	"RIMSHOTZ.SMP", // 84 - Rimshot
+	"HOJASZZZ.SMP",
+	"WATER_2Z.SMP", // 86 - Water splash
+	"MOTOSZZZ.SMP", // 87 - Motorcycle
+	"HOJASZZZ.SMP",
+	"TWANGZZZ.SMP", // 89 - Twang
+	"HOJASZZZ.SMP",
+	"QUAKE2ZZ.SMP", // 91 - Earthquake
+	"HOJASZZZ.SMP",
+	"SORBOZZZ.SMP", // 93 - Slurp
+	"BOTEZZZZ.SMP", // 94 - Bottle sound
+	"ELVIS1ZZ.SMP", // 95 - Elvis impression
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"LLAVESZZ.SMP", // 100 - Keys jingling
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"EVLLAUGH.SMP", // 104 - Evil laugh
+	"HOJASZZZ.SMP",
+	"BURROLZZ.SMP", // 106 - Donkey bray
+	"HOJASZZZ.SMP",
+	"TWANGZZZ.SMP", // 108
+	"HOJASZZZ.SMP",
+	"TWANGZZZ.SMP", // 110
+	"ELVIS1ZZ.SMP", // 111 - Elvis impression
+	"SEX3ZZZZ.SMP"  // 112 - Suggestive sound
+};
+
 class MenuManager {
 public:
-	MenuManager(Graphics::Screen *screen, PelrockEventManager *events, ResourceManager *res);
+	MenuManager(Graphics::Screen *screen, PelrockEventManager *events, ResourceManager *res, SoundManager *sound);
 	~MenuManager();
 	void menuLoop();
 	void drawScreen();
@@ -58,6 +176,7 @@ public:
 
 private:
 	void checkMouseClick(int x, int y);
+	bool selectInventoryItem(int i);
 	void loadMenuTexts();
 	void cleanUp();
 	void drawButtons();
@@ -67,6 +186,7 @@ private:
 	Graphics::Screen *_screen = nullptr;
 	PelrockEventManager *_events = nullptr;
 	ResourceManager *_res = nullptr;
+	SoundManager *_sound = nullptr;
 	byte *_mainMenu = nullptr;
 	byte *_compositeBuffer = nullptr;
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 28cb289d895..3cb01f23df7 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -95,7 +95,7 @@ Common::Error PelrockEngine::run() {
 	_res = new ResourceManager();
 	_sound = new SoundManager(_mixer);
 	_dialog = new DialogManager(_screen, _events, _graphics);
-	_menu = new MenuManager(_screen, _events, _res);
+	_menu = new MenuManager(_screen, _events, _res, _sound);
 	_smallFont = new SmallFont();
 	_smallFont->load("ALFRED.4");
 	_largeFont = new LargeFont();
@@ -156,7 +156,7 @@ void PelrockEngine::init() {
 		// setScreen(0, ALFRED_DOWN);
 		// setScreen(3, ALFRED_RIGHT);
 		// setScreen(22, ALFRED_DOWN);
-		setScreen(9, ALFRED_DOWN);
+		setScreen(25, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
@@ -1600,7 +1600,6 @@ VerbIcon PelrockEngine::isActionUnder(int x, int y) {
 	for (int i = 0; i < loopEnd; i++) {
 		Common::Point p = getPositionInBallonForIndex(i, _actionPopupState.x, _actionPopupState.y);
 		Common::Rect actionRect = Common::Rect(p.x, p.y, p.x + kVerbIconWidth, p.y + kVerbIconHeight);
-		debug("Checking action %d at rect (%d,%d) to (%d,%d) against mouse position %d,%d", i, actionRect.left, actionRect.top, actionRect.right, actionRect.bottom, x, y);
 		if (i == actions.size()) {
 			// Check inventory item
 			if (actionRect.contains(x, y)) {
@@ -1610,7 +1609,7 @@ VerbIcon PelrockEngine::isActionUnder(int x, int y) {
 			return actions[i];
 		}
 	}
-	debug("No action under mouse at position %d,%d", x, y);
+
 	return NO_ACTION;
 }
 
@@ -1685,7 +1684,6 @@ void PelrockEngine::checkMouseHover() {
 
 	if (isActionUnder(_events->_mouseX, _events->_mouseY) != NO_ACTION) {
 		hotspotDetected = false;
-		debug("Mouse is over action icon, not hotspot, so showing action cursor");
 	}
 
 	// Calculate walk target first (before checking anything else)
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 90560b89ba0..284f5e1c275 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -250,6 +250,8 @@ public:
 	void performActionTrigger(uint16 actionTrigger);
 	void dialogActionTrigger(uint16 actionTrigger, byte room, byte rootIndex);
 
+	void toJail();
+
 	void executeAction(VerbIcon action, HotSpot *hotspot);
 	void openRoomDrawer(HotSpot *hotspot);
 	void closeRoomDrawer(HotSpot *hotspot);
@@ -318,6 +320,7 @@ public:
 	void openTravelAgencyDoor(HotSpot *hotspot);
 	void closeTravelAgencyDoor(HotSpot *hotspot);
 	void usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot);
+	void pickupSunflower(HotSpot *hotspot);
 	void pickUpBook(int i);
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 3b4e32d4331..5f519e9e4d8 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -34,6 +34,7 @@
 
 #include "pelrock/pelrock.h"
 #include "pelrock/sound.h"
+#include "sound.h"
 
 namespace Pelrock {
 
@@ -58,6 +59,15 @@ void SoundManager::playSound(byte index, int volume, int channel) {
 	}
 }
 
+void SoundManager::playSound(const char *filename, int volume, int channel) {
+	auto it = _soundMap.find(filename);
+	if (it != _soundMap.end()) {
+		playSound(it->_value, volume);
+	} else {
+		debug("Sound file %s not found in sound map", filename);
+	}
+}
+
 void SoundManager::playSound(SonidoFile sound, int volume, int channel) {
 	Common::File sonidosFile;
 	if (!sonidosFile.open(Common::Path("SONIDOS.DAT"))) {
@@ -99,12 +109,11 @@ void SoundManager::playSound(SonidoFile sound, int volume, int channel) {
 	}
 
 	if (stream) {
-		if(channel == -1) {
+		if (channel == -1) {
 			// Find a free channel
-		 	channel = findFreeChannel();
-		}
-		else {
-			if(_mixer->isSoundHandleActive(_sfxHandles[channel])) {
+			channel = findFreeChannel();
+		} else {
+			if (_mixer->isSoundHandleActive(_sfxHandles[channel])) {
 				_mixer->stopHandle(_sfxHandles[channel]);
 			}
 		}
@@ -157,7 +166,7 @@ int SoundManager::getSampleRate(byte *data, SoundFormat format) {
 }
 
 int SoundManager::findFreeChannel() {
-	//Reserve first 3 channels for one-off sounds
+	// Reserve first 3 channels for one-off sounds
 	for (int i = 3; i < kMaxChannels; i++) {
 		if (!_mixer->isSoundHandleActive(_sfxHandles[i])) {
 			return i;
@@ -245,7 +254,7 @@ void SoundManager::loadSoundIndex() {
 	sonidosFile.close();
 }
 
-static const uint kAmbientCounterMask = 0x1F;  // Trigger when (counter & mask) == mask
+static const uint kAmbientCounterMask = 0x1F; // Trigger when (counter & mask) == mask
 
 int SoundManager::tickAmbientSound(uint32 frameCount) {
 	// Counter gate: only trigger every 32 frames when (counter & 0x1F) == 0x1F
@@ -261,7 +270,7 @@ int SoundManager::tickAmbientSound(uint32 frameCount) {
 	// Pick random ambient slot 0-3 (corresponds to room sound indices 4-7)
 	int ambientSlotOffset = g_engine->getRandomNumber(3);
 
-	return ambientSlotOffset;  // Caller adds 4 to get room sound index
+	return ambientSlotOffset; // Caller adds 4 to get room sound index
 }
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index 604773622ab..de38a2fb4c1 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -150,15 +150,15 @@ struct SoundData {
 	uint32 size;
 };
 
-
 const int kMaxChannels = 15;
-const int kAmbientSoundSlotBase = 4;  // Room sound indices 4-7 are ambient sounds
+const int kAmbientSoundSlotBase = 4; // Room sound indices 4-7 are ambient sounds
 
 class SoundManager {
 public:
 	SoundManager(Audio::Mixer *mixer);
 	~SoundManager();
 	void playSound(byte index, int volume = 128, int channel = -1);
+	void playSound(const char *filename, int volume, int channel);
 	void playSound(byte *soundData, uint32 size, int volume = 128);
 	void stopAllSounds();
 	void stopSound(int channel);
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 2028f549702..8093d1f5d18 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -465,6 +465,7 @@ struct ResetEntry {
 #define FLAG_ALFRED_INTELIGENTE 9
 #define FLAG_ALFRED_SABE_EGIPCIO 10
 #define FLAG_VENDEDOR_DEJA_DE_JODER 11
+
 #define FLAG_VIAJE_A_EGIPTO 12
 #define FLAG_PARADOJA_RESUELTA 13
 #define FLAG_CROCODILLO_ENCENDIDO 14
@@ -502,13 +503,15 @@ struct ResetEntry {
 #define FLAG_TIENDA_ABIERTA 46
 #define FLAG_NUMERO_DE_COPAS 47
 #define FLAG_INGREDIENTES_CONSEGUIDOS 48
+
 #define FLAG_GUARDIA_PIDECOSAS 49
 #define FLAG_GUARDIA_DNI_ENTREGADO 50
 #define FLAG_AGENCIA_ABIERTA 51
 #define FLAG_CONSIGNAS_VENDEDOR 52
 #define FLAG_PUTA_250_VECES 53
+#define FLAG_RESPUESTAS_ACERTADAS 54
 
-const int kNumGameFlags = 54;
+const int kNumGameFlags = 55;
 
 struct GameStateData {
 	byte flags[kNumGameFlags];


Commit: ae81d1088f00b7b7d723e5fcd1d8e0059885dca9
    https://github.com/scummvm/scummvm/commit/ae81d1088f00b7b7d723e5fcd1d8e0059885dca9
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:00+02:00

Commit Message:
PELROCK: Fixes conversation root model to be setRoot

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/console.cpp
    engines/pelrock/console.h
    engines/pelrock/dialog.cpp
    engines/pelrock/saveload.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index c5832e1c4d6..be04fbf1080 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -215,15 +215,15 @@ void PelrockEngine::buyFromStore(HotSpot *hotspot, int stickerId) {
 void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte rootIndex) {
 	switch (actionTrigger) {
 	case 328:
-		debug("Disabling root %d in room %d", rootIndex, room);
-		_state->setRootDisabledState(room, rootIndex, true);
+		debug("Setting current root to %d in room %d", rootIndex + 1, room);
+		_state->setCurrentRoot(room, rootIndex + 1);
 		break;
 	case 329:
 		_state->setFlag(FLAG_PUTA_250_VECES, true);
 		break;
 	case 258:
 		_state->setFlag(FLAG_GUARDIA_PIDECOSAS, true);
-		_state->setRootDisabledState(4, 1, true);
+		_state->setCurrentRoot(4, 2);
 		break;
 	case 259:
 		_dialog->say(_res->_ingameTexts[NO_EMPECEMOS]);
@@ -242,15 +242,14 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_dialog->say(_res->_ingameTexts[UN_POCO_RESPETO]);
 		break;
 	case 264:
-		// disables the two first roots, the second one will be enabled later!
-		_state->setRootDisabledState(room, rootIndex, true);
-		_state->setRootDisabledState(room, rootIndex + 1, true);
+		// skip to root after the next one
+		_state->setCurrentRoot(room, rootIndex + 2);
 		break;
 	case 267:
-		_state->setRootDisabledState(7, 1, true);
+		_state->setCurrentRoot(7, 2);
 		break;
 	case 272:
-		_state->setRootDisabledState(room, rootIndex, true);
+		_state->setCurrentRoot(room, rootIndex + 1);
 		break;
 	case 273:
 		WalkBox w1;
@@ -271,14 +270,14 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 274:
 	case 275:
 	case 276:
-		_state->setRootDisabledState(room, rootIndex, true);
+		_state->setCurrentRoot(room, rootIndex + 1);
 		break;
 	case 277:
-		_state->setRootDisabledState(room, rootIndex, true);
+		_state->setCurrentRoot(room, rootIndex + 1);
 		_state->setFlag(FLAG_JEFE_INGRESA_PASTA, true);
 		break;
 	case 278:
-		_state->setRootDisabledState(room, rootIndex, true);
+		_state->setCurrentRoot(room, rootIndex + 1);
 		break;
 	case 279:
 		travelToEgypt();
@@ -306,7 +305,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		break;
 	case 334:
 		addInventoryItem(86);
-		_state->setRootDisabledState(room, rootIndex, true);
+		_state->setCurrentRoot(room, rootIndex + 1);
 
 		break;
 	case 335:
@@ -326,7 +325,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 344:
 	case 345:
 	case 346:
-		_state->setRootDisabledState(room, rootIndex, true);
+		_state->setCurrentRoot(room, rootIndex + 1);
 		break;
 	case 348: {
 		// Anti-piracy punishment: corrupt screen + noise + crash
@@ -336,13 +335,13 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 349:
 		_state->setFlag(FLAG_CONSIGNAS_VENDEDOR, _state->getFlag(FLAG_CONSIGNAS_VENDEDOR) + 1);
 		if (_state->getFlag(FLAG_CONSIGNAS_VENDEDOR) == 2) {
-			_state->setRootDisabledState(room, rootIndex, true);
+			_state->setCurrentRoot(room, rootIndex + 1);
 		}
 		break;
 	case 350:
 		_state->setFlag(FLAG_CONSIGNAS_VENDEDOR, _state->getFlag(FLAG_CONSIGNAS_VENDEDOR) + 1);
 		if (_state->getFlag(FLAG_CONSIGNAS_VENDEDOR) == 2) {
-			_state->setRootDisabledState(room, rootIndex, true);
+			_state->setCurrentRoot(room, rootIndex + 1);
 		}
 		break;
 	case 351:
@@ -367,8 +366,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		break;
 		// end moros
 	case 353:
-		_state->setRootDisabledState(room, rootIndex, true);
-		_state->setRootDisabledState(room, rootIndex + 1, true);
+		_state->setCurrentRoot(room, rootIndex + 2);
 		break;
 	case 354:
 		if(_state->hasInventoryItem(105)) {
@@ -381,52 +379,62 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		toJail();
 		break;
 	case 356:
-		_state->setRootDisabledState(room, 0, true);
-		_state->setRootDisabledState(room, 1, true);
-		_state->setRootDisabledState(room, 2, true);
+		_state->setCurrentRoot(room, 3);
 		break;
 	//end puta
 	// sabio
 	case 366:
-		_state->setRootDisabledState(room, rootIndex, true);
+		_state->setCurrentRoot(room, rootIndex + 1);
 		break;
 	case 363:
 		toJail();
 		break;
 	case 367: //accept riddle
-		_state->setRootDisabledState(room, 26, true);
+		_state->setCurrentRoot(room, 27);
 		walkAndAction(_room->findHotspotByExtra(467), TALK);
 		break;
 // hasta aqui
 
-	case 357: // mal
-		_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, _state->getFlag(FLAG_RESPUESTAS_ACERTADAS) - 1);
-		_state->setRootDisabledState(room, rootIndex, true);
+	case 357: // wrong answer: counter-- (min 0)
+		if (_state->getFlag(FLAG_RESPUESTAS_ACERTADAS) > 0) {
+			_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, _state->getFlag(FLAG_RESPUESTAS_ACERTADAS) - 1);
+		}
+		_state->setCurrentRoot(room, rootIndex + 1);
 		break;
-	case 358: // muy mal
-		_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, _state->getFlag(FLAG_RESPUESTAS_ACERTADAS) - 2);
-		_state->setRootDisabledState(room, rootIndex, true);
+	case 358: // very wrong answer: counter-=2 (min 0)
+		if (_state->getFlag(FLAG_RESPUESTAS_ACERTADAS) > 1) {
+			_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, _state->getFlag(FLAG_RESPUESTAS_ACERTADAS) - 2);
+		}
+		_state->setCurrentRoot(room, rootIndex + 1);
 		break;
-	case 359: // bien
+	case 359: // correct answer: counter++, award pin at 15
 		_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, _state->getFlag(FLAG_RESPUESTAS_ACERTADAS) + 1);
-		if(_state->getFlag(FLAG_RESPUESTAS_ACERTADAS) == 15) {
+		if (_state->getFlag(FLAG_RESPUESTAS_ACERTADAS) == 15) {
 			addInventoryItem(106); // pin
 			_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, 0);
-
 		}
-		_state->setRootDisabledState(room, rootIndex, true);
+		_state->setCurrentRoot(room, rootIndex + 1);
 		break;
-	case 360:
-
-	case 361:
+	case 360: // neutral reset: counter = 0
 		_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, 0);
-		//back to conversation 27
-	case 362:
-//?
-	case 365:
-		//correct
-	case 364:
-		//wrong
+		_state->setCurrentRoot(room, rootIndex + 1);
+		break;
+	case 361: // "no sé" (I don't know): no counter change, just advance
+		_state->setCurrentRoot(room, rootIndex + 1);
+		break;
+	case 362: // special trigger: enables HIJODELAGRANPUTA cheat code
+		// Sets cheat_code_checking_enabled flag in original (0x495F3)
+		// TODO: Implement cheat code sequence checker in game loop
+		_state->setFlag(FLAG_CHEAT_CODE_ENABLED, 1);
+		_state->setCurrentRoot(room, rootIndex + 1);
+		break;
+	case 364: // riddle wrong answer: advance to next riddle
+		_state->setCurrentRoot(room, rootIndex + 1);
+		break;
+	case 365: // riddle correct: set riddle-solved flag
+		_state->setFlag(FLAG_RIDDLE_SOLVED, 1);
+		_state->setCurrentRoot(room, rootIndex + 1);
+		break;
 
 	default:
 		debug("Got actionTrigger %d in dialogActionTrigger, but no handler defined", actionTrigger);
@@ -499,13 +507,12 @@ void PelrockEngine::closeRoomDrawer(HotSpot *hotspot) {
 	_room->enableHotspot(hotspot);
 }
 
-void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
+	void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
 	debug("Withdrawing money from ATM using card (inv obj %d)", inventoryObject);
 	if (_state->getFlag(FLAG_JEFE_INGRESA_PASTA)) {
 		_state->setFlag(FLAG_JEFE_INGRESA_PASTA, false);
 		addInventoryItem(75);
-		_state->setRootDisabledState(20, 0, true);
-		_state->setRootDisabledState(20, 1, true);
+		_state->setCurrentRoot(20, 2);
 	} else {
 		int billCount = 0;
 		for (uint i = 0; i < _state->inventoryItems.size(); i++) {
@@ -750,7 +757,7 @@ void PelrockEngine::pickCables(HotSpot *hotspot) {
 	_room->addSticker(21);
 
 	_dialog->say(_res->_ingameTexts[RELOJ_HA_CAMBIADO]);
-	_state->setRootDisabledState(4, 0, true);
+	_state->setCurrentRoot(4, 1);
 }
 
 void PelrockEngine::giveIdToGuard(int inventoryObject, HotSpot *hotspot) {
@@ -770,7 +777,7 @@ void PelrockEngine::giveIdToGuard(int inventoryObject, HotSpot *hotspot) {
 }
 
 void PelrockEngine::unlockMuseum() {
-	_state->setRootDisabledState(4, 2, true);
+	_state->setCurrentRoot(4, 3);
 	_room->enableSprite(2, 100, PERSIST_PERM);
 	_room->enableSprite(3, 100, PERSIST_PERM);
 	_room->addStickerToRoom(4, 87, PERSIST_PERM);
@@ -812,9 +819,7 @@ void PelrockEngine::useAmuletWithStatue(int inventoryObject, HotSpot *hotspot) {
 	if (!_room->hasSticker(24)) {
 		_room->addSticker(24);
 		_state->removeInventoryItem(7);
-		_state->setRootDisabledState(7, 0, true);
-		_state->setRootDisabledState(7, 1, false);
-		_state->setRootDisabledState(7, 2, true);
+		_state->setCurrentRoot(7, 1);
 		_alfredState.direction = ALFRED_RIGHT;
 
 		HotSpot *statueHotspot = _room->findHotspotByExtra(91);
@@ -930,9 +935,7 @@ void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
 void PelrockEngine::pickupSunflower(HotSpot *hotspot) {
 	if(_state->getFlag(FLAG_PARADOJA_RESUELTA) == false) {
 		_dialog->say(_res->_ingameTexts[OIGA]);
-		for(int i = 0; i < 26; i++) {
-			_state->setRootDisabledState(25, i, true);
-		}
+		_state->setCurrentRoot(25, 26);
 
 		walkAndAction(_room->findHotspotByExtra(467), TALK);
 	}
@@ -945,10 +948,10 @@ void PelrockEngine::pickupSunflower(HotSpot *hotspot) {
 void PelrockEngine::pickUpBook(int i) {
 	if (!_state->hasInventoryItem(10)) {
 		_dialog->say(_res->_ingameTexts[VENGA_ACA]);
-		_state->setRootDisabledState(9, 0, true);
+		_state->setCurrentRoot(9, 1);
 
 		if (_state->hasInventoryItem(3)) {
-			_state->setRootDisabledState(9, 1, true);
+			_state->setCurrentRoot(9, 2);
 			addInventoryItem(10);
 		}
 
@@ -959,9 +962,9 @@ void PelrockEngine::pickUpBook(int i) {
 		waitForActionEnd();
 		if (!_state->hasInventoryItem(3)) {
 
-			_state->setRootDisabledState(9, 0, false);
+			_state->setCurrentRoot(9, 0);
 		} else {
-			_state->setRootDisabledState(9, 2, true);
+			_state->setCurrentRoot(9, 3);
 		}
 	} else {
 		if (_state->libraryShelf == -1) {
@@ -1032,11 +1035,8 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		waitForSpecialAnimation();
 
 		loadExtraScreenAndPresent(3);
-		_state->setRootDisabledState(17, 0, true);
-		_state->setRootDisabledState(18, 0, true);
-		_state->setRootDisabledState(18, 1, true);
-		_state->setRootDisabledState(18, 2, true);
-		_state->setRootDisabledState(18, 3, true);
+		_state->setCurrentRoot(17, 1);
+		_state->setCurrentRoot(18, 4);
 		debug("After extra screen");
 		_dialog->say(_res->_ingameTexts[QUEASCO]);
 		break;
@@ -1067,8 +1067,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		waitForSpecialAnimation();
 		_dialog->say(_res->_ingameTexts[COSASAPRENDIDO]);
 		_state->setFlag(FLAG_ALFRED_INTELIGENTE, true);
-		_state->setRootDisabledState(14, 0, true);
-		_state->setRootDisabledState(14, 1, true);
+		_state->setCurrentRoot(14, 2);
 		break;
 	case 64:
 		_res->loadAlfredSpecialAnim(0);
diff --git a/engines/pelrock/console.cpp b/engines/pelrock/console.cpp
index 7ffbb0c6154..d7cdabcb528 100644
--- a/engines/pelrock/console.cpp
+++ b/engines/pelrock/console.cpp
@@ -29,7 +29,7 @@ namespace Pelrock {
 PelrockConsole::PelrockConsole(PelrockEngine *engine) : GUI::Debugger(), _engine(engine) {
 	registerCmd("room", WRAP_METHOD(PelrockConsole, cmdLoadRoom));
 	registerCmd("give", WRAP_METHOD(PelrockConsole, cmdGiveItems));
-	registerCmd("disableRoot", WRAP_METHOD(PelrockConsole, disableRoot));
+	registerCmd("setRoot", WRAP_METHOD(PelrockConsole, setRoot));
 	registerCmd("setFlag", WRAP_METHOD(PelrockConsole, setFlag));
 }
 
@@ -49,15 +49,15 @@ bool PelrockConsole::setFlag(int argc, const char **argv) {
 	return true;
 }
 
-bool PelrockConsole::disableRoot(int argc, const char **argv) {
+bool PelrockConsole::setRoot(int argc, const char **argv) {
 	if (argc < 3) {
-		debugPrintf("Usage: disableRoot <roomNumber> <rootIndex>");
+		debugPrintf("Usage: setRoot <roomNumber> <rootIndex>");
 		return true;
 	}
 	int roomNumber = atoi(argv[1]);
 	int rootIndex = atoi(argv[2]);
-	_engine->_state->setRootDisabledState(roomNumber, rootIndex, true);
-	debugPrintf("Disabled root %d in room %d\n", rootIndex, roomNumber);
+	_engine->_state->setCurrentRoot(roomNumber, rootIndex);
+	debugPrintf("Set current root to %d in room %d\n", rootIndex, roomNumber);
 	return true;
 }
 
diff --git a/engines/pelrock/console.h b/engines/pelrock/console.h
index f8728363680..aafaa3afc39 100644
--- a/engines/pelrock/console.h
+++ b/engines/pelrock/console.h
@@ -34,7 +34,7 @@ private:
 	bool cmdLoadRoom(int argc, const char **argv);
 	bool cmdGiveItems(int argc, const char **argv);
 	bool cmdTest(int argc, const char **argv);
-	bool disableRoot(int argc, const char **argv);
+	bool setRoot(int argc, const char **argv);
 	bool setFlag(int argc, const char **argv);
 
 public:
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index d99c2fec6dd..f606390665b 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -446,14 +446,6 @@ void DialogManager::setCurSprite(int index) {
 	_curSprite = nullptr;
 }
 
-bool isRootDisabled(byte room, int root) {
-
-	if (g_engine->_state->getRootDisabledState(room, root)) {
-		return true;
-	}
-	return false;
-}
-
 void DialogManager::startConversation(const byte *conversationData, uint32 dataSize, byte npcIndex, Sprite *animSet) {
 	if (!conversationData || dataSize == 0) {
 		debug("startConversation: No conversation data");
@@ -591,17 +583,22 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 }
 
 uint32 DialogManager::findRoot(int &currentRoot, uint32 currentPosition, uint32 dataSize, const byte *conversationData) {
-	while (g_engine->_state->getRootDisabledState(g_engine->_room->_currentRoomNumber, currentRoot)) {
-		// This root is disabled, skip to next
-		while (currentPosition < dataSize) {
+	// Check if a specific root has been set for this room
+	int targetRoot = g_engine->_state->getCurrentRoot(g_engine->_room->_currentRoomNumber);
+	
+	if (targetRoot >= 0) {
+		// Skip to the specified root
+		while (currentRoot < targetRoot && currentPosition < dataSize) {
 			if (conversationData[currentPosition] == CTRL_END_BRANCH) {
 				currentPosition++; // Move past end branch marker
 				currentRoot++;
-				break;
+			} else {
+				currentPosition++;
 			}
-			currentPosition++;
 		}
 	}
+	// If targetRoot is -1 or not set, use the first root (default behavior)
+	
 	return currentPosition;
 }
 
diff --git a/engines/pelrock/saveload.cpp b/engines/pelrock/saveload.cpp
index b665f9c244f..d6dcf7dbc72 100644
--- a/engines/pelrock/saveload.cpp
+++ b/engines/pelrock/saveload.cpp
@@ -335,8 +335,8 @@ bool syncGameStateData(Common::Serializer &s, GameStateData *gameState) {
 		}
 	}
 
-	// Conversation roots state
-	s.syncBytes(gameState->conversationRootsState, 4 * 56);
+	// Conversation current root indices
+	s.syncBytes(gameState->conversationCurrentRoot, 56);
 	return !s.err();
 }
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 8093d1f5d18..959929f17b3 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -510,8 +510,10 @@ struct ResetEntry {
 #define FLAG_CONSIGNAS_VENDEDOR 52
 #define FLAG_PUTA_250_VECES 53
 #define FLAG_RESPUESTAS_ACERTADAS 54
+#define FLAG_CHEAT_CODE_ENABLED 55    // 0x495F3 - enables HIJODELAGRANPUTA cheat code input
+#define FLAG_RIDDLE_SOLVED 56         // 0x495D0 - set when Egyptian riddle answered correctly
 
-const int kNumGameFlags = 55;
+const int kNumGameFlags = 57;
 
 struct GameStateData {
 	byte flags[kNumGameFlags];
@@ -532,15 +534,15 @@ struct GameStateData {
 	Common::HashMap<byte, Common::Array<SpriteChange>> spriteChanges;
 
 	GameStateData() {
-		memset(conversationRootsState, 0, 4 * 56);
+		memset(conversationCurrentRoot, 0xFF, 56); // 0xFF = not set
 		for (int i = 0; i < kNumGameFlags; i++)
 			flags[i] = 0;
 		flags[FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ] = true;
 	}
 
 	~GameStateData() {
-		delete[] conversationRootsState;
-		conversationRootsState = nullptr;
+		delete[] conversationCurrentRoot;
+		conversationCurrentRoot = nullptr;
 	}
 
 	void addDisabledBranch(ResetEntry entry) {
@@ -581,14 +583,23 @@ struct GameStateData {
 		return false;
 	}
 
-	byte *conversationRootsState = new byte[4 * 56];
+	// Store current root index for each room (0xFF = not set, use findRoot logic)
+	byte *conversationCurrentRoot = new byte[56];
 
-	bool getRootDisabledState(byte room, byte root) const {
-		return (conversationRootsState[room * 4 + root] != 0);
+	int getCurrentRoot(byte room) const {
+		if (room >= 56)
+			return -1;
+		return (conversationCurrentRoot[room] == 0xFF) ? -1 : conversationCurrentRoot[room];
 	}
 
-	void setRootDisabledState(byte room, byte root, bool disabled) {
-		conversationRootsState[room * 4 + root] = disabled ? 1 : 0;
+	void setCurrentRoot(byte room, int root) {
+		if (room >= 56)
+			return;
+		if (root < 0 || root > 254) {
+			conversationCurrentRoot[room] = 0xFF; // Reset to auto-select
+		} else {
+			conversationCurrentRoot[room] = (byte)root;
+		}
 	}
 
 	int findFirstBookIndex() {


Commit: 77340b2ddeb0fe5bb5fac3733c529dfad6a6f2f1
    https://github.com/scummvm/scummvm/commit/77340b2ddeb0fe5bb5fac3733c529dfad6a6f2f1
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:01+02:00

Commit Message:
PELROCK: Room 25

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index be04fbf1080..1dcd1dc749f 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -119,7 +119,6 @@ const ActionEntry actionTable[] = {
 	// Room 28
 	{472, PICKUP, &PelrockEngine::pickUpMatches},
 
-
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
 	{WILDCARD, TALK, &PelrockEngine::noOpAction},   // Generic talk action
@@ -295,8 +294,8 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_dialog->say(_res->_ingameTexts[HECHOELPRIMO]);
 		break;
 	case 332:
-		//psychologist card
-		if(!_state->hasInventoryItem(104)) {
+		// psychologist card
+		if (!_state->hasInventoryItem(104)) {
 			addInventoryItem(104);
 		}
 		break;
@@ -309,7 +308,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 
 		break;
 	case 335:
-		//many oranges
+		// many oranges
 		addInventoryItem(104);
 		break;
 	case 336:
@@ -369,56 +368,65 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_state->setCurrentRoot(room, rootIndex + 2);
 		break;
 	case 354:
-		if(_state->hasInventoryItem(105)) {
+		if (_state->hasInventoryItem(105)) {
 			addInventoryItem(105);
 		}
 		break;
 	case 352:
 	case 355:
-		//a la carcel
+		// a la carcel
 		toJail();
 		break;
 	case 356:
 		_state->setCurrentRoot(room, 3);
 		break;
-	//end puta
-	// sabio
+	// end puta
+	//  sabio
 	case 366:
 		_state->setCurrentRoot(room, rootIndex + 1);
 		break;
 	case 363:
 		toJail();
 		break;
-	case 367: //accept riddle
+	case 367: // accept riddle
 		_state->setCurrentRoot(room, 27);
 		walkAndAction(_room->findHotspotByExtra(467), TALK);
 		break;
-// hasta aqui
+		// hasta aqui
 
 	case 357: // wrong answer: counter-- (min 0)
+	{
 		if (_state->getFlag(FLAG_RESPUESTAS_ACERTADAS) > 0) {
 			_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, _state->getFlag(FLAG_RESPUESTAS_ACERTADAS) - 1);
 		}
-		_state->setCurrentRoot(room, rootIndex + 1);
+		advanceQuotesConversation(rootIndex, room);
 		break;
+	}
 	case 358: // very wrong answer: counter-=2 (min 0)
+	{
 		if (_state->getFlag(FLAG_RESPUESTAS_ACERTADAS) > 1) {
 			_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, _state->getFlag(FLAG_RESPUESTAS_ACERTADAS) - 2);
 		}
-		_state->setCurrentRoot(room, rootIndex + 1);
+		advanceQuotesConversation(rootIndex, room);
 		break;
+	}
 	case 359: // correct answer: counter++, award pin at 15
+	{
 		_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, _state->getFlag(FLAG_RESPUESTAS_ACERTADAS) + 1);
 		if (_state->getFlag(FLAG_RESPUESTAS_ACERTADAS) == 15) {
 			addInventoryItem(106); // pin
 			_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, 0);
 		}
-		_state->setCurrentRoot(room, rootIndex + 1);
+		advanceQuotesConversation(rootIndex, room);
 		break;
+	}
 	case 360: // neutral reset: counter = 0
+	{
 		_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, 0);
 		_state->setCurrentRoot(room, rootIndex + 1);
+		advanceQuotesConversation(rootIndex, room);
 		break;
+	}
 	case 361: // "no sé" (I don't know): no counter change, just advance
 		_state->setCurrentRoot(room, rootIndex + 1);
 		break;
@@ -426,14 +434,20 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		// Sets cheat_code_checking_enabled flag in original (0x495F3)
 		// TODO: Implement cheat code sequence checker in game loop
 		_state->setFlag(FLAG_CHEAT_CODE_ENABLED, 1);
-		_state->setCurrentRoot(room, rootIndex + 1);
+		advanceQuotesConversation(rootIndex, room);
 		break;
 	case 364: // riddle wrong answer: advance to next riddle
-		_state->setCurrentRoot(room, rootIndex + 1);
+	{
+		int targetIndex = rootIndex + 1;
+		if (rootIndex == 43) {
+			targetIndex = 27; // skip riddle explanation
+		}
+		_state->setCurrentRoot(room, targetIndex);
 		break;
+	}
 	case 365: // riddle correct: set riddle-solved flag
-		_state->setFlag(FLAG_RIDDLE_SOLVED, 1);
-		_state->setCurrentRoot(room, rootIndex + 1);
+		_state->setFlag(FLAG_PARADOJA_RESUELTA, 1);
+		_state->setCurrentRoot(room, 1);
 		break;
 
 	default:
@@ -442,6 +456,14 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	}
 }
 
+void PelrockEngine::advanceQuotesConversation(byte rootIndex, byte room) {
+	int targetRoot = rootIndex + 1;
+	if (targetRoot == 26) {
+		targetRoot = 2;
+	}
+	_state->setCurrentRoot(room, targetRoot);
+}
+
 void PelrockEngine::toJail() {
 	_graphics->fadeToBlack(10);
 	_alfredState.x = 342;
@@ -507,7 +529,7 @@ void PelrockEngine::closeRoomDrawer(HotSpot *hotspot) {
 	_room->enableHotspot(hotspot);
 }
 
-	void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
+void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
 	debug("Withdrawing money from ATM using card (inv obj %d)", inventoryObject);
 	if (_state->getFlag(FLAG_JEFE_INGRESA_PASTA)) {
 		_state->setFlag(FLAG_JEFE_INGRESA_PASTA, false);
@@ -621,11 +643,11 @@ void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
 		_events->pollEvent();
 		renderScene(OVERLAY_NONE);
 		// if (_chrono->_gameTick) {
-			_room->findSpriteByIndex(7)->y -= 10;
-			if (_room->findSpriteByIndex(7)->y <= 70) {
-				_room->findSpriteByIndex(7)->zOrder = -1;
-				break;
-			}
+		_room->findSpriteByIndex(7)->y -= 10;
+		if (_room->findSpriteByIndex(7)->y <= 70) {
+			_room->findSpriteByIndex(7)->zOrder = -1;
+			break;
+		}
 		// }
 		_screen->update();
 		g_system->delayMillis(10);
@@ -897,7 +919,7 @@ void PelrockEngine::closeNewspaperBossDoor(HotSpot *hotspot) {
 
 void PelrockEngine::openTravelAgencyDoor(HotSpot *hotspot) {
 
-	if(_state->getFlag(FLAG_AGENCIA_ABIERTA)) {
+	if (_state->getFlag(FLAG_AGENCIA_ABIERTA)) {
 		openDoor(hotspot, 1, 57, FEMININE, false);
 	}
 	// The game originally did nothing here
@@ -925,7 +947,7 @@ void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
 		g_system->delayMillis(10);
 	}
 	_graphics->fadeToBlack(10);
-	//update conversaton state
+	// update conversaton state
 	_alfredState.x = 300;
 	_alfredState.y = 238;
 	setScreen(28, ALFRED_DOWN);
@@ -933,18 +955,19 @@ void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
 }
 
 void PelrockEngine::pickupSunflower(HotSpot *hotspot) {
-	if(_state->getFlag(FLAG_PARADOJA_RESUELTA) == false) {
+	if (_state->getFlag(FLAG_PARADOJA_RESUELTA) == false) {
 		_dialog->say(_res->_ingameTexts[OIGA]);
 		_state->setCurrentRoot(25, 26);
-
+		_state->setFlag(FLAG_RIDDLE_PRESENTED, true);
 		walkAndAction(_room->findHotspotByExtra(467), TALK);
-	}
-	else {
-
+	} else {
+		addInventoryItem(85);
+		_room->disableHotspot(hotspot);
+		_state->setCurrentRoot(25, 1);
+		_room->addSticker(73);
 	}
 }
 
-
 void PelrockEngine::pickUpBook(int i) {
 	if (!_state->hasInventoryItem(10)) {
 		_dialog->say(_res->_ingameTexts[VENGA_ACA]);
@@ -984,7 +1007,6 @@ void PelrockEngine::pickUpBook(int i) {
 			_state->selectedBookIndex = -1;
 		}
 	}
-
 }
 
 void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
@@ -1062,12 +1084,17 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		_state->setFlag(FLAG_ALFRED_SABE_EGIPCIO, true);
 		break;
 	case 24:
-		_res->loadAlfredSpecialAnim(0);
-		_alfredState.animState = ALFRED_SPECIAL_ANIM;
-		waitForSpecialAnimation();
-		_dialog->say(_res->_ingameTexts[COSASAPRENDIDO]);
-		_state->setFlag(FLAG_ALFRED_INTELIGENTE, true);
-		_state->setCurrentRoot(14, 2);
+		if (_state->getFlag(FLAG_RIDDLE_PRESENTED) == true) {
+			_dialog->say(_res->_ingameTexts[CAPITULOPARADOJAS]);
+			_state->setCurrentRoot(25, 44);
+		} else {
+			_res->loadAlfredSpecialAnim(0);
+			_alfredState.animState = ALFRED_SPECIAL_ANIM;
+			waitForSpecialAnimation();
+			_dialog->say(_res->_ingameTexts[COSASAPRENDIDO]);
+			_state->setFlag(FLAG_ALFRED_INTELIGENTE, true);
+			_state->setCurrentRoot(14, 2);
+		}
 		break;
 	case 64:
 		_res->loadAlfredSpecialAnim(0);
@@ -1148,7 +1175,7 @@ void PelrockEngine::animateStatuePaletteFade(bool reverse) {
 		_events->pollEvent();
 
 		bool didRender = renderScene(OVERLAY_NONE);
-		if(didRender) {
+		if (didRender) {
 
 			for (int i = 0; i < 16; i++) {
 				byte paletteIndex = paletteData.indices[i];
@@ -1183,8 +1210,7 @@ void PelrockEngine::animateStatuePaletteFade(bool reverse) {
 void PelrockEngine::checkObjectsForPart2() {
 	if (_state->hasInventoryItem(17) &&
 		_state->hasInventoryItem(59) &&
-		_state->hasInventoryItem(24)
-	) {
+		_state->hasInventoryItem(24)) {
 		_room->addStickerToRoom(19, 54, PERSIST_BOTH);
 		_room->addStickerToRoom(19, 55, PERSIST_BOTH);
 		_room->addStickerToRoom(19, 56, PERSIST_BOTH);
@@ -1203,7 +1229,6 @@ void PelrockEngine::waitForActionEnd() {
 	}
 }
 
-
 /**
  * Handler for picking up object with extra_id 472 in Room 28.
  * Loads a special palette from ALFRED.7 at offset 0x1610CE and
@@ -1257,7 +1282,7 @@ void PelrockEngine::antiPiracyEffect() {
 	_sound->stopMusic();
 
 	// Generate a buffer of white noise for the PC speaker simulation
-	const int kNoiseLength = 16000; // 1 second at 8kHz
+	const int kNoiseLength = 16000;                // 1 second at 8kHz
 	byte *noiseData = new byte[kNoiseLength + 44]; // WAV header + data
 
 	// Write a minimal WAV header
@@ -1265,13 +1290,13 @@ void PelrockEngine::antiPiracyEffect() {
 	WRITE_LE_UINT32(noiseData + 4, kNoiseLength + 36);
 	memcpy(noiseData + 8, "WAVE", 4);
 	memcpy(noiseData + 12, "fmt ", 4);
-	WRITE_LE_UINT32(noiseData + 16, 16);      // chunk size
-	WRITE_LE_UINT16(noiseData + 20, 1);       // PCM format
-	WRITE_LE_UINT16(noiseData + 22, 1);       // mono
-	WRITE_LE_UINT32(noiseData + 24, 8000);    // sample rate
-	WRITE_LE_UINT32(noiseData + 28, 8000);    // byte rate
-	WRITE_LE_UINT16(noiseData + 32, 1);       // block align
-	WRITE_LE_UINT16(noiseData + 34, 8);       // bits per sample
+	WRITE_LE_UINT32(noiseData + 16, 16);   // chunk size
+	WRITE_LE_UINT16(noiseData + 20, 1);    // PCM format
+	WRITE_LE_UINT16(noiseData + 22, 1);    // mono
+	WRITE_LE_UINT32(noiseData + 24, 8000); // sample rate
+	WRITE_LE_UINT32(noiseData + 28, 8000); // byte rate
+	WRITE_LE_UINT16(noiseData + 32, 1);    // block align
+	WRITE_LE_UINT16(noiseData + 34, 8);    // bits per sample
 	memcpy(noiseData + 36, "data", 4);
 	WRITE_LE_UINT32(noiseData + 40, kNoiseLength);
 
@@ -1301,7 +1326,7 @@ void PelrockEngine::antiPiracyEffect() {
 			break;
 		}
 
-		//generate random pixels on the screen (simulating corrupted video memory)
+		// generate random pixels on the screen (simulating corrupted video memory)
 		for (int i = 0; i < screenSize; i++) {
 			screenPixels[i] = (byte)getRandomNumber(255);
 		}
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index f606390665b..ecefc64faff 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -585,7 +585,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 uint32 DialogManager::findRoot(int &currentRoot, uint32 currentPosition, uint32 dataSize, const byte *conversationData) {
 	// Check if a specific root has been set for this room
 	int targetRoot = g_engine->_state->getCurrentRoot(g_engine->_room->_currentRoomNumber);
-	
+
 	if (targetRoot >= 0) {
 		// Skip to the specified root
 		while (currentRoot < targetRoot && currentPosition < dataSize) {
@@ -598,7 +598,7 @@ uint32 DialogManager::findRoot(int &currentRoot, uint32 currentPosition, uint32
 		}
 	}
 	// If targetRoot is -1 or not set, use the first root (default behavior)
-	
+
 	return currentPosition;
 }
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 284f5e1c275..d7edb3fd9bd 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -250,6 +250,8 @@ public:
 	void performActionTrigger(uint16 actionTrigger);
 	void dialogActionTrigger(uint16 actionTrigger, byte room, byte rootIndex);
 
+	void advanceQuotesConversation(byte rootIndex, byte room);
+
 	void toJail();
 
 	void executeAction(VerbIcon action, HotSpot *hotspot);
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 1cf969b758f..a8107699e84 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -50,6 +50,7 @@ static const int unpickableHotspotExtras[] = {
 	361,
 	362,
 	472, // matches
+	609 // sunflower
 };
 
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 959929f17b3..e0f96b70891 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -465,9 +465,11 @@ struct ResetEntry {
 #define FLAG_ALFRED_INTELIGENTE 9
 #define FLAG_ALFRED_SABE_EGIPCIO 10
 #define FLAG_VENDEDOR_DEJA_DE_JODER 11
+#define FLAG_PARADOJA_RESUELTA 13
+
+
 
 #define FLAG_VIAJE_A_EGIPTO 12
-#define FLAG_PARADOJA_RESUELTA 13
 #define FLAG_CROCODILLO_ENCENDIDO 14
 #define FLAG_MIRA_SIMBOLO_FUERA_MUSEO 15
 #define FLAG_PUERTA_SECRETA_ABIERTA 16
@@ -511,7 +513,7 @@ struct ResetEntry {
 #define FLAG_PUTA_250_VECES 53
 #define FLAG_RESPUESTAS_ACERTADAS 54
 #define FLAG_CHEAT_CODE_ENABLED 55    // 0x495F3 - enables HIJODELAGRANPUTA cheat code input
-#define FLAG_RIDDLE_SOLVED 56         // 0x495D0 - set when Egyptian riddle answered correctly
+#define FLAG_RIDDLE_PRESENTED 56
 
 const int kNumGameFlags = 57;
 


Commit: 8067a173d116ef02d62d8ab151dd9fa496dd3161
    https://github.com/scummvm/scummvm/commit/8067a173d116ef02d62d8ab151dd9fa496dd3161
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:01+02:00

Commit Message:
PELROCK: Implements picking up objects in room 28

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 1dcd1dc749f..09bfcc1013d 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -118,6 +118,10 @@ const ActionEntry actionTable[] = {
 
 	// Room 28
 	{472, PICKUP, &PelrockEngine::pickUpMatches},
+	{87, PICKUP, &PelrockEngine::pickUpChainsaw},
+	{88, PICKUP, &PelrockEngine::pickUpSpellbook},
+	{89, PICKUP, &PelrockEngine::pickUpBoot},
+	{112, PICKUP, &PelrockEngine::pickupCondoms},
 
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
@@ -966,6 +970,14 @@ void PelrockEngine::pickupSunflower(HotSpot *hotspot) {
 		_state->setCurrentRoot(25, 1);
 		_room->addSticker(73);
 	}
+	checkIngredients();
+}
+
+void PelrockEngine::checkIngredients() {
+	byte ingredientes = _state->getFlag(FLAG_INGREDIENTES_CONSEGUIDOS);
+	int textLine = PRIMERINGREDIENTE + ingredientes;
+	_dialog->say(_res->_ingameTexts[textLine]);
+	_state->setFlag(FLAG_INGREDIENTES_CONSEGUIDOS, ingredientes + 1);
 }
 
 void PelrockEngine::pickUpBook(int i) {
@@ -1009,6 +1021,25 @@ void PelrockEngine::pickUpBook(int i) {
 	}
 }
 
+void PelrockEngine::pickUpChainsaw(HotSpot *hotspot) {
+
+	_room->addSticker(99);
+}
+
+void PelrockEngine::pickUpSpellbook(HotSpot *hotspot) {
+	_room->addSticker(97);
+}
+
+void PelrockEngine::pickUpBoot(HotSpot *hotspot) {
+
+	_room->addSticker(98);
+}
+
+void PelrockEngine::pickupCondoms(HotSpot *hotspot) {
+
+	_room->addSticker(100);
+}
+
 void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	debug("Performing action trigger: %d", actionTrigger);
 	switch (actionTrigger) {
@@ -1216,8 +1247,6 @@ void PelrockEngine::checkObjectsForPart2() {
 		_room->addStickerToRoom(19, 56, PERSIST_BOTH);
 		_room->addStickerToRoom(19, 58, PERSIST_BOTH);
 		_state->setFlag(FLAG_AGENCIA_ABIERTA, true);
-		// _state->setFlag(FLAG_PUEDE_VIAJAR_EN_EL_TIEMPO, true);
-		// _state->setRootDisabledState(19, 0, true);
 	}
 }
 
@@ -1233,8 +1262,6 @@ void PelrockEngine::waitForActionEnd() {
  * Handler for picking up object with extra_id 472 in Room 28.
  * Loads a special palette from ALFRED.7 at offset 0x1610CE and
  * fades to it using the step-wise palette transition.
- *
- * Original handler at Ghidra address 0x1FED8.
  */
 void PelrockEngine::pickUpMatches(HotSpot *hotspot) {
 	// Load the special palette from ALFRED.7 at offset 0x1610CE
@@ -1260,8 +1287,12 @@ void PelrockEngine::pickUpMatches(HotSpot *hotspot) {
 	_graphics->fadePaletteToTarget(targetPalette, 25);
 	debug("Finished palette fade for room 28 object pickup");
 	// Pick up the item
-	// addInventoryItem(hotspot->extra);
 	_room->disableHotspot(hotspot);
+	_state->setFlag(FLAG_CROCODILLO_ENCENDIDO, true);
+	_room->moveHotspot(_room->findHotspotByExtra(87), 415, 171);
+	_room->moveHotspot(_room->findHotspotByExtra(88), 305, 217);
+	_room->moveHotspot(_room->findHotspotByExtra(89), 201, 239);
+	_room->moveHotspot(_room->findHotspotByExtra(112), 261, 259);
 }
 
 /**
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 3cb01f23df7..3f26f1d81cc 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -670,13 +670,13 @@ void PelrockEngine::placeSticker(Sticker sticker) {
 	for (int y = 0; y < sticker.h; y++) {
 		for (int x = 0; x < sticker.w; x++) {
 			byte pixel = sticker.stickerData[y * sticker.w + x];
-			if (pixel != 0) {
+			// if (pixel != 0) {
 				int bgX = sticker.x + x;
 				int bgY = sticker.y + y;
 				if (bgX >= 0 && bgX < 640 && bgY >= 0 && bgY < 400) {
 					_compositeBuffer[bgY * 640 + bgX] = pixel;
 				}
-			}
+			// }
 		}
 	}
 }
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index d7edb3fd9bd..820c9354bd0 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -323,7 +323,12 @@ public:
 	void closeTravelAgencyDoor(HotSpot *hotspot);
 	void usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot);
 	void pickupSunflower(HotSpot *hotspot);
+	void checkIngredients();
 	void pickUpBook(int i);
+	void pickUpChainsaw(HotSpot *hotspot);
+	void pickUpSpellbook(HotSpot *hotspot);
+	void pickUpBoot(HotSpot *hotspot);
+	void pickupCondoms(HotSpot *hotspot);
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
 


Commit: 1f929725d2ac323f75acaf629a450a21d67088ca
    https://github.com/scummvm/scummvm/commit/1f929725d2ac323f75acaf629a450a21d67088ca
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:01+02:00

Commit Message:
PELROCK: Spell book

Changed paths:
  A engines/pelrock/extrascreens.cpp
  A engines/pelrock/extrascreens.h
    engines/pelrock/actions.cpp
    engines/pelrock/computer.cpp
    engines/pelrock/graphics.cpp
    engines/pelrock/graphics.h
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h
    engines/pelrock/module.mk
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 09bfcc1013d..ac8ea7ff1aa 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -26,6 +26,7 @@
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/util.h"
+#include "pelrock/extrascreens.h"
 
 namespace Pelrock {
 
@@ -1138,6 +1139,41 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 			_dialog->say(_res->_ingameTexts[QUELASTIMA_NOSEEGIPCIO]);
 		}
 		break;
+	case 88: {
+
+		SpellBook spellBook = SpellBook(_events, _res);
+		_res->loadAlfredSpecialAnim(0);
+		_alfredState.animState = ALFRED_SPECIAL_ANIM;
+		waitForSpecialAnimation();
+
+		Spell *spell = spellBook.run();
+		if (spell) {
+			_alfredState.direction = ALFRED_LEFT;
+			_dialog->say(_res->_ingameTexts[DIOSHALCON + spell->page], 1);
+			if(spell->page == 12 && _room->_currentRoomNumber == 28) {
+				_graphics->clearScreen();
+				int waitFrames = 0;
+				//blank screen for half a second
+				while (!shouldQuit() && waitFrames < 20)
+				{
+					_events->pollEvent();
+					_chrono->updateChrono();
+					if(_chrono->_gameTick) {
+						waitFrames++;
+					}
+					_screen->markAllDirty();
+					_screen->update();
+					g_system->delayMillis(10);
+				}
+
+				_alfredState.x = 145;
+				_alfredState.y = 312;
+				setScreen(25, ALFRED_RIGHT);
+				_dialog->say(_res->_ingameTexts[MENUDAAVENTURA]);
+			}
+		}
+		break;
+	}
 	case 0: // yellow book
 		_res->loadAlfredSpecialAnim(0);
 		_alfredState.animState = ALFRED_SPECIAL_ANIM;
diff --git a/engines/pelrock/computer.cpp b/engines/pelrock/computer.cpp
index 4b675b82dec..04b9d7c9005 100644
--- a/engines/pelrock/computer.cpp
+++ b/engines/pelrock/computer.cpp
@@ -23,7 +23,6 @@
 #include "common/system.h"
 #include "graphics/paletteman.h"
 
-#include "computer.h"
 #include "pelrock/computer.h"
 #include "pelrock/library_books.h"
 #include "pelrock/pelrock.h"
diff --git a/engines/pelrock/extrascreens.cpp b/engines/pelrock/extrascreens.cpp
new file mode 100644
index 00000000000..e47a39e2223
--- /dev/null
+++ b/engines/pelrock/extrascreens.cpp
@@ -0,0 +1,175 @@
+/* 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 "common/events.h"
+#include "graphics/paletteman.h"
+
+#include "extrascreens.h"
+#include "pelrock/extrascreens.h"
+#include "pelrock/graphics.h"
+#include "pelrock/util.h"
+
+namespace Pelrock {
+
+SpellBook::SpellBook(PelrockEventManager *eventMan, ResourceManager *res)
+	: _backgroundScreen(nullptr),
+	  _palette(nullptr),
+	  _events(eventMan),
+	  _res(res),
+	  _spell(nullptr) {
+	init();
+}
+
+SpellBook::~SpellBook() {
+	cleanup();
+}
+
+Spell *SpellBook::run() {
+	loadBackground();
+	g_engine->changeCursor(DEFAULT);
+	while (!g_engine->shouldQuit() && !_selectedSpell) {
+		_events->pollEvent();
+		drawScreen();
+		if (_events->_leftMouseClicked) {
+			_events->_leftMouseClicked = false;
+			checkMouse(_events->_mouseClickX, _events->_mouseClickY);
+		}
+		g_engine->_screen->markAllDirty();
+		g_engine->_screen->update();
+		g_system->delayMillis(10);
+	}
+	memset(g_engine->_screen->getPixels(), 0, 640 * 400);
+	// Restore room palette
+	g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
+	return _selectedSpell;
+}
+
+void SpellBook::init() {
+	_compositeScreen = new byte[640 * 400];
+
+	// selectPage(0);
+}
+
+void SpellBook::selectPage(int page) {
+	_spell = new Spell();
+	_spell->page = page;
+	Common::File alfred7;
+	if (!alfred7.open("ALFRED.7")) {
+		return;
+	}
+
+	Common::File juegoFile;
+	if (!juegoFile.open("JUEGO.EXE")) {
+		return;
+	}
+
+	alfred7.seek(1268719, SEEK_SET);
+	int w = 119;
+	int h = 99;
+	int nFrames = 13;
+	byte *compressedData = nullptr;
+	byte *spriteData = nullptr;
+	size_t outSize = 0;
+	readUntilBuda(&alfred7, 1268723, compressedData, outSize);
+	rleDecompress(compressedData, outSize, 0, w * h * nFrames, &spriteData, false);
+	_spell->image = new byte[w * h];
+	extractSingleFrame(spriteData, _spell->image, page, w, h);
+
+	juegoFile.seek(0x0004661C, SEEK_SET);
+	byte *textData = new byte[2861];
+	juegoFile.read(textData, 2861);
+
+	for (int i = 0; i < 2861; ++i) {
+		if (textData[i] == 0x0D)
+			textData[i] = 23;
+	}
+
+	Common::Array<Common::StringArray> spells = _res->processTextData(textData, 2861, true);
+
+	_spell->text = spells[page];
+	delete[] compressedData;
+	delete[] spriteData;
+	alfred7.close();
+	juegoFile.close();
+}
+
+void SpellBook::drawScreen() {
+	memcpy(_compositeScreen, _backgroundScreen, 640 * 400);
+
+	int textY = 83;
+	int textX = 317;
+
+	if (_spell != nullptr) {
+		drawSpriteToBuffer(_compositeScreen, 640, _spell->image, 168, 143, 119, 99, 207);
+		g_engine->_graphics->drawColoredTexts(_compositeScreen, _spell->text, textX, textY, 640, 0, g_engine->_smallFont);
+	}
+
+	drawPaletteSquares(_compositeScreen, _palette);
+	memcpy(g_engine->_screen->getPixels(), _compositeScreen, 640 * 400);
+	if (_spell != nullptr) {
+		g_engine->_graphics->drawColoredTexts(g_engine->_screen, _spell->text, textX, textY, 640, 0, g_engine->_smallFont);
+	}
+}
+
+void SpellBook::loadBackground() {
+	_backgroundScreen = new byte[640 * 400];
+	_palette = new byte[768];
+	_res->getExtraScreen(8, _backgroundScreen, _palette);
+	g_system->getPaletteManager()->setPalette(_palette, 0, 256);
+}
+
+void SpellBook::cleanup() {
+	if (_backgroundScreen) {
+		delete[] _backgroundScreen;
+		_backgroundScreen = nullptr;
+	}
+	if (_palette) {
+		delete[] _palette;
+		_palette = nullptr;
+	}
+	if (_spell) {
+		delete _spell;
+		_spell = nullptr;
+	}
+	g_engine->_screen->markAllDirty();
+	g_engine->_screen->update();
+}
+
+void SpellBook::checkMouse(int x, int y) {
+	// Check bookmarks
+	for (int i = 0; i < 13; i++) {
+		Common::Rect r = Common::Rect(_bookmarks[i].x, _bookmarks[i].y, _bookmarks[i].x + _bookmarks[i].w, _bookmarks[i].y + _bookmarks[i].h);
+		if (r.contains(x, y)) {
+			selectPage(_bookmarks[i].page);
+			return;
+		}
+	}
+
+	// Check text area
+	if (_spell == nullptr) {
+		return;
+	}
+	Common::Rect textArea = Common::Rect(321, 81, 321 + 140, 81 + (_spell->text.size() * 10));
+	if (textArea.contains(x, y)) {
+		_selectedSpell = _spell;
+	}
+}
+
+} // End of namespace Pelrock
diff --git a/engines/pelrock/extrascreens.h b/engines/pelrock/extrascreens.h
new file mode 100644
index 00000000000..c1fe52a1be5
--- /dev/null
+++ b/engines/pelrock/extrascreens.h
@@ -0,0 +1,92 @@
+/* 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 PELROCK_EXTRASCREENS_H
+#define PELROCK_EXTRASCREENS_H
+
+#include "pelrock/pelrock.h"
+
+namespace Pelrock {
+
+class PelrockEngine;
+
+struct Spell {
+	Common::StringArray text;
+    int page;
+	byte *image;
+};
+
+struct Bookmark {
+	int16 x;
+	int16 y;
+	byte w;
+	byte h;
+	int page;
+};
+
+static const Bookmark _bookmarks[13] = {
+	{244, 8, 23, 40, 0},
+	{396, 17, 44, 20, 1},
+	{480, 68, 40, 42, 4},
+	{480, 129, 30, 25, 5},
+	{480, 224, 28, 32, 8},
+	{416, 344, 23, 17, 12},
+	{368, 346, 28, 34, 11},
+	{198, 340, 26, 28, 10},
+	{164, 336, 33, 17, 9},
+	{95, 277, 33, 24, 7},
+	{105, 227, 27, 33, 6},
+	{103, 118, 30, 26, 3},
+    {101, 78, 36, 33, 2}
+};
+
+class SpellBook {
+
+public:
+	SpellBook(PelrockEventManager *eventMan, ResourceManager *res);
+	~SpellBook();
+
+	/**
+	 * returns the spell the user selected
+	 */
+	Spell *run();
+
+private:
+	PelrockEventManager *_events;
+	ResourceManager *_res;
+	byte *_backgroundScreen;
+	byte *_compositeScreen;
+	byte *_palette;
+
+	Spell *_spell = nullptr;
+
+	Spell *_selectedSpell = nullptr;
+	void init();
+	void selectPage(int page);
+	void drawScreen();
+	void loadBackground();
+	void cleanup();
+	void checkMouse(int x, int y);
+};
+
+} // End of namespace Pelrock
+
+#endif // PELROCK_EXTRASCREENS_H
diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index bda96e13442..5d88c147a5a 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -147,4 +147,91 @@ void GraphicsManager::clearScreen() {
 	memset(g_engine->_screen->getPixels(), 0, g_engine->_screen->pitch * g_engine->_screen->h);
 }
 
+void GraphicsManager::drawColoredText(Graphics::ManagedSurface *screen, const Common::String &text, int x, int y, int w, byte &defaultColor, Graphics::Font *font) {
+	int currentX = x;
+
+	Common::String segment;
+	for (uint i = 0; i < text.size(); i++) {
+		if (text[i] == '@' && i + 1 < text.size()) {
+			// Draw accumulated segment
+			if (!segment.empty()) {
+				font->drawString(screen, segment, currentX, y, w, defaultColor);
+				currentX += font->getStringWidth(segment);
+				segment.clear();
+			}
+			defaultColor = text[i + 1];
+			i++; // skip color code
+		} else {
+			segment += text[i];
+		}
+	}
+
+	// Draw remaining segment
+	if (!segment.empty()) {
+		font->drawString(screen, segment, currentX, y, w, defaultColor);
+	}
+}
+
+void GraphicsManager::drawColoredText(byte *buf, const Common::String &text, int x, int y, int w, byte &defaultColor, Graphics::Font *font) {
+
+	Graphics::Surface tempSurface;
+	Common::Rect r = font->getBoundingBox(text); // Ensure font metrics are loaded before creating surface
+
+	tempSurface.create(r.width(), r.height(), Graphics::PixelFormat::createFormatCLUT8());
+
+	int currentX = x;
+
+	Common::String segment;
+	for (uint i = 0; i < text.size(); i++) {
+		if (text[i] == '@' && i + 1 < text.size()) {
+			// Draw accumulated segment
+			if (!segment.empty()) {
+				font->drawString(&tempSurface, segment, currentX, y, w, defaultColor);
+				currentX += font->getStringWidth(segment);
+				segment.clear();
+			}
+			defaultColor = text[i + 1];
+			i++; // skip color code
+		} else {
+			segment += text[i];
+		}
+	}
+
+	// Draw remaining segment
+	if (!segment.empty()) {
+		font->drawString(&tempSurface, segment, currentX, y, w, defaultColor);
+	}
+
+	for(int j = 0; j < tempSurface.h; j++) {
+		for(int i = 0; i < tempSurface.w; i++) {
+			int idx = j * tempSurface.w + i;
+			if (y + j < 400 && x + i < 640) {
+				byte pixel = *((byte *)tempSurface.getBasePtr(i, j));
+				if (pixel != 0) { // Assuming 0 is transparent
+					debug("Drawing pixel at (%d, %d) with color %d", x + i, y + j, pixel);
+					buf[(y + j) * 640 + (x + i)] = pixel;
+				}
+			}
+		}
+	}
+}
+
+void GraphicsManager::drawColoredTexts(Graphics::ManagedSurface *surface, const Common::StringArray &text, int x, int y, int w, int yPadding, Graphics::Font *font) {
+	int currentX = x;
+	byte currentColor = 255;
+
+	for(int i =0; i < text.size(); i++) {
+		drawColoredText(surface, text[i], currentX, y + i * (font->getFontHeight() + yPadding), w, currentColor, font);
+	}
+}
+
+void GraphicsManager::drawColoredTexts(byte *buf, const Common::StringArray &text, int x, int y, int w, int yPadding, Graphics::Font *font) {
+	int currentX = x;
+	byte currentColor = 255;
+
+	for(int i =0; i < text.size(); i++) {
+		drawColoredText(buf, text[i], currentX, y + i * (font->getFontHeight() + yPadding), w, currentColor, font);
+	}
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/graphics.h b/engines/pelrock/graphics.h
index be2230e10a9..fd2de370bd2 100644
--- a/engines/pelrock/graphics.h
+++ b/engines/pelrock/graphics.h
@@ -21,7 +21,11 @@
 #ifndef PELROCK_GRAPHICS_H
 #define PELROCK_GRAPHICS_H
 
+#include "common/array.h"
+#include "common/str-array.h"
 #include "common/scummsys.h"
+
+#include "graphics/font.h"
 #include "graphics/screen.h"
 
 namespace Pelrock {
@@ -37,6 +41,10 @@ public:
 	void fadeToBlack(int stepSize);
 	void fadePaletteToTarget(byte *targetPalette, int stepSize);
 	void clearScreen();
+	void drawColoredText(Graphics::ManagedSurface *screen, const Common::String &text, int x, int y, int w, byte &defaultColor, Graphics::Font *font);
+	void drawColoredText(byte *buf, const Common::String &text, int x, int y, int w, byte &defaultColor, Graphics::Font *font);
+	void drawColoredTexts(Graphics::ManagedSurface *surface, const Common::StringArray &text, int x, int y, int w, int yPadding, Graphics::Font *font);
+	void drawColoredTexts(byte *buf, const Common::StringArray &text, int x, int y, int w, int yPadding, Graphics::Font *font);
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 69449dcc1f4..b67c763260e 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -34,32 +34,6 @@ namespace Pelrock {
 Pelrock::MenuManager::MenuManager(Graphics::Screen *screen, PelrockEventManager *events, ResourceManager *res, SoundManager *sound) : _screen(screen), _events(events), _res(res), _sound(sound) {
 }
 
-void MenuManager::drawColoredText(Graphics::ManagedSurface *screen, const Common::String &text, int x, int y, int w, Graphics::Font *font) {
-	int currentX = x;
-	uint32 currentColor = 255;
-
-	Common::String segment;
-	for (uint i = 0; i < text.size(); i++) {
-		if (text[i] == '@' && i + 1 < text.size()) {
-			// Draw accumulated segment
-			if (!segment.empty()) {
-				font->drawString(screen, segment, currentX, y, w, currentColor);
-				currentX += font->getStringWidth(segment);
-				segment.clear();
-			}
-			currentColor = text[i + 1];
-			i++; // skip color code
-		} else {
-			segment += text[i];
-		}
-	}
-
-	// Draw remaining segment
-	if (!segment.empty()) {
-		font->drawString(screen, segment, currentX, y, w, currentColor);
-	}
-}
-
 MenuButton MenuManager::isButtonClicked(int x, int y) {
 	if (_questionMarkRect.contains(x, y)) {
 		return QUESTION_MARK_BUTTON;
@@ -175,8 +149,9 @@ void MenuManager::drawScreen() {
 	drawInventoryIcons();
 
 	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
+	byte defaultColor = 255;
 	for (int i = 0; _menuText.size() > i; i++) {
-		drawColoredText(_screen, _menuText[i], 230, 200 + (i * 10), 200, g_engine->_smallFont);
+		g_engine->_graphics->drawColoredText(_screen, _menuText[i], 230, 200 + (i * 10), 200, defaultColor, g_engine->_smallFont);
 	}
 
 	drawText(g_engine->_smallFont, Common::String::format("%d,%d", _events->_mouseX, _events->_mouseY), 0, 0, 640, 13);
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 781b721715d..0c097817f77 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -180,7 +180,6 @@ private:
 	void loadMenuTexts();
 	void cleanUp();
 	void drawButtons();
-	void drawColoredText(Graphics::ManagedSurface *surface, const Common::String &text, int x, int y, int w, Graphics::Font *font);
 	void readButton(Common::File &alfred7, uint32 offset, byte *outBuffer[2], Common::Rect rect);
 	MenuButton isButtonClicked(int x, int y);
 	Graphics::Screen *_screen = nullptr;
diff --git a/engines/pelrock/module.mk b/engines/pelrock/module.mk
index 5a295a952b0..dbbf56ce979 100644
--- a/engines/pelrock/module.mk
+++ b/engines/pelrock/module.mk
@@ -20,7 +20,8 @@ MODULE_OBJS = \
 	dialog.o \
 	menu.o \
 	graphics.o \
-	saveload.o
+	saveload.o \
+	extrascreens.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_PELROCK), DYNAMIC_PLUGIN)
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 9ff2fa9fd2e..0af6d1883c0 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -467,9 +467,13 @@ const ExtraImages extraScreens[] = {
 	{0xFFC47, // girl book
 	 0x1180C9,
 	 8},
-	{0x1183C5, // book
+	/*{0x1183C5, // book
 	 0x1358F3,
-	 8},
+	 8},*/
+{	1147849,
+	1267955,
+	8
+},
 	{0x152A88, // portrait
 	 0x15BFC8,
 	 8},
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 3f26f1d81cc..55711136eaa 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -39,6 +39,7 @@
 #include "pelrock/computer.h"
 #include "pelrock/console.h"
 #include "pelrock/detection.h"
+#include "pelrock/extrascreens.h"
 #include "pelrock/fonts/small_font.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pathfinding.h"
@@ -156,7 +157,7 @@ void PelrockEngine::init() {
 		// setScreen(0, ALFRED_DOWN);
 		// setScreen(3, ALFRED_RIGHT);
 		// setScreen(22, ALFRED_DOWN);
-		setScreen(25, ALFRED_DOWN);
+		setScreen(28, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
@@ -1487,6 +1488,14 @@ void PelrockEngine::gameLoop() {
 		antiPiracyEffect();
 		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
 	}
+	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_s) {
+		SpellBook spellBook(_events, _res);
+		Spell *selectedSpell = spellBook.run();
+		if(selectedSpell != nullptr) {
+			_dialog->sayAlfred(selectedSpell->text);
+		}
+		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
+	}
 	renderScene();
 	// _events->waitForKey();
 	_screen->update();
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index f7fa6e94803..6fdec3ae0cf 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -24,7 +24,6 @@
 #include "pelrock/pelrock.h"
 #include "pelrock/room.h"
 #include "pelrock/util.h"
-#include "resources.h"
 
 namespace Pelrock {
 
@@ -358,10 +357,11 @@ Common::Array<Common::StringArray> ResourceManager::processTextData(byte *data,
 			pos++;
 			continue;
 		}
-		if (data[pos] == 0x00) {
+		if (data[pos] == 0x00 || data[pos] == 0x78) {
 			pos++;
 			continue;
 		}
+
 		if (data[pos] == CTRL_SPEAKER_ID) {
 			byte color = data[pos + 1];
 			desc.append(1, '@');
@@ -435,7 +435,7 @@ void ResourceManager::mergeRleBlocks(Common::SeekableReadStream *stream, uint32
 		readUntilBuda(stream, stream->pos(), thisBlock, blockSize);
 		uint8_t *block_data = nullptr;
 		size_t decompressedSize = rleDecompress(thisBlock, blockSize, 0, 640 * 400, &block_data, true);
-		debug("Decompressed block %d: %zu bytes, total %zu", i, decompressedSize, combined_size + decompressedSize);
+		// debug("Decompressed block %d: %zu bytes, total %zu", i, decompressedSize, combined_size + decompressedSize);
 		if (combined_size + decompressedSize > 640 * 400) {
 			debug("Warning: decompressed data exceeds output buffer size, truncating");
 			decompressedSize = 640 * 400 - combined_size;


Commit: 496946f4b27bbe27675015201494bd36e880bd3c
    https://github.com/scummvm/scummvm/commit/496946f4b27bbe27675015201494bd36e880bd3c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:02+02:00

Commit Message:
PELROCK: Implements Egyptian museum

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index ac8ea7ff1aa..aa4779cca8c 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -124,6 +124,19 @@ const ActionEntry actionTable[] = {
 	{89, PICKUP, &PelrockEngine::pickUpBoot},
 	{112, PICKUP, &PelrockEngine::pickupCondoms},
 
+	// Room 29
+	{434, OPEN, &PelrockEngine::openEgyptMuseumDoor},
+	{434, CLOSE, &PelrockEngine::closeEgyptMuseumDoor},
+
+	// Room 30
+	{435, PUSH, &PelrockEngine::pushSymbol1},
+	{436, PUSH, &PelrockEngine::pushSymbol2},
+	{437, PUSH, &PelrockEngine::pushSymbol3},
+	{438, PUSH, &PelrockEngine::pushSymbol4},
+
+	// Room 38
+	{81, PICKUP, &PelrockEngine::pickUpHairStrand},
+
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
 	{WILDCARD, TALK, &PelrockEngine::noOpAction},   // Generic talk action
@@ -1041,6 +1054,70 @@ void PelrockEngine::pickupCondoms(HotSpot *hotspot) {
 	_room->addSticker(100);
 }
 
+void PelrockEngine::openEgyptMuseumDoor(HotSpot *hotspot) {
+	openDoor(hotspot, 0, 59, MASCULINE, false);
+}
+
+void PelrockEngine::closeEgyptMuseumDoor(HotSpot *hotspot) {
+	closeDoor(hotspot, 0, 59, MASCULINE, false);
+}
+
+void PelrockEngine::pushSymbol1(HotSpot *hotspot) {
+	if(_state->getFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO) == true) {
+		byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
+		debug("Current symbols pulled: %d", symbolsPulled);
+		_state->setFlag(FLAG_SYMBOLS_PUSHED, symbolsPulled | 0x1);
+		debug("New symbols pulled: %d", _state->getFlag(FLAG_SYMBOLS_PUSHED));
+		checkAllSymbols();
+	}
+}
+
+void PelrockEngine::pushSymbol2(HotSpot *hotspot) {
+	if(_state->getFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO) == true) {
+		byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
+		debug("Current symbols pulled: %d", symbolsPulled);
+		_state->setFlag(FLAG_SYMBOLS_PUSHED, symbolsPulled | 0x2);
+		debug("New symbols pulled: %d", _state->getFlag(FLAG_SYMBOLS_PUSHED));
+		checkAllSymbols();
+	}
+}
+
+void PelrockEngine::pushSymbol3(HotSpot *hotspot) {
+	if(_state->getFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO) == true) {
+		byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
+		debug("Current symbols pulled: %d", symbolsPulled);
+		_state->setFlag(FLAG_SYMBOLS_PUSHED, symbolsPulled | 0x4);
+		debug("New symbols pulled: %d", _state->getFlag(FLAG_SYMBOLS_PUSHED));
+		checkAllSymbols();
+	}
+}
+
+void PelrockEngine::pushSymbol4(HotSpot *hotspot) {
+	if(_state->getFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO) == true) {
+		byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
+		debug("Current symbols pulled: %d", symbolsPulled);
+		_state->setFlag(FLAG_SYMBOLS_PUSHED, symbolsPulled | 0x8);
+		debug("New symbols pulled: %d", _state->getFlag(FLAG_SYMBOLS_PUSHED));
+		checkAllSymbols();
+	}
+}
+
+void PelrockEngine::pickUpHairStrand(HotSpot *hotspot) {
+	checkIngredients();
+}
+
+void PelrockEngine::checkAllSymbols() {
+	byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
+	debug("Checking symbols, current value: %d", symbolsPulled);
+	if(symbolsPulled == 0x0F) {
+		//Activates animation
+		_room->enableSprite(4, 100, PERSIST_TEMP);
+		_room->enableExit(0, PERSIST_BOTH);
+		_room->addSticker(61);
+		_room->addSticker(63);
+	}
+}
+
 void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	debug("Performing action trigger: %d", actionTrigger);
 	switch (actionTrigger) {
@@ -1070,6 +1147,9 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	case 282:
 		_dialog->say(_res->_ingameTexts[SELORECOMIENDO]);
 		break;
+	case 327:
+		_state->setFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO, true);
+		break;
 	}
 }
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 55711136eaa..87b09417263 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -157,7 +157,7 @@ void PelrockEngine::init() {
 		// setScreen(0, ALFRED_DOWN);
 		// setScreen(3, ALFRED_RIGHT);
 		// setScreen(22, ALFRED_DOWN);
-		setScreen(28, ALFRED_DOWN);
+		setScreen(30, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
@@ -1180,6 +1180,10 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 				animData.curFrame = 0;
 				animData.curLoop++;
 			} else {
+				if(sprite->disableAfterSequence) {
+					sprite->zOrder = -1;
+					return;
+				}
 				animData.curFrame = 0;
 				animData.curLoop = 0;
 				if (sprite->curAnimIndex < sprite->numAnims - 1) {
@@ -1356,7 +1360,7 @@ Exit *PelrockEngine::isExitUnder(int x, int y) {
 		Exit exit = _room->_currentRoomExits[i];
 		// Original game uses: x <= exit.x + exit.w - 1 and y <= exit.y + exit.h - 1
 		if (x >= exit.x && x <= (exit.x + exit.w - 1) &&
-			y >= exit.y && y <= (exit.y + exit.h - 1) && exit.isEnabled) {
+			y >= exit.y && y <= (exit.y + exit.h - 1) /*&& exit.isEnabled*/) {
 			return &(_room->_currentRoomExits[i]);
 		}
 	}
@@ -1813,7 +1817,18 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			_dialog->say(_res->_ingameTexts[QUIENYO]);
 			_dialog->say(_res->_ingameTexts[PINTA_BUENAPERSONA]);
 		}
-
+	case 38: {
+		int x = _alfredState.x;
+		int y = _alfredState.y;
+		_alfredState.x -= 57;
+		_alfredState.y += 2;
+		_res->loadAlfredSpecialAnim(6);
+		_alfredState.setState(ALFRED_SPECIAL_ANIM);
+		waitForSpecialAnimation();
+		_alfredState.x = x;
+		_alfredState.y = y;
+		break;
+	}
 	default:
 		break;
 	}
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 820c9354bd0..2302c045bdc 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -329,6 +329,14 @@ public:
 	void pickUpSpellbook(HotSpot *hotspot);
 	void pickUpBoot(HotSpot *hotspot);
 	void pickupCondoms(HotSpot *hotspot);
+	void openEgyptMuseumDoor(HotSpot *hotspot);
+	void closeEgyptMuseumDoor(HotSpot *hotspot);
+	void pushSymbol1(HotSpot *hotspot);
+	void pushSymbol2(HotSpot *hotspot);
+	void pushSymbol3(HotSpot *hotspot);
+	void pushSymbol4(HotSpot *hotspot);
+	void pickUpHairStrand(HotSpot *hotspot);
+	void checkAllSymbols();
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
 
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 6fdec3ae0cf..b46915b8def 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -36,12 +36,13 @@ ResourceManager::ResourceManager(/* args */) {
 }
 
 const AlfredSpecialAnimOffset ResourceManager::alfredSpecialAnims[] = {
-	{10, 51, 102, 1, 7, 559685, 1, }, // READ BOOK
+	{10, 51, 102, 1, 7, 559685, 1,}, // READ BOOK
 	{10, 51, 102, 1, 7, 578943, 1}, // READ RECIPE
 	{3, 45, 87, 0, 7, 37000, 1}, // ELECTRIC SHOCK 1
 	{2, 82, 58, 0, 7, 53106, 20}, // ELECTRIC SHOCK 3
 	{3, 71, 110, 1, 2, 20724, 1, 62480}, // Throw
 	{14, 171, 107, 1, 7, 1556540, 1} , //crocodile
+	{12, 113, 103, 1, 7, 1583702, 1} // exit through manhole
 };
 
 ResourceManager::~ResourceManager() {
@@ -244,7 +245,11 @@ void ResourceManager::loadAlfredSpecialAnim(int numAnim, bool reverse) {
 	_currentSpecialAnim->animData = new byte[size];
 	if (anim.numBudas > 0) {
 		debug("Loading special anim with budas: numBudas=%d, totalSize %d", anim.numBudas, size);
-		mergeRleBlocks(&alfredFile, anim.offset, anim.numBudas, _currentSpecialAnim->animData);
+		byte *thisBlock = nullptr;
+		size_t blockSize = 0;
+		readUntilBuda(&alfredFile, anim.offset, thisBlock, blockSize);
+		rleDecompress(thisBlock, blockSize, 0, size, &_currentSpecialAnim->animData, false);
+		delete[] thisBlock;
 	} else {
 		alfredFile.read(_currentSpecialAnim->animData, anim.numFrames * anim.w * anim.h);
 	}
@@ -435,7 +440,7 @@ void ResourceManager::mergeRleBlocks(Common::SeekableReadStream *stream, uint32
 		readUntilBuda(stream, stream->pos(), thisBlock, blockSize);
 		uint8_t *block_data = nullptr;
 		size_t decompressedSize = rleDecompress(thisBlock, blockSize, 0, 640 * 400, &block_data, true);
-		// debug("Decompressed block %d: %zu bytes, total %zu", i, decompressedSize, combined_size + decompressedSize);
+		debug("Decompressed block %d: %zu bytes, total %zu", i, decompressedSize, combined_size + decompressedSize);
 		if (combined_size + decompressedSize > 640 * 400) {
 			debug("Warning: decompressed data exceeds output buffer size, truncating");
 			decompressedSize = 640 * 400 - combined_size;
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 495c43fce1e..52f556912a4 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -898,6 +898,7 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 		debug("Sprite %d: x=%d y=%d w=%d h=%d stride=%d numAnims=%d zOrder=%d extra=%d", i, sprite.x, sprite.y, sprite.w, sprite.h, sprite.stride, sprite.numAnims, sprite.zOrder, sprite.extra);
 		sprite.actionFlags = data[animOffset + 34];
 		sprite.isHotspotDisabled = data[animOffset + 38];
+		sprite.disableAfterSequence = data[animOffset + 39];
 		for (int j = 0; j < spriteChanges.size(); j++) {
 			if (spriteChanges[j].spriteIndex == sprite.index) {
 				sprite.zOrder = spriteChanges[j].zIndex;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index e0f96b70891..1bcd8a17d54 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -235,6 +235,7 @@ struct Sprite {
 
 	byte actionFlags;       // 34
 	bool isHotspotDisabled; // 38
+	bool disableAfterSequence = false; // 39
 	bool isTalking = false;
 	Anim *animData;
 	int16 extra;
@@ -466,12 +467,12 @@ struct ResetEntry {
 #define FLAG_ALFRED_SABE_EGIPCIO 10
 #define FLAG_VENDEDOR_DEJA_DE_JODER 11
 #define FLAG_PARADOJA_RESUELTA 13
+#define FLAG_MIRA_SIMBOLO_FUERA_MUSEO 15
+#define FLAG_CROCODILLO_ENCENDIDO 14
 
 
 
 #define FLAG_VIAJE_A_EGIPTO 12
-#define FLAG_CROCODILLO_ENCENDIDO 14
-#define FLAG_MIRA_SIMBOLO_FUERA_MUSEO 15
 #define FLAG_PUERTA_SECRETA_ABIERTA 16
 #define FLAG_ROBA_PELO_PRINCESA 17
 #define FLAG_A_LA_CARCEL 18
@@ -514,8 +515,9 @@ struct ResetEntry {
 #define FLAG_RESPUESTAS_ACERTADAS 54
 #define FLAG_CHEAT_CODE_ENABLED 55    // 0x495F3 - enables HIJODELAGRANPUTA cheat code input
 #define FLAG_RIDDLE_PRESENTED 56
+#define FLAG_SYMBOLS_PUSHED 57
 
-const int kNumGameFlags = 57;
+const int kNumGameFlags = 58;
 
 struct GameStateData {
 	byte flags[kNumGameFlags];


Commit: 87d5093bfd6002dcba4c741cbf905fb88b87fda3
    https://github.com/scummvm/scummvm/commit/87d5093bfd6002dcba4c741cbf905fb88b87fda3
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:02+02:00

Commit Message:
PELROCK: Play sound when opening secret door

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index aa4779cca8c..58b5585c4e1 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1111,6 +1111,7 @@ void PelrockEngine::checkAllSymbols() {
 	debug("Checking symbols, current value: %d", symbolsPulled);
 	if(symbolsPulled == 0x0F) {
 		//Activates animation
+		_sound->playSound(_room->_roomSfx[0]);
 		_room->enableSprite(4, 100, PERSIST_TEMP);
 		_room->enableExit(0, PERSIST_BOTH);
 		_room->addSticker(61);
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 0af6d1883c0..1ed37e6f326 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -401,30 +401,30 @@ const uint16_t description_offsets[NUM_DESCRIPTIONS] = {
 	0x1D7A, // Object 77: Un poco de escayola vieja
 	0x1DA3, // Object 78: Un cacho de ladrillo
 	0x1DCC, // Object 79: Un boligrafo
-	0x1DE5, // Object 80: Un radiocasete
-	0x1E00, // Object 81: Es una pistola. (Real como la vida misma)
-	0x1E49, // Object 82: Una pieza de fruta
-	0x1E6B, // Object 83: Un frasco de pastillas para dormir
-	0x1EA0, // Object 84: Una pulsera
-	0x1EB9, // Object 85: Una estatua pequeña
-	0x1EE0, // Object 86: Una jodida (disculpen las molestias) cinta de video
-	0x1F4F, // Object 87: Una jodida (disculpen las molestias) cadena hifi
-	0x1FBE, // Object 88: Una magdalena
-	0x1FD8, // Object 89: Un poco de cecina
-	0x1FFC, // Object 90: Un televisor portatil con forma de Mickey Mouse
-	0x2053, // Object 91: Un destornillador
-	0x2071, // Object 92: Alicates de electricista
-	0x2097, // Object 93: Un cable
-	0x20AE, // Object 94: Una linterna
-	0x20C6, // Object 95: Unas pilas gigantes
-	0x20E7, // Object 96: La bolsa de basura negra
-	0x2116, // Object 97: Foto de un tal Gerardo (desconocido)
-	0x2158, // Object 98: Una cinta de casete
-	0x2179, // Object 99: Un walkman
-	0x218F, // Object 100: Un papel con un telefono
-	0x21BE, // Object 101: Una llave grande de metacrilato
-	0x21F1, // Object 102: Una pequeña llave
-	0x220E, // Object 103: Autenticas naranjas de Nules
+	0x1DE5, // Object 80: Un radiocasete < --- aqui
+	0x1E00, // Object 81: El pelo de una princesa egipcia
+	0x1E49, // Object 82: Un mogollon de pasta
+	0x1E6B, // Object 83: Una replica de Elvis
+	0x1EA0, // Object 84: Aunque no sean piramides se puede decir que son monumentos
+	0x1EB9, // Object 85: Un girasol
+	0x1EE0, // Object 86: Una calabaza llena de agua
+	0x1F4F, // Object 87: Una motosierra
+	0x1FBE, // Object 88: Un libro de recetas magicas
+	0x1FD8, // Object 89: Una bota usada
+	0x1FFC, // Object 90: Una autentica piedra egipcia
+	0x2053, // Object 91: Una piedra egipcia
+	0x2071, // Object 92: Un poco de barro
+	0x2097, // Object 93: Licor de arena
+	0x20AE, // Object 94: Crema para el sol
+	0x20C6, // Object 95: Banda sonora de alfred pelrock
+	0x20E7, // Object 96: Un album de pantallas
+	0x2116, // Object 97: Plano de la piramide
+	0x2158, // Object 98: Plano detallado
+	0x2179, // Object 99: Es una peluca
+	0x218F, // Object 100: Una pequeña llave
+	0x21BE, // Object 101: Un trozo de papel con unos numeritos
+	0x21F1, // Object 102: Autenticas naranjas de Nules
+	0x220E, // Object 103: Un mogollon de naranjas de Nules
 	0x2236, // Object 104: No se haga el loco: Llameme !!!
 	0x2285, // Object 105: Folletos explicativos sobre el SIDA
 	0x22BE, // Object 106: Un pin que acredita mi sabiduria
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 87b09417263..d72895aae7b 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1360,7 +1360,7 @@ Exit *PelrockEngine::isExitUnder(int x, int y) {
 		Exit exit = _room->_currentRoomExits[i];
 		// Original game uses: x <= exit.x + exit.w - 1 and y <= exit.y + exit.h - 1
 		if (x >= exit.x && x <= (exit.x + exit.w - 1) &&
-			y >= exit.y && y <= (exit.y + exit.h - 1) /*&& exit.isEnabled*/) {
+			y >= exit.y && y <= (exit.y + exit.h - 1) && exit.isEnabled) {
 			return &(_room->_currentRoomExits[i]);
 		}
 	}


Commit: 556703dc941ed85bb196923369747a8e6e4f84ac
    https://github.com/scummvm/scummvm/commit/556703dc941ed85bb196923369747a8e6e4f84ac
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:02+02:00

Commit Message:
PELROCK: Implements Rooms 31 and 32

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/menu.cpp
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/saveload.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 58b5585c4e1..1979779bc92 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -137,6 +137,10 @@ const ActionEntry actionTable[] = {
 	// Room 38
 	{81, PICKUP, &PelrockEngine::pickUpHairStrand},
 
+	// Room 31
+	{462, OPEN, &PelrockEngine::openJailFloorTile},
+	// Room 32
+	{473, OPEN, &PelrockEngine::openTunnelDrawer},
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
 	{WILDCARD, TALK, &PelrockEngine::noOpAction},   // Generic talk action
@@ -467,7 +471,13 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_state->setFlag(FLAG_PARADOJA_RESUELTA, 1);
 		_state->setCurrentRoot(room, 1);
 		break;
-
+	case 292:
+		_state->setCurrentRoot(room, rootIndex + 1);
+		break;
+	case 293:
+		_room->disableHotspot(_room->findHotspotByExtra(451)); // Disable talk hotspot for the riddle
+		_state->setCurrentRoot(room, rootIndex + 1);
+		break;
 	default:
 		debug("Got actionTrigger %d in dialogActionTrigger, but no handler defined", actionTrigger);
 		break;
@@ -956,7 +966,7 @@ void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
 	_alfredState.x -= 10;
 	_alfredState.y += 20;
 	waitForSpecialAnimation();
-	_sound->playSound(_room->_roomSfx[0], 100, 0); // Belch
+	_sound->playSound(_room->_roomSfx[0], 0); // Belch
 	bool isPlaying = true;
 	while (!shouldQuit() && isPlaying) {
 		_events->pollEvent();
@@ -1102,10 +1112,6 @@ void PelrockEngine::pushSymbol4(HotSpot *hotspot) {
 	}
 }
 
-void PelrockEngine::pickUpHairStrand(HotSpot *hotspot) {
-	checkIngredients();
-}
-
 void PelrockEngine::checkAllSymbols() {
 	byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
 	debug("Checking symbols, current value: %d", symbolsPulled);
@@ -1119,6 +1125,24 @@ void PelrockEngine::checkAllSymbols() {
 	}
 }
 
+void PelrockEngine::pickUpHairStrand(HotSpot *hotspot) {
+	checkIngredients();
+}
+
+void PelrockEngine::openJailFloorTile(HotSpot *hotspot) {
+	if (_room->hasSticker(77)) {
+		_dialog->say(_res->_ingameTexts[YA_ABIERTO_M]);
+		return;
+	}
+	_room->enableExit(0, PERSIST_BOTH);
+	_room->addSticker(77, PERSIST_BOTH);
+}
+
+void PelrockEngine::openTunnelDrawer(HotSpot *hotspot) {
+	_room->addSticker(78, PERSIST_BOTH);
+	_room->disableHotspot(hotspot);
+}
+
 void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	debug("Performing action trigger: %d", actionTrigger);
 	switch (actionTrigger) {
@@ -1151,6 +1175,10 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	case 327:
 		_state->setFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO, true);
 		break;
+	case 294:
+		HotSpot *floorTile = _room->findHotspotByExtra(462);
+		floorTile->actionFlags = ACTION_MASK_OPEN;
+		_room->changeHotSpot(*floorTile);
 	}
 }
 
@@ -1459,7 +1487,7 @@ void PelrockEngine::antiPiracyEffect() {
 	}
 
 	// Play the noise
-	_sound->playSound(noiseData, kNoiseLength + 44, 200);
+	_sound->playSound(noiseData, kNoiseLength + 44);
 
 	byte *screenPixels = (byte *)_screen->getPixels();
 	int screenSize = _screen->pitch * _screen->h;
@@ -1488,7 +1516,7 @@ void PelrockEngine::antiPiracyEffect() {
 			}
 		}
 		if (!_sound->isPlaying()) {
-			_sound->playSound(noiseData, kNoiseLength + 44, 200);
+			_sound->playSound(noiseData, kNoiseLength + 44);
 		}
 
 		_screen->markAllDirty();
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index ecefc64faff..82055ccf378 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -215,9 +215,9 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 
 		_screen->transBlitFrom(s, s.getRect(), Common::Point(xPos, yPos), 255);
 		drawPos(_screen, xPos, yPos, speakerId);
-		drawRect(_screen, xPos, yPos,
-				 s.getRect().width(),
-				 s.getRect().height(), speakerId);
+		// drawRect(_screen, xPos, yPos,
+		// 		 s.getRect().width(),
+		// 		 s.getRect().height(), speakerId);
 		// Present to screen
 		_screen->markAllDirty();
 		_screen->update();
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index b67c763260e..2110525df6e 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -110,7 +110,7 @@ bool MenuManager::selectInventoryItem(int i) {
 
 	_selectedInvIndex = g_engine->_state->inventoryItems[_curInventoryPage * 4 + i];
 	_menuText = _inventoryDescriptions[_selectedInvIndex];
-	_sound->playSound(inventorySounds[_selectedInvIndex], 100, 0);
+	_sound->playSound(inventorySounds[_selectedInvIndex], 0);
 	g_engine->_state->selectedInventoryItem = _selectedInvIndex;
 	debug("Selected inventory item %d", _selectedInvIndex);
 	return true;
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 1ed37e6f326..f77ae9531c4 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -338,9 +338,10 @@ const uint16_t description_offsets[NUM_DESCRIPTIONS] = {
 	0x039E, // Object 14: Titulo: El sistema inmunologico de los cefalopodos (v.I)
 	0x0412, // Object 15: Titulo: Dos y dos son 5
 	0x0493, // Object 16: Titulo: La parte creativa
-	0x057B, // Object 17: Titulo: 10 maneras de preparar fideos chinos
-	0x065C, // Object 18: Titulo: Los Peces Gato del Rio Tajo
-	0x06D8, // Object 19: Titulo: Gato por liebre
+	0x057B, // Object 17: Titulo: Aprenda egipcio en 10 dias
+	0x065C, // Object 18: Titulo: Gato por liebre
+	0x06D8, // Object 19: Titulo: Enrique de Ofterdingeng
+
 	0x0753, // Object 20: Titulo: Hiper-cocina para solteros
 	0x07CE, // Object 21: Titulo: El camaleon humano
 	0x084E, // Object 22: Titulo: Psiquiatria Avanzada (vol. 8)
@@ -379,29 +380,30 @@ const uint16_t description_offsets[NUM_DESCRIPTIONS] = {
 	0x185D, // Object 55: Titulo: Los Heraldos Negros
 	0x18D1, // Object 56: Titulo: La Piedra Rosetta
 	0x194A, // Object 57: Titulo: Fabulas de Ciencia Ficcion
-	0x19C3, // Object 58: Titulo: Elogio de la pereza
-	0x1A35, // Object 59: Un pequeño altavoz
-	0x1A52, // Object 60: Un altavoz mediano
-	0x1A70, // Object 61: Un altavoz grande
-	0x1A8C, // Object 62: Un extintor
-	0x1AA3, // Object 63: Parece un extintor, pero es un termo de cafe. De los de antes, de los grandes
-	0x1B2D, // Object 64: Un termo con cafe
-	0x1B53, // Object 65: Una cafetera expres para una taza
-	0x1B8D, // Object 66: La tarjeta de acceso de Erika
-	0x1BBE, // Object 67: Billetes de avion para salir por patas hacia Valencia
-	0x1C26, // Object 68: Una bolsa de patatas fritas
-	0x1C52, // Object 69: Llave de la habitacion de Lucy
-	0x1C82, // Object 70: Llave de la habitacion de Erika
-	0x1CB3, // Object 71: Un ordenador de sobremesa
-	0x1CE1, // Object 72: Un cuadro
-	0x1CF9, // Object 73: Una moto mega-retuneada
-	0x1D24, // Object 74: Una lamparita
-	0x1D3F, // Object 75: Un libro gordo
-	0x1D5C, // Object 76: Un libro finito
-	0x1D7A, // Object 77: Un poco de escayola vieja
-	0x1DA3, // Object 78: Un cacho de ladrillo
-	0x1DCC, // Object 79: Un boligrafo
-	0x1DE5, // Object 80: Un radiocasete < --- aqui
+	0x19C3, // Object 58: Titulo: La musica amansa a las fieras
+
+	0x1A35, // Object 59: Un libro de recetas magicas
+	0x1A52, // Object 60: Un bote de tomate
+	0x1A70, // Object 61: Un bote de mostaza
+	0x1A8C, // Object 62: Salsa ultra-picante
+	0x1AA3, // Object 63: Receta de las hamburguesas de McDowells
+	0x1B2D, // Object 64: Un papiro con una inscripcion jeroglifica
+	0x1B53, // Object 65: Una guitarra española
+	0x1B8D, // Object 66: Un pez disecado
+	0x1BBE, // Object 67: Un osito de peluche
+	0x1C26, // Object 68: Unos discos antiguos
+	0x1C52, // Object 69: Un cerebro de mono
+	0x1C82, // Object 70: Novelas del salvaje oeste
+	0x1CB3, // Object 71: Una paleta
+	0x1CE1, // Object 72: Caramelos de todos los sabores
+	0x1CF9, // Object 73: Una caracola
+	0x1D24, // Object 74: Un sombrero
+	0x1D3F, // Object 75: 150000 pesetas
+	0x1D5C, // Object 76: Una calabaza para meter agua dentro
+	0x1D7A, // Object 77: Henna roja para el pelo
+	0x1DA3, // Object 78: Piramides de recuerdo
+	0x1DCC, // Object 79: Un chupa-chup de higo chumbo
+	0x1DE5, // Object 80: Un amuleto egipcio
 	0x1E00, // Object 81: El pelo de una princesa egipcia
 	0x1E49, // Object 82: Un mogollon de pasta
 	0x1E6B, // Object 83: Una replica de Elvis
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 2302c045bdc..3338db34518 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -336,6 +336,8 @@ public:
 	void pushSymbol3(HotSpot *hotspot);
 	void pushSymbol4(HotSpot *hotspot);
 	void pickUpHairStrand(HotSpot *hotspot);
+	void openJailFloorTile(HotSpot *hotspot);
+	void openTunnelDrawer(HotSpot *hotspot);
 	void checkAllSymbols();
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 52f556912a4..0f6fb18e071 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -472,7 +472,7 @@ Common::Array<HotSpot> RoomManager::loadHotspots(byte *data, size_t size) {
 			// if the hotspot has been changed, load the changed version
 			for (int j = 0; j < g_engine->_state->roomHotSpotChanges[_currentRoomNumber].size(); j++) {
 				if (g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspotIndex == spot.innerIndex) {
-					debug("Hotspot %d has been changed, loading changed version, Hotspot x=%d, y = %d, extra = %d", spot.innerIndex, g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot.x, g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot.y, g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot.extra);
+					debug("Hotspot %d has been changed, loading changed version, Hotspot x=%d, y = %d, extra = %d, isEnabled=%d", spot.innerIndex, g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot.x, g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot.y, g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot.extra, g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot.isEnabled);
 					hotspots.push_back(g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot);
 					isChanged = true;
 					break;
diff --git a/engines/pelrock/saveload.cpp b/engines/pelrock/saveload.cpp
index d6dcf7dbc72..06e00ae088f 100644
--- a/engines/pelrock/saveload.cpp
+++ b/engines/pelrock/saveload.cpp
@@ -77,7 +77,7 @@ void syncHotSpot(Common::Serializer &s, HotSpot &hotspot) {
 	s.syncAsSint16LE(hotspot.w);
 	s.syncAsSint16LE(hotspot.h);
 	s.syncAsByte(hotspot.actionFlags);
-	s.syncAsByte(hotspot.extra);
+	s.syncAsSint16LE(hotspot.extra);
 	s.syncAsByte((byte &)hotspot.isEnabled);
 	s.syncAsByte((byte &)hotspot.isSprite);
 	s.syncAsByte(hotspot.zOrder);
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 5f519e9e4d8..6eee91a28e4 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -49,26 +49,26 @@ SoundManager::~SoundManager() {
 	stopMusic();
 }
 
-void SoundManager::playSound(byte index, int volume, int channel) {
+void SoundManager::playSound(byte index, int channel) {
 	// debug("Playing sound index %d (%s)", index, SOUND_FILENAMES[index]);
 	auto it = _soundMap.find(SOUND_FILENAMES[index]);
 	if (it != _soundMap.end()) {
-		playSound(it->_value, volume);
+		playSound(it->_value);
 	} else {
 		debug("Sound file %s not found in sound map", SOUND_FILENAMES[index]);
 	}
 }
 
-void SoundManager::playSound(const char *filename, int volume, int channel) {
+void SoundManager::playSound(const char *filename, int channel) {
 	auto it = _soundMap.find(filename);
 	if (it != _soundMap.end()) {
-		playSound(it->_value, volume);
+		playSound(it->_value);
 	} else {
 		debug("Sound file %s not found in sound map", filename);
 	}
 }
 
-void SoundManager::playSound(SonidoFile sound, int volume, int channel) {
+void SoundManager::playSound(SonidoFile sound, int channel) {
 	Common::File sonidosFile;
 	if (!sonidosFile.open(Common::Path("SONIDOS.DAT"))) {
 		debug("Failed to open SONIDOS.DAT");
@@ -117,15 +117,15 @@ void SoundManager::playSound(SonidoFile sound, int volume, int channel) {
 				_mixer->stopHandle(_sfxHandles[channel]);
 			}
 		}
-		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, volume, 0, DisposeAfterUse::YES);
+		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, 255U, 0, DisposeAfterUse::YES);
 	}
 }
 
-void SoundManager::playSound(byte *soundData, uint32 size, int volume) {
+void SoundManager::playSound(byte *soundData, uint32 size) {
 	Audio::AudioStream *stream = Audio::makeRawStream(soundData, size, 11025, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
 	if (stream) {
 		int channel = findFreeChannel();
-		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, volume, 0, DisposeAfterUse::YES);
+		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, 255U, 0, DisposeAfterUse::YES);
 	}
 }
 
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index de38a2fb4c1..bd3f27ed6db 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -157,9 +157,9 @@ class SoundManager {
 public:
 	SoundManager(Audio::Mixer *mixer);
 	~SoundManager();
-	void playSound(byte index, int volume = 128, int channel = -1);
-	void playSound(const char *filename, int volume, int channel);
-	void playSound(byte *soundData, uint32 size, int volume = 128);
+	void playSound(byte index, int channel = -1);
+	void playSound(const char *filename, int channel);
+	void playSound(byte *soundData, uint32 size);
 	void stopAllSounds();
 	void stopSound(int channel);
 	void stopMusic();
@@ -182,7 +182,7 @@ public:
 	int tickAmbientSound(uint32 frameCount);
 
 private:
-	void playSound(SonidoFile sound, int volume = 255, int channel = -1);
+	void playSound(SonidoFile sound, int channel = -1);
 	SoundFormat detectFormat(byte *data, uint32 size);
 	int getSampleRate(byte *data, SoundFormat format);
 	int findFreeChannel();
@@ -196,6 +196,7 @@ private:
 	Audio::SoundHandle _musicHandle;
 	Audio::SoundHandle _sfxHandles[kMaxChannels];
 	Common::HashMap<Common::String, SonidoFile> _soundMap;
+
 };
 
 } // End of namespace Pelrock


Commit: 426763e30a769c89965ea8b527cfbf9943023da5
    https://github.com/scummvm/scummvm/commit/426763e30a769c89965ea8b527cfbf9943023da5
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:02+02:00

Commit Message:
PELROCK: Implements Room 33

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/console.cpp
    engines/pelrock/console.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/room.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 1979779bc92..67c40827c01 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -141,6 +141,11 @@ const ActionEntry actionTable[] = {
 	{462, OPEN, &PelrockEngine::openJailFloorTile},
 	// Room 32
 	{473, OPEN, &PelrockEngine::openTunnelDrawer},
+	// Room 33
+	{651, OPEN, &PelrockEngine::openSafe},
+	{465, OPEN, &PelrockEngine::openTunnelDoor},
+	{465, CLOSE, &PelrockEngine::closeTunnelDoor},
+
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
 	{WILDCARD, TALK, &PelrockEngine::noOpAction},   // Generic talk action
@@ -167,6 +172,7 @@ const CombinationEntry combinationTable[] = {
 	{8, 358, &PelrockEngine::giveSecretCodeToLibrarian},
 	{4, 358, &PelrockEngine::useBrickWithLibrarian}, // Any hotspot in the lamppost will work
 	{76, 469, &PelrockEngine::usePumpkinWithRiver},
+	{100, 650, &PelrockEngine::useKeyWithPortrait},
 	// End marker
 	{WILDCARD, WILDCARD, nullptr}};
 
@@ -497,6 +503,7 @@ void PelrockEngine::toJail() {
 	_alfredState.x = 342;
 	_alfredState.y = 277;
 	setScreen(31, ALFRED_DOWN);
+	_room->moveHotspot(_room->findHotspotByExtra(101), 444, 166);
 }
 
 void PelrockEngine::noOpAction(HotSpot *hotspot) {
@@ -1143,6 +1150,29 @@ void PelrockEngine::openTunnelDrawer(HotSpot *hotspot) {
 	_room->disableHotspot(hotspot);
 }
 
+void PelrockEngine::useKeyWithPortrait(int inventoryObject, HotSpot *hotspot) {
+	_room->disableHotspot(hotspot);
+	_room->addSticker(101);
+}
+
+void PelrockEngine::openSafe(HotSpot *hotspot) {
+	if(_state->getFlag(FLAG_CLAVE_CAJA_FUERTE) == true) {
+		_room->addSticker(102);
+		_dialog->say(_res->_ingameTexts[GRANCANTIDAD_DINERO]);
+		addInventoryItem(82);
+	} else {
+		_dialog->say(_res->_ingameTexts[SISUPIERA_COMBINACION]);
+	}
+}
+
+void PelrockEngine::openTunnelDoor(HotSpot *hotspot) {
+	openDoor(hotspot, 0, 66, MASCULINE, true);
+}
+
+void PelrockEngine::closeTunnelDoor(HotSpot *hotspot) {
+	closeDoor(hotspot, 0, 66, MASCULINE, true);
+}
+
 void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	debug("Performing action trigger: %d", actionTrigger);
 	switch (actionTrigger) {
@@ -1289,6 +1319,13 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		waitForSpecialAnimation();
 		_dialog->say(_res->_ingameTexts[CUENTOPARECIDO]);
 		break;
+	case 101: // combination
+		_res->loadAlfredSpecialAnim(1);
+		_alfredState.animState = ALFRED_SPECIAL_ANIM;
+		waitForSpecialAnimation();
+		_dialog->say(_res->_ingameTexts[PARECE_COMBINACION_CAJAFUERTE]);
+		_state->setFlag(FLAG_CLAVE_CAJA_FUERTE, true);
+		break;
 	default:
 		break;
 	}
diff --git a/engines/pelrock/console.cpp b/engines/pelrock/console.cpp
index d7cdabcb528..404512862a5 100644
--- a/engines/pelrock/console.cpp
+++ b/engines/pelrock/console.cpp
@@ -29,15 +29,16 @@ namespace Pelrock {
 PelrockConsole::PelrockConsole(PelrockEngine *engine) : GUI::Debugger(), _engine(engine) {
 	registerCmd("room", WRAP_METHOD(PelrockConsole, cmdLoadRoom));
 	registerCmd("give", WRAP_METHOD(PelrockConsole, cmdGiveItems));
-	registerCmd("setRoot", WRAP_METHOD(PelrockConsole, setRoot));
-	registerCmd("setFlag", WRAP_METHOD(PelrockConsole, setFlag));
+	registerCmd("setRoot", WRAP_METHOD(PelrockConsole, cmdSetRoot));
+	registerCmd("setFlag", WRAP_METHOD(PelrockConsole, cmdSetFlag));
+	registerCmd("toJail", WRAP_METHOD(PelrockConsole, cmdToJail));
 }
 
 PelrockConsole::~PelrockConsole() {
 }
 
 
-bool PelrockConsole::setFlag(int argc, const char **argv) {
+bool PelrockConsole::cmdSetFlag(int argc, const char **argv) {
 	if (argc < 3) {
 		debugPrintf("Usage: setFlag <flagIndex> <value>");
 		return true;
@@ -49,7 +50,7 @@ bool PelrockConsole::setFlag(int argc, const char **argv) {
 	return true;
 }
 
-bool PelrockConsole::setRoot(int argc, const char **argv) {
+bool PelrockConsole::cmdSetRoot(int argc, const char **argv) {
 	if (argc < 3) {
 		debugPrintf("Usage: setRoot <roomNumber> <rootIndex>");
 		return true;
@@ -90,4 +91,9 @@ bool PelrockConsole::cmdGiveItems(int argc, const char **argv) {
 	return true;
 }
 
+bool PelrockConsole::cmdToJail(int argc, const char **argv) {
+	g_engine->toJail();
+	return true;
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/console.h b/engines/pelrock/console.h
index aafaa3afc39..ff86ea92f52 100644
--- a/engines/pelrock/console.h
+++ b/engines/pelrock/console.h
@@ -33,9 +33,10 @@ private:
 	PelrockEngine *_engine;
 	bool cmdLoadRoom(int argc, const char **argv);
 	bool cmdGiveItems(int argc, const char **argv);
+	bool cmdToJail(int argc, const char **argv);
 	bool cmdTest(int argc, const char **argv);
-	bool setRoot(int argc, const char **argv);
-	bool setFlag(int argc, const char **argv);
+	bool cmdSetRoot(int argc, const char **argv);
+	bool cmdSetFlag(int argc, const char **argv);
 
 public:
 	PelrockConsole(PelrockEngine *engine);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index d72895aae7b..f9afd57cf65 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -880,6 +880,11 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 				Exit *exit = isExitUnder(_alfredState.x, _alfredState.y);
 				if (exit != nullptr) {
 					debug("Using exit to room %d", exit->targetRoom);
+					if(exit->targetRoom == 31 && _room->_currentRoomNumber == 32) {
+						_res->loadAlfredSpecialAnim(8);
+						_alfredState.setState(ALFRED_SPECIAL_ANIM);
+						waitForSpecialAnimation();
+					}
 					_alfredState.x = exit->targetX;
 					_alfredState.y = exit->targetY;
 					setScreen(exit->targetRoom, exit->dir);
@@ -1723,7 +1728,6 @@ void PelrockEngine::checkMouseHover() {
 }
 
 void PelrockEngine::setScreen(int roomNumber, AlfredDirection dir) {
-
 	Common::File roomFile;
 	if (!roomFile.open(Common::Path("ALFRED.1"))) {
 		error("Could not open ALFRED.1");
@@ -1829,6 +1833,20 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		_alfredState.y = y;
 		break;
 	}
+	case 32: {
+		if(_room->_prevRoomNumber == 31) {
+			int x = _alfredState.x;
+			int y = _alfredState.y;
+			// _alfredState.x += 57;
+			// _alfredState.y -= 2;
+			_res->loadAlfredSpecialAnim(7);
+			_alfredState.setState(ALFRED_SPECIAL_ANIM);
+			waitForSpecialAnimation();
+			_alfredState.x = x;
+			_alfredState.y = y;
+
+		}
+	}
 	default:
 		break;
 	}
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 3338db34518..cd14e5a0239 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -338,6 +338,10 @@ public:
 	void pickUpHairStrand(HotSpot *hotspot);
 	void openJailFloorTile(HotSpot *hotspot);
 	void openTunnelDrawer(HotSpot *hotspot);
+	void useKeyWithPortrait(int inventoryObject, HotSpot *hotspot);
+	void openSafe(HotSpot *hotspot);
+	void openTunnelDoor(HotSpot *hotspot);
+	void closeTunnelDoor(HotSpot *hotspot);
 	void checkAllSymbols();
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index b46915b8def..44057dadb16 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -36,13 +36,15 @@ ResourceManager::ResourceManager(/* args */) {
 }
 
 const AlfredSpecialAnimOffset ResourceManager::alfredSpecialAnims[] = {
-	{10, 51, 102, 1, 7, 559685, 1,}, // READ BOOK
-	{10, 51, 102, 1, 7, 578943, 1}, // READ RECIPE
-	{3, 45, 87, 0, 7, 37000, 1}, // ELECTRIC SHOCK 1
-	{2, 82, 58, 0, 7, 53106, 20}, // ELECTRIC SHOCK 3
-	{3, 71, 110, 1, 2, 20724, 1, 62480}, // Throw
-	{14, 171, 107, 1, 7, 1556540, 1} , //crocodile
-	{12, 113, 103, 1, 7, 1583702, 1} // exit through manhole
+	{10, 51, 102, 1, 7, 559685, 1,}, // 0 - READ BOOK
+	{10, 51, 102, 1, 7, 578943, 1}, // 1 - READ RECIPE
+	{3, 45, 87, 0, 7, 37000, 1}, // 2 -  ELECTRIC SHOCK 1
+	{2, 82, 58, 0, 7, 53106, 20}, // 3 - ELECTRIC SHOCK 3
+	{3, 71, 110, 1, 2, 20724, 1, 62480}, // 4 - Throw
+	{14, 171, 107, 1, 7, 1556540, 1} , // 5 - crocodile
+	{12, 113, 103, 1, 7, 1583702, 1}, // 6 - exit through manhole
+	{11, 33, 72, 1, 7, 1761234, 1}, // 7 - alfred climbs down
+	{9, 33, 72, 1, 7, 1766378, 1} // 8 - alfred climbs up
 };
 
 ResourceManager::~ResourceManager() {
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 0f6fb18e071..adb389bc798 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -527,6 +527,7 @@ void RoomManager::resetConversationStates(byte roomNumber, byte *conversationDat
 void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 
 	_roomStickers.clear();
+	_prevRoomNumber = _currentRoomNumber;
 	_currentRoomNumber = roomNumber;
 	int roomOffset = roomNumber * kRoomStructSize;
 
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index a8107699e84..03b666c9320 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -140,6 +140,7 @@ public:
 	}
 
 	byte _currentRoomNumber = 0;
+	int _prevRoomNumber = -1;
 	Common::Array<HotSpot> _currentRoomHotspots;
 	Common::Array<Sprite> _currentRoomAnims;
 	Common::Array<Exit> _currentRoomExits;


Commit: 92cb18e7b708d5b27854c37850607cf9a929e0ac
    https://github.com/scummvm/scummvm/commit/92cb18e7b708d5b27854c37850607cf9a929e0ac
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:03+02:00

Commit Message:
PELROCK: Fixes animation always playing when entering room 38

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 2110525df6e..f8f08ce9a14 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -99,6 +99,9 @@ void MenuManager::checkMouseClick(int x, int y) {
 	case LOAD_GAME_BUTTON:
 		g_engine->loadGameDialog();
 		break;
+	case EXIT_MENU_BUTTON:
+		g_engine->quitGame();
+		break;
 	default:
 		break;
 	}
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index f9afd57cf65..f7faae56339 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1822,16 +1822,18 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			_dialog->say(_res->_ingameTexts[PINTA_BUENAPERSONA]);
 		}
 	case 38: {
-		int x = _alfredState.x;
-		int y = _alfredState.y;
-		_alfredState.x -= 57;
-		_alfredState.y += 2;
-		_res->loadAlfredSpecialAnim(6);
-		_alfredState.setState(ALFRED_SPECIAL_ANIM);
-		waitForSpecialAnimation();
-		_alfredState.x = x;
-		_alfredState.y = y;
-		break;
+		if(_room->_prevRoomNumber == 30) {
+			int x = _alfredState.x;
+			int y = _alfredState.y;
+			_alfredState.x -= 57;
+			_alfredState.y += 2;
+			_res->loadAlfredSpecialAnim(6);
+			_alfredState.setState(ALFRED_SPECIAL_ANIM);
+			waitForSpecialAnimation();
+			_alfredState.x = x;
+			_alfredState.y = y;
+			break;
+		}
 	}
 	case 32: {
 		if(_room->_prevRoomNumber == 31) {


Commit: 9519dd6c93b59d33190f68a2e55eed76f2dc3261
    https://github.com/scummvm/scummvm/commit/9519dd6c93b59d33190f68a2e55eed76f2dc3261
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:03+02:00

Commit Message:
PELROCK: Tunnel exit animation

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index f7faae56339..1fb97ecda7d 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1800,6 +1800,7 @@ void PelrockEngine::waitForSpecialAnimation() {
 	while (!g_engine->shouldQuit() && !_res->_isSpecialAnimFinished) {
 		_events->pollEvent();
 		renderScene(OVERLAY_NONE);
+		_events->waitForKey();
 		_screen->update();
 		g_system->delayMillis(10);
 	}
@@ -1839,8 +1840,6 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		if(_room->_prevRoomNumber == 31) {
 			int x = _alfredState.x;
 			int y = _alfredState.y;
-			// _alfredState.x += 57;
-			// _alfredState.y -= 2;
 			_res->loadAlfredSpecialAnim(7);
 			_alfredState.setState(ALFRED_SPECIAL_ANIM);
 			waitForSpecialAnimation();
@@ -1849,6 +1848,19 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 
 		}
 	}
+	case 27: {
+		if(_room->_prevRoomNumber == 33) {
+			int x = _alfredState.x;
+			int y = _alfredState.y;
+			_alfredState.x = 12;
+			_alfredState.y += 10;
+			_res->loadAlfredSpecialAnim(9);
+			_alfredState.setState(ALFRED_SPECIAL_ANIM);
+			waitForSpecialAnimation();
+			_alfredState.x = x;
+			_alfredState.y = y;
+		}
+	}
 	default:
 		break;
 	}
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 44057dadb16..a68eb19ac5f 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -44,7 +44,9 @@ const AlfredSpecialAnimOffset ResourceManager::alfredSpecialAnims[] = {
 	{14, 171, 107, 1, 7, 1556540, 1} , // 5 - crocodile
 	{12, 113, 103, 1, 7, 1583702, 1}, // 6 - exit through manhole
 	{11, 33, 72, 1, 7, 1761234, 1}, // 7 - alfred climbs down
-	{9, 33, 72, 1, 7, 1766378, 1} // 8 - alfred climbs up
+	{9, 33, 72, 1, 7, 1766378, 1}, // 8 - alfred climbs up
+	{16, 158, 115, 0, 7, 1770196, 1}, // 9 - alfred exits tunnel
+	{7, 208, 102, 0, 7, 1600956, 1} // 10 - alfred with workers
 };
 
 ResourceManager::~ResourceManager() {


Commit: d89bbc6eb957bacbe9370191b302a189468899b5
    https://github.com/scummvm/scummvm/commit/d89bbc6eb957bacbe9370191b302a189468899b5
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:03+02:00

Commit Message:
PELROCK: Fixes conversations in room 27

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 1fb97ecda7d..b5c068cbdc1 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -157,7 +157,7 @@ void PelrockEngine::init() {
 		// setScreen(0, ALFRED_DOWN);
 		// setScreen(3, ALFRED_RIGHT);
 		// setScreen(22, ALFRED_DOWN);
-		setScreen(30, ALFRED_DOWN);
+		setScreen(27, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
@@ -802,7 +802,7 @@ void PelrockEngine::talkTo(HotSpot *hotspot) {
 	}
 	changeCursor(DEFAULT);
 	debug("Talking to hotspot %d (%d) with extra %d", hotspot->index, hotspot->isSprite ? hotspot->index : hotspot->index - _room->_currentRoomAnims.size(), hotspot->extra);
-	_dialog->startConversation(_room->_conversationData, _room->_conversationDataSize, hotspot->index, animSet);
+	_dialog->startConversation(_room->_conversationData, _room->_conversationDataSize, animSet->talkingAnimIndex, animSet);
 }
 
 void PelrockEngine::lookAt(HotSpot *hotspot) {
@@ -1426,7 +1426,7 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 void PelrockEngine::animateTalkingNPC(Sprite *animSet) {
 	// Change with the right index
 
-	int index = animSet->index;
+	int index = animSet->talkingAnimIndex;
 	TalkingAnims *animHeader = &_room->_talkingAnimHeader;
 
 	int x = animSet->x + (index ? animHeader->offsetXAnimB : animHeader->offsetXAnimA);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index adb389bc798..c1744b15e5e 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -883,7 +883,7 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 	uint32_t picOffset = 0;
 
 	Common::Array<SpriteChange> spriteChanges = g_engine->_state->spriteChanges[_currentRoomNumber];
-
+	int talkingAnims = 0;
 	for (int i = 0; i < spriteCount; i++) {
 		uint32_t animOffset = metadata_start + (i * 44);
 		Sprite sprite;
@@ -898,6 +898,9 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 		sprite.extra = READ_LE_INT16(data + animOffset + 32);
 		debug("Sprite %d: x=%d y=%d w=%d h=%d stride=%d numAnims=%d zOrder=%d extra=%d", i, sprite.x, sprite.y, sprite.w, sprite.h, sprite.stride, sprite.numAnims, sprite.zOrder, sprite.extra);
 		sprite.actionFlags = data[animOffset + 34];
+		if(sprite.actionFlags & ACTION_MASK_TALK) {
+			sprite.talkingAnimIndex = talkingAnims++;
+		}
 		sprite.isHotspotDisabled = data[animOffset + 38];
 		sprite.disableAfterSequence = data[animOffset + 39];
 		for (int j = 0; j < spriteChanges.size(); j++) {
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 1bcd8a17d54..b0602bd8a77 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -237,6 +237,7 @@ struct Sprite {
 	bool isHotspotDisabled; // 38
 	bool disableAfterSequence = false; // 39
 	bool isTalking = false;
+	byte talkingAnimIndex = 0;
 	Anim *animData;
 	int16 extra;
 };
@@ -469,6 +470,7 @@ struct ResetEntry {
 #define FLAG_PARADOJA_RESUELTA 13
 #define FLAG_MIRA_SIMBOLO_FUERA_MUSEO 15
 #define FLAG_CROCODILLO_ENCENDIDO 14
+#define FLAG_CLAVE_CAJA_FUERTE 19
 
 
 
@@ -476,7 +478,6 @@ struct ResetEntry {
 #define FLAG_PUERTA_SECRETA_ABIERTA 16
 #define FLAG_ROBA_PELO_PRINCESA 17
 #define FLAG_A_LA_CARCEL 18
-#define FLAG_CLAVE_CAJA_FUERTE 19
 #define FLAG_SE_HA_PUESTO_EL_MUNECO 20
 #define FLAG_VIGILANTE_BEBE_AGUA 21
 #define FLAG_VIGILANTE_MEANDO 22


Commit: d8f0c53e7f52d968775cb5ddacf4c79739d46fb9
    https://github.com/scummvm/scummvm/commit/d8f0c53e7f52d968775cb5ddacf4c79739d46fb9
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:04+02:00

Commit Message:
PELROCK: Activate room 27's new conversations when opening Safe

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/saveload.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 67c40827c01..53195a35ee1 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -484,6 +484,22 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_room->disableHotspot(_room->findHotspotByExtra(451)); // Disable talk hotspot for the riddle
 		_state->setCurrentRoot(room, rootIndex + 1);
 		break;
+	//merchants
+	case 370:
+		break;
+	case 371:
+		break;
+	case 286:
+		break;
+	case 287:
+		break;
+	case 288:
+		break;
+	case 289:
+		break;
+	case 290:
+		break;
+	// end merchants
 	default:
 		debug("Got actionTrigger %d in dialogActionTrigger, but no handler defined", actionTrigger);
 		break;
@@ -1160,6 +1176,8 @@ void PelrockEngine::openSafe(HotSpot *hotspot) {
 		_room->addSticker(102);
 		_dialog->say(_res->_ingameTexts[GRANCANTIDAD_DINERO]);
 		addInventoryItem(82);
+		_state->setCurrentRoot(27, 1, 0);
+		_state->setCurrentRoot(27, 1, 1);
 	} else {
 		_dialog->say(_res->_ingameTexts[SISUPIERA_COMBINACION]);
 	}
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 82055ccf378..21332852b11 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -582,9 +582,9 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 	debug("Conversation ended");
 }
 
-uint32 DialogManager::findRoot(int &currentRoot, uint32 currentPosition, uint32 dataSize, const byte *conversationData) {
+uint32 DialogManager::findRoot(int npc, int &currentRoot, uint32 currentPosition, uint32 dataSize, const byte *conversationData) {
 	// Check if a specific root has been set for this room
-	int targetRoot = g_engine->_state->getCurrentRoot(g_engine->_room->_currentRoomNumber);
+	int targetRoot = g_engine->_state->getCurrentRoot(g_engine->_room->_currentRoomNumber, npc);
 
 	if (targetRoot >= 0) {
 		// Skip to the specified root
@@ -645,7 +645,7 @@ ConversationState DialogManager::initializeConversation(const byte *data, uint32
 	ConversationState state;
 	state.position = findSpeaker(npcIndex, dataSize, data);
 	state.currentRoot = 0;
-	state.position = findRoot(state.currentRoot, state.position, dataSize, data);
+	state.position = findRoot(npcIndex, state.currentRoot, state.position, dataSize, data);
 	state.currentChoiceLevel = -1;
 	state.lastSelectedChoice = ChoiceOption();
 
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 44361644696..67c124576e6 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -101,7 +101,7 @@ public:
 	void displayChoices(Common::Array<ChoiceOption> *choices, byte *compositeBuffer);
 	int selectChoice(Common::Array<Common::String> &choices, byte *compositeBuffer);
 	void startConversation(const byte *conversationData, uint32 dataSize, byte npcIndex, Sprite *alfredAnimSet = nullptr);
-	uint32 findRoot(int &currentRoot, uint32 position, uint32 dataSize, const byte *conversationData);
+	uint32 findRoot(int npc, int &currentRoot, uint32 position, uint32 dataSize, const byte *conversationData);
 	uint32 findSpeaker(byte npcIndex, uint32 dataSize, const byte *conversationData);
 	void sayAlfred(Description description);
 	void sayAlfred(Common::StringArray texts);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index b5c068cbdc1..14edf33a3bf 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1800,7 +1800,7 @@ void PelrockEngine::waitForSpecialAnimation() {
 	while (!g_engine->shouldQuit() && !_res->_isSpecialAnimFinished) {
 		_events->pollEvent();
 		renderScene(OVERLAY_NONE);
-		_events->waitForKey();
+		// _events->waitForKey();
 		_screen->update();
 		g_system->delayMillis(10);
 	}
diff --git a/engines/pelrock/saveload.cpp b/engines/pelrock/saveload.cpp
index 06e00ae088f..c110acaf2b7 100644
--- a/engines/pelrock/saveload.cpp
+++ b/engines/pelrock/saveload.cpp
@@ -336,7 +336,7 @@ bool syncGameStateData(Common::Serializer &s, GameStateData *gameState) {
 	}
 
 	// Conversation current root indices
-	s.syncBytes(gameState->conversationCurrentRoot, 56);
+	s.syncBytes(gameState->conversationCurrentRoot, 112);
 	return !s.err();
 }
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index b0602bd8a77..283b1b14fa3 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -589,21 +589,21 @@ struct GameStateData {
 	}
 
 	// Store current root index for each room (0xFF = not set, use findRoot logic)
-	byte *conversationCurrentRoot = new byte[56];
+	byte *conversationCurrentRoot = new byte[112];
 
-	int getCurrentRoot(byte room) const {
+	int getCurrentRoot(byte room, int npc = 0) const {
 		if (room >= 56)
 			return -1;
-		return (conversationCurrentRoot[room] == 0xFF) ? -1 : conversationCurrentRoot[room];
+		return (conversationCurrentRoot[room * 2 + npc] == 0xFF) ? -1 : conversationCurrentRoot[room * 2 + npc];
 	}
 
-	void setCurrentRoot(byte room, int root) {
+	void setCurrentRoot(byte room, int root, int npc = 0) {
 		if (room >= 56)
 			return;
 		if (root < 0 || root > 254) {
-			conversationCurrentRoot[room] = 0xFF; // Reset to auto-select
+			conversationCurrentRoot[room * 2 + npc] = 0xFF; // Reset to auto-select
 		} else {
-			conversationCurrentRoot[room] = (byte)root;
+			conversationCurrentRoot[room * 2 + npc] = (byte)root;
 		}
 	}
 


Commit: a3d3a1778e2bdf7fc47556800bc31bc75cc831f1
    https://github.com/scummvm/scummvm/commit/a3d3a1778e2bdf7fc47556800bc31bc75cc831f1
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:04+02:00

Commit Message:
PELROCK: Fix conversation advancement on previously done tirggers

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/console.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 53195a35ee1..edbdfa091de 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -23,10 +23,10 @@
 
 #include "pelrock.h"
 #include "pelrock/actions.h"
+#include "pelrock/extrascreens.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/util.h"
-#include "pelrock/extrascreens.h"
 
 namespace Pelrock {
 
@@ -243,14 +243,14 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	switch (actionTrigger) {
 	case 328:
 		debug("Setting current root to %d in room %d", rootIndex + 1, room);
-		_state->setCurrentRoot(room, rootIndex + 1);
+		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
 	case 329:
 		_state->setFlag(FLAG_PUTA_250_VECES, true);
 		break;
 	case 258:
 		_state->setFlag(FLAG_GUARDIA_PIDECOSAS, true);
-		_state->setCurrentRoot(4, 2);
+		_state->setCurrentRoot(4, 2, 0);
 		break;
 	case 259:
 		_dialog->say(_res->_ingameTexts[NO_EMPECEMOS]);
@@ -270,13 +270,13 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		break;
 	case 264:
 		// skip to root after the next one
-		_state->setCurrentRoot(room, rootIndex + 2);
+		_state->setCurrentRoot(room, rootIndex + 2, 0);
 		break;
 	case 267:
-		_state->setCurrentRoot(7, 2);
+		_state->setCurrentRoot(7, 2, 0);
 		break;
 	case 272:
-		_state->setCurrentRoot(room, rootIndex + 1);
+		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
 	case 273:
 		WalkBox w1;
@@ -297,14 +297,14 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 274:
 	case 275:
 	case 276:
-		_state->setCurrentRoot(room, rootIndex + 1);
+		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
 	case 277:
-		_state->setCurrentRoot(room, rootIndex + 1);
+		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		_state->setFlag(FLAG_JEFE_INGRESA_PASTA, true);
 		break;
 	case 278:
-		_state->setCurrentRoot(room, rootIndex + 1);
+		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
 	case 279:
 		travelToEgypt();
@@ -332,8 +332,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		break;
 	case 334:
 		addInventoryItem(86);
-		_state->setCurrentRoot(room, rootIndex + 1);
-
+		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
 	case 335:
 		// many oranges
@@ -352,7 +351,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 344:
 	case 345:
 	case 346:
-		_state->setCurrentRoot(room, rootIndex + 1);
+		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
 	case 348: {
 		// Anti-piracy punishment: corrupt screen + noise + crash
@@ -362,13 +361,13 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 349:
 		_state->setFlag(FLAG_CONSIGNAS_VENDEDOR, _state->getFlag(FLAG_CONSIGNAS_VENDEDOR) + 1);
 		if (_state->getFlag(FLAG_CONSIGNAS_VENDEDOR) == 2) {
-			_state->setCurrentRoot(room, rootIndex + 1);
+			_state->setCurrentRoot(room, rootIndex + 1, 1);
 		}
 		break;
 	case 350:
 		_state->setFlag(FLAG_CONSIGNAS_VENDEDOR, _state->getFlag(FLAG_CONSIGNAS_VENDEDOR) + 1);
 		if (_state->getFlag(FLAG_CONSIGNAS_VENDEDOR) == 2) {
-			_state->setCurrentRoot(room, rootIndex + 1);
+			_state->setCurrentRoot(room, rootIndex + 1, 1);
 		}
 		break;
 	case 351:
@@ -393,7 +392,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		break;
 		// end moros
 	case 353:
-		_state->setCurrentRoot(room, rootIndex + 2);
+		_state->setCurrentRoot(room, rootIndex + 2, 0);
 		break;
 	case 354:
 		if (_state->hasInventoryItem(105)) {
@@ -406,18 +405,18 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		toJail();
 		break;
 	case 356:
-		_state->setCurrentRoot(room, 3);
+		_state->setCurrentRoot(room, 3, 0);
 		break;
 	// end puta
 	//  sabio
 	case 366:
-		_state->setCurrentRoot(room, rootIndex + 1);
+		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
 	case 363:
 		toJail();
 		break;
 	case 367: // accept riddle
-		_state->setCurrentRoot(room, 27);
+		_state->setCurrentRoot(room, 27, 0);
 		walkAndAction(_room->findHotspotByExtra(467), TALK);
 		break;
 		// hasta aqui
@@ -451,12 +450,12 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 360: // neutral reset: counter = 0
 	{
 		_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, 0);
-		_state->setCurrentRoot(room, rootIndex + 1);
+		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		advanceQuotesConversation(rootIndex, room);
 		break;
 	}
 	case 361: // "no sé" (I don't know): no counter change, just advance
-		_state->setCurrentRoot(room, rootIndex + 1);
+		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
 	case 362: // special trigger: enables HIJODELAGRANPUTA cheat code
 		// Sets cheat_code_checking_enabled flag in original (0x495F3)
@@ -470,34 +469,51 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		if (rootIndex == 43) {
 			targetIndex = 27; // skip riddle explanation
 		}
-		_state->setCurrentRoot(room, targetIndex);
+		_state->setCurrentRoot(room, targetIndex, 0);
 		break;
 	}
 	case 365: // riddle correct: set riddle-solved flag
 		_state->setFlag(FLAG_PARADOJA_RESUELTA, 1);
-		_state->setCurrentRoot(room, 1);
+		_state->setCurrentRoot(room, 1, 0);
 		break;
 	case 292:
-		_state->setCurrentRoot(room, rootIndex + 1);
+		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
 	case 293:
 		_room->disableHotspot(_room->findHotspotByExtra(451)); // Disable talk hotspot for the riddle
-		_state->setCurrentRoot(room, rootIndex + 1);
+		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
-	//merchants
+	// merchants
 	case 370:
+		addInventoryItem(111);
 		break;
 	case 371:
+		addInventoryItem(111);
+		addInventoryItem(110);
+		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
 	case 286:
+		addInventoryItem(83);
+		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
 	case 287:
+		addInventoryItem(77);
+		addInventoryItem(107);
+		givenItems();
 		break;
 	case 288:
+		addInventoryItem(78);
+		givenItems();
 		break;
 	case 289:
+		addInventoryItem(79);
+		addInventoryItem(108);
+		givenItems();
 		break;
 	case 290:
+		addInventoryItem(80);
+		addInventoryItem(109);
+		givenItems();
 		break;
 	// end merchants
 	default:
@@ -506,12 +522,19 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	}
 }
 
+void PelrockEngine::givenItems() {
+	_state->setFlag(FLAG_MERCHANT_GIVENITEMS, _state->getFlag(FLAG_MERCHANT_GIVENITEMS) + 1);
+	if(_state->getFlag(FLAG_MERCHANT_GIVENITEMS) == 4) {
+		_state->setCurrentRoot(27, 2, 1);
+	}
+}
+
 void PelrockEngine::advanceQuotesConversation(byte rootIndex, byte room) {
 	int targetRoot = rootIndex + 1;
 	if (targetRoot == 26) {
 		targetRoot = 2;
 	}
-	_state->setCurrentRoot(room, targetRoot);
+	_state->setCurrentRoot(room, targetRoot, 0);
 }
 
 void PelrockEngine::toJail() {
@@ -585,7 +608,7 @@ void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
 	if (_state->getFlag(FLAG_JEFE_INGRESA_PASTA)) {
 		_state->setFlag(FLAG_JEFE_INGRESA_PASTA, false);
 		addInventoryItem(75);
-		_state->setCurrentRoot(20, 2);
+		_state->setCurrentRoot(20, 2, 0);
 	} else {
 		int billCount = 0;
 		for (uint i = 0; i < _state->inventoryItems.size(); i++) {
@@ -830,7 +853,7 @@ void PelrockEngine::pickCables(HotSpot *hotspot) {
 	_room->addSticker(21);
 
 	_dialog->say(_res->_ingameTexts[RELOJ_HA_CAMBIADO]);
-	_state->setCurrentRoot(4, 1);
+	_state->setCurrentRoot(4, 1, 0);
 }
 
 void PelrockEngine::giveIdToGuard(int inventoryObject, HotSpot *hotspot) {
@@ -850,7 +873,7 @@ void PelrockEngine::giveIdToGuard(int inventoryObject, HotSpot *hotspot) {
 }
 
 void PelrockEngine::unlockMuseum() {
-	_state->setCurrentRoot(4, 3);
+	_state->setCurrentRoot(4, 3, 0);
 	_room->enableSprite(2, 100, PERSIST_PERM);
 	_room->enableSprite(3, 100, PERSIST_PERM);
 	_room->addStickerToRoom(4, 87, PERSIST_PERM);
@@ -892,7 +915,7 @@ void PelrockEngine::useAmuletWithStatue(int inventoryObject, HotSpot *hotspot) {
 	if (!_room->hasSticker(24)) {
 		_room->addSticker(24);
 		_state->removeInventoryItem(7);
-		_state->setCurrentRoot(7, 1);
+		_state->setCurrentRoot(7, 1, 0);
 		_alfredState.direction = ALFRED_RIGHT;
 
 		HotSpot *statueHotspot = _room->findHotspotByExtra(91);
@@ -1008,13 +1031,13 @@ void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
 void PelrockEngine::pickupSunflower(HotSpot *hotspot) {
 	if (_state->getFlag(FLAG_PARADOJA_RESUELTA) == false) {
 		_dialog->say(_res->_ingameTexts[OIGA]);
-		_state->setCurrentRoot(25, 26);
+		_state->setCurrentRoot(25, 26, 0);
 		_state->setFlag(FLAG_RIDDLE_PRESENTED, true);
 		walkAndAction(_room->findHotspotByExtra(467), TALK);
 	} else {
 		addInventoryItem(85);
 		_room->disableHotspot(hotspot);
-		_state->setCurrentRoot(25, 1);
+		_state->setCurrentRoot(25, 1, 0);
 		_room->addSticker(73);
 	}
 	checkIngredients();
@@ -1030,10 +1053,10 @@ void PelrockEngine::checkIngredients() {
 void PelrockEngine::pickUpBook(int i) {
 	if (!_state->hasInventoryItem(10)) {
 		_dialog->say(_res->_ingameTexts[VENGA_ACA]);
-		_state->setCurrentRoot(9, 1);
+		_state->setCurrentRoot(9, 1, 0);
 
 		if (_state->hasInventoryItem(3)) {
-			_state->setCurrentRoot(9, 2);
+			_state->setCurrentRoot(9, 2, 0);
 			addInventoryItem(10);
 		}
 
@@ -1044,9 +1067,9 @@ void PelrockEngine::pickUpBook(int i) {
 		waitForActionEnd();
 		if (!_state->hasInventoryItem(3)) {
 
-			_state->setCurrentRoot(9, 0);
+			_state->setCurrentRoot(9, 0, 0);
 		} else {
-			_state->setCurrentRoot(9, 3);
+			_state->setCurrentRoot(9, 3, 0);
 		}
 	} else {
 		if (_state->libraryShelf == -1) {
@@ -1096,7 +1119,7 @@ void PelrockEngine::closeEgyptMuseumDoor(HotSpot *hotspot) {
 }
 
 void PelrockEngine::pushSymbol1(HotSpot *hotspot) {
-	if(_state->getFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO) == true) {
+	if (_state->getFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO) == true) {
 		byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
 		debug("Current symbols pulled: %d", symbolsPulled);
 		_state->setFlag(FLAG_SYMBOLS_PUSHED, symbolsPulled | 0x1);
@@ -1106,7 +1129,7 @@ void PelrockEngine::pushSymbol1(HotSpot *hotspot) {
 }
 
 void PelrockEngine::pushSymbol2(HotSpot *hotspot) {
-	if(_state->getFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO) == true) {
+	if (_state->getFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO) == true) {
 		byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
 		debug("Current symbols pulled: %d", symbolsPulled);
 		_state->setFlag(FLAG_SYMBOLS_PUSHED, symbolsPulled | 0x2);
@@ -1116,7 +1139,7 @@ void PelrockEngine::pushSymbol2(HotSpot *hotspot) {
 }
 
 void PelrockEngine::pushSymbol3(HotSpot *hotspot) {
-	if(_state->getFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO) == true) {
+	if (_state->getFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO) == true) {
 		byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
 		debug("Current symbols pulled: %d", symbolsPulled);
 		_state->setFlag(FLAG_SYMBOLS_PUSHED, symbolsPulled | 0x4);
@@ -1126,7 +1149,7 @@ void PelrockEngine::pushSymbol3(HotSpot *hotspot) {
 }
 
 void PelrockEngine::pushSymbol4(HotSpot *hotspot) {
-	if(_state->getFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO) == true) {
+	if (_state->getFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO) == true) {
 		byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
 		debug("Current symbols pulled: %d", symbolsPulled);
 		_state->setFlag(FLAG_SYMBOLS_PUSHED, symbolsPulled | 0x8);
@@ -1138,8 +1161,8 @@ void PelrockEngine::pushSymbol4(HotSpot *hotspot) {
 void PelrockEngine::checkAllSymbols() {
 	byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
 	debug("Checking symbols, current value: %d", symbolsPulled);
-	if(symbolsPulled == 0x0F) {
-		//Activates animation
+	if (symbolsPulled == 0x0F) {
+		// Activates animation
 		_sound->playSound(_room->_roomSfx[0]);
 		_room->enableSprite(4, 100, PERSIST_TEMP);
 		_room->enableExit(0, PERSIST_BOTH);
@@ -1172,7 +1195,7 @@ void PelrockEngine::useKeyWithPortrait(int inventoryObject, HotSpot *hotspot) {
 }
 
 void PelrockEngine::openSafe(HotSpot *hotspot) {
-	if(_state->getFlag(FLAG_CLAVE_CAJA_FUERTE) == true) {
+	if (_state->getFlag(FLAG_CLAVE_CAJA_FUERTE) == true) {
 		_room->addSticker(102);
 		_dialog->say(_res->_ingameTexts[GRANCANTIDAD_DINERO]);
 		addInventoryItem(82);
@@ -1246,8 +1269,8 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		waitForSpecialAnimation();
 
 		loadExtraScreenAndPresent(3);
-		_state->setCurrentRoot(17, 1);
-		_state->setCurrentRoot(18, 4);
+		_state->setCurrentRoot(17, 1, 0);
+		_state->setCurrentRoot(18, 4, 0);
 		debug("After extra screen");
 		_dialog->say(_res->_ingameTexts[QUEASCO]);
 		break;
@@ -1275,14 +1298,14 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 	case 24:
 		if (_state->getFlag(FLAG_RIDDLE_PRESENTED) == true) {
 			_dialog->say(_res->_ingameTexts[CAPITULOPARADOJAS]);
-			_state->setCurrentRoot(25, 44);
+			_state->setCurrentRoot(25, 44, 0);
 		} else {
 			_res->loadAlfredSpecialAnim(0);
 			_alfredState.animState = ALFRED_SPECIAL_ANIM;
 			waitForSpecialAnimation();
 			_dialog->say(_res->_ingameTexts[COSASAPRENDIDO]);
 			_state->setFlag(FLAG_ALFRED_INTELIGENTE, true);
-			_state->setCurrentRoot(14, 2);
+			_state->setCurrentRoot(14, 2, 0);
 		}
 		break;
 	case 64:
@@ -1307,15 +1330,14 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		if (spell) {
 			_alfredState.direction = ALFRED_LEFT;
 			_dialog->say(_res->_ingameTexts[DIOSHALCON + spell->page], 1);
-			if(spell->page == 12 && _room->_currentRoomNumber == 28) {
+			if (spell->page == 12 && _room->_currentRoomNumber == 28) {
 				_graphics->clearScreen();
 				int waitFrames = 0;
-				//blank screen for half a second
-				while (!shouldQuit() && waitFrames < 20)
-				{
+				// blank screen for half a second
+				while (!shouldQuit() && waitFrames < 20) {
 					_events->pollEvent();
 					_chrono->updateChrono();
-					if(_chrono->_gameTick) {
+					if (_chrono->_gameTick) {
 						waitFrames++;
 					}
 					_screen->markAllDirty();
diff --git a/engines/pelrock/console.cpp b/engines/pelrock/console.cpp
index 404512862a5..929d83a7205 100644
--- a/engines/pelrock/console.cpp
+++ b/engines/pelrock/console.cpp
@@ -52,13 +52,14 @@ bool PelrockConsole::cmdSetFlag(int argc, const char **argv) {
 
 bool PelrockConsole::cmdSetRoot(int argc, const char **argv) {
 	if (argc < 3) {
-		debugPrintf("Usage: setRoot <roomNumber> <rootIndex>");
+		debugPrintf("Usage: setRoot <roomNumber> <rootIndex> <npcIndex (optional, default=0)>");
 		return true;
 	}
 	int roomNumber = atoi(argv[1]);
 	int rootIndex = atoi(argv[2]);
-	_engine->_state->setCurrentRoot(roomNumber, rootIndex);
-	debugPrintf("Set current root to %d in room %d\n", rootIndex, roomNumber);
+	int npcIndex = (argc >= 4) ? atoi(argv[3]) : 0;
+	_engine->_state->setCurrentRoot(roomNumber, rootIndex, npcIndex);
+	debugPrintf("Set current root to %d in room %d for npc %d\n", rootIndex, roomNumber, npcIndex);
 	return true;
 }
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index cd14e5a0239..4f3f3469c19 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -250,8 +250,8 @@ public:
 	void performActionTrigger(uint16 actionTrigger);
 	void dialogActionTrigger(uint16 actionTrigger, byte room, byte rootIndex);
 
+	void givenItems();
 	void advanceQuotesConversation(byte rootIndex, byte room);
-
 	void toJail();
 
 	void executeAction(VerbIcon action, HotSpot *hotspot);
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 283b1b14fa3..11598cca564 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -471,6 +471,8 @@ struct ResetEntry {
 #define FLAG_MIRA_SIMBOLO_FUERA_MUSEO 15
 #define FLAG_CROCODILLO_ENCENDIDO 14
 #define FLAG_CLAVE_CAJA_FUERTE 19
+#define FLAG_INGREDIENTES_CONSEGUIDOS 48
+#define FLAG_MERCHANT_GIVENITEMS 58
 
 
 
@@ -506,7 +508,6 @@ struct ResetEntry {
 #define FLAG_HA_USADO_AGUA 45
 #define FLAG_TIENDA_ABIERTA 46
 #define FLAG_NUMERO_DE_COPAS 47
-#define FLAG_INGREDIENTES_CONSEGUIDOS 48
 
 #define FLAG_GUARDIA_PIDECOSAS 49
 #define FLAG_GUARDIA_DNI_ENTREGADO 50
@@ -518,7 +519,7 @@ struct ResetEntry {
 #define FLAG_RIDDLE_PRESENTED 56
 #define FLAG_SYMBOLS_PUSHED 57
 
-const int kNumGameFlags = 58;
+const int kNumGameFlags = 59;
 
 struct GameStateData {
 	byte flags[kNumGameFlags];
@@ -591,13 +592,13 @@ struct GameStateData {
 	// Store current root index for each room (0xFF = not set, use findRoot logic)
 	byte *conversationCurrentRoot = new byte[112];
 
-	int getCurrentRoot(byte room, int npc = 0) const {
+	int getCurrentRoot(byte room, int npc) const {
 		if (room >= 56)
 			return -1;
 		return (conversationCurrentRoot[room * 2 + npc] == 0xFF) ? -1 : conversationCurrentRoot[room * 2 + npc];
 	}
 
-	void setCurrentRoot(byte room, int root, int npc = 0) {
+	void setCurrentRoot(byte room, int root, int npc) {
 		if (room >= 56)
 			return;
 		if (root < 0 || root > 254) {


Commit: d7e147ed85391d9cff34161d2949d50e6878290c
    https://github.com/scummvm/scummvm/commit/d7e147ed85391d9cff34161d2949d50e6878290c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:04+02:00

Commit Message:
PELROCK: Trigger police after going to jail

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index edbdfa091de..e684d581cda 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -516,6 +516,17 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		givenItems();
 		break;
 	// end merchants
+	case 369:
+	case 383:
+		_state->setCurrentRoot(room, rootIndex + 1, 0);
+		break;
+	case 295:
+		addInventoryItem(84);
+		_state->setCurrentRoot(room, rootIndex + 1, 0);
+		break;
+	case 285:
+		toJail();
+		break;
 	default:
 		debug("Got actionTrigger %d in dialogActionTrigger, but no handler defined", actionTrigger);
 		break;
@@ -542,6 +553,7 @@ void PelrockEngine::toJail() {
 	_alfredState.x = 342;
 	_alfredState.y = 277;
 	setScreen(31, ALFRED_DOWN);
+	_state->setFlag(FLAG_A_LA_CARCEL, true);
 	_room->moveHotspot(_room->findHotspotByExtra(101), 444, 166);
 }
 
@@ -1032,15 +1044,15 @@ void PelrockEngine::pickupSunflower(HotSpot *hotspot) {
 	if (_state->getFlag(FLAG_PARADOJA_RESUELTA) == false) {
 		_dialog->say(_res->_ingameTexts[OIGA]);
 		_state->setCurrentRoot(25, 26, 0);
-		_state->setFlag(FLAG_RIDDLE_PRESENTED, true);
 		walkAndAction(_room->findHotspotByExtra(467), TALK);
+		_state->setFlag(FLAG_RIDDLE_PRESENTED, true);
 	} else {
 		addInventoryItem(85);
 		_room->disableHotspot(hotspot);
 		_state->setCurrentRoot(25, 1, 0);
 		_room->addSticker(73);
+		checkIngredients();
 	}
-	checkIngredients();
 }
 
 void PelrockEngine::checkIngredients() {
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 21332852b11..5636c573c58 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -453,7 +453,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 	}
 	setCurSprite(animSet ? animSet->index : -1);
 
-	debug("Starting conversation with %d bytes of data, for npc %d, hotspot %d", dataSize, npcIndex, animSet ? animSet->index : -1);
+	debug("Starting conversation with %d bytes of data, for npc %d, hotspot %d, currentRoot is = %d", dataSize, npcIndex, animSet ? animSet->index : -1, g_engine->_state->getCurrentRoot(g_engine->_room->_currentRoomNumber, npcIndex));
 
 	// Initialize conversation state
 	ConversationState state = initializeConversation(conversationData, dataSize, npcIndex);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 14edf33a3bf..16a360c8502 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1860,6 +1860,21 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			_alfredState.x = x;
 			_alfredState.y = y;
 		}
+		break;
+	}
+	case 26: {
+		if(_state->getFlag(FLAG_A_LA_CARCEL) == true) {
+			if(!_state->getFlag(FLAG_SE_HA_PUESTO_EL_MUNECO)) {
+				_state->setCurrentRoot(26, 2, 1);
+			} else {
+				_dialog->say(_res->_ingameTexts[OIGAUSTED], 1);
+				_dialog->say(_res->_ingameTexts[ESAMI]);
+				_dialog->say(_res->_ingameTexts[VENGAAHORAMISMO], 1);
+				_state->setCurrentRoot(26, 1, 1);
+				walkAndAction(_room->findHotspotByExtra(421), TALK);
+			}
+		}
+		break;
 	}
 	default:
 		break;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 11598cca564..967c70e01bb 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -540,7 +540,7 @@ struct GameStateData {
 	Common::HashMap<byte, Common::Array<SpriteChange>> spriteChanges;
 
 	GameStateData() {
-		memset(conversationCurrentRoot, 0xFF, 56); // 0xFF = not set
+		memset(conversationCurrentRoot, 0, 112); // Initialize all to 0xFF (not set)
 		for (int i = 0; i < kNumGameFlags; i++)
 			flags[i] = 0;
 		flags[FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ] = true;


Commit: f2af94bf4c74c3691bb15dbc5c6238743cd9a580
    https://github.com/scummvm/scummvm/commit/f2af94bf4c74c3691bb15dbc5c6238743cd9a580
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:05+02:00

Commit Message:
PELROCK: Doll animation in prison cell

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index e684d581cda..6182144ef85 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -173,6 +173,7 @@ const CombinationEntry combinationTable[] = {
 	{4, 358, &PelrockEngine::useBrickWithLibrarian}, // Any hotspot in the lamppost will work
 	{76, 469, &PelrockEngine::usePumpkinWithRiver},
 	{100, 650, &PelrockEngine::useKeyWithPortrait},
+	{83, 461, &PelrockEngine::useDollWithBed},
 	// End marker
 	{WILDCARD, WILDCARD, nullptr}};
 
@@ -1226,6 +1227,27 @@ void PelrockEngine::closeTunnelDoor(HotSpot *hotspot) {
 	closeDoor(hotspot, 0, 66, MASCULINE, true);
 }
 
+void PelrockEngine::useDollWithBed(int inventoryObject, HotSpot *hotspot) {
+	int x = _alfredState.x;
+	int y = _alfredState.y;
+	_alfredState.x -= 36;
+	_alfredState.y += 7;
+	_res->loadAlfredSpecialAnim(11);
+	_alfredState.animState = ALFRED_SPECIAL_ANIM;
+	waitForSpecialAnimation();
+	_alfredState.x -= 4;
+	_alfredState.y += 12;
+	_res->loadAlfredSpecialAnim(12);
+	_alfredState.animState = ALFRED_SPECIAL_ANIM;
+	waitForSpecialAnimation();
+	_alfredState.direction = ALFRED_DOWN;
+	_state->setFlag(FLAG_SE_HA_PUESTO_EL_MUNECO, true);
+	_state->removeInventoryItem(83);
+	_room->addSticker(126);
+	_alfredState.x = x;
+	_alfredState.y = y;
+}
+
 void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	debug("Performing action trigger: %d", actionTrigger);
 	switch (actionTrigger) {
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 16a360c8502..47bdbe997e3 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -157,7 +157,7 @@ void PelrockEngine::init() {
 		// setScreen(0, ALFRED_DOWN);
 		// setScreen(3, ALFRED_RIGHT);
 		// setScreen(22, ALFRED_DOWN);
-		setScreen(27, ALFRED_DOWN);
+		setScreen(31, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 4f3f3469c19..178591e1cc6 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -342,6 +342,7 @@ public:
 	void openSafe(HotSpot *hotspot);
 	void openTunnelDoor(HotSpot *hotspot);
 	void closeTunnelDoor(HotSpot *hotspot);
+	void useDollWithBed(int inventoryObject, HotSpot *hotspot);
 	void checkAllSymbols();
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index a68eb19ac5f..520b104e927 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -46,7 +46,9 @@ const AlfredSpecialAnimOffset ResourceManager::alfredSpecialAnims[] = {
 	{11, 33, 72, 1, 7, 1761234, 1}, // 7 - alfred climbs down
 	{9, 33, 72, 1, 7, 1766378, 1}, // 8 - alfred climbs up
 	{16, 158, 115, 0, 7, 1770196, 1}, // 9 - alfred exits tunnel
-	{7, 208, 102, 0, 7, 1600956, 1} // 10 - alfred with workers
+	{7, 208, 102, 0, 7, 1600956, 1}, // 10 - alfred with workers
+	{23, 116, 124, 1, 7, 2060916, 1}, // 11 - Munheco 1
+	{18, 177, 124, 1, 7, 2115632, 1}, // 12 - Munheco 2
 };
 
 ResourceManager::~ResourceManager() {


Commit: 2d39406a8578a9e0dac67cf852137a04b090b08f
    https://github.com/scummvm/scummvm/commit/2d39406a8578a9e0dac67cf852137a04b090b08f
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:05+02:00

Commit Message:
PELROCK: Disables interaction during cutscenes

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 6182144ef85..97841372fda 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -762,6 +762,7 @@ void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
 	_room->addStickerToRoom(_room->_currentRoomNumber, 10, PERSIST_PERM);
 	_room->disableHotspot(_room->findHotspotByExtra(295)); // Disable storefront hotspot
 	_room->disableHotspot(_room->findHotspotByExtra(294)); // Disable window hotspot
+	_disableAction = true; // Prevent player from doing anything until they move Alfred
 	walkTo(630, _alfredState.y);
 }
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 47bdbe997e3..3a656bf319d 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -487,6 +487,10 @@ void PelrockEngine::checkMouse() {
 
 	checkMouseHover();
 
+	if(_disableAction) {
+		return;
+	}
+
 	if (_alfredState.animState == ALFRED_WALKING && !_alfredState.isWalkingCancelable) {
 		// Ignore clicks while Alfred is walking
 		_events->_leftMouseClicked = false;
@@ -860,7 +864,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 				debug("Finished walking to target");
 				_alfredState.setState(ALFRED_IDLE);
 				_alfredState.isWalkingCancelable = true;
-
+				_disableAction = false;
 				if (_currentHotspot != nullptr)
 					_alfredState.direction = calculateAlfredsDirection(_currentHotspot);
 				drawAlfred(_res->alfredIdle[_alfredState.direction]);
@@ -1561,6 +1565,7 @@ void PelrockEngine::walkTo(int x, int y) {
 }
 
 void PelrockEngine::walkAndAction(HotSpot *hotspot, VerbIcon action) {
+	_disableAction = true;
 	walkTo(hotspot->x + hotspot->w / 2, hotspot->y + hotspot->h);
 	_queuedAction = QueuedAction{action, hotspot->index, true};
 }
@@ -1864,7 +1869,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 	}
 	case 26: {
 		if(_state->getFlag(FLAG_A_LA_CARCEL) == true) {
-			if(!_state->getFlag(FLAG_SE_HA_PUESTO_EL_MUNECO)) {
+			if(_state->getFlag(FLAG_SE_HA_PUESTO_EL_MUNECO) == true) {
 				_state->setCurrentRoot(26, 2, 1);
 			} else {
 				_dialog->say(_res->_ingameTexts[OIGAUSTED], 1);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 178591e1cc6..bf34a6b8ea7 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -178,7 +178,7 @@ public:
 	SmallFont *_smallFont = nullptr;
 	LargeFont *_largeFont = nullptr;
 	DoubleSmallFont *_doubleSmallFont = nullptr;
-
+	bool _disableAction = false;
 
 public:
 	PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc);


Commit: d385e8a5071ff3b16323e06a3e81131e62df241e
    https://github.com/scummvm/scummvm/commit/d385e8a5071ff3b16323e06a3e81131e62df241e
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:05+02:00

Commit Message:
PELROCK: Implements using glue + patches to build inflated doll

Changed paths:
    engines/pelrock/actions.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 97841372fda..278bbf23b8a 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1401,6 +1401,17 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		_dialog->say(_res->_ingameTexts[PARECE_COMBINACION_CAJAFUERTE]);
 		_state->setFlag(FLAG_CLAVE_CAJA_FUERTE, true);
 		break;
+	case 108:
+	case 109: {
+		if(_state->hasInventoryItem(110) == true) {
+			_state->removeInventoryItem(110);
+			_state->removeInventoryItem(109);
+			_state->removeInventoryItem(108);
+			addInventoryItem(83);
+			_dialog->say(_res->_ingameTexts[MUNECO_ARREGLADO]);
+		}
+		break;
+	}
 	default:
 		break;
 	}


Commit: cdb379e9ad63669b66072421a215a4b195ff34ee
    https://github.com/scummvm/scummvm/commit/cdb379e9ad63669b66072421a215a4b195ff34ee
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:06+02:00

Commit Message:
PELROCK: Fixes resetting palette in room 28

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 278bbf23b8a..a2b564540dc 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1532,30 +1532,16 @@ void PelrockEngine::waitForActionEnd() {
  * fades to it using the step-wise palette transition.
  */
 void PelrockEngine::pickUpMatches(HotSpot *hotspot) {
-	// Load the special palette from ALFRED.7 at offset 0x1610CE
-	static const uint32 kRoom28PaletteOffset = 0x1610CE;
-
-	Common::File alfred7;
-	if (!alfred7.open(Common::Path("ALFRED.7"))) {
-		warning("Could not open ALFRED.7 for room 28 palette");
-		return;
-	}
 
 	byte targetPalette[768];
-	alfred7.seek(kRoom28PaletteOffset, SEEK_SET);
-	alfred7.read(targetPalette, 768);
-	alfred7.close();
-
-	// Convert 6-bit VGA palette (0-63) to 8-bit (0-255)
-	for (int i = 0; i < 768; i++) {
-		targetPalette[i] = targetPalette[i] << 2;
-	}
+	_res->getPaletteForRoom28(targetPalette);
 
 	// Fade from current palette to the new palette
 	_graphics->fadePaletteToTarget(targetPalette, 25);
 	debug("Finished palette fade for room 28 object pickup");
 	// Pick up the item
 	_room->disableHotspot(hotspot);
+	Common::copy(targetPalette, targetPalette + 768, _room->_roomPalette);
 	_state->setFlag(FLAG_CROCODILLO_ENCENDIDO, true);
 	_room->moveHotspot(_room->findHotspotByExtra(87), 415, 171);
 	_room->moveHotspot(_room->findHotspotByExtra(88), 305, 217);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 3a656bf319d..b92baaf6b24 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1867,6 +1867,15 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		}
 		break;
 	}
+	case 28: {
+		if(_state->getFlag(FLAG_CROCODILLO_ENCENDIDO) == true) {
+			byte palette[768];
+			_res->getPaletteForRoom28(palette);
+			g_system->getPaletteManager()->setPalette(palette, 0, 256);
+			Common::copy(palette, palette + 768, _room->_roomPalette);
+			break;
+		}
+	}
 	case 26: {
 		if(_state->getFlag(FLAG_A_LA_CARCEL) == true) {
 			if(_state->getFlag(FLAG_SE_HA_PUESTO_EL_MUNECO) == true) {
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 520b104e927..1a150749150 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -24,6 +24,7 @@
 #include "pelrock/pelrock.h"
 #include "pelrock/room.h"
 #include "pelrock/util.h"
+#include "resources.h"
 
 namespace Pelrock {
 
@@ -320,6 +321,26 @@ void ResourceManager::loadHardcodedText() {
 	exe.close();
 }
 
+void ResourceManager::getPaletteForRoom28(byte *palette) {
+	// Load the special palette from ALFRED.7 at offset 0x1610CE
+	static const uint32 kRoom28PaletteOffset = 0x1610CE;
+
+	Common::File alfred7;
+	if (!alfred7.open(Common::Path("ALFRED.7"))) {
+		warning("Could not open ALFRED.7 for room 28 palette");
+		return;
+	}
+
+	alfred7.seek(kRoom28PaletteOffset, SEEK_SET);
+	alfred7.read(palette, 768);
+
+	// Convert 6-bit VGA palette (0-63) to 8-bit (0-255)
+	for (int i = 0; i < 768; i++) {
+		palette[i] = palette[i] << 2;
+	}
+
+	alfred7.close();
+}
 void ResourceManager::getExtraScreen(int screenIndex, byte *screenBuf, byte *palette) {
 	Common::File alfred7;
 	if (!alfred7.open("ALFRED.7")) {
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index a8d99221f92..bcada5183d0 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -49,6 +49,7 @@ public:
 	void clearSpecialAnim();
 	void loadInventoryItems();
 	void loadHardcodedText();
+	void getPaletteForRoom28(byte *palette);
 	Common::StringArray loadComputerText();
 	void getExtraScreen(int screenIndex, byte *screenBuf, byte *palette);
 	Common::Array<Common::StringArray> getCredits();


Commit: 7d7038667e9e0b9da60a2de70f0054ba51f49afc
    https://github.com/scummvm/scummvm/commit/7d7038667e9e0b9da60a2de70f0054ba51f49afc
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:06+02:00

Commit Message:
PELROCK: Fixes statue never giving object 8 (note)

Changed paths:
    engines/pelrock/actions.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index a2b564540dc..cf878af747e 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -62,7 +62,7 @@ const ActionEntry actionTable[] = {
 	// Room 3
 	{290, OPEN, &PelrockEngine::openShopDoor},
 	{290, CLOSE, &PelrockEngine::closeShopDoor},
-	{32, OPEN, &PelrockEngine::openLamppost},
+	{288, OPEN, &PelrockEngine::openLamppost},
 	{308, PICKUP, &PelrockEngine::moveCable}, // Lamppost cable
 
 	// Room 15
@@ -932,13 +932,14 @@ void PelrockEngine::useAmuletWithStatue(int inventoryObject, HotSpot *hotspot) {
 		_state->setCurrentRoot(7, 1, 0);
 		_alfredState.direction = ALFRED_RIGHT;
 
-		HotSpot *statueHotspot = _room->findHotspotByExtra(91);
+		HotSpot *statueHotspot = _room->findHotspotByExtra(347);
 		_currentHotspot = statueHotspot;
 		walkTo(statueHotspot->x + statueHotspot->w / 2, statueHotspot->y + statueHotspot->h);
 		animateStatuePaletteFade(false);
 		walkAndAction(statueHotspot, TALK);
 		waitForActionEnd();
 		animateStatuePaletteFade(true);
+		addInventoryItem(8);
 	}
 }
 


Commit: 44a183687775dc0d759ae7bd2761ae3346333d12
    https://github.com/scummvm/scummvm/commit/44a183687775dc0d759ae7bd2761ae3346333d12
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:06+02:00

Commit Message:
PELROCK: "Running" sprites in rooms 34, 36

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index cf878af747e..fac151a1a7c 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -165,7 +165,7 @@ const CombinationEntry combinationTable[] = {
 	{4, 294, &PelrockEngine::useBrickWithWindow},
 	{4, 295, &PelrockEngine::useBrickWithShopWindow},
 	{6, 315, &PelrockEngine::useCordWithPlug},
-	{1, 309, &PelrockEngine::giveIdToGuard},
+	{1, 309, &PelrockEngine::showIdToGuard},
 	{5, 309, &PelrockEngine::giveMoneyToGuard},
 	{7, 353, &PelrockEngine::useAmuletWithStatue},
 	{8, 353, &PelrockEngine::useSecretCodeWithStatue},
@@ -174,6 +174,8 @@ const CombinationEntry combinationTable[] = {
 	{76, 469, &PelrockEngine::usePumpkinWithRiver},
 	{100, 650, &PelrockEngine::useKeyWithPortrait},
 	{83, 461, &PelrockEngine::useDollWithBed},
+	{84, 503, &PelrockEngine::giveMagazineToGuard},
+	{86, 500, &PelrockEngine::giveWaterToGuard},
 	// End marker
 	{WILDCARD, WILDCARD, nullptr}};
 
@@ -528,6 +530,29 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 285:
 		toJail();
 		break;
+	case 374:
+	case 372:
+	case 373:
+		_state->setCurrentRoot(room, rootIndex + 1, 0);
+		break;
+	case 297: {
+		// sprite moves to right
+		_room->enableExit(0);
+		Sprite *sprite = _room->findSpriteByIndex(0);
+		sprite->animData[0].movementFlags = 0x1C; // Move right
+		//Basic loop to wait until the sprite has reached the door
+		while (!shouldQuit()) {
+			_events->pollEvent();
+			renderScene();
+			if (sprite->x >= 206) {
+				break;
+			}
+			_screen->update();
+			g_system->delayMillis(10);
+		}
+		_room->disableSprite(0);
+	} break;
+
 	default:
 		debug("Got actionTrigger %d in dialogActionTrigger, but no handler defined", actionTrigger);
 		break;
@@ -536,7 +561,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 
 void PelrockEngine::givenItems() {
 	_state->setFlag(FLAG_MERCHANT_GIVENITEMS, _state->getFlag(FLAG_MERCHANT_GIVENITEMS) + 1);
-	if(_state->getFlag(FLAG_MERCHANT_GIVENITEMS) == 4) {
+	if (_state->getFlag(FLAG_MERCHANT_GIVENITEMS) == 4) {
 		_state->setCurrentRoot(27, 2, 1);
 	}
 }
@@ -762,7 +787,7 @@ void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
 	_room->addStickerToRoom(_room->_currentRoomNumber, 10, PERSIST_PERM);
 	_room->disableHotspot(_room->findHotspotByExtra(295)); // Disable storefront hotspot
 	_room->disableHotspot(_room->findHotspotByExtra(294)); // Disable window hotspot
-	_disableAction = true; // Prevent player from doing anything until they move Alfred
+	_disableAction = true;                                 // Prevent player from doing anything until they move Alfred
 	walkTo(630, _alfredState.y);
 }
 
@@ -834,10 +859,10 @@ void PelrockEngine::useCordWithPlug(int inventoryObject, HotSpot *hotspot) {
 	if (!_room->hasSticker(18)) {
 		_dialog->say(_res->_ingameTexts[PRIMERO_ABRIRLO]);
 	} else {
-		debug("Flag is %d", _state->getFlag(FLAG_CABLES_PUESTOS));
 		if (_state->getFlag(FLAG_CABLES_PUESTOS)) {
 			_room->addSticker(19);
 			_room->moveHotspot(_room->findHotspotByIndex(6), 391, 381);
+			_state->removeInventoryItem(6);
 		}
 	}
 }
@@ -870,7 +895,7 @@ void PelrockEngine::pickCables(HotSpot *hotspot) {
 	_state->setCurrentRoot(4, 1, 0);
 }
 
-void PelrockEngine::giveIdToGuard(int inventoryObject, HotSpot *hotspot) {
+void PelrockEngine::showIdToGuard(int inventoryObject, HotSpot *hotspot) {
 	if (!_state->getFlag(FLAG_GUARDIA_PIDECOSAS)) {
 		_dialog->say(_res->_ingameTexts[CUANDOMELOPIDA]);
 		return;
@@ -1019,8 +1044,10 @@ void PelrockEngine::closeTravelAgencyDoor(HotSpot *hotspot) {
 }
 
 void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
+	_state->removeInventoryItem(76);
+	addInventoryItem(86);
 	_sound->playMusicTrack(27);
-	_dialog->say(_res->_ingameTexts[PRIMERINGREDIENTE]);
+	checkIngredients();
 	_dialog->say(_res->_ingameTexts[CUIDADOIMPRUDENTE]);
 	_res->loadAlfredSpecialAnim(5);
 	_alfredState.animState = ALFRED_SPECIAL_ANIM;
@@ -1250,6 +1277,54 @@ void PelrockEngine::useDollWithBed(int inventoryObject, HotSpot *hotspot) {
 	_alfredState.y = y;
 }
 
+void PelrockEngine::giveMagazineToGuard(int inventoryObject, HotSpot *hotspot) {
+	_state->removeInventoryItem(84);
+	_state->setCurrentRoot(34, 4, 0);
+	walkAndAction(hotspot, TALK);
+	waitForActionEnd();
+}
+
+void PelrockEngine::giveWaterToGuard(int inventoryObject, HotSpot *hotspot) {
+	_state->setFlag(FLAG_VIGILANTE_BEBE_AGUA, _state->getFlag(FLAG_VIGILANTE_BEBE_AGUA) + 1);
+
+	_dialog->say(_res->_ingameTexts[ALACONUSTED]);
+	_state->removeInventoryItem(86);
+	addInventoryItem(76);
+	if (_state->getFlag(FLAG_VIGILANTE_BEBE_AGUA) == 1) {
+		_dialog->say(_res->_ingameTexts[MEMEO]);
+
+		// guard running
+		Sprite *sprite = _room->findSpriteByIndex(0);
+		sprite->animData[0].nframes = 5;
+		sprite->animData[0].movementFlags = 0x1C; // Move right
+		byte state = 0;
+		//Basic loop to wait until the sprite has reached the door
+		while (!shouldQuit()) {
+			_events->pollEvent();
+			renderScene();
+			if (sprite->x >= 339 && state == 0) {
+				state = 1;
+				sprite->animData[0].movementFlags = 0x240;
+			}
+			if(sprite->y <= 188 && state == 1) {
+				state = 2;
+				sprite->animData[0].movementFlags = 0x14; // Move
+			}
+			if(sprite->x <= 327 && state == 2) {
+				sprite->zOrder = -1; // Hide sprite
+				break;
+			}
+			debug("Guard position: (%d, %d), state: %d", sprite->x, sprite->y, state);
+			_screen->update();
+			g_system->delayMillis(10);
+		}
+		_room->disableSprite(36, 0, PERSIST_BOTH);
+		_room->enableExit(0, PERSIST_BOTH);
+	} else {
+		_state->setCurrentRoot(36, _state->getCurrentRoot(36, 0) + 1, 0);
+	}
+}
+
 void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	debug("Performing action trigger: %d", actionTrigger);
 	switch (actionTrigger) {
@@ -1404,7 +1479,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		break;
 	case 108:
 	case 109: {
-		if(_state->hasInventoryItem(110) == true) {
+		if (_state->hasInventoryItem(110) == true) {
 			_state->removeInventoryItem(110);
 			_state->removeInventoryItem(109);
 			_state->removeInventoryItem(108);
@@ -1413,6 +1488,13 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		}
 		break;
 	}
+	case 84: {
+		_res->loadAlfredSpecialAnim(1);
+		_alfredState.animState = ALFRED_SPECIAL_ANIM;
+		waitForSpecialAnimation();
+		loadExtraScreenAndPresent(7);
+		break;
+	}
 	default:
 		break;
 	}
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index f77ae9531c4..0c35e16c060 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -469,20 +469,15 @@ const ExtraImages extraScreens[] = {
 	{0xFFC47, // girl book
 	 0x1180C9,
 	 8},
-	/*{0x1183C5, // book
-	 0x1358F3,
-	 8},*/
-{	1147849,
-	1267955,
-	8
-},
+	{1147849,
+	 1267955,
+	 8},
 	{0x152A88, // portrait
 	 0x15BFC8,
 	 8},
-	 {2727564, // CD
+	{2727564, // CD
 	 2833276,
-	 8}
-};
+	 8}};
 
 struct AlfredSpecialAnimOffset {
 	int numFrames = 0;
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index b92baaf6b24..274cba31d19 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -157,7 +157,7 @@ void PelrockEngine::init() {
 		// setScreen(0, ALFRED_DOWN);
 		// setScreen(3, ALFRED_RIGHT);
 		// setScreen(22, ALFRED_DOWN);
-		setScreen(31, ALFRED_DOWN);
+		setScreen(36, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
@@ -584,7 +584,7 @@ void PelrockEngine::updateAnimations() {
 
 void PelrockEngine::presentFrame() {
 	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
-	paintDebugLayer();
+	// paintDebugLayer();
 	_screen->markAllDirty();
 }
 
@@ -1160,10 +1160,6 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 		return;
 	}
 
-	if (_room->_currentRoomNumber == 9 && sprite->index == 2) {
-		debug("Drawing sprite 2 in room 9, anim %d, frame %d/%d, loop %d/%d", sprite->curAnimIndex, animData.curFrame, animData.nframes, animData.curLoop, animData.loopCount);
-	}
-
 	applyMovement(&(sprite->x), &(sprite->y), &(sprite->zOrder), animData.movementFlags);
 	int x = sprite->x;
 	int y = sprite->y;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index bf34a6b8ea7..6960f7d0e26 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -299,7 +299,7 @@ public:
 	void openPlug(HotSpot *hotspot);
 	void useCordWithPlug(int inventoryObject, HotSpot *hotspot);
 	void pickCables(HotSpot *hotspot);
-	void giveIdToGuard(int inventoryObject, HotSpot *hotspot);
+	void showIdToGuard(int inventoryObject, HotSpot *hotspot);
 	void unlockMuseum();
 	void giveMoneyToGuard(int inventoryObject, HotSpot *hotspot);
 	void openMuseumDoor(HotSpot *hotspot);
@@ -343,6 +343,8 @@ public:
 	void openTunnelDoor(HotSpot *hotspot);
 	void closeTunnelDoor(HotSpot *hotspot);
 	void useDollWithBed(int inventoryObject, HotSpot *hotspot);
+	void giveMagazineToGuard(int inventoryObject, HotSpot *hotspot);
+	void giveWaterToGuard(int inventoryObject, HotSpot *hotspot);
 	void checkAllSymbols();
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index c1744b15e5e..c9c5c9e2b3f 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -224,8 +224,10 @@ void RoomManager::disableSprite(byte spriteIndex, int persist) {
 }
 
 void RoomManager::disableSprite(byte roomNumber, byte spriteIndex, int persist) {
+	debug("Disabling sprite %d in room %d with persist %d", spriteIndex, roomNumber, persist);
 	if (roomNumber == _currentRoomNumber && persist & PERSIST_TEMP) {
-		_currentRoomAnims[spriteIndex].zOrder = 255;
+		debug("Disabling sprite LOCALLY %d in room %d with persist %d", spriteIndex, roomNumber, persist);
+		_currentRoomAnims[spriteIndex].zOrder = -1;
 		_currentRoomAnims[spriteIndex].isHotspotDisabled = true;
 	}
 	if (persist & PERSIST_PERM) {
@@ -940,6 +942,12 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 				// debug("  Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d loopCount=%d speed=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes, anim.loopCount, anim.speed);
 				// debug("  Movement flags: 0x%04X", anim.movementFlags);
 				picOffset += needed;
+
+				if(_currentRoomNumber == 36 && i == 0) {
+					// Room 36 sets its anim to 1 to appear idle and only enables anim later on
+					anim.nframes = 1;
+				}
+
 			} else {
 				continue;
 				// debug("Anim %d-%d: invalid dimensions, skipping", i, j);


Commit: 19605d40a5c186d9965f10960636a449e947efc5
    https://github.com/scummvm/scummvm/commit/19605d40a5c186d9965f10960636a449e947efc5
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:06+02:00

Commit Message:
PELROCK: Implements room 37

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index fac151a1a7c..7f1ee1854a3 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -146,6 +146,9 @@ const ActionEntry actionTable[] = {
 	{465, OPEN, &PelrockEngine::openTunnelDoor},
 	{465, CLOSE, &PelrockEngine::closeTunnelDoor},
 
+	//Room 37
+	{90, PICKUP, &PelrockEngine::pickUpStone},
+
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
 	{WILDCARD, TALK, &PelrockEngine::noOpAction},   // Generic talk action
@@ -540,7 +543,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_room->enableExit(0);
 		Sprite *sprite = _room->findSpriteByIndex(0);
 		sprite->animData[0].movementFlags = 0x1C; // Move right
-		//Basic loop to wait until the sprite has reached the door
+		// Basic loop to wait until the sprite has reached the door
 		while (!shouldQuit()) {
 			_events->pollEvent();
 			renderScene();
@@ -1298,7 +1301,7 @@ void PelrockEngine::giveWaterToGuard(int inventoryObject, HotSpot *hotspot) {
 		sprite->animData[0].nframes = 5;
 		sprite->animData[0].movementFlags = 0x1C; // Move right
 		byte state = 0;
-		//Basic loop to wait until the sprite has reached the door
+		// Basic loop to wait until the sprite has reached the door
 		while (!shouldQuit()) {
 			_events->pollEvent();
 			renderScene();
@@ -1306,11 +1309,11 @@ void PelrockEngine::giveWaterToGuard(int inventoryObject, HotSpot *hotspot) {
 				state = 1;
 				sprite->animData[0].movementFlags = 0x240;
 			}
-			if(sprite->y <= 188 && state == 1) {
+			if (sprite->y <= 188 && state == 1) {
 				state = 2;
 				sprite->animData[0].movementFlags = 0x14; // Move
 			}
-			if(sprite->x <= 327 && state == 2) {
+			if (sprite->x <= 327 && state == 2) {
 				sprite->zOrder = -1; // Hide sprite
 				break;
 			}
@@ -1325,6 +1328,10 @@ void PelrockEngine::giveWaterToGuard(int inventoryObject, HotSpot *hotspot) {
 	}
 }
 
+void PelrockEngine::pickUpStone(HotSpot *hotspot) {
+	checkIngredients();
+}
+
 void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	debug("Performing action trigger: %d", actionTrigger);
 	switch (actionTrigger) {
@@ -1357,11 +1364,19 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	case 327:
 		_state->setFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO, true);
 		break;
-	case 294:
+	case 294: {
 		HotSpot *floorTile = _room->findHotspotByExtra(462);
 		floorTile->actionFlags = ACTION_MASK_OPEN;
 		_room->changeHotSpot(*floorTile);
 	}
+
+	case 307: {
+		HotSpot *stone = _room->findHotspotByExtra(90);
+		stone->actionFlags = ACTION_MASK_PICKUP;
+		_room->changeHotSpot(*stone);
+		break;
+	}
+	}
 }
 
 void PelrockEngine::useOnAlfred(int inventoryObject) {
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 274cba31d19..4e0e8b60948 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -584,7 +584,7 @@ void PelrockEngine::updateAnimations() {
 
 void PelrockEngine::presentFrame() {
 	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
-	// paintDebugLayer();
+	paintDebugLayer();
 	_screen->markAllDirty();
 }
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 6960f7d0e26..e856b1843c3 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -345,6 +345,7 @@ public:
 	void useDollWithBed(int inventoryObject, HotSpot *hotspot);
 	void giveMagazineToGuard(int inventoryObject, HotSpot *hotspot);
 	void giveWaterToGuard(int inventoryObject, HotSpot *hotspot);
+	void pickUpStone(HotSpot *hotspot);
 	void checkAllSymbols();
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index c9c5c9e2b3f..214be21a605 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -212,6 +212,7 @@ void RoomManager::changeHotspot(byte room, HotSpot hotspot, int persist) {
 				_currentRoomHotspots[i] = hotspot;
 				break;
 			}
+			debug("Hotspot %d in room %d does not match changed hotspot inner index %d", _currentRoomHotspots[i].innerIndex, room, hotspot.innerIndex);
 		}
 	}
 	if (persist & PERSIST_PERM) {


Commit: 1e225caa411cd76d51494ac3c87acb73de7ace1c
    https://github.com/scummvm/scummvm/commit/1e225caa411cd76d51494ac3c87acb73de7ace1c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:07+02:00

Commit Message:
PELROCK: Implemented time travel animation

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 7f1ee1854a3..02d3f9f0229 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -146,7 +146,7 @@ const ActionEntry actionTable[] = {
 	{465, OPEN, &PelrockEngine::openTunnelDoor},
 	{465, CLOSE, &PelrockEngine::closeTunnelDoor},
 
-	//Room 37
+	// Room 37
 	{90, PICKUP, &PelrockEngine::pickUpStone},
 
 	// Generic handlers
@@ -179,6 +179,12 @@ const CombinationEntry combinationTable[] = {
 	{83, 461, &PelrockEngine::useDollWithBed},
 	{84, 503, &PelrockEngine::giveMagazineToGuard},
 	{86, 500, &PelrockEngine::giveWaterToGuard},
+
+	// Room 35 (cauldron)
+	{90, 506, &PelrockEngine::magicFormula},
+	{85, 506, &PelrockEngine::magicFormula},
+	{86, 506, &PelrockEngine::magicFormula},
+	{81, 506, &PelrockEngine::magicFormula},
 	// End marker
 	{WILDCARD, WILDCARD, nullptr}};
 
@@ -1332,6 +1338,44 @@ void PelrockEngine::pickUpStone(HotSpot *hotspot) {
 	checkIngredients();
 }
 
+void PelrockEngine::magicFormula(int inventoryObject, HotSpot *hotspot) {
+	_state->removeInventoryItem(inventoryObject);
+	_state->setFlag(FLAG_FORMULA_MAGICA, _state->getFlag(FLAG_FORMULA_MAGICA) + 1);
+	if (_state->getFlag(FLAG_FORMULA_MAGICA) == 4) {
+
+		size_t frameSize = 98 * 138;
+		size_t bufSize = frameSize * 11;
+		byte *smokeFrames = new byte[bufSize];
+		_res->loadOtherSpecialAnim(1526432, true, smokeFrames, bufSize);
+		Graphics::Surface smokeSurface;
+		smokeSurface.create(98, 138, Graphics::PixelFormat::createFormatCLUT8());
+		int curFrame = 0;
+		while (!shouldQuit()) {
+			_events->pollEvent();
+
+			bool didRender = renderScene(OVERLAY_NONE);
+
+			memset(smokeSurface.getPixels(), 0, frameSize);
+			extractSingleFrame(smokeFrames, (byte *)smokeSurface.getPixels(), curFrame, 98, 138);
+			_screen->transBlitFrom(smokeSurface, Common::Point(_alfredState.x, _alfredState.y - _alfredState.h), 255);
+			if (curFrame == 5) {
+				_alfredState.setState(ALFRED_SKIP_DRAWING);
+			}
+			if (didRender && _chrono->getFrameCount() % 2 == 0) {
+				curFrame++;
+
+				if (curFrame >= 11) {
+					break;
+				}
+			}
+			_screen->update();
+			g_system->delayMillis(10);
+		}
+		_alfredState.setState(ALFRED_IDLE);
+		setScreen(39, ALFRED_UP);
+	}
+}
+
 void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	debug("Performing action trigger: %d", actionTrigger);
 	switch (actionTrigger) {
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 4e0e8b60948..79ba055d22b 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -157,7 +157,7 @@ void PelrockEngine::init() {
 		// setScreen(0, ALFRED_DOWN);
 		// setScreen(3, ALFRED_RIGHT);
 		// setScreen(22, ALFRED_DOWN);
-		setScreen(36, ALFRED_DOWN);
+		setScreen(35, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
@@ -824,6 +824,9 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 	}
 
 	switch (_alfredState.animState) {
+	case ALFRED_SKIP_DRAWING: {
+		break;
+	}
 	case ALFRED_IDLE: {
 		drawAlfred(_res->alfredIdle[_alfredState.direction]);
 		break;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index e856b1843c3..9f9716bc69f 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -346,6 +346,7 @@ public:
 	void giveMagazineToGuard(int inventoryObject, HotSpot *hotspot);
 	void giveWaterToGuard(int inventoryObject, HotSpot *hotspot);
 	void pickUpStone(HotSpot *hotspot);
+	void magicFormula(int inventoryObject, HotSpot *hotspot);
 	void checkAllSymbols();
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 1a150749150..2be2c3aec2f 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -50,6 +50,7 @@ const AlfredSpecialAnimOffset ResourceManager::alfredSpecialAnims[] = {
 	{7, 208, 102, 0, 7, 1600956, 1}, // 10 - alfred with workers
 	{23, 116, 124, 1, 7, 2060916, 1}, // 11 - Munheco 1
 	{18, 177, 124, 1, 7, 2115632, 1}, // 12 - Munheco 2
+	{11, 98, 138, 1, 7, 1526432, 1}, // 13 - Munheco 3
 };
 
 ResourceManager::~ResourceManager() {
@@ -234,6 +235,24 @@ void ResourceManager::loadAlfredAnims() {
 	free(alfredCombLeftRaw);
 }
 
+void ResourceManager::loadOtherSpecialAnim(uint32 offset, bool rleCompressed, byte *&buffer, size_t bufferSize) {
+	Common::File alfred7;
+	if (!alfred7.open(Common::Path("ALFRED.7"))) {
+		error("Could not open ALFRED.7");
+		return;
+	}
+
+	if (rleCompressed) {
+		byte *compressed = nullptr;
+		size_t compressedSize = 0;
+		readUntilBuda(&alfred7, offset, compressed, compressedSize);
+		rleDecompress(compressed, compressedSize, 0, bufferSize, &buffer, false);
+	}
+
+	alfred7.close();
+
+}
+
 void ResourceManager::loadAlfredSpecialAnim(int numAnim, bool reverse) {
 	AlfredSpecialAnimOffset anim = alfredSpecialAnims[numAnim];
 
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index bcada5183d0..cd1b3bc1520 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -45,6 +45,7 @@ public:
 	void loadCursors();
 	void loadInteractionIcons();
 	void loadAlfredAnims();
+	void loadOtherSpecialAnim(uint32 offset, bool rleCompressed, byte *&buffer, size_t bufferSize);
 	void loadAlfredSpecialAnim(int numAnim, bool reverse = false);
 	void clearSpecialAnim();
 	void loadInventoryItems();
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 967c70e01bb..6fad2fb3e63 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -114,7 +114,8 @@ enum AlfredAnimState {
 	ALFRED_TALKING,
 	ALFRED_INTERACTING,
 	ALFRED_COMB,
-	ALFRED_SPECIAL_ANIM
+	ALFRED_SPECIAL_ANIM,
+	ALFRED_SKIP_DRAWING
 };
 
 enum AlfredDirection {


Commit: 63056fbb62e00dfaa0f180ba6bba10bad01d42da
    https://github.com/scummvm/scummvm/commit/63056fbb62e00dfaa0f180ba6bba10bad01d42da
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:07+02:00

Commit Message:
PELROCK: Swimming pool cutscene (partial implementation)

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/room.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 02d3f9f0229..720d5bb6f61 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -149,6 +149,8 @@ const ActionEntry actionTable[] = {
 	// Room 37
 	{90, PICKUP, &PelrockEngine::pickUpStone},
 
+	// Room 39
+	{700, PICKUP, &PelrockEngine::swimmingPoolCutscene},
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
 	{WILDCARD, TALK, &PelrockEngine::noOpAction},   // Generic talk action
@@ -1313,11 +1315,11 @@ void PelrockEngine::giveWaterToGuard(int inventoryObject, HotSpot *hotspot) {
 			renderScene();
 			if (sprite->x >= 339 && state == 0) {
 				state = 1;
-				sprite->animData[0].movementFlags = 0x240;
+				sprite->animData[0].movementFlags = 0x240; // Move up
 			}
 			if (sprite->y <= 188 && state == 1) {
 				state = 2;
-				sprite->animData[0].movementFlags = 0x14; // Move
+				sprite->animData[0].movementFlags = 0x14; // Move left
 			}
 			if (sprite->x <= 327 && state == 2) {
 				sprite->zOrder = -1; // Hide sprite
@@ -1338,6 +1340,101 @@ void PelrockEngine::pickUpStone(HotSpot *hotspot) {
 	checkIngredients();
 }
 
+/**
+ * Naked girls swim underwater, guard enters the scene.
+ * "diving" animation is stored in Alfred.7 as a single RLE chunk with multiple continuous sprite sizes.
+ */
+void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
+	byte *buffer = nullptr;
+	size_t bufSize = 0;
+
+	struct SwimmerInfo {
+		int w;
+		int h;
+		int nFrames;
+		int curFrame;
+		int totalSize;
+		Sprite *sprite;
+		int x;
+		int y;
+		byte *data;
+	};
+
+	Sprite *sprite1 = _room->findSpriteByIndex(3);
+	Sprite *sprite2 = _room->findSpriteByIndex(4);
+	Sprite *sprite3 = _room->findSpriteByIndex(5);
+	Sprite *sprite4 = _room->findSpriteByIndex(6);
+	SwimmerInfo swimmers[4];
+	swimmers[0] = {93, 88, 9, 0, 93 * 88 * 9, sprite1, sprite1->x, sprite1->y - 30, nullptr};
+	swimmers[1] = {68, 31, 7, 0, 68 * 31 * 7, sprite2, sprite2->x, sprite2->y, nullptr};
+	swimmers[2] = {79, 95, 9, 0, 79 * 95 * 9, sprite3, sprite3->x, sprite3->y, nullptr};
+	swimmers[3] = {54, 42, 8, 0, 54 * 42 * 8, sprite4, sprite4->x, sprite4->y, nullptr};
+
+	_res->loadOtherSpecialAnim(1446862, true, buffer, bufSize);
+
+	int acc = swimmers[0].totalSize;
+	swimmers[0].data = new byte[swimmers[0].totalSize];
+	Common::copy(buffer, buffer + swimmers[0].totalSize, swimmers[0].data);
+
+	swimmers[1].data = new byte[swimmers[1].totalSize];
+	Common::copy(buffer + acc, buffer + acc + swimmers[1].totalSize, swimmers[1].data);
+	acc += swimmers[1].totalSize;
+
+	swimmers[2].data = new byte[swimmers[2].totalSize];
+	Common::copy(buffer + acc, buffer + acc + swimmers[2].totalSize, swimmers[2].data);
+	acc += swimmers[2].totalSize;
+
+	swimmers[3].data = new byte[swimmers[3].totalSize];
+	Common::copy(buffer + acc, buffer + acc + swimmers[3].totalSize, swimmers[3].data);
+	acc += swimmers[3].totalSize;
+
+	Graphics::Surface surfaces[4];
+	for (int i = 0; i < 4; i++) {
+		surfaces[i].create(swimmers[i].w, swimmers[i].h, Graphics::PixelFormat::createFormatCLUT8());
+	}
+	sprite1->zOrder = -1;
+	sprite2->zOrder = -1;
+	sprite3->zOrder = -1;
+	sprite4->zOrder = -1;
+
+	while (!shouldQuit()) {
+		_events->pollEvent();
+		bool didRender = renderScene(OVERLAY_NONE);
+		for (int i = 0; i < 4; i++) {
+			if (swimmers[i].curFrame >= swimmers[i].nFrames) {
+				continue;
+			}
+			debug("Swimmer %d position: (%d, %d)", i + 1, swimmers[i].sprite->x, swimmers[i].sprite->y);
+			memset(surfaces[i].getPixels(), 0, swimmers[i].w * swimmers[i].h);
+			extractSingleFrame(swimmers[i].data, (byte *)surfaces[i].getPixels(), swimmers[i].curFrame, swimmers[i].w, swimmers[i].h);
+			debug("Moving swimmer %d to (%d, %d)", i + 1, swimmers[i].x, swimmers[i].y);
+			_screen->transBlitFrom(surfaces[i], Common::Point(swimmers[i].x, swimmers[i].y), 255);
+		}
+
+		if (didRender && _chrono->getFrameCount() % 2 == 0) {
+			if (swimmers[0].curFrame < swimmers[0].nFrames)
+				swimmers[0].curFrame++;
+			if (swimmers[1].curFrame < swimmers[1].nFrames)
+				swimmers[1].curFrame++;
+			if (swimmers[2].curFrame < swimmers[2].nFrames)
+				swimmers[2].curFrame++;
+			if (swimmers[3].curFrame < swimmers[3].nFrames)
+				swimmers[3].curFrame++;
+			_events->waitForKey();
+			if (swimmers[0].curFrame >= swimmers[0].nFrames &&
+				swimmers[1].curFrame >= swimmers[1].nFrames &&
+				swimmers[2].curFrame >= swimmers[2].nFrames &&
+				swimmers[3].curFrame >= swimmers[3].nFrames) {
+				break;
+			}
+		}
+		_screen->update();
+		g_system->delayMillis(10);
+	}
+	Sprite *guard = _room->findSpriteByIndex(0);
+	guard->animData[0].movementFlags = 0x14;
+}
+
 void PelrockEngine::magicFormula(int inventoryObject, HotSpot *hotspot) {
 	_state->removeInventoryItem(inventoryObject);
 	_state->setFlag(FLAG_FORMULA_MAGICA, _state->getFlag(FLAG_FORMULA_MAGICA) + 1);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 79ba055d22b..9f702636187 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -157,7 +157,7 @@ void PelrockEngine::init() {
 		// setScreen(0, ALFRED_DOWN);
 		// setScreen(3, ALFRED_RIGHT);
 		// setScreen(22, ALFRED_DOWN);
-		setScreen(35, ALFRED_DOWN);
+		setScreen(39, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 9f9716bc69f..711e6bbafe9 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -346,6 +346,7 @@ public:
 	void giveMagazineToGuard(int inventoryObject, HotSpot *hotspot);
 	void giveWaterToGuard(int inventoryObject, HotSpot *hotspot);
 	void pickUpStone(HotSpot *hotspot);
+	void swimmingPoolCutscene(HotSpot *hotspot);
 	void magicFormula(int inventoryObject, HotSpot *hotspot);
 	void checkAllSymbols();
 	void openMcDoor(HotSpot *hotspot);
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 2be2c3aec2f..0e99b0359fa 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -235,7 +235,7 @@ void ResourceManager::loadAlfredAnims() {
 	free(alfredCombLeftRaw);
 }
 
-void ResourceManager::loadOtherSpecialAnim(uint32 offset, bool rleCompressed, byte *&buffer, size_t bufferSize) {
+void ResourceManager::loadOtherSpecialAnim(uint32 offset, bool rleCompressed, byte *&buffer, size_t &bufferSize) {
 	Common::File alfred7;
 	if (!alfred7.open(Common::Path("ALFRED.7"))) {
 		error("Could not open ALFRED.7");
@@ -246,11 +246,9 @@ void ResourceManager::loadOtherSpecialAnim(uint32 offset, bool rleCompressed, by
 		byte *compressed = nullptr;
 		size_t compressedSize = 0;
 		readUntilBuda(&alfred7, offset, compressed, compressedSize);
-		rleDecompress(compressed, compressedSize, 0, bufferSize, &buffer, false);
+		bufferSize = rleDecompress(compressed, compressedSize, 0, 0, &buffer, true);
 	}
-
 	alfred7.close();
-
 }
 
 void ResourceManager::loadAlfredSpecialAnim(int numAnim, bool reverse) {
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index cd1b3bc1520..e016fff7c93 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -45,7 +45,7 @@ public:
 	void loadCursors();
 	void loadInteractionIcons();
 	void loadAlfredAnims();
-	void loadOtherSpecialAnim(uint32 offset, bool rleCompressed, byte *&buffer, size_t bufferSize);
+	void loadOtherSpecialAnim(uint32 offset, bool rleCompressed, byte *&buffer, size_t &bufferSize);
 	void loadAlfredSpecialAnim(int numAnim, bool reverse = false);
 	void clearSpecialAnim();
 	void loadInventoryItems();
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 03b666c9320..7197ece76d7 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -44,13 +44,6 @@ static const int unpickableHotspotExtras[] = {
 	74,
 	6,
 	7,
-	316, // wires
-	357, // mailbox should pick a different hotspot,
-	360,  // library shelves
-	361,
-	362,
-	472, // matches
-	609 // sunflower
 };
 
 
@@ -120,6 +113,8 @@ public:
 
 
 	bool isPickableByExtra(uint16 extra) {
+		if(extra > 112)
+			return false;
 		int size = sizeof(unpickableHotspotExtras) / sizeof(unpickableHotspotExtras[0]);
 		for (int i = 0; i < size; i++) {
 			if (extra == unpickableHotspotExtras[i])


Commit: bcf440271ce377794a16f21d365c92fa12a86b41
    https://github.com/scummvm/scummvm/commit/bcf440271ce377794a16f21d365c92fa12a86b41
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:07+02:00

Commit Message:
PELROCK: Implement police showing up after raiding tomb

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 720d5bb6f61..1beb1db5be0 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -415,6 +415,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		break;
 	case 352:
 	case 355:
+	case 291:
 		// a la carcel
 		toJail();
 		break;
@@ -1226,6 +1227,7 @@ void PelrockEngine::checkAllSymbols() {
 
 void PelrockEngine::pickUpHairStrand(HotSpot *hotspot) {
 	checkIngredients();
+	_state->setFlag(FLAG_ROBA_PELO_PRINCESA,true);
 }
 
 void PelrockEngine::openJailFloorTile(HotSpot *hotspot) {
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 9f702636187..d9e6ba05cb6 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1835,6 +1835,8 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			_res->loadAlfredSpecialAnim(6);
 			_alfredState.setState(ALFRED_SPECIAL_ANIM);
 			waitForSpecialAnimation();
+
+			_room->addStickerToRoom(38, 123, PERSIST_TEMP);
 			_alfredState.x = x;
 			_alfredState.y = y;
 			break;
@@ -1889,6 +1891,15 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		}
 		break;
 	}
+	case 30: {
+		if(_state->getFlag(FLAG_ROBA_PELO_PRINCESA) == true) {
+			_state->setFlag(FLAG_ROBA_PELO_PRINCESA, false);
+			_room->enableSprite(0, 200, PERSIST_TEMP);
+			_dialog->say(_res->_ingameTexts[OIGAUSTED]);
+			walkAndAction(_room->findHotspotByExtra(0), TALK);
+		}
+		break;
+	}
 	default:
 		break;
 	}
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 6fad2fb3e63..67002cb1ee1 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -474,20 +474,21 @@ struct ResetEntry {
 #define FLAG_CLAVE_CAJA_FUERTE 19
 #define FLAG_INGREDIENTES_CONSEGUIDOS 48
 #define FLAG_MERCHANT_GIVENITEMS 58
+#define FLAG_ROBA_PELO_PRINCESA 17
+#define FLAG_A_LA_CARCEL 18
+#define FLAG_SE_HA_PUESTO_EL_MUNECO 20
+#define FLAG_VIGILANTE_BEBE_AGUA 21
+#define FLAG_FORMULA_MAGICA 26
 
 
 
 #define FLAG_VIAJE_A_EGIPTO 12
+
 #define FLAG_PUERTA_SECRETA_ABIERTA 16
-#define FLAG_ROBA_PELO_PRINCESA 17
-#define FLAG_A_LA_CARCEL 18
-#define FLAG_SE_HA_PUESTO_EL_MUNECO 20
-#define FLAG_VIGILANTE_BEBE_AGUA 21
 #define FLAG_VIGILANTE_MEANDO 22
 #define FLAG_PIRAMIDE_JODIDA 23
 #define FLAG_PIRAMIDE_JODIDA2 24
 #define FLAG_VIGILANTE_PAJEANDOSE 25
-#define FLAG_FORMULA_MAGICA 26
 #define FLAG_VIAJA_AL_PASADO 27
 #define FLAG_APARECE_EUNUCO 28
 #define FLAG_AL_FARAON 29


Commit: e71aa5b8d1beba4dea4f897e3f717156015c9895
    https://github.com/scummvm/scummvm/commit/e71aa5b8d1beba4dea4f897e3f717156015c9895
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:08+02:00

Commit Message:
PELROCK: Full animations in swimming pool cutscene

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 1beb1db5be0..9b3a604f19d 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1227,7 +1227,7 @@ void PelrockEngine::checkAllSymbols() {
 
 void PelrockEngine::pickUpHairStrand(HotSpot *hotspot) {
 	checkIngredients();
-	_state->setFlag(FLAG_ROBA_PELO_PRINCESA,true);
+	_state->setFlag(FLAG_ROBA_PELO_PRINCESA, true);
 }
 
 void PelrockEngine::openJailFloorTile(HotSpot *hotspot) {
@@ -1351,82 +1351,58 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 	size_t bufSize = 0;
 
 	struct SwimmerInfo {
+		int spriteIndex;
+		int16 x;
+		int16 y;
 		int w;
 		int h;
 		int nFrames;
-		int curFrame;
 		int totalSize;
-		Sprite *sprite;
-		int x;
-		int y;
-		byte *data;
+		uint16 movementFlags;
 	};
 
-	Sprite *sprite1 = _room->findSpriteByIndex(3);
-	Sprite *sprite2 = _room->findSpriteByIndex(4);
-	Sprite *sprite3 = _room->findSpriteByIndex(5);
-	Sprite *sprite4 = _room->findSpriteByIndex(6);
-	SwimmerInfo swimmers[4];
-	swimmers[0] = {93, 88, 9, 0, 93 * 88 * 9, sprite1, sprite1->x, sprite1->y - 30, nullptr};
-	swimmers[1] = {68, 31, 7, 0, 68 * 31 * 7, sprite2, sprite2->x, sprite2->y, nullptr};
-	swimmers[2] = {79, 95, 9, 0, 79 * 95 * 9, sprite3, sprite3->x, sprite3->y, nullptr};
-	swimmers[3] = {54, 42, 8, 0, 54 * 42 * 8, sprite4, sprite4->x, sprite4->y, nullptr};
+	SwimmerInfo swimmers[4] = {
+		{3, 3, -17, 93, 88, 9, 93 * 88 * 9, 0x02FF}, // Move right and up
+		{4, 1, 0, 68, 31, 7, 68 * 31 * 7, 0},
+		{5, -14, -18, 79, 95, 9, 79 * 95 * 9, 0},
+		{6, -1, -8, 54, 42, 8, 54 * 42 * 8, 0}};
 
 	_res->loadOtherSpecialAnim(1446862, true, buffer, bufSize);
 
-	int acc = swimmers[0].totalSize;
-	swimmers[0].data = new byte[swimmers[0].totalSize];
-	Common::copy(buffer, buffer + swimmers[0].totalSize, swimmers[0].data);
-
-	swimmers[1].data = new byte[swimmers[1].totalSize];
-	Common::copy(buffer + acc, buffer + acc + swimmers[1].totalSize, swimmers[1].data);
-	acc += swimmers[1].totalSize;
-
-	swimmers[2].data = new byte[swimmers[2].totalSize];
-	Common::copy(buffer + acc, buffer + acc + swimmers[2].totalSize, swimmers[2].data);
-	acc += swimmers[2].totalSize;
-
-	swimmers[3].data = new byte[swimmers[3].totalSize];
-	Common::copy(buffer + acc, buffer + acc + swimmers[3].totalSize, swimmers[3].data);
-	acc += swimmers[3].totalSize;
-
-	Graphics::Surface surfaces[4];
+	int acc = 0;
 	for (int i = 0; i < 4; i++) {
-		surfaces[i].create(swimmers[i].w, swimmers[i].h, Graphics::PixelFormat::createFormatCLUT8());
+		Sprite *sprite = _room->findSpriteByIndex(swimmers[i].spriteIndex);
+		sprite->x = sprite->x + swimmers[i].x;
+		sprite->y = sprite->y + swimmers[i].y;
+		sprite->w = swimmers[i].w;
+		sprite->h = swimmers[i].h;
+		sprite->animData[0].nframes = swimmers[i].nFrames;
+		sprite->animData[0].movementFlags = swimmers[i].movementFlags;
+		sprite->animData[0].curFrame = 0;
+		sprite->animData[0].loopCount = 0;
+		sprite->animData[0].curLoop = 0;
+		sprite->animData[0].speed = 0;
+		sprite->animData[0].elpapsedFrames = 0;
+		sprite->disableAfterSequence = true;
+
+		sprite->animData[0].animData = new byte *[sprite->animData[0].nframes];
+		byte *spriteFrames[sprite->animData[0].nframes];
+		for (int i = 0; i < sprite->animData[0].nframes; i++) {
+			sprite->animData[0].animData[i] = new byte[sprite->w * sprite->h];
+			extractSingleFrame(buffer + acc, sprite->animData[0].animData[i], i, sprite->w, sprite->h);
+		}
+		acc += sprite->w * sprite->h * sprite->animData[0].nframes;
 	}
-	sprite1->zOrder = -1;
-	sprite2->zOrder = -1;
-	sprite3->zOrder = -1;
-	sprite4->zOrder = -1;
 
+	_sound->stopMusic();
+	_sound->playMusicTrack(28);
+	Sprite *s1 = _room->findSpriteByIndex(3);
 	while (!shouldQuit()) {
 		_events->pollEvent();
 		bool didRender = renderScene(OVERLAY_NONE);
-		for (int i = 0; i < 4; i++) {
-			if (swimmers[i].curFrame >= swimmers[i].nFrames) {
-				continue;
-			}
-			debug("Swimmer %d position: (%d, %d)", i + 1, swimmers[i].sprite->x, swimmers[i].sprite->y);
-			memset(surfaces[i].getPixels(), 0, swimmers[i].w * swimmers[i].h);
-			extractSingleFrame(swimmers[i].data, (byte *)surfaces[i].getPixels(), swimmers[i].curFrame, swimmers[i].w, swimmers[i].h);
-			debug("Moving swimmer %d to (%d, %d)", i + 1, swimmers[i].x, swimmers[i].y);
-			_screen->transBlitFrom(surfaces[i], Common::Point(swimmers[i].x, swimmers[i].y), 255);
-		}
 
-		if (didRender && _chrono->getFrameCount() % 2 == 0) {
-			if (swimmers[0].curFrame < swimmers[0].nFrames)
-				swimmers[0].curFrame++;
-			if (swimmers[1].curFrame < swimmers[1].nFrames)
-				swimmers[1].curFrame++;
-			if (swimmers[2].curFrame < swimmers[2].nFrames)
-				swimmers[2].curFrame++;
-			if (swimmers[3].curFrame < swimmers[3].nFrames)
-				swimmers[3].curFrame++;
-			_events->waitForKey();
-			if (swimmers[0].curFrame >= swimmers[0].nFrames &&
-				swimmers[1].curFrame >= swimmers[1].nFrames &&
-				swimmers[2].curFrame >= swimmers[2].nFrames &&
-				swimmers[3].curFrame >= swimmers[3].nFrames) {
+		if (didRender) {
+			if (_room->findSpriteByIndex(swimmers[0].spriteIndex)->zOrder == -1) {
 				break;
 			}
 		}
@@ -1435,6 +1411,21 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 	}
 	Sprite *guard = _room->findSpriteByIndex(0);
 	guard->animData[0].movementFlags = 0x14;
+	while (!shouldQuit()) {
+		_events->pollEvent();
+		renderScene();
+		if (guard->x <= _alfredState.x + 70) {
+			break;
+		}
+		_screen->update();
+		g_system->delayMillis(10);
+	}
+
+	guard->animData[0].movementFlags = 0;
+	guard->animData[0].curFrame = 0;
+	guard->animData[0].nframes = 1;
+	_alfredState.direction = ALFRED_RIGHT;
+	talkTo(_room->findHotspotByExtra(guard->extra));
 }
 
 void PelrockEngine::magicFormula(int inventoryObject, HotSpot *hotspot) {
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index d9e6ba05cb6..c3b0d362887 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -613,7 +613,7 @@ void PelrockEngine::paintDebugLayer() {
 	if (showSprites) {
 		for (uint i = 0; i < _room->_currentRoomAnims.size(); i++) {
 			Sprite sprite = _room->_currentRoomAnims[i];
-			drawRect(_screen, sprite.x, sprite.y, sprite.animData->w, sprite.animData->h, 14);
+			drawRect(_screen, sprite.x, sprite.y, sprite.w, sprite.h, 14);
 			_smallFont->drawString(_screen, Common::String::format("S %d", sprite.index), sprite.x + 2, sprite.y, 640, 14);
 		}
 	}
@@ -1166,8 +1166,8 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 	applyMovement(&(sprite->x), &(sprite->y), &(sprite->zOrder), animData.movementFlags);
 	int x = sprite->x;
 	int y = sprite->y;
-	int w = animData.w;
-	int h = animData.h;
+	int w = sprite->w;
+	int h = sprite->h;
 	if (sprite->isTalking) {
 		animateTalkingNPC(sprite);
 		return;
@@ -1385,10 +1385,10 @@ bool PelrockEngine::isSpriteUnder(Sprite *sprite, int x, int y) {
 	Anim &animData = sprite->animData[sprite->curAnimIndex];
 	int curFrame = animData.curFrame;
 
-	int localX = x - animData.x;
-	int localY = y - animData.y;
-	if (localX >= 0 && localX < animData.w && localY >= 0 && localY < animData.h) {
-		byte pixel = animData.animData[curFrame][localY * animData.w + localX];
+	int localX = x - sprite->x;
+	int localY = y - sprite->y;
+	if (localX >= 0 && localX < sprite->w && localY >= 0 && localY < sprite->h) {
+		byte pixel = animData.animData[curFrame][localY * sprite->w + localX];
 		if (pixel != 255) {
 			return true;
 		}
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 214be21a605..72822cd3a02 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -920,10 +920,6 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 		for (int j = 0; j < sprite.numAnims; j++) {
 
 			Anim anim;
-			anim.x = sprite.x;
-			anim.y = sprite.y;
-			anim.w = sprite.w;
-			anim.h = sprite.h;
 			anim.curFrame = 0;
 
 			anim.nframes = data[subAnimOffset + j];
@@ -931,18 +927,18 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 			anim.speed = data[subAnimOffset + 8 + j];
 			anim.movementFlags = data[subAnimOffset + 14 + (j * 2)] | (data[subAnimOffset + 14 + (j * 2) + 1] << 8);
 
+			uint32_t totalBytesPerFrame = sprite.w * sprite.h * anim.nframes;
 			anim.animData = new byte *[anim.nframes];
-			if (anim.w > 0 && anim.h > 0 && anim.nframes > 0) {
-				uint32_t needed = anim.w * anim.h * anim.nframes;
+			if (sprite.w > 0 && sprite.h > 0 && anim.nframes > 0) {
 				for (int i = 0; i < anim.nframes; i++) {
-					anim.animData[i] = new byte[anim.w * anim.h];
-					// debug("Extracting frame %d for anim %d-%d, w=%d h=%d, pixelDataSize=%d, current offset %d", i, j, anim.nframes, anim.w, anim.h, pixelDataSize, picOffset);
-					extractSingleFrame(pixelData + picOffset, anim.animData[i], i, anim.w, anim.h);
+					anim.animData[i] = new byte[sprite.w * sprite.h];
+					// debug("Extracting frame %d for anim %d-%d, w=%d h=%d, pixelDataSize=%d, current offset %d", i, j, anim.nframes, sprite.w, sprite.h, pixelDataSize, picOffset);
+					extractSingleFrame(pixelData + picOffset, anim.animData[i], i, sprite.w, sprite.h);
 				}
 				sprite.animData[j] = anim;
 				// debug("  Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d loopCount=%d speed=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes, anim.loopCount, anim.speed);
 				// debug("  Movement flags: 0x%04X", anim.movementFlags);
-				picOffset += needed;
+				picOffset += totalBytesPerFrame;
 
 				if(_currentRoomNumber == 36 && i == 0) {
 					// Room 36 sets its anim to 1 to appear idle and only enables anim later on
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 67002cb1ee1..b098259c4b9 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -194,10 +194,6 @@ typedef struct {
 } PathContext;
 
 struct Anim {
-	int16 x;
-	int16 y;
-	int w;
-	int h;
 	int nframes;
 	int curFrame = 0;
 	int curLoop = 0;


Commit: bd9ee4a407996a57adeca07cfc043c07a20aeb11
    https://github.com/scummvm/scummvm/commit/bd9ee4a407996a57adeca07cfc043c07a20aeb11
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:08+02:00

Commit Message:
PELROCK: Implements rest of swimming cutscene

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 9b3a604f19d..b88ab0dc749 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1396,7 +1396,6 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 
 	_sound->stopMusic();
 	_sound->playMusicTrack(28);
-	Sprite *s1 = _room->findSpriteByIndex(3);
 	while (!shouldQuit()) {
 		_events->pollEvent();
 		bool didRender = renderScene(OVERLAY_NONE);
@@ -1426,6 +1425,22 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 	guard->animData[0].nframes = 1;
 	_alfredState.direction = ALFRED_RIGHT;
 	talkTo(_room->findHotspotByExtra(guard->extra));
+	if(shouldQuit()) {
+		return;
+	}
+	_graphics->fadeToBlack(10);
+	_alfredState.x = 271;
+	_alfredState.y = 385;
+	setScreen(40, ALFRED_UP);
+	walkAndAction(_room->findHotspotByExtra(640), TALK);
+	waitForActionEnd();
+	if(shouldQuit()) {
+		return;
+	}
+	_graphics->fadeToBlack(10);
+	_alfredState.x = 271;
+	_alfredState.y = 385;
+	setScreen(41, ALFRED_UP);
 }
 
 void PelrockEngine::magicFormula(int inventoryObject, HotSpot *hotspot) {
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 5636c573c58..fcd6b8503c0 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -736,6 +736,12 @@ ConversationEndResult DialogManager::checkConversationEnd(const byte *data, uint
 }
 
 void DialogManager::addGoodbyeOptionIfNeeded(Common::Array<ChoiceOption> *choices, int currentChoiceLevel, uint originalChoiceCount) {
+	// Room entry handlers can globally disable the goodbye option for certain rooms
+	// (e.g. rooms 39/40 pharaoh, room 48).
+	if (_goodbyeDisabled) {
+		return;
+	}
+
 	// Only consider adding goodbye if there are MULTIPLE choices
 	// If there's only 1 choice, it's auto-dialogue and should not have goodbye
 	if (originalChoiceCount <= 1) {
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 67c124576e6..532515fbcbd 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -113,6 +113,9 @@ public:
 	Common::Array<Common::Array<Common::String>> wordWrap(Common::String text);
 	Common::Array<Common::Array<Common::String>> wordWrap(Common::StringArray texts);
 	Common::Array<ChoiceOption> *_currentChoices = nullptr;
+
+	// When true, the goodbye option is suppressed for all conversations in the current room.
+	bool _goodbyeDisabled = false;
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index c3b0d362887..cd9f635284d 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1811,6 +1811,9 @@ void PelrockEngine::waitForSpecialAnimation() {
 }
 
 void PelrockEngine::doExtraActions(int roomNumber) {
+	// Reset goodbye-disabled flag each room load. Specific rooms set it below.
+	_dialog->_goodbyeDisabled = false;
+
 	switch (roomNumber) {
 	case 4:
 		if (_state->getFlag(FLAG_PUESTA_SALSA_PICANTE) && !_state->getFlag(FLAG_JEFE_ENCARCELADO)) {
@@ -1879,6 +1882,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 	}
 	case 26: {
 		if(_state->getFlag(FLAG_A_LA_CARCEL) == true) {
+			_dialog->_goodbyeDisabled = true;
 			if(_state->getFlag(FLAG_SE_HA_PUESTO_EL_MUNECO) == true) {
 				_state->setCurrentRoot(26, 2, 1);
 			} else {
@@ -1893,6 +1897,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 	}
 	case 30: {
 		if(_state->getFlag(FLAG_ROBA_PELO_PRINCESA) == true) {
+			_dialog->_goodbyeDisabled = true;
 			_state->setFlag(FLAG_ROBA_PELO_PRINCESA, false);
 			_room->enableSprite(0, 200, PERSIST_TEMP);
 			_dialog->say(_res->_ingameTexts[OIGAUSTED]);
@@ -1900,6 +1905,15 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		}
 		break;
 	}
+	case 39:
+	case 40:
+		// Rooms 39/40 are the pharaoh's guard and throne room — all conversation
+		// paths lead to capture, no voluntary exit allowed.
+		_dialog->_goodbyeDisabled = true;
+		break;
+	case 48:
+		_dialog->_goodbyeDisabled = true;
+		break;
 	default:
 		break;
 	}


Commit: afa4597b81bab90c9dbee66d229e496292f53359
    https://github.com/scummvm/scummvm/commit/afa4597b81bab90c9dbee66d229e496292f53359
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:08+02:00

Commit Message:
PELROCK: Fixes bug in walkAndAction where alfred wouldnt end up looking in the right direction

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index b88ab0dc749..9db05d019b6 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1424,7 +1424,8 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 	guard->animData[0].curFrame = 0;
 	guard->animData[0].nframes = 1;
 	_alfredState.direction = ALFRED_RIGHT;
-	talkTo(_room->findHotspotByExtra(guard->extra));
+	walkAndAction(_room->findHotspotByExtra(guard->extra), TALK);
+	waitForActionEnd();
 	if(shouldQuit()) {
 		return;
 	}
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index cd9f635284d..38d35f7b930 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -487,7 +487,7 @@ void PelrockEngine::checkMouse() {
 
 	checkMouseHover();
 
-	if(_disableAction) {
+	if (_disableAction) {
 		return;
 	}
 
@@ -676,11 +676,11 @@ void PelrockEngine::placeSticker(Sticker sticker) {
 		for (int x = 0; x < sticker.w; x++) {
 			byte pixel = sticker.stickerData[y * sticker.w + x];
 			// if (pixel != 0) {
-				int bgX = sticker.x + x;
-				int bgY = sticker.y + y;
-				if (bgX >= 0 && bgX < 640 && bgY >= 0 && bgY < 400) {
-					_compositeBuffer[bgY * 640 + bgX] = pixel;
-				}
+			int bgX = sticker.x + x;
+			int bgY = sticker.y + y;
+			if (bgX >= 0 && bgX < 640 && bgY >= 0 && bgY < 400) {
+				_compositeBuffer[bgY * 640 + bgX] = pixel;
+			}
 			// }
 		}
 	}
@@ -868,14 +868,21 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 				_alfredState.setState(ALFRED_IDLE);
 				_alfredState.isWalkingCancelable = true;
 				_disableAction = false;
-				if (_currentHotspot != nullptr)
+				if (_queuedAction.isQueued) {
+					// When a queued action exists, face toward its target hotspot
+					// (not _currentHotspot which is the mouse-hovered one)
+					HotSpot *targetHotspot = &_room->_currentRoomHotspots[_queuedAction.hotspotIndex];
+					_alfredState.direction = calculateAlfredsDirection(targetHotspot);
+				} else if (_currentHotspot != nullptr) {
 					_alfredState.direction = calculateAlfredsDirection(_currentHotspot);
+				}
 				drawAlfred(_res->alfredIdle[_alfredState.direction]);
 				if (_queuedAction.isQueued) {
 					// look and talk execute immediately, others need interaction animation first
 					if (_queuedAction.verb == TALK || _queuedAction.verb == LOOK) {
 						_queuedAction.isQueued = false;
-						doAction(_queuedAction.verb, &_room->_currentRoomHotspots[_queuedAction.hotspotIndex]);
+						HotSpot *actionHotspot = &_room->_currentRoomHotspots[_queuedAction.hotspotIndex];
+						doAction(_queuedAction.verb, actionHotspot);
 						break;
 					}
 					_alfredState.setState(ALFRED_INTERACTING);
@@ -887,7 +894,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 				Exit *exit = isExitUnder(_alfredState.x, _alfredState.y);
 				if (exit != nullptr) {
 					debug("Using exit to room %d", exit->targetRoom);
-					if(exit->targetRoom == 31 && _room->_currentRoomNumber == 32) {
+					if (exit->targetRoom == 31 && _room->_currentRoomNumber == 32) {
 						_res->loadAlfredSpecialAnim(8);
 						_alfredState.setState(ALFRED_SPECIAL_ANIM);
 						waitForSpecialAnimation();
@@ -1188,7 +1195,7 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 				animData.curFrame = 0;
 				animData.curLoop++;
 			} else {
-				if(sprite->disableAfterSequence) {
+				if (sprite->disableAfterSequence) {
 					sprite->zOrder = -1;
 					return;
 				}
@@ -1503,7 +1510,7 @@ void PelrockEngine::gameLoop() {
 	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_s) {
 		SpellBook spellBook(_events, _res);
 		Spell *selectedSpell = spellBook.run();
-		if(selectedSpell != nullptr) {
+		if (selectedSpell != nullptr) {
 			_dialog->sayAlfred(selectedSpell->text);
 		}
 		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
@@ -1830,7 +1837,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			_dialog->say(_res->_ingameTexts[PINTA_BUENAPERSONA]);
 		}
 	case 38: {
-		if(_room->_prevRoomNumber == 30) {
+		if (_room->_prevRoomNumber == 30) {
 			int x = _alfredState.x;
 			int y = _alfredState.y;
 			_alfredState.x -= 57;
@@ -1846,7 +1853,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		}
 	}
 	case 32: {
-		if(_room->_prevRoomNumber == 31) {
+		if (_room->_prevRoomNumber == 31) {
 			int x = _alfredState.x;
 			int y = _alfredState.y;
 			_res->loadAlfredSpecialAnim(7);
@@ -1854,11 +1861,10 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			waitForSpecialAnimation();
 			_alfredState.x = x;
 			_alfredState.y = y;
-
 		}
 	}
 	case 27: {
-		if(_room->_prevRoomNumber == 33) {
+		if (_room->_prevRoomNumber == 33) {
 			int x = _alfredState.x;
 			int y = _alfredState.y;
 			_alfredState.x = 12;
@@ -1872,7 +1878,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		break;
 	}
 	case 28: {
-		if(_state->getFlag(FLAG_CROCODILLO_ENCENDIDO) == true) {
+		if (_state->getFlag(FLAG_CROCODILLO_ENCENDIDO) == true) {
 			byte palette[768];
 			_res->getPaletteForRoom28(palette);
 			g_system->getPaletteManager()->setPalette(palette, 0, 256);
@@ -1881,9 +1887,9 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		}
 	}
 	case 26: {
-		if(_state->getFlag(FLAG_A_LA_CARCEL) == true) {
+		if (_state->getFlag(FLAG_A_LA_CARCEL) == true) {
 			_dialog->_goodbyeDisabled = true;
-			if(_state->getFlag(FLAG_SE_HA_PUESTO_EL_MUNECO) == true) {
+			if (_state->getFlag(FLAG_SE_HA_PUESTO_EL_MUNECO) == true) {
 				_state->setCurrentRoot(26, 2, 1);
 			} else {
 				_dialog->say(_res->_ingameTexts[OIGAUSTED], 1);
@@ -1896,7 +1902,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		break;
 	}
 	case 30: {
-		if(_state->getFlag(FLAG_ROBA_PELO_PRINCESA) == true) {
+		if (_state->getFlag(FLAG_ROBA_PELO_PRINCESA) == true) {
 			_dialog->_goodbyeDisabled = true;
 			_state->setFlag(FLAG_ROBA_PELO_PRINCESA, false);
 			_room->enableSprite(0, 200, PERSIST_TEMP);


Commit: 892a4db75372ff8b264f4547535a9135e9ebfa66
    https://github.com/scummvm/scummvm/commit/892a4db75372ff8b264f4547535a9135e9ebfa66
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:09+02:00

Commit Message:
PELROCK: Stone pass skeleton

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 9db05d019b6..205408bfb29 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -181,6 +181,8 @@ const CombinationEntry combinationTable[] = {
 	{83, 461, &PelrockEngine::useDollWithBed},
 	{84, 503, &PelrockEngine::giveMagazineToGuard},
 	{86, 500, &PelrockEngine::giveWaterToGuard},
+	{91, 601, &PelrockEngine::giveStoneToSlaves},
+	{92, 601, &PelrockEngine::giveStoneToSlaves}, // Item 92 = mud/clay, same handler
 
 	// Room 35 (cauldron)
 	{90, 506, &PelrockEngine::magicFormula},
@@ -433,6 +435,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 367: // accept riddle
 		_state->setCurrentRoot(room, 27, 0);
 		walkAndAction(_room->findHotspotByExtra(467), TALK);
+		waitForActionEnd();
 		break;
 		// hasta aqui
 
@@ -564,7 +567,17 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		}
 		_room->disableSprite(0);
 	} break;
-
+	case 313:
+		_state->setCurrentRoot(room, rootIndex + 1, 0);
+		break;
+	case 308: {
+		int targetBranch = rootIndex + 1;
+		if (targetBranch > 17) {
+			targetBranch = 3;
+		}
+		_state->setCurrentRoot(room, targetBranch, 0);
+		break;
+	}
 	default:
 		debug("Got actionTrigger %d in dialogActionTrigger, but no handler defined", actionTrigger);
 		break;
@@ -1116,9 +1129,6 @@ void PelrockEngine::pickUpBook(int i) {
 
 		_alfredState.isWalkingCancelable = false;
 		walkAndAction(_room->findHotspotByExtra(102), TALK);
-		// After dialog ends, reenable first dialog root if no photo in inventory
-		// Wait for dialog to end to reenable first dialog root
-		waitForActionEnd();
 		if (!_state->hasInventoryItem(3)) {
 
 			_state->setCurrentRoot(9, 0, 0);
@@ -1294,7 +1304,6 @@ void PelrockEngine::giveMagazineToGuard(int inventoryObject, HotSpot *hotspot) {
 	_state->removeInventoryItem(84);
 	_state->setCurrentRoot(34, 4, 0);
 	walkAndAction(hotspot, TALK);
-	waitForActionEnd();
 }
 
 void PelrockEngine::giveWaterToGuard(int inventoryObject, HotSpot *hotspot) {
@@ -1342,6 +1351,76 @@ void PelrockEngine::pickUpStone(HotSpot *hotspot) {
 	checkIngredients();
 }
 
+void PelrockEngine::giveStoneToSlaves(int inventoryObject, HotSpot *hotspot) {
+	// Remove whichever stone item was used (91 = Egyptian stone, 92 = mud/clay)
+	_state->removeInventoryItem(inventoryObject);
+
+	// Play 7-frame 208×102 stone-passing animation
+	size_t frameSize = 208 * 102;
+	size_t bufSize = frameSize * 7;
+	byte *animData = new byte[bufSize];
+	_res->loadOtherSpecialAnim(1600956, false, animData, bufSize);
+
+	Graphics::Surface animSurface;
+	animSurface.create(208, 102, Graphics::PixelFormat::createFormatCLUT8());
+	int curFrame = 0;
+	while (!shouldQuit()) {
+		_events->pollEvent();
+
+		bool didRender = renderScene(OVERLAY_NONE);
+
+		memset(animSurface.getPixels(), 0, frameSize);
+		extractSingleFrame(animData, (byte *)animSurface.getPixels(), curFrame, 208, 102);
+		_screen->transBlitFrom(animSurface, Common::Point(0, 298), 255);
+		if (didRender && _chrono->getFrameCount() % 2 == 0) {
+			curFrame++;
+
+			if (curFrame >= 7) {
+				break;
+			}
+		}
+		_screen->update();
+		g_system->delayMillis(10);
+	}
+	animSurface.free();
+	delete[] animData;
+
+	//play some sound
+
+	_dialog->say(_res->_ingameTexts[HAYQUECELEBRARLO]);
+
+	// TODO: wait for slave sprite frame 6 animation (sprite index derived from
+	// play drinking animation
+	//   room_sprite_data_ptr + sprite_id * 0x2C, then poll +0x20 == 6)
+
+	// Increment stone delivery counter (tracks 0→1→2→3)
+	byte counter = _state->getFlag(FLAG_DA_PIEDRA);
+	debug("Current stone delivery count: %d", counter);
+	if (counter < 3) {
+		_state->setFlag(FLAG_DA_PIEDRA, ++counter);
+	}
+
+	// At 2nd stone delivery: slave starts singing (conversation root 2)
+	// Root 2 text: "¡Deesde Santuurce a Bilbaooo...!"
+	if (counter == 2) {
+		_state->setCurrentRoot(41, 2, 0);
+	}
+
+	// At 3rd stone delivery: pyramid is complete
+	if (counter == 3) {
+		_room->disableSprite(0);
+		// Render permanent pyramid sticker (ALFRED.6 offset 0x0696AD = sticker index 116)
+		_room->addSticker(116, PERSIST_PERM);
+
+		// TODO: add 5 walkboxes for completed pyramid layout (write_data_to_alfred1 ×4)
+		//   Original game writes walkbox count→5 and walkbox data to ALFRED.1 room 41.
+		//   Walkbox coordinates TBD from binary analysis.
+
+		// Mark pyramid quest complete
+		_state->setFlag(FLAG_PIEDRAS_COGIDAS, true);
+	}
+}
+
 /**
  * Naked girls swim underwater, guard enters the scene.
  * "diving" animation is stored in Alfred.7 as a single RLE chunk with multiple continuous sprite sizes.
@@ -1425,7 +1504,6 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 	guard->animData[0].nframes = 1;
 	_alfredState.direction = ALFRED_RIGHT;
 	walkAndAction(_room->findHotspotByExtra(guard->extra), TALK);
-	waitForActionEnd();
 	if(shouldQuit()) {
 		return;
 	}
@@ -1434,7 +1512,6 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 	_alfredState.y = 385;
 	setScreen(40, ALFRED_UP);
 	walkAndAction(_room->findHotspotByExtra(640), TALK);
-	waitForActionEnd();
 	if(shouldQuit()) {
 		return;
 	}
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 38d35f7b930..fb8a69fa81b 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1574,6 +1574,7 @@ void PelrockEngine::walkAndAction(HotSpot *hotspot, VerbIcon action) {
 	_disableAction = true;
 	walkTo(hotspot->x + hotspot->w / 2, hotspot->y + hotspot->h);
 	_queuedAction = QueuedAction{action, hotspot->index, true};
+	waitForActionEnd();
 }
 
 AlfredDirection PelrockEngine::calculateAlfredsDirection(HotSpot *hotspot) {
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 711e6bbafe9..6ee7d0cbe31 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -346,6 +346,7 @@ public:
 	void giveMagazineToGuard(int inventoryObject, HotSpot *hotspot);
 	void giveWaterToGuard(int inventoryObject, HotSpot *hotspot);
 	void pickUpStone(HotSpot *hotspot);
+	void giveStoneToSlaves(int inventoryObject, HotSpot *hotspot);
 	void swimmingPoolCutscene(HotSpot *hotspot);
 	void magicFormula(int inventoryObject, HotSpot *hotspot);
 	void checkAllSymbols();
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 0e99b0359fa..ab39d0b955f 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -248,6 +248,10 @@ void ResourceManager::loadOtherSpecialAnim(uint32 offset, bool rleCompressed, by
 		readUntilBuda(&alfred7, offset, compressed, compressedSize);
 		bufferSize = rleDecompress(compressed, compressedSize, 0, 0, &buffer, true);
 	}
+	else {
+		alfred7.seek(offset, SEEK_SET);
+		alfred7.read(buffer, bufferSize);
+	}
 	alfred7.close();
 }
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index b098259c4b9..6f0e99fe3ae 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -475,6 +475,7 @@ struct ResetEntry {
 #define FLAG_SE_HA_PUESTO_EL_MUNECO 20
 #define FLAG_VIGILANTE_BEBE_AGUA 21
 #define FLAG_FORMULA_MAGICA 26
+#define FLAG_DA_PIEDRA 31
 
 
 
@@ -489,7 +490,6 @@ struct ResetEntry {
 #define FLAG_APARECE_EUNUCO 28
 #define FLAG_AL_FARAON 29
 #define FLAG_A_CURRAR 30
-#define FLAG_DA_PIEDRA 31
 #define FLAG_PIEDRAS_COGIDAS 32
 #define FLAG_GUARDIAS_BORRACHOS 33
 #define FLAG_PIEDRA_FAKE_MOJADA 34


Commit: 82bcbdcdacea9d7b6da0923369d611825a390046
    https://github.com/scummvm/scummvm/commit/82bcbdcdacea9d7b6da0923369d611825a390046
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:09+02:00

Commit Message:
PELROCK: Implements cutscenes in room 41

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/sound.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 205408bfb29..d44e2e3870a 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -37,6 +37,8 @@ const ActionEntry actionTable[] = {
 	// Room 0
 	{261, OPEN, &PelrockEngine::openRoomDrawer},
 	{261, CLOSE, &PelrockEngine::closeRoomDrawer},
+	{263, OPEN, &PelrockEngine::openClosedDrawer},
+
 	{268, OPEN, &PelrockEngine::openRoomDoor},
 	{268, CLOSE, &PelrockEngine::closeRoomDoor},
 	{3, PICKUP, &PelrockEngine::pickUpPhoto},
@@ -666,6 +668,10 @@ void PelrockEngine::closeRoomDrawer(HotSpot *hotspot) {
 	_room->enableHotspot(hotspot);
 }
 
+void PelrockEngine::openClosedDrawer(HotSpot *hotspot) {
+	_dialog->say(_res->_ingameTexts[ESTAN_CERRADOS]);
+}
+
 void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
 	debug("Withdrawing money from ATM using card (inv obj %d)", inventoryObject);
 	if (_state->getFlag(FLAG_JEFE_INGRESA_PASTA)) {
@@ -1080,13 +1086,7 @@ void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
 	_alfredState.y += 20;
 	waitForSpecialAnimation();
 	_sound->playSound(_room->_roomSfx[0], 0); // Belch
-	bool isPlaying = true;
-	while (!shouldQuit() && isPlaying) {
-		_events->pollEvent();
-		isPlaying = _sound->isPlaying(0);
-		_screen->update();
-		g_system->delayMillis(10);
-	}
+	waitForSoundEnd();
 	_graphics->fadeToBlack(10);
 	// update conversaton state
 	_alfredState.x = 300;
@@ -1095,6 +1095,15 @@ void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
 	_dialog->say(_res->_ingameTexts[QUEOSCUROESTAESTO]);
 }
 
+void PelrockEngine::waitForSoundEnd() {
+	while (!shouldQuit() && _sound->isPlaying(0)) {
+		_events->pollEvent();
+		renderScene(OVERLAY_NONE);
+		_screen->update();
+		g_system->delayMillis(10);
+	}
+}
+
 void PelrockEngine::pickupSunflower(HotSpot *hotspot) {
 	if (_state->getFlag(FLAG_PARADOJA_RESUELTA) == false) {
 		_dialog->say(_res->_ingameTexts[OIGA]);
@@ -1351,47 +1360,68 @@ void PelrockEngine::pickUpStone(HotSpot *hotspot) {
 	checkIngredients();
 }
 
-void PelrockEngine::giveStoneToSlaves(int inventoryObject, HotSpot *hotspot) {
-	// Remove whichever stone item was used (91 = Egyptian stone, 92 = mud/clay)
-	_state->removeInventoryItem(inventoryObject);
-
-	// Play 7-frame 208×102 stone-passing animation
-	size_t frameSize = 208 * 102;
-	size_t bufSize = frameSize * 7;
+void PelrockEngine::playSpecialAnim(uint32 offset, bool compressed, int x, int y, int width, int height, int numFrames) {
+	size_t frameSize = width * height;
+	debug("Frame size: %d bytes", frameSize);
+	size_t bufSize = frameSize * numFrames;
 	byte *animData = new byte[bufSize];
-	_res->loadOtherSpecialAnim(1600956, false, animData, bufSize);
+	_res->loadOtherSpecialAnim(offset, compressed, animData, bufSize);
 
 	Graphics::Surface animSurface;
-	animSurface.create(208, 102, Graphics::PixelFormat::createFormatCLUT8());
+	animSurface.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
 	int curFrame = 0;
+	bool firstFrame = true;
 	while (!shouldQuit()) {
 		_events->pollEvent();
 
 		bool didRender = renderScene(OVERLAY_NONE);
-
-		memset(animSurface.getPixels(), 0, frameSize);
-		extractSingleFrame(animData, (byte *)animSurface.getPixels(), curFrame, 208, 102);
-		_screen->transBlitFrom(animSurface, Common::Point(0, 298), 255);
-		if (didRender && _chrono->getFrameCount() % 2 == 0) {
-			curFrame++;
-
-			if (curFrame >= 7) {
-				break;
+		if(didRender) {
+			memset(animSurface.getPixels(), 0, frameSize);
+			extractSingleFrame(animData, (byte *)animSurface.getPixels(), curFrame, width, height);
+			_screen->transBlitFrom(animSurface, Common::Point(x, y), 255);
+			if (_chrono->getFrameCount() % 2 == 0) {
+				curFrame++;
+				if (curFrame >= numFrames) {
+					_screen->markAllDirty();
+					_screen->update();
+					break;
+				}
 			}
 		}
+		_screen->markAllDirty();
 		_screen->update();
+		// _events->waitForKey();
 		g_system->delayMillis(10);
 	}
 	animSurface.free();
 	delete[] animData;
 
-	//play some sound
+}
+
+void PelrockEngine::giveStoneToSlaves(int inventoryObject, HotSpot *hotspot) {
+	// Remove whichever stone item was used (91 = Egyptian stone, 92 = mud/clay)
+	_state->removeInventoryItem(inventoryObject);
+
+	Sprite *masters = _room->findSpriteByExtra(600);
+	byte zIndex = masters->zOrder;
+	// Capture coordinates now, before any playSpecialAnim loop runs renderScene and
+	// sortAnimsByZOrder reorders _currentRoomAnims (which would make the pointer stale).
+	int16 mastersX = masters->x;
+	int16 mastersY = masters->y;
+
+	// Slaves take stone then chant
+	playSpecialAnim(1600956, false, 0, 298, 208, 102, 7);
+	_sound->playSound(_room->_roomSfx[0], 0);
+	// waitForSoundEnd();
 
 	_dialog->say(_res->_ingameTexts[HAYQUECELEBRARLO]);
 
-	// TODO: wait for slave sprite frame 6 animation (sprite index derived from
-	// play drinking animation
-	//   room_sprite_data_ptr + sprite_id * 0x2C, then poll +0x20 == 6)
+
+	//drinking animation and sound
+	_sound->playSound(_room->_roomSfx[1], 0);
+
+	_room->disableSprite(0);
+	playSpecialAnim(1473360, true, mastersX - 5, mastersY - 1, 152, 83, 7);
 
 	// Increment stone delivery counter (tracks 0→1→2→3)
 	byte counter = _state->getFlag(FLAG_DA_PIEDRA);
@@ -1400,25 +1430,26 @@ void PelrockEngine::giveStoneToSlaves(int inventoryObject, HotSpot *hotspot) {
 		_state->setFlag(FLAG_DA_PIEDRA, ++counter);
 	}
 
-	// At 2nd stone delivery: slave starts singing (conversation root 2)
-	// Root 2 text: "¡Deesde Santuurce a Bilbaooo...!"
+	// At 2nd stone delivery: masters starts singing (conversation root 2)
 	if (counter == 2) {
 		_state->setCurrentRoot(41, 2, 0);
 	}
 
-	// At 3rd stone delivery: pyramid is complete
+	// At 3rd stone delivery: masters get wasted
 	if (counter == 3) {
-		_room->disableSprite(0);
-		// Render permanent pyramid sticker (ALFRED.6 offset 0x0696AD = sticker index 116)
-		_room->addSticker(116, PERSIST_PERM);
+		playSpecialAnim(1512060, true, mastersX - 28, mastersY - 6, 172, 96, 3);
 
-		// TODO: add 5 walkboxes for completed pyramid layout (write_data_to_alfred1 ×4)
-		//   Original game writes walkbox count→5 and walkbox data to ALFRED.1 room 41.
-		//   Walkbox coordinates TBD from binary analysis.
+		_room->addSticker(116);
 
-		// Mark pyramid quest complete
+		WalkBox w1 = {3, 187, 374, 5, 17};
+		WalkBox w2 = {4, 141, 374, 46, 4};
+		_room->addWalkbox(w1);
+		_room->addWalkbox(w2);
 		_state->setFlag(FLAG_PIEDRAS_COGIDAS, true);
 	}
+	else {
+		_room->enableSprite(0, zIndex);
+	}
 }
 
 /**
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index fb8a69fa81b..633d3d5878a 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -157,7 +157,7 @@ void PelrockEngine::init() {
 		// setScreen(0, ALFRED_DOWN);
 		// setScreen(3, ALFRED_RIGHT);
 		// setScreen(22, ALFRED_DOWN);
-		setScreen(39, ALFRED_DOWN);
+		setScreen(41, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
@@ -302,7 +302,14 @@ bool PelrockEngine::renderScene(int overlayMode) {
 
 		updatePaletteAnimations();
 
-		_screen->markAllDirty();
+		// Execute deferred actions AFTER renderScene, so any scene changes
+		// (addSticker, disableSprite, etc.) are in place before the next frame's
+		// placeStickersFirstPass + presentFrame.
+		if (_queuedAction.readyToExecute) {
+			_queuedAction.readyToExecute = false;
+			doAction(_queuedAction.verb, &_room->_currentRoomHotspots[_queuedAction.hotspotIndex]);
+		}
+
 		return true;
 	}
 
@@ -521,7 +528,7 @@ void PelrockEngine::checkMouse() {
 			useOnAlfred(_state->selectedInventoryItem);
 		} else {
 			// Released outside popup - just close it
-			_queuedAction = QueuedAction{NO_ACTION, -1, false};
+			_queuedAction = QueuedAction{NO_ACTION, -1, false, false};
 			_currentHotspot = nullptr;
 		}
 	} else if (_events->_leftMouseClicked) {
@@ -559,6 +566,7 @@ void PelrockEngine::updateAnimations() {
 	// First pass: sprites behind Alfred (sprite zOrder > alfredZOrder)
 	for (uint i = 0; i < _room->_currentRoomAnims.size(); i++) {
 		if (_room->_currentRoomAnims[i].zOrder > alfredZOrder || _room->_currentRoomAnims[i].zOrder < 0) {
+			// debug("Drawing anim %d with zOrder %d in first pass (behind Alfred)", i, _room->_currentRoomAnims[i].zOrder);
 			drawNextFrame(&_room->_currentRoomAnims[i]);
 		}
 	}
@@ -569,6 +577,7 @@ void PelrockEngine::updateAnimations() {
 	// Second pass: sprites in front of Alfred (sprite zOrder <= alfredZOrder)
 	for (uint i = 0; i < _room->_currentRoomAnims.size(); i++) {
 		if (_room->_currentRoomAnims[i].zOrder <= alfredZOrder && _room->_currentRoomAnims[i].zOrder >= 0) {
+			// debug("Drawing anim %d with zOrder %d in second pass (in front of Alfred)", i, _room->_currentRoomAnims[i].zOrder);
 			drawNextFrame(&_room->_currentRoomAnims[i]);
 		}
 	}
@@ -602,10 +611,10 @@ void PelrockEngine::paintDebugLayer() {
 	bool showWalkboxes = true;
 
 	if (showWalkboxes) {
+		debug("Drawing walkboxes, count: %d", _room->_currentRoomWalkboxes.size());
 		for (uint i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
 			WalkBox box = _room->_currentRoomWalkboxes[i];
-			drawRect(_screen, box.x, box.y, box.w, box.h, 150 + i);
-			// _smallFont->drawString(_screen, Common::String::format("%d", i), box.x + 2, box.y + 2, 640, 14);
+			drawRect(_screen, box.x, box.y, box.w, box.h, 13);
 		}
 	}
 
@@ -613,6 +622,10 @@ void PelrockEngine::paintDebugLayer() {
 	if (showSprites) {
 		for (uint i = 0; i < _room->_currentRoomAnims.size(); i++) {
 			Sprite sprite = _room->_currentRoomAnims[i];
+			if(sprite.zOrder < 0) {
+				// Skip sprites with negative zOrder (not rendered)
+				continue;
+			}
 			drawRect(_screen, sprite.x, sprite.y, sprite.w, sprite.h, 14);
 			_smallFont->drawString(_screen, Common::String::format("S %d", sprite.index), sprite.x + 2, sprite.y, 640, 14);
 		}
@@ -880,9 +893,10 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 				if (_queuedAction.isQueued) {
 					// look and talk execute immediately, others need interaction animation first
 					if (_queuedAction.verb == TALK || _queuedAction.verb == LOOK) {
+						// Defer to after renderScene so any scene changes (stickers,
+						// sprites) take effect before the next presentFrame.
 						_queuedAction.isQueued = false;
-						HotSpot *actionHotspot = &_room->_currentRoomHotspots[_queuedAction.hotspotIndex];
-						doAction(_queuedAction.verb, actionHotspot);
+						_queuedAction.readyToExecute = true;
 						break;
 					}
 					_alfredState.setState(ALFRED_INTERACTING);
@@ -941,9 +955,11 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		_alfredState.curFrame++;
 		if (_alfredState.curFrame >= interactingAnimLength) {
 			if (_queuedAction.isQueued) {
+				// Defer to after renderScene so any scene changes (stickers,
+				// sprites) take effect before the next presentFrame.
 				_queuedAction.isQueued = false;
+				_queuedAction.readyToExecute = true;
 				_alfredState.setState(ALFRED_IDLE);
-				doAction(_queuedAction.verb, &_room->_currentRoomHotspots[_queuedAction.hotspotIndex]);
 				break;
 			}
 		}
@@ -1495,28 +1511,30 @@ void PelrockEngine::gameLoop() {
 
 	_events->pollEvent();
 	checkMouse();
-	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_m) {
-		travelToEgypt();
-		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
-	}
-	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_n) {
-		loadExtraScreenAndPresent(10);
-		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
-	}
-	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_p) {
-		antiPiracyEffect();
-		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
-	}
-	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_s) {
-		SpellBook spellBook(_events, _res);
-		Spell *selectedSpell = spellBook.run();
-		if (selectedSpell != nullptr) {
-			_dialog->sayAlfred(selectedSpell->text);
-		}
-		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
-	}
+
+	// if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_m) {
+	// 	travelToEgypt();
+	// 	_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
+	// }
+	// if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_n) {
+	// 	loadExtraScreenAndPresent(10);
+	// 	_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
+	// }
+	// if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_p) {
+	// 	antiPiracyEffect();
+	// 	_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
+	// }
+	// if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_s) {
+	// 	SpellBook spellBook(_events, _res);
+	// 	Spell *selectedSpell = spellBook.run();
+	// 	if (selectedSpell != nullptr) {
+	// 		_dialog->sayAlfred(selectedSpell->text);
+	// 	}
+	// 	_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
+	// }
+
 	renderScene();
-	// _events->waitForKey();
+
 	_screen->update();
 }
 
@@ -1573,7 +1591,7 @@ void PelrockEngine::walkTo(int x, int y) {
 void PelrockEngine::walkAndAction(HotSpot *hotspot, VerbIcon action) {
 	_disableAction = true;
 	walkTo(hotspot->x + hotspot->w / 2, hotspot->y + hotspot->h);
-	_queuedAction = QueuedAction{action, hotspot->index, true};
+	_queuedAction = QueuedAction{action, hotspot->index, true, false};
 	waitForActionEnd();
 }
 
@@ -1658,7 +1676,7 @@ bool PelrockEngine::isAlfredUnder(int x, int y) {
 void PelrockEngine::checkMouseClick(int x, int y) {
 
 	// This handles regular clicks (not popup selection)
-	_queuedAction = QueuedAction{NO_ACTION, -1, false};
+	_queuedAction = QueuedAction{NO_ACTION, -1, false, false};
 	_actionPopupState.isActive = false;
 	_currentHotspot = nullptr;
 	_alfredState.idleFrameCounter = 0;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 6ee7d0cbe31..508dca0945d 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -138,7 +138,7 @@ private:
 	int _newItem = -1;
 
 	Common::Point _curWalkTarget;
-	QueuedAction _queuedAction;
+	QueuedAction _queuedAction = {NO_ACTION, -1, false, false};
 
 	bool showShadows = false;
 
@@ -257,6 +257,7 @@ public:
 	void executeAction(VerbIcon action, HotSpot *hotspot);
 	void openRoomDrawer(HotSpot *hotspot);
 	void closeRoomDrawer(HotSpot *hotspot);
+	void openClosedDrawer(HotSpot *hotspot);
 	void openRoomDoor(HotSpot *hotspot);
 	void closeRoomDoor(HotSpot *hotspot);
 	void pickUpAndDisable(HotSpot *hotspot);
@@ -322,6 +323,7 @@ public:
 	void openTravelAgencyDoor(HotSpot *hotspot);
 	void closeTravelAgencyDoor(HotSpot *hotspot);
 	void usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot);
+	void waitForSoundEnd();
 	void pickupSunflower(HotSpot *hotspot);
 	void checkIngredients();
 	void pickUpBook(int i);
@@ -346,6 +348,7 @@ public:
 	void giveMagazineToGuard(int inventoryObject, HotSpot *hotspot);
 	void giveWaterToGuard(int inventoryObject, HotSpot *hotspot);
 	void pickUpStone(HotSpot *hotspot);
+	void playSpecialAnim(uint32 offset, bool compressed, int x, int y, int width, int height, int numFrames);
 	void giveStoneToSlaves(int inventoryObject, HotSpot *hotspot);
 	void swimmingPoolCutscene(HotSpot *hotspot);
 	void magicFormula(int inventoryObject, HotSpot *hotspot);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 72822cd3a02..4f00a6d10d9 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -228,8 +228,14 @@ void RoomManager::disableSprite(byte roomNumber, byte spriteIndex, int persist)
 	debug("Disabling sprite %d in room %d with persist %d", spriteIndex, roomNumber, persist);
 	if (roomNumber == _currentRoomNumber && persist & PERSIST_TEMP) {
 		debug("Disabling sprite LOCALLY %d in room %d with persist %d", spriteIndex, roomNumber, persist);
-		_currentRoomAnims[spriteIndex].zOrder = -1;
-		_currentRoomAnims[spriteIndex].isHotspotDisabled = true;
+		// Search by sprite.index, not by array position: sortAnimsByZOrder reorders the
+		// array every frame so a raw array index is unreliable.
+		for (uint i = 0; i < _currentRoomAnims.size(); i++) {
+			if (_currentRoomAnims[i].index == spriteIndex) {
+				_currentRoomAnims[i].zOrder = -1;
+				break;
+			}
+		}
 	}
 	if (persist & PERSIST_PERM) {
 		g_engine->_state->spriteChanges[roomNumber].push_back({roomNumber, spriteIndex, 255});
@@ -242,7 +248,13 @@ void RoomManager::enableSprite(byte spriteIndex, byte zOrder, int persist) {
 
 void RoomManager::enableSprite(byte roomNumber, byte spriteIndex, byte zOrder, int persist) {
 	if (roomNumber == _currentRoomNumber && persist & PERSIST_TEMP) {
-		_currentRoomAnims[spriteIndex].zOrder = zOrder;
+		// Search by sprite.index field, not by array position (array is re-sorted every frame).
+		for (uint i = 0; i < _currentRoomAnims.size(); i++) {
+			if (_currentRoomAnims[i].index == spriteIndex) {
+				_currentRoomAnims[i].zOrder = zOrder;
+				break;
+			}
+		}
 	}
 	if (persist & PERSIST_PERM) {
 		g_engine->_state->spriteChanges[roomNumber].push_back({roomNumber, spriteIndex, zOrder});
@@ -312,6 +324,15 @@ Sprite *RoomManager::findSpriteByIndex(byte index) {
 	return nullptr;
 }
 
+Sprite *RoomManager::findSpriteByExtra(int16 extra) {
+	for (uint i = 0; i < _currentRoomAnims.size(); i++) {
+		if (_currentRoomAnims[i].extra == extra) {
+			return &_currentRoomAnims[i];
+		}
+	}
+	return nullptr;
+}
+
 HotSpot *RoomManager::findHotspotByIndex(byte index) {
 	for (uint i = 0; i < _currentRoomHotspots.size(); i++) {
 		if (!_currentRoomHotspots[i].isSprite && _currentRoomHotspots[i].innerIndex == index) {
@@ -923,6 +944,9 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 			anim.curFrame = 0;
 
 			anim.nframes = data[subAnimOffset + j];
+			if (_currentRoomNumber == 41 && i == 1) {
+				anim.nframes = 3;
+			}
 			anim.loopCount = data[subAnimOffset + 4 + j];
 			anim.speed = data[subAnimOffset + 8 + j];
 			anim.movementFlags = data[subAnimOffset + 14 + (j * 2)] | (data[subAnimOffset + 14 + (j * 2) + 1] << 8);
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 7197ece76d7..f807b91a233 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -123,6 +123,7 @@ public:
 		return true;
 	}
 	Sprite *findSpriteByIndex(byte index);
+	Sprite *findSpriteByExtra(int16 extra);
 	HotSpot *findHotspotByIndex(byte index);
 	HotSpot *findHotspotByExtra(uint16 extra);
 	PaletteAnim *getPaletteAnimForRoom(int roomNumber);
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 6eee91a28e4..cee1f3675ca 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -53,7 +53,7 @@ void SoundManager::playSound(byte index, int channel) {
 	// debug("Playing sound index %d (%s)", index, SOUND_FILENAMES[index]);
 	auto it = _soundMap.find(SOUND_FILENAMES[index]);
 	if (it != _soundMap.end()) {
-		playSound(it->_value);
+		playSound(it->_value, channel);
 	} else {
 		debug("Sound file %s not found in sound map", SOUND_FILENAMES[index]);
 	}
@@ -62,7 +62,7 @@ void SoundManager::playSound(byte index, int channel) {
 void SoundManager::playSound(const char *filename, int channel) {
 	auto it = _soundMap.find(filename);
 	if (it != _soundMap.end()) {
-		playSound(it->_value);
+		playSound(it->_value, channel);
 	} else {
 		debug("Sound file %s not found in sound map", filename);
 	}
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 6f0e99fe3ae..0a790393cf0 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -303,7 +303,8 @@ struct WalkBox {
 struct QueuedAction {
 	VerbIcon verb;
 	int hotspotIndex;
-	bool isQueued;
+	bool isQueued;        // Alfred is walking/interacting toward the target
+	bool readyToExecute;  // Animation done - execute after the current renderScene
 };
 
 struct ScalingParams {
@@ -332,7 +333,6 @@ struct SpriteChange
 	byte roomNumber;
 	byte spriteIndex;
 	byte zIndex;
-
 };
 
 


Commit: 831355caa204e883c3c82723b494f63d6295a89b
    https://github.com/scummvm/scummvm/commit/831355caa204e883c3c82723b494f63d6295a89b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:09+02:00

Commit Message:
PELROCK: Implements rooms 43, 44, 45

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/offsets.h
    engines/pelrock/pathfinding.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index d44e2e3870a..a1d10a52ba8 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -153,6 +153,16 @@ const ActionEntry actionTable[] = {
 
 	// Room 39
 	{700, PICKUP, &PelrockEngine::swimmingPoolCutscene},
+
+	// Room 41
+	{605, PICKUP, &PelrockEngine::pickUpStones},
+	{606, PICKUP, &PelrockEngine::pickUpStones},
+	{607, PICKUP, &PelrockEngine::pickUpStones},
+	{608, PICKUP, &PelrockEngine::pickUpMud},
+
+	// Room 44
+	{613, OPEN, &PelrockEngine::openPyramidDoor},
+
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
 	{WILDCARD, TALK, &PelrockEngine::noOpAction},   // Generic talk action
@@ -191,6 +201,10 @@ const CombinationEntry combinationTable[] = {
 	{85, 506, &PelrockEngine::magicFormula},
 	{86, 506, &PelrockEngine::magicFormula},
 	{81, 506, &PelrockEngine::magicFormula},
+
+	{76, 617, &PelrockEngine::usePumpkinWithPond},
+
+	{86, 614, &PelrockEngine::useWaterOnFakeStone},
 	// End marker
 	{WILDCARD, WILDCARD, nullptr}};
 
@@ -327,10 +341,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 279:
 		travelToEgypt();
 		break;
-	// moros
-	case 317:
-		addInventoryItem(95);
-		break;
+		// moros
 
 	case 330:
 		// Two oranges
@@ -580,6 +591,49 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_state->setCurrentRoot(room, targetBranch, 0);
 		break;
 	}
+		/* pyramid merchant*/
+	case 314:
+		addInventoryItem(93);
+		break;
+	case 316:
+		addInventoryItem(94);
+		break;
+	case 317:
+		// CD
+		addInventoryItem(95);
+		break;
+	case 318:
+		// bg book
+		addInventoryItem(96);
+		break;
+	case 319:
+		// add pyramid map
+		addInventoryItem(97);
+		_state->setCurrentRoot(room, 2, 0);
+		break;
+	case 320:
+		_state->setCurrentRoot(room, 2, 0);
+		break;
+	case 324:
+		// ?
+		break;
+	// girls in pond
+	case 321:
+		_state->setCurrentRoot(45, 1, 0);
+		_sound->playSound("TWANGZZZ.SMP", 0);
+		break;
+	case 376: {
+		_res->loadAlfredSpecialAnim(14);
+		_alfredState.animState = ALFRED_SPECIAL_ANIM;
+		waitForSpecialAnimation();
+		loadExtraScreenAndPresent(12);
+		_state->setCurrentRoot(45, 2, 0);
+	} break;
+	case 377:
+		_state->setCurrentRoot(45, 3, 0);
+		break;
+	case 30840:
+		break;
 	default:
 		debug("Got actionTrigger %d in dialogActionTrigger, but no handler defined", actionTrigger);
 		break;
@@ -1375,7 +1429,7 @@ void PelrockEngine::playSpecialAnim(uint32 offset, bool compressed, int x, int y
 		_events->pollEvent();
 
 		bool didRender = renderScene(OVERLAY_NONE);
-		if(didRender) {
+		if (didRender) {
 			memset(animSurface.getPixels(), 0, frameSize);
 			extractSingleFrame(animData, (byte *)animSurface.getPixels(), curFrame, width, height);
 			_screen->transBlitFrom(animSurface, Common::Point(x, y), 255);
@@ -1395,7 +1449,6 @@ void PelrockEngine::playSpecialAnim(uint32 offset, bool compressed, int x, int y
 	}
 	animSurface.free();
 	delete[] animData;
-
 }
 
 void PelrockEngine::giveStoneToSlaves(int inventoryObject, HotSpot *hotspot) {
@@ -1412,12 +1465,11 @@ void PelrockEngine::giveStoneToSlaves(int inventoryObject, HotSpot *hotspot) {
 	// Slaves take stone then chant
 	playSpecialAnim(1600956, false, 0, 298, 208, 102, 7);
 	_sound->playSound(_room->_roomSfx[0], 0);
-	// waitForSoundEnd();
+	waitForSoundEnd();
 
 	_dialog->say(_res->_ingameTexts[HAYQUECELEBRARLO]);
 
-
-	//drinking animation and sound
+	// drinking animation and sound
 	_sound->playSound(_room->_roomSfx[1], 0);
 
 	_room->disableSprite(0);
@@ -1429,7 +1481,7 @@ void PelrockEngine::giveStoneToSlaves(int inventoryObject, HotSpot *hotspot) {
 	if (counter < 3) {
 		_state->setFlag(FLAG_DA_PIEDRA, ++counter);
 	}
-
+	debug("New stone delivery count: %d", _state->getFlag(FLAG_DA_PIEDRA));
 	// At 2nd stone delivery: masters starts singing (conversation root 2)
 	if (counter == 2) {
 		_state->setCurrentRoot(41, 2, 0);
@@ -1445,9 +1497,9 @@ void PelrockEngine::giveStoneToSlaves(int inventoryObject, HotSpot *hotspot) {
 		WalkBox w2 = {4, 141, 374, 46, 4};
 		_room->addWalkbox(w1);
 		_room->addWalkbox(w2);
-		_state->setFlag(FLAG_PIEDRAS_COGIDAS, true);
-	}
-	else {
+		_state->setFlag(FLAG_GUARDIAS_BORRACHOS, true);
+	} else {
+		debug("Re-enabling master sprite with zIndex %d", zIndex);
 		_room->enableSprite(0, zIndex);
 	}
 }
@@ -1535,7 +1587,7 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 	guard->animData[0].nframes = 1;
 	_alfredState.direction = ALFRED_RIGHT;
 	walkAndAction(_room->findHotspotByExtra(guard->extra), TALK);
-	if(shouldQuit()) {
+	if (shouldQuit()) {
 		return;
 	}
 	_graphics->fadeToBlack(10);
@@ -1543,7 +1595,7 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 	_alfredState.y = 385;
 	setScreen(40, ALFRED_UP);
 	walkAndAction(_room->findHotspotByExtra(640), TALK);
-	if(shouldQuit()) {
+	if (shouldQuit()) {
 		return;
 	}
 	_graphics->fadeToBlack(10);
@@ -1552,6 +1604,67 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 	setScreen(41, ALFRED_UP);
 }
 
+void PelrockEngine::pickUpStones(HotSpot *hotspot) {
+	if (_state->hasInventoryItem(91)) {
+		_dialog->say(_res->_ingameTexts[PESADEMASIADO]);
+		return;
+	}
+	if (_state->getFlag(FLAG_PIEDRAS_COGIDAS) >= 2) {
+		_dialog->say(_res->_ingameTexts[NINGUNATAMANHOAPROPIADO]);
+		return;
+	} else {
+		addInventoryItem(91);
+		_state->setFlag(FLAG_PIEDRAS_COGIDAS, _state->getFlag(FLAG_PIEDRAS_COGIDAS) + 1);
+		debug("Piedras cogidas: %d", _state->getFlag(FLAG_PIEDRAS_COGIDAS));
+	}
+}
+
+void PelrockEngine::pickUpMud(HotSpot *hotspot) {
+	if (_state->getFlag(FLAG_PIEDRAS_COGIDAS) != 2) {
+		_dialog->say(_res->_ingameTexts[PARAQUECOGERBARRO]);
+		return;
+	} else {
+		addInventoryItem(92);
+		_state->setFlag(FLAG_PIEDRAS_COGIDAS, _state->getFlag(FLAG_PIEDRAS_COGIDAS) + 1);
+		_dialog->say(_res->_ingameTexts[BUENOCOGEREUNPOCO]);
+	}
+}
+
+void PelrockEngine::openPyramidDoor(HotSpot *hotspot) {
+	_dialog->say(_res->_ingameTexts[ABSOLUTAMENTECERRADO]);
+}
+
+void PelrockEngine::usePumpkinWithPond(int inventoryObject, HotSpot *hotspot) {
+	_state->removeInventoryItem(76);
+	addInventoryItem(86);
+}
+
+void PelrockEngine::useWaterOnFakeStone(int inventoryObject, HotSpot *hotspot) {
+
+	int count = _state->getFlag(FLAG_PIEDRA_FAKE_MOJADA);
+	if(count != 3) {
+		_state->removeInventoryItem(86);
+		addInventoryItem(76);
+		switch (count)
+		{
+		case 0:
+			_room->addSticker(120);
+			break;
+		case 1:
+			_room->addSticker(121);
+			break;
+		case 2:
+			_room->addSticker(122);
+			_room->enableExit(1);
+			break;
+		default:
+			break;
+		}
+		count++;
+		_state->setFlag(FLAG_PIEDRA_FAKE_MOJADA, count);
+	}
+}
+
 void PelrockEngine::magicFormula(int inventoryObject, HotSpot *hotspot) {
 	_state->removeInventoryItem(inventoryObject);
 	_state->setFlag(FLAG_FORMULA_MAGICA, _state->getFlag(FLAG_FORMULA_MAGICA) + 1);
@@ -1768,6 +1881,15 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		loadExtraScreenAndPresent(7);
 		break;
 	}
+	case 97: {
+		_res->loadAlfredSpecialAnim(1);
+		_alfredState.animState = ALFRED_SPECIAL_ANIM;
+		waitForSpecialAnimation();
+		loadExtraScreenAndPresent(11);
+		_dialog->say(_res->_ingameTexts[MEHANTOMADO_EL_PELO]);
+		_state->setCurrentRoot(43, 1, 0);
+		break;
+	}
 	default:
 		break;
 	}
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 0c35e16c060..60b7a9a83cf 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -210,7 +210,7 @@ enum TextIndices {
 	CAPITULOPARADOJAS,
 	HAYQUECELEBRARLO,
 	PESADEMASIADO,
-	NINGUNATEMAAPROPIADO,
+	NINGUNATAMANHOAPROPIADO,
 	PARAQUECOGERBARRO,
 	BUENOCOGEREUNPOCO,
 	ABSOLUTAMENTECERRADO,
@@ -445,39 +445,49 @@ struct ExtraImages {
 };
 
 const ExtraImages extraScreens[] = {
-	{0x00, // Portrait above bed
+	{0x00, // 0 - Portrait above bed
 	 0x7984,
 	 8},
-	{0x1A9EE, // Computer screen
+	{0x1A9EE, // 1 - Computer screen
 	 0x305A2,
 	 8},
-	{0x647C3, // Alfred circle
+	{0x647C3, // 2 - Alfred circle
 	 0x7B6B1,
 	 4},
-	{0x6FBCD, // Recipe
+	{0x6FBCD, // 3 - Recipe
 	 0x7B6B1,
 	 8},
-	{0x7BA11, // Newspaper
+	{0x7BA11, // 4 - Newspaper
 	 0x88745,
 	 8},
-	{0x9237B, // tablet
+	{0x9237B, // 5 - tablet
 	 0xB0EE7,
 	 8},
-	{0x000B11F1, // map
+	{0x000B11F1, // 6 - map
 	 0xDE011,
 	 8},
-	{0xFFC47, // girl book
+	{0xFFC47, // 7 - girl book
 	 0x1180C9,
 	 8},
-	{1147849,
+	{1147849, // 8 - unknown
 	 1267955,
 	 8},
-	{0x152A88, // portrait
+	{0x152A88, // 9 - portrait
 	 0x15BFC8,
 	 8},
-	{2727564, // CD
+	{2727564, // 10 - CD
 	 2833276,
-	 8}};
+	 8},
+	{
+	2834044, // 11 - pyramid map
+	2971800,
+	8},
+	{
+	2306538, // 12 - CENSORED
+	2321064,
+	8
+	},
+};
 
 struct AlfredSpecialAnimOffset {
 	int numFrames = 0;
diff --git a/engines/pelrock/pathfinding.cpp b/engines/pelrock/pathfinding.cpp
index d141bb3af21..e7a89c34818 100644
--- a/engines/pelrock/pathfinding.cpp
+++ b/engines/pelrock/pathfinding.cpp
@@ -103,7 +103,7 @@ bool findPath(int sourceX, int sourceY, int targetX, int targetY, Common::Array<
 		context->pathLength = buildWalkboxPath(walkboxes, startBox, destBox, context->pathBuffer);
 		debug("Walkbox path to point");
 		for (int i = 0; i < context->pathLength; i++) {
-			debug("Walkbox %d: %d", i, context->pathBuffer[i]);
+			// debug("Walkbox %d: %d", i, context->pathBuffer[i]);
 		}
 		if (context->pathLength == 0) {
 			debug("Error: No path found\n");
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 633d3d5878a..a2424d910fa 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -157,7 +157,8 @@ void PelrockEngine::init() {
 		// setScreen(0, ALFRED_DOWN);
 		// setScreen(3, ALFRED_RIGHT);
 		// setScreen(22, ALFRED_DOWN);
-		setScreen(41, ALFRED_DOWN);
+		// setScreen(41, ALFRED_DOWN);
+		setScreen(43, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
@@ -611,7 +612,6 @@ void PelrockEngine::paintDebugLayer() {
 	bool showWalkboxes = true;
 
 	if (showWalkboxes) {
-		debug("Drawing walkboxes, count: %d", _room->_currentRoomWalkboxes.size());
 		for (uint i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
 			WalkBox box = _room->_currentRoomWalkboxes[i];
 			drawRect(_screen, box.x, box.y, box.w, box.h, 13);
@@ -1198,6 +1198,10 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 
 	int frameSize = sprite->stride;
 	int curFrame = animData.curFrame;
+	if(curFrame >= animData.nframes) {
+		debug("Warning: curFrame %d exceeds nframes %d for sprite %d anim %d", curFrame, animData.nframes, sprite->index, sprite->curAnimIndex);
+		curFrame = 0;
+	}
 	drawSpriteToBuffer(_compositeBuffer, 640, animData.animData[curFrame], x, y, w, h, 255);
 
 	// Original in the game: increment FIRST, then check (not check-then-increment)
@@ -1721,7 +1725,8 @@ void PelrockEngine::checkMouseHover() {
 
 	if (hotspotIndex != -1) {
 		hotspotDetected = true;
-		_hoveredMapLocation = _room->_currentRoomDescriptions[hotspotIndex].text;
+		if(hotspotIndex < _room->_currentRoomDescriptions.size())
+			_hoveredMapLocation = _room->_currentRoomDescriptions[hotspotIndex].text;
 	} else if (!alfredDetected) {
 		_hoveredMapLocation = "";
 	}
@@ -1939,6 +1944,16 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 	case 48:
 		_dialog->_goodbyeDisabled = true;
 		break;
+	case 10:{
+		// _events->waitForKey();
+		// while(!shouldQuit()) {
+		// 	playSpecialAnim(212915, true, 287, 152, 62, 58, 10);
+		// 	playSpecialAnim(236645, true, 287, 152, 62, 58, 10);
+		// 	// setScreen(11, ALFRED_DOWN);
+		// 	playSpecialAnim(261449, true, 0, 223, 64, 97, 8);
+		// }
+		break;
+		}
 	default:
 		break;
 	}
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 508dca0945d..5acb31eb49f 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -351,6 +351,11 @@ public:
 	void playSpecialAnim(uint32 offset, bool compressed, int x, int y, int width, int height, int numFrames);
 	void giveStoneToSlaves(int inventoryObject, HotSpot *hotspot);
 	void swimmingPoolCutscene(HotSpot *hotspot);
+	void pickUpStones(HotSpot *hotspot);
+	void pickUpMud(HotSpot *hotspot);
+	void openPyramidDoor(HotSpot *hotspot);
+	void usePumpkinWithPond(int inventoryObject, HotSpot *hotspot);
+	void useWaterOnFakeStone(int inventoryObject, HotSpot *hotspot);
 	void magicFormula(int inventoryObject, HotSpot *hotspot);
 	void checkAllSymbols();
 	void openMcDoor(HotSpot *hotspot);
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index ab39d0b955f..935651fa54a 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -51,6 +51,7 @@ const AlfredSpecialAnimOffset ResourceManager::alfredSpecialAnims[] = {
 	{23, 116, 124, 1, 7, 2060916, 1}, // 11 - Munheco 1
 	{18, 177, 124, 1, 7, 2115632, 1}, // 12 - Munheco 2
 	{11, 98, 138, 1, 7, 1526432, 1}, // 13 - Munheco 3
+	{4, 51, 102, 1, 7, 2972568, 1}, // 14 - descamisa
 };
 
 ResourceManager::~ResourceManager() {
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 4f00a6d10d9..7330e8237f8 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -247,6 +247,14 @@ void RoomManager::enableSprite(byte spriteIndex, byte zOrder, int persist) {
 }
 
 void RoomManager::enableSprite(byte roomNumber, byte spriteIndex, byte zOrder, int persist) {
+	for(int i =0; i < g_engine->_state->spriteChanges[roomNumber].size(); i++) {
+		if (g_engine->_state->spriteChanges[roomNumber][i].spriteIndex == spriteIndex) {
+			debug("Removing pending sprite change for sprite %d in room %d", spriteIndex, roomNumber);
+			g_engine->_state->spriteChanges[roomNumber].remove_at(i);
+			break;
+		}
+	}
+
 	if (roomNumber == _currentRoomNumber && persist & PERSIST_TEMP) {
 		// Search by sprite.index field, not by array position (array is re-sorted every frame).
 		for (uint i = 0; i < _currentRoomAnims.size(); i++) {
@@ -257,6 +265,7 @@ void RoomManager::enableSprite(byte roomNumber, byte spriteIndex, byte zOrder, i
 		}
 	}
 	if (persist & PERSIST_PERM) {
+		debug("Enabling sprite %d in room %d with zOrder %d and persist %d", spriteIndex, roomNumber, zOrder, persist);
 		g_engine->_state->spriteChanges[roomNumber].push_back({roomNumber, spriteIndex, zOrder});
 	}
 }
@@ -929,6 +938,7 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 		sprite.disableAfterSequence = data[animOffset + 39];
 		for (int j = 0; j < spriteChanges.size(); j++) {
 			if (spriteChanges[j].spriteIndex == sprite.index) {
+				debug("Sprite %d has been changed, loading changed version with zOrder %d", sprite.index, spriteChanges[j].zIndex);
 				sprite.zOrder = spriteChanges[j].zIndex;
 				break;
 			}
@@ -955,6 +965,10 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 			anim.animData = new byte *[anim.nframes];
 			if (sprite.w > 0 && sprite.h > 0 && anim.nframes > 0) {
 				for (int i = 0; i < anim.nframes; i++) {
+					if(picOffset >= pixelDataSize) {
+						debug("Pixel data offset out of bounds for sprite %d anim %d, offset %d, size %d", i, j, picOffset, pixelDataSize);
+						break;
+					}
 					anim.animData[i] = new byte[sprite.w * sprite.h];
 					// debug("Extracting frame %d for anim %d-%d, w=%d h=%d, pixelDataSize=%d, current offset %d", i, j, anim.nframes, sprite.w, sprite.h, pixelDataSize, picOffset);
 					extractSingleFrame(pixelData + picOffset, anim.animData[i], i, sprite.w, sprite.h);
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index f807b91a233..f318c66ae9c 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -44,6 +44,8 @@ static const int unpickableHotspotExtras[] = {
 	74,
 	6,
 	7,
+	91, //mud and stone should only be picked under certain conditions!
+	92
 };
 
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 0a790393cf0..b18138a74d4 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -476,6 +476,7 @@ struct ResetEntry {
 #define FLAG_VIGILANTE_BEBE_AGUA 21
 #define FLAG_FORMULA_MAGICA 26
 #define FLAG_DA_PIEDRA 31
+#define FLAG_GUARDIAS_BORRACHOS 33
 
 
 
@@ -491,7 +492,6 @@ struct ResetEntry {
 #define FLAG_AL_FARAON 29
 #define FLAG_A_CURRAR 30
 #define FLAG_PIEDRAS_COGIDAS 32
-#define FLAG_GUARDIAS_BORRACHOS 33
 #define FLAG_PIEDRA_FAKE_MOJADA 34
 #define FLAG_PUERTA_BUENA 35
 #define FLAG_TRAMPILLA_ABIERTA 36


Commit: 4043d1ffc27b56e4aea7fa81eed4d3255575f982
    https://github.com/scummvm/scummvm/commit/4043d1ffc27b56e4aea7fa81eed4d3255575f982
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:10+02:00

Commit Message:
PELROCK: Crawl animation for room 55, fixes palette remappings

Changed paths:
    engines/pelrock/graphics.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/room.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index 5d88c147a5a..c1552d88d7e 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -41,7 +41,7 @@ Common::Point GraphicsManager::showOverlay(int height, byte *buf) {
 	for (int x = 0; x < 640; x++) {
 		for (int y = overlayY; y < 400; y++) {
 			int index = y * 640 + x;
-			buf[index] = g_engine->_room->_paletteRemaps[0][buf[index]];
+			buf[index] = g_engine->_room->_paletteRemaps[2][buf[index]];
 		}
 	}
 	return Common::Point(overlayX, overlayY);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index a2424d910fa..1ce9bf7a4cb 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -158,7 +158,8 @@ void PelrockEngine::init() {
 		// setScreen(3, ALFRED_RIGHT);
 		// setScreen(22, ALFRED_DOWN);
 		// setScreen(41, ALFRED_DOWN);
-		setScreen(43, ALFRED_DOWN);
+		// setScreen(43, ALFRED_DOWN);
+		setScreen(55, ALFRED_RIGHT);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
@@ -622,7 +623,7 @@ void PelrockEngine::paintDebugLayer() {
 	if (showSprites) {
 		for (uint i = 0; i < _room->_currentRoomAnims.size(); i++) {
 			Sprite sprite = _room->_currentRoomAnims[i];
-			if(sprite.zOrder < 0) {
+			if (sprite.zOrder < 0) {
 				// Skip sprites with negative zOrder (not rendered)
 				continue;
 			}
@@ -841,7 +842,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		break;
 	}
 	case ALFRED_IDLE: {
-		drawAlfred(_res->alfredIdle[_alfredState.direction]);
+		drawIdleFrame();
 		break;
 	}
 	case ALFRED_WALKING: {
@@ -873,7 +874,8 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		if (step.distanceY > 0)
 			step.distanceY -= MIN(_alfredState.movementSpeedY, step.distanceY);
 
-		if (step.distanceX <= 0 && step.distanceY <= 0) {
+		if (step.distanceX <= 0 && step.
+			distanceY <= 0) {
 			_currentStep++;
 			if (_currentStep >= _currentContext.movementCount) {
 				_currentStep = 0;
@@ -889,7 +891,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 				} else if (_currentHotspot != nullptr) {
 					_alfredState.direction = calculateAlfredsDirection(_currentHotspot);
 				}
-				drawAlfred(_res->alfredIdle[_alfredState.direction]);
+				drawIdleFrame();
 				if (_queuedAction.isQueued) {
 					// look and talk execute immediately, others need interaction animation first
 					if (_queuedAction.verb == TALK || _queuedAction.verb == LOOK) {
@@ -921,13 +923,24 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		} else {
 			_currentContext.movementBuffer[_currentStep] = step;
 		}
+		if (_room->_currentRoomNumber == 55) {
+			if (_alfredState.curFrame >= 9) {
+				_alfredState.curFrame = 0;
+			}
+			if (_alfredState.animState == ALFRED_WALKING) { // in case it changed to idle above
+				debug("Drawing crawl frame %d for direction %d", _alfredState.curFrame, _alfredState.direction);
+				drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCrawlFrames[_alfredState.direction][_alfredState.curFrame], _alfredState.x, _alfredState.y - 55, 130, 55, 255);
+				_alfredState.curFrame++;
+			}
+		} else {
+			if (_alfredState.curFrame >= walkingAnimLengths[_alfredState.direction]) {
+				_alfredState.curFrame = 0;
+			}
+			if (_alfredState.animState == ALFRED_WALKING) { // in case it changed to idle above
 
-		if (_alfredState.curFrame >= walkingAnimLengths[_alfredState.direction]) {
-			_alfredState.curFrame = 0;
-		}
-		if (_alfredState.animState == ALFRED_WALKING) { // in case it changed to idle above
-			drawAlfred(_res->alfredWalkFrames[_alfredState.direction][_alfredState.curFrame]);
-			_alfredState.curFrame++;
+				drawAlfred(_res->alfredWalkFrames[_alfredState.direction][_alfredState.curFrame]);
+				_alfredState.curFrame++;
+			}
 		}
 		break;
 	}
@@ -1000,6 +1013,14 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 	}
 }
 
+void PelrockEngine::drawIdleFrame() {
+	if (_room->_currentRoomNumber == 55) {
+		drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCrawlFrames[_alfredState.direction][0], _alfredState.x, _alfredState.y - 55, 130, 55, 255);
+	} else {
+		drawAlfred(_res->alfredIdle[_alfredState.direction]);
+	}
+}
+
 byte *PelrockEngine::scale(int scaleY, int finalWidth, int finalHeight, byte *buf) {
 
 	// The scaling table is indexed by how many scanlines to skip (scaleY), not by final height
@@ -1113,26 +1134,28 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	// Original game scans shadow buffer
 	// at (topY + 0x66) * 640 + X + col for col = 0..width, where topY + 0x66 = feetY.
 	// The shadow map value (0-3) indexes into the palette remap tables.
-	byte shadowLevel = 0xFF; // 0xFF = no shadow
-	int feetY = _alfredState.y;
-	if (feetY >= 0 && feetY < 400 && _room->_pixelsShadows != nullptr) {
-		for (int col = 0; col < finalWidth; col++) {
-			int checkX = _alfredState.x + col;
-			if (checkX >= 0 && checkX < 640) {
-				byte shadowVal = _room->_pixelsShadows[feetY * 640 + checkX];
-				if (shadowVal != 0xFF) {
-					shadowLevel = shadowVal;
-					break; // Original breaks on first shadow pixel found
+	if (_room->_pixelsShadows != nullptr) {
+		byte shadowLevel = 0xFF; // 0xFF = no shadow
+		int feetY = _alfredState.y;
+		if (feetY >= 0 && feetY < 400 && _room->_pixelsShadows != nullptr) {
+			for (int col = 0; col < finalWidth; col++) {
+				int checkX = _alfredState.x + col;
+				if (checkX >= 0 && checkX < 640) {
+					byte shadowVal = _room->_pixelsShadows[feetY * 640 + checkX];
+					if (shadowVal != 0xFF) {
+						shadowLevel = shadowVal;
+						break; // Original breaks on first shadow pixel found
+					}
 				}
 			}
 		}
-	}
 
-	if (shadowLevel != 0xFF && shadowLevel < 4) {
-		for (int i = 0; i < finalWidth * finalHeight; i++) {
-			if (_alfredSprite[i] != 255) {
-				// _alfredSprite[i] = _room->_paletteRemaps[3 - shadowLevel][_alfredSprite[i]];
-				_alfredSprite[i] = _room->_paletteRemaps[0][_alfredSprite[i]];
+		if (shadowLevel != 0xFF && shadowLevel < 4) {
+			for (int i = 0; i < finalWidth * finalHeight; i++) {
+				if (_alfredSprite[i] != 255) {
+					_alfredSprite[i] = _room->_paletteRemaps[shadowLevel][_alfredSprite[i]];
+					// _alfredSprite[i] = _room->_paletteRemaps[0][_alfredSprite[i]];
+				}
 			}
 		}
 	}
@@ -1198,7 +1221,7 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 
 	int frameSize = sprite->stride;
 	int curFrame = animData.curFrame;
-	if(curFrame >= animData.nframes) {
+	if (curFrame >= animData.nframes) {
 		debug("Warning: curFrame %d exceeds nframes %d for sprite %d anim %d", curFrame, animData.nframes, sprite->index, sprite->curAnimIndex);
 		curFrame = 0;
 	}
@@ -1725,7 +1748,7 @@ void PelrockEngine::checkMouseHover() {
 
 	if (hotspotIndex != -1) {
 		hotspotDetected = true;
-		if(hotspotIndex < _room->_currentRoomDescriptions.size())
+		if (hotspotIndex < _room->_currentRoomDescriptions.size())
 			_hoveredMapLocation = _room->_currentRoomDescriptions[hotspotIndex].text;
 	} else if (!alfredDetected) {
 		_hoveredMapLocation = "";
@@ -1944,7 +1967,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 	case 48:
 		_dialog->_goodbyeDisabled = true;
 		break;
-	case 10:{
+	case 10: {
 		// _events->waitForKey();
 		// while(!shouldQuit()) {
 		// 	playSpecialAnim(212915, true, 287, 152, 62, 58, 10);
@@ -1953,7 +1976,11 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		// 	playSpecialAnim(261449, true, 0, 223, 64, 97, 8);
 		// }
 		break;
-		}
+	}
+	case 55: {
+
+		break;
+	}
 	default:
 		break;
 	}
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 5acb31eb49f..0377c974501 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -101,6 +101,7 @@ private:
 	void lookAt(HotSpot *hotspot);
 
 	void chooseAlfredStateAndDraw();
+void drawIdleFrame();
 	void drawAlfred(byte *buf);
 	void drawNextFrame(Sprite *animSet);
 	void animateTalkingNPC(Sprite *animSet);
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 935651fa54a..12c87fcbd7b 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -147,10 +147,12 @@ void ResourceManager::loadAlfredAnims() {
 
 	int index = 0;
 	int index3 = 0;
-	uint32_t capacity = 3060 * 102;
-	unsigned char *pic = new unsigned char[capacity];
-	rleDecompress(bufferFile, alfred3Size, 0, capacity, &pic);
+	uint32_t capacity = 3060 * 102 + 2340 * 55;
+	unsigned char *completePic = new unsigned char[capacity];
+	rleDecompress(bufferFile, alfred3Size, 0, capacity, &completePic);
 
+	byte *stdFramesPic = new byte[3060 * 102];
+	Common::copy(completePic, completePic + 3060 * 102, stdFramesPic);
 	int frameSize = kAlfredFrameHeight * kAlfredFrameWidth;
 	for (int i = 0; i < 4; i++) {
 		alfredIdle[i] = new byte[frameSize];
@@ -170,12 +172,12 @@ void ResourceManager::loadAlfredAnims() {
 
 		int standingFrame = prevWalkingFrames;
 
-		extractSingleFrame(pic, alfredIdle[i], standingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
+		extractSingleFrame(stdFramesPic, alfredIdle[i], standingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
 		for (int j = 0; j < walkingAnimLengths[i]; j++) {
 
 			alfredWalkFrames[i][j] = new byte[frameSize];
 			int walkingFrame = prevWalkingFrames + 1 + j;
-			extractSingleFrame(pic, alfredWalkFrames[i][j], walkingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
+			extractSingleFrame(stdFramesPic, alfredWalkFrames[i][j], walkingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
 		}
 
 		alfredTalkFrames[i] = new byte *[talkingAnimLengths[i]];
@@ -184,7 +186,7 @@ void ResourceManager::loadAlfredAnims() {
 		for (int j = 0; j < talkingAnimLengths[i]; j++) {
 			alfredTalkFrames[i][j] = new byte[frameSize];
 			int talkingFrame = talkingStartFrame + j;
-			extractSingleFrame(pic, alfredTalkFrames[i][j], talkingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
+			extractSingleFrame(stdFramesPic, alfredTalkFrames[i][j], talkingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
 		}
 
 		alfredInteractFrames[i] = new byte *[interactingAnimLength];
@@ -192,7 +194,19 @@ void ResourceManager::loadAlfredAnims() {
 		for (int j = 0; j < interactingAnimLength; j++) {
 			alfredInteractFrames[i][j] = new byte[frameSize];
 			int interactingFrame = interactingStartFrame + j;
-			extractSingleFrame(pic, alfredInteractFrames[i][j], interactingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
+			extractSingleFrame(stdFramesPic, alfredInteractFrames[i][j], interactingFrame, kAlfredFrameWidth, kAlfredFrameHeight);
+		}
+	}
+
+	byte *crawlFramesPic = new byte[2340 * 55];
+	Common::copy(completePic + 3060 * 102, completePic + 3060 * 102 + 2340 * 55, crawlFramesPic);
+	int crawlFrameSize = 2340 * 55;
+	for (int i = 0; i < 4; i++) {
+		alfredCrawlFrames[i] = new byte *[9];
+		for (int j = 0; j < 9; j++) {
+			int walkingFrame = (i % 2) * 9 + j;
+			alfredCrawlFrames[i][j] = new byte[crawlFrameSize];
+			extractSingleFrame(crawlFramesPic, alfredCrawlFrames[i][j], walkingFrame, 130, 55);
 		}
 	}
 
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index e016fff7c93..d4c6ea0e2f4 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -62,7 +62,7 @@ public:
 	byte *alfredIdle[4]; // 4 directions
 
 	byte **alfredWalkFrames[4]; // 4 arrays of arrays
-
+	byte **alfredCrawlFrames[4];
 	byte **alfredTalkFrames[4]; // 4 arrays of arrays
 
 	byte **alfredCombFrames[2];
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 7330e8237f8..69851aa4c47 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -1277,7 +1277,12 @@ byte *RoomManager::loadShadowMap(int roomNumber) {
 	readUntilBuda(&shadowMapFile, shadowOffset, compressed, compressedSize);
 
 	byte *shadows = nullptr;
-	rleDecompress(compressed, compressedSize, 0, 640 * 400, &shadows);
+	size_t decompressedSize = rleDecompress(compressed, compressedSize, 0, 640 * 400, &shadows);
+	if(decompressedSize == 0) {
+		debug("Failed to decompress shadow map for room %d", roomNumber);
+		shadows = nullptr;
+	}
+	debug("Decompressed shadow map for room %d, compressed size: %zu, decompressed size: %zu", roomNumber, compressedSize, decompressedSize);
 	free(compressed);
 	shadowMapFile.close();
 	return shadows;
@@ -1290,7 +1295,7 @@ void RoomManager::loadRemaps(int roomNumber) {
 		error("Couldnt find file ALFRED.9");
 	}
 
-	uint32 remapOffset = 0x200 + (roomNumber * 1024);
+	uint32 remapOffset =/* 0x200 + */(roomNumber * 1024);
 
 	remapFile.seek(remapOffset, SEEK_SET);
 	remapFile.read(_paletteRemaps[0], 256);
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index b18138a74d4..804070845a3 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -21,6 +21,7 @@
 #ifndef PELROCK_TYPES_H
 #define PELROCK_TYPES_H
 
+#include "common/debug.h"
 #include "common/scummsys.h"
 #include "common/system.h"
 #include "common/types.h"
@@ -170,6 +171,7 @@ struct AlfredState {
 	bool isWalkingCancelable = true;
 
 	void setState(AlfredAnimState nextState) {
+		debug("Transitioning Alfred from state %d to state %d", animState, nextState);
 		animState = nextState;
 		curFrame = 0;
 	}


Commit: e71946da22ce65b86c79ecedacb44153e8380da0
    https://github.com/scummvm/scummvm/commit/e71946da22ce65b86c79ecedacb44153e8380da0
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:10+02:00

Commit Message:
PELROCK: Room 47

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index a1d10a52ba8..5447535c14c 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -163,6 +163,13 @@ const ActionEntry actionTable[] = {
 	// Room 44
 	{613, OPEN, &PelrockEngine::openPyramidDoor},
 
+	// Room 46
+	{621, OPEN, &PelrockEngine::openArchitectDoor},
+	{621, CLOSE, &PelrockEngine::closeArchitectDoor},
+
+	// Room 47
+	{628, PICKUP, &PelrockEngine::pickupPyramidMap},
+
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
 	{WILDCARD, TALK, &PelrockEngine::noOpAction},   // Generic talk action
@@ -614,9 +621,22 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 320:
 		_state->setCurrentRoot(room, 2, 0);
 		break;
-	case 324:
-		// ?
+	case 324: {
+		HotSpot hotspot = HotSpot();
+		hotspot.extra = 628;
+		hotspot.x = 221;
+		hotspot.y = 202;
+		hotspot.w = 50;
+		hotspot.h = 56;
+		hotspot.isEnabled = true;
+		hotspot.isSprite = false;
+		hotspot.actionFlags = 8;
+		hotspot.index = 9;
+		hotspot.innerIndex = 5;
+		_room->changeHotspot(47, hotspot);
+		_state->setCurrentRoot(room, 2, 0);
 		break;
+	}
 	// girls in pond
 	case 321:
 		_state->setCurrentRoot(45, 1, 0);
@@ -633,6 +653,12 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_state->setCurrentRoot(45, 3, 0);
 		break;
 	case 30840:
+		//toJail()? not present in the game
+		break;
+
+	case 323:
+		_state->setCurrentRoot(47, 1, 0);
+		_state->setCurrentRoot(43, 3, 0);
 		break;
 	default:
 		debug("Got actionTrigger %d in dialogActionTrigger, but no handler defined", actionTrigger);
@@ -1703,6 +1729,18 @@ void PelrockEngine::magicFormula(int inventoryObject, HotSpot *hotspot) {
 	}
 }
 
+void PelrockEngine::openArchitectDoor(HotSpot *hotspot) {
+	openDoor(hotspot, 2, 124, FEMININE, true);
+}
+
+void PelrockEngine::closeArchitectDoor(HotSpot *hotspot) {
+	closeDoor(hotspot, 2, 124, FEMININE, true);
+}
+
+void PelrockEngine::pickupPyramidMap(HotSpot *hotspot) {
+	addInventoryItem(98);
+}
+
 void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	debug("Performing action trigger: %d", actionTrigger);
 	switch (actionTrigger) {
@@ -1747,6 +1785,11 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 		_room->changeHotSpot(*stone);
 		break;
 	}
+
+	case 322: {
+		_dialog->say(_res->_ingameTexts[NOSETEOCURRAACERCARTE]);
+		break;
+	}
 	}
 }
 
@@ -1890,6 +1933,12 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		_state->setCurrentRoot(43, 1, 0);
 		break;
 	}
+	case 98: {
+		_res->loadAlfredSpecialAnim(1);
+		_alfredState.animState = ALFRED_SPECIAL_ANIM;
+		waitForSpecialAnimation();
+		_dialog->say(_res->_ingameTexts[PUERTAAUTENTICA_IZQUIERDA]);
+	}
 	default:
 		break;
 	}
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 1ce9bf7a4cb..424cef4f320 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -159,7 +159,7 @@ void PelrockEngine::init() {
 		// setScreen(22, ALFRED_DOWN);
 		// setScreen(41, ALFRED_DOWN);
 		// setScreen(43, ALFRED_DOWN);
-		setScreen(55, ALFRED_RIGHT);
+		setScreen(46, ALFRED_RIGHT);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
@@ -375,10 +375,10 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 		int spriteIndex = anim.spriteIndex;
 		int startX = anim.startX;
 		int startY = anim.startY;
-		debug("Checking passerby anim %d for sprite %d, direction %d", _room->_passerByAnims->currentAnimIndex, spriteIndex, direction);
+		// debug("Checking passerby anim %d for sprite %d, direction %d", _room->_passerByAnims->currentAnimIndex, spriteIndex, direction);
 		Sprite *sprite = _room->findSpriteByIndex(spriteIndex);
 		if (direction == PASSERBY_RIGHT) {
-			debug("Checking passerby anim for sprite %d moving RIGHT, curpos is %d", spriteIndex, sprite->x);
+			// debug("Checking passerby anim for sprite %d moving RIGHT, curpos is %d", spriteIndex, sprite->x);
 			if (sprite->x >= anim.resetCoord) {
 				sprite->x = startX;
 				sprite->y = startY;
@@ -388,7 +388,7 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 				_room->_passerByAnims->latch = false;
 			}
 		} else if (direction == PASSERBY_LEFT) {
-			debug("Checking passerby anim for sprite %d moving LEFT, curpos is %d", spriteIndex, sprite->x);
+			// debug("Checking passerby anim for sprite %d moving LEFT, curpos is %d", spriteIndex, sprite->x);
 
 			if (sprite->x <= anim.resetCoord) {
 				sprite->x = startX;
@@ -399,7 +399,7 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 				_room->_passerByAnims->latch = false;
 			}
 		} else if (direction == PASSERBY_DOWN) {
-			debug("Checking passerby anim for sprite %d moving DOWN, curpos is %d, reset %d", spriteIndex, sprite->y, anim.resetCoord);
+			// debug("Checking passerby anim for sprite %d moving DOWN, curpos is %d, reset %d", spriteIndex, sprite->y, anim.resetCoord);
 			if (sprite->y >= anim.resetCoord) {
 				sprite->x = startX;
 				sprite->y = startY;
@@ -914,6 +914,16 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 						_res->loadAlfredSpecialAnim(8);
 						_alfredState.setState(ALFRED_SPECIAL_ANIM);
 						waitForSpecialAnimation();
+					} else if (exit->targetRoom == 55 && _room->_currentRoomNumber == 44) {
+						uint16 x = _alfredState.x;
+						uint16 y = _alfredState.y;
+						_alfredState.x -= 20;
+						_alfredState.y += 5;
+						_res->loadAlfredSpecialAnim(15);
+						_alfredState.setState(ALFRED_SPECIAL_ANIM);
+						waitForSpecialAnimation();
+						_alfredState.x = x;
+						_alfredState.y = y;
 					}
 					_alfredState.x = exit->targetX;
 					_alfredState.y = exit->targetY;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 0377c974501..e0b390f7492 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -101,7 +101,7 @@ private:
 	void lookAt(HotSpot *hotspot);
 
 	void chooseAlfredStateAndDraw();
-void drawIdleFrame();
+	void drawIdleFrame();
 	void drawAlfred(byte *buf);
 	void drawNextFrame(Sprite *animSet);
 	void animateTalkingNPC(Sprite *animSet);
@@ -358,6 +358,9 @@ public:
 	void usePumpkinWithPond(int inventoryObject, HotSpot *hotspot);
 	void useWaterOnFakeStone(int inventoryObject, HotSpot *hotspot);
 	void magicFormula(int inventoryObject, HotSpot *hotspot);
+	void openArchitectDoor(HotSpot *hotspot);
+	void closeArchitectDoor(HotSpot *hotspot);
+	void pickupPyramidMap(HotSpot *hotspot);
 	void checkAllSymbols();
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 12c87fcbd7b..37fe0a14776 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -33,7 +33,6 @@ ResourceManager::ResourceManager(/* args */) {
 	for (int i = 0; i < 4; i++) {
 		alfredIdle[i] = nullptr;
 	}
-
 }
 
 const AlfredSpecialAnimOffset ResourceManager::alfredSpecialAnims[] = {
@@ -52,6 +51,7 @@ const AlfredSpecialAnimOffset ResourceManager::alfredSpecialAnims[] = {
 	{18, 177, 124, 1, 7, 2115632, 1}, // 12 - Munheco 2
 	{11, 98, 138, 1, 7, 1526432, 1}, // 13 - Munheco 3
 	{4, 51, 102, 1, 7, 2972568, 1}, // 14 - descamisa
+	{13, 95, 99, 1, 7, 1749464, 1}, // 15 - alfred enters secret passage
 };
 
 ResourceManager::~ResourceManager() {
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 804070845a3..207b4808139 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -171,7 +171,6 @@ struct AlfredState {
 	bool isWalkingCancelable = true;
 
 	void setState(AlfredAnimState nextState) {
-		debug("Transitioning Alfred from state %d to state %d", animState, nextState);
 		animState = nextState;
 		curFrame = 0;
 	}


Commit: c5a47052d4ee4590638cbb8c3a87b5b94326e75e
    https://github.com/scummvm/scummvm/commit/c5a47052d4ee4590638cbb8c3a87b5b94326e75e
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:10+02:00

Commit Message:
PELROCK: Wrong door cutscene in Room 48

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 5447535c14c..128048a0a31 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -653,7 +653,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_state->setCurrentRoot(45, 3, 0);
 		break;
 	case 30840:
-		//toJail()? not present in the game
+		// toJail()? not present in the game
 		break;
 
 	case 323:
@@ -1668,11 +1668,10 @@ void PelrockEngine::usePumpkinWithPond(int inventoryObject, HotSpot *hotspot) {
 void PelrockEngine::useWaterOnFakeStone(int inventoryObject, HotSpot *hotspot) {
 
 	int count = _state->getFlag(FLAG_PIEDRA_FAKE_MOJADA);
-	if(count != 3) {
+	if (count != 3) {
 		_state->removeInventoryItem(86);
 		addInventoryItem(76);
-		switch (count)
-		{
+		switch (count) {
 		case 0:
 			_room->addSticker(120);
 			break;
@@ -1696,36 +1695,54 @@ void PelrockEngine::magicFormula(int inventoryObject, HotSpot *hotspot) {
 	_state->setFlag(FLAG_FORMULA_MAGICA, _state->getFlag(FLAG_FORMULA_MAGICA) + 1);
 	if (_state->getFlag(FLAG_FORMULA_MAGICA) == 4) {
 
-		size_t frameSize = 98 * 138;
-		size_t bufSize = frameSize * 11;
-		byte *smokeFrames = new byte[bufSize];
-		_res->loadOtherSpecialAnim(1526432, true, smokeFrames, bufSize);
-		Graphics::Surface smokeSurface;
-		smokeSurface.create(98, 138, Graphics::PixelFormat::createFormatCLUT8());
-		int curFrame = 0;
-		while (!shouldQuit()) {
-			_events->pollEvent();
+		smokeAnimation(-1);
+		_alfredState.setState(ALFRED_IDLE);
+		setScreen(39, ALFRED_UP);
+	}
+}
+
+void PelrockEngine::smokeAnimation(int spriteIndex, bool hide) {
+	size_t frameSize = 98 * 138;
+	size_t bufSize = frameSize * 11;
+	byte *smokeFrames = new byte[bufSize];
+	_res->loadOtherSpecialAnim(1526432, true, smokeFrames, bufSize);
+	Graphics::Surface smokeSurface;
+	smokeSurface.create(98, 138, Graphics::PixelFormat::createFormatCLUT8());
+	int curFrame = 0;
+	int x = _alfredState.x;
+	int y = _alfredState.y - _alfredState.h;
+	if (spriteIndex >= 0) {
+		x = _room->findSpriteByIndex(spriteIndex)->x;
+		y = _room->findSpriteByIndex(spriteIndex)->y;
+	}
+	while (!shouldQuit()) {
+		_events->pollEvent();
 
-			bool didRender = renderScene(OVERLAY_NONE);
+		bool didRender = renderScene(OVERLAY_NONE);
 
-			memset(smokeSurface.getPixels(), 0, frameSize);
-			extractSingleFrame(smokeFrames, (byte *)smokeSurface.getPixels(), curFrame, 98, 138);
-			_screen->transBlitFrom(smokeSurface, Common::Point(_alfredState.x, _alfredState.y - _alfredState.h), 255);
-			if (curFrame == 5) {
+		memset(smokeSurface.getPixels(), 0, frameSize);
+		extractSingleFrame(smokeFrames, (byte *)smokeSurface.getPixels(), curFrame, 98, 138);
+		_screen->transBlitFrom(smokeSurface, Common::Point(x, y), 255);
+		if (curFrame == 5) {
+			if (spriteIndex == -1) {
 				_alfredState.setState(ALFRED_SKIP_DRAWING);
+			} else {
+				if (hide) {
+					_room->disableSprite(spriteIndex);
+				} else {
+					_room->enableSprite(spriteIndex, 200);
+				}
 			}
-			if (didRender && _chrono->getFrameCount() % 2 == 0) {
-				curFrame++;
+		}
+		if (didRender && _chrono->getFrameCount()) {
+			curFrame++;
 
-				if (curFrame >= 11) {
-					break;
-				}
+			if (curFrame >= 11) {
+				break;
 			}
-			_screen->update();
-			g_system->delayMillis(10);
 		}
-		_alfredState.setState(ALFRED_IDLE);
-		setScreen(39, ALFRED_UP);
+		_screen->update();
+		g_system->delayMillis(10);
 	}
 }
 
@@ -1934,16 +1951,36 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		break;
 	}
 	case 98: {
-		_res->loadAlfredSpecialAnim(1);
-		_alfredState.animState = ALFRED_SPECIAL_ANIM;
-		waitForSpecialAnimation();
-		_dialog->say(_res->_ingameTexts[PUERTAAUTENTICA_IZQUIERDA]);
+		chooseCorrectDoor();
 	}
 	default:
 		break;
 	}
 }
 
+void PelrockEngine::chooseCorrectDoor() {
+	_res->loadAlfredSpecialAnim(1);
+	_alfredState.animState = ALFRED_SPECIAL_ANIM;
+	waitForSpecialAnimation();
+	byte puertaBuena = _state->getFlag(FLAG_PUERTA_BUENA);
+	if (puertaBuena == 0) { // if not set yet, choose randomly
+		int choice = getRandomNumber(1);
+		_state->setFlag(FLAG_PUERTA_BUENA, choice + 1);
+		debug("Randomly chosen good door: %d", _state->getFlag(FLAG_PUERTA_BUENA));
+	}
+	puertaBuena = _state->getFlag(FLAG_PUERTA_BUENA);
+	Common::String doorText = _res->_izquierda;
+	if (puertaBuena == 1) {
+		doorText = _res->_izquierda;
+	} else if (puertaBuena == 2) {
+		doorText = _res->_derecha;
+	}
+	Common::StringArray fullTextArray = _res->_ingameTexts[PUERTAAUTENTICA_IZQUIERDA];
+	fullTextArray[0] = fullTextArray[0].substr(0, 45);
+	fullTextArray[0].append(doorText);
+	_dialog->say(fullTextArray);
+}
+
 void PelrockEngine::animateStatuePaletteFade(bool reverse) {
 	// Structure at JUEGO.EXE offset 0x4C700
 	struct StatuePaletteData {
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 60b7a9a83cf..41f5c192547 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -51,7 +51,7 @@ static const uint32 kMenuTextOffset = 0x49203;
 static const uint32 kMenuTextSize = 230;
 static const uint32 kAlfredResponsesOffset = 0x441DC;
 static const uint32 kConversationTerminatorOffset = 0x0492EE;
-static const uint32 kAlfredResponsesSize = 12163;
+static const uint32 kAlfredResponsesSize = 12143;
 static const uint32 kCreditsOffset = 0x49F60;
 static const uint32 kCreditsSize = 2540;
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 424cef4f320..4da96005162 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -788,6 +788,9 @@ void PelrockEngine::animateRotatePalette(PaletteAnim *anim) {
 }
 
 void PelrockEngine::doAction(VerbIcon action, HotSpot *hotspot) {
+	if (action == NO_ACTION) {
+		return;
+	}
 	switch (action) {
 	case LOOK:
 		lookAt(hotspot);
@@ -874,8 +877,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		if (step.distanceY > 0)
 			step.distanceY -= MIN(_alfredState.movementSpeedY, step.distanceY);
 
-		if (step.distanceX <= 0 && step.
-			distanceY <= 0) {
+		if (step.distanceX <= 0 && step.distanceY <= 0) {
 			_currentStep++;
 			if (_currentStep >= _currentContext.movementCount) {
 				_currentStep = 0;
@@ -910,21 +912,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 				Exit *exit = isExitUnder(_alfredState.x, _alfredState.y);
 				if (exit != nullptr) {
 					debug("Using exit to room %d", exit->targetRoom);
-					if (exit->targetRoom == 31 && _room->_currentRoomNumber == 32) {
-						_res->loadAlfredSpecialAnim(8);
-						_alfredState.setState(ALFRED_SPECIAL_ANIM);
-						waitForSpecialAnimation();
-					} else if (exit->targetRoom == 55 && _room->_currentRoomNumber == 44) {
-						uint16 x = _alfredState.x;
-						uint16 y = _alfredState.y;
-						_alfredState.x -= 20;
-						_alfredState.y += 5;
-						_res->loadAlfredSpecialAnim(15);
-						_alfredState.setState(ALFRED_SPECIAL_ANIM);
-						waitForSpecialAnimation();
-						_alfredState.x = x;
-						_alfredState.y = y;
-					}
+					exitTriggers(exit);
 					_alfredState.x = exit->targetX;
 					_alfredState.y = exit->targetY;
 					setScreen(exit->targetRoom, exit->dir);
@@ -1023,6 +1011,37 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 	}
 }
 
+void PelrockEngine::exitTriggers(Pelrock::Exit *exit) {
+	if (exit->targetRoom == 31 && _room->_currentRoomNumber == 32) {
+		_res->loadAlfredSpecialAnim(8);
+		_alfredState.setState(ALFRED_SPECIAL_ANIM);
+		waitForSpecialAnimation();
+	} else if (exit->targetRoom == 55 && _room->_currentRoomNumber == 44) {
+		uint16 x = _alfredState.x;
+		uint16 y = _alfredState.y;
+		_alfredState.x -= 20;
+		_alfredState.y += 5;
+		_res->loadAlfredSpecialAnim(15);
+		_alfredState.setState(ALFRED_SPECIAL_ANIM);
+		waitForSpecialAnimation();
+		_alfredState.x = x;
+		_alfredState.y = y;
+	} else if (exit->targetRoom == 48 && _room->_currentRoomNumber == 46) {
+		smokeAnimation(-1);
+		uint16 x = _alfredState.x;
+		uint16 y = _alfredState.y;
+		if (x < 282) {
+			if (_state->getFlag(FLAG_PUERTA_BUENA) == 1) {
+				_state->setFlag(FLAG_CORRECT_DOOR_CHOSEN, true);
+			}
+		} else {
+			if (_state->getFlag(FLAG_PUERTA_BUENA) == 2) {
+				_state->setFlag(FLAG_CORRECT_DOOR_CHOSEN, true);
+			}
+		}
+	}
+}
+
 void PelrockEngine::drawIdleFrame() {
 	if (_room->_currentRoomNumber == 55) {
 		drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCrawlFrames[_alfredState.direction][0], _alfredState.x, _alfredState.y - 55, 130, 55, 255);
@@ -1974,9 +1993,6 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		// paths lead to capture, no voluntary exit allowed.
 		_dialog->_goodbyeDisabled = true;
 		break;
-	case 48:
-		_dialog->_goodbyeDisabled = true;
-		break;
 	case 10: {
 		// _events->waitForKey();
 		// while(!shouldQuit()) {
@@ -1987,8 +2003,29 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		// }
 		break;
 	}
-	case 55: {
+	case 48: {
+		_dialog->_goodbyeDisabled = true;
+		if (_state->getFlag(FLAG_CORRECT_DOOR_CHOSEN) == true) {
 
+		} else {
+			_dialog->say(_res->_ingameTexts[OHMISALVADOR]);
+			_dialog->say(_res->_ingameTexts[VOYPORTI_PRINCESA]);
+			// _state->setCurrentRoot(48, 0, 1);
+			Sprite *thinMummy = _room->findSpriteByIndex(0);
+			Sprite *thickMummy = _room->findSpriteByIndex(1);
+			HotSpot *fatMummy = nullptr;
+			for (uint i = 0; i < _room->_currentRoomHotspots.size(); i++) {
+				if (_room->_currentRoomHotspots[i].isSprite && _room->_currentRoomHotspots[i].index == 1) {
+					fatMummy = &_room->_currentRoomHotspots[i];
+				}
+			}
+			walkAndAction(fatMummy, NO_ACTION);
+			smokeAnimation(0);
+			smokeAnimation(1, false);
+
+			walkAndAction(fatMummy, TALK);
+			debug("Restart game now!");
+		}
 		break;
 	}
 	default:
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index e0b390f7492..9bad4333d67 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -101,6 +101,7 @@ private:
 	void lookAt(HotSpot *hotspot);
 
 	void chooseAlfredStateAndDraw();
+	void exitTriggers(Pelrock::Exit *exit);
 	void drawIdleFrame();
 	void drawAlfred(byte *buf);
 	void drawNextFrame(Sprite *animSet);
@@ -109,7 +110,6 @@ private:
 
 	void playSoundIfNeeded();
 
-
 	void gameLoop();
 	void computerLoop();
 	void extraScreenLoop();
@@ -282,6 +282,7 @@ public:
 	void noOpAction(HotSpot *hotspot);
 	void noOpItem(int item, HotSpot *hotspot);
 	void useOnAlfred(int inventoryObject);
+	void chooseCorrectDoor();
 	void useCardWithATM(int inventoryObject, HotSpot *hotspot);
 	void useBrickWithWindow(int inventoryObject, HotSpot *hotspot);
 	void moveCable(HotSpot *hotspot);
@@ -358,6 +359,7 @@ public:
 	void usePumpkinWithPond(int inventoryObject, HotSpot *hotspot);
 	void useWaterOnFakeStone(int inventoryObject, HotSpot *hotspot);
 	void magicFormula(int inventoryObject, HotSpot *hotspot);
+	void smokeAnimation(int spriteIndex, bool hide = true);
 	void openArchitectDoor(HotSpot *hotspot);
 	void closeArchitectDoor(HotSpot *hotspot);
 	void pickupPyramidMap(HotSpot *hotspot);
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 37fe0a14776..9913571a9c0 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -36,22 +36,30 @@ ResourceManager::ResourceManager(/* args */) {
 }
 
 const AlfredSpecialAnimOffset ResourceManager::alfredSpecialAnims[] = {
-	{10, 51, 102, 1, 7, 559685, 1,}, // 0 - READ BOOK
-	{10, 51, 102, 1, 7, 578943, 1}, // 1 - READ RECIPE
-	{3, 45, 87, 0, 7, 37000, 1}, // 2 -  ELECTRIC SHOCK 1
-	{2, 82, 58, 0, 7, 53106, 20}, // 3 - ELECTRIC SHOCK 3
+	{
+		10,
+		51,
+		102,
+		1,
+		7,
+		559685,
+		1,
+	},                                   // 0 - READ BOOK
+	{10, 51, 102, 1, 7, 578943, 1},      // 1 - READ RECIPE
+	{3, 45, 87, 0, 7, 37000, 1},         // 2 -  ELECTRIC SHOCK 1
+	{2, 82, 58, 0, 7, 53106, 20},        // 3 - ELECTRIC SHOCK 3
 	{3, 71, 110, 1, 2, 20724, 1, 62480}, // 4 - Throw
-	{14, 171, 107, 1, 7, 1556540, 1} , // 5 - crocodile
-	{12, 113, 103, 1, 7, 1583702, 1}, // 6 - exit through manhole
-	{11, 33, 72, 1, 7, 1761234, 1}, // 7 - alfred climbs down
-	{9, 33, 72, 1, 7, 1766378, 1}, // 8 - alfred climbs up
-	{16, 158, 115, 0, 7, 1770196, 1}, // 9 - alfred exits tunnel
-	{7, 208, 102, 0, 7, 1600956, 1}, // 10 - alfred with workers
-	{23, 116, 124, 1, 7, 2060916, 1}, // 11 - Munheco 1
-	{18, 177, 124, 1, 7, 2115632, 1}, // 12 - Munheco 2
-	{11, 98, 138, 1, 7, 1526432, 1}, // 13 - Munheco 3
-	{4, 51, 102, 1, 7, 2972568, 1}, // 14 - descamisa
-	{13, 95, 99, 1, 7, 1749464, 1}, // 15 - alfred enters secret passage
+	{14, 171, 107, 1, 7, 1556540, 1},    // 5 - crocodile
+	{12, 113, 103, 1, 7, 1583702, 1},    // 6 - exit through manhole
+	{11, 33, 72, 1, 7, 1761234, 1},      // 7 - alfred climbs down
+	{9, 33, 72, 1, 7, 1766378, 1},       // 8 - alfred climbs up
+	{16, 158, 115, 0, 7, 1770196, 1},    // 9 - alfred exits tunnel
+	{7, 208, 102, 0, 7, 1600956, 1},     // 10 - alfred with workers
+	{23, 116, 124, 1, 7, 2060916, 1},    // 11 - Munheco 1
+	{18, 177, 124, 1, 7, 2115632, 1},    // 12 - Munheco 2
+	{11, 98, 138, 1, 7, 1526432, 1},     // 13 - Munheco 3
+	{4, 51, 102, 1, 7, 2972568, 1},      // 14 - descamisa
+	{13, 95, 99, 1, 7, 1749464, 1},      // 15 - alfred enters secret passage
 };
 
 ResourceManager::~ResourceManager() {
@@ -262,8 +270,7 @@ void ResourceManager::loadOtherSpecialAnim(uint32 offset, bool rleCompressed, by
 		size_t compressedSize = 0;
 		readUntilBuda(&alfred7, offset, compressed, compressedSize);
 		bufferSize = rleDecompress(compressed, compressedSize, 0, 0, &buffer, true);
-	}
-	else {
+	} else {
 		alfred7.seek(offset, SEEK_SET);
 		alfred7.read(buffer, bufferSize);
 	}
@@ -349,6 +356,9 @@ void ResourceManager::loadHardcodedText() {
 	exe.seek(kAlfredResponsesOffset, SEEK_SET);
 	exe.read(descBuffer, kAlfredResponsesSize);
 	_ingameTexts = processTextData(descBuffer, kAlfredResponsesSize);
+	// exe.seek(-1, SEEK_CUR);
+	_izquierda = exe.readString();
+	_derecha = exe.readString(0xFD);
 	byte *terminatorBuffer = new byte[39];
 	exe.seek(kConversationTerminatorOffset, SEEK_SET);
 	exe.read(terminatorBuffer, 39);
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index d4c6ea0e2f4..679a484f028 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -72,13 +72,14 @@ public:
 	byte *_verbIcons[9];
 	byte *_popUpBalloon = nullptr;
 	Common::Array<Common::StringArray> _ingameTexts;
+	Common::String _izquierda;
+	Common::String _derecha;
 	Common::String _conversationTerminator;
 
 	// Special anims
 	AlfredSpecialAnim *_currentSpecialAnim = nullptr;
 	bool _isSpecialAnimFinished = false;
 	static const AlfredSpecialAnimOffset alfredSpecialAnims[];
-
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 207b4808139..cd6a2163fcb 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -477,12 +477,15 @@ struct ResetEntry {
 #define FLAG_VIGILANTE_BEBE_AGUA 21
 #define FLAG_FORMULA_MAGICA 26
 #define FLAG_DA_PIEDRA 31
+#define FLAG_PIEDRAS_COGIDAS 32
 #define FLAG_GUARDIAS_BORRACHOS 33
+#define FLAG_PUERTA_BUENA 35
+#define FLAG_PIEDRA_FAKE_MOJADA 34
+#define FLAG_TIENDA_ABIERTA 46
 
 
 
 #define FLAG_VIAJE_A_EGIPTO 12
-
 #define FLAG_PUERTA_SECRETA_ABIERTA 16
 #define FLAG_VIGILANTE_MEANDO 22
 #define FLAG_PIRAMIDE_JODIDA 23
@@ -492,9 +495,7 @@ struct ResetEntry {
 #define FLAG_APARECE_EUNUCO 28
 #define FLAG_AL_FARAON 29
 #define FLAG_A_CURRAR 30
-#define FLAG_PIEDRAS_COGIDAS 32
-#define FLAG_PIEDRA_FAKE_MOJADA 34
-#define FLAG_PUERTA_BUENA 35
+
 #define FLAG_TRAMPILLA_ABIERTA 36
 #define FLAG_HABITACION_PRINCESA 37
 #define FLAG_A_POR_LA_PRINCESA 38
@@ -505,7 +506,6 @@ struct ResetEntry {
 #define FLAG_FROM_INTRO 43
 #define FLAG_HE_TIRADO_PIEDRA 44
 #define FLAG_HA_USADO_AGUA 45
-#define FLAG_TIENDA_ABIERTA 46
 #define FLAG_NUMERO_DE_COPAS 47
 
 #define FLAG_GUARDIA_PIDECOSAS 49
@@ -514,11 +514,12 @@ struct ResetEntry {
 #define FLAG_CONSIGNAS_VENDEDOR 52
 #define FLAG_PUTA_250_VECES 53
 #define FLAG_RESPUESTAS_ACERTADAS 54
-#define FLAG_CHEAT_CODE_ENABLED 55    // 0x495F3 - enables HIJODELAGRANPUTA cheat code input
+#define FLAG_CHEAT_CODE_ENABLED 55
 #define FLAG_RIDDLE_PRESENTED 56
 #define FLAG_SYMBOLS_PUSHED 57
+#define FLAG_CORRECT_DOOR_CHOSEN 58
 
-const int kNumGameFlags = 59;
+const int kNumGameFlags = 60;
 
 struct GameStateData {
 	byte flags[kNumGameFlags];


Commit: 91be0ceead856eef71d3ac87c018623fcd1521ff
    https://github.com/scummvm/scummvm/commit/91be0ceead856eef71d3ac87c018623fcd1521ff
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:10+02:00

Commit Message:
PELROCK: Correct choice of door in room 48 (hatch)

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 128048a0a31..29905642b6c 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1952,9 +1952,13 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 	}
 	case 98: {
 		chooseCorrectDoor();
+		break;
 	}
-	default:
+	default: {
+		byte response = (byte)getRandomNumber(12);
+		_dialog->say(_res->_ingameTexts[154 + response]);
 		break;
+		}
 	}
 }
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 4da96005162..4f6ccef1353 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1820,6 +1820,7 @@ void PelrockEngine::setScreen(int roomNumber, AlfredDirection dir) {
 		error("Could not open ALFRED.1");
 		return;
 	}
+	changeCursor(DEFAULT);
 	_sound->stopAllSounds();
 	_currentHotspot = nullptr;
 	_alfredState.direction = dir;
@@ -2006,7 +2007,28 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 	case 48: {
 		_dialog->_goodbyeDisabled = true;
 		if (_state->getFlag(FLAG_CORRECT_DOOR_CHOSEN) == true) {
+			if(_state->getFlag(FLAG_TRAMPILLA_ABIERTA) == true) {
 
+			}
+			else {
+				_dialog->say(_res->_ingameTexts[OHMISALVADOR]);
+				_dialog->say(_res->_ingameTexts[VOYPORTI_PRINCESA]);
+				walkAndAction(_room->findHotspotByExtra(634), TALK);
+				_room->addSticker(134);
+				//wait a few frames
+				int framesToWait = 0;
+				while(!shouldQuit() && framesToWait < 10) {
+					_events->pollEvent();
+
+					bool didRender = renderScene(OVERLAY_NONE);
+					if(didRender) framesToWait++;
+					_screen->update();
+					g_system->delayMillis(10);
+				}
+				_alfredState.x = 294;
+				_alfredState.y = 387;
+				setScreen(49, ALFRED_UP);
+			}
 		} else {
 			_dialog->say(_res->_ingameTexts[OHMISALVADOR]);
 			_dialog->say(_res->_ingameTexts[VOYPORTI_PRINCESA]);


Commit: f26411b2890b978167dc9a1dd4384be936eccbe2
    https://github.com/scummvm/scummvm/commit/f26411b2890b978167dc9a1dd4384be936eccbe2
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:11+02:00

Commit Message:
PELROCK: Room 49

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 29905642b6c..bbf2365b81a 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -660,6 +660,18 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_state->setCurrentRoot(47, 1, 0);
 		_state->setCurrentRoot(43, 3, 0);
 		break;
+	case 325:
+		_state->setFlag(FLAG_ESQUELETO_RECONOCE, _state->getFlag(FLAG_ESQUELETO_RECONOCE) + 1);
+		if(_state->getFlag(FLAG_ESQUELETO_RECONOCE) == 2) {
+			_state->setCurrentRoot(49, 1, 0);
+		}
+		break;
+	case 326:
+		_state->setCurrentRoot(49, 2, 0);
+		break;
+	case 378:
+		_state->setCurrentRoot(49, 3, 0);
+		break;
 	default:
 		debug("Got actionTrigger %d in dialogActionTrigger, but no handler defined", actionTrigger);
 		break;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index cd6a2163fcb..9bd5ec34147 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -518,8 +518,9 @@ struct ResetEntry {
 #define FLAG_RIDDLE_PRESENTED 56
 #define FLAG_SYMBOLS_PUSHED 57
 #define FLAG_CORRECT_DOOR_CHOSEN 58
+#define FLAG_ESQUELETO_RECONOCE 59
 
-const int kNumGameFlags = 60;
+const int kNumGameFlags = 61;
 
 struct GameStateData {
 	byte flags[kNumGameFlags];


Commit: 20202772b6b7f42b5ca48b1bdcc99121a2928d6e
    https://github.com/scummvm/scummvm/commit/20202772b6b7f42b5ca48b1bdcc99121a2928d6e
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:11+02:00

Commit Message:
PELROCK: Trigger newspaper scene also in room 0

Changed paths:
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 4f6ccef1353..250b75c4ad7 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1900,6 +1900,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 
 	switch (roomNumber) {
 	case 4:
+	case 0:
 		if (_state->getFlag(FLAG_PUESTA_SALSA_PICANTE) && !_state->getFlag(FLAG_JEFE_ENCARCELADO)) {
 			_state->setFlag(FLAG_JEFE_ENCARCELADO, true);
 			_room->disableSprite(13, 0, true);


Commit: 39cca76119a08b5ab58d8a8f9a9c4122a03784ef
    https://github.com/scummvm/scummvm/commit/39cca76119a08b5ab58d8a8f9a9c4122a03784ef
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:11+02:00

Commit Message:
PELROCK: Implements timing and spells in rooms 51-54

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 250b75c4ad7..d45fe547d7f 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -341,12 +341,12 @@ void PelrockEngine::mouseHoverForMap() {
 	}
 }
 
-// const int kPasserbyTriggerFrameInterval = 0x3FF;
+const int kPasserbyTriggerFrameInterval = 0x3FF;
 
-const int kPasserbyTriggerFrameInterval = 15;
 void PelrockEngine::frameTriggers() {
 	uint32 frameCount = _chrono->getFrameCount();
 	passerByAnim(frameCount);
+	handleFlightRoomFrame();
 }
 
 void PelrockEngine::passerByAnim(uint32 frameCount) {
@@ -2051,9 +2051,100 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		}
 		break;
 	}
+	case 51:
+	case 52:
+	case 53:
+	case 54:
+		initGodsSequences(_room->_currentRoomNumber);
+		break;
 	default:
 		break;
 	}
 }
 
+struct FlightRoomCfg {
+	int spriteIdx;
+	int appearFrames;
+	int spellFrames;
+};
+
+static const FlightRoomCfg kFlightRooms[] = {
+	{1, 31, 17}, // room 51
+	{0, 30, 13}, // room 52
+	{1, 30, 13}, // room 53
+	{1, 38, 11}, // room 54
+};
+
+void PelrockEngine::initGodsSequences(int roomNumber) {
+	int idx = roomNumber - 51;
+	_flightFrameCounter = 0;
+	_flightSorcererSpriteIdx = kFlightRooms[idx].spriteIdx;
+	_flightSorcererAppeared = false;
+	_flightSpellCast = false;
+	_flightSpellFrameCounter = 0;
+	_flightInBlockingAnim = false;
+
+	_room->disableSprite(_flightSorcererSpriteIdx);
+}
+
+void PelrockEngine::handleFlightRoomFrame() {
+	int room = _room->_currentRoomNumber;
+	if (room < 51 || room > 54)
+		return;
+
+	// Guard against reentrance from blocking animation loops calling renderScene
+	if (_flightInBlockingAnim)
+		return;
+
+	int idx = room - 51;
+
+	_flightFrameCounter++;
+
+	// Phase 1: NPC appearance at tick 64
+	if (!_flightSorcererAppeared && _flightFrameCounter >= 64) {
+		_flightSorcererAppeared = true;
+		_flightInBlockingAnim = true;
+		_room->findSpriteByIndex(_flightSorcererSpriteIdx)->animData[0].nframes = 1;
+		smokeAnimation(_flightSorcererSpriteIdx, false);
+		_flightInBlockingAnim = false;
+		return;
+	}
+
+	// Phase 2: spell trigger at tick 104 (64 + 40)
+	if (_flightSorcererAppeared && !_flightSpellCast && _flightFrameCounter >= 104) {
+		_flightSpellCast = true;
+		_flightSpellFrameCounter = 0;
+	}
+
+	// Phase 3: wait 40 ticks after spell trigger, then cast
+	if (_flightSpellCast) {
+		_flightSpellFrameCounter++;
+		if (_flightSpellFrameCounter >= 40) {
+			_flightInBlockingAnim = true;
+
+			int spellFrames = kFlightRooms[idx].spellFrames;
+			int framesDone = 0;
+			_room->findSpriteByIndex(_flightSorcererSpriteIdx)->animData[0].nframes = kFlightRooms[idx].spellFrames;
+			_room->findSpriteByIndex(_flightSorcererSpriteIdx)->animData[0].speed = 1;
+
+			while (!shouldQuit() && framesDone < spellFrames) {
+				_events->pollEvent();
+				if (renderScene(OVERLAY_NONE))
+					framesDone++;
+				_screen->update();
+				g_system->delayMillis(10);
+			}
+			_room->findSpriteByIndex(_flightSorcererSpriteIdx)->animData[0].nframes = 1;
+			smokeAnimation(-1, true);
+
+			_flightInBlockingAnim = false;
+
+			_alfredState.x = 294;
+			_alfredState.y = 387;
+			_alfredState.direction = ALFRED_UP;
+			setScreen(49, ALFRED_UP);
+		}
+	}
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 9bad4333d67..e27b40515a4 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -174,6 +174,14 @@ public:
 	byte *_compositeBuffer = nullptr; // Working composition buffer
 
 	bool _mouseDisabled = false;
+
+	int _flightFrameCounter = 0;
+	int _flightSorcererSpriteIdx = -1;
+	bool _flightSorcererAppeared = false;
+	bool _flightSpellCast = false;
+	int _flightSpellFrameCounter = 0;
+	bool _flightInBlockingAnim = false;
+
 	GameStateData *_state = new GameStateData();
 
 	SmallFont *_smallFont = nullptr;
@@ -237,6 +245,7 @@ public:
 	bool renderScene(int overlayMode = OVERLAY_NONE);
 	void mouseHoverForMap();
 	void frameTriggers();
+	void handleFlightRoomFrame();
 
 	void passerByAnim(uint32 frameCount);
 	void reflectionEffect(byte *buf, int x, int y, int width, int height);
@@ -246,6 +255,7 @@ public:
 
 	// Actions
 	void doExtraActions(int roomNumber);
+	void initGodsSequences(int roomNumber);
 	void addInventoryItem(int item);
 	void buyFromStore(HotSpot *hotspot, int stickerId);
 	void performActionTrigger(uint16 actionTrigger);


Commit: 943271a5e7d7d1ca537a688f96ecb55e58c486f1
    https://github.com/scummvm/scummvm/commit/943271a5e7d7d1ca537a688f96ecb55e58c486f1
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:12+02:00

Commit Message:
PELROCK: Stop frame counter during dialog

Changed paths:
    engines/pelrock/chrono.cpp
    engines/pelrock/chrono.h
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/extrascreens.cpp
    engines/pelrock/extrascreens.h
    engines/pelrock/menu.cpp


diff --git a/engines/pelrock/chrono.cpp b/engines/pelrock/chrono.cpp
index db0f7d8c10a..a020350332b 100644
--- a/engines/pelrock/chrono.cpp
+++ b/engines/pelrock/chrono.cpp
@@ -39,7 +39,9 @@ void ChronoManager::updateChrono() {
 
 	if ((currentTime - _lastTick) >= kTickMs / _speedMultiplier) {
 		_gameTick = true;
-		_frameCount++;
+		if(!_pauseCounter) {
+			_frameCount++;
+		}
 		_lastTick = currentTime;
 	} else {
 		_gameTick = false;
diff --git a/engines/pelrock/chrono.h b/engines/pelrock/chrono.h
index f7f67229545..673108c433b 100644
--- a/engines/pelrock/chrono.h
+++ b/engines/pelrock/chrono.h
@@ -52,6 +52,7 @@ public:
 
 	bool _gameTick = false;
 	bool countTextDown = false;
+	bool _pauseCounter = false;
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index fcd6b8503c0..e744f8a4a20 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -186,6 +186,8 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 
 	// Clear any existing click state
 	_events->_leftMouseClicked = false;
+	_dialogActive = true;
+	g_engine->_chrono->_pauseCounter = true;
 	int curPage = 0;
 
 	// Render loop - display text and wait for click
@@ -236,6 +238,8 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 	if (_curSprite != nullptr) {
 		_curSprite->isTalking = false;
 	}
+	_dialogActive = false;
+	g_engine->_chrono->_pauseCounter = false;
 	g_engine->_alfredState.setState(ALFRED_IDLE);
 }
 
@@ -250,6 +254,8 @@ void DialogManager::displayDialogue(Common::String text, byte speakerId) {
  */
 int DialogManager::selectChoice(Common::Array<Common::String> &choices, byte *compositeBuffer) {
 	_events->_leftMouseClicked = false;
+	_dialogActive = true;
+	g_engine->_chrono->_pauseCounter = true;
 
 	int overlayHeight = choices.size() * kChoiceHeight + 2;
 	int overlayY = 400 - overlayHeight;
@@ -265,6 +271,8 @@ int DialogManager::selectChoice(Common::Array<Common::String> &choices, byte *co
 			if (_events->_mouseClickY >= overlayY) {
 				int selectedIndex = (_events->_mouseClickY - overlayY - 2) / kChoiceHeight;
 				if (selectedIndex >= 0 && selectedIndex < (int)choices.size()) {
+					_dialogActive = false;
+					g_engine->_chrono->_pauseCounter = false;
 					return selectedIndex;
 				}
 			}
@@ -273,6 +281,8 @@ int DialogManager::selectChoice(Common::Array<Common::String> &choices, byte *co
 		g_system->delayMillis(10);
 	}
 
+	_dialogActive = false;
+	g_engine->_chrono->_pauseCounter = false;
 	return 0;
 }
 
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 532515fbcbd..0ebc5f0362b 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -116,6 +116,9 @@ public:
 
 	// When true, the goodbye option is suppressed for all conversations in the current room.
 	bool _goodbyeDisabled = false;
+
+	// True while a blocking dialog or conversation is on screen.
+	bool _dialogActive = false;
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/extrascreens.cpp b/engines/pelrock/extrascreens.cpp
index e47a39e2223..aa983833ae9 100644
--- a/engines/pelrock/extrascreens.cpp
+++ b/engines/pelrock/extrascreens.cpp
@@ -44,12 +44,13 @@ SpellBook::~SpellBook() {
 Spell *SpellBook::run() {
 	loadBackground();
 	g_engine->changeCursor(DEFAULT);
-	while (!g_engine->shouldQuit() && !_selectedSpell) {
+	bool exit = false;
+	while (!g_engine->shouldQuit() && !exit) {
 		_events->pollEvent();
 		drawScreen();
 		if (_events->_leftMouseClicked) {
 			_events->_leftMouseClicked = false;
-			checkMouse(_events->_mouseClickX, _events->_mouseClickY);
+			exit = checkMouse(_events->_mouseClickX, _events->_mouseClickY);
 		}
 		g_engine->_screen->markAllDirty();
 		g_engine->_screen->update();
@@ -152,24 +153,27 @@ void SpellBook::cleanup() {
 	g_engine->_screen->update();
 }
 
-void SpellBook::checkMouse(int x, int y) {
+bool SpellBook::checkMouse(int x, int y) {
 	// Check bookmarks
 	for (int i = 0; i < 13; i++) {
 		Common::Rect r = Common::Rect(_bookmarks[i].x, _bookmarks[i].y, _bookmarks[i].x + _bookmarks[i].w, _bookmarks[i].y + _bookmarks[i].h);
 		if (r.contains(x, y)) {
 			selectPage(_bookmarks[i].page);
-			return;
+			return false;
 		}
 	}
 
 	// Check text area
 	if (_spell == nullptr) {
-		return;
+		return true;
 	}
+
 	Common::Rect textArea = Common::Rect(321, 81, 321 + 140, 81 + (_spell->text.size() * 10));
 	if (textArea.contains(x, y)) {
 		_selectedSpell = _spell;
+		return true;
 	}
+	return false;
 }
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/extrascreens.h b/engines/pelrock/extrascreens.h
index c1fe52a1be5..6be65f30087 100644
--- a/engines/pelrock/extrascreens.h
+++ b/engines/pelrock/extrascreens.h
@@ -84,7 +84,7 @@ private:
 	void drawScreen();
 	void loadBackground();
 	void cleanup();
-	void checkMouse(int x, int y);
+	bool checkMouse(int x, int y);
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index f8f08ce9a14..acdf8d1ded0 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -145,7 +145,6 @@ void MenuManager::menuLoop() {
 
 void MenuManager::drawScreen() {
 	memcpy(_compositeBuffer, _mainMenu, 640 * 400);
-	// memset(_compositeBuffer, 0, 640 * 400);
 	if (showButtons)
 		drawButtons();
 


Commit: 8c0062a9542ddb2d04b79ead466a0ec48f24214b
    https://github.com/scummvm/scummvm/commit/8c0062a9542ddb2d04b79ead466a0ec48f24214b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:12+02:00

Commit Message:
PELROCK: Play sound when using amulet and during electric shock

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index bbf2365b81a..eddc72efe57 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -779,7 +779,10 @@ void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
 		}
 		if (billCount < 13) {
 			addInventoryItem(5); // 1000 pesetas bill
-			_dialog->say(_res->_ingameTexts[TEAPETECE_BUENRATO]);
+			bool sayLine = getRandomNumber(15) == 1; // 1 in 15 chance to say the line about not having more money
+			if(sayLine) {
+				_dialog->say(_res->_ingameTexts[TEAPETECE_BUENRATO]);
+			}
 		} else {
 			_dialog->say(_res->_ingameTexts[NOTENGOMASDINERO]);
 		}
@@ -1003,6 +1006,8 @@ void PelrockEngine::pickCables(HotSpot *hotspot) {
 	// electric shock
 	int prevX = _alfredState.x;
 	_alfredState.x -= 20;
+	// original incorrectly played door closing sound here
+	_sound->playSound("ELEC3ZZZ.SMP", 0);
 	_res->loadAlfredSpecialAnim(3);
 	_alfredState.animState = ALFRED_SPECIAL_ANIM;
 	waitForSpecialAnimation();
@@ -1083,6 +1088,7 @@ void PelrockEngine::useAmuletWithStatue(int inventoryObject, HotSpot *hotspot) {
 		HotSpot *statueHotspot = _room->findHotspotByExtra(347);
 		_currentHotspot = statueHotspot;
 		walkTo(statueHotspot->x + statueHotspot->w / 2, statueHotspot->y + statueHotspot->h);
+		_sound->playSound(_room->_roomSfx[0]); // Magic sound
 		animateStatuePaletteFade(false);
 		walkAndAction(statueHotspot, TALK);
 		waitForActionEnd();
@@ -1998,7 +2004,6 @@ void PelrockEngine::chooseCorrectDoor() {
 }
 
 void PelrockEngine::animateStatuePaletteFade(bool reverse) {
-	// Structure at JUEGO.EXE offset 0x4C700
 	struct StatuePaletteData {
 		uint16 x;           // 368
 		uint16 y;           // 148
@@ -2041,8 +2046,7 @@ void PelrockEngine::animateStatuePaletteFade(bool reverse) {
 	exeFile.close();
 
 	// Animation parameters
-	const int kNumFrames = 7; // 7 step updates total
-	const int kDelayMs = 200; // ~12 ticks at 60Hz (~200ms)
+	const int numFrames = 7; // 7 step updates total
 
 	// Get current palette
 	byte currentPalette[768];
@@ -2050,7 +2054,7 @@ void PelrockEngine::animateStatuePaletteFade(bool reverse) {
 
 	// Perform the fade animation
 	int frame = 0;
-	while (!shouldQuit() && frame <= kNumFrames) {
+	while (!shouldQuit() && frame <= numFrames) {
 		_events->pollEvent();
 
 		bool didRender = renderScene(OVERLAY_NONE);
@@ -2064,9 +2068,9 @@ void PelrockEngine::animateStatuePaletteFade(bool reverse) {
 				byte *dstColor = reverse ? paletteData.source[i] : paletteData.target[i];
 
 				// Linear interpolation (6-bit VGA values)
-				byte r6 = srcColor[0] + ((dstColor[0] - srcColor[0]) * frame) / kNumFrames;
-				byte g6 = srcColor[1] + ((dstColor[1] - srcColor[1]) * frame) / kNumFrames;
-				byte b6 = srcColor[2] + ((dstColor[2] - srcColor[2]) * frame) / kNumFrames;
+				byte r6 = srcColor[0] + ((dstColor[0] - srcColor[0]) * frame) / numFrames;
+				byte g6 = srcColor[1] + ((dstColor[1] - srcColor[1]) * frame) / numFrames;
+				byte b6 = srcColor[2] + ((dstColor[2] - srcColor[2]) * frame) / numFrames;
 
 				// Convert 6-bit VGA (0-63) to 8-bit (0-255) by shifting left 2 bits
 				currentPalette[paletteIndex * 3 + 0] = r6 << 2;
@@ -2082,6 +2086,7 @@ void PelrockEngine::animateStatuePaletteFade(bool reverse) {
 		g_system->delayMillis(10);
 	}
 }
+
 /**
  * In order to unlock the second part of the game, we need to ensure
  * we have all we need to solve the game once there
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 41f5c192547..819def28a65 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -499,13 +499,14 @@ struct AlfredSpecialAnimOffset {
 	uint32 offset;
 	int stride = 0;
 	uint32 size;
+	int speed = 2;
 
-	AlfredSpecialAnimOffset(int nF, int width, int height, int nBudas, int numAlfred, uint32 off, int loops)
-		: numFrames(nF), w(width), h(height), numBudas(nBudas), numAlfred(numAlfred), offset(off), loops(loops) {
-		AlfredSpecialAnimOffset(nF, width, height, nBudas, numAlfred, off, loops, width * height * nF);
+	AlfredSpecialAnimOffset(int nF, int width, int height, int nBudas, int numAlfred, uint32 off, int loops, int speed = 2)
+		: numFrames(nF), w(width), h(height), numBudas(nBudas), numAlfred(numAlfred), offset(off), loops(loops), speed(speed) {
+		AlfredSpecialAnimOffset(nF, width, height, nBudas, numAlfred, off, loops, speed, width * height * nF);
 	}
-	AlfredSpecialAnimOffset(int nF, int width, int height, int nBudas, int numAlfred, uint32 off, int loops, uint32 sz)
-		: numFrames(nF), w(width), h(height), numBudas(nBudas), numAlfred(numAlfred), offset(off), loops(loops), size(sz) {
+	AlfredSpecialAnimOffset(int nF, int width, int height, int nBudas, int numAlfred, uint32 off, int loops, int speed, uint32 sz)
+		: numFrames(nF), w(width), h(height), numBudas(nBudas), numAlfred(numAlfred), offset(off), loops(loops), size(sz), speed(speed) {
 		stride = w * h;
 	}
 	AlfredSpecialAnimOffset() {
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index d45fe547d7f..9955dba47d1 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -990,7 +990,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			// Scale special anim frame to Alfred size before drawing
 			drawSpriteToBuffer(_compositeBuffer, 640, frame, _alfredState.x, _alfredState.y - _res->_currentSpecialAnim->h, _res->_currentSpecialAnim->w, _res->_currentSpecialAnim->h, 255);
 		}
-		if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0) {
+		if (_chrono->getFrameCount() % _res->_currentSpecialAnim->speed == 0) {
 			_res->_currentSpecialAnim->curFrame++;
 
 			if (_res->_currentSpecialAnim->curFrame >= _res->_currentSpecialAnim->numFrames) {
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 9913571a9c0..d5723293b27 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -36,18 +36,10 @@ ResourceManager::ResourceManager(/* args */) {
 }
 
 const AlfredSpecialAnimOffset ResourceManager::alfredSpecialAnims[] = {
-	{
-		10,
-		51,
-		102,
-		1,
-		7,
-		559685,
-		1,
-	},                                   // 0 - READ BOOK
+	{10, 51, 102, 1, 7, 559685, 1},    // 0 - READ BOOK
 	{10, 51, 102, 1, 7, 578943, 1},      // 1 - READ RECIPE
 	{3, 45, 87, 0, 7, 37000, 1},         // 2 -  ELECTRIC SHOCK 1
-	{2, 82, 58, 0, 7, 53106, 20},        // 3 - ELECTRIC SHOCK 3
+	{2, 82, 58, 0, 7, 53106, 20, 1},        // 3 - ELECTRIC SHOCK 3
 	{3, 71, 110, 1, 2, 20724, 1, 62480}, // 4 - Throw
 	{14, 171, 107, 1, 7, 1556540, 1},    // 5 - crocodile
 	{12, 113, 103, 1, 7, 1583702, 1},    // 6 - exit through manhole
@@ -290,7 +282,7 @@ void ResourceManager::loadAlfredSpecialAnim(int numAnim, bool reverse) {
 	alfredFile.seek(anim.offset, SEEK_SET);
 	if (_currentSpecialAnim)
 		delete _currentSpecialAnim;
-	_currentSpecialAnim = new AlfredSpecialAnim(anim.numFrames, anim.w, anim.h, anim.numBudas, anim.offset, anim.loops, anim.size);
+	_currentSpecialAnim = new AlfredSpecialAnim(anim.numFrames, anim.w, anim.h, anim.numBudas, anim.offset, anim.loops, anim.size, anim.speed);
 	uint32 size = anim.size == 0 ? anim.numFrames * anim.w * anim.h : anim.size;
 	_currentSpecialAnim->animData = new byte[size];
 	if (anim.numBudas > 0) {
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 9bd5ec34147..f5f1feebf6f 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -136,10 +136,12 @@ struct AlfredSpecialAnim {
 	int curFrame = 0;
 	int curLoop = 0;
 	uint32 size = 0;
-	AlfredSpecialAnim(int nF, int width, int height, int nBudas, uint32 off, int loopCount, uint32 sz)
-		: numFrames(nF), w(width), h(height), loopCount(loopCount), size(sz) {
+	int speed = 2;
+	AlfredSpecialAnim(int nF, int width, int height, int nBudas, uint32 off, int loopCount, uint32 sz, int spd = 2)
+		: numFrames(nF), w(width), h(height), loopCount(loopCount), size(sz), speed(spd) {
 		stride = w * h;
 	}
+
 	~AlfredSpecialAnim() {
 		if (animData) {
 			delete[] animData;


Commit: ced34be28fb813ee52dd7172e0ba3421cd063f6f
    https://github.com/scummvm/scummvm/commit/ced34be28fb813ee52dd7172e0ba3421cd063f6f
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:12+02:00

Commit Message:
PELROCK: Gods/Sorcerer scenes in rooms 51, 52, 53, 54

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/chrono.h
    engines/pelrock/dialog.cpp
    engines/pelrock/extrascreens.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index eddc72efe57..0b9e9e0bbf1 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -662,7 +662,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		break;
 	case 325:
 		_state->setFlag(FLAG_ESQUELETO_RECONOCE, _state->getFlag(FLAG_ESQUELETO_RECONOCE) + 1);
-		if(_state->getFlag(FLAG_ESQUELETO_RECONOCE) == 2) {
+		if (_state->getFlag(FLAG_ESQUELETO_RECONOCE) == 2) {
 			_state->setCurrentRoot(49, 1, 0);
 		}
 		break;
@@ -778,9 +778,9 @@ void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
 			}
 		}
 		if (billCount < 13) {
-			addInventoryItem(5); // 1000 pesetas bill
+			addInventoryItem(5);                     // 1000 pesetas bill
 			bool sayLine = getRandomNumber(15) == 1; // 1 in 15 chance to say the line about not having more money
-			if(sayLine) {
+			if (sayLine) {
 				_dialog->say(_res->_ingameTexts[TEAPETECE_BUENRATO]);
 			}
 		} else {
@@ -1895,7 +1895,6 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		}
 		break;
 	case 88: {
-
 		SpellBook spellBook = SpellBook(_events, _res);
 		_res->loadAlfredSpecialAnim(0);
 		_alfredState.animState = ALFRED_SPECIAL_ANIM;
@@ -1905,25 +1904,48 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		if (spell) {
 			_alfredState.direction = ALFRED_LEFT;
 			_dialog->say(_res->_ingameTexts[DIOSHALCON + spell->page], 1);
-			if (spell->page == 12 && _room->_currentRoomNumber == 28) {
-				_graphics->clearScreen();
-				int waitFrames = 0;
-				// blank screen for half a second
-				while (!shouldQuit() && waitFrames < 20) {
-					_events->pollEvent();
-					_chrono->updateChrono();
-					if (_chrono->_gameTick) {
-						waitFrames++;
+			switch (_room->_currentRoomNumber) {
+			case 28: {
+				if (spell->page == 12) {
+					_graphics->clearScreen();
+					int waitFrames = 0;
+					// blank screen for half a second
+					while (!shouldQuit() && waitFrames < 20) {
+						_events->pollEvent();
+						_chrono->updateChrono();
+						if (_chrono->_gameTick) {
+							waitFrames++;
+						}
+						_screen->markAllDirty();
+						_screen->update();
+						g_system->delayMillis(10);
 					}
-					_screen->markAllDirty();
-					_screen->update();
-					g_system->delayMillis(10);
-				}
 
-				_alfredState.x = 145;
-				_alfredState.y = 312;
-				setScreen(25, ALFRED_RIGHT);
-				_dialog->say(_res->_ingameTexts[MENUDAAVENTURA]);
+					_alfredState.x = 145;
+					_alfredState.y = 312;
+					setScreen(25, ALFRED_RIGHT);
+					_dialog->say(_res->_ingameTexts[MENUDAAVENTURA]);
+				}
+				break;
+			}
+			case 51:
+			case 52:
+			case 53:
+			case 54: {
+				debug("Flight spell cast in room %d, spell page: %d", _room->_currentRoomNumber, spell->page);
+				int flightIndex = _room->_currentRoomNumber - 51;
+				debug("Correct page for this spell is = %d", kFlightRooms[flightIndex].spellPage);
+				if(_flightSorcererAppeared && !_flightInBlockingAnim && spell->page == kFlightRooms[flightIndex].spellPage) {
+					_state->setFlag(FLAG_COMO_ESTAN_LOS_DIOSES, _state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) | (1 << flightIndex));
+					debug("Flight spell successful, starting animation and updating state");
+					debug("Updated FLAG_COMO_ESTAN_LOS_DIOSES: %d", _state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES));
+					smokeAnimation(kFlightRooms[flightIndex].spriteIdx, true);
+					_room->addStickerToRoom(_room->_currentRoomNumber, 127 + flightIndex);
+				}
+				break;
+			}
+			default:
+				break;
 			}
 		}
 		break;
@@ -1976,7 +1998,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		byte response = (byte)getRandomNumber(12);
 		_dialog->say(_res->_ingameTexts[154 + response]);
 		break;
-		}
+	}
 	}
 }
 
diff --git a/engines/pelrock/chrono.h b/engines/pelrock/chrono.h
index 673108c433b..0e8b5f3b8b9 100644
--- a/engines/pelrock/chrono.h
+++ b/engines/pelrock/chrono.h
@@ -39,6 +39,7 @@ private:
 	uint32 _lastTick = 0;
 	byte _speedMultiplier = 1;
 	uint32 _frameCount = 0;
+	bool _pauseCounter = false;
 
 public:
 	ChronoManager();
@@ -46,13 +47,14 @@ public:
 	void updateChrono();
 	void changeSpeed();
 	void delay(uint32 ms);
+	inline void pauseCounter() { _pauseCounter = true; }
+	inline void resumeCounter() { _pauseCounter = false; }
 	uint32 getFrameCount() const {
 		return _frameCount;
 	}
 
 	bool _gameTick = false;
 	bool countTextDown = false;
-	bool _pauseCounter = false;
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index e744f8a4a20..96ef5091fa9 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -187,7 +187,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 	// Clear any existing click state
 	_events->_leftMouseClicked = false;
 	_dialogActive = true;
-	g_engine->_chrono->_pauseCounter = true;
+	g_engine->_chrono->pauseCounter();
 	int curPage = 0;
 
 	// Render loop - display text and wait for click
@@ -239,7 +239,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		_curSprite->isTalking = false;
 	}
 	_dialogActive = false;
-	g_engine->_chrono->_pauseCounter = false;
+	g_engine->_chrono->resumeCounter();
 	g_engine->_alfredState.setState(ALFRED_IDLE);
 }
 
@@ -255,7 +255,7 @@ void DialogManager::displayDialogue(Common::String text, byte speakerId) {
 int DialogManager::selectChoice(Common::Array<Common::String> &choices, byte *compositeBuffer) {
 	_events->_leftMouseClicked = false;
 	_dialogActive = true;
-	g_engine->_chrono->_pauseCounter = true;
+	g_engine->_chrono->pauseCounter();
 
 	int overlayHeight = choices.size() * kChoiceHeight + 2;
 	int overlayY = 400 - overlayHeight;
@@ -272,7 +272,7 @@ int DialogManager::selectChoice(Common::Array<Common::String> &choices, byte *co
 				int selectedIndex = (_events->_mouseClickY - overlayY - 2) / kChoiceHeight;
 				if (selectedIndex >= 0 && selectedIndex < (int)choices.size()) {
 					_dialogActive = false;
-					g_engine->_chrono->_pauseCounter = false;
+					g_engine->_chrono->resumeCounter();
 					return selectedIndex;
 				}
 			}
@@ -282,7 +282,7 @@ int DialogManager::selectChoice(Common::Array<Common::String> &choices, byte *co
 	}
 
 	_dialogActive = false;
-	g_engine->_chrono->_pauseCounter = false;
+	g_engine->_chrono->resumeCounter();
 	return 0;
 }
 
diff --git a/engines/pelrock/extrascreens.cpp b/engines/pelrock/extrascreens.cpp
index aa983833ae9..ca708c9aa72 100644
--- a/engines/pelrock/extrascreens.cpp
+++ b/engines/pelrock/extrascreens.cpp
@@ -69,6 +69,7 @@ void SpellBook::init() {
 }
 
 void SpellBook::selectPage(int page) {
+	debug("Selected spell page: %d", page);
 	_spell = new Spell();
 	_spell->page = page;
 	Common::File alfred7;
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 9955dba47d1..27e0deb3ad2 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -977,7 +977,6 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		break;
 	}
 	case ALFRED_SPECIAL_ANIM: {
-
 		byte *frame = new byte[_res->_currentSpecialAnim->stride * _res->_currentSpecialAnim->numFrames];
 		extractSingleFrame(_res->_currentSpecialAnim->animData,
 						   frame,
@@ -2008,21 +2007,21 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 	case 48: {
 		_dialog->_goodbyeDisabled = true;
 		if (_state->getFlag(FLAG_CORRECT_DOOR_CHOSEN) == true) {
-			if(_state->getFlag(FLAG_TRAMPILLA_ABIERTA) == true) {
+			if (_state->getFlag(FLAG_TRAMPILLA_ABIERTA) == true) {
 
-			}
-			else {
+			} else {
 				_dialog->say(_res->_ingameTexts[OHMISALVADOR]);
 				_dialog->say(_res->_ingameTexts[VOYPORTI_PRINCESA]);
 				walkAndAction(_room->findHotspotByExtra(634), TALK);
 				_room->addSticker(134);
-				//wait a few frames
+				// wait a few frames
 				int framesToWait = 0;
-				while(!shouldQuit() && framesToWait < 10) {
+				while (!shouldQuit() && framesToWait < 10) {
 					_events->pollEvent();
 
 					bool didRender = renderScene(OVERLAY_NONE);
-					if(didRender) framesToWait++;
+					if (didRender)
+						framesToWait++;
 					_screen->update();
 					g_system->delayMillis(10);
 				}
@@ -2062,19 +2061,6 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 	}
 }
 
-struct FlightRoomCfg {
-	int spriteIdx;
-	int appearFrames;
-	int spellFrames;
-};
-
-static const FlightRoomCfg kFlightRooms[] = {
-	{1, 31, 17}, // room 51
-	{0, 30, 13}, // room 52
-	{1, 30, 13}, // room 53
-	{1, 38, 11}, // room 54
-};
-
 void PelrockEngine::initGodsSequences(int roomNumber) {
 	int idx = roomNumber - 51;
 	_flightFrameCounter = 0;
@@ -2091,17 +2077,25 @@ void PelrockEngine::handleFlightRoomFrame() {
 	int room = _room->_currentRoomNumber;
 	if (room < 51 || room > 54)
 		return;
+	int idx = room - 51;
+
+	if (_state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) & (1 << idx)) {
+		// Gods are already defeated in this room, no need to run sequence
+		return;
+	}
 
 	// Guard against reentrance from blocking animation loops calling renderScene
 	if (_flightInBlockingAnim)
 		return;
 
-	int idx = room - 51;
-
+	if (_alfredState.animState != ALFRED_IDLE) {
+		return;
+	}
 	_flightFrameCounter++;
 
 	// Phase 1: NPC appearance at tick 64
 	if (!_flightSorcererAppeared && _flightFrameCounter >= 64) {
+		_sound->playSound(_room->_roomSfx[0]);
 		_flightSorcererAppeared = true;
 		_flightInBlockingAnim = true;
 		_room->findSpriteByIndex(_flightSorcererSpriteIdx)->animData[0].nframes = 1;
@@ -2112,21 +2106,28 @@ void PelrockEngine::handleFlightRoomFrame() {
 
 	// Phase 2: spell trigger at tick 104 (64 + 40)
 	if (_flightSorcererAppeared && !_flightSpellCast && _flightFrameCounter >= 104) {
+		debug("spell cast triggered for room %d at frame %d", room, _flightFrameCounter);
 		_flightSpellCast = true;
 		_flightSpellFrameCounter = 0;
 	}
 
 	// Phase 3: wait 40 ticks after spell trigger, then cast
 	if (_flightSpellCast) {
+		if (_alfredState.animState != ALFRED_IDLE) {
+			debug("blocking animation active at frame %d, delaying spell cast", _flightFrameCounter);
+			return;
+		}
+		debug("in spell cast phase for room %d at frame %d", room, _flightFrameCounter);
 		_flightSpellFrameCounter++;
-		if (_flightSpellFrameCounter >= 40) {
+		if (_flightSpellFrameCounter >= 40 && !(_state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) & (1 << idx))) {
+			debug("spell cast animation starting for room %d at frame %d, flag is %d", room, _flightFrameCounter, _state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES));
 			_flightInBlockingAnim = true;
 
 			int spellFrames = kFlightRooms[idx].spellFrames;
 			int framesDone = 0;
 			_room->findSpriteByIndex(_flightSorcererSpriteIdx)->animData[0].nframes = kFlightRooms[idx].spellFrames;
 			_room->findSpriteByIndex(_flightSorcererSpriteIdx)->animData[0].speed = 1;
-
+			_sound->playSound(_room->_roomSfx[1]);
 			while (!shouldQuit() && framesDone < spellFrames) {
 				_events->pollEvent();
 				if (renderScene(OVERLAY_NONE))
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 69851aa4c47..10203030f87 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -225,9 +225,7 @@ void RoomManager::disableSprite(byte spriteIndex, int persist) {
 }
 
 void RoomManager::disableSprite(byte roomNumber, byte spriteIndex, int persist) {
-	debug("Disabling sprite %d in room %d with persist %d", spriteIndex, roomNumber, persist);
 	if (roomNumber == _currentRoomNumber && persist & PERSIST_TEMP) {
-		debug("Disabling sprite LOCALLY %d in room %d with persist %d", spriteIndex, roomNumber, persist);
 		// Search by sprite.index, not by array position: sortAnimsByZOrder reorders the
 		// array every frame so a raw array index is unreliable.
 		for (uint i = 0; i < _currentRoomAnims.size(); i++) {
@@ -249,7 +247,6 @@ void RoomManager::enableSprite(byte spriteIndex, byte zOrder, int persist) {
 void RoomManager::enableSprite(byte roomNumber, byte spriteIndex, byte zOrder, int persist) {
 	for(int i =0; i < g_engine->_state->spriteChanges[roomNumber].size(); i++) {
 		if (g_engine->_state->spriteChanges[roomNumber][i].spriteIndex == spriteIndex) {
-			debug("Removing pending sprite change for sprite %d in room %d", spriteIndex, roomNumber);
 			g_engine->_state->spriteChanges[roomNumber].remove_at(i);
 			break;
 		}
@@ -265,7 +262,6 @@ void RoomManager::enableSprite(byte roomNumber, byte spriteIndex, byte zOrder, i
 		}
 	}
 	if (persist & PERSIST_PERM) {
-		debug("Enabling sprite %d in room %d with zOrder %d and persist %d", spriteIndex, roomNumber, zOrder, persist);
 		g_engine->_state->spriteChanges[roomNumber].push_back({roomNumber, spriteIndex, zOrder});
 	}
 }
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index f5f1feebf6f..60e0caa505d 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -642,6 +642,22 @@ struct SaveGameData {
 	GameStateData *gameState = nullptr;
 };
 
+
+struct FlightRoomCfg {
+	int roomNumber;
+	int spriteIdx;
+	int appearFrames;
+	int spellFrames;
+	int spellPage;
+};
+
+static const FlightRoomCfg kFlightRooms[] = {
+	{51, 1, 31, 17, 8}, // room 51
+	{52, 0, 30, 13, 4}, // room 52
+	{53, 1, 30, 13, 0}, // room 53
+	{54, 1, 38, 11, 2}, // room 54
+};
+
 } // End of namespace Pelrock
 
 #endif


Commit: f26de9e5e1c9fcc80f86a324fe9dce6c902a7925
    https://github.com/scummvm/scummvm/commit/f26de9e5e1c9fcc80f86a324fe9dce6c902a7925
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:12+02:00

Commit Message:
PELROCK: Implements missing useless item combinations

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 0b9e9e0bbf1..b910376a5d8 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -208,6 +208,7 @@ const CombinationEntry combinationTable[] = {
 	{85, 506, &PelrockEngine::magicFormula},
 	{86, 506, &PelrockEngine::magicFormula},
 	{81, 506, &PelrockEngine::magicFormula},
+	{99, 506, &PelrockEngine::useWigWithPot},
 
 	{76, 617, &PelrockEngine::usePumpkinWithPond},
 
@@ -1263,7 +1264,6 @@ void PelrockEngine::pickUpBook(int i) {
 }
 
 void PelrockEngine::pickUpChainsaw(HotSpot *hotspot) {
-
 	_room->addSticker(99);
 }
 
@@ -1708,6 +1708,11 @@ void PelrockEngine::useWaterOnFakeStone(int inventoryObject, HotSpot *hotspot) {
 	}
 }
 
+void PelrockEngine::useWigWithPot(int inventoryObject, HotSpot *hotspot) {
+	_dialog->say(_res->_ingameTexts[NOERAAUTENTICO]);
+	_state->removeInventoryItem(99);
+}
+
 void PelrockEngine::magicFormula(int inventoryObject, HotSpot *hotspot) {
 	_state->removeInventoryItem(inventoryObject);
 	_state->setFlag(FLAG_FORMULA_MAGICA, _state->getFlag(FLAG_FORMULA_MAGICA) + 1);
@@ -1965,12 +1970,27 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		break;
 	case 108:
 	case 109: {
+
 		if (_state->hasInventoryItem(110) == true) {
-			_state->removeInventoryItem(110);
-			_state->removeInventoryItem(109);
-			_state->removeInventoryItem(108);
-			addInventoryItem(83);
-			_dialog->say(_res->_ingameTexts[MUNECO_ARREGLADO]);
+			if(_state->hasInventoryItem(109) == true && _state->hasInventoryItem(108) == true) {
+					_state->removeInventoryItem(110);
+					_state->removeInventoryItem(109);
+					_state->removeInventoryItem(108);
+					addInventoryItem(83);
+					_dialog->say(_res->_ingameTexts[MUNECO_ARREGLADO]);
+					return;
+			}
+			else if(_state->hasInventoryItem(109) == true) {
+				_dialog->say(_res->_ingameTexts[NOTENGOPARCHES]);
+
+			}
+			else if(_state->hasInventoryItem(108) == true) {
+				_dialog->say(_res->_ingameTexts[NOTENGOPEGAMENTO]);
+			}
+		}
+		else {
+			byte response = (byte)getRandomNumber(12);
+			_dialog->say(_res->_ingameTexts[154 + response]);
 		}
 		break;
 	}
@@ -1994,6 +2014,10 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		chooseCorrectDoor();
 		break;
 	}
+	case 87: {
+		_dialog->say(_res->_ingameTexts[NECESITOGASOLINA]);
+		break;
+	}
 	default: {
 		byte response = (byte)getRandomNumber(12);
 		_dialog->say(_res->_ingameTexts[154 + response]);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 27e0deb3ad2..b17f56082c3 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -2088,7 +2088,7 @@ void PelrockEngine::handleFlightRoomFrame() {
 	if (_flightInBlockingAnim)
 		return;
 
-	if (_alfredState.animState != ALFRED_IDLE) {
+	if (_alfredState.animState != ALFRED_IDLE || _actionPopupState.isActive) {
 		return;
 	}
 	_flightFrameCounter++;
@@ -2113,10 +2113,6 @@ void PelrockEngine::handleFlightRoomFrame() {
 
 	// Phase 3: wait 40 ticks after spell trigger, then cast
 	if (_flightSpellCast) {
-		if (_alfredState.animState != ALFRED_IDLE) {
-			debug("blocking animation active at frame %d, delaying spell cast", _flightFrameCounter);
-			return;
-		}
 		debug("in spell cast phase for room %d at frame %d", room, _flightFrameCounter);
 		_flightSpellFrameCounter++;
 		if (_flightSpellFrameCounter >= 40 && !(_state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) & (1 << idx))) {
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index e27b40515a4..183d773b2bd 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -368,6 +368,7 @@ public:
 	void openPyramidDoor(HotSpot *hotspot);
 	void usePumpkinWithPond(int inventoryObject, HotSpot *hotspot);
 	void useWaterOnFakeStone(int inventoryObject, HotSpot *hotspot);
+	void useWigWithPot(int inventoryObject, HotSpot *hotspot);
 	void magicFormula(int inventoryObject, HotSpot *hotspot);
 	void smokeAnimation(int spriteIndex, bool hide = true);
 	void openArchitectDoor(HotSpot *hotspot);


Commit: 63179eff5eeb843c01d6e88aa6c3f905237fcacd
    https://github.com/scummvm/scummvm/commit/63179eff5eeb843c01d6e88aa6c3f905237fcacd
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:13+02:00

Commit Message:
PELROCK: Loads CD Player screen

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/extrascreens.cpp
    engines/pelrock/extrascreens.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index b910376a5d8..dffaa63087b 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -2018,6 +2018,11 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		_dialog->say(_res->_ingameTexts[NECESITOGASOLINA]);
 		break;
 	}
+	case 95: {
+		CDPlayer player = CDPlayer(_events, _res, _sound);
+		player.run();
+		break;
+	}
 	default: {
 		byte response = (byte)getRandomNumber(12);
 		_dialog->say(_res->_ingameTexts[154 + response]);
diff --git a/engines/pelrock/extrascreens.cpp b/engines/pelrock/extrascreens.cpp
index ca708c9aa72..055604dd404 100644
--- a/engines/pelrock/extrascreens.cpp
+++ b/engines/pelrock/extrascreens.cpp
@@ -123,7 +123,6 @@ void SpellBook::drawScreen() {
 		g_engine->_graphics->drawColoredTexts(_compositeScreen, _spell->text, textX, textY, 640, 0, g_engine->_smallFont);
 	}
 
-	drawPaletteSquares(_compositeScreen, _palette);
 	memcpy(g_engine->_screen->getPixels(), _compositeScreen, 640 * 400);
 	if (_spell != nullptr) {
 		g_engine->_graphics->drawColoredTexts(g_engine->_screen, _spell->text, textX, textY, 640, 0, g_engine->_smallFont);
@@ -177,4 +176,92 @@ bool SpellBook::checkMouse(int x, int y) {
 	return false;
 }
 
+
+CDPlayer::CDPlayer(PelrockEventManager *eventMan, ResourceManager *res, SoundManager *sound) : _events(eventMan), _res(res), _sound(sound) {
+	init();
+}
+
+CDPlayer::~CDPlayer() {
+	cleanup();
+}
+
+void CDPlayer::run() {
+	loadBackground();
+	loadControls();
+	g_engine->changeCursor(DEFAULT);
+
+	while(!g_engine->shouldQuit()) {
+		_events->pollEvent();
+		drawScreen();
+		g_engine->_screen->markAllDirty();
+		g_engine->_screen->update();
+		g_system->delayMillis(10);
+	}
+	memset(g_engine->_screen->getPixels(), 0, 640 * 400);
+	// Restore room palette
+	g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
+}
+
+void CDPlayer::init() {
+	_compositeScreen = new byte[640 * 400];
+}
+
+void CDPlayer::drawScreen() {
+	memcpy(_compositeScreen, _backgroundScreen, 640 * 400);
+	drawSpriteToBuffer(_compositeScreen, 640, _controls, 0, 0, 213, 72, 207);
+	memcpy(g_engine->_screen->getPixels(), _compositeScreen, 640 * 400);
+}
+
+void CDPlayer::loadBackground() {
+	_backgroundScreen = new byte[640 * 400];
+	_palette = new byte[768];
+	_res->getExtraScreen(10, _backgroundScreen, _palette);
+	g_system->getPaletteManager()->setPalette(_palette, 0, 256);
+}
+
+void CDPlayer::cleanup() {
+	if (_backgroundScreen) {
+		delete[] _backgroundScreen;
+		_backgroundScreen = nullptr;
+	}
+
+	if (_palette) {
+		delete[] _palette;
+		_palette = nullptr;
+	}
+
+	g_engine->_screen->markAllDirty();
+	g_engine->_screen->update();
+}
+
+void CDPlayer::checkMouse(int x, int y) {
+
+}
+
+void CDPlayer::loadControls() {
+	_controls = new byte[213*72];
+	Common::File alfred7;
+	if (!alfred7.open("ALFRED.7")) {
+		return;
+	}
+	alfred7.seek(2214760, SEEK_SET);
+	byte *compressedData = nullptr;
+	size_t outSize = 0;
+	readUntilBuda(&alfred7, 2214760, compressedData, outSize);
+	byte *rawData = nullptr;
+
+	size_t decompressedSize = rleDecompress(compressedData, outSize, 0, 0, &rawData, true);
+
+	debug("Decompressed CD player controls: %d bytes", decompressedSize);
+	Common::copy(rawData, rawData + 213 * 72, _controls);
+	// for(int i = 0; i < 5; i++) {
+	// 	for (int j = 0; j < 2; j++) {
+	// 		buttons[i][j] = _controls + (i * 2 + j) * 213 * 72;
+	// 	}
+	// }
+	alfred7.close();
+	delete[] compressedData;
+	delete[] rawData;
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/extrascreens.h b/engines/pelrock/extrascreens.h
index 6be65f30087..da1624e4a1e 100644
--- a/engines/pelrock/extrascreens.h
+++ b/engines/pelrock/extrascreens.h
@@ -87,6 +87,56 @@ private:
 	bool checkMouse(int x, int y);
 };
 
+
+class CDPlayer {
+
+public:
+	CDPlayer(PelrockEventManager *eventMan, ResourceManager *res, SoundManager *sound);
+	~CDPlayer();
+
+	void run();
+
+private:
+	void init();
+	void drawScreen();
+	void loadBackground();
+	void loadControls();
+	void checkMouse(int x, int y);
+	void cleanup();
+	ResourceManager *_res;
+	SoundManager *_sound;
+	PelrockEventManager *_events;
+	byte *_backgroundScreen;
+	byte *_compositeScreen;
+	byte *_palette;
+	byte *_controls;
+	byte *buttons[5][2];
+};
+
+
+class BackgroundBook {
+
+public:
+	BackgroundBook(PelrockEventManager *eventMan, ResourceManager *res);
+	~BackgroundBook();
+
+	void run();
+
+private:
+	void init();
+	void drawScreen();
+	void loadBackground();
+	void loadControls();
+	void checkMouse(int x, int y);
+	void cleanup();
+	PelrockEventManager *_events;
+	ResourceManager *_res;
+	byte *_backgroundScreen;
+	byte *_compositeScreen;
+	byte *_palette;
+	byte *_controls;
+};
+
 } // End of namespace Pelrock
 
 #endif // PELROCK_EXTRASCREENS_H


Commit: 14945d2f9c14166908b84bda6071a1b93a7f1572
    https://github.com/scummvm/scummvm/commit/14945d2f9c14166908b84bda6071a1b93a7f1572
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:13+02:00

Commit Message:
PELROCK: CD Player (except pause)

Changed paths:
    engines/pelrock/extrascreens.cpp
    engines/pelrock/extrascreens.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h


diff --git a/engines/pelrock/extrascreens.cpp b/engines/pelrock/extrascreens.cpp
index 055604dd404..2fe16abdc6c 100644
--- a/engines/pelrock/extrascreens.cpp
+++ b/engines/pelrock/extrascreens.cpp
@@ -176,7 +176,6 @@ bool SpellBook::checkMouse(int x, int y) {
 	return false;
 }
 
-
 CDPlayer::CDPlayer(PelrockEventManager *eventMan, ResourceManager *res, SoundManager *sound) : _events(eventMan), _res(res), _sound(sound) {
 	init();
 }
@@ -186,12 +185,18 @@ CDPlayer::~CDPlayer() {
 }
 
 void CDPlayer::run() {
-	loadBackground();
-	loadControls();
+
 	g_engine->changeCursor(DEFAULT);
 
-	while(!g_engine->shouldQuit()) {
+	while (!g_engine->shouldQuit()) {
 		_events->pollEvent();
+		checkMouse(_events->_mouseX, _events->_mouseY);
+
+		if (_events->_rightMouseClicked) {
+			_events->_rightMouseClicked = false;
+			break;
+		}
+
 		drawScreen();
 		g_engine->_screen->markAllDirty();
 		g_engine->_screen->update();
@@ -204,12 +209,45 @@ void CDPlayer::run() {
 
 void CDPlayer::init() {
 	_compositeScreen = new byte[640 * 400];
+	loadBackground();
+	loadControls();
+	loadTrackNames();
+}
+
+void CDPlayer::loadTrackNames() {
+	Common::File juegoFile;
+	if (!juegoFile.open("JUEGO.EXE")) {
+		return;
+	}
+	juegoFile.seek(0x049893, SEEK_SET);
+
+	for (int i = 0; i < 31; i++) {
+		trackNames[i] = juegoFile.readString(0, 30);
+		debug("Loaded track name: %s", trackNames[i].c_str());
+	}
+
+	juegoFile.close();
 }
 
 void CDPlayer::drawScreen() {
 	memcpy(_compositeScreen, _backgroundScreen, 640 * 400);
-	drawSpriteToBuffer(_compositeScreen, 640, _controls, 0, 0, 213, 72, 207);
+	drawSpriteToBuffer(_compositeScreen, 640, _controls, 1, 1, 213, 72, 207);
+
+	drawButtons();
+
 	memcpy(g_engine->_screen->getPixels(), _compositeScreen, 640 * 400);
+	g_engine->_smallFont->drawString(g_engine->_screen, trackNames[_selectedTrack - 2], 26, 17, 640, 255, Graphics::kTextAlignLeft);
+}
+
+void CDPlayer::drawButtons() {
+
+	for (int i = 0; i < 5; i++) {
+		if (_selectedButton == i) {
+			drawSpriteToBuffer(_compositeScreen, 640, buttons[i][1], _buttonRects[i].left, _buttonRects[i].top, _buttonRects[i].width(), _buttonRects[i].height(), 207);
+		} else {
+			drawSpriteToBuffer(_compositeScreen, 640, buttons[i][0], _buttonRects[i].left, _buttonRects[i].top, _buttonRects[i].width(), _buttonRects[i].height(), 207);
+		}
+	}
 }
 
 void CDPlayer::loadBackground() {
@@ -230,16 +268,64 @@ void CDPlayer::cleanup() {
 		_palette = nullptr;
 	}
 
+	for (int i = 0; i < 5; i++) {
+		delete[] buttons[i][0];
+		delete[] buttons[i][1];
+	}
+
 	g_engine->_screen->markAllDirty();
 	g_engine->_screen->update();
 }
 
 void CDPlayer::checkMouse(int x, int y) {
+	if (_events->_leftMouseClicked) {
+		switch (_selectedButton) {
+		case STOP_BUTTON:
+			_sound->stopMusic();
+			break;
+		case PAUSE_BUTTON:
+			_sound->pauseMusic();
+			break;
+		case PLAY_BUTTON:
+			_sound->stopMusic();
+			_sound->playMusicTrack(_selectedTrack, true);
+			break;
+		case PREVIOUS_BUTTON:
+			if (_selectedTrack > 2) {
+				_selectedTrack--;
+			}
+			break;
+		case NEXT_BUTTON:
+			if (_selectedTrack < 32) {
+				_selectedTrack++;
+			}
+			break;
+		default:
+			break;
+		}
+		_selectedButton = NO_BUTTON;
+		_events->_leftMouseClicked = false;
+	}
 
+	if (_events->_leftMouseButton != 0 && _selectedButton == NO_BUTTON) {
+		_selectedButton = isButtonClicked(_events->_mouseX, _events->_mouseY);
+		if(_selectedButton != NO_BUTTON) {
+			_sound->playSound("11ZZZZZZ.SMP", 0);
+		}
+	}
+}
+
+CDPlayer::CDControls CDPlayer::isButtonClicked(int x, int y) {
+	for (int i = 0; i < 5; i++) {
+		if (_buttonRects[i].contains(x, y)) {
+			return static_cast<CDControls>(i);
+		}
+	}
+	return NO_BUTTON;
 }
 
 void CDPlayer::loadControls() {
-	_controls = new byte[213*72];
+	_controls = new byte[213 * 72];
 	Common::File alfred7;
 	if (!alfred7.open("ALFRED.7")) {
 		return;
@@ -253,12 +339,17 @@ void CDPlayer::loadControls() {
 	size_t decompressedSize = rleDecompress(compressedData, outSize, 0, 0, &rawData, true);
 
 	debug("Decompressed CD player controls: %d bytes", decompressedSize);
-	Common::copy(rawData, rawData + 213 * 72, _controls);
-	// for(int i = 0; i < 5; i++) {
-	// 	for (int j = 0; j < 2; j++) {
-	// 		buttons[i][j] = _controls + (i * 2 + j) * 213 * 72;
-	// 	}
-	// }
+	uint32 pos = 213 * 72;
+	Common::copy(rawData, rawData + pos, _controls);
+	for (int i = 0; i < 5; i++) {
+		for (int j = 0; j < 2; j++) {
+			int w = _buttonRects[i].width();
+			int h = _buttonRects[i].height();
+			buttons[i][j] = new byte[w * h];
+			Common::copy(rawData + pos, rawData + pos + w * h, buttons[i][j]);
+			pos += w * h;
+		}
+	}
 	alfred7.close();
 	delete[] compressedData;
 	delete[] rawData;
diff --git a/engines/pelrock/extrascreens.h b/engines/pelrock/extrascreens.h
index da1624e4a1e..285bbb51e72 100644
--- a/engines/pelrock/extrascreens.h
+++ b/engines/pelrock/extrascreens.h
@@ -90,6 +90,15 @@ private:
 
 class CDPlayer {
 
+enum CDControls {
+	STOP_BUTTON,
+	PAUSE_BUTTON,
+	PLAY_BUTTON,
+	PREVIOUS_BUTTON,
+	NEXT_BUTTON,
+	NO_BUTTON
+};
+
 public:
 	CDPlayer(PelrockEventManager *eventMan, ResourceManager *res, SoundManager *sound);
 	~CDPlayer();
@@ -98,11 +107,14 @@ public:
 
 private:
 	void init();
+	void loadTrackNames();
 	void drawScreen();
+	void drawButtons();
 	void loadBackground();
 	void loadControls();
 	void checkMouse(int x, int y);
 	void cleanup();
+	CDControls isButtonClicked(int x, int y);
 	ResourceManager *_res;
 	SoundManager *_sound;
 	PelrockEventManager *_events;
@@ -111,6 +123,17 @@ private:
 	byte *_palette;
 	byte *_controls;
 	byte *buttons[5][2];
+	Common::String trackNames[31];
+	Common::Rect _buttonRects[5] = {
+		Common::Rect(Common::Point(17, 46), 37, 26), // Stop
+		Common::Rect(Common::Point(57, 48), 33, 23), // Pause
+		Common::Rect(Common::Point(92, 44), 34, 28), // Play
+		Common::Rect(Common::Point(128, 45), 38, 24), // Previous
+		Common::Rect(Common::Point(168, 44), 41, 28) // Next
+	};
+	int _selectedTrack = 2;
+	CDControls _selectedButton = NO_BUTTON;
+
 };
 
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index b17f56082c3..fb75db39f6b 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -159,7 +159,8 @@ void PelrockEngine::init() {
 		// setScreen(22, ALFRED_DOWN);
 		// setScreen(41, ALFRED_DOWN);
 		// setScreen(43, ALFRED_DOWN);
-		setScreen(46, ALFRED_RIGHT);
+		// setScreen(46, ALFRED_RIGHT);
+		setScreen(0, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
@@ -1567,26 +1568,31 @@ void PelrockEngine::gameLoop() {
 	_events->pollEvent();
 	checkMouse();
 
-	// if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_m) {
-	// 	travelToEgypt();
-	// 	_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
-	// }
-	// if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_n) {
-	// 	loadExtraScreenAndPresent(10);
-	// 	_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
-	// }
-	// if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_p) {
-	// 	antiPiracyEffect();
-	// 	_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
-	// }
-	// if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_s) {
-	// 	SpellBook spellBook(_events, _res);
-	// 	Spell *selectedSpell = spellBook.run();
-	// 	if (selectedSpell != nullptr) {
-	// 		_dialog->sayAlfred(selectedSpell->text);
-	// 	}
-	// 	_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
-	// }
+	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_m) {
+		travelToEgypt();
+		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
+	}
+	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_n) {
+		loadExtraScreenAndPresent(10);
+		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
+	}
+	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_p) {
+		antiPiracyEffect();
+		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
+	}
+	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_s) {
+		SpellBook spellBook(_events, _res);
+		Spell *selectedSpell = spellBook.run();
+		if (selectedSpell != nullptr) {
+			_dialog->sayAlfred(selectedSpell->text);
+		}
+		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
+	}
+	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_c) {
+		CDPlayer cdPlayer(_events, _res, _sound);
+		cdPlayer.run();
+		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
+	}
 
 	renderScene();
 
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index cee1f3675ca..4eef53ddf46 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -208,9 +208,20 @@ bool SoundManager::isPlaying(int channel) const {
 }
 
 void SoundManager::stopMusic() {
+	_isPaused = false;
 	g_system->getAudioCDManager()->stop();
 }
 
+void SoundManager::pauseMusic() {
+	uint32 elapsed = g_system->getMillis() - _cdPlayStartTime;
+	uint32 elapsedFrames = elapsed * 75 / 1000;
+	_cdTrackStart += elapsedFrames; // advance the start offset
+	if (_cdTrackDuration > 0)
+    	_cdTrackDuration -= elapsedFrames; // shrink remaining duration
+	g_system->getAudioCDManager()->stop();
+	_isPaused = true;
+}
+
 bool SoundManager::isMusicPlaying() {
 	return g_system->getAudioCDManager()->isPlaying();
 }
@@ -221,8 +232,11 @@ void SoundManager::playMusicTrack(int trackNumber, bool loop) {
 		return;
 	}
 	_currentMusicTrack = trackNumber;
+	_cdTrackStart = 0;
+	_cdTrackDuration = 0;
+	_cdPlayStartTime = g_system->getMillis();
 	g_system->getAudioCDManager()->stop();
-	g_system->getAudioCDManager()->play(trackNumber, loop ? -1 : 0, 0, 0);
+	g_system->getAudioCDManager()->play(trackNumber, loop ? -1 : 0, _cdTrackStart, _cdTrackDuration);
 }
 
 void SoundManager::loadSoundIndex() {
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index bd3f27ed6db..631732ab58a 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -162,17 +162,16 @@ public:
 	void playSound(byte *soundData, uint32 size);
 	void stopAllSounds();
 	void stopSound(int channel);
-	void stopMusic();
-	bool isMusicPlaying();
 	void setVolume(int volume);
 	bool isPlaying() const;
 	bool isPlaying(int channel) const;
-	void playMusicTrack(int trackNumber, bool loop = true);
-	bool isMusicPlaying() const {
-		return _isMusicPlaying;
-	}
 	void loadSoundIndex();
 
+	bool isMusicPlaying();
+	void playMusicTrack(int trackNumber, bool loop = true);
+	void stopMusic();
+	void pauseMusic();
+
 	/**
 	 * Check if ambient sound should play this frame.
 	 * @param frameCount Current game frame counter
@@ -197,6 +196,11 @@ private:
 	Audio::SoundHandle _sfxHandles[kMaxChannels];
 	Common::HashMap<Common::String, SonidoFile> _soundMap;
 
+
+	uint32 _cdTrackStart = 0;
+	uint32 _cdTrackDuration;
+	uint32 _cdPlayStartTime; // time at the moment of calling play()
+	bool _isPaused = false;
 };
 
 } // End of namespace Pelrock


Commit: 3f7f9b75f0028887261cd9f4d795a519b1725556
    https://github.com/scummvm/scummvm/commit/3f7f9b75f0028887261cd9f4d795a519b1725556
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:13+02:00

Commit Message:
PELROCK: Implements background book

Changed paths:
    engines/pelrock/extrascreens.cpp
    engines/pelrock/extrascreens.h
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h


diff --git a/engines/pelrock/extrascreens.cpp b/engines/pelrock/extrascreens.cpp
index 2fe16abdc6c..446a8e986d8 100644
--- a/engines/pelrock/extrascreens.cpp
+++ b/engines/pelrock/extrascreens.cpp
@@ -205,6 +205,8 @@ void CDPlayer::run() {
 	memset(g_engine->_screen->getPixels(), 0, 640 * 400);
 	// Restore room palette
 	g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
+	_sound->stopMusic();
+	_sound->playMusicTrack(g_engine->_room->_musicTrack, true);
 }
 
 void CDPlayer::init() {
@@ -287,8 +289,13 @@ void CDPlayer::checkMouse(int x, int y) {
 			_sound->pauseMusic();
 			break;
 		case PLAY_BUTTON:
-			_sound->stopMusic();
-			_sound->playMusicTrack(_selectedTrack, true);
+			if(_sound->_isPaused && _sound->_currentMusicTrack == _selectedTrack) {
+				_sound->playMusicTrack(_selectedTrack, true);
+			}
+			else {
+				_sound->stopMusic();
+				_sound->playMusicTrack(_selectedTrack, true);
+			}
 			break;
 		case PREVIOUS_BUTTON:
 			if (_selectedTrack > 2) {
@@ -355,4 +362,185 @@ void CDPlayer::loadControls() {
 	delete[] rawData;
 }
 
+BackgroundBook::BackgroundBook(PelrockEventManager *eventMan, ResourceManager *res) : _events(eventMan), _res(res) {
+	init();
+}
+
+BackgroundBook::~BackgroundBook() {
+	cleanup();
+}
+
+void BackgroundBook::run() {
+	g_engine->changeCursor(DEFAULT);
+	while (!g_engine->shouldQuit()) {
+		_events->pollEvent();
+		checkMouse(_events->_mouseX, _events->_mouseY);
+
+		if (_events->_rightMouseClicked) {
+			_events->_rightMouseClicked = false;
+			break;
+		}
+
+		drawScreen();
+		g_engine->_screen->markAllDirty();
+		g_engine->_screen->update();
+		g_system->delayMillis(10);
+	}
+	memset(g_engine->_screen->getPixels(), 0, 640 * 400);
+	// Restore room palette
+	g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
+}
+
+void BackgroundBook::init() {
+	_compositeScreen = new byte[640 * 400];
+	loadBackground();
+	loadButtons();
+	loadRoomNames();
+}
+
+void BackgroundBook::checkMouse(int x, int y) {
+	if (_events->_leftMouseClicked) {
+		switch (_selectedButton) {
+		case PREVIOUS_BUTTON:
+			if (_selectedPage > 0) {
+				_selectedPage--;
+			}
+			break;
+		case NEXT_BUTTON:
+			if ((_selectedPage + 1) * kItemsPerPage < _roomNames.size()) {
+				_selectedPage++;
+			}
+			break;
+		default:
+			break;
+		}
+		_selectedButton = NO_BUTTON;
+
+		int firstItem = _selectedPage * kItemsPerPage;
+		if (y >= 72 && y < 72 + (kItemsPerPage * g_engine->_smallFont->getFontHeight()) && x >= 37 && x <= 37 + 200) {
+			int itemIndex = (y - 72) / g_engine->_smallFont->getFontHeight();
+			if (firstItem + itemIndex < _roomNames.size()) {
+				Common::String roomName = _roomNames[firstItem + itemIndex];
+				debug("Selected room: %s", roomName.c_str());
+			}
+		}
+
+		_events->_leftMouseClicked = false;
+	}
+
+	if (_events->_leftMouseButton != 0 && _selectedButton == NO_BUTTON) {
+		_selectedButton = isButtonClicked(_events->_mouseX, _events->_mouseY);
+	}
+}
+
+BackgroundBook::Buttons BackgroundBook::isButtonClicked(int x, int y) {
+	for (int i = 0; i < 2; i++) {
+		if (_buttonRects[i].contains(x, y)) {
+			return static_cast<Buttons>(i);
+		}
+	}
+	return NO_BUTTON;
+}
+
+void BackgroundBook::loadRoomNames() {
+	Common::StringArray roomNames;
+	Common::File juegoExe;
+	if (!juegoExe.open(Common::Path("JUEGO.EXE"))) {
+		error("Couldnt find file JUEGO.EXE");
+	}
+
+	size_t namesSize = 1335;
+	juegoExe.seek(0x49315, SEEK_SET);
+	byte *namesData = new byte[namesSize];
+	juegoExe.read(namesData, namesSize);
+	uint32 pos = 0;
+	Common::String currentName = "";
+	while (pos < namesSize) {
+		if (namesData[pos] == 0xFD &&
+			namesData[pos + 1] == 0x00 &&
+			namesData[pos + 2] == 0x08 &&
+			namesData[pos + 3] == 0x02) {
+			if (currentName.size() > 0) {
+				roomNames.push_back(currentName);
+			}
+			currentName = "";
+			pos += 4;
+			continue;
+		}
+		currentName += (char)namesData[pos];
+		pos++;
+	}
+	delete[] namesData;
+	juegoExe.close();
+	_roomNames = roomNames;
+}
+
+
+void BackgroundBook::drawScreen() {
+	memcpy(_compositeScreen, _backgroundScreen, 640 * 400);
+	drawButtons();
+	memcpy(g_engine->_screen->getPixels(), _compositeScreen, 640 * 400);
+
+
+	int firstItem = _selectedPage * kItemsPerPage;
+	for(int i = 0; i < kItemsPerPage; i++) {
+		if(firstItem + i >= _roomNames.size()) {
+			break;
+		}
+		// g_engine->_graphics->drawColoredTexts(_compositeScreen, _roomNames[i], 37, 72 + (i * g_engine->_smallFont->getFontHeight()), 640, 0, Graphics::kTextAlignLeft);
+	// g_engine->_graphics->drawColoredTexts(_compositeScreen, _spell->text, textX, textY, 640, 0, g_engine->_smallFont);
+		g_engine->_smallFont->drawString(g_engine->_screen, _roomNames[firstItem + i], 37, 72 + (i * g_engine->_smallFont->getFontHeight()), 640, 2, Graphics::kTextAlignLeft);
+	}
+}
+
+void BackgroundBook::drawButtons() {
+	for (int i = 0; i < 2; i++) {
+		if (_selectedButton == i) {
+			drawSpriteToBuffer(_compositeScreen, 640, _buttons[i][0], _buttonRects[i].left, _buttonRects[i].top, _buttonRects[i].width(), _buttonRects[i].height(), 207);
+		} else {
+			drawSpriteToBuffer(_compositeScreen, 640, _buttons[i][1], _buttonRects[i].left, _buttonRects[i].top, _buttonRects[i].width(), _buttonRects[i].height(), 207);
+		}
+	}
+}
+
+void BackgroundBook::loadBackground() {
+	_backgroundScreen = new byte[640 * 400];
+	_palette = new byte[768];
+	_res->getExtraScreen(13, _backgroundScreen, _palette);
+	g_system->getPaletteManager()->setPalette(_palette, 0, 256);
+}
+
+void BackgroundBook::loadButtons() {
+	Common::File alfred7;
+	if (!alfred7.open("ALFRED.7")) {
+		return;
+	}
+	alfred7.seek(3188448, SEEK_SET);
+	for(int i = 0; i < 2; i++) {
+		for(int j = 0; j < 2; j++) {
+			int w = _buttonRects[i].width();
+			int h = _buttonRects[i].height();
+			_buttons[i][j] = new byte[w * h];
+			alfred7.read(_buttons[i][j], w * h);
+		}
+	}
+
+	alfred7.close();
+}
+
+void BackgroundBook::cleanup() {
+	if (_compositeScreen) {
+		delete[] _compositeScreen;
+		_compositeScreen = nullptr;
+	}
+	if (_backgroundScreen) {
+		delete[] _backgroundScreen;
+		_backgroundScreen = nullptr;
+	}
+	if (_palette) {
+		delete[] _palette;
+		_palette = nullptr;
+	}
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/extrascreens.h b/engines/pelrock/extrascreens.h
index 285bbb51e72..951843b1910 100644
--- a/engines/pelrock/extrascreens.h
+++ b/engines/pelrock/extrascreens.h
@@ -122,8 +122,8 @@ private:
 	byte *_compositeScreen;
 	byte *_palette;
 	byte *_controls;
-	byte *buttons[5][2];
 	Common::String trackNames[31];
+	byte *buttons[5][2];
 	Common::Rect _buttonRects[5] = {
 		Common::Rect(Common::Point(17, 46), 37, 26), // Stop
 		Common::Rect(Common::Point(57, 48), 33, 23), // Pause
@@ -138,6 +138,12 @@ private:
 
 
 class BackgroundBook {
+enum Buttons {
+	PREVIOUS_BUTTON,
+	NEXT_BUTTON,
+	NO_BUTTON
+};
+	int kItemsPerPage = 22;
 
 public:
 	BackgroundBook(PelrockEventManager *eventMan, ResourceManager *res);
@@ -147,17 +153,29 @@ public:
 
 private:
 	void init();
+	void loadRoomNames();
 	void drawScreen();
+	void drawButtons();
+	void loadButtons();
 	void loadBackground();
-	void loadControls();
 	void checkMouse(int x, int y);
+	BackgroundBook::Buttons isButtonClicked(int x, int y);
 	void cleanup();
+
 	PelrockEventManager *_events;
 	ResourceManager *_res;
 	byte *_backgroundScreen;
 	byte *_compositeScreen;
 	byte *_palette;
-	byte *_controls;
+	byte *_buttons[2][2];
+	Buttons _selectedButton = NO_BUTTON;
+	int _selectedPage = 0;
+
+	Common::Rect _buttonRects[2] = {
+		Common::Rect(Common::Point(238, 104), 28, 44), // Stop
+		Common::Rect(Common::Point(238, 178), 28, 44), // Pause
+	};
+	Common::StringArray _roomNames;
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 819def28a65..e54a9462521 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -487,6 +487,12 @@ const ExtraImages extraScreens[] = {
 	2321064,
 	8
 	},
+	{
+		0x00226358,
+		0x00236AA8,
+		8
+	},
+
 };
 
 struct AlfredSpecialAnimOffset {
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index fb75db39f6b..d5f25f76f14 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1593,6 +1593,11 @@ void PelrockEngine::gameLoop() {
 		cdPlayer.run();
 		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
 	}
+	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_b) {
+		BackgroundBook backgroundBook(_events, _res);
+		backgroundBook.run();
+		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
+	}
 
 	renderScene();
 
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 10203030f87..fcfa9c51877 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -29,7 +29,6 @@ namespace Pelrock {
 
 RoomManager::RoomManager() {
 	_pixelsShadows = new byte[640 * 400];
-	_roomNames = loadRoomNames();
 	loadWaterPaletteRemap();
 }
 
@@ -1302,39 +1301,6 @@ void RoomManager::loadRemaps(int roomNumber) {
 
 }
 
-Common::StringArray RoomManager::loadRoomNames() {
-	Common::StringArray roomNames;
-	Common::File juegoExe;
-	if (!juegoExe.open(Common::Path("JUEGO.EXE"))) {
-		error("Couldnt find file JUEGO.EXE");
-	}
-
-	size_t namesSize = 1335;
-	juegoExe.seek(0x49315, SEEK_SET);
-	byte *namesData = new byte[namesSize];
-	juegoExe.read(namesData, namesSize);
-	uint32 pos = 0;
-	Common::String currentName = "";
-	while (pos < namesSize) {
-		if (namesData[pos] == 0xFD &&
-			namesData[pos + 1] == 0x00 &&
-			namesData[pos + 2] == 0x08 &&
-			namesData[pos + 3] == 0x02) {
-			if (currentName.size() > 0) {
-				roomNames.push_back(currentName);
-			}
-			currentName = "";
-			pos += 4;
-			continue;
-		}
-		currentName += (char)namesData[pos];
-		pos++;
-	}
-	delete[] namesData;
-	juegoExe.close();
-	return roomNames;
-}
-
 byte RoomManager::loadMusicTrackForRoom(Common::File *roomFile, int roomOffset) {
 	uint32_t pair9offset = roomOffset + (9 * 8);
 	roomFile->seek(pair9offset, SEEK_SET);
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index f318c66ae9c..205d7f5b012 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -130,12 +130,6 @@ public:
 	HotSpot *findHotspotByExtra(uint16 extra);
 	PaletteAnim *getPaletteAnimForRoom(int roomNumber);
 
-	Common::String getRoomName(int roomNumber) {
-		if (roomNumber >= 0 && (uint)roomNumber < _roomNames.size()) {
-			return _roomNames[roomNumber];
-		}
-		return "Unknown Room";
-	}
 
 	byte _currentRoomNumber = 0;
 	int _prevRoomNumber = -1;
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 4eef53ddf46..fbb24786726 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -227,14 +227,17 @@ bool SoundManager::isMusicPlaying() {
 }
 
 void SoundManager::playMusicTrack(int trackNumber, bool loop) {
-	if (_currentMusicTrack == trackNumber && isMusicPlaying()) {
+	if (!_isPaused && _currentMusicTrack == trackNumber && isMusicPlaying()) {
 		// Already playing this track
 		return;
 	}
 	_currentMusicTrack = trackNumber;
-	_cdTrackStart = 0;
-	_cdTrackDuration = 0;
-	_cdPlayStartTime = g_system->getMillis();
+
+	if(!_isPaused) {
+		_cdTrackStart = 0;
+		_cdTrackDuration = 0;
+		_cdPlayStartTime = g_system->getMillis();
+	}
 	g_system->getAudioCDManager()->stop();
 	g_system->getAudioCDManager()->play(trackNumber, loop ? -1 : 0, _cdTrackStart, _cdTrackDuration);
 }
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index 631732ab58a..09a5941ac0d 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -179,6 +179,8 @@ public:
 	 *         Add kAmbientSoundSlotBase (4) to get room sound index
 	 */
 	int tickAmbientSound(uint32 frameCount);
+	bool _isPaused = false;
+	byte _currentMusicTrack = 0;
 
 private:
 	void playSound(SonidoFile sound, int channel = -1);
@@ -191,7 +193,6 @@ private:
 	bool _isMusicPlaying = false;
 	int _currentVolume;
 	Common::File *_musicFile;
-	byte _currentMusicTrack = 0;
 	Audio::SoundHandle _musicHandle;
 	Audio::SoundHandle _sfxHandles[kMaxChannels];
 	Common::HashMap<Common::String, SonidoFile> _soundMap;
@@ -200,7 +201,7 @@ private:
 	uint32 _cdTrackStart = 0;
 	uint32 _cdTrackDuration;
 	uint32 _cdPlayStartTime; // time at the moment of calling play()
-	bool _isPaused = false;
+
 };
 
 } // End of namespace Pelrock


Commit: 6f69e96103cd9fad8fa3f7d7cf19940f83777922
    https://github.com/scummvm/scummvm/commit/6f69e96103cd9fad8fa3f7d7cf19940f83777922
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:14+02:00

Commit Message:
PELROCK: Improves styling in Library Computer

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/computer.cpp
    engines/pelrock/computer.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/room.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index dffaa63087b..994845be397 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -707,7 +707,11 @@ void PelrockEngine::noOpAction(HotSpot *hotspot) {
 }
 
 void PelrockEngine::noOpItem(int item, HotSpot *hotspot) {
-	// 154 to 169
+	if(item >= 11 && item <= 47 && hotspot->extra == 358) {
+		_state->removeInventoryItem(item);
+		_dialog->say(_res->_ingameTexts[DEACUERDO_2]);
+		return;
+	}
 	debug("No-op item %d with hotspot %d", item, hotspot->extra);
 	_alfredState.direction = ALFRED_DOWN;
 	byte response = (byte)getRandomNumber(12);
@@ -2024,6 +2028,14 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		break;
 	}
 	default: {
+		if(inventoryObject >= 11 && inventoryObject <= 47) {
+			_res->loadAlfredSpecialAnim(0);
+			_alfredState.animState = ALFRED_SPECIAL_ANIM;
+			waitForSpecialAnimation();
+			_dialog->say(_res->_ingameTexts[LIBRO_ABURRIDO]);
+			return;
+		}
+
 		byte response = (byte)getRandomNumber(12);
 		_dialog->say(_res->_ingameTexts[154 + response]);
 		break;
diff --git a/engines/pelrock/computer.cpp b/engines/pelrock/computer.cpp
index 04b9d7c9005..03930f125fa 100644
--- a/engines/pelrock/computer.cpp
+++ b/engines/pelrock/computer.cpp
@@ -39,24 +39,6 @@ Computer::Computer(PelrockEventManager *eventMan)
 	  _memorizedBookIndex(-1),
 	  _events(eventMan) {
 
-	// Initialize UI strings (Spanish - original game language)
-	_menuTitle = "MENU PRINCIPAL";
-	_menuOption1 = "1    CONSULTAR POR TITULO";
-	_menuOption2 = "2    CONSULTAR POR AUTOR";
-	_menuOption3 = "3    CANCELAR";
-	_promptLetter = "Teclea una letra (A-Z):";
-	_labelTitle = "Titulo    : ";
-	_labelAuthor = "Autor     : ";
-	_labelGenre = "Genero    : ";
-	_labelSituacion = "Situacion : ";
-	_statusPhysical = "Estante %c, fila %d";
-	_statusCatalogOnly = "Solo en catalogo";
-	_optMemorizar = "(M)emorizar";
-	_optSeguir = "(S)eguir";
-	_optCancelar = "(C)ancelar";
-	_noResults = "No se encontraron libros";
-	_memorizedMsg = "Bueno... Tendre que buscar en la estanteria de la %c";
-
 	init();
 }
 
@@ -87,20 +69,22 @@ void Computer::init() {
 		_libraryBooks.push_back(book);
 	}
 
-	for(int i = 0; i < _libraryBooks.size(); i++) {
+	for (int i = 0; i < _libraryBooks.size(); i++) {
 		const LibraryBook &book = _libraryBooks[i];
 		debug("Loaded book: title='%s', author='%s', genre='%s', unknown=%d, shelf=%d, available=%d",
 			  book.title.c_str(), book.author.c_str(), book.genre.c_str(),
 			  book.inventoryIndex, book.shelf, book.available);
 	}
 
+	_computerText = g_engine->_res->loadComputerText();
+
 	_searchResults.clear();
 	_currentResult = 0;
 	_searchLetter = 0;
 	_memorizedBookIndex = -1;
+	_lineHeight = g_engine->_smallFont->getFontHeight();
 }
 
-
 Computer::~Computer() {
 	cleanup();
 }
@@ -131,6 +115,7 @@ void Computer::cleanup() {
 int Computer::run() {
 	loadBackground();
 	_state = STATE_MAIN_MENU;
+	g_engine->changeCursor(DEFAULT);
 
 	while (!g_engine->shouldQuit() && _state != STATE_EXIT) {
 		_events->pollEvent();
@@ -166,77 +151,83 @@ void Computer::drawScreen() {
 	// Clear to background
 	memcpy(g_engine->_screen->getPixels(), _backgroundScreen, 640 * 400);
 
-	int textY = 100;
-	int textX = 180;
+	int textY = 97;
+	int textX = 225;
+
+	byte defaultColor = 0; // Light gray
 
 	switch (_state) {
-	case STATE_MAIN_MENU:
-		g_engine->_smallFont->drawString(g_engine->_screen, _menuTitle, textX, textY, 280, 15, Graphics::kTextAlignCenter);
-		g_engine->_smallFont->drawString(g_engine->_screen, _menuOption1, textX, textY + 40, 280, 14);
-		g_engine->_smallFont->drawString(g_engine->_screen, _menuOption2, textX, textY + 60, 280, 14);
-		g_engine->_smallFont->drawString(g_engine->_screen, _menuOption3, textX, textY + 80, 280, 14);
+	case STATE_MAIN_MENU: {
+		int textY = 97;
+		int textX = 225;
+		for (int i = 0; _computerText[0].size() > i; i++) {
+			Common::String line = _computerText[0][i];
+			g_engine->_graphics->drawColoredText(g_engine->_screen, line, textX, textY + i * _lineHeight, 200, defaultColor, g_engine->_smallFont);
+		}
 		break;
+	}
 
 	case STATE_SEARCH_BY_TITLE:
 	case STATE_SEARCH_BY_AUTHOR:
-		g_engine->_smallFont->drawString(g_engine->_screen,
-										 _searchType == 0 ? "CONSULTAR POR TITULO" : "CONSULTAR POR AUTOR",
-										 textX, textY, 280, 15, Graphics::kTextAlignCenter);
-		g_engine->_smallFont->drawString(g_engine->_screen, _promptLetter, textX, textY + 40, 280, 14);
+		for (int i = 0; _computerText[1].size() > i; i++) {
+			Common::String line = _computerText[1][i];
+			g_engine->_graphics->drawColoredText(g_engine->_screen, line, 172, 258 + i * _lineHeight, 200, defaultColor, g_engine->_smallFont);
+		}
 		break;
 
 	case STATE_SHOW_RESULTS: {
-		Common::String header = Common::String::format(
-			"Consulta de %s, letra %c",
-			_searchType == 0 ? "TITULO" : "AUTOR",
-			_searchLetter);
-		g_engine->_smallFont->drawString(g_engine->_screen, header, textX, textY, 280, 15, Graphics::kTextAlignCenter);
-
-		if (_searchResults.empty()) {
-			g_engine->_smallFont->drawString(g_engine->_screen, _noResults, textX, textY + 50, 280, 14);
-		} else {
-			// Display current book
-			int bookIdx = _searchResults[_currentResult];
-			const LibraryBook &book = _libraryBooks[bookIdx];
-
-			// Title (may be long, truncate if needed)
-			Common::String titleLine = Common::String::format("%s%s", _labelTitle, book.title.c_str());
-			g_engine->_smallFont->drawString(g_engine->_screen, titleLine, textX - 50, textY + 40, 340, 14);
 
-			// Author
-			Common::String authorLine = Common::String::format("%s%s", _labelAuthor, book.author.c_str());
-			g_engine->_smallFont->drawString(g_engine->_screen, authorLine, textX - 50, textY + 60, 340, 14);
-
-			// Genre
-			Common::String genreLine = Common::String::format("%s%s", _labelGenre, book.genre.c_str());
-			g_engine->_smallFont->drawString(g_engine->_screen, genreLine, textX - 50, textY + 80, 340, 14);
-
-			// Situacion (location/availability)
-			Common::String situacionLine;
-			if (book.available) {
-				situacionLine = Common::String::format("%s Estanteria %d",
-													   _labelSituacion, book.shelf);
-			} else {
-				situacionLine = Common::String::format("%s%s",
-													   _labelSituacion, _statusCatalogOnly);
-			}
-			g_engine->_smallFont->drawString(g_engine->_screen, situacionLine, textX - 50, textY + 100, 340,
-											 book.available ? 10 : 8); // Green if physical, gray if catalog-only
-
-			// Show result counter
-			Common::String counter = Common::String::format("Libro %d de %d",
-															_currentResult + 1, (int)_searchResults.size());
-			g_engine->_smallFont->drawString(g_engine->_screen, counter, textX, textY + 130, 280, 14, Graphics::kTextAlignCenter);
-
-			// Show navigation options
-			Common::String navOptions;
-			if (book.available) {
-				navOptions = Common::String::format("%s   %s   %s", _optMemorizar, _optSeguir, _optCancelar);
-			} else {
-				navOptions = Common::String::format("%s   %s", _optSeguir, _optCancelar);
-			}
-			g_engine->_smallFont->drawString(g_engine->_screen, navOptions, textX, textY + 160, 280, 8, Graphics::kTextAlignCenter);
+		const char *section = _searchType == 0 ? "TITULO " : "AUTOR  ";
+		Common::String title = _computerText[2][0];
+		int replacementIndex = title.findFirstOf("XXXXXXX");
+
+		title.replace(replacementIndex, 7, section);
+		int replacementIndex2 = title.findFirstOf("X");
+		title.replace(replacementIndex2, 1, Common::String(1, _searchLetter));
+		g_engine->_graphics->drawColoredText(g_engine->_screen, title, 210, 97, 200, defaultColor, g_engine->_smallFont);
+
+		int textX = 161;
+		int textY = 131;
+		int increment = 28;
+
+		// Display current book
+		int bookIdx = _searchResults[_currentResult];
+		const LibraryBook &book = _libraryBooks[bookIdx];
+
+		// Title (may be long, truncate if needed)
+		Common::String titleLine = _computerText[3][0];
+		int titlePlaceholderIndex = titleLine.findFirstOf("XXXX");
+
+		titleLine.replace(titlePlaceholderIndex, titleLine.size() - titlePlaceholderIndex, book.title);
+		g_engine->_graphics->drawColoredText(g_engine->_screen, titleLine, textX, textY, 340, defaultColor, g_engine->_smallFont);
+
+		// Author
+		Common::String authorLine = _computerText[4][0];
+		int authorPlaceholderIndex = authorLine.findFirstOf("XXXX");
+		authorLine.replace(authorPlaceholderIndex, authorLine.size() - authorPlaceholderIndex, book.author);
+		g_engine->_graphics->drawColoredText(g_engine->_screen, authorLine, textX, textY + increment, 340, defaultColor, g_engine->_smallFont);
+
+		// Genre
+		Common::String genreLine = _computerText[5][0];
+		int genrePlaceholderIndex = genreLine.findFirstOf("XXXX");
+		genreLine.replace(genrePlaceholderIndex, genreLine.size() - genrePlaceholderIndex, book.genre);
+		g_engine->_graphics->drawColoredText(g_engine->_screen, genreLine, textX, textY + increment * 2, 340, defaultColor, g_engine->_smallFont);
+
+		// Situacion (location/availability)
+		Common::String situacionLine = _computerText[6][0];
+		int situacionPlaceholderIndex = situacionLine.findFirstOf("XXXX");
+		situacionLine.replace(situacionPlaceholderIndex, situacionLine.size() - situacionPlaceholderIndex, book.available ? "Disponible" : "Prestado");
+		g_engine->_graphics->drawColoredText(g_engine->_screen, situacionLine, textX, textY + increment * 3, 340, defaultColor, g_engine->_smallFont);
+
+		// Show navigation options
+		Common::String navOptions;
+		Common::String actions = _computerText[7][0];
+		if (!book.available) {
+			actions.setChar(180, 1);
+			actions.setChar(180, 4);
 		}
+		g_engine->_graphics->drawColoredText(g_engine->_screen, actions, 174, 258, 325, defaultColor, g_engine->_smallFont);
+
 	} break;
 
 	case STATE_EXIT:
@@ -263,8 +254,13 @@ void Computer::handleSearchInput() {
 		_events->_lastKeyEvent <= Common::KEYCODE_z) {
 		_searchLetter = 'A' + (_events->_lastKeyEvent - Common::KEYCODE_a);
 		performSearch();
-		_currentResult = 0;
-		_state = STATE_SHOW_RESULTS;
+		if (!_searchResults.empty()) {
+			_currentResult = 0;
+			_state = STATE_SHOW_RESULTS;
+		} else {
+			// No results, return to main menu
+			_state = STATE_MAIN_MENU;
+		}
 		_events->_lastKeyEvent = Common::KEYCODE_INVALID;
 	} else if (_events->_lastKeyEvent == Common::KEYCODE_ESCAPE) {
 		_state = STATE_MAIN_MENU;
diff --git a/engines/pelrock/computer.h b/engines/pelrock/computer.h
index 7f0117c9f4c..ea1ba3c0a88 100644
--- a/engines/pelrock/computer.h
+++ b/engines/pelrock/computer.h
@@ -65,22 +65,9 @@ private:
 	Common::Array<LibraryBook> _libraryBooks;
 	int _currentResult;
 	int _memorizedBookIndex;  // Index of book that was memorized (-1 if none)
+	int _lineHeight;
 
-	const char *_menuTitle;
-	const char *_menuOption1;       // "CONSULTAR POR TITULO"
-	const char *_menuOption2;       // "CONSULTAR POR AUTOR"
-	const char *_menuOption3;       // "CANCELAR"
-	const char *_promptLetter;      // "Teclea una letra (A-Z):"
-	const char *_labelTitle;        // "Titulo    : "
-	const char *_labelAuthor;       // "Autor     : "
-	const char *_labelGenre;        // "Genero    : "
-	const char *_labelSituacion;    // "Situacion : "
-	const char *_statusPhysical;    // "Estante %c, fila %d"
-	const char *_statusCatalogOnly; // "Solo en catalogo"
-	const char *_optMemorizar;      // "(M)emorizar"
-	const char *_optSeguir;         // "(S)eguir"
-	const char *_optCancelar;       // "(C)ancelar"
-	const char *_noResults;         // "No se encontraron libros"
+	Common::Array<Common::StringArray> _computerText;
 
 	void loadBackground();
 	void cleanup();
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index d5f25f76f14..a6b9914545e 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1598,6 +1598,12 @@ void PelrockEngine::gameLoop() {
 		backgroundBook.run();
 		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
 	}
+	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_w) {
+		Computer computer(_events);
+
+		computer.run();
+		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
+	}
 
 	renderScene();
 
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index d5723293b27..3ea8726e44e 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -379,6 +379,24 @@ void ResourceManager::getPaletteForRoom28(byte *palette) {
 
 	alfred7.close();
 }
+
+Common::Array<Common::StringArray> ResourceManager::loadComputerText() {
+
+	Common::File exe;
+	if (!exe.open("JUEGO.EXE")) {
+		error("Couldnt find file JUEGO.EXE");
+	}
+	int bufSize = 416;
+	byte *computerTextBuf = new byte[bufSize];
+	exe.seek(0x0004901A, SEEK_SET);
+	exe.read(computerTextBuf, bufSize);
+	Common::Array<Common::StringArray> computerTexts = processTextData(computerTextBuf, bufSize);
+
+	delete[] computerTextBuf;
+
+	exe.close();
+	return computerTexts;
+}
 void ResourceManager::getExtraScreen(int screenIndex, byte *screenBuf, byte *palette) {
 	Common::File alfred7;
 	if (!alfred7.open("ALFRED.7")) {
@@ -416,6 +434,7 @@ Common::Array<Common::StringArray> ResourceManager::processTextData(byte *data,
 	Common::StringArray lines;
 	Common::Array<Common::StringArray> texts;
 	while (pos < size) {
+		debug("Processing byte %02X at pos %d", data[pos], pos);
 		if (data[pos] == CTRL_END_TEXT) {
 			if (!desc.empty()) {
 
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index 679a484f028..400631e683d 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -51,7 +51,7 @@ public:
 	void loadInventoryItems();
 	void loadHardcodedText();
 	void getPaletteForRoom28(byte *palette);
-	Common::StringArray loadComputerText();
+	Common::Array<Common::StringArray> loadComputerText();
 	void getExtraScreen(int screenIndex, byte *screenBuf, byte *palette);
 	Common::Array<Common::StringArray> getCredits();
 	Common::Array<Common::Array<Common::String>> processTextData(byte *data, size_t size, bool decode = false);
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 205d7f5b012..5257e5fadb0 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -172,7 +172,6 @@ private:
 	byte loadMusicTrackForRoom(Common::File *roomFile, int roomOffset);
 	Common::Array<byte> loadRoomSfx(Common::File *roomFile, int roomOffset);
 
-	Common::StringArray _roomNames;
 	byte *_resetData = nullptr;
 };
 


Commit: ccc3065030de380733dd0d11b1063816075a8cfe
    https://github.com/scummvm/scummvm/commit/ccc3065030de380733dd0d11b1063816075a8cfe
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:14+02:00

Commit Message:
PELROCK: Implements ending cutscene on room 52

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/console.cpp
    engines/pelrock/console.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 994845be397..a84b7b5fe51 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -707,7 +707,7 @@ void PelrockEngine::noOpAction(HotSpot *hotspot) {
 }
 
 void PelrockEngine::noOpItem(int item, HotSpot *hotspot) {
-	if(item >= 11 && item <= 47 && hotspot->extra == 358) {
+	if (item >= 11 && item <= 47 && hotspot->extra == 358) {
 		_state->removeInventoryItem(item);
 		_dialog->say(_res->_ingameTexts[DEACUERDO_2]);
 		return;
@@ -1834,7 +1834,86 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 		_dialog->say(_res->_ingameTexts[NOSETEOCURRAACERCARTE]);
 		break;
 	}
+	case 375: {
+		endingScene();
+		break;
+	}
+	}
+}
+
+void PelrockEngine::endingScene() {
+	int phase = 0;
+
+	int lines[5][4] = {
+		{57, 176, 301, 322},
+		{138, 159, 283, 292},
+		{213, 156, 325, 277},
+		{460, 163, 370, 292},
+		{530, 167, 353, 320}};
+
+	int stickers[5] = {113, 114, 110, 111, 112};
+
+	while (phase < 5) {
+		debug("Starting ending scene phase %d", phase);
+		for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
+			debug("Current room anim %d: zOrder %d, disableAfterSequence %d", _room->_currentRoomAnims[i].index, _room->_currentRoomAnims[i].zOrder, _room->_currentRoomAnims[i].disableAfterSequence);
+		}
+		Sprite *thisSprite = _room->findSpriteByIndex(phase + 1);
+		thisSprite->animData[0].curFrame = 0;
+		thisSprite->zOrder = 200;
+
+		while (!shouldQuit() && _room->findSpriteByIndex(phase + 1)->zOrder != -1) {
+			_events->pollEvent();
+			renderScene(OVERLAY_NONE);
+			_screen->update();
+			g_system->delayMillis(10);
+		}
+
+		_sound->playSound(_room->_roomSfx[3], 0);
+
+		copyBackgroundToBuffer();
+		placeStickersFirstPass();
+		updateAnimations();
+		presentFrame();
+
+		for (int i = 0; i < 19; i++) {
+			if (shouldQuit()) {
+				return;
+			}
+			int x1 = lines[phase][0];
+			int y1 = lines[phase][1];
+			int x2 = lines[phase][2] + i;
+			int y2 = lines[phase][3];
+			_screen->drawLine(x1, y1, x2, y2, 255);
+		}
+		_screen->markAllDirty();
+		_screen->update();
+		g_system->delayMillis(10);
+
+		if (shouldQuit()) {
+			return;
+		}
+
+		_room->addSticker(stickers[phase]);
+		copyBackgroundToBuffer();
+		placeStickersFirstPass();
+		updateAnimations();
+		presentFrame();
+
+		phase++;
 	}
+
+	_room->addSticker(115);
+	_screen->markAllDirty();
+	_screen->update();
+
+	_dialog->say(_res->_ingameTexts[MAREDEDEU]);
+
+	smokeAnimation(-1, true);
+	_state->setCurrentRoot(48, 1, 0);
+ 	_alfredState.x = 138;
+    _alfredState.y = 255;
+	setScreen(48, ALFRED_DOWN);
 }
 
 void PelrockEngine::useOnAlfred(int inventoryObject) {
@@ -1944,7 +2023,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 				debug("Flight spell cast in room %d, spell page: %d", _room->_currentRoomNumber, spell->page);
 				int flightIndex = _room->_currentRoomNumber - 51;
 				debug("Correct page for this spell is = %d", kFlightRooms[flightIndex].spellPage);
-				if(_flightSorcererAppeared && !_flightInBlockingAnim && spell->page == kFlightRooms[flightIndex].spellPage) {
+				if (_flightSorcererAppeared && !_flightInBlockingAnim && spell->page == kFlightRooms[flightIndex].spellPage) {
 					_state->setFlag(FLAG_COMO_ESTAN_LOS_DIOSES, _state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) | (1 << flightIndex));
 					debug("Flight spell successful, starting animation and updating state");
 					debug("Updated FLAG_COMO_ESTAN_LOS_DIOSES: %d", _state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES));
@@ -1976,23 +2055,20 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 	case 109: {
 
 		if (_state->hasInventoryItem(110) == true) {
-			if(_state->hasInventoryItem(109) == true && _state->hasInventoryItem(108) == true) {
-					_state->removeInventoryItem(110);
-					_state->removeInventoryItem(109);
-					_state->removeInventoryItem(108);
-					addInventoryItem(83);
-					_dialog->say(_res->_ingameTexts[MUNECO_ARREGLADO]);
-					return;
-			}
-			else if(_state->hasInventoryItem(109) == true) {
+			if (_state->hasInventoryItem(109) == true && _state->hasInventoryItem(108) == true) {
+				_state->removeInventoryItem(110);
+				_state->removeInventoryItem(109);
+				_state->removeInventoryItem(108);
+				addInventoryItem(83);
+				_dialog->say(_res->_ingameTexts[MUNECO_ARREGLADO]);
+				return;
+			} else if (_state->hasInventoryItem(109) == true) {
 				_dialog->say(_res->_ingameTexts[NOTENGOPARCHES]);
 
-			}
-			else if(_state->hasInventoryItem(108) == true) {
+			} else if (_state->hasInventoryItem(108) == true) {
 				_dialog->say(_res->_ingameTexts[NOTENGOPEGAMENTO]);
 			}
-		}
-		else {
+		} else {
 			byte response = (byte)getRandomNumber(12);
 			_dialog->say(_res->_ingameTexts[154 + response]);
 		}
@@ -2028,7 +2104,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		break;
 	}
 	default: {
-		if(inventoryObject >= 11 && inventoryObject <= 47) {
+		if (inventoryObject >= 11 && inventoryObject <= 47) {
 			_res->loadAlfredSpecialAnim(0);
 			_alfredState.animState = ALFRED_SPECIAL_ANIM;
 			waitForSpecialAnimation();
diff --git a/engines/pelrock/console.cpp b/engines/pelrock/console.cpp
index 929d83a7205..b796869b9f0 100644
--- a/engines/pelrock/console.cpp
+++ b/engines/pelrock/console.cpp
@@ -32,11 +32,28 @@ PelrockConsole::PelrockConsole(PelrockEngine *engine) : GUI::Debugger(), _engine
 	registerCmd("setRoot", WRAP_METHOD(PelrockConsole, cmdSetRoot));
 	registerCmd("setFlag", WRAP_METHOD(PelrockConsole, cmdSetFlag));
 	registerCmd("toJail", WRAP_METHOD(PelrockConsole, cmdToJail));
+	registerCmd("removeSticker", WRAP_METHOD(PelrockConsole, cmdRemoveSticker));
 }
 
 PelrockConsole::~PelrockConsole() {
 }
 
+bool PelrockConsole::cmdRemoveSticker(int argc, const char **argv) {
+	if (argc < 2) {
+		debugPrintf("Usage: removeSticker <stickerId>");
+		return true;
+	}
+	int stickerId = atoi(argv[1]);
+	for(int i = 0; i < g_engine->_state->stickersPerRoom[g_engine->_room->_currentRoomNumber].size(); i++) {
+		if (g_engine->_state->stickersPerRoom[g_engine->_room->_currentRoomNumber][i].stickerIndex == stickerId) {
+			g_engine->_state->stickersPerRoom[g_engine->_room->_currentRoomNumber].remove_at(i);
+			debugPrintf("Removed sticker %d from room %d\n", stickerId, g_engine->_room->_currentRoomNumber);
+			return true;
+		}
+	}
+	debugPrintf("Removed sticker %d from room %d\n", stickerId, g_engine->_room->_currentRoomNumber);
+	return true;
+}
 
 bool PelrockConsole::cmdSetFlag(int argc, const char **argv) {
 	if (argc < 3) {
diff --git a/engines/pelrock/console.h b/engines/pelrock/console.h
index ff86ea92f52..a426c5d4cb6 100644
--- a/engines/pelrock/console.h
+++ b/engines/pelrock/console.h
@@ -41,6 +41,7 @@ private:
 public:
 	PelrockConsole(PelrockEngine *engine);
 	~PelrockConsole() override;
+	bool cmdRemoveSticker(int argc, const char **argv);
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index a6b9914545e..aae60b36907 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -160,7 +160,8 @@ void PelrockEngine::init() {
 		// setScreen(41, ALFRED_DOWN);
 		// setScreen(43, ALFRED_DOWN);
 		// setScreen(46, ALFRED_RIGHT);
-		setScreen(0, ALFRED_DOWN);
+		// setScreen(0, ALFRED_DOWN);
+		setScreen(52, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
@@ -347,7 +348,7 @@ const int kPasserbyTriggerFrameInterval = 0x3FF;
 void PelrockEngine::frameTriggers() {
 	uint32 frameCount = _chrono->getFrameCount();
 	passerByAnim(frameCount);
-	handleFlightRoomFrame();
+	// make();
 }
 
 void PelrockEngine::passerByAnim(uint32 frameCount) {
@@ -1605,6 +1606,11 @@ void PelrockEngine::gameLoop() {
 		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
 	}
 
+	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_e && _room->_currentRoomNumber == 52) {
+		endingScene();
+		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
+	}
+
 	renderScene();
 
 	_screen->update();
@@ -2023,6 +2029,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 	}
 	case 48: {
 		_dialog->_goodbyeDisabled = true;
+
 		if (_state->getFlag(FLAG_CORRECT_DOOR_CHOSEN) == true) {
 			if (_state->getFlag(FLAG_TRAMPILLA_ABIERTA) == true) {
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 183d773b2bd..5a74bbf2d48 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -377,6 +377,7 @@ public:
 	void checkAllSymbols();
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
+	void endingScene();
 
 	void animateStatuePaletteFade(bool reverse = false);
 	void pickUpMatches(HotSpot *hotspot);


Commit: 27d21a0a070424ed2ab28b3ba6fd37dac2f14481
    https://github.com/scummvm/scummvm/commit/27d21a0a070424ed2ab28b3ba6fd37dac2f14481
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:14+02:00

Commit Message:
PELROCK: Implements scene with girl in the outro

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/util.cpp
    engines/pelrock/util.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index a84b7b5fe51..2f98a338a01 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1835,13 +1835,13 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 		break;
 	}
 	case 375: {
-		endingScene();
+		teletransportToPrincess();
 		break;
 	}
 	}
 }
 
-void PelrockEngine::endingScene() {
+void PelrockEngine::teletransportToPrincess() {
 	int phase = 0;
 
 	int lines[5][4] = {
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index e54a9462521..e5d37e1a862 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -488,10 +488,15 @@ const ExtraImages extraScreens[] = {
 	8
 	},
 	{
-		0x00226358,
-		0x00236AA8,
-		8
+	0x00226358, // 13 - Background book
+	0x00236AA8,
+	8
 	},
+	{
+	3058226, // 14 - Ending
+	3185280,
+	8
+	}
 
 };
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index aae60b36907..87d0f2df065 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -154,14 +154,14 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		// setScreen(0, ALFRED_DOWN);
+		setScreen(0, ALFRED_DOWN);
 		// setScreen(3, ALFRED_RIGHT);
 		// setScreen(22, ALFRED_DOWN);
 		// setScreen(41, ALFRED_DOWN);
 		// setScreen(43, ALFRED_DOWN);
 		// setScreen(46, ALFRED_RIGHT);
 		// setScreen(0, ALFRED_DOWN);
-		setScreen(52, ALFRED_DOWN);
+		// setScreen(52, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
 		// alfredState.x = 576;
@@ -243,14 +243,14 @@ void PelrockEngine::travelToEgypt() {
 	_graphics->fadeToBlack(10);
 	_sound->playMusicTrack(26, false);
 	byte *palette = new byte[768];
-	if (_extraScreen == nullptr) {
-		_extraScreen = new byte[640 * 400];
+	if (_bgScreen == nullptr) {
+		_bgScreen = new byte[640 * 400];
 	}
-	_res->getExtraScreen(6, _extraScreen, palette);
+	_res->getExtraScreen(6, _bgScreen, palette);
 	CursorMan.showMouse(false);
 	g_system->getPaletteManager()->setPalette(palette, 0, 256);
 
-	memcpy(_screen->getPixels(), _extraScreen, 640 * 400);
+	memcpy(_screen->getPixels(), _bgScreen, 640 * 400);
 	int frameCount = 0;
 	while (!shouldQuit() && frameCount < 96) {
 		_events->pollEvent();
@@ -273,10 +273,10 @@ void PelrockEngine::travelToEgypt() {
 	_graphics->clearScreen();
 
 	g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
-	free(_extraScreen);
-	_extraScreen = nullptr;
+	free(_bgScreen);
+	_bgScreen = nullptr;
 	CursorMan.showMouse(true);
-	delete[] _extraScreen;
+	delete[] _bgScreen;
 	delete[] palette;
 	_screen->markAllDirty();
 	_screen->update();
@@ -348,7 +348,7 @@ const int kPasserbyTriggerFrameInterval = 0x3FF;
 void PelrockEngine::frameTriggers() {
 	uint32 frameCount = _chrono->getFrameCount();
 	passerByAnim(frameCount);
-	// make();
+	handleFlightRoomFrame();
 }
 
 void PelrockEngine::passerByAnim(uint32 frameCount) {
@@ -1607,6 +1607,11 @@ void PelrockEngine::gameLoop() {
 	}
 
 	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_e && _room->_currentRoomNumber == 52) {
+		teletransportToPrincess();
+		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
+	}
+
+	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_f) {
 		endingScene();
 		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
 	}
@@ -1627,7 +1632,7 @@ void PelrockEngine::computerLoop() {
 }
 
 void PelrockEngine::extraScreenLoop() {
-	memcpy(_screen->getPixels(), _extraScreen, 640 * 400);
+	memcpy(_screen->getPixels(), _bgScreen, 640 * 400);
 	while (!shouldQuit()) {
 		_events->pollEvent();
 
@@ -1641,8 +1646,8 @@ void PelrockEngine::extraScreenLoop() {
 	}
 
 	g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
-	free(_extraScreen);
-	_extraScreen = nullptr;
+	free(_bgScreen);
+	_bgScreen = nullptr;
 }
 
 void PelrockEngine::walkLoop(int16 x, int16 y, AlfredDirection direction) {
@@ -1891,16 +1896,16 @@ void PelrockEngine::setScreen(int roomNumber, AlfredDirection dir) {
 void PelrockEngine::loadExtraScreenAndPresent(int screenIndex) {
 
 	byte *palette = new byte[768];
-	if (_extraScreen == nullptr) {
-		_extraScreen = new byte[640 * 400];
+	if (_bgScreen == nullptr) {
+		_bgScreen = new byte[640 * 400];
 	}
-	_res->getExtraScreen(screenIndex, _extraScreen, palette);
+	_res->getExtraScreen(screenIndex, _bgScreen, palette);
 	CursorMan.showMouse(false);
 	_graphics->clearScreen();
 	g_system->getPaletteManager()->setPalette(palette, 0, 256);
 	extraScreenLoop();
 	CursorMan.showMouse(true);
-	delete[] _extraScreen;
+	delete[] _bgScreen;
 	delete[] palette;
 	_screen->markAllDirty();
 	_screen->update();
@@ -2033,9 +2038,17 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		if (_state->getFlag(FLAG_CORRECT_DOOR_CHOSEN) == true) {
 			if (_state->getFlag(FLAG_TRAMPILLA_ABIERTA) == true) {
 
+				_dialog->say(_res->_ingameTexts[OHMISALVADOR]);
+				_dialog->say(_res->_ingameTexts[VOYPORTI_PRINCESA]);
+				_state->setCurrentRoot(48, 1, 0);
+				walkAndAction(_room->findHotspotByExtra(634), TALK);
+
+				endingScene();
+
 			} else {
 				_dialog->say(_res->_ingameTexts[OHMISALVADOR]);
 				_dialog->say(_res->_ingameTexts[VOYPORTI_PRINCESA]);
+				_state->setFlag(FLAG_TRAMPILLA_ABIERTA, true);
 				walkAndAction(_room->findHotspotByExtra(634), TALK);
 				_room->addSticker(134);
 				// wait a few frames
@@ -2051,6 +2064,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 				}
 				_alfredState.x = 294;
 				_alfredState.y = 387;
+				_room->addSticker(136);
 				setScreen(49, ALFRED_UP);
 			}
 		} else {
@@ -2085,6 +2099,138 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 	}
 }
 
+void PelrockEngine::endingScene() {
+	byte *palette = new byte[768];
+	if (_bgScreen == nullptr) {
+		_bgScreen = new byte[640 * 400];
+	}
+	_res->getExtraScreen(14, _bgScreen, palette);
+	CursorMan.showMouse(false);
+	_graphics->clearScreen();
+	g_system->getPaletteManager()->setPalette(palette, 0, 256);
+	Common::File alfred7;
+	if (!alfred7.open(Common::Path("ALFRED.7"))) {
+		error("Could not open ALFRED.7");
+		return;
+	}
+	byte *decompressedBuf = nullptr;
+	size_t decompressedSize = 0;
+	rleDecompressSingleBuda(&alfred7, 3222250, decompressedBuf, decompressedSize);
+	alfred7.close();
+
+	int animValues[4][8] = {
+		{426, 211, 114, 189, 2, 2, 0, 0}, // Legs anim values (2 frames)
+		{287, 68, 42, 26, 3, 1, 1, 15},   // Eyes anim values (3 frames)
+		{172, 173, 93, 71, 3, 1, 3, 17},  // Alfred hand anim values (3 frames)
+		{241, 334, 55, 66, 2, 2, 0, 0}    // Hand anim values (2 frames)
+	};
+
+	uint32 pos = 0;
+	Common::Array<Sprite *> sprites;
+	for (int i = 0; i < 4; i++) {
+		Sprite *sprite = new Sprite();
+		sprite->x = animValues[i][0];
+		sprite->y = animValues[i][1];
+		sprite->w = animValues[i][2];
+		sprite->h = animValues[i][3];
+		sprite->stride = animValues[i][2] * animValues[i][3];
+		bool idleAnim = animValues[i][7] > 0;
+		if (idleAnim) {
+			sprite->numAnims = 2;
+		} else {
+			sprite->numAnims = 1;
+		}
+
+		sprite->animData = new Anim[sprite->numAnims];
+		Anim mainAnim;
+		mainAnim.nframes = animValues[i][4];
+		mainAnim.loopCount = animValues[i][6];
+		mainAnim.speed = animValues[i][5];
+
+		byte *legsAnimData = new byte[sprite->stride * mainAnim.nframes];
+		Common::copy(decompressedBuf + pos, decompressedBuf + pos + sprite->stride * mainAnim.nframes, legsAnimData);
+		mainAnim.animData = new byte *[mainAnim.nframes];
+		for (int j = 0; j < mainAnim.nframes; j++) {
+			mainAnim.animData[j] = new byte[sprite->stride];
+			extractSingleFrame(legsAnimData, mainAnim.animData[j], j, sprite->w, sprite->h);
+		}
+
+		if (idleAnim) {
+			Anim idleAnim;
+			idleAnim.nframes = 1;
+			idleAnim.loopCount = 1;
+			idleAnim.speed = animValues[i][7];
+			idleAnim.animData = new byte *[1];
+			idleAnim.animData[0] = new byte[sprite->stride];
+			extractSingleFrame(legsAnimData, idleAnim.animData[0], 0, sprite->w, sprite->h);
+			sprite->animData[0] = idleAnim;
+			sprite->animData[1] = mainAnim;
+		} else {
+			sprite->animData[0] = mainAnim;
+		}
+
+		pos += sprite->stride * mainAnim.nframes;
+		delete[] legsAnimData;
+		sprites.push_back(sprite);
+	}
+
+	Common::Rect bbox1 = _largeFont->getBoundingBox("ALFRED PELROCK");
+	Common::Rect bbox2 = _largeFont->getBoundingBox("En busca de un sueño");
+	int y1 = 400 / 2 - bbox1.height() / 2;
+	int y2 = 400 / 2 + bbox1.height() / 2;
+	int ticks = 0;
+	_sound->playMusicTrack(3);
+	while (!shouldQuit()) {
+		_events->pollEvent();
+
+		_chrono->updateChrono();
+
+		if (_chrono->_gameTick) {
+
+			memcpy(_compositeBuffer, _bgScreen, 640 * 400);
+
+			for (Sprite *sprite : sprites) {
+				drawNextFrame(sprite);
+			}
+
+			memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
+			if (ticks > 30 && ticks < 180) {
+				drawText(_largeFont, "ALFRED PELROCK", 0, y1, 640, 255);
+				drawText(_largeFont, "En busca de un sue\x80o", 0, y2, 640, 255);
+			}
+
+			if (ticks > 200) {
+				break;
+			}
+			ticks++;
+		}
+
+		g_system->delayMillis(10);
+		_screen->markAllDirty();
+		_screen->update();
+	}
+
+	memset(_screen->getPixels(), 0, 640 * 400);
+	g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
+	free(_bgScreen);
+	_bgScreen = nullptr;
+
+	CursorMan.showMouse(true);
+	delete[] _bgScreen;
+	_bgScreen = nullptr;
+	delete[] palette;
+	_screen->markAllDirty();
+	_screen->update();
+	if (shouldQuit())
+		return;
+	credits();
+}
+
+void PelrockEngine::credits() {
+
+	debug("Starting credits sequence");
+}
+
 void PelrockEngine::initGodsSequences(int roomNumber) {
 	int idx = roomNumber - 51;
 	_flightFrameCounter = 0;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 5a74bbf2d48..3adb91b7b8d 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -131,7 +131,7 @@ private:
 	PathContext _currentContext;
 
 	byte *_currentBackground = nullptr; // Clean background - NEVER modified
-	byte *_extraScreen = nullptr;
+	byte *_bgScreen = nullptr;
 
 	ActionPopupState _actionPopupState;
 
@@ -255,6 +255,8 @@ public:
 
 	// Actions
 	void doExtraActions(int roomNumber);
+	void endingScene();
+	void credits();
 	void initGodsSequences(int roomNumber);
 	void addInventoryItem(int item);
 	void buyFromStore(HotSpot *hotspot, int stickerId);
@@ -377,7 +379,7 @@ public:
 	void checkAllSymbols();
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
-	void endingScene();
+	void teletransportToPrincess();
 
 	void animateStatuePaletteFade(bool reverse = false);
 	void pickUpMatches(HotSpot *hotspot);
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 709614e875d..4efb535eb50 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -240,6 +240,14 @@ void readUntilBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *
 	outSize = pos;
 }
 
+void rleDecompressSingleBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *&outBuffer, size_t &outSize){
+	byte *buffer = nullptr;
+	size_t size = 0;
+	readUntilBuda(stream, startPos, buffer, size);
+	outSize = rleDecompress(buffer, size, 0, 0, &outBuffer, true);
+	free(buffer);
+}
+
 // Helper function for transparent blitting
 void drawSpriteToBuffer(byte *buffer, int bufferWidth, byte *sprite, int x, int y, int width, int height, int transparentColor) {
 
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index 3425c7ef455..98305b9319c 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -34,6 +34,7 @@ namespace Pelrock {
 const int EXPECTED_SIZE = 640 * 400;
 size_t rleDecompress(const uint8_t *data, size_t data_size, uint32_t offset, uint32_t size, uint8_t **out_data, bool untilBuda = true);
 void readUntilBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *&buffer, size_t &outSize);
+void rleDecompressSingleBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *&buffer, size_t &outSize);
 void drawSpriteToBuffer(byte *buffer, int bufferWidth, byte *sprite, int x, int y, int width, int height, int transparentColor);
 void blitSurfaceToBuffer(Graphics::Surface *surface, byte *buffer, int bufferWidth, int bufferHeight, int destX, int destY);
 void extractSingleFrame(byte *source, byte *dest, int frameIndex, int frameWidth, int frameHeight);


Commit: 7fe55502095fe4786079fb35fb6cf986a8d6f807
    https://github.com/scummvm/scummvm/commit/7fe55502095fe4786079fb35fb6cf986a8d6f807
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:15+02:00

Commit Message:
PELROCK: Enables final hotspot after fight sequence

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/console.h
    engines/pelrock/dialog.cpp
    engines/pelrock/extrascreens.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 2f98a338a01..3b743ef4e48 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1855,9 +1855,6 @@ void PelrockEngine::teletransportToPrincess() {
 
 	while (phase < 5) {
 		debug("Starting ending scene phase %d", phase);
-		for (int i = 0; i < _room->_currentRoomAnims.size(); i++) {
-			debug("Current room anim %d: zOrder %d, disableAfterSequence %d", _room->_currentRoomAnims[i].index, _room->_currentRoomAnims[i].zOrder, _room->_currentRoomAnims[i].disableAfterSequence);
-		}
 		Sprite *thisSprite = _room->findSpriteByIndex(phase + 1);
 		thisSprite->animData[0].curFrame = 0;
 		thisSprite->zOrder = 200;
@@ -2029,7 +2026,21 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 					debug("Updated FLAG_COMO_ESTAN_LOS_DIOSES: %d", _state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES));
 					smokeAnimation(kFlightRooms[flightIndex].spriteIdx, true);
 					_room->addStickerToRoom(_room->_currentRoomNumber, 127 + flightIndex);
+					// if(_state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) == 0b1111) {
+						HotSpot hotspot = HotSpot();
+						hotspot.actionFlags = 0;
+						hotspot.extra = 999;
+						hotspot.x = 320;
+						hotspot.y = 288;
+						hotspot.w = 35;
+						hotspot.h = 21;
+						hotspot.innerIndex = 0;
+						hotspot.index = 8;
+						_room->changeHotspot(52, hotspot);
+					// }
 				}
+
+
 				break;
 			}
 			default:
diff --git a/engines/pelrock/console.h b/engines/pelrock/console.h
index a426c5d4cb6..e20cd09951e 100644
--- a/engines/pelrock/console.h
+++ b/engines/pelrock/console.h
@@ -37,11 +37,11 @@ private:
 	bool cmdTest(int argc, const char **argv);
 	bool cmdSetRoot(int argc, const char **argv);
 	bool cmdSetFlag(int argc, const char **argv);
+	bool cmdRemoveSticker(int argc, const char **argv);
 
 public:
 	PelrockConsole(PelrockEngine *engine);
 	~PelrockConsole() override;
-	bool cmdRemoveSticker(int argc, const char **argv);
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 96ef5091fa9..400cb513941 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -187,7 +187,6 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 	// Clear any existing click state
 	_events->_leftMouseClicked = false;
 	_dialogActive = true;
-	g_engine->_chrono->pauseCounter();
 	int curPage = 0;
 
 	// Render loop - display text and wait for click
@@ -239,7 +238,6 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		_curSprite->isTalking = false;
 	}
 	_dialogActive = false;
-	g_engine->_chrono->resumeCounter();
 	g_engine->_alfredState.setState(ALFRED_IDLE);
 }
 
diff --git a/engines/pelrock/extrascreens.cpp b/engines/pelrock/extrascreens.cpp
index 446a8e986d8..1dd9673b356 100644
--- a/engines/pelrock/extrascreens.cpp
+++ b/engines/pelrock/extrascreens.cpp
@@ -345,7 +345,7 @@ void CDPlayer::loadControls() {
 
 	size_t decompressedSize = rleDecompress(compressedData, outSize, 0, 0, &rawData, true);
 
-	debug("Decompressed CD player controls: %d bytes", decompressedSize);
+	// debug("Decompressed CD player controls: %d bytes", decompressedSize);
 	uint32 pos = 213 * 72;
 	Common::copy(rawData, rawData + pos, _controls);
 	for (int i = 0; i < 5; i++) {
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 3ea8726e44e..4eb473bae70 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -434,7 +434,6 @@ Common::Array<Common::StringArray> ResourceManager::processTextData(byte *data,
 	Common::StringArray lines;
 	Common::Array<Common::StringArray> texts;
 	while (pos < size) {
-		debug("Processing byte %02X at pos %d", data[pos], pos);
 		if (data[pos] == CTRL_END_TEXT) {
 			if (!desc.empty()) {
 
@@ -524,7 +523,7 @@ void ResourceManager::mergeRleBlocks(Common::SeekableReadStream *stream, uint32
 		readUntilBuda(stream, stream->pos(), thisBlock, blockSize);
 		uint8_t *block_data = nullptr;
 		size_t decompressedSize = rleDecompress(thisBlock, blockSize, 0, 640 * 400, &block_data, true);
-		debug("Decompressed block %d: %zu bytes, total %zu", i, decompressedSize, combined_size + decompressedSize);
+		// debug("Decompressed block %d: %zu bytes, total %zu", i, decompressedSize, combined_size + decompressedSize);
 		if (combined_size + decompressedSize > 640 * 400) {
 			debug("Warning: decompressed data exceeds output buffer size, truncating");
 			decompressedSize = 640 * 400 - combined_size;
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index fcfa9c51877..c50af161fb3 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -854,6 +854,7 @@ Common::Array<HotSpot> RoomManager::unifyHotspots(Common::Array<Pelrock::Sprite>
 		thisHotspot.isEnabled = !anims[i].isHotspotDisabled;
 		thisHotspot.isSprite = true;
 		thisHotspot.zOrder = anims[i].zOrder;
+		thisHotspot.innerIndex = anims[i].index;
 		unifiedHotspots.push_back(thisHotspot);
 	}
 
@@ -1213,7 +1214,7 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 	readUntilBuda(&talkFile, talkHeader.spritePointer, data, dataSize);
 	size_t decompressedSize = rleDecompress(data, dataSize, 0, dataSize, &decompressed);
 	free(data);
-	debug("Decompressed talking anim A size: %zu, decompressed size: %zu", dataSize, decompressedSize);
+	// debug("Decompressed talking anim A size: %zu, decompressed size: %zu", dataSize, decompressedSize);
 	for (int i = 0; i < talkHeader.numFramesAnimA; i++) {
 		talkHeader.animA[i] = new byte[talkHeader.wAnimA * talkHeader.hAnimA];
 		extractSingleFrame(decompressed, talkHeader.animA[i], i, talkHeader.wAnimA, talkHeader.hAnimA);
@@ -1224,7 +1225,7 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 		for (int i = 0; i < talkHeader.numFramesAnimB; i++) {
 			talkHeader.animB[i] = new byte[talkHeader.wAnimB * talkHeader.hAnimB];
 			uint32 offset = animASize + (i * talkHeader.wAnimB * talkHeader.hAnimB);
-			debug("Extracting talking anim B frame %d at offset %d, size = %d", i, animASize + (i * talkHeader.wAnimB * talkHeader.hAnimB), talkHeader.wAnimB * talkHeader.hAnimB);
+			// debug("Extracting talking anim B frame %d at offset %d, size = %d", i, animASize + (i * talkHeader.wAnimB * talkHeader.hAnimB), talkHeader.wAnimB * talkHeader.hAnimB);
 			if (offset + talkHeader.wAnimB * talkHeader.hAnimB >= decompressedSize) {
 				debug("Error: offset %d is beyond decompressed size %zu", offset, decompressedSize);
 				talkHeader.numFramesAnimB = 0;
@@ -1277,7 +1278,7 @@ byte *RoomManager::loadShadowMap(int roomNumber) {
 		debug("Failed to decompress shadow map for room %d", roomNumber);
 		shadows = nullptr;
 	}
-	debug("Decompressed shadow map for room %d, compressed size: %zu, decompressed size: %zu", roomNumber, compressedSize, decompressedSize);
+	// debug("Decompressed shadow map for room %d, compressed size: %zu, decompressed size: %zu", roomNumber, compressedSize, decompressedSize);
 	free(compressed);
 	shadowMapFile.close();
 	return shadows;


Commit: 78a126017cc63921b85be40e352c72b90be4ea6d
    https://github.com/scummvm/scummvm/commit/78a126017cc63921b85be40e352c72b90be4ea6d
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:15+02:00

Commit Message:
PELROCK: Loads inventory paging arrows

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 87d0f2df065..2effd749c00 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -167,6 +167,22 @@ void PelrockEngine::init() {
 		// alfredState.x = 576;
 		// alfredState.y = 374;
 	}
+
+	loadInventoryArrows();
+}
+
+void PelrockEngine::loadInventoryArrows() {
+	Common::File alfred7;
+	if(!alfred7.open("ALFRED.7")) {
+		error("Failed to open ALFRED.7 to load inventory arrows");
+		return;
+	}
+	alfred7.seek(3186048, SEEK_SET); // Offset for inventory arrows in ALFRED.7
+	_inventoryOverlayState.arrows[0] = new byte[20 * 60];
+	_inventoryOverlayState.arrows[1] = new byte[20 * 60];
+	alfred7.read(_inventoryOverlayState.arrows[0], 20 * 60);
+	alfred7.read(_inventoryOverlayState.arrows[1], 20 * 60);
+	alfred7.close();
 }
 
 void PelrockEngine::loadAnims() {
@@ -1485,7 +1501,9 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
 
 	VerbIcon icon = isActionUnder(_events->_mouseX, _events->_mouseY);
+
 	bool shouldBlink = _chrono->getFrameCount() % kIconBlinkPeriod == 0;
+
 	for (uint i = 0; i < actions.size(); i++) {
 		if (icon == actions[i] && shouldBlink) {
 			continue;
@@ -1493,17 +1511,29 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 		Common::Point p = getPositionInBallonForIndex(i, posx, posy);
 		drawSpriteToBuffer(_compositeBuffer, 640, _res->_verbIcons[actions[i]], p.x, p.y, kVerbIconWidth, kVerbIconHeight, 1);
 	}
+
+	if(icon == ITEM) {
+		if(!_inventoryOverlayState.isActive) {
+			_inventoryOverlayState.isActive = true;
+			// _inventoryOverlayState.page =
+		}
+		showInventoryOverlay();
+	}
+
 	if (_state->selectedInventoryItem >= 0 && !_state->inventoryItems.empty()) {
 		if (icon == ITEM && shouldBlink) {
 			return;
 		}
 		drawSpriteToBuffer(_compositeBuffer, 640, _res->getIconForObject(_state->selectedInventoryItem).iconData, posx + 20 + (actions.size() * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
 	}
+
 	if (_actionPopupState.curFrame < 3) {
 		_actionPopupState.curFrame++;
 	} else {
 		_actionPopupState.curFrame = 0;
 	}
+
+
 }
 
 void PelrockEngine::animateTalkingNPC(Sprite *animSet) {
@@ -1543,11 +1573,11 @@ void PelrockEngine::animateTalkingNPC(Sprite *animSet) {
 }
 
 Common::Point getPositionInOverlayForIndex(uint index) {
-	return Common::Point(5 + index * (60 + 2), 400 - 60 - 5);
+	return Common::Point(20 + index * (60), 340);
 }
 
 void PelrockEngine::pickupIconFlash() {
-	_graphics->showOverlay(65, _compositeBuffer);
+	_graphics->showOverlay(60, _compositeBuffer);
 	if (_newItem == -1)
 		return;
 	uint invSize = _state->inventoryItems.size();
@@ -1564,6 +1594,19 @@ void PelrockEngine::pickupIconFlash() {
 	}
 }
 
+void PelrockEngine::showInventoryOverlay() {
+	_graphics->showOverlay(60, _compositeBuffer);
+	uint invSize = _state->inventoryItems.size();
+	int firstItem = _inventoryOverlayState.page * kInventoryPageSize;
+
+	for (int i = firstItem; i < invSize && i < firstItem + kInventoryPageSize; i++) {
+		Common::Point p = getPositionInOverlayForIndex(i - firstItem);
+		drawSpriteToBuffer(_compositeBuffer, 640, _res->getIconForObject(_state->inventoryItems[i]).iconData, p.x, p.y, 60, 60, 1);
+	}
+	drawSpriteToBuffer(_compositeBuffer, 640, _inventoryOverlayState.arrows[0], 0, 340, 20, 60, 255);
+	drawSpriteToBuffer(_compositeBuffer, 640, _inventoryOverlayState.arrows[1], 620, 340, 20, 60, 255);
+}
+
 void PelrockEngine::gameLoop() {
 
 	_events->pollEvent();
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 3adb91b7b8d..a1e03ef0045 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -65,6 +65,7 @@ private:
 	MenuManager *_menu = nullptr;
 
 	void init();
+	void loadInventoryArrows();
 	void loadAnims();
 
 	/*
@@ -110,6 +111,8 @@ private:
 
 	void playSoundIfNeeded();
 
+	void showInventoryOverlay();
+
 	void gameLoop();
 	void computerLoop();
 	void extraScreenLoop();
@@ -134,6 +137,8 @@ private:
 	byte *_bgScreen = nullptr;
 
 	ActionPopupState _actionPopupState;
+	InventoryOverlayState _inventoryOverlayState;
+
 
 	HotSpot *_currentHotspot = nullptr;
 	int _newItem = -1;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 60e0caa505d..cbe3cdf7f2f 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -83,9 +83,10 @@ const int kChoiceHeight = 16; // Height of each choice line in pixels
 const int kTalkAnimationSpeed = 2; // Frames per update
 const int kAlfredAnimationSpeed = 2; // Frames per update
 
-
 const int kAlfredIdleAnimationFrameCount = 300;
 
+const int kInventoryPageSize = 10;
+
 // Direction flags (bit-packed)
 #define MOVE_RIGHT 0x01 // Move right (positive X)
 #define MOVE_LEFT 0x02  // Move left (negative X)
@@ -159,6 +160,12 @@ struct ActionPopupState {
 	bool isAlfredUnder = false;
 };
 
+struct InventoryOverlayState {
+	bool isActive = false;
+	int page = 0;
+	byte *arrows[2] = { nullptr, nullptr };
+};
+
 struct AlfredState {
 	AlfredAnimState animState = ALFRED_IDLE;
 	AlfredDirection direction = ALFRED_DOWN;
@@ -411,6 +418,7 @@ struct PaletteAnim {
 #define PASSERBY_RIGHT 0
 #define PASSERBY_LEFT 1
 #define PASSERBY_DOWN 2
+
 struct PasserByAnim
 {
 	uint32 frameTrigger = 0x3FF;
@@ -484,7 +492,7 @@ struct ResetEntry {
 #define FLAG_PUERTA_BUENA 35
 #define FLAG_PIEDRA_FAKE_MOJADA 34
 #define FLAG_TIENDA_ABIERTA 46
-
+#define FLAG_COMO_ESTAN_LOS_DIOSES 41
 
 
 #define FLAG_VIAJE_A_EGIPTO 12
@@ -503,7 +511,6 @@ struct ResetEntry {
 #define FLAG_A_POR_LA_PRINCESA 38
 #define FLAG_VUELTA_A_EMPEZAR 39
 #define FLAG_A_LOS_PASILLOS 40
-#define FLAG_COMO_ESTAN_LOS_DIOSES 41
 #define FLAG_END_OF_GAME 42
 #define FLAG_FROM_INTRO 43
 #define FLAG_HE_TIRADO_PIEDRA 44
@@ -642,7 +649,6 @@ struct SaveGameData {
 	GameStateData *gameState = nullptr;
 };
 
-
 struct FlightRoomCfg {
 	int roomNumber;
 	int spriteIdx;


Commit: e197092b5ef6296291658c8a8079a01312e4a576
    https://github.com/scummvm/scummvm/commit/e197092b5ef6296291658c8a8079a01312e4a576
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:15+02:00

Commit Message:
PELROCK: Implements inventory overlay when using items

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h
    engines/pelrock/util.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 2effd749c00..c272b57eae4 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -154,7 +154,7 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		setScreen(0, ALFRED_DOWN);
+		setScreen(0, ALFRED_LEFT);
 		// setScreen(3, ALFRED_RIGHT);
 		// setScreen(22, ALFRED_DOWN);
 		// setScreen(41, ALFRED_DOWN);
@@ -164,7 +164,7 @@ void PelrockEngine::init() {
 		// setScreen(52, ALFRED_DOWN);
 		// setScreen(15, ALFRED_DOWN);
 		// setScreen(2, ALFRED_LEFT);
-		// alfredState.x = 576;
+		// _alfredState.x = 576;
 		// alfredState.y = 374;
 	}
 
@@ -1492,11 +1492,18 @@ bool PelrockEngine::isSpriteUnder(Sprite *sprite, int x, int y) {
 	return false;
 }
 
-Common::Point getPositionInBallonForIndex(int i, int x, int y) {
-	return Common::Point(x + 20 + (i * (kVerbIconWidth + 2)), y + 20);
+Common::Point getPositionInBalloonForIndex(int i, int x, int y) {
+	return Common::Point(4 + x + (i * kVerbIconWidth), y + 18);
+}
+
+Common::Rect getActionArea(int x, int y) {
+	Common::Point p1 = getPositionInBalloonForIndex(0, x, y);
+	Common::Point p2 = getPositionInBalloonForIndex(4, x, y);
+	return Common::Rect(p1.x, p1.y, p2.x + kVerbIconWidth, p2.y + kVerbIconHeight);
 }
 
 void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
+
 	drawSpriteToBuffer(_compositeBuffer, 640, _res->_popUpBalloon + (curFrame * kBalloonHeight * kBalloonWidth), posx, posy, kBalloonWidth, kBalloonHeight, 255);
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
 
@@ -1508,23 +1515,29 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 		if (icon == actions[i] && shouldBlink) {
 			continue;
 		}
-		Common::Point p = getPositionInBallonForIndex(i, posx, posy);
+		Common::Point p = getPositionInBalloonForIndex(i, posx, posy);
 		drawSpriteToBuffer(_compositeBuffer, 640, _res->_verbIcons[actions[i]], p.x, p.y, kVerbIconWidth, kVerbIconHeight, 1);
 	}
 
-	if(icon == ITEM) {
-		if(!_inventoryOverlayState.isActive) {
-			_inventoryOverlayState.isActive = true;
-			// _inventoryOverlayState.page =
-		}
+	Common::Rect actionArea = getActionArea(posx, posy);
+	// moving mouse over action area outside of the item closes the inventoryoverlay
+	if (actionArea.contains(_events->_mouseX, _events->_mouseY)) {
+		_inventoryOverlayState.isActive = icon == ITEM;
+	}
+
+	if(_inventoryOverlayState.isActive) {
 		showInventoryOverlay();
+		if(_inventoryOverlayState.posInInventorySelectionArea(_events->_mouseX, _events->_mouseY)) {
+			checkMouseOverInventoryOverlay(_events->_mouseX, _events->_mouseY);
+		}
 	}
 
+
 	if (_state->selectedInventoryItem >= 0 && !_state->inventoryItems.empty()) {
-		if (icon == ITEM && shouldBlink) {
-			return;
+		if (icon != ITEM || !shouldBlink) {
+			Common::Point p = getPositionInBalloonForIndex(actions.size(), posx, posy);
+			drawSpriteToBuffer(_compositeBuffer, 640, _res->getIconForObject(_state->selectedInventoryItem).iconData, p.x, p.y, kVerbIconWidth, kVerbIconHeight, 1);
 		}
-		drawSpriteToBuffer(_compositeBuffer, 640, _res->getIconForObject(_state->selectedInventoryItem).iconData, posx + 20 + (actions.size() * (kVerbIconWidth + 2)), posy + 20, kVerbIconWidth, kVerbIconHeight, 1);
 	}
 
 	if (_actionPopupState.curFrame < 3) {
@@ -1532,8 +1545,6 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	} else {
 		_actionPopupState.curFrame = 0;
 	}
-
-
 }
 
 void PelrockEngine::animateTalkingNPC(Sprite *animSet) {
@@ -1601,12 +1612,44 @@ void PelrockEngine::showInventoryOverlay() {
 
 	for (int i = firstItem; i < invSize && i < firstItem + kInventoryPageSize; i++) {
 		Common::Point p = getPositionInOverlayForIndex(i - firstItem);
+		if( i == _inventoryOverlayState.flashingIconIndex && _chrono->getFrameCount() % kIconBlinkPeriod == 0) {
+			continue;
+		}
 		drawSpriteToBuffer(_compositeBuffer, 640, _res->getIconForObject(_state->inventoryItems[i]).iconData, p.x, p.y, 60, 60, 1);
 	}
+
 	drawSpriteToBuffer(_compositeBuffer, 640, _inventoryOverlayState.arrows[0], 0, 340, 20, 60, 255);
 	drawSpriteToBuffer(_compositeBuffer, 640, _inventoryOverlayState.arrows[1], 620, 340, 20, 60, 255);
 }
 
+void PelrockEngine::checkMouseOverInventoryOverlay(int x, int y) {
+
+	if(x < 20) {
+		// if(_inventoryOverlayState.page > 0) {
+		// 	_inventoryOverlayState.page--;
+		// }
+		debug("left arrow of inventory overlay");
+	} else if(x >= 620) {
+		// uint maxPage = (_state->inventoryItems.size() - 1) / kInventoryPageSize;
+		// if(_inventoryOverlayState.page < maxPage) {
+		// 	_inventoryOverlayState.page++;
+		// }
+		debug("right arrow of inventory overlay");
+	} else {
+		// mouse hover over inventory item, laid out horizontally, y coordinate is not relevant for determining which item is selected
+		int index = (x - 20) / 60 + (_inventoryOverlayState.page * kInventoryPageSize);
+		if(index < _state->inventoryItems.size()) {
+			debug("hovering over inventory item %d at index %d", _state->inventoryItems[index], index);
+			// _state->selectedInventoryItem = index;
+			_inventoryOverlayState.flashingIconIndex = index;
+		} else {
+			debug("hovering over empty slot in inventory overlay, no item at index %d", index);
+			// _state->selectedInventoryItem = -1;
+			_inventoryOverlayState.flashingIconIndex = -1;
+		}
+	}
+}
+
 void PelrockEngine::gameLoop() {
 
 	_events->pollEvent();
@@ -1772,7 +1815,7 @@ VerbIcon PelrockEngine::isActionUnder(int x, int y) {
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
 	int loopEnd = _state->selectedInventoryItem != -1 ? actions.size() + 1 : actions.size();
 	for (int i = 0; i < loopEnd; i++) {
-		Common::Point p = getPositionInBallonForIndex(i, _actionPopupState.x, _actionPopupState.y);
+		Common::Point p = getPositionInBalloonForIndex(i, _actionPopupState.x, _actionPopupState.y);
 		Common::Rect actionRect = Common::Rect(p.x, p.y, p.x + kVerbIconWidth, p.y + kVerbIconHeight);
 		if (i == actions.size()) {
 			// Check inventory item
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index a1e03ef0045..f47ba3be604 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -113,6 +113,8 @@ private:
 
 	void showInventoryOverlay();
 
+	void checkMouseOverInventoryOverlay(int x, int y);
+
 	void gameLoop();
 	void computerLoop();
 	void extraScreenLoop();
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index cbe3cdf7f2f..db650c74b2b 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -21,6 +21,7 @@
 #ifndef PELROCK_TYPES_H
 #define PELROCK_TYPES_H
 
+#include "common/rect.h"
 #include "common/debug.h"
 #include "common/scummsys.h"
 #include "common/system.h"
@@ -163,7 +164,14 @@ struct ActionPopupState {
 struct InventoryOverlayState {
 	bool isActive = false;
 	int page = 0;
+	int flashingIconIndex = -1;
+	Common::Rect inventorySelectionArea = Common::Rect(0, 340, 640, 400);
+	Common::Rect ballonInventoryPath = Common::Rect(0, 0, kBalloonWidth, kBalloonHeight);
 	byte *arrows[2] = { nullptr, nullptr };
+
+	bool posInInventorySelectionArea(int x, int y) {
+		return inventorySelectionArea.contains(x, y);
+	}
 };
 
 struct AlfredState {
@@ -172,7 +180,7 @@ struct AlfredState {
 	int curFrame = 0;
 	uint16 movementSpeedX = 6; // pixels per frame
 	uint16 movementSpeedY = 5; // pixels per frame
-	uint16 x = 319;
+	uint16 x = 330;
 	uint16 y = 302;
 	byte w = kAlfredFrameWidth;
 	byte h = kAlfredFrameHeight;
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 4efb535eb50..c27d22fbe34 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -48,7 +48,7 @@ void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color
 void drawRect(byte *screenBuffer, int x, int y, int w, int h, byte color) {
 	Graphics::Surface surface;
 	surface.create(w, h, Graphics::PixelFormat::createFormatCLUT8());
-	drawRect(&surface, 0, 0, w, h, color);
+	drawRect(&surface, 0, 0, w - 1, h, color);
 
 	for (int py = 0; py < h; py++) {
 		for (int px = 0; px < w; px++) {


Commit: 8ccb1b06a25e18c402b25282cc055503b95e0634
    https://github.com/scummvm/scummvm/commit/8ccb1b06a25e18c402b25282cc055503b95e0634
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:16+02:00

Commit Message:
PELROCK: Implements inventory overlay scroll and click

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index c272b57eae4..8a2842824a4 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -546,11 +546,17 @@ void PelrockEngine::checkMouse() {
 		} else if (_actionPopupState.isAlfredUnder && actionClicked != NO_ACTION) {
 			debug("Using item on Alfred");
 			useOnAlfred(_state->selectedInventoryItem);
+		} else if (_inventoryOverlayState.isActive && _inventoryOverlayState.posInInventorySelectionArea(_events->_releaseX, _events->_releaseY)) {
+			int item = checkMouseClickInventoryOverlay(_events->_releaseX, _events->_releaseY);
+			_state->selectedInventoryItem = item;
+			walkAndAction(_currentHotspot, ITEM);
 		} else {
 			// Released outside popup - just close it
 			_queuedAction = QueuedAction{NO_ACTION, -1, false, false};
 			_currentHotspot = nullptr;
 		}
+
+
 	} else if (_events->_leftMouseClicked) {
 		// Regular click (not during popup mode)
 		checkMouseClick(_events->_mouseClickX, _events->_mouseClickY);
@@ -1523,6 +1529,7 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	// moving mouse over action area outside of the item closes the inventoryoverlay
 	if (actionArea.contains(_events->_mouseX, _events->_mouseY)) {
 		_inventoryOverlayState.isActive = icon == ITEM;
+		_inventoryOverlayState.flashingIconIndex = -1;
 	}
 
 	if(_inventoryOverlayState.isActive) {
@@ -1608,7 +1615,7 @@ void PelrockEngine::pickupIconFlash() {
 void PelrockEngine::showInventoryOverlay() {
 	_graphics->showOverlay(60, _compositeBuffer);
 	uint invSize = _state->inventoryItems.size();
-	int firstItem = _inventoryOverlayState.page * kInventoryPageSize;
+	int firstItem = _inventoryOverlayState.invStartingPos * kInventoryPageSize;
 
 	for (int i = firstItem; i < invSize && i < firstItem + kInventoryPageSize; i++) {
 		Common::Point p = getPositionInOverlayForIndex(i - firstItem);
@@ -1618,26 +1625,28 @@ void PelrockEngine::showInventoryOverlay() {
 		drawSpriteToBuffer(_compositeBuffer, 640, _res->getIconForObject(_state->inventoryItems[i]).iconData, p.x, p.y, 60, 60, 1);
 	}
 
-	drawSpriteToBuffer(_compositeBuffer, 640, _inventoryOverlayState.arrows[0], 0, 340, 20, 60, 255);
-	drawSpriteToBuffer(_compositeBuffer, 640, _inventoryOverlayState.arrows[1], 620, 340, 20, 60, 255);
+	//draw arrows if there are more items to show in either direction
+	if(_inventoryOverlayState.invStartingPos > 0) {
+		 drawSpriteToBuffer(_compositeBuffer, 640, _inventoryOverlayState.arrows[0], 0, 340, 20, 60, 255);
+	}
+	if((firstItem + kInventoryPageSize) < invSize) {
+		drawSpriteToBuffer(_compositeBuffer, 640, _inventoryOverlayState.arrows[1], 620, 340, 20, 60, 255);
+	}
+	// drawSpriteToBuffer(_compositeBuffer, 640, _inventoryOverlayState.arrows[0], 0, 340, 20, 60, 255);
 }
 
 void PelrockEngine::checkMouseOverInventoryOverlay(int x, int y) {
-
 	if(x < 20) {
-		// if(_inventoryOverlayState.page > 0) {
-		// 	_inventoryOverlayState.page--;
-		// }
-		debug("left arrow of inventory overlay");
+		if(_inventoryOverlayState.invStartingPos > 0 && _chrono->getFrameCount() % 2 == 0) {
+			_inventoryOverlayState.invStartingPos--;
+		}
 	} else if(x >= 620) {
-		// uint maxPage = (_state->inventoryItems.size() - 1) / kInventoryPageSize;
-		// if(_inventoryOverlayState.page < maxPage) {
-		// 	_inventoryOverlayState.page++;
-		// }
-		debug("right arrow of inventory overlay");
+		if(_inventoryOverlayState.invStartingPos < (_state->inventoryItems.size() / kInventoryPageSize) && _chrono->getFrameCount() % 2 == 0) {
+			_inventoryOverlayState.invStartingPos++;
+		}
 	} else {
 		// mouse hover over inventory item, laid out horizontally, y coordinate is not relevant for determining which item is selected
-		int index = (x - 20) / 60 + (_inventoryOverlayState.page * kInventoryPageSize);
+		int index = (x - 20) / 60 + (_inventoryOverlayState.invStartingPos * kInventoryPageSize);
 		if(index < _state->inventoryItems.size()) {
 			debug("hovering over inventory item %d at index %d", _state->inventoryItems[index], index);
 			// _state->selectedInventoryItem = index;
@@ -1650,6 +1659,18 @@ void PelrockEngine::checkMouseOverInventoryOverlay(int x, int y) {
 	}
 }
 
+int PelrockEngine::checkMouseClickInventoryOverlay(int x, int y) {
+	if(x < 20) {
+		return -1;
+	} else if(x >= 620) {
+		return -1;
+	} else {
+		// mouse hover over inventory item, laid out horizontally, y coordinate is not relevant for determining which item is selected
+		int index = (x - 20) / 60 + (_inventoryOverlayState.invStartingPos * kInventoryPageSize);
+		return _state->inventoryItems[index];
+	}
+}
+
 void PelrockEngine::gameLoop() {
 
 	_events->pollEvent();
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index f47ba3be604..a32bc60b0d3 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -114,6 +114,7 @@ private:
 	void showInventoryOverlay();
 
 	void checkMouseOverInventoryOverlay(int x, int y);
+	int checkMouseClickInventoryOverlay(int x, int y);
 
 	void gameLoop();
 	void computerLoop();
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index db650c74b2b..41dfea8d976 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -163,7 +163,7 @@ struct ActionPopupState {
 
 struct InventoryOverlayState {
 	bool isActive = false;
-	int page = 0;
+	int invStartingPos = 0;
 	int flashingIconIndex = -1;
 	Common::Rect inventorySelectionArea = Common::Rect(0, 340, 640, 400);
 	Common::Rect ballonInventoryPath = Common::Rect(0, 0, kBalloonWidth, kBalloonHeight);


Commit: 1f188c55f886920db6a120125f2790e190a4a722
    https://github.com/scummvm/scummvm/commit/1f188c55f886920db6a120125f2790e190a4a722
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:16+02:00

Commit Message:
PELROCK: Scroll dialog choices

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 400cb513941..7be58fa128d 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -20,8 +20,8 @@
  */
 #include "common/stack.h"
 
-#include "pelrock/dialog.h"
 #include "dialog.h"
+#include "pelrock/dialog.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/util.h"
@@ -117,14 +117,44 @@ void DialogManager::displayChoices(Common::Array<ChoiceOption> *choices, byte *c
 	Common::Point overlayPos = _graphics->showOverlay(overlayHeight, compositeBuffer);
 	for (uint i = 0; i < choices->size(); i++) {
 		ChoiceOption choice = (*choices)[i];
-		int choicePadding = 32;
 		int width = g_engine->_doubleSmallFont->getStringWidth(choice.text);
-		Common::Rect bbox(0, overlayPos.y + i * kChoiceHeight, width + choicePadding * 2, overlayPos.y + i * kChoiceHeight + kChoiceHeight);
-		int color = 14;
+		int yPos = overlayPos.y + 2 + i * kChoiceHeight;
+		Common::Rect bbox(kChoicePadding, yPos, kChoicePadding + 600, yPos + kChoiceHeight);
+		Common::Rect leftArrowBox(0, yPos, kChoicePadding, yPos + kChoiceHeight);
+		Common::Rect rightArrowBox(640 - kChoicePadding, yPos, 640, yPos + kChoiceHeight);
+		int choiceColor = 14;
+		int lArrowColor = 14;
+		int rArrowColor = 14;
+
 		if (bbox.contains(_events->_mouseX, _events->_mouseY)) {
-			color = 15;
+			choiceColor = 15;
+		} else if (leftArrowBox.contains(_events->_mouseX, _events->_mouseY)) {
+			if (choice.charOffset > 0) {
+				choice.charOffset--;
+				choices->remove_at(i);
+				choices->insert_at(i, choice);
+			}
+			lArrowColor = 15;
+		} else if (rightArrowBox.contains(_events->_mouseX, _events->_mouseY)) {
+			if (i == 0) {
+				debug("First choice charOffset %d, text length %d", choice.charOffset, choice.text.size());
+			}
+			if (choice.charOffset + 76 < choice.text.size()) {
+				choice.charOffset = choice.charOffset + 1;
+				choices->remove_at(i);
+				choices->insert_at(i, choice);
+				debug("Right arrow clicked, new charOffset %d, text length %d", choice.charOffset, choice.text.size());
+			}
+			rArrowColor = 15;
+		}
+
+		if (choice.charOffset > 0) {
+			drawText(compositeBuffer, g_engine->_doubleSmallFont, _leftArrow, 0, yPos, g_engine->_doubleSmallFont->getCharWidth(17), lArrowColor);
+		}
+		drawText(compositeBuffer, g_engine->_doubleSmallFont, choice.text.substr(choice.charOffset, 76), kChoicePadding, yPos, 620, choiceColor);
+		if (choice.charOffset + 76 < choice.text.size()) {
+			drawText(compositeBuffer, g_engine->_doubleSmallFont, _rightArrow, 640 - kArrowWidth, yPos, g_engine->_doubleSmallFont->getCharWidth(16), rArrowColor);
 		}
-		drawText(compositeBuffer, g_engine->_doubleSmallFont, choice.text, choicePadding, overlayPos.y + 2 + i * kChoiceHeight, 620, color);
 	}
 }
 
@@ -216,10 +246,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 
 		_screen->transBlitFrom(s, s.getRect(), Common::Point(xPos, yPos), 255);
 		drawPos(_screen, xPos, yPos, speakerId);
-		// drawRect(_screen, xPos, yPos,
-		// 		 s.getRect().width(),
-		// 		 s.getRect().height(), speakerId);
-		// Present to screen
+
 		_screen->markAllDirty();
 		_screen->update();
 
@@ -368,8 +395,10 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 						}
 						textPos++;
 					}
-					if (!opt.isDisabled)
+					if (!opt.isDisabled) {
+						opt.text = "  " + opt.text; // Adds three spaces for padding to allow for scrolling past the indentation
 						outChoices->push_back(opt);
+					}
 				} else if (choiceIndex < firstChoiceIndex) {
 					// Hit a choice at a LOWER level - stop scanning
 					// This means we've gone past all choices at our level
@@ -532,11 +561,11 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 
 		if (choices->empty()) {
 			state.position = positionStack.empty() ? 0 : positionStack.pop();
-			if(state.position == 0) {
+			if (state.position == 0) {
 				debug("No choices and no previous position to go back to, ending conversation");
 				break;
 			}
-			checkAllSubBranchesExhausted(conversationData, dataSize, state.position, state.currentChoiceLevel-1);
+			checkAllSubBranchesExhausted(conversationData, dataSize, state.position, state.currentChoiceLevel - 1);
 			debug("No choices found, popping back to previous choice menu, position %u", state.position);
 			skipToChoices = true;
 			// state.position = peekPos;
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 0ebc5f0362b..85362b44dd5 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -67,6 +67,9 @@ struct ConversationEndResult {
 };
 
 class DialogManager {
+	const static int kMaxChoiceChars = 50; // Max characters to show for a choice option (for truncation)
+	const static int kArrowWidth = 8;      // Width of arrow character for scroll
+	const static int kChoicePadding = 16;  // padding for the choice text surface
 private:
 	Graphics::Screen *_screen = nullptr;
 	PelrockEventManager *_events = nullptr;
@@ -87,12 +90,12 @@ private:
 	uint32 skipControlBytes(const byte *data, uint32 dataSize, uint32 position);
 	uint32 peekNextMeaningfulByte(const byte *data, uint32 dataSize, uint32 position);
 	ConversationState initializeConversation(const byte *data, uint32 dataSize, byte npcIndex);
-	bool handleGoBack(const byte *data, Common::Stack<uint32> &positionStack,  uint32 position, ConversationState &state);
+	bool handleGoBack(const byte *data, Common::Stack<uint32> &positionStack, uint32 position, ConversationState &state);
 	uint32 readAndDisplayDialogue(const byte *data, uint32 dataSize, uint32 position);
 	ConversationEndResult checkConversationEnd(const byte *data, uint32 dataSize, uint32 position);
 	void addGoodbyeOptionIfNeeded(Common::Array<ChoiceOption> *choices, int currentChoiceLevel, uint originalChoiceCount);
 	uint32 processChoiceSelection(const byte *data, uint32 dataSize, Common::Array<ChoiceOption> *choices, int selectedIndex, ConversationState &state);
-	void disableChoiceIfNeeded(Common::Array<Pelrock::ChoiceOption> * choices, int selectedIndex, const byte * data, uint32 dataSize, uint32 endPos, Pelrock::ConversationState & state);
+	void disableChoiceIfNeeded(Common::Array<Pelrock::ChoiceOption> *choices, int selectedIndex, const byte *data, uint32 dataSize, uint32 endPos, Pelrock::ConversationState &state);
 
 public:
 	DialogManager(Graphics::Screen *screen, PelrockEventManager *events, GraphicsManager *graphics);
@@ -119,6 +122,9 @@ public:
 
 	// True while a blocking dialog or conversation is on screen.
 	bool _dialogActive = false;
+
+	Common::String _leftArrow = Common::String(17);
+	Common::String _rightArrow = Common::String(16);
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 8a2842824a4..9b590d96be2 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -154,9 +154,9 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		setScreen(0, ALFRED_LEFT);
+		// setScreen(0, ALFRED_LEFT);
 		// setScreen(3, ALFRED_RIGHT);
-		// setScreen(22, ALFRED_DOWN);
+		setScreen(22, ALFRED_DOWN);
 		// setScreen(41, ALFRED_DOWN);
 		// setScreen(43, ALFRED_DOWN);
 		// setScreen(46, ALFRED_RIGHT);
@@ -173,7 +173,7 @@ void PelrockEngine::init() {
 
 void PelrockEngine::loadInventoryArrows() {
 	Common::File alfred7;
-	if(!alfred7.open("ALFRED.7")) {
+	if (!alfred7.open("ALFRED.7")) {
 		error("Failed to open ALFRED.7 to load inventory arrows");
 		return;
 	}
@@ -556,7 +556,6 @@ void PelrockEngine::checkMouse() {
 			_currentHotspot = nullptr;
 		}
 
-
 	} else if (_events->_leftMouseClicked) {
 		// Regular click (not during popup mode)
 		checkMouseClick(_events->_mouseClickX, _events->_mouseClickY);
@@ -1532,14 +1531,13 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 		_inventoryOverlayState.flashingIconIndex = -1;
 	}
 
-	if(_inventoryOverlayState.isActive) {
+	if (_inventoryOverlayState.isActive) {
 		showInventoryOverlay();
-		if(_inventoryOverlayState.posInInventorySelectionArea(_events->_mouseX, _events->_mouseY)) {
+		if (_inventoryOverlayState.posInInventorySelectionArea(_events->_mouseX, _events->_mouseY)) {
 			checkMouseOverInventoryOverlay(_events->_mouseX, _events->_mouseY);
 		}
 	}
 
-
 	if (_state->selectedInventoryItem >= 0 && !_state->inventoryItems.empty()) {
 		if (icon != ITEM || !shouldBlink) {
 			Common::Point p = getPositionInBalloonForIndex(actions.size(), posx, posy);
@@ -1619,35 +1617,35 @@ void PelrockEngine::showInventoryOverlay() {
 
 	for (int i = firstItem; i < invSize && i < firstItem + kInventoryPageSize; i++) {
 		Common::Point p = getPositionInOverlayForIndex(i - firstItem);
-		if( i == _inventoryOverlayState.flashingIconIndex && _chrono->getFrameCount() % kIconBlinkPeriod == 0) {
+		if (i == _inventoryOverlayState.flashingIconIndex && _chrono->getFrameCount() % kIconBlinkPeriod == 0) {
 			continue;
 		}
 		drawSpriteToBuffer(_compositeBuffer, 640, _res->getIconForObject(_state->inventoryItems[i]).iconData, p.x, p.y, 60, 60, 1);
 	}
 
-	//draw arrows if there are more items to show in either direction
-	if(_inventoryOverlayState.invStartingPos > 0) {
-		 drawSpriteToBuffer(_compositeBuffer, 640, _inventoryOverlayState.arrows[0], 0, 340, 20, 60, 255);
+	// draw arrows if there are more items to show in either direction
+	if (_inventoryOverlayState.invStartingPos > 0) {
+		drawSpriteToBuffer(_compositeBuffer, 640, _inventoryOverlayState.arrows[0], 0, 340, 20, 60, 255);
 	}
-	if((firstItem + kInventoryPageSize) < invSize) {
+	if ((firstItem + kInventoryPageSize) < invSize) {
 		drawSpriteToBuffer(_compositeBuffer, 640, _inventoryOverlayState.arrows[1], 620, 340, 20, 60, 255);
 	}
 	// drawSpriteToBuffer(_compositeBuffer, 640, _inventoryOverlayState.arrows[0], 0, 340, 20, 60, 255);
 }
 
 void PelrockEngine::checkMouseOverInventoryOverlay(int x, int y) {
-	if(x < 20) {
-		if(_inventoryOverlayState.invStartingPos > 0 && _chrono->getFrameCount() % 2 == 0) {
+	if (x < 20) {
+		if (_inventoryOverlayState.invStartingPos > 0 && _chrono->getFrameCount() % 2 == 0) {
 			_inventoryOverlayState.invStartingPos--;
 		}
-	} else if(x >= 620) {
-		if(_inventoryOverlayState.invStartingPos < (_state->inventoryItems.size() / kInventoryPageSize) && _chrono->getFrameCount() % 2 == 0) {
+	} else if (x >= 620) {
+		if (_inventoryOverlayState.invStartingPos < (_state->inventoryItems.size() / kInventoryPageSize) && _chrono->getFrameCount() % 2 == 0) {
 			_inventoryOverlayState.invStartingPos++;
 		}
 	} else {
 		// mouse hover over inventory item, laid out horizontally, y coordinate is not relevant for determining which item is selected
 		int index = (x - 20) / 60 + (_inventoryOverlayState.invStartingPos * kInventoryPageSize);
-		if(index < _state->inventoryItems.size()) {
+		if (index < _state->inventoryItems.size()) {
 			debug("hovering over inventory item %d at index %d", _state->inventoryItems[index], index);
 			// _state->selectedInventoryItem = index;
 			_inventoryOverlayState.flashingIconIndex = index;
@@ -1660,9 +1658,9 @@ void PelrockEngine::checkMouseOverInventoryOverlay(int x, int y) {
 }
 
 int PelrockEngine::checkMouseClickInventoryOverlay(int x, int y) {
-	if(x < 20) {
+	if (x < 20) {
 		return -1;
-	} else if(x >= 620) {
+	} else if (x >= 620) {
 		return -1;
 	} else {
 		// mouse hover over inventory item, laid out horizontally, y coordinate is not relevant for determining which item is selected
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 41dfea8d976..22f7d967f88 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -21,8 +21,8 @@
 #ifndef PELROCK_TYPES_H
 #define PELROCK_TYPES_H
 
-#include "common/rect.h"
 #include "common/debug.h"
+#include "common/rect.h"
 #include "common/scummsys.h"
 #include "common/system.h"
 #include "common/types.h"
@@ -81,7 +81,7 @@ const int kAlfredFrameHeight = 102;
 
 const int kChoiceHeight = 16; // Height of each choice line in pixels
 
-const int kTalkAnimationSpeed = 2; // Frames per update
+const int kTalkAnimationSpeed = 2;   // Frames per update
 const int kAlfredAnimationSpeed = 2; // Frames per update
 
 const int kAlfredIdleAnimationFrameCount = 300;
@@ -167,7 +167,7 @@ struct InventoryOverlayState {
 	int flashingIconIndex = -1;
 	Common::Rect inventorySelectionArea = Common::Rect(0, 340, 640, 400);
 	Common::Rect ballonInventoryPath = Common::Rect(0, 0, kBalloonWidth, kBalloonHeight);
-	byte *arrows[2] = { nullptr, nullptr };
+	byte *arrows[2] = {nullptr, nullptr};
 
 	bool posInInventorySelectionArea(int x, int y) {
 		return inventorySelectionArea.contains(x, y);
@@ -246,10 +246,10 @@ struct Sprite {
 	uint16 stride; // 6-7
 	int numAnims;  // 8
 	int curAnimIndex = 0;
-	int8 zOrder; //32-33
+	int8 zOrder; // 32-33
 
-	byte actionFlags;       // 34
-	bool isHotspotDisabled; // 38
+	byte actionFlags;                  // 34
+	bool isHotspotDisabled;            // 38
 	bool disableAfterSequence = false; // 39
 	bool isTalking = false;
 	byte talkingAnimIndex = 0;
@@ -321,8 +321,8 @@ struct WalkBox {
 struct QueuedAction {
 	VerbIcon verb;
 	int hotspotIndex;
-	bool isQueued;        // Alfred is walking/interacting toward the target
-	bool readyToExecute;  // Animation done - execute after the current renderScene
+	bool isQueued;       // Alfred is walking/interacting toward the target
+	bool readyToExecute; // Animation done - execute after the current renderScene
 };
 
 struct ScalingParams {
@@ -346,14 +346,12 @@ enum GameState {
 	COMPUTER = 104
 };
 
-struct SpriteChange
-{
+struct SpriteChange {
 	byte roomNumber;
 	byte spriteIndex;
 	byte zIndex;
 };
 
-
 struct HotSpotChange {
 	byte roomNumber;
 	byte hotspotIndex;
@@ -427,8 +425,7 @@ struct PaletteAnim {
 #define PASSERBY_LEFT 1
 #define PASSERBY_DOWN 2
 
-struct PasserByAnim
-{
+struct PasserByAnim {
 	uint32 frameTrigger = 0x3FF;
 	int16 startX;
 	int16 startY;
@@ -447,7 +444,6 @@ struct RoomPasserBys {
 	RoomPasserBys(byte roomNum, byte numAnims) : roomNumber(roomNum), numAnims(numAnims) {}
 };
 
-
 /**
  * Structure to hold a parsed choice option
  */
@@ -460,6 +456,7 @@ struct ChoiceOption {
 	bool shouldDisableOnSelect = false;
 	bool hasConversationEndMarker = false;
 	bool isTerminator = false;
+	int charOffset = 0;
 
 	ChoiceOption() : choiceIndex(-1), dataOffset(0) {}
 };
@@ -502,7 +499,6 @@ struct ResetEntry {
 #define FLAG_TIENDA_ABIERTA 46
 #define FLAG_COMO_ESTAN_LOS_DIOSES 41
 
-
 #define FLAG_VIAJE_A_EGIPTO 12
 #define FLAG_PUERTA_SECRETA_ABIERTA 16
 #define FLAG_VIGILANTE_MEANDO 22
@@ -635,7 +631,6 @@ struct GameStateData {
 		return -1;
 	}
 
-
 	int booksInInventory() {
 		int l = inventoryItems.size();
 		int count = 0;
@@ -646,7 +641,6 @@ struct GameStateData {
 		}
 		return count;
 	}
-
 };
 
 struct SaveGameData {


Commit: d574cf97d001699a53fb9ee8abb1eeea9d757d33
    https://github.com/scummvm/scummvm/commit/d574cf97d001699a53fb9ee8abb1eeea9d757d33
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:16+02:00

Commit Message:
PELROCK: add correct sizing to dialogue surface

Changed paths:
    engines/pelrock/dialog.cpp


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 7be58fa128d..25c6108a79c 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -161,7 +161,7 @@ void DialogManager::displayChoices(Common::Array<ChoiceOption> *choices, byte *c
 Graphics::Surface DialogManager::getDialogueSurface(Common::Array<Common::String> dialogueLines, byte speakerId) {
 
 	int maxWidth = 0;
-	int height = dialogueLines.size() * 24;
+	int height = dialogueLines.size() * 25; // Add some padding
 	for (int i = 0; i < dialogueLines.size(); i++) {
 		maxWidth = MAX(maxWidth, g_engine->_largeFont->getStringWidth(dialogueLines[i]));
 	}


Commit: 3d1eb5b3882799002411d36d6b72e39aee24b36c
    https://github.com/scummvm/scummvm/commit/3d1eb5b3882799002411d36d6b72e39aee24b36c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:16+02:00

Commit Message:
PELROCK: Implements missing actions

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 3b743ef4e48..877827a4efe 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -46,6 +46,7 @@ const ActionEntry actionTable[] = {
 	// Room 1
 	{4, PICKUP, &PelrockEngine::pickUpBrick}, // Brick
 	{277, OPEN, &PelrockEngine::openIceCreamShopDoor},
+	{273, PICKUP, &PelrockEngine::pickupGarbageCan},
 	// Room 2
 	{282, OPEN, &PelrockEngine::openMcDoor},
 	{282, CLOSE, &PelrockEngine::closeMcDoor},
@@ -85,6 +86,8 @@ const ActionEntry actionTable[] = {
 	{315, OPEN, &PelrockEngine::openPlug},
 	{316, PICKUP, &PelrockEngine::pickCables},
 	{312, OPEN, &PelrockEngine::openMuseumDoor},
+	{310, PICKUP, &PelrockEngine::pickupFruit},
+	{311, PICKUP, &PelrockEngine::pickupFruit},
 
 	// // Room 5
 	// {},
@@ -756,6 +759,10 @@ void PelrockEngine::openIceCreamShopDoor(HotSpot *hotspot) {
 	_dialog->say(_res->_ingameTexts[HELADERIA_CERRADA]);
 }
 
+void PelrockEngine::pickupGarbageCan(HotSpot *hotspot) {
+	_dialog->say(_res->_ingameTexts[POBRE_PERO_NO_HE_LLEGADO_A_ESO]);
+}
+
 void PelrockEngine::closeRoomDrawer(HotSpot *hotspot) {
 	if (!_room->hasSticker(91)) {
 		_dialog->say(_res->_ingameTexts[YA_CERRADO_M]);
@@ -1082,6 +1089,10 @@ void PelrockEngine::openMuseumDoor(HotSpot *hotspot) {
 	}
 }
 
+void PelrockEngine::pickupFruit(HotSpot *hotspot) {
+	_dialog->say(_res->_ingameTexts[NO_THEY_MAKEYOU_FAT]);
+}
+
 void PelrockEngine::useAmuletWithStatue(int inventoryObject, HotSpot *hotspot) {
 
 	if (!_room->hasSticker(24)) {
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index a32bc60b0d3..435969c995f 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -299,6 +299,7 @@ public:
 	void pickYellowBook(HotSpot *hotspot);
 	void pickUpBrick(HotSpot *hotspot);
 	void openIceCreamShopDoor(HotSpot *hotspot);
+	void pickupGarbageCan(HotSpot *hotspot);
 	void noOpAction(HotSpot *hotspot);
 	void noOpItem(int item, HotSpot *hotspot);
 	void useOnAlfred(int inventoryObject);
@@ -326,6 +327,7 @@ public:
 	void unlockMuseum();
 	void giveMoneyToGuard(int inventoryObject, HotSpot *hotspot);
 	void openMuseumDoor(HotSpot *hotspot);
+	void pickupFruit(HotSpot *hotspot);
 	void useAmuletWithStatue(int inventoryObject, HotSpot *hotspot);
 	void useSecretCodeWithStatue(int inventoryObject, HotSpot *hotspot);
 	void pickUpLetter(HotSpot *hotspot);


Commit: 121b734d3442c243d8b1bce9abb2398389ce004b
    https://github.com/scummvm/scummvm/commit/121b734d3442c243d8b1bce9abb2398389ce004b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:17+02:00

Commit Message:
PELROCK: Credits in main menu

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index acdf8d1ded0..0840da818c4 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -84,6 +84,8 @@ void MenuManager::checkMouseClick(int x, int y) {
 	switch (button) {
 	case QUESTION_MARK_BUTTON:
 		debug("Show credits");
+		_events->_leftMouseClicked = false;
+		showCredits();
 		break;
 	case INVENTORY_PREV_BUTTON:
 		if (_curInventoryPage > 0)
@@ -107,6 +109,46 @@ void MenuManager::checkMouseClick(int x, int y) {
 	}
 }
 
+void MenuManager::showCredits() {
+	memset(_compositeBuffer, 0, 640 * 400);
+	Common::File alfred7;
+	if (!alfred7.open(Common::Path("ALFRED.7"))) {
+		error("Could not open ALFRED.7");
+		return;
+	}
+
+	alfred7.seek(kCreditsBackgroundOffset, SEEK_SET);
+	alfred7.read(_compositeBuffer, 640 * 400);
+	byte *creditsBuf = nullptr;
+	size_t creditsSize = 0;
+	int numCredits = 29;
+	int creditWidth = 240;
+	int creditHeight = 22;
+	readUntilBuda(&alfred7, kCreditsBackgroundOffset + 256000, creditsBuf, creditsSize);
+	byte *decompressedCredits = nullptr;
+	rleDecompress(creditsBuf, creditsSize, 0, 0, &decompressedCredits, true);
+	// draw credits in two columns taking the entire height of the screen and stating in y = 0
+	for(int i = 0; i < 34; i++) {
+		byte *singleCredit = new byte[creditWidth * creditHeight];
+		int x = (i < 34 / 2) ? 39 : 359;
+		int y = 3 + (i % (34 / 2)) * (400 / (34 / 2));
+		extractSingleFrame(decompressedCredits, singleCredit, kCreditsOrder[i], creditWidth, creditHeight);
+		drawSpriteToBuffer(_compositeBuffer, 640, singleCredit, x, y, creditWidth, creditHeight, 255);
+		delete[] singleCredit;
+	}
+
+	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
+	delete[] decompressedCredits;
+	delete[] creditsBuf;
+
+	while(!g_engine->shouldQuit() && !_events->_leftMouseClicked && !_events->_rightMouseClicked) {
+		_events->pollEvent();
+		_screen->markAllDirty();
+		_screen->update();
+		g_system->delayMillis(10);
+	}
+}
+
 bool MenuManager::selectInventoryItem(int i) {
 	if (_curInventoryPage * 4 + i >= g_engine->_state->inventoryItems.size())
 		return false;
@@ -123,6 +165,7 @@ void MenuManager::menuLoop() {
 
 	g_system->getPaletteManager()->setPalette(_mainMenuPalette, 0, 256);
 	g_engine->changeCursor(DEFAULT);
+
 	while (!g_engine->shouldQuit() && !_events->_rightMouseClicked) {
 
 		_events->pollEvent();
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 0c097817f77..30d6d5cc1db 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -33,6 +33,7 @@ namespace Pelrock {
 const int kQuestionMarkOffset = 3214046;
 const int kInvLeftArrowOffset = 3215906;
 const int kTransparentColor = 15;
+const uint32 kCreditsBackgroundOffset = 3271454;
 
 enum MenuButton {
 	QUESTION_MARK_BUTTON,
@@ -47,6 +48,43 @@ enum MenuButton {
 	NO_BUTTON
 };
 
+static const int kCreditsOrder[34] = {
+	5, // PROGRAMACION
+	 8, // Juan Jose Gil
+	 20, // Jose Vicente Pons
+	 24, // LuisFer Fernandez
+	 22, // Fernando Perez
+	 7, // GRAFICOS
+	12, // Queral,
+	14, // Ana maria polo
+	18, // Juan Arocas
+	16, // Gost
+	26, // Astorga
+	28, // Santi Sanz
+	2, // Fernando Aparicio
+	11,// INTRODUCCION
+	12, // Queral,
+	26, // Astorga
+	28, // Santi Sanz
+	9, // MUSICA
+	6, // Rufino Acosta
+	13, // GUION
+	8, // Juan Jose Gil
+	19, // DIALOGOS
+	4, // Vicent raul arnau,
+	8, // Juan Jose Gil,
+	21, // PROBADORES
+	0, //David Burgos
+	10, // Alberto Leon
+	1, // Carles Pons
+	3, // Roman Pons
+	25, // Andres Ruiz,
+	27, // Juan Jose Ruiz
+	23, // Marilo
+	15, // PRODUCCION
+	17 // DDM
+};
+
 static const char *inventorySounds[113] = {
 
 	"HOJASZZZ.SMP", // 0 - Default leaf rustle
@@ -176,6 +214,7 @@ public:
 
 private:
 	void checkMouseClick(int x, int y);
+	void showCredits();
 	bool selectInventoryItem(int i);
 	void loadMenuTexts();
 	void cleanUp();


Commit: 87769a6d090a22faad5163b29db46e0a55241087
    https://github.com/scummvm/scummvm/commit/87769a6d090a22faad5163b29db46e0a55241087
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:17+02:00

Commit Message:
PELROCK: Refactor alfred special anim code into its own function

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 877827a4efe..5425de59fde 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -647,9 +647,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_sound->playSound("TWANGZZZ.SMP", 0);
 		break;
 	case 376: {
-		_res->loadAlfredSpecialAnim(14);
-		_alfredState.animState = ALFRED_SPECIAL_ANIM;
-		waitForSpecialAnimation();
+		playAlfredSpecialAnim(14);
 		loadExtraScreenAndPresent(12);
 		_state->setCurrentRoot(45, 2, 0);
 	} break;
@@ -876,13 +874,8 @@ void PelrockEngine::openLamppost(HotSpot *hotspot) {
 
 void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
 
-	// TODO: Play Alfred's throwing animation
-	// This would require adding a new special animation entry
-	_res->loadAlfredSpecialAnim(4);
-	_alfredState.animState = ALFRED_SPECIAL_ANIM;
-	waitForSpecialAnimation();
+	playAlfredSpecialAnim(4);
 
-	// TODO: Animate sprite 8 (brick projectile) moving to window
 	Sprite *brickSprite = _room->findSpriteByIndex(7);
 	HotSpot *windowHotspot = _room->findHotspotByExtra(294);
 	brickSprite->x = 420;
@@ -892,17 +885,14 @@ void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
 	while (!shouldQuit()) {
 		_events->pollEvent();
 		renderScene(OVERLAY_NONE);
-		// if (_chrono->_gameTick) {
 		_room->findSpriteByIndex(7)->y -= 10;
 		if (_room->findSpriteByIndex(7)->y <= 70) {
 			_room->findSpriteByIndex(7)->zOrder = -1;
 			break;
 		}
-		// }
 		_screen->update();
 		g_system->delayMillis(10);
 	}
-	// This would involve loading and animating the room sprite
 
 	// Add the broken window sticker
 	_room->addSticker(11);
@@ -1011,24 +1001,17 @@ void PelrockEngine::pickCables(HotSpot *hotspot) {
 		return;
 	}
 	// Duck to pick cables
-	_res->loadAlfredSpecialAnim(2);
-	_alfredState.animState = ALFRED_SPECIAL_ANIM;
-	waitForSpecialAnimation();
-
+	playAlfredSpecialAnim(2);
 	// electric shock
 	int prevX = _alfredState.x;
 	_alfredState.x -= 20;
 	// original incorrectly played door closing sound here
 	_sound->playSound("ELEC3ZZZ.SMP", 0);
-	_res->loadAlfredSpecialAnim(3);
-	_alfredState.animState = ALFRED_SPECIAL_ANIM;
-	waitForSpecialAnimation();
+	playAlfredSpecialAnim(3);
 	_alfredState.x = prevX;
 
 	// Stand up (reverse of duck)
-	_res->loadAlfredSpecialAnim(2, true);
-	_alfredState.animState = ALFRED_SPECIAL_ANIM;
-	waitForSpecialAnimation();
+	playAlfredSpecialAnim(2, true);
 	_room->addSticker(21);
 
 	_dialog->say(_res->_ingameTexts[RELOJ_HA_CAMBIADO]);
@@ -1194,11 +1177,9 @@ void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
 	_sound->playMusicTrack(27);
 	checkIngredients();
 	_dialog->say(_res->_ingameTexts[CUIDADOIMPRUDENTE]);
-	_res->loadAlfredSpecialAnim(5);
-	_alfredState.animState = ALFRED_SPECIAL_ANIM;
 	_alfredState.x -= 10;
 	_alfredState.y += 20;
-	waitForSpecialAnimation();
+	playAlfredSpecialAnim(5);
 	_sound->playSound(_room->_roomSfx[0], 0); // Belch
 	waitForSoundEnd();
 	_graphics->fadeToBlack(10);
@@ -1209,6 +1190,12 @@ void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
 	_dialog->say(_res->_ingameTexts[QUEOSCUROESTAESTO]);
 }
 
+void PelrockEngine::playAlfredSpecialAnim(int anim, bool reverse) {
+	_res->loadAlfredSpecialAnim(anim, reverse);
+	_alfredState.animState = ALFRED_SPECIAL_ANIM;
+	waitForSpecialAnimation();
+}
+
 void PelrockEngine::waitForSoundEnd() {
 	while (!shouldQuit() && _sound->isPlaying(0)) {
 		_events->pollEvent();
@@ -1406,14 +1393,10 @@ void PelrockEngine::useDollWithBed(int inventoryObject, HotSpot *hotspot) {
 	int y = _alfredState.y;
 	_alfredState.x -= 36;
 	_alfredState.y += 7;
-	_res->loadAlfredSpecialAnim(11);
-	_alfredState.animState = ALFRED_SPECIAL_ANIM;
-	waitForSpecialAnimation();
+	playAlfredSpecialAnim(11);
 	_alfredState.x -= 4;
 	_alfredState.y += 12;
-	_res->loadAlfredSpecialAnim(12);
-	_alfredState.animState = ALFRED_SPECIAL_ANIM;
-	waitForSpecialAnimation();
+	playAlfredSpecialAnim(12);
 	_alfredState.direction = ALFRED_DOWN;
 	_state->setFlag(FLAG_SE_HA_PUESTO_EL_MUNECO, true);
 	_state->removeInventoryItem(83);
@@ -1935,9 +1918,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		_dialog->say(_res->_ingameTexts[PERIODICOSENSACIONALISTA], 1);
 		break;
 	case 63: // Recipe
-		_res->loadAlfredSpecialAnim(1);
-		_alfredState.animState = ALFRED_SPECIAL_ANIM;
-		waitForSpecialAnimation();
+		playAlfredSpecialAnim(1);
 
 		loadExtraScreenAndPresent(3);
 		_state->setCurrentRoot(17, 1, 0);
@@ -1947,22 +1928,16 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		break;
 	case 59: // Recipe book
 		if (!_state->hasInventoryItem(64)) {
-			_res->loadAlfredSpecialAnim(0);
-			_alfredState.animState = ALFRED_SPECIAL_ANIM;
-			waitForSpecialAnimation();
+			playAlfredSpecialAnim(0);
 			_dialog->say(_res->_ingameTexts[HOJAENTREPAGINAS]);
 			addInventoryItem(64);
 		} else {
-			_res->loadAlfredSpecialAnim(0);
-			_alfredState.animState = ALFRED_SPECIAL_ANIM;
-			waitForSpecialAnimation();
+			playAlfredSpecialAnim(0);
 			_dialog->say(_res->_ingameTexts[NOENTIENDONADA]);
 		}
 		break;
 	case 17: // Egyptian book
-		_res->loadAlfredSpecialAnim(0);
-		_alfredState.animState = ALFRED_SPECIAL_ANIM;
-		waitForSpecialAnimation();
+		playAlfredSpecialAnim(0);
 		_dialog->say(_res->_ingameTexts[YASEEGIPCIO]);
 		_state->setFlag(FLAG_ALFRED_SABE_EGIPCIO, true);
 		break;
@@ -1971,18 +1946,14 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 			_dialog->say(_res->_ingameTexts[CAPITULOPARADOJAS]);
 			_state->setCurrentRoot(25, 44, 0);
 		} else {
-			_res->loadAlfredSpecialAnim(0);
-			_alfredState.animState = ALFRED_SPECIAL_ANIM;
-			waitForSpecialAnimation();
+			playAlfredSpecialAnim(0);
 			_dialog->say(_res->_ingameTexts[COSASAPRENDIDO]);
 			_state->setFlag(FLAG_ALFRED_INTELIGENTE, true);
 			_state->setCurrentRoot(14, 2, 0);
 		}
 		break;
 	case 64:
-		_res->loadAlfredSpecialAnim(0);
-		_alfredState.animState = ALFRED_SPECIAL_ANIM;
-		waitForSpecialAnimation();
+		playAlfredSpecialAnim(0);
 		loadExtraScreenAndPresent(5);
 		if (_state->getFlag(FLAG_ALFRED_SABE_EGIPCIO)) {
 			_dialog->say(_res->_ingameTexts[FORMULAVIAJETIEMPO]);
@@ -1992,9 +1963,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		break;
 	case 88: {
 		SpellBook spellBook = SpellBook(_events, _res);
-		_res->loadAlfredSpecialAnim(0);
-		_alfredState.animState = ALFRED_SPECIAL_ANIM;
-		waitForSpecialAnimation();
+		playAlfredSpecialAnim(0);
 
 		Spell *spell = spellBook.run();
 		if (spell) {
@@ -2061,15 +2030,11 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		break;
 	}
 	case 0: // yellow book
-		_res->loadAlfredSpecialAnim(0);
-		_alfredState.animState = ALFRED_SPECIAL_ANIM;
-		waitForSpecialAnimation();
+		playAlfredSpecialAnim(0);
 		_dialog->say(_res->_ingameTexts[CUENTOPARECIDO]);
 		break;
 	case 101: // combination
-		_res->loadAlfredSpecialAnim(1);
-		_alfredState.animState = ALFRED_SPECIAL_ANIM;
-		waitForSpecialAnimation();
+		playAlfredSpecialAnim(1);
 		_dialog->say(_res->_ingameTexts[PARECE_COMBINACION_CAJAFUERTE]);
 		_state->setFlag(FLAG_CLAVE_CAJA_FUERTE, true);
 		break;
@@ -2097,16 +2062,12 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		break;
 	}
 	case 84: {
-		_res->loadAlfredSpecialAnim(1);
-		_alfredState.animState = ALFRED_SPECIAL_ANIM;
-		waitForSpecialAnimation();
+		playAlfredSpecialAnim(1);
 		loadExtraScreenAndPresent(7);
 		break;
 	}
 	case 97: {
-		_res->loadAlfredSpecialAnim(1);
-		_alfredState.animState = ALFRED_SPECIAL_ANIM;
-		waitForSpecialAnimation();
+		playAlfredSpecialAnim(1);
 		loadExtraScreenAndPresent(11);
 		_dialog->say(_res->_ingameTexts[MEHANTOMADO_EL_PELO]);
 		_state->setCurrentRoot(43, 1, 0);
@@ -2127,9 +2088,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 	}
 	default: {
 		if (inventoryObject >= 11 && inventoryObject <= 47) {
-			_res->loadAlfredSpecialAnim(0);
-			_alfredState.animState = ALFRED_SPECIAL_ANIM;
-			waitForSpecialAnimation();
+			playAlfredSpecialAnim(0);
 			_dialog->say(_res->_ingameTexts[LIBRO_ABURRIDO]);
 			return;
 		}
@@ -2142,9 +2101,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 }
 
 void PelrockEngine::chooseCorrectDoor() {
-	_res->loadAlfredSpecialAnim(1);
-	_alfredState.animState = ALFRED_SPECIAL_ANIM;
-	waitForSpecialAnimation();
+	playAlfredSpecialAnim(1);
 	byte puertaBuena = _state->getFlag(FLAG_PUERTA_BUENA);
 	if (puertaBuena == 0) { // if not set yet, choose randomly
 		int choice = getRandomNumber(1);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 435969c995f..8aa35244cb3 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -347,6 +347,7 @@ public:
 	void openTravelAgencyDoor(HotSpot *hotspot);
 	void closeTravelAgencyDoor(HotSpot *hotspot);
 	void usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot);
+	void playAlfredSpecialAnim(int anim, bool reverse = false);
 	void waitForSoundEnd();
 	void pickupSunflower(HotSpot *hotspot);
 	void checkIngredients();


Commit: c491840579737ddf7d6c2cbf3bf7f4b359c43039
    https://github.com/scummvm/scummvm/commit/c491840579737ddf7d6c2cbf3bf7f4b359c43039
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:17+02:00

Commit Message:
PELROCK: Fixes positioning action balloon and inventory scroll

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 25c6108a79c..a5e88c4be26 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -191,8 +191,10 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 			_curSprite->isTalking = false;
 		}
 		// Offset X position for Alfred to avoid overlapping with his sprite
-		xBasePos = g_engine->_alfredState.x;                      //+ kAlfredFrameWidth / 2 - maxWidth / 2;
-		yBasePos = g_engine->_alfredState.y - kAlfredFrameHeight; // Above sprite, adjust for line
+		xBasePos = g_engine->_alfredState.x;
+		// Original game: uses the scaled character height (varies with perspective),
+		// not the fixed kAlfredFrameHeight. _alfredState.h is updated by drawAlfred().
+		yBasePos = g_engine->_alfredState.y - g_engine->_alfredState.h; // Above scaled sprite top
 	} else {
 		g_engine->_alfredState.setState(ALFRED_IDLE);
 		if (_curSprite != nullptr) {
@@ -240,9 +242,9 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 
 		Graphics::Surface s = getDialogueSurface(textLines, speakerId);
 
-		// Clamp to screen bounds
-		xPos = CLIP(xPos, 0, 640 - maxWidth);
-		yPos = CLIP(yPos, 0, 400 - s.getRect().height());
+		// Clamp to screen bounds (original game: min Y = 1, max X = 639 - width)
+		xPos = CLIP(xPos, 0, 639 - maxWidth);
+		yPos = CLIP(yPos, 1, 400 - (int)s.getRect().height());
 
 		_screen->transBlitFrom(s, s.getRect(), Common::Point(xPos, yPos), 255);
 		drawPos(_screen, xPos, yPos, speakerId);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 9b590d96be2..123445a28bc 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -154,9 +154,9 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		// setScreen(0, ALFRED_LEFT);
+		setScreen(0, ALFRED_LEFT);
 		// setScreen(3, ALFRED_RIGHT);
-		setScreen(22, ALFRED_DOWN);
+		// setScreen(22, ALFRED_DOWN);
 		// setScreen(41, ALFRED_DOWN);
 		// setScreen(43, ALFRED_DOWN);
 		// setScreen(46, ALFRED_RIGHT);
@@ -1319,17 +1319,13 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 	bool alfredUnder = isAlfredUnder(x, y);
 	if ((hotspotIndex != -1 || alfredUnder) && !_actionPopupState.isActive) {
 
-		_actionPopupState.x = _alfredState.x + kAlfredFrameWidth / 2 - kBalloonWidth / 2;
-		if (_actionPopupState.x < 0)
-			_actionPopupState.x = 0;
-		if (_actionPopupState.x + kBalloonWidth > 640) {
-			_actionPopupState.x = 640 - kBalloonWidth;
-		}
+		// Original game positions balloon at alfred_x - 70, clamped to [1, 390]
+		_actionPopupState.x = CLIP((int)_alfredState.x - 70, 1, 390);
 
-		_actionPopupState.y = _alfredState.y - kAlfredFrameHeight - kBalloonHeight;
-		if (_actionPopupState.y < 0) {
-			_actionPopupState.y = 0;
-		}
+		// Original game: Y = max(10, alfred_y - character_sprite_height - 102)
+		// The 102 offset is a fixed gap above Alfred's head, NOT the balloon height.
+		// This means the balloon bottom overlaps Alfred's head by ~10 pixels.
+		_actionPopupState.y = MAX(10, (int)_alfredState.y - (int)kAlfredFrameHeight - 102);
 		_actionPopupState.isActive = true;
 		_actionPopupState.curFrame = 0;
 		debug("Setting alfred under popup: %d", alfredUnder);
@@ -1613,9 +1609,11 @@ void PelrockEngine::pickupIconFlash() {
 void PelrockEngine::showInventoryOverlay() {
 	_graphics->showOverlay(60, _compositeBuffer);
 	uint invSize = _state->inventoryItems.size();
-	int firstItem = _inventoryOverlayState.invStartingPos * kInventoryPageSize;
+	// invStartingPos is an ITEM index (not a page number).
+	// The original game scrolls 1 item at a time, not 1 page at a time.
+	int firstItem = _inventoryOverlayState.invStartingPos;
 
-	for (int i = firstItem; i < invSize && i < firstItem + kInventoryPageSize; i++) {
+	for (int i = firstItem; i < (int)invSize && i < firstItem + kInventoryPageSize; i++) {
 		Common::Point p = getPositionInOverlayForIndex(i - firstItem);
 		if (i == _inventoryOverlayState.flashingIconIndex && _chrono->getFrameCount() % kIconBlinkPeriod == 0) {
 			continue;
@@ -1627,31 +1625,30 @@ void PelrockEngine::showInventoryOverlay() {
 	if (_inventoryOverlayState.invStartingPos > 0) {
 		drawSpriteToBuffer(_compositeBuffer, 640, _inventoryOverlayState.arrows[0], 0, 340, 20, 60, 255);
 	}
-	if ((firstItem + kInventoryPageSize) < invSize) {
+	if (firstItem + kInventoryPageSize < (int)invSize) {
 		drawSpriteToBuffer(_compositeBuffer, 640, _inventoryOverlayState.arrows[1], 620, 340, 20, 60, 255);
 	}
-	// drawSpriteToBuffer(_compositeBuffer, 640, _inventoryOverlayState.arrows[0], 0, 340, 20, 60, 255);
 }
 
 void PelrockEngine::checkMouseOverInventoryOverlay(int x, int y) {
+	// Original game: invStartingPos is an item index, scrolls 1 item per frame
+	// with no frame throttling (scrolls every game tick = ~55ms).
 	if (x < 20) {
-		if (_inventoryOverlayState.invStartingPos > 0 && _chrono->getFrameCount() % 2 == 0) {
+		if (_inventoryOverlayState.invStartingPos > 0) {
 			_inventoryOverlayState.invStartingPos--;
 		}
 	} else if (x >= 620) {
-		if (_inventoryOverlayState.invStartingPos < (_state->inventoryItems.size() / kInventoryPageSize) && _chrono->getFrameCount() % 2 == 0) {
+		if (_inventoryOverlayState.invStartingPos + kInventoryPageSize < (int)_state->inventoryItems.size()) {
 			_inventoryOverlayState.invStartingPos++;
 		}
 	} else {
-		// mouse hover over inventory item, laid out horizontally, y coordinate is not relevant for determining which item is selected
-		int index = (x - 20) / 60 + (_inventoryOverlayState.invStartingPos * kInventoryPageSize);
-		if (index < _state->inventoryItems.size()) {
+		// mouse hover over inventory item, laid out horizontally, y coordinate is not relevant
+		int index = (x - 20) / 60 + _inventoryOverlayState.invStartingPos;
+		if (index < (int)_state->inventoryItems.size()) {
 			debug("hovering over inventory item %d at index %d", _state->inventoryItems[index], index);
-			// _state->selectedInventoryItem = index;
 			_inventoryOverlayState.flashingIconIndex = index;
 		} else {
 			debug("hovering over empty slot in inventory overlay, no item at index %d", index);
-			// _state->selectedInventoryItem = -1;
 			_inventoryOverlayState.flashingIconIndex = -1;
 		}
 	}
@@ -1663,9 +1660,12 @@ int PelrockEngine::checkMouseClickInventoryOverlay(int x, int y) {
 	} else if (x >= 620) {
 		return -1;
 	} else {
-		// mouse hover over inventory item, laid out horizontally, y coordinate is not relevant for determining which item is selected
-		int index = (x - 20) / 60 + (_inventoryOverlayState.invStartingPos * kInventoryPageSize);
-		return _state->inventoryItems[index];
+		// mouse hover over inventory item, laid out horizontally, y coordinate is not relevant
+		int index = (x - 20) / 60 + _inventoryOverlayState.invStartingPos;
+		if (index < (int)_state->inventoryItems.size()) {
+			return _state->inventoryItems[index];
+		}
+		return -1;
 	}
 }
 


Commit: d0997de2960496ea32665d3a099ad29f9f2c10f9
    https://github.com/scummvm/scummvm/commit/d0997de2960496ea32665d3a099ad29f9f2c10f9
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:18+02:00

Commit Message:
PELROCK: Pickup bush in room 2, door in room 47

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 5425de59fde..4289dbf3441 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -50,6 +50,8 @@ const ActionEntry actionTable[] = {
 	// Room 2
 	{282, OPEN, &PelrockEngine::openMcDoor},
 	{282, CLOSE, &PelrockEngine::closeMcDoor},
+	{283, PICKUP, &PelrockEngine::pickupBush},
+	{284, PICKUP, &PelrockEngine::pickupBush},
 
 	// Room 12
 	{60, PICKUP, &PelrockEngine::grabKetchup},
@@ -84,8 +86,9 @@ const ActionEntry actionTable[] = {
 
 	// Room 4
 	{315, OPEN, &PelrockEngine::openPlug},
-	{316, PICKUP, &PelrockEngine::pickCables},
 	{312, OPEN, &PelrockEngine::openMuseumDoor},
+	{316, PICKUP, &PelrockEngine::pickCables},
+	{312, CLOSE, &PelrockEngine::closeMuseumDoor},
 	{310, PICKUP, &PelrockEngine::pickupFruit},
 	{311, PICKUP, &PelrockEngine::pickupFruit},
 
@@ -172,6 +175,8 @@ const ActionEntry actionTable[] = {
 
 	// Room 47
 	{628, PICKUP, &PelrockEngine::pickupPyramidMap},
+	{800, OPEN, &PelrockEngine::openArchitectDoorFromInside},
+	{800, CLOSE, &PelrockEngine::closeArchitectDoorFromInside},
 
 	// Generic handlers
 	{WILDCARD, PICKUP, &PelrockEngine::noOpAction}, // Generic pickup action
@@ -808,6 +813,10 @@ void PelrockEngine::closeMcDoor(HotSpot *hotspot) {
 	closeDoor(hotspot, 2, 7, FEMININE, false);
 }
 
+void PelrockEngine::pickupBush(HotSpot *hotspot) {
+	_dialog->say(_res->_ingameTexts[MEHEVUELTOAPINCHAR]);
+}
+
 void PelrockEngine::pickUpAndDisable(HotSpot *hotspot) {
 	addInventoryItem(hotspot->extra);
 	_room->disableHotspot(hotspot);
@@ -1072,6 +1081,10 @@ void PelrockEngine::openMuseumDoor(HotSpot *hotspot) {
 	}
 }
 
+void PelrockEngine::closeMuseumDoor(HotSpot *hotspot) {
+	closeDoor(hotspot, 1, 22, FEMININE, false);
+}
+
 void PelrockEngine::pickupFruit(HotSpot *hotspot) {
 	_dialog->say(_res->_ingameTexts[NO_THEY_MAKEYOU_FAT]);
 }
@@ -1779,6 +1792,26 @@ void PelrockEngine::pickupPyramidMap(HotSpot *hotspot) {
 	addInventoryItem(98);
 }
 
+void PelrockEngine::openArchitectDoorFromInside(HotSpot *hotspot) {
+	if (!_room->hasSticker(104)) {
+		_dialog->say(_res->_ingameTexts[YA_ABIERTA_F]);
+		return;
+	}
+	_room->enableExit(0, PERSIST_TEMP);
+	_room->removeSticker(104);
+	_sound->playSound(_room->_roomSfx[0]);
+}
+
+void PelrockEngine::closeArchitectDoorFromInside(HotSpot *hotspot) {
+	if (_room->hasSticker(104)) {
+		_dialog->say(_res->_ingameTexts[YA_CERRADA_F]);
+		return;
+	}
+	_room->disableExit(0, PERSIST_TEMP);
+	_room->addSticker(104, PERSIST_TEMP);
+	_sound->playSound(_room->_roomSfx[1]);
+}
+
 void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	debug("Performing action trigger: %d", actionTrigger);
 	switch (actionTrigger) {
@@ -1835,6 +1868,26 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	}
 }
 
+// Bresenham line draw using a 256-byte palette remap table (semi-transparent effect).
+// Each pixel on the line is replaced by remapTable[existing_color] instead of a flat color.
+static void drawRemappedLine(byte *buf, int x0, int y0, int x1, int y1, const byte *remapTable) {
+	int dx = ABS(x1 - x0);
+	int dy = ABS(y1 - y0);
+	int sx = (x0 < x1) ? 1 : -1;
+	int sy = (y0 < y1) ? 1 : -1;
+	int err = dx - dy;
+	while (true) {
+		if (x0 >= 0 && x0 < 640 && y0 >= 0 && y0 < 400) {
+			int idx = y0 * 640 + x0;
+			buf[idx] = remapTable[buf[idx]];
+		}
+		if (x0 == x1 && y0 == y1) break;
+		int e2 = 2 * err;
+		if (e2 > -dy) { err -= dy; x0 += sx; }
+		if (e2 < dx)  { err += dx; y0 += sy; }
+	}
+}
+
 void PelrockEngine::teletransportToPrincess() {
 	int phase = 0;
 
@@ -1862,48 +1915,68 @@ void PelrockEngine::teletransportToPrincess() {
 
 		_sound->playSound(_room->_roomSfx[3], 0);
 
+		// Draw 19 semi-transparent remapped lines (behind sprites, matching original)
+		// Original uses shadow_palette_remap_tables[1] — _paletteRemaps[1] from ALFRED.9
 		copyBackgroundToBuffer();
 		placeStickersFirstPass();
 		updateAnimations();
 		presentFrame();
-
+		_screen->update();
+		g_system->delayMillis(10);
 		for (int i = 0; i < 19; i++) {
-			if (shouldQuit()) {
+			if (shouldQuit())
 				return;
-			}
 			int x1 = lines[phase][0];
 			int y1 = lines[phase][1];
 			int x2 = lines[phase][2] + i;
 			int y2 = lines[phase][3];
-			_screen->drawLine(x1, y1, x2, y2, 255);
+			drawRemappedLine(_compositeBuffer, x1, y1, x2, y2, _room->_paletteRemaps[1]);
 		}
-		_screen->markAllDirty();
+		updateAnimations();
+		presentFrame();
 		_screen->update();
 		g_system->delayMillis(10);
-
-		if (shouldQuit()) {
+		_events->pollEvent();
+		if (shouldQuit())
 			return;
-		}
 
+		// Restore clean frame with sticker (lines gone)
 		_room->addSticker(stickers[phase]);
 		copyBackgroundToBuffer();
 		placeStickersFirstPass();
 		updateAnimations();
 		presentFrame();
+		_screen->update();
 
 		phase++;
 	}
+	// small delay before last sticker
+	int delay = 10;
+	while (!shouldQuit() && delay > 0) {
+		_events->pollEvent();
+		bool didRender = renderScene(OVERLAY_NONE);
+		_screen->update();
+		g_system->delayMillis(10);
+		if (didRender) {
+			delay--;
+		}
+	}
 
 	_room->addSticker(115);
-	_screen->markAllDirty();
+	copyBackgroundToBuffer();
+	placeStickersFirstPass();
+	updateAnimations();
+	presentFrame();
 	_screen->update();
 
 	_dialog->say(_res->_ingameTexts[MAREDEDEU]);
 
+	// endgameTransportAnimation();
 	smokeAnimation(-1, true);
+	_state->setFlag(FLAG_END_OF_GAME, 1);
 	_state->setCurrentRoot(48, 1, 0);
- 	_alfredState.x = 138;
-    _alfredState.y = 255;
+	_alfredState.x = 138;
+	_alfredState.y = 255;
 	setScreen(48, ALFRED_DOWN);
 }
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 123445a28bc..f6f4285c056 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1716,7 +1716,7 @@ void PelrockEngine::gameLoop() {
 		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
 	}
 
-	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_f) {
+	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_q) {
 		endingScene();
 		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
 	}
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 8aa35244cb3..1397186c177 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -327,6 +327,7 @@ public:
 	void unlockMuseum();
 	void giveMoneyToGuard(int inventoryObject, HotSpot *hotspot);
 	void openMuseumDoor(HotSpot *hotspot);
+	void closeMuseumDoor(HotSpot *hotspot);
 	void pickupFruit(HotSpot *hotspot);
 	void useAmuletWithStatue(int inventoryObject, HotSpot *hotspot);
 	void useSecretCodeWithStatue(int inventoryObject, HotSpot *hotspot);
@@ -384,12 +385,16 @@ public:
 	void useWigWithPot(int inventoryObject, HotSpot *hotspot);
 	void magicFormula(int inventoryObject, HotSpot *hotspot);
 	void smokeAnimation(int spriteIndex, bool hide = true);
+	// void endgameTransportAnimation();
 	void openArchitectDoor(HotSpot *hotspot);
 	void closeArchitectDoor(HotSpot *hotspot);
 	void pickupPyramidMap(HotSpot *hotspot);
+	void openArchitectDoorFromInside(HotSpot *hotspot);
+	void closeArchitectDoorFromInside(HotSpot *hotspot);
 	void checkAllSymbols();
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
+	void pickupBush(HotSpot *hotspot);
 	void teletransportToPrincess();
 
 	void animateStatuePaletteFade(bool reverse = false);


Commit: 2510f3061bd080b1e101fe8ca86d6b0c13be0bf9
    https://github.com/scummvm/scummvm/commit/2510f3061bd080b1e101fe8ca86d6b0c13be0bf9
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:18+02:00

Commit Message:
PELROCK: Implements action and continue trigger in conversations

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 4289dbf3441..cb4fdb93758 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -679,12 +679,36 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 378:
 		_state->setCurrentRoot(49, 3, 0);
 		break;
+	case 380:
+		turnLightsOff();
+		break;
 	default:
 		debug("Got actionTrigger %d in dialogActionTrigger, but no handler defined", actionTrigger);
 		break;
 	}
 }
 
+/**
+ * Turns the screen all dark by changing all palette colors except those of the dialog text for both
+ * Alfred and the princess.
+ */
+void PelrockEngine::turnLightsOff() {
+	memset(_currentBackground, 0, 640 * 400);
+	memset(_compositeBuffer, 0, 640 * 400);
+	memset(_screen->getPixels(), 0, 640 * 400);
+	byte darkPalette[768] = {};
+	darkPalette[238 * 3 + 0] = 60 << 2; // R = 240
+	darkPalette[238 * 3 + 1] = 57 << 2; // G = 228
+	darkPalette[238 * 3 + 2] = 57 << 2; // B = 228
+	darkPalette[13 * 3 + 0] = 63 << 2;  // R = 252
+	darkPalette[13 * 3 + 1] = 21 << 2;  // G = 84
+	darkPalette[13 * 3 + 2] = 63 << 2;  // B = 252
+	g_system->getPaletteManager()->setPalette(darkPalette, 0, 256);
+	memcpy(_room->_roomPalette, darkPalette, 768);
+	_screen->markAllDirty();
+	_screen->update();
+}
+
 void PelrockEngine::givenItems() {
 	_state->setFlag(FLAG_MERCHANT_GIVENITEMS, _state->getFlag(FLAG_MERCHANT_GIVENITEMS) + 1);
 	if (_state->getFlag(FLAG_MERCHANT_GIVENITEMS) == 4) {
@@ -1881,10 +1905,17 @@ static void drawRemappedLine(byte *buf, int x0, int y0, int x1, int y1, const by
 			int idx = y0 * 640 + x0;
 			buf[idx] = remapTable[buf[idx]];
 		}
-		if (x0 == x1 && y0 == y1) break;
+		if (x0 == x1 && y0 == y1)
+			break;
 		int e2 = 2 * err;
-		if (e2 > -dy) { err -= dy; x0 += sx; }
-		if (e2 < dx)  { err += dx; y0 += sy; }
+		if (e2 > -dy) {
+			err -= dy;
+			x0 += sx;
+		}
+		if (e2 < dx) {
+			err += dx;
+			y0 += sy;
+		}
 	}
 }
 
@@ -1932,6 +1963,7 @@ void PelrockEngine::teletransportToPrincess() {
 			int y2 = lines[phase][3];
 			drawRemappedLine(_compositeBuffer, x1, y1, x2, y2, _room->_paletteRemaps[1]);
 		}
+
 		updateAnimations();
 		presentFrame();
 		_screen->update();
@@ -1973,7 +2005,7 @@ void PelrockEngine::teletransportToPrincess() {
 
 	// endgameTransportAnimation();
 	smokeAnimation(-1, true);
-	_state->setFlag(FLAG_END_OF_GAME, 1);
+	_state->setFlag(FLAG_END_OF_GAME, true);
 	_state->setCurrentRoot(48, 1, 0);
 	_alfredState.x = 138;
 	_alfredState.y = 255;
@@ -2080,20 +2112,19 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 					smokeAnimation(kFlightRooms[flightIndex].spriteIdx, true);
 					_room->addStickerToRoom(_room->_currentRoomNumber, 127 + flightIndex);
 					// if(_state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) == 0b1111) {
-						HotSpot hotspot = HotSpot();
-						hotspot.actionFlags = 0;
-						hotspot.extra = 999;
-						hotspot.x = 320;
-						hotspot.y = 288;
-						hotspot.w = 35;
-						hotspot.h = 21;
-						hotspot.innerIndex = 0;
-						hotspot.index = 8;
-						_room->changeHotspot(52, hotspot);
+					HotSpot hotspot = HotSpot();
+					hotspot.actionFlags = 0;
+					hotspot.extra = 999;
+					hotspot.x = 320;
+					hotspot.y = 288;
+					hotspot.w = 35;
+					hotspot.h = 21;
+					hotspot.innerIndex = 0;
+					hotspot.index = 8;
+					_room->changeHotspot(52, hotspot);
 					// }
 				}
 
-
 				break;
 			}
 			default:
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index a5e88c4be26..6513ab87e09 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -87,9 +87,9 @@ uint32 DialogManager::readTextBlock(
 		byte b = data[pos];
 
 		// End markers - stop reading text
-		if (b == CTRL_END_TEXT || b == CTRL_END_CONVERSATION || b == CTRL_ACTION_TRIGGER ||
+		if (b == CTRL_END_TEXT || b == CTRL_END_CONVERSATION || b == CTRL_ACTION_AND_END ||
 			b == CTRL_END_BRANCH || b == CTRL_DIALOGUE_MARKER || b == CTRL_DIALOGUE_MARKER_ONEOFF ||
-			b == CTRL_TEXT_TERMINATOR || b == CTRL_ALT_END_MARKER_1 || b == CTRL_ALT_END_MARKER_2 ||
+			b == CTRL_TEXT_TERMINATOR || b == CTRL_ALT_END_MARKER_1 || b == CTRL_ACTION_AND_CONTINUE ||
 			b == CTRL_GO_BACK || b == CTRL_SPEAKER_ID) {
 			break;
 		}
@@ -378,7 +378,7 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 									break;
 								}
 								// Found a conversation terminator - this choice ends the conversation
-								if (sb == CTRL_END_CONVERSATION || sb == CTRL_ACTION_TRIGGER) {
+								if (sb == CTRL_END_CONVERSATION || sb == CTRL_ACTION_AND_END) {
 									opt.hasConversationEndMarker = true;
 									break;
 								}
@@ -521,10 +521,11 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 
 		if (!skipToChoices) {
 			ConversationEndResult endResult = checkConversationEnd(conversationData, dataSize, state.position);
+			// Dispatch action for both 0xF8 (action+end) and 0xEB (action+continue)
+			if (endResult.hasAction) {
+				g_engine->dialogActionTrigger(endResult.actionCode, g_engine->_room->_currentRoomNumber, state.currentRoot);
+			}
 			if (endResult.shouldEnd) {
-				if (endResult.hasAction) {
-					g_engine->dialogActionTrigger(endResult.actionCode, g_engine->_room->_currentRoomNumber, state.currentRoot);
-				}
 				break;
 			}
 			state.position = endResult.nextPosition;
@@ -658,10 +659,11 @@ uint32 DialogManager::findSpeaker(byte npcIndex, uint32 dataSize, const byte *co
 }
 
 // Skip control bytes that should be ignored
+// NOTE: 0xEB (CTRL_ALT_END_MARKER_2) must NOT be skipped here — it is an action-and-continue
+// trigger that must be processed by checkConversationEnd, not silently discarded.
 uint32 DialogManager::skipControlBytes(const byte *data, uint32 dataSize, uint32 position) {
 	while (position < dataSize &&
-		   (data[position] == CTRL_ALT_END_MARKER_1 ||
-			data[position] == CTRL_ALT_END_MARKER_2)) {
+		   data[position] == CTRL_ALT_END_MARKER_1) {
 		position++;
 	}
 	return position;
@@ -672,7 +674,6 @@ uint32 DialogManager::peekNextMeaningfulByte(const byte *data, uint32 dataSize,
 	uint32 peekPos = position;
 	while (peekPos < dataSize &&
 		   (data[peekPos] == CTRL_ALT_END_MARKER_1 ||
-			data[peekPos] == CTRL_ALT_END_MARKER_2 ||
 			data[peekPos] == CTRL_TEXT_TERMINATOR)) {
 		peekPos++;
 	}
@@ -758,7 +759,7 @@ ConversationEndResult DialogManager::checkConversationEnd(const byte *data, uint
 		return result;
 	}
 
-	if (controlByte == CTRL_ACTION_TRIGGER) {
+	if (controlByte == CTRL_ACTION_AND_END) {
 		result.actionCode = data[position + 1] | (data[position + 2] << 8);
 		debug("Action trigger %d encountered!", result.actionCode);
 		result.shouldEnd = true;
@@ -766,6 +767,18 @@ ConversationEndResult DialogManager::checkConversationEnd(const byte *data, uint
 		return result;
 	}
 
+	if (controlByte == CTRL_ACTION_AND_CONTINUE) {
+		// 0xEB: action-and-continue — dispatch the action but do NOT exit the conversation.
+		if (position + 2 < dataSize) {
+			result.actionCode = data[position + 1] | (data[position + 2] << 8);
+			debug("Action-and-continue trigger %d encountered (0xEB)", result.actionCode);
+			result.hasAction = true;
+		}
+		result.shouldEnd = false;
+		result.nextPosition = position + 3;
+		return result;
+	}
+
 	// Move past control byte
 	if (controlByte == CTRL_END_TEXT) {
 		result.nextPosition = position + 1;
@@ -848,7 +861,7 @@ uint32 DialogManager::processChoiceSelection(
 	if (position < dataSize) {
 		byte endByte = data[position];
 		if (endByte == CTRL_END_TEXT || endByte == CTRL_END_BRANCH ||
-			endByte == CTRL_ACTION_TRIGGER) {
+			endByte == CTRL_ACTION_AND_END) {
 			position++;
 		}
 	}
@@ -1022,7 +1035,7 @@ bool DialogManager::processColorAndTrim(Common::StringArray &lines, byte &speake
 }
 
 bool isEndMarker(unsigned char char_byte) {
-	return char_byte == CTRL_END_TEXT || char_byte == CTRL_END_CONVERSATION || char_byte == CTRL_ACTION_TRIGGER || char_byte == CTRL_GO_BACK;
+	return char_byte == CTRL_END_TEXT || char_byte == CTRL_END_CONVERSATION || char_byte == CTRL_ACTION_AND_END || char_byte == CTRL_GO_BACK;
 }
 
 int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
@@ -1041,7 +1054,7 @@ int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
 		isEnd = true;
 	}
 	if (pos < text.size() && !isEnd) {
-		if (text[pos] == CTRL_ACTION_TRIGGER) { // 0xF8 (-8) special case
+		if (text[pos] == CTRL_ACTION_AND_END) { // 0xF8 (-8) special case
 			wordLength += 3;
 		} else {
 			// Count all consecutive spaces
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 85362b44dd5..73ae82a13ec 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -41,14 +41,14 @@ namespace Pelrock {
 #define CTRL_DIALOGUE_MARKER 0xF1        /* Choice marker that sticks */
 #define CTRL_DISABLED_CHOICE 0xFA        /* Disabled choice marker */
 #define CTRL_PAGE_BREAK_CONV 0xF9        /* Page break in conversation */
-#define CTRL_ACTION_TRIGGER 0xF8         /* Action trigger */
+#define CTRL_ACTION_AND_END 0xF8         /* Action trigger */
 #define CTRL_END_BRANCH 0xF7             /* End of branch */
 #define CTRL_LINE_CONTINUE 0xF6          /* Line continue/newline */
 #define CTRL_ALT_END_MARKER_1 0xF5       /* Alt end marker - do nothing */
 #define CTRL_END_CONVERSATION 0xF4       /* End conversation and disable option */
 #define CTRL_DIALOGUE_MARKER_ONEOFF 0xFB /* Alt choice marker that disappears */
 #define CTRL_GO_BACK 0xF0                /* Go back in conversation */
-#define CTRL_ALT_END_MARKER_2 0xEB       /* Alt end marker 2 */
+#define CTRL_ACTION_AND_CONTINUE 0xEB       /* Action-and-continue: dispatch action, conversation keeps going (unlike 0xF8 which exits) */
 #define CTRL_ALT_SPEAKER_ROOT 0xFE       /* Separates conversations from different speakers */
 
 // Helper structures for conversation state management
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 1397186c177..e15f62cf509 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -271,6 +271,8 @@ public:
 	void performActionTrigger(uint16 actionTrigger);
 	void dialogActionTrigger(uint16 actionTrigger, byte room, byte rootIndex);
 
+	void turnLightsOff();
+
 	void givenItems();
 	void advanceQuotesConversation(byte rootIndex, byte room);
 	void toJail();


Commit: 3b03b4ba43e417b4a0b8f48a4155b900282c5ada
    https://github.com/scummvm/scummvm/commit/3b03b4ba43e417b4a0b8f48a4155b900282c5ada
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:18+02:00

Commit Message:
PELROCK: Implements credits in the ending

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/console.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/saveload.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index cb4fdb93758..b107fb3c7c2 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -728,7 +728,7 @@ void PelrockEngine::toJail() {
 	_graphics->fadeToBlack(10);
 	_alfredState.x = 342;
 	_alfredState.y = 277;
-	setScreen(31, ALFRED_DOWN);
+	setScreenAndPrepare(31, ALFRED_DOWN);
 	_state->setFlag(FLAG_A_LA_CARCEL, true);
 	_room->moveHotspot(_room->findHotspotByExtra(101), 444, 166);
 }
@@ -1223,7 +1223,7 @@ void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
 	// update conversaton state
 	_alfredState.x = 300;
 	_alfredState.y = 238;
-	setScreen(28, ALFRED_DOWN);
+	setScreenAndPrepare(28, ALFRED_DOWN);
 	_dialog->say(_res->_ingameTexts[QUEOSCUROESTAESTO]);
 }
 
@@ -1672,7 +1672,7 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 	_graphics->fadeToBlack(10);
 	_alfredState.x = 271;
 	_alfredState.y = 385;
-	setScreen(40, ALFRED_UP);
+	setScreenAndPrepare(40, ALFRED_UP);
 	walkAndAction(_room->findHotspotByExtra(640), TALK);
 	if (shouldQuit()) {
 		return;
@@ -1680,7 +1680,7 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 	_graphics->fadeToBlack(10);
 	_alfredState.x = 271;
 	_alfredState.y = 385;
-	setScreen(41, ALFRED_UP);
+	setScreenAndPrepare(41, ALFRED_UP);
 }
 
 void PelrockEngine::pickUpStones(HotSpot *hotspot) {
@@ -1755,7 +1755,7 @@ void PelrockEngine::magicFormula(int inventoryObject, HotSpot *hotspot) {
 
 		smokeAnimation(-1);
 		_alfredState.setState(ALFRED_IDLE);
-		setScreen(39, ALFRED_UP);
+		setScreenAndPrepare(39, ALFRED_UP);
 	}
 }
 
@@ -2009,7 +2009,7 @@ void PelrockEngine::teletransportToPrincess() {
 	_state->setCurrentRoot(48, 1, 0);
 	_alfredState.x = 138;
 	_alfredState.y = 255;
-	setScreen(48, ALFRED_DOWN);
+	setScreenAndPrepare(48, ALFRED_DOWN);
 }
 
 void PelrockEngine::useOnAlfred(int inventoryObject) {
@@ -2093,7 +2093,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 
 					_alfredState.x = 145;
 					_alfredState.y = 312;
-					setScreen(25, ALFRED_RIGHT);
+					setScreenAndPrepare(25, ALFRED_RIGHT);
 					_dialog->say(_res->_ingameTexts[MENUDAAVENTURA]);
 				}
 				break;
diff --git a/engines/pelrock/console.cpp b/engines/pelrock/console.cpp
index b796869b9f0..a4676a5e913 100644
--- a/engines/pelrock/console.cpp
+++ b/engines/pelrock/console.cpp
@@ -87,7 +87,7 @@ bool PelrockConsole::cmdLoadRoom(int argc, const char **argv) {
 	}
 
 	int roomNumber = atoi(argv[1]);
-	g_engine->setScreen(roomNumber, ALFRED_DOWN);
+	g_engine->setScreenAndPrepare(roomNumber, ALFRED_DOWN);
 	debugPrintf("Loaded room %d\n", roomNumber);
 	return true;
 }
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 6513ab87e09..f5e691e6dbb 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -167,7 +167,7 @@ Graphics::Surface DialogManager::getDialogueSurface(Common::Array<Common::String
 	}
 
 	Graphics::Surface s;
-	s.create(maxWidth, height, Graphics::PixelFormat::createFormatCLUT8());
+	s.create(maxWidth + 1, height + 1, Graphics::PixelFormat::createFormatCLUT8());
 	s.fillRect(s.getRect(), 255); // Clear surface
 
 	for (int i = 0; i < dialogueLines.size(); i++) {
@@ -215,7 +215,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 	if (dialogueLines.empty()) {
 		return;
 	}
-
+	_dismissDialog = false;
 	// Clear any existing click state
 	_events->_leftMouseClicked = false;
 	_dialogActive = true;
@@ -258,9 +258,13 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 			if (curPage < (int)dialogueLines.size() - 1) {
 				curPage++;
 			} else {
-				break; // Exit dialogue on last page click
+				_dismissDialog = true;
 			}
 		}
+		if(_dismissDialog) {
+			_dismissDialog = false;
+			break; // Exit dialogue if dismissed programmatically
+		}
 		g_system->delayMillis(10);
 	}
 	if (_curSprite != nullptr) {
@@ -761,7 +765,7 @@ ConversationEndResult DialogManager::checkConversationEnd(const byte *data, uint
 
 	if (controlByte == CTRL_ACTION_AND_END) {
 		result.actionCode = data[position + 1] | (data[position + 2] << 8);
-		debug("Action trigger %d encountered!", result.actionCode);
+		debug("Action-and-end trigger %d encountered!", result.actionCode);
 		result.shouldEnd = true;
 		result.hasAction = true;
 		return result;
@@ -771,7 +775,7 @@ ConversationEndResult DialogManager::checkConversationEnd(const byte *data, uint
 		// 0xEB: action-and-continue — dispatch the action but do NOT exit the conversation.
 		if (position + 2 < dataSize) {
 			result.actionCode = data[position + 1] | (data[position + 2] << 8);
-			debug("Action-and-continue trigger %d encountered (0xEB)", result.actionCode);
+			debug("Action-and-continue trigger %d encountered", result.actionCode);
 			result.hasAction = true;
 		}
 		result.shouldEnd = false;
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 73ae82a13ec..e3ce72c7e9f 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -122,6 +122,7 @@ public:
 
 	// True while a blocking dialog or conversation is on screen.
 	bool _dialogActive = false;
+	bool _dismissDialog = false; // When true, the current dialog will be dismissed on the next iteration of the conversation loop (used for programmatically closing dialogs, e.g. when exiting a room)
 
 	Common::String _leftArrow = Common::String(17);
 	Common::String _rightArrow = Common::String(16);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index f6f4285c056..261104fd926 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -154,7 +154,7 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		setScreen(0, ALFRED_LEFT);
+		setScreenAndPrepare(0, ALFRED_LEFT);
 		// setScreen(3, ALFRED_RIGHT);
 		// setScreen(22, ALFRED_DOWN);
 		// setScreen(41, ALFRED_DOWN);
@@ -241,6 +241,8 @@ void sortAnimsByZOrder(Common::Array<Sprite> &anims) {
 }
 
 void PelrockEngine::playSoundIfNeeded() {
+	if (_disableAmbientSounds)
+		return;
 	// Get ambient slot offset (0-3) or -1 if no sound this frame
 	int ambientSlotOffset = _sound->tickAmbientSound(_chrono->getFrameCount());
 	if (ambientSlotOffset >= 0) {
@@ -938,7 +940,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 					exitTriggers(exit);
 					_alfredState.x = exit->targetX;
 					_alfredState.y = exit->targetY;
-					setScreen(exit->targetRoom, exit->dir);
+					setScreenAndPrepare(exit->targetRoom, exit->dir);
 				}
 			}
 		} else {
@@ -1946,7 +1948,7 @@ void PelrockEngine::checkMouseHover() {
 	}
 }
 
-void PelrockEngine::setScreen(int roomNumber, AlfredDirection dir) {
+void PelrockEngine::setScreen(int roomNumber) {
 	Common::File roomFile;
 	if (!roomFile.open(Common::Path("ALFRED.1"))) {
 		error("Could not open ALFRED.1");
@@ -1955,8 +1957,6 @@ void PelrockEngine::setScreen(int roomNumber, AlfredDirection dir) {
 	changeCursor(DEFAULT);
 	_sound->stopAllSounds();
 	_currentHotspot = nullptr;
-	_alfredState.direction = dir;
-	_alfredState.setState(ALFRED_IDLE);
 	_currentStep = 0;
 	int roomOffset = roomNumber * kRoomStructSize;
 	_alfredState.curFrame = 0;
@@ -1964,18 +1964,30 @@ void PelrockEngine::setScreen(int roomNumber, AlfredDirection dir) {
 	byte *palette = new byte[256 * 3];
 	_room->getPalette(&roomFile, roomOffset, palette);
 
-	byte *background = new byte[640 * 400];
-	_room->getBackground(&roomFile, roomOffset, background);
+	if (_currentBackground != nullptr) {
+		delete[] _currentBackground;
+	}
+	_currentBackground = new byte[640 * 400];
+	_room->getBackground(&roomFile, roomOffset, _currentBackground);
 
 	_screen->clear();
-	_screen->markAllDirty();
-	_screen->update();
 
-	Common::copy(background, background + 640 * 400, _currentBackground);
 	copyBackgroundToBuffer();
 	g_system->getPaletteManager()->setPalette(palette, 0, 256);
 
 	_room->loadRoomMetadata(&roomFile, roomNumber);
+
+	_screen->markAllDirty();
+	_screen->update();
+
+	roomFile.close();
+	delete[] palette;
+}
+
+void PelrockEngine::setScreenAndPrepare(int roomNumber, AlfredDirection dir) {
+	setScreen(roomNumber);
+	_alfredState.direction = dir;
+	_alfredState.setState(ALFRED_IDLE);
 	_room->loadRoomTalkingAnimations(roomNumber);
 	if (_room->_musicTrack > 0)
 		_sound->playMusicTrack(_room->_musicTrack);
@@ -1989,13 +2001,7 @@ void PelrockEngine::setScreen(int roomNumber, AlfredDirection dir) {
 		_alfredState.y = w.y;
 	}
 
-	_screen->markAllDirty();
-	_screen->update();
-
 	doExtraActions(roomNumber);
-	roomFile.close();
-	delete[] background;
-	delete[] palette;
 }
 
 void PelrockEngine::loadExtraScreenAndPresent(int screenIndex) {
@@ -2140,38 +2146,37 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 	case 48: {
 		_dialog->_goodbyeDisabled = true;
 
-		if (_state->getFlag(FLAG_CORRECT_DOOR_CHOSEN) == true) {
-			if (_state->getFlag(FLAG_TRAMPILLA_ABIERTA) == true) {
+		if (_state->getFlag(FLAG_END_OF_GAME) == true) {
 
-				_dialog->say(_res->_ingameTexts[OHMISALVADOR]);
-				_dialog->say(_res->_ingameTexts[VOYPORTI_PRINCESA]);
-				_state->setCurrentRoot(48, 1, 0);
-				walkAndAction(_room->findHotspotByExtra(634), TALK);
+			_dialog->say(_res->_ingameTexts[OHMISALVADOR]);
+			_dialog->say(_res->_ingameTexts[VOYPORTI_PRINCESA]);
+			_state->setCurrentRoot(48, 1, 0);
+			walkAndAction(_room->findHotspotByExtra(634), TALK);
 
-				endingScene();
+			endingScene();
 
-			} else {
-				_dialog->say(_res->_ingameTexts[OHMISALVADOR]);
-				_dialog->say(_res->_ingameTexts[VOYPORTI_PRINCESA]);
-				_state->setFlag(FLAG_TRAMPILLA_ABIERTA, true);
-				walkAndAction(_room->findHotspotByExtra(634), TALK);
-				_room->addSticker(134);
-				// wait a few frames
-				int framesToWait = 0;
-				while (!shouldQuit() && framesToWait < 10) {
-					_events->pollEvent();
-
-					bool didRender = renderScene(OVERLAY_NONE);
-					if (didRender)
-						framesToWait++;
-					_screen->update();
-					g_system->delayMillis(10);
-				}
-				_alfredState.x = 294;
-				_alfredState.y = 387;
-				_room->addSticker(136);
-				setScreen(49, ALFRED_UP);
+		} else if (_state->getFlag(FLAG_CORRECT_DOOR_CHOSEN) == true) {
+			_dialog->say(_res->_ingameTexts[OHMISALVADOR]);
+			_dialog->say(_res->_ingameTexts[VOYPORTI_PRINCESA]);
+			_state->setFlag(FLAG_TRAMPILLA_ABIERTA, true);
+			walkAndAction(_room->findHotspotByExtra(634), TALK);
+			_room->addSticker(134);
+			// wait a few frames
+			int framesToWait = 0;
+			while (!shouldQuit() && framesToWait < 10) {
+				_events->pollEvent();
+
+				bool didRender = renderScene(OVERLAY_NONE);
+				if (didRender)
+					framesToWait++;
+				_screen->update();
+				g_system->delayMillis(10);
 			}
+			_alfredState.x = 294;
+			_alfredState.y = 387;
+			_room->addSticker(136);
+			setScreenAndPrepare(49, ALFRED_UP);
+
 		} else {
 			_dialog->say(_res->_ingameTexts[OHMISALVADOR]);
 			_dialog->say(_res->_ingameTexts[VOYPORTI_PRINCESA]);
@@ -2285,13 +2290,14 @@ void PelrockEngine::endingScene() {
 	int y2 = 400 / 2 + bbox1.height() / 2;
 	int ticks = 0;
 	_sound->playMusicTrack(3);
+	// Loop runs indefinitely — original game waits for any keypress before advancing to credits.
+	_events->_lastKeyEvent = Common::KEYCODE_INVALID;
 	while (!shouldQuit()) {
 		_events->pollEvent();
 
 		_chrono->updateChrono();
 
 		if (_chrono->_gameTick) {
-
 			memcpy(_compositeBuffer, _bgScreen, 640 * 400);
 
 			for (Sprite *sprite : sprites) {
@@ -2299,17 +2305,20 @@ void PelrockEngine::endingScene() {
 			}
 
 			memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
-			if (ticks > 30 && ticks < 180) {
+			// Title text visible frames 21–149 (original: frame > 0x14 && frame < 0x96)
+			if (ticks > 20 && ticks < 150) {
 				drawText(_largeFont, "ALFRED PELROCK", 0, y1, 640, 255);
 				drawText(_largeFont, "En busca de un sue\x80o", 0, y2, 640, 255);
 			}
-
-			if (ticks > 200) {
-				break;
-			}
 			ticks++;
 		}
 
+		// Any keypress advances to credits (original: infinite loop until input)
+		if (_events->_lastKeyEvent != Common::KEYCODE_INVALID) {
+			_events->_lastKeyEvent = Common::KEYCODE_INVALID;
+			break;
+		}
+
 		g_system->delayMillis(10);
 		_screen->markAllDirty();
 		_screen->update();
@@ -2317,12 +2326,10 @@ void PelrockEngine::endingScene() {
 
 	memset(_screen->getPixels(), 0, 640 * 400);
 	g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
-	free(_bgScreen);
+	delete[] _bgScreen;
 	_bgScreen = nullptr;
 
 	CursorMan.showMouse(true);
-	delete[] _bgScreen;
-	_bgScreen = nullptr;
 	delete[] palette;
 	_screen->markAllDirty();
 	_screen->update();
@@ -2332,8 +2339,102 @@ void PelrockEngine::endingScene() {
 }
 
 void PelrockEngine::credits() {
+	// 25-page room slideshow: each page loads a game room and overlays credit texts.
+	static const int kNumCreditPages = 25;
+	static const int kCreditRooms[kNumCreditPages] = {
+		22, 27, 36, 23, 24, 37, 25, 26, 49, 43, 35, 52, 29,
+		39, 40, 41, 45, 47, 21, 50, 46, 42, 34, 30, 14};
+	static const int kFramesPerPage = 35;
+
+	Common::Array<Common::StringArray> creditTexts = _res->getCredits();
+
+	Common::File roomFile;
+	if (!roomFile.open(Common::Path("ALFRED.1"))) {
+		error("Could not open ALFRED.1 for credits");
+		return;
+	}
+
+	byte *bg = new byte[640 * 400];
+	byte *palette = new byte[768];
+	CursorMan.showMouse(false);
+
+	// Outer restart loop — keypress during display restarts from page 0
+	bool restart = false;
+	_alfredState.setState(ALFRED_SKIP_DRAWING);
+	_disableAmbientSounds = true;
+	do {
+		restart = false;
+		for (int page = 0; page < kNumCreditPages && !shouldQuit(); page++) {
+			debug("Credits: loading page %d (room %d)", page, kCreditRooms[page]);
+			setScreen(kCreditRooms[page]);
+
+			Common::StringArray lines;
+
+			if (page < (int)creditTexts.size()) {
+				for (const Common::String &block : creditTexts[page]) {
+					Common::String cur;
+					for (uint ci = 0; ci < block.size(); ci++) {
+						byte b = (byte)block[ci];
+						if (b == 0xB1) {
+							if (!cur.empty()) {
+								lines.push_back(cur);
+								cur.clear();
+							}
+						} else if (b >= 0x20) {
+							cur += (char)b;
+						}
+					}
+					if (!cur.empty())
+						lines.push_back(cur);
+				}
+			}
 
-	debug("Starting credits sequence");
+			// Compute vertical centering (LargeFont: CHAR_HEIGHT = 24px per line)
+			int lineH = _largeFont->getFontHeight();
+			int totalH = (int)lines.size() * lineH;
+			int startY = (400 - totalH) / 2;
+
+			byte speakerId;
+			_dialog->processColorAndTrim(lines, speakerId);
+			Graphics::Surface s = _dialog->getDialogueSurface(lines, speakerId);
+
+			int frames = 0;
+			_events->_lastKeyEvent = Common::KEYCODE_INVALID;
+			while (!shouldQuit() && frames < kFramesPerPage) {
+				debug("Credits page %d, frame %d/%d", page, frames, kFramesPerPage);
+				_events->pollEvent();
+				bool didRender = renderScene(OVERLAY_NONE);
+
+				if (didRender) {
+					_screen->transBlitFrom(s, s.getRect(), Common::Point(0, startY), 255);
+
+					if (_chrono->getFrameCount() % 2 == 0) // Original game increments frame counter every other frame
+						frames++;
+				}
+				_screen->markAllDirty();
+				_screen->update();
+				g_system->delayMillis(10);
+
+				// Keypress → restart slideshow from page 0 (original behaviour)
+				if (_events->_lastKeyEvent != Common::KEYCODE_INVALID) {
+					_events->_lastKeyEvent = Common::KEYCODE_INVALID;
+					restart = true;
+					break;
+				}
+			}
+
+			_screen->markAllDirty();
+			_screen->update();
+			if (restart)
+				break;
+		}
+	} while (restart && !shouldQuit());
+
+	_disableAmbientSounds = false;
+	delete[] bg;
+	delete[] palette;
+	roomFile.close();
+	CursorMan.showMouse(true);
 }
 
 void PelrockEngine::initGodsSequences(int roomNumber) {
@@ -2414,7 +2515,7 @@ void PelrockEngine::handleFlightRoomFrame() {
 			_alfredState.x = 294;
 			_alfredState.y = 387;
 			_alfredState.direction = ALFRED_UP;
-			setScreen(49, ALFRED_UP);
+			setScreenAndPrepare(49, ALFRED_UP);
 		}
 	}
 }
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index e15f62cf509..6b57cbbc8c9 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -189,6 +189,7 @@ public:
 	bool _flightSpellCast = false;
 	int _flightSpellFrameCounter = 0;
 	bool _flightInBlockingAnim = false;
+	bool _disableAmbientSounds = false;
 
 	GameStateData *_state = new GameStateData();
 
@@ -247,7 +248,8 @@ public:
 		return syncGame(s);
 	}
 
-	void setScreen(int s, AlfredDirection dir);
+	void setScreen(int s);
+	void setScreenAndPrepare(int s, AlfredDirection dir);
 	void loadExtraScreenAndPresent(int screenIndex);
 	void waitForSpecialAnimation();
 	bool renderScene(int overlayMode = OVERLAY_NONE);
diff --git a/engines/pelrock/saveload.cpp b/engines/pelrock/saveload.cpp
index c110acaf2b7..07ee9409bba 100644
--- a/engines/pelrock/saveload.cpp
+++ b/engines/pelrock/saveload.cpp
@@ -373,7 +373,7 @@ void PelrockEngine::loadGame(SaveGameData &saveGame) {
 	_alfredState.direction = (AlfredDirection)saveGame.alfredDir;
 	_state = saveGame.gameState;
 
-	setScreen(saveGame.currentRoom, _alfredState.direction);
+	setScreenAndPrepare(saveGame.currentRoom, _alfredState.direction);
 	_state->stateGame = GAME;
 }
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 22f7d967f88..7b5483f032d 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -484,8 +484,6 @@ struct ResetEntry {
 #define FLAG_MIRA_SIMBOLO_FUERA_MUSEO 15
 #define FLAG_CROCODILLO_ENCENDIDO 14
 #define FLAG_CLAVE_CAJA_FUERTE 19
-#define FLAG_INGREDIENTES_CONSEGUIDOS 48
-#define FLAG_MERCHANT_GIVENITEMS 58
 #define FLAG_ROBA_PELO_PRINCESA 17
 #define FLAG_A_LA_CARCEL 18
 #define FLAG_SE_HA_PUESTO_EL_MUNECO 20
@@ -494,10 +492,25 @@ struct ResetEntry {
 #define FLAG_DA_PIEDRA 31
 #define FLAG_PIEDRAS_COGIDAS 32
 #define FLAG_GUARDIAS_BORRACHOS 33
-#define FLAG_PUERTA_BUENA 35
 #define FLAG_PIEDRA_FAKE_MOJADA 34
-#define FLAG_TIENDA_ABIERTA 46
+#define FLAG_PUERTA_BUENA 35
+#define FLAG_TRAMPILLA_ABIERTA 36
 #define FLAG_COMO_ESTAN_LOS_DIOSES 41
+#define FLAG_TIENDA_ABIERTA 46
+#define FLAG_END_OF_GAME 42
+#define FLAG_INGREDIENTES_CONSEGUIDOS 48
+#define FLAG_GUARDIA_PIDECOSAS 49
+#define FLAG_GUARDIA_DNI_ENTREGADO 50
+#define FLAG_AGENCIA_ABIERTA 51
+#define FLAG_CONSIGNAS_VENDEDOR 52
+#define FLAG_PUTA_250_VECES 53
+#define FLAG_RESPUESTAS_ACERTADAS 54
+#define FLAG_CHEAT_CODE_ENABLED 55
+#define FLAG_RIDDLE_PRESENTED 56
+#define FLAG_SYMBOLS_PUSHED 57
+#define FLAG_MERCHANT_GIVENITEMS 58
+#define FLAG_CORRECT_DOOR_CHOSEN 59
+#define FLAG_ESQUELETO_RECONOCE 60
 
 #define FLAG_VIAJE_A_EGIPTO 12
 #define FLAG_PUERTA_SECRETA_ABIERTA 16
@@ -510,29 +523,15 @@ struct ResetEntry {
 #define FLAG_AL_FARAON 29
 #define FLAG_A_CURRAR 30
 
-#define FLAG_TRAMPILLA_ABIERTA 36
 #define FLAG_HABITACION_PRINCESA 37
 #define FLAG_A_POR_LA_PRINCESA 38
 #define FLAG_VUELTA_A_EMPEZAR 39
 #define FLAG_A_LOS_PASILLOS 40
-#define FLAG_END_OF_GAME 42
 #define FLAG_FROM_INTRO 43
 #define FLAG_HE_TIRADO_PIEDRA 44
 #define FLAG_HA_USADO_AGUA 45
 #define FLAG_NUMERO_DE_COPAS 47
 
-#define FLAG_GUARDIA_PIDECOSAS 49
-#define FLAG_GUARDIA_DNI_ENTREGADO 50
-#define FLAG_AGENCIA_ABIERTA 51
-#define FLAG_CONSIGNAS_VENDEDOR 52
-#define FLAG_PUTA_250_VECES 53
-#define FLAG_RESPUESTAS_ACERTADAS 54
-#define FLAG_CHEAT_CODE_ENABLED 55
-#define FLAG_RIDDLE_PRESENTED 56
-#define FLAG_SYMBOLS_PUSHED 57
-#define FLAG_CORRECT_DOOR_CHOSEN 58
-#define FLAG_ESQUELETO_RECONOCE 59
-
 const int kNumGameFlags = 61;
 
 struct GameStateData {


Commit: 923774a099fe97495d7361924aaa3114da9b5ec1
    https://github.com/scummvm/scummvm/commit/923774a099fe97495d7361924aaa3114da9b5ec1
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:18+02:00

Commit Message:
PELROCK: Implements pyramid shaking scene

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index b107fb3c7c2..3e343aec52a 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1490,7 +1490,18 @@ void PelrockEngine::giveWaterToGuard(int inventoryObject, HotSpot *hotspot) {
 }
 
 void PelrockEngine::pickUpStone(HotSpot *hotspot) {
+	_room->addSticker(117);
+	_state->setFlag(FLAG_PIRAMIDE_JODIDA, true);
+	_sound->playSound("QUAKE2ZZ.SMP", 0, 0);
+	_shakeEffectState.enable();
 	checkIngredients();
+	_dialog->say(_res->_ingameTexts[AYAYAY]);
+	_alfredState.direction = ALFRED_DOWN;
+	_dialog->say(_res->_ingameTexts[NADIELOHAVISTO]);
+
+	_alfredState.direction = ALFRED_LEFT;
+	_disableAction = true;
+	walkTo(592, 306);
 }
 
 void PelrockEngine::playSpecialAnim(uint32 offset, bool compressed, int x, int y, int width, int height, int numFrames) {
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index f5e691e6dbb..cfcfdf4a868 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -240,12 +240,20 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		int xPos = xBasePos - maxWidth / 2;
 		int yPos = yBasePos - height;
 
+
 		Graphics::Surface s = getDialogueSurface(textLines, speakerId);
 
 		// Clamp to screen bounds (original game: min Y = 1, max X = 639 - width)
 		xPos = CLIP(xPos, 0, 639 - maxWidth);
 		yPos = CLIP(yPos, 1, 400 - (int)s.getRect().height());
 
+		if(g_engine->_shakeEffectState.enabled) {
+			debug("Applying shake effect to dialogue, shakeX: %d", g_engine->_shakeEffectState.shakeX);
+			xPos -= g_engine->_shakeEffectState.shakeX;
+		} else {
+			debug("No shake effect applied to dialogue");
+		}
+
 		_screen->transBlitFrom(s, s.getRect(), Common::Point(xPos, yPos), 255);
 		drawPos(_screen, xPos, yPos, speakerId);
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 261104fd926..77031c2deb7 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -367,6 +367,21 @@ void PelrockEngine::frameTriggers() {
 	uint32 frameCount = _chrono->getFrameCount();
 	passerByAnim(frameCount);
 	handleFlightRoomFrame();
+	shakeEffect();
+}
+
+void PelrockEngine::shakeEffect() {
+	if(!_shakeEffectState.enabled) {
+		return;
+	}
+	if(_room->_currentRoomNumber == 36) {
+		_shakeEffectState.disable();
+		return;
+	}
+
+	_shakeEffectState.shakeX = (_chrono->getFrameCount() % 4 < 2) ? 2 : -2;
+	g_system->setShakePos(_shakeEffectState.shakeX, _shakeEffectState.shakeY);
+	_alfredState.x += (_shakeEffectState.shakeX/2); // Adjust Alfred's position to counteract shake for better readability
 }
 
 void PelrockEngine::passerByAnim(uint32 frameCount) {
@@ -2397,7 +2412,6 @@ void PelrockEngine::credits() {
 			byte speakerId;
 			_dialog->processColorAndTrim(lines, speakerId);
 			Graphics::Surface s = _dialog->getDialogueSurface(lines, speakerId);
-
 			int frames = 0;
 			_events->_lastKeyEvent = Common::KEYCODE_INVALID;
 			while (!shouldQuit() && frames < kFramesPerPage) {
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 6b57cbbc8c9..11bf406a39d 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -179,6 +179,7 @@ public:
 	ChronoManager *_chrono = nullptr;
 	PelrockEventManager *_events = nullptr;
 	AlfredState _alfredState;
+	ShakeEffectState _shakeEffectState;
 	byte *_compositeBuffer = nullptr; // Working composition buffer
 
 	bool _mouseDisabled = false;
@@ -255,6 +256,7 @@ public:
 	bool renderScene(int overlayMode = OVERLAY_NONE);
 	void mouseHoverForMap();
 	void frameTriggers();
+	void shakeEffect();
 	void handleFlightRoomFrame();
 
 	void passerByAnim(uint32 frameCount);
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index fbb24786726..f543601fe3b 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -59,16 +59,16 @@ void SoundManager::playSound(byte index, int channel) {
 	}
 }
 
-void SoundManager::playSound(const char *filename, int channel) {
+void SoundManager::playSound(const char *filename, int channel, int loopCount) {
 	auto it = _soundMap.find(filename);
 	if (it != _soundMap.end()) {
-		playSound(it->_value, channel);
+		playSound(it->_value, channel, loopCount);
 	} else {
 		debug("Sound file %s not found in sound map", filename);
 	}
 }
 
-void SoundManager::playSound(SonidoFile sound, int channel) {
+void SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
 	Common::File sonidosFile;
 	if (!sonidosFile.open(Common::Path("SONIDOS.DAT"))) {
 		debug("Failed to open SONIDOS.DAT");
@@ -82,7 +82,7 @@ void SoundManager::playSound(SonidoFile sound, int channel) {
 
 	SoundFormat format = detectFormat(data, sound.size);
 	uint32_t sampleRate = getSampleRate(data, format);
-	Audio::AudioStream *stream = nullptr;
+	Audio::SeekableAudioStream *stream = nullptr;
 
 	if (format == SOUND_FORMAT_RIFF) {
 		// For WAV/RIFF files, use the wave decoder
@@ -117,7 +117,9 @@ void SoundManager::playSound(SonidoFile sound, int channel) {
 				_mixer->stopHandle(_sfxHandles[channel]);
 			}
 		}
-		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, 255U, 0, DisposeAfterUse::YES);
+		Audio::AudioStream *finalStream = loopCount == -1 ? stream : Audio::makeLoopingAudioStream(stream, 0);
+
+		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], finalStream, loopCount, 255U, 0, DisposeAfterUse::YES);
 	}
 }
 
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index 09a5941ac0d..ec4d1f4dd81 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -29,12 +29,12 @@
 
 namespace Pelrock {
 
-typedef struct {
+struct SonidoFile {
 	Common::String filename;
 	uint32_t offset;
 	uint32_t size;
 	unsigned char *data;
-} SonidoFile;
+};
 
 static const char *SOUND_FILENAMES[] = {
 	"NO_SOUND.SMP", // 0 - Silence/disabled
@@ -158,7 +158,7 @@ public:
 	SoundManager(Audio::Mixer *mixer);
 	~SoundManager();
 	void playSound(byte index, int channel = -1);
-	void playSound(const char *filename, int channel);
+	void playSound(const char *filename, int channel, int loopCount = 1);
 	void playSound(byte *soundData, uint32 size);
 	void stopAllSounds();
 	void stopSound(int channel);
@@ -183,7 +183,7 @@ public:
 	byte _currentMusicTrack = 0;
 
 private:
-	void playSound(SonidoFile sound, int channel = -1);
+	void playSound(SonidoFile sound, int channel = -1, int loopCount = 1);
 	SoundFormat detectFormat(byte *data, uint32 size);
 	int getSampleRate(byte *data, SoundFormat format);
 	int findFreeChannel();
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 7b5483f032d..1d4079d5cd3 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -193,23 +193,42 @@ struct AlfredState {
 	}
 };
 
-typedef struct {
+struct ShakeEffectState
+{
+	bool enabled = false;
+	int shakeX = 0;
+	int shakeY = 0;
+
+	void enable() {
+		enabled = true;
+	}
+
+	void disable() {
+		enabled = false;
+		shakeX = 0;
+		shakeY = 0;
+		g_system->setShakePos(0, 0);
+	}
+};
+
+
+struct MovementStep {
 	uint8_t flags;      /* Direction flags (see MOVE_* constants) */
 	uint16_t distanceX; // Horizontal distance to move
 	uint16_t distanceY; // Vertical distance to move
-} MovementStep;
+};
 
 /**
  * Pathfinding context
  */
-typedef struct {
+struct PathContext {
 	uint8_t *pathBuffer;          // Sequence of walkbox indices
 	MovementStep *movementBuffer; // Array of movement steps
 	uint8_t *compressed_path;     // Final compressed path
 	uint16_t pathLength;
 	uint16_t movementCount;
 	uint16_t compressed_length;
-} PathContext;
+};
 
 struct Anim {
 	int nframes;
@@ -488,6 +507,7 @@ struct ResetEntry {
 #define FLAG_A_LA_CARCEL 18
 #define FLAG_SE_HA_PUESTO_EL_MUNECO 20
 #define FLAG_VIGILANTE_BEBE_AGUA 21
+#define FLAG_PIRAMIDE_JODIDA 23
 #define FLAG_FORMULA_MAGICA 26
 #define FLAG_DA_PIEDRA 31
 #define FLAG_PIEDRAS_COGIDAS 32
@@ -515,7 +535,6 @@ struct ResetEntry {
 #define FLAG_VIAJE_A_EGIPTO 12
 #define FLAG_PUERTA_SECRETA_ABIERTA 16
 #define FLAG_VIGILANTE_MEANDO 22
-#define FLAG_PIRAMIDE_JODIDA 23
 #define FLAG_PIRAMIDE_JODIDA2 24
 #define FLAG_VIGILANTE_PAJEANDOSE 25
 #define FLAG_VIAJA_AL_PASADO 27


Commit: 919f61d3f8c0dd60cbd4738b5a4c4ada3f879dbe
    https://github.com/scummvm/scummvm/commit/919f61d3f8c0dd60cbd4738b5a4c4ada3f879dbe
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:19+02:00

Commit Message:
PELROCK: Pyramid collapsing animation on room 36

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/sound.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 3e343aec52a..8812bf50dad 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1454,34 +1454,10 @@ void PelrockEngine::giveWaterToGuard(int inventoryObject, HotSpot *hotspot) {
 	_dialog->say(_res->_ingameTexts[ALACONUSTED]);
 	_state->removeInventoryItem(86);
 	addInventoryItem(76);
-	if (_state->getFlag(FLAG_VIGILANTE_BEBE_AGUA) == 1) {
+	if (_state->getFlag(FLAG_VIGILANTE_BEBE_AGUA) == 3) {
 		_dialog->say(_res->_ingameTexts[MEMEO]);
-
-		// guard running
-		Sprite *sprite = _room->findSpriteByIndex(0);
-		sprite->animData[0].nframes = 5;
-		sprite->animData[0].movementFlags = 0x1C; // Move right
-		byte state = 0;
-		// Basic loop to wait until the sprite has reached the door
-		while (!shouldQuit()) {
-			_events->pollEvent();
-			renderScene();
-			if (sprite->x >= 339 && state == 0) {
-				state = 1;
-				sprite->animData[0].movementFlags = 0x240; // Move up
-			}
-			if (sprite->y <= 188 && state == 1) {
-				state = 2;
-				sprite->animData[0].movementFlags = 0x14; // Move left
-			}
-			if (sprite->x <= 327 && state == 2) {
-				sprite->zOrder = -1; // Hide sprite
-				break;
-			}
-			debug("Guard position: (%d, %d), state: %d", sprite->x, sprite->y, state);
-			_screen->update();
-			g_system->delayMillis(10);
-		}
+		_state->setFlag(FLAG_VIGILANTE_MEANDO, true);
+		guardMovement();
 		_room->disableSprite(36, 0, PERSIST_BOTH);
 		_room->enableExit(0, PERSIST_BOTH);
 	} else {
@@ -1489,10 +1465,39 @@ void PelrockEngine::giveWaterToGuard(int inventoryObject, HotSpot *hotspot) {
 	}
 }
 
+void PelrockEngine::guardMovement() {
+
+	// guard running
+	Sprite *sprite = _room->findSpriteByIndex(0);
+	sprite->animData[0].nframes = 5;
+	sprite->animData[0].movementFlags = 0x1C; // Move right
+	byte state = 0;
+	// Basic loop to wait until the sprite has reached the door
+	while (!shouldQuit()) {
+		_events->pollEvent();
+		renderScene();
+		if (sprite->x >= 339 && state == 0) {
+			state = 1;
+			sprite->animData[0].movementFlags = 0x240; // Move up
+		}
+		if (sprite->y <= 188 && state == 1) {
+			state = 2;
+			sprite->animData[0].movementFlags = 0x14; // Move left
+		}
+		if (sprite->x <= 327 && state == 2) {
+			sprite->zOrder = -1; // Hide sprite
+			break;
+		}
+		debug("Guard position: (%d, %d), state: %d", sprite->x, sprite->y, state);
+		_screen->update();
+		g_system->delayMillis(10);
+	}
+}
+
 void PelrockEngine::pickUpStone(HotSpot *hotspot) {
 	_room->addSticker(117);
 	_state->setFlag(FLAG_PIRAMIDE_JODIDA, true);
-	_sound->playSound("QUAKE2ZZ.SMP", 0, 0);
+	_sound->playSound("QUAKE2ZZ.SMP", 0, -1);
 	_shakeEffectState.enable();
 	checkIngredients();
 	_dialog->say(_res->_ingameTexts[AYAYAY]);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 77031c2deb7..2f804f30f6c 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -154,7 +154,9 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		setScreenAndPrepare(0, ALFRED_LEFT);
+		// setScreenAndPrepare(0, ALFRED_LEFT);
+				setScreenAndPrepare(36, ALFRED_LEFT);
+
 		// setScreen(3, ALFRED_RIGHT);
 		// setScreen(22, ALFRED_DOWN);
 		// setScreen(41, ALFRED_DOWN);
@@ -374,10 +376,6 @@ void PelrockEngine::shakeEffect() {
 	if(!_shakeEffectState.enabled) {
 		return;
 	}
-	if(_room->_currentRoomNumber == 36) {
-		_shakeEffectState.disable();
-		return;
-	}
 
 	_shakeEffectState.shakeX = (_chrono->getFrameCount() % 4 < 2) ? 2 : -2;
 	g_system->setShakePos(_shakeEffectState.shakeX, _shakeEffectState.shakeY);
@@ -1737,6 +1735,12 @@ void PelrockEngine::gameLoop() {
 		endingScene();
 		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
 	}
+	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_u) {
+		if(_room->_currentRoomNumber == 36) {
+			pyramidCollapse();
+		}
+		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
+	}
 
 	renderScene();
 
@@ -2142,6 +2146,20 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		}
 		break;
 	}
+	case 36: {
+		if(_shakeEffectState.enabled) {
+			_shakeEffectState.disable();
+		}
+		if(_state->getFlag(FLAG_PIRAMIDE_JODIDA) == true &&
+		// _state->getFlag(FLAG_VIGILANTE_MEANDO) == true &&
+		_state->getFlag(FLAG_PIRAMIDE_JODIDA2) == false) {
+			_state->setFlag(FLAG_VIGILANTE_MEANDO, false);
+			_state->setFlag(FLAG_PIRAMIDE_JODIDA2, true);
+			debug("Pyramid is now active!");
+			pyramidCollapse();
+		}
+		break;
+	}
 	case 39:
 	case 40:
 		// Rooms 39/40 are the pharaoh's guard and throne room — all conversation
@@ -2224,6 +2242,128 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 	}
 }
 
+void PelrockEngine::pyramidCollapse() {
+	// === Pyramid Collapse Sequence (Room 36 per-frame handler at 0x1098F) ===
+	// Binary: sprite index 2 = collapse animation, sprite index 0 = NPC guard.
+	// Original sprite table indices are offset by 2 from ScummVM indices due
+	// to the 2 header sprite slots in the room data.
+
+	// Hide NPC initially — binary sets sprite_2 field 0x21 = 0xFF (zOrder = -1)
+	Sprite *npc = _room->findSpriteByIndex(0);
+	if (npc)
+		npc->zOrder = -1;
+
+	// Start collapse animation — binary sets sprite_4 field 0x21 = 0xFE (zOrder = 254)
+	Sprite *collapseSprite = _room->findSpriteByIndex(2);
+	if (collapseSprite)
+		collapseSprite->zOrder = 254;
+
+	// Play collapse sound
+	_sound->playSound("QUAKE1ZZ.SMP", 0);
+
+	// ----- PHASE 1: Wait for collapse animation frame 5 -----
+	// Binary: loop sleep(0x69) + tick + render, check sprite_4 field 0x20 == 5
+	while (!shouldQuit()) {
+		_events->pollEvent();
+		renderScene(OVERLAY_NONE);
+		_screen->update();
+		g_system->delayMillis(10);
+		collapseSprite = _room->findSpriteByIndex(2);
+		if (!collapseSprite || collapseSprite->animData[collapseSprite->curAnimIndex].curFrame >= 5) {
+			collapseSprite->zOrder = -1; // Hide collapse animation sprite after frame 5
+			break;
+		}
+	}
+	debug("Collapse animation reached frame 5, applying background changes");
+
+	// ----- PHASE 2: Background tile copies (hide pyramid top) -----
+	// Copy 1: 99×45 from secondary buffer to front buffer (fills collapsed area)
+	{
+		static const int srcX = 240, srcY = 145;
+		// static const int dstX = 510, dstY = 33;
+		static const int copyW = 99, copyH = 45;
+		for (int row = 0; row < copyH; row++) {
+			memcpy(
+				_currentBackground + (srcY + row) * 640 + srcX,
+				_compositeBuffer + (srcY + row) * 640 + srcX,
+				copyW);
+		}
+	}
+	_room->findSpriteByIndex(2)->zOrder = -1;
+
+	_dialog->say(_res->_ingameTexts[YANOSEHACEONCOMOANTES]);
+	npc = _room->findSpriteByIndex(0);
+	if (npc)
+		npc->zOrder = 254;
+
+	npc = _room->findSpriteByIndex(0);
+	npc->animData[0].nframes = 5;
+	if (npc) {
+		npc->animData[npc->curAnimIndex].movementFlags = 0x1C;
+		npc->y -= 25; // One-time nudge upward to emerge from behind pyramid
+	}
+	while (!shouldQuit()) {
+		_events->pollEvent();
+		renderScene(OVERLAY_NONE);
+		_screen->update();
+		g_system->delayMillis(10);
+		npc = _room->findSpriteByIndex(0);
+		if (!npc || npc->x >= 339) break;
+	}
+
+	npc = _room->findSpriteByIndex(0);
+	if (npc)
+		npc->animData[npc->curAnimIndex].movementFlags = 0x340;
+	while (!shouldQuit()) {
+		_events->pollEvent();
+		renderScene(OVERLAY_NONE);
+		_screen->update();
+		g_system->delayMillis(10);
+		npc = _room->findSpriteByIndex(0);
+		if (!npc || npc->y >= 206) break;
+	}
+
+	npc = _room->findSpriteByIndex(0);
+	if (npc)
+		npc->animData[npc->curAnimIndex].movementFlags = 0x14;
+	while (!shouldQuit()) {
+		_events->pollEvent();
+		renderScene(OVERLAY_NONE);
+		_screen->update();
+		g_system->delayMillis(10);
+		npc = _room->findSpriteByIndex(0);
+		if (!npc || npc->x <= 307) break;
+	}
+
+	// Stop NPC movement
+	npc = _room->findSpriteByIndex(0);
+	npc->animData[0].nframes = 1;
+	if (npc)
+		npc->animData[npc->curAnimIndex].movementFlags = 0;
+
+	_dialog->say(_res->_ingameTexts[POR5MINUTOS], 0);
+
+	_room->disableExit(36, 0);
+
+	_room->addStickerToRoom(21, 79);
+	_room->disableExit(21, 2, PERSIST_BOTH);
+	HotSpot *pyramidHotspot = new HotSpot();
+	pyramidHotspot->x = 510;
+	pyramidHotspot->y = 33;
+	pyramidHotspot->w = 99;
+	pyramidHotspot->h = 45;
+	pyramidHotspot->extra = 411;
+	pyramidHotspot->isEnabled = false;
+	pyramidHotspot->innerIndex = 2;
+	pyramidHotspot->index = 7;
+	_room->disableHotspot(21, pyramidHotspot, PERSIST_BOTH);
+
+	_dialog->say(_res->_ingameTexts[TALUEGOLUCAS]);
+
+	// Walk Alfred to right edge exit -> room 21
+	walkLoop(603, 212, ALFRED_RIGHT);
+}
+
 void PelrockEngine::endingScene() {
 	byte *palette = new byte[768];
 	if (_bgScreen == nullptr) {
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 11bf406a39d..872160fa48a 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -267,6 +267,7 @@ public:
 
 	// Actions
 	void doExtraActions(int roomNumber);
+void pyramidCollapse();
 	void endingScene();
 	void credits();
 	void initGodsSequences(int roomNumber);
@@ -379,6 +380,7 @@ public:
 	void useDollWithBed(int inventoryObject, HotSpot *hotspot);
 	void giveMagazineToGuard(int inventoryObject, HotSpot *hotspot);
 	void giveWaterToGuard(int inventoryObject, HotSpot *hotspot);
+	void guardMovement();
 	void pickUpStone(HotSpot *hotspot);
 	void playSpecialAnim(uint32 offset, bool compressed, int x, int y, int width, int height, int numFrames);
 	void giveStoneToSlaves(int inventoryObject, HotSpot *hotspot);
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index f543601fe3b..5015e32566c 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -117,7 +117,8 @@ void SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
 				_mixer->stopHandle(_sfxHandles[channel]);
 			}
 		}
-		Audio::AudioStream *finalStream = loopCount == -1 ? stream : Audio::makeLoopingAudioStream(stream, 0);
+		debug("Playing sound with loop count %d on channel %d", loopCount, channel);
+		Audio::AudioStream *finalStream = loopCount != -1 ? stream : Audio::makeLoopingAudioStream(stream, 0);
 
 		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], finalStream, loopCount, 255U, 0, DisposeAfterUse::YES);
 	}
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 1d4079d5cd3..0bfe3ead7c2 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -507,7 +507,9 @@ struct ResetEntry {
 #define FLAG_A_LA_CARCEL 18
 #define FLAG_SE_HA_PUESTO_EL_MUNECO 20
 #define FLAG_VIGILANTE_BEBE_AGUA 21
+#define FLAG_VIGILANTE_MEANDO 22
 #define FLAG_PIRAMIDE_JODIDA 23
+#define FLAG_PIRAMIDE_JODIDA2 24
 #define FLAG_FORMULA_MAGICA 26
 #define FLAG_DA_PIEDRA 31
 #define FLAG_PIEDRAS_COGIDAS 32
@@ -534,8 +536,6 @@ struct ResetEntry {
 
 #define FLAG_VIAJE_A_EGIPTO 12
 #define FLAG_PUERTA_SECRETA_ABIERTA 16
-#define FLAG_VIGILANTE_MEANDO 22
-#define FLAG_PIRAMIDE_JODIDA2 24
 #define FLAG_VIGILANTE_PAJEANDOSE 25
 #define FLAG_VIAJA_AL_PASADO 27
 #define FLAG_APARECE_EUNUCO 28


Commit: e53ffc7a6d7275c9db8e4befa3bd971e51841ac9
    https://github.com/scummvm/scummvm/commit/e53ffc7a6d7275c9db8e4befa3bd971e51841ac9
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:19+02:00

Commit Message:
PELROCK: Adjusts text in credits slideshow

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index cfcfdf4a868..21fcbacf1e0 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -158,7 +158,7 @@ void DialogManager::displayChoices(Common::Array<ChoiceOption> *choices, byte *c
 	}
 }
 
-Graphics::Surface DialogManager::getDialogueSurface(Common::Array<Common::String> dialogueLines, byte speakerId) {
+Graphics::Surface DialogManager::getDialogueSurface(Common::Array<Common::String> dialogueLines, byte speakerId, Graphics::TextAlign alignment) {
 
 	int maxWidth = 0;
 	int height = dialogueLines.size() * 25; // Add some padding
@@ -174,7 +174,8 @@ Graphics::Surface DialogManager::getDialogueSurface(Common::Array<Common::String
 
 		int xPos = 0;
 		int yPos = i * 25; // Above sprite, adjust for line
-		g_engine->_largeFont->drawString(&s, dialogueLines[i], xPos, yPos, maxWidth, speakerId, Graphics::kTextAlignCenter);
+		debug("Drawing dialogue line %d: \"%s\" at position (%d, %d) with speaker ID %d", i, dialogueLines[i].c_str(), xPos, yPos, speakerId);
+		g_engine->_largeFont->drawString(&s, dialogueLines[i], xPos, yPos, maxWidth, speakerId, alignment);
 	}
 
 	return s;
@@ -1028,21 +1029,25 @@ bool DialogManager::processColorAndTrim(Common::StringArray &lines, byte &speake
 	speakerId = lines[0][1];
 
 	if (speakerMarker == '@') {
-
 		for (int i = 0; i < lines.size(); i++) {
 			// Remove first two marker bytes
-			if (lines[i].size() > 2) {
-				lines[i] = lines[i].substr(2);
-
-				if (lines[i][0] == 0x78 && lines[i][1] == 0x78) { // Remove additional control chars
+			if (i == 0) {
+				if (lines[i].size() > 2) {
 					lines[i] = lines[i].substr(2);
+
+					if (lines[i][0] == 0x78 && lines[i][1] == 0x78) { // Remove additional control chars
+						lines[i] = lines[i].substr(2);
+					}
+				} else {
+					lines[i] = "";
 				}
-			} else {
-				lines[i] = "";
 			}
 		}
 		return true;
 	}
+	else {
+		debug("No speaker marker found, defaulting to Alfred");
+	}
 	return false;
 }
 
@@ -1080,7 +1085,7 @@ int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
 }
 
 Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::String text) {
-
+	debug("Word-wrapping text: \"%s\"", text.c_str());
 	Common::Array<Common::Array<Common::String>> pages;
 	Common::Array<Common::String> currentPage;
 	Common::Array<Common::String> currentLine;
@@ -1090,9 +1095,9 @@ Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::Str
 	while (position < text.size()) {
 		bool isEnd = false;
 		int wordLength = calculateWordLength(text, position, isEnd);
-		// # Extract the word (including trailing spaces)
+		// Extract the word (including trailing spaces)
 		Common::String word = text.substr(position, wordLength);
-		// # Key decision: if word_length > chars_remaining, wrap to next line
+		// if word_length > chars_remaining, wrap to next line
 		if (wordLength > charsRemaining) {
 			// Word is longer than the entire line - need to split
 			currentPage.push_back(joinStrings(currentLine, ""));
@@ -1137,32 +1142,63 @@ Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::Str
 			break;
 		}
 	}
-	if (currentLine.empty() == false) {
+
+	if (!currentLine.empty()) {
 		Common::String lineText = joinStrings(currentLine, "");
 		while (lineText.lastChar() == CHAR_SPACE) {
 			lineText = lineText.substr(0, lineText.size() - 1);
 		}
 		currentPage.push_back(lineText);
 	}
-	if (currentPage.empty() == false) {
+
+	if (!currentPage.empty()) {
 		pages.push_back(currentPage);
 	}
-	for (int i = 0; i < pages.size(); i++) {
-		for (int j = 0; j < pages[i].size(); j++) {
-		}
+
+	//print all the pages and lines for debugging
+	for (uint i = 0; i < pages.size(); i++) {
+		debug("Page %d:", i);
+		for (uint j = 0; j < pages[i].size(); j++) {
+			debug(" Line %d: \"%s\"", j, pages[i][j].c_str());
+				}
 	}
 	return pages;
 }
 
-Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::StringArray texts) {
-	Common::Array<Common::Array<Common::String>> allWrappedLines;
-	for (int i = 0; i < texts.size(); i++) {
-		Common::Array<Common::Array<Common::String>> wrapped = wordWrap(texts[i]);
-		for (int j = 0; j < wrapped.size(); j++) {
-			allWrappedLines.push_back(wrapped[j]);
+Common::Array<Common::StringArray> DialogManager::wordWrap(Common::StringArray texts) {
+	// Sometimes we already get a pre-processed list of strings the character has to speak
+	// but we still need to add line breaks if they exceed the max chars.
+	// That means if we receive 3 lines but one is longer than the max we need to make 4 lines.
+	// if we already have 5 pages, but one of them exceeds the max, we need to construct a new page.
+	// for this is not enough to just push the result of wordWrap(String) becasue we still need to calculate new pages.
+
+	Common::Array<Common::StringArray> pages;
+	Common::Array<Common::String> currentPage;
+	int currentLineNum = 0;
+	for (uint i = 0; i < texts.size(); i++) {
+		Common::String thisLine = texts[i];
+		Common::Array<Common::Array<Common::String>> wrapped = wordWrap(thisLine);
+		debug("Wrapped line %s, %d into %d pages", thisLine.c_str(), thisLine.size(), wrapped.size());
+		for (uint j = 0; j < wrapped.size(); j++) {
+			for (int i = 0; i < wrapped[j].size(); i++)
+			{
+				if(currentLineNum < MAX_LINES) {
+					currentPage.push_back(wrapped[j][i]);
+					currentLineNum++;
+				} else {
+					pages.push_back(currentPage);
+					currentPage.clear();
+					currentPage.push_back(wrapped[j][i]);
+					currentLineNum = 1;
+				}
+			}
 		}
 	}
-	return allWrappedLines;
-}
 
+	if (!currentPage.empty()) {
+		pages.push_back(currentPage);
+	}
+
+	return pages;
+}
 } // namespace Pelrock
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index e3ce72c7e9f..dbffbeb2d58 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -111,7 +111,7 @@ public:
 	void say(Common::StringArray texts, byte spriteIndex = 0);
 	void say(Common::StringArray texts, int16 x, int16 y);
 	bool processColorAndTrim(Common::StringArray &lines, byte &speakerId);
-	Graphics::Surface getDialogueSurface(Common::Array<Common::String> dialogueLines, byte speakerId);
+	Graphics::Surface getDialogueSurface(Common::Array<Common::String> dialogueLines, byte speakerId, Graphics::TextAlign alignment = Graphics::kTextAlignCenter);
 
 	Common::Array<Common::Array<Common::String>> wordWrap(Common::String text);
 	Common::Array<Common::Array<Common::String>> wordWrap(Common::StringArray texts);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 2f804f30f6c..469ca86bab0 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -155,7 +155,7 @@ void PelrockEngine::init() {
 		gameInitialized = true;
 		loadAnims();
 		// setScreenAndPrepare(0, ALFRED_LEFT);
-				setScreenAndPrepare(36, ALFRED_LEFT);
+		setScreenAndPrepare(36, ALFRED_LEFT);
 
 		// setScreen(3, ALFRED_RIGHT);
 		// setScreen(22, ALFRED_DOWN);
@@ -373,13 +373,13 @@ void PelrockEngine::frameTriggers() {
 }
 
 void PelrockEngine::shakeEffect() {
-	if(!_shakeEffectState.enabled) {
+	if (!_shakeEffectState.enabled) {
 		return;
 	}
 
 	_shakeEffectState.shakeX = (_chrono->getFrameCount() % 4 < 2) ? 2 : -2;
 	g_system->setShakePos(_shakeEffectState.shakeX, _shakeEffectState.shakeY);
-	_alfredState.x += (_shakeEffectState.shakeX/2); // Adjust Alfred's position to counteract shake for better readability
+	_alfredState.x += (_shakeEffectState.shakeX / 2); // Adjust Alfred's position to counteract shake for better readability
 }
 
 void PelrockEngine::passerByAnim(uint32 frameCount) {
@@ -1736,12 +1736,16 @@ void PelrockEngine::gameLoop() {
 		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
 	}
 	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_u) {
-		if(_room->_currentRoomNumber == 36) {
+		if (_room->_currentRoomNumber == 36) {
 			pyramidCollapse();
 		}
 		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
 	}
 
+	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_k) {
+		credits();
+	}
+
 	renderScene();
 
 	_screen->update();
@@ -2147,12 +2151,12 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		break;
 	}
 	case 36: {
-		if(_shakeEffectState.enabled) {
+		if (_shakeEffectState.enabled) {
 			_shakeEffectState.disable();
 		}
-		if(_state->getFlag(FLAG_PIRAMIDE_JODIDA) == true &&
-		// _state->getFlag(FLAG_VIGILANTE_MEANDO) == true &&
-		_state->getFlag(FLAG_PIRAMIDE_JODIDA2) == false) {
+		if (_state->getFlag(FLAG_PIRAMIDE_JODIDA) == true &&
+			// _state->getFlag(FLAG_VIGILANTE_MEANDO) == true &&
+			_state->getFlag(FLAG_PIRAMIDE_JODIDA2) == false) {
 			_state->setFlag(FLAG_VIGILANTE_MEANDO, false);
 			_state->setFlag(FLAG_PIRAMIDE_JODIDA2, true);
 			debug("Pyramid is now active!");
@@ -2308,7 +2312,8 @@ void PelrockEngine::pyramidCollapse() {
 		_screen->update();
 		g_system->delayMillis(10);
 		npc = _room->findSpriteByIndex(0);
-		if (!npc || npc->x >= 339) break;
+		if (!npc || npc->x >= 339)
+			break;
 	}
 
 	npc = _room->findSpriteByIndex(0);
@@ -2320,7 +2325,8 @@ void PelrockEngine::pyramidCollapse() {
 		_screen->update();
 		g_system->delayMillis(10);
 		npc = _room->findSpriteByIndex(0);
-		if (!npc || npc->y >= 206) break;
+		if (!npc || npc->y >= 206)
+			break;
 	}
 
 	npc = _room->findSpriteByIndex(0);
@@ -2332,7 +2338,8 @@ void PelrockEngine::pyramidCollapse() {
 		_screen->update();
 		g_system->delayMillis(10);
 		npc = _room->findSpriteByIndex(0);
-		if (!npc || npc->x <= 307) break;
+		if (!npc || npc->x <= 307)
+			break;
 	}
 
 	// Stop NPC movement
@@ -2494,101 +2501,87 @@ void PelrockEngine::endingScene() {
 }
 
 void PelrockEngine::credits() {
+	_sound->playMusicTrack(3);
 	// 25-page room slideshow: each page loads a game room and overlays credit texts.
 	static const int kNumCreditPages = 25;
 	static const int kCreditRooms[kNumCreditPages] = {
 		22, 27, 36, 23, 24, 37, 25, 26, 49, 43, 35, 52, 29,
 		39, 40, 41, 45, 47, 21, 50, 46, 42, 34, 30, 14};
-	static const int kFramesPerPage = 35;
+	static const int kFramesPerPage = 45;
 
 	Common::Array<Common::StringArray> creditTexts = _res->getCredits();
 
-	Common::File roomFile;
-	if (!roomFile.open(Common::Path("ALFRED.1"))) {
-		error("Could not open ALFRED.1 for credits");
-		return;
-	}
-
-	byte *bg = new byte[640 * 400];
-	byte *palette = new byte[768];
 	CursorMan.showMouse(false);
 
 	// Outer restart loop — keypress during display restarts from page 0
 	bool restart = false;
 	_alfredState.setState(ALFRED_SKIP_DRAWING);
 	_disableAmbientSounds = true;
+	_disableAction = true;
+	bool keyPressed = false;
 	do {
-		restart = false;
 		for (int page = 0; page < kNumCreditPages && !shouldQuit(); page++) {
-			debug("Credits: loading page %d (room %d)", page, kCreditRooms[page]);
+			// loads screen
 			setScreen(kCreditRooms[page]);
 
-			Common::StringArray lines;
-
-			if (page < (int)creditTexts.size()) {
-				for (const Common::String &block : creditTexts[page]) {
-					Common::String cur;
-					for (uint ci = 0; ci < block.size(); ci++) {
-						byte b = (byte)block[ci];
-						if (b == 0xB1) {
-							if (!cur.empty()) {
-								lines.push_back(cur);
-								cur.clear();
-							}
-						} else if (b >= 0x20) {
-							cur += (char)b;
-						}
-					}
-					if (!cur.empty())
-						lines.push_back(cur);
-				}
+			byte speakerId;
+			_dialog->processColorAndTrim(creditTexts[page], speakerId);
+
+			// Text is already encoded for new lines but should also be wrapped to respect the max chars per line!
+			creditTexts[page] = _dialog->wordWrap(creditTexts[page])[0];
+			// all lines start with a space but the first one contains the trailing space of the speakerId
+			creditTexts[page][0] = creditTexts[page][0].substr(1, creditTexts[page][0].size() - 1);
+
+			int height = creditTexts[page].size() * 25; // Add some padding
+
+			Graphics::Surface s;
+			s.create(640, height + 1, Graphics::PixelFormat::createFormatCLUT8());
+			s.fillRect(s.getRect(), 255); // Clear surface
+
+			int maxWidth = 0;
+			/**
+			 * Last line is less indented for some reason, so skip that for calculation
+			 */
+			for (int i = 0; i < creditTexts[page].size(); i++) {
+				maxWidth = MAX(maxWidth, g_engine->_largeFont->getStringWidth(creditTexts[page][i]));
 			}
 
-			// Compute vertical centering (LargeFont: CHAR_HEIGHT = 24px per line)
-			int lineH = _largeFont->getFontHeight();
-			int totalH = (int)lines.size() * lineH;
-			int startY = (400 - totalH) / 2;
+			int startX = 320 - (maxWidth / 2);
+			int startY = (400 - s.getRect().height()) / 2 - 10;
+
+			for (int i = 0; i < creditTexts[page].size(); i++) {
+				// subtract that extra negative identation
+				int xPos = i == creditTexts[page].size() - 1 ? startX - 10 : startX;
+				int yPos = i * 25; // Above sprite, adjust for line
+				g_engine->_largeFont->drawString(&s, creditTexts[page][i], xPos, yPos, 640, speakerId, Graphics::kTextAlignLeft);
+			}
 
-			byte speakerId;
-			_dialog->processColorAndTrim(lines, speakerId);
-			Graphics::Surface s = _dialog->getDialogueSurface(lines, speakerId);
 			int frames = 0;
 			_events->_lastKeyEvent = Common::KEYCODE_INVALID;
 			while (!shouldQuit() && frames < kFramesPerPage) {
-				debug("Credits page %d, frame %d/%d", page, frames, kFramesPerPage);
 				_events->pollEvent();
 				bool didRender = renderScene(OVERLAY_NONE);
 
 				if (didRender) {
 					_screen->transBlitFrom(s, s.getRect(), Common::Point(0, startY), 255);
-
-					if (_chrono->getFrameCount() % 2 == 0) // Original game increments frame counter every other frame
-						frames++;
+					frames++;
 				}
 				_screen->markAllDirty();
 				_screen->update();
 				g_system->delayMillis(10);
-
-				// Keypress → restart slideshow from page 0 (original behaviour)
 				if (_events->_lastKeyEvent != Common::KEYCODE_INVALID) {
-					_events->_lastKeyEvent = Common::KEYCODE_INVALID;
-					restart = true;
+					// Any key press exits credits
+					keyPressed = true;
 					break;
 				}
 			}
-
-			_screen->markAllDirty();
-			_screen->update();
-			if (restart)
+			if (keyPressed) {
 				break;
+			}
 		}
-	} while (restart && !shouldQuit());
+	} while (restart && !shouldQuit() && !keyPressed);
 
-	_disableAmbientSounds = false;
-	delete[] bg;
-	delete[] palette;
-	roomFile.close();
-	CursorMan.showMouse(true);
+	g_engine->quitGame();
 }
 
 void PelrockEngine::initGodsSequences(int roomNumber) {
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 872160fa48a..e52474519b6 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -61,7 +61,6 @@ private:
 	Common::RandomSource _randomSource;
 	VideoManager *_videoManager = nullptr;
 	SoundManager *_sound = nullptr;
-	DialogManager *_dialog = nullptr;
 	MenuManager *_menu = nullptr;
 
 	void init();
@@ -178,6 +177,7 @@ public:
 	RoomManager *_room = nullptr;
 	ChronoManager *_chrono = nullptr;
 	PelrockEventManager *_events = nullptr;
+	DialogManager *_dialog = nullptr;
 	AlfredState _alfredState;
 	ShakeEffectState _shakeEffectState;
 	byte *_compositeBuffer = nullptr; // Working composition buffer
@@ -267,7 +267,7 @@ public:
 
 	// Actions
 	void doExtraActions(int roomNumber);
-void pyramidCollapse();
+	void pyramidCollapse();
 	void endingScene();
 	void credits();
 	void initGodsSequences(int roomNumber);
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 4eb473bae70..93452c8777a 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -435,13 +435,10 @@ Common::Array<Common::StringArray> ResourceManager::processTextData(byte *data,
 	Common::Array<Common::StringArray> texts;
 	while (pos < size) {
 		if (data[pos] == CTRL_END_TEXT) {
-			if (!desc.empty()) {
-
 				lines.push_back(desc);
 				texts.push_back(lines);
 				lines.clear();
 				desc = Common::String();
-			}
 			pos++;
 			continue;
 		}
@@ -458,8 +455,11 @@ Common::Array<Common::StringArray> ResourceManager::processTextData(byte *data,
 
 			continue;
 		}
-		if (data[pos] == 0xC8) {
-			lines.push_back(desc);
+
+		if (data[pos] == 0xC8 || data[pos] == 0xB1) {
+			if(!desc.empty() || data[pos] == 0xC8) {
+				lines.push_back(desc);
+			}
 			desc = Common::String();
 			pos++;
 			continue;


Commit: 24970d5b9030ac48fd09412364878b7c1b0a8228
    https://github.com/scummvm/scummvm/commit/24970d5b9030ac48fd09412364878b7c1b0a8228
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:19+02:00

Commit Message:
PELROCK: Post intro cutscene

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 8812bf50dad..6c84682c650 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -357,7 +357,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 279:
 		travelToEgypt();
 		break;
-		// moros
+		// merchants
 
 	case 330:
 		// Two oranges
@@ -435,7 +435,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_dialog->say(_res->_ingameTexts[PARAUNAVEZ], 0);
 		_dialog->say(_res->_ingameTexts[MEJORMELARGO], 1);
 		break;
-		// end moros
+		// end merchants
 	case 353:
 		_state->setCurrentRoot(room, rootIndex + 2, 0);
 		break;
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 21fcbacf1e0..711cc71d2dd 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -174,7 +174,7 @@ Graphics::Surface DialogManager::getDialogueSurface(Common::Array<Common::String
 
 		int xPos = 0;
 		int yPos = i * 25; // Above sprite, adjust for line
-		debug("Drawing dialogue line %d: \"%s\" at position (%d, %d) with speaker ID %d", i, dialogueLines[i].c_str(), xPos, yPos, speakerId);
+		// debug("Drawing dialogue line %d: \"%s\" at position (%d, %d) with speaker ID %d", i, dialogueLines[i].c_str(), xPos, yPos, speakerId);
 		g_engine->_largeFont->drawString(&s, dialogueLines[i], xPos, yPos, maxWidth, speakerId, alignment);
 	}
 
@@ -185,7 +185,10 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 	int16 xBasePos = 0;
 	int16 yBasePos = 0;
 	if (speakerId == ALFRED_COLOR) {
-		if (g_engine->_alfredState.animState != ALFRED_TALKING) {
+		if (g_engine->_state->getFlag(FLAG_FROM_INTRO) == true) {
+			debug("Setting special anim");
+			g_engine->_alfredState.setState(ALFRED_SPECIAL_ANIM);
+		} else {
 			g_engine->_alfredState.setState(ALFRED_TALKING);
 		}
 		if (_curSprite != nullptr) {
@@ -221,7 +224,8 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 	_events->_leftMouseClicked = false;
 	_dialogActive = true;
 	int curPage = 0;
-
+	bool fromIntro = g_engine->_state->getFlag(FLAG_FROM_INTRO) == true;
+	debug("Displaying dialog, from intro = %d", fromIntro);
 	// Render loop - display text and wait for click
 	while (!g_engine->shouldQuit()) {
 		_events->pollEvent();
@@ -241,18 +245,15 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		int xPos = xBasePos - maxWidth / 2;
 		int yPos = yBasePos - height;
 
-
 		Graphics::Surface s = getDialogueSurface(textLines, speakerId);
 
 		// Clamp to screen bounds (original game: min Y = 1, max X = 639 - width)
 		xPos = CLIP(xPos, 0, 639 - maxWidth);
 		yPos = CLIP(yPos, 1, 400 - (int)s.getRect().height());
 
-		if(g_engine->_shakeEffectState.enabled) {
+		if (g_engine->_shakeEffectState.enabled) {
 			debug("Applying shake effect to dialogue, shakeX: %d", g_engine->_shakeEffectState.shakeX);
 			xPos -= g_engine->_shakeEffectState.shakeX;
-		} else {
-			debug("No shake effect applied to dialogue");
 		}
 
 		_screen->transBlitFrom(s, s.getRect(), Common::Point(xPos, yPos), 255);
@@ -263,17 +264,25 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 
 		if (_events->_leftMouseClicked) {
 			_events->_leftMouseClicked = false;
-			// debug("Dialogue click to advance, current page: %d, totalPages: %d", curPage, (int)dialogueLines.size());
-			if (curPage < (int)dialogueLines.size() - 1) {
-				curPage++;
-			} else {
-				_dismissDialog = true;
+			if (!_disableClickToAdvance) {
+				// debug("Dialogue click to advance, current page: %d, totalPages: %d", curPage, (int)dialogueLines.size());
+				if (curPage < (int)dialogueLines.size() - 1) {
+					curPage++;
+				} else {
+					_dismissDialog = true;
+				}
 			}
 		}
-		if(_dismissDialog) {
+		if (_dismissDialog) {
 			_dismissDialog = false;
 			break; // Exit dialogue if dismissed programmatically
 		}
+
+		if(fromIntro && g_engine->_res->_isSpecialAnimFinished) {
+			debug("Dismissing due to speciawl anim ending!");
+			break;
+		}
+
 		g_system->delayMillis(10);
 	}
 	if (_curSprite != nullptr) {
@@ -970,7 +979,12 @@ void DialogManager::disableChoiceIfNeeded(Common::Array<Pelrock::ChoiceOption> *
 	}
 }
 void DialogManager::sayAlfred(Common::StringArray texts) {
-	g_engine->_alfredState.setState(ALFRED_TALKING);
+	if (g_engine->_state->getFlag(FLAG_FROM_INTRO) == true) {
+		debug("Setting special anim");
+		g_engine->_alfredState.setState(ALFRED_SPECIAL_ANIM);
+	} else {
+		g_engine->_alfredState.setState(ALFRED_TALKING);
+	}
 
 	_curSprite = nullptr;
 	Common::Array<Common::StringArray> textLines = wordWrap(texts);
@@ -1044,8 +1058,7 @@ bool DialogManager::processColorAndTrim(Common::StringArray &lines, byte &speake
 			}
 		}
 		return true;
-	}
-	else {
+	} else {
 		debug("No speaker marker found, defaulting to Alfred");
 	}
 	return false;
@@ -1155,12 +1168,12 @@ Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::Str
 		pages.push_back(currentPage);
 	}
 
-	//print all the pages and lines for debugging
+	// print all the pages and lines for debugging
 	for (uint i = 0; i < pages.size(); i++) {
 		debug("Page %d:", i);
 		for (uint j = 0; j < pages[i].size(); j++) {
 			debug(" Line %d: \"%s\"", j, pages[i][j].c_str());
-				}
+		}
 	}
 	return pages;
 }
@@ -1180,9 +1193,8 @@ Common::Array<Common::StringArray> DialogManager::wordWrap(Common::StringArray t
 		Common::Array<Common::Array<Common::String>> wrapped = wordWrap(thisLine);
 		debug("Wrapped line %s, %d into %d pages", thisLine.c_str(), thisLine.size(), wrapped.size());
 		for (uint j = 0; j < wrapped.size(); j++) {
-			for (int i = 0; i < wrapped[j].size(); i++)
-			{
-				if(currentLineNum < MAX_LINES) {
+			for (int i = 0; i < wrapped[j].size(); i++) {
+				if (currentLineNum < MAX_LINES) {
 					currentPage.push_back(wrapped[j][i]);
 					currentLineNum++;
 				} else {
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index dbffbeb2d58..1e0c61e68bb 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -123,6 +123,7 @@ public:
 	// True while a blocking dialog or conversation is on screen.
 	bool _dialogActive = false;
 	bool _dismissDialog = false; // When true, the current dialog will be dismissed on the next iteration of the conversation loop (used for programmatically closing dialogs, e.g. when exiting a room)
+	bool _disableClickToAdvance = false;
 
 	Common::String _leftArrow = Common::String(17);
 	Common::String _rightArrow = Common::String(16);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 469ca86bab0..ec595061cd0 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -122,9 +122,11 @@ Common::Error PelrockEngine::run() {
 			_menu->menuLoop();
 			_state->stateGame = GAME;
 		} else if (_state->stateGame == GAME) {
+			maybePlayPostIntro();
 			gameLoop();
 		} else if (_state->stateGame == INTRO) {
 			_videoManager->playIntro();
+			_state->setFlag(FLAG_FROM_INTRO, true);
 			_state->stateGame = GAME;
 		} else if (_state->stateGame == COMPUTER) {
 			computerLoop();
@@ -154,8 +156,8 @@ void PelrockEngine::init() {
 	if (gameInitialized == false) {
 		gameInitialized = true;
 		loadAnims();
-		// setScreenAndPrepare(0, ALFRED_LEFT);
-		setScreenAndPrepare(36, ALFRED_LEFT);
+		setScreenAndPrepare(0, ALFRED_DOWN);
+		// setScreenAndPrepare(36, ALFRED_LEFT);
 
 		// setScreen(3, ALFRED_RIGHT);
 		// setScreen(22, ALFRED_DOWN);
@@ -372,6 +374,29 @@ void PelrockEngine::frameTriggers() {
 	shakeEffect();
 }
 
+void PelrockEngine::maybePlayPostIntro() {
+	if (_state->getFlag(FLAG_FROM_INTRO)) {
+		setScreenAndPrepare(0, ALFRED_LEFT);
+		_alfredState.x = 396;
+		_alfredState.y = 267;
+
+		_res->loadAlfredSpecialAnim(16, false);
+		_alfredState.animState = ALFRED_SPECIAL_ANIM;
+		_dialog->_disableClickToAdvance = true;
+		_dialog->say(_res->_ingameTexts[VAYASUENHO]);
+		_dialog->_disableClickToAdvance = false;
+		waitForSpecialAnimation();
+		_graphics->fadeToBlack(20);
+		g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
+		_state->setFlag(FLAG_FROM_INTRO, false);
+		_alfredState.direction = ALFRED_DOWN;
+		_alfredState.x = kAlfredInitialPosX;
+		_alfredState.y = kAlfredInitialPosY;
+		// setScreenAndPrepare(0, ALFRED_DOWN);
+		_dialog->say(_res->_ingameTexts[MENSAJEOTRAEPOCA]);
+	}
+}
+
 void PelrockEngine::shakeEffect() {
 	if (!_shakeEffectState.enabled) {
 		return;
@@ -1751,6 +1776,7 @@ void PelrockEngine::gameLoop() {
 	_screen->update();
 }
 
+
 void PelrockEngine::computerLoop() {
 	Computer computer(_events);
 	computer.run();
@@ -2231,7 +2257,12 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			smokeAnimation(1, false);
 
 			walkAndAction(fatMummy, TALK);
-			debug("Restart game now!");
+			_state->clear();
+			_alfredState.x = kAlfredInitialPosX;
+			_alfredState.y = kAlfredInitialPosY;
+			_graphics->fadeToBlack(20);
+			_state->setFlag(FLAG_FROM_INTRO, true);
+			setScreenAndPrepare(0, ALFRED_LEFT);
 		}
 		break;
 	}
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index e52474519b6..187c28df98a 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -116,6 +116,7 @@ private:
 	int checkMouseClickInventoryOverlay(int x, int y);
 
 	void gameLoop();
+	void firstScene();
 	void computerLoop();
 	void extraScreenLoop();
 	void walkLoop(int16 x, int16 y, AlfredDirection direction);
@@ -256,6 +257,7 @@ public:
 	bool renderScene(int overlayMode = OVERLAY_NONE);
 	void mouseHoverForMap();
 	void frameTriggers();
+	void maybePlayPostIntro();
 	void shakeEffect();
 	void handleFlightRoomFrame();
 
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 93452c8777a..c9fd605f7d4 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -52,6 +52,7 @@ const AlfredSpecialAnimOffset ResourceManager::alfredSpecialAnims[] = {
 	{11, 98, 138, 1, 7, 1526432, 1},     // 13 - Munheco 3
 	{4, 51, 102, 1, 7, 2972568, 1},      // 14 - descamisa
 	{13, 95, 99, 1, 7, 1749464, 1},      // 15 - alfred enters secret passage
+	{14, 71, 66, 1, 7, 3038454, 1, 2}, // 16- Alfred in bed
 };
 
 ResourceManager::~ResourceManager() {
@@ -212,6 +213,7 @@ void ResourceManager::loadAlfredAnims() {
 
 	free(bufferFile);
 
+
 	Common::File alfred7;
 	if (!alfred7.open(Common::Path("ALFRED.7"))) {
 		error("Could not open ALFRED.7");
@@ -219,6 +221,7 @@ void ResourceManager::loadAlfredAnims() {
 	}
 	int spriteMapSize = frameSize * 11;
 
+	/* Combing */
 	byte *alfredCombRightRaw;
 	size_t alfredCombRightSize;
 
@@ -245,9 +248,10 @@ void ResourceManager::loadAlfredAnims() {
 		extractSingleFrame(alfredCombLeft, alfredCombFrames[1][i], i, kAlfredFrameWidth, kAlfredFrameHeight);
 	}
 
-	alfred7.close();
 	free(alfredCombRightRaw);
 	free(alfredCombLeftRaw);
+
+	alfred7.close();
 }
 
 void ResourceManager::loadOtherSpecialAnim(uint32 offset, bool rleCompressed, byte *&buffer, size_t &bufferSize) {
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 0bfe3ead7c2..bbd2b765a83 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -88,6 +88,9 @@ const int kAlfredIdleAnimationFrameCount = 300;
 
 const int kInventoryPageSize = 10;
 
+const int kAlfredInitialPosX = 235;
+const int kAlfredInitialPosY = 279;
+
 // Direction flags (bit-packed)
 #define MOVE_RIGHT 0x01 // Move right (positive X)
 #define MOVE_LEFT 0x02  // Move left (negative X)
@@ -118,7 +121,8 @@ enum AlfredAnimState {
 	ALFRED_INTERACTING,
 	ALFRED_COMB,
 	ALFRED_SPECIAL_ANIM,
-	ALFRED_SKIP_DRAWING
+	ALFRED_SKIP_DRAWING,
+	ALFRED_IN_BED
 };
 
 enum AlfredDirection {
@@ -180,8 +184,8 @@ struct AlfredState {
 	int curFrame = 0;
 	uint16 movementSpeedX = 6; // pixels per frame
 	uint16 movementSpeedY = 5; // pixels per frame
-	uint16 x = 330;
-	uint16 y = 302;
+	uint16 x = kAlfredInitialPosX;
+	uint16 y = kAlfredInitialPosY;
 	byte w = kAlfredFrameWidth;
 	byte h = kAlfredFrameHeight;
 	int idleFrameCounter = 0;
@@ -572,10 +576,7 @@ struct GameStateData {
 	Common::HashMap<byte, Common::Array<SpriteChange>> spriteChanges;
 
 	GameStateData() {
-		memset(conversationCurrentRoot, 0, 112); // Initialize all to 0xFF (not set)
-		for (int i = 0; i < kNumGameFlags; i++)
-			flags[i] = 0;
-		flags[FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ] = true;
+		clear();
 	}
 
 	~GameStateData() {
@@ -583,6 +584,25 @@ struct GameStateData {
 		conversationCurrentRoot = nullptr;
 	}
 
+	void clear() {
+		memset(conversationCurrentRoot, 0, 112); // Initialize all to 0xFF (not set)
+		for (int i = 0; i < kNumGameFlags; i++)
+			flags[i] = 0;
+		flags[FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ] = true;
+		disabledBranches.clear();
+		inventoryItems.clear();
+		stickersPerRoom.clear();
+		roomExitChanges.clear();
+		roomWalkBoxChanges.clear();
+		roomHotSpotChanges.clear();
+		spriteChanges.clear();
+		disabledBranches.clear();
+		libraryShelf = -1;
+		selectedBookIndex = -1;
+		bookLetter = '\0';
+		stateGame = GAME;
+	}
+
 	void addDisabledBranch(ResetEntry entry) {
 		disabledBranches[entry.room].push_back(entry);
 	}


Commit: fc84b9a8945c4c3da451ef2fc56f9afd27ee7ac3
    https://github.com/scummvm/scummvm/commit/fc84b9a8945c4c3da451ef2fc56f9afd27ee7ac3
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:20+02:00

Commit Message:
PELROCK: Fix warnings

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/computer.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/extrascreens.cpp
    engines/pelrock/extrascreens.h
    engines/pelrock/graphics.cpp
    engines/pelrock/library_books.h
    engines/pelrock/menu.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h
    engines/pelrock/types.h
    engines/pelrock/util.cpp
    engines/pelrock/video/video.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 6c84682c650..f1047ddd261 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -332,13 +332,11 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		w1.y = 356;
 		w1.w = 4;
 		w1.h = 14;
-		w1.flags = 0;
 		WalkBox w2;
 		w2.x = 440;
 		w2.y = 368;
 		w2.w = 148;
 		w2.h = 2;
-		w2.flags = 0;
 
 		_room->addWalkbox(w1);
 		_room->addWalkbox(w2);
@@ -914,7 +912,6 @@ void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
 	brickSprite->x = 420;
 	brickSprite->y = 241;
 	brickSprite->zOrder = 10; // Make it visible
-	int target = windowHotspot->y + windowHotspot->h / 2;
 	while (!shouldQuit()) {
 		_events->pollEvent();
 		renderScene(OVERLAY_NONE);
@@ -1511,7 +1508,6 @@ void PelrockEngine::pickUpStone(HotSpot *hotspot) {
 
 void PelrockEngine::playSpecialAnim(uint32 offset, bool compressed, int x, int y, int width, int height, int numFrames) {
 	size_t frameSize = width * height;
-	debug("Frame size: %d bytes", frameSize);
 	size_t bufSize = frameSize * numFrames;
 	byte *animData = new byte[bufSize];
 	_res->loadOtherSpecialAnim(offset, compressed, animData, bufSize);
@@ -1519,7 +1515,6 @@ void PelrockEngine::playSpecialAnim(uint32 offset, bool compressed, int x, int y
 	Graphics::Surface animSurface;
 	animSurface.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
 	int curFrame = 0;
-	bool firstFrame = true;
 	while (!shouldQuit()) {
 		_events->pollEvent();
 
@@ -1588,8 +1583,8 @@ void PelrockEngine::giveStoneToSlaves(int inventoryObject, HotSpot *hotspot) {
 
 		_room->addSticker(116);
 
-		WalkBox w1 = {3, 187, 374, 5, 17};
-		WalkBox w2 = {4, 141, 374, 46, 4};
+		WalkBox w1 = {3, 187, 374, 5, 17, 0};
+		WalkBox w2 = {4, 141, 374, 46, 4, 0};
 		_room->addWalkbox(w1);
 		_room->addWalkbox(w2);
 		_state->setFlag(FLAG_GUARDIAS_BORRACHOS, true);
@@ -1643,10 +1638,9 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 		sprite->disableAfterSequence = true;
 
 		sprite->animData[0].animData = new byte *[sprite->animData[0].nframes];
-		byte *spriteFrames[sprite->animData[0].nframes];
-		for (int i = 0; i < sprite->animData[0].nframes; i++) {
-			sprite->animData[0].animData[i] = new byte[sprite->w * sprite->h];
-			extractSingleFrame(buffer + acc, sprite->animData[0].animData[i], i, sprite->w, sprite->h);
+		for (int j = 0; j < sprite->animData[0].nframes; j++) {
+			sprite->animData[0].animData[j] = new byte[sprite->w * sprite->h];
+			extractSingleFrame(buffer + acc, sprite->animData[0].animData[j], j, sprite->w, sprite->h);
 		}
 		acc += sprite->w * sprite->h * sprite->animData[0].nframes;
 	}
diff --git a/engines/pelrock/computer.cpp b/engines/pelrock/computer.cpp
index 03930f125fa..5a1ee64e2cc 100644
--- a/engines/pelrock/computer.cpp
+++ b/engines/pelrock/computer.cpp
@@ -50,8 +50,6 @@ void Computer::init() {
 	}
 
 	alfred7File.seek(kBookDataOffset, SEEK_SET);
-	int index = 0;
-	int index2 = 0;
 	while (alfred7File.pos() < kBookDataEnd) {
 		LibraryBook book;
 
@@ -151,9 +149,6 @@ void Computer::drawScreen() {
 	// Clear to background
 	memcpy(g_engine->_screen->getPixels(), _backgroundScreen, 640 * 400);
 
-	int textY = 97;
-	int textX = 225;
-
 	byte defaultColor = 0; // Light gray
 
 	switch (_state) {
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 711cc71d2dd..6038cf219d4 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -80,6 +80,7 @@ uint32 DialogManager::readTextBlock(
 	}
 
 	int lineIndex = data[++pos];
+	debug("Line index %d", lineIndex);
 	pos++; // blank
 	// debug("Reading text block starting at pos %u, line index %d, speaker ID %d", startPos, lineIndex, outSpeakerId);
 	// Read text until control byte
@@ -117,7 +118,6 @@ void DialogManager::displayChoices(Common::Array<ChoiceOption> *choices, byte *c
 	Common::Point overlayPos = _graphics->showOverlay(overlayHeight, compositeBuffer);
 	for (uint i = 0; i < choices->size(); i++) {
 		ChoiceOption choice = (*choices)[i];
-		int width = g_engine->_doubleSmallFont->getStringWidth(choice.text);
 		int yPos = overlayPos.y + 2 + i * kChoiceHeight;
 		Common::Rect bbox(kChoicePadding, yPos, kChoicePadding + 600, yPos + kChoiceHeight);
 		Common::Rect leftArrowBox(0, yPos, kChoicePadding, yPos + kChoiceHeight);
@@ -1193,14 +1193,14 @@ Common::Array<Common::StringArray> DialogManager::wordWrap(Common::StringArray t
 		Common::Array<Common::Array<Common::String>> wrapped = wordWrap(thisLine);
 		debug("Wrapped line %s, %d into %d pages", thisLine.c_str(), thisLine.size(), wrapped.size());
 		for (uint j = 0; j < wrapped.size(); j++) {
-			for (int i = 0; i < wrapped[j].size(); i++) {
+			for (int k = 0; k < wrapped[j].size(); k++) {
 				if (currentLineNum < MAX_LINES) {
-					currentPage.push_back(wrapped[j][i]);
+					currentPage.push_back(wrapped[j][k]);
 					currentLineNum++;
 				} else {
 					pages.push_back(currentPage);
 					currentPage.clear();
-					currentPage.push_back(wrapped[j][i]);
+					currentPage.push_back(wrapped[j][k]);
 					currentLineNum = 1;
 				}
 			}
diff --git a/engines/pelrock/extrascreens.cpp b/engines/pelrock/extrascreens.cpp
index 1dd9673b356..b1901494a0d 100644
--- a/engines/pelrock/extrascreens.cpp
+++ b/engines/pelrock/extrascreens.cpp
@@ -225,7 +225,6 @@ void CDPlayer::loadTrackNames() {
 
 	for (int i = 0; i < 31; i++) {
 		trackNames[i] = juegoFile.readString(0, 30);
-		debug("Loaded track name: %s", trackNames[i].c_str());
 	}
 
 	juegoFile.close();
@@ -310,13 +309,13 @@ void CDPlayer::checkMouse(int x, int y) {
 		default:
 			break;
 		}
-		_selectedButton = NO_BUTTON;
+		_selectedButton = NO_CDBUTTON;
 		_events->_leftMouseClicked = false;
 	}
 
-	if (_events->_leftMouseButton != 0 && _selectedButton == NO_BUTTON) {
+	if (_events->_leftMouseButton != 0 && _selectedButton == NO_CDBUTTON) {
 		_selectedButton = isButtonClicked(_events->_mouseX, _events->_mouseY);
-		if(_selectedButton != NO_BUTTON) {
+		if(_selectedButton != NO_CDBUTTON) {
 			_sound->playSound("11ZZZZZZ.SMP", 0);
 		}
 	}
@@ -328,7 +327,7 @@ CDPlayer::CDControls CDPlayer::isButtonClicked(int x, int y) {
 			return static_cast<CDControls>(i);
 		}
 	}
-	return NO_BUTTON;
+	return NO_CDBUTTON;
 }
 
 void CDPlayer::loadControls() {
@@ -343,7 +342,7 @@ void CDPlayer::loadControls() {
 	readUntilBuda(&alfred7, 2214760, compressedData, outSize);
 	byte *rawData = nullptr;
 
-	size_t decompressedSize = rleDecompress(compressedData, outSize, 0, 0, &rawData, true);
+	rleDecompress(compressedData, outSize, 0, 0, &rawData, true);
 
 	// debug("Decompressed CD player controls: %d bytes", decompressedSize);
 	uint32 pos = 213 * 72;
@@ -414,7 +413,7 @@ void BackgroundBook::checkMouse(int x, int y) {
 		default:
 			break;
 		}
-		_selectedButton = NO_BUTTON;
+		_selectedButton = NO_BG_BUTTON;
 
 		int firstItem = _selectedPage * kItemsPerPage;
 		if (y >= 72 && y < 72 + (kItemsPerPage * g_engine->_smallFont->getFontHeight()) && x >= 37 && x <= 37 + 200) {
@@ -428,7 +427,7 @@ void BackgroundBook::checkMouse(int x, int y) {
 		_events->_leftMouseClicked = false;
 	}
 
-	if (_events->_leftMouseButton != 0 && _selectedButton == NO_BUTTON) {
+	if (_events->_leftMouseButton != 0 && _selectedButton == NO_BG_BUTTON) {
 		_selectedButton = isButtonClicked(_events->_mouseX, _events->_mouseY);
 	}
 }
@@ -439,7 +438,7 @@ BackgroundBook::Buttons BackgroundBook::isButtonClicked(int x, int y) {
 			return static_cast<Buttons>(i);
 		}
 	}
-	return NO_BUTTON;
+	return NO_BG_BUTTON;
 }
 
 void BackgroundBook::loadRoomNames() {
diff --git a/engines/pelrock/extrascreens.h b/engines/pelrock/extrascreens.h
index 951843b1910..8f0c209d42e 100644
--- a/engines/pelrock/extrascreens.h
+++ b/engines/pelrock/extrascreens.h
@@ -96,7 +96,7 @@ enum CDControls {
 	PLAY_BUTTON,
 	PREVIOUS_BUTTON,
 	NEXT_BUTTON,
-	NO_BUTTON
+	NO_CDBUTTON
 };
 
 public:
@@ -132,7 +132,7 @@ private:
 		Common::Rect(Common::Point(168, 44), 41, 28) // Next
 	};
 	int _selectedTrack = 2;
-	CDControls _selectedButton = NO_BUTTON;
+	CDControls _selectedButton = NO_CDBUTTON;
 
 };
 
@@ -141,7 +141,7 @@ class BackgroundBook {
 enum Buttons {
 	PREVIOUS_BUTTON,
 	NEXT_BUTTON,
-	NO_BUTTON
+	NO_BG_BUTTON
 };
 	int kItemsPerPage = 22;
 
@@ -168,7 +168,7 @@ private:
 	byte *_compositeScreen;
 	byte *_palette;
 	byte *_buttons[2][2];
-	Buttons _selectedButton = NO_BUTTON;
+	Buttons _selectedButton = NO_BG_BUTTON;
 	int _selectedPage = 0;
 
 	Common::Rect _buttonRects[2] = {
diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index c1552d88d7e..74fcf293483 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -204,7 +204,6 @@ void GraphicsManager::drawColoredText(byte *buf, const Common::String &text, int
 
 	for(int j = 0; j < tempSurface.h; j++) {
 		for(int i = 0; i < tempSurface.w; i++) {
-			int idx = j * tempSurface.w + i;
 			if (y + j < 400 && x + i < 640) {
 				byte pixel = *((byte *)tempSurface.getBasePtr(i, j));
 				if (pixel != 0) { // Assuming 0 is transparent
diff --git a/engines/pelrock/library_books.h b/engines/pelrock/library_books.h
index 15a6491b98e..7f38362b7d6 100644
--- a/engines/pelrock/library_books.h
+++ b/engines/pelrock/library_books.h
@@ -45,7 +45,7 @@ struct LibraryBook {
     bool available;  // true = can be found on shelf, false = catalog only
 };
 
-const LibraryBook noBook = {"", "", "", 0, 0, false};
+static const LibraryBook noBook = {"", "", "", 0, 0, false};
 
 
 // static const int kLibraryBookCount = 125;
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 0840da818c4..6bfb31678ec 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -121,7 +121,6 @@ void MenuManager::showCredits() {
 	alfred7.read(_compositeBuffer, 640 * 400);
 	byte *creditsBuf = nullptr;
 	size_t creditsSize = 0;
-	int numCredits = 29;
 	int creditWidth = 240;
 	int creditHeight = 22;
 	readUntilBuda(&alfred7, kCreditsBackgroundOffset + 256000, creditsBuf, creditsSize);
@@ -270,12 +269,6 @@ void MenuManager::loadMenu() {
 		alfred7.seek(2563266, SEEK_SET);
 		alfred7.read(_mainMenu + curPos, 92160);
 	} else {
-		Common::File alfred7;
-		if (!alfred7.open(Common::Path("ALFRED.7"))) {
-			error("Could not open ALFRED.7");
-			return;
-		}
-
 		_mainMenu = new byte[640 * 400];
 
 		alfred7.seek(kAlternateSettingsPaletteOffset, SEEK_SET);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index ec595061cd0..ef165ba0472 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -365,8 +365,6 @@ void PelrockEngine::mouseHoverForMap() {
 	}
 }
 
-const int kPasserbyTriggerFrameInterval = 0x3FF;
-
 void PelrockEngine::frameTriggers() {
 	uint32 frameCount = _chrono->getFrameCount();
 	passerByAnim(frameCount);
@@ -375,7 +373,7 @@ void PelrockEngine::frameTriggers() {
 }
 
 void PelrockEngine::maybePlayPostIntro() {
-	if (_state->getFlag(FLAG_FROM_INTRO)) {
+	if (_state->getFlag(FLAG_FROM_INTRO) == true) {
 		setScreenAndPrepare(0, ALFRED_LEFT);
 		_alfredState.x = 396;
 		_alfredState.y = 267;
@@ -1091,7 +1089,6 @@ void PelrockEngine::exitTriggers(Pelrock::Exit *exit) {
 	} else if (exit->targetRoom == 48 && _room->_currentRoomNumber == 46) {
 		smokeAnimation(-1);
 		uint16 x = _alfredState.x;
-		uint16 y = _alfredState.y;
 		if (x < 282) {
 			if (_state->getFlag(FLAG_PUERTA_BUENA) == 1) {
 				_state->setFlag(FLAG_CORRECT_DOOR_CHOSEN, true);
@@ -1310,7 +1307,6 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 		return;
 	}
 
-	int frameSize = sprite->stride;
 	int curFrame = animData.curFrame;
 	if (curFrame >= animData.nframes) {
 		debug("Warning: curFrame %d exceeds nframes %d for sprite %d anim %d", curFrame, animData.nframes, sprite->index, sprite->curAnimIndex);
@@ -2244,8 +2240,6 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			_dialog->say(_res->_ingameTexts[OHMISALVADOR]);
 			_dialog->say(_res->_ingameTexts[VOYPORTI_PRINCESA]);
 			// _state->setCurrentRoot(48, 0, 1);
-			Sprite *thinMummy = _room->findSpriteByIndex(0);
-			Sprite *thickMummy = _room->findSpriteByIndex(1);
 			HotSpot *fatMummy = nullptr;
 			for (uint i = 0; i < _room->_currentRoomHotspots.size(); i++) {
 				if (_room->_currentRoomHotspots[i].isSprite && _room->_currentRoomHotspots[i].index == 1) {
@@ -2437,8 +2431,8 @@ void PelrockEngine::endingScene() {
 		sprite->w = animValues[i][2];
 		sprite->h = animValues[i][3];
 		sprite->stride = animValues[i][2] * animValues[i][3];
-		bool idleAnim = animValues[i][7] > 0;
-		if (idleAnim) {
+		bool isIdleAnim = animValues[i][7] > 0;
+		if (isIdleAnim) {
 			sprite->numAnims = 2;
 		} else {
 			sprite->numAnims = 1;
@@ -2458,7 +2452,7 @@ void PelrockEngine::endingScene() {
 			extractSingleFrame(legsAnimData, mainAnim.animData[j], j, sprite->w, sprite->h);
 		}
 
-		if (idleAnim) {
+		if (isIdleAnim) {
 			Anim idleAnim;
 			idleAnim.nframes = 1;
 			idleAnim.loopCount = 1;
@@ -2478,7 +2472,6 @@ void PelrockEngine::endingScene() {
 	}
 
 	Common::Rect bbox1 = _largeFont->getBoundingBox("ALFRED PELROCK");
-	Common::Rect bbox2 = _largeFont->getBoundingBox("En busca de un sueño");
 	int y1 = 400 / 2 - bbox1.height() / 2;
 	int y2 = 400 / 2 + bbox1.height() / 2;
 	int ticks = 0;
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index c9fd605f7d4..546bd1c88af 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -127,7 +127,6 @@ void ResourceManager::loadInteractionIcons() {
 
 	int iconSize = kVerbIconHeight * kVerbIconWidth;
 	for (int i = 0; i < kNumVerbIcons; i++) {
-		uint32_t iconOffset = i * iconSize;
 		_verbIcons[i] = new byte[iconSize];
 		alfred4File.read(_verbIcons[i], iconSize);
 	}
@@ -146,8 +145,6 @@ void ResourceManager::loadAlfredAnims() {
 	alfred3.read(bufferFile, alfred3Size);
 	alfred3.close();
 
-	int index = 0;
-	int index3 = 0;
 	uint32_t capacity = 3060 * 102 + 2340 * 55;
 	unsigned char *completePic = new unsigned char[capacity];
 	rleDecompress(bufferFile, alfred3Size, 0, capacity, &completePic);
@@ -241,7 +238,7 @@ void ResourceManager::loadAlfredAnims() {
 	size_t alfredCombLeftSize;
 	readUntilBuda(&alfred7, ALFRED7_ALFRED_COMB_L, alfredCombLeftRaw, alfredCombLeftSize);
 	byte *alfredCombLeft = nullptr;
-	size_t outSize = rleDecompress(alfredCombLeftRaw, alfredCombLeftSize, 0, spriteMapSize, &alfredCombLeft);
+	rleDecompress(alfredCombLeftRaw, alfredCombLeftSize, 0, spriteMapSize, &alfredCombLeft);
 
 	for (int i = 0; i < 11; i++) {
 		alfredCombFrames[1][i] = new byte[frameSize];
@@ -333,7 +330,6 @@ void ResourceManager::loadInventoryItems() {
 	alfred4File.seek(42366, SEEK_SET);
 	alfred4File.read(iconData, iconsSize);
 
-	int iconSize = 60 * 60; // each icon has 30 bytes of header
 	for (int i = 0; i < 69; i++) {
 		_inventoryIcons[i].index = i;
 		extractSingleFrame(iconData, _inventoryIcons[i].iconData, i, 60, 60);
@@ -523,7 +519,6 @@ void ResourceManager::mergeRleBlocks(Common::SeekableReadStream *stream, uint32
 	for (int i = 0; i < numBlocks; i++) {
 		byte *thisBlock = nullptr;
 		size_t blockSize = 0;
-		uint32 pos = stream->pos();
 		readUntilBuda(stream, stream->pos(), thisBlock, blockSize);
 		uint8_t *block_data = nullptr;
 		size_t decompressedSize = rleDecompress(thisBlock, blockSize, 0, 640 * 400, &block_data, true);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index c50af161fb3..2923879abaf 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -960,14 +960,14 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 			uint32_t totalBytesPerFrame = sprite.w * sprite.h * anim.nframes;
 			anim.animData = new byte *[anim.nframes];
 			if (sprite.w > 0 && sprite.h > 0 && anim.nframes > 0) {
-				for (int i = 0; i < anim.nframes; i++) {
+				for (int k = 0; k < anim.nframes; k++) {
 					if(picOffset >= pixelDataSize) {
-						debug("Pixel data offset out of bounds for sprite %d anim %d, offset %d, size %d", i, j, picOffset, pixelDataSize);
+						debug("Pixel data offset out of bounds for sprite %d anim %d, offset %u, size %lu", i, j, picOffset, pixelDataSize);
 						break;
 					}
-					anim.animData[i] = new byte[sprite.w * sprite.h];
+					anim.animData[k] = new byte[sprite.w * sprite.h];
 					// debug("Extracting frame %d for anim %d-%d, w=%d h=%d, pixelDataSize=%d, current offset %d", i, j, anim.nframes, sprite.w, sprite.h, pixelDataSize, picOffset);
-					extractSingleFrame(pixelData + picOffset, anim.animData[i], i, sprite.w, sprite.h);
+					extractSingleFrame(pixelData + picOffset, anim.animData[k], k, sprite.w, sprite.h);
 				}
 				sprite.animData[j] = anim;
 				// debug("  Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d loopCount=%d speed=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes, anim.loopCount, anim.speed);
@@ -1224,10 +1224,10 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 		talkHeader.animB = new byte *[talkHeader.numFramesAnimB];
 		for (int i = 0; i < talkHeader.numFramesAnimB; i++) {
 			talkHeader.animB[i] = new byte[talkHeader.wAnimB * talkHeader.hAnimB];
-			uint32 offset = animASize + (i * talkHeader.wAnimB * talkHeader.hAnimB);
+			uint32 animBFrameOffset = animASize + (i * talkHeader.wAnimB * talkHeader.hAnimB);
 			// debug("Extracting talking anim B frame %d at offset %d, size = %d", i, animASize + (i * talkHeader.wAnimB * talkHeader.hAnimB), talkHeader.wAnimB * talkHeader.hAnimB);
-			if (offset + talkHeader.wAnimB * talkHeader.hAnimB >= decompressedSize) {
-				debug("Error: offset %d is beyond decompressed size %zu", offset, decompressedSize);
+			if (animBFrameOffset + talkHeader.wAnimB * talkHeader.hAnimB >= decompressedSize) {
+				debug("Error: offset %d is beyond decompressed size %zu", animBFrameOffset, decompressedSize);
 				talkHeader.numFramesAnimB = 0;
 			} else {
 				extractSingleFrame(decompressed + animASize, talkHeader.animB[i], i, talkHeader.wAnimB, talkHeader.hAnimB);
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 5015e32566c..30e18ccf30b 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -39,7 +39,7 @@
 namespace Pelrock {
 
 SoundManager::SoundManager(Audio::Mixer *mixer)
-	: _mixer(mixer), _currentVolume(128), _musicFile(nullptr) {
+	: _mixer(mixer), _currentVolume(128) {
 	// TODO: Initialize sound manager
 	g_system->getAudioCDManager()->open();
 }
@@ -117,7 +117,6 @@ void SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
 				_mixer->stopHandle(_sfxHandles[channel]);
 			}
 		}
-		debug("Playing sound with loop count %d on channel %d", loopCount, channel);
 		Audio::AudioStream *finalStream = loopCount != -1 ? stream : Audio::makeLoopingAudioStream(stream, 0);
 
 		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], finalStream, loopCount, 255U, 0, DisposeAfterUse::YES);
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index ec4d1f4dd81..e5b971dd7e3 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -190,9 +190,7 @@ private:
 
 private:
 	Audio::Mixer *_mixer;
-	bool _isMusicPlaying = false;
 	int _currentVolume;
-	Common::File *_musicFile;
 	Audio::SoundHandle _musicHandle;
 	Audio::SoundHandle _sfxHandles[kMaxChannels];
 	Common::HashMap<Common::String, SonidoFile> _soundMap;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index bbd2b765a83..77a63a52151 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -121,8 +121,7 @@ enum AlfredAnimState {
 	ALFRED_INTERACTING,
 	ALFRED_COMB,
 	ALFRED_SPECIAL_ANIM,
-	ALFRED_SKIP_DRAWING,
-	ALFRED_IN_BED
+	ALFRED_SKIP_DRAWING
 };
 
 enum AlfredDirection {
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index c27d22fbe34..58a53b1c6c3 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -213,7 +213,6 @@ size_t rleDecompress(
 }
 
 void readUntilBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *&buffer, size_t &outSize) {
-	const char marker[] = "BUDA";
 	const int markerLen = 4;
 	size_t bufferSize = 4096;
 	size_t pos = 0;
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index a4f726a8478..0b682059e51 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -63,7 +63,6 @@ void VideoManager::playIntro() {
 	_textSurface.fillRect(Common::Rect(0, 0, 640, 400), 255);
 	for (int sequence = 0; sequence < 1; sequence++) {
 		uint16 frameCounter = 0;
-		int chunksInBuffer = 0;
 		bool videoExitFlag = false;
 
 		while (!videoExitFlag && !g_engine->shouldQuit() && _events->_lastKeyEvent != Common::KEYCODE_ESCAPE) {


Commit: 849b58d6a520c39cceb530083845c2141183f655
    https://github.com/scummvm/scummvm/commit/849b58d6a520c39cceb530083845c2141183f655
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:20+02:00

Commit Message:
PELROCK: Fixes fall-through with salesman

Changed paths:
    engines/pelrock/actions.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index f1047ddd261..6c88af3c6c7 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -340,6 +340,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 
 		_room->addWalkbox(w1);
 		_room->addWalkbox(w2);
+		break;
 	case 274:
 	case 275:
 	case 276:


Commit: 0a34967556d7d8009f88ae350c2d2d8602b07399
    https://github.com/scummvm/scummvm/commit/0a34967556d7d8009f88ae350c2d2d8602b07399
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:20+02:00

Commit Message:
PELROCK: Fall through room 15

Changed paths:
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index ef165ba0472..b8a3c4f6589 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -2096,6 +2096,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			_dialog->say(_res->_ingameTexts[GAMBERROS]);
 			_dialog->say(_res->_ingameTexts[QUIENYO]);
 			_dialog->say(_res->_ingameTexts[PINTA_BUENAPERSONA]);
+			break;
 		}
 	case 38: {
 		if (_room->_prevRoomNumber == 30) {


Commit: 692e16d654bee51e1dde57dc8954377e56bd4d51
    https://github.com/scummvm/scummvm/commit/692e16d654bee51e1dde57dc8954377e56bd4d51
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:21+02:00

Commit Message:
PELROCK: Implements dog pee animation in room 19 and pigeons scene in 24

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index b8a3c4f6589..f7387341132 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -370,6 +370,35 @@ void PelrockEngine::frameTriggers() {
 	passerByAnim(frameCount);
 	handleFlightRoomFrame();
 	shakeEffect();
+	maybeHaveDogPee();
+}
+
+void PelrockEngine::maybeHaveDogPee() {
+
+	if (_room->_currentRoomNumber != 19) {
+		return;
+	}
+	Sprite * dog = _room->findSpriteByIndex(2);
+
+	if(_alfredState.x < 146 && !_isDogPeeing) {
+		_isDogPeeing = true;
+		dog->animData[0].nframes = 24;
+		while(!shouldQuit() && dog->animData[0].curFrame < 23) {
+			_events->pollEvent();
+			renderScene(OVERLAY_NONE);
+
+			_screen->update();
+			g_system->delayMillis(10);
+		}
+		dog->animData[0].nframes = 9;
+		dog->animData[0].curFrame = 0;
+
+		_dialog->say(_res->_ingameTexts[QUEASCO_CASIMEMEA]);
+		_currentHotspot = nullptr; // Clear so arrival direction isn't overridden by dog hotspot
+		walkTo(152, _alfredState.y);
+		_isDogPeeing = false;
+	}
+
 }
 
 void PelrockEngine::maybePlayPostIntro() {
@@ -1325,7 +1354,7 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 				animData.curFrame = 0;
 				animData.curLoop++;
 			} else {
-				if (sprite->disableAfterSequence) {
+				if (sprite->disableAfterSequence && sprite->curAnimIndex == sprite->numAnims - 1) {
 					sprite->zOrder = -1;
 					return;
 				}
@@ -1925,18 +1954,6 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 	_curWalkTarget = walkTarget;
 
 	walkTo(walkTarget.x, walkTarget.y);
-
-	// { // For quick room navigation
-	// 	Exit *exit = isExitUnder(walkTarget.x, walkTarget.y);
-
-	// 	if (exit != nullptr) {
-	// 		alfredState.x = exit->targetX;
-	// 		alfredState.y = exit->targetY;
-
-	// 		setScreen(exit->targetRoom, exit->dir);
-	// 	} else {
-	// 	}
-	// }
 }
 
 void PelrockEngine::changeCursor(Cursor cursor) {
@@ -2098,6 +2115,38 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			_dialog->say(_res->_ingameTexts[PINTA_BUENAPERSONA]);
 			break;
 		}
+	case 19: {
+		Sprite *dog = _room->findSpriteByIndex(2);
+		dog->animData[0].nframes = 9;
+		break;
+	}
+	case 24: {
+		_room->findSpriteByIndex(1)->numAnims = 1;
+
+		if(_state->hasInventoryItem(88) && _state->getFlag(FLAG_PIGEON_DEAD) == false) {
+			_dialog->say(_res->_ingameTexts[PROBARLIBRO]);
+			playAlfredSpecialAnim(0);
+			Sprite *pigeons = _room->findSpriteByIndex(1);
+			pigeons->numAnims = 4;
+			pigeons->curAnimIndex = 0;
+			pigeons->disableAfterSequence = true;
+			pigeons->animData[0].curFrame = 0;
+			while(!g_engine->shouldQuit() && pigeons->zOrder != -1) {
+				_events->pollEvent();
+				renderScene();
+				_screen->update();
+				// debug("Pigeons animation, current anim index %d, current frame %d", pigeons->curAnimIndex, pigeons->animData[pigeons->curAnimIndex].curFrame);
+				// if(pigeons->curAnimIndex == 3 && pigeons->animData[3].curFrame == 3) {
+				// 	debug("Pigeons animation finished, hiding pigeons and enabling next part of the scene");
+				// 	pigeons->zOrder = -1;
+				// }
+				g_system->delayMillis(10);
+			}
+			_dialog->say(_res->_ingameTexts[PRACTICAR_MAS]);
+			_state->setFlag(FLAG_PIGEON_DEAD, true);
+		}
+		break;
+	}
 	case 38: {
 		if (_room->_prevRoomNumber == 30) {
 			int x = _alfredState.x;
@@ -2548,6 +2597,10 @@ void PelrockEngine::credits() {
 		for (int page = 0; page < kNumCreditPages && !shouldQuit(); page++) {
 			// loads screen
 			setScreen(kCreditRooms[page]);
+			if(kCreditRooms[page] == 24) {
+				Sprite *pigeons = _room->findSpriteByIndex(1);
+				pigeons->disableAfterSequence = true;
+			}
 
 			byte speakerId;
 			_dialog->processColorAndTrim(creditTexts[page], speakerId);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 187c28df98a..fe2da82f092 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -192,6 +192,7 @@ public:
 	int _flightSpellFrameCounter = 0;
 	bool _flightInBlockingAnim = false;
 	bool _disableAmbientSounds = false;
+	bool _isDogPeeing = false;
 
 	GameStateData *_state = new GameStateData();
 
@@ -257,6 +258,7 @@ public:
 	bool renderScene(int overlayMode = OVERLAY_NONE);
 	void mouseHoverForMap();
 	void frameTriggers();
+	void maybeHaveDogPee();
 	void maybePlayPostIntro();
 	void shakeEffect();
 	void handleFlightRoomFrame();
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 2923879abaf..44ab35729ba 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -666,12 +666,13 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		Sprite *mouse = findSpriteByIndex(2);
 		Sprite *blank = findSpriteByIndex(4);
 		mouse->animData[0].loopCount = 3;
+		mouse->animData[0].movementFlags = 0x1F;
 		mouse->animData[1].loopCount = 1;
 		mouse->animData[1].movementFlags = 0x3FF;
 		mouse->animData[2].loopCount = 1;
-		mouse->animData[2].movementFlags = 0x801F;
+		mouse->animData[2].movementFlags = 0x1F;
 		mouse->animData[3].loopCount = 4;
-		mouse->animData[3].movementFlags = 0x3C0;
+		mouse->animData[3].movementFlags = 0x3E0;
 
 		anims = new RoomPasserBys(roomNumber, 1);
 		PasserByAnim anim;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 77a63a52151..5e3e624df09 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -448,7 +448,8 @@ struct PaletteAnim {
 #define PASSERBY_DOWN 2
 
 struct PasserByAnim {
-	uint32 frameTrigger = 0x3FF;
+	// uint32 frameTrigger = 0x3FF;
+	uint32 frameTrigger = 0x96;
 	int16 startX;
 	int16 startY;
 	int16 resetCoord;
@@ -553,8 +554,9 @@ struct ResetEntry {
 #define FLAG_HE_TIRADO_PIEDRA 44
 #define FLAG_HA_USADO_AGUA 45
 #define FLAG_NUMERO_DE_COPAS 47
+#define FLAG_PIGEON_DEAD 61
 
-const int kNumGameFlags = 61;
+const int kNumGameFlags = 62;
 
 struct GameStateData {
 	byte flags[kNumGameFlags];


Commit: 2f0ac4216560f5f2c2d3ea2fd07d77850bf59170
    https://github.com/scummvm/scummvm/commit/2f0ac4216560f5f2c2d3ea2fd07d77850bf59170
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:21+02:00

Commit Message:
PELROCK: Implements sound effects and music in intro video

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h
    engines/pelrock/video/video.cpp
    engines/pelrock/video/video.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 6c88af3c6c7..17c6e16ad2a 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -2414,7 +2414,7 @@ void PelrockEngine::antiPiracyEffect() {
 	}
 
 	// Play the noise
-	_sound->playSound(noiseData, kNoiseLength + 44);
+	_sound->playSound(noiseData, kNoiseLength + 44, 0);
 
 	byte *screenPixels = (byte *)_screen->getPixels();
 	int screenSize = _screen->pitch * _screen->h;
@@ -2443,7 +2443,7 @@ void PelrockEngine::antiPiracyEffect() {
 			}
 		}
 		if (!_sound->isPlaying()) {
-			_sound->playSound(noiseData, kNoiseLength + 44);
+			_sound->playSound(noiseData, kNoiseLength + 44, 0);
 		}
 
 		_screen->markAllDirty();
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index fe2da82f092..d6dc1584a65 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -151,7 +151,7 @@ private:
 
 	bool showShadows = false;
 
-	bool shouldPlayIntro = false;
+	bool shouldPlayIntro = true;
 	bool gameInitialized = false;
 	bool screenReady = false;
 
@@ -160,13 +160,6 @@ private:
 
 	int _numPressedX = 0;
 
-	// int prevDirX = 0;
-	// int prevDirY = 0;
-	// Common::String objectToShow = "";
-	// int prevWhichScreen = 0;
-	// int whichScreen = 0;
-	// byte *pixelsShadows; // =new int[640*400];
-
 protected:
 	// Engine APIs
 	Common::Error run() override;
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 30e18ccf30b..5321351ac57 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -123,10 +123,12 @@ void SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
 	}
 }
 
-void SoundManager::playSound(byte *soundData, uint32 size) {
+void SoundManager::playSound(byte *soundData, uint32 size, int channel) {
 	Audio::AudioStream *stream = Audio::makeRawStream(soundData, size, 11025, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
 	if (stream) {
-		int channel = findFreeChannel();
+		if (_mixer->isSoundHandleActive(_sfxHandles[channel])) {
+				_mixer->stopHandle(_sfxHandles[channel]);
+		}
 		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, 255U, 0, DisposeAfterUse::YES);
 	}
 }
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index e5b971dd7e3..6e33487d34b 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -159,7 +159,7 @@ public:
 	~SoundManager();
 	void playSound(byte index, int channel = -1);
 	void playSound(const char *filename, int channel, int loopCount = 1);
-	void playSound(byte *soundData, uint32 size);
+	void playSound(byte *soundData, uint32 size, int channel);
 	void stopAllSounds();
 	void stopSound(int channel);
 	void setVolume(int volume);
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index 0b682059e51..2dce77e9e56 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -92,22 +92,36 @@ void VideoManager::playIntro() {
 
 				if (_voiceEffect.contains(frameCounter)) {
 					// Wait for any playing voice to finish before starting new one
-					while (_sound->isPlaying()) {
+					while (_sound->isPlaying(0)) {
 						_events->pollEvent();
 						g_system->delayMillis(10);
 						if (g_engine->shouldQuit() || _events->_lastKeyEvent == Common::KEYCODE_ESCAPE) {
 							break;
 						}
 					}
-					Voice voice = _voiceEffect[frameCounter];
+					AudioEffect voice = _voiceEffect[frameCounter];
 					debug("Playing voice effect: '%s'", voice.filename.c_str());
 					VoiceData voiceData = _sounds[voice.filename];
 					_introSndFile.seek(voiceData.offset, SEEK_SET);
 					byte *voiceBuffer = new byte[voiceData.length];
 					_introSndFile.read(voiceBuffer, voiceData.length);
-					_sound->playSound(voiceBuffer, voiceData.length);
+					_sound->playSound(voiceBuffer, voiceData.length, 0);
 				}
 
+				if(_sfxEffect.contains(frameCounter)) {
+					AudioEffect sfx = _sfxEffect[frameCounter];
+					debug("Playing SFX effect: '%s'", sfx.filename.c_str());
+					VoiceData sfxData = _sounds[sfx.filename];
+					_introSndFile.seek(sfxData.offset, SEEK_SET);
+					byte *sfxBuffer = new byte[sfxData.length];
+					_introSndFile.read(sfxBuffer, sfxData.length);
+					_sound->playSound(sfxBuffer, sfxData.length, 1);
+				}
+
+				if(_musicEffect.contains(frameCounter)) {
+					MusicEffect music = _musicEffect[frameCounter];
+					_sound->playMusicTrack(music.trackNumber, true);
+				}
 
 				if (subtitle != nullptr) {
 					Common::StringArray lines = _dialog->wordWrap(subtitle->text)[0];
@@ -272,21 +286,57 @@ void VideoManager::initMetadata() {
 				Subtitle subtitle = readSubtitle(metadataFile);
 				_subtitles.push_back(subtitle);
 			} else if (nextChar == 'x') {
-				Voice voice = readVoice(metadataFile);
+				AudioEffect voice = readAudioEffect(metadataFile);
 				// Read filename (up to 12 bytes, null-terminated)
 				_voiceEffect[voice.startFrame] = voice;
+			} else if (nextChar == 'f') {
+				AudioEffect sfx = readAudioEffect(metadataFile);
+				_sfxEffect[sfx.startFrame] = sfx;
+			}
+			else if (nextChar == 'c') {
+				MusicEffect music = readMusicEffect(metadataFile);
+				_musicEffect[music.startFrame] = music;
 			}
 		}
 	}
 
 	debug("Loaded %d subtitles", _subtitles.size());
-	debug("Loaded %d audio effects", _voiceEffect.size());
+	debug("Loaded %d speech files", _voiceEffect.size());
 
 	metadataFile.close();
 }
 
-Voice VideoManager::readVoice(Common::File &metadataFile) {
-	Voice voice;
+MusicEffect VideoManager::readMusicEffect(Common::File &metadataFile) {
+	MusicEffect music;
+	Common::String buffer;
+
+	// Skip spaces after "/c"
+	while (!metadataFile.eos() && metadataFile.readByte() == ' ')
+		;
+	metadataFile.seek(-1, SEEK_CUR); // Step back one byte
+
+	bool frameCountRead = false;
+	while (!metadataFile.eos()) {
+		char c = metadataFile.readByte();
+		if (c == ' ') {
+			if (!buffer.empty() && !frameCountRead) {
+				music.startFrame = atoi(buffer.c_str());
+				buffer.clear();
+				frameCountRead = true;
+			}
+		} else if (c == 0x0D || c == 0x0A) {
+			break;
+		} else {
+			buffer += c;
+		}
+	}
+	music.trackNumber = atoi(buffer.c_str());
+	debug("Loaded music effect: frame %d, track %d", music.startFrame, music.trackNumber);
+	return music;
+}
+
+AudioEffect VideoManager::readAudioEffect(Common::File &metadataFile) {
+	AudioEffect voice;
 	Common::String buffer;
 
 	// Skip spaces after "/x"
diff --git a/engines/pelrock/video/video.h b/engines/pelrock/video/video.h
index 064a0c7b7ae..aa4fbf5412b 100644
--- a/engines/pelrock/video/video.h
+++ b/engines/pelrock/video/video.h
@@ -42,6 +42,7 @@ struct Effect {
 };
 
 struct AudioEffect : Effect {
+	Common::String filename;
 };
 
 struct Subtitle : Effect {
@@ -52,16 +53,8 @@ struct Subtitle : Effect {
 	Common::String text;
 };
 
-struct Voice : AudioEffect {
-	Common::String filename;
-};
-
-struct Sfx : AudioEffect {
-	uint32 soundId;
-};
-
-struct ExtraSound : AudioEffect {
-	Common::String filename;
+struct MusicEffect : Effect {
+	uint32 trackNumber;
 };
 
 struct VoiceData {
@@ -112,7 +105,8 @@ private:
 	void initMetadata();
 	void readSubtitle(Common::File &metadataFile, Pelrock::Subtitle &subtitle);
 	Subtitle readSubtitle(Common::File &metadataFile);
-	Voice readVoice(Common::File &metadataFile);
+	MusicEffect readMusicEffect(Common::File &metadataFile);
+	AudioEffect readAudioEffect(Common::File &metadataFile);
 	char decodeChar(byte c);
 	Subtitle *getSubtitleForFrame(uint16 frameNumber);
 	int _currentSubtitleIndex = 0;
@@ -120,7 +114,9 @@ private:
 	Graphics::ManagedSurface _textSurface = Graphics::ManagedSurface();
 	Common::Array<ChunkHeader> _chunkBuffer;
 	Common::Array<Subtitle> _subtitles;
-	Common::HashMap<uint16, Voice> _voiceEffect;
+	Common::HashMap<uint16, AudioEffect> _voiceEffect;
+	Common::HashMap<uint16, AudioEffect> _sfxEffect;
+	Common::HashMap<uint16, MusicEffect> _musicEffect;
 	Common::HashMap<Common::String, VoiceData> _sounds;
 	Common::File _introSndFile;
 };


Commit: 0e07504d16c5d8701b05753a6e86705c7522c2ac
    https://github.com/scummvm/scummvm/commit/0e07504d16c5d8701b05753a6e86705c7522c2ac
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:21+02:00

Commit Message:
PELROCK: Fixes animation in room 3 and computer text

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/computer.cpp
    engines/pelrock/computer.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/resources.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 17c6e16ad2a..49c2e3f1315 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -925,6 +925,7 @@ void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
 		g_system->delayMillis(10);
 	}
 
+	debug("Brick hit the window");
 	// Add the broken window sticker
 	_room->addSticker(11);
 	_sound->playSound(_room->_roomSfx[2]); // Play glass breaking sound
@@ -1273,7 +1274,7 @@ void PelrockEngine::pickUpBook(int i) {
 		}
 
 		_alfredState.isWalkingCancelable = false;
-		walkAndAction(_room->findHotspotByExtra(102), TALK);
+		walkAndAction(_room->findHotspotByExtra(358), TALK);
 		if (!_state->hasInventoryItem(3)) {
 
 			_state->setCurrentRoot(9, 0, 0);
diff --git a/engines/pelrock/computer.cpp b/engines/pelrock/computer.cpp
index 5a1ee64e2cc..fc251c14d6a 100644
--- a/engines/pelrock/computer.cpp
+++ b/engines/pelrock/computer.cpp
@@ -67,19 +67,13 @@ void Computer::init() {
 		_libraryBooks.push_back(book);
 	}
 
-	for (int i = 0; i < _libraryBooks.size(); i++) {
-		const LibraryBook &book = _libraryBooks[i];
-		debug("Loaded book: title='%s', author='%s', genre='%s', unknown=%d, shelf=%d, available=%d",
-			  book.title.c_str(), book.author.c_str(), book.genre.c_str(),
-			  book.inventoryIndex, book.shelf, book.available);
-	}
-
 	_computerText = g_engine->_res->loadComputerText();
 
 	_searchResults.clear();
 	_currentResult = 0;
 	_searchLetter = 0;
 	_memorizedBookIndex = -1;
+	_memorizedMsg = _computerText[10][0].substr(16, _computerText[10][0].size() - 16); // "Bueno... Tendre que buscar en la estanteria de la %c"
 	_lineHeight = g_engine->_smallFont->getFontHeight();
 }
 
@@ -211,7 +205,7 @@ void Computer::drawScreen() {
 		// Situacion (location/availability)
 		Common::String situacionLine = _computerText[6][0];
 		int situacionPlaceholderIndex = situacionLine.findFirstOf("XXXX");
-		situacionLine.replace(situacionPlaceholderIndex, situacionLine.size() - situacionPlaceholderIndex, book.available ? "Disponible" : "Prestado");
+		situacionLine.replace(situacionPlaceholderIndex, situacionLine.size() - situacionPlaceholderIndex, book.available ? _computerText[8][0] : _computerText[9][0]);
 		g_engine->_graphics->drawColoredText(g_engine->_screen, situacionLine, textX, textY + increment * 3, 340, defaultColor, g_engine->_smallFont);
 
 		// Show navigation options
diff --git a/engines/pelrock/computer.h b/engines/pelrock/computer.h
index ea1ba3c0a88..e666d7de5bc 100644
--- a/engines/pelrock/computer.h
+++ b/engines/pelrock/computer.h
@@ -42,7 +42,7 @@ public:
 	 * @return Book index if a book was memorized, -1 otherwise
 	 */
 	int run();
-	const char *_memorizedMsg;      // "Bueno... Tendre que buscar en la estanteria de la %c"
+	Common::String _memorizedMsg;      // "Bueno... Tendre que buscar en la estanteria de la %c"
 
 private:
 	enum ComputerState {
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index f7387341132..1a95787527a 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1079,6 +1079,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			// Scale special anim frame to Alfred size before drawing
 			drawSpriteToBuffer(_compositeBuffer, 640, frame, _alfredState.x, _alfredState.y - _res->_currentSpecialAnim->h, _res->_currentSpecialAnim->w, _res->_currentSpecialAnim->h, 255);
 		}
+		debug("Playing special anim frame %d/%d, speed %d", _res->_currentSpecialAnim->curFrame, _res->_currentSpecialAnim->numFrames, _res->_currentSpecialAnim->speed);
 		if (_chrono->getFrameCount() % _res->_currentSpecialAnim->speed == 0) {
 			_res->_currentSpecialAnim->curFrame++;
 
@@ -1807,7 +1808,7 @@ void PelrockEngine::computerLoop() {
 	computer.run();
 	if (_state->selectedBookIndex != -1 && _state->bookLetter != '\0') {
 		Common::StringArray lines;
-		lines.push_back(Common::String::format(computer._memorizedMsg, _state->bookLetter));
+		lines.push_back(computer._memorizedMsg + _state->bookLetter);
 		_dialog->sayAlfred(lines);
 	}
 }
@@ -2087,8 +2088,8 @@ void PelrockEngine::loadExtraScreenAndPresent(int screenIndex) {
 void PelrockEngine::waitForSpecialAnimation() {
 	while (!g_engine->shouldQuit() && !_res->_isSpecialAnimFinished) {
 		_events->pollEvent();
+		debug("Waiting for special animation to finish, current frame %d", _res->_currentSpecialAnim->curFrame);
 		renderScene(OVERLAY_NONE);
-		// _events->waitForKey();
 		_screen->update();
 		g_system->delayMillis(10);
 	}
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index d6dc1584a65..44020602b92 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -151,7 +151,7 @@ private:
 
 	bool showShadows = false;
 
-	bool shouldPlayIntro = true;
+	bool shouldPlayIntro = false;
 	bool gameInitialized = false;
 	bool screenReady = false;
 
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 546bd1c88af..da9f1007cf0 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -40,7 +40,7 @@ const AlfredSpecialAnimOffset ResourceManager::alfredSpecialAnims[] = {
 	{10, 51, 102, 1, 7, 578943, 1},      // 1 - READ RECIPE
 	{3, 45, 87, 0, 7, 37000, 1},         // 2 -  ELECTRIC SHOCK 1
 	{2, 82, 58, 0, 7, 53106, 20, 1},        // 3 - ELECTRIC SHOCK 3
-	{3, 71, 110, 1, 2, 20724, 1, 62480}, // 4 - Throw
+	{3, 71, 110, 1, 2, 20724, 1, 1, 62480}, // 4 - Throw
 	{14, 171, 107, 1, 7, 1556540, 1},    // 5 - crocodile
 	{12, 113, 103, 1, 7, 1583702, 1},    // 6 - exit through manhole
 	{11, 33, 72, 1, 7, 1761234, 1},      // 7 - alfred climbs down
@@ -386,12 +386,19 @@ Common::Array<Common::StringArray> ResourceManager::loadComputerText() {
 	if (!exe.open("JUEGO.EXE")) {
 		error("Couldnt find file JUEGO.EXE");
 	}
-	int bufSize = 416;
+	int bufSize = 490;
 	byte *computerTextBuf = new byte[bufSize];
 	exe.seek(0x0004901A, SEEK_SET);
 	exe.read(computerTextBuf, bufSize);
 	Common::Array<Common::StringArray> computerTexts = processTextData(computerTextBuf, bufSize);
 
+	for(int i = 0; i < computerTexts.size(); i++) {
+		debug("Computer text %d:", i);
+		Common::StringArray &lines = computerTexts[i];
+		for (int j = 0; j < lines.size(); j++) {
+			debug("  Line %d: '%s'", j, lines[j].c_str());
+		}
+	}
 	delete[] computerTextBuf;
 
 	exe.close();


Commit: 60acdb4c8d0f71fdce9ba3236bbf192e2df16208
    https://github.com/scummvm/scummvm/commit/60acdb4c8d0f71fdce9ba3236bbf192e2df16208
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:21+02:00

Commit Message:
PELROCK: Fixes menu descriptions

Changed paths:
    engines/pelrock/computer.cpp
    engines/pelrock/computer.h
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h
    engines/pelrock/offsets.h


diff --git a/engines/pelrock/computer.cpp b/engines/pelrock/computer.cpp
index fc251c14d6a..18e1f250ec5 100644
--- a/engines/pelrock/computer.cpp
+++ b/engines/pelrock/computer.cpp
@@ -73,6 +73,8 @@ void Computer::init() {
 	_currentResult = 0;
 	_searchLetter = 0;
 	_memorizedBookIndex = -1;
+	_titleMsg = _computerText[10][0].substr(0, 7);
+	_authorMsg = _computerText[10][0].substr(7, 6);
 	_memorizedMsg = _computerText[10][0].substr(16, _computerText[10][0].size() - 16); // "Bueno... Tendre que buscar en la estanteria de la %c"
 	_lineHeight = g_engine->_smallFont->getFontHeight();
 }
@@ -166,7 +168,7 @@ void Computer::drawScreen() {
 
 	case STATE_SHOW_RESULTS: {
 
-		const char *section = _searchType == 0 ? "TITULO " : "AUTOR  ";
+		const char *section = _searchType == 0 ? _titleMsg.c_str() : _authorMsg.c_str();
 		Common::String title = _computerText[2][0];
 		int replacementIndex = title.findFirstOf("XXXXXXX");
 
diff --git a/engines/pelrock/computer.h b/engines/pelrock/computer.h
index e666d7de5bc..848fec53cc9 100644
--- a/engines/pelrock/computer.h
+++ b/engines/pelrock/computer.h
@@ -42,6 +42,8 @@ public:
 	 * @return Book index if a book was memorized, -1 otherwise
 	 */
 	int run();
+	Common::String _titleMsg;
+	Common::String _authorMsg;
 	Common::String _memorizedMsg;      // "Bueno... Tendre que buscar en la estanteria de la %c"
 
 private:
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 6bfb31678ec..4680e454e7e 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -69,8 +69,10 @@ void MenuManager::checkMouseClick(int x, int y) {
 
 	bool selectedItem = false;
 	for (int i = 0; i < 4; i++) {
-		if (x >= 140 + (82 * i) && x <= 140 + (82 * i) + 64 &&
-			y >= 115 - (8 * i) && y <= 115 - (8 * i) + 64) {
+
+		Common::Rect itemRect = Common::Rect(_inventorySlots[i], 60, 60);
+
+		if (itemRect.contains(x, y)) {
 			selectedItem = selectInventoryItem(i);
 			return;
 		}
@@ -127,7 +129,7 @@ void MenuManager::showCredits() {
 	byte *decompressedCredits = nullptr;
 	rleDecompress(creditsBuf, creditsSize, 0, 0, &decompressedCredits, true);
 	// draw credits in two columns taking the entire height of the screen and stating in y = 0
-	for(int i = 0; i < 34; i++) {
+	for (int i = 0; i < 34; i++) {
 		byte *singleCredit = new byte[creditWidth * creditHeight];
 		int x = (i < 34 / 2) ? 39 : 359;
 		int y = 3 + (i % (34 / 2)) * (400 / (34 / 2));
@@ -140,7 +142,7 @@ void MenuManager::showCredits() {
 	delete[] decompressedCredits;
 	delete[] creditsBuf;
 
-	while(!g_engine->shouldQuit() && !_events->_leftMouseClicked && !_events->_rightMouseClicked) {
+	while (!g_engine->shouldQuit() && !_events->_leftMouseClicked && !_events->_rightMouseClicked) {
 		_events->pollEvent();
 		_screen->markAllDirty();
 		_screen->update();
@@ -208,10 +210,11 @@ void MenuManager::drawInventoryIcons() {
 		if (g_engine->_state->inventoryItems.size() <= itemIndex)
 			continue;
 		InventoryObject item = g_engine->_res->getIconForObject(g_engine->_state->inventoryItems[itemIndex]);
-		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, 140 + (82 * i), 115 - (8 * i), 60, 60, 1);
+		Common::Point slot = _inventorySlots[i];
+		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, slot.x, slot.y, 60, 60, 1);
 		if (debugIcons) {
-			drawRect(_compositeBuffer, 140 + (82 * i), 115 - (8 * i), 60, 60, 13);
-			drawText(_compositeBuffer, g_engine->_smallFont, Common::String::format("ID %d", g_engine->_state->inventoryItems[itemIndex]), 140 + (82 * i) + 2, 115 - (8 * i) + 2, 640, 13);
+			drawRect(_compositeBuffer, slot.x, slot.y, 60, 60, 13);
+			drawText(_compositeBuffer, g_engine->_smallFont, Common::String::format("ID %d", g_engine->_state->inventoryItems[itemIndex]), slot.x + 2, slot.y + 2, 640, 13);
 		}
 	}
 }
@@ -294,6 +297,10 @@ void MenuManager::loadMenu() {
 
 	_menuText = _menuTexts[0];
 	alfred7.close();
+
+	for (int i = 0; i < 4; i++) {
+		_inventorySlots.push_back(Common::Point(140 + (82 * i), 115 - (8 * i)));
+	}
 }
 
 void MenuManager::readButton(Common::File &alfred7, uint32 offset, byte *outBuffer[2], Common::Rect rect) {
@@ -317,6 +324,9 @@ void MenuManager::loadMenuTexts() {
 	exe.seek(kInventoryDescriptionsOffset, SEEK_SET);
 	exe.read(descBuffer, kInventoryDescriptionsSize);
 	_inventoryDescriptions = _res->processTextData(descBuffer, kInventoryDescriptionsSize, true);
+	for(size_t i = 0; i < _inventoryDescriptions.size(); i++) {
+		debug("Inventory description %d: %s", i, _inventoryDescriptions[i][0].c_str());
+	}
 	delete[] descBuffer;
 
 	Common::String desc = "";
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 30d6d5cc1db..658c9c9243d 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -261,6 +261,7 @@ private:
 	int _curInventoryPage = 0;
 	Common::StringArray _menuText;
 	Common::Array<Common::StringArray> _inventoryDescriptions;
+	Common::Array<Common::Point> _inventorySlots;
 
 	bool showButtons = true;
 };
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index e5d37e1a862..05f5a82f24f 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -45,7 +45,7 @@ static const uint32_t kSettingsPaletteOffset = 0x2884c2;         // 640 * 480
 #define DESCRIPTION_BASE_OFFSET 0x4715D
 #define NUM_DESCRIPTIONS 113
 
-static const uint32 kInventoryDescriptionsOffset = 0x4715D;
+static const uint32 kInventoryDescriptionsOffset = 0x4715E;
 static const uint32 kInventoryDescriptionsSize = 7868;
 static const uint32 kMenuTextOffset = 0x49203;
 static const uint32 kMenuTextSize = 230;


Commit: c2cbca370cedb17b63c0bbdb3eb3fa87abb795b5
    https://github.com/scummvm/scummvm/commit/c2cbca370cedb17b63c0bbdb3eb3fa87abb795b5
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:22+02:00

Commit Message:
PELROCK: Fixes walkboxes in room 14

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/room.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 49c2e3f1315..a704fafb94b 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -326,21 +326,14 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 272:
 		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
-	case 273:
-		WalkBox w1;
-		w1.x = 436;
-		w1.y = 356;
-		w1.w = 4;
-		w1.h = 14;
-		WalkBox w2;
-		w2.x = 440;
-		w2.y = 368;
-		w2.w = 148;
-		w2.h = 2;
+	case 273: {
+		WalkBox w1 = { 3, 436, 356, 4, 14, 0 };
+		WalkBox w2 = { 4, 440, 368,148, 2, 0 };
 
 		_room->addWalkbox(w1);
 		_room->addWalkbox(w2);
 		break;
+	}
 	case 274:
 	case 275:
 	case 276:
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 44ab35729ba..a62a615fa98 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -315,6 +315,7 @@ void RoomManager::addWalkbox(WalkBox walkbox, int persist) {
 		_currentRoomWalkboxes.push_back(walkbox);
 	}
 	if (persist & PERSIST_PERM) {
+		debug("Adding walkbox change for room %d, index %d, x=%d y=%d w=%d h=%d", _currentRoomNumber, walkbox.index, walkbox.x, walkbox.y, walkbox.w, walkbox.h);
 		g_engine->_state->roomWalkBoxChanges[_currentRoomNumber].push_back({_currentRoomNumber, walkbox.index, walkbox});
 	}
 }
@@ -1007,16 +1008,18 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(byte *data, size_t size) {
 		int16 w = READ_LE_INT16(data + boxOffset + 4);
 		int16 h = READ_LE_INT16(data + boxOffset + 6);
 		byte flags = data[boxOffset + 8];
-		// debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
+		debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
 		WalkBox box;
 		box.index = i;
 		bool isChanged = false;
 		if (g_engine->_state->roomWalkBoxChanges.contains(_currentRoomNumber)) {
+			debug("Checking for changes to walkbox %d in room %d", i, _currentRoomNumber);
 			// if the walkbox has been changed, load the changed version
 			for (int j = 0; j < g_engine->_state->roomWalkBoxChanges[_currentRoomNumber].size(); j++) {
 				if (g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkboxIndex == i) {
 					walkboxes.push_back(g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkbox);
 					isChanged = true;
+					debug("Walkbox %d has been changed, loading changed version, x1=%d y1=%d w=%d h=%d", i, g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkbox.x, g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkbox.y, g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkbox.w, g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkbox.h);
 					break;
 				}
 			}
@@ -1042,11 +1045,12 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(byte *data, size_t size) {
 				}
 			}
 			if (!found) {
+				debug("Adding new walkbox for room %d at index %d", _currentRoomNumber, g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkboxIndex);
 				walkboxes.push_back(g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkbox);
 			}
 		}
 	}
-
+	debug("Total walkboxes for room %d: %d", _currentRoomNumber, walkboxes.size());
 	return walkboxes;
 }
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 5e3e624df09..6efc93e3401 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -448,8 +448,7 @@ struct PaletteAnim {
 #define PASSERBY_DOWN 2
 
 struct PasserByAnim {
-	// uint32 frameTrigger = 0x3FF;
-	uint32 frameTrigger = 0x96;
+	uint32 frameTrigger = 0x3FF;
 	int16 startX;
 	int16 startY;
 	int16 resetCoord;


Commit: 197f9e676d9f2f2c8ff8fa46d7969a20c9fe2d85
    https://github.com/scummvm/scummvm/commit/197f9e676d9f2f2c8ff8fa46d7969a20c9fe2d85
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:22+02:00

Commit Message:
PELROCK: Fixes music in travel scene

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/console.cpp
    engines/pelrock/console.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/sound.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index a704fafb94b..5cff78e7307 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -346,14 +346,16 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 278:
 		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
-	case 279:
+	case 279:{
+		_state->removeInventoryItem(75);
 		travelToEgypt();
+
 		break;
 		// merchants
-
+	}
 	case 330:
 		// Two oranges
-		addInventoryItem(103);
+		addInventoryItem(102);
 		break;
 	case 331:
 		_dialog->say(_res->_ingameTexts[HECHOELPRIMO]);
@@ -373,7 +375,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		break;
 	case 335:
 		// many oranges
-		addInventoryItem(104);
+		addInventoryItem(103);
 		break;
 	case 336:
 		_dialog->say(_res->_ingameTexts[PESADO_UNRATO]);
diff --git a/engines/pelrock/console.cpp b/engines/pelrock/console.cpp
index a4676a5e913..0a5f6f25f3e 100644
--- a/engines/pelrock/console.cpp
+++ b/engines/pelrock/console.cpp
@@ -31,6 +31,7 @@ PelrockConsole::PelrockConsole(PelrockEngine *engine) : GUI::Debugger(), _engine
 	registerCmd("give", WRAP_METHOD(PelrockConsole, cmdGiveItems));
 	registerCmd("setRoot", WRAP_METHOD(PelrockConsole, cmdSetRoot));
 	registerCmd("setFlag", WRAP_METHOD(PelrockConsole, cmdSetFlag));
+	registerCmd("getFlag", WRAP_METHOD(PelrockConsole, cmdGetFlag));
 	registerCmd("toJail", WRAP_METHOD(PelrockConsole, cmdToJail));
 	registerCmd("removeSticker", WRAP_METHOD(PelrockConsole, cmdRemoveSticker));
 }
@@ -67,6 +68,17 @@ bool PelrockConsole::cmdSetFlag(int argc, const char **argv) {
 	return true;
 }
 
+bool PelrockConsole::cmdGetFlag(int argc, const char **argv) {
+	if (argc < 2) {
+		debugPrintf("Usage: getFlag <flagIndex>");
+		return true;
+	}
+	int flagIndex = atoi(argv[1]);
+	int value = _engine->_state->getFlag(flagIndex);
+	debugPrintf("Flag %d is %d\n", flagIndex, value);
+	return true;
+}
+
 bool PelrockConsole::cmdSetRoot(int argc, const char **argv) {
 	if (argc < 3) {
 		debugPrintf("Usage: setRoot <roomNumber> <rootIndex> <npcIndex (optional, default=0)>");
diff --git a/engines/pelrock/console.h b/engines/pelrock/console.h
index e20cd09951e..53f5dfca4ce 100644
--- a/engines/pelrock/console.h
+++ b/engines/pelrock/console.h
@@ -37,6 +37,7 @@ private:
 	bool cmdTest(int argc, const char **argv);
 	bool cmdSetRoot(int argc, const char **argv);
 	bool cmdSetFlag(int argc, const char **argv);
+	bool cmdGetFlag(int argc, const char **argv);
 	bool cmdRemoveSticker(int argc, const char **argv);
 
 public:
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 1a95787527a..d8ecb49cf1b 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -263,7 +263,8 @@ void PelrockEngine::playSoundIfNeeded() {
 
 void PelrockEngine::travelToEgypt() {
 	_graphics->fadeToBlack(10);
-	_sound->playMusicTrack(26, false);
+
+	_sound->playMusicTrack(26, 1);
 	byte *palette = new byte[768];
 	if (_bgScreen == nullptr) {
 		_bgScreen = new byte[640 * 400];
@@ -287,7 +288,6 @@ void PelrockEngine::travelToEgypt() {
 			g_system->getPaletteManager()->setPalette(palette, 0, 256);
 			frameCount++;
 		}
-		drawPaletteSquares((byte *)_screen->getPixels(), palette);
 
 		_screen->markAllDirty();
 		_screen->update();
@@ -302,6 +302,7 @@ void PelrockEngine::travelToEgypt() {
 	delete[] palette;
 	_screen->markAllDirty();
 	_screen->update();
+	setScreenAndPrepare(21, ALFRED_DOWN);
 }
 
 bool PelrockEngine::renderScene(int overlayMode) {
@@ -2104,7 +2105,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 	case 0:
 		if (_state->getFlag(FLAG_PUESTA_SALSA_PICANTE) && !_state->getFlag(FLAG_JEFE_ENCARCELADO)) {
 			_state->setFlag(FLAG_JEFE_ENCARCELADO, true);
-			_room->disableSprite(13, 0, true);
+			_room->disableSprite(13, 0, PERSIST_BOTH);
 			loadExtraScreenAndPresent(4);
 		}
 		break;
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 5321351ac57..96b621e9769 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -119,7 +119,7 @@ void SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
 		}
 		Audio::AudioStream *finalStream = loopCount != -1 ? stream : Audio::makeLoopingAudioStream(stream, 0);
 
-		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], finalStream, loopCount, 255U, 0, DisposeAfterUse::YES);
+		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], finalStream, loopCount, _currentVolume, 0, DisposeAfterUse::YES);
 	}
 }
 
@@ -129,7 +129,7 @@ void SoundManager::playSound(byte *soundData, uint32 size, int channel) {
 		if (_mixer->isSoundHandleActive(_sfxHandles[channel])) {
 				_mixer->stopHandle(_sfxHandles[channel]);
 		}
-		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, 255U, 0, DisposeAfterUse::YES);
+		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, _currentVolume, 0, DisposeAfterUse::YES);
 	}
 }
 
@@ -233,9 +233,11 @@ bool SoundManager::isMusicPlaying() {
 void SoundManager::playMusicTrack(int trackNumber, bool loop) {
 	if (!_isPaused && _currentMusicTrack == trackNumber && isMusicPlaying()) {
 		// Already playing this track
+		debug("Track %d is already playing", trackNumber);
 		return;
 	}
 	_currentMusicTrack = trackNumber;
+	debug("Playing music track %d, loop=%d", trackNumber, loop);
 
 	if(!_isPaused) {
 		_cdTrackStart = 0;


Commit: acae2fa73395b594e1880da80713cc801e7afbed
    https://github.com/scummvm/scummvm/commit/acae2fa73395b594e1880da80713cc801e7afbed
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:22+02:00

Commit Message:
PELROCK: Fixes using pumpkin with river

Changed paths:
    engines/pelrock/actions.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 5cff78e7307..9a66fb40c65 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -370,7 +370,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_dialog->say(_res->_ingameTexts[MEHANTOMADO_EL_PELO]);
 		break;
 	case 334:
-		addInventoryItem(86);
+		addInventoryItem(76);
 		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
 	case 335:
@@ -1203,8 +1203,8 @@ void PelrockEngine::closeTravelAgencyDoor(HotSpot *hotspot) {
 }
 
 void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
-	_state->removeInventoryItem(76);
-	addInventoryItem(86);
+	_state->removeInventoryItem(86);
+	addInventoryItem(76);
 	_sound->playMusicTrack(27);
 	checkIngredients();
 	_dialog->say(_res->_ingameTexts[CUIDADOIMPRUDENTE]);
@@ -1213,10 +1213,13 @@ void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
 	playAlfredSpecialAnim(5);
 	_sound->playSound(_room->_roomSfx[0], 0); // Belch
 	waitForSoundEnd();
+	_alfredState.animState = ALFRED_SKIP_DRAWING;
 	_graphics->fadeToBlack(10);
 	// update conversaton state
 	_alfredState.x = 300;
 	_alfredState.y = 238;
+	waitForSoundEnd();
+	_alfredState.animState = ALFRED_IDLE;
 	setScreenAndPrepare(28, ALFRED_DOWN);
 	_dialog->say(_res->_ingameTexts[QUEOSCUROESTAESTO]);
 }
@@ -1879,6 +1882,7 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 		HotSpot *floorTile = _room->findHotspotByExtra(462);
 		floorTile->actionFlags = ACTION_MASK_OPEN;
 		_room->changeHotSpot(*floorTile);
+		break;
 	}
 
 	case 307: {


Commit: 3ec216e9e91f63899cbd0144ce372353b55e9b7d
    https://github.com/scummvm/scummvm/commit/3ec216e9e91f63899cbd0144ce372353b55e9b7d
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:23+02:00

Commit Message:
PELROCK: Migrate to using ManagedSurfaces

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/computer.cpp
    engines/pelrock/computer.h
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/extrascreens.cpp
    engines/pelrock/extrascreens.h
    engines/pelrock/graphics.cpp
    engines/pelrock/graphics.h
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/util.cpp
    engines/pelrock/util.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 9a66fb40c65..6110d1562c1 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -687,9 +687,9 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
  * Alfred and the princess.
  */
 void PelrockEngine::turnLightsOff() {
-	memset(_currentBackground, 0, 640 * 400);
-	memset(_compositeBuffer, 0, 640 * 400);
-	memset(_screen->getPixels(), 0, 640 * 400);
+	_currentBackground.clear(0);
+	_compositeBuffer.clear(0);
+	_screen->clear(0);
 	byte darkPalette[768] = {};
 	darkPalette[238 * 3 + 0] = 60 << 2; // R = 240
 	darkPalette[238 * 3 + 1] = 57 << 2; // G = 228
@@ -1972,7 +1972,7 @@ void PelrockEngine::teletransportToPrincess() {
 			int y1 = lines[phase][1];
 			int x2 = lines[phase][2] + i;
 			int y2 = lines[phase][3];
-			drawRemappedLine(_compositeBuffer, x1, y1, x2, y2, _room->_paletteRemaps[1]);
+			drawRemappedLine((byte *)_compositeBuffer.getPixels(), x1, y1, x2, y2, _room->_paletteRemaps[1]);
 		}
 
 		updateAnimations();
@@ -2078,7 +2078,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		}
 		break;
 	case 88: {
-		SpellBook spellBook = SpellBook(_events, _res);
+		SpellBook spellBook(_events, _res);
 		playAlfredSpecialAnim(0);
 
 		Spell *spell = spellBook.run();
@@ -2197,7 +2197,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		break;
 	}
 	case 95: {
-		CDPlayer player = CDPlayer(_events, _res, _sound);
+		CDPlayer player(_events, _res, _sound);
 		player.run();
 		break;
 	}
diff --git a/engines/pelrock/computer.cpp b/engines/pelrock/computer.cpp
index 18e1f250ec5..fe8b574f810 100644
--- a/engines/pelrock/computer.cpp
+++ b/engines/pelrock/computer.cpp
@@ -30,8 +30,7 @@
 namespace Pelrock {
 
 Computer::Computer(PelrockEventManager *eventMan)
-	: _backgroundScreen(nullptr),
-	  _palette(nullptr),
+	: _palette(nullptr),
 	  _state(STATE_MAIN_MENU),
 	  _searchLetter(0),
 	  _searchType(0),
@@ -85,17 +84,14 @@ Computer::~Computer() {
 
 void Computer::loadBackground() {
 	_palette = new byte[768];
-	_backgroundScreen = new byte[640 * 400];
+	_backgroundScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 
-	g_engine->_res->getExtraScreen(1, _backgroundScreen, _palette);
+	g_engine->_res->getExtraScreen(1, (byte *)_backgroundScreen.getPixels(), _palette);
 	g_system->getPaletteManager()->setPalette(_palette, 0, 256);
 }
 
 void Computer::cleanup() {
-	if (_backgroundScreen) {
-		delete[] _backgroundScreen;
-		_backgroundScreen = nullptr;
-	}
+	_backgroundScreen.free();
 	if (_palette) {
 		// Restore room palette
 		g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
@@ -143,7 +139,7 @@ int Computer::run() {
 
 void Computer::drawScreen() {
 	// Clear to background
-	memcpy(g_engine->_screen->getPixels(), _backgroundScreen, 640 * 400);
+	g_engine->_screen->blitFrom(_backgroundScreen);
 
 	byte defaultColor = 0; // Light gray
 
diff --git a/engines/pelrock/computer.h b/engines/pelrock/computer.h
index 848fec53cc9..c34e918f873 100644
--- a/engines/pelrock/computer.h
+++ b/engines/pelrock/computer.h
@@ -24,6 +24,7 @@
 
 #include "common/array.h"
 #include "common/str.h"
+#include "graphics/managed_surface.h"
 
 #include "pelrock/events.h"
 #include "pelrock/library_books.h"
@@ -56,7 +57,7 @@ private:
 	};
 
 	PelrockEventManager *_events;
-	byte *_backgroundScreen;
+	Graphics::ManagedSurface _backgroundScreen;
 	byte *_palette;
 
 	// State variables
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 6038cf219d4..1319172f159 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -112,7 +112,7 @@ uint32 DialogManager::readTextBlock(
 	return pos;
 }
 
-void DialogManager::displayChoices(Common::Array<ChoiceOption> *choices, byte *compositeBuffer) {
+void DialogManager::displayChoices(Common::Array<ChoiceOption> *choices, Graphics::ManagedSurface &compositeBuffer) {
 
 	int overlayHeight = choices->size() * kChoiceHeight + 2;
 	Common::Point overlayPos = _graphics->showOverlay(overlayHeight, compositeBuffer);
@@ -301,7 +301,7 @@ void DialogManager::displayDialogue(Common::String text, byte speakerId) {
  * Select a choice from displayed options
  * Returns the index of the selected choice in the choices array
  */
-int DialogManager::selectChoice(Common::Array<Common::String> &choices, byte *compositeBuffer) {
+int DialogManager::selectChoice(Common::Array<Common::String> &choices, Graphics::ManagedSurface &compositeBuffer) {
 	_events->_leftMouseClicked = false;
 	_dialogActive = true;
 	g_engine->_chrono->pauseCounter();
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 1e0c61e68bb..51e98d0f1dd 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -23,6 +23,7 @@
 
 #include "common/scummsys.h"
 #include "common/stack.h"
+#include "graphics/managed_surface.h"
 #include "graphics/screen.h"
 
 #include "pelrock/events.h"
@@ -101,8 +102,8 @@ public:
 	DialogManager(Graphics::Screen *screen, PelrockEventManager *events, GraphicsManager *graphics);
 	~DialogManager();
 
-	void displayChoices(Common::Array<ChoiceOption> *choices, byte *compositeBuffer);
-	int selectChoice(Common::Array<Common::String> &choices, byte *compositeBuffer);
+	void displayChoices(Common::Array<ChoiceOption> *choices, Graphics::ManagedSurface &compositeBuffer);
+	int selectChoice(Common::Array<Common::String> &choices, Graphics::ManagedSurface &compositeBuffer);
 	void startConversation(const byte *conversationData, uint32 dataSize, byte npcIndex, Sprite *alfredAnimSet = nullptr);
 	uint32 findRoot(int npc, int &currentRoot, uint32 position, uint32 dataSize, const byte *conversationData);
 	uint32 findSpeaker(byte npcIndex, uint32 dataSize, const byte *conversationData);
diff --git a/engines/pelrock/extrascreens.cpp b/engines/pelrock/extrascreens.cpp
index b1901494a0d..0c9ad6fa5bd 100644
--- a/engines/pelrock/extrascreens.cpp
+++ b/engines/pelrock/extrascreens.cpp
@@ -29,8 +29,7 @@
 namespace Pelrock {
 
 SpellBook::SpellBook(PelrockEventManager *eventMan, ResourceManager *res)
-	: _backgroundScreen(nullptr),
-	  _palette(nullptr),
+	: _palette(nullptr),
 	  _events(eventMan),
 	  _res(res),
 	  _spell(nullptr) {
@@ -56,14 +55,14 @@ Spell *SpellBook::run() {
 		g_engine->_screen->update();
 		g_system->delayMillis(10);
 	}
-	memset(g_engine->_screen->getPixels(), 0, 640 * 400);
+	g_engine->_screen->clear(0);
 	// Restore room palette
 	g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
 	return _selectedSpell;
 }
 
 void SpellBook::init() {
-	_compositeScreen = new byte[640 * 400];
+	_compositeScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 
 	// selectPage(0);
 }
@@ -113,34 +112,32 @@ void SpellBook::selectPage(int page) {
 }
 
 void SpellBook::drawScreen() {
-	memcpy(_compositeScreen, _backgroundScreen, 640 * 400);
+	_compositeScreen.blitFrom(_backgroundScreen);
 
 	int textY = 83;
 	int textX = 317;
 
 	if (_spell != nullptr) {
-		drawSpriteToBuffer(_compositeScreen, 640, _spell->image, 168, 143, 119, 99, 207);
+		drawSpriteToBuffer(_compositeScreen, _spell->image, 168, 143, 119, 99, 207);
 		g_engine->_graphics->drawColoredTexts(_compositeScreen, _spell->text, textX, textY, 640, 0, g_engine->_smallFont);
 	}
 
-	memcpy(g_engine->_screen->getPixels(), _compositeScreen, 640 * 400);
+	g_engine->_screen->blitFrom(_compositeScreen);
 	if (_spell != nullptr) {
 		g_engine->_graphics->drawColoredTexts(g_engine->_screen, _spell->text, textX, textY, 640, 0, g_engine->_smallFont);
 	}
 }
 
 void SpellBook::loadBackground() {
-	_backgroundScreen = new byte[640 * 400];
+	_backgroundScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 	_palette = new byte[768];
-	_res->getExtraScreen(8, _backgroundScreen, _palette);
+	_res->getExtraScreen(8, (byte *)_backgroundScreen.getPixels(), _palette);
 	g_system->getPaletteManager()->setPalette(_palette, 0, 256);
 }
 
 void SpellBook::cleanup() {
-	if (_backgroundScreen) {
-		delete[] _backgroundScreen;
-		_backgroundScreen = nullptr;
-	}
+	_backgroundScreen.free();
+	_compositeScreen.free();
 	if (_palette) {
 		delete[] _palette;
 		_palette = nullptr;
@@ -202,7 +199,7 @@ void CDPlayer::run() {
 		g_engine->_screen->update();
 		g_system->delayMillis(10);
 	}
-	memset(g_engine->_screen->getPixels(), 0, 640 * 400);
+	g_engine->_screen->clear(0);
 	// Restore room palette
 	g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
 	_sound->stopMusic();
@@ -210,7 +207,7 @@ void CDPlayer::run() {
 }
 
 void CDPlayer::init() {
-	_compositeScreen = new byte[640 * 400];
+	_compositeScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 	loadBackground();
 	loadControls();
 	loadTrackNames();
@@ -231,12 +228,12 @@ void CDPlayer::loadTrackNames() {
 }
 
 void CDPlayer::drawScreen() {
-	memcpy(_compositeScreen, _backgroundScreen, 640 * 400);
-	drawSpriteToBuffer(_compositeScreen, 640, _controls, 1, 1, 213, 72, 207);
+	_compositeScreen.blitFrom(_backgroundScreen);
+	drawSpriteToBuffer(_compositeScreen, _controls, 1, 1, 213, 72, 207);
 
 	drawButtons();
 
-	memcpy(g_engine->_screen->getPixels(), _compositeScreen, 640 * 400);
+	g_engine->_screen->blitFrom(_compositeScreen);
 	g_engine->_smallFont->drawString(g_engine->_screen, trackNames[_selectedTrack - 2], 26, 17, 640, 255, Graphics::kTextAlignLeft);
 }
 
@@ -244,25 +241,23 @@ void CDPlayer::drawButtons() {
 
 	for (int i = 0; i < 5; i++) {
 		if (_selectedButton == i) {
-			drawSpriteToBuffer(_compositeScreen, 640, buttons[i][1], _buttonRects[i].left, _buttonRects[i].top, _buttonRects[i].width(), _buttonRects[i].height(), 207);
+			drawSpriteToBuffer(_compositeScreen, buttons[i][1], _buttonRects[i].left, _buttonRects[i].top, _buttonRects[i].width(), _buttonRects[i].height(), 207);
 		} else {
-			drawSpriteToBuffer(_compositeScreen, 640, buttons[i][0], _buttonRects[i].left, _buttonRects[i].top, _buttonRects[i].width(), _buttonRects[i].height(), 207);
+			drawSpriteToBuffer(_compositeScreen, buttons[i][0], _buttonRects[i].left, _buttonRects[i].top, _buttonRects[i].width(), _buttonRects[i].height(), 207);
 		}
 	}
 }
 
 void CDPlayer::loadBackground() {
-	_backgroundScreen = new byte[640 * 400];
+	_backgroundScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 	_palette = new byte[768];
-	_res->getExtraScreen(10, _backgroundScreen, _palette);
+	_res->getExtraScreen(10, (byte *)_backgroundScreen.getPixels(), _palette);
 	g_system->getPaletteManager()->setPalette(_palette, 0, 256);
 }
 
 void CDPlayer::cleanup() {
-	if (_backgroundScreen) {
-		delete[] _backgroundScreen;
-		_backgroundScreen = nullptr;
-	}
+	_backgroundScreen.free();
+	_compositeScreen.free();
 
 	if (_palette) {
 		delete[] _palette;
@@ -385,13 +380,13 @@ void BackgroundBook::run() {
 		g_engine->_screen->update();
 		g_system->delayMillis(10);
 	}
-	memset(g_engine->_screen->getPixels(), 0, 640 * 400);
+	g_engine->_screen->clear(0);
 	// Restore room palette
 	g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
 }
 
 void BackgroundBook::init() {
-	_compositeScreen = new byte[640 * 400];
+	_compositeScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 	loadBackground();
 	loadButtons();
 	loadRoomNames();
@@ -476,9 +471,9 @@ void BackgroundBook::loadRoomNames() {
 
 
 void BackgroundBook::drawScreen() {
-	memcpy(_compositeScreen, _backgroundScreen, 640 * 400);
+	_compositeScreen.blitFrom(_backgroundScreen);
 	drawButtons();
-	memcpy(g_engine->_screen->getPixels(), _compositeScreen, 640 * 400);
+	g_engine->_screen->blitFrom(_compositeScreen);
 
 
 	int firstItem = _selectedPage * kItemsPerPage;
@@ -495,17 +490,17 @@ void BackgroundBook::drawScreen() {
 void BackgroundBook::drawButtons() {
 	for (int i = 0; i < 2; i++) {
 		if (_selectedButton == i) {
-			drawSpriteToBuffer(_compositeScreen, 640, _buttons[i][0], _buttonRects[i].left, _buttonRects[i].top, _buttonRects[i].width(), _buttonRects[i].height(), 207);
+			drawSpriteToBuffer(_compositeScreen, _buttons[i][0], _buttonRects[i].left, _buttonRects[i].top, _buttonRects[i].width(), _buttonRects[i].height(), 207);
 		} else {
-			drawSpriteToBuffer(_compositeScreen, 640, _buttons[i][1], _buttonRects[i].left, _buttonRects[i].top, _buttonRects[i].width(), _buttonRects[i].height(), 207);
+			drawSpriteToBuffer(_compositeScreen, _buttons[i][1], _buttonRects[i].left, _buttonRects[i].top, _buttonRects[i].width(), _buttonRects[i].height(), 207);
 		}
 	}
 }
 
 void BackgroundBook::loadBackground() {
-	_backgroundScreen = new byte[640 * 400];
+	_backgroundScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 	_palette = new byte[768];
-	_res->getExtraScreen(13, _backgroundScreen, _palette);
+	_res->getExtraScreen(13, (byte *)_backgroundScreen.getPixels(), _palette);
 	g_system->getPaletteManager()->setPalette(_palette, 0, 256);
 }
 
@@ -528,14 +523,8 @@ void BackgroundBook::loadButtons() {
 }
 
 void BackgroundBook::cleanup() {
-	if (_compositeScreen) {
-		delete[] _compositeScreen;
-		_compositeScreen = nullptr;
-	}
-	if (_backgroundScreen) {
-		delete[] _backgroundScreen;
-		_backgroundScreen = nullptr;
-	}
+	_compositeScreen.free();
+	_backgroundScreen.free();
 	if (_palette) {
 		delete[] _palette;
 		_palette = nullptr;
diff --git a/engines/pelrock/extrascreens.h b/engines/pelrock/extrascreens.h
index 8f0c209d42e..f0226b1d965 100644
--- a/engines/pelrock/extrascreens.h
+++ b/engines/pelrock/extrascreens.h
@@ -22,6 +22,8 @@
 #ifndef PELROCK_EXTRASCREENS_H
 #define PELROCK_EXTRASCREENS_H
 
+#include "graphics/managed_surface.h"
+
 #include "pelrock/pelrock.h"
 
 namespace Pelrock {
@@ -72,8 +74,8 @@ public:
 private:
 	PelrockEventManager *_events;
 	ResourceManager *_res;
-	byte *_backgroundScreen;
-	byte *_compositeScreen;
+	Graphics::ManagedSurface _backgroundScreen;
+	Graphics::ManagedSurface _compositeScreen;
 	byte *_palette;
 
 	Spell *_spell = nullptr;
@@ -118,8 +120,8 @@ private:
 	ResourceManager *_res;
 	SoundManager *_sound;
 	PelrockEventManager *_events;
-	byte *_backgroundScreen;
-	byte *_compositeScreen;
+	Graphics::ManagedSurface _backgroundScreen;
+	Graphics::ManagedSurface _compositeScreen;
 	byte *_palette;
 	byte *_controls;
 	Common::String trackNames[31];
@@ -164,8 +166,8 @@ private:
 
 	PelrockEventManager *_events;
 	ResourceManager *_res;
-	byte *_backgroundScreen;
-	byte *_compositeScreen;
+	Graphics::ManagedSurface _backgroundScreen;
+	Graphics::ManagedSurface _compositeScreen;
 	byte *_palette;
 	byte *_buttons[2][2];
 	Buttons _selectedButton = NO_BG_BUTTON;
diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index 74fcf293483..d0c1c79b1eb 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -35,38 +35,37 @@ GraphicsManager::GraphicsManager() {
 GraphicsManager::~GraphicsManager() {
 }
 
-Common::Point GraphicsManager::showOverlay(int height, byte *buf) {
+Common::Point GraphicsManager::showOverlay(int height, Graphics::ManagedSurface &buf) {
 	int overlayY = 400 - height;
 	int overlayX = 0;
 	for (int x = 0; x < 640; x++) {
 		for (int y = overlayY; y < 400; y++) {
-			int index = y * 640 + x;
-			buf[index] = g_engine->_room->_paletteRemaps[2][buf[index]];
+			byte pixel = (byte)buf.getPixel(x, y);
+			buf.setPixel(x, y, g_engine->_room->_paletteRemaps[2][pixel]);
 		}
 	}
 	return Common::Point(overlayX, overlayY);
 }
 
-byte *GraphicsManager::grabBackgroundSlice(byte *buf, int x, int y, int w, int h) {
+byte *GraphicsManager::grabBackgroundSlice(Graphics::ManagedSurface &buf, int x, int y, int w, int h) {
 	byte *bg = new byte[w * h];
 	for (int j = 0; j < w; j++) {
 		for (int i = 0; i < h; i++) {
 			int idx = i * w + j;
 			if (y + i < 400 && x + j < 640) {
-				*(bg + idx) = buf[(y + i) * 640 + (x + j)];
+				*(bg + idx) = (byte)buf.getPixel(x + j, y + i);
 			}
 		}
 	}
 	return bg;
 }
 
-void GraphicsManager::putBackgroundSlice(byte *buf, int x, int y, int w, int h, byte *slice) {
+void GraphicsManager::putBackgroundSlice(Graphics::ManagedSurface &buf, int x, int y, int w, int h, byte *slice) {
 	for (int i = 0; i < w; i++) {
 		for (int j = 0; j < h; j++) {
 			int index = (j * w + i);
 			if (x + i < 640 && y + j < 400) {
-				buf[(y + j) * 640 + (x + i)] = slice[index];
-				// *(byte *)_screen->getBasePtr(x + i, y + j) = slice[index];
+				buf.setPixel(x + i, y + j, slice[index]);
 			}
 		}
 	}
@@ -144,7 +143,7 @@ void GraphicsManager::fadePaletteToTarget(byte *targetPalette, int stepSize) {
 }
 
 void GraphicsManager::clearScreen() {
-	memset(g_engine->_screen->getPixels(), 0, g_engine->_screen->pitch * g_engine->_screen->h);
+	g_engine->_screen->clear(0);
 }
 
 void GraphicsManager::drawColoredText(Graphics::ManagedSurface *screen, const Common::String &text, int x, int y, int w, byte &defaultColor, Graphics::Font *font) {
@@ -172,7 +171,7 @@ void GraphicsManager::drawColoredText(Graphics::ManagedSurface *screen, const Co
 	}
 }
 
-void GraphicsManager::drawColoredText(byte *buf, const Common::String &text, int x, int y, int w, byte &defaultColor, Graphics::Font *font) {
+void GraphicsManager::drawColoredText(Graphics::ManagedSurface &buf, const Common::String &text, int x, int y, int w, byte &defaultColor, Graphics::Font *font) {
 
 	Graphics::Surface tempSurface;
 	Common::Rect r = font->getBoundingBox(text); // Ensure font metrics are loaded before creating surface
@@ -202,17 +201,9 @@ void GraphicsManager::drawColoredText(byte *buf, const Common::String &text, int
 		font->drawString(&tempSurface, segment, currentX, y, w, defaultColor);
 	}
 
-	for(int j = 0; j < tempSurface.h; j++) {
-		for(int i = 0; i < tempSurface.w; i++) {
-			if (y + j < 400 && x + i < 640) {
-				byte pixel = *((byte *)tempSurface.getBasePtr(i, j));
-				if (pixel != 0) { // Assuming 0 is transparent
-					debug("Drawing pixel at (%d, %d) with color %d", x + i, y + j, pixel);
-					buf[(y + j) * 640 + (x + i)] = pixel;
-				}
-			}
-		}
-	}
+	// Use transBlitFrom to blit non-zero pixels
+	buf.transBlitFrom(tempSurface, Common::Point(x, y), 0);
+	tempSurface.free();
 }
 
 void GraphicsManager::drawColoredTexts(Graphics::ManagedSurface *surface, const Common::StringArray &text, int x, int y, int w, int yPadding, Graphics::Font *font) {
@@ -224,7 +215,7 @@ void GraphicsManager::drawColoredTexts(Graphics::ManagedSurface *surface, const
 	}
 }
 
-void GraphicsManager::drawColoredTexts(byte *buf, const Common::StringArray &text, int x, int y, int w, int yPadding, Graphics::Font *font) {
+void GraphicsManager::drawColoredTexts(Graphics::ManagedSurface &buf, const Common::StringArray &text, int x, int y, int w, int yPadding, Graphics::Font *font) {
 	int currentX = x;
 	byte currentColor = 255;
 
diff --git a/engines/pelrock/graphics.h b/engines/pelrock/graphics.h
index fd2de370bd2..6d31b038367 100644
--- a/engines/pelrock/graphics.h
+++ b/engines/pelrock/graphics.h
@@ -26,6 +26,7 @@
 #include "common/scummsys.h"
 
 #include "graphics/font.h"
+#include "graphics/managed_surface.h"
 #include "graphics/screen.h"
 
 namespace Pelrock {
@@ -35,16 +36,16 @@ public:
 	GraphicsManager();
 	~GraphicsManager();
 
-	Common::Point showOverlay(int height, byte *buf);
-	byte *grabBackgroundSlice(byte *buf, int x, int y, int w, int h);
-	void putBackgroundSlice(byte *buf, int x, int y, int w, int h, byte *slice);
+	Common::Point showOverlay(int height, Graphics::ManagedSurface &buf);
+	byte *grabBackgroundSlice(Graphics::ManagedSurface &buf, int x, int y, int w, int h);
+	void putBackgroundSlice(Graphics::ManagedSurface &buf, int x, int y, int w, int h, byte *slice);
 	void fadeToBlack(int stepSize);
 	void fadePaletteToTarget(byte *targetPalette, int stepSize);
 	void clearScreen();
 	void drawColoredText(Graphics::ManagedSurface *screen, const Common::String &text, int x, int y, int w, byte &defaultColor, Graphics::Font *font);
-	void drawColoredText(byte *buf, const Common::String &text, int x, int y, int w, byte &defaultColor, Graphics::Font *font);
+	void drawColoredText(Graphics::ManagedSurface &buf, const Common::String &text, int x, int y, int w, byte &defaultColor, Graphics::Font *font);
 	void drawColoredTexts(Graphics::ManagedSurface *surface, const Common::StringArray &text, int x, int y, int w, int yPadding, Graphics::Font *font);
-	void drawColoredTexts(byte *buf, const Common::StringArray &text, int x, int y, int w, int yPadding, Graphics::Font *font);
+	void drawColoredTexts(Graphics::ManagedSurface &buf, const Common::StringArray &text, int x, int y, int w, int yPadding, Graphics::Font *font);
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 4680e454e7e..9ddad5ec3ca 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -112,7 +112,7 @@ void MenuManager::checkMouseClick(int x, int y) {
 }
 
 void MenuManager::showCredits() {
-	memset(_compositeBuffer, 0, 640 * 400);
+	_compositeBuffer.clear(0);
 	Common::File alfred7;
 	if (!alfred7.open(Common::Path("ALFRED.7"))) {
 		error("Could not open ALFRED.7");
@@ -120,7 +120,7 @@ void MenuManager::showCredits() {
 	}
 
 	alfred7.seek(kCreditsBackgroundOffset, SEEK_SET);
-	alfred7.read(_compositeBuffer, 640 * 400);
+	alfred7.read(_compositeBuffer.getPixels(), 640 * 400);
 	byte *creditsBuf = nullptr;
 	size_t creditsSize = 0;
 	int creditWidth = 240;
@@ -134,11 +134,11 @@ void MenuManager::showCredits() {
 		int x = (i < 34 / 2) ? 39 : 359;
 		int y = 3 + (i % (34 / 2)) * (400 / (34 / 2));
 		extractSingleFrame(decompressedCredits, singleCredit, kCreditsOrder[i], creditWidth, creditHeight);
-		drawSpriteToBuffer(_compositeBuffer, 640, singleCredit, x, y, creditWidth, creditHeight, 255);
+		drawSpriteToBuffer(_compositeBuffer, singleCredit, x, y, creditWidth, creditHeight, 255);
 		delete[] singleCredit;
 	}
 
-	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
+	_screen->blitFrom(_compositeBuffer);
 	delete[] decompressedCredits;
 	delete[] creditsBuf;
 
@@ -188,13 +188,13 @@ void MenuManager::menuLoop() {
 }
 
 void MenuManager::drawScreen() {
-	memcpy(_compositeBuffer, _mainMenu, 640 * 400);
+	_compositeBuffer.blitFrom(_mainMenu);
 	if (showButtons)
 		drawButtons();
 
 	drawInventoryIcons();
 
-	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
+	_screen->blitFrom(_compositeBuffer);
 	byte defaultColor = 255;
 	for (int i = 0; _menuText.size() > i; i++) {
 		g_engine->_graphics->drawColoredText(_screen, _menuText[i], 230, 200 + (i * 10), 200, defaultColor, g_engine->_smallFont);
@@ -211,9 +211,9 @@ void MenuManager::drawInventoryIcons() {
 			continue;
 		InventoryObject item = g_engine->_res->getIconForObject(g_engine->_state->inventoryItems[itemIndex]);
 		Common::Point slot = _inventorySlots[i];
-		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, slot.x, slot.y, 60, 60, 1);
+		drawSpriteToBuffer(_compositeBuffer, item.iconData, slot.x, slot.y, 60, 60, 1);
 		if (debugIcons) {
-			drawRect(_compositeBuffer, slot.x, slot.y, 60, 60, 13);
+			drawRect(&_compositeBuffer, slot.x, slot.y, 60, 60, 13);
 			drawText(_compositeBuffer, g_engine->_smallFont, Common::String::format("ID %d", g_engine->_state->inventoryItems[itemIndex]), slot.x + 2, slot.y + 2, 640, 13);
 		}
 	}
@@ -228,8 +228,8 @@ void MenuManager::loadMenu() {
 		return;
 	}
 
-	_compositeBuffer = new byte[640 * 400];
-	_mainMenu = new byte[640 * 400];
+	_compositeBuffer.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
+	_mainMenu.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 	loadMenuTexts();
 	if (!alternateMenu) {
 		alfred7.seek(kSettingsPaletteOffset, SEEK_SET);
@@ -242,7 +242,7 @@ void MenuManager::loadMenu() {
 
 		uint32 curPos = 0;
 		alfred7.seek(2405266, SEEK_SET);
-		alfred7.read(_mainMenu, 65536);
+		alfred7.read(_mainMenu.getPixels(), 65536);
 
 		curPos += 65536;
 
@@ -251,28 +251,28 @@ void MenuManager::loadMenu() {
 		byte *decompressedPart1 = nullptr;
 		size_t decompressedSize = rleDecompress(compressedPart1, 29418, 0, 0, &decompressedPart1, true);
 
-		memcpy(_mainMenu + curPos, decompressedPart1, decompressedSize);
+		memcpy((byte *)_mainMenu.getPixels() + curPos, decompressedPart1, decompressedSize);
 		curPos += decompressedSize;
 
 		delete[] compressedPart1;
 		delete[] decompressedPart1;
 		alfred7.seek(2500220, SEEK_SET);
-		alfred7.read(_mainMenu + curPos, 32768);
+		alfred7.read((byte *)_mainMenu.getPixels() + curPos, 32768);
 		curPos += 32768;
 		byte *compressedPart2 = new byte[30288];
 		alfred7.read(compressedPart2, 30288);
 		byte *decompressedPart2 = nullptr;
 		decompressedSize = rleDecompress(compressedPart2, 30288, 0, 0, &decompressedPart2, true);
 
-		memcpy(_mainMenu + curPos, decompressedPart2, decompressedSize);
+		memcpy((byte *)_mainMenu.getPixels() + curPos, decompressedPart2, decompressedSize);
 		curPos += decompressedSize;
 		debug("Settings menu size loaded: %d, with last block %d", curPos, curPos + 92160);
 		delete[] compressedPart2;
 		delete[] decompressedPart2;
 		alfred7.seek(2563266, SEEK_SET);
-		alfred7.read(_mainMenu + curPos, 92160);
+		alfred7.read((byte *)_mainMenu.getPixels() + curPos, 92160);
 	} else {
-		_mainMenu = new byte[640 * 400];
+		_mainMenu.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 
 		alfred7.seek(kAlternateSettingsPaletteOffset, SEEK_SET);
 		alfred7.read(_mainMenuPalette, 768);
@@ -282,7 +282,7 @@ void MenuManager::loadMenu() {
 			_mainMenuPalette[i * 3 + 2] = _mainMenuPalette[i * 3 + 2] << 2;
 		}
 
-		g_engine->_res->mergeRleBlocks(&alfred7, kAlternateSettingsMenuOffset, 8, _mainMenu);
+		g_engine->_res->mergeRleBlocks(&alfred7, kAlternateSettingsMenuOffset, 8, (byte *)_mainMenu.getPixels());
 	}
 
 	readButton(alfred7, 3193376, _saveButtons, _saveGameRect);
@@ -350,39 +350,39 @@ void MenuManager::drawButtons() {
 		button = isButtonClicked(_events->_mouseX, _events->_mouseY);
 	}
 	byte *buf = button == QUESTION_MARK_BUTTON ? _questionMark[1] : _questionMark[0];
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, _questionMarkRect.left, _questionMarkRect.top, _questionMarkRect.width(), _questionMarkRect.height(), kTransparentColor);
+	drawSpriteToBuffer(_compositeBuffer, buf, _questionMarkRect.left, _questionMarkRect.top, _questionMarkRect.width(), _questionMarkRect.height(), kTransparentColor);
 
 	buf = button == INVENTORY_PREV_BUTTON ? _inventoryLeftArrow[1] : _inventoryLeftArrow[0];
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, _invLeft.left, _invLeft.top, _invLeft.width(), _invLeft.height(), kTransparentColor);
+	drawSpriteToBuffer(_compositeBuffer, buf, _invLeft.left, _invLeft.top, _invLeft.width(), _invLeft.height(), kTransparentColor);
 
 	buf = button == INVENTORY_NEXT_BUTTON ? _inventoryRightArrow[1] : _inventoryRightArrow[0];
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, _invRight.left, _invRight.top, _invRight.width(), _invRight.height(), kTransparentColor);
+	drawSpriteToBuffer(_compositeBuffer, buf, _invRight.left, _invRight.top, _invRight.width(), _invRight.height(), kTransparentColor);
 
 	buf = button == SAVE_GAME_BUTTON ? _saveButtons[1] : _saveButtons[0];
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, _saveGameRect.left, _saveGameRect.top, _saveGameRect.width(), _saveGameRect.height(), kTransparentColor);
+	drawSpriteToBuffer(_compositeBuffer, buf, _saveGameRect.left, _saveGameRect.top, _saveGameRect.width(), _saveGameRect.height(), kTransparentColor);
 
 	buf = button == LOAD_GAME_BUTTON ? _loadButtons[1] : _loadButtons[0];
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, _loadGameRect.left, _loadGameRect.top, _loadGameRect.width(), _loadGameRect.height(), kTransparentColor);
+	drawSpriteToBuffer(_compositeBuffer, buf, _loadGameRect.left, _loadGameRect.top, _loadGameRect.width(), _loadGameRect.height(), kTransparentColor);
 
 	buf = button == LOAD_GAME_BUTTON ? _loadButtons[1] : _loadButtons[0];
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, _loadGameRect.left, _loadGameRect.top, _loadGameRect.width(), _loadGameRect.height(), kTransparentColor);
+	drawSpriteToBuffer(_compositeBuffer, buf, _loadGameRect.left, _loadGameRect.top, _loadGameRect.width(), _loadGameRect.height(), kTransparentColor);
 
 	buf = button == SOUNDS_BUTTON ? _soundsButtons[1] : _soundsButtons[0];
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, _soundsRect.left, _soundsRect.top, _soundsRect.width(), _soundsRect.height(), kTransparentColor);
+	drawSpriteToBuffer(_compositeBuffer, buf, _soundsRect.left, _soundsRect.top, _soundsRect.width(), _soundsRect.height(), kTransparentColor);
 
 	buf = button == EXIT_MENU_BUTTON ? _exitToDosButtons[1] : _exitToDosButtons[0];
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, _exitToDosRect.left, _exitToDosRect.top, _exitToDosRect.width(), _exitToDosRect.height(), kTransparentColor);
+	drawSpriteToBuffer(_compositeBuffer, buf, _exitToDosRect.left, _exitToDosRect.top, _exitToDosRect.width(), _exitToDosRect.height(), kTransparentColor);
 
 	buf = button == SAVEGAME_PREV_BUTTON ? _savesUpArrows[1] : _savesUpArrows[0];
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, _savesUp.left, _savesUp.top, _savesUp.width(), _savesUp.height(), kTransparentColor);
+	drawSpriteToBuffer(_compositeBuffer, buf, _savesUp.left, _savesUp.top, _savesUp.width(), _savesUp.height(), kTransparentColor);
 
 	buf = button == SAVEGAME_NEXT_BUTTON ? _savesDownArrows[1] : _savesDownArrows[0];
-	drawSpriteToBuffer(_compositeBuffer, 640, buf, _savesDown.left, _savesDown.top, _savesDown.width(), _savesDown.height(), kTransparentColor);
+	drawSpriteToBuffer(_compositeBuffer, buf, _savesDown.left, _savesDown.top, _savesDown.width(), _savesDown.height(), kTransparentColor);
 }
 
 Pelrock::MenuManager::~MenuManager() {
-	delete[] _mainMenu;
-	delete[] _compositeBuffer;
+	_mainMenu.free();
+	_compositeBuffer.free();
 	delete[] _questionMark[0];
 	delete[] _questionMark[1];
 	delete[] _inventoryLeftArrow[0];
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 658c9c9243d..36bfe819821 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -22,6 +22,7 @@
 #define PELROCK_MENU_H
 
 #include "graphics/font.h"
+#include "graphics/managed_surface.h"
 #include "graphics/screen.h"
 
 #include "pelrock/events.h"
@@ -225,8 +226,8 @@ private:
 	PelrockEventManager *_events = nullptr;
 	ResourceManager *_res = nullptr;
 	SoundManager *_sound = nullptr;
-	byte *_mainMenu = nullptr;
-	byte *_compositeBuffer = nullptr;
+	Graphics::ManagedSurface _mainMenu;
+	Graphics::ManagedSurface _compositeBuffer;
 
 	Common::Rect _saveGameRect = Common::Rect(Common::Point(132, 186), 81, 34);
 	byte *_saveButtons[2] = {nullptr};
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index d8ecb49cf1b..f23cc843ea6 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -58,10 +58,6 @@ PelrockEngine::PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc) :
 }
 
 PelrockEngine::~PelrockEngine() {
-	if (_compositeBuffer)
-		delete[] _compositeBuffer;
-	if (_currentBackground)
-		delete[] _currentBackground;
 	delete _largeFont;
 	delete _smallFont;
 	delete _doubleSmallFont;
@@ -147,8 +143,8 @@ void PelrockEngine::init() {
 	_menu->loadMenu();
 
 	calculateScalingMasks();
-	_compositeBuffer = new byte[640 * 400];
-	_currentBackground = new byte[640 * 400];
+	_compositeBuffer.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
+	_currentBackground.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 
 	changeCursor(DEFAULT);
 	CursorMan.showMouse(true);
@@ -266,14 +262,14 @@ void PelrockEngine::travelToEgypt() {
 
 	_sound->playMusicTrack(26, 1);
 	byte *palette = new byte[768];
-	if (_bgScreen == nullptr) {
-		_bgScreen = new byte[640 * 400];
+	if (!_bgScreen.getPixels()) {
+		_bgScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 	}
-	_res->getExtraScreen(6, _bgScreen, palette);
+	_res->getExtraScreen(6, (byte *)_bgScreen.getPixels(), palette);
 	CursorMan.showMouse(false);
 	g_system->getPaletteManager()->setPalette(palette, 0, 256);
 
-	memcpy(_screen->getPixels(), _bgScreen, 640 * 400);
+	_screen->blitFrom(_bgScreen);
 	int frameCount = 0;
 	while (!shouldQuit() && frameCount < 96) {
 		_events->pollEvent();
@@ -295,10 +291,8 @@ void PelrockEngine::travelToEgypt() {
 	_graphics->clearScreen();
 
 	g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
-	free(_bgScreen);
-	_bgScreen = nullptr;
+	_bgScreen.free();
 	CursorMan.showMouse(true);
-	delete[] _bgScreen;
 	delete[] palette;
 	_screen->markAllDirty();
 	_screen->update();
@@ -534,9 +528,12 @@ void PelrockEngine::reflectionEffect(byte *buf, int x, int y, int width, int hei
 			byte pixel = buf[(height - 1 - row) * width + col]; // Read from bottom up for mirror
 			// Only reflect pixels 0-15 (high nibble must be 0)
 			if (pixel != 255 && (pixel & 0xF0) == 0) {
-				byte bgPixel = _compositeBuffer[reflectY * 640 + x + col];
-				if (bgPixel >= 223 && bgPixel < 228) { // Is water (0xDF-0xE3)
-					_compositeBuffer[reflectY * 640 + x + col] = _room->_paletteRemaps[4][pixel];
+				int px = x + col;
+				if (px >= 0 && px < 640) {
+					byte bgPixel = (byte)_compositeBuffer.getPixel(px, reflectY);
+					if (bgPixel >= 223 && bgPixel < 228) { // Is water (0xDF-0xE3)
+						_compositeBuffer.setPixel(px, reflectY, _room->_paletteRemaps[4][pixel]);
+					}
 				}
 			}
 		}
@@ -640,7 +637,7 @@ void PelrockEngine::checkMouse() {
 
 void PelrockEngine::copyBackgroundToBuffer() {
 	// copy background to buffer
-	memcpy(_compositeBuffer, _currentBackground, 640 * 400);
+	_compositeBuffer.blitFrom(_currentBackground);
 }
 
 // Calculate Alfred's z-order based on Y position
@@ -685,7 +682,7 @@ void PelrockEngine::updateAnimations() {
 }
 
 void PelrockEngine::presentFrame() {
-	memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
+	_screen->blitFrom(_compositeBuffer);
 	paintDebugLayer();
 	_screen->markAllDirty();
 }
@@ -749,7 +746,7 @@ void PelrockEngine::paintDebugLayer() {
 	drawPos(_screen, _curWalkTarget.x, _curWalkTarget.y, 100);
 
 	if (showShadows) {
-		memcpy(_screen->getPixels(), _room->_pixelsShadows, 640 * 400);
+		_screen->copyRectToSurface(_room->_pixelsShadows, 640, 0, 0, 640, 400);
 	}
 	_smallFont->drawString(_screen, Common::String::format("Room number: %d", _room->_currentRoomNumber), 0, 4, 640, 13);
 	_smallFont->drawString(_screen, Common::String::format("Alfred pos: %d, %d (%d)", _alfredState.x, _alfredState.y, _alfredState.y - kAlfredFrameHeight), 0, 18, 640, 13);
@@ -777,18 +774,18 @@ void PelrockEngine::placeStickersSecondPass() {
 }
 
 void PelrockEngine::placeSticker(Sticker sticker) {
-	for (int y = 0; y < sticker.h; y++) {
-		for (int x = 0; x < sticker.w; x++) {
-			byte pixel = sticker.stickerData[y * sticker.w + x];
-			// if (pixel != 0) {
-			int bgX = sticker.x + x;
-			int bgY = sticker.y + y;
-			if (bgX >= 0 && bgX < 640 && bgY >= 0 && bgY < 400) {
-				_compositeBuffer[bgY * 640 + bgX] = pixel;
-			}
-			// }
-		}
-	}
+	// Wrap sticker data as a surface and blit (no transparency - all pixels copied)
+	Graphics::Surface stickerSurf;
+	stickerSurf.init(sticker.w, sticker.h, sticker.w, sticker.stickerData, Graphics::PixelFormat::createFormatCLUT8());
+	// Clip to screen bounds
+	Common::Rect destRect(sticker.x, sticker.y, sticker.x + sticker.w, sticker.y + sticker.h);
+	Common::Rect screenRect(0, 0, 640, 400);
+	destRect.clip(screenRect);
+	if (destRect.isEmpty())
+		return;
+	Common::Rect srcRect(destRect.left - sticker.x, destRect.top - sticker.y,
+						 destRect.right - sticker.x, destRect.bottom - sticker.y);
+	_compositeBuffer.blitFrom(stickerSurf, srcRect, Common::Point(destRect.left, destRect.top));
 }
 
 void PelrockEngine::renderOverlay(int overlayMode) {
@@ -1018,7 +1015,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			}
 			if (_alfredState.animState == ALFRED_WALKING) { // in case it changed to idle above
 				debug("Drawing crawl frame %d for direction %d", _alfredState.curFrame, _alfredState.direction);
-				drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCrawlFrames[_alfredState.direction][_alfredState.curFrame], _alfredState.x, _alfredState.y - 55, 130, 55, 255);
+				drawSpriteToBuffer(_compositeBuffer, _res->alfredCrawlFrames[_alfredState.direction][_alfredState.curFrame], _alfredState.x, _alfredState.y - 55, 130, 55, 255);
 				_alfredState.curFrame++;
 			}
 		} else {
@@ -1078,7 +1075,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			drawAlfred(frame);
 		} else {
 			// Scale special anim frame to Alfred size before drawing
-			drawSpriteToBuffer(_compositeBuffer, 640, frame, _alfredState.x, _alfredState.y - _res->_currentSpecialAnim->h, _res->_currentSpecialAnim->w, _res->_currentSpecialAnim->h, 255);
+			drawSpriteToBuffer(_compositeBuffer, frame, _alfredState.x, _alfredState.y - _res->_currentSpecialAnim->h, _res->_currentSpecialAnim->w, _res->_currentSpecialAnim->h, 255);
 		}
 		debug("Playing special anim frame %d/%d, speed %d", _res->_currentSpecialAnim->curFrame, _res->_currentSpecialAnim->numFrames, _res->_currentSpecialAnim->speed);
 		if (_chrono->getFrameCount() % _res->_currentSpecialAnim->speed == 0) {
@@ -1134,7 +1131,7 @@ void PelrockEngine::exitTriggers(Pelrock::Exit *exit) {
 
 void PelrockEngine::drawIdleFrame() {
 	if (_room->_currentRoomNumber == 55) {
-		drawSpriteToBuffer(_compositeBuffer, 640, _res->alfredCrawlFrames[_alfredState.direction][0], _alfredState.x, _alfredState.y - 55, 130, 55, 255);
+		drawSpriteToBuffer(_compositeBuffer, _res->alfredCrawlFrames[_alfredState.direction][0], _alfredState.x, _alfredState.y - 55, 130, 55, 255);
 	} else {
 		drawAlfred(_res->alfredIdle[_alfredState.direction]);
 	}
@@ -1279,7 +1276,7 @@ void PelrockEngine::drawAlfred(byte *buf) {
 		}
 	}
 
-	drawSpriteToBuffer(_compositeBuffer, 640, _alfredSprite, _alfredState.x, _alfredState.y - finalHeight, finalWidth, finalHeight, 255);
+	drawSpriteToBuffer(_compositeBuffer, _alfredSprite, _alfredState.x, _alfredState.y - finalHeight, finalWidth, finalHeight, 255);
 
 	// Water reflection (rooms 25 and 45 only)
 	if ((_room->_currentRoomNumber == 25 || _room->_currentRoomNumber == 45) && _alfredState.y >= 299) {
@@ -1343,7 +1340,7 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 		debug("Warning: curFrame %d exceeds nframes %d for sprite %d anim %d", curFrame, animData.nframes, sprite->index, sprite->curAnimIndex);
 		curFrame = 0;
 	}
-	drawSpriteToBuffer(_compositeBuffer, 640, animData.animData[curFrame], x, y, w, h, 255);
+	drawSpriteToBuffer(_compositeBuffer, animData.animData[curFrame], x, y, w, h, 255);
 
 	// Original in the game: increment FIRST, then check (not check-then-increment)
 	animData.elpapsedFrames++;
@@ -1572,7 +1569,7 @@ Common::Rect getActionArea(int x, int y) {
 
 void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 
-	drawSpriteToBuffer(_compositeBuffer, 640, _res->_popUpBalloon + (curFrame * kBalloonHeight * kBalloonWidth), posx, posy, kBalloonWidth, kBalloonHeight, 255);
+	drawSpriteToBuffer(_compositeBuffer, _res->_popUpBalloon + (curFrame * kBalloonHeight * kBalloonWidth), posx, posy, kBalloonWidth, kBalloonHeight, 255);
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
 
 	VerbIcon icon = isActionUnder(_events->_mouseX, _events->_mouseY);
@@ -1584,7 +1581,7 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 			continue;
 		}
 		Common::Point p = getPositionInBalloonForIndex(i, posx, posy);
-		drawSpriteToBuffer(_compositeBuffer, 640, _res->_verbIcons[actions[i]], p.x, p.y, kVerbIconWidth, kVerbIconHeight, 1);
+		drawSpriteToBuffer(_compositeBuffer, _res->_verbIcons[actions[i]], p.x, p.y, kVerbIconWidth, kVerbIconHeight, 1);
 	}
 
 	Common::Rect actionArea = getActionArea(posx, posy);
@@ -1604,7 +1601,7 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	if (_state->selectedInventoryItem >= 0 && !_state->inventoryItems.empty()) {
 		if (icon != ITEM || !shouldBlink) {
 			Common::Point p = getPositionInBalloonForIndex(actions.size(), posx, posy);
-			drawSpriteToBuffer(_compositeBuffer, 640, _res->getIconForObject(_state->selectedInventoryItem).iconData, p.x, p.y, kVerbIconWidth, kVerbIconHeight, 1);
+			drawSpriteToBuffer(_compositeBuffer, _res->getIconForObject(_state->selectedInventoryItem).iconData, p.x, p.y, kVerbIconWidth, kVerbIconHeight, 1);
 		}
 	}
 
@@ -1648,7 +1645,7 @@ void PelrockEngine::animateTalkingNPC(Sprite *animSet) {
 	}
 	byte *frame = index ? animHeader->animB[curFrame] : animHeader->animA[curFrame];
 
-	drawSpriteToBuffer(_compositeBuffer, 640, frame, x, y, w, h, 255);
+	drawSpriteToBuffer(_compositeBuffer, frame, x, y, w, h, 255);
 }
 
 Common::Point getPositionInOverlayForIndex(uint index) {
@@ -1662,14 +1659,14 @@ void PelrockEngine::pickupIconFlash() {
 	uint invSize = _state->inventoryItems.size();
 	for (int i = 0; i < invSize; i++) {
 		Common::Point p = getPositionInOverlayForIndex(i);
-		drawSpriteToBuffer(_compositeBuffer, 640, _res->getIconForObject(_state->inventoryItems[i]).iconData, p.x, p.y, 60, 60, 1);
+		drawSpriteToBuffer(_compositeBuffer, _res->getIconForObject(_state->inventoryItems[i]).iconData, p.x, p.y, 60, 60, 1);
 	}
 
 	InventoryObject item = _res->getIconForObject(_newItem);
 	if (_chrono->getFrameCount() % kIconBlinkPeriod == 0) {
 		Common::Point p = getPositionInOverlayForIndex(invSize);
 		debug("Drawing pickup icon for item %d at inventory index %d, position %d,%d", _newItem, invSize, p.x, p.y);
-		drawSpriteToBuffer(_compositeBuffer, 640, item.iconData, p.x, p.y, 60, 60, 1);
+		drawSpriteToBuffer(_compositeBuffer, item.iconData, p.x, p.y, 60, 60, 1);
 	}
 }
 
@@ -1685,15 +1682,15 @@ void PelrockEngine::showInventoryOverlay() {
 		if (i == _inventoryOverlayState.flashingIconIndex && _chrono->getFrameCount() % kIconBlinkPeriod == 0) {
 			continue;
 		}
-		drawSpriteToBuffer(_compositeBuffer, 640, _res->getIconForObject(_state->inventoryItems[i]).iconData, p.x, p.y, 60, 60, 1);
+		drawSpriteToBuffer(_compositeBuffer, _res->getIconForObject(_state->inventoryItems[i]).iconData, p.x, p.y, 60, 60, 1);
 	}
 
 	// draw arrows if there are more items to show in either direction
 	if (_inventoryOverlayState.invStartingPos > 0) {
-		drawSpriteToBuffer(_compositeBuffer, 640, _inventoryOverlayState.arrows[0], 0, 340, 20, 60, 255);
+		drawSpriteToBuffer(_compositeBuffer, _inventoryOverlayState.arrows[0], 0, 340, 20, 60, 255);
 	}
 	if (firstItem + kInventoryPageSize < (int)invSize) {
-		drawSpriteToBuffer(_compositeBuffer, 640, _inventoryOverlayState.arrows[1], 620, 340, 20, 60, 255);
+		drawSpriteToBuffer(_compositeBuffer, _inventoryOverlayState.arrows[1], 620, 340, 20, 60, 255);
 	}
 }
 
@@ -1815,7 +1812,7 @@ void PelrockEngine::computerLoop() {
 }
 
 void PelrockEngine::extraScreenLoop() {
-	memcpy(_screen->getPixels(), _bgScreen, 640 * 400);
+	_screen->blitFrom(_bgScreen);
 	while (!shouldQuit()) {
 		_events->pollEvent();
 
@@ -1829,8 +1826,7 @@ void PelrockEngine::extraScreenLoop() {
 	}
 
 	g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
-	free(_bgScreen);
-	_bgScreen = nullptr;
+	_bgScreen.free();
 }
 
 void PelrockEngine::walkLoop(int16 x, int16 y, AlfredDirection direction) {
@@ -2028,11 +2024,8 @@ void PelrockEngine::setScreen(int roomNumber) {
 	byte *palette = new byte[256 * 3];
 	_room->getPalette(&roomFile, roomOffset, palette);
 
-	if (_currentBackground != nullptr) {
-		delete[] _currentBackground;
-	}
-	_currentBackground = new byte[640 * 400];
-	_room->getBackground(&roomFile, roomOffset, _currentBackground);
+	_currentBackground.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
+	_room->getBackground(&roomFile, roomOffset, (byte *)_currentBackground.getPixels());
 
 	_screen->clear();
 
@@ -2071,16 +2064,15 @@ void PelrockEngine::setScreenAndPrepare(int roomNumber, AlfredDirection dir) {
 void PelrockEngine::loadExtraScreenAndPresent(int screenIndex) {
 
 	byte *palette = new byte[768];
-	if (_bgScreen == nullptr) {
-		_bgScreen = new byte[640 * 400];
+	if (!_bgScreen.getPixels()) {
+		_bgScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 	}
-	_res->getExtraScreen(screenIndex, _bgScreen, palette);
+	_res->getExtraScreen(screenIndex, (byte *)_bgScreen.getPixels(), palette);
 	CursorMan.showMouse(false);
 	_graphics->clearScreen();
 	g_system->getPaletteManager()->setPalette(palette, 0, 256);
 	extraScreenLoop();
 	CursorMan.showMouse(true);
-	delete[] _bgScreen;
 	delete[] palette;
 	_screen->markAllDirty();
 	_screen->update();
@@ -2363,12 +2355,8 @@ void PelrockEngine::pyramidCollapse() {
 		static const int srcX = 240, srcY = 145;
 		// static const int dstX = 510, dstY = 33;
 		static const int copyW = 99, copyH = 45;
-		for (int row = 0; row < copyH; row++) {
-			memcpy(
-				_currentBackground + (srcY + row) * 640 + srcX,
-				_compositeBuffer + (srcY + row) * 640 + srcX,
-				copyW);
-		}
+		Common::Rect copyRect(srcX, srcY, srcX + copyW, srcY + copyH);
+		_currentBackground.blitFrom(_compositeBuffer, copyRect, Common::Point(srcX, srcY));
 	}
 	_room->findSpriteByIndex(2)->zOrder = -1;
 
@@ -2450,10 +2438,10 @@ void PelrockEngine::pyramidCollapse() {
 
 void PelrockEngine::endingScene() {
 	byte *palette = new byte[768];
-	if (_bgScreen == nullptr) {
-		_bgScreen = new byte[640 * 400];
+	if (!_bgScreen.getPixels()) {
+		_bgScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 	}
-	_res->getExtraScreen(14, _bgScreen, palette);
+	_res->getExtraScreen(14, (byte *)_bgScreen.getPixels(), palette);
 	CursorMan.showMouse(false);
 	_graphics->clearScreen();
 	g_system->getPaletteManager()->setPalette(palette, 0, 256);
@@ -2536,13 +2524,13 @@ void PelrockEngine::endingScene() {
 		_chrono->updateChrono();
 
 		if (_chrono->_gameTick) {
-			memcpy(_compositeBuffer, _bgScreen, 640 * 400);
+			_compositeBuffer.blitFrom(_bgScreen);
 
 			for (Sprite *sprite : sprites) {
 				drawNextFrame(sprite);
 			}
 
-			memcpy(_screen->getPixels(), _compositeBuffer, 640 * 400);
+			_screen->blitFrom(_compositeBuffer);
 			// Title text visible frames 21–149 (original: frame > 0x14 && frame < 0x96)
 			if (ticks > 20 && ticks < 150) {
 				drawText(_largeFont, "ALFRED PELROCK", 0, y1, 640, 255);
@@ -2562,10 +2550,9 @@ void PelrockEngine::endingScene() {
 		_screen->update();
 	}
 
-	memset(_screen->getPixels(), 0, 640 * 400);
+	_screen->clear(0);
 	g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
-	delete[] _bgScreen;
-	_bgScreen = nullptr;
+	_bgScreen.free();
 
 	CursorMan.showMouse(true);
 	delete[] palette;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 44020602b92..6df37af5dc1 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -33,6 +33,7 @@
 #include "common/util.h"
 #include "engines/engine.h"
 #include "engines/savestate.h"
+#include "graphics/managed_surface.h"
 #include "graphics/screen.h"
 #include "image/png.h"
 
@@ -136,8 +137,8 @@ private:
 	int _currentStep = 0;
 	PathContext _currentContext;
 
-	byte *_currentBackground = nullptr; // Clean background - NEVER modified
-	byte *_bgScreen = nullptr;
+	Graphics::ManagedSurface _currentBackground; // Clean background - NEVER modified
+	Graphics::ManagedSurface _bgScreen;
 
 	ActionPopupState _actionPopupState;
 	InventoryOverlayState _inventoryOverlayState;
@@ -174,7 +175,7 @@ public:
 	DialogManager *_dialog = nullptr;
 	AlfredState _alfredState;
 	ShakeEffectState _shakeEffectState;
-	byte *_compositeBuffer = nullptr; // Working composition buffer
+	Graphics::ManagedSurface _compositeBuffer; // Working composition buffer
 
 	bool _mouseDisabled = false;
 
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 58a53b1c6c3..8032112eb8c 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -114,6 +114,32 @@ void drawText(Graphics::Font *font, Common::String text, int x, int y, int w, by
 	font->drawString(g_engine->_screen, text.c_str(), x, y, w, color, Graphics::kTextAlignCenter);
 }
 
+void drawText(Graphics::ManagedSurface &dest, Graphics::Font *font, Common::String text, int x, int y, int w, byte color, Graphics::TextAlign align) {
+	Common::Rect rect = font->getBoundingBox(text.c_str());
+	int bboxW = rect.width();
+	int bboxH = rect.height();
+
+	Graphics::Surface surface;
+	surface.create(bboxW, bboxH, Graphics::PixelFormat::createFormatCLUT8());
+	surface.fillRect(Common::Rect(0, 0, bboxW, bboxH), 255);
+	if (x + bboxW > 640) {
+		x = 640 - bboxW - 2;
+	}
+	if (y + bboxH > 400) {
+		y = 400 - bboxH - 2;
+	}
+	if (x < 0) {
+		x = 0;
+	}
+	if (y < 0) {
+		y = 0;
+	}
+
+	font->drawString(&surface, text.c_str(), 0, 0, bboxW, color, align);
+	dest.transBlitFrom(surface, Common::Point(x, y), 255);
+	surface.free();
+}
+
 size_t rleDecompress(
 	const uint8_t *input,
 	size_t inputSize,
@@ -268,6 +294,13 @@ void drawSpriteToBuffer(byte *buffer, int bufferWidth, byte *sprite, int x, int
 	}
 }
 
+// ManagedSurface overload: wraps sprite data in a Surface and uses transBlitFrom
+void drawSpriteToBuffer(Graphics::ManagedSurface &dest, byte *sprite, int x, int y, int width, int height, int transparentColor) {
+	Graphics::Surface spriteSurf;
+	spriteSurf.init(width, height, width, sprite, Graphics::PixelFormat::createFormatCLUT8());
+	dest.transBlitFrom(spriteSurf, Common::Point(x, y), transparentColor);
+}
+
 void blitSurfaceToBuffer(Graphics::Surface *surface, byte *buffer, int bufferWidth, int bufferHeight, int destX, int destY) {
 	for (int y = 0; y < surface->h; y++) {
 		for (int x = 0; x < surface->w; x++) {
@@ -393,4 +426,22 @@ void drawPaletteSquares(byte *screenBuffer, byte *palette) {
 	}
 }
 
+void drawPaletteSquares(Graphics::ManagedSurface &dest, byte *palette) {
+	const int squareSize = 6;
+	const int colorsPerRow = 16;
+	const int startX = 10;
+	const int startY = 10;
+	const int spacing = 1;
+
+	for (int colorIndex = 0; colorIndex < 256; colorIndex++) {
+		int row = colorIndex / colorsPerRow;
+		int col = colorIndex % colorsPerRow;
+
+		int x = startX + col * (squareSize + spacing);
+		int y = startY + row * (squareSize + spacing);
+
+		dest.fillRect(Common::Rect(x, y, x + squareSize, y + squareSize), colorIndex);
+	}
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index 98305b9319c..1aedf5a2b4f 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -36,12 +36,14 @@ size_t rleDecompress(const uint8_t *data, size_t data_size, uint32_t offset, uin
 void readUntilBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *&buffer, size_t &outSize);
 void rleDecompressSingleBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *&buffer, size_t &outSize);
 void drawSpriteToBuffer(byte *buffer, int bufferWidth, byte *sprite, int x, int y, int width, int height, int transparentColor);
+void drawSpriteToBuffer(Graphics::ManagedSurface &dest, byte *sprite, int x, int y, int width, int height, int transparentColor);
 void blitSurfaceToBuffer(Graphics::Surface *surface, byte *buffer, int bufferWidth, int bufferHeight, int destX, int destY);
 void extractSingleFrame(byte *source, byte *dest, int frameIndex, int frameWidth, int frameHeight);
 void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byte color);
 void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color);
 void drawRect(byte *screenBuffer, int x, int y, int w, int h, byte color);
 void drawText(byte *screenBuffer, Graphics::Font *font, Common::String text, int x, int y, int w, byte color, Graphics::TextAlign align = Graphics::kTextAlignLeft);
+void drawText(Graphics::ManagedSurface &dest, Graphics::Font *font, Common::String text, int x, int y, int w, byte color, Graphics::TextAlign align = Graphics::kTextAlignLeft);
 void drawText(Graphics::Font *font, Common::String text, int x, int y, int w, byte color);
 Common::String joinStrings(const Common::Array<Common::String> &strings, const Common::String &separator);
 void drawPos(Graphics::ManagedSurface *surface, int x, int y, byte color);
@@ -50,6 +52,7 @@ void changeGameSpeed(Common::Event e);
 Common::StringArray arrayOf(Common::String str);
 void invertSprite(byte *data, int w, int h);
 void drawPaletteSquares(byte *screenBuffer, byte *palette);
+void drawPaletteSquares(Graphics::ManagedSurface &dest, byte *palette);
 
 static const int special_chars[] = {
 	168, // inverted ?


Commit: 133fe537c9e95f51088136ba0beda3ee22cf1cdf
    https://github.com/scummvm/scummvm/commit/133fe537c9e95f51088136ba0beda3ee22cf1cdf
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:23+02:00

Commit Message:
PELROCK: Fixes original bug in statue

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 6110d1562c1..5bd86dea95c 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -320,9 +320,12 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		// skip to root after the next one
 		_state->setCurrentRoot(room, rootIndex + 2, 0);
 		break;
-	case 267:
+	case 267: {
 		_state->setCurrentRoot(7, 2, 0);
+		addInventoryItem(8);
+		animateStatuePaletteFade(true);
 		break;
+	}
 	case 272:
 		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
@@ -1122,8 +1125,6 @@ void PelrockEngine::useAmuletWithStatue(int inventoryObject, HotSpot *hotspot) {
 		animateStatuePaletteFade(false);
 		walkAndAction(statueHotspot, TALK);
 		waitForActionEnd();
-		animateStatuePaletteFade(true);
-		addInventoryItem(8);
 	}
 }
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index f23cc843ea6..bec290f5559 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -2101,6 +2101,10 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			loadExtraScreenAndPresent(4);
 		}
 		break;
+	case 7: { // not in the original but it was clearly a bug
+		_dialog->_goodbyeDisabled = true;
+		break;
+	}
 	case 15:
 		if (_state->getFlag(FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ)) {
 			_state->setFlag(FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ, false);


Commit: d94c6dc3790f5a7a738a81f919a3e10ceb4bc2ce
    https://github.com/scummvm/scummvm/commit/d94c6dc3790f5a7a738a81f919a3e10ceb4bc2ce
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:23+02:00

Commit Message:
PELROCK: Fixes intro timing

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/video/video.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 5bd86dea95c..ca9761466d4 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -592,6 +592,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 			g_system->delayMillis(10);
 		}
 		_room->disableSprite(0);
+		_state->setCurrentRoot(room, 2, 0);
 	} break;
 	case 313:
 		_state->setCurrentRoot(room, rootIndex + 1, 0);
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index 2dce77e9e56..9c7243def1b 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -61,88 +61,106 @@ void VideoManager::playIntro() {
 
 	_videoSurface.fillRect(Common::Rect(0, 0, 640, 400), 0);
 	_textSurface.fillRect(Common::Rect(0, 0, 640, 400), 255);
-	for (int sequence = 0; sequence < 1; sequence++) {
-		uint16 frameCounter = 0;
-		bool videoExitFlag = false;
 
-		while (!videoExitFlag && !g_engine->shouldQuit() && _events->_lastKeyEvent != Common::KEYCODE_ESCAPE) {
-			_chrono->updateChrono();
-			_events->pollEvent();
+	uint16 frameCounter = 0;
+	bool videoExitFlag = false;
+
+	while (!videoExitFlag && !g_engine->shouldQuit() && _events->_lastKeyEvent != Common::KEYCODE_ESCAPE) {
+		_events->pollEvent();
+
+		ChunkHeader chunk;
+		readChunk(videoFile, chunk);
+		debug("Read chunk type %d at frame %d", chunk.chunkType, frameCounter);
+
+		switch (chunk.chunkType) {
+		case 1:
+		case 2: {
+			// Visual frame: wait for chrono timing before presenting
+			// Fix Bug 2/3: only visual frames (types 1/2) participate in chrono gating
 			Subtitle *subtitle = getSubtitleForFrame(frameCounter);
 			int frameSkip = subtitle != nullptr ? 4 : 2;
-			if (_chrono->_gameTick && _chrono->getFrameCount() % frameSkip == 0) {
-				ChunkHeader chunk;
-				readChunk(videoFile, chunk);
-				debug("Read chunk type %d at frame %d", chunk.chunkType, frameCounter);
-				switch (chunk.chunkType) {
-				case 1:
-				case 2:
-					processFrame(chunk, frameCounter++);
-					break;
-				case 3:
-					videoExitFlag = true;
-					break;
-				case 4:
-					loadPalette(chunk);
-					break;
-				default:
-					debug("Unknown chunk type %d encountered", chunk.chunkType);
+			while (!g_engine->shouldQuit() && _events->_lastKeyEvent != Common::KEYCODE_ESCAPE) {
+				_chrono->updateChrono();
+				_events->pollEvent();
+				if (_chrono->_gameTick && _chrono->getFrameCount() % frameSkip == 0)
 					break;
-				}
-
-				if (_voiceEffect.contains(frameCounter)) {
-					// Wait for any playing voice to finish before starting new one
-					while (_sound->isPlaying(0)) {
-						_events->pollEvent();
-						g_system->delayMillis(10);
-						if (g_engine->shouldQuit() || _events->_lastKeyEvent == Common::KEYCODE_ESCAPE) {
-							break;
-						}
-					}
-					AudioEffect voice = _voiceEffect[frameCounter];
-					debug("Playing voice effect: '%s'", voice.filename.c_str());
-					VoiceData voiceData = _sounds[voice.filename];
-					_introSndFile.seek(voiceData.offset, SEEK_SET);
-					byte *voiceBuffer = new byte[voiceData.length];
-					_introSndFile.read(voiceBuffer, voiceData.length);
-					_sound->playSound(voiceBuffer, voiceData.length, 0);
-				}
+				g_system->delayMillis(10);
+			}
 
-				if(_sfxEffect.contains(frameCounter)) {
-					AudioEffect sfx = _sfxEffect[frameCounter];
-					debug("Playing SFX effect: '%s'", sfx.filename.c_str());
-					VoiceData sfxData = _sounds[sfx.filename];
-					_introSndFile.seek(sfxData.offset, SEEK_SET);
-					byte *sfxBuffer = new byte[sfxData.length];
-					_introSndFile.read(sfxBuffer, sfxData.length);
-					_sound->playSound(sfxBuffer, sfxData.length, 1);
+			// Fix Bug 1: capture current frame BEFORE increment so audio fires at the correct frame
+			int currentFrame = frameCounter++;
+			processFrame(chunk, currentFrame);
+
+			// Fix Bug 1: all audio checks use currentFrame (not the already-incremented frameCounter)
+			if (_voiceEffect.contains(currentFrame)) {
+				// Wait for any playing voice to finish before starting new one
+				while (_sound->isPlaying(0)) {
+					_events->pollEvent();
+					g_system->delayMillis(10);
+					if (g_engine->shouldQuit() || _events->_lastKeyEvent == Common::KEYCODE_ESCAPE)
+						break;
 				}
+				AudioEffect voice = _voiceEffect[currentFrame];
+				debug("Playing voice effect: '%s'", voice.filename.c_str());
+				VoiceData voiceData = _sounds[voice.filename];
+				_introSndFile.seek(voiceData.offset, SEEK_SET);
+				byte *voiceBuffer = new byte[voiceData.length];
+				_introSndFile.read(voiceBuffer, voiceData.length);
+				_sound->playSound(voiceBuffer, voiceData.length, 0);
+			}
 
-				if(_musicEffect.contains(frameCounter)) {
-					MusicEffect music = _musicEffect[frameCounter];
-					_sound->playMusicTrack(music.trackNumber, true);
-				}
+			if (_sfxEffect.contains(currentFrame)) {
+				AudioEffect sfx = _sfxEffect[currentFrame];
+				debug("Playing SFX effect: '%s'", sfx.filename.c_str());
+				VoiceData sfxData = _sounds[sfx.filename];
+				_introSndFile.seek(sfxData.offset, SEEK_SET);
+				byte *sfxBuffer = new byte[sfxData.length];
+				_introSndFile.read(sfxBuffer, sfxData.length);
+				_sound->playSound(sfxBuffer, sfxData.length, 1);
+			}
 
-				if (subtitle != nullptr) {
-					Common::StringArray lines = _dialog->wordWrap(subtitle->text)[0];
+			if (_musicEffect.contains(currentFrame)) {
+				MusicEffect music = _musicEffect[currentFrame];
+				_sound->playMusicTrack(music.trackNumber, true);
+			}
 
-					byte color;
-					_dialog->processColorAndTrim(lines, color);
-					Graphics::Surface s = _dialog->getDialogueSurface(lines, color);
-					_textSurface.transBlitFrom(s, Common::Point(subtitle->x, subtitle->y), 255);
+			// Fix Bug 4: subtitles are suppressed in the blackout range (frames 571-669)
+			bool inBlackoutRange = (currentFrame >= 571 && currentFrame <= 669);
+			if (subtitle != nullptr && !inBlackoutRange) {
+				Common::StringArray lines = _dialog->wordWrap(subtitle->text)[0];
 
-					drawPos(&_textSurface, subtitle->x, subtitle->y, color);
-					drawRect(&_textSurface, subtitle->x, subtitle->y,
-							 s.getRect().width(),
-							 s.getRect().height(), color);
-				}
+				byte color;
+				_dialog->processColorAndTrim(lines, color);
+				Graphics::Surface s = _dialog->getDialogueSurface(lines, color);
+				_textSurface.transBlitFrom(s, Common::Point(subtitle->x, subtitle->y), 255);
 
-				presentFrame();
+				drawPos(&_textSurface, subtitle->x, subtitle->y, color);
+				drawRect(&_textSurface, subtitle->x, subtitle->y,
+						 s.getRect().width(),
+						 s.getRect().height(), color);
 			}
-			g_system->delayMillis(10);
+
+			presentFrame();
+			break;
+		}
+		case 3:
+			videoExitFlag = true;
+			break;
+		case 4:
+			loadPalette(chunk);
+			break;
+		case 6:
+			// Fix Bug 3: type 6 is a timing pad. Original costs ~20ms per chunk.
+			// Do NOT gate on chrono — process immediately with a short delay.
+			g_system->delayMillis(20);
+			break;
+		default:
+			debug("Unknown chunk type %d encountered", chunk.chunkType);
+			break;
 		}
-		debug("Total frames played: %d", frameCounter);
 	}
+
+	debug("Total frames played: %d", frameCounter);
 	videoFile.close();
 }
 


Commit: a4c9db112d0750cd57552e80b576939caf467a63
    https://github.com/scummvm/scummvm/commit/a4c9db112d0750cd57552e80b576939caf467a63
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:24+02:00

Commit Message:
PELROCK: Fixes inventory overlay glitches (autoscrolls on last icon)

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/extrascreens.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index ca9761466d4..2f3aac6a705 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -252,6 +252,7 @@ void PelrockEngine::addInventoryItem(int item) {
 	}
 	_newItem = item;
 	int frameCounter = 0;
+	_state->addInventoryItem(item);
 	while (frameCounter < kIconFlashDuration) {
 		_events->pollEvent();
 
@@ -262,7 +263,6 @@ void PelrockEngine::addInventoryItem(int item) {
 		}
 		g_system->delayMillis(10);
 	}
-	_state->addInventoryItem(item);
 
 	checkObjectsForPart2();
 }
@@ -1205,25 +1205,27 @@ void PelrockEngine::closeTravelAgencyDoor(HotSpot *hotspot) {
 }
 
 void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
-	_state->removeInventoryItem(86);
-	addInventoryItem(76);
-	_sound->playMusicTrack(27);
-	checkIngredients();
-	_dialog->say(_res->_ingameTexts[CUIDADOIMPRUDENTE]);
-	_alfredState.x -= 10;
-	_alfredState.y += 20;
-	playAlfredSpecialAnim(5);
-	_sound->playSound(_room->_roomSfx[0], 0); // Belch
-	waitForSoundEnd();
-	_alfredState.animState = ALFRED_SKIP_DRAWING;
-	_graphics->fadeToBlack(10);
-	// update conversaton state
-	_alfredState.x = 300;
-	_alfredState.y = 238;
-	waitForSoundEnd();
-	_alfredState.animState = ALFRED_IDLE;
-	setScreenAndPrepare(28, ALFRED_DOWN);
-	_dialog->say(_res->_ingameTexts[QUEOSCUROESTAESTO]);
+	_state->removeInventoryItem(76);
+	addInventoryItem(86);
+	if(_state->getFlag(FLAG_CROCODILLO_ENCENDIDO) == false) {
+		_sound->playMusicTrack(27);
+		checkIngredients();
+		_dialog->say(_res->_ingameTexts[CUIDADOIMPRUDENTE]);
+		_alfredState.x -= 10;
+		_alfredState.y += 20;
+		playAlfredSpecialAnim(5);
+		_sound->playSound(_room->_roomSfx[0], 0); // Belch
+		waitForSoundEnd();
+		_alfredState.animState = ALFRED_SKIP_DRAWING;
+		_graphics->fadeToBlack(10);
+		// update conversaton state
+		_alfredState.x = 300;
+		_alfredState.y = 238;
+		waitForSoundEnd();
+		_alfredState.animState = ALFRED_IDLE;
+		setScreenAndPrepare(28, ALFRED_DOWN);
+		_dialog->say(_res->_ingameTexts[QUEOSCUROESTAESTO]);
+	}
 }
 
 void PelrockEngine::playAlfredSpecialAnim(int anim, bool reverse) {
@@ -1761,6 +1763,9 @@ void PelrockEngine::useWigWithPot(int inventoryObject, HotSpot *hotspot) {
 }
 
 void PelrockEngine::magicFormula(int inventoryObject, HotSpot *hotspot) {
+	if(inventoryObject == 86) {
+		addInventoryItem(76);
+	}
 	_state->removeInventoryItem(inventoryObject);
 	_state->setFlag(FLAG_FORMULA_MAGICA, _state->getFlag(FLAG_FORMULA_MAGICA) + 1);
 	if (_state->getFlag(FLAG_FORMULA_MAGICA) == 4) {
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 1319172f159..9b94bcea530 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -838,7 +838,7 @@ void DialogManager::addGoodbyeOptionIfNeeded(Common::Array<ChoiceOption> *choice
 		termChoice.isTerminator = true;
 		termChoice.isDisabled = false;
 		termChoice.shouldDisableOnSelect = false;
-		termChoice.text = g_engine->_res->_conversationTerminator;
+		termChoice.text = "  " + g_engine->_res->_conversationTerminator;
 		choices->push_back(termChoice);
 	}
 }
diff --git a/engines/pelrock/extrascreens.cpp b/engines/pelrock/extrascreens.cpp
index 0c9ad6fa5bd..bb6457a29c2 100644
--- a/engines/pelrock/extrascreens.cpp
+++ b/engines/pelrock/extrascreens.cpp
@@ -63,8 +63,6 @@ Spell *SpellBook::run() {
 
 void SpellBook::init() {
 	_compositeScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
-
-	// selectPage(0);
 }
 
 void SpellBook::selectPage(int page) {
@@ -93,7 +91,7 @@ void SpellBook::selectPage(int page) {
 	_spell->image = new byte[w * h];
 	extractSingleFrame(spriteData, _spell->image, page, w, h);
 
-	juegoFile.seek(0x0004661C, SEEK_SET);
+	juegoFile.seek(0x0004661D, SEEK_SET);
 	byte *textData = new byte[2861];
 	juegoFile.read(textData, 2861);
 
@@ -103,7 +101,6 @@ void SpellBook::selectPage(int page) {
 	}
 
 	Common::Array<Common::StringArray> spells = _res->processTextData(textData, 2861, true);
-
 	_spell->text = spells[page];
 	delete[] compressedData;
 	delete[] spriteData;
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index bec290f5559..6924697df5f 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1391,6 +1391,7 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 		// This means the balloon bottom overlaps Alfred's head by ~10 pixels.
 		_actionPopupState.y = MAX(10, (int)_alfredState.y - (int)kAlfredFrameHeight - 102);
 		_actionPopupState.isActive = true;
+		_inventoryOverlayState.invStartingPos = -1;
 		_actionPopupState.curFrame = 0;
 		debug("Setting alfred under popup: %d", alfredUnder);
 		_actionPopupState.isAlfredUnder = alfredUnder;
@@ -1592,8 +1593,14 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	}
 
 	if (_inventoryOverlayState.isActive) {
+		// find selectedInventoryItem index in inventoryItems, set invStartingPos to that index if found, otherwise 0
+		int scrollPos = getScrollPositionForItem(_state->selectedInventoryItem);
+		if(_inventoryOverlayState.invStartingPos == -1) {
+			_inventoryOverlayState.invStartingPos = scrollPos != -1 ? scrollPos : 0;
+		}
 		showInventoryOverlay();
 		if (_inventoryOverlayState.posInInventorySelectionArea(_events->_mouseX, _events->_mouseY)) {
+			_inventoryOverlayState.flashingIconIndex = -1;
 			checkMouseOverInventoryOverlay(_events->_mouseX, _events->_mouseY);
 		}
 	}
@@ -1612,6 +1619,23 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	}
 }
 
+int PelrockEngine::getScrollPositionForItem(int item) {
+	int selectedIndex = -1;
+	for (size_t i = 0; i < _state->inventoryItems.size(); i++) {
+		if (_state->inventoryItems[i] == item) {
+			selectedIndex = i;
+			break;
+		}
+	}
+	if (selectedIndex != -1) {
+		// put the selected item at the end in the overlay
+		if (_state->inventoryItems.size() > kInventoryPageSize) {
+			selectedIndex = CLIP(selectedIndex, 0, (int)_state->inventoryItems.size() - kInventoryPageSize);
+		}
+	}
+	return selectedIndex;
+}
+
 void PelrockEngine::animateTalkingNPC(Sprite *animSet) {
 	// Change with the right index
 
@@ -1653,21 +1677,10 @@ Common::Point getPositionInOverlayForIndex(uint index) {
 }
 
 void PelrockEngine::pickupIconFlash() {
-	_graphics->showOverlay(60, _compositeBuffer);
-	if (_newItem == -1)
-		return;
 	uint invSize = _state->inventoryItems.size();
-	for (int i = 0; i < invSize; i++) {
-		Common::Point p = getPositionInOverlayForIndex(i);
-		drawSpriteToBuffer(_compositeBuffer, _res->getIconForObject(_state->inventoryItems[i]).iconData, p.x, p.y, 60, 60, 1);
-	}
-
-	InventoryObject item = _res->getIconForObject(_newItem);
-	if (_chrono->getFrameCount() % kIconBlinkPeriod == 0) {
-		Common::Point p = getPositionInOverlayForIndex(invSize);
-		debug("Drawing pickup icon for item %d at inventory index %d, position %d,%d", _newItem, invSize, p.x, p.y);
-		drawSpriteToBuffer(_compositeBuffer, item.iconData, p.x, p.y, 60, 60, 1);
-	}
+	_inventoryOverlayState.invStartingPos = getScrollPositionForItem(_state->inventoryItems[invSize - 1]);
+	_inventoryOverlayState.flashingIconIndex = invSize - 1;
+	showInventoryOverlay();
 }
 
 void PelrockEngine::showInventoryOverlay() {
@@ -1675,6 +1688,12 @@ void PelrockEngine::showInventoryOverlay() {
 	uint invSize = _state->inventoryItems.size();
 	// invStartingPos is an ITEM index (not a page number).
 	// The original game scrolls 1 item at a time, not 1 page at a time.
+	if(_inventoryOverlayState.invStartingPos == -1) {
+		_inventoryOverlayState.invStartingPos = getScrollPositionForItem(_state->selectedInventoryItem);
+		if(_inventoryOverlayState.invStartingPos == -1) {
+			_inventoryOverlayState.invStartingPos = 0;
+		}
+	}
 	int firstItem = _inventoryOverlayState.invStartingPos;
 
 	for (int i = firstItem; i < (int)invSize && i < firstItem + kInventoryPageSize; i++) {
@@ -1701,10 +1720,12 @@ void PelrockEngine::checkMouseOverInventoryOverlay(int x, int y) {
 		if (_inventoryOverlayState.invStartingPos > 0) {
 			_inventoryOverlayState.invStartingPos--;
 		}
+		debug("Mouse at x=%d triggers scroll left, new invStartingPos=%d", x, _inventoryOverlayState.invStartingPos);
 	} else if (x >= 620) {
 		if (_inventoryOverlayState.invStartingPos + kInventoryPageSize < (int)_state->inventoryItems.size()) {
 			_inventoryOverlayState.invStartingPos++;
 		}
+		debug("Mouse at x=%d triggers scroll right, new invStartingPos=%d", x, _inventoryOverlayState.invStartingPos);
 	} else {
 		// mouse hover over inventory item, laid out horizontally, y coordinate is not relevant
 		int index = (x - 20) / 60 + _inventoryOverlayState.invStartingPos;
@@ -1959,6 +1980,10 @@ void PelrockEngine::changeCursor(Cursor cursor) {
 }
 
 void PelrockEngine::checkMouseHover() {
+	if(_actionPopupState.isActive) {
+		debug("Mouse hover ignored because action popup is active");
+		return;
+	}
 
 	bool hotspotDetected = false;
 	int hotspotIndex = isHotspotUnder(_events->_mouseX, _events->_mouseY);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 6df37af5dc1..46ef3f43767 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -84,6 +84,8 @@ private:
 
 	void showActionBalloon(int posx, int posy, int curFrame);
 
+	int getScrollPositionForItem(int item);
+
 	void checkMouse();
 	void copyBackgroundToBuffer();
 	void updateAnimations();


Commit: cef88966e5bda54b4a96737b4a8150c3146fc9b7
    https://github.com/scummvm/scummvm/commit/cef88966e5bda54b4a96737b4a8150c3146fc9b7
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:24+02:00

Commit Message:
PELROCK: Fixes stone delivery sequence in rooms 41 & 42

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 2f3aac6a705..e6b91df562b 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -600,7 +600,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 308: {
 		int targetBranch = rootIndex + 1;
 		if (targetBranch > 17) {
-			targetBranch = 3;
+			targetBranch = 2;
 		}
 		_state->setCurrentRoot(room, targetBranch, 0);
 		break;
@@ -1234,8 +1234,8 @@ void PelrockEngine::playAlfredSpecialAnim(int anim, bool reverse) {
 	waitForSpecialAnimation();
 }
 
-void PelrockEngine::waitForSoundEnd() {
-	while (!shouldQuit() && _sound->isPlaying(0)) {
+void PelrockEngine::waitForSoundEnd(int channel) {
+	while (!shouldQuit() && _sound->isPlaying(channel)) {
 		_events->pollEvent();
 		renderScene(OVERLAY_NONE);
 		_screen->update();
@@ -1563,17 +1563,19 @@ void PelrockEngine::giveStoneToSlaves(int inventoryObject, HotSpot *hotspot) {
 
 	_dialog->say(_res->_ingameTexts[HAYQUECELEBRARLO]);
 
+	byte counter = _state->getFlag(FLAG_DA_PIEDRA);
 	// drinking animation and sound
-	_sound->playSound(_room->_roomSfx[1], 0);
+	_sound->playSound(_room->_roomSfx[1], 2);
+
+	_room->findSpriteByIndex(0)->zOrder = -1;
 
-	_room->disableSprite(0);
-	playSpecialAnim(1473360, true, mastersX - 5, mastersY - 1, 152, 83, 7);
+	playSpecialAnim(1473360, true, mastersX - 6, mastersY - 1, 152, 83, 7);
 
 	// Increment stone delivery counter (tracks 0→1→2→3)
-	byte counter = _state->getFlag(FLAG_DA_PIEDRA);
 	debug("Current stone delivery count: %d", counter);
 	if (counter < 3) {
 		_state->setFlag(FLAG_DA_PIEDRA, ++counter);
+		_room->findSpriteByIndex(0)->zOrder = zIndex;
 	}
 	debug("New stone delivery count: %d", _state->getFlag(FLAG_DA_PIEDRA));
 	// At 2nd stone delivery: masters starts singing (conversation root 2)
@@ -1583,18 +1585,14 @@ void PelrockEngine::giveStoneToSlaves(int inventoryObject, HotSpot *hotspot) {
 
 	// At 3rd stone delivery: masters get wasted
 	if (counter == 3) {
+		_room->disableSprite(0, PERSIST_BOTH);
 		playSpecialAnim(1512060, true, mastersX - 28, mastersY - 6, 172, 96, 3);
-
 		_room->addSticker(116);
-
 		WalkBox w1 = {3, 187, 374, 5, 17, 0};
 		WalkBox w2 = {4, 141, 374, 46, 4, 0};
 		_room->addWalkbox(w1);
 		_room->addWalkbox(w2);
 		_state->setFlag(FLAG_GUARDIAS_BORRACHOS, true);
-	} else {
-		debug("Re-enabling master sprite with zIndex %d", zIndex);
-		_room->enableSprite(0, zIndex);
 	}
 }
 
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 9b94bcea530..5cc7535edcf 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -1098,7 +1098,6 @@ int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
 }
 
 Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::String text) {
-	debug("Word-wrapping text: \"%s\"", text.c_str());
 	Common::Array<Common::Array<Common::String>> pages;
 	Common::Array<Common::String> currentPage;
 	Common::Array<Common::String> currentLine;
@@ -1169,12 +1168,12 @@ Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::Str
 	}
 
 	// print all the pages and lines for debugging
-	for (uint i = 0; i < pages.size(); i++) {
-		debug("Page %d:", i);
-		for (uint j = 0; j < pages[i].size(); j++) {
-			debug(" Line %d: \"%s\"", j, pages[i][j].c_str());
-		}
-	}
+	// for (uint i = 0; i < pages.size(); i++) {
+	// 	debug("Page %d:", i);
+	// 	for (uint j = 0; j < pages[i].size(); j++) {
+	// 		debug(" Line %d: \"%s\"", j, pages[i][j].c_str());
+	// 	}
+	// }
 	return pages;
 }
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 6924697df5f..dd00c2f2f09 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1740,6 +1740,7 @@ void PelrockEngine::checkMouseOverInventoryOverlay(int x, int y) {
 }
 
 int PelrockEngine::checkMouseClickInventoryOverlay(int x, int y) {
+
 	if (x < 20) {
 		return -1;
 	} else if (x >= 620) {
@@ -1872,6 +1873,10 @@ void PelrockEngine::walkTo(int x, int y) {
 }
 
 void PelrockEngine::walkAndAction(HotSpot *hotspot, VerbIcon action) {
+	if(_currentHotspot == nullptr) {
+		debug("Error: walkAndAction called with null hotspot");
+		return;
+	}
 	_disableAction = true;
 	walkTo(hotspot->x + hotspot->w / 2, hotspot->y + hotspot->h);
 	_queuedAction = QueuedAction{action, hotspot->index, true, false};
@@ -1981,7 +1986,6 @@ void PelrockEngine::changeCursor(Cursor cursor) {
 
 void PelrockEngine::checkMouseHover() {
 	if(_actionPopupState.isActive) {
-		debug("Mouse hover ignored because action popup is active");
 		return;
 	}
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 46ef3f43767..4ea450ae383 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -356,7 +356,7 @@ public:
 	void closeTravelAgencyDoor(HotSpot *hotspot);
 	void usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot);
 	void playAlfredSpecialAnim(int anim, bool reverse = false);
-	void waitForSoundEnd();
+	void waitForSoundEnd(int channel = 0);
 	void pickupSunflower(HotSpot *hotspot);
 	void checkIngredients();
 	void pickUpBook(int i);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index a62a615fa98..ff099f4faf3 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -246,6 +246,7 @@ void RoomManager::enableSprite(byte spriteIndex, byte zOrder, int persist) {
 void RoomManager::enableSprite(byte roomNumber, byte spriteIndex, byte zOrder, int persist) {
 	for(int i =0; i < g_engine->_state->spriteChanges[roomNumber].size(); i++) {
 		if (g_engine->_state->spriteChanges[roomNumber][i].spriteIndex == spriteIndex) {
+			debug("Removing sprite change for room %d, sprite index %d, zOrder %d", roomNumber, spriteIndex, g_engine->_state->spriteChanges[roomNumber][i].zIndex);
 			g_engine->_state->spriteChanges[roomNumber].remove_at(i);
 			break;
 		}
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 96b621e9769..42340cdb5fe 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -49,11 +49,11 @@ SoundManager::~SoundManager() {
 	stopMusic();
 }
 
-void SoundManager::playSound(byte index, int channel) {
+void SoundManager::playSound(byte index, int channel, int loopCount) {
 	// debug("Playing sound index %d (%s)", index, SOUND_FILENAMES[index]);
 	auto it = _soundMap.find(SOUND_FILENAMES[index]);
 	if (it != _soundMap.end()) {
-		playSound(it->_value, channel);
+		playSound(it->_value, channel, loopCount);
 	} else {
 		debug("Sound file %s not found in sound map", SOUND_FILENAMES[index]);
 	}
@@ -115,11 +115,15 @@ void SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
 		} else {
 			if (_mixer->isSoundHandleActive(_sfxHandles[channel])) {
 				_mixer->stopHandle(_sfxHandles[channel]);
+				debug("Stopped active sound on channel %d to play new sound %s", channel, sound.filename.c_str());
+			}
+			else {
+				debug("Warning: channel %d is already free when trying to play sound %s", channel, sound.filename.c_str());
 			}
 		}
 		Audio::AudioStream *finalStream = loopCount != -1 ? stream : Audio::makeLoopingAudioStream(stream, 0);
 
-		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], finalStream, loopCount, _currentVolume, 0, DisposeAfterUse::YES);
+		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], finalStream, -1, _currentVolume, 0, DisposeAfterUse::YES);
 	}
 }
 
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index 6e33487d34b..3480b333974 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -157,7 +157,7 @@ class SoundManager {
 public:
 	SoundManager(Audio::Mixer *mixer);
 	~SoundManager();
-	void playSound(byte index, int channel = -1);
+	void playSound(byte index, int channel = -1, int loopCount = 1);
 	void playSound(const char *filename, int channel, int loopCount = 1);
 	void playSound(byte *soundData, uint32 size, int channel);
 	void stopAllSounds();


Commit: b0fe4b04a9ee3351e02f130e262261b9f9e029ce
    https://github.com/scummvm/scummvm/commit/b0fe4b04a9ee3351e02f130e262261b9f9e029ce
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:24+02:00

Commit Message:
PELROCK: Fixes credits not restarting

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index e6b91df562b..19c85ee504a 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1761,10 +1761,10 @@ void PelrockEngine::useWigWithPot(int inventoryObject, HotSpot *hotspot) {
 }
 
 void PelrockEngine::magicFormula(int inventoryObject, HotSpot *hotspot) {
+	_state->removeInventoryItem(inventoryObject);
 	if(inventoryObject == 86) {
 		addInventoryItem(76);
 	}
-	_state->removeInventoryItem(inventoryObject);
 	_state->setFlag(FLAG_FORMULA_MAGICA, _state->getFlag(FLAG_FORMULA_MAGICA) + 1);
 	if (_state->getFlag(FLAG_FORMULA_MAGICA) == 4) {
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index dd00c2f2f09..28ec71e66ec 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1873,8 +1873,7 @@ void PelrockEngine::walkTo(int x, int y) {
 }
 
 void PelrockEngine::walkAndAction(HotSpot *hotspot, VerbIcon action) {
-	if(_currentHotspot == nullptr) {
-		debug("Error: walkAndAction called with null hotspot");
+	if(hotspot == nullptr) {
 		return;
 	}
 	_disableAction = true;
@@ -2610,7 +2609,6 @@ void PelrockEngine::credits() {
 	CursorMan.showMouse(false);
 
 	// Outer restart loop — keypress during display restarts from page 0
-	bool restart = false;
 	_alfredState.setState(ALFRED_SKIP_DRAWING);
 	_disableAmbientSounds = true;
 	_disableAction = true;
@@ -2679,7 +2677,7 @@ void PelrockEngine::credits() {
 				break;
 			}
 		}
-	} while (restart && !shouldQuit() && !keyPressed);
+	} while (!shouldQuit() && !keyPressed);
 
 	g_engine->quitGame();
 }


Commit: 1f06f642782cebf9ec5aee5bc3699191d7e6d374
    https://github.com/scummvm/scummvm/commit/1f06f642782cebf9ec5aee5bc3699191d7e6d374
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:24+02:00

Commit Message:
PELROCK: Fixes credits changing color upon repeat

Changed paths:
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 28ec71e66ec..aea025c45b4 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -2605,10 +2605,22 @@ void PelrockEngine::credits() {
 	static const int kFramesPerPage = 45;
 
 	Common::Array<Common::StringArray> creditTexts = _res->getCredits();
+	Common::Array<int> creditsSpeakerId;
+	// Preprocess credit texts: extract speaker IDs and apply word wrapping
+	for(int i = 0; i < creditTexts.size(); i++) {
+		byte speakerId;
+		_dialog->processColorAndTrim(creditTexts[i], speakerId);
+		creditsSpeakerId.push_back(speakerId);
+		// Text is already encoded for new lines but should also be wrapped to respect the max chars per line!
+		creditTexts[i] = _dialog->wordWrap(creditTexts[i])[0];
+		// all lines start with a space but the first one contains the trailing space of the speakerId
+		creditTexts[i][0] = creditTexts[i][0].substr(1, creditTexts[i][0].size() - 1);
+	}
 
 	CursorMan.showMouse(false);
 
 	// Outer restart loop — keypress during display restarts from page 0
+
 	_alfredState.setState(ALFRED_SKIP_DRAWING);
 	_disableAmbientSounds = true;
 	_disableAction = true;
@@ -2622,14 +2634,6 @@ void PelrockEngine::credits() {
 				pigeons->disableAfterSequence = true;
 			}
 
-			byte speakerId;
-			_dialog->processColorAndTrim(creditTexts[page], speakerId);
-
-			// Text is already encoded for new lines but should also be wrapped to respect the max chars per line!
-			creditTexts[page] = _dialog->wordWrap(creditTexts[page])[0];
-			// all lines start with a space but the first one contains the trailing space of the speakerId
-			creditTexts[page][0] = creditTexts[page][0].substr(1, creditTexts[page][0].size() - 1);
-
 			int height = creditTexts[page].size() * 25; // Add some padding
 
 			Graphics::Surface s;
@@ -2651,7 +2655,7 @@ void PelrockEngine::credits() {
 				// subtract that extra negative identation
 				int xPos = i == creditTexts[page].size() - 1 ? startX - 10 : startX;
 				int yPos = i * 25; // Above sprite, adjust for line
-				g_engine->_largeFont->drawString(&s, creditTexts[page][i], xPos, yPos, 640, speakerId, Graphics::kTextAlignLeft);
+				g_engine->_largeFont->drawString(&s, creditTexts[page][i], xPos, yPos, 640, creditsSpeakerId[page], Graphics::kTextAlignLeft);
 			}
 
 			int frames = 0;


Commit: aa3c4639b8b7d88bc7365b27155d2ca281f21af1
    https://github.com/scummvm/scummvm/commit/aa3c4639b8b7d88bc7365b27155d2ca281f21af1
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:25+02:00

Commit Message:
PELROCK: Fixes removal of letter x in ingame texts

Changed paths:
    engines/pelrock/resources.cpp


diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index da9f1007cf0..504ca209d28 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -449,7 +449,7 @@ Common::Array<Common::StringArray> ResourceManager::processTextData(byte *data,
 			pos++;
 			continue;
 		}
-		if (data[pos] == 0x00 || data[pos] == 0x78) {
+		if (data[pos] == 0x00) {
 			pos++;
 			continue;
 		}


Commit: e482a187e1718b403ec9540b79c2c32038cb9f17
    https://github.com/scummvm/scummvm/commit/e482a187e1718b403ec9540b79c2c32038cb9f17
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:25+02:00

Commit Message:
PELROCK: Fixes for end cutscene

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 19c85ee504a..37a8f82d90f 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1902,7 +1902,7 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 		break;
 	}
 	case 375: {
-		teletransportToPrincess();
+		teleportToPrincess();
 		break;
 	}
 	}
@@ -1935,7 +1935,7 @@ static void drawRemappedLine(byte *buf, int x0, int y0, int x1, int y1, const by
 	}
 }
 
-void PelrockEngine::teletransportToPrincess() {
+void PelrockEngine::teleportToPrincess() {
 	int phase = 0;
 
 	int lines[5][4] = {
@@ -1948,7 +1948,6 @@ void PelrockEngine::teletransportToPrincess() {
 	int stickers[5] = {113, 114, 110, 111, 112};
 
 	while (phase < 5) {
-		debug("Starting ending scene phase %d", phase);
 		Sprite *thisSprite = _room->findSpriteByIndex(phase + 1);
 		thisSprite->animData[0].curFrame = 0;
 		thisSprite->zOrder = 200;
@@ -1957,19 +1956,17 @@ void PelrockEngine::teletransportToPrincess() {
 			_events->pollEvent();
 			renderScene(OVERLAY_NONE);
 			_screen->update();
-			g_system->delayMillis(10);
 		}
 
 		_sound->playSound(_room->_roomSfx[3], 0);
 
-		// Draw 19 semi-transparent remapped lines (behind sprites, matching original)
-		// Original uses shadow_palette_remap_tables[1] — _paletteRemaps[1] from ALFRED.9
+		// Draw 19 semi-transparent remapped lines
 		copyBackgroundToBuffer();
 		placeStickersFirstPass();
 		updateAnimations();
 		presentFrame();
 		_screen->update();
-		g_system->delayMillis(10);
+
 		for (int i = 0; i < 19; i++) {
 			if (shouldQuit())
 				return;
@@ -1983,7 +1980,7 @@ void PelrockEngine::teletransportToPrincess() {
 		updateAnimations();
 		presentFrame();
 		_screen->update();
-		g_system->delayMillis(10);
+
 		_events->pollEvent();
 		if (shouldQuit())
 			return;
@@ -1995,7 +1992,6 @@ void PelrockEngine::teletransportToPrincess() {
 		updateAnimations();
 		presentFrame();
 		_screen->update();
-
 		phase++;
 	}
 	// small delay before last sticker
@@ -2019,7 +2015,6 @@ void PelrockEngine::teletransportToPrincess() {
 
 	_dialog->say(_res->_ingameTexts[MAREDEDEU]);
 
-	// endgameTransportAnimation();
 	smokeAnimation(-1, true);
 	_state->setFlag(FLAG_END_OF_GAME, true);
 	_state->setCurrentRoot(48, 1, 0);
@@ -2089,9 +2084,9 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		Spell *spell = spellBook.run();
 		if (spell) {
 			_alfredState.direction = ALFRED_LEFT;
-			_dialog->say(_res->_ingameTexts[DIOSHALCON + spell->page], 1);
 			switch (_room->_currentRoomNumber) {
 			case 28: {
+				_dialog->say(_res->_ingameTexts[DIOSHALCON + spell->page], 1);
 				if (spell->page == 12) {
 					_graphics->clearScreen();
 					int waitFrames = 0;
@@ -2118,6 +2113,8 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 			case 52:
 			case 53:
 			case 54: {
+				_sound->playSound(_room->_roomSfx[8], 0);
+				_dialog->say(_res->_ingameTexts[DIOSHALCON + spell->page], 1);
 				debug("Flight spell cast in room %d, spell page: %d", _room->_currentRoomNumber, spell->page);
 				int flightIndex = _room->_currentRoomNumber - 51;
 				debug("Correct page for this spell is = %d", kFlightRooms[flightIndex].spellPage);
@@ -2125,22 +2122,25 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 					_state->setFlag(FLAG_COMO_ESTAN_LOS_DIOSES, _state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) | (1 << flightIndex));
 					debug("Flight spell successful, starting animation and updating state");
 					debug("Updated FLAG_COMO_ESTAN_LOS_DIOSES: %d", _state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES));
+					_sound->playSound(_room->_roomSfx[1], 0);
 					smokeAnimation(kFlightRooms[flightIndex].spriteIdx, true);
 					_room->addStickerToRoom(_room->_currentRoomNumber, 127 + flightIndex);
-					// if(_state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) == 0b1111) {
-					HotSpot hotspot = HotSpot();
-					hotspot.actionFlags = 0;
-					hotspot.extra = 999;
-					hotspot.x = 320;
-					hotspot.y = 288;
-					hotspot.w = 35;
-					hotspot.h = 21;
-					hotspot.innerIndex = 0;
-					hotspot.index = 8;
-					_room->changeHotspot(52, hotspot);
-					// }
+					if(_state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) == 0b1111) {
+						HotSpot hotspot = HotSpot();
+						hotspot.actionFlags = 0;
+						hotspot.extra = 999;
+						hotspot.x = 320;
+						hotspot.y = 288;
+						hotspot.w = 35;
+						hotspot.h = 21;
+						hotspot.innerIndex = 0;
+						hotspot.index = 8;
+						_room->changeHotspot(52, hotspot);
+					}
+				}
+				else {
+					_sound->playSound(_room->_roomSfx[2], 0);
 				}
-
 				break;
 			}
 			default:
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index aea025c45b4..d69c571275d 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1798,7 +1798,7 @@ void PelrockEngine::gameLoop() {
 	}
 
 	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_e && _room->_currentRoomNumber == 52) {
-		teletransportToPrincess();
+		teleportToPrincess();
 		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
 	}
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 4ea450ae383..96286d5b569 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -403,7 +403,7 @@ public:
 	void openMcDoor(HotSpot *hotspot);
 	void closeMcDoor(HotSpot *hotspot);
 	void pickupBush(HotSpot *hotspot);
-	void teletransportToPrincess();
+	void teleportToPrincess();
 
 	void animateStatuePaletteFade(bool reverse = false);
 	void pickUpMatches(HotSpot *hotspot);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index ff099f4faf3..2c52568a54b 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -1330,7 +1330,7 @@ Common::Array<byte> RoomManager::loadRoomSfx(Common::File *roomFile, int roomOff
 	for (int i = 0; i < kNumSfxPerRoom; i++) {
 		byte sfx = roomFile->readByte();
 		roomSfx[i] = sfx;
-		// debug("SFX %d for room at offset %d is %d (%s)", i, roomOffset, sfx, SOUND_FILENAMES[sfx]);
+		debug("SFX %d for room at offset %d is %d (%s)", i, roomOffset, sfx, SOUND_FILENAMES[sfx]);
 	}
 	return roomSfx;
 }
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 5257e5fadb0..a2c98b4dc72 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -29,7 +29,7 @@
 
 namespace Pelrock {
 
-static const int kNumSfxPerRoom = 8;
+static const int kNumSfxPerRoom = 9;
 static const int unpickableHotspotExtras[] = {
 	308, // lamppost cable
 	65, // objects in shop


Commit: 23093df6e0416c898754f20acac30ae8ddf3954f
    https://github.com/scummvm/scummvm/commit/23093df6e0416c898754f20acac30ae8ddf3954f
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:25+02:00

Commit Message:
PELROCK: General code cleanup

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/events.h
    engines/pelrock/graphics.cpp
    engines/pelrock/offsets.h
    engines/pelrock/pathfinding.cpp
    engines/pelrock/pathfinding.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h
    engines/pelrock/types.h
    engines/pelrock/util.cpp
    engines/pelrock/util.h
    engines/pelrock/video/video.cpp
    engines/pelrock/video/video.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 37a8f82d90f..0defabc6729 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1135,7 +1135,7 @@ void PelrockEngine::useSecretCodeWithStatue(int inventoryObject, HotSpot *hotspo
 
 void PelrockEngine::pickUpLetter(HotSpot *hotspot) {
 	addInventoryItem(9);
-	_room->setActionMask(hotspot, ACTION_MASK_NONE); // Disable hotspot
+	_room->setActionMask(hotspot, kActionMaskNone); // Disable hotspot
 }
 
 void PelrockEngine::openLibraryOutdoorsDoor(HotSpot *hotspot) {
@@ -1885,14 +1885,14 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 		break;
 	case 294: {
 		HotSpot *floorTile = _room->findHotspotByExtra(462);
-		floorTile->actionFlags = ACTION_MASK_OPEN;
+		floorTile->actionFlags = kActionMaskOpen;
 		_room->changeHotSpot(*floorTile);
 		break;
 	}
 
 	case 307: {
 		HotSpot *stone = _room->findHotspotByExtra(90);
-		stone->actionFlags = ACTION_MASK_PICKUP;
+		stone->actionFlags = kActionMaskPickup;
 		_room->changeHotSpot(*stone);
 		break;
 	}
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 5cc7535edcf..9b0bd49770d 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -47,7 +47,7 @@ uint32 DialogManager::readTextBlock(
 	byte &outSpeakerId) {
 
 	uint32 pos = startPos;
-	outSpeakerId = ALFRED_COLOR; // Default to Alfred's color
+	outSpeakerId = kAlfredColor; // Default to Alfred's color
 	outText = "";
 
 	// Skip control bytes at start
@@ -75,7 +75,7 @@ uint32 DialogManager::readTextBlock(
 			pos++;
 		}
 		// Choice text is always spoken by ALFRED
-		outSpeakerId = ALFRED_COLOR;
+		outSpeakerId = kAlfredColor;
 		pos += 2;
 	}
 
@@ -162,7 +162,7 @@ Graphics::Surface DialogManager::getDialogueSurface(Common::Array<Common::String
 
 	int maxWidth = 0;
 	int height = dialogueLines.size() * 25; // Add some padding
-	for (int i = 0; i < dialogueLines.size(); i++) {
+	for (uint i = 0; i < dialogueLines.size(); i++) {
 		maxWidth = MAX(maxWidth, g_engine->_largeFont->getStringWidth(dialogueLines[i]));
 	}
 
@@ -170,7 +170,7 @@ Graphics::Surface DialogManager::getDialogueSurface(Common::Array<Common::String
 	s.create(maxWidth + 1, height + 1, Graphics::PixelFormat::createFormatCLUT8());
 	s.fillRect(s.getRect(), 255); // Clear surface
 
-	for (int i = 0; i < dialogueLines.size(); i++) {
+	for (uint i = 0; i < dialogueLines.size(); i++) {
 
 		int xPos = 0;
 		int yPos = i * 25; // Above sprite, adjust for line
@@ -184,7 +184,7 @@ Graphics::Surface DialogManager::getDialogueSurface(Common::Array<Common::String
 void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>> dialogueLines, byte speakerId) {
 	int16 xBasePos = 0;
 	int16 yBasePos = 0;
-	if (speakerId == ALFRED_COLOR) {
+	if (speakerId == kAlfredColor) {
 		if (g_engine->_state->getFlag(FLAG_FROM_INTRO) == true) {
 			debug("Setting special anim");
 			g_engine->_alfredState.setState(ALFRED_SPECIAL_ANIM);
@@ -238,7 +238,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 
 		int maxWidth = 0;
 		int height = textLines.size() * 24;
-		for (int i = 0; i < textLines.size(); i++) {
+		for (uint i = 0; i < textLines.size(); i++) {
 			maxWidth = MAX(maxWidth, g_engine->_largeFont->getStringWidth(textLines[i]));
 		}
 
@@ -859,7 +859,7 @@ uint32 DialogManager::processChoiceSelection(
 	state.lastSelectedChoice = (*choices)[selectedIndex];
 
 	if (state.lastSelectedChoice.isTerminator) {
-		displayDialogue(state.lastSelectedChoice.text, ALFRED_COLOR);
+		displayDialogue(state.lastSelectedChoice.text, kAlfredColor);
 		return dataSize; // End conversation
 	}
 
@@ -872,7 +872,7 @@ uint32 DialogManager::processChoiceSelection(
 	uint32 endPos = readTextBlock(data, dataSize, position, choiceText, choiceSpeakerId);
 
 	if (!choiceText.empty() && choiceText.size() > 1) {
-		displayDialogue(choiceText, ALFRED_COLOR);
+		displayDialogue(choiceText, kAlfredColor);
 		debug("Will check if choice should be disabled after displaying dialogue");
 		disableChoiceIfNeeded(choices, selectedIndex, data, dataSize, endPos, state);
 	}
@@ -988,7 +988,7 @@ void DialogManager::sayAlfred(Common::StringArray texts) {
 
 	_curSprite = nullptr;
 	Common::Array<Common::StringArray> textLines = wordWrap(texts);
-	displayDialogue(textLines, ALFRED_COLOR);
+	displayDialogue(textLines, kAlfredColor);
 }
 
 void DialogManager::sayAlfred(Description description) {
@@ -1009,7 +1009,7 @@ void DialogManager::say(Common::StringArray texts, byte spriteIndex) {
 	bool wasProcessed = processColorAndTrim(texts, speakerId);
 
 	if (wasProcessed) {
-		if (speakerId == ALFRED_COLOR) {
+		if (speakerId == kAlfredColor) {
 			sayAlfred(texts);
 			return;
 		} else {
@@ -1043,7 +1043,7 @@ bool DialogManager::processColorAndTrim(Common::StringArray &lines, byte &speake
 	speakerId = lines[0][1];
 
 	if (speakerMarker == '@') {
-		for (int i = 0; i < lines.size(); i++) {
+		for (uint i = 0; i < lines.size(); i++) {
 			// Remove first two marker bytes
 			if (i == 0) {
 				if (lines[i].size() > 2) {
@@ -1064,7 +1064,7 @@ bool DialogManager::processColorAndTrim(Common::StringArray &lines, byte &speake
 	return false;
 }
 
-bool isEndMarker(unsigned char char_byte) {
+bool isEndMarker(byte char_byte) {
 	return char_byte == CTRL_END_TEXT || char_byte == CTRL_END_CONVERSATION || char_byte == CTRL_ACTION_AND_END || char_byte == CTRL_GO_BACK;
 }
 
@@ -1084,7 +1084,7 @@ int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
 		isEnd = true;
 	}
 	if (pos < text.size() && !isEnd) {
-		if (text[pos] == CTRL_ACTION_AND_END) { // 0xF8 (-8) special case
+		if ((byte)text[pos] == CTRL_ACTION_AND_END) { // 0xF8 (-8) special case
 			wordLength += 3;
 		} else {
 			// Count all consecutive spaces
@@ -1101,7 +1101,7 @@ Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::Str
 	Common::Array<Common::Array<Common::String>> pages;
 	Common::Array<Common::String> currentPage;
 	Common::Array<Common::String> currentLine;
-	int charsRemaining = MAX_CHARS_PER_LINE;
+	int charsRemaining = kMaxCharsPerLine;
 	int position = 0;
 	int currentLineNum = 0;
 	while (position < text.size()) {
@@ -1114,10 +1114,10 @@ Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::Str
 			// Word is longer than the entire line - need to split
 			currentPage.push_back(joinStrings(currentLine, ""));
 			currentLine.clear();
-			charsRemaining = MAX_CHARS_PER_LINE;
+			charsRemaining = kMaxCharsPerLine;
 			currentLineNum++;
 
-			if (currentLineNum >= MAX_LINES) {
+			if (currentLineNum >= kMaxLines) {
 				pages.push_back(currentPage);
 				currentPage.clear();
 				currentLineNum = 0;
@@ -1137,10 +1137,10 @@ Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::Str
 				currentPage.push_back(lineText);
 				//  current_line = [' ' * trailing_spaces]
 				Common::String current_line(trailingSpaces, ' ');
-				charsRemaining = MAX_CHARS_PER_LINE - trailingSpaces;
+				charsRemaining = kMaxCharsPerLine - trailingSpaces;
 				currentLineNum += 1;
 
-				if (currentLineNum >= MAX_LINES) {
+				if (currentLineNum >= kMaxLines) {
 					pages.push_back(currentPage);
 					currentPage.clear();
 					currentLineNum = 0;
@@ -1193,7 +1193,7 @@ Common::Array<Common::StringArray> DialogManager::wordWrap(Common::StringArray t
 		debug("Wrapped line %s, %d into %d pages", thisLine.c_str(), thisLine.size(), wrapped.size());
 		for (uint j = 0; j < wrapped.size(); j++) {
 			for (int k = 0; k < wrapped[j].size(); k++) {
-				if (currentLineNum < MAX_LINES) {
+				if (currentLineNum < kMaxLines) {
 					currentPage.push_back(wrapped[j][k]);
 					currentLineNum++;
 				} else {
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 51e98d0f1dd..568d596b61f 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -37,20 +37,36 @@ namespace Pelrock {
 // Control character codes (negative values in signed char)
 #define CHAR_SPACE 0x20                  /* ' ' */
 #define CTRL_SPEAKER_ID 0x08             /* Next byte is speaker ID (color) */
-#define CTRL_END_TEXT 0xFD               /* End of text segment */
-#define CTRL_TEXT_TERMINATOR 0xFC        /* Text terminator */
-#define CTRL_DIALOGUE_MARKER 0xF1        /* Choice marker that sticks */
-#define CTRL_DISABLED_CHOICE 0xFA        /* Disabled choice marker */
-#define CTRL_PAGE_BREAK_CONV 0xF9        /* Page break in conversation */
-#define CTRL_ACTION_AND_END 0xF8         /* Action trigger */
-#define CTRL_END_BRANCH 0xF7             /* End of branch */
-#define CTRL_LINE_CONTINUE 0xF6          /* Line continue/newline */
-#define CTRL_ALT_END_MARKER_1 0xF5       /* Alt end marker - do nothing */
-#define CTRL_END_CONVERSATION 0xF4       /* End conversation and disable option */
-#define CTRL_DIALOGUE_MARKER_ONEOFF 0xFB /* Alt choice marker that disappears */
-#define CTRL_GO_BACK 0xF0                /* Go back in conversation */
-#define CTRL_ACTION_AND_CONTINUE 0xEB       /* Action-and-continue: dispatch action, conversation keeps going (unlike 0xF8 which exits) */
-#define CTRL_ALT_SPEAKER_ROOT 0xFE       /* Separates conversations from different speakers */
+const byte kCtrlEndText             = 0xFD; /* End of text segment */
+const byte kCtrlTextTerminator      = 0xFC; /* Text terminator */
+const byte kCtrlDialogueMarker      = 0xF1; /* Choice marker that sticks */
+const byte kCtrlDisabledChoice      = 0xFA; /* Disabled choice marker */
+const byte kCtrlPageBreakConv       = 0xF9; /* Page break in conversation */
+const byte kCtrlActionAndEnd        = 0xF8; /* Action trigger */
+const byte kCtrlEndBranch           = 0xF7; /* End of branch */
+const byte kCtrlLineContinue        = 0xF6; /* Line continue/newline */
+const byte kCtrlAltEndMarker1       = 0xF5; /* Alt end marker - do nothing */
+const byte kCtrlEndConversation     = 0xF4; /* End conversation and disable option */
+const byte kCtrlDialogueMarkerOneoff = 0xFB; /* Alt choice marker that disappears */
+const byte kCtrlGoBack              = 0xF0; /* Go back in conversation */
+const byte kCtrlActionAndContinue   = 0xEB; /* Action-and-continue: dispatch action, conversation keeps going (unlike 0xF8 which exits) */
+const byte kCtrlAltSpeakerRoot      = 0xFE; /* Separates conversations from different speakers */
+
+// Keep old names as aliases for compatibility
+#define CTRL_END_TEXT           kCtrlEndText
+#define CTRL_TEXT_TERMINATOR    kCtrlTextTerminator
+#define CTRL_DIALOGUE_MARKER    kCtrlDialogueMarker
+#define CTRL_DISABLED_CHOICE    kCtrlDisabledChoice
+#define CTRL_PAGE_BREAK_CONV    kCtrlPageBreakConv
+#define CTRL_ACTION_AND_END     kCtrlActionAndEnd
+#define CTRL_END_BRANCH         kCtrlEndBranch
+#define CTRL_LINE_CONTINUE      kCtrlLineContinue
+#define CTRL_ALT_END_MARKER_1   kCtrlAltEndMarker1
+#define CTRL_END_CONVERSATION   kCtrlEndConversation
+#define CTRL_DIALOGUE_MARKER_ONEOFF kCtrlDialogueMarkerOneoff
+#define CTRL_GO_BACK            kCtrlGoBack
+#define CTRL_ACTION_AND_CONTINUE kCtrlActionAndContinue
+#define CTRL_ALT_SPEAKER_ROOT   kCtrlAltSpeakerRoot
 
 // Helper structures for conversation state management
 struct ConversationState {
diff --git a/engines/pelrock/events.h b/engines/pelrock/events.h
index fe25ba04382..6b2c54a700c 100644
--- a/engines/pelrock/events.h
+++ b/engines/pelrock/events.h
@@ -41,8 +41,8 @@ public:
 	bool _longClicked = false;
 	bool _rightMouseClicked = false;
 	bool _popupSelectionMode = false;
-	bool _leftMouseButton = 0;
-	bool _rightMouseButton = 0;
+	bool _leftMouseButton = false;
+	bool _rightMouseButton = false;
 	Common::KeyCode _lastKeyEvent = Common::KEYCODE_INVALID;
 	PelrockEventManager();
 	void pollEvent();
diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index d0c1c79b1eb..062de9031bb 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -210,7 +210,7 @@ void GraphicsManager::drawColoredTexts(Graphics::ManagedSurface *surface, const
 	int currentX = x;
 	byte currentColor = 255;
 
-	for(int i =0; i < text.size(); i++) {
+	for (uint i = 0; i < text.size(); i++) {
 		drawColoredText(surface, text[i], currentX, y + i * (font->getFontHeight() + yPadding), w, currentColor, font);
 	}
 }
@@ -219,7 +219,7 @@ void GraphicsManager::drawColoredTexts(Graphics::ManagedSurface &buf, const Comm
 	int currentX = x;
 	byte currentColor = 255;
 
-	for(int i =0; i < text.size(); i++) {
+	for (uint i = 0; i < text.size(); i++) {
 		drawColoredText(buf, text[i], currentX, y + i * (font->getFontHeight() + yPadding), w, currentColor, font);
 	}
 }
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 05f5a82f24f..1a2da8ac59f 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -25,25 +25,25 @@
 
 namespace Pelrock {
 
-static const uint32_t cursor_offsets[5] = {
+static const uint32 cursor_offsets[5] = {
 	0x0FDDFD,
 	0x0FDCDD,
 	0x0FDF1D,
 	0x0FE33D,
 	0x367EF0};
 
-static const uint32_t kBalloonFramesOffset = 2176936;
-static const uint32_t kBalloonFramesSize = 24950;
+static const uint32 kBalloonFramesOffset = 2176936;
+static const uint32 kBalloonFramesSize = 24950;
 
-static const uint32_t ALFRED7_ALFRED_COMB_R = 67768;
-static const uint32_t ALFRED7_ALFRED_COMB_L = 88408;
+static const uint32 ALFRED7_ALFRED_COMB_R = 67768;
+static const uint32 ALFRED7_ALFRED_COMB_L = 88408;
 
-static const uint32_t kAlternateSettingsMenuOffset = 910097;
-static const uint32_t kAlternateSettingsPaletteOffset = 1038141; // 640 * 480
-static const uint32_t kSettingsPaletteOffset = 0x2884c2;         // 640 * 480
+static const uint32 kAlternateSettingsMenuOffset = 910097;
+static const uint32 kAlternateSettingsPaletteOffset = 1038141; // 640 * 480
+static const uint32 kSettingsPaletteOffset = 0x2884c2;         // 640 * 480
 
-#define DESCRIPTION_BASE_OFFSET 0x4715D
-#define NUM_DESCRIPTIONS 113
+const uint32 kDescriptionBaseOffset = 0x4715D;
+const uint16 kNumDescriptions = 113;
 
 static const uint32 kInventoryDescriptionsOffset = 0x4715E;
 static const uint32 kInventoryDescriptionsSize = 7868;
@@ -55,7 +55,7 @@ static const uint32 kAlfredResponsesSize = 12143;
 static const uint32 kCreditsOffset = 0x49F60;
 static const uint32 kCreditsSize = 2540;
 
-const uint32_t pegatina_offsets[137] = {
+static const uint32 pegatina_offsets[137] = {
 	0x000000, 0x00005B, 0x0000B6, 0x000298, 0x00047A, 0x0023C8, 0x004316, 0x004376,
 	0x005119, 0x005EBC, 0x0083ED, 0x008529, 0x0092C4, 0x00A3AA, 0x00B490, 0x00B6A6,
 	0x00C05A, 0x00CA0E, 0x00D3D0, 0x00D46E, 0x00F036, 0x00FB8F, 0x00FC55, 0x0119D7,
@@ -75,7 +75,7 @@ const uint32_t pegatina_offsets[137] = {
 	0x07ECED, 0x07F52F, 0x07FD71, 0x080591, 0x080B24, 0x080B84, 0x080F39, 0x0812F5,
 	0x0816B1};
 
-const byte pegatina_rooms[140] = {
+static const byte pegatina_rooms[140] = {
 	0, 0, 0, 0, 0, 0, 0,                            // Sprites 0-6: Room 0
 	2, 2,                                           // Sprites 7-8: Room 2
 	3, 3, 3, 3, 3, 3, 3, 3,                         // Sprites 9-16: Room 3
@@ -319,8 +319,8 @@ enum TextIndices {
 	LASPUERTAS_DELCIELO,
 };
 
-// Description offsets relative to DESCRIPTION_BASE_OFFSET
-const uint16_t description_offsets[NUM_DESCRIPTIONS] = {
+// Description offsets relative to kDescriptionBaseOffset
+static const uint16 description_offsets[kNumDescriptions] = {
 	0x0000, // Object 0: Historia de la Princesa Zenna y su amante insatisfecho
 	0x0058, // Object 1: Nombre: Alfred Pelrock
 	0x00C4, // Object 2: La tipica tarjeta por la que te sacan commisiones
@@ -444,7 +444,7 @@ struct ExtraImages {
 	byte numChunks;
 };
 
-const ExtraImages extraScreens[] = {
+static const ExtraImages extraScreens[] = {
 	{0x00, // 0 - Portrait above bed
 	 0x7984,
 	 8},
diff --git a/engines/pelrock/pathfinding.cpp b/engines/pelrock/pathfinding.cpp
index e7a89c34818..b1dfdeb790c 100644
--- a/engines/pelrock/pathfinding.cpp
+++ b/engines/pelrock/pathfinding.cpp
@@ -27,24 +27,24 @@
 
 namespace Pelrock {
 
-Common::String printMovementFlags(uint8_t flags) {
+Common::String printMovementFlags(byte flags) {
 	Common::String result;
-	if (flags & MOVE_HORIZ) {
+	if (flags & kMoveHoriz) {
 		result += "HORIZ ";
 	}
-	if (flags & MOVE_VERT) {
+	if (flags & kMoveVert) {
 		result += "VERT ";
 	}
-	if (flags & MOVE_DOWN) {
+	if (flags & kMoveDown) {
 		result += "DOWN ";
 	}
-	if (flags & MOVE_LEFT) {
+	if (flags & kMoveLeft) {
 		result += "LEFT ";
 	}
-	if (flags & MOVE_UP) {
+	if (flags & kMoveUp) {
 		result += "UP ";
 	}
-	if (flags & MOVE_RIGHT) {
+	if (flags & kMoveRight) {
 		result += "RIGHT ";
 	}
 	return result;
@@ -52,11 +52,11 @@ Common::String printMovementFlags(uint8_t flags) {
 
 bool findPath(int sourceX, int sourceY, int targetX, int targetY, Common::Array<WalkBox> &walkboxes, PathContext *context, HotSpot *hotspot) {
 
-	if (context->pathBuffer == NULL) {
-		context->pathBuffer = (uint8_t *)malloc(MAX_PATH_LENGTH);
+	if (context->pathBuffer == nullptr) {
+		context->pathBuffer = (byte *)malloc(kMaxPathLength);
 	}
-	if (context->movementBuffer == NULL) {
-		context->movementBuffer = (MovementStep *)malloc(MAX_MOVEMENT_STEPS * sizeof(MovementStep));
+	if (context->movementBuffer == nullptr) {
+		context->movementBuffer = (MovementStep *)malloc(kMaxMovementSteps * sizeof(MovementStep));
 	}
 
 	int startX = sourceX;
@@ -66,8 +66,8 @@ bool findPath(int sourceX, int sourceY, int targetX, int targetY, Common::Array<
 	targetY = target.y;
 	// debug("Startx= %d, starty= %d, destx= %d, desty= %d", startX, startY, targetX, targetY);
 
-	uint8_t startBox = findWalkboxForPoint(walkboxes, startX, startY);
-	uint8_t destBox = findWalkboxForPoint(walkboxes, targetX, targetY);
+	byte startBox = findWalkboxForPoint(walkboxes, startX, startY);
+	byte destBox = findWalkboxForPoint(walkboxes, targetX, targetY);
 
 	// debug("Pathfinding from (%d, %d) in box %d to (%d, %d) in box %d\n", startX, startY, startBox, targetX, targetY, destBox);
 	// Check if both points are in valid walkboxes
@@ -82,18 +82,18 @@ bool findPath(int sourceX, int sourceY, int targetX, int targetY, Common::Array<
 		direct_step.flags = 0;
 		if (startX < targetX) {
 			direct_step.distanceX = targetX - startX;
-			direct_step.flags |= MOVE_RIGHT;
+			direct_step.flags |= kMoveRight;
 		} else {
 			direct_step.distanceX = startX - targetX;
-			direct_step.flags |= MOVE_LEFT;
+			direct_step.flags |= kMoveLeft;
 		}
 
 		if (startY < targetY) {
 			direct_step.distanceY = targetY - startY;
-			direct_step.flags |= MOVE_DOWN;
+			direct_step.flags |= kMoveDown;
 		} else {
 			direct_step.distanceY = startY - targetY;
-			direct_step.flags |= MOVE_UP;
+			direct_step.flags |= kMoveUp;
 		}
 
 		context->movementBuffer[0] = direct_step;
@@ -193,8 +193,8 @@ Common::Point calculateWalkTarget(Common::Array<WalkBox> &walkboxes,
 	return target;
 }
 
-uint8_t findWalkboxForPoint(Common::Array<WalkBox> &walkboxes, uint16_t x, uint16_t y) {
-	for (uint8_t i = 0; i < walkboxes.size(); i++) {
+byte findWalkboxForPoint(Common::Array<WalkBox> &walkboxes, uint16 x, uint16 y) {
+	for (byte i = 0; i < walkboxes.size(); i++) {
 		if (isPointInWalkbox(&walkboxes[i], x, y)) {
 			return i;
 		}
@@ -202,7 +202,7 @@ uint8_t findWalkboxForPoint(Common::Array<WalkBox> &walkboxes, uint16_t x, uint1
 	return 0xFF; // Not found
 }
 
-bool isPointInWalkbox(WalkBox *box, uint16_t x, uint16_t y) {
+bool isPointInWalkbox(WalkBox *box, uint16 x, uint16 y) {
 	return (x >= box->x &&
 			x <= box->x + box->w &&
 			y >= box->y &&
@@ -213,10 +213,10 @@ bool isPointInWalkbox(WalkBox *box, uint16_t x, uint16_t y) {
  * Check if two walkboxes overlap or touch (are adjacent)
  */
 bool areWalkboxesAdjacent(WalkBox *box1, WalkBox *box2) {
-	uint16_t box1_x_max = box1->x + box1->w;
-	uint16_t box1_y_max = box1->y + box1->h;
-	uint16_t box2_x_max = box2->x + box2->w;
-	uint16_t box2_y_max = box2->y + box2->h;
+	uint16 box1_x_max = box1->x + box1->w;
+	uint16 box1_y_max = box1->y + box1->h;
+	uint16 box2_x_max = box2->x + box2->w;
+	uint16 box2_y_max = box2->y + box2->h;
 
 	// Check if X ranges overlap
 	bool xOverlap = (box1->x <= box2_x_max) && (box2->x <= box1_x_max);
@@ -227,14 +227,14 @@ bool areWalkboxesAdjacent(WalkBox *box1, WalkBox *box2) {
 	return xOverlap && yOverlap;
 }
 
-uint8_t getAdjacentWalkbox(Common::Array<WalkBox> &walkboxes, uint8_t currentBoxIndex) {
+byte getAdjacentWalkbox(Common::Array<WalkBox> &walkboxes, byte currentBoxIndex) {
 	WalkBox *currentBox = &walkboxes[currentBoxIndex];
 
 	// Mark current walkbox as visited
 	currentBox->flags = 0x01;
 
 	// Search for adjacent unvisited walkbox
-	for (uint8_t i = 0; i < walkboxes.size(); i++) {
+	for (byte i = 0; i < walkboxes.size(); i++) {
 		// Skip current walkbox
 		if (i == currentBoxIndex) {
 			continue;
@@ -260,10 +260,10 @@ void clearVisitedFlags(Common::Array<WalkBox> &walkboxes) {
 	}
 }
 
-uint16_t buildWalkboxPath(Common::Array<WalkBox> &walkboxes, uint8_t startBox, uint8_t destBox, uint8_t *pathBuffer) {
+uint16 buildWalkboxPath(Common::Array<WalkBox> &walkboxes, byte startBox, byte destBox, byte *pathBuffer) {
 
-	uint16_t pathIndex = 0;
-	uint8_t currentBox = startBox;
+	uint16 pathIndex = 0;
+	byte currentBox = startBox;
 
 	// Initialize path with start walkbox
 	pathBuffer[pathIndex++] = startBox;
@@ -272,8 +272,8 @@ uint16_t buildWalkboxPath(Common::Array<WalkBox> &walkboxes, uint8_t startBox, u
 	clearVisitedFlags(walkboxes);
 
 	// Breadth-first search through walkboxes
-	while (currentBox != destBox && pathIndex < MAX_PATH_LENGTH - 1) {
-		uint8_t nextBox = getAdjacentWalkbox(walkboxes, currentBox);
+	while (currentBox != destBox && pathIndex < kMaxPathLength - 1) {
+		byte nextBox = getAdjacentWalkbox(walkboxes, currentBox);
 
 		if (nextBox == 0xFF) {
 			// Dead end - backtrack
@@ -296,7 +296,7 @@ uint16_t buildWalkboxPath(Common::Array<WalkBox> &walkboxes, uint8_t startBox, u
 	}
 
 	// Terminate path
-	pathBuffer[pathIndex] = PATH_END;
+	pathBuffer[pathIndex] = kPathEnd;
 	debug("Built walkbox path of length %d", pathIndex);
 	return pathIndex;
 }
@@ -304,7 +304,7 @@ uint16_t buildWalkboxPath(Common::Array<WalkBox> &walkboxes, uint8_t startBox, u
 /**
  * Calculate movement needed to reach a target within a walkbox
  */
-void calculateMovementToTarget(uint16_t currentX, uint16_t currentY, uint16_t targetX, uint16_t targetY, WalkBox *box, MovementStep *step) {
+void calculateMovementToTarget(uint16 currentX, uint16 currentY, uint16 targetX, uint16 targetY, WalkBox *box, MovementStep *step) {
 	step->flags = 0;
 	step->distanceX = 0;
 	step->distanceY = 0;
@@ -313,22 +313,22 @@ void calculateMovementToTarget(uint16_t currentX, uint16_t currentY, uint16_t ta
 	if (currentX < box->x) {
 		// Need to move right to enter walkbox
 		step->distanceX = box->x - currentX;
-		step->flags |= MOVE_RIGHT;
+		step->flags |= kMoveRight;
 	} else if (currentX > box->x + box->w) {
 		// Need to move left to enter walkbox
 		step->distanceX = currentX - (box->x + box->w);
-		step->flags |= MOVE_LEFT;
+		step->flags |= kMoveLeft;
 	}
 
 	// Calculate vertical movement
 	if (currentY < box->y) {
 		// Need to move down to enter walkbox
 		step->distanceY = box->y - currentY;
-		step->flags |= MOVE_DOWN;
+		step->flags |= kMoveDown;
 	} else if (currentY > box->y + box->h) {
 		// Need to move up to enter walkbox
 		step->distanceY = currentY - (box->y + box->h);
-		step->flags |= MOVE_UP;
+		step->flags |= kMoveUp;
 	}
 }
 
@@ -336,19 +336,19 @@ void calculateMovementToTarget(uint16_t currentX, uint16_t currentY, uint16_t ta
  * Generate movement steps from walkbox path
  * Returns: number of movement steps generated
  */
-uint16_t generateMovementSteps(Common::Array<WalkBox> &walkboxes,
-							   uint8_t *pathBuffer,
-							   uint16_t pathLength,
-							   uint16_t startX, uint16_t startY,
-							   uint16_t destX, uint16_t destY,
+uint16 generateMovementSteps(Common::Array<WalkBox> &walkboxes,
+							   byte *pathBuffer,
+							   uint16 pathLength,
+							   uint16 startX, uint16 startY,
+							   uint16 destX, uint16 destY,
 							   MovementStep *movementBuffer) {
-	uint16_t currentX = startX;
-	uint16_t currentY = startY;
-	uint16_t movementIndex = 0;
+	uint16 currentX = startX;
+	uint16 currentY = startY;
+	uint16 movementIndex = 0;
 
 	// Generate movements for each walkbox in path
-	for (uint16_t i = 0; i < pathLength && pathBuffer[i] != PATH_END; i++) {
-		uint8_t boxIndex = pathBuffer[i];
+	for (uint16 i = 0; i < pathLength && pathBuffer[i] != kPathEnd; i++) {
+		byte boxIndex = pathBuffer[i];
 		WalkBox *box = &walkboxes[boxIndex];
 
 		MovementStep step;
@@ -358,15 +358,15 @@ uint16_t generateMovementSteps(Common::Array<WalkBox> &walkboxes,
 			movementBuffer[movementIndex++] = step;
 
 			// Update current position
-			if (step.flags & MOVE_RIGHT) {
+			if (step.flags & kMoveRight) {
 				currentX = box->x;
-			} else if (step.flags & MOVE_LEFT) {
+			} else if (step.flags & kMoveLeft) {
 				currentX = box->x + box->w;
 			}
 
-			if (step.flags & MOVE_DOWN) {
+			if (step.flags & kMoveDown) {
 				currentY = box->y;
-			} else if (step.flags & MOVE_UP) {
+			} else if (step.flags & kMoveUp) {
 				currentY = box->y + box->h;
 			}
 		}
@@ -378,20 +378,20 @@ uint16_t generateMovementSteps(Common::Array<WalkBox> &walkboxes,
 
 	if (currentX < destX) {
 		final_step.distanceX = destX - currentX;
-		final_step.flags |= MOVE_RIGHT;
+		final_step.flags |= kMoveRight;
 	} else if (currentX > destX) {
 		final_step.distanceX = currentX - destX;
-		final_step.flags |= MOVE_LEFT;
+		final_step.flags |= kMoveLeft;
 	} else {
 		final_step.distanceX = 0;
 	}
 
 	if (currentY < destY) {
 		final_step.distanceY = destY - currentY;
-		final_step.flags |= MOVE_DOWN;
+		final_step.flags |= kMoveDown;
 	} else if (currentY > destY) {
 		final_step.distanceY = currentY - destY;
-		final_step.flags |= MOVE_UP;
+		final_step.flags |= kMoveUp;
 	} else {
 		final_step.distanceY = 0;
 	}
diff --git a/engines/pelrock/pathfinding.h b/engines/pelrock/pathfinding.h
index 6b7a3a628ef..f6f3d2c1063 100644
--- a/engines/pelrock/pathfinding.h
+++ b/engines/pelrock/pathfinding.h
@@ -39,11 +39,11 @@ bool findPath(int sourceX, int sourceY, int targetX, int targetY, Common::Array<
  * @return                  Calculated walk target point.
  */
 Common::Point calculateWalkTarget(Common::Array<WalkBox> &walkboxes, int sourceX, int sourceY, bool mouseHoverState, HotSpot *hotspot);
-uint8_t findWalkboxForPoint(Common::Array<WalkBox> &walkboxes, uint16_t x, uint16_t y);
-uint8_t getAdjacentWalkbox(Common::Array<WalkBox> &walkboxes, uint8_t current_box_index);
-uint16_t buildWalkboxPath(Common::Array<WalkBox> &walkboxes, uint8_t start_box, uint8_t dest_box, uint8_t *path_buffer);
-uint16_t generateMovementSteps(Common::Array<WalkBox> &walkboxes, uint8_t *path_buffer, uint16_t path_length, uint16_t start_x, uint16_t start_y, uint16_t dest_x, uint16_t dest_y, MovementStep *movement_buffer);
-bool isPointInWalkbox(WalkBox *box, uint16_t x, uint16_t y);
+byte findWalkboxForPoint(Common::Array<WalkBox> &walkboxes, uint16 x, uint16 y);
+byte getAdjacentWalkbox(Common::Array<WalkBox> &walkboxes, byte current_box_index);
+uint16 buildWalkboxPath(Common::Array<WalkBox> &walkboxes, byte start_box, byte dest_box, byte *path_buffer);
+uint16 generateMovementSteps(Common::Array<WalkBox> &walkboxes, byte *path_buffer, uint16 path_length, uint16 start_x, uint16 start_y, uint16 dest_x, uint16 dest_y, MovementStep *movement_buffer);
+bool isPointInWalkbox(WalkBox *box, uint16 x, uint16 y);
 void clearVisitedFlags(Common::Array<WalkBox> &walkboxes);
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index d69c571275d..e1a594d3234 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -71,6 +71,14 @@ PelrockEngine::~PelrockEngine() {
 	delete _dialog;
 	delete _menu;
 	delete _graphics;
+	delete _state;
+	delete[] _alfredSprite;
+	delete[] _inventoryOverlayState.arrows[0];
+	delete[] _inventoryOverlayState.arrows[1];
+	// Free path-finding buffers (allocated via malloc in findPath)
+	free(_currentContext.pathBuffer);
+	free(_currentContext.movementBuffer);
+	free(_currentContext.compressed_path);
 }
 
 uint32 PelrockEngine::getFeatures() const {
@@ -196,25 +204,25 @@ Common::Array<VerbIcon> PelrockEngine::availableActions(HotSpot *hotspot) {
 	Common::Array<VerbIcon> verbs;
 	verbs.push_back(LOOK);
 
-	if (hotspot->actionFlags & ACTION_MASK_OPEN) {
+	if (hotspot->actionFlags & kActionMaskOpen) {
 		verbs.push_back(OPEN);
 	}
-	if (hotspot->actionFlags & ACTION_MASK_CLOSE) {
+	if (hotspot->actionFlags & kActionMaskClose) {
 		verbs.push_back(CLOSE);
 	}
-	if (hotspot->actionFlags & ACTION_MASK_UNKNOWN) {
+	if (hotspot->actionFlags & kActionMaskUnknown) {
 		verbs.push_back(UNKNOWN);
 	}
-	if (hotspot->actionFlags & ACTION_MASK_PICKUP) {
+	if (hotspot->actionFlags & kActionMaskPickup) {
 		verbs.push_back(PICKUP);
 	}
-	if (hotspot->actionFlags & ACTION_MASK_TALK) {
+	if (hotspot->actionFlags & kActionMaskTalk) {
 		verbs.push_back(TALK);
 	}
-	if (hotspot->actionFlags & ACTION_MASK_PUSH) {
+	if (hotspot->actionFlags & kActionMaskPush) {
 		verbs.push_back(PUSH);
 	}
-	if (hotspot->actionFlags & ACTION_MASK_PULL) {
+	if (hotspot->actionFlags & kActionMaskPull) {
 		verbs.push_back(PULL);
 	}
 	return verbs;
@@ -356,7 +364,7 @@ bool PelrockEngine::renderScene(int overlayMode) {
 void PelrockEngine::mouseHoverForMap() {
 	if (_room->_currentRoomNumber == 21 && !_hoveredMapLocation.empty()) {
 		Common::Rect r = _largeFont->getBoundingBox(_hoveredMapLocation.c_str());
-		drawText(_compositeBuffer, _largeFont, _hoveredMapLocation.c_str(), _events->_mouseX - r.width() / 2, _events->_mouseY - r.height(), 640, ALFRED_COLOR);
+		drawText(_compositeBuffer, _largeFont, _hoveredMapLocation.c_str(), _events->_mouseX - r.width() / 2, _events->_mouseY - r.height(), 640, kAlfredColor);
 	}
 }
 
@@ -457,7 +465,7 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 		int startY = anim.startY;
 		// debug("Checking passerby anim %d for sprite %d, direction %d", _room->_passerByAnims->currentAnimIndex, spriteIndex, direction);
 		Sprite *sprite = _room->findSpriteByIndex(spriteIndex);
-		if (direction == PASSERBY_RIGHT) {
+		if (direction == kPasserbyRight) {
 			// debug("Checking passerby anim for sprite %d moving RIGHT, curpos is %d", spriteIndex, sprite->x);
 			if (sprite->x >= anim.resetCoord) {
 				sprite->x = startX;
@@ -467,7 +475,7 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 				sprite->animData[0].curFrame = 0;
 				_room->_passerByAnims->latch = false;
 			}
-		} else if (direction == PASSERBY_LEFT) {
+		} else if (direction == kPasserbyLeft) {
 			// debug("Checking passerby anim for sprite %d moving LEFT, curpos is %d", spriteIndex, sprite->x);
 
 			if (sprite->x <= anim.resetCoord) {
@@ -478,7 +486,7 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 				sprite->animData[0].curFrame = 0;
 				_room->_passerByAnims->latch = false;
 			}
-		} else if (direction == PASSERBY_DOWN) {
+		} else if (direction == kPasserbyDown) {
 			// debug("Checking passerby anim for sprite %d moving DOWN, curpos is %d, reset %d", spriteIndex, sprite->y, anim.resetCoord);
 			if (sprite->y >= anim.resetCoord) {
 				sprite->x = startX;
@@ -939,21 +947,21 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 	case ALFRED_WALKING: {
 		MovementStep step = _currentContext.movementBuffer[_currentStep];
 		if (step.distanceX > 0) {
-			if (step.flags & MOVE_RIGHT) {
+			if (step.flags & kMoveRight) {
 				_alfredState.direction = ALFRED_RIGHT;
 				_alfredState.x += MIN(_alfredState.movementSpeedX, step.distanceX);
 			}
-			if (step.flags & MOVE_LEFT) {
+			if (step.flags & kMoveLeft) {
 				_alfredState.direction = ALFRED_LEFT;
 				_alfredState.x -= MIN(_alfredState.movementSpeedX, step.distanceX);
 			}
 		}
 		if (step.distanceY > 0) {
-			if (step.flags & MOVE_DOWN) {
+			if (step.flags & kMoveDown) {
 				_alfredState.direction = ALFRED_DOWN;
 				_alfredState.y += MIN(_alfredState.movementSpeedY, step.distanceY);
 			}
-			if (step.flags & MOVE_UP) {
+			if (step.flags & kMoveUp) {
 				_alfredState.direction = ALFRED_UP;
 				_alfredState.y -= MIN(_alfredState.movementSpeedY, step.distanceY);
 			}
@@ -1286,7 +1294,7 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	}
 }
 
-void applyMovement(int16_t *x, int16_t *y, int8_t *z, uint16_t flags) {
+void applyMovement(int16 *x, int16 *y, int8 *z, uint16 flags) {
 	// X-axis movement
 	if (flags & 0x10) {            // Bit 4: X movement enabled
 		int amount = flags & 0x07; // Bits 0-2: pixels per frame
@@ -2644,14 +2652,14 @@ void PelrockEngine::credits() {
 			/**
 			 * Last line is less indented for some reason, so skip that for calculation
 			 */
-			for (int i = 0; i < creditTexts[page].size(); i++) {
+			for (uint i = 0; i < creditTexts[page].size(); i++) {
 				maxWidth = MAX(maxWidth, g_engine->_largeFont->getStringWidth(creditTexts[page][i]));
 			}
 
 			int startX = 320 - (maxWidth / 2);
 			int startY = (400 - s.getRect().height()) / 2 - 10;
 
-			for (int i = 0; i < creditTexts[page].size(); i++) {
+			for (uint i = 0; i < creditTexts[page].size(); i++) {
 				// subtract that extra negative identation
 				int xPos = i == creditTexts[page].size() - 1 ? startX - 10 : startX;
 				int yPos = i * 25; // Above sprite, adjust for line
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 504ca209d28..894a1ffec2f 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -81,8 +81,20 @@ ResourceManager::~ResourceManager() {
 		delete[] alfredIdle[i];
 	}
 
+	for (int i = 0; i < 11; i++) {
+		delete[] alfredCombFrames[0][i];
+		delete[] alfredCombFrames[1][i];
+	}
 	delete[] alfredCombFrames[0];
 	delete[] alfredCombFrames[1];
+
+	for (int i = 0; i < 4; i++) {
+		for (int j = 0; j < 9; j++) {
+			delete[] alfredCrawlFrames[i][j];
+		}
+		delete[] alfredCrawlFrames[i];
+	}
+
 	delete[] _inventoryIcons;
 }
 
@@ -92,7 +104,7 @@ void ResourceManager::loadCursors() {
 		error("Couldnt find file ALFRED.7");
 	}
 	for (int i = 0; i < 5; i++) {
-		uint32_t cursorOffset = cursor_offsets[i];
+		uint32 cursorOffset = cursor_offsets[i];
 		alfred7File.seek(cursorOffset);
 		_cursorMasks[i] = new byte[kCursorSize];
 		alfred7File.read(_cursorMasks[i], kCursorSize);
@@ -108,10 +120,10 @@ void ResourceManager::loadInteractionIcons() {
 
 	alfred7File.seek(kBalloonFramesOffset, SEEK_SET);
 
-	uint32_t totalBalloonSize = kBalloonWidth * kBalloonHeight * kBalloonFrames;
+	uint32 totalBalloonSize = kBalloonWidth * kBalloonHeight * kBalloonFrames;
 	_popUpBalloon = new byte[totalBalloonSize];
 
-	uint32_t compressedSize = kBalloonFramesSize;
+	uint32 compressedSize = kBalloonFramesSize;
 
 	byte *raw = new byte[compressedSize];
 	alfred7File.read(raw, compressedSize);
@@ -140,13 +152,13 @@ void ResourceManager::loadAlfredAnims() {
 		return;
 	}
 	int alfred3Size = alfred3.size();
-	unsigned char *bufferFile = (unsigned char *)malloc(alfred3Size);
+	byte *bufferFile = (byte *)malloc(alfred3Size);
 	alfred3.seek(0, SEEK_SET);
 	alfred3.read(bufferFile, alfred3Size);
 	alfred3.close();
 
-	uint32_t capacity = 3060 * 102 + 2340 * 55;
-	unsigned char *completePic = new unsigned char[capacity];
+	uint32 capacity = 3060 * 102 + 2340 * 55;
+	byte *completePic = new byte[capacity];
 	rleDecompress(bufferFile, alfred3Size, 0, capacity, &completePic);
 
 	byte *stdFramesPic = new byte[3060 * 102];
@@ -208,6 +220,9 @@ void ResourceManager::loadAlfredAnims() {
 		}
 	}
 
+	delete[] crawlFramesPic;
+	delete[] stdFramesPic;
+	delete[] completePic;
 	free(bufferFile);
 
 
@@ -245,6 +260,8 @@ void ResourceManager::loadAlfredAnims() {
 		extractSingleFrame(alfredCombLeft, alfredCombFrames[1][i], i, kAlfredFrameWidth, kAlfredFrameHeight);
 	}
 
+	free(alfredCombRight);
+	free(alfredCombLeft);
 	free(alfredCombRightRaw);
 	free(alfredCombLeftRaw);
 
@@ -355,6 +372,7 @@ void ResourceManager::loadHardcodedText() {
 	exe.seek(kConversationTerminatorOffset, SEEK_SET);
 	exe.read(terminatorBuffer, 39);
 	_conversationTerminator = Common::String((const char *)terminatorBuffer, 39);
+	delete[] terminatorBuffer;
 	delete[] descBuffer;
 	exe.close();
 }
@@ -527,7 +545,7 @@ void ResourceManager::mergeRleBlocks(Common::SeekableReadStream *stream, uint32
 		byte *thisBlock = nullptr;
 		size_t blockSize = 0;
 		readUntilBuda(stream, stream->pos(), thisBlock, blockSize);
-		uint8_t *block_data = nullptr;
+		byte *block_data = nullptr;
 		size_t decompressedSize = rleDecompress(thisBlock, blockSize, 0, 640 * 400, &block_data, true);
 		// debug("Decompressed block %d: %zu bytes, total %zu", i, decompressedSize, combined_size + decompressedSize);
 		if (combined_size + decompressedSize > 640 * 400) {
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 2c52568a54b..7a613ee0042 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -74,18 +74,18 @@ void RoomManager::getBackground(Common::File *roomFile, int roomOffset, byte *ba
 	// get screen
 	size_t combined_size = 0;
 	for (int pair_idx = 0; pair_idx < 8; pair_idx++) {
-		uint32_t pair_offset = roomOffset + (pair_idx * 8);
+		uint32 pair_offset = roomOffset + (pair_idx * 8);
 		if (pair_offset + 8 > roomFile->size())
 			continue;
 
 		roomFile->seek(pair_offset, SEEK_SET);
-		uint32_t offset = roomFile->readUint32LE();
-		uint32_t size = roomFile->readUint32LE();
+		uint32 offset = roomFile->readUint32LE();
+		uint32 size = roomFile->readUint32LE();
 		if (offset > 0 && size > 0 && offset < roomFile->size()) {
 			byte *data = new byte[size];
 			roomFile->seek(offset, SEEK_SET);
 			roomFile->read(data, size);
-			uint8_t *block_data = NULL;
+			byte *block_data = nullptr;
 			size_t block_size = rleDecompress(data, size, 0, 640 * 400, &block_data);
 			if (block_size + combined_size > 640 * 400) {
 				debug(" Warning: decompressed background size exceeds buffer size!");
@@ -365,7 +365,7 @@ PaletteAnim *RoomManager::getPaletteAnimForRoom(int roomNumber) {
 		debug("Could not open JUEGO.EXE for palette animation!");
 		return nullptr;
 	}
-	uint32_t offset = 0;
+	uint32 offset = 0;
 	switch (roomNumber) {
 	case 0:
 		offset = 0x0004B88C;
@@ -471,7 +471,7 @@ Common::Array<Exit> RoomManager::loadExits(byte *data, size_t size) {
 
 		if (g_engine->_state->roomExitChanges.contains(_currentRoomNumber)) {
 			// if the exit has been changed, load the changed version
-			for (int j = 0; j < g_engine->_state->roomExitChanges[_currentRoomNumber].size(); j++) {
+			for (uint j = 0; j < g_engine->_state->roomExitChanges[_currentRoomNumber].size(); j++) {
 				if (g_engine->_state->roomExitChanges[_currentRoomNumber][j].exitIndex == i) {
 					exit.isEnabled = g_engine->_state->roomExitChanges[_currentRoomNumber][j].enabled;
 					break;
@@ -500,7 +500,7 @@ Common::Array<HotSpot> RoomManager::loadHotspots(byte *data, size_t size) {
 		bool isChanged = false;
 		if (g_engine->_state->roomHotSpotChanges.contains(_currentRoomNumber)) {
 			// if the hotspot has been changed, load the changed version
-			for (int j = 0; j < g_engine->_state->roomHotSpotChanges[_currentRoomNumber].size(); j++) {
+			for (uint j = 0; j < g_engine->_state->roomHotSpotChanges[_currentRoomNumber].size(); j++) {
 				if (g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspotIndex == spot.innerIndex) {
 					debug("Hotspot %d has been changed, loading changed version, Hotspot x=%d, y = %d, extra = %d, isEnabled=%d", spot.innerIndex, g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot.x, g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot.y, g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot.extra, g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot.isEnabled);
 					hotspots.push_back(g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot);
@@ -573,10 +573,10 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	_roomSfx = loadRoomSfx(roomFile, roomOffset);
 
 	// Pair 10
-	uint32_t pair10offset = roomOffset + (10 * 8);
+	uint32 pair10offset = roomOffset + (10 * 8);
 	roomFile->seek(pair10offset, SEEK_SET);
-	uint32_t pair10dataOffset = roomFile->readUint32LE();
-	uint32_t pair10size = roomFile->readUint32LE();
+	uint32 pair10dataOffset = roomFile->readUint32LE();
+	uint32 pair10size = roomFile->readUint32LE();
 
 	byte *pair10 = new byte[pair10size];
 	roomFile->seek(pair10dataOffset, SEEK_SET);
@@ -597,10 +597,10 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	// Pair 11 is the palette, already loaded
 
 	// Pair 12 - Room Texts
-	uint32_t pair12offset = roomOffset + (12 * 8);
+	uint32 pair12offset = roomOffset + (12 * 8);
 	roomFile->seek(pair12offset, SEEK_SET);
-	uint32_t pair12dataOffset = roomFile->readUint32LE();
-	uint32_t pair12size = roomFile->readUint32LE();
+	uint32 pair12dataOffset = roomFile->readUint32LE();
+	uint32 pair12size = roomFile->readUint32LE();
 
 	byte *pair12 = new byte[pair12size];
 	roomFile->seek(pair12dataOffset, SEEK_SET);
@@ -617,7 +617,7 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 
 	loadRemaps(roomNumber);
 
-	for (int i = 0; i < _currentRoomHotspots.size(); i++) {
+	for (uint i = 0; i < _currentRoomHotspots.size(); i++) {
 		HotSpot hotspot = _currentRoomHotspots[i];
 		drawRect(g_engine->_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 200 + i);
 	}
@@ -681,7 +681,7 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		anim.spriteIndex = 2;
 		anim.startX = mouse->x;
 		anim.startY = mouse->y;
-		anim.dir = PASSERBY_DOWN;
+		anim.dir = kPasserbyDown;
 		anim.targetZIndex = blank->zOrder + 1;
 		anim.resetCoord = blank->y;
 		anims->passerByAnims[0] = anim;
@@ -701,7 +701,7 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		Sprite *camel = findSpriteByIndex(anim.spriteIndex);
 		anim.startX = camel->x;
 		anim.startY = camel->y;
-		anim.dir = PASSERBY_RIGHT;
+		anim.dir = kPasserbyRight;
 		anim.frameTrigger = 0x1FFF;
 		anim.targetZIndex = 1;
 		anim.resetCoord = 639 + camel->w;
@@ -716,7 +716,7 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		Sprite *camel = findSpriteByIndex(3);
 		anim.startX = camel->x;
 		anim.startY = camel->y;
-		anim.dir = PASSERBY_LEFT;
+		anim.dir = kPasserbyLeft;
 		anim.resetCoord = 0 - camel->w;
 		anim.targetZIndex = 1;
 
@@ -732,7 +732,7 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		animA.spriteIndex = 2;
 		animA.startX = carLeft->x;
 		animA.startY = carLeft->y;
-		animA.dir = PASSERBY_LEFT;
+		animA.dir = kPasserbyLeft;
 		animA.resetCoord = carRight->x + carRight->w - carLeft->w;
 		animA.targetZIndex = 100;
 
@@ -741,7 +741,7 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		animB.spriteIndex = 3;
 		animB.startX = carRight->x;
 		animB.startY = carRight->y;
-		animB.dir = PASSERBY_RIGHT;
+		animB.dir = kPasserbyRight;
 		animB.targetZIndex = 100;
 		animB.resetCoord = 639 + carRight->w;
 		anims->passerByAnims[1] = animB;
@@ -756,7 +756,7 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		anim.spriteIndex = 2;
 		anim.startX = walker->x;
 		anim.startY = walker->y;
-		anim.dir = PASSERBY_RIGHT;
+		anim.dir = kPasserbyRight;
 		anim.resetCoord = dark->x;
 		anim.targetZIndex = dark->zOrder + 1;
 		anims->passerByAnims[0] = anim;
@@ -771,7 +771,7 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		animA.spriteIndex = 2;
 		animA.startX = catRight->x;
 		animA.startY = catRight->y;
-		animA.dir = PASSERBY_RIGHT;
+		animA.dir = kPasserbyRight;
 		animA.resetCoord = catLeft->x;
 		animA.targetZIndex = blank->zOrder + 1;
 
@@ -780,7 +780,7 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		animB.spriteIndex = 3;
 		animB.startX = catLeft->x;
 		animB.startY = catLeft->y;
-		animB.dir = PASSERBY_LEFT;
+		animB.dir = kPasserbyLeft;
 		animB.resetCoord = blank->x;
 		animB.targetZIndex = blank->zOrder + 1;
 		anims->passerByAnims[1] = animB;
@@ -796,7 +796,7 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		animA.spriteIndex = 3;
 		animA.startX = mouseRight->x;
 		animA.startY = mouseRight->y;
-		animA.dir = PASSERBY_RIGHT;
+		animA.dir = kPasserbyRight;
 		animA.resetCoord = mouseLeft->x;
 		animA.targetZIndex = papers->zOrder + 1;
 		anims->passerByAnims[0] = animA;
@@ -805,7 +805,7 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		animB.spriteIndex = 4;
 		animB.startX = mouseLeft->x;
 		animB.startY = mouseLeft->y;
-		animB.dir = PASSERBY_LEFT;
+		animB.dir = kPasserbyLeft;
 		animB.resetCoord = mouseRight->x;
 		animB.targetZIndex = papers->zOrder + 1;
 		anims->passerByAnims[1] = animB;
@@ -820,7 +820,7 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		animA.spriteIndex = 2;
 		animA.startX = mummyLeft->x;
 		animA.startY = mummyLeft->y;
-		animA.dir = PASSERBY_LEFT;
+		animA.dir = kPasserbyLeft;
 		animA.resetCoord = 0 - mummyLeft->w;
 		animA.targetZIndex = 1;
 
@@ -829,7 +829,7 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		animB.spriteIndex = 3;
 		animB.startX = mummyRight->x;
 		animB.startY = mummyRight->y;
-		animB.dir = PASSERBY_RIGHT;
+		animB.dir = kPasserbyRight;
 		animB.targetZIndex = 1;
 		animB.resetCoord = 639 + mummyRight->w;
 		anims->passerByAnims[1] = animB;
@@ -845,7 +845,7 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 
 Common::Array<HotSpot> RoomManager::unifyHotspots(Common::Array<Pelrock::Sprite> &anims, Common::Array<Pelrock::HotSpot> &staticHotspots) {
 	Common::Array<HotSpot> unifiedHotspots;
-	for (int i = 0; i < anims.size(); i++) {
+	for (uint i = 0; i < anims.size(); i++) {
 		HotSpot thisHotspot;
 		thisHotspot.index = i;
 		thisHotspot.x = anims[i].x;
@@ -862,7 +862,7 @@ Common::Array<HotSpot> RoomManager::unifyHotspots(Common::Array<Pelrock::Sprite>
 	}
 
 	// debug("total descriptions = %d, anims = %d, hotspots = %d", descriptions.size(), anims.size(), staticHotspots.size());
-	for (int i = 0; i < staticHotspots.size(); i++) {
+	for (uint i = 0; i < staticHotspots.size(); i++) {
 		HotSpot hotspot = staticHotspots[i];
 		hotspot.index = anims.size() + i;
 		unifiedHotspots.push_back(hotspot);
@@ -882,10 +882,10 @@ void RoomManager::init() {
 }
 
 void RoomManager::loadAnimationPixelData(Common::File *roomFile, int roomOffset, byte *&buffer, size_t &outSize) {
-	uint32_t pair_offset = roomOffset + (8 * 8);
+	uint32 pair_offset = roomOffset + (8 * 8);
 	roomFile->seek(pair_offset, SEEK_SET);
-	uint32_t offset = roomFile->readUint32LE();
-	uint32_t size = roomFile->readUint32LE();
+	uint32 offset = roomFile->readUint32LE();
+	uint32 size = roomFile->readUint32LE();
 
 	byte *pixelData = new byte[size];
 	roomFile->seek(offset, SEEK_SET);
@@ -908,16 +908,16 @@ void RoomManager::loadAnimationPixelData(Common::File *roomFile, int roomOffset,
 Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pixelDataSize, byte *data, size_t size) {
 
 	Common::Array<Sprite> anims = Common::Array<Sprite>();
-	uint32_t spriteCountPos = 5;
+	uint32 spriteCountPos = 5;
 	byte spriteCount = data[spriteCountPos] - 2;
 	debug("Sprite count: %d", spriteCount);
-	uint32_t metadata_start = spriteCountPos + (44 * 2 + 5);
-	uint32_t picOffset = 0;
+	uint32 metadata_start = spriteCountPos + (44 * 2 + 5);
+	uint32 picOffset = 0;
 
 	Common::Array<SpriteChange> spriteChanges = g_engine->_state->spriteChanges[_currentRoomNumber];
 	int talkingAnims = 0;
 	for (int i = 0; i < spriteCount; i++) {
-		uint32_t animOffset = metadata_start + (i * 44);
+		uint32 animOffset = metadata_start + (i * 44);
 		Sprite sprite;
 		sprite.index = i;
 		sprite.x = READ_LE_INT16(data + animOffset + 0);
@@ -930,12 +930,12 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 		sprite.extra = READ_LE_INT16(data + animOffset + 32);
 		debug("Sprite %d: x=%d y=%d w=%d h=%d stride=%d numAnims=%d zOrder=%d extra=%d", i, sprite.x, sprite.y, sprite.w, sprite.h, sprite.stride, sprite.numAnims, sprite.zOrder, sprite.extra);
 		sprite.actionFlags = data[animOffset + 34];
-		if(sprite.actionFlags & ACTION_MASK_TALK) {
+		if(sprite.actionFlags & kActionMaskTalk) {
 			sprite.talkingAnimIndex = talkingAnims++;
 		}
 		sprite.isHotspotDisabled = data[animOffset + 38];
 		sprite.disableAfterSequence = data[animOffset + 39];
-		for (int j = 0; j < spriteChanges.size(); j++) {
+		for (uint j = 0; j < spriteChanges.size(); j++) {
 			if (spriteChanges[j].spriteIndex == sprite.index) {
 				debug("Sprite %d has been changed, loading changed version with zOrder %d", sprite.index, spriteChanges[j].zIndex);
 				sprite.zOrder = spriteChanges[j].zIndex;
@@ -960,7 +960,7 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 			anim.speed = data[subAnimOffset + 8 + j];
 			anim.movementFlags = data[subAnimOffset + 14 + (j * 2)] | (data[subAnimOffset + 14 + (j * 2) + 1] << 8);
 
-			uint32_t totalBytesPerFrame = sprite.w * sprite.h * anim.nframes;
+			uint32 totalBytesPerFrame = sprite.w * sprite.h * anim.nframes;
 			anim.animData = new byte *[anim.nframes];
 			if (sprite.w > 0 && sprite.h > 0 && anim.nframes > 0) {
 				for (int k = 0; k < anim.nframes; k++) {
@@ -1000,10 +1000,10 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(byte *data, size_t size) {
 	byte walkboxCount = data[walkboxCountOffset];
 
 	// debug("Walkbox count: %d", walkbox_count);
-	uint32_t walkboxOffset = 0x218;
+	uint32 walkboxOffset = 0x218;
 	Common::Array<WalkBox> walkboxes;
 	for (int i = 0; i < walkboxCount; i++) {
-		uint32_t boxOffset = walkboxOffset + i * 9;
+		uint32 boxOffset = walkboxOffset + i * 9;
 		int16 x1 = READ_LE_INT16(data + boxOffset);
 		int16 y1 = READ_LE_INT16(data + boxOffset + 2);
 		int16 w = READ_LE_INT16(data + boxOffset + 4);
@@ -1016,7 +1016,7 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(byte *data, size_t size) {
 		if (g_engine->_state->roomWalkBoxChanges.contains(_currentRoomNumber)) {
 			debug("Checking for changes to walkbox %d in room %d", i, _currentRoomNumber);
 			// if the walkbox has been changed, load the changed version
-			for (int j = 0; j < g_engine->_state->roomWalkBoxChanges[_currentRoomNumber].size(); j++) {
+			for (uint j = 0; j < g_engine->_state->roomWalkBoxChanges[_currentRoomNumber].size(); j++) {
 				if (g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkboxIndex == i) {
 					walkboxes.push_back(g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkbox);
 					isChanged = true;
@@ -1037,9 +1037,9 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(byte *data, size_t size) {
 
 	if (g_engine->_state->roomWalkBoxChanges.contains(_currentRoomNumber)) {
 		// Add any new walkboxes that were added
-		for (int j = 0; j < g_engine->_state->roomWalkBoxChanges[_currentRoomNumber].size(); j++) {
+		for (uint j = 0; j < g_engine->_state->roomWalkBoxChanges[_currentRoomNumber].size(); j++) {
 			bool found = false;
-			for (int i = 0; i < walkboxes.size(); i++) {
+			for (uint i = 0; i < walkboxes.size(); i++) {
 				if (g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkboxIndex == walkboxes[i].index) {
 					found = true;
 					break;
@@ -1056,8 +1056,8 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(byte *data, size_t size) {
 }
 
 uint32 RoomManager::loadDescriptions(byte *pair12data, size_t pair12size, Common::Array<Description> &outDescriptions) {
-	uint32_t pos = 0;
-	uint32_t lastDescPos = 0;
+	uint32 pos = 0;
+	uint32 lastDescPos = 0;
 	outDescriptions.clear();
 	while (pos < (pair12size)) {
 		if (pair12data[pos] == 0xFF) {
@@ -1115,7 +1115,7 @@ void RoomManager::applyDisabledChoices(byte roomNumber, byte *conversationData,
 		return;
 	}
 	debug("Disabling %d conversation branches for room %d", disabledBranches.size(), roomNumber);
-	for (int i = 0; i < disabledBranches.size(); i++) {
+	for (uint i = 0; i < disabledBranches.size(); i++) {
 		ResetEntry resetEntry = disabledBranches[i];
 		applyDisabledChoice(resetEntry, conversationData, conversationDataSize);
 	}
@@ -1248,7 +1248,7 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 
 ScalingParams RoomManager::loadScalingParams(byte *data, size_t size) {
 
-	uint32_t scalingParamsOffset = 0x214;
+	uint32 scalingParamsOffset = 0x214;
 
 	ScalingParams scalingParams;
 	scalingParams.yThreshold = READ_LE_INT16(data + scalingParamsOffset);
@@ -1309,9 +1309,9 @@ void RoomManager::loadRemaps(int roomNumber) {
 }
 
 byte RoomManager::loadMusicTrackForRoom(Common::File *roomFile, int roomOffset) {
-	uint32_t pair9offset = roomOffset + (9 * 8);
+	uint32 pair9offset = roomOffset + (9 * 8);
 	roomFile->seek(pair9offset, SEEK_SET);
-	uint32_t pair9_data_offset = roomFile->readUint32LE();
+	uint32 pair9_data_offset = roomFile->readUint32LE();
 
 	roomFile->seek(pair9_data_offset, SEEK_SET);
 	byte musicTrack = roomFile->readByte();
@@ -1320,9 +1320,9 @@ byte RoomManager::loadMusicTrackForRoom(Common::File *roomFile, int roomOffset)
 }
 
 Common::Array<byte> RoomManager::loadRoomSfx(Common::File *roomFile, int roomOffset) {
-	uint32_t pair9offset = roomOffset + (9 * 8);
+	uint32 pair9offset = roomOffset + (9 * 8);
 	roomFile->seek(pair9offset, SEEK_SET);
-	uint32_t pair9_data_offset = roomFile->readUint32LE();
+	uint32 pair9_data_offset = roomFile->readUint32LE();
 
 	roomFile->seek(pair9_data_offset, SEEK_SET);
 	roomFile->skip(1); // skip music track byte
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 42340cdb5fe..1969cf6a78a 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -38,6 +38,105 @@
 
 namespace Pelrock {
 
+const char *SOUND_FILENAMES[] = {
+	"NO_SOUND.SMP", // 0 - Silence/disabled
+	"BUHO_ZZZ.SMP", // 1 - Owl
+	"BIRD_1_1.SMP", // 2 - Bird variant 1
+	"BIRD_1_2.SMP", // 3 - Bird variant 2
+	"BIRD_1_3.SMP", // 4 - Bird variant 3
+	"DESPERZZ.SMP", // 5 - Yawn/stretch
+	"HORN_5ZZ.SMP", // 6 - Car horn 5
+	"HORN_6ZZ.SMP", // 7 - Car horn 6
+	"HORN_8ZZ.SMP", // 8 - Car horn 8
+	"SUZIPASS.SMP", // 9 - Suzi passing
+	"CAT_1ZZZ.SMP", // 10 - Cat
+	"DOG_01ZZ.SMP", // 11 - Dog bark 1
+	"DOG_02ZZ.SMP", // 12 - Dog bark 2
+	"DOG_04ZZ.SMP", // 13 - Dog bark 4
+	"DOG_05ZZ.SMP", // 14 - Dog bark 5
+	"DOG_06ZZ.SMP", // 15 - Dog bark 6
+	"DOG_07ZZ.SMP", // 16 - Dog bark 7
+	"DOG_09ZZ.SMP", // 17 - Dog bark 9
+	"ALARMZZZ.SMP", // 18 - Alarm
+	"AMBULAN1.SMP", // 19 - Ambulance
+	"FOUNTAIN.SMP", // 20 - Fountain
+	"GRILLOSZ.SMP", // 21 - Crickets
+	"HOJASZZZ.SMP", // 22 - Leaves rustling
+	"FLASHZZZ.SMP", // 23 - Flash/camera
+	"CUCHI1ZZ.SMP", // 24 - Knife 1
+	"KNRRRRRZ.SMP", // 25 - Snoring
+	"PHONE_02.SMP", // 26 - Phone ring 2
+	"PHONE_03.SMP", // 27 - Phone ring 3
+	"SSSHTZZZ.SMP", // 28 - Shush/quiet
+	"BURGUER1.SMP", // 29 - Burger sizzle
+	"FLIES_2Z.SMP", // 30 - Flies buzzing
+	"PARRILLA.SMP", // 31 - Grill
+	"WATER_2Z.SMP", // 32 - Water
+	"XIQUETZZ.SMP", // 33 - Whistle
+	"RONQUIZZ.SMP", // 34 - Snoring
+	"MOCO1ZZZ.SMP", // 35 - Snot/mucus 1
+	"MOCO2ZZZ.SMP", // 36 - Snot/mucus 2
+	"SPRINGZZ.SMP", // 37 - Spring bounce
+	"MARUJASZ.SMP", // 38 - Gossip/chatter
+	"ELECTROZ.SMP", // 39 - Electric shock
+	"GLASS1ZZ.SMP", // 40 - Glass clink
+	"OPDOORZZ.SMP", // 41 - Door open
+	"CLDOORZZ.SMP", // 42 - Door close
+	"FXH2ZZZZ.SMP", // 43 - Effect 2
+	"BOTEZZZZ.SMP", // 44 - Bottle
+	"ELEC3ZZZ.SMP", // 45 - Electric 3
+	"AJARLZZZ.SMP", // 46 - Ajar/creak
+	"BELCHZZZ.SMP", // 47 - Belch/burp
+	"64ZZZZZZ.SMP", // 48 - Sound effect 64
+	"BIRDOWL2.SMP", // 49 - Bird/owl 2
+	"BUBBLE2Z.SMP", // 50 - Bubbles
+	"BURGUER1.SMP", // 51 - Burger (duplicate)
+	"CACKLEZZ.SMP", // 52 - Cackle/laugh
+	"CERAMIC1.SMP", // 53 - Ceramic break
+	"CLANG5ZZ.SMP", // 54 - Metal clang
+	"CUCHI2ZZ.SMP", // 55 - Knife 2
+	"CUCHI3ZZ.SMP", // 56 - Knife 3
+	"ELEC3ZZZ.SMP", // 57 - Electric 3 (duplicate)
+	"HOJASZZZ.SMP", // 58 - Leaves (duplicate)
+	"LIMA1ZZZ.SMP", // 59 - File/rasp
+	"MOROSZZZ.SMP", // 60 - Moors/crowd
+	"MOROZZZZ.SMP", // 61 - Moor/crowd
+	"MUD1ZZZZ.SMP", // 62 - Mud squelch
+	"PICOZZZZ.SMP", // 63 - Pickaxe
+	"PICO1XZZ.SMP", // 64 - Pickaxe 1
+	"PICO2XZZ.SMP", // 65 - Pickaxe 2
+	"PICO3XZZ.SMP", // 66 - Pickaxe 3
+	"RIMSHOTZ.SMP", // 67 - Rimshot drum
+	"RONCOZZZ.SMP", // 68 - Snoring
+	"SORBOZZZ.SMP", // 69 - Slurp/sip
+	"VIENTO1Z.SMP", // 70 - Wind
+	"2ZZZZZZZ.SMP", // 71 - Sound 2
+	"20ZZZZZZ.SMP", // 72 - Sound 20
+	"21ZZZZZZ.SMP", // 73 - Sound 21
+	"23ZZZZZZ.SMP", // 74 - Sound 23
+	"107ZZZZZ.SMP", // 75 - Sound 107
+	"39ZZZZZZ.SMP", // 76 - Sound 39
+	"81ZZZZZZ.SMP", // 77 - Sound 81
+	"88ZZZZZZ.SMP", // 78 - Sound 88
+	"92ZZZZZZ.SMP", // 79 - Sound 92
+	"SAW_2ZZZ.SMP", // 80 - Saw
+	"QUAKE2ZZ.SMP", // 81 - Earthquake
+	"ROCKSZZZ.SMP", // 82 - Rocks falling
+	"IN_FIREZ.SMP", // 83 - Fire
+	"BEAMZZZZ.SMP", // 84 - Beam/ray
+	"GLISSDWN.SMP", // 85 - Glissando down
+	"REMATERL.SMP", // 86 - Rematerialize
+	"FXH1ZZZZ.SMP", // 87 - Effect 1
+	"FXH3ZZZZ.SMP", // 88 - Effect 3
+	"FXH4ZZZZ.SMP", // 89 - Effect 4
+	"MATCHZZZ.SMP", // 90 - Match strike
+	"SURF_01Z.SMP", // 91 - Surf wave 1
+	"SURF_02Z.SMP", // 92 - Surf wave 2
+	"SURF_04Z.SMP", // 93 - Surf wave 4
+	"TWANGZZZ.SMP", // 94 - Twang
+	"LANDCRAS.SMP", // 95 - Crash landing
+};
+
 SoundManager::SoundManager(Audio::Mixer *mixer)
 	: _mixer(mixer), _currentVolume(128) {
 	// TODO: Initialize sound manager
@@ -81,7 +180,7 @@ void SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
 	sonidosFile.close();
 
 	SoundFormat format = detectFormat(data, sound.size);
-	uint32_t sampleRate = getSampleRate(data, format);
+	uint32 sampleRate = getSampleRate(data, format);
 	Audio::SeekableAudioStream *stream = nullptr;
 
 	if (format == SOUND_FORMAT_RIFF) {
@@ -270,7 +369,7 @@ void SoundManager::loadSoundIndex() {
 	debug("SONIDOS.DAT contains %u files", fileCount);
 	sonidosFile.skip(3); // Padding bytes
 
-	for (uint32_t i = 0; i < fileCount; i++) {
+	for (uint32 i = 0; i < fileCount; i++) {
 		SonidoFile sonido;
 		sonido.filename = sonidosFile.readString('\0', 12);
 		sonidosFile.skip(1);
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index 3480b333974..86195d7af38 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -31,109 +31,12 @@ namespace Pelrock {
 
 struct SonidoFile {
 	Common::String filename;
-	uint32_t offset;
-	uint32_t size;
-	unsigned char *data;
+	uint32 offset;
+	uint32 size;
+	byte *data;
 };
 
-static const char *SOUND_FILENAMES[] = {
-	"NO_SOUND.SMP", // 0 - Silence/disabled
-	"BUHO_ZZZ.SMP", // 1 - Owl
-	"BIRD_1_1.SMP", // 2 - Bird variant 1
-	"BIRD_1_2.SMP", // 3 - Bird variant 2
-	"BIRD_1_3.SMP", // 4 - Bird variant 3
-	"DESPERZZ.SMP", // 5 - Yawn/stretch
-	"HORN_5ZZ.SMP", // 6 - Car horn 5
-	"HORN_6ZZ.SMP", // 7 - Car horn 6
-	"HORN_8ZZ.SMP", // 8 - Car horn 8
-	"SUZIPASS.SMP", // 9 - Suzi passing
-	"CAT_1ZZZ.SMP", // 10 - Cat
-	"DOG_01ZZ.SMP", // 11 - Dog bark 1
-	"DOG_02ZZ.SMP", // 12 - Dog bark 2
-	"DOG_04ZZ.SMP", // 13 - Dog bark 4
-	"DOG_05ZZ.SMP", // 14 - Dog bark 5
-	"DOG_06ZZ.SMP", // 15 - Dog bark 6
-	"DOG_07ZZ.SMP", // 16 - Dog bark 7
-	"DOG_09ZZ.SMP", // 17 - Dog bark 9
-	"ALARMZZZ.SMP", // 18 - Alarm
-	"AMBULAN1.SMP", // 19 - Ambulance
-	"FOUNTAIN.SMP", // 20 - Fountain
-	"GRILLOSZ.SMP", // 21 - Crickets
-	"HOJASZZZ.SMP", // 22 - Leaves rustling
-	"FLASHZZZ.SMP", // 23 - Flash/camera
-	"CUCHI1ZZ.SMP", // 24 - Knife 1
-	"KNRRRRRZ.SMP", // 25 - Snoring
-	"PHONE_02.SMP", // 26 - Phone ring 2
-	"PHONE_03.SMP", // 27 - Phone ring 3
-	"SSSHTZZZ.SMP", // 28 - Shush/quiet
-	"BURGUER1.SMP", // 29 - Burger sizzle
-	"FLIES_2Z.SMP", // 30 - Flies buzzing
-	"PARRILLA.SMP", // 31 - Grill
-	"WATER_2Z.SMP", // 32 - Water
-	"XIQUETZZ.SMP", // 33 - Whistle
-	"RONQUIZZ.SMP", // 34 - Snoring
-	"MOCO1ZZZ.SMP", // 35 - Snot/mucus 1
-	"MOCO2ZZZ.SMP", // 36 - Snot/mucus 2
-	"SPRINGZZ.SMP", // 37 - Spring bounce
-	"MARUJASZ.SMP", // 38 - Gossip/chatter
-	"ELECTROZ.SMP", // 39 - Electric shock
-	"GLASS1ZZ.SMP", // 40 - Glass clink
-	"OPDOORZZ.SMP", // 41 - Door open
-	"CLDOORZZ.SMP", // 42 - Door close
-	"FXH2ZZZZ.SMP", // 43 - Effect 2
-	"BOTEZZZZ.SMP", // 44 - Bottle
-	"ELEC3ZZZ.SMP", // 45 - Electric 3
-	"AJARLZZZ.SMP", // 46 - Ajar/creak
-	"BELCHZZZ.SMP", // 47 - Belch/burp
-	"64ZZZZZZ.SMP", // 48 - Sound effect 64
-	"BIRDOWL2.SMP", // 49 - Bird/owl 2
-	"BUBBLE2Z.SMP", // 50 - Bubbles
-	"BURGUER1.SMP", // 51 - Burger (duplicate)
-	"CACKLEZZ.SMP", // 52 - Cackle/laugh
-	"CERAMIC1.SMP", // 53 - Ceramic break
-	"CLANG5ZZ.SMP", // 54 - Metal clang
-	"CUCHI2ZZ.SMP", // 55 - Knife 2
-	"CUCHI3ZZ.SMP", // 56 - Knife 3
-	"ELEC3ZZZ.SMP", // 57 - Electric 3 (duplicate)
-	"HOJASZZZ.SMP", // 58 - Leaves (duplicate)
-	"LIMA1ZZZ.SMP", // 59 - File/rasp
-	"MOROSZZZ.SMP", // 60 - Moors/crowd
-	"MOROZZZZ.SMP", // 61 - Moor/crowd
-	"MUD1ZZZZ.SMP", // 62 - Mud squelch
-	"PICOZZZZ.SMP", // 63 - Pickaxe
-	"PICO1XZZ.SMP", // 64 - Pickaxe 1
-	"PICO2XZZ.SMP", // 65 - Pickaxe 2
-	"PICO3XZZ.SMP", // 66 - Pickaxe 3
-	"RIMSHOTZ.SMP", // 67 - Rimshot drum
-	"RONCOZZZ.SMP", // 68 - Snoring
-	"SORBOZZZ.SMP", // 69 - Slurp/sip
-	"VIENTO1Z.SMP", // 70 - Wind
-	"2ZZZZZZZ.SMP", // 71 - Sound 2
-	"20ZZZZZZ.SMP", // 72 - Sound 20
-	"21ZZZZZZ.SMP", // 73 - Sound 21
-	"23ZZZZZZ.SMP", // 74 - Sound 23
-	"107ZZZZZ.SMP", // 75 - Sound 107
-	"39ZZZZZZ.SMP", // 76 - Sound 39
-	"81ZZZZZZ.SMP", // 77 - Sound 81
-	"88ZZZZZZ.SMP", // 78 - Sound 88
-	"92ZZZZZZ.SMP", // 79 - Sound 92
-	"SAW_2ZZZ.SMP", // 80 - Saw
-	"QUAKE2ZZ.SMP", // 81 - Earthquake
-	"ROCKSZZZ.SMP", // 82 - Rocks falling
-	"IN_FIREZ.SMP", // 83 - Fire
-	"BEAMZZZZ.SMP", // 84 - Beam/ray
-	"GLISSDWN.SMP", // 85 - Glissando down
-	"REMATERL.SMP", // 86 - Rematerialize
-	"FXH1ZZZZ.SMP", // 87 - Effect 1
-	"FXH3ZZZZ.SMP", // 88 - Effect 3
-	"FXH4ZZZZ.SMP", // 89 - Effect 4
-	"MATCHZZZ.SMP", // 90 - Match strike
-	"SURF_01Z.SMP", // 91 - Surf wave 1
-	"SURF_02Z.SMP", // 92 - Surf wave 2
-	"SURF_04Z.SMP", // 93 - Surf wave 4
-	"TWANGZZZ.SMP", // 94 - Twang
-	"LANDCRAS.SMP", // 95 - Crash landing
-};
+extern const char *SOUND_FILENAMES[];
 
 enum SoundFormat {
 	SOUND_FORMAT_RAWPCM,
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 6efc93e3401..fb597db08c8 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -37,14 +37,14 @@ enum Cursor {
 	COMBINATION
 };
 
-#define ACTION_MASK_NONE 0
-#define ACTION_MASK_OPEN 1
-#define ACTION_MASK_CLOSE 2
-#define ACTION_MASK_UNKNOWN 4
-#define ACTION_MASK_PICKUP 8
-#define ACTION_MASK_TALK 16
-#define ACTION_MASK_PUSH 32
-#define ACTION_MASK_PULL 128
+const byte kActionMaskNone    = 0;
+const byte kActionMaskOpen    = 1;
+const byte kActionMaskClose   = 2;
+const byte kActionMaskUnknown = 4;
+const byte kActionMaskPickup  = 8;
+const byte kActionMaskTalk    = 16;
+const byte kActionMaskPush    = 32;
+const byte kActionMaskPull    = 128;
 
 enum VerbIcon {
 	PICKUP,
@@ -60,7 +60,7 @@ enum VerbIcon {
 	NO_ACTION
 };
 
-static const uint32 kLongClickDuration = 500; // 500ms for long click
+const uint32 kLongClickDuration = 500; // 500ms for long click
 const int kCursorWidth = 16;
 const int kCursorHeight = 18;
 const int kCursorSize = 288; // 16 * 18
@@ -92,25 +92,32 @@ const int kAlfredInitialPosX = 235;
 const int kAlfredInitialPosY = 279;
 
 // Direction flags (bit-packed)
-#define MOVE_RIGHT 0x01 // Move right (positive X)
-#define MOVE_LEFT 0x02  // Move left (negative X)
-#define MOVE_HORIZ 0x03 // Horizontal movement mask
-#define MOVE_DOWN 0x04  // Move down (positive Y)
-#define MOVE_UP 0x08    // Move up (negative Y)
-#define MOVE_VERT 0x0C  // Vertical movement mask
-#define MAX_PATH_LENGTH 100
-#define MAX_MOVEMENT_STEPS 100 // 500 bytes / 5 bytes per step
-#define PATH_END 0xFF          // End of path marker
-
-#define MAX_CHARS_PER_LINE 0x2F // 47 characters
-#define MAX_LINES 5             // Maximum number of lines per page (0-indexed check against 4)
-
-#define ALFRED_COLOR 0x0D
-
-#define OVERLAY_NONE 0
-#define OVERLAY_CHOICES 1
-#define OVERLAY_PICKUP_ICON 2
-#define OVERLAY_ACTION 3
+const byte kMoveRight = 0x01; // Move right (positive X)
+const byte kMoveLeft  = 0x02; // Move left (negative X)
+const byte kMoveHoriz = 0x03; // Horizontal movement mask
+const byte kMoveDown  = 0x04; // Move down (positive Y)
+const byte kMoveUp    = 0x08; // Move up (negative Y)
+const byte kMoveVert  = 0x0C; // Vertical movement mask
+const int  kMaxPathLength     = 100;
+const int  kMaxMovementSteps  = 100; // 500 bytes / 5 bytes per step
+const byte kPathEnd           = 0xFF; // End of path marker
+
+const int  kMaxCharsPerLine   = 0x2F; // 47 characters
+const int  kMaxLines          = 5;    // Maximum number of lines per page
+
+const byte kAlfredColor = 0x0D;
+
+enum OverlayMode {
+	OVERLAY_NONE        = 0,
+	OVERLAY_CHOICES     = 1,
+	OVERLAY_PICKUP_ICON = 2,
+	OVERLAY_ACTION      = 3
+};
+
+// Passerby direction constants
+const byte kPasserbyRight = 0;
+const byte kPasserbyLeft  = 1;
+const byte kPasserbyDown  = 2;
 
 const byte kIconBlinkPeriod = 4;
 
@@ -216,21 +223,21 @@ struct ShakeEffectState
 
 
 struct MovementStep {
-	uint8_t flags;      /* Direction flags (see MOVE_* constants) */
-	uint16_t distanceX; // Horizontal distance to move
-	uint16_t distanceY; // Vertical distance to move
+	byte   flags;     /* Direction flags (see kMove* constants) */
+	uint16 distanceX; // Horizontal distance to move
+	uint16 distanceY; // Vertical distance to move
 };
 
 /**
  * Pathfinding context
  */
 struct PathContext {
-	uint8_t *pathBuffer;          // Sequence of walkbox indices
+	byte   *pathBuffer;          // Sequence of walkbox indices
 	MovementStep *movementBuffer; // Array of movement steps
-	uint8_t *compressed_path;     // Final compressed path
-	uint16_t pathLength;
-	uint16_t movementCount;
-	uint16_t compressed_length;
+	byte   *compressed_path;     // Final compressed path
+	uint16  pathLength;
+	uint16  movementCount;
+	uint16  compressed_length;
 };
 
 struct Anim {
@@ -443,10 +450,6 @@ struct PaletteAnim {
 	byte tickCount = 0;
 };
 
-#define PASSERBY_RIGHT 0
-#define PASSERBY_LEFT 1
-#define PASSERBY_DOWN 2
-
 struct PasserByAnim {
 	uint32 frameTrigger = 0x3FF;
 	int16 startX;
@@ -567,7 +570,7 @@ struct GameStateData {
 
 	int libraryShelf = -1;
 	int selectedBookIndex = -1;
-	unsigned char bookLetter = '\0';
+	char bookLetter = '\0';
 	Common::HashMap<byte, Common::Array<Sticker>> stickersPerRoom;
 	Common::HashMap<byte, Common::Array<ExitChange>> roomExitChanges;
 	Common::HashMap<byte, Common::Array<WalkBoxChange>> roomWalkBoxChanges;
@@ -585,7 +588,7 @@ struct GameStateData {
 	}
 
 	void clear() {
-		memset(conversationCurrentRoot, 0, 112); // Initialize all to 0xFF (not set)
+		memset(conversationCurrentRoot, 0xFF, 112); // Initialize all to 0xFF (not set)
 		for (int i = 0; i < kNumGameFlags; i++)
 			flags[i] = 0;
 		flags[FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ] = true;
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 8032112eb8c..9e522918a23 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -141,15 +141,15 @@ void drawText(Graphics::ManagedSurface &dest, Graphics::Font *font, Common::Stri
 }
 
 size_t rleDecompress(
-	const uint8_t *input,
+	const byte *input,
 	size_t inputSize,
-	uint32_t offset,
-	uint32_t expectedSize,
-	uint8_t **out_data,
+	uint32 offset,
+	uint32 expectedSize,
+	byte **out_data,
 	bool untilBuda) {
 	// Check for uncompressed markers
 	if (inputSize == 0x8000 || inputSize == 0x6800) {
-		*out_data = (uint8_t *)malloc(inputSize);
+		*out_data = (byte *)malloc(inputSize);
 		if (!*out_data)
 			return 0;
 		memcpy(*out_data, input + offset, inputSize);
@@ -159,27 +159,27 @@ size_t rleDecompress(
 	// RLE compressed
 	size_t bufferCapacity;
 	size_t result_size = 0;
-	uint32_t pos = offset;
-	uint8_t last_value = 0;
+	uint32 pos = offset;
+	byte last_value = 0;
 
 	if (untilBuda || expectedSize == 0) {
 		// Dynamic allocation mode - grow buffer as needed
 		bufferCapacity = 4096;
-		*out_data = (uint8_t *)malloc(bufferCapacity);
+		*out_data = (byte *)malloc(bufferCapacity);
 		if (!*out_data)
 			return 0;
 	} else {
 		// Fixed size mode
 		bufferCapacity = expectedSize;
-		*out_data = (uint8_t *)malloc(bufferCapacity);
+		*out_data = (byte *)malloc(bufferCapacity);
 		if (!*out_data)
 			return 0;
 	}
 
 	while (pos + 2 <= inputSize) {
 		// Read the RLE pair
-		uint8_t count = input[pos];
-		uint8_t value = input[pos + 1];
+		byte count = input[pos];
+		byte value = input[pos + 1];
 		last_value = value;
 
 		// Write pixels for this pair
@@ -195,7 +195,7 @@ size_t rleDecompress(
 					break;
 
 				}
-				uint8_t *newBuf = (uint8_t *)realloc(*out_data, bufferCapacity);
+				byte *newBuf = (byte *)realloc(*out_data, bufferCapacity);
 				if (!newBuf) {
 					free(*out_data);
 					*out_data = nullptr;
@@ -217,7 +217,7 @@ size_t rleDecompress(
 			// Grow buffer if needed
 			if (result_size >= bufferCapacity) {
 				bufferCapacity++;
-				uint8_t *newBuf = (uint8_t *)realloc(*out_data, bufferCapacity);
+				byte *newBuf = (byte *)realloc(*out_data, bufferCapacity);
 				if (!newBuf) {
 					free(*out_data);
 					*out_data = nullptr;
@@ -238,7 +238,7 @@ size_t rleDecompress(
 	return result_size;
 }
 
-void readUntilBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *&buffer, size_t &outSize) {
+void readUntilBuda(Common::SeekableReadStream *stream, uint32 startPos, byte *&buffer, size_t &outSize) {
 	const int markerLen = 4;
 	size_t bufferSize = 4096;
 	size_t pos = 0;
@@ -265,7 +265,7 @@ void readUntilBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *
 	outSize = pos;
 }
 
-void rleDecompressSingleBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *&outBuffer, size_t &outSize){
+void rleDecompressSingleBuda(Common::SeekableReadStream *stream, uint32 startPos, byte *&outBuffer, size_t &outSize){
 	byte *buffer = nullptr;
 	size_t size = 0;
 	readUntilBuda(stream, startPos, buffer, size);
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index 1aedf5a2b4f..fe61d1d2d42 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -32,9 +32,9 @@
 namespace Pelrock {
 
 const int EXPECTED_SIZE = 640 * 400;
-size_t rleDecompress(const uint8_t *data, size_t data_size, uint32_t offset, uint32_t size, uint8_t **out_data, bool untilBuda = true);
-void readUntilBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *&buffer, size_t &outSize);
-void rleDecompressSingleBuda(Common::SeekableReadStream *stream, uint32_t startPos, byte *&buffer, size_t &outSize);
+size_t rleDecompress(const byte *data, size_t data_size, uint32 offset, uint32 size, byte **out_data, bool untilBuda = true);
+void readUntilBuda(Common::SeekableReadStream *stream, uint32 startPos, byte *&buffer, size_t &outSize);
+void rleDecompressSingleBuda(Common::SeekableReadStream *stream, uint32 startPos, byte *&buffer, size_t &outSize);
 void drawSpriteToBuffer(byte *buffer, int bufferWidth, byte *sprite, int x, int y, int width, int height, int transparentColor);
 void drawSpriteToBuffer(Graphics::ManagedSurface &dest, byte *sprite, int x, int y, int width, int height, int transparentColor);
 void blitSurfaceToBuffer(Graphics::Surface *surface, byte *buffer, int bufferWidth, int bufferHeight, int destX, int destY);
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index 9c7243def1b..cb36bc35ecc 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -281,8 +281,8 @@ void VideoManager::initMetadata() {
 		char signature[5] = {0};
 		_introSndFile.read(signature, 4);
 		if (strcmp(signature, "PACK") == 0) {
-			uint32_t numFiles = _introSndFile.readUint32LE();
-			for (uint32_t i = 0; i < numFiles; ++i) {
+			uint32 numFiles = _introSndFile.readUint32LE();
+			for (uint32 i = 0; i < numFiles; ++i) {
 				VoiceData sound;
 				Common::String filename = _introSndFile.readString();
 				// _introSndFile.skip(1);
diff --git a/engines/pelrock/video/video.h b/engines/pelrock/video/video.h
index aa4fbf5412b..392734eb5fc 100644
--- a/engines/pelrock/video/video.h
+++ b/engines/pelrock/video/video.h
@@ -30,9 +30,9 @@
 namespace Pelrock {
 
 struct ChunkHeader {
-    uint32_t blockCount;      // +0x00: Number of 0x5000-byte blocks
-    uint32_t dataOffset;      // +0x04: Varies by chunk type
-    uint8_t  chunkType;       // +0x08: 1=RLE, 2=BlockCopy, 3=End, 4=Palette, 6=Special
+    uint32 blockCount;      // +0x00: Number of 0x5000-byte blocks
+    uint32 dataOffset;      // +0x04: Varies by chunk type
+    byte   chunkType;       // +0x08: 1=RLE, 2=BlockCopy, 3=End, 4=Palette, 6=Special
     // +0x0D: Frame data begins
 	byte *data;
 };


Commit: b7057581cbdec775c76bd9ce56aed6898b90a0ad
    https://github.com/scummvm/scummvm/commit/b7057581cbdec775c76bd9ce56aed6898b90a0ad
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:26+02:00

Commit Message:
PELROCK: Disable looping for incidental music

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/sound.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 0defabc6729..1ccd01c386b 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1855,7 +1855,7 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	debug("Performing action trigger: %d", actionTrigger);
 	switch (actionTrigger) {
 	case 257: // look portrait
-		_sound->playMusicTrack(25);
+		_sound->playMusicTrack(25, false);
 		loadExtraScreenAndPresent(9);
 		_dialog->say(_res->_ingameTexts[QUEBUENA_ESTA]);
 		_screen->markAllDirty();
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index e1a594d3234..569d314581b 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -268,7 +268,7 @@ void PelrockEngine::playSoundIfNeeded() {
 void PelrockEngine::travelToEgypt() {
 	_graphics->fadeToBlack(10);
 
-	_sound->playMusicTrack(26, 1);
+	_sound->playMusicTrack(26, false);
 	byte *palette = new byte[768];
 	if (!_bgScreen.getPixels()) {
 		_bgScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 1969cf6a78a..2d5f1be7aa0 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -348,7 +348,7 @@ void SoundManager::playMusicTrack(int trackNumber, bool loop) {
 		_cdPlayStartTime = g_system->getMillis();
 	}
 	g_system->getAudioCDManager()->stop();
-	g_system->getAudioCDManager()->play(trackNumber, loop ? -1 : 0, _cdTrackStart, _cdTrackDuration);
+	g_system->getAudioCDManager()->play(trackNumber, loop ? -1 : 1, _cdTrackStart, _cdTrackDuration);
 }
 
 void SoundManager::loadSoundIndex() {


Commit: f1b6d3a7edf75a13bead2b1acf74d1f9c13db912
    https://github.com/scummvm/scummvm/commit/f1b6d3a7edf75a13bead2b1acf74d1f9c13db912
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:26+02:00

Commit Message:
PELROCK: Fixes stale item staying in action popup after use

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 9ddad5ec3ca..6682cf1ecac 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -166,7 +166,7 @@ void MenuManager::menuLoop() {
 
 	g_system->getPaletteManager()->setPalette(_mainMenuPalette, 0, 256);
 	g_engine->changeCursor(DEFAULT);
-
+	_menuText = _menuTexts[0];
 	while (!g_engine->shouldQuit() && !_events->_rightMouseClicked) {
 
 		_events->pollEvent();
@@ -221,7 +221,6 @@ void MenuManager::drawInventoryIcons() {
 
 void MenuManager::loadMenu() {
 
-	bool alternateMenu = false;
 	Common::File alfred7;
 	if (!alfred7.open(Common::Path("ALFRED.7"))) {
 		error("Could not open ALFRED.7");
@@ -231,60 +230,46 @@ void MenuManager::loadMenu() {
 	_compositeBuffer.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 	_mainMenu.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 	loadMenuTexts();
-	if (!alternateMenu) {
-		alfred7.seek(kSettingsPaletteOffset, SEEK_SET);
-		alfred7.read(_mainMenuPalette, 768);
-		for (int i = 0; i < 256; i++) {
-			_mainMenuPalette[i * 3] = _mainMenuPalette[i * 3] << 2;
-			_mainMenuPalette[i * 3 + 1] = _mainMenuPalette[i * 3 + 1] << 2;
-			_mainMenuPalette[i * 3 + 2] = _mainMenuPalette[i * 3 + 2] << 2;
-		}
-
-		uint32 curPos = 0;
-		alfred7.seek(2405266, SEEK_SET);
-		alfred7.read(_mainMenu.getPixels(), 65536);
-
-		curPos += 65536;
-
-		byte *compressedPart1 = new byte[29418];
-		alfred7.read(compressedPart1, 29418);
-		byte *decompressedPart1 = nullptr;
-		size_t decompressedSize = rleDecompress(compressedPart1, 29418, 0, 0, &decompressedPart1, true);
-
-		memcpy((byte *)_mainMenu.getPixels() + curPos, decompressedPart1, decompressedSize);
-		curPos += decompressedSize;
-
-		delete[] compressedPart1;
-		delete[] decompressedPart1;
-		alfred7.seek(2500220, SEEK_SET);
-		alfred7.read((byte *)_mainMenu.getPixels() + curPos, 32768);
-		curPos += 32768;
-		byte *compressedPart2 = new byte[30288];
-		alfred7.read(compressedPart2, 30288);
-		byte *decompressedPart2 = nullptr;
-		decompressedSize = rleDecompress(compressedPart2, 30288, 0, 0, &decompressedPart2, true);
-
-		memcpy((byte *)_mainMenu.getPixels() + curPos, decompressedPart2, decompressedSize);
-		curPos += decompressedSize;
-		debug("Settings menu size loaded: %d, with last block %d", curPos, curPos + 92160);
-		delete[] compressedPart2;
-		delete[] decompressedPart2;
-		alfred7.seek(2563266, SEEK_SET);
-		alfred7.read((byte *)_mainMenu.getPixels() + curPos, 92160);
-	} else {
-		_mainMenu.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
-
-		alfred7.seek(kAlternateSettingsPaletteOffset, SEEK_SET);
-		alfred7.read(_mainMenuPalette, 768);
-		for (int i = 0; i < 256; i++) {
-			_mainMenuPalette[i * 3] = _mainMenuPalette[i * 3] << 2;
-			_mainMenuPalette[i * 3 + 1] = _mainMenuPalette[i * 3 + 1] << 2;
-			_mainMenuPalette[i * 3 + 2] = _mainMenuPalette[i * 3 + 2] << 2;
-		}
-
-		g_engine->_res->mergeRleBlocks(&alfred7, kAlternateSettingsMenuOffset, 8, (byte *)_mainMenu.getPixels());
+	alfred7.seek(kSettingsPaletteOffset, SEEK_SET);
+	alfred7.read(_mainMenuPalette, 768);
+	for (int i = 0; i < 256; i++) {
+		_mainMenuPalette[i * 3] = _mainMenuPalette[i * 3] << 2;
+		_mainMenuPalette[i * 3 + 1] = _mainMenuPalette[i * 3 + 1] << 2;
+		_mainMenuPalette[i * 3 + 2] = _mainMenuPalette[i * 3 + 2] << 2;
 	}
 
+	uint32 curPos = 0;
+	alfred7.seek(2405266, SEEK_SET);
+	alfred7.read(_mainMenu.getPixels(), 65536);
+
+	curPos += 65536;
+
+	byte *compressedPart1 = new byte[29418];
+	alfred7.read(compressedPart1, 29418);
+	byte *decompressedPart1 = nullptr;
+	size_t decompressedSize = rleDecompress(compressedPart1, 29418, 0, 0, &decompressedPart1, true);
+
+	memcpy((byte *)_mainMenu.getPixels() + curPos, decompressedPart1, decompressedSize);
+	curPos += decompressedSize;
+
+	delete[] compressedPart1;
+	delete[] decompressedPart1;
+	alfred7.seek(2500220, SEEK_SET);
+	alfred7.read((byte *)_mainMenu.getPixels() + curPos, 32768);
+	curPos += 32768;
+	byte *compressedPart2 = new byte[30288];
+	alfred7.read(compressedPart2, 30288);
+	byte *decompressedPart2 = nullptr;
+	decompressedSize = rleDecompress(compressedPart2, 30288, 0, 0, &decompressedPart2, true);
+
+	memcpy((byte *)_mainMenu.getPixels() + curPos, decompressedPart2, decompressedSize);
+	curPos += decompressedSize;
+	debug("Settings menu size loaded: %d, with last block %d", curPos, curPos + 92160);
+	delete[] compressedPart2;
+	delete[] decompressedPart2;
+	alfred7.seek(2563266, SEEK_SET);
+	alfred7.read((byte *)_mainMenu.getPixels() + curPos, 92160);
+
 	readButton(alfred7, 3193376, _saveButtons, _saveGameRect);
 	readButton(alfred7, alfred7.pos(), _loadButtons, _loadGameRect);
 	readButton(alfred7, alfred7.pos(), _soundsButtons, _soundsRect);
@@ -324,9 +309,6 @@ void MenuManager::loadMenuTexts() {
 	exe.seek(kInventoryDescriptionsOffset, SEEK_SET);
 	exe.read(descBuffer, kInventoryDescriptionsSize);
 	_inventoryDescriptions = _res->processTextData(descBuffer, kInventoryDescriptionsSize, true);
-	for(size_t i = 0; i < _inventoryDescriptions.size(); i++) {
-		debug("Inventory description %d: %s", i, _inventoryDescriptions[i][0].c_str());
-	}
 	delete[] descBuffer;
 
 	Common::String desc = "";
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 569d314581b..9e5a51dec7b 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -76,9 +76,12 @@ PelrockEngine::~PelrockEngine() {
 	delete[] _inventoryOverlayState.arrows[0];
 	delete[] _inventoryOverlayState.arrows[1];
 	// Free path-finding buffers (allocated via malloc in findPath)
-	free(_currentContext.pathBuffer);
-	free(_currentContext.movementBuffer);
-	free(_currentContext.compressed_path);
+	if(_currentContext.pathBuffer) {
+		free(_currentContext.pathBuffer);
+	}
+	if(_currentContext.movementBuffer) {
+		free(_currentContext.movementBuffer);
+	}
 }
 
 uint32 PelrockEngine::getFeatures() const {
@@ -1874,7 +1877,7 @@ void PelrockEngine::walkLoop(int16 x, int16 y, AlfredDirection direction) {
 
 void PelrockEngine::walkTo(int x, int y) {
 	_currentStep = 0;
-	PathContext context = {nullptr, nullptr, nullptr, 0, 0, 0};
+	PathContext context = {nullptr, nullptr, 0, 0, 0};
 	findPath(_alfredState.x, _alfredState.y, x, y, _room->_currentRoomWalkboxes, &context, _currentHotspot);
 	_currentContext = context;
 	_alfredState.setState(ALFRED_WALKING);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 96286d5b569..4dad41aeb7f 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -137,7 +137,7 @@ private:
 
 	// walking
 	int _currentStep = 0;
-	PathContext _currentContext;
+	PathContext _currentContext = {nullptr, nullptr, 0, 0, 0};
 
 	Graphics::ManagedSurface _currentBackground; // Clean background - NEVER modified
 	Graphics::ManagedSurface _bgScreen;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index fb597db08c8..3440d1b0ab9 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -37,14 +37,14 @@ enum Cursor {
 	COMBINATION
 };
 
-const byte kActionMaskNone    = 0;
-const byte kActionMaskOpen    = 1;
-const byte kActionMaskClose   = 2;
+const byte kActionMaskNone = 0;
+const byte kActionMaskOpen = 1;
+const byte kActionMaskClose = 2;
 const byte kActionMaskUnknown = 4;
-const byte kActionMaskPickup  = 8;
-const byte kActionMaskTalk    = 16;
-const byte kActionMaskPush    = 32;
-const byte kActionMaskPull    = 128;
+const byte kActionMaskPickup = 8;
+const byte kActionMaskTalk = 16;
+const byte kActionMaskPush = 32;
+const byte kActionMaskPull = 128;
 
 enum VerbIcon {
 	PICKUP,
@@ -93,31 +93,31 @@ const int kAlfredInitialPosY = 279;
 
 // Direction flags (bit-packed)
 const byte kMoveRight = 0x01; // Move right (positive X)
-const byte kMoveLeft  = 0x02; // Move left (negative X)
+const byte kMoveLeft = 0x02;  // Move left (negative X)
 const byte kMoveHoriz = 0x03; // Horizontal movement mask
-const byte kMoveDown  = 0x04; // Move down (positive Y)
-const byte kMoveUp    = 0x08; // Move up (negative Y)
-const byte kMoveVert  = 0x0C; // Vertical movement mask
-const int  kMaxPathLength     = 100;
-const int  kMaxMovementSteps  = 100; // 500 bytes / 5 bytes per step
-const byte kPathEnd           = 0xFF; // End of path marker
+const byte kMoveDown = 0x04;  // Move down (positive Y)
+const byte kMoveUp = 0x08;    // Move up (negative Y)
+const byte kMoveVert = 0x0C;  // Vertical movement mask
+const int kMaxPathLength = 100;
+const int kMaxMovementSteps = 100; // 500 bytes / 5 bytes per step
+const byte kPathEnd = 0xFF;        // End of path marker
 
-const int  kMaxCharsPerLine   = 0x2F; // 47 characters
-const int  kMaxLines          = 5;    // Maximum number of lines per page
+const int kMaxCharsPerLine = 0x2F; // 47 characters
+const int kMaxLines = 5;           // Maximum number of lines per page
 
 const byte kAlfredColor = 0x0D;
 
 enum OverlayMode {
-	OVERLAY_NONE        = 0,
-	OVERLAY_CHOICES     = 1,
+	OVERLAY_NONE = 0,
+	OVERLAY_CHOICES = 1,
 	OVERLAY_PICKUP_ICON = 2,
-	OVERLAY_ACTION      = 3
+	OVERLAY_ACTION = 3
 };
 
 // Passerby direction constants
 const byte kPasserbyRight = 0;
-const byte kPasserbyLeft  = 1;
-const byte kPasserbyDown  = 2;
+const byte kPasserbyLeft = 1;
+const byte kPasserbyDown = 2;
 
 const byte kIconBlinkPeriod = 4;
 
@@ -203,8 +203,7 @@ struct AlfredState {
 	}
 };
 
-struct ShakeEffectState
-{
+struct ShakeEffectState {
 	bool enabled = false;
 	int shakeX = 0;
 	int shakeY = 0;
@@ -221,9 +220,8 @@ struct ShakeEffectState
 	}
 };
 
-
 struct MovementStep {
-	byte   flags;     /* Direction flags (see kMove* constants) */
+	byte flags;       /* Direction flags (see kMove* constants) */
 	uint16 distanceX; // Horizontal distance to move
 	uint16 distanceY; // Vertical distance to move
 };
@@ -232,12 +230,11 @@ struct MovementStep {
  * Pathfinding context
  */
 struct PathContext {
-	byte   *pathBuffer;          // Sequence of walkbox indices
+	byte *pathBuffer;             // Sequence of walkbox indices
 	MovementStep *movementBuffer; // Array of movement steps
-	byte   *compressed_path;     // Final compressed path
-	uint16  pathLength;
-	uint16  movementCount;
-	uint16  compressed_length;
+	uint16 pathLength;
+	uint16 movementCount;
+	uint16 compressed_length;
 };
 
 struct Anim {
@@ -630,7 +627,14 @@ struct GameStateData {
 		for (uint i = 0; i < inventoryItems.size(); i++) {
 			if (inventoryItems[i] == id) {
 				inventoryItems.remove_at(i);
-				return;
+				break;
+			}
+		}
+		if (selectedInventoryItem == id) {
+			if (inventoryItems.size() > 0) {
+				selectedInventoryItem = inventoryItems[0];
+			} else {
+				selectedInventoryItem = -1;
 			}
 		}
 	}


Commit: ad219ccbbd08185baf8d2bf022205f32d2019d36
    https://github.com/scummvm/scummvm/commit/ad219ccbbd08185baf8d2bf022205f32d2019d36
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:26+02:00

Commit Message:
PELROCK: Fixes book titles not having new lines

Changed paths:
  R engines/pelrock/library_books.h
    engines/pelrock/computer.cpp
    engines/pelrock/computer.h


diff --git a/engines/pelrock/computer.cpp b/engines/pelrock/computer.cpp
index fe8b574f810..5c90775c4ac 100644
--- a/engines/pelrock/computer.cpp
+++ b/engines/pelrock/computer.cpp
@@ -21,10 +21,10 @@
 
 #include "common/events.h"
 #include "common/system.h"
+#include "graphics/cursorman.h"
 #include "graphics/paletteman.h"
 
 #include "pelrock/computer.h"
-#include "pelrock/library_books.h"
 #include "pelrock/pelrock.h"
 
 namespace Pelrock {
@@ -41,6 +41,22 @@ Computer::Computer(PelrockEventManager *eventMan)
 	init();
 }
 
+Common::StringArray split(const Common::String &str) {
+	Common::StringArray result;
+	Common::String token;
+
+	for (uint i = 0; i < str.size(); i++) {
+		if ((unsigned char)str[i] == 0xC8) {
+			result.push_back(token);
+			token.clear();
+		} else {
+			token += str[i];
+		}
+	}
+	result.push_back(token); // last token
+	return result;
+}
+
 void Computer::init() {
 	Common::File alfred7File;
 	if (!alfred7File.open("ALFRED.7")) {
@@ -51,12 +67,13 @@ void Computer::init() {
 	alfred7File.seek(kBookDataOffset, SEEK_SET);
 	while (alfred7File.pos() < kBookDataEnd) {
 		LibraryBook book;
-
-		book.title = alfred7File.readString(0, kBookTitleSize);
-		book.author = alfred7File.readString(0, kBookAuthorSize);
+		Common::String title = alfred7File.readString(0, kBookTitleSize);
+		title.trim();
+		book.title = split(title);
+		Common::String author = alfred7File.readString(0, kBookAuthorSize);
+		author.trim();
+		book.author = split(author);
 		book.genre = alfred7File.readString(0, kBookGenreSize);
-		book.title.trim();
-		book.author.trim();
 		book.genre.trim();
 		book.inventoryIndex = alfred7File.readByte() - 55;
 		book.shelf = alfred7File.readByte();
@@ -100,11 +117,13 @@ void Computer::cleanup() {
 	}
 	g_engine->_screen->markAllDirty();
 	g_engine->_screen->update();
+	CursorMan.showMouse(true);
 }
 
 int Computer::run() {
 	loadBackground();
 	_state = STATE_MAIN_MENU;
+	CursorMan.showMouse(false);
 	g_engine->changeCursor(DEFAULT);
 
 	while (!g_engine->shouldQuit() && _state != STATE_EXIT) {
@@ -185,14 +204,35 @@ void Computer::drawScreen() {
 		Common::String titleLine = _computerText[3][0];
 		int titlePlaceholderIndex = titleLine.findFirstOf("XXXX");
 
-		titleLine.replace(titlePlaceholderIndex, titleLine.size() - titlePlaceholderIndex, book.title);
-		g_engine->_graphics->drawColoredText(g_engine->_screen, titleLine, textX, textY, 340, defaultColor, g_engine->_smallFont);
+		int titleIndex = 0;
+		while(titleIndex < book.title.size()) {
+			Common::String thisLine;
+			if(titleIndex == 0) {
+				thisLine = titleLine;
+				thisLine.replace(titlePlaceholderIndex, titleLine.size() - titlePlaceholderIndex, book.title[titleIndex]);
+			} else {
+				thisLine = book.title[titleIndex];
+			}
+			g_engine->_graphics->drawColoredText(g_engine->_screen, thisLine, textX, textY + _lineHeight * titleIndex, 340, defaultColor, g_engine->_smallFont);
+			titleIndex++;
+		}
 
 		// Author
 		Common::String authorLine = _computerText[4][0];
 		int authorPlaceholderIndex = authorLine.findFirstOf("XXXX");
-		authorLine.replace(authorPlaceholderIndex, authorLine.size() - authorPlaceholderIndex, book.author);
-		g_engine->_graphics->drawColoredText(g_engine->_screen, authorLine, textX, textY + increment, 340, defaultColor, g_engine->_smallFont);
+		int authorIndex = 0;
+
+		while(authorIndex < book.author.size()) {
+			Common::String thisLine;
+			if(authorIndex == 0) {
+				thisLine = authorLine;
+				thisLine.replace(authorPlaceholderIndex, authorLine.size() - authorPlaceholderIndex, book.author[authorIndex]);
+			} else {
+				thisLine = book.author[authorIndex];
+			}
+			g_engine->_graphics->drawColoredText(g_engine->_screen, thisLine, textX, textY + increment + _lineHeight * authorIndex, 340, defaultColor, g_engine->_smallFont);
+			authorIndex++;
+		}
 
 		// Genre
 		Common::String genreLine = _computerText[5][0];
@@ -258,7 +298,9 @@ void Computer::handleSearchInput() {
 void Computer::handleResultsDisplay() {
 	if (_events->_lastKeyEvent == Common::KEYCODE_s) {
 		if (!_searchResults.empty()) {
-			_currentResult = (_currentResult + 1) % _searchResults.size();
+			_currentResult = (_currentResult + 1);
+			if(_currentResult >= _searchResults.size())
+				_state = STATE_MAIN_MENU;
 		}
 		_events->_lastKeyEvent = Common::KEYCODE_INVALID;
 	} else if (_events->_lastKeyEvent == Common::KEYCODE_m) {
@@ -292,16 +334,16 @@ void Computer::memorizeBook(int bookIndex) {
 
 	g_engine->_state->libraryShelf = book.shelf; // 0-based shelf index
 	g_engine->_state->selectedBookIndex = book.inventoryIndex;
-	g_engine->_state->bookLetter = book.title[0];
+	g_engine->_state->bookLetter = book.title[0][0];
 
-	debug("Memorized book '%s' with index %d, shelf %d, letter %c", book.title.c_str(), g_engine->_state->selectedBookIndex, g_engine->_state->libraryShelf, g_engine->_state->bookLetter);
+	debug("Memorized book '%s' with index %d, shelf %d, letter %c", book.title[0].c_str(), g_engine->_state->selectedBookIndex, g_engine->_state->libraryShelf, g_engine->_state->bookLetter);
 }
 
 void Computer::performSearch() {
 	_searchResults.clear();
 
 	for (int i = 0; i < _libraryBooks.size(); i++) {
-		Common::String searchField = _searchType == 0 ? _libraryBooks[i].title : _libraryBooks[i].author;
+		Common::String searchField = _searchType == 0 ? _libraryBooks[i].title[0] : _libraryBooks[i].author[0];
 
 		// Check if first letter matches (case-insensitive)
 		char firstChar = searchField[0];
diff --git a/engines/pelrock/computer.h b/engines/pelrock/computer.h
index c34e918f873..f4ff43c5bdf 100644
--- a/engines/pelrock/computer.h
+++ b/engines/pelrock/computer.h
@@ -27,11 +27,34 @@
 #include "graphics/managed_surface.h"
 
 #include "pelrock/events.h"
-#include "pelrock/library_books.h"
 
 namespace Pelrock {
 
-class PelrockEngine;
+// Book data location in ALFRED.7
+static const uint32 kBookDataOffset = 0x309E0;
+static const uint32 kBookDataEnd = 0x33F05;
+static const int kBookEntrySize = 108;  // 55 + 30 + 20 + 1 + 1 + 1
+
+// Field sizes
+static const int kBookTitleSize = 55;
+static const int kBookAuthorSize = 30;
+static const int kBookGenreSize = 20;
+
+// Status byte values
+static const byte kBookStatusCatalogOnly = 0x01;  // No physical copy
+static const byte kBookStatusPhysical = 0x02;     // Has physical copy on shelf
+
+struct LibraryBook {
+    Common::StringArray title; //55 bytes for title
+    Common::StringArray author; //30 bytes for author
+    Common::String genre; //20 bytes for genre
+    byte inventoryIndex;
+    byte shelf;       // 1-3 for row number, 0 if catalog-only
+    bool available;  // true = can be found on shelf, false = catalog only
+};
+
+
+static const LibraryBook noBook = { Common::StringArray(), Common::StringArray(), "", 0, 0, false};
 
 class Computer {
 public:
diff --git a/engines/pelrock/library_books.h b/engines/pelrock/library_books.h
deleted file mode 100644
index 7f38362b7d6..00000000000
--- a/engines/pelrock/library_books.h
+++ /dev/null
@@ -1,308 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * Alfred Pelrock Library Book Database
- * Extracted from ALFRED.7 offset 0x309E0 to 0x33F05
- *
- * This file was auto-generated by extract_alfred7_books_full.py
- * DO NOT EDIT MANUALLY
- *
- * Book format in ALFRED.7 (108 bytes per entry):
- *   - Title:        55 bytes (space-padded)
- *   - Author:       30 bytes (space-padded)
- *   - Genre:        20 bytes (space-padded)
- *   - Shelf Letter:  1 byte (A-Z or space if catalog-only)
- *   - Shelf Row:     1 byte (1-3 or space)
- *   - Status:        1 byte (0x01=catalog only, 0x02=physical copy)
- */
-
-#ifndef PELROCK_LIBRARY_BOOKS_H
-#define PELROCK_LIBRARY_BOOKS_H
-
-#include "common/scummsys.h"
-
-namespace Pelrock {
-
-// Book data location in ALFRED.7
-static const uint32 kBookDataOffset = 0x309E0;
-static const uint32 kBookDataEnd = 0x33F05;
-static const int kBookEntrySize = 108;  // 55 + 30 + 20 + 1 + 1 + 1
-
-// Field sizes
-static const int kBookTitleSize = 55;
-static const int kBookAuthorSize = 30;
-static const int kBookGenreSize = 20;
-
-// Status byte values
-static const byte kBookStatusCatalogOnly = 0x01;  // No physical copy
-static const byte kBookStatusPhysical = 0x02;     // Has physical copy on shelf
-
-struct LibraryBook {
-    Common::String title; //55 bytes for title
-    Common::String author; //30 bytes for author
-    Common::String genre; //20 bytes for genre
-    byte inventoryIndex;
-    byte shelf;       // 1-3 for row number, 0 if catalog-only
-    bool available;  // true = can be found on shelf, false = catalog only
-};
-
-static const LibraryBook noBook = {"", "", "", 0, 0, false};
-
-
-// static const int kLibraryBookCount = 125;
-
-// static const LibraryBook kLibraryBooks[] = {
-//     // Book 1: Los hombres: ¡ Como caparlos !
-//     {"Los hombres: ¡ Como caparlos !", "Herminia Gutierrez", "Feminismo", ' ', 0, false},
-//     // Book 2: Mujeres del mundo: ¡ No os depileis las ...
-//     {"Mujeres del mundo: ¡ No os depileis las axilas !", "Herminia Gutierrez", "Feminismo", ' ', 0, false},
-//     // Book 3: Gato por liebre
-//     {"Gato por liebre", "Karlos Arguiñano", "Cocina", 'I', 1, true},
-//     // Book 4: Hamlet
-//     {"Hamlet", "William Shakespeare", "Teatro", ' ', 0, false},
-//     // Book 5: Fausto
-//     {"Fausto", "Goethe", "Novela", ' ', 0, false},
-//     // Book 6: Enrique de Ofterdingen
-//     {"Enrique de Ofterdingen", "Novalis", "Novela", 'J', 1, true},
-//     // Book 7: Guia de desobediencia civica
-//     {"Guia de desobediencia civica", "Azagra", "Ensayo", ' ', 0, false},
-//     // Book 8: Literatura en la edad de piedra: cuestio...
-//     {"Literatura en la edad de piedra: cuestion de fuerza", "Profesor Cebollo", "Ensayo", ' ', 0, false},
-//     // Book 9: Turbo C ++ con Intratex
-//     {"Turbo C ++ con Intratex", "Programadores reunidos", "Informatica", ' ', 0, false},
-//     // Book 10: Codigo Maquina a pelo
-//     {"Codigo Maquina a pelo", "Programadores reunidos", "Informatica", 'K', 1, true},
-//     // Book 11: Asesino por vocacion
-//     {"Asesino por vocacion", "Chema Ton", "Novela negra", ' ', 0, false},
-//     // Book 12: Poemas rebuscados
-//     {"Poemas rebuscados", "Ramon Rodriguez", "Poesia", ' ', 0, false},
-//     // Book 13: El parto de las tortugas: Un proceso len...
-//     {"El parto de las tortugas: Un proceso lento", "Profesor Lorin Colorado", "Biologia", ' ', 0, false},
-//     // Book 14: Te parto la cara ¡ Capuyo !
-//     {"Te parto la cara ¡ Capuyo !", "Jhonny Rapper", "Critica Social", 'L', 3, true},
-//     // Book 15: En los tugurios del Himalaya
-//     {"En los tugurios del Himalaya", "Coronel Tapioca", "Aventuras", ' ', 0, false},
-//     // Book 16: En las callejuelas del amazonas
-//     {"En las callejuelas del amazonas", "Coronel Tapioca", "Aventuras", ' ', 0, false},
-//     // Book 17: En las selvas de Londres
-//     {"En las selvas de Londres", "Coronel Tapioca", "Aventuras", 'M', 1, true},
-//     // Book 18: Sueños binarios
-//     {"Sueños binarios", "Doctor Chip", "Ciencia ficcion", ' ', 0, false},
-//     // Book 19: Cien cuentos cortisimos
-//     {"Cien cuentos cortisimos", "Billi el rapido", "Novela", ' ', 0, false},
-//     // Book 20: Teoria de la relatividad
-//     {"Teoria de la relatividad", "Alfred Einstein", "Ciencia", ' ', 0, false},
-//     // Book 21: El ultimo paso para la cuadratura del ci...
-//     {"El ultimo paso para la cuadratura del circulo", "Anonimo", "Filosofia", 'N', 1, true},
-//     // Book 22: Correlacion entre el sentido de los colo...
-//     {"Correlacion entre el sentido de los colores y sonidos", "Anonimo", "Filosofia", ' ', 0, false},
-//     // Book 23: Los cuatro evangelios: Interpretados por...
-//     {"Los cuatro evangelios: Interpretados por ordenador", "Doctor Chip", "Teologia", ' ', 0, false},
-//     // Book 24: Enciclopedia de bolsillo
-//     {"Enciclopedia de bolsillo", "Profesor Lumbreras", "Enciclopedia", 'O', 1, true},
-//     // Book 25: Sigueme y revienta
-//     {"Sigueme y revienta", "M. Indurain", "Deporte", 'P', 3, true},
-//     // Book 26: Me duele too...
-//     {"Me duele too...", "Carmen Opausica", "Ensayo", ' ', 0, false},
-//     // Book 27: El Perro de Sam Rocker si tiene rabo
-//     {"El Perro de Sam Rocker si tiene rabo", "El Perro de Sam Rocker", "Biografia", ' ', 0, false},
-//     // Book 28: Donde estara mi carro ?
-//     {"Donde estara mi carro ?", "Manolo Escobar", "Aventuras", ' ', 0, false},
-//     // Book 29: Oh tu, bella flor del jardin
-//     {"Oh tu, bella flor del jardin", "La abeja Maya", "Poesia", 'Q', 2, true},
-//     // Book 30: Yogui, ¡ Bajate de esa motoneta !
-//     {"Yogui, ¡ Bajate de esa motoneta !", "Bubu", "Filosofia", ' ', 0, false},
-//     // Book 31: Odio a muerte a loh mardito rohedore !
-//     {"Odio a muerte a loh mardito rohedore !", "Er gato Yin", "Zoologia", ' ', 0, false},
-//     // Book 32: Pissi, Dissi, ¡ Sargan de su aguhero !
-//     {"Pissi, Dissi, ¡ Sargan de su aguhero !", "Er gato Yin", "Zoologia", ' ', 0, false},
-//     // Book 33: Mardito sea ... ¡ Er queso !
-//     {"Mardito sea ... ¡ Er queso !", "Er gato Yin", "Zoologia", ' ', 0, false},
-//     // Book 34: La mate porque era mia
-//     {"La mate porque era mia", "Loquillo", "Novela", ' ', 0, false},
-//     // Book 35: Los gallos: Esos desconocidos
-//     {"Los gallos: Esos desconocidos", "Paco rico", "Zoologia", ' ', 0, false},
-//     // Book 36: Cuentos corrientes
-//     {"Cuentos corrientes", "Pepe Lopez", "Novela", 'R', 1, true},
-//     // Book 37: Mas madera
-//     {"Mas madera", "R. Gepetto", "Bricolage", 'S', 2, true},
-//     // Book 38: Cuentos del Lejano Oriente
-//     {"Cuentos del Lejano Oriente", "Jhonny Mc. Dowall", "Novela", ' ', 0, false},
-//     // Book 39: Saca el guiski cheli
-//     {"Saca el guiski cheli", "Manolo lailo", "Cronica Social", ' ', 0, false},
-//     // Book 40: Ta totao !
-//     {"Ta totao !", "Lao Tse", "Filosofia", ' ', 0, false},
-//     // Book 41: Mis conversaciones con el señor Roca
-//     {"Mis conversaciones con el señor Roca", "Francisca Gando", "Epistolar", 'T', 2, true},
-//     // Book 42: Guia para la supervivencia
-//     {"Guia para la supervivencia", "Robinson Crusoe", "Manual", ' ', 0, false},
-//     // Book 43: No esperes a ser la segunda: ¡ Engaña a ...
-//     {"No esperes a ser la segunda: ¡ Engaña a tu marido !", "Herminia Gutierrez", "Feminismo", ' ', 0, false},
-//     // Book 44: Yoga Sutras
-//     {"Yoga Sutras", "Patanjali", "Filosofia", 'X', 3, true},
-//     // Book 45: El juego de los Abalorios
-//     {"El juego de los Abalorios", "Herman Hesse", "Novela", ' ', 0, false},
-//     // Book 46: Tienes suerte de ser tan pequeño, ¡ mard...
-//     {"Tienes suerte de ser tan pequeño, ¡ mardito roedo !", "Er gato Yin", "Novela", ' ', 0, false},
-//     // Book 47: Como hacerse rico en diez minutos
-//     {"Como hacerse rico en diez minutos", "El Dioni", "Manual", 'Y', 1, true},
-//     // Book 48: Te querre a pesar de tu madre
-//     {"Te querre a pesar de tu madre", "Corin Tellado", "Novela rosa", ' ', 0, false},
-//     // Book 49: Hasta que el mando a distancia nos separ...
-//     {"Hasta que el mando a distancia nos separe", "Corin Tellado", "Novela rosa", ' ', 0, false},
-//     // Book 50: Una pasion ostentorea
-//     {"Una pasion ostentorea", "Corin Tellado y Jesus Gil", "Novela rosa", 'Z', 3, true},
-//     // Book 51: Por mi, como si te la machacas
-//     {"Por mi, como si te la machacas", "Seneca", "Filosofia", ' ', 0, false},
-//     // Book 52: Conversaciones con mi caballo
-//     {"Conversaciones con mi caballo", "Jesus Gil", "Humor", ' ', 0, false},
-//     // Book 53: El poder curativo de la mierda comun
-//     {"El poder curativo de la mierda comun", "Sri Ramachrinaraska", "Esoterismo", ' ', 1, true},
-//     // Book 54: La liberacion mediante la eneriga de los...
-//     {"La liberacion mediante la eneriga de los pedos", "Sri Ramachrinaraska", "Esoterismo", ' ', 0, false},
-//     // Book 55: Sobre la imperceptibilidad de lo imperce...
-//     {"Sobre la imperceptibilidad de lo imperceptible", "Perogrullo", "Ensayo", ' ', 0, false},
-//     // Book 56: Piojos; como educarlos sin traumas
-//     {"Piojos; como educarlos sin traumas", "Franz Franzfrenz", "Psicologia", ' ', 0, false},
-//     // Book 57: I Ching
-//     {"I Ching", "Richard Willem", "Filosofia", ' ', 0, false},
-//     // Book 58: No se nada, ni me importa
-//     {"No se nada, ni me importa", "Socrates", "Filosofia", 'U', 2, true},
-//     // Book 59: No he sido yo, ¡ Lo juro !
-//     {"No he sido yo, ¡ Lo juro !", "Juan Jose Gil", "Biografia", ' ', 0, false},
-//     // Book 60: Relatos cortos
-//     {"Relatos cortos", "Tachenko", "Novela de ficcion", 'V', 3, true},
-//     // Book 61: El martillo como elemento cognitivo
-//     {"El martillo como elemento cognitivo", "Friedrich Nietzsche", "Filosofia", ' ', 0, false},
-//     // Book 62: La maravillosa vida del escarabajo pelot...
-//     {"La maravillosa vida del escarabajo pelotero (v. I)", "Dr. Federico Ñazo", "Botanica", ' ', 0, false},
-//     // Book 63: La maravillosa vida del escarabajo pelot...
-//     {"La maravillosa vida del escarabajo pelotero (v. II)", "Dr. Federico Ñazo", "Botanica", ' ', 0, false},
-//     // Book 64: La maravillosa vida del escarabajo pelot...
-//     {"La maravillosa vida del escarabajo pelotero (v. III)", "Dr. Federico Ñazo", "Botanica", ' ', 0, false},
-//     // Book 65: La maravillosa vida del escarabajo pelot...
-//     {"La maravillosa vida del escarabajo pelotero (v. IV)", "Dr. Federico Ñazo", "Botanica", ' ', 0, false},
-//     // Book 66: La maravillosa vida del escarabajo pelot...
-//     {"La maravillosa vida del escarabajo pelotero (v. V)", "Dr. Federico Ñazo", "Botanica", ' ', 0, false},
-//     // Book 67: Tu eliges: Tu madre o yo
-//     {"Tu eliges: Tu madre o yo", "Anonimo", "Psicologia aplicada", 'W', 3, true},
-//     // Book 68: Mi lucha
-//     {"Mi lucha", "Adolf Hitler", "Humor negro", ' ', 0, false},
-//     // Book 69: Cuentos de amor y desidia
-//     {"Cuentos de amor y desidia", "Jardiel Poncela", "Teatro", ' ', 1, true},
-//     // Book 70: Nuevas andanzas de Zaratustra
-//     {"Nuevas andanzas de Zaratustra", "Anonimo", "Aventuras", ' ', 2, true},
-//     // Book 71: Me se cuadre ¡ Coño !
-//     {"Me se cuadre ¡ Coño !", "Sargento Cienfuegos", "Etica militar", ' ', 2, true},
-//     // Book 72: Gatos: solos o con Ketchup
-//     {"Gatos: solos o con Ketchup", "El perro de Sam Rocker", "Cocina", ' ', 1, true},
-//     // Book 73: Querida Adelaida: mi marido NO ha dejado...
-//     {"Querida Adelaida: mi marido NO ha dejado de roncar", "Maruja Mones", "Epistolar", ' ', 0, false},
-//     // Book 74: Sobre el papel de El Lepe en el nuevo Or...
-//     {"Sobre el papel de El Lepe en el nuevo Orden Mundial", "General Sintacha", "Estrategia", ' ', 0, false},
-//     // Book 75: Aqui no hay nadie que se acueste sin cen...
-//     {"Aqui no hay nadie que se acueste sin cenar (v. I)", "Fidel Castro", "Politica", ' ', 1, true},
-//     // Book 76: Aqui no hay nadie que se acueste sin cen...
-//     {"Aqui no hay nadie que se acueste sin cenar (v. II)", "Fidel Castro", "Politica", ' ', 0, false},
-//     // Book 77: Aqui no hay nadie que se acueste sin cen...
-//     {"Aqui no hay nadie que se acueste sin cenar (v. III)", "Fidel Castro", "Politica", ' ', 0, false},
-//     // Book 78: Aqui no hay nadie que se acueste sin cen...
-//     {"Aqui no hay nadie que se acueste sin cenar (v. IV)", "Fidel Castro", "Politica", ' ', 0, false},
-//     // Book 79: Aqui no hay nadie que se acueste sin cen...
-//     {"Aqui no hay nadie que se acueste sin cenar (v. V)", "Fidel Castro", "Politica", ' ', 0, false},
-//     // Book 80: Domine la metafisica en 5 dias
-//     {"Domine la metafisica en 5 dias", "Profesor Pinocho", "Manual", ' ', 1, true},
-//     // Book 81: Domine el ensamblador en 5 dias
-//     {"Domine el ensamblador en 5 dias", "Profesor Pinocho", "Manual", ' ', 1, true},
-//     // Book 82: Dominese a si mismo en 5 dias
-//     {"Dominese a si mismo en 5 dias", "Profesor Pinocho", "Manual", ' ', 1, true},
-//     // Book 83: Piernas de Ciertopelo
-//     {"Piernas de Ciertopelo", "Chichi Mondongo", "Erotica", ' ', 2, true},
-//     // Book 84: Otra vuelta de tuerca
-//     {"Otra vuelta de tuerca", "Pepe, el fontanero", "Bricolage", ' ', 2, true},
-//     // Book 85: La Tierra: ¡ Planeta limpio !
-//     {"La Tierra: ¡ Planeta limpio !", "Juan, el basurero", "Ciencia Ficcion", ' ', 2, true},
-//     // Book 86: Liberad a Brian !
-//     {"Liberad a Brian !", "Roger Rabitt", "Historica", ' ', 2, true},
-//     // Book 87: La vida es una mierda
-//     {"La vida es una mierda", "Juanjo Dido", "Ensayo", ' ', 2, true},
-//     // Book 88: No era eso lo que yo di a entender
-//     {"No era eso lo que yo di a entender", "Jesus de Nazaret", "Religion", ' ', 2, true},
-//     // Book 89: Castigos a Piratas Informaticos
-//     {"Castigos a Piratas Informaticos", "Torquemada", "Inquisicion", ' ', 1, true},
-//     // Book 90: Confiesa, bruja asquerosa
-//     {"Confiesa, bruja asquerosa", "Troquemada", "Inquisicion", ' ', 1, true},
-//     // Book 91: El cocherito lere
-//     {"El cocherito lere", "Paco costas", "Automovilismo", ' ', 1, true},
-//     // Book 92: La musica amansa a las fieras
-//     {"La musica amansa a las fieras", "Wagner", "Musica", ' ', 2, true},
-//     // Book 93: Pinocho en el Parlamento
-//     {"Pinocho en el Parlamento", "Carmen Tirosa", "Cronica Social", ' ', 0, false},
-//     // Book 94: Hagase famoso gracias a la energia de la...
-//     {"Hagase famoso gracias a la energia de las petunias", "Carmelo Cuelo", "Esoterismo", ' ', 0, false},
-//     // Book 95: Dios mio, ¡ Que cruz !
-//     {"Dios mio, ¡ Que cruz !", "Jesus de Nazaret", "Autobiografia", ' ', 0, false},
-//     // Book 96: Psicologia de la motivacion inmotivada
-//     {"Psicologia de la motivacion inmotivada", "Dr. Chemi", "Psicologia", ' ', 2, true},
-//     // Book 97: Magia rosa para iniciados
-//     {"Magia rosa para iniciados", "Manolo Lailo", "Esoterismo", ' ', 0, false},
-//     // Book 98: Un mundo Feliz
-//     {"Un mundo Feliz", "Aldous Huxley", "Novela", ' ', 0, false},
-//     // Book 99: Sexo oral y por escrito
-//     {"Sexo oral y por escrito", "Franz Masturmann", "Sexologia", ' ', 3, true},
-//     // Book 100: El contrato social de aprendizaje
-//     {"El contrato social de aprendizaje", "Rousseau", "Ensayo", ' ', 1, true},
-//     // Book 101: Vida sexual del escarabajo de la Patagon...
-//     {"Vida sexual del escarabajo de la Patagonia", "Dr. Tedio Plomez", "Botanica", ' ', 3, true},
-//     // Book 102: Manual del necrofago
-//     {"Manual del necrofago", "Jesus Gil", "Manual", ' ', 0, false},
-//     // Book 103: Canticos espirituales en formato *.ZIP
-//     {"Canticos espirituales en formato *.ZIP", "Doctor Chip", "Poesia", 'B', 1, true},
-//     // Book 104: Novela erotica en formato *.MAS
-//     {"Novela erotica en formato *.MAS", "Doctor Chip", "Erotica", ' ', 0, false},
-//     // Book 105: Plopuestas colelacionales en coyuntulas ...
-//     {"Plopuestas colelacionales en coyuntulas bilatelales", "Senadol Chan Chu Yo", "Politica", ' ', 0, false},
-//     // Book 106: Ereh un fistro
-//     {"Ereh un fistro", "Chiquito de la casa", "Humor", ' ', 0, false},
-//     // Book 107: El hacedor de la Lluvia
-//     {"El hacedor de la Lluvia", "Herman Hesse", "Cuentos", ' ', 0, false},
-//     // Book 108: Pasiones recalcitrantes
-//     {"Pasiones recalcitrantes", "Corin Tellado", "Novela rosa", 'C', 2, true},
-//     // Book 109: No me mates con tomate
-//     {"No me mates con tomate", "Karlos Arguiñano", "Cocina", ' ', 0, false},
-//     // Book 110: El valenciano en los albores del siglo X...
-//     {"El valenciano en los albores del siglo XXI", "Jaume i Pascual", "Nacionalismo", 'D', 1, true},
-//     // Book 111: El valenciano es la lengua del futuro
-//     {"El valenciano es la lengua del futuro", "Jaume i Pascual", "Nacionalismo", ' ', 0, false},
-//     // Book 112: Valencia: mes que mai
-//     {"Valencia: mes que mai", "Jaume i Pascual", "Nacionalismo", ' ', 0, false},
-//     // Book 113: Sistema inmunitario de los cefalopodos (...
-//     {"Sistema inmunitario de los cefalopodos (v. I)", "Dr. Tedio Plomez", "Biologia", 'E', 3, true},
-//     // Book 114: Sistema inmunitario de los cefalopodos (...
-//     {"Sistema inmunitario de los cefalopodos (v. II)", "Dr. Tedio Plomez", "Biologia", ' ', 0, false},
-//     // Book 115: Sistema inmunitario de los cefalopodos (...
-//     {"Sistema inmunitario de los cefalopodos (v. III)", "Dr. Tedio Plomez", "Biologia", ' ', 0, false},
-//     // Book 116: Sistema inmunitario de los cefalopodos (...
-//     {"Sistema inmunitario de los cefalopodos (v. IV)", "Dr. Tedio Plomez", "Biologia", ' ', 0, false},
-//     // Book 117: Sistema inmunitario de los cefalopodos (...
-//     {"Sistema inmunitario de los cefalopodos (v. V)", "Dr. Tedio Plomez", "Biologia", ' ', 0, false},
-//     // Book 118: Dos mas dos son cinco
-//     {"Dos mas dos son cinco", "Joan Josep Climent Colomer", "Matematicas", 'F', 1, true},
-//     // Book 119: El algebra es la base de la programacion
-//     {"El algebra es la base de la programacion", "Joan Josep Climent Colomer", "Humor absurdo", ' ', 0, false},
-//     // Book 120: Autobiografia de una miseria humana
-//     {"Autobiografia de una miseria humana", "Joan Josep Climent Colomer", "Esperpento", ' ', 0, false},
-//     // Book 121: El arte mundial antes y despues de mi
-//     {"El arte mundial antes y despues de mi", "Nacho Taulet Perman", "Arte", ' ', 0, false},
-//     // Book 122: La parte Creativa
-//     {"La parte Creativa", "Nacho Taulet Perman", "Arte", 'G', 2, true},
-//     // Book 123: Llamame cuando se muera tu abuelo
-//     {"Llamame cuando se muera tu abuelo", "Jose Bart Carrion", "Teatro", ' ', 0, false},
-//     // Book 124: Soy un incomprendido
-//     {"Soy un incomprendido", "Jose Bart Carrion", "Autobiografia", ' ', 0, false},
-//     // Book 125: El arte de limpiar botijos por dentro
-//     {"El arte de limpiar botijos por dentro", "Varios autores", "Manualidades", ' ', 0, false},
-// };
-
-} // namespace Pelrock
-
-#endif // PELROCK_LIBRARY_BOOKS_H


Commit: 8577942222838db20d3243fe4d815e44228e727f
    https://github.com/scummvm/scummvm/commit/8577942222838db20d3243fe4d815e44228e727f
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:27+02:00

Commit Message:
PELROCK: Implement loading of background book

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/computer.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 1ccd01c386b..b736bab7f60 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -2206,6 +2206,11 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		player.run();
 		break;
 	}
+	case 96: {
+		BackgroundBook book(_events, _res);
+		book.run();
+		break;
+	}
 	default: {
 		if (inventoryObject >= 11 && inventoryObject <= 47) {
 			playAlfredSpecialAnim(0);
diff --git a/engines/pelrock/computer.h b/engines/pelrock/computer.h
index f4ff43c5bdf..7e9379ccad8 100644
--- a/engines/pelrock/computer.h
+++ b/engines/pelrock/computer.h
@@ -53,9 +53,6 @@ struct LibraryBook {
     bool available;  // true = can be found on shelf, false = catalog only
 };
 
-
-static const LibraryBook noBook = { Common::StringArray(), Common::StringArray(), "", 0, 0, false};
-
 class Computer {
 public:
 	Computer(PelrockEventManager *eventMan);


Commit: 719de81bfbd27c25712545bd8786aed4d98c121b
    https://github.com/scummvm/scummvm/commit/719de81bfbd27c25712545bd8786aed4d98c121b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:27+02:00

Commit Message:
PELROCK: Removes all items when moving to Egypt

Changed paths:
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 9e5a51dec7b..8db72e9a20f 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -307,7 +307,19 @@ void PelrockEngine::travelToEgypt() {
 	delete[] palette;
 	_screen->markAllDirty();
 	_screen->update();
+
+	_alfredState.x = 575;
+	_alfredState.y = 210;
 	setScreenAndPrepare(21, ALFRED_DOWN);
+
+	// Original gives 4 items after room load (items 17, 64, 24, 59)
+	_state->inventoryItems.clear();
+	_state->selectedInventoryItem = -1;
+	// we dont want a flashing animation in this case!
+	_state->addInventoryItem(17);
+	_state->addInventoryItem(64);
+	_state->addInventoryItem(24);
+	_state->addInventoryItem(59);
 }
 
 bool PelrockEngine::renderScene(int overlayMode) {


Commit: 8962a72e542ba927e527250b1f058c0f09138f1b
    https://github.com/scummvm/scummvm/commit/8962a72e542ba927e527250b1f058c0f09138f1b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:27+02:00

Commit Message:
PELROCK: Fixes zorder

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index b736bab7f60..0478d2c396c 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -917,7 +917,7 @@ void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
 		renderScene(OVERLAY_NONE);
 		_room->findSpriteByIndex(7)->y -= 10;
 		if (_room->findSpriteByIndex(7)->y <= 70) {
-			_room->findSpriteByIndex(7)->zOrder = -1;
+			_room->findSpriteByIndex(7)->zOrder = 255;
 			break;
 		}
 		_screen->update();
@@ -1486,7 +1486,7 @@ void PelrockEngine::guardMovement() {
 			sprite->animData[0].movementFlags = 0x14; // Move left
 		}
 		if (sprite->x <= 327 && state == 2) {
-			sprite->zOrder = -1; // Hide sprite
+			sprite->zOrder = 255; // Hide sprite
 			break;
 		}
 		debug("Guard position: (%d, %d), state: %d", sprite->x, sprite->y, state);
@@ -1567,7 +1567,7 @@ void PelrockEngine::giveStoneToSlaves(int inventoryObject, HotSpot *hotspot) {
 	// drinking animation and sound
 	_sound->playSound(_room->_roomSfx[1], 2);
 
-	_room->findSpriteByIndex(0)->zOrder = -1;
+	_room->findSpriteByIndex(0)->zOrder = 255;
 
 	playSpecialAnim(1473360, true, mastersX - 6, mastersY - 1, 152, 83, 7);
 
@@ -1654,7 +1654,7 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 		bool didRender = renderScene(OVERLAY_NONE);
 
 		if (didRender) {
-			if (_room->findSpriteByIndex(swimmers[0].spriteIndex)->zOrder == -1) {
+			if (_room->findSpriteByIndex(swimmers[0].spriteIndex)->zOrder == 255) {
 				break;
 			}
 		}
@@ -1952,7 +1952,7 @@ void PelrockEngine::teleportToPrincess() {
 		thisSprite->animData[0].curFrame = 0;
 		thisSprite->zOrder = 200;
 
-		while (!shouldQuit() && _room->findSpriteByIndex(phase + 1)->zOrder != -1) {
+		while (!shouldQuit() && _room->findSpriteByIndex(phase + 1)->zOrder != 255) {
 			_events->pollEvent();
 			renderScene(OVERLAY_NONE);
 			_screen->update();
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 8db72e9a20f..302b684db99 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -461,7 +461,7 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 		PasserByAnim anim = _room->_passerByAnims->passerByAnims[animIndex];
 		if ((frameCount & anim.frameTrigger) == anim.frameTrigger) {
 			Sprite *sprite = _room->findSpriteByIndex(anim.spriteIndex);
-			if (sprite && sprite->zOrder == -1) {
+			if (sprite && sprite->zOrder == 255) {
 				debug("Starting passerby anim for sprite %d at index %d", anim.spriteIndex, animIndex);
 				sprite->zOrder = anim.targetZIndex;
 				sprite->curAnimIndex = 0;
@@ -485,7 +485,7 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 			if (sprite->x >= anim.resetCoord) {
 				sprite->x = startX;
 				sprite->y = startY;
-				sprite->zOrder = -1;
+				sprite->zOrder = 255;
 				sprite->curAnimIndex = 0;
 				sprite->animData[0].curFrame = 0;
 				_room->_passerByAnims->latch = false;
@@ -496,7 +496,7 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 			if (sprite->x <= anim.resetCoord) {
 				sprite->x = startX;
 				sprite->y = startY;
-				sprite->zOrder = -1;
+				sprite->zOrder = 255;
 				sprite->curAnimIndex = 0;
 				sprite->animData[0].curFrame = 0;
 				_room->_passerByAnims->latch = false;
@@ -506,7 +506,7 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 			if (sprite->y >= anim.resetCoord) {
 				sprite->x = startX;
 				sprite->y = startY;
-				sprite->zOrder = -1;
+				sprite->zOrder = 255;
 				sprite->curAnimIndex = 0;
 				sprite->animData[0].curFrame = 0;
 				_room->_passerByAnims->latch = false;
@@ -678,7 +678,7 @@ void PelrockEngine::updateAnimations() {
 
 	// First pass: sprites behind Alfred (sprite zOrder > alfredZOrder)
 	for (uint i = 0; i < _room->_currentRoomAnims.size(); i++) {
-		if (_room->_currentRoomAnims[i].zOrder > alfredZOrder || _room->_currentRoomAnims[i].zOrder < 0) {
+		if (_room->_currentRoomAnims[i].zOrder > alfredZOrder || _room->_currentRoomAnims[i].zOrder == 255) {
 			// debug("Drawing anim %d with zOrder %d in first pass (behind Alfred)", i, _room->_currentRoomAnims[i].zOrder);
 			drawNextFrame(&_room->_currentRoomAnims[i]);
 		}
@@ -689,7 +689,7 @@ void PelrockEngine::updateAnimations() {
 
 	// Second pass: sprites in front of Alfred (sprite zOrder <= alfredZOrder)
 	for (uint i = 0; i < _room->_currentRoomAnims.size(); i++) {
-		if (_room->_currentRoomAnims[i].zOrder <= alfredZOrder && _room->_currentRoomAnims[i].zOrder >= 0) {
+		if (_room->_currentRoomAnims[i].zOrder <= alfredZOrder && _room->_currentRoomAnims[i].zOrder != 255) {
 			// debug("Drawing anim %d with zOrder %d in second pass (in front of Alfred)", i, _room->_currentRoomAnims[i].zOrder);
 			drawNextFrame(&_room->_currentRoomAnims[i]);
 		}
@@ -734,8 +734,8 @@ void PelrockEngine::paintDebugLayer() {
 	if (showSprites) {
 		for (uint i = 0; i < _room->_currentRoomAnims.size(); i++) {
 			Sprite sprite = _room->_currentRoomAnims[i];
-			if (sprite.zOrder < 0) {
-				// Skip sprites with negative zOrder (not rendered)
+			if (sprite.zOrder == 255) {
+				// Skip disabled sprites (zOrder 0xFF)
 				continue;
 			}
 			drawRect(_screen, sprite.x, sprite.y, sprite.w, sprite.h, 14);
@@ -1309,7 +1309,7 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	}
 }
 
-void applyMovement(int16 *x, int16 *y, int8 *z, uint16 flags) {
+void applyMovement(int16 *x, int16 *y, byte *z, uint16 flags) {
 	// X-axis movement
 	if (flags & 0x10) {            // Bit 4: X movement enabled
 		int amount = flags & 0x07; // Bits 0-2: pixels per frame
@@ -1343,8 +1343,8 @@ void applyMovement(int16 *x, int16 *y, int8 *z, uint16 flags) {
 
 void PelrockEngine::drawNextFrame(Sprite *sprite) {
 	Anim &animData = sprite->animData[sprite->curAnimIndex];
-	if (sprite->zOrder == -1) {
-		// skips z0rder -1 sprites
+	if (sprite->zOrder == 255) {
+		// Skip disabled sprites (zOrder 0xFF = disabled in original game)
 		return;
 	}
 
@@ -1377,7 +1377,7 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 				animData.curLoop++;
 			} else {
 				if (sprite->disableAfterSequence && sprite->curAnimIndex == sprite->numAnims - 1) {
-					sprite->zOrder = -1;
+					sprite->zOrder = 255;
 					return;
 				}
 				animData.curFrame = 0;
@@ -2180,15 +2180,10 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			pigeons->curAnimIndex = 0;
 			pigeons->disableAfterSequence = true;
 			pigeons->animData[0].curFrame = 0;
-			while(!g_engine->shouldQuit() && pigeons->zOrder != -1) {
+			while(!g_engine->shouldQuit() && pigeons->zOrder != 255) {
 				_events->pollEvent();
 				renderScene();
 				_screen->update();
-				// debug("Pigeons animation, current anim index %d, current frame %d", pigeons->curAnimIndex, pigeons->animData[pigeons->curAnimIndex].curFrame);
-				// if(pigeons->curAnimIndex == 3 && pigeons->animData[3].curFrame == 3) {
-				// 	debug("Pigeons animation finished, hiding pigeons and enabling next part of the scene");
-				// 	pigeons->zOrder = -1;
-				// }
 				g_system->delayMillis(10);
 			}
 			_dialog->say(_res->_ingameTexts[PRACTICAR_MAS]);
@@ -2376,10 +2371,10 @@ void PelrockEngine::pyramidCollapse() {
 	// Original sprite table indices are offset by 2 from ScummVM indices due
 	// to the 2 header sprite slots in the room data.
 
-	// Hide NPC initially — binary sets sprite_2 field 0x21 = 0xFF (zOrder = -1)
+	// Hide NPC initially — binary sets sprite_2 field 0x21 = 0xFF (zOrder = 255)
 	Sprite *npc = _room->findSpriteByIndex(0);
 	if (npc)
-		npc->zOrder = -1;
+		npc->zOrder = 255;
 
 	// Start collapse animation — binary sets sprite_4 field 0x21 = 0xFE (zOrder = 254)
 	Sprite *collapseSprite = _room->findSpriteByIndex(2);
@@ -2398,7 +2393,7 @@ void PelrockEngine::pyramidCollapse() {
 		g_system->delayMillis(10);
 		collapseSprite = _room->findSpriteByIndex(2);
 		if (!collapseSprite || collapseSprite->animData[collapseSprite->curAnimIndex].curFrame >= 5) {
-			collapseSprite->zOrder = -1; // Hide collapse animation sprite after frame 5
+			collapseSprite->zOrder = 255; // Hide collapse animation sprite after frame 5
 			break;
 		}
 	}
@@ -2413,7 +2408,7 @@ void PelrockEngine::pyramidCollapse() {
 		Common::Rect copyRect(srcX, srcY, srcX + copyW, srcY + copyH);
 		_currentBackground.blitFrom(_compositeBuffer, copyRect, Common::Point(srcX, srcY));
 	}
-	_room->findSpriteByIndex(2)->zOrder = -1;
+	_room->findSpriteByIndex(2)->zOrder = 255;
 
 	_dialog->say(_res->_ingameTexts[YANOSEHACEONCOMOANTES]);
 	npc = _room->findSpriteByIndex(0);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 7a613ee0042..8e05a8469da 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -229,7 +229,7 @@ void RoomManager::disableSprite(byte roomNumber, byte spriteIndex, int persist)
 		// array every frame so a raw array index is unreliable.
 		for (uint i = 0; i < _currentRoomAnims.size(); i++) {
 			if (_currentRoomAnims[i].index == spriteIndex) {
-				_currentRoomAnims[i].zOrder = -1;
+				_currentRoomAnims[i].zOrder = 255;
 				break;
 			}
 		}
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 3440d1b0ab9..04248301f3d 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -272,7 +272,7 @@ struct Sprite {
 	uint16 stride; // 6-7
 	int numAnims;  // 8
 	int curAnimIndex = 0;
-	int8 zOrder; // 32-33
+	byte zOrder; // byte at file offset 23 (in-memory struct offset 0x21). Unsigned 0-254, 255=disabled
 
 	byte actionFlags;                  // 34
 	bool isHotspotDisabled;            // 38


Commit: 0aa41619236f59fa4cbfadce40c5b89fdc5928d7
    https://github.com/scummvm/scummvm/commit/0aa41619236f59fa4cbfadce40c5b89fdc5928d7
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:27+02:00

Commit Message:
PELROCK: Adds extra stickers with glowy eyes to statues in final fight

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 0478d2c396c..f4f603c7935 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -2125,7 +2125,9 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 					_sound->playSound(_room->_roomSfx[1], 0);
 					smokeAnimation(kFlightRooms[flightIndex].spriteIdx, true);
 					_room->addStickerToRoom(_room->_currentRoomNumber, 127 + flightIndex);
-					if(_state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) == 0b1111) {
+					_room->addStickerToRoom(52, 106 + flightIndex);
+
+					if(_state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) == 15) { // all 4 spells successful
 						HotSpot hotspot = HotSpot();
 						hotspot.actionFlags = 0;
 						hotspot.extra = 999;
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 302b684db99..6723b3ca330 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -2085,9 +2085,6 @@ void PelrockEngine::setScreen(int roomNumber) {
 
 	_room->loadRoomMetadata(&roomFile, roomNumber);
 
-	_screen->markAllDirty();
-	_screen->update();
-
 	roomFile.close();
 	delete[] palette;
 }
@@ -2359,6 +2356,9 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 	case 53:
 	case 54:
 		initGodsSequences(_room->_currentRoomNumber);
+		if(roomNumber == 52) {
+			_room->addStickerToRoom(52, 105);
+		}
 		break;
 	default:
 		break;
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 8e05a8469da..0311234fc00 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -106,6 +106,10 @@ void RoomManager::addSticker(int stickerId, int persist) {
 void RoomManager::addStickerToRoom(byte room, int stickerId, int persist) {
 	Sticker sticker = g_engine->_res->getSticker(stickerId);
 	if (room == _currentRoomNumber && persist & PERSIST_TEMP) {
+		if(hasSticker(sticker.stickerIndex)) {
+			debug("Sticker %d already exists in room %d, skipping add", stickerId, room);
+			return;
+		}
 		_roomStickers.push_back(sticker);
 	}
 	if (persist & PERSIST_PERM) {


Commit: d9d8e6cac0a98542c97be490974e1b4c20ec7e86
    https://github.com/scummvm/scummvm/commit/d9d8e6cac0a98542c97be490974e1b4c20ec7e86
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:28+02:00

Commit Message:
PELROCK: Implements background viewer

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/extrascreens.cpp
    engines/pelrock/extrascreens.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index f4f603c7935..1b5ead2c48e 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -2209,7 +2209,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		break;
 	}
 	case 96: {
-		BackgroundBook book(_events, _res);
+		BackgroundBook book(_events, _res, _room);
 		book.run();
 		break;
 	}
diff --git a/engines/pelrock/extrascreens.cpp b/engines/pelrock/extrascreens.cpp
index bb6457a29c2..d7d6e13f22b 100644
--- a/engines/pelrock/extrascreens.cpp
+++ b/engines/pelrock/extrascreens.cpp
@@ -24,6 +24,7 @@
 #include "extrascreens.h"
 #include "pelrock/extrascreens.h"
 #include "pelrock/graphics.h"
+#include "pelrock/room.h"
 #include "pelrock/util.h"
 
 namespace Pelrock {
@@ -353,7 +354,7 @@ void CDPlayer::loadControls() {
 	delete[] rawData;
 }
 
-BackgroundBook::BackgroundBook(PelrockEventManager *eventMan, ResourceManager *res) : _events(eventMan), _res(res) {
+BackgroundBook::BackgroundBook(PelrockEventManager *eventMan, ResourceManager *res, RoomManager *room) : _events(eventMan), _res(res), _room(room) {
 	init();
 }
 
@@ -410,9 +411,11 @@ void BackgroundBook::checkMouse(int x, int y) {
 		int firstItem = _selectedPage * kItemsPerPage;
 		if (y >= 72 && y < 72 + (kItemsPerPage * g_engine->_smallFont->getFontHeight()) && x >= 37 && x <= 37 + 200) {
 			int itemIndex = (y - 72) / g_engine->_smallFont->getFontHeight();
-			if (firstItem + itemIndex < _roomNames.size()) {
-				Common::String roomName = _roomNames[firstItem + itemIndex];
-				debug("Selected room: %s", roomName.c_str());
+			int roomIndex = firstItem + itemIndex;
+			if (roomIndex < (int)_roomNames.size()) {
+				_events->_leftMouseClicked = false;
+				int finalRoomIndex = roomIndex < 10 ? roomIndex: roomIndex + 2;
+				showRoom(finalRoomIndex);
 			}
 		}
 
@@ -440,17 +443,14 @@ void BackgroundBook::loadRoomNames() {
 		error("Couldnt find file JUEGO.EXE");
 	}
 
-	size_t namesSize = 1335;
-	juegoExe.seek(0x49315, SEEK_SET);
+	size_t namesSize = 1297;
+	juegoExe.seek(299797, SEEK_SET);
 	byte *namesData = new byte[namesSize];
 	juegoExe.read(namesData, namesSize);
 	uint32 pos = 0;
 	Common::String currentName = "";
 	while (pos < namesSize) {
-		if (namesData[pos] == 0xFD &&
-			namesData[pos + 1] == 0x00 &&
-			namesData[pos + 2] == 0x08 &&
-			namesData[pos + 3] == 0x02) {
+		if (namesData[pos] == 0xFD) {
 			if (currentName.size() > 0) {
 				roomNames.push_back(currentName);
 			}
@@ -470,16 +470,17 @@ void BackgroundBook::loadRoomNames() {
 void BackgroundBook::drawScreen() {
 	_compositeScreen.blitFrom(_backgroundScreen);
 	drawButtons();
-	g_engine->_screen->blitFrom(_compositeScreen);
 
+	if(thumbSurface) {
+		_compositeScreen.blitFrom(*thumbSurface, Common::Point(338, 120));
+	}
+	g_engine->_screen->blitFrom(_compositeScreen);
 
 	int firstItem = _selectedPage * kItemsPerPage;
 	for(int i = 0; i < kItemsPerPage; i++) {
 		if(firstItem + i >= _roomNames.size()) {
 			break;
 		}
-		// g_engine->_graphics->drawColoredTexts(_compositeScreen, _roomNames[i], 37, 72 + (i * g_engine->_smallFont->getFontHeight()), 640, 0, Graphics::kTextAlignLeft);
-	// g_engine->_graphics->drawColoredTexts(_compositeScreen, _spell->text, textX, textY, 640, 0, g_engine->_smallFont);
 		g_engine->_smallFont->drawString(g_engine->_screen, _roomNames[firstItem + i], 37, 72 + (i * g_engine->_smallFont->getFontHeight()), 640, 2, Graphics::kTextAlignLeft);
 	}
 }
@@ -519,6 +520,28 @@ void BackgroundBook::loadButtons() {
 	alfred7.close();
 }
 
+void BackgroundBook::showRoom(int roomIndex) {
+	Common::File roomFile;
+	if (!roomFile.open(Common::Path("ALFRED.1"))) {
+		warning("BackgroundBook: Could not open ALFRED.1");
+		return;
+	}
+
+	int roomOffset = roomIndex * kRoomStructSize;
+	Graphics::ManagedSurface bgSurface(640, 400, Graphics::PixelFormat::createFormatCLUT8());
+
+	byte *roomPalette = new byte[256 * 3];
+	_room->getPalette(&roomFile, roomOffset, roomPalette);
+	_room->getBackground(&roomFile, roomOffset, (byte *)bgSurface.getPixels());
+	roomFile.close();
+
+	thumbSurface = bgSurface.scale(160, 100);
+	// Set room palette and display the background
+	g_system->getPaletteManager()->setPalette(roomPalette, 0, 256);
+
+	bgSurface.free();
+}
+
 void BackgroundBook::cleanup() {
 	_compositeScreen.free();
 	_backgroundScreen.free();
@@ -526,6 +549,15 @@ void BackgroundBook::cleanup() {
 		delete[] _palette;
 		_palette = nullptr;
 	}
+	for (int i = 0; i < 2; i++) {
+		for (int j = 0; j < 2; j++) {
+			delete[] _buttons[i][j];
+		}
+	}
+	if (thumbSurface) {
+		thumbSurface->free();
+		thumbSurface = nullptr;
+	}
 }
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/extrascreens.h b/engines/pelrock/extrascreens.h
index f0226b1d965..e0c68dd9fc6 100644
--- a/engines/pelrock/extrascreens.h
+++ b/engines/pelrock/extrascreens.h
@@ -148,7 +148,7 @@ enum Buttons {
 	int kItemsPerPage = 22;
 
 public:
-	BackgroundBook(PelrockEventManager *eventMan, ResourceManager *res);
+	BackgroundBook(PelrockEventManager *eventMan, ResourceManager *res, RoomManager *room);
 	~BackgroundBook();
 
 	void run();
@@ -162,12 +162,15 @@ private:
 	void loadBackground();
 	void checkMouse(int x, int y);
 	BackgroundBook::Buttons isButtonClicked(int x, int y);
+	void showRoom(int roomIndex);
 	void cleanup();
 
 	PelrockEventManager *_events;
 	ResourceManager *_res;
+	RoomManager *_room;
 	Graphics::ManagedSurface _backgroundScreen;
 	Graphics::ManagedSurface _compositeScreen;
+	Graphics::ManagedSurface *thumbSurface = nullptr;
 	byte *_palette;
 	byte *_buttons[2][2];
 	Buttons _selectedButton = NO_BG_BUTTON;
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 6723b3ca330..2b0bf43e1ee 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1809,7 +1809,7 @@ void PelrockEngine::gameLoop() {
 		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
 	}
 	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_b) {
-		BackgroundBook backgroundBook(_events, _res);
+		BackgroundBook backgroundBook(_events, _res, _room);
 		backgroundBook.run();
 		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
 	}
@@ -2074,7 +2074,7 @@ void PelrockEngine::setScreen(int roomNumber) {
 
 	byte *palette = new byte[256 * 3];
 	_room->getPalette(&roomFile, roomOffset, palette);
-
+	memcpy(_room->_roomPalette, palette, 768);
 	_currentBackground.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 	_room->getBackground(&roomFile, roomOffset, (byte *)_currentBackground.getPixels());
 
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 0311234fc00..cfd51083793 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -66,7 +66,6 @@ void RoomManager::getPalette(Common::File *roomFile, int roomOffset, byte *palet
 		palette[i * 3 + 1] = palette[i * 3 + 1] << 2;
 		palette[i * 3 + 2] = palette[i * 3 + 2] << 2;
 	}
-	memcpy(_roomPalette, palette, 768);
 }
 
 void RoomManager::getBackground(Common::File *roomFile, int roomOffset, byte *background) {


Commit: cbf01025e923469ba08e940e30150a92ac5701cc
    https://github.com/scummvm/scummvm/commit/cbf01025e923469ba08e940e30150a92ac5701cc
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:28+02:00

Commit Message:
PELROCK: Refactor cdplayer, background book, spellbook

Changed paths:
  A engines/pelrock/backgroundbook.cpp
  A engines/pelrock/backgroundbook.h
  A engines/pelrock/cdplayer.cpp
  A engines/pelrock/cdplayer.h
  A engines/pelrock/spellbook.cpp
  A engines/pelrock/spellbook.h
  R engines/pelrock/extrascreens.cpp
  R engines/pelrock/extrascreens.h
    engines/pelrock/actions.cpp
    engines/pelrock/module.mk
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/sound.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 1b5ead2c48e..81068b91b1d 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -21,11 +21,13 @@
 
 #include "graphics/paletteman.h"
 
-#include "pelrock.h"
+
 #include "pelrock/actions.h"
-#include "pelrock/extrascreens.h"
+#include "pelrock/backgroundbook.h"
+#include "pelrock/cdplayer.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
+#include "pelrock/spellbook.h"
 #include "pelrock/util.h"
 
 namespace Pelrock {
diff --git a/engines/pelrock/backgroundbook.cpp b/engines/pelrock/backgroundbook.cpp
new file mode 100644
index 00000000000..8573849ec81
--- /dev/null
+++ b/engines/pelrock/backgroundbook.cpp
@@ -0,0 +1,235 @@
+/* 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 "common/events.h"
+#include "graphics/paletteman.h"
+
+#include "pelrock/backgroundbook.h"
+#include "pelrock/room.h"
+#include "pelrock/util.h"
+
+namespace Pelrock {
+
+BackgroundBook::BackgroundBook(PelrockEventManager *eventMan, ResourceManager *res, RoomManager *room)
+	: _events(eventMan), _res(res), _room(room) {
+	init();
+}
+
+BackgroundBook::~BackgroundBook() {
+	cleanup();
+}
+
+void BackgroundBook::run() {
+	g_engine->changeCursor(DEFAULT);
+	while (!g_engine->shouldQuit()) {
+		_events->pollEvent();
+		checkMouse(_events->_mouseX, _events->_mouseY);
+
+		if (_events->_rightMouseClicked) {
+			_events->_rightMouseClicked = false;
+			break;
+		}
+
+		drawScreen();
+		g_engine->_screen->markAllDirty();
+		g_engine->_screen->update();
+		g_system->delayMillis(10);
+	}
+	g_engine->_screen->clear(0);
+	g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
+}
+
+void BackgroundBook::init() {
+	_compositeScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
+	loadBackground();
+	loadButtons();
+	loadRoomNames();
+}
+
+void BackgroundBook::checkMouse(int x, int y) {
+	if (_events->_leftMouseClicked) {
+		switch (_selectedButton) {
+		case PREVIOUS_BUTTON:
+			if (_selectedPage > 0) {
+				_selectedPage--;
+			}
+			break;
+		case NEXT_BUTTON:
+			if ((_selectedPage + 1) * kItemsPerPage < (int)_roomNames.size()) {
+				_selectedPage++;
+			}
+			break;
+		default:
+			break;
+		}
+		_selectedButton = NO_BG_BUTTON;
+
+		int firstItem = _selectedPage * kItemsPerPage;
+		if (y >= 72 && y < 72 + (kItemsPerPage * g_engine->_smallFont->getFontHeight()) && x >= 37 && x <= 37 + 200) {
+			int itemIndex = (y - 72) / g_engine->_smallFont->getFontHeight();
+			int roomIndex = firstItem + itemIndex;
+			if (roomIndex < (int)_roomNames.size()) {
+				_events->_leftMouseClicked = false;
+				int finalRoomIndex = roomIndex < 10 ? roomIndex : roomIndex + 2;
+				showRoom(finalRoomIndex);
+			}
+		}
+
+		_events->_leftMouseClicked = false;
+	}
+
+	if (_events->_leftMouseButton != 0 && _selectedButton == NO_BG_BUTTON) {
+		_selectedButton = isButtonClicked(_events->_mouseX, _events->_mouseY);
+	}
+}
+
+BackgroundBook::Buttons BackgroundBook::isButtonClicked(int x, int y) {
+	for (int i = 0; i < 2; i++) {
+		if (_buttonRects[i].contains(x, y)) {
+			return static_cast<Buttons>(i);
+		}
+	}
+	return NO_BG_BUTTON;
+}
+
+void BackgroundBook::loadRoomNames() {
+	Common::StringArray roomNames;
+	Common::File juegoExe;
+	if (!juegoExe.open(Common::Path("JUEGO.EXE"))) {
+		error("Couldnt find file JUEGO.EXE");
+	}
+
+	size_t namesSize = 1297;
+	juegoExe.seek(299797, SEEK_SET);
+	byte *namesData = new byte[namesSize];
+	juegoExe.read(namesData, namesSize);
+	uint32 pos = 0;
+	Common::String currentName = "";
+	while (pos < namesSize) {
+		if (namesData[pos] == 0xFD) {
+			if (currentName.size() > 0) {
+				roomNames.push_back(currentName);
+			}
+			currentName = "";
+			pos += 4;
+			continue;
+		}
+		currentName += (char)namesData[pos];
+		pos++;
+	}
+	delete[] namesData;
+	juegoExe.close();
+	_roomNames = roomNames;
+}
+
+void BackgroundBook::drawScreen() {
+	_compositeScreen.blitFrom(_backgroundScreen);
+	drawButtons();
+
+	if (thumbSurface) {
+		_compositeScreen.blitFrom(*thumbSurface, Common::Point(338, 120));
+	}
+	g_engine->_screen->blitFrom(_compositeScreen);
+
+	int firstItem = _selectedPage * kItemsPerPage;
+	for (int i = 0; i < kItemsPerPage; i++) {
+		if (firstItem + i >= (int)_roomNames.size()) {
+			break;
+		}
+		g_engine->_smallFont->drawString(g_engine->_screen, _roomNames[firstItem + i], 37, 72 + (i * g_engine->_smallFont->getFontHeight()), 640, 2, Graphics::kTextAlignLeft);
+	}
+}
+
+void BackgroundBook::drawButtons() {
+	for (int i = 0; i < 2; i++) {
+		if (_selectedButton == i) {
+			drawSpriteToBuffer(_compositeScreen, _buttons[i][0], _buttonRects[i].left, _buttonRects[i].top, _buttonRects[i].width(), _buttonRects[i].height(), 207);
+		} else {
+			drawSpriteToBuffer(_compositeScreen, _buttons[i][1], _buttonRects[i].left, _buttonRects[i].top, _buttonRects[i].width(), _buttonRects[i].height(), 207);
+		}
+	}
+}
+
+void BackgroundBook::loadBackground() {
+	_backgroundScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
+	_palette = new byte[768];
+	_res->getExtraScreen(13, (byte *)_backgroundScreen.getPixels(), _palette);
+	g_system->getPaletteManager()->setPalette(_palette, 0, 256);
+}
+
+void BackgroundBook::loadButtons() {
+	Common::File alfred7;
+	if (!alfred7.open("ALFRED.7")) {
+		return;
+	}
+	alfred7.seek(3188448, SEEK_SET);
+	for (int i = 0; i < 2; i++) {
+		for (int j = 0; j < 2; j++) {
+			int w = _buttonRects[i].width();
+			int h = _buttonRects[i].height();
+			_buttons[i][j] = new byte[w * h];
+			alfred7.read(_buttons[i][j], w * h);
+		}
+	}
+	alfred7.close();
+}
+
+void BackgroundBook::showRoom(int roomIndex) {
+	Common::File roomFile;
+	if (!roomFile.open(Common::Path("ALFRED.1"))) {
+		warning("BackgroundBook: Could not open ALFRED.1");
+		return;
+	}
+
+	int roomOffset = roomIndex * kRoomStructSize;
+	Graphics::ManagedSurface bgSurface(640, 400, Graphics::PixelFormat::createFormatCLUT8());
+
+	byte *roomPalette = new byte[256 * 3];
+	_room->getPalette(&roomFile, roomOffset, roomPalette);
+	_room->getBackground(&roomFile, roomOffset, (byte *)bgSurface.getPixels());
+	roomFile.close();
+
+	thumbSurface = bgSurface.scale(160, 100);
+	// Set room palette and display the background
+	g_system->getPaletteManager()->setPalette(roomPalette, 0, 256);
+
+	bgSurface.free();
+}
+
+void BackgroundBook::cleanup() {
+	_compositeScreen.free();
+	_backgroundScreen.free();
+	if (_palette) {
+		delete[] _palette;
+		_palette = nullptr;
+	}
+	for (int i = 0; i < 2; i++) {
+		for (int j = 0; j < 2; j++) {
+			delete[] _buttons[i][j];
+		}
+	}
+	if (thumbSurface) {
+		thumbSurface->free();
+		thumbSurface = nullptr;
+	}
+}
+
+} // End of namespace Pelrock
diff --git a/engines/pelrock/backgroundbook.h b/engines/pelrock/backgroundbook.h
new file mode 100644
index 00000000000..f037f4d32ab
--- /dev/null
+++ b/engines/pelrock/backgroundbook.h
@@ -0,0 +1,82 @@
+/* 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 PELROCK_BACKGROUNDBOOK_H
+#define PELROCK_BACKGROUNDBOOK_H
+
+#include "common/array.h"
+#include "common/rect.h"
+#include "common/str.h"
+#include "graphics/managed_surface.h"
+
+#include "pelrock/pelrock.h"
+
+namespace Pelrock {
+
+class BackgroundBook {
+
+	enum Buttons {
+		PREVIOUS_BUTTON,
+		NEXT_BUTTON,
+		NO_BG_BUTTON
+	};
+
+	int kItemsPerPage = 22;
+
+public:
+	BackgroundBook(PelrockEventManager *eventMan, ResourceManager *res, RoomManager *room);
+	~BackgroundBook();
+
+	void run();
+
+private:
+	void init();
+	void loadRoomNames();
+	void drawScreen();
+	void drawButtons();
+	void loadButtons();
+	void loadBackground();
+	void checkMouse(int x, int y);
+	Buttons isButtonClicked(int x, int y);
+	void showRoom(int roomIndex);
+	void cleanup();
+
+	PelrockEventManager *_events;
+	ResourceManager *_res;
+	RoomManager *_room;
+	Graphics::ManagedSurface _backgroundScreen;
+	Graphics::ManagedSurface _compositeScreen;
+	Graphics::ManagedSurface *thumbSurface = nullptr;
+	byte *_palette;
+	byte *_buttons[2][2];
+	Buttons _selectedButton = NO_BG_BUTTON;
+	int _selectedPage = 0;
+
+	Common::Rect _buttonRects[2] = {
+		Common::Rect(Common::Point(238, 104), 28, 44), // Previous
+		Common::Rect(Common::Point(238, 178), 28, 44), // Next
+	};
+	Common::StringArray _roomNames;
+};
+
+} // End of namespace Pelrock
+
+#endif
diff --git a/engines/pelrock/cdplayer.cpp b/engines/pelrock/cdplayer.cpp
new file mode 100644
index 00000000000..7b4b5d983b5
--- /dev/null
+++ b/engines/pelrock/cdplayer.cpp
@@ -0,0 +1,211 @@
+/* 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 "common/events.h"
+#include "graphics/paletteman.h"
+
+#include "pelrock/cdplayer.h"
+#include "pelrock/room.h"
+#include "pelrock/util.h"
+
+namespace Pelrock {
+
+CDPlayer::CDPlayer(PelrockEventManager *eventMan, ResourceManager *res, SoundManager *sound)
+	: _events(eventMan), _res(res), _sound(sound) {
+	init();
+}
+
+CDPlayer::~CDPlayer() {
+	cleanup();
+}
+
+void CDPlayer::run() {
+	g_engine->changeCursor(DEFAULT);
+
+	while (!g_engine->shouldQuit()) {
+		_events->pollEvent();
+		checkMouse(_events->_mouseX, _events->_mouseY);
+
+		if (_events->_rightMouseClicked) {
+			_events->_rightMouseClicked = false;
+			break;
+		}
+
+		drawScreen();
+		g_engine->_screen->markAllDirty();
+		g_engine->_screen->update();
+		g_system->delayMillis(10);
+	}
+	g_engine->_screen->clear(0);
+	// Restore room palette
+	g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
+	_sound->stopMusic();
+	_sound->playMusicTrack(g_engine->_room->_musicTrack, true);
+}
+
+void CDPlayer::init() {
+	_compositeScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
+	loadBackground();
+	loadControls();
+	loadTrackNames();
+}
+
+void CDPlayer::loadTrackNames() {
+	Common::File juegoFile;
+	if (!juegoFile.open("JUEGO.EXE")) {
+		return;
+	}
+	juegoFile.seek(301203, SEEK_SET);
+
+	for (int i = 0; i < 31; i++) {
+		trackNames[i] = juegoFile.readString(0, 30);
+	}
+
+	juegoFile.close();
+}
+
+void CDPlayer::drawScreen() {
+	_compositeScreen.blitFrom(_backgroundScreen);
+	drawSpriteToBuffer(_compositeScreen, _controls, 1, 1, 213, 72, 207);
+
+	drawButtons();
+
+	g_engine->_screen->blitFrom(_compositeScreen);
+	g_engine->_smallFont->drawString(g_engine->_screen, trackNames[_selectedTrack - 2], 26, 17, 640, 255, Graphics::kTextAlignLeft);
+}
+
+void CDPlayer::drawButtons() {
+	for (int i = 0; i < 5; i++) {
+		if (_selectedButton == i) {
+			drawSpriteToBuffer(_compositeScreen, buttons[i][1], _buttonRects[i].left, _buttonRects[i].top, _buttonRects[i].width(), _buttonRects[i].height(), 207);
+		} else {
+			drawSpriteToBuffer(_compositeScreen, buttons[i][0], _buttonRects[i].left, _buttonRects[i].top, _buttonRects[i].width(), _buttonRects[i].height(), 207);
+		}
+	}
+}
+
+void CDPlayer::loadBackground() {
+	_backgroundScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
+	_palette = new byte[768];
+	_res->getExtraScreen(10, (byte *)_backgroundScreen.getPixels(), _palette);
+	g_system->getPaletteManager()->setPalette(_palette, 0, 256);
+}
+
+void CDPlayer::cleanup() {
+	_backgroundScreen.free();
+	_compositeScreen.free();
+
+	if (_palette) {
+		delete[] _palette;
+		_palette = nullptr;
+	}
+
+	for (int i = 0; i < 5; i++) {
+		delete[] buttons[i][0];
+		delete[] buttons[i][1];
+	}
+
+	g_engine->_screen->markAllDirty();
+	g_engine->_screen->update();
+}
+
+void CDPlayer::checkMouse(int x, int y) {
+	if (_events->_leftMouseClicked) {
+		switch (_selectedButton) {
+		case STOP_BUTTON:
+			_sound->stopMusic();
+			break;
+		case PAUSE_BUTTON:
+			_sound->pauseMusic();
+			break;
+		case PLAY_BUTTON:
+			if (_sound->isPaused() && _sound->getCurrentMusicTrack() == _selectedTrack) {
+				_sound->playMusicTrack(_selectedTrack, true);
+			} else {
+				_sound->stopMusic();
+				_sound->playMusicTrack(_selectedTrack, true);
+			}
+			break;
+		case PREVIOUS_BUTTON:
+			if (_selectedTrack > 2) {
+				_selectedTrack--;
+			}
+			break;
+		case NEXT_BUTTON:
+			if (_selectedTrack < 32) {
+				_selectedTrack++;
+			}
+			break;
+		default:
+			break;
+		}
+		_selectedButton = NO_CDBUTTON;
+		_events->_leftMouseClicked = false;
+	}
+
+	if (_events->_leftMouseButton != 0 && _selectedButton == NO_CDBUTTON) {
+		_selectedButton = isButtonClicked(_events->_mouseX, _events->_mouseY);
+		if (_selectedButton != NO_CDBUTTON) {
+			_sound->playSound("11ZZZZZZ.SMP", 0);
+		}
+	}
+}
+
+CDPlayer::CDControls CDPlayer::isButtonClicked(int x, int y) {
+	for (int i = 0; i < 5; i++) {
+		if (_buttonRects[i].contains(x, y)) {
+			return static_cast<CDControls>(i);
+		}
+	}
+	return NO_CDBUTTON;
+}
+
+void CDPlayer::loadControls() {
+	_controls = new byte[213 * 72];
+	Common::File alfred7;
+	if (!alfred7.open("ALFRED.7")) {
+		return;
+	}
+	alfred7.seek(2214760, SEEK_SET);
+	byte *compressedData = nullptr;
+	size_t outSize = 0;
+	readUntilBuda(&alfred7, 2214760, compressedData, outSize);
+	byte *rawData = nullptr;
+
+	rleDecompress(compressedData, outSize, 0, 0, &rawData, true);
+
+	uint32 pos = 213 * 72;
+	Common::copy(rawData, rawData + pos, _controls);
+	for (int i = 0; i < 5; i++) {
+		for (int j = 0; j < 2; j++) {
+			int w = _buttonRects[i].width();
+			int h = _buttonRects[i].height();
+			buttons[i][j] = new byte[w * h];
+			Common::copy(rawData + pos, rawData + pos + w * h, buttons[i][j]);
+			pos += w * h;
+		}
+	}
+	alfred7.close();
+	delete[] compressedData;
+	delete[] rawData;
+}
+
+} // End of namespace Pelrock
diff --git a/engines/pelrock/cdplayer.h b/engines/pelrock/cdplayer.h
new file mode 100644
index 00000000000..23e781f8689
--- /dev/null
+++ b/engines/pelrock/cdplayer.h
@@ -0,0 +1,82 @@
+/* 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 PELROCK_CDPLAYER_H
+#define PELROCK_CDPLAYER_H
+
+#include "common/rect.h"
+#include "common/str.h"
+#include "graphics/managed_surface.h"
+
+#include "pelrock/pelrock.h"
+
+namespace Pelrock {
+
+class CDPlayer {
+
+	enum CDControls {
+		STOP_BUTTON,
+		PAUSE_BUTTON,
+		PLAY_BUTTON,
+		PREVIOUS_BUTTON,
+		NEXT_BUTTON,
+		NO_CDBUTTON
+	};
+
+public:
+	CDPlayer(PelrockEventManager *eventMan, ResourceManager *res, SoundManager *sound);
+	~CDPlayer();
+
+	void run();
+
+private:
+	void init();
+	void loadTrackNames();
+	void drawScreen();
+	void drawButtons();
+	void loadBackground();
+	void loadControls();
+	void checkMouse(int x, int y);
+	void cleanup();
+	CDControls isButtonClicked(int x, int y);
+
+	ResourceManager *_res;
+	SoundManager *_sound;
+	PelrockEventManager *_events;
+	Graphics::ManagedSurface _backgroundScreen;
+	Graphics::ManagedSurface _compositeScreen;
+	byte *_palette;
+	byte *_controls;
+	Common::String trackNames[31];
+	byte *buttons[5][2];
+	Common::Rect _buttonRects[5] = {
+		Common::Rect(Common::Point(17, 46), 37, 26),  // Stop
+		Common::Rect(Common::Point(57, 48), 33, 23),  // Pause
+		Common::Rect(Common::Point(92, 44), 34, 28),  // Play
+		Common::Rect(Common::Point(128, 45), 38, 24), // Previous
+		Common::Rect(Common::Point(168, 44), 41, 28)  // Next
+	};
+	int _selectedTrack = 2;
+	CDControls _selectedButton = NO_CDBUTTON;
+};
+
+} // End of namespace Pelrock
+
+#endif
diff --git a/engines/pelrock/extrascreens.cpp b/engines/pelrock/extrascreens.cpp
deleted file mode 100644
index d7d6e13f22b..00000000000
--- a/engines/pelrock/extrascreens.cpp
+++ /dev/null
@@ -1,563 +0,0 @@
-/* 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 "common/events.h"
-#include "graphics/paletteman.h"
-
-#include "extrascreens.h"
-#include "pelrock/extrascreens.h"
-#include "pelrock/graphics.h"
-#include "pelrock/room.h"
-#include "pelrock/util.h"
-
-namespace Pelrock {
-
-SpellBook::SpellBook(PelrockEventManager *eventMan, ResourceManager *res)
-	: _palette(nullptr),
-	  _events(eventMan),
-	  _res(res),
-	  _spell(nullptr) {
-	init();
-}
-
-SpellBook::~SpellBook() {
-	cleanup();
-}
-
-Spell *SpellBook::run() {
-	loadBackground();
-	g_engine->changeCursor(DEFAULT);
-	bool exit = false;
-	while (!g_engine->shouldQuit() && !exit) {
-		_events->pollEvent();
-		drawScreen();
-		if (_events->_leftMouseClicked) {
-			_events->_leftMouseClicked = false;
-			exit = checkMouse(_events->_mouseClickX, _events->_mouseClickY);
-		}
-		g_engine->_screen->markAllDirty();
-		g_engine->_screen->update();
-		g_system->delayMillis(10);
-	}
-	g_engine->_screen->clear(0);
-	// Restore room palette
-	g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
-	return _selectedSpell;
-}
-
-void SpellBook::init() {
-	_compositeScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
-}
-
-void SpellBook::selectPage(int page) {
-	debug("Selected spell page: %d", page);
-	_spell = new Spell();
-	_spell->page = page;
-	Common::File alfred7;
-	if (!alfred7.open("ALFRED.7")) {
-		return;
-	}
-
-	Common::File juegoFile;
-	if (!juegoFile.open("JUEGO.EXE")) {
-		return;
-	}
-
-	alfred7.seek(1268719, SEEK_SET);
-	int w = 119;
-	int h = 99;
-	int nFrames = 13;
-	byte *compressedData = nullptr;
-	byte *spriteData = nullptr;
-	size_t outSize = 0;
-	readUntilBuda(&alfred7, 1268723, compressedData, outSize);
-	rleDecompress(compressedData, outSize, 0, w * h * nFrames, &spriteData, false);
-	_spell->image = new byte[w * h];
-	extractSingleFrame(spriteData, _spell->image, page, w, h);
-
-	juegoFile.seek(0x0004661D, SEEK_SET);
-	byte *textData = new byte[2861];
-	juegoFile.read(textData, 2861);
-
-	for (int i = 0; i < 2861; ++i) {
-		if (textData[i] == 0x0D)
-			textData[i] = 23;
-	}
-
-	Common::Array<Common::StringArray> spells = _res->processTextData(textData, 2861, true);
-	_spell->text = spells[page];
-	delete[] compressedData;
-	delete[] spriteData;
-	alfred7.close();
-	juegoFile.close();
-}
-
-void SpellBook::drawScreen() {
-	_compositeScreen.blitFrom(_backgroundScreen);
-
-	int textY = 83;
-	int textX = 317;
-
-	if (_spell != nullptr) {
-		drawSpriteToBuffer(_compositeScreen, _spell->image, 168, 143, 119, 99, 207);
-		g_engine->_graphics->drawColoredTexts(_compositeScreen, _spell->text, textX, textY, 640, 0, g_engine->_smallFont);
-	}
-
-	g_engine->_screen->blitFrom(_compositeScreen);
-	if (_spell != nullptr) {
-		g_engine->_graphics->drawColoredTexts(g_engine->_screen, _spell->text, textX, textY, 640, 0, g_engine->_smallFont);
-	}
-}
-
-void SpellBook::loadBackground() {
-	_backgroundScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
-	_palette = new byte[768];
-	_res->getExtraScreen(8, (byte *)_backgroundScreen.getPixels(), _palette);
-	g_system->getPaletteManager()->setPalette(_palette, 0, 256);
-}
-
-void SpellBook::cleanup() {
-	_backgroundScreen.free();
-	_compositeScreen.free();
-	if (_palette) {
-		delete[] _palette;
-		_palette = nullptr;
-	}
-	if (_spell) {
-		delete _spell;
-		_spell = nullptr;
-	}
-	g_engine->_screen->markAllDirty();
-	g_engine->_screen->update();
-}
-
-bool SpellBook::checkMouse(int x, int y) {
-	// Check bookmarks
-	for (int i = 0; i < 13; i++) {
-		Common::Rect r = Common::Rect(_bookmarks[i].x, _bookmarks[i].y, _bookmarks[i].x + _bookmarks[i].w, _bookmarks[i].y + _bookmarks[i].h);
-		if (r.contains(x, y)) {
-			selectPage(_bookmarks[i].page);
-			return false;
-		}
-	}
-
-	// Check text area
-	if (_spell == nullptr) {
-		return true;
-	}
-
-	Common::Rect textArea = Common::Rect(321, 81, 321 + 140, 81 + (_spell->text.size() * 10));
-	if (textArea.contains(x, y)) {
-		_selectedSpell = _spell;
-		return true;
-	}
-	return false;
-}
-
-CDPlayer::CDPlayer(PelrockEventManager *eventMan, ResourceManager *res, SoundManager *sound) : _events(eventMan), _res(res), _sound(sound) {
-	init();
-}
-
-CDPlayer::~CDPlayer() {
-	cleanup();
-}
-
-void CDPlayer::run() {
-
-	g_engine->changeCursor(DEFAULT);
-
-	while (!g_engine->shouldQuit()) {
-		_events->pollEvent();
-		checkMouse(_events->_mouseX, _events->_mouseY);
-
-		if (_events->_rightMouseClicked) {
-			_events->_rightMouseClicked = false;
-			break;
-		}
-
-		drawScreen();
-		g_engine->_screen->markAllDirty();
-		g_engine->_screen->update();
-		g_system->delayMillis(10);
-	}
-	g_engine->_screen->clear(0);
-	// Restore room palette
-	g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
-	_sound->stopMusic();
-	_sound->playMusicTrack(g_engine->_room->_musicTrack, true);
-}
-
-void CDPlayer::init() {
-	_compositeScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
-	loadBackground();
-	loadControls();
-	loadTrackNames();
-}
-
-void CDPlayer::loadTrackNames() {
-	Common::File juegoFile;
-	if (!juegoFile.open("JUEGO.EXE")) {
-		return;
-	}
-	juegoFile.seek(0x049893, SEEK_SET);
-
-	for (int i = 0; i < 31; i++) {
-		trackNames[i] = juegoFile.readString(0, 30);
-	}
-
-	juegoFile.close();
-}
-
-void CDPlayer::drawScreen() {
-	_compositeScreen.blitFrom(_backgroundScreen);
-	drawSpriteToBuffer(_compositeScreen, _controls, 1, 1, 213, 72, 207);
-
-	drawButtons();
-
-	g_engine->_screen->blitFrom(_compositeScreen);
-	g_engine->_smallFont->drawString(g_engine->_screen, trackNames[_selectedTrack - 2], 26, 17, 640, 255, Graphics::kTextAlignLeft);
-}
-
-void CDPlayer::drawButtons() {
-
-	for (int i = 0; i < 5; i++) {
-		if (_selectedButton == i) {
-			drawSpriteToBuffer(_compositeScreen, buttons[i][1], _buttonRects[i].left, _buttonRects[i].top, _buttonRects[i].width(), _buttonRects[i].height(), 207);
-		} else {
-			drawSpriteToBuffer(_compositeScreen, buttons[i][0], _buttonRects[i].left, _buttonRects[i].top, _buttonRects[i].width(), _buttonRects[i].height(), 207);
-		}
-	}
-}
-
-void CDPlayer::loadBackground() {
-	_backgroundScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
-	_palette = new byte[768];
-	_res->getExtraScreen(10, (byte *)_backgroundScreen.getPixels(), _palette);
-	g_system->getPaletteManager()->setPalette(_palette, 0, 256);
-}
-
-void CDPlayer::cleanup() {
-	_backgroundScreen.free();
-	_compositeScreen.free();
-
-	if (_palette) {
-		delete[] _palette;
-		_palette = nullptr;
-	}
-
-	for (int i = 0; i < 5; i++) {
-		delete[] buttons[i][0];
-		delete[] buttons[i][1];
-	}
-
-	g_engine->_screen->markAllDirty();
-	g_engine->_screen->update();
-}
-
-void CDPlayer::checkMouse(int x, int y) {
-	if (_events->_leftMouseClicked) {
-		switch (_selectedButton) {
-		case STOP_BUTTON:
-			_sound->stopMusic();
-			break;
-		case PAUSE_BUTTON:
-			_sound->pauseMusic();
-			break;
-		case PLAY_BUTTON:
-			if(_sound->_isPaused && _sound->_currentMusicTrack == _selectedTrack) {
-				_sound->playMusicTrack(_selectedTrack, true);
-			}
-			else {
-				_sound->stopMusic();
-				_sound->playMusicTrack(_selectedTrack, true);
-			}
-			break;
-		case PREVIOUS_BUTTON:
-			if (_selectedTrack > 2) {
-				_selectedTrack--;
-			}
-			break;
-		case NEXT_BUTTON:
-			if (_selectedTrack < 32) {
-				_selectedTrack++;
-			}
-			break;
-		default:
-			break;
-		}
-		_selectedButton = NO_CDBUTTON;
-		_events->_leftMouseClicked = false;
-	}
-
-	if (_events->_leftMouseButton != 0 && _selectedButton == NO_CDBUTTON) {
-		_selectedButton = isButtonClicked(_events->_mouseX, _events->_mouseY);
-		if(_selectedButton != NO_CDBUTTON) {
-			_sound->playSound("11ZZZZZZ.SMP", 0);
-		}
-	}
-}
-
-CDPlayer::CDControls CDPlayer::isButtonClicked(int x, int y) {
-	for (int i = 0; i < 5; i++) {
-		if (_buttonRects[i].contains(x, y)) {
-			return static_cast<CDControls>(i);
-		}
-	}
-	return NO_CDBUTTON;
-}
-
-void CDPlayer::loadControls() {
-	_controls = new byte[213 * 72];
-	Common::File alfred7;
-	if (!alfred7.open("ALFRED.7")) {
-		return;
-	}
-	alfred7.seek(2214760, SEEK_SET);
-	byte *compressedData = nullptr;
-	size_t outSize = 0;
-	readUntilBuda(&alfred7, 2214760, compressedData, outSize);
-	byte *rawData = nullptr;
-
-	rleDecompress(compressedData, outSize, 0, 0, &rawData, true);
-
-	// debug("Decompressed CD player controls: %d bytes", decompressedSize);
-	uint32 pos = 213 * 72;
-	Common::copy(rawData, rawData + pos, _controls);
-	for (int i = 0; i < 5; i++) {
-		for (int j = 0; j < 2; j++) {
-			int w = _buttonRects[i].width();
-			int h = _buttonRects[i].height();
-			buttons[i][j] = new byte[w * h];
-			Common::copy(rawData + pos, rawData + pos + w * h, buttons[i][j]);
-			pos += w * h;
-		}
-	}
-	alfred7.close();
-	delete[] compressedData;
-	delete[] rawData;
-}
-
-BackgroundBook::BackgroundBook(PelrockEventManager *eventMan, ResourceManager *res, RoomManager *room) : _events(eventMan), _res(res), _room(room) {
-	init();
-}
-
-BackgroundBook::~BackgroundBook() {
-	cleanup();
-}
-
-void BackgroundBook::run() {
-	g_engine->changeCursor(DEFAULT);
-	while (!g_engine->shouldQuit()) {
-		_events->pollEvent();
-		checkMouse(_events->_mouseX, _events->_mouseY);
-
-		if (_events->_rightMouseClicked) {
-			_events->_rightMouseClicked = false;
-			break;
-		}
-
-		drawScreen();
-		g_engine->_screen->markAllDirty();
-		g_engine->_screen->update();
-		g_system->delayMillis(10);
-	}
-	g_engine->_screen->clear(0);
-	// Restore room palette
-	g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
-}
-
-void BackgroundBook::init() {
-	_compositeScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
-	loadBackground();
-	loadButtons();
-	loadRoomNames();
-}
-
-void BackgroundBook::checkMouse(int x, int y) {
-	if (_events->_leftMouseClicked) {
-		switch (_selectedButton) {
-		case PREVIOUS_BUTTON:
-			if (_selectedPage > 0) {
-				_selectedPage--;
-			}
-			break;
-		case NEXT_BUTTON:
-			if ((_selectedPage + 1) * kItemsPerPage < _roomNames.size()) {
-				_selectedPage++;
-			}
-			break;
-		default:
-			break;
-		}
-		_selectedButton = NO_BG_BUTTON;
-
-		int firstItem = _selectedPage * kItemsPerPage;
-		if (y >= 72 && y < 72 + (kItemsPerPage * g_engine->_smallFont->getFontHeight()) && x >= 37 && x <= 37 + 200) {
-			int itemIndex = (y - 72) / g_engine->_smallFont->getFontHeight();
-			int roomIndex = firstItem + itemIndex;
-			if (roomIndex < (int)_roomNames.size()) {
-				_events->_leftMouseClicked = false;
-				int finalRoomIndex = roomIndex < 10 ? roomIndex: roomIndex + 2;
-				showRoom(finalRoomIndex);
-			}
-		}
-
-		_events->_leftMouseClicked = false;
-	}
-
-	if (_events->_leftMouseButton != 0 && _selectedButton == NO_BG_BUTTON) {
-		_selectedButton = isButtonClicked(_events->_mouseX, _events->_mouseY);
-	}
-}
-
-BackgroundBook::Buttons BackgroundBook::isButtonClicked(int x, int y) {
-	for (int i = 0; i < 2; i++) {
-		if (_buttonRects[i].contains(x, y)) {
-			return static_cast<Buttons>(i);
-		}
-	}
-	return NO_BG_BUTTON;
-}
-
-void BackgroundBook::loadRoomNames() {
-	Common::StringArray roomNames;
-	Common::File juegoExe;
-	if (!juegoExe.open(Common::Path("JUEGO.EXE"))) {
-		error("Couldnt find file JUEGO.EXE");
-	}
-
-	size_t namesSize = 1297;
-	juegoExe.seek(299797, SEEK_SET);
-	byte *namesData = new byte[namesSize];
-	juegoExe.read(namesData, namesSize);
-	uint32 pos = 0;
-	Common::String currentName = "";
-	while (pos < namesSize) {
-		if (namesData[pos] == 0xFD) {
-			if (currentName.size() > 0) {
-				roomNames.push_back(currentName);
-			}
-			currentName = "";
-			pos += 4;
-			continue;
-		}
-		currentName += (char)namesData[pos];
-		pos++;
-	}
-	delete[] namesData;
-	juegoExe.close();
-	_roomNames = roomNames;
-}
-
-
-void BackgroundBook::drawScreen() {
-	_compositeScreen.blitFrom(_backgroundScreen);
-	drawButtons();
-
-	if(thumbSurface) {
-		_compositeScreen.blitFrom(*thumbSurface, Common::Point(338, 120));
-	}
-	g_engine->_screen->blitFrom(_compositeScreen);
-
-	int firstItem = _selectedPage * kItemsPerPage;
-	for(int i = 0; i < kItemsPerPage; i++) {
-		if(firstItem + i >= _roomNames.size()) {
-			break;
-		}
-		g_engine->_smallFont->drawString(g_engine->_screen, _roomNames[firstItem + i], 37, 72 + (i * g_engine->_smallFont->getFontHeight()), 640, 2, Graphics::kTextAlignLeft);
-	}
-}
-
-void BackgroundBook::drawButtons() {
-	for (int i = 0; i < 2; i++) {
-		if (_selectedButton == i) {
-			drawSpriteToBuffer(_compositeScreen, _buttons[i][0], _buttonRects[i].left, _buttonRects[i].top, _buttonRects[i].width(), _buttonRects[i].height(), 207);
-		} else {
-			drawSpriteToBuffer(_compositeScreen, _buttons[i][1], _buttonRects[i].left, _buttonRects[i].top, _buttonRects[i].width(), _buttonRects[i].height(), 207);
-		}
-	}
-}
-
-void BackgroundBook::loadBackground() {
-	_backgroundScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
-	_palette = new byte[768];
-	_res->getExtraScreen(13, (byte *)_backgroundScreen.getPixels(), _palette);
-	g_system->getPaletteManager()->setPalette(_palette, 0, 256);
-}
-
-void BackgroundBook::loadButtons() {
-	Common::File alfred7;
-	if (!alfred7.open("ALFRED.7")) {
-		return;
-	}
-	alfred7.seek(3188448, SEEK_SET);
-	for(int i = 0; i < 2; i++) {
-		for(int j = 0; j < 2; j++) {
-			int w = _buttonRects[i].width();
-			int h = _buttonRects[i].height();
-			_buttons[i][j] = new byte[w * h];
-			alfred7.read(_buttons[i][j], w * h);
-		}
-	}
-
-	alfred7.close();
-}
-
-void BackgroundBook::showRoom(int roomIndex) {
-	Common::File roomFile;
-	if (!roomFile.open(Common::Path("ALFRED.1"))) {
-		warning("BackgroundBook: Could not open ALFRED.1");
-		return;
-	}
-
-	int roomOffset = roomIndex * kRoomStructSize;
-	Graphics::ManagedSurface bgSurface(640, 400, Graphics::PixelFormat::createFormatCLUT8());
-
-	byte *roomPalette = new byte[256 * 3];
-	_room->getPalette(&roomFile, roomOffset, roomPalette);
-	_room->getBackground(&roomFile, roomOffset, (byte *)bgSurface.getPixels());
-	roomFile.close();
-
-	thumbSurface = bgSurface.scale(160, 100);
-	// Set room palette and display the background
-	g_system->getPaletteManager()->setPalette(roomPalette, 0, 256);
-
-	bgSurface.free();
-}
-
-void BackgroundBook::cleanup() {
-	_compositeScreen.free();
-	_backgroundScreen.free();
-	if (_palette) {
-		delete[] _palette;
-		_palette = nullptr;
-	}
-	for (int i = 0; i < 2; i++) {
-		for (int j = 0; j < 2; j++) {
-			delete[] _buttons[i][j];
-		}
-	}
-	if (thumbSurface) {
-		thumbSurface->free();
-		thumbSurface = nullptr;
-	}
-}
-
-} // End of namespace Pelrock
diff --git a/engines/pelrock/extrascreens.h b/engines/pelrock/extrascreens.h
deleted file mode 100644
index e0c68dd9fc6..00000000000
--- a/engines/pelrock/extrascreens.h
+++ /dev/null
@@ -1,188 +0,0 @@
-/* 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 PELROCK_EXTRASCREENS_H
-#define PELROCK_EXTRASCREENS_H
-
-#include "graphics/managed_surface.h"
-
-#include "pelrock/pelrock.h"
-
-namespace Pelrock {
-
-class PelrockEngine;
-
-struct Spell {
-	Common::StringArray text;
-    int page;
-	byte *image;
-};
-
-struct Bookmark {
-	int16 x;
-	int16 y;
-	byte w;
-	byte h;
-	int page;
-};
-
-static const Bookmark _bookmarks[13] = {
-	{244, 8, 23, 40, 0},
-	{396, 17, 44, 20, 1},
-	{480, 68, 40, 42, 4},
-	{480, 129, 30, 25, 5},
-	{480, 224, 28, 32, 8},
-	{416, 344, 23, 17, 12},
-	{368, 346, 28, 34, 11},
-	{198, 340, 26, 28, 10},
-	{164, 336, 33, 17, 9},
-	{95, 277, 33, 24, 7},
-	{105, 227, 27, 33, 6},
-	{103, 118, 30, 26, 3},
-    {101, 78, 36, 33, 2}
-};
-
-class SpellBook {
-
-public:
-	SpellBook(PelrockEventManager *eventMan, ResourceManager *res);
-	~SpellBook();
-
-	/**
-	 * returns the spell the user selected
-	 */
-	Spell *run();
-
-private:
-	PelrockEventManager *_events;
-	ResourceManager *_res;
-	Graphics::ManagedSurface _backgroundScreen;
-	Graphics::ManagedSurface _compositeScreen;
-	byte *_palette;
-
-	Spell *_spell = nullptr;
-
-	Spell *_selectedSpell = nullptr;
-	void init();
-	void selectPage(int page);
-	void drawScreen();
-	void loadBackground();
-	void cleanup();
-	bool checkMouse(int x, int y);
-};
-
-
-class CDPlayer {
-
-enum CDControls {
-	STOP_BUTTON,
-	PAUSE_BUTTON,
-	PLAY_BUTTON,
-	PREVIOUS_BUTTON,
-	NEXT_BUTTON,
-	NO_CDBUTTON
-};
-
-public:
-	CDPlayer(PelrockEventManager *eventMan, ResourceManager *res, SoundManager *sound);
-	~CDPlayer();
-
-	void run();
-
-private:
-	void init();
-	void loadTrackNames();
-	void drawScreen();
-	void drawButtons();
-	void loadBackground();
-	void loadControls();
-	void checkMouse(int x, int y);
-	void cleanup();
-	CDControls isButtonClicked(int x, int y);
-	ResourceManager *_res;
-	SoundManager *_sound;
-	PelrockEventManager *_events;
-	Graphics::ManagedSurface _backgroundScreen;
-	Graphics::ManagedSurface _compositeScreen;
-	byte *_palette;
-	byte *_controls;
-	Common::String trackNames[31];
-	byte *buttons[5][2];
-	Common::Rect _buttonRects[5] = {
-		Common::Rect(Common::Point(17, 46), 37, 26), // Stop
-		Common::Rect(Common::Point(57, 48), 33, 23), // Pause
-		Common::Rect(Common::Point(92, 44), 34, 28), // Play
-		Common::Rect(Common::Point(128, 45), 38, 24), // Previous
-		Common::Rect(Common::Point(168, 44), 41, 28) // Next
-	};
-	int _selectedTrack = 2;
-	CDControls _selectedButton = NO_CDBUTTON;
-
-};
-
-
-class BackgroundBook {
-enum Buttons {
-	PREVIOUS_BUTTON,
-	NEXT_BUTTON,
-	NO_BG_BUTTON
-};
-	int kItemsPerPage = 22;
-
-public:
-	BackgroundBook(PelrockEventManager *eventMan, ResourceManager *res, RoomManager *room);
-	~BackgroundBook();
-
-	void run();
-
-private:
-	void init();
-	void loadRoomNames();
-	void drawScreen();
-	void drawButtons();
-	void loadButtons();
-	void loadBackground();
-	void checkMouse(int x, int y);
-	BackgroundBook::Buttons isButtonClicked(int x, int y);
-	void showRoom(int roomIndex);
-	void cleanup();
-
-	PelrockEventManager *_events;
-	ResourceManager *_res;
-	RoomManager *_room;
-	Graphics::ManagedSurface _backgroundScreen;
-	Graphics::ManagedSurface _compositeScreen;
-	Graphics::ManagedSurface *thumbSurface = nullptr;
-	byte *_palette;
-	byte *_buttons[2][2];
-	Buttons _selectedButton = NO_BG_BUTTON;
-	int _selectedPage = 0;
-
-	Common::Rect _buttonRects[2] = {
-		Common::Rect(Common::Point(238, 104), 28, 44), // Stop
-		Common::Rect(Common::Point(238, 178), 28, 44), // Pause
-	};
-	Common::StringArray _roomNames;
-};
-
-} // End of namespace Pelrock
-
-#endif // PELROCK_EXTRASCREENS_H
diff --git a/engines/pelrock/module.mk b/engines/pelrock/module.mk
index dbbf56ce979..8f1fa08902b 100644
--- a/engines/pelrock/module.mk
+++ b/engines/pelrock/module.mk
@@ -21,7 +21,9 @@ MODULE_OBJS = \
 	menu.o \
 	graphics.o \
 	saveload.o \
-	extrascreens.o
+	spellbook.o \
+	cdplayer.o \
+	backgroundbook.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_PELROCK), DYNAMIC_PLUGIN)
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 2b0bf43e1ee..ff0343e5744 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -34,12 +34,10 @@
 
 #include "backends/audiocd/audiocd.h"
 
-#include "pelrock.h"
 #include "pelrock/actions.h"
 #include "pelrock/computer.h"
 #include "pelrock/console.h"
 #include "pelrock/detection.h"
-#include "pelrock/extrascreens.h"
 #include "pelrock/fonts/small_font.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pathfinding.h"
@@ -1795,24 +1793,7 @@ void PelrockEngine::gameLoop() {
 		antiPiracyEffect();
 		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
 	}
-	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_s) {
-		SpellBook spellBook(_events, _res);
-		Spell *selectedSpell = spellBook.run();
-		if (selectedSpell != nullptr) {
-			_dialog->sayAlfred(selectedSpell->text);
-		}
-		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
-	}
-	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_c) {
-		CDPlayer cdPlayer(_events, _res, _sound);
-		cdPlayer.run();
-		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
-	}
-	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_b) {
-		BackgroundBook backgroundBook(_events, _res, _room);
-		backgroundBook.run();
-		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
-	}
+
 	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_w) {
 		Computer computer(_events);
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 4dad41aeb7f..03b48167aec 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -163,6 +163,18 @@ private:
 
 	int _numPressedX = 0;
 
+	bool _mouseDisabled = false;
+
+	int _flightFrameCounter = 0;
+	int _flightSorcererSpriteIdx = -1;
+	bool _flightSorcererAppeared = false;
+	bool _flightSpellCast = false;
+	int _flightSpellFrameCounter = 0;
+	bool _flightInBlockingAnim = false;
+	bool _disableAmbientSounds = false;
+	bool _isDogPeeing = false;
+	bool _disableAction = false;
+
 protected:
 	// Engine APIs
 	Common::Error run() override;
@@ -179,23 +191,11 @@ public:
 	ShakeEffectState _shakeEffectState;
 	Graphics::ManagedSurface _compositeBuffer; // Working composition buffer
 
-	bool _mouseDisabled = false;
-
-	int _flightFrameCounter = 0;
-	int _flightSorcererSpriteIdx = -1;
-	bool _flightSorcererAppeared = false;
-	bool _flightSpellCast = false;
-	int _flightSpellFrameCounter = 0;
-	bool _flightInBlockingAnim = false;
-	bool _disableAmbientSounds = false;
-	bool _isDogPeeing = false;
-
 	GameStateData *_state = new GameStateData();
 
 	SmallFont *_smallFont = nullptr;
 	LargeFont *_largeFont = nullptr;
 	DoubleSmallFont *_doubleSmallFont = nullptr;
-	bool _disableAction = false;
 
 public:
 	PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc);
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index 86195d7af38..baaa8701cad 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -82,8 +82,9 @@ public:
 	 *         Add kAmbientSoundSlotBase (4) to get room sound index
 	 */
 	int tickAmbientSound(uint32 frameCount);
-	bool _isPaused = false;
-	byte _currentMusicTrack = 0;
+
+	bool isPaused() const { return _isPaused; }
+	byte getCurrentMusicTrack() const { return _currentMusicTrack; }
 
 private:
 	void playSound(SonidoFile sound, int channel = -1, int loopCount = 1);
@@ -97,6 +98,8 @@ private:
 	Audio::SoundHandle _musicHandle;
 	Audio::SoundHandle _sfxHandles[kMaxChannels];
 	Common::HashMap<Common::String, SonidoFile> _soundMap;
+	bool _isPaused = false;
+	byte _currentMusicTrack = 0;
 
 
 	uint32 _cdTrackStart = 0;
diff --git a/engines/pelrock/spellbook.cpp b/engines/pelrock/spellbook.cpp
new file mode 100644
index 00000000000..ec8e1d00852
--- /dev/null
+++ b/engines/pelrock/spellbook.cpp
@@ -0,0 +1,174 @@
+/* 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 "common/events.h"
+#include "graphics/paletteman.h"
+
+#include "pelrock/graphics.h"
+#include "pelrock/room.h"
+#include "pelrock/spellbook.h"
+#include "pelrock/util.h"
+
+namespace Pelrock {
+
+SpellBook::SpellBook(PelrockEventManager *eventMan, ResourceManager *res)
+	: _palette(nullptr),
+	  _events(eventMan),
+	  _res(res),
+	  _spell(nullptr) {
+	init();
+}
+
+SpellBook::~SpellBook() {
+	cleanup();
+}
+
+Spell *SpellBook::run() {
+	loadBackground();
+	g_engine->changeCursor(DEFAULT);
+	bool exit = false;
+	while (!g_engine->shouldQuit() && !exit) {
+		_events->pollEvent();
+		drawScreen();
+		if (_events->_leftMouseClicked) {
+			_events->_leftMouseClicked = false;
+			exit = checkMouse(_events->_mouseClickX, _events->_mouseClickY);
+		}
+		g_engine->_screen->markAllDirty();
+		g_engine->_screen->update();
+		g_system->delayMillis(10);
+	}
+	g_engine->_screen->clear(0);
+	// Restore room palette
+	g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
+	return _selectedSpell;
+}
+
+void SpellBook::init() {
+	_compositeScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
+}
+
+void SpellBook::selectPage(int page) {
+	debug("Selected spell page: %d", page);
+	_spell = new Spell();
+	_spell->page = page;
+	Common::File alfred7;
+	if (!alfred7.open("ALFRED.7")) {
+		return;
+	}
+
+	Common::File juegoFile;
+	if (!juegoFile.open("JUEGO.EXE")) {
+		return;
+	}
+
+	alfred7.seek(1268719, SEEK_SET);
+	int w = 119;
+	int h = 99;
+	int nFrames = 13;
+	byte *compressedData = nullptr;
+	byte *spriteData = nullptr;
+	size_t outSize = 0;
+	readUntilBuda(&alfred7, 1268723, compressedData, outSize);
+	rleDecompress(compressedData, outSize, 0, w * h * nFrames, &spriteData, false);
+	_spell->image = new byte[w * h];
+	extractSingleFrame(spriteData, _spell->image, page, w, h);
+
+	juegoFile.seek(0x0004661D, SEEK_SET);
+	byte *textData = new byte[2861];
+	juegoFile.read(textData, 2861);
+
+	for (int i = 0; i < 2861; ++i) {
+		if (textData[i] == 0x0D)
+			textData[i] = 23;
+	}
+
+	Common::Array<Common::StringArray> spells = _res->processTextData(textData, 2861, true);
+	_spell->text = spells[page];
+	delete[] compressedData;
+	delete[] spriteData;
+	alfred7.close();
+	juegoFile.close();
+}
+
+void SpellBook::drawScreen() {
+	_compositeScreen.blitFrom(_backgroundScreen);
+
+	int textY = 83;
+	int textX = 317;
+
+	if (_spell != nullptr) {
+		drawSpriteToBuffer(_compositeScreen, _spell->image, 168, 143, 119, 99, 207);
+		g_engine->_graphics->drawColoredTexts(_compositeScreen, _spell->text, textX, textY, 640, 0, g_engine->_smallFont);
+	}
+
+	g_engine->_screen->blitFrom(_compositeScreen);
+	if (_spell != nullptr) {
+		g_engine->_graphics->drawColoredTexts(g_engine->_screen, _spell->text, textX, textY, 640, 0, g_engine->_smallFont);
+	}
+}
+
+void SpellBook::loadBackground() {
+	_backgroundScreen.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
+	_palette = new byte[768];
+	_res->getExtraScreen(8, (byte *)_backgroundScreen.getPixels(), _palette);
+	g_system->getPaletteManager()->setPalette(_palette, 0, 256);
+}
+
+void SpellBook::cleanup() {
+	_backgroundScreen.free();
+	_compositeScreen.free();
+	if (_palette) {
+		delete[] _palette;
+		_palette = nullptr;
+	}
+	if (_spell) {
+		delete _spell;
+		_spell = nullptr;
+	}
+	g_engine->_screen->markAllDirty();
+	g_engine->_screen->update();
+}
+
+bool SpellBook::checkMouse(int x, int y) {
+	// Check bookmarks
+	for (int i = 0; i < 13; i++) {
+		Common::Rect r = Common::Rect(_bookmarks[i].x, _bookmarks[i].y, _bookmarks[i].x + _bookmarks[i].w, _bookmarks[i].y + _bookmarks[i].h);
+		if (r.contains(x, y)) {
+			selectPage(_bookmarks[i].page);
+			return false;
+		}
+	}
+
+	// Check text area
+	if (_spell == nullptr) {
+		return true;
+	}
+
+	Common::Rect textArea = Common::Rect(321, 81, 321 + 140, 81 + (_spell->text.size() * 10));
+	if (textArea.contains(x, y)) {
+		_selectedSpell = _spell;
+		return true;
+	}
+	return false;
+}
+
+} // End of namespace Pelrock
diff --git a/engines/pelrock/spellbook.h b/engines/pelrock/spellbook.h
new file mode 100644
index 00000000000..6a587d194d4
--- /dev/null
+++ b/engines/pelrock/spellbook.h
@@ -0,0 +1,92 @@
+/* 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 PELROCK_SPELLBOOK_H
+#define PELROCK_SPELLBOOK_H
+
+#include "graphics/managed_surface.h"
+
+#include "pelrock/pelrock.h"
+
+namespace Pelrock {
+
+struct Spell {
+	Common::StringArray text;
+	int page;
+	byte *image;
+};
+
+struct Bookmark {
+	int16 x;
+	int16 y;
+	byte w;
+	byte h;
+	int page;
+};
+
+static const Bookmark _bookmarks[13] = {
+	{244, 8, 23, 40, 0},
+	{396, 17, 44, 20, 1},
+	{480, 68, 40, 42, 4},
+	{480, 129, 30, 25, 5},
+	{480, 224, 28, 32, 8},
+	{416, 344, 23, 17, 12},
+	{368, 346, 28, 34, 11},
+	{198, 340, 26, 28, 10},
+	{164, 336, 33, 17, 9},
+	{95, 277, 33, 24, 7},
+	{105, 227, 27, 33, 6},
+	{103, 118, 30, 26, 3},
+	{101, 78, 36, 33, 2}
+};
+
+class SpellBook {
+
+public:
+	SpellBook(PelrockEventManager *eventMan, ResourceManager *res);
+	~SpellBook();
+
+	/**
+	 * Returns the spell the user selected
+	 */
+	Spell *run();
+
+private:
+	PelrockEventManager *_events;
+	ResourceManager *_res;
+	Graphics::ManagedSurface _backgroundScreen;
+	Graphics::ManagedSurface _compositeScreen;
+	byte *_palette;
+
+	Spell *_spell = nullptr;
+	Spell *_selectedSpell = nullptr;
+
+	void init();
+	void selectPage(int page);
+	void drawScreen();
+	void loadBackground();
+	void cleanup();
+	bool checkMouse(int x, int y);
+};
+
+} // End of namespace Pelrock
+
+#endif


Commit: 8767c0a4bc4020dba8fad49b60c286a7cff4519c
    https://github.com/scummvm/scummvm/commit/8767c0a4bc4020dba8fad49b60c286a7cff4519c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:28+02:00

Commit Message:
PELROCK: Turn all offsets and sizes into constants

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/backgroundbook.cpp
    engines/pelrock/cdplayer.cpp
    engines/pelrock/fonts/large_font.cpp
    engines/pelrock/fonts/small_font.cpp
    engines/pelrock/menu.cpp
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/spellbook.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 81068b91b1d..f88b9cdbe1c 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -32,6 +32,8 @@
 
 namespace Pelrock {
 
+static const uint32 kStatuePaletteDataOffset = 0x4C700; // JUEGO.EXE — statue palette animation data
+
 #define MASCULINE true
 #define FEMININE false
 
@@ -2268,7 +2270,7 @@ void PelrockEngine::animateStatuePaletteFade(bool reverse) {
 	}
 
 	// Read the palette data structure from JUEGO.EXE
-	exeFile.seek(0x4C700, SEEK_SET);
+	exeFile.seek(kStatuePaletteDataOffset, SEEK_SET);
 
 	StatuePaletteData paletteData;
 	paletteData.x = exeFile.readUint16LE();
diff --git a/engines/pelrock/backgroundbook.cpp b/engines/pelrock/backgroundbook.cpp
index 8573849ec81..106ae4d079a 100644
--- a/engines/pelrock/backgroundbook.cpp
+++ b/engines/pelrock/backgroundbook.cpp
@@ -28,6 +28,10 @@
 
 namespace Pelrock {
 
+static const uint32 kBgBookButtonsOffset = 3188448; // ALFRED.7 — UI buttons
+static const uint32 kRoomNamesOffset     = 299797;  // JUEGO.EXE — room name strings
+static const uint32 kRoomNamesSize       = 1297;
+
 BackgroundBook::BackgroundBook(PelrockEventManager *eventMan, ResourceManager *res, RoomManager *room)
 	: _events(eventMan), _res(res), _room(room) {
 	init();
@@ -117,8 +121,8 @@ void BackgroundBook::loadRoomNames() {
 		error("Couldnt find file JUEGO.EXE");
 	}
 
-	size_t namesSize = 1297;
-	juegoExe.seek(299797, SEEK_SET);
+	size_t namesSize = kRoomNamesSize;
+	juegoExe.seek(kRoomNamesOffset, SEEK_SET);
 	byte *namesData = new byte[namesSize];
 	juegoExe.read(namesData, namesSize);
 	uint32 pos = 0;
@@ -180,7 +184,7 @@ void BackgroundBook::loadButtons() {
 	if (!alfred7.open("ALFRED.7")) {
 		return;
 	}
-	alfred7.seek(3188448, SEEK_SET);
+	alfred7.seek(kBgBookButtonsOffset, SEEK_SET);
 	for (int i = 0; i < 2; i++) {
 		for (int j = 0; j < 2; j++) {
 			int w = _buttonRects[i].width();
diff --git a/engines/pelrock/cdplayer.cpp b/engines/pelrock/cdplayer.cpp
index 7b4b5d983b5..a67575a2cbb 100644
--- a/engines/pelrock/cdplayer.cpp
+++ b/engines/pelrock/cdplayer.cpp
@@ -28,6 +28,9 @@
 
 namespace Pelrock {
 
+static const uint32 kCDPlayerTrackNamesOffset = 301203;  // JUEGO.EXE — track name strings
+static const uint32 kCDPlayerControlsOffset   = 2214760; // ALFRED.7 — controls sprite
+
 CDPlayer::CDPlayer(PelrockEventManager *eventMan, ResourceManager *res, SoundManager *sound)
 	: _events(eventMan), _res(res), _sound(sound) {
 	init();
@@ -73,7 +76,7 @@ void CDPlayer::loadTrackNames() {
 	if (!juegoFile.open("JUEGO.EXE")) {
 		return;
 	}
-	juegoFile.seek(301203, SEEK_SET);
+	juegoFile.seek(kCDPlayerTrackNamesOffset, SEEK_SET);
 
 	for (int i = 0; i < 31; i++) {
 		trackNames[i] = juegoFile.readString(0, 30);
@@ -184,10 +187,10 @@ void CDPlayer::loadControls() {
 	if (!alfred7.open("ALFRED.7")) {
 		return;
 	}
-	alfred7.seek(2214760, SEEK_SET);
+	alfred7.seek(kCDPlayerControlsOffset, SEEK_SET);
 	byte *compressedData = nullptr;
 	size_t outSize = 0;
-	readUntilBuda(&alfred7, 2214760, compressedData, outSize);
+	readUntilBuda(&alfred7, kCDPlayerControlsOffset, compressedData, outSize);
 	byte *rawData = nullptr;
 
 	rleDecompress(compressedData, outSize, 0, 0, &rawData, true);
diff --git a/engines/pelrock/fonts/large_font.cpp b/engines/pelrock/fonts/large_font.cpp
index f6aa4f12e7f..22d56c5c761 100644
--- a/engines/pelrock/fonts/large_font.cpp
+++ b/engines/pelrock/fonts/large_font.cpp
@@ -23,6 +23,8 @@
 
 namespace Pelrock {
 
+static const uint32 kLargeFontOffset = 0x7DC8; // ALFRED.7 — large font bitmap data
+
 LargeFont::LargeFont() : _fontData(nullptr) {
 }
 
@@ -36,7 +38,7 @@ bool LargeFont::load(const Common::String &filename) {
 		return false;
 	}
 
-	file.seek(0x7DC8, SEEK_SET);
+	file.seek(kLargeFontOffset, SEEK_SET);
 	const int numChars = 100;
 	const int charWidth = 12;
 	const int charHeight = 24;
diff --git a/engines/pelrock/fonts/small_font.cpp b/engines/pelrock/fonts/small_font.cpp
index 9dff0d694e2..93d06bb31d9 100644
--- a/engines/pelrock/fonts/small_font.cpp
+++ b/engines/pelrock/fonts/small_font.cpp
@@ -23,6 +23,8 @@
 
 namespace Pelrock {
 
+static const uint32 kSmallFontOffset = 0x8F32; // ALFRED.4 — small font bitmap data
+
 SmallFont::SmallFont() : _fontData(nullptr) {
 }
 
@@ -36,7 +38,7 @@ bool SmallFont::load(const Common::String &filename) {
 		return false;
 	}
 
-	file.seek(0x8F32, SEEK_SET);
+	file.seek(kSmallFontOffset, SEEK_SET);
 
 	const int dataSize = kNumChars * 8; // 256 characters, 8x8 pixels
 	debug("SmallFont::load: Loading font data of size %d from %s", dataSize, filename.c_str());
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 6682cf1ecac..01e478b3d3b 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -31,6 +31,30 @@
 
 namespace Pelrock {
 
+// ALFRED.7 — alternate settings palette
+static const uint32 kSettingsPaletteOffset = 0x2884C2;
+
+// JUEGO.EXE — inventory object descriptions
+static const uint32 kInventoryDescriptionsOffset = 0x4715E;
+static const uint32 kInventoryDescriptionsSize   = 7868;
+
+// JUEGO.EXE — in-menu text strings
+static const uint32 kMenuTextOffset = 0x49203;
+static const uint32 kMenuTextSize   = 230;
+
+// ALFRED.7 — main menu background
+static const uint32 kMainMenuPart1Offset         = 2405266;
+static const uint32 kMainMenuPart1RawSize        = 65536; // first uncompressed chunk
+static const uint32 kMainMenuPart1CompressedSize = 29418; // following compressed tail
+static const uint32 kMainMenuPart2Offset         = 2500220;
+static const uint32 kMainMenuPart2RawSize        = 32768;
+static const uint32 kMainMenuPart2CompressedSize = 30288;
+static const uint32 kMainMenuPart3Offset         = 2563266;
+static const uint32 kMainMenuPart3Size           = 92160;
+
+// ALFRED.7 — menu buttons (save/load/sound/exit, one contiguous block)
+static const uint32 kMenuButtonsOffset = 3193376;
+
 Pelrock::MenuManager::MenuManager(Graphics::Screen *screen, PelrockEventManager *events, ResourceManager *res, SoundManager *sound) : _screen(screen), _events(events), _res(res), _sound(sound) {
 }
 
@@ -239,38 +263,38 @@ void MenuManager::loadMenu() {
 	}
 
 	uint32 curPos = 0;
-	alfred7.seek(2405266, SEEK_SET);
-	alfred7.read(_mainMenu.getPixels(), 65536);
+	alfred7.seek(kMainMenuPart1Offset, SEEK_SET);
+	alfred7.read(_mainMenu.getPixels(), kMainMenuPart1RawSize);
 
-	curPos += 65536;
+	curPos += kMainMenuPart1RawSize;
 
-	byte *compressedPart1 = new byte[29418];
-	alfred7.read(compressedPart1, 29418);
+	byte *compressedPart1 = new byte[kMainMenuPart1CompressedSize];
+	alfred7.read(compressedPart1, kMainMenuPart1CompressedSize);
 	byte *decompressedPart1 = nullptr;
-	size_t decompressedSize = rleDecompress(compressedPart1, 29418, 0, 0, &decompressedPart1, true);
+	size_t decompressedSize = rleDecompress(compressedPart1, kMainMenuPart1CompressedSize, 0, 0, &decompressedPart1, true);
 
 	memcpy((byte *)_mainMenu.getPixels() + curPos, decompressedPart1, decompressedSize);
 	curPos += decompressedSize;
 
 	delete[] compressedPart1;
 	delete[] decompressedPart1;
-	alfred7.seek(2500220, SEEK_SET);
-	alfred7.read((byte *)_mainMenu.getPixels() + curPos, 32768);
-	curPos += 32768;
-	byte *compressedPart2 = new byte[30288];
-	alfred7.read(compressedPart2, 30288);
+	alfred7.seek(kMainMenuPart2Offset, SEEK_SET);
+	alfred7.read((byte *)_mainMenu.getPixels() + curPos, kMainMenuPart2RawSize);
+	curPos += kMainMenuPart2RawSize;
+	byte *compressedPart2 = new byte[kMainMenuPart2CompressedSize];
+	alfred7.read(compressedPart2, kMainMenuPart2CompressedSize);
 	byte *decompressedPart2 = nullptr;
-	decompressedSize = rleDecompress(compressedPart2, 30288, 0, 0, &decompressedPart2, true);
+	decompressedSize = rleDecompress(compressedPart2, kMainMenuPart2CompressedSize, 0, 0, &decompressedPart2, true);
 
 	memcpy((byte *)_mainMenu.getPixels() + curPos, decompressedPart2, decompressedSize);
 	curPos += decompressedSize;
-	debug("Settings menu size loaded: %d, with last block %d", curPos, curPos + 92160);
+	debug("Settings menu size loaded: %d, with last block %d", curPos, curPos + (int)kMainMenuPart3Size);
 	delete[] compressedPart2;
 	delete[] decompressedPart2;
-	alfred7.seek(2563266, SEEK_SET);
-	alfred7.read((byte *)_mainMenu.getPixels() + curPos, 92160);
+	alfred7.seek(kMainMenuPart3Offset, SEEK_SET);
+	alfred7.read((byte *)_mainMenu.getPixels() + curPos, kMainMenuPart3Size);
 
-	readButton(alfred7, 3193376, _saveButtons, _saveGameRect);
+	readButton(alfred7, kMenuButtonsOffset, _saveButtons, _saveGameRect);
 	readButton(alfred7, alfred7.pos(), _loadButtons, _loadGameRect);
 	readButton(alfred7, alfred7.pos(), _soundsButtons, _soundsRect);
 	readButton(alfred7, alfred7.pos(), _exitToDosButtons, _exitToDosRect);
@@ -278,7 +302,7 @@ void MenuManager::loadMenu() {
 	readButton(alfred7, alfred7.pos(), _inventoryRightArrow, _invRight);
 	readButton(alfred7, alfred7.pos(), _savesUpArrows, _savesUp);
 	readButton(alfred7, alfred7.pos(), _savesDownArrows, _savesDown);
-	readButton(alfred7, 3214046, _questionMark, _questionMarkRect);
+	readButton(alfred7, kQuestionMarkOffset, _questionMark, _questionMarkRect);
 
 	_menuText = _menuTexts[0];
 	alfred7.close();
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 1a2da8ac59f..e03d6c60501 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -25,37 +25,7 @@
 
 namespace Pelrock {
 
-static const uint32 cursor_offsets[5] = {
-	0x0FDDFD,
-	0x0FDCDD,
-	0x0FDF1D,
-	0x0FE33D,
-	0x367EF0};
-
-static const uint32 kBalloonFramesOffset = 2176936;
-static const uint32 kBalloonFramesSize = 24950;
-
-static const uint32 ALFRED7_ALFRED_COMB_R = 67768;
-static const uint32 ALFRED7_ALFRED_COMB_L = 88408;
-
-static const uint32 kAlternateSettingsMenuOffset = 910097;
-static const uint32 kAlternateSettingsPaletteOffset = 1038141; // 640 * 480
-static const uint32 kSettingsPaletteOffset = 0x2884c2;         // 640 * 480
-
-const uint32 kDescriptionBaseOffset = 0x4715D;
-const uint16 kNumDescriptions = 113;
-
-static const uint32 kInventoryDescriptionsOffset = 0x4715E;
-static const uint32 kInventoryDescriptionsSize = 7868;
-static const uint32 kMenuTextOffset = 0x49203;
-static const uint32 kMenuTextSize = 230;
-static const uint32 kAlfredResponsesOffset = 0x441DC;
-static const uint32 kConversationTerminatorOffset = 0x0492EE;
-static const uint32 kAlfredResponsesSize = 12143;
-static const uint32 kCreditsOffset = 0x49F60;
-static const uint32 kCreditsSize = 2540;
-
-static const uint32 pegatina_offsets[137] = {
+static const uint32 stickerOffsets[137] = {
 	0x000000, 0x00005B, 0x0000B6, 0x000298, 0x00047A, 0x0023C8, 0x004316, 0x004376,
 	0x005119, 0x005EBC, 0x0083ED, 0x008529, 0x0092C4, 0x00A3AA, 0x00B490, 0x00B6A6,
 	0x00C05A, 0x00CA0E, 0x00D3D0, 0x00D46E, 0x00F036, 0x00FB8F, 0x00FC55, 0x0119D7,
@@ -75,51 +45,6 @@ static const uint32 pegatina_offsets[137] = {
 	0x07ECED, 0x07F52F, 0x07FD71, 0x080591, 0x080B24, 0x080B84, 0x080F39, 0x0812F5,
 	0x0816B1};
 
-static const byte pegatina_rooms[140] = {
-	0, 0, 0, 0, 0, 0, 0,                            // Sprites 0-6: Room 0
-	2, 2,                                           // Sprites 7-8: Room 2
-	3, 3, 3, 3, 3, 3, 3, 3,                         // Sprites 9-16: Room 3
-	4, 4, 4, 4, 4,                                  // Sprites 17-21: Room 4
-	5, 5,                                           // Sprites 22-23: Room 5
-	7,                                              // Sprite 24: Room 7
-	8, 8,                                           // Sprites 25-26: Room 8
-	9, 9, 9, 9, 9,                                  // Sprites 27-31: Room 9
-	12, 12,                                         // Sprites 32-33: Room 12
-	13, 13, 13,                                     // Sprites 34-36: Room 13
-	12,                                             // Sprite 37: Room 12
-	15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, // Sprites 38-49: Room 15
-	16, 16,                                         // Sprites 50-51: Room 16
-	17, 17,                                         // Sprites 52-53: Room 17
-	19, 19, 19, 19, 19,                             // Sprites 54-58: Room 19
-
-	0, 0, 0, 0, 0, 0, 0, // Sprites 59-65: Room 0
-	33, 33,              // Sprites 66-67: Room 33
-	29, 29,              // Sprites 68-69: Room 29
-	0, 0, 0,             // Sprites 70-72: Room 0
-
-	34, 35, 31, 25,         // Sprites 73-76: Various rooms
-	31,                     // Sprite 77: Room 31
-	32,                     // Sprite 78: Room 32
-	21, 25,                 // Sprites 79-80: Rooms 21, 25
-	0,                      // Sprite 81: Room 0
-	0, 0, 0, 0, 0,          // Sprites 82-86: Room 0
-	4, 4, 4, 4,             // Sprites 87-90: Room 4
-	0, 0, 0, 0,             // Sprites 91-94: Room 0
-	0, 0, 0, 0, 0, 0,       // Sprites 95-100: Room 0
-	33, 33,                 // Sprites 101-102: Room 33
-	47, 47,                 // Sprites 103-104: Room 47
-	52, 52, 52, 52, 52,     // Sprites 105-109: Room 52
-	52, 52, 52, 52, 52, 52, // Sprites 110-115: Room 52
-	41,                     // Sprite 116: Room 41
-	0,                      // Sprite 117: Room 0
-	30,                     // Sprite 118: Room 30
-	44, 44, 44, 44,         // Sprites 119-122: Room 44
-	31,                     // Sprite 123: Room 31
-	46, 46,                 // Sprites 124-125: Room 46
-	31,                     // Sprite 126: Room 31
-	51, 52, 53, 54          // Sprites 127-130: Various rooms
-};
-
 enum TextIndices {
 	ESTAN_CERRADOS,
 	HOY_NO_DISPONIBLES,
@@ -319,8 +244,9 @@ enum TextIndices {
 	LASPUERTAS_DELCIELO,
 };
 
-// Description offsets relative to kDescriptionBaseOffset
-static const uint16 description_offsets[kNumDescriptions] = {
+// Description offsets relative to base offset 0x4715D.
+// NOTE: unused dead code — kept for reference only.
+static const uint16 description_offsets[113] = {
 	0x0000, // Object 0: Historia de la Princesa Zenna y su amante insatisfecho
 	0x0058, // Object 1: Nombre: Alfred Pelrock
 	0x00C4, // Object 2: La tipica tarjeta por la que te sacan commisiones
@@ -500,28 +426,19 @@ static const ExtraImages extraScreens[] = {
 
 };
 
+// AlfredSpecialAnimOffset: POD struct (no constructors) to avoid global-constructor overhead.
+// Fields are ordered to match natural aggregate-initializer order.
+// size == 0 means "compute as numFrames * w * h" at load time.
 struct AlfredSpecialAnimOffset {
-	int numFrames = 0;
-	int w = 0;
-	int h = 0;
+	int numFrames;
+	int w;
+	int h;
 	int numBudas;
-	int loops;
 	int numAlfred;
 	uint32 offset;
-	int stride = 0;
-	uint32 size;
-	int speed = 2;
-
-	AlfredSpecialAnimOffset(int nF, int width, int height, int nBudas, int numAlfred, uint32 off, int loops, int speed = 2)
-		: numFrames(nF), w(width), h(height), numBudas(nBudas), numAlfred(numAlfred), offset(off), loops(loops), speed(speed) {
-		AlfredSpecialAnimOffset(nF, width, height, nBudas, numAlfred, off, loops, speed, width * height * nF);
-	}
-	AlfredSpecialAnimOffset(int nF, int width, int height, int nBudas, int numAlfred, uint32 off, int loops, int speed, uint32 sz)
-		: numFrames(nF), w(width), h(height), numBudas(nBudas), numAlfred(numAlfred), offset(off), loops(loops), size(sz), speed(speed) {
-		stride = w * h;
-	}
-	AlfredSpecialAnimOffset() {
-	}
+	int loops;
+	int speed;
+	uint32 size; // 0 = compute from numFrames * w * h
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index ff0343e5744..93aa740080e 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -46,6 +46,8 @@
 
 namespace Pelrock {
 
+static const uint32 kInventoryArrowsOffset = 3186048; // ALFRED.7 — inventory scroll arrows
+
 PelrockEngine *g_engine;
 
 PelrockEngine::PelrockEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engine(syst),
@@ -186,7 +188,7 @@ void PelrockEngine::loadInventoryArrows() {
 		error("Failed to open ALFRED.7 to load inventory arrows");
 		return;
 	}
-	alfred7.seek(3186048, SEEK_SET); // Offset for inventory arrows in ALFRED.7
+	alfred7.seek(kInventoryArrowsOffset, SEEK_SET); // Inventory arrows in ALFRED.7
 	_inventoryOverlayState.arrows[0] = new byte[20 * 60];
 	_inventoryOverlayState.arrows[1] = new byte[20 * 60];
 	alfred7.read(_inventoryOverlayState.arrows[0], 20 * 60);
@@ -704,7 +706,7 @@ void PelrockEngine::updateAnimations() {
 
 void PelrockEngine::presentFrame() {
 	_screen->blitFrom(_compositeBuffer);
-	paintDebugLayer();
+	// paintDebugLayer();
 	_screen->markAllDirty();
 }
 
@@ -2110,7 +2112,6 @@ void PelrockEngine::loadExtraScreenAndPresent(int screenIndex) {
 void PelrockEngine::waitForSpecialAnimation() {
 	while (!g_engine->shouldQuit() && !_res->_isSpecialAnimFinished) {
 		_events->pollEvent();
-		debug("Waiting for special animation to finish, current frame %d", _res->_currentSpecialAnim->curFrame);
 		renderScene(OVERLAY_NONE);
 		_screen->update();
 		g_system->delayMillis(10);
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 894a1ffec2f..ec51bd361fb 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -28,6 +28,54 @@
 
 namespace Pelrock {
 
+// ALFRED.7 — cursor sprites
+static const uint32 cursor_offsets[5] = {0x0FDDFD, 0x0FDCDD, 0x0FDF1D, 0x0FE33D, 0x367EF0};
+
+// ALFRED.3 / ALFRED.7 — Alfred combing animations
+static const uint32 ALFRED7_ALFRED_COMB_R = 67768;
+static const uint32 ALFRED7_ALFRED_COMB_L = 88408;
+
+// ALFRED.4 — inventory icon pixel data
+static const uint32 kInventoryIconsOffset   = 42366;
+static const uint32 kInventoryIconsTailSize = 423656; // bytes trailing the icon block
+
+// ALFRED.7 — action-popup balloon frames
+static const uint32 kBalloonFramesOffset = 2176936;
+static const uint32 kBalloonFramesSize   = 24950; // compressed size
+
+// JUEGO.EXE — Alfred in-game text responses
+static const uint32 kAlfredResponsesOffset        = 0x441DC;
+static const uint32 kAlfredResponsesSize          = 12143;
+static const uint32 kConversationTerminatorOffset = 0x0492EE;
+
+// JUEGO.EXE — credits
+static const uint32 kCreditsOffset = 0x49F60;
+static const uint32 kCreditsSize   = 2540;
+
+// JUEGO.EXE — computer-screen text
+static const uint32 kComputerTextOffset = 0x0004901A;
+static const uint32 kComputerTextSize   = 490;
+
+// Alfred special animation data (file index given per entry in alfredSpecialAnims[])
+static const uint32 kAlfredAnimReadBookOffset       = 559685;  // 0  - READ BOOK
+static const uint32 kAlfredAnimReadRecipeOffset     = 578943;  // 1  - READ RECIPE
+static const uint32 kAlfredAnimElectricShock1Offset = 37000;   // 2  - ELECTRIC SHOCK 1
+static const uint32 kAlfredAnimElectricShock3Offset = 53106;   // 3  - ELECTRIC SHOCK 3
+static const uint32 kAlfredAnimThrowOffset          = 20724;   // 4  - Throw
+static const uint32 kAlfredAnimThrowSize            = 62480;   // 4  - Throw explicit size
+static const uint32 kAlfredAnimCrocodileOffset      = 1556540; // 5  - Crocodile
+static const uint32 kAlfredAnimManholeOffset        = 1583702; // 6  - Exit manhole
+static const uint32 kAlfredAnimClimbDownOffset      = 1761234; // 7  - Climbs down
+static const uint32 kAlfredAnimClimbUpOffset        = 1766378; // 8  - Climbs up
+static const uint32 kAlfredAnimExitTunnelOffset     = 1770196; // 9  - Exits tunnel
+static const uint32 kAlfredAnimWorkersOffset        = 1600956; // 10 - With workers
+static const uint32 kAlfredAnimMunheco1Offset       = 2060916; // 11 - Munheco 1
+static const uint32 kAlfredAnimMunheco2Offset       = 2115632; // 12 - Munheco 2
+static const uint32 kAlfredAnimMunheco3Offset       = 1526432; // 13 - Munheco 3
+static const uint32 kAlfredAnimDescamisaOffset      = 2972568; // 14 - Descamisa
+static const uint32 kAlfredAnimSecretPassageOffset  = 1749464; // 15 - Secret passage
+static const uint32 kAlfredAnimInBedOffset          = 3038454; // 16 - Alfred in bed
+
 ResourceManager::ResourceManager(/* args */) {
 	_inventoryIcons = new InventoryObject[69];
 	for (int i = 0; i < 4; i++) {
@@ -36,23 +84,23 @@ ResourceManager::ResourceManager(/* args */) {
 }
 
 const AlfredSpecialAnimOffset ResourceManager::alfredSpecialAnims[] = {
-	{10, 51, 102, 1, 7, 559685, 1},    // 0 - READ BOOK
-	{10, 51, 102, 1, 7, 578943, 1},      // 1 - READ RECIPE
-	{3, 45, 87, 0, 7, 37000, 1},         // 2 -  ELECTRIC SHOCK 1
-	{2, 82, 58, 0, 7, 53106, 20, 1},        // 3 - ELECTRIC SHOCK 3
-	{3, 71, 110, 1, 2, 20724, 1, 1, 62480}, // 4 - Throw
-	{14, 171, 107, 1, 7, 1556540, 1},    // 5 - crocodile
-	{12, 113, 103, 1, 7, 1583702, 1},    // 6 - exit through manhole
-	{11, 33, 72, 1, 7, 1761234, 1},      // 7 - alfred climbs down
-	{9, 33, 72, 1, 7, 1766378, 1},       // 8 - alfred climbs up
-	{16, 158, 115, 0, 7, 1770196, 1},    // 9 - alfred exits tunnel
-	{7, 208, 102, 0, 7, 1600956, 1},     // 10 - alfred with workers
-	{23, 116, 124, 1, 7, 2060916, 1},    // 11 - Munheco 1
-	{18, 177, 124, 1, 7, 2115632, 1},    // 12 - Munheco 2
-	{11, 98, 138, 1, 7, 1526432, 1},     // 13 - Munheco 3
-	{4, 51, 102, 1, 7, 2972568, 1},      // 14 - descamisa
-	{13, 95, 99, 1, 7, 1749464, 1},      // 15 - alfred enters secret passage
-	{14, 71, 66, 1, 7, 3038454, 1, 2}, // 16- Alfred in bed
+	{10,  51, 102, 1, 7, kAlfredAnimReadBookOffset,       1, 2, 0},              // 0  - READ BOOK
+	{10,  51, 102, 1, 7, kAlfredAnimReadRecipeOffset,     1, 2, 0},              // 1  - READ RECIPE
+	{ 3,  45,  87, 0, 7, kAlfredAnimElectricShock1Offset, 1, 2, 0},              // 2  - ELECTRIC SHOCK 1
+	{ 2,  82,  58, 0, 7, kAlfredAnimElectricShock3Offset, 20, 1, 0},             // 3  - ELECTRIC SHOCK 3
+	{ 3,  71, 110, 1, 2, kAlfredAnimThrowOffset,          1, 1, kAlfredAnimThrowSize}, // 4  - Throw
+	{14, 171, 107, 1, 7, kAlfredAnimCrocodileOffset,      1, 2, 0},              // 5  - crocodile
+	{12, 113, 103, 1, 7, kAlfredAnimManholeOffset,        1, 2, 0},              // 6  - exit through manhole
+	{11,  33,  72, 1, 7, kAlfredAnimClimbDownOffset,      1, 2, 0},              // 7  - alfred climbs down
+	{ 9,  33,  72, 1, 7, kAlfredAnimClimbUpOffset,        1, 2, 0},              // 8  - alfred climbs up
+	{16, 158, 115, 0, 7, kAlfredAnimExitTunnelOffset,     1, 2, 0},              // 9  - alfred exits tunnel
+	{ 7, 208, 102, 0, 7, kAlfredAnimWorkersOffset,        1, 2, 0},              // 10 - alfred with workers
+	{23, 116, 124, 1, 7, kAlfredAnimMunheco1Offset,       1, 2, 0},              // 11 - Munheco 1
+	{18, 177, 124, 1, 7, kAlfredAnimMunheco2Offset,       1, 2, 0},              // 12 - Munheco 2
+	{11,  98, 138, 1, 7, kAlfredAnimMunheco3Offset,       1, 2, 0},              // 13 - Munheco 3
+	{ 4,  51, 102, 1, 7, kAlfredAnimDescamisaOffset,      1, 2, 0},              // 14 - descamisa
+	{13,  95,  99, 1, 7, kAlfredAnimSecretPassageOffset,  1, 2, 0},              // 15 - alfred enters secret passage
+	{14,  71,  66, 1, 7, kAlfredAnimInBedOffset,          1, 2, 0},              // 16 - Alfred in bed
 };
 
 ResourceManager::~ResourceManager() {
@@ -342,9 +390,9 @@ void ResourceManager::loadInventoryItems() {
 	if (!alfred4File.open("ALFRED.4")) {
 		error("Couldnt find file ALFRED.4");
 	}
-	uint32 iconsSize = alfred4File.size() - 423656;
+	uint32 iconsSize = alfred4File.size() - kInventoryIconsTailSize;
 	byte *iconData = new byte[iconsSize];
-	alfred4File.seek(42366, SEEK_SET);
+	alfred4File.seek(kInventoryIconsOffset, SEEK_SET);
 	alfred4File.read(iconData, iconsSize);
 
 	for (int i = 0; i < 69; i++) {
@@ -404,9 +452,9 @@ Common::Array<Common::StringArray> ResourceManager::loadComputerText() {
 	if (!exe.open("JUEGO.EXE")) {
 		error("Couldnt find file JUEGO.EXE");
 	}
-	int bufSize = 490;
+	int bufSize = kComputerTextSize;
 	byte *computerTextBuf = new byte[bufSize];
-	exe.seek(0x0004901A, SEEK_SET);
+	exe.seek(kComputerTextOffset, SEEK_SET);
 	exe.read(computerTextBuf, bufSize);
 	Common::Array<Common::StringArray> computerTexts = processTextData(computerTextBuf, bufSize);
 
@@ -508,14 +556,13 @@ Pelrock::Sticker ResourceManager::getSticker(int stickerIndex) {
 		error("Couldnt find file ALFRED.6");
 	}
 
-	uint32 stickerOffset = pegatina_offsets[stickerIndex];
+	uint32 stickerOffset = stickerOffsets[stickerIndex];
 	alfred6File.seek(stickerOffset, SEEK_SET);
 	Sticker sticker;
 	sticker.x = alfred6File.readUint16LE();
 	sticker.y = alfred6File.readUint16LE();
 	sticker.w = alfred6File.readByte();
 	sticker.h = alfred6File.readByte();
-	sticker.roomNumber = pegatina_rooms[stickerIndex];
 	sticker.stickerIndex = stickerIndex;
 	sticker.stickerData = new byte[sticker.w * sticker.h];
 	alfred6File.read(sticker.stickerData, sticker.w * sticker.h);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index cfd51083793..e08ad61d9f8 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -27,6 +27,8 @@
 
 namespace Pelrock {
 
+static const uint32 kPaletteRemapOffset = 0x4C77C; // JUEGO.EXE — water-effect palette remap table
+
 RoomManager::RoomManager() {
 	_pixelsShadows = new byte[640 * 400];
 	loadWaterPaletteRemap();
@@ -46,7 +48,7 @@ void RoomManager::loadWaterPaletteRemap() {
 	if(!exe.open("JUEGO.EXE")) {
 		error("Couldnt find file JUEGO.EXE");
 	}
-	exe.seek(0x4C77C, SEEK_SET);
+	exe.seek(kPaletteRemapOffset, SEEK_SET);
 	exe.read(_paletteRemaps[4], 256);
 	exe.close();
 }
diff --git a/engines/pelrock/spellbook.cpp b/engines/pelrock/spellbook.cpp
index ec8e1d00852..ac802b8cc93 100644
--- a/engines/pelrock/spellbook.cpp
+++ b/engines/pelrock/spellbook.cpp
@@ -29,6 +29,11 @@
 
 namespace Pelrock {
 
+static const uint32 kSpellbookImgOffset     = 1268719; // ALFRED.7 — spellbook sprite sheet start
+static const uint32 kSpellbookImgDataOffset = 1268723; // ALFRED.7 — compressed sprite data
+static const uint32 kSpellbookTextOffset    = 288285;  // JUEGO.EXE — spellbook page text
+static const uint32 kSpellbookTextSize      = 2861;
+
 SpellBook::SpellBook(PelrockEventManager *eventMan, ResourceManager *res)
 	: _palette(nullptr),
 	  _events(eventMan),
@@ -80,28 +85,28 @@ void SpellBook::selectPage(int page) {
 		return;
 	}
 
-	alfred7.seek(1268719, SEEK_SET);
+	alfred7.seek(kSpellbookImgOffset, SEEK_SET);
 	int w = 119;
 	int h = 99;
 	int nFrames = 13;
 	byte *compressedData = nullptr;
 	byte *spriteData = nullptr;
 	size_t outSize = 0;
-	readUntilBuda(&alfred7, 1268723, compressedData, outSize);
+	readUntilBuda(&alfred7, kSpellbookImgDataOffset, compressedData, outSize);
 	rleDecompress(compressedData, outSize, 0, w * h * nFrames, &spriteData, false);
 	_spell->image = new byte[w * h];
 	extractSingleFrame(spriteData, _spell->image, page, w, h);
 
-	juegoFile.seek(0x0004661D, SEEK_SET);
-	byte *textData = new byte[2861];
-	juegoFile.read(textData, 2861);
+	juegoFile.seek(kSpellbookTextOffset, SEEK_SET);
+	byte *textData = new byte[kSpellbookTextSize];
+	juegoFile.read(textData, kSpellbookTextSize);
 
-	for (int i = 0; i < 2861; ++i) {
+	for (int i = 0; i < (int)kSpellbookTextSize; ++i) {
 		if (textData[i] == 0x0D)
 			textData[i] = 23;
 	}
 
-	Common::Array<Common::StringArray> spells = _res->processTextData(textData, 2861, true);
+	Common::Array<Common::StringArray> spells = _res->processTextData(textData, kSpellbookTextSize, true);
 	_spell->text = spells[page];
 	delete[] compressedData;
 	delete[] spriteData;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 04248301f3d..e227d1a4ae9 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -420,7 +420,6 @@ struct PaletteAnimFade {
 };
 
 struct Sticker {
-	int roomNumber;
 	int stickerIndex;
 	uint16 x;
 	uint16 y;


Commit: 0171a5e989e009c76a58b8070260543432c831ba
    https://github.com/scummvm/scummvm/commit/0171a5e989e009c76a58b8070260543432c831ba
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:29+02:00

Commit Message:
PELROCK: Prevent sticker flicker on room change

Changed paths:
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 93aa740080e..9764a649f3c 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1027,6 +1027,11 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 					_alfredState.x = exit->targetX;
 					_alfredState.y = exit->targetY;
 					setScreenAndPrepare(exit->targetRoom, exit->dir);
+					// setScreen() resets the composite buffer to the bare background;
+					// place first-pass stickers now so they are present in the very
+					// first presentFrame() for the new room (avoids a one-frame flash
+					// without stickers).
+					placeStickersFirstPass();
 				}
 			}
 		} else {


Commit: 58677e4489e1329214aad1d51794932ebbda9045
    https://github.com/scummvm/scummvm/commit/58677e4489e1329214aad1d51794932ebbda9045
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:29+02:00

Commit Message:
PELROCK: Closes main menu after loading/saving

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h
    engines/pelrock/saveload.cpp


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 01e478b3d3b..33209820fdf 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -89,7 +89,7 @@ MenuButton MenuManager::isButtonClicked(int x, int y) {
 	return NO_BUTTON; // Default fallback
 }
 
-void MenuManager::checkMouseClick(int x, int y) {
+bool MenuManager::checkMouseClick(int x, int y) {
 
 	bool selectedItem = false;
 	for (int i = 0; i < 4; i++) {
@@ -98,7 +98,7 @@ void MenuManager::checkMouseClick(int x, int y) {
 
 		if (itemRect.contains(x, y)) {
 			selectedItem = selectInventoryItem(i);
-			return;
+			return false;
 		}
 	}
 	if (!selectedItem) {
@@ -122,10 +122,10 @@ void MenuManager::checkMouseClick(int x, int y) {
 			_curInventoryPage++;
 		break;
 	case SAVE_GAME_BUTTON:
-		g_engine->saveGameDialog();
+		return g_engine->saveGameDialog();
 		break;
 	case LOAD_GAME_BUTTON:
-		g_engine->loadGameDialog();
+		return g_engine->loadGameDialog();
 		break;
 	case EXIT_MENU_BUTTON:
 		g_engine->quitGame();
@@ -133,6 +133,7 @@ void MenuManager::checkMouseClick(int x, int y) {
 	default:
 		break;
 	}
+	return false;
 }
 
 void MenuManager::showCredits() {
@@ -191,15 +192,19 @@ void MenuManager::menuLoop() {
 	g_system->getPaletteManager()->setPalette(_mainMenuPalette, 0, 256);
 	g_engine->changeCursor(DEFAULT);
 	_menuText = _menuTexts[0];
-	while (!g_engine->shouldQuit() && !_events->_rightMouseClicked) {
+	while (!g_engine->shouldQuit()) {
 
 		_events->pollEvent();
 
 		if (_events->_leftMouseClicked) {
-			checkMouseClick(_events->_mouseX, _events->_mouseY);
+			if(checkMouseClick(_events->_mouseX, _events->_mouseY)) {
+				break;
+			}
 			_events->_leftMouseClicked = false;
 		}
-
+		if(_events->_rightMouseClicked) {
+			break;
+		}
 		drawScreen();
 		_screen->markAllDirty();
 		_screen->update();
@@ -207,6 +212,7 @@ void MenuManager::menuLoop() {
 	}
 	g_engine->_graphics->clearScreen();
 	_events->_rightMouseClicked = false;
+	_events->_leftMouseClicked = false;
 	g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
 	cleanUp();
 }
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 36bfe819821..76e9eea7ece 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -214,7 +214,7 @@ public:
 	byte _mainMenuPalette[768] = {0};
 
 private:
-	void checkMouseClick(int x, int y);
+	bool checkMouseClick(int x, int y);
 	void showCredits();
 	bool selectInventoryItem(int i);
 	void loadMenuTexts();
diff --git a/engines/pelrock/saveload.cpp b/engines/pelrock/saveload.cpp
index 07ee9409bba..ac62e159528 100644
--- a/engines/pelrock/saveload.cpp
+++ b/engines/pelrock/saveload.cpp
@@ -384,6 +384,7 @@ SaveGameData *PelrockEngine::createSaveGameData() const {
 	saveGame->alfredY = _alfredState.y;
 	saveGame->alfredDir = _alfredState.direction;
 	saveGame->gameState = g_engine->_state;
+	_state->stateGame = GAME;
 	return saveGame;
 }
 


Commit: 3fb39ab5c7e2caf6b9b09d90836413539f5d8b84
    https://github.com/scummvm/scummvm/commit/3fb39ab5c7e2caf6b9b09d90836413539f5d8b84
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:29+02:00

Commit Message:
PELROCK: Implement TTL for dialogue lines

Changed paths:
    engines/pelrock/dialog.cpp


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 9b0bd49770d..dbadaf4d149 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -128,17 +128,14 @@ void DialogManager::displayChoices(Common::Array<ChoiceOption> *choices, Graphic
 
 		if (bbox.contains(_events->_mouseX, _events->_mouseY)) {
 			choiceColor = 15;
-		} else if (leftArrowBox.contains(_events->_mouseX, _events->_mouseY)) {
+		} else if (leftArrowBox.contains(_events->_mouseX, _events->_mouseY)) { // scroll left
 			if (choice.charOffset > 0) {
 				choice.charOffset--;
 				choices->remove_at(i);
 				choices->insert_at(i, choice);
 			}
 			lArrowColor = 15;
-		} else if (rightArrowBox.contains(_events->_mouseX, _events->_mouseY)) {
-			if (i == 0) {
-				debug("First choice charOffset %d, text length %d", choice.charOffset, choice.text.size());
-			}
+		} else if (rightArrowBox.contains(_events->_mouseX, _events->_mouseY)) { // scroll right
 			if (choice.charOffset + 76 < choice.text.size()) {
 				choice.charOffset = choice.charOffset + 1;
 				choices->remove_at(i);
@@ -158,6 +155,11 @@ void DialogManager::displayChoices(Common::Array<ChoiceOption> *choices, Graphic
 	}
 }
 
+/**
+ * In order to display multi-line text whilst still centered we create a surface with
+ * the maxWidth + height then print the text onto that surface with the appropriate alignment,
+ * then blit that surface to the screen.
+ */
 Graphics::Surface DialogManager::getDialogueSurface(Common::Array<Common::String> dialogueLines, byte speakerId, Graphics::TextAlign alignment) {
 
 	int maxWidth = 0;
@@ -196,7 +198,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		}
 		// Offset X position for Alfred to avoid overlapping with his sprite
 		xBasePos = g_engine->_alfredState.x;
-		// Original game: uses the scaled character height (varies with perspective),
+		// Original game: uses the scaled character height
 		// not the fixed kAlfredFrameHeight. _alfredState.h is updated by drawAlfred().
 		yBasePos = g_engine->_alfredState.y - g_engine->_alfredState.h; // Above scaled sprite top
 	} else {
@@ -210,10 +212,22 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 	displayDialogue(dialogueLines, speakerId, xBasePos, yBasePos); // Default position
 }
 
+uint32 calcPageTtlMs(Common::Array<Common::String> dialogueLine) {
+    uint32 charCount = 0;
+    for (uint i = 0; i < dialogueLine.size(); i++) {
+        charCount += dialogueLine[i].size();
+    }
+    return charCount * kTickMs;
+}
+
 /**
- * Display dialogue text and wait for click to advance
- * @param text The text to display
- * @param speakerId The speaker ID which is used as color
+ * Display dialogue text and wait for click or TTL to advance.
+ *
+ * TTL ->  Each page auto-advances after (char_count * kTickMs) milliseconds.
+ * Where char_count = total visible characters on the page.
+ *
+ * Skip -> All dialogs are click-skippable unless _disableClickToAdvance flag is enabled; those
+ * dialogs still auto-advance via TTL.
  */
 void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>> dialogueLines, byte speakerId, int xBasePos, int yBasePos) {
 	if (dialogueLines.empty()) {
@@ -226,7 +240,11 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 	int curPage = 0;
 	bool fromIntro = g_engine->_state->getFlag(FLAG_FROM_INTRO) == true;
 	debug("Displaying dialog, from intro = %d", fromIntro);
-	// Render loop - display text and wait for click
+
+	uint32 pageTtlMs = calcPageTtlMs(dialogueLines[curPage]);
+	uint32 pageStartMs = g_system->getMillis();
+
+	// Render loop - display text and wait for click or TTL
 	while (!g_engine->shouldQuit()) {
 		_events->pollEvent();
 
@@ -262,17 +280,24 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		_screen->markAllDirty();
 		_screen->update();
 
-		if (_events->_leftMouseClicked) {
+		// Check if TTL expired for this page (always applies, even for _disableClickToAdvance)
+		bool ttlExpired = !fromIntro && (pageTtlMs > 0) && (g_system->getMillis() - pageStartMs >= pageTtlMs);
+
+		// Click-to-advance (disabled for special intro sequences)
+		bool clickAdvance = _events->_leftMouseClicked && !_disableClickToAdvance;
+		if (_events->_leftMouseClicked)
 			_events->_leftMouseClicked = false;
-			if (!_disableClickToAdvance) {
-				// debug("Dialogue click to advance, current page: %d, totalPages: %d", curPage, (int)dialogueLines.size());
+
+		if (clickAdvance || ttlExpired) {
 				if (curPage < (int)dialogueLines.size() - 1) {
 					curPage++;
+				pageStartMs = g_system->getMillis();
+				pageTtlMs = calcPageTtlMs(dialogueLines[curPage]);
 				} else {
 					_dismissDialog = true;
 				}
 			}
-		}
+
 		if (_dismissDialog) {
 			_dismissDialog = false;
 			break; // Exit dialogue if dismissed programmatically


Commit: 0e5168ea3a401a870a340999384d641e139113d7
    https://github.com/scummvm/scummvm/commit/0e5168ea3a401a870a340999384d641e139113d7
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:30+02:00

Commit Message:
PELROCK: Move pertinent code into graphics

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/graphics.cpp
    engines/pelrock/graphics.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index f88b9cdbe1c..2882dd763d7 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1965,10 +1965,10 @@ void PelrockEngine::teleportToPrincess() {
 		_sound->playSound(_room->_roomSfx[3], 0);
 
 		// Draw 19 semi-transparent remapped lines
-		copyBackgroundToBuffer();
-		placeStickersFirstPass();
+		_graphics->copyBackgroundToBuffer();
+		_graphics->placeStickersFirstPass();
 		updateAnimations();
-		presentFrame();
+		_graphics->presentFrame();
 		_screen->update();
 
 		for (int i = 0; i < 19; i++) {
@@ -1982,7 +1982,7 @@ void PelrockEngine::teleportToPrincess() {
 		}
 
 		updateAnimations();
-		presentFrame();
+		_graphics->presentFrame();
 		_screen->update();
 
 		_events->pollEvent();
@@ -1991,10 +1991,10 @@ void PelrockEngine::teleportToPrincess() {
 
 		// Restore clean frame with sticker (lines gone)
 		_room->addSticker(stickers[phase]);
-		copyBackgroundToBuffer();
-		placeStickersFirstPass();
+		_graphics->copyBackgroundToBuffer();
+		_graphics->placeStickersFirstPass();
 		updateAnimations();
-		presentFrame();
+		_graphics->presentFrame();
 		_screen->update();
 		phase++;
 	}
@@ -2011,10 +2011,10 @@ void PelrockEngine::teleportToPrincess() {
 	}
 
 	_room->addSticker(115);
-	copyBackgroundToBuffer();
-	placeStickersFirstPass();
+	_graphics->copyBackgroundToBuffer();
+	_graphics->placeStickersFirstPass();
 	updateAnimations();
-	presentFrame();
+	_graphics->presentFrame();
 	_screen->update();
 
 	_dialog->say(_res->_ingameTexts[MAREDEDEU]);
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index dbadaf4d149..8c7aa0911f5 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -140,7 +140,6 @@ void DialogManager::displayChoices(Common::Array<ChoiceOption> *choices, Graphic
 				choice.charOffset = choice.charOffset + 1;
 				choices->remove_at(i);
 				choices->insert_at(i, choice);
-				debug("Right arrow clicked, new charOffset %d, text length %d", choice.charOffset, choice.text.size());
 			}
 			rArrowColor = 15;
 		}
@@ -289,21 +288,21 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 			_events->_leftMouseClicked = false;
 
 		if (clickAdvance || ttlExpired) {
-				if (curPage < (int)dialogueLines.size() - 1) {
-					curPage++;
+			if (curPage < (int)dialogueLines.size() - 1) {
+				curPage++;
 				pageStartMs = g_system->getMillis();
 				pageTtlMs = calcPageTtlMs(dialogueLines[curPage]);
-				} else {
-					_dismissDialog = true;
-				}
+			} else {
+				_dismissDialog = true;
 			}
+		}
 
 		if (_dismissDialog) {
 			_dismissDialog = false;
 			break; // Exit dialogue if dismissed programmatically
 		}
 
-		if(fromIntro && g_engine->_res->_isSpecialAnimFinished) {
+		if (fromIntro && g_engine->_res->_isSpecialAnimFinished) {
 			debug("Dismissing due to speciawl anim ending!");
 			break;
 		}
diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index 062de9031bb..279fe2f2622 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -21,11 +21,13 @@
 
 #include "common/scummsys.h"
 #include "engines/util.h"
+#include "graphics/cursorman.h"
 #include "graphics/paletteman.h"
 
 #include "graphics.h"
 #include "pelrock/graphics.h"
 #include "pelrock/pelrock.h"
+#include "pelrock/util.h"
 
 namespace Pelrock {
 
@@ -224,4 +226,329 @@ void GraphicsManager::drawColoredTexts(Graphics::ManagedSurface &buf, const Comm
 	}
 }
 
+void GraphicsManager::copyBackgroundToBuffer() {
+	g_engine->_compositeBuffer.blitFrom(g_engine->_currentBackground);
+}
+
+void GraphicsManager::presentFrame() {
+	g_engine->_screen->blitFrom(g_engine->_compositeBuffer);
+	// paintDebugLayer();
+	g_engine->_screen->markAllDirty();
+}
+
+void GraphicsManager::updatePaletteAnimations() {
+	if (g_engine->_room->_currentPaletteAnim != nullptr) {
+		if (g_engine->_room->_currentPaletteAnim->paletteMode == 1) {
+			animateFadePalette(g_engine->_room->_currentPaletteAnim);
+		} else {
+			animateRotatePalette(g_engine->_room->_currentPaletteAnim);
+		}
+	}
+}
+
+void GraphicsManager::animateFadePalette(PaletteAnim *anim) {
+	// FADE palette animation - cycles a single palette entry between min/max RGB values
+	// Data layout (after loading from EXE):
+	//   data[0] = current R
+	//   data[1] = current G
+	//   data[2] = current B
+	//   data[3] = min R
+	//   data[4] = min G
+	//   data[5] = min B
+	//   data[6] = max R
+	//   data[7] = max G
+	//   data[8] = max B
+	//   data[9] = flags byte:
+	//             bits 0-1: R increment
+	//             bits 2-3: G increment
+	//             bits 4-5: B increment
+	//             bit 6: direction (0=decreasing toward min, 1=increasing toward max)
+
+	byte flags = anim->data[9];
+	// Increments are scaled by 4 (<<2) to match the shifted RGB values
+	byte rInc = (flags & 0x03) << 2;
+	byte gInc = ((flags >> 2) & 0x03) << 2;
+	byte bInc = ((flags >> 4) & 0x03) << 2;
+	bool increasing = (flags & 0x40) != 0;
+
+	if (increasing) {
+		// Increasing toward max values
+		anim->data[0] += rInc;
+		anim->data[1] += gInc;
+		anim->data[2] += bInc;
+		// Check if R reached max, then reverse direction
+		if (anim->data[0] >= anim->data[6]) {
+			anim->data[9] &= ~0x40; // Clear direction bit
+		}
+	} else {
+		// Decreasing toward min values
+		anim->data[0] -= rInc;
+		anim->data[1] -= gInc;
+		anim->data[2] -= bInc;
+		// Check if R reached min, then reverse direction
+		if (anim->data[0] <= anim->data[3]) {
+			anim->data[9] |= 0x40; // Set direction bit
+		}
+	}
+
+	g_engine->_room->_roomPalette[anim->startIndex * 3] = anim->data[0];
+	g_engine->_room->_roomPalette[anim->startIndex * 3 + 1] = anim->data[1];
+	g_engine->_room->_roomPalette[anim->startIndex * 3 + 2] = anim->data[2];
+	g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
+}
+
+void GraphicsManager::animateRotatePalette(PaletteAnim *anim) {
+	if (anim->curFrame >= anim->data[1]) {
+		anim->curFrame = 0;
+		int colors = anim->paletteMode;
+		byte *paletteValues = new byte[colors * 3];
+		for (int i = 0; i < colors; i++) {
+			paletteValues[i * 3] = g_engine->_room->_roomPalette[(anim->startIndex + i) * 3];
+			paletteValues[i * 3 + 1] = g_engine->_room->_roomPalette[(anim->startIndex + i) * 3 + 1];
+			paletteValues[i * 3 + 2] = g_engine->_room->_roomPalette[(anim->startIndex + i) * 3 + 2];
+		}
+		for (int i = 0; i < colors; i++) {
+			int srcIndex = (i + 1) % colors;
+			g_engine->_room->_roomPalette[(anim->startIndex + i) * 3] = paletteValues[srcIndex * 3];
+			g_engine->_room->_roomPalette[(anim->startIndex + i) * 3 + 1] = paletteValues[srcIndex * 3 + 1];
+			g_engine->_room->_roomPalette[(anim->startIndex + i) * 3 + 2] = paletteValues[srcIndex * 3 + 2];
+		}
+		delete[] paletteValues;
+
+		g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
+
+	} else {
+		anim->curFrame++;
+	}
+}
+
+void GraphicsManager::placeStickersFirstPass() {
+	// also place temporary stickers
+	for (uint i = 0; i < g_engine->_room->_roomStickers.size(); i++) {
+		Sticker sticker = g_engine->_room->_roomStickers[i];
+		placeSticker(sticker);
+	}
+}
+
+void GraphicsManager::placeStickersSecondPass() {
+	// Some stickers need to be placed AFTER sprites, hardcoded in the original
+	if (g_engine->_room->_currentRoomNumber == 3) {
+		for (uint i = 0; i < g_engine->_state->stickersPerRoom[3].size(); i++) {
+			if (g_engine->_state->stickersPerRoom[3][i].stickerIndex == 14) {
+				placeSticker(g_engine->_state->stickersPerRoom[3][i]);
+				break;
+			}
+		}
+	}
+}
+
+void GraphicsManager::placeSticker(Sticker sticker) {
+	// Wrap sticker data as a surface and blit (no transparency - all pixels copied)
+	Graphics::Surface stickerSurf;
+	stickerSurf.init(sticker.w, sticker.h, sticker.w, sticker.stickerData, Graphics::PixelFormat::createFormatCLUT8());
+	// Clip to screen bounds
+	Common::Rect destRect(sticker.x, sticker.y, sticker.x + sticker.w, sticker.y + sticker.h);
+	Common::Rect screenRect(0, 0, 640, 400);
+	destRect.clip(screenRect);
+	if (destRect.isEmpty())
+		return;
+	Common::Rect srcRect(destRect.left - sticker.x, destRect.top - sticker.y,
+						 destRect.right - sticker.x, destRect.bottom - sticker.y);
+	g_engine->_compositeBuffer.blitFrom(stickerSurf, srcRect, Common::Point(destRect.left, destRect.top));
+}
+
+void GraphicsManager::reflectionEffect(byte *buf, int x, int y, int width, int height) {
+	// Water reflection - draws mirrored sprite on water pixels
+	// Only sprite pixels 0-15 are reflected (checked via pixel & 0xF0 == 0)
+	for (int row = 0; row < height; row++) {
+		int reflectY = y + row;
+
+		if (reflectY >= 400)
+			break; // Screen boundary
+
+		for (int col = 0; col < width; col++) {
+			byte pixel = buf[(height - 1 - row) * width + col]; // Read from bottom up for mirror
+			// Only reflect pixels 0-15 (high nibble must be 0)
+			if (pixel != 255 && (pixel & 0xF0) == 0) {
+				int px = x + col;
+				if (px >= 0 && px < 640) {
+					byte bgPixel = (byte)g_engine->_compositeBuffer.getPixel(px, reflectY);
+					if (bgPixel >= 223 && bgPixel < 228) { // Is water (0xDF-0xE3)
+						g_engine->_compositeBuffer.setPixel(px, reflectY, g_engine->_room->_paletteRemaps[4][pixel]);
+					}
+				}
+			}
+		}
+	}
+}
+
+void GraphicsManager::calculateScalingMasks() {
+	for (int scaleFactor = 0; scaleFactor < kAlfredFrameWidth; scaleFactor++) {
+		float step = kAlfredFrameWidth / (scaleFactor + 1.0f);
+		Common::Array<int> row;
+		float index = 0.0f;
+		int sourcePixel = 0;
+
+		while (index < kAlfredFrameWidth) {
+			row.push_back(sourcePixel);
+			index += step;
+			sourcePixel += 1;
+			if (sourcePixel >= kAlfredFrameWidth) {
+				sourcePixel = kAlfredFrameWidth - 1;
+			}
+		}
+
+		// Pad to exactly kAlfredFrameWidth entries
+		while (row.size() < kAlfredFrameWidth) {
+			row.push_back(row.empty() ? 0 : row[row.size() - 1]);
+		}
+
+		_widthScalingTable.push_back(row);
+	}
+
+	for (int scaleFactor = 0; scaleFactor < kAlfredFrameHeight; scaleFactor++) {
+		float step = kAlfredFrameHeight / (scaleFactor + 1.0f);
+		Common::Array<int> row;
+		row.resize(kAlfredFrameHeight, 0);
+		float position = step;
+		int counter = 1;
+		while (position < kAlfredFrameHeight) {
+			int idx = static_cast<int>(round(position));
+			if (idx < kAlfredFrameHeight) {
+				row[idx] = counter;
+				counter++;
+			}
+			position += step;
+		}
+		_heightScalingTable.push_back(row);
+	}
+}
+
+ScaleCalculation GraphicsManager::calculateScaling(int yPos, ScalingParams scalingParams) {
+	int scaleY = 0;
+	int scaleX = 0;
+	if (scalingParams.scaleMode == 0xFF) {
+		// Maximum scaling - character is very small (used for bird's eye view maps)
+		scaleY = 0x5e; // 94
+		scaleX = 0x2f; // 47
+	} else if (scalingParams.scaleMode == 0xFE) {
+		// No scaling - full size character
+		scaleY = 0;
+		scaleX = 0;
+	} else if (scalingParams.scaleMode == 0) {
+		// Dynamic scaling based on Y position
+		if (scalingParams.yThreshold < yPos) {
+			// Below threshold - no scaling
+			scaleY = 0;
+			scaleX = 0;
+		} else {
+			if (scalingParams.scaleDivisor != 0) {
+				scaleY = (scalingParams.yThreshold - yPos) / scalingParams.scaleDivisor;
+				scaleX = scaleY / 2;
+			} else {
+				scaleY = 0;
+				scaleX = 0;
+			}
+		}
+	} else {
+		scaleY = 0;
+		scaleX = 0;
+	}
+
+	// Original game formula: actual dimensions = base - scale amount
+	int finalHeight = kAlfredFrameHeight - scaleY;
+	if (finalHeight < 1)
+		finalHeight = 1;
+
+	int finalWidth = kAlfredFrameWidth - scaleX;
+	if (finalWidth < 1)
+		finalWidth = 1;
+
+	ScaleCalculation scaleCalc;
+	scaleCalc.scaledHeight = finalHeight;
+	scaleCalc.scaledWidth = finalWidth;
+	scaleCalc.scaleY = scaleY;
+	scaleCalc.scaleX = scaleX;
+	return scaleCalc;
+}
+
+byte *GraphicsManager::scale(int scaleY, int finalWidth, int finalHeight, byte *buf) {
+	// The scaling table is indexed by how many scanlines to skip (scaleY), not by final height
+	int scaleIndex = scaleY;
+	if (scaleIndex >= (int)_heightScalingTable.size()) {
+		scaleIndex = _heightScalingTable.size() - 1;
+	}
+	if (scaleIndex < 0) {
+		scaleIndex = 0;
+	}
+	int linesToSkip = kAlfredFrameHeight - finalHeight;
+
+	byte *finalBuf = new byte[finalWidth * finalHeight];
+
+	if (linesToSkip > 0) {
+		int skipInterval = kAlfredFrameHeight / linesToSkip;
+		Common::Array<float> idealSkipPositions;
+		for (int i = 0; i < linesToSkip; i++) {
+			float idealPos = (i + 0.5f) * skipInterval;
+			idealSkipPositions.push_back(idealPos);
+		}
+
+		Common::Array<int> tableSkipPositions;
+		for (int scanline = 0; scanline < kAlfredFrameHeight; scanline++) {
+			if (_heightScalingTable[scaleIndex][scanline] != 0) {
+				tableSkipPositions.push_back(scanline);
+			}
+		}
+
+		Common::Array<int> skipTheseLines;
+		for (size_t i = 0; i < idealSkipPositions.size(); i++) {
+			float idealPos = idealSkipPositions[i];
+			int closest = -1;
+			int minDiff = INT32_MAX;
+			for (size_t j = 0; j < tableSkipPositions.size(); j++) {
+				int candidate = tableSkipPositions[j];
+				int diff = static_cast<int>(abs(candidate - idealPos));
+				if (diff < minDiff) {
+					minDiff = diff;
+					closest = candidate;
+				}
+			}
+			if (closest != -1) {
+				skipTheseLines.push_back(closest);
+			}
+			if (skipTheseLines.size() >= static_cast<size_t>(linesToSkip)) {
+				break;
+			}
+		}
+
+		int outY = 0;
+		for (int srcY = 0; srcY < kAlfredFrameHeight; srcY++) {
+			bool skipLine = false;
+			for (size_t skipIdx = 0; skipIdx < skipTheseLines.size(); ++skipIdx) {
+				if (skipTheseLines[skipIdx] == srcY) {
+					skipLine = true;
+					break;
+				}
+			}
+			if (!skipLine) {
+				for (int outX = 0; outX < finalWidth; outX++) {
+					int srcX = static_cast<int>(outX * kAlfredFrameWidth / finalWidth);
+					if (srcX >= kAlfredFrameWidth) {
+						srcX = kAlfredFrameWidth - 1;
+					}
+					int srcIndex = srcY * kAlfredFrameWidth + srcX;
+					int outIndex = outY * finalWidth + outX;
+					if (outIndex >= finalWidth * finalHeight || srcIndex >= kAlfredFrameWidth * kAlfredFrameHeight) {
+					} else
+						finalBuf[outIndex] = buf[srcIndex];
+				}
+				outY++;
+			}
+		}
+	} else {
+		Common::copy(buf, buf + (kAlfredFrameWidth * kAlfredFrameHeight), finalBuf);
+	}
+	return finalBuf;
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/graphics.h b/engines/pelrock/graphics.h
index 6d31b038367..c205d1bbedd 100644
--- a/engines/pelrock/graphics.h
+++ b/engines/pelrock/graphics.h
@@ -29,6 +29,8 @@
 #include "graphics/managed_surface.h"
 #include "graphics/screen.h"
 
+#include "pelrock/types.h"
+
 namespace Pelrock {
 
 class GraphicsManager {
@@ -36,16 +38,60 @@ public:
 	GraphicsManager();
 	~GraphicsManager();
 
+	// Overlay / palette utilities
 	Common::Point showOverlay(int height, Graphics::ManagedSurface &buf);
 	byte *grabBackgroundSlice(Graphics::ManagedSurface &buf, int x, int y, int w, int h);
 	void putBackgroundSlice(Graphics::ManagedSurface &buf, int x, int y, int w, int h, byte *slice);
 	void fadeToBlack(int stepSize);
 	void fadePaletteToTarget(byte *targetPalette, int stepSize);
 	void clearScreen();
+
+	// Text rendering
 	void drawColoredText(Graphics::ManagedSurface *screen, const Common::String &text, int x, int y, int w, byte &defaultColor, Graphics::Font *font);
 	void drawColoredText(Graphics::ManagedSurface &buf, const Common::String &text, int x, int y, int w, byte &defaultColor, Graphics::Font *font);
 	void drawColoredTexts(Graphics::ManagedSurface *surface, const Common::StringArray &text, int x, int y, int w, int yPadding, Graphics::Font *font);
 	void drawColoredTexts(Graphics::ManagedSurface &buf, const Common::StringArray &text, int x, int y, int w, int yPadding, Graphics::Font *font);
+
+	// Frame / background management
+	void copyBackgroundToBuffer();
+	void presentFrame();
+
+	// Sticker rendering
+	void placeStickersFirstPass();
+	void placeStickersSecondPass();
+	void placeSticker(Sticker sticker);
+
+	// Palette animations
+	void updatePaletteAnimations();
+	void animateFadePalette(PaletteAnim *anim);
+	void animateRotatePalette(PaletteAnim *anim);
+
+	/** Water reflection: mirrors buf pixels at (x,y) for water-palette pixels. */
+	void reflectionEffect(byte *buf, int x, int y, int width, int height);
+
+
+	// scaling
+	/**
+	 * Initializes _widthScalingTable / _heightScalingTable.
+	 * Must be called once during engine init, before any drawAlfred().
+	 */
+	void calculateScalingMasks();
+
+	/**
+	 * Returns the scaled width/height and scale parameters for Alfred at yPos.
+	 */
+	ScaleCalculation calculateScaling(int yPos, ScalingParams scalingParams);
+
+	/**
+	 * Scales a source frame (kAlfredFrameWidth × kAlfredFrameHeight) to
+	 * (finalWidth × finalHeight) using the pre-computed scaling tables.
+	 * Caller owns the returned buffer.
+	 */
+	byte *scale(int scaleY, int finalWidth, int finalHeight, byte *buf);
+
+	// Scaling look-up tables initialized by calculateScalingMasks().
+	Common::Array<Common::Array<int>> _widthScalingTable;
+	Common::Array<Common::Array<int>> _heightScalingTable;
 };
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 9764a649f3c..25b6baf77af 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -153,7 +153,7 @@ void PelrockEngine::init() {
 	_sound->loadSoundIndex();
 	_menu->loadMenu();
 
-	calculateScalingMasks();
+	_graphics->calculateScalingMasks();
 	_compositeBuffer.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 	_currentBackground.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 
@@ -330,21 +330,21 @@ bool PelrockEngine::renderScene(int overlayMode) {
 
 		playSoundIfNeeded();
 
-		copyBackgroundToBuffer();
+		_graphics->copyBackgroundToBuffer();
 
-		placeStickersFirstPass();
+		_graphics->placeStickersFirstPass();
 
 		updateAnimations();
 
-		placeStickersSecondPass();
+		_graphics->placeStickersSecondPass();
 
 		renderOverlay(overlayMode);
 
 		mouseHoverForMap();
 
-		presentFrame();
+		_graphics->presentFrame();
 
-		updatePaletteAnimations();
+		_graphics->updatePaletteAnimations();
 
 		// Execute deferred actions AFTER renderScene, so any scene changes
 		// (addSticker, disableSprite, etc.) are in place before the next frame's
@@ -515,54 +515,6 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 	}
 }
 
-void PelrockEngine::reflectionEffect(byte *buf, int x, int y, int width, int height) {
-
-	// ScaleCalculation scaleCalc = calculateScaling(y + 50, _room->_scaleParams);
-
-	// // Update Alfred's scale state for use by other functions
-	// _alfredState.scaledX = scaleCalc.scaleX;
-	// _alfredState.scaledY = scaleCalc.scaleY;
-
-	// // Use the pre-calculated scaled dimensions from calculateScaling
-	// int finalHeight = scaleCalc.scaledHeight;
-	// int finalWidth = scaleCalc.scaledWidth;
-
-	// if (finalHeight <= 0) {
-	// 	finalHeight = 1;
-	// }
-	// if (finalWidth <= 0) {
-	// 	finalWidth = 1;
-	// }
-
-	// byte *finalBuf = scale(scaleCalc.scaleY, finalWidth, finalHeight, buf);
-	// height = finalHeight;
-	// width = finalWidth;
-
-	// Water reflection - draws mirrored sprite on water pixels
-	// Only sprite pixels 0-15 are reflected (checked via pixel & 0xF0 == 0)
-	for (int row = 0; row < height; row++) {
-		int reflectY = y + row;
-
-		if (reflectY >= 400)
-			break; // Screen boundary
-
-		for (int col = 0; col < width; col++) {
-			// byte pixel = finalBuf[(height - 1 - row) * width + col]; // Read from bottom up for mirror
-			byte pixel = buf[(height - 1 - row) * width + col]; // Read from bottom up for mirror
-			// Only reflect pixels 0-15 (high nibble must be 0)
-			if (pixel != 255 && (pixel & 0xF0) == 0) {
-				int px = x + col;
-				if (px >= 0 && px < 640) {
-					byte bgPixel = (byte)_compositeBuffer.getPixel(px, reflectY);
-					if (bgPixel >= 223 && bgPixel < 228) { // Is water (0xDF-0xE3)
-						_compositeBuffer.setPixel(px, reflectY, _room->_paletteRemaps[4][pixel]);
-					}
-				}
-			}
-		}
-	}
-}
-
 void PelrockEngine::executeAction(VerbIcon action, HotSpot *hotspot) {
 	debug("Executing action %d on hotspot %d", action, hotspot->extra);
 	if (action == ITEM) {
@@ -658,11 +610,6 @@ void PelrockEngine::checkMouse() {
 	}
 }
 
-void PelrockEngine::copyBackgroundToBuffer() {
-	// copy background to buffer
-	_compositeBuffer.blitFrom(_currentBackground);
-}
-
 // Calculate Alfred's z-order based on Y position
 // At Y=399 (bottom of screen): z = 10 (foreground)
 // At Y=0 (top of screen): z = 209 (background)
@@ -704,113 +651,6 @@ void PelrockEngine::updateAnimations() {
 	}
 }
 
-void PelrockEngine::presentFrame() {
-	_screen->blitFrom(_compositeBuffer);
-	// paintDebugLayer();
-	_screen->markAllDirty();
-}
-
-void PelrockEngine::updatePaletteAnimations() {
-	if (_room->_currentPaletteAnim != nullptr) {
-		if (_room->_currentPaletteAnim->paletteMode == 1) {
-			animateFadePalette(_room->_currentPaletteAnim);
-		} else {
-			animateRotatePalette(_room->_currentPaletteAnim);
-		}
-	}
-}
-
-void PelrockEngine::paintDebugLayer() {
-	bool showWalkboxes = true;
-
-	if (showWalkboxes) {
-		for (uint i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
-			WalkBox box = _room->_currentRoomWalkboxes[i];
-			drawRect(_screen, box.x, box.y, box.w, box.h, 13);
-		}
-	}
-
-	bool showSprites = true;
-	if (showSprites) {
-		for (uint i = 0; i < _room->_currentRoomAnims.size(); i++) {
-			Sprite sprite = _room->_currentRoomAnims[i];
-			if (sprite.zOrder == 255) {
-				// Skip disabled sprites (zOrder 0xFF)
-				continue;
-			}
-			drawRect(_screen, sprite.x, sprite.y, sprite.w, sprite.h, 14);
-			_smallFont->drawString(_screen, Common::String::format("S %d", sprite.index), sprite.x + 2, sprite.y, 640, 14);
-		}
-	}
-
-	bool showHotspots = true;
-	if (showHotspots) {
-		for (uint i = 0; i < _room->_currentRoomHotspots.size(); i++) {
-			HotSpot hotspot = _room->_currentRoomHotspots[i];
-			if (!hotspot.isEnabled || hotspot.isSprite)
-				continue;
-			drawRect(_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 12);
-			_smallFont->drawString(_screen, Common::String::format("HS %d - %d", hotspot.index - _room->_currentRoomAnims.size(), hotspot.extra), hotspot.x + 2, hotspot.y + 2, 640, 12);
-			// _smallFont->drawString(_screen, Common::String::format("x=%d", hotspot.extra), hotspot.x + 2, hotspot.y + 2 + 14, 640, 14);
-		}
-	}
-
-	bool showExits = true;
-	if (showExits) {
-		for (uint i = 0; i < _room->_currentRoomExits.size(); i++) {
-			Exit exit = _room->_currentRoomExits[i];
-			drawRect(_screen, exit.x, exit.y, exit.w, exit.h, 200 + i);
-			_smallFont->drawString(_screen, Common::String::format("Exit %d -> Room %d", i, exit.targetRoom), exit.x + 2, exit.y + 2, 640, 14);
-		}
-	}
-
-	drawPos(_screen, _alfredState.x, _alfredState.y, 13);
-	drawPos(_screen, _alfredState.x, _alfredState.y - kAlfredFrameHeight, 13);
-	drawPos(_screen, _curWalkTarget.x, _curWalkTarget.y, 100);
-
-	if (showShadows) {
-		_screen->copyRectToSurface(_room->_pixelsShadows, 640, 0, 0, 640, 400);
-	}
-	_smallFont->drawString(_screen, Common::String::format("Room number: %d", _room->_currentRoomNumber), 0, 4, 640, 13);
-	_smallFont->drawString(_screen, Common::String::format("Alfred pos: %d, %d (%d)", _alfredState.x, _alfredState.y, _alfredState.y - kAlfredFrameHeight), 0, 18, 640, 13);
-	_smallFont->drawString(_screen, Common::String::format("Frame number: %d", _chrono->getFrameCount()), 0, 30, 640, 13);
-}
-
-void PelrockEngine::placeStickersFirstPass() {
-	// also place temporary stickers
-	for (uint i = 0; i < _room->_roomStickers.size(); i++) {
-		Sticker sticker = _room->_roomStickers[i];
-		placeSticker(sticker);
-	}
-}
-
-void PelrockEngine::placeStickersSecondPass() {
-	// Some stickers need to be placed AFTER sprites, hardcoded in the original
-	if (_room->_currentRoomNumber == 3) {
-		for (uint i = 0; i < _state->stickersPerRoom[3].size(); i++) {
-			if (_state->stickersPerRoom[3][i].stickerIndex == 14) {
-				placeSticker(_state->stickersPerRoom[3][i]);
-				break;
-			}
-		}
-	}
-}
-
-void PelrockEngine::placeSticker(Sticker sticker) {
-	// Wrap sticker data as a surface and blit (no transparency - all pixels copied)
-	Graphics::Surface stickerSurf;
-	stickerSurf.init(sticker.w, sticker.h, sticker.w, sticker.stickerData, Graphics::PixelFormat::createFormatCLUT8());
-	// Clip to screen bounds
-	Common::Rect destRect(sticker.x, sticker.y, sticker.x + sticker.w, sticker.y + sticker.h);
-	Common::Rect screenRect(0, 0, 640, 400);
-	destRect.clip(screenRect);
-	if (destRect.isEmpty())
-		return;
-	Common::Rect srcRect(destRect.left - sticker.x, destRect.top - sticker.y,
-						 destRect.right - sticker.x, destRect.bottom - sticker.y);
-	_compositeBuffer.blitFrom(stickerSurf, srcRect, Common::Point(destRect.left, destRect.top));
-}
-
 void PelrockEngine::renderOverlay(int overlayMode) {
 	if (overlayMode == OVERLAY_CHOICES) {
 		_dialog->displayChoices(_dialog->_currentChoices, _compositeBuffer);
@@ -821,83 +661,6 @@ void PelrockEngine::renderOverlay(int overlayMode) {
 	}
 }
 
-void PelrockEngine::animateFadePalette(PaletteAnim *anim) {
-	// FADE palette animation - cycles a single palette entry between min/max RGB values
-	// Data layout (after loading from EXE):
-	//   data[0] = current R
-	//   data[1] = current G
-	//   data[2] = current B
-	//   data[3] = min R
-	//   data[4] = min G
-	//   data[5] = min B
-	//   data[6] = max R
-	//   data[7] = max G
-	//   data[8] = max B
-	//   data[9] = flags byte:
-	//             bits 0-1: R increment
-	//             bits 2-3: G increment
-	//             bits 4-5: B increment
-	//             bit 6: direction (0=decreasing toward min, 1=increasing toward max)
-
-	byte flags = anim->data[9];
-	// Increments are scaled by 4 (<<2) to match the shifted RGB values
-	byte rInc = (flags & 0x03) << 2;
-	byte gInc = ((flags >> 2) & 0x03) << 2;
-	byte bInc = ((flags >> 4) & 0x03) << 2;
-	bool increasing = (flags & 0x40) != 0;
-
-	if (increasing) {
-		// Increasing toward max values
-		anim->data[0] += rInc;
-		anim->data[1] += gInc;
-		anim->data[2] += bInc;
-		// Check if R reached max, then reverse direction
-		if (anim->data[0] >= anim->data[6]) {
-			anim->data[9] &= ~0x40; // Clear direction bit
-		}
-	} else {
-		// Decreasing toward min values
-		anim->data[0] -= rInc;
-		anim->data[1] -= gInc;
-		anim->data[2] -= bInc;
-		// Check if R reached min, then reverse direction
-		if (anim->data[0] <= anim->data[3]) {
-			anim->data[9] |= 0x40; // Set direction bit
-		}
-	}
-
-	_room->_roomPalette[anim->startIndex * 3] = anim->data[0];
-	_room->_roomPalette[anim->startIndex * 3 + 1] = anim->data[1];
-	_room->_roomPalette[anim->startIndex * 3 + 2] = anim->data[2];
-	g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
-}
-
-void PelrockEngine::animateRotatePalette(PaletteAnim *anim) {
-
-	if (anim->curFrame >= anim->data[1]) {
-		anim->curFrame = 0;
-		int colors = anim->paletteMode;
-		byte *paletteValues = new byte[colors * 3];
-		for (int i = 0; i < colors; i++) {
-			paletteValues[i * 3] = _room->_roomPalette[(anim->startIndex + i) * 3];
-			paletteValues[i * 3 + 1] = _room->_roomPalette[(anim->startIndex + i) * 3 + 1];
-			paletteValues[i * 3 + 2] = _room->_roomPalette[(anim->startIndex + i) * 3 + 2];
-		}
-		for (int i = 0; i < colors; i++) {
-			int srcIndex = (i + 1) % colors;
-			_room->_roomPalette[(anim->startIndex + i) * 3] = paletteValues[srcIndex * 3];
-			_room->_roomPalette[(anim->startIndex + i) * 3 + 1] = paletteValues[srcIndex * 3 + 1];
-			_room->_roomPalette[(anim->startIndex + i) * 3 + 2] = paletteValues[srcIndex * 3 + 2];
-		}
-		delete[] paletteValues;
-
-		g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
-
-	} else {
-		anim->curFrame++;
-	}
-}
-
 void PelrockEngine::doAction(VerbIcon action, HotSpot *hotspot) {
 	if (action == NO_ACTION) {
 		return;
@@ -1031,7 +794,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 					// place first-pass stickers now so they are present in the very
 					// first presentFrame() for the new room (avoids a one-frame flash
 					// without stickers).
-					placeStickersFirstPass();
+					_graphics->placeStickersFirstPass();
 				}
 			}
 		} else {
@@ -1165,95 +928,12 @@ void PelrockEngine::drawIdleFrame() {
 	}
 }
 
-byte *PelrockEngine::scale(int scaleY, int finalWidth, int finalHeight, byte *buf) {
-
-	// The scaling table is indexed by how many scanlines to skip (scaleY), not by final height
-	int scaleIndex = scaleY;
-	if (scaleIndex >= (int)_heightScalingTable.size()) {
-		scaleIndex = _heightScalingTable.size() - 1;
-	}
-	if (scaleIndex < 0) {
-		scaleIndex = 0;
-	}
-	int linesToSkip = kAlfredFrameHeight - finalHeight;
-
-	byte *finalBuf = new byte[finalWidth * finalHeight];
-
-	if (linesToSkip > 0) {
-		int skipInterval = kAlfredFrameHeight / linesToSkip;
-		Common::Array<float> idealSkipPositions;
-		for (int i = 0; i < linesToSkip; i++) {
-			float idealPos = (i + 0.5f) * skipInterval;
-			idealSkipPositions.push_back(idealPos);
-		}
-
-		Common::Array<int> tableSkipPositions;
-		for (int scanline = 0; scanline < kAlfredFrameHeight; scanline++) {
-			if (_heightScalingTable[scaleIndex][scanline] != 0) {
-				tableSkipPositions.push_back(scanline);
-			}
-		}
-
-		// for (size_t i = 0; i < tableSkipPositions.size(); i++) {
-		// }
-
-		Common::Array<int> skipTheseLines;
-		for (size_t i = 0; i < idealSkipPositions.size(); i++) {
-			float idealPos = idealSkipPositions[i];
-			int closest = -1;
-			int minDiff = INT32_MAX;
-			for (size_t j = 0; j < tableSkipPositions.size(); j++) {
-				int candidate = tableSkipPositions[j];
-				int diff = static_cast<int>(abs(candidate - idealPos));
-				if (diff < minDiff) {
-					minDiff = diff;
-					closest = candidate;
-				}
-			}
-			if (closest != -1) {
-				skipTheseLines.push_back(closest);
-			}
-			if (skipTheseLines.size() >= static_cast<size_t>(linesToSkip)) {
-				break;
-			}
-		}
-
-		int outY = 0;
-		for (int srcY = 0; srcY < kAlfredFrameHeight; srcY++) {
-			bool skipLine = false;
-			for (size_t skipIdx = 0; skipIdx < skipTheseLines.size(); ++skipIdx) {
-				if (skipTheseLines[skipIdx] == srcY) {
-					skipLine = true;
-					break;
-				}
-			}
-			if (!skipLine) {
-				for (int outX = 0; outX < finalWidth; outX++) {
-					int srcX = static_cast<int>(outX * kAlfredFrameWidth / finalWidth);
-					if (srcX >= kAlfredFrameWidth) {
-						srcX = kAlfredFrameWidth - 1;
-					}
-					int srcIndex = srcY * kAlfredFrameWidth + srcX;
-					int outIndex = outY * finalWidth + outX;
-					if (outIndex >= finalWidth * finalHeight || srcIndex >= kAlfredFrameWidth * kAlfredFrameHeight) {
-					} else
-						finalBuf[outIndex] = buf[srcIndex];
-				}
-				outY++;
-			}
-		}
-	} else {
-		Common::copy(buf, buf + (kAlfredFrameWidth * kAlfredFrameHeight), finalBuf);
-	}
-	return finalBuf;
-}
-
 /**
  * Scales and shades alfred sprite and draws it to the composite buffer
  */
 void PelrockEngine::drawAlfred(byte *buf) {
 
-	ScaleCalculation scaleCalc = calculateScaling(_alfredState.y, _room->_scaleParams);
+	ScaleCalculation scaleCalc = _graphics->calculateScaling(_alfredState.y, _room->_scaleParams);
 
 	// Use the pre-calculated scaled dimensions from calculateScaling
 	int finalHeight = scaleCalc.scaledHeight;
@@ -1270,7 +950,7 @@ void PelrockEngine::drawAlfred(byte *buf) {
 		finalWidth = 1;
 	}
 
-	byte *scaledBuf = scale(scaleCalc.scaleY, finalWidth, finalHeight, buf);
+	byte *scaledBuf = _graphics->scale(scaleCalc.scaleY, finalWidth, finalHeight, buf);
 	delete[] _alfredSprite;
 	_alfredSprite = scaledBuf;
 
@@ -1310,7 +990,7 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	if ((_room->_currentRoomNumber == 25 || _room->_currentRoomNumber == 45) && _alfredState.y >= 299) {
 		// Offset from Alfred's feet to start of reflection
 		int yOffset = (_room->_currentRoomNumber == 45) ? 25 : 13;
-		reflectionEffect(_alfredSprite, _alfredState.x, _alfredState.y + yOffset, finalWidth, finalHeight);
+		_graphics->reflectionEffect(_alfredSprite, _alfredState.x, _alfredState.y + yOffset, finalWidth, finalHeight);
 	}
 }
 
@@ -1405,6 +1085,60 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 	}
 }
 
+void PelrockEngine::paintDebugLayer() {
+	bool showWalkboxes = true;
+
+	if (showWalkboxes) {
+		for (uint i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
+			WalkBox box = _room->_currentRoomWalkboxes[i];
+			drawRect(_screen, box.x, box.y, box.w, box.h, 13);
+		}
+	}
+
+	bool showSprites = true;
+	if (showSprites) {
+		for (uint i = 0; i < _room->_currentRoomAnims.size(); i++) {
+			Sprite sprite = _room->_currentRoomAnims[i];
+			if (sprite.zOrder == 255) {
+				continue;
+			}
+			drawRect(_screen, sprite.x, sprite.y, sprite.w, sprite.h, 14);
+			_smallFont->drawString(_screen, Common::String::format("S %d", sprite.index), sprite.x + 2, sprite.y, 640, 14);
+		}
+	}
+
+	bool showHotspots = true;
+	if (showHotspots) {
+		for (uint i = 0; i < _room->_currentRoomHotspots.size(); i++) {
+			HotSpot hotspot = _room->_currentRoomHotspots[i];
+			if (!hotspot.isEnabled || hotspot.isSprite)
+				continue;
+			drawRect(_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 12);
+			_smallFont->drawString(_screen, Common::String::format("HS %d - %d", hotspot.index - _room->_currentRoomAnims.size(), hotspot.extra), hotspot.x + 2, hotspot.y + 2, 640, 12);
+		}
+	}
+
+	bool showExits = true;
+	if (showExits) {
+		for (uint i = 0; i < _room->_currentRoomExits.size(); i++) {
+			Exit exit = _room->_currentRoomExits[i];
+			drawRect(_screen, exit.x, exit.y, exit.w, exit.h, 200 + i);
+			_smallFont->drawString(_screen, Common::String::format("Exit %d -> Room %d", i, exit.targetRoom), exit.x + 2, exit.y + 2, 640, 14);
+		}
+	}
+
+	drawPos(_screen, _alfredState.x, _alfredState.y, 13);
+	drawPos(_screen, _alfredState.x, _alfredState.y - kAlfredFrameHeight, 13);
+	drawPos(_screen, _curWalkTarget.x, _curWalkTarget.y, 100);
+
+	if (showShadows) {
+		_screen->copyRectToSurface(_room->_pixelsShadows, 640, 0, 0, 640, 400);
+	}
+	_smallFont->drawString(_screen, Common::String::format("Room number: %d", _room->_currentRoomNumber), 0, 4, 640, 13);
+	_smallFont->drawString(_screen, Common::String::format("Alfred pos: %d, %d (%d)", _alfredState.x, _alfredState.y, _alfredState.y - kAlfredFrameHeight), 0, 18, 640, 13);
+	_smallFont->drawString(_screen, Common::String::format("Frame number: %d", _chrono->getFrameCount()), 0, 30, 640, 13);
+}
+
 void PelrockEngine::checkLongMouseClick(int x, int y) {
 	_alfredState.idleFrameCounter = 0;
 	int hotspotIndex = isHotspotUnder(x, y);
@@ -1431,98 +1165,6 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 	}
 }
 
-void PelrockEngine::calculateScalingMasks() {
-
-	for (int scaleFactor = 0; scaleFactor < kAlfredFrameWidth; scaleFactor++) {
-		float step = kAlfredFrameWidth / (scaleFactor + 1.0f);
-		Common::Array<int> row;
-		float index = 0.0f;
-		int sourcePixel = 0;
-
-		while (index < kAlfredFrameWidth) {
-			row.push_back(sourcePixel);
-			index += step;
-			sourcePixel += 1;
-			if (sourcePixel >= kAlfredFrameWidth) {
-				sourcePixel = kAlfredFrameWidth - 1;
-			}
-		}
-
-		// Pad to exactly CHAR_WIDTH entries
-		while (row.size() < kAlfredFrameWidth) {
-			row.push_back(row.empty() ? 0 : row[row.size() - 1]);
-		}
-
-		_widthScalingTable.push_back(row);
-	}
-
-	for (int scaleFactor = 0; scaleFactor < kAlfredFrameHeight; scaleFactor++) {
-		float step = kAlfredFrameHeight / (scaleFactor + 1.0f);
-		Common::Array<int> row;
-		row.resize(kAlfredFrameHeight, 0);
-		float position = step;
-		int counter = 1;
-		while (position < kAlfredFrameHeight) {
-			int idx = static_cast<int>(round(position));
-			if (idx < kAlfredFrameHeight) {
-				row[idx] = counter;
-				counter++;
-			}
-			position += step;
-		}
-		_heightScalingTable.push_back(row);
-	}
-}
-
-ScaleCalculation PelrockEngine::calculateScaling(int yPos, ScalingParams scalingParams) {
-	// scaleY = amount to subtract from height (94 max for 0xFF mode)
-	// scaleX = amount to subtract from width (47 max for 0xFF mode, = scaleY/2)
-	int scaleY = 0;
-	int scaleX = 0;
-	if (scalingParams.scaleMode == 0xFF) {
-		// Maximum scaling - character is very small (used for bird's eye view maps)
-		scaleY = 0x5e; // 94
-		scaleX = 0x2f; // 47
-	} else if (scalingParams.scaleMode == 0xFE) {
-		// No scaling - full size character
-		scaleY = 0;
-		scaleX = 0;
-	} else if (scalingParams.scaleMode == 0) {
-		// Dynamic scaling based on Y position
-		if (scalingParams.yThreshold < yPos) {
-			// Below threshold - no scaling
-			scaleY = 0;
-			scaleX = 0;
-		} else {
-			if (scalingParams.scaleDivisor != 0) {
-				scaleY = (scalingParams.yThreshold - yPos) / scalingParams.scaleDivisor;
-				scaleX = scaleY / 2;
-			} else {
-				scaleY = 0;
-				scaleX = 0;
-			}
-		}
-	} else {
-		scaleY = 0;
-		scaleX = 0;
-	}
-
-	// Original game formula: actual dimensions = base - scale amount
-	int finalHeight = kAlfredFrameHeight - scaleY;
-	if (finalHeight < 1)
-		finalHeight = 1;
-
-	int finalWidth = kAlfredFrameWidth - scaleX;
-	if (finalWidth < 1)
-		finalWidth = 1;
-
-	ScaleCalculation scaleCalc;
-	scaleCalc.scaledHeight = finalHeight;
-	scaleCalc.scaledWidth = finalWidth;
-	scaleCalc.scaleY = scaleY;
-	scaleCalc.scaleX = scaleX;
-	return scaleCalc;
-}
 
 int PelrockEngine::isHotspotUnder(int x, int y) {
 
@@ -2068,7 +1710,7 @@ void PelrockEngine::setScreen(int roomNumber) {
 
 	_screen->clear();
 
-	copyBackgroundToBuffer();
+	_graphics->copyBackgroundToBuffer();
 	g_system->getPaletteManager()->setPalette(palette, 0, 256);
 
 	_room->loadRoomMetadata(&roomFile, roomNumber);
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 03b48167aec..922b7653555 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -87,18 +87,9 @@ private:
 	int getScrollPositionForItem(int item);
 
 	void checkMouse();
-	void copyBackgroundToBuffer();
 	void updateAnimations();
-	void presentFrame();
-	void updatePaletteAnimations();
-	void paintDebugLayer();
-	void placeStickersFirstPass();
-	void placeStickersSecondPass();
-	void placeSticker(Sticker sticker);
 	void renderOverlay(int overlayMode);
 
-	void animateFadePalette(PaletteAnim *anim);
-	void animateRotatePalette(PaletteAnim *anim);
 	void doAction(VerbIcon action, HotSpot *hotspot);
 	void talkTo(HotSpot *hotspot);
 	void lookAt(HotSpot *hotspot);
@@ -127,21 +118,12 @@ private:
 	void checkMouseHover();
 	void checkMouseClick(int x, int y);
 	void checkLongMouseClick(int x, int y);
-
-	void calculateScalingMasks();
-	ScaleCalculation calculateScaling(int yPos, ScalingParams scalingParams);
-	byte *scale(int scaleY, int finalWidth, int finalHeight, byte *buf);
-
-	Common::Array<Common::Array<int>> _widthScalingTable;
-	Common::Array<Common::Array<int>> _heightScalingTable;
+	void paintDebugLayer();
 
 	// walking
 	int _currentStep = 0;
 	PathContext _currentContext = {nullptr, nullptr, 0, 0, 0};
 
-	Graphics::ManagedSurface _currentBackground; // Clean background - NEVER modified
-	Graphics::ManagedSurface _bgScreen;
-
 	ActionPopupState _actionPopupState;
 	InventoryOverlayState _inventoryOverlayState;
 
@@ -153,7 +135,6 @@ private:
 	QueuedAction _queuedAction = {NO_ACTION, -1, false, false};
 
 	bool showShadows = false;
-
 	bool shouldPlayIntro = false;
 	bool gameInitialized = false;
 	bool screenReady = false;
@@ -190,6 +171,8 @@ public:
 	AlfredState _alfredState;
 	ShakeEffectState _shakeEffectState;
 	Graphics::ManagedSurface _compositeBuffer; // Working composition buffer
+	Graphics::ManagedSurface _currentBackground; // Clean background - NEVER modified
+	Graphics::ManagedSurface _bgScreen;
 
 	GameStateData *_state = new GameStateData();
 
@@ -260,7 +243,6 @@ public:
 	void handleFlightRoomFrame();
 
 	void passerByAnim(uint32 frameCount);
-	void reflectionEffect(byte *buf, int x, int y, int width, int height);
 	void changeCursor(Cursor cursor);
 
 	void travelToEgypt();


Commit: 57a84acae1e928f4fc0a13d7dbfed8ddc2f2018e
    https://github.com/scummvm/scummvm/commit/57a84acae1e928f4fc0a13d7dbfed8ddc2f2018e
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:30+02:00

Commit Message:
PELROCK: Fixes mouse hover breaking if positioned where action popup used to be

Changed paths:
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 25b6baf77af..c38d2f3b5f0 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1662,10 +1662,6 @@ void PelrockEngine::checkMouseHover() {
 		return;
 	}
 
-	if (isActionUnder(_events->_mouseX, _events->_mouseY) != NO_ACTION) {
-		hotspotDetected = false;
-	}
-
 	// Calculate walk target first (before checking anything else)
 	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, _events->_mouseX, _events->_mouseY, hotspotDetected, hotspotDetected ? &_room->_currentRoomHotspots[hotspotIndex] : nullptr);
 


Commit: 4736dc2ad8c4061805d112e14a7e0346b6c10314
    https://github.com/scummvm/scummvm/commit/4736dc2ad8c4061805d112e14a7e0346b6c10314
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:30+02:00

Commit Message:
PELROCK: Implements original timing when walking/talking (halving the speed)

Changed paths:
    engines/pelrock/detection.h
    engines/pelrock/detection_tables.h
    engines/pelrock/metaengine.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/detection.h b/engines/pelrock/detection.h
index 263ea2e9ac3..b66e27e9a5c 100644
--- a/engines/pelrock/detection.h
+++ b/engines/pelrock/detection.h
@@ -39,6 +39,7 @@ extern const PlainGameDescriptor pelrockGames[];
 extern const ADGameDescription gameDescriptions[];
 
 #define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1
+#define GAMEOPTION_ALTERNATE_TIMING GUIO_GAMEOPTIONS2
 
 } // End of namespace Pelrock
 
diff --git a/engines/pelrock/detection_tables.h b/engines/pelrock/detection_tables.h
index f4c05cbab9e..79c0987d677 100644
--- a/engines/pelrock/detection_tables.h
+++ b/engines/pelrock/detection_tables.h
@@ -31,10 +31,10 @@ const ADGameDescription gameDescriptions[] = {
 		"pelrock",
 		nullptr,
 		AD_ENTRY1s("ALFRED.1", "ee0047cfcceece9c4f6a426245b6f449", 12915352),
-		Common::EN_ANY,
+		Common::ES_ESP,
 		Common::kPlatformDOS,
 		ADGF_UNSTABLE,
-		GUIO1(GUIO_NONE)
+		GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_ALTERNATE_TIMING)
 	},
 
 	AD_TABLE_END_MARKER
diff --git a/engines/pelrock/metaengine.cpp b/engines/pelrock/metaengine.cpp
index a6268bedeaf..11bff334e70 100644
--- a/engines/pelrock/metaengine.cpp
+++ b/engines/pelrock/metaengine.cpp
@@ -39,6 +39,17 @@ static const ADExtraGuiOptionsMap optionsList[] = {
 			0
 		}
 	},
+	{
+		GAMEOPTION_ALTERNATE_TIMING,
+		{
+			_s("Alternate timing"),
+			_s("Use ScummVM's alternate animation timing instead of the original game's slower walking and talking speed"),
+			"alternate_timing",
+			false,
+			0,
+			0
+		}
+	},
 	AD_EXTRA_GUI_OPTIONS_TERMINATOR
 };
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index c38d2f3b5f0..52587086ba3 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -92,6 +92,10 @@ Common::String PelrockEngine::getGameId() const {
 	return _gameDescription->gameId;
 }
 
+bool PelrockEngine::isAlternateTiming() const {
+	return ConfMan.getBool("alternate_timing");
+}
+
 // Common::Array<Common::Array<Common::String>> wordWrap(Common::String text);
 
 Common::Error PelrockEngine::run() {
@@ -326,6 +330,18 @@ bool PelrockEngine::renderScene(int overlayMode) {
 
 	_chrono->updateChrono();
 	if (_chrono->_gameTick) {
+		if (!isAlternateTiming()) {
+			bool inHalfSpeedMode = (_alfredState.animState == ALFRED_WALKING) ||
+			                       (_dialog->_dialogActive && _alfredState.animState == ALFRED_TALKING);
+			if (inHalfSpeedMode) {
+				_halfSpeedToggle = !_halfSpeedToggle;
+				if (!_halfSpeedToggle)
+					return false; // skip this tick — animate at half speed
+			} else {
+				_halfSpeedToggle = false; // reset when leaving half-speed mode
+			}
+		}
+
 		frameTriggers();
 
 		playSoundIfNeeded();
@@ -1650,6 +1666,7 @@ void PelrockEngine::checkMouseHover() {
 		_hoveredMapLocation = "Alfred";
 	}
 
+
 	if (hotspotIndex != -1) {
 		hotspotDetected = true;
 		if (hotspotIndex < _room->_currentRoomDescriptions.size())
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 922b7653555..97f0baf72a8 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -146,6 +146,9 @@ private:
 
 	bool _mouseDisabled = false;
 
+	// Original timing: toggles each game tick during walking/talking to halve animation speed
+	bool _halfSpeedToggle = false;
+
 	int _flightFrameCounter = 0;
 	int _flightSorcererSpriteIdx = -1;
 	bool _flightSorcererAppeared = false;
@@ -198,6 +201,12 @@ public:
 		return _randomSource.getRandomNumber(maxNum);
 	}
 
+	/**
+	 * Returns true if "Alternate timing" option is enabled.
+	 * When false, the engine uses the original game's half-speed walking/talking timing.
+	 */
+	bool isAlternateTiming() const;
+
 	bool hasFeature(EngineFeature f) const override {
 		return (f == kSupportsLoadingDuringRuntime) ||
 			   (f == kSupportsSavingDuringRuntime) ||


Commit: f0fdccb1d8606fd092001bb07e0ee873976eea65
    https://github.com/scummvm/scummvm/commit/f0fdccb1d8606fd092001bb07e0ee873976eea65
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:30+02:00

Commit Message:
PELROCK: Respect original timing too for NPC talking animations

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 8c7aa0911f5..2b3e172f655 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -206,6 +206,10 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 			_curSprite->isTalking = true;
 			xBasePos = _curSprite->x + _curSprite->w / 2;
 			yBasePos = _curSprite->y; // Above sprite, adjust for line
+
+			// Set NPC talk speed byte for original timing.
+			TalkingAnims *th = &g_engine->_room->_talkingAnimHeader;
+			g_engine->_npcTalkSpeedByte = _curSprite->talkingAnimIndex ? th->speedByteB : th->speedByteA;
 		}
 	}
 	displayDialogue(dialogueLines, speakerId, xBasePos, yBasePos); // Default position
@@ -243,6 +247,9 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 	uint32 pageTtlMs = calcPageTtlMs(dialogueLines[curPage]);
 	uint32 pageStartMs = g_system->getMillis();
 
+	if(speakerId != kAlfredColor) {
+		_isNPCTalking = true;
+	}
 	// Render loop - display text and wait for click or TTL
 	while (!g_engine->shouldQuit()) {
 		_events->pollEvent();
@@ -313,6 +320,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		_curSprite->isTalking = false;
 	}
 	_dialogActive = false;
+	_isNPCTalking = false;
 	g_engine->_alfredState.setState(ALFRED_IDLE);
 }
 
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 568d596b61f..b021f149630 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -141,6 +141,7 @@ public:
 	bool _dialogActive = false;
 	bool _dismissDialog = false; // When true, the current dialog will be dismissed on the next iteration of the conversation loop (used for programmatically closing dialogs, e.g. when exiting a room)
 	bool _disableClickToAdvance = false;
+	bool _isNPCTalking = false;
 
 	Common::String _leftArrow = Common::String(17);
 	Common::String _rightArrow = Common::String(16);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 52587086ba3..a0f4c703346 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -326,20 +326,39 @@ void PelrockEngine::travelToEgypt() {
 	_state->addInventoryItem(59);
 }
 
+bool PelrockEngine::shouldSkipFrame() {
+	if (isAlternateTiming()) {
+		return false; // never skip frames in alternate timing mode
+	}
+	int skipAmount = 0;
+	if (_alfredState.animState == ALFRED_WALKING) {
+		skipAmount = 1;
+	} else if (_dialog->_dialogActive) {
+		if (_alfredState.animState == ALFRED_TALKING) {
+			skipAmount = 1; // Alfred speaking
+		} else if (_dialog->_isNPCTalking && _npcTalkSpeedByte > 0) {
+			skipAmount = _npcTalkSpeedByte; // NPC speaking
+		}
+	}
+
+	if (skipAmount > 0) {
+		if (_renderSkipCounter < skipAmount) {
+			_renderSkipCounter++;
+			return true; // skip this tick
+		}
+		_renderSkipCounter = 0; // render this tick, reset
+	} else {
+		_renderSkipCounter = 0;
+	}
+	return false;
+}
+
 bool PelrockEngine::renderScene(int overlayMode) {
 
 	_chrono->updateChrono();
 	if (_chrono->_gameTick) {
-		if (!isAlternateTiming()) {
-			bool inHalfSpeedMode = (_alfredState.animState == ALFRED_WALKING) ||
-			                       (_dialog->_dialogActive && _alfredState.animState == ALFRED_TALKING);
-			if (inHalfSpeedMode) {
-				_halfSpeedToggle = !_halfSpeedToggle;
-				if (!_halfSpeedToggle)
-					return false; // skip this tick — animate at half speed
-			} else {
-				_halfSpeedToggle = false; // reset when leaving half-speed mode
-			}
+		if (shouldSkipFrame()) {
+			return false;
 		}
 
 		frameTriggers();
@@ -432,7 +451,6 @@ void PelrockEngine::maybeHaveDogPee() {
 		walkTo(152, _alfredState.y);
 		_isDogPeeing = false;
 	}
-
 }
 
 void PelrockEngine::maybePlayPostIntro() {
@@ -713,11 +731,20 @@ void PelrockEngine::talkTo(HotSpot *hotspot) {
 	}
 	changeCursor(DEFAULT);
 	debug("Talking to hotspot %d (%d) with extra %d", hotspot->index, hotspot->isSprite ? hotspot->index : hotspot->index - _room->_currentRoomAnims.size(), hotspot->extra);
+
+	// Set NPC talk speed byte for original timing
+	TalkingAnims *th = &_room->_talkingAnimHeader;
+	_npcTalkSpeedByte = animSet->talkingAnimIndex ? th->speedByteB : th->speedByteA;
+	debug("NPC talk speed byte: %d (slot %d)", _npcTalkSpeedByte, animSet->talkingAnimIndex);
+
 	_dialog->startConversation(_room->_conversationData, _room->_conversationDataSize, animSet->talkingAnimIndex, animSet);
+
+	// Conversation ended — clear NPC talk speed byte
+	_npcTalkSpeedByte = 0;
+	_renderSkipCounter = 0;
 }
 
 void PelrockEngine::lookAt(HotSpot *hotspot) {
-	debug("Looking at hotspot %d (%d) with extra %d", hotspot->index, hotspot->isSprite ? hotspot->index : hotspot->index - _room->_currentRoomAnims.size(), hotspot->extra);
 	_dialog->sayAlfred(_room->_currentRoomDescriptions[_currentHotspot->index]);
 	_actionPopupState.isActive = false;
 }
@@ -1181,7 +1208,6 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 	}
 }
 
-
 int PelrockEngine::isHotspotUnder(int x, int y) {
 
 	for (size_t i = 0; i < _room->_currentRoomHotspots.size(); i++) {
@@ -1335,7 +1361,10 @@ void PelrockEngine::animateTalkingNPC(Sprite *animSet) {
 	int h = index ? animHeader->hAnimB : animHeader->hAnimA;
 	int numFrames = index ? animHeader->numFramesAnimB : animHeader->numFramesAnimA;
 
-	if (_chrono->getFrameCount() % kTalkAnimationSpeed == 0) {
+	// In the original game, the render rate itself is throttled to match kTalkAnimationSpeed.
+	// When in "Alternate timing" mode, we use kTalkAnimationSpeed to avoid slowing down the game loop.
+	int npcTalkSpeed = isAlternateTiming() ? kTalkAnimationSpeed : 1;
+	if (_chrono->getFrameCount() % npcTalkSpeed == 0) {
 		if (index) {
 			animHeader->currentFrameAnimB++;
 		} else {
@@ -1491,7 +1520,6 @@ void PelrockEngine::gameLoop() {
 	_screen->update();
 }
 
-
 void PelrockEngine::computerLoop() {
 	Computer computer(_events);
 	computer.run();
@@ -1666,7 +1694,6 @@ void PelrockEngine::checkMouseHover() {
 		_hoveredMapLocation = "Alfred";
 	}
 
-
 	if (hotspotIndex != -1) {
 		hotspotDetected = true;
 		if (hotspotIndex < _room->_currentRoomDescriptions.size())
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 97f0baf72a8..5677e478f05 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -146,8 +146,10 @@ private:
 
 	bool _mouseDisabled = false;
 
-	// Original timing: toggles each game tick during walking/talking to halve animation speed
-	bool _halfSpeedToggle = false;
+	// Original timing: counter-based render-skip to replicate process_game_state(N) slowdown.
+	// _renderSkipAmount = N means skip N ticks, then render 1 → (1+N) ticks per render.
+	int _renderSkipAmount = 0;
+	int _renderSkipCounter = 0;
 
 	int _flightFrameCounter = 0;
 	int _flightSorcererSpriteIdx = -1;
@@ -173,6 +175,7 @@ public:
 	DialogManager *_dialog = nullptr;
 	AlfredState _alfredState;
 	ShakeEffectState _shakeEffectState;
+	byte _npcTalkSpeedByte = 0;
 	Graphics::ManagedSurface _compositeBuffer; // Working composition buffer
 	Graphics::ManagedSurface _currentBackground; // Clean background - NEVER modified
 	Graphics::ManagedSurface _bgScreen;
@@ -256,6 +259,8 @@ public:
 
 	void travelToEgypt();
 
+	bool shouldSkipFrame();
+
 	// Actions
 	void doExtraActions(int roomNumber);
 	void pyramidCollapse();
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index e08ad61d9f8..fa229e1ef31 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -1199,7 +1199,8 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 	talkHeader.hAnimA = talkFile.readByte();
 	talkFile.read(&talkHeader.unknown3, 2);
 	talkHeader.numFramesAnimA = talkFile.readByte();
-	talkFile.read(&talkHeader.unknown4, 5);
+	talkFile.read(&talkHeader.unknown4, 4);
+	talkHeader.speedByteA = talkFile.readByte();
 
 	talkHeader.offsetXAnimB = talkFile.readByte();
 	talkHeader.offsetYAnimB = talkFile.readByte();
@@ -1207,7 +1208,9 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 	talkHeader.hAnimB = talkFile.readByte();
 	talkFile.read(&talkHeader.unknown5, 2);
 	talkHeader.numFramesAnimB = talkFile.readByte();
-	talkFile.read(&talkHeader.unknown6, 29);
+	talkFile.read(&talkHeader.unknown7, 4);
+	talkHeader.speedByteB = talkFile.readByte();
+	talkFile.read(&talkHeader.unknown6, 24);
 	// debug("Talking anim header for room %d: spritePointer=%d, wA=%d, hA=%d, framesA=%d, wB=%d, hB=%d, framesB=%d", roomNumber, talkHeader.spritePointer, talkHeader.wAnimA, talkHeader.hAnimA, talkHeader.numFramesAnimA, talkHeader.wAnimB, talkHeader.hAnimB, talkHeader.numFramesAnimB);
 
 	if (talkHeader.spritePointer == 0) {
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index e227d1a4ae9..b500b39d5ff 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -310,17 +310,22 @@ struct TalkingAnims {
 	byte hAnimA;
 	byte unknown3[2];
 	byte numFramesAnimA;
-	byte unknown4[5];
+	byte unknown4[4];         // slot 0 data pointer (unused in ScummVM)
+	byte speedByteA;          // slot 0 offset 0x12: controls NPC talk render rate (original: 2+speedByte ticks per render)
 
 	byte offsetXAnimB;
 	byte offsetYAnimB;
-	byte currentFrameAnimA;
 
 	byte wAnimB;
 	byte hAnimB;
-	byte unknown5;
+	byte unknown5[2];         // slot 1 stride (unused in ScummVM)
 	byte numFramesAnimB;
-	byte unknown6[29];
+	byte unknown7[4];         // slot 1 data pointer (unused in ScummVM)
+	byte speedByteB;          // slot 1 speed byte at file offset 30
+	byte unknown6[24];        // slots 2-3 (unused)
+
+	// Runtime fields (not read from file)
+	byte currentFrameAnimA;
 	byte currentFrameAnimB;
 
 	byte **animA = nullptr;


Commit: 266df65e9a43e86f967a60093a615d0bad8cabb4
    https://github.com/scummvm/scummvm/commit/266df65e9a43e86f967a60093a615d0bad8cabb4
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:31+02:00

Commit Message:
PELROCK: Fixes idle animation of talking character in room 39

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 2882dd763d7..28762c91d5e 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1680,6 +1680,8 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 	guard->animData[0].movementFlags = 0;
 	guard->animData[0].curFrame = 0;
 	guard->animData[0].nframes = 1;
+	// copy idle frame from talking animation
+	guard->animData[0].animData[0] = _room->_talkingAnimHeader.animA[0];
 	_alfredState.direction = ALFRED_RIGHT;
 	walkAndAction(_room->findHotspotByExtra(guard->extra), TALK);
 	if (shouldQuit()) {
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 2b3e172f655..11935299f1a 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -195,10 +195,8 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		if (_curSprite != nullptr) {
 			_curSprite->isTalking = false;
 		}
-		// Offset X position for Alfred to avoid overlapping with his sprite
+
 		xBasePos = g_engine->_alfredState.x;
-		// Original game: uses the scaled character height
-		// not the fixed kAlfredFrameHeight. _alfredState.h is updated by drawAlfred().
 		yBasePos = g_engine->_alfredState.y - g_engine->_alfredState.h; // Above scaled sprite top
 	} else {
 		g_engine->_alfredState.setState(ALFRED_IDLE);


Commit: 1a9467777cfd095a663ceca1434d8b1accb6625e
    https://github.com/scummvm/scummvm/commit/1a9467777cfd095a663ceca1434d8b1accb6625e
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:31+02:00

Commit Message:
PELROCK: Icons and scaling for volume control

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 33209820fdf..a4ecb23e424 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -36,21 +36,21 @@ static const uint32 kSettingsPaletteOffset = 0x2884C2;
 
 // JUEGO.EXE — inventory object descriptions
 static const uint32 kInventoryDescriptionsOffset = 0x4715E;
-static const uint32 kInventoryDescriptionsSize   = 7868;
+static const uint32 kInventoryDescriptionsSize = 7868;
 
 // JUEGO.EXE — in-menu text strings
 static const uint32 kMenuTextOffset = 0x49203;
-static const uint32 kMenuTextSize   = 230;
+static const uint32 kMenuTextSize = 230;
 
 // ALFRED.7 — main menu background
-static const uint32 kMainMenuPart1Offset         = 2405266;
-static const uint32 kMainMenuPart1RawSize        = 65536; // first uncompressed chunk
+static const uint32 kMainMenuPart1Offset = 2405266;
+static const uint32 kMainMenuPart1RawSize = 65536;        // first uncompressed chunk
 static const uint32 kMainMenuPart1CompressedSize = 29418; // following compressed tail
-static const uint32 kMainMenuPart2Offset         = 2500220;
-static const uint32 kMainMenuPart2RawSize        = 32768;
+static const uint32 kMainMenuPart2Offset = 2500220;
+static const uint32 kMainMenuPart2RawSize = 32768;
 static const uint32 kMainMenuPart2CompressedSize = 30288;
-static const uint32 kMainMenuPart3Offset         = 2563266;
-static const uint32 kMainMenuPart3Size           = 92160;
+static const uint32 kMainMenuPart3Offset = 2563266;
+static const uint32 kMainMenuPart3Size = 92160;
 
 // ALFRED.7 — menu buttons (save/load/sound/exit, one contiguous block)
 static const uint32 kMenuButtonsOffset = 3193376;
@@ -86,9 +86,35 @@ MenuButton MenuManager::isButtonClicked(int x, int y) {
 	if (_savesDown.contains(x, y)) {
 		return SAVEGAME_NEXT_BUTTON;
 	}
+	if(_showSoundOptions) {
+		if(_masterVolumeLeftRect.contains(x, y)) {
+			return MASTER_LEFT_BUTTON;
+		}
+		if(_masterVolumeRightRect.contains(x, y)) {
+			return MASTER_RIGHT_BUTTON;
+		}
+		if(_sfxVolumeLeftRect.contains(x, y)) {
+			return SFX_LEFT_BUTTON;
+		}
+		if(_sfxVolumeRightRect.contains(x, y)) {
+			return SFX_RIGHT_BUTTON;
+		}
+		if(_musicVolumeLeftRect.contains(x, y)) {
+			return MUSIC_LEFT_BUTTON;
+		}
+		if(_musicVolumeRightRect.contains(x, y)) {
+			return MUSIC_RIGHT_BUTTON;
+		}
+	}
 	return NO_BUTTON; // Default fallback
 }
 
+Graphics::ManagedSurface scale(Graphics::ManagedSurface, const byte *original, float scale) {
+	Graphics::ManagedSurface newSurface = Graphics::ManagedSurface(66, 64, Graphics::PixelFormat::createFormatCLUT8());
+	memcpy(newSurface.getPixels(), original, 66 * 64);
+	return *newSurface.scale(66 * scale, 64 * scale);
+}
+
 bool MenuManager::checkMouseClick(int x, int y) {
 
 	bool selectedItem = false;
@@ -107,6 +133,17 @@ bool MenuManager::checkMouseClick(int x, int y) {
 	}
 
 	MenuButton button = isButtonClicked(x, y);
+
+	if( button != MUSIC_LEFT_BUTTON &&
+		button != MUSIC_RIGHT_BUTTON &&
+		button != SFX_LEFT_BUTTON &&
+		button != SFX_RIGHT_BUTTON &&
+		button != MASTER_LEFT_BUTTON &&
+		button != MASTER_RIGHT_BUTTON
+	) {
+		_showSoundOptions = false;
+	}
+
 	switch (button) {
 	case QUESTION_MARK_BUTTON:
 		debug("Show credits");
@@ -130,6 +167,34 @@ bool MenuManager::checkMouseClick(int x, int y) {
 	case EXIT_MENU_BUTTON:
 		g_engine->quitGame();
 		break;
+	case SOUNDS_BUTTON:
+		_showSoundOptions = true;
+		_menuText = Common::StringArray();
+		break;
+	case MASTER_LEFT_BUTTON:
+		currentMasterVolumeScale = MAX(0.4f, currentMasterVolumeScale - 0.1f);
+		_masterSoundIcon = scale(_masterSoundIcon, _soundControlMasterIcon, currentMasterVolumeScale);
+		break;
+	case MASTER_RIGHT_BUTTON:
+		currentMasterVolumeScale = MIN(1.0f, currentMasterVolumeScale + 0.1f);
+		_masterSoundIcon = scale(_masterSoundIcon, _soundControlMasterIcon, currentMasterVolumeScale);
+		break;
+	case SFX_LEFT_BUTTON:
+		currentSfxVolumeScale = MAX(0.4f, currentSfxVolumeScale - 0.1f);
+		_sfxSoundIcon = scale(_sfxSoundIcon, _soundControlSfxIcon, currentSfxVolumeScale);
+		break;
+	case SFX_RIGHT_BUTTON:
+		currentSfxVolumeScale = MIN(1.0f, currentSfxVolumeScale + 0.1f);
+		_sfxSoundIcon = scale(_sfxSoundIcon, _soundControlSfxIcon, currentSfxVolumeScale);
+		break;
+	case MUSIC_LEFT_BUTTON:
+		currentMusicVolumeScale = MAX(0.4f, currentMusicVolumeScale - 0.1f);
+		_musicSoundIcon = scale(_musicSoundIcon, _soundControlMusicIcon, currentMusicVolumeScale);
+		break;
+	case MUSIC_RIGHT_BUTTON:
+		currentMusicVolumeScale = MIN(1.0f, currentMusicVolumeScale + 0.1f);
+		_musicSoundIcon = scale(_musicSoundIcon, _soundControlMusicIcon, currentMusicVolumeScale);
+		break;
 	default:
 		break;
 	}
@@ -197,15 +262,16 @@ void MenuManager::menuLoop() {
 		_events->pollEvent();
 
 		if (_events->_leftMouseClicked) {
-			if(checkMouseClick(_events->_mouseX, _events->_mouseY)) {
+			if (checkMouseClick(_events->_mouseX, _events->_mouseY)) {
 				break;
 			}
 			_events->_leftMouseClicked = false;
 		}
-		if(_events->_rightMouseClicked) {
+		if (_events->_rightMouseClicked) {
 			break;
 		}
 		drawScreen();
+		drawPaletteSquares((byte *)_screen->getPixels(), _mainMenuPalette);
 		_screen->markAllDirty();
 		_screen->update();
 		g_system->delayMillis(10);
@@ -294,7 +360,6 @@ void MenuManager::loadMenu() {
 
 	memcpy((byte *)_mainMenu.getPixels() + curPos, decompressedPart2, decompressedSize);
 	curPos += decompressedSize;
-	debug("Settings menu size loaded: %d, with last block %d", curPos, curPos + (int)kMainMenuPart3Size);
 	delete[] compressedPart2;
 	delete[] decompressedPart2;
 	alfred7.seek(kMainMenuPart3Offset, SEEK_SET);
@@ -310,6 +375,42 @@ void MenuManager::loadMenu() {
 	readButton(alfred7, alfred7.pos(), _savesDownArrows, _savesDown);
 	readButton(alfred7, kQuestionMarkOffset, _questionMark, _questionMarkRect);
 
+	byte *soundArrowsData = nullptr;
+	size_t soundArrowsSize = 0;
+	rleDecompressSingleBuda(&alfred7, kSoundControlOffset, soundArrowsData, soundArrowsSize);
+
+	readButton(soundArrowsData, 0, _soundControlArrowLeft, 36, 28);
+	readButton(soundArrowsData, 36 * 28 * 2, _soundControlArrowRight, 31, 28);
+	delete[] soundArrowsData;
+
+	byte *soundIconMasterData = nullptr;
+	size_t soundIconMasterSize = 0;
+	rleDecompressSingleBuda(&alfred7, kSoundMasterOffset, soundIconMasterData, soundIconMasterSize);
+	_soundControlMasterIcon = new byte[66 * 64];
+	extractSingleFrame(soundIconMasterData, _soundControlMasterIcon, 0, 66, 64);
+	delete[] soundIconMasterData;
+
+	byte *soundIconSfxData = nullptr;
+	size_t soundIconSfxSize = 0;
+	rleDecompressSingleBuda(&alfred7, kSoundSfxOffset, soundIconSfxData, soundIconSfxSize);
+	_soundControlSfxIcon = new byte[66 * 64];
+	extractSingleFrame(soundIconSfxData, _soundControlSfxIcon, 0, 66, 64);
+	delete[] soundIconSfxData;
+
+	byte *soundIconMusicData = nullptr;
+	size_t soundIconMusicSize = 0;
+	rleDecompressSingleBuda(&alfred7, kSoundMusicOffset, soundIconMusicData, soundIconMusicSize);
+	_soundControlMusicIcon = new byte[66 * 64];
+	extractSingleFrame(soundIconMusicData, _soundControlMusicIcon, 0, 66, 64);
+	delete[] soundIconMusicData;
+
+	_masterSoundIcon = Graphics::ManagedSurface(66, 64, Graphics::PixelFormat::createFormatCLUT8());
+	_sfxSoundIcon = Graphics::ManagedSurface(66, 64, Graphics::PixelFormat::createFormatCLUT8());
+	_musicSoundIcon = Graphics::ManagedSurface(66, 64, Graphics::PixelFormat::createFormatCLUT8());
+	memcpy(_masterSoundIcon.getPixels(), _soundControlMasterIcon, 66 * 64);
+	memcpy(_sfxSoundIcon.getPixels(), _soundControlSfxIcon, 66 * 64);
+	memcpy(_musicSoundIcon.getPixels(), _soundControlMusicIcon, 66 * 64);
+
 	_menuText = _menuTexts[0];
 	alfred7.close();
 
@@ -318,12 +419,23 @@ void MenuManager::loadMenu() {
 	}
 }
 
+void MenuManager::readButton(byte *rawData, uint32 offset, byte *outBuffer[2], int w, int h) {
+	byte *buttonData = new byte[w * h * 2];
+	outBuffer[0] = new byte[w * h];
+	outBuffer[1] = new byte[w * h];
+	Common::copy(rawData + offset, rawData + offset + w * h * 2, buttonData);
+	extractSingleFrame(buttonData, outBuffer[0], 0, w, h);
+	extractSingleFrame(buttonData, outBuffer[1], 1, w, h);
+	delete[] buttonData;
+}
+
 void MenuManager::readButton(Common::File &alfred7, uint32 offset, byte *outBuffer[2], Common::Rect rect) {
 	alfred7.seek(offset, SEEK_SET);
 	byte *buttonData = new byte[rect.width() * rect.height() * 2];
-	alfred7.read(buttonData, rect.width() * rect.height() * 2);
 	outBuffer[0] = new byte[rect.width() * rect.height()];
 	outBuffer[1] = new byte[rect.width() * rect.height()];
+	alfred7.read(buttonData, rect.width() * rect.height() * 2);
+
 	extractSingleFrame(buttonData, outBuffer[0], 0, rect.width(), rect.height());
 	extractSingleFrame(buttonData, outBuffer[1], 1, rect.width(), rect.height());
 	delete[] buttonData;
@@ -354,6 +466,9 @@ void MenuManager::loadMenuTexts() {
 }
 
 void MenuManager::cleanUp() {
+	_masterSoundIcon.free();
+	_sfxSoundIcon.free();
+	_musicSoundIcon.free();
 }
 
 void MenuManager::drawButtons() {
@@ -390,6 +505,28 @@ void MenuManager::drawButtons() {
 
 	buf = button == SAVEGAME_NEXT_BUTTON ? _savesDownArrows[1] : _savesDownArrows[0];
 	drawSpriteToBuffer(_compositeBuffer, buf, _savesDown.left, _savesDown.top, _savesDown.width(), _savesDown.height(), kTransparentColor);
+
+	if(_showSoundOptions) {
+		// _compositeBuffer.transBlitFrom(_masterSoundIcon, Common::Point(233, 188), kSoundControlsTransparentColor);
+		// _compositeBuffer.transBlitFrom(_musicSoundIcon, Common::Point(299, 188), kSoundControlsTransparentColor);
+		// _compositeBuffer.transBlitFrom(_sfxSoundIcon, Common::Point(365, 188), kSoundControlsTransparentColor);
+		_compositeBuffer.transBlitFrom(_masterSoundIcon, Common::Point(266 - _masterSoundIcon.w /2 , 212 - _masterSoundIcon.h / 2), kSoundControlsTransparentColor);
+		_compositeBuffer.transBlitFrom(_musicSoundIcon, Common::Point(333 - _musicSoundIcon.w / 2, 212 - _musicSoundIcon.h / 2), kSoundControlsTransparentColor);
+		_compositeBuffer.transBlitFrom(_sfxSoundIcon, Common::Point(399 - _sfxSoundIcon.w / 2, 212 - _sfxSoundIcon.h / 2), kSoundControlsTransparentColor);
+
+		buf = button == MASTER_LEFT_BUTTON ? _soundControlArrowLeft[1] : _soundControlArrowLeft[0];
+		drawSpriteToBuffer(_compositeBuffer, buf, _masterVolumeLeftRect.left, _masterVolumeLeftRect.top, _masterVolumeLeftRect.width(), _masterVolumeLeftRect.height(), kSoundControlsTransparentColor);
+		buf = button == MASTER_RIGHT_BUTTON ? _soundControlArrowRight[1] : _soundControlArrowRight[0];
+		drawSpriteToBuffer(_compositeBuffer, buf, _masterVolumeRightRect.left, _masterVolumeRightRect.top, _masterVolumeRightRect.width(), _masterVolumeRightRect.height(), kSoundControlsTransparentColor);
+		buf = button == SFX_LEFT_BUTTON ? _soundControlArrowLeft[1] : _soundControlArrowLeft[0];
+		drawSpriteToBuffer(_compositeBuffer, buf, _sfxVolumeLeftRect.left, _sfxVolumeLeftRect.top, _sfxVolumeLeftRect.width(), _sfxVolumeLeftRect.height(), kSoundControlsTransparentColor);
+		buf = button == SFX_RIGHT_BUTTON ? _soundControlArrowRight[1] : _soundControlArrowRight[0];
+		drawSpriteToBuffer(_compositeBuffer, buf, _sfxVolumeRightRect.left, _sfxVolumeRightRect.top, _sfxVolumeRightRect.width(), _sfxVolumeRightRect.height(), kSoundControlsTransparentColor);
+		buf = button == MUSIC_LEFT_BUTTON ? _soundControlArrowLeft[1] : _soundControlArrowLeft[0];
+		drawSpriteToBuffer(_compositeBuffer, buf, _musicVolumeLeftRect.left, _musicVolumeLeftRect.top, _musicVolumeLeftRect.width(), _musicVolumeLeftRect.height(), kSoundControlsTransparentColor);
+		buf = button == MUSIC_RIGHT_BUTTON ? _soundControlArrowRight[1] : _soundControlArrowRight[0];
+		drawSpriteToBuffer(_compositeBuffer, buf, _musicVolumeRightRect.left, _musicVolumeRightRect.top, _musicVolumeRightRect.width(), _musicVolumeRightRect.height(), kSoundControlsTransparentColor);
+	}
 }
 
 Pelrock::MenuManager::~MenuManager() {
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 76e9eea7ece..9574f89d930 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -31,9 +31,15 @@
 
 namespace Pelrock {
 
-const int kQuestionMarkOffset = 3214046;
-const int kInvLeftArrowOffset = 3215906;
+const uint32 kQuestionMarkOffset = 3214046;
+const uint32 kInvLeftArrowOffset = 3215906;
+const uint32 kSoundControlOffset = 3037008;
+const uint32 kSoundMasterOffset = 	2662588;
+const uint32 kSoundMusicOffset = 2664746;
+const uint32 kSoundSfxOffset = 2667140;
 const int kTransparentColor = 15;
+// const int kTransparentColor = 195;
+const int kSoundControlsTransparentColor = 195;
 const uint32 kCreditsBackgroundOffset = 3271454;
 
 enum MenuButton {
@@ -46,6 +52,12 @@ enum MenuButton {
 	SAVE_GAME_BUTTON,
 	LOAD_GAME_BUTTON,
 	SOUNDS_BUTTON,
+	MASTER_LEFT_BUTTON,
+	MASTER_RIGHT_BUTTON,
+	SFX_LEFT_BUTTON,
+	SFX_RIGHT_BUTTON,
+	MUSIC_LEFT_BUTTON,
+	MUSIC_RIGHT_BUTTON,
 	NO_BUTTON
 };
 
@@ -220,6 +232,7 @@ private:
 	void loadMenuTexts();
 	void cleanUp();
 	void drawButtons();
+	void readButton(byte *rawData, uint32 offset, byte *outBuffer[2], int w, int h);
 	void readButton(Common::File &alfred7, uint32 offset, byte *outBuffer[2], Common::Rect rect);
 	MenuButton isButtonClicked(int x, int y);
 	Graphics::Screen *_screen = nullptr;
@@ -256,6 +269,31 @@ private:
 	Common::Rect _questionMarkRect = Common::Rect(Common::Point(217, 293), 31, 30);
 	byte *_questionMark[2] = {nullptr};
 
+	Common::Rect _masterVolumeLeftRect = Common::Rect(Common::Point(232, 252), 36, 28);
+	Common::Rect _masterVolumeRightRect = Common::Rect(Common::Point(268, 252), 31, 28);
+
+	Common::Rect _sfxVolumeLeftRect = Common::Rect(Common::Point(298, 252), 36, 28);
+	Common::Rect _sfxVolumeRightRect = Common::Rect(Common::Point(334, 252), 31, 28);
+
+	Common::Rect _musicVolumeLeftRect = Common::Rect(Common::Point(364, 252), 36, 28);
+	Common::Rect _musicVolumeRightRect = Common::Rect(Common::Point(400, 252), 31, 28);
+
+
+	byte *_soundControlArrowLeft[2] = {nullptr};
+	byte *_soundControlArrowRight[2] = {nullptr};
+
+	byte *_soundControlMasterIcon = nullptr;
+	byte *_soundControlSfxIcon = nullptr;
+	byte *_soundControlMusicIcon = nullptr;
+
+	Graphics::ManagedSurface _masterSoundIcon;
+	Graphics::ManagedSurface _sfxSoundIcon;
+	Graphics::ManagedSurface _musicSoundIcon;
+
+	float currentMasterVolumeScale = 1.0f;
+	float currentSfxVolumeScale = 1.0f;
+	float currentMusicVolumeScale = 1.0f;
+
 	Common::Array<Common::StringArray> _menuTexts;
 	// Temporary
 	int _selectedInvIndex = 0;
@@ -265,6 +303,7 @@ private:
 	Common::Array<Common::Point> _inventorySlots;
 
 	bool showButtons = true;
+	bool _showSoundOptions = false;
 };
 
 } // End of namespace Pelrock


Commit: cb596d4fbeb5c070d50b2208d8220164f3b8ca7e
    https://github.com/scummvm/scummvm/commit/cb596d4fbeb5c070d50b2208d8220164f3b8ca7e
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:31+02:00

Commit Message:
PELROCK: Volume icons scale up/down with left button rather than click

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index a4ecb23e424..8e5c2ab2a0e 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -86,23 +86,23 @@ MenuButton MenuManager::isButtonClicked(int x, int y) {
 	if (_savesDown.contains(x, y)) {
 		return SAVEGAME_NEXT_BUTTON;
 	}
-	if(_showSoundOptions) {
-		if(_masterVolumeLeftRect.contains(x, y)) {
+	if (_showSoundOptions) {
+		if (_masterVolumeLeftRect.contains(x, y)) {
 			return MASTER_LEFT_BUTTON;
 		}
-		if(_masterVolumeRightRect.contains(x, y)) {
+		if (_masterVolumeRightRect.contains(x, y)) {
 			return MASTER_RIGHT_BUTTON;
 		}
-		if(_sfxVolumeLeftRect.contains(x, y)) {
+		if (_sfxVolumeLeftRect.contains(x, y)) {
 			return SFX_LEFT_BUTTON;
 		}
-		if(_sfxVolumeRightRect.contains(x, y)) {
+		if (_sfxVolumeRightRect.contains(x, y)) {
 			return SFX_RIGHT_BUTTON;
 		}
-		if(_musicVolumeLeftRect.contains(x, y)) {
+		if (_musicVolumeLeftRect.contains(x, y)) {
 			return MUSIC_LEFT_BUTTON;
 		}
-		if(_musicVolumeRightRect.contains(x, y)) {
+		if (_musicVolumeRightRect.contains(x, y)) {
 			return MUSIC_RIGHT_BUTTON;
 		}
 	}
@@ -115,6 +115,39 @@ Graphics::ManagedSurface scale(Graphics::ManagedSurface, const byte *original, f
 	return *newSurface.scale(66 * scale, 64 * scale);
 }
 
+void MenuManager::checkMouseDown(int x, int y) {
+	if(!_events->_leftMouseButton) {
+		return;
+	}
+	MenuButton b = isButtonClicked(x, y);
+	switch (b) {
+	case MASTER_LEFT_BUTTON:
+		currentMasterVolumeScale = MAX(0.4f, currentMasterVolumeScale - 0.1f);
+		_masterSoundIcon = scale(_masterSoundIcon, _soundControlMasterIcon, currentMasterVolumeScale);
+		break;
+	case MASTER_RIGHT_BUTTON:
+		currentMasterVolumeScale = MIN(1.0f, currentMasterVolumeScale + 0.1f);
+		_masterSoundIcon = scale(_masterSoundIcon, _soundControlMasterIcon, currentMasterVolumeScale);
+		break;
+	case SFX_LEFT_BUTTON:
+		currentSfxVolumeScale = MAX(0.4f, currentSfxVolumeScale - 0.1f);
+		_sfxSoundIcon = scale(_sfxSoundIcon, _soundControlSfxIcon, currentSfxVolumeScale);
+		break;
+	case SFX_RIGHT_BUTTON:
+		currentSfxVolumeScale = MIN(1.0f, currentSfxVolumeScale + 0.1f);
+		_sfxSoundIcon = scale(_sfxSoundIcon, _soundControlSfxIcon, currentSfxVolumeScale);
+		break;
+	case MUSIC_LEFT_BUTTON:
+		currentMusicVolumeScale = MAX(0.4f, currentMusicVolumeScale - 0.1f);
+		_musicSoundIcon = scale(_musicSoundIcon, _soundControlMusicIcon, currentMusicVolumeScale);
+		break;
+	case MUSIC_RIGHT_BUTTON:
+		currentMusicVolumeScale = MIN(1.0f, currentMusicVolumeScale + 0.1f);
+		_musicSoundIcon = scale(_musicSoundIcon, _soundControlMusicIcon, currentMusicVolumeScale);
+		break;
+	}
+}
+
 bool MenuManager::checkMouseClick(int x, int y) {
 
 	bool selectedItem = false;
@@ -134,13 +167,12 @@ bool MenuManager::checkMouseClick(int x, int y) {
 
 	MenuButton button = isButtonClicked(x, y);
 
-	if( button != MUSIC_LEFT_BUTTON &&
+	if (button != MUSIC_LEFT_BUTTON &&
 		button != MUSIC_RIGHT_BUTTON &&
 		button != SFX_LEFT_BUTTON &&
 		button != SFX_RIGHT_BUTTON &&
 		button != MASTER_LEFT_BUTTON &&
-		button != MASTER_RIGHT_BUTTON
-	) {
+		button != MASTER_RIGHT_BUTTON) {
 		_showSoundOptions = false;
 	}
 
@@ -171,29 +203,11 @@ bool MenuManager::checkMouseClick(int x, int y) {
 		_showSoundOptions = true;
 		_menuText = Common::StringArray();
 		break;
-	case MASTER_LEFT_BUTTON:
-		currentMasterVolumeScale = MAX(0.4f, currentMasterVolumeScale - 0.1f);
-		_masterSoundIcon = scale(_masterSoundIcon, _soundControlMasterIcon, currentMasterVolumeScale);
-		break;
-	case MASTER_RIGHT_BUTTON:
-		currentMasterVolumeScale = MIN(1.0f, currentMasterVolumeScale + 0.1f);
-		_masterSoundIcon = scale(_masterSoundIcon, _soundControlMasterIcon, currentMasterVolumeScale);
-		break;
 	case SFX_LEFT_BUTTON:
-		currentSfxVolumeScale = MAX(0.4f, currentSfxVolumeScale - 0.1f);
-		_sfxSoundIcon = scale(_sfxSoundIcon, _soundControlSfxIcon, currentSfxVolumeScale);
+		_sound->playSound("CAT_1ZZZ.SMP", -1);
 		break;
 	case SFX_RIGHT_BUTTON:
-		currentSfxVolumeScale = MIN(1.0f, currentSfxVolumeScale + 0.1f);
-		_sfxSoundIcon = scale(_sfxSoundIcon, _soundControlSfxIcon, currentSfxVolumeScale);
-		break;
-	case MUSIC_LEFT_BUTTON:
-		currentMusicVolumeScale = MAX(0.4f, currentMusicVolumeScale - 0.1f);
-		_musicSoundIcon = scale(_musicSoundIcon, _soundControlMusicIcon, currentMusicVolumeScale);
-		break;
-	case MUSIC_RIGHT_BUTTON:
-		currentMusicVolumeScale = MIN(1.0f, currentMusicVolumeScale + 0.1f);
-		_musicSoundIcon = scale(_musicSoundIcon, _soundControlMusicIcon, currentMusicVolumeScale);
+		_sound->playSound("CAT_1ZZZ.SMP", -1);
 		break;
 	default:
 		break;
@@ -260,7 +274,7 @@ void MenuManager::menuLoop() {
 	while (!g_engine->shouldQuit()) {
 
 		_events->pollEvent();
-
+		checkMouseDown(_events->_mouseX, _events->_mouseY);
 		if (_events->_leftMouseClicked) {
 			if (checkMouseClick(_events->_mouseX, _events->_mouseY)) {
 				break;
@@ -506,11 +520,11 @@ void MenuManager::drawButtons() {
 	buf = button == SAVEGAME_NEXT_BUTTON ? _savesDownArrows[1] : _savesDownArrows[0];
 	drawSpriteToBuffer(_compositeBuffer, buf, _savesDown.left, _savesDown.top, _savesDown.width(), _savesDown.height(), kTransparentColor);
 
-	if(_showSoundOptions) {
+	if (_showSoundOptions) {
 		// _compositeBuffer.transBlitFrom(_masterSoundIcon, Common::Point(233, 188), kSoundControlsTransparentColor);
 		// _compositeBuffer.transBlitFrom(_musicSoundIcon, Common::Point(299, 188), kSoundControlsTransparentColor);
 		// _compositeBuffer.transBlitFrom(_sfxSoundIcon, Common::Point(365, 188), kSoundControlsTransparentColor);
-		_compositeBuffer.transBlitFrom(_masterSoundIcon, Common::Point(266 - _masterSoundIcon.w /2 , 212 - _masterSoundIcon.h / 2), kSoundControlsTransparentColor);
+		_compositeBuffer.transBlitFrom(_masterSoundIcon, Common::Point(266 - _masterSoundIcon.w / 2, 212 - _masterSoundIcon.h / 2), kSoundControlsTransparentColor);
 		_compositeBuffer.transBlitFrom(_musicSoundIcon, Common::Point(333 - _musicSoundIcon.w / 2, 212 - _musicSoundIcon.h / 2), kSoundControlsTransparentColor);
 		_compositeBuffer.transBlitFrom(_sfxSoundIcon, Common::Point(399 - _sfxSoundIcon.w / 2, 212 - _sfxSoundIcon.h / 2), kSoundControlsTransparentColor);
 
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 9574f89d930..25e73d2c929 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -226,6 +226,7 @@ public:
 	byte _mainMenuPalette[768] = {0};
 
 private:
+	void checkMouseDown(int x, int y);
 	bool checkMouseClick(int x, int y);
 	void showCredits();
 	bool selectInventoryItem(int i);
@@ -272,11 +273,11 @@ private:
 	Common::Rect _masterVolumeLeftRect = Common::Rect(Common::Point(232, 252), 36, 28);
 	Common::Rect _masterVolumeRightRect = Common::Rect(Common::Point(268, 252), 31, 28);
 
-	Common::Rect _sfxVolumeLeftRect = Common::Rect(Common::Point(298, 252), 36, 28);
-	Common::Rect _sfxVolumeRightRect = Common::Rect(Common::Point(334, 252), 31, 28);
+	Common::Rect _musicVolumeLeftRect = Common::Rect(Common::Point(298, 252), 36, 28);
+	Common::Rect _musicVolumeRightRect = Common::Rect(Common::Point(334, 252), 31, 28);
 
-	Common::Rect _musicVolumeLeftRect = Common::Rect(Common::Point(364, 252), 36, 28);
-	Common::Rect _musicVolumeRightRect = Common::Rect(Common::Point(400, 252), 31, 28);
+	Common::Rect _sfxVolumeLeftRect = Common::Rect(Common::Point(364, 252), 36, 28);
+	Common::Rect _sfxVolumeRightRect = Common::Rect(Common::Point(400, 252), 31, 28);
 
 
 	byte *_soundControlArrowLeft[2] = {nullptr};


Commit: 4fefe69117006960df155b7384e051447937a1de
    https://github.com/scummvm/scummvm/commit/4fefe69117006960df155b7384e051447937a1de
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:32+02:00

Commit Message:
PELROCK: Implements volume controls

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 8e5c2ab2a0e..4d54c1ea3d2 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -58,6 +58,30 @@ static const uint32 kMenuButtonsOffset = 3193376;
 Pelrock::MenuManager::MenuManager(Graphics::Screen *screen, PelrockEventManager *events, ResourceManager *res, SoundManager *sound) : _screen(screen), _events(events), _res(res), _sound(sound) {
 }
 
+static int levelToMixerVolume(int level) {
+	return (level * 255) / 14;
+}
+
+static int mixerVolumeToLevel(int volume) {
+	return (volume * 14 + 127) / 255;
+}
+
+static float levelToScale(int level) {
+	return 0.4f + (level / 14.0f) * 0.6f;
+}
+
+static void rebuildSoundIcon(Graphics::ManagedSurface &target, const byte *source, int level) {
+	float s = levelToScale(level);
+	int newW = MAX(1, (int)(66 * s));
+	int newH = MAX(1, (int)(64 * s));
+	Graphics::ManagedSurface temp;
+	temp.create(66, 64, Graphics::PixelFormat::createFormatCLUT8());
+	memcpy(temp.getPixels(), source, 66 * 64);
+	Graphics::ManagedSurface *scaled = temp.scale(newW, newH, false);
+	target.copyFrom(*scaled);
+	delete scaled;
+}
+
 MenuButton MenuManager::isButtonClicked(int x, int y) {
 	if (_questionMarkRect.contains(x, y)) {
 		return QUESTION_MARK_BUTTON;
@@ -109,12 +133,6 @@ MenuButton MenuManager::isButtonClicked(int x, int y) {
 	return NO_BUTTON; // Default fallback
 }
 
-Graphics::ManagedSurface scale(Graphics::ManagedSurface, const byte *original, float scale) {
-	Graphics::ManagedSurface newSurface = Graphics::ManagedSurface(66, 64, Graphics::PixelFormat::createFormatCLUT8());
-	memcpy(newSurface.getPixels(), original, 66 * 64);
-	return *newSurface.scale(66 * scale, 64 * scale);
-}
-
 void MenuManager::checkMouseDown(int x, int y) {
 	if(!_events->_leftMouseButton) {
 		return;
@@ -122,28 +140,36 @@ void MenuManager::checkMouseDown(int x, int y) {
 	MenuButton b = isButtonClicked(x, y);
 	switch (b) {
 	case MASTER_LEFT_BUTTON:
-		currentMasterVolumeScale = MAX(0.4f, currentMasterVolumeScale - 0.1f);
-		_masterSoundIcon = scale(_masterSoundIcon, _soundControlMasterIcon, currentMasterVolumeScale);
+		_masterVolumeLevel = MAX(0, _masterVolumeLevel - 1);
+		_sound->setVolumeMaster(levelToMixerVolume(_masterVolumeLevel));
+		rebuildSoundIcon(_masterSoundIcon, _soundControlMasterIcon, _masterVolumeLevel);
 		break;
 	case MASTER_RIGHT_BUTTON:
-		currentMasterVolumeScale = MIN(1.0f, currentMasterVolumeScale + 0.1f);
-		_masterSoundIcon = scale(_masterSoundIcon, _soundControlMasterIcon, currentMasterVolumeScale);
+		_masterVolumeLevel = MIN(14, _masterVolumeLevel + 1);
+		_sound->setVolumeMaster(levelToMixerVolume(_masterVolumeLevel));
+		rebuildSoundIcon(_masterSoundIcon, _soundControlMasterIcon, _masterVolumeLevel);
 		break;
 	case SFX_LEFT_BUTTON:
-		currentSfxVolumeScale = MAX(0.4f, currentSfxVolumeScale - 0.1f);
-		_sfxSoundIcon = scale(_sfxSoundIcon, _soundControlSfxIcon, currentSfxVolumeScale);
+		_sfxVolumeLevel = MAX(0, _sfxVolumeLevel - 1);
+		_sound->setVolumeSfx(levelToMixerVolume(_sfxVolumeLevel));
+		rebuildSoundIcon(_sfxSoundIcon, _soundControlSfxIcon, _sfxVolumeLevel);
 		break;
 	case SFX_RIGHT_BUTTON:
-		currentSfxVolumeScale = MIN(1.0f, currentSfxVolumeScale + 0.1f);
-		_sfxSoundIcon = scale(_sfxSoundIcon, _soundControlSfxIcon, currentSfxVolumeScale);
+		_sfxVolumeLevel = MIN(14, _sfxVolumeLevel + 1);
+		_sound->setVolumeSfx(levelToMixerVolume(_sfxVolumeLevel));
+		rebuildSoundIcon(_sfxSoundIcon, _soundControlSfxIcon, _sfxVolumeLevel);
 		break;
 	case MUSIC_LEFT_BUTTON:
-		currentMusicVolumeScale = MAX(0.4f, currentMusicVolumeScale - 0.1f);
-		_musicSoundIcon = scale(_musicSoundIcon, _soundControlMusicIcon, currentMusicVolumeScale);
+		_musicVolumeLevel = MAX(0, _musicVolumeLevel - 1);
+		_sound->setVolumeMusic(levelToMixerVolume(_musicVolumeLevel));
+		rebuildSoundIcon(_musicSoundIcon, _soundControlMusicIcon, _musicVolumeLevel);
 		break;
 	case MUSIC_RIGHT_BUTTON:
-		currentMusicVolumeScale = MIN(1.0f, currentMusicVolumeScale + 0.1f);
-		_musicSoundIcon = scale(_musicSoundIcon, _soundControlMusicIcon, currentMusicVolumeScale);
+		_musicVolumeLevel = MIN(14, _musicVolumeLevel + 1);
+		_sound->setVolumeMusic(levelToMixerVolume(_musicVolumeLevel));
+		rebuildSoundIcon(_musicSoundIcon, _soundControlMusicIcon, _musicVolumeLevel);
+		break;
+	default:
 		break;
 	}
 }
@@ -160,10 +186,7 @@ bool MenuManager::checkMouseClick(int x, int y) {
 			return false;
 		}
 	}
-	if (!selectedItem) {
-		_selectedInvIndex = -1;
-		_menuText = _menuTexts[0];
-	}
+
 
 	MenuButton button = isButtonClicked(x, y);
 
@@ -174,6 +197,10 @@ bool MenuManager::checkMouseClick(int x, int y) {
 		button != MASTER_LEFT_BUTTON &&
 		button != MASTER_RIGHT_BUTTON) {
 		_showSoundOptions = false;
+		if (!selectedItem) {
+			_selectedInvIndex = -1;
+			_menuText = _menuTexts[0];
+		}
 	}
 
 	switch (button) {
@@ -270,7 +297,25 @@ void MenuManager::menuLoop() {
 
 	g_system->getPaletteManager()->setPalette(_mainMenuPalette, 0, 256);
 	g_engine->changeCursor(DEFAULT);
+	_showSoundOptions = false;
 	_menuText = _menuTexts[0];
+
+	// Initialize volume levels from current settings
+	_sfxVolumeLevel    = mixerVolumeToLevel(_sound->getVolumeSfx());
+	_musicVolumeLevel  = mixerVolumeToLevel(_sound->getVolumeMusic());
+	_masterVolumeLevel = mixerVolumeToLevel(_sound->getVolumeMaster());
+
+	debug("Initial master volume level: %d", _masterVolumeLevel);
+	debug("Initial SFX volume level: %d", _sfxVolumeLevel);
+	debug("Initial Music volume level: %d", _musicVolumeLevel);
+	_masterSoundIcon.create(66, 64, Graphics::PixelFormat::createFormatCLUT8());
+	_sfxSoundIcon.create(66, 64, Graphics::PixelFormat::createFormatCLUT8());
+	_musicSoundIcon.create(66, 64, Graphics::PixelFormat::createFormatCLUT8());
+
+	rebuildSoundIcon(_masterSoundIcon, _soundControlMasterIcon, _masterVolumeLevel);
+	rebuildSoundIcon(_sfxSoundIcon, _soundControlSfxIcon, _sfxVolumeLevel);
+	rebuildSoundIcon(_musicSoundIcon, _soundControlMusicIcon, _musicVolumeLevel);
+
 	while (!g_engine->shouldQuit()) {
 
 		_events->pollEvent();
@@ -418,12 +463,6 @@ void MenuManager::loadMenu() {
 	extractSingleFrame(soundIconMusicData, _soundControlMusicIcon, 0, 66, 64);
 	delete[] soundIconMusicData;
 
-	_masterSoundIcon = Graphics::ManagedSurface(66, 64, Graphics::PixelFormat::createFormatCLUT8());
-	_sfxSoundIcon = Graphics::ManagedSurface(66, 64, Graphics::PixelFormat::createFormatCLUT8());
-	_musicSoundIcon = Graphics::ManagedSurface(66, 64, Graphics::PixelFormat::createFormatCLUT8());
-	memcpy(_masterSoundIcon.getPixels(), _soundControlMasterIcon, 66 * 64);
-	memcpy(_sfxSoundIcon.getPixels(), _soundControlSfxIcon, 66 * 64);
-	memcpy(_musicSoundIcon.getPixels(), _soundControlMusicIcon, 66 * 64);
 
 	_menuText = _menuTexts[0];
 	alfred7.close();
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 25e73d2c929..7a543947682 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -291,9 +291,9 @@ private:
 	Graphics::ManagedSurface _sfxSoundIcon;
 	Graphics::ManagedSurface _musicSoundIcon;
 
-	float currentMasterVolumeScale = 1.0f;
-	float currentSfxVolumeScale = 1.0f;
-	float currentMusicVolumeScale = 1.0f;
+	int _masterVolumeLevel = 14;
+	int _sfxVolumeLevel = 14;
+	int _musicVolumeLevel = 14;
 
 	Common::Array<Common::StringArray> _menuTexts;
 	// Temporary
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 2d5f1be7aa0..46aa916acc6 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -24,6 +24,7 @@
 #include "audio/decoders/wave.h"
 #include "audio/mixer.h"
 
+#include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/endian.h"
 #include "common/file.h"
@@ -294,8 +295,34 @@ void SoundManager::stopSound(int channel) {
 	}
 }
 
-void SoundManager::setVolume(int volume) {
-	// TODO: Set sound volume
+void SoundManager::setVolumeSfx(int volume) {
+	ConfMan.setInt("sfx_volume", volume);
+	g_engine->syncSoundSettings();
+}
+
+void SoundManager::setVolumeMusic(int volume) {
+	ConfMan.setInt("music_volume", volume);
+	g_engine->syncSoundSettings();
+}
+
+void SoundManager::setVolumeMaster(int volume) {
+	ConfMan.setInt("sfx_volume", volume);
+	ConfMan.setInt("music_volume", volume);
+	ConfMan.setInt("speech_volume", volume);
+	g_engine->syncSoundSettings();
+}
+
+int SoundManager::getVolumeSfx() const {
+	return ConfMan.getInt("sfx_volume");
+}
+
+int SoundManager::getVolumeMusic() const {
+	return ConfMan.getInt("music_volume");
+}
+
+int SoundManager::getVolumeMaster() const {
+	// Master is the minimum of the channel volumes
+	return MIN(ConfMan.getInt("sfx_volume"), ConfMan.getInt("music_volume"));
 }
 
 bool SoundManager::isPlaying() const {
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index baaa8701cad..4a0f6053e50 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -65,7 +65,14 @@ public:
 	void playSound(byte *soundData, uint32 size, int channel);
 	void stopAllSounds();
 	void stopSound(int channel);
-	void setVolume(int volume);
+
+	void setVolumeSfx(int volume);
+	void setVolumeMusic(int volume);
+	void setVolumeMaster(int volume);
+	int getVolumeSfx() const;
+	int getVolumeMusic() const;
+	int getVolumeMaster() const;
+
 	bool isPlaying() const;
 	bool isPlaying(int channel) const;
 	void loadSoundIndex();


Commit: 9333118fe97e4a372a3d0f5b32d2652a24d630aa
    https://github.com/scummvm/scummvm/commit/9333118fe97e4a372a3d0f5b32d2652a24d630aa
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:32+02:00

Commit Message:
PELROCK: Sounds in menu

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h
    engines/pelrock/sound.cpp


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 4d54c1ea3d2..9291ec3abdd 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -205,28 +205,35 @@ bool MenuManager::checkMouseClick(int x, int y) {
 
 	switch (button) {
 	case QUESTION_MARK_BUTTON:
+		_sound->playSound("56ZZZZZZ.SMP", 0);
 		debug("Show credits");
 		_events->_leftMouseClicked = false;
 		showCredits();
 		break;
 	case INVENTORY_PREV_BUTTON:
+		_sound->playSound("56ZZZZZZ.SMP", 0);
 		if (_curInventoryPage > 0)
 			_curInventoryPage--;
 		break;
 	case INVENTORY_NEXT_BUTTON:
+		_sound->playSound("56ZZZZZZ.SMP", 0);
 		if ((_curInventoryPage + 1) * 4 < g_engine->_state->inventoryItems.size())
 			_curInventoryPage++;
 		break;
 	case SAVE_GAME_BUTTON:
+		_sound->playSound("11ZZZZZZ.SMP", 0);
 		return g_engine->saveGameDialog();
 		break;
 	case LOAD_GAME_BUTTON:
+		_sound->playSound("11ZZZZZZ.SMP", 0);
 		return g_engine->loadGameDialog();
 		break;
 	case EXIT_MENU_BUTTON:
+		_sound->playSound("11ZZZZZZ.SMP", 0);
 		g_engine->quitGame();
 		break;
 	case SOUNDS_BUTTON:
+		_sound->playSound("11ZZZZZZ.SMP", 0);
 		_showSoundOptions = true;
 		_menuText = Common::StringArray();
 		break;
@@ -560,9 +567,6 @@ void MenuManager::drawButtons() {
 	drawSpriteToBuffer(_compositeBuffer, buf, _savesDown.left, _savesDown.top, _savesDown.width(), _savesDown.height(), kTransparentColor);
 
 	if (_showSoundOptions) {
-		// _compositeBuffer.transBlitFrom(_masterSoundIcon, Common::Point(233, 188), kSoundControlsTransparentColor);
-		// _compositeBuffer.transBlitFrom(_musicSoundIcon, Common::Point(299, 188), kSoundControlsTransparentColor);
-		// _compositeBuffer.transBlitFrom(_sfxSoundIcon, Common::Point(365, 188), kSoundControlsTransparentColor);
 		_compositeBuffer.transBlitFrom(_masterSoundIcon, Common::Point(266 - _masterSoundIcon.w / 2, 212 - _masterSoundIcon.h / 2), kSoundControlsTransparentColor);
 		_compositeBuffer.transBlitFrom(_musicSoundIcon, Common::Point(333 - _musicSoundIcon.w / 2, 212 - _musicSoundIcon.h / 2), kSoundControlsTransparentColor);
 		_compositeBuffer.transBlitFrom(_sfxSoundIcon, Common::Point(399 - _sfxSoundIcon.w / 2, 212 - _sfxSoundIcon.h / 2), kSoundControlsTransparentColor);
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 7a543947682..a0293eceb87 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -37,8 +37,7 @@ const uint32 kSoundControlOffset = 3037008;
 const uint32 kSoundMasterOffset = 	2662588;
 const uint32 kSoundMusicOffset = 2664746;
 const uint32 kSoundSfxOffset = 2667140;
-const int kTransparentColor = 15;
-// const int kTransparentColor = 195;
+const int kTransparentColor = 65;
 const int kSoundControlsTransparentColor = 195;
 const uint32 kCreditsBackgroundOffset = 3271454;
 
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 46aa916acc6..151045cd902 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -217,9 +217,6 @@ void SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
 				_mixer->stopHandle(_sfxHandles[channel]);
 				debug("Stopped active sound on channel %d to play new sound %s", channel, sound.filename.c_str());
 			}
-			else {
-				debug("Warning: channel %d is already free when trying to play sound %s", channel, sound.filename.c_str());
-			}
 		}
 		Audio::AudioStream *finalStream = loopCount != -1 ? stream : Audio::makeLoopingAudioStream(stream, 0);
 


Commit: 7aff19c6eafb9d8f1de278df332637388e89e6b3
    https://github.com/scummvm/scummvm/commit/7aff19c6eafb9d8f1de278df332637388e89e6b3
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:32+02:00

Commit Message:
sve thumbnail

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 9291ec3abdd..7386610bf47 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -22,6 +22,7 @@
 #include "common/debug.h"
 #include "common/file.h"
 #include "graphics/paletteman.h"
+#include "graphics/thumbnail.h"
 
 #include "menu.h"
 #include "pelrock/menu.h"
@@ -302,6 +303,10 @@ bool MenuManager::selectInventoryItem(int i) {
 
 void MenuManager::menuLoop() {
 
+	//Save screenshot in case the user saves
+	saveScreenshot();
+	g_engine->_saveAllowed = false;
+
 	g_system->getPaletteManager()->setPalette(_mainMenuPalette, 0, 256);
 	g_engine->changeCursor(DEFAULT);
 	_showSoundOptions = false;
@@ -346,9 +351,15 @@ void MenuManager::menuLoop() {
 	_events->_rightMouseClicked = false;
 	_events->_leftMouseClicked = false;
 	g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
+	g_engine->_saveAllowed = true;
 	cleanUp();
 }
 
+void MenuManager::saveScreenshot() {
+	g_engine->_saveThumbnail.free();
+	Graphics::createThumbnail(g_engine->_saveThumbnail);
+}
+
 void MenuManager::drawScreen() {
 	_compositeBuffer.blitFrom(_mainMenu);
 	if (showButtons)
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index a0293eceb87..8b59bba538d 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -219,6 +219,7 @@ public:
 	MenuManager(Graphics::Screen *screen, PelrockEventManager *events, ResourceManager *res, SoundManager *sound);
 	~MenuManager();
 	void menuLoop();
+	void saveScreenshot();
 	void drawScreen();
 	void drawInventoryIcons();
 	void loadMenu();
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 5677e478f05..742ca30c446 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -179,9 +179,10 @@ public:
 	Graphics::ManagedSurface _compositeBuffer; // Working composition buffer
 	Graphics::ManagedSurface _currentBackground; // Clean background - NEVER modified
 	Graphics::ManagedSurface _bgScreen;
+	Graphics::Surface _saveThumbnail;
 
 	GameStateData *_state = new GameStateData();
-
+	bool _saveAllowed = true;
 	SmallFont *_smallFont = nullptr;
 	LargeFont *_largeFont = nullptr;
 	DoubleSmallFont *_doubleSmallFont = nullptr;
@@ -220,7 +221,7 @@ public:
 		return true;
 	}
 	bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override {
-		return true;
+		return _saveAllowed;
 	}
 
 	/**


Commit: cf6c76d0f58feebc051cc96f46c32b1ec8dd7e0b
    https://github.com/scummvm/scummvm/commit/cf6c76d0f58feebc051cc96f46c32b1ec8dd7e0b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:33+02:00

Commit Message:
PELROCK: Saves games with the right thumbnail

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/metaengine.cpp
    engines/pelrock/metaengine.h
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 7386610bf47..950104fb8ab 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -305,7 +305,7 @@ void MenuManager::menuLoop() {
 
 	//Save screenshot in case the user saves
 	saveScreenshot();
-	g_engine->_saveAllowed = false;
+	g_engine->_autoSaveAllowed = false;
 
 	g_system->getPaletteManager()->setPalette(_mainMenuPalette, 0, 256);
 	g_engine->changeCursor(DEFAULT);
@@ -342,7 +342,6 @@ void MenuManager::menuLoop() {
 			break;
 		}
 		drawScreen();
-		drawPaletteSquares((byte *)_screen->getPixels(), _mainMenuPalette);
 		_screen->markAllDirty();
 		_screen->update();
 		g_system->delayMillis(10);
@@ -351,7 +350,7 @@ void MenuManager::menuLoop() {
 	_events->_rightMouseClicked = false;
 	_events->_leftMouseClicked = false;
 	g_system->getPaletteManager()->setPalette(g_engine->_room->_roomPalette, 0, 256);
-	g_engine->_saveAllowed = true;
+	g_engine->_autoSaveAllowed = true;
 	cleanUp();
 }
 
diff --git a/engines/pelrock/metaengine.cpp b/engines/pelrock/metaengine.cpp
index 11bff334e70..f659918398e 100644
--- a/engines/pelrock/metaengine.cpp
+++ b/engines/pelrock/metaengine.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/translation.h"
+#include "graphics/thumbnail.h"
 
 #include "pelrock/metaengine.h"
 #include "pelrock/detection.h"
@@ -68,6 +69,15 @@ Common::Error PelrockMetaEngine::createInstance(OSystem *syst, Engine **engine,
 	return Common::kNoError;
 }
 
+void PelrockMetaEngine::getSavegameThumbnail(Graphics::Surface &thumb) {
+	Pelrock::PelrockEngine *engine = static_cast<Pelrock::PelrockEngine *>(g_engine);
+	if (engine && engine->_saveThumbnail.getPixels()) {
+		thumb.copyFrom(engine->_saveThumbnail);
+	} else {
+		Graphics::createThumbnail(thumb);
+	}
+}
+
 bool PelrockMetaEngine::hasFeature(MetaEngineFeature f) const {
 	return checkExtendedSaves(f) ||
 		(f == kSupportsLoadingDuringStartup);
diff --git a/engines/pelrock/metaengine.h b/engines/pelrock/metaengine.h
index d1649be567f..596f967bf63 100644
--- a/engines/pelrock/metaengine.h
+++ b/engines/pelrock/metaengine.h
@@ -23,6 +23,7 @@
 #define PELROCK_METAENGINE_H
 
 #include "engines/advancedDetector.h"
+#include "graphics/surface.h"
 
 class PelrockMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
 public:
@@ -38,6 +39,8 @@ public:
 	bool hasFeature(MetaEngineFeature f) const override;
 
 	const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override;
+
+	void getSavegameThumbnail(Graphics::Surface &thumb) override;
 };
 
 #endif // PELROCK_METAENGINE_H
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 742ca30c446..79044d61c1c 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -182,7 +182,7 @@ public:
 	Graphics::Surface _saveThumbnail;
 
 	GameStateData *_state = new GameStateData();
-	bool _saveAllowed = true;
+	bool _autoSaveAllowed = true;
 	SmallFont *_smallFont = nullptr;
 	LargeFont *_largeFont = nullptr;
 	DoubleSmallFont *_doubleSmallFont = nullptr;
@@ -221,7 +221,11 @@ public:
 		return true;
 	}
 	bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override {
-		return _saveAllowed;
+		return true;
+	}
+
+	bool canSaveAutosaveCurrently() override {
+		return _autoSaveAllowed;
 	}
 
 	/**


Commit: 48b3c9d8d9e6aa93e191d2dea6fc68c3dfe04b15
    https://github.com/scummvm/scummvm/commit/48b3c9d8d9e6aa93e191d2dea6fc68c3dfe04b15
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:33+02:00

Commit Message:
PELROCK: First implementation of sliding screensaver

Changed paths:
  A engines/pelrock/slidingpuzzle.cpp
  A engines/pelrock/slidingpuzzle.h
    engines/pelrock/events.cpp
    engines/pelrock/events.h
    engines/pelrock/module.mk
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/events.cpp b/engines/pelrock/events.cpp
index c90705e7c06..2c3352b1962 100644
--- a/engines/pelrock/events.cpp
+++ b/engines/pelrock/events.cpp
@@ -42,22 +42,14 @@ void PelrockEventManager::pollEvent() {
 		case Common::EVENT_QUIT:
 		case Common::EVENT_RETURN_TO_LAUNCHER:
 			return;
-
-		// case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
-		// 	// handle action
-		// 	handleKey(_event);
-		// 	break;
-
-		// case Common::EVENT_CUSTOM_ENGINE_ACTION_END:
-		// 	break;
 		case Common::EVENT_KEYDOWN:
 			changeGameSpeed(_event);
-			// _keyPressed = true;
 			_lastKeyEvent = _event.kbd.keycode;
+			_isKeydown = true;
 			break;
-		// 	return;
-		// case Common::EVENT_KEYUP:
-		// 	return;
+		case Common::EVENT_KEYUP:
+			_isKeydown = false;
+			return;
 		case Common::EVENT_LBUTTONDOWN:
 			if (_leftMouseButton == 0) {
 				_clickTime = g_system->getMillis();
@@ -68,10 +60,7 @@ void PelrockEventManager::pollEvent() {
 			break;
 		case Common::EVENT_LBUTTONUP:
 			if (_leftMouseButton == 1) {
-				// Don't treat as regular click if we're in popup selection mode
-				// if (!_popupSelectionMode) {
 				_leftMouseClicked = true;
-				// }
 				_releaseX = _event.mouse.x;
 				_releaseY = _event.mouse.y;
 				_longClicked = false;
diff --git a/engines/pelrock/events.h b/engines/pelrock/events.h
index 6b2c54a700c..84b86b8ec4a 100644
--- a/engines/pelrock/events.h
+++ b/engines/pelrock/events.h
@@ -43,6 +43,7 @@ public:
 	bool _popupSelectionMode = false;
 	bool _leftMouseButton = false;
 	bool _rightMouseButton = false;
+	bool _isKeydown = false;
 	Common::KeyCode _lastKeyEvent = Common::KEYCODE_INVALID;
 	PelrockEventManager();
 	void pollEvent();
diff --git a/engines/pelrock/module.mk b/engines/pelrock/module.mk
index 8f1fa08902b..32053f15ecd 100644
--- a/engines/pelrock/module.mk
+++ b/engines/pelrock/module.mk
@@ -22,6 +22,7 @@ MODULE_OBJS = \
 	graphics.o \
 	saveload.o \
 	spellbook.o \
+	slidingpuzzle.o \
 	cdplayer.o \
 	backgroundbook.o
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index a0f4c703346..6ac79b31174 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -42,6 +42,7 @@
 #include "pelrock/offsets.h"
 #include "pelrock/pathfinding.h"
 #include "pelrock/pelrock.h"
+#include "pelrock/slidingpuzzle.h"
 #include "pelrock/util.h"
 
 namespace Pelrock {
@@ -1512,11 +1513,16 @@ void PelrockEngine::gameLoop() {
 	}
 
 	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_k) {
+		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
 		credits();
 	}
+	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_v) {
+		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
+		SlidingPuzzle puzzle(_events, _sound);
+		puzzle.run();
+	}
 
 	renderScene();
-
 	_screen->update();
 }
 
diff --git a/engines/pelrock/slidingpuzzle.cpp b/engines/pelrock/slidingpuzzle.cpp
new file mode 100644
index 00000000000..30ef21061f1
--- /dev/null
+++ b/engines/pelrock/slidingpuzzle.cpp
@@ -0,0 +1,272 @@
+/* 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 "common/system.h"
+#include "graphics/paletteman.h"
+
+#include "pelrock/slidingpuzzle.h"
+#include "pelrock/pelrock.h"
+#include "pelrock/events.h"
+#include "pelrock/room.h"
+#include "pelrock/sound.h"
+
+namespace Pelrock {
+
+byte SlidingPuzzle::_sizeIndex = 0;
+
+SlidingPuzzle::SlidingPuzzle(PelrockEventManager *eventMan, SoundManager *sound)
+	: _events(eventMan),
+	  _sound(sound),
+	  _tileSize(0),
+	  _gridWidth(0),
+	  _gridHeight(0),
+	  _totalTiles(0),
+	  _emptyPos(-1),
+	  _tileMap(nullptr) {
+}
+
+SlidingPuzzle::~SlidingPuzzle() {
+	delete[] _tileMap;
+	_puzzleBuffer.free();
+}
+
+void SlidingPuzzle::run() {
+	// calculate grid
+	_tileSize   = kTileSizes[_sizeIndex];
+	_gridWidth  = kPuzzleScreenWidth / _tileSize;
+	_gridHeight = kPuzzleScreenHeight / _tileSize;
+	_totalTiles = _gridWidth * _gridHeight;
+
+	initGrid();
+
+	// Copy the current scene into our working buffer.
+	_puzzleBuffer.create(kPuzzleScreenWidth, kPuzzleScreenHeight, Graphics::PixelFormat::createFormatCLUT8());
+	_puzzleBuffer.copyFrom(g_engine->_compositeBuffer);
+
+	drawGridLines();
+	present();
+
+	_events->_lastKeyEvent = Common::KEYCODE_INVALID;
+
+	//Shuffle
+	shuffleLoop();
+
+	// Guarantee the puzzle is solvable, fix it otherwise
+	ensureSolvable();
+
+	// Remove the last tile to create the empty slot (tile_count = total - 1).
+	_emptyPos = _totalTiles - 1;
+	fillEmptyTile(_emptyPos);
+
+	g_engine->changeCursor(DEFAULT);
+	present();
+
+	puzzleLoop();
+
+	// Cycle to the next tile size for the next activation.
+	_sizeIndex = (_sizeIndex + 1) % kNumPuzzleSizes;
+}
+
+// After shuffling, ensure the resulting permutation is reachable from the solved state.
+// For all our grid widths (8, 16, 32, 64 — all even) with the blank forced to the
+// bottom-right corner (row-from-bottom = 0), the solvability condition is:
+//   inversions in _tileMap[0 .. N-2] must be even.
+// Each tile swap flips parity, so if we end up with an odd count, one extra swap fixes it.
+void SlidingPuzzle::ensureSolvable() {
+	int inversions = 0;
+	for (int i = 0; i < _totalTiles - 1; i++) {
+		for (int j = i + 1; j < _totalTiles - 1; j++) {
+			if (_tileMap[i] > _tileMap[j])
+				inversions++;
+		}
+	}
+	if (inversions & 1)
+		swapTiles(0, 1);
+}
+
+// Set up the identity permutation: tile i is in slot i.
+void SlidingPuzzle::initGrid() {
+	_tileMap = new uint16[_totalTiles];
+	for (uint16 i = 0; i < _totalTiles; i++)
+		_tileMap[i] = (uint16)i;
+}
+
+/**
+ * Original draws grid lines to make tile separation evident.
+ */
+void SlidingPuzzle::drawGridLines() {
+	for (int col = 0; col < _gridWidth; col++) {
+		int x = col * _tileSize;
+		_puzzleBuffer.drawLine(x, 0, x, kPuzzleScreenHeight - 1, kEmptyTileColor);
+	}
+	for (int row = 0; row < _gridHeight; row++) {
+		int y = row * _tileSize;
+		_puzzleBuffer.drawLine(0, y, kPuzzleScreenWidth - 1, y, kEmptyTileColor);
+	}
+}
+
+void SlidingPuzzle::swapTiles(int a, int b) {
+	SWAP(_tileMap[a], _tileMap[b]);
+
+	// Pixel coordinates: skip the 1px grid line at top/left of each tile.
+	int ax = (a % _gridWidth) * _tileSize + 1;
+	int ay = (a / _gridWidth) * _tileSize + 1;
+	int bx = (b % _gridWidth) * _tileSize + 1;
+	int by = (b / _gridWidth) * _tileSize + 1;
+	int inner = _tileSize - 1;
+
+	for (int row = 0; row < inner; row++) {
+		byte *pA = (byte *)_puzzleBuffer.getBasePtr(ax, ay + row);
+		byte *pB = (byte *)_puzzleBuffer.getBasePtr(bx, by + row);
+		for (int col = 0; col < inner; col++)
+			SWAP(pA[col], pB[col]);
+	}
+}
+
+void SlidingPuzzle::fillEmptyTile(int pos) {
+	int ex = (pos % _gridWidth) * _tileSize + 1;
+	int ey = (pos / _gridWidth) * _tileSize + 1;
+	int inner = _tileSize - 1;
+	_puzzleBuffer.fillRect(Common::Rect(ex, ey, ex + inner, ey + inner), kEmptyTileColor);
+}
+
+void SlidingPuzzle::present() {
+	g_engine->_screen->blitFrom(_puzzleBuffer);
+	g_engine->_screen->markAllDirty();
+	g_engine->_screen->update();
+}
+
+void SlidingPuzzle::playTileSound() {
+	_sound->playSound("11ZZZZZZ.SMP", -1);
+}
+
+void SlidingPuzzle::shuffleLoop() {
+	const int shuffleRange = _totalTiles - 1; // never touch the blank slot (N-1)
+	while (!g_engine->shouldQuit()) {
+		_events->pollEvent();
+
+		int a, b;
+		do {
+			a = g_engine->getRandomNumber(shuffleRange - 1);
+			b = g_engine->getRandomNumber(shuffleRange - 1);
+		} while (a == b);
+
+		swapTiles(a, b);
+		present();
+		playTileSound();
+
+		if (_events->_lastKeyEvent != Common::KEYCODE_INVALID) {
+			_events->_lastKeyEvent = Common::KEYCODE_INVALID;
+			break;
+		}
+		g_system->delayMillis(10);
+	}
+}
+
+void SlidingPuzzle::puzzleLoop() {
+	_events->_lastKeyEvent = Common::KEYCODE_INVALID;
+	_events->_isKeydown = false;
+
+	while (!g_engine->shouldQuit()) {
+		_events->pollEvent();
+
+		if (_events->_leftMouseClicked) {
+			_events->_leftMouseClicked = false;
+			if (handleClick(_events->_mouseClickX, _events->_mouseClickY))
+				break;
+		}
+
+		if (_events->_lastKeyEvent == Common::KEYCODE_ESCAPE) {
+			break;
+		}
+
+		// While any key is held, show the original (unscrambled) background.
+		if (_events->_isKeydown && _events->_lastKeyEvent != Common::KEYCODE_ESCAPE) {
+			showOriginalImage();
+		}
+		_events->_lastKeyEvent = Common::KEYCODE_INVALID;
+
+		present();
+		g_system->delayMillis(10);
+	}
+}
+
+bool SlidingPuzzle::handleClick(int screenX, int screenY) {
+	int col = screenX / _tileSize;
+	int row = screenY / _tileSize;
+	if (col >= _gridWidth || row >= _gridHeight)
+		return false;
+
+	int clicked = row * _gridWidth + col;
+	if (clicked == _emptyPos)
+		return false;
+
+	// Check adjacency without row wrap-around.
+	int emptyRow = _emptyPos / _gridWidth;
+	int emptyCol = _emptyPos % _gridWidth;
+	bool adjacent = (row == emptyRow && abs(col - emptyCol) == 1) ||
+	                (col == emptyCol && abs(row - emptyRow) == 1);
+	if (!adjacent)
+		return false;
+
+	swapTiles(clicked, _emptyPos);
+	_emptyPos = clicked;
+	fillEmptyTile(_emptyPos);
+	playTileSound();
+	present();
+
+	if (isSolved()) {
+		_sound->playSound("CHIQUITO.WAV", 3);
+		while (!g_engine->shouldQuit() && _sound->isPlaying(3)) {
+			_events->pollEvent();
+			g_system->delayMillis(10);
+		}
+		_sound->stopSound(3);
+		return true;
+	}
+
+	return false;
+}
+
+bool SlidingPuzzle::isSolved() const {
+	for (int i = 0; i < _totalTiles - 1; i++) {
+		if (_tileMap[i] != (uint16)i)
+			return false;
+	}
+	return true;
+}
+
+// show the original background while a key is held.
+void SlidingPuzzle::showOriginalImage() {
+	// original game showed only the room background
+	g_engine->_screen->blitFrom(g_engine->_compositeBuffer);
+	g_engine->_screen->markAllDirty();
+	g_engine->_screen->update();
+
+	while (!g_engine->shouldQuit()) {
+		_events->pollEvent();
+		if (_events->_isKeydown == false)
+			break;
+		g_system->delayMillis(10);
+	}
+}
+
+} // End of namespace Pelrock
diff --git a/engines/pelrock/slidingpuzzle.h b/engines/pelrock/slidingpuzzle.h
new file mode 100644
index 00000000000..fb7b4d3c20e
--- /dev/null
+++ b/engines/pelrock/slidingpuzzle.h
@@ -0,0 +1,79 @@
+/* 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 PELROCK_SLIDING_PUZZLE_H
+#define PELROCK_SLIDING_PUZZLE_H
+
+#include "common/scummsys.h"
+#include "graphics/managed_surface.h"
+
+#include "pelrock/events.h"
+#include "pelrock/sound.h"
+
+namespace Pelrock {
+
+static const int kPuzzleScreenWidth = 640;
+static const int kPuzzleScreenHeight = 400;
+static const int kNumPuzzleSizes = 4;
+static const int kTileSizes[kNumPuzzleSizes] = {80, 40, 20, 10};
+static const byte kEmptyTileColor = 0x0F;
+
+class SlidingPuzzle {
+public:
+	SlidingPuzzle(PelrockEventManager *eventMan, SoundManager *sound);
+	~SlidingPuzzle();
+
+	void run();
+
+	// Persisted across activations so the size cycles each time.
+	static byte _sizeIndex;
+
+private:
+	PelrockEventManager *_events;
+	SoundManager *_sound;
+
+	void playTileSound();
+
+	int _tileSize;
+	int _gridWidth;
+	int _gridHeight;
+	int _totalTiles;       // gridWidth * gridHeight`
+	int _emptyPos;         // index of the empty (removed) tile
+	uint16 *_tileMap;      // logical position -> original tile index
+
+	Graphics::ManagedSurface _puzzleBuffer;
+
+	void initGrid();
+	void drawGridLines();
+	void swapTiles(int a, int b);
+	void fillEmptyTile(int pos);
+	void shuffleLoop();
+	void ensureSolvable();
+	void puzzleLoop();
+	bool handleClick(int screenX, int screenY);
+	bool isSolved() const;
+	void showOriginalImage();
+	void present();
+};
+
+} // End of namespace Pelrock
+
+#endif // PELROCK_SLIDING_PUZZLE_H


Commit: 9e1917d9f0fbdde38cdca01bc1dc882a46a2c44e
    https://github.com/scummvm/scummvm/commit/9e1917d9f0fbdde38cdca01bc1dc882a46a2c44e
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:33+02:00

Commit Message:
PELROCK: Enables fake teeth animation in room 3, increases amount of random responses

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/graphics.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/slidingpuzzle.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 28762c91d5e..37553d3022b 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -21,7 +21,7 @@
 
 #include "graphics/paletteman.h"
 
-
+#include "pelrock.h"
 #include "pelrock/actions.h"
 #include "pelrock/backgroundbook.h"
 #include "pelrock/cdplayer.h"
@@ -746,8 +746,7 @@ void PelrockEngine::noOpItem(int item, HotSpot *hotspot) {
 	}
 	debug("No-op item %d with hotspot %d", item, hotspot->extra);
 	_alfredState.direction = ALFRED_DOWN;
-	byte response = (byte)getRandomNumber(12);
-	_dialog->say(_res->_ingameTexts[154 + response]);
+	sayRandomIncorrectResponse();
 }
 
 void PelrockEngine::openRoomDoor(HotSpot *hotspot) {
@@ -950,6 +949,7 @@ void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
 	_room->addStickerToRoom(_room->_currentRoomNumber, 10, PERSIST_PERM);
 	_room->disableHotspot(_room->findHotspotByExtra(295)); // Disable storefront hotspot
 	_room->disableHotspot(_room->findHotspotByExtra(294)); // Disable window hotspot
+	_room->enableSprite(5, 100, PERSIST_PERM); // Enable fake teeth sprite
 	_disableAction = true;                                 // Prevent player from doing anything until they move Alfred
 	walkTo(630, _alfredState.y);
 }
@@ -2184,8 +2184,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 				_dialog->say(_res->_ingameTexts[NOTENGOPEGAMENTO]);
 			}
 		} else {
-			byte response = (byte)getRandomNumber(12);
-			_dialog->say(_res->_ingameTexts[154 + response]);
+			sayRandomIncorrectResponse();
 		}
 		break;
 	}
@@ -2226,13 +2225,16 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 			return;
 		}
 
-		byte response = (byte)getRandomNumber(12);
-		_dialog->say(_res->_ingameTexts[154 + response]);
+		sayRandomIncorrectResponse();
 		break;
 	}
 	}
 }
 
+void PelrockEngine::sayRandomIncorrectResponse() {
+	byte response = (byte)getRandomNumber(15);
+	_dialog->say(_res->_ingameTexts[154 + response]);
+}
 void PelrockEngine::chooseCorrectDoor() {
 	playAlfredSpecialAnim(1);
 	byte puertaBuena = _state->getFlag(FLAG_PUERTA_BUENA);
diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index 279fe2f2622..6b7c7c325cb 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -232,7 +232,7 @@ void GraphicsManager::copyBackgroundToBuffer() {
 
 void GraphicsManager::presentFrame() {
 	g_engine->_screen->blitFrom(g_engine->_compositeBuffer);
-	// paintDebugLayer();
+	g_engine->paintDebugLayer();
 	g_engine->_screen->markAllDirty();
 }
 
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 79044d61c1c..f492898c3fe 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -118,7 +118,6 @@ private:
 	void checkMouseHover();
 	void checkMouseClick(int x, int y);
 	void checkLongMouseClick(int x, int y);
-	void paintDebugLayer();
 
 	// walking
 	int _currentStep = 0;
@@ -258,6 +257,7 @@ public:
 	void maybePlayPostIntro();
 	void shakeEffect();
 	void handleFlightRoomFrame();
+	void paintDebugLayer();
 
 	void passerByAnim(uint32 frameCount);
 	void changeCursor(Cursor cursor);
@@ -311,6 +311,7 @@ public:
 	void noOpAction(HotSpot *hotspot);
 	void noOpItem(int item, HotSpot *hotspot);
 	void useOnAlfred(int inventoryObject);
+	void sayRandomIncorrectResponse();
 	void chooseCorrectDoor();
 	void useCardWithATM(int inventoryObject, HotSpot *hotspot);
 	void useBrickWithWindow(int inventoryObject, HotSpot *hotspot);
diff --git a/engines/pelrock/slidingpuzzle.cpp b/engines/pelrock/slidingpuzzle.cpp
index 30ef21061f1..31aacc04e9c 100644
--- a/engines/pelrock/slidingpuzzle.cpp
+++ b/engines/pelrock/slidingpuzzle.cpp
@@ -256,7 +256,7 @@ bool SlidingPuzzle::isSolved() const {
 
 // show the original background while a key is held.
 void SlidingPuzzle::showOriginalImage() {
-	// original game showed only the room background
+	// original game showed only the room background, we show the entire scene
 	g_engine->_screen->blitFrom(g_engine->_compositeBuffer);
 	g_engine->_screen->markAllDirty();
 	g_engine->_screen->update();


Commit: a7ded2599447685b393c278b8cb5b5bf024ad5dd
    https://github.com/scummvm/scummvm/commit/a7ded2599447685b393c278b8cb5b5bf024ad5dd
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:33+02:00

Commit Message:
PELROCK: Fixes for interaction with the statue in room 7

Changed paths:
    engines/pelrock/actions.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 37553d3022b..6c4e09bb0f3 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -204,7 +204,7 @@ const CombinationEntry combinationTable[] = {
 	{1, 309, &PelrockEngine::showIdToGuard},
 	{5, 309, &PelrockEngine::giveMoneyToGuard},
 	{7, 353, &PelrockEngine::useAmuletWithStatue},
-	{8, 353, &PelrockEngine::useSecretCodeWithStatue},
+	{8, 347, &PelrockEngine::useSecretCodeWithStatue},
 	{8, 358, &PelrockEngine::giveSecretCodeToLibrarian},
 	{4, 358, &PelrockEngine::useBrickWithLibrarian}, // Any hotspot in the lamppost will work
 	{76, 469, &PelrockEngine::usePumpkinWithRiver},
@@ -1128,13 +1128,16 @@ void PelrockEngine::useAmuletWithStatue(int inventoryObject, HotSpot *hotspot) {
 		walkTo(statueHotspot->x + statueHotspot->w / 2, statueHotspot->y + statueHotspot->h);
 		_sound->playSound(_room->_roomSfx[0]); // Magic sound
 		animateStatuePaletteFade(false);
+		_dialog->say(_res->_ingameTexts[ANDA]);
 		walkAndAction(statueHotspot, TALK);
 		waitForActionEnd();
 	}
 }
 
 void PelrockEngine::useSecretCodeWithStatue(int inventoryObject, HotSpot *hotspot) {
-	_dialog->say(_res->_ingameTexts[NOESAMIAQUIENDEBES], 1);
+	_dialog->say(_res->_ingameTexts[NOESAMIAQUIENDEBES]);
+	_dialog->say(_res->_ingameTexts[AQUIENENTONCES]);
+	_dialog->say(_res->_ingameTexts[LIBROSSECRETOS]);
 }
 
 void PelrockEngine::pickUpLetter(HotSpot *hotspot) {


Commit: c507068bb2e4f85bf05514d31a668e58154a633f
    https://github.com/scummvm/scummvm/commit/c507068bb2e4f85bf05514d31a668e58154a633f
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:34+02:00

Commit Message:
PELROCK: Refactors main menu and adds confirmation upon exit

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 950104fb8ab..6529c439525 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/file.h"
 #include "graphics/paletteman.h"
@@ -83,7 +84,37 @@ static void rebuildSoundIcon(Graphics::ManagedSurface &target, const byte *sourc
 	delete scaled;
 }
 
-MenuButton MenuManager::isButtonClicked(int x, int y) {
+SoundMenuButton MenuManager::isSoundMenuButtonUnder(int x, int y) {
+	if (_menuState != SOUND) {
+		return NO_SOUND_BUTTON;
+	}
+	if (_masterVolumeLeftRect.contains(x, y)) {
+		return MASTER_LEFT_BUTTON;
+	}
+	if (_masterVolumeRightRect.contains(x, y)) {
+		return MASTER_RIGHT_BUTTON;
+	}
+	if (_sfxVolumeLeftRect.contains(x, y)) {
+		return SFX_LEFT_BUTTON;
+	}
+	if (_sfxVolumeRightRect.contains(x, y)) {
+		return SFX_RIGHT_BUTTON;
+	}
+	if (_musicVolumeLeftRect.contains(x, y)) {
+		return MUSIC_LEFT_BUTTON;
+	}
+	if (_musicVolumeRightRect.contains(x, y)) {
+		return MUSIC_RIGHT_BUTTON;
+	}
+	return NO_SOUND_BUTTON; // Default fallback
+}
+
+MainMenuButton MenuManager::isMainMenuButtonUnder(int x, int y) {
+	if (_menuState != MAIN_MENU) {
+		debug("Not checking for main menu buttons because menu state is not MAIN_MENU");
+		return NO_MAIN_BUTTON;
+	}
+
 	if (_questionMarkRect.contains(x, y)) {
 		return QUESTION_MARK_BUTTON;
 	}
@@ -111,34 +142,14 @@ MenuButton MenuManager::isButtonClicked(int x, int y) {
 	if (_savesDown.contains(x, y)) {
 		return SAVEGAME_NEXT_BUTTON;
 	}
-	if (_showSoundOptions) {
-		if (_masterVolumeLeftRect.contains(x, y)) {
-			return MASTER_LEFT_BUTTON;
-		}
-		if (_masterVolumeRightRect.contains(x, y)) {
-			return MASTER_RIGHT_BUTTON;
-		}
-		if (_sfxVolumeLeftRect.contains(x, y)) {
-			return SFX_LEFT_BUTTON;
-		}
-		if (_sfxVolumeRightRect.contains(x, y)) {
-			return SFX_RIGHT_BUTTON;
-		}
-		if (_musicVolumeLeftRect.contains(x, y)) {
-			return MUSIC_LEFT_BUTTON;
-		}
-		if (_musicVolumeRightRect.contains(x, y)) {
-			return MUSIC_RIGHT_BUTTON;
-		}
-	}
-	return NO_BUTTON; // Default fallback
+	return NO_MAIN_BUTTON; // Default fallback
 }
 
 void MenuManager::checkMouseDown(int x, int y) {
-	if(!_events->_leftMouseButton) {
+	if (!_events->_leftMouseButton) {
 		return;
 	}
-	MenuButton b = isButtonClicked(x, y);
+	SoundMenuButton b = isSoundMenuButtonUnder(x, y);
 	switch (b) {
 	case MASTER_LEFT_BUTTON:
 		_masterVolumeLevel = MAX(0, _masterVolumeLevel - 1);
@@ -176,38 +187,55 @@ void MenuManager::checkMouseDown(int x, int y) {
 }
 
 bool MenuManager::checkMouseClick(int x, int y) {
+	debug("Checking mouse click at %d, %d, with menu state %d", x, y, _menuState);
 
-	bool selectedItem = false;
-	for (int i = 0; i < 4; i++) {
-
-		Common::Rect itemRect = Common::Rect(_inventorySlots[i], 60, 60);
-
-		if (itemRect.contains(x, y)) {
-			selectedItem = selectInventoryItem(i);
-			return false;
-		}
+	switch (_menuState) {
+	case MAIN_MENU: {
+		return checkMainMenuMouse(x, y);
+		break;
+	}
+	case SOUND: {
+		checkSoundMenuClick(x, y);
+		break;
 	}
+	case ORIGINAL_SAVE: {
 
+		break;
+	}
+	case ORIGINAL_LOAD: {
+		break;
+	}
+	case EXIT_GAME: {
+		break;
+	}
+	}
+	return false;
+}
 
-	MenuButton button = isButtonClicked(x, y);
+void MenuManager::checkSoundMenuClick(int x, int y) {
+	SoundMenuButton soundMenuButton = isSoundMenuButtonUnder(x, y);
+
+	if (soundMenuButton != MUSIC_LEFT_BUTTON &&
+		soundMenuButton != MUSIC_RIGHT_BUTTON &&
+		soundMenuButton != SFX_LEFT_BUTTON &&
+		soundMenuButton != SFX_RIGHT_BUTTON &&
+		soundMenuButton != MASTER_LEFT_BUTTON &&
+		soundMenuButton != MASTER_RIGHT_BUTTON) {
+		_menuState = MAIN_MENU;
+		_menuText = _menuTexts[0];
+	}
 
-	if (button != MUSIC_LEFT_BUTTON &&
-		button != MUSIC_RIGHT_BUTTON &&
-		button != SFX_LEFT_BUTTON &&
-		button != SFX_RIGHT_BUTTON &&
-		button != MASTER_LEFT_BUTTON &&
-		button != MASTER_RIGHT_BUTTON) {
-		_showSoundOptions = false;
-		if (!selectedItem) {
-			_selectedInvIndex = -1;
-			_menuText = _menuTexts[0];
-		}
+	if (soundMenuButton == SFX_LEFT_BUTTON || soundMenuButton == SFX_RIGHT_BUTTON) {
+		_sound->playSound("CAT_1ZZZ.SMP", -1);
 	}
+}
 
-	switch (button) {
+bool MenuManager::checkMainMenuMouse(int x, int y) {
+	debug("Checking main menu buttons");
+	MainMenuButton mainMenuButton = isMainMenuButtonUnder(x, y);
+	switch (mainMenuButton) {
 	case QUESTION_MARK_BUTTON:
 		_sound->playSound("56ZZZZZZ.SMP", 0);
-		debug("Show credits");
 		_events->_leftMouseClicked = false;
 		showCredits();
 		break;
@@ -223,27 +251,32 @@ bool MenuManager::checkMouseClick(int x, int y) {
 		break;
 	case SAVE_GAME_BUTTON:
 		_sound->playSound("11ZZZZZZ.SMP", 0);
-		return g_engine->saveGameDialog();
+		if (ConfMan.getBool("original_menus") == true) {
+			_menuState = ORIGINAL_SAVE;
+			_menuText = Common::StringArray();
+		} else {
+			return g_engine->saveGameDialog();
+		}
 		break;
 	case LOAD_GAME_BUTTON:
 		_sound->playSound("11ZZZZZZ.SMP", 0);
-		return g_engine->loadGameDialog();
+		if (ConfMan.getBool("original_menus") == true) {
+			_menuState = ORIGINAL_LOAD;
+			_menuText = Common::StringArray();
+		} else {
+			return g_engine->loadGameDialog();
+		}
 		break;
 	case EXIT_MENU_BUTTON:
 		_sound->playSound("11ZZZZZZ.SMP", 0);
-		g_engine->quitGame();
+		_menuState = EXIT_GAME;
+		// g_engine->quitGame();
 		break;
 	case SOUNDS_BUTTON:
 		_sound->playSound("11ZZZZZZ.SMP", 0);
-		_showSoundOptions = true;
+		_menuState = SOUND;
 		_menuText = Common::StringArray();
 		break;
-	case SFX_LEFT_BUTTON:
-		_sound->playSound("CAT_1ZZZ.SMP", -1);
-		break;
-	case SFX_RIGHT_BUTTON:
-		_sound->playSound("CAT_1ZZZ.SMP", -1);
-		break;
 	default:
 		break;
 	}
@@ -303,18 +336,18 @@ bool MenuManager::selectInventoryItem(int i) {
 
 void MenuManager::menuLoop() {
 
-	//Save screenshot in case the user saves
+	// Save screenshot in case the user saves
 	saveScreenshot();
 	g_engine->_autoSaveAllowed = false;
 
 	g_system->getPaletteManager()->setPalette(_mainMenuPalette, 0, 256);
 	g_engine->changeCursor(DEFAULT);
-	_showSoundOptions = false;
+	_menuState = MAIN_MENU;
 	_menuText = _menuTexts[0];
 
 	// Initialize volume levels from current settings
-	_sfxVolumeLevel    = mixerVolumeToLevel(_sound->getVolumeSfx());
-	_musicVolumeLevel  = mixerVolumeToLevel(_sound->getVolumeMusic());
+	_sfxVolumeLevel = mixerVolumeToLevel(_sound->getVolumeSfx());
+	_musicVolumeLevel = mixerVolumeToLevel(_sound->getVolumeMusic());
 	_masterVolumeLevel = mixerVolumeToLevel(_sound->getVolumeMaster());
 
 	debug("Initial master volume level: %d", _masterVolumeLevel);
@@ -333,14 +366,24 @@ void MenuManager::menuLoop() {
 		_events->pollEvent();
 		checkMouseDown(_events->_mouseX, _events->_mouseY);
 		if (_events->_leftMouseClicked) {
+			_events->_leftMouseClicked = false;
 			if (checkMouseClick(_events->_mouseX, _events->_mouseY)) {
 				break;
 			}
-			_events->_leftMouseClicked = false;
 		}
 		if (_events->_rightMouseClicked) {
 			break;
 		}
+		if (_menuState == EXIT_GAME) {
+			if (_events->_lastKeyEvent == Common::KEYCODE_s) {
+				g_engine->quitGame();
+			} else if (_events->_lastKeyEvent == Common::KEYCODE_n) {
+				_menuState = MAIN_MENU;
+				_menuText = _menuTexts[0];
+			}
+			_events->_lastKeyEvent = Common::KEYCODE_INVALID;
+		}
+
 		drawScreen();
 		_screen->markAllDirty();
 		_screen->update();
@@ -480,7 +523,6 @@ void MenuManager::loadMenu() {
 	extractSingleFrame(soundIconMusicData, _soundControlMusicIcon, 0, 66, 64);
 	delete[] soundIconMusicData;
 
-
 	_menuText = _menuTexts[0];
 	alfred7.close();
 
@@ -542,11 +584,42 @@ void MenuManager::cleanUp() {
 }
 
 void MenuManager::drawButtons() {
-	MenuButton button = NO_BUTTON;
+	// always draw main buttons
+	drawMainButtons();
+
+	switch (_menuState) {
+	case MAIN_MENU:
+		break;
+	case ORIGINAL_SAVE:
+		drawSaves();
+		break;
+	case ORIGINAL_LOAD:
+		drawSaves();
+		break;
+	case SOUND:
+		drawSoundControls();
+		break;
+	case EXIT_GAME:
+		drawConfirmation();
+		break;
+	}
+}
+
+void MenuManager::drawConfirmation() {
+	_menuText = _menuTexts[4];
+}
+
+void MenuManager::drawSaves() {
+}
+
+void MenuManager::drawMainButtons() {
+	MainMenuButton button = NO_MAIN_BUTTON;
 	if (_events->_leftMouseButton != 0) {
-		button = isButtonClicked(_events->_mouseX, _events->_mouseY);
+		button = isMainMenuButtonUnder(_events->_mouseX, _events->_mouseY);
 	}
-	byte *buf = button == QUESTION_MARK_BUTTON ? _questionMark[1] : _questionMark[0];
+
+	byte *buf;
+	buf = button == QUESTION_MARK_BUTTON ? _questionMark[1] : _questionMark[0];
 	drawSpriteToBuffer(_compositeBuffer, buf, _questionMarkRect.left, _questionMarkRect.top, _questionMarkRect.width(), _questionMarkRect.height(), kTransparentColor);
 
 	buf = button == INVENTORY_PREV_BUTTON ? _inventoryLeftArrow[1] : _inventoryLeftArrow[0];
@@ -575,25 +648,30 @@ void MenuManager::drawButtons() {
 
 	buf = button == SAVEGAME_NEXT_BUTTON ? _savesDownArrows[1] : _savesDownArrows[0];
 	drawSpriteToBuffer(_compositeBuffer, buf, _savesDown.left, _savesDown.top, _savesDown.width(), _savesDown.height(), kTransparentColor);
+}
 
-	if (_showSoundOptions) {
-		_compositeBuffer.transBlitFrom(_masterSoundIcon, Common::Point(266 - _masterSoundIcon.w / 2, 212 - _masterSoundIcon.h / 2), kSoundControlsTransparentColor);
-		_compositeBuffer.transBlitFrom(_musicSoundIcon, Common::Point(333 - _musicSoundIcon.w / 2, 212 - _musicSoundIcon.h / 2), kSoundControlsTransparentColor);
-		_compositeBuffer.transBlitFrom(_sfxSoundIcon, Common::Point(399 - _sfxSoundIcon.w / 2, 212 - _sfxSoundIcon.h / 2), kSoundControlsTransparentColor);
-
-		buf = button == MASTER_LEFT_BUTTON ? _soundControlArrowLeft[1] : _soundControlArrowLeft[0];
-		drawSpriteToBuffer(_compositeBuffer, buf, _masterVolumeLeftRect.left, _masterVolumeLeftRect.top, _masterVolumeLeftRect.width(), _masterVolumeLeftRect.height(), kSoundControlsTransparentColor);
-		buf = button == MASTER_RIGHT_BUTTON ? _soundControlArrowRight[1] : _soundControlArrowRight[0];
-		drawSpriteToBuffer(_compositeBuffer, buf, _masterVolumeRightRect.left, _masterVolumeRightRect.top, _masterVolumeRightRect.width(), _masterVolumeRightRect.height(), kSoundControlsTransparentColor);
-		buf = button == SFX_LEFT_BUTTON ? _soundControlArrowLeft[1] : _soundControlArrowLeft[0];
-		drawSpriteToBuffer(_compositeBuffer, buf, _sfxVolumeLeftRect.left, _sfxVolumeLeftRect.top, _sfxVolumeLeftRect.width(), _sfxVolumeLeftRect.height(), kSoundControlsTransparentColor);
-		buf = button == SFX_RIGHT_BUTTON ? _soundControlArrowRight[1] : _soundControlArrowRight[0];
-		drawSpriteToBuffer(_compositeBuffer, buf, _sfxVolumeRightRect.left, _sfxVolumeRightRect.top, _sfxVolumeRightRect.width(), _sfxVolumeRightRect.height(), kSoundControlsTransparentColor);
-		buf = button == MUSIC_LEFT_BUTTON ? _soundControlArrowLeft[1] : _soundControlArrowLeft[0];
-		drawSpriteToBuffer(_compositeBuffer, buf, _musicVolumeLeftRect.left, _musicVolumeLeftRect.top, _musicVolumeLeftRect.width(), _musicVolumeLeftRect.height(), kSoundControlsTransparentColor);
-		buf = button == MUSIC_RIGHT_BUTTON ? _soundControlArrowRight[1] : _soundControlArrowRight[0];
-		drawSpriteToBuffer(_compositeBuffer, buf, _musicVolumeRightRect.left, _musicVolumeRightRect.top, _musicVolumeRightRect.width(), _musicVolumeRightRect.height(), kSoundControlsTransparentColor);
+void MenuManager::drawSoundControls() {
+	SoundMenuButton button = NO_SOUND_BUTTON;
+	if (_events->_leftMouseButton != 0) {
+		button = isSoundMenuButtonUnder(_events->_mouseX, _events->_mouseY);
 	}
+	byte *buf;
+	_compositeBuffer.transBlitFrom(_masterSoundIcon, Common::Point(266 - _masterSoundIcon.w / 2, 212 - _masterSoundIcon.h / 2), kSoundControlsTransparentColor);
+	_compositeBuffer.transBlitFrom(_musicSoundIcon, Common::Point(333 - _musicSoundIcon.w / 2, 212 - _musicSoundIcon.h / 2), kSoundControlsTransparentColor);
+	_compositeBuffer.transBlitFrom(_sfxSoundIcon, Common::Point(399 - _sfxSoundIcon.w / 2, 212 - _sfxSoundIcon.h / 2), kSoundControlsTransparentColor);
+
+	buf = button == MASTER_LEFT_BUTTON ? _soundControlArrowLeft[1] : _soundControlArrowLeft[0];
+	drawSpriteToBuffer(_compositeBuffer, buf, _masterVolumeLeftRect.left, _masterVolumeLeftRect.top, _masterVolumeLeftRect.width(), _masterVolumeLeftRect.height(), kSoundControlsTransparentColor);
+	buf = button == MASTER_RIGHT_BUTTON ? _soundControlArrowRight[1] : _soundControlArrowRight[0];
+	drawSpriteToBuffer(_compositeBuffer, buf, _masterVolumeRightRect.left, _masterVolumeRightRect.top, _masterVolumeRightRect.width(), _masterVolumeRightRect.height(), kSoundControlsTransparentColor);
+	buf = button == SFX_LEFT_BUTTON ? _soundControlArrowLeft[1] : _soundControlArrowLeft[0];
+	drawSpriteToBuffer(_compositeBuffer, buf, _sfxVolumeLeftRect.left, _sfxVolumeLeftRect.top, _sfxVolumeLeftRect.width(), _sfxVolumeLeftRect.height(), kSoundControlsTransparentColor);
+	buf = button == SFX_RIGHT_BUTTON ? _soundControlArrowRight[1] : _soundControlArrowRight[0];
+	drawSpriteToBuffer(_compositeBuffer, buf, _sfxVolumeRightRect.left, _sfxVolumeRightRect.top, _sfxVolumeRightRect.width(), _sfxVolumeRightRect.height(), kSoundControlsTransparentColor);
+	buf = button == MUSIC_LEFT_BUTTON ? _soundControlArrowLeft[1] : _soundControlArrowLeft[0];
+	drawSpriteToBuffer(_compositeBuffer, buf, _musicVolumeLeftRect.left, _musicVolumeLeftRect.top, _musicVolumeLeftRect.width(), _musicVolumeLeftRect.height(), kSoundControlsTransparentColor);
+	buf = button == MUSIC_RIGHT_BUTTON ? _soundControlArrowRight[1] : _soundControlArrowRight[0];
+	drawSpriteToBuffer(_compositeBuffer, buf, _musicVolumeRightRect.left, _musicVolumeRightRect.top, _musicVolumeRightRect.width(), _musicVolumeRightRect.height(), kSoundControlsTransparentColor);
 }
 
 Pelrock::MenuManager::~MenuManager() {
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 8b59bba538d..731451cacb1 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -41,7 +41,7 @@ const int kTransparentColor = 65;
 const int kSoundControlsTransparentColor = 195;
 const uint32 kCreditsBackgroundOffset = 3271454;
 
-enum MenuButton {
+enum MainMenuButton {
 	QUESTION_MARK_BUTTON,
 	INVENTORY_PREV_BUTTON,
 	INVENTORY_NEXT_BUTTON,
@@ -51,13 +51,17 @@ enum MenuButton {
 	SAVE_GAME_BUTTON,
 	LOAD_GAME_BUTTON,
 	SOUNDS_BUTTON,
+	NO_MAIN_BUTTON
+};
+
+enum SoundMenuButton {
 	MASTER_LEFT_BUTTON,
 	MASTER_RIGHT_BUTTON,
 	SFX_LEFT_BUTTON,
 	SFX_RIGHT_BUTTON,
 	MUSIC_LEFT_BUTTON,
 	MUSIC_RIGHT_BUTTON,
-	NO_BUTTON
+	NO_SOUND_BUTTON
 };
 
 static const int kCreditsOrder[34] = {
@@ -215,6 +219,15 @@ static const char *inventorySounds[113] = {
 };
 
 class MenuManager {
+
+	enum MenuState {
+		MAIN_MENU,
+		SOUND,
+		ORIGINAL_SAVE,
+		ORIGINAL_LOAD,
+		EXIT_GAME
+	};
+
 public:
 	MenuManager(Graphics::Screen *screen, PelrockEventManager *events, ResourceManager *res, SoundManager *sound);
 	~MenuManager();
@@ -228,14 +241,22 @@ public:
 private:
 	void checkMouseDown(int x, int y);
 	bool checkMouseClick(int x, int y);
+void checkSoundMenuClick(int x, int y);
+	bool checkMainMenuMouse(int x, int y, bool &retFlag);
+	bool checkMainMenuMouse(int x, int y); // returns bool if its supposed to close the menu
 	void showCredits();
 	bool selectInventoryItem(int i);
 	void loadMenuTexts();
 	void cleanUp();
 	void drawButtons();
+	void drawConfirmation();
+	void drawSaves();
+	void drawMainButtons();
+	void drawSoundControls();
 	void readButton(byte *rawData, uint32 offset, byte *outBuffer[2], int w, int h);
 	void readButton(Common::File &alfred7, uint32 offset, byte *outBuffer[2], Common::Rect rect);
-	MenuButton isButtonClicked(int x, int y);
+	SoundMenuButton isSoundMenuButtonUnder(int x, int y);
+	MainMenuButton isMainMenuButtonUnder(int x, int y);
 	Graphics::Screen *_screen = nullptr;
 	PelrockEventManager *_events = nullptr;
 	ResourceManager *_res = nullptr;
@@ -304,7 +325,7 @@ private:
 	Common::Array<Common::Point> _inventorySlots;
 
 	bool showButtons = true;
-	bool _showSoundOptions = false;
+	MenuState _menuState = MAIN_MENU;
 };
 
 } // End of namespace Pelrock


Commit: 1d6826d392353a9d9475fc6f57c54c37807e3cf1
    https://github.com/scummvm/scummvm/commit/1d6826d392353a9d9475fc6f57c54c37807e3cf1
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:34+02:00

Commit Message:
PELROCK: Implements original save/load screens

Changed paths:
    engines/pelrock/events.cpp
    engines/pelrock/events.h
    engines/pelrock/graphics.cpp
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h


diff --git a/engines/pelrock/events.cpp b/engines/pelrock/events.cpp
index 2c3352b1962..effa865cc6f 100644
--- a/engines/pelrock/events.cpp
+++ b/engines/pelrock/events.cpp
@@ -45,6 +45,7 @@ void PelrockEventManager::pollEvent() {
 		case Common::EVENT_KEYDOWN:
 			changeGameSpeed(_event);
 			_lastKeyEvent = _event.kbd.keycode;
+			_lastKeyAscii = _event.kbd.ascii;
 			_isKeydown = true;
 			break;
 		case Common::EVENT_KEYUP:
diff --git a/engines/pelrock/events.h b/engines/pelrock/events.h
index 84b86b8ec4a..c6f477824ca 100644
--- a/engines/pelrock/events.h
+++ b/engines/pelrock/events.h
@@ -45,6 +45,7 @@ public:
 	bool _rightMouseButton = false;
 	bool _isKeydown = false;
 	Common::KeyCode _lastKeyEvent = Common::KEYCODE_INVALID;
+	uint16 _lastKeyAscii = 0;
 	PelrockEventManager();
 	void pollEvent();
 	void waitForKey();
diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index 6b7c7c325cb..fda9b0e597c 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -156,6 +156,7 @@ void GraphicsManager::drawColoredText(Graphics::ManagedSurface *screen, const Co
 		if (text[i] == '@' && i + 1 < text.size()) {
 			// Draw accumulated segment
 			if (!segment.empty()) {
+				debug("Drawing text segment '%s' at %d, %d with color %d", segment.c_str(), currentX, y, defaultColor);
 				font->drawString(screen, segment, currentX, y, w, defaultColor);
 				currentX += font->getStringWidth(segment);
 				segment.clear();
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 6529c439525..a12263932a6 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -22,6 +22,7 @@
 #include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/file.h"
+#include "engines/metaengine.h"
 #include "graphics/paletteman.h"
 #include "graphics/thumbnail.h"
 
@@ -57,6 +58,19 @@ static const uint32 kMainMenuPart3Size = 92160;
 // ALFRED.7 — menu buttons (save/load/sound/exit, one contiguous block)
 static const uint32 kMenuButtonsOffset = 3193376;
 
+static const uint32 kQuestionMarkOffset = 3214046;
+static const uint32 kInvLeftArrowOffset = 3215906;
+static const uint32 kSoundControlOffset = 3037008;
+static const uint32 kSoundMasterOffset = 2662588;
+static const uint32 kSoundMusicOffset = 2664746;
+static const uint32 kSoundSfxOffset = 2667140;
+static const int kTransparentColor = 65;
+static const int kSoundControlsTransparentColor = 195;
+static const uint32 kCreditsBackgroundOffset = 3271454;
+static const int16 kTextStartX = 227;
+static const int16 kTextStartY = 191;
+static const byte kNumberColor = 52;
+
 Pelrock::MenuManager::MenuManager(Graphics::Screen *screen, PelrockEventManager *events, ResourceManager *res, SoundManager *sound) : _screen(screen), _events(events), _res(res), _sound(sound) {
 }
 
@@ -111,7 +125,6 @@ SoundMenuButton MenuManager::isSoundMenuButtonUnder(int x, int y) {
 
 MainMenuButton MenuManager::isMainMenuButtonUnder(int x, int y) {
 	if (_menuState != MAIN_MENU) {
-		debug("Not checking for main menu buttons because menu state is not MAIN_MENU");
 		return NO_MAIN_BUTTON;
 	}
 
@@ -199,10 +212,72 @@ bool MenuManager::checkMouseClick(int x, int y) {
 		break;
 	}
 	case ORIGINAL_SAVE: {
-
+		// Pagination arrows
+		if (_savesUp.contains(x, y)) {
+			if (_saveGamePage > 0) {
+				_saveGamePage--;
+				_editingSaveSlot = -1;
+			}
+			break;
+		}
+		if (_savesDown.contains(x, y)) {
+			if ((_saveGamePage + 1) * 8 < 256) {
+				_saveGamePage++;
+				_editingSaveSlot = -1;
+			}
+			break;
+		}
+		// CANCEL
+		if (_cancelarRect.contains(x, y)) {
+			_editingSaveSlot = -1;
+			_menuState = MAIN_MENU;
+			_menuText = _menuTexts[0];
+			break;
+		}
+		// Save slot click: begin (or switch) editing
+		for (int i = 0; i < (int)_saveSlotRects.size(); i++) {
+			if (_saveSlotRects[i].contains(x, y)) {
+				int slot = _saveGamePage * 8 + i;
+				_editingSaveSlot = slot;
+				_editingName = _saveDescriptions[slot];
+				break;
+			}
+		}
 		break;
 	}
 	case ORIGINAL_LOAD: {
+		// Pagination arrows
+		if (_savesUp.contains(x, y)) {
+			if (_saveGamePage > 0)
+				_saveGamePage--;
+			break;
+		}
+		if (_savesDown.contains(x, y)) {
+			if ((_saveGamePage + 1) * 8 < 256)
+				_saveGamePage++;
+			break;
+		}
+		// CANCELAR
+		if (_cancelarRect.contains(x, y)) {
+			_menuState = MAIN_MENU;
+			_menuText = _menuTexts[0];
+			break;
+		}
+		// Save slot click: load if slot has a save
+		for (int i = 0; i < (int)_saveSlotRects.size(); i++) {
+			if (_saveSlotRects[i].contains(x, y)) {
+				int slot = _saveGamePage * 8 + i;
+				if (!_saveDescriptions[slot].empty()) {
+					g_engine->loadGameState(slot);
+					return true;
+				}
+				else {
+					_menuState = MAIN_MENU;
+					_menuText = _menuTexts[6];
+				}
+				break;
+			}
+		}
 		break;
 	}
 	case EXIT_GAME: {
@@ -252,6 +327,9 @@ bool MenuManager::checkMainMenuMouse(int x, int y) {
 	case SAVE_GAME_BUTTON:
 		_sound->playSound("11ZZZZZZ.SMP", 0);
 		if (ConfMan.getBool("original_menus") == true) {
+			_saveGamePage = 0;
+			_editingSaveSlot = -1;
+			refreshSaveDescriptions();
 			_menuState = ORIGINAL_SAVE;
 			_menuText = Common::StringArray();
 		} else {
@@ -261,6 +339,8 @@ bool MenuManager::checkMainMenuMouse(int x, int y) {
 	case LOAD_GAME_BUTTON:
 		_sound->playSound("11ZZZZZZ.SMP", 0);
 		if (ConfMan.getBool("original_menus") == true) {
+			_saveGamePage = 0;
+			refreshSaveDescriptions();
 			_menuState = ORIGINAL_LOAD;
 			_menuText = Common::StringArray();
 		} else {
@@ -334,6 +414,29 @@ bool MenuManager::selectInventoryItem(int i) {
 	return true;
 }
 
+void MenuManager::handleSaveMenuKey(Common::KeyCode key, uint16 ascii) {
+	if (key == Common::KEYCODE_ESCAPE) {
+		_editingSaveSlot = -1;
+		_menuState = MAIN_MENU;
+		_menuText = _menuTexts[0];
+		return;
+	}
+	if (_editingSaveSlot < 0)
+		return;
+
+	if (key == Common::KEYCODE_RETURN || key == Common::KEYCODE_KP_ENTER) {
+		// Commit save
+		g_engine->saveGameState(_editingSaveSlot, _editingName);
+		_saveDescriptions[_editingSaveSlot] = _editingName;
+		_editingSaveSlot = -1;
+	} else if (key == Common::KEYCODE_BACKSPACE) {
+		if (!_editingName.empty())
+			_editingName.deleteLastChar();
+	} else if (ascii >= 32 && ascii < 256 && ascii != 127) {
+		_editingName += (char)ascii;
+	}
+}
+
 void MenuManager::menuLoop() {
 
 	// Save screenshot in case the user saves
@@ -372,16 +475,35 @@ void MenuManager::menuLoop() {
 			}
 		}
 		if (_events->_rightMouseClicked) {
-			break;
-		}
-		if (_menuState == EXIT_GAME) {
-			if (_events->_lastKeyEvent == Common::KEYCODE_s) {
-				g_engine->quitGame();
-			} else if (_events->_lastKeyEvent == Common::KEYCODE_n) {
-				_menuState = MAIN_MENU;
-				_menuText = _menuTexts[0];
+			_events->_rightMouseClicked = false;
+			// In original save/load sub-menus only CANCELAR / ESC can exit
+			if (_menuState != ORIGINAL_SAVE && _menuState != ORIGINAL_LOAD) {
+				break;
 			}
+		}
+
+		// Keyboard handling
+		if (_events->_lastKeyEvent != Common::KEYCODE_INVALID) {
+			Common::KeyCode key = _events->_lastKeyEvent;
+			uint16 ascii = _events->_lastKeyAscii;
 			_events->_lastKeyEvent = Common::KEYCODE_INVALID;
+			_events->_lastKeyAscii = 0;
+
+			if (_menuState == ORIGINAL_SAVE) {
+				handleSaveMenuKey(key, ascii);
+			} else if (_menuState == ORIGINAL_LOAD) {
+				if (key == Common::KEYCODE_ESCAPE) {
+					_menuState = MAIN_MENU;
+					_menuText = _menuTexts[0];
+				}
+			} else if (_menuState == EXIT_GAME) {
+				if (key == Common::KEYCODE_s) {
+					g_engine->quitGame();
+				} else if (key == Common::KEYCODE_n) {
+					_menuState = MAIN_MENU;
+					_menuText = _menuTexts[0];
+				}
+			}
 		}
 
 		drawScreen();
@@ -412,7 +534,7 @@ void MenuManager::drawScreen() {
 	_screen->blitFrom(_compositeBuffer);
 	byte defaultColor = 255;
 	for (int i = 0; _menuText.size() > i; i++) {
-		g_engine->_graphics->drawColoredText(_screen, _menuText[i], 230, 200 + (i * 10), 200, defaultColor, g_engine->_smallFont);
+		g_engine->_graphics->drawColoredText(_screen, _menuText[i], kTextStartX, kTextStartY + (i * _textLineH), 200, defaultColor, g_engine->_smallFont);
 	}
 
 	drawText(g_engine->_smallFont, Common::String::format("%d,%d", _events->_mouseX, _events->_mouseY), 0, 0, 640, 13);
@@ -526,6 +648,7 @@ void MenuManager::loadMenu() {
 	_menuText = _menuTexts[0];
 	alfred7.close();
 
+	_textLineH = g_engine->_smallFont->getFontHeight();
 	for (int i = 0; i < 4; i++) {
 		_inventorySlots.push_back(Common::Point(140 + (82 * i), 115 - (8 * i)));
 	}
@@ -569,8 +692,22 @@ void MenuManager::loadMenuTexts() {
 	byte *textBuffer = new byte[kMenuTextSize];
 	exe.seek(kMenuTextOffset, SEEK_SET);
 	exe.read(textBuffer, kMenuTextSize);
-	_menuTexts = _res->processTextData(textBuffer, kMenuTextSize, true);
-
+	Common::Array<Common::StringArray> unprocessedMenuTexts;
+	unprocessedMenuTexts = _res->processTextData(textBuffer, kMenuTextSize, true);
+	_menuText = Common::StringArray();
+	for (int i = 0; i < (int)unprocessedMenuTexts.size(); i++) {
+		if (i == 1) {
+			Common::StringArray emptyText;
+			emptyText.push_back(unprocessedMenuTexts[i][0]);
+			Common::StringArray loadText;
+			loadText.push_back(unprocessedMenuTexts[i][1]);
+			_menuTexts.push_back(emptyText);
+			_menuTexts.push_back(loadText);
+		} else {
+			_menuTexts.push_back(unprocessedMenuTexts[i]);
+		}
+	}
+	debug("Menu texts size after processing: %d", (int)_menuTexts.size());
 	_menuText = _menuTexts[0];
 	delete[] textBuffer;
 
@@ -609,7 +746,74 @@ void MenuManager::drawConfirmation() {
 	_menuText = _menuTexts[4];
 }
 
+void MenuManager::refreshSaveDescriptions() {
+	_saveDescriptions.clear();
+	_saveDescriptions.resize(256);
+
+	SaveStateList saves = g_engine->getMetaEngine()->listSaves(ConfMan.getActiveDomainName().c_str());
+	for (const SaveStateDescriptor &desc : saves) {
+		int slot = desc.getSaveSlot();
+		if (slot >= 0 && slot < 256)
+			_saveDescriptions[slot] = desc.getDescription();
+	}
+}
 void MenuManager::drawSaves() {
+
+	// Compute the area for the overlay
+	const int startX = kTextStartX;
+	const int startY = kTextStartY;
+	const int overlayW = 216;
+	const int numSlots = 8;
+
+	// Headline: _menuTexts[1] = save, _menuTexts[2] = load
+	Common::StringArray headline;
+	if (_menuState == ORIGINAL_SAVE)
+		headline = _menuTexts[3];
+	else
+		headline = _menuTexts[2];
+	_menuText = headline;
+
+	_saveSlotRects.clear();
+
+	int y = startY + _textLineH;
+	const Common::Point mousePos(_events->_mouseX, _events->_mouseY);
+	for (int i = 0; i < numSlots; i++) {
+		int slot = _saveGamePage * numSlots + i;
+		Common::String slotText;
+		Common::String slotNumber = Common::String::format("%02d ", slot + 1);
+		int slotNumberWidth = g_engine->_smallFont->getStringWidth(slotNumber);
+
+		Common::Rect slotRect(startX - 2, y - 1, startX + overlayW - 2, y + _textLineH);
+		bool hovered = (_editingSaveSlot != slot) && slotRect.contains(mousePos);
+		byte textColor = hovered ? 18 : 0x0F;
+
+		if (_editingSaveSlot == slot) {
+			// Show editing cursor
+			slotText = Common::String::format("%s_", _editingName.c_str());
+			textColor = 244; // highlight colour for active editing
+			// Light highlight behind the editing row
+			_compositeBuffer.fillRect(Common::Rect(startX - 2, y - 1, startX + overlayW - 2, y + _textLineH), 1);
+		} else {
+			const Common::String &desc = _saveDescriptions[slot];
+			if (desc.empty())
+				slotText = Common::String::format("%s", _menuTexts[1][0].c_str());
+			else {
+				slotText = desc;
+				slotText.toUppercase();
+			}
+		}
+		drawText(_compositeBuffer, g_engine->_smallFont, slotNumber, startX, y, overlayW, kNumberColor);
+		drawText(_compositeBuffer, g_engine->_smallFont, slotText, startX + slotNumberWidth, y, overlayW - slotNumberWidth, textColor);
+
+		_saveSlotRects.push_back(slotRect);
+		y += _textLineH;
+	}
+
+	// CANCEL row
+	_cancelarRect = Common::Rect(startX - 2, y - 1, startX + overlayW - 2, y + _textLineH);
+	byte cancelColor = _cancelarRect.contains(mousePos) ? 18 : 0x0F;
+	Common::String cancelText = _menuTexts[4][0].substr(2, _menuTexts[4][0].size() - 2);
+	drawText(_compositeBuffer, g_engine->_smallFont, cancelText, startX, y, overlayW, cancelColor);
 }
 
 void MenuManager::drawMainButtons() {
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 731451cacb1..a5acd16d8b5 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -31,16 +31,6 @@
 
 namespace Pelrock {
 
-const uint32 kQuestionMarkOffset = 3214046;
-const uint32 kInvLeftArrowOffset = 3215906;
-const uint32 kSoundControlOffset = 3037008;
-const uint32 kSoundMasterOffset = 	2662588;
-const uint32 kSoundMusicOffset = 2664746;
-const uint32 kSoundSfxOffset = 2667140;
-const int kTransparentColor = 65;
-const int kSoundControlsTransparentColor = 195;
-const uint32 kCreditsBackgroundOffset = 3271454;
-
 enum MainMenuButton {
 	QUESTION_MARK_BUTTON,
 	INVENTORY_PREV_BUTTON,
@@ -241,7 +231,7 @@ public:
 private:
 	void checkMouseDown(int x, int y);
 	bool checkMouseClick(int x, int y);
-void checkSoundMenuClick(int x, int y);
+	void checkSoundMenuClick(int x, int y);
 	bool checkMainMenuMouse(int x, int y, bool &retFlag);
 	bool checkMainMenuMouse(int x, int y); // returns bool if its supposed to close the menu
 	void showCredits();
@@ -255,6 +245,8 @@ void checkSoundMenuClick(int x, int y);
 	void drawSoundControls();
 	void readButton(byte *rawData, uint32 offset, byte *outBuffer[2], int w, int h);
 	void readButton(Common::File &alfred7, uint32 offset, byte *outBuffer[2], Common::Rect rect);
+	void refreshSaveDescriptions();
+	void handleSaveMenuKey(Common::KeyCode key, uint16 ascii);
 	SoundMenuButton isSoundMenuButtonUnder(int x, int y);
 	MainMenuButton isMainMenuButtonUnder(int x, int y);
 	Graphics::Screen *_screen = nullptr;
@@ -326,6 +318,15 @@ void checkSoundMenuClick(int x, int y);
 
 	bool showButtons = true;
 	MenuState _menuState = MAIN_MENU;
+	int _textLineH = 0;
+
+	// Save/Load sub-menu state
+	int _saveGamePage = 0;
+	int _editingSaveSlot = -1;       // -1 = not editing any slot
+	Common::String _editingName;     // name being typed for a save
+	Common::Rect _cancelarRect;      // hit-rect for the CANCELAR row
+	Common::Array<Common::Rect> _saveSlotRects; // hit-rects for the 8 visible save rows
+	Common::StringArray _saveDescriptions;       // indexed by slot 0-255
 };
 
 } // End of namespace Pelrock


Commit: 7f8b1610880145aa92e32f8b57e831ca4f8816d4
    https://github.com/scummvm/scummvm/commit/7f8b1610880145aa92e32f8b57e831ca4f8816d4
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:34+02:00

Commit Message:
PELROCK: Missing actions

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 6c4e09bb0f3..b5622e1653e 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -305,6 +305,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_state->setCurrentRoot(4, 2, 0);
 		break;
 	case 259:
+		_dialog->say(_res->_ingameTexts[DEPIEDRANO_DEHIELO], 1);
 		_dialog->say(_res->_ingameTexts[NO_EMPECEMOS]);
 		break;
 	case 260:
@@ -870,10 +871,14 @@ void PelrockEngine::closeKitchenDoor(HotSpot *HotSpot) {
 void PelrockEngine::openKitchenDrawer(HotSpot *hotspot) {
 	if (!_state->getFlag(FLAG_JEFE_ENCARCELADO)) {
 		_dialog->say(_res->_ingameTexts[QUITA_ESAS_MANOS]);
-	} else {
+	} else if (!_state->getFlag(FLAG_RECIPE_TAKEN)) {
+		_state->setFlag(FLAG_RECIPE_TAKEN, true);
 		_room->addSticker(36);
 		addInventoryItem(63); // Add recipe
 		_dialog->say(_res->_ingameTexts[QUESESTO_RECETA]);
+	} else {
+		// Already took the recipe
+		_dialog->say(_res->_ingameTexts[YAESTA_ABIERTO]);
 	}
 }
 
@@ -1252,10 +1257,15 @@ void PelrockEngine::waitForSoundEnd(int channel) {
 
 void PelrockEngine::pickupSunflower(HotSpot *hotspot) {
 	if (_state->getFlag(FLAG_PARADOJA_RESUELTA) == false) {
-		_dialog->say(_res->_ingameTexts[OIGA]);
-		_state->setCurrentRoot(25, 26, 0);
-		walkAndAction(_room->findHotspotByExtra(467), TALK);
-		_state->setFlag(FLAG_RIDDLE_PRESENTED, true);
+		if (_state->getFlag(FLAG_RIDDLE_PRESENTED)) {
+			// try to take the sunflower before solving the riddle
+			_dialog->say(_res->_ingameTexts[LEESTOYVIGILANDO]);
+		} else {
+			_dialog->say(_res->_ingameTexts[OIGA]);
+			_state->setCurrentRoot(25, 26, 0);
+			walkAndAction(_room->findHotspotByExtra(467), TALK);
+			_state->setFlag(FLAG_RIDDLE_PRESENTED, true);
+		}
 	} else {
 		addInventoryItem(85);
 		_room->disableHotspot(hotspot);
@@ -2087,6 +2097,11 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		}
 		break;
 	case 88: {
+		if (_room->_currentRoomNumber != 28 &&
+			(_room->_currentRoomNumber < 51 || _room->_currentRoomNumber > 54)) {
+			_dialog->say(_res->_ingameTexts[AQUI_NO_NECESITO]);
+			break;
+		}
 		SpellBook spellBook(_events, _res);
 		playAlfredSpecialAnim(0);
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index b500b39d5ff..7bc1d91b7dd 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -558,8 +558,9 @@ struct ResetEntry {
 #define FLAG_HA_USADO_AGUA 45
 #define FLAG_NUMERO_DE_COPAS 47
 #define FLAG_PIGEON_DEAD 61
+#define FLAG_RECIPE_TAKEN 62
 
-const int kNumGameFlags = 62;
+const int kNumGameFlags = 63;
 
 struct GameStateData {
 	byte flags[kNumGameFlags];


Commit: 995444994280b95a83e813910dfc90cdce4ef5a3
    https://github.com/scummvm/scummvm/commit/995444994280b95a83e813910dfc90cdce4ef5a3
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:35+02:00

Commit Message:
PELROCK: Fixes conversation bug (present in the original) in room 45

Changed paths:
    engines/pelrock/dialog.cpp


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 11935299f1a..f8d6c5f8647 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -806,6 +806,18 @@ ConversationEndResult DialogManager::checkConversationEnd(const byte *data, uint
 	byte controlByte = data[position];
 
 	if (controlByte == CTRL_END_CONVERSATION) {
+		// Bug in the original in room 45, root 1: The conversation data has F4 (END_CONV) after
+		// the opening NPC text instead of FD (END_TEXT), so the 3 choices that follow are
+		// unreachable. Treat F4 as FD specifically for this root to restore them.
+		int room = g_engine->_room->_currentRoomNumber;
+		uint32 peekPos = position + 1;
+		if (room == 45 && currentRoot == 1 &&
+			peekPos < dataSize &&
+			(data[peekPos] == kCtrlDialogueMarker || data[peekPos] == kCtrlDialogueMarkerOneoff)) {
+			debug("Room 45 Root 1: F4 followed by choice marker treated as FD (data bug workaround)");
+			result.nextPosition = position + 1;
+			return result;
+		}
 		debug("End of conversation marker found");
 		result.shouldEnd = true;
 		return result;


Commit: e35f47c289a887d7277065a7678cc2d9d6b149d3
    https://github.com/scummvm/scummvm/commit/e35f47c289a887d7277065a7678cc2d9d6b149d3
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:35+02:00

Commit Message:
PELROCK: Fixes exit confirmation

Changed paths:
    engines/pelrock/menu.cpp


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index a12263932a6..bea1eca54ec 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -350,7 +350,6 @@ bool MenuManager::checkMainMenuMouse(int x, int y) {
 	case EXIT_MENU_BUTTON:
 		_sound->playSound("11ZZZZZZ.SMP", 0);
 		_menuState = EXIT_GAME;
-		// g_engine->quitGame();
 		break;
 	case SOUNDS_BUTTON:
 		_sound->playSound("11ZZZZZZ.SMP", 0);
@@ -743,7 +742,7 @@ void MenuManager::drawButtons() {
 }
 
 void MenuManager::drawConfirmation() {
-	_menuText = _menuTexts[4];
+	_menuText = _menuTexts[5];
 }
 
 void MenuManager::refreshSaveDescriptions() {


Commit: 5708dae5d5b69ad2abf574c2b73ad3388ededa4d
    https://github.com/scummvm/scummvm/commit/5708dae5d5b69ad2abf574c2b73ad3388ededa4d
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:35+02:00

Commit Message:
PELROCK: Fixes styling of saving name input

Changed paths:
    engines/pelrock/menu.cpp


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index bea1eca54ec..130f1ef06bd 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -70,6 +70,8 @@ static const uint32 kCreditsBackgroundOffset = 3271454;
 static const int16 kTextStartX = 227;
 static const int16 kTextStartY = 191;
 static const byte kNumberColor = 52;
+static const byte kCursorChar = 0xDB;
+static const byte kWhiteColor = 0x0F;
 
 Pelrock::MenuManager::MenuManager(Graphics::Screen *screen, PelrockEventManager *events, ResourceManager *res, SoundManager *sound) : _screen(screen), _events(events), _res(res), _sound(sound) {
 }
@@ -784,14 +786,13 @@ void MenuManager::drawSaves() {
 
 		Common::Rect slotRect(startX - 2, y - 1, startX + overlayW - 2, y + _textLineH);
 		bool hovered = (_editingSaveSlot != slot) && slotRect.contains(mousePos);
-		byte textColor = hovered ? 18 : 0x0F;
+		byte textColor = hovered ? 18 : kWhiteColor;
 
 		if (_editingSaveSlot == slot) {
 			// Show editing cursor
-			slotText = Common::String::format("%s_", _editingName.c_str());
-			textColor = 244; // highlight colour for active editing
-			// Light highlight behind the editing row
-			_compositeBuffer.fillRect(Common::Rect(startX - 2, y - 1, startX + overlayW - 2, y + _textLineH), 1);
+			_editingName.toUppercase();
+			slotText = _editingName;
+			textColor = 18; // highlight colour for active editing
 		} else {
 			const Common::String &desc = _saveDescriptions[slot];
 			if (desc.empty())
@@ -804,13 +805,19 @@ void MenuManager::drawSaves() {
 		drawText(_compositeBuffer, g_engine->_smallFont, slotNumber, startX, y, overlayW, kNumberColor);
 		drawText(_compositeBuffer, g_engine->_smallFont, slotText, startX + slotNumberWidth, y, overlayW - slotNumberWidth, textColor);
 
+		if(_editingSaveSlot == slot) {
+			// Draw cursor
+			int cursorX = startX + slotNumberWidth + g_engine->_smallFont->getStringWidth(slotText);
+			drawText(_compositeBuffer, g_engine->_smallFont, Common::String(kCursorChar), cursorX, y, overlayW - (cursorX - startX), kWhiteColor);
+		}
+
 		_saveSlotRects.push_back(slotRect);
 		y += _textLineH;
 	}
 
 	// CANCEL row
 	_cancelarRect = Common::Rect(startX - 2, y - 1, startX + overlayW - 2, y + _textLineH);
-	byte cancelColor = _cancelarRect.contains(mousePos) ? 18 : 0x0F;
+	byte cancelColor = _cancelarRect.contains(mousePos) ? 18 : kWhiteColor;
 	Common::String cancelText = _menuTexts[4][0].substr(2, _menuTexts[4][0].size() - 2);
 	drawText(_compositeBuffer, g_engine->_smallFont, cancelText, startX, y, overlayW, cancelColor);
 }


Commit: 2b1bcc34dc9bf0134b3b184d4097f4eaf88529f8
    https://github.com/scummvm/scummvm/commit/2b1bcc34dc9bf0134b3b184d4097f4eaf88529f8
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:35+02:00

Commit Message:
PELROCK: Fixes spells in spellbook having extraneous characters

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 6ac79b31174..df3a285d515 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -912,7 +912,6 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			// Scale special anim frame to Alfred size before drawing
 			drawSpriteToBuffer(_compositeBuffer, frame, _alfredState.x, _alfredState.y - _res->_currentSpecialAnim->h, _res->_currentSpecialAnim->w, _res->_currentSpecialAnim->h, 255);
 		}
-		debug("Playing special anim frame %d/%d, speed %d", _res->_currentSpecialAnim->curFrame, _res->_currentSpecialAnim->numFrames, _res->_currentSpecialAnim->speed);
 		if (_chrono->getFrameCount() % _res->_currentSpecialAnim->speed == 0) {
 			_res->_currentSpecialAnim->curFrame++;
 
@@ -1511,7 +1510,6 @@ void PelrockEngine::gameLoop() {
 		}
 		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
 	}
-
 	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_k) {
 		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
 		credits();
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index ec51bd361fb..99fdb4cd677 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -36,45 +36,45 @@ static const uint32 ALFRED7_ALFRED_COMB_R = 67768;
 static const uint32 ALFRED7_ALFRED_COMB_L = 88408;
 
 // ALFRED.4 — inventory icon pixel data
-static const uint32 kInventoryIconsOffset   = 42366;
+static const uint32 kInventoryIconsOffset = 42366;
 static const uint32 kInventoryIconsTailSize = 423656; // bytes trailing the icon block
 
 // ALFRED.7 — action-popup balloon frames
 static const uint32 kBalloonFramesOffset = 2176936;
-static const uint32 kBalloonFramesSize   = 24950; // compressed size
+static const uint32 kBalloonFramesSize = 24950; // compressed size
 
 // JUEGO.EXE — Alfred in-game text responses
-static const uint32 kAlfredResponsesOffset        = 0x441DC;
-static const uint32 kAlfredResponsesSize          = 12143;
+static const uint32 kAlfredResponsesOffset = 0x441DC;
+static const uint32 kAlfredResponsesSize = 12143;
 static const uint32 kConversationTerminatorOffset = 0x0492EE;
 
 // JUEGO.EXE — credits
 static const uint32 kCreditsOffset = 0x49F60;
-static const uint32 kCreditsSize   = 2540;
+static const uint32 kCreditsSize = 2540;
 
 // JUEGO.EXE — computer-screen text
 static const uint32 kComputerTextOffset = 0x0004901A;
-static const uint32 kComputerTextSize   = 490;
+static const uint32 kComputerTextSize = 490;
 
 // Alfred special animation data (file index given per entry in alfredSpecialAnims[])
-static const uint32 kAlfredAnimReadBookOffset       = 559685;  // 0  - READ BOOK
-static const uint32 kAlfredAnimReadRecipeOffset     = 578943;  // 1  - READ RECIPE
-static const uint32 kAlfredAnimElectricShock1Offset = 37000;   // 2  - ELECTRIC SHOCK 1
-static const uint32 kAlfredAnimElectricShock3Offset = 53106;   // 3  - ELECTRIC SHOCK 3
-static const uint32 kAlfredAnimThrowOffset          = 20724;   // 4  - Throw
-static const uint32 kAlfredAnimThrowSize            = 62480;   // 4  - Throw explicit size
-static const uint32 kAlfredAnimCrocodileOffset      = 1556540; // 5  - Crocodile
-static const uint32 kAlfredAnimManholeOffset        = 1583702; // 6  - Exit manhole
-static const uint32 kAlfredAnimClimbDownOffset      = 1761234; // 7  - Climbs down
-static const uint32 kAlfredAnimClimbUpOffset        = 1766378; // 8  - Climbs up
-static const uint32 kAlfredAnimExitTunnelOffset     = 1770196; // 9  - Exits tunnel
-static const uint32 kAlfredAnimWorkersOffset        = 1600956; // 10 - With workers
-static const uint32 kAlfredAnimMunheco1Offset       = 2060916; // 11 - Munheco 1
-static const uint32 kAlfredAnimMunheco2Offset       = 2115632; // 12 - Munheco 2
-static const uint32 kAlfredAnimMunheco3Offset       = 1526432; // 13 - Munheco 3
-static const uint32 kAlfredAnimDescamisaOffset      = 2972568; // 14 - Descamisa
-static const uint32 kAlfredAnimSecretPassageOffset  = 1749464; // 15 - Secret passage
-static const uint32 kAlfredAnimInBedOffset          = 3038454; // 16 - Alfred in bed
+static const uint32 kAlfredAnimReadBookOffset = 559685;       // 0  - READ BOOK
+static const uint32 kAlfredAnimReadRecipeOffset = 578943;     // 1  - READ RECIPE
+static const uint32 kAlfredAnimElectricShock1Offset = 37000;  // 2  - ELECTRIC SHOCK 1
+static const uint32 kAlfredAnimElectricShock3Offset = 53106;  // 3  - ELECTRIC SHOCK 3
+static const uint32 kAlfredAnimThrowOffset = 20724;           // 4  - Throw
+static const uint32 kAlfredAnimThrowSize = 62480;             // 4  - Throw explicit size
+static const uint32 kAlfredAnimCrocodileOffset = 1556540;     // 5  - Crocodile
+static const uint32 kAlfredAnimManholeOffset = 1583702;       // 6  - Exit manhole
+static const uint32 kAlfredAnimClimbDownOffset = 1761234;     // 7  - Climbs down
+static const uint32 kAlfredAnimClimbUpOffset = 1766378;       // 8  - Climbs up
+static const uint32 kAlfredAnimExitTunnelOffset = 1770196;    // 9  - Exits tunnel
+static const uint32 kAlfredAnimWorkersOffset = 1600956;       // 10 - With workers
+static const uint32 kAlfredAnimMunheco1Offset = 2060916;      // 11 - Munheco 1
+static const uint32 kAlfredAnimMunheco2Offset = 2115632;      // 12 - Munheco 2
+static const uint32 kAlfredAnimMunheco3Offset = 1526432;      // 13 - Munheco 3
+static const uint32 kAlfredAnimDescamisaOffset = 2972568;     // 14 - Descamisa
+static const uint32 kAlfredAnimSecretPassageOffset = 1749464; // 15 - Secret passage
+static const uint32 kAlfredAnimInBedOffset = 3038454;         // 16 - Alfred in bed
 
 ResourceManager::ResourceManager(/* args */) {
 	_inventoryIcons = new InventoryObject[69];
@@ -84,23 +84,23 @@ ResourceManager::ResourceManager(/* args */) {
 }
 
 const AlfredSpecialAnimOffset ResourceManager::alfredSpecialAnims[] = {
-	{10,  51, 102, 1, 7, kAlfredAnimReadBookOffset,       1, 2, 0},              // 0  - READ BOOK
-	{10,  51, 102, 1, 7, kAlfredAnimReadRecipeOffset,     1, 2, 0},              // 1  - READ RECIPE
-	{ 3,  45,  87, 0, 7, kAlfredAnimElectricShock1Offset, 1, 2, 0},              // 2  - ELECTRIC SHOCK 1
-	{ 2,  82,  58, 0, 7, kAlfredAnimElectricShock3Offset, 20, 1, 0},             // 3  - ELECTRIC SHOCK 3
-	{ 3,  71, 110, 1, 2, kAlfredAnimThrowOffset,          1, 1, kAlfredAnimThrowSize}, // 4  - Throw
-	{14, 171, 107, 1, 7, kAlfredAnimCrocodileOffset,      1, 2, 0},              // 5  - crocodile
-	{12, 113, 103, 1, 7, kAlfredAnimManholeOffset,        1, 2, 0},              // 6  - exit through manhole
-	{11,  33,  72, 1, 7, kAlfredAnimClimbDownOffset,      1, 2, 0},              // 7  - alfred climbs down
-	{ 9,  33,  72, 1, 7, kAlfredAnimClimbUpOffset,        1, 2, 0},              // 8  - alfred climbs up
-	{16, 158, 115, 0, 7, kAlfredAnimExitTunnelOffset,     1, 2, 0},              // 9  - alfred exits tunnel
-	{ 7, 208, 102, 0, 7, kAlfredAnimWorkersOffset,        1, 2, 0},              // 10 - alfred with workers
-	{23, 116, 124, 1, 7, kAlfredAnimMunheco1Offset,       1, 2, 0},              // 11 - Munheco 1
-	{18, 177, 124, 1, 7, kAlfredAnimMunheco2Offset,       1, 2, 0},              // 12 - Munheco 2
-	{11,  98, 138, 1, 7, kAlfredAnimMunheco3Offset,       1, 2, 0},              // 13 - Munheco 3
-	{ 4,  51, 102, 1, 7, kAlfredAnimDescamisaOffset,      1, 2, 0},              // 14 - descamisa
-	{13,  95,  99, 1, 7, kAlfredAnimSecretPassageOffset,  1, 2, 0},              // 15 - alfred enters secret passage
-	{14,  71,  66, 1, 7, kAlfredAnimInBedOffset,          1, 2, 0},              // 16 - Alfred in bed
+	{10, 51, 102, 1, 7, kAlfredAnimReadBookOffset, 1, 2, 0},                // 0  - READ BOOK
+	{10, 51, 102, 1, 7, kAlfredAnimReadRecipeOffset, 1, 2, 0},              // 1  - READ RECIPE
+	{3, 45, 87, 0, 7, kAlfredAnimElectricShock1Offset, 1, 2, 0},            // 2  - ELECTRIC SHOCK 1
+	{2, 82, 58, 0, 7, kAlfredAnimElectricShock3Offset, 20, 1, 0},           // 3  - ELECTRIC SHOCK 3
+	{3, 71, 110, 1, 2, kAlfredAnimThrowOffset, 1, 1, kAlfredAnimThrowSize}, // 4  - Throw
+	{14, 171, 107, 1, 7, kAlfredAnimCrocodileOffset, 1, 2, 0},              // 5  - crocodile
+	{12, 113, 103, 1, 7, kAlfredAnimManholeOffset, 1, 2, 0},                // 6  - exit through manhole
+	{11, 33, 72, 1, 7, kAlfredAnimClimbDownOffset, 1, 2, 0},                // 7  - alfred climbs down
+	{9, 33, 72, 1, 7, kAlfredAnimClimbUpOffset, 1, 2, 0},                   // 8  - alfred climbs up
+	{16, 158, 115, 0, 7, kAlfredAnimExitTunnelOffset, 1, 2, 0},             // 9  - alfred exits tunnel
+	{7, 208, 102, 0, 7, kAlfredAnimWorkersOffset, 1, 2, 0},                 // 10 - alfred with workers
+	{23, 116, 124, 1, 7, kAlfredAnimMunheco1Offset, 1, 2, 0},               // 11 - Munheco 1
+	{18, 177, 124, 1, 7, kAlfredAnimMunheco2Offset, 1, 2, 0},               // 12 - Munheco 2
+	{11, 98, 138, 1, 7, kAlfredAnimMunheco3Offset, 1, 2, 0},                // 13 - Munheco 3
+	{4, 51, 102, 1, 7, kAlfredAnimDescamisaOffset, 1, 2, 0},                // 14 - descamisa
+	{13, 95, 99, 1, 7, kAlfredAnimSecretPassageOffset, 1, 2, 0},            // 15 - alfred enters secret passage
+	{14, 71, 66, 1, 7, kAlfredAnimInBedOffset, 1, 2, 0},                    // 16 - Alfred in bed
 };
 
 ResourceManager::~ResourceManager() {
@@ -273,7 +273,6 @@ void ResourceManager::loadAlfredAnims() {
 	delete[] completePic;
 	free(bufferFile);
 
-
 	Common::File alfred7;
 	if (!alfred7.open(Common::Path("ALFRED.7"))) {
 		error("Could not open ALFRED.7");
@@ -458,7 +457,7 @@ Common::Array<Common::StringArray> ResourceManager::loadComputerText() {
 	exe.read(computerTextBuf, bufSize);
 	Common::Array<Common::StringArray> computerTexts = processTextData(computerTextBuf, bufSize);
 
-	for(int i = 0; i < computerTexts.size(); i++) {
+	for (int i = 0; i < computerTexts.size(); i++) {
 		debug("Computer text %d:", i);
 		Common::StringArray &lines = computerTexts[i];
 		for (int j = 0; j < lines.size(); j++) {
@@ -508,10 +507,10 @@ Common::Array<Common::StringArray> ResourceManager::processTextData(byte *data,
 	Common::Array<Common::StringArray> texts;
 	while (pos < size) {
 		if (data[pos] == CTRL_END_TEXT) {
-				lines.push_back(desc);
-				texts.push_back(lines);
-				lines.clear();
-				desc = Common::String();
+			lines.push_back(desc);
+			texts.push_back(lines);
+			lines.clear();
+			desc = Common::String();
 			pos++;
 			continue;
 		}
@@ -525,12 +524,15 @@ Common::Array<Common::StringArray> ResourceManager::processTextData(byte *data,
 			desc.append(1, '@');
 			desc.append(1, color);
 			pos += 2;
+			if (data[pos + 1] == 0x78 || data[pos + 2] == 0x78) {
+				pos += 2;
+			}
 
 			continue;
 		}
 
 		if (data[pos] == 0xC8 || data[pos] == 0xB1) {
-			if(!desc.empty() || data[pos] == 0xC8) {
+			if (!desc.empty() || data[pos] == 0xC8) {
 				lines.push_back(desc);
 			}
 			desc = Common::String();


Commit: 9ede09dccf2b8d8eae6d25236317d2f80ddb1bae
    https://github.com/scummvm/scummvm/commit/9ede09dccf2b8d8eae6d25236317d2f80ddb1bae
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:36+02:00

Commit Message:
PELROCK: Amend checkConversation signature

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index f8d6c5f8647..d13f0df33fc 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -572,7 +572,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		}
 
 		if (!skipToChoices) {
-			ConversationEndResult endResult = checkConversationEnd(conversationData, dataSize, state.position);
+			ConversationEndResult endResult = checkConversationEnd(conversationData, dataSize, state.position, state.currentRoot);
 			// Dispatch action for both 0xF8 (action+end) and 0xEB (action+continue)
 			if (endResult.hasAction) {
 				g_engine->dialogActionTrigger(endResult.actionCode, g_engine->_room->_currentRoomNumber, state.currentRoot);
@@ -790,7 +790,7 @@ uint32 DialogManager::readAndDisplayDialogue(const byte *data, uint32 dataSize,
 	return endPos;
 }
 
-ConversationEndResult DialogManager::checkConversationEnd(const byte *data, uint32 dataSize, uint32 position) {
+ConversationEndResult DialogManager::checkConversationEnd(const byte *data, uint32 dataSize, uint32 position, int currentRoot) {
 	ConversationEndResult result;
 	result.shouldEnd = false;
 	result.nextPosition = position;
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index b021f149630..a808b763c70 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -109,7 +109,7 @@ private:
 	ConversationState initializeConversation(const byte *data, uint32 dataSize, byte npcIndex);
 	bool handleGoBack(const byte *data, Common::Stack<uint32> &positionStack, uint32 position, ConversationState &state);
 	uint32 readAndDisplayDialogue(const byte *data, uint32 dataSize, uint32 position);
-	ConversationEndResult checkConversationEnd(const byte *data, uint32 dataSize, uint32 position);
+	ConversationEndResult checkConversationEnd(const byte *data, uint32 dataSize, uint32 position, int currentRoot = -1);
 	void addGoodbyeOptionIfNeeded(Common::Array<ChoiceOption> *choices, int currentChoiceLevel, uint originalChoiceCount);
 	uint32 processChoiceSelection(const byte *data, uint32 dataSize, Common::Array<ChoiceOption> *choices, int selectedIndex, ConversationState &state);
 	void disableChoiceIfNeeded(Common::Array<Pelrock::ChoiceOption> *choices, int selectedIndex, const byte *data, uint32 dataSize, uint32 endPos, Pelrock::ConversationState &state);


Commit: 6b269d220332ec5c47497e3be3122e4e8f0d5b43
    https://github.com/scummvm/scummvm/commit/6b269d220332ec5c47497e3be3122e4e8f0d5b43
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:36+02:00

Commit Message:
PELROCK: Fixes bug (present in the original) when handing books to librarian or using them on alfred

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/menu.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index b5622e1653e..056edc609d1 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -740,7 +740,8 @@ void PelrockEngine::noOpAction(HotSpot *hotspot) {
 }
 
 void PelrockEngine::noOpItem(int item, HotSpot *hotspot) {
-	if (item >= 11 && item <= 47 && hotspot->extra == 358) {
+	// Original game checked against 47 instead of 58
+	if (item >= 11 && item <= 58 && hotspot->extra == 358) {
 		_state->removeInventoryItem(item);
 		_dialog->say(_res->_ingameTexts[DEACUERDO_2]);
 		return;
@@ -2237,12 +2238,12 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		break;
 	}
 	default: {
-		if (inventoryObject >= 11 && inventoryObject <= 47) {
+		// Original game incorrectly checked until object 47
+		if (inventoryObject >= 11 && inventoryObject <= 58) {
 			playAlfredSpecialAnim(0);
 			_dialog->say(_res->_ingameTexts[LIBRO_ABURRIDO]);
 			return;
 		}
-
 		sayRandomIncorrectResponse();
 		break;
 	}
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 130f1ef06bd..42c8dd6ca58 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -308,7 +308,18 @@ void MenuManager::checkSoundMenuClick(int x, int y) {
 }
 
 bool MenuManager::checkMainMenuMouse(int x, int y) {
-	debug("Checking main menu buttons");
+
+	// Inventory items
+	bool selectedItem = false;
+	for (int i = 0; i < 4; i++) {
+		Common::Rect itemRect = Common::Rect(_inventorySlots[i], 60, 60);
+		if (itemRect.contains(x, y)) {
+			selectedItem = selectInventoryItem(i);
+			return false;
+		}
+	}
+
+	// Buttons
 	MainMenuButton mainMenuButton = isMainMenuButtonUnder(x, y);
 	switch (mainMenuButton) {
 	case QUESTION_MARK_BUTTON:


Commit: b74947e4da36ec038190858f308fb62f9fb6c994
    https://github.com/scummvm/scummvm/commit/b74947e4da36ec038190858f308fb62f9fb6c994
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:36+02:00

Commit Message:
PELROCK: Allows loading from ScummVM's main menu

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 42c8dd6ca58..90ae2ff581d 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -202,7 +202,6 @@ void MenuManager::checkMouseDown(int x, int y) {
 }
 
 bool MenuManager::checkMouseClick(int x, int y) {
-	debug("Checking mouse click at %d, %d, with menu state %d", x, y, _menuState);
 
 	switch (_menuState) {
 	case MAIN_MENU: {
@@ -232,8 +231,7 @@ bool MenuManager::checkMouseClick(int x, int y) {
 		// CANCEL
 		if (_cancelarRect.contains(x, y)) {
 			_editingSaveSlot = -1;
-			_menuState = MAIN_MENU;
-			_menuText = _menuTexts[0];
+			backToMainMenu();
 			break;
 		}
 		// Save slot click: begin (or switch) editing
@@ -261,8 +259,7 @@ bool MenuManager::checkMouseClick(int x, int y) {
 		}
 		// CANCELAR
 		if (_cancelarRect.contains(x, y)) {
-			_menuState = MAIN_MENU;
-			_menuText = _menuTexts[0];
+			backToMainMenu();
 			break;
 		}
 		// Save slot click: load if slot has a save
@@ -271,6 +268,7 @@ bool MenuManager::checkMouseClick(int x, int y) {
 				int slot = _saveGamePage * 8 + i;
 				if (!_saveDescriptions[slot].empty()) {
 					g_engine->loadGameState(slot);
+					backToMainMenu();
 					return true;
 				}
 				else {
@@ -298,8 +296,8 @@ void MenuManager::checkSoundMenuClick(int x, int y) {
 		soundMenuButton != SFX_RIGHT_BUTTON &&
 		soundMenuButton != MASTER_LEFT_BUTTON &&
 		soundMenuButton != MASTER_RIGHT_BUTTON) {
-		_menuState = MAIN_MENU;
-		_menuText = _menuTexts[0];
+		backToMainMenu();
+		return;
 	}
 
 	if (soundMenuButton == SFX_LEFT_BUTTON || soundMenuButton == SFX_RIGHT_BUTTON) {
@@ -429,8 +427,7 @@ bool MenuManager::selectInventoryItem(int i) {
 void MenuManager::handleSaveMenuKey(Common::KeyCode key, uint16 ascii) {
 	if (key == Common::KEYCODE_ESCAPE) {
 		_editingSaveSlot = -1;
-		_menuState = MAIN_MENU;
-		_menuText = _menuTexts[0];
+		backToMainMenu();
 		return;
 	}
 	if (_editingSaveSlot < 0)
@@ -441,6 +438,7 @@ void MenuManager::handleSaveMenuKey(Common::KeyCode key, uint16 ascii) {
 		g_engine->saveGameState(_editingSaveSlot, _editingName);
 		_saveDescriptions[_editingSaveSlot] = _editingName;
 		_editingSaveSlot = -1;
+		backToMainMenu();
 	} else if (key == Common::KEYCODE_BACKSPACE) {
 		if (!_editingName.empty())
 			_editingName.deleteLastChar();
@@ -449,6 +447,11 @@ void MenuManager::handleSaveMenuKey(Common::KeyCode key, uint16 ascii) {
 	}
 }
 
+void MenuManager::backToMainMenu() {
+	_menuState = MAIN_MENU;
+	_menuText = _menuTexts[0];
+}
+
 void MenuManager::menuLoop() {
 
 	// Save screenshot in case the user saves
@@ -457,8 +460,7 @@ void MenuManager::menuLoop() {
 
 	g_system->getPaletteManager()->setPalette(_mainMenuPalette, 0, 256);
 	g_engine->changeCursor(DEFAULT);
-	_menuState = MAIN_MENU;
-	_menuText = _menuTexts[0];
+	backToMainMenu();
 
 	// Initialize volume levels from current settings
 	_sfxVolumeLevel = mixerVolumeToLevel(_sound->getVolumeSfx());
@@ -505,15 +507,13 @@ void MenuManager::menuLoop() {
 				handleSaveMenuKey(key, ascii);
 			} else if (_menuState == ORIGINAL_LOAD) {
 				if (key == Common::KEYCODE_ESCAPE) {
-					_menuState = MAIN_MENU;
-					_menuText = _menuTexts[0];
+					backToMainMenu();
 				}
 			} else if (_menuState == EXIT_GAME) {
 				if (key == Common::KEYCODE_s) {
 					g_engine->quitGame();
 				} else if (key == Common::KEYCODE_n) {
-					_menuState = MAIN_MENU;
-					_menuText = _menuTexts[0];
+					backToMainMenu();
 				}
 			}
 		}
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index a5acd16d8b5..15293a1157c 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -247,6 +247,7 @@ private:
 	void readButton(Common::File &alfred7, uint32 offset, byte *outBuffer[2], Common::Rect rect);
 	void refreshSaveDescriptions();
 	void handleSaveMenuKey(Common::KeyCode key, uint16 ascii);
+void backToMainMenu();
 	SoundMenuButton isSoundMenuButtonUnder(int x, int y);
 	MainMenuButton isMainMenuButtonUnder(int x, int y);
 	Graphics::Screen *_screen = nullptr;
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index df3a285d515..f16b6fcdeff 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -120,14 +120,15 @@ Common::Error PelrockEngine::run() {
 	// Set the engine's debugger console
 	setDebugger(new PelrockConsole(this));
 
+	_state->stateGame = shouldPlayIntro ? INTRO : GAME;
+
+	init();
+
 	// If a savegame was selected from the launcher, load it
 	int saveSlot = ConfMan.getInt("save_slot");
 	if (saveSlot != -1)
 		(void)loadGameState(saveSlot);
 
-	_state->stateGame = shouldPlayIntro ? INTRO : GAME;
-
-	init();
 	while (!shouldQuit()) {
 		if (_state->stateGame == SETTINGS) {
 			_graphics->clearScreen();


Commit: a5964bf279872fc0c33f92ba516d5e22c86e9ae0
    https://github.com/scummvm/scummvm/commit/a5964bf279872fc0c33f92ba516d5e22c86e9ae0
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:37+02:00

Commit Message:
PELROCK: Fixes extraneous characters on intro's subtitles

Changed paths:
    engines/pelrock/detection.h
    engines/pelrock/detection_tables.h
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h
    engines/pelrock/metaengine.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp
    engines/pelrock/video/video.cpp


diff --git a/engines/pelrock/detection.h b/engines/pelrock/detection.h
index b66e27e9a5c..921919f14e9 100644
--- a/engines/pelrock/detection.h
+++ b/engines/pelrock/detection.h
@@ -40,6 +40,7 @@ extern const ADGameDescription gameDescriptions[];
 
 #define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1
 #define GAMEOPTION_ALTERNATE_TIMING GUIO_GAMEOPTIONS2
+#define GAMEOPTION_PLAY_INTRO GUIO_GAMEOPTIONS3
 
 } // End of namespace Pelrock
 
diff --git a/engines/pelrock/detection_tables.h b/engines/pelrock/detection_tables.h
index 79c0987d677..57be38649ae 100644
--- a/engines/pelrock/detection_tables.h
+++ b/engines/pelrock/detection_tables.h
@@ -34,7 +34,7 @@ const ADGameDescription gameDescriptions[] = {
 		Common::ES_ESP,
 		Common::kPlatformDOS,
 		ADGF_UNSTABLE,
-		GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_ALTERNATE_TIMING)
+		GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_ALTERNATE_TIMING, GAMEOPTION_PLAY_INTRO)
 	},
 
 	AD_TABLE_END_MARKER
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 90ae2ff581d..af9af7135bc 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -308,11 +308,11 @@ void MenuManager::checkSoundMenuClick(int x, int y) {
 bool MenuManager::checkMainMenuMouse(int x, int y) {
 
 	// Inventory items
-	bool selectedItem = false;
+
 	for (int i = 0; i < 4; i++) {
 		Common::Rect itemRect = Common::Rect(_inventorySlots[i], 60, 60);
 		if (itemRect.contains(x, y)) {
-			selectedItem = selectInventoryItem(i);
+			selectInventoryItem(i);
 			return false;
 		}
 	}
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 15293a1157c..2077c0ee84e 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -247,7 +247,7 @@ private:
 	void readButton(Common::File &alfred7, uint32 offset, byte *outBuffer[2], Common::Rect rect);
 	void refreshSaveDescriptions();
 	void handleSaveMenuKey(Common::KeyCode key, uint16 ascii);
-void backToMainMenu();
+	void backToMainMenu();
 	SoundMenuButton isSoundMenuButtonUnder(int x, int y);
 	MainMenuButton isMainMenuButtonUnder(int x, int y);
 	Graphics::Screen *_screen = nullptr;
diff --git a/engines/pelrock/metaengine.cpp b/engines/pelrock/metaengine.cpp
index f659918398e..e44f3ab20cc 100644
--- a/engines/pelrock/metaengine.cpp
+++ b/engines/pelrock/metaengine.cpp
@@ -51,6 +51,17 @@ static const ADExtraGuiOptionsMap optionsList[] = {
 			0
 		}
 	},
+	{
+		GAMEOPTION_PLAY_INTRO,
+		{
+			_s("Play intro"),
+			_s("Play the game's intro sequence. Plays automatically the first time the game is launched."),
+			"play_intro",
+			false,
+			0,
+			0
+		}
+	},
 	AD_EXTRA_GUI_OPTIONS_TERMINATOR
 };
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index f16b6fcdeff..bd931f1d419 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -117,6 +117,9 @@ Common::Error PelrockEngine::run() {
 	_doubleSmallFont->load("ALFRED.4");
 	_videoManager = new VideoManager(_screen, _events, _chrono, _largeFont, _dialog, _sound);
 
+	bool playIntroFromConfig = ConfMan.getBool("play_intro");
+	bool hasLaunchedBefore = ConfMan.getBool("launched_before");
+	shouldPlayIntro = playIntroFromConfig || !hasLaunchedBefore;
 	// Set the engine's debugger console
 	setDebugger(new PelrockConsole(this));
 
@@ -138,7 +141,11 @@ Common::Error PelrockEngine::run() {
 			maybePlayPostIntro();
 			gameLoop();
 		} else if (_state->stateGame == INTRO) {
+			CursorMan.showMouse(false);
 			_videoManager->playIntro();
+			CursorMan.showMouse(true);
+			ConfMan.setBool("launched_before", true);
+			ConfMan.flushToDisk();
 			_state->setFlag(FLAG_FROM_INTRO, true);
 			_state->stateGame = GAME;
 		} else if (_state->stateGame == COMPUTER) {
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index fa229e1ef31..03231bab42a 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -690,7 +690,6 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		anim.targetZIndex = blank->zOrder + 1;
 		anim.resetCoord = blank->y;
 		anims->passerByAnims[0] = anim;
-		debug("Loaded passerby animation for room %d, direction = %d", roomNumber, anim.dir);
 		break;
 	}
 
@@ -843,8 +842,6 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 	default:
 		break;
 	}
-	if (anims != nullptr)
-		debug("Loaded passerby anims for room %d, count = %d", roomNumber, anims->numAnims);
 	return anims;
 }
 
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index cb36bc35ecc..0aace022921 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -70,7 +70,10 @@ void VideoManager::playIntro() {
 
 		ChunkHeader chunk;
 		readChunk(videoFile, chunk);
-		debug("Read chunk type %d at frame %d", chunk.chunkType, frameCounter);
+
+		if(_events->_lastKeyEvent == Common::KEYCODE_ESCAPE) {
+			break;
+		}
 
 		switch (chunk.chunkType) {
 		case 1:
@@ -133,11 +136,7 @@ void VideoManager::playIntro() {
 				_dialog->processColorAndTrim(lines, color);
 				Graphics::Surface s = _dialog->getDialogueSurface(lines, color);
 				_textSurface.transBlitFrom(s, Common::Point(subtitle->x, subtitle->y), 255);
-
-				drawPos(&_textSurface, subtitle->x, subtitle->y, color);
-				drawRect(&_textSurface, subtitle->x, subtitle->y,
-						 s.getRect().width(),
-						 s.getRect().height(), color);
+				s.free();
 			}
 
 			presentFrame();
@@ -285,15 +284,11 @@ void VideoManager::initMetadata() {
 			for (uint32 i = 0; i < numFiles; ++i) {
 				VoiceData sound;
 				Common::String filename = _introSndFile.readString();
-				// _introSndFile.skip(1);
 				sound.offset = _introSndFile.readUint32LE();
 				sound.length = _introSndFile.readUint32LE();
 				_sounds[filename] = sound;
-				debug("Loaded sound: '%s' (offset: %u, length: %u)", filename.c_str(), sound.offset, sound.length);
 			}
 		}
-
-		debug("Loaded %d sound entries", _sounds.size());
 	}
 
 	while (metadataFile.eos() == false) {
@@ -318,9 +313,6 @@ void VideoManager::initMetadata() {
 		}
 	}
 
-	debug("Loaded %d subtitles", _subtitles.size());
-	debug("Loaded %d speech files", _voiceEffect.size());
-
 	metadataFile.close();
 }
 
@@ -349,7 +341,6 @@ MusicEffect VideoManager::readMusicEffect(Common::File &metadataFile) {
 		}
 	}
 	music.trackNumber = atoi(buffer.c_str());
-	debug("Loaded music effect: frame %d, track %d", music.startFrame, music.trackNumber);
 	return music;
 }
 
@@ -378,7 +369,6 @@ AudioEffect VideoManager::readAudioEffect(Common::File &metadataFile) {
 		}
 	}
 	voice.filename = buffer;
-	debug("Loaded voice: frame %d, file '%s'", voice.startFrame, voice.filename.c_str());
 	return voice;
 }
 
@@ -389,8 +379,7 @@ Subtitle VideoManager::readSubtitle(Common::File &metadataFile) {
 	int valueIndex = 0;
 
 	// Skip spaces after "/t"
-	while (!metadataFile.eos() && metadataFile.readByte() == ' ')
-		;
+	while (!metadataFile.eos() && metadataFile.readByte() == ' ');
 	metadataFile.seek(-1, SEEK_CUR); // Step back one byte
 
 	// Parse 4 space-delimited numbers
@@ -412,7 +401,8 @@ Subtitle VideoManager::readSubtitle(Common::File &metadataFile) {
 			break;
 		}
 	}
-	metadataFile.skip(1); // Skip the extra space
+
+	// metadataFile.skip(1); // Skip the extra space
 
 	subtitle.startFrame = values[0];
 	subtitle.endFrame = values[1];
@@ -421,18 +411,34 @@ Subtitle VideoManager::readSubtitle(Common::File &metadataFile) {
 
 	// Read text until CRLF (0x0D 0x0A)
 	subtitle.text.clear();
+
+	// skip leading spaces in subtitle tex
+	byte nextByte;
+	do {
+		nextByte = metadataFile.readByte();
+	} while (nextByte == ' ' && !metadataFile.eos());
+
+	if(nextByte == 0x08) {
+		subtitle.text += '@';
+	} else {
+		subtitle.text += decodeChar(nextByte);
+	}
+
 	while (!metadataFile.eos()) {
-		char c = metadataFile.readByte();
 
+		char c = metadataFile.readByte();
 		if (c == 0x0D) {
 			char next = metadataFile.readByte();
 			if (next == 0x0A) {
 				break;
 			} else {
-				subtitle.text += c;
-				subtitle.text += next;
+				subtitle.text += decodeChar(c);
+				subtitle.text += decodeChar(next);
 			}
 		} else {
+			if(c == 0x00) {
+				// do nothing
+			}
 			if (c == 0x08)
 				subtitle.text += '@';
 			else


Commit: c1c9772c506950784e65318bae87fc158e90b403
    https://github.com/scummvm/scummvm/commit/c1c9772c506950784e65318bae87fc158e90b403
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:37+02:00

Commit Message:
PELROCK: Fixes bug (present in the original) where endless loop can happen during conversation

Changed paths:
    engines/pelrock/dialog.cpp


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index d13f0df33fc..65272c791ae 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -481,6 +481,11 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 bool DialogManager::checkAllSubBranchesExhausted(const byte *data, uint32 dataSize, uint32 startPos, int currentChoiceLevel) {
 	uint32 pos = startPos;
 
+	// Fix for room 26 where an endless loop will happen due to buggy command codes that casued the conversation
+	// to go back with a single choice and thus repeating over and over.
+	int room = g_engine->_room->_currentRoomNumber;
+	bool f0IsBoundary = (room == 26 && currentChoiceLevel == 2 && startPos < dataSize && data[startPos] == 0x0C);
+
 	while (pos < dataSize) {
 		byte b = data[pos];
 
@@ -493,6 +498,13 @@ bool DialogManager::checkAllSubBranchesExhausted(const byte *data, uint32 dataSi
 			break;
 		}
 
+		// For that one bug in room 26
+		// treat F0 as a boundary to prevent scanning past unreachable choices
+		if (b == CTRL_GO_BACK && f0IsBoundary) {
+			debug("checkAllSubBranchesExhausted: F0 boundary hit at pos %u (room %d fix)", pos, room);
+			break;
+		}
+
 		// Found FB (one-time choice marker)
 		if (b == CTRL_DIALOGUE_MARKER_ONEOFF && pos + 2 < dataSize) {
 			byte choiceIdx = data[pos + 1];


Commit: 67735982b71ff7ce02ff6c2173f12711cc5cb721
    https://github.com/scummvm/scummvm/commit/67735982b71ff7ce02ff6c2173f12711cc5cb721
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:37+02:00

Commit Message:
PELROCK: Fixes not being able to use stuff with Alfred from inventory overlay

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 65272c791ae..7bf07e9faed 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -501,7 +501,6 @@ bool DialogManager::checkAllSubBranchesExhausted(const byte *data, uint32 dataSi
 		// For that one bug in room 26
 		// treat F0 as a boundary to prevent scanning past unreachable choices
 		if (b == CTRL_GO_BACK && f0IsBoundary) {
-			debug("checkAllSubBranchesExhausted: F0 boundary hit at pos %u (room %d fix)", pos, room);
 			break;
 		}
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index bd931f1d419..e1f32fc90b6 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -632,7 +632,11 @@ void PelrockEngine::checkMouse() {
 		} else if (_inventoryOverlayState.isActive && _inventoryOverlayState.posInInventorySelectionArea(_events->_releaseX, _events->_releaseY)) {
 			int item = checkMouseClickInventoryOverlay(_events->_releaseX, _events->_releaseY);
 			_state->selectedInventoryItem = item;
-			walkAndAction(_currentHotspot, ITEM);
+			if(_actionPopupState.isAlfredUnder) {
+				useOnAlfred(item);
+			} else if (_currentHotspot != nullptr) {
+				walkAndAction(_currentHotspot, ITEM);
+			}
 		} else {
 			// Released outside popup - just close it
 			_queuedAction = QueuedAction{NO_ACTION, -1, false, false};
@@ -1341,18 +1345,24 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 
 int PelrockEngine::getScrollPositionForItem(int item) {
 	int selectedIndex = -1;
+	// assign selectedIndex to the index of the item
 	for (size_t i = 0; i < _state->inventoryItems.size(); i++) {
 		if (_state->inventoryItems[i] == item) {
 			selectedIndex = i;
 			break;
 		}
 	}
+	// take the selectedIndex as the starting pos for calculatiom, it should ALWAYS be visible at the end of the overlay, if enough items
 	if (selectedIndex != -1) {
-		// put the selected item at the end in the overlay
-		if (_state->inventoryItems.size() > kInventoryPageSize) {
-			selectedIndex = CLIP(selectedIndex, 0, (int)_state->inventoryItems.size() - kInventoryPageSize);
+		if (selectedIndex < _inventoryOverlayState.invStartingPos) {
+			selectedIndex = selectedIndex; // scroll left to show the selected item
+		} else if (selectedIndex >= _inventoryOverlayState.invStartingPos + kInventoryPageSize) {
+			selectedIndex = selectedIndex - kInventoryPageSize + 1; // scroll right to show the selected item
+		} else {
+			selectedIndex = _inventoryOverlayState.invStartingPos; // no scrolling needed, keep current starting pos
 		}
 	}
+
 	return selectedIndex;
 }
 
@@ -1401,6 +1411,7 @@ Common::Point getPositionInOverlayForIndex(uint index) {
 
 void PelrockEngine::pickupIconFlash() {
 	uint invSize = _state->inventoryItems.size();
+	// focus on the last positionin the inventory, where the newly picked up item would be, if there is at least 1 item in the inventory
 	_inventoryOverlayState.invStartingPos = getScrollPositionForItem(_state->inventoryItems[invSize - 1]);
 	_inventoryOverlayState.flashingIconIndex = invSize - 1;
 	showInventoryOverlay();


Commit: 780c84181b3b087f2612ca7c1b15cf5118ca639c
    https://github.com/scummvm/scummvm/commit/780c84181b3b087f2612ca7c1b15cf5118ca639c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:38+02:00

Commit Message:
PELROCK: Fix sounds in inventory to leave click as default sound

Changed paths:
    engines/pelrock/menu.h


diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 2077c0ee84e..6cc811609d2 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -102,7 +102,7 @@ static const char *inventorySounds[113] = {
 	"ELEC3ZZZ.SMP", // 6 - Electric zap
 	"REMATERL.SMP", // 7 - Rematerialize
 	"81ZZZZZZ.SMP", // 8 - (numbered SFX)
-	"HOJASZZZ.SMP",
+	"11ZZZZZZ.SMP", // 9
 	"SSSHTZZZ.SMP", // 10 - Shushing
 	"HOJASZZZ.SMP", // 11 - Default leaf rustle
 	"HOJASZZZ.SMP",
@@ -163,46 +163,46 @@ static const char *inventorySounds[113] = {
 	"BOOOOOIZ.SMP", // 67 - Boing
 	"DISCOSZZ.SMP", // 68 - Disco music
 	"MONORLZZ.SMP", // 69 - Monorail
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
+	"11ZZZZZZ.SMP", // 70
+	"11ZZZZZZ.SMP", // 71
+	"11ZZZZZZ.SMP", // 72
 	"CARACOLA.SMP", // 73 - Seashell
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
+	"11ZZZZZZ.SMP", // 74
+	"11ZZZZZZ.SMP", // 75
 	"WATER_2Z.SMP", // 76 - Water splash
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
+	"11ZZZZZZ.SMP", // 77
+	"11ZZZZZZ.SMP", // 78
 	"EEEEKZZZ.SMP", // 79 - Shriek
 	"REMATERL.SMP", // 80 - Rematerialize
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
+	"11ZZZZZZ.SMP", // 81
+	"11ZZZZZZ.SMP", // 82
 	"ELVIS1ZZ.SMP", // 83 - Elvis impression
 	"RIMSHOTZ.SMP", // 84 - Rimshot
-	"HOJASZZZ.SMP",
+	"11ZZZZZZ.SMP", // 85
 	"WATER_2Z.SMP", // 86 - Water splash
 	"MOTOSZZZ.SMP", // 87 - Motorcycle
 	"HOJASZZZ.SMP",
 	"TWANGZZZ.SMP", // 89 - Twang
-	"HOJASZZZ.SMP",
+	"11ZZZZZZ.SMP", // 90
 	"QUAKE2ZZ.SMP", // 91 - Earthquake
-	"HOJASZZZ.SMP",
+	"11ZZZZZZ.SMP", // 92
 	"SORBOZZZ.SMP", // 93 - Slurp
 	"BOTEZZZZ.SMP", // 94 - Bottle sound
 	"ELVIS1ZZ.SMP", // 95 - Elvis impression
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP", // 96
+	"HOJASZZZ.SMP", // 97
+	"HOJASZZZ.SMP", // 98
+	"11ZZZZZZ.SMP", // 99
 	"LLAVESZZ.SMP", // 100 - Keys jingling
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP", // 101
+	"11ZZZZZZ.SMP", // 102
+	"11ZZZZZZ.SMP", // 103
 	"EVLLAUGH.SMP", // 104 - Evil laugh
-	"HOJASZZZ.SMP",
+	"11ZZZZZZ.SMP", // 105
 	"BURROLZZ.SMP", // 106 - Donkey bray
-	"HOJASZZZ.SMP",
+	"11ZZZZZZ.SMP", // 107
 	"TWANGZZZ.SMP", // 108
-	"HOJASZZZ.SMP",
+	"11ZZZZZZ.SMP", // 109
 	"TWANGZZZ.SMP", // 110
 	"ELVIS1ZZ.SMP", // 111 - Elvis impression
 	"SEX3ZZZZ.SMP"  // 112 - Suggestive sound


Commit: 13834000a97a417e459786e64f16fc94e472710c
    https://github.com/scummvm/scummvm/commit/13834000a97a417e459786e64f16fc94e472710c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:38+02:00

Commit Message:
PELROCK: Scale tile shuffling with tile count in sliding puzzle minigame

Changed paths:
    engines/pelrock/graphics.cpp
    engines/pelrock/slidingpuzzle.cpp


diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index fda9b0e597c..6b7c7c325cb 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -156,7 +156,6 @@ void GraphicsManager::drawColoredText(Graphics::ManagedSurface *screen, const Co
 		if (text[i] == '@' && i + 1 < text.size()) {
 			// Draw accumulated segment
 			if (!segment.empty()) {
-				debug("Drawing text segment '%s' at %d, %d with color %d", segment.c_str(), currentX, y, defaultColor);
 				font->drawString(screen, segment, currentX, y, w, defaultColor);
 				currentX += font->getStringWidth(segment);
 				segment.clear();
diff --git a/engines/pelrock/slidingpuzzle.cpp b/engines/pelrock/slidingpuzzle.cpp
index 31aacc04e9c..a54b1f8ebdc 100644
--- a/engines/pelrock/slidingpuzzle.cpp
+++ b/engines/pelrock/slidingpuzzle.cpp
@@ -160,9 +160,16 @@ void SlidingPuzzle::playTileSound() {
 
 void SlidingPuzzle::shuffleLoop() {
 	const int shuffleRange = _totalTiles - 1; // never touch the blank slot (N-1)
+
+	// scale tile swap with tile count
+	const int kBaseTileCount = (kPuzzleScreenWidth / kTileSizes[0]) *
+	                           (kPuzzleScreenHeight / kTileSizes[0]);
+	const int swapsPerFrame = MAX(1, _totalTiles / kBaseTileCount);
+
 	while (!g_engine->shouldQuit()) {
 		_events->pollEvent();
 
+		for (int s = 0; s < swapsPerFrame; s++) {
 		int a, b;
 		do {
 			a = g_engine->getRandomNumber(shuffleRange - 1);


Commit: e31fce39dda84478879dba57109a2095263c37b1
    https://github.com/scummvm/scummvm/commit/e31fce39dda84478879dba57109a2095263c37b1
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:38+02:00

Commit Message:
PELROCK: Improves anti-piracy joke

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/slidingpuzzle.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 056edc609d1..5c763fb009c 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -19,6 +19,9 @@
  *
  */
 
+#include "audio/audiostream.h"
+#include "audio/decoders/raw.h"
+#include "audio/mixer.h"
 #include "graphics/paletteman.h"
 
 #include "pelrock.h"
@@ -2407,94 +2410,59 @@ void PelrockEngine::pickUpMatches(HotSpot *hotspot) {
 }
 
 /**
- * Original behavior:
- * 1. Stop all sound
- * 2. Loop: corrupt the background buffer pointer with random values,
- *    copy garbage to screen, write sequential memory bytes to PC speaker
- *    port 0x61 to produce noise
- * 3. On keypress: divide by zero -> crash to DOS
- *
- * ScummVM behavior:
- * 1. Stop all sound
- * 2. Loop: fill screen with random pixels, play white noise
- * 3. On keypress: return to launcher
+ *  Simulates white noise and "crashes" the game
  */
 void PelrockEngine::antiPiracyEffect() {
 	_sound->stopAllSounds();
 	_sound->stopMusic();
 
-	// Generate a buffer of white noise for the PC speaker simulation
-	const int kNoiseLength = 16000;                // 1 second at 8kHz
-	byte *noiseData = new byte[kNoiseLength + 44]; // WAV header + data
-
-	// Write a minimal WAV header
-	memcpy(noiseData, "RIFF", 4);
-	WRITE_LE_UINT32(noiseData + 4, kNoiseLength + 36);
-	memcpy(noiseData + 8, "WAVE", 4);
-	memcpy(noiseData + 12, "fmt ", 4);
-	WRITE_LE_UINT32(noiseData + 16, 16);   // chunk size
-	WRITE_LE_UINT16(noiseData + 20, 1);    // PCM format
-	WRITE_LE_UINT16(noiseData + 22, 1);    // mono
-	WRITE_LE_UINT32(noiseData + 24, 8000); // sample rate
-	WRITE_LE_UINT32(noiseData + 28, 8000); // byte rate
-	WRITE_LE_UINT16(noiseData + 32, 1);    // block align
-	WRITE_LE_UINT16(noiseData + 34, 8);    // bits per sample
-	memcpy(noiseData + 36, "data", 4);
-	WRITE_LE_UINT32(noiseData + 40, kNoiseLength);
-
-	// Fill with random noise (simulating garbage bytes written to port 0x61)
-	byte curNoise = (byte)getRandomNumber(255);
+	// Generate noise
+	const int kNoiseLength = 8000; // ~1 second at 8000 Hz, will loop
+	byte *noiseData = (byte *)malloc(kNoiseLength);
 	for (int i = 0; i < kNoiseLength; i++) {
-		noiseData[44 + i] = curNoise;
-		bool changeNoise = getRandomNumber(10) < 2; // 20% chance to change noise value
-		if (changeNoise) {
-			curNoise = (byte)getRandomNumber(255);
-		}
+		noiseData[i] = (byte)((getRandomNumber(255)));
 	}
 
-	// Play the noise
-	_sound->playSound(noiseData, kNoiseLength + 44, 0);
+	// Create a looping raw audio stream with the random noise data
+	Audio::SeekableAudioStream *rawStream = Audio::makeRawStream(noiseData, kNoiseLength, 8000, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
+	Audio::AudioStream *loopStream = Audio::makeLoopingAudioStream(rawStream, 0);
 
-	byte *screenPixels = (byte *)_screen->getPixels();
-	int screenSize = _screen->pitch * _screen->h;
+	Audio::SoundHandle noiseHandle;
+	g_system->getMixer()->playStream(Audio::Mixer::kSFXSoundType, &noiseHandle, loopStream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::YES);
 
-	// Clear any pending key event before starting the loop
-	_events->_lastKeyEvent = Common::KEYCODE_INVALID;
+	// Set a grayscale palette so random pixel values produce snow effect
+	byte grayPalette[256 * 3];
+	for (int i = 0; i < 256; i++) {
+		grayPalette[i * 3 + 0] = (byte)i; // R
+		grayPalette[i * 3 + 1] = (byte)i; // G
+		grayPalette[i * 3 + 2] = (byte)i; // B
+	}
+	g_system->getPaletteManager()->setPalette(grayPalette, 0, 256);
+
+	byte *screenPixels = (byte *)_screen->getPixels();
 
 	while (!shouldQuit()) {
 		_events->pollEvent();
 
 		if (_events->_lastKeyEvent != Common::KEYCODE_INVALID) {
-			break;
+			g_system->getMixer()->stopHandle(noiseHandle);
+			g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
+			// Original divides by zero to intentionally crash to DOS.
+			// We exit the game instead
+			g_engine->quitGame();
 		}
 
-		// generate random pixels on the screen (simulating corrupted video memory)
+		// generate white-noise like scene
+		int screenSize = 640 * 400;
 		for (int i = 0; i < screenSize; i++) {
 			screenPixels[i] = (byte)getRandomNumber(255);
 		}
 
-		// Regenerate noise periodically
-		for (int i = 0; i < kNoiseLength; i++) {
-			noiseData[44 + i] = curNoise;
-			bool changeNoise = getRandomNumber(10) < 2; // 20% chance to change noise value
-			if (changeNoise) {
-				curNoise = (byte)getRandomNumber(255);
-			}
-		}
-		if (!_sound->isPlaying()) {
-			_sound->playSound(noiseData, kNoiseLength + 44, 0);
-		}
-
 		_screen->markAllDirty();
 		_screen->update();
-		g_system->delayMillis(50);
+		g_system->delayMillis(10);
 	}
 
-	_sound->stopAllSounds();
-	delete[] noiseData;
-
-	// Return to launcher
-	Engine::quitGame();
 }
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index e1f32fc90b6..68bf1dd8f01 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1355,7 +1355,7 @@ int PelrockEngine::getScrollPositionForItem(int item) {
 	// take the selectedIndex as the starting pos for calculatiom, it should ALWAYS be visible at the end of the overlay, if enough items
 	if (selectedIndex != -1) {
 		if (selectedIndex < _inventoryOverlayState.invStartingPos) {
-			selectedIndex = selectedIndex; // scroll left to show the selected item
+			// do nothing
 		} else if (selectedIndex >= _inventoryOverlayState.invStartingPos + kInventoryPageSize) {
 			selectedIndex = selectedIndex - kInventoryPageSize + 1; // scroll right to show the selected item
 		} else {
@@ -1539,6 +1539,11 @@ void PelrockEngine::gameLoop() {
 		puzzle.run();
 	}
 
+	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_h) {
+		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
+		antiPiracyEffect();
+	}
+
 	renderScene();
 	_screen->update();
 }
diff --git a/engines/pelrock/slidingpuzzle.cpp b/engines/pelrock/slidingpuzzle.cpp
index a54b1f8ebdc..0baf21f30be 100644
--- a/engines/pelrock/slidingpuzzle.cpp
+++ b/engines/pelrock/slidingpuzzle.cpp
@@ -170,13 +170,14 @@ void SlidingPuzzle::shuffleLoop() {
 		_events->pollEvent();
 
 		for (int s = 0; s < swapsPerFrame; s++) {
-		int a, b;
-		do {
-			a = g_engine->getRandomNumber(shuffleRange - 1);
-			b = g_engine->getRandomNumber(shuffleRange - 1);
-		} while (a == b);
+			int a, b;
+			do {
+				a = g_engine->getRandomNumber(shuffleRange - 1);
+				b = g_engine->getRandomNumber(shuffleRange - 1);
+			} while (a == b);
 
-		swapTiles(a, b);
+			swapTiles(a, b);
+		}
 		present();
 		playTileSound();
 


Commit: b6f5b1bd5ad5e1521406208fa0d7bd45a7851773
    https://github.com/scummvm/scummvm/commit/b6f5b1bd5ad5e1521406208fa0d7bd45a7851773
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:38+02:00

Commit Message:
PELROCK: Fixes bug that would make travel agency inaccesible

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/graphics.cpp
    engines/pelrock/sound.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 5c763fb009c..f6aab593323 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -2370,11 +2370,13 @@ void PelrockEngine::checkObjectsForPart2() {
 	if (_state->hasInventoryItem(17) &&
 		_state->hasInventoryItem(59) &&
 		_state->hasInventoryItem(24)) {
-		_room->addStickerToRoom(19, 54, PERSIST_BOTH);
-		_room->addStickerToRoom(19, 55, PERSIST_BOTH);
-		_room->addStickerToRoom(19, 56, PERSIST_BOTH);
-		_room->addStickerToRoom(19, 58, PERSIST_BOTH);
-		_state->setFlag(FLAG_AGENCIA_ABIERTA, true);
+		if(_state->getFlag(FLAG_AGENCIA_ABIERTA) == false) {
+			_room->addStickerToRoom(19, 54, PERSIST_BOTH);
+			_room->addStickerToRoom(19, 55, PERSIST_BOTH);
+			_room->addStickerToRoom(19, 56, PERSIST_BOTH);
+			_room->addStickerToRoom(19, 58, PERSIST_BOTH);
+			_state->setFlag(FLAG_AGENCIA_ABIERTA, true);
+		}
 	}
 }
 
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 7bf07e9faed..ee8e99f5b27 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -279,7 +279,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		}
 
 		_screen->transBlitFrom(s, s.getRect(), Common::Point(xPos, yPos), 255);
-		drawPos(_screen, xPos, yPos, speakerId);
+		// drawPos(_screen, xPos, yPos, speakerId);
 
 		_screen->markAllDirty();
 		_screen->update();
@@ -1243,7 +1243,6 @@ Common::Array<Common::StringArray> DialogManager::wordWrap(Common::StringArray t
 	for (uint i = 0; i < texts.size(); i++) {
 		Common::String thisLine = texts[i];
 		Common::Array<Common::Array<Common::String>> wrapped = wordWrap(thisLine);
-		debug("Wrapped line %s, %d into %d pages", thisLine.c_str(), thisLine.size(), wrapped.size());
 		for (uint j = 0; j < wrapped.size(); j++) {
 			for (int k = 0; k < wrapped[j].size(); k++) {
 				if (currentLineNum < kMaxLines) {
diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index 6b7c7c325cb..eb8d2b6585e 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -232,7 +232,7 @@ void GraphicsManager::copyBackgroundToBuffer() {
 
 void GraphicsManager::presentFrame() {
 	g_engine->_screen->blitFrom(g_engine->_compositeBuffer);
-	g_engine->paintDebugLayer();
+	// g_engine->paintDebugLayer();
 	g_engine->_screen->markAllDirty();
 }
 
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 151045cd902..80c48612f54 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -40,102 +40,103 @@
 namespace Pelrock {
 
 const char *SOUND_FILENAMES[] = {
-	"NO_SOUND.SMP", // 0 - Silence/disabled
-	"BUHO_ZZZ.SMP", // 1 - Owl
-	"BIRD_1_1.SMP", // 2 - Bird variant 1
-	"BIRD_1_2.SMP", // 3 - Bird variant 2
-	"BIRD_1_3.SMP", // 4 - Bird variant 3
-	"DESPERZZ.SMP", // 5 - Yawn/stretch
-	"HORN_5ZZ.SMP", // 6 - Car horn 5
-	"HORN_6ZZ.SMP", // 7 - Car horn 6
-	"HORN_8ZZ.SMP", // 8 - Car horn 8
-	"SUZIPASS.SMP", // 9 - Suzi passing
-	"CAT_1ZZZ.SMP", // 10 - Cat
-	"DOG_01ZZ.SMP", // 11 - Dog bark 1
-	"DOG_02ZZ.SMP", // 12 - Dog bark 2
-	"DOG_04ZZ.SMP", // 13 - Dog bark 4
-	"DOG_05ZZ.SMP", // 14 - Dog bark 5
-	"DOG_06ZZ.SMP", // 15 - Dog bark 6
-	"DOG_07ZZ.SMP", // 16 - Dog bark 7
-	"DOG_09ZZ.SMP", // 17 - Dog bark 9
-	"ALARMZZZ.SMP", // 18 - Alarm
-	"AMBULAN1.SMP", // 19 - Ambulance
-	"FOUNTAIN.SMP", // 20 - Fountain
-	"GRILLOSZ.SMP", // 21 - Crickets
-	"HOJASZZZ.SMP", // 22 - Leaves rustling
-	"FLASHZZZ.SMP", // 23 - Flash/camera
-	"CUCHI1ZZ.SMP", // 24 - Knife 1
-	"KNRRRRRZ.SMP", // 25 - Snoring
-	"PHONE_02.SMP", // 26 - Phone ring 2
-	"PHONE_03.SMP", // 27 - Phone ring 3
-	"SSSHTZZZ.SMP", // 28 - Shush/quiet
-	"BURGUER1.SMP", // 29 - Burger sizzle
-	"FLIES_2Z.SMP", // 30 - Flies buzzing
-	"PARRILLA.SMP", // 31 - Grill
-	"WATER_2Z.SMP", // 32 - Water
-	"XIQUETZZ.SMP", // 33 - Whistle
-	"RONQUIZZ.SMP", // 34 - Snoring
-	"MOCO1ZZZ.SMP", // 35 - Snot/mucus 1
-	"MOCO2ZZZ.SMP", // 36 - Snot/mucus 2
-	"SPRINGZZ.SMP", // 37 - Spring bounce
-	"MARUJASZ.SMP", // 38 - Gossip/chatter
-	"ELECTROZ.SMP", // 39 - Electric shock
-	"GLASS1ZZ.SMP", // 40 - Glass clink
-	"OPDOORZZ.SMP", // 41 - Door open
-	"CLDOORZZ.SMP", // 42 - Door close
-	"FXH2ZZZZ.SMP", // 43 - Effect 2
-	"BOTEZZZZ.SMP", // 44 - Bottle
-	"ELEC3ZZZ.SMP", // 45 - Electric 3
-	"AJARLZZZ.SMP", // 46 - Ajar/creak
-	"BELCHZZZ.SMP", // 47 - Belch/burp
-	"64ZZZZZZ.SMP", // 48 - Sound effect 64
-	"BIRDOWL2.SMP", // 49 - Bird/owl 2
-	"BUBBLE2Z.SMP", // 50 - Bubbles
-	"BURGUER1.SMP", // 51 - Burger (duplicate)
-	"CACKLEZZ.SMP", // 52 - Cackle/laugh
-	"CERAMIC1.SMP", // 53 - Ceramic break
-	"CLANG5ZZ.SMP", // 54 - Metal clang
-	"CUCHI2ZZ.SMP", // 55 - Knife 2
-	"CUCHI3ZZ.SMP", // 56 - Knife 3
-	"ELEC3ZZZ.SMP", // 57 - Electric 3 (duplicate)
-	"HOJASZZZ.SMP", // 58 - Leaves (duplicate)
-	"LIMA1ZZZ.SMP", // 59 - File/rasp
-	"MOROSZZZ.SMP", // 60 - Moors/crowd
-	"MOROZZZZ.SMP", // 61 - Moor/crowd
-	"MUD1ZZZZ.SMP", // 62 - Mud squelch
-	"PICOZZZZ.SMP", // 63 - Pickaxe
-	"PICO1XZZ.SMP", // 64 - Pickaxe 1
-	"PICO2XZZ.SMP", // 65 - Pickaxe 2
-	"PICO3XZZ.SMP", // 66 - Pickaxe 3
-	"RIMSHOTZ.SMP", // 67 - Rimshot drum
-	"RONCOZZZ.SMP", // 68 - Snoring
-	"SORBOZZZ.SMP", // 69 - Slurp/sip
-	"VIENTO1Z.SMP", // 70 - Wind
-	"2ZZZZZZZ.SMP", // 71 - Sound 2
-	"20ZZZZZZ.SMP", // 72 - Sound 20
-	"21ZZZZZZ.SMP", // 73 - Sound 21
-	"23ZZZZZZ.SMP", // 74 - Sound 23
-	"107ZZZZZ.SMP", // 75 - Sound 107
-	"39ZZZZZZ.SMP", // 76 - Sound 39
-	"81ZZZZZZ.SMP", // 77 - Sound 81
-	"88ZZZZZZ.SMP", // 78 - Sound 88
-	"92ZZZZZZ.SMP", // 79 - Sound 92
-	"SAW_2ZZZ.SMP", // 80 - Saw
-	"QUAKE2ZZ.SMP", // 81 - Earthquake
-	"ROCKSZZZ.SMP", // 82 - Rocks falling
-	"IN_FIREZ.SMP", // 83 - Fire
-	"BEAMZZZZ.SMP", // 84 - Beam/ray
-	"GLISSDWN.SMP", // 85 - Glissando down
-	"REMATERL.SMP", // 86 - Rematerialize
-	"FXH1ZZZZ.SMP", // 87 - Effect 1
-	"FXH3ZZZZ.SMP", // 88 - Effect 3
-	"FXH4ZZZZ.SMP", // 89 - Effect 4
-	"MATCHZZZ.SMP", // 90 - Match strike
-	"SURF_01Z.SMP", // 91 - Surf wave 1
-	"SURF_02Z.SMP", // 92 - Surf wave 2
-	"SURF_04Z.SMP", // 93 - Surf wave 4
-	"TWANGZZZ.SMP", // 94 - Twang
-	"LANDCRAS.SMP", // 95 - Crash landing
+	"NO_SOUND.SMP",
+	"BUHO_ZZZ.SMP",
+	"BIRD_1_1.SMP",
+	"BIRD_1_2.SMP",
+	"BIRD_1_3.SMP",
+	"DESPERZZ.SMP",
+	"HORN_5ZZ.SMP",
+	"HORN_6ZZ.SMP",
+	"HORN_8ZZ.SMP",
+	"SUZIPASS.SMP",
+	"CAT_1ZZZ.SMP",
+	"DOG_01ZZ.SMP",
+	"DOG_02ZZ.SMP",
+	"DOG_04ZZ.SMP",
+	"DOG_05ZZ.SMP",
+	"DOG_06ZZ.SMP",
+	"DOG_07ZZ.SMP",
+	"DOG_09ZZ.SMP",
+	"ALARMZZZ.SMP",
+	"AMBULAN1.SMP",
+	"FOUNTAIN.SMP",
+	"GRILLOSZ.SMP",
+	"HOJASZZZ.SMP",
+	"FLASHZZZ.SMP",
+	"CUCHI1ZZ.SMP",
+	"KNRRRRRZ.SMP",
+	"PHONE_02.SMP",
+	"PHONE_03.SMP",
+	"SSSHTZZZ.SMP",
+	"BURGUER1.SMP",
+	"FLIES_2Z.SMP",
+	"PARRILLA.SMP",
+	"WATER_2Z.SMP",
+	"XIQUETZZ.SMP",
+	"RONQUIZZ.SMP",
+	"MOCO1ZZZ.SMP",
+	"MOCO2ZZZ.SMP",
+	"SPRINGZZ.SMP",
+	"MARUJASZ.SMP",
+	"ELECTROZ.SMP",
+	"GLASS1ZZ.SMP",
+	"OPDOORZZ.SMP",
+	"CLDOORZZ.SMP",
+	"FXH2ZZZZ.SMP",
+	"BOTEZZZZ.SMP",
+	"ELEC3ZZZ.SMP",
+	"AJARLZZZ.SMP",
+	"BELCHZZZ.SMP",
+	"64ZZZZZZ.SMP",
+	"BIRDOWL2.SMP",
+	"BUBBLE2Z.SMP",
+	"BURGUER1.SMP",
+	"CACKLEZZ.SMP",
+	"CERAMIC1.SMP",
+	"CLANG5ZZ.SMP",
+	"CUCHI2ZZ.SMP",
+	"CUCHI3ZZ.SMP",
+	"ELEC3ZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"LIMA1ZZZ.SMP",
+	"MOROSZZZ.SMP",
+	"MOROZZZZ.SMP",
+	"MUD1ZZZZ.SMP",
+	"PICOZZZZ.SMP",
+	"PICO1XZZ.SMP",
+	"PICO2XZZ.SMP",
+	"PICO3XZZ.SMP",
+	"RIMSHOTZ.SMP",
+	"RONCOZZZ.SMP",
+	"SORBOZZZ.SMP",
+	"VIENTO1Z.SMP",
+	"2ZZZZZZZ.SMP",
+	"20ZZZZZZ.SMP",
+	"21ZZZZZZ.SMP",
+	"23ZZZZZZ.SMP",
+	"107ZZZZZ.SMP",
+	"39ZZZZZZ.SMP",
+	"81ZZZZZZ.SMP",
+	"88ZZZZZZ.SMP",
+	"92ZZZZZZ.SMP",
+	"SAW_2ZZZ.SMP",
+	"QUAKE2ZZ.SMP",
+	"ROCKSZZZ.SMP",
+	"IN_FIREZ.SMP",
+	"BEAMZZZZ.SMP",
+	"GLISSDWN.SMP",
+	"REMATERL.SMP",
+	"FXH1ZZZZ.SMP",
+	"FXH3ZZZZ.SMP",
+	"FXH4ZZZZ.SMP",
+	"MATCHZZZ.SMP",
+	"SURF_01Z.SMP",
+	"SURF_02Z.SMP",
+	"SURF_04Z.SMP",
+	"TWANGZZZ.SMP",
+	"LANDCRAS.SMP",
+	"KKKKKKKK.SMP",
 };
 
 SoundManager::SoundManager(Audio::Mixer *mixer)


Commit: d3e3cb2d7dda74b5e513b7b1edfc043fb3763c55
    https://github.com/scummvm/scummvm/commit/d3e3cb2d7dda74b5e513b7b1edfc043fb3763c55
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:39+02:00

Commit Message:
PELROCK: Fix extraneous idle Alfred showing up after being eaten by croc

Changed paths:
    engines/pelrock/actions.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index f6aab593323..92f981f4094 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1230,9 +1230,9 @@ void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
 		_alfredState.x -= 10;
 		_alfredState.y += 20;
 		playAlfredSpecialAnim(5);
+		_alfredState.animState = ALFRED_SKIP_DRAWING;
 		_sound->playSound(_room->_roomSfx[0], 0); // Belch
 		waitForSoundEnd();
-		_alfredState.animState = ALFRED_SKIP_DRAWING;
 		_graphics->fadeToBlack(10);
 		// update conversaton state
 		_alfredState.x = 300;


Commit: 027a91d16bb14e270b81127a5770c1ce08e99a15
    https://github.com/scummvm/scummvm/commit/027a91d16bb14e270b81127a5770c1ce08e99a15
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:39+02:00

Commit Message:
PELROCK: Ensure same ambient sound doesnt overlap with itself

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 68bf1dd8f01..572239ba639 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -275,7 +275,10 @@ void PelrockEngine::playSoundIfNeeded() {
 		if (roomSoundIndex < _room->_roomSfx.size()) {
 			byte soundFileIndex = _room->_roomSfx[roomSoundIndex];
 			if (soundFileIndex != 0) { // 0 = NO_SOUND.SMP (disabled)
-				_sound->playSound(soundFileIndex);
+				// Don't play the same sound twice at the same time
+				if (!_sound->isSoundIndexPlaying(soundFileIndex)) {
+					_sound->playSound(soundFileIndex);
+				}
 			}
 		}
 	}
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 80c48612f54..e575d8410fd 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -143,6 +143,7 @@ SoundManager::SoundManager(Audio::Mixer *mixer)
 	: _mixer(mixer), _currentVolume(128) {
 	// TODO: Initialize sound manager
 	g_system->getAudioCDManager()->open();
+	memset(_sfxSoundIndex, 0xFF, sizeof(_sfxSoundIndex));
 }
 
 SoundManager::~SoundManager() {
@@ -154,7 +155,9 @@ void SoundManager::playSound(byte index, int channel, int loopCount) {
 	// debug("Playing sound index %d (%s)", index, SOUND_FILENAMES[index]);
 	auto it = _soundMap.find(SOUND_FILENAMES[index]);
 	if (it != _soundMap.end()) {
-		playSound(it->_value, channel, loopCount);
+		int usedChannel = playSound(it->_value, channel, loopCount);
+		if (usedChannel >= 0 && usedChannel < kMaxChannels)
+			_sfxSoundIndex[usedChannel] = index;
 	} else {
 		debug("Sound file %s not found in sound map", SOUND_FILENAMES[index]);
 	}
@@ -169,11 +172,11 @@ void SoundManager::playSound(const char *filename, int channel, int loopCount) {
 	}
 }
 
-void SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
+int SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
 	Common::File sonidosFile;
 	if (!sonidosFile.open(Common::Path("SONIDOS.DAT"))) {
 		debug("Failed to open SONIDOS.DAT");
-		return;
+		return -1;
 	}
 
 	sonidosFile.seek(sound.offset, SEEK_SET);
@@ -206,7 +209,7 @@ void SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
 	} else {
 		debug("Unknown sound format on sound with name %s at offset %d, with size %d", sound.filename.c_str(), sound.offset, sound.size);
 		delete[] data;
-		return;
+		return -1;
 	}
 
 	if (stream) {
@@ -222,7 +225,9 @@ void SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
 		Audio::AudioStream *finalStream = loopCount != -1 ? stream : Audio::makeLoopingAudioStream(stream, 0);
 
 		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], finalStream, -1, _currentVolume, 0, DisposeAfterUse::YES);
+		return channel;
 	}
+	return -1;
 }
 
 void SoundManager::playSound(byte *soundData, uint32 size, int channel) {
@@ -281,6 +286,14 @@ int SoundManager::findFreeChannel() {
 	return 0;
 }
 
+bool SoundManager::isSoundIndexPlaying(byte index) const {
+	for (int i = 0; i < kMaxChannels; i++) {
+		if (_sfxSoundIndex[i] == index && _mixer->isSoundHandleActive(_sfxHandles[i]))
+			return true;
+	}
+	return false;
+}
+
 void SoundManager::stopAllSounds() {
 	for (int i = 0; i < kMaxChannels; i++) {
 		_mixer->stopHandle(_sfxHandles[i]);
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index 4a0f6053e50..407abf114c3 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -75,6 +75,7 @@ public:
 
 	bool isPlaying() const;
 	bool isPlaying(int channel) const;
+	bool isSoundIndexPlaying(byte index) const;
 	void loadSoundIndex();
 
 	bool isMusicPlaying();
@@ -94,7 +95,7 @@ public:
 	byte getCurrentMusicTrack() const { return _currentMusicTrack; }
 
 private:
-	void playSound(SonidoFile sound, int channel = -1, int loopCount = 1);
+	int playSound(SonidoFile sound, int channel = -1, int loopCount = 1);
 	SoundFormat detectFormat(byte *data, uint32 size);
 	int getSampleRate(byte *data, SoundFormat format);
 	int findFreeChannel();
@@ -104,6 +105,7 @@ private:
 	int _currentVolume;
 	Audio::SoundHandle _musicHandle;
 	Audio::SoundHandle _sfxHandles[kMaxChannels];
+	byte _sfxSoundIndex[kMaxChannels]; // tracks which sound index is on each channel (0xFF = none)
 	Common::HashMap<Common::String, SonidoFile> _soundMap;
 	bool _isPaused = false;
 	byte _currentMusicTrack = 0;


Commit: ecd269a800236465b2fef66085602f301fad88db
    https://github.com/scummvm/scummvm/commit/ecd269a800236465b2fef66085602f301fad88db
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:39+02:00

Commit Message:
PELROCK: Clear inventory before time travelling

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 92f981f4094..552a3738aed 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1790,9 +1790,13 @@ void PelrockEngine::magicFormula(int inventoryObject, HotSpot *hotspot) {
 	}
 	_state->setFlag(FLAG_FORMULA_MAGICA, _state->getFlag(FLAG_FORMULA_MAGICA) + 1);
 	if (_state->getFlag(FLAG_FORMULA_MAGICA) == 4) {
-
 		smokeAnimation(-1);
 		_alfredState.setState(ALFRED_IDLE);
+		_state->clearInventory();
+		_state->addInventoryItem(88);
+		_state->addInventoryItem(76);
+		_state->addInventoryItem(82);
+
 		setScreenAndPrepare(39, ALFRED_UP);
 	}
 }
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 7bc1d91b7dd..df4df025152 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -628,6 +628,11 @@ struct GameStateData {
 		inventoryItems.push_back(id);
 	}
 
+	void clearInventory() {
+		inventoryItems.clear();
+		selectedInventoryItem = -1;
+	}
+
 	void removeInventoryItem(int id) {
 		for (uint i = 0; i < inventoryItems.size(); i++) {
 			if (inventoryItems[i] == id) {


Commit: dd1ab34e358913c58e07b52f5fa6a312b3cbdb66
    https://github.com/scummvm/scummvm/commit/dd1ab34e358913c58e07b52f5fa6a312b3cbdb66
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:40+02:00

Commit Message:
PELROCK: Cleanup video, fonts and actions

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/fonts/large_font.cpp
    engines/pelrock/fonts/small_font.cpp
    engines/pelrock/fonts/small_font_double.cpp
    engines/pelrock/fonts/small_font_double.h
    engines/pelrock/video/video.cpp
    engines/pelrock/video/video.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 552a3738aed..102d776ace9 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -99,12 +99,6 @@ const ActionEntry actionTable[] = {
 	{310, PICKUP, &PelrockEngine::pickupFruit},
 	{311, PICKUP, &PelrockEngine::pickupFruit},
 
-	// // Room 5
-	// {},
-
-	// // Room 7
-	// {},
-
 	// Room 8
 	{355, OPEN, &PelrockEngine::openLibraryOutdoorsDoor},
 	{355, CLOSE, &PelrockEngine::closeLibraryOutdoorsDoor},
@@ -209,14 +203,14 @@ const CombinationEntry combinationTable[] = {
 	{7, 353, &PelrockEngine::useAmuletWithStatue},
 	{8, 347, &PelrockEngine::useSecretCodeWithStatue},
 	{8, 358, &PelrockEngine::giveSecretCodeToLibrarian},
-	{4, 358, &PelrockEngine::useBrickWithLibrarian}, // Any hotspot in the lamppost will work
+	{4, 358, &PelrockEngine::useBrickWithLibrarian},
 	{76, 469, &PelrockEngine::usePumpkinWithRiver},
 	{100, 650, &PelrockEngine::useKeyWithPortrait},
 	{83, 461, &PelrockEngine::useDollWithBed},
 	{84, 503, &PelrockEngine::giveMagazineToGuard},
 	{86, 500, &PelrockEngine::giveWaterToGuard},
 	{91, 601, &PelrockEngine::giveStoneToSlaves},
-	{92, 601, &PelrockEngine::giveStoneToSlaves}, // Item 92 = mud/clay, same handler
+	{92, 601, &PelrockEngine::giveStoneToSlaves},
 
 	// Room 35 (cauldron)
 	{90, 506, &PelrockEngine::magicFormula},
@@ -231,6 +225,9 @@ const CombinationEntry combinationTable[] = {
 	// End marker
 	{WILDCARD, WILDCARD, nullptr}};
 
+/**
+ * Convenience methods for open/close doors which usually have the same behavior
+ */
 void PelrockEngine::openDoor(HotSpot *hotspot, int exitIndex, int sticker, bool masculine, bool stayClosed) {
 	if (_room->hasSticker(sticker)) {
 		int text = masculine == MASCULINE ? YA_ABIERTO_M : YA_ABIERTA_F;
@@ -253,6 +250,9 @@ void PelrockEngine::closeDoor(HotSpot *hotspot, int exitIndex, int sticker, bool
 	_sound->playSound(_room->_roomSfx[1]);
 }
 
+/**
+ * Adds item to inventory but also shows the item overlay
+ */
 void PelrockEngine::addInventoryItem(int item) {
 	if (_state->inventoryItems.empty()) {
 		_state->selectedInventoryItem = item;
@@ -274,6 +274,9 @@ void PelrockEngine::addInventoryItem(int item) {
 	checkObjectsForPart2();
 }
 
+/**
+ * Every object from the store removes bill, adds item, cutscene happens.
+ */
 void PelrockEngine::buyFromStore(HotSpot *hotspot, int stickerId) {
 	if (_state->hasInventoryItem(5) == false) {
 		_dialog->say(_res->_ingameTexts[NOTENGODINERO]);
@@ -294,10 +297,10 @@ void PelrockEngine::buyFromStore(HotSpot *hotspot, int stickerId) {
 	}
 }
 
+// F8 commands on conversations
 void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte rootIndex) {
 	switch (actionTrigger) {
 	case 328:
-		debug("Setting current root to %d in room %d", rootIndex + 1, room);
 		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
 	case 329:
@@ -338,8 +341,8 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
 	case 273: {
-		WalkBox w1 = { 3, 436, 356, 4, 14, 0 };
-		WalkBox w2 = { 4, 440, 368,148, 2, 0 };
+		WalkBox w1 = {3, 436, 356, 4, 14, 0};
+		WalkBox w2 = {4, 440, 368, 148, 2, 0};
 
 		_room->addWalkbox(w1);
 		_room->addWalkbox(w2);
@@ -357,7 +360,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 278:
 		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
-	case 279:{
+	case 279: {
 		_state->removeInventoryItem(75);
 		travelToEgypt();
 
@@ -404,7 +407,6 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
 	case 348: {
-		// Anti-piracy punishment: corrupt screen + noise + crash
 		antiPiracyEffect();
 		break;
 	}
@@ -452,14 +454,13 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 352:
 	case 355:
 	case 291:
-		// a la carcel
 		toJail();
 		break;
 	case 356:
 		_state->setCurrentRoot(room, 3, 0);
 		break;
-	// end puta
-	//  sabio
+	// end hooker
+	//  hermit
 	case 366:
 		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
@@ -471,7 +472,6 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		walkAndAction(_room->findHotspotByExtra(467), TALK);
 		waitForActionEnd();
 		break;
-		// hasta aqui
 
 	case 357: // wrong answer: counter-- (min 0)
 	{
@@ -509,9 +509,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 361: // "no sé" (I don't know): no counter change, just advance
 		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
-	case 362: // special trigger: enables HIJODELAGRANPUTA cheat code
-		// Sets cheat_code_checking_enabled flag in original (0x495F3)
-		// TODO: Implement cheat code sequence checker in game loop
+	case 362:
 		_state->setFlag(FLAG_CHEAT_CODE_ENABLED, 1);
 		advanceQuotesConversation(rootIndex, room);
 		break;
@@ -666,7 +664,6 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_state->setCurrentRoot(45, 3, 0);
 		break;
 	case 30840:
-		// toJail()? not present in the game
 		break;
 
 	case 323:
@@ -744,12 +741,12 @@ void PelrockEngine::noOpAction(HotSpot *hotspot) {
 
 void PelrockEngine::noOpItem(int item, HotSpot *hotspot) {
 	// Original game checked against 47 instead of 58
+	// These are books in the library being returned to the librarian
 	if (item >= 11 && item <= 58 && hotspot->extra == 358) {
 		_state->removeInventoryItem(item);
 		_dialog->say(_res->_ingameTexts[DEACUERDO_2]);
 		return;
 	}
-	debug("No-op item %d with hotspot %d", item, hotspot->extra);
 	_alfredState.direction = ALFRED_DOWN;
 	sayRandomIncorrectResponse();
 }
@@ -823,8 +820,9 @@ void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
 			}
 		}
 		if (billCount < 13) {
-			addInventoryItem(5);                     // 1000 pesetas bill
-			bool sayLine = getRandomNumber(15) == 1; // 1 in 15 chance to say the line about not having more money
+			addInventoryItem(5);
+			// 1 in every 15 times, the hooker will say "fancy a good time?"
+			bool sayLine = getRandomNumber(15) == 1;
 			if (sayLine) {
 				_dialog->say(_res->_ingameTexts[TEAPETECE_BUENRATO]);
 			}
@@ -839,7 +837,6 @@ void PelrockEngine::openMcDoor(HotSpot *hotspot) {
 }
 
 void PelrockEngine::closeMcDoor(HotSpot *hotspot) {
-	// FIXME: Impossible to close right now
 	closeDoor(hotspot, 2, 7, FEMININE, false);
 }
 
@@ -923,7 +920,7 @@ void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
 	HotSpot *windowHotspot = _room->findHotspotByExtra(294);
 	brickSprite->x = 420;
 	brickSprite->y = 241;
-	brickSprite->zOrder = 10; // Make it visible
+	brickSprite->zOrder = 10;
 	while (!shouldQuit()) {
 		_events->pollEvent();
 		renderScene(OVERLAY_NONE);
@@ -937,7 +934,6 @@ void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
 	}
 
 	debug("Brick hit the window");
-	// Add the broken window sticker
 	_room->addSticker(11);
 	_sound->playSound(_room->_roomSfx[2]); // Play glass breaking sound
 
@@ -956,10 +952,10 @@ void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
 	_state->setFlag(FLAG_TIENDA_ABIERTA, true);
 	_room->addStickerToRoom(_room->_currentRoomNumber, 9, PERSIST_PERM);
 	_room->addStickerToRoom(_room->_currentRoomNumber, 10, PERSIST_PERM);
-	_room->disableHotspot(_room->findHotspotByExtra(295)); // Disable storefront hotspot
+	_room->disableHotspot(_room->findHotspotByExtra(295));
 	_room->disableHotspot(_room->findHotspotByExtra(294)); // Disable window hotspot
-	_room->enableSprite(5, 100, PERSIST_PERM); // Enable fake teeth sprite
-	_disableAction = true;                                 // Prevent player from doing anything until they move Alfred
+	_room->enableSprite(5, 100, PERSIST_PERM);             // Enable fake teeth sprite
+	_disableAction = true;                                 // Prevent player from doing anything until the action is done
 	walkTo(630, _alfredState.y);
 }
 
@@ -967,7 +963,7 @@ void PelrockEngine::moveCable(HotSpot *hotspot) {
 	_room->addSticker(15);
 	_room->addSticker(16);
 	_room->addSticker(17);
-	_room->addStickerToRoom(4, 20); // Room 4, sticker 20
+	_room->addStickerToRoom(4, 20);
 	_state->setFlag(FLAG_CABLES_PUESTOS, true);
 }
 
@@ -1049,7 +1045,7 @@ void PelrockEngine::pickCables(HotSpot *hotspot) {
 	// electric shock
 	int prevX = _alfredState.x;
 	_alfredState.x -= 20;
-	// original incorrectly played door closing sound here
+	// original incorrectly played door closing sound here!
 	_sound->playSound("ELEC3ZZZ.SMP", 0);
 	playAlfredSpecialAnim(3);
 	_alfredState.x = prevX;
@@ -1095,7 +1091,7 @@ void PelrockEngine::giveMoneyToGuard(int inventoryObject, HotSpot *hotspot) {
 	} else if (!_state->getFlag(FLAG_SOBORNO_PORTERO)) {
 		_state->setFlag(FLAG_SOBORNO_PORTERO, true);
 		_dialog->say(_res->_ingameTexts[MUYBIEN]);
-		_state->removeInventoryItem(5); // Remove 1000 pesetas bill
+		_state->removeInventoryItem(5);
 	}
 	if (_state->getFlag(FLAG_SOBORNO_PORTERO) && _state->getFlag(FLAG_GUARDIA_DNI_ENTREGADO)) {
 		unlockMuseum();
@@ -1213,7 +1209,7 @@ void PelrockEngine::openTravelAgencyDoor(HotSpot *hotspot) {
 	if (_state->getFlag(FLAG_AGENCIA_ABIERTA)) {
 		openDoor(hotspot, 1, 57, FEMININE, false);
 	}
-	// The game originally did nothing here
+	// The game originally already did nothing here
 }
 
 void PelrockEngine::closeTravelAgencyDoor(HotSpot *hotspot) {
@@ -1223,7 +1219,7 @@ void PelrockEngine::closeTravelAgencyDoor(HotSpot *hotspot) {
 void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
 	_state->removeInventoryItem(76);
 	addInventoryItem(86);
-	if(_state->getFlag(FLAG_CROCODILLO_ENCENDIDO) == false) {
+	if (_state->getFlag(FLAG_CROCODILLO_ENCENDIDO) == false) {
 		_sound->playMusicTrack(27);
 		checkIngredients();
 		_dialog->say(_res->_ingameTexts[CUIDADOIMPRUDENTE]);
@@ -1234,7 +1230,6 @@ void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
 		_sound->playSound(_room->_roomSfx[0], 0); // Belch
 		waitForSoundEnd();
 		_graphics->fadeToBlack(10);
-		// update conversaton state
 		_alfredState.x = 300;
 		_alfredState.y = 238;
 		waitForSoundEnd();
@@ -1353,9 +1348,7 @@ void PelrockEngine::closeEgyptMuseumDoor(HotSpot *hotspot) {
 void PelrockEngine::pushSymbol1(HotSpot *hotspot) {
 	if (_state->getFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO) == true) {
 		byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
-		debug("Current symbols pulled: %d", symbolsPulled);
 		_state->setFlag(FLAG_SYMBOLS_PUSHED, symbolsPulled | 0x1);
-		debug("New symbols pulled: %d", _state->getFlag(FLAG_SYMBOLS_PUSHED));
 		checkAllSymbols();
 	}
 }
@@ -1363,9 +1356,7 @@ void PelrockEngine::pushSymbol1(HotSpot *hotspot) {
 void PelrockEngine::pushSymbol2(HotSpot *hotspot) {
 	if (_state->getFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO) == true) {
 		byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
-		debug("Current symbols pulled: %d", symbolsPulled);
 		_state->setFlag(FLAG_SYMBOLS_PUSHED, symbolsPulled | 0x2);
-		debug("New symbols pulled: %d", _state->getFlag(FLAG_SYMBOLS_PUSHED));
 		checkAllSymbols();
 	}
 }
@@ -1373,9 +1364,7 @@ void PelrockEngine::pushSymbol2(HotSpot *hotspot) {
 void PelrockEngine::pushSymbol3(HotSpot *hotspot) {
 	if (_state->getFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO) == true) {
 		byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
-		debug("Current symbols pulled: %d", symbolsPulled);
 		_state->setFlag(FLAG_SYMBOLS_PUSHED, symbolsPulled | 0x4);
-		debug("New symbols pulled: %d", _state->getFlag(FLAG_SYMBOLS_PUSHED));
 		checkAllSymbols();
 	}
 }
@@ -1383,16 +1372,13 @@ void PelrockEngine::pushSymbol3(HotSpot *hotspot) {
 void PelrockEngine::pushSymbol4(HotSpot *hotspot) {
 	if (_state->getFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO) == true) {
 		byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
-		debug("Current symbols pulled: %d", symbolsPulled);
 		_state->setFlag(FLAG_SYMBOLS_PUSHED, symbolsPulled | 0x8);
-		debug("New symbols pulled: %d", _state->getFlag(FLAG_SYMBOLS_PUSHED));
 		checkAllSymbols();
 	}
 }
 
 void PelrockEngine::checkAllSymbols() {
 	byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
-	debug("Checking symbols, current value: %d", symbolsPulled);
 	if (symbolsPulled == 0x0F) {
 		// Activates animation
 		_sound->playSound(_room->_roomSfx[0]);
@@ -1498,6 +1484,7 @@ void PelrockEngine::guardMovement() {
 	while (!shouldQuit()) {
 		_events->pollEvent();
 		renderScene();
+		// has the guard move right, up, left, then disappear behind the pyramid
 		if (sprite->x >= 339 && state == 0) {
 			state = 1;
 			sprite->animData[0].movementFlags = 0x240; // Move up
@@ -1510,7 +1497,6 @@ void PelrockEngine::guardMovement() {
 			sprite->zOrder = 255; // Hide sprite
 			break;
 		}
-		debug("Guard position: (%d, %d), state: %d", sprite->x, sprite->y, state);
 		_screen->update();
 		g_system->delayMillis(10);
 	}
@@ -1559,7 +1545,6 @@ void PelrockEngine::playSpecialAnim(uint32 offset, bool compressed, int x, int y
 		}
 		_screen->markAllDirty();
 		_screen->update();
-		// _events->waitForKey();
 		g_system->delayMillis(10);
 	}
 	animSurface.free();
@@ -1567,7 +1552,7 @@ void PelrockEngine::playSpecialAnim(uint32 offset, bool compressed, int x, int y
 }
 
 void PelrockEngine::giveStoneToSlaves(int inventoryObject, HotSpot *hotspot) {
-	// Remove whichever stone item was used (91 = Egyptian stone, 92 = mud/clay)
+	// Remove whichever stone item was used (91 = stone, 92 = mud/clay)
 	_state->removeInventoryItem(inventoryObject);
 
 	Sprite *masters = _room->findSpriteByExtra(600);
@@ -1729,7 +1714,6 @@ void PelrockEngine::pickUpStones(HotSpot *hotspot) {
 	} else {
 		addInventoryItem(91);
 		_state->setFlag(FLAG_PIEDRAS_COGIDAS, _state->getFlag(FLAG_PIEDRAS_COGIDAS) + 1);
-		debug("Piedras cogidas: %d", _state->getFlag(FLAG_PIEDRAS_COGIDAS));
 	}
 }
 
@@ -1785,7 +1769,7 @@ void PelrockEngine::useWigWithPot(int inventoryObject, HotSpot *hotspot) {
 
 void PelrockEngine::magicFormula(int inventoryObject, HotSpot *hotspot) {
 	_state->removeInventoryItem(inventoryObject);
-	if(inventoryObject == 86) {
+	if (inventoryObject == 86) {
 		addInventoryItem(76);
 	}
 	_state->setFlag(FLAG_FORMULA_MAGICA, _state->getFlag(FLAG_FORMULA_MAGICA) + 1);
@@ -1879,7 +1863,6 @@ void PelrockEngine::closeArchitectDoorFromInside(HotSpot *hotspot) {
 }
 
 void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
-	debug("Performing action trigger: %d", actionTrigger);
 	switch (actionTrigger) {
 	case 257: // look portrait
 		_sound->playMusicTrack(25, false);
@@ -1895,7 +1878,6 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 		_dialog->say(_res->_ingameTexts[TRABAJARIA_MEJOR_SI_NO_ME_MOLESTARA]);
 		break;
 	case 270:
-		// loadExtraScreenAndPresent(9);
 		_state->stateGame = COMPUTER;
 		break;
 	case 280:
@@ -2054,10 +2036,10 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 
 	debug("Using item %d on Alfred", inventoryObject);
 	switch (inventoryObject) {
-	case 9: // carta
+	case 9: // Letter
 		_dialog->say(_res->_ingameTexts[CORRESPONDENCIA_AJENA]);
 		break;
-	case 34: // Como hacerse rico...
+	case 34: // How to become rich book
 		_dialog->say(_res->_ingameTexts[PERIODICOSENSACIONALISTA], 1);
 		break;
 	case 63: // Recipe
@@ -2084,7 +2066,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		_dialog->say(_res->_ingameTexts[YASEEGIPCIO]);
 		_state->setFlag(FLAG_ALFRED_SABE_EGIPCIO, true);
 		break;
-	case 24:
+	case 24: // Encyclopedia
 		if (_state->getFlag(FLAG_RIDDLE_PRESENTED) == true) {
 			_dialog->say(_res->_ingameTexts[CAPITULOPARADOJAS]);
 			_state->setCurrentRoot(25, 44, 0);
@@ -2095,7 +2077,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 			_state->setCurrentRoot(14, 2, 0);
 		}
 		break;
-	case 64:
+	case 64: // Formula for time travel
 		playAlfredSpecialAnim(0);
 		loadExtraScreenAndPresent(5);
 		if (_state->getFlag(FLAG_ALFRED_SABE_EGIPCIO)) {
@@ -2104,7 +2086,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 			_dialog->say(_res->_ingameTexts[QUELASTIMA_NOSEEGIPCIO]);
 		}
 		break;
-	case 88: {
+	case 88: { // spellbook
 		if (_room->_currentRoomNumber != 28 &&
 			(_room->_currentRoomNumber < 51 || _room->_currentRoomNumber > 54)) {
 			_dialog->say(_res->_ingameTexts[AQUI_NO_NECESITO]);
@@ -2147,19 +2129,15 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 			case 54: {
 				_sound->playSound(_room->_roomSfx[8], 0);
 				_dialog->say(_res->_ingameTexts[DIOSHALCON + spell->page], 1);
-				debug("Flight spell cast in room %d, spell page: %d", _room->_currentRoomNumber, spell->page);
 				int flightIndex = _room->_currentRoomNumber - 51;
-				debug("Correct page for this spell is = %d", kFlightRooms[flightIndex].spellPage);
 				if (_flightSorcererAppeared && !_flightInBlockingAnim && spell->page == kFlightRooms[flightIndex].spellPage) {
 					_state->setFlag(FLAG_COMO_ESTAN_LOS_DIOSES, _state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) | (1 << flightIndex));
-					debug("Flight spell successful, starting animation and updating state");
-					debug("Updated FLAG_COMO_ESTAN_LOS_DIOSES: %d", _state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES));
 					_sound->playSound(_room->_roomSfx[1], 0);
 					smokeAnimation(kFlightRooms[flightIndex].spriteIdx, true);
 					_room->addStickerToRoom(_room->_currentRoomNumber, 127 + flightIndex);
 					_room->addStickerToRoom(52, 106 + flightIndex);
 
-					if(_state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) == 15) { // all 4 spells successful
+					if (_state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) == 15) { // all 4 spells successful
 						HotSpot hotspot = HotSpot();
 						hotspot.actionFlags = 0;
 						hotspot.extra = 999;
@@ -2171,8 +2149,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 						hotspot.index = 8;
 						_room->changeHotspot(52, hotspot);
 					}
-				}
-				else {
+				} else {
 					_sound->playSound(_room->_roomSfx[2], 0);
 				}
 				break;
@@ -2192,7 +2169,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		_dialog->say(_res->_ingameTexts[PARECE_COMBINACION_CAJAFUERTE]);
 		_state->setFlag(FLAG_CLAVE_CAJA_FUERTE, true);
 		break;
-	case 108:
+	case 108: // glue + patches
 	case 109: {
 
 		if (_state->hasInventoryItem(110) == true) {
@@ -2219,14 +2196,14 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		loadExtraScreenAndPresent(7);
 		break;
 	}
-	case 97: {
+	case 97: { // pyramid map
 		playAlfredSpecialAnim(1);
 		loadExtraScreenAndPresent(11);
 		_dialog->say(_res->_ingameTexts[MEHANTOMADO_EL_PELO]);
 		_state->setCurrentRoot(43, 1, 0);
 		break;
 	}
-	case 98: {
+	case 98: { // correct pyramid blueprints
 		chooseCorrectDoor();
 		break;
 	}
@@ -2245,7 +2222,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		break;
 	}
 	default: {
-		// Original game incorrectly checked until object 47
+		// Original game incorrectly checked until object 47; Reading any book.
 		if (inventoryObject >= 11 && inventoryObject <= 58) {
 			playAlfredSpecialAnim(0);
 			_dialog->say(_res->_ingameTexts[LIBRO_ABURRIDO]);
@@ -2261,13 +2238,13 @@ void PelrockEngine::sayRandomIncorrectResponse() {
 	byte response = (byte)getRandomNumber(15);
 	_dialog->say(_res->_ingameTexts[154 + response]);
 }
+
 void PelrockEngine::chooseCorrectDoor() {
 	playAlfredSpecialAnim(1);
 	byte puertaBuena = _state->getFlag(FLAG_PUERTA_BUENA);
 	if (puertaBuena == 0) { // if not set yet, choose randomly
 		int choice = getRandomNumber(1);
 		_state->setFlag(FLAG_PUERTA_BUENA, choice + 1);
-		debug("Randomly chosen good door: %d", _state->getFlag(FLAG_PUERTA_BUENA));
 	}
 	puertaBuena = _state->getFlag(FLAG_PUERTA_BUENA);
 	Common::String doorText = _res->_izquierda;
@@ -2282,6 +2259,9 @@ void PelrockEngine::chooseCorrectDoor() {
 	_dialog->say(fullTextArray);
 }
 
+/**
+ * When using amulet with statue, the statue turns red-ish and then starts to talk.
+ */
 void PelrockEngine::animateStatuePaletteFade(bool reverse) {
 	struct StatuePaletteData {
 		uint16 x;           // 368
@@ -2324,10 +2304,8 @@ void PelrockEngine::animateStatuePaletteFade(bool reverse) {
 
 	exeFile.close();
 
-	// Animation parameters
-	const int numFrames = 7; // 7 step updates total
+	const int numFrames = 7;
 
-	// Get current palette
 	byte currentPalette[768];
 	memcpy(currentPalette, _room->_roomPalette, 768);
 
@@ -2342,7 +2320,6 @@ void PelrockEngine::animateStatuePaletteFade(bool reverse) {
 			for (int i = 0; i < 16; i++) {
 				byte paletteIndex = paletteData.indices[i];
 
-				// Determine source and target based on direction
 				byte *srcColor = reverse ? paletteData.target[i] : paletteData.source[i];
 				byte *dstColor = reverse ? paletteData.source[i] : paletteData.target[i];
 
@@ -2351,13 +2328,11 @@ void PelrockEngine::animateStatuePaletteFade(bool reverse) {
 				byte g6 = srcColor[1] + ((dstColor[1] - srcColor[1]) * frame) / numFrames;
 				byte b6 = srcColor[2] + ((dstColor[2] - srcColor[2]) * frame) / numFrames;
 
-				// Convert 6-bit VGA (0-63) to 8-bit (0-255) by shifting left 2 bits
 				currentPalette[paletteIndex * 3 + 0] = r6 << 2;
 				currentPalette[paletteIndex * 3 + 1] = g6 << 2;
 				currentPalette[paletteIndex * 3 + 2] = b6 << 2;
 			}
 
-			// Apply the palette
 			g_system->getPaletteManager()->setPalette(currentPalette, 0, 256);
 			frame++;
 		}
@@ -2374,7 +2349,7 @@ void PelrockEngine::checkObjectsForPart2() {
 	if (_state->hasInventoryItem(17) &&
 		_state->hasInventoryItem(59) &&
 		_state->hasInventoryItem(24)) {
-		if(_state->getFlag(FLAG_AGENCIA_ABIERTA) == false) {
+		if (_state->getFlag(FLAG_AGENCIA_ABIERTA) == false) {
 			_room->addStickerToRoom(19, 54, PERSIST_BOTH);
 			_room->addStickerToRoom(19, 55, PERSIST_BOTH);
 			_room->addStickerToRoom(19, 56, PERSIST_BOTH);
@@ -2393,9 +2368,7 @@ void PelrockEngine::waitForActionEnd() {
 }
 
 /**
- * Handler for picking up object with extra_id 472 in Room 28.
- * Loads a special palette from ALFRED.7 at offset 0x1610CE and
- * fades to it using the step-wise palette transition.
+ * Picking up matches in room 28 has the palette change to "lighten it up".
  */
 void PelrockEngine::pickUpMatches(HotSpot *hotspot) {
 
@@ -2468,7 +2441,6 @@ void PelrockEngine::antiPiracyEffect() {
 		_screen->update();
 		g_system->delayMillis(10);
 	}
-
 }
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/fonts/large_font.cpp b/engines/pelrock/fonts/large_font.cpp
index 22d56c5c761..8d4609c5770 100644
--- a/engines/pelrock/fonts/large_font.cpp
+++ b/engines/pelrock/fonts/large_font.cpp
@@ -50,7 +50,6 @@ bool LargeFont::load(const Common::String &filename) {
 	const int dataSize = numChars * paddedHeight * paddedWidth; // 96 characters × 14 × 26 bytes
 	byte *rawFontData = new byte[numChars * 48];                // original format: 96 × 48 bytes
 	file.read(rawFontData, numChars * 48);
-	debug("LargeFont::load: Loading large font data from %s, size %d bytes", filename.c_str(), dataSize);
 	file.close();
 
 	delete[] _fontData;
@@ -72,7 +71,7 @@ bool LargeFont::load(const Common::String &filename) {
 				mask[i + pad][bit + 8 + pad] = (rowByte2 & (0x80 >> bit)) != 0;
 			}
 		}
-
+		// adds a border mask to the original font
 		bool borderMask[paddedHeight][paddedWidth] = {false};
 
 		for (int y = 0; y < paddedHeight; y++) {
diff --git a/engines/pelrock/fonts/small_font.cpp b/engines/pelrock/fonts/small_font.cpp
index 93d06bb31d9..b439b5db2f9 100644
--- a/engines/pelrock/fonts/small_font.cpp
+++ b/engines/pelrock/fonts/small_font.cpp
@@ -41,7 +41,6 @@ bool SmallFont::load(const Common::String &filename) {
 	file.seek(kSmallFontOffset, SEEK_SET);
 
 	const int dataSize = kNumChars * 8; // 256 characters, 8x8 pixels
-	debug("SmallFont::load: Loading font data of size %d from %s", dataSize, filename.c_str());
 	_fontData = new byte[dataSize];
 	file.read(_fontData, dataSize);
 	file.close();
diff --git a/engines/pelrock/fonts/small_font_double.cpp b/engines/pelrock/fonts/small_font_double.cpp
index 19fb92645cf..4ebc5a580ed 100644
--- a/engines/pelrock/fonts/small_font_double.cpp
+++ b/engines/pelrock/fonts/small_font_double.cpp
@@ -18,8 +18,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
-#include "common/debug.h"
 #include "pelrock/fonts/small_font_double.h"
+#include "common/debug.h"
 
 namespace Pelrock {
 
@@ -31,7 +31,6 @@ DoubleSmallFont::~DoubleSmallFont() {
 
 void DoubleSmallFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
 	if (!_fontData || chr > kNumChars - 1) {
-		// debug("DoubleSmallFont::drawChar: Invalid char %d", chr);
 		return;
 	}
 	int charOffset = chr * 8;
diff --git a/engines/pelrock/fonts/small_font_double.h b/engines/pelrock/fonts/small_font_double.h
index 5bf265ca0fa..68a603ff28e 100644
--- a/engines/pelrock/fonts/small_font_double.h
+++ b/engines/pelrock/fonts/small_font_double.h
@@ -29,12 +29,15 @@
 #include "pelrock/fonts/small_font.h"
 
 namespace Pelrock {
+
+/**
+ * Same as SmallFont but doubling the height of each character
+ */
 class DoubleSmallFont : public SmallFont {
 public:
 	DoubleSmallFont();
 	~DoubleSmallFont();
 
-
 	int getFontHeight() const override { return CHAR_HEIGHT; };
 	void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
 
@@ -44,4 +47,3 @@ private:
 
 } // End of namespace Pelrock
 #endif
-
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index 0aace022921..cd344ae194b 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -71,15 +71,13 @@ void VideoManager::playIntro() {
 		ChunkHeader chunk;
 		readChunk(videoFile, chunk);
 
-		if(_events->_lastKeyEvent == Common::KEYCODE_ESCAPE) {
+		if (_events->_lastKeyEvent == Common::KEYCODE_ESCAPE) {
 			break;
 		}
 
 		switch (chunk.chunkType) {
 		case 1:
 		case 2: {
-			// Visual frame: wait for chrono timing before presenting
-			// Fix Bug 2/3: only visual frames (types 1/2) participate in chrono gating
 			Subtitle *subtitle = getSubtitleForFrame(frameCounter);
 			int frameSkip = subtitle != nullptr ? 4 : 2;
 			while (!g_engine->shouldQuit() && _events->_lastKeyEvent != Common::KEYCODE_ESCAPE) {
@@ -90,11 +88,9 @@ void VideoManager::playIntro() {
 				g_system->delayMillis(10);
 			}
 
-			// Fix Bug 1: capture current frame BEFORE increment so audio fires at the correct frame
 			int currentFrame = frameCounter++;
 			processFrame(chunk, currentFrame);
 
-			// Fix Bug 1: all audio checks use currentFrame (not the already-incremented frameCounter)
 			if (_voiceEffect.contains(currentFrame)) {
 				// Wait for any playing voice to finish before starting new one
 				while (_sound->isPlaying(0)) {
@@ -127,9 +123,9 @@ void VideoManager::playIntro() {
 				_sound->playMusicTrack(music.trackNumber, true);
 			}
 
-			// Fix Bug 4: subtitles are suppressed in the blackout range (frames 571-669)
-			bool inBlackoutRange = (currentFrame >= 571 && currentFrame <= 669);
-			if (subtitle != nullptr && !inBlackoutRange) {
+			// subtitles are suppressed in the frame range 571-669)
+			bool skipSubs = (currentFrame >= 571 && currentFrame <= 669);
+			if (subtitle != nullptr && !skipSubs) {
 				Common::StringArray lines = _dialog->wordWrap(subtitle->text)[0];
 
 				byte color;
@@ -149,8 +145,7 @@ void VideoManager::playIntro() {
 			loadPalette(chunk);
 			break;
 		case 6:
-			// Fix Bug 3: type 6 is a timing pad. Original costs ~20ms per chunk.
-			// Do NOT gate on chrono — process immediately with a short delay.
+			// type 6 is merely wait for 20ms
 			g_system->delayMillis(20);
 			break;
 		default:
@@ -159,7 +154,6 @@ void VideoManager::playIntro() {
 		}
 	}
 
-	debug("Total frames played: %d", frameCounter);
 	videoFile.close();
 }
 
@@ -305,8 +299,7 @@ void VideoManager::initMetadata() {
 			} else if (nextChar == 'f') {
 				AudioEffect sfx = readAudioEffect(metadataFile);
 				_sfxEffect[sfx.startFrame] = sfx;
-			}
-			else if (nextChar == 'c') {
+			} else if (nextChar == 'c') {
 				MusicEffect music = readMusicEffect(metadataFile);
 				_musicEffect[music.startFrame] = music;
 			}
@@ -379,7 +372,8 @@ Subtitle VideoManager::readSubtitle(Common::File &metadataFile) {
 	int valueIndex = 0;
 
 	// Skip spaces after "/t"
-	while (!metadataFile.eos() && metadataFile.readByte() == ' ');
+	while (!metadataFile.eos() && metadataFile.readByte() == ' ')
+		;
 	metadataFile.seek(-1, SEEK_CUR); // Step back one byte
 
 	// Parse 4 space-delimited numbers
@@ -402,14 +396,11 @@ Subtitle VideoManager::readSubtitle(Common::File &metadataFile) {
 		}
 	}
 
-	// metadataFile.skip(1); // Skip the extra space
-
 	subtitle.startFrame = values[0];
 	subtitle.endFrame = values[1];
 	subtitle.x = values[2];
 	subtitle.y = values[3];
 
-	// Read text until CRLF (0x0D 0x0A)
 	subtitle.text.clear();
 
 	// skip leading spaces in subtitle tex
@@ -418,12 +409,13 @@ Subtitle VideoManager::readSubtitle(Common::File &metadataFile) {
 		nextByte = metadataFile.readByte();
 	} while (nextByte == ' ' && !metadataFile.eos());
 
-	if(nextByte == 0x08) {
+	if (nextByte == 0x08) {
 		subtitle.text += '@';
 	} else {
 		subtitle.text += decodeChar(nextByte);
 	}
 
+	// Read text until CRLF (0x0D 0x0A)
 	while (!metadataFile.eos()) {
 
 		char c = metadataFile.readByte();
@@ -436,7 +428,7 @@ Subtitle VideoManager::readSubtitle(Common::File &metadataFile) {
 				subtitle.text += decodeChar(next);
 			}
 		} else {
-			if(c == 0x00) {
+			if (c == 0x00) {
 				// do nothing
 			}
 			if (c == 0x08)
diff --git a/engines/pelrock/video/video.h b/engines/pelrock/video/video.h
index 392734eb5fc..07f79a3606a 100644
--- a/engines/pelrock/video/video.h
+++ b/engines/pelrock/video/video.h
@@ -30,10 +30,10 @@
 namespace Pelrock {
 
 struct ChunkHeader {
-    uint32 blockCount;      // +0x00: Number of 0x5000-byte blocks
-    uint32 dataOffset;      // +0x04: Varies by chunk type
-    byte   chunkType;       // +0x08: 1=RLE, 2=BlockCopy, 3=End, 4=Palette, 6=Special
-    // +0x0D: Frame data begins
+	uint32 blockCount; // +0x00: Number of 0x5000-byte blocks
+	uint32 dataOffset; // +0x04: Varies by chunk type
+	byte chunkType;    // +0x08: 1=RLE, 2=BlockCopy, 3=End, 4=Palette, 6=Special
+					   // +0x0D: Frame data begins
 	byte *data;
 };
 
@@ -67,7 +67,7 @@ static const uint32 chunkSize = 0x5000;
 static const int video_special_chars[] = {
 	0x83, // inverted ?
 	0x82, // inverted !
-	165, // capital N tilde
+	0xA5, // capital N tilde
 	0x80, // small n tilde
 	0x7F, // small u tilde
 	0x7E, // small o tilde
@@ -84,8 +84,7 @@ public:
 		ChronoManager *chrono,
 		LargeFont *largeFont,
 		DialogManager *dialog,
-		SoundManager *sound
-	);
+		SoundManager *sound);
 	~VideoManager();
 	void playIntro();
 
@@ -98,7 +97,7 @@ private:
 	SoundManager *_sound;
 	void loadPalette(ChunkHeader &chunk);
 	byte *decodeCopyBlock(byte *data, uint32 offset);
-    byte *decodeRLE(byte *data, size_t size, uint32 offset);
+	byte *decodeRLE(byte *data, size_t size, uint32 offset);
 	void readChunk(Common::SeekableReadStream &stream, ChunkHeader &chunk);
 	void processFrame(ChunkHeader &chunk, const int frameCount);
 	void presentFrame();


Commit: ffd5b9652eede8db6fdec6f79f2ecf975a07af18
    https://github.com/scummvm/scummvm/commit/ffd5b9652eede8db6fdec6f79f2ecf975a07af18
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:40+02:00

Commit Message:
PELROCK: Cleanup of dialog and computer

Changed paths:
    engines/pelrock/backgroundbook.cpp
    engines/pelrock/chrono.cpp
    engines/pelrock/chrono.h
    engines/pelrock/computer.cpp
    engines/pelrock/computer.h
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/fonts/large_font.cpp
    engines/pelrock/fonts/small_font.h
    engines/pelrock/resources.cpp


diff --git a/engines/pelrock/backgroundbook.cpp b/engines/pelrock/backgroundbook.cpp
index 106ae4d079a..b9c78d1d897 100644
--- a/engines/pelrock/backgroundbook.cpp
+++ b/engines/pelrock/backgroundbook.cpp
@@ -29,8 +29,8 @@
 namespace Pelrock {
 
 static const uint32 kBgBookButtonsOffset = 3188448; // ALFRED.7 — UI buttons
-static const uint32 kRoomNamesOffset     = 299797;  // JUEGO.EXE — room name strings
-static const uint32 kRoomNamesSize       = 1297;
+static const uint32 kRoomNamesOffset = 299797;      // JUEGO.EXE — room name strings
+static const uint32 kRoomNamesSize = 1297;
 
 BackgroundBook::BackgroundBook(PelrockEventManager *eventMan, ResourceManager *res, RoomManager *room)
 	: _events(eventMan), _res(res), _room(room) {
diff --git a/engines/pelrock/chrono.cpp b/engines/pelrock/chrono.cpp
index a020350332b..89d00a69c83 100644
--- a/engines/pelrock/chrono.cpp
+++ b/engines/pelrock/chrono.cpp
@@ -39,7 +39,7 @@ void ChronoManager::updateChrono() {
 
 	if ((currentTime - _lastTick) >= kTickMs / _speedMultiplier) {
 		_gameTick = true;
-		if(!_pauseCounter) {
+		if (!_pauseCounter) {
 			_frameCount++;
 		}
 		_lastTick = currentTime;
diff --git a/engines/pelrock/chrono.h b/engines/pelrock/chrono.h
index 0e8b5f3b8b9..602d721d2b0 100644
--- a/engines/pelrock/chrono.h
+++ b/engines/pelrock/chrono.h
@@ -25,13 +25,7 @@
 
 namespace Pelrock {
 
-// const int kTickMs = 18;
-// const int kTickMs = 100;
-// const int kTickMs = 15;
-// const int kTickMs = 33;
-// const int kTickMs = 38;
 const int kTickMs = 55;
-// const int kTickMs = 60;
 const int kHalfTickMultiplier = 2;
 
 class ChronoManager {
diff --git a/engines/pelrock/computer.cpp b/engines/pelrock/computer.cpp
index 5c90775c4ac..95a7bb81404 100644
--- a/engines/pelrock/computer.cpp
+++ b/engines/pelrock/computer.cpp
@@ -37,7 +37,6 @@ Computer::Computer(PelrockEventManager *eventMan)
 	  _currentResult(0),
 	  _memorizedBookIndex(-1),
 	  _events(eventMan) {
-
 	init();
 }
 
@@ -78,8 +77,6 @@ void Computer::init() {
 		book.inventoryIndex = alfred7File.readByte() - 55;
 		book.shelf = alfred7File.readByte();
 		book.available = alfred7File.readByte() == 2;
-		// book.index = book.available ? index++ : -1;
-		// book.index2 = index2++;
 		_libraryBooks.push_back(book);
 	}
 
@@ -205,9 +202,9 @@ void Computer::drawScreen() {
 		int titlePlaceholderIndex = titleLine.findFirstOf("XXXX");
 
 		int titleIndex = 0;
-		while(titleIndex < book.title.size()) {
+		while (titleIndex < book.title.size()) {
 			Common::String thisLine;
-			if(titleIndex == 0) {
+			if (titleIndex == 0) {
 				thisLine = titleLine;
 				thisLine.replace(titlePlaceholderIndex, titleLine.size() - titlePlaceholderIndex, book.title[titleIndex]);
 			} else {
@@ -222,9 +219,9 @@ void Computer::drawScreen() {
 		int authorPlaceholderIndex = authorLine.findFirstOf("XXXX");
 		int authorIndex = 0;
 
-		while(authorIndex < book.author.size()) {
+		while (authorIndex < book.author.size()) {
 			Common::String thisLine;
-			if(authorIndex == 0) {
+			if (authorIndex == 0) {
 				thisLine = authorLine;
 				thisLine.replace(authorPlaceholderIndex, authorLine.size() - authorPlaceholderIndex, book.author[authorIndex]);
 			} else {
@@ -240,7 +237,7 @@ void Computer::drawScreen() {
 		genreLine.replace(genrePlaceholderIndex, genreLine.size() - genrePlaceholderIndex, book.genre);
 		g_engine->_graphics->drawColoredText(g_engine->_screen, genreLine, textX, textY + increment * 2, 340, defaultColor, g_engine->_smallFont);
 
-		// Situacion (location/availability)
+		// "Situacion" (location/availability)
 		Common::String situacionLine = _computerText[6][0];
 		int situacionPlaceholderIndex = situacionLine.findFirstOf("XXXX");
 		situacionLine.replace(situacionPlaceholderIndex, situacionLine.size() - situacionPlaceholderIndex, book.available ? _computerText[8][0] : _computerText[9][0]);
@@ -299,7 +296,7 @@ void Computer::handleResultsDisplay() {
 	if (_events->_lastKeyEvent == Common::KEYCODE_s) {
 		if (!_searchResults.empty()) {
 			_currentResult = (_currentResult + 1);
-			if(_currentResult >= _searchResults.size())
+			if (_currentResult >= _searchResults.size())
 				_state = STATE_MAIN_MENU;
 		}
 		_events->_lastKeyEvent = Common::KEYCODE_INVALID;
@@ -336,7 +333,6 @@ void Computer::memorizeBook(int bookIndex) {
 	g_engine->_state->selectedBookIndex = book.inventoryIndex;
 	g_engine->_state->bookLetter = book.title[0][0];
 
-	debug("Memorized book '%s' with index %d, shelf %d, letter %c", book.title[0].c_str(), g_engine->_state->selectedBookIndex, g_engine->_state->libraryShelf, g_engine->_state->bookLetter);
 }
 
 void Computer::performSearch() {
diff --git a/engines/pelrock/computer.h b/engines/pelrock/computer.h
index 7e9379ccad8..403a620717d 100644
--- a/engines/pelrock/computer.h
+++ b/engines/pelrock/computer.h
@@ -33,39 +33,31 @@ namespace Pelrock {
 // Book data location in ALFRED.7
 static const uint32 kBookDataOffset = 0x309E0;
 static const uint32 kBookDataEnd = 0x33F05;
-static const int kBookEntrySize = 108;  // 55 + 30 + 20 + 1 + 1 + 1
 
 // Field sizes
 static const int kBookTitleSize = 55;
 static const int kBookAuthorSize = 30;
 static const int kBookGenreSize = 20;
 
-// Status byte values
-static const byte kBookStatusCatalogOnly = 0x01;  // No physical copy
-static const byte kBookStatusPhysical = 0x02;     // Has physical copy on shelf
-
 struct LibraryBook {
-    Common::StringArray title; //55 bytes for title
-    Common::StringArray author; //30 bytes for author
-    Common::String genre; //20 bytes for genre
-    byte inventoryIndex;
-    byte shelf;       // 1-3 for row number, 0 if catalog-only
-    bool available;  // true = can be found on shelf, false = catalog only
+	Common::StringArray title;  // 55 bytes for title
+	Common::StringArray author; // 30 bytes for author
+	Common::String genre;       // 20 bytes for genre
+	byte inventoryIndex;
+	byte shelf;     // 1-3 for row number
+	bool available; // true = can be found on shelf, false = catalog only
 };
 
 class Computer {
 public:
 	Computer(PelrockEventManager *eventMan);
-
 	~Computer();
 
 	/**
 	 * @return Book index if a book was memorized, -1 otherwise
 	 */
 	int run();
-	Common::String _titleMsg;
-	Common::String _authorMsg;
-	Common::String _memorizedMsg;      // "Bueno... Tendre que buscar en la estanteria de la %c"
+	Common::String _memorizedMsg; // "Bueno... Tendre que buscar en la estanteria de la %c"
 
 private:
 	enum ComputerState {
@@ -76,19 +68,21 @@ private:
 		STATE_EXIT
 	};
 
-	PelrockEventManager *_events;
+	PelrockEventManager *_events = nullptr;
 	Graphics::ManagedSurface _backgroundScreen;
-	byte *_palette;
+	byte *_palette = nullptr;
 
 	// State variables
 	ComputerState _state;
-	char _searchLetter;
-	int _searchType;  // 0 = title, 1 = author
+	char _searchLetter = ' ';
+	int _searchType = 0; // 0 = title, 1 = author
 	Common::Array<int> _searchResults;
 	Common::Array<LibraryBook> _libraryBooks;
 	int _currentResult;
-	int _memorizedBookIndex;  // Index of book that was memorized (-1 if none)
+	int _memorizedBookIndex; // Index of book that was memorized (-1 if none)
 	int _lineHeight;
+	Common::String _titleMsg;
+	Common::String _authorMsg;
 
 	Common::Array<Common::StringArray> _computerText;
 
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index ee8e99f5b27..dd810023823 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -51,7 +51,7 @@ uint32 DialogManager::readTextBlock(
 	outText = "";
 
 	// Skip control bytes at start
-	if (data[pos] == CTRL_TEXT_TERMINATOR) {
+	if (data[pos] == kCtrlTextTerminator) {
 		pos += 2;
 	}
 
@@ -60,14 +60,14 @@ uint32 DialogManager::readTextBlock(
 	}
 
 	// Check for speaker ID marker
-	if (data[pos] == CTRL_SPEAKER_ID) {
+	if (data[pos] == kCtrlSpeakerId) {
 		pos++;
 		if (pos < dataSize) {
 			outSpeakerId = data[pos];
 		}
 	}
 	// Check for dialogue marker (choice text)
-	else if (data[pos] == CTRL_DIALOGUE_MARKER || data[pos] == CTRL_DIALOGUE_MARKER_ONEOFF) {
+	else if (data[pos] == kCtrlDialogueMarker || data[pos] == kCtrlDialogueMarkerOneoff) {
 		pos++; // Skip marker
 
 		// Skip choice index
@@ -79,23 +79,24 @@ uint32 DialogManager::readTextBlock(
 		pos += 2;
 	}
 
-	int lineIndex = data[++pos];
-	debug("Line index %d", lineIndex);
+
+	// Line index could be useful for translations
+	/*int lineIndex =  data[++pos]; */
+	pos++; // Skip line index
 	pos++; // blank
-	// debug("Reading text block starting at pos %u, line index %d, speaker ID %d", startPos, lineIndex, outSpeakerId);
 	// Read text until control byte
 	while (pos < dataSize) {
 		byte b = data[pos];
 
 		// End markers - stop reading text
-		if (b == CTRL_END_TEXT || b == CTRL_END_CONVERSATION || b == CTRL_ACTION_AND_END ||
-			b == CTRL_END_BRANCH || b == CTRL_DIALOGUE_MARKER || b == CTRL_DIALOGUE_MARKER_ONEOFF ||
-			b == CTRL_TEXT_TERMINATOR || b == CTRL_ALT_END_MARKER_1 || b == CTRL_ACTION_AND_CONTINUE ||
-			b == CTRL_GO_BACK || b == CTRL_SPEAKER_ID) {
+		if (b == kCtrlEndText || b == kCtrlEndConversation || b == kCtrlActionAndEnd ||
+			b == kCtrlEndBranch || b == kCtrlDialogueMarker || b == kCtrlDialogueMarkerOneoff ||
+			b == kCtrlTextTerminator || b == kCtrlAltEndMarker1 || b == kCtrlActionAndContinue ||
+			b == kCtrlGoBack || b == kCtrlSpeakerId) {
 			break;
 		}
 
-		if (b == CTRL_LINE_CONTINUE || b == CTRL_PAGE_BREAK_CONV) {
+		if (b == kCtrlLineContinue || b == kCtrlPageBreakConv) {
 			warning("Found unexpected line/page break control code in readTextBlock at pos %u", pos);
 			outText += ' ';
 			pos++;
@@ -103,7 +104,7 @@ uint32 DialogManager::readTextBlock(
 		}
 
 		// Regular text - does not need decoding
-		if (b >= CHAR_SPACE && b <= 0x83) {
+		if (b >= kCtrlSpace && b <= 0x83) {
 			outText += b;
 		}
 		pos++;
@@ -115,6 +116,7 @@ uint32 DialogManager::readTextBlock(
 void DialogManager::displayChoices(Common::Array<ChoiceOption> *choices, Graphics::ManagedSurface &compositeBuffer) {
 
 	int overlayHeight = choices->size() * kChoiceHeight + 2;
+	// Grab the overlay position to start drawing the choices, and draw the choices there
 	Common::Point overlayPos = _graphics->showOverlay(overlayHeight, compositeBuffer);
 	for (uint i = 0; i < choices->size(); i++) {
 		ChoiceOption choice = (*choices)[i];
@@ -145,10 +147,13 @@ void DialogManager::displayChoices(Common::Array<ChoiceOption> *choices, Graphic
 		}
 
 		if (choice.charOffset > 0) {
+			//draw left arrow
 			drawText(compositeBuffer, g_engine->_doubleSmallFont, _leftArrow, 0, yPos, g_engine->_doubleSmallFont->getCharWidth(17), lArrowColor);
 		}
 		drawText(compositeBuffer, g_engine->_doubleSmallFont, choice.text.substr(choice.charOffset, 76), kChoicePadding, yPos, 620, choiceColor);
+
 		if (choice.charOffset + 76 < choice.text.size()) {
+			//draw right arrow
 			drawText(compositeBuffer, g_engine->_doubleSmallFont, _rightArrow, 640 - kArrowWidth, yPos, g_engine->_doubleSmallFont->getCharWidth(16), rArrowColor);
 		}
 	}
@@ -187,7 +192,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 	int16 yBasePos = 0;
 	if (speakerId == kAlfredColor) {
 		if (g_engine->_state->getFlag(FLAG_FROM_INTRO) == true) {
-			debug("Setting special anim");
+			// Different talking animation for the post-intro sequence in which Alfred speaks in bed
 			g_engine->_alfredState.setState(ALFRED_SPECIAL_ANIM);
 		} else {
 			g_engine->_alfredState.setState(ALFRED_TALKING);
@@ -213,6 +218,9 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 	displayDialogue(dialogueLines, speakerId, xBasePos, yBasePos); // Default position
 }
 
+/**
+ * Simply wait tick period * char count
+ */
 uint32 calcPageTtlMs(Common::Array<Common::String> dialogueLine) {
     uint32 charCount = 0;
     for (uint i = 0; i < dialogueLine.size(); i++) {
@@ -240,7 +248,6 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 	_dialogActive = true;
 	int curPage = 0;
 	bool fromIntro = g_engine->_state->getFlag(FLAG_FROM_INTRO) == true;
-	debug("Displaying dialog, from intro = %d", fromIntro);
 
 	uint32 pageTtlMs = calcPageTtlMs(dialogueLines[curPage]);
 	uint32 pageStartMs = g_system->getMillis();
@@ -308,7 +315,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		}
 
 		if (fromIntro && g_engine->_res->_isSpecialAnimFinished) {
-			debug("Dismissing due to speciawl anim ending!");
+			// in post-intro, text stops only after the animation is done!
 			break;
 		}
 
@@ -323,7 +330,6 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 }
 
 void DialogManager::displayDialogue(Common::String text, byte speakerId) {
-	debug("Displaying dialogue: \"%s\" (Speaker ID: %d)", text.c_str(), speakerId);
 	displayDialogue(wordWrap(text), speakerId);
 }
 
@@ -379,19 +385,18 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 	int firstChoiceIndex = -1;
 
 	// Scan for choices with SAME index
-	// The key insight: choices at the same level may be scattered throughout the data,
-	// separated by deeper-level sub-branches. We must scan past higher-index choices
-	// to find all choices at our level, but stop when we hit a LOWER index.
+	// Choices with the same index are not contiguous; for each choice the entire branch is laid out first.
+	// So we must scan past higher-index choices to find all choices at our level, but stop when we hit a LOWER index.
 	while (pos < dataSize) {
 		byte b = data[pos];
 
 		// Stop at end markers
-		if (b == CTRL_ALT_END_MARKER_1 || b == CTRL_END_BRANCH || b == CTRL_ALT_SPEAKER_ROOT) {
+		if (b == kCtrlAltEndMarker1 || b == kCtrlEndBranch || b == kCtrlAltSpeakerRoot) {
 			break;
 		}
 
 		// Found a dialogue marker
-		if (b == CTRL_DIALOGUE_MARKER || b == CTRL_DIALOGUE_MARKER_ONEOFF) {
+		if (b == kCtrlDialogueMarker || b == kCtrlDialogueMarkerOneoff) {
 			if (pos + 1 < dataSize) {
 				int choiceIndex = data[pos + 1];
 
@@ -404,33 +409,33 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 				if (choiceIndex == firstChoiceIndex) {
 					ChoiceOption opt;
 					opt.room = g_engine->_room->_currentRoomNumber;
-					opt.shouldDisableOnSelect = b == CTRL_DIALOGUE_MARKER_ONEOFF;
+					opt.shouldDisableOnSelect = b == kCtrlDialogueMarkerOneoff;
 					opt.choiceIndex = choiceIndex;
 					opt.dataOffset = pos;
 					pos += 2; // Move past marker + index
-					if (data[pos] == CTRL_DISABLED_CHOICE) {
+					if (data[pos] == kCtrlDisabledChoice) {
 						opt.isDisabled = true;
 					}
 					// Parse the choice text
 					uint32 textPos = pos + 4;
 					while (textPos < dataSize) {
 						byte tb = data[textPos];
-						if (tb == CTRL_END_TEXT || tb == CTRL_DIALOGUE_MARKER ||
-							tb == CTRL_DIALOGUE_MARKER_ONEOFF || tb == CTRL_END_BRANCH ||
-							tb == CTRL_ALT_END_MARKER_1) {
+						if (tb == kCtrlEndText || tb == kCtrlDialogueMarker ||
+							tb == kCtrlDialogueMarkerOneoff || tb == kCtrlEndBranch ||
+							tb == kCtrlAltEndMarker1) {
 							// Check if there is a terminator (F4 or F8) at the end of this choice's response
 							// Scan forward but stop at another choice marker or branch end
 							uint32 scanPos = textPos;
 							while (scanPos < dataSize) {
 								byte sb = data[scanPos];
 								// Stop scanning at another choice marker or branch boundaries
-								if (sb == CTRL_DIALOGUE_MARKER || sb == CTRL_DIALOGUE_MARKER_ONEOFF ||
-									sb == CTRL_END_BRANCH || sb == CTRL_ALT_END_MARKER_1 ||
-									sb == CTRL_ALT_SPEAKER_ROOT) {
+								if (sb == kCtrlDialogueMarker || sb == kCtrlDialogueMarkerOneoff ||
+									sb == kCtrlEndBranch || sb == kCtrlAltEndMarker1 ||
+									sb == kCtrlAltSpeakerRoot) {
 									break;
 								}
 								// Found a conversation terminator - this choice ends the conversation
-								if (sb == CTRL_END_CONVERSATION || sb == CTRL_ACTION_AND_END) {
+								if (sb == kCtrlEndConversation || sb == kCtrlActionAndEnd) {
 									opt.hasConversationEndMarker = true;
 									break;
 								}
@@ -458,8 +463,6 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 					// This means we've gone past all choices at our level
 					break;
 				}
-				// If choiceIndex > firstChoiceIndex, we're in a deeper sub-branch
-				// Continue scanning to find more choices at our level
 			}
 		}
 
@@ -470,7 +473,7 @@ uint32 DialogManager::parseChoices(const byte *data, uint32 dataSize, uint32 sta
 }
 
 /**
- * Check if all sub-branches of the current choice level are exhausted.
+ * Check if all sub-branches of the current choice level are exhausted to mark the choice for disabling.
  *
  * Returns true if we should disable the current choice, which happens when:
  * - There are no FB sub-branches at higher indices, OR
@@ -493,27 +496,25 @@ bool DialogManager::checkAllSubBranchesExhausted(const byte *data, uint32 dataSi
 		// NOTE: Do NOT stop at F4 (CTRL_END_CONVERSATION) - F4 markers appear between
 		// choices as terminators for each choice's response path. We need to scan
 		// past them to find all choices at the target level.
-		if (b == CTRL_ALT_END_MARKER_1 || b == CTRL_END_BRANCH ||
-			b == CTRL_ALT_SPEAKER_ROOT) {
+		if (b == kCtrlAltEndMarker1 || b == kCtrlEndBranch ||
+			b == kCtrlAltSpeakerRoot) {
 			break;
 		}
 
 		// For that one bug in room 26
 		// treat F0 as a boundary to prevent scanning past unreachable choices
-		if (b == CTRL_GO_BACK && f0IsBoundary) {
+		if (b == kCtrlGoBack && f0IsBoundary) {
 			break;
 		}
 
 		// Found FB (one-time choice marker)
-		if (b == CTRL_DIALOGUE_MARKER_ONEOFF && pos + 2 < dataSize) {
+		if (b == kCtrlDialogueMarkerOneoff && pos + 2 < dataSize) {
 			byte choiceIdx = data[pos + 1];
 
 			// Only check sub-branches (higher index = deeper level)
 			if (choiceIdx > currentChoiceLevel) {
 				// Check if NOT disabled (no FA at pos+2)
-				if (data[pos + 2] != CTRL_DISABLED_CHOICE) {
-					debug("checkAllSubBranchesExhausted: Active FB at pos %u, idx %d (current %d) - NOT exhausted",
-						  pos, choiceIdx, currentChoiceLevel);
+				if (data[pos + 2] != kCtrlDisabledChoice) {
 					return false; // Don't disable parent
 				}
 			} else if (choiceIdx <= currentChoiceLevel) {
@@ -526,7 +527,6 @@ bool DialogManager::checkAllSubBranchesExhausted(const byte *data, uint32 dataSi
 		pos++;
 	}
 
-	debug("checkAllSubBranchesExhausted: All sub-branches exhausted at level %d", currentChoiceLevel);
 	return true;
 }
 
@@ -550,22 +550,21 @@ void DialogManager::setCurSprite(int index) {
 
 void DialogManager::startConversation(const byte *conversationData, uint32 dataSize, byte npcIndex, Sprite *animSet) {
 	if (!conversationData || dataSize == 0) {
-		debug("startConversation: No conversation data");
+		warning("startConversation: No conversation data");
 		return;
 	}
 	setCurSprite(animSet ? animSet->index : -1);
 
-	debug("Starting conversation with %d bytes of data, for npc %d, hotspot %d, currentRoot is = %d", dataSize, npcIndex, animSet ? animSet->index : -1, g_engine->_state->getCurrentRoot(g_engine->_room->_currentRoomNumber, npcIndex));
-
 	// Initialize conversation state
 	ConversationState state = initializeConversation(conversationData, dataSize, npcIndex);
 	bool skipToChoices = false;
 	Common::Stack<uint32> positionStack; // Stack to handle nested branches for "go back" functionality
+
 	// Main conversation loop
 	while (state.position < dataSize && !g_engine->shouldQuit()) {
 		state.position = skipControlBytes(conversationData, dataSize, state.position);
 
-		if (state.position < dataSize && conversationData[state.position] == CTRL_GO_BACK) {
+		if (state.position < dataSize && conversationData[state.position] == kCtrlGoBack) {
 			if (handleGoBack(conversationData, positionStack, state.position, state)) {
 				skipToChoices = true;
 			} else {
@@ -600,10 +599,10 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 
 			// If not at a choice marker, there's more dialogue to read
 			if (peekPos < dataSize &&
-				conversationData[peekPos] != CTRL_DIALOGUE_MARKER &&
-				conversationData[peekPos] != CTRL_DIALOGUE_MARKER_ONEOFF &&
-				conversationData[peekPos] != CTRL_END_CONVERSATION &&
-				conversationData[peekPos] != CTRL_DISABLED_CHOICE) {
+				conversationData[peekPos] != kCtrlDialogueMarker &&
+				conversationData[peekPos] != kCtrlDialogueMarkerOneoff &&
+				conversationData[peekPos] != kCtrlEndConversation &&
+				conversationData[peekPos] != kCtrlDisabledChoice) {
 				continue;
 			}
 		}
@@ -618,23 +617,15 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		uint originalChoiceCount = choices->size();
 		addGoodbyeOptionIfNeeded(choices, state.currentChoiceLevel, originalChoiceCount);
 
-		debug("Parsed %u choices", choices->size());
-		for (uint i = 0; i < choices->size(); i++) {
-			debug(" Choice %u (index %d): \"%s\" (Disabled: %s)", i, (*choices)[i].choiceIndex, (*choices)[i].text.c_str(),
-				  (*choices)[i].isDisabled ? "Yes" : "No");
-		}
-		debug("-----------------------");
-
 		if (choices->empty()) {
 			state.position = positionStack.empty() ? 0 : positionStack.pop();
 			if (state.position == 0) {
-				debug("No choices and no previous position to go back to, ending conversation");
+				// No choices and no previous position to go back to, ending conversation
 				break;
 			}
 			checkAllSubBranchesExhausted(conversationData, dataSize, state.position, state.currentChoiceLevel - 1);
-			debug("No choices found, popping back to previous choice menu, position %u", state.position);
+			// No choices found, popping back to previous choice menu, position %u
 			skipToChoices = true;
-			// state.position = peekPos;
 			continue;
 		}
 
@@ -653,7 +644,6 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 			}
 
 			if (!foundExpectedLevel) {
-				debug("No choices found at level %d or %d, ending conversation", state.currentChoiceLevel, state.currentChoiceLevel + 1);
 				break;
 			}
 		}
@@ -661,8 +651,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		// Process user selection
 		int selectedIndex = 0;
 		if (choices->size() == 1) {
-			// Auto-dialogue: display it automatically
-			debug("Auto-selecting single choice: \"%s\"", (*choices)[0].text.c_str());
+			// Auto-dialogue: display it automatically if ony one choice!
 			selectedIndex = 0;
 		} else {
 			// Real choice: show menu and wait for selection
@@ -685,6 +674,9 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 	debug("Conversation ended");
 }
 
+/**
+ * Finds a conversation root for a given NPC
+ */
 uint32 DialogManager::findRoot(int npc, int &currentRoot, uint32 currentPosition, uint32 dataSize, const byte *conversationData) {
 	// Check if a specific root has been set for this room
 	int targetRoot = g_engine->_state->getCurrentRoot(g_engine->_room->_currentRoomNumber, npc);
@@ -692,7 +684,7 @@ uint32 DialogManager::findRoot(int npc, int &currentRoot, uint32 currentPosition
 	if (targetRoot >= 0) {
 		// Skip to the specified root
 		while (currentRoot < targetRoot && currentPosition < dataSize) {
-			if (conversationData[currentPosition] == CTRL_END_BRANCH) {
+			if (conversationData[currentPosition] == kCtrlEndBranch) {
 				currentPosition++; // Move past end branch marker
 				currentRoot++;
 			} else {
@@ -701,17 +693,19 @@ uint32 DialogManager::findRoot(int npc, int &currentRoot, uint32 currentPosition
 		}
 	}
 	// If targetRoot is -1 or not set, use the first root (default behavior)
-
 	return currentPosition;
 }
 
+/**
+ * Find the tree for the given NPC.
+ */
 uint32 DialogManager::findSpeaker(byte npcIndex, uint32 dataSize, const byte *conversationData) {
 	// Find the speaker tree for this NPC; they are marked by 0xFE 0xXX where XX is NPC index + 1
 	bool speakerTreeOffsetFound = false;
 	int currentConversationTree = npcIndex + 1;
 	uint32 position = 0;
 	while (position < dataSize && !speakerTreeOffsetFound) {
-		if (conversationData[position] == CTRL_ALT_SPEAKER_ROOT && conversationData[position + 1] == currentConversationTree) {
+		if (conversationData[position] == kCtrlAltSpeakerRoot && conversationData[position + 1] == currentConversationTree) {
 			speakerTreeOffsetFound = true;
 			position += 2; // Move past the speaker tree marker and npc index
 		} else {
@@ -722,11 +716,9 @@ uint32 DialogManager::findSpeaker(byte npcIndex, uint32 dataSize, const byte *co
 }
 
 // Skip control bytes that should be ignored
-// NOTE: 0xEB (CTRL_ALT_END_MARKER_2) must NOT be skipped here — it is an action-and-continue
-// trigger that must be processed by checkConversationEnd, not silently discarded.
 uint32 DialogManager::skipControlBytes(const byte *data, uint32 dataSize, uint32 position) {
 	while (position < dataSize &&
-		   data[position] == CTRL_ALT_END_MARKER_1) {
+		   data[position] == kCtrlAltEndMarker1) {
 		position++;
 	}
 	return position;
@@ -736,8 +728,8 @@ uint32 DialogManager::skipControlBytes(const byte *data, uint32 dataSize, uint32
 uint32 DialogManager::peekNextMeaningfulByte(const byte *data, uint32 dataSize, uint32 position) {
 	uint32 peekPos = position;
 	while (peekPos < dataSize &&
-		   (data[peekPos] == CTRL_ALT_END_MARKER_1 ||
-			data[peekPos] == CTRL_TEXT_TERMINATOR)) {
+		   (data[peekPos] == kCtrlAltEndMarker1 ||
+			data[peekPos] == kCtrlTextTerminator)) {
 		peekPos++;
 	}
 	return peekPos;
@@ -753,7 +745,7 @@ ConversationState DialogManager::initializeConversation(const byte *data, uint32
 	state.lastSelectedChoice = ChoiceOption();
 
 	// Skip any junk at start until we find a speaker marker
-	while (state.position < dataSize && data[state.position] != CTRL_SPEAKER_ID) {
+	while (state.position < dataSize && data[state.position] != kCtrlSpeakerId) {
 		state.position++;
 	}
 
@@ -765,24 +757,21 @@ ConversationState DialogManager::initializeConversation(const byte *data, uint32
 // The cascading disable in disableChoiceIfNeeded should have already disabled
 // the choices that led here. We just need to go back to the parent level.
 bool DialogManager::handleGoBack(const byte *data, Common::Stack<uint32> &positionStack, uint32 position, ConversationState &state) {
-	if (data[position] != CTRL_GO_BACK) {
+	if (data[position] != kCtrlGoBack) {
 		return false;
 	}
 
-	debug("F0 Go Back hit at position %u, current level %d", position, state.currentChoiceLevel);
-
 	// Pop position stack - we're going back to parent level
 	uint32 parentPos = positionStack.empty() ? 0 : positionStack.pop();
 
 	if (parentPos == 0) {
-		debug("F0: No parent position on stack, ending conversation");
+		// F0: No parent position on stack, ending conversation
 		return false;
 	}
 
 	// Go up one level
 	state.currentChoiceLevel--;
 	state.position = parentPos;
-	debug("F0: Moved back to level %d, position %u", state.currentChoiceLevel, parentPos);
 
 	return true;
 }
@@ -816,7 +805,7 @@ ConversationEndResult DialogManager::checkConversationEnd(const byte *data, uint
 
 	byte controlByte = data[position];
 
-	if (controlByte == CTRL_END_CONVERSATION) {
+	if (controlByte == kCtrlEndConversation) {
 		// Bug in the original in room 45, root 1: The conversation data has F4 (END_CONV) after
 		// the opening NPC text instead of FD (END_TEXT), so the 3 choices that follow are
 		// unreachable. Treat F4 as FD specifically for this root to restore them.
@@ -825,28 +814,25 @@ ConversationEndResult DialogManager::checkConversationEnd(const byte *data, uint
 		if (room == 45 && currentRoot == 1 &&
 			peekPos < dataSize &&
 			(data[peekPos] == kCtrlDialogueMarker || data[peekPos] == kCtrlDialogueMarkerOneoff)) {
-			debug("Room 45 Root 1: F4 followed by choice marker treated as FD (data bug workaround)");
 			result.nextPosition = position + 1;
 			return result;
 		}
-		debug("End of conversation marker found");
 		result.shouldEnd = true;
 		return result;
 	}
 
-	if (controlByte == CTRL_ACTION_AND_END) {
+	if (controlByte == kCtrlActionAndEnd) {
 		result.actionCode = data[position + 1] | (data[position + 2] << 8);
-		debug("Action-and-end trigger %d encountered!", result.actionCode);
+		// Action-and-end trigger encountered
 		result.shouldEnd = true;
 		result.hasAction = true;
 		return result;
 	}
 
-	if (controlByte == CTRL_ACTION_AND_CONTINUE) {
-		// 0xEB: action-and-continue — dispatch the action but do NOT exit the conversation.
+	if (controlByte == kCtrlActionAndContinue) {
 		if (position + 2 < dataSize) {
 			result.actionCode = data[position + 1] | (data[position + 2] << 8);
-			debug("Action-and-continue trigger %d encountered", result.actionCode);
+			// Action-and-continue trigger encountered
 			result.hasAction = true;
 		}
 		result.shouldEnd = false;
@@ -855,16 +841,24 @@ ConversationEndResult DialogManager::checkConversationEnd(const byte *data, uint
 	}
 
 	// Move past control byte
-	if (controlByte == CTRL_END_TEXT) {
+	if (controlByte == kCtrlEndText) {
 		result.nextPosition = position + 1;
 	}
 
 	return result;
 }
 
+/**
+ * When there are no choices that lead to ending conversation, a generic option is added, as long as
+ * _goodbyeDisabled is not set for the room.
+ *
+ * So Goodbye is added if:
+ * - Goodbye is not globally disabled for this room, AND
+ * - There are multiple choices (if only 1, it's auto-dialogue and shouldn't have goodbye), AND
+ * - None of the choices already have a conversation terminator (F4 or F8) in their response path
+ */
 void DialogManager::addGoodbyeOptionIfNeeded(Common::Array<ChoiceOption> *choices, int currentChoiceLevel, uint originalChoiceCount) {
 	// Room entry handlers can globally disable the goodbye option for certain rooms
-	// (e.g. rooms 39/40 pharaoh, room 48).
 	if (_goodbyeDisabled) {
 		return;
 	}
@@ -926,8 +920,8 @@ uint32 DialogManager::processChoiceSelection(
 
 	if (!choiceText.empty() && choiceText.size() > 1) {
 		displayDialogue(choiceText, kAlfredColor);
-		debug("Will check if choice should be disabled after displaying dialogue");
-		disableChoiceIfNeeded(choices, selectedIndex, data, dataSize, endPos, state);
+		//Will check if choice should be disabled after displaying dialogue
+		maybeDisableChoice(choices, selectedIndex, data, dataSize, endPos, state);
 	}
 
 	position = endPos;
@@ -935,21 +929,25 @@ uint32 DialogManager::processChoiceSelection(
 	// Skip past end marker
 	if (position < dataSize) {
 		byte endByte = data[position];
-		if (endByte == CTRL_END_TEXT || endByte == CTRL_END_BRANCH ||
-			endByte == CTRL_ACTION_AND_END) {
+		if (endByte == kCtrlEndText || endByte == kCtrlEndBranch ||
+			endByte == kCtrlActionAndEnd) {
 			position++;
 		}
 	}
 
 	return position;
 }
-void DialogManager::disableChoiceIfNeeded(Common::Array<Pelrock::ChoiceOption> *choices, int selectedIndex, const byte *data, uint32 dataSize, uint32 endPos, Pelrock::ConversationState &state) {
+
+void DialogManager::maybeDisableChoice(Common::Array<Pelrock::ChoiceOption> *choices, int selectedIndex, const byte *data, uint32 dataSize, uint32 endPos, Pelrock::ConversationState &state) {
 	// Cascading parent disable:
 	// 1. Check if current choice's sub-branches are exhausted
 	// 2. If so AND it's 0xFB, disable the current choice
 	// 3. Go up to parent level, check if parent's sub-branches are exhausted
 	// 4. Continue until we find a level with active sub-branches or reach level 1
 
+	// Basically this means if the current choice getting disabled was also the last sub-branch of the previous level, that choice also
+	// has to be disabled.
+
 	// Start with the currently selected choice
 	int currentLevel = state.currentChoiceLevel;
 	uint32 currentChoicePos = (*choices)[selectedIndex].dataOffset;
@@ -960,18 +958,15 @@ void DialogManager::disableChoiceIfNeeded(Common::Array<Pelrock::ChoiceOption> *
 		bool allExhausted = checkAllSubBranchesExhausted(data, dataSize, currentChoicePos + 4, currentLevel);
 
 		if (!allExhausted) {
-			debug("Cascading disable stopped at level %d - active sub-branches found", currentLevel);
 			break;
 		}
 
 		// Check if this choice is F1 (repeatable) - don't disable
 		if (!isCurrentFB) {
-			debug("Choice at level %d is repeatable (F1), stopping cascade", currentLevel);
 			break;
 		}
 
 		// Disable this one-time choice
-		debug("Cascading disable: level %d, offset %u", currentLevel, currentChoicePos);
 		ChoiceOption choiceToDisable;
 		choiceToDisable.room = g_engine->_room->_currentRoomNumber;
 		choiceToDisable.dataOffset = currentChoicePos;
@@ -981,7 +976,7 @@ void DialogManager::disableChoiceIfNeeded(Common::Array<Pelrock::ChoiceOption> *
 
 		// Stop if we've reached level 1
 		if (currentLevel <= 1) {
-			debug("Reached level 1, stopping cascading disable");
+			// Reached level 1, stopping cascading disable
 			break;
 		}
 
@@ -995,7 +990,7 @@ void DialogManager::disableChoiceIfNeeded(Common::Array<Pelrock::ChoiceOption> *
 			byte b = data[scanPos];
 
 			// Found 0xFB marker
-			if (b == CTRL_DIALOGUE_MARKER_ONEOFF && scanPos + 1 < dataSize) {
+			if (b == kCtrlDialogueMarkerOneoff && scanPos + 1 < dataSize) {
 				byte idx = data[scanPos + 1];
 				if (idx == (byte)currentLevel) {
 					currentChoicePos = scanPos;
@@ -1006,34 +1001,34 @@ void DialogManager::disableChoiceIfNeeded(Common::Array<Pelrock::ChoiceOption> *
 				}
 			}
 			// Found 0xF1 marker (repeatable)
-			else if (b == CTRL_DIALOGUE_MARKER && scanPos + 1 < dataSize) {
+			else if (b == kCtrlDialogueMarker && scanPos + 1 < dataSize) {
 				byte idx = data[scanPos + 1];
 				if (idx == (byte)currentLevel) {
 					// Found 0xF1 parent - will stop cascade on next iteration
 					currentChoicePos = scanPos;
 					isCurrentFB = false;
 					foundParent = true;
-					debug("Found parent 0xF1 at level %d, pos %u - will stop cascade", currentLevel, currentChoicePos);
 					break;
 				}
 			}
 
 			// Hit boundary markers - stop searching
-			if (b == CTRL_ALT_SPEAKER_ROOT || b == CTRL_END_BRANCH || b == CTRL_ALT_END_MARKER_1) {
-				debug("Hit boundary at pos %u while looking for parent level %d", scanPos, currentLevel);
+			if (b == kCtrlAltSpeakerRoot || b == kCtrlEndBranch || b == kCtrlAltEndMarker1) {
 				break;
 			}
 		}
 
 		if (!foundParent) {
-			debug("Could not find parent at level %d, stopping cascade", currentLevel);
 			break;
 		}
 	}
 }
+
+/**
+ * Convenience method if we know it's Alfred talking
+ */
 void DialogManager::sayAlfred(Common::StringArray texts) {
 	if (g_engine->_state->getFlag(FLAG_FROM_INTRO) == true) {
-		debug("Setting special anim");
 		g_engine->_alfredState.setState(ALFRED_SPECIAL_ANIM);
 	} else {
 		g_engine->_alfredState.setState(ALFRED_TALKING);
@@ -1044,6 +1039,9 @@ void DialogManager::sayAlfred(Common::StringArray texts) {
 	displayDialogue(textLines, kAlfredColor);
 }
 
+/**
+ * Convenience method for Descriptions
+ */
 void DialogManager::sayAlfred(Description description) {
 	Common::StringArray texts;
 
@@ -1054,6 +1052,10 @@ void DialogManager::sayAlfred(Description description) {
 	}
 }
 
+/**
+ * Convenience method when we want to enforce a specific NPC to speak the line.
+ * Used mostly when it's npc index 1
+ */
 void DialogManager::say(Common::StringArray texts, byte spriteIndex) {
 	if (texts.empty()) {
 		return;
@@ -1075,6 +1077,10 @@ void DialogManager::say(Common::StringArray texts, byte spriteIndex) {
 	}
 }
 
+/**
+ * Convenience method to say a line normally but enforce x and y.
+ * Regular path will simply use the sprite's x and y
+ */
 void DialogManager::say(Common::StringArray texts, int16 x, int16 y) {
 	if (texts.empty()) {
 		return;
@@ -1091,6 +1097,9 @@ void DialogManager::say(Common::StringArray texts, int16 x, int16 y) {
 	}
 }
 
+/**
+ * Read from the formatted string the color code, and trim text and control chars
+ */
 bool DialogManager::processColorAndTrim(Common::StringArray &lines, byte &speakerId) {
 	int speakerMarker = lines[0][0];
 	speakerId = lines[0][1];
@@ -1118,7 +1127,7 @@ bool DialogManager::processColorAndTrim(Common::StringArray &lines, byte &speake
 }
 
 bool isEndMarker(byte char_byte) {
-	return char_byte == CTRL_END_TEXT || char_byte == CTRL_END_CONVERSATION || char_byte == CTRL_ACTION_AND_END || char_byte == CTRL_GO_BACK;
+	return char_byte == kCtrlEndText || char_byte == kCtrlEndConversation || char_byte == kCtrlActionAndEnd || char_byte == kCtrlGoBack;
 }
 
 int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
@@ -1126,7 +1135,7 @@ int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
 	int pos = startPos;
 	while (pos < text.size()) {
 		char char_byte = text[pos];
-		if (char_byte == CHAR_SPACE || isEndMarker(char_byte)) {
+		if (char_byte == kCtrlSpace || isEndMarker(char_byte)) {
 			break;
 		}
 		wordLength++;
@@ -1137,11 +1146,11 @@ int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
 		isEnd = true;
 	}
 	if (pos < text.size() && !isEnd) {
-		if ((byte)text[pos] == CTRL_ACTION_AND_END) { // 0xF8 (-8) special case
+		if ((byte)text[pos] == kCtrlActionAndEnd) { // 0xF8 (-8) special case
 			wordLength += 3;
 		} else {
 			// Count all consecutive spaces
-			while (pos < text.size() && text[pos] == CHAR_SPACE) {
+			while (pos < text.size() && text[pos] == kCtrlSpace) {
 				wordLength++;
 				pos++;
 			}
@@ -1150,6 +1159,11 @@ int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
 	return wordLength;
 }
 
+/**
+ * Wrap a String into pages of multiple Strings.
+ * The game enforces a maximum of 47 characters per line and 5 lines per page.
+ * If a String is longer than that it gets broken down into multiple pages.
+ */
 Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::String text) {
 	Common::Array<Common::Array<Common::String>> pages;
 	Common::Array<Common::String> currentPage;
@@ -1182,7 +1196,7 @@ Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::Str
 
 		if (charsRemaining == 0 && isEnd) {
 			Common::String lineText = joinStrings(currentLine, "");
-			while (lineText.lastChar() == CHAR_SPACE) {
+			while (lineText.lastChar() == kCtrlSpace) {
 				lineText = lineText.substr(0, lineText.size() - 1);
 			}
 			int trailingSpaces = currentLine.size() - lineText.size();
@@ -1210,7 +1224,7 @@ Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::Str
 
 	if (!currentLine.empty()) {
 		Common::String lineText = joinStrings(currentLine, "");
-		while (lineText.lastChar() == CHAR_SPACE) {
+		while (lineText.lastChar() == kCtrlSpace) {
 			lineText = lineText.substr(0, lineText.size() - 1);
 		}
 		currentPage.push_back(lineText);
@@ -1220,13 +1234,6 @@ Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::Str
 		pages.push_back(currentPage);
 	}
 
-	// print all the pages and lines for debugging
-	// for (uint i = 0; i < pages.size(); i++) {
-	// 	debug("Page %d:", i);
-	// 	for (uint j = 0; j < pages[i].size(); j++) {
-	// 		debug(" Line %d: \"%s\"", j, pages[i][j].c_str());
-	// 	}
-	// }
 	return pages;
 }
 
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index a808b763c70..cda61767630 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -34,39 +34,23 @@
 
 namespace Pelrock {
 
-// Control character codes (negative values in signed char)
-#define CHAR_SPACE 0x20                  /* ' ' */
-#define CTRL_SPEAKER_ID 0x08             /* Next byte is speaker ID (color) */
-const byte kCtrlEndText             = 0xFD; /* End of text segment */
-const byte kCtrlTextTerminator      = 0xFC; /* Text terminator */
-const byte kCtrlDialogueMarker      = 0xF1; /* Choice marker that sticks */
-const byte kCtrlDisabledChoice      = 0xFA; /* Disabled choice marker */
-const byte kCtrlPageBreakConv       = 0xF9; /* Page break in conversation */
-const byte kCtrlActionAndEnd        = 0xF8; /* Action trigger */
-const byte kCtrlEndBranch           = 0xF7; /* End of branch */
-const byte kCtrlLineContinue        = 0xF6; /* Line continue/newline */
-const byte kCtrlAltEndMarker1       = 0xF5; /* Alt end marker - do nothing */
-const byte kCtrlEndConversation     = 0xF4; /* End conversation and disable option */
-const byte kCtrlDialogueMarkerOneoff = 0xFB; /* Alt choice marker that disappears */
-const byte kCtrlGoBack              = 0xF0; /* Go back in conversation */
-const byte kCtrlActionAndContinue   = 0xEB; /* Action-and-continue: dispatch action, conversation keeps going (unlike 0xF8 which exits) */
-const byte kCtrlAltSpeakerRoot      = 0xFE; /* Separates conversations from different speakers */
-
-// Keep old names as aliases for compatibility
-#define CTRL_END_TEXT           kCtrlEndText
-#define CTRL_TEXT_TERMINATOR    kCtrlTextTerminator
-#define CTRL_DIALOGUE_MARKER    kCtrlDialogueMarker
-#define CTRL_DISABLED_CHOICE    kCtrlDisabledChoice
-#define CTRL_PAGE_BREAK_CONV    kCtrlPageBreakConv
-#define CTRL_ACTION_AND_END     kCtrlActionAndEnd
-#define CTRL_END_BRANCH         kCtrlEndBranch
-#define CTRL_LINE_CONTINUE      kCtrlLineContinue
-#define CTRL_ALT_END_MARKER_1   kCtrlAltEndMarker1
-#define CTRL_END_CONVERSATION   kCtrlEndConversation
-#define CTRL_DIALOGUE_MARKER_ONEOFF kCtrlDialogueMarkerOneoff
-#define CTRL_GO_BACK            kCtrlGoBack
-#define CTRL_ACTION_AND_CONTINUE kCtrlActionAndContinue
-#define CTRL_ALT_SPEAKER_ROOT   kCtrlAltSpeakerRoot
+// Control character codes
+const byte kCtrlSpace                = 0x20; /* ' ' */
+const byte kCtrlSpeakerId            = 0x08; /* Next byte is speaker ID (color) */
+const byte kCtrlEndText              = 0xFD; /* End of text segment */
+const byte kCtrlTextTerminator       = 0xFC; /* Text terminator */
+const byte kCtrlDialogueMarker       = 0xF1; /* Choice marker that sticks */
+const byte kCtrlDialogueMarkerOneoff = 0xFB; /* Choice marker that disappears after use */
+const byte kCtrlDisabledChoice       = 0xFA; /* Disabled choice marker, generally only after usage */
+const byte kCtrlPageBreakConv        = 0xF9; /* Page break in conversation */
+const byte kCtrlActionAndEnd         = 0xF8; /* Action trigger and end conversation */
+const byte kCtrlActionAndContinue    = 0xEB; /* Action-and-continue: dispatch action, conversation keeps going (unlike 0xF8 which exits) */
+const byte kCtrlEndBranch            = 0xF7; /* End of branch */
+const byte kCtrlLineContinue         = 0xF6; /* Line continue/newline */
+const byte kCtrlAltEndMarker1        = 0xF5; /* Alt end marker - do nothing */
+const byte kCtrlEndConversation      = 0xF4; /* End conversation and disable option */
+const byte kCtrlGoBack               = 0xF0; /* Go back in conversation */
+const byte kCtrlAltSpeakerRoot       = 0xFE; /* Separates conversations from different speakers */
 
 // Helper structures for conversation state management
 struct ConversationState {
@@ -84,13 +68,13 @@ struct ConversationEndResult {
 };
 
 class DialogManager {
-	const static int kMaxChoiceChars = 50; // Max characters to show for a choice option (for truncation)
-	const static int kArrowWidth = 8;      // Width of arrow character for scroll
-	const static int kChoicePadding = 16;  // padding for the choice text surface
+	const static int kArrowWidth = 8;     // Width of arrow character for scroll
+	const static int kChoicePadding = 16; // padding for the choice text surface
 private:
 	Graphics::Screen *_screen = nullptr;
 	PelrockEventManager *_events = nullptr;
 	GraphicsManager *_graphics = nullptr;
+	// Current talking sprite, to disable and replace with talking animation
 	Sprite *_curSprite = nullptr;
 
 	// Private helper functions for conversation parsing
@@ -100,10 +84,8 @@ private:
 	uint32 readTextBlock(const byte *data, uint32 dataSize, uint32 startPos, Common::String &outText, byte &outSpeakerId);
 	uint32 parseChoices(const byte *data, uint32 dataSize, uint32 startPos, Common::Array<ChoiceOption> *outChoices);
 	void setCurSprite(int index);
-	void checkMouse();
 	bool checkAllSubBranchesExhausted(const byte *data, uint32 dataSize, uint32 startPos, int currentChoiceLevel);
 
-	// Refactored helper functions for startConversation
 	uint32 skipControlBytes(const byte *data, uint32 dataSize, uint32 position);
 	uint32 peekNextMeaningfulByte(const byte *data, uint32 dataSize, uint32 position);
 	ConversationState initializeConversation(const byte *data, uint32 dataSize, byte npcIndex);
@@ -112,7 +94,7 @@ private:
 	ConversationEndResult checkConversationEnd(const byte *data, uint32 dataSize, uint32 position, int currentRoot = -1);
 	void addGoodbyeOptionIfNeeded(Common::Array<ChoiceOption> *choices, int currentChoiceLevel, uint originalChoiceCount);
 	uint32 processChoiceSelection(const byte *data, uint32 dataSize, Common::Array<ChoiceOption> *choices, int selectedIndex, ConversationState &state);
-	void disableChoiceIfNeeded(Common::Array<Pelrock::ChoiceOption> *choices, int selectedIndex, const byte *data, uint32 dataSize, uint32 endPos, Pelrock::ConversationState &state);
+	void maybeDisableChoice(Common::Array<Pelrock::ChoiceOption> *choices, int selectedIndex, const byte *data, uint32 dataSize, uint32 endPos, Pelrock::ConversationState &state);
 
 public:
 	DialogManager(Graphics::Screen *screen, PelrockEventManager *events, GraphicsManager *graphics);
diff --git a/engines/pelrock/fonts/large_font.cpp b/engines/pelrock/fonts/large_font.cpp
index 8d4609c5770..98048e39d80 100644
--- a/engines/pelrock/fonts/large_font.cpp
+++ b/engines/pelrock/fonts/large_font.cpp
@@ -58,7 +58,7 @@ bool LargeFont::load(const Common::String &filename) {
 	memset(_fontData, 0, dataSize);
 	for (int c = 0; c < numChars; c++) {
 		// Temporary bitmap for character + border
-		bool mask[paddedHeight][paddedWidth] = {false};
+		bool mask[paddedHeight][paddedWidth] = {{false}};
 		// Decode character pixels from rawFontData
 		int charOffset = c * 0x30;
 		for (int i = 0; i < charHeight; i++) {
@@ -72,7 +72,7 @@ bool LargeFont::load(const Common::String &filename) {
 			}
 		}
 		// adds a border mask to the original font
-		bool borderMask[paddedHeight][paddedWidth] = {false};
+		bool borderMask[paddedHeight][paddedWidth] = {{false}};
 
 		for (int y = 0; y < paddedHeight; y++) {
 			for (int x = 0; x < paddedWidth; x++) {
diff --git a/engines/pelrock/fonts/small_font.h b/engines/pelrock/fonts/small_font.h
index cd80c690a1b..3bde2c152b0 100644
--- a/engines/pelrock/fonts/small_font.h
+++ b/engines/pelrock/fonts/small_font.h
@@ -44,8 +44,8 @@ public:
 	void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
 
 	byte *_fontData;
-protected:
 
+protected:
 private:
 	static const int CHAR_WIDTH = 8;
 	static const int CHAR_HEIGHT = 8;
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 99fdb4cd677..3ebe7a50af5 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -506,7 +506,7 @@ Common::Array<Common::StringArray> ResourceManager::processTextData(byte *data,
 	Common::StringArray lines;
 	Common::Array<Common::StringArray> texts;
 	while (pos < size) {
-		if (data[pos] == CTRL_END_TEXT) {
+		if (data[pos] == kCtrlEndText) {
 			lines.push_back(desc);
 			texts.push_back(lines);
 			lines.clear();
@@ -519,7 +519,7 @@ Common::Array<Common::StringArray> ResourceManager::processTextData(byte *data,
 			continue;
 		}
 
-		if (data[pos] == CTRL_SPEAKER_ID) {
+		if (data[pos] == kCtrlSpeakerId) {
 			byte color = data[pos + 1];
 			desc.append(1, '@');
 			desc.append(1, color);


Commit: 89189c032b318f17772b8871b8721734010d11fd
    https://github.com/scummvm/scummvm/commit/89189c032b318f17772b8871b8721734010d11fd
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:40+02:00

Commit Message:
PELROCK: Cleanup of menu and graphics

Changed paths:
    engines/pelrock/events.cpp
    engines/pelrock/events.h
    engines/pelrock/graphics.cpp
    engines/pelrock/graphics.h
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h


diff --git a/engines/pelrock/events.cpp b/engines/pelrock/events.cpp
index effa865cc6f..c12669baa86 100644
--- a/engines/pelrock/events.cpp
+++ b/engines/pelrock/events.cpp
@@ -20,10 +20,10 @@
  */
 #include "common/events.h"
 
+#include "events.h"
 #include "pelrock/events.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/util.h"
-#include "events.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/events.h b/engines/pelrock/events.h
index c6f477824ca..3198089d687 100644
--- a/engines/pelrock/events.h
+++ b/engines/pelrock/events.h
@@ -30,6 +30,7 @@ class PelrockEventManager {
 private:
 	Common::Event _event;
 	uint32 _clickTime = 0;
+
 public:
 	int16 _mouseX = 0;
 	int16 _mouseY = 0;
diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index eb8d2b6585e..6d8b3e1173f 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -49,30 +49,6 @@ Common::Point GraphicsManager::showOverlay(int height, Graphics::ManagedSurface
 	return Common::Point(overlayX, overlayY);
 }
 
-byte *GraphicsManager::grabBackgroundSlice(Graphics::ManagedSurface &buf, int x, int y, int w, int h) {
-	byte *bg = new byte[w * h];
-	for (int j = 0; j < w; j++) {
-		for (int i = 0; i < h; i++) {
-			int idx = i * w + j;
-			if (y + i < 400 && x + j < 640) {
-				*(bg + idx) = (byte)buf.getPixel(x + j, y + i);
-			}
-		}
-	}
-	return bg;
-}
-
-void GraphicsManager::putBackgroundSlice(Graphics::ManagedSurface &buf, int x, int y, int w, int h, byte *slice) {
-	for (int i = 0; i < w; i++) {
-		for (int j = 0; j < h; j++) {
-			int index = (j * w + i);
-			if (x + i < 640 && y + j < 400) {
-				buf.setPixel(x + i, y + j, slice[index]);
-			}
-		}
-	}
-}
-
 void GraphicsManager::fadeToBlack(int stepSize) {
 	byte palette[768];
 	g_system->getPaletteManager()->grabPalette(palette, 0, 256);
diff --git a/engines/pelrock/graphics.h b/engines/pelrock/graphics.h
index c205d1bbedd..1616c0aa215 100644
--- a/engines/pelrock/graphics.h
+++ b/engines/pelrock/graphics.h
@@ -22,8 +22,8 @@
 #define PELROCK_GRAPHICS_H
 
 #include "common/array.h"
-#include "common/str-array.h"
 #include "common/scummsys.h"
+#include "common/str-array.h"
 
 #include "graphics/font.h"
 #include "graphics/managed_surface.h"
@@ -40,8 +40,6 @@ public:
 
 	// Overlay / palette utilities
 	Common::Point showOverlay(int height, Graphics::ManagedSurface &buf);
-	byte *grabBackgroundSlice(Graphics::ManagedSurface &buf, int x, int y, int w, int h);
-	void putBackgroundSlice(Graphics::ManagedSurface &buf, int x, int y, int w, int h, byte *slice);
 	void fadeToBlack(int stepSize);
 	void fadePaletteToTarget(byte *targetPalette, int stepSize);
 	void clearScreen();
@@ -69,12 +67,7 @@ public:
 	/** Water reflection: mirrors buf pixels at (x,y) for water-palette pixels. */
 	void reflectionEffect(byte *buf, int x, int y, int width, int height);
 
-
 	// scaling
-	/**
-	 * Initializes _widthScalingTable / _heightScalingTable.
-	 * Must be called once during engine init, before any drawAlfred().
-	 */
 	void calculateScalingMasks();
 
 	/**
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index af9af7135bc..e6c0bd4764b 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -129,7 +129,6 @@ MainMenuButton MenuManager::isMainMenuButtonUnder(int x, int y) {
 	if (_menuState != MAIN_MENU) {
 		return NO_MAIN_BUTTON;
 	}
-
 	if (_questionMarkRect.contains(x, y)) {
 		return QUESTION_MARK_BUTTON;
 	}
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 6cc811609d2..a694caf5712 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -55,56 +55,56 @@ enum SoundMenuButton {
 };
 
 static const int kCreditsOrder[34] = {
-	5, // PROGRAMACION
-	 8, // Juan Jose Gil
-	 20, // Jose Vicente Pons
-	 24, // LuisFer Fernandez
-	 22, // Fernando Perez
-	 7, // GRAFICOS
+	5,  // PROGRAMACION
+	8,  // Juan Jose Gil
+	20, // Jose Vicente Pons
+	24, // LuisFer Fernandez
+	22, // Fernando Perez
+	7,  // GRAFICOS
 	12, // Queral,
 	14, // Ana maria polo
 	18, // Juan Arocas
 	16, // Gost
 	26, // Astorga
 	28, // Santi Sanz
-	2, // Fernando Aparicio
-	11,// INTRODUCCION
+	2,  // Fernando Aparicio
+	11, // INTRODUCCION
 	12, // Queral,
 	26, // Astorga
 	28, // Santi Sanz
-	9, // MUSICA
-	6, // Rufino Acosta
+	9,  // MUSICA
+	6,  // Rufino Acosta
 	13, // GUION
-	8, // Juan Jose Gil
+	8,  // Juan Jose Gil
 	19, // DIALOGOS
-	4, // Vicent raul arnau,
-	8, // Juan Jose Gil,
+	4,  // Vicent raul arnau,
+	8,  // Juan Jose Gil,
 	21, // PROBADORES
-	0, //David Burgos
+	0,  // David Burgos
 	10, // Alberto Leon
-	1, // Carles Pons
-	3, // Roman Pons
+	1,  // Carles Pons
+	3,  // Roman Pons
 	25, // Andres Ruiz,
 	27, // Juan Jose Ruiz
 	23, // Marilo
 	15, // PRODUCCION
-	17 // DDM
+	17  // DDM
 };
 
 static const char *inventorySounds[113] = {
 
-	"HOJASZZZ.SMP", // 0 - Default leaf rustle
-	"11ZZZZZZ.SMP", // 1 -
+	"HOJASZZZ.SMP", // 0
 	"11ZZZZZZ.SMP",
 	"11ZZZZZZ.SMP",
-	"GLASS1ZZ.SMP", // 4 - Glass clink (brick)
 	"11ZZZZZZ.SMP",
-	"ELEC3ZZZ.SMP", // 6 - Electric zap
-	"REMATERL.SMP", // 7 - Rematerialize
-	"81ZZZZZZ.SMP", // 8 - (numbered SFX)
-	"11ZZZZZZ.SMP", // 9
-	"SSSHTZZZ.SMP", // 10 - Shushing
-	"HOJASZZZ.SMP", // 11 - Default leaf rustle
+	"GLASS1ZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"ELEC3ZZZ.SMP",
+	"REMATERL.SMP",
+	"81ZZZZZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"SSSHTZZZ.SMP", // 10
+	"HOJASZZZ.SMP",
 	"HOJASZZZ.SMP",
 	"HOJASZZZ.SMP",
 	"HOJASZZZ.SMP",
@@ -154,58 +154,58 @@ static const char *inventorySounds[113] = {
 	"HOJASZZZ.SMP",
 	"HOJASZZZ.SMP",
 	"BOTEZZZZ.SMP", // 60
-	"BOTEZZZZ.SMP", // 61
-	"BOTEZZZZ.SMP", // 62 - Bottle sound
-	"BELCHZZZ.SMP", // 63 - Belch
-	"BEAMZZZZ.SMP", // 64 - Beam/ray
-	"ELVIS1ZZ.SMP", // 65 - Elvis impression
-	"CAT_1ZZZ.SMP", // 66 - Cat sound
-	"BOOOOOIZ.SMP", // 67 - Boing
-	"DISCOSZZ.SMP", // 68 - Disco music
-	"MONORLZZ.SMP", // 69 - Monorail
+	"BOTEZZZZ.SMP",
+	"BOTEZZZZ.SMP",
+	"BELCHZZZ.SMP",
+	"BEAMZZZZ.SMP",
+	"ELVIS1ZZ.SMP",
+	"CAT_1ZZZ.SMP",
+	"BOOOOOIZ.SMP",
+	"DISCOSZZ.SMP",
+	"MONORLZZ.SMP",
 	"11ZZZZZZ.SMP", // 70
-	"11ZZZZZZ.SMP", // 71
-	"11ZZZZZZ.SMP", // 72
-	"CARACOLA.SMP", // 73 - Seashell
-	"11ZZZZZZ.SMP", // 74
-	"11ZZZZZZ.SMP", // 75
-	"WATER_2Z.SMP", // 76 - Water splash
-	"11ZZZZZZ.SMP", // 77
-	"11ZZZZZZ.SMP", // 78
-	"EEEEKZZZ.SMP", // 79 - Shriek
+	"11ZZZZZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"CARACOLA.SMP",
+	"11ZZZZZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"WATER_2Z.SMP",
+	"11ZZZZZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"EEEEKZZZ.SMP",
 	"REMATERL.SMP", // 80 - Rematerialize
-	"11ZZZZZZ.SMP", // 81
-	"11ZZZZZZ.SMP", // 82
-	"ELVIS1ZZ.SMP", // 83 - Elvis impression
-	"RIMSHOTZ.SMP", // 84 - Rimshot
-	"11ZZZZZZ.SMP", // 85
-	"WATER_2Z.SMP", // 86 - Water splash
-	"MOTOSZZZ.SMP", // 87 - Motorcycle
-	"HOJASZZZ.SMP",
-	"TWANGZZZ.SMP", // 89 - Twang
+	"11ZZZZZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"ELVIS1ZZ.SMP",
+	"RIMSHOTZ.SMP",
+	"11ZZZZZZ.SMP",
+	"WATER_2Z.SMP",
+	"MOTOSZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"TWANGZZZ.SMP",
 	"11ZZZZZZ.SMP", // 90
-	"QUAKE2ZZ.SMP", // 91 - Earthquake
-	"11ZZZZZZ.SMP", // 92
-	"SORBOZZZ.SMP", // 93 - Slurp
-	"BOTEZZZZ.SMP", // 94 - Bottle sound
-	"ELVIS1ZZ.SMP", // 95 - Elvis impression
-	"HOJASZZZ.SMP", // 96
-	"HOJASZZZ.SMP", // 97
-	"HOJASZZZ.SMP", // 98
-	"11ZZZZZZ.SMP", // 99
-	"LLAVESZZ.SMP", // 100 - Keys jingling
-	"HOJASZZZ.SMP", // 101
-	"11ZZZZZZ.SMP", // 102
-	"11ZZZZZZ.SMP", // 103
-	"EVLLAUGH.SMP", // 104 - Evil laugh
-	"11ZZZZZZ.SMP", // 105
-	"BURROLZZ.SMP", // 106 - Donkey bray
-	"11ZZZZZZ.SMP", // 107
-	"TWANGZZZ.SMP", // 108
-	"11ZZZZZZ.SMP", // 109
+	"QUAKE2ZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"SORBOZZZ.SMP",
+	"BOTEZZZZ.SMP",
+	"ELVIS1ZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"LLAVESZZ.SMP", // 100
+	"HOJASZZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"EVLLAUGH.SMP",
+	"11ZZZZZZ.SMP",
+	"BURROLZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"TWANGZZZ.SMP",
+	"11ZZZZZZ.SMP",
 	"TWANGZZZ.SMP", // 110
-	"ELVIS1ZZ.SMP", // 111 - Elvis impression
-	"SEX3ZZZZ.SMP"  // 112 - Suggestive sound
+	"ELVIS1ZZ.SMP",
+	"SEX3ZZZZ.SMP"
 };
 
 class MenuManager {
@@ -232,7 +232,6 @@ private:
 	void checkMouseDown(int x, int y);
 	bool checkMouseClick(int x, int y);
 	void checkSoundMenuClick(int x, int y);
-	bool checkMainMenuMouse(int x, int y, bool &retFlag);
 	bool checkMainMenuMouse(int x, int y); // returns bool if its supposed to close the menu
 	void showCredits();
 	bool selectInventoryItem(int i);
@@ -293,7 +292,6 @@ private:
 	Common::Rect _sfxVolumeLeftRect = Common::Rect(Common::Point(364, 252), 36, 28);
 	Common::Rect _sfxVolumeRightRect = Common::Rect(Common::Point(400, 252), 31, 28);
 
-
 	byte *_soundControlArrowLeft[2] = {nullptr};
 	byte *_soundControlArrowRight[2] = {nullptr};
 
@@ -323,11 +321,11 @@ private:
 
 	// Save/Load sub-menu state
 	int _saveGamePage = 0;
-	int _editingSaveSlot = -1;       // -1 = not editing any slot
-	Common::String _editingName;     // name being typed for a save
-	Common::Rect _cancelarRect;      // hit-rect for the CANCELAR row
+	int _editingSaveSlot = -1;                  // -1 = not editing any slot
+	Common::String _editingName;                // name being typed for a save
+	Common::Rect _cancelarRect;                 // hit-rect for the CANCELAR row
 	Common::Array<Common::Rect> _saveSlotRects; // hit-rects for the 8 visible save rows
-	Common::StringArray _saveDescriptions;       // indexed by slot 0-255
+	Common::StringArray _saveDescriptions;      // indexed by slot 0-255
 };
 
 } // End of namespace Pelrock


Commit: cf7004677e62071e985cf681de5b1580eca36f52
    https://github.com/scummvm/scummvm/commit/cf7004677e62071e985cf681de5b1580eca36f52
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:41+02:00

Commit Message:
PELROCK: Cleanup of pathfinding and room

Changed paths:
    engines/pelrock/pathfinding.cpp
    engines/pelrock/pathfinding.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h


diff --git a/engines/pelrock/pathfinding.cpp b/engines/pelrock/pathfinding.cpp
index b1dfdeb790c..e3dd713db12 100644
--- a/engines/pelrock/pathfinding.cpp
+++ b/engines/pelrock/pathfinding.cpp
@@ -27,29 +27,6 @@
 
 namespace Pelrock {
 
-Common::String printMovementFlags(byte flags) {
-	Common::String result;
-	if (flags & kMoveHoriz) {
-		result += "HORIZ ";
-	}
-	if (flags & kMoveVert) {
-		result += "VERT ";
-	}
-	if (flags & kMoveDown) {
-		result += "DOWN ";
-	}
-	if (flags & kMoveLeft) {
-		result += "LEFT ";
-	}
-	if (flags & kMoveUp) {
-		result += "UP ";
-	}
-	if (flags & kMoveRight) {
-		result += "RIGHT ";
-	}
-	return result;
-}
-
 bool findPath(int sourceX, int sourceY, int targetX, int targetY, Common::Array<WalkBox> &walkboxes, PathContext *context, HotSpot *hotspot) {
 
 	if (context->pathBuffer == nullptr) {
@@ -61,15 +38,13 @@ bool findPath(int sourceX, int sourceY, int targetX, int targetY, Common::Array<
 
 	int startX = sourceX;
 	int startY = sourceY;
-	Common::Point target = calculateWalkTarget(walkboxes, targetX, targetY, 2, nullptr);
+	Common::Point target = calculateWalkTarget(walkboxes, targetX, targetY, nullptr);
 	targetX = target.x;
 	targetY = target.y;
-	// debug("Startx= %d, starty= %d, destx= %d, desty= %d", startX, startY, targetX, targetY);
 
 	byte startBox = findWalkboxForPoint(walkboxes, startX, startY);
 	byte destBox = findWalkboxForPoint(walkboxes, targetX, targetY);
 
-	// debug("Pathfinding from (%d, %d) in box %d to (%d, %d) in box %d\n", startX, startY, startBox, targetX, targetY, destBox);
 	// Check if both points are in valid walkboxes
 	if (startBox == 0xFF || destBox == 0xFF) {
 		debug("Error: Start or destination not in any walkbox\n");
@@ -101,10 +76,6 @@ bool findPath(int sourceX, int sourceY, int targetX, int targetY, Common::Array<
 	} else {
 		// Build walkbox path
 		context->pathLength = buildWalkboxPath(walkboxes, startBox, destBox, context->pathBuffer);
-		debug("Walkbox path to point");
-		for (int i = 0; i < context->pathLength; i++) {
-			// debug("Walkbox %d: %d", i, context->pathBuffer[i]);
-		}
 		if (context->pathLength == 0) {
 			debug("Error: No path found\n");
 			return false;
@@ -112,21 +83,16 @@ bool findPath(int sourceX, int sourceY, int targetX, int targetY, Common::Array<
 
 		// Generate movement steps
 		context->movementCount = generateMovementSteps(walkboxes, context->pathBuffer, context->pathLength, startX, startY, targetX, targetY, context->movementBuffer);
-		// for (int i = 0; i < context->movementCount; i++) {
-		// 	debug("Movement step %d: flags=\"%s\", dx=%d, dy=%d", i, printMovementFlags(context->movementBuffer[i].flags).c_str(), context->movementBuffer[i].distanceX, context->movementBuffer[i].distanceY);
-		// }
 	}
 	return true;
 }
 
-Common::Point calculateWalkTarget(Common::Array<WalkBox> &walkboxes,
-								  int sourceX, int sourceY,
-								  bool mouseHoverState,
-								  HotSpot *hotspot) {
+Common::Point calculateWalkTarget(Common::Array<WalkBox> &walkboxes, int sourceX, int sourceY, HotSpot *hotspot) {
 
-	if(hotspot != nullptr) {
+	if (hotspot != nullptr) {
+		// if there is a hotspot then the source is the center of the hotspot.
 		sourceX = hotspot->x + hotspot->w / 2;
-		sourceY = hotspot->y + hotspot->h;
+		sourceY = hotspot->y + hotspot->h / 2;
 	}
 
 	// Find nearest walkbox
@@ -151,7 +117,6 @@ Common::Point calculateWalkTarget(Common::Array<WalkBox> &walkboxes,
 			xDistance = sourceX - (walkboxes[i].x + walkboxes[i].w - 1);
 			xDirection = 0; // LEFT
 		}
-		// else: sourceX is inside, xDistance = 0
 
 		// Calculate Y distance with direction
 		if (sourceY < walkboxes[i].y) {
@@ -162,7 +127,6 @@ Common::Point calculateWalkTarget(Common::Array<WalkBox> &walkboxes,
 			yDistance = sourceY - (walkboxes[i].y + walkboxes[i].h - 1);
 			yDirection = 0; // UP
 		}
-		// else: sourceY is inside, yDistance = 0
 
 		uint32 totalDistance = xDistance + yDistance;
 
@@ -175,7 +139,7 @@ Common::Point calculateWalkTarget(Common::Array<WalkBox> &walkboxes,
 		}
 	}
 
-	// Step 3: Calculate final target point
+	// Calculate final target point
 	Common::Point target;
 
 	if (bestXDirection == 1) {
@@ -337,11 +301,11 @@ void calculateMovementToTarget(uint16 currentX, uint16 currentY, uint16 targetX,
  * Returns: number of movement steps generated
  */
 uint16 generateMovementSteps(Common::Array<WalkBox> &walkboxes,
-							   byte *pathBuffer,
-							   uint16 pathLength,
-							   uint16 startX, uint16 startY,
-							   uint16 destX, uint16 destY,
-							   MovementStep *movementBuffer) {
+							 byte *pathBuffer,
+							 uint16 pathLength,
+							 uint16 startX, uint16 startY,
+							 uint16 destX, uint16 destY,
+							 MovementStep *movementBuffer) {
 	uint16 currentX = startX;
 	uint16 currentY = startY;
 	uint16 movementIndex = 0;
diff --git a/engines/pelrock/pathfinding.h b/engines/pelrock/pathfinding.h
index f6f3d2c1063..6775cf3a4a9 100644
--- a/engines/pelrock/pathfinding.h
+++ b/engines/pelrock/pathfinding.h
@@ -30,15 +30,15 @@ namespace Pelrock {
 bool findPath(int sourceX, int sourceY, int targetX, int targetY, Common::Array<WalkBox> &walkboxes, PathContext *context, HotSpot *hotspot = nullptr);
 
 /**
- * Calculate the walk target point based on source coordinates and mouse hover state.
+ * Calculate the walk target point. This is used both for actually walking and for the mouse hover exit calculation. E.g. if the resulting walk target
+ * would lead to the character landing in an exit, the Exit cursor is shown.
  * @param walkboxes         Array of walkboxes in the current room.
- * @param sourceX           X coordinate of the source point (e.g., mouse position).
- * @param sourceY           Y coordinate of the source point (e.g., mouse position).
- * @param mouseHoverState   State indicating what the mouse is hovering over (0 = nothing, 1 = hotspot hover, 2 = hotspot click).
- * @param hotspot           Pointer to the hotspot being hovered over (if applicable).
+ * @param sourceX           X coordinate of the source point
+ * @param sourceY           Y coordinate of the source point
+ * @param hotspot           Pointer to the hotspot being hovered over (if any).
  * @return                  Calculated walk target point.
  */
-Common::Point calculateWalkTarget(Common::Array<WalkBox> &walkboxes, int sourceX, int sourceY, bool mouseHoverState, HotSpot *hotspot);
+Common::Point calculateWalkTarget(Common::Array<WalkBox> &walkboxes, int sourceX, int sourceY, HotSpot *hotspot);
 byte findWalkboxForPoint(Common::Array<WalkBox> &walkboxes, uint16 x, uint16 y);
 byte getAdjacentWalkbox(Common::Array<WalkBox> &walkboxes, byte current_box_index);
 uint16 buildWalkboxPath(Common::Array<WalkBox> &walkboxes, byte start_box, byte dest_box, byte *path_buffer);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 572239ba639..e162930be1d 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1701,7 +1701,7 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 		isHotspotUnder = true;
 	}
 	_currentHotspot = isHotspotUnder ? &_room->_currentRoomHotspots[hotspotIndex] : nullptr;
-	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, _events->_mouseX, _events->_mouseY, isHotspotUnder, _currentHotspot);
+	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, _events->_mouseX, _events->_mouseY, _currentHotspot);
 	_curWalkTarget = walkTarget;
 
 	walkTo(walkTarget.x, walkTarget.y);
@@ -1738,7 +1738,7 @@ void PelrockEngine::checkMouseHover() {
 	}
 
 	// Calculate walk target first (before checking anything else)
-	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, _events->_mouseX, _events->_mouseY, hotspotDetected, hotspotDetected ? &_room->_currentRoomHotspots[hotspotIndex] : nullptr);
+	Common::Point walkTarget = calculateWalkTarget(_room->_currentRoomWalkboxes, _events->_mouseX, _events->_mouseY, hotspotDetected ? &_room->_currentRoomHotspots[hotspotIndex] : nullptr);
 
 	// Check if walk target hits any exit
 	bool exitDetected = false;
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 3ebe7a50af5..825a63a6318 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -69,9 +69,9 @@ static const uint32 kAlfredAnimClimbDownOffset = 1761234;     // 7  - Climbs dow
 static const uint32 kAlfredAnimClimbUpOffset = 1766378;       // 8  - Climbs up
 static const uint32 kAlfredAnimExitTunnelOffset = 1770196;    // 9  - Exits tunnel
 static const uint32 kAlfredAnimWorkersOffset = 1600956;       // 10 - With workers
-static const uint32 kAlfredAnimMunheco1Offset = 2060916;      // 11 - Munheco 1
-static const uint32 kAlfredAnimMunheco2Offset = 2115632;      // 12 - Munheco 2
-static const uint32 kAlfredAnimMunheco3Offset = 1526432;      // 13 - Munheco 3
+static const uint32 kAlfredAnimMunheco1Offset = 2060916;      // 11 - Doll 1
+static const uint32 kAlfredAnimMunheco2Offset = 2115632;      // 12 - Doll 2
+static const uint32 kAlfredAnimMunheco3Offset = 1526432;      // 13 - Doll 3
 static const uint32 kAlfredAnimDescamisaOffset = 2972568;     // 14 - Descamisa
 static const uint32 kAlfredAnimSecretPassageOffset = 1749464; // 15 - Secret passage
 static const uint32 kAlfredAnimInBedOffset = 3038454;         // 16 - Alfred in bed
@@ -95,10 +95,10 @@ const AlfredSpecialAnimOffset ResourceManager::alfredSpecialAnims[] = {
 	{9, 33, 72, 1, 7, kAlfredAnimClimbUpOffset, 1, 2, 0},                   // 8  - alfred climbs up
 	{16, 158, 115, 0, 7, kAlfredAnimExitTunnelOffset, 1, 2, 0},             // 9  - alfred exits tunnel
 	{7, 208, 102, 0, 7, kAlfredAnimWorkersOffset, 1, 2, 0},                 // 10 - alfred with workers
-	{23, 116, 124, 1, 7, kAlfredAnimMunheco1Offset, 1, 2, 0},               // 11 - Munheco 1
-	{18, 177, 124, 1, 7, kAlfredAnimMunheco2Offset, 1, 2, 0},               // 12 - Munheco 2
-	{11, 98, 138, 1, 7, kAlfredAnimMunheco3Offset, 1, 2, 0},                // 13 - Munheco 3
-	{4, 51, 102, 1, 7, kAlfredAnimDescamisaOffset, 1, 2, 0},                // 14 - descamisa
+	{23, 116, 124, 1, 7, kAlfredAnimMunheco1Offset, 1, 2, 0},               // 11 - Doll 1
+	{18, 177, 124, 1, 7, kAlfredAnimMunheco2Offset, 1, 2, 0},               // 12 - Doll 2
+	{11, 98, 138, 1, 7, kAlfredAnimMunheco3Offset, 1, 2, 0},                // 13 - Doll 3
+	{4, 51, 102, 1, 7, kAlfredAnimDescamisaOffset, 1, 2, 0},                // 14 - Taking off shirt
 	{13, 95, 99, 1, 7, kAlfredAnimSecretPassageOffset, 1, 2, 0},            // 15 - alfred enters secret passage
 	{14, 71, 66, 1, 7, kAlfredAnimInBedOffset, 1, 2, 0},                    // 16 - Alfred in bed
 };
@@ -351,7 +351,6 @@ void ResourceManager::loadAlfredSpecialAnim(int numAnim, bool reverse) {
 	uint32 size = anim.size == 0 ? anim.numFrames * anim.w * anim.h : anim.size;
 	_currentSpecialAnim->animData = new byte[size];
 	if (anim.numBudas > 0) {
-		debug("Loading special anim with budas: numBudas=%d, totalSize %d", anim.numBudas, size);
 		byte *thisBlock = nullptr;
 		size_t blockSize = 0;
 		readUntilBuda(&alfredFile, anim.offset, thisBlock, blockSize);
@@ -397,7 +396,6 @@ void ResourceManager::loadInventoryItems() {
 	for (int i = 0; i < 69; i++) {
 		_inventoryIcons[i].index = i;
 		extractSingleFrame(iconData, _inventoryIcons[i].iconData, i, 60, 60);
-		// _inventoryIcons[i].description = _inventoryDescriptions[i];
 	}
 	delete[] iconData;
 }
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index 400631e683d..6dfb6c06879 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -44,8 +44,17 @@ public:
 	void loadSettingsMenu();
 	void loadCursors();
 	void loadInteractionIcons();
+	/**
+	 * Loads standard Alfred animations
+	 */
 	void loadAlfredAnims();
+	/**
+	 * Loads some other special anims for NPCs
+	 */
 	void loadOtherSpecialAnim(uint32 offset, bool rleCompressed, byte *&buffer, size_t &bufferSize);
+	/**
+	 * Loads one of the pre-defined Special anims for alfred.
+	 */
 	void loadAlfredSpecialAnim(int numAnim, bool reverse = false);
 	void clearSpecialAnim();
 	void loadInventoryItems();
@@ -62,11 +71,11 @@ public:
 	byte *alfredIdle[4]; // 4 directions
 
 	byte **alfredWalkFrames[4]; // 4 arrays of arrays
-	byte **alfredCrawlFrames[4];
+	byte **alfredCrawlFrames[4];// 4 arrays of arrays
 	byte **alfredTalkFrames[4]; // 4 arrays of arrays
+	byte **alfredInteractFrames[4];
 
 	byte **alfredCombFrames[2];
-	byte **alfredInteractFrames[4];
 
 	byte *_cursorMasks[5];
 	byte *_verbIcons[9];
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 03231bab42a..396552cbadb 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -43,9 +43,9 @@ RoomManager::~RoomManager() {
 }
 
 void RoomManager::loadWaterPaletteRemap() {
-	//Extra remap for water effect
+	// Extra remap for water effect
 	Common::File exe;
-	if(!exe.open("JUEGO.EXE")) {
+	if (!exe.open("JUEGO.EXE")) {
 		error("Couldnt find file JUEGO.EXE");
 	}
 	exe.seek(kPaletteRemapOffset, SEEK_SET);
@@ -107,7 +107,7 @@ void RoomManager::addSticker(int stickerId, int persist) {
 void RoomManager::addStickerToRoom(byte room, int stickerId, int persist) {
 	Sticker sticker = g_engine->_res->getSticker(stickerId);
 	if (room == _currentRoomNumber && persist & PERSIST_TEMP) {
-		if(hasSticker(sticker.stickerIndex)) {
+		if (hasSticker(sticker.stickerIndex)) {
 			debug("Sticker %d already exists in room %d, skipping add", stickerId, room);
 			return;
 		}
@@ -216,7 +216,6 @@ void RoomManager::changeHotspot(byte room, HotSpot hotspot, int persist) {
 				_currentRoomHotspots[i] = hotspot;
 				break;
 			}
-			debug("Hotspot %d in room %d does not match changed hotspot inner index %d", _currentRoomHotspots[i].innerIndex, room, hotspot.innerIndex);
 		}
 	}
 	if (persist & PERSIST_PERM) {
@@ -249,9 +248,8 @@ void RoomManager::enableSprite(byte spriteIndex, byte zOrder, int persist) {
 }
 
 void RoomManager::enableSprite(byte roomNumber, byte spriteIndex, byte zOrder, int persist) {
-	for(int i =0; i < g_engine->_state->spriteChanges[roomNumber].size(); i++) {
+	for (int i = 0; i < g_engine->_state->spriteChanges[roomNumber].size(); i++) {
 		if (g_engine->_state->spriteChanges[roomNumber][i].spriteIndex == spriteIndex) {
-			debug("Removing sprite change for room %d, sprite index %d, zOrder %d", roomNumber, spriteIndex, g_engine->_state->spriteChanges[roomNumber][i].zIndex);
 			g_engine->_state->spriteChanges[roomNumber].remove_at(i);
 			break;
 		}
@@ -321,7 +319,6 @@ void RoomManager::addWalkbox(WalkBox walkbox, int persist) {
 		_currentRoomWalkboxes.push_back(walkbox);
 	}
 	if (persist & PERSIST_PERM) {
-		debug("Adding walkbox change for room %d, index %d, x=%d y=%d w=%d h=%d", _currentRoomNumber, walkbox.index, walkbox.x, walkbox.y, walkbox.w, walkbox.h);
 		g_engine->_state->roomWalkBoxChanges[_currentRoomNumber].push_back({_currentRoomNumber, walkbox.index, walkbox});
 	}
 }
@@ -347,7 +344,6 @@ Sprite *RoomManager::findSpriteByExtra(int16 extra) {
 HotSpot *RoomManager::findHotspotByIndex(byte index) {
 	for (uint i = 0; i < _currentRoomHotspots.size(); i++) {
 		if (!_currentRoomHotspots[i].isSprite && _currentRoomHotspots[i].innerIndex == index) {
-			debug("Found hotspot %d at index %d, extra = %d", index, i, _currentRoomHotspots[i].extra);
 			return &_currentRoomHotspots[i];
 		}
 	}
@@ -422,11 +418,6 @@ PaletteAnim *RoomManager::getPaletteAnimForRoom(int roomNumber) {
 	anim->paletteMode = exeFile.readByte();
 	exeFile.read(anim->data, 10);
 	if (anim->paletteMode == 1) {
-		// FADE mode: shift RGB values to convert from 6-bit VGA to 8-bit
-		// data[0-2] = current R,G,B
-		// data[3-5] = min R,G,B
-		// data[6-8] = max R,G,B
-		// data[9] = flags (R/G/B increments + direction) - NOT shifted
 		for (int i = 0; i < 9; i++) {
 			anim->data[i] = anim->data[i] << 2;
 		}
@@ -484,9 +475,6 @@ Common::Array<Exit> RoomManager::loadExits(byte *data, size_t size) {
 			}
 		}
 		exits.push_back(exit);
-		// debug("Exit %d: targetRoom=%d isEnabled=%d x=%d y=%d w=%d h=%d targetX=%d targetY=%d dir=%d",
-		// 	  i, exit.targetRoom, exit.isEnabled, exit.x, exit.y, exit.w, exit.h,
-		// 	  exit.targetX, exit.targetY, exit.dir);
 	}
 	return exits;
 }
@@ -507,7 +495,6 @@ Common::Array<HotSpot> RoomManager::loadHotspots(byte *data, size_t size) {
 			// if the hotspot has been changed, load the changed version
 			for (uint j = 0; j < g_engine->_state->roomHotSpotChanges[_currentRoomNumber].size(); j++) {
 				if (g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspotIndex == spot.innerIndex) {
-					debug("Hotspot %d has been changed, loading changed version, Hotspot x=%d, y = %d, extra = %d, isEnabled=%d", spot.innerIndex, g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot.x, g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot.y, g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot.extra, g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot.isEnabled);
 					hotspots.push_back(g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot);
 					isChanged = true;
 					break;
@@ -523,7 +510,6 @@ Common::Array<HotSpot> RoomManager::loadHotspots(byte *data, size_t size) {
 		spot.h = data[hotspotOffset + 6];
 		spot.isSprite = false;
 		spot.extra = READ_LE_INT16(data + hotspotOffset + 7);
-		debug("Hotspot %d: type=%d x=%d y=%d w=%d h=%d extra=%d, index =%d, isEnabled=%d", spot.innerIndex, spot.actionFlags, spot.x, spot.y, spot.w, spot.h, spot.extra, spot.innerIndex, spot.isEnabled);
 		hotspots.push_back(spot);
 	}
 
@@ -554,7 +540,6 @@ void RoomManager::resetConversationStates(byte roomNumber, byte *conversationDat
 			continue;
 		}
 		Common::copy(entry.data, entry.data + entry.dataSize, conversationData + entry.offset);
-		// delete[] entry.data;
 	}
 	alfredB.close();
 }
@@ -646,6 +631,9 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	delete[] pair12;
 }
 
+/**
+ * A number of rooms have a single passerby sprite that walks across the screen.
+ */
 int streetWalkerIndices[] = {
 	-1, // room 0,
 	5,  // room 1,
@@ -664,8 +652,8 @@ int streetWalkerIndices[] = {
 	2,  // room 14,
 	-1, // room 15,
 	2
-
 };
+
 RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 	RoomPasserBys *anims = nullptr;
 	switch (roomNumber) {
@@ -877,10 +865,6 @@ void RoomManager::init() {
 	if (!alfred8.open("ALFRED.8")) {
 		error("Couldnt find file ALFRED.8");
 	}
-	// _resetDataSize = alfred8.size();
-	// _resetData = new byte[_resetDataSize];
-	// alfred8.read(_resetData, _resetDataSize);
-	// alfred8.close();
 }
 
 void RoomManager::loadAnimationPixelData(Common::File *roomFile, int roomOffset, byte *&buffer, size_t &outSize) {
@@ -893,18 +877,15 @@ void RoomManager::loadAnimationPixelData(Common::File *roomFile, int roomOffset,
 	roomFile->seek(offset, SEEK_SET);
 	roomFile->read(pixelData, size);
 	if (offset > 0 && size > 0) {
-		if(_currentRoomNumber != 40) {
+		if (_currentRoomNumber != 40) {
 			outSize = rleDecompress(pixelData, size, 0, size, &buffer, true);
-		}
-		else {
+		} else {
 			// room 40 has uncompressed animation data for some reason
 			buffer = new byte[size];
 			Common::copy(pixelData, pixelData + size, buffer);
 			outSize = size;
 		}
 	}
-
-
 }
 
 Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pixelDataSize, byte *data, size_t size) {
@@ -930,16 +911,14 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 		sprite.numAnims = data[animOffset + 8];
 		sprite.zOrder = data[animOffset + 23];
 		sprite.extra = READ_LE_INT16(data + animOffset + 32);
-		debug("Sprite %d: x=%d y=%d w=%d h=%d stride=%d numAnims=%d zOrder=%d extra=%d", i, sprite.x, sprite.y, sprite.w, sprite.h, sprite.stride, sprite.numAnims, sprite.zOrder, sprite.extra);
 		sprite.actionFlags = data[animOffset + 34];
-		if(sprite.actionFlags & kActionMaskTalk) {
+		if (sprite.actionFlags & kActionMaskTalk) {
 			sprite.talkingAnimIndex = talkingAnims++;
 		}
 		sprite.isHotspotDisabled = data[animOffset + 38];
 		sprite.disableAfterSequence = data[animOffset + 39];
 		for (uint j = 0; j < spriteChanges.size(); j++) {
 			if (spriteChanges[j].spriteIndex == sprite.index) {
-				debug("Sprite %d has been changed, loading changed version with zOrder %d", sprite.index, spriteChanges[j].zIndex);
 				sprite.zOrder = spriteChanges[j].zIndex;
 				break;
 			}
@@ -966,27 +945,23 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 			anim.animData = new byte *[anim.nframes];
 			if (sprite.w > 0 && sprite.h > 0 && anim.nframes > 0) {
 				for (int k = 0; k < anim.nframes; k++) {
-					if(picOffset >= pixelDataSize) {
+					if (picOffset >= pixelDataSize) {
 						debug("Pixel data offset out of bounds for sprite %d anim %d, offset %u, size %lu", i, j, picOffset, pixelDataSize);
 						break;
 					}
 					anim.animData[k] = new byte[sprite.w * sprite.h];
-					// debug("Extracting frame %d for anim %d-%d, w=%d h=%d, pixelDataSize=%d, current offset %d", i, j, anim.nframes, sprite.w, sprite.h, pixelDataSize, picOffset);
 					extractSingleFrame(pixelData + picOffset, anim.animData[k], k, sprite.w, sprite.h);
 				}
 				sprite.animData[j] = anim;
-				// debug("  Anim %d-%d: x=%d y=%d w=%d h=%d nframes=%d loopCount=%d speed=%d", i, j, anim.x, anim.y, anim.w, anim.h, anim.nframes, anim.loopCount, anim.speed);
-				// debug("  Movement flags: 0x%04X", anim.movementFlags);
 				picOffset += totalBytesPerFrame;
 
-				if(_currentRoomNumber == 36 && i == 0) {
+				if (_currentRoomNumber == 36 && i == 0) {
 					// Room 36 sets its anim to 1 to appear idle and only enables anim later on
 					anim.nframes = 1;
 				}
 
 			} else {
 				continue;
-				// debug("Anim %d-%d: invalid dimensions, skipping", i, j);
 			}
 			sprite.animData[j] = anim;
 		}
@@ -1011,18 +986,15 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(byte *data, size_t size) {
 		int16 w = READ_LE_INT16(data + boxOffset + 4);
 		int16 h = READ_LE_INT16(data + boxOffset + 6);
 		byte flags = data[boxOffset + 8];
-		debug("Walkbox %d: x1=%d y1=%d w=%d h=%d", i, x1, y1, w, h);
 		WalkBox box;
 		box.index = i;
 		bool isChanged = false;
 		if (g_engine->_state->roomWalkBoxChanges.contains(_currentRoomNumber)) {
-			debug("Checking for changes to walkbox %d in room %d", i, _currentRoomNumber);
 			// if the walkbox has been changed, load the changed version
 			for (uint j = 0; j < g_engine->_state->roomWalkBoxChanges[_currentRoomNumber].size(); j++) {
 				if (g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkboxIndex == i) {
 					walkboxes.push_back(g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkbox);
 					isChanged = true;
-					debug("Walkbox %d has been changed, loading changed version, x1=%d y1=%d w=%d h=%d", i, g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkbox.x, g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkbox.y, g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkbox.w, g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkbox.h);
 					break;
 				}
 			}
@@ -1048,12 +1020,10 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(byte *data, size_t size) {
 				}
 			}
 			if (!found) {
-				debug("Adding new walkbox for room %d at index %d", _currentRoomNumber, g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkboxIndex);
 				walkboxes.push_back(g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkbox);
 			}
 		}
 	}
-	debug("Total walkboxes for room %d: %d", _currentRoomNumber, walkboxes.size());
 	return walkboxes;
 }
 
@@ -1111,12 +1081,14 @@ void RoomManager::loadConversationData(byte *pair12data, size_t pair12size, uint
 	}
 }
 
+/**
+ * Disables choices according to saved data
+ */
 void RoomManager::applyDisabledChoices(byte roomNumber, byte *conversationData, size_t conversationDataSize) {
 	Common::Array<ResetEntry> disabledBranches = g_engine->_state->disabledBranches[roomNumber];
 	if (disabledBranches.size() == 0) {
 		return;
 	}
-	debug("Disabling %d conversation branches for room %d", disabledBranches.size(), roomNumber);
 	for (uint i = 0; i < disabledBranches.size(); i++) {
 		ResetEntry resetEntry = disabledBranches[i];
 		applyDisabledChoice(resetEntry, conversationData, conversationDataSize);
@@ -1134,7 +1106,7 @@ void RoomManager::addDisabledChoice(ChoiceOption choice) {
 	debug("Adding disabled branch for room %d at offset %d (FA written at %d)",
 		  choice.room, choice.dataOffset, disableOffset);
 
-	debug("Disabled branch is: \"%s\"",  choice.text.c_str());
+	debug("Disabled branch is: \"%s\"", choice.text.c_str());
 	ResetEntry resetEntry = ResetEntry();
 	resetEntry.room = choice.room;
 	resetEntry.offset = disableOffset;
@@ -1208,7 +1180,6 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 	talkFile.read(&talkHeader.unknown7, 4);
 	talkHeader.speedByteB = talkFile.readByte();
 	talkFile.read(&talkHeader.unknown6, 24);
-	// debug("Talking anim header for room %d: spritePointer=%d, wA=%d, hA=%d, framesA=%d, wB=%d, hB=%d, framesB=%d", roomNumber, talkHeader.spritePointer, talkHeader.wAnimA, talkHeader.hAnimA, talkHeader.numFramesAnimA, talkHeader.wAnimB, talkHeader.hAnimB, talkHeader.numFramesAnimB);
 
 	if (talkHeader.spritePointer == 0) {
 		debug("No talking animation for room %d", roomNumber);
@@ -1225,7 +1196,6 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 	readUntilBuda(&talkFile, talkHeader.spritePointer, data, dataSize);
 	size_t decompressedSize = rleDecompress(data, dataSize, 0, dataSize, &decompressed);
 	free(data);
-	// debug("Decompressed talking anim A size: %zu, decompressed size: %zu", dataSize, decompressedSize);
 	for (int i = 0; i < talkHeader.numFramesAnimA; i++) {
 		talkHeader.animA[i] = new byte[talkHeader.wAnimA * talkHeader.hAnimA];
 		extractSingleFrame(decompressed, talkHeader.animA[i], i, talkHeader.wAnimA, talkHeader.hAnimA);
@@ -1236,7 +1206,6 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 		for (int i = 0; i < talkHeader.numFramesAnimB; i++) {
 			talkHeader.animB[i] = new byte[talkHeader.wAnimB * talkHeader.hAnimB];
 			uint32 animBFrameOffset = animASize + (i * talkHeader.wAnimB * talkHeader.hAnimB);
-			// debug("Extracting talking anim B frame %d at offset %d, size = %d", i, animASize + (i * talkHeader.wAnimB * talkHeader.hAnimB), talkHeader.wAnimB * talkHeader.hAnimB);
 			if (animBFrameOffset + talkHeader.wAnimB * talkHeader.hAnimB >= decompressedSize) {
 				debug("Error: offset %d is beyond decompressed size %zu", animBFrameOffset, decompressedSize);
 				talkHeader.numFramesAnimB = 0;
@@ -1285,7 +1254,7 @@ byte *RoomManager::loadShadowMap(int roomNumber) {
 
 	byte *shadows = nullptr;
 	size_t decompressedSize = rleDecompress(compressed, compressedSize, 0, 640 * 400, &shadows);
-	if(decompressedSize == 0) {
+	if (decompressedSize == 0) {
 		debug("Failed to decompress shadow map for room %d", roomNumber);
 		shadows = nullptr;
 	}
@@ -1302,7 +1271,7 @@ void RoomManager::loadRemaps(int roomNumber) {
 		error("Couldnt find file ALFRED.9");
 	}
 
-	uint32 remapOffset =/* 0x200 + */(roomNumber * 1024);
+	uint32 remapOffset = /* 0x200 + */ (roomNumber * 1024);
 
 	remapFile.seek(remapOffset, SEEK_SET);
 	remapFile.read(_paletteRemaps[0], 256);
@@ -1310,7 +1279,6 @@ void RoomManager::loadRemaps(int roomNumber) {
 	remapFile.read(_paletteRemaps[2], 256);
 	remapFile.read(_paletteRemaps[3], 256);
 	remapFile.close();
-
 }
 
 byte RoomManager::loadMusicTrackForRoom(Common::File *roomFile, int roomOffset) {
@@ -1335,7 +1303,6 @@ Common::Array<byte> RoomManager::loadRoomSfx(Common::File *roomFile, int roomOff
 	for (int i = 0; i < kNumSfxPerRoom; i++) {
 		byte sfx = roomFile->readByte();
 		roomSfx[i] = sfx;
-		debug("SFX %d for room at offset %d is %d (%s)", i, roomOffset, sfx, SOUND_FILENAMES[sfx]);
 	}
 	return roomSfx;
 }
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index a2c98b4dc72..65116ae2cd8 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -32,7 +32,7 @@ namespace Pelrock {
 static const int kNumSfxPerRoom = 9;
 static const int unpickableHotspotExtras[] = {
 	308, // lamppost cable
-	65, // objects in shop
+	65,  // objects in shop
 	66,
 	67,
 	68,
@@ -44,10 +44,8 @@ static const int unpickableHotspotExtras[] = {
 	74,
 	6,
 	7,
-	91, //mud and stone should only be picked under certain conditions!
-	92
-};
-
+	91, // mud and stone should only be picked under certain conditions!
+	92};
 
 #define PERSIST_TEMP 1
 #define PERSIST_PERM 2
@@ -58,7 +56,13 @@ public:
 	RoomManager();
 	~RoomManager();
 	void loadRoomMetadata(Common::File *roomFile, int roomNumber);
+	/**
+	 * Passer by animations are animations of characters that merely traverse the scene as ambient
+	 */
 	RoomPasserBys *loadPasserByAnims(int roomNumber);
+	/**
+	 * Treats sprites and hotspots the same.
+	 */
 	Common::Array<HotSpot> unifyHotspots(Common::Array<Pelrock::Sprite> &anims, Common::Array<Pelrock::HotSpot> &staticHotspots);
 	void loadRoomTalkingAnimations(int roomNumber);
 	void getPalette(Common::File *roomFile, int roomOffset, byte *palette);
@@ -113,9 +117,11 @@ public:
 	void applyDisabledChoice(ResetEntry entry, byte *conversationData, size_t conversationDataSize);
 	void addDisabledChoice(ChoiceOption choice);
 
-
+	/**
+	 * Will apply the default "take item with given extra" handler if returns true
+	 */
 	bool isPickableByExtra(uint16 extra) {
-		if(extra > 112)
+		if (extra > 112)
 			return false;
 		int size = sizeof(unpickableHotspotExtras) / sizeof(unpickableHotspotExtras[0]);
 		for (int i = 0; i < size; i++) {
@@ -124,13 +130,13 @@ public:
 		}
 		return true;
 	}
+
 	Sprite *findSpriteByIndex(byte index);
 	Sprite *findSpriteByExtra(int16 extra);
 	HotSpot *findHotspotByIndex(byte index);
 	HotSpot *findHotspotByExtra(uint16 extra);
 	PaletteAnim *getPaletteAnimForRoom(int roomNumber);
 
-
 	byte _currentRoomNumber = 0;
 	int _prevRoomNumber = -1;
 	Common::Array<HotSpot> _currentRoomHotspots;


Commit: 9afa4e1c632ca559ae5cb99c71c1306fb77e881f
    https://github.com/scummvm/scummvm/commit/9afa4e1c632ca559ae5cb99c71c1306fb77e881f
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:41+02:00

Commit Message:
PELROCK: Cleanup of sound and spellbook

Changed paths:
    engines/pelrock/slidingpuzzle.cpp
    engines/pelrock/slidingpuzzle.h
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h
    engines/pelrock/spellbook.cpp
    engines/pelrock/spellbook.h


diff --git a/engines/pelrock/slidingpuzzle.cpp b/engines/pelrock/slidingpuzzle.cpp
index 0baf21f30be..e2625962127 100644
--- a/engines/pelrock/slidingpuzzle.cpp
+++ b/engines/pelrock/slidingpuzzle.cpp
@@ -22,10 +22,10 @@
 #include "common/system.h"
 #include "graphics/paletteman.h"
 
-#include "pelrock/slidingpuzzle.h"
-#include "pelrock/pelrock.h"
 #include "pelrock/events.h"
+#include "pelrock/pelrock.h"
 #include "pelrock/room.h"
+#include "pelrock/slidingpuzzle.h"
 #include "pelrock/sound.h"
 
 namespace Pelrock {
@@ -50,8 +50,8 @@ SlidingPuzzle::~SlidingPuzzle() {
 
 void SlidingPuzzle::run() {
 	// calculate grid
-	_tileSize   = kTileSizes[_sizeIndex];
-	_gridWidth  = kPuzzleScreenWidth / _tileSize;
+	_tileSize = kTileSizes[_sizeIndex];
+	_gridWidth = kPuzzleScreenWidth / _tileSize;
 	_gridHeight = kPuzzleScreenHeight / _tileSize;
 	_totalTiles = _gridWidth * _gridHeight;
 
@@ -66,7 +66,7 @@ void SlidingPuzzle::run() {
 
 	_events->_lastKeyEvent = Common::KEYCODE_INVALID;
 
-	//Shuffle
+	// Shuffle
 	shuffleLoop();
 
 	// Guarantee the puzzle is solvable, fix it otherwise
@@ -163,13 +163,13 @@ void SlidingPuzzle::shuffleLoop() {
 
 	// scale tile swap with tile count
 	const int kBaseTileCount = (kPuzzleScreenWidth / kTileSizes[0]) *
-	                           (kPuzzleScreenHeight / kTileSizes[0]);
+							   (kPuzzleScreenHeight / kTileSizes[0]);
 	const int swapsPerFrame = MAX(1, _totalTiles / kBaseTileCount);
 
 	while (!g_engine->shouldQuit()) {
 		_events->pollEvent();
 
-		for (int s = 0; s < swapsPerFrame; s++) {
+		for (int i = 0; i < swapsPerFrame; i++) {
 			int a, b;
 			do {
 				a = g_engine->getRandomNumber(shuffleRange - 1);
@@ -231,7 +231,7 @@ bool SlidingPuzzle::handleClick(int screenX, int screenY) {
 	int emptyRow = _emptyPos / _gridWidth;
 	int emptyCol = _emptyPos % _gridWidth;
 	bool adjacent = (row == emptyRow && abs(col - emptyCol) == 1) ||
-	                (col == emptyCol && abs(row - emptyRow) == 1);
+					(col == emptyCol && abs(row - emptyRow) == 1);
 	if (!adjacent)
 		return false;
 
diff --git a/engines/pelrock/slidingpuzzle.h b/engines/pelrock/slidingpuzzle.h
index fb7b4d3c20e..55a47a897ab 100644
--- a/engines/pelrock/slidingpuzzle.h
+++ b/engines/pelrock/slidingpuzzle.h
@@ -55,9 +55,9 @@ private:
 	int _tileSize;
 	int _gridWidth;
 	int _gridHeight;
-	int _totalTiles;       // gridWidth * gridHeight`
-	int _emptyPos;         // index of the empty (removed) tile
-	uint16 *_tileMap;      // logical position -> original tile index
+	int _totalTiles;  // gridWidth * gridHeight`
+	int _emptyPos;    // index of the empty (removed) tile
+	uint16 *_tileMap; // logical position -> original tile index
 
 	Graphics::ManagedSurface _puzzleBuffer;
 
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index e575d8410fd..15247333b4a 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -234,7 +234,7 @@ void SoundManager::playSound(byte *soundData, uint32 size, int channel) {
 	Audio::AudioStream *stream = Audio::makeRawStream(soundData, size, 11025, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
 	if (stream) {
 		if (_mixer->isSoundHandleActive(_sfxHandles[channel])) {
-				_mixer->stopHandle(_sfxHandles[channel]);
+			_mixer->stopHandle(_sfxHandles[channel]);
 		}
 		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[channel], stream, -1, _currentVolume, 0, DisposeAfterUse::YES);
 	}
@@ -362,7 +362,7 @@ void SoundManager::pauseMusic() {
 	uint32 elapsedFrames = elapsed * 75 / 1000;
 	_cdTrackStart += elapsedFrames; // advance the start offset
 	if (_cdTrackDuration > 0)
-    	_cdTrackDuration -= elapsedFrames; // shrink remaining duration
+		_cdTrackDuration -= elapsedFrames; // shrink remaining duration
 	g_system->getAudioCDManager()->stop();
 	_isPaused = true;
 }
@@ -374,13 +374,11 @@ bool SoundManager::isMusicPlaying() {
 void SoundManager::playMusicTrack(int trackNumber, bool loop) {
 	if (!_isPaused && _currentMusicTrack == trackNumber && isMusicPlaying()) {
 		// Already playing this track
-		debug("Track %d is already playing", trackNumber);
 		return;
 	}
 	_currentMusicTrack = trackNumber;
-	debug("Playing music track %d, loop=%d", trackNumber, loop);
 
-	if(!_isPaused) {
+	if (!_isPaused) {
 		_cdTrackStart = 0;
 		_cdTrackDuration = 0;
 		_cdPlayStartTime = g_system->getMillis();
@@ -404,7 +402,6 @@ void SoundManager::loadSoundIndex() {
 		return;
 	}
 	byte fileCount = sonidosFile.readByte();
-	debug("SONIDOS.DAT contains %u files", fileCount);
 	sonidosFile.skip(3); // Padding bytes
 
 	for (uint32 i = 0; i < fileCount; i++) {
@@ -421,12 +418,12 @@ void SoundManager::loadSoundIndex() {
 static const uint kAmbientCounterMask = 0x1F; // Trigger when (counter & mask) == mask
 
 int SoundManager::tickAmbientSound(uint32 frameCount) {
-	// Counter gate: only trigger every 32 frames when (counter & 0x1F) == 0x1F
+	// trigger every 32 frames
 	if ((frameCount & kAmbientCounterMask) != kAmbientCounterMask) {
 		return -1;
 	}
 
-	// 50% probability gate using ScummVM's random source
+	// 50% probability gate
 	if (g_engine->getRandomNumber(1) == 0) {
 		return -1;
 	}
@@ -434,7 +431,7 @@ int SoundManager::tickAmbientSound(uint32 frameCount) {
 	// Pick random ambient slot 0-3 (corresponds to room sound indices 4-7)
 	int ambientSlotOffset = g_engine->getRandomNumber(3);
 
-	return ambientSlotOffset; // Caller adds 4 to get room sound index
+	return ambientSlotOffset;
 }
 
 } // End of namespace Pelrock
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index 407abf114c3..0219efb7a2c 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -85,9 +85,6 @@ public:
 
 	/**
 	 * Check if ambient sound should play this frame.
-	 * @param frameCount Current game frame counter
-	 * @return Ambient slot offset (0-3) to play, or -1 if no sound this frame
-	 *         Add kAmbientSoundSlotBase (4) to get room sound index
 	 */
 	int tickAmbientSound(uint32 frameCount);
 
diff --git a/engines/pelrock/spellbook.cpp b/engines/pelrock/spellbook.cpp
index ac802b8cc93..50497244b30 100644
--- a/engines/pelrock/spellbook.cpp
+++ b/engines/pelrock/spellbook.cpp
@@ -29,10 +29,10 @@
 
 namespace Pelrock {
 
-static const uint32 kSpellbookImgOffset     = 1268719; // ALFRED.7 — spellbook sprite sheet start
+static const uint32 kSpellbookImgOffset = 1268719;     // ALFRED.7 — spellbook sprite sheet start
 static const uint32 kSpellbookImgDataOffset = 1268723; // ALFRED.7 — compressed sprite data
-static const uint32 kSpellbookTextOffset    = 288285;  // JUEGO.EXE — spellbook page text
-static const uint32 kSpellbookTextSize      = 2861;
+static const uint32 kSpellbookTextOffset = 288285;     // JUEGO.EXE — spellbook page text
+static const uint32 kSpellbookTextSize = 2861;
 
 SpellBook::SpellBook(PelrockEventManager *eventMan, ResourceManager *res)
 	: _palette(nullptr),
diff --git a/engines/pelrock/spellbook.h b/engines/pelrock/spellbook.h
index 6a587d194d4..9d944fd7d37 100644
--- a/engines/pelrock/spellbook.h
+++ b/engines/pelrock/spellbook.h
@@ -55,8 +55,7 @@ static const Bookmark _bookmarks[13] = {
 	{95, 277, 33, 24, 7},
 	{105, 227, 27, 33, 6},
 	{103, 118, 30, 26, 3},
-	{101, 78, 36, 33, 2}
-};
+	{101, 78, 36, 33, 2}};
 
 class SpellBook {
 


Commit: 903b6e4d22a3081308bba8c75f2a9386be6002ca
    https://github.com/scummvm/scummvm/commit/903b6e4d22a3081308bba8c75f2a9386be6002ca
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:41+02:00

Commit Message:
PELROCK: Cleanup utils

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/util.cpp
    engines/pelrock/util.h


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index e6c0bd4764b..c1c962c3cc7 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -552,7 +552,7 @@ void MenuManager::drawScreen() {
 }
 
 void MenuManager::drawInventoryIcons() {
-	bool debugIcons = true;
+	bool debugIcons = false;
 	for (int i = 0; i < 4; i++) {
 		int itemIndex = _curInventoryPage * 4 + i;
 		if (g_engine->_state->inventoryItems.size() <= itemIndex)
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 9e522918a23..012a9a7c90b 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -31,71 +31,12 @@
 namespace Pelrock {
 
 void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byte color) {
-	// debug("Drawing rect at (%d,%d) w=%d h=%d color=%d", x, y, w, h, color);
 	surface->drawLine(x, y, x + w, y, color);
 	surface->drawLine(x, y + h, x + w, y + h, color);
 	surface->drawLine(x, y, x, y + h, color);
 	surface->drawLine(x + w, y, x + w, y + h, color);
 }
 
-void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color) {
-	surface->drawLine(x, y, x + w, y, color);
-	surface->drawLine(x, y + h, x + w, y + h, color);
-	surface->drawLine(x, y, x, y + h, color);
-	surface->drawLine(x + w, y, x + w, y + h, color);
-}
-
-void drawRect(byte *screenBuffer, int x, int y, int w, int h, byte color) {
-	Graphics::Surface surface;
-	surface.create(w, h, Graphics::PixelFormat::createFormatCLUT8());
-	drawRect(&surface, 0, 0, w - 1, h, color);
-
-	for (int py = 0; py < h; py++) {
-		for (int px = 0; px < w; px++) {
-			int destIdx = (y + py) * 640 + (x + px);
-			int pixelColor = *((byte *)surface.getBasePtr(px, py));
-			if (pixelColor != 0)
-				screenBuffer[destIdx] = pixelColor;
-		}
-	}
-	surface.free();
-}
-
-void drawText(byte *screenBuffer, Graphics::Font *font, Common::String text, int x, int y, int w, byte color, Graphics::TextAlign align) {
-	Common::Rect rect = font->getBoundingBox(text.c_str());
-	Graphics::Surface surface;
-	int bboxW = rect.width();
-	int bboxH = rect.height();
-
-	surface.create(bboxW, bboxH, Graphics::PixelFormat::createFormatCLUT8());
-	surface.fillRect(Common::Rect(0, 0, bboxW, bboxH), 255);
-	if (x + bboxW > 640) {
-		x = 640 - bboxW - 2;
-	}
-	if (y + bboxH > 400) {
-		y = 400 - bboxH - 2;
-	}
-	if (x < 0) {
-		x = 0;
-	}
-	if (y < 0) {
-		y = 0;
-	}
-
-	// Draw main text on top
-	font->drawString(&surface, text.c_str(), 0, 0, bboxW, color, align);
-	// drawRect(surface, 0, 0, bboxW - 1, bboxH - 1, color);
-	for (int py = 0; py < bboxH; py++) {
-		for (int px = 0; px < bboxW; px++) {
-			int destIdx = (y + py) * 640 + (x + px);
-			int pixelColor = *((byte *)surface.getBasePtr(px, py));
-			if(pixelColor != 255 && destIdx >= 0 && destIdx < 256000)
-				screenBuffer[destIdx] = pixelColor;
-		}
-	}
-	surface.free();
-}
-
 void drawText(Graphics::Font *font, Common::String text, int x, int y, int w, byte color) {
 	Common::Rect rect = font->getBoundingBox(text.c_str());
 	if (x + rect.width() > 640) {
@@ -273,27 +214,6 @@ void rleDecompressSingleBuda(Common::SeekableReadStream *stream, uint32 startPos
 	free(buffer);
 }
 
-// Helper function for transparent blitting
-void drawSpriteToBuffer(byte *buffer, int bufferWidth, byte *sprite, int x, int y, int width, int height, int transparentColor) {
-
-	for (int py = 0; py < height; py++) {
-		for (int px = 0; px < width; px++) {
-			int srcIdx = py * width + px;
-			byte pixel = sprite[srcIdx];
-
-			if (pixel != transparentColor) {
-				int destX = x + px;
-				int destY = y + py;
-
-				if (destX >= 0 && destX < 640 &&
-					destY >= 0 && destY < 400) {
-					buffer[destY * bufferWidth + destX] = pixel;
-				}
-			}
-		}
-	}
-}
-
 // ManagedSurface overload: wraps sprite data in a Surface and uses transBlitFrom
 void drawSpriteToBuffer(Graphics::ManagedSurface &dest, byte *sprite, int x, int y, int width, int height, int transparentColor) {
 	Graphics::Surface spriteSurf;
@@ -301,22 +221,6 @@ void drawSpriteToBuffer(Graphics::ManagedSurface &dest, byte *sprite, int x, int
 	dest.transBlitFrom(spriteSurf, Common::Point(x, y), transparentColor);
 }
 
-void blitSurfaceToBuffer(Graphics::Surface *surface, byte *buffer, int bufferWidth, int bufferHeight, int destX, int destY) {
-	for (int y = 0; y < surface->h; y++) {
-		for (int x = 0; x < surface->w; x++) {
-			int px = destX + x;
-			int py = destY + y;
-			if (px >= 0 && px < bufferWidth && py >= 0 && py < bufferHeight) {
-
-				byte pixel = *((byte *)surface->getBasePtr(x, y));
-				if (pixel != 0) {
-					buffer[py * bufferWidth + px] = pixel;
-				}
-			}
-		}
-	}
-}
-
 void extractSingleFrame(byte *source, byte *dest, int frameIndex, int frameWidth, int frameHeight) {
 	for (int y = 0; y < frameHeight; y++) {
 		for (int x = 0; x < frameWidth; x++) {
@@ -377,55 +281,6 @@ void changeGameSpeed(Common::Event e) {
 	}
 }
 
-Common::StringArray arrayOf(Common::String str) {
-	return Common::StringArray(1, str);
-}
-
-
-void invertSprite(byte *spriteBuf, int w, int h) {
-	// invert horizontal lines so character is upside down
-	for (int y = 0; y < h / 2; y++) {
-		for (int x = 0; x < w; x++) {
-			int topIndex = y * w + x;
-			int bottomIndex = (h - 1 - y) * w + x;
-			byte temp = spriteBuf[topIndex];
-			spriteBuf[topIndex] = spriteBuf[bottomIndex];
-			spriteBuf[bottomIndex] = temp;
-		}
-	}
-}
-
-void drawPaletteSquares(byte *screenBuffer, byte *palette) {
-	// Draw 3x3 squares for all 256 palette colors
-	// Arrange them in a 16x16 grid (256 colors)
-	const int squareSize = 6;
-	const int colorsPerRow = 16;
-	const int startX = 10;  // Left margin
-	const int startY = 10;  // Top margin
-	const int spacing = 1;  // Space between squares
-
-	for (int colorIndex = 0; colorIndex < 256; colorIndex++) {
-		int row = colorIndex / colorsPerRow;
-		int col = colorIndex % colorsPerRow;
-
-		int x = startX + col * (squareSize + spacing);
-		int y = startY + row * (squareSize + spacing);
-
-		// Draw the 3x3 square with the current color
-		for (int py = 0; py < squareSize; py++) {
-			for (int px = 0; px < squareSize; px++) {
-				int destX = x + px;
-				int destY = y + py;
-
-				// Bounds check
-				if (destX >= 0 && destX < 640 && destY >= 0 && destY < 400) {
-					screenBuffer[destY * 640 + destX] = colorIndex;
-				}
-			}
-		}
-	}
-}
-
 void drawPaletteSquares(Graphics::ManagedSurface &dest, byte *palette) {
 	const int squareSize = 6;
 	const int colorsPerRow = 16;
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index fe61d1d2d42..95f73dcff12 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -31,27 +31,21 @@
 
 namespace Pelrock {
 
-const int EXPECTED_SIZE = 640 * 400;
 size_t rleDecompress(const byte *data, size_t data_size, uint32 offset, uint32 size, byte **out_data, bool untilBuda = true);
 void readUntilBuda(Common::SeekableReadStream *stream, uint32 startPos, byte *&buffer, size_t &outSize);
 void rleDecompressSingleBuda(Common::SeekableReadStream *stream, uint32 startPos, byte *&buffer, size_t &outSize);
-void drawSpriteToBuffer(byte *buffer, int bufferWidth, byte *sprite, int x, int y, int width, int height, int transparentColor);
 void drawSpriteToBuffer(Graphics::ManagedSurface &dest, byte *sprite, int x, int y, int width, int height, int transparentColor);
-void blitSurfaceToBuffer(Graphics::Surface *surface, byte *buffer, int bufferWidth, int bufferHeight, int destX, int destY);
 void extractSingleFrame(byte *source, byte *dest, int frameIndex, int frameWidth, int frameHeight);
-void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byte color);
-void drawRect(Graphics::Surface *surface, int x, int y, int w, int h, byte color);
-void drawRect(byte *screenBuffer, int x, int y, int w, int h, byte color);
-void drawText(byte *screenBuffer, Graphics::Font *font, Common::String text, int x, int y, int w, byte color, Graphics::TextAlign align = Graphics::kTextAlignLeft);
+
 void drawText(Graphics::ManagedSurface &dest, Graphics::Font *font, Common::String text, int x, int y, int w, byte color, Graphics::TextAlign align = Graphics::kTextAlignLeft);
 void drawText(Graphics::Font *font, Common::String text, int x, int y, int w, byte color);
 Common::String joinStrings(const Common::Array<Common::String> &strings, const Common::String &separator);
-void drawPos(Graphics::ManagedSurface *surface, int x, int y, byte color);
 byte decodeChar(byte b);
 void changeGameSpeed(Common::Event e);
-Common::StringArray arrayOf(Common::String str);
-void invertSprite(byte *data, int w, int h);
-void drawPaletteSquares(byte *screenBuffer, byte *palette);
+
+// debug functions
+void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byte color);
+void drawPos(Graphics::ManagedSurface *surface, int x, int y, byte color);
 void drawPaletteSquares(Graphics::ManagedSurface &dest, byte *palette);
 
 static const int special_chars[] = {


Commit: ce2b01f4faa74250e08a5eddbf66558733cd8305
    https://github.com/scummvm/scummvm/commit/ce2b01f4faa74250e08a5eddbf66558733cd8305
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:41+02:00

Commit Message:
PELROCK: Types cleanup

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h
    engines/pelrock/room.h
    engines/pelrock/saveload.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 102d776ace9..d093a424679 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -2130,10 +2130,10 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 				_sound->playSound(_room->_roomSfx[8], 0);
 				_dialog->say(_res->_ingameTexts[DIOSHALCON + spell->page], 1);
 				int flightIndex = _room->_currentRoomNumber - 51;
-				if (_flightSorcererAppeared && !_flightInBlockingAnim && spell->page == kFlightRooms[flightIndex].spellPage) {
+				if (_fightSorcererAppeared && !_fightInBlockingAnim && spell->page == kFightRooms[flightIndex].spellPage) {
 					_state->setFlag(FLAG_COMO_ESTAN_LOS_DIOSES, _state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) | (1 << flightIndex));
 					_sound->playSound(_room->_roomSfx[1], 0);
-					smokeAnimation(kFlightRooms[flightIndex].spriteIdx, true);
+					smokeAnimation(kFightRooms[flightIndex].spriteIdx, true);
 					_room->addStickerToRoom(_room->_currentRoomNumber, 127 + flightIndex);
 					_room->addStickerToRoom(52, 106 + flightIndex);
 
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index cda61767630..50f5486cd85 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -52,6 +52,10 @@ const byte kCtrlEndConversation      = 0xF4; /* End conversation and disable opt
 const byte kCtrlGoBack               = 0xF0; /* Go back in conversation */
 const byte kCtrlAltSpeakerRoot       = 0xFE; /* Separates conversations from different speakers */
 
+const int kChoiceHeight = 16; 		// Height of each choice line in pixels
+const int kMaxCharsPerLine = 47; 	// 47 characters
+const int kMaxLines = 5;			// Maximum number of lines per page
+
 // Helper structures for conversation state management
 struct ConversationState {
 	uint32 position;
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index e162930be1d..9cda936bb97 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -433,7 +433,7 @@ void PelrockEngine::mouseHoverForMap() {
 void PelrockEngine::frameTriggers() {
 	uint32 frameCount = _chrono->getFrameCount();
 	passerByAnim(frameCount);
-	handleFlightRoomFrame();
+	handleFightRoomFrame();
 	shakeEffect();
 	maybeHaveDogPee();
 }
@@ -2406,17 +2406,17 @@ void PelrockEngine::credits() {
 
 void PelrockEngine::initGodsSequences(int roomNumber) {
 	int idx = roomNumber - 51;
-	_flightFrameCounter = 0;
-	_flightSorcererSpriteIdx = kFlightRooms[idx].spriteIdx;
-	_flightSorcererAppeared = false;
-	_flightSpellCast = false;
-	_flightSpellFrameCounter = 0;
-	_flightInBlockingAnim = false;
+	_fightFrameCounter = 0;
+	_fightSorcererSpriteIdx = kFightRooms[idx].spriteIdx;
+	_fightSorcererAppeared = false;
+	_fightSpellCast = false;
+	_fightSpellFrameCounter = 0;
+	_fightInBlockingAnim = false;
 
-	_room->disableSprite(_flightSorcererSpriteIdx);
+	_room->disableSprite(_fightSorcererSpriteIdx);
 }
 
-void PelrockEngine::handleFlightRoomFrame() {
+void PelrockEngine::handleFightRoomFrame() {
 	int room = _room->_currentRoomNumber;
 	if (room < 51 || room > 54)
 		return;
@@ -2428,44 +2428,44 @@ void PelrockEngine::handleFlightRoomFrame() {
 	}
 
 	// Guard against reentrance from blocking animation loops calling renderScene
-	if (_flightInBlockingAnim)
+	if (_fightInBlockingAnim)
 		return;
 
 	if (_alfredState.animState != ALFRED_IDLE || _actionPopupState.isActive) {
 		return;
 	}
-	_flightFrameCounter++;
+	_fightFrameCounter++;
 
 	// Phase 1: NPC appearance at tick 64
-	if (!_flightSorcererAppeared && _flightFrameCounter >= 64) {
+	if (!_fightSorcererAppeared && _fightFrameCounter >= 64) {
 		_sound->playSound(_room->_roomSfx[0]);
-		_flightSorcererAppeared = true;
-		_flightInBlockingAnim = true;
-		_room->findSpriteByIndex(_flightSorcererSpriteIdx)->animData[0].nframes = 1;
-		smokeAnimation(_flightSorcererSpriteIdx, false);
-		_flightInBlockingAnim = false;
+		_fightSorcererAppeared = true;
+		_fightInBlockingAnim = true;
+		_room->findSpriteByIndex(_fightSorcererSpriteIdx)->animData[0].nframes = 1;
+		smokeAnimation(_fightSorcererSpriteIdx, false);
+		_fightInBlockingAnim = false;
 		return;
 	}
 
 	// Phase 2: spell trigger at tick 104 (64 + 40)
-	if (_flightSorcererAppeared && !_flightSpellCast && _flightFrameCounter >= 104) {
-		debug("spell cast triggered for room %d at frame %d", room, _flightFrameCounter);
-		_flightSpellCast = true;
-		_flightSpellFrameCounter = 0;
+	if (_fightSorcererAppeared && !_fightSpellCast && _fightFrameCounter >= 104) {
+		debug("spell cast triggered for room %d at frame %d", room, _fightFrameCounter);
+		_fightSpellCast = true;
+		_fightSpellFrameCounter = 0;
 	}
 
 	// Phase 3: wait 40 ticks after spell trigger, then cast
-	if (_flightSpellCast) {
-		debug("in spell cast phase for room %d at frame %d", room, _flightFrameCounter);
-		_flightSpellFrameCounter++;
-		if (_flightSpellFrameCounter >= 40 && !(_state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) & (1 << idx))) {
-			debug("spell cast animation starting for room %d at frame %d, flag is %d", room, _flightFrameCounter, _state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES));
-			_flightInBlockingAnim = true;
-
-			int spellFrames = kFlightRooms[idx].spellFrames;
+	if (_fightSpellCast) {
+		debug("in spell cast phase for room %d at frame %d", room, _fightFrameCounter);
+		_fightSpellFrameCounter++;
+		if (_fightSpellFrameCounter >= 40 && !(_state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) & (1 << idx))) {
+			debug("spell cast animation starting for room %d at frame %d, flag is %d", room, _fightFrameCounter, _state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES));
+			_fightInBlockingAnim = true;
+
+			int spellFrames = kFightRooms[idx].spellFrames;
 			int framesDone = 0;
-			_room->findSpriteByIndex(_flightSorcererSpriteIdx)->animData[0].nframes = kFlightRooms[idx].spellFrames;
-			_room->findSpriteByIndex(_flightSorcererSpriteIdx)->animData[0].speed = 1;
+			_room->findSpriteByIndex(_fightSorcererSpriteIdx)->animData[0].nframes = kFightRooms[idx].spellFrames;
+			_room->findSpriteByIndex(_fightSorcererSpriteIdx)->animData[0].speed = 1;
 			_sound->playSound(_room->_roomSfx[1]);
 			while (!shouldQuit() && framesDone < spellFrames) {
 				_events->pollEvent();
@@ -2474,10 +2474,10 @@ void PelrockEngine::handleFlightRoomFrame() {
 				_screen->update();
 				g_system->delayMillis(10);
 			}
-			_room->findSpriteByIndex(_flightSorcererSpriteIdx)->animData[0].nframes = 1;
+			_room->findSpriteByIndex(_fightSorcererSpriteIdx)->animData[0].nframes = 1;
 			smokeAnimation(-1, true);
 
-			_flightInBlockingAnim = false;
+			_fightInBlockingAnim = false;
 
 			_alfredState.x = 294;
 			_alfredState.y = 387;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index f492898c3fe..cfcf45c4a99 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -150,12 +150,12 @@ private:
 	int _renderSkipAmount = 0;
 	int _renderSkipCounter = 0;
 
-	int _flightFrameCounter = 0;
-	int _flightSorcererSpriteIdx = -1;
-	bool _flightSorcererAppeared = false;
-	bool _flightSpellCast = false;
-	int _flightSpellFrameCounter = 0;
-	bool _flightInBlockingAnim = false;
+	int _fightFrameCounter = 0;
+	int _fightSorcererSpriteIdx = -1;
+	bool _fightSorcererAppeared = false;
+	bool _fightSpellCast = false;
+	int _fightSpellFrameCounter = 0;
+	bool _fightInBlockingAnim = false;
 	bool _disableAmbientSounds = false;
 	bool _isDogPeeing = false;
 	bool _disableAction = false;
@@ -256,7 +256,7 @@ public:
 	void maybeHaveDogPee();
 	void maybePlayPostIntro();
 	void shakeEffect();
-	void handleFlightRoomFrame();
+	void handleFightRoomFrame();
 	void paintDebugLayer();
 
 	void passerByAnim(uint32 frameCount);
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 65116ae2cd8..5c9efd3abf9 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -29,6 +29,8 @@
 
 namespace Pelrock {
 
+static const int kRoomStructSize = 104;
+static const int kTalkingAnimHeaderSize = 55;
 static const int kNumSfxPerRoom = 9;
 static const int unpickableHotspotExtras[] = {
 	308, // lamppost cable
diff --git a/engines/pelrock/saveload.cpp b/engines/pelrock/saveload.cpp
index ac62e159528..2b6c3cfa3b0 100644
--- a/engines/pelrock/saveload.cpp
+++ b/engines/pelrock/saveload.cpp
@@ -42,7 +42,6 @@ void syncExit(Common::Serializer &s, Exit &exit) {
 	s.syncAsUint16LE(exit.targetRoom);
 	s.syncAsSint16LE(exit.targetX);
 	s.syncAsSint16LE(exit.targetY);
-	s.syncAsUint16LE(exit.targetDir);
 	s.syncAsByte((byte &)exit.dir);
 	s.syncAsByte(exit.isEnabled);
 }
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index df4df025152..1780ec71d2f 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -29,6 +29,9 @@
 
 namespace Pelrock {
 
+/**
+ * Cursor types when hovering over hotspots
+ */
 enum Cursor {
 	DEFAULT,
 	HOTSPOT,
@@ -37,6 +40,9 @@ enum Cursor {
 	COMBINATION
 };
 
+/**
+ * Actions masks on hotspots, matching against these gives us the available actions.
+ */
 const byte kActionMaskNone = 0;
 const byte kActionMaskOpen = 1;
 const byte kActionMaskClose = 2;
@@ -60,27 +66,21 @@ enum VerbIcon {
 	NO_ACTION
 };
 
-const uint32 kLongClickDuration = 500; // 500ms for long click
 const int kCursorWidth = 16;
 const int kCursorHeight = 18;
 const int kCursorSize = 288; // 16 * 18
-const int kRoomStructSize = 104;
-const int kTalkingAnimHeaderSize = 55;
-const int kNumRooms = 56;
+
 const int kVerbIconWidth = 60;
 const int kVerbIconHeight = 60;
 const int kNumVerbIcons = 9;
+
 const int kBalloonWidth = 247;
 const int kBalloonHeight = 112;
 const int kBalloonFrames = 4;
-const int kTextCharDisplayTime = 100; // 10ms per character
-const int kVerbIconPadding = 20;
 
 const int kAlfredFrameWidth = 51;
 const int kAlfredFrameHeight = 102;
 
-const int kChoiceHeight = 16; // Height of each choice line in pixels
-
 const int kTalkAnimationSpeed = 2;   // Frames per update
 const int kAlfredAnimationSpeed = 2; // Frames per update
 
@@ -102,9 +102,6 @@ const int kMaxPathLength = 100;
 const int kMaxMovementSteps = 100; // 500 bytes / 5 bytes per step
 const byte kPathEnd = 0xFF;        // End of path marker
 
-const int kMaxCharsPerLine = 0x2F; // 47 characters
-const int kMaxLines = 5;           // Maximum number of lines per page
-
 const byte kAlfredColor = 0x0D;
 
 enum OverlayMode {
@@ -237,6 +234,9 @@ struct PathContext {
 	uint16 compressed_length;
 };
 
+/**
+ * Each Anim has its own speed, loopCount and movement!
+ */
 struct Anim {
 	int nframes;
 	int curFrame = 0;
@@ -257,14 +257,12 @@ struct Exit {
 	uint16 targetRoom;
 	int16 targetX;
 	int16 targetY;
-	uint16 targetDir;
 	AlfredDirection dir;
 	byte isEnabled;
 };
 
 struct Sprite {
-	byte index; // number of the animation in the rooms
-	byte type;
+	byte index;    // number of the animation in the rooms
 	int16 x;       // 0
 	int16 y;       // 2
 	int w;         // 4
@@ -272,8 +270,7 @@ struct Sprite {
 	uint16 stride; // 6-7
 	int numAnims;  // 8
 	int curAnimIndex = 0;
-	byte zOrder; // byte at file offset 23 (in-memory struct offset 0x21). Unsigned 0-254, 255=disabled
-
+	byte zOrder;                       // byte at file offset 23
 	byte actionFlags;                  // 34
 	bool isHotspotDisabled;            // 38
 	bool disableAfterSequence = false; // 39
@@ -310,19 +307,19 @@ struct TalkingAnims {
 	byte hAnimA;
 	byte unknown3[2];
 	byte numFramesAnimA;
-	byte unknown4[4];         // slot 0 data pointer (unused in ScummVM)
-	byte speedByteA;          // slot 0 offset 0x12: controls NPC talk render rate (original: 2+speedByte ticks per render)
+	byte unknown4[4]; // slot 0 data pointer (unused in ScummVM)
+	byte speedByteA;  // slot 0 offset 0x12: controls NPC talk render rate (original: 2+speedByte ticks per render)
 
 	byte offsetXAnimB;
 	byte offsetYAnimB;
 
 	byte wAnimB;
 	byte hAnimB;
-	byte unknown5[2];         // slot 1 stride (unused in ScummVM)
+	byte unknown5[2]; // slot 1 stride (unused in ScummVM)
 	byte numFramesAnimB;
-	byte unknown7[4];         // slot 1 data pointer (unused in ScummVM)
-	byte speedByteB;          // slot 1 speed byte at file offset 30
-	byte unknown6[24];        // slots 2-3 (unused)
+	byte unknown7[4];  // slot 1 data pointer (unused in ScummVM)
+	byte speedByteB;   // slot 1 speed byte at file offset 30
+	byte unknown6[24]; // slots 2-3 (unused)
 
 	// Runtime fields (not read from file)
 	byte currentFrameAnimA;
@@ -706,7 +703,7 @@ struct SaveGameData {
 	GameStateData *gameState = nullptr;
 };
 
-struct FlightRoomCfg {
+struct FightRoomCfg {
 	int roomNumber;
 	int spriteIdx;
 	int appearFrames;
@@ -714,7 +711,7 @@ struct FlightRoomCfg {
 	int spellPage;
 };
 
-static const FlightRoomCfg kFlightRooms[] = {
+static const FightRoomCfg kFightRooms[] = {
 	{51, 1, 31, 17, 8}, // room 51
 	{52, 0, 30, 13, 4}, // room 52
 	{53, 1, 30, 13, 0}, // room 53


Commit: 34129b6bff87dc3bce85fe515fdfbd9d1cd73779
    https://github.com/scummvm/scummvm/commit/34129b6bff87dc3bce85fe515fdfbd9d1cd73779
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:42+02:00

Commit Message:
PELROCK: Cleanup of offsets

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/offsets.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/room.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index d093a424679..116edb997f3 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -230,7 +230,7 @@ const CombinationEntry combinationTable[] = {
  */
 void PelrockEngine::openDoor(HotSpot *hotspot, int exitIndex, int sticker, bool masculine, bool stayClosed) {
 	if (_room->hasSticker(sticker)) {
-		int text = masculine == MASCULINE ? YA_ABIERTO_M : YA_ABIERTA_F;
+		int text = masculine == MASCULINE ? kTextYaAbiertoM : kTextYaAbiertaF;
 		_dialog->say(_res->_ingameTexts[text]);
 		return;
 	}
@@ -241,7 +241,7 @@ void PelrockEngine::openDoor(HotSpot *hotspot, int exitIndex, int sticker, bool
 
 void PelrockEngine::closeDoor(HotSpot *hotspot, int exitIndex, int sticker, bool masculine, bool stayOpen) {
 	if (!_room->hasSticker(sticker)) {
-		int text = masculine == MASCULINE ? YA_CERRADO_M : YA_CERRADA_F;
+		int text = masculine == MASCULINE ? kTextYaCerradoM : kTextYaCerradaF;
 		_dialog->say(_res->_ingameTexts[text]);
 		return;
 	}
@@ -279,7 +279,7 @@ void PelrockEngine::addInventoryItem(int item) {
  */
 void PelrockEngine::buyFromStore(HotSpot *hotspot, int stickerId) {
 	if (_state->hasInventoryItem(5) == false) {
-		_dialog->say(_res->_ingameTexts[NOTENGODINERO]);
+		_dialog->say(_res->_ingameTexts[kTextNoTengoDinero]);
 		return;
 	} else {
 		_room->addSticker(stickerId);
@@ -290,9 +290,9 @@ void PelrockEngine::buyFromStore(HotSpot *hotspot, int stickerId) {
 		addInventoryItem(hotspot->extra);
 		_currentHotspot = nullptr;
 		walkLoop(224, 283, ALFRED_LEFT);
-		_dialog->say(_res->_ingameTexts[CUESTA1000]);
-		_dialog->say(_res->_ingameTexts[AQUITIENE]);
-		_dialog->say(_res->_ingameTexts[MUYBIEN]);
+		_dialog->say(_res->_ingameTexts[kTextCuesta1000]);
+		_dialog->say(_res->_ingameTexts[kTextAquiTiene]);
+		_dialog->say(_res->_ingameTexts[kTextMuyBien]);
 		_state->removeInventoryItem(5); // Remove 1000 pesetas bill
 	}
 }
@@ -311,21 +311,21 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_state->setCurrentRoot(4, 2, 0);
 		break;
 	case 259:
-		_dialog->say(_res->_ingameTexts[DEPIEDRANO_DEHIELO], 1);
-		_dialog->say(_res->_ingameTexts[NO_EMPECEMOS]);
+		_dialog->say(_res->_ingameTexts[kTextDePiedraNoDeHielo], 1);
+		_dialog->say(_res->_ingameTexts[kTextNoEmpecemos]);
 		break;
 	case 260:
-		_dialog->say(_res->_ingameTexts[CUERPO_DANONE], 1);
-		_dialog->say(_res->_ingameTexts[CABEZA_HUECA]);
+		_dialog->say(_res->_ingameTexts[kTextCuerpoDanone], 1);
+		_dialog->say(_res->_ingameTexts[kTextCabezaHueca]);
 		break;
 	case 261:
-		_dialog->say(_res->_ingameTexts[ESO_LO_SERAS_TU], 1);
+		_dialog->say(_res->_ingameTexts[kTextEsoLoSerasTu], 1);
 		break;
 	case 262:
-		_dialog->say(_res->_ingameTexts[DEMASIADO_NO_PUEDO_PENSAR], 1);
+		_dialog->say(_res->_ingameTexts[kTextDemasiadoNoPuedoPensar], 1);
 		break;
 	case 263:
-		_dialog->say(_res->_ingameTexts[UN_POCO_RESPETO]);
+		_dialog->say(_res->_ingameTexts[kTextUnPocoRespeto]);
 		break;
 	case 264:
 		// skip to root after the next one
@@ -372,7 +372,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		addInventoryItem(102);
 		break;
 	case 331:
-		_dialog->say(_res->_ingameTexts[HECHOELPRIMO]);
+		_dialog->say(_res->_ingameTexts[kTextHechoElPrimo]);
 		break;
 	case 332:
 		// psychologist card
@@ -381,7 +381,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		}
 		break;
 	case 333:
-		_dialog->say(_res->_ingameTexts[MEHANTOMADO_EL_PELO]);
+		_dialog->say(_res->_ingameTexts[kTextMeHanTomadoElPelo]);
 		break;
 	case 334:
 		addInventoryItem(76);
@@ -392,7 +392,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		addInventoryItem(103);
 		break;
 	case 336:
-		_dialog->say(_res->_ingameTexts[PESADO_UNRATO]);
+		_dialog->say(_res->_ingameTexts[kTextPesadoUnRato]);
 		break;
 	case 337:
 	case 338:
@@ -423,24 +423,24 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		}
 		break;
 	case 351:
-		_dialog->say(_res->_ingameTexts[TRAIDOR], 0);
-		_dialog->say(_res->_ingameTexts[TUTIA], 1);
-		_dialog->say(_res->_ingameTexts[LATUYA], 0);
-		_dialog->say(_res->_ingameTexts[GORDO], 1);
-		_dialog->say(_res->_ingameTexts[FIDEO], 0);
-		_dialog->say(_res->_ingameTexts[LIMPIACULO], 1);
-		_dialog->say(_res->_ingameTexts[CONTUTURBANTE], 0);
-		_dialog->say(_res->_ingameTexts[OSO], 1);
-		_dialog->say(_res->_ingameTexts[COMADREJA], 0);
-		_dialog->say(_res->_ingameTexts[CABEZON], 1);
-		_dialog->say(_res->_ingameTexts[TUABUELO], 0);
-		_dialog->say(_res->_ingameTexts[TUMUJER], 1);
-		_dialog->say(_res->_ingameTexts[PERDEDOR], 0);
-		_dialog->say(_res->_ingameTexts[SOYMEJORQUETU], 1);
-		_dialog->say(_res->_ingameTexts[TRAMPOSO], 0);
-		_dialog->say(_res->_ingameTexts[MALPERDEDOR], 1);
-		_dialog->say(_res->_ingameTexts[PARAUNAVEZ], 0);
-		_dialog->say(_res->_ingameTexts[MEJORMELARGO], 1);
+		_dialog->say(_res->_ingameTexts[kTextTraidor], 0);
+		_dialog->say(_res->_ingameTexts[kTextTuTia], 1);
+		_dialog->say(_res->_ingameTexts[kTextLaTuya], 0);
+		_dialog->say(_res->_ingameTexts[kTextGordo], 1);
+		_dialog->say(_res->_ingameTexts[kTextFideo], 0);
+		_dialog->say(_res->_ingameTexts[kTextLimpiaculos], 1);
+		_dialog->say(_res->_ingameTexts[kTextConTuTurbante], 0);
+		_dialog->say(_res->_ingameTexts[kTextOso], 1);
+		_dialog->say(_res->_ingameTexts[kTextComadreja], 0);
+		_dialog->say(_res->_ingameTexts[kTextCabezon], 1);
+		_dialog->say(_res->_ingameTexts[kTextTuAbuelo], 0);
+		_dialog->say(_res->_ingameTexts[kTextTuMujer], 1);
+		_dialog->say(_res->_ingameTexts[kTextPerdedor], 0);
+		_dialog->say(_res->_ingameTexts[kTextSoyMejorQueTu], 1);
+		_dialog->say(_res->_ingameTexts[kTextTramposo], 0);
+		_dialog->say(_res->_ingameTexts[kTextMalPerdedor], 1);
+		_dialog->say(_res->_ingameTexts[kTextParaUnaVez], 0);
+		_dialog->say(_res->_ingameTexts[kTextMejorMeLargo], 1);
 		break;
 		// end merchants
 	case 353:
@@ -744,7 +744,7 @@ void PelrockEngine::noOpItem(int item, HotSpot *hotspot) {
 	// These are books in the library being returned to the librarian
 	if (item >= 11 && item <= 58 && hotspot->extra == 358) {
 		_state->removeInventoryItem(item);
-		_dialog->say(_res->_ingameTexts[DEACUERDO_2]);
+		_dialog->say(_res->_ingameTexts[kTextDeAcuerdo2]);
 		return;
 	}
 	_alfredState.direction = ALFRED_DOWN;
@@ -757,7 +757,7 @@ void PelrockEngine::openRoomDoor(HotSpot *hotspot) {
 
 void PelrockEngine::openRoomDrawer(HotSpot *hotspot) {
 	if (_room->hasSticker(91)) {
-		_dialog->say(_res->_ingameTexts[YA_ABIERTO_M]);
+		_dialog->say(_res->_ingameTexts[kTextYaAbiertoM]);
 		return;
 	}
 	_room->addSticker(91);
@@ -786,16 +786,16 @@ void PelrockEngine::pickUpBrick(HotSpot *hotspot) {
 }
 
 void PelrockEngine::openIceCreamShopDoor(HotSpot *hotspot) {
-	_dialog->say(_res->_ingameTexts[HELADERIA_CERRADA]);
+	_dialog->say(_res->_ingameTexts[kTextHeladeraCerrada]);
 }
 
 void PelrockEngine::pickupGarbageCan(HotSpot *hotspot) {
-	_dialog->say(_res->_ingameTexts[POBRE_PERO_NO_HE_LLEGADO_A_ESO]);
+	_dialog->say(_res->_ingameTexts[kTextPobreNoHeLlegadoAEso]);
 }
 
 void PelrockEngine::closeRoomDrawer(HotSpot *hotspot) {
 	if (!_room->hasSticker(91)) {
-		_dialog->say(_res->_ingameTexts[YA_CERRADO_M]);
+		_dialog->say(_res->_ingameTexts[kTextYaCerradoM]);
 		return;
 	}
 	_room->removeSticker(91);
@@ -803,7 +803,7 @@ void PelrockEngine::closeRoomDrawer(HotSpot *hotspot) {
 }
 
 void PelrockEngine::openClosedDrawer(HotSpot *hotspot) {
-	_dialog->say(_res->_ingameTexts[ESTAN_CERRADOS]);
+	_dialog->say(_res->_ingameTexts[kTextEstanCerrados]);
 }
 
 void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
@@ -824,10 +824,10 @@ void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
 			// 1 in every 15 times, the hooker will say "fancy a good time?"
 			bool sayLine = getRandomNumber(15) == 1;
 			if (sayLine) {
-				_dialog->say(_res->_ingameTexts[TEAPETECE_BUENRATO]);
+				_dialog->say(_res->_ingameTexts[kTextTeApeteceBuenRato]);
 			}
 		} else {
-			_dialog->say(_res->_ingameTexts[NOTENGOMASDINERO]);
+			_dialog->say(_res->_ingameTexts[kTextNoTengoMasDinero]);
 		}
 	}
 }
@@ -841,7 +841,7 @@ void PelrockEngine::closeMcDoor(HotSpot *hotspot) {
 }
 
 void PelrockEngine::pickupBush(HotSpot *hotspot) {
-	_dialog->say(_res->_ingameTexts[MEHEVUELTOAPINCHAR]);
+	_dialog->say(_res->_ingameTexts[kTextMeHeVueltoAPinchar]);
 }
 
 void PelrockEngine::pickUpAndDisable(HotSpot *hotspot) {
@@ -871,15 +871,15 @@ void PelrockEngine::closeKitchenDoor(HotSpot *HotSpot) {
 
 void PelrockEngine::openKitchenDrawer(HotSpot *hotspot) {
 	if (!_state->getFlag(FLAG_JEFE_ENCARCELADO)) {
-		_dialog->say(_res->_ingameTexts[QUITA_ESAS_MANOS]);
+		_dialog->say(_res->_ingameTexts[kTextQuitaEsasManos]);
 	} else if (!_state->getFlag(FLAG_RECIPE_TAKEN)) {
 		_state->setFlag(FLAG_RECIPE_TAKEN, true);
 		_room->addSticker(36);
 		addInventoryItem(63); // Add recipe
-		_dialog->say(_res->_ingameTexts[QUESESTO_RECETA]);
+		_dialog->say(_res->_ingameTexts[kTextQuesEstoReceta]);
 	} else {
 		// Already took the recipe
-		_dialog->say(_res->_ingameTexts[YAESTA_ABIERTO]);
+		_dialog->say(_res->_ingameTexts[kTextYaEstaAbierto]);
 	}
 }
 
@@ -890,12 +890,12 @@ void PelrockEngine::openKitchenDoorFromInside(HotSpot *hotspot) {
 void PelrockEngine::useSpicySauceWithBurger(int inventoryObject, HotSpot *hotspot) {
 	_state->setFlag(FLAG_PUESTA_SALSA_PICANTE, true);
 	_sound->playSound(_room->_roomSfx[2]);
-	_dialog->say(_res->_ingameTexts[VAESTAR_POCOFUERTE]);
+	_dialog->say(_res->_ingameTexts[kTextVaestarPocoFuerte]);
 }
 
 void PelrockEngine::openShopDoor(HotSpot *hotspot) {
 	if (!_state->getFlag(FLAG_TIENDA_ABIERTA)) {
-		_dialog->say(_res->_ingameTexts[TIENDA_CERRADA]);
+		_dialog->say(_res->_ingameTexts[kTextTiendaCerrada]);
 		return;
 	} else {
 		openDoor(hotspot, 0, 13, MASCULINE, false);
@@ -945,9 +945,9 @@ void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
 	// Play the NPC dialog sequence
 	int16 dialog1y = y + 22;
 	int16 dialog2y = dialog1y + 10 + _largeFont->getFontHeight();
-	_dialog->say(_res->_ingameTexts[QUEHASIDOESO], x, dialog1y);
-	_dialog->say(_res->_ingameTexts[QUIENANDAAHI], x, dialog2y);
-	_dialog->say(_res->_ingameTexts[YOMEVOY]);
+	_dialog->say(_res->_ingameTexts[kTextQueHaSidoEso], x, dialog1y);
+	_dialog->say(_res->_ingameTexts[kTextQuienAndaAhi], x, dialog2y);
+	_dialog->say(_res->_ingameTexts[kTextYoMeVoy]);
 
 	_state->setFlag(FLAG_TIENDA_ABIERTA, true);
 	_room->addStickerToRoom(_room->_currentRoomNumber, 9, PERSIST_PERM);
@@ -968,7 +968,7 @@ void PelrockEngine::moveCable(HotSpot *hotspot) {
 }
 
 void PelrockEngine::useBrickWithShopWindow(int inventoryObject, HotSpot *hotspot) {
-	_dialog->say(_res->_ingameTexts[NOSE_ENTERARIA]);
+	_dialog->say(_res->_ingameTexts[kTextNoseEnteraria]);
 }
 
 void PelrockEngine::pickGuitar(HotSpot *hotspot) {
@@ -1025,7 +1025,7 @@ void PelrockEngine::openPlug(HotSpot *hotspot) {
 
 void PelrockEngine::useCordWithPlug(int inventoryObject, HotSpot *hotspot) {
 	if (!_room->hasSticker(18)) {
-		_dialog->say(_res->_ingameTexts[PRIMERO_ABRIRLO]);
+		_dialog->say(_res->_ingameTexts[kTextPrimeroAbrirlo]);
 	} else {
 		if (_state->getFlag(FLAG_CABLES_PUESTOS)) {
 			_room->addSticker(19);
@@ -1037,7 +1037,7 @@ void PelrockEngine::useCordWithPlug(int inventoryObject, HotSpot *hotspot) {
 
 void PelrockEngine::pickCables(HotSpot *hotspot) {
 	if (_room->hasSticker(21)) {
-		_dialog->say(_res->_ingameTexts[QUELOSCOJA_SUPADRE]);
+		_dialog->say(_res->_ingameTexts[kTextQueLosCojaSupadre]);
 		return;
 	}
 	// Duck to pick cables
@@ -1054,19 +1054,19 @@ void PelrockEngine::pickCables(HotSpot *hotspot) {
 	playAlfredSpecialAnim(2, true);
 	_room->addSticker(21);
 
-	_dialog->say(_res->_ingameTexts[RELOJ_HA_CAMBIADO]);
+	_dialog->say(_res->_ingameTexts[kTextRelojHaCambiado]);
 	_state->setCurrentRoot(4, 1, 0);
 }
 
 void PelrockEngine::showIdToGuard(int inventoryObject, HotSpot *hotspot) {
 	if (!_state->getFlag(FLAG_GUARDIA_PIDECOSAS)) {
-		_dialog->say(_res->_ingameTexts[CUANDOMELOPIDA]);
+		_dialog->say(_res->_ingameTexts[kTextCuandoMeLoPida]);
 		return;
 	}
 
 	if (!_state->getFlag(FLAG_GUARDIA_DNI_ENTREGADO)) {
 		_state->setFlag(FLAG_GUARDIA_DNI_ENTREGADO, true);
-		_dialog->say(_res->_ingameTexts[DEACUERDO]);
+		_dialog->say(_res->_ingameTexts[kTextDeAcuerdo]);
 	}
 	if (_state->getFlag(FLAG_SOBORNO_PORTERO) && _state->getFlag(FLAG_GUARDIA_DNI_ENTREGADO)) {
 		unlockMuseum();
@@ -1086,11 +1086,11 @@ void PelrockEngine::unlockMuseum() {
 
 void PelrockEngine::giveMoneyToGuard(int inventoryObject, HotSpot *hotspot) {
 	if (!_state->getFlag(FLAG_GUARDIA_PIDECOSAS)) {
-		_dialog->say(_res->_ingameTexts[PRETENDEUSTED_SOBORNARME]);
+		_dialog->say(_res->_ingameTexts[kTextPretendeUstedSobornarme]);
 		return;
 	} else if (!_state->getFlag(FLAG_SOBORNO_PORTERO)) {
 		_state->setFlag(FLAG_SOBORNO_PORTERO, true);
-		_dialog->say(_res->_ingameTexts[MUYBIEN]);
+		_dialog->say(_res->_ingameTexts[kTextMuyBien]);
 		_state->removeInventoryItem(5);
 	}
 	if (_state->getFlag(FLAG_SOBORNO_PORTERO) && _state->getFlag(FLAG_GUARDIA_DNI_ENTREGADO)) {
@@ -1101,12 +1101,12 @@ void PelrockEngine::giveMoneyToGuard(int inventoryObject, HotSpot *hotspot) {
 
 void PelrockEngine::openMuseumDoor(HotSpot *hotspot) {
 	if (!_state->getFlag(FLAG_GUARDIA_PIDECOSAS)) {
-		_dialog->say(_res->_ingameTexts[ALTO]);
+		_dialog->say(_res->_ingameTexts[kTextAlto]);
 		return;
 	} else if (!_state->getFlag(FLAG_GUARDIA_DNI_ENTREGADO)) {
-		_dialog->say(_res->_ingameTexts[NECESITODNI]);
+		_dialog->say(_res->_ingameTexts[kTextNecesitaDni]);
 	} else if (!_state->getFlag(FLAG_SOBORNO_PORTERO)) {
-		_dialog->say(_res->_ingameTexts[QUE_RECIBO_ACAMBIO]);
+		_dialog->say(_res->_ingameTexts[kTextQueReciboACambio]);
 	} else {
 		openDoor(hotspot, 1, 22, FEMININE, false);
 	}
@@ -1117,7 +1117,7 @@ void PelrockEngine::closeMuseumDoor(HotSpot *hotspot) {
 }
 
 void PelrockEngine::pickupFruit(HotSpot *hotspot) {
-	_dialog->say(_res->_ingameTexts[NO_THEY_MAKEYOU_FAT]);
+	_dialog->say(_res->_ingameTexts[kTextNoTheyMakeyouFat]);
 }
 
 void PelrockEngine::useAmuletWithStatue(int inventoryObject, HotSpot *hotspot) {
@@ -1133,16 +1133,16 @@ void PelrockEngine::useAmuletWithStatue(int inventoryObject, HotSpot *hotspot) {
 		walkTo(statueHotspot->x + statueHotspot->w / 2, statueHotspot->y + statueHotspot->h);
 		_sound->playSound(_room->_roomSfx[0]); // Magic sound
 		animateStatuePaletteFade(false);
-		_dialog->say(_res->_ingameTexts[ANDA]);
+		_dialog->say(_res->_ingameTexts[kTextAnda]);
 		walkAndAction(statueHotspot, TALK);
 		waitForActionEnd();
 	}
 }
 
 void PelrockEngine::useSecretCodeWithStatue(int inventoryObject, HotSpot *hotspot) {
-	_dialog->say(_res->_ingameTexts[NOESAMIAQUIENDEBES]);
-	_dialog->say(_res->_ingameTexts[AQUIENENTONCES]);
-	_dialog->say(_res->_ingameTexts[LIBROSSECRETOS]);
+	_dialog->say(_res->_ingameTexts[kTextNoEsAMiAQuienDebes]);
+	_dialog->say(_res->_ingameTexts[kTextAQuienEntonces]);
+	_dialog->say(_res->_ingameTexts[kTextLibrosSecretos]);
 }
 
 void PelrockEngine::pickUpLetter(HotSpot *hotspot) {
@@ -1179,13 +1179,13 @@ void PelrockEngine::pickBooksFromShelf3(HotSpot *hotspot) {
 }
 
 void PelrockEngine::giveSecretCodeToLibrarian(int inventoryObject, HotSpot *hotspot) {
-	_dialog->say(_res->_ingameTexts[REGALO_LIBRO_RECETAS]);
+	_dialog->say(_res->_ingameTexts[kTextRegaloLibroRecetas]);
 	_state->removeInventoryItem(8);
 	addInventoryItem(59);
 }
 
 void PelrockEngine::useBrickWithLibrarian(int inventoryObject, HotSpot *hotspot) {
-	_dialog->say(_res->_ingameTexts[YSI_METIRA_MAQUINA]);
+	_dialog->say(_res->_ingameTexts[kTextYSiMeTiraMaquina]);
 }
 
 void PelrockEngine::openNewspaperDoor(HotSpot *hotspot) {
@@ -1222,7 +1222,7 @@ void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
 	if (_state->getFlag(FLAG_CROCODILLO_ENCENDIDO) == false) {
 		_sound->playMusicTrack(27);
 		checkIngredients();
-		_dialog->say(_res->_ingameTexts[CUIDADOIMPRUDENTE]);
+		_dialog->say(_res->_ingameTexts[kTextCuidadoImprudente]);
 		_alfredState.x -= 10;
 		_alfredState.y += 20;
 		playAlfredSpecialAnim(5);
@@ -1235,7 +1235,7 @@ void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
 		waitForSoundEnd();
 		_alfredState.animState = ALFRED_IDLE;
 		setScreenAndPrepare(28, ALFRED_DOWN);
-		_dialog->say(_res->_ingameTexts[QUEOSCUROESTAESTO]);
+		_dialog->say(_res->_ingameTexts[kTextQueOscuroEstaEsto]);
 	}
 }
 
@@ -1258,9 +1258,9 @@ void PelrockEngine::pickupSunflower(HotSpot *hotspot) {
 	if (_state->getFlag(FLAG_PARADOJA_RESUELTA) == false) {
 		if (_state->getFlag(FLAG_RIDDLE_PRESENTED)) {
 			// try to take the sunflower before solving the riddle
-			_dialog->say(_res->_ingameTexts[LEESTOYVIGILANDO]);
+			_dialog->say(_res->_ingameTexts[kTextLeEstoyVigilando]);
 		} else {
-			_dialog->say(_res->_ingameTexts[OIGA]);
+			_dialog->say(_res->_ingameTexts[kTextOiga]);
 			_state->setCurrentRoot(25, 26, 0);
 			walkAndAction(_room->findHotspotByExtra(467), TALK);
 			_state->setFlag(FLAG_RIDDLE_PRESENTED, true);
@@ -1276,14 +1276,14 @@ void PelrockEngine::pickupSunflower(HotSpot *hotspot) {
 
 void PelrockEngine::checkIngredients() {
 	byte ingredientes = _state->getFlag(FLAG_INGREDIENTES_CONSEGUIDOS);
-	int textLine = PRIMERINGREDIENTE + ingredientes;
+	int textLine = kTextPrimerIngrediente + ingredientes;
 	_dialog->say(_res->_ingameTexts[textLine]);
 	_state->setFlag(FLAG_INGREDIENTES_CONSEGUIDOS, ingredientes + 1);
 }
 
 void PelrockEngine::pickUpBook(int i) {
 	if (!_state->hasInventoryItem(10)) {
-		_dialog->say(_res->_ingameTexts[VENGA_ACA]);
+		_dialog->say(_res->_ingameTexts[kTextVengaAca]);
 		_state->setCurrentRoot(9, 1, 0);
 
 		if (_state->hasInventoryItem(3)) {
@@ -1301,9 +1301,9 @@ void PelrockEngine::pickUpBook(int i) {
 		}
 	} else {
 		if (_state->libraryShelf == -1) {
-			_dialog->say(_res->_ingameTexts[TODOS]);
+			_dialog->say(_res->_ingameTexts[kTextTodos]);
 		} else if (_state->libraryShelf != i) {
-			_dialog->say(_res->_ingameTexts[EL_LIBRO_NOESTA_AQUI]);
+			_dialog->say(_res->_ingameTexts[kTextElLibroNoEstaAqui]);
 		} else {
 			_state->libraryShelf = -1;
 			int booksInInventory = _state->booksInInventory();
@@ -1311,7 +1311,7 @@ void PelrockEngine::pickUpBook(int i) {
 				int firstBook = _state->findFirstBookIndex();
 				if (firstBook != -1)
 					_state->removeInventoryItem(firstBook);
-				_dialog->say(_res->_ingameTexts[TENDRE_DEJAR_LIBRO]);
+				_dialog->say(_res->_ingameTexts[kTextTendreDejarLibro]);
 			}
 			addInventoryItem(_state->selectedBookIndex);
 			_state->selectedBookIndex = -1;
@@ -1396,7 +1396,7 @@ void PelrockEngine::pickUpHairStrand(HotSpot *hotspot) {
 
 void PelrockEngine::openJailFloorTile(HotSpot *hotspot) {
 	if (_room->hasSticker(77)) {
-		_dialog->say(_res->_ingameTexts[YA_ABIERTO_M]);
+		_dialog->say(_res->_ingameTexts[kTextYaAbiertoM]);
 		return;
 	}
 	_room->enableExit(0, PERSIST_BOTH);
@@ -1416,12 +1416,12 @@ void PelrockEngine::useKeyWithPortrait(int inventoryObject, HotSpot *hotspot) {
 void PelrockEngine::openSafe(HotSpot *hotspot) {
 	if (_state->getFlag(FLAG_CLAVE_CAJA_FUERTE) == true) {
 		_room->addSticker(102);
-		_dialog->say(_res->_ingameTexts[GRANCANTIDAD_DINERO]);
+		_dialog->say(_res->_ingameTexts[kTextGranCantidadDinero]);
 		addInventoryItem(82);
 		_state->setCurrentRoot(27, 1, 0);
 		_state->setCurrentRoot(27, 1, 1);
 	} else {
-		_dialog->say(_res->_ingameTexts[SISUPIERA_COMBINACION]);
+		_dialog->say(_res->_ingameTexts[kTextSiSupieraCombinacion]);
 	}
 }
 
@@ -1459,11 +1459,11 @@ void PelrockEngine::giveMagazineToGuard(int inventoryObject, HotSpot *hotspot) {
 void PelrockEngine::giveWaterToGuard(int inventoryObject, HotSpot *hotspot) {
 	_state->setFlag(FLAG_VIGILANTE_BEBE_AGUA, _state->getFlag(FLAG_VIGILANTE_BEBE_AGUA) + 1);
 
-	_dialog->say(_res->_ingameTexts[ALACONUSTED]);
+	_dialog->say(_res->_ingameTexts[kTextAlaConUsted]);
 	_state->removeInventoryItem(86);
 	addInventoryItem(76);
 	if (_state->getFlag(FLAG_VIGILANTE_BEBE_AGUA) == 3) {
-		_dialog->say(_res->_ingameTexts[MEMEO]);
+		_dialog->say(_res->_ingameTexts[kTextMeMeo]);
 		_state->setFlag(FLAG_VIGILANTE_MEANDO, true);
 		guardMovement();
 		_room->disableSprite(36, 0, PERSIST_BOTH);
@@ -1508,9 +1508,9 @@ void PelrockEngine::pickUpStone(HotSpot *hotspot) {
 	_sound->playSound("QUAKE2ZZ.SMP", 0, -1);
 	_shakeEffectState.enable();
 	checkIngredients();
-	_dialog->say(_res->_ingameTexts[AYAYAY]);
+	_dialog->say(_res->_ingameTexts[kTextAyAyAy]);
 	_alfredState.direction = ALFRED_DOWN;
-	_dialog->say(_res->_ingameTexts[NADIELOHAVISTO]);
+	_dialog->say(_res->_ingameTexts[kTextNadieLaHaVisto]);
 
 	_alfredState.direction = ALFRED_LEFT;
 	_disableAction = true;
@@ -1567,7 +1567,7 @@ void PelrockEngine::giveStoneToSlaves(int inventoryObject, HotSpot *hotspot) {
 	_sound->playSound(_room->_roomSfx[0], 0);
 	waitForSoundEnd();
 
-	_dialog->say(_res->_ingameTexts[HAYQUECELEBRARLO]);
+	_dialog->say(_res->_ingameTexts[kTextHayQueCelebrarlo]);
 
 	byte counter = _state->getFlag(FLAG_DA_PIEDRA);
 	// drinking animation and sound
@@ -1705,11 +1705,11 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 
 void PelrockEngine::pickUpStones(HotSpot *hotspot) {
 	if (_state->hasInventoryItem(91)) {
-		_dialog->say(_res->_ingameTexts[PESADEMASIADO]);
+		_dialog->say(_res->_ingameTexts[kTextPesaDemasiado]);
 		return;
 	}
 	if (_state->getFlag(FLAG_PIEDRAS_COGIDAS) >= 2) {
-		_dialog->say(_res->_ingameTexts[NINGUNATAMANHOAPROPIADO]);
+		_dialog->say(_res->_ingameTexts[kTextNingunaTamanhoApropiado]);
 		return;
 	} else {
 		addInventoryItem(91);
@@ -1719,17 +1719,17 @@ void PelrockEngine::pickUpStones(HotSpot *hotspot) {
 
 void PelrockEngine::pickUpMud(HotSpot *hotspot) {
 	if (_state->getFlag(FLAG_PIEDRAS_COGIDAS) != 2) {
-		_dialog->say(_res->_ingameTexts[PARAQUECOGERBARRO]);
+		_dialog->say(_res->_ingameTexts[kTextParaQueCogeBarro]);
 		return;
 	} else {
 		addInventoryItem(92);
 		_state->setFlag(FLAG_PIEDRAS_COGIDAS, _state->getFlag(FLAG_PIEDRAS_COGIDAS) + 1);
-		_dialog->say(_res->_ingameTexts[BUENOCOGEREUNPOCO]);
+		_dialog->say(_res->_ingameTexts[kTextBuenoCogereUnPoco]);
 	}
 }
 
 void PelrockEngine::openPyramidDoor(HotSpot *hotspot) {
-	_dialog->say(_res->_ingameTexts[ABSOLUTAMENTECERRADO]);
+	_dialog->say(_res->_ingameTexts[kTextAbsolutamenteCerrado]);
 }
 
 void PelrockEngine::usePumpkinWithPond(int inventoryObject, HotSpot *hotspot) {
@@ -1763,7 +1763,7 @@ void PelrockEngine::useWaterOnFakeStone(int inventoryObject, HotSpot *hotspot) {
 }
 
 void PelrockEngine::useWigWithPot(int inventoryObject, HotSpot *hotspot) {
-	_dialog->say(_res->_ingameTexts[NOERAAUTENTICO]);
+	_dialog->say(_res->_ingameTexts[kTextNoEraAutentico]);
 	_state->removeInventoryItem(99);
 }
 
@@ -1844,7 +1844,7 @@ void PelrockEngine::pickupPyramidMap(HotSpot *hotspot) {
 
 void PelrockEngine::openArchitectDoorFromInside(HotSpot *hotspot) {
 	if (!_room->hasSticker(104)) {
-		_dialog->say(_res->_ingameTexts[YA_ABIERTA_F]);
+		_dialog->say(_res->_ingameTexts[kTextYaAbiertaF]);
 		return;
 	}
 	_room->enableExit(0, PERSIST_TEMP);
@@ -1854,7 +1854,7 @@ void PelrockEngine::openArchitectDoorFromInside(HotSpot *hotspot) {
 
 void PelrockEngine::closeArchitectDoorFromInside(HotSpot *hotspot) {
 	if (_room->hasSticker(104)) {
-		_dialog->say(_res->_ingameTexts[YA_CERRADA_F]);
+		_dialog->say(_res->_ingameTexts[kTextYaCerradaF]);
 		return;
 	}
 	_room->disableExit(0, PERSIST_TEMP);
@@ -1867,27 +1867,27 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	case 257: // look portrait
 		_sound->playMusicTrack(25, false);
 		loadExtraScreenAndPresent(9);
-		_dialog->say(_res->_ingameTexts[QUEBUENA_ESTA]);
+		_dialog->say(_res->_ingameTexts[kTextQueBuenaEsta]);
 		_screen->markAllDirty();
 		_screen->update();
 		break;
 	case 268: // look at statue
-		_dialog->say(_res->_ingameTexts[TUCREES]);
+		_dialog->say(_res->_ingameTexts[kTextTuCrees]);
 		break;
 	case 271: // look at librarian
-		_dialog->say(_res->_ingameTexts[TRABAJARIA_MEJOR_SI_NO_ME_MOLESTARA]);
+		_dialog->say(_res->_ingameTexts[kTextTrabajariaMejorSiNoMeMolestara]);
 		break;
 	case 270:
 		_state->stateGame = COMPUTER;
 		break;
 	case 280:
-		_dialog->say(_res->_ingameTexts[NOVIO2METROS]);
+		_dialog->say(_res->_ingameTexts[kTextNovio2Metros]);
 		break;
 	case 281:
-		_dialog->say(_res->_ingameTexts[GRANIDEA]);
+		_dialog->say(_res->_ingameTexts[kTextGranIdea]);
 		break;
 	case 282:
-		_dialog->say(_res->_ingameTexts[SELORECOMIENDO]);
+		_dialog->say(_res->_ingameTexts[kTextSeLorecomiendo]);
 		break;
 	case 327:
 		_state->setFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO, true);
@@ -1907,7 +1907,7 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 	}
 
 	case 322: {
-		_dialog->say(_res->_ingameTexts[NOSETEOCURRAACERCARTE]);
+		_dialog->say(_res->_ingameTexts[kTextNoSeTeCurraCercarte]);
 		break;
 	}
 	case 375: {
@@ -2022,7 +2022,7 @@ void PelrockEngine::teleportToPrincess() {
 	_graphics->presentFrame();
 	_screen->update();
 
-	_dialog->say(_res->_ingameTexts[MAREDEDEU]);
+	_dialog->say(_res->_ingameTexts[kTextMareDeDou]);
 
 	smokeAnimation(-1, true);
 	_state->setFlag(FLAG_END_OF_GAME, true);
@@ -2037,10 +2037,10 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 	debug("Using item %d on Alfred", inventoryObject);
 	switch (inventoryObject) {
 	case 9: // Letter
-		_dialog->say(_res->_ingameTexts[CORRESPONDENCIA_AJENA]);
+		_dialog->say(_res->_ingameTexts[kTextCorrespondenciaAjena]);
 		break;
 	case 34: // How to become rich book
-		_dialog->say(_res->_ingameTexts[PERIODICOSENSACIONALISTA], 1);
+		_dialog->say(_res->_ingameTexts[kTextPeriodicoSensacionalista], 1);
 		break;
 	case 63: // Recipe
 		playAlfredSpecialAnim(1);
@@ -2049,30 +2049,30 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		_state->setCurrentRoot(17, 1, 0);
 		_state->setCurrentRoot(18, 4, 0);
 		debug("After extra screen");
-		_dialog->say(_res->_ingameTexts[QUEASCO]);
+		_dialog->say(_res->_ingameTexts[kTextQueAsco]);
 		break;
 	case 59: // Recipe book
 		if (!_state->hasInventoryItem(64)) {
 			playAlfredSpecialAnim(0);
-			_dialog->say(_res->_ingameTexts[HOJAENTREPAGINAS]);
+			_dialog->say(_res->_ingameTexts[kTextHojaEntrePaginas]);
 			addInventoryItem(64);
 		} else {
 			playAlfredSpecialAnim(0);
-			_dialog->say(_res->_ingameTexts[NOENTIENDONADA]);
+			_dialog->say(_res->_ingameTexts[kTextNoEntiendonada]);
 		}
 		break;
 	case 17: // Egyptian book
 		playAlfredSpecialAnim(0);
-		_dialog->say(_res->_ingameTexts[YASEEGIPCIO]);
+		_dialog->say(_res->_ingameTexts[kTextYaSeEgipcio]);
 		_state->setFlag(FLAG_ALFRED_SABE_EGIPCIO, true);
 		break;
 	case 24: // Encyclopedia
 		if (_state->getFlag(FLAG_RIDDLE_PRESENTED) == true) {
-			_dialog->say(_res->_ingameTexts[CAPITULOPARADOJAS]);
+			_dialog->say(_res->_ingameTexts[kTextCapituloParadojas]);
 			_state->setCurrentRoot(25, 44, 0);
 		} else {
 			playAlfredSpecialAnim(0);
-			_dialog->say(_res->_ingameTexts[COSASAPRENDIDO]);
+			_dialog->say(_res->_ingameTexts[kTextCosasAprendido]);
 			_state->setFlag(FLAG_ALFRED_INTELIGENTE, true);
 			_state->setCurrentRoot(14, 2, 0);
 		}
@@ -2081,15 +2081,15 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		playAlfredSpecialAnim(0);
 		loadExtraScreenAndPresent(5);
 		if (_state->getFlag(FLAG_ALFRED_SABE_EGIPCIO)) {
-			_dialog->say(_res->_ingameTexts[FORMULAVIAJETIEMPO]);
+			_dialog->say(_res->_ingameTexts[kTextFormulaViajeAlTiempo]);
 		} else {
-			_dialog->say(_res->_ingameTexts[QUELASTIMA_NOSEEGIPCIO]);
+			_dialog->say(_res->_ingameTexts[kTextQueLastimaNoSeeEgipcio]);
 		}
 		break;
 	case 88: { // spellbook
 		if (_room->_currentRoomNumber != 28 &&
 			(_room->_currentRoomNumber < 51 || _room->_currentRoomNumber > 54)) {
-			_dialog->say(_res->_ingameTexts[AQUI_NO_NECESITO]);
+			_dialog->say(_res->_ingameTexts[kTextAquiNoNecesito]);
 			break;
 		}
 		SpellBook spellBook(_events, _res);
@@ -2100,7 +2100,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 			_alfredState.direction = ALFRED_LEFT;
 			switch (_room->_currentRoomNumber) {
 			case 28: {
-				_dialog->say(_res->_ingameTexts[DIOSHALCON + spell->page], 1);
+				_dialog->say(_res->_ingameTexts[kTextDiosHalcon + spell->page], 1);
 				if (spell->page == 12) {
 					_graphics->clearScreen();
 					int waitFrames = 0;
@@ -2119,7 +2119,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 					_alfredState.x = 145;
 					_alfredState.y = 312;
 					setScreenAndPrepare(25, ALFRED_RIGHT);
-					_dialog->say(_res->_ingameTexts[MENUDAAVENTURA]);
+					_dialog->say(_res->_ingameTexts[kTextMenudaAventura]);
 				}
 				break;
 			}
@@ -2128,7 +2128,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 			case 53:
 			case 54: {
 				_sound->playSound(_room->_roomSfx[8], 0);
-				_dialog->say(_res->_ingameTexts[DIOSHALCON + spell->page], 1);
+				_dialog->say(_res->_ingameTexts[kTextDiosHalcon + spell->page], 1);
 				int flightIndex = _room->_currentRoomNumber - 51;
 				if (_fightSorcererAppeared && !_fightInBlockingAnim && spell->page == kFightRooms[flightIndex].spellPage) {
 					_state->setFlag(FLAG_COMO_ESTAN_LOS_DIOSES, _state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) | (1 << flightIndex));
@@ -2162,11 +2162,11 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 	}
 	case 0: // yellow book
 		playAlfredSpecialAnim(0);
-		_dialog->say(_res->_ingameTexts[CUENTOPARECIDO]);
+		_dialog->say(_res->_ingameTexts[kTextCuentoParecido]);
 		break;
 	case 101: // combination
 		playAlfredSpecialAnim(1);
-		_dialog->say(_res->_ingameTexts[PARECE_COMBINACION_CAJAFUERTE]);
+		_dialog->say(_res->_ingameTexts[kTextPareceCombinacionCajaFuerte]);
 		_state->setFlag(FLAG_CLAVE_CAJA_FUERTE, true);
 		break;
 	case 108: // glue + patches
@@ -2178,13 +2178,13 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 				_state->removeInventoryItem(109);
 				_state->removeInventoryItem(108);
 				addInventoryItem(83);
-				_dialog->say(_res->_ingameTexts[MUNECO_ARREGLADO]);
+				_dialog->say(_res->_ingameTexts[kTextMuecoArreglado]);
 				return;
 			} else if (_state->hasInventoryItem(109) == true) {
-				_dialog->say(_res->_ingameTexts[NOTENGOPARCHES]);
+				_dialog->say(_res->_ingameTexts[kTextNoTengoParches]);
 
 			} else if (_state->hasInventoryItem(108) == true) {
-				_dialog->say(_res->_ingameTexts[NOTENGOPEGAMENTO]);
+				_dialog->say(_res->_ingameTexts[kTextNoTengoPegamento]);
 			}
 		} else {
 			sayRandomIncorrectResponse();
@@ -2199,7 +2199,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 	case 97: { // pyramid map
 		playAlfredSpecialAnim(1);
 		loadExtraScreenAndPresent(11);
-		_dialog->say(_res->_ingameTexts[MEHANTOMADO_EL_PELO]);
+		_dialog->say(_res->_ingameTexts[kTextMeHanTomadoElPelo]);
 		_state->setCurrentRoot(43, 1, 0);
 		break;
 	}
@@ -2208,7 +2208,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		break;
 	}
 	case 87: {
-		_dialog->say(_res->_ingameTexts[NECESITOGASOLINA]);
+		_dialog->say(_res->_ingameTexts[kTextNecesitaGasolina]);
 		break;
 	}
 	case 95: {
@@ -2225,7 +2225,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		// Original game incorrectly checked until object 47; Reading any book.
 		if (inventoryObject >= 11 && inventoryObject <= 58) {
 			playAlfredSpecialAnim(0);
-			_dialog->say(_res->_ingameTexts[LIBRO_ABURRIDO]);
+			_dialog->say(_res->_ingameTexts[kTextLibroAburrido]);
 			return;
 		}
 		sayRandomIncorrectResponse();
@@ -2253,7 +2253,7 @@ void PelrockEngine::chooseCorrectDoor() {
 	} else if (puertaBuena == 2) {
 		doorText = _res->_derecha;
 	}
-	Common::StringArray fullTextArray = _res->_ingameTexts[PUERTAAUTENTICA_IZQUIERDA];
+	Common::StringArray fullTextArray = _res->_ingameTexts[kTextPuertaAutenticaIzquierda];
 	fullTextArray[0] = fullTextArray[0].substr(0, 45);
 	fullTextArray[0].append(doorText);
 	_dialog->say(fullTextArray);
diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index e03d6c60501..2e5a66f8a6e 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -23,423 +23,249 @@
 
 #include "common/scummsys.h"
 
-namespace Pelrock {
-
-static const uint32 stickerOffsets[137] = {
-	0x000000, 0x00005B, 0x0000B6, 0x000298, 0x00047A, 0x0023C8, 0x004316, 0x004376,
-	0x005119, 0x005EBC, 0x0083ED, 0x008529, 0x0092C4, 0x00A3AA, 0x00B490, 0x00B6A6,
-	0x00C05A, 0x00CA0E, 0x00D3D0, 0x00D46E, 0x00F036, 0x00FB8F, 0x00FC55, 0x0119D7,
-	0x013759, 0x01391F, 0x014A9D, 0x015C1B, 0x017601, 0x018FE7, 0x019048, 0x0190A9,
-	0x01910A, 0x0197F4, 0x019EDE, 0x01A7EC, 0x01B0FA, 0x01B8C4, 0x01C644, 0x01D83A,
-	0x01E104, 0x01E8C6, 0x01F45D, 0x01FBBB, 0x02011D, 0x02052F, 0x020A95, 0x020E5B,
-	0x0210B3, 0x0216E6, 0x021D5E, 0x0233A3, 0x0249E8, 0x025777, 0x026506, 0x028E2B,
-	0x02B82F, 0x02C9D7, 0x02E4CA, 0x02FFBD, 0x03234A, 0x0346D7, 0x036A83, 0x038E2F,
-	0x03B18D, 0x03D4EB, 0x03DEC9, 0x03F813, 0x04115D, 0x045303, 0x0494A9, 0x04955F,
-	0x049615, 0x0496CB, 0x0499E1, 0x049EC7, 0x04A023, 0x04A447, 0x04BA6D, 0x04BFA1,
-	0x04CE33, 0x04CF09, 0x04DB3B, 0x052885, 0x0575CF, 0x05775B, 0x057D79, 0x058397,
-	0x058969, 0x058F50, 0x05A9DB, 0x05C561, 0x05C72E, 0x05C8FB, 0x05EAC1, 0x060C87,
-	0x060D19, 0x060E62, 0x061039, 0x0613C2, 0x061764, 0x061847, 0x062535, 0x062D4B,
-	0x064F11, 0x0670D7, 0x067381, 0x0675A9, 0x0677EF, 0x067A98, 0x067DDE, 0x068115,
-	0x0684E3, 0x068A76, 0x068F30, 0x0693C8, 0x0696AD, 0x06C2C9, 0x06C84D, 0x07095D,
-	0x071854, 0x07274B, 0x073642, 0x074539, 0x075454, 0x0791DA, 0x07CF60, 0x07E4AB,
-	0x07ECED, 0x07F52F, 0x07FD71, 0x080591, 0x080B24, 0x080B84, 0x080F39, 0x0812F5,
-	0x0816B1};
-
-enum TextIndices {
-	ESTAN_CERRADOS,
-	HOY_NO_DISPONIBLES,
-	YA_ABIERTO_M,
-	YA_CERRADO_M,
-	YA_ABIERTA_F,
-	YA_CERRADA_F,
-	HELADERIA_CERRADA,
-	POBRE_PERO_NO_HE_LLEGADO_A_ESO,
-	QUEBUENA_ESTA,
-	BOTONVERDEPARASACAR_BOTONVERDEPARACANCELAR,
-	PRIMEROMETA_TARJETA,
-	NOTENGOMASDINERO,
-	MEHEVUELTOAPINCHAR,
-	QUEHASIDOESO,
-	QUIENANDAAHI,
-	YOMEVOY,
-	TIENDA_CERRADA,
-	NOSE_ENTERARIA,
-	PRIMERO_ABRIRLO,
-	QUELOSCOJA_SUPADRE,
-	PRETENDEUSTED_SOBORNARME,
-	MUYBIEN_1,
-	CUANDOMELOPIDA,
-	DEACUERDO,
-	NECESITODNI,
-	QUE_RECIBO_ACAMBIO,
-	ESPOCO,
-	ALTO,
-	NIPARAEMPEZAR,
-	PARAQUE,
-	DEPIEDRANO_DEHIELO,
-	NO_EMPECEMOS,
-	CUERPO_DANONE,
-	CABEZA_HUECA,
-	ESO_LO_SERAS_TU,
-	DEMASIADO_NO_PUEDO_PENSAR,
-	UN_POCO_RESPETO,
-	NO_THEY_MAKEYOU_FAT,
-	RELOJ_HA_CAMBIADO,
-	CORRESPONDENCIA_AJENA,
-	ANDA,
-	TUCREES,
-	NOESAMIAQUIENDEBES,
-	AQUIENENTONCES,
-	LIBROSSECRETOS,
-	VENGA_ACA,
-	TODOS,
-	EL_LIBRO_NOESTA_AQUI,
-	TENDRE_DEJAR_LIBRO,
-	TRABAJARIA_MEJOR_SI_NO_ME_MOLESTARA,
-	REGALO_LIBRO_RECETAS,
-	YSI_METIRA_MAQUINA,
-	QUITA_ESAS_MANOS,
-	QUEASCO,
-	QUESESTO_RECETA,
-	YAESTA_ABIERTO,
-	VAESTAR_POCOFUERTE,
-	CUENTOPARECIDO,
-	COSASAPRENDIDO,
-	PERIODICOSENSACIONALISTA,
-	HOJAENTREPAGINAS,
-	NOENTIENDONADA,
-	NOTENGODINERO,
-	CUESTA1000,
-	AQUITIENE,
-	MUYBIEN,
-	YASEEGIPCIO,
-	QUELASTIMA_NOSEEGIPCIO,
-	FORMULAVIAJETIEMPO,
-	PARECECERRADO,
-	NOVIO2METROS,
-	GRANIDEA,
-	SELORECOMIENDO,
-	OIGAUSTED,
-	ESAMI,
-	VENGAAHORAMISMO,
-	CUIDADOIMPRUDENTE,
-	QUEOSCUROESTAESTO,
-	MENUDAAVENTURA,
-	NECESITOGASOLINA,
-	YANOSEHACEONCOMOANTES,
-	NADIELOHAVISTO,
-	AYAYAY,
-	OIGAUSTED2,
-	LEESTOYVIGILANDO,
-	OIGA,
-	CAPITULOPARADOJAS,
-	HAYQUECELEBRARLO,
-	PESADEMASIADO,
-	NINGUNATAMANHOAPROPIADO,
-	PARAQUECOGERBARRO,
-	BUENOCOGEREUNPOCO,
-	ABSOLUTAMENTECERRADO,
-	NOSETEOCURRAACERCARTE,
-	PUERTAAUTENTICA_IZQUIERDA,
-	OHMISALVADOR,
-	VOYPORTI_PRINCESA,
-	AMISBRAZOS,
-	DIOSMIOQUEESESTO,
-	QUEPASA,
-	OLVIDECERRARTRAMPILLA,
-	NOTEPREOCUPES_VOLVERE,
-	ALACONUSTED,
-	MEMEO,
-	POR5MINUTOS,
-	TALUEGOLUCAS,
-	SISUPIERA_COMBINACION,
-	PARECE_COMBINACION_CAJAFUERTE,
-	GRANCANTIDAD_DINERO,
-	TEAPETECE_BUENRATO,
-	YLOSCONDONES,
-	QUEASCO_CASIMEMEA,
-	HECHOELPRIMO,
-	MEHANTOMADO_EL_PELO,
-	PESADO_UNRATO,
-	TRAIDOR,
-	TUTIA,
-	LATUYA,
-	GORDO,
-	FIDEO,
-	LIMPIACULO,
-	CONTUTURBANTE,
-	OSO,
-	COMADREJA,
-	CABEZON,
-	TUABUELO,
-	TUMUJER,
-	PERDEDOR,
-	SOYMEJORQUETU,
-	TRAMPOSO,
-	MALPERDEDOR,
-	PARAUNAVEZ,
-	MEJORMELARGO,
-	NOTENGOPARCHES,
-	NOTENGOPEGAMENTO,
-	MUNECO_ARREGLADO,
-	MAREDEDEU,
-	PROBARLIBRO,
-	PRACTICAR_MAS,
-	AQUI_NO_NECESITO,
-	DIOSHALCON,
-	OHGRANOSIRIS,
-	HEMEAQUI,
-	OHSOBEK,
-	OHTOTH,
-	TODOSLASCOSAS,
-	HELLEGADOPURO,
-	DIOSDELATURBULENCIA,
-	OHANUBIS,
-	HEVENIDO,
-	HELLEGADOATI,
-	OHPTHA,
-	LASPUERTASDELCIELO,
-	VAYASUENHO,
-	PARAQUE_2,
-	YESO,
-	UNPOCODESESPERADO,
-	COMBINACIONESMEJORES,
-	NOSEQUEPRETENDES_CONESO,
-	COMO,
-	MUCHOSENTIDO,
-	PORPROBAR,
-	NOLOENTIENDO,
-	PARAESONOSIRVE,
-	PRUEBAOTRACOSA,
-	SIHOMBREQUEMAS,
-	NOSEQUEPRETENDES,
-	COSASRARAS,
-	ARTE_O_LOCURA,
-	UTILIDADES,
-	TITULOJUEGO,
-	MENSAJEOTRAEPOCA,
-	NOERAAUTENTICO,
-	PRIMERINGREDIENTE,
-	DOSINGREDIENTES,
-	TRESINGREDIENTES,
-	CUATROINGREDIENTES,
-	LIBRO_ABURRIDO,
-	DEACUERDO_2,
-	GAMBERROS,
-	QUIENYO,
-	PINTA_BUENAPERSONA,
-	DEMO_FINAL,
-	DIOSHALCON_2,
-	GRANOSIRIS,
-	HEMEAQUI_ISIS,
-	OHSOBEK_2,
-	OHTOTH_2,
-	PROTEGEN_MI_CUERPO,
-	HELLEGADO_PURO,
-	DIOSDELATURBULENCIA_2,
-	OHANUBIS_2,
-	HEVENIDO_2,
-	HELLEGADO_ATI,
-	OHPTHA_2,
-	LASPUERTAS_DELCIELO,
-};
-
-// Description offsets relative to base offset 0x4715D.
-// NOTE: unused dead code — kept for reference only.
-static const uint16 description_offsets[113] = {
-	0x0000, // Object 0: Historia de la Princesa Zenna y su amante insatisfecho
-	0x0058, // Object 1: Nombre: Alfred Pelrock
-	0x00C4, // Object 2: La tipica tarjeta por la que te sacan commisiones
-	0x010E, // Object 3: Una pequeña foto de Alfred
-	0x012C, // Object 4: Un ladrillo
-	0x013B, // Object 5: 1000 pesetas
-	0x014B, // Object 6: Una alargadera con un extremo suelto
-	0x0173, // Object 7: Un amuleto egipcio con forma de escarabajo alado
-	0x01A7, // Object 8: Dice: OM OM RASKAMAMOM
-	0x01C1, // Object 9: Es una carta de la Asociacion Ra-Amoniana
-	0x020A, // Object 10: Un carnet de biblioteca
-	0x0247, // Object 11: Titulo: Canticos espirituales en formato *.zip
-	0x02B3, // Object 12: Titulo: Pasion Flagrante
-	0x0327, // Object 13: Titulo: El Valenciano en los comienzos del siglo XXI
-	0x039E, // Object 14: Titulo: El sistema inmunologico de los cefalopodos (v.I)
-	0x0412, // Object 15: Titulo: Dos y dos son 5
-	0x0493, // Object 16: Titulo: La parte creativa
-	0x057B, // Object 17: Titulo: Aprenda egipcio en 10 dias
-	0x065C, // Object 18: Titulo: Gato por liebre
-	0x06D8, // Object 19: Titulo: Enrique de Ofterdingeng
+#include "pelrock/types.h"
 
-	0x0753, // Object 20: Titulo: Hiper-cocina para solteros
-	0x07CE, // Object 21: Titulo: El camaleon humano
-	0x084E, // Object 22: Titulo: Psiquiatria Avanzada (vol. 8)
-	0x08CA, // Object 23: Titulo: Sistemas de alcantarillado en el siglo XV
-	0x0949, // Object 24: Titulo: Cartas de amor de Pol Pot a su novia
-	0x09CC, // Object 25: Titulo: El gran libro de las preposiciones
-	0x0A50, // Object 26: Titulo: Corazon, vida y muerte de un tenista
-	0x0ACC, // Object 27: Titulo: Analisis de la vida de los funcionarios
-	0x0B4D, // Object 28: Titulo: Ensayos sobre la putrefaccion
-	0x0BC9, // Object 29: Titulo: Cocinar bien es imposible
-	0x0C49, // Object 30: Titulo: 1000 formas de hacer sonar un claxon
-	0x0CC8, // Object 31: Titulo: El arte de la peluqueria
-	0x0D3B, // Object 32: Titulo: Analisis de las tramas de las mejores telecomedias
-	0x0DC7, // Object 33: Titulo: Tratado de las empanadillas
-	0x0E40, // Object 34: Titulo: Misterios de los numeros
-	0x0EBA, // Object 35: Titulo: Como vender mas
-	0x0F31, // Object 36: Titulo: Todos podemos estar de moda
-	0x0FAD, // Object 37: Titulo: La economia capitalista (Tomo VI)
-	0x102E, // Object 38: Titulo: Aventuras con mis hemorrides
-	0x10AB, // Object 39: Titulo: Automate. Tomo IV: Suicidio
-	0x1128, // Object 40: Titulo: El cienpies azul
-	0x11A1, // Object 41: Titulo: Guia sexual de la mosca
-	0x121E, // Object 42: Titulo: La Oveja. El gran misterio
-	0x1297, // Object 43: Titulo: Mi libro de cocina
-	0x1309, // Object 44: Titulo: Ariel
-	0x1377, // Object 45: Titulo: Matar cucarachas con la mirada
-	0x13F4, // Object 46: Titulo: Telepatia: Caso practico
-	0x1476, // Object 47: Titulo: Vida y obra de Paquirrin
-	0x14F4, // Object 48: Titulo: Odas para aliviar el estrenimiento
-	0x1577, // Object 49: Titulo: Mi vida en el gran mercado
-	0x15F4, // Object 50: Titulo: Oda al tocino
-	0x1669, // Object 51: Titulo: Como escribir una novela
-	0x16E0, // Object 52: Titulo: Recogiendo oro en las cloacas
-	0x175B, // Object 53: Titulo: Como comer bien. Tomo XXI. Entrantes
-	0x17DD, // Object 54: Titulo: No tengo nada mejor que hacer
-	0x185D, // Object 55: Titulo: Los Heraldos Negros
-	0x18D1, // Object 56: Titulo: La Piedra Rosetta
-	0x194A, // Object 57: Titulo: Fabulas de Ciencia Ficcion
-	0x19C3, // Object 58: Titulo: La musica amansa a las fieras
+namespace Pelrock {
 
-	0x1A35, // Object 59: Un libro de recetas magicas
-	0x1A52, // Object 60: Un bote de tomate
-	0x1A70, // Object 61: Un bote de mostaza
-	0x1A8C, // Object 62: Salsa ultra-picante
-	0x1AA3, // Object 63: Receta de las hamburguesas de McDowells
-	0x1B2D, // Object 64: Un papiro con una inscripcion jeroglifica
-	0x1B53, // Object 65: Una guitarra española
-	0x1B8D, // Object 66: Un pez disecado
-	0x1BBE, // Object 67: Un osito de peluche
-	0x1C26, // Object 68: Unos discos antiguos
-	0x1C52, // Object 69: Un cerebro de mono
-	0x1C82, // Object 70: Novelas del salvaje oeste
-	0x1CB3, // Object 71: Una paleta
-	0x1CE1, // Object 72: Caramelos de todos los sabores
-	0x1CF9, // Object 73: Una caracola
-	0x1D24, // Object 74: Un sombrero
-	0x1D3F, // Object 75: 150000 pesetas
-	0x1D5C, // Object 76: Una calabaza para meter agua dentro
-	0x1D7A, // Object 77: Henna roja para el pelo
-	0x1DA3, // Object 78: Piramides de recuerdo
-	0x1DCC, // Object 79: Un chupa-chup de higo chumbo
-	0x1DE5, // Object 80: Un amuleto egipcio
-	0x1E00, // Object 81: El pelo de una princesa egipcia
-	0x1E49, // Object 82: Un mogollon de pasta
-	0x1E6B, // Object 83: Una replica de Elvis
-	0x1EA0, // Object 84: Aunque no sean piramides se puede decir que son monumentos
-	0x1EB9, // Object 85: Un girasol
-	0x1EE0, // Object 86: Una calabaza llena de agua
-	0x1F4F, // Object 87: Una motosierra
-	0x1FBE, // Object 88: Un libro de recetas magicas
-	0x1FD8, // Object 89: Una bota usada
-	0x1FFC, // Object 90: Una autentica piedra egipcia
-	0x2053, // Object 91: Una piedra egipcia
-	0x2071, // Object 92: Un poco de barro
-	0x2097, // Object 93: Licor de arena
-	0x20AE, // Object 94: Crema para el sol
-	0x20C6, // Object 95: Banda sonora de alfred pelrock
-	0x20E7, // Object 96: Un album de pantallas
-	0x2116, // Object 97: Plano de la piramide
-	0x2158, // Object 98: Plano detallado
-	0x2179, // Object 99: Es una peluca
-	0x218F, // Object 100: Una pequeña llave
-	0x21BE, // Object 101: Un trozo de papel con unos numeritos
-	0x21F1, // Object 102: Autenticas naranjas de Nules
-	0x220E, // Object 103: Un mogollon de naranjas de Nules
-	0x2236, // Object 104: No se haga el loco: Llameme !!!
-	0x2285, // Object 105: Folletos explicativos sobre el SIDA
-	0x22BE, // Object 106: Un pin que acredita mi sabiduria
-	0x22E2, // Object 107: Una bayeta para frotar lamparas magicas
-	0x2316, // Object 108: Parches ultra-fuertes
-	0x2337, // Object 109: Pegamento que te cagas
-	0x235A, // Object 110: Una replica de Alfred pinchada
-	0x2383, // Object 111: Una cinta del Rey Elvis
-	0x23A4, // Object 112: Una caja de condone
+// Indices to in game text responses in JUEGO.EXE
+enum TextStringId {
+	kTextEstanCerrados,
+	kTextHoyNoDisponibles,
+	kTextYaAbiertoM,
+	kTextYaCerradoM,
+	kTextYaAbiertaF,
+	kTextYaCerradaF,
+	kTextHeladeraCerrada,
+	kTextPobreNoHeLlegadoAEso,
+	kTextQueBuenaEsta,
+	kTextBotonVerdeParaSacarCancelar,
+	kTextPrimeroMetaTarjeta,
+	kTextNoTengoMasDinero,
+	kTextMeHeVueltoAPinchar,
+	kTextQueHaSidoEso,
+	kTextQuienAndaAhi,
+	kTextYoMeVoy,
+	kTextTiendaCerrada,
+	kTextNoseEnteraria,
+	kTextPrimeroAbrirlo,
+	kTextQueLosCojaSupadre,
+	kTextPretendeUstedSobornarme,
+	kTextMuyBien1,
+	kTextCuandoMeLoPida,
+	kTextDeAcuerdo,
+	kTextNecesitaDni,
+	kTextQueReciboACambio,
+	kTextEsPoco,
+	kTextAlto,
+	kTextNiParaEmpezar,
+	kTextParaQue,
+	kTextDePiedraNoDeHielo,
+	kTextNoEmpecemos,
+	kTextCuerpoDanone,
+	kTextCabezaHueca,
+	kTextEsoLoSerasTu,
+	kTextDemasiadoNoPuedoPensar,
+	kTextUnPocoRespeto,
+	kTextNoTheyMakeyouFat,
+	kTextRelojHaCambiado,
+	kTextCorrespondenciaAjena,
+	kTextAnda,
+	kTextTuCrees,
+	kTextNoEsAMiAQuienDebes,
+	kTextAQuienEntonces,
+	kTextLibrosSecretos,
+	kTextVengaAca,
+	kTextTodos,
+	kTextElLibroNoEstaAqui,
+	kTextTendreDejarLibro,
+	kTextTrabajariaMejorSiNoMeMolestara,
+	kTextRegaloLibroRecetas,
+	kTextYSiMeTiraMaquina,
+	kTextQuitaEsasManos,
+	kTextQueAsco,
+	kTextQuesEstoReceta,
+	kTextYaEstaAbierto,
+	kTextVaestarPocoFuerte,
+	kTextCuentoParecido,
+	kTextCosasAprendido,
+	kTextPeriodicoSensacionalista,
+	kTextHojaEntrePaginas,
+	kTextNoEntiendonada,
+	kTextNoTengoDinero,
+	kTextCuesta1000,
+	kTextAquiTiene,
+	kTextMuyBien,
+	kTextYaSeEgipcio,
+	kTextQueLastimaNoSeeEgipcio,
+	kTextFormulaViajeAlTiempo,
+	kTextPareceCerrado,
+	kTextNovio2Metros,
+	kTextGranIdea,
+	kTextSeLorecomiendo,
+	kTextOigaUsted,
+	kTextEsAMi,
+	kTextVengaAhoraMismo,
+	kTextCuidadoImprudente,
+	kTextQueOscuroEstaEsto,
+	kTextMenudaAventura,
+	kTextNecesitaGasolina,
+	kTextYaNoSeHaceOnComoAntes,
+	kTextNadieLaHaVisto,
+	kTextAyAyAy,
+	kTextOigaUsted2,
+	kTextLeEstoyVigilando,
+	kTextOiga,
+	kTextCapituloParadojas,
+	kTextHayQueCelebrarlo,
+	kTextPesaDemasiado,
+	kTextNingunaTamanhoApropiado,
+	kTextParaQueCogeBarro,
+	kTextBuenoCogereUnPoco,
+	kTextAbsolutamenteCerrado,
+	kTextNoSeTeCurraCercarte,
+	kTextPuertaAutenticaIzquierda,
+	kTextOhMiSalvador,
+	kTextVoyPoriPrincesa,
+	kTextAMisBrazos,
+	kTextDiosMioQueEsEsto,
+	kTextQuePasa,
+	kTextOlvideCerrarTrampilla,
+	kTextNotePreocupesVolvere,
+	kTextAlaConUsted,
+	kTextMeMeo,
+	kTextPor5Minutos,
+	kTextTaLuegoLucas,
+	kTextSiSupieraCombinacion,
+	kTextPareceCombinacionCajaFuerte,
+	kTextGranCantidadDinero,
+	kTextTeApeteceBuenRato,
+	kTextYLosCondones,
+	kTextQueAscoCasiMeMea,
+	kTextHechoElPrimo,
+	kTextMeHanTomadoElPelo,
+	kTextPesadoUnRato,
+	kTextTraidor,
+	kTextTuTia,
+	kTextLaTuya,
+	kTextGordo,
+	kTextFideo,
+	kTextLimpiaculos,
+	kTextConTuTurbante,
+	kTextOso,
+	kTextComadreja,
+	kTextCabezon,
+	kTextTuAbuelo,
+	kTextTuMujer,
+	kTextPerdedor,
+	kTextSoyMejorQueTu,
+	kTextTramposo,
+	kTextMalPerdedor,
+	kTextParaUnaVez,
+	kTextMejorMeLargo,
+	kTextNoTengoParches,
+	kTextNoTengoPegamento,
+	kTextMuecoArreglado,
+	kTextMareDeDou,
+	kTextProbarLibro,
+	kTextPracticarMas,
+	kTextAquiNoNecesito,
+	kTextDiosHalcon,
+	kTextOhGranOsiris,
+	kTextHemeAqui,
+	kTextOhSobek,
+	kTextOhToth,
+	kTextTodasLasCosas,
+	kTextHeLlegadoPuro,
+	kTextDiosDeLaTurbulencia,
+	kTextOhAnubis,
+	kTextHeVenido,
+	kTextHeLlegadoATi,
+	kTextOhPtha,
+	kTextLasPuertasDelCielo,
+	kTextVayaSuenho,
+	kTextParaQue2,
+	kTextYEso,
+	kTextUnPocoDeseperado,
+	kTextCombinacionesMejores,
+	kTextNoSeQuePretendeConEso,
+	kTextComo,
+	kTextMuchoSentido,
+	kTextPorProbar,
+	kTextNoLoEntiendo,
+	kTextParaEsoNoSirve,
+	kTextPruebaOtraCosa,
+	kTextSiHombrQueEmas,
+	kTextNoSeQuePretendes,
+	kTextCosasRaras,
+	kTextArteOLocura,
+	kTextUtilidades,
+	kTextTituloJuego,
+	kTextMensajeOtraEpoca,
+	kTextNoEraAutentico,
+	kTextPrimerIngrediente,
+	kTextDosIngredientes,
+	kTextTresIngredientes,
+	kTextCuatroIngredientes,
+	kTextLibroAburrido,
+	kTextDeAcuerdo2,
+	kTextGamberros,
+	kTextQuienYo,
+	kTextPintaBuenaPersona,
+	kTextDemoFinal,
+	kTextDiosHalcon2,
+	kTextGranOsiris,
+	kTextHemeAquiIsis,
+	kTextOhSobek2,
+	kTextOhToth2,
+	kTextProtigenMiCuerpo,
+	kTextHeLlegadoPuro2,
+	kTextDiosDeLaTurbulencia2,
+	kTextOhAnubis2,
+	kTextHeVenido2,
+	kTextHeLlegadoATi2,
+	kTextOhPtha2,
+	kTextLasPuertasDelCielo2,
 };
 
-struct ExtraImages {
-	uint32 offset;
-	uint32 paletteOffset;
-	byte numChunks;
+// ALFRED.7 extra screen data (file offsets given per entry in extraScreens[])
+static const ExtraScreen extraScreens[] = {
+	{0x00007984, 0x000305A2, 8},      // 0 - Portrait above bed
+	{0x001A9EE, 0x00305A2, 8},        // 1 - Computer screen
+	{0x00647C3, 0x007B6B1, 4},        // 2 - Alfred circle
+	{0x006FBCD, 0x007B6B1, 8},        // 3 - Recipe
+	{0x007BA11, 0x0088745, 8},        // 4 - Newspaper
+	{0x009237B, 0x00B0EE7, 8},        // 5 - Tablet
+	{0x00B11F1, 0x00DE011, 8},        // 6 - Map
+	{0x00FFC47, 0x01180C9, 8},        // 7 - Girl book
+	{0x118649,  0x135A13, 8},         // 8 - Unknown
+	{0x152A88,  0x15BFC8, 8},         // 9 - Portrait
+	{0x299E0C,  0x2B3C3C, 8},         // 10 - CD
+	{0x2B3F1C,  0x2D5B18, 8},         // 11 - Pyramid map
+	{0x232B1A,  0x237C28, 8},         // 12 - CENSORED
+	{0x226358,  0x236AA8, 8},         // 13 - Background book
+	{0x2EBD12,  0x309E40, 8}          // 14 - Ending
 };
 
-static const ExtraImages extraScreens[] = {
-	{0x00, // 0 - Portrait above bed
-	 0x7984,
-	 8},
-	{0x1A9EE, // 1 - Computer screen
-	 0x305A2,
-	 8},
-	{0x647C3, // 2 - Alfred circle
-	 0x7B6B1,
-	 4},
-	{0x6FBCD, // 3 - Recipe
-	 0x7B6B1,
-	 8},
-	{0x7BA11, // 4 - Newspaper
-	 0x88745,
-	 8},
-	{0x9237B, // 5 - tablet
-	 0xB0EE7,
-	 8},
-	{0x000B11F1, // 6 - map
-	 0xDE011,
-	 8},
-	{0xFFC47, // 7 - girl book
-	 0x1180C9,
-	 8},
-	{1147849, // 8 - unknown
-	 1267955,
-	 8},
-	{0x152A88, // 9 - portrait
-	 0x15BFC8,
-	 8},
-	{2727564, // 10 - CD
-	 2833276,
-	 8},
-	{
-	2834044, // 11 - pyramid map
-	2971800,
-	8},
-	{
-	2306538, // 12 - CENSORED
-	2321064,
-	8
-	},
-	{
-	0x00226358, // 13 - Background book
-	0x00236AA8,
-	8
-	},
-	{
-	3058226, // 14 - Ending
-	3185280,
-	8
-	}
-
-};
 
-// AlfredSpecialAnimOffset: POD struct (no constructors) to avoid global-constructor overhead.
-// Fields are ordered to match natural aggregate-initializer order.
-// size == 0 means "compute as numFrames * w * h" at load time.
-struct AlfredSpecialAnimOffset {
-	int numFrames;
-	int w;
-	int h;
-	int numBudas;
-	int numAlfred;
-	uint32 offset;
-	int loops;
-	int speed;
-	uint32 size; // 0 = compute from numFrames * w * h
-};
+// Alfred.7 special animation data (file index given per entry in alfredSpecialAnims[])
+static const uint32 kAlfredAnimReadBookOffset = 559685;       // 0  - READ BOOK
+static const uint32 kAlfredAnimReadRecipeOffset = 578943;     // 1  - READ RECIPE
+static const uint32 kAlfredAnimElectricShock1Offset = 37000;  // 2  - ELECTRIC SHOCK 1
+static const uint32 kAlfredAnimElectricShock3Offset = 53106;  // 3  - ELECTRIC SHOCK 3
+static const uint32 kAlfredAnimThrowOffset = 20724;           // 4  - Throw
+static const uint32 kAlfredAnimThrowSize = 62480;             // 4  - Throw explicit size
+static const uint32 kAlfredAnimCrocodileOffset = 1556540;     // 5  - Crocodile
+static const uint32 kAlfredAnimManholeOffset = 1583702;       // 6  - Exit manhole
+static const uint32 kAlfredAnimClimbDownOffset = 1761234;     // 7  - Climbs down
+static const uint32 kAlfredAnimClimbUpOffset = 1766378;       // 8  - Climbs up
+static const uint32 kAlfredAnimExitTunnelOffset = 1770196;    // 9  - Exits tunnel
+static const uint32 kAlfredAnimWorkersOffset = 1600956;       // 10 - With workers
+static const uint32 kAlfredAnimMunheco1Offset = 2060916;      // 11 - Doll 1
+static const uint32 kAlfredAnimMunheco2Offset = 2115632;      // 12 - Doll 2
+static const uint32 kAlfredAnimMunheco3Offset = 1526432;      // 13 - Doll 3
+static const uint32 kAlfredAnimDescamisaOffset = 2972568;     // 14 - Descamisa
+static const uint32 kAlfredAnimSecretPassageOffset = 1749464; // 15 - Secret passage
+static const uint32 kAlfredAnimInBedOffset = 3038454;         // 16 - Alfred in bed
 
 } // End of namespace Pelrock
 #endif
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 9cda936bb97..ee753999ca2 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -412,7 +412,7 @@ bool PelrockEngine::renderScene(int overlayMode) {
 			if (_state->getFlag(FLAG_PUTA_250_VECES) == true) {
 				_numPressedX++;
 				if (_numPressedX == 250) {
-					_dialog->say(_res->_ingameTexts[YLOSCONDONES]);
+					_dialog->say(_res->_ingameTexts[kTextYLosCondones]);
 				}
 			}
 		}
@@ -458,7 +458,7 @@ void PelrockEngine::maybeHaveDogPee() {
 		dog->animData[0].nframes = 9;
 		dog->animData[0].curFrame = 0;
 
-		_dialog->say(_res->_ingameTexts[QUEASCO_CASIMEMEA]);
+		_dialog->say(_res->_ingameTexts[kTextQueAscoCasiMeMea]);
 		_currentHotspot = nullptr; // Clear so arrival direction isn't overridden by dog hotspot
 		walkTo(152, _alfredState.y);
 		_isDogPeeing = false;
@@ -474,7 +474,7 @@ void PelrockEngine::maybePlayPostIntro() {
 		_res->loadAlfredSpecialAnim(16, false);
 		_alfredState.animState = ALFRED_SPECIAL_ANIM;
 		_dialog->_disableClickToAdvance = true;
-		_dialog->say(_res->_ingameTexts[VAYASUENHO]);
+		_dialog->say(_res->_ingameTexts[kTextVayaSuenho]);
 		_dialog->_disableClickToAdvance = false;
 		waitForSpecialAnimation();
 		_graphics->fadeToBlack(20);
@@ -484,7 +484,7 @@ void PelrockEngine::maybePlayPostIntro() {
 		_alfredState.x = kAlfredInitialPosX;
 		_alfredState.y = kAlfredInitialPosY;
 		// setScreenAndPrepare(0, ALFRED_DOWN);
-		_dialog->say(_res->_ingameTexts[MENSAJEOTRAEPOCA]);
+		_dialog->say(_res->_ingameTexts[kTextMensajeOtraEpoca]);
 	}
 }
 
@@ -1856,9 +1856,9 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 	case 15:
 		if (_state->getFlag(FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ)) {
 			_state->setFlag(FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ, false);
-			_dialog->say(_res->_ingameTexts[GAMBERROS]);
-			_dialog->say(_res->_ingameTexts[QUIENYO]);
-			_dialog->say(_res->_ingameTexts[PINTA_BUENAPERSONA]);
+			_dialog->say(_res->_ingameTexts[kTextGamberros]);
+			_dialog->say(_res->_ingameTexts[kTextQuienYo]);
+			_dialog->say(_res->_ingameTexts[kTextPintaBuenaPersona]);
 			break;
 		}
 	case 19: {
@@ -1870,7 +1870,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		_room->findSpriteByIndex(1)->numAnims = 1;
 
 		if(_state->hasInventoryItem(88) && _state->getFlag(FLAG_PIGEON_DEAD) == false) {
-			_dialog->say(_res->_ingameTexts[PROBARLIBRO]);
+			_dialog->say(_res->_ingameTexts[kTextProbarLibro]);
 			playAlfredSpecialAnim(0);
 			Sprite *pigeons = _room->findSpriteByIndex(1);
 			pigeons->numAnims = 4;
@@ -1883,7 +1883,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 				_screen->update();
 				g_system->delayMillis(10);
 			}
-			_dialog->say(_res->_ingameTexts[PRACTICAR_MAS]);
+			_dialog->say(_res->_ingameTexts[kTextPracticarMas]);
 			_state->setFlag(FLAG_PIGEON_DEAD, true);
 		}
 		break;
@@ -1944,9 +1944,9 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			if (_state->getFlag(FLAG_SE_HA_PUESTO_EL_MUNECO) == true) {
 				_state->setCurrentRoot(26, 2, 1);
 			} else {
-				_dialog->say(_res->_ingameTexts[OIGAUSTED], 1);
-				_dialog->say(_res->_ingameTexts[ESAMI]);
-				_dialog->say(_res->_ingameTexts[VENGAAHORAMISMO], 1);
+				_dialog->say(_res->_ingameTexts[kTextOigaUsted], 1);
+				_dialog->say(_res->_ingameTexts[kTextEsAMi]);
+				_dialog->say(_res->_ingameTexts[kTextVengaAhoraMismo], 1);
 				_state->setCurrentRoot(26, 1, 1);
 				walkAndAction(_room->findHotspotByExtra(421), TALK);
 			}
@@ -1958,7 +1958,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			_dialog->_goodbyeDisabled = true;
 			_state->setFlag(FLAG_ROBA_PELO_PRINCESA, false);
 			_room->enableSprite(0, 200, PERSIST_TEMP);
-			_dialog->say(_res->_ingameTexts[OIGAUSTED]);
+			_dialog->say(_res->_ingameTexts[kTextOigaUsted]);
 			walkAndAction(_room->findHotspotByExtra(0), TALK);
 		}
 		break;
@@ -1998,16 +1998,16 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 
 		if (_state->getFlag(FLAG_END_OF_GAME) == true) {
 
-			_dialog->say(_res->_ingameTexts[OHMISALVADOR]);
-			_dialog->say(_res->_ingameTexts[VOYPORTI_PRINCESA]);
+			_dialog->say(_res->_ingameTexts[kTextOhMiSalvador]);
+			_dialog->say(_res->_ingameTexts[kTextVoyPoriPrincesa]);
 			_state->setCurrentRoot(48, 1, 0);
 			walkAndAction(_room->findHotspotByExtra(634), TALK);
 
 			endingScene();
 
 		} else if (_state->getFlag(FLAG_CORRECT_DOOR_CHOSEN) == true) {
-			_dialog->say(_res->_ingameTexts[OHMISALVADOR]);
-			_dialog->say(_res->_ingameTexts[VOYPORTI_PRINCESA]);
+			_dialog->say(_res->_ingameTexts[kTextOhMiSalvador]);
+			_dialog->say(_res->_ingameTexts[kTextVoyPoriPrincesa]);
 			_state->setFlag(FLAG_TRAMPILLA_ABIERTA, true);
 			walkAndAction(_room->findHotspotByExtra(634), TALK);
 			_room->addSticker(134);
@@ -2028,8 +2028,8 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			setScreenAndPrepare(49, ALFRED_UP);
 
 		} else {
-			_dialog->say(_res->_ingameTexts[OHMISALVADOR]);
-			_dialog->say(_res->_ingameTexts[VOYPORTI_PRINCESA]);
+			_dialog->say(_res->_ingameTexts[kTextOhMiSalvador]);
+			_dialog->say(_res->_ingameTexts[kTextVoyPoriPrincesa]);
 			// _state->setCurrentRoot(48, 0, 1);
 			HotSpot *fatMummy = nullptr;
 			for (uint i = 0; i < _room->_currentRoomHotspots.size(); i++) {
@@ -2110,7 +2110,7 @@ void PelrockEngine::pyramidCollapse() {
 	}
 	_room->findSpriteByIndex(2)->zOrder = 255;
 
-	_dialog->say(_res->_ingameTexts[YANOSEHACEONCOMOANTES]);
+	_dialog->say(_res->_ingameTexts[kTextYaNoSeHaceOnComoAntes]);
 	npc = _room->findSpriteByIndex(0);
 	if (npc)
 		npc->zOrder = 254;
@@ -2163,7 +2163,7 @@ void PelrockEngine::pyramidCollapse() {
 	if (npc)
 		npc->animData[npc->curAnimIndex].movementFlags = 0;
 
-	_dialog->say(_res->_ingameTexts[POR5MINUTOS], 0);
+	_dialog->say(_res->_ingameTexts[kTextPor5Minutos], 0);
 
 	_room->disableExit(36, 0);
 
@@ -2180,7 +2180,7 @@ void PelrockEngine::pyramidCollapse() {
 	pyramidHotspot->index = 7;
 	_room->disableHotspot(21, pyramidHotspot, PERSIST_BOTH);
 
-	_dialog->say(_res->_ingameTexts[TALUEGOLUCAS]);
+	_dialog->say(_res->_ingameTexts[kTextTaLuegoLucas]);
 
 	// Walk Alfred to right edge exit -> room 21
 	walkLoop(603, 212, ALFRED_RIGHT);
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 825a63a6318..b9b88543c0b 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -56,25 +56,6 @@ static const uint32 kCreditsSize = 2540;
 static const uint32 kComputerTextOffset = 0x0004901A;
 static const uint32 kComputerTextSize = 490;
 
-// Alfred special animation data (file index given per entry in alfredSpecialAnims[])
-static const uint32 kAlfredAnimReadBookOffset = 559685;       // 0  - READ BOOK
-static const uint32 kAlfredAnimReadRecipeOffset = 578943;     // 1  - READ RECIPE
-static const uint32 kAlfredAnimElectricShock1Offset = 37000;  // 2  - ELECTRIC SHOCK 1
-static const uint32 kAlfredAnimElectricShock3Offset = 53106;  // 3  - ELECTRIC SHOCK 3
-static const uint32 kAlfredAnimThrowOffset = 20724;           // 4  - Throw
-static const uint32 kAlfredAnimThrowSize = 62480;             // 4  - Throw explicit size
-static const uint32 kAlfredAnimCrocodileOffset = 1556540;     // 5  - Crocodile
-static const uint32 kAlfredAnimManholeOffset = 1583702;       // 6  - Exit manhole
-static const uint32 kAlfredAnimClimbDownOffset = 1761234;     // 7  - Climbs down
-static const uint32 kAlfredAnimClimbUpOffset = 1766378;       // 8  - Climbs up
-static const uint32 kAlfredAnimExitTunnelOffset = 1770196;    // 9  - Exits tunnel
-static const uint32 kAlfredAnimWorkersOffset = 1600956;       // 10 - With workers
-static const uint32 kAlfredAnimMunheco1Offset = 2060916;      // 11 - Doll 1
-static const uint32 kAlfredAnimMunheco2Offset = 2115632;      // 12 - Doll 2
-static const uint32 kAlfredAnimMunheco3Offset = 1526432;      // 13 - Doll 3
-static const uint32 kAlfredAnimDescamisaOffset = 2972568;     // 14 - Descamisa
-static const uint32 kAlfredAnimSecretPassageOffset = 1749464; // 15 - Secret passage
-static const uint32 kAlfredAnimInBedOffset = 3038454;         // 16 - Alfred in bed
 
 ResourceManager::ResourceManager(/* args */) {
 	_inventoryIcons = new InventoryObject[69];
@@ -472,7 +453,7 @@ void ResourceManager::getExtraScreen(int screenIndex, byte *screenBuf, byte *pal
 	if (!alfred7.open("ALFRED.7")) {
 		error("Couldnt find file ALFRED.7");
 	}
-	ExtraImages screen = extraScreens[screenIndex];
+	ExtraScreen screen = extraScreens[screenIndex];
 	mergeRleBlocks(&alfred7, screen.offset, 8, screenBuf);
 	alfred7.seek(screen.paletteOffset, SEEK_SET);
 	alfred7.read(palette, 768);
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 5c9efd3abf9..9ff5afc04ad 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -49,6 +49,27 @@ static const int unpickableHotspotExtras[] = {
 	91, // mud and stone should only be picked under certain conditions!
 	92};
 
+static const uint32 stickerOffsets[137] = {
+	0x000000, 0x00005B, 0x0000B6, 0x000298, 0x00047A, 0x0023C8, 0x004316, 0x004376,
+	0x005119, 0x005EBC, 0x0083ED, 0x008529, 0x0092C4, 0x00A3AA, 0x00B490, 0x00B6A6,
+	0x00C05A, 0x00CA0E, 0x00D3D0, 0x00D46E, 0x00F036, 0x00FB8F, 0x00FC55, 0x0119D7,
+	0x013759, 0x01391F, 0x014A9D, 0x015C1B, 0x017601, 0x018FE7, 0x019048, 0x0190A9,
+	0x01910A, 0x0197F4, 0x019EDE, 0x01A7EC, 0x01B0FA, 0x01B8C4, 0x01C644, 0x01D83A,
+	0x01E104, 0x01E8C6, 0x01F45D, 0x01FBBB, 0x02011D, 0x02052F, 0x020A95, 0x020E5B,
+	0x0210B3, 0x0216E6, 0x021D5E, 0x0233A3, 0x0249E8, 0x025777, 0x026506, 0x028E2B,
+	0x02B82F, 0x02C9D7, 0x02E4CA, 0x02FFBD, 0x03234A, 0x0346D7, 0x036A83, 0x038E2F,
+	0x03B18D, 0x03D4EB, 0x03DEC9, 0x03F813, 0x04115D, 0x045303, 0x0494A9, 0x04955F,
+	0x049615, 0x0496CB, 0x0499E1, 0x049EC7, 0x04A023, 0x04A447, 0x04BA6D, 0x04BFA1,
+	0x04CE33, 0x04CF09, 0x04DB3B, 0x052885, 0x0575CF, 0x05775B, 0x057D79, 0x058397,
+	0x058969, 0x058F50, 0x05A9DB, 0x05C561, 0x05C72E, 0x05C8FB, 0x05EAC1, 0x060C87,
+	0x060D19, 0x060E62, 0x061039, 0x0613C2, 0x061764, 0x061847, 0x062535, 0x062D4B,
+	0x064F11, 0x0670D7, 0x067381, 0x0675A9, 0x0677EF, 0x067A98, 0x067DDE, 0x068115,
+	0x0684E3, 0x068A76, 0x068F30, 0x0693C8, 0x0696AD, 0x06C2C9, 0x06C84D, 0x07095D,
+	0x071854, 0x07274B, 0x073642, 0x074539, 0x075454, 0x0791DA, 0x07CF60, 0x07E4AB,
+	0x07ECED, 0x07F52F, 0x07FD71, 0x080591, 0x080B24, 0x080B84, 0x080F39, 0x0812F5,
+	0x0816B1
+};
+
 #define PERSIST_TEMP 1
 #define PERSIST_PERM 2
 #define PERSIST_BOTH 3
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 1780ec71d2f..966f07647c3 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -234,6 +234,27 @@ struct PathContext {
 	uint16 compressed_length;
 };
 
+struct ExtraScreen {
+	uint32 offset;
+	uint32 paletteOffset;
+	byte numChunks;
+};
+
+/**
+ * Struct for special or one-off animations that replace Alfred's regular animations.
+ */
+struct AlfredSpecialAnimOffset {
+	int numFrames;
+	int w;
+	int h;
+	int numBudas;
+	int numAlfred;
+	uint32 offset;
+	int loops;
+	int speed;
+	uint32 size; // 0 = compute from numFrames * w * h
+};
+
 /**
  * Each Anim has its own speed, loopCount and movement!
  */


Commit: cb817060195a44e3e7baabdbaff1b95fd439fb21
    https://github.com/scummvm/scummvm/commit/cb817060195a44e3e7baabdbaff1b95fd439fb21
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:42+02:00

Commit Message:
PELROCK: Fix warnings

Changed paths:
    engines/pelrock/computer.cpp
    engines/pelrock/computer.h
    engines/pelrock/console.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/computer.cpp b/engines/pelrock/computer.cpp
index 95a7bb81404..1b97e0eb3a1 100644
--- a/engines/pelrock/computer.cpp
+++ b/engines/pelrock/computer.cpp
@@ -163,7 +163,7 @@ void Computer::drawScreen() {
 	case STATE_MAIN_MENU: {
 		int textY = 97;
 		int textX = 225;
-		for (int i = 0; _computerText[0].size() > i; i++) {
+		for (uint i = 0; _computerText[0].size() > i; i++) {
 			Common::String line = _computerText[0][i];
 			g_engine->_graphics->drawColoredText(g_engine->_screen, line, textX, textY + i * _lineHeight, 200, defaultColor, g_engine->_smallFont);
 		}
@@ -172,7 +172,7 @@ void Computer::drawScreen() {
 
 	case STATE_SEARCH_BY_TITLE:
 	case STATE_SEARCH_BY_AUTHOR:
-		for (int i = 0; _computerText[1].size() > i; i++) {
+		for (uint i = 0; _computerText[1].size() > i; i++) {
 			Common::String line = _computerText[1][i];
 			g_engine->_graphics->drawColoredText(g_engine->_screen, line, 172, 258 + i * _lineHeight, 200, defaultColor, g_engine->_smallFont);
 		}
@@ -201,7 +201,7 @@ void Computer::drawScreen() {
 		Common::String titleLine = _computerText[3][0];
 		int titlePlaceholderIndex = titleLine.findFirstOf("XXXX");
 
-		int titleIndex = 0;
+		uint titleIndex = 0;
 		while (titleIndex < book.title.size()) {
 			Common::String thisLine;
 			if (titleIndex == 0) {
@@ -217,7 +217,7 @@ void Computer::drawScreen() {
 		// Author
 		Common::String authorLine = _computerText[4][0];
 		int authorPlaceholderIndex = authorLine.findFirstOf("XXXX");
-		int authorIndex = 0;
+		uint authorIndex = 0;
 
 		while (authorIndex < book.author.size()) {
 			Common::String thisLine;
@@ -338,7 +338,7 @@ void Computer::memorizeBook(int bookIndex) {
 void Computer::performSearch() {
 	_searchResults.clear();
 
-	for (int i = 0; i < _libraryBooks.size(); i++) {
+	for (uint i = 0; i < _libraryBooks.size(); i++) {
 		Common::String searchField = _searchType == 0 ? _libraryBooks[i].title[0] : _libraryBooks[i].author[0];
 
 		// Check if first letter matches (case-insensitive)
diff --git a/engines/pelrock/computer.h b/engines/pelrock/computer.h
index 403a620717d..f2905a1426f 100644
--- a/engines/pelrock/computer.h
+++ b/engines/pelrock/computer.h
@@ -78,7 +78,7 @@ private:
 	int _searchType = 0; // 0 = title, 1 = author
 	Common::Array<int> _searchResults;
 	Common::Array<LibraryBook> _libraryBooks;
-	int _currentResult;
+	uint _currentResult;
 	int _memorizedBookIndex; // Index of book that was memorized (-1 if none)
 	int _lineHeight;
 	Common::String _titleMsg;
diff --git a/engines/pelrock/console.cpp b/engines/pelrock/console.cpp
index 0a5f6f25f3e..b8240c13e6c 100644
--- a/engines/pelrock/console.cpp
+++ b/engines/pelrock/console.cpp
@@ -45,7 +45,7 @@ bool PelrockConsole::cmdRemoveSticker(int argc, const char **argv) {
 		return true;
 	}
 	int stickerId = atoi(argv[1]);
-	for(int i = 0; i < g_engine->_state->stickersPerRoom[g_engine->_room->_currentRoomNumber].size(); i++) {
+	for(uint i = 0; i < g_engine->_state->stickersPerRoom[g_engine->_room->_currentRoomNumber].size(); i++) {
 		if (g_engine->_state->stickersPerRoom[g_engine->_room->_currentRoomNumber][i].stickerIndex == stickerId) {
 			g_engine->_state->stickersPerRoom[g_engine->_room->_currentRoomNumber].remove_at(i);
 			debugPrintf("Removed sticker %d from room %d\n", stickerId, g_engine->_room->_currentRoomNumber);
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index dd810023823..9a54e15acf3 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -1130,9 +1130,9 @@ bool isEndMarker(byte char_byte) {
 	return char_byte == kCtrlEndText || char_byte == kCtrlEndConversation || char_byte == kCtrlActionAndEnd || char_byte == kCtrlGoBack;
 }
 
-int calculateWordLength(Common::String text, int startPos, bool &isEnd) {
+int calculateWordLength(Common::String text, uint startPos, bool &isEnd) {
 	int wordLength = 0;
-	int pos = startPos;
+	uint pos = startPos;
 	while (pos < text.size()) {
 		char char_byte = text[pos];
 		if (char_byte == kCtrlSpace || isEndMarker(char_byte)) {
@@ -1169,7 +1169,7 @@ Common::Array<Common::Array<Common::String>> DialogManager::wordWrap(Common::Str
 	Common::Array<Common::String> currentPage;
 	Common::Array<Common::String> currentLine;
 	int charsRemaining = kMaxCharsPerLine;
-	int position = 0;
+	uint position = 0;
 	int currentLineNum = 0;
 	while (position < text.size()) {
 		bool isEnd = false;
@@ -1251,7 +1251,7 @@ Common::Array<Common::StringArray> DialogManager::wordWrap(Common::StringArray t
 		Common::String thisLine = texts[i];
 		Common::Array<Common::Array<Common::String>> wrapped = wordWrap(thisLine);
 		for (uint j = 0; j < wrapped.size(); j++) {
-			for (int k = 0; k < wrapped[j].size(); k++) {
+			for (uint k = 0; k < wrapped[j].size(); k++) {
 				if (currentLineNum < kMaxLines) {
 					currentPage.push_back(wrapped[j][k]);
 					currentLineNum++;
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index ee753999ca2..1bfbdf9bc7b 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -271,7 +271,7 @@ void PelrockEngine::playSoundIfNeeded() {
 	int ambientSlotOffset = _sound->tickAmbientSound(_chrono->getFrameCount());
 	if (ambientSlotOffset >= 0) {
 		// Convert to room sound index: slots 12-15 = room indices 4-7
-		int roomSoundIndex = kAmbientSoundSlotBase + ambientSlotOffset;
+		byte roomSoundIndex = kAmbientSoundSlotBase + ambientSlotOffset;
 		if (roomSoundIndex < _room->_roomSfx.size()) {
 			byte soundFileIndex = _room->_roomSfx[roomSoundIndex];
 			if (soundFileIndex != 0) { // 0 = NO_SOUND.SMP (disabled)
@@ -1660,7 +1660,7 @@ VerbIcon PelrockEngine::isActionUnder(int x, int y) {
 	}*/
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
 	int loopEnd = _state->selectedInventoryItem != -1 ? actions.size() + 1 : actions.size();
-	for (int i = 0; i < loopEnd; i++) {
+	for (uint i = 0; i < loopEnd; i++) {
 		Common::Point p = getPositionInBalloonForIndex(i, _actionPopupState.x, _actionPopupState.y);
 		Common::Rect actionRect = Common::Rect(p.x, p.y, p.x + kVerbIconWidth, p.y + kVerbIconHeight);
 		if (i == actions.size()) {
@@ -1727,7 +1727,7 @@ void PelrockEngine::checkMouseHover() {
 
 	if (hotspotIndex != -1) {
 		hotspotDetected = true;
-		if (hotspotIndex < _room->_currentRoomDescriptions.size())
+		if (hotspotIndex < (int)_room->_currentRoomDescriptions.size())
 			_hoveredMapLocation = _room->_currentRoomDescriptions[hotspotIndex].text;
 	} else if (!alfredDetected) {
 		_hoveredMapLocation = "";
@@ -1859,8 +1859,8 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			_dialog->say(_res->_ingameTexts[kTextGamberros]);
 			_dialog->say(_res->_ingameTexts[kTextQuienYo]);
 			_dialog->say(_res->_ingameTexts[kTextPintaBuenaPersona]);
-			break;
 		}
+		break;
 	case 19: {
 		Sprite *dog = _room->findSpriteByIndex(2);
 		dog->animData[0].nframes = 9;
@@ -1901,8 +1901,8 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			_room->addStickerToRoom(38, 123, PERSIST_TEMP);
 			_alfredState.x = x;
 			_alfredState.y = y;
-			break;
 		}
+		break;
 	}
 	case 32: {
 		if (_room->_prevRoomNumber == 31) {
@@ -1914,6 +1914,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			_alfredState.x = x;
 			_alfredState.y = y;
 		}
+		break;
 	}
 	case 27: {
 		if (_room->_prevRoomNumber == 33) {
@@ -1935,8 +1936,8 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			_res->getPaletteForRoom28(palette);
 			g_system->getPaletteManager()->setPalette(palette, 0, 256);
 			Common::copy(palette, palette + 768, _room->_roomPalette);
-			break;
 		}
+		break;
 	}
 	case 26: {
 		if (_state->getFlag(FLAG_A_LA_CARCEL) == true) {
@@ -2325,7 +2326,7 @@ void PelrockEngine::credits() {
 	Common::Array<Common::StringArray> creditTexts = _res->getCredits();
 	Common::Array<int> creditsSpeakerId;
 	// Preprocess credit texts: extract speaker IDs and apply word wrapping
-	for(int i = 0; i < creditTexts.size(); i++) {
+	for(uint i = 0; i < creditTexts.size(); i++) {
 		byte speakerId;
 		_dialog->processColorAndTrim(creditTexts[i], speakerId);
 		creditsSpeakerId.push_back(speakerId);
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index b9b88543c0b..55b06c3c7a4 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -436,13 +436,6 @@ Common::Array<Common::StringArray> ResourceManager::loadComputerText() {
 	exe.read(computerTextBuf, bufSize);
 	Common::Array<Common::StringArray> computerTexts = processTextData(computerTextBuf, bufSize);
 
-	for (int i = 0; i < computerTexts.size(); i++) {
-		debug("Computer text %d:", i);
-		Common::StringArray &lines = computerTexts[i];
-		for (int j = 0; j < lines.size(); j++) {
-			debug("  Line %d: '%s'", j, lines[j].c_str());
-		}
-	}
 	delete[] computerTextBuf;
 
 	exe.close();
@@ -480,7 +473,7 @@ Common::Array<Common::StringArray> ResourceManager::getCredits() {
 }
 
 Common::Array<Common::StringArray> ResourceManager::processTextData(byte *data, size_t size, bool decode) {
-	int pos = 0;
+	uint pos = 0;
 	Common::String desc = "";
 	Common::StringArray lines;
 	Common::Array<Common::StringArray> texts;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 966f07647c3..913b747b970 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -146,8 +146,8 @@ struct AlfredSpecialAnim {
 	int curLoop = 0;
 	uint32 size = 0;
 	int speed = 2;
-	AlfredSpecialAnim(int nF, int width, int height, int nBudas, uint32 off, int loopCount, uint32 sz, int spd = 2)
-		: numFrames(nF), w(width), h(height), loopCount(loopCount), size(sz), speed(spd) {
+	AlfredSpecialAnim(int nF, int width, int height, int nBudas, uint32 off, int lCount, uint32 sz, int spd = 2)
+		: numFrames(nF), w(width), h(height), loopCount(lCount), size(sz), speed(spd) {
 		stride = w * h;
 	}
 
@@ -485,7 +485,7 @@ struct RoomPasserBys {
 	byte currentAnimIndex = 0;
 	byte numAnims = 0;
 	bool latch = false;
-	RoomPasserBys(byte roomNum, byte numAnims) : roomNumber(roomNum), numAnims(numAnims) {}
+	RoomPasserBys(byte roomNum, byte nAnims) : roomNumber(roomNum), numAnims(nAnims) {}
 };
 
 /**
@@ -500,7 +500,7 @@ struct ChoiceOption {
 	bool shouldDisableOnSelect = false;
 	bool hasConversationEndMarker = false;
 	bool isTerminator = false;
-	int charOffset = 0;
+	uint charOffset = 0;
 
 	ChoiceOption() : choiceIndex(-1), dataOffset(0) {}
 };


Commit: 13330521f9d238f322a8539e06983c9afb3a5b79
    https://github.com/scummvm/scummvm/commit/13330521f9d238f322a8539e06983c9afb3a5b79
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:42+02:00

Commit Message:
PELROCK: Move inventory item sounds lookup table into implementation

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index c1c962c3cc7..5603fb84161 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -34,6 +34,123 @@
 
 namespace Pelrock {
 
+static const char *inventorySounds[113] = {
+
+	"HOJASZZZ.SMP", // 0
+	"11ZZZZZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"GLASS1ZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"ELEC3ZZZ.SMP",
+	"REMATERL.SMP",
+	"81ZZZZZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"SSSHTZZZ.SMP", // 10
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP", // 20
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP", // 30
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP", // 40
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP", // 50
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"BOTEZZZZ.SMP", // 60
+	"BOTEZZZZ.SMP",
+	"BOTEZZZZ.SMP",
+	"BELCHZZZ.SMP",
+	"BEAMZZZZ.SMP",
+	"ELVIS1ZZ.SMP",
+	"CAT_1ZZZ.SMP",
+	"BOOOOOIZ.SMP",
+	"DISCOSZZ.SMP",
+	"MONORLZZ.SMP",
+	"11ZZZZZZ.SMP", // 70
+	"11ZZZZZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"CARACOLA.SMP",
+	"11ZZZZZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"WATER_2Z.SMP",
+	"11ZZZZZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"EEEEKZZZ.SMP",
+	"REMATERL.SMP", // 80 - Rematerialize
+	"11ZZZZZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"ELVIS1ZZ.SMP",
+	"RIMSHOTZ.SMP",
+	"11ZZZZZZ.SMP",
+	"WATER_2Z.SMP",
+	"MOTOSZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"TWANGZZZ.SMP",
+	"11ZZZZZZ.SMP", // 90
+	"QUAKE2ZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"SORBOZZZ.SMP",
+	"BOTEZZZZ.SMP",
+	"ELVIS1ZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"HOJASZZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"LLAVESZZ.SMP", // 100
+	"HOJASZZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"EVLLAUGH.SMP",
+	"11ZZZZZZ.SMP",
+	"BURROLZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"TWANGZZZ.SMP",
+	"11ZZZZZZ.SMP",
+	"TWANGZZZ.SMP", // 110
+	"ELVIS1ZZ.SMP",
+	"SEX3ZZZZ.SMP"
+};
+
 // ALFRED.7 — alternate settings palette
 static const uint32 kSettingsPaletteOffset = 0x2884C2;
 
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index a694caf5712..bd30ef1b79d 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -91,123 +91,6 @@ static const int kCreditsOrder[34] = {
 	17  // DDM
 };
 
-static const char *inventorySounds[113] = {
-
-	"HOJASZZZ.SMP", // 0
-	"11ZZZZZZ.SMP",
-	"11ZZZZZZ.SMP",
-	"11ZZZZZZ.SMP",
-	"GLASS1ZZ.SMP",
-	"11ZZZZZZ.SMP",
-	"ELEC3ZZZ.SMP",
-	"REMATERL.SMP",
-	"81ZZZZZZ.SMP",
-	"11ZZZZZZ.SMP",
-	"SSSHTZZZ.SMP", // 10
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP", // 20
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP", // 30
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP", // 40
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP", // 50
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"BOTEZZZZ.SMP", // 60
-	"BOTEZZZZ.SMP",
-	"BOTEZZZZ.SMP",
-	"BELCHZZZ.SMP",
-	"BEAMZZZZ.SMP",
-	"ELVIS1ZZ.SMP",
-	"CAT_1ZZZ.SMP",
-	"BOOOOOIZ.SMP",
-	"DISCOSZZ.SMP",
-	"MONORLZZ.SMP",
-	"11ZZZZZZ.SMP", // 70
-	"11ZZZZZZ.SMP",
-	"11ZZZZZZ.SMP",
-	"CARACOLA.SMP",
-	"11ZZZZZZ.SMP",
-	"11ZZZZZZ.SMP",
-	"WATER_2Z.SMP",
-	"11ZZZZZZ.SMP",
-	"11ZZZZZZ.SMP",
-	"EEEEKZZZ.SMP",
-	"REMATERL.SMP", // 80 - Rematerialize
-	"11ZZZZZZ.SMP",
-	"11ZZZZZZ.SMP",
-	"ELVIS1ZZ.SMP",
-	"RIMSHOTZ.SMP",
-	"11ZZZZZZ.SMP",
-	"WATER_2Z.SMP",
-	"MOTOSZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"TWANGZZZ.SMP",
-	"11ZZZZZZ.SMP", // 90
-	"QUAKE2ZZ.SMP",
-	"11ZZZZZZ.SMP",
-	"SORBOZZZ.SMP",
-	"BOTEZZZZ.SMP",
-	"ELVIS1ZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"HOJASZZZ.SMP",
-	"11ZZZZZZ.SMP",
-	"LLAVESZZ.SMP", // 100
-	"HOJASZZZ.SMP",
-	"11ZZZZZZ.SMP",
-	"11ZZZZZZ.SMP",
-	"EVLLAUGH.SMP",
-	"11ZZZZZZ.SMP",
-	"BURROLZZ.SMP",
-	"11ZZZZZZ.SMP",
-	"TWANGZZZ.SMP",
-	"11ZZZZZZ.SMP",
-	"TWANGZZZ.SMP", // 110
-	"ELVIS1ZZ.SMP",
-	"SEX3ZZZZ.SMP"
-};
-
 class MenuManager {
 
 	enum MenuState {


Commit: 97c26c5ec3a1a724ebcf8b1a91fadd07653af7ab
    https://github.com/scummvm/scummvm/commit/97c26c5ec3a1a724ebcf8b1a91fadd07653af7ab
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:43+02:00

Commit Message:
PELROCK: Rename size constants for custom fonts

Changed paths:
    engines/pelrock/fonts/large_font.cpp
    engines/pelrock/fonts/large_font.h
    engines/pelrock/fonts/small_font.cpp
    engines/pelrock/fonts/small_font.h


diff --git a/engines/pelrock/fonts/large_font.cpp b/engines/pelrock/fonts/large_font.cpp
index 98048e39d80..5b534af3d27 100644
--- a/engines/pelrock/fonts/large_font.cpp
+++ b/engines/pelrock/fonts/large_font.cpp
@@ -111,7 +111,7 @@ bool LargeFont::load(const Common::String &filename) {
 }
 
 int LargeFont::getCharWidth(uint32 chr) const {
-	return CHAR_WIDTH + 1;
+	return LARGE_FONT_CHAR_WIDTH + 1;
 }
 
 void LargeFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
diff --git a/engines/pelrock/fonts/large_font.h b/engines/pelrock/fonts/large_font.h
index bd87b9012d2..2da5c24e5da 100644
--- a/engines/pelrock/fonts/large_font.h
+++ b/engines/pelrock/fonts/large_font.h
@@ -36,12 +36,12 @@ public:
 
 	// Required Font interface methods
 	int getFontHeight() const override { return CHAR_HEIGHT; }
-	int getMaxCharWidth() const override { return CHAR_WIDTH; }
+	int getMaxCharWidth() const override { return LARGE_FONT_CHAR_WIDTH; }
 	int getCharWidth(uint32 chr) const override;
 	void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
 
 private:
-	static const int CHAR_WIDTH = 12;
+	static const int LARGE_FONT_CHAR_WIDTH = 12;
 	static const int CHAR_HEIGHT = 24;
 	byte *_fontData;
 };
diff --git a/engines/pelrock/fonts/small_font.cpp b/engines/pelrock/fonts/small_font.cpp
index b439b5db2f9..d1de37d9a5c 100644
--- a/engines/pelrock/fonts/small_font.cpp
+++ b/engines/pelrock/fonts/small_font.cpp
@@ -49,7 +49,7 @@ bool SmallFont::load(const Common::String &filename) {
 }
 
 int SmallFont::getCharWidth(uint32 chr) const {
-	return CHAR_WIDTH;
+	return SMALL_FONT_CHAR_WIDTH;
 }
 
 void SmallFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
diff --git a/engines/pelrock/fonts/small_font.h b/engines/pelrock/fonts/small_font.h
index 3bde2c152b0..f64e3fa4a19 100644
--- a/engines/pelrock/fonts/small_font.h
+++ b/engines/pelrock/fonts/small_font.h
@@ -38,8 +38,8 @@ public:
 	bool load(const Common::String &filename);
 
 	// Required Font interface methods
-	int getFontHeight() const override { return CHAR_HEIGHT; }
-	int getMaxCharWidth() const override { return CHAR_WIDTH; }
+	int getFontHeight() const override { return SMALL_FONT_CHAR_HEIGHT; }
+	int getMaxCharWidth() const override { return SMALL_FONT_CHAR_WIDTH; }
 	int getCharWidth(uint32 chr) const override;
 	void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
 
@@ -47,8 +47,8 @@ public:
 
 protected:
 private:
-	static const int CHAR_WIDTH = 8;
-	static const int CHAR_HEIGHT = 8;
+	static const int SMALL_FONT_CHAR_WIDTH = 8;
+	static const int SMALL_FONT_CHAR_HEIGHT = 8;
 };
 
 } // End of namespace Pelrock


Commit: 5977e6a2f380c277a2ded410ccab730b66e80ef2
    https://github.com/scummvm/scummvm/commit/5977e6a2f380c277a2ded410ccab730b66e80ef2
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:43+02:00

Commit Message:
PELROCK: Removal of warnings due to incompatible types

Changed paths:
    engines/pelrock/fonts/large_font.cpp
    engines/pelrock/fonts/large_font.h
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp
    engines/pelrock/video/video.h


diff --git a/engines/pelrock/fonts/large_font.cpp b/engines/pelrock/fonts/large_font.cpp
index 5b534af3d27..7294b2ef8bf 100644
--- a/engines/pelrock/fonts/large_font.cpp
+++ b/engines/pelrock/fonts/large_font.cpp
@@ -116,7 +116,7 @@ int LargeFont::getCharWidth(uint32 chr) const {
 
 void LargeFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
 	chr -= 32; // Adjust for font starting at ASCII 32
-	if (!_fontData || chr >= 100 || chr < 0) {
+	if (!_fontData || chr >= 100) {
 		return;
 	}
 
diff --git a/engines/pelrock/fonts/large_font.h b/engines/pelrock/fonts/large_font.h
index 2da5c24e5da..fc6e39ebeea 100644
--- a/engines/pelrock/fonts/large_font.h
+++ b/engines/pelrock/fonts/large_font.h
@@ -35,14 +35,14 @@ public:
 	bool load(const Common::String &filename);
 
 	// Required Font interface methods
-	int getFontHeight() const override { return CHAR_HEIGHT; }
+	int getFontHeight() const override { return LARGE_FONT_CHAR_HEIGHT; }
 	int getMaxCharWidth() const override { return LARGE_FONT_CHAR_WIDTH; }
 	int getCharWidth(uint32 chr) const override;
 	void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
 
 private:
 	static const int LARGE_FONT_CHAR_WIDTH = 12;
-	static const int CHAR_HEIGHT = 24;
+	static const int LARGE_FONT_CHAR_HEIGHT = 24;
 	byte *_fontData;
 };
 } // End of namespace Pelrock
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 5603fb84161..70f1c7d2ad5 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -661,7 +661,7 @@ void MenuManager::drawScreen() {
 
 	_screen->blitFrom(_compositeBuffer);
 	byte defaultColor = 255;
-	for (int i = 0; _menuText.size() > i; i++) {
+	for (uint i = 0; _menuText.size() > i; i++) {
 		g_engine->_graphics->drawColoredText(_screen, _menuText[i], kTextStartX, kTextStartY + (i * _textLineH), 200, defaultColor, g_engine->_smallFont);
 	}
 
@@ -670,8 +670,8 @@ void MenuManager::drawScreen() {
 
 void MenuManager::drawInventoryIcons() {
 	bool debugIcons = false;
-	for (int i = 0; i < 4; i++) {
-		int itemIndex = _curInventoryPage * 4 + i;
+	for (uint i = 0; i < 4; i++) {
+		uint itemIndex = _curInventoryPage * 4 + i;
 		if (g_engine->_state->inventoryItems.size() <= itemIndex)
 			continue;
 		InventoryObject item = g_engine->_res->getIconForObject(g_engine->_state->inventoryItems[itemIndex]);
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index bd30ef1b79d..04f89c96371 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -193,7 +193,7 @@ private:
 	Common::Array<Common::StringArray> _menuTexts;
 	// Temporary
 	int _selectedInvIndex = 0;
-	int _curInventoryPage = 0;
+	uint _curInventoryPage = 0;
 	Common::StringArray _menuText;
 	Common::Array<Common::StringArray> _inventoryDescriptions;
 	Common::Array<Common::Point> _inventorySlots;
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 1bfbdf9bc7b..c129c768cfa 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1659,7 +1659,7 @@ VerbIcon PelrockEngine::isActionUnder(int x, int y) {
 		return NO_ACTION;
 	}*/
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
-	int loopEnd = _state->selectedInventoryItem != -1 ? actions.size() + 1 : actions.size();
+	uint loopEnd = _state->selectedInventoryItem != -1 ? actions.size() + 1 : actions.size();
 	for (uint i = 0; i < loopEnd; i++) {
 		Common::Point p = getPositionInBalloonForIndex(i, _actionPopupState.x, _actionPopupState.y);
 		Common::Rect actionRect = Common::Rect(p.x, p.y, p.x + kVerbIconWidth, p.y + kVerbIconHeight);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 396552cbadb..57f94badbc4 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -248,7 +248,7 @@ void RoomManager::enableSprite(byte spriteIndex, byte zOrder, int persist) {
 }
 
 void RoomManager::enableSprite(byte roomNumber, byte spriteIndex, byte zOrder, int persist) {
-	for (int i = 0; i < g_engine->_state->spriteChanges[roomNumber].size(); i++) {
+	for (uint i = 0; i < g_engine->_state->spriteChanges[roomNumber].size(); i++) {
 		if (g_engine->_state->spriteChanges[roomNumber][i].spriteIndex == spriteIndex) {
 			g_engine->_state->spriteChanges[roomNumber].remove_at(i);
 			break;
diff --git a/engines/pelrock/video/video.h b/engines/pelrock/video/video.h
index 07f79a3606a..596509dfb37 100644
--- a/engines/pelrock/video/video.h
+++ b/engines/pelrock/video/video.h
@@ -108,7 +108,7 @@ private:
 	AudioEffect readAudioEffect(Common::File &metadataFile);
 	char decodeChar(byte c);
 	Subtitle *getSubtitleForFrame(uint16 frameNumber);
-	int _currentSubtitleIndex = 0;
+	uint _currentSubtitleIndex = 0;
 	Graphics::Surface _videoSurface = Graphics::Surface();
 	Graphics::ManagedSurface _textSurface = Graphics::ManagedSurface();
 	Common::Array<ChunkHeader> _chunkBuffer;


Commit: c5b87203f07f16e8d5fdba12264ae9b26ddcb8af
    https://github.com/scummvm/scummvm/commit/c5b87203f07f16e8d5fdba12264ae9b26ddcb8af
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:43+02:00

Commit Message:
PELROCK: Fix mismatched free/deletes

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 70f1c7d2ad5..b8e874c276c 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -709,7 +709,7 @@ void MenuManager::loadMenu() {
 
 	curPos += kMainMenuPart1RawSize;
 
-	byte *compressedPart1 = new byte[kMainMenuPart1CompressedSize];
+	byte *compressedPart1 = (byte *) malloc(kMainMenuPart1CompressedSize);
 	alfred7.read(compressedPart1, kMainMenuPart1CompressedSize);
 	byte *decompressedPart1 = nullptr;
 	size_t decompressedSize = rleDecompress(compressedPart1, kMainMenuPart1CompressedSize, 0, 0, &decompressedPart1, true);
@@ -717,20 +717,21 @@ void MenuManager::loadMenu() {
 	memcpy((byte *)_mainMenu.getPixels() + curPos, decompressedPart1, decompressedSize);
 	curPos += decompressedSize;
 
-	delete[] compressedPart1;
-	delete[] decompressedPart1;
+	free(compressedPart1);
+	free(decompressedPart1);
 	alfred7.seek(kMainMenuPart2Offset, SEEK_SET);
 	alfred7.read((byte *)_mainMenu.getPixels() + curPos, kMainMenuPart2RawSize);
 	curPos += kMainMenuPart2RawSize;
-	byte *compressedPart2 = new byte[kMainMenuPart2CompressedSize];
+	byte *compressedPart2 = (byte *) malloc(kMainMenuPart2CompressedSize);
 	alfred7.read(compressedPart2, kMainMenuPart2CompressedSize);
 	byte *decompressedPart2 = nullptr;
 	decompressedSize = rleDecompress(compressedPart2, kMainMenuPart2CompressedSize, 0, 0, &decompressedPart2, true);
 
 	memcpy((byte *)_mainMenu.getPixels() + curPos, decompressedPart2, decompressedSize);
 	curPos += decompressedSize;
-	delete[] compressedPart2;
-	delete[] decompressedPart2;
+	free(compressedPart2);
+	free(decompressedPart2);
+
 	alfred7.seek(kMainMenuPart3Offset, SEEK_SET);
 	alfred7.read((byte *)_mainMenu.getPixels() + curPos, kMainMenuPart3Size);
 
@@ -750,28 +751,28 @@ void MenuManager::loadMenu() {
 
 	readButton(soundArrowsData, 0, _soundControlArrowLeft, 36, 28);
 	readButton(soundArrowsData, 36 * 28 * 2, _soundControlArrowRight, 31, 28);
-	delete[] soundArrowsData;
+	free(soundArrowsData);
 
 	byte *soundIconMasterData = nullptr;
 	size_t soundIconMasterSize = 0;
 	rleDecompressSingleBuda(&alfred7, kSoundMasterOffset, soundIconMasterData, soundIconMasterSize);
 	_soundControlMasterIcon = new byte[66 * 64];
 	extractSingleFrame(soundIconMasterData, _soundControlMasterIcon, 0, 66, 64);
-	delete[] soundIconMasterData;
+	free(soundIconMasterData);
 
 	byte *soundIconSfxData = nullptr;
 	size_t soundIconSfxSize = 0;
 	rleDecompressSingleBuda(&alfred7, kSoundSfxOffset, soundIconSfxData, soundIconSfxSize);
 	_soundControlSfxIcon = new byte[66 * 64];
 	extractSingleFrame(soundIconSfxData, _soundControlSfxIcon, 0, 66, 64);
-	delete[] soundIconSfxData;
+	free(soundIconSfxData);
 
 	byte *soundIconMusicData = nullptr;
 	size_t soundIconMusicSize = 0;
 	rleDecompressSingleBuda(&alfred7, kSoundMusicOffset, soundIconMusicData, soundIconMusicSize);
 	_soundControlMusicIcon = new byte[66 * 64];
 	extractSingleFrame(soundIconMusicData, _soundControlMusicIcon, 0, 66, 64);
-	delete[] soundIconMusicData;
+	free(soundIconMusicData);
 
 	_menuText = _menuTexts[0];
 	alfred7.close();
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 55b06c3c7a4..37037f82acf 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -91,7 +91,7 @@ ResourceManager::~ResourceManager() {
 	for (int i = 0; i < kNumVerbIcons; i++) {
 		delete[] _verbIcons[i];
 	}
-	delete[] _popUpBalloon;
+	free(_popUpBalloon);
 	for (int i = 0; i < 4; i++) {
 		// free all frame buffers
 		for (int j = 0; j < walkingAnimLengths[i]; j++) {
@@ -150,7 +150,7 @@ void ResourceManager::loadInteractionIcons() {
 	alfred7File.seek(kBalloonFramesOffset, SEEK_SET);
 
 	uint32 totalBalloonSize = kBalloonWidth * kBalloonHeight * kBalloonFrames;
-	_popUpBalloon = new byte[totalBalloonSize];
+	_popUpBalloon = nullptr;
 
 	uint32 compressedSize = kBalloonFramesSize;
 
@@ -187,7 +187,7 @@ void ResourceManager::loadAlfredAnims() {
 	alfred3.close();
 
 	uint32 capacity = 3060 * 102 + 2340 * 55;
-	byte *completePic = new byte[capacity];
+	byte *completePic = nullptr;
 	rleDecompress(bufferFile, alfred3Size, 0, capacity, &completePic);
 
 	byte *stdFramesPic = new byte[3060 * 102];
@@ -251,7 +251,7 @@ void ResourceManager::loadAlfredAnims() {
 
 	delete[] crawlFramesPic;
 	delete[] stdFramesPic;
-	delete[] completePic;
+	free(completePic);
 	free(bufferFile);
 
 	Common::File alfred7;
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 57f94badbc4..549825ac098 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -36,7 +36,7 @@ RoomManager::RoomManager() {
 
 RoomManager::~RoomManager() {
 	if (_pixelsShadows != nullptr) {
-		delete[] _pixelsShadows;
+		free(_pixelsShadows);
 		_pixelsShadows = nullptr;
 	}
 	delete[] _resetData;
@@ -602,7 +602,7 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	loadConversationData(pair12, pair12size, _conversationOffset, _conversationDataSize, _conversationData);
 
 	if (_pixelsShadows != nullptr)
-		delete[] _pixelsShadows;
+		free(_pixelsShadows);
 	_pixelsShadows = loadShadowMap(roomNumber);
 
 	loadRemaps(roomNumber);


Commit: 98b3285baeff931f7cfab5a11b037b8bded379b5
    https://github.com/scummvm/scummvm/commit/98b3285baeff931f7cfab5a11b037b8bded379b5
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:43+02:00

Commit Message:
PELROCK: Fixes leak on Dialog surface

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/dialog.h
    engines/pelrock/pathfinding.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/types.h
    engines/pelrock/video/video.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 116edb997f3..78e8f538538 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1683,7 +1683,7 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 	guard->animData[0].curFrame = 0;
 	guard->animData[0].nframes = 1;
 	// copy idle frame from talking animation
-	guard->animData[0].animData[0] = _room->_talkingAnimHeader.animA[0];
+	guard->animData[0].animData[0] = _room->_talkingAnims.animA[0];
 	_alfredState.direction = ALFRED_RIGHT;
 	walkAndAction(_room->findHotspotByExtra(guard->extra), TALK);
 	if (shouldQuit()) {
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 9a54e15acf3..b9dd53eb0ac 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -164,7 +164,7 @@ void DialogManager::displayChoices(Common::Array<ChoiceOption> *choices, Graphic
  * the maxWidth + height then print the text onto that surface with the appropriate alignment,
  * then blit that surface to the screen.
  */
-Graphics::Surface DialogManager::getDialogueSurface(Common::Array<Common::String> dialogueLines, byte speakerId, Graphics::TextAlign alignment) {
+Graphics::Surface *DialogManager::getDialogueSurface(Common::Array<Common::String> dialogueLines, byte speakerId, Graphics::TextAlign alignment) {
 
 	int maxWidth = 0;
 	int height = dialogueLines.size() * 25; // Add some padding
@@ -172,16 +172,16 @@ Graphics::Surface DialogManager::getDialogueSurface(Common::Array<Common::String
 		maxWidth = MAX(maxWidth, g_engine->_largeFont->getStringWidth(dialogueLines[i]));
 	}
 
-	Graphics::Surface s;
-	s.create(maxWidth + 1, height + 1, Graphics::PixelFormat::createFormatCLUT8());
-	s.fillRect(s.getRect(), 255); // Clear surface
+	Graphics::Surface *s = new Graphics::Surface();
+	s->create(maxWidth + 1, height + 1, Graphics::PixelFormat::createFormatCLUT8());
+	s->fillRect(s->getRect(), 255); // Clear surface
 
 	for (uint i = 0; i < dialogueLines.size(); i++) {
 
 		int xPos = 0;
 		int yPos = i * 25; // Above sprite, adjust for line
 		// debug("Drawing dialogue line %d: \"%s\" at position (%d, %d) with speaker ID %d", i, dialogueLines[i].c_str(), xPos, yPos, speakerId);
-		g_engine->_largeFont->drawString(&s, dialogueLines[i], xPos, yPos, maxWidth, speakerId, alignment);
+		g_engine->_largeFont->drawString(s, dialogueLines[i], xPos, yPos, maxWidth, speakerId, alignment);
 	}
 
 	return s;
@@ -211,7 +211,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 			yBasePos = _curSprite->y; // Above sprite, adjust for line
 
 			// Set NPC talk speed byte for original timing.
-			TalkingAnims *th = &g_engine->_room->_talkingAnimHeader;
+			TalkingAnims *th = &g_engine->_room->_talkingAnims;
 			g_engine->_npcTalkSpeedByte = _curSprite->talkingAnimIndex ? th->speedByteB : th->speedByteA;
 		}
 	}
@@ -274,22 +274,24 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		int xPos = xBasePos - maxWidth / 2;
 		int yPos = yBasePos - height;
 
-		Graphics::Surface s = getDialogueSurface(textLines, speakerId);
+		Graphics::Surface *s = getDialogueSurface(textLines, speakerId);
 
 		// Clamp to screen bounds (original game: min Y = 1, max X = 639 - width)
 		xPos = CLIP(xPos, 0, 639 - maxWidth);
-		yPos = CLIP(yPos, 1, 400 - (int)s.getRect().height());
+		yPos = CLIP(yPos, 1, 400 - (int)s->getRect().height());
 
 		if (g_engine->_shakeEffectState.enabled) {
 			debug("Applying shake effect to dialogue, shakeX: %d", g_engine->_shakeEffectState.shakeX);
 			xPos -= g_engine->_shakeEffectState.shakeX;
 		}
 
-		_screen->transBlitFrom(s, s.getRect(), Common::Point(xPos, yPos), 255);
+		_screen->transBlitFrom(*s, s->getRect(), Common::Point(xPos, yPos), 255);
 		// drawPos(_screen, xPos, yPos, speakerId);
 
 		_screen->markAllDirty();
 		_screen->update();
+		s->free();
+		delete s;
 
 		// Check if TTL expired for this page (always applies, even for _disableClickToAdvance)
 		bool ttlExpired = !fromIntro && (pageTtlMs > 0) && (g_system->getMillis() - pageStartMs >= pageTtlMs);
diff --git a/engines/pelrock/dialog.h b/engines/pelrock/dialog.h
index 50f5486cd85..045da583084 100644
--- a/engines/pelrock/dialog.h
+++ b/engines/pelrock/dialog.h
@@ -114,7 +114,7 @@ public:
 	void say(Common::StringArray texts, byte spriteIndex = 0);
 	void say(Common::StringArray texts, int16 x, int16 y);
 	bool processColorAndTrim(Common::StringArray &lines, byte &speakerId);
-	Graphics::Surface getDialogueSurface(Common::Array<Common::String> dialogueLines, byte speakerId, Graphics::TextAlign alignment = Graphics::kTextAlignCenter);
+	Graphics::Surface *getDialogueSurface(Common::Array<Common::String> dialogueLines, byte speakerId, Graphics::TextAlign alignment = Graphics::kTextAlignCenter);
 
 	Common::Array<Common::Array<Common::String>> wordWrap(Common::String text);
 	Common::Array<Common::Array<Common::String>> wordWrap(Common::StringArray texts);
diff --git a/engines/pelrock/pathfinding.cpp b/engines/pelrock/pathfinding.cpp
index e3dd713db12..38f8483e9a6 100644
--- a/engines/pelrock/pathfinding.cpp
+++ b/engines/pelrock/pathfinding.cpp
@@ -219,7 +219,7 @@ byte getAdjacentWalkbox(Common::Array<WalkBox> &walkboxes, byte currentBoxIndex)
 }
 
 void clearVisitedFlags(Common::Array<WalkBox> &walkboxes) {
-	for (int i = 0; i < walkboxes.size(); i++) {
+	for (uint i = 0; i < walkboxes.size(); i++) {
 		walkboxes[i].flags = 0;
 	}
 }
@@ -261,7 +261,6 @@ uint16 buildWalkboxPath(Common::Array<WalkBox> &walkboxes, byte startBox, byte d
 
 	// Terminate path
 	pathBuffer[pathIndex] = kPathEnd;
-	debug("Built walkbox path of length %d", pathIndex);
 	return pathIndex;
 }
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index c129c768cfa..eceb0c4a3f0 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -562,7 +562,6 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 }
 
 void PelrockEngine::executeAction(VerbIcon action, HotSpot *hotspot) {
-	debug("Executing action %d on hotspot %d", action, hotspot->extra);
 	if (action == ITEM) {
 		int inventoryObject = _state->selectedInventoryItem;
 		for (const CombinationEntry *entry = combinationTable; entry->handler != nullptr; entry++) {
@@ -623,9 +622,6 @@ void PelrockEngine::checkMouse() {
 		_actionPopupState.isActive = false;
 		// Mouse was released while popup is active
 		VerbIcon actionClicked = isActionUnder(_events->_releaseX, _events->_releaseY);
-		if (actionClicked != NO_ACTION) {
-			debug("Popup action clicked: %d, is alfredunder %d", actionClicked, _actionPopupState.isAlfredUnder);
-		}
 		if (actionClicked != NO_ACTION && _currentHotspot != nullptr) {
 			// Action was selected - queue it
 			walkAndAction(_currentHotspot, actionClicked);
@@ -746,12 +742,10 @@ void PelrockEngine::talkTo(HotSpot *hotspot) {
 		}
 	}
 	changeCursor(DEFAULT);
-	debug("Talking to hotspot %d (%d) with extra %d", hotspot->index, hotspot->isSprite ? hotspot->index : hotspot->index - _room->_currentRoomAnims.size(), hotspot->extra);
 
 	// Set NPC talk speed byte for original timing
-	TalkingAnims *th = &_room->_talkingAnimHeader;
+	TalkingAnims *th = &_room->_talkingAnims;
 	_npcTalkSpeedByte = animSet->talkingAnimIndex ? th->speedByteB : th->speedByteA;
-	debug("NPC talk speed byte: %d (slot %d)", _npcTalkSpeedByte, animSet->talkingAnimIndex);
 
 	_dialog->startConversation(_room->_conversationData, _room->_conversationDataSize, animSet->talkingAnimIndex, animSet);
 
@@ -814,7 +808,6 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			_currentStep++;
 			if (_currentStep >= _currentContext.movementCount) {
 				_currentStep = 0;
-				debug("Finished walking to target");
 				_alfredState.setState(ALFRED_IDLE);
 				_alfredState.isWalkingCancelable = true;
 				_disableAction = false;
@@ -844,7 +837,6 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 				// Only check exits after walking is complete AND no queued action
 				Exit *exit = isExitUnder(_alfredState.x, _alfredState.y);
 				if (exit != nullptr) {
-					debug("Using exit to room %d", exit->targetRoom);
 					exitTriggers(exit);
 					_alfredState.x = exit->targetX;
 					_alfredState.y = exit->targetY;
@@ -899,7 +891,6 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		break;
 	}
 	case ALFRED_INTERACTING: {
-		debug("Alfred interacting frame %d/%d, direction %d", _alfredState.curFrame, interactingAnimLength, _alfredState.direction);
 		drawAlfred(_res->alfredInteractFrames[_alfredState.direction][_alfredState.curFrame]);
 		_alfredState.curFrame++;
 		if (_alfredState.curFrame >= interactingAnimLength) {
@@ -1203,17 +1194,12 @@ void PelrockEngine::checkLongMouseClick(int x, int y) {
 	bool alfredUnder = isAlfredUnder(x, y);
 	if ((hotspotIndex != -1 || alfredUnder) && !_actionPopupState.isActive) {
 
-		// Original game positions balloon at alfred_x - 70, clamped to [1, 390]
 		_actionPopupState.x = CLIP((int)_alfredState.x - 70, 1, 390);
 
-		// Original game: Y = max(10, alfred_y - character_sprite_height - 102)
-		// The 102 offset is a fixed gap above Alfred's head, NOT the balloon height.
-		// This means the balloon bottom overlaps Alfred's head by ~10 pixels.
 		_actionPopupState.y = MAX(10, (int)_alfredState.y - (int)kAlfredFrameHeight - 102);
 		_actionPopupState.isActive = true;
 		_inventoryOverlayState.invStartingPos = -1;
 		_actionPopupState.curFrame = 0;
-		debug("Setting alfred under popup: %d", alfredUnder);
 		_actionPopupState.isAlfredUnder = alfredUnder;
 		if (hotspotIndex != -1) {
 			_currentHotspot = &_room->_currentRoomHotspots[hotspotIndex];
@@ -1373,7 +1359,7 @@ void PelrockEngine::animateTalkingNPC(Sprite *animSet) {
 	// Change with the right index
 
 	int index = animSet->talkingAnimIndex;
-	TalkingAnims *animHeader = &_room->_talkingAnimHeader;
+	TalkingAnims *animHeader = &_room->_talkingAnims;
 
 	int x = animSet->x + (index ? animHeader->offsetXAnimB : animHeader->offsetXAnimA);
 	int y = animSet->y + (index ? animHeader->offsetYAnimB : animHeader->offsetYAnimA);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 549825ac098..593e29e3f72 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -30,7 +30,6 @@ namespace Pelrock {
 static const uint32 kPaletteRemapOffset = 0x4C77C; // JUEGO.EXE — water-effect palette remap table
 
 RoomManager::RoomManager() {
-	_pixelsShadows = new byte[640 * 400];
 	loadWaterPaletteRemap();
 }
 
@@ -39,9 +38,24 @@ RoomManager::~RoomManager() {
 		free(_pixelsShadows);
 		_pixelsShadows = nullptr;
 	}
+	clearAnims();
 	delete[] _resetData;
 }
 
+void RoomManager::clearAnims() {
+	for (auto &sprite : _currentRoomAnims) {
+		if (sprite.animData) {
+			for (int a = 0; a < sprite.numAnims; a++) {
+				for (int f = 0; f < sprite.animData[a].nframes; f++) {
+					delete[] sprite.animData[a].animData[f]; // free each frame
+				}
+				delete[] sprite.animData[a].animData; // free frame pointer array
+			}
+			delete[] sprite.animData; // free anim array
+		}
+	}
+}
+
 void RoomManager::loadWaterPaletteRemap() {
 	// Extra remap for water effect
 	Common::File exe;
@@ -578,6 +592,10 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	Common::Array<Sprite> sprites = loadRoomAnimations(pic, pixelDataSize, pair10, pair10size);
 	Common::Array<HotSpot> staticHotspots = loadHotspots(pair10, pair10size);
 
+
+	// clear anims from previous room
+	clearAnims();
+
 	_currentRoomAnims = sprites;
 	_currentRoomHotspots = unifyHotspots(sprites, staticHotspots);
 	_currentRoomExits = loadExits(pair10, pair10size);
@@ -893,7 +911,6 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 	Common::Array<Sprite> anims = Common::Array<Sprite>();
 	uint32 spriteCountPos = 5;
 	byte spriteCount = data[spriteCountPos] - 2;
-	debug("Sprite count: %d", spriteCount);
 	uint32 metadata_start = spriteCountPos + (44 * 2 + 5);
 	uint32 picOffset = 0;
 
@@ -1182,7 +1199,6 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 	talkFile.read(&talkHeader.unknown6, 24);
 
 	if (talkHeader.spritePointer == 0) {
-		debug("No talking animation for room %d", roomNumber);
 		talkFile.close();
 		return;
 	}
@@ -1215,7 +1231,7 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 		}
 	}
 	free(decompressed);
-	_talkingAnimHeader = talkHeader;
+	_talkingAnims = talkHeader;
 
 	talkFile.close();
 }
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 9ff5afc04ad..dcdab505744 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -78,6 +78,7 @@ class RoomManager {
 public:
 	RoomManager();
 	~RoomManager();
+	void clearAnims();
 	void loadRoomMetadata(Common::File *roomFile, int roomNumber);
 	/**
 	 * Passer by animations are animations of characters that merely traverse the scene as ambient
@@ -168,7 +169,7 @@ public:
 	Common::Array<WalkBox> _currentRoomWalkboxes;
 	Common::Array<Description> _currentRoomDescriptions;
 
-	TalkingAnims _talkingAnimHeader;
+	TalkingAnims _talkingAnims;
 	ScalingParams _scaleParams;
 	byte *_pixelsShadows = nullptr;
 	byte _roomPalette[768];
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 913b747b970..1031e05af6e 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -302,49 +302,49 @@ struct Sprite {
 };
 
 struct HotSpot {
-	byte index;
-	byte innerIndex;
-	int id;
-	int16 x;
-	int16 y;
-	int w;
-	int h;
-	byte actionFlags;
-	int16 extra;
+	byte index = 0;
+	byte innerIndex = 0;
+	int id = 0;
+	int16 x = 0;
+	int16 y = 0;
+	int w = 0;
+	int h = 0;
+	byte actionFlags = 0;
+	int16 extra = 0;
 	bool isEnabled = true;
 	bool isSprite = false;
 	byte zOrder = 0;
 };
 
 struct TalkingAnims {
-	uint32 spritePointer;
+	uint32 spritePointer = 0;
 
 	byte unknown2[3];
 
-	int8 offsetXAnimA;
-	int8 offsetYAnimA;
+	int8 offsetXAnimA = 0;
+	int8 offsetYAnimA = 0;
 
-	byte wAnimA;
-	byte hAnimA;
+	byte wAnimA = 0;
+	byte hAnimA = 0;
 	byte unknown3[2];
-	byte numFramesAnimA;
+	byte numFramesAnimA = 0;
 	byte unknown4[4]; // slot 0 data pointer (unused in ScummVM)
-	byte speedByteA;  // slot 0 offset 0x12: controls NPC talk render rate (original: 2+speedByte ticks per render)
+	byte speedByteA = 0;  // slot 0 offset 0x12: controls NPC talk render rate (original: 2+speedByte ticks per render)
 
-	byte offsetXAnimB;
-	byte offsetYAnimB;
+	int8 offsetXAnimB = 0;
+	int8 offsetYAnimB = 0;
 
-	byte wAnimB;
-	byte hAnimB;
+	byte wAnimB = 0;
+	byte hAnimB = 0;
 	byte unknown5[2]; // slot 1 stride (unused in ScummVM)
-	byte numFramesAnimB;
+	byte numFramesAnimB = 0;
 	byte unknown7[4];  // slot 1 data pointer (unused in ScummVM)
-	byte speedByteB;   // slot 1 speed byte at file offset 30
+	byte speedByteB = 0;   // slot 1 speed byte at file offset 30
 	byte unknown6[24]; // slots 2-3 (unused)
 
 	// Runtime fields (not read from file)
-	byte currentFrameAnimA;
-	byte currentFrameAnimB;
+	byte currentFrameAnimA = 0;
+	byte currentFrameAnimB = 0;
 
 	byte **animA = nullptr;
 	byte **animB = nullptr;
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index cd344ae194b..91021021c63 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -130,9 +130,10 @@ void VideoManager::playIntro() {
 
 				byte color;
 				_dialog->processColorAndTrim(lines, color);
-				Graphics::Surface s = _dialog->getDialogueSurface(lines, color);
-				_textSurface.transBlitFrom(s, Common::Point(subtitle->x, subtitle->y), 255);
-				s.free();
+				Graphics::Surface *s = _dialog->getDialogueSurface(lines, color);
+				_textSurface.transBlitFrom(*s, Common::Point(subtitle->x, subtitle->y), 255);
+				s->free();
+				delete s;
 			}
 
 			presentFrame();


Commit: 1e2346f201d176ea599aafc2c8fa98d66c28ba6b
    https://github.com/scummvm/scummvm/commit/1e2346f201d176ea599aafc2c8fa98d66c28ba6b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:44+02:00

Commit Message:
PELROCK: Fixes leak with passer-by-animations and palette animations

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/room.cpp
    engines/pelrock/saveload.cpp
    engines/pelrock/types.h
    engines/pelrock/video/video.cpp


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index b8e874c276c..c3507043ea6 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -836,7 +836,6 @@ void MenuManager::loadMenuTexts() {
 			_menuTexts.push_back(unprocessedMenuTexts[i]);
 		}
 	}
-	debug("Menu texts size after processing: %d", (int)_menuTexts.size());
 	_menuText = _menuTexts[0];
 	delete[] textBuffer;
 
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 593e29e3f72..97c787b0a93 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -40,6 +40,12 @@ RoomManager::~RoomManager() {
 	}
 	clearAnims();
 	delete[] _resetData;
+	if(_currentPaletteAnim) {
+		delete _currentPaletteAnim;
+	}
+	if(_passerByAnims) {
+		delete _passerByAnims;
+	}
 }
 
 void RoomManager::clearAnims() {
@@ -592,6 +598,7 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	Common::Array<Sprite> sprites = loadRoomAnimations(pic, pixelDataSize, pair10, pair10size);
 	Common::Array<HotSpot> staticHotspots = loadHotspots(pair10, pair10size);
 
+	free(pic);
 
 	// clear anims from previous room
 	clearAnims();
@@ -904,6 +911,7 @@ void RoomManager::loadAnimationPixelData(Common::File *roomFile, int roomOffset,
 			outSize = size;
 		}
 	}
+	delete[] pixelData;
 }
 
 Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pixelDataSize, byte *data, size_t size) {
@@ -1159,7 +1167,7 @@ void RoomManager::resetMetadataDefaults(byte room, byte *&data, size_t size) {
 			continue;
 		}
 		Common::copy(entry.data, entry.data + entry.dataSize, data + entry.offset);
-		// delete[] entry.data;
+		delete[] entry.data;
 	}
 	alfred8.close();
 }
diff --git a/engines/pelrock/saveload.cpp b/engines/pelrock/saveload.cpp
index 2b6c3cfa3b0..cbf2c00e135 100644
--- a/engines/pelrock/saveload.cpp
+++ b/engines/pelrock/saveload.cpp
@@ -318,7 +318,8 @@ bool syncGameStateData(Common::Serializer &s, GameStateData *gameState) {
 			}
 		}
 	} else {
-		gameState->disabledBranches.clear();
+
+		gameState->clearBranches();
 		for (uint16 idx = 0; idx < disabledBranchesSize; ++idx) {
 			byte roomNumber;
 			s.syncAsByte(roomNumber);
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 1031e05af6e..1b563f96c2d 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -612,20 +612,31 @@ struct GameStateData {
 		for (int i = 0; i < kNumGameFlags; i++)
 			flags[i] = 0;
 		flags[FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ] = true;
-		disabledBranches.clear();
 		inventoryItems.clear();
 		stickersPerRoom.clear();
 		roomExitChanges.clear();
 		roomWalkBoxChanges.clear();
 		roomHotSpotChanges.clear();
 		spriteChanges.clear();
-		disabledBranches.clear();
+		clearBranches();
 		libraryShelf = -1;
 		selectedBookIndex = -1;
 		bookLetter = '\0';
 		stateGame = GAME;
 	}
 
+	void clearBranches() {
+		for(auto &entry : disabledBranches) {
+			for (ResetEntry &resetEntry : entry._value) {
+				if (resetEntry.data) {
+					delete[] resetEntry.data;
+					resetEntry.data = nullptr;
+				}
+			}
+		}
+		disabledBranches.clear();
+	}
+
 	void addDisabledBranch(ResetEntry entry) {
 		disabledBranches[entry.room].push_back(entry);
 	}
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index 91021021c63..e5d51272256 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -100,7 +100,6 @@ void VideoManager::playIntro() {
 						break;
 				}
 				AudioEffect voice = _voiceEffect[currentFrame];
-				debug("Playing voice effect: '%s'", voice.filename.c_str());
 				VoiceData voiceData = _sounds[voice.filename];
 				_introSndFile.seek(voiceData.offset, SEEK_SET);
 				byte *voiceBuffer = new byte[voiceData.length];
@@ -110,7 +109,6 @@ void VideoManager::playIntro() {
 
 			if (_sfxEffect.contains(currentFrame)) {
 				AudioEffect sfx = _sfxEffect[currentFrame];
-				debug("Playing SFX effect: '%s'", sfx.filename.c_str());
 				VoiceData sfxData = _sounds[sfx.filename];
 				_introSndFile.seek(sfxData.offset, SEEK_SET);
 				byte *sfxBuffer = new byte[sfxData.length];


Commit: d81b5918a5bc185dfa42eb1e20fd439c0db72e79
    https://github.com/scummvm/scummvm/commit/d81b5918a5bc185dfa42eb1e20fd439c0db72e79
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:44+02:00

Commit Message:
PELROCK: Make sure to clear animations before loading new ones

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/menu.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/saveload.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 78e8f538538..879fdef3099 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -2034,7 +2034,6 @@ void PelrockEngine::teleportToPrincess() {
 
 void PelrockEngine::useOnAlfred(int inventoryObject) {
 
-	debug("Using item %d on Alfred", inventoryObject);
 	switch (inventoryObject) {
 	case 9: // Letter
 		_dialog->say(_res->_ingameTexts[kTextCorrespondenciaAjena]);
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index b9dd53eb0ac..d9d1a7b8948 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -998,7 +998,6 @@ void DialogManager::maybeDisableChoice(Common::Array<Pelrock::ChoiceOption> *cho
 					currentChoicePos = scanPos;
 					isCurrentFB = true;
 					foundParent = true;
-					debug("Found parent FB at level %d, pos %u", currentLevel, currentChoicePos);
 					break;
 				}
 			}
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index c3507043ea6..5138141493a 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -536,7 +536,6 @@ bool MenuManager::selectInventoryItem(int i) {
 	_menuText = _inventoryDescriptions[_selectedInvIndex];
 	_sound->playSound(inventorySounds[_selectedInvIndex], 0);
 	g_engine->_state->selectedInventoryItem = _selectedInvIndex;
-	debug("Selected inventory item %d", _selectedInvIndex);
 	return true;
 }
 
@@ -583,9 +582,6 @@ void MenuManager::menuLoop() {
 	_musicVolumeLevel = mixerVolumeToLevel(_sound->getVolumeMusic());
 	_masterVolumeLevel = mixerVolumeToLevel(_sound->getVolumeMaster());
 
-	debug("Initial master volume level: %d", _masterVolumeLevel);
-	debug("Initial SFX volume level: %d", _sfxVolumeLevel);
-	debug("Initial Music volume level: %d", _musicVolumeLevel);
 	_masterSoundIcon.create(66, 64, Graphics::PixelFormat::createFormatCLUT8());
 	_sfxSoundIcon.create(66, 64, Graphics::PixelFormat::createFormatCLUT8());
 	_musicSoundIcon.create(66, 64, Graphics::PixelFormat::createFormatCLUT8());
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index eceb0c4a3f0..8a3a2244a70 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -626,7 +626,6 @@ void PelrockEngine::checkMouse() {
 			// Action was selected - queue it
 			walkAndAction(_currentHotspot, actionClicked);
 		} else if (_actionPopupState.isAlfredUnder && actionClicked != NO_ACTION) {
-			debug("Using item on Alfred");
 			useOnAlfred(_state->selectedInventoryItem);
 		} else if (_inventoryOverlayState.isActive && _inventoryOverlayState.posInInventorySelectionArea(_events->_releaseX, _events->_releaseY)) {
 			int item = checkMouseClickInventoryOverlay(_events->_releaseX, _events->_releaseY);
@@ -1580,9 +1579,7 @@ void PelrockEngine::walkLoop(int16 x, int16 y, AlfredDirection direction) {
 
 void PelrockEngine::walkTo(int x, int y) {
 	_currentStep = 0;
-	PathContext context = {nullptr, nullptr, 0, 0, 0};
-	findPath(_alfredState.x, _alfredState.y, x, y, _room->_currentRoomWalkboxes, &context, _currentHotspot);
-	_currentContext = context;
+	findPath(_alfredState.x, _alfredState.y, x, y, _room->_currentRoomWalkboxes, &_currentContext, _currentHotspot);
 	_alfredState.setState(ALFRED_WALKING);
 }
 
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 37037f82acf..8eccb2bff5e 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -336,7 +336,7 @@ void ResourceManager::loadAlfredSpecialAnim(int numAnim, bool reverse) {
 		size_t blockSize = 0;
 		readUntilBuda(&alfredFile, anim.offset, thisBlock, blockSize);
 		rleDecompress(thisBlock, blockSize, 0, size, &_currentSpecialAnim->animData, false);
-		delete[] thisBlock;
+		free(thisBlock);
 	} else {
 		alfredFile.read(_currentSpecialAnim->animData, anim.numFrames * anim.w * anim.h);
 	}
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 97c787b0a93..72ed74bb262 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -39,6 +39,7 @@ RoomManager::~RoomManager() {
 		_pixelsShadows = nullptr;
 	}
 	clearAnims();
+	clearTalkingAnims();
 	delete[] _resetData;
 	if(_currentPaletteAnim) {
 		delete _currentPaletteAnim;
@@ -48,6 +49,17 @@ RoomManager::~RoomManager() {
 	}
 }
 
+void RoomManager::clearTalkingAnims() {
+    for (int i = 0; i < _talkingAnims.numFramesAnimA; i++)
+        delete[] _talkingAnims.animA[i];
+    delete[] _talkingAnims.animA;
+    for (int i = 0; i < _talkingAnims.numFramesAnimB; i++)
+        delete[] _talkingAnims.animB[i];
+    delete[] _talkingAnims.animB;
+    _talkingAnims.animA = nullptr;
+    _talkingAnims.animB = nullptr;
+}
+
 void RoomManager::clearAnims() {
 	for (auto &sprite : _currentRoomAnims) {
 		if (sprite.animData) {
@@ -1128,10 +1140,7 @@ void RoomManager::addDisabledChoice(ChoiceOption choice) {
 	// Write 0xFA at offset+2 (after FB/F1 marker and level byte)
 	// This marks the choice as disabled without destroying the marker structure
 	uint32 disableOffset = choice.dataOffset + 2;
-	debug("Adding disabled branch for room %d at offset %d (FA written at %d)",
-		  choice.room, choice.dataOffset, disableOffset);
 
-	debug("Disabled branch is: \"%s\"", choice.text.c_str());
 	ResetEntry resetEntry = ResetEntry();
 	resetEntry.room = choice.room;
 	resetEntry.offset = disableOffset;
@@ -1239,6 +1248,7 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 		}
 	}
 	free(decompressed);
+	clearTalkingAnims();
 	_talkingAnims = talkHeader;
 
 	talkFile.close();
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index dcdab505744..5b14be2505d 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -78,6 +78,7 @@ class RoomManager {
 public:
 	RoomManager();
 	~RoomManager();
+	void clearTalkingAnims();
 	void clearAnims();
 	void loadRoomMetadata(Common::File *roomFile, int roomNumber);
 	/**
diff --git a/engines/pelrock/saveload.cpp b/engines/pelrock/saveload.cpp
index cbf2c00e135..5492b12aac2 100644
--- a/engines/pelrock/saveload.cpp
+++ b/engines/pelrock/saveload.cpp
@@ -114,12 +114,6 @@ bool syncGeneralData(Common::Serializer &s, SaveGameData *game) {
 	s.syncAsUint16LE(game->alfredY);
 	s.syncAsByte((byte &)game->alfredDir);
 
-	if (s.isLoading()) {
-		debug("LOAD: room=%d, x=%d, y=%d, dir=%d", game->currentRoom, game->alfredX, game->alfredY, game->alfredDir);
-	} else {
-		debug("SAVE: room=%d, x=%d, y=%d, dir=%d", game->currentRoom, game->alfredX, game->alfredY, game->alfredDir);
-	}
-
 	return !s.err();
 }
 
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 15247333b4a..14e24b86d76 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -180,7 +180,7 @@ int SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
 	}
 
 	sonidosFile.seek(sound.offset, SEEK_SET);
-	byte *data = new byte[sound.size];
+	byte *data = (byte *)malloc(sound.size);
 	sonidosFile.read(data, sound.size);
 	sonidosFile.close();
 
@@ -192,6 +192,7 @@ int SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
 		// For WAV/RIFF files, use the wave decoder
 		Common::MemoryReadStream *memStream = new Common::MemoryReadStream(data, sound.size, DisposeAfterUse::YES);
 		stream = Audio::makeWAVStream(memStream, DisposeAfterUse::YES);
+		// no need to free 'data' here, it will be freed when memStream is disposed
 	} else if (format == SOUND_FORMAT_RAWPCM || format == SOUND_FORMAT_MILES || format == SOUND_FORMAT_MILES2) {
 		// Determine the offset to skip the header
 		uint32 headerSize = 0;
@@ -202,13 +203,13 @@ int SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
 		uint32 pcmSize = sound.size - headerSize;
 		byte *pcmData = (byte *)malloc(pcmSize);
 		memcpy(pcmData, data + headerSize, pcmSize);
-		delete[] data;
+		free(data);
 
 		// Create raw audio stream (8-bit unsigned mono is common for old games)
 		stream = Audio::makeRawStream(pcmData, pcmSize, sampleRate, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
 	} else {
 		debug("Unknown sound format on sound with name %s at offset %d, with size %d", sound.filename.c_str(), sound.offset, sound.size);
-		delete[] data;
+		free(data);
 		return -1;
 	}
 
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 1b563f96c2d..e586f2dfa7f 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -153,7 +153,7 @@ struct AlfredSpecialAnim {
 
 	~AlfredSpecialAnim() {
 		if (animData) {
-			delete[] animData;
+			free(animData);
 			animData = nullptr;
 		}
 	}


Commit: ee7fa87c946a4136d77afe88a09654d6d8c28fc9
    https://github.com/scummvm/scummvm/commit/ee7fa87c946a4136d77afe88a09654d6d8c28fc9
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:44+02:00

Commit Message:
PELROCK: Fix menu buttons not being cleared

Changed paths:
    engines/pelrock/menu.cpp


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 5138141493a..f381375751b 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -1016,6 +1016,19 @@ Pelrock::MenuManager::~MenuManager() {
 	delete[] _inventoryLeftArrow[1];
 	delete[] _inventoryRightArrow[0];
 	delete[] _inventoryRightArrow[1];
+	// delete buttons
+	delete[] _saveButtons[0]; delete[] _saveButtons[1];
+	delete[] _loadButtons[0]; delete[] _loadButtons[1];
+	delete[] _soundsButtons[0]; delete[] _soundsButtons[1];
+	delete[] _exitToDosButtons[0]; delete[] _exitToDosButtons[1];
+	delete[] _savesUpArrows[0]; delete[] _savesUpArrows[1];
+	delete[] _savesDownArrows[0]; delete[] _savesDownArrows[1];
+	delete[] _soundControlArrowLeft[0]; delete[] _soundControlArrowLeft[1];
+	delete[] _soundControlArrowRight[0]; delete[] _soundControlArrowRight[1];
+
+	delete[] _soundControlMasterIcon;
+	delete[] _soundControlSfxIcon;
+	delete[] _soundControlMusicIcon;
 }
 
 } // End of namespace Pelrock


Commit: b827ac0f9bc91dafecdac500cf494a3a3e36bdfd
    https://github.com/scummvm/scummvm/commit/b827ac0f9bc91dafecdac500cf494a3a3e36bdfd
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:45+02:00

Commit Message:
PELROCK: Fixes leak in room reset data

Changed paths:
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 72ed74bb262..22c0aa6c298 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -41,23 +41,23 @@ RoomManager::~RoomManager() {
 	clearAnims();
 	clearTalkingAnims();
 	delete[] _resetData;
-	if(_currentPaletteAnim) {
+	if (_currentPaletteAnim) {
 		delete _currentPaletteAnim;
 	}
-	if(_passerByAnims) {
+	if (_passerByAnims) {
 		delete _passerByAnims;
 	}
 }
 
 void RoomManager::clearTalkingAnims() {
-    for (int i = 0; i < _talkingAnims.numFramesAnimA; i++)
-        delete[] _talkingAnims.animA[i];
-    delete[] _talkingAnims.animA;
-    for (int i = 0; i < _talkingAnims.numFramesAnimB; i++)
-        delete[] _talkingAnims.animB[i];
-    delete[] _talkingAnims.animB;
-    _talkingAnims.animA = nullptr;
-    _talkingAnims.animB = nullptr;
+	for (int i = 0; i < _talkingAnims.numFramesAnimA; i++)
+		delete[] _talkingAnims.animA[i];
+	delete[] _talkingAnims.animA;
+	for (int i = 0; i < _talkingAnims.numFramesAnimB; i++)
+		delete[] _talkingAnims.animB[i];
+	delete[] _talkingAnims.animB;
+	_talkingAnims.animA = nullptr;
+	_talkingAnims.animB = nullptr;
 }
 
 void RoomManager::clearAnims() {
@@ -565,13 +565,16 @@ void RoomManager::resetConversationStates(byte roomNumber, byte *conversationDat
 		if (roomNumber < entry.room) {
 			// We've passed the room we care about
 			roomDone = true;
+			delete[] entry.data;
 			break;
 		}
 		if (roomNumber > entry.room) {
 			// Not the room we care about, skip
+			delete[] entry.data;
 			continue;
 		}
 		Common::copy(entry.data, entry.data + entry.dataSize, conversationData + entry.offset);
+		delete[] entry.data;
 	}
 	alfredB.close();
 }
@@ -650,10 +653,11 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	}
 
 	PaletteAnim *anim = getPaletteAnimForRoom(roomNumber);
+	if (_currentPaletteAnim != nullptr) {
+		delete _currentPaletteAnim;
+	}
 	if (anim != nullptr) {
-		if (_currentPaletteAnim != nullptr) {
-			delete _currentPaletteAnim;
-		}
+
 		_currentPaletteAnim = anim;
 	} else {
 		_currentPaletteAnim = nullptr;
@@ -688,8 +692,7 @@ int streetWalkerIndices[] = {
 	-1, // room 13,
 	2,  // room 14,
 	-1, // room 15,
-	2
-};
+	2};
 
 RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 	RoomPasserBys *anims = nullptr;
@@ -1169,10 +1172,12 @@ void RoomManager::resetMetadataDefaults(byte room, byte *&data, size_t size) {
 		if (room < entry.room) {
 			// We've passed the room we care about
 			roomDone = true;
+			delete[] entry.data;
 			break;
 		}
 		if (room > entry.room) {
 			// Not the room we care about, skip
+			delete[] entry.data;
 			continue;
 		}
 		Common::copy(entry.data, entry.data + entry.dataSize, data + entry.offset);


Commit: 693a07ddec1c20c184788cde959632a8e95e50c2
    https://github.com/scummvm/scummvm/commit/693a07ddec1c20c184788cde959632a8e95e50c2
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:45+02:00

Commit Message:
PELROCK: Fix savegame data leak

Changed paths:
    engines/pelrock/saveload.cpp


diff --git a/engines/pelrock/saveload.cpp b/engines/pelrock/saveload.cpp
index 5492b12aac2..b159da5e760 100644
--- a/engines/pelrock/saveload.cpp
+++ b/engines/pelrock/saveload.cpp
@@ -357,6 +357,7 @@ Common::Error PelrockEngine::syncGame(Common::Serializer &s) {
 	} else {
 		SaveGameData *saveGame = createSaveGameData();
 		result = syncSaveData(s, saveGame);
+		delete saveGame;
 	}
 	return result;
 }


Commit: fefda1a14e36fd47f72f5d63467de32a9e4cca15
    https://github.com/scummvm/scummvm/commit/fefda1a14e36fd47f72f5d63467de32a9e4cca15
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:45+02:00

Commit Message:
PELROCK: Avoid leaking choice list

Changed paths:
    engines/pelrock/dialog.cpp


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index d9d1a7b8948..302c9caa9a6 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -622,12 +622,14 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		if (choices->empty()) {
 			state.position = positionStack.empty() ? 0 : positionStack.pop();
 			if (state.position == 0) {
+				delete choices;
 				// No choices and no previous position to go back to, ending conversation
 				break;
 			}
 			checkAllSubBranchesExhausted(conversationData, dataSize, state.position, state.currentChoiceLevel - 1);
 			// No choices found, popping back to previous choice menu, position %u
 			skipToChoices = true;
+			delete choices;
 			continue;
 		}
 


Commit: a9f9e974d064b022e994bc6f07f601e42253487b
    https://github.com/scummvm/scummvm/commit/a9f9e974d064b022e994bc6f07f601e42253487b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:46+02:00

Commit Message:
PELROCK: Make sure to free all pointers in room, pelrock and resources

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 8a3a2244a70..1508596e297 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -83,6 +83,7 @@ PelrockEngine::~PelrockEngine() {
 	if(_currentContext.movementBuffer) {
 		free(_currentContext.movementBuffer);
 	}
+	_saveThumbnail.free();
 }
 
 uint32 PelrockEngine::getFeatures() const {
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 8eccb2bff5e..f15c55c7b1d 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -125,6 +125,7 @@ ResourceManager::~ResourceManager() {
 	}
 
 	delete[] _inventoryIcons;
+	clearSpecialAnim();
 }
 
 void ResourceManager::loadCursors() {
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 22c0aa6c298..4b4ab9abba6 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -47,6 +47,9 @@ RoomManager::~RoomManager() {
 	if (_passerByAnims) {
 		delete _passerByAnims;
 	}
+	if(_conversationData) {
+		delete[] _conversationData;
+	}
 }
 
 void RoomManager::clearTalkingAnims() {


Commit: 1653e473e92aa1ce317c4d7c09fcb98f32d8a15b
    https://github.com/scummvm/scummvm/commit/1653e473e92aa1ce317c4d7c09fcb98f32d8a15b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:46+02:00

Commit Message:
PELROCK: Moves sticker pixel data into its own structure

Changed paths:
    engines/pelrock/graphics.cpp
    engines/pelrock/graphics.h
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/room.cpp
    engines/pelrock/room.h
    engines/pelrock/types.h


diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index 6d8b3e1173f..ec7637a4885 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -301,27 +301,26 @@ void GraphicsManager::animateRotatePalette(PaletteAnim *anim) {
 void GraphicsManager::placeStickersFirstPass() {
 	// also place temporary stickers
 	for (uint i = 0; i < g_engine->_room->_roomStickers.size(); i++) {
-		Sticker sticker = g_engine->_room->_roomStickers[i];
-		placeSticker(sticker);
+		placeSticker(g_engine->_room->_roomStickers[i], g_engine->_room->_roomStickerPixelData[i]);
 	}
 }
 
 void GraphicsManager::placeStickersSecondPass() {
 	// Some stickers need to be placed AFTER sprites, hardcoded in the original
 	if (g_engine->_room->_currentRoomNumber == 3) {
-		for (uint i = 0; i < g_engine->_state->stickersPerRoom[3].size(); i++) {
-			if (g_engine->_state->stickersPerRoom[3][i].stickerIndex == 14) {
-				placeSticker(g_engine->_state->stickersPerRoom[3][i]);
+		for (uint i = 0; i < g_engine->_room->_roomStickers.size(); i++) {
+			if (g_engine->_room->_roomStickers[i].stickerIndex == 14) {
+				placeSticker(g_engine->_room->_roomStickers[i], g_engine->_room->_roomStickerPixelData[i]);
 				break;
 			}
 		}
 	}
 }
 
-void GraphicsManager::placeSticker(Sticker sticker) {
+void GraphicsManager::placeSticker(Sticker &sticker, byte *pixels) {
 	// Wrap sticker data as a surface and blit (no transparency - all pixels copied)
 	Graphics::Surface stickerSurf;
-	stickerSurf.init(sticker.w, sticker.h, sticker.w, sticker.stickerData, Graphics::PixelFormat::createFormatCLUT8());
+	stickerSurf.init(sticker.w, sticker.h, sticker.w, pixels, Graphics::PixelFormat::createFormatCLUT8());
 	// Clip to screen bounds
 	Common::Rect destRect(sticker.x, sticker.y, sticker.x + sticker.w, sticker.y + sticker.h);
 	Common::Rect screenRect(0, 0, 640, 400);
diff --git a/engines/pelrock/graphics.h b/engines/pelrock/graphics.h
index 1616c0aa215..2609c83d7fa 100644
--- a/engines/pelrock/graphics.h
+++ b/engines/pelrock/graphics.h
@@ -57,7 +57,7 @@ public:
 	// Sticker rendering
 	void placeStickersFirstPass();
 	void placeStickersSecondPass();
-	void placeSticker(Sticker sticker);
+	void placeSticker(Sticker &sticker, byte *pixels);
 
 	// Palette animations
 	void updatePaletteAnimations();
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index f15c55c7b1d..83d09536b6a 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -539,12 +539,23 @@ Pelrock::Sticker ResourceManager::getSticker(int stickerIndex) {
 	sticker.w = alfred6File.readByte();
 	sticker.h = alfred6File.readByte();
 	sticker.stickerIndex = stickerIndex;
-	sticker.stickerData = new byte[sticker.w * sticker.h];
-	alfred6File.read(sticker.stickerData, sticker.w * sticker.h);
 	alfred6File.close();
 	return sticker;
 }
 
+byte *ResourceManager::loadStickerPixels(const Sticker &sticker) {
+	Common::File alfred6File;
+	if (!alfred6File.open("ALFRED.6")) {
+		error("Couldnt find file ALFRED.6");
+	}
+	uint32 pixelOffset = stickerOffsets[sticker.stickerIndex] + 6; // skip x(2)+y(2)+w(1)+h(1)
+	alfred6File.seek(pixelOffset, SEEK_SET);
+	byte *pixels = new byte[sticker.w * sticker.h];
+	alfred6File.read(pixels, sticker.w * sticker.h);
+	alfred6File.close();
+	return pixels;
+}
+
 InventoryObject ResourceManager::getIconForObject(byte objectIndex) {
 	byte iconIndex = 0;
 	if (objectIndex < 59) {
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index 6dfb6c06879..6242a991895 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -65,6 +65,7 @@ public:
 	Common::Array<Common::StringArray> getCredits();
 	Common::Array<Common::Array<Common::String>> processTextData(byte *data, size_t size, bool decode = false);
 	Sticker getSticker(int stickerIndex);
+	byte *loadStickerPixels(const Sticker &sticker);
 	InventoryObject getIconForObject(byte index);
 	byte *loadExtra();
 
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 4b4ab9abba6..c0a6e6b78b2 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -34,6 +34,7 @@ RoomManager::RoomManager() {
 }
 
 RoomManager::~RoomManager() {
+	clearRoomStickerPixels();
 	if (_pixelsShadows != nullptr) {
 		free(_pixelsShadows);
 		_pixelsShadows = nullptr;
@@ -77,6 +78,14 @@ void RoomManager::clearAnims() {
 	}
 }
 
+void RoomManager::clearRoomStickerPixels() {
+	for (uint i = 0; i < _roomStickerPixelData.size(); i++) {
+		delete[] _roomStickerPixelData[i];
+	}
+	_roomStickerPixelData.clear();
+	_roomStickers.clear();
+}
+
 void RoomManager::loadWaterPaletteRemap() {
 	// Extra remap for water effect
 	Common::File exe;
@@ -140,16 +149,23 @@ void RoomManager::addSticker(int stickerId, int persist) {
 }
 
 void RoomManager::addStickerToRoom(byte room, int stickerId, int persist) {
-	Sticker sticker = g_engine->_res->getSticker(stickerId);
-	if (room == _currentRoomNumber && persist & PERSIST_TEMP) {
-		if (hasSticker(sticker.stickerIndex)) {
+	// Check for duplicate before loading
+	if (room == _currentRoomNumber && (persist & PERSIST_TEMP)) {
+		if (hasSticker(stickerId)) {
 			debug("Sticker %d already exists in room %d, skipping add", stickerId, room);
 			return;
 		}
-		_roomStickers.push_back(sticker);
 	}
+	Sticker stickerMetadata = g_engine->_res->getSticker(stickerId); // metadata only
 	if (persist & PERSIST_PERM) {
-		g_engine->_state->stickersPerRoom[room].push_back(sticker);
+		// stickersPerRoom stores persistent metadata only
+		g_engine->_state->stickersPerRoom[room].push_back(stickerMetadata);
+	}
+
+	if (room == _currentRoomNumber && (persist & PERSIST_TEMP)) {
+		// Load pixel data only when the sticker is visible in the current room
+		_roomStickers.push_back(stickerMetadata);
+		_roomStickerPixelData.push_back(g_engine->_res->loadStickerPixels(stickerMetadata));
 	}
 }
 
@@ -158,15 +174,17 @@ void RoomManager::removeSticker(int stickerId) {
 }
 
 void RoomManager::removeStickerFromRoom(byte room, int stickerId) {
-	// First check and remove from room stickers
+	// Remove from current room view and free its pixel data
 	for (uint i = 0; i < _roomStickers.size(); i++) {
 		if (_roomStickers[i].stickerIndex == stickerId) {
+			delete[] _roomStickerPixelData[i];
+			_roomStickerPixelData.remove_at(i);
 			_roomStickers.remove_at(i);
-			return;
+			break;
 		}
 	}
 
-	// Then check and remove from persisted stickers
+	// Remove from persisted metadata store
 	for (uint i = 0; i < g_engine->_state->stickersPerRoom[room].size(); i++) {
 		if (g_engine->_state->stickersPerRoom[room][i].stickerIndex == stickerId) {
 			g_engine->_state->stickersPerRoom[room].remove_at(i);
@@ -584,7 +602,6 @@ void RoomManager::resetConversationStates(byte roomNumber, byte *conversationDat
 
 void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 
-	_roomStickers.clear();
 	_prevRoomNumber = _currentRoomNumber;
 	_currentRoomNumber = roomNumber;
 	int roomOffset = roomNumber * kRoomStructSize;
@@ -626,7 +643,12 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	_currentRoomExits = loadExits(pair10, pair10size);
 	_currentRoomWalkboxes = loadWalkboxes(pair10, pair10size);
 	_scaleParams = loadScalingParams(pair10, pair10size);
+
+	clearRoomStickerPixels(); // free all sticker buffers first
 	_roomStickers = g_engine->_state->stickersPerRoom[roomNumber];
+	for (uint i = 0; i < _roomStickers.size(); i++) {
+		_roomStickerPixelData.push_back(g_engine->_res->loadStickerPixels(_roomStickers[i]));
+	}
 	// Pair 11 is the palette, already loaded
 
 	// Pair 12 - Room Texts
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 5b14be2505d..030acd28b46 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -80,6 +80,7 @@ public:
 	~RoomManager();
 	void clearTalkingAnims();
 	void clearAnims();
+	void clearRoomStickerPixels();
 	void loadRoomMetadata(Common::File *roomFile, int roomNumber);
 	/**
 	 * Passer by animations are animations of characters that merely traverse the scene as ambient
@@ -182,6 +183,7 @@ public:
 	byte *_conversationData = nullptr;
 	size_t _conversationDataSize = 0;
 	Common::Array<Sticker> _roomStickers;
+	Common::Array<byte *> _roomStickerPixelData;
 	uint32 _conversationOffset;
 
 private:
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index e586f2dfa7f..3a47c21c8b0 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -448,7 +448,6 @@ struct Sticker {
 	uint16 y;
 	byte w;
 	byte h;
-	byte *stickerData;
 };
 
 struct PaletteAnimRotate {


Commit: 6c8e9d1496c933572d973c6b9d6af9401a4cac70
    https://github.com/scummvm/scummvm/commit/6c8e9d1496c933572d973c6b9d6af9401a4cac70
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:46+02:00

Commit Message:
PELROCK: Fix leak when loading game, and on conversation exit

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/saveload.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 302c9caa9a6..048b0174a4b 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -648,6 +648,7 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 			}
 
 			if (!foundExpectedLevel) {
+				delete choices;
 				break;
 			}
 		}
@@ -673,6 +674,9 @@ void DialogManager::startConversation(const byte *conversationData, uint32 dataS
 		}
 
 		state.position = processChoiceSelection(conversationData, dataSize, choices, selectedIndex, state);
+		if (choices != _currentChoices) {
+			delete choices;
+		}
 	}
 
 	debug("Conversation ended");
diff --git a/engines/pelrock/saveload.cpp b/engines/pelrock/saveload.cpp
index b159da5e760..008d1356b50 100644
--- a/engines/pelrock/saveload.cpp
+++ b/engines/pelrock/saveload.cpp
@@ -366,6 +366,7 @@ void PelrockEngine::loadGame(SaveGameData &saveGame) {
 	_alfredState.x = saveGame.alfredX;
 	_alfredState.y = saveGame.alfredY;
 	_alfredState.direction = (AlfredDirection)saveGame.alfredDir;
+	delete _state;
 	_state = saveGame.gameState;
 
 	setScreenAndPrepare(saveGame.currentRoom, _alfredState.direction);
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 3a47c21c8b0..5375dbc3fd6 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -153,7 +153,7 @@ struct AlfredSpecialAnim {
 
 	~AlfredSpecialAnim() {
 		if (animData) {
-			free(animData);
+			delete[] animData;
 			animData = nullptr;
 		}
 	}
@@ -602,6 +602,7 @@ struct GameStateData {
 	}
 
 	~GameStateData() {
+		clearBranches();
 		delete[] conversationCurrentRoot;
 		conversationCurrentRoot = nullptr;
 	}


Commit: 5a73a5c07a07d13a5dd276e4af7bf1aff02b9c6e
    https://github.com/scummvm/scummvm/commit/5a73a5c07a07d13a5dd276e4af7bf1aff02b9c6e
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:46+02:00

Commit Message:
PELROCK: Fix error when converting offsets

Changed paths:
    engines/pelrock/offsets.h


diff --git a/engines/pelrock/offsets.h b/engines/pelrock/offsets.h
index 2e5a66f8a6e..770032ee022 100644
--- a/engines/pelrock/offsets.h
+++ b/engines/pelrock/offsets.h
@@ -229,7 +229,7 @@ enum TextStringId {
 
 // ALFRED.7 extra screen data (file offsets given per entry in extraScreens[])
 static const ExtraScreen extraScreens[] = {
-	{0x00007984, 0x000305A2, 8},      // 0 - Portrait above bed
+	{0x0000000, 0x0007984, 8},      // 0 - Portrait above bed
 	{0x001A9EE, 0x00305A2, 8},        // 1 - Computer screen
 	{0x00647C3, 0x007B6B1, 4},        // 2 - Alfred circle
 	{0x006FBCD, 0x007B6B1, 8},        // 3 - Recipe
@@ -237,13 +237,13 @@ static const ExtraScreen extraScreens[] = {
 	{0x009237B, 0x00B0EE7, 8},        // 5 - Tablet
 	{0x00B11F1, 0x00DE011, 8},        // 6 - Map
 	{0x00FFC47, 0x01180C9, 8},        // 7 - Girl book
-	{0x118649,  0x135A13, 8},         // 8 - Unknown
-	{0x152A88,  0x15BFC8, 8},         // 9 - Portrait
-	{0x299E0C,  0x2B3C3C, 8},         // 10 - CD
-	{0x2B3F1C,  0x2D5B18, 8},         // 11 - Pyramid map
-	{0x232B1A,  0x237C28, 8},         // 12 - CENSORED
-	{0x226358,  0x236AA8, 8},         // 13 - Background book
-	{0x2EBD12,  0x309E40, 8}          // 14 - Ending
+	{0x01183C9, 0x01358F3, 8},         // 8 - Spellbook
+	{0x0152A88, 0x015BFC8, 8},         // 9 - Portrait
+	{0x0299E8C, 0x02B3B7C, 8},         // 10 - CD
+	{0x02B3E7C, 0x02D5898, 8},         // 11 - Pyramid map
+	{0x02331EA, 0x0236AA8, 8},         // 12 - CENSORED
+	{0x0226358, 0x0236AA8, 8},         // 13 - Background book
+	{0x02EAA32, 0x0309A80, 8}          // 14 - Ending
 };
 
 


Commit: fab16d9824e78f09d90b63a2f68639798f01eef4
    https://github.com/scummvm/scummvm/commit/fab16d9824e78f09d90b63a2f68639798f01eef4
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:47+02:00

Commit Message:
PELROCK: Consolidate drawColoredTexts functions in graphics

Changed paths:
    engines/pelrock/graphics.cpp
    engines/pelrock/graphics.h
    engines/pelrock/spellbook.cpp


diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index ec7637a4885..35157e4cbe9 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -149,41 +149,6 @@ void GraphicsManager::drawColoredText(Graphics::ManagedSurface *screen, const Co
 	}
 }
 
-void GraphicsManager::drawColoredText(Graphics::ManagedSurface &buf, const Common::String &text, int x, int y, int w, byte &defaultColor, Graphics::Font *font) {
-
-	Graphics::Surface tempSurface;
-	Common::Rect r = font->getBoundingBox(text); // Ensure font metrics are loaded before creating surface
-
-	tempSurface.create(r.width(), r.height(), Graphics::PixelFormat::createFormatCLUT8());
-
-	int currentX = x;
-
-	Common::String segment;
-	for (uint i = 0; i < text.size(); i++) {
-		if (text[i] == '@' && i + 1 < text.size()) {
-			// Draw accumulated segment
-			if (!segment.empty()) {
-				font->drawString(&tempSurface, segment, currentX, y, w, defaultColor);
-				currentX += font->getStringWidth(segment);
-				segment.clear();
-			}
-			defaultColor = text[i + 1];
-			i++; // skip color code
-		} else {
-			segment += text[i];
-		}
-	}
-
-	// Draw remaining segment
-	if (!segment.empty()) {
-		font->drawString(&tempSurface, segment, currentX, y, w, defaultColor);
-	}
-
-	// Use transBlitFrom to blit non-zero pixels
-	buf.transBlitFrom(tempSurface, Common::Point(x, y), 0);
-	tempSurface.free();
-}
-
 void GraphicsManager::drawColoredTexts(Graphics::ManagedSurface *surface, const Common::StringArray &text, int x, int y, int w, int yPadding, Graphics::Font *font) {
 	int currentX = x;
 	byte currentColor = 255;
@@ -193,15 +158,6 @@ void GraphicsManager::drawColoredTexts(Graphics::ManagedSurface *surface, const
 	}
 }
 
-void GraphicsManager::drawColoredTexts(Graphics::ManagedSurface &buf, const Common::StringArray &text, int x, int y, int w, int yPadding, Graphics::Font *font) {
-	int currentX = x;
-	byte currentColor = 255;
-
-	for (uint i = 0; i < text.size(); i++) {
-		drawColoredText(buf, text[i], currentX, y + i * (font->getFontHeight() + yPadding), w, currentColor, font);
-	}
-}
-
 void GraphicsManager::copyBackgroundToBuffer() {
 	g_engine->_compositeBuffer.blitFrom(g_engine->_currentBackground);
 }
diff --git a/engines/pelrock/graphics.h b/engines/pelrock/graphics.h
index 2609c83d7fa..910c4d87eb2 100644
--- a/engines/pelrock/graphics.h
+++ b/engines/pelrock/graphics.h
@@ -46,9 +46,7 @@ public:
 
 	// Text rendering
 	void drawColoredText(Graphics::ManagedSurface *screen, const Common::String &text, int x, int y, int w, byte &defaultColor, Graphics::Font *font);
-	void drawColoredText(Graphics::ManagedSurface &buf, const Common::String &text, int x, int y, int w, byte &defaultColor, Graphics::Font *font);
 	void drawColoredTexts(Graphics::ManagedSurface *surface, const Common::StringArray &text, int x, int y, int w, int yPadding, Graphics::Font *font);
-	void drawColoredTexts(Graphics::ManagedSurface &buf, const Common::StringArray &text, int x, int y, int w, int yPadding, Graphics::Font *font);
 
 	// Frame / background management
 	void copyBackgroundToBuffer();
diff --git a/engines/pelrock/spellbook.cpp b/engines/pelrock/spellbook.cpp
index 50497244b30..83d023d31a5 100644
--- a/engines/pelrock/spellbook.cpp
+++ b/engines/pelrock/spellbook.cpp
@@ -122,13 +122,10 @@ void SpellBook::drawScreen() {
 
 	if (_spell != nullptr) {
 		drawSpriteToBuffer(_compositeScreen, _spell->image, 168, 143, 119, 99, 207);
-		g_engine->_graphics->drawColoredTexts(_compositeScreen, _spell->text, textX, textY, 640, 0, g_engine->_smallFont);
+		g_engine->_graphics->drawColoredTexts(&_compositeScreen, _spell->text, textX, textY, 640, 0, g_engine->_smallFont);
 	}
 
 	g_engine->_screen->blitFrom(_compositeScreen);
-	if (_spell != nullptr) {
-		g_engine->_graphics->drawColoredTexts(g_engine->_screen, _spell->text, textX, textY, 640, 0, g_engine->_smallFont);
-	}
 }
 
 void SpellBook::loadBackground() {


Commit: c1e9301f6b3ef53cae2c65b9e559de6cea72b114
    https://github.com/scummvm/scummvm/commit/c1e9301f6b3ef53cae2c65b9e559de6cea72b114
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:47+02:00

Commit Message:
PELROCK: Make crocodile sprite stick in crocodile sequence

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 879fdef3099..57e72fa55f5 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1226,13 +1226,20 @@ void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
 		_alfredState.x -= 10;
 		_alfredState.y += 20;
 		playAlfredSpecialAnim(5);
+		{
+			// Copy crocodile into background so it sticks during fade
+			static const int srcX = 189, srcY = 260;
+			static const int copyW = 127, copyH = 80;
+			Common::Rect copyRect(srcX, srcY, srcX + copyW, srcY + copyH);
+			_currentBackground.blitFrom(_compositeBuffer, copyRect, Common::Point(srcX, srcY));
+		}
 		_alfredState.animState = ALFRED_SKIP_DRAWING;
 		_sound->playSound(_room->_roomSfx[0], 0); // Belch
 		waitForSoundEnd();
-		_graphics->fadeToBlack(10);
+		_graphics->fadeToBlack(20);
+		_graphics->clearScreen();
 		_alfredState.x = 300;
 		_alfredState.y = 238;
-		waitForSoundEnd();
 		_alfredState.animState = ALFRED_IDLE;
 		setScreenAndPrepare(28, ALFRED_DOWN);
 		_dialog->say(_res->_ingameTexts[kTextQueOscuroEstaEsto]);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 1508596e297..a7d0daa4989 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -2051,17 +2051,13 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 }
 
 void PelrockEngine::pyramidCollapse() {
-	// === Pyramid Collapse Sequence (Room 36 per-frame handler at 0x1098F) ===
-	// Binary: sprite index 2 = collapse animation, sprite index 0 = NPC guard.
-	// Original sprite table indices are offset by 2 from ScummVM indices due
-	// to the 2 header sprite slots in the room data.
 
-	// Hide NPC initially — binary sets sprite_2 field 0x21 = 0xFF (zOrder = 255)
+	// Hide NPC initially
 	Sprite *npc = _room->findSpriteByIndex(0);
 	if (npc)
 		npc->zOrder = 255;
 
-	// Start collapse animation — binary sets sprite_4 field 0x21 = 0xFE (zOrder = 254)
+	// Start collapse animation
 	Sprite *collapseSprite = _room->findSpriteByIndex(2);
 	if (collapseSprite)
 		collapseSprite->zOrder = 254;
@@ -2069,8 +2065,7 @@ void PelrockEngine::pyramidCollapse() {
 	// Play collapse sound
 	_sound->playSound("QUAKE1ZZ.SMP", 0);
 
-	// ----- PHASE 1: Wait for collapse animation frame 5 -----
-	// Binary: loop sleep(0x69) + tick + render, check sprite_4 field 0x20 == 5
+	// Wait for collapse animation frame 5
 	while (!shouldQuit()) {
 		_events->pollEvent();
 		renderScene(OVERLAY_NONE);
@@ -2078,17 +2073,15 @@ void PelrockEngine::pyramidCollapse() {
 		g_system->delayMillis(10);
 		collapseSprite = _room->findSpriteByIndex(2);
 		if (!collapseSprite || collapseSprite->animData[collapseSprite->curAnimIndex].curFrame >= 5) {
-			collapseSprite->zOrder = 255; // Hide collapse animation sprite after frame 5
+			collapseSprite->zOrder = 255; // Hide collapse animation sprite
 			break;
 		}
 	}
-	debug("Collapse animation reached frame 5, applying background changes");
 
-	// ----- PHASE 2: Background tile copies (hide pyramid top) -----
-	// Copy 1: 99×45 from secondary buffer to front buffer (fills collapsed area)
+	// Background tile copies to have collapsed pyramid stick
+	// copy 99×45 from secondary buffer to front buffer
 	{
 		static const int srcX = 240, srcY = 145;
-		// static const int dstX = 510, dstY = 33;
 		static const int copyW = 99, copyH = 45;
 		Common::Rect copyRect(srcX, srcY, srcX + copyW, srcY + copyH);
 		_currentBackground.blitFrom(_compositeBuffer, copyRect, Common::Point(srcX, srcY));


Commit: 920635b2f4a1ebb4407efd4cbaf2d475af22d168
    https://github.com/scummvm/scummvm/commit/920635b2f4a1ebb4407efd4cbaf2d475af22d168
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:47+02:00

Commit Message:
PELROCK: Improvements on idle animation and screensaver minigame

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index a7d0daa4989..420ba41b810 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -77,10 +77,10 @@ PelrockEngine::~PelrockEngine() {
 	delete[] _inventoryOverlayState.arrows[0];
 	delete[] _inventoryOverlayState.arrows[1];
 	// Free path-finding buffers (allocated via malloc in findPath)
-	if(_currentContext.pathBuffer) {
+	if (_currentContext.pathBuffer) {
 		free(_currentContext.pathBuffer);
 	}
-	if(_currentContext.movementBuffer) {
+	if (_currentContext.movementBuffer) {
 		free(_currentContext.movementBuffer);
 	}
 	_saveThumbnail.free();
@@ -444,12 +444,12 @@ void PelrockEngine::maybeHaveDogPee() {
 	if (_room->_currentRoomNumber != 19) {
 		return;
 	}
-	Sprite * dog = _room->findSpriteByIndex(2);
+	Sprite *dog = _room->findSpriteByIndex(2);
 
-	if(_alfredState.x < 146 && !_isDogPeeing) {
+	if (_alfredState.x < 146 && !_isDogPeeing) {
 		_isDogPeeing = true;
 		dog->animData[0].nframes = 24;
-		while(!shouldQuit() && dog->animData[0].curFrame < 23) {
+		while (!shouldQuit() && dog->animData[0].curFrame < 23) {
 			_events->pollEvent();
 			renderScene(OVERLAY_NONE);
 
@@ -631,7 +631,7 @@ void PelrockEngine::checkMouse() {
 		} else if (_inventoryOverlayState.isActive && _inventoryOverlayState.posInInventorySelectionArea(_events->_releaseX, _events->_releaseY)) {
 			int item = checkMouseClickInventoryOverlay(_events->_releaseX, _events->_releaseY);
 			_state->selectedInventoryItem = item;
-			if(_actionPopupState.isAlfredUnder) {
+			if (_actionPopupState.isAlfredUnder) {
 				useOnAlfred(item);
 			} else if (_currentHotspot != nullptr) {
 				walkAndAction(_currentHotspot, ITEM);
@@ -760,11 +760,24 @@ void PelrockEngine::lookAt(HotSpot *hotspot) {
 }
 
 void PelrockEngine::chooseAlfredStateAndDraw() {
-	if (_alfredState.idleFrameCounter++ >= kAlfredIdleAnimationFrameCount &&
-		_alfredState.animState == ALFRED_IDLE &&
-		(_alfredState.direction == ALFRED_LEFT || _alfredState.direction == ALFRED_RIGHT)) {
-		_alfredState.idleFrameCounter = 0;
-		_alfredState.setState(ALFRED_COMB);
+	if (_alfredState.animState != ALFRED_IDLE &&
+		 _alfredState.animState != ALFRED_COMB // need to exclude comb so that screen saver can still engage
+	) {
+		_alfredState.resetIdles(); // reset idle frame counter when not idle
+	} else {
+
+		if (_alfredState.idleFrameCounter++ >= kAlfredIdleAnimationFrameCount &&
+			_alfredState.animState == ALFRED_IDLE &&
+			(_alfredState.direction == ALFRED_LEFT || _alfredState.direction == ALFRED_RIGHT)) {
+			_alfredState.idleFrameCounter = 0;
+			_alfredState.setState(ALFRED_COMB);
+		}
+		if(_alfredState.screenSaverFrameCounter++ >= kAlfredIdleScreenSaverFrameCount &&
+			_alfredState.animState == ALFRED_IDLE) {
+			SlidingPuzzle slidingPuzzle(_events, _sound);
+			_alfredState.screenSaverFrameCounter = 0;
+			slidingPuzzle.run();
+		}
 	}
 
 	switch (_alfredState.animState) {
@@ -1189,7 +1202,7 @@ void PelrockEngine::paintDebugLayer() {
 }
 
 void PelrockEngine::checkLongMouseClick(int x, int y) {
-	_alfredState.idleFrameCounter = 0;
+	_alfredState.resetIdles(); // reset idle frame counter on interaction
 	int hotspotIndex = isHotspotUnder(x, y);
 	bool alfredUnder = isAlfredUnder(x, y);
 	if ((hotspotIndex != -1 || alfredUnder) && !_actionPopupState.isActive) {
@@ -1308,7 +1321,7 @@ void PelrockEngine::showActionBalloon(int posx, int posy, int curFrame) {
 	if (_inventoryOverlayState.isActive) {
 		// find selectedInventoryItem index in inventoryItems, set invStartingPos to that index if found, otherwise 0
 		int scrollPos = getScrollPositionForItem(_state->selectedInventoryItem);
-		if(_inventoryOverlayState.invStartingPos == -1) {
+		if (_inventoryOverlayState.invStartingPos == -1) {
 			_inventoryOverlayState.invStartingPos = scrollPos != -1 ? scrollPos : 0;
 		}
 		showInventoryOverlay();
@@ -1411,9 +1424,9 @@ void PelrockEngine::showInventoryOverlay() {
 	uint invSize = _state->inventoryItems.size();
 	// invStartingPos is an ITEM index (not a page number).
 	// The original game scrolls 1 item at a time, not 1 page at a time.
-	if(_inventoryOverlayState.invStartingPos == -1) {
+	if (_inventoryOverlayState.invStartingPos == -1) {
 		_inventoryOverlayState.invStartingPos = getScrollPositionForItem(_state->selectedInventoryItem);
-		if(_inventoryOverlayState.invStartingPos == -1) {
+		if (_inventoryOverlayState.invStartingPos == -1) {
 			_inventoryOverlayState.invStartingPos = 0;
 		}
 	}
@@ -1585,7 +1598,7 @@ void PelrockEngine::walkTo(int x, int y) {
 }
 
 void PelrockEngine::walkAndAction(HotSpot *hotspot, VerbIcon action) {
-	if(hotspot == nullptr) {
+	if (hotspot == nullptr) {
 		return;
 	}
 	_disableAction = true;
@@ -1678,7 +1691,7 @@ void PelrockEngine::checkMouseClick(int x, int y) {
 	_queuedAction = QueuedAction{NO_ACTION, -1, false, false};
 	_actionPopupState.isActive = false;
 	_currentHotspot = nullptr;
-	_alfredState.idleFrameCounter = 0;
+	_alfredState.resetIdles();
 	int hotspotIndex = isHotspotUnder(_events->_mouseX, _events->_mouseY);
 	bool isHotspotUnder = false;
 	if (hotspotIndex != -1) {
@@ -1696,7 +1709,7 @@ void PelrockEngine::changeCursor(Cursor cursor) {
 }
 
 void PelrockEngine::checkMouseHover() {
-	if(_actionPopupState.isActive) {
+	if (_actionPopupState.isActive) {
 		return;
 	}
 
@@ -1853,7 +1866,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 	case 24: {
 		_room->findSpriteByIndex(1)->numAnims = 1;
 
-		if(_state->hasInventoryItem(88) && _state->getFlag(FLAG_PIGEON_DEAD) == false) {
+		if (_state->hasInventoryItem(88) && _state->getFlag(FLAG_PIGEON_DEAD) == false) {
 			_dialog->say(_res->_ingameTexts[kTextProbarLibro]);
 			playAlfredSpecialAnim(0);
 			Sprite *pigeons = _room->findSpriteByIndex(1);
@@ -1861,7 +1874,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			pigeons->curAnimIndex = 0;
 			pigeons->disableAfterSequence = true;
 			pigeons->animData[0].curFrame = 0;
-			while(!g_engine->shouldQuit() && pigeons->zOrder != 255) {
+			while (!g_engine->shouldQuit() && pigeons->zOrder != 255) {
 				_events->pollEvent();
 				renderScene();
 				_screen->update();
@@ -2041,7 +2054,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 	case 53:
 	case 54:
 		initGodsSequences(_room->_currentRoomNumber);
-		if(roomNumber == 52) {
+		if (roomNumber == 52) {
 			_room->addStickerToRoom(52, 105);
 		}
 		break;
@@ -2303,7 +2316,7 @@ void PelrockEngine::credits() {
 	Common::Array<Common::StringArray> creditTexts = _res->getCredits();
 	Common::Array<int> creditsSpeakerId;
 	// Preprocess credit texts: extract speaker IDs and apply word wrapping
-	for(uint i = 0; i < creditTexts.size(); i++) {
+	for (uint i = 0; i < creditTexts.size(); i++) {
 		byte speakerId;
 		_dialog->processColorAndTrim(creditTexts[i], speakerId);
 		creditsSpeakerId.push_back(speakerId);
@@ -2325,7 +2338,7 @@ void PelrockEngine::credits() {
 		for (int page = 0; page < kNumCreditPages && !shouldQuit(); page++) {
 			// loads screen
 			setScreen(kCreditRooms[page]);
-			if(kCreditRooms[page] == 24) {
+			if (kCreditRooms[page] == 24) {
 				Sprite *pigeons = _room->findSpriteByIndex(1);
 				pigeons->disableAfterSequence = true;
 			}
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 5375dbc3fd6..1e59f830b86 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -84,7 +84,8 @@ const int kAlfredFrameHeight = 102;
 const int kTalkAnimationSpeed = 2;   // Frames per update
 const int kAlfredAnimationSpeed = 2; // Frames per update
 
-const int kAlfredIdleAnimationFrameCount = 300;
+const int kAlfredIdleAnimationFrameCount = 300; // comb animation plays every ~16 seconds of idle alfred
+const int kAlfredIdleScreenSaverFrameCount = 1090; // screen saver shows up after 60 seconds
 
 const int kInventoryPageSize = 10;
 
@@ -192,12 +193,18 @@ struct AlfredState {
 	byte w = kAlfredFrameWidth;
 	byte h = kAlfredFrameHeight;
 	int idleFrameCounter = 0;
+	int screenSaverFrameCounter = 0;
 	bool isWalkingCancelable = true;
 
 	void setState(AlfredAnimState nextState) {
 		animState = nextState;
 		curFrame = 0;
 	}
+
+	void resetIdles() {
+		idleFrameCounter = 0;
+		screenSaverFrameCounter = 0;
+	}
 };
 
 struct ShakeEffectState {


Commit: c2cf7a3d40e72ab81a3a6fb3e42954804ca2def8
    https://github.com/scummvm/scummvm/commit/c2cf7a3d40e72ab81a3a6fb3e42954804ca2def8
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:48+02:00

Commit Message:
PELROCK: Fix scaling algorithm so feet arent cropped

Changed paths:
    engines/pelrock/graphics.cpp


diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index 35157e4cbe9..feaf410cb64 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -404,7 +404,7 @@ ScaleCalculation GraphicsManager::calculateScaling(int yPos, ScalingParams scali
 }
 
 byte *GraphicsManager::scale(int scaleY, int finalWidth, int finalHeight, byte *buf) {
-	// The scaling table is indexed by how many scanlines to skip (scaleY), not by final height
+	// The table marks which source rows to skip: non-zero = skip.
 	int scaleIndex = scaleY;
 	if (scaleIndex >= (int)_heightScalingTable.size()) {
 		scaleIndex = _heightScalingTable.size() - 1;
@@ -412,69 +412,26 @@ byte *GraphicsManager::scale(int scaleY, int finalWidth, int finalHeight, byte *
 	if (scaleIndex < 0) {
 		scaleIndex = 0;
 	}
-	int linesToSkip = kAlfredFrameHeight - finalHeight;
 
 	byte *finalBuf = new byte[finalWidth * finalHeight];
+	memset(finalBuf, 255, finalWidth * finalHeight);
 
-	if (linesToSkip > 0) {
-		int skipInterval = kAlfredFrameHeight / linesToSkip;
-		Common::Array<float> idealSkipPositions;
-		for (int i = 0; i < linesToSkip; i++) {
-			float idealPos = (i + 0.5f) * skipInterval;
-			idealSkipPositions.push_back(idealPos);
-		}
-
-		Common::Array<int> tableSkipPositions;
-		for (int scanline = 0; scanline < kAlfredFrameHeight; scanline++) {
-			if (_heightScalingTable[scaleIndex][scanline] != 0) {
-				tableSkipPositions.push_back(scanline);
-			}
-		}
-
-		Common::Array<int> skipTheseLines;
-		for (size_t i = 0; i < idealSkipPositions.size(); i++) {
-			float idealPos = idealSkipPositions[i];
-			int closest = -1;
-			int minDiff = INT32_MAX;
-			for (size_t j = 0; j < tableSkipPositions.size(); j++) {
-				int candidate = tableSkipPositions[j];
-				int diff = static_cast<int>(abs(candidate - idealPos));
-				if (diff < minDiff) {
-					minDiff = diff;
-					closest = candidate;
-				}
-			}
-			if (closest != -1) {
-				skipTheseLines.push_back(closest);
-			}
-			if (skipTheseLines.size() >= static_cast<size_t>(linesToSkip)) {
-				break;
-			}
-		}
-
+	if (scaleIndex > 0) {
 		int outY = 0;
-		for (int srcY = 0; srcY < kAlfredFrameHeight; srcY++) {
-			bool skipLine = false;
-			for (size_t skipIdx = 0; skipIdx < skipTheseLines.size(); ++skipIdx) {
-				if (skipTheseLines[skipIdx] == srcY) {
-					skipLine = true;
-					break;
-				}
+		for (int srcY = 0; srcY < kAlfredFrameHeight && outY < finalHeight; srcY++) {
+			// Skip rows where the height scaling table says to skip (non-zero value)
+			if (_heightScalingTable[scaleIndex][srcY] != 0) {
+				continue;
 			}
-			if (!skipLine) {
-				for (int outX = 0; outX < finalWidth; outX++) {
-					int srcX = static_cast<int>(outX * kAlfredFrameWidth / finalWidth);
-					if (srcX >= kAlfredFrameWidth) {
-						srcX = kAlfredFrameWidth - 1;
-					}
-					int srcIndex = srcY * kAlfredFrameWidth + srcX;
-					int outIndex = outY * finalWidth + outX;
-					if (outIndex >= finalWidth * finalHeight || srcIndex >= kAlfredFrameWidth * kAlfredFrameHeight) {
-					} else
-						finalBuf[outIndex] = buf[srcIndex];
+
+			for (int outX = 0; outX < finalWidth; outX++) {
+				int srcX = outX * kAlfredFrameWidth / finalWidth;
+				if (srcX >= kAlfredFrameWidth) {
+					srcX = kAlfredFrameWidth - 1;
 				}
-				outY++;
+				finalBuf[outY * finalWidth + outX] = buf[srcY * kAlfredFrameWidth + srcX];
 			}
+			outY++;
 		}
 	} else {
 		Common::copy(buf, buf + (kAlfredFrameWidth * kAlfredFrameHeight), finalBuf);


Commit: c1285621445640702e87a0806a58db9e55fdfb69
    https://github.com/scummvm/scummvm/commit/c1285621445640702e87a0806a58db9e55fdfb69
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:48+02:00

Commit Message:
PELROCK: Add option to disable screensaver

Changed paths:
    engines/pelrock/detection.h
    engines/pelrock/detection_tables.h
    engines/pelrock/metaengine.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/detection.h b/engines/pelrock/detection.h
index 921919f14e9..cffa9df0502 100644
--- a/engines/pelrock/detection.h
+++ b/engines/pelrock/detection.h
@@ -41,6 +41,7 @@ extern const ADGameDescription gameDescriptions[];
 #define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1
 #define GAMEOPTION_ALTERNATE_TIMING GUIO_GAMEOPTIONS2
 #define GAMEOPTION_PLAY_INTRO GUIO_GAMEOPTIONS3
+#define GAMEOPTION_DISABLE_SCREENSAVER GUIO_GAMEOPTIONS4
 
 } // End of namespace Pelrock
 
diff --git a/engines/pelrock/detection_tables.h b/engines/pelrock/detection_tables.h
index 57be38649ae..eb5c5c057cb 100644
--- a/engines/pelrock/detection_tables.h
+++ b/engines/pelrock/detection_tables.h
@@ -34,7 +34,7 @@ const ADGameDescription gameDescriptions[] = {
 		Common::ES_ESP,
 		Common::kPlatformDOS,
 		ADGF_UNSTABLE,
-		GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_ALTERNATE_TIMING, GAMEOPTION_PLAY_INTRO)
+		GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_ALTERNATE_TIMING, GAMEOPTION_PLAY_INTRO, GAMEOPTION_DISABLE_SCREENSAVER)
 	},
 
 	AD_TABLE_END_MARKER
diff --git a/engines/pelrock/metaengine.cpp b/engines/pelrock/metaengine.cpp
index e44f3ab20cc..a6468fe0341 100644
--- a/engines/pelrock/metaengine.cpp
+++ b/engines/pelrock/metaengine.cpp
@@ -62,6 +62,17 @@ static const ADExtraGuiOptionsMap optionsList[] = {
 			0
 		}
 	},
+	{
+		GAMEOPTION_DISABLE_SCREENSAVER,
+		{
+			_s("Disable screensaver"),
+			_s("Disable original screensaver slider mini-game every 60 seconds."),
+			"disable_screensaver",
+			false,
+			0,
+			0
+		}
+	},
 	AD_EXTRA_GUI_OPTIONS_TERMINATOR
 };
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 420ba41b810..52e6d53afcb 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -20,20 +20,14 @@
  */
 
 #include "common/config-manager.h"
-#include "common/endian.h"
 #include "common/events.h"
 #include "common/file.h"
 #include "common/scummsys.h"
-#include "common/system.h"
 #include "engines/util.h"
 #include "graphics/cursorman.h"
-#include "graphics/framelimiter.h"
 #include "graphics/paletteman.h"
-#include "image/pcx.h"
-#include "image/png.h"
-
-#include "backends/audiocd/audiocd.h"
 
+#include "pelrock.h"
 #include "pelrock/actions.h"
 #include "pelrock/computer.h"
 #include "pelrock/console.h"
@@ -98,7 +92,9 @@ bool PelrockEngine::isAlternateTiming() const {
 	return ConfMan.getBool("alternate_timing");
 }
 
-// Common::Array<Common::Array<Common::String>> wordWrap(Common::String text);
+bool PelrockEngine::isScreenSaverDisabled() const {
+	return ConfMan.getBool("disable_screensaver");
+}
 
 Common::Error PelrockEngine::run() {
 	// Initialize 320x200 paletted graphics mode
@@ -761,7 +757,7 @@ void PelrockEngine::lookAt(HotSpot *hotspot) {
 
 void PelrockEngine::chooseAlfredStateAndDraw() {
 	if (_alfredState.animState != ALFRED_IDLE &&
-		 _alfredState.animState != ALFRED_COMB // need to exclude comb so that screen saver can still engage
+		_alfredState.animState != ALFRED_COMB // need to exclude comb so that screen saver can still engage
 	) {
 		_alfredState.resetIdles(); // reset idle frame counter when not idle
 	} else {
@@ -772,11 +768,13 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			_alfredState.idleFrameCounter = 0;
 			_alfredState.setState(ALFRED_COMB);
 		}
-		if(_alfredState.screenSaverFrameCounter++ >= kAlfredIdleScreenSaverFrameCount &&
+		if (_alfredState.screenSaverFrameCounter++ >= kAlfredIdleScreenSaverFrameCount &&
 			_alfredState.animState == ALFRED_IDLE) {
-			SlidingPuzzle slidingPuzzle(_events, _sound);
-			_alfredState.screenSaverFrameCounter = 0;
-			slidingPuzzle.run();
+			if (!isScreenSaverDisabled()) {
+				SlidingPuzzle slidingPuzzle(_events, _sound);
+				_alfredState.screenSaverFrameCounter = 0;
+				slidingPuzzle.run();
+			}
 		}
 	}
 
@@ -1492,60 +1490,8 @@ int PelrockEngine::checkMouseClickInventoryOverlay(int x, int y) {
 }
 
 void PelrockEngine::gameLoop() {
-
 	_events->pollEvent();
 	checkMouse();
-
-	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_m) {
-		travelToEgypt();
-		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
-	}
-	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_n) {
-		loadExtraScreenAndPresent(10);
-		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
-	}
-	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_p) {
-		antiPiracyEffect();
-		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
-	}
-
-	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_w) {
-		Computer computer(_events);
-
-		computer.run();
-		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
-	}
-
-	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_e && _room->_currentRoomNumber == 52) {
-		teleportToPrincess();
-		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
-	}
-
-	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_q) {
-		endingScene();
-		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
-	}
-	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_u) {
-		if (_room->_currentRoomNumber == 36) {
-			pyramidCollapse();
-		}
-		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
-	}
-	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_k) {
-		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
-		credits();
-	}
-	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_v) {
-		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
-		SlidingPuzzle puzzle(_events, _sound);
-		puzzle.run();
-	}
-
-	if (_events->_lastKeyEvent == Common::KeyCode::KEYCODE_h) {
-		_events->_lastKeyEvent = Common::KeyCode::KEYCODE_INVALID;
-		antiPiracyEffect();
-	}
-
 	renderScene();
 	_screen->update();
 }
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index cfcf45c4a99..82aa3e5c6ad 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -210,6 +210,8 @@ public:
 	 */
 	bool isAlternateTiming() const;
 
+	bool isScreenSaverDisabled() const;
+
 	bool hasFeature(EngineFeature f) const override {
 		return (f == kSupportsLoadingDuringRuntime) ||
 			   (f == kSupportsSavingDuringRuntime) ||


Commit: c849d9789c68be5088e30c6b3ced3faea400e968
    https://github.com/scummvm/scummvm/commit/c849d9789c68be5088e30c6b3ced3faea400e968
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:48+02:00

Commit Message:
PELROCK: Cleanup of pelrock.cpp

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 52e6d53afcb..c371c83919f 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -174,19 +174,6 @@ void PelrockEngine::init() {
 		gameInitialized = true;
 		loadAnims();
 		setScreenAndPrepare(0, ALFRED_DOWN);
-		// setScreenAndPrepare(36, ALFRED_LEFT);
-
-		// setScreen(3, ALFRED_RIGHT);
-		// setScreen(22, ALFRED_DOWN);
-		// setScreen(41, ALFRED_DOWN);
-		// setScreen(43, ALFRED_DOWN);
-		// setScreen(46, ALFRED_RIGHT);
-		// setScreen(0, ALFRED_DOWN);
-		// setScreen(52, ALFRED_DOWN);
-		// setScreen(15, ALFRED_DOWN);
-		// setScreen(2, ALFRED_LEFT);
-		// _alfredState.x = 576;
-		// alfredState.y = 374;
 	}
 
 	loadInventoryArrows();
@@ -198,7 +185,7 @@ void PelrockEngine::loadInventoryArrows() {
 		error("Failed to open ALFRED.7 to load inventory arrows");
 		return;
 	}
-	alfred7.seek(kInventoryArrowsOffset, SEEK_SET); // Inventory arrows in ALFRED.7
+	alfred7.seek(kInventoryArrowsOffset, SEEK_SET);
 	_inventoryOverlayState.arrows[0] = new byte[20 * 60];
 	_inventoryOverlayState.arrows[1] = new byte[20 * 60];
 	alfred7.read(_inventoryOverlayState.arrows[0], 20 * 60);
@@ -243,7 +230,7 @@ Common::Array<VerbIcon> PelrockEngine::availableActions(HotSpot *hotspot) {
 
 Common::Point getPositionInBallonForIndex(int i);
 
-// Sort sprites by zOrder in-place using insertion sort (efficient for nearly-sorted data)
+// Sort sprites by zOrder in-place
 void sortAnimsByZOrder(Common::Array<Sprite> &anims) {
 	for (size_t i = 0; i < anims.size(); ++i) {
 		Sprite key = anims[i];
@@ -255,10 +242,6 @@ void sortAnimsByZOrder(Common::Array<Sprite> &anims) {
 		}
 		anims[j] = key;
 	}
-	// debug("Sorted anims by zOrder");
-	// for (size_t i = 0; i < anims.size(); i++) {
-	// debug("Anim %d, extra = %d: zOrder=%d", i, anims[i].extra, anims[i].zOrder);
-	// }
 }
 
 void PelrockEngine::playSoundIfNeeded() {
@@ -281,6 +264,9 @@ void PelrockEngine::playSoundIfNeeded() {
 	}
 }
 
+/**
+ * Travel to egypt sequence loads extra screen, plays palette animation then loads room 21
+ */
 void PelrockEngine::travelToEgypt() {
 	_graphics->fadeToBlack(10);
 
@@ -328,13 +314,17 @@ void PelrockEngine::travelToEgypt() {
 	// Original gives 4 items after room load (items 17, 64, 24, 59)
 	_state->inventoryItems.clear();
 	_state->selectedInventoryItem = -1;
-	// we dont want a flashing animation in this case!
+	// we dont want a flashing animation in this case! calling state->addInventory directly
 	_state->addInventoryItem(17);
 	_state->addInventoryItem(64);
 	_state->addInventoryItem(24);
 	_state->addInventoryItem(59);
 }
 
+/**
+ * Original would skip fram ticks during walking & talking
+ * Alternate timing avoids this.
+ */
 bool PelrockEngine::shouldSkipFrame() {
 	if (isAlternateTiming()) {
 		return false; // never skip frames in alternate timing mode
@@ -403,9 +393,9 @@ bool PelrockEngine::renderScene(int overlayMode) {
 
 	switch (_room->_currentRoomNumber) {
 	case 2: {
+		// Easter egg in room 2, pressing x 250 times after the character has mentioned it triggers a special dialog
 		if (_events->_lastKeyEvent == Common::KEYCODE_x) {
 			_events->_lastKeyEvent = Common::KEYCODE_INVALID;
-			debug("Pressed X in room 2, numPressedX is now %d, flag is %d", _numPressedX + 1, _state->getFlag(FLAG_PUTA_250_VECES));
 			if (_state->getFlag(FLAG_PUTA_250_VECES) == true) {
 				_numPressedX++;
 				if (_numPressedX == 250) {
@@ -429,12 +419,15 @@ void PelrockEngine::mouseHoverForMap() {
 
 void PelrockEngine::frameTriggers() {
 	uint32 frameCount = _chrono->getFrameCount();
-	passerByAnim(frameCount);
+	maybeUpdatePasserByAnim(frameCount);
 	handleFightRoomFrame();
-	shakeEffect();
+	maybeShakeEffect();
 	maybeHaveDogPee();
 }
 
+/**
+ * Dog pees in room 19 only when alfred gets close.
+ */
 void PelrockEngine::maybeHaveDogPee() {
 
 	if (_room->_currentRoomNumber != 19) {
@@ -480,22 +473,21 @@ void PelrockEngine::maybePlayPostIntro() {
 		_alfredState.direction = ALFRED_DOWN;
 		_alfredState.x = kAlfredInitialPosX;
 		_alfredState.y = kAlfredInitialPosY;
-		// setScreenAndPrepare(0, ALFRED_DOWN);
 		_dialog->say(_res->_ingameTexts[kTextMensajeOtraEpoca]);
 	}
 }
 
-void PelrockEngine::shakeEffect() {
+void PelrockEngine::maybeShakeEffect() {
 	if (!_shakeEffectState.enabled) {
 		return;
 	}
 
 	_shakeEffectState.shakeX = (_chrono->getFrameCount() % 4 < 2) ? 2 : -2;
 	g_system->setShakePos(_shakeEffectState.shakeX, _shakeEffectState.shakeY);
-	_alfredState.x += (_shakeEffectState.shakeX / 2); // Adjust Alfred's position to counteract shake for better readability
+	_alfredState.x += (_shakeEffectState.shakeX / 2);
 }
 
-void PelrockEngine::passerByAnim(uint32 frameCount) {
+void PelrockEngine::maybeUpdatePasserByAnim(uint32 frameCount) {
 	if (_room->_passerByAnims == nullptr) {
 		return;
 	}
@@ -505,7 +497,6 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 		if ((frameCount & anim.frameTrigger) == anim.frameTrigger) {
 			Sprite *sprite = _room->findSpriteByIndex(anim.spriteIndex);
 			if (sprite && sprite->zOrder == 255) {
-				debug("Starting passerby anim for sprite %d at index %d", anim.spriteIndex, animIndex);
 				sprite->zOrder = anim.targetZIndex;
 				sprite->curAnimIndex = 0;
 				sprite->animData[0].curFrame = 0;
@@ -521,10 +512,8 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 		int spriteIndex = anim.spriteIndex;
 		int startX = anim.startX;
 		int startY = anim.startY;
-		// debug("Checking passerby anim %d for sprite %d, direction %d", _room->_passerByAnims->currentAnimIndex, spriteIndex, direction);
 		Sprite *sprite = _room->findSpriteByIndex(spriteIndex);
 		if (direction == kPasserbyRight) {
-			// debug("Checking passerby anim for sprite %d moving RIGHT, curpos is %d", spriteIndex, sprite->x);
 			if (sprite->x >= anim.resetCoord) {
 				sprite->x = startX;
 				sprite->y = startY;
@@ -534,7 +523,6 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 				_room->_passerByAnims->latch = false;
 			}
 		} else if (direction == kPasserbyLeft) {
-			// debug("Checking passerby anim for sprite %d moving LEFT, curpos is %d", spriteIndex, sprite->x);
 
 			if (sprite->x <= anim.resetCoord) {
 				sprite->x = startX;
@@ -545,7 +533,6 @@ void PelrockEngine::passerByAnim(uint32 frameCount) {
 				_room->_passerByAnims->latch = false;
 			}
 		} else if (direction == kPasserbyDown) {
-			// debug("Checking passerby anim for sprite %d moving DOWN, curpos is %d, reset %d", spriteIndex, sprite->y, anim.resetCoord);
 			if (sprite->y >= anim.resetCoord) {
 				sprite->x = startX;
 				sprite->y = startY;
@@ -568,7 +555,6 @@ void PelrockEngine::executeAction(VerbIcon action, HotSpot *hotspot) {
 			}
 		}
 		noOpItem(inventoryObject, hotspot);
-		warning("No handler for using inventory object %d with hotspot %d", inventoryObject, hotspot->extra);
 		return;
 	}
 
@@ -588,7 +574,7 @@ void PelrockEngine::executeAction(VerbIcon action, HotSpot *hotspot) {
 		}
 	}
 
-	// No handler found
+	// No handler found - should never get to this point
 	warning("No handler for hotspot %d with action %d", hotspot->extra, action);
 }
 
@@ -611,6 +597,7 @@ void PelrockEngine::checkMouse() {
 		_alfredState.curFrame = 0;
 		_alfredState.setState(ALFRED_IDLE);
 	}
+
 	// Handle mouse release after long press (popup selection mode)
 	if (_events->_popupSelectionMode && !_events->_leftMouseButton) {
 		_events->_leftMouseButton = false;
@@ -668,7 +655,6 @@ void PelrockEngine::updateAnimations() {
 	// First pass: sprites behind Alfred (sprite zOrder > alfredZOrder)
 	for (uint i = 0; i < _room->_currentRoomAnims.size(); i++) {
 		if (_room->_currentRoomAnims[i].zOrder > alfredZOrder || _room->_currentRoomAnims[i].zOrder == 255) {
-			// debug("Drawing anim %d with zOrder %d in first pass (behind Alfred)", i, _room->_currentRoomAnims[i].zOrder);
 			drawNextFrame(&_room->_currentRoomAnims[i]);
 		}
 	}
@@ -679,7 +665,6 @@ void PelrockEngine::updateAnimations() {
 	// Second pass: sprites in front of Alfred (sprite zOrder <= alfredZOrder)
 	for (uint i = 0; i < _room->_currentRoomAnims.size(); i++) {
 		if (_room->_currentRoomAnims[i].zOrder <= alfredZOrder && _room->_currentRoomAnims[i].zOrder != 255) {
-			// debug("Drawing anim %d with zOrder %d in second pass (in front of Alfred)", i, _room->_currentRoomAnims[i].zOrder);
 			drawNextFrame(&_room->_currentRoomAnims[i]);
 		}
 	}
@@ -852,10 +837,6 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 					_alfredState.x = exit->targetX;
 					_alfredState.y = exit->targetY;
 					setScreenAndPrepare(exit->targetRoom, exit->dir);
-					// setScreen() resets the composite buffer to the bare background;
-					// place first-pass stickers now so they are present in the very
-					// first presentFrame() for the new room (avoids a one-frame flash
-					// without stickers).
 					_graphics->placeStickersFirstPass();
 				}
 			}
@@ -867,7 +848,6 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 				_alfredState.curFrame = 0;
 			}
 			if (_alfredState.animState == ALFRED_WALKING) { // in case it changed to idle above
-				debug("Drawing crawl frame %d for direction %d", _alfredState.curFrame, _alfredState.direction);
 				drawSpriteToBuffer(_compositeBuffer, _res->alfredCrawlFrames[_alfredState.direction][_alfredState.curFrame], _alfredState.x, _alfredState.y - 55, 130, 55, 255);
 				_alfredState.curFrame++;
 			}
@@ -950,6 +930,9 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 	}
 }
 
+/**
+ * Triggers upon exiting certain rooms, namely exit animations (like climbing a ladder)
+ */
 void PelrockEngine::exitTriggers(Pelrock::Exit *exit) {
 	if (exit->targetRoom == 31 && _room->_currentRoomNumber == 32) {
 		_res->loadAlfredSpecialAnim(8);
@@ -1015,8 +998,6 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	_alfredSprite = scaledBuf;
 
 	// Shadow detection: scan across Alfred's width at feet line.
-	// Original game scans shadow buffer
-	// at (topY + 0x66) * 640 + X + col for col = 0..width, where topY + 0x66 = feetY.
 	// The shadow map value (0-3) indexes into the palette remap tables.
 	if (_room->_pixelsShadows != nullptr) {
 		byte shadowLevel = 0xFF; // 0xFF = no shadow
@@ -1028,7 +1009,7 @@ void PelrockEngine::drawAlfred(byte *buf) {
 					byte shadowVal = _room->_pixelsShadows[feetY * 640 + checkX];
 					if (shadowVal != 0xFF) {
 						shadowLevel = shadowVal;
-						break; // Original breaks on first shadow pixel found
+						break;
 					}
 				}
 			}
@@ -1038,7 +1019,6 @@ void PelrockEngine::drawAlfred(byte *buf) {
 			for (int i = 0; i < finalWidth * finalHeight; i++) {
 				if (_alfredSprite[i] != 255) {
 					_alfredSprite[i] = _room->_paletteRemaps[shadowLevel][_alfredSprite[i]];
-					// _alfredSprite[i] = _room->_paletteRemaps[0][_alfredSprite[i]];
 				}
 			}
 		}
@@ -1054,6 +1034,9 @@ void PelrockEngine::drawAlfred(byte *buf) {
 	}
 }
 
+/**
+ * displace sprites in accordance to the movement flags
+ */
 void applyMovement(int16 *x, int16 *y, byte *z, uint16 flags) {
 	// X-axis movement
 	if (flags & 0x10) {            // Bit 4: X movement enabled
@@ -1089,7 +1072,7 @@ void applyMovement(int16 *x, int16 *y, byte *z, uint16 flags) {
 void PelrockEngine::drawNextFrame(Sprite *sprite) {
 	Anim &animData = sprite->animData[sprite->curAnimIndex];
 	if (sprite->zOrder == 255) {
-		// Skip disabled sprites (zOrder 0xFF = disabled in original game)
+		// Skip disabled sprites
 		return;
 	}
 
@@ -1105,7 +1088,6 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 
 	int curFrame = animData.curFrame;
 	if (curFrame >= animData.nframes) {
-		debug("Warning: curFrame %d exceeds nframes %d for sprite %d anim %d", curFrame, animData.nframes, sprite->index, sprite->curAnimIndex);
 		curFrame = 0;
 	}
 	drawSpriteToBuffer(_compositeBuffer, animData.animData[curFrame], x, y, w, h, 255);
@@ -1130,7 +1112,7 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 				if (sprite->curAnimIndex < sprite->numAnims - 1) {
 					sprite->curAnimIndex++;
 
-					// Trigger ring on phone on every start of animation 2
+					// Trigger ring on phone on every start of animation 2 in room 9
 					if (_room->_currentRoomNumber == 9 && sprite->index == 3) {
 						if (sprite->curAnimIndex == 1 && animData.curLoop == 0) {
 							byte soundFileIndex = _room->_roomSfx[2];
@@ -1411,7 +1393,7 @@ Common::Point getPositionInOverlayForIndex(uint index) {
 
 void PelrockEngine::pickupIconFlash() {
 	uint invSize = _state->inventoryItems.size();
-	// focus on the last positionin the inventory, where the newly picked up item would be, if there is at least 1 item in the inventory
+	// focus on the last position in the inventory, where the newly picked up item would be, if there is at least 1 item in the inventory
 	_inventoryOverlayState.invStartingPos = getScrollPositionForItem(_state->inventoryItems[invSize - 1]);
 	_inventoryOverlayState.flashingIconIndex = invSize - 1;
 	showInventoryOverlay();
@@ -1421,7 +1403,7 @@ void PelrockEngine::showInventoryOverlay() {
 	_graphics->showOverlay(60, _compositeBuffer);
 	uint invSize = _state->inventoryItems.size();
 	// invStartingPos is an ITEM index (not a page number).
-	// The original game scrolls 1 item at a time, not 1 page at a time.
+	// Scrolls 1 item at a time, not 1 page at a time.
 	if (_inventoryOverlayState.invStartingPos == -1) {
 		_inventoryOverlayState.invStartingPos = getScrollPositionForItem(_state->selectedInventoryItem);
 		if (_inventoryOverlayState.invStartingPos == -1) {
@@ -1448,26 +1430,20 @@ void PelrockEngine::showInventoryOverlay() {
 }
 
 void PelrockEngine::checkMouseOverInventoryOverlay(int x, int y) {
-	// Original game: invStartingPos is an item index, scrolls 1 item per frame
-	// with no frame throttling (scrolls every game tick = ~55ms).
 	if (x < 20) {
 		if (_inventoryOverlayState.invStartingPos > 0) {
 			_inventoryOverlayState.invStartingPos--;
 		}
-		debug("Mouse at x=%d triggers scroll left, new invStartingPos=%d", x, _inventoryOverlayState.invStartingPos);
 	} else if (x >= 620) {
 		if (_inventoryOverlayState.invStartingPos + kInventoryPageSize < (int)_state->inventoryItems.size()) {
 			_inventoryOverlayState.invStartingPos++;
 		}
-		debug("Mouse at x=%d triggers scroll right, new invStartingPos=%d", x, _inventoryOverlayState.invStartingPos);
 	} else {
 		// mouse hover over inventory item, laid out horizontally, y coordinate is not relevant
 		int index = (x - 20) / 60 + _inventoryOverlayState.invStartingPos;
 		if (index < (int)_state->inventoryItems.size()) {
-			debug("hovering over inventory item %d at index %d", _state->inventoryItems[index], index);
 			_inventoryOverlayState.flashingIconIndex = index;
 		} else {
-			debug("hovering over empty slot in inventory overlay, no item at index %d", index);
 			_inventoryOverlayState.flashingIconIndex = -1;
 		}
 	}
@@ -1534,7 +1510,6 @@ void PelrockEngine::walkLoop(int16 x, int16 y, AlfredDirection direction) {
 		_screen->update();
 		g_system->delayMillis(10);
 	}
-	debug("Walk loop ended");
 }
 
 void PelrockEngine::walkTo(int x, int y) {
@@ -1598,9 +1573,7 @@ AlfredDirection PelrockEngine::calculateAlfredsDirection(HotSpot *hotspot) {
 }
 
 VerbIcon PelrockEngine::isActionUnder(int x, int y) {
-	/*if (_currentHotspot == nullptr) {
-		return NO_ACTION;
-	}*/
+
 	Common::Array<VerbIcon> actions = availableActions(_currentHotspot);
 	uint loopEnd = _state->selectedInventoryItem != -1 ? actions.size() + 1 : actions.size();
 	for (uint i = 0; i < loopEnd; i++) {
@@ -1927,16 +1900,6 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		// paths lead to capture, no voluntary exit allowed.
 		_dialog->_goodbyeDisabled = true;
 		break;
-	case 10: {
-		// _events->waitForKey();
-		// while(!shouldQuit()) {
-		// 	playSpecialAnim(212915, true, 287, 152, 62, 58, 10);
-		// 	playSpecialAnim(236645, true, 287, 152, 62, 58, 10);
-		// 	// setScreen(11, ALFRED_DOWN);
-		// 	playSpecialAnim(261449, true, 0, 223, 64, 97, 8);
-		// }
-		break;
-	}
 	case 48: {
 		_dialog->_goodbyeDisabled = true;
 
@@ -1974,7 +1937,6 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		} else {
 			_dialog->say(_res->_ingameTexts[kTextOhMiSalvador]);
 			_dialog->say(_res->_ingameTexts[kTextVoyPoriPrincesa]);
-			// _state->setCurrentRoot(48, 0, 1);
 			HotSpot *fatMummy = nullptr;
 			for (uint i = 0; i < _room->_currentRoomHotspots.size(); i++) {
 				if (_room->_currentRoomHotspots[i].isSprite && _room->_currentRoomHotspots[i].index == 1) {
@@ -2386,17 +2348,14 @@ void PelrockEngine::handleFightRoomFrame() {
 
 	// Phase 2: spell trigger at tick 104 (64 + 40)
 	if (_fightSorcererAppeared && !_fightSpellCast && _fightFrameCounter >= 104) {
-		debug("spell cast triggered for room %d at frame %d", room, _fightFrameCounter);
 		_fightSpellCast = true;
 		_fightSpellFrameCounter = 0;
 	}
 
 	// Phase 3: wait 40 ticks after spell trigger, then cast
 	if (_fightSpellCast) {
-		debug("in spell cast phase for room %d at frame %d", room, _fightFrameCounter);
 		_fightSpellFrameCounter++;
 		if (_fightSpellFrameCounter >= 40 && !(_state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) & (1 << idx))) {
-			debug("spell cast animation starting for room %d at frame %d, flag is %d", room, _fightFrameCounter, _state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES));
 			_fightInBlockingAnim = true;
 
 			int spellFrames = kFightRooms[idx].spellFrames;
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 82aa3e5c6ad..c4fe4a00d4e 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -257,11 +257,11 @@ public:
 	void frameTriggers();
 	void maybeHaveDogPee();
 	void maybePlayPostIntro();
-	void shakeEffect();
+	void maybeShakeEffect();
 	void handleFightRoomFrame();
 	void paintDebugLayer();
 
-	void passerByAnim(uint32 frameCount);
+	void maybeUpdatePasserByAnim(uint32 frameCount);
 	void changeCursor(Cursor cursor);
 
 	void travelToEgypt();


Commit: d7b1739e883685b4063eddabb34d57183ba6f20e
    https://github.com/scummvm/scummvm/commit/d7b1739e883685b4063eddabb34d57183ba6f20e
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:49+02:00

Commit Message:
PELROCK: Fix sticker in egyptian museum door

Changed paths:
    engines/pelrock/actions.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 57e72fa55f5..c9eaa2793f8 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1345,11 +1345,11 @@ void PelrockEngine::pickupCondoms(HotSpot *hotspot) {
 }
 
 void PelrockEngine::openEgyptMuseumDoor(HotSpot *hotspot) {
-	openDoor(hotspot, 0, 59, MASCULINE, false);
+	openDoor(hotspot, 0, 68, MASCULINE, false);
 }
 
 void PelrockEngine::closeEgyptMuseumDoor(HotSpot *hotspot) {
-	closeDoor(hotspot, 0, 59, MASCULINE, false);
+	closeDoor(hotspot, 0, 68, MASCULINE, false);
 }
 
 void PelrockEngine::pushSymbol1(HotSpot *hotspot) {


Commit: 9c2bfd6fb03a2fa03614add6f473ef046025a0d7
    https://github.com/scummvm/scummvm/commit/9c2bfd6fb03a2fa03614add6f473ef046025a0d7
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:49+02:00

Commit Message:
PELROCK: Set extra flags in case they are needed

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index c9eaa2793f8..a0cbf6d9186 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -585,6 +585,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 297: {
 		// sprite moves to right
 		_room->enableExit(0);
+		_state->setFlag(FLAG_VIGILANTE_PAJEANDOSE, true);
 		Sprite *sprite = _room->findSpriteByIndex(0);
 		sprite->animData[0].movementFlags = 0x1C; // Move right
 		// Basic loop to wait until the sprite has reached the door
@@ -948,7 +949,7 @@ void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
 	_dialog->say(_res->_ingameTexts[kTextQueHaSidoEso], x, dialog1y);
 	_dialog->say(_res->_ingameTexts[kTextQuienAndaAhi], x, dialog2y);
 	_dialog->say(_res->_ingameTexts[kTextYoMeVoy]);
-
+	_state->setFlag(FLAG_HE_TIRADO_PIEDRA, true);
 	_state->setFlag(FLAG_TIENDA_ABIERTA, true);
 	_room->addStickerToRoom(_room->_currentRoomNumber, 9, PERSIST_PERM);
 	_room->addStickerToRoom(_room->_currentRoomNumber, 10, PERSIST_PERM);
@@ -1389,6 +1390,7 @@ void PelrockEngine::checkAllSymbols() {
 	if (symbolsPulled == 0x0F) {
 		// Activates animation
 		_sound->playSound(_room->_roomSfx[0]);
+		_state->setFlag(FLAG_PUERTA_SECRETA_ABIERTA, true);
 		_room->enableSprite(4, 100, PERSIST_TEMP);
 		_room->enableExit(0, PERSIST_BOTH);
 		_room->addSticker(61);
@@ -1674,6 +1676,7 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 		_screen->update();
 		g_system->delayMillis(10);
 	}
+	_state->setFlag(FLAG_APARECE_EUNUCO, true);
 	Sprite *guard = _room->findSpriteByIndex(0);
 	guard->animData[0].movementFlags = 0x14;
 	while (!shouldQuit()) {
@@ -1700,6 +1703,7 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 	_alfredState.x = 271;
 	_alfredState.y = 385;
 	setScreenAndPrepare(40, ALFRED_UP);
+	_state->setFlag(FLAG_AL_FARAON, true);
 	walkAndAction(_room->findHotspotByExtra(640), TALK);
 	if (shouldQuit()) {
 		return;
@@ -1707,6 +1711,7 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 	_graphics->fadeToBlack(10);
 	_alfredState.x = 271;
 	_alfredState.y = 385;
+	_state->setFlag(FLAG_A_CURRAR, true);
 	setScreenAndPrepare(41, ALFRED_UP);
 }
 
@@ -1782,6 +1787,7 @@ void PelrockEngine::magicFormula(int inventoryObject, HotSpot *hotspot) {
 	_state->setFlag(FLAG_FORMULA_MAGICA, _state->getFlag(FLAG_FORMULA_MAGICA) + 1);
 	if (_state->getFlag(FLAG_FORMULA_MAGICA) == 4) {
 		smokeAnimation(-1);
+		_state->setFlag(FLAG_VIAJA_AL_PASADO, true);
 		_alfredState.setState(ALFRED_IDLE);
 		_state->clearInventory();
 		_state->addInventoryItem(88);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index c371c83919f..83b99c4ae0a 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -268,6 +268,7 @@ void PelrockEngine::playSoundIfNeeded() {
  * Travel to egypt sequence loads extra screen, plays palette animation then loads room 21
  */
 void PelrockEngine::travelToEgypt() {
+	_state->setFlag(FLAG_VIAJE_A_EGIPTO, true);
 	_graphics->fadeToBlack(10);
 
 	_sound->playMusicTrack(26, false);
@@ -1932,6 +1933,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			_alfredState.x = 294;
 			_alfredState.y = 387;
 			_room->addSticker(136);
+			_state->setFlag(FLAG_A_LOS_PASILLOS, true);
 			setScreenAndPrepare(49, ALFRED_UP);
 
 		} else {
@@ -1949,6 +1951,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 
 			walkAndAction(fatMummy, TALK);
 			_state->clear();
+			_state->setFlag(FLAG_VUELTA_A_EMPEZAR, true);
 			_alfredState.x = kAlfredInitialPosX;
 			_alfredState.y = kAlfredInitialPosY;
 			_graphics->fadeToBlack(20);
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 1e59f830b86..1fa31e0e72b 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -530,27 +530,42 @@ struct ResetEntry {
 #define FLAG_ALFRED_INTELIGENTE 9
 #define FLAG_ALFRED_SABE_EGIPCIO 10
 #define FLAG_VENDEDOR_DEJA_DE_JODER 11
+#define FLAG_VIAJE_A_EGIPTO 12
 #define FLAG_PARADOJA_RESUELTA 13
-#define FLAG_MIRA_SIMBOLO_FUERA_MUSEO 15
 #define FLAG_CROCODILLO_ENCENDIDO 14
-#define FLAG_CLAVE_CAJA_FUERTE 19
+#define FLAG_MIRA_SIMBOLO_FUERA_MUSEO 15
+#define FLAG_PUERTA_SECRETA_ABIERTA 16
 #define FLAG_ROBA_PELO_PRINCESA 17
 #define FLAG_A_LA_CARCEL 18
+#define FLAG_CLAVE_CAJA_FUERTE 19
 #define FLAG_SE_HA_PUESTO_EL_MUNECO 20
 #define FLAG_VIGILANTE_BEBE_AGUA 21
 #define FLAG_VIGILANTE_MEANDO 22
 #define FLAG_PIRAMIDE_JODIDA 23
 #define FLAG_PIRAMIDE_JODIDA2 24
+#define FLAG_VIGILANTE_PAJEANDOSE 25
 #define FLAG_FORMULA_MAGICA 26
+#define FLAG_VIAJA_AL_PASADO 27
+#define FLAG_APARECE_EUNUCO 28
+#define FLAG_AL_FARAON 29
+#define FLAG_A_CURRAR 30
 #define FLAG_DA_PIEDRA 31
 #define FLAG_PIEDRAS_COGIDAS 32
 #define FLAG_GUARDIAS_BORRACHOS 33
 #define FLAG_PIEDRA_FAKE_MOJADA 34
 #define FLAG_PUERTA_BUENA 35
 #define FLAG_TRAMPILLA_ABIERTA 36
+#define FLAG_HABITACION_PRINCESA 37
+#define FLAG_A_POR_LA_PRINCESA 38
+#define FLAG_VUELTA_A_EMPEZAR 39
+#define FLAG_A_LOS_PASILLOS 40
 #define FLAG_COMO_ESTAN_LOS_DIOSES 41
-#define FLAG_TIENDA_ABIERTA 46
 #define FLAG_END_OF_GAME 42
+#define FLAG_FROM_INTRO 43
+#define FLAG_HE_TIRADO_PIEDRA 44
+#define FLAG_HA_USADO_AGUA 45
+#define FLAG_TIENDA_ABIERTA 46
+#define FLAG_NUMERO_DE_COPAS 47
 #define FLAG_INGREDIENTES_CONSEGUIDOS 48
 #define FLAG_GUARDIA_PIDECOSAS 49
 #define FLAG_GUARDIA_DNI_ENTREGADO 50
@@ -564,23 +579,6 @@ struct ResetEntry {
 #define FLAG_MERCHANT_GIVENITEMS 58
 #define FLAG_CORRECT_DOOR_CHOSEN 59
 #define FLAG_ESQUELETO_RECONOCE 60
-
-#define FLAG_VIAJE_A_EGIPTO 12
-#define FLAG_PUERTA_SECRETA_ABIERTA 16
-#define FLAG_VIGILANTE_PAJEANDOSE 25
-#define FLAG_VIAJA_AL_PASADO 27
-#define FLAG_APARECE_EUNUCO 28
-#define FLAG_AL_FARAON 29
-#define FLAG_A_CURRAR 30
-
-#define FLAG_HABITACION_PRINCESA 37
-#define FLAG_A_POR_LA_PRINCESA 38
-#define FLAG_VUELTA_A_EMPEZAR 39
-#define FLAG_A_LOS_PASILLOS 40
-#define FLAG_FROM_INTRO 43
-#define FLAG_HE_TIRADO_PIEDRA 44
-#define FLAG_HA_USADO_AGUA 45
-#define FLAG_NUMERO_DE_COPAS 47
 #define FLAG_PIGEON_DEAD 61
 #define FLAG_RECIPE_TAKEN 62
 


Commit: e37bc9064d438f44b6edc9f0d47b304559b69f41
    https://github.com/scummvm/scummvm/commit/e37bc9064d438f44b6edc9f0d47b304559b69f41
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:49+02:00

Commit Message:
PELROCK: Translate flag names into English (from the original Spanish)

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index a0cbf6d9186..dab0464714c 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -304,10 +304,10 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
 	case 329:
-		_state->setFlag(FLAG_PUTA_250_VECES, true);
+		_state->setFlag(FLAG_HOOKER_250_TIMES, true);
 		break;
 	case 258:
-		_state->setFlag(FLAG_GUARDIA_PIDECOSAS, true);
+		_state->setFlag(FLAG_GUARD_ASKS_FOR_STUFF, true);
 		_state->setCurrentRoot(4, 2, 0);
 		break;
 	case 259:
@@ -341,6 +341,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
 	case 273: {
+		_state->setFlag(FLAG_SALESMAN_LEAVES_ALFRED_ALONE, true);
 		WalkBox w1 = {3, 436, 356, 4, 14, 0};
 		WalkBox w2 = {4, 440, 368, 148, 2, 0};
 
@@ -355,7 +356,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		break;
 	case 277:
 		_state->setCurrentRoot(room, rootIndex + 1, 0);
-		_state->setFlag(FLAG_JEFE_INGRESA_PASTA, true);
+		_state->setFlag(FLAG_BOSS_WIRED_MONEY, true);
 		break;
 	case 278:
 		_state->setCurrentRoot(room, rootIndex + 1, 0);
@@ -411,14 +412,14 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		break;
 	}
 	case 349:
-		_state->setFlag(FLAG_CONSIGNAS_VENDEDOR, _state->getFlag(FLAG_CONSIGNAS_VENDEDOR) + 1);
-		if (_state->getFlag(FLAG_CONSIGNAS_VENDEDOR) == 2) {
+		_state->setFlag(FLAG_MERCHANT_SLOGANS, _state->getFlag(FLAG_MERCHANT_SLOGANS) + 1);
+		if (_state->getFlag(FLAG_MERCHANT_SLOGANS) == 2) {
 			_state->setCurrentRoot(room, rootIndex + 1, 1);
 		}
 		break;
 	case 350:
-		_state->setFlag(FLAG_CONSIGNAS_VENDEDOR, _state->getFlag(FLAG_CONSIGNAS_VENDEDOR) + 1);
-		if (_state->getFlag(FLAG_CONSIGNAS_VENDEDOR) == 2) {
+		_state->setFlag(FLAG_MERCHANT_SLOGANS, _state->getFlag(FLAG_MERCHANT_SLOGANS) + 1);
+		if (_state->getFlag(FLAG_MERCHANT_SLOGANS) == 2) {
 			_state->setCurrentRoot(room, rootIndex + 1, 1);
 		}
 		break;
@@ -475,33 +476,33 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 
 	case 357: // wrong answer: counter-- (min 0)
 	{
-		if (_state->getFlag(FLAG_RESPUESTAS_ACERTADAS) > 0) {
-			_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, _state->getFlag(FLAG_RESPUESTAS_ACERTADAS) - 1);
+		if (_state->getFlag(FLAG_CORRECT_ANSWERS) > 0) {
+			_state->setFlag(FLAG_CORRECT_ANSWERS, _state->getFlag(FLAG_CORRECT_ANSWERS) - 1);
 		}
 		advanceQuotesConversation(rootIndex, room);
 		break;
 	}
 	case 358: // very wrong answer: counter-=2 (min 0)
 	{
-		if (_state->getFlag(FLAG_RESPUESTAS_ACERTADAS) > 1) {
-			_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, _state->getFlag(FLAG_RESPUESTAS_ACERTADAS) - 2);
+		if (_state->getFlag(FLAG_CORRECT_ANSWERS) > 1) {
+			_state->setFlag(FLAG_CORRECT_ANSWERS, _state->getFlag(FLAG_CORRECT_ANSWERS) - 2);
 		}
 		advanceQuotesConversation(rootIndex, room);
 		break;
 	}
 	case 359: // correct answer: counter++, award pin at 15
 	{
-		_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, _state->getFlag(FLAG_RESPUESTAS_ACERTADAS) + 1);
-		if (_state->getFlag(FLAG_RESPUESTAS_ACERTADAS) == 15) {
+		_state->setFlag(FLAG_CORRECT_ANSWERS, _state->getFlag(FLAG_CORRECT_ANSWERS) + 1);
+		if (_state->getFlag(FLAG_CORRECT_ANSWERS) == 15) {
 			addInventoryItem(106); // pin
-			_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, 0);
+			_state->setFlag(FLAG_CORRECT_ANSWERS, 0);
 		}
 		advanceQuotesConversation(rootIndex, room);
 		break;
 	}
 	case 360: // neutral reset: counter = 0
 	{
-		_state->setFlag(FLAG_RESPUESTAS_ACERTADAS, 0);
+		_state->setFlag(FLAG_CORRECT_ANSWERS, 0);
 		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		advanceQuotesConversation(rootIndex, room);
 		break;
@@ -523,7 +524,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		break;
 	}
 	case 365: // riddle correct: set riddle-solved flag
-		_state->setFlag(FLAG_PARADOJA_RESUELTA, 1);
+		_state->setFlag(FLAG_SOLVED_PARADOX, 1);
 		_state->setCurrentRoot(room, 1, 0);
 		break;
 	case 292:
@@ -585,7 +586,7 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 297: {
 		// sprite moves to right
 		_room->enableExit(0);
-		_state->setFlag(FLAG_VIGILANTE_PAJEANDOSE, true);
+		_state->setFlag(FLAG_GUARD_HAVINGFUN, true);
 		Sprite *sprite = _room->findSpriteByIndex(0);
 		sprite->animData[0].movementFlags = 0x1C; // Move right
 		// Basic loop to wait until the sprite has reached the door
@@ -672,8 +673,8 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_state->setCurrentRoot(43, 3, 0);
 		break;
 	case 325:
-		_state->setFlag(FLAG_ESQUELETO_RECONOCE, _state->getFlag(FLAG_ESQUELETO_RECONOCE) + 1);
-		if (_state->getFlag(FLAG_ESQUELETO_RECONOCE) == 2) {
+		_state->setFlag(FLAG_SKELETON_RECOGNIZES, _state->getFlag(FLAG_SKELETON_RECOGNIZES) + 1);
+		if (_state->getFlag(FLAG_SKELETON_RECOGNIZES) == 2) {
 			_state->setCurrentRoot(49, 1, 0);
 		}
 		break;
@@ -733,7 +734,7 @@ void PelrockEngine::toJail() {
 	_alfredState.x = 342;
 	_alfredState.y = 277;
 	setScreenAndPrepare(31, ALFRED_DOWN);
-	_state->setFlag(FLAG_A_LA_CARCEL, true);
+	_state->setFlag(FLAG_TO_JAIL, true);
 	_room->moveHotspot(_room->findHotspotByExtra(101), 444, 166);
 }
 
@@ -809,8 +810,8 @@ void PelrockEngine::openClosedDrawer(HotSpot *hotspot) {
 
 void PelrockEngine::useCardWithATM(int inventoryObject, HotSpot *hotspot) {
 	debug("Withdrawing money from ATM using card (inv obj %d)", inventoryObject);
-	if (_state->getFlag(FLAG_JEFE_INGRESA_PASTA)) {
-		_state->setFlag(FLAG_JEFE_INGRESA_PASTA, false);
+	if (_state->getFlag(FLAG_BOSS_WIRED_MONEY)) {
+		_state->setFlag(FLAG_BOSS_WIRED_MONEY, false);
 		addInventoryItem(75);
 		_state->setCurrentRoot(20, 2, 0);
 	} else {
@@ -871,7 +872,7 @@ void PelrockEngine::closeKitchenDoor(HotSpot *HotSpot) {
 }
 
 void PelrockEngine::openKitchenDrawer(HotSpot *hotspot) {
-	if (!_state->getFlag(FLAG_JEFE_ENCARCELADO)) {
+	if (!_state->getFlag(FLAG_BOSS_IN_JAIL)) {
 		_dialog->say(_res->_ingameTexts[kTextQuitaEsasManos]);
 	} else if (!_state->getFlag(FLAG_RECIPE_TAKEN)) {
 		_state->setFlag(FLAG_RECIPE_TAKEN, true);
@@ -889,13 +890,13 @@ void PelrockEngine::openKitchenDoorFromInside(HotSpot *hotspot) {
 }
 
 void PelrockEngine::useSpicySauceWithBurger(int inventoryObject, HotSpot *hotspot) {
-	_state->setFlag(FLAG_PUESTA_SALSA_PICANTE, true);
+	_state->setFlag(FLAG_SPICY_SAUCE_PLACED, true);
 	_sound->playSound(_room->_roomSfx[2]);
 	_dialog->say(_res->_ingameTexts[kTextVaestarPocoFuerte]);
 }
 
 void PelrockEngine::openShopDoor(HotSpot *hotspot) {
-	if (!_state->getFlag(FLAG_TIENDA_ABIERTA)) {
+	if (!_state->getFlag(FLAG_OPEN_SHOP)) {
 		_dialog->say(_res->_ingameTexts[kTextTiendaCerrada]);
 		return;
 	} else {
@@ -949,8 +950,8 @@ void PelrockEngine::useBrickWithWindow(int inventoryObject, HotSpot *hotspot) {
 	_dialog->say(_res->_ingameTexts[kTextQueHaSidoEso], x, dialog1y);
 	_dialog->say(_res->_ingameTexts[kTextQuienAndaAhi], x, dialog2y);
 	_dialog->say(_res->_ingameTexts[kTextYoMeVoy]);
-	_state->setFlag(FLAG_HE_TIRADO_PIEDRA, true);
-	_state->setFlag(FLAG_TIENDA_ABIERTA, true);
+	_state->setFlag(FLAG_STONE_THROWN, true);
+	_state->setFlag(FLAG_OPEN_SHOP, true);
 	_room->addStickerToRoom(_room->_currentRoomNumber, 9, PERSIST_PERM);
 	_room->addStickerToRoom(_room->_currentRoomNumber, 10, PERSIST_PERM);
 	_room->disableHotspot(_room->findHotspotByExtra(295));
@@ -965,7 +966,7 @@ void PelrockEngine::moveCable(HotSpot *hotspot) {
 	_room->addSticker(16);
 	_room->addSticker(17);
 	_room->addStickerToRoom(4, 20);
-	_state->setFlag(FLAG_CABLES_PUESTOS, true);
+	_state->setFlag(FLAG_CABLES_PLACED, true);
 }
 
 void PelrockEngine::useBrickWithShopWindow(int inventoryObject, HotSpot *hotspot) {
@@ -1028,7 +1029,7 @@ void PelrockEngine::useCordWithPlug(int inventoryObject, HotSpot *hotspot) {
 	if (!_room->hasSticker(18)) {
 		_dialog->say(_res->_ingameTexts[kTextPrimeroAbrirlo]);
 	} else {
-		if (_state->getFlag(FLAG_CABLES_PUESTOS)) {
+		if (_state->getFlag(FLAG_CABLES_PLACED)) {
 			_room->addSticker(19);
 			_room->moveHotspot(_room->findHotspotByIndex(6), 391, 381);
 			_state->removeInventoryItem(6);
@@ -1060,16 +1061,16 @@ void PelrockEngine::pickCables(HotSpot *hotspot) {
 }
 
 void PelrockEngine::showIdToGuard(int inventoryObject, HotSpot *hotspot) {
-	if (!_state->getFlag(FLAG_GUARDIA_PIDECOSAS)) {
+	if (!_state->getFlag(FLAG_GUARD_ASKS_FOR_STUFF)) {
 		_dialog->say(_res->_ingameTexts[kTextCuandoMeLoPida]);
 		return;
 	}
 
-	if (!_state->getFlag(FLAG_GUARDIA_DNI_ENTREGADO)) {
-		_state->setFlag(FLAG_GUARDIA_DNI_ENTREGADO, true);
+	if (!_state->getFlag(FLAG_GUARD_ID_DELIVERED)) {
+		_state->setFlag(FLAG_GUARD_ID_DELIVERED, true);
 		_dialog->say(_res->_ingameTexts[kTextDeAcuerdo]);
 	}
-	if (_state->getFlag(FLAG_SOBORNO_PORTERO) && _state->getFlag(FLAG_GUARDIA_DNI_ENTREGADO)) {
+	if (_state->getFlag(FLAG_DOORMAN_BRIBED) && _state->getFlag(FLAG_GUARD_ID_DELIVERED)) {
 		unlockMuseum();
 		return;
 	}
@@ -1086,27 +1087,27 @@ void PelrockEngine::unlockMuseum() {
 }
 
 void PelrockEngine::giveMoneyToGuard(int inventoryObject, HotSpot *hotspot) {
-	if (!_state->getFlag(FLAG_GUARDIA_PIDECOSAS)) {
+	if (!_state->getFlag(FLAG_GUARD_ASKS_FOR_STUFF)) {
 		_dialog->say(_res->_ingameTexts[kTextPretendeUstedSobornarme]);
 		return;
-	} else if (!_state->getFlag(FLAG_SOBORNO_PORTERO)) {
-		_state->setFlag(FLAG_SOBORNO_PORTERO, true);
+	} else if (!_state->getFlag(FLAG_DOORMAN_BRIBED)) {
+		_state->setFlag(FLAG_DOORMAN_BRIBED, true);
 		_dialog->say(_res->_ingameTexts[kTextMuyBien]);
 		_state->removeInventoryItem(5);
 	}
-	if (_state->getFlag(FLAG_SOBORNO_PORTERO) && _state->getFlag(FLAG_GUARDIA_DNI_ENTREGADO)) {
+	if (_state->getFlag(FLAG_DOORMAN_BRIBED) && _state->getFlag(FLAG_GUARD_ID_DELIVERED)) {
 		unlockMuseum();
 		return;
 	}
 }
 
 void PelrockEngine::openMuseumDoor(HotSpot *hotspot) {
-	if (!_state->getFlag(FLAG_GUARDIA_PIDECOSAS)) {
+	if (!_state->getFlag(FLAG_GUARD_ASKS_FOR_STUFF)) {
 		_dialog->say(_res->_ingameTexts[kTextAlto]);
 		return;
-	} else if (!_state->getFlag(FLAG_GUARDIA_DNI_ENTREGADO)) {
+	} else if (!_state->getFlag(FLAG_GUARD_ID_DELIVERED)) {
 		_dialog->say(_res->_ingameTexts[kTextNecesitaDni]);
-	} else if (!_state->getFlag(FLAG_SOBORNO_PORTERO)) {
+	} else if (!_state->getFlag(FLAG_DOORMAN_BRIBED)) {
 		_dialog->say(_res->_ingameTexts[kTextQueReciboACambio]);
 	} else {
 		openDoor(hotspot, 1, 22, FEMININE, false);
@@ -1207,7 +1208,7 @@ void PelrockEngine::closeNewspaperBossDoor(HotSpot *hotspot) {
 
 void PelrockEngine::openTravelAgencyDoor(HotSpot *hotspot) {
 
-	if (_state->getFlag(FLAG_AGENCIA_ABIERTA)) {
+	if (_state->getFlag(FLAG_TRAVELAGENCY_OPEN)) {
 		openDoor(hotspot, 1, 57, FEMININE, false);
 	}
 	// The game originally already did nothing here
@@ -1220,7 +1221,7 @@ void PelrockEngine::closeTravelAgencyDoor(HotSpot *hotspot) {
 void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
 	_state->removeInventoryItem(76);
 	addInventoryItem(86);
-	if (_state->getFlag(FLAG_CROCODILLO_ENCENDIDO) == false) {
+	if (_state->getFlag(FLAG_CROCODILE_ON) == false) {
 		_sound->playMusicTrack(27);
 		checkIngredients();
 		_dialog->say(_res->_ingameTexts[kTextCuidadoImprudente]);
@@ -1263,7 +1264,7 @@ void PelrockEngine::waitForSoundEnd(int channel) {
 }
 
 void PelrockEngine::pickupSunflower(HotSpot *hotspot) {
-	if (_state->getFlag(FLAG_PARADOJA_RESUELTA) == false) {
+	if (_state->getFlag(FLAG_SOLVED_PARADOX) == false) {
 		if (_state->getFlag(FLAG_RIDDLE_PRESENTED)) {
 			// try to take the sunflower before solving the riddle
 			_dialog->say(_res->_ingameTexts[kTextLeEstoyVigilando]);
@@ -1283,10 +1284,10 @@ void PelrockEngine::pickupSunflower(HotSpot *hotspot) {
 }
 
 void PelrockEngine::checkIngredients() {
-	byte ingredientes = _state->getFlag(FLAG_INGREDIENTES_CONSEGUIDOS);
+	byte ingredientes = _state->getFlag(FLAG_COLLECTED_INGREDIENTS);
 	int textLine = kTextPrimerIngrediente + ingredientes;
 	_dialog->say(_res->_ingameTexts[textLine]);
-	_state->setFlag(FLAG_INGREDIENTES_CONSEGUIDOS, ingredientes + 1);
+	_state->setFlag(FLAG_COLLECTED_INGREDIENTS, ingredientes + 1);
 }
 
 void PelrockEngine::pickUpBook(int i) {
@@ -1354,7 +1355,7 @@ void PelrockEngine::closeEgyptMuseumDoor(HotSpot *hotspot) {
 }
 
 void PelrockEngine::pushSymbol1(HotSpot *hotspot) {
-	if (_state->getFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO) == true) {
+	if (_state->getFlag(FLAG_LOOKED_SYMBOL_MUSEUM_EXTERIOR) == true) {
 		byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
 		_state->setFlag(FLAG_SYMBOLS_PUSHED, symbolsPulled | 0x1);
 		checkAllSymbols();
@@ -1362,7 +1363,7 @@ void PelrockEngine::pushSymbol1(HotSpot *hotspot) {
 }
 
 void PelrockEngine::pushSymbol2(HotSpot *hotspot) {
-	if (_state->getFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO) == true) {
+	if (_state->getFlag(FLAG_LOOKED_SYMBOL_MUSEUM_EXTERIOR) == true) {
 		byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
 		_state->setFlag(FLAG_SYMBOLS_PUSHED, symbolsPulled | 0x2);
 		checkAllSymbols();
@@ -1370,7 +1371,7 @@ void PelrockEngine::pushSymbol2(HotSpot *hotspot) {
 }
 
 void PelrockEngine::pushSymbol3(HotSpot *hotspot) {
-	if (_state->getFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO) == true) {
+	if (_state->getFlag(FLAG_LOOKED_SYMBOL_MUSEUM_EXTERIOR) == true) {
 		byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
 		_state->setFlag(FLAG_SYMBOLS_PUSHED, symbolsPulled | 0x4);
 		checkAllSymbols();
@@ -1378,7 +1379,7 @@ void PelrockEngine::pushSymbol3(HotSpot *hotspot) {
 }
 
 void PelrockEngine::pushSymbol4(HotSpot *hotspot) {
-	if (_state->getFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO) == true) {
+	if (_state->getFlag(FLAG_LOOKED_SYMBOL_MUSEUM_EXTERIOR) == true) {
 		byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
 		_state->setFlag(FLAG_SYMBOLS_PUSHED, symbolsPulled | 0x8);
 		checkAllSymbols();
@@ -1390,7 +1391,7 @@ void PelrockEngine::checkAllSymbols() {
 	if (symbolsPulled == 0x0F) {
 		// Activates animation
 		_sound->playSound(_room->_roomSfx[0]);
-		_state->setFlag(FLAG_PUERTA_SECRETA_ABIERTA, true);
+		_state->setFlag(FLAG_SECRET_DOOR_OPEN, true);
 		_room->enableSprite(4, 100, PERSIST_TEMP);
 		_room->enableExit(0, PERSIST_BOTH);
 		_room->addSticker(61);
@@ -1400,7 +1401,7 @@ void PelrockEngine::checkAllSymbols() {
 
 void PelrockEngine::pickUpHairStrand(HotSpot *hotspot) {
 	checkIngredients();
-	_state->setFlag(FLAG_ROBA_PELO_PRINCESA, true);
+	_state->setFlag(FLAG_STOLE_PRINCESS_HAIR, true);
 }
 
 void PelrockEngine::openJailFloorTile(HotSpot *hotspot) {
@@ -1423,7 +1424,7 @@ void PelrockEngine::useKeyWithPortrait(int inventoryObject, HotSpot *hotspot) {
 }
 
 void PelrockEngine::openSafe(HotSpot *hotspot) {
-	if (_state->getFlag(FLAG_CLAVE_CAJA_FUERTE) == true) {
+	if (_state->getFlag(FLAG_SAFE_COMBINATION) == true) {
 		_room->addSticker(102);
 		_dialog->say(_res->_ingameTexts[kTextGranCantidadDinero]);
 		addInventoryItem(82);
@@ -1452,7 +1453,7 @@ void PelrockEngine::useDollWithBed(int inventoryObject, HotSpot *hotspot) {
 	_alfredState.y += 12;
 	playAlfredSpecialAnim(12);
 	_alfredState.direction = ALFRED_DOWN;
-	_state->setFlag(FLAG_SE_HA_PUESTO_EL_MUNECO, true);
+	_state->setFlag(FLAG_DOLL_PLACED, true);
 	_state->removeInventoryItem(83);
 	_room->addSticker(126);
 	_alfredState.x = x;
@@ -1466,14 +1467,14 @@ void PelrockEngine::giveMagazineToGuard(int inventoryObject, HotSpot *hotspot) {
 }
 
 void PelrockEngine::giveWaterToGuard(int inventoryObject, HotSpot *hotspot) {
-	_state->setFlag(FLAG_VIGILANTE_BEBE_AGUA, _state->getFlag(FLAG_VIGILANTE_BEBE_AGUA) + 1);
+	_state->setFlag(FLAG_GUARD_WATER_DRINKED, _state->getFlag(FLAG_GUARD_WATER_DRINKED) + 1);
 
 	_dialog->say(_res->_ingameTexts[kTextAlaConUsted]);
 	_state->removeInventoryItem(86);
 	addInventoryItem(76);
-	if (_state->getFlag(FLAG_VIGILANTE_BEBE_AGUA) == 3) {
+	if (_state->getFlag(FLAG_GUARD_WATER_DRINKED) == 3) {
 		_dialog->say(_res->_ingameTexts[kTextMeMeo]);
-		_state->setFlag(FLAG_VIGILANTE_MEANDO, true);
+		_state->setFlag(FLAG_GUARD_PEEING, true);
 		guardMovement();
 		_room->disableSprite(36, 0, PERSIST_BOTH);
 		_room->enableExit(0, PERSIST_BOTH);
@@ -1513,7 +1514,7 @@ void PelrockEngine::guardMovement() {
 
 void PelrockEngine::pickUpStone(HotSpot *hotspot) {
 	_room->addSticker(117);
-	_state->setFlag(FLAG_PIRAMIDE_JODIDA, true);
+	_state->setFlag(FLAG_PYRAMID_COLLAPSED, true);
 	_sound->playSound("QUAKE2ZZ.SMP", 0, -1);
 	_shakeEffectState.enable();
 	checkIngredients();
@@ -1578,7 +1579,7 @@ void PelrockEngine::giveStoneToSlaves(int inventoryObject, HotSpot *hotspot) {
 
 	_dialog->say(_res->_ingameTexts[kTextHayQueCelebrarlo]);
 
-	byte counter = _state->getFlag(FLAG_DA_PIEDRA);
+	byte counter = _state->getFlag(FLAG_STONE_GIVEN);
 	// drinking animation and sound
 	_sound->playSound(_room->_roomSfx[1], 2);
 
@@ -1589,10 +1590,10 @@ void PelrockEngine::giveStoneToSlaves(int inventoryObject, HotSpot *hotspot) {
 	// Increment stone delivery counter (tracks 0→1→2→3)
 	debug("Current stone delivery count: %d", counter);
 	if (counter < 3) {
-		_state->setFlag(FLAG_DA_PIEDRA, ++counter);
+		_state->setFlag(FLAG_STONE_GIVEN, ++counter);
 		_room->findSpriteByIndex(0)->zOrder = zIndex;
 	}
-	debug("New stone delivery count: %d", _state->getFlag(FLAG_DA_PIEDRA));
+	debug("New stone delivery count: %d", _state->getFlag(FLAG_STONE_GIVEN));
 	// At 2nd stone delivery: masters starts singing (conversation root 2)
 	if (counter == 2) {
 		_state->setCurrentRoot(41, 2, 0);
@@ -1607,7 +1608,7 @@ void PelrockEngine::giveStoneToSlaves(int inventoryObject, HotSpot *hotspot) {
 		WalkBox w2 = {4, 141, 374, 46, 4, 0};
 		_room->addWalkbox(w1);
 		_room->addWalkbox(w2);
-		_state->setFlag(FLAG_GUARDIAS_BORRACHOS, true);
+		_state->setFlag(FLAG_DRUNK_GUARDS, true);
 	}
 }
 
@@ -1676,7 +1677,7 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 		_screen->update();
 		g_system->delayMillis(10);
 	}
-	_state->setFlag(FLAG_APARECE_EUNUCO, true);
+	_state->setFlag(FLAG_EUNUCH_APPEARS, true);
 	Sprite *guard = _room->findSpriteByIndex(0);
 	guard->animData[0].movementFlags = 0x14;
 	while (!shouldQuit()) {
@@ -1703,7 +1704,7 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 	_alfredState.x = 271;
 	_alfredState.y = 385;
 	setScreenAndPrepare(40, ALFRED_UP);
-	_state->setFlag(FLAG_AL_FARAON, true);
+	_state->setFlag(FLAG_PHARAOH_VIEWING, true);
 	walkAndAction(_room->findHotspotByExtra(640), TALK);
 	if (shouldQuit()) {
 		return;
@@ -1711,7 +1712,7 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 	_graphics->fadeToBlack(10);
 	_alfredState.x = 271;
 	_alfredState.y = 385;
-	_state->setFlag(FLAG_A_CURRAR, true);
+	_state->setFlag(FLAG_TO_WORK, true);
 	setScreenAndPrepare(41, ALFRED_UP);
 }
 
@@ -1720,22 +1721,22 @@ void PelrockEngine::pickUpStones(HotSpot *hotspot) {
 		_dialog->say(_res->_ingameTexts[kTextPesaDemasiado]);
 		return;
 	}
-	if (_state->getFlag(FLAG_PIEDRAS_COGIDAS) >= 2) {
+	if (_state->getFlag(FLAG_COLLECTED_STONES) >= 2) {
 		_dialog->say(_res->_ingameTexts[kTextNingunaTamanhoApropiado]);
 		return;
 	} else {
 		addInventoryItem(91);
-		_state->setFlag(FLAG_PIEDRAS_COGIDAS, _state->getFlag(FLAG_PIEDRAS_COGIDAS) + 1);
+		_state->setFlag(FLAG_COLLECTED_STONES, _state->getFlag(FLAG_COLLECTED_STONES) + 1);
 	}
 }
 
 void PelrockEngine::pickUpMud(HotSpot *hotspot) {
-	if (_state->getFlag(FLAG_PIEDRAS_COGIDAS) != 2) {
+	if (_state->getFlag(FLAG_COLLECTED_STONES) != 2) {
 		_dialog->say(_res->_ingameTexts[kTextParaQueCogeBarro]);
 		return;
 	} else {
 		addInventoryItem(92);
-		_state->setFlag(FLAG_PIEDRAS_COGIDAS, _state->getFlag(FLAG_PIEDRAS_COGIDAS) + 1);
+		_state->setFlag(FLAG_COLLECTED_STONES, _state->getFlag(FLAG_COLLECTED_STONES) + 1);
 		_dialog->say(_res->_ingameTexts[kTextBuenoCogereUnPoco]);
 	}
 }
@@ -1751,7 +1752,7 @@ void PelrockEngine::usePumpkinWithPond(int inventoryObject, HotSpot *hotspot) {
 
 void PelrockEngine::useWaterOnFakeStone(int inventoryObject, HotSpot *hotspot) {
 
-	int count = _state->getFlag(FLAG_PIEDRA_FAKE_MOJADA);
+	int count = _state->getFlag(FLAG_FAKE_STONE_WET);
 	if (count != 3) {
 		_state->removeInventoryItem(86);
 		addInventoryItem(76);
@@ -1770,7 +1771,7 @@ void PelrockEngine::useWaterOnFakeStone(int inventoryObject, HotSpot *hotspot) {
 			break;
 		}
 		count++;
-		_state->setFlag(FLAG_PIEDRA_FAKE_MOJADA, count);
+		_state->setFlag(FLAG_FAKE_STONE_WET, count);
 	}
 }
 
@@ -1784,10 +1785,10 @@ void PelrockEngine::magicFormula(int inventoryObject, HotSpot *hotspot) {
 	if (inventoryObject == 86) {
 		addInventoryItem(76);
 	}
-	_state->setFlag(FLAG_FORMULA_MAGICA, _state->getFlag(FLAG_FORMULA_MAGICA) + 1);
-	if (_state->getFlag(FLAG_FORMULA_MAGICA) == 4) {
+	_state->setFlag(FLAG_MAGIC_FORMULA, _state->getFlag(FLAG_MAGIC_FORMULA) + 1);
+	if (_state->getFlag(FLAG_MAGIC_FORMULA) == 4) {
 		smokeAnimation(-1);
-		_state->setFlag(FLAG_VIAJA_AL_PASADO, true);
+		_state->setFlag(FLAG_TIME_TRAVEL, true);
 		_alfredState.setState(ALFRED_IDLE);
 		_state->clearInventory();
 		_state->addInventoryItem(88);
@@ -1903,7 +1904,7 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 		_dialog->say(_res->_ingameTexts[kTextSeLorecomiendo]);
 		break;
 	case 327:
-		_state->setFlag(FLAG_MIRA_SIMBOLO_FUERA_MUSEO, true);
+		_state->setFlag(FLAG_LOOKED_SYMBOL_MUSEUM_EXTERIOR, true);
 		break;
 	case 294: {
 		HotSpot *floorTile = _room->findHotspotByExtra(462);
@@ -2076,7 +2077,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 	case 17: // Egyptian book
 		playAlfredSpecialAnim(0);
 		_dialog->say(_res->_ingameTexts[kTextYaSeEgipcio]);
-		_state->setFlag(FLAG_ALFRED_SABE_EGIPCIO, true);
+		_state->setFlag(FLAG_ALFRED_SPEAKS_EGYPTIAN, true);
 		break;
 	case 24: // Encyclopedia
 		if (_state->getFlag(FLAG_RIDDLE_PRESENTED) == true) {
@@ -2085,14 +2086,14 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		} else {
 			playAlfredSpecialAnim(0);
 			_dialog->say(_res->_ingameTexts[kTextCosasAprendido]);
-			_state->setFlag(FLAG_ALFRED_INTELIGENTE, true);
+			_state->setFlag(FLAG_ALFRED_SMART, true);
 			_state->setCurrentRoot(14, 2, 0);
 		}
 		break;
 	case 64: // Formula for time travel
 		playAlfredSpecialAnim(0);
 		loadExtraScreenAndPresent(5);
-		if (_state->getFlag(FLAG_ALFRED_SABE_EGIPCIO)) {
+		if (_state->getFlag(FLAG_ALFRED_SPEAKS_EGYPTIAN)) {
 			_dialog->say(_res->_ingameTexts[kTextFormulaViajeAlTiempo]);
 		} else {
 			_dialog->say(_res->_ingameTexts[kTextQueLastimaNoSeeEgipcio]);
@@ -2143,13 +2144,13 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 				_dialog->say(_res->_ingameTexts[kTextDiosHalcon + spell->page], 1);
 				int flightIndex = _room->_currentRoomNumber - 51;
 				if (_fightSorcererAppeared && !_fightInBlockingAnim && spell->page == kFightRooms[flightIndex].spellPage) {
-					_state->setFlag(FLAG_COMO_ESTAN_LOS_DIOSES, _state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) | (1 << flightIndex));
+					_state->setFlag(FLAG_GODS_STANCES, _state->getFlag(FLAG_GODS_STANCES) | (1 << flightIndex));
 					_sound->playSound(_room->_roomSfx[1], 0);
 					smokeAnimation(kFightRooms[flightIndex].spriteIdx, true);
 					_room->addStickerToRoom(_room->_currentRoomNumber, 127 + flightIndex);
 					_room->addStickerToRoom(52, 106 + flightIndex);
 
-					if (_state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) == 15) { // all 4 spells successful
+					if (_state->getFlag(FLAG_GODS_STANCES) == 15) { // all 4 spells successful
 						HotSpot hotspot = HotSpot();
 						hotspot.actionFlags = 0;
 						hotspot.extra = 999;
@@ -2179,7 +2180,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 	case 101: // combination
 		playAlfredSpecialAnim(1);
 		_dialog->say(_res->_ingameTexts[kTextPareceCombinacionCajaFuerte]);
-		_state->setFlag(FLAG_CLAVE_CAJA_FUERTE, true);
+		_state->setFlag(FLAG_SAFE_COMBINATION, true);
 		break;
 	case 108: // glue + patches
 	case 109: {
@@ -2253,12 +2254,12 @@ void PelrockEngine::sayRandomIncorrectResponse() {
 
 void PelrockEngine::chooseCorrectDoor() {
 	playAlfredSpecialAnim(1);
-	byte puertaBuena = _state->getFlag(FLAG_PUERTA_BUENA);
+	byte puertaBuena = _state->getFlag(FLAG_CORRECT_DOOR);
 	if (puertaBuena == 0) { // if not set yet, choose randomly
 		int choice = getRandomNumber(1);
-		_state->setFlag(FLAG_PUERTA_BUENA, choice + 1);
+		_state->setFlag(FLAG_CORRECT_DOOR, choice + 1);
 	}
-	puertaBuena = _state->getFlag(FLAG_PUERTA_BUENA);
+	puertaBuena = _state->getFlag(FLAG_CORRECT_DOOR);
 	Common::String doorText = _res->_izquierda;
 	if (puertaBuena == 1) {
 		doorText = _res->_izquierda;
@@ -2361,12 +2362,12 @@ void PelrockEngine::checkObjectsForPart2() {
 	if (_state->hasInventoryItem(17) &&
 		_state->hasInventoryItem(59) &&
 		_state->hasInventoryItem(24)) {
-		if (_state->getFlag(FLAG_AGENCIA_ABIERTA) == false) {
+		if (_state->getFlag(FLAG_TRAVELAGENCY_OPEN) == false) {
 			_room->addStickerToRoom(19, 54, PERSIST_BOTH);
 			_room->addStickerToRoom(19, 55, PERSIST_BOTH);
 			_room->addStickerToRoom(19, 56, PERSIST_BOTH);
 			_room->addStickerToRoom(19, 58, PERSIST_BOTH);
-			_state->setFlag(FLAG_AGENCIA_ABIERTA, true);
+			_state->setFlag(FLAG_TRAVELAGENCY_OPEN, true);
 		}
 	}
 }
@@ -2393,7 +2394,7 @@ void PelrockEngine::pickUpMatches(HotSpot *hotspot) {
 	// Pick up the item
 	_room->disableHotspot(hotspot);
 	Common::copy(targetPalette, targetPalette + 768, _room->_roomPalette);
-	_state->setFlag(FLAG_CROCODILLO_ENCENDIDO, true);
+	_state->setFlag(FLAG_CROCODILE_ON, true);
 	_room->moveHotspot(_room->findHotspotByExtra(87), 415, 171);
 	_room->moveHotspot(_room->findHotspotByExtra(88), 305, 217);
 	_room->moveHotspot(_room->findHotspotByExtra(89), 201, 239);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 83b99c4ae0a..f4f1b790880 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -268,7 +268,7 @@ void PelrockEngine::playSoundIfNeeded() {
  * Travel to egypt sequence loads extra screen, plays palette animation then loads room 21
  */
 void PelrockEngine::travelToEgypt() {
-	_state->setFlag(FLAG_VIAJE_A_EGIPTO, true);
+	_state->setFlag(FLAG_TRAVEL_TO_EGYPT, true);
 	_graphics->fadeToBlack(10);
 
 	_sound->playMusicTrack(26, false);
@@ -397,7 +397,7 @@ bool PelrockEngine::renderScene(int overlayMode) {
 		// Easter egg in room 2, pressing x 250 times after the character has mentioned it triggers a special dialog
 		if (_events->_lastKeyEvent == Common::KEYCODE_x) {
 			_events->_lastKeyEvent = Common::KEYCODE_INVALID;
-			if (_state->getFlag(FLAG_PUTA_250_VECES) == true) {
+			if (_state->getFlag(FLAG_HOOKER_250_TIMES) == true) {
 				_numPressedX++;
 				if (_numPressedX == 250) {
 					_dialog->say(_res->_ingameTexts[kTextYLosCondones]);
@@ -953,11 +953,11 @@ void PelrockEngine::exitTriggers(Pelrock::Exit *exit) {
 		smokeAnimation(-1);
 		uint16 x = _alfredState.x;
 		if (x < 282) {
-			if (_state->getFlag(FLAG_PUERTA_BUENA) == 1) {
+			if (_state->getFlag(FLAG_CORRECT_DOOR) == 1) {
 				_state->setFlag(FLAG_CORRECT_DOOR_CHOSEN, true);
 			}
 		} else {
-			if (_state->getFlag(FLAG_PUERTA_BUENA) == 2) {
+			if (_state->getFlag(FLAG_CORRECT_DOOR) == 2) {
 				_state->setFlag(FLAG_CORRECT_DOOR_CHOSEN, true);
 			}
 		}
@@ -1760,8 +1760,8 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 	switch (roomNumber) {
 	case 4:
 	case 0:
-		if (_state->getFlag(FLAG_PUESTA_SALSA_PICANTE) && !_state->getFlag(FLAG_JEFE_ENCARCELADO)) {
-			_state->setFlag(FLAG_JEFE_ENCARCELADO, true);
+		if (_state->getFlag(FLAG_SPICY_SAUCE_PLACED) && !_state->getFlag(FLAG_BOSS_IN_JAIL)) {
+			_state->setFlag(FLAG_BOSS_IN_JAIL, true);
 			_room->disableSprite(13, 0, PERSIST_BOTH);
 			loadExtraScreenAndPresent(4);
 		}
@@ -1771,8 +1771,8 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		break;
 	}
 	case 15:
-		if (_state->getFlag(FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ)) {
-			_state->setFlag(FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ, false);
+		if (_state->getFlag(FLAG_FIRST_TIME_IN_SHOP)) {
+			_state->setFlag(FLAG_FIRST_TIME_IN_SHOP, false);
 			_dialog->say(_res->_ingameTexts[kTextGamberros]);
 			_dialog->say(_res->_ingameTexts[kTextQuienYo]);
 			_dialog->say(_res->_ingameTexts[kTextPintaBuenaPersona]);
@@ -1848,7 +1848,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		break;
 	}
 	case 28: {
-		if (_state->getFlag(FLAG_CROCODILLO_ENCENDIDO) == true) {
+		if (_state->getFlag(FLAG_CROCODILE_ON) == true) {
 			byte palette[768];
 			_res->getPaletteForRoom28(palette);
 			g_system->getPaletteManager()->setPalette(palette, 0, 256);
@@ -1857,9 +1857,9 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		break;
 	}
 	case 26: {
-		if (_state->getFlag(FLAG_A_LA_CARCEL) == true) {
+		if (_state->getFlag(FLAG_TO_JAIL) == true) {
 			_dialog->_goodbyeDisabled = true;
-			if (_state->getFlag(FLAG_SE_HA_PUESTO_EL_MUNECO) == true) {
+			if (_state->getFlag(FLAG_DOLL_PLACED) == true) {
 				_state->setCurrentRoot(26, 2, 1);
 			} else {
 				_dialog->say(_res->_ingameTexts[kTextOigaUsted], 1);
@@ -1872,9 +1872,9 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		break;
 	}
 	case 30: {
-		if (_state->getFlag(FLAG_ROBA_PELO_PRINCESA) == true) {
+		if (_state->getFlag(FLAG_STOLE_PRINCESS_HAIR) == true) {
 			_dialog->_goodbyeDisabled = true;
-			_state->setFlag(FLAG_ROBA_PELO_PRINCESA, false);
+			_state->setFlag(FLAG_STOLE_PRINCESS_HAIR, false);
 			_room->enableSprite(0, 200, PERSIST_TEMP);
 			_dialog->say(_res->_ingameTexts[kTextOigaUsted]);
 			walkAndAction(_room->findHotspotByExtra(0), TALK);
@@ -1885,11 +1885,11 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		if (_shakeEffectState.enabled) {
 			_shakeEffectState.disable();
 		}
-		if (_state->getFlag(FLAG_PIRAMIDE_JODIDA) == true &&
+		if (_state->getFlag(FLAG_PYRAMID_COLLAPSED) == true &&
 			// _state->getFlag(FLAG_VIGILANTE_MEANDO) == true &&
-			_state->getFlag(FLAG_PIRAMIDE_JODIDA2) == false) {
-			_state->setFlag(FLAG_VIGILANTE_MEANDO, false);
-			_state->setFlag(FLAG_PIRAMIDE_JODIDA2, true);
+			_state->getFlag(FLAG_PYRAMID_COLLAPSED2) == false) {
+			_state->setFlag(FLAG_GUARD_PEEING, false);
+			_state->setFlag(FLAG_PYRAMID_COLLAPSED2, true);
 			debug("Pyramid is now active!");
 			pyramidCollapse();
 		}
@@ -1916,7 +1916,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		} else if (_state->getFlag(FLAG_CORRECT_DOOR_CHOSEN) == true) {
 			_dialog->say(_res->_ingameTexts[kTextOhMiSalvador]);
 			_dialog->say(_res->_ingameTexts[kTextVoyPoriPrincesa]);
-			_state->setFlag(FLAG_TRAMPILLA_ABIERTA, true);
+			_state->setFlag(FLAG_TRAPDOOR_OPEN, true);
 			walkAndAction(_room->findHotspotByExtra(634), TALK);
 			_room->addSticker(134);
 			// wait a few frames
@@ -1933,7 +1933,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			_alfredState.x = 294;
 			_alfredState.y = 387;
 			_room->addSticker(136);
-			_state->setFlag(FLAG_A_LOS_PASILLOS, true);
+			_state->setFlag(FLAG_CORRIDORS, true);
 			setScreenAndPrepare(49, ALFRED_UP);
 
 		} else {
@@ -1951,7 +1951,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 
 			walkAndAction(fatMummy, TALK);
 			_state->clear();
-			_state->setFlag(FLAG_VUELTA_A_EMPEZAR, true);
+			_state->setFlag(FLAG_GAME_RESTART, true);
 			_alfredState.x = kAlfredInitialPosX;
 			_alfredState.y = kAlfredInitialPosY;
 			_graphics->fadeToBlack(20);
@@ -2324,7 +2324,7 @@ void PelrockEngine::handleFightRoomFrame() {
 		return;
 	int idx = room - 51;
 
-	if (_state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) & (1 << idx)) {
+	if (_state->getFlag(FLAG_GODS_STANCES) & (1 << idx)) {
 		// Gods are already defeated in this room, no need to run sequence
 		return;
 	}
@@ -2358,7 +2358,7 @@ void PelrockEngine::handleFightRoomFrame() {
 	// Phase 3: wait 40 ticks after spell trigger, then cast
 	if (_fightSpellCast) {
 		_fightSpellFrameCounter++;
-		if (_fightSpellFrameCounter >= 40 && !(_state->getFlag(FLAG_COMO_ESTAN_LOS_DIOSES) & (1 << idx))) {
+		if (_fightSpellFrameCounter >= 40 && !(_state->getFlag(FLAG_GODS_STANCES) & (1 << idx))) {
 			_fightInBlockingAnim = true;
 
 			int spellFrames = kFightRooms[idx].spellFrames;
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 1fa31e0e72b..e8828f779ac 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -518,67 +518,67 @@ struct ResetEntry {
 	byte *data = nullptr;
 };
 
-#define FLAG_JEFE_INGRESA_PASTA 0
-#define FLAG_JEFE_ENCARCELADO 1
-#define FLAG_PUESTA_SALSA_PICANTE 2
-#define FLAG_CRISTAL_ROTO 3
-#define FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ 4
-#define FLAG_ELECTROCUTACION 5
-#define FLAG_CABLES_PUESTOS 6
-#define FLAG_SOBORNO_PORTERO 7
-#define FLAG_MEMORIZA_LIBRO 8
-#define FLAG_ALFRED_INTELIGENTE 9
-#define FLAG_ALFRED_SABE_EGIPCIO 10
-#define FLAG_VENDEDOR_DEJA_DE_JODER 11
-#define FLAG_VIAJE_A_EGIPTO 12
-#define FLAG_PARADOJA_RESUELTA 13
-#define FLAG_CROCODILLO_ENCENDIDO 14
-#define FLAG_MIRA_SIMBOLO_FUERA_MUSEO 15
-#define FLAG_PUERTA_SECRETA_ABIERTA 16
-#define FLAG_ROBA_PELO_PRINCESA 17
-#define FLAG_A_LA_CARCEL 18
-#define FLAG_CLAVE_CAJA_FUERTE 19
-#define FLAG_SE_HA_PUESTO_EL_MUNECO 20
-#define FLAG_VIGILANTE_BEBE_AGUA 21
-#define FLAG_VIGILANTE_MEANDO 22
-#define FLAG_PIRAMIDE_JODIDA 23
-#define FLAG_PIRAMIDE_JODIDA2 24
-#define FLAG_VIGILANTE_PAJEANDOSE 25
-#define FLAG_FORMULA_MAGICA 26
-#define FLAG_VIAJA_AL_PASADO 27
-#define FLAG_APARECE_EUNUCO 28
-#define FLAG_AL_FARAON 29
-#define FLAG_A_CURRAR 30
-#define FLAG_DA_PIEDRA 31
-#define FLAG_PIEDRAS_COGIDAS 32
-#define FLAG_GUARDIAS_BORRACHOS 33
-#define FLAG_PIEDRA_FAKE_MOJADA 34
-#define FLAG_PUERTA_BUENA 35
-#define FLAG_TRAMPILLA_ABIERTA 36
-#define FLAG_HABITACION_PRINCESA 37
-#define FLAG_A_POR_LA_PRINCESA 38
-#define FLAG_VUELTA_A_EMPEZAR 39
-#define FLAG_A_LOS_PASILLOS 40
-#define FLAG_COMO_ESTAN_LOS_DIOSES 41
+#define FLAG_BOSS_WIRED_MONEY 0
+#define FLAG_BOSS_IN_JAIL 1
+#define FLAG_SPICY_SAUCE_PLACED 2
+#define FLAG_BROKEN_GLASS 3
+#define FLAG_FIRST_TIME_IN_SHOP 4
+#define FLAG_ELECTROSHOCK 5
+#define FLAG_CABLES_PLACED 6
+#define FLAG_DOORMAN_BRIBED 7
+#define FLAG_BOOK_MEMORIZED 8
+#define FLAG_ALFRED_SMART 9
+#define FLAG_ALFRED_SPEAKS_EGYPTIAN 10
+#define FLAG_SALESMAN_LEAVES_ALFRED_ALONE 11
+#define FLAG_TRAVEL_TO_EGYPT 12
+#define FLAG_SOLVED_PARADOX 13
+#define FLAG_CROCODILE_ON 14
+#define FLAG_LOOKED_SYMBOL_MUSEUM_EXTERIOR 15
+#define FLAG_SECRET_DOOR_OPEN 16
+#define FLAG_STOLE_PRINCESS_HAIR 17
+#define FLAG_TO_JAIL 18
+#define FLAG_SAFE_COMBINATION 19
+#define FLAG_DOLL_PLACED 20
+#define FLAG_GUARD_WATER_DRINKED 21
+#define FLAG_GUARD_PEEING 22
+#define FLAG_PYRAMID_COLLAPSED 23
+#define FLAG_PYRAMID_COLLAPSED2 24
+#define FLAG_GUARD_HAVINGFUN 25
+#define FLAG_MAGIC_FORMULA 26
+#define FLAG_TIME_TRAVEL 27
+#define FLAG_EUNUCH_APPEARS 28
+#define FLAG_PHARAOH_VIEWING 29
+#define FLAG_TO_WORK 30
+#define FLAG_STONE_GIVEN 31
+#define FLAG_COLLECTED_STONES 32
+#define FLAG_DRUNK_GUARDS 33
+#define FLAG_FAKE_STONE_WET 34
+#define FLAG_CORRECT_DOOR 35
+#define FLAG_TRAPDOOR_OPEN 36
+#define FLAG_PRINCESS_ROOM 37
+#define FLAG_GET_THE_PRINCESS 38
+#define FLAG_GAME_RESTART 39
+#define FLAG_CORRIDORS 40
+#define FLAG_GODS_STANCES 41
 #define FLAG_END_OF_GAME 42
 #define FLAG_FROM_INTRO 43
-#define FLAG_HE_TIRADO_PIEDRA 44
-#define FLAG_HA_USADO_AGUA 45
-#define FLAG_TIENDA_ABIERTA 46
-#define FLAG_NUMERO_DE_COPAS 47
-#define FLAG_INGREDIENTES_CONSEGUIDOS 48
-#define FLAG_GUARDIA_PIDECOSAS 49
-#define FLAG_GUARDIA_DNI_ENTREGADO 50
-#define FLAG_AGENCIA_ABIERTA 51
-#define FLAG_CONSIGNAS_VENDEDOR 52
-#define FLAG_PUTA_250_VECES 53
-#define FLAG_RESPUESTAS_ACERTADAS 54
+#define FLAG_STONE_THROWN 44
+#define FLAG_USED_WATER 45
+#define FLAG_OPEN_SHOP 46
+#define FLAG_DRINKS_NUMBER 47
+#define FLAG_COLLECTED_INGREDIENTS 48
+#define FLAG_GUARD_ASKS_FOR_STUFF 49
+#define FLAG_GUARD_ID_DELIVERED 50
+#define FLAG_TRAVELAGENCY_OPEN 51
+#define FLAG_MERCHANT_SLOGANS 52
+#define FLAG_HOOKER_250_TIMES 53
+#define FLAG_CORRECT_ANSWERS 54
 #define FLAG_CHEAT_CODE_ENABLED 55
 #define FLAG_RIDDLE_PRESENTED 56
 #define FLAG_SYMBOLS_PUSHED 57
 #define FLAG_MERCHANT_GIVENITEMS 58
 #define FLAG_CORRECT_DOOR_CHOSEN 59
-#define FLAG_ESQUELETO_RECONOCE 60
+#define FLAG_SKELETON_RECOGNIZES 60
 #define FLAG_PIGEON_DEAD 61
 #define FLAG_RECIPE_TAKEN 62
 
@@ -616,7 +616,7 @@ struct GameStateData {
 		memset(conversationCurrentRoot, 0xFF, 112); // Initialize all to 0xFF (not set)
 		for (int i = 0; i < kNumGameFlags; i++)
 			flags[i] = 0;
-		flags[FLAG_ENTRA_EN_TIENDA_PRIMERA_VEZ] = true;
+		flags[FLAG_FIRST_TIME_IN_SHOP] = true;
 		inventoryItems.clear();
 		stickersPerRoom.clear();
 		roomExitChanges.clear();


Commit: 97706b2f175b8716b289fead44b8b13af0529d47
    https://github.com/scummvm/scummvm/commit/97706b2f175b8716b289fead44b8b13af0529d47
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:49+02:00

Commit Message:
PELROCK: Introduce getBoolFlag to prevent unsafe comparison on Windows

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index dab0464714c..d40bdcfd95e 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1355,7 +1355,7 @@ void PelrockEngine::closeEgyptMuseumDoor(HotSpot *hotspot) {
 }
 
 void PelrockEngine::pushSymbol1(HotSpot *hotspot) {
-	if (_state->getFlag(FLAG_LOOKED_SYMBOL_MUSEUM_EXTERIOR) == true) {
+	if (_state->getBoolFlag(FLAG_LOOKED_SYMBOL_MUSEUM_EXTERIOR) == true) {
 		byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
 		_state->setFlag(FLAG_SYMBOLS_PUSHED, symbolsPulled | 0x1);
 		checkAllSymbols();
@@ -1363,7 +1363,7 @@ void PelrockEngine::pushSymbol1(HotSpot *hotspot) {
 }
 
 void PelrockEngine::pushSymbol2(HotSpot *hotspot) {
-	if (_state->getFlag(FLAG_LOOKED_SYMBOL_MUSEUM_EXTERIOR) == true) {
+	if (_state->getBoolFlag(FLAG_LOOKED_SYMBOL_MUSEUM_EXTERIOR) == true) {
 		byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
 		_state->setFlag(FLAG_SYMBOLS_PUSHED, symbolsPulled | 0x2);
 		checkAllSymbols();
@@ -1371,7 +1371,7 @@ void PelrockEngine::pushSymbol2(HotSpot *hotspot) {
 }
 
 void PelrockEngine::pushSymbol3(HotSpot *hotspot) {
-	if (_state->getFlag(FLAG_LOOKED_SYMBOL_MUSEUM_EXTERIOR) == true) {
+	if (_state->getBoolFlag(FLAG_LOOKED_SYMBOL_MUSEUM_EXTERIOR) == true) {
 		byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
 		_state->setFlag(FLAG_SYMBOLS_PUSHED, symbolsPulled | 0x4);
 		checkAllSymbols();
@@ -1379,7 +1379,7 @@ void PelrockEngine::pushSymbol3(HotSpot *hotspot) {
 }
 
 void PelrockEngine::pushSymbol4(HotSpot *hotspot) {
-	if (_state->getFlag(FLAG_LOOKED_SYMBOL_MUSEUM_EXTERIOR) == true) {
+	if (_state->getBoolFlag(FLAG_LOOKED_SYMBOL_MUSEUM_EXTERIOR) == true) {
 		byte symbolsPulled = _state->getFlag(FLAG_SYMBOLS_PUSHED);
 		_state->setFlag(FLAG_SYMBOLS_PUSHED, symbolsPulled | 0x8);
 		checkAllSymbols();
@@ -1424,7 +1424,7 @@ void PelrockEngine::useKeyWithPortrait(int inventoryObject, HotSpot *hotspot) {
 }
 
 void PelrockEngine::openSafe(HotSpot *hotspot) {
-	if (_state->getFlag(FLAG_SAFE_COMBINATION) == true) {
+	if (_state->getBoolFlag(FLAG_SAFE_COMBINATION) == true) {
 		_room->addSticker(102);
 		_dialog->say(_res->_ingameTexts[kTextGranCantidadDinero]);
 		addInventoryItem(82);
@@ -2080,7 +2080,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		_state->setFlag(FLAG_ALFRED_SPEAKS_EGYPTIAN, true);
 		break;
 	case 24: // Encyclopedia
-		if (_state->getFlag(FLAG_RIDDLE_PRESENTED) == true) {
+		if (_state->getBoolFlag(FLAG_RIDDLE_PRESENTED) == true) {
 			_dialog->say(_res->_ingameTexts[kTextCapituloParadojas]);
 			_state->setCurrentRoot(25, 44, 0);
 		} else {
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 048b0174a4b..aa586b5a4e8 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -191,7 +191,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 	int16 xBasePos = 0;
 	int16 yBasePos = 0;
 	if (speakerId == kAlfredColor) {
-		if (g_engine->_state->getFlag(FLAG_FROM_INTRO) == true) {
+		if (g_engine->_state->getBoolFlag(FLAG_FROM_INTRO) == true) {
 			// Different talking animation for the post-intro sequence in which Alfred speaks in bed
 			g_engine->_alfredState.setState(ALFRED_SPECIAL_ANIM);
 		} else {
@@ -247,7 +247,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 	_events->_leftMouseClicked = false;
 	_dialogActive = true;
 	int curPage = 0;
-	bool fromIntro = g_engine->_state->getFlag(FLAG_FROM_INTRO) == true;
+	bool fromIntro = g_engine->_state->getBoolFlag(FLAG_FROM_INTRO) == true;
 
 	uint32 pageTtlMs = calcPageTtlMs(dialogueLines[curPage]);
 	uint32 pageStartMs = g_system->getMillis();
@@ -1035,7 +1035,7 @@ void DialogManager::maybeDisableChoice(Common::Array<Pelrock::ChoiceOption> *cho
  * Convenience method if we know it's Alfred talking
  */
 void DialogManager::sayAlfred(Common::StringArray texts) {
-	if (g_engine->_state->getFlag(FLAG_FROM_INTRO) == true) {
+	if (g_engine->_state->getBoolFlag(FLAG_FROM_INTRO) == true) {
 		g_engine->_alfredState.setState(ALFRED_SPECIAL_ANIM);
 	} else {
 		g_engine->_alfredState.setState(ALFRED_TALKING);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index f4f1b790880..3e33208b8f9 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -397,7 +397,7 @@ bool PelrockEngine::renderScene(int overlayMode) {
 		// Easter egg in room 2, pressing x 250 times after the character has mentioned it triggers a special dialog
 		if (_events->_lastKeyEvent == Common::KEYCODE_x) {
 			_events->_lastKeyEvent = Common::KEYCODE_INVALID;
-			if (_state->getFlag(FLAG_HOOKER_250_TIMES) == true) {
+			if (_state->getFlag(FLAG_HOOKER_250_TIMES)) {
 				_numPressedX++;
 				if (_numPressedX == 250) {
 					_dialog->say(_res->_ingameTexts[kTextYLosCondones]);
@@ -457,7 +457,7 @@ void PelrockEngine::maybeHaveDogPee() {
 }
 
 void PelrockEngine::maybePlayPostIntro() {
-	if (_state->getFlag(FLAG_FROM_INTRO) == true) {
+	if (_state->getFlag(FLAG_FROM_INTRO)) {
 		setScreenAndPrepare(0, ALFRED_LEFT);
 		_alfredState.x = 396;
 		_alfredState.y = 267;
@@ -1848,7 +1848,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		break;
 	}
 	case 28: {
-		if (_state->getFlag(FLAG_CROCODILE_ON) == true) {
+		if (_state->getFlag(FLAG_CROCODILE_ON)) {
 			byte palette[768];
 			_res->getPaletteForRoom28(palette);
 			g_system->getPaletteManager()->setPalette(palette, 0, 256);
@@ -1857,9 +1857,9 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		break;
 	}
 	case 26: {
-		if (_state->getFlag(FLAG_TO_JAIL) == true) {
+		if (_state->getBoolFlag(FLAG_TO_JAIL) == true) {
 			_dialog->_goodbyeDisabled = true;
-			if (_state->getFlag(FLAG_DOLL_PLACED) == true) {
+			if (_state->getBoolFlag(FLAG_DOLL_PLACED) == true) {
 				_state->setCurrentRoot(26, 2, 1);
 			} else {
 				_dialog->say(_res->_ingameTexts[kTextOigaUsted], 1);
@@ -1872,7 +1872,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		break;
 	}
 	case 30: {
-		if (_state->getFlag(FLAG_STOLE_PRINCESS_HAIR) == true) {
+		if (_state->getBoolFlag(FLAG_STOLE_PRINCESS_HAIR) == true) {
 			_dialog->_goodbyeDisabled = true;
 			_state->setFlag(FLAG_STOLE_PRINCESS_HAIR, false);
 			_room->enableSprite(0, 200, PERSIST_TEMP);
@@ -1885,8 +1885,8 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 		if (_shakeEffectState.enabled) {
 			_shakeEffectState.disable();
 		}
-		if (_state->getFlag(FLAG_PYRAMID_COLLAPSED) == true &&
-			// _state->getFlag(FLAG_VIGILANTE_MEANDO) == true &&
+		if (_state->getBoolFlag(FLAG_PYRAMID_COLLAPSED) == true &&
+			// _state->getBoolFlag(FLAG_GUARD_PEEING) == true &&
 			_state->getFlag(FLAG_PYRAMID_COLLAPSED2) == false) {
 			_state->setFlag(FLAG_GUARD_PEEING, false);
 			_state->setFlag(FLAG_PYRAMID_COLLAPSED2, true);
@@ -1904,7 +1904,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 	case 48: {
 		_dialog->_goodbyeDisabled = true;
 
-		if (_state->getFlag(FLAG_END_OF_GAME) == true) {
+		if (_state->getBoolFlag(FLAG_END_OF_GAME) == true) {
 
 			_dialog->say(_res->_ingameTexts[kTextOhMiSalvador]);
 			_dialog->say(_res->_ingameTexts[kTextVoyPoriPrincesa]);
@@ -1913,7 +1913,7 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 
 			endingScene();
 
-		} else if (_state->getFlag(FLAG_CORRECT_DOOR_CHOSEN) == true) {
+		} else if (_state->getBoolFlag(FLAG_CORRECT_DOOR_CHOSEN) == true) {
 			_dialog->say(_res->_ingameTexts[kTextOhMiSalvador]);
 			_dialog->say(_res->_ingameTexts[kTextVoyPoriPrincesa]);
 			_state->setFlag(FLAG_TRAPDOOR_OPEN, true);
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index e8828f779ac..8b9a9d8aa69 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -652,6 +652,10 @@ struct GameStateData {
 		return flags[flagIndex];
 	}
 
+	bool getBoolFlag(int flagIndex) const {
+		return getFlag(flagIndex) != 0;
+	}
+
 	void setFlag(int flagIndex, byte value) {
 		if (flagIndex < 0 || flagIndex >= kNumGameFlags)
 			return;


Commit: d4878a08f9f7c53b349a68707237f56819f2f040
    https://github.com/scummvm/scummvm/commit/d4878a08f9f7c53b349a68707237f56819f2f040
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:50+02:00

Commit Message:
PELROCK: Normalize types for chrono

Changed paths:
    engines/pelrock/chrono.h


diff --git a/engines/pelrock/chrono.h b/engines/pelrock/chrono.h
index 602d721d2b0..cbd348868ab 100644
--- a/engines/pelrock/chrono.h
+++ b/engines/pelrock/chrono.h
@@ -25,13 +25,13 @@
 
 namespace Pelrock {
 
-const int kTickMs = 55;
+const uint32 kTickMs = 55;
 const int kHalfTickMultiplier = 2;
 
 class ChronoManager {
 private:
 	uint32 _lastTick = 0;
-	byte _speedMultiplier = 1;
+	uint32 _speedMultiplier = 1;
 	uint32 _frameCount = 0;
 	bool _pauseCounter = false;
 


Commit: ebb2a7be7c30769565455af1b69213518f456c42
    https://github.com/scummvm/scummvm/commit/ebb2a7be7c30769565455af1b69213518f456c42
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:50+02:00

Commit Message:
PELROCK: Remove extraneous debug lines

Changed paths:
    engines/pelrock/dialog.cpp
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index aa586b5a4e8..2894454b0a0 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -281,7 +281,6 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		yPos = CLIP(yPos, 1, 400 - (int)s->getRect().height());
 
 		if (g_engine->_shakeEffectState.enabled) {
-			debug("Applying shake effect to dialogue, shakeX: %d", g_engine->_shakeEffectState.shakeX);
 			xPos -= g_engine->_shakeEffectState.shakeX;
 		}
 
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 3e33208b8f9..285dde92400 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -1886,11 +1886,10 @@ void PelrockEngine::doExtraActions(int roomNumber) {
 			_shakeEffectState.disable();
 		}
 		if (_state->getBoolFlag(FLAG_PYRAMID_COLLAPSED) == true &&
-			// _state->getBoolFlag(FLAG_GUARD_PEEING) == true &&
+			_state->getBoolFlag(FLAG_GUARD_PEEING) == true &&
 			_state->getFlag(FLAG_PYRAMID_COLLAPSED2) == false) {
 			_state->setFlag(FLAG_GUARD_PEEING, false);
 			_state->setFlag(FLAG_PYRAMID_COLLAPSED2, true);
-			debug("Pyramid is now active!");
 			pyramidCollapse();
 		}
 		break;


Commit: 244339e0fbd220d61f1caf57eec57815e1cce9da
    https://github.com/scummvm/scummvm/commit/244339e0fbd220d61f1caf57eec57815e1cce9da
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:50+02:00

Commit Message:
PELROCK: Fix double-free after swimming pool cutscene

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/pathfinding.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index d40bdcfd95e..7765d9c73c2 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -1588,12 +1588,10 @@ void PelrockEngine::giveStoneToSlaves(int inventoryObject, HotSpot *hotspot) {
 	playSpecialAnim(1473360, true, mastersX - 6, mastersY - 1, 152, 83, 7);
 
 	// Increment stone delivery counter (tracks 0→1→2→3)
-	debug("Current stone delivery count: %d", counter);
 	if (counter < 3) {
 		_state->setFlag(FLAG_STONE_GIVEN, ++counter);
 		_room->findSpriteByIndex(0)->zOrder = zIndex;
 	}
-	debug("New stone delivery count: %d", _state->getFlag(FLAG_STONE_GIVEN));
 	// At 2nd stone delivery: masters starts singing (conversation root 2)
 	if (counter == 2) {
 		_state->setCurrentRoot(41, 2, 0);
@@ -1694,7 +1692,7 @@ void PelrockEngine::swimmingPoolCutscene(HotSpot *hotspot) {
 	guard->animData[0].curFrame = 0;
 	guard->animData[0].nframes = 1;
 	// copy idle frame from talking animation
-	guard->animData[0].animData[0] = _room->_talkingAnims.animA[0];
+	Common::copy(_room->_talkingAnims.animA[0], _room->_talkingAnims.animA[0] +  guard->stride, guard->animData[0].animData[0]);
 	_alfredState.direction = ALFRED_RIGHT;
 	walkAndAction(_room->findHotspotByExtra(guard->extra), TALK);
 	if (shouldQuit()) {
diff --git a/engines/pelrock/pathfinding.cpp b/engines/pelrock/pathfinding.cpp
index 38f8483e9a6..49fe3446ec8 100644
--- a/engines/pelrock/pathfinding.cpp
+++ b/engines/pelrock/pathfinding.cpp
@@ -92,7 +92,7 @@ Common::Point calculateWalkTarget(Common::Array<WalkBox> &walkboxes, int sourceX
 	if (hotspot != nullptr) {
 		// if there is a hotspot then the source is the center of the hotspot.
 		sourceX = hotspot->x + hotspot->w / 2;
-		sourceY = hotspot->y + hotspot->h / 2;
+		sourceY = hotspot->y + hotspot->h;
 	}
 
 	// Find nearest walkbox
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 285dde92400..14d790970fd 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -222,9 +222,6 @@ Common::Array<VerbIcon> PelrockEngine::availableActions(HotSpot *hotspot) {
 	if (hotspot->actionFlags & kActionMaskPush) {
 		verbs.push_back(PUSH);
 	}
-	if (hotspot->actionFlags & kActionMaskPull) {
-		verbs.push_back(PULL);
-	}
 	return verbs;
 }
 
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index c0a6e6b78b2..3b83d558e1e 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -54,11 +54,19 @@ RoomManager::~RoomManager() {
 }
 
 void RoomManager::clearTalkingAnims() {
-	for (int i = 0; i < _talkingAnims.numFramesAnimA; i++)
-		delete[] _talkingAnims.animA[i];
+	for (int i = 0; i < _talkingAnims.numFramesAnimA; i++) {
+		if(_talkingAnims.animA[i]) {
+			delete[] _talkingAnims.animA[i];
+			_talkingAnims.animA[i] = nullptr;
+		}
+	}
 	delete[] _talkingAnims.animA;
-	for (int i = 0; i < _talkingAnims.numFramesAnimB; i++)
-		delete[] _talkingAnims.animB[i];
+	for (int i = 0; i < _talkingAnims.numFramesAnimB; i++) {
+		if(_talkingAnims.animB[i]) {
+			delete[] _talkingAnims.animB[i];
+			_talkingAnims.animB[i] = nullptr;
+		}
+	}
 	delete[] _talkingAnims.animB;
 	_talkingAnims.animA = nullptr;
 	_talkingAnims.animB = nullptr;


Commit: 16fae188a6aac16bbdae71fe69340eed80796faf
    https://github.com/scummvm/scummvm/commit/16fae188a6aac16bbdae71fe69340eed80796faf
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:51+02:00

Commit Message:
PELROCK: Credits

Changed paths:
    engines/pelrock/credits.pl
    engines/pelrock/detection.h


diff --git a/engines/pelrock/credits.pl b/engines/pelrock/credits.pl
index 3f3ab6a82f2..3b543b73b79 100644
--- a/engines/pelrock/credits.pl
+++ b/engines/pelrock/credits.pl
@@ -1,3 +1,3 @@
 begin_section("Pelrock");
-	add_person("Name 1", "Handle 1", "");
+	add_person("Gabriel Sanmartín", "kelmer", "");
 end_section();
diff --git a/engines/pelrock/detection.h b/engines/pelrock/detection.h
index cffa9df0502..45fc229fc44 100644
--- a/engines/pelrock/detection.h
+++ b/engines/pelrock/detection.h
@@ -57,11 +57,11 @@ public:
 	}
 
 	const char *getEngineName() const override {
-		return "Pelrock";
+		return "Alfred Pelrock";
 	}
 
 	const char *getOriginalCopyright() const override {
-		return "Pelrock (C)";
+		return "(C) 1997 DDM";
 	}
 
 	const DebugChannelDef *getDebugChannels() const override {


Commit: 7707b16203fb1bb411b8bec3c4c914fd299ea209
    https://github.com/scummvm/scummvm/commit/7707b16203fb1bb411b8bec3c4c914fd299ea209
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:51+02:00

Commit Message:
PELROCK: Remove unnecessary files

Changed paths:
  R engines/pelrock/.gitignore


diff --git a/engines/pelrock/.gitignore b/engines/pelrock/.gitignore
deleted file mode 100644
index 7e97ef32309..00000000000
--- a/engines/pelrock/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-*.o
-*.a
-.deps


Commit: f968301d6f8920531313d6ba6d152ee64b737732
    https://github.com/scummvm/scummvm/commit/f968301d6f8920531313d6ba6d152ee64b737732
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:51+02:00

Commit Message:
PELROCK: Make sure to free textSurface in intro video

Changed paths:
    engines/pelrock/video/video.cpp


diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video/video.cpp
index e5d51272256..f884230d8b2 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video/video.cpp
@@ -48,6 +48,7 @@ VideoManager::VideoManager(
 VideoManager::~VideoManager() {
 	_videoSurface.free();
 	_introSndFile.close();
+	_textSurface.free();
 }
 
 void VideoManager::playIntro() {


Commit: 23a9a6592b7fe679133a96ecec8928a010ea91e6
    https://github.com/scummvm/scummvm/commit/23a9a6592b7fe679133a96ecec8928a010ea91e6
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:51+02:00

Commit Message:
PELROCK: Move video to root of the engine

Changed paths:
  A engines/pelrock/video.cpp
  A engines/pelrock/video.h
  R engines/pelrock/video/video.cpp
  R engines/pelrock/video/video.h
    engines/pelrock/module.mk
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/module.mk b/engines/pelrock/module.mk
index 32053f15ecd..ece444a0612 100644
--- a/engines/pelrock/module.mk
+++ b/engines/pelrock/module.mk
@@ -1,30 +1,30 @@
 MODULE := engines/pelrock
 
 MODULE_OBJS = \
-	pelrock.o \
 	actions.o \
+	backgroundbook.o \
+	cdplayer.o \
 	chrono.o \
 	computer.o \
 	console.o \
-	metaengine.o \
-	room.o \
+	dialog.o \
+	events.o \
 	fonts/small_font.o \
 	fonts/large_font.o \
 	fonts/small_font_double.o \
-	util.o \
-	resources.o\
-	sound.o \
-	video/video.o \
-	pathfinding.o \
-	events.o \
-	dialog.o \
-	menu.o \
 	graphics.o \
+	menu.o \
+	metaengine.o \
+	pathfinding.o \
+	pelrock.o \
+	resources.o\
+	room.o \
 	saveload.o \
-	spellbook.o \
 	slidingpuzzle.o \
-	cdplayer.o \
-	backgroundbook.o
+	sound.o \
+	spellbook.o \
+	util.o \
+	video.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_PELROCK), DYNAMIC_PLUGIN)
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index c4fe4a00d4e..593702c97b6 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -50,7 +50,7 @@
 #include "pelrock/room.h"
 #include "pelrock/sound.h"
 #include "pelrock/types.h"
-#include "pelrock/video/video.h"
+#include "pelrock/video.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/video/video.cpp b/engines/pelrock/video.cpp
similarity index 99%
rename from engines/pelrock/video/video.cpp
rename to engines/pelrock/video.cpp
index f884230d8b2..2495bd2e084 100644
--- a/engines/pelrock/video/video.cpp
+++ b/engines/pelrock/video.cpp
@@ -26,8 +26,7 @@
 #include "pelrock/chrono.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/util.h"
-#include "pelrock/video/video.h"
-#include "video.h"
+#include "pelrock/video.h"
 
 namespace Pelrock {
 
@@ -60,6 +59,7 @@ void VideoManager::playIntro() {
 	}
 	videoFile.seek(0, SEEK_SET);
 
+
 	_videoSurface.fillRect(Common::Rect(0, 0, 640, 400), 0);
 	_textSurface.fillRect(Common::Rect(0, 0, 640, 400), 255);
 
@@ -108,6 +108,9 @@ void VideoManager::playIntro() {
 				_sound->playSound(voiceBuffer, voiceData.length, 0);
 			}
 
+
+
+
 			if (_sfxEffect.contains(currentFrame)) {
 				AudioEffect sfx = _sfxEffect[currentFrame];
 				VoiceData sfxData = _sounds[sfx.filename];
diff --git a/engines/pelrock/video/video.h b/engines/pelrock/video.h
similarity index 100%
rename from engines/pelrock/video/video.h
rename to engines/pelrock/video.h


Commit: a89f9d1c5312331697874242f794e9c8b2462e3f
    https://github.com/scummvm/scummvm/commit/a89f9d1c5312331697874242f794e9c8b2462e3f
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:52+02:00

Commit Message:
PELROCK: Header cleanup

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/chrono.cpp
    engines/pelrock/console.h
    engines/pelrock/detection.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/events.cpp
    engines/pelrock/graphics.cpp
    engines/pelrock/menu.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/room.cpp
    engines/pelrock/saveload.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/util.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 7765d9c73c2..4e25e8b5629 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -24,7 +24,6 @@
 #include "audio/mixer.h"
 #include "graphics/paletteman.h"
 
-#include "pelrock.h"
 #include "pelrock/actions.h"
 #include "pelrock/backgroundbook.h"
 #include "pelrock/cdplayer.h"
diff --git a/engines/pelrock/chrono.cpp b/engines/pelrock/chrono.cpp
index 89d00a69c83..73f091dc016 100644
--- a/engines/pelrock/chrono.cpp
+++ b/engines/pelrock/chrono.cpp
@@ -22,7 +22,6 @@
 #include "common/events.h"
 #include "common/system.h"
 
-#include "chrono.h"
 #include "pelrock/chrono.h"
 #include "pelrock/pelrock.h"
 
diff --git a/engines/pelrock/console.h b/engines/pelrock/console.h
index 53f5dfca4ce..57c6848c7ad 100644
--- a/engines/pelrock/console.h
+++ b/engines/pelrock/console.h
@@ -24,6 +24,7 @@
 #define PELROCK_CONSOLE_H
 
 #include "gui/debugger.h"
+
 #include "pelrock/pelrock.h"
 
 namespace Pelrock {
diff --git a/engines/pelrock/detection.cpp b/engines/pelrock/detection.cpp
index d0b46ac7502..90c368e4324 100644
--- a/engines/pelrock/detection.cpp
+++ b/engines/pelrock/detection.cpp
@@ -26,6 +26,7 @@
 #include "common/str-array.h"
 #include "common/translation.h"
 #include "common/util.h"
+
 #include "pelrock/detection.h"
 #include "pelrock/detection_tables.h"
 
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 2894454b0a0..5d140a9e999 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -20,7 +20,6 @@
  */
 #include "common/stack.h"
 
-#include "dialog.h"
 #include "pelrock/dialog.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
diff --git a/engines/pelrock/events.cpp b/engines/pelrock/events.cpp
index c12669baa86..355d97ce389 100644
--- a/engines/pelrock/events.cpp
+++ b/engines/pelrock/events.cpp
@@ -20,7 +20,6 @@
  */
 #include "common/events.h"
 
-#include "events.h"
 #include "pelrock/events.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/util.h"
diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index feaf410cb64..ea403aa162d 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -24,7 +24,6 @@
 #include "graphics/cursorman.h"
 #include "graphics/paletteman.h"
 
-#include "graphics.h"
 #include "pelrock/graphics.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/util.h"
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index f381375751b..edcd2df4222 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -26,7 +26,6 @@
 #include "graphics/paletteman.h"
 #include "graphics/thumbnail.h"
 
-#include "menu.h"
 #include "pelrock/menu.h"
 #include "pelrock/offsets.h"
 #include "pelrock/pelrock.h"
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 14d790970fd..81db3e85cd7 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -27,7 +27,6 @@
 #include "graphics/cursorman.h"
 #include "graphics/paletteman.h"
 
-#include "pelrock.h"
 #include "pelrock/actions.h"
 #include "pelrock/computer.h"
 #include "pelrock/console.h"
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 83d09536b6a..b13c58656ca 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -24,7 +24,6 @@
 #include "pelrock/pelrock.h"
 #include "pelrock/room.h"
 #include "pelrock/util.h"
-#include "resources.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index 6242a991895..76ed355e05d 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -23,6 +23,7 @@
 
 #include "common/scummsys.h"
 #include "common/stream.h"
+
 #include "pelrock/offsets.h"
 #include "pelrock/types.h"
 
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 3b83d558e1e..8ed72978615 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -23,7 +23,6 @@
 #include "pelrock/pelrock.h"
 #include "pelrock/room.h"
 #include "pelrock/util.h"
-#include "room.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/saveload.cpp b/engines/pelrock/saveload.cpp
index 008d1356b50..3aae8c5c511 100644
--- a/engines/pelrock/saveload.cpp
+++ b/engines/pelrock/saveload.cpp
@@ -20,7 +20,6 @@
  */
 #include "common/savefile.h"
 
-#include "pelrock.h"
 #include "pelrock/pelrock.h"
 #include "pelrock/types.h"
 
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 14e24b86d76..2c4e31d7a4d 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -35,7 +35,6 @@
 
 #include "pelrock/pelrock.h"
 #include "pelrock/sound.h"
-#include "sound.h"
 
 namespace Pelrock {
 
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index 012a9a7c90b..c7d3cbcf3a7 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -26,7 +26,6 @@
 #include "pelrock/pelrock.h"
 #include "pelrock/types.h"
 #include "pelrock/util.h"
-#include "util.h"
 
 namespace Pelrock {
 


Commit: a25a5816706546be757c82a5bba6528ffc018247
    https://github.com/scummvm/scummvm/commit/a25a5816706546be757c82a5bba6528ffc018247
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:52+02:00

Commit Message:
PELROCK: Cleans up sliding puzzle

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/slidingpuzzle.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 4e25e8b5629..6e31d2d0dcc 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -664,8 +664,6 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 	case 377:
 		_state->setCurrentRoot(45, 3, 0);
 		break;
-	case 30840:
-		break;
 
 	case 323:
 		_state->setCurrentRoot(47, 1, 0);
diff --git a/engines/pelrock/slidingpuzzle.h b/engines/pelrock/slidingpuzzle.h
index 55a47a897ab..e7f37cada51 100644
--- a/engines/pelrock/slidingpuzzle.h
+++ b/engines/pelrock/slidingpuzzle.h
@@ -19,10 +19,9 @@
  *
  */
 
-#ifndef PELROCK_SLIDING_PUZZLE_H
-#define PELROCK_SLIDING_PUZZLE_H
+#ifndef PELROCK_SLIDINGPUZZLE_H
+#define PELROCK_SLIDINGPUZZLE_H
 
-#include "common/scummsys.h"
 #include "graphics/managed_surface.h"
 
 #include "pelrock/events.h"


Commit: e84cbd9ff234d0aa68a8d015ad4f68b1cc1cba2f
    https://github.com/scummvm/scummvm/commit/e84cbd9ff234d0aa68a8d015ad4f68b1cc1cba2f
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:52+02:00

Commit Message:
PELROCK: Decorate errors with function signature

Changed paths:
    engines/pelrock/backgroundbook.cpp
    engines/pelrock/computer.cpp
    engines/pelrock/menu.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/video.cpp


diff --git a/engines/pelrock/backgroundbook.cpp b/engines/pelrock/backgroundbook.cpp
index b9c78d1d897..b66776d5f2d 100644
--- a/engines/pelrock/backgroundbook.cpp
+++ b/engines/pelrock/backgroundbook.cpp
@@ -118,7 +118,7 @@ void BackgroundBook::loadRoomNames() {
 	Common::StringArray roomNames;
 	Common::File juegoExe;
 	if (!juegoExe.open(Common::Path("JUEGO.EXE"))) {
-		error("Couldnt find file JUEGO.EXE");
+		error("BackgroundBook::loadRoomNames(): Couldnt find file JUEGO.EXE");
 	}
 
 	size_t namesSize = kRoomNamesSize;
diff --git a/engines/pelrock/computer.cpp b/engines/pelrock/computer.cpp
index 1b97e0eb3a1..5e436df9915 100644
--- a/engines/pelrock/computer.cpp
+++ b/engines/pelrock/computer.cpp
@@ -59,7 +59,7 @@ Common::StringArray split(const Common::String &str) {
 void Computer::init() {
 	Common::File alfred7File;
 	if (!alfred7File.open("ALFRED.7")) {
-		error("Could not open ALFRED.7");
+		error("Computer::init(): Could not open ALFRED.7");
 		return;
 	}
 
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index edcd2df4222..3fe727a6e7e 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -492,7 +492,7 @@ void MenuManager::showCredits() {
 	_compositeBuffer.clear(0);
 	Common::File alfred7;
 	if (!alfred7.open(Common::Path("ALFRED.7"))) {
-		error("Could not open ALFRED.7");
+		error("MenuManager::showCredits(): Could not open ALFRED.7");
 		return;
 	}
 
@@ -683,7 +683,7 @@ void MenuManager::loadMenu() {
 
 	Common::File alfred7;
 	if (!alfred7.open(Common::Path("ALFRED.7"))) {
-		error("Could not open ALFRED.7");
+		error("MenuManager::loadMenu(): Could not open ALFRED.7");
 		return;
 	}
 
@@ -804,7 +804,7 @@ void MenuManager::loadMenuTexts() {
 
 	Common::File exe;
 	if (!exe.open("JUEGO.EXE")) {
-		error("Couldnt find file JUEGO.EXE");
+		error("MenuManager::loadMenuTexts(): Couldnt find file JUEGO.EXE");
 	}
 	byte *descBuffer = new byte[kInventoryDescriptionsSize];
 	exe.seek(kInventoryDescriptionsOffset, SEEK_SET);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 81db3e85cd7..e983c9255f6 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -181,7 +181,7 @@ void PelrockEngine::init() {
 void PelrockEngine::loadInventoryArrows() {
 	Common::File alfred7;
 	if (!alfred7.open("ALFRED.7")) {
-		error("Failed to open ALFRED.7 to load inventory arrows");
+		error("PelrockEngine::loadInventoryArrows(): Failed to open ALFRED.7 to load inventory arrows");
 		return;
 	}
 	alfred7.seek(kInventoryArrowsOffset, SEEK_SET);
@@ -1676,7 +1676,7 @@ void PelrockEngine::checkMouseHover() {
 void PelrockEngine::setScreen(int roomNumber) {
 	Common::File roomFile;
 	if (!roomFile.open(Common::Path("ALFRED.1"))) {
-		error("Could not open ALFRED.1");
+		error("PelrockEngine::setScreen(): Could not open ALFRED.1");
 		return;
 	}
 	changeCursor(DEFAULT);
@@ -2094,7 +2094,7 @@ void PelrockEngine::endingScene() {
 	g_system->getPaletteManager()->setPalette(palette, 0, 256);
 	Common::File alfred7;
 	if (!alfred7.open(Common::Path("ALFRED.7"))) {
-		error("Could not open ALFRED.7");
+		error("PelrockEngine::endingScene(): Could not open ALFRED.7");
 		return;
 	}
 	byte *decompressedBuf = nullptr;
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index b13c58656ca..68a40a352ce 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -130,7 +130,7 @@ ResourceManager::~ResourceManager() {
 void ResourceManager::loadCursors() {
 	Common::File alfred7File;
 	if (!alfred7File.open("ALFRED.7")) {
-		error("Couldnt find file ALFRED.7");
+		error("ResourceManager::loadCursors(): Couldnt find file ALFRED.7");
 	}
 	for (int i = 0; i < 5; i++) {
 		uint32 cursorOffset = cursor_offsets[i];
@@ -144,7 +144,7 @@ void ResourceManager::loadCursors() {
 void ResourceManager::loadInteractionIcons() {
 	Common::File alfred7File;
 	if (!alfred7File.open("ALFRED.7")) {
-		error("Couldnt find file ALFRED.7");
+		error("ResourceManager::loadInteractionIcons(): Couldnt find file ALFRED.7");
 	}
 
 	alfred7File.seek(kBalloonFramesOffset, SEEK_SET);
@@ -163,7 +163,7 @@ void ResourceManager::loadInteractionIcons() {
 	alfred7File.close();
 	Common::File alfred4File;
 	if (!alfred4File.open("ALFRED.4")) {
-		error("Couldnt find file ALFRED.4");
+		error("ResourceManager::loadInteractionIcons(): Couldnt find file ALFRED.4");
 	}
 
 	int iconSize = kVerbIconHeight * kVerbIconWidth;
@@ -177,7 +177,7 @@ void ResourceManager::loadInteractionIcons() {
 void ResourceManager::loadAlfredAnims() {
 	Common::File alfred3;
 	if (!alfred3.open(Common::Path("ALFRED.3"))) {
-		error("Could not open ALFRED.3");
+		error("ResourceManager::loadAlfredAnims(): Could not open ALFRED.3");
 		return;
 	}
 	int alfred3Size = alfred3.size();
@@ -256,7 +256,7 @@ void ResourceManager::loadAlfredAnims() {
 
 	Common::File alfred7;
 	if (!alfred7.open(Common::Path("ALFRED.7"))) {
-		error("Could not open ALFRED.7");
+		error("ResourceManager::loadAlfredAnims(): Could not open ALFRED.7");
 		return;
 	}
 	int spriteMapSize = frameSize * 11;
@@ -299,7 +299,7 @@ void ResourceManager::loadAlfredAnims() {
 void ResourceManager::loadOtherSpecialAnim(uint32 offset, bool rleCompressed, byte *&buffer, size_t &bufferSize) {
 	Common::File alfred7;
 	if (!alfred7.open(Common::Path("ALFRED.7"))) {
-		error("Could not open ALFRED.7");
+		error("ResourceManager::loadOtherSpecialAnim(): Could not open ALFRED.7");
 		return;
 	}
 
@@ -321,7 +321,7 @@ void ResourceManager::loadAlfredSpecialAnim(int numAnim, bool reverse) {
 	Common::String filename = Common::String::format("ALFRED.%d", anim.numAlfred);
 	Common::File alfredFile;
 	if (!alfredFile.open(Common::Path(filename))) {
-		error("Could not open %s", filename.c_str());
+		error("ResourceManager::loadAlfredSpecialAnim(): Could not open %s", filename.c_str());
 		return;
 	}
 
@@ -367,7 +367,7 @@ void ResourceManager::loadInventoryItems() {
 	// loadInventoryDescriptions();
 	Common::File alfred4File;
 	if (!alfred4File.open("ALFRED.4")) {
-		error("Couldnt find file ALFRED.4");
+		error("ResourceManager::loadInventoryItems(): Couldnt find file ALFRED.4");
 	}
 	uint32 iconsSize = alfred4File.size() - kInventoryIconsTailSize;
 	byte *iconData = new byte[iconsSize];
@@ -385,7 +385,7 @@ void ResourceManager::loadHardcodedText() {
 
 	Common::File exe;
 	if (!exe.open("JUEGO.EXE")) {
-		error("Couldnt find file JUEGO.EXE");
+		error("ResourceManager::loadHardcodedText(): Couldnt find file JUEGO.EXE");
 	}
 	byte *descBuffer = new byte[kAlfredResponsesSize];
 	exe.seek(kAlfredResponsesOffset, SEEK_SET);
@@ -428,7 +428,7 @@ Common::Array<Common::StringArray> ResourceManager::loadComputerText() {
 
 	Common::File exe;
 	if (!exe.open("JUEGO.EXE")) {
-		error("Couldnt find file JUEGO.EXE");
+		error("ResourceManager::loadComputerText(): Couldnt find file JUEGO.EXE");
 	}
 	int bufSize = kComputerTextSize;
 	byte *computerTextBuf = new byte[bufSize];
@@ -444,7 +444,7 @@ Common::Array<Common::StringArray> ResourceManager::loadComputerText() {
 void ResourceManager::getExtraScreen(int screenIndex, byte *screenBuf, byte *palette) {
 	Common::File alfred7;
 	if (!alfred7.open("ALFRED.7")) {
-		error("Couldnt find file ALFRED.7");
+		error("ResourceManager::getExtraScreen(): Couldnt find file ALFRED.7");
 	}
 	ExtraScreen screen = extraScreens[screenIndex];
 	mergeRleBlocks(&alfred7, screen.offset, 8, screenBuf);
@@ -461,7 +461,7 @@ void ResourceManager::getExtraScreen(int screenIndex, byte *screenBuf, byte *pal
 Common::Array<Common::StringArray> ResourceManager::getCredits() {
 	Common::File exe;
 	if (!exe.open("JUEGO.EXE")) {
-		error("Couldnt find file JUEGO.EXE");
+		error("ResourceManager::getCredits(): Couldnt find file JUEGO.EXE");
 	}
 	byte *descBuffer = new byte[kCreditsSize];
 	exe.seek(kCreditsOffset, SEEK_SET);
@@ -527,7 +527,7 @@ Common::Array<Common::StringArray> ResourceManager::processTextData(byte *data,
 Pelrock::Sticker ResourceManager::getSticker(int stickerIndex) {
 	Common::File alfred6File;
 	if (!alfred6File.open("ALFRED.6")) {
-		error("Couldnt find file ALFRED.6");
+		error("ResourceManager::getSticker(): Couldnt find file ALFRED.6");
 	}
 
 	uint32 stickerOffset = stickerOffsets[stickerIndex];
@@ -545,7 +545,7 @@ Pelrock::Sticker ResourceManager::getSticker(int stickerIndex) {
 byte *ResourceManager::loadStickerPixels(const Sticker &sticker) {
 	Common::File alfred6File;
 	if (!alfred6File.open("ALFRED.6")) {
-		error("Couldnt find file ALFRED.6");
+		error("ResourceManager::loadStickerPixels(): Couldnt find file ALFRED.6");
 	}
 	uint32 pixelOffset = stickerOffsets[sticker.stickerIndex] + 6; // skip x(2)+y(2)+w(1)+h(1)
 	alfred6File.seek(pixelOffset, SEEK_SET);
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 8ed72978615..37661d86e65 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -97,7 +97,7 @@ void RoomManager::loadWaterPaletteRemap() {
 	// Extra remap for water effect
 	Common::File exe;
 	if (!exe.open("JUEGO.EXE")) {
-		error("Couldnt find file JUEGO.EXE");
+		error("RoomManager::loadWaterPaletteRemap(): Couldnt find file JUEGO.EXE");
 	}
 	exe.seek(kPaletteRemapOffset, SEEK_SET);
 	exe.read(_paletteRemaps[4], 256);
@@ -935,7 +935,7 @@ Common::Array<HotSpot> RoomManager::unifyHotspots(Common::Array<Pelrock::Sprite>
 void RoomManager::init() {
 	Common::File alfred8;
 	if (!alfred8.open("ALFRED.8")) {
-		error("Couldnt find file ALFRED.8");
+		error("RoomManager::init(): Couldnt find file ALFRED.8");
 	}
 }
 
@@ -1191,7 +1191,7 @@ void RoomManager::addDisabledChoice(ChoiceOption choice) {
 void RoomManager::resetMetadataDefaults(byte room, byte *&data, size_t size) {
 	Common::File alfred8;
 	if (!alfred8.open("ALFRED.8")) {
-		error("Couldnt find file ALFRED.8");
+		error("RoomManager::resetMetadataDefaults(): Couldnt find file ALFRED.8");
 	}
 	bool roomDone = false;
 	while (!alfred8.eos() && !roomDone) {
@@ -1226,7 +1226,7 @@ void RoomManager::loadRoomTalkingAnimations(int roomNumber) {
 	TalkingAnims talkHeader;
 	Common::File talkFile;
 	if (!talkFile.open("ALFRED.2")) {
-		error("Couldnt find file ALFRED.2");
+		error("RoomManager::loadRoomTalkingAnimations(): Couldnt find file ALFRED.2");
 	}
 
 	talkFile.seek(offset, SEEK_SET);
diff --git a/engines/pelrock/video.cpp b/engines/pelrock/video.cpp
index 2495bd2e084..c5b61654d0c 100644
--- a/engines/pelrock/video.cpp
+++ b/engines/pelrock/video.cpp
@@ -40,7 +40,7 @@ VideoManager::VideoManager(
 	_videoSurface.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 	_textSurface.create(640, 400, Graphics::PixelFormat::createFormatCLUT8());
 	if (!_introSndFile.open("introsnd.dat")) {
-		error("Could not open introsnd.dat");
+		error("VideoManager::VideoManager(): Could not open introsnd.dat");
 	}
 }
 
@@ -54,7 +54,7 @@ void VideoManager::playIntro() {
 	initMetadata();
 	Common::File videoFile;
 	if (!videoFile.open("ESCENAX.SSN")) {
-		error("Could not open ESCENAX.SSN");
+		error("VideoManager::playIntro(): Could not open ESCENAX.SSN");
 		return;
 	}
 	videoFile.seek(0, SEEK_SET);
@@ -268,7 +268,7 @@ void VideoManager::presentFrame() {
 void VideoManager::initMetadata() {
 	Common::File metadataFile;
 	if (!metadataFile.open("ESCENAX.SCR")) {
-		error("Could not open ESCENAX.SCR");
+		error("VideoManager::initMetadata(): Could not open ESCENAX.SCR");
 		return;
 	}
 


Commit: 9e1d34ce2eba5211d3fe39d0838a2b564fce2143
    https://github.com/scummvm/scummvm/commit/9e1d34ce2eba5211d3fe39d0838a2b564fce2143
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:53+02:00

Commit Message:
PELROCK: Simplify pixel setting in fonts

Changed paths:
    engines/pelrock/detection_tables.h
    engines/pelrock/fonts/large_font.cpp
    engines/pelrock/fonts/small_font.cpp
    engines/pelrock/fonts/small_font_double.cpp
    engines/pelrock/room.cpp


diff --git a/engines/pelrock/detection_tables.h b/engines/pelrock/detection_tables.h
index eb5c5c057cb..1e3944454b7 100644
--- a/engines/pelrock/detection_tables.h
+++ b/engines/pelrock/detection_tables.h
@@ -22,7 +22,7 @@
 namespace Pelrock {
 
 const PlainGameDescriptor pelrockGames[] = {
-	{ "pelrock", "Pelrock" },
+	{ "pelrock", "Alfred Pelrock: En Busca de un Sueño" },
 	{ 0, 0 }
 };
 
diff --git a/engines/pelrock/fonts/large_font.cpp b/engines/pelrock/fonts/large_font.cpp
index 7294b2ef8bf..2228bda30c9 100644
--- a/engines/pelrock/fonts/large_font.cpp
+++ b/engines/pelrock/fonts/large_font.cpp
@@ -132,9 +132,9 @@ void LargeFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint3
 			if (px < 0 || px >= dst->w || py < 0 || py >= dst->h)
 				continue;
 			if (val == 1) {
-				*((byte *)dst->getBasePtr(px, py)) = 0;
+				dst->setPixel(px, py, 0);
 			} else if (val == 2) {
-				*((byte *)dst->getBasePtr(px, py)) = color;
+				dst->setPixel(px, py, color);
 			}
 		}
 	}
diff --git a/engines/pelrock/fonts/small_font.cpp b/engines/pelrock/fonts/small_font.cpp
index d1de37d9a5c..083c9adc3f5 100644
--- a/engines/pelrock/fonts/small_font.cpp
+++ b/engines/pelrock/fonts/small_font.cpp
@@ -65,7 +65,7 @@ void SmallFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint3
 			bool pixelOn = (rowByte & (0x80 >> bit)) != 0;
 			if (pixelOn) {
 				if ((x + bit) < dst->w && (y + i) < dst->h) {
-					*((byte *)dst->getBasePtr(x + bit, y + i)) = color;
+					dst->setPixel(x + bit, y + i, color);
 				}
 			}
 		}
diff --git a/engines/pelrock/fonts/small_font_double.cpp b/engines/pelrock/fonts/small_font_double.cpp
index 4ebc5a580ed..4f1484685ef 100644
--- a/engines/pelrock/fonts/small_font_double.cpp
+++ b/engines/pelrock/fonts/small_font_double.cpp
@@ -42,8 +42,8 @@ void DoubleSmallFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y,
 			int yPos = y + (i * 2);
 			if (pixelOn) {
 				if ((x + bit) < dst->w && (yPos + 1) < dst->h) {
-					*((byte *)dst->getBasePtr(x + bit, yPos)) = color;
-					*((byte *)dst->getBasePtr(x + bit, yPos + 1)) = color;
+					dst->setPixel(x + bit, yPos, color);
+					dst->setPixel(x + bit, yPos + 1, color);
 				}
 			}
 		}
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 37661d86e65..1fe5be938da 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -1311,7 +1311,7 @@ static uint32 readUint24(Common::ReadStream &stream) {
 byte *RoomManager::loadShadowMap(int roomNumber) {
 	Common::File shadowMapFile;
 	if (!shadowMapFile.open("ALFRED.5")) {
-		error("Couldnt find file ALFRED.5");
+		error("RoomManager::loadShadowMap(): Couldnt find file ALFRED.5");
 	}
 
 	uint32 entryOffset = roomNumber * 6;
@@ -1339,7 +1339,7 @@ void RoomManager::loadRemaps(int roomNumber) {
 
 	Common::File remapFile;
 	if (!remapFile.open("ALFRED.9")) {
-		error("Couldnt find file ALFRED.9");
+		error("RoomManager::loadRemaps(): Couldnt find file ALFRED.9");
 	}
 
 	uint32 remapOffset = /* 0x200 + */ (roomNumber * 1024);


Commit: c83cb780a19ac22fc7239e71862087a22e2be8ac
    https://github.com/scummvm/scummvm/commit/c83cb780a19ac22fc7239e71862087a22e2be8ac
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:53+02:00

Commit Message:
PELROCK: Fixes on syntax according to code reviews

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/events.cpp
    engines/pelrock/graphics.cpp
    engines/pelrock/menu.cpp
    engines/pelrock/menu.h
    engines/pelrock/resources.cpp
    engines/pelrock/resources.h
    engines/pelrock/util.cpp
    engines/pelrock/util.h
    engines/pelrock/video.cpp


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index 6e31d2d0dcc..e42b8e12774 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -514,14 +514,13 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		advanceQuotesConversation(rootIndex, room);
 		break;
 	case 364: // riddle wrong answer: advance to next riddle
-	{
-		int targetIndex = rootIndex + 1;
 		if (rootIndex == 43) {
-			targetIndex = 27; // skip riddle explanation
+			_state->setCurrentRoot(room, 27, 0); // skip riddle explanation
+		}
+		else {
+			_state->setCurrentRoot(room, rootIndex + 1, 0);
 		}
-		_state->setCurrentRoot(room, targetIndex, 0);
 		break;
-	}
 	case 365: // riddle correct: set riddle-solved flag
 		_state->setFlag(FLAG_SOLVED_PARADOX, 1);
 		_state->setCurrentRoot(room, 1, 0);
@@ -605,11 +604,10 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
 	case 308: {
-		int targetBranch = rootIndex + 1;
-		if (targetBranch > 17) {
-			targetBranch = 2;
-		}
-		_state->setCurrentRoot(room, targetBranch, 0);
+		if (rootIndex + 1 > 17)
+			_state->setCurrentRoot(room, 2, 0);
+		else
+			_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
 	}
 		/* pyramid merchant*/
@@ -656,11 +654,11 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		_state->setCurrentRoot(45, 1, 0);
 		_sound->playSound("TWANGZZZ.SMP", 0);
 		break;
-	case 376: {
+	case 376:
 		playAlfredSpecialAnim(14);
 		loadExtraScreenAndPresent(12);
 		_state->setCurrentRoot(45, 2, 0);
-	} break;
+		break;
 	case 377:
 		_state->setCurrentRoot(45, 3, 0);
 		break;
@@ -1300,7 +1298,6 @@ void PelrockEngine::pickUpBook(int i) {
 		_alfredState.isWalkingCancelable = false;
 		walkAndAction(_room->findHotspotByExtra(358), TALK);
 		if (!_state->hasInventoryItem(3)) {
-
 			_state->setCurrentRoot(9, 0, 0);
 		} else {
 			_state->setCurrentRoot(9, 3, 0);
@@ -1481,7 +1478,6 @@ void PelrockEngine::giveWaterToGuard(int inventoryObject, HotSpot *hotspot) {
 }
 
 void PelrockEngine::guardMovement() {
-
 	// guard running
 	Sprite *sprite = _room->findSpriteByIndex(0);
 	sprite->animData[0].nframes = 5;
@@ -2249,18 +2245,12 @@ void PelrockEngine::sayRandomIncorrectResponse() {
 
 void PelrockEngine::chooseCorrectDoor() {
 	playAlfredSpecialAnim(1);
-	byte puertaBuena = _state->getFlag(FLAG_CORRECT_DOOR);
-	if (puertaBuena == 0) { // if not set yet, choose randomly
-		int choice = getRandomNumber(1);
-		_state->setFlag(FLAG_CORRECT_DOOR, choice + 1);
-	}
-	puertaBuena = _state->getFlag(FLAG_CORRECT_DOOR);
-	Common::String doorText = _res->_izquierda;
-	if (puertaBuena == 1) {
-		doorText = _res->_izquierda;
-	} else if (puertaBuena == 2) {
-		doorText = _res->_derecha;
+	byte correctDoor = _state->getFlag(FLAG_CORRECT_DOOR);
+	if (correctDoor == 0) { // if not set yet, choose randomly
+		_state->setFlag(FLAG_CORRECT_DOOR, getRandomNumber(1) + 1);
 	}
+	correctDoor = _state->getFlag(FLAG_CORRECT_DOOR);
+	Common::String doorText = (correctDoor == 1) ? _res->_left : _res->_right;
 	Common::StringArray fullTextArray = _res->_ingameTexts[kTextPuertaAutenticaIzquierda];
 	fullTextArray[0] = fullTextArray[0].substr(0, 45);
 	fullTextArray[0].append(doorText);
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index 5d140a9e999..b111b899d73 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -284,7 +284,6 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 		}
 
 		_screen->transBlitFrom(*s, s->getRect(), Common::Point(xPos, yPos), 255);
-		// drawPos(_screen, xPos, yPos, speakerId);
 
 		_screen->markAllDirty();
 		_screen->update();
diff --git a/engines/pelrock/events.cpp b/engines/pelrock/events.cpp
index 355d97ce389..abd73d25506 100644
--- a/engines/pelrock/events.cpp
+++ b/engines/pelrock/events.cpp
@@ -75,11 +75,7 @@ void PelrockEventManager::pollEvent() {
 			_rightMouseButton = 1;
 			break;
 		case Common::EVENT_RBUTTONUP:
-			if (_rightMouseButton == 1) {
-				_rightMouseClicked = true;
-			} else {
-				_rightMouseClicked = false;
-			}
+			_rightMouseClicked = (_rightMouseButton == 1);
 			_rightMouseButton = 0;
 			break;
 		default:
@@ -100,7 +96,6 @@ void PelrockEventManager::pollEvent() {
 void PelrockEventManager::waitForKey() {
 	bool waitForKey = false;
 	Common::Event e;
-	debug("Waiting for key!");
 	while (!waitForKey && !g_engine->shouldQuit()) {
 		while (g_system->getEventManager()->pollEvent(e)) {
 			if (e.type == Common::EVENT_KEYDOWN) {
diff --git a/engines/pelrock/graphics.cpp b/engines/pelrock/graphics.cpp
index ea403aa162d..863d107e4cb 100644
--- a/engines/pelrock/graphics.cpp
+++ b/engines/pelrock/graphics.cpp
@@ -163,7 +163,6 @@ void GraphicsManager::copyBackgroundToBuffer() {
 
 void GraphicsManager::presentFrame() {
 	g_engine->_screen->blitFrom(g_engine->_compositeBuffer);
-	// g_engine->paintDebugLayer();
 	g_engine->_screen->markAllDirty();
 }
 
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 3fe727a6e7e..32268424842 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -344,7 +344,7 @@ bool MenuManager::checkMouseClick(int x, int y) {
 			break;
 		}
 		// CANCEL
-		if (_cancelarRect.contains(x, y)) {
+		if (_cancelRect.contains(x, y)) {
 			_editingSaveSlot = -1;
 			backToMainMenu();
 			break;
@@ -372,8 +372,8 @@ bool MenuManager::checkMouseClick(int x, int y) {
 				_saveGamePage++;
 			break;
 		}
-		// CANCELAR
-		if (_cancelarRect.contains(x, y)) {
+		// Cancel
+		if (_cancelRect.contains(x, y)) {
 			backToMainMenu();
 			break;
 		}
@@ -938,8 +938,8 @@ void MenuManager::drawSaves() {
 	}
 
 	// CANCEL row
-	_cancelarRect = Common::Rect(startX - 2, y - 1, startX + overlayW - 2, y + _textLineH);
-	byte cancelColor = _cancelarRect.contains(mousePos) ? 18 : kWhiteColor;
+	_cancelRect = Common::Rect(startX - 2, y - 1, startX + overlayW - 2, y + _textLineH);
+	byte cancelColor = _cancelRect.contains(mousePos) ? 18 : kWhiteColor;
 	Common::String cancelText = _menuTexts[4][0].substr(2, _menuTexts[4][0].size() - 2);
 	drawText(_compositeBuffer, g_engine->_smallFont, cancelText, startX, y, overlayW, cancelColor);
 }
diff --git a/engines/pelrock/menu.h b/engines/pelrock/menu.h
index 04f89c96371..a8952090c57 100644
--- a/engines/pelrock/menu.h
+++ b/engines/pelrock/menu.h
@@ -206,7 +206,7 @@ private:
 	int _saveGamePage = 0;
 	int _editingSaveSlot = -1;                  // -1 = not editing any slot
 	Common::String _editingName;                // name being typed for a save
-	Common::Rect _cancelarRect;                 // hit-rect for the CANCELAR row
+	Common::Rect _cancelRect;                 // hit-rect for the CANCELAR row
 	Common::Array<Common::Rect> _saveSlotRects; // hit-rects for the 8 visible save rows
 	Common::StringArray _saveDescriptions;      // indexed by slot 0-255
 };
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 68a40a352ce..13d14dcf73a 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -392,8 +392,8 @@ void ResourceManager::loadHardcodedText() {
 	exe.read(descBuffer, kAlfredResponsesSize);
 	_ingameTexts = processTextData(descBuffer, kAlfredResponsesSize);
 	// exe.seek(-1, SEEK_CUR);
-	_izquierda = exe.readString();
-	_derecha = exe.readString(0xFD);
+	_left = exe.readString();
+	_right = exe.readString(0xFD);
 	byte *terminatorBuffer = new byte[39];
 	exe.seek(kConversationTerminatorOffset, SEEK_SET);
 	exe.read(terminatorBuffer, 39);
diff --git a/engines/pelrock/resources.h b/engines/pelrock/resources.h
index 76ed355e05d..315fa895a4a 100644
--- a/engines/pelrock/resources.h
+++ b/engines/pelrock/resources.h
@@ -83,8 +83,8 @@ public:
 	byte *_verbIcons[9];
 	byte *_popUpBalloon = nullptr;
 	Common::Array<Common::StringArray> _ingameTexts;
-	Common::String _izquierda;
-	Common::String _derecha;
+	Common::String _left;
+	Common::String _right;
 	Common::String _conversationTerminator;
 
 	// Special anims
diff --git a/engines/pelrock/util.cpp b/engines/pelrock/util.cpp
index c7d3cbcf3a7..b9cbfef8ba9 100644
--- a/engines/pelrock/util.cpp
+++ b/engines/pelrock/util.cpp
@@ -250,21 +250,21 @@ byte decodeChar(byte b) {
 
 	switch (b) {
 	case 0x82:
-		return special_chars[1];
+		return specialChars[1];
 	case 0x83:
-		return special_chars[0];
+		return specialChars[0];
 	case 0x80:
-		return special_chars[3]; // n tilde
+		return specialChars[3]; // n tilde
 	case 0x7F:
-		return special_chars[4];
+		return specialChars[4];
 	case 0x7E:
-		return special_chars[5];
+		return specialChars[5];
 	case 0x7D:
-		return special_chars[6];
+		return specialChars[6];
 	case 0x7C:
-		return special_chars[7];
+		return specialChars[7];
 	case 0x7B:
-		return special_chars[8];
+		return specialChars[8];
 	default:
 		return b;
 	}
diff --git a/engines/pelrock/util.h b/engines/pelrock/util.h
index 95f73dcff12..865558294e7 100644
--- a/engines/pelrock/util.h
+++ b/engines/pelrock/util.h
@@ -48,7 +48,7 @@ void drawRect(Graphics::ManagedSurface *surface, int x, int y, int w, int h, byt
 void drawPos(Graphics::ManagedSurface *surface, int x, int y, byte color);
 void drawPaletteSquares(Graphics::ManagedSurface &dest, byte *palette);
 
-static const int special_chars[] = {
+static const int specialChars[] = {
 	168, // inverted ?
 	173, // inverted !
 	165, // capital N tilde
diff --git a/engines/pelrock/video.cpp b/engines/pelrock/video.cpp
index c5b61654d0c..7047045d89e 100644
--- a/engines/pelrock/video.cpp
+++ b/engines/pelrock/video.cpp
@@ -179,14 +179,11 @@ byte *VideoManager::decodeCopyBlock(byte *data, uint32 offset) {
 	// the first 3 bytes are the offset within the screen to which to
 	// copy the bytes. The 5th byte is the length of the block to copy.
 	while (true) {
-		byte dest_lo = data[pos];
-		byte dest_mid = data[pos + 1];
-		byte dest_hi = data[pos + 2];
 		byte length = data[pos + 4];
 		if (length == 0) {
 			break;
 		}
-		uint32 dest_offset = dest_lo | (dest_mid << 8) | (dest_hi << 16);
+		uint32 dest_offset = READ_LE_UINT24(data + pos);
 
 		if (dest_offset + length > 256000) {
 			break;


Commit: 1dfb93703d8326d1e5764d6179d5bef668b4bbd6
    https://github.com/scummvm/scummvm/commit/1dfb93703d8326d1e5764d6179d5bef668b4bbd6
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:53+02:00

Commit Message:
PELROCK: Syntax fixes from code reviews

Changed paths:
    engines/pelrock/actions.cpp
    engines/pelrock/dialog.cpp
    engines/pelrock/menu.cpp
    engines/pelrock/pelrock.cpp
    engines/pelrock/room.cpp
    engines/pelrock/room.h


diff --git a/engines/pelrock/actions.cpp b/engines/pelrock/actions.cpp
index e42b8e12774..5062db15a0e 100644
--- a/engines/pelrock/actions.cpp
+++ b/engines/pelrock/actions.cpp
@@ -500,12 +500,10 @@ void PelrockEngine::dialogActionTrigger(uint16 actionTrigger, byte room, byte ro
 		break;
 	}
 	case 360: // neutral reset: counter = 0
-	{
 		_state->setFlag(FLAG_CORRECT_ANSWERS, 0);
 		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		advanceQuotesConversation(rootIndex, room);
 		break;
-	}
 	case 361: // "no sé" (I don't know): no counter change, just advance
 		_state->setCurrentRoot(room, rootIndex + 1, 0);
 		break;
@@ -1225,8 +1223,8 @@ void PelrockEngine::usePumpkinWithRiver(int inventoryObject, HotSpot *hotspot) {
 		playAlfredSpecialAnim(5);
 		{
 			// Copy crocodile into background so it sticks during fade
-			static const int srcX = 189, srcY = 260;
-			static const int copyW = 127, copyH = 80;
+			const int srcX = 189, srcY = 260;
+			const int copyW = 127, copyH = 80;
 			Common::Rect copyRect(srcX, srcY, srcX + copyW, srcY + copyH);
 			_currentBackground.blitFrom(_compositeBuffer, copyRect, Common::Point(srcX, srcY));
 		}
@@ -1521,7 +1519,7 @@ void PelrockEngine::pickUpStone(HotSpot *hotspot) {
 }
 
 void PelrockEngine::playSpecialAnim(uint32 offset, bool compressed, int x, int y, int width, int height, int numFrames) {
-	size_t frameSize = width * height;
+	uint frameSize = width * height;
 	size_t bufSize = frameSize * numFrames;
 	byte *animData = new byte[bufSize];
 	_res->loadOtherSpecialAnim(offset, compressed, animData, bufSize);
@@ -1915,11 +1913,10 @@ void PelrockEngine::performActionTrigger(uint16 actionTrigger) {
 		_dialog->say(_res->_ingameTexts[kTextNoSeTeCurraCercarte]);
 		break;
 	}
-	case 375: {
+	case 375:
 		teleportToPrincess();
 		break;
 	}
-	}
 }
 
 // Bresenham line draw using a 256-byte palette remap table (semi-transparent effect).
@@ -1970,6 +1967,7 @@ void PelrockEngine::teleportToPrincess() {
 			_events->pollEvent();
 			renderScene(OVERLAY_NONE);
 			_screen->update();
+			g_system->delayMillis(10);
 		}
 
 		_sound->playSound(_room->_roomSfx[3], 0);
@@ -2225,7 +2223,7 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		book.run();
 		break;
 	}
-	default: {
+	default:
 		// Original game incorrectly checked until object 47; Reading any book.
 		if (inventoryObject >= 11 && inventoryObject <= 58) {
 			playAlfredSpecialAnim(0);
@@ -2235,7 +2233,6 @@ void PelrockEngine::useOnAlfred(int inventoryObject) {
 		sayRandomIncorrectResponse();
 		break;
 	}
-	}
 }
 
 void PelrockEngine::sayRandomIncorrectResponse() {
diff --git a/engines/pelrock/dialog.cpp b/engines/pelrock/dialog.cpp
index b111b899d73..312ee25c896 100644
--- a/engines/pelrock/dialog.cpp
+++ b/engines/pelrock/dialog.cpp
@@ -251,7 +251,7 @@ void DialogManager::displayDialogue(Common::Array<Common::Array<Common::String>>
 	uint32 pageTtlMs = calcPageTtlMs(dialogueLines[curPage]);
 	uint32 pageStartMs = g_system->getMillis();
 
-	if(speakerId != kAlfredColor) {
+	if (speakerId != kAlfredColor) {
 		_isNPCTalking = true;
 	}
 	// Render loop - display text and wait for click or TTL
diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 32268424842..e0a9d47aa9b 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -927,7 +927,7 @@ void MenuManager::drawSaves() {
 		drawText(_compositeBuffer, g_engine->_smallFont, slotNumber, startX, y, overlayW, kNumberColor);
 		drawText(_compositeBuffer, g_engine->_smallFont, slotText, startX + slotNumberWidth, y, overlayW - slotNumberWidth, textColor);
 
-		if(_editingSaveSlot == slot) {
+		if (_editingSaveSlot == slot) {
 			// Draw cursor
 			int cursorX = startX + slotNumberWidth + g_engine->_smallFont->getStringWidth(slotText);
 			drawText(_compositeBuffer, g_engine->_smallFont, Common::String(kCursorChar), cursorX, y, overlayW - (cursorX - startX), kWhiteColor);
diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index e983c9255f6..6e19f5b899b 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -280,7 +280,6 @@ void PelrockEngine::travelToEgypt() {
 	int frameCount = 0;
 	while (!shouldQuit() && frameCount < 96) {
 		_events->pollEvent();
-		g_system->delayMillis(10);
 		_chrono->updateChrono();
 		if (_chrono->_gameTick && _chrono->getFrameCount() % 2 == 0) {
 			int colorIndex = 160 + frameCount;
@@ -294,6 +293,7 @@ void PelrockEngine::travelToEgypt() {
 
 		_screen->markAllDirty();
 		_screen->update();
+		g_system->delayMillis(10);
 	}
 	_graphics->clearScreen();
 
@@ -388,8 +388,7 @@ bool PelrockEngine::renderScene(int overlayMode) {
 		return true;
 	}
 
-	switch (_room->_currentRoomNumber) {
-	case 2: {
+	if (_room->_currentRoomNumber == 2) {
 		// Easter egg in room 2, pressing x 250 times after the character has mentioned it triggers a special dialog
 		if (_events->_lastKeyEvent == Common::KEYCODE_x) {
 			_events->_lastKeyEvent = Common::KEYCODE_INVALID;
@@ -400,8 +399,6 @@ bool PelrockEngine::renderScene(int overlayMode) {
 				}
 			}
 		}
-		break;
-	}
 	}
 
 	return false;
@@ -860,7 +857,7 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 		}
 		break;
 	}
-	case ALFRED_TALKING: {
+	case ALFRED_TALKING:
 		drawAlfred(_res->alfredTalkFrames[_alfredState.direction][_alfredState.curFrame]);
 		if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0)
 			_alfredState.curFrame++;
@@ -868,7 +865,6 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 			_alfredState.curFrame = 0;
 		}
 		break;
-	}
 	case ALFRED_COMB: {
 
 		drawAlfred(_res->alfredCombFrames[_alfredState.direction][_alfredState.curFrame]);
@@ -1125,54 +1121,37 @@ void PelrockEngine::drawNextFrame(Sprite *sprite) {
 }
 
 void PelrockEngine::paintDebugLayer() {
-	bool showWalkboxes = true;
-
-	if (showWalkboxes) {
-		for (uint i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
-			WalkBox box = _room->_currentRoomWalkboxes[i];
-			drawRect(_screen, box.x, box.y, box.w, box.h, 13);
-		}
+	for (uint i = 0; i < _room->_currentRoomWalkboxes.size(); i++) {
+		WalkBox box = _room->_currentRoomWalkboxes[i];
+		drawRect(_screen, box.x, box.y, box.w, box.h, 13);
 	}
 
-	bool showSprites = true;
-	if (showSprites) {
-		for (uint i = 0; i < _room->_currentRoomAnims.size(); i++) {
-			Sprite sprite = _room->_currentRoomAnims[i];
-			if (sprite.zOrder == 255) {
-				continue;
-			}
-			drawRect(_screen, sprite.x, sprite.y, sprite.w, sprite.h, 14);
-			_smallFont->drawString(_screen, Common::String::format("S %d", sprite.index), sprite.x + 2, sprite.y, 640, 14);
+	for (uint i = 0; i < _room->_currentRoomAnims.size(); i++) {
+		Sprite sprite = _room->_currentRoomAnims[i];
+		if (sprite.zOrder == 255) {
+			continue;
 		}
+		drawRect(_screen, sprite.x, sprite.y, sprite.w, sprite.h, 14);
+		_smallFont->drawString(_screen, Common::String::format("S %d", sprite.index), sprite.x + 2, sprite.y, 640, 14);
 	}
 
-	bool showHotspots = true;
-	if (showHotspots) {
-		for (uint i = 0; i < _room->_currentRoomHotspots.size(); i++) {
-			HotSpot hotspot = _room->_currentRoomHotspots[i];
-			if (!hotspot.isEnabled || hotspot.isSprite)
-				continue;
-			drawRect(_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 12);
-			_smallFont->drawString(_screen, Common::String::format("HS %d - %d", hotspot.index - _room->_currentRoomAnims.size(), hotspot.extra), hotspot.x + 2, hotspot.y + 2, 640, 12);
-		}
+	for (uint i = 0; i < _room->_currentRoomHotspots.size(); i++) {
+		HotSpot hotspot = _room->_currentRoomHotspots[i];
+		if (!hotspot.isEnabled || hotspot.isSprite)
+			continue;
+		drawRect(_screen, hotspot.x, hotspot.y, hotspot.w, hotspot.h, 12);
+		_smallFont->drawString(_screen, Common::String::format("HS %d - %d", hotspot.index - _room->_currentRoomAnims.size(), hotspot.extra), hotspot.x + 2, hotspot.y + 2, 640, 12);
 	}
-
-	bool showExits = true;
-	if (showExits) {
-		for (uint i = 0; i < _room->_currentRoomExits.size(); i++) {
-			Exit exit = _room->_currentRoomExits[i];
-			drawRect(_screen, exit.x, exit.y, exit.w, exit.h, 200 + i);
-			_smallFont->drawString(_screen, Common::String::format("Exit %d -> Room %d", i, exit.targetRoom), exit.x + 2, exit.y + 2, 640, 14);
-		}
+	for (uint i = 0; i < _room->_currentRoomExits.size(); i++) {
+		Exit exit = _room->_currentRoomExits[i];
+		drawRect(_screen, exit.x, exit.y, exit.w, exit.h, 200 + i);
+		_smallFont->drawString(_screen, Common::String::format("Exit %d -> Room %d", i, exit.targetRoom), exit.x + 2, exit.y + 2, 640, 14);
 	}
 
 	drawPos(_screen, _alfredState.x, _alfredState.y, 13);
 	drawPos(_screen, _alfredState.x, _alfredState.y - kAlfredFrameHeight, 13);
 	drawPos(_screen, _curWalkTarget.x, _curWalkTarget.y, 100);
 
-	if (showShadows) {
-		_screen->copyRectToSurface(_room->_pixelsShadows, 640, 0, 0, 640, 400);
-	}
 	_smallFont->drawString(_screen, Common::String::format("Room number: %d", _room->_currentRoomNumber), 0, 4, 640, 13);
 	_smallFont->drawString(_screen, Common::String::format("Alfred pos: %d, %d (%d)", _alfredState.x, _alfredState.y, _alfredState.y - kAlfredFrameHeight), 0, 18, 640, 13);
 	_smallFont->drawString(_screen, Common::String::format("Frame number: %d", _chrono->getFrameCount()), 0, 30, 640, 13);
@@ -1488,9 +1467,9 @@ void PelrockEngine::extraScreenLoop() {
 			_events->_leftMouseClicked = false;
 			break;
 		}
-		g_system->delayMillis(10);
 		_screen->markAllDirty();
 		_screen->update();
+		g_system->delayMillis(10);
 	}
 
 	g_system->getPaletteManager()->setPalette(_room->_roomPalette, 0, 256);
@@ -2000,8 +1979,8 @@ void PelrockEngine::pyramidCollapse() {
 	// Background tile copies to have collapsed pyramid stick
 	// copy 99×45 from secondary buffer to front buffer
 	{
-		static const int srcX = 240, srcY = 145;
-		static const int copyW = 99, copyH = 45;
+		const int srcX = 240, srcY = 145;
+		const int copyW = 99, copyH = 45;
 		Common::Rect copyRect(srcX, srcY, srcX + copyW, srcY + copyH);
 		_currentBackground.blitFrom(_compositeBuffer, copyRect, Common::Point(srcX, srcY));
 	}
@@ -2048,10 +2027,10 @@ void PelrockEngine::pyramidCollapse() {
 		_events->pollEvent();
 		renderScene(OVERLAY_NONE);
 		_screen->update();
-		g_system->delayMillis(10);
 		npc = _room->findSpriteByIndex(0);
 		if (!npc || npc->x <= 307)
 			break;
+		g_system->delayMillis(10);
 	}
 
 	// Stop NPC movement
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 1fe5be938da..ea197b30d55 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -1103,7 +1103,7 @@ uint32 RoomManager::loadDescriptions(byte *pair12data, size_t pair12size, Common
 	uint32 pos = 0;
 	uint32 lastDescPos = 0;
 	outDescriptions.clear();
-	while (pos < (pair12size)) {
+	while (pos < pair12size) {
 		if (pair12data[pos] == 0xFF) {
 			Description description;
 
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 030acd28b46..b0c7bddfee5 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -47,7 +47,8 @@ static const int unpickableHotspotExtras[] = {
 	6,
 	7,
 	91, // mud and stone should only be picked under certain conditions!
-	92};
+	92
+};
 
 static const uint32 stickerOffsets[137] = {
 	0x000000, 0x00005B, 0x0000B6, 0x000298, 0x00047A, 0x0023C8, 0x004316, 0x004376,


Commit: a51d5b0afbcd0e0a3d6546e33c8e5c3f1d5c3155
    https://github.com/scummvm/scummvm/commit/a51d5b0afbcd0e0a3d6546e33c8e5c3f1d5c3155
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:54+02:00

Commit Message:
PELROCK: Added ALFRED.2 to the detection tables

Changed paths:
    engines/pelrock/detection_tables.h
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/detection_tables.h b/engines/pelrock/detection_tables.h
index 1e3944454b7..87467325e96 100644
--- a/engines/pelrock/detection_tables.h
+++ b/engines/pelrock/detection_tables.h
@@ -30,7 +30,11 @@ const ADGameDescription gameDescriptions[] = {
 	{
 		"pelrock",
 		nullptr,
-		AD_ENTRY1s("ALFRED.1", "ee0047cfcceece9c4f6a426245b6f449", 12915352),
+
+		AD_ENTRY2s(
+			"ALFRED.1", "ee0047cfcceece9c4f6a426245b6f449", 12915352,
+  			"ALFRED.2", "ca76c50da4a3af83c08d274a3c2ae101", 540260
+		),
 		Common::ES_ESP,
 		Common::kPlatformDOS,
 		ADGF_UNSTABLE,
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 593702c97b6..9507638a805 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -133,7 +133,6 @@ private:
 	Common::Point _curWalkTarget;
 	QueuedAction _queuedAction = {NO_ACTION, -1, false, false};
 
-	bool showShadows = false;
 	bool shouldPlayIntro = false;
 	bool gameInitialized = false;
 	bool screenReady = false;


Commit: e87dd28ce1ed62b479e6dcbc7a4771d8c6e6095c
    https://github.com/scummvm/scummvm/commit/e87dd28ce1ed62b479e6dcbc7a4771d8c6e6095c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:54+02:00

Commit Message:
PELROCK: Replace usage of char with byte where platform sensitive

Changed paths:
    engines/pelrock/video.cpp
    engines/pelrock/video.h


diff --git a/engines/pelrock/video.cpp b/engines/pelrock/video.cpp
index 7047045d89e..e506d936974 100644
--- a/engines/pelrock/video.cpp
+++ b/engines/pelrock/video.cpp
@@ -418,9 +418,9 @@ Subtitle VideoManager::readSubtitle(Common::File &metadataFile) {
 	// Read text until CRLF (0x0D 0x0A)
 	while (!metadataFile.eos()) {
 
-		char c = metadataFile.readByte();
+		byte c = metadataFile.readByte();
 		if (c == 0x0D) {
-			char next = metadataFile.readByte();
+			byte next = metadataFile.readByte();
 			if (next == 0x0A) {
 				break;
 			} else {
@@ -440,7 +440,7 @@ Subtitle VideoManager::readSubtitle(Common::File &metadataFile) {
 	return subtitle;
 }
 
-char VideoManager::decodeChar(byte c) {
+byte VideoManager::decodeChar(byte c) {
 
 	switch (c) {
 	case 0xAD:
diff --git a/engines/pelrock/video.h b/engines/pelrock/video.h
index 596509dfb37..826fadab3f3 100644
--- a/engines/pelrock/video.h
+++ b/engines/pelrock/video.h
@@ -106,7 +106,7 @@ private:
 	Subtitle readSubtitle(Common::File &metadataFile);
 	MusicEffect readMusicEffect(Common::File &metadataFile);
 	AudioEffect readAudioEffect(Common::File &metadataFile);
-	char decodeChar(byte c);
+	byte decodeChar(byte c);
 	Subtitle *getSubtitleForFrame(uint16 frameNumber);
 	uint _currentSubtitleIndex = 0;
 	Graphics::Surface _videoSurface = Graphics::Surface();


Commit: 963da1bbe5696d89bedb14e35316157418fbb0ca
    https://github.com/scummvm/scummvm/commit/963da1bbe5696d89bedb14e35316157418fbb0ca
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:54+02:00

Commit Message:
PELROCK: Remove statics from room header

Changed paths:
    engines/pelrock/resources.cpp
    engines/pelrock/room.cpp
    engines/pelrock/room.h


diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 13d14dcf73a..466de52b646 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -55,6 +55,26 @@ static const uint32 kCreditsSize = 2540;
 static const uint32 kComputerTextOffset = 0x0004901A;
 static const uint32 kComputerTextSize = 490;
 
+const uint32 stickerOffsets[137] = {
+	0x000000, 0x00005B, 0x0000B6, 0x000298, 0x00047A, 0x0023C8, 0x004316, 0x004376,
+	0x005119, 0x005EBC, 0x0083ED, 0x008529, 0x0092C4, 0x00A3AA, 0x00B490, 0x00B6A6,
+	0x00C05A, 0x00CA0E, 0x00D3D0, 0x00D46E, 0x00F036, 0x00FB8F, 0x00FC55, 0x0119D7,
+	0x013759, 0x01391F, 0x014A9D, 0x015C1B, 0x017601, 0x018FE7, 0x019048, 0x0190A9,
+	0x01910A, 0x0197F4, 0x019EDE, 0x01A7EC, 0x01B0FA, 0x01B8C4, 0x01C644, 0x01D83A,
+	0x01E104, 0x01E8C6, 0x01F45D, 0x01FBBB, 0x02011D, 0x02052F, 0x020A95, 0x020E5B,
+	0x0210B3, 0x0216E6, 0x021D5E, 0x0233A3, 0x0249E8, 0x025777, 0x026506, 0x028E2B,
+	0x02B82F, 0x02C9D7, 0x02E4CA, 0x02FFBD, 0x03234A, 0x0346D7, 0x036A83, 0x038E2F,
+	0x03B18D, 0x03D4EB, 0x03DEC9, 0x03F813, 0x04115D, 0x045303, 0x0494A9, 0x04955F,
+	0x049615, 0x0496CB, 0x0499E1, 0x049EC7, 0x04A023, 0x04A447, 0x04BA6D, 0x04BFA1,
+	0x04CE33, 0x04CF09, 0x04DB3B, 0x052885, 0x0575CF, 0x05775B, 0x057D79, 0x058397,
+	0x058969, 0x058F50, 0x05A9DB, 0x05C561, 0x05C72E, 0x05C8FB, 0x05EAC1, 0x060C87,
+	0x060D19, 0x060E62, 0x061039, 0x0613C2, 0x061764, 0x061847, 0x062535, 0x062D4B,
+	0x064F11, 0x0670D7, 0x067381, 0x0675A9, 0x0677EF, 0x067A98, 0x067DDE, 0x068115,
+	0x0684E3, 0x068A76, 0x068F30, 0x0693C8, 0x0696AD, 0x06C2C9, 0x06C84D, 0x07095D,
+	0x071854, 0x07274B, 0x073642, 0x074539, 0x075454, 0x0791DA, 0x07CF60, 0x07E4AB,
+	0x07ECED, 0x07F52F, 0x07FD71, 0x080591, 0x080B24, 0x080B84, 0x080F39, 0x0812F5,
+	0x0816B1
+};
 
 ResourceManager::ResourceManager(/* args */) {
 	_inventoryIcons = new InventoryObject[69];
diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index ea197b30d55..7edb96a6742 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -23,9 +23,31 @@
 #include "pelrock/pelrock.h"
 #include "pelrock/room.h"
 #include "pelrock/util.h"
+#include "room.h"
 
 namespace Pelrock {
 
+const int kTalkingAnimHeaderSize = 55;
+const int kNumSfxPerRoom = 9;
+const int unpickableHotspotExtras[] = {
+	308, // lamppost cable
+	65,  // objects in shop
+	66,
+	67,
+	68,
+	69,
+	70,
+	71,
+	72,
+	73,
+	74,
+	6,
+	7,
+	91, // mud and stone should only be picked under certain conditions!
+	92};
+
+const int kNumUnpickableHotspotExtras = ARRAYSIZE(unpickableHotspotExtras);
+
 static const uint32 kPaletteRemapOffset = 0x4C77C; // JUEGO.EXE — water-effect palette remap table
 
 RoomManager::RoomManager() {
@@ -47,21 +69,21 @@ RoomManager::~RoomManager() {
 	if (_passerByAnims) {
 		delete _passerByAnims;
 	}
-	if(_conversationData) {
+	if (_conversationData) {
 		delete[] _conversationData;
 	}
 }
 
 void RoomManager::clearTalkingAnims() {
 	for (int i = 0; i < _talkingAnims.numFramesAnimA; i++) {
-		if(_talkingAnims.animA[i]) {
+		if (_talkingAnims.animA[i]) {
 			delete[] _talkingAnims.animA[i];
 			_talkingAnims.animA[i] = nullptr;
 		}
 	}
 	delete[] _talkingAnims.animA;
 	for (int i = 0; i < _talkingAnims.numFramesAnimB; i++) {
-		if(_talkingAnims.animB[i]) {
+		if (_talkingAnims.animB[i]) {
 			delete[] _talkingAnims.animB[i];
 			_talkingAnims.animB[i] = nullptr;
 		}
@@ -383,7 +405,18 @@ void RoomManager::addWalkbox(WalkBox walkbox, int persist) {
 	}
 }
 
+bool RoomManager::isPickableByExtra(uint16 extra) {
+	if (extra > 112)
+		return false;
+	for (int i = 0; i < kNumUnpickableHotspotExtras; i++) {
+		if (extra == unpickableHotspotExtras[i])
+			return false;
+	}
+	return true;
+}
+
 Sprite *RoomManager::findSpriteByIndex(byte index) {
+
 	for (uint i = 0; i < _currentRoomAnims.size(); i++) {
 		if (_currentRoomAnims[i].index == index) {
 			return &_currentRoomAnims[i];
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index b0c7bddfee5..0946dca44a7 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -29,47 +29,7 @@
 
 namespace Pelrock {
 
-static const int kRoomStructSize = 104;
-static const int kTalkingAnimHeaderSize = 55;
-static const int kNumSfxPerRoom = 9;
-static const int unpickableHotspotExtras[] = {
-	308, // lamppost cable
-	65,  // objects in shop
-	66,
-	67,
-	68,
-	69,
-	70,
-	71,
-	72,
-	73,
-	74,
-	6,
-	7,
-	91, // mud and stone should only be picked under certain conditions!
-	92
-};
-
-static const uint32 stickerOffsets[137] = {
-	0x000000, 0x00005B, 0x0000B6, 0x000298, 0x00047A, 0x0023C8, 0x004316, 0x004376,
-	0x005119, 0x005EBC, 0x0083ED, 0x008529, 0x0092C4, 0x00A3AA, 0x00B490, 0x00B6A6,
-	0x00C05A, 0x00CA0E, 0x00D3D0, 0x00D46E, 0x00F036, 0x00FB8F, 0x00FC55, 0x0119D7,
-	0x013759, 0x01391F, 0x014A9D, 0x015C1B, 0x017601, 0x018FE7, 0x019048, 0x0190A9,
-	0x01910A, 0x0197F4, 0x019EDE, 0x01A7EC, 0x01B0FA, 0x01B8C4, 0x01C644, 0x01D83A,
-	0x01E104, 0x01E8C6, 0x01F45D, 0x01FBBB, 0x02011D, 0x02052F, 0x020A95, 0x020E5B,
-	0x0210B3, 0x0216E6, 0x021D5E, 0x0233A3, 0x0249E8, 0x025777, 0x026506, 0x028E2B,
-	0x02B82F, 0x02C9D7, 0x02E4CA, 0x02FFBD, 0x03234A, 0x0346D7, 0x036A83, 0x038E2F,
-	0x03B18D, 0x03D4EB, 0x03DEC9, 0x03F813, 0x04115D, 0x045303, 0x0494A9, 0x04955F,
-	0x049615, 0x0496CB, 0x0499E1, 0x049EC7, 0x04A023, 0x04A447, 0x04BA6D, 0x04BFA1,
-	0x04CE33, 0x04CF09, 0x04DB3B, 0x052885, 0x0575CF, 0x05775B, 0x057D79, 0x058397,
-	0x058969, 0x058F50, 0x05A9DB, 0x05C561, 0x05C72E, 0x05C8FB, 0x05EAC1, 0x060C87,
-	0x060D19, 0x060E62, 0x061039, 0x0613C2, 0x061764, 0x061847, 0x062535, 0x062D4B,
-	0x064F11, 0x0670D7, 0x067381, 0x0675A9, 0x0677EF, 0x067A98, 0x067DDE, 0x068115,
-	0x0684E3, 0x068A76, 0x068F30, 0x0693C8, 0x0696AD, 0x06C2C9, 0x06C84D, 0x07095D,
-	0x071854, 0x07274B, 0x073642, 0x074539, 0x075454, 0x0791DA, 0x07CF60, 0x07E4AB,
-	0x07ECED, 0x07F52F, 0x07FD71, 0x080591, 0x080B24, 0x080B84, 0x080F39, 0x0812F5,
-	0x0816B1
-};
+const int kRoomStructSize = 104;
 
 #define PERSIST_TEMP 1
 #define PERSIST_PERM 2
@@ -147,16 +107,7 @@ public:
 	/**
 	 * Will apply the default "take item with given extra" handler if returns true
 	 */
-	bool isPickableByExtra(uint16 extra) {
-		if (extra > 112)
-			return false;
-		int size = sizeof(unpickableHotspotExtras) / sizeof(unpickableHotspotExtras[0]);
-		for (int i = 0; i < size; i++) {
-			if (extra == unpickableHotspotExtras[i])
-				return false;
-		}
-		return true;
-	}
+	bool isPickableByExtra(uint16 extra);
 
 	Sprite *findSpriteByIndex(byte index);
 	Sprite *findSpriteByExtra(int16 extra);


Commit: 828574c3f57c3499a32f6b153e7df2589542bf1f
    https://github.com/scummvm/scummvm/commit/828574c3f57c3499a32f6b153e7df2589542bf1f
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:54+02:00

Commit Message:
PELROCK: Create menu sfx utility functions

Changed paths:
    engines/pelrock/menu.cpp
    engines/pelrock/sound.cpp
    engines/pelrock/sound.h
    engines/pelrock/spellbook.cpp


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index e0a9d47aa9b..3e3fdfa3511 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -436,22 +436,22 @@ bool MenuManager::checkMainMenuMouse(int x, int y) {
 	MainMenuButton mainMenuButton = isMainMenuButtonUnder(x, y);
 	switch (mainMenuButton) {
 	case QUESTION_MARK_BUTTON:
-		_sound->playSound("56ZZZZZZ.SMP", 0);
+		_sound->playClonk();
 		_events->_leftMouseClicked = false;
 		showCredits();
 		break;
 	case INVENTORY_PREV_BUTTON:
-		_sound->playSound("56ZZZZZZ.SMP", 0);
+		_sound->playClonk();
 		if (_curInventoryPage > 0)
 			_curInventoryPage--;
 		break;
 	case INVENTORY_NEXT_BUTTON:
-		_sound->playSound("56ZZZZZZ.SMP", 0);
+		_sound->playClonk();
 		if ((_curInventoryPage + 1) * 4 < g_engine->_state->inventoryItems.size())
 			_curInventoryPage++;
 		break;
 	case SAVE_GAME_BUTTON:
-		_sound->playSound("11ZZZZZZ.SMP", 0);
+		_sound->playClick();
 		if (ConfMan.getBool("original_menus") == true) {
 			_saveGamePage = 0;
 			_editingSaveSlot = -1;
@@ -463,7 +463,7 @@ bool MenuManager::checkMainMenuMouse(int x, int y) {
 		}
 		break;
 	case LOAD_GAME_BUTTON:
-		_sound->playSound("11ZZZZZZ.SMP", 0);
+		_sound->playClick();
 		if (ConfMan.getBool("original_menus") == true) {
 			_saveGamePage = 0;
 			refreshSaveDescriptions();
@@ -474,11 +474,11 @@ bool MenuManager::checkMainMenuMouse(int x, int y) {
 		}
 		break;
 	case EXIT_MENU_BUTTON:
-		_sound->playSound("11ZZZZZZ.SMP", 0);
+		_sound->playClick();
 		_menuState = EXIT_GAME;
 		break;
 	case SOUNDS_BUTTON:
-		_sound->playSound("11ZZZZZZ.SMP", 0);
+		_sound->playClick();
 		_menuState = SOUND;
 		_menuText = Common::StringArray();
 		break;
diff --git a/engines/pelrock/sound.cpp b/engines/pelrock/sound.cpp
index 2c4e31d7a4d..4eda6b9c65e 100644
--- a/engines/pelrock/sound.cpp
+++ b/engines/pelrock/sound.cpp
@@ -35,6 +35,7 @@
 
 #include "pelrock/pelrock.h"
 #include "pelrock/sound.h"
+#include "sound.h"
 
 namespace Pelrock {
 
@@ -219,7 +220,6 @@ int SoundManager::playSound(SonidoFile sound, int channel, int loopCount) {
 		} else {
 			if (_mixer->isSoundHandleActive(_sfxHandles[channel])) {
 				_mixer->stopHandle(_sfxHandles[channel]);
-				debug("Stopped active sound on channel %d to play new sound %s", channel, sound.filename.c_str());
 			}
 		}
 		Audio::AudioStream *finalStream = loopCount != -1 ? stream : Audio::makeLoopingAudioStream(stream, 0);
@@ -434,4 +434,12 @@ int SoundManager::tickAmbientSound(uint32 frameCount) {
 	return ambientSlotOffset;
 }
 
+void SoundManager::playClick() {
+	playSound("11ZZZZZZ.SMP", 0);
+}
+
+void SoundManager::playClonk() {
+	playSound("56ZZZZZZ.SMP", 0);
+}
+
 } // End of namespace Pelrock
diff --git a/engines/pelrock/sound.h b/engines/pelrock/sound.h
index 0219efb7a2c..7107b163831 100644
--- a/engines/pelrock/sound.h
+++ b/engines/pelrock/sound.h
@@ -88,6 +88,9 @@ public:
 	 */
 	int tickAmbientSound(uint32 frameCount);
 
+	void playClick();
+	void playClonk();
+
 	bool isPaused() const { return _isPaused; }
 	byte getCurrentMusicTrack() const { return _currentMusicTrack; }
 
diff --git a/engines/pelrock/spellbook.cpp b/engines/pelrock/spellbook.cpp
index 83d023d31a5..b8f3cdf4b8a 100644
--- a/engines/pelrock/spellbook.cpp
+++ b/engines/pelrock/spellbook.cpp
@@ -72,7 +72,6 @@ void SpellBook::init() {
 }
 
 void SpellBook::selectPage(int page) {
-	debug("Selected spell page: %d", page);
 	_spell = new Spell();
 	_spell->page = page;
 	Common::File alfred7;


Commit: b3bfc1776e342f2d200031554c6a4cb5c060184c
    https://github.com/scummvm/scummvm/commit/b3bfc1776e342f2d200031554c6a4cb5c060184c
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:55+02:00

Commit Message:
PELROCK: Refactor Room metadata loading so it doesnt return by value

Changed paths:
    engines/pelrock/room.cpp
    engines/pelrock/room.h


diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 7edb96a6742..7283cd3ebe2 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -520,8 +520,8 @@ PaletteAnim *RoomManager::getPaletteAnimForRoom(int roomNumber) {
 	return anim;
 }
 
-Common::Array<Exit> RoomManager::loadExits(byte *data, size_t size) {
-	Common::Array<Exit> exits;
+void RoomManager::loadExits(byte *data, size_t size) {
+	_currentRoomExits.clear();
 	int exitCountOffset = 0x1BE;
 	byte exitCount = data[exitCountOffset];
 	int exitDataOffset = 0x1BF;
@@ -567,17 +567,16 @@ Common::Array<Exit> RoomManager::loadExits(byte *data, size_t size) {
 				}
 			}
 		}
-		exits.push_back(exit);
+		_currentRoomExits.push_back(exit);
 	}
-	return exits;
 }
 
-Common::Array<HotSpot> RoomManager::loadHotspots(byte *data, size_t size) {
+void RoomManager::loadHotspots(byte *data, size_t size) {
+	_staticHotspots.clear();
 	int pair10StartingPos = 0x47a;
 
 	byte hotspot_count = data[pair10StartingPos];
 	int hotspotsDataStart = pair10StartingPos + 2;
-	Common::Array<HotSpot> hotspots;
 	for (int i = 0; i < hotspot_count; i++) {
 		int hotspotOffset = hotspotsDataStart + i * 9;
 		HotSpot spot;
@@ -588,7 +587,7 @@ Common::Array<HotSpot> RoomManager::loadHotspots(byte *data, size_t size) {
 			// if the hotspot has been changed, load the changed version
 			for (uint j = 0; j < g_engine->_state->roomHotSpotChanges[_currentRoomNumber].size(); j++) {
 				if (g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspotIndex == spot.innerIndex) {
-					hotspots.push_back(g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot);
+					_staticHotspots.push_back(g_engine->_state->roomHotSpotChanges[_currentRoomNumber][j].hotspot);
 					isChanged = true;
 					break;
 				}
@@ -603,10 +602,8 @@ Common::Array<HotSpot> RoomManager::loadHotspots(byte *data, size_t size) {
 		spot.h = data[hotspotOffset + 6];
 		spot.isSprite = false;
 		spot.extra = READ_LE_INT16(data + hotspotOffset + 7);
-		hotspots.push_back(spot);
+		_staticHotspots.push_back(spot);
 	}
-
-	return hotspots;
 }
 
 void RoomManager::resetConversationStates(byte roomNumber, byte *conversationData, size_t conversationDataSize) {
@@ -670,18 +667,17 @@ void RoomManager::loadRoomMetadata(Common::File *roomFile, int roomNumber) {
 	// The user's game can be in any state so we reset to defaults first
 	resetMetadataDefaults(roomNumber, pair10, pair10size);
 
-	Common::Array<Sprite> sprites = loadRoomAnimations(pic, pixelDataSize, pair10, pair10size);
-	Common::Array<HotSpot> staticHotspots = loadHotspots(pair10, pair10size);
+	// clear anims from previous room before loading new ones into _currentRoomAnims
+	clearAnims();
 
-	free(pic);
+	loadRoomAnimations(pic, pixelDataSize, pair10, pair10size);
+	loadHotspots(pair10, pair10size);
 
-	// clear anims from previous room
-	clearAnims();
+	free(pic);
 
-	_currentRoomAnims = sprites;
-	_currentRoomHotspots = unifyHotspots(sprites, staticHotspots);
-	_currentRoomExits = loadExits(pair10, pair10size);
-	_currentRoomWalkboxes = loadWalkboxes(pair10, pair10size);
+	_currentRoomHotspots = unifyHotspots(_currentRoomAnims, _staticHotspots);
+	loadExits(pair10, pair10size);
+	loadWalkboxes(pair10, pair10size);
 	_scaleParams = loadScalingParams(pair10, pair10size);
 
 	clearRoomStickerPixels(); // free all sticker buffers first
@@ -994,9 +990,9 @@ void RoomManager::loadAnimationPixelData(Common::File *roomFile, int roomOffset,
 	delete[] pixelData;
 }
 
-Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pixelDataSize, byte *data, size_t size) {
+void RoomManager::loadRoomAnimations(byte *pixelData, size_t pixelDataSize, byte *data, size_t size) {
 
-	Common::Array<Sprite> anims = Common::Array<Sprite>();
+	_currentRoomAnims.clear();
 	uint32 spriteCountPos = 5;
 	byte spriteCount = data[spriteCountPos] - 2;
 	uint32 metadata_start = spriteCountPos + (44 * 2 + 5);
@@ -1071,19 +1067,18 @@ Common::Array<Sprite> RoomManager::loadRoomAnimations(byte *pixelData, size_t pi
 			sprite.animData[j] = anim;
 		}
 
-		anims.push_back(sprite);
+		_currentRoomAnims.push_back(sprite);
 	}
-	return anims;
 }
 
-Common::Array<WalkBox> RoomManager::loadWalkboxes(byte *data, size_t size) {
+void RoomManager::loadWalkboxes(byte *data, size_t size) {
 
+	_currentRoomWalkboxes.clear();
 	int walkboxCountOffset = 0x213;
 	byte walkboxCount = data[walkboxCountOffset];
 
 	// debug("Walkbox count: %d", walkbox_count);
 	uint32 walkboxOffset = 0x218;
-	Common::Array<WalkBox> walkboxes;
 	for (int i = 0; i < walkboxCount; i++) {
 		uint32 boxOffset = walkboxOffset + i * 9;
 		int16 x1 = READ_LE_INT16(data + boxOffset);
@@ -1098,7 +1093,7 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(byte *data, size_t size) {
 			// if the walkbox has been changed, load the changed version
 			for (uint j = 0; j < g_engine->_state->roomWalkBoxChanges[_currentRoomNumber].size(); j++) {
 				if (g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkboxIndex == i) {
-					walkboxes.push_back(g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkbox);
+					_currentRoomWalkboxes.push_back(g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkbox);
 					isChanged = true;
 					break;
 				}
@@ -1111,25 +1106,24 @@ Common::Array<WalkBox> RoomManager::loadWalkboxes(byte *data, size_t size) {
 		box.w = w;
 		box.h = h;
 		box.flags = flags;
-		walkboxes.push_back(box);
+		_currentRoomWalkboxes.push_back(box);
 	}
 
 	if (g_engine->_state->roomWalkBoxChanges.contains(_currentRoomNumber)) {
 		// Add any new walkboxes that were added
 		for (uint j = 0; j < g_engine->_state->roomWalkBoxChanges[_currentRoomNumber].size(); j++) {
 			bool found = false;
-			for (uint i = 0; i < walkboxes.size(); i++) {
-				if (g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkboxIndex == walkboxes[i].index) {
+			for (uint i = 0; i < _currentRoomWalkboxes.size(); i++) {
+				if (g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkboxIndex == _currentRoomWalkboxes[i].index) {
 					found = true;
 					break;
 				}
 			}
 			if (!found) {
-				walkboxes.push_back(g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkbox);
+				_currentRoomWalkboxes.push_back(g_engine->_state->roomWalkBoxChanges[_currentRoomNumber][j].walkbox);
 			}
 		}
 	}
-	return walkboxes;
 }
 
 uint32 RoomManager::loadDescriptions(byte *pair12data, size_t pair12size, Common::Array<Description> &outDescriptions) {
diff --git a/engines/pelrock/room.h b/engines/pelrock/room.h
index 0946dca44a7..93bfd8e24a2 100644
--- a/engines/pelrock/room.h
+++ b/engines/pelrock/room.h
@@ -141,11 +141,11 @@ public:
 private:
 	void init();
 	void loadAnimationPixelData(Common::File *roomFile, int roomOffset, byte *&buffer, size_t &outSize);
-	Common::Array<Sprite> loadRoomAnimations(byte *pixelData, size_t pixelDataSize, byte *data, size_t size);
-	Common::Array<HotSpot> loadHotspots(byte *data, size_t size);
-	Common::Array<Exit> loadExits(byte *data, size_t size);
+	void loadRoomAnimations(byte *pixelData, size_t pixelDataSize, byte *data, size_t size);
+	void loadHotspots(byte *data, size_t size);
+	void loadExits(byte *data, size_t size);
 	ScalingParams loadScalingParams(byte *data, size_t size);
-	Common::Array<WalkBox> loadWalkboxes(byte *data, size_t size);
+	void loadWalkboxes(byte *data, size_t size);
 	uint32 loadDescriptions(byte *pair12data, size_t pair12size, Common::Array<Description> &outDescriptions);
 	void loadConversationData(byte *pair12data, size_t pair12size, uint32 startPos, size_t &outConversationDataSize, byte *&outConversationData);
 	void resetConversationStates(byte roomNumber, byte *conversationData, size_t conversationDataSize);
@@ -158,6 +158,7 @@ private:
 	Common::Array<byte> loadRoomSfx(Common::File *roomFile, int roomOffset);
 
 	byte *_resetData = nullptr;
+	Common::Array<HotSpot> _staticHotspots;
 };
 
 } // End of namespace Pelrock


Commit: 853400116543bb49858828de18a88d4d47de6b63
    https://github.com/scummvm/scummvm/commit/853400116543bb49858828de18a88d4d47de6b63
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:55+02:00

Commit Message:
PELROCK: Fix warnings on overflowing chars

Changed paths:
    engines/pelrock/computer.cpp
    engines/pelrock/resources.cpp


diff --git a/engines/pelrock/computer.cpp b/engines/pelrock/computer.cpp
index 5e436df9915..3f4b407ffdd 100644
--- a/engines/pelrock/computer.cpp
+++ b/engines/pelrock/computer.cpp
@@ -247,8 +247,8 @@ void Computer::drawScreen() {
 		Common::String navOptions;
 		Common::String actions = _computerText[7][0];
 		if (!book.available) {
-			actions.setChar(180, 1);
-			actions.setChar(180, 4);
+			actions.setChar((char)180, 1);
+			actions.setChar((char)180, 4);
 		}
 		g_engine->_graphics->drawColoredText(g_engine->_screen, actions, 174, 258, 325, defaultColor, g_engine->_smallFont);
 
diff --git a/engines/pelrock/resources.cpp b/engines/pelrock/resources.cpp
index 466de52b646..3e74157745e 100644
--- a/engines/pelrock/resources.cpp
+++ b/engines/pelrock/resources.cpp
@@ -413,7 +413,7 @@ void ResourceManager::loadHardcodedText() {
 	_ingameTexts = processTextData(descBuffer, kAlfredResponsesSize);
 	// exe.seek(-1, SEEK_CUR);
 	_left = exe.readString();
-	_right = exe.readString(0xFD);
+	_right = exe.readString((char)0xFD);
 	byte *terminatorBuffer = new byte[39];
 	exe.seek(kConversationTerminatorOffset, SEEK_SET);
 	exe.read(terminatorBuffer, 39);


Commit: d44fc4aaa5df0f894bb53249b4b0e2ebc15337a1
    https://github.com/scummvm/scummvm/commit/d44fc4aaa5df0f894bb53249b4b0e2ebc15337a1
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:55+02:00

Commit Message:
PELROCK: Remove duplicate logic into resetPasserByAnim

Changed paths:
    engines/pelrock/pelrock.cpp
    engines/pelrock/pelrock.h


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 6e19f5b899b..9139fc5cdb7 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -481,6 +481,15 @@ void PelrockEngine::maybeShakeEffect() {
 	_alfredState.x += (_shakeEffectState.shakeX / 2);
 }
 
+void PelrockEngine::resetPasserByAnim(int startX, int startY, Sprite *sprite) {
+	sprite->x = startX;
+	sprite->y = startY;
+	sprite->zOrder = 255;
+	sprite->curAnimIndex = 0;
+	sprite->animData[0].curFrame = 0;
+	_room->_passerByAnims->latch = false;
+}
+
 void PelrockEngine::maybeUpdatePasserByAnim(uint32 frameCount) {
 	if (_room->_passerByAnims == nullptr) {
 		return;
@@ -509,31 +518,15 @@ void PelrockEngine::maybeUpdatePasserByAnim(uint32 frameCount) {
 		Sprite *sprite = _room->findSpriteByIndex(spriteIndex);
 		if (direction == kPasserbyRight) {
 			if (sprite->x >= anim.resetCoord) {
-				sprite->x = startX;
-				sprite->y = startY;
-				sprite->zOrder = 255;
-				sprite->curAnimIndex = 0;
-				sprite->animData[0].curFrame = 0;
-				_room->_passerByAnims->latch = false;
+				resetPasserByAnim(startX, startY, sprite);
 			}
 		} else if (direction == kPasserbyLeft) {
-
 			if (sprite->x <= anim.resetCoord) {
-				sprite->x = startX;
-				sprite->y = startY;
-				sprite->zOrder = 255;
-				sprite->curAnimIndex = 0;
-				sprite->animData[0].curFrame = 0;
-				_room->_passerByAnims->latch = false;
+				resetPasserByAnim(startX, startY, sprite);
 			}
 		} else if (direction == kPasserbyDown) {
 			if (sprite->y >= anim.resetCoord) {
-				sprite->x = startX;
-				sprite->y = startY;
-				sprite->zOrder = 255;
-				sprite->curAnimIndex = 0;
-				sprite->animData[0].curFrame = 0;
-				_room->_passerByAnims->latch = false;
+				resetPasserByAnim(startX, startY, sprite);
 			}
 		}
 	}
diff --git a/engines/pelrock/pelrock.h b/engines/pelrock/pelrock.h
index 9507638a805..70859e4d027 100644
--- a/engines/pelrock/pelrock.h
+++ b/engines/pelrock/pelrock.h
@@ -257,6 +257,7 @@ public:
 	void maybeHaveDogPee();
 	void maybePlayPostIntro();
 	void maybeShakeEffect();
+	void resetPasserByAnim(int startX, int startY, Sprite *sprite);
 	void handleFightRoomFrame();
 	void paintDebugLayer();
 


Commit: e71a30c89587ab2b9796dd84846d0b4f71dcab7b
    https://github.com/scummvm/scummvm/commit/e71a30c89587ab2b9796dd84846d0b4f71dcab7b
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:56+02:00

Commit Message:
PELROCK: Create constructor for PasserBy animations

Changed paths:
    engines/pelrock/room.cpp
    engines/pelrock/types.h


diff --git a/engines/pelrock/room.cpp b/engines/pelrock/room.cpp
index 7283cd3ebe2..ac5a5a8f4a3 100644
--- a/engines/pelrock/room.cpp
+++ b/engines/pelrock/room.cpp
@@ -771,13 +771,7 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		mouse->animData[3].movementFlags = 0x3E0;
 
 		anims = new RoomPasserBys(roomNumber, 1);
-		PasserByAnim anim;
-		anim.spriteIndex = 2;
-		anim.startX = mouse->x;
-		anim.startY = mouse->y;
-		anim.dir = kPasserbyDown;
-		anim.targetZIndex = blank->zOrder + 1;
-		anim.resetCoord = blank->y;
+		PasserByAnim anim(2, mouse->x, mouse->y, kPasserbyDown, blank->y, blank->zOrder + 1, 0x3FF);
 		anims->passerByAnims[0] = anim;
 		break;
 	}
@@ -821,22 +815,9 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		Sprite *carRight = findSpriteByIndex(3);
 
 		anims = new RoomPasserBys(roomNumber, 2);
-		PasserByAnim animA;
-		animA.spriteIndex = 2;
-		animA.startX = carLeft->x;
-		animA.startY = carLeft->y;
-		animA.dir = kPasserbyLeft;
-		animA.resetCoord = carRight->x + carRight->w - carLeft->w;
-		animA.targetZIndex = 100;
-
+		PasserByAnim animA(2, carLeft->x, carLeft->y, kPasserbyLeft, carRight->x + carRight->w - carLeft->w, 100, 0x3FF);
 		anims->passerByAnims[0] = animA;
-		PasserByAnim animB;
-		animB.spriteIndex = 3;
-		animB.startX = carRight->x;
-		animB.startY = carRight->y;
-		animB.dir = kPasserbyRight;
-		animB.targetZIndex = 100;
-		animB.resetCoord = 639 + carRight->w;
+		PasserByAnim animB(3, carRight->x, carRight->y, kPasserbyRight, 639 + carRight->w, 100, 0x3FF);
 		anims->passerByAnims[1] = animB;
 		break;
 	}
@@ -845,13 +826,7 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		anims = new RoomPasserBys(roomNumber, 1);
 		Sprite *walker = findSpriteByIndex(2);
 		Sprite *dark = findSpriteByIndex(5);
-		PasserByAnim anim;
-		anim.spriteIndex = 2;
-		anim.startX = walker->x;
-		anim.startY = walker->y;
-		anim.dir = kPasserbyRight;
-		anim.resetCoord = dark->x;
-		anim.targetZIndex = dark->zOrder + 1;
+		PasserByAnim anim(2, walker->x, walker->y, kPasserbyRight, dark->x, dark->zOrder + 1, 0x3FF);
 		anims->passerByAnims[0] = anim;
 		break;
 	}
@@ -860,22 +835,9 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		Sprite *catLeft = findSpriteByIndex(3);
 		Sprite *blank = findSpriteByIndex(0);
 		anims = new RoomPasserBys(roomNumber, 2);
-		PasserByAnim animA;
-		animA.spriteIndex = 2;
-		animA.startX = catRight->x;
-		animA.startY = catRight->y;
-		animA.dir = kPasserbyRight;
-		animA.resetCoord = catLeft->x;
-		animA.targetZIndex = blank->zOrder + 1;
-
+		PasserByAnim animA(2, catRight->x, catRight->y, kPasserbyRight, catLeft->x, blank->zOrder + 1, 0x3FF);
 		anims->passerByAnims[0] = animA;
-		PasserByAnim animB;
-		animB.spriteIndex = 3;
-		animB.startX = catLeft->x;
-		animB.startY = catLeft->y;
-		animB.dir = kPasserbyLeft;
-		animB.resetCoord = blank->x;
-		animB.targetZIndex = blank->zOrder + 1;
+		PasserByAnim animB(3, catLeft->x, catLeft->y, kPasserbyLeft, blank->x, blank->zOrder + 1, 0x3FF);
 		anims->passerByAnims[1] = animB;
 		break;
 	}
@@ -885,22 +847,9 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		Sprite *papers = findSpriteByIndex(1);
 
 		anims = new RoomPasserBys(roomNumber, 2);
-		PasserByAnim animA;
-		animA.spriteIndex = 3;
-		animA.startX = mouseRight->x;
-		animA.startY = mouseRight->y;
-		animA.dir = kPasserbyRight;
-		animA.resetCoord = mouseLeft->x;
-		animA.targetZIndex = papers->zOrder + 1;
+		PasserByAnim animA(3, mouseRight->x, mouseRight->y, kPasserbyRight, mouseLeft->x, papers->zOrder + 1, 0x3FF);
 		anims->passerByAnims[0] = animA;
-
-		PasserByAnim animB;
-		animB.spriteIndex = 4;
-		animB.startX = mouseLeft->x;
-		animB.startY = mouseLeft->y;
-		animB.dir = kPasserbyLeft;
-		animB.resetCoord = mouseRight->x;
-		animB.targetZIndex = papers->zOrder + 1;
+		PasserByAnim animB(4, mouseLeft->x, mouseLeft->y, kPasserbyLeft, mouseRight->x, papers->zOrder + 1, 0x3FF);
 		anims->passerByAnims[1] = animB;
 		break;
 	}
@@ -909,22 +858,9 @@ RoomPasserBys *RoomManager::loadPasserByAnims(int roomNumber) {
 		Sprite *mummyRight = findSpriteByIndex(3);
 
 		anims = new RoomPasserBys(roomNumber, 2);
-		PasserByAnim animA;
-		animA.spriteIndex = 2;
-		animA.startX = mummyLeft->x;
-		animA.startY = mummyLeft->y;
-		animA.dir = kPasserbyLeft;
-		animA.resetCoord = 0 - mummyLeft->w;
-		animA.targetZIndex = 1;
-
+		PasserByAnim animA(2, mummyLeft->x, mummyLeft->y, kPasserbyLeft, 0 - mummyLeft->w, 1, 0x3FF);
 		anims->passerByAnims[0] = animA;
-		PasserByAnim animB;
-		animB.spriteIndex = 3;
-		animB.startX = mummyRight->x;
-		animB.startY = mummyRight->y;
-		animB.dir = kPasserbyRight;
-		animB.targetZIndex = 1;
-		animB.resetCoord = 639 + mummyRight->w;
+		PasserByAnim animB(3, mummyRight->x, mummyRight->y, kPasserbyRight, 639 + mummyRight->w, 1, 0x3FF);
 		anims->passerByAnims[1] = animB;
 		break;
 	}
diff --git a/engines/pelrock/types.h b/engines/pelrock/types.h
index 8b9a9d8aa69..34b56a55dda 100644
--- a/engines/pelrock/types.h
+++ b/engines/pelrock/types.h
@@ -483,6 +483,10 @@ struct PasserByAnim {
 	byte dir;
 	byte spriteIndex;
 	byte targetZIndex;
+
+	PasserByAnim() = default;
+	PasserByAnim(byte spriteIndex_, int16 startX_, int16 startY_, byte dir_, int16 resetCoord_, byte targetZIndex_, uint32 frameTrigger_)
+	    : frameTrigger(frameTrigger_), startX(startX_), startY(startY_), resetCoord(resetCoord_), dir(dir_), spriteIndex(spriteIndex_), targetZIndex(targetZIndex_) {}
 };
 
 struct RoomPasserBys {


Commit: 5c953ada383b389b002728c30295d1d1d0a41e84
    https://github.com/scummvm/scummvm/commit/5c953ada383b389b002728c30295d1d1d0a41e84
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:56+02:00

Commit Message:
PELROCK: Creates macro for deleting button sprites in menu

Changed paths:
    engines/pelrock/menu.cpp


diff --git a/engines/pelrock/menu.cpp b/engines/pelrock/menu.cpp
index 3e3fdfa3511..1624f5fc58f 100644
--- a/engines/pelrock/menu.cpp
+++ b/engines/pelrock/menu.cpp
@@ -33,6 +33,10 @@
 
 namespace Pelrock {
 
+#define DELETE_SPRITE(sprite) \
+  delete[] sprite[0]; \
+  delete[] sprite[1];
+
 static const char *inventorySounds[113] = {
 
 	"HOJASZZZ.SMP", // 0
@@ -1009,21 +1013,17 @@ void MenuManager::drawSoundControls() {
 Pelrock::MenuManager::~MenuManager() {
 	_mainMenu.free();
 	_compositeBuffer.free();
-	delete[] _questionMark[0];
-	delete[] _questionMark[1];
-	delete[] _inventoryLeftArrow[0];
-	delete[] _inventoryLeftArrow[1];
-	delete[] _inventoryRightArrow[0];
-	delete[] _inventoryRightArrow[1];
-	// delete buttons
-	delete[] _saveButtons[0]; delete[] _saveButtons[1];
-	delete[] _loadButtons[0]; delete[] _loadButtons[1];
-	delete[] _soundsButtons[0]; delete[] _soundsButtons[1];
-	delete[] _exitToDosButtons[0]; delete[] _exitToDosButtons[1];
-	delete[] _savesUpArrows[0]; delete[] _savesUpArrows[1];
-	delete[] _savesDownArrows[0]; delete[] _savesDownArrows[1];
-	delete[] _soundControlArrowLeft[0]; delete[] _soundControlArrowLeft[1];
-	delete[] _soundControlArrowRight[0]; delete[] _soundControlArrowRight[1];
+	DELETE_SPRITE(_questionMark);
+	DELETE_SPRITE(_inventoryLeftArrow);
+	DELETE_SPRITE(_inventoryRightArrow);
+	DELETE_SPRITE(_saveButtons);
+	DELETE_SPRITE(_loadButtons);
+	DELETE_SPRITE(_soundsButtons);
+	DELETE_SPRITE(_exitToDosButtons);
+	DELETE_SPRITE(_savesUpArrows);
+	DELETE_SPRITE(_savesDownArrows);
+	DELETE_SPRITE(_soundControlArrowLeft);
+	DELETE_SPRITE(_soundControlArrowRight);
 
 	delete[] _soundControlMasterIcon;
 	delete[] _soundControlSfxIcon;


Commit: fa6b927ca889b216bbf9b4636e46adcc5417be23
    https://github.com/scummvm/scummvm/commit/fa6b927ca889b216bbf9b4636e46adcc5417be23
Author: kelmer (kelmer at gmail.com)
Date: 2026-05-05T13:00:56+02:00

Commit Message:
PELROCK: Fixes talking animation occasionally not advancing frames on original timing

Changed paths:
    engines/pelrock/pelrock.cpp


diff --git a/engines/pelrock/pelrock.cpp b/engines/pelrock/pelrock.cpp
index 9139fc5cdb7..ba52a4b303a 100644
--- a/engines/pelrock/pelrock.cpp
+++ b/engines/pelrock/pelrock.cpp
@@ -852,8 +852,15 @@ void PelrockEngine::chooseAlfredStateAndDraw() {
 	}
 	case ALFRED_TALKING:
 		drawAlfred(_res->alfredTalkFrames[_alfredState.direction][_alfredState.curFrame]);
-		if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0)
+		// In alternate timing, shouldSkipFrame() never throttles, so use the frameCount gate.
+		// In original timing, shouldSkipFrame() already skips every other tick while talking,
+		// so advancing every rendered frame gives the same rate without parity phase-locking.
+		if (isAlternateTiming()) {
+			if (_chrono->getFrameCount() % kAlfredAnimationSpeed == 0)
+				_alfredState.curFrame++;
+		} else {
 			_alfredState.curFrame++;
+		}
 		if (_alfredState.curFrame >= talkingAnimLengths[_alfredState.direction] - 1) {
 			_alfredState.curFrame = 0;
 		}




More information about the Scummvm-git-logs mailing list