Imported Upstream version 2008.1+svn1656
[opeanno-debian-packaging.git] / game / session.py
blob4de4b2dc4895c0793515fc53c576fa9d00d4e532
1 # ###################################################
2 # Copyright (C) 2008 The OpenAnno Team
3 # team@openanno.org
4 # This file is part of OpenAnno.
6 # OpenAnno 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.
11 # This program 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.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the
18 # Free Software Foundation, Inc.,
19 # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 # ###################################################
22 import math
23 import shutil
24 import os
25 import os.path
26 import time
28 import fife
30 import game.main
31 from game.gui.selectiontool import SelectionTool
32 from game.world.building import building
33 from game.world.units.ship import Ship
34 from game.world.player import Player
35 from game.gui.ingamegui import IngameGui
36 from game.gui.ingamekeylistener import IngameKeyListener
37 from game.world.island import Island
38 from game.dbreader import DbReader
39 from game.timer import Timer
40 from game.scheduler import Scheduler
41 from game.manager import SPManager
42 from game.view import View
43 from game.world import World
44 from game.entities import Entities
45 from game.util import livingObject, livingProperty, WorldObject
47 class Session(livingObject):
48 """Session class represents the games main ingame view and controls cameras and map loading.
50 This is the most important class if you are going to hack on OpenAnno, it provides most of
51 the important ingame variables that you will be constantly accessing by game.main.session.x
52 Here's a small list of commonly used attributes:
53 * manager - game.manager instance. Used to execute commands that need to be tick,
54 synchronized check the class for more information.
55 * scheduler - game.scheduler instance. Used to execute timed events that do not effect
56 network game.
57 * view - game.view instance. Used to control the ingame camera.
58 * entities - game.entities instance. used to hold preconstructed dummy classes from the db
59 for later initialization.
60 * ingame_gui - game.gui.ingame_gui instance. Used to controll the ingame gui.
61 * cursor - game.gui.{navigation/cursor/selection/building}tool instance. Used to controll
62 mouse events, check the classes for more info.
63 * selected_instances - Set that holds the currently selected instances (building, units).
65 TUTORIAL:
66 For further digging you should now be checking out the load() function.
67 """
68 timer = livingProperty()
69 manager = livingProperty()
70 scheduler = livingProperty()
71 view = livingProperty()
72 entities = livingProperty()
73 ingame_gui = livingProperty()
74 keylistener = livingProperty()
75 cursor = livingProperty()
76 world = livingProperty()
78 def begin(self):
79 super(Session, self).begin()
81 WorldObject.reset()
83 #game
84 self.timer = Timer()
85 self.manager = SPManager()
86 self.scheduler = Scheduler(self.timer)
87 self.view = View((15, 15))
88 self.entities = Entities()
90 #GUI
91 self.ingame_gui = IngameGui()
92 self.keylistener = IngameKeyListener()
93 self.cursor = SelectionTool()
95 self.selected_instances = set()
96 self.selection_groups = [set()] * 10 # List of sets that holds the player assigned unit groups.
98 #autosave
99 if game.main.settings.savegame.autosaveinterval != 0:
100 game.main.ext_scheduler.add_new_object(self.autosave, self.autosave, game.main.settings.savegame.autosaveinterval * 60, -1)
102 def end(self):
103 self.scheduler.rem_all_classinst_calls(self)
105 self.cursor = None
106 self.keylistener = None
107 self.ingame_gui = None
108 self.entities = None
109 self.view = None
110 self.scheduler = None
111 self.manager = None
112 self.timer = None
113 self.world = None
115 self.selected_instances = None
116 self.selection_groups = None
117 super(Session, self).end()
119 def autosave(self):
120 """Called automatically in an interval"""
121 self.save(game.main.savegamemanager.create_autosave_filename())
122 game.main.savegamemanager.delete_dispensable_savegames(autosaves = True)
124 def quicksave(self):
125 """Called when user presses a hotkey"""
126 self.save(game.main.savegamemanager.create_quicksave_filename())
127 game.main.savegamemanager.delete_dispensable_savegames(quicksaves = True)
129 def quickload(self):
130 """Loads last quicksave"""
131 files = game.main.savegamemanager.get_quicksaves(include_displaynames = False)[0]
132 if len(files) == 0:
133 game.main.showPopup("No quicksaves found", "You need to quicksave before you can quickload.")
134 return
135 files.sort()
136 game.main.loadGame(files[-1])
138 def save(self, savegame):
140 @param savegame: the file, where the game will be saved
142 if os.path.exists(savegame):
143 os.unlink(savegame)
144 shutil.copyfile('content/savegame_template.sqlite', savegame)
146 db = DbReader(savegame)
147 try:
148 print 'STARTING SAVING'
149 db("BEGIN")
150 self.world.save(db)
151 #self.manager.save(db)
152 self.view.save(db)
153 self.ingame_gui.save(db)
155 for instance in self.selected_instances:
156 db("INSERT INTO selected(`group`, id) VALUES(NULL, ?)", instance.getId())
157 for group in xrange(len(self.selection_groups)):
158 for instance in self.selection_groups[group]:
159 db("INSERT INTO selected(`group`, id) VALUES(?, ?)", group, instance.getId())
161 print 'writing metadata'
162 game.main.savegamemanager.write_metadata(db)
163 except Exception, e:
164 print "Save exception", e
165 finally:
166 db("COMMIT")
167 print 'FINISHED SAVING'
169 def record(self, savegame):
170 self.save(savegame)
171 game.main.db("ATTACH ? AS demo", savegame)
172 self.manager.recording = True
174 def stop_record(self):
175 assert(self.manager.recording)
176 self.manager.recording = False
177 game.main.db("DETACH demo")
179 def load(self, savegame, playername = "", playercolor = None):
180 """Loads a map.
181 @param savegame: path to the savegame database.
182 @param playername: string with the playername
183 @param playercolor: game.util.color instance with the player's color
185 db = DbReader(savegame) # Initialize new dbreader
186 self.world = World(db) # Load game.world module (check game/world/__init__.py)
187 if playername != "":
188 self.world.setupPlayer(playername, playercolor) # setup new player
189 self.view.load(db) # load view
190 self.manager.load(db) # load the manager (there might me old scheduled ticks.
191 self.ingame_gui.load(db) # load the old gui positions and stuff
192 #setup view
193 #self.view.center(((self.world.max_x - self.world.min_x) / 2.0), ((self.world.max_y - self.world.min_y) / 2.0))
195 for instance_id in db("SELECT id FROM selected WHERE `group` IS NULL"): # Set old selected instance
196 obj = WorldObject.getObjectById(instance_id[0])
197 self.selected_instances.add(obj)
198 obj.select()
199 for group in xrange(len(self.selection_groups)): # load user defined unit groups
200 for instance_id in db("SELECT id FROM selected WHERE `group` = ?", group):
201 self.selection_groups[group].add(WorldObject.getObjectById(instance_id[0]))
203 self.cursor.apply_select() # Set cursor correctly, menus might need to be opened.
206 TUTORIAL:
207 From here on you should digg into the classes that are loaded above, especially the world class.
208 (game/world/__init__.py). It's where the magic happens and all buildings and units are loaded.
211 def generateMap(self):
212 """Generates a map."""
214 #load map
215 game.main.db("attach ':memory:' as map")
216 #...
217 self.world = World()
219 #setup view
220 self.view.center(((self.world.max_x - self.world.min_x) / 2.0), ((self.world.max_y - self.world.min_y) / 2.0))
222 def speed_set(self, ticks):
223 old = self.timer.ticks_per_second
224 self.timer.ticks_per_second = ticks
225 self.view.map.setTimeMultiplier(float(ticks) / float(game.main.settings.ticks.default))
226 if old == 0 and self.timer.tick_next_time is None: #back from paused state
227 self.timer.tick_next_time = time.time() + (self.paused_time_missing / ticks)
228 elif ticks == 0 or self.timer.tick_next_time is None: #go into paused state or very early speed change (before any tick)
229 self.paused_time_missing = ((self.timer.tick_next_time - time.time()) * old) if self.timer.tick_next_time is not None else None
230 self.timer.tick_next_time = None
231 else:
232 self.timer.tick_next_time = self.timer.tick_next_time + ((self.timer.tick_next_time - time.time()) * old / ticks)
234 def speed_up(self):
235 if self.timer.ticks_per_second in game.main.settings.ticks.steps:
236 i = game.main.settings.ticks.steps.index(self.timer.ticks_per_second)
237 if i + 1 < len(game.main.settings.ticks.steps):
238 self.speed_set(game.main.settings.ticks.steps[i + 1])
239 else:
240 self.speed_set(game.main.settings.ticks.steps[0])
242 def speed_down(self):
243 if self.timer.ticks_per_second in game.main.settings.ticks.steps:
244 i = game.main.settings.ticks.steps.index(self.timer.ticks_per_second)
245 if i > 0:
246 self.speed_set(game.main.settings.ticks.steps[i - 1])
247 else:
248 self.speed_set(game.main.settings.ticks.steps[0])
250 def speed_pause(self):
251 if self.timer.ticks_per_second != 0:
252 self.paused_ticks_per_second = self.timer.ticks_per_second
253 self.speed_set(0)
255 def speed_unpause(self):
256 if self.timer.ticks_per_second == 0:
257 self.speed_set(self.paused_ticks_per_second)
260 def speed_toggle_pause(self):
261 if self.timer.ticks_per_second == 0:
262 self.speed_unpause()
263 else:
264 self.speed_pause()