C++ Data Driven Roguelike


Project Features                                                                   Custom C++ Engine / OpenGL / Development Time - 12 Weeks

  • All game actors, entities, and items created through xml data utilizing flyweight design pattern

  • Map fog of war and line of sight raycasting

  • A* Pathfinding with g-cost parameterization

  • Artificial Intelligence: Goal Oriented Action Planning and traditional FSM all driven by xml data

  • Custom triggers, events, and interactions exposed through data

  • RPG like combat system with melee and ranged combat

  • Inventory system 

  • Player levels up by gaining experience, which increases stats determined by data

  • Custom C++ Engine integrates with Tiled for 2D map editor

  • Designer friendly error reporting system

 


Project Overview

The goal of this project was to build a roguelike game with RPG like mechanics utilizing data-driven design. All game maps, actors, entities, and items form their blueprints from xml data and individual instances are instantiated from these blueprints. The project integrates with Tiled 2D map editor, which allows designers to build maps with tilesets and place actors, entities, and items. Each actor's AI is also set through the blueprint system. A traditional FSM system can be used or Goal Oriented Action Planning. The project also features fog of war, line of sight raycasting, an inventory system, and an experience based leveling system for actors. Player movement is accomplished through the mouse. Many debugging features are available through the console and hotkeys, such as combat logs, pathing, and AI states. 

 


Data Driven Architecture

Much of the focus was on empowering designers with intuitive options through the data. The three screenshots to the side and below demonstrate a portion of the data options. Designers can specify triggers and events, which increase different disposition levels of the actors. On the example to the right, the Orc Pawn's hate level increases when damage is received. Melee and ranged combat preferences are also set within blueprints, along with starting items, starting attributes, experience levels, and more. Also on the blueprints, designers may specify additional costs for traversing different types of tiles. This is factored into the G-Cost of A* pathfinding and greatly influences an AI's pathing decisions. The complete data files are available for viewing in the code snippets at the bottom of the page.

 


Pathfinding

All actors use A* pathfinding to traverse the map. The debug lines rendered on the map represent the current path each actor is executing. The A* system supports querying into the current map and for actor's pathing properties, such as the additional G-Cost price for traversing over certain tile types. Path requests are amortized over frames to avoid hitches in framerate. Path requests are inserted into a queue which are processed and granted by the world each frame. The player generates paths by right clicking on a tile that is traversable, which is similar to the controls of a MOBA. 


Artificial Intelligence

AI behavior of actors is set through the actor blueprints. Designers have the option of using a traditional FSM system for the AI based on hate, fear, and indifference levels or they can opt to use Goal Oriented Action Planning. The color of the actor's current path trajectory indicates their current disposition to the player. A red line means hateful, yellow means neutral, green means opportunistic or looking for items, and purple means fearful. Hateful enemies will look to attack the player, neutral enemies will move indifferently, opportunistic enemies will look for items to advance their state, and fearful enemies will avoid the player at all costs. If the designer opts to use GOAP for the AI, it adds hunger and thirst into the equation. GOAP actors will project their state into the future when deciding which action set to pick. This includes potential future inventory items and state results from being in a new position. This allows the AI to incorporate potential future state from intermediate actions when making decisions. This allows for a more forward thinking AI. See the video for a demonstration of a GOAP actor looking to minimize thirst, hunger, and fear. 

 


Combat and Inventory System

The game includes a melee and ranged combat system and an inventory system. When an actor attacks another actor, their offensive stats and defensive stats are always factored in, such as block chance and armor. Finding items in the game can increase these stats and bolster an actor's survivability and offense capability. I attempted to emulate Dota2's combat system as much as possible with damage calculations and how stats such as strength, agility, and intelligence effect actors. The combat log shows the result of each combat interaction. Actor's have limited inventory capacity and a set amount of slots for wearing items. Actor's can equip a shield, helmet, chestpiece, leggings, weapon, bracers, and boots. Certain items are stackable within the inventory, such as potions. 



Project Post Mortem

What Went Well

  • The data driven architecture proved to be successful and flexible. The resulting data interface was intuitive for designers and provided enough options to make for interesting gameplay, especially given the time restrictions for development.  The project deepened my understanding of building the back-end of gameplay tools, which is an aspect of game development I am very interested in. 
  • The project deepened my understanding game design patterns. It is one thing to read about design patterns and another to actually employ and use them. I made copious use of the flyweight design pattern and the factory design pattern. The flyweight design pattern is especially useful in game development.
  • Parameterizing pathfinding G-Costs allowed for a very cost efficient way to diversify enemies. Designers can create sea creatures that will only traverse waters or Orcs that are fast on land but terrified of water.

What Went Wrong

  • Originally, I had an idea to make A* pathfinding as map agnostic as possible. I wanted to make it as portable as possible to other games and potentially label it as engine code. This was a mistake quickly realized as performance suffered. Because I made it map agnostic, I had to rely on heap allocations and or object pooling because I could not embed information into the map. Because of this, I had to aggressively amortize pathfinding requests each frame. Sometimes larger path requests would limit the game to only one path requested granted per frame! This really opened my eyes to the expensiveness of heap allocation. It wasn't until I overrided new and delete to provide my own memory management that this method became perfomant. 
  • One of the greatest challenges of GOAP is finding a performant H-Cost heuristic. I implemented GOAP into my AI system towards the end of the project and did my best to incorporate into the game's current structure and rules. This led to a very complex GOAP system, with projecting actor state into the future often being a very expensive calculation. Because of the complexity, finding a good H-Cost heuristic was very difficult. This made traversing sets of possible actions more expensive than it needed to be. 
  • While integrating a 3rd party map editor allowed me to focus on other areas of development, it did cause limitations. The exported data format was often full of bloat and contained a lot of unnecessary meta data. At the end of the project, I realized why most editors are done in-house and are integrated into the game engine. In my next project, I would opt to build the map editor as a part of the game engine if time permitted. 

What Was Learned

  • As mentioned in what went wrong, GOAP was an AI system I implemented towards the end of the project. Some systems are so complex, that the game needs to be designed around them or very careful planning and consideration are given early on. GOAP is one of these systems. I learned it was no simple task to just append a completely new system towards the end of the project. While the system was functional and exposed through data, it was not as performant as I would have hoped. The good news is I learned a great deal about GOAP!
  • Pathfinding is a lot more than just implementing an A* pathfinding algorithm. Some of the most difficult aspects of the project were the little things, such as knowing when to recalculate paths during combat to keep up with enemies based on weapon range and attack speed (since I was using mouse based movement). In future projects, I would allocate much more development time for how actors interface and use the pathfinding system
  • Heap allocation should be avoided per frame as much as possible. When doing performance profiling, this was often the bottleneck. I spent a lot of time towards the end of the project optimizing the systems and avoiding calls to new and delete. 

Code Samples

  • ActorBlueprint.hpp

    #ifndef included_ActorBlueprint
    #define included_ActorBlueprint
    #pragma once
    
    #include 
    #include 
    
    #include "EngineMacros.hpp"
    
    #include "AABB.hpp"
    #include "CollisionData.hpp"
    #include "CombatAbility.hpp"
    #include "Size.hpp"
    #include "Texture.hpp"
    #include "ObjectStats.hpp"
    #include "AIProfile.hpp"
    #include "TilePreferences.hpp"
    
    class Actor;
    class XMLNode;
    
    
    class ActorBlueprint {
    public:
    	friend class Actor;
    
    	static std::map actorBlueprintRegistry;
    	static void addActorBlueprintToRegistry( ActorBlueprint* blueprintToAdd );
    	static ActorBlueprint* getActorBlueprint( const std::string& actorType );
    	static Actor* createActorWithBlueprint( const std::string& actorType );
    	static Actor* createActorWithBlueprint( ActorBlueprint* actorBlueprint );
    
    	~ActorBlueprint();
    	explicit ActorBlueprint( const XMLNode& xmlNode );
    
    	// Inline Mutators
    	const std::string& getActorName() const;
    	void setCollisionType( CollisionType colType );
    	const CollisionType& getCollisionType() const;
    
    protected:
    	// Convenience Functions
    	void buildBlueprintFromXMLNode( const XMLNode& actorBlueprintNode );
    	void populateActorVisibilityData( Actor* actor );
    	void populateActorSpriteData( Actor* actor );
    	void populateActorPhysicsData( Actor* actor );
    	void populateActorCombatData( Actor* actor );
    	void populateActorAIProfileData( Actor* actor );
    	void populateActorAttributes( Actor* actor );
    	void populateActorStartingItems( Actor* actor );
    	void populateActorLevelData( Actor* actor );
    	void convertStringToCollisionType( const std::string& collisionTypeString );
    
    	// Construction Convenience Functions
    	void extractActorVisibilityNodeData( const XMLNode& visibilityNodeData );
    	void extractSpriteNodeData( const XMLNode& spriteNodeData );
    	void extractPhysicsNodeData( const XMLNode& physicsDataNode );
    	void extractTileMovementPreferenceData( const XMLNode& movementNode );
    	void extractCombatNodeData( const XMLNode& combatDataNode );
    	void extractAIProfileNodeData( const XMLNode& AIProfileNodeData );
    	void extractAttributeNodeData( const XMLNode& attributesNode );
    	void extractItemNodeData( const XMLNode& itemNode );
    	void extractLevelNodeData( const XMLNode& levelNode );
    
    	void setBlueprintDefaults();
    private:
    	PREVENT_COPY_AND_ASSIGN( ActorBlueprint );
    	
    	// Root Node Data
    	std::string					m_actorName;
    	std::string					m_actorDescription;
    	std::string					m_controlType;
    
    	// Visibility
    	float						m_visibilityRadius;
    
    	// Sprite Data
    	std::string					m_textureImageFilePath;
    	cbengine::Size				m_spriteSize;
    	cbengine::Texture*			m_deadTexture;
    
    	// Physics
    	cbengine::AABB				m_AABB;
    	float						m_velocity;
    	CollisionType				m_collisionType;
    
    	// Combat
    	CombatAbility				m_meleeCombat;
    	CombatAbility				m_rangedCombat;
    
    	// Items
    	bool						m_dropsItemsOnDeath;
    	// Initial Possessions
    	std::map	m_startingItems;
    
    	// Attributes
    	ObjectStats					m_baseStartingStats;
    	float						m_startingHitPoints;
    	float						m_startingManaPoints;
    
    	// Level Data
    	ObjectStats					m_levelUpStats;
    	bool						m_canGainExperience;
    	float						m_experienceValueForKill;
    	float						m_baseExperienceToLevelUp;
    	float						m_experiencePerecentageIncreaseByLevel;
    
    	// AIProfile
    	AIProfile					m_AIProfile;
    
    	// Movement
    	TilePreferences				m_tileMovementPreferences[ NUM_TILE_TYPES ];
    
    };
    
    // Inline Mutators
    inline const std::string& ActorBlueprint::getActorName() const {
    	return m_actorName;
    }
    
    
    inline void ActorBlueprint::setCollisionType( CollisionType colType ) {
    	m_collisionType = colType;
    }
    
    
    inline const CollisionType& ActorBlueprint::getCollisionType() const {
    	return m_collisionType;
    }
    
    
    namespace ActorBlueprintConstants {
    	const std::string ACTOR_BLUEPRINT_NODE_NAME = "ActorBlueprint";
    	const std::string ACTOR_NO_NAME				= "unknown";
    	const std::string ACTOR_NO_COLLISION_TYPE	= "none";
    	const std::string ACTOR_NO_COLLISION_BODY	= "none";
    	const float		  ACTOR_DEFAULT_VELOCITY    = 1.0f;
    	const float		  ACTOR_DEFAULT_MAX_HP      = 10.0f;
    	const float		  ACTOR_DEFAULT_START_HP	= 10.0f;
    	const float		  ACTOR_DEFAULT_HP_REGEN	= 0.0f;
    	const float		  ACTOR_DEFAULT_MAX_MP		= 0.0f;
    	const float		  ACTOR_DEFAULT_MP_REGEN	= 1.2f;
    }
    
    #endif
    							
  • ActorBlueprint.cpp

    #include "ActorBlueprint.hpp"
    
    #include "EngineErrorManager.hpp"
    #include "EngineSettings.hpp"
    #include "MathUtil.hpp"
    #include "Disk2D.hpp"
    #include "TextureManager.hpp"
    
    #include "EventSystem.hpp"
    #include "NamedProperties.hpp"
    
    #include "Actor.hpp"
    #include "Item.hpp"
    #include "Sprite.hpp"
    #include "CombatAbility.hpp"
    #include "XMLNode.hpp"
    #include "XMLParser.hpp"
    
    #include "AIStimulusReaction.hpp"
    
    // Static Variables
    STATIC std::map ActorBlueprint::actorBlueprintRegistry;
    
    
    // Static Functions
    STATIC void ActorBlueprint::addActorBlueprintToRegistry( ActorBlueprint* blueprintToAdd ) {
    
    	std::pair::iterator,bool> returnResult;
    	returnResult = ActorBlueprint::actorBlueprintRegistry.insert( std::pair( blueprintToAdd->getActorName(), blueprintToAdd ) );
    	if ( returnResult.second == false ) {
    		EngineErrorManager& errorManager = EngineErrorManager::getEngineErrorManager();
    		std::string errorTitle = "Error: Duplicate Actor Names";
    		std::string errorMessage = "Error: Duplicate ActorBlueprint Names Detected: ";
    		errorMessage += blueprintToAdd->getActorName();
    		errorManager.reportFatalErrorAndTerminateProgram( errorTitle, errorMessage );
    	}
    }
    
    
    STATIC ActorBlueprint* ActorBlueprint::getActorBlueprint( const std::string& actorType ) {
    
    	ActorBlueprint* blueprintToReturn = nullptr;
    	std::map::iterator it;
    	it = ActorBlueprint::actorBlueprintRegistry.find( actorType );
    	if ( it != ActorBlueprint::actorBlueprintRegistry.end() ) {
    		blueprintToReturn = it->second;
    	}
    	return blueprintToReturn;
    }
    
    // Returns nullptr if not successful
    STATIC Actor* ActorBlueprint::createActorWithBlueprint( const std::string& actorType ) {
    
    	ActorBlueprint* actorBlueprint = nullptr;
    	std::map::iterator it;
    	it = ActorBlueprint::actorBlueprintRegistry.find( actorType );
    	if ( it != ActorBlueprint::actorBlueprintRegistry.end() ) {
    		actorBlueprint = it->second;
    		return ActorBlueprint::createActorWithBlueprint( actorBlueprint );
    	}
    	return nullptr;
    }
    
    
    STATIC Actor* ActorBlueprint::createActorWithBlueprint( ActorBlueprint* actorBlueprint ) {
    
    	Actor* actorToCreate = new Actor( actorBlueprint );
    	assert( actorToCreate != nullptr );
    
    	actorBlueprint->populateActorVisibilityData( actorToCreate );
    	actorBlueprint->populateActorSpriteData( actorToCreate );
    	actorBlueprint->populateActorPhysicsData( actorToCreate );
    	actorBlueprint->populateActorCombatData( actorToCreate );
    	actorBlueprint->populateActorAIProfileData( actorToCreate );
    	actorBlueprint->populateActorAttributes( actorToCreate );
    	actorBlueprint->populateActorStartingItems( actorToCreate );
    	actorBlueprint->populateActorLevelData( actorToCreate );
    
    	return actorToCreate;
    }
    
    
    // Constructor and Destructor
    ActorBlueprint::~ActorBlueprint() {
    
    }
    
    
    ActorBlueprint::ActorBlueprint( const XMLNode& xmlNode ) {
    
    	setBlueprintDefaults();
    	buildBlueprintFromXMLNode( xmlNode );
    }
    
    
    void ActorBlueprint::setBlueprintDefaults() {
    
    	m_velocity = 0.0f;
    	m_visibilityRadius = 0.0f;
    	m_startingHitPoints = 0.0f;
    	m_startingManaPoints = 0.0f;
    	m_deadTexture = nullptr;
    	m_dropsItemsOnDeath = false;
    }
    
    // Convenience Functions
    // TODO:: Replace assertions with custom error reports
    void ActorBlueprint::buildBlueprintFromXMLNode( const XMLNode& actorBlueprintNode ) {
    
    	using namespace ActorBlueprintConstants;
    	assert( actorBlueprintNode.getNodeName() == ACTOR_BLUEPRINT_NODE_NAME );
    	XMLParser& xmlParser = XMLParser::getSharedXMLParser();
    	
    	// Grab Root Node Attribute Data
    	xmlParser.validateXMLAttributes( actorBlueprintNode, "name,controlType", "description", true );
    	xmlParser.validateXMLChildNodes( actorBlueprintNode, "ActorVisibility,SpriteData,Physics,Attributes,LevelData", "Combat,AIProfile,Items", true );
    	m_actorName = xmlParser.getStringXMLAttribute( actorBlueprintNode, "name", ACTOR_NO_NAME );
    	m_actorDescription = xmlParser.getStringXMLAttribute( actorBlueprintNode, "description" );
    	m_controlType = xmlParser.getStringXMLAttribute( actorBlueprintNode, "controlType" );
    	assert( m_actorName != ACTOR_NO_NAME ); // This handles case where someone has name="" which is not allowed
    
    	XMLNode actorVisibilityNode;
    	XMLNode spriteDataNode;
    	XMLNode physicsDataNode;
    	XMLNode AIProfileDataNode;
    	XMLNode combatDataNode;
    	XMLNode attributeDataNode;
    	XMLNode itemDataNode;
    	XMLNode levelDataNode;
    	actorBlueprintNode.getChildOfNode( "ActorVisibility", actorVisibilityNode );
    	actorBlueprintNode.getChildOfNode( "SpriteData", spriteDataNode );
    	actorBlueprintNode.getChildOfNode( "Physics", physicsDataNode );
    	actorBlueprintNode.getChildOfNode( "AIProfile", AIProfileDataNode );
    	actorBlueprintNode.getChildOfNode( "Combat", combatDataNode );
    	actorBlueprintNode.getChildOfNode( "Attributes", attributeDataNode );
    	actorBlueprintNode.getChildOfNode( "Items", itemDataNode );
    	actorBlueprintNode.getChildOfNode( "LevelData", levelDataNode );
    
    	extractActorVisibilityNodeData( actorVisibilityNode );
    	extractSpriteNodeData( spriteDataNode );
    	extractPhysicsNodeData( physicsDataNode );
    	extractAIProfileNodeData( AIProfileDataNode );
    	extractCombatNodeData( combatDataNode );
    	extractAttributeNodeData( attributeDataNode );
    	extractItemNodeData( itemDataNode );
    	extractLevelNodeData( levelDataNode );
    }
    
    
    void ActorBlueprint::extractActorVisibilityNodeData( const XMLNode& visibilityNodeData ) {
    
    	assert( visibilityNodeData.getNodeName() != "" );
    	XMLParser& xmlParser = XMLParser::getSharedXMLParser();
    
    	xmlParser.validateXMLAttributes( visibilityNodeData, "sightRadius", "", true );
    
    	m_visibilityRadius = xmlParser.getFloatXMLAttribute( visibilityNodeData, "sightRadius", 0.0f );
    	assert( m_visibilityRadius >= 0.0f );
    }
    
    
    // Construction Convenience Functions
    void ActorBlueprint::extractSpriteNodeData( const XMLNode& spriteNodeData ) {
    
    	assert(spriteNodeData.getNodeName() != "" );
    	XMLParser& xmlParser = XMLParser::getSharedXMLParser();
    	
    	xmlParser.validateXMLAttributes( spriteNodeData, "textureImage,width,height","deadTexture", true );
    	m_textureImageFilePath = xmlParser.getStringXMLAttribute( spriteNodeData, "textureImage" );
    	m_spriteSize.m_width = xmlParser.getFloatXMLAttribute( spriteNodeData, "width" );
    	m_spriteSize.m_height = xmlParser.getFloatXMLAttribute( spriteNodeData, "height" );
    	std::string deadTextureName = xmlParser.getStringXMLAttribute( spriteNodeData, "deadTexture" );
    
    	cbengine::TextureManager* sharedTextureManager = cbengine::TextureManager::sharedTextureManager();
    	m_deadTexture = sharedTextureManager->generateOrGrabExistingTexture( deadTextureName );
    
    	assert( m_spriteSize.m_width > 0.0f && m_spriteSize.m_height > 0.0f );
    	assert( m_textureImageFilePath.length() > 0 );
    }
    
    
    // TODO :: Support more physics bodies other than AABB
    void ActorBlueprint::extractPhysicsNodeData( const XMLNode& physicsDataNode ) {
    
    	using namespace ActorBlueprintConstants;
    	assert( physicsDataNode.getNodeName() != "" );
    	XMLParser& xmlParser = XMLParser::getSharedXMLParser();
    
    	xmlParser.validateXMLChildNodes( physicsDataNode, "CollisionData,Movement", "", true );
    	XMLNode collisionDataNode;
    	XMLNode aabbNode;
    	XMLNode movementNode;
    	physicsDataNode.getChildOfNode( "CollisionData", collisionDataNode );
    	xmlParser.validateXMLAttributes( collisionDataNode, "collisionType", "" );
    	std::string collisionTypeString = xmlParser.getStringXMLAttribute( collisionDataNode, "collisionType", ACTOR_NO_COLLISION_TYPE );
    	convertStringToCollisionType( collisionTypeString );
    
    	collisionDataNode.getChildOfNode( "AABB", aabbNode );
    	xmlParser.validateXMLAttributes( aabbNode, "width,height", "", true );
    	m_AABB.m_width = xmlParser.getFloatXMLAttribute( aabbNode, "width" );
    	m_AABB.m_height = xmlParser.getFloatXMLAttribute( aabbNode, "height" );
    	assert( ( m_AABB.m_width > 0.0f ) && ( m_AABB.m_height > 0.0f ) ); // It is possible for attribute to be present and be 0.0f
    
    	physicsDataNode.getChildOfNode( "Movement", movementNode );
    	xmlParser.validateXMLAttributes( movementNode, "", "velocity", true );
    	m_velocity = xmlParser.getFloatXMLAttribute( movementNode, "velocity", ACTOR_DEFAULT_VELOCITY );
    
    	extractTileMovementPreferenceData( movementNode );
    }
    
    
    void ActorBlueprint::extractTileMovementPreferenceData( const XMLNode& movementNode ) {
    
    	assert( movementNode.getNodeName() != "" );
    
    	XMLParser& xmlParser = XMLParser::getSharedXMLParser();
    
    	std::vector tilePreferenceNodes;
    	movementNode.getAllChildrenOfNode( tilePreferenceNodes );
    
    	for ( size_t i = 0; i < tilePreferenceNodes.size(); ++i ) {
    
    		XMLNode& tilePrefNode = tilePreferenceNodes[i];
    		std::string tilePrefNodeNameAsString = tilePrefNode.getNodeName();
    		TileType prefTileType = TilePreferences::convertStringToTileType( tilePrefNodeNameAsString );
    
    		float additionalGCostPenalty = xmlParser.getFloatXMLAttribute( tilePrefNode, "additionalGCost", 0.0f );
    		float movementSpeedCoefficient = xmlParser.getFloatXMLAttribute( tilePrefNode, "movementSpeedCoefficient", 1.0f );
    
    		TilePreferences& tilePref = m_tileMovementPreferences[ prefTileType ];	
    		tilePref.m_tileType = prefTileType;
    		tilePref.m_additionalGCost = additionalGCostPenalty;
    		tilePref.m_movementSpeedCoefficient = movementSpeedCoefficient;
    
    	}
    }
    
    
    void ActorBlueprint::extractCombatNodeData( const XMLNode& combatDataNode ) {
    
    	using namespace ActorBlueprintConstants;
    	if ( combatDataNode.getNodeName() == "" ) {
    		// Node is optional, so no need for an assertion
    		return;
    	}
    	XMLParser& xmlParser = XMLParser::getSharedXMLParser();
    	
    	xmlParser.validateXMLAttributes( combatDataNode, "", "meleeWeightPreference,rangeWeightPreference", true );
    	xmlParser.validateXMLChildNodes( combatDataNode, "","Melee,Ranged", true );
    
    	m_meleeCombat.m_weightPreferenceForCombatType = xmlParser.getFloatXMLAttribute( combatDataNode, "meleeWeightPreference", 0.30f );
    	m_rangedCombat.m_weightPreferenceForCombatType = xmlParser.getFloatXMLAttribute( combatDataNode, "rangeWeightPreference", 0.30f );
    
    	XMLNode meleeNode;
    	combatDataNode.getChildOfNode( "Melee", meleeNode );
    	
    	if ( meleeNode.getNodeName() == "Melee" ) {
    		// Actor has melee abilities
    		xmlParser.validateXMLAttributes( meleeNode, "baseDamage,baseAttackTimeCooldownSeconds,baseRange", "", true );
    		m_meleeCombat.m_combatType = TYPE_MELEE;
    		m_meleeCombat.m_baseDamage = xmlParser.getFloatXMLAttribute( meleeNode, "baseDamage", 0.0f );
    		m_meleeCombat.m_baseAttackTimeCooldownSeconds = xmlParser.getFloatXMLAttribute( meleeNode, "baseAttackTimeCooldownSeconds", 0.0f );
    		m_meleeCombat.m_baseAttackRange = xmlParser.getFloatXMLAttribute( meleeNode, "baseRange", 0.0f );	
    	}
    
    	XMLNode rangedNode;
    	combatDataNode.getChildOfNode( "Ranged", rangedNode );
    
    	if ( rangedNode.getNodeName() == "Ranged" ) {
    		// 
    		xmlParser.validateXMLAttributes( rangedNode, "baseDamage,baseAttackTimeCooldownSeconds,baseRange", "", true );
    
    		m_rangedCombat.m_combatType = TYPE_RANGED;
    		m_rangedCombat.m_baseDamage = xmlParser.getFloatXMLAttribute( rangedNode, "baseDamage", 0.0f );
    		m_rangedCombat.m_baseAttackTimeCooldownSeconds = xmlParser.getFloatXMLAttribute( rangedNode, "baseAttackTimeCooldownSeconds", 0.0f );
    		m_rangedCombat.m_baseAttackRange = xmlParser.getFloatXMLAttribute( rangedNode, "baseRange", 0.0f );
    	}
    }
    
    
    void ActorBlueprint::extractAIProfileNodeData( const XMLNode& AIProfileNodeData ) {
    
    	if ( m_controlType == "Player" || AIProfileNodeData.getNodeName() == "" ) {
    		return;
    	}
    
    	XMLParser& xmlParser = XMLParser::getSharedXMLParser();
    
    	xmlParser.validateXMLAttributes( AIProfileNodeData, "", "looksForAmmoWhenFleeing,patrolsWhenIdle", true );
    	xmlParser.validateXMLChildNodes( AIProfileNodeData, "HateLevel,FearLevel,IndifferenceLevel", "", true );
    
    	m_AIProfile.m_looksForAmmoWhenFleeing = xmlParser.getBoolXMLAttribute( AIProfileNodeData, "looksForAmmoWhenFleeing", false );
    	m_AIProfile.m_patrolsWhenIdle = xmlParser.getBoolXMLAttribute( AIProfileNodeData, "patrolsWhenIdle", false );
    
    	XMLNode hateNode;
    	XMLNode fearNode;
    	XMLNode indifferenceNode;
    
    	AIProfileNodeData.getChildOfNode( "HateLevel", hateNode );
    	AIProfileNodeData.getChildOfNode( "FearLevel", fearNode );
    	AIProfileNodeData.getChildOfNode( "IndifferenceLevel", indifferenceNode );
    
    	float startHateValue = xmlParser.getFloatXMLAttribute( hateNode, "startValue", 0.0f );
    	float startFearValue = xmlParser.getFloatXMLAttribute( fearNode, "startValue", 0.0f );
    	float startIndifferenceValue = xmlParser.getFloatXMLAttribute( indifferenceNode, "startValue", 0.0f );
    	m_AIProfile.setHateLevel( startHateValue );
    	m_AIProfile.setFearLevel( startFearValue );
    	m_AIProfile.setIndifferenceLevel( startIndifferenceValue );
    
    	std::vector hateChildNodes;
    	hateNode.getAllChildrenOfNode( hateChildNodes );
    	
    	// TODO:: 
    	// For now hardcoding these for testing
    	for ( size_t i = 0; i < hateChildNodes.size(); ++i ) {
    
    		XMLNode& hateChildNode = hateChildNodes[i];
    		if ( hateChildNode.getNodeName() == "OnDamageReceived" ) {
    
    			AIStimulusReaction stimReaction( REACTION_TYPE_HATE, CONDITION_INCREASE_LEVEL );
    			stimReaction.m_increaseMultiplier = xmlParser.getFloatXMLAttribute( hateChildNode, "damageAmountMultiplier", 0.0f );
    			m_AIProfile.addStimulusAndReaction( STIMULUS_ON_DAMAGE_RECEIVED, stimReaction );
    		}
    	}
    
    	std::vector fearChildNodes;
    	fearNode.getAllChildrenOfNode( fearChildNodes );
    	
    	for ( size_t i = 0; i < fearChildNodes.size(); ++i ) {
    
    		XMLNode& fearChildNode = fearChildNodes[i];
    		if ( fearChildNode.getNodeName() == "OnHealthChange" ) {
    
    			float ifHealthBelowPercentValue = xmlParser.getFloatXMLAttribute( fearChildNode, "ifHealthBelowPercent", -1.0f );
    			float ifHealthAbovePercentValue = xmlParser.getFloatXMLAttribute( fearChildNode, "ifHealthAbovePercent", -1.0f );
    			float setLevel = xmlParser.getFloatXMLAttribute( fearChildNode, "setLevel", 0.0f );
    			bool oneTime = xmlParser.getBoolXMLAttribute( fearChildNode, "oneTime", false );
    			if ( ifHealthBelowPercentValue > 0.0f ) {
    				
    				AIStimulusReaction stimReaction( REACTION_TYPE_FEAR, CONDITION_SET_WHEN_BELOW_THRESHOLD );
    				stimReaction.m_thresholdValue = ifHealthBelowPercentValue / 100.0f;
    				stimReaction.m_thresholdIncreaseValue = setLevel;
    				stimReaction.m_oneTimeReaction = oneTime;
    				m_AIProfile.addStimulusAndReaction( STIMULUS_ON_HEALTH_THRESHOLD_CROSSED, stimReaction );
    
    			} else if ( ifHealthAbovePercentValue > 0.0f ) {
    
    				AIStimulusReaction stimReaction( REACTION_TYPE_FEAR, CONDITION_SET_WHEN_ABOVE_THRESHOLD );
    				stimReaction.m_thresholdValue = ifHealthAbovePercentValue / 100.0f;
    				stimReaction.m_thresholdIncreaseValue = setLevel;
    				stimReaction.m_oneTimeReaction = oneTime;
    				m_AIProfile.addStimulusAndReaction( STIMULUS_ON_HEALTH_THRESHOLD_CROSSED, stimReaction );
    			}
    			
    		}
    	}
    }
    
    
    void ActorBlueprint::extractAttributeNodeData( const XMLNode& attributesNode ) {
    
    	using namespace ActorBlueprintConstants;
    	assert( attributesNode.getNodeName() != "" );
    	XMLParser& xmlParser = XMLParser::getSharedXMLParser();
    
    	xmlParser.validateXMLChildNodes( attributesNode, "Health,Mana,Strength,Agility,Intelligence,Armor", "", true );
    	XMLNode healthNode;
    	XMLNode manaNode;
    	XMLNode strengthNode;
    	XMLNode agilityNode;
    	XMLNode intelligenceNode;
    	XMLNode armorNode;
    	attributesNode.getChildOfNode( "Health", healthNode );
    	attributesNode.getChildOfNode( "Mana", manaNode );
    	attributesNode.getChildOfNode( "Strength", strengthNode );
    	attributesNode.getChildOfNode( "Agility", agilityNode );
    	attributesNode.getChildOfNode( "Intelligence", intelligenceNode );
    	attributesNode.getChildOfNode( "Armor", armorNode );
    
    	
    	xmlParser.validateXMLAttributes( healthNode, "maxHitPoints,startHitPoints", "healthRegen", true );
    
    	// Health and Mana
    	// TODO:: FloatRange with Starting and Max
    	m_startingHitPoints = xmlParser.getFloatXMLAttribute( healthNode, "startHitPoints", ACTOR_DEFAULT_START_HP );
    	m_startingManaPoints = xmlParser.getFloatXMLAttribute( manaNode, "startManaPoints", ACTOR_DEFAULT_MAX_MP );
    
    	float startingMaxBaseHitPoints = xmlParser.getFloatXMLAttribute( healthNode, "maxHitPoints", ACTOR_DEFAULT_MAX_HP );
    	float baseHitPointRegen = xmlParser.getFloatXMLAttribute( healthNode, "healthRegen", ACTOR_DEFAULT_HP_REGEN );
    	float startingMaxBaseManaPoints = xmlParser.getFloatXMLAttribute( manaNode, "maxManaPoints", ACTOR_DEFAULT_MAX_MP );
    	float baseManaPointRegen = xmlParser.getFloatXMLAttribute( manaNode, "manaRegen", ACTOR_DEFAULT_MP_REGEN );
    	
    	m_baseStartingStats.addToStat( STAT_HEALTH, startingMaxBaseHitPoints );
    	m_baseStartingStats.addToStat( STAT_HEALTH_REGEN, baseHitPointRegen );
    	m_baseStartingStats.addToStat( STAT_MANA, startingMaxBaseManaPoints );
    	m_baseStartingStats.addToStat( STAT_MANA_REGEN, baseManaPointRegen );
    
    	// Strength, Agility, Intelligence, and Armor
    	float startingStrengthValue = xmlParser.getFloatXMLAttribute( strengthNode, "baseStrengthValue", 0.0f );
    	float startingAgilityValue = xmlParser.getFloatXMLAttribute( agilityNode, "baseAgilityValue", 0.0f );
    	float startingIntelligenceValue = xmlParser.getFloatXMLAttribute( intelligenceNode, "baseIntelligenceValue", 0.0f );
    	float startingArmorValue = xmlParser.getFloatXMLAttribute( armorNode, "baseArmorValue", 0.0f );
    
    	m_baseStartingStats.addToStat( STAT_STRENGTH, startingStrengthValue );
    	m_baseStartingStats.addToStat( STAT_AGILITY, startingAgilityValue );
    	m_baseStartingStats.addToStat( STAT_INTELLIGENCE, startingIntelligenceValue );
    	m_baseStartingStats.addToStat( STAT_ARMOR, startingArmorValue );
    }
    
    
    void ActorBlueprint::extractItemNodeData( const XMLNode& itemDataNode ) {
    	// Note: This is an optional child node
    	if ( itemDataNode.getNodeName() == "" ) {
    		return;
    	}
    
    	XMLParser& xmlParser = XMLParser::getSharedXMLParser();
    
    	xmlParser.validateXMLAttributes( itemDataNode, "", "dropsItemsOnDeath", true );
    
    	m_dropsItemsOnDeath = xmlParser.getBoolXMLAttribute( itemDataNode, "dropsItemsOnDeath", false );
    
    	std::vector itemChildNodes;
    	itemDataNode.getAllChildrenOfNode( itemChildNodes );
    
    	for ( size_t i = 0; i < itemChildNodes.size(); ++i ) {
    
    		XMLNode& itemNode = itemChildNodes[i];
    		xmlParser.validateXMLAttributes( itemNode, "name","count", true );
    		std::string itemName = xmlParser.getStringXMLAttribute( itemNode, "name", "None" );
    		int count = xmlParser.getIntXMLAttribute( itemNode, "count", 1 );
    		if ( itemName != "None" ) {
    			m_startingItems.insert( std::pair( itemName, count ) );
    		}
    	}
    	
    }
    
    
    void ActorBlueprint::extractLevelNodeData( const XMLNode& levelNode ) {
    
    	assert( levelNode.getNodeName() != "" );
    
    	XMLParser& xmlParser = XMLParser::getSharedXMLParser();
    
    	xmlParser.validateXMLAttributes( levelNode, "experienceValueWhenKilled", "canGainExperience,nextLevelExperience,eachLevelIncreasesByPercent" );
    
    	m_experienceValueForKill = xmlParser.getFloatXMLAttribute( levelNode, "experienceValueWhenKilled", 0.0f );
    	m_canGainExperience = xmlParser.getBoolXMLAttribute( levelNode, "canGainExperience", false );
    	m_baseExperienceToLevelUp = xmlParser.getFloatXMLAttribute( levelNode, "nextLevelExperience", 200.0f );
    	m_experiencePerecentageIncreaseByLevel = xmlParser.getFloatXMLAttribute( levelNode, "eachLevelIncreaseByPercent", 0.05f );
    
    	XMLNode levelUpIncreaseNode;
    	levelNode.getChildOfNode( "LevelUpIncrease", levelUpIncreaseNode );
    
    	XMLNode healthIncrementNode;
    	levelUpIncreaseNode.getChildOfNode( "Health", healthIncrementNode );
    	float maxHealthIncrement = xmlParser.getFloatXMLAttribute( healthIncrementNode, "maxHitPoints", 0.0f );
    	m_levelUpStats.addToStat( STAT_HEALTH, maxHealthIncrement );
    	
    	float healthRegenIncrement = xmlParser.getFloatXMLAttribute( healthIncrementNode, "healthRegen", 0.0f );
    	m_levelUpStats.addToStat( STAT_HEALTH_REGEN, healthRegenIncrement );
    
    	XMLNode manaIncrementNode;
    	levelUpIncreaseNode.getChildOfNode( "Mana", manaIncrementNode );
    	float maxManaIncrement = xmlParser.getFloatXMLAttribute( manaIncrementNode, "maxManaPoints", 0.0f );
    	m_levelUpStats.addToStat( STAT_MANA, maxManaIncrement );
    
    	float manaRegenIncrement = xmlParser.getFloatXMLAttribute( manaIncrementNode, "manaRegen", 0.0f );
    	m_levelUpStats.addToStat( STAT_MANA_REGEN, manaRegenIncrement );
    
    	XMLNode strengthIncrementNode;
    	levelUpIncreaseNode.getChildOfNode( "Strength", strengthIncrementNode );
    	float strengthIncrement = xmlParser.getFloatXMLAttribute( strengthIncrementNode, "strengthValue", 0.0f );
    	m_levelUpStats.addToStat( STAT_STRENGTH, strengthIncrement );
    
    	XMLNode agilityIncrementNode;
    	levelUpIncreaseNode.getChildOfNode( "Agility", agilityIncrementNode );
    	float agilityIncrement = xmlParser.getFloatXMLAttribute( agilityIncrementNode, "agilityValue", 0.0f );
    	m_levelUpStats.addToStat( STAT_AGILITY, agilityIncrement );
    
    	XMLNode intelligenceIncrementNode;
    	levelUpIncreaseNode.getChildOfNode( "Intelligence", intelligenceIncrementNode );
    	float intelligenceIncrement = xmlParser.getFloatXMLAttribute( intelligenceIncrementNode, "intelligenceValue", 0.0f );
    	m_levelUpStats.addToStat( STAT_INTELLIGENCE, intelligenceIncrement );
    
    	XMLNode armorIncrementNode;
    	levelUpIncreaseNode.getChildOfNode( "Armor", armorIncrementNode );
    	float armorValue = xmlParser.getFloatXMLAttribute( armorIncrementNode, "armorValue", 0.0f );
    	m_levelUpStats.addToStat( STAT_ARMOR, armorValue );
    }
    
    
    void ActorBlueprint::populateActorVisibilityData( Actor* actor ) {
    
    	actor->setVisibilityRadius( m_visibilityRadius );
    }
    
    
    void ActorBlueprint::populateActorSpriteData( Actor* actor ) {
    
    	Sprite* actorSprite = new Sprite( m_textureImageFilePath, m_spriteSize );
    	assert(actorSprite != nullptr );
    	actor->setActorSprite( actorSprite );
    }
    
    
    void ActorBlueprint::populateActorPhysicsData( Actor* actor ) {
    
    	actor->setAABB2( m_AABB ); // TODO:: Allow for more physics bodies for actors
    	cbengine::Disk2D diskToSet;
    	diskToSet.radius = m_AABB.m_width * DISK_RADIUS_ADJUSTMENT_FROM_AABB;
    	actor->setDisk2D( diskToSet );
    
    	for ( size_t i = 0; i < NUM_TILE_TYPES; ++i ) {
    
    		actor->m_tileMovementPreferences[i] = m_tileMovementPreferences[i];
    	}
    }
    
    
    void ActorBlueprint::populateActorCombatData( Actor* actor ) {
    
    	actor->setMeleeCombatAbility( m_meleeCombat );
    	actor->setRangedCombatAbility( m_rangedCombat );	
    }
    
    
    void ActorBlueprint::populateActorAIProfileData( Actor* actor ) {
    
    	actor->m_AIProfile = m_AIProfile;
    }
    
    
    void ActorBlueprint::populateActorAttributes( Actor* actor ) {
    
    	actor->m_baseActorStats.modifyObjectStats( m_baseStartingStats );
    	actor->setCurrentHitPoints( m_startingHitPoints );
    	actor->setCurrentManaPoints( m_startingManaPoints );
    }
    
    
    void ActorBlueprint::populateActorStartingItems( Actor* actor ) {
    
    	std::map::iterator it;
    	for ( it = m_startingItems.begin(); it != m_startingItems.end(); ++it ) {
    
    		const std::string& itemName = it->first;
    		int itemCount = it->second;
    		Inventory& inventory = actor->getInventory();
    		ItemBlueprint* blueprint = ItemBlueprint::getItemBlueprint( itemName );
    		
    		assert( blueprint != nullptr ); // TODO:: Write error message code
    
    		for ( int i = 0; i < itemCount; ++i ) {
    
    			Item* itemToCreate = ItemBlueprint::createItemWithBlueprint( blueprint );
    			assert( itemToCreate != nullptr );
    			NamedProperties params;
    			std::string objectProp = "GameItem";
    			std::string eventName = "NewGameItem";
    			params.setProperty( objectProp, itemToCreate );
    			cbengine::FireEvent( eventName, params ); // To inform GameWorld of a new object
    			inventory.addItemToInventory( itemToCreate );
    		}
    	}
    }
    
    
    void ActorBlueprint::populateActorLevelData( Actor* actor ) {
    
    	actor->m_nextLevelExperience = m_baseExperienceToLevelUp;
    	actor->m_currentExperience = 0.0f;
    
    }
    
    
    void ActorBlueprint::convertStringToCollisionType( const std::string& collisionTypeString ) {
    	// TODO
    	UNUSED( collisionTypeString );
    	m_collisionType = COLLISION_TYPE_PLAYER;
    }
    							
  • Actor.hpp

    #ifndef included_Actor
    #define included_Actor
    #pragma once
    
    #include "GameObject.hpp"
    
    #include 
    #include 
    
    #include "ControllerDelegate.hpp"
    #include "MathUtil.hpp"
    #include "ActorBlueprint.hpp"
    #include "Inventory.hpp"
    #include "CombatAbility.hpp"
    #include "ObjectStats.hpp"
    #include "DamageProfile.hpp"
    #include "HealthBar.hpp"
    #include "AIProfile.hpp"
    #include "TilePreferences.hpp"
    #include "Tile.hpp"
    
    const float DURATION_TO_DELETE_AI = 5.0f;
    
    // TODO:: Move this to be data driven...
    const float STRENGTH_TO_HP_COEFFICIENT = 1.5f;
    const float STRENGTH_TO_HP_REGEN_COEFFICIENT = 0.02f;
    const float STRENGTH_TO_PHYSICAL_MELEE_DMG_COEFFICIENT = 0.25f;
    const float AGILITY_TO_PHYSICAL_RANGE_DMG_COEFFICIENT = 0.20f;
    const float INTELLIGENCE_TO_MAGIC_DMG_COEFFICIENT = 0.15f;
    const float INTELLIGENCE_TO_MP_COEFFICIENT = 0.70f;
    const float INTELLIGENCE_TO_MP_REGEN_COEFFICIENT = 0.04f;
    const float AGILITY_TO_ARMOR_COEFFICIENT = 0.15f;
    const float AGILITY_TO_DODGE_CHANCE_COEFFICIENT = 0.30f;
    const float PROJECTILE_DAMAGE_PENALITY = 0.70f;
    const float PROJECTILE_SPAWN_ADJUSTMENT_COEFFICIENT = 0.50f;
    const float DODGE_CHANCE_COEFFICIENT = 0.05f;
    const float ARMOR_DAMAGE_REDUCTION_COEFFICIENT = 0.06f;
    const float MAX_ARMOR_DAMAGE_REDUCTION = 0.75f;
    const float MAX_DODGE_CHANCE = 0.60f;
    
    // For Reference... Equations
    // Damage reduction = ((0.06 * armor) / (1 + 0.06 * armor))
    // Dodge Chance = ((0.05 * dodge) / (1 + 0.05 * dodge)
    
    class Sprite;
    class Projectile;
    class TileMap;
    
    
    // TODO:: Stunned, Blind, Confused, etc...
    typedef enum {
    
    	STATE_ACTOR_ALIVE,
    	STATE_ACTOR_DEAD,
    } ActorState;
    
    
    typedef enum {
    
    	COMBAT_MODE_NONE,
    	COMBAT_MODE_MELEE,
    	COMBAT_MODE_RANGED,
    } CombatMode;
    
    
    typedef enum {
    
    	STATE_VISIBLE,
    	STATE_REMEMBER_BUT_NOT_IN_VIEW,
    	STATE_UNDISCOVERED,
    } FogOfWarState;
    
    
    typedef enum {
    
    	AI_STATE_IDLE,
    	AI_STATE_PATROLLING,
    	AI_STATE_SEEKING_COMBAT_WITH_PLAYER,
    	AI_STATE_COMBAT_WITH_PLAYER,
    	AI_STATE_FLEEING_PLAYER
    
    } AIActorState;
    
    class Actor : public GameObject {
    public:
    	friend class PlayerController;
    	friend class AIController;
    	friend class ActorBlueprint;
    
    	explicit Actor( ActorBlueprint* blueprint );
    	virtual ~Actor();
    
    	// Core Function Overrides
    	virtual void update( float deltaSeconds );
    	virtual void update( float deltaSeconds, Tile* tileStandingOn );
    	virtual void render( float deltaSeconds ) const;
    
    	// Core Functions
    	void changeActorState( ActorState stateToChange );
    	void updateVisibility( float deltaSeconds, TileMap* currentTileMap );
    	void updateVisibilityUsingRayCasting( float deltaSeconds, TileMap* currentTileMap );
    	void adjustVisibilityMapForNewTileMap( const TileMap* currentTileMap );
    	void markEntireMapAsVisibleToActor( TileMap* currentTileMap );
    	void onActorKill( Actor* actorKilled, const DamageProfile& damageProfileOfKillingBlow );
    	float getExperienceValueWhenKilled() const;
    	bool canActorGainExperience() const;
    
    	// AI Core Functions
    	const AIActorState& getAIActorState() const;
    	void changeAIActorState( AIActorState newState );
    	AIProfile& getAIProfile();
    	bool isAI() const;
    
    	// Combat
    	virtual bool canPerformMeleeAttack() const;
    	virtual bool canPerformRangedAttack();
    	virtual bool hasAmmoForRangedAttack();
    	virtual bool hasAbilityToPerformMeleeAttack();
    	virtual void performMeleeAttack( const PositionVec2& targetPosition );
    	virtual void performRangedAttack( const PositionVec2& targetPosition, Projectile* projectileToFire );
    	float determineMeleeDamage();
    	void receiveMeleeAttack( float damageAmount, DamageProfile& damageProfile );
    	void receiveRangedAttack( float damageAmount, DamageProfile& damageProfile );
    
    	// Movement
    	const TilePreferences* getActorTilePreferences() const;
    
    	// Items
    	bool canActorReceiveItem( Item* itemToReceive );
    	bool receiveItemAndTakeOwnership( Item* itemToReceive );
    	Item* getProjectileContainerCurrentlyEquip();
    
    	// Life Cycle Functions
    	virtual void onCreate();
    	virtual void onPause();
    	virtual void onDestroy();
    
    	// Stats
    	void getCurrentActorStats( ObjectStats& out_actorStats );
    	float determineCurrentMaxHealth();
    	float determineCurrentMaxMana();
    
    	// Debug
    	void outputCurrentActorStatsToConsole();
    
    	// Inline Mutators
    	void setVisibilityRadius( float visibilityRadius );
    	float getVisibilityRadius() const;
    	bool isPlayer() const;
    	void setActorBlueprint( ActorBlueprint* blueprint );
    	const ActorBlueprint* getActorBlueprint() const;
    	void setActorSprite( Sprite* sprite );
    	const Sprite* getActorSprite() const;
    	void setCurrentHitPoints( float hitPoints );
    	const float getCurrentHitPoints() const;
    	void setCurrentManaPoints( float manaPoints );
    	const float getCurrentManaPoints() const;
    	Inventory& getInventory(); // For now until later design decision is made
    	const float getMaxMovementVelocity() const;
    	const ActorState& getActorState() const;
    	const std::string& getActorName() const;
    	bool doesDropsItemsOnDeath() const;
    
    	void setMeleeCombatAbility( const CombatAbility& meleeCombatAbility );
    	const CombatAbility& getMeleeCombatAbility() const;
    	void setRangedCombatAbility( const CombatAbility& rangedCombatAbility );
    	const CombatAbility& getRangedCombatAbility() const;
    	void setCombatMode( const CombatMode& combatModeToSet );
    	const CombatMode& getCombatMode() const;
    	const std::vector& getFogOfWarStateVectorForActor() const;
    
    	void setActionDelegate( ControllerDelegate* actionDelegate );
    protected:
    	void updateActorState( float deltaSeconds );
    	void updateActorAlive( float deltaSeconds );
    	void updateActorDead( float deltaSeconds );
    	void updatePhysics( float deltaSeconds, Tile* tileStandingOn );
    	void updateCooldowns( float deltaSeconds );
    	void updateInventory( float deltaSeconds );
    	void updatePlayer( float deltaSeconds );
    	void updateAI( float deltaSeconds );
    
    	void applyRegenHPAndRegenMP( float deltaSeconds, const ObjectStats& currentStats );
    	void clampHealthAndManaDueToPotentialItemChanges( const ObjectStats& currentStats );
    
    	void renderWithSprite( float deltaSeconds ) const;
    	void renderHealthBar( float deltaSeconds ) const;
    
    	// Stat Helper Functions
    	void buildActorAggregateStats( ObjectStats& out_aggregateActorStats );
    	void buildActorCurrentStatsFromAggregate( const ObjectStats& aggregateActorStats, ObjectStats& out_currentStats );
    	float determineCurrentMaxHealth( const ObjectStats& aggregateActorStats ) const;
    	float determineCurrentMaxMana( const ObjectStats& aggregateActorStats ) const;
    	float determineHealthBonusFromCurrentStrength( const ObjectStats& aggregateActorStats ) const;
    	float determineHealthRegenBonusFromCurrentStrength( const ObjectStats& aggregateActorStats ) const;
    	float determineManaBonusFromCurrentIntelligence( const ObjectStats& aggregateActorStats ) const;
    	float determineManaRegenBonusFromCurrentIntelligence( const ObjectStats& aggregateActorStats ) const;
    	float determineArmorBonusFromCurrentAgility( const ObjectStats& aggregateActorStats ) const;
    	float determineCurrentDodgeChance( const ObjectStats& aggregateActorStats ) const;
    	float determineCurrentDamageReductionFromArmor( const ObjectStats& aggregateActorStats ) const;
    	float determinePhysicalMeleeDamageBonusFromStrength( const ObjectStats& aggregateActorStats ) const;
    	float determinePhysicalRangeDamageBonusFromAgility( const ObjectStats& aggregateActorStats ) const;
    	float determineMagicDamageBonusFromIntelligence( const ObjectStats& aggregateActorStats ) const;
    
    	void takeDamage( float damageAmountToTake );
    	void increaseActorBaseStatsByLevelUpStats();
    
    	bool doesActorHaveRayCastLineOfSight( TileMap* tileMap, const PositionVec2& positionToCheck );
    
    	void setActorDefaults();
    	void determineControllerStatus();
    private:
    
    	ActorBlueprint*					m_blueprint;
    	ControllerDelegate*				m_actionDelegate;
    	Sprite*							m_sprite;
    	Inventory						m_inventory;
    	ActorState						m_actorState;
    	float							m_durationDead;
    	HealthBar						m_healthBar;
    
    	// Actor Visibility
    	std::vector		m_mapVisibility;
    	float							m_visibilityRadius;
    	PositionVec2					m_previousPosition; // Optimization for fog of war
    
    	// Actor Attributes
    	bool							m_isPlayer;
    	ObjectStats						m_baseActorStats;
    	float							m_currentHitPoints;
    	float							m_currentManaPoints;
    
    	// Levels
    	float							m_currentExperience;
    	float							m_nextLevelExperience;
    	int								m_currentLevel;
    
    	// Combat
    	CombatMode						m_combatMode;
    	CombatAbility					m_meleeCombat;
    	CombatAbility					m_rangedCombat;
    	float							m_currentMeleeAttackCooldown;
    	float							m_currentRangedAttackCooldown;
    
    	// Movement
    	TilePreferences					m_tileMovementPreferences[ NUM_TILE_TYPES ];
    
    	// AI
    	AIProfile						m_AIProfile;
    	AIActorState					m_AIActorState;
    
    };
    
    // Stat Functions
    inline float Actor::determineCurrentMaxHealth() {
    	// TODO:: Optimize this
    	ObjectStats currentStats;
    	currentStats.modifyObjectStats( m_baseActorStats );
    	m_inventory.getStatsFromEquipItems( currentStats );
    	float healthBonusDueToStrengh = determineHealthBonusFromCurrentStrength( currentStats );
    	float currentMaxHealth = ( currentStats.getStat( STAT_HEALTH ) + healthBonusDueToStrengh );
    	return currentMaxHealth;
    }
    
    
    inline float Actor::determineCurrentMaxMana() {
    	// TODO:: Optimize this
    	ObjectStats currentStats;
    	currentStats.modifyObjectStats( m_baseActorStats );
    	m_inventory.getStatsFromEquipItems( currentStats );
    	float manaBonusDueToInt = determineManaBonusFromCurrentIntelligence( currentStats );
    	float currentMaxMana = ( currentStats.getStat( STAT_MANA ) + manaBonusDueToInt );
    	return currentMaxMana;
    }
    
    
    inline float Actor::determineCurrentMaxHealth( const ObjectStats& aggregateActorStats ) const {
    
    	float maxHealthNoStrengthBonus = aggregateActorStats.getStat( STAT_HEALTH );
    	float hpDueToStrengthBonus = determineHealthBonusFromCurrentStrength( aggregateActorStats );
    	float maxStrength = ( maxHealthNoStrengthBonus + hpDueToStrengthBonus );
    	return maxStrength;
    }
    
    
    inline float Actor::determineCurrentMaxMana( const ObjectStats& aggregateActorStats ) const {
    
    	float maxManaNoIntBonus = aggregateActorStats.getStat( STAT_MANA );
    	float manaBonusFromInt = determineManaBonusFromCurrentIntelligence( aggregateActorStats );
    	float maxMana = ( maxManaNoIntBonus + manaBonusFromInt );
    	return maxMana;
    }
    
    
    inline float Actor::determineHealthBonusFromCurrentStrength( const ObjectStats& aggregateActorStats ) const {
    
    	float currentStrength = aggregateActorStats.getStat( STAT_STRENGTH );
    	float healthBonus = currentStrength * STRENGTH_TO_HP_COEFFICIENT;
    	return healthBonus;
    }
    
    
    inline float Actor::determineHealthRegenBonusFromCurrentStrength( const ObjectStats& aggregateActorStats ) const {
    
    	float currentStrength = aggregateActorStats.getStat( STAT_STRENGTH );
    	float healthRegenBonus = currentStrength * STRENGTH_TO_HP_REGEN_COEFFICIENT;
    	return healthRegenBonus;
    }
    
    inline float Actor::determineManaBonusFromCurrentIntelligence( const ObjectStats& aggregateActorStats ) const {
    
    	float currentInt = aggregateActorStats.getStat( STAT_INTELLIGENCE );
    	float manaBonus = currentInt * INTELLIGENCE_TO_MP_COEFFICIENT;
    	return manaBonus;
    }
    
    
    inline float Actor::determineManaRegenBonusFromCurrentIntelligence( const ObjectStats& aggregateActorStats ) const {
    
    	float currentInt = aggregateActorStats.getStat( STAT_INTELLIGENCE );
    	float manaRegenBonus = currentInt * INTELLIGENCE_TO_MP_REGEN_COEFFICIENT;
    	return manaRegenBonus;
    }
    
    
    inline float Actor::determineArmorBonusFromCurrentAgility( const ObjectStats& aggregateActorStats ) const {
    
    	float currentAgility = aggregateActorStats.getStat( STAT_AGILITY );
    	float armorBonusDueToAgility = currentAgility * AGILITY_TO_ARMOR_COEFFICIENT;
    	return armorBonusDueToAgility;
    }
    
    
    inline float Actor::determineCurrentDodgeChance( const ObjectStats& aggregateActorStats ) const {
    
    	float currentAgility = aggregateActorStats.getStat( STAT_AGILITY );
    	float currentDodge = currentAgility * AGILITY_TO_DODGE_CHANCE_COEFFICIENT;
    	float dodgeChance = ( DODGE_CHANCE_COEFFICIENT * currentDodge ) / ( 1.0f + ( DODGE_CHANCE_COEFFICIENT * currentDodge ) );
    	return ( dodgeChance > MAX_DODGE_CHANCE ) ? MAX_DODGE_CHANCE : dodgeChance;
    }
    
    
    inline float Actor::determineCurrentDamageReductionFromArmor( const ObjectStats& aggregateActorStats ) const {
    
    	float currentArmorStat = aggregateActorStats.getStat( STAT_ARMOR );
    	float armorDueToAgility = determineArmorBonusFromCurrentAgility( aggregateActorStats );
    	float currentArmor = currentArmorStat + armorDueToAgility;
    	float damageReduction = ( ARMOR_DAMAGE_REDUCTION_COEFFICIENT * currentArmor ) / ( 1.0f + ( ARMOR_DAMAGE_REDUCTION_COEFFICIENT * currentArmor ) );
    	return ( damageReduction > MAX_ARMOR_DAMAGE_REDUCTION ) ? MAX_ARMOR_DAMAGE_REDUCTION : damageReduction; 
    }
    
    
    inline float Actor::determinePhysicalMeleeDamageBonusFromStrength( const ObjectStats& aggregateActorStats ) const {
    	
    	float currentStrength = aggregateActorStats.getStat( STAT_STRENGTH );
    	float damageBonusDuetoStrength = currentStrength * STRENGTH_TO_PHYSICAL_MELEE_DMG_COEFFICIENT;
    	return damageBonusDuetoStrength;
    }
    
    
    inline float Actor::determinePhysicalRangeDamageBonusFromAgility( const ObjectStats& aggregateActorStats ) const {
    
    	float currentAgility = aggregateActorStats.getStat( STAT_AGILITY );
    	float damageBonusDueToAgility = currentAgility * AGILITY_TO_PHYSICAL_RANGE_DMG_COEFFICIENT;
    	return damageBonusDueToAgility;
    }
    
    
    inline float Actor::determineMagicDamageBonusFromIntelligence( const ObjectStats& aggregateActorStats ) const {
    
    	float currentIntelligence = aggregateActorStats.getStat( STAT_INTELLIGENCE );
    	float damageBonusDueToIntelligence = currentIntelligence * INTELLIGENCE_TO_MAGIC_DMG_COEFFICIENT;
    	return damageBonusDueToIntelligence;
    }
    
    
    inline void Actor::takeDamage( float damageAmountToTake ) {
    
    	float previousHealth = m_currentHitPoints;
    
    	m_currentHitPoints -= damageAmountToTake;
    	m_AIProfile.onDamageReceived( damageAmountToTake, this );
    
    	if ( m_currentHitPoints < 0.0f ) {
    		changeActorState( STATE_ACTOR_DEAD );
    		bool dropsItemsOnDeath = doesDropsItemsOnDeath();
    		if ( dropsItemsOnDeath ) {
    			//TODO:: implement this
    			m_inventory.dropAllItems();
    		}
    	}
    
    	// Clamp to max just in case a heal occured
    	ObjectStats aggregateActorStats;
    	aggregateActorStats.modifyObjectStats( m_baseActorStats );
    	m_inventory.getStatsFromEquipItems( aggregateActorStats );
    	float currentMaxHealth = determineCurrentMaxHealth( aggregateActorStats );
    
    	if ( m_currentHitPoints > currentMaxHealth ) {
    		m_currentHitPoints = currentMaxHealth;
    	}
    
    	m_AIProfile.onHealthChange( previousHealth, m_currentHitPoints, currentMaxHealth, this );
    }
    
    // Inline Mutator Functions
    inline void Actor::setVisibilityRadius( float visibilityRadius ) {
    
    	if ( visibilityRadius < 0.0f ) {
    		m_visibilityRadius = 0.0f;
    		return;
    	}
    
    	m_visibilityRadius = visibilityRadius;
    }
    
    
    inline float Actor::getVisibilityRadius() const {
    	return m_visibilityRadius;
    }
    
    
    inline bool Actor::isPlayer() const {
    	return m_isPlayer;
    }
    
    
    inline void Actor::setActorBlueprint( ActorBlueprint* blueprint ) {
    
    	assert( blueprint != nullptr );
    	m_blueprint = blueprint;
    }
    
    
    inline const ActorBlueprint* Actor::getActorBlueprint() const {
    	return m_blueprint;
    }
    
    
    inline void Actor::setActorSprite( Sprite* sprite ) {
    	assert( sprite != nullptr );
    	m_sprite = sprite;
    }
    
    
    inline const Sprite* Actor::getActorSprite() const {
    	return m_sprite;
    }
    
    // TODO:: INVENTORY / ITEM BLUEPRINT STATS / PROPOGATION / FILL OUT GETCURRENTMAXHP,MP Functions / CLAMP CURRENT FUNCTIONS 
    inline void Actor::setCurrentHitPoints( float hitPoints ) {
    
    	float currentMaxHitPoints = determineCurrentMaxHealth();
    	cbengine::clampFloat( 0.0f, currentMaxHitPoints, hitPoints );
    	m_currentHitPoints = hitPoints;
    }
    
    
    inline const float Actor::getCurrentHitPoints() const {
    	return m_currentHitPoints;
    }
    
    
    inline void Actor::setCurrentManaPoints( float manaPoints ) {
    
    	float currentMaxManaPoints = determineCurrentMaxMana();
    	cbengine::clampFloat( 0.0f, currentMaxManaPoints, manaPoints );
    	m_currentManaPoints = manaPoints;
    }
    
    
    inline const float Actor::getCurrentManaPoints() const {
    	return m_currentManaPoints;
    }
    
    
    
    inline Inventory& Actor::getInventory() {
    	return m_inventory;
    }
    
    
    inline const float Actor::getMaxMovementVelocity() const {
    	return m_blueprint->m_velocity;
    }
    
    
    inline void Actor::setMeleeCombatAbility( const CombatAbility& meleeCombatAbility ) {
    	m_meleeCombat = meleeCombatAbility;
    }
    
    
    inline const CombatAbility& Actor::getMeleeCombatAbility() const {
    	return m_meleeCombat;
    }
    
    
    inline void Actor::setRangedCombatAbility( const CombatAbility& rangedCombatAbility ) {
    	m_rangedCombat = rangedCombatAbility;
    }
    
    
    inline const CombatAbility& Actor::getRangedCombatAbility() const {
    	return m_rangedCombat;
    }
    
    
    inline void Actor::setCombatMode( const CombatMode& combatModeToSet ) {
    
    	m_combatMode = combatModeToSet;
    }
    
    
    inline const CombatMode& Actor::getCombatMode() const {
    	return m_combatMode;
    }
    
    
    inline void Actor::setActionDelegate( ControllerDelegate* actionDelegate ) {
    	assert( actionDelegate != nullptr );
    	m_actionDelegate = actionDelegate;
    }
    
    
    inline const ActorState& Actor::getActorState() const {
    	return m_actorState;
    }
    
    
    inline const std::string& Actor::getActorName() const {
    
    	assert( m_blueprint != nullptr );
    	return m_blueprint->m_actorName;
    }
    
    
    inline const std::vector& Actor::getFogOfWarStateVectorForActor() const {
    	return m_mapVisibility;
    }
    
    
    inline AIProfile& Actor::getAIProfile() {
    	return m_AIProfile;
    }
    
    
    inline bool Actor::isAI() const {
    	return !m_isPlayer;
    }
    
    
    inline const AIActorState& Actor::getAIActorState() const {
    	return m_AIActorState;
    }
    
    // TODO::
    inline bool Actor::doesDropsItemsOnDeath() const {
    	return m_blueprint->m_dropsItemsOnDeath;
    }
    
    
    inline float Actor::getExperienceValueWhenKilled() const {
    
    	assert( m_blueprint != nullptr );
    	return m_blueprint->m_experienceValueForKill;
    }
    
    
    inline bool Actor::canActorGainExperience() const {
    
    	assert( m_blueprint != nullptr );
    	return m_blueprint->m_canGainExperience;
    }
    
    
    inline const TilePreferences* Actor::getActorTilePreferences() const {
    
    	return &m_tileMovementPreferences[0];
    }
    
    #endif
    							
  • Actor.cpp

    #include "Actor.hpp"
    
    #include 
    
    #include "EngineCommon.hpp"
    #include "EventSystem.hpp"
    
    #include "Projectile.hpp"
    
    #include "MathUtil.hpp"
    #include "Sprite.hpp"
    #include "TileMap.hpp"
    #include "RayCast2DResult.hpp"
    
    #include "Console.hpp"
    #include "Vector4.hpp"
    
    Actor::~Actor() {
    
    	if ( m_sprite ) {
    		delete m_sprite;
    	}
    }
    
    
    Actor::Actor( ActorBlueprint* actorBlueprint ) {
    
    	setActorDefaults();
    	setActorBlueprint( actorBlueprint );
    	determineControllerStatus();
    }
    
    // Core Functions
    void Actor::update( float deltaSeconds ) {
    
    	update( deltaSeconds, nullptr );
    }
    
    
    void Actor::update( float deltaSeconds, Tile* tileStandingOn ) {
    
    	updateActorState( deltaSeconds );
    
    	if ( m_actorState == STATE_ACTOR_DEAD ) {
    		return;
    	}
    
    	if ( isPlayer() ) {
    		updatePlayer( deltaSeconds );
    	} else {
    		updateAI( deltaSeconds );
    	}
    
    	updatePhysics( deltaSeconds, tileStandingOn );
    	updateCooldowns( deltaSeconds );
    	updateInventory( deltaSeconds );
    }
    
    
    void Actor::changeActorState( ActorState stateToChange ) {
    
    	if ( stateToChange == STATE_ACTOR_DEAD ) {
    
    		if ( m_blueprint->m_deadTexture != nullptr ) {
    			m_sprite->setDiffuseTexture( m_blueprint->m_deadTexture );
    		}	
    
    		m_actorState = STATE_ACTOR_DEAD;
    
    	} else if ( stateToChange == STATE_ACTOR_ALIVE ) {
    
    		// TODO:: set back to normal sprite
    		// TODO:: reset cooldowns
    		m_actorState = STATE_ACTOR_ALIVE;
    	}
    }
    
    
    void Actor::updateActorState( float deltaSeconds ) {
    
    	if ( m_actorState == STATE_ACTOR_ALIVE ) {
    
    		updateActorAlive( deltaSeconds );
    
    	} else if ( m_actorState == STATE_ACTOR_DEAD ) {
    		
    		updateActorDead( deltaSeconds );
    	}
    }
    
    
    void Actor::updateActorAlive( float deltaSeconds ) {
    
    	if ( m_currentHitPoints < 0.0f ) {
    		changeActorState( STATE_ACTOR_DEAD );
    	}
    
    	ObjectStats currentActorStats;
    	getCurrentActorStats( currentActorStats );
    
    	clampHealthAndManaDueToPotentialItemChanges( currentActorStats );
    	applyRegenHPAndRegenMP( deltaSeconds, currentActorStats );
    
    
    }
    
    
    void Actor::updateActorDead( float deltaSeconds ) {
    
    	m_durationDead = m_durationDead + deltaSeconds;
    	// TODO:: move to game settings to be data driven or actorblueprint
    	if ( m_durationDead > DURATION_TO_DELETE_AI && !isPlayer() ) {
    		flagForDeletion();
    	}
    }
    
    
    void Actor::updatePlayer( float deltaSeconds ) {
    	UNUSED(deltaSeconds);
    }
    
    
    void Actor::updateAI( float deltaSeconds ) {
    	UNUSED(deltaSeconds);
    }
    
    void Actor::updatePhysics( float deltaSeconds, Tile* tileStandingOn ) {
    
    	float movementSpeedCoefficient = 1.0f;
    	if ( tileStandingOn != nullptr ) {
    
    		TileType tileType = tileStandingOn->m_tileType;
    		const TilePreferences& tilePref = m_tileMovementPreferences[ tileType ];
    		movementSpeedCoefficient = tilePref.m_movementSpeedCoefficient;
    	}
    
    	// Cache previous position for fog of war optimization
    	m_previousPosition.x = m_position.x;
    	m_previousPosition.y = m_position.y;
    	
    	// Simple Fwd Euler Integration
    	m_position.x = m_position.x + ( deltaSeconds * ( m_currentVelocity.x * movementSpeedCoefficient ) );
    	m_position.y = m_position.y + ( deltaSeconds * ( m_currentVelocity.y * movementSpeedCoefficient ) );
    	updatePhysicsBody( m_position ); 
    }
    
    
    void Actor::updateCooldowns( float deltaSeconds ) {
    
    	m_currentMeleeAttackCooldown = m_currentMeleeAttackCooldown - deltaSeconds;
    	m_currentRangedAttackCooldown = m_currentRangedAttackCooldown - deltaSeconds;
    }
    
    
    void Actor::updateInventory( float deltaSeconds ) {
    
    	m_inventory.update( deltaSeconds );
    }
    
    
    void Actor::render( float deltaSeconds ) const {
    
    	renderWithSprite( deltaSeconds );
    }
    
    
    void Actor::renderWithSprite( float deltaSeconds ) const {
    
    	if ( m_isVisible ) {
    
    		if ( m_sprite != nullptr ) {
    			const PositionVec2& currentPosition = getPosition();
    			m_sprite->render( deltaSeconds, currentPosition );
    		}
    
    		if ( m_actorState != STATE_ACTOR_DEAD ) {
    			renderHealthBar( deltaSeconds );
    		}
    		
    	}
    }
    
    
    void Actor::renderHealthBar( float deltaSeconds ) const {
    
    	PositionVec2 positionForHealthBar;
    	const cbengine::Size& spriteSize = m_blueprint->m_spriteSize;
    	positionForHealthBar.x = m_position.x - ( 0.53f * spriteSize.m_width );
    	positionForHealthBar.y = m_position.y + ( 0.53f * spriteSize.m_height );
    	
    	float maxHealth = 0.0f;
    	ObjectStats aggregateStats;
    	aggregateStats.modifyObjectStats( m_baseActorStats );
    	m_inventory.getStatsFromEquipItems( aggregateStats );
    	maxHealth = determineCurrentMaxHealth( aggregateStats );
    	m_healthBar.render( deltaSeconds, positionForHealthBar, 1.1f, maxHealth, m_currentHitPoints );
    
    }
    
    // Stats
    void Actor::getCurrentActorStats( ObjectStats& out_actorStats ) {
    
    	ObjectStats aggregateStats;
    	buildActorAggregateStats( aggregateStats );
    	buildActorCurrentStatsFromAggregate( aggregateStats, out_actorStats );
    }
    
    
    void Actor::buildActorAggregateStats( ObjectStats& out_aggregateActorStats ) {
    	// Does not account for bonuses due to stats
    	out_aggregateActorStats.modifyObjectStats( m_baseActorStats );
    	
    	m_inventory.getStatsFromEquipItems( out_aggregateActorStats );
    
    	// TODO:: 
    	// Add/Sub Effect Bonus
    }
    
    
    void Actor::buildActorCurrentStatsFromAggregate( const ObjectStats& aggregateActorStats, ObjectStats& out_currentStats ) {
    
    	out_currentStats.modifyObjectStats( aggregateActorStats );
    	// Now add bonuses
    	float bonusHPDueToStrength = determineHealthBonusFromCurrentStrength( aggregateActorStats );
    	float bonusHPRegenDueToStrength = determineHealthRegenBonusFromCurrentStrength( aggregateActorStats );
    	float bonusMPDueToInt = determineManaBonusFromCurrentIntelligence( aggregateActorStats );
    	float bonusMPRegenDueToInt = determineManaRegenBonusFromCurrentIntelligence( aggregateActorStats );
    	float bonusArmorDueToAgility = determineArmorBonusFromCurrentAgility( aggregateActorStats );
    	
    	out_currentStats.addToStat( STAT_HEALTH, bonusHPDueToStrength );
    	out_currentStats.addToStat( STAT_HEALTH_REGEN, bonusHPRegenDueToStrength );
    	out_currentStats.addToStat( STAT_MANA, bonusMPDueToInt );
    	out_currentStats.addToStat( STAT_MANA_REGEN, bonusMPRegenDueToInt );
    	out_currentStats.addToStat( STAT_ARMOR, bonusArmorDueToAgility );
    }
    
    
    
    void Actor::applyRegenHPAndRegenMP( float deltaSeconds, const ObjectStats& currentStats ) {
    
    	float previousHealth = m_currentHitPoints;
    
    	float currentHPRegen = currentStats.getStat( STAT_HEALTH_REGEN );
    	float currentMPRegen = currentStats.getStat( STAT_MANA_REGEN );
    
    	m_currentHitPoints += ( currentHPRegen * deltaSeconds );
    	m_currentManaPoints += ( currentMPRegen * deltaSeconds );
    
    	float currentMaxHP = currentStats.getStat( STAT_HEALTH );
    	float currentMaxMP = currentStats.getStat( STAT_MANA );
    
    	if ( m_currentHitPoints > currentMaxHP ) {
    		m_currentHitPoints = currentMaxHP;
    	}
    
    	if ( m_currentManaPoints > currentMaxMP ) {
    		m_currentManaPoints = currentMaxMP;
    	}
    
    	m_AIProfile.onHealthChange( previousHealth, m_currentHitPoints, currentMaxHP, this );
    }
    
    
    void Actor::clampHealthAndManaDueToPotentialItemChanges( const ObjectStats& currentStats ) {
    
    	float currentMaxHP = currentStats.getStat( STAT_HEALTH );
    	float currentMaxMP = currentStats.getStat( STAT_MANA );
    
    	if ( m_currentHitPoints > currentMaxHP ) {
    		m_currentHitPoints = currentMaxHP;
    	}
    
    	if ( m_currentManaPoints > currentMaxMP ) {
    		m_currentManaPoints = currentMaxMP;
    	}
    }
    
    // Life Cycle Functions
    void Actor::onCreate() {
    
    }
    
    
    void Actor::onPause() {
    
    }
    
    
    void Actor::onDestroy() {
    
    }
    
    
    // Combat
    bool Actor::canPerformMeleeAttack() const {
    
    	if ( m_meleeCombat.m_combatType == TYPE_MELEE && m_currentMeleeAttackCooldown < 0.0f ) {
    		return true;
    	} else {
    		return false;
    	}
    }
    
    
    bool Actor::hasAbilityToPerformMeleeAttack() {
    
    	if ( m_meleeCombat.m_combatType == TYPE_MELEE ) {
    		return true;
    	} else {
    		return false;
    	}
    }
    
    
    bool Actor::canPerformRangedAttack() {
    
    	if ( m_rangedCombat.m_combatType == TYPE_RANGED && m_currentRangedAttackCooldown < 0.0f ) {
    
    		bool hasProjectileContainerAndAmmoForRanged = m_inventory.hasRangedContainerAndAmmo();
    
    		if ( !hasProjectileContainerAndAmmoForRanged ) {
    			 hasProjectileContainerAndAmmoForRanged = m_inventory.replaceEmptyRangedContainerWithContainerWithAmmo();
    		}
    
    		return hasProjectileContainerAndAmmoForRanged;
    	} else {
    		return false;
    	}
    }
    
    
    bool Actor::hasAmmoForRangedAttack() {
    
    	if ( m_rangedCombat.m_combatType == TYPE_RANGED ) {
    
    		bool hasProjectileContainerAndAmmoForRanged = m_inventory.hasRangedContainerAndAmmo();
    
    		if ( !hasProjectileContainerAndAmmoForRanged ) {
    			hasProjectileContainerAndAmmoForRanged = m_inventory.replaceEmptyRangedContainerWithContainerWithAmmo();
    		}
    
    		return hasProjectileContainerAndAmmoForRanged;
    	} else {
    		return false;
    	}
    
    }
    
    // Poor mans combat!
    void Actor::performMeleeAttack( const PositionVec2& targetPosition ) {
    
    	if ( m_actionDelegate == nullptr ) {
    		return;
    	}
    
    	if ( m_combatMode != COMBAT_MODE_MELEE ) {
    		return;
    	}
    
    	const float attackWidthDegrees = 40.0f;
    
    	if ( m_meleeCombat.m_combatType == TYPE_MELEE ) {
    		if ( m_currentMeleeAttackCooldown < 0.0f ) {
    
    			PositionVec2 positionDif;
    			positionDif.x = m_position.x - targetPosition.x;
    			positionDif.y = m_position.y - targetPosition.y;
    			float orientationTargetRads = atan2f( positionDif.y, positionDif.x );
    			float orientationTargetDegrees = cbengine::radiansToDegrees( orientationTargetRads );
    			PositionVec2 swingPosition;
    			swingPosition.x = m_position.x - ( ( m_meleeCombat.m_baseAttackRange * 0.50f ) * cos( orientationTargetRads ) );
    			swingPosition.y = m_position.y - ( ( m_meleeCombat.m_baseAttackRange * 0.50f ) * sin( orientationTargetRads ) );
    
    			float swingStartDegrees = orientationTargetDegrees - attackWidthDegrees + 360.0f;
    			float swingEndDegrees = orientationTargetDegrees + attackWidthDegrees + 360.0f;
    			float attackSpeed = 100.0f;
    
    			cbengine::Disk2D effectedArea;
    			effectedArea.radius = m_meleeCombat.m_baseAttackRange * 0.50f; // TODO:: Stat seems misleading (baseAttackRange)
    			effectedArea.origin.x = swingPosition.x;
    			effectedArea.origin.y = swingPosition.y;
    
    			m_actionDelegate->announceMeleeAttack( this, effectedArea, swingStartDegrees, swingEndDegrees, attackSpeed );
    			// TODO:: incorporate modifiers for attack cool down and attack speed
    			m_currentMeleeAttackCooldown = m_meleeCombat.m_baseAttackTimeCooldownSeconds;
    		}
    	}
    }
    
    
    void Actor::performRangedAttack( const PositionVec2& targetPosition, Projectile* projectileToFire ) {
    
    	if ( m_actionDelegate == nullptr || projectileToFire == nullptr ) {
    		return;
    	}
    
    	if ( m_combatMode != COMBAT_MODE_RANGED ) {
    		return;
    	}
    
    	if ( m_rangedCombat.m_combatType == TYPE_RANGED ) {
    
    		bool canActorPerformRangedAttack = false;
    		if ( m_rangedCombat.m_combatType == TYPE_RANGED && m_currentRangedAttackCooldown < 0.0f ) {
    			canActorPerformRangedAttack = true;
    		}
    
    		if ( canActorPerformRangedAttack ) {
    
    			ObjectStats currentStats;
    			currentStats.modifyObjectStats( m_baseActorStats );
    			m_inventory.getStatsFromEquipItems( currentStats );
    			
    			float bonusDamageFromAgility = determinePhysicalRangeDamageBonusFromAgility( currentStats );
    			float currentMinDamage = ( currentStats.getStat( STAT_DAMAGE_MIN ) );
    			float currentMaxDamage = ( currentStats.getStat( STAT_DAMAGE_MAX ) );
    			float baseRangedDamage = m_rangedCombat.m_baseDamage;
    			float damageMinToAddToProjectile = ( currentMinDamage * PROJECTILE_DAMAGE_PENALITY );
    			float damageMaxToAddToProjectile = ( currentMaxDamage * PROJECTILE_DAMAGE_PENALITY );
    
    			projectileToFire->addToProjectileDamageMax( damageMaxToAddToProjectile );
    			projectileToFire->addToProjectileDamageMin( damageMinToAddToProjectile );
    			projectileToFire->addToProjectileBaseDamage( baseRangedDamage + bonusDamageFromAgility );
    
    			PositionVec2 positionDelta;
    			positionDelta.x = targetPosition.x - m_position.x;
    			positionDelta.y = targetPosition.y - m_position.y;
    
    			float angleRadians = atan2f( positionDelta.y, positionDelta.x );
    			float angleDegrees = cbengine::radiansToDegrees( angleRadians );
    			projectileToFire->setOrientationDegrees( angleDegrees );
    
    			PositionVec2 projectileVelocity;
    			float projectileSpeed = projectileToFire->getProjectileSpeed();
    			float cosOfAngleRadians = cos( angleRadians );
    			float sinOfAngleRadians = sin( angleRadians );
    			
    			projectileVelocity.x = cosOfAngleRadians * projectileSpeed;
    			projectileVelocity.y = sinOfAngleRadians * projectileSpeed;
    			projectileToFire->setCurrentVelocity( projectileVelocity );
    
    			PositionVec2 adjustedProjectileStartPosition;
    			adjustedProjectileStartPosition.x = m_position.x + ( cosOfAngleRadians * ( m_disk2D.radius * PROJECTILE_SPAWN_ADJUSTMENT_COEFFICIENT ) );
    			adjustedProjectileStartPosition.y = m_position.y + ( sinOfAngleRadians * ( m_disk2D.radius * PROJECTILE_SPAWN_ADJUSTMENT_COEFFICIENT ) );
    			projectileToFire->setPosition( adjustedProjectileStartPosition );
    
    			// Ask delegate to spawn the projectile
    			projectileToFire->setInstigator( this );
    			m_actionDelegate->announceRangedProjectileAttack( projectileToFire );
    
    			m_currentRangedAttackCooldown = m_rangedCombat.m_baseAttackTimeCooldownSeconds;
    		}
    	}
    
    }
    
    
    float Actor::determineMeleeDamage() {
    	
    	float totalMeleeDamage = 0.0f;
    
    	ObjectStats aggregateObjectStats;
    	aggregateObjectStats.modifyObjectStats( m_baseActorStats );
    	m_inventory.getStatsFromEquipItems( aggregateObjectStats );
    	float damageBonusFromStrength = determinePhysicalMeleeDamageBonusFromStrength( aggregateObjectStats );
    	float minDmgBonus = aggregateObjectStats.getStat( STAT_DAMAGE_MIN );
    	float maxDmgBonus = aggregateObjectStats.getStat( STAT_DAMAGE_MAX );
    	float randomZeroToOne = cbengine::getRandomZeroToOne();
    	float damageBonusRaw = ( maxDmgBonus - minDmgBonus ) * randomZeroToOne;
    
    	totalMeleeDamage += ( m_meleeCombat.m_baseDamage + damageBonusFromStrength + minDmgBonus + damageBonusRaw );
    
    	return totalMeleeDamage;
    }
    
    
    void Actor::receiveMeleeAttack( float damageAmount, DamageProfile& damageProfile ) {
    
    	// TODO:: have this function return a damage profile
    	damageProfile.m_receiver = this;
    	damageProfile.m_receiverName = getActorName();
    	if ( m_actorState == STATE_ACTOR_DEAD || m_currentHitPoints <= 0.0f ) {
    		damageProfile.m_damageType = DAMAGE_TYPE_OBJECT_DEAD;
    		return;	
    	}
    
    	float adjustedDamageAmount = damageAmount;
    	damageProfile.m_damageType = DAMAGE_TYPE_MELEE;
    
    	// First determine dodge chance
    	ObjectStats aggregateObjectStats;
    	aggregateObjectStats.modifyObjectStats( m_baseActorStats );
    	m_inventory.getStatsFromEquipItems( aggregateObjectStats );
    
    	float dodgeChance = determineCurrentDodgeChance( aggregateObjectStats ); // Result [ 0.0 - Max (0.60) ]
    	float rollForDodge = cbengine::getRandomZeroToOne();
    	
    	if ( rollForDodge <= dodgeChance ) {
    		damageProfile.m_wasDodged = true;
    		return;
    	}
    
    	// Dodge was not successful, check to see if a Block occured
    	float blockChance = aggregateObjectStats.getStat( STAT_BLOCK_CHANCE );
    	float rollForBlock = cbengine::getRandomZeroToOne();
    	
    	if ( rollForBlock <= blockChance ) {
    
    		float blockValue = aggregateObjectStats.getStat( STAT_BLOCK );
    		adjustedDamageAmount -= blockValue;
    		damageProfile.m_damageReducedFromBlock = blockValue;
    
    		if ( adjustedDamageAmount < 0.0f ) {
    			return;
    		}
    	}
    
    	// Reduce Damage From Armor
    	float damageReductionDueToArmor = determineCurrentDamageReductionFromArmor( aggregateObjectStats );
    	adjustedDamageAmount -= damageReductionDueToArmor;
    	damageProfile.m_damageReducedFromArmor = damageReductionDueToArmor;
    	if ( adjustedDamageAmount < 0.0f ) {
    		return;
    	}
    
    	// At this point if damage still exists... Apply it
    	damageProfile.m_damageReceived = adjustedDamageAmount;
    	takeDamage( adjustedDamageAmount );
    
    	if ( m_actorState == STATE_ACTOR_DEAD ) {
    		if ( damageProfile.m_instigator != nullptr && !damageProfile.m_instigator->isFlaggedForDeletion() ) {
    
    			damageProfile.m_instigator->onActorKill( this, damageProfile );
    		}
    	}
    	
    }
    
    
    void Actor::receiveRangedAttack( float damageAmount, DamageProfile& damageProfile ) {
    
    	damageProfile.m_receiver = this;
    	damageProfile.m_receiverName = getActorName();
    	if ( m_actorState == STATE_ACTOR_DEAD || m_currentHitPoints <= 0.0f ) {
    		damageProfile.m_damageType = DAMAGE_TYPE_OBJECT_DEAD;
    		return;	
    	}
    
    	float adjustedDamageAmount = damageAmount;
    	damageProfile.m_damageType = DAMAGE_TYPE_RANGED;
    
    	// First determine dodge chance
    	ObjectStats aggregateObjectStats;
    	aggregateObjectStats.modifyObjectStats( m_baseActorStats );
    	m_inventory.getStatsFromEquipItems( aggregateObjectStats );
    
    	float dodgeChance = determineCurrentDodgeChance( aggregateObjectStats ); // Result [ 0.0 - Max (0.60) ]
    	float rollForDodge = cbengine::getRandomZeroToOne();
    
    	if ( rollForDodge <= dodgeChance ) {
    		damageProfile.m_wasDodged = true;
    		return;
    	}
    
    	// Dodge was not successful, check to see if a Ranged Block occured
    	float blockChance = aggregateObjectStats.getStat( STAT_BLOCK_CHANCE );
    	float rollForBlock = cbengine::getRandomZeroToOne();
    
    	if ( rollForBlock <= blockChance ) {
    
    		float blockValue = aggregateObjectStats.getStat( STAT_BLOCK );
    		adjustedDamageAmount -= blockValue;
    		damageProfile.m_damageReducedFromBlock = blockValue;
    
    		if ( adjustedDamageAmount < 0.0f ) {
    			return;
    		}
    	}
    
    	// Reduce Damage From Armor
    	float damageReductionDueToArmor = determineCurrentDamageReductionFromArmor( aggregateObjectStats );
    	adjustedDamageAmount -= damageReductionDueToArmor;
    	damageProfile.m_damageReducedFromArmor = damageReductionDueToArmor;
    	if ( adjustedDamageAmount < 0.0f ) {
    		return;
    	}
    
    	// At this point if damage still exists... Apply it
    	damageProfile.m_damageReceived = adjustedDamageAmount;
    	takeDamage( adjustedDamageAmount );
    
    	if ( m_actorState == STATE_ACTOR_DEAD ) {
    		if ( damageProfile.m_instigator != nullptr && !damageProfile.m_instigator->isFlaggedForDeletion() ) {
    
    			damageProfile.m_instigator->onActorKill( this, damageProfile );
    		}
    	}
    
    }
    
    
    void Actor::onActorKill( Actor* actorKilled, const DamageProfile& damageProfileOfKillingBlow ) {
    
    	UNUSED( damageProfileOfKillingBlow );
    
    	bool actorCanReceiveExp = canActorGainExperience();
    	if ( !actorCanReceiveExp ) {
    		return;
    	}
    
    	float experienceValueForKill = actorKilled->getExperienceValueWhenKilled();
    
    	bool actorShouldLevelUp = false;
    	float expCarryOver = 0.0f;
    	m_currentExperience += experienceValueForKill;
    
    	if ( m_currentExperience > m_nextLevelExperience ) {
    
    		expCarryOver = m_currentExperience - m_nextLevelExperience;
    		actorShouldLevelUp = true;
    	}
    
    	if ( actorShouldLevelUp ) {
    
    		float percentageIncreaseLevelExpPerLevel = m_blueprint->m_experiencePerecentageIncreaseByLevel;
    		m_nextLevelExperience = ( m_nextLevelExperience * ( 1.0f + percentageIncreaseLevelExpPerLevel ) );
    		m_currentExperience = expCarryOver; 
    		expCarryOver = 0.0f;
    		++m_currentLevel;
    		
    		increaseActorBaseStatsByLevelUpStats();
    
    		// Handle case where monster can yield enough exp for multiple levels
    		actorShouldLevelUp = false;
    		while ( !actorShouldLevelUp ) {
    
    			if ( m_currentExperience > m_nextLevelExperience ) {
    
    				expCarryOver = m_currentExperience - m_nextLevelExperience;
    				percentageIncreaseLevelExpPerLevel = m_blueprint->m_experiencePerecentageIncreaseByLevel;
    				m_nextLevelExperience = ( m_nextLevelExperience * ( 1.0f + percentageIncreaseLevelExpPerLevel ) );
    				m_currentExperience = expCarryOver; 
    				++m_currentLevel;
    				increaseActorBaseStatsByLevelUpStats();
    
    			} else {
    
    				expCarryOver = 0.0f;
    				actorShouldLevelUp = true;
    			}
    		}
    	}
    
    }
    
    
    void Actor::increaseActorBaseStatsByLevelUpStats() {
    
    	assert( m_blueprint != nullptr );
    	m_baseActorStats.modifyObjectStats( m_blueprint->m_levelUpStats );
    }
    
    // Items
    bool Actor::canActorReceiveItem( Item* itemToReceive ) {
    	
    	if ( itemToReceive == nullptr ) {
    		return false;
    	}
    
    	bool canReceiveItem = m_inventory.canReceiveItem( itemToReceive );
    	return canReceiveItem;
    }
    
    
    bool Actor::receiveItemAndTakeOwnership( Item* itemToReceive ) {
    
    	bool itemWasReceived = m_inventory.addItemToInventory( itemToReceive );
    	return itemWasReceived;
    }
    
    
    Item* Actor::getProjectileContainerCurrentlyEquip() {
    
    	Item* projectileContainer = m_inventory.getProjectileContainerCurrentlyEquip();
    	return projectileContainer;
    }
    
    // Misc Functions
    void Actor::setActorDefaults() {
    
    	m_currentLevel = 1;
    	m_flagForDeletion = false;
    	m_sprite = nullptr;
    	m_actionDelegate = nullptr;
    	m_isVisible = true;
    	m_currentMeleeAttackCooldown = 0.0f;
    	m_currentRangedAttackCooldown = 0.0f;
    	m_durationDead = 0.0f;
    	m_orientationDegrees = 0.0f;
    	m_visibilityRadius = 0.0f;
    	m_currentExperience = 0.0f;
    	m_nextLevelExperience = 0.0f;
    	m_actorState = STATE_ACTOR_ALIVE;
    	m_combatMode = COMBAT_MODE_MELEE;
    	m_AIActorState = AI_STATE_IDLE;
    }
    
    
    void Actor::determineControllerStatus() {
    
    	assert( m_blueprint != nullptr );
    
    	if ( m_blueprint->m_controlType == "Player" ) {
    		m_isPlayer = true;
    	} else {
    		m_isPlayer = false;
    	}
    }
    
    
    void Actor::updateVisibility( float deltaSeconds, TileMap* currentTileMap ) {
    
    	UNUSED( deltaSeconds );
    	if ( currentTileMap == nullptr ||  ( m_position == m_previousPosition ) ) {
    		return;
    	}
    
    
    	size_t sizeOfCurrentTileMap = currentTileMap->getNumTilesInTileMap();
    	if ( m_mapVisibility.size() != sizeOfCurrentTileMap ) {
    		// TODO:: signal to world and or tilemap the need for an update to visibilityVector in Actor
    		return;
    	}
    
    	std::vector& tileMapVector = currentTileMap->getTileMapToModify();
    
    	cbengine::Disk2D actorVisibilityDisk;
    	actorVisibilityDisk.radius = m_visibilityRadius;
    	actorVisibilityDisk.origin.x = m_position.x;
    	actorVisibilityDisk.origin.y = m_position.y;
    
    	const float halfTileWidth = 0.50f;
    	const float halfTileHeight = 0.50f;
    	cbengine::Disk2D adjustedTileDisk;
    	adjustedTileDisk.radius = 0.45f;
    
    	PositionVec2 currentTilePosition;
    
    	// Simple naive visibility calculations
    	for ( size_t i = 0; i < m_mapVisibility.size(); ++i ) {
    
    		Tile& currentTile = tileMapVector[i];
    		currentTileMap->convertIndexToVec2( i, currentTilePosition );
    		adjustedTileDisk.origin.x = currentTilePosition.x + halfTileWidth;
    		adjustedTileDisk.origin.y = currentTilePosition.y + halfTileHeight;
    
    		bool isInVisiblityRadius = cbengine::doesDiskIntersectDiskOrTouch( actorVisibilityDisk, adjustedTileDisk );
    		if ( isInVisiblityRadius ) {
    
    			FogOfWarState& currentStateOfTile = m_mapVisibility[i];
    			currentStateOfTile = STATE_VISIBLE;
    			currentTile.m_isVisibleToActorInFocus = true;
    		} else {
    
    			currentTile.m_isVisibleToActorInFocus = false;
    			FogOfWarState& currentStateOfTile = m_mapVisibility[i];
    			if ( currentStateOfTile == STATE_VISIBLE ) {
    
    				currentStateOfTile = STATE_REMEMBER_BUT_NOT_IN_VIEW;
    			} else if ( currentStateOfTile != STATE_REMEMBER_BUT_NOT_IN_VIEW ) {
    
    				currentStateOfTile = STATE_UNDISCOVERED;
    			}
    		}
    	}
    }
    
    
    void Actor::markEntireMapAsVisibleToActor( TileMap* currentTileMap ) {
    
    	size_t sizeOfCurrentTileMap = currentTileMap->getNumTilesInTileMap();
    	if ( m_mapVisibility.size() != sizeOfCurrentTileMap ) {
    		// TODO:: signal to world and or tilemap the need for an update to visibilityVector in Actor
    		return;
    	}
    
    	std::vector& tileMapVector = currentTileMap->getTileMapToModify();
    
    	for ( size_t i = 0; i < m_mapVisibility.size(); ++i ) {
    
    		FogOfWarState& stateAtTile = m_mapVisibility[i];
    		stateAtTile = STATE_VISIBLE;
    		Tile& currentTile = tileMapVector[i];
    		currentTile.m_isVisibleToActorInFocus = true;
    	}
    }
    
    
    void Actor::updateVisibilityUsingRayCasting( float deltaSeconds, TileMap* currentTileMap ) {
    
    	UNUSED( deltaSeconds );
    	if ( currentTileMap == nullptr ) {
    		return;
    	}
    
    
    	size_t sizeOfCurrentTileMap = currentTileMap->getNumTilesInTileMap();
    	if ( m_mapVisibility.size() != sizeOfCurrentTileMap ) {
    		// TODO:: signal to world and or tilemap the need for an update to visibilityVector in Actor
    		return;
    	}
    
    	std::vector& tileMapVector = currentTileMap->getTileMapToModify();
    
    	cbengine::Disk2D actorVisibilityDisk;
    	actorVisibilityDisk.radius = m_visibilityRadius;
    	actorVisibilityDisk.origin.x = m_position.x;
    	actorVisibilityDisk.origin.y = m_position.y;
    
    	const float halfTileWidth = 0.50f;
    	const float halfTileHeight = 0.50f;
    	cbengine::Disk2D adjustedTileDisk;
    	adjustedTileDisk.radius = 0.45f;
    
    	PositionVec2 currentTilePosition;
    
    	// Simple naive visibility calculations
    	for ( size_t i = 0; i < m_mapVisibility.size(); ++i ) {
    
    		Tile& currentTile = tileMapVector[i];
    		currentTileMap->convertIndexToVec2( i, currentTilePosition );
    		adjustedTileDisk.origin.x = currentTilePosition.x + halfTileWidth;
    		adjustedTileDisk.origin.y = currentTilePosition.y + halfTileHeight;
    
    		bool isInVisiblityRadius = cbengine::doesDiskIntersectDiskOrTouch( actorVisibilityDisk, adjustedTileDisk );
    		if ( isInVisiblityRadius ) {
    
    			FogOfWarState& currentStateOfTile = m_mapVisibility[i];
    			bool actorHasLineOfSight = doesActorHaveRayCastLineOfSight( currentTileMap, adjustedTileDisk.origin );
    
    			if ( actorHasLineOfSight ) {
    
    				
    				currentStateOfTile = STATE_VISIBLE;
    				currentTile.m_isVisibleToActorInFocus = true;
    			} else {
    
    				if ( !currentTile.doesTileBlockMovementForGameObject( this ) ) {
    					if ( currentStateOfTile == STATE_VISIBLE ) {
    						currentStateOfTile = STATE_REMEMBER_BUT_NOT_IN_VIEW;
    						currentTile.m_isVisibleToActorInFocus = false;
    					} else if ( currentStateOfTile != STATE_REMEMBER_BUT_NOT_IN_VIEW ) {
    						currentStateOfTile = STATE_UNDISCOVERED;
    						currentTile.m_isVisibleToActorInFocus = false;
    					}
    				}
    			}
    			
    		} else {
    
    			currentTile.m_isVisibleToActorInFocus = false;
    			FogOfWarState& currentStateOfTile = m_mapVisibility[i];
    			if ( currentStateOfTile == STATE_VISIBLE ) {
    
    				currentStateOfTile = STATE_REMEMBER_BUT_NOT_IN_VIEW;
    			} else if ( currentStateOfTile != STATE_REMEMBER_BUT_NOT_IN_VIEW ) {
    
    				currentStateOfTile = STATE_UNDISCOVERED;
    			}
    		}
    	}
    
    }
    
    
    bool Actor::doesActorHaveRayCastLineOfSight( TileMap* tileMap, const PositionVec2& positionToCheck ) {
    
    	const float halfWidth = m_AABB2.m_width * 0.50f;
    	const float halfHeight = m_AABB2.m_height * 0.50f;
    	const PositionVec2& currentPosition = getPosition();
    	std::vector& tileMapVector = tileMap->getTileMapToModify();
    	UNUSED(tileMapVector);
    
    	RayCast2DResult currentRayCastResult;
    	PositionVec2 positionToCheckFrom;
    	Tile* tileAtEndPosition = tileMap->getTileAtPosition( positionToCheck );
    	size_t indexOfEndTile = 0;
    	tileMap->convertVec2ToIndex( positionToCheck, indexOfEndTile );
    
    	// Bottom left corner
    	positionToCheckFrom.x = currentPosition.x - halfWidth;
    	positionToCheckFrom.y = currentPosition.y - halfHeight;
    	
    	tileMap->cast2DRay( this, positionToCheckFrom, positionToCheck, currentRayCastResult );
    
    	if ( currentRayCastResult.m_solidTileCollidedWith == nullptr ) {
    
    		return true;
    	} else {
    
    		size_t tileIndex = 0;
    		tileMap->convertVec2ToIndex( currentRayCastResult.m_endPosition, tileIndex );
    		Tile* tileAtPosition = tileMap->getTileAtPosition( currentRayCastResult.m_endPosition );
    
    		if ( tileAtPosition != nullptr ) {
    
    			tileAtPosition->m_isVisibleToActorInFocus = true;
    			m_mapVisibility[tileIndex] = STATE_VISIBLE;
    		}
    	}
    
    	// Bottom right corner
    	positionToCheckFrom.x = currentPosition.x + halfWidth;
    	positionToCheckFrom.y = currentPosition.y - halfHeight;
    	tileMap->cast2DRay( this, positionToCheckFrom, positionToCheck, currentRayCastResult );
    
    	if ( currentRayCastResult.m_solidTileCollidedWith == nullptr ) {
    
    		return true;
    	} else {
    
    		size_t tileIndex = 0;
    		tileMap->convertVec2ToIndex( currentRayCastResult.m_endPosition, tileIndex );
    		Tile* tileAtPosition = tileMap->getTileAtPosition( currentRayCastResult.m_endPosition );
    
    		if ( tileAtPosition != nullptr ) {
    
    			tileAtPosition->m_isVisibleToActorInFocus = true;
    			m_mapVisibility[tileIndex] = STATE_VISIBLE;
    		}
    	}
    
    	// Top right corner
    	positionToCheckFrom.x = currentPosition.x + halfWidth;
    	positionToCheckFrom.y = currentPosition.y + halfHeight;
    	tileMap->cast2DRay( this, positionToCheckFrom, positionToCheck, currentRayCastResult );
    
    	if ( currentRayCastResult.m_solidTileCollidedWith == nullptr ) {
    
    		return true;
    	} else {
    
    		size_t tileIndex = 0;
    		tileMap->convertVec2ToIndex( currentRayCastResult.m_endPosition, tileIndex );
    		Tile* tileAtPosition = tileMap->getTileAtPosition( currentRayCastResult.m_endPosition );
    
    		if ( tileAtPosition != nullptr ) {
    
    			tileAtPosition->m_isVisibleToActorInFocus = true;
    			m_mapVisibility[tileIndex] = STATE_VISIBLE;
    		}
    	}
    
    	// Top left corner
    	positionToCheckFrom.x = currentPosition.x - halfWidth;
    	positionToCheckFrom.y = currentPosition.y + halfHeight;
    	tileMap->cast2DRay( this, positionToCheckFrom, positionToCheck, currentRayCastResult );
    
    	if ( currentRayCastResult.m_solidTileCollidedWith == nullptr ) {
    
    		return true;
    	} else {
    
    		size_t tileIndex = 0; 
    		tileMap->convertVec2ToIndex( currentRayCastResult.m_endPosition, tileIndex );
    		Tile* tileAtPosition = tileMap->getTileAtPosition( currentRayCastResult.m_endPosition );
    
    		if ( tileAtPosition != nullptr ) {
    			
    			tileAtPosition->m_isVisibleToActorInFocus = true;
    			m_mapVisibility[tileIndex] = STATE_VISIBLE;
    		}
    	}
    
    	
    	if ( tileAtEndPosition != nullptr && tileAtEndPosition->doesTileBlockMovementForGameObject( this ) ) {
    
    		currentRayCastResult.m_solidTileCollidedWith = nullptr;
    		tileMap->cast2DRay( this, currentPosition, positionToCheck, currentRayCastResult );
    		size_t tileIndex = 0; 
    		tileMap->convertVec2ToIndex( currentRayCastResult.m_endPosition, tileIndex );
    		Tile* tileAtPosition = tileMap->getTileAtPosition( currentRayCastResult.m_endPosition );
    
    		if ( tileAtPosition != nullptr ) {
    			if ( tileAtPosition != tileAtEndPosition ) {
    
    				FogOfWarState& endTileState = m_mapVisibility[indexOfEndTile];
    				if ( endTileState == STATE_VISIBLE ) {
    					endTileState = STATE_REMEMBER_BUT_NOT_IN_VIEW;
    					tileAtEndPosition->m_isVisibleToActorInFocus = false;
    				} else if ( endTileState != STATE_REMEMBER_BUT_NOT_IN_VIEW ) {
    					endTileState = STATE_UNDISCOVERED;
    					tileAtEndPosition->m_isVisibleToActorInFocus = false;
    				}
    			}
    		}
    	}
    	
    
    	return false;
    }
    
    // TODO:: Account for already visited maps
    void Actor::adjustVisibilityMapForNewTileMap( const TileMap* currentTileMap ) {
    
    	if ( currentTileMap == nullptr ) {
    		return;
    	}
    
    	m_mapVisibility.clear();
    
    	size_t sizeOfCurrentTileMap = currentTileMap->getNumTilesInTileMap();
    
    	for ( size_t i = 0; i < sizeOfCurrentTileMap; ++i ) {
    
    		m_mapVisibility.push_back( STATE_UNDISCOVERED );
    	}
    }
    
    
    void Actor::changeAIActorState( AIActorState newState ) {
    	// TODO::
    	//AIActorState previousState = newState;
    	m_AIActorState = newState;
    	
    }
    
    
    void Actor::outputCurrentActorStatsToConsole() {
    
    	cbengine::Vector4 textColor( 0.10f, 0.10f, 0.90f, 1.0f );
    	Console* sharedConsole = Console::sharedDeveloperConsole();
    	std::string statString;
    	statString = "Showing current stats for Actor: ";
    	statString += getActorName();
    	sharedConsole->addTextToConsole( statString, textColor );
    	statString.clear();
    
    	ObjectStats currentActorStats;
    	getCurrentActorStats( currentActorStats );
    
    	std::string health( std::to_string( static_cast( currentActorStats.getStat( STAT_HEALTH ) ) ) );
    	statString += "Health: ";
    	statString += health;
    	sharedConsole->addTextToConsole( statString, textColor );
    	statString.clear();
    
    	std::string healthRegen( std::to_string( static_cast( currentActorStats.getStat( STAT_HEALTH_REGEN ) ) ) );
    	statString += "Health Regen: ";
    	statString += healthRegen;
    	sharedConsole->addTextToConsole( statString, textColor );
    	statString.clear();
    
    	std::string manaString( std::to_string( static_cast( currentActorStats.getStat( STAT_MANA ) ) ) );
    	statString += "Mana: ";
    	statString += manaString;
    	sharedConsole->addTextToConsole( statString, textColor );
    	statString.clear();
    
    	std::string manaRegen( std::to_string( static_cast( currentActorStats.getStat( STAT_MANA_REGEN ) ) ) );
    	statString += "Mana Regen: ";
    	statString += manaRegen;
    	sharedConsole->addStringToConsole( statString, textColor );
    	statString.clear();
    
    	std::string strengthString( std::to_string( static_cast( currentActorStats.getStat( STAT_STRENGTH ) ) ) );
    	statString += "Strength: ";
    	statString += strengthString;
    	sharedConsole->addStringToConsole( statString, textColor );
    	statString.clear();
    
    	std::string agilityString( std::to_string( static_cast( currentActorStats.getStat( STAT_AGILITY ) ) ) );
    	statString += "Agility: ";
    	statString += agilityString;
    	sharedConsole->addStringToConsole( statString, textColor );
    	statString.clear();
    
    	std::string intString( std::to_string( static_cast( currentActorStats.getStat( STAT_INTELLIGENCE ) ) ) );
    	statString += "Intelligence: ";
    	statString += intString;
    	sharedConsole->addStringToConsole( statString, textColor );
    	statString.clear();
    
    	std::string armorString( std::to_string( static_cast( currentActorStats.getStat( STAT_ARMOR ) ) ) );
    	statString += "Armor: ";
    	statString += armorString;
    	sharedConsole->addStringToConsole( statString, textColor );
    	statString.clear();
    
    	std::string blockChance( std::to_string( static_cast( currentActorStats.getStat( STAT_BLOCK_CHANCE ) ) ) );
    	statString += "Block Chance: ";
    	statString += blockChance;
    	sharedConsole->addStringToConsole( statString, textColor );
    	statString.clear();
    
    	std::string blockString( std::to_string( static_cast( currentActorStats.getStat( STAT_BLOCK ) ) ) );
    	statString += "Block: ";
    	statString += blockString;
    	sharedConsole->addStringToConsole( statString, textColor );
    	statString.clear();
    
    	std::string damageMin( std::to_string( static_cast( currentActorStats.getStat( STAT_DAMAGE_MIN ) ) ) );
    	statString += "Damage Min: ";
    	statString += damageMin;
    	sharedConsole->addStringToConsole( statString, textColor );
    	statString.clear();
    
    	std::string damageMax( std::to_string( static_cast( currentActorStats.getStat( STAT_DAMAGE_MAX ) ) ) );
    	statString += "Damage Max: ";
    	statString += damageMax;
    	sharedConsole->addStringToConsole( statString, textColor );
    	statString.clear();
    
    	std::string currentLevel( std::to_string(  static_cast( m_currentLevel ) ) );
    	statString += "Current Level: ";
    	statString += currentLevel;
    	sharedConsole->addStringToConsole( statString, textColor );
    	statString.clear();
    }
    							
  • Game.hpp

    #ifndef included_LadyBugGame
    #define included_LadyBugGame
    #pragma once
    
    #include 
    #include 
    
    #include "../EngineCode/EngineCommon.hpp"
    #include "../EngineCode/CBGame.hpp"
    #include "ActionDelegate.hpp"
    
    class BitmapFont;
    class GameWorld;
    
    class LadyBugGame : public CBGame, public ActionDelegate {
    public:
    	virtual ~LadyBugGame();
    	LadyBugGame();
    
    	// Core Functions
    	virtual void update( const float deltaSeconds );
    	virtual void render( const float deltaSeconds ) const;
    
    	// Keyboard Input
    	virtual bool processKeyDownEvent( unsigned char virtualKeyCode );
    	virtual bool processKeyUpEvent( unsigned char virtualKeyCode );
    	virtual void processWMCHARKeyEvents( std::queue wmCharQueue );
    
    	// Delegate Overrides
    	virtual const bool *					getKeyboardStatus() const;
    	virtual int								getNumVirtualKeys() const;
    	virtual bool							isConsoleVisible() const;
    	virtual void							notifyConsoleEnd();
    	virtual std::queue&		getWmCharMessageQueue();
    	virtual void							notifyGameOfObjectSpawn( GameObject* gameObject );
    
    	// Inline Mutators
    	const GameWorld* getCurrentGameWorld() const;
    	bool isGameOver() const;
    
    protected:
    	// Initialization Functions
    	void initializeGameData();
    	void initializeTileBlueprintData();
    	void initializeTextureAtlasData();
    	void initializeActorBlueprintData();
    	void initializeItemBlueprintData();
    	void initializeEntityBlueprintData();
    	void initializeProjectileBlueprintData();
    	void initializeGameWorld();
    	void spawnInitialGameObjectsForStartMap();
    	void setDefaultVariableValues();
    
    	// Console Functions
    	void loadConsoleFont( BitmapFont*& consoleFont );
    	void loadConsoleAndCommandPrompt();
    	void updateConsole( float deltaSeconds );
    	void renderConsole( float deltaSeconds ) const;
    
    	// Core Protected Functions
    	void updateGameWorld( float deltaSeconds );
    	void renderGameWorld( float deltaSeconds ) const;
    	void processKeyboardInput( float deltaSeconds );
    
    	// Debug
    	void renderDebugAxis() const;
    
    private:
    	PREVENT_COPY_AND_ASSIGN( LadyBugGame );
    
    	GameWorld*					m_currentWorld;
    	std::queue   m_wmCharEvents;
    	bool						m_consoleMode;
    	float						m_durationSinceConsoleExit;
    	bool						m_gameIsPaused;
    	bool						m_isGameOver;
    };
    
    
    // Inline Mutators
    inline const GameWorld* LadyBugGame::getCurrentGameWorld() const {
    	return m_currentWorld;
    }
    
    
    inline bool LadyBugGame::isGameOver() const {
    	return m_isGameOver;
    }
    
    #endif
    							
  • Game.cpp

    #include "LadyBugGame.hpp"
    
    #include 
    
    #include "GameWorld.hpp"
    #include "GameConstants.hpp"
    #include "LBCamera.hpp"
    
    #include "..\EngineCode\XMLParser.hpp"
    #include "..\EngineCode\XMLNode.hpp"
    #include "..\EngineCode\TextureAtlasManager.hpp"
    #include "..\EngineCode\TextureAtlas.hpp"
    #include "..\EngineCode\TileBlueprint.hpp"
    #include "..\EngineCode\Tile.hpp"
    #include "..\EngineCode\TileMapManager.hpp"
    #include "..\EngineCode\ActorBlueprint.hpp"
    #include "..\EngineCode\Actor.hpp"
    #include "..\EngineCode\ItemBlueprint.hpp"
    #include "..\EngineCode\Item.hpp"
    #include "..\EngineCode\EntityBlueprint.hpp"
    #include "..\EngineCode\Entity2D.hpp"
    #include "..\EngineCode\ProjectileBlueprint.hpp"
    #include "..\EngineCode\Projectile.hpp"
    
    #include "..\EngineCode\Console.hpp"
    #include "..\EngineCode\FontManager.hpp"
    #include "..\EngineCode\BitmapFont.hpp"
    #include "..\EngineCode\MatrixStack.hpp"
    #include "..\EngineCode\Matrix44.hpp"
    #include "..\EngineCode\DebugShapeRenderer.hpp"
    #include "..\EngineCode\Geometry3D.hpp"
    
    
    LadyBugGame::~LadyBugGame() {
    
    	if ( m_currentWorld ) {
    		delete m_currentWorld;
    	}
    }
    
    
    LadyBugGame::LadyBugGame() {
    
    	setDefaultVariableValues();
    	loadConsoleAndCommandPrompt();
    	initializeGameData();
    	initializeGameWorld();
    }
    
    
    void LadyBugGame::initializeGameData() {
    
    	initializeTileBlueprintData();
    	initializeTextureAtlasData();
    	initializeActorBlueprintData();
    	initializeItemBlueprintData();
    	initializeEntityBlueprintData();
    	initializeProjectileBlueprintData();
    }
    
    void LadyBugGame::update( const float deltaSeconds ) {
    
    	if ( !m_consoleMode ) {
    
    		m_currentWorld->updateCamera( deltaSeconds );
    		processKeyboardInput( deltaSeconds );
    		m_durationSinceConsoleExit = m_durationSinceConsoleExit - deltaSeconds;
    	} else {
    
    		m_durationSinceConsoleExit = ladybug::CONSOLE_RESET_DUR;
    	}
    
    	if ( !m_gameIsPaused ) {
    		updateGameWorld( deltaSeconds );
    	}
    	
    	updateConsole( deltaSeconds );
    }
    
    
    void LadyBugGame::render( const float deltaSeconds ) const {
    
    	renderGameWorld( deltaSeconds );
    	renderConsole( deltaSeconds );
    }
    
    
    void LadyBugGame::updateGameWorld( float deltaSeconds ) {
    
    	m_currentWorld->update( deltaSeconds );
    }
    
    
    void LadyBugGame::renderGameWorld( float deltaSeconds ) const {
    
    	m_currentWorld->render( deltaSeconds );
    }
    
    
    // Console Functions
    void LadyBugGame::updateConsole( float deltaSeconds ) {
    
    	if ( m_consoleMode ) {
    		Console * developerConsole = Console::sharedDeveloperConsole();
    		developerConsole->update( deltaSeconds );
    	}
    }
    
    
    void LadyBugGame::renderConsole( float deltaSeconds ) const {
    
    	if ( m_consoleMode ) {
    		MatrixStack * matrixStack = MatrixStack::sharedMatrixStack();
    		glUseProgram(0);
    		glMatrixMode( GL_MODELVIEW );
    		glLoadIdentity();
    		matrixStack->emptyCurrentMatrixStackAndPushIdentityMatrix();
    
    		matrixStack->createOrthoMatrixAndPushToStack( 0.0f, 
    			( ladybug::SCREEN_WIDTH ), 
    			0.0f, 
    			( ladybug::SCREEN_HEIGHT ),
    			0.0f,
    			1.0f );
    
    		const Matrix44 & topOfStack = matrixStack->getMatrixFromTopOfStack();
    		glLoadMatrixf( topOfStack.matrixData );
    
    		Console* developerConsole = Console::sharedDeveloperConsole();
    		developerConsole->render( deltaSeconds );
    
    		matrixStack->emptyCurrentMatrixStackAndPushIdentityMatrix();
    	}
    }
    
    // TODO :: Loop through directory and load all blueprints
    void LadyBugGame::initializeTileBlueprintData() {
    
    	std::string tileBlueprintFileName = "TileBlueprintDefinitions.xml";
    	XMLParser& xmlParser = XMLParser::getSharedXMLParser();
    	xmlParser.loadXMLDocument( ladybug::TILEBLUEPRINT_LOCATION, "TileBlueprintDefinitions.xml", true );
    	XMLNode rootTileBlueprintNode = xmlParser.getActiveDocumentRootNode();
    	TileBlueprint* firstBlueprint = new TileBlueprint( rootTileBlueprintNode );
    	TileBlueprint::addTileBlueprintToRegistry( firstBlueprint );
    	XMLNode* currentNode = &rootTileBlueprintNode;
    	XMLNode siblingNode;
    
    	bool siblingFound = true;
    	while ( siblingFound ) {
    
    		siblingFound = currentNode->getNextSibling( siblingNode );
    		if ( siblingFound ) {
    			currentNode = &siblingNode;
    			TileBlueprint* tileBlueprint = new TileBlueprint( siblingNode );
    			TileBlueprint::addTileBlueprintToRegistry( tileBlueprint );
    		} // end if
    	} // end while
    
    	xmlParser.releaseDocumentFromCacheWithName( tileBlueprintFileName );
    } // end function
    
    // TODO :: Use a file directory loop
    void LadyBugGame::initializeTextureAtlasData() {
    
    	std::string textureAtlasFilePath = "TextureAtlas/Environment.xml";
    	std::string textureAtlasFileName = "Environment.xml";
    	TextureAtlasManager& atlasManager = TextureAtlasManager::getSharedTextureAtlasManager();
    	atlasManager.loadTextureAtlasOrGrabFromCache( textureAtlasFilePath, textureAtlasFileName );
    	// This function call is very imporant
    	TileBlueprint::populateBlueprintTextureCoordinates();
    }
    
    // TODO :: Use a file directory loop
    void LadyBugGame::initializeActorBlueprintData() {
    
    	XMLParser& xmlParser = XMLParser::getSharedXMLParser();
    	std::string actorBlueprintFilePath = "XML/ActorBlueprints/ActorBlueprints.xml";
    	std::string actorBlueprintFileName = "ActorBlueprints.xml";
    	xmlParser.loadXMLDocument( actorBlueprintFilePath, actorBlueprintFileName, true );
    	XMLNode rootActorBlueprintNode = xmlParser.getActiveDocumentRootNode();
    
    	ActorBlueprint* firstBlueprint = new ActorBlueprint( rootActorBlueprintNode );
    	ActorBlueprint::addActorBlueprintToRegistry( firstBlueprint );
    	XMLNode* currentNode = &rootActorBlueprintNode;
    	XMLNode siblingNode;
    
    	bool siblingFound = true;
    	while ( siblingFound ) {
    
    		siblingFound = currentNode->getNextSibling( siblingNode );
    		if ( siblingFound ) {
    			currentNode = &siblingNode;
    			ActorBlueprint* actorBlueprint = new ActorBlueprint( siblingNode );
    			ActorBlueprint::addActorBlueprintToRegistry( actorBlueprint );
    		} // end if
    	} // end while
    
    	xmlParser.releaseDocumentFromCacheWithName( actorBlueprintFileName );
    }
    
    // TODO :: Use a file directory loop
    void LadyBugGame::initializeItemBlueprintData() {
    
    	XMLParser& xmlParser = XMLParser::getSharedXMLParser();
    	std::string itemBlueprintFilePath = "XML/ItemBlueprints/ItemBlueprints.xml";
    	std::string itemBlueprintFileName = "ItemBlueprints.xml";
    	xmlParser.loadXMLDocument( itemBlueprintFilePath, itemBlueprintFileName, true );
    	XMLNode rootItemBlueprintNode = xmlParser.getActiveDocumentRootNode();
    
    	ItemBlueprint* firstBlueprint = new ItemBlueprint( rootItemBlueprintNode );
    	ItemBlueprint::addItemBlueprintToRegistry( firstBlueprint );
    	XMLNode* currentNode = &rootItemBlueprintNode;
    	XMLNode siblingNode;
    
    	bool siblingFound = true;
    	while ( siblingFound ) {
    
    		siblingFound = currentNode->getNextSibling( siblingNode );
    		if ( siblingFound ) {
    			currentNode = &siblingNode;
    			ItemBlueprint* itemBlueprint = new ItemBlueprint( siblingNode );
    			ItemBlueprint::addItemBlueprintToRegistry( itemBlueprint );
    		} // end if
    	} // end while
    
    	xmlParser.releaseDocumentFromCacheWithName( itemBlueprintFileName );
    }
    
    // TODO :: Use a file directory loop
    void LadyBugGame::initializeEntityBlueprintData() {
    
    	XMLParser& xmlParser = XMLParser::getSharedXMLParser();
    	std::string entityBlueprintFilePath = "XML/EntityBlueprints/EntityBlueprints.xml";
    	std::string entityBlueprintFileName = "EntityBlueprints.xml";
    	xmlParser.loadXMLDocument( entityBlueprintFilePath, entityBlueprintFileName, true );
    	XMLNode rootEntityBlueprintNode = xmlParser.getActiveDocumentRootNode();
    
    	EntityBlueprint* firstBlueprint = new EntityBlueprint( rootEntityBlueprintNode );
    	EntityBlueprint::addEntityBlueprintToRegistry( firstBlueprint );
    	XMLNode* currentNode = &rootEntityBlueprintNode;
    	XMLNode siblingNode;
    
    	bool siblingFound = true;
    	while ( siblingFound ) {
    
    		siblingFound = currentNode->getNextSibling( siblingNode );
    		if ( siblingFound ) {
    			currentNode = &siblingNode;
    			EntityBlueprint* entityBlueprint = new EntityBlueprint( siblingNode );
    			EntityBlueprint::addEntityBlueprintToRegistry( entityBlueprint );
    		} // end if
    	} // end while
    
    	xmlParser.releaseDocumentFromCacheWithName( entityBlueprintFileName );
    }
    
    
    void LadyBugGame::initializeProjectileBlueprintData() {
    
    	XMLParser& xmlParser = XMLParser::getSharedXMLParser();
    	std::string projectileBlueprintFilePath = "XML/ProjectileBlueprints/ProjectileBlueprints.xml";
    	std::string projectileBlueprintFileName = "ProjectileBlueprints.xml";
    	xmlParser.loadXMLDocument( projectileBlueprintFilePath, projectileBlueprintFileName, true );
    	XMLNode rootProjectileBlueprintNode = xmlParser.getActiveDocumentRootNode();
    
    	ProjectileBlueprint* firstBlueprint = new ProjectileBlueprint( rootProjectileBlueprintNode );
    	ProjectileBlueprint::addProjectileBlueprintToRegistry( firstBlueprint );
    	XMLNode* currentNode = &rootProjectileBlueprintNode;
    	XMLNode siblingNode;
    
    	bool siblingFound = true;
    	while ( siblingFound ) {
    
    		siblingFound = currentNode->getNextSibling( siblingNode );
    		if ( siblingFound ) {
    			currentNode = &siblingNode;
    			ProjectileBlueprint* projectileBlueprint = new ProjectileBlueprint( siblingNode );
    			ProjectileBlueprint::addProjectileBlueprintToRegistry( projectileBlueprint );
    		} // end if
    	} // end while
    
    	xmlParser.releaseDocumentFromCacheWithName( projectileBlueprintFileName );
    
    
    }
    
    
    void LadyBugGame::initializeGameWorld() {
    	// TODO :: Move the starting map decision to an external file
    	std::string tileMapFilePath = "TileMaps/LadyBugA3.tmx";
    	std::string tileMapFileName = "LadyBugA3.tmx";
    	m_currentWorld = new GameWorld( this, tileMapFilePath, tileMapFileName );
    	// Uncomment for testing
    	//spawnInitialGameObjectsForStartMap(); // TEST FUNCTION
    }
    
    // THIS IS A TEST FUNCTION : WILL BE REMOVED LATER
    void LadyBugGame::spawnInitialGameObjectsForStartMap() {
    	// Use this function to test spawning of Actor,Item, and Entity
    	PositionVec2 startPosition( 10.0f, 10.0f );
    	// Test Actors
    	std::string testActor = "Hero";
    	m_currentWorld->spawnActorAtPositionInTileMap( testActor, startPosition, m_currentWorld->getCurrentTileMap() );
    	startPosition.x = 20.0f;
    	startPosition.y = 12.0f;
    	testActor = "Orc Pawn";
    	m_currentWorld->spawnActorAtPositionInTileMap( testActor, startPosition, m_currentWorld->getCurrentTileMap() );
    
    	// Test Items
    	std::string testItem = "MinorHealthPotion";
    	std::string testItemTwo = "MinorManaPotion";
    	std::string testItemThree = "MinorHastePotion";
    	startPosition.x = 15.0f;
    	startPosition.y = 10.0f;
    	m_currentWorld->spawnItemAtPositionInTileMap( testItem, startPosition, m_currentWorld->getCurrentTileMap() );
    	startPosition.x = 20.0f;
    	startPosition.y = 18.0f;
    	m_currentWorld->spawnItemAtPositionInTileMap( testItemTwo, startPosition, m_currentWorld->getCurrentTileMap() );
    	startPosition.x = 2.0f;
    	startPosition.y = 10.0f;
    	m_currentWorld->spawnItemAtPositionInTileMap( testItemThree, startPosition, m_currentWorld->getCurrentTileMap() );
    	
    	// Test Entities
    	std::string testEntityOne = "Chest";
    	std::string testEntityTwo = "Crate";
    	startPosition.x = 14.0f;
    	startPosition.y = 12.0f;
    	m_currentWorld->spawnEntityAtPositionInTileMap( testEntityOne, startPosition, m_currentWorld->getCurrentTileMap() );
    	startPosition.x = 16.0f;
    	startPosition.y = 12.0f;
    	m_currentWorld->spawnEntityAtPositionInTileMap( testEntityTwo, startPosition, m_currentWorld->getCurrentTileMap() );
    }
    
    // Console Initialization Functions
    void LadyBugGame::loadConsoleFont( BitmapFont*& consoleFont ) {
    
    	FontManager * fontManager = FontManager::sharedFontManager();
    	std::string fontName = "MainFont";
    	std::string fontXMLFilePath = "Fonts/MainFont_EN.FontDef.xml";
    	std::string fontTextureFilePath = "Fonts/MainFont_EN_00.png";
    	consoleFont = fontManager->loadBitmapFont( "MainFont", fontXMLFilePath, fontTextureFilePath );
    	assert( consoleFont != nullptr );
    }
    
    
    void LadyBugGame::loadConsoleAndCommandPrompt() {
    
    	BitmapFont* consoleFont = nullptr;
    	loadConsoleFont( consoleFont );
    	Console * sharedConsole = Console::sharedDeveloperConsole();
    	std::string consoleTextureName = "Art/DeveloperConsoleBackground.png";
    	std::string commandPromptTextureName = "Art/CommandPromptBackground.png";
    	cbengine::Vector4 consoleColor( 1.0f, 1.0f, 1.0f, 0.80f );
    	cbengine::Vector2 consolePosition( 0.0f, 900.0f - CONSOLE_HEIGHT );
    	cbengine::Vector2 commandPromptPosition( 0.0f, 0.0f );
    	sharedConsole->buildConsoleAndCommandPrompt( consoleTextureName, 
    		commandPromptTextureName, 
    		consoleColor, 
    		commandPromptPosition, 
    		consolePosition, 
    		consoleFont );
    	sharedConsole->setActionDelegate( this );
    }
    
    // ActionDelegate Overrides
    const bool * LadyBugGame::getKeyboardStatus() const {
    	return m_isKeyDown;
    }
    
    
    int LadyBugGame::getNumVirtualKeys() const {
    	return NUM_VIRTUAL_KEYS;
    }
    
    
    std::queue& LadyBugGame::getWmCharMessageQueue() {
    	return m_wmCharEvents;
    }
    
    
    void LadyBugGame::notifyGameOfObjectSpawn( GameObject* gameObject ) {
    
    	assert( m_currentWorld != nullptr );
    	m_currentWorld->addSpawnedGameObjectToRegistry( gameObject );
    }
    
    void LadyBugGame::setDefaultVariableValues() {
    
    	for ( int i = 0; i < NUM_VIRTUAL_KEYS; ++i ) {
    		m_isKeyDown[i] = false;
    	}
    
    	m_isGameOver = false;
    	m_consoleMode = false;
    	m_gameIsPaused = false;
    	m_durationSinceConsoleExit = ladybug::CONSOLE_RESET_DUR;
    }
    
    
    bool LadyBugGame::isConsoleVisible() const {
    	return m_consoleMode;
    }
    
    
    void LadyBugGame::notifyConsoleEnd() {
    	m_consoleMode = false;
    }
    
    
    // Input (Overrides from CBGame)
    bool LadyBugGame::processKeyDownEvent( unsigned char virtualKeyCode ) {
    	// Key still propagates forward
    	m_isKeyDown[virtualKeyCode] = true;
    	return false;
    } // end processKeyDown
    
    
    bool LadyBugGame::processKeyUpEvent( unsigned char virtualKeyCode ) {
    	// Key still propagates forward
    	m_isKeyDown[virtualKeyCode] = false;
    	return false;
    } // end processKeyUp
    
    
    void LadyBugGame::processWMCHARKeyEvents( std::queue wmCharQueue ) {
    	m_wmCharEvents = wmCharQueue;
    }
    
    
    void LadyBugGame::processKeyboardInput( float deltaSeconds ) {
    
    	UNUSED(deltaSeconds);
    	static bool aposKeyDown = false;
    	if ( m_isKeyDown[ VK_OEM_3 ] ) {
    		if ( !aposKeyDown ) {
    			m_consoleMode = !m_consoleMode;
    		}
    		aposKeyDown = true;
    	} else {
    		aposKeyDown = false;
    	}
    
    	static bool escapeKeyDown = false;
    	if ( m_isKeyDown[ VK_ESCAPE ] ) {
    		if ( !escapeKeyDown && !m_consoleMode && ( m_durationSinceConsoleExit < 0.0f ) ) {
    			m_isGameOver = true;
    			std::exit(0);
    		}
    		escapeKeyDown = true;
    
    	} else {
    		escapeKeyDown = false;
    	}
    
    	static bool pKeyDown = false;
    	if ( m_isKeyDown[ 'P' ] ){
    		if ( !pKeyDown ) {
    			m_gameIsPaused = !m_gameIsPaused;
    		} 
    		pKeyDown = true;
    	} else {
    		pKeyDown = false;
    	}
    
    }
    
    
    void LadyBugGame::renderDebugAxis() const {
    
    	MatrixStack * matrixStack = MatrixStack::sharedMatrixStack();
    	glUseProgram(0);
    	const float debugAxisLineWidth = 5.0f;
    	const float debugAxisLength = 10.0f;
    	glMatrixMode( GL_MODELVIEW );
    	glLoadIdentity();
    	const Matrix44 & topMatrix = matrixStack->getMatrixFromTopOfStack();
    	glLoadMatrixf(  topMatrix.matrixData );
    	cbengine::drawAxis( debugAxisLength, debugAxisLineWidth );		
    }
    							
  • ActorBlueprints.xml

    
    
    	
    	
    	
    		
    			
    		
    		
    			 
    			
    			
    		
    	
    	
    		
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    		
    	
    	
    		
    			
    			
    			
    			
    			
    			
    		
    	
    
    
    
    
    	
    	
    	
    		
    			
    		
    		
    			
    			
    			
    			
    			
    			
    			
    			
    			
    		
    	
    	
    		
    	
    	
    		
    			
    		
    		
    		
    		
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    		
    		
    		
    	
    	
    	
    
    
    
    	
    	
    	
    		
    			
    		
    		
    			
    			
    			
    			
    			
    			
    			
    			
    			
    		
    	
    	
    		
    	
    	
    		
    			
    		
    		
    		
    		
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    		
    		
    		
    		
    	
    	
    	
    
    
    
    
    	
    	
    	
    		
    			
    		
    		
    			
    			
    			
    			
    			
    			
    			
    			
    			
    		
    	
    	
    		
    		
    	
    	
    		
    			
    		
    		
    			
    			
    		
    		
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    	
    
    
    
    
    	
    	
    	
    		
    			
    		
    		
    			
    			
    			
    			
    			
    			
    			
    			
    			
    		
    	
    	
    		
    	
    	
    		
    			
    		
    		
    			
    			
    		
    		
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    		
    		
    		
    	
    	
    	
    
    
    
    
    	
    	
    	
    		
    			
    		
    		
    			
    			
    			
    			
    			
    			
    			
    			
    		
    	
    	
    		
    		
    	
    	
    		
    			
    		
    		
    			
    			
    		
    		
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    		
    		
    	
    	
    	
    
    
    
    	
    	
    	
    		
    			
    		
    		
    			
    			
    			
    			
    			
    			
    			
    			
    			 
    		
    	
    	
    		
    		
    	
    	
    		
    			
    		
    		
    		
    		
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    		
    		
    		
    		
    		
    	
    	
    	
    
    
    
    
    	
    	
    	
    		
    			
    		
    		
    			
    			
    			
    			
    			
    			
    			
    			 
    			
    		
    	
    	
    		
    		
    	
    	
    		
    			
    		
    		
    			
    			
    		
    		
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    		
    	
    	
    	
    
    
    
    
    	
    	
    	
    		
    			
    		
    		
    			
    			
    			
    			
    			
    			
    			
    			 
    			
    		
    	
    	
    		
    	
    	
    		
    			
    		
    		
    			
    			
    		
    		
    		
    		
    			
    		
    		
    			
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    		
    	
    	
    	
    
    
    
    
    	
    	
    	
    		
    			
    		
    		
    			
    			
    			
    			
    			
    			
    			
    			 
    			
    		
    	
    	
    		
    	
    	
    		
    			
    		
    		
    			
    		
    		
    		
    		
    			
    		
    		
    			
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    		
    	
    	
    	
    
    
    
    
    	
    	
    	
    		
    			
    		
    		
    			
    			
    			
    			
    			
    			
    			
    			 
    			
    		
    	
    	
    		
    	
    	
    		
    			
    		
    		
    			
    			
    		
    		
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    		
    		
    	
    	
    	
    
    
    
    
    	
    	
    	
    		
    			
    		
    		
    			
    			
    			
    			
    			
    			
    			
    			
    			
    		
    	
    	
    		
    	
    	
    		
    			
    		
    		
    		
    		
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    	
    	
    	
    
    
    
    
    	
    	
    	
    		
    			
    		
    		
    			
    			
    			
    			
    			
    			
    			
    			
    			
    		
    	
    	
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    	
    	
    	
    
    
    
    
    	
    	
    	
    		
    			
    		
    		
    			 
    			 
    			
    			
    			
    			 
    			
    			
    			
    		
    	
    	
    		
    	
    	
    		
    			
    		
    		
    			
    			
    		
    		
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    		
    	
    	
    	
    
    
    
    
    	
    	
    	
    		
    			
    		
    		
    			 
    			 
    			
    			
    			
    			
    			
    			
    			
    		
    	
    	
    		
    		
    	
    	
    		
    			
    		
    		
    			
    			
    		
    		
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    		
    		
    	
    	
    	
    
    
    
    
    	
    	
    	
    		
    			
    		
    		
    			
    			 
    			 
    			
    			
    			
    			
    			
    			
    		
    	
    	
    		
    	
    	
    		
    			
    		
    		
    			
    			
    		
    		
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    		
    	
    	
    	
    
    
    
    
    	
    	
    	
    		
    			
    		
    		
    			
    			 
    			 
    			
    			
    			
    			
    			
    			
    		
    	
    	
    		
    	
    	
    		
    			
    		
    		
    			
    			
    		
    		
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    		
    	
    	
    	
    
    
    
    	
    	
    	
    		
    			
    		
    		
    			 
    			 
    			
    			
    			
    			
    			
    			
    			
    		
    	
    	
    		
    	
    	
    		
    			
    		
    		
    		
    		
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    		
    		
    	
    	
    	
    
    
    
    
    	
    	
    	
    		
    			
    		
    		
    			 
    			 
    			
    			
    			
    			
    			
    			
    			
    		
    	
    	
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    	
    	
    	
    
    
    
    
    	
    	
    	
    		
    			
    		
    		
    			
    			
    			 
    			 
    			
    			
    			
    			
    			
    		
    	
    	
    		
    	
    	
    		
    		
    		
    			
    		
    		
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    	
    	
    	
    
    
    
    
    	
    	
    	
    		
    			
    		
    		
    			
    			
    			
    			 
    			 
    			
    			
    			
    			
    		
    	
    	
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    		
    		
    		
    		
    		
    		
    	
    	
    	
    	
    	
    
    							
  • ItemBlueprints.xml

    
    
    	
    	
    		
    			
    		
    	
    
    	
    
    	
    
    
    
    
    	
    	
    		
    			
    		
    	
    
    	
    
    	
    
    
    
    
    	
    	
    		
    			
    		
    	
    
    	
    
    	
    
    
    
    	
    	
    		
    			
    		
    	
    
    	
    
    	
    
    
    
    
    	
    	
    		
    			
    		
    	
    
    	
    
    	
    
    
    
    	
    	
    		
    			
    		
    	
    
    	
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    	
    
    
    
    
    	
    	
    		
    			
    		
    	
    
    	
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    	
    
    
    
    
    	
    	
    		
    			
    		
    	
    
    	
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    	
    
    
    
    
    	
    	
    		
    			
    		
    	
    
    	
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    	
    
    
    
    
    	
    	
    		
    			
    		
    	
    
    	
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    	
    
    
    
    
    	
    	
    		
    			
    		
    	
    
    	
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    	
    
    
    
    
    	
    	
    		
    			
    		
    	
    
    	
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    	
    
    
    
    
    	
    	
    		
    			
    		
    	
    
    	
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    	
    
    
    
    
    	
    	
    		
    			
    		
    	
    
    	
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    	
    
    
    
    
    	
    	
    		
    			
    		
    	
    
    	
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    	
    
    	
    
    
    
    
    
    	
    	
    		
    			
    		
    	
    
    	
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    	
    	
    
    
    
    
    
    	
    	
    		
    			
    		
    	
    
    	
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    	
    	
    
    
    
    
    	
    	
    		
    			
    		
    	
    
    	
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    	
    	
    
    
    
    
    	
    	
    		
    			
    		
    	
    
    	
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    	
    
    
    
    
    	
    	
    		
    			
    		
    	
    
    	
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    	
    
    
    
    
    	
    	
    		
    			
    		
    	
    
    	
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    	
    
    
    
    
    	
    	
    		
    			
    		
    	
    
    	
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    	
    
    							
  • EntityBlueprints.xml