22fa0a4eb9018ce9e9ac50e87373284af57db9e8
[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     def change_tool(self, label, ref):
134         self.tool = ref
135         self.draw_toolbar()
136
137     def contained(self, node, pos):
138         if pos[0] < node.pos[0] or pos[0] > node.pos[0] + TILE_SIZE:
139             return False
140         if pos[1] < node.pos[1] or pos[1] > node.pos[1] + TILE_SIZE:
141             return False
142         return True
143
144     def change_node(self, node, touch):
145         if self.tool and self.contained(node, touch.pos):
146             tile_pos = (node.pos[0] / TILE_SIZE, node.pos[1] / TILE_SIZE)
147             action = tool_map[self.tool]
148             if self.level_obj.get_tile_type(tile_pos) != action:
149                 self.level_obj.set_tile_type(tile_pos, action)
150                 for map_pos, new_tile in self.level_obj.get_changed_tiles():
151                     node_pos = (map_pos[0] * TILE_SIZE, map_pos[1] * TILE_SIZE)
152                     old_node = self.nodes[node_pos]
153                     self.remove_widget(old_node)
154                     self._draw_tile(node_pos, new_tile)
155
156
157 class EditorApp(App):
158
159     def __init__(self, level):
160         self.level = level
161         super(EditorApp, self).__init__()
162
163     def build(self):
164         editor = EditorWindow(self.level)
165         editor.build()
166         return editor
167
168
169 def main():
170     if len(sys.argv) > 1:
171         level = sys.argv[1]
172     else:
173         level = 'level1.txt'
174     EditorApp(level).run()
175
176
177 if __name__ == '__main__':
178     main()