vehicles.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. EntityContainer = EntityContainer or {}
  2. local function show_vehicle_error(message)
  3. print(message)
  4. if not dialog:show_message(message) then
  5. return false
  6. end
  7. return true
  8. end
  9. local Vehicle = {}
  10. Vehicle.__index = Vehicle
  11. Vehicle.terrains = {}
  12. Vehicle.board = function(self)
  13. show_vehicle_error("Vehicle.board must be implemented by the vehicle.")
  14. return false
  15. end
  16. Vehicle.unboard = function(self)
  17. show_vehicle_error("Vehicle.unboard must be implemented by the vehicle.")
  18. return false
  19. end
  20. Vehicle.on_interact = function(self)
  21. self:board()
  22. end
  23. --[[
  24. Creates a vehicle that inherits the Vehicle interface.
  25. @param entity_name Managed world-map entity name.
  26. @param terrains Walkmap terrain type names this vehicle can travel.
  27. @param rise_amount Height offset applied when boarding.
  28. @return The vehicle script table.
  29. ]]
  30. local function extend_vehicle(entity_name, terrains, rise_amount, unboarding_terrains)
  31. return setmetatable({
  32. entity_name = entity_name,
  33. terrains = terrains,
  34. unboarding_terrains = unboarding_terrains or terrains,
  35. rise_amount = rise_amount,
  36. }, Vehicle)
  37. end
  38. local function is_unboarding_terrain_allowed(vehicle)
  39. local terrain_name = world_map_module:get_terrain_type_under_entity(vehicle.entity_name)
  40. for _, allowed_terrain in ipairs(vehicle.unboarding_terrains) do
  41. if terrain_name == allowed_terrain then
  42. return true
  43. end
  44. end
  45. return false, terrain_name
  46. end
  47. --[[
  48. Copies a terrain list so a Chocobo color change does not mutate a shared profile.
  49. @param terrains Terrain type names to copy.
  50. @return A new terrain list.
  51. ]]
  52. local function copy_terrain_list(terrains)
  53. local copy = {}
  54. for _, terrain_name in ipairs(terrains) do
  55. table.insert(copy, terrain_name)
  56. end
  57. return copy
  58. end
  59. local yellow_chocobo_terrains = {
  60. "GRASS", "FOREST", "SWAMP", "DESERT", "WASTELAND", "SNOW", "RIVERSIDE",
  61. "COREL_BRIDGE", "WUTAI_BRIDGE", "HILL_SIDE", "BEACH", "CANYON", "MOUNTAIN_PASS",
  62. "GOLD_SAUCER_DESERT_BORDER", "BRIDGE_HEAD",
  63. }
  64. local green_chocobo_terrains = copy_terrain_list(yellow_chocobo_terrains)
  65. table.insert(green_chocobo_terrains, "MOUNTAIN")
  66. table.insert(green_chocobo_terrains, "CLIFF")
  67. local blue_chocobo_terrains = copy_terrain_list(yellow_chocobo_terrains)
  68. table.insert(blue_chocobo_terrains, "RIVER_CROSSING")
  69. table.insert(blue_chocobo_terrains, "RIVER")
  70. table.insert(blue_chocobo_terrains, "WATER")
  71. local black_chocobo_terrains = copy_terrain_list(green_chocobo_terrains)
  72. table.insert(black_chocobo_terrains, "RIVER_CROSSING")
  73. table.insert(black_chocobo_terrains, "RIVER")
  74. table.insert(black_chocobo_terrains, "WATER")
  75. local gold_chocobo_terrains = {
  76. "GRASS", "FOREST", "MOUNTAIN", "SEA_DEEP_WATER", "RIVER_CROSSING", "RIVER", "WATER",
  77. "SWAMP", "DESERT", "WASTELAND", "SNOW", "RIVERSIDE", "CLIFF", "COREL_BRIDGE",
  78. "WUTAI_BRIDGE", "UNUSED_1", "HILL_SIDE", "BEACH", "SUBMARINE_PEN", "CANYON",
  79. "MOUNTAIN_PASS", "UNKNOWN_1", "WATERFALL", "UNUSED_2", "GOLD_SAUCER_DESERT", "JUNGLE",
  80. "SEA", "NORTHERND_CAVE", "GOLD_SAUCER_DESERT_BORDER", "BRIDGE_HEAD", "BACK_ENTRANCE",
  81. "UNUSED_3",
  82. }
  83. local chocobo_terrains_by_color = {
  84. YELLOW = yellow_chocobo_terrains,
  85. GREEN = green_chocobo_terrains,
  86. BLUE = blue_chocobo_terrains,
  87. BLACK = black_chocobo_terrains,
  88. GOLD = gold_chocobo_terrains,
  89. }
  90. EntityContainer["WorldMapHighwind"] = extend_vehicle(
  91. "WorldMapHighwind",
  92. {
  93. "GRASS", "FOREST", "MOUNTAIN", "SEA_DEEP_WATER", "RIVER_CROSSING", "RIVER",
  94. "WATER", "SWAMP", "DESERT", "WASTELAND", "SNOW", "RIVERSIDE", "CLIFF",
  95. "COREL_BRIDGE", "WUTAI_BRIDGE", "HILL_SIDE", "BEACH", "SUBMARINE_PEN", "CANYON",
  96. "MOUNTAIN_PASS", "UNKNOWN_1", "WATERFALL", "GOLD_SAUCER_DESERT", "JUNGLE", "SEA",
  97. "NORTHERND_CAVE", "GOLD_SAUCER_DESERT_BORDER", "BRIDGE_HEAD", "BACK_ENTRANCE",
  98. },
  99. 0.75,
  100. {"GRASS", "NORTHERND_CAVE"}
  101. )
  102. EntityContainer["WorldMapHighwind"].board = function(self)
  103. local player_entity = entity_manager:get_player_entity()
  104. local vehicle_entity = entity_manager:get_entity(self.entity_name)
  105. if not player_entity or not vehicle_entity then
  106. return false
  107. end
  108. local player_name = player_entity:get_name()
  109. if player_name ~= "WorldMapCloud" and player_name ~= "WorldMapChocobo" then
  110. show_vehicle_error("Leave your current vehicle before boarding another.")
  111. return false
  112. end
  113. vehicle_entity:move_by(0, self.rise_amount, 0)
  114. player_entity:set_visible(false)
  115. player_entity:set_solid(false)
  116. player_entity:set_talkable(false)
  117. world_map_module:clear_vehicle_terrains()
  118. for _, terrain_name in ipairs(self.terrains) do
  119. world_map_module:add_vehicle_terrain(terrain_name)
  120. end
  121. entity_manager:set_player_entity(self.entity_name)
  122. return true
  123. end
  124. EntityContainer["WorldMapHighwind"].unboard = function(self)
  125. if not is_unboarding_terrain_allowed(self) then
  126. show_vehicle_error("The Highwind cannot be unboarded on this terrain.")
  127. return false
  128. end
  129. if not world_map_module:unboard_vehicle(self.entity_name) then
  130. return false
  131. end
  132. local vehicle_entity = entity_manager:get_entity(self.entity_name)
  133. if vehicle_entity then
  134. vehicle_entity:move_by(0, -self.rise_amount, 0)
  135. end
  136. return true
  137. end
  138. EntityContainer["WorldMapBuggy"] = extend_vehicle(
  139. "WorldMapBuggy",
  140. {
  141. "GRASS", "FOREST", "SWAMP", "DESERT", "WASTELAND", "SNOW", "RIVERSIDE",
  142. "COREL_BRIDGE", "WUTAI_BRIDGE", "HILL_SIDE", "BEACH", "RIVER_CROSSING", "CANYON",
  143. "MOUNTAIN_PASS", "GOLD_SAUCER_DESERT", "JUNGLE", "GOLD_SAUCER_DESERT_BORDER",
  144. "BRIDGE_HEAD", "BACK_ENTRANCE",
  145. },
  146. 0,
  147. {
  148. "GRASS", "FOREST", "WASTELAND", "SNOW", "SWAMP", "DESERT", "COREL_BRIDGE",
  149. "HILL_SIDE", "BEACH", "CANYON", "MOUNTAIN_PASS", "JUNGLE",
  150. }
  151. )
  152. EntityContainer["WorldMapBuggy"].board = function(self)
  153. local player_entity = entity_manager:get_player_entity()
  154. local vehicle_entity = entity_manager:get_entity(self.entity_name)
  155. if not player_entity or not vehicle_entity then
  156. return false
  157. end
  158. if player_entity:get_name() ~= "WorldMapCloud" then
  159. show_vehicle_error("Leave your current vehicle before boarding another.")
  160. return false
  161. end
  162. player_entity:set_visible(false)
  163. player_entity:set_solid(false)
  164. player_entity:set_talkable(false)
  165. world_map_module:clear_vehicle_terrains()
  166. for _, terrain_name in ipairs(self.terrains) do
  167. world_map_module:add_vehicle_terrain(terrain_name)
  168. end
  169. entity_manager:set_player_entity(self.entity_name)
  170. return true
  171. end
  172. EntityContainer["WorldMapBuggy"].unboard = function(self)
  173. if not is_unboarding_terrain_allowed(self) then
  174. show_vehicle_error("The Buggy cannot be unboarded on this terrain.")
  175. return false
  176. end
  177. return world_map_module:unboard_vehicle(self.entity_name)
  178. end
  179. EntityContainer["WorldMapChocobo"] = extend_vehicle(
  180. "WorldMapChocobo",
  181. copy_terrain_list(chocobo_terrains_by_color.YELLOW),
  182. 0,
  183. {
  184. "GRASS", "FOREST", "WASTELAND", "SNOW", "SWAMP", "DESERT", "COREL_BRIDGE",
  185. "HILL_SIDE", "BEACH", "CANYON", "MOUNTAIN_PASS", "JUNGLE",
  186. }
  187. )
  188. EntityContainer["WorldMapChocobo"].color = "YELLOW"
  189. EntityContainer["WorldMapChocobo"].set_color = function(self, color)
  190. if type(color) ~= "string" then
  191. return false
  192. end
  193. local color_name = string.upper(color)
  194. local terrains = chocobo_terrains_by_color[color_name]
  195. if not terrains then
  196. show_vehicle_error("Unknown Chocobo color: " .. color_name)
  197. return false
  198. end
  199. self.color = color_name
  200. self.terrains = copy_terrain_list(terrains)
  201. local player_entity = entity_manager:get_player_entity()
  202. if player_entity and player_entity:get_name() == self.entity_name then
  203. world_map_module:clear_vehicle_terrains()
  204. for _, terrain_name in ipairs(self.terrains) do
  205. world_map_module:add_vehicle_terrain(terrain_name)
  206. end
  207. end
  208. return true
  209. end
  210. EntityContainer["WorldMapChocobo"].board = function(self)
  211. local player_entity = entity_manager:get_player_entity()
  212. local vehicle_entity = entity_manager:get_entity(self.entity_name)
  213. if not player_entity or not vehicle_entity then
  214. return false
  215. end
  216. if player_entity:get_name() ~= "WorldMapCloud" then
  217. show_vehicle_error("Leave your current vehicle before boarding another.")
  218. return false
  219. end
  220. player_entity:set_visible(false)
  221. player_entity:set_solid(false)
  222. player_entity:set_talkable(false)
  223. world_map_module:clear_vehicle_terrains()
  224. for _, terrain_name in ipairs(self.terrains) do
  225. world_map_module:add_vehicle_terrain(terrain_name)
  226. end
  227. entity_manager:set_player_entity(self.entity_name)
  228. return true
  229. end
  230. EntityContainer["WorldMapChocobo"].unboard = function(self)
  231. if not is_unboarding_terrain_allowed(self) then
  232. show_vehicle_error("The Chocobo cannot be unboarded on this terrain.")
  233. return false
  234. end
  235. return world_map_module:unboard_vehicle(self.entity_name)
  236. end
  237. EntityContainer["WorldMapSubmarine"] = extend_vehicle(
  238. "WorldMapSubmarine",
  239. {"SEA_DEEP_WATER", "SEA", "SUBMARINE_PEN"},
  240. 0,
  241. {"SUBMARINE_PEN"}
  242. )
  243. EntityContainer["WorldMapSubmarine"].board = function(self)
  244. local player_entity = entity_manager:get_player_entity()
  245. local vehicle_entity = entity_manager:get_entity(self.entity_name)
  246. if not player_entity or not vehicle_entity then
  247. return false
  248. end
  249. if player_entity:get_name() ~= "WorldMapCloud" then
  250. show_vehicle_error("Leave your current vehicle before boarding another.")
  251. return false
  252. end
  253. player_entity:set_visible(false)
  254. player_entity:set_solid(false)
  255. player_entity:set_talkable(false)
  256. world_map_module:clear_vehicle_terrains()
  257. for _, terrain_name in ipairs(self.terrains) do
  258. world_map_module:add_vehicle_terrain(terrain_name)
  259. end
  260. entity_manager:set_player_entity(self.entity_name)
  261. return true
  262. end
  263. EntityContainer["WorldMapSubmarine"].unboard = function(self)
  264. if not is_unboarding_terrain_allowed(self) then
  265. show_vehicle_error("The Submarine cannot be unboarded on this terrain.")
  266. return false
  267. end
  268. return world_map_module:unboard_vehicle(self.entity_name)
  269. end
  270. EntityContainer["WorldMapTinyBronco"] = extend_vehicle(
  271. "WorldMapTinyBronco",
  272. {
  273. "RIVERSIDE", "BEACH", "RIVER_CROSSING", "RIVER", "WATER",
  274. },
  275. 0,
  276. {"BEACH", "RIVERSIDE"}
  277. )
  278. EntityContainer["WorldMapTinyBronco"].board = function(self)
  279. local player_entity = entity_manager:get_player_entity()
  280. local vehicle_entity = entity_manager:get_entity(self.entity_name)
  281. if not player_entity or not vehicle_entity then
  282. return false
  283. end
  284. if player_entity:get_name() ~= "WorldMapCloud" then
  285. show_vehicle_error("Leave your current vehicle before boarding another.")
  286. return false
  287. end
  288. player_entity:set_visible(false)
  289. player_entity:set_solid(false)
  290. player_entity:set_talkable(false)
  291. world_map_module:clear_vehicle_terrains()
  292. for _, terrain_name in ipairs(self.terrains) do
  293. world_map_module:add_vehicle_terrain(terrain_name)
  294. end
  295. entity_manager:set_player_entity(self.entity_name)
  296. return true
  297. end
  298. EntityContainer["WorldMapTinyBronco"].unboard = function(self)
  299. if not is_unboarding_terrain_allowed(self) then
  300. show_vehicle_error("The Tiny Bronco cannot be unboarded on this terrain.")
  301. return false
  302. end
  303. return world_map_module:unboard_vehicle(self.entity_name)
  304. end