Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

#!/usr/bin/env python3 

 

import time, pyglet 

from .errors import * 

from .forums import Forum 

from .multiplayer import ClientForum, ServerActor 

 

class Theater: 

""" 

Manage whichever stage is currently active. This involves both 

updating the current stage and handling transitions between stages. 

""" 

 

def __init__(self, initial_stage=None, gui=None): 

self._gui = gui 

self._initial_stage = initial_stage 

self._current_stage = None 

self._current_update = self._update_before_loop 

self._previous_time = time.time() 

 

@property 

def gui(self): 

return self._gui 

 

@gui.setter 

def gui(self, gui): 

if self._current_stage: 

raise ApiUsageError("theater already playing; can't set gui.") 

self._gui = gui 

 

@property 

def initial_stage(self): 

return self._initial_stage 

 

@initial_stage.setter 

def initial_stage(self, stage): 

require_stage(stage) 

if self._current_stage: 

raise ApiUsageError("theater already playing; can't set initial stage.") 

self._initial_stage = stage 

 

@property 

def current_stage(self): 

return self._current_stage 

 

@property 

def is_finished(self): 

return self._current_update == self._update_after_loop 

 

def update(self, dt=None): 

self._current_update(dt) 

 

def exit(self): 

if self._current_stage: 

self._current_stage.on_exit_stage() 

 

self._current_update = self._update_after_loop 

 

 

def _update_before_loop(self, dt): 

self._current_stage = self._initial_stage 

self._current_stage.theater = self 

self._current_stage.on_enter_stage() 

 

self._current_update = self._update_main_loop 

self._current_update(dt) 

 

def _update_main_loop(self, dt): 

if dt is None: 

current_time = time.time() 

dt = current_time - self._previous_time 

self._previous_time = current_time 

 

self._current_stage.on_update_stage(dt) 

 

if self._current_stage.is_finished: 

self._current_stage.on_exit_stage() 

self._current_stage = self._current_stage.successor 

 

if not self._current_stage: 

self.exit() 

else: 

require_stage(self._current_stage) 

self._current_stage.theater = self 

self._current_stage.on_enter_stage() 

 

def _update_after_loop(self, dt): 

raise AssertionError("shouldn't update the theater after the game has ended.") 

 

 

class PygletTheater(Theater): 

 

def play(self, frames_per_sec=50): 

pyglet.clock.schedule_interval(self.update, 1/frames_per_sec) 

pyglet.app.run() 

 

def exit(self): 

super().exit() 

pyglet.app.exit() 

 

 

class Stage: 

 

def __init__(self): 

self.theater = None 

self.successor = None 

self.is_finished = False 

 

@property 

def gui(self): 

return self.theater.gui 

 

def exit_stage(self): 

""" 

Stop this stage from executing once the current update ends. 

""" 

self.is_finished = True 

 

def exit_theater(self): 

""" 

Exit the game once the current update ends. 

""" 

self.theater.exit() 

 

def on_enter_stage(self): 

""" 

Give the stage a chance to set itself up before it is updated for the  

first time. 

""" 

pass 

 

def on_update_stage(self, dt): 

""" 

Give the stage a chance to react to each clock cycle. 

 

The amount of time that passed since the last clock cycle is provided  

as an argument. 

""" 

pass 

 

def on_exit_stage(self): 

""" 

Give the stage a chance to react before it is stopped and the next  

stage is started. 

 

You can define the next stage by setting the Stage.successor attribute.  

If the successor is static, you can just set it in the constructor.  

But if it will differ depending on the context, this method may be a  

good place to calculate it because it is called only once and just  

before the theater queries for the successor. 

""" 

pass 

 

 

class GameStage(Stage): 

 

def __init__(self, world, forum, actors): 

Stage.__init__(self) 

 

from .tokens import require_world; require_world(world) 

from .forums import require_forum; require_forum(forum) 

from .actors import require_actors; require_actors(actors) 

 

self.world = world 

self.forum = forum 

self.actors = actors 

self.successor = None 

 

def on_enter_stage(self): 

""" 

Prepare the actors, the world, and the messaging system to begin  

playing the game. 

 

This method is guaranteed to be called exactly once upon entering the  

game stage. 

""" 

with self.world._unlock_temporarily(): 

self.forum.connect_everyone(self.world, self.actors) 

 

# 1. Setup the forum. 

 

self.forum.on_start_game() 

 

# 2. Setup the world. 

 

with self.world._unlock_temporarily(): 

self.world.on_start_game() 

 

# 3. Setup the actors. Because this is done after the forum and the  

# world have been setup, this signals to the actors that they can  

# send messages and query the game world as usual. 

 

num_players = len(self.actors) - 1 

 

for actor in self.actors: 

actor.on_setup_gui(self.gui) 

 

for actor in self.actors: 

actor.on_start_game(num_players) 

 

def on_update_stage(self, dt): 

""" 

Sequentially update the actors, the world, and the messaging system.  

The theater terminates once all of the actors indicate that they are done. 

""" 

 

for actor in self.actors: 

actor.on_update_game(dt) 

 

self.forum.on_update_game() 

 

with self.world._unlock_temporarily(): 

self.world.on_update_game(dt) 

 

if self.world.has_game_ended(): 

self.exit_stage() 

 

def on_exit_stage(self): 

""" 

Give the actors, the world, and the messaging system a chance to react  

to the end of the game. 

""" 

 

# 1. Let the forum react to the end of the game. Local forums don't  

# react to this, but remote forums take the opportunity to stop  

# trying to extract tokens from messages. 

 

self.forum.on_finish_game() 

 

# 2. Let the actors react to the end of the game. 

 

for actor in self.actors: 

actor.on_finish_game() 

 

# 3. Let the world react to the end of the game. 

 

with self.world._unlock_temporarily(): 

self.world.on_finish_game() 

 

 

class UniplayerGameStage(GameStage): 

 

def __init__(self, world, referee, gui_actor, ai_actors=None): 

forum = Forum() 

actors = [referee, gui_actor] + list(ai_actors or []) 

GameStage.__init__(self, world, forum, actors) 

 

 

class MultiplayerClientGameStage(Stage): 

 

def __init__(self, world, gui_actor, pipe): 

super().__init__() 

self.forum = ClientForum(pipe) 

self.successor = GameStage(world, self.forum, [gui_actor]) 

 

def on_update_stage(self, dt): 

if self.forum.receive_id_from_server(): 

self.exit_stage() 

 

 

class MultiplayerServerGameStage(GameStage): 

 

def __init__(self, world, referee, ai_actors, pipes): 

forum = Forum() 

actors = [referee] + [ServerActor(x) for x in pipes] + ai_actors 

GameStage.__init__(self, world, forum, actors) 

 

 

 

@debug_only 

def require_stage(object): 

require_instance(Stage(), object)