I feel nothing for you.
[naja.git] / naja / scenes / load_save.py
1 """
2 Load and save scenes.
3 """
4
5 import json
6 import os
7 from datetime import datetime
8
9 import pygame.locals as pgl
10
11 from naja.constants import KEYS
12 from naja.events import SceneChangeEvent, InvalidateTheWorld, LoadGameEvent
13 from naja.gamestate import GameState
14 from naja.options import options
15 from naja.scenes.scene import Scene
16 from naja.sound import sound
17 from naja.widgets.save_slot import SaveSlotWidget
18 from naja.widgets.selector import SelectorWidget
19
20
21 def save_path(path):
22     return os.path.join(options.save_location, *path.split('/'))
23
24
25 def ensure_save_path_exists():
26     location = save_path('')
27     if not os.path.isdir(location):
28         os.makedirs(location)
29
30
31 class SaveGameSlot(object):
32     def __init__(self, slot_num):
33         self.slot_num = slot_num
34
35     def save_path(self):
36         return save_path('slot_%s.json' % (self.slot_num,))
37
38     def get_game_data(self):
39         try:
40             with open(self.save_path(), 'r') as save_file:
41                 return json.load(save_file)
42         except IOError:
43             return None
44         except Exception as e:
45             print "Error reading savegame in slot %s: %s" % (self.slot_num, e)
46             return None
47
48     def save(self, state):
49         ensure_save_path_exists()
50         save_data = {
51             'timestamp': datetime.now().ctime(),
52             'data': state.gameboard.export(),
53         }
54         with open(self.save_path(), 'w') as save_file:
55             json.dump(save_data, save_file)
56
57     def load(self):
58         game_data = self.get_game_data()
59         if game_data is None:
60             return
61         return GameState.load(game_data['data'])
62
63
64 class LoadSaveGameBase(Scene):
65     def __init__(self, state):
66         super(LoadSaveGameBase, self).__init__(state)
67         selector = SelectorWidget()
68         self.add(selector)
69         self.slots = {}
70
71         for slot_num in range(8):
72             slot = self.make_slot_widget(slot_num)
73             self.slots[slot_num] = slot
74             selector.add(slot)
75
76     def make_slot_widget(self, slot_num):
77         game_data = SaveGameSlot(slot_num).get_game_data()
78         y_offset = 74 * slot_num + 8
79         slot = SaveSlotWidget((100, y_offset), slot_num, game_data)
80         slot.add_callback('click', lambda event: self.perform_action(slot_num))
81         return slot
82
83     def handle_scene_event(self, ev):
84         if ev.type == pgl.KEYDOWN and ev.key in KEYS.QUIT:
85             from naja.scenes.menu import MenuScene
86             SceneChangeEvent.post(MenuScene)
87             return
88
89     def fail(self):
90         sound.play_sound('error.ogg')
91         InvalidateTheWorld.post()
92
93     def succeed(self):
94         raise NotImplementedError("Success not implemented.")
95
96     def perform_action(self, slot_num):
97         raise NotImplementedError("Nothing to see here.")
98
99
100 class SaveGameScene(LoadSaveGameBase):
101     def perform_action(self, slot_num):
102         try:
103             SaveGameSlot(slot_num).save(self.state)
104         except Exception as e:
105             print "Error saving game in slot %s: %s" % (slot_num, e)
106             self.fail()
107         else:
108             self.succeed()
109
110     def succeed(self):
111         from naja.scenes.menu import MenuScene
112         sound.play_sound('chirp.ogg', volume=0.5)
113         SceneChangeEvent.post(MenuScene)
114
115
116 class LoadGameScene(LoadSaveGameBase):
117     def perform_action(self, slot_num):
118         state = SaveGameSlot(slot_num).load()
119         if state is not None:
120             LoadGameEvent.post(state)
121             self.succeed()
122         else:
123             self.fail()
124
125     def succeed(self):
126         from naja.scenes.game import GameScene
127         sound.play_sound('chirp.ogg', volume=0.5)
128         SceneChangeEvent.post(GameScene)