GlobalSpirit::GlobalSpirit(uint32 id, uint32 count) :
    GlobalObject(id, count)
{
    if((_id <= MAX_LEG_ARMOR_ID) || (_id > MAX_SPIRIT_ID)) {
        IF_PRINT_WARNING(GLOBAL_DEBUG) << "invalid id in constructor: " << _id << std::endl;
        _InvalidateObject();
        return;
    }

    ReadScriptDescriptor& script_file = GlobalManager->GetSpiritsScript();
    if (script_file.DoesTableExist(_id) == false) {
        IF_PRINT_WARNING(GLOBAL_DEBUG) << "No valid data for spirit id: " << _id << std::endl;
        _InvalidateObject();
        return;
    }

    // Load the spirit data from the script
    script_file.OpenTable(_id);
    _LoadObjectData(script_file);

    script_file.CloseTable();
    if (script_file.IsErrorDetected()) {
        IF_PRINT_WARNING(GLOBAL_DEBUG) << "one or more errors occurred while reading spirit data - they are listed below" << std::endl
            << script_file.GetErrorMessages() << std::endl;

        _InvalidateObject();
    }
} // void GlobalSpirit::GlobalSpirit(uint32 id, uint32 count = 1)
GlobalKeyItem::GlobalKeyItem(uint32 id, uint32 count) :
	GlobalObject(id, count)
{
	if ((_id <= MAX_SHARD_ID) || (_id > MAX_KEY_ITEM_ID)) {
		IF_PRINT_WARNING(GLOBAL_DEBUG) << "invalid id in constructor: " << _id << endl;
		_InvalidateObject();
		return;
	}

	ReadScriptDescriptor& script_file = GlobalManager->GetKeyItemsScript();
	if (script_file.DoesTableExist(_id) == false) {
		IF_PRINT_WARNING(GLOBAL_DEBUG) << "no valid data for key item in definition file: " << _id << endl;
		_InvalidateObject();
		return;
	}

	// Load the item data from the script
	script_file.OpenTable(_id);
	_LoadObjectData(script_file);

	script_file.CloseTable();
	if (script_file.IsErrorDetected()) {
		if (GLOBAL_DEBUG) {
			PRINT_WARNING << "one or more errors occurred while reading key item data - they are listed below" << endl;
			cerr << script_file.GetErrorMessages() << endl;
		}
		_InvalidateObject();
	}
} // void GlobalKeyItem::GlobalKeyItem(uint32 id, uint32 count = 1)
GlobalWeapon::GlobalWeapon(uint32 id, uint32 count) :
    GlobalObject(id, count)
{
    if((_id <= MAX_ITEM_ID) || (_id > MAX_WEAPON_ID)) {
        IF_PRINT_WARNING(GLOBAL_DEBUG) << "invalid id in constructor: " << _id << std::endl;
        _InvalidateObject();
        return;
    }

    ReadScriptDescriptor &script_file = GlobalManager->GetWeaponsScript();
    if(script_file.DoesTableExist(_id) == false) {
        IF_PRINT_WARNING(GLOBAL_DEBUG) << "no valid data for weapon in definition file: " << _id << std::endl;
        _InvalidateObject();
        return;
    }

    // Load the weapon data from the script
    script_file.OpenTable(_id);
    _LoadObjectData(script_file);

    _LoadStatusEffects(script_file);
    _LoadEquipmentSkills(script_file);

    _physical_attack = script_file.ReadUInt("physical_attack");
    _magical_attack = script_file.ReadUInt("magical_attack");

    _usable_by = script_file.ReadUInt("usable_by");

    uint32 spirits_number = script_file.ReadUInt("slots");
    // Only permit a max of 5 spirits for equipment
    if (spirits_number > 5) {
        spirits_number = 5;
        PRINT_WARNING << "More than 5 spirit slots declared in item " << _id << std::endl;
    }
    _spirit_slots.resize(spirits_number, NULL);

    // Load the possible battle ammo animated image filename.
    _ammo_image_file = script_file.ReadString("battle_ammo_animation_file");

    // Load the weapon battle animation info
    if (script_file.DoesTableExist("battle_animations"))
        _LoadWeaponBattleAnimations(script_file);

    script_file.CloseTable(); // id
    if(script_file.IsErrorDetected()) {
        if(GLOBAL_DEBUG) {
            PRINT_WARNING << "one or more errors occurred while reading weapon data - they are listed below"
                          << std::endl << script_file.GetErrorMessages() << std::endl;
        }
        _InvalidateObject();
    }
} // void GlobalWeapon::GlobalWeapon(uint32 id, uint32 count = 1)
GlobalShard::GlobalShard(uint32 id, uint32 count) :
	GlobalObject(id, count)
{
	if ((_id <= MAX_LEG_ARMOR_ID) || (_id > MAX_SHARD_ID)) {
		IF_PRINT_WARNING(GLOBAL_DEBUG) << "invalid id in constructor: " << _id << endl;
		_InvalidateObject();
		return;
	}

	// TODO: uncomment the code below when shards scripts are available
// 	ReadScriptDescriptor& script_file = GlobalManager->GetShardsScript();
// 	if (script_file.DoesTableExist(_id) == false) {
// 		IF_PRINT_WARNING(GLOBAL_DEBUG) << "no valid data for shard in definition file: " << _id << endl;
// 		_InvalidateObject();
// 		return;
// 	}
//
// 	// Load the shard data from the script
// 	script_file.OpenTable(_id);
// 	_LoadObjectData(script_file);
//
// 	script_file.CloseTable();
// 	if (script_file.IsErrorDetected()) {
// 		if (GLOBAL_DEBUG) {
// 			PRINT_WARNING << "one or more errors occurred while reading shard data - they are listed below" << endl;
// 			cerr << script_file.GetErrorMessages() << endl;
// 		}
// 		_InvalidateObject();
// 	}
} // void GlobalShard::GlobalShard(uint32 id, uint32 count = 1)
void GlobalObject::_LoadObjectData(hoa_script::ReadScriptDescriptor& script) {
	_name = MakeUnicodeString(script.ReadString("name"));
	_description = MakeUnicodeString(script.ReadString("description"));
	_price = script.ReadUInt("standard_price");
	string icon_file = script.ReadString("icon");
	if (_icon_image.Load(icon_file) == false) {
		IF_PRINT_WARNING(GLOBAL_DEBUG) << "failed to load icon image for item: " << _id << endl;
		_InvalidateObject();
	}
}
GlobalWeapon::GlobalWeapon(uint32 id, uint32 count) :
	GlobalObject(id, count)
{
	// Initialize all elemental effects as neutral
	_elemental_effects.insert(pair<GLOBAL_ELEMENTAL, GLOBAL_INTENSITY>(GLOBAL_ELEMENTAL_FIRE, GLOBAL_INTENSITY_NEUTRAL));
	_elemental_effects.insert(pair<GLOBAL_ELEMENTAL, GLOBAL_INTENSITY>(GLOBAL_ELEMENTAL_WATER, GLOBAL_INTENSITY_NEUTRAL));
	_elemental_effects.insert(pair<GLOBAL_ELEMENTAL, GLOBAL_INTENSITY>(GLOBAL_ELEMENTAL_VOLT, GLOBAL_INTENSITY_NEUTRAL));
	_elemental_effects.insert(pair<GLOBAL_ELEMENTAL, GLOBAL_INTENSITY>(GLOBAL_ELEMENTAL_EARTH, GLOBAL_INTENSITY_NEUTRAL));
	_elemental_effects.insert(pair<GLOBAL_ELEMENTAL, GLOBAL_INTENSITY>(GLOBAL_ELEMENTAL_SLICING, GLOBAL_INTENSITY_NEUTRAL));
	_elemental_effects.insert(pair<GLOBAL_ELEMENTAL, GLOBAL_INTENSITY>(GLOBAL_ELEMENTAL_SMASHING, GLOBAL_INTENSITY_NEUTRAL));
	_elemental_effects.insert(pair<GLOBAL_ELEMENTAL, GLOBAL_INTENSITY>(GLOBAL_ELEMENTAL_MAULING, GLOBAL_INTENSITY_NEUTRAL));
	_elemental_effects.insert(pair<GLOBAL_ELEMENTAL, GLOBAL_INTENSITY>(GLOBAL_ELEMENTAL_PIERCING, GLOBAL_INTENSITY_NEUTRAL));

	if ((_id <= MAX_ITEM_ID) || (_id > MAX_WEAPON_ID)) {
		IF_PRINT_WARNING(GLOBAL_DEBUG) << "invalid id in constructor: " << _id << endl;
		_InvalidateObject();
		return;
	}

	ReadScriptDescriptor& script_file = GlobalManager->GetWeaponsScript();
	if (script_file.DoesTableExist(_id) == false) {
		IF_PRINT_WARNING(GLOBAL_DEBUG) << "no valid data for weapon in definition file: " << _id << endl;
		_InvalidateObject();
		return;
	}

	// Load the weapon data from the script
	script_file.OpenTable(_id);
	_LoadObjectData(script_file);

	_physical_attack = script_file.ReadUInt("physical_attack");
	_metaphysical_attack = script_file.ReadUInt("metaphysical_attack");
	_usable_by = script_file.ReadUInt("usable_by");

	script_file.CloseTable();
	if (script_file.IsErrorDetected()) {
		if (GLOBAL_DEBUG) {
			PRINT_WARNING << "one or more errors occurred while reading weapon data - they are listed below" << endl;
			cerr << script_file.GetErrorMessages() << endl;
		}
		_InvalidateObject();
	}
} // void GlobalWeapon::GlobalWeapon(uint32 id, uint32 count = 1)
GlobalItem::GlobalItem(uint32 id, uint32 count) :
	GlobalObject(id, count),
	_target_type(GLOBAL_TARGET_INVALID),
	_warmup_time(0),
	_cooldown_time(0)
{
	if ((_id == 0) || (_id > MAX_ITEM_ID)) {
		IF_PRINT_WARNING(GLOBAL_DEBUG) << "invalid id in constructor: " << _id << endl;
		_InvalidateObject();
		return;
	}

	ReadScriptDescriptor& script_file = GlobalManager->GetItemsScript();
	if (script_file.DoesTableExist(_id) == false) {
		IF_PRINT_WARNING(GLOBAL_DEBUG) << "no valid data for item in definition file: " << _id << endl;
		_InvalidateObject();
		return;
	}

	// Load the item data from the script
	script_file.OpenTable(_id);
	_LoadObjectData(script_file);

	_target_type = static_cast<GLOBAL_TARGET>(script_file.ReadInt("target_type"));
	_warmup_time = script_file.ReadUInt("warmup_time");
	_cooldown_time = script_file.ReadUInt("cooldown_time");

	_battle_use_function = script_file.ReadFunctionPointer("BattleUse");
	_field_use_function = script_file.ReadFunctionPointer("FieldUse");

	script_file.CloseTable();
	if (script_file.IsErrorDetected()) {
		if (GLOBAL_DEBUG) {
			PRINT_WARNING << "one or more errors occurred while reading item data - they are listed below" << endl;
			cerr << script_file.GetErrorMessages() << endl;
		}
		_InvalidateObject();
	}
} // void GlobalItem::GlobalItem(uint32 id, uint32 count = 1)
GlobalArmor::GlobalArmor(uint32 id, uint32 count) :
	GlobalObject(id, count)
{
	// Initialize all elemental effects as neutral
	_elemental_effects.insert(pair<GLOBAL_ELEMENTAL, GLOBAL_INTENSITY>(GLOBAL_ELEMENTAL_FIRE, GLOBAL_INTENSITY_NEUTRAL));
	_elemental_effects.insert(pair<GLOBAL_ELEMENTAL, GLOBAL_INTENSITY>(GLOBAL_ELEMENTAL_WATER, GLOBAL_INTENSITY_NEUTRAL));
	_elemental_effects.insert(pair<GLOBAL_ELEMENTAL, GLOBAL_INTENSITY>(GLOBAL_ELEMENTAL_VOLT, GLOBAL_INTENSITY_NEUTRAL));
	_elemental_effects.insert(pair<GLOBAL_ELEMENTAL, GLOBAL_INTENSITY>(GLOBAL_ELEMENTAL_EARTH, GLOBAL_INTENSITY_NEUTRAL));
	_elemental_effects.insert(pair<GLOBAL_ELEMENTAL, GLOBAL_INTENSITY>(GLOBAL_ELEMENTAL_SLICING, GLOBAL_INTENSITY_NEUTRAL));
	_elemental_effects.insert(pair<GLOBAL_ELEMENTAL, GLOBAL_INTENSITY>(GLOBAL_ELEMENTAL_SMASHING, GLOBAL_INTENSITY_NEUTRAL));
	_elemental_effects.insert(pair<GLOBAL_ELEMENTAL, GLOBAL_INTENSITY>(GLOBAL_ELEMENTAL_MAULING, GLOBAL_INTENSITY_NEUTRAL));
	_elemental_effects.insert(pair<GLOBAL_ELEMENTAL, GLOBAL_INTENSITY>(GLOBAL_ELEMENTAL_PIERCING, GLOBAL_INTENSITY_NEUTRAL));

	if ((_id <= MAX_WEAPON_ID) || (_id > MAX_LEG_ARMOR_ID)) {
		IF_PRINT_WARNING(GLOBAL_DEBUG) << "invalid id in constructor: " << _id << endl;
		_InvalidateObject();
		return;
	}

	// Figure out the appropriate script reference to grab based on the id value
	ReadScriptDescriptor* script_file;
	switch (GetObjectType()) {
		case GLOBAL_OBJECT_HEAD_ARMOR:
			script_file = &(GlobalManager->GetHeadArmorScript());
			break;
		case GLOBAL_OBJECT_TORSO_ARMOR:
			script_file = &(GlobalManager->GetTorsoArmorScript());
			break;
		case GLOBAL_OBJECT_ARM_ARMOR:
			script_file = &(GlobalManager->GetArmArmorScript());
			break;
		case GLOBAL_OBJECT_LEG_ARMOR:
			script_file = &(GlobalManager->GetLegArmorScript());
			break;
		default:
			IF_PRINT_WARNING(GLOBAL_DEBUG) << "could not determine armor type: " << _id << endl;
			_InvalidateObject();
			return;
	}

	if (script_file->DoesTableExist(_id) == false) {
		IF_PRINT_WARNING(GLOBAL_DEBUG) << "no valid data for armor in definition file: " << _id << endl;
		_InvalidateObject();
		return;
	}

	// Load the armor data from the script
	script_file->OpenTable(_id);
	_LoadObjectData(*script_file);

	_physical_defense = script_file->ReadUInt("physical_defense");
	_metaphysical_defense = script_file->ReadUInt("metaphysical_defense");
	_usable_by = script_file->ReadUInt("usable_by");

	script_file->CloseTable();
	if (script_file->IsErrorDetected()) {
		if (GLOBAL_DEBUG) {
			PRINT_WARNING << "one or more errors occurred while reading armor data - they are listed below" << endl;
			cerr << script_file->GetErrorMessages() << endl;
		}
		_InvalidateObject();
	}
} // void GlobalArmor::GlobalArmor(uint32 id, uint32 count = 1)
GlobalArmor::GlobalArmor(uint32 id, uint32 count) :
    GlobalObject(id, count)
{
    if((_id <= MAX_WEAPON_ID) || (_id > MAX_LEG_ARMOR_ID)) {
        IF_PRINT_WARNING(GLOBAL_DEBUG) << "invalid id in constructor: " << _id << std::endl;
        _InvalidateObject();
        return;
    }

    // Figure out the appropriate script reference to grab based on the id value
    ReadScriptDescriptor *script_file;
    switch(GetObjectType()) {
    case GLOBAL_OBJECT_HEAD_ARMOR:
        script_file = &(GlobalManager->GetHeadArmorScript());
        break;
    case GLOBAL_OBJECT_TORSO_ARMOR:
        script_file = &(GlobalManager->GetTorsoArmorScript());
        break;
    case GLOBAL_OBJECT_ARM_ARMOR:
        script_file = &(GlobalManager->GetArmArmorScript());
        break;
    case GLOBAL_OBJECT_LEG_ARMOR:
        script_file = &(GlobalManager->GetLegArmorScript());
        break;
    default:
        IF_PRINT_WARNING(GLOBAL_DEBUG) << "could not determine armor type: " << _id << std::endl;
        _InvalidateObject();
        return;
    }

    if(script_file->DoesTableExist(_id) == false) {
        IF_PRINT_WARNING(GLOBAL_DEBUG) << "no valid data for armor in definition file: " << _id << std::endl;
        _InvalidateObject();
        return;
    }

    // Load the armor data from the script
    script_file->OpenTable(_id);
    _LoadObjectData(*script_file);

    _LoadStatusEffects(*script_file);
    _LoadEquipmentSkills(*script_file);

    _physical_defense = script_file->ReadUInt("physical_defense");
    _magical_defense = script_file->ReadUInt("magical_defense");

    _usable_by = script_file->ReadUInt("usable_by");

    uint32 spirits_number = script_file->ReadUInt("slots");
    // Only permit a max of 5 spirits for equipment
    if (spirits_number > 5) {
        spirits_number = 5;
        PRINT_WARNING << "More than 5 spirit slots declared in item " << _id << std::endl;
    }
    _spirit_slots.resize(spirits_number, NULL);

    script_file->CloseTable();
    if(script_file->IsErrorDetected()) {
        if(GLOBAL_DEBUG) {
            PRINT_WARNING << "one or more errors occurred while reading armor data - they are listed below"
                          << std::endl << script_file->GetErrorMessages() << std::endl;
        }
        _InvalidateObject();
    }
} // void GlobalArmor::GlobalArmor(uint32 id, uint32 count = 1)