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