name_menu.lua 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. if UiContainer == nil then UiContainer = {} end
  2. --- The name menu.
  3. --- Is a key one that types a character in the name menu? The letters (their name is one character)
  4. -- and the space bar.
  5. local function is_typing_key(key)
  6. return key ~= nil and (key == "Space" or #key == 1)
  7. end
  8. UiContainer.NameMenu = {
  9. --- Indicates if a chocobo is being renamed instead of a character.
  10. chocobo = false,
  11. --- ID of the character or chocobo being renamed.
  12. id = -1,
  13. --- Default name.
  14. default_name = "",
  15. --- Is the name menu shown when the game asks to name a character?
  16. --
  17. -- TODO: Turn it on when the menu works with the controller settings (see is_typing_key). While
  18. -- it is off, the characters keep their default names and the game goes on.
  19. enabled = false,
  20. --- Currently input-ed name.
  21. name = "",
  22. --- Max number of characters allowed in a name.
  23. max_characters = 12,
  24. --- Current text cursor
  25. text_cursor = 1,
  26. --- Accepted characters in a name.
  27. letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ ",
  28. --- Current options cursor position.
  29. options_position = 1,
  30. --- Total options cursor position.
  31. options_position_total = 3,
  32. --- Current confirm dialog cursor position.
  33. confirm_position = 1,
  34. --- Total confirm dialog cursor position.
  35. confirm_position_total = 2,
  36. --- Run when the menu is creaded.
  37. --
  38. -- It does nothing.
  39. on_start = function(self)
  40. return 0
  41. end,
  42. --- Handles button events.
  43. --
  44. -- For the current submenu, handles the directions, OK, and the keys that type letters.
  45. -- @param button Game event (the directions, OK...) or, for the letters and the space bar,
  46. -- the name of the key.
  47. -- @param event Trigger event. Normally, "Press".
  48. -- @param source_key Name of the key that produced the event.
  49. on_button = function(self, button, event, source_key)
  50. if UiContainer.current_menu == "name" then
  51. -- A letter or the space bar that is also the OK key types, it doesn't confirm.
  52. if button == Config.ACTIONS.CONFIRM and is_typing_key(source_key) and UiContainer.current_submenu == "" then
  53. return 0
  54. end
  55. if UiContainer.current_submenu == "confirm" then
  56. if button == Config.CONTROLS.RIGHT then
  57. audio_manager:play_sound("Cursor")
  58. self.confirm_position = self.confirm_position + 1
  59. if self.confirm_position > self.confirm_position_total then
  60. self.confirm_position = 1
  61. end
  62. ui_manager:get_widget("NameMenu.Container.Confirm.Cursor"):set_default_animation("Position" .. self.confirm_position)
  63. elseif button == Config.CONTROLS.LEFT then
  64. audio_manager:play_sound("Cursor")
  65. self.confirm_position = self.confirm_position - 1
  66. if self.confirm_position < 1 then
  67. self.confirm_position = self.confirm_position_total
  68. end
  69. ui_manager:get_widget("NameMenu.Container.Confirm.Cursor"):set_default_animation("Position" .. self.confirm_position)
  70. elseif button == Config.ACTIONS.CONFIRM then
  71. audio_manager:play_sound("Cursor")
  72. if self.confirm_position == 2 then -- Back
  73. UiContainer.current_submenu = ""
  74. ui_manager:get_widget("NameMenu.Container.Confirm"):set_visible(false)
  75. elseif self.confirm_position == 1 then
  76. ui_manager:get_widget("NameMenu.Container.Confirm"):set_visible(false)
  77. self.apply_name(self);
  78. end
  79. end
  80. elseif UiContainer.current_submenu == "" then
  81. -- Before handling buttons, get keycode, for letters and space.
  82. local character_index
  83. if button == "Space" then
  84. character_index = string.find(self.letters, " ")
  85. elseif is_typing_key(button) then
  86. character_index = string.find(self.letters, button)
  87. end
  88. -- Handle buttons.
  89. if button == Config.CONTROLS.DOWN then
  90. audio_manager:play_sound("Cursor")
  91. self.options_position = self.options_position + 1
  92. if self.options_position > self.options_position_total then
  93. self.options_position = 1
  94. end
  95. ui_manager:get_widget("NameMenu.Container.Options.Cursor"):set_default_animation("Position" .. self.options_position)
  96. elseif button == Config.CONTROLS.UP then
  97. audio_manager:play_sound("Cursor")
  98. self.options_position = self.options_position - 1
  99. if self.options_position < 1 then
  100. self.options_position = self.options_position_total
  101. end
  102. ui_manager:get_widget("NameMenu.Container.Options.Cursor"):set_default_animation("Position" .. self.options_position)
  103. elseif button == Config.CONTROLS.LEFT then
  104. if self.text_cursor > 1 then
  105. self.text_cursor = self.text_cursor - 1
  106. self.draw_cursor(self)
  107. audio_manager:play_sound("Cursor")
  108. else
  109. audio_manager:play_sound("Error")
  110. end
  111. elseif button == Config.CONTROLS.RIGHT then
  112. if self.text_cursor < self.max_characters then
  113. audio_manager:play_sound("Cursor")
  114. self.text_cursor = self.text_cursor + 1
  115. self.draw_cursor(self)
  116. else
  117. audio_manager:play_sound("Error")
  118. end
  119. elseif button == Config.ACTIONS.CONFIRM then
  120. if self.options_position == 1 then -- Clear name.
  121. audio_manager:play_sound("Cursor")
  122. self.name = ""
  123. self.text_cursor = 1
  124. self.populate_name(self)
  125. self.draw_cursor(self)
  126. elseif self.options_position == 2 then -- Default name.
  127. audio_manager:play_sound("Cursor")
  128. self.name = self.default_name
  129. self.text_cursor = math.min(#(self.name) + 1, self.max_characters)
  130. self.populate_name(self)
  131. self.draw_cursor(self)
  132. elseif self.options_position == 3 then -- Show confirm dialog.
  133. self.format_name(self)
  134. self.populate_name(self)
  135. self.draw_cursor(self)
  136. if #(self.name) > 0 then
  137. audio_manager:play_sound("Cursor")
  138. UiContainer.current_submenu = "confirm"
  139. local name_display = self.name -- Pad with spaces, to display only.
  140. while #(name_display) < self.max_characters do
  141. name_display = " " .. name_display
  142. end
  143. ui_manager:get_widget("NameMenu.Container.Confirm.Name"):set_text(name_display)
  144. ui_manager:get_widget("NameMenu.Container.Confirm"):set_visible(true)
  145. self.confirm_position = 1
  146. ui_manager:get_widget("NameMenu.Container.Options.Cursor"):set_default_animation("Position" .. self.options_position)
  147. else
  148. audio_manager:play_sound("Error")
  149. end
  150. end
  151. -- TODO: Implement backspace, but how?
  152. -- Letters and spaces
  153. elseif character_index ~= nil then
  154. audio_manager:play_sound("Cursor")
  155. self.set_char(self, character_index)
  156. self.text_cursor = math.min(self.text_cursor + 1, self.max_characters)
  157. self.populate_name(self)
  158. self.draw_cursor(self)
  159. end
  160. end
  161. end
  162. return 0
  163. end,
  164. --- Gets the default name of a character.
  165. --
  166. -- @param id ID of the character.
  167. get_default_name = function(self, id)
  168. local name = Characters[id].name
  169. -- HACK: Hack for Ex-Soldier default. Can this be extracted from menu.lgp?
  170. if string.lower(name) == "ex-soldier" then return "Cloud" end
  171. return name
  172. end,
  173. --- Gives a character its default name, without showing the menu.
  174. --
  175. -- Used when the menu is not enabled.
  176. --
  177. -- @param id ID of the character.
  178. apply_default_name = function(self, id)
  179. Characters[id].name = self:get_default_name(id)
  180. export_character_names()
  181. end,
  182. --- Opens the name menu.
  183. --
  184. -- Populates and updates displayed data before opening. It also pauses the game. Shows a fade
  185. -- effect.
  186. show = function(self)
  187. entity_manager:set_paused(true)
  188. MenuSettings.pause_available = false
  189. script:request_end_sync(Script.UI, "Fade", "fade_out", 0)
  190. ui_manager:get_widget("NameMenu"):set_visible(true)
  191. UiContainer.current_menu = "name"
  192. UiContainer.current_submenu = ""
  193. self.options_position = 1
  194. self.confirm_position = 1
  195. ui_manager:get_widget("NameMenu.Container.Confirm.Cursor"):set_default_animation("Position" .. self.confirm_position)
  196. ui_manager:get_widget("NameMenu.Container.Options.Cursor"):set_default_animation("Position" .. self.options_position)
  197. ui_manager:get_widget("NameMenu.Container.Character.Portrait"):set_image("characters/" .. tostring(self.id) .. ".png")
  198. self.default_name = self:get_default_name(self.id)
  199. self.name = self.default_name
  200. self.text_cursor = math.min(#(self.name) + 1, self.max_characters)
  201. self.populate_name(self)
  202. self.draw_cursor(self)
  203. script:request_end_sync(Script.UI, "Fade", "fade_in", 0)
  204. return 0;
  205. end,
  206. --- Redraws the text cursor, indicating the current character.
  207. draw_cursor = function(self)
  208. for i = 1, self.max_characters do
  209. if self.text_cursor == i then
  210. ui_manager:get_widget("NameMenu.Container.Character.Name.Score" .. tostring(i)):set_height(1.8)
  211. else
  212. ui_manager:get_widget("NameMenu.Container.Character.Name.Score" .. tostring(i)):set_height(0.3)
  213. end
  214. end
  215. end,
  216. --- Writes the name characters in the name input line.
  217. populate_name = function(self)
  218. for i = 1, self.max_characters do
  219. if #(self.name) < i then
  220. ui_manager:get_widget("NameMenu.Container.Character.Name.Char" .. tostring(i)):set_text("")
  221. else
  222. ui_manager:get_widget("NameMenu.Container.Character.Name.Char" .. tostring(i)):set_text(self.name:sub(i, i))
  223. end
  224. end
  225. end,
  226. --- Sets a character in the current cursor position.
  227. set_char = function(self, character_index)
  228. local temp_name = ""
  229. for i = 1, self.max_characters do
  230. if i == self.text_cursor then
  231. -- First position or after space: Use upper case.
  232. if i == 1 or temp_name:sub(i - 1, i - 1) == " " then
  233. temp_name = temp_name .. self.letters:sub(character_index, character_index)
  234. else
  235. temp_name = temp_name .. string.lower(self.letters:sub(character_index, character_index))
  236. end
  237. elseif #(self.name) >= i then
  238. if i == 1 or temp_name:sub(i - 1, i - 1) == " " then
  239. temp_name = temp_name .. string.upper(self.name:sub(i, i))
  240. else
  241. temp_name = temp_name .. string.lower(self.name:sub(i, i))
  242. end
  243. else
  244. temp_name = temp_name .. " "
  245. end
  246. end
  247. self.name = temp_name
  248. end,
  249. --- Formats the current name.
  250. --
  251. -- Removes leading, trailing and double spaces, trims the length, and ensures capitalization.
  252. format_name = function(self)
  253. local temp_name = self.name
  254. -- Trailing and leading spaces: Remove.
  255. temp_name = string.gsub(temp_name, '^%s*(.-)%s*$', '%1')
  256. -- Double (or more) spaces: One space.
  257. temp_name = temp_name:gsub("%s+", " ")
  258. -- Length. Trim to self.max_characters
  259. if #(temp_name) > self.max_characters then
  260. temp_name = temp_name:sub(1, self.max_characters)
  261. end
  262. -- Capitalization
  263. self.name = ""
  264. for i = 1, #(temp_name) do
  265. if i == 1 or temp_name:sub(i - 1, i - 1) == " " then
  266. self.name = self.name .. string.upper(temp_name:sub(i, i))
  267. else
  268. self.name = self.name .. string.lower(temp_name:sub(i, i))
  269. end
  270. end
  271. end,
  272. apply_name = function(self)
  273. if self.chocobo == false then
  274. Characters[self.id].name = self.name
  275. export_character_names()
  276. else
  277. -- TODO: Do chocobo.
  278. end
  279. self.hide(self)
  280. end,
  281. --- Hides the item menu and goes back to the main menu.
  282. --
  283. -- It also unpauses the game. Shows a fade effect.
  284. hide = function(self)
  285. script:request_end_sync(Script.UI, "Fade", "fade_out", 0)
  286. ui_manager:get_widget("NameMenu"):set_visible(false)
  287. UiContainer.current_menu = ""
  288. UiContainer.current_submenu = ""
  289. MenuSettings.pause_available = true
  290. entity_manager:set_paused(false)
  291. script:request_end_sync(Script.UI, "Fade", "fade_in", 0)
  292. return 0;
  293. end,
  294. }