Mana
Loading...
Searching...
No Matches
spritedef.cpp
Go to the documentation of this file.
1/*
2 * The Mana Client
3 * Copyright (C) 2004-2009 The Mana World Development Team
4 * Copyright (C) 2009-2012 The Mana Developers
5 *
6 * This file is part of The Mana Client.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "resources/spritedef.h"
23
24#include "log.h"
25#include "sprite.h"
26
27#include "resources/action.h"
28#include "resources/animation.h"
29#include "resources/dye.h"
30#include "resources/image.h"
32
33#include "configuration.h"
34
35#include "utils/xml.h"
36
37#include <set>
38
39std::set<std::string> processedFiles;
40
41Action *SpriteDef::getAction(const std::string &action) const
42{
43 auto i = mActions.find(action);
44
45 if (i == mActions.end())
46 {
47 Log::warn("No action \"%s\" defined!", action.c_str());
48 return nullptr;
49 }
50
51 return i->second;
52}
53
54SpriteDef *SpriteDef::load(const std::string &animationFile, int variant)
55{
56 std::string::size_type pos = animationFile.find('|');
57 std::string palettes;
58 if (pos != std::string::npos)
59 palettes = animationFile.substr(pos + 1);
60
61 processedFiles.clear();
62 processedFiles.insert(animationFile);
63
64 XML::Document doc(animationFile.substr(0, pos));
65 XML::Node rootNode = doc.rootNode();
66
67 if (!rootNode || rootNode.name() != "sprite")
68 {
69 Log::info("Error, failed to parse %s", animationFile.c_str());
70
71 std::string errorFile = paths.getStringValue("sprites")
72 + paths.getStringValue("spriteErrorFile");
73 if (animationFile != errorFile)
74 {
75 return load(errorFile, 0);
76 }
77
78 return nullptr;
79 }
80
81 auto *def = new SpriteDef;
82 def->loadSprite(rootNode, variant, palettes);
83 def->substituteActions();
84 return def;
85}
86
87void SpriteDef::substituteAction(std::string complete, std::string with)
88{
89 if (mActions.find(complete) == mActions.end())
90 {
91 auto i = mActions.find(with);
92 if (i != mActions.end())
93 {
94 mActions[complete] = i->second;
95 }
96 }
97}
98
100{
101 substituteAction(SpriteAction::STAND, SpriteAction::DEFAULT);
102 substituteAction(SpriteAction::MOVE, SpriteAction::STAND);
103 substituteAction(SpriteAction::ATTACK, SpriteAction::STAND);
104 substituteAction(SpriteAction::CAST_MAGIC, SpriteAction::ATTACK);
105 substituteAction(SpriteAction::USE_ITEM, SpriteAction::CAST_MAGIC);
106 substituteAction(SpriteAction::SIT, SpriteAction::STAND);
107 substituteAction(SpriteAction::SLEEP, SpriteAction::SIT);
108 substituteAction(SpriteAction::HURT, SpriteAction::STAND);
109 substituteAction(SpriteAction::DEAD, SpriteAction::HURT);
110}
111
112void SpriteDef::loadSprite(XML::Node spriteNode, int variant,
113 const std::string &palettes)
114{
115 // Get the variant
116 const int variantCount = spriteNode.getProperty("variants", 0);
117 int variant_offset = 0;
118
119 if (variantCount > 0 && variant < variantCount)
120 {
121 variant_offset =
122 variant * spriteNode.getProperty("variant_offset", 0);
123 }
124
125 for (auto node : spriteNode.children())
126 {
127 if (node.name() == "imageset")
128 {
129 loadImageSet(node, palettes);
130 }
131 else if (node.name() == "action")
132 {
133 loadAction(node, variant_offset);
134 }
135 else if (node.name() == "include")
136 {
137 includeSprite(node);
138 }
139 }
140}
141
142void SpriteDef::loadImageSet(XML::Node node, const std::string &palettes)
143{
144 const std::string name = node.getProperty("name", "");
145
146 // We don't allow redefining image sets. This way, an included sprite
147 // definition will use the already loaded image set with the same name.
148 if (mImageSets.find(name) != mImageSets.end())
149 return;
150
151 const int width = node.getProperty("width", 0);
152 const int height = node.getProperty("height", 0);
153 std::string imageSrc = node.getProperty("src", "");
154 Dye::instantiate(imageSrc, palettes);
155
157 auto imageSet = resman->getImageSet(imageSrc, width, height);
158 if (!imageSet)
159 {
160 Log::critical(strprintf("Couldn't load imageset (%s)!",
161 imageSrc.c_str()));
162 }
163
164 imageSet->setOffsetX(node.getProperty("offsetX", 0));
165 imageSet->setOffsetY(node.getProperty("offsetY", 0));
166 mImageSets[name] = imageSet;
167}
168
169void SpriteDef::loadAction(XML::Node node, int variant_offset)
170{
171 const std::string actionName = node.getProperty("name", "");
172 const std::string imageSetName = node.getProperty("imageset", "");
173
174 auto si = mImageSets.find(imageSetName);
175 if (si == mImageSets.end())
176 {
177 Log::warn("imageset \"%s\" not defined in %s",
178 imageSetName.c_str(), getIdPath().c_str());
179 return;
180 }
181 ImageSet *imageSet = si->second;
182
183 if (actionName == SpriteAction::INVALID)
184 {
185 Log::warn("Unknown action \"%s\" defined in %s",
186 actionName.c_str(), getIdPath().c_str());
187 return;
188 }
189 auto *action = new Action;
190 mActions[actionName] = action;
191
192 // When first action set it as default direction
193 if (mActions.size() == 1)
194 {
195 mActions[SpriteAction::DEFAULT] = action;
196 }
197
198 // Load animations
199 for (auto animationNode : node.children())
200 {
201 if (animationNode.name() == "animation")
202 {
203 loadAnimation(animationNode, action, imageSet, variant_offset);
204 }
205 }
206}
207
209 Action *action, ImageSet *imageSet,
210 int variant_offset)
211{
212 const std::string directionName =
213 animationNode.getProperty("direction", "");
214 const SpriteDirection directionType = makeSpriteDirection(directionName);
215
216 if (directionType == DIRECTION_INVALID)
217 {
218 Log::warn("Unknown direction \"%s\" used in %s",
219 directionName.c_str(), getIdPath().c_str());
220 return;
221 }
222
223 auto *animation = new Animation;
224 action->setAnimation(directionType, animation);
225
226 // Get animation frames
227 for (auto frameNode : animationNode.children())
228 {
229 const int delay = frameNode.getProperty("delay",
231 int offsetX = frameNode.getProperty("offsetX", 0) +
232 imageSet->getOffsetX();
233 int offsetY = frameNode.getProperty("offsetY", 0) +
234 imageSet->getOffsetY();
235
236 if (frameNode.name() == "frame")
237 {
238 const int index = frameNode.getProperty("index", -1);
239
240 if (index < 0)
241 {
242 Log::info("No valid value for 'index'");
243 continue;
244 }
245
246 Image *img = imageSet->get(index + variant_offset);
247
248 if (!img)
249 {
250 Log::info("No image at index %d", index + variant_offset);
251 continue;
252 }
253
254 animation->addFrame(img, delay, offsetX, offsetY);
255 }
256 else if (frameNode.name() == "sequence")
257 {
258 int start = frameNode.getProperty("start", -1);
259 const int end = frameNode.getProperty("end", -1);
260
261 if (start < 0 || end < 0)
262 {
263 Log::info("No valid value for 'start' or 'end'");
264 continue;
265 }
266
267 while (end >= start)
268 {
269 Image *img = imageSet->get(start + variant_offset);
270
271 if (!img)
272 {
273 Log::info("No image at index %d", start + variant_offset);
274 break;
275 }
276
277 animation->addFrame(img, delay, offsetX, offsetY);
278 start++;
279 }
280 }
281 else if (frameNode.name() == "end")
282 {
283 animation->addTerminator();
284 }
285 } // for frameNode
286}
287
289{
290 std::string filename = includeNode.getProperty("file", "");
291
292 if (filename.empty())
293 return;
294 filename = paths.getStringValue("sprites") + filename;
295
296 if (processedFiles.find(filename) != processedFiles.end())
297 {
298 Log::info("Error, Tried to include %s which already is included.",
299 filename.c_str());
300 return;
301 }
302 processedFiles.insert(filename);
303
304 XML::Document doc(filename);
305 XML::Node rootNode = doc.rootNode();
306
307 if (!rootNode || rootNode.name() != "sprite")
308 {
309 Log::info("Error, no sprite root node in %s", filename.c_str());
310 return;
311 }
312
313 loadSprite(rootNode, 0);
314}
315
317{
318 // Actions are shared, so ensure they are deleted only once.
319 std::set<Action*> actions;
320 for (const auto &action : mActions)
321 {
322 actions.insert(action.second);
323 }
324
325 for (auto action : actions)
326 {
327 delete action;
328 }
329}
330
332{
333 if (direction.empty() || direction == "default")
334 return DIRECTION_DEFAULT;
335 if (direction == "up")
336 return DIRECTION_UP;
337 if (direction == "left")
338 return DIRECTION_LEFT;
339 if (direction == "right")
340 return DIRECTION_RIGHT;
341 if (direction == "down")
342 return DIRECTION_DOWN;
343
344 return DIRECTION_INVALID;
345}
An action consists of several animations, one for each direction.
Definition action.h:32
void setAnimation(int direction, Animation *animation)
Definition action.cpp:47
An animation consists of several frames, each with their own delay and offset.
Definition animation.h:47
std::string getStringValue(const std::string &key) const
static void instantiate(std::string &target, const std::string &palettes)
Fills the blank in a dye placeholder with some palette names.
Definition dye.cpp:252
Stores a set of subimages originating from a single image.
Definition imageset.h:34
int getOffsetY() const
Definition imageset.h:66
Image * get(size_t i) const
Definition imageset.cpp:49
int getOffsetX() const
Definition imageset.h:60
Defines a class for loading and storing images.
Definition image.h:45
A class for loading and managing resources.
static ResourceManager * getInstance()
Returns an instance of the class, creating one if it does not already exist.
ResourceRef< ImageSet > getImageSet(const std::string &imagePath, int w, int h)
Loads a image set based on the image referenced by the given path and the supplied sprite sizes.
const std::string & getIdPath() const
Return the path identifying this resource.
Definition resource.h:57
Defines a class to load an animation.
Definition spritedef.h:87
void loadAnimation(XML::Node animationNode, Action *action, ImageSet *imageSet, int variant_offset)
Loads an animation element.
void loadAction(XML::Node node, int variant_offset)
Loads an action element.
void loadSprite(XML::Node spriteNode, int variant, const std::string &palettes=std::string())
Loads a sprite element.
void substituteActions()
Complete missing actions by copying existing ones.
Definition spritedef.cpp:99
std::map< std::string, Action * > mActions
Definition spritedef.h:150
std::map< std::string, ResourceRef< ImageSet > > mImageSets
Definition spritedef.h:149
~SpriteDef() override
void includeSprite(XML::Node includeNode)
Include another sprite into this one.
void loadImageSet(XML::Node node, const std::string &palettes)
Loads an imageset element.
static SpriteDirection makeSpriteDirection(const std::string &direction)
Converts a string into a SpriteDirection enum.
void substituteAction(std::string complete, std::string with)
When there are no animations defined for the action "complete", its animations become a copy of those...
Definition spritedef.cpp:87
static SpriteDef * load(const std::string &file, int variant)
Loads a sprite definition file.
Definition spritedef.cpp:54
Action * getAction(const std::string &action) const
Returns the specified action.
Definition spritedef.cpp:41
A helper class for parsing an XML document, which also cleans it up again (RAII).
Definition xml.h:190
Node rootNode() const
Returns the root node of the document (or NULL if there was a load error).
Definition xml.h:213
std::string_view name() const
Definition xml.h:46
int getProperty(const char *name, int def) const
Definition xml.h:144
Children children() const
Definition xml.h:97
Configuration paths
XML default paths information reader.
Definition client.cpp:99
void warn(const char *log_text,...) LOG_PRINTF_ATTR
void info(const char *log_text,...) LOG_PRINTF_ATTR
const int DEFAULT_FRAME_DELAY
Definition sprite.h:31
std::set< std::string > processedFiles
Definition spritedef.cpp:39
SpriteDirection
Definition spritedef.h:74
@ DIRECTION_INVALID
Definition spritedef.h:80
@ DIRECTION_DOWN
Definition spritedef.h:77
@ DIRECTION_LEFT
Definition spritedef.h:78
@ DIRECTION_UP
Definition spritedef.h:76
@ DIRECTION_DEFAULT
Definition spritedef.h:75
@ DIRECTION_RIGHT
Definition spritedef.h:79
std::string strprintf(char const *format,...)
A safe version of sprintf that returns a std::string of the result.