1 from copy
import deepcopy
3 from importlib
import import_module
4 from inspect
import isclass
6 from os
.path
import basename
7 from logging
import info
9 from panda3d
.core
import Texture
, TextNode
10 from direct
.gui
.OnscreenImage
import OnscreenImage
11 from direct
.gui
.DirectGui
import DirectButton
, DirectFrame
, DirectEntry
, \
12 YesNoDialog
, DirectOptionMenu
13 from direct
.gui
.DirectGuiGlobals
import FLAT
, NORMAL
14 from direct
.gui
.OnscreenText
import OnscreenText
15 from direct
.showbase
.DirectObject
import DirectObject
16 from pmachines
.items
.item
import Item
17 from pmachines
.editor
.scene_list
import SceneList
18 from pmachines
.editor
.inspector
import Inspector
21 class SceneEditor(DirectObject
):
23 def __init__(self
, json
, json_name
, context
, add_item
, items
):
28 self
.__json
= json
= {
37 self
.__inspector
= None
38 self
.__context
= context
39 self
.__add
_item
= add_item
40 self
._font
= base
.loader
.load_font(
41 'assets/fonts/Hanken-Book.ttf')
43 self
._font
.set_pixels_per_unit(60)
44 self
._font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
45 self
._font
.set_outline((0, 0, 0, 1), .8, .2)
48 'text_font': self
._font
,
49 'text_fg': (.9, .9, .9, 1),
51 'frameColor': (.4, .4, .4, .14),
52 'rolloverSound': loader
.load_sfx(
53 'assets/audio/sfx/rollover.ogg'),
54 'clickSound': loader
.load_sfx(
55 'assets/audio/sfx/click.ogg')}
56 w
, h
, tw
, l
= 1.8, 1, 30, .36
57 self
._frm
= DirectFrame(frameColor
=(.4, .4, .4, .06),
58 frameSize
=(0, w
, 0, h
),
59 parent
=base
.a2dBottomCenter
,
62 _('Filename'), pos
=(l
- .03, h
- .1), parent
=self
._frm
,
63 font
=self
._common
['text_font'],
64 scale
=self
._common
['scale'],
65 fg
=self
._common
['text_fg'],
66 align
=TextNode
.A_right
)
67 self
.__filenamename
_entry
= DirectEntry(
68 scale
=self
._common
['scale'],
72 frameColor
=self
._common
['frameColor'],
73 initialText
=json_name
,
75 text_fg
=self
._common
['text_fg'])
77 _('Name'), pos
=(l
- .03, h
- .2), parent
=self
._frm
,
78 font
=self
._common
['text_font'],
79 scale
=self
._common
['scale'],
80 fg
=self
._common
['text_fg'],
81 align
=TextNode
.A_right
)
82 self
.__name
_entry
= DirectEntry(
83 scale
=self
._common
['scale'],
87 frameColor
=self
._common
['frameColor'],
88 initialText
=json
['name'],
90 text_fg
=self
._common
['text_fg'])
92 _('Description'), pos
=(l
- .03, h
- .3), parent
=self
._frm
,
93 font
=self
._common
['text_font'],
94 scale
=self
._common
['scale'],
95 fg
=self
._common
['text_fg'],
96 align
=TextNode
.A_right
)
97 def add_line_break(txt
, entry
):
98 curpos
= entry
.node().getCursorPosition()
99 entry
.set(txt
[:curpos
]+ "\n" + txt
[curpos
:])
100 entry
.node().setCursorPosition(curpos
+1)
102 self
.__instructions
_entry
= DirectEntry(
103 scale
=self
._common
['scale'],
105 entryFont
=self
._font
,
109 frameColor
=self
._common
['frameColor'],
110 initialText
=json
['instructions'].replace('\\n', '\n'),
112 text_fg
=self
._common
['text_fg'])
113 self
.__instructions
_entry
['command'] = add_line_break
114 self
.__instructions
_entry
['extraArgs'] = [self
.__instructions
_entry
]
115 def load_images_btn(path
, col
):
118 (.6, .6, .6, 1), # ready
119 (1, 1, 1, 1), # press
120 (.8, .8, .8, 1), # rollover
126 (.4, .1, .1, .4)]}[col
]
127 return [self
.__load
_img
_btn
(path
, col
) for col
in colors
]
128 fcols
= (.4, .4, .4, .14), (.3, .3, .3, .05)
130 image
=load_images_btn('exitRight', 'gray'), scale
=.05,
132 parent
=self
._frm
, command
=self
.__on
_close
, state
=NORMAL
, relief
=FLAT
,
134 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
135 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
137 image
=load_images_btn('save', 'gray'), scale
=.05,
139 parent
=self
._frm
, command
=self
.__on
_save
, state
=NORMAL
, relief
=FLAT
,
141 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
142 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
144 image
=load_images_btn('menuList', 'gray'), scale
=.05,
146 parent
=self
._frm
, command
=self
.__on
_scene
_list
, state
=NORMAL
, relief
=FLAT
,
148 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
149 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
150 item_modules
= glob('pmachines/items/*.py')
151 item_modules
= [basename(i
)[:-3] for i
in item_modules
]
152 self
.__new
_items
= {}
153 for item_module
in item_modules
:
154 mod_name
= 'pmachines.items.' + item_module
155 for member
in import_module(mod_name
).__dict
__.values():
156 if isclass(member
) and issubclass(member
, Item
) and \
158 self
.__new
_items
[member
.__name
__] = member
160 _('new item'), pos
=(.02, .46), parent
=self
._frm
,
161 font
=self
._common
['text_font'],
162 scale
=self
._common
['scale'],
163 fg
=self
._common
['text_fg'],
164 align
=TextNode
.A_left
)
167 text
=_('new item'), pos
=(.02, 1, .4), items
=list(self
.__new
_items
.keys()),
168 parent
=self
._frm
, command
=self
.__on
_new
_item
, state
=NORMAL
,
169 relief
=FLAT
, item_relief
=FLAT
,
170 frameColor
=fcols
[0], item_frameColor
=fcols
[0],
171 popupMenu_frameColor
=fcols
[0],
172 popupMarker_frameColor
=fcols
[0],
173 text_font
=self
._font
, text_fg
=(.9, .9, .9, 1),
174 highlightColor
=(.9, .9, .9, .9),
175 item_text_font
=self
._font
, item_text_fg
=(.9, .9, .9, 1),
176 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
177 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
179 image
=load_images_btn('plus', 'gray'), scale
=.05,
181 parent
=self
._frm
, command
=self
.__on
_new
_scene
, state
=NORMAL
, relief
=FLAT
,
183 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
184 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
185 messenger
.send('editor-start')
186 self
.accept('editor-item-click', self
.__on
_item
_click
)
187 self
.accept('editor-inspector-destroy', self
.__on
_inspector
_destroy
)
189 def __on_close(self
):
190 self
.__json
['name'] = self
.__name
_entry
.get()
191 self
.__json
['instructions'] = self
.__instructions
_entry
.get()
192 if self
.__compute
_hash
() == self
.__json
['version']:
195 self
.__dialog
= YesNoDialog(dialogName
='Unsaved changes',
196 text
=_('You have unsaved changes. Really quit?'),
197 command
=self
.__actually
_close
)
198 self
.__dialog
['frameColor'] = (.4, .4, .4, .14)
199 self
.__dialog
['relief'] = FLAT
200 self
.__dialog
.component('text0')['fg'] = (.9, .9, .9, 1)
201 self
.__dialog
.component('text0')['font'] = self
._font
202 for b
in self
.__dialog
.buttonList
:
203 b
['frameColor'] = (.4, .4, .4, .14)
204 b
.component('text0')['fg'] = (.9, .9, .9, 1)
205 b
.component('text0')['font'] = self
._font
206 b
.component('text1')['fg'] = (.9, .1, .1, 1)
207 b
.component('text1')['font'] = self
._font
208 b
.component('text2')['fg'] = (.9, .9, .1, 1)
209 b
.component('text2')['font'] = self
._font
211 def __on_new_item(self
, item
):
214 _item
= self
.__new
_items
[item
](
215 self
.__context
.world
,
216 self
.__context
.plane_node
,
217 self
.__context
.cb_inst
,
218 self
.__context
.curr_bottom
,
219 self
.__context
.repos
,
221 _item
._Item
__editing
= True
222 self
.__add
_item
(_item
)
223 item_json
['class'] = _item
.__class
__.__name
__
224 item_json
['position'] = list(_item
._np
.get_pos())
225 self
.__json
['items'] += [item_json
]
227 def __actually_close(self
, arg
):
230 messenger
.send('editor-stop')
231 self
.__dialog
.cleanup()
234 self
.__json
['name'] = self
.__name
_entry
.get()
235 self
.__json
['instructions'] = self
.__instructions
_entry
.get()
236 self
.__json
['version'] = self
.__compute
_hash
()
237 json_name
= self
.__filenamename
_entry
.get()
238 with
open('assets/scenes/%s.json' % json_name
, 'w') as f
:
239 f
.write(dumps(self
.__json
, indent
=2, sort_keys
=True))
241 def __on_scene_list(self
):
244 def __load_img_btn(self
, path
, col
):
245 img
= OnscreenImage('assets/images/buttons/%s.dds' % path
)
246 img
.set_transparency(True)
251 def __compute_hash(self
):
252 new_dict
= deepcopy(self
.__json
)
253 del new_dict
['version']
254 new_dict_str
= str(dumps(new_dict
, indent
=2, sort_keys
=True))
255 h
= hashlib
.new('sha256')
256 h
.update(new_dict_str
.encode())
257 return h
.hexdigest()[:12]
259 def __on_item_click(self
, item
):
260 if self
.__inspector
and self
.__inspector
.item
== item
: return
262 self
.__inspector
.destroy()
263 self
.__inspector
= Inspector(item
, self
.__items
)
265 def __on_inspector_destroy(self
):
266 self
.__inspector
= None
268 def __on_new_scene(self
):
270 messenger
.send('editor-stop')
271 messenger
.send('new_scene')
276 self
.__inspector
.destroy()
277 self
.ignore('editor-item-click')
278 self
.ignore('editor-inspector-destroy')