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