BGE logic: new sensor "tap" option to generate automatically on/off pulses
[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 // needed for IsTriggered()
36 #include "SCA_PythonController.h"
37
38 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif
41
42 /* Native functions */
43 void    SCA_ISensor::ReParent(SCA_IObject* parent)
44 {
45         SCA_ILogicBrick::ReParent(parent);
46         // will be done when the sensor is activated
47         //m_eventmgr->RegisterSensor(this);
48         this->SetActive(false);
49 }
50
51
52 SCA_ISensor::SCA_ISensor(SCA_IObject* gameobj,
53                                                  class SCA_EventManager* eventmgr,
54                                                  PyTypeObject* T ) :
55         SCA_ILogicBrick(gameobj,T)
56 {
57         m_links = 0;
58         m_suspended = false;
59         m_invert = false;
60         m_level = false;
61         m_tap = false;
62         m_reset = false;
63         m_pos_ticks = 0;
64         m_neg_ticks = 0;
65         m_pos_pulsemode = false;
66         m_neg_pulsemode = false;
67         m_pulse_frequency = 0;
68         m_state = false;
69         m_prev_state = false;
70         
71         m_eventmgr = eventmgr;
72 }
73
74
75 SCA_ISensor::~SCA_ISensor()  
76 {
77         // intentionally empty
78 }
79
80 bool SCA_ISensor::IsPositiveTrigger() { 
81         bool result = false;
82         
83         if (m_eventval) {
84                 result = (m_eventval->GetNumber() != 0.0);
85         }
86         if (m_invert) {
87                 result = !result;
88         }
89         
90         return result;
91 }
92
93 void SCA_ISensor::SetPulseMode(bool posmode, 
94                                                            bool negmode,
95                                                            int freq) {
96         m_pos_pulsemode = posmode;
97         m_neg_pulsemode = negmode;
98         m_pulse_frequency = freq;
99 }
100
101 void SCA_ISensor::SetInvert(bool inv) {
102         m_invert = inv;
103 }
104
105 void SCA_ISensor::SetLevel(bool lvl) {
106         m_level = lvl;
107 }
108
109 void SCA_ISensor::SetTap(bool tap) {
110         m_tap = tap;
111 }
112
113
114 double SCA_ISensor::GetNumber() {
115         return GetState();
116 }
117
118 void SCA_ISensor::Suspend() {
119         m_suspended = true;
120 }
121
122 bool SCA_ISensor::IsSuspended() {
123         return m_suspended;
124 }
125
126 void SCA_ISensor::Resume() {
127         m_suspended = false;
128 }
129
130 void SCA_ISensor::Init() {
131         printf("Sensor %s has no init function, please report this bug to Blender.org\n", m_name.Ptr());
132 }
133
134 void SCA_ISensor::DecLink() {
135         m_links--;
136         if (m_links < 0) 
137         {
138                 printf("Warning: sensor %s has negative m_links: %d\n", m_name.Ptr(), m_links);
139                 m_links = 0;
140         }
141         if (!m_links)
142         {
143                 // sensor is detached from all controllers, remove it from manager
144                 UnregisterToManager();
145         }
146 }
147
148 void SCA_ISensor::RegisterToManager()
149 {
150         // sensor is just activated, initialize it
151         Init();
152         m_state = false;
153         m_newControllers.erase(m_newControllers.begin(), m_newControllers.end());
154         m_eventmgr->RegisterSensor(this);
155 }
156
157 void SCA_ISensor::UnregisterToManager()
158 {
159         m_eventmgr->RemoveSensor(this);
160 }
161
162 void SCA_ISensor::Activate(class SCA_LogicManager* logicmgr,      CValue* event)
163 {
164         
165         // calculate if a __triggering__ is wanted
166         // don't evaluate a sensor that is not connected to any controller
167         if (m_links && !m_suspended) {
168                 bool result = this->Evaluate(event);
169                 // store the state for the rest of the logic system
170                 m_prev_state = m_state;
171                 m_state = this->IsPositiveTrigger();
172                 if (result) {
173                         // the sensor triggered this frame
174                         if (m_state || !m_tap) {
175                                 logicmgr->AddActivatedSensor(this);     
176                                 // reset these counters so that pulse are synchronized with transition
177                                 m_pos_ticks = 0;
178                                 m_neg_ticks = 0;
179                         } else
180                         {
181                                 result = false;
182                         }
183                 } else
184                 {
185                         /* First, the pulsing behaviour, if pulse mode is
186                          * active. It seems something goes wrong if pulse mode is
187                          * not set :( */
188                         if (m_pos_pulsemode) {
189                                 m_pos_ticks++;
190                                 if (m_pos_ticks > m_pulse_frequency) {
191                                         if ( m_state )
192                                         {
193                                                 logicmgr->AddActivatedSensor(this);
194                                                 result = true;
195                                         }
196                                         m_pos_ticks = 0;
197                                 } 
198                         }
199                         // negative pulse doesn't make sense in tap mode, skip
200                         if (m_neg_pulsemode && !m_tap)
201                         {
202                                 m_neg_ticks++;
203                                 if (m_neg_ticks > m_pulse_frequency) {
204                                         if (!m_state )
205                                         {
206                                                 logicmgr->AddActivatedSensor(this);
207                                         }
208                                         m_neg_ticks = 0;
209                                 }
210                         }
211                 }
212                 if (m_tap)
213                 {
214                         // in tap mode: we send always a negative pulse immediately after a positive pulse
215                         if (!result)
216                         {
217                                 // the sensor did not trigger on this frame
218                                 if (m_prev_state)
219                                 {
220                                         // but it triggered on previous frame => send a negative pulse
221                                         logicmgr->AddActivatedSensor(this);     
222                                 }
223                                 // in any case, absence of trigger means sensor off
224                                 m_state = false;
225                         }
226                 }
227                 if (!m_newControllers.empty())
228                 {
229                         if (!IsActive() && m_level)
230                         {
231                                 // This level sensor is connected to at least one controller that was just made 
232                                 // active but it did not generate an event yet, do it now to those controllers only 
233                                 for (std::vector<SCA_IController*>::iterator ci=m_newControllers.begin();
234                                          ci != m_newControllers.end(); ci++)
235                                 {
236                                         logicmgr->AddTriggeredController(*ci, this);
237                                 }
238                         }
239                         // clear the list. Instead of using clear, which also release the memory,
240                         // use erase, which keeps the memory available for next time.
241                         m_newControllers.erase(m_newControllers.begin(), m_newControllers.end());
242                 }
243         } 
244 }
245 /* ----------------------------------------------- */
246 /* Python Functions                                                        */
247 /* ----------------------------------------------- */
248
249 //Deprecated Functions ------>
250 const char SCA_ISensor::IsPositive_doc[] = 
251 "isPositive()\n"
252 "\tReturns whether the sensor is in an active state.\n";
253 PyObject* SCA_ISensor::PyIsPositive()
254 {
255         ShowDeprecationWarning("isPositive()", "the read-only positive property");
256         int retval = GetState();
257         return PyInt_FromLong(retval);
258 }
259
260 const char SCA_ISensor::IsTriggered_doc[] = 
261 "isTriggered()\n"
262 "\tReturns whether the sensor has triggered the current controller.\n";
263 PyObject* SCA_ISensor::PyIsTriggered()
264 {
265         ShowDeprecationWarning("isTriggered()", "the read-only triggered property");
266         // check with the current controller
267         int retval = 0;
268         if (SCA_PythonController::m_sCurrentController)
269                 retval = SCA_PythonController::m_sCurrentController->IsTriggered(this);
270         return PyInt_FromLong(retval);
271 }
272
273 /**
274  * getUsePulseMode: getter for the pulse mode (KX_TRUE = on)
275  */
276 const char SCA_ISensor::GetUsePosPulseMode_doc[] = 
277 "getUsePosPulseMode()\n"
278 "\tReturns whether positive pulse mode is active.\n";
279 PyObject* SCA_ISensor::PyGetUsePosPulseMode()
280 {
281         ShowDeprecationWarning("getUsePosPulseMode()", "the usePosPulseMode property");
282         return BoolToPyArg(m_pos_pulsemode);
283 }
284
285 /**
286  * setUsePulseMode: setter for the pulse mode (KX_TRUE = on)
287  */
288 const char SCA_ISensor::SetUsePosPulseMode_doc[] = 
289 "setUsePosPulseMode(pulse?)\n"
290 "\t - pulse? : Pulse when a positive event occurs?\n"
291 "\t            (KX_TRUE, KX_FALSE)\n"
292 "\tSet whether to do pulsing when positive pulses occur.\n";
293 PyObject* SCA_ISensor::PySetUsePosPulseMode(PyObject* args)
294 {
295         ShowDeprecationWarning("setUsePosPulseMode()", "the usePosPulseMode property");
296         int pyarg = 0;
297         if(!PyArg_ParseTuple(args, "i:setUsePosPulseMode", &pyarg)) { return NULL; }
298         m_pos_pulsemode = PyArgToBool(pyarg);
299         Py_RETURN_NONE;
300 }
301
302 /**
303  * getFrequency: getter for the pulse mode interval
304  */
305 const char SCA_ISensor::GetFrequency_doc[] = 
306 "getFrequency()\n"
307 "\tReturns the frequency of the updates in pulse mode.\n" ;
308 PyObject* SCA_ISensor::PyGetFrequency()
309 {
310         ShowDeprecationWarning("getFrequency()", "the frequency property");
311         return PyInt_FromLong(m_pulse_frequency);
312 }
313
314 /**
315  * setFrequency: setter for the pulse mode (KX_TRUE = on)
316  */
317 const char SCA_ISensor::SetFrequency_doc[] = 
318 "setFrequency(pulse_frequency)\n"
319 "\t- pulse_frequency: The frequency of the updates in pulse mode (integer)"
320 "\tSet the frequency of the updates in pulse mode.\n"
321 "\tIf the frequency is negative, it is set to 0.\n" ;
322 PyObject* SCA_ISensor::PySetFrequency(PyObject* args)
323 {
324         ShowDeprecationWarning("setFrequency()", "the frequency property");
325         int pulse_frequencyArg = 0;
326
327         if(!PyArg_ParseTuple(args, "i:setFrequency", &pulse_frequencyArg)) {
328                 return NULL;
329         }
330         
331         /* We can do three things here: clip, ignore and raise an exception.  */
332         /* Exceptions don't work yet, ignoring is not desirable now...        */
333         if (pulse_frequencyArg < 0) {
334                 pulse_frequencyArg = 0;
335         };      
336         m_pulse_frequency = pulse_frequencyArg;
337
338         Py_RETURN_NONE;
339 }
340
341
342 const char SCA_ISensor::GetInvert_doc[] = 
343 "getInvert()\n"
344 "\tReturns whether or not pulses from this sensor are inverted.\n" ;
345 PyObject* SCA_ISensor::PyGetInvert()
346 {
347         ShowDeprecationWarning("getInvert()", "the invert property");
348         return BoolToPyArg(m_invert);
349 }
350
351 const char SCA_ISensor::SetInvert_doc[] = 
352 "setInvert(invert?)\n"
353 "\t- invert?: Invert the event-values? (KX_TRUE, KX_FALSE)\n"
354 "\tSet whether to invert pulses.\n";
355 PyObject* SCA_ISensor::PySetInvert(PyObject* args)
356 {
357         ShowDeprecationWarning("setInvert()", "the invert property");
358         int pyarg = 0;
359         if(!PyArg_ParseTuple(args, "i:setInvert", &pyarg)) { return NULL; }
360         m_invert = PyArgToBool(pyarg);
361         Py_RETURN_NONE;
362 }
363
364 const char SCA_ISensor::GetLevel_doc[] = 
365 "getLevel()\n"
366 "\tReturns whether this sensor is a level detector or a edge detector.\n"
367 "\tIt makes a difference only in case of logic state transition (state actuator).\n"
368 "\tA level detector will immediately generate a pulse, negative or positive\n"
369 "\tdepending on the sensor condition, as soon as the state is activated.\n"
370 "\tA edge detector will wait for a state change before generating a pulse.\n";
371 PyObject* SCA_ISensor::PyGetLevel()
372 {
373         ShowDeprecationWarning("getLevel()", "the level property");
374         return BoolToPyArg(m_level);
375 }
376
377 const char SCA_ISensor::SetLevel_doc[] = 
378 "setLevel(level?)\n"
379 "\t- level?: Detect level instead of edge? (KX_TRUE, KX_FALSE)\n"
380 "\tSet whether to detect level or edge transition when entering a state.\n";
381 PyObject* SCA_ISensor::PySetLevel(PyObject* args)
382 {
383         ShowDeprecationWarning("setLevel()", "the level property");
384         int pyarg = 0;
385         if(!PyArg_ParseTuple(args, "i:setLevel", &pyarg)) { return NULL; }
386         m_level = PyArgToBool(pyarg);
387         Py_RETURN_NONE;
388 }
389
390 const char SCA_ISensor::GetUseNegPulseMode_doc[] = 
391 "getUseNegPulseMode()\n"
392 "\tReturns whether negative pulse mode is active.\n";
393 PyObject* SCA_ISensor::PyGetUseNegPulseMode()
394 {
395         ShowDeprecationWarning("getUseNegPulseMode()", "the useNegPulseMode property");
396         return BoolToPyArg(m_neg_pulsemode);
397 }
398
399 const char SCA_ISensor::SetUseNegPulseMode_doc[] = 
400 "setUseNegPulseMode(pulse?)\n"
401 "\t - pulse? : Pulse when a negative event occurs?\n"
402 "\t            (KX_TRUE, KX_FALSE)\n"
403 "\tSet whether to do pulsing when negative pulses occur.\n";
404 PyObject* SCA_ISensor::PySetUseNegPulseMode(PyObject* args)
405 {
406         ShowDeprecationWarning("setUseNegPulseMode()", "the useNegPulseMode property");
407         int pyarg = 0;
408         if(!PyArg_ParseTuple(args, "i:setUseNegPulseMode", &pyarg)) { return NULL; }
409         m_neg_pulsemode = PyArgToBool(pyarg);
410         Py_RETURN_NONE;
411 }
412 //<------Deprecated
413
414 KX_PYMETHODDEF_DOC_NOARGS(SCA_ISensor, reset,
415 "reset()\n"
416 "\tReset sensor internal state, effect depends on the type of sensor and settings.\n"
417 "\tThe sensor is put in its initial state as if it was just activated.\n")
418 {
419         Init();
420         m_prev_state = false;
421         Py_RETURN_NONE;
422 }
423
424 /* ----------------------------------------------- */
425 /* Python Integration Hooks                                            */
426 /* ----------------------------------------------- */
427
428 PyTypeObject SCA_ISensor::Type = {
429 #if (PY_VERSION_HEX >= 0x02060000)
430         PyVarObject_HEAD_INIT(NULL, 0)
431 #else
432         /* python 2.5 and below */
433         PyObject_HEAD_INIT( NULL )  /* required py macro */
434         0,                          /* ob_size */
435 #endif
436         "SCA_ISensor",
437         sizeof(PyObjectPlus_Proxy),
438         0,
439         py_base_dealloc,
440         0,
441         0,
442         0,
443         0,
444         py_base_repr,
445         0,0,0,0,0,0,
446         py_base_getattro,
447         py_base_setattro,
448         0,0,0,0,0,0,0,0,0,
449         Methods
450 };
451
452 PyParentObject SCA_ISensor::Parents[] = {
453         &SCA_ISensor::Type,
454         &SCA_ILogicBrick::Type,
455         &CValue::Type,
456         NULL
457 };
458 PyMethodDef SCA_ISensor::Methods[] = {
459         //Deprecated functions ----->
460         {"isPositive", (PyCFunction) SCA_ISensor::sPyIsPositive, 
461          METH_NOARGS, (PY_METHODCHAR)IsPositive_doc},
462         {"isTriggered", (PyCFunction) SCA_ISensor::sPyIsTriggered, 
463          METH_VARARGS, (PY_METHODCHAR)IsTriggered_doc},
464         {"getUsePosPulseMode", (PyCFunction) SCA_ISensor::sPyGetUsePosPulseMode, 
465          METH_NOARGS, (PY_METHODCHAR)GetUsePosPulseMode_doc},
466         {"setUsePosPulseMode", (PyCFunction) SCA_ISensor::sPySetUsePosPulseMode, 
467          METH_VARARGS, (PY_METHODCHAR)SetUsePosPulseMode_doc},
468         {"getFrequency", (PyCFunction) SCA_ISensor::sPyGetFrequency, 
469          METH_NOARGS, (PY_METHODCHAR)GetFrequency_doc},
470         {"setFrequency", (PyCFunction) SCA_ISensor::sPySetFrequency, 
471          METH_VARARGS, (PY_METHODCHAR)SetFrequency_doc},
472         {"getUseNegPulseMode", (PyCFunction) SCA_ISensor::sPyGetUseNegPulseMode, 
473          METH_NOARGS, (PY_METHODCHAR)GetUseNegPulseMode_doc},
474         {"setUseNegPulseMode", (PyCFunction) SCA_ISensor::sPySetUseNegPulseMode, 
475          METH_VARARGS, (PY_METHODCHAR)SetUseNegPulseMode_doc},
476         {"getInvert", (PyCFunction) SCA_ISensor::sPyGetInvert, 
477          METH_NOARGS, (PY_METHODCHAR)GetInvert_doc},
478         {"setInvert", (PyCFunction) SCA_ISensor::sPySetInvert, 
479          METH_VARARGS, (PY_METHODCHAR)SetInvert_doc},
480         {"getLevel", (PyCFunction) SCA_ISensor::sPyGetLevel, 
481          METH_NOARGS, (PY_METHODCHAR)GetLevel_doc},
482         {"setLevel", (PyCFunction) SCA_ISensor::sPySetLevel, 
483          METH_VARARGS, (PY_METHODCHAR)SetLevel_doc},
484          //<----- Deprecated
485         KX_PYMETHODTABLE_NOARGS(SCA_ISensor, reset),
486         {NULL,NULL} //Sentinel
487 };
488
489 PyAttributeDef SCA_ISensor::Attributes[] = {
490         KX_PYATTRIBUTE_BOOL_RW("usePosPulseMode",SCA_ISensor,m_pos_pulsemode),
491         KX_PYATTRIBUTE_BOOL_RW("useNegPulseMode",SCA_ISensor,m_neg_pulsemode),
492         KX_PYATTRIBUTE_INT_RW("frequency",0,100000,true,SCA_ISensor,m_pulse_frequency),
493         KX_PYATTRIBUTE_BOOL_RW("invert",SCA_ISensor,m_invert),
494         KX_PYATTRIBUTE_BOOL_RW_CHECK("level",SCA_ISensor,m_level,pyattr_check_level),
495         KX_PYATTRIBUTE_BOOL_RW_CHECK("tap",SCA_ISensor,m_tap,pyattr_check_tap),
496         KX_PYATTRIBUTE_RO_FUNCTION("triggered", SCA_ISensor, pyattr_get_triggered),
497         KX_PYATTRIBUTE_RO_FUNCTION("positive", SCA_ISensor, pyattr_get_positive),
498         //KX_PYATTRIBUTE_TODO("links"),
499         //KX_PYATTRIBUTE_TODO("posTicks"),
500         //KX_PYATTRIBUTE_TODO("negTicks"),
501         { NULL }        //Sentinel
502 };
503
504 PyObject* SCA_ISensor::py_getattro(PyObject *attr)
505 {
506         py_getattro_up(SCA_ILogicBrick);
507 }
508
509 PyObject* SCA_ISensor::py_getattro_dict() {
510         py_getattro_dict_up(SCA_ILogicBrick);
511 }
512
513 int SCA_ISensor::py_setattro(PyObject *attr, PyObject *value)
514 {
515         py_setattro_up(SCA_ILogicBrick);
516 }
517
518 PyObject* SCA_ISensor::pyattr_get_triggered(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
519 {
520         SCA_ISensor* self= static_cast<SCA_ISensor*>(self_v);
521         int retval = 0;
522         if (SCA_PythonController::m_sCurrentController)
523                 retval = SCA_PythonController::m_sCurrentController->IsTriggered(self);
524         return PyInt_FromLong(retval);
525 }
526
527 PyObject* SCA_ISensor::pyattr_get_positive(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
528 {
529         SCA_ISensor* self= static_cast<SCA_ISensor*>(self_v);
530         return PyInt_FromLong(self->GetState());
531 }
532
533 int SCA_ISensor::pyattr_check_level(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
534 {
535         SCA_ISensor* self= static_cast<SCA_ISensor*>(self_v);
536         if (self->m_level)
537                 self->m_tap = false;
538         return 0;
539 }
540
541 int SCA_ISensor::pyattr_check_tap(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
542 {
543         SCA_ISensor* self= static_cast<SCA_ISensor*>(self_v);
544         if (self->m_tap)
545                 self->m_level = false;
546         return 0;
547 }
548
549 /* eof */