Recreating my GSoC branch.
[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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 #include "FloatValue.h"
42 #include <stdio.h>
43
44 SCA_PropertySensor::SCA_PropertySensor(SCA_EventManager* eventmgr,
45                                                                          SCA_IObject* gameobj,
46                                                                          const STR_String& propname,
47                                                                          const STR_String& propval,
48                                                                          const STR_String& propmaxval,
49                                                                          KX_PROPSENSOR_TYPE checktype)
50         : SCA_ISensor(gameobj,eventmgr),
51           m_checktype(checktype),
52           m_checkpropval(propval),
53           m_checkpropmaxval(propmaxval),
54           m_checkpropname(propname),
55           m_range_expr(NULL)
56 {
57         //CParser pars;
58         //pars.SetContext(this->AddRef());
59         //CValue* resultval = m_rightexpr->Calculate();
60
61         CValue* orgprop = GetParent()->FindIdentifier(m_checkpropname);
62         if (!orgprop->IsError())
63         {
64                 m_previoustext = orgprop->GetText();
65         }
66         orgprop->Release();
67
68         if (m_checktype==KX_PROPSENSOR_INTERVAL)
69         {
70                 PrecalculateRangeExpression();
71         }
72         Init();
73 }
74
75 void SCA_PropertySensor::Init()
76 {
77         m_recentresult = false;
78         m_lastresult = m_invert?true:false;
79         m_reset = true;
80 }
81
82 void SCA_PropertySensor::PrecalculateRangeExpression()
83 {
84                 CParser pars;
85                 //The context is needed to retrieve the property at runtime but it creates
86                 //loop of references
87                 pars.SetContext(this->AddRef());
88                 STR_String checkstr = "(" + m_checkpropval + " <= " 
89                                                         + m_checkpropname + ") && ( " 
90                                                         + m_checkpropname + " <= " 
91                                                         + m_checkpropmaxval + ")";
92
93                 m_range_expr = pars.ProcessText(checkstr);
94 }
95
96 // Forced deletion of precalculated range expression to break reference loop
97 // Use this function when you know that you won't use the sensor anymore
98 void SCA_PropertySensor::Delete()
99 {
100         if (m_range_expr)
101         {
102                 m_range_expr->Release();
103                 m_range_expr = NULL;
104         }
105         Release();
106 }
107
108 CValue* SCA_PropertySensor::GetReplica()
109 {
110         SCA_PropertySensor* replica = new SCA_PropertySensor(*this);
111         // m_range_expr must be recalculated on replica!
112         replica->ProcessReplica();
113         replica->Init();
114
115         replica->m_range_expr = NULL;
116         if (replica->m_checktype==KX_PROPSENSOR_INTERVAL)
117         {
118                 replica->PrecalculateRangeExpression();
119         }
120         
121         
122         return replica;
123 }
124
125
126
127 bool SCA_PropertySensor::IsPositiveTrigger()
128 {
129         bool result = m_recentresult;//CheckPropertyCondition();
130         if (m_invert)
131                 result = !result;
132
133         return result;
134 }
135
136
137
138 SCA_PropertySensor::~SCA_PropertySensor()
139 {
140         //if (m_rightexpr)
141         //      m_rightexpr->Release();
142
143         if (m_range_expr)
144         {
145                 m_range_expr->Release();
146                 m_range_expr=NULL;
147         }
148
149 }
150
151
152
153 bool SCA_PropertySensor::Evaluate()
154 {
155         bool result = CheckPropertyCondition();
156         bool reset = m_reset && m_level;
157         
158         m_reset = false;
159         if (m_lastresult!=result)
160         {
161                 m_lastresult = result;
162                 return true;
163         }
164         return (reset) ? true : false;
165 }
166
167
168 bool    SCA_PropertySensor::CheckPropertyCondition()
169 {
170
171         m_recentresult=false;
172         bool result=false;
173         bool reverse = false;
174         switch (m_checktype)
175         {
176         case KX_PROPSENSOR_NOTEQUAL:
177                 reverse = true;
178         case KX_PROPSENSOR_EQUAL:
179                 {
180                         CValue* orgprop = GetParent()->FindIdentifier(m_checkpropname);
181                         if (!orgprop->IsError())
182                         {
183                                 const STR_String& testprop = orgprop->GetText();
184                                 // Force strings to upper case, to avoid confusion in
185                                 // bool tests. It's stupid the prop's identity is lost
186                                 // on the way here...
187                                 if ((&testprop == &CBoolValue::sTrueString) || (&testprop == &CBoolValue::sFalseString)) {
188                                         m_checkpropval.Upper();
189                                 }
190                                 result = (testprop == m_checkpropval);
191                                 
192                                 /* Patch: floating point values cant use strings usefully since you can have "0.0" == "0.0000"
193                                  * this could be made into a generic Value class function for comparing values with a string.
194                                  */
195                                 if(result==false && dynamic_cast<CFloatValue *>(orgprop) != NULL) {
196                                         float f;
197                                         
198                                         if(EOF == sscanf(m_checkpropval.ReadPtr(), "%f", &f))
199                                         {
200                                                 //error
201                                         } 
202                                         else {
203                                                 result = (f == ((CFloatValue *)orgprop)->GetFloat());
204                                         }
205                                 }
206                                 /* end patch */
207                         }
208                         orgprop->Release();
209
210                         if (reverse)
211                                 result = !result;
212                         break;
213
214                 }
215
216         case KX_PROPSENSOR_EXPRESSION:
217                 {
218                         /*
219                         if (m_rightexpr)
220                         {
221                                 CValue* resultval = m_rightexpr->Calculate();
222                                 if (resultval->IsError())
223                                 {
224                                         int i=0;
225                                         STR_String errortest = resultval->GetText();
226                                         printf(errortest);
227
228                                 } else
229                                 {
230                                         result = resultval->GetNumber() != 0;
231                                 }
232                         }
233                         */
234                         break;
235                 }
236         case KX_PROPSENSOR_INTERVAL:
237                 {
238                         //CValue* orgprop = GetParent()->FindIdentifier(m_checkpropname);
239                         //if (orgprop)
240                         //{
241                                 if (m_range_expr)
242                                 {
243                                         CValue* vallie = m_range_expr->Calculate();
244                                         if (vallie)
245                                         {
246                                                 const STR_String& errtext = vallie->GetText();
247                                                 if (&errtext == &CBoolValue::sTrueString)
248                                                 {
249                                                         result = true;
250                                                 } else
251                                                 {
252                                                         if (vallie->IsError())
253                                                         {
254                                                                 //printf (errtext.ReadPtr());
255                                                         } 
256                                                 }
257                                                 
258                                                 vallie->Release();
259                                         }
260                                 }
261
262                                 
263                         //}
264                         
265                 //cout << " \nSens:Prop:interval!"; /* need implementation here!!! */
266
267                 break;
268                 }
269         case KX_PROPSENSOR_CHANGED:
270                 {
271                         CValue* orgprop = GetParent()->FindIdentifier(m_checkpropname);
272                                 
273                         if (!orgprop->IsError())
274                         {
275                                 if (m_previoustext != orgprop->GetText())
276                                 {
277                                         m_previoustext = orgprop->GetText();
278                                         result = true;
279                                 }
280                         }
281                         orgprop->Release();
282
283                         //cout << " \nSens:Prop:changed!"; /* need implementation here!!! */
284                         break;
285                 }
286         default:
287                 ; /* error */
288         }
289
290         //the concept of Edge and Level triggering has unwanted effect for KX_PROPSENSOR_CHANGED
291         //see Game Engine bugtracker [ #3809 ]
292         if (m_checktype != KX_PROPSENSOR_CHANGED)
293         {
294                 m_recentresult=result;
295         } else
296         {
297                 m_recentresult=result;//true;
298         }
299         return result;
300 }
301
302 CValue* SCA_PropertySensor::FindIdentifier(const STR_String& identifiername)
303 {
304         return  GetParent()->FindIdentifier(identifiername);
305 }
306
307 #ifndef DISABLE_PYTHON
308
309 /* ------------------------------------------------------------------------- */
310 /* Python functions                                                          */
311 /* ------------------------------------------------------------------------- */
312
313 int SCA_PropertySensor::validValueForProperty(void *self, const PyAttributeDef*)
314 {
315         /*  If someone actually do type checking please make sure the 'max' and 'min'
316                 are checked as well (currently they are calling the PrecalculateRangeExpression
317                 function directly       */
318
319         /*  There is no type checking at this moment, unfortunately...           */
320         return 0;
321 }
322
323 int SCA_PropertySensor::validValueForIntervalProperty(void *self, const PyAttributeDef*)
324 {
325         SCA_PropertySensor*     sensor = reinterpret_cast<SCA_PropertySensor*>(self);
326
327         if (sensor->m_checktype==KX_PROPSENSOR_INTERVAL)
328         {
329                 sensor->PrecalculateRangeExpression();
330         }
331         return 0;
332 }
333
334 int SCA_PropertySensor::modeChange(void *self, const PyAttributeDef* attrdef)
335 {
336         SCA_PropertySensor*     sensor = reinterpret_cast<SCA_PropertySensor*>(self);
337
338         if (sensor->m_checktype==KX_PROPSENSOR_INTERVAL)
339         {
340                 sensor->PrecalculateRangeExpression();
341         }
342         return 0;
343 }
344
345 /* Integration hooks ------------------------------------------------------- */
346 PyTypeObject SCA_PropertySensor::Type = {
347         PyVarObject_HEAD_INIT(NULL, 0)
348         "SCA_PropertySensor",
349         sizeof(PyObjectPlus_Proxy),
350         0,
351         py_base_dealloc,
352         0,
353         0,
354         0,
355         0,
356         py_base_repr,
357         0,0,0,0,0,0,0,0,0,
358         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
359         0,0,0,0,0,0,0,
360         Methods,
361         0,
362         0,
363         &SCA_ISensor::Type,
364         0,0,0,0,0,0,
365         py_base_new
366 };
367
368 PyMethodDef SCA_PropertySensor::Methods[] = {
369         {NULL,NULL} //Sentinel
370 };
371
372 PyAttributeDef SCA_PropertySensor::Attributes[] = {
373         KX_PYATTRIBUTE_INT_RW_CHECK("mode",KX_PROPSENSOR_NODEF,KX_PROPSENSOR_MAX-1,false,SCA_PropertySensor,m_checktype,modeChange),
374         KX_PYATTRIBUTE_STRING_RW_CHECK("propName",0,100,false,SCA_PropertySensor,m_checkpropname,CheckProperty),
375         KX_PYATTRIBUTE_STRING_RW_CHECK("value",0,100,false,SCA_PropertySensor,m_checkpropval,validValueForProperty),
376         KX_PYATTRIBUTE_STRING_RW_CHECK("min",0,100,false,SCA_PropertySensor,m_checkpropval,validValueForIntervalProperty),
377         KX_PYATTRIBUTE_STRING_RW_CHECK("max",0,100,false,SCA_PropertySensor,m_checkpropmaxval,validValueForIntervalProperty),
378         { NULL }        //Sentinel
379 };
380
381 #endif // DISABLE_PYTHON
382
383 /* eof */