Opened 5 years ago

Closed 5 years ago

#10751 closed defect (fixed)

QFG4: Rope obscured by bkg when rescuing Igor

Reported by: Vhati Owned by: bluegr
Priority: low Component: Engine: SCI
Version: Keywords: SCI32 original has-pull-request
Cc: Game: Quest for Glory 4

Description

ScummVM 2.1.0git3770-g15306581ab (Oct 18 2018 04:27:32)
Windows 7 64bit
QFG4 CD (English)

When Igor is trapped in the graveyard...

After climbing a tree and using the grapnel, the rope is briefly obscured by the facade of a background tomb, during a sequence where the hero climbs down and pulls.

File - 5kb MD5 - Full MD5
RESOURCE.000 - 263dce4aa34c49d3ad29bec889007b1c - 1364ba69e3c0abb68cc0170650a56692
RESOURCE.AUD - c39521bffb1d8b19a57394866184a0ca - 71098b9e97e20c8941c0e4812d5f906f
RESOURCE.MAP - aba367f2102e81782d961b14fbe3d630 - 801a04cc6aa5d437681a2dd0b6545248
RESOURCE.SFX - 3cf95e09dab8b11d675e0537e18b499a - 7c858d7253f86dab4cc6066013c5ecec

Attachments (3)

sci.021 (70.3 KB ) - added by Vhati 5 years ago.
SavedGame - Grapnel
Grapnel.png (131.2 KB ) - added by Vhati 5 years ago.
Screenshot - Grapnel
Pic Priority (CD) - Room 500.png (4.2 KB ) - added by Vhati 5 years ago.
Pic Priority (CD) - Room 500

Download all attachments as: .zip

Change History (16)

by Vhati, 5 years ago

Attachment: sci.021 added

SavedGame - Grapnel

by Vhati, 5 years ago

Attachment: Grapnel.png added

Screenshot - Grapnel

comment:1 by digitall, 5 years ago

@Vhati: Thanks for the bug report. Could you try testing this with the original SCI interpreter to see if this occurs in the original as well?

comment:2 by Vhati, 5 years ago

The original interpreter also does this.

comment:3 by digitall, 5 years ago

Keywords: original added

comment:4 by Vhati, 5 years ago

The floppy edition under ScummVM did not have this bug.


ScummVM 2.1.0git3797-ge7d23d2cd9 (Oct 25 2018 04:17:12)
Windows 7 64bit
QFG4 Floppy 1.1a + note patch (English)

File - 5kb MD5 - Full MD5
RESOURCE.000 - f64fd6aa3977939a86ff30783dd677e1 - ff42260a665995a85aeb277ad80aac8a
RESOURCE.MAP - d10a4cc177d2091d744e2ad8c049b0ae - 3695b1b0a1d15f3d324ea9f0cc325245
RESOURCE.SFX - 3cf95e09dab8b11d675e0537e18b499a - 7c858d7253f86dab4cc6066013c5ecec

by Vhati, 5 years ago

Pic Priority (CD) - Room 500

comment:5 by Vhati, 5 years ago

Quick and dirty way to skip to Igor's rescue.

  • Create a rogue.
  • Set flag 37 (Heard "Igor is missing!" outside the Burgomeister's office).
    • vv g 502 1024
  • Give yourself the rope.
    • send hero get 16
  • Teleport to the graveyard.
    • room 500
  • Click the rope & grappling hook on the tree branch.
Last edited 5 years ago by Vhati (previous) (diff)

comment:6 by Vhati, 5 years ago

SCI Companion says the background Pic has priority regions for the crypt pillars (priority 106).

With some experimentation I learned adjusting priority doesn't update the graphics. Gotta hide/show as well. Batches of commands can be done together from the debugger before dismissing it.

send rope2 setPri 1000
send rope2 hide
send rope2 show

Giving a rope priority 107 was exactly enough to move in front of the pillars.


Breakpoints confirm bigBranch::doVerb(grapnel) is assigning priority (rope1:148, rope2:116).

Start a fresh rescue... climb the tree, click the grapnel on the branch... Sending hide/show is enough to bring both ropes to the front.

When hero comes down, pulling is animated, and the rope comes to the front on its own.

Last edited 5 years ago by Vhati (previous) (diff)

comment:7 by Vhati, 5 years ago

Diffing script 500, CD vs floppy, reveals some changes.
Nothing rope-related stands out though.

The speed slider doesn't help. CD always has a problem; floppy never does.


rope1 and rope2 are Actors, whose ancestry is Feature/View/Prop.


script 64998 - View::setPri() is the same.

(method (setPri param1)
	(cond 
		((== argc 0) (= fixPriority 1))
		((== param1 -1) (= fixPriority 0))
		(else (= priority param1) (= fixPriority 1))
	)
)

script 64998 - Actor::doit() changed, but they still have this.

(if (and (& -info- $0008) (self isNotHidden:))
	(UpdateScreenItem self)
)

script 64950 - View::isNotHidden()

(method (isNotHidden)
	(return (not (& signal $0008)))
)
Last edited 5 years ago by Vhati (previous) (diff)

comment:8 by Vhati, 5 years ago

bigBranch::doVerb(grapnel) is calling init() and setPri().
That's the moment it becomes visible, albeit obscured.


script 500 - bigBranch::doVerb(grapnel)

(rope1 init: setPri: 148)
(rope2 init: setPri: 116)
(global2 setScript: sClimbDown)

rope2 starts out with signal=24576 aka 0x6000.
If I do "send rope2 signal 24577", move the mouse to the icon bar, and dismiss the debugger... the rope remains obscured... until I move the mouse off the icon bar to unpause the game. Then it is immediately repainted, fully.


The script doesn't naturally signal like that until...

script 500 - sTryTree::changeState()

(7
	(rope2
		signal: (| (rope2 signal?) $0001)
		setCycle: End self
	)
)
# ...
(12
	(global0 view: 7 setLoop: 0 1 setCel: 0 setCycle: Fwd)
	(rope1
		signal: (| (rope1 signal?) $0001)
		setCycle: End
		setStep: 1 1
		setMotion: MoveTo (rope1 x?) (+ (rope1 y?) 16)
	)
	(rope2
		signal: (| (rope2 signal?) $0001)
		setStep: 1 1
		setMotion: MoveTo (+ (rope2 x?) 50) (- (rope1 y?) 50)
	)
	(igor show:)
	(headStone
		signal: (| (headStone signal?) $0001)
		setCycle: Beg self
	)
)

And those are the moments rope2 and rope1 get painted properly.


"send rope2 doit" didn't help.

"logkernel UpdateScreenItem"

# Floppy when the ropes first appear.
kUpdateScreenItem: 0044:3786 (rope1) = 1
kUpdateScreenItem: 0044:381c (rope2) = 1

"bp_method rope2::doit" tripped.
"vo rope2 priority" said 116 already at that moment, and UpdateScreenItem() ran.

# CD, nothing until after hero climbs down and pulls.
kUpdateScreenItem: 0044:3cf1 (rope2) = 1
kUpdateScreenItem: 0044:36d5 (moss) = 1
kUpdateScreenItem: 0044:3cf1 (rope2) = 1
kUpdateScreenItem: 0044:3cf1 (rope2) = 1
Last edited 5 years ago by Vhati (previous) (diff)

comment:9 by Vhati, 5 years ago

Actor::doit() changed, but they still have this.

Ah. It moved though.


Floppy script 64998 - Actor::doit()

(method (doit &tmp temp0 temp1 temp2 temp3 temp4 temp5 temp6 temp7)
	(if script (script doit:))
	(if code (code doit: self))

# This condition fails, but it doesn't matter.
#   UpdateScreenItem() is outside.
	(if (& signal notUpd)
		(if viewer (viewer doit: self))
		(if avoider (avoider doit:))
		(if mover
			(if
			(and (& scaleSignal $0001) (not (& scaleSignal $0004)))
				(= temp5 (>> origStep $0008))
				(= temp6 (& origStep $00ff))
				(= temp3
					(if (= temp7 (/ (* temp5 scaleX) 128)) else 1)
				)
				(= temp4
					(if (= temp7 (/ (* temp6 scaleY) 128)) else 1)
				)
				(if (or (!= temp3 xStep) (!= temp4 yStep))
					(self setStep: temp3 temp4 1)
				)
			)
			(if mover (mover doit:))
		)
		(if scaler (scaler doit:))
		(if cycler
			(= temp1 brLeft)
			(= temp2 brRight)
			(cycler doit:)
			(cond 
				((not (& signal $0020)))
				(baseSetter (baseSetter doit: self))
				(else (BaseSetter self))
			)
		)
	)
	(= xLast x)
	(= yLast y)
	(if (and (& -info- $0008) (self isNotHidden:))
		(UpdateScreenItem self)
	)
)



CD script 64998 - Actor::doit()

(method (doit &tmp [temp0 2] temp2 temp3 temp4)
	(if robot (robot doit:))
	(if script (script doit:))
	(if code (code doit: self))

# This condition fails as doit() is repeatedly called.
	(if (& signal notUpd)
		(if viewer (viewer doit: self))
		(if avoider (avoider doit:))
		(if mover (mover doit:))
		(if cycler (cycler doit:))
		(if (& -info- $0008)
			(if scaler (scaler doit:))
			(= xLast x)
			(= yLast y)
# So this doesn't happen.
			(if (self isNotHidden:) (UpdateScreenItem self))
			(if
				(and
					(& scaleSignal $0001)
					(not (& scaleSignal $0004))
					(!= scaleX oldScaleX)
				)
				(= oldScaleX scaleX)
				(= temp2
					(if
						(= temp4
							(>> (+ (* (>> origStep $0008) scaleX) 64) $0007)
						)
					else
						1
					)
				)
				(= temp3
					(if
						(= temp4
							(>> (+ (* (& origStep $00ff) scaleY) 64) $0007)
						)
					else
						1
					)
				)
				(if (or (!= temp2 xStep) (!= temp3 yStep))
					(self setStep: temp2 temp3 1)
				)
			)
			(cond 
				((not (& signal $0020)))
				(baseSetter (baseSetter doit: self))
				(else (BaseSetter self))
			)
		)
	)
)



Dunno what's up with the label "notUpd".
Disassembly says that's an AND between "signal" and a literal 1.


Which means this notifies the rope to call UpdateScreenItem().

(rope2 signal: (| (rope2 signal?) $0001))

That sets signal's 1 bit to satisfy the condition.

Last edited 5 years ago by Vhati (previous) (diff)

comment:10 by Vhati, 5 years ago

Maybe I could tweak the signal value the ropes are instantiated with?

script 500

(instance rope1 of Actor
	(properties
		x 100
		y -10
		view 502
		signal $6000
	)
)

(instance rope2 of Actor
	(properties
		x 127
		y -5
		view 502
		loop 1
		signal $6000
	)
)

That'd be tidy. Except I don't know how to disassemble/patch those blocks.


Hrm. I think I found them. In heap 500's raw bytes. 127 was a distinctive number.
(In SCI Companion, list the heaps, right-click heap 500 and "view raw data". Hex values coincide with an instance's properties list in SCI Companion's disassembly.)

Guess that means I can't patch there.

Last edited 5 years ago by Vhati (previous) (diff)

comment:11 by Vhati, 5 years ago

Correction, I CAN patch the heap, with no special effort.

Except the "signal" value in particular doesn't survive init().
Gotta set it elsewhere then...


EDIT: No wait, it works! Took a little special effort. My savegame was IN the graveyard. I needed to re-enter the room for my heap patch to work.

Last edited 5 years ago by Vhati (previous) (diff)

comment:12 by Vhati, 5 years ago

Keywords: has-pull-request added

Pull Request: SCI32: Fix QFG4 obscured ropes when rescuing Igor

comment:13 by bluegr, 5 years ago

Owner: set to bluegr
Resolution: fixed
Status: newclosed

Thanks for your work! The pull request has been merged, so this can be closed now

Note: See TracTickets for help on using tickets.