PageRenderTime 68ms CodeModel.GetById 15ms app.highlight 48ms RepoModel.GetById 1ms app.codeStats 0ms

/TGame/TCommon/Move/Move.cpp

http://awoe.googlecode.com/
C++ | 662 lines | 481 code | 126 blank | 55 comment | 94 complexity | ae719be65105e4cbcdaae26c8620a678 MD5 | raw file
  1
  2
  3
  4
  5#include "stdafx.h"
  6#include "MoveController.h"
  7
  8
  9
 10
 11
 12Move::Move()
 13:m_nCurMoveType(MT_WALK), 
 14m_nPrevMoveState(UnitBase::ums_Idle), 
 15m_bWalkOnly(false),
 16m_MaxSpeed(MAX_SPEED), 
 17m_MaxTurn ( MAX_TURN ), 
 18m_AngularSpeed (ANGULAR_SPEED)
 19{
 20}
 21
 22Move::~Move()
 23{
 24}
 25
 26bool 
 27Move::OnCreate(StaticUnitGene* pGSData, IGeneData* pGDData)
 28{
 29	if (__super::OnCreate(pGSData, pGDData))
 30	{
 31		int nData;
 32		nData = m_pStaticData->GetArg(AT_MaxSpeed);
 33		if (nData!=0)
 34		{
 35			m_MaxSpeed = float(nData) / 1000;
 36		}
 37
 38		nData = m_pStaticData->GetArg(AT_MaxTurn);
 39		if (nData!=0)
 40		{
 41			m_MaxTurn = float(nData) / 1000;
 42		}
 43
 44		nData = m_pStaticData->GetArg(AT_ArgularSpeed);
 45		if (nData!=0)
 46		{
 47			m_AngularSpeed = float(nData) / 1000;
 48		}
 49	}
 50
 51	return true;
 52}
 53
 54
 55
 56bool
 57Move::OnAttach()
 58{
 59	m_pUnit->RegGeneEvt( UGE_Move, this);
 60	m_pUnit->RegGeneEvt( UGE_Stop, this );
 61
 62	if (m_pUnit->IsMonster())
 63	{
 64		m_bWalkOnly = true;
 65	}
 66
 67	return true;
 68}
 69
 70bool
 71Move::OnDetach()
 72{
 73	m_vctPath.clear();
 74	m_nPathIndex = 0;
 75	
 76	OnMoveStop();
 77
 78	m_pUnit->UnregGeneEvt( UGE_Move, this);
 79	m_pUnit->UnregGeneEvt( UGE_Stop, this );
 80	return true;
 81}
 82
 83bool 
 84Move::OnUpdate(int nElapse)
 85{
 86	if (m_nPrevMoveState!=m_pUnit->GetMoveState())
 87	{
 88		//
 89		//	someone else change the unit move state
 90		if (m_nPrevMoveState==UnitBase::ums_Moving && 
 91			m_pUnit->GetMoveState()!=UnitBase::ums_OutOfControl)
 92		{
 93			OnMoveStop(false, false);
 94		}
 95
 96		//
 97		//	keep state in sync with unit move state
 98		m_nPrevMoveState = m_pUnit->GetMoveState();
 99	}
100
101	if (m_nPrevMoveState==UnitBase::ums_Idle ||
102		m_nPrevMoveState==UnitBase::ums_Freeze)
103	{
104		return true;
105	}
106
107	Vector3 vct3Current = ToVector3(m_pUnit->GetPosition());
108	Vector3 vct3Advance = ToVector3(m_pUnit->GetDirection());
109	Vector3 vct3Position;
110	Vector3 vct3DiffToNextPt;
111	Vector3 vct3CurrentDiff;
112	Real    nCurrDiff;
113	Real    nNewDiff;
114	Scene*  pScene;
115
116
117	if (m_vctPath.size() == 0)
118	{
119		return true;
120	}
121
122	// Always go into idle to allow blending
123
124	pScene = m_pUnit->GetScene();
125
126	if (pScene == NULL)
127	{
128		return true;
129	}
130
131	//m_MaxSpeed = pScene->GetMaxSpeed();
132
133	//start to move
134	if (m_pUnit->GetVelocity() == 0.0f)
135	{
136		m_pUnit->SetVelocity(0.01f);  // Just so Idle stops
137		m_nAcceleration = 1.0f;   
138	}
139	else 
140	{
141		// Check to make sure the desired and actual directions match so the unit doesn't make a wide turn on startup which can cause collision issues, remove check if that is ok
142		//    if (m_pUnit->GetVelocity() < m_pu3dMoveAnimation->m_nMaxRange) // && m_pUnit->m_lmUnit.GetDirectionsMatch() == true)
143		if (m_nAcceleration != 0.0f)//moving
144		{
145			float nVelocity;
146
147			nVelocity = m_pUnit->GetVelocity() * m_MaxSpeed/* * 0.75*/;
148
149			nVelocity += (m_MaxSpeed * (float(nElapse)) / 1000 * m_nAcceleration) / 0.5;
150
151			// Check ranges and stop
152			if (nVelocity >= m_MaxSpeed)//to max speed
153			{
154				m_pUnit->SetVelocity(1.0);
155
156				m_nAcceleration = 0.0f;
157			}
158			else if (nVelocity <= 0.0f)//stop moving
159			{
160				m_pUnit->SetVelocity(0.0f);
161				m_nAcceleration = 0.0f;
162
163				OnMoveStop();
164
165				m_vctPath.clear();
166			}
167			else//
168			{
169				if (nVelocity > 0.0f)
170					m_pUnit->SetVelocity(nVelocity/m_MaxSpeed);
171				//          TRACE("Move Weight %f\n", nWeight);
172			}
173		}
174
175		// If the unit is turning, make them turn                                                   
176		if (m_pUnit->m_lmUnit.GetDirectionsMatch() == false)
177		{
178			Matrix3 mtx3AngularMo;
179			Vector3 vct3NewDirection;
180			Vector3 vct3Base(0.0f, 0.0f, -1.0f);
181			float nVectorAngle;
182			float nOrigVectorAngle;
183			float nNewVectorAngle;
184			Vector2Ex  vct2Direction(vct3Advance.x, -vct3Advance.z);
185			if(!m_vctPath.empty() && 0 <= m_nPathIndex && m_nPathIndex < (int)m_vctPath.size())
186			{
187				m_pUnit->m_lmUnit.SetDesiredDirection(m_vctPath[m_nPathIndex] - ToAD_Vector3(vct3Current));
188			}
189
190
191			Vector2Ex  vct2DesiredDirection(m_pUnit->m_lmUnit.m_vct3DesiredDirection.x, -m_pUnit->m_lmUnit.m_vct3DesiredDirection.z);
192
193			float nAngularInc = 0;
194
195			nAngularInc = (float(nElapse)) / 1000 * m_AngularSpeed;
196
197			// Setup the vectors and normalize them
198			vct2Direction.normalise();
199			vct2DesiredDirection.normalise();
200
201			// Get the angle between the two vectors
202			nOrigVectorAngle = vct2DesiredDirection.GetAngleBetween(vct2Direction); 
203
204			// If the desired direction is clockwise to the current direction spin counter clockwise to rotate along shortest angle
205			if (vct2Direction.GetIsClockwise(vct2DesiredDirection) == true)
206				nOrigVectorAngle = -nOrigVectorAngle;
207
208			//       TRACE("Pre: nIndex %d Angle %f ", m_nPathIndex, nOrigVectorAngle);
209
210			// Cap the maximum turn amount
211			nVectorAngle = max(-m_MaxTurn, min(m_MaxTurn, nOrigVectorAngle * nAngularInc));
212
213			//       TRACE("Pst: nIndex %d Angle %f ", m_nPathIndex, nVectorAngle);
214
215			// Setup the rotation
216			mtx3AngularMo.FromAxisAngle(Vector3(0, 1, 0), (Radian)nVectorAngle);
217			Matrix4 mtx4AngularMo(mtx3AngularMo);
218
219			// Rotate the current direction
220			vct3NewDirection = mtx4AngularMo * vct3Advance;
221
222			vct2Direction.x = vct3NewDirection.x;
223			vct2Direction.y = -vct3NewDirection.z;
224			vct2Direction.normalise();
225
226			// Test out the new angle to see if it is farther away to signify the rotation is complete
227			nNewVectorAngle = vct2DesiredDirection.GetAngleBetween(vct2Direction); 
228			if (vct2Direction.GetIsClockwise(vct2DesiredDirection) == true)
229				nNewVectorAngle = -nNewVectorAngle;
230
231			//       TRACE("Angle: Pre %f Post %f\n", nOrigVectorAngle, nNewVectorAngle);
232			//       if (nNewVectorAngle > nOrigVectorAngle)
233			// Crossed zero, done turning
234			if (fabsf(nOrigVectorAngle - nNewVectorAngle) > fabsf(nOrigVectorAngle)) 
235			{
236				vct3NewDirection = ToVector3(m_pUnit->m_lmUnit.m_vct3DesiredDirection);
237
238				nNewVectorAngle = 0.0f; 
239				//          TRACE("Cmp: %d\n", m_nPathIndex);
240			}
241
242			if(!m_vctPath.empty() && 0 <= m_nPathIndex && m_nPathIndex < (int)m_vctPath.size())
243			{
244				AD_Vector3 vec3Tmp = m_vctPath[m_nPathIndex] - ToAD_Vector3(vct3Current);
245				if (ToVector3(vec3Tmp).length() < 10)       //
246				{
247					vct3NewDirection = ToVector3(vec3Tmp);
248				}
249			}
250
251
252
253			// Set the current direction
254			m_pUnit->SetDirection(ToAD_Vector3(vct3NewDirection));
255
256			// Stop if turn is completed (ie we turned less than the max)
257			if (nNewVectorAngle == 0.0f)
258			{
259				m_pUnit->m_lmUnit.SetDirectionsMatch(false);
260			}
261		}
262
263
264		// Find out why statement below is needed, needed for when a scene is switched
265		if (m_pUnit->GetVelocity() > 0 && m_nPathIndex + 1 <= (int)m_vctPath.size())
266		{
267			Real nInc = (float(nElapse)) / 1000 * (m_pUnit->GetVelocity() * m_MaxSpeed); 
268
269			//       TRACE("TimePos: %f\n", pAnimationState->getTimePosition());
270
271			vct3Position = ToVector3(m_pUnit->GetPosition());
272
273			// Get Current distance to next waypoint
274			vct3CurrentDiff.x = m_vctPath[m_nPathIndex].x - vct3Position.x;
275			vct3CurrentDiff.y = m_vctPath[m_nPathIndex].y - vct3Position.y;
276			vct3CurrentDiff.z = m_vctPath[m_nPathIndex].z - vct3Position.z;
277			nCurrDiff = vct3CurrentDiff.squaredLength();
278
279			// Advance the character
280			vct3Position = vct3Current + vct3Advance * nInc;
281
282			if (!pScene->GetTilingGrid().GetIsBlockPosition(ToAD_Vector3(vct3Position)))
283			{
284				m_pUnit->SetPosition(ToAD_Vector3(vct3Position));
285			}
286			else if (!pScene->GetIsValidPosition(ToAD_Vector3(vct3Position)))
287			{
288				TilingGrid & tg = pScene->GetTilingGrid();
289				int width = tg.GetGridWidth();
290				int depth = tg.GetGridDepth();
291				const float diff = 0.1f;
292				if (vct3Position.x <= 0.0)
293				{
294					vct3Position.x = 0.0 + diff;
295				}
296				else if (vct3Position.x >= width)
297				{
298					vct3Position.x = width - diff;
299				}
300				if (vct3Position.z <= 0.0)
301				{
302					vct3Position.z = 0.0 + diff;
303				}
304				else if (vct3Position.z >= depth)
305				{
306					vct3Position.z = depth - diff;
307				}
308			}
309			else if (pScene->GetTilingGrid().GetIsBlockPosition(ToAD_Vector3(vct3Position)))
310			{
311				TilingGrid & tg = pScene->GetTilingGrid();
312				float width = tg.GetTileWidth();
313				float depth = tg.GetTileDepth();
314
315				TilePos nTileX_old;
316				TilePos nTileZ_old;
317				tg.GridPixelsToTilePosition(m_pUnit->GetPosition(), nTileX_old, nTileZ_old);
318				TilePos nTileX_new;
319				TilePos nTileZ_new;
320				tg.GridPixelsToTilePosition(ToAD_Vector3(vct3Position), nTileX_new, nTileZ_new);
321
322				if ((int)nTileX_old-(int)nTileX_new > 1 ||
323					(int)nTileX_old-(int)nTileX_new < -1 ||
324					(int)nTileZ_old-(int)nTileZ_new > 1 ||
325					(int)nTileZ_old-(int)nTileZ_new < -1)
326				{
327					AD_WARN("ERROR!! Move over 1 tile next frame\n");
328				}
329				else
330				{
331					Vector3 adjust = vct3Position; 
332					if (tg.GetIsBlockPt((int)nTileX_new,(int)nTileZ_old))
333					{
334						int x = (int)nTileX_old>(int)nTileX_new?(int)nTileX_old:(int)nTileX_new;
335						adjust.x = x*tg.GetTileWidth();
336						adjust.x += (int)nTileX_old>(int)nTileX_new?0.1:-0.1;
337					}
338					if (tg.GetIsBlockPt((int)nTileX_old,(int)nTileZ_new))
339					{
340						int z = (int)nTileZ_old>(int)nTileZ_new?(int)nTileZ_old:(int)nTileZ_new;
341						adjust.z = z*tg.GetTileDepth();
342						adjust.z += (int)nTileZ_old>(int)nTileZ_new?0.1:-0.1;
343					}
344					vct3Position = adjust;
345				}
346			}
347			if (pScene->GetIsValidPosition(ToAD_Vector3(vct3Position)) &&
348				!pScene->GetTilingGrid().GetIsBlockPosition(ToAD_Vector3(vct3Position)))
349			{			
350				m_pUnit->SetPosition(ToAD_Vector3(vct3Position));
351			}
352
353			//Move chat bubble.
354			MoveChatBubble();
355			//       m_pScene->GetTilingGrid().GetGridYPositionInPixels(vct3Position.x, vct3Position.z, vct3Position.y);
356
357			// Get new distance to way 
358			vct3DiffToNextPt.x = m_vctPath[m_nPathIndex].x - vct3Position.x;
359			vct3DiffToNextPt.y = m_vctPath[m_nPathIndex].y - vct3Position.y;
360			vct3DiffToNextPt.z = m_vctPath[m_nPathIndex].z - vct3Position.z;
361			nNewDiff = vct3DiffToNextPt.squaredLength();
362
363			//       TRACE("Index %d/%d Curr %f Diff %f\n", m_nPathIndex, m_vctPath.size(), nCurrDiff, nNewDiff);
364			//       TRACE("            CX %f CY %f CZ %f NX %f NY %f NZ %f\n", vct3Position.x, vct3Position.y, vct3Position.z, m_vctPath[m_nPathIndex].x, m_vctPath[m_nPathIndex].y, m_vctPath[m_nPathIndex].z);
365
366
367			Vector3 vct3ToEnd;
368			int vectorSize = m_vctPath.size();
369			vct3ToEnd.x = m_vctPath[vectorSize-1].x - vct3Position.x;
370			vct3ToEnd.y = m_vctPath[vectorSize-1].y - vct3Position.y;
371			vct3ToEnd.z = m_vctPath[vectorSize-1].z - vct3Position.z;
372			Real nToEndDiff = vct3ToEnd.squaredLength();
373			Real SlowDownLength = 1*pScene->GetTileDepth();
374			if (nToEndDiff < SlowDownLength*SlowDownLength)
375			{
376				m_pUnit->SetVelocity(0.5);
377			}
378			// Get ready to stop
379			if (nNewDiff < 1.40f || (vct3CurrentDiff.dotProduct(vct3DiffToNextPt) < 0.0f)) // && nNewDiff < 1000.0f))
380			{
381				// If at the end, stop
382				if (m_nPathIndex + 1 >= (int)m_vctPath.size())
383				{
384					m_pUnit->SetVelocity(0.0f);
385					m_nAcceleration = 0.0f;
386
387					OnMoveStop(true);
388
389					m_vctPath.clear();
390				}
391				else
392				{
393					if (m_nPathIndex + 1 < (int)m_vctPath.size())
394					{
395						m_nPathIndex++;
396						SetDesiredUnitOrientation(ToVector3(m_pUnit->GetPosition()), ToVector3(m_vctPath[m_nPathIndex]));
397					}
398				}
399			}
400		}
401	}
402	return true;
403}
404
405void Move::OnHook(GeneEvt& evt)
406{
407	if (m_pUnit->GetMoveState()!=UnitBase::ums_Freeze)
408	{
409		if (evt.GetID()==UGE_Move)
410		{
411			TilePos fX = evt.GetArgX()/1000;
412			TilePos fY = evt.GetArgY()/1000;
413
414			if (!m_bWalkOnly &&  m_nCurMoveType != evt.GetArgZ())
415			{
416				m_nCurMoveType = evt.GetArgZ();
417
418				int	nNewID = m_pStaticData->GetArg(AT_LinkToMove+m_nCurMoveType);
419
420				static IStaticDataLoader* s_geneLoader = NULL;
421				if (s_geneLoader==NULL)
422				{
423					s_geneLoader = g_StaticDataFactory->GetLoader(StaticUnitGene::category);
424				}
425
426				if  (s_geneLoader)
427				{
428					StaticUnitGene* pNewSD = dynamic_cast<StaticUnitGene*>(s_geneLoader->Get(nNewID));
429					if (pNewSD)
430					{
431						OnCreate(pNewSD);
432					}
433				}
434			}
435
436			DeterminePath(fX, fY);
437		}
438	}
439
440	if ( UGE_Stop == evt.GetID() )
441	{
442		m_vctPath.clear();
443		m_nPathIndex = 0;
444
445		if (evt.GetArgX()!=0)
446		{
447			//
448			//	this is a stop that will freeze current unit
449			//	unit in freeze mode can't move to other place
450			//
451			OnMoveStop(true, true);
452		}
453		else
454		{
455			OnMoveStop();
456		}
457	}
458}
459
460
461
462bool Move::DeterminePath(TilePos nXDest, TilePos nZDest)//Scene* pScene, TilePos nXDest, TilePos nZDest, DWORD nTick)
463{
464	if(NULL == m_pUnit)
465		return false;
466
467	Scene * pScene = m_pUnit->GetScene();
468	m_nPathIndex = 0;
469	bool ret = false;
470	if (m_pUnit && pScene != NULL)
471	{
472		//ScenePath spUnit(pScene);
473		TilePos  nXStart;
474		TilePos  nZStart;
475		Vector3  vct3Grid;
476		Vector3  vctPrevious;
477		CPoint   ptGrid;
478
479		m_pUnit->GetTilePosition(nXStart, nZStart);
480
481		if ((int)nXStart == (int)nXDest &&
482			(int)nZStart == (int)nZDest)
483		{
484			return false;
485		}
486
487		m_nAcceleration = 1.0f;
488		m_vctPath.clear();
489
490		AStartPath AstartPath(pScene);
491		AstartPath.GetPath(m_vctPath, CPoint(nXStart, nZStart), CPoint(nXDest, nZDest));
492
493		m_nPathIndex = 1;
494		// If a path convert to real pixels
495		if (m_vctPath.size() > 0)
496		{
497			if (m_vctPath.size() >= 2)
498			{
499				SetDesiredUnitOrientation(ToVector3(m_vctPath[0]), ToVector3(m_vctPath[1]));
500			}
501
502			if (m_Target.x > 0.0 && m_Target.z > 0.0)//m_Target has been set
503			{
504				vctPrevious = ToVector3(m_vctPath[m_vctPath.size()-1]);
505
506				pScene->GetTilingGrid().GridToPixels(m_Target.x, m_Target.z, ToAD_Vector3(vct3Grid));
507				float dx = vct3Grid.x - vctPrevious.x;
508				float dz = vct3Grid.z - vctPrevious.z;
509				if (dx<pScene->GetTileWidth() && dx>0.0 && dz<pScene->GetTileDepth()  && dz>0.0)
510				{
511					m_vctPath[m_vctPath.size()-1] = ToAD_Vector3(vct3Grid);
512				}
513			}
514			m_pUnit->m_lmUnit.SetDirectionsMatch(false);
515			ret = true;
516			OnMoveStart();
517		}
518		else
519		{
520			OnMoveStop();
521		}
522	}
523	else
524	{
525		ret = false;
526	}
527
528
529	return ret;
530}
531
532
533/**
534* Sets the orientation so the unit faces the next position
535*
536* @param vct3Position  Reference or current position
537* @param vct3NextPosition Next position
538*/
539
540bool Move::SetDesiredUnitOrientation(const Vector3& vct3Position, const Vector3& vct3NextPosition)
541{
542	Vector3 vct3DesiredDir(vct3NextPosition.x - vct3Position.x, vct3NextPosition.y - vct3Position.y, vct3NextPosition.z - vct3Position.z);
543	Vector3 vct3Dir;
544
545	m_pUnit->m_lmUnit.SetDesiredDirection(ToAD_Vector3(vct3DesiredDir));
546
547	// Get current direction and see if they match
548	vct3Dir = ToVector3(m_pUnit->m_lmUnit.m_vct3Direction);
549
550	vct3DesiredDir.normalise();
551	vct3Dir.normalise();
552
553	// find angle between
554	float nAngle = vct3DesiredDir.dotProduct(vct3Dir);
555
556	// 1 is parallel, so give some play
557	if (vct3DesiredDir.dotProduct(vct3Dir) < ALLOW_ANGLE_PLAY)
558		m_pUnit->m_lmUnit.SetDirectionsMatch(false);
559	else
560		m_pUnit->m_lmUnit.SetDirectionsMatch(false);
561
562	return m_pUnit->m_lmUnit.GetDirectionsMatch();
563}
564
565void Move::MoveChatBubble()
566{
567	UnitRole* pRole = dynamic_cast<UnitRole*>(m_pUnit);
568	if(NULL == pRole)
569	{
570		return ;
571	}
572
573	Scene* pScene = pRole->GetScene();
574
575	//Change the position to screen from world.
576	ControlWorld2Screen  w2s;
577	ObsEvtDefault evt(Ob_Evt_Control_GetCeilingCenter,  &w2s.vctWorld);
578	pRole->Changed(evt);
579	ObsEvtDefault evtX(Ob_Evt_Control_World2Screen,  &w2s);
580	pScene->Changed(evtX);
581
582	//Move chat bubble.
583	ChatBubbleInfo infoData(ChatBubbleInfo::BubbleType_NormalPlayer);
584	infoData.m_uiRoleId = pRole->GetRoleID();
585	infoData.m_nX = WINDOW_WIDTH_NORMAL*(w2s.vctScreen.x);
586	infoData.m_nY = WINDOW_HEIGHT_NORMAL*(w2s.vctScreen.y); 
587
588	CMsgMoveChatBubble msgMove;
589	msgMove.m_pChatInfo = &infoData;
590	pScene->GetInputDevice()->Send(&msgMove);
591}
592
593void Move::SetMaxSpeed(float maxSpeed)
594{
595	m_MaxSpeed = maxSpeed;
596}
597
598float Move::GetMaxSpeed()
599{
600	return m_MaxSpeed;
601}
602
603void Move::OnMoveStart()
604{
605	m_nPrevMoveState = UnitBase::ums_Moving;
606	m_pUnit->SetMoveState(m_nPrevMoveState);
607
608	ObsEvtUSC evt(m_pStaticData->GetArg(AT_MoveState, 2));
609	m_pUnit->Changed(evt);
610
611}
612
613void Move::OnMoveStop(bool bEndPathing /* = false */, bool bIsFreeze /* = false */)
614{
615	if( NULL == m_pUnit ) return;
616
617	m_Target.x = -1.0;
618	m_Target.y = -1.0;
619	m_Target.z = -1.0;
620
621	m_pUnit->SetVelocity(0.0f);
622	if (bEndPathing)
623	{
624		CMsgMoveUnit mumBeginPathing((UnitBase*)m_pUnit);
625		mumBeginPathing.m_eMoveStateType = CMsgMoveUnit::MST_ENDPATHING;
626		m_pUnit->GetScene()->GetInputDevice()->Send(&mumBeginPathing);
627	}
628
629	if (!bIsFreeze)
630	{
631		m_nPrevMoveState = UnitBase::ums_Idle;
632		m_pUnit->SetMoveState(m_nPrevMoveState);
633	}
634	else
635	{
636		m_nPrevMoveState = UnitBase::ums_Freeze;
637		m_pUnit->SetMoveState(m_nPrevMoveState);
638	}
639
640	ObsEvtUSC evt(m_pStaticData->GetArg(AT_StopState, 1));
641	m_pUnit->Changed(evt);
642}
643
644void Move::SetMaxTurn(float maxTurn)
645{
646	m_MaxTurn = maxTurn;
647}
648
649float Move::GetMaxTurn()
650{
651	return m_MaxTurn;
652}
653
654void Move::SetAngularSpeed(float angularSpeed)
655{
656	m_AngularSpeed = angularSpeed;
657}
658
659float Move::GetAngularSpeed()
660{
661	return m_AngularSpeed;
662}