00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072 import micropolisgenericengine
00073 import micropolisrobot
00074 import os
00075 import sys
00076 import random
00077 import math
00078 import array
00079 import time
00080 import tempfile
00081 import socket
00082 from datetime import datetime
00083 import traceback
00084 import re
00085 import cairo
00086 from pyMicropolis.tileEngine import tileengine
00087 import micropolisengine
00088 from turbogears import identity
00089 import cherrypy
00090 import eliza
00091 from micropolis import model
00092 import micropoliszone
00093
00094
00095
00096
00097
00098
00099 UserNameExp = re.compile('^[a-zA-Z0-9_-]+$')
00100
00101 MicropolisCorePath = 'micropolis/MicropolisCore/src'
00102
00103 MicropolisTilesPath = 'micropolis/htdocs/static/images/micropolis_tiles.png'
00104
00105 LoopsPerYear = micropolisengine.PASSES_PER_CITYTIME * micropolisengine.CITYTIMES_PER_YEAR
00106
00107 DefaultAnimateDelay = 250
00108 DefaultPollDelay = 50
00109
00110 SpeedConfigurations = [
00111 {
00112 'speed': 3,
00113 'pollDelay': DefaultPollDelay,
00114 'animateDelay': DefaultAnimateDelay,
00115 'loopsPerSecond': round(LoopsPerYear / 1000.0),
00116 'maxLoopsPerPoll': 100,
00117 },
00118 {
00119 'speed': 3,
00120 'pollDelay': DefaultPollDelay,
00121 'animateDelay': DefaultAnimateDelay,
00122 'loopsPerSecond': round(LoopsPerYear / 500.0),
00123 'maxLoopsPerPoll': 200,
00124 },
00125 {
00126 'speed': 3,
00127 'pollDelay': DefaultPollDelay,
00128 'animateDelay': DefaultAnimateDelay,
00129 'loopsPerSecond': round(LoopsPerYear / 250.0),
00130 'maxLoopsPerPoll': 400,
00131 },
00132 {
00133 'speed': 3,
00134 'pollDelay': DefaultPollDelay,
00135 'animateDelay': DefaultAnimateDelay,
00136 'loopsPerSecond': round(LoopsPerYear / 100.0),
00137 'maxLoopsPerPoll': 600,
00138 },
00139 {
00140 'speed': 3,
00141 'pollDelay': DefaultPollDelay,
00142 'animateDelay': DefaultAnimateDelay,
00143 'loopsPerSecond': round(LoopsPerYear / 50.0),
00144 'maxLoopsPerPoll': 800,
00145 },
00146 {
00147 'speed': 3,
00148 'pollDelay': DefaultPollDelay,
00149 'animateDelay': DefaultAnimateDelay,
00150 'loopsPerSecond': round(0.5 * LoopsPerYear / 15.0),
00151 'maxLoopsPerPoll': 1000,
00152 },
00153 {
00154 'speed': 3,
00155 'pollDelay': DefaultPollDelay,
00156 'animateDelay': DefaultAnimateDelay,
00157 'loopsPerSecond': round(LoopsPerYear / 5.0),
00158 'maxLoopsPerPoll': 2000,
00159 },
00160 {
00161 'speed': 3,
00162 'pollDelay': DefaultPollDelay,
00163 'animateDelay': DefaultAnimateDelay,
00164 'loopsPerSecond': round(LoopsPerYear / 1.0),
00165 'maxLoopsPerPoll': LoopsPerYear * 2,
00166 },
00167 {
00168 'speed': 3,
00169 'pollDelay': DefaultPollDelay,
00170 'animateDelay': DefaultAnimateDelay,
00171 'loopsPerSecond': round(LoopsPerYear / 0.25),
00172 'maxLoopsPerPoll': LoopsPerYear * 5,
00173 },
00174 {
00175 'speed': 3,
00176 'pollDelay': DefaultPollDelay,
00177 'animateDelay': DefaultAnimateDelay,
00178 'loopsPerSecond': round(LoopsPerYear / 0.05),
00179 'maxLoopsPerPoll': LoopsPerYear * 10,
00180 },
00181 ]
00182
00183 CityNames = {
00184 'about': MicropolisCorePath + '/cities/about.xml',
00185 'badnews': MicropolisCorePath + '/cities/badnews.xml',
00186 'bluebird': MicropolisCorePath + '/cities/bluebird.xml',
00187 'bruce': MicropolisCorePath + '/cities/bruce.xml',
00188 'deadwood': MicropolisCorePath + '/cities/deadwood.xml',
00189 'finnigan': MicropolisCorePath + '/cities/finnigan.xml',
00190 'freds': MicropolisCorePath + '/cities/freds.xml',
00191 'haight': MicropolisCorePath + '/cities/haight.xml',
00192 'happisle': MicropolisCorePath + '/cities/happisle.xml',
00193 'joffburg': MicropolisCorePath + '/cities/joffburg.xml',
00194 'kamakura': MicropolisCorePath + '/cities/kamakura.xml',
00195 'kobe': MicropolisCorePath + '/cities/kobe.xml',
00196 'kowloon': MicropolisCorePath + '/cities/kowloon.xml',
00197 'kyoto': MicropolisCorePath + '/cities/kyoto.xml',
00198 'linecity': MicropolisCorePath + '/cities/linecity.xml',
00199 'med_isle': MicropolisCorePath + '/cities/med_isle.xml',
00200 'ndulls': MicropolisCorePath + '/cities/ndulls.xml',
00201 'neatmap': MicropolisCorePath + '/cities/neatmap.xml',
00202 'radial': MicropolisCorePath + '/cities/radial.xml',
00203 'senri': MicropolisCorePath + '/cities/senri.xml',
00204 'southpac': MicropolisCorePath + '/cities/southpac.xml',
00205 'splats': MicropolisCorePath + '/cities/splats.xml',
00206 'wetcity': MicropolisCorePath + '/cities/wetcity.xml',
00207 'yokohama': MicropolisCorePath + '/cities/yokohama.xml',
00208 }
00209
00210 ToolNameToIndex = {
00211 'pacbot': -1,
00212 'residential': 0,
00213 'commercial': 1,
00214 'industrial': 2,
00215 'firestation': 3,
00216 'policestation': 4,
00217 'query': 5,
00218 'wire': 6,
00219 'bulldozer': 7,
00220 'railroad': 8,
00221 'road': 9,
00222 'stadium': 10,
00223 'park': 11,
00224 'seaport': 12,
00225 'coalpower': 13,
00226 'nuclearpower': 14,
00227 'airport': 15,
00228 'network': 16,
00229 'water': 17,
00230 'land': 18,
00231 'forest': 19,
00232 }
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255 AniTileGroupMap = {}
00256 for tileIndex in range(0, micropolisengine.TILE_COUNT):
00257
00258
00259 if tileIndex in AniTileGroupMap:
00260 continue
00261
00262 nextTileIndex = micropolisengine.Micropolis.getNextAnimatedTile(tileIndex)
00263
00264 if nextTileIndex == tileIndex:
00265 continue
00266
00267 curGroup = [tileIndex]
00268 AniTileGroupMap[tileIndex] = curGroup
00269
00270
00271 while True:
00272 if nextTileIndex in AniTileGroupMap:
00273
00274
00275 if nextTileIndex == tileIndex:
00276 break
00277
00278 group = AniTileGroupMap[nextTileIndex]
00279 if group == curGroup:
00280
00281 break
00282
00283
00284 group.extend(curGroup)
00285 for i in curGroup:
00286 AniTileGroupMap[i] = group
00287 break
00288
00289 tileIndex = nextTileIndex
00290 curGroup.append(tileIndex)
00291
00292 AniTileGroupMap[nextTileIndex] = curGroup
00293 nextTileIndex = micropolisengine.Micropolis.getNextAnimatedTile(tileIndex)
00294
00295
00296
00297 AniTileGroups = []
00298 for group in AniTileGroupMap.values():
00299 if group not in AniTileGroups:
00300 AniTileGroups.append(group)
00301
00302 for group in AniTileGroups:
00303 group.sort()
00304
00305 AniTileGroups.sort()
00306
00307
00308
00309 AniTileMap = array.array('i')
00310 for i in range(0, micropolisengine.TILE_COUNT):
00311 AniTileMap.append(i)
00312
00313 for group in AniTileGroups:
00314 baseTile = group[0]
00315 for tile in group:
00316 AniTileMap[tile] = baseTile
00317
00318
00319
00320
00321
00322
00323
00324
00325 def PRINT(*args):
00326 print args
00327
00328
00329 def Now():
00330 return time.time()
00331
00332
00333 def UniqueID(prefix="ID_"):
00334 id = prefix
00335 for i in range(0, 4):
00336 id += "_%06d" % (random.randint(0, 1000000),)
00337 return id
00338
00339
00340
00341
00342
00343
00344 class Session(object):
00345
00346
00347 def __init__(self, controller, sessionID):
00348 self.sessionID = sessionID
00349 self.engine = None
00350 self.messages = []
00351 self.messagesSeen = {}
00352 self.sequence = 0
00353 self.createTime = time.time()
00354 self.lastPollTime = 0
00355 self.lastTouchTime = 0
00356 self.expireDelay = 60 * 10
00357 self.controller = controller
00358 self.userName = None
00359 self.language = None
00360
00361 self.touch()
00362
00363 tileViewCache = array.array('i')
00364 self.tileViewCache = tileViewCache
00365 row = (-1,) * micropolisengine.WORLD_W
00366 for i in range(0, micropolisengine.WORLD_H):
00367 tileViewCache.extend(row)
00368
00369
00370 def __del__(self):
00371 self.setEngine(None)
00372
00373
00374 def touch(self):
00375 self.lastTouchTime = Now()
00376
00377
00378 def handlePoll(self, pollDict):
00379
00380
00381 self.lastPollTime = Now()
00382
00383 self.sequence += 1
00384
00385 self.engine.handlePoll(
00386 pollDict,
00387 self)
00388
00389 result = self.receiveMessages()
00390
00391
00392 return result
00393
00394
00395 def getUser(self):
00396 userName = self.userName
00397 user = None
00398 if userName != None:
00399 try:
00400 user = model.User.query.filter_by(user_name=userName).first()
00401 except: pass
00402 return user
00403
00404
00405 def touchAge(self):
00406 return Now() - self.lastTouchTime
00407
00408
00409 def pollAge(self):
00410 return Now() - self.lastPollTime
00411
00412
00413 def isExpired(self):
00414 return self.pollAge < self.expireDelay
00415
00416
00417 def expire(self):
00418
00419 self.setEngine(None)
00420
00421
00422 def isMessageQueued(self, message, id=None, variable=None):
00423 return (message, variable, id) in self.messagesSeen
00424
00425 def sendMessage(self, msg):
00426
00427
00428 collapse = msg.get('collapse', False)
00429
00430 if collapse:
00431 message = msg.get('message', '')
00432 variable = msg.get('variable', '')
00433 id = msg.get('id', None)
00434 key = (message, variable, id)
00435 messagesSeen = self.messagesSeen
00436 if key in messagesSeen:
00437 messagesSeen[key].update(msg)
00438 else:
00439 messagesSeen[key] = msg
00440 self.messages.append(msg)
00441 else:
00442 self.messages.append(msg)
00443 self.touch()
00444
00445
00446 def receiveMessages(self):
00447 messages = self.messages
00448
00449 self.messages = []
00450 self.messagesSeen = {}
00451
00452 if False and messages:
00453 print "=" * 72
00454 for message in messages:
00455 print message['message'], message.get('variable', ''), message
00456
00457 if False:
00458 print [
00459 (message['message'], message.get('variable', None))
00460 for message in messages
00461 ]
00462
00463 return messages
00464
00465
00466 def setEngine(self, engine):
00467
00468 if self.engine:
00469 self.engine.removeSession(self)
00470 self.engine = engine
00471 if engine:
00472 engine.addSession(self)
00473
00474
00475 def createEngine(self):
00476 if self.engine:
00477 return
00478
00479 engine = CreateTurboGearsEngine(
00480 controller=self.controller)
00481
00482 self.setEngine(engine)
00483
00484
00485
00486
00487
00488
00489 class MicropolisTurboGearsEngine(micropolisgenericengine.MicropolisGenericEngine):
00490
00491
00492 def __init__(
00493 self,
00494 controller=None,
00495 *args,
00496 **kw):
00497
00498 micropolisgenericengine.MicropolisGenericEngine.__init__(self, *args, **kw)
00499
00500 self.controller = controller
00501 self.sessions = []
00502 self.eliza = eliza.eliza()
00503 self.generatedCitySeed = 0
00504 self.historySerial = 0
00505 self.gameMode = None
00506 self.citySource = None
00507 self.cityID = None
00508 self.cityCookie = None
00509
00510
00511
00512 self.pause()
00513 self.initGamePython()
00514
00515
00516 def __repr__(self):
00517 return "<MicropolisTurboGearsEngine>"
00518
00519
00520 def initGamePython(self):
00521
00522 self.sessions = []
00523
00524 self.resourceDir = MicropolisCorePath + '/res'
00525
00526 tengine = tileengine.TileEngine()
00527 self.tengine = tengine
00528
00529 tengine.setBuffer(self.getMapBuffer())
00530 tengine.width = micropolisengine.WORLD_W
00531 tengine.height = micropolisengine.WORLD_H
00532
00533
00534 tengine.tileFormat = tileengine.TILE_FORMAT_SHORT_UNSIGNED
00535 tengine.colBytes = micropolisengine.BYTES_PER_TILE * micropolisengine.WORLD_H
00536 tengine.rowBytes = micropolisengine.BYTES_PER_TILE
00537 tengine.tileMask = micropolisengine.LOMASK
00538
00539 self.tileCount = micropolisengine.TILE_COUNT
00540 self.tileSize = micropolisengine.EDITOR_TILE_SIZE
00541
00542 self.tilesSurface = cairo.ImageSurface.create_from_png(MicropolisTilesPath)
00543 self.tilesWidth = self.tilesSurface.get_width()
00544 self.tilesHeight = self.tilesSurface.get_height()
00545 self.tilesCols = self.tilesWidth / micropolisengine.EDITOR_TILE_SIZE
00546
00547 self.tileSurface = self.tilesSurface.create_similar(cairo.CONTENT_COLOR, self.tileSize, self.tileSize)
00548 self.tileCtx = cairo.Context(self.tileSurface)
00549 self.tileCtx.set_antialias(cairo.ANTIALIAS_NONE)
00550
00551 self.tileMap = AniTileMap
00552
00553 self.tileSizeCache = {}
00554
00555 self.startVirtualSpeed = 2
00556 self.virtualSpeed = self.startVirtualSpeed
00557 self.loopsPerSecond = 100
00558 self.maxLoopsPerPoll = 10000
00559
00560 self.resetCity()
00561
00562 self.loadInitialCity()
00563
00564
00565 def loadInitialCity(self):
00566
00567
00568
00569 cityFileName = MicropolisCorePath + '/cities/haight.xml'
00570
00571 self.loadMetaCity(cityFileName)
00572
00573
00574
00575 self.setSpeed(2)
00576 self.pause()
00577 self.setFunds(1000000000)
00578 self.setCityTax(10)
00579 self.setAutoGoto(False)
00580 self.setEnableDisasters(False)
00581
00582
00583 def startTimer(self):
00584 pass
00585
00586
00587 def stopTimer(self):
00588 pass
00589
00590
00591 def addSession(self, session):
00592 sessions = self.sessions
00593 if session not in sessions:
00594 sessions.append(session)
00595
00596
00597 def removeSession(self, session):
00598 sessions = self.sessions
00599 if session in sessions:
00600 sessions.remove(session)
00601
00602
00603 def sendAllControllerSessions(self, message, exceptSession=None):
00604
00605 for session in self.controller.getSessions():
00606 if session != exceptSession:
00607 try:
00608 session.sendMessage(message)
00609 except Exception, e:
00610 print "======== XXX sendSessions exception:", e
00611 traceback.print_exc(10)
00612
00613
00614
00615 if 'collapse' in message:
00616 del message['collapse']
00617
00618
00619 def sendSessions(self, message):
00620 try:
00621 for session in self.sessions:
00622 session.sendMessage(message)
00623 except Exception, e:
00624 print "======== XXX sendSessions exception:", e
00625 traceback.print_exc(100)
00626
00627
00628
00629 if 'collapse' in message:
00630 del message['collapse']
00631
00632
00633 def updateNotice(
00634 self,
00635 title,
00636 description,
00637 url,
00638 showPicture,
00639 picture,
00640 showMap,
00641 x,
00642 y):
00643
00644 self.sendSessions({
00645 'message': 'update',
00646 'variable': 'notice',
00647 'notice': {
00648 'title': title,
00649 'description': description,
00650 'url': url,
00651 'showPicture': showPicture,
00652 'picture': picture,
00653 'showMap': showMap,
00654 'x': x,
00655 'y': y,
00656 },
00657 'collapse': False,
00658 })
00659
00660
00661 def handleMessage(self, session, messageDict):
00662 message = messageDict.get('message', None)
00663 user = session.getUser()
00664
00665
00666
00667 methodName = 'handleMessage_' + message
00668 method = getattr(self, methodName, None)
00669 if not method:
00670 print "handleMessage: UNKNOWN MESSAGE", message
00671 else:
00672 method(session, messageDict, user)
00673
00674
00675 def handleMessage_disaster(self, session, messageDict, user):
00676 disaster = messageDict.get('disaster', None)
00677
00678
00679 if disaster == 'monster':
00680 self.makeMonster()
00681 elif disaster == 'fire':
00682 self.setFire()
00683 elif disaster == 'flood':
00684 self.makeFlood()
00685 elif disaster == 'meltdown':
00686 self.makeMeltdown()
00687 elif disaster == 'tornado':
00688 self.makeTornado()
00689 elif disaster == 'earthquake':
00690 self.makeEarthquake()
00691 elif disaster == 'eco':
00692 self.heatSteps = 1
00693 self.heatRule = 1
00694 elif disaster == 'melt':
00695 self.heatSteps = 1
00696 self.heatRule = 0
00697 else:
00698 print "Invalid disaster name:", disaster
00699
00700
00701 def handleMessage_setTaxRate(self, session, messageDict, user):
00702 taxRateStr = messageDict.get('taxRate', self.cityTax)
00703 taxRate = self.cityTax
00704 try:
00705 taxRate = int(taxRateStr)
00706 except: pass
00707 taxRate = max(0, min(taxRate, 20))
00708 self.cityTax = taxRate
00709
00710
00711
00712 def handleMessage_setRoadPercent(self, session, messageDict, user):
00713 roadPercent = int(self.roadPercent * 100.0)
00714 roadPercentStr = messageDict.get('roadPercent', roadPercent)
00715 try:
00716 roadPercent = int(roadPercentStr)
00717 except: pass
00718 roadPercent = max(0, min(roadPercent, 100))
00719 self.roadPercent = float(roadPercent) / 100.0
00720 self.roadSpend = int(self.roadPercent * self.roadFund)
00721 self.updateFundEffects()
00722
00723
00724
00725 def handleMessage_setFirePercent(self, session, messageDict, user):
00726 firePercent = int(self.firePercent * 100.0)
00727 firePercentStr = messageDict.get('firePercent', firePercent)
00728 try:
00729 firePercent = int(firePercentStr)
00730 except: pass
00731 firePercent = max(0, min(firePercent, 100))
00732 self.firePercent = float(firePercent) / 100.0
00733 self.fireSpend = int(self.firePercent * self.fireFund)
00734 self.updateFundEffects()
00735
00736
00737
00738 def handleMessage_setPolicePercent(self, session, messageDict, user):
00739 policePercent = int(self.policePercent * 100.0)
00740 policePercentStr = messageDict.get('policePercent', policePercent)
00741 try:
00742 policePercent = int(policePercentStr)
00743 except: pass
00744 policePercent = max(0, min(policePercent, 100))
00745 self.policePercent = float(policePercent) / 100.0
00746 self.policeSpend = int(self.policePercent * self.policeFund)
00747 self.updateFundEffects()
00748
00749
00750
00751 def handleMessage_loadSharedCity(self, session, messageDict, user):
00752 id = messageDict.get('id', None)
00753
00754 if id in CityNames:
00755 cityFileName = CityNames[id]
00756
00757 self.loadMetaCity(cityFileName)
00758
00759
00760 def handleMessage_loadScenario(self, session, messageDict, user):
00761 scenarioStr = messageDict.get('id', 0)
00762
00763 scenario = 0
00764 try:
00765 scenario = int(scenarioStr)
00766 except: pass
00767 if scenario:
00768 self.loadScenario(scenario)
00769
00770
00771 def handleMessage_generateCity(self, session, messageDict, user):
00772 self.generateCityWithSeed(messageDict.get('seed', 0))
00773
00774
00775 def handleMessage_startGame(self, session, messageDict, user):
00776 citySource = messageDict['citySource']
00777 cityID = messageDict['cityID']
00778 title = messageDict['title']
00779 description = messageDict['description']
00780 print "STARTGAME", citySource, cityID, title, description
00781 self.citySource = citySource
00782 self.cityID = cityID
00783 self.cityCookie = None
00784 self.title = title
00785 self.description = description
00786 self.readOnly = False
00787 self.setGameMode('play', user)
00788
00789 if user:
00790 if citySource == 'mycity':
00791 cookie = cityID
00792 city = model.City.query.filter_by(cookie=cookie).first()
00793 if ((not city) or
00794 (city.user_id != user.user_id)):
00795 print "handleMessage_startGame: User tried to start city they do not own", user, city
00796 return
00797 print "USING EXISTING CITY cookie", city.cookie
00798 city.title = title
00799 city.description = description
00800 elif citySource in ('scenario', 'generated', 'sharedcity',):
00801 city = self.saveCityToDatabase(user, None, title, description)
00802 self.updateSavedCities(session, user)
00803 print "MADE NEW CITY cookie", city.cookie
00804 else:
00805 print "handleMessage_startGame: Unexpected city source", citySource
00806 return
00807
00808 def handleMessage_setGameMode(self, session, messageDict, user):
00809 gameMode = messageDict.get('gameMode')
00810 self.gameMode = gameMode
00811 self.setGameMode(gameMode, user)
00812
00813
00814 def handleMessage_setPaused(self, session, messageDict, user):
00815 paused = messageDict.get('paused')
00816
00817
00818 if paused == True:
00819 self.pause()
00820 elif paused == False:
00821 self.resume()
00822 else:
00823 print "Bad paused value, should be true or false, not", paused
00824
00825
00826 def handleMessage_setVirtualSpeed(self, session, messageDict, user):
00827 virtualSpeed = int(messageDict.get('virtualSpeed'))
00828 speed = max(0, min(virtualSpeed, len(SpeedConfigurations) - 1))
00829
00830
00831 self.setVirtualSpeed(virtualSpeed)
00832
00833
00834 def handleMessage_abandonCity(self, session, messageDict, user):
00835 pass
00836
00837
00838 def handleMessage_saveCity(self, session, messageDict, user):
00839 print "saveCity", session, messageDict, user
00840 if not user:
00841 cookie = messageDict['cookie']
00842 title = messageDict['title']
00843 description = messageDict['description']
00844 source = messageDict['source']
00845 if source != 'mycity':
00846 cookie = None
00847 self.saveCityToDatabase(user, cookie, title, description)
00848 self.updateSavedCities(session, user)
00849
00850
00851 def handleMessage_checkPointCity(self, session, messageDict, user):
00852 print "checkPointCity", session, messageDict, user
00853 if user:
00854
00855 cookie = messageDict['cookie']
00856 title = messageDict['title']
00857 description = messageDict['description']
00858 source = messageDict['source']
00859 if source != 'mycity':
00860 cookie = None
00861 self.saveCityToDatabase(user, cookie, title, description)
00862 self.updateSavedCities(session, user)
00863
00864
00865 def handleMessage_setMyCityTitle(self, session, messageDict, user):
00866 print "SETMYCITYTITLE", messageDict, user
00867 if user:
00868 city = None
00869 try:
00870 cookie = messageDict['cookie']
00871 city = model.City.query.filter_by(cookie=cookie).first()
00872 except Exception, e:
00873 print "City query error 1", e
00874 if city:
00875 if city.user_id != user.user_id:
00876 print "User tried to set title of city they do not own", user, city
00877 else:
00878 title = messageDict['title']
00879 city.title = unicode(title)
00880
00881
00882 def handleMessage_setMyCityDescription(self, session, messageDict, user):
00883 print "SETMYCITYDESCRIPTION", messageDict, user
00884 if user:
00885 city = None
00886 try:
00887 cookie = messageDict['cookie']
00888 city = model.City.query.filter_by(cookie=cookie).first()
00889 except Exception, e:
00890 print "City query error 2", e
00891 if city:
00892 if city.user_id != user.user_id:
00893 print "User tried to set description of city they do not own", user, city
00894 else:
00895 description = messageDict['description']
00896 city.description = unicode(description)
00897
00898
00899 def handleMessage_setMyCityShared(self, session, messageDict, user):
00900 print "SETMYCITYSHARED", messageDict, user
00901 if user:
00902 city = None
00903 try:
00904 cookie = messageDict['cookie']
00905 city = model.City.query.filter_by(cookie=cookie).first()
00906 except Exception, e:
00907 print "City query error 3", e
00908 print "CITY", city
00909 if city:
00910 if city.user_id != user.user_id:
00911 print "User tried to set shared flag of city they do not own", user, city
00912 else:
00913 shared = messageDict['shared']
00914 print "before shared", city.shared
00915 city.shared = shared
00916 print "Setting shared", city.shared, type(city.shared)
00917
00918
00919 def handleMessage_deleteMyCity(self, session, messageDict, user):
00920 print "DELETEMYCITY", messageDict, user
00921 if user:
00922 city = None
00923 try:
00924 cookie = messageDict['cookie']
00925 city = model.City.query.filter_by(cookie=cookie).first()
00926 except Exception, e:
00927 print "City query error 4", e
00928 if city:
00929 if city.user_id != user.user_id:
00930 print "User tried to delete city they do not own", user, user.user_id, city, city.user_id
00931 else:
00932 print "DELETE CITY", city
00933 savedCities = user.getSavedCities(session)
00934 id = city.city_id
00935 print "DESTROY", city
00936 for cityData in savedCities:
00937 print cityData['cookie'], cityData['title'], cityData
00938 city.destroy()
00939
00940
00941 def handleMessage_loadMyCity(self, session, messageDict, user):
00942 if user:
00943 city = None
00944 try:
00945 cookie = messageDict['cookie']
00946 city = model.City.query.filter_by(cookie=cookie).first()
00947 except Exception, e:
00948 print "City query error 5", e
00949 if city:
00950 if city.user_id != user.user_id:
00951 print "User tried to delete city they do not own", user, user.user_id, city, city.user_id
00952 else:
00953 saveFile = city.save_file
00954 tempFileName = tempfile.mktemp()
00955 f = open(tempFileName, 'wb')
00956 f.write(saveFile)
00957 f.close()
00958 self.loadCity(tempFileName)
00959 os.remove(tempFileName)
00960
00961
00962 def handleMessage_drawToolStart(self, session, messageDict, user):
00963
00964 tool = messageDict.get('tool')
00965 x = int(messageDict.get('x'))
00966 y = int(messageDict.get('y'))
00967
00968 if ((tool not in ToolNameToIndex) or
00969 (not self.testBounds(x, y))):
00970
00971 print "INVALID ARGUMENT TO DRAWTOOLSTART", tool, x, y
00972
00973 else:
00974
00975 toolIndex = ToolNameToIndex[tool]
00976 if toolIndex < 0:
00977 self.toolDownScripted(toolIndex, x, y)
00978 else:
00979 self.toolDown(toolIndex, x, y)
00980
00981
00982 def handleMessage_drawToolMove(self, session, messageDict, user):
00983
00984 tool = messageDict.get('tool')
00985 x0 = int(messageDict.get('x0'))
00986 y0 = int(messageDict.get('y0'))
00987 x1 = int(messageDict.get('x1'))
00988 y1 = int(messageDict.get('y1'))
00989
00990
00991 if ((tool not in ToolNameToIndex) or
00992 (not self.testBounds(x0, y0)) or
00993 (not self.testBounds(x1, y1))):
00994
00995 print "INVALID ARGUMENT TO DRAWTOOLMOVE", tool, x0, y0, x1, y1
00996
00997 else:
00998
00999 toolIndex = ToolNameToIndex[tool]
01000 if toolIndex < 0:
01001 self.toolDragScripted(toolIndex, x0, y0, x1, y1)
01002 else:
01003 self.toolDrag(toolIndex, x0, y0, x1, y1)
01004
01005
01006 def handleMessage_drawToolStop(self, session, messageDict, user):
01007
01008 tool = messageDict.get('tool')
01009 x = int(messageDict.get('x'))
01010 y = int(messageDict.get('y'))
01011
01012
01013 if ((tool not in ToolNameToIndex) or
01014 (not self.testBounds(x, y))):
01015
01016 print "INVALID ARGUMENT TO DRAWTOOLSTOP", tool, x, y
01017
01018 else:
01019
01020
01021
01022 pass
01023
01024
01025 def handleMessage_sendChatMessage(self, session, messageDict, user):
01026 text = messageDict['text']
01027 channel = messageDict['channel']
01028 language = messageDict['language']
01029
01030 userName = 'anonymous'
01031 user = session.getUser()
01032 if user:
01033 userName = user.user_name
01034
01035
01036
01037 if text and text[0] == '!':
01038
01039 args = text[1:].split()
01040 if not args:
01041 return
01042 command = args[0]
01043 args = args[1:]
01044
01045 if command == 'million':
01046 self.spend(-1000000)
01047 elif command == 'faith':
01048 if len(args) == 0:
01049 session.sendMessage({
01050 'message': 'chatMessage',
01051 'from': 'Micropolis',
01052 'channel': 'all',
01053 'text': 'Current faith is ' + str(self.faith) + '.',
01054 'collapse': False,
01055 })
01056 elif len(args) == 1:
01057 faith = 0
01058 try:
01059 faith = int(args[0])
01060 except: pass
01061 self.faith = faith
01062 session.sendMessage({
01063 'message': 'chatMessage',
01064 'from': 'Micropolis',
01065 'channel': 'all',
01066 'text': 'Changed faith adjustment to ' + str(self.faith) + '.',
01067 'collapse': False,
01068 })
01069
01070 elif channel == 'eliza':
01071
01072 elizaResponse = self.eliza.respond(text)
01073
01074
01075 session.sendMessage({
01076 'message': 'chatMessage',
01077 'from': 'Me',
01078 'channel': channel,
01079 'text': text,
01080 'collapse': False,
01081 })
01082
01083 session.sendMessage({
01084 'message': 'chatMessage',
01085 'from': 'Eliza',
01086 'channel': channel,
01087 'text': elizaResponse,
01088 'collapse': False,
01089 })
01090
01091 elif channel == 'all':
01092
01093 session.sendMessage({
01094 'message': 'chatMessage',
01095 'from': 'You',
01096 'channel': channel,
01097 'text': text,
01098 'collapse': False,
01099 })
01100
01101 self.sendAllControllerSessions(
01102 {
01103 'message': 'chatMessage',
01104 'from': userName,
01105 'channel': channel,
01106 'text': text,
01107 'collapse': False,
01108 },
01109 exceptSession=session)
01110
01111
01112 else:
01113
01114 print "Unknown chat channel:", channel
01115
01116
01117 def handleMessage_tiles(self, session, messageDict, user):
01118
01119 try:
01120 id = messageDict['id']
01121 col = messageDict['col']
01122 row = messageDict['row']
01123 cols = messageDict['cols']
01124 rows = messageDict['rows']
01125 viewX = messageDict['viewX']
01126 viewY = messageDict['viewY']
01127 viewWidth = messageDict['viewWidth']
01128 viewHeight = messageDict['viewHeight']
01129 code = messageDict['code']
01130 except Exception, e:
01131 self.expectationFailed("Invalid parameters: " + str(e))
01132
01133 if True:
01134
01135
01136
01137
01138
01139 if ((col < 0) or
01140 (row < 0) or
01141 (cols <= 0) or
01142 (rows <= 0) or
01143 ((col + cols) > micropolisengine.WORLD_W) or
01144 ((row + rows) > micropolisengine.WORLD_H)):
01145 self.expectationFailed("Invalid parameters.")
01146
01147
01148 tiles = self.tengine.getTileData(
01149 None,
01150 self.tileMap,
01151 col, row,
01152 cols, rows,
01153 code,
01154 session.tileViewCache)
01155
01156
01157
01158
01159
01160
01161
01162 session.sendMessage({
01163 'message': 'update',
01164 'variable': 'tiles',
01165 'view': {
01166 'id': id,
01167 'col': col,
01168 'row': row,
01169 'cols': cols,
01170 'rows': rows,
01171 'viewX': viewX,
01172 'viewY': viewY,
01173 'viewWidth': viewWidth,
01174 'viewHeight': viewHeight,
01175 'tiles': tiles,
01176 'code': code,
01177 },
01178
01179 })
01180
01181
01182 def handleMessage_sprites(self, session, messageDict, user):
01183 if not session.isMessageQueued('update', 'sprites'):
01184
01185 sprites = []
01186 sprite = self.spriteList
01187 while True:
01188
01189 if not sprite:
01190 break
01191 sprites.append({
01192 'type': sprite.type,
01193 'frame': sprite.frame,
01194 'x': sprite.x,
01195 'y': sprite.y,
01196 'width': sprite.width,
01197 'height': sprite.height,
01198 'xOffset': sprite.xOffset,
01199 'yOffset': sprite.yOffset,
01200 'xHot': sprite.xHot,
01201 'yHot': sprite.yHot,
01202 })
01203 sprite = sprite.next
01204
01205
01206 session.sendMessage({
01207 'message': 'update',
01208 'variable': 'sprites',
01209 'sprites': sprites,
01210 'collapse': True,
01211 })
01212
01213
01214 def handleMessage_robots(self, session, messageDict, user):
01215 robots = [
01216 robot.getData()
01217 for robot in self.robots
01218 ]
01219
01220
01221 session.sendMessage({
01222 'message': 'update',
01223 'variable': 'robots',
01224 'robots': robots,
01225 'collapse': True,
01226 })
01227
01228
01229 def handleMessage_historyview(self, session, messageDict, user):
01230
01231
01232 try:
01233 id = messageDict['id']
01234 historyScale = messageDict['scale']
01235 historyCount = messageDict['count']
01236 historyOffset = messageDict['offset']
01237 historyTypes = messageDict['types']
01238 historyWidth = messageDict['width']
01239 historyHeight = messageDict['height']
01240 except Exception, e:
01241 self.expectationFailed("Invalid parameters: " + str(e))
01242
01243 if not session.isMessageQueued('update', 'historyview', id):
01244
01245
01246
01247
01248
01249
01250
01251
01252 def calcScale(maxVal):
01253 if maxVal < 128:
01254 maxVal = 0
01255 if maxVal > 0:
01256 return 128.0 / float(maxVal)
01257 else:
01258 return 1.0
01259
01260 cityTime = self.cityTime
01261 startingYear = self.startingYear
01262 year = int(cityTime / 48) + startingYear
01263 month = int(cityTime % 48) >> 2
01264
01265 getHistoryRange = self.getHistoryRange
01266 getHistory = self.getHistory
01267
01268 resHistoryMin, resHistoryMax = getHistoryRange(
01269 micropolisengine.HISTORY_TYPE_RES,
01270 historyScale)
01271 comHistoryMin, comHistoryMax = getHistoryRange(
01272 micropolisengine.HISTORY_TYPE_COM,
01273 historyScale)
01274 indHistoryMin, indHistoryMax = getHistoryRange(
01275 micropolisengine.HISTORY_TYPE_IND,
01276 historyScale)
01277 allMax = max(resHistoryMax,
01278 max(comHistoryMax,
01279 indHistoryMax))
01280 rciScale = calcScale(allMax)
01281
01282
01283
01284
01285 moneyHistoryMin, moneyHistoryMax = getHistoryRange(
01286 micropolisengine.HISTORY_TYPE_MONEY,
01287 historyScale)
01288 crimeHistoryMin, crimeHistoryMax = getHistoryRange(
01289 micropolisengine.HISTORY_TYPE_CRIME,
01290 historyScale)
01291 pollutionHistoryMin, pollutionHistoryMax = getHistoryRange(
01292 micropolisengine.HISTORY_TYPE_POLLUTION,
01293 historyScale)
01294 moneyScale = calcScale(moneyHistoryMax)
01295 crimeScale = calcScale(crimeHistoryMax)
01296 pollutionScale = calcScale(pollutionHistoryMax)
01297
01298 historyRange = 128.0
01299
01300 valueScales = (
01301 rciScale, rciScale, rciScale,
01302 moneyScale, crimeScale, pollutionScale,
01303 )
01304
01305 valueRanges = (
01306 (resHistoryMin, resHistoryMax,),
01307 (comHistoryMin, comHistoryMax,),
01308 (indHistoryMin, indHistoryMax,),
01309 (moneyHistoryMin, moneyHistoryMax,),
01310 (crimeHistoryMin, crimeHistoryMax,),
01311 (pollutionHistoryMin, pollutionHistoryMax,),
01312 )
01313
01314 histories = []
01315
01316 for historyType in range(0, micropolisengine.HISTORY_TYPE_COUNT):
01317
01318 if historyType not in historyTypes:
01319 histories.append(None)
01320 continue
01321
01322 valueScale = valueScales[historyType]
01323 valueRange = valueRanges[historyType]
01324
01325 values = [
01326 getHistory(
01327 historyType,
01328 historyScale,
01329 historyIndex)
01330 for historyIndex in range(micropolisengine.HISTORY_COUNT - 1, -1, -1)
01331 ]
01332
01333 histories.append({
01334 'historyType': historyType,
01335 'valueScale': valueScale,
01336 'valueRange': valueRange,
01337 'values': values,
01338 })
01339
01340 msg = {
01341 'message': 'update',
01342 'variable': 'historyview',
01343 'id': id,
01344 'scale': historyScale,
01345 'count': historyCount,
01346 'offset': historyOffset,
01347 'types': historyTypes,
01348 'width': historyWidth,
01349 'height': historyHeight,
01350 'year': year,
01351 'month': month,
01352 'histories': histories,
01353 'range': historyRange,
01354 'collapse': True,
01355 }
01356
01357 session.sendMessage(msg)
01358
01359
01360 def handleMessage_login(self, session, messageDict, user):
01361
01362
01363 success = True
01364 feedback = ''
01365
01366 userName = messageDict['userName']
01367 password = messageDict['password']
01368 passwordEncrypted = identity.encrypt_password(password)
01369 fullName = ''
01370 emailAddress = ''
01371 savedCities = []
01372
01373 user = model.User.by_user_name(unicode(userName))
01374
01375 if user and user.password == passwordEncrypted:
01376 success = True
01377 feedback = 'You are logged in.*'
01378 fullName = user.display_name
01379 emailAddress = user.email_address
01380 session.userName = user.user_name
01381 savedCities = user.getSavedCities(session)
01382 else:
01383 success = False
01384 feedback = 'Incorrect user name or password.'
01385 session.userName = None
01386
01387 session.sendMessage({
01388 'message': 'loginResponse',
01389 'success': success,
01390 'feedback': feedback,
01391 'fullName': fullName,
01392 'emailAddress': emailAddress,
01393 'savedCities': savedCities,
01394 })
01395
01396
01397 def handleMessage_logout(self, session, messageDict, user):
01398
01399
01400 loggedIn = user != None
01401 if not loggedIn:
01402 success = False
01403 feedback = 'You are already logged out,*'
01404 else:
01405 session.userName = None
01406 success = True
01407 feedback = 'You are logged out.*'
01408
01409 session.sendMessage({
01410 'message': 'logoutResponse',
01411 'success': success,
01412 'feedback': feedback,
01413 })
01414
01415
01416 def handleMessage_newAccount(self, session, messageDict, user):
01417
01418
01419 success = False
01420 feedback = ''
01421
01422 userName = messageDict['userName']
01423 password1 = messageDict['password1']
01424 password2 = messageDict['password2']
01425 fullName = messageDict['fullName']
01426 emailAddress = messageDict['emailAddress']
01427
01428 if userName == '':
01429 feedback = 'Please enter a user name.'
01430 elif not self.checkUserName(userName):
01431 feedback = 'The user name contains invalid characters.*'
01432 elif password1 == '':
01433 feedback = 'Please enter a password.'
01434 elif model.User.by_user_name(unicode(userName)):
01435 feedback = 'A user of that name already exists.*'
01436 elif password1 != password2:
01437 feedback = 'Please repeat the same password in the next field.'
01438 else:
01439 user = model.User()
01440 user.user_name = unicode(userName)
01441 user.email_address = unicode(emailAddress)
01442 user.display_name = unicode(fullName)
01443 registeredGroup = model.Group.by_name(u'registered')
01444 user.groups.append(registeredGroup)
01445 user.password = unicode(password1)
01446 user.created = datetime.now()
01447 user.activity = datetime.now()
01448 success = True
01449 feedback = 'A new account has been created*'
01450 session.userName = user.user_name
01451
01452 session.sendMessage({
01453 'message': 'newAccountResponse',
01454 'success': success,
01455 'feedback': feedback,
01456 })
01457
01458
01459 def handleMessage_changePassword(self, session, messageDict, user):
01460 passwordOld = messageDict['passwordOld']
01461 passwordOldEncrypted = identity.encrypt_password(passwordOld)
01462 password1 = messageDict['password1']
01463 password2 = messageDict['password2']
01464 feedback = ''
01465 success = False
01466
01467 if not user:
01468 feedback = 'You are not logged in.'
01469 elif passwordOldEncrypted != user.password:
01470 feedback = 'Incorrect old password.'
01471 elif (not password1) or (not password2):
01472 feedback = 'You must enter your new password twice.'
01473 elif password1 != password2:
01474 feedback = 'The new passwords do not match.'
01475 else:
01476 user.password = unicode(password1)
01477 feedback = 'Your password has been changed.'
01478 success = True
01479
01480 session.sendMessage({
01481 'message': 'changePasswordResponse',
01482 'success': success,
01483 'feedback': feedback,
01484 })
01485
01486
01487 def handleMessage_setUserFullName(self, session, messageDict, user):
01488
01489 if user:
01490 fullName = messageDict['fullName']
01491 user.display_name = unicode(fullName)
01492
01493
01494 def handleMessage_setUserEmailAddress(self, session, messageDict, user):
01495
01496 if user:
01497 emailAddress = messageDict['emailAddress']
01498 user.email_address = emailAddress
01499
01500
01501 def handleMessage_setLanguage(self, session, messageDict, user):
01502
01503 session.language = messageDict['language']
01504
01505
01506 def handleMessage_sendRobot(self, session, messageDict, user):
01507 robotID = messageDict['robotID']
01508 command = messageDict['command']
01509 args = messageDict['args']
01510 robot = self.getRobot(robotID)
01511 if robot:
01512 robot.sendCommand(command, args)
01513
01514
01515 def toolDownScripted(self, toolIndex, x, y):
01516
01517 if toolIndex == -1:
01518 toolCost = 1000
01519 if self.totalFunds < toolCost:
01520 self.makeSound("interface", "Sorry", x, y);
01521 else:
01522 self.spend(toolCost)
01523 self.makePacBot(x, y)
01524
01525
01526 def toolDragScripted(self, toolIndex, x0, y0, x1, y1):
01527
01528 pass
01529
01530
01531 def makePacBot(self, x, y):
01532
01533 robot = micropolisrobot.MicropolisRobot_PacBot(
01534 x=(x * 16) + 8,
01535 y=(y * 16) + 8,
01536 direction = random.randint(0, 3) * math.pi / 2)
01537 self.addRobot(robot)
01538
01539
01540 def heartBeat(self):
01541 pass
01542
01543
01544
01545 def saveCityToDatabase(self, user, cookie, title, description):
01546 print "!!!! saveCityToDatabase", user, cookie, title, description
01547
01548 savedCity = cookie and model.City.query.filter_by(cookie=cookie).first()
01549
01550 now = datetime.now()
01551
01552 if savedCity and savedCity.user_id != user.user_id:
01553 print "A user tried to save somebody else's city!", "user", user, "savedCity", savedCity, "savedCity.user_id", savedCity.user_id
01554 savedCity = None
01555
01556 if not savedCity:
01557 savedCity = model.City()
01558 savedCity.user = user
01559
01560 savedCity.created = now
01561 savedCity.makeCookie()
01562
01563 print "Made a new city"
01564
01565 if not title:
01566 title = ''
01567 if not description:
01568 description = ''
01569 saveFile = self.getSaveFileData()
01570 metadata = self.getMetaData()
01571 iconData = self.getMapImageData(1)
01572 thumbnailData = self.getMapImageData(3)
01573
01574 savedCity.title = title
01575 savedCity.description = description
01576 savedCity.modified = now
01577 savedCity.save_file = saveFile
01578 savedCity.metadata = metadata
01579 savedCity.icon = iconData
01580 savedCity.thumbnail = thumbnailData
01581
01582 return savedCity
01583
01584
01585 def updateSavedCities(self, session, user):
01586
01587 self.sendSessions({
01588 'message': 'update',
01589 'variable': 'savedCities',
01590 'savedCities': user.getSavedCities(session),
01591 })
01592
01593
01594
01595 def getMapImageData(self, tileSize):
01596 surface = self.getMapImage(
01597 width=micropolisengine.WORLD_W * tileSize,
01598 height=micropolisengine.WORLD_H * tileSize)
01599 tempFileName = tempfile.mktemp()
01600 surface.write_to_png(tempFileName)
01601 surface.finish()
01602 f = open(tempFileName, 'rb')
01603 data = f.read()
01604 f.close()
01605 os.unlink(tempFileName)
01606 return data
01607
01608
01609 def getSaveFileData(self):
01610 tempFileName = tempfile.mktemp()
01611 self.saveCityAs(tempFileName)
01612 data = open(tempFileName, 'rb').read()
01613 os.remove(tempFileName)
01614
01615 return data
01616
01617
01618 def generateCityWithSeed(self, seed):
01619 if seed == 0:
01620 seed = int(random.getrandbits(31))
01621 self.generatedCitySeed = seed
01622
01623
01624 self.generateSomeCity(seed)
01625
01626
01627 def checkUserName(self, userName):
01628 match = UserNameExp.match(userName)
01629
01630 return match
01631
01632
01633 def setGameMode(self, gameMode, user):
01634 print "setGameMode", gameMode, self.startVirtualSpeed
01635 if gameMode == "start":
01636 self.setVirtualSpeed(0)
01637 self.pause()
01638 elif gameMode == "play":
01639 self.setVirtualSpeed(self.startVirtualSpeed)
01640 self.resetCity()
01641 self.resume()
01642
01643
01644 def setVirtualSpeed(self, virtualSpeed):
01645
01646 self.virtualSpeed = virtualSpeed
01647 speedConfiguration = SpeedConfigurations[virtualSpeed]
01648
01649 self.loopsPerSecond = speedConfiguration['loopsPerSecond']
01650 self.maxLoopsPerPoll = speedConfiguration['maxLoopsPerPoll']
01651 simSpeed = speedConfiguration['speed']
01652 if self.simSpeed != simSpeed:
01653
01654 self.simSpeed = simSpeed
01655
01656 self.handle_update('virtualSpeed')
01657
01658
01659 def tickEngine(self):
01660
01661
01662
01663 now = time.time()
01664 fracTime = now - math.floor(now)
01665
01666 self.blinkFlag = fracTime < 0.5
01667
01668 if not self.simPaused:
01669
01670
01671 elapsed = now - self.lastLoopTime
01672 ticks = int(max(0, math.floor(elapsed * self.loopsPerSecond)))
01673 if ticks:
01674 self.lastLoopTime = now
01675 else:
01676
01677 return
01678
01679
01680 ticks = min(ticks, self.maxLoopsPerPoll)
01681
01682
01683 print ticks,
01684 sys.stdout.flush()
01685
01686 if self.simPasses != ticks:
01687 self.setPasses(ticks)
01688
01689
01690
01691
01692
01693 try:
01694 self.simTick()
01695 except Exception, e:
01696 print "SIMTICK EXCEPTION:", e
01697
01698 self.animateTiles()
01699
01700
01701
01702 if not self.simPaused:
01703 self.updateMapView()
01704
01705
01706
01707
01708
01709
01710
01711
01712
01713
01714
01715 def expectationFailed(
01716 self,
01717 message):
01718
01719 self.fatalError(
01720 417,
01721 message)
01722
01723
01724
01725
01726
01727
01728
01729 def fatalError(
01730 self,
01731 status,
01732 message):
01733
01734 raise cherrypy.HTTPError(
01735 status,
01736 message)
01737
01738
01739 def handlePoll(self, pollDict, session):
01740
01741
01742
01743 messages = pollDict.get('messages', None)
01744 if messages:
01745 for messageDict in messages:
01746
01747 self.handleMessage(session, messageDict)
01748
01749 self.tickEngine()
01750
01751
01752 def renderTiles(
01753 self,
01754 ctx,
01755 tileSize,
01756 col, row,
01757 cols, rows,
01758 alpha):
01759
01760 if False:
01761 self.tengine.renderTiles(
01762 ctx,
01763 self.tilesSurface,
01764 self.tilesWidth,
01765 self.tilesHeight,
01766 None,
01767 None,
01768 tileSize,
01769 col,
01770 row,
01771 cols,
01772 rows,
01773 alpha)
01774 else:
01775 self.renderTilesLazy(
01776 ctx,
01777 tileSize,
01778 col, row,
01779 cols, rows,
01780 alpha)
01781
01782
01783 def renderTilesLazy(
01784 self,
01785 ctx,
01786 tileSize,
01787 col, row,
01788 cols, rows,
01789 alpha):
01790
01791 tileSizeCache = self.tileSizeCache
01792 d = tileSizeCache.get(tileSize, None)
01793 if not d:
01794 d = {
01795 'tileSize': tileSize,
01796 'tileCache': array.array('i', (0, 0, 0, 0,) * (self.tileCount)),
01797 'tileCacheSurfaces': [],
01798 'tileCacheCount': 0,
01799 }
01800 tileSizeCache[tileSize] = d
01801
01802 self.tengine.renderTilesLazy(
01803 ctx,
01804 None,
01805 self.tileMap,
01806 tileSize,
01807 col,
01808 row,
01809 cols,
01810 rows,
01811 alpha,
01812 lambda tile: self.generateTile(tile, d),
01813 d['tileCache'],
01814 d['tileCacheSurfaces'],
01815 None)
01816
01817
01818
01819
01820
01821
01822 def generateTile(
01823 self,
01824 tile,
01825 d):
01826
01827
01828 try:
01829 tileSize = d['tileSize']
01830
01831
01832
01833 sourceTileSize = micropolisengine.EDITOR_TILE_SIZE
01834 tilesSurface = self.tilesSurface
01835 tilesCols = self.tilesCols
01836 maxSurfaceSize = 512
01837
01838
01839
01840
01841
01842
01843
01844 tileColsPerSurface = max(1, int(math.floor(maxSurfaceSize / tileSize)))
01845
01846
01847 tilesPerSurface = tileColsPerSurface * tileColsPerSurface
01848
01849
01850 surfaceSize = tileColsPerSurface * tileSize
01851
01852
01853 cacheTile = d['tileCacheCount']
01854 d['tileCacheCount'] += 1
01855
01856 surfaceIndex = int(math.floor(cacheTile / tilesPerSurface))
01857
01858
01859 tileCacheSurfaces = d['tileCacheSurfaces']
01860 while len(tileCacheSurfaces) <= surfaceIndex:
01861
01862 surface = self.tileSurface.create_similar(cairo.CONTENT_COLOR, surfaceSize, surfaceSize)
01863 tileCacheSurfaces.append(surface)
01864
01865
01866 surface = tileCacheSurfaces[surfaceIndex]
01867 tileOnSurface = cacheTile % tilesPerSurface
01868
01869 tileCol = tileOnSurface % tileColsPerSurface
01870 tileRow = int(math.floor(tileOnSurface / tileColsPerSurface))
01871
01872 tileX = tileCol * tileSize
01873 tileY = tileRow * tileSize
01874
01875 sourceTileCol = tile % tilesCols
01876 sourceTileRow = int(math.floor(tile / tilesCols))
01877
01878
01879
01880 tileCtx = self.tileCtx
01881 tileCtx.set_source_surface(
01882 self.tilesSurface,
01883 -sourceTileCol * sourceTileSize,
01884 -sourceTileRow * sourceTileSize)
01885 tileCtx.paint()
01886
01887 tilesCtx = cairo.Context(surface)
01888 tilesCtx.set_source_surface(tilesSurface, 0, 0)
01889
01890
01891 tilesCtx.save()
01892
01893 x = tileCol * tileSize
01894 y = tileRow * tileSize
01895
01896 tilesCtx.rectangle(
01897 x,
01898 y,
01899 tileSize,
01900 tileSize)
01901 tilesCtx.clip()
01902
01903
01904 fudge = 0
01905
01906 x += fudge
01907 y += fudge
01908
01909 tilesCtx.translate(
01910 x,
01911 y)
01912
01913
01914
01915
01916
01917 zoomScale = float(tileSize) / float(sourceTileSize - 1.0)
01918
01919
01920
01921 tilesCtx.scale(
01922 zoomScale,
01923 zoomScale)
01924
01925 tilesCtx.set_source_surface(
01926 self.tileSurface,
01927 -0.5,
01928 -0.5)
01929 tilesCtx.paint()
01930
01931 tilesCtx.restore()
01932
01933
01934
01935 result = (surfaceIndex, tileX, tileY)
01936
01937 return result
01938
01939 except Exception, e:
01940 print "GENERATE TILE ERROR", e
01941
01942
01943 def getTileData(
01944 self,
01945 col, row,
01946 cols, rows,
01947 code,
01948 **kw):
01949
01950 tiles = self.tengine.getTileData(
01951 None,
01952 self.tileMap,
01953 col, row,
01954 cols, rows,
01955 code,
01956 None)
01957
01958 return tiles
01959
01960
01961 def updateAll(self):
01962
01963 self.handle_update('funds')
01964 self.handle_update('date')
01965 self.handle_update('evaluation')
01966 self.handle_update('paused')
01967 self.handle_update('virtualSpeed')
01968 self.handle_update('demand')
01969 self.handle_update('options')
01970 self.handle_update('gameLevel')
01971 self.handle_update('cityName')
01972 self.updateMapView()
01973
01974
01975 def updateMapView(self):
01976
01977 self.handle_update('map')
01978
01979
01980 def handle_autoGoto(self, x, y):
01981
01982 self.sendSessions({
01983 'message': 'autoGoto',
01984 'x': x,
01985 'y': y,
01986 'collapse': true,
01987 })
01988
01989
01990 def handle_didGenerateMap(self):
01991
01992 self.sendSessions({
01993 'message': 'didGenerateMap',
01994 'seed': self.generatedCitySeed
01995 })
01996 self.resetCity()
01997 self.updateMapView()
01998
01999
02000 def handle_didLoadCity(self):
02001
02002 self.sendSessions({
02003 'message': 'didLoadCity',
02004 })
02005 self.resetCity()
02006 self.updateMapView()
02007
02008
02009 def handle_didLoadScenario(self):
02010
02011 self.sendSessions({
02012 'message': 'didLoadScenario',
02013 'scenario': self.scenario,
02014 })
02015 self.resetCity()
02016 self.updateMapView()
02017
02018
02019 def handle_didSaveCity(self):
02020 print "handle_didSaveCity(self)", (self,)
02021 self.sendSessions({
02022 'message': 'didSaveCity',
02023 })
02024
02025
02026 def handle_didTool(self, name, x, y):
02027
02028 self.sendSessions({
02029 'message': 'didTool',
02030 'name': name,
02031 'x': x,
02032 'y': y,
02033 })
02034
02035
02036 def handle_didntLoadCity(self, msg):
02037
02038 self.sendSessions({
02039 'message': 'didntLoadCity',
02040 'msg': msg,
02041 })
02042
02043
02044 def handle_didntSaveCity(self, msg):
02045 print "handle_didntSaveCity(self, msg)", (self, msg)
02046 self.sendSessions({
02047 'message': 'didntSaveCity',
02048 'msg': msg,
02049 })
02050
02051
02052 def handle_loseGame(self):
02053
02054 self.sendSessions({
02055 'message': 'loseGame',
02056 })
02057
02058
02059 def handle_makeSound(self, channel, sound, x, y):
02060
02061 self.sendSessions({
02062 'message': 'makeSound',
02063 'channel': channel,
02064 'sound': sound,
02065 'x': x,
02066 'y': y,
02067 })
02068
02069
02070 def handle_newGame(self):
02071
02072 self.sendSessions({
02073 'message': 'newGame',
02074 })
02075 self.updateAll()
02076
02077
02078 def handle_playNewCity(self):
02079
02080 self.sendSessions({
02081 'message': 'playNewCity',
02082 })
02083
02084
02085 def handle_reallyStartGame(self):
02086
02087 self.sendSessions({
02088 'message': 'reallyStartGame',
02089 })
02090
02091
02092 def handle_saveCityAs(self):
02093 print "handle_saveCityAs(self)", (self,)
02094 self.sendSessions({
02095 'message': 'saveCityAs',
02096 })
02097
02098
02099 def handle_showBudgetAndWait(self):
02100
02101 self.sendSessions({
02102 'message': 'showBudgetAndWait',
02103 })
02104
02105
02106 def handle_showPicture(self, id):
02107
02108 self.sendSessions({
02109 'message': 'showPicture',
02110 'id': id,
02111 })
02112
02113
02114 def handle_showZoneStatus(self, str, s0, s1, s2, s3, s4, x, y):
02115
02116 self.sendSessions({
02117 'message': 'showZoneStatus',
02118 'str': str,
02119 's0': s0,
02120 's1': s1,
02121 's2': s2,
02122 's3': s3,
02123 's4': s4,
02124 'x': x,
02125 'y': y,
02126 'collapse': True,
02127 })
02128
02129
02130 def handle_startEarthquake(self, magnitude):
02131
02132 self.sendSessions({
02133 'message': 'startEarthquake',
02134 'magnitude': magnitude,
02135 })
02136
02137
02138 def handle_stopEarthquake(self):
02139
02140 self.sendSessions({
02141 'message': 'stopEarthquake',
02142 })
02143
02144
02145 def handle_startLoad(self):
02146
02147 self.sendSessions({
02148 'message': 'startLoad',
02149 })
02150
02151
02152 def handle_startScenario(self, scenario):
02153
02154 self.sendSessions({
02155 'message': 'startScenario',
02156 'scenario': scenario,
02157 })
02158
02159
02160 def handle_winGame(self):
02161
02162 self.sendSessions({
02163 'message': 'winGame',
02164 })
02165
02166
02167 def handle_update(self, variable, *args):
02168
02169
02170
02171
02172
02173
02174
02175
02176
02177 try:
02178
02179 message = {
02180 'message': 'update',
02181 'variable': variable,
02182 'args': args,
02183 }
02184
02185 if variable == 'funds':
02186
02187 message['funds'] = self.totalFunds
02188 message['collapse'] = True
02189
02190 elif variable == 'history':
02191
02192 self.historySerial += 1
02193 message['historySerial'] = self.historySerial
02194
02195 elif variable == 'date':
02196
02197 cityTime = self.cityTime
02198 startingYear = self.startingYear
02199 year = int(cityTime / 48) + startingYear
02200 month = int(cityTime % 48) >> 2
02201
02202 message['cityTime'] = cityTime
02203 message['startingYear'] = startingYear
02204 message['year'] = year
02205 message['month'] = month
02206 message['collapse'] = True
02207
02208 elif variable == 'evaluation':
02209
02210 problems = []
02211 for i in range(0, self.countProblems()):
02212 problems.append((
02213 self.getProblemNumber(i),
02214 self.getProblemVotes(i)))
02215
02216 message.update({
02217 'year': self.currentYear(),
02218 'population': self.cityPop,
02219 'migration': self.cityPopDelta,
02220 'assessedValue': self.cityAssessedValue,
02221 'category': self.cityClass,
02222 'gameLevel': self.gameLevel,
02223 'currentScore': self.cityScore,
02224 'annualChange': self.cityScoreDelta,
02225 'goodJob': self.cityYes,
02226 'worstProblems': problems,
02227 'collapse': True,
02228 })
02229
02230 elif variable == 'paused':
02231
02232 paused = self.simPaused and 'true' or 'false'
02233 message['paused'] = paused
02234 message['collapse'] = True
02235
02236 elif variable == 'tick':
02237
02238 pass
02239
02240 elif variable == 'passes':
02241
02242 return
02243
02244 elif variable == 'speed':
02245
02246 return
02247
02248 elif variable == 'delay':
02249
02250 virtualSpeed = self.virtualSpeed
02251 speedConfiguration = SpeedConfigurations[virtualSpeed]
02252
02253 message['pollDelay'] = speedConfiguration['pollDelay']
02254 message['animateDelay'] = speedConfiguration['animateDelay']
02255 message['collapse'] = True
02256
02257 elif variable == 'demand':
02258
02259 resDemand, comDemand, indDemand = self.getDemands()
02260 message['demand'] = {
02261 'resDemand': resDemand,
02262 'comDemand': comDemand,
02263 'indDemand': indDemand,
02264 }
02265 message['collapse'] = True
02266
02267
02268 elif variable == 'options':
02269
02270 message['options'] = {
02271 'disasters': self.enableDisasters,
02272 }
02273 message['collapse'] = True
02274
02275 elif variable == 'gameLevel':
02276
02277 message['gameLevel'] = self.gameLevel
02278 message['collapse'] = True
02279
02280 elif variable == 'cityName':
02281
02282 message['cityName'] = self.cityName
02283 message['collapse'] = True
02284
02285 elif (variable == 'budget') or (variable == 'taxRate'):
02286
02287 budget = {}
02288
02289 budget['taxRate'] = self.cityTax
02290 budget['totalFunds'] = self.totalFunds
02291 budget['taxFund'] = self.taxFund
02292
02293 budget['firePercent'] = math.floor(self.firePercent * 100.0)
02294 budget['fireFund'] = self.fireFund
02295 budget['fireValue'] = self.fireValue
02296
02297 budget['policePercent'] = math.floor(self.policePercent * 100.0)
02298 budget['policeFund'] = self.policeFund
02299 budget['policeValue'] = self.policeValue
02300
02301 budget['roadPercent'] = math.floor(self.roadPercent * 100.0)
02302 budget['roadFund'] = self.roadFund
02303 budget['roadValue'] = self.roadValue
02304
02305 budget['cashFlow'] = (
02306 budget['taxFund'] -
02307 budget['fireValue'] -
02308 budget['policeValue'] -
02309 budget['roadValue']
02310 )
02311
02312 budget['previousFunds'] = budget['totalFunds']
02313 budget['currentFunds'] = budget['cashFlow'] + budget['totalFunds']
02314 budget['taxesCollected'] = budget['taxFund']
02315
02316 message['variable'] = 'budget'
02317 message['budget'] = budget
02318 message['collapse'] = True
02319
02320
02321
02322 elif variable == 'message':
02323
02324
02325 msg = {
02326 'number': args[0],
02327 'x': args[1],
02328 'y': args[2],
02329 'picture': args[3],
02330 'important': args[4],
02331 }
02332
02333 message['msg'] = msg
02334 message['args'] = None
02335
02336 elif variable == 'virtualSpeed':
02337
02338
02339 virtualSpeed = self.virtualSpeed
02340 speedConfiguration = SpeedConfigurations[virtualSpeed]
02341 message['virtualSpeed'] = virtualSpeed
02342 message['pollDelay'] = speedConfiguration['pollDelay']
02343 message['animateDelay'] = speedConfiguration['animateDelay']
02344 message['collapse'] = True
02345
02346
02347
02348 if 'args' in message:
02349 del message['args']
02350
02351 self.sendSessions(message)
02352
02353 except Exception, e:
02354 print '======== XXX handle_update ERROR:', e
02355 traceback.print_exc(10)
02356
02357
02358
02359
02360
02361 def CreateTurboGearsEngine(**params):
02362
02363
02364 engine = MicropolisTurboGearsEngine(**params)
02365
02366 return engine
02367
02368
02369