We just reached 30,000 articles on this wiki! 🥳
If you appreciate the work done within the wiki, please consider supporting The Cutting Room Floor on Patreon. Thanks for all your support!

Bugs:Psychonauts

From The Cutting Room Floor
Jump to navigation Jump to search

This page details bugs of Psychonauts.

Misattributed Lines

Various lines of dialogue in the game shown when interacting with certain characters (such as Boyd after he becomes the milkman, or Dr. Loboto near the end of the game) have dialogue intended to be spoken by Raz that is mistakenly spoken by the character interacted with instead.

For interactions with characters, the game specifies a list of linecodes that the character will say when being interacted. Linecodes can be either direct strings that are just the line ID, or they can be tables that can contain the line ID alongside extra properties. In table form, linecodes can specify an "owner override", which will allow a different character to speak for certain lines instead (for example, Raz).

However, the game's code for processing these linecodes mistakenly goes one level too deep in the tables, meaning that for table-based linecodes the game will actually process each entry of the table (such as the entry for the line ID and the owner override) as if each entry was a linecode itself. Since the game can also accept linecodes as just a line ID string without requiring them to be in a table, it correctly processes the line IDs and skips past data it doesn't recognise such as the owner override. The issue can be easily fixed by simply removing one of the for loops in the code.

Fred's Pieces

Fred Bonaparte and Raz are intended to have dialogue for interacting with his game "pieces" in the real world - the teddy bear, the gurney wheel and the gnome.

However, due to not one, but two oversights in his Lua script, these will never play in-game.

This is the function that is intended to play these conversations:

function Ob:onFredPropActivate( piece )
	if( 1 == self:isInsane() ) then
		local filterTable = {	gnome = 'ActivateGnome',
					gurneyWheel = 'ActivateGurneyWheel',
					teddybear = 'ActivateTeddybear' }
		if( nil ~= filterTable[piece] ) then
			self.crh:chatterStart(1, 0, 0, filterTable[piece] )
		else
			HardBreak()
		end
	end
end

Characters in Psychonauts have dialogue tables used for chatter, dialogue that can be configured to play in specific ways. Chatter lines can have "filter codes" meaning you can have certain chatter lines only play when you specifically request them via their filter code. Here, the filterTable maps each prop by name to a corresponding filter code for Fred's chatter table.

The first bug is in the naming of the filter codes. This table makes the gnome look for ActivateGnome and so on, but the actual lines in Fred's table all have "Insane" appended on the end (i.e ActivateGnomeInsane). Most of his lines use "Insane" and "Cured" to distinguish between lines to play before and after finishing his level but these lines have no cured variant, so the "Insane" postfix is unnecessary.

If you fix this bug, then the gnome and teddy bear's dialogues will play correctly, but the gurney wheel's lines still won't work. This is where the second oversight comes in.

Code elsewhere passes the name of the piece interacted with to this function, which then uses the piece's entity name to look up which filter code to use (by directly indexing the filterTable using the prop's name). The problem though, is that this is case sensitive. The gurney wheel's entity name is gurneywheel, but the entry in the filter table is gurneyWheel, meaning the lookup fails. This oversight is actually especially egregious, because the nil ~= filterTable[piece] check would cause the game to hard break in a debugging environment if it were to fail, meaning this bug should have been immediately caught during testing had they tested interacting with the pieces (and, likely, the other oversight would've been caught along with it).

Because of these two oversights, three short conversations with Fred go inadvertently unused.