1 | /* |
---|
2 | * ORXONOX - the hottest 3D action shooter ever to exist |
---|
3 | * > www.orxonox.net < |
---|
4 | * |
---|
5 | * |
---|
6 | * License notice: |
---|
7 | * |
---|
8 | * This program is free software; you can redistribute it and/or |
---|
9 | * modify it under the terms of the GNU General Public License |
---|
10 | * as published by the Free Software Foundation; either version 2 |
---|
11 | * of the License, or (at your option) 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, write to the Free Software |
---|
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
---|
21 | * |
---|
22 | * Author: |
---|
23 | * Benjamin Knecht |
---|
24 | * Co-authors: |
---|
25 | * ... |
---|
26 | * |
---|
27 | */ |
---|
28 | |
---|
29 | /** |
---|
30 | @file DistanceTrigger.cc |
---|
31 | @brief Implementation of the DistanceTrigger class. |
---|
32 | @ingroup NormalTrigger |
---|
33 | */ |
---|
34 | |
---|
35 | #include "DistanceTrigger.h" |
---|
36 | |
---|
37 | #include "core/CoreIncludes.h" |
---|
38 | #include "core/XMLPort.h" |
---|
39 | |
---|
40 | #include "worldentities/pawns/Pawn.h" |
---|
41 | |
---|
42 | #include "DistanceTriggerBeacon.h" |
---|
43 | |
---|
44 | namespace orxonox |
---|
45 | { |
---|
46 | |
---|
47 | /*static*/ const std::string DistanceTrigger::beaconModeOff_s = "off"; |
---|
48 | /*static*/ const std::string DistanceTrigger::beaconModeIdentify_s = "identify"; |
---|
49 | /*static*/ const std::string DistanceTrigger::beaconModeExlcude_s = "exclude"; |
---|
50 | |
---|
51 | RegisterClass(DistanceTrigger); |
---|
52 | |
---|
53 | /** |
---|
54 | @brief |
---|
55 | Constructor. Registers and initializes the object. |
---|
56 | @param creator |
---|
57 | The creator of this trigger. |
---|
58 | */ |
---|
59 | DistanceTrigger::DistanceTrigger(Context* context) : Trigger(context) |
---|
60 | { |
---|
61 | RegisterObject(DistanceTrigger); |
---|
62 | |
---|
63 | this->distance_ = 100; |
---|
64 | this->targetMask_.exclude(Class(BaseObject)); |
---|
65 | this->targetName_ = ""; |
---|
66 | this->beaconMask_.exclude(Class(BaseObject)); |
---|
67 | this->beaconMask_.include(Class(DistanceTriggerBeacon)); |
---|
68 | } |
---|
69 | |
---|
70 | /** |
---|
71 | @brief |
---|
72 | Destructor. |
---|
73 | */ |
---|
74 | DistanceTrigger::~DistanceTrigger() |
---|
75 | { |
---|
76 | |
---|
77 | } |
---|
78 | |
---|
79 | /** |
---|
80 | @brief |
---|
81 | Method for creating a DistanceTrigger object through XML. |
---|
82 | */ |
---|
83 | void DistanceTrigger::XMLPort(Element& xmlelement, XMLPort::Mode mode) |
---|
84 | { |
---|
85 | SUPER(DistanceTrigger, XMLPort, xmlelement, mode); |
---|
86 | |
---|
87 | XMLPortParam(DistanceTrigger, "distance", setDistance, getDistance, xmlelement, mode).defaultValues(100.0f); |
---|
88 | XMLPortParamLoadOnly(DistanceTrigger, "target", addTarget, xmlelement, mode).defaultValues("Pawn"); |
---|
89 | XMLPortParam(DistanceTrigger, "beaconMode", setBeaconMode, getBeaconMode, xmlelement, mode); |
---|
90 | XMLPortParam(DistanceTrigger, "targetname", setTargetName, getTargetName, xmlelement, mode); |
---|
91 | } |
---|
92 | |
---|
93 | /** |
---|
94 | @brief |
---|
95 | Add some target to the DistanceTrigger. |
---|
96 | @param targetStr |
---|
97 | The target class name as a string. |
---|
98 | */ |
---|
99 | void DistanceTrigger::addTarget(const std::string& targetStr) |
---|
100 | { |
---|
101 | Identifier* targetId = ClassByString(targetStr); |
---|
102 | |
---|
103 | // Checks whether the target is (or is derived from) a Pawn and if so set the PlayerTrigger aspect of this trigger to be for the player, meaning, that from this Trigger one can derive the Pawn that caused it to trigger. |
---|
104 | Identifier* pawnId = Class(Pawn); |
---|
105 | if(targetId->isA(pawnId)) |
---|
106 | this->setForPlayer(true); |
---|
107 | |
---|
108 | if (targetId == nullptr) |
---|
109 | { |
---|
110 | orxout(internal_error, context::triggers) << "\"" << targetStr << "\" is not a valid class name to include in ClassTreeMask (in " << this->getName() << ", class " << this->getIdentifier()->getName() << ')' << endl; |
---|
111 | return; |
---|
112 | } |
---|
113 | |
---|
114 | // Add the target to the target mask. |
---|
115 | this->targetMask_.include(targetId); |
---|
116 | |
---|
117 | // The DistanceTrigger shouldn't react to itself or other triggers. |
---|
118 | this->targetMask_.exclude(Class(TriggerBase), true); |
---|
119 | |
---|
120 | // We only want WorldEntities (since only they have a position) |
---|
121 | ClassTreeMask WEMask; |
---|
122 | WEMask.include(Class(WorldEntity)); |
---|
123 | this->targetMask_ *= WEMask; |
---|
124 | |
---|
125 | this->notifyMaskUpdate(); // Inform interested parties that the target mask has been updated. |
---|
126 | } |
---|
127 | |
---|
128 | /** |
---|
129 | @brief |
---|
130 | Remove some target from the DistanceTrigger. |
---|
131 | @param targetStr |
---|
132 | The target class name as a string. |
---|
133 | */ |
---|
134 | void DistanceTrigger::removeTarget(const std::string& targetStr) |
---|
135 | { |
---|
136 | Identifier* targetId = ClassByString(targetStr); |
---|
137 | this->targetMask_.exclude(targetId); |
---|
138 | } |
---|
139 | |
---|
140 | /** |
---|
141 | @brief |
---|
142 | Check, whether there are entities that are targets of this DistanceTrigger in its range. |
---|
143 | @return |
---|
144 | Returns true if there are valid entities in its range. |
---|
145 | */ |
---|
146 | bool DistanceTrigger::checkDistance() |
---|
147 | { |
---|
148 | // Check whether there is a cached object, it still exists and whether it is still in range, if so nothing further needs to be done. |
---|
149 | if(this->cache_ != nullptr) |
---|
150 | { |
---|
151 | if((this->cache_->getWorldPosition() - this->getWorldPosition()).length() < this->distance_) |
---|
152 | return true; |
---|
153 | else |
---|
154 | this->cache_.reset(); |
---|
155 | } |
---|
156 | |
---|
157 | // Check for new objects that are in range |
---|
158 | ClassTreeMask targetMask = this->targetMask_; |
---|
159 | // If we are in identify-mode another target mask has to be applies to find the DistanceTriggerBeacons. |
---|
160 | if(this->beaconMode_ == DistanceTriggerBeaconMode::identify) |
---|
161 | targetMask = this->beaconMask_; |
---|
162 | |
---|
163 | // Iterate through all objects that are targets of the DistanceTrigger. |
---|
164 | for (ClassTreeMaskObjectIterator it = targetMask.begin(); it != targetMask.end(); ++it) |
---|
165 | { |
---|
166 | WorldEntity* entity = static_cast<WorldEntity*>(*it); |
---|
167 | |
---|
168 | // If the DistanceTrigger is in identify-mode and the DistanceTriggerBeacon attached to the object has the wrong name we ignore it. |
---|
169 | if(this->beaconMode_ == DistanceTriggerBeaconMode::identify) |
---|
170 | { |
---|
171 | if(entity->getName() != this->targetName_) |
---|
172 | continue; |
---|
173 | // If the object, the DistanceTriggerBeacon is attached to, is not a target of this DistanceMultiTrigger. |
---|
174 | else if(this->targetMask_.isExcluded(entity->getParent()->getIdentifier())) |
---|
175 | continue; |
---|
176 | } |
---|
177 | |
---|
178 | // If the DistanceTrigger is in exclude mode and the DistanceTriggerBeacon attached to the object has the right name, we ignore it. |
---|
179 | if(this->beaconMode_ == DistanceTriggerBeaconMode::exclude) |
---|
180 | { |
---|
181 | |
---|
182 | const std::set<WorldEntity*> attachedObjects = entity->getAttachedObjects(); |
---|
183 | bool found = false; |
---|
184 | for(WorldEntity* attachedObject : attachedObjects) |
---|
185 | { |
---|
186 | if(attachedObject->isA(ClassIdentifier<DistanceTriggerBeacon>::getIdentifier()) && static_cast<DistanceTriggerBeacon*>(attachedObject)->getName() == this->targetName_) |
---|
187 | { |
---|
188 | found = true; |
---|
189 | break; |
---|
190 | } |
---|
191 | } |
---|
192 | if(found) |
---|
193 | continue; |
---|
194 | } |
---|
195 | |
---|
196 | // Check if the entity is in range. |
---|
197 | Vector3 distanceVec = entity->getWorldPosition() - this->getWorldPosition(); |
---|
198 | if (distanceVec.length() < this->distance_) |
---|
199 | { |
---|
200 | // If the target is a player (resp. is a, or is derived from a, Pawn) the triggeringPlayer is set to the target entity. |
---|
201 | if(this->isForPlayer()) |
---|
202 | { |
---|
203 | // Change the entity to the parent of the DistanceTriggerBeacon (if in identify-mode), which is the entity to which the beacon is attached. |
---|
204 | if(this->beaconMode_ == DistanceTriggerBeaconMode::identify) |
---|
205 | entity = entity->getParent(); |
---|
206 | |
---|
207 | Pawn* pawn = orxonox_cast<Pawn*>(entity); |
---|
208 | if(pawn != nullptr) |
---|
209 | this->setTriggeringPawn(pawn); |
---|
210 | else |
---|
211 | orxout(internal_warning, context::triggers) << "Pawn was nullptr." << endl; |
---|
212 | } |
---|
213 | |
---|
214 | // Add the entity to the cache. |
---|
215 | this->cache_ = entity; |
---|
216 | |
---|
217 | return true; |
---|
218 | } |
---|
219 | } |
---|
220 | |
---|
221 | return false; |
---|
222 | } |
---|
223 | |
---|
224 | /** |
---|
225 | @brief |
---|
226 | Set the beacon mode. |
---|
227 | @param mode |
---|
228 | The mode as an enum. |
---|
229 | */ |
---|
230 | void DistanceTrigger::setBeaconModeDirect(DistanceTriggerBeaconMode mode) |
---|
231 | { |
---|
232 | this->beaconMode_ = mode; |
---|
233 | } |
---|
234 | |
---|
235 | /** |
---|
236 | @brief |
---|
237 | Get the beacon mode. |
---|
238 | @return |
---|
239 | Returns the mode as a string. |
---|
240 | */ |
---|
241 | const std::string& DistanceTrigger::getBeaconMode(void) const |
---|
242 | { |
---|
243 | switch(this->getBeaconModeDirect()) |
---|
244 | { |
---|
245 | case DistanceTriggerBeaconMode::off : |
---|
246 | return DistanceTrigger::beaconModeOff_s; |
---|
247 | case DistanceTriggerBeaconMode::identify: |
---|
248 | return DistanceTrigger::beaconModeIdentify_s; |
---|
249 | case DistanceTriggerBeaconMode::exclude: |
---|
250 | return DistanceTrigger::beaconModeExlcude_s; |
---|
251 | default : |
---|
252 | assert(0); // This is impossible. |
---|
253 | return BLANKSTRING; |
---|
254 | } |
---|
255 | } |
---|
256 | |
---|
257 | /** |
---|
258 | @brief |
---|
259 | Set the beacon mode. |
---|
260 | @param mode |
---|
261 | The mode as a string. |
---|
262 | */ |
---|
263 | void DistanceTrigger::setBeaconMode(const std::string& mode) |
---|
264 | { |
---|
265 | if(mode == DistanceTrigger::beaconModeOff_s) |
---|
266 | this->setBeaconModeDirect(DistanceTriggerBeaconMode::off); |
---|
267 | else if(mode == DistanceTrigger::beaconModeIdentify_s) |
---|
268 | this->setBeaconModeDirect(DistanceTriggerBeaconMode::identify); |
---|
269 | else if(mode == DistanceTrigger::beaconModeExlcude_s) |
---|
270 | this->setBeaconModeDirect(DistanceTriggerBeaconMode::exclude); |
---|
271 | else |
---|
272 | orxout(internal_error, context::triggers) << "Invalid beacon mode in DistanceTrigger." << endl; |
---|
273 | } |
---|
274 | |
---|
275 | /** |
---|
276 | @brief |
---|
277 | Check whether the DistanceTrigger is triggered. |
---|
278 | It is triggered if it is triggered according only to its mode (i.e. its sub-triggers) and if a target is in range. |
---|
279 | @param mode |
---|
280 | The mode for which it is tested, whether the DistanceTrigger is triggered. |
---|
281 | @return |
---|
282 | Returns true if it is triggered ,false if not. |
---|
283 | */ |
---|
284 | bool DistanceTrigger::isTriggered(TriggerMode mode) |
---|
285 | { |
---|
286 | if (Trigger::isTriggered(mode)) |
---|
287 | return checkDistance(); |
---|
288 | else |
---|
289 | return false; |
---|
290 | } |
---|
291 | } |
---|