Merge with trunk r37677
[blender.git] / source / gameengine / Converter / BL_ActionActuator.cpp
1 /*
2 * $Id$
3 *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): none yet.
26  *
27  * ***** END GPL LICENSE BLOCK *****
28 */
29
30 /** \file gameengine/Converter/BL_ActionActuator.cpp
31  *  \ingroup bgeconv
32  */
33
34
35 #include "SCA_LogicManager.h"
36 #include "BL_ActionActuator.h"
37 #include "BL_ArmatureObject.h"
38 #include "BL_SkinDeformer.h"
39 #include "BL_Action.h"
40 #include "KX_GameObject.h"
41 #include "STR_HashedString.h"
42 #include "MEM_guardedalloc.h"
43 #include "DNA_nla_types.h"
44 #include "DNA_action_types.h"
45 #include "DNA_armature_types.h"
46 #include "DNA_scene_types.h"
47 #include "BLI_blenlib.h"
48 #include "BLI_math.h"
49 #include "BLI_utildefines.h"
50 #include "MT_Matrix4x4.h"
51
52 #include "BKE_action.h"
53 #include "FloatValue.h"
54 #include "PyObjectPlus.h"
55 #include "KX_PyMath.h"
56
57 extern "C" {
58 #include "BKE_animsys.h"
59 #include "BKE_action.h"
60 #include "RNA_access.h"
61 #include "RNA_define.h"
62 }
63
64 BL_ActionActuator::~BL_ActionActuator()
65 {
66         if (m_pose)
67                 game_free_pose(m_pose);
68         if (m_userpose)
69                 game_free_pose(m_userpose);
70         if (m_blendpose)
71                 game_free_pose(m_blendpose);
72 }
73
74 void BL_ActionActuator::ProcessReplica()
75 {
76         SCA_IActuator::ProcessReplica();
77         
78         m_pose = NULL;
79         m_blendpose = NULL;
80         m_localtime=m_startframe;
81         m_lastUpdate=-1;
82         
83 }
84
85 void BL_ActionActuator::SetBlendTime (float newtime){
86         m_blendframe = newtime;
87 }
88
89 CValue* BL_ActionActuator::GetReplica() {
90         BL_ActionActuator* replica = new BL_ActionActuator(*this);//m_float,GetName());
91         replica->ProcessReplica();
92         return replica;
93 }
94
95 bool BL_ActionActuator::ClampLocalTime()
96 {
97         if (m_startframe < m_endframe)
98         {
99                 if (m_localtime < m_startframe)
100                 {
101                         m_localtime = m_startframe;
102                         return true;
103                 } 
104                 else if (m_localtime > m_endframe)
105                 {
106                         m_localtime = m_endframe;
107                         return true;
108                 }
109         } else {
110                 if (m_localtime > m_startframe)
111                 {
112                         m_localtime = m_startframe;
113                         return true;
114                 }
115                 else if (m_localtime < m_endframe)
116                 {
117                         m_localtime = m_endframe;
118                         return true;
119                 }
120         }
121         return false;
122 }
123
124 void BL_ActionActuator::SetStartTime(float curtime)
125 {
126         float direction = m_startframe < m_endframe ? 1.0 : -1.0;
127         
128         if (!(m_flag & ACT_FLAG_REVERSE))
129                 m_starttime = curtime - direction*(m_localtime - m_startframe)/KX_KetsjiEngine::GetAnimFrameRate();
130         else
131                 m_starttime = curtime - direction*(m_endframe - m_localtime)/KX_KetsjiEngine::GetAnimFrameRate();
132 }
133
134 void BL_ActionActuator::SetLocalTime(float curtime)
135 {
136         float delta_time = (curtime - m_starttime)*KX_KetsjiEngine::GetAnimFrameRate();
137         
138         if (m_endframe < m_startframe)
139                 delta_time = -delta_time;
140
141         if (!(m_flag & ACT_FLAG_REVERSE))
142                 m_localtime = m_startframe + delta_time;
143         else
144                 m_localtime = m_endframe - delta_time;
145 }
146
147 bool BL_ActionActuator::Update(double curtime, bool frame)
148 {
149         bool bNegativeEvent = false;
150         bool bPositiveEvent = false;
151         KX_GameObject *obj = (KX_GameObject*)GetParent();
152         short play_mode = BL_Action::ACT_MODE_PLAY;
153         float start = m_startframe, end = m_endframe;
154
155         // If we don't have an action, we can't do anything
156         if (!m_action)
157                 return false;
158
159         // Don't do anything if we're not "active"
160         if (!frame)
161                 return true;
162         
163         // Convert playmode
164         if (m_playtype == ACT_ACTION_LOOP_END)
165                 play_mode = BL_Action::ACT_MODE_LOOP;
166         else if (m_playtype == ACT_ACTION_LOOP_STOP)
167                 play_mode = BL_Action::ACT_MODE_LOOP;
168         else if (m_playtype == ACT_ACTION_PINGPONG)
169                 play_mode = BL_Action::ACT_MODE_PING_PONG;
170         else if (m_playtype == ACT_ACTION_FROM_PROP)
171         {
172                 CValue* prop = GetParent()->GetProperty(m_propname);
173
174                 play_mode = BL_Action::ACT_MODE_PLAY;
175                 start = end = prop->GetNumber();
176                 m_is_going = false;
177         }
178
179         // Handle events
180         bNegativeEvent = m_negevent;
181         bPositiveEvent = m_posevent;
182         RemoveAllEvents();
183
184         if (!m_is_going && bPositiveEvent)
185         {               
186                 m_is_going = true;
187                 obj->PlayAction(m_action->id.name+2, start, end, m_layer, m_blendin, play_mode, 0, m_ipo_flags);
188                 if (m_end_reset)
189                         obj->SetActionFrame(m_layer, m_localtime);
190         }
191         else if (m_is_going && bNegativeEvent)
192         {
193                 m_is_going = false;
194                 
195                 if (!m_end_reset)
196                 {
197                         obj->StopAction(m_layer);
198                         return false;
199                 }
200
201                 m_localtime = obj->GetActionFrame(m_layer);
202                 obj->StopAction(m_layer); // Stop the action after getting the frame
203         }
204
205         // Handle a frame property if it's defined
206         if (m_framepropname[0] != 0)
207         {
208                 CValue* oldprop = obj->GetProperty(m_framepropname);
209                 CValue* newval = new CFloatValue(obj->GetActionFrame(m_layer));
210                 if (oldprop)
211                         oldprop->SetValue(newval);
212                 else
213                         obj->SetProperty(m_framepropname, newval);
214
215                 newval->Release();
216         }
217         if (m_playtype == ACT_ACTION_FROM_PROP)
218         {
219                 return true;
220         }
221         // Handle a finished animation
222         else if (m_is_going && obj->IsActionDone(m_layer))
223         {
224                 return false;
225         }
226
227         return true;
228 }
229
230 #if 0 // Kept around as reference for now
231 bool BL_ActionActuator::Update(double curtime, bool frame)
232 {
233         bool bNegativeEvent = false;
234         bool bPositiveEvent = false;
235         bool keepgoing = true;
236         bool wrap = false;
237         bool apply=true;
238         int     priority;
239         float newweight;
240
241         curtime -= KX_KetsjiEngine::GetSuspendedDelta();
242         
243         // result = true if animation has to be continued, false if animation stops
244         // maybe there are events for us in the queue !
245         if (frame)
246         {
247                 bNegativeEvent = m_negevent;
248                 bPositiveEvent = m_posevent;
249                 RemoveAllEvents();
250                 
251                 if (bPositiveEvent)
252                         m_flag |= ACT_FLAG_ACTIVE;
253                 
254                 if (bNegativeEvent)
255                 {
256                         // dont continue where we left off when restarting
257                         if (m_end_reset) {
258                                 m_flag &= ~ACT_FLAG_LOCKINPUT;
259                         }
260                         
261                         if (!(m_flag & ACT_FLAG_ACTIVE))
262                                 return false;
263                         m_flag &= ~ACT_FLAG_ACTIVE;
264                 }
265         }
266         
267         /*      We know that action actuators have been discarded from all non armature objects:
268         if we're being called, we're attached to a BL_ArmatureObject */
269         BL_ArmatureObject *obj = (BL_ArmatureObject*)GetParent();
270         float length = m_endframe - m_startframe;
271         
272         priority = m_priority;
273         
274         /* Determine pre-incrementation behaviour and set appropriate flags */
275         switch (m_playtype){
276         case ACT_ACTION_MOTION:
277                 if (bNegativeEvent){
278                         keepgoing=false;
279                         apply=false;
280                 };
281                 break;
282         case ACT_ACTION_FROM_PROP:
283                 if (bNegativeEvent){
284                         apply=false;
285                         keepgoing=false;
286                 }
287                 break;
288         case ACT_ACTION_LOOP_END:
289                 if (bPositiveEvent){
290                         if (!(m_flag & ACT_FLAG_LOCKINPUT)){
291                                 m_flag &= ~ACT_FLAG_KEYUP;
292                                 m_flag &= ~ACT_FLAG_REVERSE;
293                                 m_flag |= ACT_FLAG_LOCKINPUT;
294                                 m_localtime = m_startframe;
295                                 m_starttime = curtime;
296                         }
297                 }
298                 if (bNegativeEvent){
299                         m_flag |= ACT_FLAG_KEYUP;
300                 }
301                 break;
302         case ACT_ACTION_LOOP_STOP:
303                 if (bPositiveEvent){
304                         if (!(m_flag & ACT_FLAG_LOCKINPUT)){
305                                 m_flag &= ~ACT_FLAG_REVERSE;
306                                 m_flag &= ~ACT_FLAG_KEYUP;
307                                 m_flag |= ACT_FLAG_LOCKINPUT;
308                                 SetStartTime(curtime);
309                         }
310                 }
311                 if (bNegativeEvent){
312                         m_flag |= ACT_FLAG_KEYUP;
313                         m_flag &= ~ACT_FLAG_LOCKINPUT;
314                         keepgoing=false;
315                         apply=false;
316                 }
317                 break;
318         case ACT_ACTION_PINGPONG:
319                 if (bPositiveEvent){
320                         if (!(m_flag & ACT_FLAG_LOCKINPUT)){
321                                 m_flag &= ~ACT_FLAG_KEYUP;
322                                 m_localtime = m_starttime;
323                                 m_starttime = curtime;
324                                 m_flag |= ACT_FLAG_LOCKINPUT;
325                         }
326                 }
327                 break;
328         case ACT_ACTION_FLIPPER:
329                 if (bPositiveEvent){
330                         if (!(m_flag & ACT_FLAG_LOCKINPUT)){
331                                 m_flag &= ~ACT_FLAG_REVERSE;
332                                 m_flag |= ACT_FLAG_LOCKINPUT;
333                                 SetStartTime(curtime);
334                         }
335                 }
336                 else if (bNegativeEvent){
337                         m_flag |= ACT_FLAG_REVERSE;
338                         m_flag &= ~ACT_FLAG_LOCKINPUT;
339                         SetStartTime(curtime);
340                 }
341                 break;
342         case ACT_ACTION_PLAY:
343                 if (bPositiveEvent){
344                         if (!(m_flag & ACT_FLAG_LOCKINPUT)){
345                                 m_flag &= ~ACT_FLAG_REVERSE;
346                                 m_localtime = m_starttime;
347                                 m_starttime = curtime;
348                                 m_flag |= ACT_FLAG_LOCKINPUT;
349                         }
350                 }
351                 break;
352         default:
353                 break;
354         }
355         
356         /* Perform increment */
357         if (keepgoing){
358                 if (m_playtype == ACT_ACTION_MOTION){
359                         MT_Point3       newpos;
360                         MT_Point3       deltapos;
361                         
362                         newpos = obj->NodeGetWorldPosition();
363                         
364                         /* Find displacement */
365                         deltapos = newpos-m_lastpos;
366                         m_localtime += (length/m_stridelength) * deltapos.length();
367                         m_lastpos = newpos;
368                 }
369                 else{
370                         SetLocalTime(curtime);
371                 }
372         }
373         
374         /* Check if a wrapping response is needed */
375         if (length){
376                 if (m_localtime < m_startframe || m_localtime > m_endframe)
377                 {
378                         m_localtime = m_startframe + fmod(m_localtime, length);
379                         wrap = true;
380                 }
381         }
382         else
383                 m_localtime = m_startframe;
384         
385         /* Perform post-increment tasks */
386         switch (m_playtype){
387         case ACT_ACTION_FROM_PROP:
388                 {
389                         CValue* propval = GetParent()->GetProperty(m_propname);
390                         if (propval)
391                                 m_localtime = propval->GetNumber();
392                         
393                         if (bNegativeEvent){
394                                 keepgoing=false;
395                         }
396                 }
397                 break;
398         case ACT_ACTION_MOTION:
399                 break;
400         case ACT_ACTION_LOOP_STOP:
401                 break;
402         case ACT_ACTION_PINGPONG:
403                 if (wrap){
404                         if (!(m_flag & ACT_FLAG_REVERSE))
405                                 m_localtime = m_endframe;
406                         else 
407                                 m_localtime = m_startframe;
408
409                         m_flag &= ~ACT_FLAG_LOCKINPUT;
410                         m_flag ^= ACT_FLAG_REVERSE; //flip direction
411                         keepgoing = false;
412                 }
413                 break;
414         case ACT_ACTION_FLIPPER:
415                 if (wrap){
416                         if (!(m_flag & ACT_FLAG_REVERSE)){
417                                 m_localtime=m_endframe;
418                                 //keepgoing = false;
419                         }
420                         else {
421                                 m_localtime=m_startframe;
422                                 keepgoing = false;
423                         }
424                 }
425                 break;
426         case ACT_ACTION_LOOP_END:
427                 if (wrap){
428                         if (m_flag & ACT_FLAG_KEYUP){
429                                 keepgoing = false;
430                                 m_localtime = m_endframe;
431                                 m_flag &= ~ACT_FLAG_LOCKINPUT;
432                         }
433                         SetStartTime(curtime);
434                 }
435                 break;
436         case ACT_ACTION_PLAY:
437                 if (wrap){
438                         m_localtime = m_endframe;
439                         keepgoing = false;
440                         m_flag &= ~ACT_FLAG_LOCKINPUT;
441                 }
442                 break;
443         default:
444                 keepgoing = false;
445                 break;
446         }
447         
448         /* Set the property if its defined */
449         if (m_framepropname[0] != '\0') {
450                 CValue* propowner = GetParent();
451                 CValue* oldprop = propowner->GetProperty(m_framepropname);
452                 CValue* newval = new CFloatValue(m_localtime);
453                 if (oldprop) {
454                         oldprop->SetValue(newval);
455                 } else {
456                         propowner->SetProperty(m_framepropname, newval);
457                 }
458                 newval->Release();
459         }
460         
461         if (bNegativeEvent)
462                 m_blendframe=0.0;
463         
464         /* Apply the pose if necessary*/
465         if (apply){
466
467                 /* Priority test */
468                 if (obj->SetActiveAction(this, priority, curtime)){
469                         
470                         /* Get the underlying pose from the armature */
471                         obj->GetPose(&m_pose);
472
473 // 2.4x function, 
474                         /* Override the necessary channels with ones from the action */
475                         // XXX extract_pose_from_action(m_pose, m_action, m_localtime);
476                         
477                         
478 // 2.5x - replacement for extract_pose_from_action(...) above.
479                         {
480                                 struct PointerRNA id_ptr;
481                                 Object *arm= obj->GetArmatureObject();
482                                 bPose *pose_back= arm->pose;
483                                 
484                                 arm->pose= m_pose;
485                                 RNA_id_pointer_create((ID *)arm, &id_ptr);
486                                 animsys_evaluate_action(&id_ptr, m_action, NULL, m_localtime);
487                                 
488                                 arm->pose= pose_back;
489                         
490 // 2.5x - could also do this but looks too high level, constraints use this, it works ok.
491 //                              Object workob; /* evaluate using workob */
492 //                              what_does_obaction(obj->GetArmatureObject(), &workob, m_pose, m_action, NULL, m_localtime);
493                         }
494
495                         // done getting the pose from the action
496                         
497                         /* Perform the user override (if any) */
498                         if (m_userpose){
499                                 extract_pose_from_pose(m_pose, m_userpose);
500                                 game_free_pose(m_userpose); //cant use MEM_freeN(m_userpose) because the channels need freeing too.
501                                 m_userpose = NULL;
502                         }
503 #if 1
504                         /* Handle blending */
505                         if (m_blendin && (m_blendframe<m_blendin)){
506                                 /* If this is the start of a blending sequence... */
507                                 if ((m_blendframe==0.0) || (!m_blendpose)){
508                                         obj->GetMRDPose(&m_blendpose);
509                                         m_blendstart = curtime;
510                                 }
511                                 
512                                 /* Find percentages */
513                                 newweight = (m_blendframe/(float)m_blendin);
514                                 game_blend_poses(m_pose, m_blendpose, 1.0 - newweight);
515
516                                 /* Increment current blending percentage */
517                                 m_blendframe = (curtime - m_blendstart)*KX_KetsjiEngine::GetAnimFrameRate();
518                                 if (m_blendframe>m_blendin)
519                                         m_blendframe = m_blendin;
520                                 
521                         }
522 #endif
523                         m_lastUpdate = m_localtime;
524                         obj->SetPose (m_pose);
525                 }
526                 else{
527                         m_blendframe = 0.0;
528                 }
529         }
530         
531         if (!keepgoing){
532                 m_blendframe = 0.0;
533         }
534         return keepgoing;
535 };
536 #endif
537
538 #ifdef WITH_PYTHON
539
540 /* ------------------------------------------------------------------------- */
541 /* Python functions                                                          */
542 /* ------------------------------------------------------------------------- */
543
544 PyObject* BL_ActionActuator::PyGetChannel(PyObject* value) {
545         char *string= _PyUnicode_AsString(value);
546         
547         if (!string) {
548                 PyErr_SetString(PyExc_TypeError, "expected a single string");
549                 return NULL;
550         }
551         
552         bPoseChannel *pchan;
553         
554         if(m_userpose==NULL && m_pose==NULL) {
555                 BL_ArmatureObject *obj = (BL_ArmatureObject*)GetParent();
556                 obj->GetPose(&m_pose); /* Get the underlying pose from the armature */
557         }
558         
559         // get_pose_channel accounts for NULL pose, run on both incase one exists but
560         // the channel doesnt
561         if(             !(pchan=get_pose_channel(m_userpose, string)) &&
562                         !(pchan=get_pose_channel(m_pose, string))  )
563         {
564                 PyErr_SetString(PyExc_ValueError, "channel doesnt exist");
565                 return NULL;
566         }
567
568         PyObject *ret = PyTuple_New(3);
569         
570         PyObject *list = PyList_New(3); 
571         PyList_SET_ITEM(list, 0, PyFloat_FromDouble(pchan->loc[0]));
572         PyList_SET_ITEM(list, 1, PyFloat_FromDouble(pchan->loc[1]));
573         PyList_SET_ITEM(list, 2, PyFloat_FromDouble(pchan->loc[2]));
574         PyTuple_SET_ITEM(ret, 0, list);
575         
576         list = PyList_New(3);
577         PyList_SET_ITEM(list, 0, PyFloat_FromDouble(pchan->size[0]));
578         PyList_SET_ITEM(list, 1, PyFloat_FromDouble(pchan->size[1]));
579         PyList_SET_ITEM(list, 2, PyFloat_FromDouble(pchan->size[2]));
580         PyTuple_SET_ITEM(ret, 1, list);
581         
582         list = PyList_New(4);
583         PyList_SET_ITEM(list, 0, PyFloat_FromDouble(pchan->quat[0]));
584         PyList_SET_ITEM(list, 1, PyFloat_FromDouble(pchan->quat[1]));
585         PyList_SET_ITEM(list, 2, PyFloat_FromDouble(pchan->quat[2]));
586         PyList_SET_ITEM(list, 3, PyFloat_FromDouble(pchan->quat[3]));
587         PyTuple_SET_ITEM(ret, 2, list);
588
589         return ret;
590 /*
591         return Py_BuildValue("([fff][fff][ffff])",
592                 pchan->loc[0], pchan->loc[1], pchan->loc[2],
593                 pchan->size[0], pchan->size[1], pchan->size[2],
594                 pchan->quat[0], pchan->quat[1], pchan->quat[2], pchan->quat[3] );
595 */
596 }
597
598 /*     setChannel                                                         */
599 KX_PYMETHODDEF_DOC(BL_ActionActuator, setChannel,
600 "setChannel(channel, matrix)\n"
601 "\t - channel   : A string specifying the name of the bone channel.\n"
602 "\t - matrix    : A 4x4 matrix specifying the overriding transformation\n"
603 "\t               as an offset from the bone's rest position.\n")
604 {
605         BL_ArmatureObject *obj = (BL_ArmatureObject*)GetParent();
606         char *string;
607         PyObject *pymat= NULL;
608         PyObject *pyloc= NULL, *pysize= NULL, *pyquat= NULL;
609         bPoseChannel *pchan;
610         
611         if(PyTuple_Size(args)==2) {
612                 if (!PyArg_ParseTuple(args,"sO:setChannel", &string, &pymat)) // matrix
613                         return NULL;
614         }
615         else if(PyTuple_Size(args)==4) {
616                 if (!PyArg_ParseTuple(args,"sOOO:setChannel", &string, &pyloc, &pysize, &pyquat)) // loc/size/quat
617                         return NULL;
618         }
619         else {
620                 PyErr_SetString(PyExc_ValueError, "Expected a string and a 4x4 matrix (2 args) or a string and loc/size/quat sequences (4 args)");
621                 return NULL;
622         }
623         
624         if(pymat) {
625                 float matrix[4][4];
626                 MT_Matrix4x4 mat;
627                 
628                 if(!PyMatTo(pymat, mat))
629                         return NULL;
630                 
631                 mat.getValue((float*)matrix);
632                 
633                 BL_ArmatureObject *obj = (BL_ArmatureObject*)GetParent();
634                 
635                 if (!m_userpose) {
636                         if(!m_pose)
637                                 obj->GetPose(&m_pose); /* Get the underlying pose from the armature */
638                         game_copy_pose(&m_userpose, m_pose, 0);
639                 }
640                 // pchan= verify_pose_channel(m_userpose, string); // adds the channel if its not there.
641                 pchan= get_pose_channel(m_userpose, string); // adds the channel if its not there.
642                 
643                 if(pchan) {
644                         VECCOPY (pchan->loc, matrix[3]);
645                         mat4_to_size( pchan->size,matrix);
646                         mat4_to_quat( pchan->quat,matrix);
647                 }
648         }
649         else {
650                 MT_Vector3 loc;
651                 MT_Vector3 size;
652                 MT_Quaternion quat;
653                 
654                 if (!PyVecTo(pyloc, loc) || !PyVecTo(pysize, size) || !PyQuatTo(pyquat, quat))
655                         return NULL;
656                 
657                 // same as above
658                 if (!m_userpose) {
659                         if(!m_pose)
660                                 obj->GetPose(&m_pose); /* Get the underlying pose from the armature */
661                         game_copy_pose(&m_userpose, m_pose, 0);
662                 }
663                 // pchan= verify_pose_channel(m_userpose, string);
664                 pchan= get_pose_channel(m_userpose, string); // adds the channel if its not there.
665                 
666                 // for some reason loc.setValue(pchan->loc) fails
667                 if(pchan) {
668                         pchan->loc[0]= loc[0]; pchan->loc[1]= loc[1]; pchan->loc[2]= loc[2];
669                         pchan->size[0]= size[0]; pchan->size[1]= size[1]; pchan->size[2]= size[2];
670                         pchan->quat[0]= quat[3]; pchan->quat[1]= quat[0]; pchan->quat[2]= quat[1]; pchan->quat[3]= quat[2]; /* notice xyzw -> wxyz is intentional */
671                 }
672         }
673         
674         if(pchan==NULL) {
675                 PyErr_SetString(PyExc_ValueError, "Channel could not be found, use the 'channelNames' attribute to get a list of valid channels");
676                 return NULL;
677         }
678         
679         Py_RETURN_NONE;
680 }
681
682 /* ------------------------------------------------------------------------- */
683 /* Python Integration Hooks                                                                      */
684 /* ------------------------------------------------------------------------- */
685
686 PyTypeObject BL_ActionActuator::Type = {
687         PyVarObject_HEAD_INIT(NULL, 0)
688         "BL_ActionActuator",
689         sizeof(PyObjectPlus_Proxy),
690         0,
691         py_base_dealloc,
692         0,
693         0,
694         0,
695         0,
696         py_base_repr,
697         0,0,0,0,0,0,0,0,0,
698         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
699         0,0,0,0,0,0,0,
700         Methods,
701         0,
702         0,
703         &SCA_IActuator::Type,
704         0,0,0,0,0,0,
705         py_base_new
706 };
707
708 PyMethodDef BL_ActionActuator::Methods[] = {
709         {"getChannel", (PyCFunction) BL_ActionActuator::sPyGetChannel, METH_O},
710         KX_PYMETHODTABLE(BL_ActionActuator, setChannel),
711         {NULL,NULL} //Sentinel
712 };
713
714 PyAttributeDef BL_ActionActuator::Attributes[] = {
715         KX_PYATTRIBUTE_FLOAT_RW("frameStart", 0, MAXFRAMEF, BL_ActionActuator, m_startframe),
716         KX_PYATTRIBUTE_FLOAT_RW("frameEnd", 0, MAXFRAMEF, BL_ActionActuator, m_endframe),
717         KX_PYATTRIBUTE_FLOAT_RW("blendIn", 0, MAXFRAMEF, BL_ActionActuator, m_blendin),
718         KX_PYATTRIBUTE_RW_FUNCTION("action", BL_ActionActuator, pyattr_get_action, pyattr_set_action),
719         KX_PYATTRIBUTE_RO_FUNCTION("channelNames", BL_ActionActuator, pyattr_get_channel_names),
720         KX_PYATTRIBUTE_SHORT_RW("priority", 0, 100, false, BL_ActionActuator, m_priority),
721         KX_PYATTRIBUTE_FLOAT_RW_CHECK("frame", 0, MAXFRAMEF, BL_ActionActuator, m_localtime, CheckFrame),
722         KX_PYATTRIBUTE_STRING_RW("propName", 0, 31, false, BL_ActionActuator, m_propname),
723         KX_PYATTRIBUTE_STRING_RW("framePropName", 0, 31, false, BL_ActionActuator, m_framepropname),
724         KX_PYATTRIBUTE_BOOL_RW("useContinue", BL_ActionActuator, m_end_reset),
725         KX_PYATTRIBUTE_FLOAT_RW_CHECK("blendTime", 0, MAXFRAMEF, BL_ActionActuator, m_blendframe, CheckBlendTime),
726         KX_PYATTRIBUTE_SHORT_RW_CHECK("mode",0,100,false,BL_ActionActuator,m_playtype,CheckType),
727         { NULL }        //Sentinel
728 };
729
730 PyObject* BL_ActionActuator::pyattr_get_action(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
731 {
732         BL_ActionActuator* self= static_cast<BL_ActionActuator*>(self_v);
733         return PyUnicode_FromString(self->GetAction() ? self->GetAction()->id.name+2 : "");
734 }
735
736 int BL_ActionActuator::pyattr_set_action(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
737 {
738         BL_ActionActuator* self= static_cast<BL_ActionActuator*>(self_v);
739         
740         if (!PyUnicode_Check(value))
741         {
742                 PyErr_SetString(PyExc_ValueError, "actuator.action = val: Action Actuator, expected the string name of the action");
743                 return PY_SET_ATTR_FAIL;
744         }
745
746         bAction *action= NULL;
747         STR_String val = _PyUnicode_AsString(value);
748         
749         if (val != "")
750         {
751                 action= (bAction*)SCA_ILogicBrick::m_sCurrentLogicManager->GetActionByName(val);
752                 if (!action)
753                 {
754                         PyErr_SetString(PyExc_ValueError, "actuator.action = val: Action Actuator, action not found!");
755                         return PY_SET_ATTR_FAIL;
756                 }
757         }
758         
759         self->SetAction(action);
760         return PY_SET_ATTR_SUCCESS;
761
762 }
763
764 PyObject* BL_ActionActuator::pyattr_get_channel_names(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
765 {
766         BL_ActionActuator* self= static_cast<BL_ActionActuator*>(self_v);
767         PyObject *ret= PyList_New(0);
768         PyObject *item;
769         
770         bPose *pose= ((BL_ArmatureObject*)self->GetParent())->GetOrigPose();
771         
772         if(pose) {
773                 bPoseChannel *pchan;
774                 for(pchan= (bPoseChannel *)pose->chanbase.first; pchan; pchan= (bPoseChannel *)pchan->next) {
775                         item= PyUnicode_FromString(pchan->name);
776                         PyList_Append(ret, item);
777                         Py_DECREF(item);
778                 }
779         }
780         
781         return ret;
782 }
783
784 #endif // WITH_PYTHON