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 sys, copy |
22
|
|
|
from ige import log |
23
|
|
|
from ige.ospace import Const |
24
|
|
|
import ige.ospace.Rules |
25
|
|
|
import hashlib, os.path |
26
|
|
|
import cPickle as pickle |
27
|
|
|
import types |
28
|
|
|
from ige.ospace import TechHandlers |
29
|
|
|
from xml.sax.handler import ContentHandler |
30
|
|
|
from ige.IDataHolder import IDataHolder |
31
|
|
|
import xml.sax |
32
|
|
|
import copy |
33
|
|
|
|
34
|
|
|
def noop(*args, **kwargs): |
35
|
|
|
return 1 |
36
|
|
|
|
37
|
|
|
attrs = { |
38
|
|
|
'id': 0, |
39
|
|
|
# types of technologies |
40
|
|
|
'isDiscovery': 0, |
41
|
|
|
'isStructure': 0, |
42
|
|
|
'isProject': 0, |
43
|
|
|
'isShipEquip': 0, |
44
|
|
|
'isShipHull': 0, |
45
|
|
|
'isMine': 0, |
46
|
|
|
'isStarting': 0, |
47
|
|
|
"subtype": "techSubtype", |
48
|
|
|
"level": 0, |
49
|
|
|
"maxImprovement": 5, # Rules.techMaxImprovement, |
50
|
|
|
"isMilitary": 0, |
51
|
|
|
"globalDisabled": 0, # to disallow some projects in global queues |
52
|
|
|
# dialog info |
53
|
|
|
'shortname': '', # for TechInfoDlg tech linkages |
54
|
|
|
# construction costs & conditions |
55
|
|
|
'buildProd': 0, |
56
|
|
|
'buildTurns': 1, |
57
|
|
|
'buildSRes': {'resource': 'amount'}, |
58
|
|
|
# operation costs |
59
|
|
|
'operBio': 0, |
60
|
|
|
'operEn': 0, |
61
|
|
|
"operWorkers": 0, |
62
|
|
|
# production |
63
|
|
|
'prodBio': 0 # bio |
64
|
|
|
'prodEn': 0 # energy |
65
|
|
|
'prodProd': 0 # production |
66
|
|
|
'prodSci': 0 # science |
67
|
|
|
'prodEnv': 0 # enviromental effect |
68
|
|
|
'prodPop': 0 # produce population |
69
|
|
|
'prodBioMod': [0.0, 0.0, 0.0, 0.0] # tuple of (plBio, plMin, plEn, default) |
70
|
|
|
'prodEnMod': [0.0, 0.0, 0.0, 0.0] # dtto |
71
|
|
|
'prodProdMod': [0.0, 0.0, 0.0, 0.0] # dtto |
72
|
|
|
'prodSciMod': [0.0, 0.0, 0.0, 1.0] # ditto; default is 1.0 for legacy support |
73
|
|
|
# env |
74
|
|
|
'solarMod': 0 # -: cloak solar radiation / +: create thermal radiation |
75
|
|
|
# storage |
76
|
|
|
'storBio': 0, |
77
|
|
|
'storProd': 0, |
78
|
|
|
'storEn': 0, |
79
|
|
|
'storPop': 0, |
80
|
|
|
# morale affecting |
81
|
|
|
'revoltThr': 0, |
82
|
|
|
'moraleTrgt': 0, |
83
|
|
|
'govPwr': 0, |
84
|
|
|
# military |
85
|
|
|
'scannerPwr': 0, |
86
|
|
|
"structWeapons": [0], |
87
|
|
|
"planetShield": 0 # planetary shield; when structure built, shield = 0; shield will regenerate at 2% per turn until equal to this value. Structures do not add shield strength; strongest shield = planet shield |
88
|
|
|
"systemAtt": 0, |
89
|
|
|
"systemDef": 0, |
90
|
|
|
"refuelMax": 0, |
91
|
|
|
"refuelInc": 0, |
92
|
|
|
"repairShip": 0.0, |
93
|
|
|
"upgradeShip": 0, |
94
|
|
|
"trainShipInc": 0.0 # how many exp/turn |
95
|
|
|
"trainShipMax": 0 # exp. cap (in base exps), not affected by techEff |
96
|
|
|
"fleetSpeedBoost": 0.0 # speed boost for stargates |
97
|
|
|
# misc |
98
|
|
|
"unpackPop": 0, |
99
|
|
|
'envDmg': 0, |
100
|
|
|
'maxHP': 0, |
101
|
|
|
"fullInfo": 0 # if True, show full tech info even player not own tech |
102
|
|
|
# ship equipment |
103
|
|
|
'equipType': '' # identifier of subtype seq_mod's equipment type; see maxEquipType in Rules/__init__ |
104
|
|
|
"addMP": 0 # for extra MP to be added to ship equipment |
105
|
|
|
'combatClass': 0, |
106
|
|
|
'combatAtt': 0 # not cumulative for equipment; cumulative for hull, drives, cockpits, etc |
107
|
|
|
'combatDef': 0 # not cumulative for equipment; cumulative for hull, drives, cockpits, etc |
108
|
|
|
"missileDef": 0 # not cumulative for equipment; cumulative for hull, drives, cockpits, etc |
109
|
|
|
"combatAttPerc": 1.0 # multiplier of ATT; min of 100%; not cumulative |
110
|
|
|
"combatDefPerc": 1.0 # multiplier of DEF; min of 100%; not cumulative |
111
|
|
|
"missileDefPerc": 1.0 # multiplier of missile DEF; min of 100%; not cumulative |
112
|
|
|
'unpackStruct': '', |
113
|
|
|
'deployHandlerID': '' # technology ID of tech to find deployHandlerFunction & deployHandlerValidator (this can be the deployable device OR a project) |
114
|
|
|
'deployHandlerFunction': noop # function name of TechHandler |
115
|
|
|
'deployHandlerValidator': noop # function name of TechHandler Validator |
116
|
|
|
'signature': 0 # **** NOT cumulative (change effective 0.5.63) |
117
|
|
|
'signatureCloak': 1.0 # max of 1.0 is effective; not cumulative |
118
|
|
|
'signatureDecloak': 1.0 # min of 1.0 is effective; not cumulative |
119
|
|
|
"minSignature": 0, |
120
|
|
|
"slots": 0, |
121
|
|
|
"weight": 0, |
122
|
|
|
"maxWeight": 0, |
123
|
|
|
"engPwr": 0, |
124
|
|
|
"engStlPwr": 0, |
125
|
|
|
"shieldPerc": 0.0 # how many percent of maxHP have shields |
126
|
|
|
"minHull": 0, |
127
|
|
|
"maxHull": 10 # just make this higher than the largest hull so we know it doesn't break anything |
128
|
|
|
"maxInstallations": 0, |
129
|
|
|
"shieldRechargeFix": 0 # fixed amount of HP/turn to recharge |
130
|
|
|
"shieldRechargePerc": 0.0 # how many percent of shieldHP/turn is recharged |
131
|
|
|
"hardShield": 0.0 # shield penetrating weapons will penetrate at 100%; use as 1-hardShield for penentration level (hardShield percent = %damage absorbed by shield) |
132
|
|
|
"autoRepairFix": 0 # fixed amount of HP/turn to repair |
133
|
|
|
"autoRepairPerc": 0.0 # how many percent of maxHP/turn is repaired |
134
|
|
|
"damageAbsorb": 0 # amount of damage absorbed by the hull (not shield!); max sum is 5 damage (set in Rules) |
135
|
|
|
# weapons |
136
|
|
|
'weaponDmgMin': 0, |
137
|
|
|
'weaponDmgMax': 0, |
138
|
|
|
'weaponAtt': 0, |
139
|
|
|
'weaponClass': 0, |
140
|
|
|
"weaponROF": 0.0, |
141
|
|
|
"weaponIgnoreShield": 0, |
142
|
|
|
"weaponIsMissile": 0, |
143
|
|
|
"weaponGoodForFlak": 1, |
144
|
|
|
# mines |
145
|
|
|
'mineclass':0 # tech id of the mine; usually level 99 tech - structure in the system with the highest tech id will always deploy; others will be ignored (per player) |
146
|
|
|
'minenum':0 # number of mines this control structure supports; if another structure built more mines, mines will not self destruct |
147
|
|
|
'minerate':0 # number of turns between mine deployments; note that system will deploy mines on: turn%minerate==0 |
148
|
|
|
# research |
149
|
|
|
'researchRequires': ['technology'], |
150
|
|
|
'researchEnables': ['technology'], |
151
|
|
|
'researchDisables': ['technology'], |
152
|
|
|
'researchReqSRes': ['resource'], |
153
|
|
|
"researchMod": "expr", |
154
|
|
|
'researchTurns': 1, |
155
|
|
|
"researchRaces": "BCH", |
156
|
|
|
# misc |
157
|
|
|
"data": "none", |
158
|
|
|
"recheckWhenTechLost": 0, |
159
|
|
|
"deprecated": 0 # this tech is no longer active |
160
|
|
|
# before build handler |
161
|
|
|
'validateConstrHandler': noop, |
162
|
|
|
# after build handler |
163
|
|
|
'finishConstrHandler': noop, |
164
|
|
|
# after research handler |
165
|
|
|
'finishResearchHandler': noop, |
166
|
|
|
# names |
167
|
|
|
'name': u'Unspecified', |
168
|
|
|
# textual description |
169
|
|
|
'textPreRsrch': u'Not specified', |
170
|
|
|
'textDescr': u'Not specified', |
171
|
|
|
'textFlavor': u'Not specified', |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
# class representing technologies |
175
|
|
|
class Technology: |
176
|
|
|
|
177
|
|
|
def __init__(self, id, symbol, reg): |
178
|
|
|
self.id = id |
179
|
|
|
self.symbol = symbol |
180
|
|
|
if id in reg: |
181
|
|
|
raise KeyError("%s is already registered" % id) |
182
|
|
|
reg[id] = self |
183
|
|
|
|
184
|
|
|
def set(self, key, value): |
185
|
|
|
if attrs.has_key(key): |
186
|
|
|
attrType = type(attrs[key]) |
187
|
|
|
if attrType == types.IntType: |
188
|
|
|
value = int(value) |
189
|
|
|
elif attrType == types.FloatType: |
190
|
|
|
value = float(value) |
191
|
|
|
elif attrType == types.UnicodeType: |
192
|
|
|
pass |
193
|
|
|
elif attrType == types.StringType: |
194
|
|
|
value = str(value) |
195
|
|
|
elif attrType == types.FunctionType: |
196
|
|
|
value = getattr(TechHandlers, value) |
197
|
|
|
elif attrType == types.ListType: |
198
|
|
|
itemType = type(attrs[key][0]) |
199
|
|
|
if itemType == types.IntType: |
200
|
|
|
convertFunc = int |
201
|
|
|
elif itemType == types.StringType: |
202
|
|
|
convertFunc = str |
203
|
|
|
elif itemType == types.FloatType: |
204
|
|
|
convertFunc = float |
205
|
|
|
else: |
206
|
|
|
raise 'Unsupported attribute type %s' % repr(itemType) |
207
|
|
|
result = [] |
208
|
|
|
for item in value.split(','): |
209
|
|
|
if item: |
210
|
|
|
result.append(convertFunc(item)) |
211
|
|
|
value = result |
212
|
|
|
elif attrType == types.DictType: |
213
|
|
|
# format is key:value,key2:value2 |
214
|
|
|
dict_key, dict_value = copy.copy(attrs[key]).popitem() |
215
|
|
|
keyType = type(dict_key) |
216
|
|
|
if keyType == types.IntType: |
217
|
|
|
convertFuncKey = int |
218
|
|
|
elif keyType == types.StringType: |
219
|
|
|
convertFuncKey = str |
220
|
|
|
elif keyType == types.FloatType: |
221
|
|
|
convertFuncKey = float |
222
|
|
|
else: |
223
|
|
|
raise 'Unsupported attribute type %s' % repr(keyType) |
224
|
|
|
valueType = type(dict_value) |
225
|
|
|
if valueType == types.IntType: |
226
|
|
|
convertFuncValue = int |
227
|
|
|
elif valueType == types.StringType: |
228
|
|
|
convertFuncValue = str |
229
|
|
|
elif valueType == types.FloatType: |
230
|
|
|
convertFuncValue = float |
231
|
|
|
else: |
232
|
|
|
raise 'Unsupported attribute type %s' % repr(valueType) |
233
|
|
|
# let's parse! |
234
|
|
|
result = {} |
235
|
|
|
for pair in value.split(','): |
236
|
|
|
pair_key, pair_value = pair.split(':') |
237
|
|
|
pair_key = convertFuncKey(pair_key) |
238
|
|
|
pair_value = convertFuncValue(pair_value) |
239
|
|
|
result[pair_key] = pair_value |
240
|
|
|
value = result |
241
|
|
|
else: |
242
|
|
|
raise 'Unsupported attribute type %s' % repr(attrType) |
243
|
|
|
setattr(self, key, value) |
244
|
|
|
else: |
245
|
|
|
raise AttributeError('Cannot create %s - unsupported attribute.' % key) |
246
|
|
|
|
247
|
|
|
def __getattr__(self, attr): |
248
|
|
|
if attrs.has_key(attr): |
249
|
|
|
# optimalization |
250
|
|
|
setattr(self, attr, attrs[attr]) |
251
|
|
|
return attrs[attr] |
252
|
|
|
else: |
253
|
|
|
raise AttributeError('No attribute %s' % attr) |
254
|
|
|
|
255
|
|
|
def isDefault(self, attr): |
256
|
|
|
if hasattr(self, attr): |
257
|
|
|
return getattr(self, attr) == attrs[attr] |
258
|
|
|
else: |
259
|
|
|
return 1 |
260
|
|
|
|
261
|
|
|
def __repr__(self): |
262
|
|
|
result = '(Technology ' |
263
|
|
|
for key, value in self.__dict__.items(): |
264
|
|
|
result += '%s : %s, ' % (repr(key), repr(value)) |
265
|
|
|
result += ')' |
266
|
|
|
return result |
267
|
|
|
|
268
|
|
|
# holder for all technologies |
269
|
|
|
techs = {} |
270
|
|
|
|
271
|
|
|
# parse TechTree.xml and create all tech objects |
272
|
|
|
class TechTreeContentHandler(ContentHandler): |
273
|
|
|
def startDocument(self): |
274
|
|
|
self.state = 1 |
275
|
|
|
self.text = '' |
276
|
|
|
|
277
|
|
|
def endDocument(self): |
278
|
|
|
if self.state != 1: |
279
|
|
|
raise 'Wrong TechTree specification' |
280
|
|
|
|
281
|
|
|
def startElement(self, name, attrs): |
282
|
|
|
if self.state == 1 and name == 'techtree': |
283
|
|
|
self.state = 2 |
284
|
|
|
elif self.state == 2 and name == 'technology': |
285
|
|
|
log.debug('Tech %s [%s]' % (attrs['name'], attrs['id'])) |
286
|
|
|
self.state = 3 |
287
|
|
|
self.tech = Technology(int(attrs['id']), attrs['symbol'], techs) |
288
|
|
|
setattr(Tech, attrs['symbol'], int(attrs['id'])) |
289
|
|
|
self.tech.set('name', attrs['name']) |
290
|
|
|
elif self.state == 3 and name == 'structure': |
291
|
|
|
self.tech.set('isStructure', 1) |
292
|
|
|
for key in attrs.keys(): |
293
|
|
|
self.tech.set(key, attrs[key]) |
294
|
|
|
realBuildProd = int(self.tech.buildProd * ige.ospace.Rules.structDefaultCpCosts) |
295
|
|
|
self.tech.set('buildProd', realBuildProd) |
296
|
|
|
elif self.state == 3 and name == 'discovery': |
297
|
|
|
self.tech.set('isDiscovery', 1) |
298
|
|
|
for key in attrs.keys(): |
299
|
|
|
self.tech.set(key, attrs[key]) |
300
|
|
|
elif self.state == 3 and name == 'notdiscovery': |
301
|
|
|
self.tech.set('isDiscovery', 0) |
302
|
|
|
for key in attrs.keys(): |
303
|
|
|
self.tech.set(key, attrs[key]) |
304
|
|
|
elif self.state == 3 and name == 'starting': |
305
|
|
|
self.tech.set('isStarting', 1) |
306
|
|
|
for key in attrs.keys(): |
307
|
|
|
self.tech.set(key, attrs[key]) |
308
|
|
|
elif self.state == 3 and name == 'notstarting': |
309
|
|
|
self.tech.set('isStarting', 0) |
310
|
|
|
for key in attrs.keys(): |
311
|
|
|
self.tech.set(key, attrs[key]) |
312
|
|
|
elif self.state == 3 and name == 'shipequip': |
313
|
|
|
self.tech.set('isShipEquip', 1) |
314
|
|
|
for key in attrs.keys(): |
315
|
|
|
self.tech.set(key, attrs[key]) |
316
|
|
|
elif self.state == 3 and name == 'project': |
317
|
|
|
self.tech.set('isProject', 1) |
318
|
|
|
for key in attrs.keys(): |
319
|
|
|
self.tech.set(key, attrs[key]) |
320
|
|
|
elif self.state == 3 and name == 'shiphull': |
321
|
|
|
self.tech.set('isShipHull', 1) |
322
|
|
|
for key in attrs.keys(): |
323
|
|
|
self.tech.set(key, attrs[key]) |
324
|
|
|
elif self.state == 3 and name == 'mine': |
325
|
|
|
self.tech.set('isMine', 1) |
326
|
|
|
for key in attrs.keys(): |
327
|
|
|
self.tech.set(key, attrs[key]) |
328
|
|
|
elif self.state == 3 and name == 'data': |
329
|
|
|
for key in attrs.keys(): |
330
|
|
|
self.tech.set(key, attrs[key]) |
331
|
|
|
elif self.state == 3 and name == 'preresearch': |
332
|
|
|
self.state = 4 |
333
|
|
|
self.text = '' |
334
|
|
|
elif self.state == 3 and name == 'description': |
335
|
|
|
self.state = 4 |
336
|
|
|
self.text = '' |
337
|
|
|
elif self.state == 3 and name == 'flavor': |
338
|
|
|
self.state = 4 |
339
|
|
|
self.text = '' |
340
|
|
|
else: |
341
|
|
|
raise 'Unsupported tag %s' % str(name) |
342
|
|
|
|
343
|
|
|
def endElement(self, name): |
344
|
|
|
if self.state == 2 and name == 'techtree': |
345
|
|
|
self.state = 1 |
346
|
|
|
elif self.state == 3 and name == 'technology': |
347
|
|
|
self.state = 2 |
348
|
|
|
elif self.state == 4 and name == 'preresearch': |
349
|
|
|
self.tech.textPreRsrch = self.text |
350
|
|
|
self.state = 3 |
351
|
|
|
elif self.state == 4 and name == 'description': |
352
|
|
|
self.tech.textDescr = self.text |
353
|
|
|
self.state = 3 |
354
|
|
|
elif self.state == 4 and name == 'flavor': |
355
|
|
|
self.tech.textFlavor = self.text |
356
|
|
|
self.state = 3 |
357
|
|
|
|
358
|
|
|
def characters(self, text): |
359
|
|
|
self.text += text |
360
|
|
|
|
361
|
|
|
Tech = IDataHolder() |
362
|
|
|
|
363
|
|
|
## init is wrapping all code that needs to be performed before module |
364
|
|
|
# is ready. That is checking if techs changed, if not loading pickled |
365
|
|
|
# data, othervise rebuild database from source data |
366
|
|
|
# We should be able to do server-provided techs in the future (per galaxy |
367
|
|
|
# rulesets) |
368
|
|
|
|
369
|
|
|
|
370
|
|
|
def init(configDir): |
371
|
|
|
global Tech, techs |
372
|
|
|
## check, if anything has been changed |
373
|
|
|
|
374
|
|
|
def chsumDir(chsum, dirname, names): |
375
|
|
|
names.sort() |
376
|
|
|
for filename in names: |
377
|
|
|
if os.path.splitext(filename)[1] in ('.xml', '.py'): |
378
|
|
|
log.debug('Checking file', filename) |
379
|
|
|
fh = open(os.path.join(dirname, filename), 'rb') |
380
|
|
|
chsum.update(fh.read()) |
381
|
|
|
fh.close() |
382
|
|
|
|
383
|
|
|
# compute checksum |
384
|
|
|
moduleFile = sys.modules['ige.ospace.Rules'].__file__ |
385
|
|
|
forceLoad = 0 |
386
|
|
|
|
387
|
|
|
# regular module |
388
|
|
|
moduleDirectory = os.path.dirname(moduleFile) |
389
|
|
|
chsum = hashlib.sha1() |
390
|
|
|
os.path.walk(moduleDirectory, chsumDir, chsum) |
391
|
|
|
|
392
|
|
|
# read old checksum |
393
|
|
|
try: |
394
|
|
|
fh = open(os.path.join(configDir, 'checksum'), 'rb') |
395
|
|
|
oldChsum = fh.read() |
396
|
|
|
fh.close() |
397
|
|
|
except IOError: |
398
|
|
|
oldChsum = '' |
399
|
|
|
|
400
|
|
|
# compare |
401
|
|
|
if forceLoad or chsum.hexdigest() == oldChsum: |
402
|
|
|
# load old definitions |
403
|
|
|
log.message('Loading stored specifications from', configDir) |
404
|
|
|
techs = pickle.load(open(os.path.join(configDir, 'techs.spf'), 'rb')) |
405
|
|
|
Tech = pickle.load(open(os.path.join(configDir, 'Tech.spf'), 'rb')) |
406
|
|
|
|
407
|
|
|
log.message("There is %d technologies" % len(techs)) |
408
|
|
|
|
409
|
|
|
# clean up 'type' in lists |
410
|
|
|
for key in attrs.keys(): |
411
|
|
|
if type(attrs[key]) == types.ListType and len(attrs[key]) == 1: |
412
|
|
|
log.debug("Cleaning up", key) |
413
|
|
|
attrs[key] = [] |
414
|
|
|
|
415
|
|
|
else: |
416
|
|
|
# create new ones |
417
|
|
|
## load technologies definitions |
418
|
|
|
|
419
|
|
|
def processDir(arg, dirname, names): |
420
|
|
|
log.message('Loading XML files from', dirname) |
421
|
|
|
names.sort() |
422
|
|
|
for filename in names: |
423
|
|
|
if os.path.splitext(filename)[1] == '.xml': |
424
|
|
|
log.message('Parsing XML file', filename) |
425
|
|
|
xml.sax.parse(os.path.join(dirname, filename), TechTreeContentHandler()) |
426
|
|
|
|
427
|
|
|
# collect xml files |
428
|
|
|
os.path.walk(moduleDirectory, processDir, None) |
429
|
|
|
|
430
|
|
|
# clean up 'type' in lists |
431
|
|
|
for key in attrs.keys(): |
432
|
|
|
if type(attrs[key]) == types.ListType and len(attrs[key]) == 1: |
433
|
|
|
log.debug("Cleaning up", key) |
434
|
|
|
attrs[key] = [] |
435
|
|
|
elif type(attrs[key]) == types.DictType and len(attrs[key]) == 1: |
436
|
|
|
log.debug("Cleaning up", key) |
437
|
|
|
attrs[key] = {} |
438
|
|
|
|
439
|
|
|
|
440
|
|
|
# link tech tree using researchRequires fields |
441
|
|
|
# construct researchEnables fields |
442
|
|
|
log.message('Converting symbolic fields...') |
443
|
|
|
for techID in techs.keys(): |
444
|
|
|
tech = techs[techID] |
445
|
|
|
# convert symbolic names to numbers |
446
|
|
|
techIDs = [] |
447
|
|
|
for techSymName in tech.researchRequires: |
448
|
|
|
symName, improvement = techSymName.split('-') |
449
|
|
|
techIDs.append((getattr(Tech, symName), int(improvement))) |
450
|
|
|
tech.researchRequires = techIDs |
451
|
|
|
techIDs = {1: [], 2:[], 3:[], 4:[], 5:[], 6:[]} |
452
|
|
|
for techSymName in tech.researchEnables: |
453
|
|
|
improvement, symName = techSymName.split('-') |
454
|
|
|
techIDs[int(improvement)].append(getattr(Tech, symName)) |
455
|
|
|
tech.researchEnables = techIDs |
456
|
|
|
techIDs = [] |
457
|
|
|
for techSymName in tech.researchDisables: |
458
|
|
|
techIDs.append(getattr(Tech, techSymName)) |
459
|
|
|
tech.researchDisables = techIDs |
460
|
|
|
techIDs = [] |
461
|
|
|
if tech.unpackStruct: |
462
|
|
|
tech.unpackStruct = getattr(Tech, tech.unpackStruct) |
463
|
|
|
else: |
464
|
|
|
tech.unpackStruct = 0 |
465
|
|
|
|
466
|
|
|
# strat. resources |
467
|
|
|
# this has to be here to prevent import deadlock in Rules |
468
|
|
|
from ige.ospace.Rules import stratResAmountBig |
469
|
|
|
from ige.ospace.Rules import stratResAmountSmall |
470
|
|
|
|
471
|
|
|
stratRes = [] |
472
|
|
|
for sr in tech.researchReqSRes: |
473
|
|
|
stratRes.append(getattr(Const, sr)) |
474
|
|
|
tech.researchReqSRes = stratRes |
475
|
|
|
# build requirements with quantities |
476
|
|
|
stratRes = {} |
477
|
|
|
for resource in tech.buildSRes: |
478
|
|
|
resource_id = getattr(Const, resource) |
479
|
|
|
# prescription contains constants for big and small amounts |
480
|
|
|
# possibly within expression |
481
|
|
|
resource_amount = eval(tech.buildSRes[resource]) |
482
|
|
|
stratRes[resource_id] = resource_amount |
483
|
|
|
tech.buildSRes = stratRes |
484
|
|
|
|
485
|
|
|
# evaluate researchMod |
486
|
|
|
if tech.researchMod == "expr": |
487
|
|
|
tech.researchMod = 1.0 |
488
|
|
|
else: |
489
|
|
|
tech.researchMod = eval(tech.researchMod) |
490
|
|
|
|
491
|
|
|
# link |
492
|
|
|
log.message('Linking tech tree...') |
493
|
|
|
for techID in techs.keys(): |
494
|
|
|
tech = techs[techID] |
495
|
|
|
for tmpTechID, improvement in tech.researchRequires: |
496
|
|
|
if techID not in techs[tmpTechID].researchEnables[improvement]: |
497
|
|
|
techs[tmpTechID].researchEnables[improvement].append(techID) |
498
|
|
|
for improvement in tech.researchEnables.keys(): |
499
|
|
|
for tmpTechID in tech.researchEnables[improvement]: |
500
|
|
|
if (techID, improvement) not in techs[tmpTechID].researchRequires: |
501
|
|
|
techs[tmpTechID].researchRequires.append((techID, improvement)) |
502
|
|
|
|
503
|
|
|
changed = 1 |
504
|
|
|
while changed: |
505
|
|
|
changed = 0 |
506
|
|
|
log.debug("Tech disable iteration") |
507
|
|
|
for techID in techs: |
508
|
|
|
tech = techs[techID] |
509
|
|
|
for tech2ID in tech.researchDisables: |
510
|
|
|
tech2 = techs[tech2ID] |
511
|
|
|
if techID not in tech2.researchDisables and techID != tech2ID: |
512
|
|
|
tech2.researchDisables.append(techID) |
513
|
|
|
changed = 1 |
514
|
|
|
log.debug("Adding", tech2ID, "DISABLES", techID, ", NOW", tech2.researchDisables) |
515
|
|
|
for tech3ID in tech2.researchDisables: |
516
|
|
|
tech3 = techs[tech3ID] |
517
|
|
|
if tech3ID not in tech.researchDisables and tech3ID != techID: |
518
|
|
|
tech.researchDisables.append(tech3ID) |
519
|
|
|
changed = 1 |
520
|
|
|
log.debug("Adding", techID, "DISABLES", tech3ID, "NOW", tech.researchDisables) |
521
|
|
|
|
522
|
|
|
# save new specification |
523
|
|
|
log.message('Saving specification...') |
524
|
|
|
pickle.dump(techs, open(os.path.join(configDir, 'techs.spf'), 'wb'), 1) |
525
|
|
|
pickle.dump(Tech, open(os.path.join(configDir, 'Tech.spf'), 'wb'), 1) |
526
|
|
|
fh = open(os.path.join(configDir, 'checksum'), 'wb') |
527
|
|
|
fh.write(chsum.hexdigest()) |
528
|
|
|
fh.close() |
529
|
|
|
|
530
|
|
|
log.message("There is %d technologies" % len(techs)) |
531
|
|
|
|