a8737e14f9d9017ebf469c3ccd446ccc41282a92
[erdslangetjie.git] / erdslangetjie / editor / editor.py
1 import sys
2 import os
3
4 from erdslangetjie.constants import TILE_SIZE
5
6 from kivy.app import App
7 from kivy.uix.widget import Widget
8 from kivy.uix.floatlayout import FloatLayout
9 from kivy.graphics import Color, Rectangle
10 from kivy.uix.label import Label
11 from kivy.uix.popup import Popup
12 from kivy.utils import platform
13 from kivy.config import Config
14
15 from erdslangetjie.data import filepath, load
16 from erdslangetjie.level import (Level, WALL, GATE, BUTTON,
17         FLOOR, ENTRY, EXIT)
18
19
20 if platform() != 'android':
21     Config.set('graphics', 'width', '1326')
22     Config.set('graphics', 'height', '760')
23
24
25 tool_map = {
26         'button': BUTTON,
27         'gate': GATE,
28         'wall': WALL,
29         'floor': FLOOR,
30         'add entry': ENTRY,
31         'add exit': EXIT
32         }
33
34
35 class EditorWindow(FloatLayout):
36
37     def __init__(self, level):
38         super(EditorWindow, self).__init__(size=(1326, 760))
39         if os.path.exists(filepath(level)):
40             level_data = load(level)
41             self.level = level
42         elif os.path.exists(filepath('levels/' + level)):
43             level_data = load('levels/' + level)
44             self.level = 'levels/' + level
45         else:
46             print 'Unable to find %s - treating this as a new level' % level
47             level_data = load('levels/blank.txt')
48             if 'levels/' in level:
49                 self.level = level
50             else:
51                 self.level = 'levels/' + level
52         self.level_obj = Level(level_data)
53         level_data.close()
54         self.level_obj.load_tiles()
55
56         self.tool = None
57         self.tool_widgets = []
58
59     def build(self):
60         self.clear_widgets()
61         tiles = self.level_obj.get_tiles()
62         bx, by = 0, 0
63         self.nodes = {}
64         for tile_line in tiles:
65             bx = 0
66             for tile in tile_line:
67                 self._draw_tile((bx, by), tile)
68                 bx += TILE_SIZE
69             by += TILE_SIZE
70
71         name = Label(text="Level: [color=00ffcc]%s[/color]" % self.level,
72                 markup=True, font_size=24, pos=(500, 360))
73         self.add_widget(name)
74
75         self.draw_toolbar()
76         self.draw_save_button()
77
78     def _draw_tile(self, tile_pos, tile):
79         node = Widget(size=(TILE_SIZE, TILE_SIZE), pos=tile_pos)
80         node.bind(on_touch_down=self.change_node)
81         node.bind(on_touch_move=self.change_node)
82         with node.canvas:
83             Color(1, 1, 1)
84             Rectangle(pos=node.pos, size=node.size, texture=tile.texture)
85         self.add_widget(node)
86         self.nodes[tile_pos] = node
87
88     def draw_toolbar(self):
89         for widget in self.tool_widgets:
90             widget.unbind(on_ref_press=self.change_tool)
91             self.remove_widget(widget)
92         self.tool_widgets = []
93         y = 300
94         for tool in sorted(tool_map):
95             if self.tool == tool:
96                 color = '00cccc'
97             else:
98                 color = 'cccccc'
99             tool_label = Label(text='[color=%s][ref=%s]%s[/ref][/color]' %
100                     (color, tool, tool), markup=True, font_size=24,
101                     pos=(500, y))
102             y -= 50
103             tool_label.bind(on_ref_press=self.change_tool)
104             self.add_widget(tool_label)
105             self.tool_widgets.append(tool_label)
106
107     def draw_save_button(self):
108         save_label = Label(text='[color=ffffff][ref=save]Save[/ref][/color]',
109                 markup=True, font_size=24, pos=(500, -100))
110         save_label.bind(on_ref_press=self.do_save)
111         self.add_widget(save_label)
112
113     def do_save(self, label, ref):
114         try:
115             self.level_obj.validate()
116         except RuntimeError as err:
117             print err
118             popup = Popup(title='Error',
119                     content=Label(text='Level not valid: %s' % err),
120                     size_hint=(.5, .5))
121             popup.open()
122             return
123         save_data = self.level_obj.get_level_data()
124         savefile = file(filepath(self.level), 'w')
125         savefile.write(save_data)
126         savefile.write('\n')
127         savefile.close()
128         popup = Popup(title='Success',
129                 content=Label(text='Level saved to %s' % self.level),
130                 size_hint=(.5, .5))
131         popup.open()
132
133
134     def change_tool(self, label, ref):
135         self.tool = ref
136         self.draw_toolbar()
137
138     def contained(self, node, pos):
139         if pos[0] < node.pos[0] or pos[0] > node.pos[0] + TILE_SIZE:
140             return False
141         if pos[1] < node.pos[1] or pos[1] > node.pos[1] + TILE_SIZE:
142             return False
143         return True
144
145     def change_node(self, node, touch):
146         if self.tool and self.contained(node, touch.pos):
147             tile_pos = (node.pos[0] / TILE_SIZE, node.pos[1] / TILE_SIZE)
148             action = tool_map[self.tool]
149             if self.level_obj.get_tile_type(tile_pos) != action:
150                 self.level_obj.set_tile_type(tile_pos, action)
151                 for map_pos, new_tile in self.level_obj.get_changed_tiles():
152                     node_pos = (map_pos[0] * TILE_SIZE, map_pos[1] * TILE_SIZE)
153                     old_node = self.nodes[node_pos]
154                     self.remove_widget(old_node)
155                     self._draw_tile(node_pos, new_tile)
156
157
158 class EditorApp(App):
159
160     def __init__(self, level):
161         self.level = level
162         super(EditorApp, self).__init__()
163
164     def build(self):
165         editor = EditorWindow(self.level)
166         editor.build()
167         return editor
168
169
170 def main():
171     if len(sys.argv) > 1:
172         level = sys.argv[1]
173     else:
174         level = 'level1.txt'
175     EditorApp(level).run()
176
177
178 if __name__ == '__main__':
179     main()