Fix for bugs: 1788 (forces) and 1799 (python delattr on game objects)
[blender.git] / source / gameengine / Ketsji / KX_IpoActuator.cpp
1 /**
2  * Do Ipo stuff
3  *
4  * $Id$
5  *
6  * ***** BEGIN GPL/BL DUAL 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. The Blender
12  * Foundation also sells licenses for use in proprietary software under
13  * the Blender License.  See http://www.blender.org/BL/ for information
14  * about this.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  *
25  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
26  * All rights reserved.
27  *
28  * The Original Code is: all of this file.
29  *
30  * Contributor(s): none yet.
31  *
32  * ***** END GPL/BL DUAL LICENSE BLOCK *****
33  */
34
35 #include <cmath>
36  
37 #include "KX_IpoActuator.h"
38 #include "KX_GameObject.h"
39
40 #ifdef HAVE_CONFIG_H
41 #include <config.h>
42 #endif
43
44 #include "KX_KetsjiEngine.h"
45
46 /* ------------------------------------------------------------------------- */
47 /* Type strings                                                              */
48 /* ------------------------------------------------------------------------- */
49
50 STR_String KX_IpoActuator::S_KX_ACT_IPO_PLAY_STRING      = "Play";
51 STR_String KX_IpoActuator::S_KX_ACT_IPO_PINGPONG_STRING  = "PingPong";
52 STR_String KX_IpoActuator::S_KX_ACT_IPO_FLIPPER_STRING   = "Flipper";
53 STR_String KX_IpoActuator::S_KX_ACT_IPO_LOOPSTOP_STRING  = "LoopStop";
54 STR_String KX_IpoActuator::S_KX_ACT_IPO_LOOPEND_STRING   = "LoopEnd";
55 STR_String KX_IpoActuator::S_KX_ACT_IPO_KEY2KEY_STRING   = "Key2key";
56 STR_String KX_IpoActuator::S_KX_ACT_IPO_FROM_PROP_STRING = "FromProp";
57
58 /* ------------------------------------------------------------------------- */
59 /* Native functions                                                          */
60 /* ------------------------------------------------------------------------- */
61 /** Another poltergeist? This seems to be a very transient class... */
62 class CIpoAction : public CAction
63 {
64         float           m_curtime;
65         bool            m_resurse;
66         KX_GameObject* m_gameobj;
67         bool        m_ipo_as_force;
68         bool        m_force_ipo_local;
69
70 public:
71         CIpoAction(KX_GameObject* gameobj,
72                 float curtime,
73                 bool recurse, 
74                 bool ipo_as_force,
75                 bool force_ipo_local) :
76           m_curtime(curtime) ,
77           m_resurse(recurse),
78           m_gameobj(gameobj),
79           m_ipo_as_force(ipo_as_force),
80           m_force_ipo_local(force_ipo_local) 
81           {
82                   /* intentionally empty */
83           };
84
85         virtual void Execute() const
86         {
87                 m_gameobj->UpdateIPO(
88                         m_curtime, 
89                         m_resurse, 
90                         m_ipo_as_force, 
91                         m_force_ipo_local);
92         };
93
94 };
95
96 KX_IpoActuator::KX_IpoActuator(SCA_IObject* gameobj,
97                                                            const STR_String& propname,
98                                                            float starttime,
99                                                            float endtime,
100                                                            bool recurse,
101                                                            int acttype,
102                                                            bool ipo_as_force,
103                                                            bool force_ipo_local,
104                                                            PyTypeObject* T) 
105         : SCA_IActuator(gameobj,T),
106         m_bNegativeEvent(false),
107         m_startframe (starttime),
108         m_endframe(endtime),
109         m_recurse(recurse),
110         m_localtime(starttime),
111         m_starttime(-1.0),
112         m_direction(1),
113         m_propname(propname),
114         m_ipo_as_force(ipo_as_force),
115         m_force_ipo_local(force_ipo_local),
116         m_type((IpoActType)acttype)
117 {
118         // intentionally empty
119 }
120
121 void KX_IpoActuator::SetStart(float starttime) 
122
123         m_startframe=starttime;
124 }
125
126 void KX_IpoActuator::SetEnd(float endtime) 
127
128         m_endframe=endtime;
129 }
130
131 bool KX_IpoActuator::ClampLocalTime()
132 {
133         if (m_startframe < m_endframe)
134         {
135                 if (m_localtime < m_startframe)
136                 {
137                         m_localtime = m_startframe;
138                         return true;
139                 } 
140                 else if (m_localtime > m_endframe)
141                 {
142                         m_localtime = m_endframe;
143                         return true;
144                 }
145         } else {
146                 if (m_localtime > m_startframe)
147                 {
148                         m_localtime = m_startframe;
149                         return true;
150                 }
151                 else if (m_localtime < m_endframe)
152                 {
153                         m_localtime = m_endframe;
154                         return true;
155                 }
156         }
157         return false;
158 }
159
160 void KX_IpoActuator::SetStartTime(float curtime)
161 {
162         float direction = m_startframe < m_endframe ? 1.0 : -1.0;
163         
164         if (m_direction > 0)
165                 m_starttime = curtime - direction*(m_localtime - m_startframe)/KX_FIXED_FRAME_PER_SEC;
166         else
167                 m_starttime = curtime - direction*(m_endframe - m_localtime)/KX_FIXED_FRAME_PER_SEC;
168 }
169
170 void KX_IpoActuator::SetLocalTime(float curtime)
171 {
172         float delta_time = (curtime - m_starttime)*KX_FIXED_FRAME_PER_SEC;
173         
174         if (m_endframe < m_startframe)
175                 delta_time = -delta_time;
176
177         if (m_direction > 0)
178                 m_localtime = m_startframe + delta_time;
179         else
180                 m_localtime = m_endframe - delta_time;
181 }
182
183 bool KX_IpoActuator::Update(double curtime, bool frame)
184 {
185         // result = true if animation has to be continued, false if animation stops
186         // maybe there are events for us in the queue !
187         bool bNegativeEvent = false;
188         int numevents = m_events.size();
189
190         for (vector<CValue*>::iterator i=m_events.end(); !(i==m_events.begin());)
191         {
192                 i--;
193                 if ((*i)->GetNumber() == 0.0f)
194                         bNegativeEvent = true;
195                 
196                 (*i)->Release();
197                 m_events.pop_back();
198         }
199
200         if (bNegativeEvent)
201         {
202                 RemoveAllEvents();
203         }
204         
205
206         double  start_smaller_then_end = ( m_startframe < m_endframe ? 1.0 : -1.0);
207
208         bool result=true;
209         if (m_starttime < 0.0)
210                 m_starttime = curtime;
211         
212         switch (m_type)
213         {
214                 
215         case KX_ACT_IPO_PLAY:
216         {
217                 // Check if playing forwards.  result = ! finished
218                 if (start_smaller_then_end > 0.0)
219                         result = (m_localtime < m_endframe && !(m_localtime == m_startframe && bNegativeEvent));
220                 else
221                         result = (m_localtime > m_endframe && !(m_localtime == m_startframe && bNegativeEvent));
222                 
223                 if (result)
224                 {
225                         SetLocalTime(curtime);
226                 
227                         /* Perform clamping */
228                         ClampLocalTime();
229                         
230                         CIpoAction ipoaction(
231                                 (KX_GameObject*)GetParent(), 
232                                 m_localtime, 
233                                 m_recurse, 
234                                 m_ipo_as_force,
235                                 m_force_ipo_local);
236                         GetParent()->Execute(ipoaction);
237                 } else
238                 {
239                         m_localtime=m_startframe;
240                         m_starttime=curtime;
241                         m_direction=1;
242                 }
243                 break;
244         }
245         case KX_ACT_IPO_PINGPONG:
246         {
247                 result = true;
248                 if (bNegativeEvent && ((m_localtime == m_startframe )|| (m_localtime == m_endframe)))
249                         result = false;
250                 else
251                         SetLocalTime(curtime);
252                         
253                 if (ClampLocalTime())
254                 {
255                         result = false;
256                         m_direction = -m_direction;
257                 }
258                 
259                 CIpoAction ipoaction(
260                         (KX_GameObject*) GetParent(),
261                         m_localtime,
262                         m_recurse, 
263                         m_ipo_as_force,
264                         m_force_ipo_local);
265                 GetParent()->Execute(ipoaction);
266                 break;
267         }
268         case KX_ACT_IPO_FLIPPER:
269         {
270                 result = true;
271                 if (numevents)
272                 {
273                         if (bNegativeEvent)
274                                 m_direction = -1;
275                         else
276                                 m_direction = 1;
277                         SetStartTime(curtime);
278                 }
279                 
280                 SetLocalTime(curtime);
281                 
282                 if (ClampLocalTime() && m_localtime == m_startframe)
283                         result = false;
284                         
285                 CIpoAction ipoaction(
286                         (KX_GameObject*) GetParent(),
287                         m_localtime,
288                         m_recurse,
289                         m_ipo_as_force,
290                         m_force_ipo_local);
291                 GetParent()->Execute(ipoaction);
292                 break;
293         }
294
295         case KX_ACT_IPO_LOOPSTOP:
296         {
297                 if (numevents)
298                 {
299                         if (bNegativeEvent)
300                         {
301                                 result = false;
302                                 m_bNegativeEvent = false;
303                                 numevents = 0;
304                         }
305                         SetStartTime(curtime);
306                 } // fall through to loopend, and quit the ipo animation immediatly 
307         }
308         case KX_ACT_IPO_LOOPEND:
309         {
310                 if (numevents){
311                         if (bNegativeEvent){
312                                 m_bNegativeEvent = true;
313                         }
314                 }
315                 
316                 if (bNegativeEvent && m_localtime == m_startframe){
317                         result = false;
318                 } 
319                 else
320                 {
321                         if (m_localtime*start_smaller_then_end < m_endframe*start_smaller_then_end)
322                         {
323                                 SetLocalTime(curtime);
324                         }
325                         else{
326                                 if (!m_bNegativeEvent){
327                                         /* Perform wraparound */
328                                         SetLocalTime(curtime);
329                                         m_localtime = m_startframe + fmod(m_localtime, m_startframe - m_endframe);
330                                         SetStartTime(curtime);
331                                 }
332                                 else
333                                 {       
334                                         /* Perform clamping */
335                                         m_localtime=m_endframe;
336                                         result = false;
337                                         m_bNegativeEvent = false;
338                                 }
339                         }
340                 }
341                 
342                 CIpoAction ipoaction(
343                         (KX_GameObject*) GetParent(),
344                         m_localtime,
345                         m_recurse,
346                         m_ipo_as_force,
347                         m_force_ipo_local);
348                 GetParent()->Execute(ipoaction);
349                 break;
350         }
351         
352         case KX_ACT_IPO_KEY2KEY:
353         {
354                 // not implemented yet
355                 result = false;
356                 break;
357         }
358         
359         case KX_ACT_IPO_FROM_PROP:
360         {
361                 result = !bNegativeEvent;
362
363                 CValue* propval = GetParent()->GetProperty(m_propname);
364                 if (propval)
365                 {
366                         float target = propval->GetNumber(); 
367                         float delta_time = (curtime - m_starttime)*KX_FIXED_FRAME_PER_SEC;
368                         if (target > m_localtime)
369                         {
370                                 m_localtime += delta_time;
371                                 if (m_localtime > target)
372                                         m_localtime = target;
373                         }
374                         else
375                         {
376                                 m_localtime -= delta_time;
377                                 if (m_localtime < target)
378                                         m_localtime = target;
379                         }
380         
381                         CIpoAction ipoaction(
382                                 (KX_GameObject*) GetParent(),
383                                 m_localtime,
384                                 m_recurse,
385                                 m_ipo_as_force,
386                                 m_force_ipo_local);
387                         GetParent()->Execute(ipoaction);
388
389                 } else
390                 {
391                         result = false;
392                 }
393                 break;
394         }
395                 
396         default:
397                 result = false;
398         }
399         
400         if (!result && m_type != KX_ACT_IPO_LOOPSTOP)
401                 m_starttime = -1.0;
402
403         return result;
404 }
405
406 KX_IpoActuator::IpoActType KX_IpoActuator::string2mode(char* modename) {
407         IpoActType res = KX_ACT_IPO_NODEF;
408
409         if (modename == S_KX_ACT_IPO_PLAY_STRING) { 
410                 res = KX_ACT_IPO_PLAY;
411         } else if (modename == S_KX_ACT_IPO_PINGPONG_STRING) {
412                 res = KX_ACT_IPO_PINGPONG;
413         } else if (modename == S_KX_ACT_IPO_FLIPPER_STRING) {
414                 res = KX_ACT_IPO_FLIPPER;
415         } else if (modename == S_KX_ACT_IPO_LOOPSTOP_STRING) {
416                 res = KX_ACT_IPO_LOOPSTOP;
417         } else if (modename == S_KX_ACT_IPO_LOOPEND_STRING) {
418                 res = KX_ACT_IPO_LOOPEND;
419         } else if (modename == S_KX_ACT_IPO_KEY2KEY_STRING) {
420                 res = KX_ACT_IPO_KEY2KEY;
421         } else if (modename == S_KX_ACT_IPO_FROM_PROP_STRING) {
422                 res = KX_ACT_IPO_FROM_PROP;
423         }
424
425         return res;
426 }
427
428 /* ------------------------------------------------------------------------- */
429 /* Python functions                                                          */
430 /* ------------------------------------------------------------------------- */
431
432
433
434 /* Integration hooks ------------------------------------------------------- */
435 PyTypeObject KX_IpoActuator::Type = {
436         PyObject_HEAD_INIT(&PyType_Type)
437         0,
438         "KX_IpoActuator",
439         sizeof(KX_IpoActuator),
440         0,
441         PyDestructor,
442         0,
443         __getattr,
444         __setattr,
445         0, //&MyPyCompare,
446         __repr,
447         0, //&cvalue_as_number,
448         0,
449         0,
450         0,
451         0
452 };
453
454 PyParentObject KX_IpoActuator::Parents[] = {
455         &KX_IpoActuator::Type,
456         &SCA_IActuator::Type,
457         &SCA_ILogicBrick::Type,
458         &CValue::Type,
459         NULL
460 };
461
462 PyMethodDef KX_IpoActuator::Methods[] = {
463         {"set", (PyCFunction) KX_IpoActuator::sPySet, 
464                 METH_VARARGS, Set_doc},
465         {"setProperty", (PyCFunction) KX_IpoActuator::sPySetProperty, 
466                 METH_VARARGS, SetProperty_doc},
467         {"setStart", (PyCFunction) KX_IpoActuator::sPySetStart, 
468                 METH_VARARGS, SetStart_doc},
469         {"getStart", (PyCFunction) KX_IpoActuator::sPyGetStart, 
470                 METH_VARARGS, GetStart_doc},
471         {"setEnd", (PyCFunction) KX_IpoActuator::sPySetEnd, 
472                 METH_VARARGS, SetEnd_doc},
473         {"getEnd", (PyCFunction) KX_IpoActuator::sPyGetEnd, 
474                 METH_VARARGS, GetEnd_doc},
475         {"setIpoAsForce", (PyCFunction) KX_IpoActuator::sPySetIpoAsForce, 
476                 METH_VARARGS, SetIpoAsForce_doc},
477         {"getIpoAsForce", (PyCFunction) KX_IpoActuator::sPyGetIpoAsForce, 
478                 METH_VARARGS, GetIpoAsForce_doc},
479         {"setType", (PyCFunction) KX_IpoActuator::sPySetType, 
480                 METH_VARARGS, SetType_doc},
481         {"getType", (PyCFunction) KX_IpoActuator::sPyGetType, 
482                 METH_VARARGS, GetType_doc},     
483         {"setForceIpoActsLocal", (PyCFunction) KX_IpoActuator::sPySetForceIpoActsLocal,
484                 METH_VARARGS, SetForceIpoActsLocal_doc},
485         {"getForceIpoActsLocal", (PyCFunction) KX_IpoActuator::sPyGetForceIpoActsLocal,
486                 METH_VARARGS, GetForceIpoActsLocal_doc},
487         {NULL,NULL} //Sentinel
488 };
489
490 PyObject* KX_IpoActuator::_getattr(const STR_String& attr) {
491         _getattr_up(SCA_IActuator);
492 }
493
494
495
496 /* set --------------------------------------------------------------------- */
497 char KX_IpoActuator::Set_doc[] = 
498 "set(mode, startframe, endframe, force?)\n"
499 "\t - mode:       Play, PingPong, Flipper, LoopStop, LoopEnd or FromProp (string)\n"
500 "\t - startframe: first frame to use (int)\n"
501 "\t - endframe  : last frame to use (int)\n"
502 "\t - force?    : interpret this ipo as a force? (KX_TRUE, KX_FALSE)"
503 "\tSet the properties of the actuator.\n";
504 PyObject* KX_IpoActuator::PySet(PyObject* self, 
505                                                                 PyObject* args, 
506                                                                 PyObject* kwds) {
507         /* sets modes PLAY, PINGPONG, FLIPPER, LOOPSTOP, LOOPEND                 */
508         /* arg 1 = mode string, arg 2 = startframe, arg3 = stopframe,            */
509         /* arg4 = force toggle                                                   */
510         char* mode;
511         int forceToggle;
512         IpoActType modenum;
513         int startFrame, stopFrame;
514         if(!PyArg_ParseTuple(args, "siii", &mode, &startFrame, 
515                                                  &stopFrame, &forceToggle)) {
516                 return NULL;
517         }
518         modenum = string2mode(mode);
519         
520         switch (modenum) {
521         case KX_ACT_IPO_PLAY:
522         case KX_ACT_IPO_PINGPONG:
523         case KX_ACT_IPO_FLIPPER:
524         case KX_ACT_IPO_LOOPSTOP:
525         case KX_ACT_IPO_LOOPEND:
526                 m_type         = modenum;
527                 m_startframe    = startFrame;
528                 m_endframe      = stopFrame;
529                 m_ipo_as_force = PyArgToBool(forceToggle);
530                 break;
531         default:
532                 ; /* error */
533         }
534
535         Py_Return;
536 }
537
538 /* set property  ----------------------------------------------------------- */
539 char KX_IpoActuator::SetProperty_doc[] = 
540 "setProperty(propname)\n"
541 "\t - propname: name of the property (string)\n"
542 "\tSet the property to be used in FromProp mode.\n";
543 PyObject* KX_IpoActuator::PySetProperty(PyObject* self, 
544                                                                                 PyObject* args, 
545                                                                                 PyObject* kwds) {
546         /* mode is implicit here, but not supported yet... */
547         /* args: property */
548         char *propertyName;
549         if(!PyArg_ParseTuple(args, "s", &propertyName)) {
550                 return NULL;
551         }
552
553         m_propname = propertyName;
554         
555         Py_Return;
556 }
557
558 /* 4. setStart:                                                              */
559 char KX_IpoActuator::SetStart_doc[] = 
560 "setStart(frame)\n"
561 "\t - frame: first frame to use (int)\n"
562 "\tSet the frame from which the ipo starts playing.\n";
563 PyObject* KX_IpoActuator::PySetStart(PyObject* self, 
564                                                                          PyObject* args, 
565                                                                          PyObject* kwds) {
566         float startArg;
567         if(!PyArg_ParseTuple(args, "f", &startArg)) {
568                 return NULL;            
569         }
570         
571         m_startframe = startArg;
572
573         Py_Return;
574 }
575 /* 5. getStart:                                                              */
576 char KX_IpoActuator::GetStart_doc[] = 
577 "getStart()\n"
578 "\tReturns the frame from which the ipo starts playing.\n";
579 PyObject* KX_IpoActuator::PyGetStart(PyObject* self, 
580                                                                          PyObject* args, 
581                                                                          PyObject* kwds) {
582         return PyFloat_FromDouble(m_startframe);
583 }
584
585 /* 6. setEnd:                                                                */
586 char KX_IpoActuator::SetEnd_doc[] = 
587 "setEnd(frame)\n"
588 "\t - frame: last frame to use (int)\n"
589 "\tSet the frame at which the ipo stops playing.\n";
590 PyObject* KX_IpoActuator::PySetEnd(PyObject* self, 
591                                                                    PyObject* args, 
592                                                                    PyObject* kwds) {
593         float endArg;
594         if(!PyArg_ParseTuple(args, "f", &endArg)) {
595                 return NULL;            
596         }
597         
598         m_endframe = endArg;
599
600         Py_Return;
601 }
602 /* 7. getEnd:                                                                */
603 char KX_IpoActuator::GetEnd_doc[] = 
604 "getEnd()\n"
605 "\tReturns the frame at which the ipo stops playing.\n";
606 PyObject* KX_IpoActuator::PyGetEnd(PyObject* self, 
607                                                                    PyObject* args, 
608                                                                    PyObject* kwds) {
609         return PyFloat_FromDouble(m_endframe);
610 }
611
612 /* 6. setIpoAsForce:                                                           */
613 char KX_IpoActuator::SetIpoAsForce_doc[] = 
614 "setIpoAsForce(force?)\n"
615 "\t - force?    : interpret this ipo as a force? (KX_TRUE, KX_FALSE)\n"
616 "\tSet whether to interpret the ipo as a force rather than a displacement.\n";
617 PyObject* KX_IpoActuator::PySetIpoAsForce(PyObject* self, 
618                                                                                   PyObject* args, 
619                                                                                   PyObject* kwds) { 
620         int boolArg;
621         
622         if (!PyArg_ParseTuple(args, "i", &boolArg)) {
623                 return NULL;
624         }
625
626         m_ipo_as_force = PyArgToBool(boolArg);
627         
628         Py_Return;      
629 }
630 /* 7. getIpoAsForce:                                                         */
631 char KX_IpoActuator::GetIpoAsForce_doc[] = 
632 "getIpoAsForce()\n"
633 "\tReturns whether to interpret the ipo as a force rather than a displacement.\n";
634 PyObject* KX_IpoActuator::PyGetIpoAsForce(PyObject* self, 
635                                                                                   PyObject* args, 
636                                                                                   PyObject* kwds) {
637         return BoolToPyArg(m_ipo_as_force);
638 }
639
640 /* 8. setType:                                                               */
641 char KX_IpoActuator::SetType_doc[] = 
642 "setType(mode)\n"
643 "\t - mode: Play, PingPong, Flipper, LoopStop, LoopEnd or FromProp (string)\n"
644 "\tSet the operation mode of the actuator.\n";
645 PyObject* KX_IpoActuator::PySetType(PyObject* self, 
646                                                                         PyObject* args, 
647                                                                         PyObject* kwds) {
648         int typeArg;
649         
650         if (!PyArg_ParseTuple(args, "i", &typeArg)) {
651                 return NULL;
652         }
653         
654         if ( (typeArg > KX_ACT_IPO_NODEF) 
655                  && (typeArg < KX_ACT_IPO_KEY2KEY) ) {
656                 m_type = (IpoActType) typeArg;
657         }
658         
659         Py_Return;
660 }
661 /* 9. getType:                                                               */
662 char KX_IpoActuator::GetType_doc[] = 
663 "getType()\n"
664 "\tReturns the operation mode of the actuator.\n";
665 PyObject* KX_IpoActuator::PyGetType(PyObject* self, 
666                                                                         PyObject* args, 
667                                                                         PyObject* kwds) {
668         return PyInt_FromLong(m_type);
669 }
670
671 /* 10. setForceIpoActsLocal:                                                 */
672 char KX_IpoActuator::SetForceIpoActsLocal_doc[] = 
673 "setForceIpoActsLocal(local?)\n"
674 "\t - local?    : Apply the ipo-as-force in the object's local\n"
675 "\t               coordinates? (KX_TRUE, KX_FALSE)\n"
676 "\tSet whether to apply the force in the object's local\n"
677 "\tcoordinates rather than the world global coordinates.\n";
678 PyObject* KX_IpoActuator::PySetForceIpoActsLocal(PyObject* self, 
679                                                                                          PyObject* args, 
680                                                                                      PyObject* kwds) { 
681         int boolArg;
682         
683         if (!PyArg_ParseTuple(args, "i", &boolArg)) {
684                 return NULL;
685         }
686
687         m_force_ipo_local = PyArgToBool(boolArg);
688         
689         Py_Return;      
690 }
691 /* 11. getForceIpoActsLocal:                                                */
692 char KX_IpoActuator::GetForceIpoActsLocal_doc[] = 
693 "getForceIpoActsLocal()\n"
694 "\tReturn whether to apply the force in the object's local\n"
695 "\tcoordinates rather than the world global coordinates.\n";
696 PyObject* KX_IpoActuator::PyGetForceIpoActsLocal(PyObject* self, 
697                                                                                          PyObject* args, 
698                                                                                          PyObject* kwds) {
699         return BoolToPyArg(m_force_ipo_local);
700 }
701
702
703 /* eof */