Merge with 2.5 -r 21003:21788.
[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 #include "BoolValue.h"
41
42 #ifdef HAVE_CONFIG_H
43 #include <config.h>
44 #endif
45
46 SCA_PropertySensor::SCA_PropertySensor(SCA_EventManager* eventmgr,
47                                                                          SCA_IObject* gameobj,
48                                                                          const STR_String& propname,
49                                                                          const STR_String& propval,
50                                                                          const STR_String& propmaxval,
51                                                                          KX_PROPSENSOR_TYPE checktype)
52         : SCA_ISensor(gameobj,eventmgr),
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         replica->ProcessReplica();
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()
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                                 const 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 == &CBoolValue::sTrueString) || (&testprop == &CBoolValue::sFalseString)) {
190                                         m_checkpropval.Upper();
191                                 }
192                                 result = (testprop == m_checkpropval);
193                         }
194                         orgprop->Release();
195
196                         if (reverse)
197                                 result = !result;
198                         break;
199
200                 }
201
202         case KX_PROPSENSOR_EXPRESSION:
203                 {
204                         /*
205                         if (m_rightexpr)
206                         {
207                                 CValue* resultval = m_rightexpr->Calculate();
208                                 if (resultval->IsError())
209                                 {
210                                         int i=0;
211                                         STR_String errortest = resultval->GetText();
212                                         printf(errortest);
213
214                                 } else
215                                 {
216                                         result = resultval->GetNumber() != 0;
217                                 }
218                         }
219                         */
220                         break;
221                 }
222         case KX_PROPSENSOR_INTERVAL:
223                 {
224                         //CValue* orgprop = GetParent()->FindIdentifier(m_checkpropname);
225                         //if (orgprop)
226                         //{
227                                 if (m_range_expr)
228                                 {
229                                         CValue* vallie = m_range_expr->Calculate();
230                                         if (vallie)
231                                         {
232                                                 const STR_String& errtext = vallie->GetText();
233                                                 if (&errtext == &CBoolValue::sTrueString)
234                                                 {
235                                                         result = true;
236                                                 } else
237                                                 {
238                                                         if (vallie->IsError())
239                                                         {
240                                                                 //printf (errtext.ReadPtr());
241                                                         } 
242                                                 }
243                                                 
244                                                 vallie->Release();
245                                         }
246                                 }
247
248                                 
249                         //}
250                         
251                 //cout << " \nSens:Prop:interval!"; /* need implementation here!!! */
252
253                 break;
254                 }
255         case KX_PROPSENSOR_CHANGED:
256                 {
257                         CValue* orgprop = GetParent()->FindIdentifier(m_checkpropname);
258                                 
259                         if (!orgprop->IsError())
260                         {
261                                 if (m_previoustext != orgprop->GetText())
262                                 {
263                                         m_previoustext = orgprop->GetText();
264                                         result = true;
265                                 }
266                         }
267                         orgprop->Release();
268
269                         //cout << " \nSens:Prop:changed!"; /* need implementation here!!! */
270                         break;
271                 }
272         default:
273                 ; /* error */
274         }
275
276         //the concept of Edge and Level triggering has unwanted effect for KX_PROPSENSOR_CHANGED
277         //see Game Engine bugtracker [ #3809 ]
278         if (m_checktype != KX_PROPSENSOR_CHANGED)
279         {
280                 m_recentresult=result;
281         } else
282         {
283                 m_recentresult=result;//true;
284         }
285         return result;
286 }
287
288 CValue* SCA_PropertySensor::FindIdentifier(const STR_String& identifiername)
289 {
290         return  GetParent()->FindIdentifier(identifiername);
291 }
292
293 int SCA_PropertySensor::validValueForProperty(void *self, const PyAttributeDef*)
294 {
295         /*  There is no type checking at this moment, unfortunately...           */
296         return 0;
297 }
298
299 /* ------------------------------------------------------------------------- */
300 /* Python functions                                                          */
301 /* ------------------------------------------------------------------------- */
302
303 /* Integration hooks ------------------------------------------------------- */
304 PyTypeObject SCA_PropertySensor::Type = {
305 #if (PY_VERSION_HEX >= 0x02060000)
306         PyVarObject_HEAD_INIT(NULL, 0)
307 #else
308         /* python 2.5 and below */
309         PyObject_HEAD_INIT( NULL )  /* required py macro */
310         0,                          /* ob_size */
311 #endif
312         "SCA_PropertySensor",
313         sizeof(PyObjectPlus_Proxy),
314         0,
315         py_base_dealloc,
316         0,
317         0,
318         0,
319         0,
320         py_base_repr,
321         0,0,0,0,0,0,0,0,0,
322         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
323         0,0,0,0,0,0,0,
324         Methods,
325         0,
326         0,
327         &SCA_ISensor::Type,
328         0,0,0,0,0,0,
329         py_base_new
330 };
331
332 PyMethodDef SCA_PropertySensor::Methods[] = {
333         //Deprecated functions ------>
334         {"getType", (PyCFunction) SCA_PropertySensor::sPyGetType, METH_NOARGS, (PY_METHODCHAR)GetType_doc},
335         {"setType", (PyCFunction) SCA_PropertySensor::sPySetType, METH_VARARGS, (PY_METHODCHAR)SetType_doc},
336         {"getProperty", (PyCFunction) SCA_PropertySensor::sPyGetProperty, METH_NOARGS, (PY_METHODCHAR)GetProperty_doc},
337         {"setProperty", (PyCFunction) SCA_PropertySensor::sPySetProperty, METH_VARARGS, (PY_METHODCHAR)SetProperty_doc},
338         {"getValue", (PyCFunction) SCA_PropertySensor::sPyGetValue, METH_NOARGS, (PY_METHODCHAR)GetValue_doc},
339         {"setValue", (PyCFunction) SCA_PropertySensor::sPySetValue, METH_VARARGS, (PY_METHODCHAR)SetValue_doc},
340         //<----- Deprecated
341         {NULL,NULL} //Sentinel
342 };
343
344 PyAttributeDef SCA_PropertySensor::Attributes[] = {
345         KX_PYATTRIBUTE_INT_RW("mode",KX_PROPSENSOR_NODEF,KX_PROPSENSOR_MAX-1,false,SCA_PropertySensor,m_checktype),
346         KX_PYATTRIBUTE_STRING_RW_CHECK("propName",0,100,false,SCA_PropertySensor,m_checkpropname,CheckProperty),
347         KX_PYATTRIBUTE_STRING_RW_CHECK("value",0,100,false,SCA_PropertySensor,m_checkpropval,validValueForProperty),
348         { NULL }        //Sentinel
349 };
350
351 /* 1. getType */
352 const char SCA_PropertySensor::GetType_doc[] = 
353 "getType()\n"
354 "\tReturns the type of check this sensor performs.\n";
355 PyObject* SCA_PropertySensor::PyGetType()
356 {
357         ShowDeprecationWarning("getType()", "the mode property");
358         return PyLong_FromSsize_t(m_checktype);
359 }
360
361 /* 2. setType */
362 const char SCA_PropertySensor::SetType_doc[] = 
363 "setType(type)\n"
364 "\t- type: KX_PROPSENSOR_EQUAL, KX_PROPSENSOR_NOTEQUAL,\n"
365 "\t        KX_PROPSENSOR_INTERVAL, KX_PROPSENSOR_CHANGED,\n"
366 "\t        or KX_PROPSENSOR_EXPRESSION.\n"
367 "\tSet the type of check to perform.\n";
368 PyObject* SCA_PropertySensor::PySetType(PyObject* args) 
369 {
370         ShowDeprecationWarning("setType()", "the mode property");
371         int typeArg;
372         
373         if (!PyArg_ParseTuple(args, "i:setType", &typeArg)) {
374                 return NULL;
375         }
376         
377         if ( (typeArg > KX_PROPSENSOR_NODEF) 
378                  && (typeArg < KX_PROPSENSOR_MAX) ) {
379                 m_checktype =  typeArg;
380         }
381         
382         Py_RETURN_NONE;
383 }
384
385 /* 3. getProperty */
386 const char SCA_PropertySensor::GetProperty_doc[] = 
387 "getProperty()\n"
388 "\tReturn the property with which the sensor operates.\n";
389 PyObject* SCA_PropertySensor::PyGetProperty() 
390 {
391         ShowDeprecationWarning("getProperty()", "the 'propName' property");
392         return PyUnicode_FromString(m_checkpropname);
393 }
394
395 /* 4. setProperty */
396 const char SCA_PropertySensor::SetProperty_doc[] = 
397 "setProperty(name)\n"
398 "\t- name: string\n"
399 "\tSets the property with which to operate. If there is no property\n"
400 "\tof this name, the call is ignored.\n";
401 PyObject* SCA_PropertySensor::PySetProperty(PyObject* args) 
402 {
403         ShowDeprecationWarning("setProperty()", "the 'propName' property");
404         /* We should query whether the name exists. Or should we create a prop   */
405         /* on the fly?                                                           */
406         char *propNameArg = NULL;
407
408         if (!PyArg_ParseTuple(args, "s:setProperty", &propNameArg)) {
409                 return NULL;
410         }
411
412         CValue *prop = FindIdentifier(STR_String(propNameArg));
413         if (!prop->IsError()) {
414                 m_checkpropname = propNameArg;
415         } else {
416                 ; /* error: bad property name */
417         }
418         prop->Release();
419         Py_RETURN_NONE;
420 }
421
422 /* 5. getValue */
423 const char SCA_PropertySensor::GetValue_doc[] = 
424 "getValue()\n"
425 "\tReturns the value with which the sensor operates.\n";
426 PyObject* SCA_PropertySensor::PyGetValue() 
427 {
428         ShowDeprecationWarning("getValue()", "the value property");
429         return PyUnicode_FromString(m_checkpropval);
430 }
431
432 /* 6. setValue */
433 const char SCA_PropertySensor::SetValue_doc[] = 
434 "setValue(value)\n"
435 "\t- value: string\n"
436 "\tSet the value with which the sensor operates. If the value\n"
437 "\tis not compatible with the type of the property, the subsequent\n"
438 "\t action is ignored.\n";
439 PyObject* SCA_PropertySensor::PySetValue(PyObject* args) 
440 {
441         ShowDeprecationWarning("setValue()", "the value property");
442         /* Here, we need to check whether the value is 'valid' for this property.*/
443         /* We know that the property exists, or is NULL.                         */
444         char *propValArg = NULL;
445
446         if(!PyArg_ParseTuple(args, "s:setValue", &propValArg)) {
447                 return NULL;
448         }
449         STR_String oldval = m_checkpropval;
450         m_checkpropval = propValArg;
451         if (validValueForProperty(m_proxy, NULL)) {
452                 m_checkpropval = oldval;
453                 return NULL;
454         }       
455         Py_RETURN_NONE;
456 }
457
458 /* eof */