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