Refactor
[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         if tile_pos in self.nodes:
80             self.remove_widget(self.nodes[tile_pos])
81         node = Widget(size=(TILE_SIZE, TILE_SIZE), pos=tile_pos)
82         node.bind(on_touch_down=self.change_node)
83         node.bind(on_touch_move=self.change_node)
84         with node.canvas:
85             Color(1, 1, 1)
86             Rectangle(pos=node.pos, size=node.size, texture=tile.texture)
87         self.add_widget(node)
88         self.nodes[tile_pos] = node
89
90     def draw_toolbar(self):
91         for widget in self.tool_widgets:
92             widget.unbind(on_ref_press=self.change_tool)
93             self.remove_widget(widget)
94         self.tool_widgets = []
95         y = 300
96         for tool in sorted(tool_map):
97             if self.tool == tool:
98                 color = '00cccc'
99             else:
100                 color = 'cccccc'
101             tool_label = Label(text='[color=%s][ref=%s]%s[/ref][/color]' %
102                     (color, tool, tool), markup=True, font_size=24,
103                     pos=(500, y))
104             y -= 50
105             tool_label.bind(on_ref_press=self.change_tool)
106             self.add_widget(tool_label)
107             self.tool_widgets.append(tool_label)
108
109     def draw_save_button(self):
110         save_label = Label(text='[color=ffffff][ref=save]Save[/ref][/color]',
111                 markup=True, font_size=24, pos=(500, -100))
112         save_label.bind(on_ref_press=self.do_save)
113         self.add_widget(save_label)
114
115     def do_save(self, label, ref):
116         try:
117             self.level_obj.validate()
118         except RuntimeError as err:
119             print err
120             popup = Popup(title='Error',
121                     content=Label(text='Level not valid: %s' % err),
122                     size_hint=(.5, .5))
123             popup.open()
124             return
125         save_data = self.level_obj.get_level_data()
126         savefile = file(filepath(self.level), 'w')
127         savefile.write(save_data)
128         savefile.write('\n')
129         savefile.close()
130         popup = Popup(title='Success',
131                 content=Label(text='Level saved to %s' % self.level),
132                 size_hint=(.5, .5))
133         popup.open()
134
135     def change_tool(self, label, ref):
136         self.tool = ref
137         self.draw_toolbar()
138
139     def contained(self, node, pos):
140         if pos[0] < node.pos[0] or pos[0] > node.pos[0] + TILE_SIZE:
141             return False
142         if pos[1] < node.pos[1] or pos[1] > node.pos[1] + TILE_SIZE:
143             return False
144         return True
145
146     def change_node(self, node, touch):
147         if self.tool and self.contained(node, touch.pos):
148             tile_pos = (node.pos[0] / TILE_SIZE, node.pos[1] / TILE_SIZE)
149             action = tool_map[self.tool]
150             if self.level_obj.get_tile_type(tile_pos) != action:
151                 self.level_obj.set_tile_type(tile_pos, action)
152                 for map_pos, new_tile in self.level_obj.get_changed_tiles():
153                     node_pos = (map_pos[0] * TILE_SIZE, map_pos[1] * TILE_SIZE)
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()