JNR
aiMobState_Flyer.cpp
1 /*
2 
3 Jump'n'Run Engine
4 http://www.atanaslaskov.com/jnr/
5 
6 BSD LICENSE
7 Copyright (c) 2007-2013, Atanas Laskov
8 All rights reserved.
9 
10 Redistribution and use in source and binary forms, with or without
11 modification, are permitted provided that the following conditions are met:
12 1. Redistributions of source code must retain the above copyright notice,
13 this list of conditions and the following disclaimer.
14 2. Redistributions in binary form must reproduce the above copyright notice,
15 this list of conditions and the following disclaimer in the documentation
16 and/or other materials provided with the distribution.
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 DISCLAIMED. IN NO EVENT SHALL ATANAS LASKOV BE LIABLE FOR ANY
21 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 */
29 
30 #include "stdafx.h"
31 #include "ai.h"
32 
33 aiMobState_Flyer::aiMobState_Flyer(void)
34 {
35 }
36 
37 aiMobState_Flyer::~aiMobState_Flyer(void)
38 {
39 }
40 
41 void aiMobState_Flyer::load(laFileParser *fp)
42 {
43  aiMobState::load(fp);
44  //aiMobState_Ramble::load(fp);
45 
46  fp->readDouble(&_dFlutterRange);
47  fp->readDouble(&_dSwayRange);
48  fp->readDouble(&_dFSSpeed);
49 
50  _ang = rand()%90;
51 }
52 
53 // AI Percept Handlers
54 //
55 void aiMobState_Flyer::perceive_active( std::string strID_Old )
56 {
57  laMonster* pMonster = (laMonster*) self();
58  laPlayer* pPlayer = (laPlayer*) player();
59 
60  pMonster->getPivot()-> bSimulateGravitation = M_FALSE;
61  pMonster->move_v( laPoint3(1,0,0) );
62 
63  dY_Initial = pMonster->getPosition().y();
64 
65  pMonster->agressive(M_TRUE);
66  pMonster->enableBattleGUI(M_FALSE);
67 }
68 
69 void aiMobState_Flyer::perceive_inactive( std::string strID_New )
70 {
71  laMonster* pMonster = (laMonster*) self();
72  laPlayer* pPlayer = (laPlayer*) player();
73 }
74 
75 /*void aiMobState_Flyer::perceive_InTerritory(unsigned state)
76 {
77  aiMobState_Ramble::perceive_InTerritory(state);
78 
79  laMonster* pMonster = (laMonster*) self();
80  laPlayer* pPlayer = (laPlayer*) player();
81 
82  //_select_creature();
83  //pMonster->setOutline( laOutline(M_TRUE, laColor(255,255,255), 1.5) );
84  //agent()->state("approach");
85 }*/
86 
87 void aiMobState_Flyer::perceive_InRange(unsigned state)
88 {
89  laMonster* pMonster = (laMonster*) self();
90  laPlayer* pPlayer = (laPlayer*) player();
91 
92  switch(state)
93  {
94  case PERCEPT_SET:
95  pMonster->enableBattleGUI(M_TRUE);
96  //pMonster->attackSelect(M_MATTACK_MAIN + rand()%1);
97  pMonster->attackInitiate();
98  break;
99 
100  case PERCEPT_HOLD:
101  if( pMonster->attackIsCharged() ) {
102  pMonster->attack(pPlayer);
103  }
104  else {
105  pMonster->attackSelect(M_MATTACK_MAIN + rand()%2);
106  pMonster->attackInitiate();
107  }
108  break;
109 
110  case PERCEPT_RESET:
111  pMonster->enableBattleGUI(M_FALSE);
112  pMonster->attackStop();
113  break;
114  }
115 }
116 
117 void aiMobState_Flyer::perceive(unsigned state, M_BOOL global, unsigned id, aiPerceptData data)
118 {
119  laMonster* pMonster = (laMonster*) self();
120  laPivot *pMobPivot = pMonster->getPivot();
121 
122  aiMobState::perceive(state, global, id, data);
123 
124  // NOTE: For this AI type gravity is off,
125  // so we can controll both X and Y directly,
126  // and use move_v() instead of move()
127  //
128  laPoint3 dir = laPoint3( pMonster->getMoveDirection(), 0);
129  dir = _update_direction(dir);
130  dir = _chase_player(dir);
131  pMonster->move_v( dir );
132 
133  // Small-scale random motion, simulate flight
134  _flutter();
135 }
136 
137 // Determine mov dir; Takes precedence
138 //
139 laPoint3 aiMobState_Flyer::_update_direction(laPoint3 dir)
140 {
141  laMonster* pMonster = (laMonster*) self();
142 
143  if( _obstacle_infront() )
144  {
145  dir[0] = -1 * dir.x(); // => reverse dir
146  }
147  else
148  {
149  // no obstacle => patrol within territory range
150  //
151  if( !_within_left_range() )
152  dir[0] = +1;
153 
154  if( !_within_right_range() )
155  dir[0] = -1;
156  }
157 
158  return dir;
159 }
160 
161 laPoint3 aiMobState_Flyer::_chase_player(laPoint3 dir)
162 {
163  laMonster* pMonster = (laMonster*) self();
164  laPlayer* pPlayer = (laPlayer*) player();
165 
166  // Chase player
167  //
168  M_BOOL bChase = M_FALSE;
169 
170  if( _within_left_range() && _within_right_range() )
171  {
172  if( M_ABS(_dPlayerDistance) <= 4 * M_UNIT )
173  {
174  // Chase player along Y but don't overlap
175  if( M_ABS(pPlayer->getPosition().y() - pMonster->getPosition().y() ) > M_UNIT )
176  {
177  if( pPlayer->getPosition().y() > pMonster->getPosition().y() ) dir[1] = +1;
178  else dir[1] = -1;
179  }
180  // Move up a bit to avoid getting stuck to the ground
181  else if( pPlayer->getPosition().y() < pMonster->getPosition().y() + M_UNIT * 0.8 )
182  dir[1] = -1;
183  else
184  dir[1] = 0; // close enough
185 
186  // Chase player along X but don't overlap
187  //
188  if( (!agent()->isPerceiving(P__PLAYER_IN_RANGE))
189  &&
190  M_ABS(pPlayer->getPosition().x() - pMonster->getPosition().x() ) > M_UNIT )
191  {
192  if( pPlayer->getPosition().x() > pMonster->getPosition().x() ) dir[0] = +1;
193  else dir[0] = -1;
194  }
195  else dir[0] = 0; // close enough
196 
197  // Avoid placing the mosnter directly on top
198  //
199  if( M_ABS(pPlayer->getPosition().x() - pMonster->getPosition().x() ) < M_UNIT * 0.8 )
200  {
201  if( pMonster->getPosition().x() > pPlayer->getPosition().x() )
202  {
203  pMonster->setExplicitFacing(M_TRUE);
204  pMonster->setFaceDirection(-1);
205  dir[0] = +1;
206  }
207  else
208  {
209  pMonster->setExplicitFacing(M_TRUE);
210  pMonster->setFaceDirection(+1);
211  dir[0] = -1;
212  }
213  }
214  else pMonster->setExplicitFacing(M_FALSE);
215 
216  // Remain in chasing mode
217  bChase = M_TRUE;
218  pPlayer->ctlBattleMode();
219  }
220  }
221 
222  _select_creature( bChase );
223 
224  // TODO: Steer to avoid terrain obstacles
225  //
226  /*if( _obstacle_infront() && ( dir.y() > 0 ) ) dir[1] =0;
227  if( _obstacle_below() && ( dir.y() > 0 ) ) dir[1] =0;
228  if( _obstacle_below() && ( dir.y() > 0 ) ) dir[1] =0;*/
229 
230  // Return to original Y
231  //
232  if( (!bChase) && (M_ABS(dY_Initial - pMonster->getPosition().y() ) >= 0.1 * M_UNIT) )
233  {
234  if( dY_Initial > pMonster->getPosition().y() ) dir[1] = +1;
235  else dir[1] = -1;
236 
237  dir[0] = 1; // 'casue it gets nulified if the player is reached
238  }
239 
240  return dir;
241 }
242 
243 // Flutter and sway
244 //
245 void aiMobState_Flyer::_flutter()
246 {
247  laMonster* pMonster = (laMonster*) self();
248  laPivot *pMobPivot = pMonster->getPivot();
249  double spf = ::laSystemIntegrator::getEnvironment()->getTimer()->delta();
250 
251  double var1 = sin(_ang);
252  double var2 = sin(_ang*2);
253  double var3 = sin(_ang*3);
254  //double var4 = sin(_ang*2);
255 
256  pMobPivot->ptGeometryOffset[0] = var1 * M_UNIT * _dFlutterRange;
257  pMobPivot->ptGeometryOffset[1] = var2 * M_UNIT * _dFlutterRange;
258  pMobPivot->ptGeometryOffset[2]= var3 * M_UNIT * _dFlutterRange;
259  pMobPivot->angleZ = var1 * _dSwayRange - _dSwayRange/2.0;
260  pMobPivot->angleX = var2 * _dSwayRange - _dSwayRange/2.0;
261 
262  _ang += spf * _dFSSpeed;
263  if( _ang > 360 ) _ang -= 360;
264 }
265 
266 M_BOOL aiMobState_Flyer::_in_y_range(double distance)
267 {
268  laMonster* pMonster = (laMonster*) self();
269 
270  if( M_ABS(dY_Initial - pMonster->getPosition().y() ) <= distance )
271  return M_TRUE;
272  return M_FALSE;
273 }
274 
275 M_BOOL aiMobState_Flyer::_obstacle_infront()
276 {
277  laMonster* pMonster = (laMonster*) self();
278  laPivot *pMobPivot = pMonster->getPivot();
279 
280  int direction = pMonster->getMoveDirection();
281  laPoint3 vTentacle(direction*M_UNIT*2, 0, 0);
282  laPivot vPivot = (*pMobPivot);
283 
284  vPivot[1] += M_UNIT * 0.3; // nudge up a bit to obtain a safe margin
285  // NOTE: since the pitot is most likely in the middle,
286  // use the margin to avoid penetration
287 
288  vTentacle = vPivot.projectVector(vTentacle);
289 
290  if( vTentacle.lenght() <= (vPivot.size.x()*0.5 + M_UNIT*0.15) ) return M_TRUE;
291  else return M_FALSE;
292 }
293 
294 M_BOOL aiMobState_Flyer::_obstacle_below()
295 {
296  laMonster* pMonster = (laMonster*) self();
297  laPivot *pMobPivot = pMonster->getPivot();
298 
299  int direction = pMonster->getMoveDirection();
300  laPoint3 vTentacle(0, M_UNIT, 0);
301  laPivot vPivot = (*pMobPivot);
302 
303  //vPivot[1] += M_UNIT * 0.3;
304  vTentacle = vPivot.projectVector(vTentacle);
305 
306  if( vTentacle.lenght() <= M_UNIT*0.8 ) return M_TRUE;
307  return M_FALSE;
308 }
309 
310 M_BOOL aiMobState_Flyer::_obstacle_above()
311 {
312  laMonster* pMonster = (laMonster*) self();
313  laPivot *pMobPivot = pMonster->getPivot();
314 
315  int direction = pMonster->getMoveDirection();
316  laPoint3 vTentacle(0, -1*M_UNIT, 0);
317  laPivot vPivot = (*pMobPivot);
318 
319  //vPivot[1] += M_UNIT * 0.3;
320  vTentacle = vPivot.projectVector(vTentacle);
321 
322  if( vTentacle.lenght() <= M_UNIT*0.8 ) return M_TRUE;
323  return M_FALSE;
324 }
Playable Character.
Definition: laPlayer.h:46
#define M_UNIT
Unit of 1 meter.
#define M_ABS(a)
Return abs(a)
Adds capabilities and percepts specific to monster creatures.
Definition: laMonster.h:45
Dynamic Object Pivot.
Definition: laPivot.h:44
File Parser.
Definition: laFileParser.h:41