remove redundant argument from mathutils callbacks
[blender.git] / source / gameengine / Ketsji / KX_ObjectActuator.cpp
1 /**
2  * Do translation/rotation actions
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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_ObjectActuator.h"
33 #include "KX_GameObject.h"
34 #include "KX_PyMath.h" // For PyVecTo - should this include be put in PyObjectPlus?
35 #include "KX_IPhysicsController.h"
36
37 /* ------------------------------------------------------------------------- */
38 /* Native functions                                                          */
39 /* ------------------------------------------------------------------------- */
40
41 KX_ObjectActuator::
42 KX_ObjectActuator(
43         SCA_IObject* gameobj,
44         KX_GameObject* refobj,
45         const MT_Vector3& force,
46         const MT_Vector3& torque,
47         const MT_Vector3& dloc,
48         const MT_Vector3& drot,
49         const MT_Vector3& linV,
50         const MT_Vector3& angV,
51         const short damping,
52         const KX_LocalFlags& flag
53 ) : 
54         SCA_IActuator(gameobj, KX_ACT_OBJECT),
55         m_force(force),
56         m_torque(torque),
57         m_dloc(dloc),
58         m_drot(drot),
59         m_linear_velocity(linV),
60         m_angular_velocity(angV),
61         m_linear_length2(0.0),
62         m_current_linear_factor(0.0),
63         m_current_angular_factor(0.0),
64         m_damping(damping),
65         m_previous_error(0.0,0.0,0.0),
66         m_error_accumulator(0.0,0.0,0.0),
67         m_bitLocalFlag (flag),
68         m_reference(refobj),
69         m_active_combined_velocity (false),
70         m_linear_damping_active(false),
71         m_angular_damping_active(false)
72 {
73         if (m_bitLocalFlag.ServoControl)
74         {
75                 // in servo motion, the force is local if the target velocity is local
76                 m_bitLocalFlag.Force = m_bitLocalFlag.LinearVelocity;
77
78                 m_pid = m_torque;
79         }
80         if (m_reference)
81                 m_reference->RegisterActuator(this);
82         UpdateFuzzyFlags();
83 }
84
85 KX_ObjectActuator::~KX_ObjectActuator()
86 {
87         if (m_reference)
88                 m_reference->UnregisterActuator(this);
89 }
90
91 bool KX_ObjectActuator::Update()
92 {
93         
94         bool bNegativeEvent = IsNegativeEvent();
95         RemoveAllEvents();
96                 
97         KX_GameObject *parent = static_cast<KX_GameObject *>(GetParent()); 
98
99         if (bNegativeEvent) {
100                 // If we previously set the linear velocity we now have to inform
101                 // the physics controller that we no longer wish to apply it and that
102                 // it should reconcile the externally set velocity with it's 
103                 // own velocity.
104                 if (m_active_combined_velocity) {
105                         if (parent)
106                                 parent->ResolveCombinedVelocities(
107                                                 m_linear_velocity,
108                                                 m_angular_velocity,
109                                                 (m_bitLocalFlag.LinearVelocity) != 0,
110                                                 (m_bitLocalFlag.AngularVelocity) != 0
111                                         );
112                         m_active_combined_velocity = false;
113                 } 
114                 m_linear_damping_active = false;
115                 m_angular_damping_active = false;
116                 m_error_accumulator.setValue(0.0,0.0,0.0);
117                 m_previous_error.setValue(0.0,0.0,0.0);
118                 return false; 
119
120         } else if (parent)
121         {
122                 if (m_bitLocalFlag.ServoControl) 
123                 {
124                         // In this mode, we try to reach a target speed using force
125                         // As we don't know the friction, we must implement a generic 
126                         // servo control to achieve the speed in a configurable
127                         // v = current velocity
128                         // V = target velocity
129                         // e = V-v = speed error
130                         // dt = time interval since previous update
131                         // I = sum(e(t)*dt)
132                         // dv = e(t) - e(t-1)
133                         // KP, KD, KI : coefficient
134                         // F = KP*e+KI*I+KD*dv
135                         MT_Scalar mass = parent->GetMass();
136                         if (mass < MT_EPSILON)
137                                 return false;
138                         MT_Vector3 v = parent->GetLinearVelocity(m_bitLocalFlag.LinearVelocity);
139                         if (m_reference)
140                         {
141                                 const MT_Point3& mypos = parent->NodeGetWorldPosition();
142                                 const MT_Point3& refpos = m_reference->NodeGetWorldPosition();
143                                 MT_Point3 relpos;
144                                 relpos = (mypos-refpos);
145                                 MT_Vector3 vel= m_reference->GetVelocity(relpos);
146                                 if (m_bitLocalFlag.LinearVelocity)
147                                         // must convert in local space
148                                         vel = parent->NodeGetWorldOrientation().transposed()*vel;
149                                 v -= vel;
150                         }
151                         MT_Vector3 e = m_linear_velocity - v;
152                         MT_Vector3 dv = e - m_previous_error;
153                         MT_Vector3 I = m_error_accumulator + e;
154
155                         m_force = m_pid.x()*e+m_pid.y()*I+m_pid.z()*dv;
156                         // to automatically adapt the PID coefficient to mass;
157                         m_force *= mass;
158                         if (m_bitLocalFlag.Torque) 
159                         {
160                                 if (m_force[0] > m_dloc[0])
161                                 {
162                                         m_force[0] = m_dloc[0];
163                                         I[0] = m_error_accumulator[0];
164                                 } else if (m_force[0] < m_drot[0])
165                                 {
166                                         m_force[0] = m_drot[0];
167                                         I[0] = m_error_accumulator[0];
168                                 }
169                         }
170                         if (m_bitLocalFlag.DLoc) 
171                         {
172                                 if (m_force[1] > m_dloc[1])
173                                 {
174                                         m_force[1] = m_dloc[1];
175                                         I[1] = m_error_accumulator[1];
176                                 } else if (m_force[1] < m_drot[1])
177                                 {
178                                         m_force[1] = m_drot[1];
179                                         I[1] = m_error_accumulator[1];
180                                 }
181                         }
182                         if (m_bitLocalFlag.DRot) 
183                         {
184                                 if (m_force[2] > m_dloc[2])
185                                 {
186                                         m_force[2] = m_dloc[2];
187                                         I[2] = m_error_accumulator[2];
188                                 } else if (m_force[2] < m_drot[2])
189                                 {
190                                         m_force[2] = m_drot[2];
191                                         I[2] = m_error_accumulator[2];
192                                 }
193                         }
194                         m_previous_error = e;
195                         m_error_accumulator = I;
196                         parent->ApplyForce(m_force,(m_bitLocalFlag.LinearVelocity) != 0);
197                 } else
198                 {
199                         if (!m_bitLocalFlag.ZeroForce)
200                         {
201                                 parent->ApplyForce(m_force,(m_bitLocalFlag.Force) != 0);
202                         }
203                         if (!m_bitLocalFlag.ZeroTorque)
204                         {
205                                 parent->ApplyTorque(m_torque,(m_bitLocalFlag.Torque) != 0);
206                         }
207                         if (!m_bitLocalFlag.ZeroDLoc)
208                         {
209                                 parent->ApplyMovement(m_dloc,(m_bitLocalFlag.DLoc) != 0);
210                         }
211                         if (!m_bitLocalFlag.ZeroDRot)
212                         {
213                                 parent->ApplyRotation(m_drot,(m_bitLocalFlag.DRot) != 0);
214                         }
215                         if (!m_bitLocalFlag.ZeroLinearVelocity)
216                         {
217                                 if (m_bitLocalFlag.AddOrSetLinV) {
218                                         parent->addLinearVelocity(m_linear_velocity,(m_bitLocalFlag.LinearVelocity) != 0);
219                                 } else {
220                                         m_active_combined_velocity = true;
221                                         if (m_damping > 0) {
222                                                 MT_Vector3 linV;
223                                                 if (!m_linear_damping_active) {
224                                                         // delta and the start speed (depends on the existing speed in that direction)
225                                                         linV = parent->GetLinearVelocity(m_bitLocalFlag.LinearVelocity);
226                                                         // keep only the projection along the desired direction
227                                                         m_current_linear_factor = linV.dot(m_linear_velocity)/m_linear_length2;
228                                                         m_linear_damping_active = true;
229                                                 }
230                                                 if (m_current_linear_factor < 1.0)
231                                                         m_current_linear_factor += 1.0/m_damping;
232                                                 if (m_current_linear_factor > 1.0)
233                                                         m_current_linear_factor = 1.0;
234                                                 linV = m_current_linear_factor * m_linear_velocity;
235                                                 parent->setLinearVelocity(linV,(m_bitLocalFlag.LinearVelocity) != 0);
236                                         } else {
237                                                 parent->setLinearVelocity(m_linear_velocity,(m_bitLocalFlag.LinearVelocity) != 0);
238                                         }
239                                 }
240                         }
241                         if (!m_bitLocalFlag.ZeroAngularVelocity)
242                         {
243                                 m_active_combined_velocity = true;
244                                 if (m_damping > 0) {
245                                         MT_Vector3 angV;
246                                         if (!m_angular_damping_active) {
247                                                 // delta and the start speed (depends on the existing speed in that direction)
248                                                 angV = parent->GetAngularVelocity(m_bitLocalFlag.AngularVelocity);
249                                                 // keep only the projection along the desired direction
250                                                 m_current_angular_factor = angV.dot(m_angular_velocity)/m_angular_length2;
251                                                 m_angular_damping_active = true;
252                                         }
253                                         if (m_current_angular_factor < 1.0)
254                                                 m_current_angular_factor += 1.0/m_damping;
255                                         if (m_current_angular_factor > 1.0)
256                                                 m_current_angular_factor = 1.0;
257                                         angV = m_current_angular_factor * m_angular_velocity;
258                                         parent->setAngularVelocity(angV,(m_bitLocalFlag.AngularVelocity) != 0);
259                                 } else {
260                                         parent->setAngularVelocity(m_angular_velocity,(m_bitLocalFlag.AngularVelocity) != 0);
261                                 }
262                         }
263                 }
264                 
265         }
266         return true;
267 }
268
269
270
271 CValue* KX_ObjectActuator::GetReplica()
272 {
273         KX_ObjectActuator* replica = new KX_ObjectActuator(*this);//m_float,GetName());
274         replica->ProcessReplica();
275
276         return replica;
277 }
278
279 void KX_ObjectActuator::ProcessReplica()
280 {
281         SCA_IActuator::ProcessReplica();
282         if (m_reference)
283                 m_reference->RegisterActuator(this);
284 }
285
286 bool KX_ObjectActuator::UnlinkObject(SCA_IObject* clientobj)
287 {
288         if (clientobj == (SCA_IObject*)m_reference)
289         {
290                 // this object is being deleted, we cannot continue to use it as reference.
291                 m_reference = NULL;
292                 return true;
293         }
294         return false;
295 }
296
297 void KX_ObjectActuator::Relink(GEN_Map<GEN_HashedPtr, void*> *obj_map)
298 {
299         void **h_obj = (*obj_map)[m_reference];
300         if (h_obj) {
301                 if (m_reference)
302                         m_reference->UnregisterActuator(this);
303                 m_reference = (KX_GameObject*)(*h_obj);
304                 m_reference->RegisterActuator(this);
305         }
306 }
307
308 /* some 'standard' utilities... */
309 bool KX_ObjectActuator::isValid(KX_ObjectActuator::KX_OBJECT_ACT_VEC_TYPE type)
310 {
311         bool res = false;
312         res = (type > KX_OBJECT_ACT_NODEF) && (type < KX_OBJECT_ACT_MAX);
313         return res;
314 }
315
316 #ifndef DISABLE_PYTHON
317
318 /* ------------------------------------------------------------------------- */
319 /* Python functions                                                          */
320 /* ------------------------------------------------------------------------- */
321
322 /* Integration hooks ------------------------------------------------------- */
323 PyTypeObject KX_ObjectActuator::Type = {
324         PyVarObject_HEAD_INIT(NULL, 0)
325         "KX_ObjectActuator",
326         sizeof(PyObjectPlus_Proxy),
327         0,
328         py_base_dealloc,
329         0,
330         0,
331         0,
332         0,
333         py_base_repr,
334         0,0,0,0,0,0,0,0,0,
335         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
336         0,0,0,0,0,0,0,
337         Methods,
338         0,
339         0,
340         &SCA_IActuator::Type,
341         0,0,0,0,0,0,
342         py_base_new
343 };
344
345 PyMethodDef KX_ObjectActuator::Methods[] = {
346         {NULL,NULL} //Sentinel
347 };
348
349 PyAttributeDef KX_ObjectActuator::Attributes[] = {
350         KX_PYATTRIBUTE_VECTOR_RW_CHECK("force", -1000, 1000, false, KX_ObjectActuator, m_force, PyUpdateFuzzyFlags),
351         KX_PYATTRIBUTE_BOOL_RW("useLocalForce", KX_ObjectActuator, m_bitLocalFlag.Force),
352         KX_PYATTRIBUTE_VECTOR_RW_CHECK("torque", -1000, 1000, false, KX_ObjectActuator, m_torque, PyUpdateFuzzyFlags),
353         KX_PYATTRIBUTE_BOOL_RW("useLocalTorque", KX_ObjectActuator, m_bitLocalFlag.Torque),
354         KX_PYATTRIBUTE_VECTOR_RW_CHECK("dLoc", -1000, 1000, false, KX_ObjectActuator, m_dloc, PyUpdateFuzzyFlags),
355         KX_PYATTRIBUTE_BOOL_RW("useLocalDLoc", KX_ObjectActuator, m_bitLocalFlag.DLoc),
356         KX_PYATTRIBUTE_VECTOR_RW_CHECK("dRot", -1000, 1000, false, KX_ObjectActuator, m_drot, PyUpdateFuzzyFlags),
357         KX_PYATTRIBUTE_BOOL_RW("useLocalDRot", KX_ObjectActuator, m_bitLocalFlag.DRot),
358 #ifdef USE_MATHUTILS
359         KX_PYATTRIBUTE_RW_FUNCTION("linV", KX_ObjectActuator, pyattr_get_linV, pyattr_set_linV),
360         KX_PYATTRIBUTE_RW_FUNCTION("angV", KX_ObjectActuator, pyattr_get_angV, pyattr_set_angV),
361 #else
362         KX_PYATTRIBUTE_VECTOR_RW_CHECK("linV", -1000, 1000, false, KX_ObjectActuator, m_linear_velocity, PyUpdateFuzzyFlags),
363         KX_PYATTRIBUTE_VECTOR_RW_CHECK("angV", -1000, 1000, false, KX_ObjectActuator, m_angular_velocity, PyUpdateFuzzyFlags),
364 #endif
365         KX_PYATTRIBUTE_BOOL_RW("useLocalLinV", KX_ObjectActuator, m_bitLocalFlag.LinearVelocity),
366         KX_PYATTRIBUTE_BOOL_RW("useLocalAngV", KX_ObjectActuator, m_bitLocalFlag.AngularVelocity),
367         KX_PYATTRIBUTE_SHORT_RW("damping", 0, 1000, false, KX_ObjectActuator, m_damping),
368         KX_PYATTRIBUTE_RW_FUNCTION("forceLimitX", KX_ObjectActuator, pyattr_get_forceLimitX, pyattr_set_forceLimitX),
369         KX_PYATTRIBUTE_RW_FUNCTION("forceLimitY", KX_ObjectActuator, pyattr_get_forceLimitY, pyattr_set_forceLimitY),
370         KX_PYATTRIBUTE_RW_FUNCTION("forceLimitZ", KX_ObjectActuator, pyattr_get_forceLimitZ, pyattr_set_forceLimitZ),
371         KX_PYATTRIBUTE_VECTOR_RW_CHECK("pid", -100, 200, true, KX_ObjectActuator, m_pid, PyCheckPid),
372         KX_PYATTRIBUTE_RW_FUNCTION("reference", KX_ObjectActuator,pyattr_get_reference,pyattr_set_reference),
373         { NULL }        //Sentinel
374 };
375
376 /* Attribute get/set functions */
377
378 #ifdef USE_MATHUTILS
379
380 /* These require an SGNode */
381 #define MATHUTILS_VEC_CB_LINV 1
382 #define MATHUTILS_VEC_CB_ANGV 2
383
384 static int mathutils_kxobactu_vector_cb_index= -1; /* index for our callbacks */
385
386 static int mathutils_obactu_generic_check(BaseMathObject *bmo)
387 {
388         KX_ObjectActuator* self= static_cast<KX_ObjectActuator*>BGE_PROXY_REF(bmo->cb_user);
389         if(self==NULL)
390                 return 0;
391
392         return 1;
393 }
394
395 static int mathutils_obactu_vector_get(BaseMathObject *bmo, int subtype)
396 {
397         KX_ObjectActuator* self= static_cast<KX_ObjectActuator*>BGE_PROXY_REF(bmo->cb_user);
398         if(self==NULL)
399                 return 0;
400
401         switch(subtype) {
402                 case MATHUTILS_VEC_CB_LINV:
403                         self->m_linear_velocity.getValue(bmo->data);
404                         break;
405                 case MATHUTILS_VEC_CB_ANGV:
406                         self->m_angular_velocity.getValue(bmo->data);
407                         break;
408         }
409
410         return 1;
411 }
412
413 static int mathutils_obactu_vector_set(BaseMathObject *bmo, int subtype)
414 {
415         KX_ObjectActuator* self= static_cast<KX_ObjectActuator*>BGE_PROXY_REF(bmo->cb_user);
416         if(self==NULL)
417                 return 0;
418
419         switch(subtype) {
420                 case MATHUTILS_VEC_CB_LINV:
421                         self->m_linear_velocity.setValue(bmo->data);
422                         break;
423                 case MATHUTILS_VEC_CB_ANGV:
424                         self->m_angular_velocity.setValue(bmo->data);
425                         break;
426         }
427
428         return 1;
429 }
430
431 static int mathutils_obactu_vector_get_index(BaseMathObject *bmo, int subtype, int index)
432 {
433         float f[4];
434         /* lazy, avoid repeteing the case statement */
435         if(!mathutils_obactu_vector_get(bmo, subtype))
436                 return 0;
437
438         bmo->data[index]= f[index];
439         return 1;
440 }
441
442 static int mathutils_obactu_vector_set_index(BaseMathObject *bmo, int subtype, int index)
443 {
444         float f= bmo->data[index];
445
446         /* lazy, avoid repeteing the case statement */
447         if(!mathutils_obactu_vector_get(bmo, subtype))
448                 return 0;
449
450         bmo->data[index]= f;
451         mathutils_obactu_vector_set(bmo, subtype);
452
453         return 1;
454 }
455
456 Mathutils_Callback mathutils_obactu_vector_cb = {
457         mathutils_obactu_generic_check,
458         mathutils_obactu_vector_get,
459         mathutils_obactu_vector_set,
460         mathutils_obactu_vector_get_index,
461         mathutils_obactu_vector_set_index
462 };
463
464 PyObject* KX_ObjectActuator::pyattr_get_linV(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
465 {
466         return newVectorObject_cb(BGE_PROXY_FROM_REF(self_v), 3, mathutils_kxobactu_vector_cb_index, MATHUTILS_VEC_CB_LINV);
467 }
468
469 int KX_ObjectActuator::pyattr_set_linV(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
470 {
471         KX_ObjectActuator* self= static_cast<KX_ObjectActuator*>(self_v);
472         if (!PyVecTo(value, self->m_linear_velocity))
473                 return PY_SET_ATTR_FAIL;
474
475         return PY_SET_ATTR_SUCCESS;
476 }
477
478 PyObject* KX_ObjectActuator::pyattr_get_angV(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
479 {
480         return newVectorObject_cb(BGE_PROXY_FROM_REF(self_v), 3, mathutils_kxobactu_vector_cb_index, MATHUTILS_VEC_CB_ANGV);
481 }
482
483 int KX_ObjectActuator::pyattr_set_angV(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
484 {
485         KX_ObjectActuator* self= static_cast<KX_ObjectActuator*>(self_v);
486         if (!PyVecTo(value, self->m_angular_velocity))
487                 return PY_SET_ATTR_FAIL;
488
489         return PY_SET_ATTR_SUCCESS;
490 }
491
492
493 void KX_ObjectActuator_Mathutils_Callback_Init(void)
494 {
495         // register mathutils callbacks, ok to run more then once.
496         mathutils_kxobactu_vector_cb_index= Mathutils_RegisterCallback(&mathutils_obactu_vector_cb);
497 }
498
499 #endif // USE_MATHUTILS
500
501 PyObject* KX_ObjectActuator::pyattr_get_forceLimitX(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
502 {
503         KX_ObjectActuator* self = reinterpret_cast<KX_ObjectActuator*>(self_v);
504         PyObject *retVal = PyList_New(3);
505
506         PyList_SET_ITEM(retVal, 0, PyFloat_FromDouble(self->m_drot[0]));
507         PyList_SET_ITEM(retVal, 1, PyFloat_FromDouble(self->m_dloc[0]));
508         PyList_SET_ITEM(retVal, 2, PyBool_FromLong(self->m_bitLocalFlag.Torque));
509         
510         return retVal;
511 }
512
513 int KX_ObjectActuator::pyattr_set_forceLimitX(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
514 {
515         KX_ObjectActuator* self = reinterpret_cast<KX_ObjectActuator*>(self_v);
516
517         PyObject* seq = PySequence_Fast(value, "");
518         if (seq && PySequence_Fast_GET_SIZE(seq) == 3)
519         {
520                 self->m_drot[0] = PyFloat_AsDouble(PySequence_Fast_GET_ITEM(value, 0));
521                 self->m_dloc[0] = PyFloat_AsDouble(PySequence_Fast_GET_ITEM(value, 1));
522                 self->m_bitLocalFlag.Torque = (PyLong_AsSsize_t(PySequence_Fast_GET_ITEM(value, 2)) != 0);
523
524                 if (!PyErr_Occurred())
525                 {
526                         Py_DECREF(seq);
527                         return PY_SET_ATTR_SUCCESS;
528                 }
529         }
530
531         Py_XDECREF(seq);
532
533         PyErr_SetString(PyExc_ValueError, "expected a sequence of 2 floats and a bool");
534         return PY_SET_ATTR_FAIL;
535 }
536
537 PyObject* KX_ObjectActuator::pyattr_get_forceLimitY(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
538 {
539         KX_ObjectActuator* self = reinterpret_cast<KX_ObjectActuator*>(self_v);
540         PyObject *retVal = PyList_New(3);
541
542         PyList_SET_ITEM(retVal, 0, PyFloat_FromDouble(self->m_drot[1]));
543         PyList_SET_ITEM(retVal, 1, PyFloat_FromDouble(self->m_dloc[1]));
544         PyList_SET_ITEM(retVal, 2, PyBool_FromLong(self->m_bitLocalFlag.DLoc));
545         
546         return retVal;
547 }
548
549 int     KX_ObjectActuator::pyattr_set_forceLimitY(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
550 {
551         KX_ObjectActuator* self = reinterpret_cast<KX_ObjectActuator*>(self_v);
552
553         PyObject* seq = PySequence_Fast(value, "");
554         if (seq && PySequence_Fast_GET_SIZE(seq) == 3)
555         {
556                 self->m_drot[1] = PyFloat_AsDouble(PySequence_Fast_GET_ITEM(value, 0));
557                 self->m_dloc[1] = PyFloat_AsDouble(PySequence_Fast_GET_ITEM(value, 1));
558                 self->m_bitLocalFlag.DLoc = (PyLong_AsSsize_t(PySequence_Fast_GET_ITEM(value, 2)) != 0);
559
560                 if (!PyErr_Occurred())
561                 {
562                         Py_DECREF(seq);
563                         return PY_SET_ATTR_SUCCESS;
564                 }
565         }
566
567         Py_XDECREF(seq);
568
569         PyErr_SetString(PyExc_ValueError, "expected a sequence of 2 floats and a bool");
570         return PY_SET_ATTR_FAIL;
571 }
572
573 PyObject* KX_ObjectActuator::pyattr_get_forceLimitZ(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
574 {
575         KX_ObjectActuator* self = reinterpret_cast<KX_ObjectActuator*>(self_v);
576         PyObject *retVal = PyList_New(3);
577
578         PyList_SET_ITEM(retVal, 0, PyFloat_FromDouble(self->m_drot[2]));
579         PyList_SET_ITEM(retVal, 1, PyFloat_FromDouble(self->m_dloc[2]));
580         PyList_SET_ITEM(retVal, 2, PyBool_FromLong(self->m_bitLocalFlag.DRot));
581         
582         return retVal;
583 }
584
585 int     KX_ObjectActuator::pyattr_set_forceLimitZ(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
586 {
587         KX_ObjectActuator* self = reinterpret_cast<KX_ObjectActuator*>(self_v);
588
589         PyObject* seq = PySequence_Fast(value, "");
590         if (seq && PySequence_Fast_GET_SIZE(seq) == 3)
591         {
592                 self->m_drot[2] = PyFloat_AsDouble(PySequence_Fast_GET_ITEM(value, 0));
593                 self->m_dloc[2] = PyFloat_AsDouble(PySequence_Fast_GET_ITEM(value, 1));
594                 self->m_bitLocalFlag.DRot = (PyLong_AsSsize_t(PySequence_Fast_GET_ITEM(value, 2)) != 0);
595
596                 if (!PyErr_Occurred())
597                 {
598                         Py_DECREF(seq);
599                         return PY_SET_ATTR_SUCCESS;
600                 }
601         }
602
603         Py_XDECREF(seq);
604
605         PyErr_SetString(PyExc_ValueError, "expected a sequence of 2 floats and a bool");
606         return PY_SET_ATTR_FAIL;
607 }
608
609 PyObject* KX_ObjectActuator::pyattr_get_reference(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
610 {
611         KX_ObjectActuator* actuator = static_cast<KX_ObjectActuator*>(self);
612         if (!actuator->m_reference)
613                 Py_RETURN_NONE;
614         
615         return actuator->m_reference->GetProxy();
616 }
617
618 int KX_ObjectActuator::pyattr_set_reference(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
619 {
620         KX_ObjectActuator* actuator = static_cast<KX_ObjectActuator*>(self);
621         KX_GameObject *refOb;
622         
623         if (!ConvertPythonToGameObject(value, &refOb, true, "actu.reference = value: KX_ObjectActuator"))
624                 return PY_SET_ATTR_FAIL;
625         
626         if (actuator->m_reference)
627                 actuator->m_reference->UnregisterActuator(actuator);
628         
629         if(refOb==NULL) {
630                 actuator->m_reference= NULL;
631         }
632         else {  
633                 actuator->m_reference = refOb;
634                 actuator->m_reference->RegisterActuator(actuator);
635         }
636         
637         return PY_SET_ATTR_SUCCESS;
638 }
639
640 #endif // DISABLE_PYTHON
641
642 /* eof */