Passed
Pull Request — master (#291)
by Marek
01:53
created

StructTaskDlg._showStructures()   F

Complexity

Conditions 22

Size

Total Lines 42
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 22
eloc 36
nop 2
dl 0
loc 42
rs 0
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like osci.dialog.StructTaskDlg.StructTaskDlg._showStructures() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
#
2
#  Copyright 2001 - 2016 Ludek Smid [http://www.ospace.net/]
3
#
4
#  This file is part of Outer Space.
5
#
6
#  Outer Space is free software; you can redistribute it and/or modify
7
#  it under the terms of the GNU General Public License as published by
8
#  the Free Software Foundation; either version 2 of the License, or
9
#  (at your option) any later version.
10
#
11
#  Outer Space is distributed in the hope that it will be useful,
12
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
#  GNU General Public License for more details.
15
#
16
#  You should have received a copy of the GNU General Public License
17
#  along with Outer Space; if not, write to the Free Software
18
#  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19
#
20
import math
21
22
import pygameui as ui
23
24
from ige import GameException
25
from ige.ospace import Rules
26
import ige.ospace.Const as Const
27
28
from osci import gdata, client, res
29
from TechInfoDlg import TechInfoDlg
30
from ConfirmDlg import ConfirmDlg
31
import Filter
32
33
class StructTaskDlg:
34
35
    def __init__(self, app):
36
        self.app = app
37
        self.showStructures = 1
38
        self.showShips = 0
39
        self.showOther = 0
40
        self.techID = 0
41
        self.sort = 'type'
42
        self.showLevels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
43
        self.techInfoDlg = TechInfoDlg(app)
44
        self.confirmDlg = ConfirmDlg(app)
45
        self.createUI()
46
47
    def display(self, caller, planetID, extraSlot=False, structToDemolish=Const.OID_NONE):
48
        if gdata.config.defaults.reportfinalization != None:
49
            val = gdata.config.defaults.reportfinalization
50
            self.win.vReportFin.checked = val == 'yes'
51
52
        self.caller = caller
53
        self.systemID = caller.systemID
54
        self.planetID = caller.planetID
55
        self.playerID = client.getPlayerID()
56
        self.sourceID = caller.planetID
57
        self.extraSlot = extraSlot
58
        self.maxTechLevel = 0
59
        self.quantity = 1
60
        self.govTransferConfirm = False
61
        self.govTransferData = None
62
        self.structToDemolish = structToDemolish
63
        self.win.vPlanets.selectItem(None)
64
        self.showPlanets()
65
        self.showTechs()
66
        self.win.show()
67
        gdata.updateDlgs.append(self)
68
69
    def hide(self):
70
        self.win.setStatus(_("Ready."))
71
        if self in gdata.updateDlgs:
72
            gdata.updateDlgs.remove(self)
73
        self.win.hide()
74
75
    def update(self):
76
        if self.win.visible:
77
            self.quantity = int(self.win.vQuantity.text)
78
            self.showPlanets()
79
            self.showTechs()
80
81
    def showPlanets(self):
82
        info = []
83
        system = client.get(self.systemID, noUpdate=1)
84
        select = None
85
        playerID = client.getPlayerID()
86
        firstEnabled = None
87
        if hasattr(system, 'planets'):
88
            for planetID in system.planets:
89
                # get planet
90
                planet = client.get(planetID, noUpdate=1)
91
                # only player owned planets can be source planets
92
                enabled = getattr(planet, "owner") == playerID
93
                buttonText = "%s / %s" % (getattr(planet, 'name', res.getUnknownName()), getattr(planet, "effProdProd", "?"))
94
                item = ui.Item(buttonText,
95
                               planetID=planetID,
96
                               enabled=enabled,
97
                               align=ui.ALIGN_NONE,
98
                )
99
                info.append(item)
100
                # remember first players planet
101
                if enabled and firstEnabled == None:
102
                    firstEnabled = item
103
104
                # select actual planet as source only if player owns it
105
                if planetID == self.sourceID and enabled:
106
                    select = item
107
108
        # set as selected source first players planet
109
        if select == None:
110
            select = firstEnabled
111
            self.sourceID = firstEnabled.planetID
112
113
        self.win.vPlanets.items = info
114
        self.win.vPlanets.itemsChanged()
115
        self.win.vPlanets.selectItem(select)
116
117
    def _showStructures(self, prodProd):
118
        items = []
119
        _filterStructure = Filter.Filter()
120
        _filterStructure.add((lambda x: x.isMilitary) if self.win.vMilitary.checked else Filter.false)
121
        _filterStructure.add((lambda x: getattr(x, "prodBio", 0)) if self.win.vBioProduction.checked else Filter.false)
122
        # prodEnv can be negative for structures destroying environment
123
        _filterStructure.add((lambda x: getattr(x, "prodEnv", 0) > 0) if self.win.vBioProduction.checked else Filter.false)
124
        _filterStructure.add((lambda x: getattr(x, "prodEn", 0)) if self.win.vEnProduction.checked else Filter.false)
125
        _filterStructure.add((lambda x: getattr(x, "prodProd", 0)) if self.win.vCPProduction.checked else Filter.false)
126
        _filterStructure.add((lambda x: getattr(x, "prodSci", 0)) if self.win.vRPProduction.checked else Filter.false)
127
        _filterStructure.add((lambda x: getattr(x, "moraleTrgt", 0)) if self.win.vMorale.checked else Filter.false)
128
129
        for techID in client.getPlayer().techs.keys():
130
            tech = client.getTechInfo(techID)
131
            if not tech.isStructure or tech.level not in self.showLevels or \
132
               (tech.isStructure and not _filterStructure.OR(tech)):
133
                continue
134
135
            if prodProd > 0:
136
                etc = math.ceil(float(tech.buildProd) / prodProd)
137
                if self.sourceID != self.planetID:
138
                    etc *= Rules.buildOnAnotherPlanetMod
139
                etc = res.formatTime(etc)
140
            else:
141
                etc = _("N/A")
142
143
            item = ui.Item(etc,
144
                           techID=techID,
145
                           tIsShip=0,
146
                           name=tech.name,
147
                           tl=tech.level,
148
                           subtype=tech.subtype,
149
                           icons=((res.getTechImg(techID), ui.ALIGN_N),),
150
                           font="small-bold",
151
                           align=ui.ALIGN_S,
152
                           tooltipTitle=_("Details"),
153
                           tooltip="%s, %d %s, %s %d" % (tech.name, tech.buildProd, _("CP"), _("TL"), tech.level),
154
                           statustip="%s, %d %s, %s %d" % (tech.name, tech.buildProd, _("CP"), _("TL"), tech.level),
155
                          )
156
            self.maxTechLevel = max(self.maxTechLevel, tech.level)
157
            items.append(item)
158
        return items
159
160
    def showTechs(self):
161
        sourcePlanet = client.get(self.sourceID, noUpdate=1)
162
        prodProd = getattr(sourcePlanet, "effProdProd", 0)
163
164
        items = self._showStructures(prodProd)
165
166
        # sort methods
167
        if self.sort == 'none': # sort by name
168
            items.sort(key=lambda a: a.name)
169
        elif self.sort == 'tl': # sort by TL, subsort by name
170
            items.sort(key=lambda a: a.name)
171
            items.sort(key=lambda a: a.tl)
172
        elif self.sort == 'type': #sort by subtype, subsort by tl
173
            items.sort(key=lambda a: a.tl)
174
            items.sort(key=lambda a: a.subtype)
175
        self.win.vTechs.items = items
176
        self.win.vTechs.itemsChanged()
177
178
        # filter
179
        for i in xrange(1, 10):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable xrange does not seem to be defined.
Loading history...
180
            widget = getattr(self.win, 'vLevel%d' % i)
181
            if i in self.showLevels and i <= self.maxTechLevel:
182
                widget.visible = 1
183
                widget.pressed = 1
184
            elif i not in self.showLevels and i <= self.maxTechLevel:
185
                widget.visible = 1
186
                widget.pressed = 0
187
            else:
188
                widget.visible = 0
189
190
        # quantity
191
        self.win.vQuantity.text = str(self.quantity)
192
193
    def onSelectPlanet(self, widget, action, data):
194
        if data == None:
195
            for item in self.win.vPlanets.items:
196
                if self.sourceID == item.planetID:
197
                    self.win.vPlanets.selectItem(item)
198
                    break
199
            return
200
        self.quantity = int(self.win.vQuantity.text)
201
        self.sourceID = data.planetID
202
        self.showTechs()
203
204
    def onToggleLevel(self, widget, action, data):
205
        i = widget.data
206
        if i in self.showLevels:
207
            self.showLevels.remove(i)
208
        else:
209
            self.showLevels.append(i)
210
        self.update()
211
212
    def onCancel(self, widget, action, data):
213
        self.hide()
214
215
    def onGovTransferConfirmed(self):
216
        # we assume player wants to build just one center - in opposite case, he may change quantity in the task itself
217
        self.win.vQuantity.text = str(1)
218
        self.govTransferConfirm = True
219
        self.onConstruct(*self.govTransferData)
220
221
    def onConstruct(self, widget, action, data):
222
        if not data:
223
            self.win.setStatus(_('Select technology to construct.'))
224
            return
225
226
        if not self.sourceID:
227
            self.sourceID = self.planetID
228
229
        try:
230
            self.quantity = int(self.win.vQuantity.text)
231
        except ValueError:
232
            self.win.setStatus(_('Specify quantity (1, 2, 3, ...).'))
233
            return
234
        # government centers have additional query and if confirmed, another round of this function is called
235
        tech = client.getTechInfo(data.techID)
236
        if tech.govPwr and not self.govTransferConfirm:
237
            # confirm dialog doesn't send through parameters, so we have to save them
238
            self.govTransferData = (widget, action, data)
239
            self.confirmDlg.display(_("Do you want to issue relocation of your government?"),
240
                _("Yes"), _("No"), self.onGovTransferConfirmed)
241
        else:
242
            try:
243
                self.win.setStatus(_('Executing START CONSTRUCTION command...'))
244
                planet = client.get(self.sourceID, noUpdate=1)
245
                player = client.getPlayer()
246
                if self.extraSlot:
247
                    # check required special resources, if not available, do not continue
248
                    # (without check, server start slot expansion but not the tech)
249
                    specialResources = player.stratRes
250
                    for sr in tech.buildSRes:
251
                        if specialResources.get(sr, 0) < self.quantity:
252
                            self.win.setStatus(_('You do not own required strategic resource(s)'))
253
                            return
254
                        else:
255
                            specialResources[sr] = specialResources.get(sr, 0) - self.quantity
256
                    for i in range(1, self.quantity + 1):
257
                        # as we need two slots instead of one, check whether is task queue short
258
                        # enough (ie 8 tasks max)
259
                        if len(planet.prodQueue) > 8:
260
                            self.win.setStatus(_('Queue is full'))
261
                            return
262
                        client.cmdProxy.startConstruction(self.sourceID,
263
                            Rules.Tech.ADDSLOT3, 1, self.planetID, False,
264
                            self.win.vReportFin.checked, Const.OID_NONE)
265
                        planet.prodQueue, player.stratRes = client.cmdProxy.startConstruction(self.sourceID,
266
                            data.techID, 1, self.planetID, data.techID < 1000,
267
                            self.win.vReportFin.checked, self.structToDemolish)
268
                else:
269
                        planet.prodQueue, player.stratRes = client.cmdProxy.startConstruction(self.sourceID,
270
                        data.techID, self.quantity, self.planetID, data.techID < 1000,
271
                        self.win.vReportFin.checked, self.structToDemolish)
272
                self.win.setStatus(_('Command has been executed.'))
273
            except GameException, e:
274
                self.win.setStatus(e.args[0])
275
                return
276
277
        self.win.vTechs.selectItem(None)
278
        self.hide()
279
        self.caller.update()
280
281
    def onInfo(self, widget, action, data):
282
        if data:
283
            self.techInfoDlg.display(data.techID)
284
285
    def onFilter(self, widget, action, data):
286
        self.update()
287
288
    def onSort(self, widget, action, data):
289
        self.sort = widget.data
290
        if widget.data == 'none':
291
            self.win.vSortTL.checked = 0
292
            self.win.vSortType.checked = 0
293
        elif widget.data == 'tl':
294
            self.win.vSortNone.checked = 0
295
            self.win.vSortType.checked = 0
296
        elif widget.data == 'type':
297
            self.win.vSortTL.checked = 0
298
            self.win.vSortNone.checked = 0
299
        self.update()
300
301
    def createUI(self):
302
        w, h = gdata.scrnSize
303
        cols = 32
304
        rows = 20
305
        dlgWidth = cols * 20 + 4
306
        dlgHeight = rows * 20 + 4
307
        self.win = ui.Window(self.app,
308
                             modal=1,
309
                             escKeyClose=1,
310
                             movable=0,
311
                             title=_('Select structure to construct'),
312
                             rect=ui.Rect((w - dlgWidth) / 2, (h - dlgHeight) / 2, dlgWidth, dlgHeight),
313
                             layoutManager=ui.SimpleGridLM(),
314
        )
315
        self.win.subscribeAction('*', self)
316
        rows -= 1 # title
317
318
        ui.Title(self.win, layout=(0, 0, cols, 1), text=_('Production planet'),
319
                 align=ui.ALIGN_W, font='normal-bold')
320
        ui.ButtonArray(self.win, layout=(0, 1, cols, 3), id='vPlanets',
321
                 buttonSize=(8, 1), showSlider=0, action='onSelectPlanet')
322
323
        ui.Title(self.win, layout=(0, 4, cols - 10, 1), text=_('Structures to construct'),
324
                 align=ui.ALIGN_W, font='normal-bold')
325
        ui.Title(self.win, layout=(cols - 10, 4, 10, 1), text=_('(right click for technology info)'),
326
                 align=ui.ALIGN_E, font='normal')
327
        ui.ButtonArray(self.win, layout=(0, 5, cols, 9), id='vTechs',
328
                       buttonSize=(2, 3), showSlider=0, action='onConstruct', rmbAction='onInfo')
329
330
        ui.Title(self.win, layout=(0, 14, 18, 1), text=_('Filters'),
331
                 align=ui.ALIGN_W, font='normal-bold')
332
        ui.Label(self.win, layout=(0, 15, 6, 1), text=_('Technology levels:'), align=ui.ALIGN_W)
333
        ui.Button(self.win, layout=(6, 15, 1, 1), text=_('1'), id='vLevel1',
334
                  toggle=1, action='onToggleLevel', data=1)
335
        ui.Button(self.win, layout=(7, 15, 1, 1), text=_('2'), id='vLevel2',
336
                  toggle=1, action='onToggleLevel', data=2)
337
        ui.Button(self.win, layout=(8, 15, 1, 1), text=_('3'), id='vLevel3',
338
                  toggle=1, action='onToggleLevel', data=3)
339
        ui.Button(self.win, layout=(9, 15, 1, 1), text=_('4'), id='vLevel4',
340
                  toggle=1, action='onToggleLevel', data=4)
341
        ui.Button(self.win, layout=(10, 15, 1, 1), text=_('5'), id='vLevel5',
342
                  toggle=1, action='onToggleLevel', data=5)
343
        ui.Button(self.win, layout=(11, 15, 1, 1), text=_('6'), id='vLevel6',
344
                  toggle=1, action='onToggleLevel', data=6)
345
        ui.Button(self.win, layout=(12, 15, 1, 1), text=_('7'), id='vLevel7',
346
                  toggle=1, action='onToggleLevel', data=7)
347
        ui.Button(self.win, layout=(13, 15, 1, 1), text=_('8'), id='vLevel8',
348
                  toggle=1, action='onToggleLevel', data=8)
349
        ui.Button(self.win, layout=(14, 15, 1, 1), text=_('9'), id='vLevel9',
350
                  toggle=1, action='onToggleLevel', data=9)
351
352
        ui.Check(self.win, layout=(0, 16, 6, 1), text=_('Bio production'),
353
                 id='vBioProduction', checked=1, align=ui.ALIGN_W, action='onFilter')
354
        ui.Check(self.win, layout=(0, 17, 6, 1), text=_('En production'),
355
                 id='vEnProduction', checked=1, align=ui.ALIGN_W, action='onFilter')
356
        ui.Check(self.win, layout=(6, 16, 6, 1), text=_('CP production'),
357
                 id='vCPProduction', checked=1, align=ui.ALIGN_W, action='onFilter')
358
        ui.Check(self.win, layout=(6, 17, 6, 1), text=_('RP production'),
359
                 id='vRPProduction', checked=1, align=ui.ALIGN_W, action='onFilter')
360
        ui.Check(self.win, layout=(12, 16, 6, 1), text=_('Military'),
361
                 id='vMilitary', checked=1, align=ui.ALIGN_W, action='onFilter')
362
        ui.Check(self.win, layout=(12, 17, 6, 1), text=_('Morale'),
363
                 id='vMorale', checked=1, align=ui.ALIGN_W, action='onFilter')
364
365
        ui.Title(self.win, layout=(18, 14, 6, 1), text=_('Sort'),
366
                 align=ui.ALIGN_W, font='normal-bold')
367
        ui.Check(self.win, layout=(18, 15, 6, 1), text=_('Type'),
368
                 id='vSortType', checked=1, align=ui.ALIGN_W, action='onSort', data='type')
369
        ui.Check(self.win, layout=(18, 16, 6, 1), text=_('Tech Level'),
370
                 id='vSortTL', checked=0, align=ui.ALIGN_W, action='onSort', data='tl')
371
        ui.Check(self.win, layout=(18, 17, 6, 1), text=_('Name'),
372
                 id='vSortNone', checked=0, align=ui.ALIGN_W, action='onSort', data='none')
373
374
        ui.Title(self.win, layout=(24, 14, 8, 1), text=_('Options'),
375
                 align=ui.ALIGN_W, font='normal-bold')
376
        ui.Label(self.win, layout=(24, 15, 3, 1), text=_('Quantity'), align=ui.ALIGN_W)
377
        ui.Entry(self.win, layout=(27, 15, 5, 1), id='vQuantity', align=ui.ALIGN_E)
378
        ui.Check(self.win, layout=(26, 16, 8, 1), id='vReportFin', text=_('Report finalization'),
379
                 align=ui.ALIGN_W)
380
381
        ui.Title(self.win, layout=(0, rows - 1, cols - 5, 1), align=ui.ALIGN_W)
382
        ui.TitleButton(self.win, layout=(cols - 5, rows - 1, 5, 1), text=_('Cancel'), action='onCancel')
383