Renamed Rect to Rectf
[supertux.git] / src / sprite / sprite_data.cpp
1 //  SuperTux
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "sprite/sprite_data.hpp"
18
19 #include <stdexcept>
20 #include <sstream>
21
22 #include "lisp/list_iterator.hpp"
23 #include "util/log.hpp"
24 #include "util/reader.hpp"
25
26 SpriteData::Action::Action() :
27   name(),
28   x_offset(),
29   y_offset(),
30   hitbox_w(),
31   hitbox_h(),
32   z_order(),
33   fps(),
34   surfaces()
35 {
36   x_offset = 0;
37   y_offset = 0;
38   hitbox_w = 0;
39   hitbox_h = 0;
40   z_order = 0;
41   fps = 10;
42 }
43
44 SpriteData::Action::~Action()
45 {
46   for(std::vector<Surface*>::iterator i = surfaces.begin();
47       i != surfaces.end(); ++i)
48     delete *i;
49 }
50
51 SpriteData::SpriteData(const Reader& lisp, const std::string& basedir) :
52   actions(),
53   name()
54 {
55   lisp::ListIterator iter(&lisp);
56   while(iter.next()) {
57     if(iter.item() == "name") {
58       iter.value()->get(name);
59     } else if(iter.item() == "action") {
60       parse_action(*iter.lisp(), basedir);
61     } else {
62       log_warning << "Unknown sprite field: " << iter.item() << std::endl;
63     }
64   }
65   if(actions.empty())
66     throw std::runtime_error("Error: Sprite without actions.");
67 }
68
69 SpriteData::~SpriteData()
70 {
71   for(Actions::iterator i=actions.begin(); i != actions.end(); ++i)
72     delete i->second;
73 }
74
75 void
76 SpriteData::parse_action(const Reader& lisp, const std::string& basedir)
77 {
78   Action* action = new Action;
79
80   if(!lisp.get("name", action->name)) {
81     if(!actions.empty())
82       throw std::runtime_error(
83         "If there are more than one action, they need names!");
84   }
85   std::vector<float> hitbox;
86   if (lisp.get("hitbox", hitbox)) {
87     switch(hitbox.size()) {
88       case 4:
89         action->hitbox_h = hitbox[3];
90         action->hitbox_w = hitbox[2];
91
92         //fall-through
93       case 2:
94         action->y_offset = hitbox[1];
95         action->x_offset = hitbox[0];
96         break;
97
98       default:
99         throw std::runtime_error("hitbox should specify 2/4 coordinates");
100     }
101   }
102   lisp.get("z-order", action->z_order);
103   lisp.get("fps", action->fps);
104
105   std::string mirror_action;
106   lisp.get("mirror-action", mirror_action);
107   if(!mirror_action.empty()) {
108     Action* act_tmp = get_action(mirror_action);
109     if(act_tmp == NULL) {
110       throw std::runtime_error("Could not mirror action. Action not found.\n"
111                                "Mirror actions must be defined after the real one!");
112     } else {
113       float max_w = 0;
114       float max_h = 0;
115       for(int i = 0; static_cast<unsigned int>(i) < act_tmp->surfaces.size(); i++) {
116         Surface* surface = new Surface(*(act_tmp->surfaces[i]));
117         surface->hflip();
118         max_w = std::max(max_w, (float) surface->get_width());
119         max_h = std::max(max_h, (float) surface->get_height());
120         action->surfaces.push_back(surface);
121       }
122       if (action->hitbox_w < 1) action->hitbox_w = max_w - action->x_offset;
123       if (action->hitbox_h < 1) action->hitbox_h = max_h - action->y_offset;
124     }
125   } else { // Load images
126     std::vector<std::string> images;
127     if(!lisp.get("images", images)) {
128       std::stringstream msg;
129       msg << "Sprite '" << name << "' contains no images in action '"
130           << action->name << "'.";
131       throw std::runtime_error(msg.str());
132     }
133
134     float max_w = 0;
135     float max_h = 0;
136     for(std::vector<std::string>::size_type i = 0; i < images.size(); i++) {
137       Surface* surface = new Surface(basedir + images[i]);
138       max_w = std::max(max_w, (float) surface->get_width());
139       max_h = std::max(max_h, (float) surface->get_height());
140       action->surfaces.push_back(surface);
141     }
142     if (action->hitbox_w < 1) action->hitbox_w = max_w - action->x_offset;
143     if (action->hitbox_h < 1) action->hitbox_h = max_h - action->y_offset;
144   }
145   actions[action->name] = action;
146 }
147
148 SpriteData::Action*
149 SpriteData::get_action(std::string act)
150 {
151   Actions::iterator i = actions.find(act);
152   if(i == actions.end()) {
153     return 0;
154   }
155   return i->second;
156 }
157
158 /* EOF */