Passed
Pull Request — master (#291)
by Marek
17:46 queued 10:40
created

StructTaskDlg._showStructures()   F

Complexity

Conditions 22

Size

Total Lines 41
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 22
eloc 36
nop 2
dl 0
loc 41
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
                info.append(item)
99
                # remember first players planet
100
                if enabled and firstEnabled == None:
101
                    firstEnabled = item
102
103
                # select actual planet as source only if player owns it
104
                if planetID == self.sourceID and enabled:
105
                    select = item
106
107
        # set as selected source first players planet
108
        if select == None:
109
            select = firstEnabled
110
            self.sourceID = firstEnabled.planetID
111
112
        self.win.vPlanets.items = info
113
        self.win.vPlanets.itemsChanged()
114
        self.win.vPlanets.selectItem(select)
115
116
    def _showStructures(self, prodProd):
117
        items = []
118
        _filterStructure = Filter.Filter()
119
        _filterStructure.add((lambda x: x.isMilitary) if self.win.vMilitary.checked else Filter.false)
120
        _filterStructure.add((lambda x: getattr(x, "prodBio", 0)) if self.win.vBioProduction.checked else Filter.false)
121
        # prodEnv can be negative for structures destroying environment
122
        _filterStructure.add((lambda x: getattr(x, "prodEnv", 0) > 0) if self.win.vBioProduction.checked else Filter.false)
123
        _filterStructure.add((lambda x: getattr(x, "prodEn", 0)) if self.win.vEnProduction.checked else Filter.false)
124
        _filterStructure.add((lambda x: getattr(x, "prodProd", 0)) if self.win.vCPProduction.checked else Filter.false)
125
        _filterStructure.add((lambda x: getattr(x, "prodSci", 0)) if self.win.vRPProduction.checked else Filter.false)
126
        _filterStructure.add((lambda x: getattr(x, "moraleTrgt", 0)) if self.win.vMorale.checked else Filter.false)
127
128
        for techID in client.getPlayer().techs.keys():
129
            tech = client.getTechInfo(techID)
130
            if not tech.isStructure or tech.level not in self.showLevels or \
131
               (tech.isStructure and not _filterStructure.OR(tech)):
132
                continue
133
134
            if prodProd > 0:
135
                etc = math.ceil(float(tech.buildProd) / prodProd)
136
                if self.sourceID != self.planetID:
137
                    etc *= Rules.buildOnAnotherPlanetMod
138
                etc = res.formatTime(etc)
139
            else:
140
                etc = _("N/A")
141
142
            item = ui.Item(etc,
143
                           techID=techID,
144
                           tIsShip=0,
145
                           name=tech.name,
146
                           tl=tech.level,
147
                           subtype=tech.subtype,
148
                           icons=((res.getTechImg(techID), ui.ALIGN_N),),
149
                           font="small-bold",
150
                           align=ui.ALIGN_S,
151
                           tooltipTitle=_("Details"),
152
                           tooltip="%s, %d %s, %s %d" % (tech.name, tech.buildProd, _("CP"), _("TL"), tech.level),
153
                           statustip="%s, %d %s, %s %d" % (tech.name, tech.buildProd, _("CP"), _("TL"), tech.level))
154
            self.maxTechLevel = max(self.maxTechLevel, tech.level)
155
            items.append(item)
156
        return items
157
158
    def showTechs(self):
159
        sourcePlanet = client.get(self.sourceID, noUpdate=1)
160
        prodProd = getattr(sourcePlanet, "effProdProd", 0)
161
162
        items = self._showStructures(prodProd)
163
164
        # sort methods
165
        if self.sort == 'none': # sort by name
166
            items.sort(key=lambda a: a.name)
167
        elif self.sort == 'tl': # sort by TL, subsort by name
168
            items.sort(key=lambda a: a.name)
169
            items.sort(key=lambda a: a.tl)
170
        elif self.sort == 'type': #sort by subtype, subsort by tl
171
            items.sort(key=lambda a: a.tl)
172
            items.sort(key=lambda a: a.subtype)
173
        self.win.vTechs.items = items
174
        self.win.vTechs.itemsChanged()
175
176
        # filter
177
        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...
178
            widget = getattr(self.win, 'vLevel%d' % i)
179
            if i in self.showLevels and i <= self.maxTechLevel:
180
                widget.visible = 1
181
                widget.pressed = 1
182
            elif i not in self.showLevels and i <= self.maxTechLevel:
183
                widget.visible = 1
184
                widget.pressed = 0
185
            else:
186
                widget.visible = 0
187
188
        # quantity
189
        self.win.vQuantity.text = str(self.quantity)
190
191
    def onSelectPlanet(self, widget, action, data):
192
        if data == None:
193
            for item in self.win.vPlanets.items:
194
                if self.sourceID == item.planetID:
195
                    self.win.vPlanets.selectItem(item)
196
                    break
197
            return
198
        self.quantity = int(self.win.vQuantity.text)
199
        self.sourceID = data.planetID
200
        self.showTechs()
201
202
    def onToggleLevel(self, widget, action, data):
203
        i = widget.data
204
        if i in self.showLevels:
205
            self.showLevels.remove(i)
206
        else:
207
            self.showLevels.append(i)
208
        self.update()
209
210
    def onCancel(self, widget, action, data):
211
        self.hide()
212
213
    def onGovTransferConfirmed(self):
214
        # we assume player wants to build just one center - in opposite case, he may change quantity in the task itself
215
        self.win.vQuantity.text = str(1)
216
        self.govTransferConfirm = True
217
        self.onConstruct(*self.govTransferData)
218
219
    def onConstruct(self, widget, action, data):
220
        if not data:
221
            self.win.setStatus(_('Select technology to construct.'))
222
            return
223
224
        if not self.sourceID:
225
            self.sourceID = self.planetID
226
227
        try:
228
            self.quantity = int(self.win.vQuantity.text)
229
        except ValueError:
230
            self.win.setStatus(_('Specify quantity (1, 2, 3, ...).'))
231
            return
232
        # government centers have additional query and if confirmed, another round of this function is called
233
        tech = client.getTechInfo(data.techID)
234
        if tech.govPwr and not self.govTransferConfirm:
235
            # confirm dialog doesn't send through parameters, so we have to save them
236
            self.govTransferData = (widget, action, data)
237
            self.confirmDlg.display(_("Do you want to issue relocation of your government?"),
238
                _("Yes"), _("No"), self.onGovTransferConfirmed)
239
        else:
240
            try:
241
                self.win.setStatus(_('Executing START CONSTRUCTION command...'))
242
                planet = client.get(self.sourceID, noUpdate=1)
243
                player = client.getPlayer()
244
                if self.extraSlot:
245
                    # check required special resources, if not available, do not continue
246
                    # (without check, server start slot expansion but not the tech)
247
                    specialResources = player.stratRes
248
                    for sr in tech.buildSRes:
249
                        if specialResources.get(sr, 0) < self.quantity:
250
                            self.win.setStatus(_('You do not own required strategic resource(s)'))
251
                            return
252
                        else:
253
                            specialResources[sr] = specialResources.get(sr, 0) - self.quantity
254
                    for i in range(1, self.quantity + 1):
255
                        # as we need two slots instead of one, check whether is task queue short
256
                        # enough (ie 8 tasks max)
257
                        if len(planet.prodQueue) > 8:
258
                            self.win.setStatus(_('Queue is full'))
259
                            return
260
                        client.cmdProxy.startConstruction(self.sourceID,
261
                            Rules.Tech.ADDSLOT3, 1, self.planetID, False,
262
                            self.win.vReportFin.checked, Const.OID_NONE)
263
                        planet.prodQueue, player.stratRes = client.cmdProxy.startConstruction(self.sourceID,
264
                            data.techID, 1, self.planetID, data.techID < 1000,
265
                            self.win.vReportFin.checked, self.structToDemolish)
266
                else:
267
                        planet.prodQueue, player.stratRes = client.cmdProxy.startConstruction(self.sourceID,
268
                        data.techID, self.quantity, self.planetID, data.techID < 1000,
269
                        self.win.vReportFin.checked, self.structToDemolish)
270
                self.win.setStatus(_('Command has been executed.'))
271
            except GameException, e:
272
                self.win.setStatus(e.args[0])
273
                return
274
275
        self.win.vTechs.selectItem(None)
276
        self.hide()
277
        self.caller.update()
278
279
    def onInfo(self, widget, action, data):
280
        if data:
281
            self.techInfoDlg.display(data.techID)
282
283
    def onFilter(self, widget, action, data):
284
        self.update()
285
286
    def onSort(self, widget, action, data):
287
        self.sort = widget.data
288
        if widget.data == 'none':
289
            self.win.vSortTL.checked = 0
290
            self.win.vSortType.checked = 0
291
        elif widget.data == 'tl':
292
            self.win.vSortNone.checked = 0
293
            self.win.vSortType.checked = 0
294
        elif widget.data == 'type':
295
            self.win.vSortTL.checked = 0
296
            self.win.vSortNone.checked = 0
297
        self.update()
298
299
    def createUI(self):
300
        w, h = gdata.scrnSize
301
        cols = 32
302
        rows = 20
303
        dlgWidth = cols * 20 + 4
304
        dlgHeight = rows * 20 + 4
305
        self.win = ui.Window(self.app,
306
                             modal=1,
307
                             escKeyClose=1,
308
                             movable=0,
309
                             title=_('Select structure to construct'),
310
                             rect=ui.Rect((w - dlgWidth) / 2, (h - dlgHeight) / 2, dlgWidth, dlgHeight),
311
                             layoutManager=ui.SimpleGridLM())
312
        self.win.subscribeAction('*', self)
313
        rows -= 1 # title
314
315
        ui.Title(self.win, layout=(0, 0, cols, 1), text=_('Production planet'),
316
                 align=ui.ALIGN_W, font='normal-bold')
317
        ui.ButtonArray(self.win, layout=(0, 1, cols, 3), id='vPlanets',
318
                       buttonSize=(8, 1), showSlider=0, action='onSelectPlanet')
319
320
        ui.Title(self.win, layout=(0, 4, cols - 10, 1), text=_('Structures to construct'),
321
                 align=ui.ALIGN_W, font='normal-bold')
322
        ui.Title(self.win, layout=(cols - 10, 4, 10, 1), text=_('(right click for technology info)'),
323
                 align=ui.ALIGN_E, font='normal')
324
        ui.ButtonArray(self.win, layout=(0, 5, cols, 9), id='vTechs',
325
                       buttonSize=(2, 3), showSlider=0, action='onConstruct', rmbAction='onInfo')
326
327
        ui.Title(self.win, layout=(0, 14, 18, 1), text=_('Filters'),
328
                 align=ui.ALIGN_W, font='normal-bold')
329
        ui.Label(self.win, layout=(0, 15, 6, 1), text=_('Technology levels:'), align=ui.ALIGN_W)
330
        ui.Button(self.win, layout=(6, 15, 1, 1), text=_('1'), id='vLevel1',
331
                  toggle=1, action='onToggleLevel', data=1)
332
        ui.Button(self.win, layout=(7, 15, 1, 1), text=_('2'), id='vLevel2',
333
                  toggle=1, action='onToggleLevel', data=2)
334
        ui.Button(self.win, layout=(8, 15, 1, 1), text=_('3'), id='vLevel3',
335
                  toggle=1, action='onToggleLevel', data=3)
336
        ui.Button(self.win, layout=(9, 15, 1, 1), text=_('4'), id='vLevel4',
337
                  toggle=1, action='onToggleLevel', data=4)
338
        ui.Button(self.win, layout=(10, 15, 1, 1), text=_('5'), id='vLevel5',
339
                  toggle=1, action='onToggleLevel', data=5)
340
        ui.Button(self.win, layout=(11, 15, 1, 1), text=_('6'), id='vLevel6',
341
                  toggle=1, action='onToggleLevel', data=6)
342
        ui.Button(self.win, layout=(12, 15, 1, 1), text=_('7'), id='vLevel7',
343
                  toggle=1, action='onToggleLevel', data=7)
344
        ui.Button(self.win, layout=(13, 15, 1, 1), text=_('8'), id='vLevel8',
345
                  toggle=1, action='onToggleLevel', data=8)
346
        ui.Button(self.win, layout=(14, 15, 1, 1), text=_('9'), id='vLevel9',
347
                  toggle=1, action='onToggleLevel', data=9)
348
349
        ui.Check(self.win, layout=(0, 16, 6, 1), text=_('Bio production'),
350
                 id='vBioProduction', checked=1, align=ui.ALIGN_W, action='onFilter')
351
        ui.Check(self.win, layout=(0, 17, 6, 1), text=_('En production'),
352
                 id='vEnProduction', checked=1, align=ui.ALIGN_W, action='onFilter')
353
        ui.Check(self.win, layout=(6, 16, 6, 1), text=_('CP production'),
354
                 id='vCPProduction', checked=1, align=ui.ALIGN_W, action='onFilter')
355
        ui.Check(self.win, layout=(6, 17, 6, 1), text=_('RP production'),
356
                 id='vRPProduction', checked=1, align=ui.ALIGN_W, action='onFilter')
357
        ui.Check(self.win, layout=(12, 16, 6, 1), text=_('Military'),
358
                 id='vMilitary', checked=1, align=ui.ALIGN_W, action='onFilter')
359
        ui.Check(self.win, layout=(12, 17, 6, 1), text=_('Morale'),
360
                 id='vMorale', checked=1, align=ui.ALIGN_W, action='onFilter')
361
362
        ui.Title(self.win, layout=(18, 14, 6, 1), text=_('Sort'),
363
                 align=ui.ALIGN_W, font='normal-bold')
364
        ui.Check(self.win, layout=(18, 15, 6, 1), text=_('Type'),
365
                 id='vSortType', checked=1, align=ui.ALIGN_W, action='onSort', data='type')
366
        ui.Check(self.win, layout=(18, 16, 6, 1), text=_('Tech Level'),
367
                 id='vSortTL', checked=0, align=ui.ALIGN_W, action='onSort', data='tl')
368
        ui.Check(self.win, layout=(18, 17, 6, 1), text=_('Name'),
369
                 id='vSortNone', checked=0, align=ui.ALIGN_W, action='onSort', data='none')
370
371
        ui.Title(self.win, layout=(24, 14, 8, 1), text=_('Options'),
372
                 align=ui.ALIGN_W, font='normal-bold')
373
        ui.Label(self.win, layout=(24, 15, 3, 1), text=_('Quantity'), align=ui.ALIGN_W)
374
        ui.Entry(self.win, layout=(27, 15, 5, 1), id='vQuantity', align=ui.ALIGN_E)
375
        ui.Check(self.win, layout=(26, 16, 8, 1), id='vReportFin', text=_('Report finalization'),
376
                 align=ui.ALIGN_W)
377
378
        ui.Title(self.win, layout=(0, rows - 1, cols - 5, 1), align=ui.ALIGN_W)
379
        ui.TitleButton(self.win, layout=(cols - 5, rows - 1, 5, 1), text=_('Cancel'), action='onCancel')
380