1 /** @file sprite.cpp Sprite definition accessor.
2 *
3 * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 * @authors Copyright © 2006-2015 Daniel Swanson <danij@dengine.net>
5 * @authors Copyright © 2006 Jamie Jones <jamie_jones_au@yahoo.com.au>
6 * @authors Copyright © 1993-1996 by id Software, Inc.
7 *
8 * @par License
9 * GPL: http://www.gnu.org/licenses/gpl.html
10 *
11 * <small>This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version. This program is distributed in the hope that it
15 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
17 * Public License for more details. You should have received a copy of the GNU
18 * General Public License along with this program; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA</small>
21 */
22
23 #include "doomsday/defs/sprite.h"
24 #include "doomsday/defs/ded.h"
25 #include "doomsday/UriValue"
26
27 #include <de/Record>
28 #include <de/RecordValue>
29 #include <de/DictionaryValue>
30
31 using namespace de;
32
33 namespace defn {
34
35 static QString const VAR_VIEWS ("views");
36 static QString const VAR_FRONT_ONLY("frontOnly");
37 static QString const VAR_MATERIAL ("material"); // UriValue
38 static QString const VAR_MIRROR_X ("mirrorX");
39
40 //---------------------------------------------------------------------------------------
41
CompiledSprite()42 CompiledSprite::CompiledSprite()
43 {}
44
CompiledSprite(Record const & spriteDef)45 CompiledSprite::CompiledSprite(Record const &spriteDef)
46 {
47 frontOnly = spriteDef.getb(VAR_FRONT_ONLY);
48
49 // Compile the views into a vector.
50 auto const &viewsDict = spriteDef.getdt(VAR_VIEWS).elements();
51 for (auto iter = viewsDict.begin(); iter != viewsDict.end(); ++iter)
52 {
53 ++viewCount;
54
55 int angle = iter->first.value->asInt();
56 if (views.size() <= angle) views.resize(angle + 1);
57
58 Record const &viewDef = iter->second->as<RecordValue>().dereference();
59 auto &view = views[angle];
60
61 view.uri = viewDef.get(VAR_MATERIAL).as<UriValue>().uri();
62 view.mirrorX = viewDef.getb(VAR_MIRROR_X);
63 }
64 }
65
66 //---------------------------------------------------------------------------------------
67
def()68 CompiledSpriteRecord &Sprite::def()
69 {
70 return static_cast<CompiledSpriteRecord &>(Definition::def());
71 }
72
def() const73 CompiledSpriteRecord const &Sprite::def() const
74 {
75 return static_cast<CompiledSpriteRecord const &>(Definition::def());
76 }
77
resetToDefaults()78 void Sprite::resetToDefaults()
79 {
80 Definition::resetToDefaults();
81
82 def().resetCompiled();
83
84 // Add all expected fields with their default values.
85 def().addBoolean(VAR_FRONT_ONLY, true); ///< @c true= only use the front View.
86 def().addDictionary(VAR_VIEWS);
87 }
88
viewsDict()89 DictionaryValue &Sprite::viewsDict()
90 {
91 return def()[VAR_VIEWS].value<DictionaryValue>();
92 }
93
94 /*DictionaryValue const &Sprite::viewsDict() const
95 {
96 return def().getdt(VAR_VIEWS);
97 }*/
98
addView(String material,dint angle,bool mirrorX)99 Record &Sprite::addView(String material, dint angle, bool mirrorX)
100 {
101 def().resetCompiled();
102
103 if (angle <= 0)
104 {
105 def().addDictionary(VAR_VIEWS);
106 }
107 def().set(VAR_FRONT_ONLY, angle <= 0);
108
109 auto *view = new Record;
110 view->add(VAR_MATERIAL).set(new UriValue(de::makeUri(material)));
111 view->addBoolean(VAR_MIRROR_X, mirrorX);
112 viewsDict().add(new NumberValue(de::max(0, angle - 1)), new RecordValue(view, RecordValue::OwnsRecord));
113 return *view;
114 }
115
viewCount() const116 dint Sprite::viewCount() const
117 {
118 return def().compiled().viewCount;
119 }
120
hasView(dint angle) const121 bool Sprite::hasView(dint angle) const
122 {
123 auto const &cmpl = def().compiled();
124
125 if (cmpl.frontOnly) angle = 0;
126
127 //return viewsDict().contains(NumberValue(angle));
128
129 return (angle < cmpl.views.size() && !cmpl.views.at(angle).uri.isEmpty());
130
131 /*for (Value const *val : geta("views").elements())
132 {
133 Record const &view = val->as<RecordValue>().dereference();
134 if (view.geti("angle") == angle)
135 return true;
136 }
137 return false;*/
138 }
139
140 #if 0
141 Record &Sprite::findView(dint angle)
142 {
143 if (angle && getb(VAR_FRONT_ONLY)) angle = 0;
144
145 return viewsDict().element(NumberValue(angle)).as<RecordValue>().dereference();
146
147 /*for (Value *val : def().geta("views").elements())
148 {
149 Record &view = val->as<RecordValue>().dereference();
150 if (view.geti("angle") == angle)
151 return view;
152 }
153 /// @throw MissingViewError Invalid angle specified.
154 throw MissingViewError("Sprite::view", "Unknown view:" + String::number(angle));*/
155 }
156
157 Record const *Sprite::tryFindView(dint angle) const
158 {
159 if (angle && getb(VAR_FRONT_ONLY)) angle = 0;
160
161 NumberValue const ang(angle);
162 auto const &elems = viewsDict().elements();
163 auto found = elems.find(&ang);
164 if (found != elems.end())
165 {
166 return found->second->as<RecordValue>().record();
167 }
168 return nullptr;
169 }
170 #endif
171
172 static de::Uri nullUri;
173
view(de::dint angle) const174 Sprite::View Sprite::view(de::dint angle) const
175 {
176 auto const &cmpl = def().compiled();
177
178 if (cmpl.frontOnly) angle = 0;
179
180 View v;
181 if (angle < cmpl.views.size())
182 {
183 v.material = &cmpl.views.at(angle).uri;
184 v.mirrorX = cmpl.views.at(angle).mirrorX;
185 }
186 else
187 {
188 v.material = &nullUri;
189 v.mirrorX = false;
190 }
191 return v;
192 }
193
viewMaterial(de::dint angle) const194 de::Uri const &Sprite::viewMaterial(de::dint angle) const
195 {
196 auto const &cmpl = def().compiled();
197 if (angle < cmpl.views.size())
198 {
199 return cmpl.views.at(angle).uri;
200 }
201 return nullUri;
202 }
203
nearestView(angle_t mobjAngle,angle_t angleToEye,bool noRotation) const204 Sprite::View Sprite::nearestView(angle_t mobjAngle, angle_t angleToEye, bool noRotation) const
205 {
206 dint angle = 0; // Use the front view (default).
207
208 if (!noRotation)
209 {
210 // Choose a view according to the relative angle with viewer (the eye).
211 angle = ((angleToEye - mobjAngle + (unsigned) (ANG45 / 2) * 9) - (unsigned) (ANGLE_180 / 16)) >> 28;
212 }
213
214 return view(angle);
215 }
216
217 } // namespace defn
218