Completed
Push — master ( 600d1a...7beec5 )
by Marek
16s queued 14s
created

ige.ospace.ISystem.ISystem.getScanInfos()   C

Complexity

Conditions 11

Size

Total Lines 40
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 39
nop 5
dl 0
loc 40
rs 5.4
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like ige.ospace.ISystem.ISystem.getScanInfos() 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
import random
21
import copy
22
23
from xml.dom.minidom import Node
24
25
import ige
26
import Const
27
import Rules
28
import Utils
29
30
from ige import log
31
from ige.IObject import IObject, public
32
from ige.IDataHolder import IDataHolder
33
34
class ISystem(IObject):
35
36
    typeID = Const.T_SYSTEM
37
38
    def init(self, obj):
39
        IObject.init(self, obj)
40
        #
41
        obj.x = 0.0
42
        obj.y = 0.0
43
        obj.planets = []
44
        obj.fleets = []
45
        obj.closeFleets = []
46
        obj.starClass = u'---' # Star clasification
47
        obj.signature = 100
48
        # renaming
49
        obj.lastNameChng = 0
50
        # combat
51
        obj.combatCounter = 0
52
        # system wide data
53
        obj.scannerPwrs = {}
54
        # mine field
55
        obj.minefield = {}  # for every owner (key) list of minefield (triplet) exists (mine_id, amount, last_deployed)
56
57
    def update(self, tran, obj):
58
        # check existence of all planets
59
        if 0:
60
            for planetID in obj.planets:
61
                if not tran.db.has_key(planetID):
62
                    log.debug("CONSISTENCY - planet %d from system %d does not exists" % (planetID, obj.oid))
63
                elif tran.db[planetID].type != Const.T_PLANET:
64
                    log.debug("CONSISTENCY - planet %d from system %d is not a Const.T_PLANET" % (planetID, obj.oid))
65
        # check that all .fleet are in .closeFleets
66
        for fleetID in obj.fleets:
67
            if fleetID not in obj.closeFleets:
68
                log.debug("CONSISTENCY - fleet %d is in .fleet but not in .closeFleets - adding" % fleetID)
69
                obj.closeFleets.append(fleetID)
70
        # check existence of all fleets
71
        for fleetID in obj.closeFleets:
72
            if not tran.db.has_key(fleetID):
73
                log.debug("CONSISTENCY - fleet %d from system %d does not exists" % (fleetID, obj.oid))
74
            elif tran.db[fleetID].type not in (Const.T_FLEET, Const.T_ASTEROID):
75
                log.debug("CONSISTENCY - fleet %d from system %d is not a Const.T_FLEET" % (fleetID, obj.oid))
76
        # delete nonexistent fleets
77
        index = 0
78
        while index < len(obj.closeFleets) and obj.closeFleets:
79
            fleet = tran.db.get(obj.closeFleets[index], None)
80
            if fleet == None:
81
                log.debug("CONSISTENCY - fleet %d does not exists" % obj.closeFleets[index])
82
                fleetID = obj.closeFleets[index]
83
                obj.closeFleets.remove(fleetID)
84
                obj.fleets.remove(fleetID)
85
            else:
86
                index += 1
87
        # check compOf
88
        if not tran.db.has_key(obj.compOf) or tran.db[obj.compOf].type != Const.T_GALAXY:
89
            log.debug("CONSISTENCY invalid compOf for system", obj.oid)
90
        # rebuild closeFleets attribute
91
        old = obj.closeFleets
92
        obj.closeFleets = []
93
        for fleetID in old:
94
            fleet = tran.db.get(fleetID, None)
95
            if fleet and fleet.closeSystem == obj.oid and fleetID not in obj.closeFleets:
96
                obj.closeFleets.append(fleetID)
97
        if old != obj.closeFleets:
98
            log.debug("System close fleets fixed", obj.oid, old, obj.closeFleets)
99
100
    update.public = 0
101
102
    def getReferences(self, tran, obj):
103
        return obj.planets
104
105
    getReferences.public = 0
106
107
    def getScanInfos(self, tran, obj, scanPwr, player):
108
        result = IDataHolder()
109
        results = [result]
110
        if scanPwr >= Rules.level1InfoScanPwr:
111
            result._type = Const.T_SCAN
112
            result.scanPwr = scanPwr
113
            result.oid = obj.oid
114
            result.x = obj.x
115
            result.y = obj.y
116
            if hasattr(obj, 'destinationOid'):
117
                result.destinationOid = obj.destinationOid
118
            result.signature = obj.signature
119
            result.type = obj.type
120
            result.compOf = obj.compOf
121
            result.starClass = obj.starClass
122
        if scanPwr >= Rules.level2InfoScanPwr:
123
            result.name = obj.name
124
            result.combatCounter = obj.combatCounter
125
        if scanPwr >= Rules.level3InfoScanPwr:
126
            result.planets = obj.planets
127
            result.owner = obj.owner
128
            for planetID in obj.planets:
129
                planet = tran.db[planetID]
130
                if planet.owner == player: ####### This was player.owner, which made no sense. Hope this change doesn't break something
131
                    continue
132
                newPwr = scanPwr * planet.signature / obj.signature
133
                results.extend(self.cmd(planet).getScanInfos(tran, planet, newPwr, player))
134
        if scanPwr >= Rules.level4InfoScanPwr:
135
            result.fleets = obj.fleets
136
            for fleetID in obj.fleets:
137
                fleet = tran.db[fleetID]
138
                if fleet.owner == player:
139
                    continue
140
                newPwr = scanPwr * fleet.signature / obj.signature
141
                results.extend(self.cmd(fleet).getScanInfos(tran, fleet, newPwr, player))
142
143
            result.minefield = self.getMines(obj, player.oid)
144
            ownsMines = 1 if result.minefield else 0
145
            result.hasmines = min(2, len(self.getAllMines(obj))) - ownsMines
146
        return results
147
148
    @public(Const.AL_ADMIN)
149
    def processINITPhase(self, tran, obj, data):
150
        obj.scannerPwrs = {}
151
152
        return obj.planets
153
154
    @public(Const.AL_ADMIN)
155
    def processPRODPhase(self, tran, obj, data):
156
        # mine deployment
157
        owners = []
158
        for planetID in obj.planets:
159
            planet = tran.db[planetID]
160
            if planet.owner not in owners + [Const.OID_NONE]:
161
                owners.append(planet.owner)
162
        for owner_id in owners:
163
            self.deployMines(tran, obj, owner_id)
164
        return obj.planets
165
166
    @public(Const.AL_ADMIN)
167
    def processACTIONPhase(self, tran, obj, data):
168
        # distribute resources
169
        planets = {}
170
        # group planets by owner
171
        for planetID in obj.planets:
172
            planet = tran.db[planetID]
173
            if planet.owner != Const.OID_NONE:
174
                tmp = planets.get(planet.owner, [])
175
                tmp.append(planet)
176
                planets[planet.owner] = tmp
177
        # group planets if owners are allied
178
        # TODO
179
        # process each group
180
        for owner in planets.keys():
181
            # skip alone planets
182
            if len(planets[owner]) < 2:
183
                continue
184
            # process each resource
185
            for resName in ('Bio', 'En'):
186
                donors = []
187
                donees = []
188
                minRes = 'min%s' % resName
189
                maxRes = 'max%s' % resName
190
                storRes = 'stor%s' % resName
191
                donorsSum = 0
192
                doneesSum = 0
193
                # put planets into donors/donees
194
                for planet in planets[owner]:
195
                    if getattr(planet, storRes) > getattr(planet, minRes):
196
                        donors.append(planet)
197
                        donorsSum += getattr(planet, storRes) - getattr(planet, minRes)
198
                    elif getattr(planet, storRes) < getattr(planet, minRes):
199
                        donees.append(planet)
200
                        doneesSum += getattr(planet, minRes) - getattr(planet, storRes)
201
                #@log.debug('ISystem', obj.oid, 'Donors / donees for %s' % resName, donorsSum, doneesSum)
202
                # there are requests for donation and there is somebody able to donate
203
                if doneesSum > 0 and donorsSum > 0:
204
                    #@log.debug('ISystem', 'Redistributin %s for' % resName, owner)
205
                    # give
206
                    balance = 0
207
                    tmpRatio = min(float(doneesSum) / donorsSum, 1.0)
208
                    for planet in donees:
209
                        diff = getattr(planet, minRes) - getattr(planet, storRes)
210
                        amount = int(float(diff) / doneesSum * donorsSum * tmpRatio)
211
                        #@log.debug('ISystem', 'Give res', planet.oid, amount)
212
                        balance -= amount
213
                        setattr(planet, storRes, getattr(planet, storRes) + amount)
214
                    # take
215
                    assert donorsSum + balance >= 0
216
                    lastPlanet = None
217
                    tmpRatio = min(float(donorsSum) / doneesSum, 1.0)
218
                    for planet in donors:
219
                        diff = getattr(planet, storRes) - getattr(planet, minRes)
220
                        amount = int(float(diff) / donorsSum * doneesSum * tmpRatio)
221
                        balance += amount
222
                        #@log.debug('ISystem', 'Take res', planet.oid, amount)
223
                        setattr(planet, storRes, getattr(planet, storRes) - amount)
224
                        lastPlanet = planet
225
                    # fix rounding error
226
                    setattr(lastPlanet, storRes, getattr(lastPlanet, storRes) + balance)
227
                    #@log.debug('ISystem', 'Rounding error', balance)
228
                # try to move additional resources to the other planets
229
                for planet in planets[owner]:
230
                    if getattr(planet, storRes) > getattr(planet, maxRes):
231
                        excess = getattr(planet, storRes) - getattr(planet, maxRes)
232
                        #@log.debug('ISystem', 'Trying to move excess rsrcs from', planet.oid, excess)
233
                        for planet2 in planets[owner]:
234
                            if planet == planet2:
235
                                continue
236
                            if getattr(planet2, storRes) < getattr(planet2, maxRes):
237
                                space = getattr(planet2, maxRes) - getattr(planet2, storRes)
238
                                amount = min(space, excess)
239
                                #@log.debug('ISystem', 'Moved to', planet2.oid, amount)
240
                                setattr(planet2, storRes, getattr(planet2, storRes) + amount)
241
                                excess -= amount
242
                                if excess == 0:
243
                                    break
244
                        #@log.debug('ISystem', 'Cannot move excess rsrcs on', planet.oid, excess)
245
                        setattr(planet, storRes, getattr(planet, maxRes) + excess)
246
        # process planets and fleets
247
        #@log.debug("System close fleets", obj.oid, obj.closeFleets)
248
        return obj.planets[:] + obj.closeFleets[:]
249
250
    @public(Const.AL_ADMIN)
251
    def getObjectsInSpace(self, tran, obj):
252
        inSpace = obj.closeFleets[:]
253
        for fleetID in obj.fleets:
254
            try:
255
                inSpace.remove(fleetID)
256
            except ValueError:
257
                log.warning(obj.oid, "Cannot remove fleet from closeFleets", fleetID, obj.fleets, obj.closeFleets)
258
        return inSpace
259
260
    @public(Const.AL_ADMIN)
261
    def processBATTLEPhase(self, tran, obj, data):
262
        system = obj
263
        #@log.debug('ISystem', 'BATTLE - system', obj.oid)
264
        # we are processing fleets, planets, ...
265
        objects = obj.planets[:] + obj.fleets[:]
266
        # shuffle them to prevent predetermined one-sided battles (temporary hack)
267
        random.shuffle(objects)
268
        # store owners of objects
269
        # find enemies and allies
270
        attack = {}
271
        allies = {}
272
        owners = {}
273
        ownerIDs = {}
274
        systemAtt = {}
275
        systemDef = {}
276
        hasMine = {}
277
        isOwnedObject = 0
278
        for objID in objects:
279
            attack[objID] = []
280
            allies[objID] = []
281
            owner = tran.db[objID].owner
282
            owners[objID] = owner
283
            ownerIDs[owner] = owner
284
            if owner != Const.OID_NONE:
285
                isOwnedObject = 1
286
        for owner in ownerIDs:
287
            tempAtt, tempDef = self.getSystemCombatBonuses(tran, system, owner)
288
            systemAtt[owner] = tempAtt
289
            systemDef[owner] = tempDef
290
            hasMine[owner] = self.getSystemMineSource(tran, system, owner)
291
        if not isOwnedObject:
292
            #@log.debug('ISystem', 'No combat')
293
            # reset combat counters
294
            system.combatCounter = 0
295
            return
296
        # first - direct ones
297
        index = 1
298
        for obj1ID in objects:
299
            obj1 = tran.db[obj1ID]
300
            if obj1.owner == Const.OID_NONE:
301
                index += 1
302
                continue
303
            commander = tran.db[obj1.owner]
304
            # relationships
305
            #for obj2ID in objects[index:]:
306
            for obj2ID in objects:
307
                obj2 = tran.db[obj2ID]
308
                if obj2.owner == Const.OID_NONE or obj1 is obj2:
309
                    continue
310
                if obj1.owner == obj2.owner:
311
                    allies[obj1ID].append(obj2ID)
312
                    allies[obj2ID].append(obj1ID)
313
                    continue
314
                # planet and military object
315
                elif obj1.type == Const.T_PLANET and obj2.isMilitary and \
316
                    not self.cmd(commander).isPactActive(tran, commander, obj2.owner, Const.PACT_ALLOW_MILITARY_SHIPS):
317
                    #@log.debug("ISystem pl - mil", obj1ID, obj2ID)
318
                    if obj2ID not in attack[obj1ID]:
319
                        attack[obj1ID].append(obj2ID)
320
                    if obj1ID not in attack[obj2ID]:
321
                        attack[obj2ID].append(obj1ID)
322
                # planet and civilian object
323
                elif obj1.type == Const.T_PLANET and not obj2.isMilitary and \
324
                    not self.cmd(commander).isPactActive(tran, commander, obj2.owner, Const.PACT_ALLOW_CIVILIAN_SHIPS):
325
                    #@log.debug("ISystem pl - civ", obj1ID, obj2ID)
326
                    if obj2ID not in attack[obj1ID]:
327
                        attack[obj1ID].append(obj2ID)
328
                    if obj1ID not in attack[obj2ID]:
329
                        attack[obj2ID].append(obj1ID)
330
                # military and military object
331
                elif obj1.isMilitary and obj2.isMilitary and \
332
                    not self.cmd(commander).isPactActive(tran, commander, obj2.owner, Const.PACT_ALLOW_MILITARY_SHIPS):
333
                    #@log.debug("ISystem mil - mil", obj1ID, obj2ID)
334
                    if obj2ID not in attack[obj1ID]:
335
                        attack[obj1ID].append(obj2ID)
336
                    if obj1ID not in attack[obj2ID]:
337
                        attack[obj2ID].append(obj1ID)
338
                # military and civilian object
339
                elif obj1.isMilitary and not obj2.isMilitary and \
340
                    not self.cmd(commander).isPactActive(tran, commander, obj2.owner, Const.PACT_ALLOW_CIVILIAN_SHIPS):
341
                    #@log.debug("ISystem mil - civ", obj1ID, obj2ID)
342
                    if obj2ID not in attack[obj1ID]:
343
                        attack[obj1ID].append(obj2ID)
344
                    if obj1ID not in attack[obj2ID]:
345
                        attack[obj2ID].append(obj1ID)
346
                # planet and fleet
347
                #elif obj1.type == Const.T_PLANET and obj2.type == Const.T_FLEET and \
348
                #    self.cmd(commander).isPactActive(tran, commander, obj2.owner, PACT_MUTUAL_DEFENCE):
349
                #    allies[obj1ID].append(obj2ID)
350
                #    allies[obj2ID].append(obj1ID)
351
                # fleet and fleet
352
                #elif obj1.type == Const.T_FLEET and obj2.type == Const.T_FLEET and \
353
                #    self.cmd(commander).isPactActive(tran, commander, obj2.owner, PACT_MUTUAL_OFFENCE):
354
                #    allies[obj1ID].append(obj2ID)
355
                #    allies[obj2ID].append(obj1ID)
356
                # asteroid
357
                if obj2.type == Const.T_ASTEROID:
358
                    attack[obj1ID].append(obj2ID)
359
                    attack[obj2ID].append(obj1ID)
360
            index += 1
361
        #@log.debug('ISystem', 'Targets:', targets)
362
        #@log.debug('ISystem', 'Allies:', allies)
363
        # find indirect a/e
364
        #for objID in objects:
365
        #    iTargets = []
366
        #    iAllies = []
367
        #    # find indirect a/e
368
        #    todo = allies[objID][:]
369
        #    while todo:
370
        #        id = todo.pop(0)
371
        #        iTargets.extend(targets[id])
372
        #        for tmpID in allies[id]:
373
        #            if tmpID not in iAllies:
374
        #                todo.append(tmpID)
375
        #                iAllies.append(tmpID)
376
        #    # remove allies from targets
377
        #    for id in iAllies:
378
        #        if id in iTargets:
379
        #            iTargets.remove(id)
380
        #    # IMPORTATNT preffer NOT to fire at possible allies
381
        #    # add my targets
382
        #    #for id in targets[objID]:
383
        #    #    if id not in iTargets:
384
        #    #        iTargets.append(id)
385
        #    # that's all folks
386
        #    for id in iTargets:
387
        #        if objID not in attack[id]:
388
        #            attack[id].append(objID)
389
        #        if id not in attack[objID]:
390
        #            attack[objID].append(id)
391
        # NOT VALID: objects with action ACTION_ATTACK will attack only their targets
392
        # check, if there are any targets
393
        isCombat = 0
394
        for objID in objects:
395
            if attack[objID]:
396
                isCombat = 1
397
                break #end loop
398
        if not isCombat:
399
            #@log.debug('ISystem', 'No combat')
400
            # reset combat counters
401
            system.combatCounter = 0
402
            for fleetID in system.fleets:
403
                tran.db[fleetID].combatCounter = 0
404
            return
405
        # increase combat counters
406
        system.combatCounter += 1
407
        for fleetID in system.fleets:
408
            tran.db[fleetID].combatCounter += 1
409
        # debug
410
        log.debug('ISystem', 'Final attacks in system %d:' % system.oid, attack)
411
        # mines detonate before battle
412
        shots = {}
413
        targets = {}
414
        firing = {}
415
        damageCaused = {}
416
        killsCaused = {}
417
        damageTaken = {}
418
        shipsLost = {}
419
        minesTriggered = {}
420
        fleetOwners = {}
421
        isCombat = False
422
        isMineCombat = False
423
        for owner in ownerIDs:
424
            if owner not in hasMine: # no planets
425
                continue
426
            if not hasMine[owner]: # no planet with control structure
427
                continue
428
            controlPlanetID = hasMine[owner][0]  # there is list returned, all planets have same effect
429
            if len(self.getMines(system, owner)) == 0:
430
                continue # no mines, something broke
431
            if len(attack[controlPlanetID]) == 0:
432
                continue # no targets
433
            isMineFired = True
434
            mineTargets = copy.copy(attack[controlPlanetID])
435
            while isMineFired:
436
                while len(mineTargets) > 0:
437
                    targetID = random.choice(mineTargets) # select random target
438
                    targetobj = tran.db.get(targetID, None)
439
                    try:
440
                        if targetobj.type == Const.T_FLEET:
441
                            fleetOwners[targetID] = targetobj.owner
442
                            break # target found
443
                        mineTargets.remove(targetID)  # remove an object type that a mine can't hit from the temporary targets list
444
                    except:
445
                        mineTargets.remove(targetID)  # remove a dead fleet from the temporary targets list
446
447
                if len(mineTargets) == 0:
448
                    break # no fleet targets for mines
449
                temp, temp, firing[targetID] = self.cmd(targetobj).getPreCombatData(tran, targetobj)  # fix firing for "surrender to" section
0 ignored issues
show
introduced by
The variable targetobj does not seem to be defined for all execution paths.
Loading history...
450
                damage, att, ignoreshield, mineID = self.cmd(obj).fireMine(system, owner)
451
                if not damage: # no more mines
452
                    isMineFired = False
453
                    break
454
                log.debug('ISystem', 'Mine Shooting (damage, att, ignore shield):', damage, att, ignoreshield)
455
                isMineCombat = True
456
                minesTriggered[mineID] = minesTriggered.get(mineID, 0) + 1
457
                # Process Combat
458
                # for now we assume only one ship can be destroyed by one mine
459
                dmg, destroyed = self.cmd(targetobj).applyMine(tran, targetobj, att, damage, ignoreshield)
460
                #log.debug('ISystem-Mines', 'Actual Damage Done:',dmg)
461
                if dmg > 0:
462
                    damageTaken[targetID] = damageTaken.get(targetID, 0) + dmg
0 ignored issues
show
introduced by
The variable targetID does not seem to be defined for all execution paths.
Loading history...
463
                    shipsLost[targetID] = shipsLost.get(targetID, 0) + destroyed
464
                    killsCaused[mineID] = killsCaused.get(mineID, 0) + destroyed
465
                if dmg > 0:
466
                    damageCaused[mineID] = damageCaused.get(mineID, 0) + dmg
467
            # send messages about mine effects to the owner of the minefield
468
            # collect hit players
469
            players = {}
470
            for triggerID in firing.keys():
471
                players[owners[triggerID]] = None
472
            controllerPlanet = tran.db.get(controlPlanetID, None)
473
            damageCausedSum = 0
474
            killsCausedSum = 0
475
            for mineID in damageCaused.keys():
476
                damageCausedSum = damageCausedSum + damageCaused.get(mineID, 0)
477
                killsCausedSum = killsCausedSum + killsCaused.get(mineID, 0)
478
            Utils.sendMessage(tran, controllerPlanet, Const.MSG_MINES_OWNER_RESULTS, system.oid, (players.keys(),(damageCaused, killsCaused, minesTriggered),damageCausedSum,killsCausedSum))
479
        # send messages to the players whose fleets got hit by minefields
480
        for targetID in damageTaken.keys():
481
            targetFleet = tran.db.get(targetID, None)
482
            if targetFleet:
483
                Utils.sendMessage(tran, targetFleet, Const.MSG_MINES_FLEET_RESULTS, system.oid, (damageTaken[targetID], shipsLost[targetID]))
484
            else:
485
                targetFleet = IDataHolder()
486
                targetFleet.oid = fleetOwners[targetID]
487
                Utils.sendMessage(tran, targetFleet, Const.MSG_MINES_FLEET_RESULTS, system.oid, (damageTaken[targetID], shipsLost[targetID]))
488
                Utils.sendMessage(tran, targetFleet, Const.MSG_DESTROYED_FLEET, system.oid, ())
489
        damageCaused = {}
490
        killsCaused = {}
491
        damageTaken = {}
492
        shipsLost = {}
493
        # now to battle
494
        for objID in objects:
495
            obj = tran.db.get(objID, None)
496
            # get shots from object, should be sorted by weaponClass
497
            # shots = [ shot, ...], shot = (combatAtt, weaponID)
498
            # get target classes and numbers
499
            # (class1, class2, class3, class4)
500
            # cls0 == fighters, cls1 == midships, cls2 == capital ships, cls3 == planet installations
501
            #@log.debug(objID, obj.name, "getting pre combat data")
502
            if obj: # source already destroyed; ignore
503
                shots[objID], targets[objID], firing[objID] = self.cmd(obj).getPreCombatData(tran, obj)
504
                if firing[objID]:
505
                    isCombat = True
506
        if not isCombat and not isMineCombat:
507
            # no shots has been fired
508
            #@log.debug('ISystem', 'No combat')
509
            # reset combat counters
510
            system.combatCounter = 0
511
            for fleetID in system.fleets:
512
                tran.db[fleetID].combatCounter = 0
513
            return
514
        #@log.debug("Shots:", shots)
515
        #@log.debug("Targets", targets)
516
        if isCombat:
517
            for shotIdx in (3, 2, 1, 0):
518
                for objID in objects:
519
                    # obj CAN be deleted at this point
520
                    obj = tran.db.get(objID, None)
521
                    if obj == None:
522
                        continue # source already destroyed; move to next source
523
                    # if object is fleet, then it's signature is max
524
                    if obj and obj.type == Const.T_FLEET:
525
                        obj.signature = Rules.maxSignature
526
                    # target preselection
527
                    totalClass = [0, 0, 0, 0]
528
                    total = 0
529
                    for targetID in attack[objID]:
530
                        totalClass[0] += targets[targetID][0]
531
                        totalClass[1] += targets[targetID][1]
532
                        totalClass[2] += targets[targetID][2]
533
                        totalClass[3] += targets[targetID][3]
534
                    total = totalClass[0] + totalClass[1] + totalClass[2] + totalClass[3]
535
                    # process shots
536
                    for combatAtt, weaponID in shots[objID][shotIdx]:
537
                        weapon = Rules.techs[weaponID]
538
                        weaponClass = weapon.weaponClass
539
                        if total == 0:
540
                            # there are no targets
541
                            break
542
                        #@log.debug('ISystem', 'Processing shot', objID, weapon.name, weaponClass)
543
                        # process from weaponClass up
544
                        # never shoot on smaller ships than weaponClass
545
                        applied = 0
546
                        for tmpWpnClass in xrange(weaponClass, 4):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable xrange does not seem to be defined.
Loading history...
547
                            #@log.debug('ISystem', 'Trying target class', tmpWpnClass, totalClass[tmpWpnClass])
548
                            # select target
549
                            if totalClass[tmpWpnClass]:
550
                                target = Utils.rand(0, totalClass[tmpWpnClass])
551
                                #@log.debug('ISystem', 'Target rnd num', target, totalClass[tmpWpnClass])
552
                                for targetID in attack[objID]:
553
                                    if target < targets[targetID][tmpWpnClass]:
554
                                        #@log.debug(objID, 'attacks', targetID, tmpWpnClass)
555
                                        # targetID can be deleted at this point
556
                                        anObj = tran.db.get(targetID, None)
557
                                        if anObj:
558
                                            dmg, destroyed, destroyedClass = self.cmd(anObj).applyShot(tran, anObj, systemDef[owners[targetID]], combatAtt + systemAtt[owners[objID]], weaponID, tmpWpnClass, target)
559
                                            #@log.debug("ISystem result", dmg, destroyed, destroyedClass, tmpWpnClass)
560
                                            #@print objID, 'dmg, destroyed', dmg, destroyed
561
                                            damageTaken[targetID] = damageTaken.get(targetID, 0) + dmg
562
                                            if destroyed > 0:
563
                                                shipsLost[targetID] = shipsLost.get(targetID, 0) + destroyed
564
                                                total -= destroyed
565
                                                totalClass[destroyedClass] -= destroyed
566
                                            if dmg > 0 and obj:
567
                                                obj.combatExp += dmg
568
                                                damageCaused[objID] = damageCaused.get(objID, 0) + dmg
569
                                            applied = 1
570
                                        else:
571
                                            continue # target already destroyed, move to next target
572
                                        break
573
                                    else:
574
                                        #@log.debug('ISystem', 'Lovering target by', targets[targetID][tmpWpnClass])
575
                                        target -= targets[targetID][tmpWpnClass]
576
                            if applied:
577
                                break
578
        # send messages and modify diplomacy relations
579
        # distribute experience pts
580
        for objID in objects:
581
            obj = tran.db.get(objID, None)
582
            if obj:
583
                self.cmd(obj).distributeExp(tran, obj)
584
            if attack[objID]:
585
                source = obj or tran.db[owners[objID]]
586
                # collect players
587
                players = {}
588
                for attackerID in attack[objID]:
589
                    players[owners[attackerID]] = None
590
                d1 = damageTaken.get(objID,0)
591
                d2 = damageCaused.get(objID,0)
592
                l = shipsLost.get(objID, 0)
593
                if d1 or d2 or l:
594
                    # send only if damage is taken/caused
595
                    Utils.sendMessage(tran, source, Const.MSG_COMBAT_RESULTS, system.oid, (d1, d2, l, players.keys()))
596
                if not obj:
597
                    # report DESTROYED status
598
                    Utils.sendMessage(tran, source, Const.MSG_DESTROYED_FLEET, system.oid, ())
599
                # modify diplomacy relations
600
                objOwner = tran.db[owners[objID]]
601
                for attackerID in attack[objID]:
602
                    attOwner = tran.db.get(owners[attackerID], None)
603
                    # owner of the fleet
604
                    rel = self.cmd(objOwner).getDiplomacyWith(tran, objOwner, attOwner.oid)
605
                    rel.relChng = Rules.relLostWhenAttacked
606
                    # attacker
607
                    rel = self.cmd(attOwner).getDiplomacyWith(tran, attOwner, objOwner.oid)
608
                    rel.rechChng = Rules.relLostWhenAttacked
609
        # check if object surrenders
610
        for objID in objects:
611
            # object surrender IFF it and its allies had target and was not able
612
            # to fire at it, planet is not counted as ally in this case
613
            obj = tran.db.get(objID, None)
614
            if firing[objID] and obj:
615
                continue
616
            surrenderTo = []
617
            for attID in attack[objID]:
618
                if firing[attID] and tran.db.has_key(attID):
619
                    surrenderTo.append(tran.db[attID].owner)
620
            for allyID in allies[objID]:
621
                if not tran.db.has_key(allyID):
622
                    continue
623
                ally = tran.db[allyID]
624
                if firing[allyID] and ally.type != Const.T_PLANET:
625
                    surrenderTo = []
626
                    break
627
            if surrenderTo:
628
                index = Utils.rand(0, len(surrenderTo))
629
                if obj:
630
                    if self.cmd(obj).surrenderTo(tran, obj, surrenderTo[index]):
631
                        winner = tran.db[surrenderTo[index]]
632
                        source = tran.db.get(owners[objID], None)
633
                        log.debug('ISystem', 'BATTLE - surrender', objID, surrenderTo[index], surrenderTo)
634
                        if source:
635
                            Utils.sendMessage(tran, source, Const.MSG_COMBAT_LOST, system.oid, winner.oid)
636
                            Utils.sendMessage(tran, winner, Const.MSG_COMBAT_WON, system.oid, source.oid)
637
                        else:
638
                            Utils.sendMessage(tran, winner, Const.MSG_COMBAT_WON, system.oid, obj.oid)
639
                else:
640
                    winner = tran.db[surrenderTo[index]]
641
                    source = tran.db[owners[objID]]
642
                    log.debug('ISystem', 'BATTLE - surrender', objID, surrenderTo[index], surrenderTo)
643
                    Utils.sendMessage(tran, source, Const.MSG_COMBAT_LOST, system.oid, winner.oid)
644
                    Utils.sendMessage(tran, winner, Const.MSG_COMBAT_WON, system.oid, source.oid)
645
        return
646
647
    @public(Const.AL_ADMIN)
648
    def processFINALPhase(self, tran, obj, data):
649
        # TODO find new starting points
650
        # clean up mines if system ownership was lost
651
        owners = []
652
        for planetID in obj.planets:
653
            planet = tran.db[planetID]
654
            if planet.owner not in owners:
655
                owners.append(planet.owner)
656
        for ownerid in obj.minefield:
657
            if ownerid not in owners:
658
                self.clearMines(obj, ownerid)
659
        return obj.planets[:] + obj.closeFleets[:]
660
661
    def sortPlanets(self, tran, obj, data):
662
        obj.planets.sort(key=lambda planetID: tran.db[planetID].plEn, reverse = True)
663
        orbit = 1
664
        for planetID in obj.planets:
665
            planet = tran.db[planetID]
666
            planet.orbit = orbit
667
            orbit += 1
668
669
    sortPlanets.public = 0
670
671
    @public(Const.AL_NONE)
672
    def rename(self, tran, obj, newName, nType):
673
        newName = newName.strip()
674
        # you have to own all planets
675
        # TODO: Throw another cmdr exc AFTER you have no planet
676
        haveOne = 0
677
        anotherComm = 0
678
        for planetID in obj.planets:
679
            planet = tran.db[planetID]
680
            if planet.owner != tran.session.cid and planet.owner != Const.OID_NONE:
681
                anotherComm = 1
682
            if planet.owner == tran.session.cid:
683
                haveOne = 1
684
        if not haveOne:
685
            raise ige.GameException('You cannot change name of this system - you have no planet in this system.')
686
        if anotherComm:
687
            raise ige.GameException('You cannot change name of this system - another commander in system.')
688
        # check validity of name
689
        if not Utils.isCorrectName(newName):
690
            raise ige.GameException('Invalid name. Only characters, digits, space, dot and dash permitted, max. length is 30 characters.')
691
        # check if there is other system with this name
692
        galaxy = tran.db[obj.compOf]
693
        for systemID in galaxy.systems:
694
            if tran.db[systemID].name == newName and systemID != obj.oid:
695
                raise ige.GameException('This name is already used.')
696
        # TODO you have to own this system longer than previous owner
697
        # one change per 1 day allowed
698
        turn = tran.db[Const.OID_UNIVERSE].turn
699
        if obj.lastNameChng + Rules.turnsPerDay <= turn:
700
            # rename system
701
            obj.name = newName
702
            # rename planets
703
            newNames = [obj.name]
704
            for planetID in obj.planets:
705
                planet = tran.db[planetID]
706
                planet.name = Utils.getPlanetName(obj.name, nType, planet.orbit - 1)
707
                newNames.append(planet.name)
708
            obj.lastNameChng = turn
709
        else:
710
            raise ige.GameException('You cannot change name of this system - name has been changed recently (try it one day later).')
711
        return newNames
712
713
    def createPlanet(self, tran, obj):
714
        planet = self.new(Const.T_PLANET)
715
        planet.compOf = obj.oid
716
        oid = tran.db.create(planet)
717
        obj.planets.append(oid)
718
        return oid
719
720
    @public(Const.AL_ADMIN)
721
    def deployMines(self, tran, obj, owner_id):
722
        """ Go through all mine control structures and attempt to add mines.
723
724
        """
725
        for tech, struct in self.getSystemMineLauncher(tran, obj, owner_id):
726
            if not struct[Const.STRUCT_IDX_STATUS] & Const.STRUCT_STATUS_ON:
727
                # structure is offline, reset timer
728
                self.addMine(tran, obj, owner_id, tech.mineclass, 0)
729
                continue
730
            efficiency = struct[Const.STRUCT_IDX_HP] / float(tech.maxHP)
731
            minerate = int(tech.minerate / efficiency)
732
            minenum = int(tech.minenum * efficiency)
733
            if self.addMine(tran, obj, owner_id, tech.mineclass, minenum, minerate):
734
                log.debug('ISystem', 'Mine deployed for owner %d in system %d' % (owner_id, obj.oid))
735
736
    def _expandExistingMinefield(self, minefields, index, current_turn, max_amount, mine_rate):
737
        mine_id, amount, deploy_turn = minefields[index]
738
        if max_amount is not None and amount >= max_amount:
739
            # cannot add more, thus update deploy turn to current one
740
            # (so replenish starts after that amount of turns if needed)
741
            minefields[index] = (mine_id, amount, current_turn)
742
            return False
743
        if mine_rate and (current_turn - deploy_turn) < mine_rate:
744
            # need more time to deploy new mine
745
            return False
746
        minefields[index] = (mine_id, amount + 1, current_turn)
747
        return True
748
749
    @public(Const.AL_ADMIN)
750
    def addMine(self, tran, obj, owner_id, mine_tech_id, max_amount=None, mine_rate=None):
751
        """ Increment amount within particular minefield of particular player.
752
        Set current turn as a date of latest deployment.
753
754
        Returns True if mine was added.
755
756
        """
757
        current_turn = tran.db[obj.compOf].galaxyTurn
758
        if owner_id in obj.minefield:
759
            index = -1
760
            for mine_id, amount, deploy_turn in obj.minefield[owner_id]:
761
                index += 1
762
                if mine_id != mine_tech_id:
763
                    continue
764
                return self._expandExistingMinefield(obj.minefield[owner_id], index, current_turn, max_amount, mine_rate)
765
            # owner has some minefield, but not this type
766
            obj.minefield[owner_id].append((mine_tech_id, 1, current_turn))
767
            return True
768
        else:
769
            # this will be owner's first minefield in this system
770
            if mine_rate == 1 or mine_rate is None:
771
                obj.minefield[owner_id] = [(mine_tech_id, 1, current_turn)]
772
                return True
773
            else:
774
                obj.minefield[owner_id] = [(mine_tech_id, 0, current_turn - 1)]
775
                return False
776
777
778
    @public(Const.AL_ADMIN)
779
    def removeMine(self, obj, owner_id, mine_tech_id):
780
        """ Decrement amount within particular minefield of particular player.
781
        It's not possible to clean up records - it's expected to stay there till
782
        the end of game like that.
783
784
        """
785
        if owner_id in obj.minefield:
786
            index = -1
787
            for mine_id, amount, deploy_turn in obj.minefield[owner_id]:
788
                index += 1
789
                if mine_id != mine_tech_id or amount == 0:
790
                    continue
791
                amount -= 1
792
                obj.minefield[owner_id][index] = (mine_id, amount, deploy_turn)
793
                break
794
795
    def getMines(self, obj, owner_id):
796
        """ Return list of tuples representing each minefield.
797
798
        """
799
        try:
800
            return [mines for mines in obj.minefield[owner_id] if mines[Const.MINE_IDX_AMOUNT]]
801
        except KeyError:
802
            return []
803
804
    def getAllMines(self, obj):
805
        """ Return list of tuples representing each minefield.
806
807
        """
808
        minefields = {}
809
        for owner_id in obj.minefield:
810
            mines = self.getMines(obj, owner_id)
811
            if mines:
812
                minefields[owner_id] = mines
813
        return minefields
814
815
    def clearMines(self, obj, owner_id):
816
        """ Remove all minefields of given owner from the system.
817
818
        """
819
        if owner_id in obj.minefield:
820
            del obj.minefield[owner_id]
821
822
    clearMines.public = 0
823
824
    @public(Const.AL_ADMIN)
825
    def fireMine(self, obj, owner_id): # shoot the mine
826
        if owner_id not in obj.minefield:
827
            return False, False, False, False
828
829
        mine_ids = []
830
        amounts = []
831
        for mine_id, amount, deploy_turn in obj.minefield[owner_id]:
832
            mine_ids.append(mine_id)
833
            amounts.append(amount)
834
        if not sum(amounts):
835
            return False, False, False, False
836
        mine = Utils.weightedRandom(mine_ids, amounts) # select random mine to detonate
837
        self.removeMine(obj, owner_id, mine)
838
        tech = Rules.techs[mine]
839
        damage = random.randrange(tech.weaponDmgMin, tech.weaponDmgMax)
840
        attack = tech.weaponAtt
841
        ignore_shield = tech.weaponIgnoreShield
842
        return damage, attack, ignore_shield, mine
843
844
    def getSystemMineLauncher(self, tran, obj, playerID):
845
        launchers = []
846
        for planetID in obj.planets:
847
            planet = tran.db[planetID]
848
            if planet.owner == playerID:
849
                for struct in planet.slots:
850
                    tech = Rules.techs[struct[Const.STRUCT_IDX_TECHID]]
851
                    if tech.mineclass:
852
                        launchtech = tech
853
                        launchers.append((launchtech, struct))
854
        return launchers
855
856
    getSystemMineLauncher.public = 0
857
858
    def getSystemMineSource(self, tran, obj, playerID):
859
        sources = []
860
        for planetID in obj.planets:
861
            planet = tran.db[planetID]
862
            if planet.owner == playerID:
863
                for struct in planet.slots:
864
                    tech = Rules.techs[struct[Const.STRUCT_IDX_TECHID]]
865
                    if tech.mineclass:
866
                        sources.append(planetID)
867
        return sources
868
869
    getSystemMineSource.public = 0
870
871
    def getSystemCombatBonuses(self, tran, obj, playerID):
872
        systemAtt = 0;
873
        systemDef = 0;
874
        for planetID in obj.planets:
875
            planet = tran.db[planetID]
876
            if planet.owner == playerID:
877
                for struct in planet.slots:
878
                    tech = Rules.techs[struct[Const.STRUCT_IDX_TECHID]]
879
                    techEff = Utils.getTechEff(tran, struct[Const.STRUCT_IDX_TECHID], planet.owner)
880
                    if tech.systemAtt > 0 or tech.systemDef > 0:
881
                        systemAtt = max(systemAtt, tech.systemAtt * techEff)
882
                        systemDef = max(systemDef, tech.systemDef * techEff)
883
        return (systemAtt, systemDef)
884
885
    getSystemCombatBonuses.public = 0
886
887
    def loadDOMNode(self, tran, obj, xoff, yoff, node):
888
        obj.x = float(node.getAttribute('x')) + xoff
889
        obj.y = float(node.getAttribute('y')) + yoff
890
        orbit = 1
891
        nType = Utils.getPlanetNamesType()
892
        for elem in node.childNodes:
893
            if elem.nodeType == Node.ELEMENT_NODE:
894
                name = elem.tagName
895
                if name == 'properties':
896
                    self.loadDOMAttrs(obj, elem)
897
                elif name == 'planet':
898
                    # create planet
899
                    planet = tran.db[self.createPlanet(tran, obj)]
900
                    self.cmd(planet).loadDOMNode(tran, planet, obj.x, obj.y, orbit, elem)
901
                    # planet.name = u'%s %s' % (obj.name, '-ABCDEFGHIJKLMNOPQRSTUVWXYZ'[orbit])
902
                    planet.name = Utils.getPlanetName(obj.name, nType, orbit - 1)
903
                    orbit += 1
904
                else:
905
                    raise ige.GameException('Unknown element %s' % name)
906
        return Const.SUCC
907