field.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. -- When V-Gears is run, export character names.
  2. export_character_names()
  3. --- Helper function that changes field maps through fade in and fadeout
  4. --
  5. -- @param map_name The target map name.
  6. -- @param point_name Spawn point name in the target map for the PC.
  7. load_field_map_request = function(map_name, point_name)
  8. if type(map_name) == "string" and map_name ~= "" then
  9. System["MapChanger"].map_name = map_name
  10. System["MapChanger"].point_name = point_name
  11. script:request(Script.SYSTEM, "MapChanger", "ffvii_field", 0)
  12. end
  13. end
  14. dispatch_menu = function(menu_id, parameter)
  15. menu_id = tonumber(menu_id)
  16. parameter = tonumber(parameter)
  17. if not menu_id or not parameter then
  18. return
  19. end
  20. if menu_id == 5 then
  21. ending_credits()
  22. elseif menu_id == 6 then
  23. if parameter == 64 then
  24. name_chocobo()
  25. else
  26. name_character(parameter)
  27. end
  28. elseif menu_id == 7 then
  29. if parameter == 1 then
  30. Party.split_teams(3)
  31. elseif parameter == 2 then
  32. Party.split_teams(2)
  33. else
  34. Party.choose_party()
  35. end
  36. elseif menu_id == 8 then
  37. open_shop(parameter)
  38. elseif menu_id == 9 then
  39. open_menu("party")
  40. elseif menu_id == 14 then
  41. open_menu("save")
  42. elseif menu_id == 15 then
  43. Party.steal_materia()
  44. elseif menu_id == 18 then
  45. Characters.remove_materia(parameter)
  46. elseif menu_id == 19 then
  47. Characters.restore_materia(parameter)
  48. end
  49. end
  50. --- Assigns an entity to a playable character.
  51. --
  52. -- @param entity_name The entity name.
  53. -- @param character_id Id of the character to assign.
  54. set_entity_to_character = function(entity_name, character_id)
  55. if character_id == Party[1] and entity_name ~= "" then
  56. if System.MapChanger.point_name ~= "" then
  57. local point = entity_manager:get_entity_point(System.MapChanger.point_name)
  58. if point ~= nil then
  59. local x, y, z = point:get_position()
  60. local rotation = point:get_rotation()
  61. local player = entity_manager:get_entity(entity_name)
  62. player:set_position(x, y, z)
  63. player:set_rotation(rotation)
  64. player:set_solid(true)
  65. player:set_visible(true)
  66. end
  67. end
  68. entity_manager:set_player_entity(entity_name)
  69. local world_map_entry = System.MapChanger.world_map_entry
  70. if world_map_entry then
  71. entity_manager:place_player_at_field_entry(
  72. world_map_entry.x, world_map_entry.y,
  73. world_map_entry.triangle, world_map_entry.direction
  74. )
  75. System.MapChanger.world_map_entry = nil
  76. end
  77. end
  78. entity_manager:set_entity_to_character(entity_name, character_id)
  79. end
  80. --- Joins the party members in the field.
  81. --
  82. -- @param speed Speed at which the characters join
  83. join_party = function(speed)
  84. local player = entity_manager:get_player_entity()
  85. if (player == nil) then
  86. do return end
  87. end
  88. local x, y, z = player:get_position()
  89. for c = 2, 3 do
  90. if Party[c] ~= nil then
  91. local character = entity_manager:get_entity_from_character_id(Party[c])
  92. --print(" Name: " .. character:get_name())
  93. if character ~= nil then
  94. local cur_speed = character:get_move_auto_speed()
  95. character:set_solid(false)
  96. -- Approximated speed, good enough for now.
  97. -- TODO: Calculate speed based on time, as the orignal opcode.
  98. character:set_move_auto_speed(speed / 16)
  99. character:move_to_position(x, y)
  100. character:move_sync()
  101. character:set_move_auto_speed(cur_speed)
  102. character:set_visible(false)
  103. character:set_talkable(false)
  104. end
  105. end
  106. end
  107. end
  108. --- Splits the party members in the field.
  109. --
  110. -- @param ax_addr Bank for ax, or zero if ax is specified as a literal value.
  111. -- @param ay_addr Bank for ay, or zero if ay is specified as a literal value.
  112. -- @param ar_addr Bank for ar, or zero if ar is specified as a literal value.
  113. -- @param bx_addr Bank for bx, or zero if bx is specified as a literal value.
  114. -- @param by_addr Bank for by, or zero if by is specified as a literal value.
  115. -- @param br_addr Bank for br, or zero if by is specified as a literal value.
  116. -- @param ax X-coordinate of the second character in the party after the split, or address for the value if ax_addr is non-zero.
  117. -- @param ay Y-coordinate of the second character in the party after the split, or address for the value if ax_addr is non-zero.
  118. -- @param ar Direction the second character faces after the split, or address for the value if ar_addr is non-zero.
  119. -- @param bx X-coordinate of the third character in the party after the split, or address for the value if bx_addr is non-zero.
  120. -- @param by Y-coordinate of the third character in the party after the split, or address for the value if bx_addr is non-zero.
  121. -- @param br Direction the third character faces after the split, or address for the value if br_addr is non-zero.
  122. -- @param speed Speed at which the characters split.
  123. split_party = function(ax, ay, ar, bx, by, br, speed)
  124. local player = entity_manager:get_player_entity()
  125. local x, y, z = player:get_position()
  126. for c = 2, 3 do
  127. if Party[c] ~= nil then
  128. local character = entity_manager:get_entity_from_character_id(Party[c])
  129. if character ~= nil then
  130. local cur_speed = character:get_move_auto_speed()
  131. -- Approximated speed, good enough for now.
  132. -- TODO: Calculate speed based on time, as the orignal opcode.
  133. character:set_move_auto_speed(speed / 16)
  134. --if character:is_visible() == false then
  135. -- In theory, a member is never visible before a split, but some maps
  136. -- are wrong. In these cases, don't change position and move the entity
  137. -- from it's current position.
  138. character:set_position(x, y, z)
  139. character:set_visible(true)
  140. --end
  141. character:set_solid(false)
  142. if c == 2 then
  143. character:move_to_position(ax, ay)
  144. elseif c == 3 then
  145. character:move_to_position(bx, by)
  146. end
  147. character:move_sync()
  148. if c == 2 then
  149. character:set_rotation(ar)
  150. elseif c == 3 then
  151. character:set_rotation(br)
  152. end
  153. character:set_move_auto_speed(cur_speed)
  154. character:set_solid(true)
  155. character:set_talkable(true)
  156. end
  157. end
  158. end
  159. end
  160. --- Locks or unlocks player movement and menu access.
  161. -- @param lock True to lock, false to unlock.
  162. player_lock = function(lock)
  163. entity_manager:player_lock(lock)
  164. MenuSettings.available = (lock == false)
  165. end
  166. --- Sets a bit variable on (1).
  167. --
  168. -- @param bank Memory bank (0-index, as in game).
  169. -- @param variable Bank address (0-index, as in game).
  170. -- @param bit Address bit (0-index, as in game).
  171. bit_on = function(bank, variable, bit)
  172. BitBanks[bank][variable][bit] = 1
  173. end
  174. --- Sets a bit variable off (0).
  175. --
  176. -- @param bank Memory bank (0-index, as in game).
  177. -- @param variable Bank address (0-index, as in game).
  178. -- @param bit Address bit (0-index, as in game).
  179. bit_off = function(bank, variable, bit)
  180. BitBanks[bank][variable][bit] = 0
  181. end
  182. --- Retrieves a bit variable.
  183. --
  184. -- @param bank Memory bank (0-index, as in game).
  185. -- @param variable Bank address (0-index, as in game).
  186. -- @param bit Address bit (0-index, as in game).
  187. -- @return 1 or 0
  188. bit = function(bank, variable, bit)
  189. return BitBanks[bank][variable][bit]
  190. end
  191. --- Stores the coordinates and walkmesh triangle in memory.
  192. --
  193. -- Stores the values as in the original game, without scaling.
  194. -- @param bx Bank at which to store the X coordinate.
  195. -- @param by Bank at which to store the Y coordinate.
  196. -- @param bz Bank at which to store the Z coordinate.
  197. -- @param bt Bank at which to store the triangle.
  198. -- @param id The entity ID.
  199. -- @param ax Address at which to store the X coordinate.
  200. -- @param ay Address at which to store the Y coordinate.
  201. -- @param az Address at which to store the Z coordinate.
  202. -- @param at Address at which to store the triangle.
  203. -- @param scale Map scale to multiply values
  204. axyzi = function(bx, by, bz, bt, id, ax, ay, az, at, scale)
  205. local entity = entity_manager:get_entity_from_index(id)
  206. if entity == nil then
  207. do return end
  208. end
  209. local x, y, z = entity:get_position()
  210. local t = entity:get_move_triangle_id()
  211. Banks[bx][ax] = math.floor(x * scale)
  212. Banks[by][ay] = math.floor(y * scale)
  213. Banks[bz][az] = math.floor(z * scale)
  214. Banks[bt][at] = t
  215. return 0
  216. end
  217. --- Shows the ending credits.
  218. ending_credits = function()
  219. -- TODO: Implement. Probably in a looooong loooong time.
  220. end
  221. --- Displays a window where a name can be assigned to a character.
  222. --
  223. -- @param id ID of the character to name.
  224. name_character = function(id)
  225. -- TODO: Implement.
  226. if Characters[id] == nil then
  227. print("Tried to rename invalid character with ID " .. tostring(id))
  228. return 0
  229. end
  230. UiContainer.NameMenu.id = id
  231. UiContainer.NameMenu.chocobo = false
  232. script:request(Script.UI, "NameMenu", "show", 0)
  233. end
  234. --- Displays a window where a name can be assigned to a chocobo.
  235. name_chocobo = function()
  236. -- TODO: Implement.
  237. end
  238. --- Automatically opens a menu.
  239. --
  240. -- @param menu The name of the menu.
  241. open_menu = function(menu)
  242. if menu == "party" then
  243. -- TODO
  244. elseif menu == "save" then
  245. -- TODO
  246. end
  247. end
  248. --- Opens a shop.
  249. --
  250. -- @param id Shop ID.
  251. open_shop = function(id)
  252. -- TODO: Implement
  253. end
  254. --- Plays one of the tracks assigned to the field.
  255. --
  256. -- @param id ID of the track in the field.
  257. play_map_music = function(id)
  258. local track_id = audio_manager:get_track_id(id)
  259. if track_id ~= -1 then
  260. audio_manager:play_music(tostring(track_id))
  261. else
  262. print("Requested non-existent music track ID " .. tostring(id))
  263. end
  264. end
  265. --- Sets the map name
  266. --
  267. -- It's not set from a literal text, but from a dialog identifier.
  268. --
  269. -- @param text_id ID of the dialog text that holds the map name.
  270. set_map_name = function(text_id)
  271. dialog:set_map_name(text_id)
  272. System["MapChanger"].location_name = dialog:get_map_name()
  273. end
  274. --- Shows a choice dialog and stores the result in the selected bank address.
  275. --
  276. -- @param window_id ID of the window to show the dialog in.
  277. -- @param message_id ID of the text to show in the window.
  278. -- @param first First selectable line.
  279. -- @param last Last selectable line.
  280. -- @param bank Data bank to store the selected line in.
  281. -- @param address Addres of the bank at which to store the selected line in.
  282. dialog_ask = function(window_id, message_id, first, last, bank, address)
  283. dialog:set_clickable(window_id, true)
  284. dialog:set_cursor(window_id, first, last)
  285. dialog:dialog_set_text(window_id, message_id)
  286. dialog:dialog_wait_for_close(window_id)
  287. Banks[bank][address] = dialog:get_cursor(window_id)
  288. end
  289. --- Utility to change fields and enter battles.
  290. System["MapChanger"] = {
  291. --- ID of the map to change to.
  292. map_name = "",
  293. --- Point of the map to change to.
  294. point_name = "",
  295. world_map_entry = nil,
  296. --- ID of the current field.
  297. current_map_name = "",
  298. --- Location name, to display in the main menu and in save slots.
  299. location_name = "",
  300. --- Jumps to a new map.
  301. ffvii_field = function(self)
  302. if self.map_name ~= "" then
  303. player_lock(true) -- disable menu and pc movement while load map
  304. script:request_end_sync(Script.UI, "Fade", "fade_out", 0)
  305. map(self.map_name)
  306. player_lock(false) -- enable menu and pc movement after load map
  307. self.current_map_name = self.map_name
  308. self.map_name = ""
  309. script:request_end_sync(Script.UI, "Fade", "fade_in", 0)
  310. end
  311. return 0
  312. end,
  313. --- Starts a battle.
  314. --
  315. -- @todo Implement.
  316. ffvii_battle = function(self)
  317. if self.map_name ~= "" then
  318. player_lock(true) -- disable menu and pc movement while load map
  319. script:request_end_sync(Script.UI, "Fade", "fade_out", 0)
  320. map(self.map_name)
  321. -- load battle ui
  322. -- load player entity
  323. --self.map_name = ""
  324. script:request_end_sync(Script.UI, "Fade", "fade_in", 0)
  325. end
  326. return 0
  327. end,
  328. }