1
|
|
|
# |
2
|
|
|
# Copyright 2001 - 2016 Ludek Smid [http://www.ospace.net/] |
3
|
|
|
# |
4
|
|
|
# This file is part of Outer Space. |
5
|
|
|
# |
6
|
|
|
# Outer Space is free software; you can redistribute it and/or modify |
7
|
|
|
# it under the terms of the GNU General Public License as published by |
8
|
|
|
# the Free Software Foundation; either version 2 of the License, or |
9
|
|
|
# (at your option) any later version. |
10
|
|
|
# |
11
|
|
|
# Outer Space is distributed in the hope that it will be useful, |
12
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
13
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14
|
|
|
# GNU General Public License for more details. |
15
|
|
|
# |
16
|
|
|
# You should have received a copy of the GNU General Public License |
17
|
|
|
# along with Outer Space; if not, write to the Free Software |
18
|
|
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
19
|
|
|
# |
20
|
|
|
|
21
|
|
|
import math |
22
|
|
|
import os |
23
|
|
|
import random |
24
|
|
|
import tempfile |
25
|
|
|
import time |
26
|
|
|
|
27
|
|
|
from ige.IObject import IObject, public |
28
|
|
|
from ige.IDataHolder import IDataHolder |
29
|
|
|
from Const import * |
30
|
|
|
import Rules |
31
|
|
|
from IGalaxy import IGalaxy |
32
|
|
|
import ige |
33
|
|
|
import ige.version |
34
|
|
|
from ige import log |
35
|
|
|
import GalaxyGenerator |
36
|
|
|
from ige import GameException, NoSuchObjectException |
37
|
|
|
|
38
|
|
|
class IUniverse(IObject): |
39
|
|
|
|
40
|
|
|
typeID = T_UNIVERSE |
|
|
|
|
41
|
|
|
|
42
|
|
|
forums = { |
43
|
|
|
# Official English forums |
44
|
|
|
"NEWS": 112, "PUBLIC": 112, |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
def init(self, obj): |
48
|
|
|
IObject.init(self, obj) |
49
|
|
|
# |
50
|
|
|
obj.name = "Outer Space" |
51
|
|
|
obj.turn = 0 |
52
|
|
|
obj.owner = OID_ADMIN |
|
|
|
|
53
|
|
|
obj.galaxies = [] |
54
|
|
|
obj.players = [] |
55
|
|
|
obj.waitingPlayers = [] |
56
|
|
|
# auto loading of galaxies |
57
|
|
|
obj.galX = 0.0 |
58
|
|
|
obj.galY = 0.0 |
59
|
|
|
obj.galXStep = 100.0 |
60
|
|
|
obj.galYStep = 100.0 |
61
|
|
|
obj.galFilename = '' |
62
|
|
|
obj.galID = '' |
63
|
|
|
|
64
|
|
|
@public(AL_NONE) |
65
|
|
|
def getIntroInfo(self, tran, obj): |
66
|
|
|
result = IDataHolder() |
67
|
|
|
result.cid = tran.cid |
68
|
|
|
result.turn = obj.turn |
69
|
|
|
result.serverTime = time.time() |
70
|
|
|
result.version = ige.version.version |
71
|
|
|
return result |
72
|
|
|
|
73
|
|
|
@public(AL_NONE) |
74
|
|
|
def multiGetInfo(self, tran, obj, objIDs): |
75
|
|
|
result = [] |
76
|
|
|
messages = [] |
77
|
|
|
# perform getInfo or getPublicInfo command for each objID |
78
|
|
|
for objID in objIDs: |
79
|
|
|
try: |
80
|
|
|
tmpObj, tmpMsgs = tran.gameMngr.execute(tran.session.sid, 'getInfo', objID) |
81
|
|
|
except ige.SecurityException: |
82
|
|
|
tmpObj, tmpMsgs = tran.gameMngr.execute(tran.session.sid, 'getPublicInfo', objID) |
83
|
|
|
except ige.NoSuchObjectException: |
84
|
|
|
tmpObj = None |
85
|
|
|
if tmpObj: |
86
|
|
|
result.append(tmpObj) |
87
|
|
|
messages.extend(tmpMsgs) |
88
|
|
|
# restore messages |
89
|
|
|
for msgID, data in messages: |
90
|
|
|
tran.session.messages[msgID] = data |
91
|
|
|
return result |
92
|
|
|
|
93
|
|
|
@public(AL_NONE) |
94
|
|
|
def multiGetMsgs(self, tran, obj, mailboxes): |
95
|
|
|
result = [] |
96
|
|
|
messages = [] |
97
|
|
|
# perform getMsgs |
98
|
|
|
for objID, lastID in mailboxes: |
99
|
|
|
data, tmpMsgs = tran.gameMngr.execute(tran.session.sid, 'getMsgs', objID, lastID) |
100
|
|
|
result.append((objID, data)) |
101
|
|
|
messages.extend(tmpMsgs) |
102
|
|
|
# restore messages |
103
|
|
|
for msgID, data in messages: |
104
|
|
|
tran.session.messages[msgID] = data |
105
|
|
|
return result |
106
|
|
|
|
107
|
|
|
@public(AL_ADMIN) |
108
|
|
|
def createGalaxy(self, tran, obj): |
109
|
|
|
galaxy = self.new(T_GALAXY) |
|
|
|
|
110
|
|
|
galaxy.compOf = obj.oid |
111
|
|
|
oid = tran.db.create(galaxy) |
112
|
|
|
obj.galaxies.append(oid) |
113
|
|
|
return oid |
114
|
|
|
|
115
|
|
|
@public(AL_ADMIN) |
116
|
|
|
def createAsteroid(self, tran, obj, x, y, targetID, speed, hp): |
117
|
|
|
asteroid = self.new(T_ASTEROID) |
|
|
|
|
118
|
|
|
tran.db.create(asteroid) |
119
|
|
|
self.cmd(asteroid).create(tran, asteroid, x, y, targetID, speed, hp) |
120
|
|
|
return asteroid.oid |
121
|
|
|
|
122
|
|
|
@public(AL_ADMIN) |
123
|
|
|
def processINITPhase(self, tran, obj, data): |
124
|
|
|
for galaxyID in obj.galaxies: |
125
|
|
|
galaxy = tran.db[galaxyID] |
126
|
|
|
self.cmd(galaxy).enableTime(tran, galaxy) |
127
|
|
|
try: |
128
|
|
|
## find active/inactive pacts |
129
|
|
|
# set all active/on pacts to active |
130
|
|
|
for playerID in obj.players: |
131
|
|
|
#@log.debug("Processing player", playerID) |
132
|
|
|
player = tran.db[playerID] |
133
|
|
|
for partyID in player.diplomacyRels: |
134
|
|
|
#@log.debug("Processing party", partyID) |
135
|
|
|
dipl = player.diplomacyRels[partyID] |
136
|
|
|
for pactID in dipl.pacts.keys(): |
137
|
|
|
if pactID not in Rules.pactDescrs: |
138
|
|
|
# this is invalid pactID |
139
|
|
|
log.debug(playerID, "Deleting invalid pact with", partyID, "pact", pactID) |
140
|
|
|
del dipl.pacts[pactID] |
141
|
|
|
continue |
142
|
|
|
if dipl.pacts[pactID][0] > PACT_OFF: |
|
|
|
|
143
|
|
|
dipl.pacts[pactID][0] = PACT_ACTIVE |
|
|
|
|
144
|
|
|
# inactivate all pact that does not satisfy conditions |
145
|
|
|
changed = 1 |
146
|
|
|
defaultPact = [PACT_OFF] |
147
|
|
|
while changed: |
148
|
|
|
changed = 0 |
149
|
|
|
log.debug("Inactivate pacts iteration starting...") |
150
|
|
|
for playerID in obj.players: |
151
|
|
|
#@log.debug("Processing player", playerID) |
152
|
|
|
player = tran.db[playerID] |
153
|
|
|
# all parties of a player |
154
|
|
|
for partyID in player.diplomacyRels: |
155
|
|
|
#@log.debug("Processing party", partyID) |
156
|
|
|
party = tran.db[partyID] |
157
|
|
|
partyDipl = party.diplomacyRels.get(playerID, None) |
158
|
|
|
if not partyDipl: |
159
|
|
|
continue |
160
|
|
|
dipl = player.diplomacyRels[partyID] |
161
|
|
|
# correct relations |
162
|
|
|
dipl.relation = min(dipl.relation, partyDipl.relation) |
163
|
|
|
# all pacts with party |
164
|
|
|
for pactID in dipl.pacts: |
165
|
|
|
# check validity interval |
166
|
|
|
pactSpec = Rules.pactDescrs[pactID] |
167
|
|
|
if (dipl.relation < pactSpec.validityInterval[0] or \ |
168
|
|
|
dipl.relation > pactSpec.validityInterval[1]) and \ |
169
|
|
|
dipl.pacts[pactID][0] == PACT_ACTIVE: |
170
|
|
|
#@log.debug("Inactivating pact (validity interval)", playerID, pactID) |
171
|
|
|
dipl.pacts[pactID][0] = PACT_INACTIVE |
|
|
|
|
172
|
|
|
changed = 1 |
173
|
|
|
# check conditions for the pact if pact is active |
174
|
|
|
if dipl.pacts[pactID][0] == PACT_ACTIVE: |
175
|
|
|
for condPactID in dipl.pacts[pactID][1:]: |
176
|
|
|
#@log.debug("Checking", playerID, pactID, "against", partyID, condPactID) |
177
|
|
|
if partyDipl and partyDipl.pacts.get(condPactID, defaultPact)[0] != PACT_ACTIVE: |
178
|
|
|
dipl.pacts[pactID][0] = PACT_INACTIVE |
179
|
|
|
changed = 1 |
180
|
|
|
except Exception: |
181
|
|
|
log.warning("Cannot process diplomacy initialization") |
182
|
|
|
# TODO - send notifications if pacts are changed |
183
|
|
|
# remove old messages |
184
|
|
|
self.cmd(obj).deleteOldMsgs(tran, obj) |
185
|
|
|
return obj.players[:] + [OID_NATURE] |
|
|
|
|
186
|
|
|
|
187
|
|
|
@public(AL_ADMIN) |
188
|
|
|
def processPRODPhase(self, tran, obj, data): |
189
|
|
|
raise NotImplementedError() |
190
|
|
|
|
191
|
|
|
@public(AL_ADMIN) |
192
|
|
|
def processACTIONPhase(self, tran, obj, data): |
193
|
|
|
raise NotImplementedError() |
194
|
|
|
|
195
|
|
|
@public(AL_ADMIN) |
196
|
|
|
def processBATTLEPhase(self, tran, obj, data): |
197
|
|
|
raise NotImplementedError() |
198
|
|
|
|
199
|
|
|
@public(AL_ADMIN) |
200
|
|
|
def processFINALPhase(self, tran, obj, data): |
201
|
|
|
return obj.players[:] + [OID_NATURE] |
|
|
|
|
202
|
|
|
|
203
|
|
|
@public(AL_ADMIN) |
204
|
|
|
def processFINAL2Phase(self, tran, obj, data): |
205
|
|
|
# distribute stats to contacts |
206
|
|
|
for playerID in obj.players: |
207
|
|
|
player = tran.db[playerID] |
208
|
|
|
for partyID in player.diplomacyRels: |
209
|
|
|
dipl = player.diplomacyRels[partyID] |
210
|
|
|
if dipl.contactType > CONTACT_NONE and tran.db.has_key(partyID): |
|
|
|
|
211
|
|
|
dipl.stats = tran.db[partyID].stats |
212
|
|
|
else: |
213
|
|
|
dipl.stats = None |
214
|
|
|
|
215
|
|
|
# process each galaxy winning checking routines |
216
|
|
|
for galaxyID in obj.galaxies: |
217
|
|
|
log.debug("Voting for galaxy", galaxyID) |
218
|
|
|
galaxy = tran.db[galaxyID] |
219
|
|
|
if not galaxy.timeEnabled: |
220
|
|
|
# skip this galaxy |
221
|
|
|
continue |
222
|
|
|
if galaxy.scenario == SCENARIO_OUTERSPACE: |
|
|
|
|
223
|
|
|
self.processScenarioOuterspace(tran, obj, galaxy) |
224
|
|
|
continue |
225
|
|
|
elif galaxy.scenario == SCENARIO_SINGLE: |
|
|
|
|
226
|
|
|
self.processScenarioSingle(tran, obj, galaxy) |
227
|
|
|
continue |
228
|
|
|
elif galaxy.scenario == SCENARIO_COOP: |
|
|
|
|
229
|
|
|
self.processScenarioCoop(tran, obj, galaxy) |
230
|
|
|
continue |
231
|
|
|
elif galaxy.scenario == SCENARIO_BRAWL: |
|
|
|
|
232
|
|
|
self.processScenarioBrawl(tran, obj, galaxy) |
233
|
|
|
continue |
234
|
|
|
|
235
|
|
|
# collect mailboxes |
236
|
|
|
used = [self.cmd(obj).getMailboxName(tran, obj)] |
237
|
|
|
for galaxyID in obj.galaxies: |
238
|
|
|
tmpObj = tran.db[galaxyID] |
239
|
|
|
used.append(self.cmd(tmpObj).getMailboxName(tran, tmpObj)) |
240
|
|
|
for playerID in obj.players: |
241
|
|
|
tmpObj = tran.db[playerID] |
242
|
|
|
used.append(self.cmd(tmpObj).getMailboxName(tran, tmpObj)) |
243
|
|
|
# trash unused mailboxes |
244
|
|
|
tran.gameMngr.msgMngr.trashUnusedMailboxes(used) |
245
|
|
|
return obj.galaxies |
246
|
|
|
|
247
|
|
|
def _announceImperatorVoting(self, tran, obj, galaxy): |
248
|
|
|
message = { |
249
|
|
|
"sender": "GNC", |
250
|
|
|
"senderID": galaxy.oid, |
251
|
|
|
"forum": "NEWS", |
252
|
|
|
"data": (galaxy.oid, MSG_GNC_VOTING_COMING, galaxy.oid, obj.turn, Rules.voteForImpAnnounceOffset), |
|
|
|
|
253
|
|
|
"topic": "EVENT", |
254
|
|
|
} |
255
|
|
|
self.cmd(galaxy).sendMsg(tran, galaxy, message) |
256
|
|
|
|
257
|
|
|
def _countVotes(self, tran, obj, galaxy): |
258
|
|
|
VALID_TYPES = [T_PLAYER, T_AIPLAYER] |
|
|
|
|
259
|
|
|
log.debug("Voting for galaxy", galaxy.oid) |
260
|
|
|
# compute votes |
261
|
|
|
votesByName = {} |
262
|
|
|
votesByID = {} |
263
|
|
|
voterNames = {} |
264
|
|
|
for playerID in obj.players: |
265
|
|
|
player = tran.db[playerID] |
266
|
|
|
if galaxy.oid != player.galaxy: |
267
|
|
|
continue |
268
|
|
|
if player.type not in VALID_TYPES: |
269
|
|
|
continue |
270
|
|
|
# add to sum |
271
|
|
|
log.debug(playerID, "votes for", player.voteFor, "with votes", player.stats.slots) |
272
|
|
|
|
273
|
|
|
tmpPlayer = tran.db.get(player.voteFor, None) |
274
|
|
|
if not tmpPlayer or tmpPlayer.type not in VALID_TYPES: |
275
|
|
|
# reset vote |
276
|
|
|
player.voteFor = OID_NONE |
|
|
|
|
277
|
|
|
votedName = None |
278
|
|
|
else: |
279
|
|
|
votedName = tmpPlayer.name |
280
|
|
|
|
281
|
|
|
# count votes |
282
|
|
|
votesByName[votedName] = votesByName.get(votedName, 0) + player.stats.slots |
283
|
|
|
votesByID[player.voteFor] = votesByID.get(player.voteFor, 0) + player.stats.slots |
284
|
|
|
try: |
285
|
|
|
voterNames[votedName].append(player.name) |
286
|
|
|
except KeyError: |
287
|
|
|
voterNames[votedName] = [player.name] |
288
|
|
|
return votesByName, votesByID, voterNames |
289
|
|
|
|
290
|
|
|
def _processElectedImperator(self, tran, obj, galaxy, imperator, votesByName, voterNames): |
291
|
|
|
# 2 imperator, 3+ winner |
292
|
|
|
imperator.imperator = max(2, imperator.imperator + 1) |
293
|
|
|
if galaxy.imperator != OID_NONE and galaxy.imperator != imperator.oid: |
|
|
|
|
294
|
|
|
tran.db[galaxy.imperator].imperator = 0 |
295
|
|
|
galaxy.imperator = imperator.oid |
296
|
|
|
# send message |
297
|
|
|
message = { |
298
|
|
|
"sender": "GNC", |
299
|
|
|
"senderID": galaxy.oid, |
300
|
|
|
"forum": "NEWS", |
301
|
|
|
"data": (galaxy.oid, MSG_GNC_VOTING_IMPERATOR, galaxy.oid, obj.turn, (imperator.name, (votesByName, voterNames))), |
|
|
|
|
302
|
|
|
"topic": "EVENT", |
303
|
|
|
} |
304
|
|
|
self.cmd(galaxy).sendMsg(tran, galaxy, message) |
305
|
|
|
|
306
|
|
|
def _processElectedLeader(self, tran, obj, galaxy, leader, votesByName, voterNames): |
307
|
|
|
leader.imperator = 1 |
308
|
|
|
if galaxy.imperator != OID_NONE and galaxy.imperator != leader.oid: |
|
|
|
|
309
|
|
|
tran.db[galaxy.imperator].imperator = 0 |
310
|
|
|
galaxy.imperator = leader.oid |
311
|
|
|
# send message |
312
|
|
|
message = { |
313
|
|
|
"sender": "GNC", |
314
|
|
|
"senderID": galaxy.oid, |
315
|
|
|
"forum": "NEWS", |
316
|
|
|
"data": (galaxy.oid, MSG_GNC_VOTING_LEADER, galaxy.oid, obj.turn, (leader.name, (votesByName, voterNames))), |
|
|
|
|
317
|
|
|
"topic": "EVENT", |
318
|
|
|
} |
319
|
|
|
self.cmd(galaxy).sendMsg(tran, galaxy, message) |
320
|
|
|
|
321
|
|
|
def _processNoWinner(self, tran, obj, galaxy, votesByName, voterNames): |
322
|
|
|
# nobody wins |
323
|
|
|
if galaxy.imperator != OID_NONE: |
|
|
|
|
324
|
|
|
tran.db[galaxy.imperator].imperator = 0 |
325
|
|
|
galaxy.imperator = OID_NONE |
326
|
|
|
message = { |
327
|
|
|
"sender": "GNC", |
328
|
|
|
"senderID": galaxy.oid, |
329
|
|
|
"forum": "NEWS", |
330
|
|
|
"data": (galaxy.oid, MSG_GNC_VOTING_NOWINNER, galaxy.oid, obj.turn, ((votesByName, voterNames),)), |
|
|
|
|
331
|
|
|
"topic": "EVENT", |
332
|
|
|
} |
333
|
|
|
self.cmd(galaxy).sendMsg(tran, galaxy, message) |
334
|
|
|
|
335
|
|
|
def _processImperatorVoting(self, tran, obj, galaxy): |
336
|
|
|
votesByName, votesByID, voterNames = self._countVotes(tran, obj, galaxy) |
337
|
|
|
# check winner |
338
|
|
|
totalVotes = sum(votesByID.values()) |
339
|
|
|
nominated = sorted(votesByID, key=lambda a: votesByID[a], reverse = True) |
340
|
|
|
winnerID = OID_NONE |
|
|
|
|
341
|
|
|
# OID_NONE is not valid target |
342
|
|
|
if OID_NONE in nominated: |
343
|
|
|
nominated.remove(OID_NONE) |
344
|
|
|
# check winner |
345
|
|
|
try: |
346
|
|
|
winnerID = nominated[0] |
347
|
|
|
winner = tran.db[winnerID] |
348
|
|
|
if float(votesByID[winnerID]) / totalVotes >= Rules.ratioNeededForImp: |
349
|
|
|
self._processElectedImperator(tran, obj, galaxy, winner, votesByName, voterNames) |
350
|
|
|
elif len(nominated) > 1 and votesByID[winnerID] == votesByID[nominated[1]]: |
351
|
|
|
# oh no, more than one winner |
352
|
|
|
self._processNoWinner(tran, obj, galaxy, votesByName, voterNames) |
353
|
|
|
else: |
354
|
|
|
self._processElectedLeader(tran, obj, galaxy, winner, votesByName, voterNames) |
355
|
|
|
except IndexError: |
356
|
|
|
# no nominations? |
357
|
|
|
self._processNoWinner(tran, obj, galaxy, votesByName, voterNames) |
358
|
|
|
|
359
|
|
|
def _autoFinishOuterspace(self, tran, obj, galaxy): |
360
|
|
|
if tran.gameMngr.config.server.mode != "normal": |
361
|
|
|
# check autoend conditions, but only in normal mode |
362
|
|
|
# development mode does not end galaxies |
363
|
|
|
return |
364
|
|
|
for playerID in obj.players: |
365
|
|
|
player = tran.db[playerID] |
366
|
|
|
if galaxy.oid != player.galaxy: |
367
|
|
|
continue |
368
|
|
|
if player.type == T_PIRPLAYER: |
|
|
|
|
369
|
|
|
piratePlayer = True |
370
|
|
|
activePlayerCount += 1 |
|
|
|
|
371
|
|
|
continue |
372
|
|
|
if player.type != T_PLAYER: |
|
|
|
|
373
|
|
|
continue |
374
|
|
|
selfName = player.name |
375
|
|
|
activePlayerCount += 1 |
376
|
|
|
|
377
|
|
|
if activePlayerCount <= 1: |
378
|
|
|
log.message("AUTO FINISHING GALAXY", galaxy.oid) |
379
|
|
|
if activePlayerCount == 0: |
380
|
|
|
self.finishGalaxyAutomated(tran, obj, galaxy.oid, ["The galaxy was ended with no active players."]) |
381
|
|
|
elif piratePlayer: #if the pirate is still alive, then he must be the winner. |
|
|
|
|
382
|
|
|
self.finishGalaxyAutomated(tran, obj, galaxy.oid, ["The galaxy was automatically ended with the Pirate as a winner!"]) |
383
|
|
|
elif selfName: #if there is only one player, selfName must be themselves if it isn't null |
|
|
|
|
384
|
|
|
self.finishGalaxyAutomated(tran, obj, galaxy.oid, ["The galaxy was automatically ended with commander %s as the only remaining player." % selfName]) |
385
|
|
|
|
386
|
|
|
def processScenarioOuterspace(self, tran, obj, galaxy): |
387
|
|
|
if (galaxy.galaxyTurn + Rules.voteForImpAnnounceOffset) % Rules.voteForImpPeriod == 0: |
388
|
|
|
self._announceImperatorVoting(tran, obj, galaxy) |
389
|
|
|
if galaxy.galaxyTurn % Rules.voteForImpPeriod == 0: |
390
|
|
|
# voting |
391
|
|
|
self._processImperatorVoting(tran, obj, galaxy) |
392
|
|
|
self._autoFinishOuterspace(tran, obj, galaxy) |
393
|
|
|
|
394
|
|
|
def processScenarioSingle(self, tran, obj, galaxy): |
395
|
|
|
""" If owner of the galaxy is not present anymore, remove it. |
396
|
|
|
There are no winning conditions right now. |
397
|
|
|
""" |
398
|
|
|
try: |
399
|
|
|
player = tran.db[galaxy.owner] |
400
|
|
|
if galaxy.oid == player.galaxy: |
401
|
|
|
# all is well |
402
|
|
|
return True |
403
|
|
|
# new player has nothing to do with this galaxy |
404
|
|
|
except NoSuchObjectException: |
405
|
|
|
# there is no object with owner OID |
406
|
|
|
pass |
407
|
|
|
except AttributeError: |
408
|
|
|
# there is object with owner OID, but it's not player object |
409
|
|
|
pass |
410
|
|
|
# let's clean it |
411
|
|
|
if tran.gameMngr.config.server.mode == "normal": |
412
|
|
|
self.cmd(galaxy).delete(tran, galaxy) |
413
|
|
|
|
414
|
|
|
def processScenarioCoop(self, tran, obj, galaxy): |
415
|
|
|
ENEMIES = [T_AIEDENPLAYER, T_AIMUTPLAYER, T_AIPIRPLAYER] |
|
|
|
|
416
|
|
|
clear = True |
417
|
|
|
players = [] |
418
|
|
|
for playerID in obj.players: |
419
|
|
|
player = tran.db[playerID] |
420
|
|
|
if galaxy.oid != player.galaxy: |
421
|
|
|
continue |
422
|
|
|
if player.type in ENEMIES: |
423
|
|
|
clear = False |
424
|
|
|
continue |
425
|
|
|
if player.type == T_AIPLAYER: |
|
|
|
|
426
|
|
|
players.append(player) |
427
|
|
|
if player.type != T_PLAYER: |
|
|
|
|
428
|
|
|
# skip non-regular players |
429
|
|
|
continue |
430
|
|
|
players.append(player) |
431
|
|
|
if not len(players): |
432
|
|
|
# no player left? what a strange state! Let's delete it quick! |
433
|
|
|
self.cmd(galaxy).delete(tran, galaxy) |
434
|
|
|
return False |
435
|
|
|
if not clear: |
436
|
|
|
# struggle is ongoing |
437
|
|
|
return True |
438
|
|
|
# no enemies left, let's celebrate by sending a message, and finish the galaxy |
439
|
|
|
victors = map(lambda x: x.name, players) |
440
|
|
|
message = { |
441
|
|
|
"sender": "Galaxy %s" % galaxy.name, |
442
|
|
|
"senderID": tran.cid, |
443
|
|
|
"forum": "NEWS", |
444
|
|
|
"data": (galaxy.oid, MSG_GNC_GALAXY_COOP_WON, galaxy.oid, obj.turn, (galaxy.name, victors)), |
|
|
|
|
445
|
|
|
"topic": "EVENT", |
446
|
|
|
} |
447
|
|
|
self.cmd(obj).sendMsg(tran, obj, message) |
448
|
|
|
self.cmd(galaxy).delete(tran, galaxy) |
449
|
|
|
|
450
|
|
|
def processScenarioBrawl(self, tran, obj, galaxy): |
451
|
|
|
players = [] |
452
|
|
|
for playerID in obj.players: |
453
|
|
|
player = tran.db[playerID] |
454
|
|
|
if galaxy.oid != player.galaxy: |
455
|
|
|
continue |
456
|
|
|
if player.type != T_PLAYER: |
|
|
|
|
457
|
|
|
# skip non-regular players |
458
|
|
|
continue |
459
|
|
|
players.append(player) |
460
|
|
|
if len(players) > 1: |
461
|
|
|
# fight continues |
462
|
|
|
return True |
463
|
|
|
if not len(players): |
464
|
|
|
# no player left? what a strange state! Let's delete it quick! |
465
|
|
|
self.cmd(galaxy).delete(tran, galaxy) |
466
|
|
|
return False |
467
|
|
|
# last man standing! Let's send announcement, and change the galaxy |
468
|
|
|
# to SCENARIO_SINGLE for winner to enjoy it (and pause / finish at will) |
469
|
|
|
winner = players[0] |
470
|
|
|
message = { |
471
|
|
|
"sender": "Galaxy %s" % galaxy.name, |
472
|
|
|
"senderID": tran.cid, |
473
|
|
|
"forum": "NEWS", |
474
|
|
|
"data": (galaxy.oid, MSG_GNC_GALAXY_BRAWL_WON, galaxy.oid, obj.turn, (galaxy.name, winner.name)), |
|
|
|
|
475
|
|
|
"topic": "EVENT", |
476
|
|
|
} |
477
|
|
|
self.cmd(obj).sendMsg(tran, obj, message) |
478
|
|
|
galaxy.scenario = SCENARIO_SINGLE |
|
|
|
|
479
|
|
|
galaxy.name += " won on {0}".format(obj.turn) |
480
|
|
|
galaxy.owner = winner.oid |
481
|
|
|
return False |
482
|
|
|
|
483
|
|
|
def update(self, tran, obj): |
484
|
|
|
# check existence of all galaxies |
485
|
|
|
log.debug('Game turn is',obj.turn) |
486
|
|
|
if 0: |
487
|
|
|
for galaxyID in obj.galaxies: |
488
|
|
|
if not tran.db.has_key(galaxyID): |
489
|
|
|
log.debug("CONSISTENCY - galaxy %d from universe %d does not exists" % (galaxyID, obj.oid)) |
490
|
|
|
elif tran.db[galaxyID].type != T_GALAXY: |
491
|
|
|
log.debug("CONSISTENCY - galaxy %d from universe %d is not a T_GALAXY" % (galaxyID, obj.oid)) |
492
|
|
|
# check existence of all players |
493
|
|
|
for playerID in obj.players[:]: |
494
|
|
|
if not tran.db.has_key(playerID): |
495
|
|
|
log.debug("CONSISTENCY - player %d from universe %d does not exists" % (playerID, obj.oid)) |
496
|
|
|
log.debug("Removing reference to player", playerID) |
497
|
|
|
obj.players.remove(playerID) |
498
|
|
|
elif tran.db[playerID].type not in PLAYER_TYPES: |
|
|
|
|
499
|
|
|
log.debug("CONSISTENCY - player %d from universe %d is not a %s, it's %d" % (playerID, obj.oid, str(PLAYER_TYPES), tran.db[playerID].type)) |
500
|
|
|
log.debug("Removing reference to player", playerID) |
501
|
|
|
obj.players.remove(playerID) |
502
|
|
|
|
503
|
|
|
update.public = 0 |
504
|
|
|
|
505
|
|
|
def getReferences(self, tran, obj): |
506
|
|
|
return obj.players[:] + obj.galaxies[:] + [OID_NATURE] |
|
|
|
|
507
|
|
|
|
508
|
|
|
getReferences.public = 0 |
509
|
|
|
|
510
|
|
|
@public(AL_ADMIN) |
511
|
|
|
def getActivePlayers(self, tran, obj): |
512
|
|
|
playerNames = [] |
513
|
|
|
for playerID in obj.players: |
514
|
|
|
player = tran.db[playerID] |
515
|
|
|
if not player.type in AI_PLAYER_TYPES: |
|
|
|
|
516
|
|
|
playerNames.append(player.name) |
517
|
|
|
return playerNames |
518
|
|
|
|
519
|
|
|
@public(AL_NONE) |
520
|
|
|
def getPublicInfo(self, tran, obj): |
521
|
|
|
result = IDataHolder() |
522
|
|
|
result.oid = obj.oid |
523
|
|
|
result.type = obj.type |
524
|
|
|
result.name = obj.name |
525
|
|
|
result.turn = obj.turn |
526
|
|
|
return result |
527
|
|
|
|
528
|
|
|
## messaging |
529
|
|
|
def canGetMsgs(self, tran, obj, oid): |
530
|
|
|
return 1 |
531
|
|
|
|
532
|
|
|
canGetMsgs.public = 0 |
533
|
|
|
|
534
|
|
|
def canSendMsg(self, tran, obj, oid, forum): |
535
|
|
|
if forum.endswith("PUBLIC"): |
536
|
|
|
return 1 |
537
|
|
|
elif forum.endswith("NEWS"): |
538
|
|
|
return 1 |
539
|
|
|
return 0 |
540
|
|
|
|
541
|
|
|
canSendMsg.public = 0 |
542
|
|
|
|
543
|
|
|
@public(AL_NONE) |
544
|
|
|
def finishGalaxyImperator(self, tran, obj, galaxyID, imperatorMessage): |
545
|
|
|
log.debug("Finishing Galaxy", galaxyID) |
546
|
|
|
galaxy = tran.db[galaxyID] |
547
|
|
|
if galaxy.scenario == SCENARIO_OUTERSPACE: |
|
|
|
|
548
|
|
|
if galaxy.imperator == 0 or galaxy.imperator != tran.cid: |
549
|
|
|
raise GameException('Only galaxy imperator can finish galaxy') |
550
|
|
|
|
551
|
|
|
imperator = tran.db[tran.cid] |
552
|
|
|
if imperator.imperator < 3: |
553
|
|
|
raise GameException('Only imperator elected three times and more can finish galaxy') |
554
|
|
|
|
555
|
|
|
log.debug("Sending message", imperatorMessage) |
556
|
|
|
message = { |
557
|
|
|
"sender": imperator.name, |
558
|
|
|
"senderID": tran.cid, |
559
|
|
|
"forum": "NEWS", |
560
|
|
|
"data": (galaxyID, MSG_GNC_GALAXY_FINISHED, galaxyID, obj.turn, (imperator.name, galaxy.name, imperatorMessage)), |
|
|
|
|
561
|
|
|
"topic": "EVENT", |
562
|
|
|
} |
563
|
|
|
self.cmd(obj).sendMsg(tran, obj, message) |
564
|
|
|
else: |
565
|
|
|
raise GameException('Galaxy finish not permitted.') |
566
|
|
|
|
567
|
|
|
log.debug("Deleting galaxy", galaxyID) |
568
|
|
|
self.cmd(galaxy).delete(tran, galaxy) |
569
|
|
|
|
570
|
|
|
@public(AL_ADMIN) |
571
|
|
|
def finishGalaxyAutomated(self, tran, obj, galaxyID, imperatorMessage): #server-initiated restart |
572
|
|
|
log.debug("Restarting Galaxy", galaxyID) |
573
|
|
|
log.debug("Sending message", imperatorMessage) |
574
|
|
|
galaxy = tran.db[galaxyID] |
575
|
|
|
message = { |
576
|
|
|
"sender": "Galaxy %s" % galaxy.name, |
577
|
|
|
"senderID": tran.cid, |
578
|
|
|
"forum": "NEWS", |
579
|
|
|
"data": (galaxyID, MSG_GNC_GALAXY_AUTO_FINISHED, galaxyID, obj.turn, (galaxy.name, imperatorMessage)), |
|
|
|
|
580
|
|
|
"topic": "EVENT", |
581
|
|
|
} |
582
|
|
|
self.cmd(obj).sendMsg(tran, obj, message) |
583
|
|
|
log.debug("Deleting galaxy", galaxyID) |
584
|
|
|
self.cmd(galaxy).delete(tran, galaxy) |
585
|
|
|
|
586
|
|
|
def _sendCreationMessage(self, tran, obj, galaxy): |
587
|
|
|
message = { |
588
|
|
|
"sender": "GNC", |
589
|
|
|
"senderID": galaxy.oid, |
590
|
|
|
"forum": "NEWS", |
591
|
|
|
"data": (galaxy.oid, MSG_GNC_GALAXY_CREATED, galaxy.oid, obj.turn, (obj.turn)), |
|
|
|
|
592
|
|
|
"topic": "EVENT", |
593
|
|
|
} |
594
|
|
|
self.cmd(galaxy).sendMsg(tran, galaxy, message) |
595
|
|
|
|
596
|
|
|
@public(AL_ADMIN) |
597
|
|
|
def createNewSubscribedGalaxy(self, tran, obj, galaxyName, galaxyType, listOfPlayers): |
598
|
|
|
galGen = GalaxyGenerator.GalaxyGenerator() |
599
|
|
|
galaxyRadius = galGen.getGalaxyTemplate(galaxyType).radius |
600
|
|
|
posX, posY = self.cmd(obj).findSpotForGalaxy(tran, obj, galaxyRadius) |
601
|
|
|
log.message("Adding new galaxy '%s' to (%d, %d)" % (galaxyType, posX, posY)) |
602
|
|
|
galaxyFileName = galGen.generateGalaxy(galaxyType) |
603
|
|
|
log.debug("Creating new galaxy") |
604
|
|
|
newGalaxyID = self.createGalaxy(tran, obj) |
605
|
|
|
log.debug("Created new galaxy", newGalaxyID) |
606
|
|
|
newGalaxy = tran.db[newGalaxyID] |
607
|
|
|
log.debug("Loading new ", newGalaxyID) |
608
|
|
|
self.cmd(newGalaxy).loadFromXML(tran, newGalaxy, galaxyFileName, galaxyType, posX, posY, galaxyName) |
609
|
|
|
|
610
|
|
|
log.debug("Setup Enviroment", newGalaxyID) |
611
|
|
|
self.cmd(newGalaxy).setupEnvironment(tran, newGalaxy) |
612
|
|
|
log.debug("Sending Announcement Message", newGalaxyID) |
613
|
|
|
#self.cmd(newGalaxy).announceGalaxy(tran,newGalaxy) |
614
|
|
|
log.debug("Removing temp file", galaxyFileName) |
615
|
|
|
os.remove(galaxyFileName) |
616
|
|
|
for playerLogin in listOfPlayers: |
617
|
|
|
tran.gameMngr.createNewSubscribedPlayer(playerLogin, newGalaxyID) |
618
|
|
|
if newGalaxy.scenario != SCENARIO_SINGLE: |
|
|
|
|
619
|
|
|
# no point in announcing single scenario - it starts ticking right away |
620
|
|
|
self._sendCreationMessage(tran, obj, newGalaxy) |
621
|
|
|
log.debug("Galaxy creation END") |
622
|
|
|
return newGalaxyID |
623
|
|
|
|
624
|
|
|
@public(AL_ADMIN) |
625
|
|
|
def deleteGalaxy(self, tran, galaxyID): |
626
|
|
|
galaxy = tran.db[galaxyID] |
627
|
|
|
log.debug("Deleting galaxy", galaxyID) |
628
|
|
|
self.cmd(galaxy).delete(tran, galaxy) |
629
|
|
|
|
630
|
|
|
@public(AL_ADMIN) |
631
|
|
|
def findSpotForGalaxy(self, tran, obj, new_gal_radius): |
632
|
|
|
""" We start with sum of surfaces of active galaxies (with borders) with this, |
633
|
|
|
we count the hypothetical square all galaxies should fit together. We then |
634
|
|
|
increase the size a bit, and try to place the new galaxy there randomly. |
635
|
|
|
If not successful, increase again and repeat. |
636
|
|
|
""" |
637
|
|
|
log.debug("Seeking position for new galaxy") |
638
|
|
|
|
639
|
|
|
attempts_amount = 10000 # number of placement attempts within one resize |
640
|
|
|
magic_constant = 1.1 |
641
|
|
|
magic_constant_step = 0.1 |
642
|
|
|
border = 50 |
643
|
|
|
|
644
|
|
|
# count surface required |
645
|
|
|
whole_surface = 0 |
646
|
|
|
for galaxy_id in obj.galaxies: |
647
|
|
|
galaxy = tran.db[galaxy_id] |
648
|
|
|
# border is counted only once as it can overlap |
649
|
|
|
whole_surface += (galaxy.radius * 2 + border) ** 2 |
650
|
|
|
# adding whole_surface of the galaxy currently processed |
651
|
|
|
whole_surface += (new_gal_radius * 2 + border) ** 2 |
652
|
|
|
|
653
|
|
|
search = True |
654
|
|
|
attempts = attempts_amount |
655
|
|
|
low_limit = new_gal_radius # only positive coordinates |
656
|
|
|
while search: |
657
|
|
|
high_limit = math.ceil(whole_surface ** 0.5 * magic_constant) |
658
|
|
|
attempts -= 1 |
659
|
|
|
pos_x = int(random.randrange(low_limit, high_limit)) |
660
|
|
|
pos_y = int(random.randrange(low_limit, high_limit)) |
661
|
|
|
is_blocked = False |
662
|
|
|
for galaxy_id in obj.galaxies: |
663
|
|
|
galaxy = tran.db[galaxy_id] |
664
|
|
|
needed_space = galaxy.radius + border + new_gal_radius |
665
|
|
|
if math.hypot(galaxy.x - pos_x, galaxy.y - pos_y) < needed_space: |
666
|
|
|
is_blocked = True |
667
|
|
|
break |
668
|
|
|
search = is_blocked |
669
|
|
|
if not attempts: |
670
|
|
|
magic_constant += magic_constant_step |
671
|
|
|
attempts = attempts_amount |
672
|
|
|
return (pos_x, pos_y) |
|
|
|
|
673
|
|
|
|