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