Angel 3.2
A 2D Game Prototyping Engine
Actor.cpp
1 
2 // Copyright (C) 2008-2013, Shane Liesegang
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of the copyright holder nor the names of any
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 // POSSIBILITY OF SUCH DAMAGE.
29 
30 #include "stdafx.h"
31 #include "../Actors/Actor.h"
32 
33 #include "../Infrastructure/TagCollection.h"
34 #include "../Infrastructure/Textures.h"
35 #include "../Infrastructure/TextRendering.h"
36 #include "../Infrastructure/Log.h"
37 
38 #include "../Util/StringUtil.h"
39 #include "../Util/MathUtil.h"
40 #include "../Messaging/Switchboard.h"
41 #include "../Infrastructure/World.h"
42 #include "../Scripting/LuaModule.h"
43 
44 #include <sstream>
45 
46 const float Actor::_squareVertices[] = {
47  -0.5f, 0.5f,
48  -0.5f, -0.5f,
49  0.5f, 0.5f,
50  0.5f, -0.5f,
51 };
52 
53 float Actor::_circleVertices[(CIRCLE_DRAW_SECTIONS+2)*2];
54 float Actor::_circleTextureCoords[(CIRCLE_DRAW_SECTIONS+2)*2];
55 Actor* Actor::_scriptCreatedActor = NULL;
56 bool __inittedActorCirclePoints = false;
57 
58 std::map<String, Actor*> Actor::_nameList;
59 
61 {
62  if (!__inittedActorCirclePoints)
63  {
64  Actor::_circleVertices[0] = 0.0f;
65  Actor::_circleVertices[1] = 0.0f;
66  Actor::_circleTextureCoords[0] = 0.5f;
67  Actor::_circleTextureCoords[1] = 0.5f;
68  for (int i=1; i < CIRCLE_DRAW_SECTIONS+2; i++)
69  {
70  Actor::_circleVertices[i*2] = 0.5f * cos((float) MathUtil::TwoPi * i / CIRCLE_DRAW_SECTIONS);
71  Actor::_circleVertices[(i*2)+1] = 0.5f * sin((float) MathUtil::TwoPi * i / CIRCLE_DRAW_SECTIONS);
72  Actor::_circleTextureCoords[i*2] = Actor::_circleVertices[i*2] + 0.5f;
73  Actor::_circleTextureCoords[(i*2)+1] = Actor::_circleVertices[(i*2)+1] + 0.5f;
74  }
75  __inittedActorCirclePoints = true;
76  }
77 
78  SetColor(1.0f, 1.0f, 1.0f);
79  SetAlpha(1.0f);
80  SetSize(1.0f);
81  SetRotation(0.0f);
82  SetPosition(0.0f, 0.0f);
83  SetUVs(Vector2(0.f, 0.f), Vector2(1.f, 1.f));
84  _name = "";
85 
86  _spriteNumFrames = 0;
87  _spriteCurrentFrame = 0;
88  _spriteTextureReferences[0] = -1;
89  _spriteFrameDelay = 0.0f;
90  _displayListIndex = -1;
91 
92  _layer = 0;
93 
94  _drawShape = ADS_Square;
95 }
96 
98 {
99  StringSet::iterator it = _tags.begin();
100  while (it != _tags.end())
101  {
102  String tag = *it;
103  it++;
104  Untag(tag);
105  }
106 
107  Actor::_nameList.erase(_name);
108 }
109 
110 void Actor::Update(float dt)
111 {
112  UpdateSpriteAnimation(dt);
113 
114  if (_positionInterval.ShouldStep())
115  {
116  SetPosition(_positionInterval.Step(dt));
117  if (!_positionInterval.ShouldStep())
118  {
119  if (_positionIntervalMessage != "")
120  {
121  theSwitchboard.Broadcast(new Message(_positionIntervalMessage, this));
122  }
123  }
124  }
125  if (_rotationInterval.ShouldStep())
126  {
127  SetRotation(_rotationInterval.Step(dt));
128  if (!_rotationInterval.ShouldStep())
129  {
130  if (_rotationIntervalMessage != "")
131  {
132  theSwitchboard.Broadcast(new Message(_rotationIntervalMessage, this));
133  }
134  }
135  }
136  if (_colorInterval.ShouldStep())
137  {
138  SetColor(_colorInterval.Step(dt));
139  if (!_colorInterval.ShouldStep())
140  {
141  if (_colorIntervalMessage != "")
142  {
143  theSwitchboard.Broadcast(new Message(_colorIntervalMessage, this));
144  }
145  }
146  }
147  if (_sizeInterval.ShouldStep())
148  {
149  Vector2 newSize = _sizeInterval.Step(dt);
150  SetSize(newSize.X, newSize.Y);
151  if (!_sizeInterval.ShouldStep())
152  {
153  if (_sizeIntervalMessage != "")
154  {
155  theSwitchboard.Broadcast(new Message(_sizeIntervalMessage, this));
156  }
157  }
158  }
159 }
160 
161 void Actor::UpdateSpriteAnimation(float dt)
162 {
163  if (_spriteFrameDelay > 0.0f)
164  {
165  _spriteCurrentFrameDelay -= dt;
166 
167  if (_spriteCurrentFrameDelay < 0.0f)
168  {
169  while (_spriteCurrentFrameDelay < 0.0f)
170  {
171  if (_spriteAnimType == SAT_Loop)
172  {
173  if (_spriteCurrentFrame == _spriteAnimEndFrame)
174  _spriteCurrentFrame = _spriteAnimStartFrame;
175  else
176  ++_spriteCurrentFrame;
177  }
178  else if (_spriteAnimType == SAT_PingPong)
179  {
180  if (_spriteAnimDirection == 1)
181  {
182  if (_spriteCurrentFrame == _spriteAnimEndFrame)
183  {
184  _spriteAnimDirection = -1;
185  _spriteCurrentFrame = _spriteAnimEndFrame - 1;
186  }
187  else
188  ++_spriteCurrentFrame;
189 
190  }
191  else
192  {
193  if (_spriteCurrentFrame == _spriteAnimStartFrame)
194  {
195  _spriteAnimDirection = 1;
196  _spriteCurrentFrame = _spriteAnimStartFrame + 1;
197  }
198  else
199  {
200  --_spriteCurrentFrame;
201  }
202  }
203  }
204  else if (_spriteAnimType == SAT_OneShot)
205  {
206  // If we're done with our one shot and they set an animName, let them know it's done.
207  if (_spriteCurrentFrame == _spriteAnimEndFrame)
208  {
209  // Needs to get called before callback, in case they start a new animation.
210  _spriteAnimType = SAT_None;
211 
212  if (_currentAnimName.length() > 0)
213  {
214  AnimCallback(_currentAnimName);
215  }
216  }
217  else
218  {
219  _spriteCurrentFrame += _spriteAnimDirection;
220  }
221  }
222 
223  _spriteCurrentFrameDelay += _spriteFrameDelay;
224  }
225  }
226  }
227 }
228 
230 {
231  _drawShape = drawShape;
232 }
233 
235 {
236  return _drawShape;
237 }
238 
239 void Actor::MoveTo(const Vector2& newPosition, float duration, bool smooth, String onCompletionMessage)
240 {
241  _positionInterval = Interval<Vector2>(_position, newPosition, duration, smooth);
242  _positionIntervalMessage = onCompletionMessage;
243 }
244 
245 void Actor::RotateTo(float newRotation, float duration, bool smooth, String onCompletionMessage)
246 {
247  _rotationInterval = Interval<float>(_rotation, newRotation, duration, smooth);
248  _rotationIntervalMessage = onCompletionMessage;
249 }
250 
251 void Actor::ChangeColorTo(const Color& newColor, float duration, bool smooth, String onCompletionMessage)
252 {
253  _colorInterval = Interval<Color>(_color, newColor, duration, smooth);
254  _colorIntervalMessage = onCompletionMessage;
255 }
256 
257 void Actor::ChangeSizeTo(const Vector2& newSize, float duration, bool smooth, String onCompletionMessage)
258 {
259  _sizeInterval = Interval<Vector2>(_size, newSize, duration, smooth);
260  _sizeIntervalMessage = onCompletionMessage;
261 }
262 
263 void Actor::ChangeSizeTo(float newSize, float duration, bool smooth, String onCompletionMessage)
264 {
265  ChangeSizeTo(Vector2(newSize, newSize), duration, smooth, onCompletionMessage);
266 }
267 
269 {
270  glPushMatrix();
271  glTranslatef(_position.X, _position.Y, 0.0f);
272  glRotatef(_rotation, 0, 0, 1);
273  glScalef(_size.X, _size.Y, 1.0f);
274  glColor4f(_color.R, _color.G, _color.B, _color.A);
275 
276  int textureReference = _spriteTextureReferences[_spriteCurrentFrame];
277  if (textureReference >= 0)
278  {
279  glEnable(GL_TEXTURE_2D);
280  glBindTexture(GL_TEXTURE_2D, textureReference);
281  }
282 
283  switch( _drawShape )
284  {
285  default:
286  case ADS_Square:
287  glEnableClientState(GL_VERTEX_ARRAY);
288  glEnableClientState(GL_TEXTURE_COORD_ARRAY);
289  glVertexPointer(2, GL_FLOAT, 0, _squareVertices);
290  glTexCoordPointer(2, GL_FLOAT, 0, _UV);
291  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
292  break;
293 
294  case ADS_Circle:
295  glEnableClientState(GL_VERTEX_ARRAY);
296  glEnableClientState(GL_TEXTURE_COORD_ARRAY);
297  glVertexPointer(2, GL_FLOAT, 0, _circleVertices);
298  glTexCoordPointer(2, GL_FLOAT, 0, _circleTextureCoords);
299  glDrawArrays(GL_TRIANGLE_FAN, 0, CIRCLE_DRAW_SECTIONS+2);
300  break;
301 
302  case ADS_CustomList:
303  #if ANGEL_MOBILE
304  sysLog.Printf("glCallList is unsupported in OpenGL|ES.");
305  #else
306  if (_displayListIndex < 0)
307  {
308  sysLog.Printf("Invalid display list index: %i.", _displayListIndex);
309  }
310  else
311  {
312  glCallList(_displayListIndex);
313  }
314  #endif
315  break;
316  }
317 
318  if (textureReference >= 0)
319  {
320  glDisable(GL_TEXTURE_2D);
321  }
322 
323  glPopMatrix();
324 }
325 
326 void Actor::SetSize(float x, float y)
327 {
328  float sizeX, sizeY;
329  if (x < 0.0f)
330  sizeX = 0.0f;
331  else
332  sizeX = x;
333  if (y <= 0.f)
334  sizeY = x;
335  else
336  sizeY = y;
337  _size = Vector2(sizeX, sizeY);
338 }
339 
340 void Actor::SetSize(const Vector2& newSize)
341 {
342  _size = newSize;
343  if (_size.X < 0.0f)
344  {
345  _size.X = 0.0f;
346  }
347  if (_size.Y < 0.0f)
348  {
349  _size.Y = 0.0f;
350  }
351 }
352 
353 const Vector2& Actor::GetSize() const
354 {
355  return _size;
356 }
357 
359 {
360  BoundingBox forReturn;
361  forReturn.Min = _position - (_size / 2.0f);
362  forReturn.Max = _position + (_size / 2.0f);
363  return forReturn;
364 }
365 
366 void Actor::SetPosition(float x, float y)
367 {
368  _position.X = x;
369  _position.Y = y;
370 }
371 
372 void Actor::SetPosition(const Vector2& pos)
373 {
374  _position = pos;
375 }
376 
378 {
379  return _position;
380 }
381 
382 void Actor::SetRotation(float rotation)
383 {
384  _rotation = rotation;
385 }
386 
387 const float Actor::GetRotation() const
388 {
389  return _rotation;
390 }
391 
392 const Color& Actor::GetColor() const
393 {
394  return _color;
395 }
396 
397 void Actor::SetColor(float r, float g, float b, float a)
398 {
399  _color = Color(r, g, b, a);
400 }
401 
402 void Actor::SetColor(const Color& color)
403 {
404  _color = color;
405 }
406 
407 void Actor::SetAlpha(float newAlpha)
408 {
409  _color.A = newAlpha;
410 }
411 
412 const float Actor::GetAlpha() const
413 {
414  return _color.A;
415 }
416 
417 void Actor::SetSpriteTexture(int texRef, int frame)
418 {
419  frame = MathUtil::Clamp(frame, 0, MAX_SPRITE_FRAMES - 1);
420 
421  // Make sure to bump the number of frames if this frame surpasses it.
422  if (frame >= _spriteNumFrames)
423  {
424  _spriteNumFrames = frame + 1;
425  }
426 
427  _spriteTextureReferences[frame] = texRef;
428 }
429 
430 int Actor::GetSpriteTexture(int frame) const
431 {
432  frame = MathUtil::Clamp(frame, 0, _spriteNumFrames - 1);
433 
434  return _spriteTextureReferences[frame];
435 }
436 
437 
438 // Will load the sprite if it doesn't find it in the texture cache.
439 // The texture cache caches textures by filename.
440 bool Actor::SetSprite(const String& filename, int frame, GLint clampmode, GLint filtermode, bool optional)
441 {
442  int textureReference = GetTextureReference(filename, clampmode, filtermode, optional);
443  if (textureReference == -1)
444  return false;
445 
446  SetSpriteTexture(textureReference, frame);
447  return true;
448 }
449 
451 {
452  for (int i=0; i<_spriteNumFrames; ++i)
453  {
454  _spriteTextureReferences[i] = -1;
455  }
456  _spriteAnimType = SAT_None;
457  _spriteFrameDelay = 0.0f;
458  _spriteCurrentFrame = 0;
459 }
460 
461 void Actor::SetSpriteFrame(int frame)
462 {
463  frame = MathUtil::Clamp(frame, 0, _spriteNumFrames - 1);
464 
465  if (_spriteTextureReferences[frame] == -1)
466  {
467  sysLog.Log("setSpriteFrame() - Warning: frame(" + IntToString(frame) + ") has an invalid texture reference.");
468  }
469 
470  _spriteCurrentFrame = frame;
471 }
472 
473 void Actor::PlaySpriteAnimation(float delay, spriteAnimationType animType, int startFrame, int endFrame, const char* _animName)
474 {
475  startFrame = MathUtil::Clamp(startFrame, 0, _spriteNumFrames-1);
476  endFrame = MathUtil::Clamp(endFrame, 0, _spriteNumFrames-1);
477 
478  _spriteAnimDirection = startFrame > endFrame ? -1 : 1;
479 
480  _spriteCurrentFrameDelay = _spriteFrameDelay = delay;
481  _spriteAnimType= animType;
482  _spriteAnimStartFrame = _spriteCurrentFrame = startFrame;
483  _spriteAnimEndFrame = endFrame;
484  if (_animName)
485  _currentAnimName = _animName;
486 }
487 
488 void Actor::LoadSpriteFrames(const String& firstFilename, GLint clampmode, GLint filtermode)
489 {
490  int extensionLocation = firstFilename.rfind(".");
491  int numberSeparator = firstFilename.rfind("_");
492  int numDigits = extensionLocation - numberSeparator - 1;
493 
494  // Clear out the number of frames we think we have.
495  _spriteNumFrames = 0;
496 
497  bool bValidNumber = true;
498  // So you're saying I've got a chance?
499  if (numberSeparator > 0 && numDigits > 0)
500  {
501  // Now see if all of the digits between _ and . are numbers (i.e. test_001.jpg).
502  for (int i=1; i<=numDigits; ++i)
503  {
504  char digit = firstFilename[numberSeparator+i];
505  if (digit < '0' || digit > '9')
506  {
507  bValidNumber = false;
508  break;
509  }
510  }
511  }
512 
513  // If these aren't valid, the format is incorrect.
514  if (numberSeparator == (int)String::npos || numDigits <= 0 || !bValidNumber)
515  {
516  sysLog.Log("LoadSpriteFrames() - Bad Format - Expecting somename_###.ext");
517  sysLog.Log("Attempting to load single texture: " + firstFilename);
518 
519  if (!SetSprite(firstFilename, 0, clampmode, filtermode))
520  return;
521  }
522 
523  // If we got this far, the filename format is correct.
524  String numberString;
525  // The number string is just the digits between the '_' and the file extension (i.e. 001).
526  numberString.append(firstFilename.c_str(), numberSeparator+1, numDigits);
527 
528  // Get our starting numberical value.
529  int number = atoi(numberString.c_str());
530 
531  String baseFilename;
532  // The base name is everything up to the '_' before the number (i.e. somefile_).
533  baseFilename.append( firstFilename.c_str(), numberSeparator+1);
534 
535  String extension;
536  // The extension is everything after the number (i.e. .jpg).
537  extension.append(firstFilename.c_str(), extensionLocation, firstFilename.length() - extensionLocation);
538 
539  // Keep loading until we stop finding images in the sequence.
540  while (true)
541  {
542  // Build up the filename of the current image in the sequence.
543  String newFilename = baseFilename + numberString + extension;
544 
545  // Were we able to load the file for this sprite?
546  if (!SetSprite(newFilename, _spriteNumFrames, clampmode, filtermode, true /*optional*/))
547  {
548  break;
549  }
550 
551  // Verify we don't go out of range on our hard-coded frame limit per sprite.
552  if (_spriteNumFrames >= MAX_SPRITE_FRAMES)
553  {
554  sysLog.Log("Maximum number of frames reached (" + IntToString(MAX_SPRITE_FRAMES) + "). Bailing out...");
555  sysLog.Log("Increment MAX_SPRITE_FRAMES if you need more.");
556  break;
557  }
558 
559  // Bump the number to the next value in the sequence.
560  ++number;
561 
562  // Serialize the numerical value to it so we can retrieve the string equivalent.
563  std::stringstream sstr;
564  sstr << number;
565  String newNumberString = sstr.str();
566 
567  // We assume that all the files have as many numerical digits as the first one (or greater) (i.e. 01..999).
568  // See if we need to pad with leading zeros.
569  int numLeadingZeros = numDigits - (int)newNumberString.length();
570 
571  // Do the leading zero padding.
572  for (int i=0; i<numLeadingZeros; ++i)
573  {
574  newNumberString = '0' + newNumberString;
575  }
576 
577  // Save off the newly formulated number string for the next image in the sequence.
578  numberString = newNumberString;
579  }
580 }
581 
582 void Actor::SetUVs(const Vector2& lowleft, const Vector2& upright)
583 {
584  _UV[0] = lowleft.X;
585  _UV[1] = upright.Y;
586  _UV[2] = lowleft.X;
587  _UV[3] = lowleft.Y;
588  _UV[4] = upright.X;
589  _UV[5] = upright.Y;
590  _UV[6] = upright.X;
591  _UV[7] = lowleft.Y;
592 }
593 
594 void Actor::GetUVs(Vector2 &lowleft, Vector2 &upright) const
595 {
596  lowleft.X = _UV[2];
597  lowleft.Y = _UV[3];
598  upright.X = _UV[4];
599  upright.Y = _UV[5];
600 }
601 
602 const bool Actor::IsTagged(const String& tag)
603 {
604  String searchTag = ToLower(tag);
605  StringSet::iterator it = _tags.find(searchTag);
606  if (it != _tags.end())
607  {
608  return true;
609  }
610  else
611  {
612  return false;
613  }
614 }
615 
616 void Actor::Tag(const String& newTag)
617 {
618  StringList tags = SplitString(newTag, ", ");
619  for(unsigned int i=0; i < tags.size(); i++)
620  {
621  tags[i] = ToLower(tags[i]);
622  _tags.insert(tags[i]);
623  theTagList.AddObjToTagList(this, tags[i]);
624  }
625 }
626 
627 void Actor::Untag(const String& oldTag)
628 {
629  String delTag = ToLower(oldTag);
630  _tags.erase(delTag);
631  theTagList.RemoveObjFromTagList(this, delTag);
632 }
633 
634 const StringSet& Actor::GetTags() const
635 {
636  return _tags;
637 }
638 
639 const String& Actor::SetName(String newName)
640 {
641  //overkill for sure, but who knows how many unique actors we'll need
642  static unsigned long long nameIndex = 0;
643 
644  if(newName.length() == 0)
645  {
646  newName = GetClassName();
647  }
648 
649  newName[0] = toupper(newName[0]);
650 
651  const Actor* preNamed = Actor::GetNamed(newName);
652  if ((preNamed == NULL) || (preNamed == this))
653  {
654  _name = newName;
655  }
656  else
657  {
658  _name = newName + ULLIntToString(++nameIndex);
659  }
660 
661  Actor::_nameList[_name] = this;
662 
663  return _name;
664 }
665 
666 const String& Actor::GetName() const
667 {
668  return _name;
669 }
670 
671 Actor* const Actor::GetNamed(const String& nameLookup)
672 {
673  std::map<String,Actor*>::iterator it = _nameList.find(nameLookup);
674  if (it == _nameList.end())
675  {
676  return NULL;
677  }
678  else
679  {
680  return it->second;
681  }
682 }
683 
684 Actor* Actor::Create(const String& archetype)
685 {
686  lua_State* L = LuaScriptingModule::GetLuaState();
687  lua_getglobal(L, "Actor_CreateAndRegister");
688  lua_pushstring(L, archetype.c_str());
689  if (lua_pcall(L, 1, 0, 0))
690  {
691  const char* errs = lua_tostring(L, -1);
692  sysLog.Printf("ERROR: %s\n", errs);
693  // error, will be in the stack trace
694  lua_gc(L, LUA_GCCOLLECT, 0); // garbage collect on error
695  return NULL;
696  }
697  else
698  {
699  return _scriptCreatedActor;
700  }
701 }
702 
703 void Actor::SetLayer(int layerIndex)
704 {
705  theWorld.UpdateLayer(this, layerIndex);
706 }
707 
708 void Actor::SetLayer(const String& layerName)
709 {
710  theWorld.UpdateLayer(this, layerName);
711 }