User File #1552249469711657

Upload All User Files

#1552249469711657 - inputreplayer-v0.3.0 for lsnes (manual)

inputreplayer-v0.3-manual.lua
System: Super Nintendo Entertainment System
1078 downloads
Uploaded 9/30/2012 9:18 PM by partyboy1a (see all 14)
Here is a description of all functions provided by inputreplayer
--[[
*******************************************************************************
  lsnes-input-replayer v0.3 manual
*******************************************************************************

0) Quickstart instructions
1) What is it good for?
2) How it works
3) global variables
4) global functions
5) inputreplayer API
5.1) inputreplayer.functions
5.2) inputreplayer.config
5.3) inputreplayer.state
6) inputreplayer_ui
7) limitations and known (possible) bugs




*******************************************************************************
0) Quickstart instructions (to get some impressions on how to use this thing)
*******************************************************************************

step 1: set lsnes_version inside inputreplayer-v0.3-config.lua to
	either "wxwidgets" or "SDL"
	
step 2: play some movie up to a point where you can control both characters
	and instantly see the reactions of them for different input.
	
(step 2a: be sure to have a backup of your movie!)

step 3: create a savestate and switch to read-write mode

step 4: start the script and press numpad_divide (-> hides menu)

step 5: play around a bit, then pause the game.

step 6: press numpad_divide, then numpad1 (-> you have selected "set mode")
	then numpad7 (-> you have selected "switch all to REPLAY")
	then numpad_divide (-> hides the menu)

step 7: load the savestate in read-write mode

step 8: magic, although you are in read-write mode, your previous input is
	used!

step 9: play around with "set offset", and see how it changes the behaviour.




*******************************************************************************
1) What is it good for?
*******************************************************************************

You're recording a TAS. You got some part of a level done, and now you want to
improve it. This tool will give you an easy way to verify if you really
improved, and where you might have lost some time.

You have recorded quite a long part, and now you notice a mistake near the
beginning. This tool will help you to reuse your progress afterwards, you
won't have to repeat all the input again.

You have a game with random lag. You have recorded quite a long part, and now
you notice a mistake near the beginning. You fix it, but the lag pattern
changes. This tool will help you to resync your movie without having to edit it
by hand.




*******************************************************************************
2) How it works
*******************************************************************************

You start lsnes-input-replayer, and start recording your process. You create
some savestates here and there. lsnes-input-replayer is in "recording" mode, 
and saves all your input, and also some important game data like your position.

When you load a savestate, lsnes-input-replayer will switch to "replay" mode.
(In this version, you have to do this by hand.) The recorded buttons will be
the "reference input". As long as you don't hold any gamepad buttons, it will
apply the previously recorded input for you. If you decide to use your gamepad,
it will become inactive until you explicitly switch back to "replay" mode.

If you decide your new try was clearly better than the old one, but you want to
improve even more, then you can overwrite the "reference input" with your new
attempt.

You can also define the offset to apply the input. A positive offset will let
you apply your input earlier than in the reference, a negative offset will
apply your input later than before.

Additionally, it is possible to remap the joypad input and the recorded input.
This is a useful feature if you're creating a multiplayer TAS. For example you
can swap controller 1 and controller 2 this way, and the same can be done with
your recorded input.




*******************************************************************************
3) global variables
*******************************************************************************

keyhook

	table which maps keys to functions. Used for on_keyhook.
	Don't modify this directly, use assignkey(key, func) instead.

index_to_player_for_on_snoop

	used to tell the script how to convert
	ncontroller,nport to a player number.
	0,0 is player 1
	1,0 is player 2
	TODO: I'm not sure about the other values.

player_to_index_for_setinput
	
	used to tell the script how to convert
	the playernumber to a number which can be used for input.set
	player 1 requires input.set(0, ..., ...)
	player 2 requires input.set(4, ..., ...)
	TODO: I'm not sure about the other values.

button_by_name
	
	if you want to know how you need to call input.set to press a
	specific button, this is for you. Example: input.set(0, button_by_name["Y"], 1)

button_by_index
	
	given an index from 0 to 11, it will tell you which button you
	would manipulate with it when used for input.set.
	Example:
		input.set(4, 5, 1)
		button_by_index[5] == "d"
		player_to_index_for_setinput[2] == 4
		------------------------------------
		you tell lsnes to hold "down" on controller 2

	This table is used to show how this script modified the input for you.

	Putting all these together, we can now understand
	on_snoop(nport, ncontroller, cindex, cvalue)

	Example: nport == 1
			 ncontroller == 0
			 cindex == 7
			 cvalue == 1
			 -----------
			 index_to_player_for_on_snoop[1][0] == 2
			 button_by_index[7] == "r"
			 -----------
			 lsnes will receive "right is pressed" for player two
			 
	And you can also use this to make your scripts quite readable
	(but lengthy), for example
		input.set(player_to_index_for_setinput[2], button_by_name["u"], 1)
	will tell the reader instantly that you want to press "up" for player 2.
		input.set(4, 4, 1)
	would do the same, but to read it, you would need to look up
	the information in two different places.
	
lsnes_version

	must be set manually inside the config file!
	Key names are different in wxwidgets and SDL, so setting this
	up in the right way is necessary!	

temp

	may be used for different purposes. Don't make any
	assumptions about its value.

player

	used several times for different loops. Don't
	make any assumptions about its value.



	
*******************************************************************************
4) global functions
*******************************************************************************

compressinput(inputtable)
	
	useful for storing all the recorded information to your harddrive,
	saves about 80% file size while still being human-readable.
	
	returns a string representing the input in a human-readable form.
	
	inputtable
	
		has all buttons in the way they are read from on_snoop, so it is a
		table of this kind:
			{[0] =  0  , [1] = 0  , ... [15] = 0   }
					or         or              or
					1          1               1
	
decompressinput(inputstring)
	
	if we compress the input, it must be decompressed as well.
	
	inputstring
	
		is a string that must be formatted exactly in the same way
		as compressinput would have returned it.
		inputstring must have at least 12 characters.
		if string.sub(inputstring, index - 1, index - 1) ~= "." then
		button button_by_index[index] shall be pressed.
		
	returns a table which can be easily used for input.set
	
assignkey(key, func)

	use these to add additional hotkeys.
	
	Example: You want to use numpad+ to increase the offset for player
	one by one. Then you would use
	
	assignkey(
		"numpad_plus", -- or "kp_plus", depending on lsnes_version!
		function()
			inputreplayer.functions.addoffset(1, 1)
		end
	)
	
	to delete the hotkey, use assignkey(key, nil).
	

	
	
*******************************************************************************
5) inputreplayer API
*******************************************************************************




*******************************************************************************
5.1) functions
*******************************************************************************

inputreplayer.functions.setmode(player, mode)
	
	sets the specified mode for the specified player.
	see above how these modes work.
	
	player
		
		1 to 8
		
	mode
		
		"RECORD", "REPLAY", "INACTIVE"

inputreplayer.functions.remaprecordedinput(player, remap)

	If there is recorded data available for this frame, the recorded input
	from the specified player for frame
	
		movie.currentframe() + inputreplayer.state.player[player].offset	
		
	will be offered to all players in the remap table. If the player in
	the remap table is in REPLAY mode at that time, and there is no other
	current joypad input for this player at the moment, this offer will
	be accepted.
	
	player
		
		1 to 8
		
	remap
		
		a table containing all players which shall receive the
		recorded input.
	
	examples:
	
		example one:
		
			inputreplayer.functions.remaprecordedinput(1, {2})
			inputreplayer.functions.remaprecordedinput(2, {1})
			
				this together will swap controller 1 and two.
		
		example two:
		
			inputreplayer.functions.remaprecordedinput(1, {1, 2})	
			
				this will send all input from controller 1 to
				controller 2, so this is like duplicating the
				input.
			
		example three:
			
			inputreplayer.functions.remaprecordedinput(1, {2})
		
				This will actually do the same as example two
				because controller one didn't get overwritten,
				and then it defaults to just using the usual
				input.
		
inputreplayer.functions.remapjoypadinput(player, remap)
	
	If you setup a joypad remap for player, it will overwrite the input
	from all players listed in the remap table with the input from the
	specified player. This is done before the current recording mode
	is taken into account.
	
	So if you're in RECORD mode, and you have used
		remapjoypadinput(1,{2})
		remapjoypadinput(2,{1})
	all input you do for player 1 will be
	used AND recorded for player 2 and vice versa.

	player
	
		see above
		
	remap
	
		see above
	
inputreplayer.functions.addoffset(player,frames)
	
	If you want to apply the recorded input sooner or later than it
	has been recorded, you can set an offset for this for each player.
	Offset is applied before remapping is taken into account.
	positive values will apply the recorded input sooner than it was recorded.
	negative values will apply the recorded input later  than it was recorded.

	player
	
		1 to 8
		
	frames
	
		number of frames to add to current offset
		
inputreplayer.functions.setoffset(player,frames)

	sets the offset for the specified player to the specified number of frames.
	see above for more details.
	
inputreplayer.functions.setnewreference()

	While in INACTIVE mode, this script will record your current attempt to
		inputreplayer.state.player[player].newinput[frame]
		
	If you want to use this attempt as new reference (because you have improved)
	then run this command, and your current attempt will become the new
	reference.

inputreplayer.functions.on_snoop(nport, ncontroller, cindex, cvalue)
	
	To be put into on_snoop callback.
	
	This function handles all the necessary joypad recording.

inputreplayer.functions.on_input()

	To be put into on_input callback.
	
	This function will modify all your input in the desired way.
	

	
	
*******************************************************************************
5.2) inputreplayer.config
*******************************************************************************
	
	see also config file
	
inputreplayer.config.playercount

	Tells the script how many players should be taken care of.
	
	TODO:
		menus don't take care of this,
		they assume playercount == 2.


		
		
*******************************************************************************
5.3) inputreplayer.state
*******************************************************************************

	holds all data used by inputreplayer.
	
inputreplayer.state.player[player]

	table holding all recorded data for the specified player.

inputreplayer.state.player[player].input

	table containing an inputstring (to be used with decompressinput)
	for each frame while the specified player was in RECORD mode.
	
inputreplayer.state.player[player].input[frame]
	
	This holds a string expressing which button the specified
	player has pressed in the specified frame. 
	
inputreplayer.state.player[player].offset

	Current offset for the specified player.
	See above how this offset is applied.
	
inputreplayer.state.player[player].remaprecordedinput

	Either nil (no remapping) or a table.
	
	If it is a table, it contains all players
	which shall receive the recorded input from the
	specified player. Offset is applied before
	this remapping is done.
		
inputreplayer.state.player[player].remapjoypadinput

	Either nil (no remapping) or a table.
	
	If it is a table, it contains all players
	which shall receive the current joypad input
	from the specified player, overwriting their
	own joypad input. The remapping is done
	before the input is actually recorded, so the
	remapping will also influence what is recorded.

inputreplayer.state.player[player].newinput

	structure: same as inputreplayer.state.player[player].input
	
	When the specified player is in INACTIVE mode, all
	input will still be recorded, but it is stored at this
	place.
	
	If you decide that your current attempt is clearly better
	than your old one, you can overwrite your old attempt
	by calling inputreplayer.functions.setnewreference()


	
	
*******************************************************************************
6) inputreplayer_ui
*******************************************************************************

	contains everything necessary to make use of all the powerful functions
	provided by inputreplayer.
	
	If you can create a better UI, this is your chance to replace the
	default user interface with your own one, just rewrite this part.

inputreplayer_ui.on_paint

	To be called from on_paint callback. Will draw all the necessary
	information on screen to use this script. will also draw the menu.
	
inputreplayer_ui.menu

	This includes the complete menu structure.
	
	inputreplayer_ui.menu is a table of numbered items. Number must
	be from 1 to 9 (otherwise they are inaccessible or won't appear).
	There must be no gap within these numbers. (If you specify item
	1 and 3, but not 2, item 3 won't be dispayed, but it will still
	react to whatever item 3 is if you press numpad3. You don't want
	such a silly behaviour.)
	
	Each item has the following structure:
	[itemnumber] == {
		string text,
		function action,
		submenu menu,
		boolean goback
	}
	
	text
		
		a string to display the intention of this item
	
	action
		
		a function to be called when this item is selected
		
		optional.
		
		If present, and goback ~= false, you will go back
		one step in the menu afterwards. Otherwise, the
		menu will stay the way it is.
		
		behavoir is undefined if
			action ~= nil and submenu ~= nil
		
	submenu
	
		a menu to be displayed when this item is to be
		selected.
		
		optional.
		
		behavoir is undefined if
			action ~= nil and submenu ~= nil
	
	goback
	
		by default, you will go one step back
		in the menu structure if an item has an
		action applied on it. If you set goback
		to false, this will not happen.
	
	
	
	Menu can be opened or closed with the menu key.
	
	Menu key is stored in inputreplayer_ui.config.keys.menu
	
	Menu key is also used to go back one step in the menu.
	
	You can navigate through the menu by using numbers from the
	numpad by default.

inputreplayer_ui.config

	everything about the UI which can be configured goes here.
	Especially, you can configure the keys for the menu navigation.



	
*******************************************************************************
7) limitations and known (possible) bugs
*******************************************************************************

- No support for multiple input on the same frame. Might never be changed, because
    those games will most probably have a lot of randomness.
	
- Interface assumes you are using exactly 2 players right now. Support for more
    than 2 players hasn't been tested.

- there is nothing implemented for watching the memory at the moment. Showing
    differences in memory between recorded version and current version will make
	this script really powerful, as it allows for a very easy way to see your
	improvements.

- The menu-driven interface forces a lot more button presses than necessary, but
    it is easy to add hotkeys for the most important functions. There is a really
	straight-forward API for this purpose.
	
- This script doesn't care about resets at all! I assume that this script isn't
	useful at all while you reset. Just activate the script afterwards, and
	everything will be fine.

- Right now it doesn't support saving the recorded data to your harddrive. Therefore,
    loading recorded data is also not implemented.

- If English is your first language, you can fix my mistakes... so:

- maybe find a better name for this script?
--]]