PanelOptimizer.py 40 KB

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