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