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