Completed
Push — master ( 72ed44...56c97c )
by Marek
16s queued 13s
created

GameMngr.getStartingPositions()   D

Complexity

Conditions 12

Size

Total Lines 34
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 29
nop 2
dl 0
loc 34
rs 4.8
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like ige.ospace.GameMngr.GameMngr.getStartingPositions() 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 json
22
import random, os, time, copy
23
24
import ige
25
from ige import log
26
27
from ige.ClientMngr import Session
28
from ige.GameMngr import GameMngr as IGEGameMngr
29
from ige.Index import Index
30
from ige.Transaction import Transaction
31
from ige.IDataHolder import IDataHolder
32
from ige import GameException, SecurityException, CreatePlayerException
33
34
import Const
35
import IPlayer, IUniverse, IGalaxy, ISystem, IWormHole, IPlanet, IFleet, IAlliance, IAsteroid
36
import INature, IAIPlayer, IAIRenegadePlayer, IAIMutantPlayer, IAIPiratePlayer
37
import GalaxyGenerator
38
import IAIEDENPlayer, IPiratePlayer
39
import Rules, Utils
40
41
from Rules import Tech
42
43
class GameMngr(IGEGameMngr):
44
45
    #
46
    # Required methods
47
    #
48
49
    def __init__(self, gameID, config, clientMngr, msgMngr, database, configDir, gameName = None):
50
        IGEGameMngr.__init__(self, gameID, config, clientMngr, msgMngr, database, configDir, gameName)
51
        # register command object
52
        self.registerObject(IUniverse.IUniverse)
53
        self.registerObject(IPlayer.IPlayer)
54
        self.registerObject(IGalaxy.IGalaxy)
55
        self.registerObject(ISystem.ISystem)
56
        self.registerObject(IWormHole.IWormHole)
57
        self.registerObject(IPlanet.IPlanet)
58
        self.registerObject(IFleet.IFleet)
59
        self.registerObject(IAlliance.IAlliance)
60
        self.registerObject(IAsteroid.IAsteroid)
61
        self.registerObject(INature.INature)
62
        self.registerObject(IAIPlayer.IAIPlayer)
63
        self.registerObject(IAIRenegadePlayer.IAIRenegadePlayer)
64
        self.registerObject(IAIMutantPlayer.IAIMutantPlayer)
65
        self.registerObject(IAIPiratePlayer.IAIPiratePlayer)
66
        self.registerObject(IAIEDENPlayer.IAIEDENPlayer)
67
        self.registerObject(IPiratePlayer.IPiratePlayer)
68
69
    def init(self):
70
        IGEGameMngr.init(self)
71
72
    def start(self):
73
        IGEGameMngr.start(self)
74
75
    def stop(self, checkpoint = 1):
76
        IGEGameMngr.stop(self, checkpoint)
77
78
    def shutdown(self):
79
        IGEGameMngr.shutdown(self)
80
81
    def reset(self):
82
        # remove all AI accounts and their records in AI list
83
        self.clientMngr.resetAIAccounts()
84
        IGEGameMngr.reset(self)
85
        # save informations
86
        self.db.checkpoint()
87
88
    def upgrade(self):
89
        IGEGameMngr.upgrade(self)
90
91
    def createAdmin(self):
92
        obj = IPlayer.IPlayer(self).new(Const.T_PLAYER)
93
        obj.name = 'GameMaster'
94
        return obj
95
96
    def accountGalaxies(self, login):
97
        """ Returns set of galaxies account is already in. Empty set otherwise """
98
        galaxyIDs = set()
99
        try:
100
            for playerID in self.db[Const.OID_I_LOGIN2OID][login]:
101
                galaxyIDs.add(self.db[playerID].galaxy)
102
        except KeyError:
103
            # fresh account
104
            pass
105
        return galaxyIDs
106
107
    def registerPlayer(self, login, playerObj, oid = None):
108
        log.debug("Registering login {0}".format(login))
109
110
        # action
111
        if not oid:
112
            log.debug("Creating object")
113
            oid = self.db.create(playerObj)
114
        else:
115
            self.db.create(playerObj, id = oid)
116
        log.debug("Fixing indexes")
117
        try:
118
            self.db[Const.OID_I_LOGIN2OID][login].append(oid)
119
        except KeyError:
120
            self.db[Const.OID_I_LOGIN2OID][login] = [oid]
121
        playerObj.oid = oid
122
        playerObj.owner = oid
123
        return oid
124
125
    def createUniverse(self):
126
        universe = self.cmdPool[Const.T_UNIVERSE].new(Const.T_UNIVERSE)
127
        self.db.create(universe, Const.OID_UNIVERSE)
128
        tran = Transaction(self, Const.OID_ADMIN)
129
        # create 'NATURE' player
130
        player = self.cmdPool[Const.T_NATURE].new(Const.T_NATURE)
131
        self.registerPlayer(player.login, player, Const.OID_NATURE)
132
133
    def getTurnData(self, sid):
134
        IGEGameMngr.getTurnData(self, sid)
135
        universe = self.db[Const.OID_UNIVERSE]
136
        universe.turn += 1
137
        return (
138
            self.db[Const.OID_UNIVERSE].turn,
139
            (
140
                ((Const.OID_UNIVERSE,), ('INIT',)),
141
                (universe.galaxies, ('INIT', 'PROD', 'ACTION', 'BATTLE', 'SCAN2', 'FINAL')),
142
                ((Const.OID_UNIVERSE,), ('FINAL', 'FINAL2')),
143
            ),
144
            None
145
        ), None
146
147
    def turnFinished(self, sid):
148
        IGEGameMngr.turnFinished(self, sid)
149
        self.generateStats()
150
        self.generateGameInfo()
151
        return 1, None
152
153
    def getActivePositions(self, sid):
154
        session = self.clientMngr.getSession(sid)
155
        result = []
156
        for playerID in self.db[Const.OID_I_LOGIN2OID].get(session.login, []):
157
            player = self.db[playerID]
158
            galaxy = self.db[player.galaxy]
159
            result.append((playerID, galaxy.name, player.type))
160
        return result, None
161
162
    def getStartingPositions(self, sid):
163
        session = self.clientMngr.getSession(sid)
164
        universe = self.db[Const.OID_UNIVERSE]
165
        badGalaxies = self.accountGalaxies(session.login)
166
        result = []
167
168
        for galaxyID in set(universe.galaxies).difference(badGalaxies):
169
            galaxy = self.db[galaxyID]
170
            if galaxy.scenario == Const.SCENARIO_SINGLE:
171
                # single player scenarios are off limit :)
172
                continue
173
            if galaxy.startingPos:
174
                result.append((galaxyID, galaxy.name, Const.PLAYER_SELECT_NEWPLAYER))
175
        for playerID in universe.players:
176
            player = self.db[playerID]
177
            if player.galaxy in badGalaxies:
178
                continue
179
            try:
180
                system = self.db[self.db[player.planets[0]].compOf]
181
            except IndexError:
182
                # no planets, definitely not a good starting position
183
                continue
184
            galaxy = self.db[system.compOf]
185
            if galaxy.scenario == Const.SCENARIO_SINGLE:
186
                # single player scenarios are off limit :)
187
                continue
188
            if player.type == Const.T_AIPLAYER and player.planets:
189
                # check if home system is under attack
190
                if system.combatCounter > 0:
191
                    continue
192
                result.append((playerID, galaxy.name, Const.PLAYER_SELECT_AIPLAYER))
193
            if player.type == Const.T_AIPIRPLAYER:
194
                result.append((playerID, galaxy.name, Const.PLAYER_SELECT_PIRATE))
195
        return result, None
196
197
    def singleGamesLimit(self, sid):
198
        session = self.clientMngr.getSession(sid)
199
        noOfSingles = 0
200
        for galaxyID in self.accountGalaxies(session.login):
201
            galaxy = self.db[galaxyID]
202
            if galaxy.scenario == Const.SCENARIO_SINGLE:
203
                noOfSingles += 1
204
        if noOfSingles >= Const.ACCOUNT_SCENARIO_LIMITS[Const.SCENARIO_SINGLE]:
205
            # limit of single galaxies allowed to the account reached
206
            return True
207
        return False
208
209
    def takeOverAIPlayer(self, sid, playerID):
210
        log.debug('Creating new player in session', sid)
211
        session = self.clientMngr.getSession(sid)
212
        log.debug('Creating new player with CID', session.cid)
213
        universe = self.db[Const.OID_UNIVERSE]
214
        log.debug('Creating transaction')
215
        tran = Transaction(self, session.cid, session)
216
        player = self.db[playerID]
217
        if not (player.type == Const.T_AIPLAYER and player.planets):
218
            raise GameException('No such starting position.')
219
        if player.galaxy in self.accountGalaxies(session.login):
220
            raise GameException('Account already owns player in this galaxy.')
221
        galaxy = self.db[player.galaxy]
222
        if galaxy.scenario == Const.SCENARIO_SINGLE:
223
            raise GameException('AI in single scenario cannot be taken over.')
224
225
226
        # create player
227
        log.debug("Morphing AI player", playerID)
228
        player.type = Const.T_PLAYER
229
        self.cmdPool[Const.T_PLAYER].upgrade(tran, player)
230
        self.cmdPool[Const.T_PLAYER].update(tran, player)
231
        # reregister player
232
        self.removePlayer(player.oid)
233
        player.name = session.nick
234
        player.login = session.login
235
        self.registerPlayer(player.login, player, player.oid)
236
        # reset relations
237
        player.diplomacyRels.clear()
238
        # add player to the universe
239
        universe.players.append(playerID)
240
        log.debug('Processing scan phase')
241
        galaxy = tran.db[player.galaxy]
242
        self.cmdPool[Const.T_GALAXY].processSCAN2Phase(tran, galaxy, True)
243
        # save game info
244
        self.generateGameInfo()
245
        return player.oid, None
246
247
    def takeOverPirate(self, sid, playerID, vipPassword):
248
        # limit this now only to the qark
249
        session = self.clientMngr.getSession(sid)
250
        player = self.db[playerID]
251
        if vipPassword != self.config.vip.password:
252
            raise SecurityException('Wrong VIP password.')
253
        if player.galaxy in self.accountGalaxies(session.login):
254
            raise GameException('Account already owns player in this galaxy.')
255
        if player.galaxy:
256
            galaxy = self.db[player.galaxy]
257
            if galaxy.scenario == Const.SCENARIO_SINGLE:
258
                raise GameException('AI in single scenario cannot be taken over.')
259
260
        log.debug('Creating pirate in session {0} with CID {1}'.format(sid, session.cid))
261
        universe = self.db[Const.OID_UNIVERSE]
262
        log.debug('Creating transaction')
263
        tran = Transaction(self, session.cid, session)
264
        # create player
265
        #log.debug("Morphing Pirate player", playerID)
266
        log.debug("Player type", player.type)
267
        if player.type != Const.T_AIPIRPLAYER:
268
            raise GameException('No such starting position.')
269
        player.type = Const.T_PIRPLAYER
270
        self.cmdPool[Const.T_PIRPLAYER].upgrade(tran, player)
271
        self.cmdPool[Const.T_PIRPLAYER].update(tran, player)
272
        # reregister player
273
        self.removePlayer(player.oid)
274
        player.fullName = "Pirate %s" % session.nick
275
        player.name = session.nick
276
        player.login = session.login
277
        self.registerPlayer(player.login, player, player.oid)
278
        # add player to the universe
279
        universe.players.append(playerID)
280
        # initial scan
281
        scannerPwr = Rules.techs[9002].scannerPwr
282
        for planetID in player.planets:
283
            planet = self.db[planetID]
284
            system = self.db[planet.compOf]
285
            system.scannerPwrs[player.oid] = scannerPwr
286
        log.debug('Processing scan phase')
287
        galaxy = tran.db[player.galaxy]
288
        self.cmdPool[Const.T_GALAXY].processSCAN2Phase(tran, galaxy, True)
289
        # save game info
290
        self.generateGameInfo()
291
        return player.oid, None
292
293
    def _createNewPlayer(self, session, galaxyID):
294
        universe = self.db[Const.OID_UNIVERSE]
295
        galaxy = self.db[galaxyID]
296
        if not galaxy.startingPos:
297
            raise GameException('No such starting position.')
298
        if galaxyID in self.accountGalaxies(session.login):
299
            raise GameException('Account already owns player in this galaxy.')
300
301
        log.debug('Creating new player with CID', session.cid)
302
        player = self.cmdPool[Const.T_PLAYER].new(Const.T_PLAYER)
303
        player.name = session.nick
304
        player.login = session.login
305
        player.timeEnabled = galaxy.timeEnabled
306
        player.galaxy = galaxy.oid
307
        log.debug('Selecting starting point')
308
        planetID = IGalaxy.IGalaxy.getFreeStartingPosition(self.db, galaxy)
309
310
        player.planets.append(planetID)
311
        log.debug('Creating transaction')
312
        tran = Transaction(self, session.cid, session)
313
        IPlayer.IPlayer.setStartingTechnologies(player)
314
        # register player
315
        log.debug('Registering player for login {0}'.format(session.login))
316
        playerID = self.registerPlayer(session.login, player)
317
        log.debug('Player ID =', playerID)
318
        # singleplayer galaxy needs owner recorded so player can log back there
319
        # also provides access rights to control it
320
        if galaxy.scenario == Const.SCENARIO_SINGLE:
321
            galaxy.owner = playerID
322
        planet = self.db[planetID]
323
        planet.owner = playerID
324
        system = tran.db[planet.compOf]
325
        IPlayer.IPlayer.setStartingShipDesigns(player)
326
        IPlayer.IPlayer.setStartingPlanet(tran, playerID, planet)
327
        IPlayer.IPlayer.setStartingFleet(tran, playerID, system)
328
        # add player to universe
329
        log.debug('Adding player to universe')
330
        universe.players.append(playerID)
331
        # initial scan
332
        system = self.db[planet.compOf]
333
        log.debug('Processing scan phase')
334
        self.cmdPool[Const.T_PLANET].processPRODPhase(tran, planet, None)
335
        # this only works for one planet starting scenarios, it might be imprecise, as it's
336
        # calculated here
337
        # TODO: make proper difference between getting stats, and acting on them, and utilize that
338
        player.effSciPoints = planet.prodSci * (1 + ((Rules.baseGovPwr - planet.storPop)  / float(Rules.baseGovPwr) ) / 2.0)
339
        system.scannerPwrs[playerID] = planet.scannerPwr = Rules.startingScannerPwr
340
        self.cmdPool[Const.T_GALAXY].processSCAN2Phase(tran, galaxy, True)
341
        # check if galaxy can be "started" (for purpose of single player games)
342
        self.cmdPool[Const.T_GALAXY].enableTime(tran, galaxy)
343
        # save game info
344
        self.generateGameInfo()
345
        return playerID, None
346
347
    def createNewPlayer(self, sid, galaxyID):
348
        log.debug('Creating new player in session', sid)
349
        session = self.clientMngr.getSession(sid)
350
        return self._createNewPlayer(session, galaxyID)
351
352
    def createNewSubscribedPlayer(self, login, galaxyID):
353
        log.debug('Creating new subscribed player using fake session')
354
        player = self.clientMngr[login]
355
        session = Session(None)
356
        session.setAttrs(login, player.nick, player.email)
357
        return self._createNewPlayer(session, galaxyID)
358
359
    def removePlayer(self, playerID):
360
        log.debug('removePlayer', playerID)
361
        player = self.db[playerID]
362
        # unregister player
363
        self.unregisterPlayer(player)
364
        # remove player from universe
365
        universe = self.db[Const.OID_UNIVERSE]
366
        try:
367
            universe.players.remove(playerID)
368
        except ValueError:
369
            log.warning("Cannot remove player", playerID)
370
371
    def validateClient(self, session):
372
        # TODO better validation
373
        return 1
374
375
376
    #
377
    # Game related methods
378
    #
379
380
    def generateGameInfo(self):
381
        """Generate game related info."""
382
        # make directory
383
        try:
384
            os.makedirs(os.path.join(self.configDir, 'website/%s' % self.gameID))
385
        except OSError:
386
            pass
387
        # create structure to save
388
        stats = dict()
389
        universe = self.db[Const.OID_UNIVERSE]
390
        stats["players"] = len(universe.players)
391
        stats["turn"] = "%d:%02d" % (universe.turn / Rules.turnsPerDay, universe.turn % Rules.turnsPerDay)
392
        galaxies = list()
393
        stats["galaxies"] = galaxies
394
        for galaxyID in universe.galaxies:
395
            galaxy = self.db[galaxyID]
396
            galaxyStats = dict(
397
                name = galaxy.name,
398
                url = "http://www.ospace.net:9080/%s/galaxy%d.html" % (self.gameID, galaxyID),
399
                freePositions = len(galaxy.startingPos),
400
                players = 0,
401
                rebels = 0,
402
                age = int((galaxy.galaxyTurn - galaxy.creationTurn) / Rules.turnsPerDay ),
403
                running = galaxy.timeEnabled,
404
            )
405
            for playerID in universe.players:
406
                player = self.db[playerID]
407
                if galaxy.oid != player.galaxy:
408
                    continue
409
                if player.type == Const.T_PLAYER:
410
                    galaxyStats["players"] += 1
411
                elif player.type == Const.T_AIPLAYER:
412
                    galaxyStats["rebels"] += 1
413
            galaxies.append(galaxyStats)
414
        json.dump(stats, open(os.path.join(self.configDir, "website/%s/info.json" % self.gameID), "w"))
415
416
    def generateStats(self):
417
        """ Generate games statistics """
418
        # gather stats
419
        try:
420
            os.makedirs(os.path.join(self.configDir, 'website/%s' % self.gameID))
421
        except OSError:
422
            pass
423
        stats = {}
424
        galaxies = {}
425
        resolutions = {}
426
        universe = self.db[Const.OID_UNIVERSE]
427
        jsonComma = False
428
        fhjson = open(os.path.join(self.configDir, 'website/%s/json.txt' % (self.gameID)), 'w')
429
        print >>fhjson, '{"turn":"%s",' % universe.turn
430
        for playerID in universe.players:
431
            player = self.db[playerID]
432
            stats[playerID] = player.stats
433
            galaxies[playerID] = player.galaxy
434
            resolution = self.cmdPool[player.type].getResolution(player)
435
            if resolutions.has_key(resolution):
436
                resolutions[resolution] += 1
437
            else:
438
                resolutions[resolution] = 1
439
        for galaxyID in universe.galaxies:
440
            gStats = copy.deepcopy(stats)
441
            for playerID in gStats.keys():
442
                # huh, someone should have commented this
443
                if galaxyID != galaxies[playerID]:
444
                    del gStats[playerID]
445
                    continue
446
                try:
447
                    gStats[playerID].storPop
448
                except AttributeError:
449
                    # time has not been enabled yet
450
                    del gStats[playerID]
451
452
            if 0:
453
                # average
454
                storPop = 0
455
                planets = 0
456
                structs = 0
457
                prodProd = 0
458
                prodSci = 0
459
                for playerID in gStats:
460
                    pStats = gStats[playerID]
461
                    storPop += pStats.storPop
462
                    planets += pStats.planets
463
                    structs += pStats.structs
464
                    prodProd += pStats.prodProd
465
                    prodSci += pStats.prodSci
466
                if prodProd == 0: prodProd = 1000
467
                if prodSci == 0: prodSci = 1000
468
                for playerID in gStats:
469
                    pStats = gStats[playerID]
470
                    pStats.storPop = int(pStats.storPop * 1000 / storPop)
471
                    pStats.planets = int(pStats.planets * 1000 / planets)
472
                    pStats.structs = int(pStats.structs * 1000 / structs)
473
                    pStats.prodProd = int(pStats.prodProd * 1000 / prodProd)
474
                    pStats.prodSci = int(pStats.prodSci * 1000 / prodSci)
475
            # generate tables
476
            fh = open(os.path.join(self.configDir, 'website/%s/galaxy%d.html' % (self.gameID, galaxyID)), 'w')
477
            galaxy = self.db[galaxyID]
478
            if galaxy.imperator != Const.OID_NONE:
479
                if self.db[galaxy.imperator].imperator > 1:
480
                    imperator = " - Imperator %s" % self.db[galaxy.imperator].name
481
                    imperatoroid = self.db[galaxy.imperator].oid
482
                    leaderoid = 0
483
                else:
484
                    imperator = " - Leader %s" % self.db[galaxy.imperator].name
485
                    imperatoroid = 0
486
                    leaderoid = self.db[galaxy.imperator].oid
487
            else:
488
                imperator = ""
489
                imperatoroid = 0
490
                leaderoid = 0
491
            print >>fh, statsHeader % (self.gameID, galaxy.name, imperator)
492
            order = self.sortStatsBy(gStats, 'storPop')
493
            self.printJSONStatsTable(fhjson, gStats, order, galaxyID, galaxy.name, imperatoroid, leaderoid, jsonComma)
494
            jsonComma = True
495
            self.printStatsEcoTable(fh, 'Sorted by population', gStats, order)
496
            #order = self.sortStatsBy(gStats, 'systems')
497
            #self.printStatsEcoTable(fh, 'Sorted by number of systems', gStats, order)
498
            order = self.sortStatsBy(gStats, 'planets')
499
            self.printStatsEcoTable(fh, 'Sorted by number of planets', gStats, order)
500
            order = self.sortStatsBy(gStats, 'structs')
501
            self.printStatsEcoTable(fh, 'Sorted by number of structures', gStats, order)
502
            order = self.sortStatsBy(gStats, 'prodProd')
503
            self.printStatsEcoTable(fh, 'Sorted by production', gStats, order)
504
            order = self.sortStatsBy(gStats, 'prodSci')
505
            self.printStatsEcoTable(fh, 'Sorted by science', gStats, order)
506
            order = self.sortStatsBy(gStats, 'fleetPwr')
507
            self.printStatsEcoTable(fh, 'Sorted by military power', gStats, order)
508
            print >>fh, statsFooter
509
            fh.close()
510
        print >>fhjson, '}'
511
        fhjson.close()
512
        #write resolutions of clients in use for statistics tracking
513
        fhres = open(os.path.join(self.configDir, 'website/res.txt'), 'w')
514
        print >>fhres, 'Resoltion: Number of users'
515
        reskeys = resolutions.keys();
516
        reskeys.sort();
517
        for resolution in reskeys:
518
            print >>fhres, '%s: %s' % (resolution, resolutions[resolution])
519
        fhres.close()
520
521
    def sortStatsBy(self, stats, attr):
522
        keyF = lambda a: getattr(stats[a], attr)
523
        order = sorted(stats.keys(), key=keyF, reverse = True)
524
        return order
525
526
    def printJSONStatsTable(self, fh, stats, order, galaxyID, galaxyName, imperatoroid, leaderoid, jsonComma):
527
        if jsonComma:
528
            print >> fh, ','
529
        print >> fh, '"%s":{"galaxyname":"%s","imperatorid":"%s","leaderid":"%s","players":' % (galaxyID, galaxyName, imperatoroid, leaderoid)
530
        print >> fh, '{'
531
        print >> fh, '"order":["name","pop","planets","structs","prod","sci","mp"]'
532
        for playerID in order:
533
            print >> fh, ','
534
            needComma = True
535
            stat = stats[playerID]
536
            print >> fh, '"%s":["%s","%s","%s","%s","%s","%s","%s"]' % (playerID, self.db[playerID].name, stat.storPop, stat.planets, stat.structs, stat.prodProd, stat.prodSci, stat.fleetPwr)
537
        print >> fh, '}}'
538
539
    def printStatsEcoTable(self, fh, title, stats, order):
540
        print >> fh, '<table cellspacing="1" border="0" cellpadding="2" width="100%">'
541
        print >> fh, '<tr>'
542
        print >> fh, '<td class="title" align="center" colspan="8">%s</td>' % title
543
        print >> fh, '</tr>'
544
        print >> fh, '<tr>'
545
        print >> fh, '<td class="title" align="right">#</td>'
546
        print >> fh, '<td class="title" align="left">Player</td>'
547
        print >> fh, '<td class="title" align="right">Population</td>'
548
        print >> fh, '<td class="title" align="right">Planets</td>'
549
        print >> fh, '<td class="title" align="right">Structures</td>'
550
        print >> fh, '<td class="title" align="right">Production</td>'
551
        print >> fh, '<td class="title" align="right">Military pwr</td>'
552
        print >> fh, '<td class="title" align="right">Science</td>'
553
        print >> fh, '</tr>'
554
        print >> fh, '<tr>'
555
        # index
556
        index = 1
557
        print >> fh, '<td align="right" nowrap>'
558
        for playerID in order:
559
            stat = stats[playerID]
560
            if index % 2: print >> fh, '%d.<br>' % index
561
            else:  print >> fh, '<font color="#c0c0c0">%d</font>.<br>' % index
562
            index += 1
563
        print >> fh, '</td>'
564
        # name
565
        index = 1
566
        print >> fh, '<td align="left" nowrap>'
567
        for playerID in order:
568
            stat = stats[playerID]
569
            if index % 2: print >> fh, '%s<br>' % self.db[playerID].name
570
            else: print >> fh, '<font color="#c0c0c0">%s</font><br>' % self.db[playerID].name
571
            index += 1
572
        print >> fh, '</td>'
573
        # storPop
574
        index = 1
575
        print >> fh, '<td align="right" nowrap>'
576
        for playerID in order:
577
            stat = stats[playerID]
578
            if index % 2: print >> fh, '%d<br>' % stat.storPop
579
            else: print >> fh, '<font color="#c0c0c0">%d</font><br>' % stat.storPop
580
            index += 1
581
        print >> fh, '</td>'
582
        # planets
583
        index = 1
584
        print >> fh, '<td align="right" nowrap>'
585
        for playerID in order:
586
            stat = stats[playerID]
587
            if index % 2: print >> fh, '%d<br>' % stat.planets
588
            else: print >> fh, '<font color="#c0c0c0">%d</font><br>' % stat.planets
589
            index += 1
590
        print >> fh, '</td>'
591
        # structs
592
        index = 1
593
        print >> fh, '<td align="right" nowrap>'
594
        for playerID in order:
595
            stat = stats[playerID]
596
            if index % 2: print >> fh, '%d<br>' % stat.structs
597
            else: print >> fh, '<font color="#c0c0c0">%d</font><br>' % stat.structs
598
            index += 1
599
        print >> fh, '</td>'
600
        # prodProd
601
        index = 1
602
        print >> fh, '<td align="right" nowrap>'
603
        for playerID in order:
604
            stat = stats[playerID]
605
            if index % 2: print >> fh, '%d<br>' % stat.prodProd
606
            else: print >> fh, '<font color="#c0c0c0">%d</font><br>' % stat.prodProd
607
            index += 1
608
        print >> fh, '</td>'
609
        # fleet
610
        index = 1
611
        print >> fh, '<td align="right" nowrap>'
612
        for playerID in order:
613
            stat = stats[playerID]
614
            if index % 2: print >> fh, '%d<br>' % stat.fleetPwr
615
            else: print >> fh, '<font color="#c0c0c0">%d</font><br>' % stat.fleetPwr
616
            index += 1
617
        # prodSci
618
        index = 1
619
        print >> fh, '<td align="right" nowrap>'
620
        for playerID in order:
621
            stat = stats[playerID]
622
            if index % 2: print >> fh, '%d<br>' % stat.prodSci
623
            else: print >> fh, '<font color="#c0c0c0">%d</font><br>' % stat.prodSci
624
            index += 1
625
        print >> fh, '</td>'
626
        print >> fh, '</tr>'
627
        print >> fh, '</table><br>'
628
629
statsHeader = '''\
630
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
631
<html>
632
<head>
633
    <title>Outer Space Statistics for Game %s</title>
634
    <link rel="STYLESHEET" href="../styles.css" type="text/css">
635
</head>
636
<body>
637
638
<center>
639
640
<h1>Statistics for galaxy %s%s</h1>
641
642
<table cellspacing=2 border=0 cellpadding=5 width="80%%" class="main">
643
<tr>
644
    <td valign="top">
645
<!-- body start -->
646
'''
647
648
statsFooter = '''\
649
<!-- body end -->
650
</td>
651
</tr>
652
<tr>
653
    <td class="footer" colspan=2 align="center">&copy; 2001 - %s Ludek Smid</td>
654
</tr>
655
</table>
656
</center>
657
658
</body>
659
</html>''' % time.localtime()[0]
660