1 /***************************************************************************
2  *   Copyright (C) 2005-2019 by the FIFE team                              *
3  *   http://www.fifengine.net                                              *
4  *   This file is part of FIFE.                                            *
5  *                                                                         *
6  *   FIFE is free software; you can redistribute it and/or                 *
7  *   modify it under the terms of the GNU Lesser General Public            *
8  *   License as published by the Free Software Foundation; either          *
9  *   version 2.1 of the License, or (at your option) any later version.    *
10  *                                                                         *
11  *   This library is distributed in the hope that it will be useful,       *
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
14  *   Lesser General Public License for more details.                       *
15  *                                                                         *
16  *   You should have received a copy of the GNU Lesser General Public      *
17  *   License along with this library; if not, write to the                 *
18  *   Free Software Foundation, Inc.,                                       *
19  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
20  ***************************************************************************/
21 
22 // Standard C++ library includes
23 #include <vector>
24 
25 // 3rd party library includes
26 
27 // FIFE includes
28 // These includes are split up in two parts, separated by one empty line
29 // First block: files included from the FIFE root src directory
30 // Second block: files included from the same folder
31 
32 #include "cell.h"
33 #include "cellcache.h"
34 #include "instance.h"
35 #include "layer.h"
36 #include "location.h"
37 #include "trigger.h"
38 
39 namespace FIFE {
40 
41 	class TriggerChangeListener : public CellChangeListener, public InstanceChangeListener, public InstanceDeleteListener {
42 	public:
TriggerChangeListener(Trigger * trigger)43 		TriggerChangeListener(Trigger* trigger)	{
44 			m_trigger = trigger;
45 		}
~TriggerChangeListener()46 		virtual ~TriggerChangeListener() {}
47 
48 		// InstanceDeleteListener callback
onInstanceDeleted(Instance * instance)49 		virtual void onInstanceDeleted(Instance* instance) {
50 			const std::vector<TriggerCondition>& types = m_trigger->getTriggerConditions();
51 			if (std::find(types.begin(), types.end(), INSTANCE_TRIGGER_DELETE) != types.end()) {
52 				m_trigger->setTriggered();
53 			}
54 			m_trigger->detach();
55 		}
56 
57 		// CellChangeListener callback
onInstanceEnteredCell(Cell * cell,Instance * instance)58 		virtual void onInstanceEnteredCell(Cell* cell, Instance* instance) {
59 			const std::vector<TriggerCondition>& types = m_trigger->getTriggerConditions();
60 			if (std::find(types.begin(), types.end(), CELL_TRIGGER_ENTER) != types.end()) {
61 				const std::vector<Instance*>& restrict = m_trigger->getEnabledInstances();
62 				if (m_trigger->isEnabledForAllInstances() ||
63 					std::find(restrict.begin(), restrict.end(), instance) != restrict.end()) {
64 					m_trigger->setTriggered();
65 				}
66 			}
67 		}
68 
69 		// CellChangeListener callback
onInstanceExitedCell(Cell * cell,Instance * instance)70 		virtual void onInstanceExitedCell(Cell* cell, Instance* instance) {
71 			const std::vector<TriggerCondition>& types = m_trigger->getTriggerConditions();
72 			if (std::find(types.begin(), types.end(), CELL_TRIGGER_EXIT) != types.end()) {
73 				const std::vector<Instance*>& restrict = m_trigger->getEnabledInstances();
74 				if (m_trigger->isEnabledForAllInstances() ||
75 					std::find(restrict.begin(), restrict.end(), instance) != restrict.end()) {
76 					m_trigger->setTriggered();
77 				}
78 			}
79 		}
80 
81 		// CellChangeListener callback
onBlockingChangedCell(Cell * cell,CellTypeInfo type,bool blocks)82 		virtual void onBlockingChangedCell(Cell* cell, CellTypeInfo type, bool blocks) {
83 			const std::vector<TriggerCondition>& types = m_trigger->getTriggerConditions();
84 			if (std::find(types.begin(), types.end(), CELL_TRIGGER_BLOCKING_CHANGE) != types.end()) {
85 				m_trigger->setTriggered();
86 			}
87 		}
88 
89 		// InstanceChangeListener callback
onInstanceChanged(Instance * instance,InstanceChangeInfo info)90 		virtual void onInstanceChanged(Instance* instance, InstanceChangeInfo info) {
91 			const std::vector<TriggerCondition>& types = m_trigger->getTriggerConditions();
92 			if (m_trigger->getAttached() == instance && (info & ICHANGE_CELL) == ICHANGE_CELL) {
93 				m_trigger->move();
94 			}
95 
96 			if (types.empty()) {
97 				return;
98 			}
99 
100 			if ((info & ICHANGE_LOC) == ICHANGE_LOC && std::find(types.begin(), types.end(), INSTANCE_TRIGGER_LOCATION) != types.end()) {
101 				m_trigger->setTriggered();
102 			} else if ((info & ICHANGE_ROTATION) == ICHANGE_ROTATION && std::find(types.begin(), types.end(), INSTANCE_TRIGGER_ROTATION) != types.end()) {
103 				m_trigger->setTriggered();
104 			} else if ((info & ICHANGE_SPEED) == ICHANGE_SPEED && std::find(types.begin(), types.end(), INSTANCE_TRIGGER_SPEED) != types.end()) {
105 				m_trigger->setTriggered();
106 			} else if ((info & ICHANGE_ACTION) == ICHANGE_ACTION && std::find(types.begin(), types.end(), INSTANCE_TRIGGER_ACTION) != types.end()) {
107 				m_trigger->setTriggered();
108 			} else if ((info & ICHANGE_TIME_MULTIPLIER) == ICHANGE_TIME_MULTIPLIER && std::find(types.begin(), types.end(), INSTANCE_TRIGGER_TIME_MULTIPLIER) != types.end()) {
109 				m_trigger->setTriggered();
110 			} else if ((info & ICHANGE_SAYTEXT) == ICHANGE_SAYTEXT && std::find(types.begin(), types.end(), INSTANCE_TRIGGER_SAYTEXT) != types.end()) {
111 				m_trigger->setTriggered();
112 			} else if ((info & ICHANGE_BLOCK) == ICHANGE_BLOCK && std::find(types.begin(), types.end(), INSTANCE_TRIGGER_BLOCK) != types.end()) {
113 				m_trigger->setTriggered();
114 			} else if ((info & ICHANGE_CELL) == ICHANGE_CELL && std::find(types.begin(), types.end(), INSTANCE_TRIGGER_CELL) != types.end()) {
115 				m_trigger->setTriggered();
116 			} else if ((info & ICHANGE_TRANSPARENCY) == ICHANGE_TRANSPARENCY && std::find(types.begin(), types.end(), INSTANCE_TRIGGER_TRANSPARENCY) != types.end()) {
117 				m_trigger->setTriggered();
118 			} else if ((info & ICHANGE_VISIBLE) == ICHANGE_VISIBLE && std::find(types.begin(), types.end(), INSTANCE_TRIGGER_VISIBLE) != types.end()) {
119 				m_trigger->setTriggered();
120 			} else if ((info & ICHANGE_STACKPOS) == ICHANGE_STACKPOS && std::find(types.begin(), types.end(), INSTANCE_TRIGGER_STACKPOS) != types.end()) {
121 				m_trigger->setTriggered();
122 			} else if ((info & ICHANGE_VISUAL) == ICHANGE_VISUAL && std::find(types.begin(), types.end(), INSTANCE_TRIGGER_VISUAL) != types.end()) {
123 				m_trigger->setTriggered();
124 			}
125 		}
126 
127 	private:
128 		Trigger* m_trigger;
129 	};
130 
Trigger()131 	Trigger::Trigger():
132 		m_name(""),
133 		m_triggered(false),
134 		m_enabledAll(false),
135 		m_attached(NULL) {
136 			m_changeListener = new TriggerChangeListener(this);
137 	}
138 
Trigger(const std::string & name)139 	Trigger::Trigger(const std::string& name):
140 		m_name(name),
141 		m_triggered(false),
142 		m_enabledAll(false),
143 		m_attached(NULL) {
144 			m_changeListener = new TriggerChangeListener(this);
145 	}
146 
~Trigger()147 	Trigger::~Trigger() {
148 		detach();
149 		std::vector<Cell*>::iterator it = m_assigned.begin();
150 		for (; it != m_assigned.end(); ++it) {
151 			(*it)->removeChangeListener(m_changeListener);
152 		}
153 		delete m_changeListener;
154 	}
155 
addTriggerListener(ITriggerListener * listener)156 	void Trigger::addTriggerListener(ITriggerListener* listener) {
157 		std::vector<ITriggerListener*>::iterator it = std::find(m_triggerListeners.begin(), m_triggerListeners.end(), listener);
158 		if (it == m_triggerListeners.end()) {
159 			m_triggerListeners.push_back(listener);
160 		}
161 	}
162 
removeTriggerListener(ITriggerListener * listener)163 	void Trigger::removeTriggerListener(ITriggerListener* listener) {
164 		std::vector<ITriggerListener*>::iterator i = m_triggerListeners.begin();
165 		while (i != m_triggerListeners.end()) {
166 			if ((*i) == listener) {
167 				// set the pointer to null, so it can be removed later
168 				*i = NULL;
169 				return;
170 			}
171 			++i;
172 		}
173 	}
174 
reset()175 	void Trigger::reset() {
176 		m_triggered = false;
177 	}
178 
setTriggered()179 	void Trigger::setTriggered() {
180 		if (!m_triggered) {
181 			m_triggered = true;
182 			std::vector<ITriggerListener*>::iterator i = m_triggerListeners.begin();
183 			while (i != m_triggerListeners.end()) {
184 				if (*i) {
185 					(*i)->onTriggered();
186 				}
187 				++i;
188 			}
189 		}
190 		// remove null pointer
191 		m_triggerListeners.erase(std::remove(m_triggerListeners.begin(), m_triggerListeners.end(), (ITriggerListener*)NULL), m_triggerListeners.end());
192 	}
193 
addTriggerCondition(TriggerCondition type)194 	void Trigger::addTriggerCondition(TriggerCondition type) {
195 		std::vector<TriggerCondition>::iterator it = std::find(m_triggerConditions.begin(), m_triggerConditions.end(), type);
196 		if (it == m_triggerConditions.end()) {
197 			m_triggerConditions.push_back(type);
198 		}
199 	}
200 
getTriggerConditions()201 	const std::vector<TriggerCondition>& Trigger::getTriggerConditions() {
202 		return m_triggerConditions;
203 	}
204 
removeTriggerCondition(TriggerCondition type)205 	void Trigger::removeTriggerCondition(TriggerCondition type) {
206 		std::vector<TriggerCondition>::iterator it = std::find(m_triggerConditions.begin(), m_triggerConditions.end(), type);
207 		if (it != m_triggerConditions.end()) {
208 			m_triggerConditions.erase(it);
209 		}
210 	}
211 
enableForInstance(Instance * instance)212 	void Trigger::enableForInstance(Instance* instance) {
213 		std::vector<Instance*>::iterator it = std::find(m_enabledInstances.begin(), m_enabledInstances.end(), instance);
214 		if (it == m_enabledInstances.end()) {
215 			m_enabledInstances.push_back(instance);
216 		}
217 	}
218 
getEnabledInstances()219 	const std::vector<Instance*>& Trigger::getEnabledInstances() {
220 		return m_enabledInstances;
221 	}
222 
disableForInstance(Instance * instance)223 	void Trigger::disableForInstance(Instance* instance) {
224 		std::vector<Instance*>::iterator it = std::find(m_enabledInstances.begin(), m_enabledInstances.end(), instance);
225 		if (it != m_enabledInstances.end()) {
226 			m_enabledInstances.erase(it);
227 		}
228 	}
229 
enableForAllInstances()230 	void Trigger::enableForAllInstances() {
231 		m_enabledAll = true;
232 	}
233 
isEnabledForAllInstances()234 	bool Trigger::isEnabledForAllInstances() {
235 		return m_enabledAll;
236 	}
237 
disableForAllInstances()238 	void Trigger::disableForAllInstances() {
239 		m_enabledAll = false;
240 	}
241 
assign(Layer * layer,const ModelCoordinate & pt)242 	void Trigger::assign(Layer* layer, const ModelCoordinate& pt) {
243 		Cell* cell = layer->getCellCache()->getCell(pt);
244 		if (!cell) {
245 			return;
246 		}
247 		std::vector<Cell*>::iterator it = std::find(m_assigned.begin(), m_assigned.end(), cell);
248 		if (it == m_assigned.end()) {
249 			m_assigned.push_back(cell);
250 			cell->addChangeListener(m_changeListener);
251 		}
252 	}
253 
remove(Layer * layer,const ModelCoordinate & pt)254 	void Trigger::remove(Layer* layer, const ModelCoordinate& pt) {
255 		Cell* cell = layer->getCellCache()->getCell(pt);
256 		if (!cell) {
257 			return;
258 		}
259 		std::vector<Cell*>::iterator it = std::find(m_assigned.begin(), m_assigned.end(), cell);
260 		if (it != m_assigned.end()) {
261 			m_assigned.erase(it);
262 			cell->removeChangeListener(m_changeListener);
263 		}
264 	}
265 
assign(Cell * cell)266 	void Trigger::assign(Cell* cell) {
267 		std::vector<Cell*>::iterator it = std::find(m_assigned.begin(), m_assigned.end(), cell);
268 		if (it == m_assigned.end()) {
269 			m_assigned.push_back(cell);
270 			cell->addChangeListener(m_changeListener);
271 		}
272 	}
273 
remove(Cell * cell)274 	void Trigger::remove(Cell* cell) {
275 		std::vector<Cell*>::iterator it = std::find(m_assigned.begin(), m_assigned.end(), cell);
276 		if (it != m_assigned.end()) {
277 			m_assigned.erase(it);
278 			cell->removeChangeListener(m_changeListener);
279 		}
280 	}
281 
getAssignedCells()282 	const std::vector<Cell*>& Trigger::getAssignedCells() {
283 		return m_assigned;
284 	}
285 
attach(Instance * instance)286 	void Trigger::attach(Instance* instance) {
287 		if (instance == m_attached) {
288 			return;
289 		}
290 
291 		if (m_attached) {
292 			detach();
293 		}
294 		m_attached = instance;
295 		m_attached->addDeleteListener(m_changeListener);
296 		m_attached->addChangeListener(m_changeListener);
297 	}
298 
detach()299 	void Trigger::detach() {
300 		if (m_attached) {
301 			m_attached->removeDeleteListener(m_changeListener);
302 			m_attached->removeChangeListener(m_changeListener);
303 			m_attached = 0;
304 		}
305 	}
306 
move()307 	void Trigger::move() {
308 		if (m_assigned.empty()) {
309 			return;
310 		}
311 		ModelCoordinate newPos = m_attached->getLocationRef().getLayerCoordinates();
312 		ModelCoordinate oldPos = m_attached->getOldLocationRef().getLayerCoordinates();
313 		moveTo(newPos, oldPos);
314 	}
315 
moveTo(const ModelCoordinate & newPos,const ModelCoordinate & oldPos)316 	void Trigger::moveTo(const ModelCoordinate& newPos, const ModelCoordinate& oldPos) {
317 		ModelCoordinate mc(newPos.x-oldPos.x, newPos.y-oldPos.y);
318 
319 		CellCache* cache = m_attached->getLocationRef().getLayer()->getCellCache();
320 		std::vector<Cell*> newCells;
321 		std::vector<Cell*>::iterator it = m_assigned.begin();
322 		for (; it != m_assigned.end(); ++it) {
323 			ModelCoordinate coord = (*it)->getLayerCoordinates();
324 			coord.x += mc.x;
325 			coord.y += mc.y;
326 			Cell* c = cache->getCell(coord);
327 			if (c) {
328 				newCells.push_back(c);
329 			}
330 		}
331 		for (it = newCells.begin(); it != newCells.end(); ++it) {
332 			std::vector<Cell*>::iterator found = std::find(m_assigned.begin(), m_assigned.end(), *it);
333 			if (found != m_assigned.end()) {
334 				m_assigned.erase(found);
335 			} else {
336 				// add new
337 				(*it)->addChangeListener(m_changeListener);
338 			}
339 		}
340 		// remove old
341 		for (it = m_assigned.begin(); it != m_assigned.end(); ++it) {
342 			(*it)->removeChangeListener(m_changeListener);
343 		}
344 		m_assigned = newCells;
345 	}
346 }
347