Issues (229)

client/osci/dialog/FleetSplitDlg.py (2 issues)

Severity
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 types
22
import pygameui as ui
23
from osci.StarMapWidget import StarMapWidget
24
from osci import gdata, res, client
25
import ige.ospace.Const as Const
26
from ige.ospace import Rules
27
import ige
28
import math
29
30
# grouping options
31
CLASS = 1
32
DAMAGE = 2
33
EXPERIENCE = 4
34
DESIGN = 8
35
LEVEL = 16
36
SPEED = 32
37
38
speedDesc = {
39
    0: _("0"),
40
    1: _("0 - 4"),
41
    2: _("4 - 5"),
42
    3: _("5 - 6"),
43
    4: _("6 - 7"),
44
    5: _("7 - 8"),
45
    6: _("> 8"),
46
}
47
48
dmgDesc = {
49
    0: _("0% - 33%"),
50
    1: _("34% - 66%"),
51
    2: _("67% - 99%"),
52
    3: _("100%"),
53
}
54
55
def getCountShipsByDesign(ships, maxCount, shipDesignID):
56
    count = 0
57
    retShips = []
58
    if maxCount < 1 and maxCount > 0: # calc percent to num ships
59
        numShips = 0
60
        for ship in ships:
61
            if ship[0] == shipDesignID:
62
                numShips += 1
63
        maxCount = int(math.floor(numShips * maxCount))
64
    if maxCount == 0:
65
        return retShips
66
    for ship in ships:
67
        if ship[0] == shipDesignID and (count < maxCount or maxCount == -1):
68
            retShips.append(ship)
69
            count += 1
70
    return retShips
71
72
def getCountShips(ships, maxCount):
73
    count = 0
74
    retShips = []
75
    if maxCount < 1 and maxCount > 0: # calc percent to num ships
76
        maxCount = int(math.floor(len(ships) * maxCount))
77
    if maxCount == 0:
78
        return retShips
79
    for ship in ships:
80
        if count < maxCount or maxCount == -1:
81
            retShips.append(ship)
82
            count += 1
83
    return retShips
84
85
def appendToDict(dict, key, object):
86
    if not dict.has_key(key):
87
        dict[key] = [object]
88
    else:
89
        dict[key].append(object)
90
91
def groupShipsByClass(ships):
92
    retShips = {}
93
    for ship in ships:
94
        appendToDict(retShips, getTech(ship).combatClass, ship)
95
96
    return retShips
97
98
def groupShipsByExpr(ships):
99
    retShips = {}
100
    for ship in ships:
101
        appendToDict(retShips, ship[3], ship)
102
103
    return retShips
104
105
def groupShipsByDmg(ships):
106
    retShips = {}
107
    for ship in ships:
108
        appendToDict(retShips, getDamageLevel(ship), ship)
109
110
    return retShips
111
112
def groupShipsByDesign(ships):
113
    player = client.getPlayer()
114
    retShips = {}
115
    for ship in ships:
116
        appendToDict(retShips, ship[0], ship)
117
118
    return retShips
119
120
def groupShipsByLevel(ships):
121
    retShips = {}
122
    for ship in ships:
123
        appendToDict(retShips, getExperienceLevel(ship), ship)
124
125
    return retShips
126
127
def groupShipsBySpeed(ships):
128
    retShips = {}
129
    for ship in ships:
130
        appendToDict(retShips, getSpeedLevel(ship), ship)
131
132
    return retShips
133
134
def getTech(ship):
135
    player = client.getPlayer()
136
    return player.shipDesigns[ship[0]]
137
138
def getExperienceLevel(ship):
139
    tech = getTech(ship)
140
    return Rules.shipExpToLevel.get(int(ship[3] / tech.baseExp), Rules.shipDefLevel)
141
142
def getDamageLevel(ship):
143
    tech = getTech(ship)
144
    dmg = (float(ship[1]) / tech.maxHP) * 100
145
    if dmg <= 33:
146
        return 0
147
    elif dmg > 33 and dmg <= 66:
148
        return 1
149
    elif dmg > 66 and dmg <= 99:
150
        return 2
151
    else: # 100%
152
        return 3
153
154
def getSpeedLevel(ship):
155
    speed = getTech(ship).speed
156
    if speed <= 0:
157
        return 0
158
    elif speed > 0 and speed < 4:
159
        return 1
160
    elif speed >= 4 and speed < 5:
161
        return 2
162
    elif speed >= 5 and speed < 6:
163
        return 3
164
    elif speed >= 6 and speed < 7:
165
        return 4
166
    elif speed >= 7 and speed < 8:
167
        return 5
168
    else: # >= 8
169
        return 6
170
171
class FleetSplitDlg:
172
173
    def __init__(self, app):
174
        self.app = app
175
        self.createUI()
176
177
    def display(self, fleetDlg):
178
        self.fleetDlg = fleetDlg
179
        fleet = client.get(self.fleetDlg.fleetID, noUpdate = 1)
180
        self.origShips = fleet.ships[:]
181
        self.storEn = self.origEn = fleet.storEn
182
        self.origEnMax = fleet.maxEn
183
        self.newShips = []
184
        self.newBio = 0
185
        self.newMin = 0
186
        self.newEn = 0
187
        self.newEnMax = 0
188
        self.checks = 0
189
        self.Nchecks = 0
190
191
        self.win.vDesign.checked = 0
192
        self.win.vClass.checked = 0
193
        self.win.vDmg.checked = 0
194
        self.win.vExpr.checked = 0
195
        self.win.vLevel.checked = 0
196
        self.win.vSpeed.checked = 0
197
        self.win.vNDesign.checked = 0
198
        self.win.vNClass.checked = 0
199
        self.win.vNDmg.checked = 0
200
        self.win.vNExpr.checked = 0
201
        self.win.vNLevel.checked = 0
202
        self.win.vNSpeed.checked = 0
203
204
        self.show()
205
        self.win.show()
206
        # register for updates
207
        if self not in gdata.updateDlgs:
208
            gdata.updateDlgs.append(self)
209
210
    def hide(self):
211
        self.win.setStatus(_("Ready."))
212
        self.win.hide()
213
        # unregister updates
214
        if self in gdata.updateDlgs:
215
            gdata.updateDlgs.remove(self)
216
217
    def update(self):
218
        self.hide()
219
220
    def groupShips(self, grouped, by):
221
        for key, item in grouped.items():
222
            if type(item) == types.ListType:
223
                if by & DESIGN:
224
                    grouped[key] = groupShipsByDesign(item)
225
                    self.groupShips(grouped[key], by - DESIGN)
226
                elif by & CLASS:
227
                    grouped[key] = groupShipsByClass(item)
228
                    self.groupShips(grouped[key], by - CLASS)
229
                elif by & DAMAGE:
230
                    grouped[key] = groupShipsByDmg(item)
231
                    self.groupShips(grouped[key], by - DAMAGE)
232
                elif by & LEVEL:
233
                    grouped[key] = groupShipsByLevel(item)
234
                    self.groupShips(grouped[key], by - LEVEL)
235
                elif by & EXPERIENCE:
236
                    grouped[key] = groupShipsByExpr(item)
237
                    self.groupShips(grouped[key], by - EXPERIENCE)
238
                elif by & SPEED:
239
                    grouped[key] = groupShipsBySpeed(item)
240
                    self.groupShips(grouped[key], by - SPEED)
241
242
    def appendShips(self, grouped, items, player, checks):
243
        for key, item in grouped.items():
244
            if checks == 0:
245
                for ship in item:
246
                    self.appendItem(ship, items, player, 0)
247
248
                return
249
250
            if type(item) == types.ListType:
251
                self.appendItem(item, items, player, checks)
252
            else:
253
                self.appendShips(item, items, player, checks)
254
255
    def show(self):
256
        # orig fleet
257
        items = []
258
        player = client.getPlayer()
259
260
        grouped = {None: self.origShips}
261
        self.groupShips(grouped, self.checks)
262
        self.appendShips(grouped, items, player, self.checks)
263
264
        self.win.vOShips.items = items
265
        self.win.vOShips.itemsChanged()
266
        self.win.vOShips.selectItem(None)
267
        self.win.vOEn.text = str(self.origEn)
268
        self.win.vOEnMax.text = _('/ %d') % self.origEnMax
269
270
        # new fleet
271
        items = []
272
273
        grouped = {None: self.newShips}
274
        self.groupShips(grouped, self.Nchecks)
275
        self.appendShips(grouped, items, player, self.Nchecks)
276
277
        self.win.vNShips.items = items
278
        self.win.vNShips.itemsChanged()
279
        self.win.vNShips.selectItem(None)
280
        self.win.vNEn.text = str(self.newEn)
281
        self.win.vNEnMax.text = _('/ %d') % self.newEnMax
282
283
    def appendItem(self, ships, items, player, checks):
284
        if type(ships[0]) == types.ListType:
285
            count = len(ships)
286
            designName = ""
287
            listData = ships
288
            exp = ""
289
            hpText = ""
290
            designID = None
291
            className = ""
292
            level = ""
293
            speed = ""
294
        else:
295
            count = ""
296
            designID, hp, shield, exp = ships
297
            tech = getTech(ships)
298
            designName = tech.name
299
            className = _(gdata.shipClasses[tech.combatClass])
300
            hpText = _("%d / %d") % (hp, shield)
301
            level = getExperienceLevel(ships)
302
            listData = [ships]
303
            speed = _("%.2f") % tech.speed
304
305
        if checks & DAMAGE:
306
            hpText = dmgDesc[getDamageLevel(listData[0])]
307
308
        if checks & CLASS:
309
            tech = getTech(listData[0])
310
            className = _(gdata.shipClasses[tech.combatClass])
311
312
        if checks & EXPERIENCE:
313
            exp = listData[0][3]
314
315
        if checks & LEVEL:
316
            level = getExperienceLevel(listData[0])
317
318
        if checks & SPEED:
319
            speed = speedDesc[getSpeedLevel(listData[0])]
320
321
        if checks & DESIGN:
322
            designName = getTech(listData[0]).name
323
            if not (checks & CLASS):
324
                tech = getTech(listData[0])
325
                className = _(gdata.shipClasses[tech.combatClass])
326
            if not (checks & SPEED):
327
                speed = _("%.2f") % getTech(listData[0]).speed
328
329
330
        item = ui.Item(designName,
331
            tHP = hpText,
332
            tExp = exp,
333
            tClass = className,
334
            designID = designID,
335
            tLevel = level,
336
            tShip = listData,
337
            tCount = count,
338
            tSpeed = speed,
339
        )
340
        items.append(item)
341
342
    def onSplit(self, widget, action, data):
343
        # TODO
344
        self.win.setStatus(_('Executing SPLIT FLEET command...'))
345
        try:
346
            fleet = client.get(self.fleetDlg.fleetID, noUpdate = 1)
347
            newFleet, origFleet, fleets = client.cmdProxy.splitFleet(self.fleetDlg.fleetID,
348
                self.newShips, self.newEn)
349
        except ige.GameException, e:
350
            self.win.setStatus(e.args[0])
351
            return
352
        # update related objects
353
        client.getPlayer().fleets = fleets
354
        client.db[newFleet.oid] = newFleet
355
        client.db[origFleet.oid] = origFleet
356
        # update client
357
        self.win.setStatus(_('Command has been executed.'))
358
        self.hide()
359
        self.fleetDlg.display(newFleet.oid)
360
        gdata.mainGameDlg.update()
361
362
    def updateRes(self):
363
        # orig max
364
        player = client.getPlayer()
365
        self.origEnMax = 0
366
        for designID, hp, shield, exp in self.origShips:
367
            tech = player.shipDesigns[designID]
368
            self.origEnMax += tech.storEn
369
        # new max
370
        self.newEnMax = 0
371
        for designID, hp, shield, exp in self.newShips:
372
            tech = player.shipDesigns[designID]
373
            self.newEnMax += tech.storEn
374
        # orig
375
        if self.origEn > self.origEnMax:
376
            self.newEn += self.origEn - self.origEnMax
377
            self.origEn = self.origEnMax
378
        # new
379
        if self.newEn > self.newEnMax:
380
            self.origEn += self.newEn - self.newEnMax
381
            self.newEn = self.newEnMax
382
383
    def onOShipSelected(self, widget, action, data):
384
        item = self.win.vOShips.selection[0]
385
        self.moveShipsToRight(item.tShip)
386
        #
387
        self.updateRes()
388
        self.show()
389
390
    def onNShipSelected(self, widget, action, data):
391
        item = self.win.vNShips.selection[0]
392
        self.moveShipsToLeft(item.tShip)
393
        #
394
        self.updateRes()
395
        self.show()
396
397
    def onStorChng(self, widget, action, data):
398
        try:
399
            self.origEn = int(self.win.vOEn.text)
400
            self.newEn = int(self.win.vNEn.text)
401
        except ValueError:
402
            self.win.setStatus(_('Enter a number.'))
403
            self.show()
404
            return
405
        # update changed
406
        newVal = getattr(self, widget.data)
407
        if widget.data[:4] == 'orig':
408
            mirror = 'new%s' % widget.data[4:]
409
            total = getattr(self, 'stor%s' % widget.data[4:])
410
        if widget.data[:3] == 'new':
411
            mirror = 'orig%s' % widget.data[3:]
412
            total = getattr(self, 'stor%s' % widget.data[3:])
413
        mirrVal = total - newVal
0 ignored issues
show
The variable total does not seem to be defined in case SubscriptNode == 'orig' on line 407 is False. Are you sure this can never be the case?
Loading history...
414
        if mirrVal < 0:
415
            newVal = total
416
            mirrVal = 0
417
        setattr(self, widget.data, newVal)
418
        setattr(self, mirror, mirrVal)
0 ignored issues
show
The variable mirror does not seem to be defined in case SubscriptNode == 'orig' on line 407 is False. Are you sure this can never be the case?
Loading history...
419
        self.updateRes()
420
        self.show()
421
422
    def onMoveMenu(self, widget, action, data):
423
        selItem = self.win.vOShips.selection[0]
424
        if hasattr(selItem, "designID"):
425
            self.moveRightPopup.show()
426
427
    def onNMoveMenu(self, widget, action, data):
428
        selItem = self.win.vNShips.selection[0]
429
        if hasattr(selItem, "designID"):
430
            self.moveLeftPopup.show()
431
432
    def onMoveAction(self, widget, action, data):
433
        selItem = self.win.vOShips.selection[0]
434
        if selItem.designID == None:
435
            self.moveShipsToRight(getCountShips(selItem.tShip, data))
436
        else:
437
            self.moveShipsToRight(getCountShipsByDesign(self.origShips, data, selItem.designID))
438
439
        self.updateRes()
440
        self.show()
441
442
    def onNMoveAction(self, widget, action, data):
443
        selItem = self.win.vNShips.selection[0]
444
        if selItem.designID == None:
445
            self.moveShipsToLeft(getCountShips(selItem.tShip, data))
446
        else:
447
            self.moveShipsToLeft(getCountShipsByDesign(self.newShips, data, selItem.designID))
448
449
        self.updateRes()
450
        self.show()
451
452
    def onClear(self, widget, action, data):
453
        self.moveShipsToLeft(self.newShips[:])
454
        self.updateRes()
455
        self.show()
456
457
    def moveShipsToRight(self, ships):
458
        for ship in ships:
459
            self.origShips.remove(ship)
460
            self.newShips.append(ship)
461
            # move fuel to the new fleet
462
            tech = client.getPlayer().shipDesigns[ship[0]]
463
            avail = min(self.origEn, tech.storEn)
464
            self.newEn += avail
465
            self.origEn -= avail
466
467
    def moveShipsToLeft(self, ships):
468
        for ship in ships:
469
            self.newShips.remove(ship)
470
            self.origShips.append(ship)
471
            # move fuel to the new fleet
472
            tech = client.getPlayer().shipDesigns[ship[0]]
473
            avail = min(self.origEn, tech.storEn)
474
            self.newEn -= avail
475
            self.origEn += avail
476
477
    def onGroup(self, widget, action, data):
478
        if widget.checked:
479
            self.checks += widget.data
480
        else:
481
            self.checks -= widget.data
482
483
        self.updateRes()
484
        self.show()
485
486
    def onNGroup(self, widget, action, data):
487
        if widget.checked:
488
            self.Nchecks += widget.data
489
        else:
490
            self.Nchecks -= widget.data
491
492
        self.updateRes()
493
        self.show()
494
495
    def onCancel(self, widget, action, data):
496
        self.hide()
497
498
    def createUI(self):
499
        w, h = gdata.scrnSize
500
        self.win = ui.Window(self.app,
501
            modal = 1,
502
            escKeyClose = 1,
503
            titleOnly = w == 800 and h == 600,
504
            movable = 0,
505
            title = _('Split Fleet'),
506
            rect = ui.Rect((w - 800 - 4 * (w != 800)) / 2, (h - 600 - 4 * (h != 600)) / 2, 800 + 4 * (w != 800), 580 + 4 * (h != 600)),
507
            layoutManager = ui.SimpleGridLM(),
508
            tabChange = True,
509
        )
510
        self.win.subscribeAction('*', self)
511
        # original fleet
512
        ui.Title(self.win, layout = (0, 0, 20, 1), text = _('Original fleet'),
513
            align = ui.ALIGN_W, font = 'normal-bold')
514
        ui.Listbox(self.win, id = 'vOShips', layout = (0, 1, 20, 21),
515
            columns = (
516
            (_('#'), 'tCount', 1.5, ui.ALIGN_NONE),
517
            (_('Name'), 'text', 5, ui.ALIGN_W),
518
            (_('Lvl'), 'tLevel', 1.5, ui.ALIGN_NONE),
519
            (_('Class'), 'tClass', 3, ui.ALIGN_E),
520
            (_('HP'), 'tHP', 4, ui.ALIGN_E),
521
            (_('Exp'), 'tExp', 2, ui.ALIGN_E),
522
            (_('Spd'), 'tSpeed', 2, ui.ALIGN_E),
523
            ),
524
            columnLabels = 1, action = 'onOShipSelected', rmbAction = 'onMoveMenu'
525
        )
526
        # grouping options
527
        ui.Title(self.win, layout = (0, 22, 20, 1), text = _('Group by'),
528
            align = ui.ALIGN_W, font = 'normal-bold')
529
        ui.Check(self.win, layout = (0, 23, 5, 1), id = "vDesign", text = _("design"),
530
            action = "onGroup", data = DESIGN)
531
        ui.Check(self.win, layout = (5, 23, 5, 1), id = "vClass", text = _("class"),
532
            action = "onGroup", data = CLASS)
533
        ui.Check(self.win, layout = (10, 23, 5, 1), id = "vDmg", text = _("damage"),
534
            action = "onGroup", data = DAMAGE)
535
        ui.Check(self.win, layout = (15, 23, 5, 1), id = "vExpr", text = _("experience"),
536
            action = "onGroup", data = EXPERIENCE)
537
        ui.Check(self.win, layout = (0, 24, 5, 1), id = "vLevel", text = _("level"),
538
            action = "onGroup", data = LEVEL)
539
        ui.Check(self.win, layout = (5, 24, 5, 1), id = "vSpeed", text = _("speed"),
540
            action = "onGroup", data = SPEED)
541
        # tanks
542
        ui.Title(self.win, layout = (0, 25, 20, 1), text = _('Tanks'),
543
            align = ui.ALIGN_W, font = 'normal-bold')
544
        ui.Label(self.win, layout = (0, 26, 5, 1), text = _('Fuel'), align = ui.ALIGN_W)
545
        ui.Entry(self.win, layout = (5, 26, 5, 1), id = 'vOEn', action = 'onStorChng',
546
            align = ui.ALIGN_E, data ='origEn', orderNo = 15)
547
        ui.Label(self.win, layout = (10, 26, 5, 1), id = 'vOEnMax', align = ui.ALIGN_W)
548
549
        # new fleet
550
        ui.Title(self.win, layout = (20, 0, 20, 1), text = _('New fleet'),
551
            align = ui.ALIGN_W, font = 'normal-bold')
552
        ui.Listbox(self.win, id = 'vNShips', layout = (20, 1, 20, 21),
553
            columns = (
554
            (_('#'), 'tCount', 1.5, ui.ALIGN_NONE),
555
            (_('Name'), 'text', 5, ui.ALIGN_W),
556
            (_('Lvl'), 'tLevel', 1.5, ui.ALIGN_NONE),
557
            (_('Class'), 'tClass', 3, ui.ALIGN_E),
558
            (_('HP'), 'tHP', 4, ui.ALIGN_E),
559
            (_('Exp'), 'tExp', 2, ui.ALIGN_E),
560
            (_('Spd'), 'tSpeed', 2, ui.ALIGN_E),
561
            ),
562
            columnLabels = 1, action = 'onNShipSelected', rmbAction = 'onNMoveMenu')
563
        # grouping
564
        ui.Title(self.win, layout = (20, 22, 16, 1), text = _('Group by'),
565
            align = ui.ALIGN_W, font = 'normal-bold')
566
        ui.Button(self.win, layout = (36, 22, 4, 1), text = _("Clear"), action = 'onClear')
567
        ui.Check(self.win, layout = (20, 23, 5, 1), id = "vNDesign", text = _("design"),
568
            action = "onNGroup", data = DESIGN)
569
        ui.Check(self.win, layout = (25, 23, 5, 1), id = "vNClass", text = _("class"),
570
            action = "onNGroup", data = CLASS)
571
        ui.Check(self.win, layout = (30, 23, 5, 1), id = "vNDmg", text = _("damage"),
572
            action = "onNGroup", data = DAMAGE)
573
        ui.Check(self.win, layout = (35, 23, 5, 1), id = "vNExpr", text = _("experience"),
574
            action = "onNGroup", data = EXPERIENCE)
575
        ui.Check(self.win, layout = (20, 24, 5, 1), id = "vNLevel", text = _("level"),
576
            action = "onNGroup", data = LEVEL)
577
        ui.Check(self.win, layout = (25, 24, 5, 1), id = "vNSpeed", text = _("speed"),
578
            action = "onNGroup", data = SPEED)
579
        # tanks
580
        ui.Title(self.win, layout = (20, 25, 20, 1), text = _('Tanks'),
581
            align = ui.ALIGN_W, font = 'normal-bold')
582
        ui.Label(self.win, layout = (20, 26, 5, 1), text = _('Fuel'), align = ui.ALIGN_W)
583
        ui.Entry(self.win, layout = (25, 26, 5, 1), id = 'vNEn', action = 'onStorChng',
584
            align = ui.ALIGN_E, data ='newEn', orderNo = 16)
585
        ui.Label(self.win, layout = (30, 26, 5, 1), id = 'vNEnMax', align = ui.ALIGN_W)
586
587
        # status bar + submit/cancel
588
        ui.Title(self.win, id = 'vStatusBar', layout = (0, 27, 30, 1), align = ui.ALIGN_W)
589
        ui.TitleButton(self.win, layout = (35, 27, 5, 1), text = _('Split'), action = 'onSplit')
590
        ui.TitleButton(self.win, layout = (30, 27, 5, 1), text = _('Cancel'), action = 'onCancel')
591
        self.moveRightPopup = ui.Menu(self.app, title = _("Move right"),
592
            width = 10,
593
            columns = 2,
594
            items = [
595
                ui.Item(_("1 ship"), action = "onMoveAction", data = 1),
596
                ui.Item(_("5 ships"), action = "onMoveAction", data = 5),
597
                ui.Item(_("10 ships"), action = "onMoveAction", data = 10),
598
                ui.Item(_("50 ships"), action = "onMoveAction", data = 50),
599
                ui.Item(_("100 ships"), action = "onMoveAction", data = 100),
600
                ui.Item(_("1/2 of ships"), action = "onMoveAction", data = 0.5),
601
                ui.Item(_("1/3 of ships"), action = "onMoveAction", data = 0.34),
602
                ui.Item(_("1/4 of ships"), action = "onMoveAction", data = 0.25),
603
                ui.Item(_("All ships"), action = "onMoveAction", data = -1),
604
            ]
605
        )
606
        self.moveRightPopup.subscribeAction("*", self)
607
608
        self.moveLeftPopup = ui.Menu(self.app, title = _("Move left"),
609
            width = 10,
610
            columns = 2,
611
                items = [
612
                ui.Item(_("1 ship"), action = "onNMoveAction", data = 1),
613
                ui.Item(_("5 ships"), action = "onNMoveAction", data = 5),
614
                ui.Item(_("10 ships"), action = "onNMoveAction", data = 10),
615
                ui.Item(_("50 ships"), action = "onNMoveAction", data = 50),
616
                ui.Item(_("100 ships"), action = "onNMoveAction", data = 100),
617
                ui.Item(_("1/2 of ships"), action = "onNMoveAction", data = 0.5),
618
                ui.Item(_("1/3 of ships"), action = "onNMoveAction", data = 0.34),
619
                ui.Item(_("1/4 of ships"), action = "onNMoveAction", data = 0.25),
620
                ui.Item(_("All ships"), action = "onNMoveAction", data = -1),
621
            ]
622
        )
623
        self.moveLeftPopup.subscribeAction("*", self)
624