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