diff --git a/fcore/options/options-type.sml b/fcore/options/options-type.sml index bc5145c..6407f45 100644 --- a/fcore/options/options-type.sml +++ b/fcore/options/options-type.sml @@ -10,7 +10,8 @@ sig | SAVE_BUTTON | CANCEL_BUTTON - type options_type = {focus: focus, lastUpPress: real, lastDownPress: real} + type options_type = + {focus: focus, isSelected: bool, lastUpPress: real, lastDownPress: real} val initial: options_type end @@ -27,7 +28,13 @@ struct | SAVE_BUTTON | CANCEL_BUTTON - type options_type = {focus: focus, lastUpPress: real, lastDownPress: real} + type options_type = + {focus: focus, isSelected: bool, lastUpPress: real, lastDownPress: real} - val initial = {focus = LEFT_KEY, lastUpPress = 0.0, lastDownPress = 0.0} + val initial = + { focus = LEFT_KEY + , isSelected = false + , lastUpPress = 0.0 + , lastDownPress = 0.0 + } end diff --git a/fcore/options/options-update.sml b/fcore/options/options-update.sml index 12a0b76..884a7fb 100644 --- a/fcore/options/options-update.sml +++ b/fcore/options/options-update.sml @@ -1,95 +1,363 @@ +signature MAKE_UPDATE_SELECTED_KEY = +sig + val containsEscape: CoreKey.user_key * CoreKey.key_code list -> bool + val updateKeys: CoreKey.key_code * CoreKey.user_key -> CoreKey.user_key + val default: OptionsType.options_type * CoreKey.user_key -> GameType.game_type + val deselect: OptionsType.options_type * CoreKey.user_key + -> GameType.game_type +end + +functor MakeUpdateSelectedKey(Fn: MAKE_UPDATE_SELECTED_KEY) = +struct + fun setNewKeys (options, userKeys, time) = + let + val {focus, lastUpPress, lastDownPress, ...} = options + val options = + { focus = focus + , lastUpPress = time + , lastDownPress = time + , isSelected = false + } + in + {mode = GameType.OPTIONS options, userKeys = userKeys} + end + + fun onSelected (options, input: FrameInputType.t, userKeys, time) = + case #newKeys input of + key :: tl => + (* change key *) + if key = CoreKey.KEY_ESCAPE orelse Fn.containsEscape (userKeys, tl) then + (* deslect as that is the function of the escape key *) + Fn.deselect (options, userKeys) + (* what if new key collides with existing key? todo *) + else + let val userKeys = Fn.updateKeys (key, userKeys) + in setNewKeys (options, userKeys, time) + end + | [] => Fn.default (options, userKeys) +end + structure OptionsUpdate = struct open OptionsType fun default (options: OptionsType.options_type, userKeys) = let - val {focus, ...} = options + val {focus, isSelected, ...} = options (* `default` function is called when no keys are pressed * so set up pressed/down pressed both to 0 * as neither is being pressed. *) - val options = {focus = focus, lastUpPress = 0.0, lastDownPress = 0.0} + val options = + { focus = focus + , lastUpPress = 0.0 + , lastDownPress = 0.0 + , isSelected = isSelected + } in {mode = GameType.OPTIONS options, userKeys = userKeys} end - fun moveFocusUp (options, newFocus, userKeys, time) = + fun moveFocusUp (options: OptionsType.options_type, newFocus, userKeys, time) = let - val {focus, lastUpPress, ...} = options + val {focus, isSelected, lastUpPress, ...} = options (* only switch to newFocus if it is time for key delay to be triggered. * We set lastDownPress to 0 because up is currently being pressed instead * so we don't want to a key delay for down. *) val options = if lastUpPress + Constants.keyDelay <= time then - {focus = newFocus, lastUpPress = time, lastDownPress = 0.0} + { focus = newFocus + , lastUpPress = time + , lastDownPress = 0.0 + , isSelected = isSelected + } else - {focus = focus, lastUpPress = lastUpPress, lastDownPress = 0.0} + { focus = focus + , lastUpPress = lastUpPress + , lastDownPress = 0.0 + , isSelected = isSelected + } in {mode = GameType.OPTIONS options, userKeys = userKeys} end - fun moveFocusDown (options, newFocus, userKeys, time) = + fun moveFocusDown + (options: OptionsType.options_type, newFocus, userKeys, time) = let - val {focus, lastDownPress, ...} = options + val {focus, isSelected, lastDownPress, ...} = options val options = if lastDownPress + Constants.keyDelay <= time then - {focus = newFocus, lastUpPress = 0.0, lastDownPress = time} + { focus = newFocus + , lastUpPress = 0.0 + , lastDownPress = time + , isSelected = isSelected + } else - {focus = focus, lastUpPress = 0.0, lastDownPress = lastDownPress} + { focus = focus + , lastUpPress = 0.0 + , lastDownPress = lastDownPress + , isSelected = isSelected + } in {mode = GameType.OPTIONS options, userKeys = userKeys} end + fun select (options: OptionsType.options_type, userKeys) = + let + val {focus, lastUpPress, lastDownPress, ...} = options + val options = + { focus = focus + , lastUpPress = lastUpPress + , lastDownPress = lastDownPress + , isSelected = true + } + in + {mode = GameType.OPTIONS options, userKeys = userKeys} + end + + fun deselect (options: OptionsType.options_type, userKeys) = + let + val {focus, lastUpPress, lastDownPress, ...} = options + val options = + { focus = focus + , lastUpPress = lastUpPress + , lastDownPress = lastDownPress + , isSelected = false + } + in + {mode = GameType.OPTIONS options, userKeys = userKeys} + end + + fun withLeftKeys (newLeft, userKeys: CoreKey.user_key) = + let + val {right, up, down, jump, attack, escape, ...} = userKeys + in + { left = newLeft + , right = right + , up = up + , down = down + , jump = jump + , attack = attack + , escape = escape + } + end + + fun withRightKeys (newRight, userKeys: CoreKey.user_key) = + let + val {left, up, down, jump, attack, escape, ...} = userKeys + in + { left = left + , right = newRight + , up = up + , down = down + , jump = jump + , attack = attack + , escape = escape + } + end + + fun withUpKeys (newUp, userKeys: CoreKey.user_key) = + let + val {left, right, down, jump, attack, escape, ...} = userKeys + in + { left = left + , right = right + , up = newUp + , down = down + , jump = jump + , attack = attack + , escape = escape + } + end + + fun withDownKeys (newDown, userKeys: CoreKey.user_key) = + let + val {left, right, up, jump, attack, escape, ...} = userKeys + in + { left = left + , right = right + , up = up + , down = newDown + , jump = jump + , attack = attack + , escape = escape + } + end + + fun withJumpKeys (newJump, userKeys: CoreKey.user_key) = + let + val {left, right, up, down, attack, escape, ...} = userKeys + in + { left = left + , right = right + , up = up + , down = down + , jump = newJump + , attack = attack + , escape = escape + } + end + + fun withAttackKeys (newAttack, userKeys: CoreKey.user_key) = + let + val {left, right, up, down, jump, escape, ...} = userKeys + in + { left = left + , right = right + , up = up + , down = down + , jump = jump + , attack = newAttack + , escape = escape + } + end + + (* Sometimes we only want to act on a key's 'press' event, + * and the list only contains press events. *) + fun containsKey (searchKey, lst) = + case lst of + hd :: tl => hd = searchKey orelse containsKey (searchKey, tl) + | [] => false + + fun containsAttack (userKeys: CoreKey.user_key, input: FrameInputType.t) = + containsKey (#attack userKeys, #newKeys input) + + fun containsEscape (userKeys: CoreKey.user_key, tl) = + containsKey (#escape userKeys, tl) + + structure UpdateLeftKey = + MakeUpdateSelectedKey + (struct + val containsEscape = containsEscape + val updateKeys = withLeftKeys + val default = default + val deselect = deselect + end) + + structure UpdateRightKey = + MakeUpdateSelectedKey + (struct + val containsEscape = containsEscape + val updateKeys = withRightKeys + val default = default + val deselect = deselect + end) + + structure UpdateUpKey = + MakeUpdateSelectedKey + (struct + val containsEscape = containsEscape + val updateKeys = withUpKeys + val default = default + val deselect = deselect + end) + + structure UpdateDownKey = + MakeUpdateSelectedKey + (struct + val containsEscape = containsEscape + val updateKeys = withDownKeys + val default = default + val deselect = deselect + end) + + structure UpdateJumpKey = + MakeUpdateSelectedKey + (struct + val containsEscape = containsEscape + val updateKeys = withJumpKeys + val default = default + val deselect = deselect + end) + + structure UpdateAttackKey = + MakeUpdateSelectedKey + (struct + val containsEscape = containsEscape + val updateKeys = withAttackKeys + val default = default + val deselect = deselect + end) + fun update (options, input: FrameInputType.t, userKeys, time) = case #focus options of LEFT_KEY => - if #downHeld input then + if #isSelected options then + UpdateLeftKey.onSelected (options, input, userKeys, time) + else if containsAttack (userKeys, input) then + select (options, userKeys) + else if #downHeld input then moveFocusDown (options, RIGHT_KEY, userKeys, time) else default (options, userKeys) | RIGHT_KEY => - if #upHeld input then + if #isSelected options then + UpdateRightKey.onSelected (options, input, userKeys, time) + else if containsAttack (userKeys, input) then + select (options, userKeys) + else if #upHeld input then moveFocusUp (options, LEFT_KEY, userKeys, time) else if #downHeld input then moveFocusDown (options, UP_KEY, userKeys, time) else default (options, userKeys) | UP_KEY => - if #upHeld input then + if #isSelected options then + UpdateUpKey.onSelected (options, input, userKeys, time) + else if containsAttack (userKeys, input) then + select (options, userKeys) + else if #upHeld input then moveFocusUp (options, RIGHT_KEY, userKeys, time) else if #downHeld input then moveFocusDown (options, DOWN_KEY, userKeys, time) else default (options, userKeys) | DOWN_KEY => - if #upHeld input then + if #isSelected options then + UpdateDownKey.onSelected (options, input, userKeys, time) + else if containsAttack (userKeys, input) then + select (options, userKeys) + else if #upHeld input then moveFocusUp (options, UP_KEY, userKeys, time) else if #downHeld input then moveFocusDown (options, JUMP_KEY, userKeys, time) else default (options, userKeys) | JUMP_KEY => - if #upHeld input then + if #isSelected options then + UpdateJumpKey.onSelected (options, input, userKeys, time) + else if containsAttack (userKeys, input) then + select (options, userKeys) + else if #upHeld input then moveFocusUp (options, DOWN_KEY, userKeys, time) else if #downHeld input then moveFocusDown (options, ATTACK_KEY, userKeys, time) else default (options, userKeys) | ATTACK_KEY => - if #upHeld input then + if #isSelected options then + UpdateAttackKey.onSelected (options, input, userKeys, time) + else if containsAttack (userKeys, input) then + select (options, userKeys) + else if #upHeld input then moveFocusUp (options, JUMP_KEY, userKeys, time) else if #downHeld input then moveFocusDown (options, SAVE_BUTTON, userKeys, time) else default (options, userKeys) | SAVE_BUTTON => - if #upHeld input then + if containsAttack (userKeys, input) then + select (options, userKeys) + else if #upHeld input then moveFocusUp (options, ATTACK_KEY, userKeys, time) else if #downHeld input then moveFocusDown (options, CANCEL_BUTTON, userKeys, time) else default (options, userKeys) | CANCEL_BUTTON => - if #upHeld input then moveFocusUp (options, SAVE_BUTTON, userKeys, time) - else default (options, userKeys) + if containsAttack (userKeys, input) then + select (options, userKeys) + else if #upHeld input then + moveFocusUp (options, SAVE_BUTTON, userKeys, time) + else + default (options, userKeys) end diff --git a/fcore/options/options-vec.sml b/fcore/options/options-vec.sml index 1b5223c..e110f05 100644 --- a/fcore/options/options-vec.sml +++ b/fcore/options/options-vec.sml @@ -7,8 +7,13 @@ struct * the branch prediction cost *) fun drawLeftKey (options, width, height) = let - val acc = MakeTextVec.make - (155, 35, width, height, "Left key", 0.3, 0.3, 0.7, []) + val acc = + if #isSelected options then + MakeTextVec.make + (155, 35, width, height, "Left key", 0.7, 0.7, 0.3, []) + else + MakeTextVec.make + (155, 35, width, height, "Left key", 0.3, 0.3, 0.7, []) val acc = MakeTextVec.make (155, 95, width, height, "Right key", 0.0, 0.0, 0.0, acc) diff --git a/fcore/title/title-update.sml b/fcore/title/title-update.sml index eab0150..2f33745 100644 --- a/fcore/title/title-update.sml +++ b/fcore/title/title-update.sml @@ -7,7 +7,7 @@ struct START_BUTTON => let val mode = - if #attackHeld input orelse #jumpHeld input then + if #attackHeld input then GameType.LEVEL LevelType.initial else let @@ -23,7 +23,7 @@ struct | OPTIONS_BUTTON => let val mode = - if #attackHeld input orelse #jumpHeld input then + if #attackHeld input then (* placeholder: go to configure screen instead once that is implemented *) GameType.OPTIONS OptionsType.initial else diff --git a/shell/input-state.sml b/shell/input-state.sml index 67d6e92..dd07c66 100644 --- a/shell/input-state.sml +++ b/shell/input-state.sml @@ -51,7 +51,11 @@ struct case GlfwKeyMap.codeFromKey key of SOME code => let - val () = #newKeys state := code :: !(#newKeys state) + val () = + if action = Input.PRESS then + #newKeys state := code :: !(#newKeys state) + else + () val {left, right, down, up, attack, jump, escape} = !keyMappings val action = actionToBool action in