Package horizons :: Package component :: Module coloroverlaycomponent
[hide private]
[frames] | no frames]

Source Code for Module horizons.component.coloroverlaycomponent

  1  # ################################################### 
  2  # Copyright (C) 2008-2017 The Unknown Horizons Team 
  3  # team@unknown-horizons.org 
  4  # This file is part of Unknown Horizons. 
  5  # 
  6  # Unknown Horizons is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, 
 12  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 14  # GNU General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the 
 18  # Free Software Foundation, Inc., 
 19  # 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
 20  # ################################################### 
 21   
 22  import logging 
 23  from collections import defaultdict 
 24  from operator import attrgetter 
 25   
 26  from fife import fife 
 27   
 28  import horizons.globals 
 29  from horizons.component import Component 
 30  from horizons.messaging import ActionChanged 
 31  from horizons.scheduler import Scheduler 
 32  from horizons.util.color import Color as UtilColor 
 33  from horizons.util.loaders.actionsetloader import ActionSetLoader 
34 35 36 -class ColorOverlayComponent(Component):
37 """Change parts of graphics dynamically on runtime ("color overlays" in FIFE terminology). 38 39 Supports multiple overlay sets for the same instance, and also 40 supports changing more than one color in the same overlay set. 41 42 While technically possible, it is not recommended to use the former: 43 You will need to make sure animation overlays exist for that z_order, 44 else a color overlay cannot be visible. 45 46 Because there usually is no way to guarantee this, it is **very much** 47 recommended to use one overlay on z_order `0` featuring multiple areas 48 with different colors instead, and to then replace those colors one by one. 49 We can guarantee that z_order of 0 works because `convertToOverlays` is 50 called when adding new color overlays, which converts the base image of 51 the current action to an animation overlay at precisely depth 0. 52 53 Directives to change colors look like this (for every action): 54 - [z_order, overlay action name, color to be replaced, target color to draw] 55 56 When in doubt, use 0 as z_order. 57 The overlay action name is the folder located next to other actions (e.g. idle). 58 Color to be replaced: List with three (rgb) or four (rgba) int elements. 59 In particular, [80, 0, 0] and [80, 0, 0, 128] are different colors! 60 Target color to draw: As above, or string interpreted as attribute of instance. 61 To access player colors, you can usually employ "owner.color". 62 63 All in all, a multi-color replacement could look like this example: 64 65 idle: 66 # color_idle: action set with three differently colored areas 67 # color red area in player color (alpha is set to 128 here) 68 - [0, color_idle, [255, 0, 0], [owner.color, 128]] 69 # also color green area *in the same images* in blue 70 - [0, color_idle, [0, 255, 0], [0, 0, 255, 128]] 71 # hide third (blue) area by setting alpha value to 0 72 - [0, color_idle, [0, 0, 255], [0, 0, 0, 0]] 73 74 # multiple single-overlay example (usually not what you want): 75 # idle: 76 # # color magenta area in player color (needs animation overlay at order 1) 77 # - [1, color1_idle, [255, 0, 255], [owner.color, 64]] 78 # # also color some other teal area in blue (needs animation overlay at order 2) 79 # - [2, color2_idle, [0, 255, 255], [0, 0, 255, 128]] 80 """ 81 NAME = "coloroverlay" 82 log = logging.getLogger('component.overlays') 83
84 - def __init__(self, overlays=None):
85 super().__init__() 86 self.overlays = overlays or {} 87 self.current_overlays = defaultdict(dict)
88 89 @property
90 - def action_set(self):
91 """E.g. 'as_lumberjack_barrack0' """ 92 return self.instance._action_set_id
93 94 @property
95 - def fife_instance(self):
96 return self.instance._instance
97 98 @property
99 - def identifier(self):
100 """E.g. 'idle_as_lumberjack_barrack0' """ 101 return self.fife_instance.getCurrentAction().getId()
102
103 - def initialize(self):
104 super().initialize() 105 ActionChanged.subscribe(self.update_overlay, self.instance) 106 ActionChanged.broadcast(self.instance, self.instance._action)
107
108 - def update_overlay(self, message):
109 #TODO Tracking is broken: remove all data stored for old action 110 # Ultimately it would be great to have current_overlays working... 111 self.current_overlays.clear() 112 try: 113 overlays = self.overlays[self.action_set][message.action] 114 except KeyError: 115 self.log.warning( 116 'No color overlay defined for action set `%s` and action `%s`. ' 117 'Consider using `null` overlays for this action.', 118 self.action_set, message.action) 119 return 120 121 for (z_order, overlay_name, from_color, to_color) in overlays: 122 if not self.current_overlays[z_order]: 123 self.add_overlay(overlay_name, z_order) 124 fife_from = fife.Color(*from_color) 125 try: 126 fife_to = fife.Color(*to_color) 127 except (TypeError, NotImplementedError): 128 color_attribute, alpha = to_color 129 color = attrgetter(color_attribute)(self.instance) 130 if isinstance(color, UtilColor) or isinstance(color, fife.Color): 131 fife_to = fife.Color(color.r, color.g, color.b, alpha) 132 else: 133 raise TypeError('Unknown color `{}` as attribute `{}`: ' 134 'Expected either fife.Color or horizons.util.Color.' 135 .format(color, to_color)) 136 self.change_color(z_order, fife_from, fife_to)
137
138 - def add_overlay(self, overlay_name, z_order):
139 """Creates color overlay recoloring the area defined in *overlay_set* 140 141 and adds it to fife instance. Note that a color overlay on *z_order* 142 can only be visible if an animation overlay with that specific order 143 exists as well. For order 0, `convertToOverlays()` makes sure they do. 144 """ 145 if not self.fife_instance.isAnimationOverlay(self.identifier): 146 # parameter False: do not convert color overlays attached to base 147 self.fife_instance.convertToOverlays(self.identifier, False) 148 149 try: 150 overlay_set = ActionSetLoader.get_set(self.action_set)[overlay_name] 151 except KeyError: 152 self.log.warning( 153 'Could not find overlay action set `%s` defined for object ' 154 '`%s` with id `%s`. Not adding overlay for this action.', 155 overlay_name, self.instance, self.identifier) 156 return 157 158 animationmanager = horizons.globals.fife.animationmanager 159 self.current_overlays[z_order] = overlay_set 160 for rotation, frames in overlay_set.items(): 161 id = '{}+{}'.format(self.identifier, rotation) 162 if animationmanager.exists(id): 163 ov_anim = animationmanager.getPtr(id) 164 else: 165 ov_anim = animationmanager.create(id) 166 for frame_img, frame_data in frames.items(): 167 try: 168 frame_length = frame_data[0] 169 except TypeError: 170 # not using atlases 171 frame_length = frame_data 172 pic = horizons.globals.fife.animationloader.load_image(frame_img, self.action_set, overlay_name, rotation) 173 frame_milliseconds = int(frame_length * 1000) 174 ov_anim.addFrame(pic, frame_milliseconds) 175 overlay = fife.OverlayColors(ov_anim) 176 self.fife_instance.addColorOverlay(self.identifier, rotation, z_order, overlay)
177
178 - def change_color(self, z_order, from_color, to_color):
179 """Changes color of *from_color*ed area to *to_color*. 180 181 color parameters: fife.Color objects 182 """ 183 for rotation in self.current_overlays[z_order]: 184 overlay = self.fife_instance.getColorOverlay(self.identifier, rotation, z_order) 185 overlay.changeColor(from_color, to_color)
186
187 - def remove_overlay(self):
188 """Removes color overlay recoloring the *color*-colored area from fife instance. 189 """ 190 for z_order, overlay_set in self.current_overlays.items(): 191 for rotation in overlay_set: 192 self.fife_instance.removeColorOverlay(self.identifier, rotation, z_order)
193
194 - def load(self, db, worldid):
195 super().load(db, worldid) 196 Scheduler().add_new_object(self.initialize, self, run_in=0)
197
198 - def remove(self):
199 """Removes all color overlays from the fife instance. """ 200 self.remove_overlay() 201 super().remove()
202