PanelOptimizer.py 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024
  1. """
  2. This file is part of RuneOptimizer.
  3. RuneOptimizer is free software: you can redistribute it and/or modify it
  4. under the terms of the GNU General Public License as published by the Free
  5. Software Foundation, either version 3 of the License, or (at your option)
  6. any later version.
  7. RuneOptimizer is distributed in the hope that it will be useful, but WITHOUT
  8. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  9. FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  10. more details.
  11. You should have received a copy of the GNU General Public License along with
  12. RuneOptimizer. If not, see <https://www.gnu.org/licenses/>.
  13. """
  14. class PanelOptimizer(wx.Panel):
  15. """
  16. The optimizer form Panel.
  17. Parameters
  18. ----------
  19. unitId : string
  20. Currently selected unit ID (default None)
  21. unitStats : int[10]
  22. The current stats of the selected unit (HP, ATK, DEF, SPD, CRR, CRD,
  23. RES, ACC). (default is [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).
  24. teamIds : string[]
  25. IDs of all teams, indexed by the order they appear in the team selector.
  26. unitIdSelectorIndex : int[]
  27. Array with the IDs of all units, in the same order as displayed in
  28. unitChoice, so an ID can be retrived knowing the choice
  29. selected index.
  30. optionsSizer : wx.BoxSizer
  31. Holds every widget that is hidden until a unit is selected.
  32. unitInfoBox : wx.StaticBox
  33. Box containig the non-editable elements with a unit info.
  34. unitChoice : wx.Choice
  35. Unit selecctor. Choosing a unit triggers selectUnit.
  36. statGrid : wx.Grid.grid
  37. Table with the unit base and current stats.
  38. runeLabelList : wx.StaticText[6]
  39. Labels with all the info about the currently equipped runes.
  40. minStatSlidList : wx.Slider[6]
  41. List of sliders for the minimum selectors for each stat.
  42. minStatTextList : wx.StaticText[6]
  43. List of text inputs for the minimum selectors for each stat.
  44. statCheckListList : wx.CheckListBox[2]
  45. Tho selctors to choose stats allowed in optimization.
  46. setChoiceList : wx.Choice[3]
  47. List of selector to pik rune sets.
  48. levelChoice : wx.Choice
  49. Selector to pick the level for the rune optimization.
  50. inventoryCheck : wx.Checkbox
  51. Checkbox to use only runes in the inventory
  52. teamCheckList : wx.CheckboxList :
  53. List of selectable teams to exclude from the optimization.
  54. progressGauge : wx.Gauge
  55. A progress bar
  56. timer : wx.Timer
  57. Timer to check the update process for new output to display.
  58. process : wx.Process
  59. The update process.
  60. Methods
  61. -------
  62. selectAllTeams(event)
  63. Selects all teams in the list.
  64. deselectAllTeams(event)
  65. Deselects all teams in the list.
  66. selectUnit(event)
  67. Loads a unit info and enables optimizaton options.
  68. processResults(jsonData)
  69. Processes data obtained from RuneOptimizer.
  70. startOptimization(event)
  71. Prepares and runs a command optimization.
  72. checkProcess(event)
  73. Checks the process and update the progress bar.
  74. updateComplete(event)
  75. Called at process end, rediresct to the result view.
  76. minStatChangeBySlider(event)
  77. Changes text when a slider is changed.
  78. minStatChangeByTExt(event)
  79. Changes the slider when the text is changed.
  80. """
  81. unitId = None
  82. unitStats = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
  83. teamIds = []
  84. unidIdSelectorIndex = []
  85. optionsSizer = None
  86. unitInfoBox = None
  87. unitChoice = None
  88. statGrid = None
  89. runeLabelList = None
  90. minStatSlidList = None
  91. minStatTextList = None
  92. statCheckListList = None
  93. setChoiceList = None
  94. levelChoice = None
  95. inventoryCheck = None
  96. teamCheckList = None
  97. progressGauge = None
  98. timer = None
  99. process = None
  100. def __init__(self, parent, id=wx.ID_ANY):
  101. """Initializes the panel.
  102. Sets upt all the widgets.
  103. Parameters
  104. ----------
  105. parent : wx.*
  106. Panel parent.
  107. id : int, optional
  108. ID for the panel (default wx.ID_ANY).
  109. """
  110. # Parent constructor
  111. wx.Panel.__init__(self, parent=parent, id=id)
  112. # Prepare some fonts.
  113. monospaceFont = wx.Font(
  114. pointSize=8, family=wx.FONTFAMILY_TELETYPE,
  115. style=wx.FONTSTYLE_NORMAL, weight=wx.FONTWEIGHT_NORMAL
  116. )
  117. # Sizer for all optimization options. Will be hidden until a unit is
  118. # selected.
  119. self.optionsSizer = wx.BoxSizer(wx.VERTICAL)
  120. # Unit info box
  121. self.unitInfoBox = wx.StaticBox(
  122. self, label="Select unit:", id=wx.ID_ANY, pos=(10, 0), size=(450, 300)
  123. )
  124. # Get data from all units and populate the unit selector
  125. cursor = conn.execute("SELECT id, name FROM units ORDER BY name")
  126. names = []
  127. for row in cursor:
  128. names.append(row[1])
  129. self.unidIdSelectorIndex.append(row[0])
  130. self.unitChoice =wx.Choice(
  131. parent=self.unitInfoBox, id=wx.ID_ANY, pos=(10, 0),
  132. size=(200, 30), choices=names
  133. )
  134. self.Bind(wx.EVT_CHOICE, self.selectUnit, self.unitChoice)
  135. # Stats table
  136. self.statGrid = wx.grid.Grid(
  137. parent=self.unitInfoBox, id=wx.ID_ANY, pos=(0, 30), size=(165, 220)
  138. )
  139. self.statGrid.CreateGrid(
  140. numRows=10, numCols=2
  141. )
  142. self.statGrid.EnableEditing(False)
  143. self.statGrid.SetDefaultCellAlignment(
  144. horiz=wx.ALIGN_RIGHT, vert=wx.ALIGN_CENTRE
  145. )
  146. self.statGrid.SetDefaultCellFont(monospaceFont)
  147. self.statGrid.SetColSize(col=0, width=50)
  148. self.statGrid.SetColLabelValue(col=0, value="Base")
  149. self.statGrid.SetColSize(col=0, width=50)
  150. self.statGrid.SetColLabelValue(col=1, value="Current")
  151. self.statGrid.SetRowLabelSize(width=35)
  152. self.statGrid.SetColLabelSize(height=20)
  153. self.statGrid.SetRowSize(row=0, height=20)
  154. self.statGrid.SetRowSize(row=1, height=20)
  155. self.statGrid.SetRowSize(row=2, height=20)
  156. self.statGrid.SetRowSize(row=3, height=20)
  157. self.statGrid.SetRowSize(row=4, height=20)
  158. self.statGrid.SetRowSize(row=5, height=20)
  159. self.statGrid.SetRowSize(row=6, height=20)
  160. self.statGrid.SetRowSize(row=7, height=20)
  161. self.statGrid.SetRowSize(row=8, height=20)
  162. self.statGrid.SetRowSize(row=9, height=20)
  163. self.statGrid.SetRowLabelValue(row=0, value=" HP")
  164. self.statGrid.SetRowLabelValue(row=1, value="ATK")
  165. self.statGrid.SetRowLabelValue(row=2, value="DEF")
  166. self.statGrid.SetRowLabelValue(row=3, value="SPD")
  167. self.statGrid.SetRowLabelValue(row=4, value="CRR")
  168. self.statGrid.SetRowLabelValue(row=5, value="CRD")
  169. self.statGrid.SetRowLabelValue(row=6, value="RES")
  170. self.statGrid.SetRowLabelValue(row=7, value="ACC")
  171. self.statGrid.SetRowLabelValue(row=8, value="EHP")
  172. self.statGrid.SetRowLabelValue(row=9, value="DMG")
  173. #self.unitContent.Add(self.statGrid)
  174. #Rune set list
  175. runeBoxList = [
  176. wx.StaticBox(
  177. parent=self.unitInfoBox, label="Slot1:", id=wx.ID_ANY,
  178. pos=(265, 30), size=(80, 115)
  179. ),
  180. wx.StaticBox(
  181. parent=self.unitInfoBox, label="Slot2:", id=wx.ID_ANY,
  182. pos=(350, 30), size=(80, 115)
  183. ),
  184. wx.StaticBox(
  185. parent=self.unitInfoBox, label="Slot3:", id=wx.ID_ANY,
  186. pos=(350, 150), size=(80, 115)
  187. ),
  188. wx.StaticBox(
  189. parent=self.unitInfoBox, label="Slot4:", id=wx.ID_ANY,
  190. pos=(265, 150), size=(80, 115)
  191. ),
  192. wx.StaticBox(
  193. parent=self.unitInfoBox, label="Slot5:", id=wx.ID_ANY,
  194. pos=(180, 150), size=(80, 115)
  195. ),
  196. wx.StaticBox(
  197. parent=self.unitInfoBox, label="Slot6:", id=wx.ID_ANY,
  198. pos=(180, 30), size=(80, 115)
  199. )
  200. ]
  201. self.runeLabelList = [
  202. wx.StaticText(
  203. parent=runeBoxList[0], id=wx.ID_ANY, label="",
  204. pos=(0, 0), size=(80, 115)
  205. ),
  206. wx.StaticText(
  207. parent=runeBoxList[1], id=wx.ID_ANY, label="",
  208. pos=(0, 0), size=(80, 115)
  209. ),
  210. wx.StaticText(
  211. parent=runeBoxList[2], id=wx.ID_ANY, label="",
  212. pos=(0, 0), size=(80, 115)
  213. ),
  214. wx.StaticText(
  215. parent=runeBoxList[3], id=wx.ID_ANY, label="",
  216. pos=(0, 0), size=(80, 115)
  217. ),
  218. wx.StaticText(
  219. parent=runeBoxList[4], id=wx.ID_ANY, label="",
  220. pos=(0, 0), size=(80, 115)
  221. ),
  222. wx.StaticText(
  223. parent=runeBoxList[5], id=wx.ID_ANY, label="",
  224. pos=(0, 0), size=(80, 115)
  225. )
  226. ]
  227. monospaceFont.PointSize -= 2
  228. for i in range(0, 6):
  229. runeBoxList[i].SetFont(monospaceFont)
  230. monospaceFont.PointSize += 2
  231. # Min stats
  232. minStatBox = wx.StaticBox(
  233. parent=self, label="Min. stats:", id=wx.ID_ANY,
  234. pos=(470, 0), size=(240, 300)
  235. )
  236. wx.StaticText(
  237. parent=minStatBox, label="HP", id=wx.ID_ANY, pos=(0, 0), size=(30, 25)
  238. )
  239. wx.StaticText(
  240. parent=minStatBox, label="ATK", id=wx.ID_ANY,
  241. pos=(0, 25), size=(30, 25)
  242. )
  243. wx.StaticText(
  244. parent=minStatBox, label="DEF", id=wx.ID_ANY,
  245. pos=(0, 50), size=(30, 25)
  246. )
  247. wx.StaticText(
  248. parent=minStatBox, label="SPD", id=wx.ID_ANY,
  249. pos=(0, 75), size=(30, 25)
  250. )
  251. wx.StaticText(
  252. parent=minStatBox, label="CRR", id=wx.ID_ANY,
  253. pos=(0, 100), size=(30, 25)
  254. )
  255. wx.StaticText(
  256. parent=minStatBox, label="CRD", id=wx.ID_ANY,
  257. pos=(0, 125), size=(30, 25)
  258. )
  259. wx.StaticText(
  260. parent=minStatBox, label="RES", id=wx.ID_ANY,
  261. pos=(0, 150), size=(30, 25)
  262. )
  263. wx.StaticText(
  264. parent=minStatBox, label="ACC", id=wx.ID_ANY,
  265. pos=(0, 175), size=(30, 25)
  266. )
  267. wx.StaticText(
  268. parent=minStatBox, label="EHP", id=wx.ID_ANY,
  269. pos=(0, 200), size=(30, 25)
  270. )
  271. wx.StaticText(
  272. parent=minStatBox, label="DMG", id=wx.ID_ANY,
  273. pos=(0, 225), size=(30, 25)
  274. )
  275. self.minStatSlidList = [
  276. wx.Slider(
  277. parent=minStatBox, id=wx.ID_ANY, pos=(30, 0),
  278. size=(120, 25), name="slid0"
  279. ),
  280. wx.Slider(
  281. parent=minStatBox, id=wx.ID_ANY, pos=(30, 25),
  282. size=(120, 25), name="slid1"
  283. ),
  284. wx.Slider(
  285. parent=minStatBox, id=wx.ID_ANY, pos=(30, 50),
  286. size=(120, 25), name="slid2"
  287. ),
  288. wx.Slider(
  289. parent=minStatBox, id=wx.ID_ANY, pos=(30, 75),
  290. size=(120, 25), name="slid3"
  291. ),
  292. wx.Slider(
  293. parent=minStatBox, id=wx.ID_ANY, pos=(30, 100),
  294. size=(120, 25), name="slid4"
  295. ),
  296. wx.Slider(
  297. parent=minStatBox, id=wx.ID_ANY, pos=(30, 125),
  298. size=(120, 25), name="slid5"
  299. ),
  300. wx.Slider(
  301. parent=minStatBox, id=wx.ID_ANY, pos=(30, 150),
  302. size=(120, 25), name="slid6"
  303. ),
  304. wx.Slider(
  305. parent=minStatBox, id=wx.ID_ANY, pos=(30, 175),
  306. size=(120, 25), name="slid7"
  307. ),
  308. wx.Slider(
  309. parent=minStatBox, id=wx.ID_ANY, pos=(30, 200),
  310. size=(120, 25), name="slid8"
  311. ),
  312. wx.Slider(
  313. parent=minStatBox, id=wx.ID_ANY, pos=(30, 225),
  314. size=(120, 25), name="slid9"
  315. )
  316. ]
  317. for i in range(0, 10):
  318. self.minStatSlidList[i].SetMin(0)
  319. self.minStatSlidList[i].SetMax(0)
  320. self.minStatSlidList[i].SetValue(0)
  321. self.Bind(
  322. wx.EVT_SCROLL, self.minStatChangeBySlider, self.minStatSlidList[i]
  323. )
  324. self.minStatTextList = [
  325. wx.TextCtrl(
  326. minStatBox, id=wx.ID_ANY, value="", pos=(155, 0), size=(65, 25),
  327. style=wx.TE_RIGHT|wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB
  328. ),
  329. wx.TextCtrl(
  330. minStatBox, id=wx.ID_ANY, value="", pos=(155, 25), size=(65, 25),
  331. style=wx.TE_RIGHT|wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB
  332. ),
  333. wx.TextCtrl(
  334. minStatBox, id=wx.ID_ANY, value="", pos=(155, 50), size=(65, 25),
  335. style=wx.TE_RIGHT|wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB
  336. ),
  337. wx.TextCtrl(
  338. minStatBox, id=wx.ID_ANY, value="", pos=(155, 75), size=(65, 25),
  339. style=wx.TE_RIGHT|wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB
  340. ),
  341. wx.TextCtrl(
  342. minStatBox, id=wx.ID_ANY, value="", pos=(155, 100), size=(65, 25),
  343. style=wx.TE_RIGHT|wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB
  344. ),
  345. wx.TextCtrl(
  346. minStatBox, id=wx.ID_ANY, value="", pos=(155, 125), size=(65, 25),
  347. style=wx.TE_RIGHT|wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB
  348. ),
  349. wx.TextCtrl(
  350. minStatBox, id=wx.ID_ANY, value="", pos=(155, 150), size=(65, 25),
  351. style=wx.TE_RIGHT|wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB
  352. ),
  353. wx.TextCtrl(
  354. minStatBox, id=wx.ID_ANY, value="", pos=(155, 175), size=(65, 25),
  355. style=wx.TE_RIGHT|wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB
  356. ),
  357. wx.TextCtrl(
  358. minStatBox, id=wx.ID_ANY, value="", pos=(155, 200), size=(65, 25),
  359. style=wx.TE_RIGHT|wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB
  360. ),
  361. wx.TextCtrl(
  362. minStatBox, id=wx.ID_ANY, value="", pos=(155, 225), size=(65, 25),
  363. style=wx.TE_RIGHT|wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB
  364. )
  365. ]
  366. for i in range(0, 9):
  367. self.Bind(
  368. wx.EVT_TEXT_ENTER,
  369. self.minStatChangeByText, self.minStatTextList[i]
  370. )
  371. minStatsReset = wx.Button(
  372. parent=minStatBox, id=wx.ID_ANY, pos=(10, 250),
  373. size=(100, 20), style=wx.LC_REPORT, label="Reset all"
  374. )
  375. minStatsAdapt = wx.Button(
  376. parent=minStatBox, id=wx.ID_ANY, pos=(120, 250),
  377. size=(100, 20), style=wx.LC_REPORT, label="Adapt all")
  378. # TODO: Add binds
  379. self.optionsSizer.Add(minStatBox)
  380. # Allowed main stats for even slots
  381. names = [
  382. ["HP ", "HP% ", "ATK ", "ATK%", "DEF ", "DEF%"],
  383. ["SPD ", "CRR ", "CRD ", "RES ", "ACC "]
  384. ]
  385. statBox = wx.StaticBox(
  386. self, id=wx.ID_ANY, label="Main stats (2, 4, 6):",
  387. pos=(10, 300), size=(150, 190)
  388. )
  389. self.statCheckListList = [
  390. wx.CheckListBox(
  391. parent=statBox, id=wx.ID_ANY, pos=(5, 5),
  392. size=(70, 155), choices=names[0]
  393. ),
  394. wx.CheckListBox(
  395. parent=statBox, id=wx.ID_ANY, pos=(70, 5),
  396. size=(70, 155), choices=names[1]
  397. )
  398. ]
  399. self.optionsSizer.Add(statBox)
  400. # Rune sets
  401. names = [
  402. "", "ENERGY ", "GUARD ", "SWIFT ", "BLADE ", "RAGE ",
  403. "FOCUS ", "ENDURE ", "FATAL ", "DESPAIR", "VAMPIRE",
  404. "VIOLENT", "NEMESIS", "WILL ", "SHIELD ", "REVENGE", "DESTROY",
  405. "FIGHT ", "DETERMI", "ENHANCE", "ACCURAC", "TOLERAN"
  406. ]
  407. setBox = wx.StaticBox(
  408. self, label="Rune Sets:", id=wx.ID_ANY,
  409. pos=(170, 300), size=(115, 120)
  410. )
  411. self.setChoiceList = [
  412. wx.Choice(
  413. parent=setBox, id=wx.ID_ANY, pos=(5, 0),
  414. size=(100, 30), choices=names
  415. ),
  416. wx.Choice(
  417. parent=setBox, id=wx.ID_ANY, pos=(5, 30),
  418. size=(100, 30), choices=names
  419. ),
  420. wx.Choice(
  421. parent=setBox, id=wx.ID_ANY, pos=(5, 60),
  422. size=(100, 30), choices=names
  423. )
  424. ]
  425. self.optionsSizer.Add(setBox)
  426. # Rune level selector
  427. levelBox = wx.StaticBox(
  428. self, label="Rune Level:", id=wx.ID_ANY,
  429. pos=(170, 420), size=(115, 70)
  430. )
  431. self.levelChoice = wx.Choice(
  432. parent=levelBox, id=wx.ID_ANY, pos=(5, 0),
  433. choices=["Current", "+ 12", " + 15"]
  434. )
  435. self.optionsSizer.Add(levelBox)
  436. # Team list
  437. teams = []
  438. cursor = conn.execute("""
  439. SELECT
  440. id, name, priority
  441. FROM teams
  442. ORDER BY priority DESC;
  443. """)
  444. for row in cursor:
  445. self.teamIds.append(row[0])
  446. teams.append(str(row[1]) + " (" + str(row[2]) + ")")
  447. teamsBox = wx.StaticBox(
  448. parent=self, label="Exclude runes from units in teams",
  449. id=wx.ID_ANY, pos=(290, 300), size=(260, 190)
  450. )
  451. # Inventory only option
  452. self.optionsSizer.Add(teamsBox)
  453. self.inventoryCheck = wx.CheckBox(
  454. parent=self, id=wx.ID_ANY, label="Only runes in storage",
  455. pos=(560, 310), size=(180, 20)
  456. )
  457. self.optionsSizer.Add(self.inventoryCheck)
  458. self.teamCheckList = wx.CheckListBox(
  459. parent=teamsBox, id=wx.ID_ANY,
  460. pos=(5, 5), size=(250, 130), choices=teams
  461. )
  462. btAllTeams = wx.Button(
  463. parent=teamsBox, id=wx.ID_ANY,
  464. pos=(5, 135), size=(120, 20), label="Select all"
  465. )
  466. btNoTeams = wx.Button(
  467. parent=teamsBox, id=wx.ID_ANY,
  468. pos=(130, 135), size=(120, 20), label="Deselect all"
  469. )
  470. self.Bind(wx.EVT_BUTTON, self.selectAllTeams, btAllTeams)
  471. self.Bind(wx.EVT_BUTTON, self.deselectAllTeams, btNoTeams)
  472. # Button to start
  473. btOptimize = wx.Button(
  474. parent=self, id=wx.ID_ANY, pos=(600, 500),
  475. size=(100, 40), style=wx.LC_REPORT, label="OPTIMIZE"
  476. )
  477. self.optionsSizer.Add(btOptimize)
  478. self.Bind(wx.EVT_BUTTON, self.startOptimization, btOptimize)
  479. # Progress bar
  480. self.progressGauge = wx.Gauge(
  481. parent=self, id=wx.ID_ANY, range=20,
  482. pos=(50, 485), size=(500, 40), style=wx.GA_HORIZONTAL
  483. )
  484. # By default, hide all optimization options
  485. self.optionsSizer.ShowItems(False)
  486. def selectAllTeams(self, event = None):
  487. """Selects all teams in the list.
  488. Parameters
  489. ----------
  490. event : wxEvent, optional
  491. The event that triggered the call (default is None).
  492. """
  493. for i in range(0, len(self.teamIds)):
  494. self.teamCheckList.Check(i, True)
  495. def deselectAllTeams(self, event = None):
  496. """Deselects all teams in the list.
  497. Parameters
  498. ----------
  499. event : wxEvent, optional
  500. The event that triggered the call (default is None).
  501. """
  502. for i in range(0, len(self.teamIds)):
  503. self.teamCheckList.Check(i, False)
  504. def selectUnit(self, event = None):
  505. """Loads a unit info and enables optimizaton options.
  506. Called when a unit is selected in unitChoice. If called from an
  507. an event, the unit selected in unitChoice gets priority. When
  508. manually called, it uses unitId, so it must be set beforehand.
  509. Parameters
  510. ----------
  511. event : wxEvent, optional
  512. The event that triggered the call (default is None).
  513. """
  514. # If its called from an event, it means a unit has been selected in this
  515. # panel selector.
  516. if event != None:
  517. self.unitId = \
  518. self.unidIdSelectorIndex[self.unitChoice.GetSelection()]
  519. # It it was not called from an event, it uses self.unitId and sets the
  520. # selected unit in the unit selector automatically.
  521. else:
  522. for i in range(0, len(self.unidIdSelectorIndex)):
  523. if self.unidIdSelectorIndex[i] == self.unitId:
  524. self.unitChoice.SetSelection(i)
  525. break
  526. # Get the unit details
  527. cursor = conn.execute("""
  528. SELECT
  529. base_hp,
  530. base_atk,
  531. base_def,
  532. base_spd,
  533. base_crr,
  534. base_crd,
  535. base_res,
  536. base_acc,
  537. current_hp,
  538. current_atk,
  539. current_def,
  540. current_spd,
  541. current_crr,
  542. current_crd,
  543. current_res,
  544. current_acc,
  545. id,
  546. name
  547. FROM units
  548. WHERE
  549. id = """ + self.unitId + """;
  550. """)
  551. row = cursor.fetchone()
  552. # First, set sliders max values
  553. self.minStatSlidList[0].SetMax(50000) #HP
  554. self.minStatSlidList[1].SetMax(5000) #ATK
  555. self.minStatSlidList[2].SetMax(5000) #DEF
  556. self.minStatSlidList[3].SetMax(500) #SPD
  557. self.minStatSlidList[4].SetMax(100) #CRR
  558. self.minStatSlidList[5].SetMax(500) #CRD
  559. self.minStatSlidList[6].SetMax(100) #RES
  560. self.minStatSlidList[7].SetMax(85) #ACC
  561. self.minStatSlidList[8].SetMax(250000) #EHP
  562. self.minStatSlidList[9].SetMax(8000) #DMG
  563. self.unitInfoBox.SetLabel(row[17] + " #" + row[16])
  564. for i in range(0, 8):
  565. value = str(row[i])
  566. self.minStatSlidList[i].SetMin(int(value))
  567. if i > 3:
  568. value = value + "%"
  569. else:
  570. value = value + " "
  571. self.statGrid.SetCellValue(row=i, col=0, s=value)
  572. for i in range(0, 8):
  573. value = str(row[8 + i])
  574. self.minStatSlidList[i].SetValue(int(value))
  575. self.minStatTextList[i].SetValue(value)
  576. self.unitStats[i] = int(value)
  577. if i > 3:
  578. value = value + "%"
  579. else:
  580. value = value + " "
  581. self.statGrid.SetCellValue(row=i, col=1, s=value)
  582. # Galculate EHP and DMG
  583. baseHp = int(self.statGrid.GetCellValue(row=0, col=0))
  584. baseDef = int(self.statGrid.GetCellValue(row=2, col=0).replace("%", ""))
  585. baseEhp = math.ceil((((baseDef * 3.5) + 1140) * baseHp) / 1000)
  586. self.statGrid.SetCellValue(row=8, col=0, s=str(baseEhp))
  587. self.minStatSlidList[8].SetMin(baseEhp)
  588. currentHp = int(self.statGrid.GetCellValue(row=0, col=1))
  589. currentDef = \
  590. int(self.statGrid.GetCellValue(row=2, col=1).replace("%", ""))
  591. currentEhp = math.ceil((((currentDef * 3.5) + 1140) * currentHp) / 1000)
  592. self.statGrid.SetCellValue(row=8, col=1, s=str(currentEhp))
  593. self.minStatSlidList[8].SetValue(currentEhp)
  594. self.minStatTextList[8].SetValue(str(currentEhp))
  595. baseAtk = int(self.statGrid.GetCellValue(row=1, col=0))
  596. baseCrr = int(self.statGrid.GetCellValue(row=4, col=0).replace("%", ""))
  597. baseCrd = int(self.statGrid.GetCellValue(row=5, col=0).replace("%", ""))
  598. if (baseCrr > 100):
  599. # Dont use crit rate over 100
  600. baseCrr = 100
  601. baseDmg = math.ceil(
  602. (baseAtk * (100 - baseCrr) / 100) +
  603. ((baseAtk + (baseAtk * baseCrd / 100)) * baseCrr / 100)
  604. )
  605. self.statGrid.SetCellValue(row=9, col=0, s=str(baseDmg))
  606. self.minStatSlidList[9].SetMin(baseDmg)
  607. currentAtk = int(self.statGrid.GetCellValue(row=1, col=1))
  608. currentCrr = \
  609. int(self.statGrid.GetCellValue(row=4, col=0).replace("%", ""))
  610. currentCrd = \
  611. int(self.statGrid.GetCellValue(row=5, col=1).replace("%", ""))
  612. if (currentCrr > 100):
  613. # Dont use crit rate over 100
  614. currentCrr = 100
  615. currentDmg = math.ceil(
  616. (currentAtk * (100 - currentCrr) / 100) +
  617. ((currentAtk + (currentAtk * currentCrd / 100)) * currentCrr / 100)
  618. )
  619. self.statGrid.SetCellValue(row=9, col=1, s=str(currentDmg))
  620. self.minStatSlidList[9].SetValue(currentDmg)
  621. self.minStatTextList[9].SetValue(str(currentDmg))
  622. # Populate the runes
  623. for i in range(0, 6):
  624. self.runeLabelList[i].SetLabel("")
  625. cursor = conn.execute("""
  626. SELECT
  627. id, slot, type
  628. FROM runes
  629. WHERE
  630. unit = """ + self.unitId + """
  631. ORDER BY slot;
  632. """)
  633. i = 0
  634. currEvenStats = [0, 0, 0]
  635. currSets = [0] * 23;
  636. for row in cursor:
  637. currSets[row[2]] += 1
  638. label = ""
  639. label = label + set_names[row[2]] + "\n"
  640. # Get all stats
  641. cursorStats = conn.execute("""
  642. SELECT
  643. slot, stat, value, grind, enchant
  644. FROM rune_stats
  645. WHERE
  646. rune = """ + row[0] + """
  647. ORDER BY slot;
  648. """)
  649. j = -1
  650. for rowStats in cursorStats:
  651. if (int(row[1]) == 2 and rowStats[0] == -1):
  652. currEvenStats[0] = rowStats[1]
  653. elif (int(row[1]) == 4 and rowStats[0] == -1):
  654. currEvenStats[1] = rowStats[1]
  655. elif (int(row[1]) == 6 and rowStats[0] == -1):
  656. currEvenStats[2] = rowStats[1]
  657. while (j != rowStats[0]):
  658. label = label + "\n"
  659. j = j + 1;
  660. label = label + \
  661. stat_names[rowStats[1]] + str(rowStats[2]).rjust(4) + ""
  662. if rowStats[3] > 0:
  663. label = label + " +" + str(rowStats[3])
  664. self.runeLabelList[i].SetLabel(label)
  665. i = i + 1
  666. # Try to infer data to set the default options, reset the rest
  667. # Except the team list, rune level and storage: never reset those.
  668. self.statCheckListList[0].SetCheckedItems(())
  669. for i in range(0, 3):
  670. if currEvenStats[i] == 1: # HP
  671. self.statCheckListList[0].Check(0, True)
  672. elif currEvenStats[i] == 2: # HP%
  673. self.statCheckListList[0].Check(1, True)
  674. elif currEvenStats[i] == 3: # ATK
  675. self.statCheckListList[0].Check(2, True)
  676. elif currEvenStats[i] == 4: # ATK%
  677. self.statCheckListList[0].Check(3, True)
  678. elif currEvenStats[i] == 5: # DEF
  679. self.statCheckListList[0].Check(4, True)
  680. elif currEvenStats[i] == 6: # DEF%
  681. self.statCheckListList[0].Check(5, True)
  682. elif currEvenStats[i] == 8: # SPD
  683. self.statCheckListList[1].Check(0, True)
  684. elif currEvenStats[i] == 9: # CRR
  685. self.statCheckListList[1].Check(1, True)
  686. elif currEvenStats[i] == 10: # CRD
  687. self.statCheckListList[1].Check(2, True)
  688. elif currEvenStats[i] == 11: # RES
  689. self.statCheckListList[1].Check(3, True)
  690. elif currEvenStats[i] == 12: # ACC
  691. self.statCheckListList[1].Check(4, True)
  692. currSetList = [-1, -1, -1]
  693. j = 0
  694. for i in range(0, 23):
  695. if j < 3:
  696. # If a set of 2 has 4 or 2:
  697. if i in [
  698. 1, 2, 4, 6, 7, 9, 12, 13, 14, 15,
  699. 16, 17, 18, 19, 20, 21, 22, 23
  700. ]:
  701. if currSets[i] >= 4:
  702. currSetList[j] = i - 1
  703. currSetList[j + 1] = i - 1
  704. j += 2
  705. elif currSets[i] >= 2:
  706. currSetList[j] = i - 1
  707. j += 1
  708. # If a set of 4 has 4
  709. elif i in [3, 5, 8, 10, 11, 13]:
  710. if currSets[i] >= 4:
  711. currSetList[j] = i - 1
  712. j += 1
  713. # Rune set IDs 9 and 12 dont exist, so do a little trick with indexes.
  714. for i in range(0, 3):
  715. #if currSetList[i] != 0:
  716. actualId = currSetList[i] + 1
  717. if (actualId > 8):
  718. actualId -= 1
  719. if (actualId > 11):
  720. actualId -= 1
  721. self.setChoiceList[i].SetSelection(actualId)
  722. self.optionsSizer.ShowItems(True)
  723. def startOptimization(self, event):
  724. """Prepares and runs a command optimization.
  725. Once is done, switches to the results view.
  726. Parameters
  727. ----------
  728. event : wxEvent, optional
  729. The event that triggered the call (default is None).
  730. """
  731. # Example call:
  732. # ../RuneOptimizer optimize 7223811472 -l 15
  733. #-e rage,blade --stats atk,crr,crd -h 10000 -f 10
  734. command = executable_path + " optimize "
  735. unitId = self.unitId
  736. command += unitId
  737. level = self.levelChoice.GetSelection()
  738. if level == 1:
  739. level = "12"
  740. elif level == 2:
  741. level = "15"
  742. else:
  743. level = "current"
  744. command += (" --level " + level)
  745. #print(" Rune level: " + level)
  746. sets = ""
  747. for i in range (0, 2):
  748. selected = self.setChoiceList[i].GetString(
  749. self.setChoiceList[i].GetSelection()
  750. ).upper().replace(" ", "");
  751. for j in range(0, 22):
  752. name = set_names[j].upper()
  753. if len(name) > 7:
  754. name = name[0:7]
  755. if selected == name:
  756. sets += set_names[j].lower() + ","
  757. if len(sets) > 0:
  758. sets = sets[:-1]
  759. # TODO: ELSE ERROR
  760. command += (" --sets " + sets)
  761. stats = ""
  762. selected_stats = \
  763. self.statCheckListList[0].GetCheckedItems() + \
  764. self.statCheckListList[1].GetCheckedItems()
  765. for s in self.statCheckListList[0].GetCheckedItems():
  766. if s == 0:
  767. stats += "hpflat,"
  768. elif s == 1:
  769. stats += "hp,"
  770. elif s == 2:
  771. stats += "atkflat,"
  772. elif s == 3:
  773. stats += "atk,"
  774. elif s == 4:
  775. stats += "defflat,"
  776. elif s == 5:
  777. stats += "def,"
  778. for s in self.statCheckListList[1].GetCheckedItems():
  779. if s == 0:
  780. stats += "spd,"
  781. elif s == 1:
  782. stats += "crr,"
  783. elif s == 2:
  784. stats += "crd,"
  785. elif s == 3:
  786. stats += "res,"
  787. elif s == 4:
  788. stats += "acc,"
  789. if len(stats) > 0:
  790. stats = stats[:-1]
  791. # TODO: ELSE ERROR
  792. command += (" --stats " + stats)
  793. #print(" Main stats: " + stats)
  794. command += (" --min-hp " + str(self.minStatSlidList[0].GetValue()))
  795. command += (" --min-atk " + str(self.minStatSlidList[1].GetValue()))
  796. command += (" --min-def " + str(self.minStatSlidList[2].GetValue()))
  797. command += (" --min-spd " + str(self.minStatSlidList[3].GetValue()))
  798. command += (" --min-crr " + str(self.minStatSlidList[4].GetValue()))
  799. command += (" --min-crd " + str(self.minStatSlidList[5].GetValue()))
  800. command += (" --min-res " + str(self.minStatSlidList[6].GetValue()))
  801. command += (" --min-acc " + str(self.minStatSlidList[7].GetValue()))
  802. command += (" --min-ehp " + str(self.minStatSlidList[8].GetValue()))
  803. command += (" --min-dmg " + str(self.minStatSlidList[9].GetValue()))
  804. if self.inventoryCheck.GetValue():
  805. command += " --storage"
  806. if len(self.teamCheckList.GetCheckedItems()) > 0:
  807. command += " --no-teams "
  808. for team in self.teamCheckList.GetCheckedItems():
  809. command += str(self.teamIds[team]) + ","
  810. command = command[:-1]
  811. command += (" --gui ")
  812. print("Command: " + command)
  813. self.progressGauge.SetValue(0)
  814. # Timer ot periodically check on the process
  815. self.timer = wx.Timer(self)
  816. self.timer.Start(1000)
  817. self.Bind(wx.EVT_TIMER, self.checkProcess)
  818. # Create and execute the process
  819. self.Bind(wx.EVT_END_PROCESS, self.optimizationComplete)
  820. #print("Command: " + command)
  821. self.process = wx.Process(self)
  822. self.process.Redirect()
  823. wx.Execute(command, wx.EXEC_ASYNC, self.process)
  824. def checkProcess(self, event):
  825. """Checks the process output and updates progress bar.
  826. Parameters
  827. ----------
  828. event : wxEvent, optional
  829. The event that triggered the call.
  830. """
  831. if self.process is not None:
  832. stream = self.process.GetInputStream()
  833. if stream.CanRead():
  834. text = bytes.decode(stream.read())
  835. text = text[:-1] # Remove the last newline
  836. # Get only the last line
  837. if text.rfind("\n") != -1:
  838. text = text[text.rfind("\n") + 1:]
  839. # If the line is just a number, it's a progress indicator (1-20)
  840. if text.isnumeric() and int(text) <= 20:
  841. self.progressGauge.SetValue(int(text))
  842. else:
  843. self.timer.Stop()
  844. def optimizationComplete(self, event):
  845. """Called when the update process is complete.
  846. Hiddes the progress image, checks for errors in the output, prints a
  847. message annd sets self.updateDone and self.updateError.
  848. Parameters
  849. ----------
  850. event : wxEvent, optional
  851. The event that triggered the call.
  852. """
  853. self.timer.Stop()
  854. self.progressGauge.SetValue(20)
  855. stream = self.process.GetInputStream()
  856. jsonText = ""
  857. if stream.CanRead():
  858. text = bytes.decode(stream.read())
  859. text = text[:-1] # Remove the last newline
  860. # Get only the last line
  861. if text.rfind("\n") != -1:
  862. text = text[text.rfind("\n") + 1:]
  863. jsonText = text
  864. print('Finished. Result:\n' + jsonText)
  865. """
  866. # DEBUG: Sample data.
  867. #self.unitId = "7223811472"
  868. #json = ""{"result_count":5000,"results":[
  869. # {"id":0,"rating":215,"hp":20461,"atk":2521,"dfc":845,"spd":137,"crr":63,"crd":167,"res":29,"acc":15,"ehp":83839,"dmg":5174,"runes":["22677809846","22920616295","21755980400","25633344349","26207902579","28512560500"]},
  870. # {"id":1,"rating":195,"hp":18876,"atk":2521,"dfc":853,"spd":137,"crr":60,"crd":174,"res":37,"acc":8,"ehp":77873,"dmg":5153,"runes":["22677809846","22920616295","27654723287","25633344349","26207902579","28512560500"]},
  871. # {"id":2,"rating":185,"hp":17730,"atk":2521,"dfc":862,"spd":147,"crr":60,"crd":164,"res":28,"acc":15,"ehp":73704,"dmg":5002,"runes":["21564691276","22920616295","27654723287","25633344349","26207902579","22348038576"]},
  872. # {"id":3,"rating":179,"hp":15810,"atk":2431,"dfc":1011,"spd":137,"crr":61,"crd":160,"res":27,"acc":15,"ehp":73968,"dmg":4804,"runes":["21564691276","22920616295","27444728625","22750814840","26670411873","22348038576"]},
  873. # {"id":4,"rating":176,"hp":15319,"atk":2530,"dfc":909,"spd":142,"crr":60,"crd":165,"res":28,"acc":15,"ehp":66202,"dmg":5035,"runes":["21564691276","16759622995","27654723287","22750814840","26207902579","22348038576"]}]}
  874. #"""
  875. print ("---- RESULT OUTPUT -------------------------------------------")
  876. print(jsonText)
  877. print ("--------------------------------------------------------------")
  878. if jsonText != "":
  879. data = json.loads(
  880. jsonText,
  881. object_hook=lambda d: SimpleNamespace(**d)
  882. )
  883. if data.result_count == 0:
  884. wx.MessageBox(
  885. parent=self,
  886. message="No results found for the current settings.",
  887. caption="No results found"
  888. )
  889. else:
  890. self.GetParent().frameResults.processResults(
  891. self.unitId, jsonText
  892. )
  893. self.GetParent().ChangeSelection(3)
  894. else:
  895. wx.MessageBox(
  896. parent=self,
  897. message="No data received from RuneOptimizer.",
  898. caption="Error"
  899. )
  900. def minStatChangeBySlider(self, event):
  901. """Changes text when a slider is changed.
  902. Doesn't do validation.
  903. Parameters
  904. ----------
  905. event : wxEvent, optional
  906. The event that triggered the call (default is None).
  907. """
  908. slidId = int(event.GetEventObject().GetName().replace("slid", ""))
  909. self.minStatTextList[slidId].SetValue(
  910. str(event.GetEventObject().GetValue())
  911. )
  912. def minStatChangeByText(self, event):
  913. """Changes the slider when the text is changed.
  914. Validates the text value.
  915. Parameters
  916. ----------
  917. event : wxEvent, optional
  918. The event that triggered the call (default is None).
  919. """
  920. textId = int(event.GetEventObject().GetName().replace("tx", ""))
  921. if event.GetEventObject().GetValue().isdigit() == False:
  922. event.GetEventObject().SetValue(
  923. str(self.minStatSlidList[textId].GetValue())
  924. )
  925. value = int(event.GetEventObject().GetValue())
  926. minValue = self.minStatSlidList[textId].GetMin()
  927. maxValue = self.minStatSlidList[textId].GetMax()
  928. if value < minValue:
  929. value = minValue
  930. event.GetEventObject().SetValue(str(value))
  931. elif value > maxValue:
  932. value = maxValue
  933. event.GetEventObject().SetValue(str(value))
  934. self.minStatSlidList[textId].SetValue(value)