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