1212 lines
50 KiB
Python
1212 lines
50 KiB
Python
import ctypes, os, platform
|
|
import sdl2
|
|
|
|
from sys import exit
|
|
|
|
from ui import SCALE_INCREMENT, OIS_WIDTH, OIS_HEIGHT, OIS_FILL
|
|
from renderable import LAYER_VIS_FULL, LAYER_VIS_DIM, LAYER_VIS_NONE
|
|
from ui_art_dialog import NewArtDialog, SaveAsDialog, QuitUnsavedChangesDialog, CloseUnsavedChangesDialog, RevertChangesDialog, ResizeArtDialog, AddFrameDialog, DuplicateFrameDialog, FrameDelayDialog, FrameDelayAllDialog, FrameIndexDialog, AddLayerDialog, DuplicateLayerDialog, SetLayerNameDialog, SetLayerZDialog, PaletteFromFileDialog, ImportFileDialog, ExportFileDialog, SetCameraZoomDialog, ExportOptionsDialog, OverlayImageOpacityDialog
|
|
from ui_game_dialog import NewGameDirDialog, LoadGameStateDialog, SaveGameStateDialog, AddRoomDialog, SetRoomCamDialog, SetRoomEdgeWarpsDialog, SetRoomBoundsObjDialog, RenameRoomDialog
|
|
from ui_info_dialog import PagedInfoDialog
|
|
from ui_file_chooser_dialog import ArtChooserDialog, CharSetChooserDialog, PaletteChooserDialog, PaletteFromImageChooserDialog, RunArtScriptDialog, OverlayImageFileChooserDialog
|
|
from ui_list_operations import LO_NONE, LO_SELECT_OBJECTS, LO_SET_SPAWN_CLASS, LO_LOAD_STATE, LO_SET_ROOM, LO_SET_ROOM_OBJECTS, LO_SET_OBJECT_ROOMS, LO_OPEN_GAME_DIR, LO_SET_ROOM_EDGE_WARP, LO_SET_ROOM_EDGE_WARP, LO_SET_ROOM_EDGE_OBJ, LO_SET_ROOM_CAMERA
|
|
from collision import CT_NONE
|
|
from art import ART_DIR, ART_FILE_EXTENSION
|
|
from key_shifts import NUMLOCK_ON_MAP, NUMLOCK_OFF_MAP
|
|
|
|
BINDS_FILENAME = 'binds.cfg'
|
|
BINDS_TEMPLATE_FILENAME = 'binds.cfg.default'
|
|
|
|
|
|
class InputLord:
|
|
|
|
"sets up key binds and handles input"
|
|
wheel_zoom_amount = 3.0
|
|
keyboard_zoom_amount = 1.0
|
|
|
|
def __init__(self, app):
|
|
self.app = app
|
|
self.ui = self.app.ui
|
|
# read from binds.cfg file or create it from template
|
|
# exec results in edit_binds, a dict whose keys are keys+mods
|
|
# and whose values are bound functions
|
|
self.edit_bind_src = None
|
|
# bad probs if a command isn't in binds.cfg, so just blow it away
|
|
# if the template is newer than it
|
|
# TODO: better solution is find any binds in template but not binds.cfg
|
|
# and add em
|
|
binds_filename = self.app.config_dir + BINDS_FILENAME
|
|
binds_outdated = not os.path.exists(binds_filename) or os.path.getmtime(binds_filename) < os.path.getmtime(BINDS_TEMPLATE_FILENAME)
|
|
if not binds_outdated and os.path.exists(binds_filename):
|
|
exec(open(binds_filename).read())
|
|
self.app.log('Loaded key binds from %s' % binds_filename)
|
|
else:
|
|
default_data = open(BINDS_TEMPLATE_FILENAME).readlines()[1:]
|
|
new_binds = open(binds_filename, 'w')
|
|
new_binds.writelines(default_data)
|
|
new_binds.close()
|
|
self.app.log('Created new key binds file %s' % binds_filename)
|
|
exec(''.join(default_data))
|
|
if not self.edit_bind_src:
|
|
self.app.log('No bind data found, Is binds.cfg.default present?')
|
|
exit()
|
|
# associate key + mod combos with methods
|
|
self.edit_binds = {}
|
|
for bind_string in self.edit_bind_src:
|
|
bind = self.parse_key_bind(bind_string)
|
|
if not bind:
|
|
continue
|
|
# bind data could be a single item (string) or a list/tuple
|
|
bind_data = self.edit_bind_src[bind_string]
|
|
if type(bind_data) is str:
|
|
bind_fnames = ['BIND_%s' % bind_data]
|
|
else:
|
|
bind_fnames = ['BIND_%s' % s for s in bind_data]
|
|
bind_functions = []
|
|
for bind_fname in bind_fnames:
|
|
if not hasattr(self, bind_fname):
|
|
continue
|
|
bind_functions.append(getattr(self, bind_fname))
|
|
self.edit_binds[bind] = bind_functions
|
|
# get controller(s)
|
|
# TODO: use kewl SDL2 gamepad system
|
|
js_init = sdl2.SDL_InitSubSystem(sdl2.SDL_INIT_JOYSTICK)
|
|
if js_init != 0:
|
|
self.app.log("SDL2: Couldn't initialize joystick subsystem, code %s" % js_init)
|
|
return
|
|
sticks = sdl2.SDL_NumJoysticks()
|
|
#self.app.log('%s gamepads found' % sticks)
|
|
self.gamepad = None
|
|
self.gamepad_left_x, self.gamepad_left_y = 0, 0
|
|
# for now, just grab first pad
|
|
if sticks > 0:
|
|
pad = sdl2.SDL_JoystickOpen(0)
|
|
pad_name = sdl2.SDL_JoystickName(pad).decode('utf-8')
|
|
pad_axes = sdl2.SDL_JoystickNumAxes(pad)
|
|
pad_buttons = sdl2.SDL_JoystickNumButtons(pad)
|
|
self.app.log('Gamepad found: %s with %s axes, %s buttons' % (pad_name, pad_axes, pad_buttons))
|
|
self.gamepad = pad
|
|
# before main loop begins, set initial mouse position -
|
|
# SDL_GetMouseState returns 0,0 if the mouse hasn't yet moved
|
|
# in the new window!
|
|
wx, wy = ctypes.c_int(0), ctypes.c_int(0)
|
|
sdl2.SDL_GetWindowPosition(self.app.window, wx, wy)
|
|
wx, wy = int(wx.value), int(wy.value)
|
|
mx, my = ctypes.c_int(0), ctypes.c_int(0)
|
|
sdl2.mouse.SDL_GetGlobalMouseState(mx, my)
|
|
mx, my = int(mx.value), int(my.value)
|
|
self.app.mouse_x, self.app.mouse_y = mx - wx, my - wy
|
|
# set flag so we know whether handle_input's SDL_GetMouseState result
|
|
# is accurate :/
|
|
self.mouse_has_moved = False
|
|
|
|
def parse_key_bind(self, in_string):
|
|
"returns a tuple of (key, mod1, mod2) key bind data from given string"
|
|
shift = False
|
|
alt = False
|
|
ctrl = False
|
|
key = None
|
|
for i in in_string.split():
|
|
if i.lower() == 'shift':
|
|
shift = True
|
|
elif i.lower() == 'alt':
|
|
alt = True
|
|
elif i.lower() == 'ctrl':
|
|
ctrl = True
|
|
else:
|
|
key = i
|
|
return (key, shift, alt, ctrl)
|
|
|
|
def get_bind_functions(self, keysym, shift, alt, ctrl):
|
|
"returns a list of methods for the given key + mods if one exists"
|
|
keystr = sdl2.SDL_GetKeyName(keysym).decode().lower()
|
|
key_data = (keystr, shift, alt, ctrl)
|
|
return self.edit_binds.get(key_data, [])
|
|
|
|
def get_keysym(self, event):
|
|
"get SDL2 keysym from event; right now only used to check numlock variants"
|
|
numlock_on = bool(event.key.keysym.mod & sdl2.KMOD_NUM)
|
|
keysym = event.key.keysym.sym
|
|
# if numlock is on, treat numpad keys like numbers
|
|
if numlock_on and keysym in NUMLOCK_ON_MAP:
|
|
return NUMLOCK_ON_MAP[keysym]
|
|
elif not numlock_on and keysym in NUMLOCK_OFF_MAP:
|
|
return NUMLOCK_OFF_MAP[keysym]
|
|
return keysym
|
|
|
|
def get_command_shortcut(self, command_function):
|
|
for bind in self.edit_bind_src:
|
|
if command_function == self.edit_bind_src[bind]:
|
|
return bind
|
|
return ''
|
|
|
|
def get_menu_items_for_command_function(self, function):
|
|
# search both menus for items; command checks
|
|
buttons = self.ui.art_menu_bar.menu_buttons + self.ui.game_menu_bar.menu_buttons
|
|
items = []
|
|
for button in buttons:
|
|
# skip eg playscii button
|
|
if not hasattr(button, 'menu_data'):
|
|
continue
|
|
for item in button.menu_data.items:
|
|
if function.__name__ == 'BIND_%s' % item.command:
|
|
items.append(item)
|
|
return items
|
|
|
|
def is_command_function_allowed(self, function):
|
|
"returns True if given function's menu bar item is available"
|
|
items = self.get_menu_items_for_command_function(function)
|
|
if not items:
|
|
return True
|
|
# return True if ANY items are active
|
|
for item in items:
|
|
if not item.always_active and item.should_dim(self.app):
|
|
continue
|
|
if item.art_mode_allowed and not self.app.game_mode:
|
|
return True
|
|
if item.game_mode_allowed and self.app.game_mode:
|
|
return True
|
|
return False
|
|
|
|
def handle_input(self):
|
|
app = self.app
|
|
# get and store mouse state
|
|
# (store everything in parent app object so stuff can access it easily)
|
|
mx, my = ctypes.c_int(0), ctypes.c_int(0)
|
|
mouse = sdl2.mouse.SDL_GetMouseState(mx, my)
|
|
app.left_mouse = bool(mouse & sdl2.SDL_BUTTON(sdl2.SDL_BUTTON_LEFT))
|
|
app.middle_mouse = bool(mouse & sdl2.SDL_BUTTON(sdl2.SDL_BUTTON_MIDDLE))
|
|
app.right_mouse = bool(mouse & sdl2.SDL_BUTTON(sdl2.SDL_BUTTON_RIGHT))
|
|
mx, my = int(mx.value), int(my.value)
|
|
# if mouse hasn't moved since init, disregard SDL_GetMouseState
|
|
if self.mouse_has_moved:
|
|
app.mouse_x, app.mouse_y = mx, my
|
|
elif mx != 0 or my != 0:
|
|
self.mouse_has_moved = True
|
|
# relative mouse move state
|
|
mdx, mdy = ctypes.c_int(0), ctypes.c_int(0)
|
|
sdl2.mouse.SDL_GetRelativeMouseState(mdx, mdy)
|
|
if self.mouse_has_moved:
|
|
app.mouse_dx, app.mouse_dy = int(mdx.value), int(mdy.value)
|
|
if app.mouse_dx != 0 or app.mouse_dy != 0:
|
|
app.keyboard_editing = False
|
|
# dragging a dialog?
|
|
if app.left_mouse and self.ui.active_dialog in self.ui.hovered_elements:
|
|
self.ui.active_dialog.update_drag(app.mouse_dx, app.mouse_dy)
|
|
# get keyboard state so later we can directly query keys
|
|
ks = sdl2.SDL_GetKeyboardState(None)
|
|
# get modifier states
|
|
self.shift_pressed, self.alt_pressed, self.ctrl_pressed = False, False, False
|
|
if ks[sdl2.SDL_SCANCODE_LSHIFT] or ks[sdl2.SDL_SCANCODE_RSHIFT]:
|
|
self.shift_pressed = True
|
|
if ks[sdl2.SDL_SCANCODE_LALT] or ks[sdl2.SDL_SCANCODE_RALT]:
|
|
self.alt_pressed = True
|
|
if ks[sdl2.SDL_SCANCODE_LCTRL] or ks[sdl2.SDL_SCANCODE_RCTRL]:
|
|
self.ctrl_pressed = True
|
|
# check and remember capslock as a special mod state
|
|
# (currently only used for text tool entry)
|
|
ms = sdl2.SDL_GetModState()
|
|
self.capslock_on = bool(ms & sdl2.KMOD_CAPS)
|
|
# macOS: treat command as interchangeable with control, is this kosher?
|
|
if platform.system() == 'Darwin' and (ks[sdl2.SDL_SCANCODE_LGUI] or ks[sdl2.SDL_SCANCODE_RGUI]):
|
|
self.ctrl_pressed = True
|
|
if app.capslock_is_ctrl and ks[sdl2.SDL_SCANCODE_CAPSLOCK]:
|
|
self.ctrl_pressed = True
|
|
# pack mods into a tuple to save listing em all out repeatedly
|
|
mods = self.shift_pressed, self.alt_pressed, self.ctrl_pressed
|
|
# get controller state
|
|
if self.gamepad:
|
|
self.gamepad_left_x = sdl2.SDL_JoystickGetAxis(self.gamepad, sdl2.SDL_CONTROLLER_AXIS_LEFTX) / 32768
|
|
self.gamepad_left_y = sdl2.SDL_JoystickGetAxis(self.gamepad, sdl2.SDL_CONTROLLER_AXIS_LEFTY) / -32768
|
|
for event in sdl2.ext.get_events():
|
|
if event.type == sdl2.SDL_QUIT:
|
|
app.should_quit = True
|
|
elif event.type == sdl2.SDL_WINDOWEVENT:
|
|
if event.window.event == sdl2.SDL_WINDOWEVENT_RESIZED:
|
|
# test window we create on init to detect resolution makes
|
|
# SDL think we've resized main app window on first tick!
|
|
if app.updates > 0:
|
|
app.resize_window(event.window.data1, event.window.data2)
|
|
elif event.type == sdl2.SDL_JOYBUTTONDOWN:
|
|
if not app.gw.paused and app.gw.player:
|
|
app.gw.player.button_pressed(event.jbutton.button)
|
|
elif event.type == sdl2.SDL_JOYBUTTONUP:
|
|
if not app.gw.paused and app.gw.player:
|
|
self.app.gw.player.button_unpressed(event.jbutton.button)
|
|
elif event.type == sdl2.SDL_KEYDOWN:
|
|
keysym = self.get_keysym(event)
|
|
# if console is up, pass input to it
|
|
if self.ui.console.visible:
|
|
self.ui.console.handle_input(keysym, *mods)
|
|
# same with dialog box
|
|
elif self.ui.active_dialog and self.ui.active_dialog is self.ui.keyboard_focus_element:
|
|
self.ui.active_dialog.handle_input(keysym, *mods)
|
|
# bail, process no further input
|
|
#sdl2.SDL_PumpEvents()
|
|
#return
|
|
# handle text input if text tool is active
|
|
elif self.ui.selected_tool is self.ui.text_tool and self.ui.text_tool.input_active:
|
|
self.ui.text_tool.handle_keyboard_input(keysym, *mods)
|
|
# see if there's a function for this bind and run it
|
|
else:
|
|
flist = self.get_bind_functions(keysym, *mods)
|
|
for f in flist:
|
|
# don't run any command whose menu bar item's dimmed / not allowed (ie wrong mode)
|
|
if self.is_command_function_allowed(f):
|
|
f()
|
|
# if game mode active, pass to world as well as any binds
|
|
if self.app.game_mode:
|
|
self.app.gw.handle_input(event, *mods)
|
|
# for key up events, use the same binds but handle them special case
|
|
# TODO: once there are enough key up events, figure out a more
|
|
# elegant way than this
|
|
elif event.type == sdl2.SDL_KEYUP:
|
|
keysym = self.get_keysym(event)
|
|
if self.app.game_mode:
|
|
self.app.gw.handle_input(event, *mods)
|
|
# dismiss selector popup
|
|
flist = self.get_bind_functions(keysym, *mods)
|
|
if not flist:
|
|
pass
|
|
elif self.ui.active_dialog:
|
|
# keyup shouldn't have any special meaning in a dialog
|
|
pass
|
|
elif self.BIND_game_grab in flist:
|
|
if self.app.game_mode and not self.ui.active_dialog and self.app.gw.player:
|
|
self.app.gw.player.button_unpressed(0)
|
|
return
|
|
elif self.BIND_toggle_picker in flist:
|
|
# ..but only for default hold-to-show setting
|
|
if self.ui.popup_hold_to_show:
|
|
self.ui.popup.hide()
|
|
elif self.BIND_select_or_paint in flist:
|
|
app.keyboard_editing = True
|
|
if not self.ui.selected_tool is self.ui.text_tool and not self.ui.text_tool.input_active:
|
|
self.app.cursor.finish_paint()
|
|
#
|
|
# mouse events aren't handled by bind table for now
|
|
#
|
|
elif event.type == sdl2.SDL_MOUSEWHEEL:
|
|
ui_wheeled = self.ui.wheel_moved(event.wheel.y)
|
|
if not ui_wheeled:
|
|
if self.app.can_edit:
|
|
if event.wheel.y > 0:
|
|
# only zoom in should track towards cursor
|
|
app.camera.zoom(-self.wheel_zoom_amount,
|
|
towards_cursor=True)
|
|
elif event.wheel.y < 0:
|
|
app.camera.zoom(self.wheel_zoom_amount)
|
|
else:
|
|
self.app.gw.mouse_wheeled(event.wheel.y)
|
|
elif event.type == sdl2.SDL_MOUSEBUTTONUP:
|
|
# "consume" input if UI handled it
|
|
ui_unclicked = self.ui.unclicked(event.button.button)
|
|
if ui_unclicked:
|
|
sdl2.SDL_PumpEvents()
|
|
return
|
|
if self.app.game_mode:
|
|
self.app.gw.unclicked(event.button.button)
|
|
# LMB up: finish paint for most tools, end select drag
|
|
if event.button.button == sdl2.SDL_BUTTON_LEFT:
|
|
if self.ui.selected_tool is self.ui.select_tool and self.ui.select_tool.selection_in_progress:
|
|
self.ui.select_tool.finish_select(self.shift_pressed, self.ctrl_pressed)
|
|
elif not self.ui.selected_tool is self.ui.text_tool and not self.ui.text_tool.input_active:
|
|
app.cursor.finish_paint()
|
|
elif event.type == sdl2.SDL_MOUSEBUTTONDOWN:
|
|
ui_clicked = self.ui.clicked(event.button.button)
|
|
# don't register edit commands if a menu is up
|
|
if ui_clicked or self.ui.menu_bar.active_menu_name or self.ui.active_dialog:
|
|
sdl2.SDL_PumpEvents()
|
|
if self.app.game_mode:
|
|
self.app.gw.last_click_on_ui = True
|
|
return
|
|
# pass clicks through to game world
|
|
if self.app.game_mode:
|
|
if not ui_clicked:
|
|
self.app.gw.clicked(event.button.button)
|
|
# LMB down: start text entry, start select drag, or paint
|
|
elif event.button.button == sdl2.SDL_BUTTON_LEFT:
|
|
if not self.ui.active_art:
|
|
return
|
|
elif self.ui.selected_tool is self.ui.text_tool:
|
|
# text tool: only start entry if click is outside popup
|
|
if not self.ui.text_tool.input_active and \
|
|
not self.ui.popup in self.ui.hovered_elements:
|
|
self.ui.text_tool.start_entry()
|
|
elif self.ui.selected_tool is self.ui.select_tool:
|
|
# select tool: accept clicks if they're outside the popup
|
|
if not self.ui.select_tool.selection_in_progress and \
|
|
(not self.ui.keyboard_focus_element or \
|
|
(self.ui.keyboard_focus_element is self.ui.popup and \
|
|
not self.ui.popup in self.ui.hovered_elements)):
|
|
self.ui.select_tool.start_select()
|
|
else:
|
|
app.cursor.start_paint()
|
|
elif event.button.button == sdl2.SDL_BUTTON_RIGHT:
|
|
if self.app.ui.active_art:
|
|
self.ui.quick_grab()
|
|
# none of the below applies to cases where a dialog is up
|
|
if self.ui.active_dialog:
|
|
sdl2.SDL_PumpEvents()
|
|
return
|
|
# directly query keys we don't want affected by OS key repeat delay
|
|
# TODO: these are hard-coded for the moment, think of a good way
|
|
# to expose this functionality to the key bind system
|
|
def pressing_up(ks):
|
|
return ks[sdl2.SDL_SCANCODE_W] or ks[sdl2.SDL_SCANCODE_UP] or ks[sdl2.SDL_SCANCODE_KP_8]
|
|
def pressing_down(ks):
|
|
return ks[sdl2.SDL_SCANCODE_S] or ks[sdl2.SDL_SCANCODE_DOWN] or ks[sdl2.SDL_SCANCODE_KP_2]
|
|
def pressing_left(ks):
|
|
return ks[sdl2.SDL_SCANCODE_A] or ks[sdl2.SDL_SCANCODE_LEFT] or ks[sdl2.SDL_SCANCODE_KP_4]
|
|
def pressing_right(ks):
|
|
return ks[sdl2.SDL_SCANCODE_D] or ks[sdl2.SDL_SCANCODE_RIGHT] or ks[sdl2.SDL_SCANCODE_KP_6]
|
|
# prevent camera move if: console is up, text input is active, editing
|
|
# is not allowed
|
|
if self.shift_pressed and not self.alt_pressed and not self.ctrl_pressed and not self.ui.console.visible and not self.ui.text_tool.input_active and self.app.can_edit and self.ui.keyboard_focus_element is None:
|
|
if pressing_up(ks):
|
|
app.camera.pan(0, 1, True)
|
|
if pressing_down(ks):
|
|
app.camera.pan(0, -1, True)
|
|
if pressing_left(ks):
|
|
app.camera.pan(-1, 0, True)
|
|
if pressing_right(ks):
|
|
app.camera.pan(1, 0, True)
|
|
if ks[sdl2.SDL_SCANCODE_X]:
|
|
app.camera.zoom(-self.keyboard_zoom_amount, keyboard=True,
|
|
towards_cursor=True)
|
|
if ks[sdl2.SDL_SCANCODE_Z]:
|
|
app.camera.zoom(self.keyboard_zoom_amount, keyboard=True)
|
|
if self.app.can_edit and app.middle_mouse and (app.mouse_dx != 0 or app.mouse_dy != 0):
|
|
app.camera.mouse_pan(app.mouse_dx, app.mouse_dy)
|
|
# game mode: arrow keys and left gamepad stick move player
|
|
if self.app.game_mode and not self.ui.console.visible and not self.ui.active_dialog and self.ui.keyboard_focus_element is None:
|
|
if pressing_up(ks):
|
|
# shift = move selected
|
|
if self.shift_pressed and self.app.can_edit:
|
|
app.gw.move_selected(0, 1, 0)
|
|
elif not self.ctrl_pressed and app.gw.player:
|
|
app.gw.player.move(0, 1)
|
|
if pressing_down(ks):
|
|
if self.shift_pressed and self.app.can_edit:
|
|
app.gw.move_selected(0, -1, 0)
|
|
elif not self.ctrl_pressed and app.gw.player:
|
|
app.gw.player.move(0, -1)
|
|
if pressing_left(ks):
|
|
if self.shift_pressed and self.app.can_edit:
|
|
app.gw.move_selected(-1, 0, 0)
|
|
elif not self.ctrl_pressed and app.gw.player:
|
|
app.gw.player.move(-1, 0)
|
|
if pressing_right(ks):
|
|
if self.shift_pressed and self.app.can_edit:
|
|
app.gw.move_selected(1, 0, 0)
|
|
elif not self.ctrl_pressed and app.gw.player:
|
|
app.gw.player.move(1, 0)
|
|
if abs(self.gamepad_left_x) > 0.15 and app.gw.player:
|
|
app.gw.player.move(self.gamepad_left_x, 0)
|
|
if abs(self.gamepad_left_y) > 0.15 and app.gw.player:
|
|
app.gw.player.move(0, self.gamepad_left_y)
|
|
sdl2.SDL_PumpEvents()
|
|
|
|
def is_key_pressed(self, key):
|
|
"returns True if given key is pressed"
|
|
key = bytes(key, encoding='utf-8')
|
|
scancode = sdl2.keyboard.SDL_GetScancodeFromName(key)
|
|
return sdl2.SDL_GetKeyboardState(None)[scancode]
|
|
|
|
#
|
|
# bind functions
|
|
#
|
|
# function names correspond with key values in binds.cfg
|
|
def BIND_quit(self):
|
|
for art in self.app.art_loaded_for_edit:
|
|
if art.unsaved_changes and self.app.can_edit:
|
|
if self.app.game_mode:
|
|
self.app.exit_game_mode()
|
|
self.ui.set_active_art(art)
|
|
self.ui.open_dialog(QuitUnsavedChangesDialog)
|
|
return
|
|
self.app.should_quit = True
|
|
|
|
def BIND_toggle_console(self):
|
|
self.ui.console.toggle()
|
|
|
|
def BIND_import_file(self):
|
|
self.ui.open_dialog(ImportFileDialog)
|
|
|
|
def BIND_export_file(self):
|
|
self.ui.open_dialog(ExportFileDialog)
|
|
|
|
def BIND_export_file_last(self):
|
|
# if user hasn't exported this session, pick an exporter
|
|
if self.ui.app.exporter:
|
|
# redo export with appropriate filename & last options if they have
|
|
out_filename = self.ui.active_art.filename
|
|
out_filename = os.path.basename(out_filename)
|
|
out_filename = os.path.splitext(out_filename)[0]
|
|
ExportOptionsDialog.do_export(self.ui.app, out_filename,
|
|
self.ui.app.last_export_options)
|
|
else:
|
|
self.ui.open_dialog(ExportFileDialog)
|
|
|
|
def BIND_decrease_ui_scale(self):
|
|
if self.ui.scale > SCALE_INCREMENT * 2:
|
|
self.ui.set_scale(self.ui.scale - SCALE_INCREMENT)
|
|
|
|
def BIND_increase_ui_scale(self):
|
|
# cap UI scale at 2
|
|
if self.ui.scale + SCALE_INCREMENT < 2.0:
|
|
self.ui.set_scale(self.ui.scale + SCALE_INCREMENT)
|
|
|
|
def BIND_toggle_fullscreen(self):
|
|
self.app.toggle_fullscreen()
|
|
|
|
def BIND_decrease_brush_size(self):
|
|
self.ui.selected_tool.decrease_brush_size()
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_increase_brush_size(self):
|
|
self.ui.selected_tool.increase_brush_size()
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_cycle_char_forward(self):
|
|
self.ui.select_char(self.ui.selected_char+1)
|
|
|
|
def BIND_cycle_char_backward(self):
|
|
self.ui.select_char(self.ui.selected_char-1)
|
|
|
|
def BIND_cycle_fg_forward(self):
|
|
self.ui.select_fg(self.ui.selected_fg_color+1)
|
|
|
|
def BIND_cycle_fg_backward(self):
|
|
self.ui.select_fg(self.ui.selected_fg_color-1)
|
|
|
|
def BIND_cycle_bg_forward(self):
|
|
self.ui.select_bg(self.ui.selected_bg_color+1)
|
|
|
|
def BIND_cycle_bg_backward(self):
|
|
self.ui.select_bg(self.ui.selected_bg_color-1)
|
|
|
|
def BIND_cycle_xform_forward(self):
|
|
self.ui.cycle_selected_xform()
|
|
|
|
def BIND_cycle_xform_backward(self):
|
|
self.ui.cycle_selected_xform(True)
|
|
|
|
def BIND_toggle_affects_char(self):
|
|
self.ui.selected_tool.toggle_affects_char()
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_toggle_affects_fg(self):
|
|
self.ui.selected_tool.toggle_affects_fg()
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_toggle_affects_bg(self):
|
|
self.ui.selected_tool.toggle_affects_bg()
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_toggle_affects_xform(self):
|
|
self.ui.selected_tool.toggle_affects_xform()
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_toggle_crt(self):
|
|
self.app.fb.toggle_crt()
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_select_pencil_tool(self):
|
|
self.ui.set_selected_tool(self.ui.pencil_tool)
|
|
|
|
def BIND_select_erase_tool(self):
|
|
self.ui.set_selected_tool(self.ui.erase_tool)
|
|
|
|
def BIND_select_rotate_tool(self):
|
|
self.ui.set_selected_tool(self.ui.rotate_tool)
|
|
|
|
def BIND_select_grab_tool(self):
|
|
self.ui.set_selected_tool(self.ui.grab_tool)
|
|
|
|
def BIND_select_text_tool(self):
|
|
self.ui.set_selected_tool(self.ui.text_tool)
|
|
|
|
def BIND_select_select_tool(self):
|
|
self.ui.set_selected_tool(self.ui.select_tool)
|
|
|
|
def BIND_select_fill_tool(self):
|
|
self.ui.set_selected_tool(self.ui.fill_tool)
|
|
|
|
def BIND_cycle_fill_boundary_mode(self):
|
|
self.ui.cycle_fill_tool_mode()
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_toggle_art_toolbar(self):
|
|
self.ui.art_toolbar.visible = not self.ui.art_toolbar.visible
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_cut_selection(self):
|
|
self.ui.cut_selection()
|
|
# switch to PasteTool
|
|
self.ui.set_selected_tool(self.ui.paste_tool)
|
|
# clear selection
|
|
self.ui.select_none()
|
|
self.ui.tool_settings_changed = True
|
|
|
|
def BIND_copy_selection(self):
|
|
self.ui.copy_selection()
|
|
# switch to PasteTool
|
|
self.ui.set_selected_tool(self.ui.paste_tool)
|
|
# clear selection
|
|
self.ui.select_none()
|
|
self.ui.tool_settings_changed = True
|
|
|
|
def BIND_select_paste_tool(self):
|
|
self.ui.set_selected_tool(self.ui.paste_tool)
|
|
|
|
def BIND_select_none(self):
|
|
if self.app.game_mode:
|
|
self.app.gw.deselect_all()
|
|
else:
|
|
self.ui.select_none()
|
|
|
|
def BIND_cancel(self):
|
|
# context-dependent:
|
|
# game mode: deselect
|
|
# normal painting mode: cancel current selection
|
|
# menu bar active: bail out of current menu
|
|
# either way: bail on image conversion if it's happening
|
|
if self.app.converter:
|
|
self.app.converter.finish(True)
|
|
if self.ui.menu_bar.active_menu_name:
|
|
self.ui.menu_bar.close_active_menu()
|
|
elif self.app.game_mode:
|
|
# bail out of list if it's active
|
|
if self.ui.keyboard_focus_element is self.ui.edit_list_panel:
|
|
self.ui.edit_list_panel.cancel()
|
|
else:
|
|
self.app.gw.deselect_all()
|
|
else:
|
|
self.ui.select_none()
|
|
|
|
def BIND_select_all(self):
|
|
self.ui.select_all()
|
|
|
|
def BIND_select_invert(self):
|
|
self.ui.invert_selection()
|
|
|
|
def BIND_edit_cfg(self):
|
|
self.ui.menu_bar.close_active_menu()
|
|
self.app.edit_cfg()
|
|
|
|
def BIND_erase_selection_or_art(self):
|
|
# if in game mode, delete selected objects
|
|
if self.app.game_mode:
|
|
# operate on a copy of selected objects list,
|
|
# as obj.destroy() removes itself from original
|
|
for obj in self.app.gw.selected_objects[:]:
|
|
# some objects can't be deleted
|
|
if obj.deleteable:
|
|
obj.destroy()
|
|
else:
|
|
self.ui.erase_selection_or_art()
|
|
|
|
def BIND_toggle_game_mode(self):
|
|
if not self.app.can_edit:
|
|
return
|
|
if not self.app.game_mode:
|
|
self.app.enter_game_mode()
|
|
else:
|
|
self.app.exit_game_mode()
|
|
self.app.update_window_title()
|
|
|
|
def BIND_new_game_dir(self):
|
|
self.ui.open_dialog(NewGameDirDialog)
|
|
|
|
def BIND_set_game_dir(self):
|
|
if self.app.can_edit:
|
|
# show available games in list panel
|
|
self.ui.edit_list_panel.set_list_operation(LO_OPEN_GAME_DIR)
|
|
|
|
def BIND_load_game_state(self):
|
|
self.ui.edit_list_panel.set_list_operation(LO_LOAD_STATE)
|
|
|
|
def BIND_save_game_state(self):
|
|
self.ui.open_dialog(SaveGameStateDialog)
|
|
|
|
def BIND_reset_game(self):
|
|
if self.app.game_mode and self.app.gw.game_dir:
|
|
self.app.gw.reset_game()
|
|
|
|
def BIND_toggle_picker(self):
|
|
if not self.ui.active_art:
|
|
return
|
|
if self.ui.popup_hold_to_show:
|
|
self.ui.popup.show()
|
|
else:
|
|
self.ui.popup.toggle()
|
|
|
|
def BIND_toggle_picker_hold(self):
|
|
self.ui.popup_hold_to_show = not self.ui.popup_hold_to_show
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_swap_fg_bg_colors(self):
|
|
if self.ui.active_art:
|
|
self.ui.swap_fg_bg_colors()
|
|
|
|
def BIND_save_current(self):
|
|
# save current game state in game mode, else save current art
|
|
if self.app.game_mode and self.app.gw.game_dir:
|
|
# as with reset, save over last loaded state
|
|
self.app.gw.save_last_state()
|
|
elif self.ui.active_art:
|
|
# if new document, ask for a name
|
|
default_name = ART_DIR + 'new.' + ART_FILE_EXTENSION
|
|
if self.ui.active_art.filename == default_name:
|
|
self.ui.open_dialog(SaveAsDialog)
|
|
else:
|
|
self.ui.active_art.save_to_file()
|
|
|
|
def BIND_toggle_ui_visibility(self):
|
|
self.ui.visible = not self.ui.visible
|
|
|
|
def BIND_toggle_grid_visibility(self):
|
|
self.app.grid.visible = not self.app.grid.visible
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_toggle_bg_texture(self):
|
|
self.app.show_bg_texture = not self.app.show_bg_texture
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_previous_frame(self):
|
|
self.ui.set_active_frame(self.ui.active_art.active_frame - 1)
|
|
|
|
def BIND_next_frame(self):
|
|
self.ui.set_active_frame(self.ui.active_art.active_frame + 1)
|
|
|
|
def BIND_toggle_anim_playback(self):
|
|
# if game mode, pause/unpause
|
|
if self.app.game_mode:
|
|
self.toggle_pause()
|
|
return
|
|
for r in self.ui.active_art.renderables:
|
|
if r.animating:
|
|
r.stop_animating()
|
|
else:
|
|
r.start_animating()
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def toggle_pause(self):
|
|
self.app.gw.toggle_pause()
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_previous_layer(self):
|
|
self.ui.set_active_layer(self.ui.active_art.active_layer - 1)
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_next_layer(self):
|
|
self.ui.set_active_layer(self.ui.active_art.active_layer + 1)
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_previous_art(self):
|
|
self.ui.previous_active_art()
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_next_art(self):
|
|
if len(self.app.art_loaded_for_edit) == 0:
|
|
return
|
|
self.ui.next_active_art()
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_undo(self):
|
|
self.ui.undo()
|
|
|
|
def BIND_redo(self):
|
|
self.ui.redo()
|
|
|
|
def BIND_quick_grab(self):
|
|
if not self.ui.active_art:
|
|
return
|
|
self.app.keyboard_editing = True
|
|
self.ui.quick_grab()
|
|
|
|
def BIND_set_camera_zoom(self):
|
|
self.ui.open_dialog(SetCameraZoomDialog)
|
|
|
|
def BIND_camera_zoom_in_proportional(self):
|
|
self.app.camera.zoom_proportional(1)
|
|
|
|
def BIND_camera_zoom_out_proportional(self):
|
|
self.app.camera.zoom_proportional(-1)
|
|
|
|
def BIND_toggle_zoom_extents(self):
|
|
self.app.camera.toggle_zoom_extents()
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_toggle_camera_tilt(self):
|
|
if self.app.camera.y_tilt == 2:
|
|
self.app.camera.y_tilt = 0
|
|
self.ui.message_line.post_line('Camera tilt disengaged.')
|
|
else:
|
|
self.app.camera.y_tilt = 2
|
|
self.ui.message_line.post_line('Camera tilt engaged.')
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_select_overlay_image(self):
|
|
self.ui.open_dialog(OverlayImageFileChooserDialog)
|
|
|
|
def BIND_toggle_overlay_image(self):
|
|
self.app.draw_overlay = not self.app.draw_overlay
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_set_overlay_image_opacity(self):
|
|
self.ui.open_dialog(OverlayImageOpacityDialog)
|
|
|
|
def BIND_set_overlay_image_scaling(self):
|
|
if self.app.overlay_scale_type == OIS_WIDTH:
|
|
self.app.overlay_scale_type = OIS_HEIGHT
|
|
elif self.app.overlay_scale_type == OIS_HEIGHT:
|
|
self.app.overlay_scale_type = OIS_FILL
|
|
elif self.app.overlay_scale_type == OIS_FILL:
|
|
self.app.overlay_scale_type = OIS_WIDTH
|
|
self.ui.size_and_position_overlay_image()
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_add_to_list_selection(self):
|
|
if not self.ui.edit_list_panel.is_visible():
|
|
return
|
|
self.ui.edit_list_panel.keyboard_select_item()
|
|
|
|
def BIND_remove_from_list_selection(self):
|
|
if not self.ui.edit_list_panel.is_visible():
|
|
return
|
|
self.ui.edit_list_panel.keyboard_select_item()
|
|
|
|
def BIND_select_or_paint(self):
|
|
if self.ui.keyboard_focus_element:
|
|
# save current focus element because kb_select_item might change it!
|
|
selected_element = self.ui.keyboard_focus_element
|
|
# get button pressed in case we need its item
|
|
button = self.ui.keyboard_focus_element.keyboard_select_item()
|
|
if selected_element is self.ui.pulldown:
|
|
# mirror behavior from MenuItemButton.click: close on select if needed
|
|
if button and button.item.close_on_select:
|
|
self.ui.menu_bar.close_active_menu()
|
|
return
|
|
if not self.ui.active_art:
|
|
return
|
|
elif self.ui.selected_tool is self.ui.text_tool and not self.ui.text_tool.input_active:
|
|
self.ui.text_tool.start_entry()
|
|
elif self.ui.selected_tool is self.ui.select_tool:
|
|
if self.ui.select_tool.selection_in_progress:
|
|
# pass in shift/alt for add/subtract
|
|
self.ui.select_tool.finish_select(self.shift_pressed, self.ctrl_pressed)
|
|
else:
|
|
self.ui.select_tool.start_select()
|
|
else:
|
|
self.app.cursor.start_paint()
|
|
|
|
def BIND_screenshot(self):
|
|
self.app.screenshot()
|
|
|
|
def BIND_run_test_mutate(self):
|
|
if self.ui.active_art.is_script_running('conway'):
|
|
self.ui.active_art.stop_script('conway')
|
|
else:
|
|
self.ui.active_art.run_script_every('conway', 0.05)
|
|
|
|
def BIND_arrow_up(self):
|
|
if self.ui.keyboard_focus_element:
|
|
self.ui.keyboard_navigate(0, -1)
|
|
else:
|
|
self.app.cursor.keyboard_move(0, 1)
|
|
|
|
def BIND_arrow_down(self):
|
|
if self.ui.keyboard_focus_element:
|
|
self.ui.keyboard_navigate(0, 1)
|
|
else:
|
|
self.app.cursor.keyboard_move(0, -1)
|
|
|
|
def BIND_arrow_left(self):
|
|
# navigate popup, menu bar etc
|
|
if self.ui.keyboard_focus_element:
|
|
self.ui.keyboard_navigate(-1, 0)
|
|
else:
|
|
self.app.cursor.keyboard_move(-1, 0)
|
|
|
|
def BIND_arrow_right(self):
|
|
if self.ui.keyboard_focus_element:
|
|
self.ui.keyboard_navigate(1, 0)
|
|
else:
|
|
self.app.cursor.keyboard_move(1, 0)
|
|
|
|
def BIND_cycle_inactive_layer_visibility(self):
|
|
if not self.ui.active_art:
|
|
return
|
|
if self.ui.active_art.layers == 1:
|
|
return
|
|
message_text = 'Non-active layers: '
|
|
if self.app.inactive_layer_visibility == LAYER_VIS_FULL:
|
|
self.app.inactive_layer_visibility = LAYER_VIS_DIM
|
|
message_text += 'dim'
|
|
elif self.app.inactive_layer_visibility == LAYER_VIS_DIM:
|
|
self.app.inactive_layer_visibility = LAYER_VIS_NONE
|
|
message_text += 'invisible'
|
|
else:
|
|
self.app.inactive_layer_visibility = LAYER_VIS_FULL
|
|
message_text += 'visible'
|
|
self.ui.message_line.post_line(message_text)
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_open_file_menu(self):
|
|
self.ui.menu_bar.open_menu_by_name('file')
|
|
|
|
def BIND_open_edit_menu(self):
|
|
self.ui.menu_bar.open_menu_by_name('edit')
|
|
|
|
def BIND_open_tool_menu(self):
|
|
self.ui.menu_bar.open_menu_by_name('tool')
|
|
|
|
def BIND_open_view_menu(self):
|
|
self.ui.menu_bar.open_menu_by_name('view')
|
|
|
|
def BIND_open_art_menu(self):
|
|
self.ui.menu_bar.open_menu_by_name('art')
|
|
|
|
def BIND_open_frame_menu(self):
|
|
if self.app.game_mode:
|
|
self.ui.menu_bar.open_menu_by_name('room')
|
|
else:
|
|
self.ui.menu_bar.open_menu_by_name('frame')
|
|
|
|
def BIND_open_layer_menu(self):
|
|
self.ui.menu_bar.open_menu_by_name('layer')
|
|
|
|
def BIND_open_char_color_menu(self):
|
|
self.ui.menu_bar.open_menu_by_name('char_color')
|
|
|
|
def BIND_open_help_menu(self):
|
|
self.ui.menu_bar.open_menu_by_name('help')
|
|
|
|
def BIND_open_game_menu(self):
|
|
self.ui.menu_bar.open_menu_by_name('game')
|
|
|
|
def BIND_open_state_menu(self):
|
|
self.ui.menu_bar.open_menu_by_name('state')
|
|
|
|
def BIND_open_world_menu(self):
|
|
self.ui.menu_bar.open_menu_by_name('world')
|
|
|
|
def BIND_open_object_menu(self):
|
|
self.ui.menu_bar.open_menu_by_name('object')
|
|
|
|
def BIND_new_art(self):
|
|
self.ui.open_dialog(NewArtDialog)
|
|
|
|
def BIND_open_art(self):
|
|
self.ui.open_dialog(ArtChooserDialog)
|
|
|
|
def BIND_save_art_as(self):
|
|
if self.app.game_mode:
|
|
self.ui.open_dialog(SaveGameStateDialog)
|
|
elif not self.ui.active_art:
|
|
return
|
|
else:
|
|
self.ui.open_dialog(SaveAsDialog)
|
|
|
|
def BIND_revert_art(self):
|
|
if not self.ui.active_art:
|
|
return
|
|
if self.ui.active_art.unsaved_changes:
|
|
self.ui.open_dialog(RevertChangesDialog)
|
|
|
|
def BIND_close_art(self):
|
|
if not self.ui.active_art:
|
|
return
|
|
if self.ui.active_art.unsaved_changes:
|
|
self.ui.open_dialog(CloseUnsavedChangesDialog)
|
|
return
|
|
self.app.close_art(self.ui.active_art)
|
|
# dismiss popup if no more arts are open
|
|
if self.ui.popup.visible and len(self.app.art_loaded_for_edit) == 0:
|
|
self.ui.popup.hide()
|
|
|
|
def BIND_open_help_docs(self):
|
|
self.app.open_help_docs()
|
|
|
|
def BIND_generate_docs(self):
|
|
self.app.generate_docs()
|
|
|
|
def BIND_open_website(self):
|
|
self.app.open_website()
|
|
|
|
def BIND_crop_to_selection(self):
|
|
self.ui.crop_to_selection(self.ui.active_art)
|
|
|
|
def BIND_resize_art(self):
|
|
self.ui.open_dialog(ResizeArtDialog)
|
|
|
|
def BIND_art_flip_horizontal(self):
|
|
self.ui.active_art.flip_horizontal(self.ui.active_art.active_frame,
|
|
self.ui.active_art.active_layer)
|
|
|
|
def BIND_art_flip_vertical(self):
|
|
self.ui.active_art.flip_vertical(self.ui.active_art.active_frame,
|
|
self.ui.active_art.active_layer)
|
|
|
|
def BIND_art_toggle_flip_affects_xforms(self):
|
|
self.ui.flip_affects_xforms = not self.ui.flip_affects_xforms
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_run_art_script(self):
|
|
self.ui.open_dialog(RunArtScriptDialog)
|
|
|
|
def BIND_run_art_script_last(self):
|
|
# if user hasn't run a script this session, pick one
|
|
if not self.ui.app.last_art_script:
|
|
self.BIND_run_art_script()
|
|
else:
|
|
self.ui.active_art.run_script(self.ui.app.last_art_script, log=False)
|
|
|
|
def BIND_art_switch_to(self, art_filename):
|
|
self.ui.set_active_art_by_filename(art_filename)
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_add_frame(self):
|
|
self.ui.open_dialog(AddFrameDialog)
|
|
|
|
def BIND_duplicate_frame(self):
|
|
self.ui.open_dialog(DuplicateFrameDialog)
|
|
|
|
def BIND_change_frame_delay(self):
|
|
self.ui.open_dialog(FrameDelayDialog)
|
|
|
|
def BIND_change_frame_delay_all(self):
|
|
self.ui.open_dialog(FrameDelayAllDialog)
|
|
|
|
def BIND_delete_frame(self):
|
|
self.ui.active_art.delete_frame_at(self.ui.active_art.active_frame)
|
|
# if we're now down to 1 frame, refresh to dim this item!
|
|
# FIXME: this doesn't dim it - why? - but it is unselectable
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_change_frame_index(self):
|
|
self.ui.open_dialog(FrameIndexDialog)
|
|
|
|
def BIND_add_layer(self):
|
|
self.ui.open_dialog(AddLayerDialog)
|
|
|
|
def BIND_duplicate_layer(self):
|
|
self.ui.open_dialog(DuplicateLayerDialog)
|
|
|
|
def BIND_layer_switch_to(self, layer_number):
|
|
self.ui.set_active_layer(layer_number)
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_change_layer_name(self):
|
|
self.ui.open_dialog(SetLayerNameDialog)
|
|
|
|
def BIND_change_layer_z(self):
|
|
self.ui.open_dialog(SetLayerZDialog)
|
|
|
|
def BIND_toggle_layer_visibility(self):
|
|
art = self.ui.active_art
|
|
is_visible = art.layers_visibility[art.active_layer]
|
|
art.layers_visibility[art.active_layer] = not is_visible
|
|
art.set_unsaved_changes(True)
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_toggle_hidden_layers_visible(self):
|
|
self.app.show_hidden_layers = not self.app.show_hidden_layers
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_delete_layer(self):
|
|
self.ui.active_art.delete_layer(self.ui.active_art.active_layer)
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_choose_charset(self):
|
|
self.ui.open_dialog(CharSetChooserDialog)
|
|
|
|
def BIND_choose_palette(self):
|
|
self.ui.open_dialog(PaletteChooserDialog)
|
|
|
|
def BIND_palette_from_file(self):
|
|
self.ui.open_dialog(PaletteFromImageChooserDialog)
|
|
|
|
def BIND_toggle_onion_visibility(self):
|
|
self.app.onion_frames_visible = not self.app.onion_frames_visible
|
|
if self.app.onion_frames_visible:
|
|
self.ui.reset_onion_frames()
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_cycle_onion_frames(self):
|
|
self.app.onion_show_frames += 1
|
|
self.app.onion_show_frames %= self.app.max_onion_frames + 1
|
|
# start cycle at 1, not 0
|
|
self.app.onion_show_frames = max(1, self.app.onion_show_frames)
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_cycle_onion_ahead_behind(self):
|
|
# cycle between next, previous, next & previous
|
|
if self.app.onion_show_frames_behind and self.app.onion_show_frames_ahead:
|
|
self.app.onion_show_frames_behind = False
|
|
elif not self.app.onion_show_frames_behind and self.app.onion_show_frames_ahead:
|
|
self.app.onion_show_frames_behind = True
|
|
self.app.onion_show_frames_ahead = False
|
|
else:
|
|
self.app.onion_show_frames_ahead = True
|
|
self.app.onion_show_frames_ahead = True
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_toggle_debug_text(self):
|
|
self.ui.debug_text.visible = not self.ui.debug_text.visible
|
|
|
|
def BIND_toggle_fps_counter(self):
|
|
self.ui.fps_counter.visible = not self.ui.fps_counter.visible
|
|
|
|
def BIND_open_all_game_assets(self):
|
|
for game_obj in self.app.gw.objects.values():
|
|
for art_filename in game_obj.get_all_art():
|
|
self.app.load_art_for_edit(art_filename)
|
|
# open all hud assets too
|
|
for art in self.app.gw.hud.arts:
|
|
self.app.load_art_for_edit(art.filename)
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_toggle_all_collision_viz(self):
|
|
self.app.gw.toggle_all_collision_viz()
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_toggle_all_bounds_viz(self):
|
|
self.app.gw.toggle_all_bounds_viz()
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_toggle_all_origin_viz(self):
|
|
self.app.gw.toggle_all_origin_viz()
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_toggle_collision_on_selected(self):
|
|
for obj in self.app.gw.selected_objects:
|
|
if obj.orig_collision_type and obj.collision_type == CT_NONE:
|
|
obj.enable_collision()
|
|
self.ui.message_line.post_line('Collision enabled for %s' % obj.name)
|
|
elif obj.collision_type != CT_NONE:
|
|
obj.disable_collision()
|
|
self.ui.message_line.post_line('Collision disabled for %s' % obj.name)
|
|
|
|
def BIND_toggle_game_edit_ui(self):
|
|
self.ui.toggle_game_edit_ui()
|
|
|
|
#
|
|
# game mode binds
|
|
#
|
|
def accept_normal_game_input(self):
|
|
return self.app.game_mode and self.app.gw.player and not self.ui.active_dialog and not self.ui.pulldown.visible
|
|
|
|
# TODO: generalize these two somehow
|
|
def BIND_game_frob(self):
|
|
if self.accept_normal_game_input():
|
|
self.app.gw.player.button_pressed(1)
|
|
|
|
def BIND_game_grab(self):
|
|
if self.accept_normal_game_input():
|
|
self.app.gw.player.button_pressed(0)
|
|
|
|
def BIND_center_cursor_in_art(self):
|
|
self.app.cursor.center_in_art()
|
|
|
|
def BIND_choose_spawn_object_class(self):
|
|
if self.app.game_mode and self.app.gw.game_dir:
|
|
self.ui.edit_list_panel.set_list_operation(LO_SET_SPAWN_CLASS)
|
|
|
|
def BIND_duplicate_selected_objects(self):
|
|
self.app.gw.duplicate_selected_objects()
|
|
|
|
def BIND_select_objects(self):
|
|
if self.app.game_mode and self.app.gw.game_dir:
|
|
self.ui.edit_list_panel.set_list_operation(LO_SELECT_OBJECTS)
|
|
|
|
def BIND_edit_art_for_selected_objects(self):
|
|
self.app.gw.edit_art_for_selected()
|
|
|
|
def BIND_edit_world_properties(self):
|
|
self.app.gw.deselect_all()
|
|
self.app.gw.select_object(self.app.gw.properties, force=True)
|
|
|
|
def BIND_change_current_room(self):
|
|
self.ui.edit_list_panel.set_list_operation(LO_SET_ROOM)
|
|
|
|
def BIND_change_current_room_to(self, new_room_name):
|
|
self.app.gw.change_room(new_room_name)
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_add_room(self):
|
|
self.ui.open_dialog(AddRoomDialog)
|
|
|
|
def BIND_remove_current_room(self):
|
|
self.app.gw.remove_room(self.app.gw.current_room.name)
|
|
|
|
def BIND_set_room_objects(self):
|
|
self.ui.edit_list_panel.set_list_operation(LO_SET_ROOM_OBJECTS)
|
|
|
|
def BIND_set_object_rooms(self):
|
|
self.ui.edit_list_panel.set_list_operation(LO_SET_OBJECT_ROOMS)
|
|
|
|
def BIND_toggle_all_rooms_visible(self):
|
|
self.app.gw.show_all_rooms = not self.app.gw.show_all_rooms
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_toggle_room_camera_changes(self):
|
|
self.app.gw.properties.set_object_property('room_camera_changes_enabled', not self.app.gw.room_camera_changes_enabled)
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_set_room_camera_marker(self):
|
|
self.ui.open_dialog(SetRoomCamDialog)
|
|
self.ui.edit_list_panel.set_list_operation(LO_SET_ROOM_CAMERA)
|
|
|
|
def BIND_objects_to_camera(self):
|
|
cam = self.app.gw.camera
|
|
for obj in self.app.gw.selected_objects:
|
|
obj.set_loc(cam.x, cam.y, cam.z)
|
|
|
|
def BIND_camera_to_objects(self):
|
|
if len(self.app.gw.selected_objects) == 0:
|
|
return
|
|
obj = self.app.gw.selected_objects[0]
|
|
self.app.gw.camera.set_loc_from_obj(obj)
|
|
|
|
def BIND_add_selected_to_room(self):
|
|
if not self.app.gw.current_room:
|
|
return
|
|
for obj in self.app.gw.selected_objects:
|
|
self.app.gw.current_room.add_object(obj)
|
|
|
|
def BIND_remove_selected_from_room(self):
|
|
if not self.app.gw.current_room:
|
|
return
|
|
for obj in self.app.gw.selected_objects:
|
|
self.app.gw.current_room.remove_object(obj)
|
|
|
|
def BIND_switch_edit_panel_focus(self):
|
|
self.ui.switch_edit_panel_focus()
|
|
|
|
def BIND_switch_edit_panel_focus_reverse(self):
|
|
self.ui.switch_edit_panel_focus(reverse=True)
|
|
|
|
def BIND_set_room_edge_warps(self):
|
|
# bring up dialog before setting list so list knows about it
|
|
self.ui.open_dialog(SetRoomEdgeWarpsDialog)
|
|
self.ui.edit_list_panel.set_list_operation(LO_SET_ROOM_EDGE_WARP)
|
|
|
|
def BIND_set_room_bounds_obj(self):
|
|
self.ui.open_dialog(SetRoomBoundsObjDialog)
|
|
self.ui.edit_list_panel.set_list_operation(LO_SET_ROOM_EDGE_OBJ)
|
|
|
|
def BIND_toggle_list_only_room_objects(self):
|
|
self.app.gw.list_only_current_room_objects = not self.app.gw.list_only_current_room_objects
|
|
self.ui.menu_bar.refresh_active_menu()
|
|
|
|
def BIND_rename_current_room(self):
|
|
self.ui.open_dialog(RenameRoomDialog)
|
|
|
|
def BIND_toggle_debug_objects(self):
|
|
if not self.app.gw.properties:
|
|
return
|
|
self.app.gw.properties.set_object_property('draw_debug_objects', not self.app.gw.draw_debug_objects)
|
|
self.ui.menu_bar.refresh_active_menu()
|