ige.ospace.GameMngr.GameMngr._createNewPlayer()   B
last analyzed

Complexity

Conditions 4

Size

Total Lines 53
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 41
nop 3
dl 0
loc 53
rs 8.896
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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