BGE patch: logic optimization part 2: remove inactive sensors from logic manager.
[blender.git] / source / gameengine / GameLogic / SCA_ISensor.cpp
1 /**
2  * Abstract class for sensor logic bricks
3  *
4  * $Id$
5  *
6  * ***** BEGIN GPL LICENSE BLOCK *****
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 Foundation,
20  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  *
22  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL LICENSE BLOCK *****
30  */
31
32 #include "SCA_ISensor.h"
33 #include "SCA_EventManager.h"
34 #include "SCA_LogicManager.h"
35
36 #ifdef HAVE_CONFIG_H
37 #include <config.h>
38 #endif
39
40 /* Native functions */
41 void    SCA_ISensor::ReParent(SCA_IObject* parent)
42 {
43         SCA_ILogicBrick::ReParent(parent);
44         // will be done when the sensor is activated
45         //m_eventmgr->RegisterSensor(this);
46         this->SetActive(false);
47 }
48
49
50 SCA_ISensor::SCA_ISensor(SCA_IObject* gameobj,
51                                                  class SCA_EventManager* eventmgr,
52                                                  PyTypeObject* T ) :
53         SCA_ILogicBrick(gameobj,T),
54         m_triggered(false)
55 {
56         m_links = 0;
57         m_suspended = false;
58         m_invert = false;
59         m_level = false;
60         m_reset = false;
61         m_pos_ticks = 0;
62         m_neg_ticks = 0;
63         m_pos_pulsemode = false;
64         m_neg_pulsemode = false;
65         m_pulse_frequency = 0;
66         
67         m_eventmgr = eventmgr;
68 }
69
70
71 SCA_ISensor::~SCA_ISensor()  
72 {
73         // intentionally empty
74 }
75
76 bool SCA_ISensor::IsPositiveTrigger() { 
77         bool result = false;
78         
79         if (m_eventval) {
80                 result = (m_eventval->GetNumber() != 0.0);
81         }
82         if (m_invert) {
83                 result = !result;
84         }
85         
86         return result;
87 }
88
89 void SCA_ISensor::SetPulseMode(bool posmode, 
90                                                            bool negmode,
91                                                            int freq) {
92         m_pos_pulsemode = posmode;
93         m_neg_pulsemode = negmode;
94         m_pulse_frequency = freq;
95 }
96
97 void SCA_ISensor::SetInvert(bool inv) {
98         m_invert = inv;
99 }
100
101 void SCA_ISensor::SetLevel(bool lvl) {
102         m_level = lvl;
103 }
104
105
106 float SCA_ISensor::GetNumber() {
107         return IsPositiveTrigger();
108 }
109
110 void SCA_ISensor::Suspend() {
111         m_suspended = true;
112 }
113
114 bool SCA_ISensor::IsSuspended() {
115         return m_suspended;
116 }
117
118 void SCA_ISensor::Resume() {
119         m_suspended = false;
120 }
121
122 void SCA_ISensor::Init() {
123         printf("Sensor %s has no init function, please report this bug to Blender.org\n", m_name.Ptr());
124 }
125
126 void SCA_ISensor::DecLink() {
127         m_links--;
128         if (m_links < 0) 
129         {
130                 printf("Warning: sensor %s has negative m_links: %d\n", m_name.Ptr(), m_links);
131                 m_links = 0;
132         }
133         if (!m_links)
134         {
135                 // sensor is detached from all controllers, initialize it so that it
136                 // is fresh as at startup when it is reattached again.
137                 UnregisterToManager();
138                 Init();
139         }
140 }
141
142 /* python integration */
143
144 PyTypeObject SCA_ISensor::Type = {
145         PyObject_HEAD_INIT(&PyType_Type)
146         0,
147         "SCA_ISensor",
148         sizeof(SCA_ISensor),
149         0,
150         PyDestructor,
151         0,
152         __getattr,
153         __setattr,
154         0, //&MyPyCompare,
155         __repr,
156         0, //&cvalue_as_number,
157         0,
158         0,
159         0,
160         0
161 };
162
163 PyParentObject SCA_ISensor::Parents[] = {
164         &SCA_ISensor::Type,
165         &SCA_ILogicBrick::Type,
166         &CValue::Type,
167         NULL
168 };
169 PyMethodDef SCA_ISensor::Methods[] = {
170         {"isPositive", (PyCFunction) SCA_ISensor::sPyIsPositive, 
171          METH_VARARGS, IsPositive_doc},
172         {"getUsePosPulseMode", (PyCFunction) SCA_ISensor::sPyGetUsePosPulseMode, 
173          METH_NOARGS, GetUsePosPulseMode_doc},
174         {"setUsePosPulseMode", (PyCFunction) SCA_ISensor::sPySetUsePosPulseMode, 
175          METH_VARARGS, SetUsePosPulseMode_doc},
176         {"getFrequency", (PyCFunction) SCA_ISensor::sPyGetFrequency, 
177          METH_NOARGS, GetFrequency_doc},
178         {"setFrequency", (PyCFunction) SCA_ISensor::sPySetFrequency, 
179          METH_VARARGS, SetFrequency_doc},
180         {"getUseNegPulseMode", (PyCFunction) SCA_ISensor::sPyGetUseNegPulseMode, 
181          METH_NOARGS, GetUseNegPulseMode_doc},
182         {"setUseNegPulseMode", (PyCFunction) SCA_ISensor::sPySetUseNegPulseMode, 
183          METH_VARARGS, SetUseNegPulseMode_doc},
184         {"getInvert", (PyCFunction) SCA_ISensor::sPyGetInvert, 
185          METH_NOARGS, GetInvert_doc},
186         {"setInvert", (PyCFunction) SCA_ISensor::sPySetInvert, 
187          METH_VARARGS, SetInvert_doc},
188         {"getLevel", (PyCFunction) SCA_ISensor::sPyGetLevel, 
189          METH_NOARGS, GetLevel_doc},
190         {"setLevel", (PyCFunction) SCA_ISensor::sPySetLevel, 
191          METH_VARARGS, SetLevel_doc},
192         {NULL,NULL} //Sentinel
193 };
194
195
196 PyObject*
197 SCA_ISensor::_getattr(const STR_String& attr)
198 {
199   _getattr_up(SCA_ILogicBrick);
200 }
201
202
203 void SCA_ISensor::RegisterToManager()
204 {
205         m_eventmgr->RegisterSensor(this);
206 }
207
208 void SCA_ISensor::UnregisterToManager()
209 {
210         m_eventmgr->RemoveSensor(this);
211 }
212
213 void SCA_ISensor::Activate(class SCA_LogicManager* logicmgr,      CValue* event)
214 {
215         
216         // calculate if a __triggering__ is wanted
217         // don't evaluate a sensor that is not connected to any controller
218         if (m_links && !m_suspended) {
219                 bool result = this->Evaluate(event);
220                 if (result) {
221                         logicmgr->AddActivatedSensor(this);     
222                 } else
223                 {
224                         /* First, the pulsing behaviour, if pulse mode is
225                          * active. It seems something goes wrong if pulse mode is
226                          * not set :( */
227                         if (m_pos_pulsemode) {
228                                 m_pos_ticks++;
229                                 if (m_pos_ticks > m_pulse_frequency) {
230                                         if ( this->IsPositiveTrigger() )
231                                         {
232                                                 logicmgr->AddActivatedSensor(this);
233                                         }
234                                         m_pos_ticks = 0;
235                                 } 
236                         }
237                         
238                         if (m_neg_pulsemode)
239                         {
240                                 m_neg_ticks++;
241                                 if (m_neg_ticks > m_pulse_frequency) {
242                                         if (!this->IsPositiveTrigger() )
243                                         {
244                                                 logicmgr->AddActivatedSensor(this);
245                                         }
246                                         m_neg_ticks = 0;
247                                 }
248                         }
249                 }
250         } 
251 }
252
253 /* Python functions: */
254 char SCA_ISensor::IsPositive_doc[] = 
255 "isPositive()\n"
256 "\tReturns whether the sensor is registered a positive event.\n";
257 PyObject* SCA_ISensor::PyIsPositive(PyObject* self, PyObject* args, PyObject* kwds)
258 {
259         int retval = IsPositiveTrigger();
260         return PyInt_FromLong(retval);
261 }
262
263 /**
264  * getUsePulseMode: getter for the pulse mode (KX_TRUE = on)
265  */
266 char SCA_ISensor::GetUsePosPulseMode_doc[] = 
267 "getUsePosPulseMode()\n"
268 "\tReturns whether positive pulse mode is active.\n";
269 PyObject* SCA_ISensor::PyGetUsePosPulseMode(PyObject* self)
270 {
271         return BoolToPyArg(m_pos_pulsemode);
272 }
273
274 /**
275  * setUsePulseMode: setter for the pulse mode (KX_TRUE = on)
276  */
277 char SCA_ISensor::SetUsePosPulseMode_doc[] = 
278 "setUsePosPulseMode(pulse?)\n"
279 "\t - pulse? : Pulse when a positive event occurs?\n"
280 "\t            (KX_TRUE, KX_FALSE)\n"
281 "\tSet whether to do pulsing when positive pulses occur.\n";
282 PyObject* SCA_ISensor::PySetUsePosPulseMode(PyObject* self, PyObject* args, PyObject* kwds)
283 {
284         int pyarg = 0;
285         if(!PyArg_ParseTuple(args, "i", &pyarg)) { return NULL; }
286         m_pos_pulsemode = PyArgToBool(pyarg);
287         Py_Return;
288 }
289
290 /**
291  * getFrequency: getter for the pulse mode interval
292  */
293 char SCA_ISensor::GetFrequency_doc[] = 
294 "getFrequency()\n"
295 "\tReturns the frequency of the updates in pulse mode.\n" ;
296 PyObject* SCA_ISensor::PyGetFrequency(PyObject* self)
297 {
298         return PyInt_FromLong(m_pulse_frequency);
299 }
300
301 /**
302  * setFrequency: setter for the pulse mode (KX_TRUE = on)
303  */
304 char SCA_ISensor::SetFrequency_doc[] = 
305 "setFrequency(pulse_frequency)\n"
306 "\t- pulse_frequency: The frequency of the updates in pulse mode (integer)"
307 "\tSet the frequency of the updates in pulse mode.\n"
308 "\tIf the frequency is negative, it is set to 0.\n" ;
309 PyObject* SCA_ISensor::PySetFrequency(PyObject* self, PyObject* args, PyObject* kwds)
310 {
311         int pulse_frequencyArg = 0;
312
313         if(!PyArg_ParseTuple(args, "i", &pulse_frequencyArg)) {
314                 return NULL;
315         }
316         
317         /* We can do three things here: clip, ignore and raise an exception.  */
318         /* Exceptions don't work yet, ignoring is not desirable now...        */
319         if (pulse_frequencyArg < 0) {
320                 pulse_frequencyArg = 0;
321         };      
322         m_pulse_frequency = pulse_frequencyArg;
323
324         Py_Return;
325 }
326
327
328 char SCA_ISensor::GetInvert_doc[] = 
329 "getInvert()\n"
330 "\tReturns whether or not pulses from this sensor are inverted.\n" ;
331 PyObject* SCA_ISensor::PyGetInvert(PyObject* self)
332 {
333         return BoolToPyArg(m_invert);
334 }
335
336 char SCA_ISensor::SetInvert_doc[] = 
337 "setInvert(invert?)\n"
338 "\t- invert?: Invert the event-values? (KX_TRUE, KX_FALSE)\n"
339 "\tSet whether to invert pulses.\n";
340 PyObject* SCA_ISensor::PySetInvert(PyObject* self, PyObject* args, PyObject* kwds)
341 {
342         int pyarg = 0;
343         if(!PyArg_ParseTuple(args, "i", &pyarg)) { return NULL; }
344         m_invert = PyArgToBool(pyarg);
345         Py_Return;
346 }
347
348 char SCA_ISensor::GetLevel_doc[] = 
349 "getLevel()\n"
350 "\tReturns whether this sensor is a level detector or a edge detector.\n"
351 "\tIt makes a difference only in case of logic state transition (state actuator).\n"
352 "\tA level detector will immediately generate a pulse, negative or positive\n"
353 "\tdepending on the sensor condition, as soon as the state is activated.\n"
354 "\tA edge detector will wait for a state change before generating a pulse.\n";
355 PyObject* SCA_ISensor::PyGetLevel(PyObject* self)
356 {
357         return BoolToPyArg(m_level);
358 }
359
360 char SCA_ISensor::SetLevel_doc[] = 
361 "setLevel(level?)\n"
362 "\t- level?: Detect level instead of edge? (KX_TRUE, KX_FALSE)\n"
363 "\tSet whether to detect level or edge transition when entering a state.\n";
364 PyObject* SCA_ISensor::PySetLevel(PyObject* self, PyObject* args, PyObject* kwds)
365 {
366         int pyarg = 0;
367         if(!PyArg_ParseTuple(args, "i", &pyarg)) { return NULL; }
368         m_level = PyArgToBool(pyarg);
369         Py_Return;
370 }
371
372 char SCA_ISensor::GetUseNegPulseMode_doc[] = 
373 "getUseNegPulseMode()\n"
374 "\tReturns whether negative pulse mode is active.\n";
375 PyObject* SCA_ISensor::PyGetUseNegPulseMode(PyObject* self)
376 {
377         return BoolToPyArg(m_neg_pulsemode);
378 }
379
380 char SCA_ISensor::SetUseNegPulseMode_doc[] = 
381 "setUseNegPulseMode(pulse?)\n"
382 "\t - pulse? : Pulse when a negative event occurs?\n"
383 "\t            (KX_TRUE, KX_FALSE)\n"
384 "\tSet whether to do pulsing when negative pulses occur.\n";
385 PyObject* SCA_ISensor::PySetUseNegPulseMode(PyObject* self, PyObject* args, PyObject* kwds)
386 {
387         int pyarg = 0;
388         if(!PyArg_ParseTuple(args, "i", &pyarg)) { return NULL; }
389         m_neg_pulsemode = PyArgToBool(pyarg);
390         Py_Return;
391 }
392
393 /* eof */