BGE Python API
[blender.git] / source / gameengine / GameLogic / SCA_PropertySensor.cpp
1 /**
2  * Property sensor
3  *
4  * $Id$
5  *
6  * ***** BEGIN GPL LICENSE BLOCK *****
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  *
22  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL LICENSE BLOCK *****
30  */
31
32 #include <iostream>
33 #include "SCA_PropertySensor.h"
34 #include "Operator2Expr.h"
35 #include "ConstExpr.h"
36 #include "InputParser.h"
37 #include "StringValue.h"
38 #include "SCA_EventManager.h"
39 #include "SCA_LogicManager.h"
40
41 #ifdef HAVE_CONFIG_H
42 #include <config.h>
43 #endif
44
45 SCA_PropertySensor::SCA_PropertySensor(SCA_EventManager* eventmgr,
46                                                                          SCA_IObject* gameobj,
47                                                                          const STR_String& propname,
48                                                                          const STR_String& propval,
49                                                                          const STR_String& propmaxval,
50                                                                          KX_PROPSENSOR_TYPE checktype,
51                                                                          PyTypeObject* T )
52         : SCA_ISensor(gameobj,eventmgr,T),
53           m_checktype(checktype),
54           m_checkpropval(propval),
55           m_checkpropmaxval(propmaxval),
56           m_checkpropname(propname),
57           m_range_expr(NULL)
58 {
59         //CParser pars;
60         //pars.SetContext(this->AddRef());
61         //CValue* resultval = m_rightexpr->Calculate();
62
63         CValue* orgprop = GetParent()->FindIdentifier(m_checkpropname);
64         if (!orgprop->IsError())
65         {
66                 m_previoustext = orgprop->GetText();
67         }
68         orgprop->Release();
69
70         if (m_checktype==KX_PROPSENSOR_INTERVAL)
71         {
72                 PrecalculateRangeExpression();
73         }
74         Init();
75 }
76
77 void SCA_PropertySensor::Init()
78 {
79         m_recentresult = false;
80         m_lastresult = m_invert?true:false;
81         m_reset = true;
82 }
83
84 void SCA_PropertySensor::PrecalculateRangeExpression()
85 {
86                 CParser pars;
87                 //The context is needed to retrieve the property at runtime but it creates
88                 //loop of references
89                 pars.SetContext(this->AddRef());
90                 STR_String checkstr = "(" + m_checkpropval + " <= " 
91                                                         + m_checkpropname + ") && ( " 
92                                                         + m_checkpropname + " <= " 
93                                                         + m_checkpropmaxval + ")";
94
95                 m_range_expr = pars.ProcessText(checkstr);
96 }
97
98 // Forced deletion of precalculated range expression to break reference loop
99 // Use this function when you know that you won't use the sensor anymore
100 void SCA_PropertySensor::Delete()
101 {
102         if (m_range_expr)
103         {
104                 m_range_expr->Release();
105                 m_range_expr = NULL;
106         }
107         Release();
108 }
109
110 CValue* SCA_PropertySensor::GetReplica()
111 {
112         SCA_PropertySensor* replica = new SCA_PropertySensor(*this);
113         // m_range_expr must be recalculated on replica!
114         CValue::AddDataToReplica(replica);
115         replica->Init();
116
117         replica->m_range_expr = NULL;
118         if (replica->m_checktype==KX_PROPSENSOR_INTERVAL)
119         {
120                 replica->PrecalculateRangeExpression();
121         }
122         
123         
124         return replica;
125 }
126
127
128
129 bool SCA_PropertySensor::IsPositiveTrigger()
130 {
131         bool result = m_recentresult;//CheckPropertyCondition();
132         if (m_invert)
133                 result = !result;
134
135         return result;
136 }
137
138
139
140 SCA_PropertySensor::~SCA_PropertySensor()
141 {
142         //if (m_rightexpr)
143         //      m_rightexpr->Release();
144
145         if (m_range_expr)
146         {
147                 m_range_expr->Release();
148                 m_range_expr=NULL;
149         }
150
151 }
152
153
154
155 bool SCA_PropertySensor::Evaluate(CValue* event)
156 {
157         bool result = CheckPropertyCondition();
158         bool reset = m_reset && m_level;
159         
160         m_reset = false;
161         if (m_lastresult!=result)
162         {
163                 m_lastresult = result;
164                 return true;
165         }
166         return (reset) ? true : false;
167 }
168
169
170 bool    SCA_PropertySensor::CheckPropertyCondition()
171 {
172
173         m_recentresult=false;
174         bool result=false;
175         bool reverse = false;
176         switch (m_checktype)
177         {
178         case KX_PROPSENSOR_NOTEQUAL:
179                 reverse = true;
180         case KX_PROPSENSOR_EQUAL:
181                 {
182                         CValue* orgprop = GetParent()->FindIdentifier(m_checkpropname);
183                         if (!orgprop->IsError())
184                         {
185                                 STR_String testprop = orgprop->GetText();
186                                 // Force strings to upper case, to avoid confusion in
187                                 // bool tests. It's stupid the prop's identity is lost
188                                 // on the way here...
189                                 if ((testprop == "TRUE") || (testprop == "FALSE")) {
190                                         STR_String checkprop = m_checkpropval;
191                                         checkprop.Upper();
192                                         result = (testprop == checkprop);
193                                 } else {
194                                         result = (orgprop->GetText() == m_checkpropval);
195                                 }
196                         }
197                         orgprop->Release();
198
199                         if (reverse)
200                                 result = !result;
201                         break;
202
203                 }
204
205         case KX_PROPSENSOR_EXPRESSION:
206                 {
207                         /*
208                         if (m_rightexpr)
209                         {
210                                 CValue* resultval = m_rightexpr->Calculate();
211                                 if (resultval->IsError())
212                                 {
213                                         int i=0;
214                                         STR_String errortest = resultval->GetText();
215                                         printf(errortest);
216
217                                 } else
218                                 {
219                                         result = resultval->GetNumber() != 0;
220                                 }
221                         }
222                         */
223                         break;
224                 }
225         case KX_PROPSENSOR_INTERVAL:
226                 {
227                         //CValue* orgprop = GetParent()->FindIdentifier(m_checkpropname);
228                         //if (orgprop)
229                         //{
230                                 if (m_range_expr)
231                                 {
232                                         CValue* vallie = m_range_expr->Calculate();
233                                         if (vallie)
234                                         {
235                                                 STR_String errtext = vallie->GetText();
236                                                 if (errtext == "TRUE")
237                                                 {
238                                                         result = true;
239                                                 } else
240                                                 {
241                                                         if (vallie->IsError())
242                                                         {
243                                                                 //printf (errtext.ReadPtr());
244                                                         } 
245                                                 }
246                                                 
247                                                 vallie->Release();
248                                         }
249                                 }
250
251                                 
252                         //}
253                         
254                 //cout << " \nSens:Prop:interval!"; /* need implementation here!!! */
255
256                 break;
257                 }
258         case KX_PROPSENSOR_CHANGED:
259                 {
260                         CValue* orgprop = GetParent()->FindIdentifier(m_checkpropname);
261                                 
262                         if (!orgprop->IsError())
263                         {
264                                 if (m_previoustext != orgprop->GetText())
265                                 {
266                                         m_previoustext = orgprop->GetText();
267                                         result = true;
268                                 }
269                         }
270                         orgprop->Release();
271
272                         //cout << " \nSens:Prop:changed!"; /* need implementation here!!! */
273                         break;
274                 }
275         default:
276                 ; /* error */
277         }
278
279         //the concept of Edge and Level triggering has unwanted effect for KX_PROPSENSOR_CHANGED
280         //see Game Engine bugtracker [ #3809 ]
281         if (m_checktype != KX_PROPSENSOR_CHANGED)
282         {
283                 m_recentresult=result;
284         } else
285         {
286                 m_recentresult=result;//true;
287         }
288         return result;
289 }
290
291 CValue* SCA_PropertySensor::FindIdentifier(const STR_String& identifiername)
292 {
293         return  GetParent()->FindIdentifier(identifiername);
294 }
295
296 int SCA_PropertySensor::validValueForProperty(void *self, const PyAttributeDef*)
297 {
298         bool result = true;
299         /*  There is no type checking at this moment, unfortunately...           */
300         return result;
301 }
302
303 /* ------------------------------------------------------------------------- */
304 /* Python functions                                                          */
305 /* ------------------------------------------------------------------------- */
306
307 /* Integration hooks ------------------------------------------------------- */
308 PyTypeObject SCA_PropertySensor::Type = {
309         PyObject_HEAD_INIT(NULL)
310         0,
311         "SCA_PropertySensor",
312         sizeof(PyObjectPlus_Proxy),
313         0,
314         py_base_dealloc,
315         0,
316         0,
317         0,
318         0,
319         py_base_repr,
320         0,0,0,0,0,0,
321         py_base_getattro,
322         py_base_setattro,
323         0,0,0,0,0,0,0,0,0,
324         Methods
325 };
326
327 PyParentObject SCA_PropertySensor::Parents[] = {
328         &SCA_PropertySensor::Type,
329         &SCA_ISensor::Type,
330         &SCA_ILogicBrick::Type,
331         &CValue::Type,
332         NULL
333 };
334
335 PyMethodDef SCA_PropertySensor::Methods[] = {
336         //Deprecated functions ------>
337         {"getType", (PyCFunction) SCA_PropertySensor::sPyGetType, METH_NOARGS, (PY_METHODCHAR)GetType_doc},
338         {"setType", (PyCFunction) SCA_PropertySensor::sPySetType, METH_VARARGS, (PY_METHODCHAR)SetType_doc},
339         {"getProperty", (PyCFunction) SCA_PropertySensor::sPyGetProperty, METH_NOARGS, (PY_METHODCHAR)GetProperty_doc},
340         {"setProperty", (PyCFunction) SCA_PropertySensor::sPySetProperty, METH_VARARGS, (PY_METHODCHAR)SetProperty_doc},
341         {"getValue", (PyCFunction) SCA_PropertySensor::sPyGetValue, METH_NOARGS, (PY_METHODCHAR)GetValue_doc},
342         {"setValue", (PyCFunction) SCA_PropertySensor::sPySetValue, METH_VARARGS, (PY_METHODCHAR)SetValue_doc},
343         //<----- Deprecated
344         {NULL,NULL} //Sentinel
345 };
346
347 PyAttributeDef SCA_PropertySensor::Attributes[] = {
348         KX_PYATTRIBUTE_INT_RW("type",KX_PROPSENSOR_NODEF,KX_PROPSENSOR_MAX-1,false,SCA_PropertySensor,m_checktype),
349         KX_PYATTRIBUTE_STRING_RW_CHECK("property",0,100,false,SCA_PropertySensor,m_checkpropname,CheckProperty),
350         KX_PYATTRIBUTE_STRING_RW_CHECK("value",0,100,false,SCA_PropertySensor,m_checkpropval,validValueForProperty),
351         { NULL }        //Sentinel
352 };
353
354
355 PyObject* SCA_PropertySensor::py_getattro(PyObject *attr) {
356         py_getattro_up(SCA_ISensor);
357 }
358
359 PyObject* SCA_PropertySensor::py_getattro_dict() {
360         py_getattro_dict_up(SCA_ISensor);
361 }
362
363 int SCA_PropertySensor::py_setattro(PyObject *attr, PyObject *value) {
364         py_setattro_up(SCA_ISensor);
365 }
366
367 /* 1. getType */
368 const char SCA_PropertySensor::GetType_doc[] = 
369 "getType()\n"
370 "\tReturns the type of check this sensor performs.\n";
371 PyObject* SCA_PropertySensor::PyGetType()
372 {
373         ShowDeprecationWarning("getType()", "the type property");
374         return PyInt_FromLong(m_checktype);
375 }
376
377 /* 2. setType */
378 const char SCA_PropertySensor::SetType_doc[] = 
379 "setType(type)\n"
380 "\t- type: KX_PROPSENSOR_EQUAL, KX_PROPSENSOR_NOTEQUAL,\n"
381 "\t        KX_PROPSENSOR_INTERVAL, KX_PROPSENSOR_CHANGED,\n"
382 "\t        or KX_PROPSENSOR_EXPRESSION.\n"
383 "\tSet the type of check to perform.\n";
384 PyObject* SCA_PropertySensor::PySetType(PyObject* args) 
385 {
386         ShowDeprecationWarning("setType()", "the type property");
387         int typeArg;
388         
389         if (!PyArg_ParseTuple(args, "i:setType", &typeArg)) {
390                 return NULL;
391         }
392         
393         if ( (typeArg > KX_PROPSENSOR_NODEF) 
394                  && (typeArg < KX_PROPSENSOR_MAX) ) {
395                 m_checktype =  typeArg;
396         }
397         
398         Py_RETURN_NONE;
399 }
400
401 /* 3. getProperty */
402 const char SCA_PropertySensor::GetProperty_doc[] = 
403 "getProperty()\n"
404 "\tReturn the property with which the sensor operates.\n";
405 PyObject* SCA_PropertySensor::PyGetProperty() 
406 {
407         ShowDeprecationWarning("getProperty()", "the 'property' property");
408         return PyString_FromString(m_checkpropname);
409 }
410
411 /* 4. setProperty */
412 const char SCA_PropertySensor::SetProperty_doc[] = 
413 "setProperty(name)\n"
414 "\t- name: string\n"
415 "\tSets the property with which to operate. If there is no property\n"
416 "\tof this name, the call is ignored.\n";
417 PyObject* SCA_PropertySensor::PySetProperty(PyObject* args) 
418 {
419         ShowDeprecationWarning("setProperty()", "the 'property' property");
420         /* We should query whether the name exists. Or should we create a prop   */
421         /* on the fly?                                                           */
422         char *propNameArg = NULL;
423
424         if (!PyArg_ParseTuple(args, "s:setProperty", &propNameArg)) {
425                 return NULL;
426         }
427
428         CValue *prop = FindIdentifier(STR_String(propNameArg));
429         if (!prop->IsError()) {
430                 m_checkpropname = propNameArg;
431         } else {
432                 ; /* error: bad property name */
433         }
434         prop->Release();
435         Py_RETURN_NONE;
436 }
437
438 /* 5. getValue */
439 const char SCA_PropertySensor::GetValue_doc[] = 
440 "getValue()\n"
441 "\tReturns the value with which the sensor operates.\n";
442 PyObject* SCA_PropertySensor::PyGetValue() 
443 {
444         ShowDeprecationWarning("getValue()", "the value property");
445         return PyString_FromString(m_checkpropval);
446 }
447
448 /* 6. setValue */
449 const char SCA_PropertySensor::SetValue_doc[] = 
450 "setValue(value)\n"
451 "\t- value: string\n"
452 "\tSet the value with which the sensor operates. If the value\n"
453 "\tis not compatible with the type of the property, the subsequent\n"
454 "\t action is ignored.\n";
455 PyObject* SCA_PropertySensor::PySetValue(PyObject* args) 
456 {
457         ShowDeprecationWarning("setValue()", "the value property");
458         /* Here, we need to check whether the value is 'valid' for this property.*/
459         /* We know that the property exists, or is NULL.                         */
460         char *propValArg = NULL;
461
462         if(!PyArg_ParseTuple(args, "s:setValue", &propValArg)) {
463                 return NULL;
464         }
465         STR_String oldval = m_checkpropval;
466         m_checkpropval = propValArg;
467         if (validValueForProperty(m_proxy, NULL)) {
468                 m_checkpropval = oldval;
469                 return NULL;
470         }       
471         Py_RETURN_NONE;
472 }
473
474 /* eof */