Completed
Pull Request — master (#208)
by Marek
02:28
created

FleetCommandDlg._evalEta()   A

Complexity

Conditions 2

Size

Total Lines 11
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
nop 4
dl 0
loc 11
rs 9.9
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
21
import pygameui as ui
22
from osci.StarMapWidget import StarMapWidget
23
from ige.ospace import Rules
24
from osci import gdata, res, client, sequip
25
import ige.ospace.Const as Const
26
from ige import log
27
import ige
28
import math
29
import string
30
31
class FleetCommandDlg:
32
33
    NEW_COMMAND = 10000
34
35
    def __init__(self, app):
36
        self.app = app
37
        self.createUI()
38
        self.targetID = Const.OID_NONE
39
        self.targetPlayerID = Const.OID_NONE
40
41
    def display(self, fleetDlg, cmdIndex):
42
        self.fleetDlg = fleetDlg
43
        self.cmdIndex = cmdIndex
44
        fleet = client.get(self.fleetDlg.fleetID, noUpdate = 1)
45
        self.win.vStarMap.centerPos = (fleet.x, fleet.y)
46
        self.win.vStarMap.alwaysShowRangeFor = fleet.oid
47
        self.win.vStarMap.precompute()
48
        self.command = Const.FLACTION_MOVE
49
        self.deplShipIndex = 0
50
        if self.targetID:
51
            target = client.get(self.targetID, noUpdate = 1)
52
            self.win.vStarMap.highlightPos = (target.x, target.y)
53
        self.showCommands()
54
        self.win.show()
55
        # register for updates
56
        if self not in gdata.updateDlgs:
57
            gdata.updateDlgs.append(self)
58
59
    def hide(self):
60
        self.win.setStatus(_("Ready."))
61
        self.win.hide()
62
        # unregister updates
63
        if self in gdata.updateDlgs:
64
            gdata.updateDlgs.remove(self)
65
66
    def update(self):
67
        self.win.vStarMap.precompute()
68
        self.showCommands()
69
70
    def _evalEta(self, fleet, origin, target):
71
        dx = origin.x - target.x
72
        dy = origin.y - target.y
73
        lnght = math.hypot(dx, dy)
74
        speedBoost = getattr(fleet, "speedBoost", 1)
75
        fleetSpeed = getattr(fleet, "speed", 0)
76
        maxDelta = fleetSpeed / Rules.turnsPerDay * speedBoost
77
        if maxDelta != 0:
78
            return lnght / maxDelta
79
        else:
80
            return None
81
82
    def showCommands(self):
83
        self.win.vMoveBtn.pressed =  self.command == Const.FLACTION_MOVE
84
        self.win.vAttackBtn.pressed =  self.command == Const.FLACTION_DECLAREWAR
85
        self.win.vDeplShipBtn.pressed =  self.command == Const.FLACTION_DEPLOY
86
        self.win.vWaitBtn.pressed =  self.command == Const.FLACTION_WAIT
87
        self.win.vRefuelBtn.pressed =  self.command == Const.FLACTION_REFUEL
88
        self.win.vRepeatBtn.pressed =  self.command == Const.FLACTION_REPEATFROM
89
        self.win.vWormholeBtn.pressed =  self.command == Const.FLACTION_ENTERWORMHOLE
90
        # hide/show widgets
91
        for widget in self.win.widgets:
92
            if widget.tags and self.command in widget.tags:
93
                widget.visible = 1
94
            elif widget.tags:
95
                widget.visible = 0
96
        # target
97
        if self.targetID == Const.OID_NONE:
98
            info = _('No target selected')
99
        elif self.command in (Const.FLACTION_MOVE, Const.FLACTION_REFUEL, Const.FLACTION_ENTERWORMHOLE):
100
            target = client.get(self.targetID, noUpdate = 1)
101
            info = getattr(target, 'name', res.getUnknownName())
102
        elif self.command == Const.FLACTION_DEPLOY:
103
            target = client.get(self.targetID, noUpdate = 1)
104
            if target.type == Const.T_PLANET:
105
                info = getattr(target, 'name', res.getUnknownName())
106
            else:
107
                info = _('No planet selected')
108
        elif self.command == Const.FLACTION_DECLAREWAR:
109
            if self.targetPlayerID not in (Const.OID_NONE, client.getPlayerID()):
110
                target = client.get(self.targetPlayerID, noUpdate = 1)
111
                info = getattr(target, 'name', res.getUnknownName())
112
            else:
113
                info = _("No commander specified.")
114
        else:
115
            info = _('?')
116
        self.win.vTarget.text = info
117
        if self.targetID != Const.OID_NONE:
118
            curTarget = client.get(self.targetID, noUpdate = 1)
119
            fleet = client.get(self.fleetDlg.fleetID, noUpdate = 1)
120
            target = Const.OID_NONE
121
            oldTarget = fleet # we can hack it - only coordinates are necessary
122
            if fleet.actions:
123
                waitTurns = sum([x[2] for x in fleet.actions[fleet.actionIndex:] if x[0] == Const.FLACTION_WAIT])
124
                eta = waitTurns # baseline
125
                targetIDs = [x[1] for x in fleet.actions[fleet.actionIndex:]]
126
                targetIDs.append(self.targetID)
127
                for newTargetID in targetIDs:
128
                    if newTargetID != Const.OID_NONE:
129
                        newTarget = client.get(newTargetID)
130
                        newEta = self._evalEta(fleet, oldTarget, newTarget)
131
                        if newEta is not None:
132
                            eta += newEta
133
                        else:
134
                            eta = None
135
                            break
136
                        oldTarget = newTarget
137
138
                if eta is not None:
139
                    self.win.vEta.text = res.formatTime(eta)
140
                else:
141
                    self.win.vEta.text = _("N/A")
142
            else:
143
                self.win.vEta.text = _("N/A")
144
145
        # ships
146
        fleet = client.get(self.fleetDlg.fleetID, noUpdate = 1)
147
        self.deplShips = []
148
        # collect buildings
149
        for designID, a, b, c in fleet.ships: #fixed bug in reference of designID...added a, b, c to do it; breaks list lookup otherwise (not sure why) with hash error --RC
150
            tech = client.getPlayer().shipDesigns[designID]
151
            if tech.deployStructs:
152
                self.deplShips.append(designID)
153
                log.debug('Adding design with structure',designID)
154
            elif tech.deployHandlers:
155
                hasHandler = False
156
                for handler in tech.deployHandlers:
157
                    if handler != '': #why are blank handlers getting added in ship construction? catch here for now
158
                        hasHandler = True
159
                        break
160
                if hasHandler:
161
                    self.deplShips.append(designID)
162
                    log.debug('Adding design with project',designID)
163
        # correct buildingIndex
164
        if self.deplShipIndex >= len(self.deplShips):
165
            self.deplShipIndex = 0
166
        if self.deplShips:
167
            self.win.vDeplShipBtn.enabled = 1
168
            techID = self.deplShips[self.deplShipIndex]
169
            self.win.vDeplShip.text = client.getPlayer().shipDesigns[techID].name
170
            self.win.vDeplShip.data = techID
171
        else:
172
            self.win.vDeplShipBtn.enabled = 0
173
            if self.command == Const.FLACTION_DEPLOY:
174
                self.command == Const.FLACTION_MOVE
175
                self.showCommands()
176
177
    def onSelectCommand(self, widget, action, data):
178
        self.command = widget.data
179
        self.showCommands()
180
181
    def onSelectMapObj(self, widget, action, data):
182
        target = client.get(data, noUpdate = 1)
183
        if target.type == Const.T_PLANET:
184
            self.targetID = target.oid
185
            self.win.vStarMap.highlightPos = (target.x, target.y)
186
        elif target.type in (Const.T_SYSTEM, Const.T_WORMHOLE):
187
            self.targetID = target.oid
188
            self.win.vStarMap.highlightPos = (target.x, target.y)
189
        else:
190
            self.win.vStarMap.hightlightPos = None
191
        self.targetPlayerID = getattr(target, "owner", Const.OID_NONE)
192
        self.showCommands()
193
194
    def onCancel(self, widget, action, data):
195
        self.hide()
196
197
    def onDeplShipChange(self, widget, action, data):
198
        self.deplShipIndex += 1
199
        self.showCommands()
200
201
    def onOrder(self, widget, action, data):
202
        targetID = self.targetID
203
        if self.command in (Const.FLACTION_MOVE, Const.FLACTION_REFUEL):
204
            if self.targetID == Const.OID_NONE:
205
                self.win.setStatus(_('Select target, please.'))
206
                return
207
            commandData = None
208
        elif self.command == Const.FLACTION_ENTERWORMHOLE:
209
            if self.targetID == Const.OID_NONE:
210
                self.win.setStatus(_('Select target, please.'))
211
                return
212
            target = client.get(self.targetID, noUpdate = 1)
213
            if target.type != Const.T_WORMHOLE:
214
                self.win.setStatus(_('Can only traverse wormholes.'))
215
                return
216
            commandData = None
217
        elif self.command == Const.FLACTION_DECLAREWAR:
218
            if self.targetPlayerID == Const.OID_NONE:
219
                self.win.setStatus(_('Select object with valid owner, please.'))
220
                return
221
            commandData = self.targetPlayerID
222
            targetID = Const.OID_NONE
223
        elif self.command == Const.FLACTION_DEPLOY:
224
            if self.targetID == Const.OID_NONE:
225
                self.win.setStatus(_('Select target planet, please.'))
226
                return
227
            target = client.get(self.targetID, noUpdate = 1)
228
            if target.type != Const.T_PLANET:
229
                self.win.setStatus(_('You can build on planets only.'))
230
                return
231
            commandData = self.win.vDeplShip.data
232
        elif self.command == Const.FLACTION_WAIT:
233
            try:
234
                commandData = int(self.win.vTurns.text)
235
            except ValueError:
236
                self.win.setStatus(_('Enter number into "Turns" field.'))
237
                return
238
            if commandData < 1:
239
                self.win.setStatus(_('"Turns" must be 1 or greater.'))
240
                return
241
            targetID = Const.OID_NONE
242
        elif self.command == Const.FLACTION_REPEATFROM:
243
            try:
244
                commandData = int(self.win.vStartFrom.text)
245
            except ValueError:
246
                self.win.setStatus(_('Enter number into "Start from command #" field.'))
247
                return
248
            if commandData < 1:
249
                self.win.setStatus(_('"Start from command #" must be 1 or greater'))
250
                return
251
            commandData -= 1
252
            targetID = Const.OID_NONE
253
        else:
254
            self.win.setStatus(_('Command not supported yet.'))
255
            return
256
        try:
257
            self.win.setStatus(_('Executing ISSUE COMMAND command...'))
258
            fleet = client.get(self.fleetDlg.fleetID, noUpdate = 1)
259
            fleet.actions, fleet.actionIndex = client.cmdProxy.addAction(self.fleetDlg.fleetID,
260
                self.cmdIndex, self.command, targetID, commandData)
261
            self.win.setStatus(_('Command has been executed.'))
262
            self.hide()
263
            self.fleetDlg.update()
264
            gdata.mainGameDlg.update()
265
        except ige.GameException, e:
266
            self.win.setStatus(_(e.args[0]))
267
268
    def createUI(self):
269
        w, h = gdata.scrnSize
270
        self.win = ui.Window(self.app,
271
            modal = 1,
272
            escKeyClose = 1,
273
            titleOnly = w == 800 and h == 600,
274
            movable = 0,
275
            title = _('New Command'),
276
            rect = ui.Rect((w - 800 - 4 * (w != 800)) / 2, (h - 600 - 4 * (h != 600)) / 2, 800 + 4 * (w != 800), 580 + 4 * (h != 600)),
277
            layoutManager = ui.SimpleGridLM(),
278
            tabChange = True
279
        )
280
        self.win.subscribeAction('*', self)
281
        StarMapWidget(self.win, layout = (0, 0, 40, 24),
282
            id = 'vStarMap', action = 'onSelectMapObj')
283
        # order buttons
284
        ui.Title(self.win, layout = (0, 24, 40, 1), text = _('Select command'),
285
            font = 'normal-bold', align = ui.ALIGN_W)
286
        ui.Button(self.win, layout = (0, 25, 5, 1), text = _('Move'), toggle = 1,
287
            id = 'vMoveBtn', action = 'onSelectCommand', data = Const.FLACTION_MOVE)
288
        ui.Button(self.win, layout = (5, 25, 5, 1), text = _('Refuel at'), toggle = 1,
289
            id = 'vRefuelBtn', action = 'onSelectCommand', data = Const.FLACTION_REFUEL)
290
        ui.Button(self.win, layout = (10, 25, 5, 1), text = _('Deploy'), toggle = 1,
291
            id = 'vDeplShipBtn', action = 'onSelectCommand', data = Const.FLACTION_DEPLOY)
292
        ui.Button(self.win, layout = (15, 25, 5, 1), text = _('Wait'), toggle = 1,
293
            id = 'vWaitBtn', action = 'onSelectCommand', data = Const.FLACTION_WAIT)
294
        ui.Button(self.win, layout = (20, 25, 5, 1), text = _('Declare War'), toggle = 1,
295
            id = 'vAttackBtn', action = 'onSelectCommand', data = Const.FLACTION_DECLAREWAR)
296
        ui.Button(self.win, layout = (25, 25, 5, 1), text = _('Repeat'), toggle = 1,
297
            id = 'vRepeatBtn', action = 'onSelectCommand', data = Const.FLACTION_REPEATFROM)
298
        ui.Button(self.win, layout = (30, 25, 5, 1), text = _('Use Wormhole'), toggle = 1,
299
            id = 'vWormholeBtn', action = 'onSelectCommand', data = Const.FLACTION_ENTERWORMHOLE)
300
        # Target indicator
301
        ui.Label(self.win, layout = (0, 26, 5, 1), text = _('Target'),
302
            align = ui.ALIGN_W, tags = [Const.FLACTION_MOVE, Const.FLACTION_DEPLOY, Const.FLACTION_REFUEL, Const.FLACTION_ENTERWORMHOLE])
303
        ui.Label(self.win, layout = (0, 26, 5, 1), text = _('At commander'),
304
            align = ui.ALIGN_W, tags = [Const.FLACTION_DECLAREWAR])
305
        ui.Label(self.win, layout = (5, 26, 10, 1), id = 'vTarget',
306
            align = ui.ALIGN_E, tags = [Const.FLACTION_MOVE, Const.FLACTION_DEPLOY,
307
            Const.FLACTION_DECLAREWAR, Const.FLACTION_REFUEL, Const.FLACTION_ENTERWORMHOLE])
308
        # Delay indicator
309
        ui.Label(self.win, layout = (0, 26, 5, 1), text = _('Turns'),
310
            align = ui.ALIGN_W, tags = [Const.FLACTION_WAIT])
311
        ui.Entry(self.win, layout = (5, 26, 5, 1), id = 'vTurns', text = '1',
312
            align = ui.ALIGN_E, tags = [Const.FLACTION_WAIT], orderNo = 1)
313
        # Ship to deploy
314
        ui.Label(self.win, layout = (15, 26, 5, 1), text = _('Ship'),
315
            align = ui.ALIGN_E, tags = [Const.FLACTION_DEPLOY])
316
        ui.Button(self.win, layout = (20, 26, 10, 1), id = 'vDeplShip',
317
            align = ui.ALIGN_NONE, tags = [Const.FLACTION_DEPLOY], action = 'onDeplShipChange')
318
        # Repeat
319
        ui.Label(self.win, layout = (0, 26, 10, 1), text = _('Start from command #'),
320
            align = ui.ALIGN_W, tags = [Const.FLACTION_REPEATFROM])
321
        ui.Entry(self.win, layout = (10, 26, 5, 1), id = 'vStartFrom', text = _('1'),
322
            align = ui.ALIGN_E, tags = [Const.FLACTION_REPEATFROM], orderNo = 2)
323
        ui.Label(self.win, layout = (36, 26, 2, 1), text = _("ETA:"), align = ui.ALIGN_W)
324
        ui.Label(self.win, layout = (38, 26, 2, 1), id = 'vEta', align = ui.ALIGN_E)
325
        # status bar + submit/cancel
326
        ui.TitleButton(self.win, layout = (35, 27, 5, 1), text = _('Order'), action = 'onOrder')
327
        ui.TitleButton(self.win, layout = (30, 27, 5, 1), text = _('Cancel'), action = 'onCancel')
328
        ui.Title(self.win, id = 'vStatusBar', layout = (0, 27, 30, 1), align = ui.ALIGN_W)
329
        #self.win.statusBar = self.win.vStatusBar
330