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

NewTaskDlg._showStructures()   F

Complexity

Conditions 20

Size

Total Lines 20
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 20
eloc 17
nop 1
dl 0
loc 20
rs 0
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like osci.dialog.NewTaskDlg.NewTaskDlg._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
21
import math
22
23
import pygameui as ui
24
25
from ige import GameException
26
from ige.ospace import Rules
27
import ige.ospace.Const as Const
28
29
from osci import gdata, client, res
30
from TechInfoDlg import TechInfoDlg
31
from ConstructionDlg import ConstructionDlg
32
from ConfirmDlg import ConfirmDlg
33
import Utils
34
import Filter
35
36
37
class NewTaskDlg:
38
39
    def __init__(self, app):
40
        self.app = app
41
        self.showStructures = 1
42
        self.showShips = 0
43
        self.showOther = 0
44
        self.techID = 0
45
        self.showLevels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 99]
46
        self.techInfoDlg = TechInfoDlg(app)
47
        self.constructionDlg = ConstructionDlg(app)
48
        self.confirmDlg = ConfirmDlg(app)
49
        self.createUI()
50
        self.win.setTagAttr('struct', 'visible', 1)
51
        self.win.setTagAttr('ship', 'visible', 0)
52
        # set default sorting for technologies
53
        self.win.vTechs.setSort("text")
54
55
    def display(self, caller, prodProd, structToDemolish=Const.OID_NONE):
56
        if gdata.config.defaults.reportfinalization != None:
57
            val = gdata.config.defaults.reportfinalization
58
            self.win.vReportFin.checked = val == 'yes'
59
60
        self.caller = caller
61
        self.systemID = caller.systemID
62
        self.planetID = caller.planetID
63
        self.playerID = client.getPlayerID()
64
        self.targetID = caller.planetID
65
        self.maxTechLevel = 0
66
        self.quantity = 1
67
        self.govTransferConfirm = False
68
        self.govTransferData = None
69
        self.prodProd = prodProd
70
        self.structToDemolish = structToDemolish
71
        self.showTechs()
72
        self.showSlots()
73
        self.win.show()
74
        gdata.updateDlgs.append(self)
75
76
    def hide(self):
77
        self.win.setStatus(_("Ready."))
78
        if self in gdata.updateDlgs:
79
            gdata.updateDlgs.remove(self)
80
        self.win.hide()
81
82
    def update(self):
83
        if self.win.visible:
84
            self.quantity = int(self.win.vQuantity.text)
85
            if self.showShips:
86
                self.win.vInfo.enabled = Utils.enableConstruction(client)
87
            self.showTechs()
88
            self.showSlots()
89
90
    def _processNonShips(self, tech):
91
        if self.prodProd > 0:
92
            etc = math.ceil(float(tech.buildProd) / self.prodProd)
93
            if self.targetID != self.planetID:
94
                etc *= Rules.buildOnAnotherPlanetMod
95
            etc = res.formatTime(etc)
96
        else:
97
            etc = _("N/A")
98
99
        item = ui.Item(tech.name,
100
                       tLevel=tech.level,
101
                       tProd=tech.buildProd,
102
                       techID=tech.id,
103
                       tIsShip=0,
104
                       tETC=etc,
105
        )
106
        self.maxTechLevel = max(self.maxTechLevel, tech.level)
107
108
        return item
109
110
    def _showStructures(self):
111
        items = []
112
        _filterStructure = Filter.Filter()
113
        _filterStructure.add((lambda x: x.isMilitary) if self.win.vMilitary.checked else Filter.false)
114
        _filterStructure.add((lambda x: getattr(x, "prodBio", 0)) if self.win.vBioProduction.checked else Filter.false)
115
        # prodEnv can be negative for structures destroying environment
116
        _filterStructure.add((lambda x: getattr(x, "prodEnv", 0) > 0) if self.win.vBioProduction.checked else Filter.false)
117
        _filterStructure.add((lambda x: getattr(x, "prodEn", 0)) if self.win.vEnProduction.checked else Filter.false)
118
        _filterStructure.add((lambda x: getattr(x, "prodProd", 0)) if self.win.vCPProduction.checked else Filter.false)
119
        _filterStructure.add((lambda x: getattr(x, "prodSci", 0)) if self.win.vRPProduction.checked else Filter.false)
120
        _filterStructure.add((lambda x: getattr(x, "moraleTrgt", 0)) if self.win.vMorale.checked else Filter.false)
121
122
        for techID in client.getPlayer().techs.keys():
123
            tech = client.getTechInfo(techID)
124
            if not tech.isStructure or tech.level not in self.showLevels or \
125
               (tech.isStructure and not _filterStructure.OR(tech)):
126
                continue
127
            items.append(self._processNonShips(tech))
128
129
        return items
130
131
    def _showProjects(self):
132
        items = []
133
        for techID in client.getPlayer().techs.keys():
134
            tech = client.getTechInfo(techID)
135
            if tech.level not in self.showLevels or not tech.isProject:
136
                continue
137
            items.append(self._processNonShips(tech))
138
139
        return items
140
141
142
    def _showShips(self):
143
        items = []
144
        _filterShipSize = Filter.Filter()
145
        _filterShipSize.add((lambda x: x.combatClass == 0) if self.win.vSmall.checked else Filter.false)
146
        _filterShipSize.add((lambda x: x.combatClass == 1) if self.win.vMedium.checked else Filter.false)
147
        _filterShipSize.add((lambda x: x.combatClass == 2) if self.win.vLarge.checked else Filter.false)
148
        _filterShipMilitary = Filter.Filter()
149
        _filterShipMilitary.add((lambda x: x.isMilitary) if self.win.vMilShip.checked else Filter.false)
150
        _filterShipMilitary.add((lambda x: not x.isMilitary) if self.win.vCivShip.checked else Filter.false)
151
152
        # special handling for ships
153
        player = client.getPlayer()
154
155
        for designID in player.shipDesigns.keys():
156
            tech = player.shipDesigns[designID]
157
            if not _filterShipSize.OR(tech) or not _filterShipMilitary.OR(tech):
158
                continue
159
160
            if tech.upgradeTo != Const.OID_NONE:
161
                # skip ships that are set to upgrade
162
                continue
163
            if self.prodProd > 0:
164
                etc = res.formatTime(math.ceil(float(tech.buildProd) / self.prodProd))
165
            else:
166
                etc = _("N/A")
167
            item = ui.Item(tech.name,
168
                           tLevel=tech.level,
169
                           tProd=tech.buildProd,
170
                           techID=designID,
171
                           tIsShip=1,
172
                           tETC=etc,
173
            )
174
            items.append(item)
175
        return items
176
177
    def _processTarget(self, planet):
178
        ownerName = res.getUnknownName()
179
        ownerID = Const.OID_NONE
180
        if hasattr(planet, 'owner'):
181
            ownerID = planet.owner
182
            if planet.owner != Const.OID_NONE:
183
                ownerName = client.get(planet.owner, noUpdate=1).name
184
        if planet.plType in ("A", "G"):
185
            color = gdata.sevColors[gdata.DISABLED]
186
        else:
187
            color = res.getPlayerColor(ownerID)
188
        plname = getattr(planet, 'name', res.getUnknownName())
189
        item = ui.Item(plname,
190
                       text_raw=getattr(planet, 'plEn', plname),
191
                       planetID=planet.oid,
192
                       plOwner=ownerName,
193
                       foreground=color,
194
        )
195
        return item
196
197
    def showTechs(self):
198
        # techs
199
        items = []
200
201
        if self.showStructures:
202
            items += self._showStructures()
203
        if self.showShips:
204
            items += self._showShips()
205
        if self.showOther:
206
            items += self._showProjects()
207
208
        # sort it by level and then by name
209
        items.sort(key=lambda a: (100 - a.tLevel, a.text))
210
        self.win.vTechs.items = items
211
        self.win.vTechs.itemsChanged()
212
        # preserve selection
213
        for item in items:
214
            if self.techID == item.techID:
215
                self.win.vTechs.selectItem(item)
216
                break
217
        # tech level filter
218
        for i in range(1, 7):
219
            widget = getattr(self.win, 'vLevel%d' % i)
220
            if i in self.showLevels and i <= self.maxTechLevel:
221
                widget.visible = 1
222
                widget.pressed = 1
223
            elif i not in self.showLevels and i <= self.maxTechLevel:
224
                widget.visible = 1
225
                widget.pressed = 0
226
            else:
227
                widget.visible = 0
228
        self.win.vStructuresToggle.pressed = self.showStructures
229
        self.win.vShipsToggle.pressed = self.showShips
230
        self.win.vOtherToggle.pressed = self.showOther
231
        # targets
232
        info = []
233
        system = client.get(self.systemID, noUpdate=1)
234
        for planetID in system.planets:
235
            planet = client.get(planetID, noUpdate=1)
236
            info.append(self._processTarget(planet))
237
        self.win.vTargets.items = info
238
        self.win.vTargets.itemsChanged()
239
        for item in info:
240
            if self.targetID == item.planetID:
241
                self.win.vTargets.selectItem(item)
242
                break
243
        # quantity
244
        self.win.vQuantity.text = str(self.quantity)
245
246 View Code Duplication
    def showSlots(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
247
        # techs
248
        items = []
249
        techs = {}
250
        if self.showStructures:
251
            player = client.getPlayer()
252
            target = client.get(self.targetID, noUpdate=1)
253
            if hasattr(target, 'slots') and target.owner == player.oid:
254
                if len(target.slots) < target.plSlots:
255
                    item = ui.Item(_("Free slot"), techID=0)
256
                    items.append(item)
257
                for struct in target.slots:
258
                    if not struct[Const.STRUCT_IDX_TECHID] in techs:
259
                        techs[struct[Const.STRUCT_IDX_TECHID]] = 1
260
                    else:
261
                        techs[struct[Const.STRUCT_IDX_TECHID]] += 1
262
                for tech in techs.keys():
263
                    techInfo = client.getTechInfo(tech)
264
                    item = ui.Item("%s (%d)" % (techInfo.name, techs[tech]), techID=tech)
265
                    items.append(item)
266
267
        self.win.vTSlots.items = items
268
        self.win.vTSlots.itemsChanged()
269
        self.structToDemolish = Const.OID_NONE
270
271
    def onSelectPlanet(self, widget, action, data):
272
        self.quantity = int(self.win.vQuantity.text)
273
        self.targetID = data.planetID
274
        self.showTechs()
275
        self.showSlots()
276
277
    def onSelectSlot(self, widget, action, data):
278
        self.structToDemolish = data.techID
279
280
    def onSelectTech(self, widget, action, data):
281
        self.techID = data.techID
282
283
    def onToggleLevel(self, widget, action, data):
284
        i = widget.data
285
        if i in self.showLevels:
286
            self.showLevels.remove(i)
287
        else:
288
            self.showLevels.append(i)
289
        self.update()
290
291
    def onCancel(self, widget, action, data):
292
        self.hide()
293
294
    def onGovTransferConfirmed(self):
295
        # we assume player wants to build just one center - in opposite case, he may change quantity in the task itself
296
        self.win.vQuantity.text = str(1)
297
        self.govTransferConfirm = True
298
        self.onConstruct(*self.govTransferData)
299
300
    def onConstruct(self, widget, action, data):
301
        planet = client.get(self.planetID, noUpdate=1)
302
        player = client.getPlayer()
303
        if not self.techID:
304
            self.win.setStatus(_('Select technology to construct.'))
305
            return
306
        if not self.targetID:
307
            self.win.setStatus(_('Select planet to construct on.'))
308
            return
309
        try:
310
            self.quantity = int(self.win.vQuantity.text)
311
        except ValueError:
312
            self.win.setStatus(_('Specify quantity (1, 2, 3, ...).'))
313
            return
314
        # government centers have additional query and if confirmed, another round of this function is called
315
        if self.techID < 1000:
316
            tech = player.shipDesigns[self.techID]
317
        else:
318
            tech = client.getTechInfo(self.techID)
319
        if not getattr(tech, 'govPwr', 0) == 0 and not self.govTransferConfirm:
320
            # confirm dialog doesn't send through parameters, so we have to save them
321
            self.govTransferData = (widget, action, data)
322
            self.confirmDlg.display(_("Do you want to issue relocation of your government?"),
323
                _("Yes"), _("No"), self.onGovTransferConfirmed)
324
        else:
325
            try:
326
                self.win.setStatus(_('Executing START CONSTRUCTION command...'))
327
                planet.prodQueue, player.stratRes = client.cmdProxy.startConstruction(self.planetID,
328
                    self.techID, self.quantity, self.targetID, self.techID < 1000,
329
                    self.win.vReportFin.checked, self.structToDemolish)
330
                self.win.setStatus(_('Command has been executed.'))
331
            except GameException, e:
332
                self.win.setStatus(e.args[0])
333
                return
334
        self.hide()
335
        self.caller.update()
336
337
    def onToggleStructures(self, widget, action, data):
338
        self.showStructures = 1
339
        self.showShips = 0
340
        self.showOther = 0
341
        self.win.setTagAttr('struct', 'visible', 1)
342
        self.win.setTagAttr('ship', 'visible', 0)
343
        self.update()
344
345
    def onToggleShips(self, widget, action, data):
346
        self.showStructures = 0
347
        self.showShips = 1
348
        self.showOther = 0
349
        self.win.setTagAttr('struct', 'visible', 0)
350
        self.win.setTagAttr('ship', 'visible', 1)
351
        self.update()
352
353
    def onToggleOther(self, widget, action, data):
354
        self.showStructures = 0
355
        self.showShips = 0
356
        self.showOther = 1
357
        self.win.setTagAttr('struct', 'visible', 0)
358
        self.win.setTagAttr('ship', 'visible', 0)
359
        self.update()
360
361
    def onInfo(self, widget, action, data):
362
        if len(self.win.vTechs.selection) == 0:
363
            return
364
        task = self.win.vTechs.selection[0]
365
        if not task.tIsShip:
366
            self.techInfoDlg.display(task.techID)
367
        else:
368
            self.constructionDlg.selectedDesignID = task.techID;
369
            self.constructionDlg.display()
370
371
    def onFilter(self, widget, action, data):
372
        self.update()
373
374
    def createUI(self):
375
        w, h = gdata.scrnSize
376
        cols = 38
377
        rows = 24 #was 23
378
        dlgWidth = cols * 20 + 4
379
        dlgHeight = rows * 20 + 4
380
        self.win = ui.Window(self.app,
381
                             modal=1,
382
                             escKeyClose=1,
383
                             movable=0,
384
                             title=_('Select new task'),
385
                             rect=ui.Rect((w - dlgWidth) / 2, (h - dlgHeight) / 2, dlgWidth, dlgHeight),
386
                             layoutManager=ui.SimpleGridLM(),
387
                             tabChange=True
388
        )
389
        self.win.subscribeAction('*', self)
390
        ui.Title(self.win, layout=(0, 0, 22, 1), text=_('Technology'),
391
                 align=ui.ALIGN_W, font='normal-bold')
392
        ui.Listbox(self.win, layout=(0, 1, 22, 19), id='vTechs',
393
                   columns=((_('Name'), 'text', 13, ui.ALIGN_W), (_('Lvl'), 'tLevel', 2, ui.ALIGN_E),
394
                   (_('Constr'), 'tProd', 3, ui.ALIGN_E), (_('ETC'), 'tETC', 3, ui.ALIGN_E)),
395
                   columnLabels=1, action='onSelectTech')
396
        # filter
397
        ui.Button(self.win, layout=(0, 20, 3, 1), text=_('Stucts'), toggle=1,
398
                  id='vStructuresToggle', action='onToggleStructures')
399
        ui.Button(self.win, layout=(3, 20, 3, 1), text=_('Ships'), toggle=1,
400
                  id='vShipsToggle', action='onToggleShips')
401
        ui.Button(self.win, layout=(6, 20, 3, 1), text=_('Misc'), toggle=1,
402
                  id='vOtherToggle', action='onToggleOther')
403
        ui.Button(self.win, layout=(9, 20, 1, 1), text=_('1'), id='vLevel1',
404
                  toggle=1, action='onToggleLevel', data=1)
405
        ui.Button(self.win, layout=(10, 20, 1, 1), text=_('2'), id='vLevel2',
406
                  toggle=1, action='onToggleLevel', data=2)
407
        ui.Button(self.win, layout=(11, 20, 1, 1), text=_('3'), id='vLevel3',
408
                  toggle=1, action='onToggleLevel', data=3)
409
        ui.Button(self.win, layout=(12, 20, 1, 1), text=_('4'), id='vLevel4',
410
                  toggle=1, action='onToggleLevel', data=4)
411
        ui.Button(self.win, layout=(13, 20, 1, 1), text=_('5'), id='vLevel5',
412
                  toggle=1, action='onToggleLevel', data=5)
413
        ui.Button(self.win, layout=(14, 20, 1, 1), text=_('6'), id='vLevel6',
414
                  toggle=1, action='onToggleLevel', data=6)
415
        ui.Button(self.win, layout=(18, 20, 4, 1), text=_('Info'), action='onInfo',
416
                  id='vInfo')
417
        # targets
418
        ui.Title(self.win, layout=(22, 0, 16, 1), text=_('Target planet'),
419
                 align=ui.ALIGN_W, font='normal-bold')
420
        ui.Listbox(self.win, layout=(22, 1, 16, 10), id='vTargets',
421
                   columns=((_('Planet'), 'text', 10, ui.ALIGN_W), (_('Owner'), 'plOwner', 5, ui.ALIGN_W)), columnLabels=1,
422
                   action='onSelectPlanet')
423
        ui.Listbox(self.win, layout=(22, 11, 16, 7), id='vTSlots',
424
                   columns=((_('Target slots'), 'text', 15, ui.ALIGN_W), ), columnLabels=1,
425
                   action='onSelectSlot')
426
        # prod types
427
        ui.Check(self.win, layout=(0, 21, 6, 1), text=_('Bio production'), tags=['struct'],
428
                 id='vBioProduction', checked=1, align=ui.ALIGN_W, action='onFilter')
429
        ui.Check(self.win, layout=(6, 21, 6, 1), text=_('En production'), tags=['struct'],
430
                 id='vEnProduction', checked=1, align=ui.ALIGN_W, action='onFilter')
431
        ui.Check(self.win, layout=(12, 21, 6, 1), text=_('CP production'), tags=['struct'],
432
                 id='vCPProduction', checked=1, align=ui.ALIGN_W, action='onFilter')
433
        ui.Check(self.win, layout=(18, 21, 6, 1), text=_('RP production'), tags=['struct'],
434
                 id='vRPProduction', checked=1, align=ui.ALIGN_W, action='onFilter')
435
        ui.Check(self.win, layout=(24, 21, 6, 1), text=_('Military'), tags=['struct'],
436
                 id='vMilitary', checked=1, align=ui.ALIGN_W, action='onFilter')
437
        ui.Check(self.win, layout=(30, 21, 6, 1), text=_('Morale'), tags=['struct'],
438
                 id='vMorale', checked=1, align=ui.ALIGN_W, action='onFilter')
439
        # ship types
440
        ui.Check(self.win, layout=(0, 21, 6, 1), text=_('Small'), tags=['ship'],
441
                 id='vSmall', checked=1, align=ui.ALIGN_W, action='onFilter')
442
        ui.Check(self.win, layout=(6, 21, 6, 1), text=_('Medium'), tags=['ship'],
443
                 id='vMedium', checked=1, align=ui.ALIGN_W, action='onFilter')
444
        ui.Check(self.win, layout=(12, 21, 6, 1), text=_('Large'), tags=['ship'],
445
                 id='vLarge', checked=1, align=ui.ALIGN_W, action='onFilter')
446
        ui.Check(self.win, layout=(18, 21, 6, 1), text=_('Civilian'), tags=['ship'],
447
                 id='vCivShip', checked=1, align=ui.ALIGN_W, action='onFilter')
448
        ui.Check(self.win, layout=(24, 21, 6, 1), text=_('Military'), tags=['ship'],
449
                 id='vMilShip', checked=1, align=ui.ALIGN_W, action='onFilter')
450
        # build
451
        ui.Title(self.win, layout=(22, 18, 16, 1), text=_('Options'),
452
                 align=ui.ALIGN_W, font='normal-bold')
453
        ui.Label(self.win, layout=(22, 19, 10, 1), text=_('Quantity'), align=ui.ALIGN_W)
454
        ui.Entry(self.win, layout=(33, 19, 5, 1), id='vQuantity', align=ui.ALIGN_E, orderNo=1)
455
        ui.Check(self.win, layout=(31, 20, 7, 1), id='vReportFin', text=_('Report finalization'))
456
        ui.Title(self.win, layout=(0, 22, 28, 1), id='vStatusBar', align=ui.ALIGN_W)
457
        ui.TitleButton(self.win, layout=(28, 22, 5, 1), text=_('Cancel'), action='onCancel')
458
        ui.TitleButton(self.win, layout=(33, 22, 5, 1), text=_('Construct'), action='onConstruct')
459