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 bisect |
||
22 | import glob |
||
23 | import math |
||
24 | import os |
||
25 | import re |
||
26 | |||
27 | import gdata, client |
||
28 | import pygame, pygame.image |
||
29 | from osci import gdata |
||
30 | import ige.ospace.Const as Const |
||
31 | from ige.ospace import Rules |
||
32 | from ige import log |
||
33 | |||
34 | import resources |
||
35 | |||
36 | whiteShift = 80000 |
||
37 | redShift = 90000 |
||
38 | |||
39 | smallStarImgs = None |
||
40 | techImgs = None |
||
41 | bigStarImgs = None |
||
42 | planetImgs = None |
||
43 | planetImgCnt = None |
||
44 | buttonImgs = None |
||
45 | cmdInProgressImg = None |
||
46 | loginLogoImg = None |
||
47 | structProblemImg = None |
||
48 | structOffImg = None |
||
49 | icons = {} |
||
50 | ui_icons = {} |
||
51 | |||
52 | def initialize(): |
||
53 | # needed for progress dlg |
||
54 | global loginLogoImg |
||
55 | loginLogoImg = pygame.image.load(resources.get('logo-login.png')).convert_alpha() |
||
56 | |||
57 | |||
58 | def updateProgress(curr, progress_dlg): |
||
59 | if not progress_dlg: return |
||
60 | if curr % 30 == 0: |
||
61 | periods = '.' * (curr / 30 % 4) |
||
62 | progress_dlg.setProgress(_('Loading resources' + periods), curr) |
||
63 | |||
64 | |||
65 | def loadResources(progress_dlg=None): |
||
66 | curr = 0 |
||
67 | max = len(glob.glob(resources.get('galaxy/*.png'))) \ |
||
68 | + len(glob.glob(resources.get('techs/*.png'))) \ |
||
69 | + len(glob.glob(resources.get('system/*.png'))) \ |
||
70 | + len(glob.glob(resources.get('icons/*.png'))) \ |
||
71 | + len(glob.glob(resources.get('buttons/*.png'))) |
||
72 | if progress_dlg: |
||
73 | progress_dlg.display(_('Loading resources'), 0, max) |
||
74 | # load star imgs |
||
75 | global smallStarImgs |
||
76 | smallStarImgs = {} |
||
77 | for filename in glob.glob(resources.get('galaxy/star_*.png')): |
||
78 | name = re.search("star_([^.]+).png", filename).group(1) |
||
79 | smallStarImgs[name] = pygame.image.load(filename).convert_alpha() |
||
80 | curr += 1 |
||
81 | updateProgress(curr, progress_dlg) |
||
82 | # load tech imgs |
||
83 | global techImgs |
||
84 | techImgs = {} |
||
85 | white = pygame.Surface((37, 37)) |
||
86 | white.fill((255, 255, 255)) |
||
87 | white.set_alpha(64) |
||
88 | red = pygame.Surface((37, 37)) |
||
89 | red.fill((255, 0, 0)) |
||
90 | red.set_alpha(64) |
||
91 | for filename in glob.glob(resources.get('techs/????.png')): |
||
92 | name = os.path.splitext(os.path.basename(filename))[0] |
||
93 | imgID = int(name) |
||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
![]() |
|||
94 | techImgs[imgID] = pygame.image.load(filename).convert_alpha() |
||
95 | copyImg = techImgs[imgID].convert_alpha() |
||
96 | copyImg.blit(white, (0, 0)) |
||
97 | techImgs[imgID + whiteShift] = copyImg |
||
98 | copyImg = techImgs[imgID].convert_alpha() |
||
99 | copyImg.blit(red, (0, 0)) |
||
100 | techImgs[imgID + redShift] = copyImg |
||
101 | curr += 1 |
||
102 | updateProgress(curr, progress_dlg) |
||
103 | # load big star imgs |
||
104 | global bigStarImgs |
||
105 | bigStarImgs = {} |
||
106 | for filename in glob.glob(resources.get('system/star_*.png')): |
||
107 | name = re.search("star_([^.]+).png", filename).group(1) |
||
108 | bigStarImgs[name] = pygame.image.load(filename).convert_alpha() |
||
109 | curr += 1 |
||
110 | updateProgress(curr, progress_dlg) |
||
111 | # load planet images |
||
112 | global planetImgs |
||
113 | global planetImgCnt |
||
114 | planetImgs = {} |
||
115 | planetImgCnt = {} |
||
116 | for filename in glob.glob(resources.get('system/planet_*.png')): |
||
117 | matchobj = re.search("planet_((.)[^.]+).png", filename) |
||
118 | name = matchobj.group(1) |
||
119 | pltype = matchobj.group(2) |
||
120 | if pltype in planetImgCnt: |
||
121 | planetImgCnt[pltype] += 1 |
||
122 | else: |
||
123 | planetImgCnt[pltype] = 1 |
||
124 | planetImgs[name] = pygame.image.load(filename).convert_alpha() |
||
125 | curr += 1 |
||
126 | updateProgress(curr, progress_dlg) |
||
127 | # load ship imgs |
||
128 | global shipImgs |
||
129 | shipImgs = {} |
||
130 | for filename in glob.glob(resources.get('ships/??.png')): |
||
131 | name = os.path.splitext(os.path.basename(filename))[0] |
||
132 | shipImgs[int(name)] = pygame.image.load(filename).convert_alpha() |
||
133 | curr += 1 |
||
134 | updateProgress(curr, progress_dlg) |
||
135 | # load star imgs |
||
136 | global icons |
||
137 | icons = {} |
||
138 | for filename in glob.glob(resources.get('icons/[!ui_]*.png')): |
||
139 | name = os.path.splitext(os.path.basename(filename))[0] |
||
140 | icons[name] = pygame.image.load(filename).convert_alpha() |
||
141 | curr += 1 |
||
142 | updateProgress(curr, progress_dlg) |
||
143 | # load UI icons |
||
144 | global ui_icons |
||
145 | ui_icons = {} |
||
146 | for filename in glob.glob(resources.get('icons/ui_*.png')): |
||
147 | name = os.path.splitext(os.path.basename(filename))[0] |
||
148 | ui_icons[name] = pygame.image.load(filename).convert_alpha() |
||
149 | curr += 1 |
||
150 | updateProgress(curr, progress_dlg) |
||
151 | # load buttons |
||
152 | global buttonImgs |
||
153 | buttonImgs = {} |
||
154 | for filename in glob.glob(resources.get('buttons/*.png')): |
||
155 | name = os.path.splitext(os.path.basename(filename))[0] |
||
156 | buttonImgs[name] = pygame.image.load(filename).convert_alpha() |
||
157 | curr += 1 |
||
158 | updateProgress(curr, progress_dlg) |
||
159 | # other icons |
||
160 | global cmdInProgressImg |
||
161 | cmdInProgressImg = pygame.image.load(resources.get('cmdInProgress.png')).convert_alpha() |
||
162 | global structProblemImg |
||
163 | structProblemImg = pygame.image.load(resources.get('struct_problem.png')).convert_alpha() |
||
164 | global structOffImg |
||
165 | structOffImg = pygame.image.load(resources.get('struct_off.png')).convert_alpha() |
||
166 | |||
167 | |||
168 | def prepareUIIcons(color): |
||
169 | for image in ui_icons.values(): |
||
170 | image.fill((0, 0, 0, 255), None, pygame.BLEND_RGBA_MULT) |
||
171 | image.fill(color[0:3] + (0, ), None, pygame.BLEND_RGBA_ADD) |
||
172 | |||
173 | |||
174 | def getUIIcon(icon_name): |
||
175 | return ui_icons["ui_" + str(icon_name)] |
||
176 | |||
177 | |||
178 | def getTechImg(techID): |
||
179 | return techImgs.get(techID, techImgs[0]) |
||
180 | |||
181 | |||
182 | def getShipImg(combatClass, isMilitary): |
||
183 | return shipImgs.get(int(combatClass) * 10 + int(isMilitary), shipImgs[99]) |
||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||
184 | |||
185 | |||
186 | def getSmallStarImg(name): |
||
187 | return smallStarImgs[name] |
||
188 | |||
189 | |||
190 | def getBigStarImg(name): |
||
191 | return bigStarImgs[name] |
||
192 | |||
193 | |||
194 | def getPlanetImg(pltype, plid): |
||
195 | global planetImgCnt |
||
196 | name = '%s%d' % (pltype, plid % planetImgCnt[pltype]) |
||
197 | return planetImgs[name] |
||
198 | |||
199 | |||
200 | def getButton(name, state): |
||
201 | if state: |
||
202 | name = "%s_%s" % (name, 'active') |
||
203 | else: |
||
204 | name = "%s_%s" % (name, 'inactive') |
||
205 | return buttonImgs[name] |
||
206 | |||
207 | |||
208 | def getUnknownName(): |
||
209 | return _('[Unknown]') |
||
210 | |||
211 | |||
212 | def getNA(): |
||
213 | return _('N/A') |
||
214 | |||
215 | |||
216 | def getSystemOverviewProblemColor(owner, problem): |
||
217 | if problem: |
||
218 | return gdata.sevColors[gdata.CRI] |
||
219 | else: |
||
220 | return getPlayerColor(owner) |
||
221 | |||
222 | |||
223 | def getFFColorCode(relationship): |
||
224 | if relationship == Const.REL_UNDEF: |
||
225 | return (0xc0, 0xc0, 0xc0) |
||
226 | if relationship == Const.REL_UNITY: |
||
227 | return (0x00, 0xff, 0x00) |
||
228 | relColors = [(0xff, 0x80, 0x80), |
||
229 | (0xff, 0x90, 0x01), |
||
230 | (0xff, 0xff, 0x00), |
||
231 | (0xb0, 0xb0, 0xff), |
||
232 | (0x80, 0xff, 0xff)] |
||
233 | return relColors[bisect.bisect(Const.REL_BOUNDARIES, relationship)] |
||
234 | |||
235 | |||
236 | def getHabitabilityColorCode(value): |
||
237 | if value < 0: |
||
238 | return (0xff, 0x00, 0xff) |
||
239 | colorCodes = [(255, 5 * value, 0), # end 255, 125, 0 |
||
240 | (255 - 2 * (value - 25), 125 + 2 * (value - 25), 0), # end 155, 225, 0 |
||
241 | (155 - 3 * (value - 75), 225, 0), # end 5, 225, 0 |
||
242 | (0, 255, 2 * (value - 125)), # end 0, 225, 250 |
||
243 | (0, 255, 255)] |
||
244 | boundaries = [25, 75, 125, 200] |
||
245 | return colorCodes[bisect.bisect(boundaries, value)] |
||
246 | |||
247 | |||
248 | def getPirateColonyColorCode(pirate): |
||
249 | if pirate is False: |
||
250 | return (0xc0, 0xc0, 0xc0) |
||
251 | elif pirate == 0: |
||
252 | return (0xff, 0x00, 0xff) |
||
253 | else: |
||
254 | # rest is the same as Habitability |
||
255 | return getHabitabilityColorCode(pirate) |
||
256 | |||
257 | def getFameColorCode(fame): |
||
258 | if fame > 0 and fame < 200: |
||
259 | # log.debug(fame, (0xff, 255 - int(255*(fame/200)), 0x00)) |
||
260 | return (0xff, 255 - int(255 * (fame / 200.0)), 0x00) |
||
261 | if fame == 200: |
||
262 | return (0xff, 0x00, 0xff) # pirate colony |
||
263 | return (0xc0, 0xc0, 0xc0) |
||
264 | |||
265 | |||
266 | def getMineralColorCode(minerals): |
||
267 | if minerals >= 0: |
||
268 | # use min, since it we occasionally get 201+ mineral levels, |
||
269 | # but it is so rare that we can ignore it for colors. |
||
270 | return (0xff, max(0, min(255, 255 - int(255 * (minerals / 200.0)))), 0x0) |
||
271 | return (0xc0, 0xc0, 0xc0) |
||
272 | |||
273 | |||
274 | def getSlotColorCode(slots): |
||
275 | if slots > 20: |
||
276 | # in case sometime in the future we have larger worlds available |
||
277 | return (0x0, 0xFF, min(255, (slots - 20) * 10)) |
||
278 | if slots > 0: |
||
279 | return (int(255 * (slots / 20.0)), 255 - int(255 * (slots / 20.0)), 0x0) |
||
280 | return (0xc0, 0xc0, 0xc0) |
||
281 | |||
282 | |||
283 | def getSlotSystemColorCode(slots, planets): |
||
284 | if planets > 0: |
||
285 | slots = int(float(slots) / planets) |
||
286 | if slots > 20: |
||
287 | # in case sometime in the future we have larger worlds available |
||
288 | return (0x0, 0xFF, min(255, (slots - 20) * 10)) |
||
289 | if slots > 0: |
||
290 | return (int(255 * (slots / 20.0)), 255 - int(255 * (slots / 20.0)), 0x0) |
||
291 | return (0xc0, 0xc0, 0xc0) |
||
292 | else: |
||
293 | return (0xc0, 0xc0, 0xc0) |
||
294 | |||
295 | |||
296 | def getStargateColorCode(accel): |
||
297 | accel = accel * 100 - 100 |
||
298 | colorCodes = [(0xc0, 0xc0, 0xc0), |
||
299 | (0xff, 0xff, 0x00), |
||
300 | (0xff, 0xc0, 0x00), |
||
301 | (0xff, 0x60, 0x00), |
||
302 | (0xff, 0x00, 0x00), |
||
303 | (0xff, 0x00, 0x80), |
||
304 | (0xff, 0x00, 0xff)] |
||
305 | boundaries = [1, 50, 150, 250, 350, 450] |
||
306 | return colorCodes[bisect.bisect(boundaries, accel)] |
||
307 | |||
308 | |||
309 | def getDockColorCode(refuel, upgrades): |
||
310 | # refuel is based on best dock; upgrades are based on sum of all docks |
||
311 | # refuel range: 0...6 |
||
312 | # upgrade range: 0...100 (cap 100) |
||
313 | if (refuel > 1 and upgrades > 0) or (refuel > 2): |
||
314 | # refuel > 2 for other player docks since they always read upgrades of 0 |
||
315 | # this probably should be corrected for allies |
||
316 | refuelScale = max(0, min(1, (refuel - 1) / 5.0)) |
||
317 | upgradeScale = max(0, min(1, upgrades / 50)) |
||
318 | return (0xFF, int(refuelScale * (1 - upgradeScale) * 255), int(refuelScale * (upgradeScale) * 255)) |
||
319 | if refuel > 0: |
||
320 | refuelScale = max(0, min(1, refuel / 2.0)) |
||
321 | return (0x00, 100 + 100 * int(refuelScale), 100 + 100 * int(refuelScale)) # cyan |
||
322 | return (0xc0, 0xc0, 0xc0) |
||
323 | |||
324 | |||
325 | def getMoraleColors(morale): |
||
326 | if morale >= 0: |
||
327 | return (255 - int(255 * (morale / 100.0)), int(255 * (morale / 100.0)), 0x0) |
||
328 | return (0xc0, 0xc0, 0xc0) |
||
329 | |||
330 | |||
331 | def getPlayerColor(owner, onlyDiplo = False): |
||
332 | if owner == Const.OID_NONE: |
||
333 | return getFFColorCode(Const.REL_UNDEF) |
||
334 | if not onlyDiplo: |
||
335 | if gdata.config.defaults.highlights == 'yes': |
||
336 | if gdata.playersHighlightColors.has_key(owner): |
||
337 | return gdata.playersHighlightColors[owner] |
||
338 | rel = min(Const.REL_UNDEF, client.getRelationTo(owner)) |
||
339 | return getFFColorCode(rel) |
||
340 | |||
341 | |||
342 | def getControlColor(owner, onlyDiplo = False): |
||
343 | if owner == Const.OID_NONE: |
||
344 | return False |
||
345 | if not onlyDiplo: |
||
346 | if gdata.config.defaults.highlights == 'yes': |
||
347 | if gdata.playersHighlightColors.has_key(owner): |
||
348 | return fadeDarkColor(gdata.playersHighlightColors[owner]) |
||
349 | rel = min(Const.REL_UNDEF, client.getRelationTo(owner)) |
||
350 | return fadeDarkColor(getFFColorCode(rel)) |
||
351 | |||
352 | |||
353 | def getGateLineWidth(owner): |
||
354 | if owner == Const.OID_NONE: |
||
355 | return 1 |
||
356 | rel = min(Const.REL_UNDEF, client.getRelationTo(owner)) |
||
357 | if rel == 1250: |
||
358 | return 2 |
||
359 | return 1 |
||
360 | |||
361 | |||
362 | def getStarmapWidgetPlanetColor(ownerid, bio, mineral, slot, |
||
363 | stargate, dockfuel, dockupgrade, fame, |
||
364 | stratres, morale, pirate=False): |
||
365 | colors = {} |
||
366 | for datatype in gdata.OVERLAY_TYPES: |
||
367 | colors[datatype] = getStarmapWidgetPlanetColorPerDatatype(datatype, ownerid, bio, mineral, slot, |
||
368 | stargate, dockfuel, dockupgrade, fame, |
||
369 | stratres, morale, pirate) |
||
370 | return colors |
||
371 | |||
372 | |||
373 | def getStarmapWidgetSystemColor(ownerid, bio, mineral, slot, |
||
374 | num_planets, stargate, dockfuel, dockupgrade, fame, |
||
375 | stratres, morale, pirate=False): |
||
376 | # systems follow the same logic of the planets |
||
377 | colors = getStarmapWidgetPlanetColor(ownerid, bio, mineral, slot, |
||
378 | stargate, dockfuel, dockupgrade, fame, |
||
379 | stratres, morale, pirate) |
||
380 | # system has only one difference, which we have to override |
||
381 | colors[gdata.OVERLAY_SLOT] = getSlotSystemColorCode(slot, num_planets) |
||
382 | return colors |
||
383 | |||
384 | |||
385 | def getStarmapWidgetPlanetColorPerDatatype(datatype, ownerid, bio, mineral, slot, |
||
386 | stargate, dockfuel, dockupgrade, fame, |
||
387 | stratres, morale, pirate): |
||
388 | if (datatype == gdata.OVERLAY_OWNER): |
||
389 | return getPlayerColor(ownerid) |
||
390 | if (datatype == gdata.OVERLAY_DIPLO): |
||
391 | return getPlayerColor(ownerid, True) |
||
392 | if (datatype == gdata.OVERLAY_BIO): |
||
393 | return getHabitabilityColorCode(bio) |
||
394 | if (datatype == gdata.OVERLAY_MIN): |
||
395 | return getMineralColorCode(mineral) |
||
396 | if (datatype == gdata.OVERLAY_SLOT): |
||
397 | return getSlotColorCode(slot) |
||
398 | if (datatype == gdata.OVERLAY_STARGATE): |
||
399 | return getStargateColorCode(stargate) |
||
400 | if (datatype == gdata.OVERLAY_DOCK): |
||
401 | return getDockColorCode(dockfuel, dockupgrade) |
||
402 | if (datatype == gdata.OVERLAY_FAME): |
||
403 | return getFameColorCode(fame) |
||
404 | if (datatype == gdata.OVERLAY_PIRATECOLONYCOST): |
||
405 | return getPirateColonyColorCode(pirate) |
||
406 | if (datatype == gdata.OVERLAY_MORALE): |
||
407 | return getMoraleColors(morale) |
||
408 | return getPlayerColor(ownerid) # default |
||
409 | |||
410 | |||
411 | def fadeColor(triplet): |
||
412 | return ((triplet[0] + 0xc0) / 2, (triplet[1] + 0xc0) / 2, (triplet[2] + 0xc0) / 2) |
||
413 | |||
414 | |||
415 | def fadeDarkColor(triplet): |
||
416 | return ((triplet[0] + 0x00 * 2) / 3, (triplet[1] + 0x00 * 2) / 3, (triplet[2] + 0x00 * 2) / 3) |
||
417 | |||
418 | |||
419 | def formatTime(time, separator=':'): |
||
420 | time = int(math.ceil(time)) |
||
421 | sign = '' |
||
422 | if time < 0: |
||
423 | time = - time |
||
424 | sign = '-' |
||
425 | days = time / Rules.turnsPerDay |
||
426 | hours = time % Rules.turnsPerDay |
||
427 | return '%s%d%s%02d' % (sign, days, separator, hours) |
||
428 | |||
429 | |||
430 | def formatBE(b, e): |
||
431 | return '%d / %d' % (b, e) |
||
432 | |||
433 | |||
434 | def globalQueueName(index): |
||
435 | return ['Default', 'Red', 'Blue', 'Yellow', 'Violet'][index] |
||
436 |