1 /** @file thinkerdata.cpp  Base class for thinker private data.
2  *
3  * @authors Copyright (c) 2014-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  *
5  * @par License
6  * GPL: http://www.gnu.org/licenses/gpl.html
7  *
8  * <small>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 the
10  * Free Software Foundation; either version 2 of the License, or (at your
11  * option) any later version. This program is distributed in the hope that it
12  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
14  * Public License for more details. You should have received a copy of the GNU
15  * General Public License along with this program; if not, write to the Free
16  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17  * 02110-1301 USA</small>
18  */
19 
20 #include "doomsday/world/thinkerdata.h"
21 #include "doomsday/world/map.h"
22 
23 #include <QMultiHash>
24 
25 using namespace de;
26 
27 static QMultiHash<Id::Type, ThinkerData *> thinkerLookup;
28 
DENG2_PIMPL(ThinkerData)29 DENG2_PIMPL(ThinkerData)
30 {
31     thinker_s *think;
32     Id id; ///< Internal unique ID.
33     Record names;
34 
35     Impl(Public *i, Id const &id)
36         : Base(i)
37         , think(0)
38         , id(id)
39     {}
40 
41     Impl(Public *i, Impl const &other)
42         : Base(i)
43         , think(other.think)
44         , id(other.id)
45         , names(other.names)
46     {}
47 
48     ~Impl()
49     {
50         thinkerLookup.remove(id, &self());
51 
52         DENG2_FOR_PUBLIC_AUDIENCE2(Deletion, i)
53         {
54             i->thinkerBeingDeleted(*think);
55         }
56     }
57 
58     DENG2_PIMPL_AUDIENCE(Deletion)
59 };
60 
DENG2_AUDIENCE_METHOD(ThinkerData,Deletion)61 DENG2_AUDIENCE_METHOD(ThinkerData, Deletion)
62 
63 ThinkerData::ThinkerData(Id const &id)
64     : d(new Impl(this, id))
65 {
66     if (d->id)
67     {
68         thinkerLookup.insert(d->id, this);
69     }
70 }
71 
ThinkerData(ThinkerData const & other)72 ThinkerData::ThinkerData(ThinkerData const &other) : d(new Impl(this, *other.d))
73 {
74     if (d->id)
75     {
76         thinkerLookup.insert(d->id, this);
77     }
78 }
79 
id() const80 Id const &ThinkerData::id() const
81 {
82     return d->id;
83 }
84 
setId(Id const & id)85 void ThinkerData::setId(Id const &id)
86 {
87     thinkerLookup.remove(d->id, this);
88     thinkerLookup.insert(id, this);
89 
90     d->id = id;
91 }
92 
setThinker(thinker_s * thinker)93 void ThinkerData::setThinker(thinker_s *thinker)
94 {
95     d->think = thinker;
96 }
97 
think()98 void ThinkerData::think()
99 {
100     /// @todo If there is a think function in the Record, call it now. -jk
101 }
102 
duplicate() const103 Thinker::IData *ThinkerData::duplicate() const
104 {
105     return new ThinkerData(*this);
106 }
107 
thinker()108 thinker_s &ThinkerData::thinker()
109 {
110     DENG2_ASSERT(d->think != 0);
111     return *d->think;
112 }
113 
thinker() const114 thinker_s const &ThinkerData::thinker() const
115 {
116     DENG2_ASSERT(d->think != 0);
117     return *d->think;
118 }
119 
objectNamespace()120 Record &ThinkerData::objectNamespace()
121 {
122     return d->names;
123 }
124 
objectNamespace() const125 Record const &ThinkerData::objectNamespace() const
126 {
127     return d->names;
128 }
129 
initBindings()130 void ThinkerData::initBindings()
131 {}
132 
operator >>(Writer & to) const133 void ThinkerData::operator >> (Writer &to) const
134 {
135     to << world::InternalSerialId(world::THINKER_DATA)
136        << d->id
137        << Record(d->names, Record::IgnoreDoubleUnderscoreMembers);
138 }
139 
operator <<(Reader & from)140 void ThinkerData::operator << (Reader &from)
141 {
142     thinkerLookup.remove(d->id, this);
143 
144     world::InternalSerialId sid;
145     from >> sid;
146 
147     switch (sid)
148     {
149     case world::THINKER_DATA:
150         from >> d->id >> d->names;
151         break;
152 
153     default:
154         throw DeserializationError("ThinkerData::operator <<",
155                                    "Invalid serial identifier " +
156                                    String::number(sid));
157     }
158 
159     // The thinker has a new ID.
160     thinkerLookup.insert(d->id, this);
161 }
162 
find(Id const & id)163 ThinkerData *ThinkerData::find(Id const &id)
164 {
165     auto found = thinkerLookup.constFind(id);
166     if (found != thinkerLookup.constEnd())
167     {
168         return found.value();
169     }
170     return nullptr;
171 }
172 
173 #ifdef DENG2_DEBUG
174 duint32 ThinkerData::DebugCounter::total = 0;
175 ThinkerData::DebugValidator ensureAllPrivateDataIsReleased;
176 #endif
177