Building the game engine with Solid/Sumo is now optional for scons using WITH_BF_SOLID.
[blender.git] / source / gameengine / Ketsji / KX_TouchSensor.cpp
1 /**
2  * Senses touch and collision events
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 "KX_TouchSensor.h"
33 #include "SCA_EventManager.h"
34 #include "SCA_LogicManager.h"
35 #include "KX_GameObject.h"
36 #include "KX_TouchEventManager.h"
37
38 #include "PHY_IPhysicsController.h"
39
40 #include <iostream>
41 #include "PHY_IPhysicsEnvironment.h"
42
43 #ifdef HAVE_CONFIG_H
44 #include <config.h>
45 #endif
46
47 /* ------------------------------------------------------------------------- */
48 /* Native functions                                                          */
49 /* ------------------------------------------------------------------------- */
50
51 void KX_TouchSensor::SynchronizeTransform()
52 {
53         // the touch sensor does not require any synchronization: it uses
54         // the same physical object which is already synchronized by Blender
55 }
56
57
58 void KX_TouchSensor::EndFrame() {
59         m_colliders->ReleaseAndRemoveAll();
60         m_hitObject = NULL;
61         m_bTriggered = false;
62 }
63
64 void KX_TouchSensor::UnregisterToManager()
65 {
66         // before unregistering the sensor, make sure we release all references
67         EndFrame();
68         m_eventmgr->RemoveSensor(this);
69 }
70
71 bool KX_TouchSensor::Evaluate(CValue* event)
72 {
73         bool result = false;
74         bool reset = m_reset && m_level;
75
76         m_reset = false;
77         if (m_bTriggered != m_bLastTriggered)
78         {
79                 m_bLastTriggered = m_bTriggered;
80                 if (!m_bTriggered)
81                         m_hitObject = NULL;
82                 result = true;
83         }
84         if (reset)
85                 // force an event
86                 result = true;
87         return result;
88 }
89
90 KX_TouchSensor::KX_TouchSensor(SCA_EventManager* eventmgr,KX_GameObject* gameobj,bool bFindMaterial,const STR_String& touchedpropname,PyTypeObject* T)
91 :SCA_ISensor(gameobj,eventmgr,T),
92 m_touchedpropname(touchedpropname),
93 m_bFindMaterial(bFindMaterial),
94 m_eventmgr(eventmgr)
95 /*m_sumoObj(sumoObj),*/
96 {
97 //      KX_TouchEventManager* touchmgr = (KX_TouchEventManager*) eventmgr;
98 //      m_resptable = touchmgr->GetResponseTable();
99         
100 //      m_solidHandle = m_sumoObj->getObjectHandle();
101
102         m_colliders = new CListValue();
103         
104         KX_ClientObjectInfo *client_info = gameobj->getClientInfo();
105         //client_info->m_gameobject = gameobj;
106         //client_info->m_auxilary_info = NULL;
107         client_info->m_sensors.push_back(this);
108         
109         m_physCtrl = dynamic_cast<PHY_IPhysicsController*>(gameobj->GetPhysicsController());
110         MT_assert( !gameobj->GetPhysicsController() || m_physCtrl );
111         Init();
112 }
113
114 void KX_TouchSensor::Init()
115 {
116         m_bCollision = false;
117         m_bTriggered = false;
118         m_bLastTriggered = (m_invert)?true:false;
119         m_hitObject =  NULL;
120         m_reset = true;
121 }
122
123 KX_TouchSensor::~KX_TouchSensor()
124 {
125         //DT_ClearObjectResponse(m_resptable,m_solidHandle);
126         m_colliders->Release();
127 }
128
129 CValue* KX_TouchSensor::GetReplica() 
130 {
131         KX_TouchSensor* replica = new KX_TouchSensor(*this);
132         replica->m_colliders = new CListValue();
133         replica->Init();
134         // this will copy properties and so on...
135         CValue::AddDataToReplica(replica);
136         return replica;
137 }
138
139 void    KX_TouchSensor::ReParent(SCA_IObject* parent)
140 {
141         KX_GameObject *gameobj = static_cast<KX_GameObject *>(parent);
142         PHY_IPhysicsController *sphy = dynamic_cast<PHY_IPhysicsController*>(((KX_GameObject*)parent)->GetPhysicsController());
143         if (sphy)
144                 m_physCtrl = sphy;
145         
146 //      m_solidHandle = m_sumoObj->getObjectHandle();
147         KX_ClientObjectInfo *client_info = gameobj->getClientInfo();
148         //client_info->m_gameobject = gameobj;
149         //client_info->m_auxilary_info = NULL;
150         
151         client_info->m_sensors.push_back(this);
152         SCA_ISensor::ReParent(parent);
153 }
154
155 void KX_TouchSensor::RegisterSumo(KX_TouchEventManager *touchman)
156 {
157         if (m_physCtrl)
158         {
159                 touchman->GetPhysicsEnvironment()->requestCollisionCallback(m_physCtrl);
160                 // collision
161                 // Deprecated   
162
163         }
164 }
165
166 void KX_TouchSensor::UnregisterSumo(KX_TouchEventManager* touchman)
167 {
168         if (m_physCtrl)
169         {
170                 touchman->GetPhysicsEnvironment()->removeCollisionCallback(m_physCtrl);
171         }
172 }
173
174 bool    KX_TouchSensor::NewHandleCollision(void*object1,void*object2,const PHY_CollData* colldata)
175 {
176 //      KX_TouchEventManager* toucheventmgr = (KX_TouchEventManager*)m_eventmgr;
177         KX_GameObject* parent = (KX_GameObject*)GetParent();
178
179         // need the mapping from PHY_IPhysicsController to gameobjects now
180         
181         KX_ClientObjectInfo* client_info = static_cast<KX_ClientObjectInfo*> (object1 == m_physCtrl? 
182                                         ((PHY_IPhysicsController*)object2)->getNewClientInfo(): 
183                                         ((PHY_IPhysicsController*)object1)->getNewClientInfo());
184
185         KX_GameObject* gameobj = ( client_info ? 
186                         client_info->m_gameobject : 
187                         NULL);
188         
189         // add the same check as in SCA_ISensor::Activate(), 
190         // we don't want to record collision when the sensor is not active.
191         if (m_links && !m_suspended &&
192                 gameobj && (gameobj != parent) && client_info->isActor())
193         {
194                 if (!m_colliders->SearchValue(gameobj))
195                         m_colliders->Add(gameobj->AddRef());
196                 
197                 bool found = m_touchedpropname.IsEmpty();
198                 if (!found)
199                 {
200                         if (m_bFindMaterial)
201                         {
202                                 if (client_info->m_auxilary_info)
203                                 {
204                                         found = (!strcmp(m_touchedpropname.Ptr(), (char*)client_info->m_auxilary_info));
205                                 }
206                         } else
207                         {
208                                 found = (gameobj->GetProperty(m_touchedpropname) != NULL);
209                         }
210                 }
211                 if (found)
212                 {
213                         m_bTriggered = true;
214                         m_hitObject = gameobj;
215                         //printf("KX_TouchSensor::HandleCollision\n");
216                 }
217                 
218         } 
219         return false; // was DT_CONTINUE but this was defined in sumo as false.
220 }
221
222
223 /* ------------------------------------------------------------------------- */
224 /* Python functions                                                          */
225 /* ------------------------------------------------------------------------- */
226 /* Integration hooks ------------------------------------------------------- */
227 PyTypeObject KX_TouchSensor::Type = {
228         PyObject_HEAD_INIT(&PyType_Type)
229         0,
230         "KX_TouchSensor",
231         sizeof(KX_TouchSensor),
232         0,
233         PyDestructor,
234         0,
235         __getattr,
236         __setattr,
237         0, //&MyPyCompare,
238         __repr,
239         0, //&cvalue_as_number,
240         0,
241         0,
242         0,
243         0
244 };
245
246 PyParentObject KX_TouchSensor::Parents[] = {
247         &KX_TouchSensor::Type,
248         &SCA_ISensor::Type,
249         &SCA_ILogicBrick::Type,
250         &CValue::Type,
251         NULL
252 };
253
254 PyMethodDef KX_TouchSensor::Methods[] = {
255         {"setProperty", 
256          (PyCFunction) KX_TouchSensor::sPySetProperty,      METH_VARARGS, (PY_METHODCHAR)SetProperty_doc},
257         {"getProperty", 
258          (PyCFunction) KX_TouchSensor::sPyGetProperty,      METH_VARARGS, (PY_METHODCHAR)GetProperty_doc},
259         {"getHitObject", 
260          (PyCFunction) KX_TouchSensor::sPyGetHitObject,     METH_VARARGS, (PY_METHODCHAR)GetHitObject_doc},
261         {"getHitObjectList", 
262          (PyCFunction) KX_TouchSensor::sPyGetHitObjectList, METH_VARARGS, (PY_METHODCHAR)GetHitObjectList_doc},
263         {NULL,NULL} //Sentinel
264 };
265
266 PyObject* KX_TouchSensor::_getattr(const char *attr) {
267         _getattr_up(SCA_ISensor);
268 }
269
270 /* Python API */
271
272 /* 1. setProperty */
273 const char KX_TouchSensor::SetProperty_doc[] = 
274 "setProperty(name)\n"
275 "\t- name: string\n"
276 "\tSet the property or material to collide with. Use\n"
277 "\tsetTouchMaterial() to switch between properties and\n"
278 "\tmaterials.";
279 PyObject* KX_TouchSensor::PySetProperty(PyObject* self, 
280                                                                                 PyObject* args, 
281                                                                                 PyObject* kwds) {
282         char *nameArg;
283         if (!PyArg_ParseTuple(args, "s", &nameArg)) {
284                 return NULL;
285         }
286
287         CValue* prop = GetParent()->FindIdentifier(nameArg);
288
289         if (!prop->IsError()) {
290                 m_touchedpropname = nameArg;
291         } else {
292                 ; /* not found ... */
293         }
294         prop->Release();
295         
296         Py_RETURN_NONE;
297 }
298 /* 2. getProperty */
299 const char KX_TouchSensor::GetProperty_doc[] = 
300 "getProperty(name)\n"
301 "\tReturns the property or material to collide with. Use\n"
302 "\tgetTouchMaterial() to find out whether this sensor\n"
303 "\tlooks for properties or materials.";
304 PyObject*  KX_TouchSensor::PyGetProperty(PyObject* self, 
305                                                                                  PyObject* args, 
306                                                                                  PyObject* kwds) {
307         return PyString_FromString(m_touchedpropname);
308 }
309
310 const char KX_TouchSensor::GetHitObject_doc[] = 
311 "getHitObject()\n"
312 ;
313 PyObject* KX_TouchSensor::PyGetHitObject(PyObject* self, 
314                                                                                  PyObject* args, 
315                                                                                  PyObject* kwds)
316 {
317         /* to do: do Py_IncRef if the object is already known in Python */
318         /* otherwise, this leaks memory */
319         if (m_hitObject)
320         {
321                 return m_hitObject->AddRef();
322         }
323         Py_RETURN_NONE;
324 }
325
326 const char KX_TouchSensor::GetHitObjectList_doc[] = 
327 "getHitObjectList()\n"
328 "\tReturn a list of the objects this object collided with,\n"
329 "\tbut only those matching the property/material condition.\n";
330 PyObject* KX_TouchSensor::PyGetHitObjectList(PyObject* self, 
331                                                                                  PyObject* args, 
332                                                                                  PyObject* kwds)
333 {
334
335         /* to do: do Py_IncRef if the object is already known in Python */
336         /* otherwise, this leaks memory */
337
338         if ( m_touchedpropname.IsEmpty() ) {
339                 return m_colliders->AddRef();
340         } else {
341                 CListValue* newList = new CListValue();
342                 int i = 0;
343                 while (i < m_colliders->GetCount()) {
344                         if (m_bFindMaterial) {
345                                 /* need to associate the CValues from the list to material
346                                  * names. The collider list _should_ contains only
347                                  * KX_GameObjects. I am loathe to cast them, though... The
348                                  * material name must be retrieved from Sumo. To a Sumo
349                                  * object, a client-info block is attached. This block
350                                  * contains the material name. 
351                                  * - this also doesn't work (obviously) for multi-materials... 
352                                  */
353                                 KX_GameObject* gameob = (KX_GameObject*) m_colliders->GetValue(i);
354                                 PHY_IPhysicsController* spc = dynamic_cast<PHY_IPhysicsController*>(gameob->GetPhysicsController());
355                                 
356                                 if (spc) {
357                                         KX_ClientObjectInfo* cl_inf = static_cast<KX_ClientObjectInfo*>(spc->getNewClientInfo());
358                                         
359                                         if (NULL != cl_inf->m_auxilary_info && m_touchedpropname == ((char*)cl_inf->m_auxilary_info)) {
360                                                 newList->Add(m_colliders->GetValue(i)->AddRef());
361                                         } 
362                                 }
363                                 
364                         } else {
365                                 CValue* val = m_colliders->GetValue(i)->FindIdentifier(m_touchedpropname);
366                                 if (!val->IsError()) {
367                                         newList->Add(m_colliders->GetValue(i)->AddRef());
368                                 }
369                                 val->Release();
370                         }
371                         
372                         i++;
373                 }
374                 return newList->AddRef();
375         }
376
377 }
378
379 /* 5. getTouchMaterial */
380 const char KX_TouchSensor::GetTouchMaterial_doc[] = 
381 "getTouchMaterial()\n"
382 "\tReturns KX_TRUE if this sensor looks for a specific material,\n"
383 "\tKX_FALSE if it looks for a specific property.\n" ;
384 PyObject* KX_TouchSensor::PyGetTouchMaterial(PyObject* self, 
385                                                                                          PyObject* args, 
386                                                                                          PyObject* kwds)
387 {
388         return PyInt_FromLong(m_bFindMaterial);
389 }
390
391 /* 6. setTouchMaterial */
392 const char KX_TouchSensor::SetTouchMaterial_doc[] = 
393 "setTouchMaterial(flag)\n"
394 "\t- flag: KX_TRUE or KX_FALSE.\n"
395 "\tSet flag to KX_TRUE to switch on positive pulse mode,\n"
396 "\tKX_FALSE to switch off positive pulse mode.\n" ;
397 PyObject* KX_TouchSensor::PySetTouchMaterial(PyObject* self, PyObject* args, PyObject* kwds)
398 {
399         int pulseArg = 0;
400
401         if(!PyArg_ParseTuple(args, "i", &pulseArg)) {
402                 return NULL;
403         }
404         
405         m_bFindMaterial = pulseArg != 0;
406
407         Py_RETURN_NONE;
408 }
409
410
411 /* eof */