'OHRRPGCE - Definitions of some menus which are common to Custom + Game
'(C) Copyright 1997-2020 James Paige, Ralph Versteegen, and the OHRRPGCE Developers
'Dual licensed under the GNU GPL v2+ and MIT Licenses. Read LICENSE.txt for terms and disclaimer of liability.

#include "config.bi"
#include "allmodex.bi"
#include "udts.bi"
#include "uiconst.bi"
#include "common.bi"
#include "slices.bi"
#include "reload.bi"
#include "loading.bi"
#include "menus.bi"
#include "animations.bi"


'Globals

REDIM npc_movetypes(15) as string
npc_movetypes(0) = "Stand Still"
npc_movetypes(1) = "Wander"
npc_movetypes(2) = "Pace"
npc_movetypes(3) = "Right Turns"
npc_movetypes(4) = "Left Turns"
npc_movetypes(5) = "Random Turns"
npc_movetypes(6) = "Chase You (Meandering)"
npc_movetypes(7) = "Avoid You (Meandering)"
npc_movetypes(8) = "Walk In Place"
npc_movetypes(9) = "Chase You (Direct)"
npc_movetypes(10) = "Avoid You (Direct)"
npc_movetypes(11) = "Follow walls (Right)"
npc_movetypes(12) = "Follow walls (Left)"
npc_movetypes(13) = "Follow walls (R) stop for others"
npc_movetypes(14) = "Follow walls (L) stop for others"
npc_movetypes(15) = "Chase You (Pathfinding)"

REDIM npc_pushtypes(7) as string
npc_pushtypes(0) = "Off"
npc_pushtypes(1) = "Full"
npc_pushtypes(2) = "Vertical"
npc_pushtypes(3) = "Horizontal"
npc_pushtypes(4) = "Up only"
npc_pushtypes(5) = "Right Only"
npc_pushtypes(6) = "Down Only"
npc_pushtypes(7) = "Left Only"

REDIM npc_usetypes(2) as string
npc_usetypes(0) = "Use"
npc_usetypes(1) = "Touch"
npc_usetypes(2) = "Step On"

REDIM npc_facetypes(2) as string
npc_facetypes(0) = "Change Direction"
npc_facetypes(1) = "Face Player"
npc_facetypes(2) = "Do Not Face Player"


'==============================================================================

'This is a debug menu, which shows all bits (including hidden and unused) and their
'indices, in order of their index.  So it throws away the ordering in bitmenu(),
'and acts like editbitset with show_index=YES, show_all=YES.
'See editbitset for arg documentation.
FUNCTION edit_all_bits (array() as integer, wof as integer, bitmenu() as IntStrPair, numbits as integer, helpkey as string="editbitset", byref remem_pt as integer = -1, immediate_quit as bool = NO, title as string = "", prevmenu as string="Previous Menu") as EditBitsetResult

 REDIM fullbitmenu(numbits - 1) as IntStrPair
 FOR i as integer = 0 TO numbits - 1
  fullbitmenu(i).i = i
 NEXT

 FOR i as integer = 0 TO UBOUND(bitmenu)
  IF bitmenu(i).i >= 0 THEN
   fullbitmenu(bitmenu(i).i).s = bitmenu(i).s
  END IF
 NEXT

 RETURN editbitset(array(), wof, fullbitmenu(), helpkey, remem_pt, immediate_quit, title, prevmenu, YES, YES)
END FUNCTION

'Edit array of bits. The bits don't have to be consecutive, but they do have to be in ascending order.
'The bits corresponding to any blank entries in names(), or starting with '##' are hidden/skipped over,
'unless show_all is true.
'If a bit name starts with ! then the diplayed value of the bit is reversed.
'remem_pt is used to store the selected bit.
'If immediate_quit is true, then toggling a bit causes the menu to quit immediately (and return edbitPickedBit).
'Return value:
' edbitCancel    if user pressed ESC
' edbitBack      if used selected prevmenu (which is useful if prevmenu is something like "Done")
' edbitPickedBit if immediate_quit=YES and the user toggled a bit
FUNCTION editbitset (array() as integer, wof as integer, names() as string, helpkey as string="editbitset", byref remem_pt as integer = -1, immediate_quit as bool = NO, title as string = "", prevmenu as string="Previous Menu", show_index as bool = NO, show_all as bool = NO) as EditBitsetResult

 REDIM bitmenu(UBOUND(names)) as IntStrPair

 DIM nextbit as integer = 0
 FOR i as integer = 0 TO UBOUND(names)
  IF show_all ORELSE LEN(names(i)) THEN
   bitmenu(nextbit).s = names(i)
   bitmenu(nextbit).i = i
   nextbit += 1
  END IF
 NEXT
 IF nextbit THEN
  REDIM PRESERVE bitmenu(nextbit - 1)
 ELSE
  ERASE bitmenu
 END IF

 RETURN editbitset(array(), wof, bitmenu(), helpkey, remem_pt, immediate_quit, title, prevmenu, show_index, show_all)
END FUNCTION

'See above for documentation.
'This overload takes an array of bits to edit which allows bits be out of order,
'and to include unselectable section headings.
'The .i member of bits() is the bit number, which is -1 for unselectable menu items.
'This overload doesn't hide bits with blank names.  Bits with ## prefix are hidden
'unless show_hidden=YES.  If you want to see unused bits (like show_all=YES for the
'other editbitset overload), use edit_all_bits instead.
FUNCTION editbitset (array() as integer, wof as integer, bits() as IntStrPair, helpkey as string="editbitset", byref remem_pt as integer = -1, immediate_quit as bool = NO, title as string = "", prevmenu as string="Previous Menu", show_index as bool = NO, show_hidden as bool = NO) as EditBitsetResult

 DIM selectable(-1 TO UBOUND(bits)) as bool  'Oversized if there are any hidden bits
 selectable(-1) = YES

 'Build bitmenu(), a copy of bits(), with hidden items removed
 '(Note that bitmenu(-1) for prevmenu doesn't exist)
 DIM bitmenu() as IntStrPair
 FOR idx as integer = 0 TO UBOUND(bits)
  IF show_hidden ORELSE LEFT(bits(idx).s, 2) <> "##" THEN
   a_append bitmenu(), bits(idx).i, LTRIM(bits(idx).s, "#")
   selectable(UBOUND(bitmenu)) = (bits(idx).i >= 0)
  END IF
 NEXT

 DIM menupos as XYPair = XY(pMenuX, pMenuY)
 IF LEN(title) THEN menupos.y += 12

 DIM state as MenuState
 state.pt = remem_pt
 state.top = -1
 state.first = -1
 state.last = UBOUND(bitmenu)
 state.autosize = YES
 state.autosize_ignore_pixels = menupos.y + 2
 calc_menu_rect state, MenuOptions(), XY(0, 0)   'For autosize
 correct_menu_state state

 DIM ret as EditBitsetResult

 push_and_reset_gfxio_state
 DO
  setwait 55
  setkeys
  state.tog = state.tog XOR 1
  IF keyval(ccCancel) > 1 THEN ret = edbitCancelled : EXIT DO
  IF keyval(scF1) > 1 THEN show_help helpkey
  usemenu state, selectable()
  IF state.pt >= 0 ANDALSO selectable(state.pt) THEN
   DIM bitnum as integer = bitmenu(state.pt).i
   DIM inverted as integer = IIF(starts_with(bitmenu(state.pt).s, "!"), 1, 0)
   DIM newval as integer = -2
   IF keyval(scDelete) > 1 OR keyval(scBackspace) > 1 THEN newval = 0 XOR inverted
   IF keyval(ccLeft) > 1 OR keyval(scLeftCaret) > 1 THEN newval = 0 XOR inverted
   IF keyval(ccRight) > 1 OR keyval(scRightCaret) > 1 THEN newval = 1 XOR inverted
   IF enter_space_click(state) THEN newval = readbit(array(), wof, bitnum) XOR 1

   IF newval <> -2 THEN
    setbit array(), wof, bitnum, newval
    IF immediate_quit THEN ret = edbitPickedBit : EXIT DO
   END IF
  ELSE
   IF enter_space_click(state) THEN ret = edbitBack : EXIT DO
  END IF

  ' Draw
  clearpage dpage
  calc_menu_rect state, MenuOptions(), menupos  ' Recalcs .size, .rect, .spacing
  draw_fullscreen_scrollbar state, , dpage
  IF LEN(title) THEN edgeprint title, pCentered, menupos.y - 12, uilook(uiMenuItem), dpage
  FOR i as integer = state.top TO small(state.top + state.size, state.last)
   DIM drawat as XYPair = menupos
   IF i > -1 THEN  'Don't indent prevmenu line
    drawat.x += 8 + IIF(state.pt = i, showRight, 0)
   END IF
   drawat.y += (i - state.top) * state.spacing
   DIM biton as integer
   DIM col as integer
   DIM text as string = IIF(i = -1, prevmenu, bitmenu(i).s)
   IF i >= 0 ANDALSO selectable(i) THEN
    biton = readbit(array(), wof, bitmenu(i).i)
    IF LEFT(text, 1) = "!" THEN  'Inverted display
     biton XOR= 1
     text = MID(text, 2)
    END IF
    IF show_index THEN text = strprintf("%2d ", bitmenu(i).i) & text
    ellipse vpages(dpage), menupos.x + 4, drawat.y + 3, 3, uilook(uiDisabledItem), IIF(biton, uilook(uiSelectedItem), -1)
   ELSE
    biton = 1  'Don't show as disabled
    IF i > -1 THEN col = uilook(eduiHeading) 'Section heading: override text color
   END IF
   col = menu_item_color(state, i, biton = 0, selectable(i) = NO, col)
   textcolor col, 0
   printstr text, drawat.x, drawat.y, dpage
  NEXT i
  SWAP vpage, dpage
  setvispage vpage
  dowait
 LOOP
 pop_gfxio_state
 remem_pt = state.pt
 RETURN ret
END FUNCTION

'This is a wrapper around editbitset, but bools instead of packed bits.
FUNCTION editbools(bools() as bool, names() as string, helpkey as string = "editbitset", byref remem_pt as integer = -2, immediate_quit as bool = NO, title as string = "", prevmenu as string="Previous Menu") as EditBitsetResult
 DIM bitarray(0 TO UBOUND(bools) \ 16) as integer
 FOR i as integer = 0 TO UBOUND(bools)
  setbit bitarray(), 0, i, bools(i)
 NEXT

 DIM ret as EditBitsetResult
 ret = editbitset(bitarray(), 0, names(), helpkey, remem_pt, immediate_quit, title, prevmenu)

 FOR i as integer = 0 TO UBOUND(bools)
  bools(i) = xreadbit(bitarray(), i)
 NEXT
 RETURN ret
END FUNCTION

SUB gather_gen_bits(bittemp() as integer)
 bittemp(0) = gen(genBits)
 bittemp(1) = gen(genBits2)
 bittemp(2) = gen(genBits2+1)
 FOR i as integer = 0 TO 3
  bittemp(3 + i) = gen(genBits3 + i)
 NEXT
END SUB

SUB scatter_gen_bits(bittemp() as integer)
 gen(genBits) = bittemp(0)
 gen(genBits2) = bittemp(1)
 gen(genBits2+1) = bittemp(2)
 FOR i as integer = 0 TO 3
  gen(genBits3 + i) = bittemp(3 + i)
 NEXT
END SUB

SUB edit_global_bitsets OVERLOAD (bitname() as string, helpfile as string)
 DIM bittemp(6) as integer
 gather_gen_bits bittemp()
 editbitset bittemp(), 0, bitname(), helpfile
 scatter_gen_bits bittemp()
END SUB

SUB edit_global_bitsets OVERLOAD (bits() as IntStrPair, helpfile as string)
 DIM bittemp(6) as integer
 gather_gen_bits bittemp()
 editbitset bittemp(), 0, bits(), helpfile
 scatter_gen_bits bittemp()
END SUB

SUB edit_general_bitsets()
 REDIM bits() as IntStrPair
 a_append bits(), -1, ""
 a_append bits(), -1, " Hero Experience, Levels and Stats"
 a_append bits(), 2,  "!Restore HP on levelup"
 a_append bits(), 3,  "!Restore MP on levelup"
 a_append bits(), 4,  "!Inns revive dead heroes"
 a_append bits(), 31, "Don't reset max stats after OOB attack"
 a_append bits(), 30, "!Divide experience between heroes"
 a_append bits(), 19, "Dead heroes gain share of experience"
 a_append bits(), 43, "Cap minimum stats at zero"
 a_append bits(), 46, "!Negative-damage harmtiles can cure above max HP"
 a_append bits(), 29, "Ignore extra Hits stat"
 a_append bits(), 52, "Ignore MP~ stat"

 a_append bits(), -1, ""
 a_append bits(), -1, " Hero Party"
 a_append bits(), 1,  "Enable caterpillar party"
 a_append bits(), 41, "Keep caterpillar length the same when speed changes"
 a_append bits(), 42, "Heroes use Walk in Place animation while idle"
 a_append bits(), 5,  "Hero swapping always available"
 a_append bits(), 20, "Locked heroes can't be re-ordered"

 a_append bits(), -1, ""
 a_append bits(), -1, " Starting or Loading Games"
 a_append bits(), 11, "Skip title screen"
 a_append bits(), 12, "Skip load screen"
 a_append bits(), 40, "!Stop music before starting/loading game"
 a_append bits(), 44, "Hide empty save slots at the bottom of the save/load menus"

 a_append bits(), -1, ""
 a_append bits(), -1, " Misc"
 a_append bits(), 8,  "!Enable debugging keys"
 a_append bits(), 10, "Permit double-triggering of scripts"
 a_append bits(), 18, "!Save gameover/loadgame script IDs"
 a_append bits(), 15, "Default passability disabled by default"
 a_append bits(), 47, "!Map joystick controls to keyboard keys for scripts"
 a_append bits(), 53, "!Map joystick (left) stick to dpad"

 edit_global_bitsets bits(), "general_game_bitsets"
END SUB

SUB edit_backcompat_bitsets()
 DIM bitname(111) as string
 bitname(9) = "Simulate old levelup bonus-accretion bug"
 bitname(16) = "Simulate pushable NPC obstruction bug"
 bitname(24) = "!Disable better scancodes for scripts"
 bitname(25) = "Simulate old fail vs element resist bit"
 bitname(27) = "!Don't recreate map slices when changing maps"
 bitname(28) = "!Harm tiles ignore non-caterpillar heroes"
 bitname(32) = "!Limit maximum tags to 999"
 bitname(33) = "Simulate Bug #430 script wait skips"
 bitname(34) = "!showtextbox is delayed"
 bitname(36) = "Old attack positioning at bottom-left of target"
 bitname(37) = "Wrap map layers over edge of Crop maps"
 bitname(39) = "Draw Backdrop slice above Script Layer"
 IF gen(genBattleMode) = 1 THEN  'turn-based
  bitname(50) = "!Non-turn attack delays can also cause turn delays"
 END IF
 bitname(54) = "!Disable cost checking for autotarget spells in random spell lists"
 bitname(57) = "Use old direction key tiebreaking"
 bitname(58) = "Ignore key repeats for confirm, cancel, and menu"
 bitname(59) = "Always allow key repeat for confirm in battles"
 bitname(61) = "!Maintain backcompat for old walktall script"
 edit_global_bitsets bitname(), "share_general_game_backcompat_bitsets"
END SUB

'Battle System bitsets
SUB edit_battle_bitsets()
 REDIM bits() as IntStrPair
 a_append bits(), -1, ""
 a_append bits(), -1, " Battle Display Options"
 a_append bits(), 6,  "!Show ready-meter"
 a_append bits(), 7,  "!Show health meter"
 a_append bits(), 49, "Show MP meter"
 a_append bits(), 14, "Disable hero cursor"
 a_append bits(), 38, "Never show script timers during battles"
 IF gen(genResolutionX) > 320 ORELSE gen(genResolutionY) > 200 THEN
  a_append bits(), 56, "!Battles display at 320x200"
 END IF
 a_append bits(), 60, "Hide battle menu while targeting attacks"

 a_append bits(), -1, ""
 a_append bits(), -1, " General Options"
 a_append bits(), 26, "0 damage when immune to attack elements"
 a_append bits(), 29, "Ignore extra Hits stat"
 a_append bits(), 52, "Ignore MP~ stat"
 a_append bits(), 55, "Turncoat & defector always treat self as an ally"

 a_append bits(), -1, ""
 IF gen(genBattleMode) = 0 THEN
  a_append bits(), -1, " Active-Time Battle Options"
  IF get_gen_bool("/mouse/mouse_battles") THEN
   a_append bits(), 17, "!Hold ESC key or right-click to run from battle"
  ELSE
   a_append bits(), 17, "!Hold ESC key to run from battle"
  END IF
  a_append bits(), 13, "Pause on all battle menus & targeting"
  a_append bits(), 0,  "Pause on Spells & Items menus"
  a_append bits(), 35, "Pause when targeting attacks"
  a_append bits(), 23, "Battle menus wait for attack animations"
  a_append bits(), 21, "Attack captions pause ready meters"
  a_append bits(), 22, "!Randomize initial ready meters"
 ELSE
  a_append bits(), -1, " Turn-based Battle Options"
  a_append bits(), 48, "!Press ESC to cancel/change a hero's attack"
  a_append bits(), 51, "Don't break Speed ties randomly"
 END IF

 edit_global_bitsets bits(), "general_battle_bitsets"
END SUB

SUB edit_mouse_options ()

 DIM menu as MenuDef
 
 DIM st as MenuState
 st.active = YES
 st.need_update = YES

 DIM t as integer
 DIM do_toggle as bool = NO
 REDIM enabled(0) as bool

 push_and_reset_gfxio_state
 setkeys YES
 DO
  setwait 55
  setkeys YES
  IF keyval(scF1) > 1 THEN show_help "edit_mouse_options"
  IF keyval(ccCancel) > 1 THEN EXIT DO

  IF st.need_update THEN
   st.need_update = NO
   InitLikeStandardMenu menu
   append_menu_item menu, "Previous menu..."
   menu.last->t = 0
   append_menu_item menu, "Click on map to move the hero: " & yesorno(get_gen_bool("/mouse/move_hero"))
   menu.last->t = 1
   IF get_gen_bool("/mouse/move_hero") THEN
    append_menu_item menu, " Display destination: " & yesorno(get_gen_bool("/mouse/move_hero/display_dest"))
    menu.last->t = 2
    append_menu_item menu, " Cancel move on battles: " & yesorno(get_gen_bool("/mouse/move_hero/cancel_on_battle"))
    menu.last->t = 3
    append_menu_item menu, " Cancel move on textboxes: " & yesorno(get_gen_bool("/mouse/move_hero/cancel_on_textbox"))
    menu.last->t = 4
    append_menu_item menu, " Cancel move on menus: " & yesorno(get_gen_bool("/mouse/move_hero/cancel_on_menu"))
    menu.last->t = 5
    append_menu_item menu, " Max tiles to walk: " & zero_default(get_gen_int("/mouse/move_hero/max_path_length"), "0 (no limit)")
    menu.last->t = 6
   END IF
   append_menu_item menu, "Show cursor even in full-screen: " & yesorno(get_gen_bool("/mouse/show_cursor"))
   menu.last->t = 9
   append_menu_item menu, "Open main menu on right-click: " & yesorno(get_gen_bool("/mouse/menu_right_click"))
   menu.last->t = 10
   append_menu_item menu, "Click to advance text boxes: " & yesorno(get_gen_bool("/mouse/click_textboxes"))
   menu.last->t = 20
   append_menu_item menu, """any key"", etc, include mouse: " & yesorno(get_gen_bool("/mouse/click_keys"))
   menu.last->t = 13
   append_menu_item menu, "Mouse support on menus: " & yesorno(get_gen_bool("/mouse/mouse_menus"))
   menu.last->t = 11
   append_menu_item menu, "Mouse support in battles: " & yesorno(get_gen_bool("/mouse/mouse_battles"))
   menu.last->t = 12

   init_menu_state st, menu
  END IF

  usemenu st, menu

  t = menu.items[st.pt]->t

  do_toggle = NO
  
  IF enter_space_click(st) THEN
   SELECT CASE t
    CASE 0: EXIT DO
    CASE 6:
    CASE ELSE
     do_toggle = YES
   END SELECT
  END IF

  SELECT CASE t
   CASE 0: 'exit
   CASE 6:
    IF gen_intgrabber("/mouse/move_hero/max_path_length") THEN st.need_update = YES
   CASE ELSE
    IF keyval(ccLeft) > 1 ORELSE keyval(ccRight) > 1 THEN
     do_toggle = YES
    END IF
  END SELECT
  
  IF do_toggle THEN
   SELECT CASE t
    CASE 1:
     toggle_gen_bool("/mouse/move_hero")
     st.need_update = YES
    CASE 2:
     toggle_gen_bool("/mouse/move_hero/display_dest")
     st.need_update = YES
    CASE 3:
     toggle_gen_bool("/mouse/move_hero/cancel_on_battle")
     st.need_update = YES
    CASE 4:
     toggle_gen_bool("/mouse/move_hero/cancel_on_textbox")
     st.need_update = YES
    CASE 5:
     toggle_gen_bool("/mouse/move_hero/cancel_on_menu")
     st.need_update = YES
    CASE 9:
     toggle_gen_bool("/mouse/show_cursor")
     st.need_update = YES
    CASE 10:
     toggle_gen_bool("/mouse/menu_right_click")
     st.need_update = YES
    CASE 11:
     toggle_gen_bool("/mouse/mouse_menus")
     st.need_update = YES
    CASE 12:
     toggle_gen_bool("/mouse/mouse_battles")
     st.need_update = YES
    CASE 13:
     toggle_gen_bool("/mouse/click_keys")
     st.need_update = YES
    CASE 20:
     toggle_gen_bool("/mouse/click_textboxes")
     st.need_update = YES
   END SELECT
  END IF
  
  clearpage dpage
  
  draw_menu menu, st, dpage
  SWAP vpage, dpage
  setvispage vpage
  
  dowait
 LOOP
 pop_gfxio_state

 #IFNDEF IS_GAME
  ' Don't write changes, in case we're live-previewing or playing an .rpgdir
  write_general_reld()
 #ENDIF
END SUB


'============================ Animation/Variant name picker ==============================


'Picker for an item from builtin_animations() or builtin_anim_variants()
type AnimationNamePicker extends ModularMenu
  name_indices(any) as integer  'Map from menu cursor position to names_list() index
  title as string         'At top of menu
  selected_pt as integer  'Selected state.pt or -1 if cancelled
  names_list as AnimVariantInfo ptr
  anim_context as AnimationContext
  add_none as bool

  declare constructor(names_list as AnimVariantInfo ptr, anim_context as AnimationContext, add_none as bool = NO)

  declare function selected_index() as integer
  declare function selected_name() as string

  declare sub update()
  declare function each_tick() as bool
  declare sub draw_underlays()
end type

' (names_list is a static array, so passing it as a dynamic array is far more dangerous than passing by ptr)
constructor AnimationNamePicker(names_list as AnimVariantInfo ptr, anim_context as AnimationContext, add_none as bool = NO)
  this.floating = YES
  this.menuopts.edged = YES
  this.names_list = names_list
  this.anim_context = anim_context
  this.add_none = add_none
end constructor

sub AnimationNamePicker.update()
  redim menu(0)
  redim name_indices(0)

  menu(0) = "Cancel"
  name_indices(0) = -1

  if add_none then
   a_append menu(), "None"
   a_append name_indices(), -2
  end if

  dim idx as integer = 0
  do
    with names_list[idx]
      if .name = NULL then exit do
      ' This name can be used in this context if the sprite type's context is a subset of the suitable ones
      if (.context and anim_context) <> 0 then
        a_append menu(), *.name
        a_append name_indices(), idx
      end if
    end with
    idx += 1
  loop
  state.pt = 1
  state.last = UBOUND(menu)
  selected_pt = -1
end sub

' Returns -1 if cancelled, -2 for added None option
function AnimationNamePicker.selected_index() as integer
  if selected_pt < 0 then return -1
  return name_indices(selected_pt)
end function

function AnimationNamePicker.selected_name() as string
  if selected_pt < 0 then return ""
  var idx = name_indices(selected_pt)
  if idx = -2 then return "(none)"
  return *names_list[idx].name
end function

function AnimationNamePicker.each_tick() as bool
  if enter_space_click(state) then
    selected_pt = state.pt
    return YES
  end if
end function

sub AnimationNamePicker.draw_underlays()
  fuzzyrect 0, 0, , , findrgb(32, 32, 100), vpage, 50, , 1
  edgeprint title, pCentered, pTop + 10, uilook(uiText), vpage
  if name_indices(state.pt) >= 0 then
    wrapprint *names_list[name_indices(state.pt)].description, pInfoX, pInfoY, uilook(uiText), vpage
  end if
end sub

' Prompt user for animation name and variant to add and add it
function prompt_from_namelist(title as string, names_list as AnimVariantInfo ptr, helpkey as string, anim_context as AnimationContext, add_none as bool = NO) as string
  var picker = AnimationNamePicker(names_list, anim_context, add_none)
  picker.title = title
  picker.helpkey = helpkey

  'picker.set_names(names_list, anim_context)
  picker.run()
  return picker.selected_name()
end function

' Returns "" if cancelled, and if add_none, adds None option that returns "(none)"
function prompt_animation_name(title as string, anim_context as AnimationContext, add_none as bool = NO) as string
  return prompt_from_namelist(title, @builtin_animations(0), "pick_animation_name", anim_context, add_none)
end function

' Returns "" if cancelled, "(none)" for a blank variant
function prompt_variant_name(title as string, anim_context as AnimationContext) as string
  return prompt_from_namelist(title, @builtin_anim_variants(0), "pick_variant_name", anim_context)
end function