Mercurial > fife-parpg
changeset 307:22253b2c9b14
- added LightEdit editor plugin (needs light branch to work; deactivated if lighting renderer is not available)
- added animation viewer to ObjectEdit
- several bugfixes for ObjectEdit plugin
FEATURES:
- ObjectEdit
- viewing and rotating animated instances (rotations are hardcoded for now, FIFE needs to expose available angles to python in order to make animation rotation work for every client)
- LightEdit
- test global light values
author | chewie@33b003aa-7bff-0310-803a-e67f0ece8222 |
---|---|
date | Tue, 11 Aug 2009 15:32:54 +0000 |
parents | 6177cdf72489 |
children | af0b233e246f |
files | clients/editor/gui/lightedit.xml clients/editor/gui/objectedit.xml clients/editor/plugins/LightEdit.py clients/editor/plugins/ObjectEdit.py |
diffstat | 4 files changed, 758 insertions(+), 106 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/clients/editor/gui/lightedit.xml Tue Aug 11 15:32:54 2009 +0000 @@ -0,0 +1,32 @@ +<Panel title="Light editor" position="10,700"> + <Label text="RGB values (float)" /> + <HBox> + <Label text="R" /> + <TextBox text="1.0" name="value_R" min_size="20,20"/> + <Button text="+" name="increase_R" /> + <Button text="-" name="decrease_R" /> + </HBox> + <HBox> + <Label text="G" /> + <TextBox text="1.0" name="value_G" min_size="20,20"/> + <Button text="+" name="increase_G" /> + <Button text="-" name="decrease_G" /> + </HBox> + <HBox> + <Label text="B" /> + <TextBox text="1.0" name="value_B" min_size="20,20"/> + <Button text="+" name="increase_B" /> + <Button text="-" name="decrease_B" /> + </HBox> + <HBox> + <Label text="A" /> + <TextBox text="1.0" name="value_A" min_size="20,20"/> + <Button text="+" name="increase_A" /> + <Button text="-" name="decrease_A" /> + </HBox> + <VBox> + <ToggleButton text="Enable" name="enable_global_light" /> + <Button text="Random" name="random_global_light" /> + <Button text="Reset" name="reset_global_light" /> + </VBox> +</Panel>
--- a/clients/editor/gui/objectedit.xml Sat Aug 08 14:24:35 2009 +0000 +++ b/clients/editor/gui/objectedit.xml Tue Aug 11 15:32:54 2009 +0000 @@ -1,9 +1,9 @@ -<Panel title="Object editor" position="10,700"> <!-- size="200,250" min_size="200,250" max_size="200,250" > --> - +<Panel title="Object editor" position="10,700" min_size="200,250"> <!-- size="200,250" min_size="200,250" max_size="200,250" > --> <Label text=" Object" background_color="0,0,0" /> - <Label text="Namespace:" min_size="85,20"/> - <Label text="None" name="object_namespace" min_size="30,20"/> - + <HBox> + <Label text="Namespace:" min_size="85,20"/> + <Label text="None" name="object_namespace" min_size="30,20"/> + </HBox> <HBox> <Label text="Object ID:" min_size="85,20"/> <Label text="None" name="object_id" min_size="30,20"/> @@ -16,28 +16,56 @@ <TextBox text="0" name="object_static" min_size="20,20"/> </HBox> - <Button name="change_data" text="Save object"/> - <Label text=" Selected Instance" background_color="0,0,0" /> <HBox> <Label text="Select Rotation:" min_size="85,20" /> - <DropDown min_size="80,0" name="select_rotations"/> </HBox> + <DropDown min_size="80,0" name="select_rotations"/> + + <VBox> + <Label text="Offset:" min_size="45,20"/> + <HBox> + <Label text="X: " min_size="25,20"/> + <TextBox text="0" name="x_offset" size="30,20" min_size="30,20" max_size="30,20" /> + <Button name="x_offset_up" text="+" max_size="20,20"/> + <Button name="x_offset_dn" text="-" max_size="20,20"/> + </HBox> + + <HBox> + <Label text="Y: " min_size="25,20"/> + <TextBox text="0" name="y_offset" size="30,20" min_size="30,20" max_size="30,20"/> + <Button name="y_offset_up" text="+" max_size="20,20"/> + <Button name="y_offset_dn" text="-" max_size="20,20"/> + </HBox> + </VBox> + + <Label text="Selected Instance" min_size="85,20" /> + <HBox > <Label text="Instance ID:" min_size="85,20"/> <TextBox text="None" name="instance_id" min_size="30,20"/> </HBox> <HBox > <Label text="Instance rot:" min_size="85,20"/> - <TextBox text="0" name="instance_rotation" min_size="30,20" hexpand="1"/> + <TextBox text="0" name="instance_rotation" min_size="30,20"/> </HBox> - <VBox name="animation_panel_wrapper" max_size="150,50"> + <HBox> + <Button name="use_data" text="Use"/> + </HBox> + <Spacer /> + <Button name="change_data" text="Save rotation"/> + + + <Label text=" Animation viewer" background_color="0,0,0" /> + <VBox name="animation_panel_wrapper"> <Spacer /> <VBox name="animation_panel"> - <Label text="Animation panel" min_size="85,20" /> + <Label text="Actions:" min_size="85,20" /> + <DropDown min_size="80,0" name="select_actions"/> + <HBox> <Button name="anim_start_pos" text="S" max_size="20,20"/> <Button name="anim_left" text="l1" max_size="20,20"/> @@ -45,11 +73,21 @@ <Button name="anim_right" text="r1" max_size="20,20"/> <Button name="anim_end_pos" text="E" max_size="20,20"/> </HBox> + <HBox> + <ToggleButton name="anim_playback" text="P" max_size="20,20"/> + <CheckBox name="anim_loop" marked="1" text="Loop:"/> + </HBox> + + <HBox> + <Label text="Rotation: (" min_size="60,20" /> + <Label name="anim_rotation" text="" min_size="20,20"/> + <Label text=")" min_size="10,20" /> + </HBox> + + <VBox min_size="100,100" size="100,100"> + <Icon image="gui/icons/add_instance.png" size="200,200" min_size="250,250" name="animTest"/> + </VBox> </VBox> </VBox> - <HBox> - <Button name="use_data" text="Use"/> - </HBox> - </Panel>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/clients/editor/plugins/LightEdit.py Tue Aug 11 15:32:54 2009 +0000 @@ -0,0 +1,292 @@ +#!/usr/bin/env python +# coding: utf-8 +# ################################################### +# Copyright (C) 2008 The Zero-Projekt team +# http://zero-projekt.net +# info@zero-projekt.net +# This file is part of Zero "Was vom Morgen blieb" +# +# The Zero-Projekt codebase is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# ################################################### + +""" a tool for FIFEdit to test global light """ + +import fife +import pychan +import pychan.widgets as widgets +from pychan.tools import callbackWithArguments as cbwa + +from fife_timer import Timer + +import scripts +import scripts.plugin as plugin +from scripts.events import * +from scripts.gui.action import Action + +import random + +DEFAULT_GLOBAL_LIGHT = { + "R" : 1.0, + "G" : 1.0, + "B" : 1.0, + "A" : 1.0, +} +DEFAULT_LIGHT_ID = "LightEdit" + +class LightEdit(plugin.Plugin): + """ The B{LightEdit} module is a plugin for FIFedit and allows to change the + global light value + + FEATURES: + - enable FIFE lighting renderer + - generate random light values + - test lightsetups by manipulating the color channels + - reset to default + """ + def __init__(self): + self.active = False + + self._renderer = None + self._camera = None + self._enabled = False + self._light = False + + self.map_loaded = False + + self._color = {} + self._color.update(DEFAULT_GLOBAL_LIGHT) + + random.seed() + + if "LightRenderer" in dir(fife): + self._renderer_available = True + else: + self._renderer_available = False + + def _reset(self): + """ resets all dynamic vars """ + pass + + def enable(self): + """ plugin method """ + if not self._renderer_available: + self._enabled = False + return + if self._enabled is True: + return + + self._editor = scripts.editor.getEditor() + self.engine = self._editor.getEngine() + + self._showAction = Action(unicode(self.getName(),"utf-8"), checkable=True) + scripts.gui.action.activated.connect(self.toggle_gui, sender=self._showAction) + + self._editor._toolsMenu.addAction(self._showAction) + + events.postMapShown.connect(self.update_renderer) + events.onMapChanged.connect(self.update_renderer) + + self._reset() + self.create_gui() + + def disable(self): + """ plugin method """ + if self._enabled is False: + return + + self._reset() + self.container.hide() + self.removeAllChildren() + + self._editor._toolsMenu.removeAction(self._showAction) + + events.postMapShown.disconnect(self.update_renderer) + events.onMapChanged.disconnect(self.update_renderer) + + def isEnabled(self): + """ plugin method """ + return self._enabled; + + def getName(self): + """ plugin method """ + return "Light editor" + + def create_gui(self): + """ create gui container and setup callbacks """ + self.container = pychan.loadXML('gui/lightedit.xml') + self.container.mapEvents({ + "enable_global_light" : self.toggle_light, + "random_global_light" : self.random_color, + "reset_global_light" : self.reset_global_light, + + "increase_R" : cbwa(self.increase_color, r=True), + "decrease_R" : cbwa(self.decrease_color, r=True), + "value_R/mouseWheelMovedUp" : cbwa(self.increase_color, step=0.2, r=True), + "value_R/mouseWheelMovedDown" : cbwa(self.decrease_color, step=0.2, r=True), + + "increase_G" : cbwa(self.increase_color, g=True), + "decrease_G" : cbwa(self.decrease_color, g=True), + "value_G/mouseWheelMovedUp" : cbwa(self.increase_color, step=0.2, g=True), + "value_G/mouseWheelMovedDown" : cbwa(self.decrease_color, step=0.2, g=True), + + "increase_B" : cbwa(self.increase_color, b=True), + "decrease_B" : cbwa(self.decrease_color, b=True), + "value_B/mouseWheelMovedUp" : cbwa(self.increase_color, step=0.2, b=True), + "value_B/mouseWheelMovedDown" : cbwa(self.decrease_color, step=0.2, b=True), + + "increase_A" : cbwa(self.increase_color, a=True), + "decrease_A" : cbwa(self.decrease_color, a=True), + "value_A/mouseWheelMovedUp" : cbwa(self.increase_color, step=0.2, a=True), + "value_A/mouseWheelMovedDown" : cbwa(self.decrease_color, step=0.2, a=True), + }) + self._widgets = { + "enable_global_light" : self.container.findChild(name="enable_global_light"), + "random_global_light" : self.container.findChild(name="random_global_light"), + "reset_global_light" : self.container.findChild(name="reset_global_light"), + + "value_R" : self.container.findChild(name="value_R"), + "value_G" : self.container.findChild(name="value_G"), + "value_B" : self.container.findChild(name="value_B"), + "value_A" : self.container.findChild(name="value_A"), + } + + def toggle_gui(self): + """ show / hide the gui """ + if self.active: + self.active = False + if self.container.isVisible() or self.container.isDocked(): + self.container.setDocked(False) + self.container.hide() + self._showAction.setChecked(False) + else: + self.active = True + self._showAction.setChecked(True) + self.container.show() + + def toggle_light(self): + """ toggle light on / off """ + if not self._renderer: + self._widgets['enable_global_light']._setToggled(False) + return + + if self._light: + self._light = False + self.reset_global_light() + self._renderer.setEnabled(False) + else: + self._light = True + self._renderer.setEnabled(True) + + def update_renderer(self): + """ sets current camera and renderer + bound to FIFedit core (updated on map change) + """ + self._camera = self._editor.getActiveMapView().getCamera() + self._renderer = fife.LightRenderer.getInstance(self._camera) + + def update_gui(self): + """ update gui widgets according to plugin data """ + self._widgets["value_R"].text = unicode(str(self._color["R"])) + self._widgets["value_G"].text = unicode(str(self._color["G"])) + self._widgets["value_B"].text = unicode(str(self._color["B"])) + self._widgets["value_A"].text = unicode(str(self._color["A"])) + + def reset_global_light(self): + """ reset global light to default values (1.0) """ + if not self._renderer: return + + self._color.update(DEFAULT_GLOBAL_LIGHT) + self.update_gui() + self.set_global_light() + + def increase_color(self, step=0.1, r=None, g=None, b=None, a=None): + """ increase a given color value by step value + + @type step float + @param step the step for changing the color channel + @type r bool + @param r flag to alter red color value + @type g bool + @param g flag to alter green color value + @type b bool + @param b flag to alter blue color value + @type a bool + @type a flag to alter alpha channel value (no effect atm) + """ + if r: + self._color["R"] += step + if g: + self._color["G"] += step + if b: + self._color["B"] += step + if a: + self._color["A"] += step + + self.update_gui() + self.set_global_light() + + def decrease_color(self, step=0.1, r=None, g=None, b=None, a=None): + """ decrease a given color value by step value + + @type step float + @param step the step for changing the color channel + @type r bool + @param r flag to alter red color value + @type g bool + @param g flag to alter green color value + @type b bool + @param b flag to alter blue color value + @type a bool + @type a flag to alter alpha channel value (no effect atm) + """ + if r: + self._color["R"] -= step + if g: + self._color["G"] -= step + if b: + self._color["B"] -= step + if a: + self._color["A"] -= step + + self.update_gui() + self.set_global_light() + + def random_color(self): + """ generate random values for color channels """ + if not self._renderer: return + + self._color["R"] = random.uniform(0,2) + self._color["G"] = random.uniform(0,2) + self._color["B"] = random.uniform(0,2) + self._color["A"] = random.uniform(0,2) + + self.update_gui() + self.set_global_light() + + def set_global_light(self): + """ update the global light with the current set colors """ + if not self._renderer: return + + self._renderer.removeAll(DEFAULT_LIGHT_ID) + self._renderer.setglobalLight( + DEFAULT_LIGHT_ID, + 1, + self._color["R"], + self._color["G"], + self._color["B"], + self._color["A"] + ) +
--- a/clients/editor/plugins/ObjectEdit.py Sat Aug 08 14:24:35 2009 +0000 +++ b/clients/editor/plugins/ObjectEdit.py Tue Aug 11 15:32:54 2009 +0000 @@ -1,3 +1,4 @@ +#!/usr/bin/env python # coding: utf-8 # ################################################### # Copyright (C) 2008 The Zero-Projekt team @@ -28,39 +29,56 @@ import pychan.widgets as widgets from pychan.tools import callbackWithArguments as cbwa +from fife_timer import Timer + import scripts import scripts.plugin as plugin from scripts.events import * from scripts.gui.action import Action + +import os +try: + import xml.etree.cElementTree as ET +except: + import xml.etree.ElementTree as ET + import math +WHITE = { + "r" : 205, + "g" : 205, + "b" : 205 +} +OUTLINE_SIZE = 1 + class ObjectEdit(plugin.Plugin): """ The B{ObjectEdit} module is a plugin for FIFedit and allows to edit - attributes of an selected instance - like instance id or rotation + attributes of an selected instance - like offset, instance id or rotation (namespaces and object id editing is excluded) current features: - click instance and get all known data - - edit rotation, instance id + - edit offsets, rotation, instance id + - save offsets to object file - outline highlighting of the selected object - - missing features: - - blocking flag (flag doesn't work yet from FIFE side) - - static flag (flag doesn't work yet from FIFE side) - - object saving - - a lot of bug fixing concerning the rotation - - the module should be able to use the editors global undo history + - animation viewer + + FIXME: + - add static and blocking flag to save routine + - fix animation rotation (FIFE has no method yet to export all + angles of an animation to python) """ def __init__(self): self.active = False self._camera = None self._layer = None + self._anim_timer = None self._enabled = False self.imagepool = None - self.animationpool = None + self._animationpool = None self.guidata = {} self.objectdata = {} @@ -70,9 +88,20 @@ resets all dynamic vars, but leaves out static ones (e.g. camera, layer) """ + if self._anim_timer: + self._anim_timer.stop() + # reset the ToggleButton + if self._gui_anim_playback._isToggled(): + self._gui_anim_playback._setToggled(0) + self._anim_timer = None + + self._object = None self._instances = None self._image = None + self._image_default_x_offset = None + self._image_default_y_offset = None self._animation = False + self._anim_data = {} self._rotation = None self._avail_rotations = [] self._namespace = None @@ -83,10 +112,10 @@ self._fixed_rotation = None if self._camera is not None: - self.renderer.removeAllOutlines() - + self.renderer.removeAllOutlines() def enable(self): + """ plugin method """ if self._enabled is True: return @@ -94,9 +123,9 @@ self.engine = self._editor.getEngine() self.imagepool = self.engine.getImagePool() - self.animationpool = self.engine.getAnimationPool() + self._animationpool = self.engine.getAnimationPool() - self._showAction = Action(u"Object editor", checkable=True) + self._showAction = Action(unicode(self.getName(),"utf-8"), checkable=True) scripts.gui.action.activated.connect(self.toggle_gui, sender=self._showAction) self._editor._toolsMenu.addAction(self._showAction) @@ -107,6 +136,7 @@ self.create_gui() def disable(self): + """ plugin method """ if self._enabled is False: return @@ -119,77 +149,173 @@ self._editor._toolsMenu.removeAction(self._showAction) def isEnabled(self): + """ plugin method """ return self._enabled; def getName(self): - return "Object editor" + """ plugin method """ + return "Object editor v2" def create_gui(self): """ - creates the gui skeleton by loading the xml file - finds some important childs and saves their widget in the object + + FIXME: + - move all dynamic widgets to dict """ self.container = pychan.loadXML('gui/objectedit.xml') self.container.mapEvents({ - 'use_data' : self.use_user_data, + 'x_offset_up' : cbwa(self.change_offset_x, 1), + 'x_offset_dn' : cbwa(self.change_offset_x, -1), + + 'y_offset_up' : cbwa(self.change_offset_y, 1), + 'y_offset_dn' : cbwa(self.change_offset_y, -1), + 'use_data' : self.use_user_data, + 'change_data' : self.save_user_data, + + 'anim_left' : self.previous_anim_frame, + 'anim_right' : self.next_anim_frame, + 'anim_start_pos' : self.anim_start_frame, + 'anim_end_pos' : self.anim_end_frame, }) self._gui_anim_panel_wrapper = self.container.findChild(name="animation_panel_wrapper") self._gui_anim_panel = self._gui_anim_panel_wrapper.findChild(name="animation_panel") - self._gui_anim_panel_wrapper.removeChild(self._gui_anim_panel) + self._gui_rotation_dropdown = self.container.findChild(name="select_rotations") + self._gui_rotation_dropdown.capture(self.gui_rotate_instance,"mouseWheelMovedUp") + self._gui_rotation_dropdown.capture(self.gui_rotate_instance,"mouseWheelMovedDown") + self._gui_rotation_dropdown.capture(self.gui_rotate_instance,"action") + + self._gui_anim_actions_dropdown = self._gui_anim_panel_wrapper.findChild(name="select_actions") + self._gui_anim_actions_dropdown.capture(self.eval_gui_anim_action,"mouseWheelMovedUp") + self._gui_anim_actions_dropdown.capture(self.eval_gui_anim_action,"mouseWheelMovedDown") + self._gui_anim_actions_dropdown.capture(self.eval_gui_anim_action,"action") + + self._gui_anim_playback = self._gui_anim_panel_wrapper.findChild(name="anim_playback") + self._gui_anim_playback.capture(self.anim_playback, "mousePressed") + self._gui_anim_loop = self._gui_anim_panel_wrapper.findChild(name="anim_loop") - self._gui_rotation_dropdown = self.container.findChild(name="select_rotations") - + self._gui_current_frame = self._gui_anim_panel_wrapper.findChild(name="anim_current_frame") + self._gui_current_frame.capture(self.previous_anim_frame,"mouseWheelMovedUp") + self._gui_current_frame.capture(self.next_anim_frame,"mouseWheelMovedDown") + + self._gui_xoffset_textfield = self.container.findChild(name="x_offset") + self._gui_yoffset_textfield = self.container.findChild(name="y_offset") + self._gui_instance_id_textfield = self.container.findChild(name="instance_id") - def _get_gui_size(self): + def anim_playback(self, widget): + """ start / stop playback of an animation due to status of a gui ToggleButton + Sets also two ivars of timer object (active & loop) """ - gets the current size of the gui window and calculates new position - (atm top right corner) - """ - size = self.container.size - self.position = ((pychan.internal.screen_width() - 50 - size[0]), 50) + if widget._isToggled(): + self._anim_timer.stop() + self._anim_timer.active = False + else: + frame_delay = self._anim_data['obj'].getFrameDuration(self._anim_data['current']) + self._anim_timer = Timer(delay=frame_delay,callback=self.next_anim_frame) + self._anim_timer.active = True + self._anim_timer.loop = self._gui_anim_loop._isMarked() + self._anim_timer.start() + + def previous_anim_frame(self): + """ show previous anim frame """ + if self._anim_data['current'] > 0: + self._anim_data['current'] -= 1 + self.update_gui() + + def next_anim_frame(self): + """ show next anim frame and reset animation frame to 0 if playback looping is active""" + if self._anim_timer.loop and (self._anim_data['current'] == self._anim_data['frames']): + self._anim_data['current'] = 0 + if self._anim_data['current'] < self._anim_data['frames']: + self._anim_data['current'] += 1 + self.update_gui() + + def anim_start_frame(self): + """ set start frame of animation """ + self._anim_data['current'] = 0 + self.update_gui() + + def anim_end_frame(self): + """ set end frame of animation """ + self._anim_data['current'] = self._anim_data['frames'] + self.update_gui() + + def set_default_offset(self, axis): + """ set default image offset for given axis """ + if axis == 'x': + self.set_offset(x=self._image_default_x_offset) + elif axis == 'y': + self.set_offset(y=self._image_default_y_offset) + def update_gui(self): """ updates the gui widgets with current instance data - FIXME: - - drop animation support or turn it into something useful """ - #if self._animation is False: - #try: - #self._gui_anim_panel_wrapper.removeChild(self._gui_anim_panel) - #except: - #pass - #elif self._animation is True: - #try: - #self._gui_anim_panel_wrapper.resizeToContent() - #self._gui_anim_panel_wrapper.addChild(self._gui_anim_panel) - #self._gui_anim_panel_wrapper.resizeToContent() - #except: - #pass + # show the image we retrieved from an animated object + if self._animation: + if not self._gui_anim_panel_wrapper.findChild(name="animation_panel"): + self._gui_anim_panel_wrapper.addChild(self._gui_anim_panel) + # get current selected image and update the icon widget + dur = 0 + for i in range(self._anim_data['frames']): + dur += self._anim_data['obj'].getFrameDuration(i) + + # set new duration for the playback timer + if self._anim_timer: + frame_delay = self._anim_data['obj'].getFrameDuration(self._anim_data['current']) + + if i == self._anim_data['current']: + # set new duration for the playback timer + if self._anim_timer and self._anim_timer.active: + self._anim_timer.setPeriod(self._anim_data['obj'].getFrameDuration(self._anim_data['current'])) + break + + image = self._anim_data['obj'].getFrameByTimestamp(dur) + self.container.findChild(name="animTest").image = image.getResourceFile() + self.container.findChild(name="animTest").size= (250,250) + self.container.findChild(name="animTest").min_size= (250,250) + + self.container.distributeInitialData({ + 'anim_current_frame' : unicode(str(self._anim_data['current'])), + 'anim_rotation' : unicode(str(self._anim_data['obj'].getDirection())), + }) + + else: + if self._gui_anim_panel_wrapper.findChild(name="animation_panel"): + self._gui_anim_panel_wrapper.removeChild(self._gui_anim_panel) + + if self._image is not None: + x_offset = unicode( self._image.getXShift() ) + y_offset = unicode( self._image.getYShift() ) + else: + x_offset = unicode( 0 ) + y_offset = unicode( 0 ) + self.container.distributeInitialData({ 'select_rotations' : self._avail_rotations, 'instance_id' : unicode( self._instances[0].getId() ), 'object_id' : unicode( self._object_id ), + 'x_offset' : x_offset, + 'y_offset' : y_offset, 'instance_rotation' : unicode( self._instances[0].getRotation() ), 'object_namespace' : unicode( self._namespace ), 'object_blocking' : unicode( self._blocking ), 'object_static' : unicode( self._static ), }) - try: - print self._avail_rotations - print self._fixed_rotation - index = self._avail_rotations.index( str(self._fixed_rotation) ) + + if not self._animation: + index = self._avail_rotations.index( self._fixed_rotation ) self._gui_rotation_dropdown._setSelected(index) - except: -# pass - print "Angle (", self._fixed_rotation, ") not supported by this instance" - self.container.adaptLayout() + + self.container.adaptLayout() def toggle_gui(self): """ @@ -206,11 +332,43 @@ self._showAction.setChecked(True) def highlight_selected_instance(self): - """ - just highlights selected instance + """ highlights selected instance """ + self.renderer.removeAllOutlines() + self.renderer.addOutlined(self._instances[0], WHITE["r"], WHITE["g"], WHITE["b"], OUTLINE_SIZE) + + def change_offset_x(self, value=1): """ - self.renderer.removeAllOutlines() - self.renderer.addOutlined(self._instances[0], 205, 205, 205, 1) + - callback for changing x offset + - changes x offset of current instance (image) + - updates gui + + @type value: int + @param value: the modifier for the x offset + """ + if self._animation: + print "Offset changes of animations are not supported yet" + return + + if self._image is not None: + self._image.setXShift(self._image.getXShift() + value) + self.update_gui() + + def change_offset_y(self, value=1): + """ + - callback for changing y offset + - changes y offset of current instance (image) + - updates gui + + @type value: int + @param value: the modifier for the y offset + """ + if self._animation: + print "Offset changes of animations are not supported yet" + return + + if self._image is not None: + self._image.setYShift(self._image.getYShift() + value) + self.update_gui() def use_user_data(self): """ @@ -218,8 +376,22 @@ - writes current data record - writes previous data record - updates gui - """ + + FIXME: + - parse user data in case user think strings are considered to be integer offset values... + """ + if self._animation: + print "Editing animated instances is not supported yet" + return + + xoffset = self._gui_xoffset_textfield._getText() + yoffset = self._gui_yoffset_textfield._getText() + instance_id = str(self._gui_instance_id_textfield._getText()) + + if instance_id == "": + instance_id = "None" + if instance_id is not None and instance_id is not "None": existing_instances = self._editor.getActiveMapView().getController()._layer.getInstances(instance_id) if len(existing_instances) <= 0: @@ -228,32 +400,127 @@ else: print "Instance ID is already in use." - # workaround - dropdown list only has 2 entries, but sends 3 -> pychan bug? - if len(self._avail_rotations) < self._gui_rotation_dropdown._getSelected(): - index = len(self._avail_rotations) - else: - index = self._gui_rotation_dropdown._getSelected() + # update rotation + angle = self.eval_gui_rotation() + self.set_rotation(angle) + + # update offsets + self.set_offset(int(xoffset), int(yoffset)) + + self.update_gui() + + def save_user_data(self): + """ saves the current object to its xml file + + NOTE: + - animations can't be saved for now + + FIXME: + - add missing object attributes to saving routine + """ + if self._object is None: + return + if self._animation: + return + + file = self._object.getResourceFile() + self.tree = ET.parse(file) - # strange, but this helps to rotate the image correctly to the value the user selected + img_lst = self.tree.findall("image") + + # apply changes to the XML structure due to current user settings in the gui + for img_tag in img_lst: + if img_tag.attrib["direction"] == self._avail_rotations[self._gui_rotation_dropdown._getSelected()]: + img_tag.attrib["x_offset"] = self._gui_xoffset_textfield._getText() + img_tag.attrib["y_offset"] = self._gui_yoffset_textfield._getText() + break + + xmlcontent = ET.tostring(self.tree.getroot()) + + # save xml data beneath the <?fife type="object"?> definition into the object file + tmp = open(file, 'w') + tmp.write('<?fife type="object"?>\n') + tmp.write(xmlcontent + "\n") + tmp.close() + + def gui_rotate_instance(self): + """ rotate an instance due to selected angle """ + angle = self.eval_gui_rotation() + self.set_rotation(angle) + + def eval_gui_rotation(self): + """ prepare rotation from gui and apply it to the current selected instance """ + index = self._gui_rotation_dropdown._getSelected() angle = int( self._avail_rotations[index] ) - angle = int(angle - abs( self._camera.getTilt() ) ) + if angle == 360: angle = 0 + + return angle + def eval_gui_anim_action(self): + """ check the selected action of an animation and update the gui accordingly """ + if not self._anim_data['actions']: return + + index = self._gui_anim_actions_dropdown._getSelected() + action = self._anim_data['actions'][index] + + self.update_anim_data(action) + self.update_gui() + + def set_rotation(self, angle): + """ set the rotation of the current instance """ +# print "...setting instance rotation from %s to %s" % (self._rotation, angle) self._instances[0].setRotation(angle) self.get_instance_data(None, None, angle) + self.update_gui() +# print "...new internal FIFE rotation ", int(self._instances[0].getRotation()) + + def set_offset(self, x=None, y=None): + """ set x/y offset of current selected instance """ + if x is not None: + self._image.setXShift(x) + if y is not None: + self._image.setYShift(y) + + def update_anim_data(self, action=None): + """ update animation data for the current selected instance from FIFE's data structure + + @type animation FIFE animation + @return animation current selected animation + """ + if action: + animation_id = action.get2dGfxVisual().getAnimationIndexByAngle(self._fixed_rotation) + animation = self._animationpool.getAnimation(animation_id) - self.update_gui() + action_ids = [] + actions = [] + + try: + action_ids = self._object.getActionIds() + for id in action_ids: + actions.append(self._object.getAction(id)) + except: + pass + + self._anim_data = {} + self._anim_data['obj'] = animation + self._anim_data['id'] = animation_id + self._anim_data['frames'] = animation.getNumFrames() + self._anim_data['current'] = 0 + self._anim_data['actions'] = actions + self._anim_data['action_ids'] = action_ids + self._anim_data['default_action'] = self._object.getDefaultAction() + self._anim_data['action'] = action + + return animation def get_instance_data(self, timestamp=None, frame=None, angle=-1, instance=None): """ - grabs all available data from both object and instance - - checks if we already hold a record (namespace + object id) - + FIXME: 1.) we need to fix the instance rotation / rotation issue - 2.) use correct instance rotations to store data for _each_ available rotation - 3.) move record code out of this method """ visual = None self._avail_rotations = [] @@ -262,9 +529,10 @@ instance = self._instances[0] object = instance.getObject() + self._object = object self._namespace = object.getNamespace() self._object_id = object.getId() - + self._instance_id = instance.getId() if self._instance_id == '': @@ -289,50 +557,72 @@ print 'Fetching visual of object - failed. :/' raise -# print "Camera Tilt: ", self._camera.getTilt() -# print "Camera Rotation: ", self._camera.getRotation() +# print "Camera tilt: ", self._camera.getTilt() +# print "Camera rotation: ", self._camera.getRotation() +# print "Instance rotation: ", instance.getRotation() self._fixed_rotation = int(instance.getRotation() + abs( self._camera.getTilt() ) ) - self._fixed_rotation = visual.getClosestMatchingAngle(self._fixed_rotation) + self._fixed_rotation = instance.getRotation() +# self._fixed_rotation = visual.getClosestMatchingAngle(self._fixed_rotation) index = visual.getStaticImageIndexByAngle(self._fixed_rotation) - if index == -1: + if index is -1: # object is an animation self._animation = True + self._image = None + # no static image available, try default action action = object.getDefaultAction() + if action: - animation_id = action.get2dGfxVisual().getAnimationIndexByAngle(self._fixed_rotation) - animation = self.animationpool.getAnimation(animation_id) -# if timestamp is None and frame is not None: -# self._image = animation.getFrame(frame) -# elif timestamp is not None and frame is None: -# self._image = animation.getFrameByTimestamp(timestamp) -# else: - self._image = animation.getFrameByTimestamp(0) - index = self._image.getPoolId() - elif index != -1: + animation = self.update_anim_data(action) + + # update gui + if animation: + self._gui_anim_actions_dropdown._setItems(self._anim_data['action_ids']) + self._gui_anim_actions_dropdown._setSelected(0) + + if timestamp is None and frame is not None: + self._image = animation.getFrame(frame) + elif timestamp is not None and frame is None: + self._image = animation.getFrameByTimestamp(timestamp) + else: + self._image = animation.getFrameByTimestamp(0) + elif index is not -1: # object is a static image self._animation = False self._image = self.imagepool.getImage(index) if not self._animation: - rotation_tuple = visual.getStaticImageAngles() - for angle in rotation_tuple: - self._avail_rotations.append( str(angle) ) - + rotations = visual.getStaticImageAngles() + for angle in rotations: + self._avail_rotations.append(angle) + + self._image_default_x_offset = self._image.getXShift() + self._image_default_y_offset = self._image.getYShift() + else: + # these doesn't work correctly +# rotations = [0,60,120,180,240,300] -# FIXME: see l. 40 - self._editor.getActiveMapView().getController()._objectedit_rotations = self._avail_rotations -# end FIXME - + # testbench to get valid angles +# angle = 0 +# rotations = [] +# while angle != 360: +# angle += 10 +# rotations.append(angle) + + # estimated angles (for hex!) to make things work - use testbench to test + # various angles and note down the working ones (watch instance + # rotation and the animation rotations shown in the gui; valid + # angles are given once the rotations are in sync + self._avail_rotations = [9,69,139,169,249,319] + def input(self, instances): """ - if called _and_ the objectedit is active, + if called _and_ the user wishes to edit offsets, gets instance data and show gui - (see run.py, pump() ) """ if instances != self._instances: if self.active is True: @@ -351,8 +641,8 @@ self.update_gui() self.container.adaptLayout() self.container.show() - self._get_gui_size() - self.container._setPosition(self.position) else: self._reset() self.container.hide() + + self.container.adaptLayout()