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