PanelOptimizer.py 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329
  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. Displays several optimization options to build and run the optimize
  18. command.
  19. Methods
  20. -------
  21. select_unit(event=None, unit_id=None)
  22. Loads a unit info and enables optimizaton options.
  23. """
  24. _selected_unit = None
  25. _team_ids = []
  26. _unit_id_selector_index = []
  27. _options_sizer = None
  28. _unit_choice = None
  29. _stat_grid = None
  30. _name_label = None
  31. _stars_label = None
  32. _level_label = None
  33. _id_label = None
  34. _priority_label = None
  35. _team_label = None
  36. _rune_label_list = None
  37. _min_stat_slid_list = None
  38. _min_stat_text_list = None
  39. _stat_checklist = None
  40. _set_choice_list = None
  41. _level_choice = None
  42. _inventory_check = None
  43. _teams_check_list = None
  44. _progress_gauge = None
  45. _timer = None
  46. _process = None
  47. def __init__(self, parent, id=wx.ID_ANY):
  48. """
  49. Initializes the panel.
  50. Sets upt all the widgets.
  51. Parameters
  52. ----------
  53. parent : wx.*
  54. Panel parent.
  55. id : int, optional
  56. ID for the panel (default wx.ID_ANY).
  57. """
  58. # Parent constructor
  59. wx.Panel.__init__(self, parent=parent, id=id)
  60. # Prepare some fonts.
  61. monospace_font = wx.Font(
  62. pointSize=8, family=wx.FONTFAMILY_TELETYPE,
  63. style=wx.FONTSTYLE_NORMAL, weight=wx.FONTWEIGHT_NORMAL
  64. )
  65. small_font = wx.Font(
  66. pointSize=7, family=wx.FONTFAMILY_DEFAULT,
  67. style=wx.FONTSTYLE_NORMAL, weight=wx.FONTWEIGHT_NORMAL
  68. )
  69. bold_font = wx.Font(
  70. pointSize=10, family=wx.FONTFAMILY_DEFAULT,
  71. style=wx.FONTSTYLE_NORMAL, weight=wx.FONTWEIGHT_BOLD
  72. )
  73. # Prepare the help icon
  74. icon_path = \
  75. os.path.dirname(os.path.realpath(__file__)) + "/res/icon/help.png"
  76. if os.name == 'nt':
  77. icon_path = os.path.dirname(__file__)
  78. if icon_path == "":
  79. icon_path += "."
  80. icon_path += "\\res\\icon\\help.png"
  81. help_icon = wx.Bitmap(name=icon_path, type=wx.BITMAP_TYPE_PNG)
  82. # Sizer for all optimization options. Will be hidden until a unit is
  83. # selected.
  84. self._options_sizer = wx.BoxSizer(wx.VERTICAL)
  85. # Unit info box
  86. unit_info_box = wx.StaticBox(
  87. self, label="Select unit:", id=wx.ID_ANY,
  88. pos=(10, 0), size=(270, 530)
  89. )
  90. # Get data from all units and populate the unit selector
  91. cursor = conn.execute("SELECT id, name FROM units ORDER BY name")
  92. unit_names = []
  93. for row in cursor:
  94. unit_names.append(row[1])
  95. self._unit_id_selector_index.append(row[0])
  96. self._unit_choice = wx.Choice(
  97. parent=unit_info_box, id=wx.ID_ANY, pos=(10, 0),
  98. size=(200, 30), choices=unit_names
  99. )
  100. self._unit_choice.Bind(wx.EVT_CHOICE, self.select_unit)
  101. # Stats table
  102. self._stat_grid = wx.grid.Grid(
  103. parent=unit_info_box, id=wx.ID_ANY,
  104. pos=(10, 30),size=(135, 220)
  105. )
  106. self._stat_grid.CreateGrid(
  107. numRows=10, numCols=2
  108. )
  109. self._stat_grid.EnableEditing(False)
  110. self._stat_grid.SetDefaultCellAlignment(
  111. horiz=wx.ALIGN_RIGHT, vert=wx.ALIGN_CENTRE
  112. )
  113. self._stat_grid.SetDefaultCellFont(monospace_font)
  114. self._stat_grid.SetColSize(col=0, width=50)
  115. self._stat_grid.SetColLabelValue(col=0, value="Base")
  116. self._stat_grid.SetColSize(col=1, width=50)
  117. self._stat_grid.SetColLabelValue(col=1, value="Curr.")
  118. self._stat_grid.SetRowLabelSize(width=35)
  119. self._stat_grid.SetColLabelSize(height=20)
  120. self._stat_grid.SetRowSize(row=0, height=20)
  121. self._stat_grid.SetRowSize(row=1, height=20)
  122. self._stat_grid.SetRowSize(row=2, height=20)
  123. self._stat_grid.SetRowSize(row=3, height=20)
  124. self._stat_grid.SetRowSize(row=4, height=20)
  125. self._stat_grid.SetRowSize(row=5, height=20)
  126. self._stat_grid.SetRowSize(row=6, height=20)
  127. self._stat_grid.SetRowSize(row=7, height=20)
  128. self._stat_grid.SetRowSize(row=8, height=20)
  129. self._stat_grid.SetRowSize(row=9, height=20)
  130. self._stat_grid.SetRowLabelValue(row=0, value=" HP")
  131. self._stat_grid.SetRowLabelValue(row=1, value="ATK")
  132. self._stat_grid.SetRowLabelValue(row=2, value="DEF")
  133. self._stat_grid.SetRowLabelValue(row=3, value="SPD")
  134. self._stat_grid.SetRowLabelValue(row=4, value="CRR")
  135. self._stat_grid.SetRowLabelValue(row=5, value="CRD")
  136. self._stat_grid.SetRowLabelValue(row=6, value="RES")
  137. self._stat_grid.SetRowLabelValue(row=7, value="ACC")
  138. self._stat_grid.SetRowLabelValue(row=8, value="EHP")
  139. self._stat_grid.SetRowLabelValue(row=9, value="DMG")
  140. self._name_label = wx.StaticText(
  141. parent=unit_info_box, id=wx.ID_ANY, label="",
  142. pos=(160, 40), size=(120, 25)
  143. )
  144. self._name_label.SetFont(bold_font)
  145. self._stars_label = wx.StaticText(
  146. parent=unit_info_box, id=wx.ID_ANY, label="",
  147. pos=(160, 70), size=(120, 25)
  148. )
  149. self._level_label = wx.StaticText(
  150. parent=unit_info_box, id=wx.ID_ANY, label="",
  151. pos=(160, 90), size=(120, 25)
  152. )
  153. self._id_label = wx.StaticText(
  154. parent=unit_info_box, id=wx.ID_ANY, label="",
  155. pos=(160, 110), size=(120, 25)
  156. )
  157. self._priority_label = wx.StaticText(
  158. parent=unit_info_box, id=wx.ID_ANY, label="",
  159. pos=(160, 130), size=(120, 25)
  160. )
  161. self._teams_label = wx.StaticText(
  162. parent=unit_info_box, id=wx.ID_ANY, label="",
  163. pos=(160, 150), size=(120, 25)
  164. )
  165. #Rune set list
  166. rune_box_list = [
  167. wx.StaticBox(
  168. parent=unit_info_box, label="Slot1:", id=wx.ID_ANY,
  169. pos=(95, 260), size=(80, 115)
  170. ),
  171. wx.StaticBox(
  172. parent=unit_info_box, label="Slot2:", id=wx.ID_ANY,
  173. pos=(180, 260), size=(80, 115)
  174. ),
  175. wx.StaticBox(
  176. parent=unit_info_box, label="Slot3:", id=wx.ID_ANY,
  177. pos=(180, 380), size=(80, 115)
  178. ),
  179. wx.StaticBox(
  180. parent=unit_info_box, label="Slot4:", id=wx.ID_ANY,
  181. pos=(95, 380), size=(80, 115)
  182. ),
  183. wx.StaticBox(
  184. parent=unit_info_box, label="Slot5:", id=wx.ID_ANY,
  185. pos=(10, 380), size=(80, 115)
  186. ),
  187. wx.StaticBox(
  188. parent=unit_info_box, label="Slot6:", id=wx.ID_ANY,
  189. pos=(10, 260), size=(80, 115)
  190. )
  191. ]
  192. for i in range(0, 6):
  193. rune_box_list[i].SetFont(monospace_font)
  194. self._rune_label_list = [
  195. wx.StaticText(
  196. parent=rune_box_list[0], id=wx.ID_ANY, label="",
  197. pos=(0, 0), size=(80, 115)
  198. ),
  199. wx.StaticText(
  200. parent=rune_box_list[1], id=wx.ID_ANY, label="",
  201. pos=(0, 0), size=(80, 115)
  202. ),
  203. wx.StaticText(
  204. parent=rune_box_list[2], id=wx.ID_ANY, label="",
  205. pos=(0, 0), size=(80, 115)
  206. ),
  207. wx.StaticText(
  208. parent=rune_box_list[3], id=wx.ID_ANY, label="",
  209. pos=(0, 0), size=(80, 115)
  210. ),
  211. wx.StaticText(
  212. parent=rune_box_list[4], id=wx.ID_ANY, label="",
  213. pos=(0, 0), size=(80, 115)
  214. ),
  215. wx.StaticText(
  216. parent=rune_box_list[5], id=wx.ID_ANY, label="",
  217. pos=(0, 0), size=(80, 115)
  218. )
  219. ]
  220. # Min stats
  221. min_stat_box = wx.StaticBox(
  222. parent=self, label="Min. stats:", id=wx.ID_ANY,
  223. pos=(300, 0), size=(240, 300)
  224. )
  225. min_stat_help = wx.BitmapButton(
  226. parent=self, id=wx.ID_ANY, bitmap=help_icon,
  227. pos=(370, 0), size=(20, 20)
  228. )
  229. min_stat_help.Bind(
  230. wx.EVT_MOTION,
  231. lambda event: self._show_help(
  232. event,
  233. text=
  234. "These are the minimum stats to aim for during the optimization."
  235. )
  236. )
  237. min_stat_help.Bind(
  238. wx.EVT_BUTTON,
  239. lambda event: self._open_help(
  240. event,
  241. title="Minimum stats",
  242. text=
  243. "These are the minimum stats to aim for during the "
  244. + "optimization.\n\nA rune combination will not be accepted "
  245. + "unless every stat has a value equal or higher than those "
  246. + "defined here."
  247. )
  248. )
  249. wx.StaticText(
  250. parent=min_stat_box, label="HP", id=wx.ID_ANY,
  251. pos=(0, 0), size=(30, 25)
  252. )
  253. wx.StaticText(
  254. parent=min_stat_box, label="ATK", id=wx.ID_ANY,
  255. pos=(0, 25), size=(30, 25)
  256. )
  257. wx.StaticText(
  258. parent=min_stat_box, label="DEF", id=wx.ID_ANY,
  259. pos=(0, 50), size=(30, 25)
  260. )
  261. wx.StaticText(
  262. parent=min_stat_box, label="SPD", id=wx.ID_ANY,
  263. pos=(0, 75), size=(30, 25)
  264. )
  265. wx.StaticText(
  266. parent=min_stat_box, label="CRR", id=wx.ID_ANY,
  267. pos=(0, 100), size=(30, 25)
  268. )
  269. wx.StaticText(
  270. parent=min_stat_box, label="CRD", id=wx.ID_ANY,
  271. pos=(0, 125), size=(30, 25)
  272. )
  273. wx.StaticText(
  274. parent=min_stat_box, label="RES", id=wx.ID_ANY,
  275. pos=(0, 150), size=(30, 25)
  276. )
  277. wx.StaticText(
  278. parent=min_stat_box, label="ACC", id=wx.ID_ANY,
  279. pos=(0, 175), size=(30, 25)
  280. )
  281. wx.StaticText(
  282. parent=min_stat_box, label="EHP", id=wx.ID_ANY,
  283. pos=(0, 200), size=(30, 25)
  284. )
  285. wx.StaticText(
  286. parent=min_stat_box, label="DMG", id=wx.ID_ANY,
  287. pos=(0, 225), size=(30, 25)
  288. )
  289. self._min_stat_slid_list = [
  290. wx.Slider(
  291. parent=min_stat_box, id=wx.ID_ANY, pos=(30, 0),
  292. size=(120, 25), name="slid0"
  293. ),
  294. wx.Slider(
  295. parent=min_stat_box, id=wx.ID_ANY, pos=(30, 25),
  296. size=(120, 25), name="slid1"
  297. ),
  298. wx.Slider(
  299. parent=min_stat_box, id=wx.ID_ANY, pos=(30, 50),
  300. size=(120, 25), name="slid2"
  301. ),
  302. wx.Slider(
  303. parent=min_stat_box, id=wx.ID_ANY, pos=(30, 75),
  304. size=(120, 25), name="slid3"
  305. ),
  306. wx.Slider(
  307. parent=min_stat_box, id=wx.ID_ANY, pos=(30, 100),
  308. size=(120, 25), name="slid4"
  309. ),
  310. wx.Slider(
  311. parent=min_stat_box, id=wx.ID_ANY, pos=(30, 125),
  312. size=(120, 25), name="slid5"
  313. ),
  314. wx.Slider(
  315. parent=min_stat_box, id=wx.ID_ANY, pos=(30, 150),
  316. size=(120, 25), name="slid6"
  317. ),
  318. wx.Slider(
  319. parent=min_stat_box, id=wx.ID_ANY, pos=(30, 175),
  320. size=(120, 25), name="slid7"
  321. ),
  322. wx.Slider(
  323. parent=min_stat_box, id=wx.ID_ANY, pos=(30, 200),
  324. size=(120, 25), name="slid8"
  325. ),
  326. wx.Slider(
  327. parent=min_stat_box, id=wx.ID_ANY, pos=(30, 225),
  328. size=(120, 25), name="slid9"
  329. )
  330. ]
  331. for i in range(0, 10):
  332. self._min_stat_slid_list[i].SetMin(0)
  333. self._min_stat_slid_list[i].SetMax(0)
  334. self._min_stat_slid_list[i].SetValue(0)
  335. self._min_stat_slid_list[i].Bind(
  336. wx.EVT_SCROLL, self._min_stat_change_by_slider
  337. )
  338. self._min_stat_text_list = [
  339. wx.TextCtrl(
  340. parent=min_stat_box, id=wx.ID_ANY, value="",
  341. pos=(155, 0), size=(65, 25), name="tx0",
  342. style=wx.TE_RIGHT|wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB
  343. ),
  344. wx.TextCtrl(
  345. parent=min_stat_box, id=wx.ID_ANY, value="",
  346. pos=(155, 25), size=(65, 25), name="tx1",
  347. style=wx.TE_RIGHT|wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB
  348. ),
  349. wx.TextCtrl(
  350. parent=min_stat_box, id=wx.ID_ANY, value="",
  351. pos=(155, 50), size=(65, 25), name="tx2",
  352. style=wx.TE_RIGHT|wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB
  353. ),
  354. wx.TextCtrl(
  355. parent=min_stat_box, id=wx.ID_ANY, value="",
  356. pos=(155, 75), size=(65, 25), name="tx3",
  357. style=wx.TE_RIGHT|wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB
  358. ),
  359. wx.TextCtrl(
  360. parent=min_stat_box, id=wx.ID_ANY, value="",
  361. pos=(155, 100), size=(65, 25), name="tx4",
  362. style=wx.TE_RIGHT|wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB
  363. ),
  364. wx.TextCtrl(
  365. parent=min_stat_box, id=wx.ID_ANY, value="",
  366. pos=(155, 125), size=(65, 25), name="tx5",
  367. style=wx.TE_RIGHT|wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB
  368. ),
  369. wx.TextCtrl(
  370. parent=min_stat_box, id=wx.ID_ANY, value="",
  371. pos=(155, 150), size=(65, 25), name="tx6",
  372. style=wx.TE_RIGHT|wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB
  373. ),
  374. wx.TextCtrl(
  375. parent=min_stat_box, id=wx.ID_ANY, value="",
  376. pos=(155, 175), size=(65, 25), name="tx7",
  377. style=wx.TE_RIGHT|wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB
  378. ),
  379. wx.TextCtrl(
  380. parent=min_stat_box, id=wx.ID_ANY, value="",
  381. pos=(155, 200), size=(65, 25), name="tx8",
  382. style=wx.TE_RIGHT|wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB
  383. ),
  384. wx.TextCtrl(
  385. parent=min_stat_box, id=wx.ID_ANY, value="",
  386. pos=(155, 225), size=(65, 25), name="tx9",
  387. style=wx.TE_RIGHT|wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB
  388. )
  389. ]
  390. for i in range(0, 9):
  391. self._min_stat_text_list[i].Bind(
  392. wx.EVT_TEXT, self._min_stat_change_by_text
  393. )
  394. wx.Button(
  395. parent=min_stat_box, id=wx.ID_ANY, pos=(10, 250),
  396. size=(100, 20), style=wx.LC_REPORT, label="Reset all"
  397. ).Bind(wx.EVT_BUTTON, self._reset_stats)
  398. wx.Button(
  399. parent=min_stat_box, id=wx.ID_ANY, pos=(120, 250),
  400. size=(100, 20), style=wx.LC_REPORT, label="Adapt all"
  401. ).Bind(wx.EVT_BUTTON, self._adapt_stats)
  402. self._options_sizer.Add(min_stat_box)
  403. self._options_sizer.Add(min_stat_help)
  404. # Allowed main stats for even slots
  405. names = [
  406. "HP ", "HP% ", "ATK ", "ATK%", "DEF ", "DEF%",
  407. "SPD ", "CRR ", "CRD ", "RES ", "ACC "
  408. ]
  409. stat_box = wx.StaticBox(
  410. self, id=wx.ID_ANY, label="Main stats:",
  411. pos=(550, 70), size=(110, 315)
  412. )
  413. stat_help = wx.BitmapButton(
  414. parent=self, id=wx.ID_ANY, bitmap=help_icon,
  415. pos=(630, 70), size=(20, 20)
  416. )
  417. stat_help.Bind(
  418. wx.EVT_MOTION,
  419. lambda event: self._show_help(
  420. event, text="Main stats for runes in slots 2, 4 and 6."
  421. )
  422. )
  423. stat_help.Bind(
  424. wx.EVT_BUTTON,
  425. lambda event: self._open_help(
  426. event,
  427. title="Main stats",
  428. text=
  429. "Stats for runes in slots 2, 4 and 6.\n\n" +
  430. + "Runes in slots 2, 4 and 6 can only have one of the main "
  431. + "stats selected here, so make sure to select at least one "
  432. + "stat that can appear as main stat in each of the three "
  433. + "slots. This has no effect for runes in slots 1, 3 and 5."
  434. )
  435. )
  436. self._stat_checklist = wx.CheckListBox(
  437. parent=stat_box, id=wx.ID_ANY, pos=(5, 5),
  438. size=(100, 285), choices=names
  439. )
  440. self._options_sizer.Add(stat_box)
  441. self._options_sizer.Add(stat_help)
  442. # Rune sets
  443. set_names = [
  444. "", "ENERGY ", "GUARD ", "SWIFT ", "BLADE ", "RAGE ",
  445. "FOCUS ", "ENDURE ", "FATAL ", "DESPAIR", "VAMPIRE",
  446. "VIOLENT", "NEMESIS", "WILL ", "SHIELD ", "REVENGE", "DESTROY",
  447. "FIGHT ", "DETERMI", "ENHANCE", "ACCURAC", "TOLERAN"
  448. ]
  449. set_box = wx.StaticBox(
  450. self, label="Rune Sets:", id=wx.ID_ANY,
  451. pos=(550, 0), size=(330, 60)
  452. )
  453. set_help = wx.BitmapButton(
  454. parent=self, id=wx.ID_ANY, bitmap=help_icon,
  455. pos=(620, 0), size=(20, 20)
  456. )
  457. set_help.Bind(
  458. wx.EVT_MOTION,
  459. lambda event: self._show_help(
  460. event, text="The unit must have at least these rune sets."
  461. )
  462. )
  463. set_help.Bind(
  464. wx.EVT_BUTTON,
  465. lambda event: self._open_help(
  466. event,
  467. title="Rune sets",
  468. text=
  469. "The unit must have at least these rune sets.\n\n" +
  470. + "A rune combination will not be accepted unless it contains "
  471. + "at least these sets. At least one set must be selected, and "
  472. + "up to three can be specified if they do they do not sum more "
  473. + "than 6 runes."
  474. )
  475. )
  476. self._set_choice_list = [
  477. wx.Choice(
  478. parent=set_box, id=wx.ID_ANY, pos=(5, 0),
  479. size=(100, 30), choices=set_names
  480. ),
  481. wx.Choice(
  482. parent=set_box, id=wx.ID_ANY, pos=(110, 0),
  483. size=(100, 30), choices=set_names
  484. ),
  485. wx.Choice(
  486. parent=set_box, id=wx.ID_ANY, pos=(215, 0),
  487. size=(100, 30), choices=set_names
  488. )
  489. ]
  490. self._options_sizer.Add(set_box)
  491. self._options_sizer.Add(set_help)
  492. # Rune level selector
  493. _level_box = wx.StaticBox(
  494. parent=self, label="Rune Level:", id=wx.ID_ANY,
  495. pos=(550, 390), size=(110, 60)
  496. )
  497. _level_help = wx.BitmapButton(
  498. parent=self, id=wx.ID_ANY, bitmap=help_icon,
  499. pos=(635, 390), size=(20, 20)
  500. )
  501. _level_help.Bind(
  502. wx.EVT_MOTION,
  503. lambda event: self._show_help(
  504. event,
  505. text=
  506. "Unit can be considered to be at it's current level, at level "
  507. + "12 or level 15."
  508. )
  509. )
  510. _level_help.Bind(
  511. wx.EVT_BUTTON,
  512. lambda event: self._open_help(
  513. event,
  514. title="Rune level",
  515. text=
  516. "Unit can be considered to be at it's current level, at level "
  517. + "12 or level 15.\n\n"
  518. + "When calculating optimizations, the optimizer can consider "
  519. + "the stats a rune will have when it is powered up to level 12 "
  520. + "or 15. This is usefull to get more results using the runes "
  521. + "you havn't yer powered up.\n\n Note that this is only "
  522. + "applied for the values of the rune main stats, and possible "
  523. + "new substats added during power ups are not ocnsidered."
  524. )
  525. )
  526. self._level_choice = wx.Choice(
  527. parent=_level_box, id=wx.ID_ANY, pos=(5, 0),
  528. choices=["Current", "+ 12", " + 15"]
  529. )
  530. self._options_sizer.Add(_level_box)
  531. self._options_sizer.Add(_level_help)
  532. _opt_set_box = wx.StaticBox(
  533. self, label="Other Rune Sets:", id=wx.ID_ANY,
  534. pos=(670, 70), size=(220, 315)
  535. )
  536. _opt_set_help = wx.BitmapButton(
  537. parent=self, id=wx.ID_ANY, bitmap=help_icon,
  538. pos=(790, 70), size=(20, 20)
  539. )
  540. _opt_set_help.Bind(
  541. wx.EVT_MOTION,
  542. lambda event: self._show_help(
  543. event,
  544. text="Other rune sets that can be used for the unit."
  545. )
  546. )
  547. _opt_set_help.Bind(
  548. wx.EVT_BUTTON,
  549. lambda event: self._open_help(
  550. event,
  551. title="Other rune sets",
  552. text=
  553. "If the sets selected in the 'Rune sets' section dont complete "
  554. + "a 6 rune pack, any rune of the selected sets selected here "
  555. + "will be used to fill out the blanks.\n\nRunes not selected "
  556. + "neither in the 'Rune sets' section nor here will not be "
  557. + "considered during thhe optimization. There is no need to "
  558. + "select a set here if it's already selected in the 'Rune "
  559. + "sets' section.\n\n Note that sets selected here are not "
  560. + "guaranteed to form a full set if the option 'Allow broken "
  561. + "sets' is checked (for example, marking 'Energy' and 'Guard' "
  562. + "here, while also marking 'Allow broken sets' can end up with "
  563. + "the unit having one rune of each, thus not getting any of "
  564. + "the set bonuses)."
  565. )
  566. )
  567. self._options_sizer.Add(_opt_set_box)
  568. self._options_sizer.Add(_opt_set_help)
  569. self._opt_set_checklist_list = [
  570. wx.CheckListBox(
  571. parent=_opt_set_box, id=wx.ID_ANY, pos=(5, 5),
  572. size=(100, 285), choices=set_names[1:12]
  573. ),
  574. wx.CheckListBox(
  575. parent=_opt_set_box, id=wx.ID_ANY, pos=(105, 5),
  576. size=(100, 285), choices=set_names[12:25]
  577. )
  578. ]
  579. # Team list
  580. teams_formatted = []
  581. for team in teams.values():
  582. self._team_ids.append(team.id)
  583. teams_formatted.append(team.name + " (" + str(team.priority) + ")")
  584. teams_box = wx.StaticBox(
  585. parent=self, label="Exclude runes from units in teams",
  586. id=wx.ID_ANY, pos=(300, 300), size=(240, 230)
  587. )
  588. teams_help = wx.BitmapButton(
  589. parent=self, id=wx.ID_ANY, bitmap=help_icon,
  590. pos=(515, 300), size=(20, 20)
  591. )
  592. teams_help.Bind(
  593. wx.EVT_MOTION,
  594. lambda event: self._show_help(
  595. event, text="Exclude runes from units in teams selected teams."
  596. )
  597. )
  598. teams_help.Bind(
  599. wx.EVT_BUTTON,
  600. lambda event: self._open_help(
  601. event,
  602. title="Exclude runes from units in teams.",
  603. text=
  604. "During the optimization, no rune will be considered if it's "
  605. + "equiped by any unit in any of the selected teams.\n\n Note "
  606. + "that runes equiped to the unit being optimized will always "
  607. + "be considered, even if the unit is on a selected team."
  608. )
  609. )
  610. self._options_sizer.Add(teams_box)
  611. self._options_sizer.Add(teams_help)
  612. self._teams_check_list = wx.CheckListBox(
  613. parent=teams_box, id=wx.ID_ANY,
  614. pos=(5, 5), size=(230, 170), choices=teams_formatted
  615. )
  616. wx.Button(
  617. parent=teams_box, id=wx.ID_ANY,
  618. pos=(10, 180), size=(105, 20), label="Select all"
  619. ).Bind(wx.EVT_BUTTON, self._select_all_teams)
  620. btNoTeams = wx.Button(
  621. parent=teams_box, id=wx.ID_ANY,
  622. pos=(120, 180), size=(105, 20), label="Deselect all"
  623. ).Bind(wx.EVT_BUTTON, self._deselect_all_teams)
  624. # Broken sets option
  625. self._broken_check = wx.CheckBox(
  626. parent=self, id=wx.ID_ANY, label="Allow broken sets",
  627. pos=(670, 400), size=(180, 20)
  628. )
  629. _broken_help = wx.BitmapButton(
  630. parent=self, id=wx.ID_ANY, bitmap=help_icon,
  631. pos=(850, 400), size=(20, 20)
  632. )
  633. _broken_help.Bind(
  634. wx.EVT_MOTION,
  635. lambda event: self._show_help(
  636. event,
  637. text="Allow runes in 'Other Rune Sets' to form broken sets."
  638. )
  639. )
  640. _broken_help.Bind(
  641. wx.EVT_BUTTON,
  642. lambda event: self._open_help(
  643. event,
  644. title="Allow broken sets.",
  645. text=
  646. "Allows the optimizer to consider broken sets using the runes "
  647. + "selected in 'Other Rune Sets'.\n\n This will probably get "
  648. + "more results with better runes, at the expense of not "
  649. + "getting rune set bonuses. Runes selected in 'Rune Sets' will "
  650. + "always form complete sets."
  651. )
  652. )
  653. self._options_sizer.Add(self._broken_check)
  654. self._options_sizer.Add(_broken_help)
  655. # Inventory only option
  656. self._inventory_check = wx.CheckBox(
  657. parent=self, id=wx.ID_ANY, label="Only runes in storage",
  658. pos=(670, 430), size=(180, 20)
  659. )
  660. _inventory_help = wx.BitmapButton(
  661. parent=self, id=wx.ID_ANY, bitmap=help_icon,
  662. pos=(850, 430), size=(20, 20)
  663. )
  664. _inventory_help.Bind(
  665. wx.EVT_MOTION,
  666. lambda event: self._show_help(
  667. event, text="Don use runes assigned to any unit."
  668. )
  669. )
  670. _inventory_help.Bind(
  671. wx.EVT_BUTTON,
  672. lambda event: self._open_help(
  673. event,
  674. title="Only runes in storage.",
  675. text=
  676. "When selected, the optimizer will consider only the runes in "
  677. + "storage, and it will not use any rune assigned to any other "
  678. + "unit.\n\n Note that runes equiped by the unit being "
  679. + "optimized will always be considered."
  680. )
  681. )
  682. self._options_sizer.Add(self._inventory_check)
  683. self._options_sizer.Add(_inventory_help)
  684. # Threads slider
  685. thread_label = wx.StaticText(
  686. parent=self, id=wx.ID_ANY, label="Threads:",
  687. pos=(550, 470), size=(180, 45)
  688. )
  689. thread_help = wx.BitmapButton(
  690. parent=self, id=wx.ID_ANY, bitmap=help_icon,
  691. pos=(570, 490), size=(20, 20)
  692. )
  693. thread_help.Bind(
  694. wx.EVT_MOTION,
  695. lambda event: self._show_help(
  696. event,
  697. text=
  698. "How many threads to use. The more, the faster the optimization."
  699. )
  700. )
  701. thread_help.Bind(
  702. wx.EVT_BUTTON,
  703. lambda event: self._open_help(
  704. event,
  705. title="Threads.",
  706. text=
  707. "Select how many threads to use during optimization.\n\n As a "
  708. + "rule, the more threads used, the faster the optimization "
  709. + "will be, but for optimal results, select as many threads as "
  710. + "CPU cores your computer has."
  711. )
  712. )
  713. self._thread_slider = wx.Slider(
  714. parent=self, id=wx.ID_ANY, pos=(620, 450), size=(200, 65),
  715. style=wx.SL_AUTOTICKS|wx.SL_LABELS|wx.SL_SELRANGE
  716. )
  717. self._thread_slider.SetMin(1)
  718. self._thread_slider.SetMax(8)
  719. cores = multiprocessing.cpu_count()
  720. if cores < 1:
  721. cores = 1
  722. if cores > 8:
  723. cores = 8;
  724. self._thread_slider.SetValue(cores)
  725. self._options_sizer.Add(thread_label)
  726. self._options_sizer.Add(self._thread_slider)
  727. self._options_sizer.Add(thread_help)
  728. # Button to start
  729. btOptimize = wx.Button(
  730. parent=self, id=wx.ID_ANY, pos=(750, 550),
  731. size=(100, 40), style=wx.LC_REPORT, label="OPTIMIZE"
  732. )
  733. self._options_sizer.Add(btOptimize)
  734. self.Bind(wx.EVT_BUTTON, self._start_optimization, btOptimize)
  735. # Progress bar
  736. self._progress_gauge = wx.Gauge(
  737. parent=self, id=wx.ID_ANY, range=20,
  738. pos=(50, 535), size=(650, 40), style=wx.GA_HORIZONTAL
  739. )
  740. self._options_sizer.Add(self._progress_gauge)
  741. # By default, hide all optimization options
  742. self._options_sizer.ShowItems(False)
  743. def _show_help(self, event, text=None):
  744. """Shows a help tooltip.
  745. Parameters
  746. ----------
  747. event : wxEvent
  748. The event that triggered the call.
  749. text : string, optional
  750. The text for the tooltip.
  751. """
  752. event.GetEventObject().SetToolTip(wx.ToolTip(text))
  753. def _open_help(self, event, title=None, text=None):
  754. """Shows a help dialog.
  755. Parameters
  756. ----------
  757. event : wxEvent
  758. The event that triggered the call.
  759. title : string, optional
  760. The text for the dialog title.
  761. text : string, optional
  762. The text for the dialog body.
  763. """
  764. wx.MessageBox(
  765. parent=self, message=text, caption=title, style=wx.ICON_INFORMATION
  766. )
  767. def _select_all_teams(self, event = None):
  768. """Selects all teams in the list.
  769. Parameters
  770. ----------
  771. event : wxEvent, optional
  772. The event that triggered the call (default is None).
  773. """
  774. for i in range(0, len(self._team_ids)):
  775. self._teams_check_list.Check(i, True)
  776. def _deselect_all_teams(self, event = None):
  777. """Deselects all teams in the list.
  778. Parameters
  779. ----------
  780. event : wxEvent, optional
  781. The event that triggered the call (default is None).
  782. """
  783. for i in range(0, len(self._team_ids)):
  784. self._teams_check_list.Check(i, False)
  785. def select_unit(self, event=None, unit_id=None):
  786. """Loads a unit info and enables optimizaton options.
  787. Called when a unit is selected in _unit_choice. If called from an
  788. an event, the unit selected in _unit_choice gets priority. When
  789. manually called, it uses unitId, so it must be set beforehand.
  790. Parameters
  791. ----------
  792. event : wxEvent, optional
  793. The event that triggered the call (default is None).
  794. """
  795. # If its called from an event, it means a unit has been selected in this
  796. # panel selector.
  797. if unit_id is None:
  798. unit_id = \
  799. self._unit_id_selector_index[self._unit_choice.GetSelection()]
  800. # It it was not called from an event, and unit_id has value
  801. else:
  802. for i in range(0, len(self._unit_id_selector_index)):
  803. if self._unit_id_selector_index[i] == unit_id:
  804. self._unit_choice.SetSelection(i)
  805. break
  806. self._selected_unit = units[unit_id]
  807. # Set unit info labels
  808. self._name_label.SetLabel(self._selected_unit.name)
  809. stars_label_text = ""
  810. for i in range(0, self._selected_unit.stars):
  811. stars_label_text += "\u272D"
  812. self._stars_label.SetLabel(stars_label_text)
  813. self._level_label.SetLabel("Lv." + str(self._selected_unit.level))
  814. self._id_label.SetLabel("#" + self._selected_unit.id)
  815. self._priority_label.SetLabel(
  816. "Priority: " + str(self._selected_unit.priority)
  817. )
  818. teams_label_text = ""
  819. if len(self._selected_unit.teams) == 0:
  820. teams_label_text = "In no teams."
  821. elif len(self._selected_unit.teams) == 1:
  822. teams_label_text = "In 1 team."
  823. else:
  824. teams_label_text = \
  825. "In " + str(len(self._selected_unit.teams)) + " teams."
  826. self._teams_label.SetLabel(teams_label_text)
  827. # Set sliders max values
  828. self._min_stat_slid_list[0].SetMax(100000) #HP
  829. self._min_stat_slid_list[1].SetMax(5000) #ATK
  830. self._min_stat_slid_list[2].SetMax(5000) #DEF
  831. self._min_stat_slid_list[3].SetMax(500) #SPD
  832. self._min_stat_slid_list[4].SetMax(100) #CRR
  833. self._min_stat_slid_list[5].SetMax(500) #CRD
  834. self._min_stat_slid_list[6].SetMax(100) #RES
  835. self._min_stat_slid_list[7].SetMax(85) #ACC
  836. self._min_stat_slid_list[8].SetMax(500000) #EHP
  837. self._min_stat_slid_list[9].SetMax(8000) #DMG
  838. stat_base_values = self._selected_unit.base_stats.values()
  839. stat_curr_values = self._selected_unit.stats.values()
  840. for i in range (0, 10):
  841. self._min_stat_slid_list[i].SetMin(stat_base_values[i])
  842. self._min_stat_slid_list[i].SetValue(stat_curr_values[i])
  843. self._min_stat_text_list[i].SetValue(str(stat_curr_values[i]))
  844. stat_str = str(stat_base_values[i])
  845. if i in [4, 5, 6, 7]: # CRR, CRD, RES, ACC
  846. stat_str = stat_str + "%"
  847. else:
  848. stat_str = stat_str + " "
  849. self._stat_grid.SetCellValue(row=i, col=0, s=stat_str)
  850. stat_str = str(stat_curr_values[i])
  851. if i in [4, 5, 6, 7]: # CRR, CRD, RES, ACC
  852. stat_str = stat_str + "%"
  853. else:
  854. stat_str = stat_str + " "
  855. self._stat_grid.SetCellValue(row=i, col=1, s=stat_str)
  856. # Populate the runes
  857. for i in range(0, 6):
  858. self._rune_label_list[i].SetLabel("")
  859. i = 0
  860. current_equiped_stats = [0, 0, 0]
  861. current_equiped_set_list = [0] * 23;
  862. for rune in self._selected_unit.runes:
  863. current_equiped_set_list[rune.type] += 1
  864. label = ""
  865. label = label + rune.type_name + "\n"
  866. # Read rune stats
  867. j = -1
  868. for stat in rune.stats:
  869. if (rune.slot == 2 and stat.slot == -1):
  870. current_equiped_stats[0] = stat.stat
  871. elif (rune.slot == 4 and stat.slot == -1):
  872. current_equiped_stats[1] = stat.stat
  873. elif (rune.slot == 6 and stat.slot == -1):
  874. current_equiped_stats[2] = stat.stat
  875. while (j != stat.slot):
  876. label = label + "\n" # No stat, empty line
  877. j = j + 1;
  878. label = label + \
  879. stat.name + str(stat.value).rjust(4)
  880. if stat.grind > 0:
  881. label = label + " +" + str(stat.grind)
  882. self._rune_label_list[i].SetLabel(label)
  883. i = i + 1
  884. # Try to infer data to set the default options, reset the rest
  885. # Except the team list, rune level and storage: never reset those.
  886. self._stat_checklist.SetCheckedItems(())
  887. for i in range(0, 3):
  888. if current_equiped_stats[i] == 1: # HP
  889. self._stat_checklist.Check(0, True)
  890. elif current_equiped_stats[i] == 2: # HP%
  891. self._stat_checklist.Check(1, True)
  892. elif current_equiped_stats[i] == 3: # ATK
  893. self._stat_checklist.Check(2, True)
  894. elif current_equiped_stats[i] == 4: # ATK%
  895. self._stat_checklist.Check(3, True)
  896. elif current_equiped_stats[i] == 5: # DEF
  897. self._stat_checklist.Check(4, True)
  898. elif current_equiped_stats[i] == 6: # DEF%
  899. self._stat_checklist.Check(5, True)
  900. elif current_equiped_stats[i] == 8: # SPD
  901. self._stat_checklist.Check(6, True)
  902. elif current_equiped_stats[i] == 9: # CRR
  903. self._stat_checklist.Check(7, True)
  904. elif current_equiped_stats[i] == 10: # CRD
  905. self._stat_checklist.Check(8, True)
  906. elif current_equiped_stats[i] == 11: # RES
  907. self._stat_checklist.Check(9, True)
  908. elif current_equiped_stats[i] == 12: # ACC
  909. self._stat_checklist.Check(10, True)
  910. current_equiped_sets = [-1, -1, -1]
  911. j = 0
  912. for i in range(0, 23):
  913. if j < 3:
  914. # If a set of 2 has 4 or 2:
  915. if i in [
  916. 1, 2, 4, 6, 7, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23
  917. ]:
  918. if current_equiped_set_list[i] >= 4:
  919. current_equiped_sets[j] = i - 1
  920. current_equiped_sets[j + 1] = i - 1
  921. j += 2
  922. elif current_equiped_set_list[i] >= 2:
  923. current_equiped_sets[j] = i - 1
  924. j += 1
  925. # If a set of 4 has 4
  926. elif i in [3, 5, 8, 10, 11, 13]:
  927. if current_equiped_set_list[i] >= 4:
  928. current_equiped_sets[j] = i - 1
  929. j += 1
  930. # Rune set IDs 9 and 12 dont exist, so do a little trick with indexes.
  931. for i in range(0, 3):
  932. #if current_equiped_sets[i] != 0:
  933. actual_id = current_equiped_sets[i] + 1
  934. if (actual_id > 8):
  935. actual_id -= 1
  936. if (actual_id > 11):
  937. actual_id -= 1
  938. self._set_choice_list[i].SetSelection(actual_id)
  939. # Clear all optional sets
  940. self._opt_set_checklist_list[0].SetCheckedItems(())
  941. self._opt_set_checklist_list[1].SetCheckedItems(())
  942. self._options_sizer.ShowItems(True)
  943. def _start_optimization(self, event):
  944. """
  945. Prepares and runs a command optimization.
  946. Once is done, switches to the results view.
  947. Parameters
  948. ----------
  949. event : wxEvent, optional
  950. The event that triggered the call (default is None).
  951. """
  952. # Example call:
  953. # ../RuneOptimizer optimize 7223811472 -l 15
  954. #-e rage,blade --stats atk,crr,crd -h 10000 -f 10
  955. # TODO: Executable name for windows.
  956. command = "runeoptimizer optimize "
  957. command += self._selected_unit.id
  958. level = self._level_choice.GetSelection()
  959. if level == 1:
  960. level = "12"
  961. elif level == 2:
  962. level = "15"
  963. else:
  964. level = "current"
  965. command += (" --level " + level)
  966. sets = ""
  967. for i in range (0, 3):
  968. selected = self._set_choice_list[i].GetString(
  969. self._set_choice_list[i].GetSelection()
  970. ).upper().replace(" ", "");
  971. for j in range(0, 22):
  972. name = SET_NAMES[j].upper()
  973. if len(name) > 7:
  974. name = name[0:7]
  975. if selected == name:
  976. sets += SET_NAMES[j].lower() + ","
  977. if len(sets) > 0:
  978. sets = sets[:-1]
  979. # TODO: ELSE ERROR
  980. command += (" --sets " + sets)
  981. stats = ""
  982. for s in self._stat_checklist.GetCheckedItems():
  983. if s == 0:
  984. stats += "hpflat,"
  985. elif s == 1:
  986. stats += "hp,"
  987. elif s == 2:
  988. stats += "atkflat,"
  989. elif s == 3:
  990. stats += "atk,"
  991. elif s == 4:
  992. stats += "defflat,"
  993. elif s == 5:
  994. stats += "def,"
  995. elif s == 6:
  996. stats += "spd,"
  997. elif s == 7:
  998. stats += "crr,"
  999. elif s == 8:
  1000. stats += "crd,"
  1001. elif s == 9:
  1002. stats += "res,"
  1003. elif s == 10:
  1004. stats += "acc,"
  1005. if len(stats) > 0:
  1006. stats = stats[:-1]
  1007. # TODO: ELSE ERROR
  1008. # Optional sets
  1009. opt_sets = ""
  1010. for s in self._opt_set_checklist_list[0].GetCheckedItems():
  1011. if s == 0:
  1012. opt_sets += "energy,"
  1013. elif s == 1:
  1014. opt_sets += "guard,"
  1015. elif s == 2:
  1016. opt_sets += "swift,"
  1017. elif s == 3:
  1018. opt_sets += "blade,"
  1019. elif s == 4:
  1020. opt_sets += "rage,"
  1021. elif s == 5:
  1022. opt_sets += "focus,"
  1023. elif s == 6:
  1024. opt_sets += "endure,"
  1025. elif s == 7:
  1026. opt_sets += "fatal,"
  1027. elif s == 8:
  1028. opt_sets += "despair,"
  1029. elif s == 9:
  1030. opt_sets += "vampire,"
  1031. elif s == 10:
  1032. opt_sets += "violent,"
  1033. for s in self._opt_set_checklist_list[1].GetCheckedItems():
  1034. if s == 0:
  1035. opt_sets += "nemesis,"
  1036. elif s == 1:
  1037. opt_sets += "will,"
  1038. elif s == 2:
  1039. opt_sets += "shield,"
  1040. elif s == 3:
  1041. opt_sets += "revenge,"
  1042. elif s == 4:
  1043. opt_sets += "destroy,"
  1044. elif s == 5:
  1045. opt_sets += "fight,"
  1046. elif s == 6:
  1047. opt_sets += "determination,"
  1048. elif s == 7:
  1049. opt_sets += "enhance,"
  1050. elif s == 8:
  1051. opt_sets += "accuracy,"
  1052. elif s == 9:
  1053. opt_sets += "tolerance,"
  1054. if len(opt_sets) > 0:
  1055. opt_sets = opt_sets[:-1]
  1056. command += (" --opt-sets " + opt_sets)
  1057. command += (" --stats " + stats)
  1058. command += (" --min-hp " + str(self._min_stat_slid_list[0].GetValue()))
  1059. command += (" --min-atk " + str(self._min_stat_slid_list[1].GetValue()))
  1060. command += (" --min-def " + str(self._min_stat_slid_list[2].GetValue()))
  1061. command += (" --min-spd " + str(self._min_stat_slid_list[3].GetValue()))
  1062. command += (" --min-crr " + str(self._min_stat_slid_list[4].GetValue()))
  1063. command += (" --min-crd " + str(self._min_stat_slid_list[5].GetValue()))
  1064. command += (" --min-res " + str(self._min_stat_slid_list[6].GetValue()))
  1065. command += (" --min-acc " + str(self._min_stat_slid_list[7].GetValue()))
  1066. command += (" --min-ehp " + str(self._min_stat_slid_list[8].GetValue()))
  1067. command += (" --min-dmg " + str(self._min_stat_slid_list[9].GetValue()))
  1068. if self._inventory_check.GetValue():
  1069. command += " --storage"
  1070. if self._broken_check.GetValue():
  1071. command += " --broken"
  1072. command += " --threads " + str(self._thread_slider.GetValue())
  1073. if len(self._teams_check_list.GetCheckedItems()) > 0:
  1074. command += " --no-teams "
  1075. for team in self._teams_check_list.GetCheckedItems():
  1076. command += str(self._team_ids[team]) + ","
  1077. command = command[:-1]
  1078. command += (" --gui ")
  1079. print("Command: " + command)
  1080. self._progress_gauge.SetValue(0)
  1081. # Timer ot periodically check on the process
  1082. self._timer = wx.Timer(self)
  1083. self._timer.Start(1000)
  1084. self.Bind(wx.EVT_TIMER, self._check_process)
  1085. # Create and execute the process
  1086. self.Bind(wx.EVT_END_PROCESS, self._optimization_complete)
  1087. #print("Command: " + command)
  1088. self._process = wx.Process(self)
  1089. self._process.Redirect()
  1090. wx.Execute(command, wx.EXEC_ASYNC, self._process)
  1091. def _check_process(self, event):
  1092. """
  1093. Checks the process output and updates progress bar.
  1094. Parameters
  1095. ----------
  1096. event : wxEvent, optional
  1097. The event that triggered the call.
  1098. """
  1099. if self._process is not None:
  1100. stream = self._process.GetInputStream()
  1101. if stream.CanRead():
  1102. text = bytes.decode(stream.read())
  1103. text = text[:-1] # Remove the last newline
  1104. # Get only the last line
  1105. if text.rfind("\n") != -1:
  1106. text = text[text.rfind("\n") + 1:]
  1107. # If the line is just a number, it's a progress indicator (1-20)
  1108. if text.isnumeric() and int(text) <= 20:
  1109. self._progress_gauge.SetValue(int(text))
  1110. else:
  1111. self._timer.Stop()
  1112. def _optimization_complete(self, event):
  1113. """Called when the update process is complete.
  1114. Hiddes the progress image, checks for errors in the output, prints a
  1115. message annd sets self.updateDone and self.updateError.
  1116. Parameters
  1117. ----------
  1118. event : wxEvent, optional
  1119. The event that triggered the call.
  1120. """
  1121. self._timer.Stop()
  1122. self._progress_gauge.SetValue(20)
  1123. stream = self._process.GetInputStream()
  1124. json_text = ""
  1125. if stream.CanRead():
  1126. text = bytes.decode(stream.read())
  1127. text = text[:-1] # Remove the last newline
  1128. # Get only the last line
  1129. if text.rfind("\n") != -1:
  1130. text = text[text.rfind("\n") + 1:]
  1131. json_text = text
  1132. print("---- RESULT OUTPUT -------------------------------------------")
  1133. print(json_text)
  1134. print("--------------------------------------------------------------")
  1135. if json_text != "":
  1136. data = json.loads(
  1137. json_text,
  1138. object_hook=lambda d: SimpleNamespace(**d)
  1139. )
  1140. if data.result_count == 0:
  1141. wx.MessageBox(
  1142. parent=self,
  1143. message="No results found for the current settings.",
  1144. caption="No results found"
  1145. )
  1146. else:
  1147. self.GetParent().panel_results.process_results(
  1148. unit_id=self._selected_unit.id, json_data=json_text
  1149. )
  1150. self.GetParent().ChangeSelection(3)
  1151. else:
  1152. wx.MessageBox(
  1153. parent=self,
  1154. message="No data received from RuneOptimizer.",
  1155. caption="Error"
  1156. )
  1157. def _min_stat_change_by_slider(self, event=None):
  1158. """
  1159. Changes text when a slider is changed.
  1160. Doesn't do validation.
  1161. Parameters
  1162. ----------
  1163. event : wxEvent, optional
  1164. The event that triggered the call (default is None).
  1165. """
  1166. slid_id = int(event.GetEventObject().GetName().replace("slid", ""))
  1167. self._min_stat_text_list[slid_id].SetValue(
  1168. str(event.GetEventObject().GetValue())
  1169. )
  1170. def _min_stat_change_by_text(self, event=None):
  1171. """
  1172. Changes the slider when the text is changed.
  1173. Validates the text value.
  1174. Parameters
  1175. ----------
  1176. event : wxEvent, optional
  1177. The event that triggered the call (default is None).
  1178. """
  1179. text_id = int(event.GetEventObject().GetName().replace("tx", ""))
  1180. if event.GetEventObject().GetValue().isdigit() == False and \
  1181. event.GetEventObject().GetValue() != "":
  1182. event.GetEventObject().SetValue(
  1183. str(self._min_stat_slid_list[text_id].GetValue())
  1184. )
  1185. min_value = self._min_stat_slid_list[text_id].GetMin()
  1186. if event.GetEventObject().GetValue().isdigit():
  1187. value = int(event.GetEventObject().GetValue())
  1188. else:
  1189. value = min_value
  1190. # Unbind scroll event of the slider, set value and rebind.
  1191. self._min_stat_slid_list[text_id].Unbind(wx.EVT_SCROLL)
  1192. self._min_stat_slid_list[text_id].SetValue(value)
  1193. self._min_stat_slid_list[text_id].Bind(
  1194. wx.EVT_SCROLL, self._min_stat_change_by_slider
  1195. )
  1196. def _adapt_stats(self, event=None):
  1197. """
  1198. Sets all stats requeriments to the unit current values.
  1199. Parameters
  1200. ----------
  1201. event : wxEvent, optional
  1202. The event that triggered the call (default is None).
  1203. """
  1204. for i in range(0, 10):
  1205. self._min_stat_slid_list[i].SetValue(self.unitStats[i])
  1206. self._min_stat_text_list[i].SetValue(
  1207. str(self._min_stat_slid_list[i].GetValue())
  1208. )
  1209. def _reset_stats(self, event=None):
  1210. """
  1211. Sets all stats requeriments to the unit base values.
  1212. Parameters
  1213. ----------
  1214. event : wxEvent, optional
  1215. The event that triggered the call (default is None).
  1216. """
  1217. for i in range(0, 10):
  1218. self._min_stat_slid_list[i].SetValue(
  1219. self._min_stat_slid_list[i].GetMin()
  1220. )
  1221. self._min_stat_text_list[i].SetValue(
  1222. str(self._min_stat_slid_list[i].GetMin())
  1223. )