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