system.lua 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913
  1. MAX_INVENTORY_SLOTS = 320
  2. MAX_KEY_SLOTS = 50
  3. MAX_MATERIA_SLOTS = 200
  4. --- System related parameters and utilities
  5. System = {
  6. --- Indicates if the game is paused.
  7. paused = false,
  8. --- Indicates if the menu was available when paused.
  9. paused_menu_settings_available = false,
  10. --- Pauses the game.
  11. pause = function(self)
  12. if MenuSettings.pause_available == false then
  13. do return end
  14. end
  15. if self.paused == false then
  16. self.paused = true
  17. entity_manager:set_paused(true)
  18. self.paused_menu_settings_available = MenuSettings.available
  19. MenuSettings.available = false
  20. audio_manager:play_sound("Cursor")
  21. UiContainer.PauseMenu:show()
  22. end
  23. end,
  24. --- Unpauses the game.
  25. unpause = function(self)
  26. if self.paused == true then
  27. self.paused = false
  28. entity_manager:set_paused(false)
  29. MenuSettings.available = self.paused_menu_settings_available
  30. audio_manager:play_sound("Cursor")
  31. UiContainer.PauseMenu:hide()
  32. end
  33. end,
  34. --- Checks if the game is paused.
  35. --
  36. -- @return True if the game is paused, false if not.
  37. is_paused = function(self)
  38. return self.paused
  39. end
  40. }
  41. Timer = {
  42. -- TODO When the game clock is implemented, dont use FPS, call this every second.
  43. FPS = 250,
  44. time = 0,
  45. frames = 0,
  46. update = function()
  47. if Timer.time > 0 or Timer.frames > 0 then
  48. Timer.frames = Timer.frames - 1
  49. if Timer.frames == 0 then
  50. Timer.time = Timer.time - 1
  51. dialog:update_timer(Timer.time)
  52. if Timer.time > 0 then
  53. Timer.frames = Timer.FPS
  54. end
  55. Banks[1][20]--[[timer_hours]] = math.floor(Timer.time / 3600)
  56. Banks[1][21]--[[timer_minutes]] = math.floor(Timer.time / 60) % 60
  57. Banks[1][22]--[[timer_seconds]] = Timer.time % 60
  58. end
  59. Banks[1][23]--[[timer_frames]] = Timer.frames
  60. end
  61. end,
  62. set = function(time)
  63. Timer.time = tonumber(time)
  64. Timer.frames = Timer.FPS
  65. Banks[1][20]--[[timer_hours]] = math.floor(Timer.time / 3600)
  66. Banks[1][21]--[[timer_minutes]] = math.floor(Timer.time / 60) % 60
  67. Banks[1][22]--[[timer_seconds]] = Timer.time % 60
  68. Banks[1][23]--[[timer_frames]] = Timer.frames
  69. end
  70. }
  71. --- Export character names to the text manager.
  72. --
  73. -- Used to compose dialog with character names. Must be called on start and when a character is
  74. -- renamed.
  75. export_character_names = function()
  76. for c = 0, 8 do
  77. if Characters[c] ~= nil then
  78. text_manager:set_character_name(c, Characters[c].name)
  79. end
  80. end
  81. end
  82. --- Export party members to the text manager.
  83. --
  84. -- Used to compose dialog with party character names. Must be called when the party formation is
  85. -- changed in any way.
  86. export_party_members = function()
  87. if Party[1] == nil then
  88. c1 = -1
  89. else
  90. c1 = Party[1]
  91. end
  92. if Party[2] == nil then
  93. c2 = -1
  94. else
  95. c2 = Party[2]
  96. end
  97. if Party[3] == nil then
  98. c3 = -1
  99. else
  100. c3 = Party[3]
  101. end
  102. text_manager:set_party(c1, c2, c3)
  103. end
  104. --- Sets the current party.
  105. --
  106. -- @param character1 The first member character ID, the party leader.
  107. -- @param character2 The second member character ID, can be nil.
  108. -- @param character3 The third member character ID, can be nil.
  109. set_party = function(character1, character2, character3)
  110. Party[1] = character1
  111. Party[2] = character2
  112. Party[3] = character3
  113. export_party_members()
  114. end
  115. --- Checks if a a character ID actually belongs to a character.
  116. --
  117. -- @param character The ID to check.
  118. -- @return True if the ID is a valid character ID, false otherwise.
  119. is_character = function(character)
  120. if Characters[character] == nil then
  121. return false
  122. end
  123. return true
  124. end
  125. --- Adds a character to the curent party.
  126. --
  127. -- @param character ID of the character to add.
  128. add_party_character = function(character)
  129. if Characters[character] == nil then
  130. print("Tried add to party invalid character \"" .. character .. "\".");
  131. return
  132. end
  133. if Characters[character].locked == true then
  134. print("Tried add to party locked character \"" .. character .. "\".");
  135. return
  136. end
  137. -- Check if character already in party
  138. for i = 1, 3 do
  139. if Party[i] == character then
  140. print("Character \"" .. character .. "\" already in party.");
  141. return
  142. end
  143. end
  144. -- Try to add in first free slot.
  145. for i = 1, 3 do
  146. if Party[i] == nil then
  147. Party[i] = character
  148. export_party_members()
  149. return
  150. end
  151. end
  152. end
  153. --- Removes a character from the curent party.
  154. --
  155. -- @param character ID of the character to remove.
  156. remove_party_character = function(character)
  157. if Characters[character] == nil then
  158. print("Tried remove from party invalid character \"" .. character .. "\".");
  159. return
  160. end
  161. if Characters[character].locked == true then
  162. print("Tried remove from party locked character \"" .. character .. "\".");
  163. return
  164. end
  165. for i = 1, 3 do
  166. if Party[i] == character then
  167. Party[i] = nil
  168. export_party_members()
  169. return
  170. end
  171. end
  172. end
  173. --- Locks a character in the party.
  174. --
  175. -- Locked characters can't be manually swapped in or out of the party.
  176. --
  177. -- @param character ID of the character to lock.
  178. set_party_lock = function(character, lock)
  179. if Characters[character] == nil then
  180. print("Tried set/unset party lock to invalid character \"" .. character .. "\".");
  181. return
  182. end
  183. Characters[character].locked = lock;
  184. end
  185. --- Checks if a character is locked in the party.
  186. --
  187. -- Locked characters can't be manually swapped in or out of the party.
  188. --
  189. -- @param character ID of the character to check for locks.
  190. get_party_lock = function(character)
  191. if Characters[character] == nil then
  192. print("Tried get party lock from invalid character \"" .. character .. "\".");
  193. return false
  194. end
  195. return Characters[character].locked
  196. end
  197. --- Enables or disables a character is the PHS.
  198. --
  199. -- Disabled characters don't appear in the PHS.
  200. --
  201. -- @param character ID of the character to enable or disable.
  202. -- @param enable True to enable, false to disable.
  203. set_character_enable = function(character, enable)
  204. if Characters[character] == nil then
  205. print("Tried enable/disable invalid character \"" .. character .. "\".");
  206. return
  207. end
  208. Characters[character].enabled = enable;
  209. end
  210. --- Checks if a character is enabled in the PHS.
  211. --
  212. -- Disabled characters don't appear in the PHS.
  213. --
  214. -- @param character ID of the character to check.
  215. -- @return True if enabled, false if disables.
  216. get_character_enable = function(character)
  217. if Characters[character] == nil then
  218. print("Tried get enabled from invalid character \"" .. character .. "\".");
  219. return false
  220. end
  221. return Characters[character].enabled;
  222. end
  223. --- Adds items to the inventory.
  224. --
  225. -- Inventory quantities will be capped at 99.
  226. --
  227. -- @param item Item ID.
  228. -- @param quantity Quantity to add.
  229. Inventory.add_item = function(item, quantity)
  230. -- TODO: Also check weapons, armors and accessories.
  231. if Game.Items[item] == nil then
  232. print("Tried add invalid item \"" .. item .. "\".");
  233. return
  234. end
  235. for i = 1, MAX_INVENTORY_SLOTS do
  236. if Inventory[i] ~= nil and Inventory[i].item == item then
  237. -- Item already in inventory, add quantity
  238. Inventory[i].quantity = Inventory[i].quantity + quantity
  239. if Inventory[i].quantity > 99 then
  240. Inventory[i].quantity = 99
  241. end
  242. return
  243. end
  244. end
  245. -- Item not in inventory, search for the first available position.
  246. for i = 1, MAX_INVENTORY_SLOTS do
  247. if Inventory[i] == nil then
  248. Inventory[i] = {item = item, quantity = quantity}
  249. if Inventory[i].quantity > 99 then
  250. Inventory[i].quantity = 99
  251. end
  252. return
  253. end
  254. end
  255. -- If reached this point, it means that the item is not in inventory, and all slots are full.
  256. -- TODO: The orignal game does nothing, but in here?
  257. return
  258. end
  259. --- Checks how many items of a type are in the inventory.
  260. --
  261. -- @param item The item ID.
  262. -- @return Number of the items in the inventory.
  263. Inventory.get_item_quantity = function(item)
  264. local qty = 0
  265. for i = 1, MAX_INVENTORY_SLOTS do
  266. if Inventory[i] ~= nil and Inventory[i].item == item then
  267. qty = qty + Inventory[i].quantity
  268. end
  269. end
  270. return qty
  271. end
  272. --- Removes items from the inventory.
  273. --
  274. -- Negative quantities can't remain in the inventory. If there are less than requested to remove,
  275. -- all of them will be removed.
  276. --
  277. -- @param item Item ID.
  278. -- @param quantity Quantity to remove.
  279. Inventory.remove_item = function(item, quantity)
  280. for i = 1, MAX_INVENTORY_SLOTS do
  281. if Inventory[i] ~= nil and Inventory[i].item == item then
  282. -- Item already in inventory, add quantity
  283. Inventory[i].quantity = Inventory[i].quantity - quantity
  284. if Inventory[i].quantity <= 0 then
  285. Inventory[i] = nil
  286. end
  287. return
  288. end
  289. end
  290. end
  291. --- Adds a key item.
  292. --
  293. -- @param item ID of the key item (0-50).
  294. Inventory.add_key_item = function(item)
  295. Inventory.key[item] = true
  296. return
  297. end
  298. --- Removes a key item.
  299. --
  300. -- @param item ID of the key item (0-50).
  301. Inventory.remove_key_item = function(item)
  302. Inventory.key[item] = false
  303. return
  304. end
  305. --- Checks if the party has a key item.
  306. --
  307. -- @param item ID of the key item (0-50).
  308. -- @return True if the party has the key item, false otherwise.
  309. Inventory.has_key_item = function(item)
  310. return Inventory.key[item]
  311. end
  312. --- Adds money.
  313. --
  314. -- @param amount The money to add.
  315. Inventory.add_money = function(amount)
  316. Inventory.money = Inventory.money + amount
  317. if Inventory.money > 99999999 then
  318. Inventory.money = 99999999
  319. end
  320. end
  321. --- Removes money.
  322. --
  323. -- Negative quantities can't remain as money. If there are less than requested to remove, all of
  324. -- it will be removed.
  325. --
  326. -- @param amount Money to remove.
  327. Inventory.remove_money = function(amount)
  328. Inventory.money = Inventory.money - amount
  329. if Inventory.money < 0 then
  330. Inventory.money = 0
  331. end
  332. end
  333. --- Copies the amount of gil the party has into data banks
  334. --
  335. -- As the gil amount is a four-byte value, in the original game the arguments require two
  336. -- destination addresses to place two two-byte values into. Address 1 takes the lower two bytes of
  337. -- the gil amount, while address 2 takes the higher two bytes.
  338. --
  339. -- @param b1 First destination bank.
  340. -- @param b2 Second destination address.
  341. -- @param a1 First destination bank.
  342. -- @param a2 Second destination address.
  343. Inventory.money_to_2_banks = function(b1, b2, a1, a2)
  344. Banks[b1][a1] = math.floor(Inventory.money / 65535)
  345. Banks[b2][a2] = math.floor(Inventory.money % 65535) - math.floor(Inventory.money / 65535)
  346. end
  347. --- Sorts the items in the inventory.
  348. --
  349. -- @param criteria {@see Inventory.ORDER.CUSTOM} (unimplemented), {@see Inventory.ORDER.FIELD.},
  350. -- {@see Inventory.ORDER.BATTLE}, {@see Inventory.ORDER.THROW}, {@see Inventory.ORDER.TYPE},
  351. -- {@see Inventory.ORDER.NAME}, {@see Inventory.ORDER.MOST} or {@see Inventory.ORDER.LEAST}. Any
  352. -- othe rvalue and the items will not be sorted.
  353. Inventory.sort = function(criteria)
  354. if criteria == Inventory.ORDER.CUSTOM then
  355. print("Custom order unimplemented")
  356. elseif criteria == Inventory.ORDER.FIELD then
  357. table.sort(Inventory, function (left, right)
  358. if Game.Items[left.item].menu ~= Game.Items[right.item].menu then
  359. return Game.Items[left.item].menu > Game.Items[right.item].menu
  360. end
  361. return left.item < right.item
  362. end
  363. )
  364. elseif criteria == Inventory.ORDER.BATTLE then
  365. table.sort(Inventory, function (left, right)
  366. if Game.Items[left.item].battle ~= Game.Items[right.item].battle then
  367. return Game.Items[left.item].battle > Game.Items[right.item].battle
  368. end
  369. return left.item < right.item
  370. end
  371. )
  372. elseif criteria == Inventory.ORDER.THROW then
  373. table.sort(Inventory, function (left, right)
  374. if Game.Items[left.item].throw ~= Game.Items[right.item].throw then
  375. return Game.Items[left.item].throw > Game.Items[right.item].throw
  376. end
  377. return left.item < right.item
  378. end
  379. )
  380. elseif criteria == Inventory.ORDER.TYPE then
  381. table.sort(Inventory, function (left, right)
  382. return left.item < right.item
  383. end
  384. )
  385. elseif criteria == Inventory.ORDER.NAME then
  386. table.sort(Inventory, function (left, right)
  387. return Game.Items[left.item].name < Game.Items[right.item].name
  388. end
  389. )
  390. elseif criteria == Inventory.ORDER.MOST then
  391. table.sort(Inventory, function (left, right)
  392. if left.quantity ~= right.quantity then
  393. return left.quantity > right.quantity
  394. end
  395. return left.item < right.item
  396. end
  397. )
  398. elseif criteria == Inventory.ORDER.LEAST then
  399. table.sort(Inventory, function (left, right)
  400. if left.quantity ~= right.quantity then
  401. return left.quantity < right.quantity
  402. end
  403. return left.item < right.item
  404. end
  405. )
  406. end
  407. end
  408. Characters.recalculate_stats = function(id)
  409. -- Initialize
  410. local stats = Characters[id].stats
  411. stats.str.total = stats.str.base
  412. stats.vit.total = stats.vit.base
  413. stats.mag.total = stats.mag.base
  414. stats.spr.total = stats.spr.base
  415. stats.dex.total = stats.dex.base
  416. stats.lck.total = stats.lck.base
  417. stats.hp.max = stats.hp.base
  418. stats.mp.max = stats.mp.base
  419. -- Weapon bonus. HP and MP are percents of base, the rest absolutes.
  420. local weapon = Game.Items[Characters[id].weapon.id]
  421. for stat, value in ipairs(weapon.stats) do
  422. if stat == "str" then
  423. stats.str.total = stats.str.total + value
  424. elseif stat == "vit" then
  425. stats.vit.total = stats.vit.total + value
  426. elseif stat == "mag" then
  427. stats.mag.total = stats.mag.total + value
  428. elseif stat == "spr" then
  429. stats.spr.total = stats.spr.total + value
  430. elseif stat == "dex" then
  431. stats.dex.total = stats.dex.total + value
  432. elseif stat == "lck" then
  433. stats.lck.total = stats.lck.total + value
  434. elseif stat == "hp" then
  435. stats.hp.max = stats.hp.max + (value * stats.hp.max / 100)
  436. elseif stat == "mp" then
  437. stats.mp.max = stats.mp.max + (value * stats.mp.max / 100)
  438. end
  439. end
  440. -- Armor bonus. HP and MP are percents of bsae, the rest absolutes.
  441. local armor = Game.Items[Characters[id].armor.id]
  442. for stat, value in ipairs(armor.stats) do
  443. if stat == "str" then
  444. stats.str.total = stats.str.total + value
  445. elseif stat == "vit" then
  446. stats.vit.total = stats.vit.total + value
  447. elseif stat == "mag" then
  448. stats.mag.total = stats.mag.total + value
  449. elseif stat == "spr" then
  450. stats.spr.total = stats.spr.total + value
  451. elseif stat == "dex" then
  452. stats.dex.total = stats.dex.total + value
  453. elseif stat == "lck" then
  454. stats.lck.total = stats.lck.total + value
  455. elseif stat == "hp" then
  456. stats.hp.max = stats.hp.max + (value * stats.hp.max / 100)
  457. elseif stat == "mp" then
  458. stats.mp.max = stats.mp.max + (value * stats.mp.max / 100)
  459. end
  460. end
  461. -- Accesory bonus
  462. local accessory = Game.Items[Characters[id].accessory]
  463. if (accessory ~= nil) then
  464. for stat, value in ipairs(armor.stats) do
  465. if stat == "str" then
  466. stats.str.total = stats.str.total + value
  467. elseif stat == "vit" then
  468. stats.vit.total = stats.vit.total + value
  469. elseif stat == "mag" then
  470. stats.mag.total = stats.mag.total + value
  471. elseif stat == "spr" then
  472. stats.spr.total = stats.spr.total + value
  473. elseif stat == "dex" then
  474. stats.dex.total = stats.dex.total + value
  475. elseif stat == "lck" then
  476. stats.lck.total = stats.lck.total + value
  477. elseif stat == "hp" then
  478. stats.hp.max = stats.hp.max + (value * stats.hp.max / 100)
  479. elseif stat == "mp" then
  480. stats.mp.max = stats.mp.max + (value * stats.mp.max / 100)
  481. end
  482. end
  483. end
  484. -- Get a list of materias
  485. local materias = {}
  486. for m = 0, 7 do
  487. if Characters[id].weapon.materia[m] ~= nil then
  488. table.insert(materias, Game.Materia[Characters[id].weapon.materia[m].id])
  489. end
  490. if Characters[id].armor.materia[m] ~= nil then
  491. table.insert(materias, Game.Materia[Characters[id].weapon.materia[m].id])
  492. end
  493. end
  494. -- Get materia bonuses. All are percents of base.
  495. for _, materia in ipairs(materias) do
  496. for stat, value in ipairs(materia.stats) do
  497. if stat == "str" then
  498. stats.str.total = stats.str.total + (value * stats.str.base / 100)
  499. elseif stat == "vit" then
  500. stats.vit.total = stats.vit.total + (value * stats.vit.base / 100)
  501. elseif stat == "mag" then
  502. stats.mag.total = stats.mag.total + (value * stats.mag.base / 100)
  503. elseif stat == "spr" then
  504. stats.spr.total = stats.spr.total + (value * stats.spr.base / 100)
  505. elseif stat == "dex" then
  506. stats.dex.total = stats.dex.total + (value * stats.dex.base / 100)
  507. elseif stat == "lck" then
  508. stats.lck.total = stats.lck.total + (value * stats.lck.base / 100)
  509. elseif stat == "hp" then
  510. stats.hp.max = stats.hp.max + (value * stats.hp.max / 100)
  511. elseif stat == "mp" then
  512. stats.mp.max = stats.mp.max + (value * stats.mp.max / 100)
  513. end
  514. end
  515. end
  516. -- Round down all basic stats
  517. stats.str.total = math.floor(stats.str.total)
  518. stats.vit.total = math.floor(stats.vit.total)
  519. stats.mag.total = math.floor(stats.mag.total)
  520. stats.spr.total = math.floor(stats.spr.total)
  521. stats.dex.total = math.floor(stats.dex.total)
  522. stats.lck.total = math.floor(stats.lck.total)
  523. stats.hp.max = math.floor(stats.hp.max)
  524. stats.mp.max = math.floor(stats.mp.max)
  525. if stats.str.total < 1 then
  526. stats.str.total = 1
  527. end
  528. if stats.vit.total < 1 then
  529. stats.vit.total = 1
  530. end
  531. if stats.mag.total < 1 then
  532. stats.mag.total = 1
  533. end
  534. if stats.spr.total < 1 then
  535. stats.spr.total = 1
  536. end
  537. if stats.dex.total < 1 then
  538. stats.dex.total = 1
  539. end
  540. if stats.lck.total < 1 then
  541. stats.lck.total = 1
  542. end
  543. if stats.hp.max < 1 then
  544. stats.hp.max = 1
  545. end
  546. if stats.mp.max < 1 then
  547. stats.mp.max = 1
  548. end
  549. -- Calculate composed stats.
  550. stats.atk = stats.str.total + weapon.power
  551. stats.acc = weapon.accuracy
  552. stats.def = stats.vit.total + armor.defense.physical
  553. stats.eva = armor.evasion.physical
  554. stats.matk = stats.mag.total
  555. stats.mdef = stats.spr.total + armor.defense.magical
  556. stats.meva = armor.evasion.magical
  557. -- Cap composed stats. TODO: Max caps?
  558. if stats.atk < 1 then
  559. stats.atk = 1
  560. end
  561. if stats.acc < 1 then
  562. stats.acc = 1
  563. end
  564. if stats.def < 1 then
  565. stats.def = 1
  566. end
  567. if stats.eva < 1 then
  568. stats.eva = 1
  569. end
  570. if stats.matk < 1 then
  571. stats.matk = 1
  572. end
  573. if stats.mdef < 1 then
  574. stats.mdef = 1
  575. end
  576. if stats.meva < 1 then
  577. stats.meva = 1
  578. end
  579. -- TODO: If HP<->MP materia, invert values.
  580. -- Cap current HP/MP
  581. if stats.hp.current > stats.hp.max then
  582. stats.hp.current = stats.hp.max
  583. end
  584. if stats.mp.current > stats.mp.max then
  585. stats.mp.current = stats.mp.max
  586. end
  587. -- Assign
  588. Characters[id].stats = stats
  589. end
  590. --- Sends the materia that doesn't fit in a piece of equipment back to the materia inventory.
  591. --
  592. -- @param char_id ID of the character.
  593. -- @param part "weapon" or "armor".
  594. -- @param slots Number of materia slots the equipment will have. Slots are 0-7 in the character.
  595. Characters.remove_excess_materia = function(char_id, part, slots)
  596. local materia = Characters[char_id][part].materia
  597. for m = slots, 7 do
  598. if materia[m] ~= nil then
  599. Materia.add(materia[m].id, materia[m].ap)
  600. materia[m] = nil
  601. end
  602. end
  603. end
  604. --- Equips an item on a character.
  605. --
  606. -- Works for weapons, armors and accessories. If there is a previously equiped item, it
  607. -- will be unequiped and added to the inventory. The equiped item will be removed form the
  608. -- inventory. For armors and weapons, if the newly equiped item has less materia slots than
  609. -- the previuos one, remainig materia will be unequiped and added to the materia inventory.
  610. --
  611. -- @param char_id ID of the character to equip the item to.
  612. -- @param item_id ID of the item to equip.
  613. -- @return True if the item was equipped. False if the character or item don't exist, if
  614. -- the item is not in the inventory, or if the item is not equippable or not equippable by
  615. -- the selected character. In any of those cases, an error message will be printed.
  616. Characters.equip = function(char_id, item_id)
  617. -- Error checks
  618. if Characters[char_id] == nil then
  619. print("Tried to equip item on wrong character: " .. tostring(char_id))
  620. return false
  621. elseif Game.Items[item_id] == nil then
  622. print("Tried to equip invalid item: " .. tostring(item_id))
  623. return false
  624. elseif Inventory.get_item_quantity(item_id) < 1 then
  625. print("Tried to equip item not in inventory: " .. item_id .. " (" .. Game.Items[item_id].name .. ")")
  626. return false
  627. end
  628. local type = Game.Items[item_id].type
  629. if type ~= Inventory.ITEM_TYPE.WEAPON and type ~= Inventory.ITEM_TYPE.ARMOR and type ~= Inventory.ITEM_TYPE.ACCESSORY then
  630. print("Tried to equip non-equipable item: " .. item_id .. " (" .. Game.Items[item_id].name .. ")")
  631. return false
  632. end
  633. local equipable = false
  634. for _, u in ipairs(Game.Items[item_id].users) do
  635. if u == char_id then
  636. equipable = true
  637. break
  638. end
  639. end
  640. if equipable == false then
  641. print("Tried to equip item " .. tostring(item_id) .. " (" .. Game.Items[item_id].name .. ") on a non valid character: " .. tostring(char_id) .. " (" .. Characters[char_id].name .. ")")
  642. return false
  643. end
  644. -- All is good, equip.
  645. if type == Inventory.ITEM_TYPE.ACCESSORY then
  646. -- Equip accessory. If any was equipped, send it to the inventory.
  647. if Characters[char_id].accessory ~= nil then
  648. Inventory.add_item(Characters[char_id].accessory, 1)
  649. end
  650. Characters[char_id].accessory = item_id
  651. Inventory.remove_item(item_id, 1)
  652. elseif type == Inventory.ITEM_TYPE.WEAPON then
  653. -- Weapon. Remove excess materia, send previuos one to the inventory.
  654. Characters.remove_excess_materia(char_id, "weapon", #(Game.Items[item_id].slots))
  655. Inventory.add_item(Characters[char_id].weapon.id, 1)
  656. Characters[char_id].weapon.id = item_id
  657. Inventory.remove_item(item_id, 1)
  658. elseif type == Inventory.ITEM_TYPE.ARMOR then
  659. -- Armor. Remove excess materia, send previuos one to the inventory.
  660. Characters.remove_excess_materia(char_id, "armor", #(Game.Items[item_id].slots))
  661. Inventory.add_item(Characters[char_id].armor.id, 1)
  662. Characters[char_id].armor.id = item_id
  663. Inventory.remove_item(item_id, 1)
  664. else
  665. return false
  666. end
  667. return true
  668. end
  669. --- Removes all materia from a character.
  670. --
  671. -- @param id ID of the character to remove materia from.
  672. Characters.remove_materia = function(id)
  673. -- TODO: Implement
  674. end
  675. --- Restores all materia to a character.
  676. --
  677. -- @param id ID of the character to restore materia to.
  678. Characters.restore_materia = function(id)
  679. -- TODO: Implement
  680. end
  681. Materia.add = function(id, ap)
  682. local ap = ap or 0
  683. if Game.Materia[id] == nil then
  684. print("Tried to add invalid materia \"" .. tostring(id) .. "\".");
  685. return
  686. end
  687. -- TODO Cap AP?
  688. for i = 1, MAX_MATERIA_SLOTS do
  689. if Materia[i] == nil then
  690. Materia[i] = {id = id, ap = ap}
  691. if Materia[i].id == Materia.ENEMY_SKILL_ID then
  692. Materia[i].ap = 0
  693. Materia[i].skills = {}
  694. for s = 1, 24 do
  695. Materia[i].skills[s] = false
  696. end
  697. end
  698. return
  699. end
  700. end
  701. -- If reached this point, it means that the materia wasn't added because all slots are full.
  702. -- TODO: Do something.
  703. return
  704. end
  705. --- Splits the party in teams.
  706. --
  707. -- @param number Number of teams (only 2 or 3).
  708. Party.split_teams = function(number)
  709. if number < 2 then
  710. number = 2
  711. elseif number > 3 then
  712. number = 3
  713. end
  714. -- TODO: Implement.
  715. end
  716. --- Displays a menu to choose a party of three.
  717. Party.choose_party = function()
  718. -- TODO: Implement
  719. end
  720. --- Special event. Removes all materia from the party, equipped and unequipped.
  721. Party.steal_materia = function()
  722. -- TODO: Implement.
  723. -- TODO: How is this reversed?
  724. end
  725. --- Implementation of OR bit operation.
  726. --
  727. -- @param a First operand.
  728. -- @param b Second operand.
  729. -- @return a | b.
  730. function bit_or(a, b)
  731. local r, m, s = 0, 2 ^ 31
  732. repeat
  733. s, a, b = a + b + m, a % m, b % m
  734. r, m = r + m * 1 % (s - a - b), m / 2
  735. until m < 1
  736. return r
  737. end
  738. --- Implementation of XOR bit operation.
  739. --
  740. -- @param a First operand.
  741. -- @param b Second operand.
  742. -- @return a ^^ b.
  743. function bit_xor(a, b)
  744. local r, m, s = 0, 2 ^ 31
  745. repeat
  746. s, a, b = a + b + m, a % m, b % m
  747. r, m = r + m * 3 % (s - a - b), m / 2
  748. until m < 1
  749. return r
  750. end
  751. --- Implementation of AND bit operation.
  752. --
  753. -- @param a First operand.
  754. -- @param b Second operand.
  755. -- @return a & b.
  756. function bit_and(a, b)
  757. local r, m, s = 0, 2 ^ 31
  758. repeat
  759. s, a, b = a + b + m, a % m, b % m
  760. r, m = r + m * 4 % (s - a - b), m / 2
  761. until m < 1
  762. return r
  763. end
  764. --- Obtains a bit from an address in a data bank
  765. --
  766. -- @param bank The bank (0-14)
  767. -- @param address The bank address (0-254)
  768. -- @param bit The bit to get (0-7)
  769. -- @return The bit value (1-0)
  770. function get_bank_bit(bank, address, bit)
  771. if bank < 0 or bank > Banks.COUNT or address < 0 or address > Banks.SIZE or bit < 0 or bit > 7 then
  772. print("ERROR: Tried to check invalid bit " .. tostring(bit) .. " from bank " .. tostring(bank) .. " at address " .. tostring(address) .. ".")
  773. return 0
  774. end
  775. local val = Banks[bank][address]
  776. return get_byte_nth_bit(val, bit)
  777. end
  778. --- Sets a bit in an address in a data bank
  779. --
  780. -- @param bank The bank (0-14)
  781. -- @param address The bank address (0-254)
  782. -- @param bit The bit to set (0-7)
  783. -- @param value The bit value (1-0)
  784. function set_bank_bit(bank, address, bit, value)
  785. if bank < 0 or bank > Banks.COUNT or address < 0 or address > Banks.SIZE or bit < 0 or bit > 7 then
  786. print("ERROR: Tried to set invalid bit " .. tostring(bit) .. " from bank " .. tostring(bank) .. " at address " .. tostring(address) .. ".")
  787. return
  788. end
  789. if value ~= 1 and value ~= 0 then
  790. print("ERROR: Tried to set bit to invalid value " .. tostring(value) .. " from bank " .. tostring(bank) .. " at address " .. tostring(address) .. ".")
  791. return
  792. end
  793. local val = Banks[bank][address]
  794. Banks[bank][address] = set_byte_nth_bit(val, bit, value)
  795. end
  796. --- Obtains a bit from an unsigned byte
  797. --
  798. -- @param value The value (0-254)
  799. -- @param bit The bit to get (0-7)
  800. -- @return The bit value (1-0)
  801. function get_byte_nth_bit(value, n)
  802. if n < 0 or n > 7 then
  803. print("ERROR: Tried to get invalid bit " .. tostring(bit) .. " from value " .. tostring(value) .. ".")
  804. return 0
  805. end
  806. if value < 0 then
  807. print("ERROR: Tried to get bit " .. tostring(bit) .. " from negative value " .. tostring(value) .. ".")
  808. return 0
  809. end
  810. if value > 254 then
  811. print("ERROR: Tried to get bit " .. tostring(bit) .. " from value " .. tostring(value) .. " that exceeds byte size.")
  812. return 0
  813. end
  814. -- Convert to binary string
  815. local s = ""
  816. local temp = value
  817. for i = 1, 8 do
  818. s = s .. tostring(temp % 2)
  819. temp = math.floor(temp / 2)
  820. end
  821. return tonumber(string.sub(s, n + 1, n + 1))
  822. end
  823. --- Obtains a bit from an unsigned byte
  824. --
  825. -- @param value The value (0-254)
  826. -- @param bit The bit to get (0-7)
  827. -- @return The bit value (1-0)
  828. function set_byte_nth_bit(value, n, bit)
  829. if bit ~= 0 and bit ~= 1 then
  830. print("ERROR: Tried to set bit " .. tostring(bit) .. " to an invalid value " .. tostring(bit) .. " from value " .. tostring(value) .. ".")
  831. return value
  832. end
  833. if n < 0 or n > 7 then
  834. print("ERROR: Tried to set invalid bit " .. tostring(bit) .. " to " .. tostring(bit) .. " from value " .. tostring(value) .. ".")
  835. return value
  836. end
  837. if value < 0 then
  838. print("ERROR: Tried to set bit " .. tostring(bit) .. " to " .. tostring(bit) .. "from negative value " .. tostring(value) .. ".")
  839. return value
  840. end
  841. if value > 254 then
  842. print("ERROR: Tried to set bit " .. tostring(bit) .. " to " .. tostring(bit) .. "from value " .. tostring(value) .. " that exceeds byte size.")
  843. return value
  844. end
  845. -- Convert to binary string.
  846. local s = ""
  847. local temp = value
  848. for i = 1, 8 do
  849. s = s .. tostring(temp % 2)
  850. temp = math.floor(temp / 2)
  851. end
  852. -- Replace the requested bit.
  853. s = string.sub(s, 1, n) .. tostring(bit) .. string.sub(s, n + 2)
  854. -- Convert back to a number.
  855. val = 0
  856. for i = 1, 8 do
  857. val = val + (tonumber(string.sub(s, i, i )) * 2 ^ (i - 1))
  858. end
  859. return math.floor(val)
  860. end
  861. set_byte_nth_bit(25, 2, 1)