Passed
Pull Request — master (#291)
by Marek
02:11
created

osci.dialog.TechInfoDlg.TechInfoDlg._preResearch()   A

Complexity

Conditions 1

Size

Total Lines 6
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nop 2
dl 0
loc 6
rs 10
c 0
b 0
f 0
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 string
21
22
import pygameui as ui
23
24
from ige.ospace import Rules, Utils
25
import ige.ospace.Const as Const
26
27
from osci import gdata, res, client
28
29
30
def cclass2Text(cclass):
31
    return [_("small"), _("medium"), _("large"), _("planet")][cclass]
32
33
34
def structWeapons2Text(array):
35
    string = ''
36
    i = 0
37
    j = 0
38
    for weaponNum in array:
39
        if i>3:
40
            break
41
        if weaponNum > 0:
42
            if j > 0:
43
                string += _(', ')
44
            string += _('%d') % ( weaponNum )
45
            string += [_("S"), _("M"), _("L"), _("P"), _("?"), _("?"), _("?"), _("?"), _("?"), _("?")][i]
46
            j+=1
47
        i+=1
48
    return string
49
50
51
def bool2Text(value):
52
    return _("Yes") if value else _("No")
53
54
55
def perc2Text(value):
56
    return _('%d%%') % (value * 100)
57
58
59
def percBase1_2Text(value):
60
    return _('%+d%%') % ((value - 1) * 100)
61
62
63
def perc100_2Text(value):
64
    return _('%d%%') % (value)
65
66
67
def getTechName(techID):
68
    try:
69
        return client.getFullTechInfo(techID).name
70
    except:
71
        return _('Unknown')
72
73
74
def getTechShortname(techID):
75
    tech = client.getFullTechInfo(techID)
76
    try:
77
        return tech.shortname if tech.shortname else tech.name
78
    except:
79
        return _('Unknown')
80
81
82
def getRateName(rate):
83
    return _('%d Turns') % (int(rate))
84
85
86
V_NONE = 0x00
87
V_STRUCT = 0x01
88
V_HULL = 0x02
89
V_SEQUIP = 0x04
90
V_PROJECT = 0x08
91
V_EFF = 0x10 # attr * techEff
92
V_EFF_REV = 0x20 # attr / techEff
93
V_ALL = V_STRUCT|V_HULL|V_SEQUIP|V_PROJECT
94
95
techAttrs = {}
96
97
defaultAttr = (_('Not specified'), V_NONE, True, None, int)
98
99
100
def addAttr(attr, descr, props, showIfDefault, default=0, convertor=int):
101
    global techAttrs
102
    techAttrs[attr] = (descr, props, showIfDefault, default, convertor)
103
104
105
addAttr('buildProd', _('Constr. reqs - construction points'), V_ALL, 0)
106
107
addAttr('operBio', _('Operational reqs - biomatter'), V_ALL, 0)
108
addAttr('operMin', _('Operational reqs - minerals'), V_ALL, 0)
109
addAttr('operEn', _('Operational reqs - energy'), V_ALL, 0)
110
addAttr('operWorkers', _('Operational reqs - workers'), V_ALL, 0)
111
112
addAttr('prodBio', _('Production - biomatter'), V_STRUCT | V_EFF, 0)
113
addAttr('prodMin', _('Production - minerals'), V_STRUCT | V_EFF, 0)
114
addAttr('prodEn', _('Production - energy'), V_STRUCT | V_EFF, 0)
115
addAttr('prodPop', _('Production - population'), V_STRUCT | V_EFF, 0)
116
addAttr('prodProd', _('Production - constr. points'), V_STRUCT | V_PROJECT | V_EFF, 0)
117
addAttr('prodSci', _('Production - research points'), V_STRUCT | V_PROJECT | V_EFF, 0)
118
addAttr('prodEnv', _('Production - env. effect'), V_STRUCT | V_EFF, 0)
119
120
addAttr('solarMod', _('Orbital Shift Effect'), V_STRUCT | V_EFF, 0)
121
122
addAttr('storBio', _('Storage - biomatter'), V_STRUCT | V_EFF, 0)
123
addAttr('storMin', _('Storage - minerals'), V_STRUCT | V_EFF, 0)
124
addAttr('storEn', _('Storage - energy'), V_ALL | V_EFF, 0)
125
addAttr('storPop', _('Accommodate population'), V_STRUCT | V_EFF, 0)
126
127
addAttr('revoltThr', _('Lowers revolt threshold by'), V_STRUCT | V_PROJECT | V_EFF, 0)
128
addAttr('moraleTrgt', _('Increases max morale by'), V_STRUCT | V_PROJECT | V_EFF, 0)
129
addAttr('govPwr', _('Government power'), V_STRUCT | V_EFF, 0)
130
addAttr('maxHP', _('Hit points'), V_STRUCT | V_HULL | V_SEQUIP | V_EFF, 0)
131
132
addAttr('scannerPwr', _('Scanner power'), V_STRUCT | V_SEQUIP | V_EFF, 0)
133
addAttr('planetShield', _('Planetary shield'), V_STRUCT | V_EFF, 0)
134
addAttr('systemAtt', _('Fleet attack (bonus)'), V_STRUCT | V_EFF, 0)
135
addAttr('systemDef', _('Fleet defense (bonus)'), V_STRUCT | V_EFF, 0)
136
addAttr('refuelMax', _('Maximum refuel percent'), V_STRUCT | V_EFF, 0, convertor=perc100_2Text)
137
addAttr('refuelInc', _('Refuel increase percent'), V_STRUCT | V_EFF, 0, convertor=perc100_2Text)
138
addAttr('repairShip', _('Ship repair percent'), V_STRUCT | V_EFF, 0, convertor=perc100_2Text)
139
addAttr('upgradeShip', _('Ship upgrade capacity'), V_STRUCT | V_EFF, 0)
140
addAttr('trainShipInc', _('Exp. points per turn'), V_STRUCT | V_EFF, 0, convertor=float)
141
addAttr('trainShipMax', _('Exp. cap (base exp multiple)'), V_STRUCT | V_EFF, 0)
142
addAttr('fleetSpeedBoost', _('Boost speed of fleets'), V_STRUCT | V_EFF, 0, convertor=float)
143
addAttr('structWeapons', _('Weapons'), V_STRUCT, 0, convertor=structWeapons2Text)
144
145
addAttr('weaponClass', _('Target class'), V_SEQUIP, True, convertor=cclass2Text)
146
addAttr('weaponDmgMin', _('Weapon minimum damage'), V_SEQUIP | V_EFF, 0)
147
addAttr('weaponDmgMax', _('Weapon maximum damage'), V_SEQUIP | V_EFF, 0)
148
addAttr('weaponIsMissile', _('Missile weapon (ECM counts)'), V_SEQUIP | V_HULL, 0, convertor=bool2Text)
149
addAttr('weaponIgnoreShield', _('Weapon ignore shield'), V_SEQUIP | V_HULL, 0, convertor=bool2Text)
150
addAttr('weaponAtt', _('Weapon attack'), V_SEQUIP | V_EFF, 0)
151
addAttr('weaponROF', _('Weapon Rate Of Fire'), V_SEQUIP, 0, convertor=float)
152
153
addAttr('mineclass', _('Mine Class Deployed'), V_STRUCT, 0, convertor=getTechShortname)
154
addAttr('minenum', _('Maximum Supported Mines'), V_STRUCT | V_EFF, 0, convertor=int)
155
addAttr('minerate', _('Mine Rate of Deploy'), V_STRUCT | V_EFF_REV, 0, convertor=getRateName)
156
157
addAttr('minHull', _('Minimum required hull'), V_SEQUIP | V_HULL, 0, convertor=cclass2Text)
158
addAttr('weight', _('Weight'), V_SEQUIP | V_HULL, 0)
159
addAttr('slots', _('Slots'), V_SEQUIP | V_HULL, 0)
160
addAttr('maxWeight', _('Maximum payload'), V_HULL, 0)
161
addAttr('engPwr', _('Engine power'), V_SEQUIP | V_EFF, 0)
162
addAttr('engStlPwr', _('Engine sub-light speed power'), V_SEQUIP | V_EFF, 0)
163
164
addAttr('signature', _('Scan signature'), V_SEQUIP | V_HULL, 0)
165
addAttr('signatureCloak', _('Signature visibility'), V_SEQUIP | V_HULL, 0)
166
addAttr('signatureDecloak', _('Signature visibility'), V_SEQUIP | V_HULL, 0)
167
addAttr('minSignature', _('Min. signature'), V_SEQUIP | V_HULL, 0)
168
169
addAttr('combatDef', _('Combat defence'), V_SEQUIP | V_HULL | V_EFF, 0)
170
addAttr('combatAtt', _('Combat attack'), V_SEQUIP | V_HULL | V_EFF, 0)
171
addAttr('missileDef', _('Missile defence'), V_SEQUIP | V_EFF, 0)
172
addAttr('combatAttPerc', _('Combat defense (extra)'), V_SEQUIP | V_HULL | V_EFF, 0, default=1, convertor=percBase1_2Text)
173
addAttr('combatDefPerc', _('Combat attack (extra)'), V_SEQUIP | V_HULL | V_EFF, 0, default=1, convertor=percBase1_2Text)
174
addAttr('missileDefPerc', _('Missile defence (extra)'), V_SEQUIP | V_EFF, 0, default=1, convertor=percBase1_2Text)
175
176
addAttr('shieldPerc', _('Shield strength'), V_SEQUIP | V_HULL | V_EFF, 0, convertor=perc2Text)
177
addAttr('shieldRechargeFix', _('Shield recharge fixed'), V_SEQUIP | V_HULL | V_EFF, 0)
178
addAttr('shieldRechargePerc', _('Shield recharge percent'), V_SEQUIP | V_HULL | V_EFF, 0, convertor=perc2Text)
179
addAttr('damageAbsorb', _('Armor damage absorbstion'), V_SEQUIP | V_HULL, 0)
180
181
addAttr('addMP', _('Device MP'), V_SEQUIP | V_HULL, 0)
182
183
184
class TechInfoDlg:
185
186
    def __init__(self, app):
187
        self.app = app
188
        self.createUI()
189
190
    def display(self, techID):
191
        self.techID = techID
192
        self.show()
193
        self.win.show()
194
        # register for updates
195
        if self not in gdata.updateDlgs:
196
            gdata.updateDlgs.append(self)
197
198
    def hide(self):
199
        self.win.setStatus(_("Ready."))
200
        self.win.hide()
201
        # unregister updates
202
        if self in gdata.updateDlgs:
203
            gdata.updateDlgs.remove(self)
204
205
    def update(self):
206
        self.show()
207
208
    def _getGranularDependency(self, techMod):
209
        descr = []
210
        if techMod[0]:
211
            descr.append(_(" - %d %% on planet's environment") % (techMod[0] * 100))
212
        if techMod[1]:
213
            descr.append(_(" - %d %% on planet's min. abundance") % (techMod[1] * 100))
214
        if techMod[2]:
215
            descr.append(_(" - %d %% on planet's en. abundance") % (techMod[2] * 100))
216
        if techMod[3]:
217
            descr.append(_(" - %d %% is not dependent on any planet's attribute") % (techMod[3] * 100))
218
        return descr
219
220
    def _productionDependency(self, tech):
221
        descr = []
222
        for techModName, text in [('prodBioMod', _('Bio')),
223
                                  ('prodEnMod', _('Energy')),
224
                                  ('prodProdMod', _('Constr. point')),
225
                                  ('prodSciMod', _('Research point'))]:
226
            techMod = getattr(tech, techModName)
227
            if techMod not in ([0, 0, 0, 0], [0, 0, 0, 1]):
228
                descr.append(_("%s production depends:" % text))
229
                descr.extend(self._getGranularDependency(techMod))
230
                descr.append("")
231
        return descr
232
233
    def _getStratResText(self, tech, attr):
234
        try:
235
            resourceDict = getattr(tech, attr)
236
        except AttributeError:
237
            return ""
238
        requiredStratRes = []
239
        for res in resourceDict:
240
            try:
241
                amount = resourceDict[res] / float(Rules.stratResAmountBig)
242
            except IndexError:
243
                # this is case of research resources - it's pure list, not a dictionary
244
                requiredStratRes += [gdata.stratRes[res]]
245
            else:
246
                # continuation of build resources
247
                requiredStratRes += ['{0} ({1})'.format(gdata.stratRes[res], amount)]
248
        return ', '.join(requiredStratRes)
249
250
    def _researchContext(self, tech):
251
        player = client.getPlayer()
252
253
        descr = []
254
        improvement = player.techs.get(self.techID, 0)
255
        if hasattr(tech, 'researchMod'):
256
            prefix = _('Improvement') if improvement else _('Research')
257
            descr.append(_('%s costs: %d') % (prefix, Utils.getTechRCost(player, self.techID)))
258
            descr.append('')
259
        # requires
260
        if tech.researchRequires and improvement == 0:
261
            descr.append(_('Research requires:'))
262
            for tmpTechID, improvement in tech.researchRequires:
263
                tmpTech = client.getTechInfo(tmpTechID)
264
                descr.append(_(' - %s improvement %d (TL%d)') % (tmpTech.name, improvement, tmpTech.level))
265
            if hasattr(tech, "researchReqSRes"):
266
                for stratRes in tech.researchReqSRes:
267
                    descr.append(_(" - %s (strategic resource)") % (gdata.stratRes[stratRes]))
268
            descr.append('')
269
        if hasattr(tech, "researchDisables") and tech.researchDisables:
270
            descr.append(_("Research disables:"))
271
            for tmpTechID in tech.researchDisables:
272
                tmpTech = client.getTechInfo(tmpTechID)
273
                descr.append(_(" - %s (TL%d)") % (tmpTech.name, tmpTech.level))
274
            descr.append('')
275
        return descr
276
277
    def _preResearch(self, tech):
278
        descr = []
279
        descr.append(_('Estimated use:'))
280
        descr.extend(tech.textPreRsrch.split('\n'))
281
        descr.append('')
282
        return descr
283
284
    def _getTechType(self, tech):
285
        techType = V_NONE
286
        if getattr(tech, 'isStructure', 0):
287
            techType = V_STRUCT
288
        elif getattr(tech, 'isShipHull', 0):
289
            techType = V_HULL
290
        elif getattr(tech, 'isShipEquip', 0):
291
            techType = V_SEQUIP
292
        elif getattr(tech, 'isProject', 0):
293
            techType = V_PROJECT
294
        return techType
295
296
    def _prepareTypeButtons(self, tech):
297
        techType = self._getTechType(tech)
298
299
        self.win.vStruct.pressed = techType == V_STRUCT
300
        self.win.vStruct.enabled = getattr(tech, 'isStructure', 0)
301
        self.win.vHull.pressed = techType == V_HULL
302
        self.win.vHull.enabled = getattr(tech, 'isShipHull', 0)
303
        self.win.vSEquip.pressed = techType == V_SEQUIP
304
        self.win.vSEquip.enabled = getattr(tech, 'isShipEquip', 0)
305
        self.win.vProject.pressed = techType == V_PROJECT
306
        self.win.vProject.enabled = getattr(tech, 'isProject', 0)
307
308
    def _processAttributes(self, tech):
309
        techEff = Rules.techImprEff[client.getPlayer().techs.get(self.techID, Rules.techBaseImprovement)]
310
        items = []
311
        techType = self._getTechType(tech)
312
313
        for attr in dir(tech):
314
            value = getattr(tech, attr)
315
            descr, props, showIfDef, default, convertor = techAttrs.get(attr, defaultAttr)
316
            if techType & props and (value != default or showIfDef):
317
                item = ui.Item(descr, tValue=convertor(value))
318
                if V_EFF & props:
319
                    item.font = 'normal-bold'
320
                    item.tValue = convertor(value * techEff)
321
                elif V_EFF_REV & props:
322
                    item.font = 'normal-bold'
323
                    item.tValue = convertor(value / techEff)
324
                items.append(item)
325
        return items
326
327
    def _researchEnablement(self, tech):
328
        descr = []
329
        tmp = []
330
        player = client.getPlayer()
331
332
        for improvement in range(1, 6):
333
            for tmpTechID in tech.researchEnables[improvement]:
334
                tmpTech = client.getTechInfo(tmpTechID)
335
                racesDispl = ""
336
                raceChoosen = None
337
                if player.techLevel == 1:
338
                    for techID in player.techs.keys():
339
                        if techID in (1990, 1991, 1992):
340
                            raceChoosen = client.getTechInfo(techID).data
341
                else:
342
                    raceChoosen = player.race
343
                if len(tmpTech.researchRaces) > 0 and len(tmpTech.researchRaces) < 3 and not raceChoosen:
344
                    racesDispl = _(", only for %s") % tmpTech.researchRaces
345
                if not raceChoosen or len(tmpTech.researchRaces) == 0 or raceChoosen in tmpTech.researchRaces:
346
                    tmp.append(_(' - %s (with improvement %d, on TL%d%s)') % (tmpTech.name, improvement, tmpTech.level, racesDispl))
347
        if tmp:
348
            descr.append(_('Research/Improvement enables:'))
349
            descr.extend(tmp)
350
            descr.append('')
351
        return descr
352
353
    def _stratResources(self, tech):
354
        descr = []
355
356
        requiredStratResForBuilding = self._getStratResText(tech, "buildSRes")
357
        requiredStratResForResearch = self._getStratResText(tech, "researchReqSRes")
358
        if len(requiredStratResForBuilding) > 0 or len(requiredStratResForResearch) > 0:
359
            descr.append(_("Required strategic resources:"))
360
            if len(requiredStratResForBuilding) > 0:
361
                descr.append(_(" - for building: %s") % requiredStratResForBuilding)
362
            if len(requiredStratResForResearch) > 0:
363
                descr.append(_(" - for research: %s") % requiredStratResForResearch)
364
365
            descr.append("")
366
        return descr
367
368
    def _descriptionFlavor(self, tech):
369
        descr = []
370
371
        # description
372
        descr.append(_('Description:'))
373
        if tech.textDescr != u'Not specified':
374
            descr.extend(tech.textDescr.split('\n'))
375
        else:
376
            descr.extend(tech.textPreRsrch.split('\n'))
377
        descr.append('')
378
        # flavor
379
        descr.append(_('Rumours:'))
380
        descr.extend(tech.textFlavor.split('\n'))
381
        descr.append('')
382
        return descr
383
384
    def show(self):
385
        tech = client.getTechInfo(self.techID)
386
        self.win.title = _('Technology: %s (TL%d)') % (tech.name, tech.level)
387
        # fill data
388
        # fill description
389
        descr = []
390
        descr.extend(self._researchContext(tech))
391
        if hasattr(tech, 'partialData') and hasattr(tech, 'textPreRsrch'):
392
            # preresearch info
393
            descr.extend(self._preResearch)
394
        elif not hasattr(tech, 'partialData'):
395
            descr.extend(self._researchEnablement(tech))
396
            descr.extend(self._productionDependency(tech))
397
            descr.extend(self._stratResources(tech))
398
            descr.extend(self._descriptionFlavor(tech))
399
400
        if not len(descr):
401
            descr.append(_('No information available'))
402
        #
403
        self.win.vDescr.text = descr
404
405
        self._prepareTypeButtons(tech)
406
407
        self.win.vData.items = self._processAttributes(tech)
408
        self.win.vData.itemsChanged()
409
410
    def onCancel(self, widget, action, data):
411
        self.hide()
412
413
    def onClose(self, widget, action, data):
414
        self.hide()
415
416
    def createUI(self):
417
        w, h = gdata.scrnSize
418
        self.win = ui.Window(self.app,
419
                             modal=1,
420
                             escKeyClose=1,
421
                             titleOnly=w == 800 and h == 600,
422
                             movable=0,
423
                             title=_('Split Fleet'),
424
                             rect=ui.Rect((w - 800 - 4 * (w != 800)) / 2,
425
                                          (h - 600 - 4 * (h != 600)) / 2,
426
                                          800 + 4 * (w != 800),
427
                                          580 + 4 * (h != 600)),
428
                             layoutManager=ui.SimpleGridLM())
429
        self.win.subscribeAction('*', self)
430
        # tech data
431
        ui.Title(self.win, layout=(0, 0, 18, 1), text=_('Data'),
432
                 align=ui.ALIGN_W, font='normal-bold')
433
        ui.Listbox(self.win, layout=(0, 1, 18, 25), id='vData',
434
                   columns=((_('Property'), 'text', 11, ui.ALIGN_W),
435
                   (_('Value'), 'tValue', 7, ui.ALIGN_E)),
436
                   columnLabels=0)
437
        ui.Button(self.win, layout=(1, 26, 4, 1), text=_('Structure'),
438
                  id='vStruct', toggle=0, data=V_STRUCT)
439
        ui.Button(self.win, layout=(5, 26, 4, 1), text=_('Ship Hull'),
440
                  id='vHull', toggle=0, data=V_HULL)
441
        ui.Button(self.win, layout=(9, 26, 4, 1), text=_('Ship Eq.'),
442
                  id='vSEquip', toggle=0, data=V_SEQUIP)
443
        ui.Button(self.win, layout=(13, 26, 4, 1), text=_('Project'),
444
                  id='vProject', toggle=0, data=V_PROJECT)
445
        ui.Button(self.win, layout=(17, 26, 1, 1), text='',
446
                  id='vEmpty1', toggle=0)
447
        ui.Button(self.win, layout=(0, 26, 1, 1), text='',
448
                  id='vEmpty2', toggle=0)
449
        # text field
450
        ui.Title(self.win, layout=(18, 0, 22, 1), text=_('Description'),
451
                 align=ui.ALIGN_W, font='normal-bold')
452
        s = ui.Scrollbar(self.win, layout=(39, 1, 1, 26))
453
        t = ui.Text(self.win, layout=(18, 1, 21, 26), id='vDescr', editable=0)
454
        t.attachVScrollbar(s)
455
        # status bar + submit/cancel
456
        ui.TitleButton(self.win, layout=(35, 27, 5, 1), text=_('Close'), action='onClose')
457
        ui.Title(self.win, id='vStatusBar', layout=(0, 27, 35, 1), align=ui.ALIGN_W)
458