BGE API Cleanup: update the python attribute definition framework.
authorBenoit Bolsee <benoit.bolsee@online.be>
Fri, 2 Jan 2009 17:43:56 +0000 (17:43 +0000)
committerBenoit Bolsee <benoit.bolsee@online.be>
Fri, 2 Jan 2009 17:43:56 +0000 (17:43 +0000)
* Value clamping to min/max is now supported as an option for integer, float
  and string attribute (for string clamping=trim to max length)
* Post check function now take PyAttributeDef parameter so that more
  generic function can be written.
* Definition of SCA_ILogicBrick::CheckProperty() function to check that
  a string attribute contains a valid property name of the parent game object.
* Definition of enum attribute vi KX_PYATTRIBUTE_ENUM... macros.
  Enum are handled just like integer but to be totally paranoid, the sizeof()
  of the enum member is check at run time to match integer size.
* More bricks updated to use the framework.

18 files changed:
source/gameengine/Expressions/PyObjectPlus.cpp
source/gameengine/Expressions/PyObjectPlus.h
source/gameengine/GameLogic/SCA_ActuatorSensor.cpp
source/gameengine/GameLogic/SCA_ActuatorSensor.h
source/gameengine/GameLogic/SCA_DelaySensor.cpp
source/gameengine/GameLogic/SCA_ILogicBrick.cpp
source/gameengine/GameLogic/SCA_ILogicBrick.h
source/gameengine/GameLogic/SCA_ISensor.cpp
source/gameengine/GameLogic/SCA_JoystickSensor.cpp
source/gameengine/GameLogic/SCA_JoystickSensor.h
source/gameengine/GameLogic/SCA_KeyboardSensor.cpp
source/gameengine/GameLogic/SCA_MouseSensor.cpp
source/gameengine/GameLogic/SCA_MouseSensor.h
source/gameengine/GameLogic/SCA_PropertyActuator.cpp
source/gameengine/GameLogic/SCA_PropertySensor.cpp
source/gameengine/GameLogic/SCA_PropertySensor.h
source/gameengine/GameLogic/SCA_RandomActuator.cpp
source/gameengine/GameLogic/SCA_RandomActuator.h

index c315876..8fd99c8 100644 (file)
@@ -164,6 +164,14 @@ PyObject *PyObjectPlus::_getattr_self(const PyAttributeDef attrlist[], void *sel
                                                        PyList_SetItem(resultlist,i,PyInt_FromLong(*val));
                                                        break;
                                                }
+                                       case KX_PYATTRIBUTE_TYPE_ENUM:
+                                               // enum are like int, just make sure the field size is the same
+                                               if (sizeof(int) != attrdef->m_size)
+                                               {
+                                                       Py_DECREF(resultlist);
+                                                       return NULL;
+                                               }
+                                               // walkthrough
                                        case KX_PYATTRIBUTE_TYPE_INT:
                                                {
                                                        int *val = reinterpret_cast<int*>(ptr);
@@ -180,6 +188,7 @@ PyObject *PyObjectPlus::_getattr_self(const PyAttributeDef attrlist[], void *sel
                                                }
                                        default:
                                                // no support for array of complex data
+                                               Py_DECREF(resultlist);
                                                return NULL;
                                        }
                                }
@@ -198,6 +207,13 @@ PyObject *PyObjectPlus::_getattr_self(const PyAttributeDef attrlist[], void *sel
                                                short int *val = reinterpret_cast<short int*>(ptr);
                                                return PyInt_FromLong(*val);
                                        }
+                               case KX_PYATTRIBUTE_TYPE_ENUM:
+                                       // enum are like int, just make sure the field size is the same
+                                       if (sizeof(int) != attrdef->m_size)
+                                       {
+                                               return NULL;
+                                       }
+                                       // walkthrough
                                case KX_PYATTRIBUTE_TYPE_INT:
                                        {
                                                int *val = reinterpret_cast<int*>(ptr);
@@ -260,6 +276,7 @@ int PyObjectPlus::_setattr_self(const PyAttributeDef attrlist[], void *self, con
                                case KX_PYATTRIBUTE_TYPE_SHORT:
                                        bufferSize = sizeof(short int);
                                        break;
+                               case KX_PYATTRIBUTE_TYPE_ENUM:
                                case KX_PYATTRIBUTE_TYPE_INT:
                                        bufferSize = sizeof(int);
                                        break;
@@ -313,7 +330,14 @@ int PyObjectPlus::_setattr_self(const PyAttributeDef attrlist[], void *self, con
                                                        if (PyInt_Check(item)) 
                                                        {
                                                                long val = PyInt_AsLong(item);
-                                                               if (val < attrdef->m_imin || val > attrdef->m_imax)
+                                                               if (attrdef->m_clamp)
+                                                               {
+                                                                       if (val < attrdef->m_imin)
+                                                                               val = attrdef->m_imin;
+                                                                       else if (val > attrdef->m_imax)
+                                                                               val = attrdef->m_imax;
+                                                               }
+                                                               else if (val < attrdef->m_imin || val > attrdef->m_imax)
                                                                {
                                                                        PyErr_SetString(PyExc_ValueError, "item value out of range");
                                                                        goto UNDO_AND_ERROR;
@@ -327,6 +351,14 @@ int PyObjectPlus::_setattr_self(const PyAttributeDef attrlist[], void *self, con
                                                        }
                                                        break;
                                                }
+                                       case KX_PYATTRIBUTE_TYPE_ENUM:
+                                               // enum are equivalent to int, just make sure that the field size matches:
+                                               if (sizeof(int) != attrdef->m_size)
+                                               {
+                                                       PyErr_SetString(PyExc_AttributeError, "attribute size check error, report to blender.org");
+                                                       goto UNDO_AND_ERROR;
+                                               }
+                                               // walkthrough
                                        case KX_PYATTRIBUTE_TYPE_INT:
                                                {
                                                        int *var = reinterpret_cast<int*>(ptr);
@@ -334,7 +366,14 @@ int PyObjectPlus::_setattr_self(const PyAttributeDef attrlist[], void *self, con
                                                        if (PyInt_Check(item)) 
                                                        {
                                                                long val = PyInt_AsLong(item);
-                                                               if (val < attrdef->m_imin || val > attrdef->m_imax)
+                                                               if (attrdef->m_clamp)
+                                                               {
+                                                                       if (val < attrdef->m_imin)
+                                                                               val = attrdef->m_imin;
+                                                                       else if (val > attrdef->m_imax)
+                                                                               val = attrdef->m_imax;
+                                                               }
+                                                               else if (val < attrdef->m_imin || val > attrdef->m_imax)
                                                                {
                                                                        PyErr_SetString(PyExc_ValueError, "item value out of range");
                                                                        goto UNDO_AND_ERROR;
@@ -352,21 +391,25 @@ int PyObjectPlus::_setattr_self(const PyAttributeDef attrlist[], void *self, con
                                                {
                                                        float *var = reinterpret_cast<float*>(ptr);
                                                        ptr += sizeof(float);
-                                                       if (PyFloat_Check(item)) 
+                                                       double val = PyFloat_AsDouble(item);
+                                                       if (val == -1.0 && PyErr_Occurred())
                                                        {
-                                                               double val = PyFloat_AsDouble(item);
-                                                               if (val < attrdef->m_fmin || val > attrdef->m_fmax)
-                                                               {
-                                                                       PyErr_SetString(PyExc_ValueError, "item value out of range");
-                                                                       goto UNDO_AND_ERROR;
-                                                               }
-                                                               *var = (float)val;
+                                                               PyErr_SetString(PyExc_TypeError, "expected a float");
+                                                               goto UNDO_AND_ERROR;
                                                        }
-                                                       else
+                                                       else if (attrdef->m_clamp) 
                                                        {
-                                                               PyErr_SetString(PyExc_TypeError, "expected a float");
+                                                               if (val < attrdef->m_fmin)
+                                                                       val = attrdef->m_fmin;
+                                                               else if (val > attrdef->m_fmax)
+                                                                       val = attrdef->m_fmax;
+                                                       }
+                                                       else if (val < attrdef->m_fmin || val > attrdef->m_fmax)
+                                                       {
+                                                               PyErr_SetString(PyExc_ValueError, "item value out of range");
                                                                goto UNDO_AND_ERROR;
                                                        }
+                                                       *var = (float)val;
                                                        break;
                                                }
                                        default:
@@ -378,7 +421,7 @@ int PyObjectPlus::_setattr_self(const PyAttributeDef attrlist[], void *self, con
                                // no error, call check function if any
                                if (attrdef->m_function != NULL)
                                {
-                                       if ((*attrdef->m_function)(self) != 0)
+                                       if ((*attrdef->m_function)(self, attrdef) != 0)
                                        {
                                                // post check returned an error, restore values
                                        UNDO_AND_ERROR:
@@ -409,6 +452,7 @@ int PyObjectPlus::_setattr_self(const PyAttributeDef attrlist[], void *self, con
                                        case KX_PYATTRIBUTE_TYPE_SHORT:
                                                bufferSize = sizeof(short);
                                                break;
+                                       case KX_PYATTRIBUTE_TYPE_ENUM:
                                        case KX_PYATTRIBUTE_TYPE_INT:
                                                bufferSize = sizeof(int);
                                                break;
@@ -460,7 +504,14 @@ int PyObjectPlus::_setattr_self(const PyAttributeDef attrlist[], void *self, con
                                                if (PyInt_Check(value)) 
                                                {
                                                        long val = PyInt_AsLong(value);
-                                                       if (val < attrdef->m_imin || val > attrdef->m_imax)
+                                                       if (attrdef->m_clamp)
+                                                       {
+                                                               if (val < attrdef->m_imin)
+                                                                       val = attrdef->m_imin;
+                                                               else if (val > attrdef->m_imax)
+                                                                       val = attrdef->m_imax;
+                                                       }
+                                                       else if (val < attrdef->m_imin || val > attrdef->m_imax)
                                                        {
                                                                PyErr_SetString(PyExc_ValueError, "value out of range");
                                                                goto FREE_AND_ERROR;
@@ -474,13 +525,28 @@ int PyObjectPlus::_setattr_self(const PyAttributeDef attrlist[], void *self, con
                                                }
                                                break;
                                        }
+                               case KX_PYATTRIBUTE_TYPE_ENUM:
+                                       // enum are equivalent to int, just make sure that the field size matches:
+                                       if (sizeof(int) != attrdef->m_size)
+                                       {
+                                               PyErr_SetString(PyExc_AttributeError, "attribute size check error, report to blender.org");
+                                               goto FREE_AND_ERROR;
+                                       }
+                                       // walkthrough
                                case KX_PYATTRIBUTE_TYPE_INT:
                                        {
                                                int *var = reinterpret_cast<int*>(ptr);
                                                if (PyInt_Check(value)) 
                                                {
                                                        long val = PyInt_AsLong(value);
-                                                       if (val < attrdef->m_imin || val > attrdef->m_imax)
+                                                       if (attrdef->m_clamp)
+                                                       {
+                                                               if (val < attrdef->m_imin)
+                                                                       val = attrdef->m_imin;
+                                                               else if (val > attrdef->m_imax)
+                                                                       val = attrdef->m_imax;
+                                                       }
+                                                       else if (val < attrdef->m_imin || val > attrdef->m_imax)
                                                        {
                                                                PyErr_SetString(PyExc_ValueError, "value out of range");
                                                                goto FREE_AND_ERROR;
@@ -497,21 +563,25 @@ int PyObjectPlus::_setattr_self(const PyAttributeDef attrlist[], void *self, con
                                case KX_PYATTRIBUTE_TYPE_FLOAT:
                                        {
                                                float *var = reinterpret_cast<float*>(ptr);
-                                               if (PyFloat_Check(value)) 
+                                               double val = PyFloat_AsDouble(value);
+                                               if (val == -1.0 && PyErr_Occurred())
                                                {
-                                                       double val = PyFloat_AsDouble(value);
-                                                       if (val < attrdef->m_fmin || val > attrdef->m_fmax)
-                                                       {
-                                                               PyErr_SetString(PyExc_ValueError, "value out of range");
-                                                               goto FREE_AND_ERROR;
-                                                       }
-                                                       *var = (float)val;
+                                                       PyErr_SetString(PyExc_TypeError, "expected a float");
+                                                       goto FREE_AND_ERROR;
                                                }
-                                               else
+                                               else if (attrdef->m_clamp)
                                                {
-                                                       PyErr_SetString(PyExc_TypeError, "expected a float");
+                                                       if (val < attrdef->m_fmin)
+                                                               val = attrdef->m_fmin;
+                                                       else if (val > attrdef->m_fmax)
+                                                               val = attrdef->m_fmax;
+                                               }
+                                               else if (val < attrdef->m_fmin || val > attrdef->m_fmax)
+                                               {
+                                                       PyErr_SetString(PyExc_ValueError, "value out of range");
                                                        goto FREE_AND_ERROR;
                                                }
+                                               *var = (float)val;
                                                break;
                                        }
                                case KX_PYATTRIBUTE_TYPE_STRING:
@@ -520,7 +590,24 @@ int PyObjectPlus::_setattr_self(const PyAttributeDef attrlist[], void *self, con
                                                if (PyString_Check(value)) 
                                                {
                                                        char *val = PyString_AsString(value);
-                                                       if (strlen(val) < attrdef->m_imin || strlen(val) > attrdef->m_imax)
+                                                       if (attrdef->m_clamp)
+                                                       {
+                                                               if (strlen(val) < attrdef->m_imin)
+                                                               {
+                                                                       // can't increase the length of the string
+                                                                       PyErr_SetString(PyExc_ValueError, "string length too short");
+                                                                       goto FREE_AND_ERROR;
+                                                               }
+                                                               else if (strlen(val) > attrdef->m_imax)
+                                                               {
+                                                                       // trim the string
+                                                                       char c = val[attrdef->m_imax];
+                                                                       val[attrdef->m_imax] = 0;
+                                                                       *var = val;
+                                                                       val[attrdef->m_imax] = c;
+                                                                       break;
+                                                               }
+                                                       } else if (strlen(val) < attrdef->m_imin || strlen(val) > attrdef->m_imax)
                                                        {
                                                                PyErr_SetString(PyExc_ValueError, "string length out of range");
                                                                goto FREE_AND_ERROR;
@@ -543,9 +630,10 @@ int PyObjectPlus::_setattr_self(const PyAttributeDef attrlist[], void *self, con
                        // check if post processing is needed
                        if (attrdef->m_function != NULL)
                        {
-                               if ((*attrdef->m_function)(self) != 0)
+                               if ((*attrdef->m_function)(self, attrdef) != 0)
                                {
                                        // restore value
+                               RESTORE_AND_ERROR:
                                        if (undoBuffer)
                                        {
                                                if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_STRING)
index 016fa29..e0e2213 100644 (file)
@@ -216,6 +216,7 @@ PyObject* class_name::Py##method_name(PyObject*)
  */
 enum KX_PYATTRIBUTE_TYPE {
        KX_PYATTRIBUTE_TYPE_BOOL,
+       KX_PYATTRIBUTE_TYPE_ENUM,
        KX_PYATTRIBUTE_TYPE_SHORT,
        KX_PYATTRIBUTE_TYPE_INT,
        KX_PYATTRIBUTE_TYPE_FLOAT,
@@ -228,17 +229,20 @@ enum KX_PYATTRIBUTE_ACCESS {
        KX_PYATTRIBUTE_RO
 };
 
-typedef int (*KX_PYATTRIBUTE_FUNCTION)(void *self);
+struct KX_PYATTRIBUTE_DEF;
+typedef int (*KX_PYATTRIBUTE_FUNCTION)(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef);
 
 typedef struct KX_PYATTRIBUTE_DEF {
        const char *m_name;                             // name of the python attribute
        KX_PYATTRIBUTE_TYPE m_type;             // type of value
        KX_PYATTRIBUTE_ACCESS m_access; // read/write access or read-only
-       int m_imin;                                             // minimum value in case of integer attributes
-       int m_imax;                                             // maximum value in case of integer attributes
+       int m_imin;                                             // minimum value in case of integer attributes (for string: minimum string length)
+       int m_imax;                                             // maximum value in case of integer attributes (for string: maximum string length)
        float m_fmin;                                   // minimum value in case of float attributes
        float m_fmax;                                   // maximum value in case of float attributes
+       bool   m_clamp;                                 // enforce min/max value by clamping
        size_t m_offset;                                // position of field in structure
+       size_t m_size;                                  // size of field for runtime verification (enum only)
        size_t m_length;                                // length of array, 1=simple attribute
        KX_PYATTRIBUTE_FUNCTION m_function;     // static function to check the assignment, returns 0 if no error
        // The following pointers are just used to have compile time check for attribute type.
@@ -254,60 +258,70 @@ typedef struct KX_PYATTRIBUTE_DEF {
 } PyAttributeDef;
 
 #define KX_PYATTRIBUTE_DUMMY(name) \
-       { name, KX_PYATTRIBUTE_TYPE_DUMMY, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, 0, 1, NULL, {NULL, NULL, NULL, NULL, NULL} }
+       { name, KX_PYATTRIBUTE_TYPE_DUMMY, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, 0, 0, 1, NULL, {NULL, NULL, NULL, NULL, NULL} }
 
 #define KX_PYATTRIBUTE_BOOL_RW(name,object,field) \
-       { name, KX_PYATTRIBUTE_TYPE_BOOL, KX_PYATTRIBUTE_RW, 0, 1, 0.f, 0.f, offsetof(object,field), 1, NULL, {&((object *)0)->field, NULL, NULL, NULL, NULL} }
+       { name, KX_PYATTRIBUTE_TYPE_BOOL, KX_PYATTRIBUTE_RW, 0, 1, 0.f, 0.f, false, offsetof(object,field), 0, 1, NULL, {&((object *)0)->field, NULL, NULL, NULL, NULL} }
 #define KX_PYATTRIBUTE_BOOL_RW_CHECK(name,object,field,function) \
-       { name, KX_PYATTRIBUTE_TYPE_BOOL, KX_PYATTRIBUTE_RW, 0, 1, 0.f, 0.f, offsetof(object,field), 1, &object::function, {&((object *)0)->field, NULL, NULL, NULL, NULL} }
+       { name, KX_PYATTRIBUTE_TYPE_BOOL, KX_PYATTRIBUTE_RW, 0, 1, 0.f, 0.f, false, offsetof(object,field), 0, 1, &object::function, {&((object *)0)->field, NULL, NULL, NULL, NULL} }
 #define KX_PYATTRIBUTE_BOOL_RO(name,object,field) \
-       { name, KX_PYATTRIBUTE_TYPE_BOOL, KX_PYATTRIBUTE_RO, 0, 1, 0.f, 0.f, offsetof(object,field), 1, NULL, {&((object *)0)->field, NULL, NULL, NULL, NULL} }
-
-#define KX_PYATTRIBUTE_SHORT_RW(name,min,max,object,field) \
-       { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, offsetof(object,field), 1, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL} }
-#define KX_PYATTRIBUTE_SHORT_RW_CHECK(name,min,max,object,field,function) \
-       { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, offsetof(object,field), 1, &object::function, {NULL, &((object *)0)->field, NULL, NULL, NULL} }
+       { name, KX_PYATTRIBUTE_TYPE_BOOL, KX_PYATTRIBUTE_RO, 0, 1, 0.f, 0.f, false, offsetof(object,field), 0, 1, NULL, {&((object *)0)->field, NULL, NULL, NULL, NULL} }
+
+// enum field cannot be mapped to pointer (because we would need a pointer for each enum)
+// use field size to verify mapping at runtime only, assuming enum size is equal to int size.
+#define KX_PYATTRIBUTE_ENUM_RW(name,min,max,clamp,object,field) \
+       { name, KX_PYATTRIBUTE_TYPE_ENUM, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), sizeof(((object *)0)->field), 1, NULL, {NULL, NULL, NULL, NULL, NULL} }
+#define KX_PYATTRIBUTE_ENUM_RW_CHECK(name,min,max,clamp,object,field,function) \
+       { name, KX_PYATTRIBUTE_TYPE_ENUM, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), sizeof(((object *)0)->field), 1, &object::function, {NULL, NULL, NULL, NULL, NULL} }
+#define KX_PYATTRIBUTE_ENUM_RO(name,object,field) \
+       { name, KX_PYATTRIBUTE_TYPE_ENUM, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, offsetof(object,field), sizeof(((object *)0)->field), 1, NULL, {NULL, NULL, NULL, NULL, NULL} }
+
+#define KX_PYATTRIBUTE_SHORT_RW(name,min,max,clamp,object,field) \
+       { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, 1, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL} }
+#define KX_PYATTRIBUTE_SHORT_RW_CHECK(name,min,max,clamp,object,field,function) \
+       { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, 1, &object::function, {NULL, &((object *)0)->field, NULL, NULL, NULL} }
 #define KX_PYATTRIBUTE_SHORT_RO(name,object,field) \
-       { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, offsetof(object,field), 1, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL} }
-#define KX_PYATTRIBUTE_SHORT_ARRAY_RW(name,min,max,object,field,length) \
-       { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, offsetof(object,field), length, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL} }
-#define KX_PYATTRIBUTE_SHORT_ARRAY_RW_CHECK(name,min,max,object,field,length,function) \
-       { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, offsetof(object,field), length, &object::function, {NULL, &((object *)0)->field, NULL, NULL, NULL} }
+       { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, offsetof(object,field), 0, 1, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL} }
+#define KX_PYATTRIBUTE_SHORT_ARRAY_RW(name,min,max,clamp,object,field,length) \
+       { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, length, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL} }
+#define KX_PYATTRIBUTE_SHORT_ARRAY_RW_CHECK(name,min,max,clamp,object,field,length,function) \
+       { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, length, &object::function, {NULL, &((object *)0)->field, NULL, NULL, NULL} }
 #define KX_PYATTRIBUTE_SHORT_ARRAY_RO(name,object,field,length) \
-       { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, offsetof(object,field), length, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL} }
+       { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, offsetof(object,field), 0, length, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL} }
 
-#define KX_PYATTRIBUTE_INT_RW(name,min,max,object,field) \
-       { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, offsetof(object,field), 1, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL} }
-#define KX_PYATTRIBUTE_INT_RW_CHECK(name,min,max,object,field,function) \
-       { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, offsetof(object,field), 1, &object::function, {NULL, NULL, &((object *)0)->field, NULL, NULL} }
+#define KX_PYATTRIBUTE_INT_RW(name,min,max,clamp,object,field) \
+       { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, 1, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL} }
+#define KX_PYATTRIBUTE_INT_RW_CHECK(name,min,max,clamp,object,field,function) \
+       { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, 1, &object::function, {NULL, NULL, &((object *)0)->field, NULL, NULL} }
 #define KX_PYATTRIBUTE_INT_RO(name,object,field) \
-       { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, offsetof(object,field), 1, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL} }
-#define KX_PYATTRIBUTE_INT_ARRAY_RW(name,min,max,object,field,length) \
-       { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, offsetof(object,field), length, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL} }
-#define KX_PYATTRIBUTE_INT_ARRAY_RW_CHECK(name,min,max,object,field,length,function) \
-       { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, offsetof(object,field), length, &object::function, {NULL, NULL, &((object *)0)->field, NULL, NULL} }
+       { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, offsetof(object,field), 0, 1, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL} }
+#define KX_PYATTRIBUTE_INT_ARRAY_RW(name,min,max,clamp,object,field,length) \
+       { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, length, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL} }
+#define KX_PYATTRIBUTE_INT_ARRAY_RW_CHECK(name,min,max,clamp,object,field,length,function) \
+       { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, length, &object::function, {NULL, NULL, &((object *)0)->field, NULL, NULL} }
 #define KX_PYATTRIBUTE_INT_ARRAY_RO(name,object,field,length) \
-       { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, offsetof(object,field), length, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL} }
+       { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, offsetof(object,field), 0, length, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL} }
 
+// always clamp for float
 #define KX_PYATTRIBUTE_FLOAT_RW(name,min,max,object,field) \
-       { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, 0, min, max, offsetof(object,field), 1, NULL, {NULL, NULL, NULL, &((object *)0)->field, NULL} }
+       { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, 0, min, max, true, offsetof(object,field), 0, 1, NULL, {NULL, NULL, NULL, &((object *)0)->field, NULL} }
 #define KX_PYATTRIBUTE_FLOAT_RW_CHECK(name,min,max,object,field,function) \
-       { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, 0, min, max, offsetof(object,field), 1, &object::function, {NULL, NULL, NULL, &((object *)0)->field, NULL} }
+       { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, 0, min, max, true, offsetof(object,field), 0, 1, &object::function, {NULL, NULL, NULL, &((object *)0)->field, NULL} }
 #define KX_PYATTRIBUTE_FLOAT_RO(name,object,field) \
-       { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, offsetof(object,field), 1, NULL, {NULL, NULL, NULL, &((object *)0)->field, NULL} }
+       { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, offsetof(object,field), 0, 1, NULL, {NULL, NULL, NULL, &((object *)0)->field, NULL} }
 #define KX_PYATTRIBUTE_FLOAT_ARRAY_RW(name,min,max,object,field,length) \
-       { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, 0, min, max, offsetof(object,field), length, NULL, {NULL, NULL, NULL, &((object *)0)->field, NULL} }
+       { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, 0, min, max, true, offsetof(object,field), 0, length, NULL, {NULL, NULL, NULL, &((object *)0)->field, NULL} }
 #define KX_PYATTRIBUTE_FLOAT_ARRAY_RW_CHECK(name,min,max,object,field,length,function) \
-       { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, 0, min, max, offsetof(object,field), length, &object::function, {NULL, NULL, NULL, &((object *)0)->field, NULL} }
+       { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, 0, min, max, true, offsetof(object,field), 0, length, &object::function, {NULL, NULL, NULL, &((object *)0)->field, NULL} }
 #define KX_PYATTRIBUTE_FLOAT_ARRAY_RO(name,object,field,length) \
-       { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, offsetof(object,field), length, NULL, {NULL, NULL, NULL, &((object *)0)->field, NULL} }
+       { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, offsetof(object,field), 0, length, NULL, {NULL, NULL, NULL, &((object *)0)->field, NULL} }
 
-#define KX_PYATTRIBUTE_STRING_RW(name,min,max,object,field) \
-       { name, KX_PYATTRIBUTE_TYPE_STRING, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, offsetof(object,field), 1, NULL, {NULL, NULL, NULL, NULL, &((object *)0)->field} }
-#define KX_PYATTRIBUTE_STRING_RW_CHECK(name,min,max,object,field,function) \
-       { name, KX_PYATTRIBUTE_TYPE_STRING, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, offsetof(object,field), 1, &object::function, {NULL, NULL, NULL, NULL, &((object *)0)->field} }
+#define KX_PYATTRIBUTE_STRING_RW(name,min,max,clamp,object,field) \
+       { name, KX_PYATTRIBUTE_TYPE_STRING, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, 1, NULL, {NULL, NULL, NULL, NULL, &((object *)0)->field} }
+#define KX_PYATTRIBUTE_STRING_RW_CHECK(name,min,max,clamp,object,field,function) \
+       { name, KX_PYATTRIBUTE_TYPE_STRING, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, 1, &object::function, {NULL, NULL, NULL, NULL, &((object *)0)->field} }
 #define KX_PYATTRIBUTE_STRING_RO(name,object,field) \
-       { name, KX_PYATTRIBUTE_TYPE_STRING, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, offsetof(object,field), 1 , NULL, {NULL, NULL, NULL, NULL, &((object *)0)->field} }
+       { name, KX_PYATTRIBUTE_TYPE_STRING, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, offsetof(object,field), 0, 1 , NULL, {NULL, NULL, NULL, NULL, &((object *)0)->field} }
 
 /*------------------------------
  * PyObjectPlus
index 5ad28db..fae8d2b 100644 (file)
@@ -156,27 +156,34 @@ PyMethodDef SCA_ActuatorSensor::Methods[] = {
        {NULL,NULL} //Sentinel
 };
 
+PyAttributeDef SCA_ActuatorSensor::Attributes[] = {
+       KX_PYATTRIBUTE_STRING_RW_CHECK("actuator",0,100,false,SCA_ActuatorSensor,m_checkactname,CheckActuator),
+       { NULL }        //Sentinel
+};
+
 PyObject* SCA_ActuatorSensor::_getattr(const STR_String& attr) {
-       if (attr == "actuator") {
-               return PyString_FromString(m_checkactname);
-       }
+       PyObject* object = _getattr_self(Attributes, this, attr);
+       if (object != NULL)
+               return object;
        _getattr_up(SCA_ISensor); /* implicit return! */
 }
 
-int SCA_ActuatorSensor::_setattr(const STR_String& attr, PyObject *value) {
-       if (PyString_Check(value)) {
-               char* sval = PyString_AsString(value);
-               if (attr == "actuator") {
-                       SCA_IActuator* act = GetParent()->FindActuator(STR_String(sval));
-                       if (act) {
-                               m_checkactname = sval;
-                               m_actuator = act;
-                               return 0;
-                       }
-                       PyErr_SetString(PyExc_AttributeError, "string does not correspond to an actuator");
-                       return 1;
-               }
+int SCA_ActuatorSensor::CheckActuator(void *self, const PyAttributeDef*)
+{
+       SCA_ActuatorSensor* sensor = reinterpret_cast<SCA_ActuatorSensor*>(self);
+       SCA_IActuator* act = sensor->GetParent()->FindActuator(sensor->m_checkactname);
+       if (act) {
+               sensor->m_actuator = act;
+               return 0;
        }
+       PyErr_SetString(PyExc_AttributeError, "string does not correspond to an actuator");
+       return 1;
+}
+
+int SCA_ActuatorSensor::_setattr(const STR_String& attr, PyObject *value) {
+       int ret = _setattr_self(Attributes, this, attr, value);
+       if (ret >= 0)
+               return ret;
        return SCA_ISensor::_setattr(attr, value);
 }
 
index 4fe4700..3d64247 100644 (file)
@@ -69,6 +69,7 @@ public:
        /* 4. getProperty */
        KX_PYMETHOD_DOC_NOARGS(SCA_ActuatorSensor,GetActuator);
        
+       static int CheckActuator(void *self, const PyAttributeDef*);
 };
 
 #endif
index 362483b..4c97ca9 100644 (file)
@@ -171,43 +171,24 @@ PyMethodDef SCA_DelaySensor::Methods[] = {
        {NULL,NULL} //Sentinel
 };
 
+PyAttributeDef SCA_DelaySensor::Attributes[] = {
+       KX_PYATTRIBUTE_INT_RW("delay",0,100000,true,SCA_DelaySensor,m_delay),
+       KX_PYATTRIBUTE_INT_RW("duration",0,100000,true,SCA_DelaySensor,m_duration),
+       KX_PYATTRIBUTE_BOOL_RW("repeat",SCA_DelaySensor,m_repeat),
+       { NULL }        //Sentinel
+};
+
 PyObject* SCA_DelaySensor::_getattr(const STR_String& attr) {
-       if (attr == "delay") {
-               return PyInt_FromLong(m_delay);
-       }
-       if (attr == "duration") {
-               return PyInt_FromLong(m_duration);
-       }
-       if (attr == "repeat") {
-               return PyInt_FromLong(m_repeat);
-       }
+       PyObject* object = _getattr_self(Attributes, this, attr);
+       if (object != NULL)
+               return object;
        _getattr_up(SCA_ISensor);
 }
 
 int SCA_DelaySensor::_setattr(const STR_String& attr, PyObject *value) {
-       if (PyInt_Check(value)) {
-               int ival = PyInt_AsLong(value);
-               if (attr == "delay") {
-                       if (ival < 0) {
-                               PyErr_SetString(PyExc_ValueError, "Delay cannot be negative");
-                               return 1;
-                       }
-                       m_delay = ival;
-                       return 0;
-               }
-               if (attr == "duration") {
-                       if (ival < 0) {
-                               PyErr_SetString(PyExc_ValueError, "Duration cannot be negative");
-                               return 1;
-                       }
-                       m_duration = ival;
-                       return 0;
-               }
-               if (attr == "repeat") {
-                       m_repeat = (ival != 0);
-                       return 0;
-               }
-       }
+       int ret = _setattr_self(Attributes, this, attr, value);
+       if (ret >= 0)
+               return ret;
        return SCA_ISensor::_setattr(attr, value);
 }
 
index 515b485..3765848 100644 (file)
@@ -252,7 +252,23 @@ PyMethodDef SCA_ILogicBrick::Methods[] = {
   {NULL,NULL} //Sentinel
 };
 
-
+int SCA_ILogicBrick::CheckProperty(void *self, const PyAttributeDef *attrdef)
+{
+       if (attrdef->m_type != KX_PYATTRIBUTE_TYPE_STRING || attrdef->m_length != 1) {
+               PyErr_SetString(PyExc_AttributeError, "inconsistent check function for attribute type, report to blender.org");
+               return 1;
+       }
+       SCA_ILogicBrick* brick = reinterpret_cast<SCA_ILogicBrick*>(self);
+       STR_String* var = reinterpret_cast<STR_String*>((char*)self+attrdef->m_offset);
+       CValue* prop = brick->GetParent()->FindIdentifier(*var);
+       bool error = prop->IsError();
+       prop->Release();
+       if (error) {
+               PyErr_SetString(PyExc_ValueError, "string does not correspond to a property");
+               return 1;
+       }
+       return 0;
+}
 
 PyObject*
 SCA_ILogicBrick::_getattr(const STR_String& attr)
index c28711a..38ec80d 100644 (file)
@@ -89,6 +89,9 @@ public:
        KX_PYMETHOD(SCA_ILogicBrick,SetExecutePriority);
        KX_PYMETHOD_NOARGS(SCA_ILogicBrick,GetExecutePriority);
 
+       // check that attribute is a property
+       static int CheckProperty(void *self, const PyAttributeDef *attrdef);
+
        enum KX_BOOL_TYPE {
                KX_BOOL_NODEF = 0,
                KX_TRUE,
index 6e46c85..68a3a93 100644 (file)
@@ -448,19 +448,24 @@ PyMethodDef SCA_ISensor::Methods[] = {
        {NULL,NULL} //Sentinel
 };
 
+PyAttributeDef SCA_ISensor::Attributes[] = {
+       KX_PYATTRIBUTE_BOOL_RW("usePosPulseMode",SCA_ISensor,m_pos_pulsemode),
+       KX_PYATTRIBUTE_BOOL_RW("useNegPulseMode",SCA_ISensor,m_neg_pulsemode),
+       KX_PYATTRIBUTE_INT_RW("frequency",0,100000,true,SCA_ISensor,m_pulse_frequency),
+       KX_PYATTRIBUTE_BOOL_RW("invert",SCA_ISensor,m_invert),
+       KX_PYATTRIBUTE_BOOL_RW("level",SCA_ISensor,m_level),
+       // make these properties read-only in _setaddr, must still implement them in _getattr
+       KX_PYATTRIBUTE_DUMMY("triggered"),
+       KX_PYATTRIBUTE_DUMMY("positive"),
+       { NULL }        //Sentinel
+};
+
 PyObject*
 SCA_ISensor::_getattr(const STR_String& attr)
 {
-       if (attr == "usePosPulseMode")
-               return PyInt_FromLong(m_pos_pulsemode);
-       if (attr == "useNegPulseMode")
-               return PyInt_FromLong(m_neg_pulsemode);
-       if (attr == "frequency")
-               return PyInt_FromLong(m_pulse_frequency);
-       if (attr == "invert")
-               return PyInt_FromLong(m_invert);
-       if (attr == "level")
-               return PyInt_FromLong(m_level);
+       PyObject* object = _getattr_self(Attributes, this, attr);
+       if (object != NULL)
+               return object;
        if (attr == "triggered")
        {
                int retval = 0;
@@ -473,54 +478,14 @@ SCA_ISensor::_getattr(const STR_String& attr)
                int retval = IsPositiveTrigger();
                return PyInt_FromLong(retval);
        }
-
-  _getattr_up(SCA_ILogicBrick);
+       _getattr_up(SCA_ILogicBrick);
 }
 
 int SCA_ISensor::_setattr(const STR_String& attr, PyObject *value)
 {
-       if (attr == "triggered")
-               PyErr_SetString(PyExc_AttributeError, "attribute \"triggered\" is read only");
-       if (attr == "positive")
-               PyErr_SetString(PyExc_AttributeError, "attribute \"positive\" is read only");
-
-       if (PyInt_Check(value))
-       {
-               int val = PyInt_AsLong(value);
-
-               if (attr == "usePosPulseMode")
-               {
-                       m_pos_pulsemode = (val != 0);
-                       return 0;
-               }
-
-               if (attr == "useNegPulseMode")
-               {
-                       m_neg_pulsemode = (val != 0);
-                       return 0;
-               }
-
-               if (attr == "invert")
-               {
-                       m_invert = (val != 0);
-                       return 0;
-               }
-
-               if (attr == "level")
-               {
-                       m_level = (val != 0);
-                       return 0;
-               }
-
-               if (attr == "frequency")
-               {
-                       if (val < 0)
-                               val = 0;
-                       m_pulse_frequency = val;
-                       return 0;
-               }
-       }
-
+       int ret = _setattr_self(Attributes, this, attr, value);
+       if (ret >= 0)
+               return ret;
        return SCA_ILogicBrick::_setattr(attr, value);
 }
 /* eof */
index ce90584..694bca9 100644 (file)
@@ -328,11 +328,11 @@ PyMethodDef SCA_JoystickSensor::Methods[] = {
 };
 
 PyAttributeDef SCA_JoystickSensor::Attributes[] = {
-       KX_PYATTRIBUTE_SHORT_RW("index",0,JOYINDEX_MAX-1,SCA_JoystickSensor,m_joyindex),
-       KX_PYATTRIBUTE_INT_RW("threshold",0,32768,SCA_JoystickSensor,m_precision),
-       KX_PYATTRIBUTE_INT_RW("button",0,100,SCA_JoystickSensor,m_button),
-       KX_PYATTRIBUTE_INT_ARRAY_RW_CHECK("axis",0,3,SCA_JoystickSensor,m_axis,2,CheckAxis),
-       KX_PYATTRIBUTE_INT_ARRAY_RW_CHECK("hat",0,12,SCA_JoystickSensor,m_hat,2,CheckHat),
+       KX_PYATTRIBUTE_SHORT_RW("index",0,JOYINDEX_MAX-1,true,SCA_JoystickSensor,m_joyindex),
+       KX_PYATTRIBUTE_INT_RW("threshold",0,32768,true,SCA_JoystickSensor,m_precision),
+       KX_PYATTRIBUTE_INT_RW("button",0,100,false,SCA_JoystickSensor,m_button),
+       KX_PYATTRIBUTE_INT_ARRAY_RW_CHECK("axis",0,3,true,SCA_JoystickSensor,m_axis,2,CheckAxis),
+       KX_PYATTRIBUTE_INT_ARRAY_RW_CHECK("hat",0,12,true,SCA_JoystickSensor,m_hat,2,CheckHat),
        // dummy attributes will just be read-only in _setattr
        // you still need to defined them in _getattr
        KX_PYATTRIBUTE_DUMMY("axisPosition"),
index 25103b3..fa11f1c 100644 (file)
@@ -149,24 +149,22 @@ public:
        KX_PYMETHOD_DOC_NOARGS(SCA_JoystickSensor,Connected);
 
        /* attribute check */
-       static int CheckAxis(void *self)
+       static int CheckAxis(void *self, const PyAttributeDef*)
        {
                SCA_JoystickSensor* sensor = reinterpret_cast<SCA_JoystickSensor*>(self);
-               if (sensor->m_axis < 1 || sensor->m_axis > 2)
-               {
-                       PyErr_SetString(PyExc_ValueError, "axis number must be 1 or 2");
-                       return 1;
-               }
+               if (sensor->m_axis < 1)
+                       sensor->m_axis = 1;
+               else if (sensor->m_axis > 2)
+                       sensor->m_axis = 2;
                return 0;
        }
-       static int CheckHat(void *self)
+       static int CheckHat(void *self, const PyAttributeDef*)
        {
                SCA_JoystickSensor* sensor = reinterpret_cast<SCA_JoystickSensor*>(self);
-               if (sensor->m_hat < 1 || sensor->m_hat > 2)
-               {
-                       PyErr_SetString(PyExc_ValueError, "hat number must be 1 or 2");
-                       return 1;
-               }
+               if (sensor->m_hat < 1)
+                       sensor->m_hat = 1;
+               else if (sensor->m_hat > 2)
+                       sensor->m_hat = 2;
                return 0;
        }
        
index a05f9ae..981d165 100644 (file)
@@ -822,11 +822,11 @@ PyMethodDef SCA_KeyboardSensor::Methods[] = {
 
 PyAttributeDef SCA_KeyboardSensor::Attributes[] = {
        KX_PYATTRIBUTE_BOOL_RW("useAllKeys",SCA_KeyboardSensor,m_bAllKeys),
-       KX_PYATTRIBUTE_INT_RW("key",0,1000,SCA_KeyboardSensor,m_hotkey),
-       KX_PYATTRIBUTE_SHORT_RW("hold1",0,1000,SCA_KeyboardSensor,m_qual),
-       KX_PYATTRIBUTE_SHORT_RW("hold2",0,1000,SCA_KeyboardSensor,m_qual2),
-       KX_PYATTRIBUTE_STRING_RW("toggleProperty",0,100,SCA_KeyboardSensor,m_toggleprop),
-       KX_PYATTRIBUTE_STRING_RW("targetProperty",0,100,SCA_KeyboardSensor,m_targetprop),
+       KX_PYATTRIBUTE_INT_RW("key",0,SCA_IInputDevice::KX_ENDKEY,true,SCA_KeyboardSensor,m_hotkey),
+       KX_PYATTRIBUTE_SHORT_RW("hold1",0,SCA_IInputDevice::KX_ENDKEY,true,SCA_KeyboardSensor,m_qual),
+       KX_PYATTRIBUTE_SHORT_RW("hold2",0,SCA_IInputDevice::KX_ENDKEY,true,SCA_KeyboardSensor,m_qual2),
+       KX_PYATTRIBUTE_STRING_RW("toggleProperty",0,100,false,SCA_KeyboardSensor,m_toggleprop),
+       KX_PYATTRIBUTE_STRING_RW("targetProperty",0,100,false,SCA_KeyboardSensor,m_targetprop),
        { NULL }        //Sentinel
 };
 
index 14d9c89..09b46e6 100644 (file)
@@ -59,7 +59,7 @@ SCA_MouseSensor::SCA_MouseSensor(SCA_MouseManager* eventmgr,
        m_mousemode   = mousemode;
        m_triggermode = true;
 
-       UpdateHotkey(this);
+       UpdateHotkey(this, NULL);
        Init();
 }
 
@@ -74,7 +74,7 @@ SCA_MouseSensor::~SCA_MouseSensor()
     /* Nothing to be done here. */
 }
 
-int SCA_MouseSensor::UpdateHotkey(void *self)
+int SCA_MouseSensor::UpdateHotkey(void *self, const PyAttributeDef*)
 {
        // gosh, this function is so damn stupid
        // its here because of a design mistake in the mouse sensor, it should only
@@ -336,7 +336,7 @@ PyMethodDef SCA_MouseSensor::Methods[] = {
 };
 
 PyAttributeDef SCA_MouseSensor::Attributes[] = {
-       KX_PYATTRIBUTE_SHORT_RW_CHECK("mode",KX_MOUSESENSORMODE_NODEF,KX_MOUSESENSORMODE_MAX-1,SCA_MouseSensor,m_mousemode,UpdateHotkey),
+       KX_PYATTRIBUTE_SHORT_RW_CHECK("mode",KX_MOUSESENSORMODE_NODEF,KX_MOUSESENSORMODE_MAX-1,true,SCA_MouseSensor,m_mousemode,UpdateHotkey),
        KX_PYATTRIBUTE_SHORT_ARRAY_RO("position",SCA_MouseSensor,m_x,2),
        { NULL }        //Sentinel
 };
index 58ee96c..82af2ce 100644 (file)
@@ -87,7 +87,7 @@ class SCA_MouseSensor : public SCA_ISensor
 
        bool isValid(KX_MOUSESENSORMODE);
        
-       static int UpdateHotkey(void *self);
+       static int UpdateHotkey(void *self, const PyAttributeDef*);
        
        SCA_MouseSensor(class SCA_MouseManager* keybdmgr,
                                        int startx,int starty,
index f8509fe..566d3b6 100644 (file)
@@ -254,36 +254,23 @@ PyMethodDef SCA_PropertyActuator::Methods[] = {
        {NULL,NULL} //Sentinel
 };
 
+PyAttributeDef SCA_PropertyActuator::Attributes[] = {
+       KX_PYATTRIBUTE_STRING_RW_CHECK("property",0,100,false,SCA_PropertyActuator,m_propname,CheckProperty),
+       KX_PYATTRIBUTE_STRING_RW("value",0,100,false,SCA_PropertyActuator,m_exprtxt),
+       { NULL }        //Sentinel
+};
+
 PyObject* SCA_PropertyActuator::_getattr(const STR_String& attr) {
-       if (attr == "property") {
-               return PyString_FromString(m_propname);
-       }
-       if (attr == "value") {
-               return PyString_FromString(m_exprtxt);
-       }
+       PyObject* object = _getattr_self(Attributes, this, attr);
+       if (object != NULL)
+               return object;
        _getattr_up(SCA_IActuator);
 }
 
 int SCA_PropertyActuator::_setattr(const STR_String& attr, PyObject *value) {
-       if (PyString_Check(value)) {
-               char* sval = PyString_AsString(value);
-               if (attr == "property") {
-                       CValue* prop = GetParent()->FindIdentifier(sval);
-                       bool error = prop->IsError();
-                       prop->Release();
-                       if (!prop->IsError()) {
-                               m_propname = sval;
-                               return 0;
-                       } else {
-                               PyErr_SetString(PyExc_ValueError, "string does not correspond to a property");
-                               return 1;
-                       }
-               } 
-               if (attr == "value") {
-                       m_exprtxt = sval;
-                       return 0;
-               }
-       }
+       int ret = _setattr_self(Attributes, this, attr, value);
+       if (ret >= 0)
+               return ret;
        return SCA_IActuator::_setattr(attr, value);
 }
 
index 19ee852..a6f7a9c 100644 (file)
@@ -293,7 +293,7 @@ CValue* SCA_PropertySensor::FindIdentifier(const STR_String& identifiername)
        return  GetParent()->FindIdentifier(identifiername);
 }
 
-bool SCA_PropertySensor::validValueForProperty(char *val, STR_String &prop)
+int SCA_PropertySensor::validValueForProperty(void *self, const PyAttributeDef*)
 {
        bool result = true;
        /*  There is no type checking at this moment, unfortunately...           */
@@ -344,51 +344,25 @@ PyMethodDef SCA_PropertySensor::Methods[] = {
        {NULL,NULL} //Sentinel
 };
 
+PyAttributeDef SCA_PropertySensor::Attributes[] = {
+       KX_PYATTRIBUTE_INT_RW("type",KX_PROPSENSOR_NODEF,KX_PROPSENSOR_MAX-1,false,SCA_PropertySensor,m_checktype),
+       KX_PYATTRIBUTE_STRING_RW_CHECK("property",0,100,false,SCA_PropertySensor,m_checkpropname,CheckProperty),
+       KX_PYATTRIBUTE_STRING_RW_CHECK("value",0,100,false,SCA_PropertySensor,m_checkpropval,validValueForProperty),
+       { NULL }        //Sentinel
+};
+
+
 PyObject* SCA_PropertySensor::_getattr(const STR_String& attr) {
-       if (attr == "type") {
-               return PyInt_FromLong(m_checktype);
-       }
-       if (attr == "property") {
-               return PyString_FromString(m_checkpropname);
-       }
-       if (attr == "value") {
-               return PyString_FromString(m_checkpropval);
-       }
+       PyObject* object = _getattr_self(Attributes, this, attr);
+       if (object != NULL)
+               return object;
        _getattr_up(SCA_ISensor); /* implicit return! */
 }
 
 int SCA_PropertySensor::_setattr(const STR_String& attr, PyObject *value) {
-       if (PyInt_Check(value)) {
-               int ival = PyInt_AsLong(value);
-               if (attr == "type") {
-                       if ((ival <= KX_PROPSENSOR_NODEF) || (ival >= KX_PROPSENSOR_MAX)) {
-                               PyErr_SetString(PyExc_ValueError, "type out of range");
-                               return 1;
-                       }
-                       m_checktype =  ival;
-               }
-               return 0;
-       }
-       if (PyString_Check(value)) {
-               char* sval = PyString_AsString(value);
-               if (attr == "property") {
-                       CValue *prop = FindIdentifier(STR_String(sval));
-                       bool error = prop->IsError();
-                       prop->Release();
-                       if (error) {
-                               PyErr_SetString(PyExc_ValueError, "string does not correspond to a property");
-                               return 1;
-                       }
-                       m_checkpropname = sval;
-               } else if (attr == "value") {
-                       if (!validValueForProperty(sval, m_checkpropname)) {
-                               PyErr_SetString(PyExc_ValueError, "string does not represent a suitable value for the property");
-                               return 1;
-                       }
-                       m_checkpropval = sval;
-               }       
-               return 0;
-       }
+       int ret = _setattr_self(Attributes, this, attr, value);
+       if (ret >= 0)
+               return ret;
        return SCA_ISensor::_setattr(attr, value);
 }
 
@@ -490,11 +464,12 @@ PyObject* SCA_PropertySensor::PySetValue(PyObject* self, PyObject* args, PyObjec
        if(!PyArg_ParseTuple(args, "s", &propValArg)) {
                return NULL;
        }
-
-       if (validValueForProperty(propValArg, m_checkpropname)) {
-               m_checkpropval = propValArg;
+       STR_String oldval = m_checkpropval;
+       m_checkpropval = propValArg;
+       if (validValueForProperty(self, NULL)) {
+               m_checkpropval = oldval;
+               return NULL;
        }       
-
        Py_Return;
 }
 
index 81203c7..e625e84 100644 (file)
@@ -47,10 +47,6 @@ class SCA_PropertySensor : public SCA_ISensor
        bool                    m_recentresult;
        CExpression*    m_range_expr;
 
-       /**
-        * Test whether this is a sensible value (type check)
-        */
-       bool validValueForProperty(char *val, STR_String &prop);
  protected:
 
 public:
@@ -104,7 +100,10 @@ public:
        KX_PYMETHOD_DOC(SCA_PropertySensor,GetValue);
        /* 6. setValue */
        KX_PYMETHOD_DOC(SCA_PropertySensor,SetValue);
-       
+       /**
+        * Test whether this is a sensible value (type check)
+        */
+       static int validValueForProperty(void* self, const PyAttributeDef*);
 };
 
 #endif
index 8dd405c..840b95d 100644 (file)
@@ -361,57 +361,38 @@ PyMethodDef SCA_RandomActuator::Methods[] = {
        {NULL,NULL} //Sentinel
 };
 
+PyAttributeDef SCA_RandomActuator::Attributes[] = {
+       KX_PYATTRIBUTE_FLOAT_RO("para1",SCA_RandomActuator,m_parameter1),
+       KX_PYATTRIBUTE_FLOAT_RO("para2",SCA_RandomActuator,m_parameter2),
+       KX_PYATTRIBUTE_ENUM_RO("distribution",SCA_RandomActuator,m_distribution),
+       KX_PYATTRIBUTE_STRING_RW_CHECK("property",0,100,false,SCA_RandomActuator,m_propname,CheckProperty),
+       { NULL }        //Sentinel
+};     
+
 PyObject* SCA_RandomActuator::_getattr(const STR_String& attr) {
+       PyObject* object = _getattr_self(Attributes, this, attr);
+       if (object != NULL)
+               return object;
        if (attr == "seed") {
                return PyInt_FromLong(m_base->GetSeed());
        }
-       if (attr == "para1") {
-               return PyFloat_FromDouble(m_parameter1);
-       }
-       if (attr == "para2") {
-               return PyFloat_FromDouble(m_parameter2);
-       }
-       if (attr == "distribution") {
-               return PyInt_FromLong(m_distribution);
-       }
-       if (attr == "property") {
-               return PyString_FromString(m_propname);
-       }
        _getattr_up(SCA_IActuator);
 }
 
 int SCA_RandomActuator::_setattr(const STR_String& attr, PyObject *value)
 {
-       if (attr == "para1") {
-               PyErr_SetString(PyExc_AttributeError, "para1 is read only");
-       }
-       if (attr == "para2") {
-               PyErr_SetString(PyExc_AttributeError, "para2 is read only");
-       }
-       if (attr == "distribution") {
-               PyErr_SetString(PyExc_AttributeError, "distribution is read only");
-       }
-       if (PyInt_Check(value)) {
-               int ival = PyInt_AsLong(value);
-               if (attr == "seed") {
+       int ret = _setattr_self(Attributes, this, attr, value);
+       if (ret >= 0)
+               return ret;
+       if (attr == "seed") {
+               if (PyInt_Check(value)) {
+                       int ival = PyInt_AsLong(value);
                        m_base->SetSeed(ival);
+                       return 0;
+               } else {
+                       PyErr_SetString(PyExc_TypeError, "expected an integer");
+                       return 1;
                }
-               return 0;
-       }
-       if (PyString_Check(value)) {
-               char* sval = PyString_AsString(value);
-               if (attr == "property") {
-                       CValue* prop = GetParent()->FindIdentifier(sval);
-                       bool error = prop->IsError();
-                       prop->Release();
-                       if (!prop->IsError()) {
-                               m_propname = sval;
-                               return 0;
-                       } else {
-                               PyErr_SetString(PyExc_ValueError, "string does not correspond to a property");
-                               return 1;
-                       }
-               } 
        }
        return SCA_IActuator::_setattr(attr, value);
 }
index 2fc14f9..de8faaf 100644 (file)
@@ -136,7 +136,6 @@ class SCA_RandomActuator : public SCA_IActuator
        KX_PYMETHOD_DOC(SCA_RandomActuator,SetFloatNormal);
        /* 20. setFloatNegativeExponential, */
        KX_PYMETHOD_DOC(SCA_RandomActuator,SetFloatNegativeExponential);
-       
 }; /* end of class KX_EditObjectActuator : public SCA_PropertyActuator */
 
 #endif