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 re |
23
|
|
|
import time |
24
|
|
|
|
25
|
|
|
import ige |
26
|
|
|
import Rules |
27
|
|
|
import Utils |
28
|
|
|
import ShipUtils |
29
|
|
|
|
30
|
|
|
from ige import log |
31
|
|
|
from ige.IObject import IObject, public |
32
|
|
|
from ige.IDataHolder import IDataHolder |
33
|
|
|
import Const |
34
|
|
|
|
35
|
|
|
class IPlayer(IObject): |
36
|
|
|
|
37
|
|
|
typeID = Const.T_PLAYER |
38
|
|
|
resignTo = Const.T_AIPLAYER |
39
|
|
|
forums = {"INBOX": 56, "OUTBOX": 56, "EVENTS": 4} |
40
|
|
|
|
41
|
|
|
def init(self, obj): |
42
|
|
|
IObject.init(self, obj) |
43
|
|
|
# |
44
|
|
|
obj.login = u'' |
45
|
|
|
obj.name = u'' |
46
|
|
|
obj.fullName = u'' |
47
|
|
|
# |
48
|
|
|
obj.buoys = {} |
49
|
|
|
obj.alliedBuoys = {} |
50
|
|
|
obj.planets = [] |
51
|
|
|
obj.fleets = [] |
52
|
|
|
obj.techs = {} # techs and their sublevel |
53
|
|
|
obj.obsoleteTechs = set() |
54
|
|
|
obj.rsrchQueue = [] |
55
|
|
|
obj.sciPoints = 0 |
56
|
|
|
obj.effSciPoints = 0 |
57
|
|
|
obj.techLevel = 1 |
58
|
|
|
obj.shipDesigns = {} |
59
|
|
|
obj.race = "H" # race Bionic, Human, Cyborg |
60
|
|
|
# bonuses |
61
|
|
|
obj.prodEff = 1.0 |
62
|
|
|
obj.sciEff = 1.0 |
63
|
|
|
# |
64
|
|
|
obj.govPwr = 0 |
65
|
|
|
obj.govPwrCtrlRange = 1 |
66
|
|
|
# fleet support |
67
|
|
|
obj.fleetUpgradePool = 0.0 |
68
|
|
|
obj.fleetUpgradeInProgress = 0 |
69
|
|
|
# production |
70
|
|
|
obj.prodQueues = [[],[],[],[],[]] |
71
|
|
|
obj.prodIncreasePool = 0.0 |
72
|
|
|
# diplomacy |
73
|
|
|
obj.diplomacyRels = {} |
74
|
|
|
obj.defaultRelation = Rules.defaultRelation |
75
|
|
|
obj.voteFor = Const.OID_NONE |
76
|
|
|
obj.governorOf = Const.OID_NONE |
77
|
|
|
obj.governors = [] |
78
|
|
|
# obj.alliance = Const.OID_NONE # not utilized, but can be in DB somewhere |
79
|
|
|
obj.imperator = 0 |
80
|
|
|
# combat |
81
|
|
|
# anti-small, anti-medium, anti-large, shield generator |
82
|
|
|
obj.planetWeapons = [None, None, None, None, None] |
83
|
|
|
# |
84
|
|
|
obj.staticMap = {} |
85
|
|
|
obj.dynamicMap = {} |
86
|
|
|
obj.galaxy = None |
87
|
|
|
obj.validSystems = [] |
88
|
|
|
# |
89
|
|
|
obj.stats = IDataHolder() |
90
|
|
|
obj.stats.type = Const.T_STATS |
91
|
|
|
obj.timeEnabled = 0 |
92
|
|
|
obj.stratRes = {} |
93
|
|
|
obj.lastLogin = 0.0 |
94
|
|
|
# |
95
|
|
|
obj.shipRedirections = {} |
96
|
|
|
obj.buoys = {} |
97
|
|
|
# |
98
|
|
|
obj.clientStats = {} |
99
|
|
|
|
100
|
|
|
def update(self, tran, obj): |
101
|
|
|
# refresh technologies |
102
|
|
|
self.setStartingTechnologies(obj) |
103
|
|
|
# clean up obsoleted technologies |
104
|
|
|
obsoleted = set(obj.techs.keys()).difference(Rules.techs.keys()) |
105
|
|
|
for techID in obsoleted: |
106
|
|
|
log.debug("Deleting obsoleted technology", obj.oid, techID) |
107
|
|
|
del obj.techs[techID] |
108
|
|
|
# update all designs |
109
|
|
|
for designID in obj.shipDesigns: |
110
|
|
|
old = obj.shipDesigns[designID] |
111
|
|
|
new = ShipUtils.makeShipMinSpec(obj, old.name, old.hullID, |
112
|
|
|
old.eqIDs, old.improvements, raiseExs = False) |
113
|
|
|
new.built = old.built |
114
|
|
|
if hasattr(old, "upgradeTo"): |
115
|
|
|
new.upgradeTo = old.upgradeTo |
116
|
|
|
obj.shipDesigns[designID] = new |
117
|
|
|
# check all diplomacyRels |
118
|
|
|
for partyID in obj.diplomacyRels.keys(): |
119
|
|
|
party = tran.db.get(partyID, None) |
120
|
|
|
if not party or party.type not in Const.PLAYER_TYPES: |
121
|
|
|
log.debug("Deleting party", obj.oid, partyID) |
122
|
|
|
del obj.diplomacyRels[partyID] |
123
|
|
|
# delete obj with low scan pwr |
124
|
|
|
# check type of the objects in the map |
125
|
|
|
for objID in obj.staticMap.keys(): |
126
|
|
|
obj.staticMap[objID] = min(obj.staticMap[objID], Rules.maxScanPwr) |
127
|
|
|
if obj.staticMap[objID] < Rules.level1InfoScanPwr: |
128
|
|
|
del obj.staticMap[objID] |
129
|
|
|
if objID not in tran.db or tran.db[objID].type not in (Const.T_SYSTEM, Const.T_WORMHOLE): |
130
|
|
|
log.debug("Deleting non system %d from static map of player %d" % (objID, obj.oid)) |
131
|
|
|
del obj.staticMap[objID] |
132
|
|
|
for objID in obj.dynamicMap.keys(): |
133
|
|
|
if obj.dynamicMap[objID] < Rules.level1InfoScanPwr: |
134
|
|
|
del obj.dynamicMap[objID] |
135
|
|
|
if objID not in tran.db or tran.db[objID].type != Const.T_FLEET: |
136
|
|
|
log.debug("Deleting obj %d from dynamic map of player %d" % (objID, objID)) |
137
|
|
|
del obj.dynamicMap[objID] |
138
|
|
|
# check if all planets are planets |
139
|
|
|
for objID in obj.planets[:]: |
140
|
|
|
try: |
141
|
|
|
if objID not in tran.db: |
142
|
|
|
log.debug("Planet does not exists - removing", obj.oid, objID) |
143
|
|
|
obj.planets.remove(objID) |
144
|
|
|
if tran.db[objID].type != Const.T_PLANET: |
145
|
|
|
log.debug("Planet is not a planet - removing", obj.oid, objID) |
146
|
|
|
obj.planets.remove(objID) |
147
|
|
|
except: |
148
|
|
|
log.warning("There is a problem when processing planet - removing", obj.oid, objID) |
149
|
|
|
obj.planets.remove(objID) |
150
|
|
|
# check if systems in buoys are systems |
151
|
|
|
for objID in obj.buoys.keys(): |
152
|
|
|
try: |
153
|
|
|
if objID not in tran.db: |
154
|
|
|
log.debug("System for buoy does not exists - removing", obj.oid, objID) |
155
|
|
|
del obj.buoys[objID] |
156
|
|
|
if tran.db[objID].type not in (Const.T_SYSTEM, Const.T_WORMHOLE): |
157
|
|
|
log.debug("System for buoy is not a system - removing", obj.oid, objID) |
158
|
|
|
del obj.buoys[objID] |
159
|
|
|
except: |
160
|
|
|
log.warning("There is a problem when processing system for buoy - removing", obj.oid, objID) |
161
|
|
|
del obj.buoys[objID] |
162
|
|
|
# check if fleets are fleets |
163
|
|
|
for objID in obj.fleets[:]: |
164
|
|
|
try: |
165
|
|
|
if objID not in tran.db: |
166
|
|
|
log.debug("Fleet does not exists - removing", obj.oid, objID) |
167
|
|
|
obj.fleets.remove(objID) |
168
|
|
|
if tran.db[objID].type != Const.T_FLEET: |
169
|
|
|
log.debug("Fleet is not a fleet - removing", obj.oid, objID) |
170
|
|
|
obj.fleets.remove(objID) |
171
|
|
|
except: |
172
|
|
|
log.warning("There is a problem when processing planet - removing", obj.oid, objID) |
173
|
|
|
# check accessible technologies |
174
|
|
|
wip = 1 |
175
|
|
|
while wip: |
176
|
|
|
wip = 0 |
177
|
|
|
for techID in obj.techs.keys(): |
178
|
|
|
if techID not in Rules.techs: |
179
|
|
|
wip = 1 |
180
|
|
|
log.debug("Deleting nonexistent tech", techID, "player", obj.oid) |
181
|
|
|
del obj.techs[techID] |
182
|
|
|
continue |
183
|
|
|
tech = Rules.techs[techID] |
184
|
|
|
# check tech level |
185
|
|
|
if tech.level > obj.techLevel: |
186
|
|
|
wip = 1 |
187
|
|
|
log.debug("Deleting tech", techID, "player", obj.oid) |
188
|
|
|
if techID in obj.techs: del obj.techs[techID] |
189
|
|
|
for rTask in obj.rsrchQueue[:]: |
190
|
|
|
if rTask.techID not in Rules.techs: |
191
|
|
|
log.debug("Deleting res task for nonexistent tech", rTask.techID, "player", obj.oid) |
192
|
|
|
obj.rsrchQueue.remove(rTask) |
193
|
|
|
continue |
194
|
|
|
tech = Rules.techs[rTask.techID] |
195
|
|
|
if tech.level == 99: |
196
|
|
|
log.debug("Deleting res task", rTask.techID, "player", obj.oid) |
197
|
|
|
obj.rsrchQueue.remove(rTask) |
198
|
|
|
# check if player is in the universe |
199
|
|
|
universe = tran.db[Const.OID_UNIVERSE] |
200
|
|
|
if obj.oid not in universe.players and obj.oid not in (Const.OID_NATURE, Const.OID_ADMIN): |
201
|
|
|
log.debug(obj.oid, "Adding player to the universe") |
202
|
|
|
universe.players.append(obj.oid) |
203
|
|
|
# check if player is a leader |
204
|
|
|
if not obj.galaxy: |
205
|
|
|
log.debug(obj.oid, obj.name, "IS NOT IN ANY GALAXY") |
206
|
|
|
else: |
207
|
|
|
galaxy = tran.db[obj.galaxy] |
208
|
|
|
if galaxy.imperator != obj.oid and obj.imperator > 0: |
209
|
|
|
log.debug(obj.oid, "Removing imperator/leader bonus") |
210
|
|
|
obj.imperator = 0 |
211
|
|
|
## NON VALIDATING CODE (DERIVED ATTRS AND SO ON) |
212
|
|
|
# get best technologies for planet weapons |
213
|
|
|
bestScores = [0, 0, 0, 0] |
214
|
|
|
obj.planetWeapons = [None, None, None, None, None] |
215
|
|
|
for techID in obj.techs: |
216
|
|
|
tech = Rules.techs[techID] |
217
|
|
|
if tech.isShipEquip and tech.weaponDmgMin > 0 and not tech.buildSRes\ |
218
|
|
|
and tech.weaponGoodForFlak: |
219
|
|
|
# compute score |
220
|
|
|
weaponEff = Rules.techImprEff[obj.techs.get(techID, Rules.techBaseImprovement)] |
221
|
|
|
score = (tech.weaponDmgMin + tech.weaponDmgMax) / 2.0 * \ |
222
|
|
|
tech.weaponROF * (tech.weaponAtt + 10.0)/10 * weaponEff |
223
|
|
|
if score > bestScores[tech.weaponClass]: |
224
|
|
|
obj.planetWeapons[tech.weaponClass] = techID |
225
|
|
|
bestScores[tech.weaponClass] = score |
226
|
|
|
#@log.debug(obj.oid, "Planet weapons", obj.planetWeapons) |
227
|
|
|
# update all ship designs |
228
|
|
|
for designID in obj.shipDesigns: |
229
|
|
|
old = obj.shipDesigns[designID] |
230
|
|
|
new = ShipUtils.makeShipMinSpec(obj, old.name, old.hullID, |
231
|
|
|
old.eqIDs, old.improvements, raiseExs = False) |
232
|
|
|
new.built = old.built |
233
|
|
|
new.upgradeTo = old.upgradeTo |
234
|
|
|
obj.shipDesigns[designID] = new |
235
|
|
|
|
236
|
|
|
@staticmethod |
237
|
|
|
def setStartingPlanet(tran, playerID, planet): |
238
|
|
|
planet.plSlots = max(planet.plSlots, 9) |
239
|
|
|
planet.plMaxSlots = max(planet.plMaxSlots, 9) |
240
|
|
|
planet.plDiameter = max(planet.plDiameter, 9000) |
241
|
|
|
planet.slots = [ |
242
|
|
|
Utils.newStructure(tran, Rules.Tech.PWRPLANTNUK1, playerID, Const.STRUCT_STATUS_ON, Rules.structNewPlayerHpRatio), |
243
|
|
|
Utils.newStructure(tran, Rules.Tech.FARM1, playerID, Const.STRUCT_STATUS_ON, Rules.structNewPlayerHpRatio), |
244
|
|
|
Utils.newStructure(tran, Rules.Tech.FARM1, playerID, Const.STRUCT_STATUS_ON, Rules.structNewPlayerHpRatio), |
245
|
|
|
Utils.newStructure(tran, Rules.Tech.ANCFACTORY, playerID, Const.STRUCT_STATUS_ON, Rules.structNewPlayerHpRatio), |
246
|
|
|
Utils.newStructure(tran, Rules.Tech.ANCFACTORY, playerID, Const.STRUCT_STATUS_ON, Rules.structNewPlayerHpRatio), |
247
|
|
|
Utils.newStructure(tran, Rules.Tech.ANCRESLAB, playerID, Const.STRUCT_STATUS_ON, Rules.structNewPlayerHpRatio), |
248
|
|
|
Utils.newStructure(tran, Rules.Tech.REPAIR1, playerID, Const.STRUCT_STATUS_ON, Rules.structNewPlayerHpRatio), |
249
|
|
|
] |
250
|
|
|
planet.storPop = Rules.startingPopulation |
251
|
|
|
planet.storBio = Rules.startingBio |
252
|
|
|
planet.storEn = Rules.startingEn |
253
|
|
|
planet.scannerPwr = Rules.startingScannerPwr |
254
|
|
|
planet.morale = Rules.maxMorale |
255
|
|
|
|
256
|
|
|
@staticmethod |
257
|
|
|
def setStartingTechnologies(obj): |
258
|
|
|
for techID, tech in Rules.techs.iteritems(): |
259
|
|
|
if tech.isStarting and techID not in obj.techs: |
260
|
|
|
obj.techs[techID] = (Rules.techBaseImprovement + tech.maxImprovement) / 2 |
261
|
|
|
|
262
|
|
|
@staticmethod |
263
|
|
|
def setStartingShipDesigns(obj): |
264
|
|
|
obj.shipDesigns[1] = ShipUtils.makeShipMinSpec(obj, 'Scout', Rules.Tech.SMALLHULL0, |
265
|
|
|
{Rules.Tech.SCOCKPIT0:1, Rules.Tech.SCANNERMOD0:1, Rules.Tech.FTLENG0:3}, []) |
266
|
|
|
obj.shipDesigns[2] = ShipUtils.makeShipMinSpec(obj, 'Fighter', Rules.Tech.SMALLHULL0, |
267
|
|
|
{Rules.Tech.SCOCKPIT0:1, Rules.Tech.CANNON0:2, Rules.Tech.FTLENG0:3}, []) |
268
|
|
|
obj.shipDesigns[3] = ShipUtils.makeShipMinSpec(obj, 'Bomber', Rules.Tech.SMALLHULL0, |
269
|
|
|
{Rules.Tech.SCOCKPIT0:1, Rules.Tech.CONBOMB0:1, Rules.Tech.FTLENG0:3}, []) |
270
|
|
|
obj.shipDesigns[4] = ShipUtils.makeShipMinSpec(obj, 'Patrol Corvette', Rules.Tech.SMALLHULL0, |
271
|
|
|
{Rules.Tech.SCOCKPIT0:1, Rules.Tech.CANNON0:3, Rules.Tech.FTLENG0:1, Rules.Tech.STLENG1:2}, []) |
272
|
|
|
obj.shipDesigns[5] = ShipUtils.makeShipMinSpec(obj, 'Colony Ship', Rules.Tech.MEDIUMHULL0, |
273
|
|
|
{Rules.Tech.SCOCKPIT0:1, Rules.Tech.COLONYMOD0:1, Rules.Tech.FTLENG0:5}, []) |
274
|
|
|
|
275
|
|
|
@staticmethod |
276
|
|
|
def setStartingFleet(tran, playerID, system): |
277
|
|
|
# add small fleet |
278
|
|
|
log.debug('Creating fleet') |
279
|
|
|
fleet = tran.gameMngr.cmdPool[Const.T_FLEET].new(Const.T_FLEET) |
280
|
|
|
tran.db.create(fleet) |
281
|
|
|
log.debug('Creating fleet - created', fleet.oid) |
282
|
|
|
tran.gameMngr.cmdPool[Const.T_FLEET].create(tran, fleet, system, playerID) |
283
|
|
|
log.debug('Creating fleet - addShips') |
284
|
|
|
# for IDs, see setStartingShipDesigns |
285
|
|
|
tran.gameMngr.cmdPool[Const.T_FLEET].addNewShip(tran, fleet, 1) |
286
|
|
|
tran.gameMngr.cmdPool[Const.T_FLEET].addNewShip(tran, fleet, 1) |
287
|
|
|
tran.gameMngr.cmdPool[Const.T_FLEET].addNewShip(tran, fleet, 2) |
288
|
|
|
tran.gameMngr.cmdPool[Const.T_FLEET].addNewShip(tran, fleet, 2) |
289
|
|
|
tran.gameMngr.cmdPool[Const.T_FLEET].addNewShip(tran, fleet, 5) |
290
|
|
|
|
291
|
|
|
@public(Const.AL_FULL) |
292
|
|
|
def startGlobalConstruction(self, tran, player, techID, quantity, isShip, reportFinished, queue): |
293
|
|
|
if len(player.prodQueues) <= queue: |
294
|
|
|
raise ige.GameException('Invalid queue.') |
295
|
|
|
if len(player.prodQueues[queue]) > Rules.maxProdQueueLen: |
296
|
|
|
raise ige.GameException('Queue is full.') |
297
|
|
|
if quantity < 1: |
298
|
|
|
raise ige.GameException("Quantity must be greater than 0") |
299
|
|
|
if techID not in player.techs and isShip == 0: |
300
|
|
|
raise ige.GameException('You do not own this kind of technology.') |
301
|
|
|
if techID not in player.shipDesigns and isShip == 1: |
302
|
|
|
raise ige.GameException('You do not own this ship design.') |
303
|
|
|
if isShip: |
304
|
|
|
tech = player.shipDesigns[techID] |
305
|
|
|
if tech.upgradeTo: |
306
|
|
|
raise ige.GameException("You cannot build obsolete ship design.") |
307
|
|
|
else: |
308
|
|
|
tech = Rules.techs[techID] |
309
|
|
|
if tech.isStructure or not tech.isProject: |
310
|
|
|
raise ige.GameException('You cannot construct this technology.') |
311
|
|
|
elif tech.globalDisabled: |
312
|
|
|
raise ige.GameException('You cannot construct targeted project.') |
313
|
|
|
neededSR = {} |
314
|
|
|
for sr in tech.buildSRes: |
315
|
|
|
nSR = neededSR.get(sr, 0) + tech.buildSRes[sr] * quantity |
316
|
|
|
if player.stratRes.get(sr, 0) < nSR: |
317
|
|
|
raise ige.GameException("You do not own required strategic resource(s)") |
318
|
|
|
neededSR[sr] = nSR |
319
|
|
|
# consume strategic resources |
320
|
|
|
for sr in neededSR: |
321
|
|
|
player.stratRes[sr] -= neededSR[sr] |
322
|
|
|
# start construction |
323
|
|
|
item = IDataHolder() |
324
|
|
|
item.techID = techID |
325
|
|
|
item.quantity = int(quantity) |
326
|
|
|
item.changePerc = 0 |
327
|
|
|
item.isShip = bool(isShip) |
328
|
|
|
item.reportFin = bool(reportFinished) |
329
|
|
|
item.type = Const.T_TASK |
330
|
|
|
player.prodQueues[queue].append(item) |
331
|
|
|
return player.prodQueues[queue], player.stratRes |
332
|
|
|
|
333
|
|
|
@public(Const.AL_FULL) |
334
|
|
|
def changeGlobalConstruction(self, tran, player, queue, index, quantity): |
335
|
|
|
if index < 0 or index >= len(player.prodQueues[queue]): |
336
|
|
|
raise ige.GameException("No such item in the construction queue.") |
337
|
|
|
|
338
|
|
|
if quantity < 1: |
339
|
|
|
raise ige.GameException("Quantity must be greater than 0") |
340
|
|
|
|
341
|
|
|
item = player.prodQueues[queue][index] |
342
|
|
|
if item.isShip: |
343
|
|
|
tech = player.shipDesigns[item.techID] |
344
|
|
|
else: |
345
|
|
|
tech = Rules.techs[item.techID] |
346
|
|
|
|
347
|
|
|
quantityChange = quantity - player.prodQueues[queue][index].quantity |
348
|
|
|
|
349
|
|
|
neededSR = {} |
350
|
|
|
for sr in tech.buildSRes: |
351
|
|
|
nSR = neededSR.get(sr, 0) + tech.buildSRes[sr] * quantityChange |
352
|
|
|
if player.stratRes.get(sr, 0) < nSR: |
353
|
|
|
raise ige.GameException("You do not own required strategic resource(s)") |
354
|
|
|
neededSR[sr] = nSR |
355
|
|
|
# consume strategic resources |
356
|
|
|
for sr in neededSR: |
357
|
|
|
player.stratRes[sr] += (-1 * neededSR[sr]) |
358
|
|
|
|
359
|
|
|
player.prodQueues[queue][index].quantity = quantity |
360
|
|
|
player.prodQueues[queue][index].const = tech.buildProd * quantity |
361
|
|
|
return player.prodQueues[queue], player.stratRes |
362
|
|
|
|
363
|
|
|
@public(Const.AL_FULL) |
364
|
|
|
def abortGlobalConstruction(self, tran, player, queue, index): |
365
|
|
|
if len(player.prodQueues) <= queue or queue < 0: |
366
|
|
|
raise ige.GameException('Invalid queue.') |
367
|
|
|
if len(player.prodQueues[queue]) <= index or index < 0: |
368
|
|
|
raise ige.GameException('Invalid task.') |
369
|
|
|
item = player.prodQueues[queue][index] |
370
|
|
|
# return strategic resources |
371
|
|
|
#is ship |
372
|
|
|
if item.techID < 1000: |
373
|
|
|
tech = player.shipDesigns[item.techID] |
374
|
|
|
else: |
375
|
|
|
tech = Rules.techs[item.techID] |
376
|
|
|
for sr in tech.buildSRes: |
377
|
|
|
player.stratRes[sr] += item.quantity * tech.buildSRes[sr] |
378
|
|
|
player.prodQueues[queue].pop(index) |
379
|
|
|
return player.prodQueues[queue], player.stratRes |
380
|
|
|
|
381
|
|
|
@public(Const.AL_FULL) |
382
|
|
|
def moveGlobalConstrItem(self, tran, player, queue, index, rel): |
383
|
|
|
if index >= len(player.prodQueues[queue]): |
384
|
|
|
raise ige.GameException('No such item in the construction queue.') |
385
|
|
|
if index + rel < 0 or index + rel >= len(player.prodQueues[queue]): |
386
|
|
|
raise ige.GameException('Cannot move.') |
387
|
|
|
item = player.prodQueues[queue][index] |
388
|
|
|
del player.prodQueues[queue][index] |
389
|
|
|
player.prodQueues[queue].insert(index + rel, item) |
390
|
|
|
return player.prodQueues[queue] |
391
|
|
|
|
392
|
|
|
def getReferences(self, tran, obj): |
393
|
|
|
return obj.fleets |
394
|
|
|
|
395
|
|
|
def loggedIn(self, tran, obj): |
396
|
|
|
obj.lastLogin = time.time() |
397
|
|
|
|
398
|
|
|
@public(Const.AL_OWNER) |
399
|
|
|
def resign(self, tran, obj): |
400
|
|
|
"""Remove player from the game. Give remaining planets, ... to the REBELS""" |
401
|
|
|
# cannot resign when time is stopped |
402
|
|
|
# TODO smarted conditions (like cannot resign twice a week or so) |
403
|
|
|
galaxy = tran.db[obj.galaxy] |
404
|
|
|
if galaxy.scenario == Const.SCENARIO_SINGLE: |
405
|
|
|
raise ige.GameException('You cannot resign current game - it is single player game.') |
406
|
|
|
if not obj.timeEnabled: |
407
|
|
|
raise ige.GameException('You cannot resign current game - time is stopped.') |
408
|
|
|
log.debug("Resigning player", obj.oid) |
409
|
|
|
# morph player to AI |
410
|
|
|
obj.type = self.resignTo |
411
|
|
|
self.cmd(obj).upgrade(tran, obj) |
412
|
|
|
self.cmd(obj).update(tran, obj) |
413
|
|
|
# reregister |
414
|
|
|
tran.gameMngr.removePlayer(obj.oid) |
415
|
|
|
self.cmd(obj).register(tran, obj, obj.galaxy) |
416
|
|
|
|
417
|
|
|
@public(Const.AL_ADMIN) |
418
|
|
|
def delete(self, tran, obj): |
419
|
|
|
# check whether it is AI or normal player |
420
|
|
|
log.debug("Deleting player", obj.oid) |
421
|
|
|
# delete relations |
422
|
|
|
for playerID in tran.db[Const.OID_UNIVERSE].players: |
423
|
|
|
player = tran.db[playerID] |
424
|
|
|
self.cmd(player).deleteDiplomacyWith(tran, player, obj.oid) |
425
|
|
|
# delete fleets |
426
|
|
|
for fleetID in obj.fleets: |
427
|
|
|
fleet = tran.db[fleetID] |
428
|
|
|
self.cmd(fleet).disbandFleet(tran, fleet) |
429
|
|
|
try: |
430
|
|
|
tran.gameMngr.removePlayer(obj.oid) |
431
|
|
|
except Exception: |
432
|
|
|
log.warning("Cannot remove player") |
433
|
|
|
|
434
|
|
|
@public(Const.AL_ADMIN) |
435
|
|
|
def giveUp(self, tran, obj, playerID): |
436
|
|
|
"""Remove player from the game. Give remaining planets, ... to the specified player""" |
437
|
|
|
# cannot resign when time is stopped |
438
|
|
|
# TODO smarted conditions (like cannot resign twice a week or so) |
439
|
|
|
if not obj.timeEnabled: |
440
|
|
|
raise ige.GameException('You cannot resign current game - time is stopped.') |
441
|
|
|
player = tran.db[playerID] |
442
|
|
|
# give planets |
443
|
|
|
for planetID in obj.planets[:]: # needs a copy - changeOwner modifies this |
444
|
|
|
planet = tran.db[planetID] |
445
|
|
|
self.cmd(planet).changeOwner(tran, planet, playerID, force = 1) |
446
|
|
|
# give fleets |
447
|
|
|
for fleetID in obj.fleets[:]: |
448
|
|
|
fleet = tran.db[fleetID] |
449
|
|
|
fleet.owner = playerID |
450
|
|
|
player.fleets.append(fleetID) |
451
|
|
|
# remove player |
452
|
|
|
tran.gameMngr.removePlayer(obj.oid) |
453
|
|
|
try: |
454
|
|
|
tran.db[Const.OID_UNIVERSE].players.remove(obj.oid) |
455
|
|
|
except ValueError: |
456
|
|
|
pass |
457
|
|
|
|
458
|
|
|
@public(Const.AL_OWNER) |
459
|
|
|
def addShipDesign(self, tran, obj, name, hullID, eqIDs): |
460
|
|
|
"""Add ship design to the database of designs.""" |
461
|
|
|
# normalize design |
462
|
|
|
name = name.strip() |
463
|
|
|
# check technologies |
464
|
|
|
if hullID not in obj.techs: |
465
|
|
|
raise ige.GameException("You do not posses this hull type.") |
466
|
|
|
for techID in eqIDs: |
467
|
|
|
if techID not in obj.techs: |
468
|
|
|
raise ige.GameException("You do not posses technology(ies) to construct this ship.") |
469
|
|
|
# create spec (throws exception for invad ones) |
470
|
|
|
spec = ShipUtils.makeShipMinSpec(obj, name, hullID, eqIDs, []) |
471
|
|
|
# check number of designs |
472
|
|
|
if len(obj.shipDesigns) > Rules.shipMaxDesigns: |
473
|
|
|
raise ige.GameException("No space to store design.") |
474
|
|
|
# check name of designs |
475
|
|
|
for designID in obj.shipDesigns: |
476
|
|
|
if obj.shipDesigns[designID].name == name: |
477
|
|
|
raise ige.GameException("Design name is already used.") |
478
|
|
|
if re.match("^\s*$",name): |
479
|
|
|
raise ige.GameException("Design name must not be entirely whitespace.") |
480
|
|
|
# find free design id |
481
|
|
|
index = 1 |
482
|
|
|
ids = obj.shipDesigns.keys() |
483
|
|
|
while 1: |
484
|
|
|
if index not in ids: |
485
|
|
|
break |
486
|
|
|
index += 1 |
487
|
|
|
# add design |
488
|
|
|
obj.shipDesigns[index] = spec |
489
|
|
|
return obj.shipDesigns, index |
490
|
|
|
|
491
|
|
|
@public(Const.AL_OWNER) |
492
|
|
|
def addBuoy(self, tran, obj, systemID, text, type): |
493
|
|
|
"""Add new buoy to player buoys.""" |
494
|
|
|
# delete buoy |
495
|
|
|
if not text: |
496
|
|
|
if systemID in obj.buoys: |
497
|
|
|
del obj.buoys[systemID] |
498
|
|
|
return obj.buoys |
499
|
|
|
else: |
500
|
|
|
raise ige.GameException("Buoy at specified system does not exist.") |
501
|
|
|
|
502
|
|
|
if type not in (Const.BUOY_PRIVATE, Const.BUOY_TO_ALLY, Const.BUOY_TO_SCANNERSHARE): |
503
|
|
|
raise ige.GameException("Wrong bouy type.") |
504
|
|
|
|
505
|
|
|
# edit buoy |
506
|
|
|
if systemID in obj.buoys: |
507
|
|
|
obj.buoys[systemID] = (text, type) |
508
|
|
|
return obj.buoys |
509
|
|
|
|
510
|
|
|
if len(obj.buoys) >= 30: |
511
|
|
|
raise ige.GameException("You cannot add more than 30 buoys.") |
512
|
|
|
|
513
|
|
|
if tran.db[systemID].type not in (Const.T_SYSTEM, Const.T_WORMHOLE): |
514
|
|
|
raise ige.GameException("You can add buoy only to system.") |
515
|
|
|
|
516
|
|
|
# new buoy |
517
|
|
|
if len(text) > 0: |
518
|
|
|
obj.buoys[systemID] = (text, type) |
519
|
|
|
|
520
|
|
|
return obj.buoys |
521
|
|
|
|
522
|
|
|
@public(Const.AL_OWNER) |
523
|
|
|
def scrapShipDesign(self, tran, obj, designID): |
524
|
|
|
"""Remove ship design from the database of designs and remove all |
525
|
|
|
active ships using this design.""" |
526
|
|
|
# check design ID |
527
|
|
|
if designID not in obj.shipDesigns: |
528
|
|
|
raise ige.GameException("No such design.") |
529
|
|
|
# delete ships |
530
|
|
|
for fleetID in obj.fleets[:]: # make copy, fleet can be deleted |
531
|
|
|
fleet = tran.db[fleetID] |
532
|
|
|
self.cmd(fleet).deleteDesign(tran, fleet, designID) |
533
|
|
|
# delete tasks |
534
|
|
|
for planetID in obj.planets: |
535
|
|
|
planet = tran.db[planetID] |
536
|
|
|
self.cmd(planet).deleteDesign(tran, planet, designID) |
537
|
|
|
# delete from global queues |
538
|
|
|
for queueID in xrange(len(obj.prodQueues)): |
|
|
|
|
539
|
|
|
queue = obj.prodQueues[queueID][:] |
540
|
|
|
for taskID in xrange(len(queue)): |
541
|
|
|
if obj.prodQueues[queueID][taskID].techID == designID: |
542
|
|
|
self.cmd(obj).abortGlobalConstruction(tran, obj, queueID, taskID) |
543
|
|
|
# clear upgradeTo |
544
|
|
|
for tmpDesignID in obj.shipDesigns: |
545
|
|
|
spec = obj.shipDesigns[tmpDesignID] |
546
|
|
|
if spec.upgradeTo == designID: |
547
|
|
|
spec.upgradeTo = 0 |
548
|
|
|
# delete design |
549
|
|
|
del obj.shipDesigns[designID] |
550
|
|
|
return obj.shipDesigns, obj.fleets, obj.stratRes, obj.prodQueues |
551
|
|
|
|
552
|
|
|
def getShipDesign(self,tran,obj,designID): |
553
|
|
|
if designID not in obj.shipDesigns: |
554
|
|
|
raise ige.GameException("No such design.") |
555
|
|
|
return obj.shipDesigns[designID] |
556
|
|
|
|
557
|
|
|
@public(Const.AL_OWNER) |
558
|
|
|
def upgradeShipDesign(self, tran, obj, oldDesignID, newDesignID): |
559
|
|
|
# check designs ID |
560
|
|
|
if oldDesignID not in obj.shipDesigns: |
561
|
|
|
raise ige.GameException("No such design.") |
562
|
|
|
if newDesignID not in obj.shipDesigns: |
563
|
|
|
raise ige.GameException("No such design.") |
564
|
|
|
if oldDesignID == newDesignID: |
565
|
|
|
raise ige.GameException("Designs are the same.") |
566
|
|
|
oldSpec = obj.shipDesigns[oldDesignID] |
567
|
|
|
newSpec = obj.shipDesigns[newDesignID] |
568
|
|
|
if oldSpec.upgradeTo: |
569
|
|
|
raise ige.GameException("Old design has already been made obsolete.") |
570
|
|
|
if newSpec.upgradeTo: |
571
|
|
|
raise ige.GameException("New design has already been made obsolete.") |
572
|
|
|
if oldSpec.combatClass != newSpec.combatClass: |
573
|
|
|
raise ige.GameException("Designs must be of the same combat class.") |
574
|
|
|
# set old design as upgradable |
575
|
|
|
oldSpec.upgradeTo = newDesignID |
576
|
|
|
# if something is upgraded to oldDesign change it to new design |
577
|
|
|
for desID in obj.shipDesigns: |
578
|
|
|
if obj.shipDesigns[desID].upgradeTo == oldDesignID: |
579
|
|
|
obj.shipDesigns[desID].upgradeTo = newDesignID |
580
|
|
|
# compute strat res difference |
581
|
|
|
stratRes = {} |
582
|
|
|
for sr in oldSpec.buildSRes: |
583
|
|
|
stratRes[sr] = stratRes.get(sr, 0) - oldSpec.buildSRes[sr] |
584
|
|
|
for sr in newSpec.buildSRes: |
585
|
|
|
stratRes[sr] = stratRes.get(sr, 0) + newSpec.buildSRes[sr] |
586
|
|
|
if stratRes[sr] == 0: |
587
|
|
|
del stratRes[sr] |
588
|
|
|
log.debug("upgradeShipDesign", obj.oid, stratRes) |
589
|
|
|
# modify tasks |
590
|
|
|
tasksUpgraded = False |
591
|
|
|
if not stratRes: |
592
|
|
|
log.debug("upgradeShipDesign - upgrading tasks") |
593
|
|
|
for planetID in obj.planets: |
594
|
|
|
planet = tran.db[planetID] |
595
|
|
|
self.cmd(planet).changeShipDesign(tran, planet, oldDesignID, newDesignID) |
596
|
|
|
# upgrade global queue as well |
597
|
|
|
for queue in obj.prodQueues: |
598
|
|
|
for task in queue: |
599
|
|
|
if task.techID == oldDesignID: |
600
|
|
|
task.techID = newDesignID |
601
|
|
|
tasksUpgraded = True |
602
|
|
|
else: |
603
|
|
|
log.debug("upgradeShipDesing - NOT upgrading tasks") |
604
|
|
|
return obj.shipDesigns, obj.stratRes, tasksUpgraded, obj.prodQueues |
605
|
|
|
|
606
|
|
|
@public(Const.AL_OWNER) |
607
|
|
|
def cancelUpgradeShipDesign(self, tran, obj, designID): |
608
|
|
|
# check designs ID |
609
|
|
|
if designID not in obj.shipDesigns: |
610
|
|
|
raise ige.GameException("No such design.") |
611
|
|
|
obj.shipDesigns[designID].upgradeTo = Const.OID_NONE |
612
|
|
|
return obj.shipDesigns |
613
|
|
|
|
614
|
|
|
@public(Const.AL_FULL) |
615
|
|
|
def startResearch(self, tran, obj, techID, improveToMax = 0): |
616
|
|
|
if len(obj.rsrchQueue) > Rules.maxRsrchQueueLen: |
617
|
|
|
ige.GameException('Queue is full.') |
618
|
|
|
tech = Rules.techs[techID] |
619
|
|
|
# player has to be a right race |
620
|
|
|
if obj.race not in tech.researchRaces: |
621
|
|
|
raise ige.GameException("Your race cannot research this technology.") |
622
|
|
|
# item cannot be researched twice |
623
|
|
|
for tmpTech in obj.rsrchQueue: |
624
|
|
|
if tmpTech.techID == techID: |
625
|
|
|
raise ige.GameException('Technology is already sheduled for research.') |
626
|
|
|
# disabled? |
627
|
|
|
for tmpTechID in obj.techs: |
628
|
|
|
if techID in Rules.techs[tmpTechID].researchDisables: |
629
|
|
|
raise ige.GameException("Previous research has disabled this technology.") |
630
|
|
|
# check requirements |
631
|
|
|
for tmpTechID, improvement in tech.researchRequires: |
632
|
|
|
if tmpTechID not in obj.techs or obj.techs[tmpTechID] < improvement: |
633
|
|
|
raise ige.GameException('You cannot research this technology yet.') |
634
|
|
|
improvement = obj.techs.get(techID, Rules.techBaseImprovement - 1) + 1 |
635
|
|
|
if improvement > Rules.techMaxImprovement or improvement > tech.maxImprovement: |
636
|
|
|
raise ige.GameException('You cannot improve this technology further.') |
637
|
|
|
if tech.level > obj.techLevel: |
638
|
|
|
raise ige.GameException("Your technological level is insufficient.") |
639
|
|
|
# check strategic resources |
640
|
|
|
if improvement == 1: |
641
|
|
|
for stratRes in tech.researchReqSRes: |
642
|
|
|
if obj.stratRes.get(stratRes, 0) < 1: |
643
|
|
|
raise ige.GameException("Required strategy resource missing.") |
644
|
|
|
item = IDataHolder() |
645
|
|
|
item.techID = techID |
646
|
|
|
item.improvement = improvement |
647
|
|
|
item.currSci = 0 |
648
|
|
|
item.changeSci = 0 |
649
|
|
|
item.improveToMax = improveToMax |
650
|
|
|
item.type = Const.T_RESTASK |
651
|
|
|
obj.rsrchQueue.append(item) |
652
|
|
|
return obj.rsrchQueue |
653
|
|
|
|
654
|
|
|
@public(Const.AL_FULL) |
655
|
|
|
def abortResearch(self, tran, obj, index): |
656
|
|
|
if index >= len(obj.rsrchQueue) or index < 0: |
657
|
|
|
ige.GameException('No such item in queue.') |
658
|
|
|
del obj.rsrchQueue[index] |
659
|
|
|
return obj.rsrchQueue |
660
|
|
|
|
661
|
|
|
@public(Const.AL_FULL) |
662
|
|
|
def editResearch(self, tran, obj, index, improveToMax = 0): |
663
|
|
|
if index >= len(obj.rsrchQueue) or index < 0: |
664
|
|
|
ige.GameException('No such item in queue.') |
665
|
|
|
obj.rsrchQueue[index].improveToMax = improveToMax |
666
|
|
|
return obj.rsrchQueue |
667
|
|
|
|
668
|
|
|
@public(Const.AL_FULL) |
669
|
|
|
def moveResearch(self, tran, obj, index, rel): |
670
|
|
|
if index >= len(obj.rsrchQueue): |
671
|
|
|
raise ige.GameException('No such item in the researcg queue.') |
672
|
|
|
if index + rel < 0 or index + rel >= len(obj.rsrchQueue): |
673
|
|
|
raise ige.GameException('Cannot move.') |
674
|
|
|
item = obj.rsrchQueue[index] |
675
|
|
|
del obj.rsrchQueue[index] |
676
|
|
|
obj.rsrchQueue.insert(index + rel, item) |
677
|
|
|
return obj.rsrchQueue |
678
|
|
|
|
679
|
|
|
@public(Const.AL_FULL) |
680
|
|
|
def redirectShips(self, tran, obj, sourceSystemID, targetSystemID): |
681
|
|
|
# check sourceSystemID |
682
|
|
|
ok = 0 |
683
|
|
|
if sourceSystemID == targetSystemID: |
684
|
|
|
targetSystemID = Const.OID_NONE |
685
|
|
|
for planetID in tran.db[sourceSystemID].planets: |
686
|
|
|
if tran.db[planetID].owner == obj.oid: |
687
|
|
|
ok = 1 |
688
|
|
|
if not ok: |
689
|
|
|
raise ige.GameException("You must own planet in the source system") |
690
|
|
|
# check targetSystemID |
691
|
|
|
if targetSystemID != Const.OID_NONE and 0: # TODO: switch on |
692
|
|
|
ok = 0 |
693
|
|
|
for planetID in tran.db[targetSystemID].planets: |
694
|
|
|
if tran.db[planetID].owner == obj.oid: |
695
|
|
|
ok = 1 |
696
|
|
|
if not ok: |
697
|
|
|
raise ige.GameException("You must own planet in the target system") |
698
|
|
|
# fine - record it |
699
|
|
|
log.debug(obj.oid, "Adding redirection", sourceSystemID, targetSystemID) |
700
|
|
|
if targetSystemID: |
701
|
|
|
obj.shipRedirections[sourceSystemID] = targetSystemID |
702
|
|
|
else: |
703
|
|
|
try: |
704
|
|
|
del obj.shipRedirections[sourceSystemID] |
705
|
|
|
except KeyError: |
706
|
|
|
pass |
707
|
|
|
return obj.shipRedirections |
708
|
|
|
|
709
|
|
|
@public(Const.AL_NONE) |
710
|
|
|
def getPublicInfo(self, tran, obj): |
711
|
|
|
result = IObject.getPublicInfo(self, tran, obj) |
712
|
|
|
result.type = obj.type |
713
|
|
|
result.name = obj.name |
714
|
|
|
return result |
715
|
|
|
|
716
|
|
|
@public(Const.AL_OWNER) |
717
|
|
|
def changePactCond(self, tran, obj, playerID, pactID, state, conditions): |
718
|
|
|
log.debug("changePactCond", obj.oid, playerID, pactID) |
719
|
|
|
# must have a contact |
720
|
|
|
if playerID not in obj.diplomacyRels: |
721
|
|
|
raise ige.GameException('No contact with this player.') |
722
|
|
|
player = tran.db[playerID] |
723
|
|
|
# must be a player |
724
|
|
|
if player.type not in Const.PLAYER_TYPES: |
725
|
|
|
raise ige.GameException('Pacts can be offered to players only.') |
726
|
|
|
# check pactID |
727
|
|
|
pact = Rules.pactDescrs.get(pactID, None) |
728
|
|
|
if not pact: |
729
|
|
|
raise ige.GameException('No such pact type.') |
730
|
|
|
# check state |
731
|
|
|
if state not in (Const.PACT_OFF, Const.PACT_INACTIVE, Const.PACT_ACTIVE): |
732
|
|
|
raise ige.GameException("Wrong pact state") |
733
|
|
|
# check conditions |
734
|
|
|
for tmpPactID in conditions: |
735
|
|
|
pact = Rules.pactDescrs.get(tmpPactID, None) |
736
|
|
|
if not pact: |
737
|
|
|
raise ige.GameException('No such pact type.') |
738
|
|
|
# record pact |
739
|
|
|
dipl = self.cmd(obj).getDiplomacyWith(tran, obj, playerID) |
740
|
|
|
dipl.pacts[pactID] = [state] |
741
|
|
|
dipl.pacts[pactID].extend(conditions) |
742
|
|
|
# if state if Const.PACT_OFF, disable state on partner's side |
743
|
|
|
if state == Const.PACT_OFF: |
744
|
|
|
partner = tran.db[playerID] |
745
|
|
|
dipl = self.cmd(partner).getDiplomacyWith(tran, partner, obj.oid) |
746
|
|
|
if pactID in dipl.pacts: |
747
|
|
|
dipl.pacts[pactID][0] = Const.PACT_OFF |
748
|
|
|
else: |
749
|
|
|
dipl.pacts[pactID] = [Const.PACT_OFF] |
750
|
|
|
return obj.diplomacyRels |
751
|
|
|
|
752
|
|
|
def getDiplomacyWith(self, tran, obj, playerID): |
753
|
|
|
if obj.governorOf: |
754
|
|
|
# player is a governor |
755
|
|
|
leader = tran.db[obj.governorOf] |
756
|
|
|
return self.cmd(leader).getDiplomacyWith(tran, leader, objID) |
|
|
|
|
757
|
|
|
# player is independent |
758
|
|
|
dipl = obj.diplomacyRels.get(playerID, None) |
759
|
|
View Code Duplication |
if not dipl: |
|
|
|
|
760
|
|
|
# make default |
761
|
|
|
dipl = IDataHolder() |
762
|
|
|
dipl.type = Const.T_DIPLREL |
763
|
|
|
dipl.pacts = { |
764
|
|
|
Const.PACT_ALLOW_CIVILIAN_SHIPS: [Const.PACT_ACTIVE, Const.PACT_ALLOW_CIVILIAN_SHIPS] |
765
|
|
|
} |
766
|
|
|
dipl.relation = obj.defaultRelation |
767
|
|
|
dipl.relChng = 0 |
768
|
|
|
dipl.lastContact = tran.db[Const.OID_UNIVERSE].turn |
769
|
|
|
dipl.contactType = Const.CONTACT_NONE |
770
|
|
|
dipl.stats = None |
771
|
|
|
if playerID != obj.oid: |
772
|
|
|
obj.diplomacyRels[playerID] = dipl |
773
|
|
|
else: |
774
|
|
|
log.debug("getDiplomacyWith myself", obj.oid) |
775
|
|
|
return dipl |
776
|
|
|
|
777
|
|
|
@public(Const.AL_OWNER) |
778
|
|
|
def getPartyDiplomacyRels(self, tran, obj, partyID): |
779
|
|
|
if partyID not in obj.diplomacyRels: |
780
|
|
|
return None, None |
781
|
|
|
if obj.diplomacyRels[partyID].contactType == Const.CONTACT_NONE: |
782
|
|
|
return obj.diplomacyRels[partyID], None |
783
|
|
|
party = tran.db[partyID] |
784
|
|
|
return obj.diplomacyRels[partyID], party.diplomacyRels.get(obj.oid, None) |
785
|
|
|
|
786
|
|
|
def isPactActive(self, tran, obj, partnerID, pactID): |
787
|
|
|
#@log.debug("isPactActive", obj.oid, partnerID, pactID) |
788
|
|
|
if partnerID not in obj.diplomacyRels: |
789
|
|
|
return 0 |
790
|
|
|
partner = tran.db[partnerID] |
791
|
|
|
partnerDipl = partner.diplomacyRels.get(obj.oid, None) |
792
|
|
|
if not partnerDipl: |
793
|
|
|
return 0 |
794
|
|
|
return partnerDipl.pacts.get(pactID, [Const.PACT_OFF])[0] == Const.PACT_ACTIVE |
795
|
|
|
|
796
|
|
|
def deleteDiplomacyWith(self, tran, obj, playerID): |
797
|
|
|
if playerID in obj.diplomacyRels: |
798
|
|
|
del obj.diplomacyRels[playerID] |
799
|
|
|
|
800
|
|
|
@public(Const.AL_FULL) |
801
|
|
|
def getRelationTo(self, tran, obj, objID): |
802
|
|
|
if objID == Const.OID_NONE: |
803
|
|
|
return Const.REL_UNDEF |
804
|
|
|
if obj.oid == objID: |
805
|
|
|
return Const.REL_UNITY |
806
|
|
|
if obj.governorOf: |
807
|
|
|
leader = tran.db[obj.governorOf] |
808
|
|
|
return self.cmd(leader).getRelationTo(tran, leader, objID) |
809
|
|
|
dipl = obj.diplomacyRels.get(objID, None) |
810
|
|
|
if dipl: |
811
|
|
|
return dipl.relation |
812
|
|
|
else: |
813
|
|
|
return obj.defaultRelation |
814
|
|
|
|
815
|
|
|
@public(Const.AL_OWNER) |
816
|
|
|
def setVoteFor(self, tran, obj, playerID): |
817
|
|
|
if playerID not in obj.diplomacyRels and playerID != obj.oid and playerID != Const.OID_NONE: |
818
|
|
|
raise ige.GameException("No contact with this commander.") |
819
|
|
|
# check type |
820
|
|
|
if playerID != Const.OID_NONE: |
821
|
|
|
player = tran.db[playerID] |
822
|
|
|
if player.type != Const.T_PLAYER: |
823
|
|
|
raise ige.GameException("You cannot vote for this player.") |
824
|
|
|
# set |
825
|
|
|
obj.voteFor = playerID |
826
|
|
|
return obj.voteFor |
827
|
|
|
|
828
|
|
|
@public(Const.AL_ADMIN) |
829
|
|
|
def processDIPLPhase(self, tran, obj, data): |
830
|
|
|
if not obj.timeEnabled: |
831
|
|
|
return |
832
|
|
|
turn = tran.db[Const.OID_UNIVERSE].turn |
833
|
|
|
# record changes from valid pacts |
834
|
|
|
for partyID in obj.diplomacyRels: |
835
|
|
|
dipl = obj.diplomacyRels[partyID] |
836
|
|
|
# check contact |
837
|
|
|
if dipl.contactType == Const.CONTACT_NONE: |
838
|
|
|
#@log.debug("Skipping contact", obj.oid, partyID) |
839
|
|
|
continue |
840
|
|
|
# base change of relation |
841
|
|
|
dipl.relChng += Rules.baseRelationChange |
842
|
|
|
# process pacts |
843
|
|
|
for pactID in dipl.pacts: |
844
|
|
|
#@log.debug("Processing pact", obj.oid, partyID, pactID, dipl.pacts[pactID]) |
845
|
|
|
if dipl.pacts[pactID][0] != Const.PACT_ACTIVE: |
846
|
|
|
continue |
847
|
|
|
pactSpec = Rules.pactDescrs[pactID] |
848
|
|
|
if dipl.relation < pactSpec.validityInterval[0] or \ |
849
|
|
|
dipl.relation > pactSpec.validityInterval[1] or \ |
850
|
|
|
dipl.relChng < Rules.relLostWhenAttacked / 2: |
851
|
|
|
# skip this non active pact, mark it as off |
852
|
|
|
# mark all pact off when attacked |
853
|
|
|
dipl.pacts[pactID][0] = Const.PACT_OFF |
854
|
|
|
# TODO report it |
855
|
|
|
continue |
856
|
|
|
# pact is valid |
857
|
|
|
if dipl.relation < pactSpec.targetRel: |
858
|
|
|
#@log.debug("Affecting relation", pactSpec.relChng) |
859
|
|
|
dipl.relChng += min(pactSpec.targetRel - dipl.relation, pactSpec.relChng) |
860
|
|
|
# apply relation changes |
861
|
|
|
for partyID in obj.diplomacyRels: |
862
|
|
|
dipl = obj.diplomacyRels[partyID] |
863
|
|
|
dipl.relation += dipl.relChng |
864
|
|
|
dipl.relation = min(dipl.relation, Const.REL_ALLY_HI) |
865
|
|
|
dipl.relation = max(dipl.relation, Const.REL_ENEMY_LO) |
866
|
|
|
#@log.debug('IPlayer', 'Final relation', obj.oid, partyID, dipl.relation, dipl.relChng) |
867
|
|
|
|
868
|
|
|
@public(Const.AL_OWNER) |
869
|
|
|
def getScannerMap(self, tran, obj): |
870
|
|
|
scanLevels = {} |
871
|
|
|
# full map for the admin |
872
|
|
|
if obj.oid == Const.OID_ADMIN: |
873
|
|
|
universe = tran.db[Const.OID_UNIVERSE] |
874
|
|
|
for galaxyID in universe.galaxies: |
875
|
|
|
galaxy = tran.db[galaxyID] |
876
|
|
|
for systemID in galaxy.systems: |
877
|
|
|
system = tran.db[systemID] |
878
|
|
|
obj.staticMap[systemID] = 111111 |
879
|
|
|
for planetID in system.planets: |
880
|
|
|
obj.staticMap[planetID] = 111111 |
881
|
|
|
# adding systems with buoys |
882
|
|
|
for objID in obj.buoys: |
883
|
|
|
scanLevels[objID] = Rules.level1InfoScanPwr |
884
|
|
|
# fixing system scan level for mine fields |
885
|
|
|
systems = {} |
886
|
|
|
for planetID in obj.planets: |
887
|
|
|
systems[tran.db[planetID].compOf] = None |
888
|
|
|
for systemID in systems.keys(): |
889
|
|
|
scanLevels[systemID] = Rules.partnerScanPwr |
890
|
|
|
# player's map |
891
|
|
|
for objID in obj.staticMap: |
892
|
|
|
scanLevels[objID] = max(scanLevels.get(objID, 0), obj.staticMap[objID]) |
893
|
|
|
for objID in obj.dynamicMap: |
894
|
|
|
scanLevels[objID] = max(scanLevels.get(objID, 0), obj.dynamicMap[objID]) |
895
|
|
|
# parties' map |
896
|
|
|
for partnerID in obj.diplomacyRels: |
897
|
|
|
if self.cmd(obj).isPactActive(tran, obj, partnerID, Const.PACT_SHARE_SCANNER): |
898
|
|
|
# load partner's map |
899
|
|
|
partner = tran.db[partnerID] |
900
|
|
|
for objID in partner.staticMap: |
901
|
|
|
scanLevels[objID] = max(scanLevels.get(objID, 0), partner.staticMap[objID]) |
902
|
|
|
for objID in partner.dynamicMap: |
903
|
|
|
scanLevels[objID] = max(scanLevels.get(objID, 0), partner.dynamicMap[objID]) |
904
|
|
|
# partner's fleets and planets |
905
|
|
|
for objID in partner.fleets: |
906
|
|
|
scanLevels[objID] = Rules.partnerScanPwr |
907
|
|
|
for objID in partner.planets: |
908
|
|
|
scanLevels[objID] = Rules.partnerScanPwr |
909
|
|
|
|
910
|
|
|
# create map |
911
|
|
|
map = dict() |
912
|
|
|
for objID, level in scanLevels.iteritems(): |
913
|
|
|
tmpObj = tran.db.get(objID, None) |
914
|
|
|
if not tmpObj: |
915
|
|
|
continue |
916
|
|
|
# add movement validation data |
917
|
|
|
if tmpObj.type in (Const.T_SYSTEM,Const.T_WORMHOLE) and objID not in obj.validSystems: |
918
|
|
|
obj.validSystems.append(objID) |
919
|
|
|
for info in self.cmd(tmpObj).getScanInfos(tran, tmpObj, level, obj): |
920
|
|
|
if (info.oid not in map) or (info.scanPwr > map[info.oid].scanPwr): |
921
|
|
|
map[info.oid] = info |
922
|
|
|
|
923
|
|
|
return map |
924
|
|
|
|
925
|
|
|
#@public(Const.AL_OWNER) |
926
|
|
|
def mergeScannerMap(self, tran, obj, map): |
927
|
|
|
#@log.debug(obj.oid, "Merging scanner map") |
928
|
|
|
contacts = {} |
929
|
|
|
for object, level in map.iteritems(): |
930
|
|
|
objID = object.oid |
931
|
|
|
if object.type in (Const.T_SYSTEM, Const.T_WORMHOLE): |
932
|
|
|
obj.staticMap[objID] = max(obj.staticMap.get(objID, 0), level) |
933
|
|
|
contacts.update(object.scannerPwrs) |
934
|
|
|
elif object.type == Const.T_FLEET: |
935
|
|
|
obj.dynamicMap[objID] = max(obj.dynamicMap.get(objID, 0), level) |
936
|
|
|
contacts[object.owner] = None |
937
|
|
|
else: |
938
|
|
|
raise ige.GameException("Unsupported type %d" % object.type) |
939
|
|
|
if obj.oid in contacts: |
940
|
|
|
del contacts[obj.oid] |
941
|
|
|
if Const.OID_NONE in contacts: |
942
|
|
|
del contacts[Const.OID_NONE] |
943
|
|
|
for partyID in contacts: |
944
|
|
|
# add to player's contacts |
945
|
|
|
dipl = self.cmd(obj).getDiplomacyWith(tran, obj, partyID) |
946
|
|
|
dipl.contactType = max(dipl.contactType, Const.CONTACT_DYNAMIC) |
947
|
|
|
dipl.lastContact = tran.db[Const.OID_UNIVERSE].turn |
948
|
|
|
# add to detected owner's contacts |
949
|
|
|
owner = tran.db[partyID] |
950
|
|
|
assert owner.type in Const.PLAYER_TYPES |
951
|
|
|
dipl = self.cmd(obj).getDiplomacyWith(tran, owner, obj.oid) |
952
|
|
|
dipl.contactType = max(dipl.contactType, Const.CONTACT_DYNAMIC) |
953
|
|
|
dipl.lastContact = tran.db[Const.OID_UNIVERSE].turn |
954
|
|
|
|
955
|
|
|
@public(Const.AL_ADMIN) |
956
|
|
|
def processRSRCHPhase(self, tran, obj, data): |
957
|
|
|
if not obj.timeEnabled: |
958
|
|
|
return |
959
|
|
|
# sci pts from allies |
960
|
|
|
pts = obj.sciPoints |
961
|
|
|
for partnerID in obj.diplomacyRels: |
962
|
|
|
if self.cmd(obj).isPactActive(tran, obj, partnerID, Const.PACT_MINOR_SCI_COOP): |
963
|
|
|
partner = tran.db[partnerID] |
964
|
|
|
pactSpec = Rules.pactDescrs[Const.PACT_MINOR_SCI_COOP] |
965
|
|
|
pts += min( |
966
|
|
|
int(partner.sciPoints * pactSpec.effectivity), |
967
|
|
|
int(obj.sciPoints * pactSpec.effectivity), |
968
|
|
|
) |
969
|
|
|
if self.cmd(obj).isPactActive(tran, obj, partnerID, Const.PACT_MAJOR_SCI_COOP): |
970
|
|
|
partner = tran.db[partnerID] |
971
|
|
|
pactSpec = Rules.pactDescrs[Const.PACT_MAJOR_SCI_COOP] |
972
|
|
|
pts += min( |
973
|
|
|
int(partner.sciPoints * pactSpec.effectivity), |
974
|
|
|
int(obj.sciPoints * pactSpec.effectivity), |
975
|
|
|
) |
976
|
|
|
# compute effective sci pts |
977
|
|
|
obj.effSciPoints = epts = pts - int(obj.stats.storPop * Rules.sciPtsPerCitizen[obj.techLevel]) |
978
|
|
|
index = 0 |
979
|
|
|
while epts > 0 and obj.rsrchQueue and index < len(obj.rsrchQueue): |
980
|
|
|
item = obj.rsrchQueue[index] |
981
|
|
|
tech = Rules.techs[item.techID] |
982
|
|
|
# check requirements |
983
|
|
|
canResearch = 1 |
984
|
|
|
# player has to be a right race |
985
|
|
|
if obj.race not in tech.researchRaces: |
986
|
|
|
canResearch = 0 |
987
|
|
|
for stratRes in tech.researchReqSRes: |
988
|
|
|
if obj.stratRes.get(stratRes, 0) < 1 and item.improvement == 1: |
989
|
|
|
Utils.sendMessage(tran, obj, Const.MSG_MISSING_STRATRES, Const.OID_NONE, stratRes) |
990
|
|
|
canResearch = 0 |
991
|
|
|
break |
992
|
|
|
for tmpTechID in obj.techs: |
993
|
|
|
if item.techID in Rules.techs[tmpTechID].researchDisables: |
994
|
|
|
canResearch = 0 |
995
|
|
|
Utils.sendMessage(tran, obj, Const.MSG_DELETED_RESEARCH, Const.OID_NONE, item.techID) |
996
|
|
|
del obj.rsrchQueue[index] |
997
|
|
|
index -= 1 |
998
|
|
|
break |
999
|
|
|
if tech.level > obj.techLevel: |
1000
|
|
|
canResearch = 0 |
1001
|
|
|
Utils.sendMessage(tran, obj, Const.MSG_DELETED_RESEARCH, Const.OID_NONE, item.techID) |
1002
|
|
|
del obj.rsrchQueue[index] |
1003
|
|
|
index -= 1 |
1004
|
|
|
if not canResearch: |
1005
|
|
|
index += 1 |
1006
|
|
|
continue |
1007
|
|
|
researchSci = Utils.getTechRCost(obj, item.techID) |
1008
|
|
|
wantSci = min(epts, researchSci - item.currSci, |
1009
|
|
|
researchSci / tech.researchTurns) |
1010
|
|
|
item.currSci += wantSci |
1011
|
|
|
item.changeSci = wantSci |
1012
|
|
|
epts -= wantSci |
1013
|
|
|
if item.currSci >= researchSci: |
1014
|
|
|
del obj.rsrchQueue[index] |
1015
|
|
|
obj.techs[item.techID] = item.improvement |
1016
|
|
|
# call finish handler |
1017
|
|
|
tech = Rules.techs[item.techID] |
1018
|
|
|
tech.finishResearchHandler(tran, obj, tech) |
1019
|
|
|
Utils.sendMessage(tran, obj, Const.MSG_COMPLETED_RESEARCH, Const.OID_NONE, item.techID) |
1020
|
|
|
# update derived attributes of player |
1021
|
|
|
self.cmd(obj).update(tran, obj) |
1022
|
|
|
# repeat research if required by player |
1023
|
|
|
if item.improveToMax == 1 and item.improvement < Rules.techMaxImprovement: |
1024
|
|
|
# reinsert the item on the top of the queue |
1025
|
|
|
self.cmd(obj).startResearch(tran, obj, item.techID, improveToMax = 1) |
1026
|
|
|
idx = len(obj.rsrchQueue) - 1 |
1027
|
|
|
self.cmd(obj).moveResearch(tran, obj, idx, - idx) |
1028
|
|
|
if epts > 0 and 0: # TODO: remove me |
1029
|
|
|
Utils.sendMessage(tran, obj, Const.MSG_WASTED_SCIPTS, Const.OID_NONE, epts) |
1030
|
|
|
return |
1031
|
|
|
# oops we have negative epts (AI does not regress) |
1032
|
|
|
isAI = obj.type in Const.AI_PLAYER_TYPES |
1033
|
|
|
while epts < 0 and not isAI: |
1034
|
|
|
log.debug("Not enough RP", epts, obj.oid) |
1035
|
|
|
if obj.rsrchQueue: |
1036
|
|
|
item = obj.rsrchQueue[0] |
1037
|
|
|
if item.currSci > 0: |
1038
|
|
|
wantSci = min(item.currSci, - epts) |
1039
|
|
|
item.currSci -= wantSci |
1040
|
|
|
item.changeSci = - wantSci |
1041
|
|
|
epts += wantSci |
1042
|
|
|
if item.currSci == 0: |
1043
|
|
|
# remove item from the queue - TODO send message to player |
1044
|
|
|
del obj.rsrchQueue[0] |
1045
|
|
|
# at this point, epts can be zero |
1046
|
|
|
if epts == 0: |
1047
|
|
|
log.debug("RP deficit satisfied", obj.oid) |
1048
|
|
|
break |
1049
|
|
|
# try next project |
1050
|
|
|
if obj.rsrchQueue: |
1051
|
|
|
continue |
1052
|
|
|
# oops we must find technology to degrade |
1053
|
|
|
avail = obj.techs.keys() |
1054
|
|
|
# do not degrade technologies, which enables others |
1055
|
|
|
for techID in obj.techs: |
1056
|
|
|
tech = Rules.techs[techID] |
1057
|
|
|
for tmpTechID, impr in tech.researchRequires: |
1058
|
|
|
if tmpTechID in avail: |
1059
|
|
|
avail.remove(tmpTechID) |
1060
|
|
|
log.debug("Techs avialable for degradation", avail) |
1061
|
|
|
if not avail: |
1062
|
|
|
# no technology... |
1063
|
|
|
break |
1064
|
|
|
# from hight to low IDs |
1065
|
|
|
avail.sort() |
1066
|
|
|
avail.reverse() |
1067
|
|
|
degraded = 0 |
1068
|
|
|
for level in range(obj.techLevel, 0, -1): |
1069
|
|
|
for techID in avail: |
1070
|
|
|
tech = Rules.techs[techID] |
1071
|
|
|
# check level |
1072
|
|
|
if tech.level != level: |
1073
|
|
|
continue |
1074
|
|
|
# do not touch starting technologies |
1075
|
|
|
if tech.isStarting and obj.techs[techID] <= 3: |
1076
|
|
|
continue |
1077
|
|
|
# ok we have one to degrade |
1078
|
|
|
item = IDataHolder() |
1079
|
|
|
item.techID = techID |
1080
|
|
|
item.improvement = obj.techs[techID] |
1081
|
|
|
item.currSci = Utils.getTechRCost(obj, techID, obj.techs[techID]) |
1082
|
|
|
item.changeSci = 0 |
1083
|
|
|
item.improveToMax = 0 |
1084
|
|
|
item.type = Const.T_RESTASK |
1085
|
|
|
obj.rsrchQueue.append(item) |
1086
|
|
|
# degrade tech |
1087
|
|
|
if obj.techs[techID] == 1: |
1088
|
|
|
# TODO send message |
1089
|
|
|
del obj.techs[techID] |
1090
|
|
|
else: |
1091
|
|
|
# TODO send message |
1092
|
|
|
obj.techs[techID] -= 1 |
1093
|
|
|
if tech.recheckWhenTechLost: |
1094
|
|
|
# reset some attributes |
1095
|
|
|
plLevel = obj.techLevel |
1096
|
|
|
obj.techLevel = 1 |
1097
|
|
|
# recheck remaining techs |
1098
|
|
|
for level in range(1, plLevel + 1): |
1099
|
|
|
for techID in obj.techs: |
1100
|
|
|
tech = Rules.techs[techID] |
1101
|
|
|
if tech.level != level: |
1102
|
|
|
continue |
1103
|
|
|
# call finish handler again |
1104
|
|
|
tech.finishResearchHandler(tran, obj, tech) |
1105
|
|
|
degraded = 1 |
1106
|
|
|
break |
1107
|
|
|
if degraded: break |
1108
|
|
|
|
1109
|
|
|
return |
1110
|
|
|
|
1111
|
|
|
@public(Const.AL_ADMIN) |
1112
|
|
|
def processACTIONPhase(self, tran, obj, data): |
1113
|
|
|
return NotImplementedError() |
1114
|
|
|
|
1115
|
|
|
@public(Const.AL_ADMIN) |
1116
|
|
|
def processINITPhase(self, tran, obj, data): |
1117
|
|
|
if not obj.timeEnabled: |
1118
|
|
|
return |
1119
|
|
|
# reset stats |
1120
|
|
|
obj.stats.storPop = 0 |
1121
|
|
|
obj.stats.prodProd = 0 |
1122
|
|
|
obj.stats.effProdProd = 0 |
1123
|
|
|
obj.stats.prodSci = 0 |
1124
|
|
|
obj.stats.effProdSci = 0 |
1125
|
|
|
obj.stats.slots = 0 |
1126
|
|
|
obj.stats.structs = 0 |
1127
|
|
|
obj.stats.planets = 0 |
1128
|
|
|
obj.stats.fleetPwr = 0 |
1129
|
|
|
obj.stats.fleetSupportProd = 0 |
1130
|
|
|
obj.govPwr = Rules.baseGovPwr |
1131
|
|
|
# remove old messages |
1132
|
|
|
self.cmd(obj).deleteOldMsgs(tran, obj) |
1133
|
|
|
# clear fleet upgrade flag |
1134
|
|
|
obj.fleetUpgradeInProgress = 0 |
1135
|
|
|
# clear production pool |
1136
|
|
|
obj.prodIncreasePool = 0 |
1137
|
|
|
# clear map |
1138
|
|
|
obj.dynamicMap.clear() |
1139
|
|
|
# set empty population distribution |
1140
|
|
|
obj.tmpPopDistr = {} |
1141
|
|
|
# do not process other cmds if time disabled |
1142
|
|
|
# clear contacts and delete too old rels |
1143
|
|
|
turn = tran.db[Const.OID_UNIVERSE].turn |
1144
|
|
|
for objID in obj.diplomacyRels.keys(): |
1145
|
|
|
dipl = obj.diplomacyRels[objID] |
1146
|
|
|
# reset contact type |
1147
|
|
|
obj.diplomacyRels[objID].contactType = Const.CONTACT_NONE |
1148
|
|
|
# delete old contacts |
1149
|
|
|
if dipl.lastContact + Rules.contactTimeout < turn: |
1150
|
|
|
del obj.diplomacyRels[objID] |
1151
|
|
|
continue |
1152
|
|
|
# lower scan powers in static map |
1153
|
|
|
for objID in obj.staticMap: |
1154
|
|
|
level = obj.staticMap[objID] |
1155
|
|
|
if level > Rules.level3InfoScanPwr: |
1156
|
|
|
obj.staticMap[objID] = max( |
1157
|
|
|
Rules.level3InfoScanPwr, |
1158
|
|
|
int(level * Rules.mapForgetScanPwr), |
1159
|
|
|
) |
1160
|
|
|
#@log.debug(obj.oid, "player static map fix", objID, level - obj.staticMap[objID]) |
1161
|
|
|
# clear relations change indicator |
1162
|
|
|
for partyID in obj.diplomacyRels: |
1163
|
|
|
obj.diplomacyRels[partyID].relChng = 0 |
1164
|
|
|
# reset science points |
1165
|
|
|
obj.sciPoints = 0 |
1166
|
|
|
|
1167
|
|
|
@public(Const.AL_ADMIN) |
1168
|
|
|
def processFINALPhase(self, tran, obj, data): |
1169
|
|
|
if not obj.timeEnabled: |
1170
|
|
|
return |
1171
|
|
|
#try/except so that entire final process doesn't break on error in sub-phase |
1172
|
|
|
try: |
1173
|
|
|
self.cmd(obj).processRSRCHPhase(tran, obj, data) |
1174
|
|
|
except: |
1175
|
|
|
log.warning('Cannot execute FINAL/RSRCH on %d' % (obj.oid)) |
1176
|
|
|
try: |
1177
|
|
|
self.cmd(obj).processDIPLPhase(tran, obj, data) |
1178
|
|
|
except: |
1179
|
|
|
log.warning('Cannot execute FINAL/DIPL on %d' % (obj.oid)) |
1180
|
|
|
# efficiency |
1181
|
|
|
obj.prodEff = 1.0 |
1182
|
|
|
obj.sciEff = 1.0 |
1183
|
|
|
if obj.imperator == 1: |
1184
|
|
|
log.debug(obj.oid, "Leader bonus") |
1185
|
|
|
obj.prodEff += Rules.galLeaderBonus |
1186
|
|
|
obj.sciEff += Rules.galLeaderBonus |
1187
|
|
|
elif obj.imperator >= 2: |
1188
|
|
|
log.debug(obj.oid, "Imperator bonus") |
1189
|
|
|
obj.prodEff += Rules.galImperatorBonus |
1190
|
|
|
obj.sciEff += Rules.galImperatorBonus |
1191
|
|
|
#@log.debug("Fleet upgrade pool", obj.oid, obj.fleetUpgradePool, obj.fleetUpgradeInProgress) |
1192
|
|
|
# compute some stats |
1193
|
|
|
# TODO remove, RAW SCI PTS represented now obj.stats.prodSci = obj.effSciPoints |
1194
|
|
|
obj.stats.planets = len(obj.planets) |
1195
|
|
|
# fleet support |
1196
|
|
|
#@log.debug("Fleet support", obj.oid, obj.stats.fleetSupportProd, obj.stats.prodProd) |
1197
|
|
|
if obj.stats.fleetSupportProd > 0 and obj.stats.prodProd > 0: |
1198
|
|
|
# TODO 0.1 shall be dependend on the race / government type |
1199
|
|
|
obj.prodEff += min(0.1 - float(obj.stats.fleetSupportProd + obj.fleetUpgradePool * Rules.operProdRatio) / obj.stats.prodProd, 0.0) |
1200
|
|
|
# delete non active player |
1201
|
|
|
if obj.lastLogin + Rules.playerTimeout < time.time(): |
1202
|
|
|
log.message("Resigning inactive player", obj.name, obj.oid) |
1203
|
|
|
# TODO send a message? |
1204
|
|
|
self.cmd(obj).resign(tran, obj) |
1205
|
|
|
# delete nonactive newbie player |
1206
|
|
|
if obj.lastLogin + Rules.novicePlayerTimeout < time.time() \ |
1207
|
|
|
and len(obj.planets) < 4: |
1208
|
|
|
log.message("Resigning inactive novice player", obj.name, obj.oid) |
1209
|
|
|
# TODO send a message? |
1210
|
|
|
self.cmd(obj).resign(tran, obj) |
1211
|
|
|
# acquire government power |
1212
|
|
|
if obj.planets: |
1213
|
|
|
planet = tran.db[obj.planets[0]] |
1214
|
|
|
for slot in planet.slots: |
1215
|
|
|
tech = Rules.techs[slot[Const.STRUCT_IDX_TECHID]] |
1216
|
|
|
if tech.govPwr > 0 and slot[Const.STRUCT_IDX_STATUS] & Const.STRUCT_STATUS_ON: |
1217
|
|
|
eff = Utils.getTechEff(tran, slot[Const.STRUCT_IDX_TECHID], obj.oid) |
1218
|
|
|
obj.govPwr = max(int(tech.govPwr * eff * (slot[Const.STRUCT_IDX_OPSTATUS] / 100.0)), obj.govPwr) |
1219
|
|
|
# compute government controll range |
1220
|
|
|
if not hasattr(obj,"tmpPopDistr"): #when player is force-resigned, tmpPopDistr is unset. This is easiest fix. |
1221
|
|
|
obj.tmpPopDistr = {} |
1222
|
|
|
ranges = obj.tmpPopDistr.keys() |
1223
|
|
|
ranges.sort() |
1224
|
|
|
sum = 0 |
1225
|
|
|
range = 1 |
1226
|
|
|
for range in ranges: |
1227
|
|
|
sum += obj.tmpPopDistr[range] |
1228
|
|
|
if sum > obj.govPwr: |
1229
|
|
|
break |
1230
|
|
|
obj.govPwrCtrlRange = max(1, range) |
1231
|
|
|
if sum < obj.govPwr and sum > 0: |
1232
|
|
|
#@log.debug(obj.oid, "GovPwr compensation", obj.govPwrCtrlRange, obj.govPwr, sum) |
1233
|
|
|
obj.govPwrCtrlRange = int(obj.govPwrCtrlRange * obj.govPwr / float(sum)) |
1234
|
|
|
#@log.debug(obj.oid, "GovPwr control range", obj.govPwrCtrlRange) |
1235
|
|
|
# compute prodBonus and sciBonus |
1236
|
|
|
sum = 0 |
1237
|
|
|
for range in ranges: |
1238
|
|
|
sum += obj.tmpPopDistr[range] |
1239
|
|
|
if sum < obj.govPwr and sum > 0: |
1240
|
|
|
ratio = float(obj.govPwr - sum) / obj.govPwr |
1241
|
|
|
#@log.debug(obj.oid, "SMALL EMPIRE BONUS", ratio, "govPwr", obj.govPwr, "sum", sum) |
1242
|
|
|
# TODO let user to define how much to invest into prod and to sci |
1243
|
|
|
obj.prodEff += ratio / 2 |
1244
|
|
|
obj.sciEff += ratio / 2 |
1245
|
|
|
del obj.tmpPopDistr # delete temporary attribute |
1246
|
|
|
# increase prod eff from pacts |
1247
|
|
|
# CPs from allies |
1248
|
|
|
sum = 0 |
1249
|
|
|
for partnerID in obj.diplomacyRels: |
1250
|
|
|
if self.cmd(obj).isPactActive(tran, obj, partnerID, Const.PACT_MINOR_CP_COOP): |
1251
|
|
|
partner = tran.db[partnerID] |
1252
|
|
|
pactSpec = Rules.pactDescrs[Const.PACT_MINOR_CP_COOP] |
1253
|
|
|
sum += min( |
1254
|
|
|
partner.stats.prodProd * pactSpec.effectivity, |
1255
|
|
|
obj.stats.prodProd * pactSpec.effectivity, |
1256
|
|
|
) |
1257
|
|
|
if self.cmd(obj).isPactActive(tran, obj, partnerID, Const.PACT_MAJOR_CP_COOP): |
1258
|
|
|
partner = tran.db[partnerID] |
1259
|
|
|
pactSpec = Rules.pactDescrs[Const.PACT_MAJOR_CP_COOP] |
1260
|
|
|
sum += min( |
1261
|
|
|
partner.stats.prodProd * pactSpec.effectivity, |
1262
|
|
|
obj.stats.prodProd * pactSpec.effectivity, |
1263
|
|
|
) |
1264
|
|
|
# apply production increase pool |
1265
|
|
|
obj.prodIncreasePool += sum |
1266
|
|
|
if obj.stats.prodProd > 0: |
1267
|
|
|
ratio = (Rules.unusedProdMod * obj.prodIncreasePool) / obj.stats.prodProd |
1268
|
|
|
real = min(ratio, math.sqrt(ratio)) |
1269
|
|
|
#@log.debug(obj.oid, "Increase production by", ratio, "real", real) |
1270
|
|
|
obj.prodEff += real |
1271
|
|
|
# clean up prodEff if prodEff < 0 (prevent abuse) |
1272
|
|
|
if obj.prodEff < 0: |
1273
|
|
|
obj.prodEff = 0.0 |
1274
|
|
|
# clean up ship redirections |
1275
|
|
|
systems = {} |
1276
|
|
|
for planetID in obj.planets: |
1277
|
|
|
systems[tran.db[planetID].compOf] = None |
1278
|
|
|
for systemID in obj.shipRedirections.keys(): |
1279
|
|
|
if systemID not in systems: |
1280
|
|
|
del obj.shipRedirections[systemID] |
1281
|
|
|
|
1282
|
|
|
# delete allied bouys |
1283
|
|
|
obj.alliedBuoys = {} |
1284
|
|
|
|
1285
|
|
|
# find all allies |
1286
|
|
|
for partnerID in obj.diplomacyRels.keys(): |
1287
|
|
|
dipl = obj.diplomacyRels[partnerID] |
1288
|
|
|
getAllyBuoys = False |
1289
|
|
|
getScannerBuoys = False |
1290
|
|
|
if dipl.relation >= Const.REL_ALLY_LO: |
1291
|
|
|
getAllyBuoys = True |
1292
|
|
|
if self.isPactActive(tran, obj, partnerID, Const.PACT_SHARE_SCANNER): |
1293
|
|
|
getScannerBuoys = True |
1294
|
|
|
if (getAllyBuoys or getScannerBuoys): |
1295
|
|
|
partner = tran.db[partnerID] |
1296
|
|
|
if hasattr(partner, "buoys"): |
1297
|
|
|
for systemID in partner.buoys.keys(): |
1298
|
|
|
toAllyBuoy = Const.BUOY_NONE |
1299
|
|
|
if getAllyBuoys and partner.buoys[systemID][1] == Const.BUOY_TO_ALLY: |
1300
|
|
|
toAllyBuoy = (partner.buoys[systemID][0], Const.BUOY_FROM_ALLY, partner.name) |
1301
|
|
|
elif getScannerBuoys and partner.buoys[systemID][1] == Const.BUOY_TO_SCANNERSHARE: |
1302
|
|
|
toAllyBuoy = (partner.buoys[systemID][0], Const.BUOY_FROM_ALLY, partner.name) |
1303
|
|
|
if toAllyBuoy != Const.BUOY_NONE: |
1304
|
|
|
if systemID in obj.alliedBuoys: |
1305
|
|
|
obj.alliedBuoys[systemID].append(toAllyBuoy) |
1306
|
|
|
else: |
1307
|
|
|
obj.alliedBuoys[systemID] = [toAllyBuoy] |
1308
|
|
|
return None |
1309
|
|
|
|
1310
|
|
|
## messaging |
1311
|
|
|
def canSendMsg(self, tran, obj, oid, forum): |
1312
|
|
|
if forum == "INBOX": |
1313
|
|
|
sender = tran.db[oid] |
1314
|
|
|
return oid == Const.OID_ADMIN or (oid in obj.diplomacyRels) or \ |
1315
|
|
|
(obj.oid in sender.diplomacyRels) |
1316
|
|
|
if forum == "OUTBOX": |
1317
|
|
|
return obj.oid == oid |
1318
|
|
|
return 0 |
1319
|
|
|
|
1320
|
|
|
@public(Const.AL_OWNER) |
1321
|
|
|
def cleanUpMsgs(self, tran, obj): |
1322
|
|
|
# get messages |
1323
|
|
|
msgs = self.cmd(obj).getMsgs(tran, obj) |
1324
|
|
|
# build list of events |
1325
|
|
|
delete = [] |
1326
|
|
|
for msg in msgs: |
1327
|
|
|
if msg["forum"] == "EVENTS": |
1328
|
|
|
delete.append(msg["id"]) |
1329
|
|
|
# delete |
1330
|
|
|
self.cmd(obj).deleteMsgs(tran, obj, delete) |
1331
|
|
|
return 1 |
1332
|
|
|
|
1333
|
|
|
@public(Const.AL_OWNER) |
1334
|
|
|
def setResolution(self, tran, obj, x, y): |
1335
|
|
|
if not hasattr(obj,'clientStats'): |
1336
|
|
|
obj.clientStats = {} |
1337
|
|
|
obj.clientStats['x'] = x; |
1338
|
|
|
obj.clientStats['y'] = y; |
1339
|
|
|
|
1340
|
|
|
def getResolution(self, obj): |
1341
|
|
|
if not hasattr(obj,'clientStats'): |
1342
|
|
|
obj.clientStats = {} |
1343
|
|
|
if 'x' in obj.clientStats and 'y' in obj.clientStats: |
1344
|
|
|
return ("%s,%s" % (obj.clientStats['x'],obj.clientStats['y'])) |
1345
|
|
|
else: |
1346
|
|
|
return "0,0" |
1347
|
|
|
|
1348
|
|
|
@public(Const.AL_FULL) |
1349
|
|
|
def addObsoleteTechs(self, tran, player, techID): |
1350
|
|
|
# add tech |
1351
|
|
|
temp = set([techID]) |
1352
|
|
|
player.obsoleteTechs = player.obsoleteTechs | temp |
1353
|
|
|
return player.obsoleteTechs |
1354
|
|
|
|
1355
|
|
|
@public(Const.AL_FULL) |
1356
|
|
|
def delObsoleteTechs(self, tran, player, techID): |
1357
|
|
|
# del tech |
1358
|
|
|
temp = set([techID]) |
1359
|
|
|
player.obsoleteTechs = player.obsoleteTechs - temp |
1360
|
|
|
return player.obsoleteTechs |
1361
|
|
|
|
1362
|
|
|
|