1 ////////////////////////////////////////////////////////////////////////////////
2 //            Copyright (C) 2004-2010 by The Allacrost Project
3 //                         All Rights Reserved
4 //
5 // This code is licensed under the GNU GPL version 2. It is free software and
6 // you may modify it and/or redistribute it under the terms of this license.
7 // See http://www.gnu.org/copyleft/gpl.html for details.
8 ////////////////////////////////////////////////////////////////////////////////
9 
10 /** ****************************************************************************
11 *** \file    battle_actors.cpp
12 *** \author  Viljami Korhonen, mindflayer@allacrost.org
13 *** \author  Corey Hoffstein, visage@allacrost.org
14 *** \author  Andy Gardner, chopperdave@allacrost.org
15 *** \brief   Source file for actors present in battles.
16 *** ***************************************************************************/
17 
18 #include "input.h"
19 #include "script.h"
20 
21 #include "battle.h"
22 #include "battle_actions.h"
23 #include "battle_actors.h"
24 #include "battle_effects.h"
25 #include "battle_indicators.h"
26 #include "battle_utils.h"
27 
28 using namespace std;
29 
30 using namespace hoa_utils;
31 using namespace hoa_audio;
32 using namespace hoa_video;
33 using namespace hoa_input;
34 using namespace hoa_system;
35 using namespace hoa_global;
36 using namespace hoa_script;
37 
38 namespace hoa_battle {
39 
40 namespace private_battle {
41 
42 ////////////////////////////////////////////////////////////////////////////////
43 // BattleActor class
44 ////////////////////////////////////////////////////////////////////////////////
45 
BattleActor(GlobalActor * actor)46 BattleActor::BattleActor(GlobalActor* actor) :
47 	GlobalActor(*actor),
48 	_state(ACTOR_STATE_INVALID),
49 	_global_actor(actor),
50 	_action(NULL),
51 	_x_origin(0.0f),
52 	_y_origin(0.0f),
53 	_x_location(0.0f),
54 	_y_location(0.0f),
55 	_execution_finished(false),
56 	_idle_state_time(0),
57 	_effects_supervisor(new EffectsSupervisor(this)),
58 	_indicator_supervisor(new IndicatorSupervisor(this))
59 {
60 	if (actor == NULL) {
61 		IF_PRINT_WARNING(BATTLE_DEBUG) << "constructor received NULL argument" << endl;
62 		return;
63 	}
64 
65 	// TODO: I have concerns about the copy constructor for GlobalActor. Currently it creates a copy
66 	// of every single attack point, weapon, armor, and skill. I wonder if perhaps we should only
67 	// create a copy of the attack point
68 }
69 
70 
71 
~BattleActor()72 BattleActor::~BattleActor() {
73 	// If the actor did not get a chance to execute their action, delete it
74 	if (_action != NULL) {
75 		delete _action;
76 		_action = NULL;
77 	}
78 
79 	delete _effects_supervisor;
80 	delete _indicator_supervisor;
81 }
82 
83 
84 
ChangeState(ACTOR_STATE new_state)85 void BattleActor::ChangeState(ACTOR_STATE new_state) {
86 	if (_state == new_state) {
87 		IF_PRINT_WARNING(BATTLE_DEBUG) << "actor was already in new state: " << new_state << endl;
88 		return;
89 	}
90 
91 	_state = new_state;
92 	_state_timer.Reset();
93 	switch (_state) {
94 		case ACTOR_STATE_IDLE:
95 			if (_action != NULL) {
96 				delete _action;
97 				_action = NULL;
98 			}
99 			_state_timer.Initialize(_idle_state_time);
100 			_state_timer.Run();
101 			break;
102 		case ACTOR_STATE_WARM_UP:
103 			if (_action == NULL) {
104 				IF_PRINT_WARNING(BATTLE_DEBUG) << "no action available during state change: " << _state << endl;
105 			}
106 			else {
107 				_state_timer.Initialize(_action->GetWarmUpTime());
108 				_state_timer.Run();
109 			}
110 			break;
111 		case ACTOR_STATE_READY:
112 			if (_action == NULL) {
113 				IF_PRINT_WARNING(BATTLE_DEBUG) << "no action available during state change: " << _state << endl;
114 			}
115 			else {
116 				BattleMode::CurrentInstance()->NotifyActorReady(this);
117 			}
118 			break;
119 		case ACTOR_STATE_COOL_DOWN:
120 			_execution_finished = false;
121 			if (_action == NULL) {
122 				IF_PRINT_WARNING(BATTLE_DEBUG) << "no action available during state change: " << _state << endl;
123 			}
124 			else {
125 				_state_timer.Initialize(_action->GetCoolDownTime());
126 				_state_timer.Run();
127 			}
128 			break;
129 		case ACTOR_STATE_DEAD:
130 			BattleMode::CurrentInstance()->NotifyActorDeath(this);
131 			break;
132 		default:
133 			break;
134 	}
135 }
136 
137 
138 
RegisterDamage(uint32 amount)139 void BattleActor::RegisterDamage(uint32 amount) {
140 	if (amount == 0) {
141 		IF_PRINT_WARNING(BATTLE_DEBUG) << "function called with a zero value argument" << endl;
142 		RegisterMiss();
143 		return;
144 	}
145 	if (_state == ACTOR_STATE_DEAD) {
146 		IF_PRINT_WARNING(BATTLE_DEBUG) << "function called when actor state was dead" << endl;
147 		RegisterMiss();
148 		return;
149 	}
150 
151 	SubtractHitPoints(amount);
152 	_indicator_supervisor->AddDamageIndicator(amount);
153 
154 	if (GetHitPoints() == 0)
155 		ChangeState(ACTOR_STATE_DEAD);
156 }
157 
158 
159 
RegisterHealing(uint32 amount)160 void BattleActor::RegisterHealing(uint32 amount) {
161 	if (amount == 0) {
162 		IF_PRINT_WARNING(BATTLE_DEBUG) << "function called with a zero value argument" << endl;
163 		RegisterMiss();
164 		return;
165 	}
166 	if (_state == ACTOR_STATE_DEAD) {
167 		IF_PRINT_WARNING(BATTLE_DEBUG) << "function called when actor state was dead" << endl;
168 		RegisterMiss();
169 		return;
170 	}
171 
172 	AddHitPoints(amount);
173 	_indicator_supervisor->AddHealingIndicator(amount);
174 }
175 
176 
177 
RegisterMiss()178 void BattleActor::RegisterMiss() {
179 	_indicator_supervisor->AddMissIndicator();
180 }
181 
182 
183 
RegisterStatusChange(GLOBAL_STATUS status,GLOBAL_INTENSITY intensity)184 void BattleActor::RegisterStatusChange(GLOBAL_STATUS status, GLOBAL_INTENSITY intensity) {
185 	GLOBAL_INTENSITY old_intensity = GLOBAL_INTENSITY_INVALID;
186 	GLOBAL_INTENSITY new_intensity = intensity;
187 
188 	// TODO: determine if an opposite status effect is active and account for it
189 
190 	old_intensity = _effects_supervisor->ChangeStatus(status, new_intensity);
191 
192 	// If the return value was invalid, no status was changed so do not create any indicator
193 	if (old_intensity == GLOBAL_INTENSITY_INVALID) {
194 		return;
195 	}
196 	_indicator_supervisor->AddStatusIndicator(status, old_intensity, status, new_intensity);
197 }
198 
199 
200 
ChangeSkillPoints(int32 amount)201 void BattleActor::ChangeSkillPoints(int32 amount) {
202 	uint32 unsigned_amount = static_cast<uint32>(amount);
203 
204 	// Modify actor's skill points accordingly
205 	if (amount > 0)
206 		AddSkillPoints(unsigned_amount);
207 	else if (amount < 0)
208 		SubtractSkillPoints(unsigned_amount);
209 
210 	// TODO: SP change text needs to be implemented
211 }
212 
213 
214 
Update()215 void BattleActor::Update() {
216 	_state_timer.Update();
217 	_effects_supervisor->Update();
218 	_indicator_supervisor->Update();
219 
220 	if (_state == ACTOR_STATE_IDLE) {
221 		if (_state_timer.IsFinished() == true)
222 			ChangeState(ACTOR_STATE_COMMAND);
223 	}
224 	else if (_state == ACTOR_STATE_WARM_UP) {
225 		if (_state_timer.IsFinished() == true)
226 			ChangeState(ACTOR_STATE_READY);
227 	}
228 	else if (_state == ACTOR_STATE_COOL_DOWN) {
229 		if (_state_timer.IsFinished() == true)
230 			ChangeState(ACTOR_STATE_IDLE);
231 	}
232 }
233 
234 
235 
DrawIndicators() const236 void BattleActor::DrawIndicators() const {
237 	_indicator_supervisor->Draw();
238 }
239 
240 
241 
SetAction(BattleAction * action)242 void BattleActor::SetAction(BattleAction* action) {
243 	if (action == NULL) {
244 		IF_PRINT_WARNING(BATTLE_DEBUG) << "function received NULL argument" << endl;
245 		return;
246 	}
247 	if (_state != ACTOR_STATE_COMMAND) {
248 		IF_PRINT_WARNING(BATTLE_DEBUG) << "actor was not in the command state when function was called" << endl;
249 		delete action;
250 		return;
251 	}
252 	if (_action != NULL) {
253 		IF_PRINT_WARNING(BATTLE_DEBUG) << "actor already had another action set -- overridding" << endl;
254 		delete _action;
255 	}
256 
257 	_action = action;
258 }
259 
260 
261 
TotalPhysicalDefense()262 uint32 BattleActor::TotalPhysicalDefense() {
263 	uint32 phys_defense = 0;
264 
265 	for (uint32 i = 0; i < _attack_points.size(); i++)
266 		phys_defense += _attack_points[i]->GetTotalPhysicalDefense();
267 	phys_defense /= _attack_points.size();
268 
269 	return phys_defense;
270 }
271 
272 
273 
TotalMetaphysicalDefense()274 uint32 BattleActor::TotalMetaphysicalDefense() {
275 	uint32 meta_defense = 0;
276 
277 	for (uint32 i = 0; i < _attack_points.size(); i++)
278 		meta_defense += _attack_points[i]->GetTotalMetaphysicalDefense();
279 	meta_defense /= _attack_points.size();
280 
281 	return meta_defense;
282 }
283 
284 
285 
TotalEvadeRating()286 float BattleActor::TotalEvadeRating() {
287 	float evade = 0.0f;
288 
289 	for (uint32 i = 0; i < _attack_points.size(); i++)
290 		evade += _attack_points[i]->GetTotalEvadeRating();
291 	evade /= static_cast<float>(_attack_points.size());
292 
293 	return evade;
294 }
295 
296 ////////////////////////////////////////////////////////////////////////////////
297 // BattleCharacter class
298 ////////////////////////////////////////////////////////////////////////////////
299 
BattleCharacter(GlobalCharacter * character)300 BattleCharacter::BattleCharacter(GlobalCharacter* character) :
301 	BattleActor(character),
302 	_global_character(character),
303 	_sprite_animation_alias("idle"),
304 	_animation_timer(0)
305 {
306 	if (_stamina_icon.Load("img/icons/actors/characters/" + character->GetFilename() + ".png", 45, 45) == false)
307 		PRINT_ERROR << "unable to load stamina icon for character: " << character->GetFilename() << endl;
308 
309 	_name_text.SetStyle(TextStyle("title22"));
310 	_name_text.SetText(GetName());
311 	_hit_points_text.SetStyle(TextStyle("text20"));
312 	_hit_points_text.SetText(NumberToString(GetHitPoints()));
313 	_skill_points_text.SetStyle(TextStyle("text20"));
314 	_skill_points_text.SetText(NumberToString(GetSkillPoints()));
315 }
316 
317 
318 
~BattleCharacter()319 BattleCharacter::~BattleCharacter() {
320 	// If character was about to use an item before being destructed, restore it to inventory
321 	if ((_action != NULL) && (_action->IsItemAction() == true)) {
322 		// TODO: not sure if this is necessary/safe to do.
323 // 		ItemAction* item_action = dynamic_cast<ItemAction*>(_action);
324 // 		item_action->GetItem()->IncrementCount(1);
325 	}
326 }
327 
328 
329 
ChangeState(ACTOR_STATE new_state)330 void BattleCharacter::ChangeState(ACTOR_STATE new_state) {
331 	BattleActor::ChangeState(new_state);
332 
333 	switch (_state) {
334 		case ACTOR_STATE_COMMAND:
335 			BattleMode::CurrentInstance()->NotifyCharacterCommand(this);
336 			break;
337 		case ACTOR_STATE_ACTING:
338 			// TODO: reset state timer?
339 			break;
340 		case ACTOR_STATE_DEAD:
341 			ChangeSpriteAnimation("idle");
342 			_global_character->RetrieveBattleAnimation("idle")->GetCurrentFrame()->EnableGrayScale();
343 			break;
344 		default:
345 			break;
346 	}
347 }
348 
349 
350 
Update()351 void BattleCharacter::Update() {
352 	BattleActor::Update();
353 	_animation_timer.Update();
354 
355 	// Update the active sprite animation
356 	if (IsAlive() == true) {
357 		_global_character->RetrieveBattleAnimation(_sprite_animation_alias)->Update();
358 	}
359 
360 	// If the character is executing their action,
361 	if (_state == ACTOR_STATE_ACTING) {
362 		if (_action->Execute() == true) {
363 			ChangeState(ACTOR_STATE_COOL_DOWN);
364 		}
365 	}
366 }
367 
368 
369 
DrawSprite()370 void BattleCharacter::DrawSprite() {
371 	// Draw the character sprite
372 	VideoManager->Move(_x_location, _y_location);
373 
374 	if (_sprite_animation_alias == "idle") {
375 		// no need to do anything
376 	}
377 	else if (_animation_timer.IsFinished()) {
378 		_sprite_animation_alias = "idle";
379 	}
380 	else {
381 		uint32 dist = 120 * _animation_timer.GetTimeExpired() / _animation_timer.GetDuration();
382 		VideoManager->MoveRelative(dist, 0.0f);
383 	}
384 
385 	_global_character->RetrieveBattleAnimation(_sprite_animation_alias)->Draw();
386 } // void BattleCharacter::DrawSprite()
387 
388 
389 
390 
ChangeSpriteAnimation(const std::string & alias)391 void BattleCharacter::ChangeSpriteAnimation(const std::string& alias) {
392 	_sprite_animation_alias = alias;
393 	_global_character->RetrieveBattleAnimation(_sprite_animation_alias)->ResetAnimation();
394 	_animation_timer.Reset();
395 	_animation_timer.SetDuration(300);
396 	_animation_timer.Run();
397 }
398 
399 
400 
DrawPortrait()401 void BattleCharacter::DrawPortrait() {
402 	VideoManager->SetDrawFlags(VIDEO_X_LEFT, VIDEO_Y_BOTTOM, VIDEO_BLEND, 0);
403 	VideoManager->Move(48.0f, 9.0f);
404 
405 	vector<StillImage>& portrait_frames = *(_global_character->GetBattlePortraits());
406 	float hp_percent =  static_cast<float>(GetHitPoints()) / static_cast<float>(GetMaxHitPoints());
407 
408 	if (GetHitPoints() == GetMaxHitPoints()) {
409 		portrait_frames[0].Draw();
410 	}
411 	else if (GetHitPoints() == 0) {
412 		portrait_frames[4].Draw();
413 	}
414 	else if (hp_percent > 0.75f) {
415 		portrait_frames[0].Draw();
416 		float alpha = 1.0f - ((hp_percent - 0.75f) * 4.0f);
417 		portrait_frames[1].Draw(Color(1.0f, 1.0f, 1.0f, alpha));
418 	}
419 	else if (hp_percent > 0.50f) {
420 		portrait_frames[1].Draw();
421 		float alpha = 1.0f - ((hp_percent - 0.50f) * 4.0f);
422 		portrait_frames[2].Draw(Color(1.0f, 1.0f, 1.0f, alpha));
423 	}
424 	else if (hp_percent > 0.25f) {
425 		portrait_frames[2].Draw();
426 		float alpha = 1.0f - ((hp_percent - 0.25f) * 4.0f);
427 		portrait_frames[3].Draw(Color(1.0f, 1.0f, 1.0f, alpha));
428 	}
429 	else { // (hp_precent > 0.0f)
430 		portrait_frames[3].Draw();
431 		float alpha = 1.0f - (hp_percent * 4.0f);
432 		portrait_frames[4].Draw(Color(1.0f, 1.0f, 1.0f, alpha));
433 	}
434 }
435 
436 
437 
DrawStatus(uint32 order)438 void BattleCharacter::DrawStatus(uint32 order) {
439 	// Used to determine where to draw the character's status
440 	float y_offset = 0.0f;
441 
442 	// Determine what vertical order the character is in and set the y_offset accordingly
443 	switch (order) {
444 		case 0:
445 			y_offset = 0.0f;
446 			break;
447 		case 1:
448 			y_offset = -25.0f;
449 			break;
450 		case 2:
451 			y_offset = -50.0f;
452 			break;
453 		case 3:
454 			y_offset = -75.0f;
455 			break;
456 		default:
457 			IF_PRINT_WARNING(BATTLE_DEBUG) << "invalid order argument: " << order << endl;
458 			y_offset = 0.0f;
459 	}
460 
461 	// Draw the character's name
462 	VideoManager->SetDrawFlags(VIDEO_X_RIGHT, VIDEO_Y_BOTTOM, VIDEO_BLEND, 0);
463 	VideoManager->Move(280.0f, 82.0f + y_offset);
464 	_name_text.Draw();
465 
466 	// If the swap key is being held down, draw status icons
467 	if (InputManager->SwapState()) {
468 		VideoManager->SetDrawFlags(VIDEO_X_LEFT, VIDEO_Y_BOTTOM, VIDEO_BLEND, 0);
469 		VideoManager->MoveRelative(10.0f, 0.0f);
470 		_effects_supervisor->Draw();
471 	}
472 
473 	// Otherwise, draw the HP and SP bars (bars are 90 pixels wide and 6 pixels high)
474 	else {
475 		float bar_size;
476 		VideoManager->SetDrawFlags(VIDEO_X_LEFT, VIDEO_NO_BLEND, 0);
477 
478 		// Draw HP bar in green
479 		bar_size = static_cast<float>(90 * GetHitPoints()) / static_cast<float>(GetMaxHitPoints());
480 		VideoManager->Move(312.0f, 90.0f + y_offset);
481 
482 		if (GetHitPoints() > 0) {
483 			VideoManager->DrawRectangle(bar_size, 6, Color(0.133f, 0.455f, 0.133f, 1.0f));
484 		}
485 
486 		// Draw SP bar in blue
487 		bar_size = static_cast<float>(90 * GetSkillPoints()) / static_cast<float>(GetMaxSkillPoints());
488 		VideoManager->Move(420.0f, 90.0f + y_offset);
489 
490 		if (GetSkillPoints() > 0) {
491 			VideoManager->DrawRectangle(bar_size, 6, Color(0.129f, 0.263f, 0.451f, 1.0f));
492 		}
493 
494 		// Draw the cover image over the top of the bar
495 		VideoManager->SetDrawFlags(VIDEO_BLEND, 0);
496 		VideoManager->Move(293.0f, 84.0f + y_offset);
497 		BattleMode::CurrentInstance()->GetCharacterBarCovers().Draw();
498 
499 		// TODO: The SetText calls below should not be done here. They should be made whenever the character's HP/SP
500 		// is modified. This re-renders the text every frame regardless of whether or not the HP/SP changed so its
501 		// not efficient
502 
503 		VideoManager->SetDrawFlags(VIDEO_X_CENTER, 0);
504 		// Draw the character's current health on top of the middle of the HP bar
505 		VideoManager->Move(355.0f, 90.0f + y_offset);
506 		_hit_points_text.SetText(NumberToString(GetHitPoints()));
507 		_hit_points_text.Draw();
508 
509 		// Draw the character's current skill points on top of the middle of the SP bar
510 		VideoManager->MoveRelative(110.0f, 0.0f);
511 		_skill_points_text.SetText(NumberToString(GetSkillPoints()));
512 		_skill_points_text.Draw();
513 	}
514 } // void BattleCharacter::DrawStatus()
515 
516 // /////////////////////////////////////////////////////////////////////////////
517 // BattleEnemy class
518 // /////////////////////////////////////////////////////////////////////////////
519 
BattleEnemy(GlobalEnemy * enemy)520 BattleEnemy::BattleEnemy(GlobalEnemy* enemy) :
521 	BattleActor(enemy),
522 	_global_enemy(enemy)
523 {
524 	if (_stamina_icon.Load("img/icons/actors/enemies/" + _global_actor->GetFilename() + ".png", 45, 45) == false)
525 		PRINT_ERROR << "failed to load enemy stamina icon: " << _global_actor->GetFilename() << endl;
526 }
527 
528 
529 
~BattleEnemy()530 BattleEnemy::~BattleEnemy() {
531 	delete _global_actor;
532 }
533 
534 
535 
536 // Compares the Y-coordinates of the actors, used for sorting the actors up-down when drawing
operator <(const BattleEnemy & other) const537 bool BattleEnemy::operator<(const BattleEnemy & other) const {
538 	// NOTE: this code is currently not working correctly
539 	//if ((_y_location - ((*GetActor()).GetHeight())) > (other.GetYLocation() - (*(other.GetActor()).GetHeight())))
540 	//	return true;
541 	return false;
542 }
543 
544 
545 
ChangeState(ACTOR_STATE new_state)546 void BattleEnemy::ChangeState(ACTOR_STATE new_state) {
547 	BattleActor::ChangeState(new_state);
548 
549 	vector<StillImage>& sprite_frames = *(_global_enemy->GetBattleSpriteFrames());
550 	switch (_state) {
551 		case ACTOR_STATE_COMMAND:
552 			_DecideAction();
553 			ChangeState(ACTOR_STATE_WARM_UP);
554 			break;
555 		case ACTOR_STATE_ACTING:
556 			_state_timer.Initialize(400); // TEMP: 400ms is a random time for the enemy sprite to move
557 			_state_timer.Run();
558 			break;
559 		case ACTOR_STATE_DEAD:
560 			sprite_frames[3].EnableGrayScale();
561 			break;
562 		default:
563 			break;
564 	}
565 }
566 
567 
568 
Update()569 void BattleEnemy::Update() {
570 	BattleActor::Update();
571 
572 	if (_state == ACTOR_STATE_ACTING) {
573 		if (_execution_finished == false)
574 			_execution_finished = _action->Execute();
575 
576 		if ((_execution_finished == true) && (_state_timer.IsFinished() == true))
577 			ChangeState(ACTOR_STATE_COOL_DOWN);
578 	}
579 }
580 
581 
582 
DrawSprite()583 void BattleEnemy::DrawSprite() {
584 	vector<StillImage>& sprite_frames = *(_global_enemy->GetBattleSpriteFrames());
585 
586 	// Draw the sprite's final damage frame, which should have grayscale enabled
587 	if (_state == ACTOR_STATE_DEAD) {
588 		VideoManager->Move(_x_location, _y_location);
589 		sprite_frames[3].Draw();
590 		return;
591 	}
592 
593 	// TEMP: when the actor is acting, change its x draw position to show it move forward and then
594 	// backward one tile as it completes its execution. In the future this functionality should be
595 	// replaced by modifying the enemy's draw location members directly
596 	uint32 enemy_draw_offset = 0;
597 	if (_state == ACTOR_STATE_ACTING) {
598 		if (_state_timer.PercentComplete() <= 0.50f)
599 			enemy_draw_offset = TILE_SIZE * (2.0f * _state_timer.PercentComplete());
600 		else
601 			enemy_draw_offset = TILE_SIZE * (2.0f - 2.0f * _state_timer.PercentComplete());
602 	}
603 
604 	// Draw the enemy's damage-blended sprite frames
605 	VideoManager->Move(_x_location - enemy_draw_offset, _y_location);
606 
607 	float hp_percent = static_cast<float>(GetHitPoints()) / static_cast<float>(GetMaxHitPoints());
608 
609 	// Alpha will range from 1.0 to 0.0 in the following calculations
610 	if (GetHitPoints() == GetMaxHitPoints()) {
611 		sprite_frames[0].Draw();
612 	}
613 	else if (GetHitPoints() == 0) {
614 		sprite_frames[3].Draw();
615 	}
616 	else if (hp_percent > 0.666f) {
617 		sprite_frames[0].Draw();
618 		float alpha = 1.0f - ((hp_percent - 0.666f) * 3.0f);
619 		sprite_frames[1].Draw(Color (1.0f, 1.0f, 1.0f, alpha));
620 	}
621 	else if (hp_percent >  0.333f) {
622 		sprite_frames[1].Draw();
623 		float alpha = 1.0f - ((hp_percent - 0.333f) * 3.0f);
624 		sprite_frames[2].Draw(Color(1.0f, 1.0f, 1.0f, alpha));
625 	}
626 	else { // (hp_precent > 0.0f)
627 		sprite_frames[2].Draw();
628 		float alpha = 1.0f - (hp_percent * 3.0f);
629 		sprite_frames[3].Draw(Color(1.0f, 1.0f, 1.0f, alpha));
630 	}
631 } // void BattleEnemy::DrawSprite()
632 
633 
634 
_DecideAction()635 void BattleEnemy::_DecideAction() {
636 	if (_global_enemy->GetSkills().empty() == true) {
637 		IF_PRINT_WARNING(BATTLE_DEBUG) << "enemy had no usable skills" << endl;
638 		ChangeState(ACTOR_STATE_IDLE);
639 	}
640 
641 	// TODO: this method is mostly temporary and makes no intelligent decisions about what action to
642 	// take or on what target to select. Currently this method does the following.
643 	//
644 	// (1): select the first skill that the enemy has available
645 	// (2): select a random character that is not in the dead state to target
646 	// (3): select a random attack point on the selected character target
647 	//
648 	// Therefore, only skills that target attack points on enemies are valid. No party or actor targets
649 	// will work. Obviously these needs will be addressed eventually.
650 
651 	// TEMP: select a random skill to use
652 	GlobalSkill* skill = NULL;
653 	skill = _global_enemy->GetSkills().begin()->second;
654 
655 	// TEMP: select a random living character in the party for the target
656 	BattleTarget target;
657 
658 	deque<BattleCharacter*> alive_characters = BattleMode::CurrentInstance()->GetCharacterActors();
659 	deque<BattleCharacter*>::iterator character_iterator = alive_characters.begin();
660 	while (character_iterator != alive_characters.end()) {
661 		if ((*character_iterator)->IsAlive() == false)
662 			character_iterator = alive_characters.erase(character_iterator);
663 		else
664 			character_iterator++;
665 	}
666 
667 	if (alive_characters.empty() == true) {
668 		IF_PRINT_WARNING(BATTLE_DEBUG) << "no characters were alive when enemy was selecting a target" << endl;
669 		ChangeState(ACTOR_STATE_IDLE);
670 		return;
671 	}
672 
673 	uint32 point_target = 0;
674 	BattleActor* actor_target = NULL;
675 
676 	// TEMP: select a random alive character
677 	if (alive_characters.size() == 1)
678 		actor_target = alive_characters[0];
679 	else
680 		actor_target = alive_characters[RandomBoundedInteger(0, alive_characters.size() - 1)];
681 
682 	// TEMP: select a random attack point on the target character
683 	uint32 num_points = actor_target->GetAttackPoints().size();
684 	if (num_points == 1)
685 		point_target = 0;
686 	else
687 		point_target = RandomBoundedInteger(0, num_points - 1);
688 
689 	// TEMP: Should not statically assign to target a foe point. Examine the selected skill's target type
690 	target.SetPointTarget(GLOBAL_TARGET_FOE_POINT, point_target, actor_target);
691 
692 	SetAction(new SkillAction(this, target, skill));
693 }
694 
695 } // namespace private_battle
696 
697 } // namespace hoa_battle
698