1 /***************************************************************************
2                           StateBehavior.cpp  -  description
3                              -------------------
4     begin                : Thu Jan 25 2001
5     copyright            : (C) 2001 by Henrik Enqvist
6     email                : henqvist@excite.com
7 
8 
9     ========================= Modifications =========================
10 
11         Apr. 4, 2017:
12             Clear the keyboard on game reset. (c30zD)
13 
14 ***************************************************************************/
15 
16 #include "Private.h"
17 #include "StateBehavior.h"
18 #include "Pinball.h"
19 #include "Group.h"
20 #include "Light.h"
21 #include "Shape3D.h"
22 #include "Polygon.h"
23 #include "SoundUtil.h"
24 #include "BallGroup.h"
25 #include "Loader.h"
26 #include "Keyboard.h"
27 
StateItem()28 StateItem::StateItem() {
29   m_iActSig = -1;
30   m_iCollSig = -1;
31   m_iSigDelay = 0;
32   m_bLight = false;
33   m_iUserProperty = PBL_NULL;
34   m_iShapeProperty = 0;
35   m_iSound = -1;
36   m_iMusic = -1;
37   m_iDelay = -1;
38   m_iDelayState = -1;
39   m_iCollState = -1;
40   m_iMoveSteps = 0;
41   m_iTick = 0;
42   m_vtxTr.x = 0;
43   m_vtxTr.y = 0;
44   m_vtxTr.z = 0;
45   m_vtxRot.x = 0;
46   m_vtxRot.y = 0;
47   m_vtxRot.z = 0;
48 }
49 
~StateItem()50 StateItem::~StateItem() {
51 }
52 
setTr(float x,float y,float z)53 void StateItem::setTr(float x, float y, float z) {
54   m_vtxTr.x = x;
55   m_vtxTr.y = y;
56   m_vtxTr.z = z;
57   //m_fTrSpeed = EM_ABS(speed);
58 }
59 
setRot(float x,float y,float z)60 void StateItem::setRot(float x, float y, float z) {
61   m_vtxRot.x = x;
62   m_vtxRot.y = y;
63   m_vtxRot.z = z;
64   //m_fRotSpeed = EM_ABS(speed);
65 }
66 
addTexCoord(float u,float v)67 void StateItem::addTexCoord(float u, float v) {
68   TexCoord tex;
69   tex.u = u;
70   tex.v = v;
71   m_vTexCoord.push_back(tex);
72 }
73 
addShapeEnable(int i,bool e)74 void StateItem::addShapeEnable(int i, bool e) {
75   if (i < 0) return;
76   // expand vector
77   while (i+1 > (signed)m_vShapeEnable.size()) {
78     m_vShapeEnable.push_back(false);
79   }
80   m_vShapeEnable[i] = e;
81 }
82 
getShapeEnable(int i)83 bool StateItem::getShapeEnable(int i) {
84   if (i < 0 || i >= (signed)m_vShapeEnable.size()) return false;
85   return m_vShapeEnable[i];
86 }
87 
StateBehavior()88 StateBehavior::StateBehavior() : Behavior() {
89   m_iOwnerBall = -1;
90   m_iTick = 0;
91   m_iCollisionSafe = 0;
92   m_bTexCoord = false;
93   m_bMove = false;
94   m_bShape = false;
95   p_CurrentStateItem = NULL;
96   m_vtxTr.x = 0;
97   m_vtxTr.y = 0;
98   m_vtxTr.z = 0;
99   m_vtxRot.x = 0;
100   m_vtxRot.y = 0;
101   m_vtxRot.z = 0;
102   this->setType(PBL_TYPE_STATEBEH);
103 }
104 
~StateBehavior()105 StateBehavior::~StateBehavior(){
106 }
107 
addStateItem(StateItem * stateitem)108 void StateBehavior::addStateItem(StateItem* stateitem) {
109   if (stateitem == NULL) return;
110   // insert state
111   m_vStateItem.push_back(stateitem);
112   if (m_vStateItem.size() == 1) {
113     p_CurrentStateItem = stateitem;
114   }
115 }
116 
removeStateItem(StateItem * stateitem)117 void StateBehavior::removeStateItem(StateItem* stateitem) {
118   if (stateitem == NULL) return;
119   if (p_CurrentStateItem == stateitem) {
120     p_CurrentStateItem = NULL;
121   }
122 
123   vector<StateItem*>::iterator iter = m_vStateItem.begin();
124   vector<StateItem*>::iterator end = m_vStateItem.end();
125   for (; iter != end; iter++) {
126     if ((*iter) == stateitem) {
127       m_vStateItem.erase(iter);
128       return;
129     }
130   }
131 
132   if (m_vStateItem.size() >= 1) {
133     p_CurrentStateItem = m_vStateItem[0];
134   }
135 }
136 
getStateItem(int i)137 StateItem * StateBehavior::getStateItem(int i) {
138   if (i < 0 || m_vStateItem.size() <= (unsigned) i ) return NULL;
139   return m_vStateItem[i];
140 }
141 
onTick()142 void StateBehavior::onTick() {
143   EmAssert(this->getParent() != NULL, "Parent NULL");
144   if (p_CurrentStateItem == NULL) return;
145 
146   // initialize origo
147   if (m_iTick == 0) {
148     this->getParent()->getTranslation(m_vtxTr.x, m_vtxTr.y, m_vtxTr.z);
149     this->getParent()->getRotation(m_vtxRot.x, m_vtxRot.y, m_vtxRot.z);
150   }
151 
152   m_iTick++;
153   p_CurrentStateItem->m_iTick++;
154 
155   if (p_CurrentStateItem->m_iDelay >= 0 &&
156       p_CurrentStateItem->m_iTick >= p_CurrentStateItem->m_iDelay &&
157       p_CurrentStateItem->m_iDelayState >= 0) {
158     this->setState(this->getStateItem(p_CurrentStateItem->m_iDelayState));
159   }
160   // the ball recently collided with this object is in a safe distance
161   if (m_iTick > m_iCollisionSafe) {
162     m_iOwnerBall = -1;
163   }
164   // move the object to towards the next spot
165   // TODO smother move
166   if (m_bMove) {
167     int steps = p_CurrentStateItem->m_iMoveSteps - p_CurrentStateItem->m_iTick;
168 
169     if (steps <= 0) {
170       this->getParent()->setTransform(m_vtxTr.x + p_CurrentStateItem->m_vtxTr.x,
171 				      m_vtxTr.y + p_CurrentStateItem->m_vtxTr.y,
172 				      m_vtxTr.z + p_CurrentStateItem->m_vtxTr.z,
173 				      m_vtxRot.x + p_CurrentStateItem->m_vtxRot.x,
174 				      m_vtxRot.y + p_CurrentStateItem->m_vtxRot.y,
175 				      m_vtxRot.z + p_CurrentStateItem->m_vtxRot.z);
176     } else {
177       float dtx, dty, dtz, drx, dry, drz;
178       float tx, ty, tz, rx, ry, rz;
179       this->getParent()->getTranslation(tx, ty, tz);
180       this->getParent()->getRotation(rx, ry, rz);
181 
182       dtx =  p_CurrentStateItem->m_vtxTr.x + m_vtxTr.x - tx;
183       dty =  p_CurrentStateItem->m_vtxTr.y + m_vtxTr.y - ty;
184       dtz =  p_CurrentStateItem->m_vtxTr.z + m_vtxTr.z - tz;
185       drx =  p_CurrentStateItem->m_vtxRot.x + m_vtxRot.x - rx;
186       dry =  p_CurrentStateItem->m_vtxRot.y + m_vtxRot.y - ry;
187       drz =  p_CurrentStateItem->m_vtxRot.z + m_vtxRot.z - rz;
188 
189       dtx /= steps;	dty /= steps;	dtz /= steps;
190       drx /= steps;	dry /= steps;	drz /= steps;
191       this->getParent()->addTransform(dtx, dty, dtz, drx, dry, drz);
192     }
193   }
194 }
195 
setState(StateItem * stateitem)196 void StateBehavior::setState(StateItem* stateitem) {
197   EmAssert(p_CurrentStateItem != NULL, "Current state item NULL");
198   if (stateitem == NULL) return;
199   StateItem* previtem = p_CurrentStateItem;
200   p_CurrentStateItem = stateitem;
201 
202   // set properties
203   this->getParent()->unsetUserProperty(previtem->m_iUserProperty);
204   this->getParent()->setUserProperty(p_CurrentStateItem->m_iUserProperty);
205   // defaults to shape 0
206   int sindex = 0;
207   Shape3D* shape = this->getParent()->getShape3D(sindex);
208   while (shape != NULL) {
209     shape->unsetProperty(previtem->m_iShapeProperty);
210     shape->setProperty(p_CurrentStateItem->m_iShapeProperty);
211     ++sindex;
212     shape = this->getParent()->getShape3D(sindex);
213   }
214   // apply texcoords
215   if (m_bTexCoord) {
216     // default to shape 0 TODO remove this thing, we can use shapes instead
217     Shape3D* shape = this->getParent()->getShape3D(0);
218     if (shape != NULL) {
219       // only apply to fisrst polygon TODO
220       Polygon3D* poly = shape->getPolygon(0);
221       if (poly != NULL) {
222 	vector<TexCoord>::iterator iter1 = p_CurrentStateItem->m_vTexCoord.begin();
223 	vector<TexCoord>::iterator end1 = p_CurrentStateItem->m_vTexCoord.end();
224 	vector<TexCoord>::iterator iter2 = shape->m_vTexCoord.begin();
225 	vector<TexCoord>::iterator end2 = shape->m_vTexCoord.end();
226 	for (; iter1 != end1 && iter2 != end2; ++iter1, ++iter2) {
227 	  (*iter2) = (*iter1);
228 	}
229       }
230     }
231   }
232   // look out shapes conflicts with shape properties
233   if (m_bShape) {
234     vector<bool>::iterator iter = p_CurrentStateItem->m_vShapeEnable.begin();
235     vector<bool>::iterator end = p_CurrentStateItem->m_vShapeEnable.end();
236     int index = 0;
237     Shape3D* shape = this->getParent()->getShape3D(index);
238     while (shape != NULL && iter != end) {
239       if (*iter) {
240 	shape->unsetProperty(EM_SHAPE3D_HIDDEN);
241       } else {
242 	shape->setProperty(EM_SHAPE3D_HIDDEN);
243       }
244       ++index;
245       ++iter;
246       shape = this->getParent()->getShape3D(index);
247     }
248   }
249   // start music
250   if (p_CurrentStateItem->m_iMusic >= 0) {
251     SoundUtil::getInstance()->playMusic(p_CurrentStateItem->m_iMusic, true);
252   }
253   // play sound
254   if (p_CurrentStateItem->m_iSound >= 0) {
255     SoundUtil::getInstance()->playSample(p_CurrentStateItem->m_iSound, false);
256   }
257   // apply light
258   SetLightOn(p_CurrentStateItem->m_bLight);
259   // zero counter
260   p_CurrentStateItem->m_iTick = 0;
261 }
262 
StdOnSignal()263 void StateBehavior::StdOnSignal() {
264   EmAssert(this->getParent() != NULL, "Parent NULL");
265   if (p_CurrentStateItem == NULL && m_vStateItem.size() == 0) return;
266 
267   // initialize origo, this is a ugle hack
268   if (m_iTick == 0) {
269     this->getParent()->getTranslation(m_vtxTr.x, m_vtxTr.y, m_vtxTr.z);
270     this->getParent()->getRotation(m_vtxRot.x, m_vtxRot.y, m_vtxRot.z);
271   }
272 
273 #if EM_DEBUG
274 	if (Loader::getInstance()->getSignal(GetSignal()) != NULL) {
275 		EM_COUT("Got signal " << Loader::getInstance()->getSignal(GetSignal()), 0);
276 	}
277 #endif
278 
279   OnSignal( PBL_SIG_RESET_ALL) {
280     this->setState(m_vStateItem[0]);
281     // do a fast move
282     if (m_bMove) {
283       this->getParent()->setTransform(m_vtxTr.x + p_CurrentStateItem->m_vtxTr.x,
284 				      m_vtxTr.y + p_CurrentStateItem->m_vtxTr.y,
285 				      m_vtxTr.z + p_CurrentStateItem->m_vtxTr.z,
286 				      m_vtxRot.x + p_CurrentStateItem->m_vtxRot.x,
287 				      m_vtxRot.y + p_CurrentStateItem->m_vtxRot.y,
288 				      m_vtxRot.z + p_CurrentStateItem->m_vtxRot.z);
289 
290     }
291     Keyboard::clear();
292   } else {
293     vector<StateItem*>::iterator iter = m_vStateItem.begin();
294     vector<StateItem*>::iterator end = m_vStateItem.end();
295     for (; iter != end; iter++) {
296       OnSignal((*iter)->m_iActSig) {
297 	this->setState(*iter);
298 	break;
299       }
300     }
301   }
302 }
303 
StdOnCollision()304 void StateBehavior::StdOnCollision() {
305   if (p_CurrentStateItem == NULL && m_vStateItem.size() == 0) return;
306 
307   OnCallerProperty( PBL_BALL ) {
308     // this is to avaid one collision to generate more than one signal
309     if (m_iTick <= m_iCollisionSafe) {
310       m_iCollisionSafe = m_iTick + 5;
311       return;
312     }
313 
314     m_iOwnerBall = ((BallGroup*)em_group)->getBall();;
315     m_iCollisionSafe = m_iTick + 5;
316 
317     //	if (p_CurrentStateItem->m_iSound >= 0) {
318     //	SoundUtil::getInstance()->playSample(p_CurrentStateItem->m_iSound, false);
319     //	}
320     if (p_CurrentStateItem->m_iCollSig >= 0) {
321       SendSignal(p_CurrentStateItem->m_iCollSig, p_CurrentStateItem->m_iSigDelay,
322 		 this->getParent(), NULL);
323     }
324     if (p_CurrentStateItem->m_iCollState >= 0) {
325       this->setState(this->getStateItem(p_CurrentStateItem->m_iCollState));
326     }
327     EM_COUT("StateBehavior::StdOnCollision ball", 0);
328   }
329 }
330