Lua script that allows you to remove input from individual players in BizHawk's TAStudio while leaving other players unaffected. Please read the comments at the top of the script, since there are some quirks to how it works.
--[[
This script allows you to remove input from one or more individual players in TAStudio while not affecting other players. Input from the following frames is shifted up to replace the old input. Currently hardcoded to support 1-4 players.
The script requires that you have at least one branch in the run.
Toggle the script while TAStudio is active to bring up a dialog box.
After the dialog box appears, load the branch that you want to edit. This is necessary in order to determine the current branch, since there's no getcurrentbranch() function like in FCEUX.
Enter the starting and ending frames of the block that you would like to remove, then select which players to edit before pressing the button.
Pressing the button more than once causes an error. There are two ways to prevent this:
1. Update the branch after each button press, thereby making the changes permanent.
2. Reload the branch, thereby undoing the changes.
Feel free to improve upon this.
Dacicus 2020/08/31
--]]
local number_of_players = 2
local current_branch = {}
local current_keys = {}
local option_form, box_text, box_frame_start, box_frame_end
local check_boxes = {}
local form = {["width"] = 300, ["height"] = 300, ["title"] = "TAStudio Input Remover"}
local text = {["bg_color"] = 0x00000000, ["startx"] = 10, ["starty"] = 10, ["startm"] = "Starting frame:",
["endx"] = 10, ["endy"] = 40, ["endm"] = "Ending frame:",
["remx"] = 10, ["remy"] = (form["height"] / 3) - 20, ["remm"] = "Input to remove:"}
local box = {["startw"] = 100, ["starth"] = 20, ["startx"] = 15 + form["width"] / 2, ["starty"] = 10,
["endw"] = 100, ["endh"] = 20, ["endx"] = 15 + form["width"] / 2, ["endy"] = 40}
local button = {["caption"] = "Remove input", ["x"] = (form["width"] / 2) - 75, ["y"] = 2 * form["height"] / 3,
["width"] = 150, ["height"] = 50}
local function get_current_keys(curr_keys)
for k, v in pairs(joypad.get()) do
if string.find(k, "P1", 1, true) then
local curr_key = string.gsub(k, "P1 ", "")
table.insert(curr_keys, curr_key)
end
end
table.sort(curr_keys)
end
get_current_keys(current_keys)
local function set_branch(idx)
current_branch["index"] = idx
current_branch["id"] = tastudio.getbranches()[idx]["Id"]
end
tastudio.onbranchload(set_branch)
local function display_text(box, text)
forms.setDefaultTextBackground(box, text["bg_color"])
forms.drawText(box, text["startx"], text["starty"], text["startm"])
forms.drawText(box, text["endx"], text["endy"], text["endm"])
forms.drawText(box, text["remx"], text["remy"], text["remm"])
end
local function initialize_input_boxes(form, box)
local box_start = forms.textbox(form, "0", box["startw"], box["starth"], "UNSIGNED", box["startx"], box["starty"])
local box_end = forms.textbox(form, "0", box["endw"], box["endh"], "UNSIGNED", box["endx"], box["endy"])
return box_start, box_end
end
local function initialize_checkboxes(form, players, xpos, ypos)
local check_box = {}
for i = 1, players do
local curr_x = 10 + ((i - 1) % 2) * (xpos / 2)
local curr_y = ypos + math.floor((i - 1) / 2) * 30
check_box[i] = forms.checkbox(form, "P" .. i, curr_x, curr_y)
end
return check_box
end
local function clear_input(fr)
for player = 1, number_of_players do
if forms.ischecked(check_boxes[player]) then
for curr_key = 1, #current_keys do
tastudio.clearinputchanges()
tastudio.submitinputchange(fr, "P" .. player .. " " .. current_keys[curr_key], false)
tastudio.applyinputchanges()
end
end
end
end
local function replace_input(old, new)
local input_new = tastudio.getbranchinput(current_branch["id"], new)
for player = 1, number_of_players do
if forms.ischecked(check_boxes[player]) then
for curr_key = 1, #current_keys do
tastudio.clearinputchanges()
local key_string = "P" .. player .. " " .. current_keys[curr_key]
tastudio.submitinputchange(old, key_string, input_new[key_string])
tastudio.applyinputchanges()
end
end
end
end
local function process_frame(fr, ln, diff)
-- Subtract 1 in next line in order to prevent an error on line 91: "invalid arguments to method call"
-- It seems like the last frame show in TAStudio doesn't actually exist? It always has to be empty.
if fr + diff > ln - 1 then
clear_input(fr)
else
replace_input(fr, fr + diff)
end
end
local function process_button()
local frame_start = tonumber(forms.gettext(box_frame_start))
local frame_end = tonumber(forms.gettext(box_frame_end))
local movie_length = movie.length()
if frame_start > frame_end then
console.writeline("ERROR: Starting frame cannot be greater than ending frame.")
elseif frame_end > movie_length then
console.writeline("ERROR: Ending frame cannot be after movie end.")
else
local frame_diff = frame_end - frame_start + 1
-- Subtract 1 in next line in order to prevent 1 extra frame from being added at the end of the movie.
for curr_frame = frame_start, movie_length - 1 do
process_frame(curr_frame, movie_length, frame_diff)
end
end
end
local function initialize_button(form, b)
forms.button(form, b["caption"], process_button, b["x"], b["y"], b["width"], b["height"])
end
if number_of_players < 1 then
console.writeline("ERROR: Must have at least 1 player.")
elseif number_of_players > 4 then
console.writeline("ERROR: Cannot have more than 4 players.")
elseif not tastudio.engaged() then
console.writeline("ERROR: TAStudio is not active.")
else
option_form = forms.newform(form["width"], form["height"], form["title"])
box_text = forms.pictureBox(option_form, 0, 0, form["width"] / 2, form["height"] / 3)
display_text(box_text, text)
box_frame_start, box_frame_end = initialize_input_boxes(option_form, box)
check_boxes = initialize_checkboxes(option_form, number_of_players, form["width"], form["height"] / 3)
initialize_button(option_form, button)
end