1152 lines
29 KiB
Plaintext
1152 lines
29 KiB
Plaintext
gc = game.translate_string
|
||
|
||
local sound_object_by_path = {}
|
||
--' <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
|
||
function get_safe_sound_object(path)
|
||
if sound_object_by_path[path] == nil then
|
||
local snd_obj = sound_object(path)
|
||
sound_object_by_path[path] = {snd_obj}
|
||
return snd_obj
|
||
end
|
||
|
||
for i, snd_obj in ipairs(sound_object_by_path[path]) do
|
||
if not snd_obj:playing() then
|
||
return snd_obj
|
||
end
|
||
end
|
||
|
||
local snd_obj = sound_object(path)
|
||
table.insert(sound_object_by_path, snd_obj)
|
||
return snd_obj
|
||
end
|
||
|
||
function stop_all_sound_object()
|
||
for k,v in pairs(sound_object_by_path) do
|
||
if v then
|
||
for i, snd_obj in pairs(v) do
|
||
snd_obj:stop()
|
||
end
|
||
end
|
||
end
|
||
sound_object_by_path = {}
|
||
end
|
||
|
||
local playing_piano_id = nil
|
||
|
||
local notes = {
|
||
"C1",
|
||
"C#1",
|
||
"D1",
|
||
"D#1",
|
||
"E1",
|
||
"F1",
|
||
"F#1",
|
||
"G1",
|
||
"G#1",
|
||
"A1",
|
||
"A#1",
|
||
"B1",
|
||
"C2",
|
||
"C#2",
|
||
"D2",
|
||
"D#2",
|
||
"E2",
|
||
"F2",
|
||
"F#2",
|
||
"G2",
|
||
"G#2",
|
||
"A2",
|
||
"A#2",
|
||
"B2",
|
||
"C3",
|
||
"C#3",
|
||
"D3",
|
||
"D#3",
|
||
"E3",
|
||
"F3",
|
||
"F#3",
|
||
"G3",
|
||
"G#3",
|
||
"A3",
|
||
"A#3",
|
||
"B3",
|
||
"C4",
|
||
"C#4",
|
||
"D4",
|
||
"D#4",
|
||
"E4",
|
||
"F4",
|
||
"F#4",
|
||
"G4",
|
||
"G#4",
|
||
"A4",
|
||
"A#4",
|
||
"B4",
|
||
"C5",
|
||
"C#5",
|
||
"D5",
|
||
"D#5",
|
||
"E5",
|
||
"F5",
|
||
"F#5",
|
||
"G5",
|
||
"G#5",
|
||
"A5",
|
||
"A#5",
|
||
"B5",
|
||
"C6",
|
||
"C#6",
|
||
"D6",
|
||
"D#6",
|
||
"E6",
|
||
"F6",
|
||
"F#6",
|
||
"G6",
|
||
"G#6",
|
||
"A6",
|
||
"A#6",
|
||
"B6",
|
||
"C7",
|
||
"C#7",
|
||
"D7",
|
||
"D#7",
|
||
"E7",
|
||
"F7",
|
||
"F#7",
|
||
"G7",
|
||
"G#7",
|
||
"A7",
|
||
"A#7",
|
||
"B7",
|
||
"C8",
|
||
}
|
||
local note2i = {}
|
||
local whitenote2i = {}
|
||
local sharpnote2i = {}
|
||
local i = 1
|
||
local note_i = 1
|
||
local sharp_i = 1
|
||
for k,v in pairs(notes) do
|
||
if string.find(v, "#") then
|
||
sharpnote2i[v] = sharp_i
|
||
sharp_i = sharp_i + 1
|
||
else
|
||
whitenote2i[v] = note_i
|
||
note_i = note_i + 1
|
||
end
|
||
note2i[v]=i
|
||
|
||
i = i + 1
|
||
end
|
||
|
||
-- local ini_instruments = ini_file_ex("plugins\\playable_piano\\instruments\\instrument_piano.ltx")
|
||
local ini_instruments = ini_file_ex("plugins\\playable_piano\\instruments\\instruments.ltx")
|
||
local instruments = {}
|
||
local note2snd = {}
|
||
-- for i, note_str in ipairs(notes) do
|
||
-- note2snd[note_str] = ini_instruments:r_string_ex("instrument_piano", note_str)
|
||
-- end
|
||
for i, section in pairs(ini_instruments:get_sections()) do
|
||
instruments[i] = {}
|
||
instruments[i]["name"] = ini_instruments:r_string_ex(section, "name")
|
||
for _, note_str in ipairs(notes) do
|
||
instruments[i][note_str] = ini_instruments:r_string_ex(section, note_str)
|
||
end
|
||
end
|
||
note2snd = instruments[1]
|
||
|
||
local dik2note = {
|
||
[DIK_keys.DIK_1] = "C2",
|
||
[DIK_keys.DIK_2] = "D2",
|
||
[DIK_keys.DIK_3] = "E2",
|
||
[DIK_keys.DIK_4] = "F2",
|
||
[DIK_keys.DIK_5] = "G2",
|
||
[DIK_keys.DIK_6] = "A2",
|
||
[DIK_keys.DIK_7] = "B2",
|
||
[DIK_keys.DIK_8] = "C3",
|
||
[DIK_keys.DIK_9] = "D3",
|
||
[DIK_keys.DIK_0] = "E3",
|
||
[DIK_keys.DIK_Q] = "F3",
|
||
[DIK_keys.DIK_W] = "G3",
|
||
[DIK_keys.DIK_E] = "A3",
|
||
[DIK_keys.DIK_R] = "B3",
|
||
[DIK_keys.DIK_T] = "C4",
|
||
[DIK_keys.DIK_Y] = "D4",
|
||
[DIK_keys.DIK_U] = "E4",
|
||
[DIK_keys.DIK_I] = "F4",
|
||
[DIK_keys.DIK_O] = "G4",
|
||
[DIK_keys.DIK_P] = "A4",
|
||
[DIK_keys.DIK_A] = "B4",
|
||
[DIK_keys.DIK_S] = "C5",
|
||
[DIK_keys.DIK_D] = "D5",
|
||
[DIK_keys.DIK_F] = "E5",
|
||
[DIK_keys.DIK_G] = "F5",
|
||
[DIK_keys.DIK_H] = "G5",
|
||
[DIK_keys.DIK_J] = "A5",
|
||
[DIK_keys.DIK_K] = "B5",
|
||
[DIK_keys.DIK_L] = "C6",
|
||
[DIK_keys.DIK_Z] = "D6",
|
||
[DIK_keys.DIK_X] = "E6",
|
||
[DIK_keys.DIK_C] = "F6",
|
||
[DIK_keys.DIK_V] = "G6",
|
||
[DIK_keys.DIK_B] = "A6",
|
||
[DIK_keys.DIK_N] = "B6",
|
||
[DIK_keys.DIK_M] = "C7",
|
||
}
|
||
|
||
local mod_dik2note = {
|
||
[DIK_keys.DIK_1] = "C#2",
|
||
[DIK_keys.DIK_2] = "D#2",
|
||
[DIK_keys.DIK_4] = "F#2",
|
||
[DIK_keys.DIK_5] = "G#2",
|
||
[DIK_keys.DIK_6] = "A#2",
|
||
|
||
[DIK_keys.DIK_8] = "C#3",
|
||
[DIK_keys.DIK_9] = "D#3",
|
||
[DIK_keys.DIK_Q] = "F#3",
|
||
[DIK_keys.DIK_W] = "G#3",
|
||
[DIK_keys.DIK_E] = "A#3",
|
||
|
||
[DIK_keys.DIK_T] = "C#4",
|
||
[DIK_keys.DIK_Y] = "D#4",
|
||
[DIK_keys.DIK_I] = "F#4",
|
||
[DIK_keys.DIK_O] = "G#4",
|
||
[DIK_keys.DIK_P] = "A#4",
|
||
|
||
[DIK_keys.DIK_S] = "C#5",
|
||
[DIK_keys.DIK_D] = "D#5",
|
||
[DIK_keys.DIK_G] = "F#5",
|
||
[DIK_keys.DIK_H] = "G#5",
|
||
[DIK_keys.DIK_J] = "A#5",
|
||
|
||
[DIK_keys.DIK_L] = "C#6",
|
||
[DIK_keys.DIK_Z] = "D#6",
|
||
[DIK_keys.DIK_C] = "F#6",
|
||
[DIK_keys.DIK_V] = "G#6",
|
||
[DIK_keys.DIK_B] = "A#6",
|
||
}
|
||
|
||
local mod_dik = DIK_keys.DIK_LSHIFT
|
||
local mod_held = false
|
||
|
||
local autokey2note = {
|
||
-- White keys
|
||
["1"] = "C2",
|
||
["2"] = "D2",
|
||
["3"] = "E2",
|
||
["4"] = "F2",
|
||
["5"] = "G2",
|
||
["6"] = "A2",
|
||
["7"] = "B2",
|
||
["8"] = "C3",
|
||
["9"] = "D3",
|
||
["0"] = "E3",
|
||
["q"] = "F3",
|
||
["w"] = "G3",
|
||
["e"] = "A3",
|
||
["r"] = "B3",
|
||
["t"] = "C4",
|
||
["y"] = "D4",
|
||
["u"] = "E4",
|
||
["i"] = "F4",
|
||
["o"] = "G4",
|
||
["p"] = "A4",
|
||
["a"] = "B4",
|
||
["s"] = "C5",
|
||
["d"] = "D5",
|
||
["f"] = "E5",
|
||
["g"] = "F5",
|
||
["h"] = "G5",
|
||
["j"] = "A5",
|
||
["k"] = "B5",
|
||
["l"] = "C6",
|
||
["z"] = "D6",
|
||
["x"] = "E6",
|
||
["c"] = "F6",
|
||
["v"] = "G6",
|
||
["b"] = "A6",
|
||
["n"] = "B6",
|
||
["m"] = "C7",
|
||
|
||
-- Black keys
|
||
["!"] = "C#2",
|
||
["@"] = "D#2",
|
||
["$"] = "F#2",
|
||
["%"] = "G#2",
|
||
["^"] = "A#2",
|
||
|
||
["*"] = "C#3",
|
||
["("] = "D#3",
|
||
["Q"] = "F#3",
|
||
["W"] = "G#3",
|
||
["E"] = "A#3",
|
||
|
||
["T"] = "C#4",
|
||
["Y"] = "D#4",
|
||
["I"] = "F#4",
|
||
["O"] = "G#4",
|
||
["P"] = "A#4",
|
||
|
||
["S"] = "C#5",
|
||
["D"] = "D#5",
|
||
["G"] = "F#5",
|
||
["H"] = "G#5",
|
||
["J"] = "A#5",
|
||
|
||
["L"] = "C#6",
|
||
["Z"] = "D#6",
|
||
["C"] = "F#6",
|
||
["V"] = "G#6",
|
||
["B"] = "A#6",
|
||
}
|
||
local bpm = 100
|
||
local transpose = 0
|
||
local volume = 1.0
|
||
local sheets = {}
|
||
local sheets_index = 1
|
||
local delay = {
|
||
WHOLENOTE = 1,
|
||
HALFNOTE = 2,
|
||
QUARTERNOTE = 4,
|
||
EIGHTHNOTE = 8
|
||
}
|
||
|
||
local function wait2secs(note)
|
||
if note == nil then
|
||
return nil
|
||
end
|
||
-- Converts note to seconds according to type of note (see enum notes) and current bpm
|
||
local spb = 60/bpm
|
||
return spb / note
|
||
end
|
||
|
||
function getSheetsWithTags(tags, tag2sheets)
|
||
local matching_sheets = {}
|
||
local tag_indexes = {}
|
||
for _, val in pairs(tags) do
|
||
-- Contains current index for access at a particular tag
|
||
tag_indexes[val] = 1
|
||
end
|
||
|
||
local selected_tag = tags[1]
|
||
local keep_looping = true
|
||
while keep_looping do
|
||
local selected_sheet = tag2sheets[selected_tag][tag_indexes[selected_tag]]
|
||
-- print(selected_sheet)
|
||
if selected_sheet == nil then
|
||
keep_looping = false
|
||
end
|
||
if keep_looping then
|
||
|
||
local next_tag = selected_tag
|
||
local is_matching = true
|
||
for _, tag in pairs(tags) do
|
||
-- Ignore comparison with the selected tag
|
||
if tag ~= selected_tag then
|
||
-- Check if tag has the same sheet as the selected one
|
||
local sheet = tag2sheets[tag][tag_indexes[tag]]
|
||
-- print("COMPARING:" .. selected_sheet .. "v" .. sheet)
|
||
if sheet ~= selected_sheet then
|
||
-- No match, pick tag with lowest sheet
|
||
is_matching = false
|
||
if sheet < selected_sheet then next_tag = tag end
|
||
end
|
||
end
|
||
end
|
||
-- found sheet with the same tags
|
||
if is_matching then
|
||
table.insert(matching_sheets, selected_sheet)
|
||
end
|
||
|
||
-- continue incrementing selected tag index
|
||
selected_tag = next_tag
|
||
tag_indexes[selected_tag] = tag_indexes[selected_tag] + 1
|
||
end
|
||
end
|
||
|
||
return matching_sheets
|
||
end
|
||
|
||
|
||
function getTags()
|
||
local ini_tags = ini_file_ex("plugins\\playable_piano\\tags\\categories.ltx")
|
||
local tags_info = {}
|
||
for _, section in pairs(ini_tags:get_sections()) do
|
||
local priority = ini_tags:r_float_ex(section, "priority")
|
||
table.insert(tags_info, {
|
||
["priority"] = priority,
|
||
["tag"] = section
|
||
})
|
||
end
|
||
table.sort(tags_info, function(a,b) return a["priority"] < b["priority"] end)
|
||
|
||
local tags = {}
|
||
local tag2sheets = {}
|
||
for i,v in ipairs(tags_info) do
|
||
table.insert(tags, v["tag"])
|
||
tag2sheets[v["tag"]] = {}
|
||
end
|
||
|
||
return tags, tag2sheets
|
||
end
|
||
|
||
local ini_sheets = ini_file_ex("plugins\\playable_piano\\sheets\\sheets.ltx")
|
||
-- local tags = getTags()
|
||
-- local tag2sheets = {}
|
||
local tags, tag2sheets = getTags()
|
||
|
||
local i = 1
|
||
for _, section in pairs(ini_sheets:get_sections()) do
|
||
local bpm = ini_sheets:r_float_ex(section, "bpm")
|
||
local title = ""
|
||
local sheet_path = ini_sheets:r_string_ex(section, "sheet")
|
||
local transpose = ini_sheets:r_float_ex(section, "transpose") or 0
|
||
local tags_str = ini_sheets:r_string_ex(section, "tags") or ""
|
||
|
||
-- Build inverted index of tags to list of sheets
|
||
local tags_list = str_explode(tags_str,",")
|
||
for j=1,#tags_list do
|
||
local tag = tags_list[j]
|
||
if tag2sheets[tag] == nil then
|
||
table.insert(tags, tag)
|
||
tag2sheets[tag] = {}
|
||
end
|
||
table.insert(tag2sheets[tag], i)
|
||
end
|
||
|
||
local file_reader = getFS():r_open("$game_config$", sheet_path)
|
||
local file_data = ""
|
||
local prev_char = nil
|
||
local simult_flag = false
|
||
local simult_notes = {}
|
||
local music_chart = {}
|
||
local read_modes = {
|
||
title=0,
|
||
subtitle=1,
|
||
sheet=2
|
||
}
|
||
local mode = read_modes.title
|
||
while not file_reader:r_eof() do
|
||
local raw_data = file_reader:r_u8()
|
||
local data = string.char(raw_data)
|
||
-- -- printf("CHAR: " .. data .. "|CODE: " .. raw_data)
|
||
|
||
if mode == read_modes.title then
|
||
if data == "\n" then
|
||
-- Transition to reading subtitles
|
||
title = file_data
|
||
file_data = ""
|
||
mode = read_modes.subtitle
|
||
else
|
||
file_data = file_data .. data
|
||
end
|
||
|
||
elseif mode == read_modes.subtitle then
|
||
if data == "\n" then
|
||
-- Transition to reading sheet
|
||
file_data = ""
|
||
mode = read_modes.sheet
|
||
end
|
||
-- Ignore subtitle text for now
|
||
|
||
-- -- printf("TITLE: " .. title)
|
||
elseif mode == read_modes.sheet then
|
||
if autokey2note[data] then
|
||
if simult_flag then
|
||
-- Collect notes together
|
||
table.insert(simult_notes, autokey2note[data])
|
||
else
|
||
-- Add quarternote pause for consecutive notes (jjjj)
|
||
if autokey2note[prev_char] or prev_char == "]" then
|
||
table.insert(music_chart, {
|
||
type = "wait", -- "note", "wait"
|
||
wait = delay.QUARTERNOTE
|
||
})
|
||
end
|
||
-- Play note (j)
|
||
table.insert(music_chart, {
|
||
type = "note", -- "note", "wait"
|
||
note = autokey2note[data]
|
||
})
|
||
end
|
||
|
||
elseif data == " " then
|
||
if simult_flag then
|
||
-- Add eighthnote pause for spaces in brackets ([j j j j])
|
||
if #simult_notes ~= 0 then
|
||
table.insert(music_chart, {
|
||
type = "notes",
|
||
notes = simult_notes
|
||
})
|
||
|
||
simult_notes = {}
|
||
end
|
||
table.insert(music_chart, {
|
||
type = "wait",
|
||
wait = delay.EIGHTHNOTE
|
||
})
|
||
else
|
||
-- Add halfnote pause for spaces (j j j j)
|
||
table.insert(music_chart, {
|
||
type = "wait",
|
||
wait = delay.HALFNOTE
|
||
})
|
||
end
|
||
elseif data == "|" then
|
||
-- Add wholenote pause for pipes (j|j|j|j)
|
||
table.insert(music_chart, {
|
||
type = "wait",
|
||
wait = delay.WHOLENOTE
|
||
})
|
||
-- -- ASCII code for carriage return (NOTE: newlines are done with codes 13, 10 in sequence)
|
||
elseif raw_data == 10 then
|
||
if prev_char == "\n" then
|
||
-- Add total of 2 whole note pause for 2 new lines (previous \n already added halfnote)
|
||
table.insert(music_chart, {
|
||
type = "wait",
|
||
wait = delay.HALFNOTE
|
||
})
|
||
table.insert(music_chart, {
|
||
type = "wait",
|
||
wait = delay.WHOLENOTE
|
||
})
|
||
else
|
||
-- Add halfnote pause for newlines
|
||
table.insert(music_chart, {
|
||
type = "wait",
|
||
wait = delay.HALFNOTE
|
||
})
|
||
end
|
||
elseif data == "[" then
|
||
if prev_char ~= " " and prev_char ~= "|" and prev_char ~= "\n" then
|
||
table.insert(music_chart, {
|
||
type = "wait", -- "note", "wait"
|
||
wait = delay.QUARTERNOTE
|
||
})
|
||
end
|
||
simult_flag = true
|
||
elseif data == "]" then
|
||
table.insert(music_chart, {
|
||
type = "notes",
|
||
notes = simult_notes
|
||
})
|
||
|
||
simult_flag = false
|
||
simult_notes = {}
|
||
end
|
||
-- if data == "\n" then
|
||
if raw_data == 10 then
|
||
file_data = file_data .. " \\n"
|
||
prev_char = data
|
||
elseif raw_data == 13 then
|
||
-- ignore carriage returns
|
||
else
|
||
file_data = file_data .. data
|
||
prev_char = data
|
||
end
|
||
|
||
end
|
||
end
|
||
|
||
table.insert(sheets, {
|
||
title = title,
|
||
bpm = bpm,
|
||
chart = music_chart,
|
||
transpose = transpose,
|
||
score = file_data,
|
||
})
|
||
i = i + 1
|
||
end
|
||
|
||
-- Functions for autoplaying music
|
||
function PlaySound(note)
|
||
-- Transpose note
|
||
local i = note2i[note]
|
||
i = i + transpose
|
||
local new_note = notes[i]
|
||
|
||
-- Play note
|
||
local snd = get_safe_sound_object(note2snd[new_note])
|
||
local obj = playing_piano_id and level.object_by_id(playing_piano_id) or db.actor
|
||
if snd then
|
||
snd:play_at_pos(obj, obj:position(), sound_object.s3d)
|
||
snd.volume = volume
|
||
else
|
||
-- printf("could not play sound")
|
||
end
|
||
end
|
||
|
||
|
||
local music_index = nil
|
||
local prev_part = nil
|
||
|
||
|
||
function UpdateChartMetadata()
|
||
bpm = sheets[sheets_index].bpm
|
||
transpose = sheets[sheets_index].transpose
|
||
-- printf("BPM: " .. bpm)
|
||
-- printf("Score: " .. sheets[sheets_index].score)
|
||
|
||
if GUI then
|
||
GUI.tempo_trackbar:SetFValue((bpm - 40) / 200)
|
||
GUI.tempo_value:SetText(bpm)
|
||
|
||
GUI.transpose_trackbar:SetFValue((12+transpose) / 24)
|
||
GUI.transpose_value:SetText(transpose)
|
||
|
||
GUI.volume_trackbar:SetFValue((volume) / 2)
|
||
GUI.volume_value:SetText(volume)
|
||
|
||
end
|
||
end
|
||
function IterateOverChart()
|
||
-- printf("IterateOverChart")
|
||
local music_chart = sheets[sheets_index].chart
|
||
if music_index == nil then
|
||
music_index = 1
|
||
end
|
||
|
||
local part = music_chart[music_index]
|
||
|
||
-- Animate key release
|
||
if prev_part and GUI then
|
||
local time_eighthnote = (60/bpm) / 8
|
||
if prev_part.notes then
|
||
for i, note in ipairs(prev_part.notes) do
|
||
local isSharp = false
|
||
if sharpnote2i[note] then
|
||
isSharp = true
|
||
end
|
||
local wait = part and wait2secs(part.wait) or time_eighthnote
|
||
wait = wait - (time_eighthnote / 2)
|
||
CreateTimeEvent("PlayablePiano","ReleaseKey-" .. note, wait, GUI.AnimateKeyRelease, GUI, note, isSharp)
|
||
end
|
||
end
|
||
if prev_part.note then
|
||
local isSharp = false
|
||
if sharpnote2i[prev_part.note] then
|
||
isSharp = true
|
||
end
|
||
local wait = part and wait2secs(part.wait) or time_eighthnote
|
||
wait = wait - (time_eighthnote / 2)
|
||
CreateTimeEvent("PlayablePiano","ReleaseKey-" .. prev_part.note, wait, GUI.AnimateKeyRelease, GUI, prev_part.note, isSharp)
|
||
end
|
||
end
|
||
|
||
if part == nil then
|
||
music_index = nil
|
||
prev_part = nil
|
||
-- printf("No more part")
|
||
return true
|
||
end
|
||
|
||
if part.type == "note" then
|
||
PlaySound(part.note)
|
||
if GUI then
|
||
local isSharp = false
|
||
if sharpnote2i[part.note] then
|
||
isSharp = true
|
||
end
|
||
GUI:AnimateKeyPress(part.note, isSharp)
|
||
end
|
||
-- printf("Play note: " .. part.note)
|
||
elseif part.type == "wait" then
|
||
-- printf("Wait for: " .. wait2secs(part.wait))
|
||
ResetTimeEvent("PlayablePiano","IterateChart",wait2secs(part.wait))
|
||
elseif part.type == "notes" then
|
||
-- printf("Play multiple notes")
|
||
for i, note in ipairs(part.notes) do
|
||
-- printf("Play note(multiple): " .. note)
|
||
PlaySound(note)
|
||
if GUI then
|
||
local isSharp = false
|
||
if sharpnote2i[note] then
|
||
isSharp = true
|
||
end
|
||
GUI:AnimateKeyPress(note, isSharp)
|
||
end
|
||
end
|
||
end
|
||
|
||
music_index = music_index + 1
|
||
prev_part = part
|
||
return false
|
||
|
||
end
|
||
|
||
-- UI for Piano Object
|
||
|
||
GUI = nil -- instance, don't touch
|
||
|
||
class "UIFurniturePiano" (CUIScriptWnd)
|
||
|
||
function UIFurniturePiano:__init() super()
|
||
self:InitControls()
|
||
self:InitCallbacks()
|
||
end
|
||
|
||
function UIFurniturePiano:__finalize()
|
||
GUI = nil
|
||
end
|
||
|
||
function UIFurniturePiano:InitControls()
|
||
self:SetWndRect(Frect():set(0,0,1024,768))
|
||
self.wide = (device().width/device().height) > (1024/768 + 0.01)
|
||
|
||
self:SetAutoDelete(true)
|
||
|
||
local xml = CScriptXmlInit()
|
||
-- xml:ParseFile("ui_sleep_dialog.xml")
|
||
xml:ParseFile("ui_furniture_piano_dialog.xml")
|
||
|
||
-- controls
|
||
self.autoplay_btn = xml:InitCheck("autoplay_btn", self)
|
||
self.autoplay_btn_text = xml:InitTextWnd("autoplay_btn:txt", self.autoplay_btn)
|
||
self.autoplay_btn_text:SetText(gc("st_autoplay"))
|
||
self:Register(self.autoplay_btn, "toggle_autoplay")
|
||
|
||
-- button toggle
|
||
self.piano_config_btn = xml:InitCheck("piano_config_btn", self)
|
||
self.piano_config_btn_text = xml:InitTextWnd("piano_config_btn:txt", self.piano_config_btn)
|
||
self.piano_config_btn_text:SetText(gc("st_configure"))
|
||
self:Register(self.piano_config_btn, "toggle_piano_config")
|
||
|
||
-- Config Background
|
||
self.piano_config_bg = xml:InitFrame("piano_config_bg", self)
|
||
self.piano_config_bg:Show(false)
|
||
self.piano_config_scroll = xml:InitScrollView("piano_config_scroll", self.piano_config_bg)
|
||
|
||
-- Tempo
|
||
self.tempo_container = xml:InitStatic("trackbar_container", self.piano_config_scroll)
|
||
self.tempo_caption = xml:InitTextWnd("trackbar_container:caption", self.tempo_container)
|
||
self.tempo_caption:SetText(gc("st_tempo"))
|
||
self.tempo_trackbar = xml:InitTrackBar("trackbar_container:trackbar", self.tempo_container)
|
||
self.tempo_value = xml:InitTextWnd("trackbar_container:value", self.tempo_container)
|
||
|
||
self.tempo_trackbar:SetStep(1/200)
|
||
self:Register(self.tempo_trackbar, "tempo")
|
||
|
||
-- Transpose
|
||
self.transpose_container = xml:InitStatic("trackbar_container", self.piano_config_scroll)
|
||
self.transpose_caption = xml:InitTextWnd("trackbar_container:caption", self.transpose_container)
|
||
self.transpose_caption:SetText(gc("st_transpose"))
|
||
self.transpose_trackbar = xml:InitTrackBar("trackbar_container:trackbar", self.transpose_container)
|
||
self.transpose_value = xml:InitTextWnd("trackbar_container:value", self.transpose_container)
|
||
|
||
self.transpose_trackbar:SetStep(1/24) -- Range: -12 to +12
|
||
self:Register(self.transpose_trackbar, "transpose")
|
||
|
||
-- Volume
|
||
self.volume_container = xml:InitStatic("trackbar_container", self.piano_config_scroll)
|
||
self.volume_caption = xml:InitTextWnd("trackbar_container:caption", self.volume_container)
|
||
self.volume_caption:SetText(gc("st_volume"))
|
||
self.volume_trackbar = xml:InitTrackBar("trackbar_container:trackbar", self.volume_container)
|
||
self.volume_value = xml:InitTextWnd("trackbar_container:value", self.volume_container)
|
||
|
||
self.volume_trackbar:SetStep(1/20) -- Range: 0.0 to 2.0
|
||
self.volume_trackbar:SetFValue(volume/2)
|
||
self:Register(self.volume_trackbar, "volume")
|
||
|
||
-- Instrument Select
|
||
self.instrument_container = xml:InitStatic("select_container", self.piano_config_scroll)
|
||
self.instrument_caption = xml:InitTextWnd("select_container:caption", self.instrument_container)
|
||
self.instrument_caption:SetText(gc("st_instrument"))
|
||
self.instrument_select = xml:InitComboBox("select_container:select", self.instrument_container)
|
||
self:Register(self.instrument_select, "instrument")
|
||
local i = 1
|
||
for instrument, notes2str in pairs(instruments) do
|
||
self.instrument_select:AddItem(notes2str["name"], i)
|
||
i = i+1
|
||
end
|
||
self.instrument_select:SetText(gc(instruments[1].name))
|
||
|
||
-- Sheet
|
||
self.sheet_bg = xml:InitFrame("sheet_bg", self)
|
||
|
||
|
||
-- Sheet Select
|
||
self.sheet_select = xml:InitComboBox("sheet_select", self)
|
||
self:Register(self.sheet_select, "sheet_select")
|
||
self.filtered_sheets = {}
|
||
for i=1,#sheets do
|
||
self.sheet_select:AddItem(sheets[i].title, i)
|
||
self.filtered_sheets[i] = i
|
||
end
|
||
self.sheet_select:SetText(sheets[1].title)
|
||
|
||
-- Tag Select
|
||
-- button toggle
|
||
self.tag_select_btn = xml:InitCheck("tag_select_btn", self)
|
||
self.tag_select_btn_text = xml:InitTextWnd("tag_select_btn:txt", self.tag_select_btn)
|
||
self.tag_select_btn_text:SetText(gc("st_filter_by_tag"))
|
||
self:Register(self.tag_select_btn, "toggle_tags")
|
||
|
||
self.tag_select_bg = xml:InitFrame("tag_select_bg", self)
|
||
self.tag_select_bg:Show(false)
|
||
self.tag_select_scroll = xml:InitScrollView("tag_select_scroll", self.tag_select_bg)
|
||
self.tag_checkboxes = {}
|
||
for i=1,#tags do
|
||
local tag_wnd = xml:InitStatic("tag_select", self.tag_select_scroll)
|
||
|
||
local tag_cap = xml:InitTextWnd("tag_select:cap_tag", tag_wnd)
|
||
tag_cap:SetText(gc(tags[i]))
|
||
local tag_check = xml:InitCheck("tag_select:check_tag", tag_wnd)
|
||
tag_check:SetCheck(false)
|
||
|
||
self:Register(tag_check, "tag_checkbox")
|
||
|
||
table.insert(self.tag_checkboxes, {
|
||
container = tag_wnd,
|
||
caption = tag_cap,
|
||
check = tag_check,
|
||
tag_name = tags[i]
|
||
})
|
||
end
|
||
|
||
self.score_scroll = xml:InitScrollView("score_scroll", self.sheet_bg)
|
||
|
||
self.title = xml:InitTextWnd("title", self.score_scroll)
|
||
self.title:SetText(sheets[sheets_index].title)
|
||
self.title:SetAutoDelete(false)
|
||
-- self.score_scroll:AddWindow(self.title, true)
|
||
|
||
self.score = xml:InitTextWnd("score", self.score_scroll)
|
||
self.score:SetText(sheets[sheets_index].score)
|
||
self.score:AdjustHeightToText()
|
||
self.score:SetAutoDelete(false)
|
||
-- self.score_scroll:AddWindow(self.score, true)
|
||
|
||
|
||
self.piano_bg = xml:InitStatic("piano_bg", self)
|
||
self.white_keys = {}
|
||
for i = 1, 36, 1 do
|
||
table.insert(self.white_keys, xml:InitStatic("white_key", self))
|
||
local new_pos = self.white_keys[i]:GetWndPos()
|
||
new_pos.x = new_pos.x + (self.white_keys[i]:GetWidth() * (i-1))
|
||
self.white_keys[i]:SetWndPos(new_pos)
|
||
end
|
||
|
||
local skip_key = {[3] = true,
|
||
[7] = true,
|
||
[10] = true,
|
||
[14] = true,
|
||
[17] = true,
|
||
[21] = true,
|
||
[24] = true,
|
||
[28] = true,
|
||
[31] = true,
|
||
}
|
||
self.black_keys = {}
|
||
local count = 1
|
||
for i = 1, 34, 1 do
|
||
if skip_key[i] ~= true then
|
||
table.insert(self.black_keys, xml:InitStatic("black_key", self))
|
||
local new_pos = self.black_keys[count]:GetWndPos()
|
||
new_pos.x = new_pos.x + (self.white_keys[1]:GetWidth() * (i-1))
|
||
self.black_keys[count]:SetWndPos(new_pos)
|
||
count = count + 1
|
||
end
|
||
end
|
||
|
||
|
||
self.btn_pickup = xml:Init3tButton("btn_pickup", self)
|
||
self:Register(self.btn_pickup, "btn_pickup")
|
||
|
||
self.btn_close = xml:Init3tButton("btn_close", self)
|
||
self:Register(self.btn_close, "btn_close")
|
||
|
||
end
|
||
|
||
function UIFurniturePiano:Pickup()
|
||
local obj = get_object_by_id(self.obj_id)
|
||
if obj:binded_object().is_world_obj then
|
||
actor_menu.set_msg(1, game.translate_string("st_reject_pickup_world_obj"),3)
|
||
self:Close()
|
||
return
|
||
end
|
||
local item_section = ini_sys:r_string_ex(obj:section(), "item_section") or "device_gas_lamp"
|
||
alife_create_item(item_section, db.actor)
|
||
-- local item = alife():create(item_section, db.actor:position(), 1, db.actor:game_vertex_id(), db.actor:id())
|
||
-- get_object_by_id(item.id):set_condition(obj:binded_object().fuel)
|
||
|
||
alife_release(obj)
|
||
self:Close()
|
||
end
|
||
|
||
function UIFurniturePiano:UpdateTempo()
|
||
local tempo = self.tempo_trackbar:GetFValue()
|
||
-- printf("Tempo (Float): " .. tempo)
|
||
-- printf("Tempo (BPM): " .. 40+(tempo*200))
|
||
bpm = round(40+(tempo*200))
|
||
self.tempo_value:SetText(bpm)
|
||
end
|
||
|
||
function UIFurniturePiano:UpdateTranspose()
|
||
local transpose_tb = self.transpose_trackbar:GetFValue()
|
||
-- printf("transpose (Float): " .. transpose_tb)
|
||
-- printf("transpose (BPM): " .. -12+(transpose_tb*24))
|
||
transpose = round(-12+(transpose_tb*24))
|
||
self.transpose_value:SetText(transpose)
|
||
end
|
||
|
||
function UIFurniturePiano:UpdateVolume()
|
||
local volume_tb = self.volume_trackbar:GetFValue()
|
||
-- printf("volume (Float): " .. volume_tb)
|
||
-- printf("volume (BPM): " .. (round(volume_tb*2*10))/10)
|
||
volume = (round(volume_tb*2*10))/10
|
||
self.volume_value:SetText(volume)
|
||
|
||
end
|
||
|
||
function UIFurniturePiano:OnInstrumentSelect()
|
||
-- printf("OnInstrumentSelect()")
|
||
local instrument_id = self.instrument_select:CurrentID()
|
||
local note_sounds = instruments[instrument_id]
|
||
note2snd = note_sounds
|
||
|
||
-- printf("OnInstrumentSelect() - COMPLETE")
|
||
end
|
||
|
||
function UIFurniturePiano:OnSheetSelect()
|
||
local channel_id = self.sheet_select:CurrentID()
|
||
sheets_index = self.filtered_sheets[channel_id]
|
||
UpdateChartMetadata()
|
||
RemoveTimeEvent("PlayablePiano","IterateChart")
|
||
music_index = 1
|
||
self.autoplay_btn:SetCheck(false)
|
||
|
||
self.title:SetText(sheets[sheets_index].title)
|
||
self.score:SetText(sheets[sheets_index].score)
|
||
|
||
self.score:AdjustHeightToText()
|
||
-- self.score:SetWndSize(vector2():set(self.score:GetWidth(), self.score:GetHeight()))
|
||
|
||
self.score_scroll:Clear()
|
||
self.score_scroll:AddWindow(self.title, true)
|
||
self.title:SetAutoDelete(false)
|
||
self.score_scroll:AddWindow(self.score, true)
|
||
self.score:SetAutoDelete(false)
|
||
|
||
end
|
||
|
||
function UIFurniturePiano:OnTagSelect()
|
||
local tags = {}
|
||
for i=1,#self.tag_checkboxes do
|
||
local is_checked = self.tag_checkboxes[i].check:GetCheck()
|
||
if is_checked then
|
||
table.insert(tags, self.tag_checkboxes[i].tag_name)
|
||
-- printf("CHECKBOX TICKED (" .. self.tag_checkboxes[i].caption:GetText() .. "): " .. i)
|
||
end
|
||
end
|
||
if next(tags) then
|
||
self.filtered_sheets = getSheetsWithTags(tags, tag2sheets)
|
||
self.sheet_select:ClearList()
|
||
for i, sheet in ipairs(self.filtered_sheets) do
|
||
self.sheet_select:AddItem(sheets[sheet].title, i)
|
||
end
|
||
else
|
||
self.filtered_sheets[i] = {}
|
||
self.sheet_select:ClearList()
|
||
for i, sheet in ipairs(sheets) do
|
||
self.sheet_select:AddItem(sheets[i].title, i)
|
||
self.filtered_sheets[i] = i
|
||
end
|
||
end
|
||
end
|
||
|
||
function UIFurniturePiano:ToggleAutoplay()
|
||
local autoplay = self.autoplay_btn:GetCheck()
|
||
if autoplay then
|
||
CreateTimeEvent("PlayablePiano","IterateChart", 0, IterateOverChart)
|
||
else
|
||
RemoveTimeEvent("PlayablePiano","IterateChart")
|
||
end
|
||
end
|
||
|
||
function UIFurniturePiano:TogglePianoConfig()
|
||
self.piano_config_bg:Show(self.piano_config_btn:GetCheck())
|
||
end
|
||
|
||
function UIFurniturePiano:ToggleTags()
|
||
self.tag_select_bg:Show(self.tag_select_btn:GetCheck())
|
||
end
|
||
|
||
function UIFurniturePiano:InitCallbacks()
|
||
-- piano controls
|
||
self:AddCallback("tempo", ui_events.BUTTON_CLICKED, self.UpdateTempo, self)
|
||
self:AddCallback("transpose", ui_events.BUTTON_CLICKED, self.UpdateTranspose, self)
|
||
self:AddCallback("volume", ui_events.BUTTON_CLICKED, self.UpdateVolume, self)
|
||
self:AddCallback("instrument", ui_events.LIST_ITEM_SELECT, self.OnInstrumentSelect, self)
|
||
|
||
self:AddCallback("toggle_autoplay", ui_events.BUTTON_CLICKED, self.ToggleAutoplay, self)
|
||
self:AddCallback("toggle_piano_config", ui_events.BUTTON_CLICKED, self.TogglePianoConfig, self)
|
||
self:AddCallback("toggle_tags", ui_events.BUTTON_CLICKED, self.ToggleTags, self)
|
||
|
||
-- sheet controls
|
||
self:AddCallback("sheet_select", ui_events.LIST_ITEM_SELECT, self.OnSheetSelect, self)
|
||
self:AddCallback("tag_checkbox", ui_events.BUTTON_CLICKED, self.OnTagSelect, self)
|
||
|
||
self:AddCallback("btn_pickup", ui_events.BUTTON_CLICKED, self.Pickup, self)
|
||
self:AddCallback("btn_close", ui_events.BUTTON_CLICKED, self.Close, self)
|
||
end
|
||
|
||
function UIFurniturePiano:Initialize()
|
||
end
|
||
|
||
function UIFurniturePiano:TestAndShow(obj_id)
|
||
-- printf("awf")
|
||
playing_piano_id = obj_id
|
||
self.obj_id = obj_id
|
||
self:Initialize()
|
||
self:ShowDialog(true)
|
||
Register_UI("UIFurniturePiano","ui_piano_dialog")
|
||
end
|
||
|
||
function UIFurniturePiano:Update()
|
||
CUIScriptWnd.Update(self)
|
||
|
||
-- self.title:SetText(sheets[sheets_index].title)
|
||
-- self.score:SetText(sheets[sheets_index].score)
|
||
end
|
||
|
||
function UIFurniturePiano:OnTrackButton()
|
||
end
|
||
|
||
function UIFurniturePiano:AnimateKeyPress(note, isSharp)
|
||
local note_static = nil
|
||
if isSharp then
|
||
local index = sharpnote2i[note] - 5
|
||
-- printf("Index: " .. index)
|
||
note_static = self.black_keys[index]
|
||
if not note_static then
|
||
-- printf("Could not find image static for note.")
|
||
return
|
||
end
|
||
note_static:InitTexture("ui_inGame2_piano_blackkey_on")
|
||
else
|
||
local index = whitenote2i[note] - 7
|
||
-- printf("Index: " .. index)
|
||
note_static = self.white_keys[index]
|
||
if not note_static then
|
||
-- printf("Could not find image static for note.")
|
||
return
|
||
end
|
||
note_static:InitTexture("ui_inGame2_piano_whitekey_on")
|
||
end
|
||
end
|
||
|
||
function UIFurniturePiano:AnimateKeyRelease(note, isSharp)
|
||
local note_static = nil
|
||
if isSharp then
|
||
local index = sharpnote2i[note] - 5
|
||
-- printf("Index: " .. index)
|
||
note_static = self.black_keys[index]
|
||
if not note_static then
|
||
-- printf("Could not find image static for note.")
|
||
return true
|
||
end
|
||
note_static:InitTexture("ui_inGame2_piano_blackkey_off")
|
||
else
|
||
local index = whitenote2i[note] - 7
|
||
-- printf("Index: " .. index)
|
||
note_static = self.white_keys[index]
|
||
if not note_static then
|
||
-- printf("Could not find image static for note.")
|
||
return true
|
||
end
|
||
note_static:InitTexture("ui_inGame2_piano_whitekey_off")
|
||
end
|
||
|
||
return true
|
||
end
|
||
|
||
function UIFurniturePiano:OnKeyboard(dik, keyboard_action)
|
||
local res = CUIScriptWnd.OnKeyboard(self,dik,keyboard_action)
|
||
if (res == false) then
|
||
local bind = dik_to_bind(dik)
|
||
if keyboard_action == ui_events.WINDOW_KEY_PRESSED then
|
||
if dik == mod_dik then
|
||
mod_held = true
|
||
end
|
||
|
||
if dik == DIK_keys.DIK_ESCAPE then
|
||
self:Close()
|
||
end
|
||
|
||
-- -- Hotkey for changing sheets
|
||
-- if dik == DIK_keys.DIK_RIGHT then
|
||
-- if sheets_index < #sheets then
|
||
-- sheets_index = sheets_index + 1
|
||
-- end
|
||
-- UpdateChartMetadata()
|
||
-- -- printf("Selected: " .. sheets[sheets_index].title)
|
||
-- end
|
||
-- if dik == DIK_keys.DIK_LEFT then
|
||
-- if sheets_index > 1 then
|
||
-- sheets_index = sheets_index - 1
|
||
-- end
|
||
-- UpdateChartMetadata()
|
||
-- -- printf("Selected: " .. sheets[sheets_index].title)
|
||
-- end
|
||
|
||
-- -- Hotkey for transpose
|
||
-- if dik == DIK_keys.DIK_UP then
|
||
-- if transpose < 12 then transpose = transpose + 1 end
|
||
-- -- printf("Transpose: " .. transpose)
|
||
-- end
|
||
-- if dik == DIK_keys.DIK_DOWN then
|
||
-- if transpose > 1 then transpose = transpose - 1 end
|
||
-- -- printf("Transpose: " .. transpose)
|
||
-- end
|
||
|
||
-- Hotkey for autoplay
|
||
-- if dik == DIK_keys.DIK_HOME then
|
||
-- -- printf("Pressed HOME key")
|
||
-- CreateTimeEvent("PlayablePiano","IterateChart", 0, IterateOverChart)
|
||
-- end
|
||
|
||
if (not mod_held) and dik2note[dik] then
|
||
local note = dik2note[dik]
|
||
PlaySound(note)
|
||
self:AnimateKeyPress(note, false)
|
||
elseif mod_held and mod_dik2note[dik] then
|
||
local note = mod_dik2note[dik]
|
||
PlaySound(note)
|
||
self:AnimateKeyPress(note, true)
|
||
end
|
||
elseif keyboard_action == ui_events.WINDOW_KEY_RELEASED then
|
||
if dik == mod_dik then
|
||
mod_held = false
|
||
end
|
||
|
||
if (not mod_held) and dik2note[dik] then
|
||
local note = dik2note[dik]
|
||
self:AnimateKeyRelease(note, false)
|
||
elseif mod_held and mod_dik2note[dik] then
|
||
local note = mod_dik2note[dik]
|
||
self:AnimateKeyRelease(note, true)
|
||
end
|
||
end
|
||
end
|
||
|
||
return res
|
||
end
|
||
|
||
function UIFurniturePiano:Close()
|
||
if (self:IsShown()) then
|
||
self:HideDialog()
|
||
end
|
||
|
||
Unregister_UI("UIFurniturePiano")
|
||
end
|
||
|
||
-------
|
||
function start_dialog(obj_id)
|
||
-- printf("awf")
|
||
if (GUI == nil) then
|
||
GUI = UIFurniturePiano()
|
||
end
|
||
GUI:TestAndShow(obj_id)
|
||
UpdateChartMetadata()
|
||
--return GUI
|
||
end |