PanelOptimizer.py 39 KB

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