|
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
|
|
|
|