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