void EnemyGroupManager::parseEnemyFilesAndStore() {
	FileParser infile;

	if (!infile.open("enemies", true, false))
		return;

	Enemy_Level new_enemy;
	infile.new_section = true;
	while (infile.next()) {
		if (infile.new_section) {
			const string fname = infile.getFileName();
			const int firstpos = fname.rfind("/") + 1;
			const int len = fname.length() - firstpos - 4; //removes the ".txt" from the filename
			new_enemy.type = fname.substr(firstpos, len);
		}

		if (infile.key == "level") {
			new_enemy.level = atoi(infile.val.c_str());
		}
		else if (infile.key == "rarity") {
			new_enemy.rarity = infile.val.c_str();
		}
		else if (infile.key == "categories") {
			string cat;
			while ( (cat = infile.nextValue()) != "") {
				_categories[cat].push_back(new_enemy);
			}
		}
	}
	infile.close();
}
void EnemyGroupManager::parseEnemyFilesAndStore() {
	FileParser infile;

	// @CLASS enemies|Describing enemies files in enemies/
	if (!infile.open("enemies", true, false))
		return;

	Enemy_Level new_enemy;
	infile.new_section = true;
	bool first = true;
	while (infile.next()) {
		if (infile.new_section || first) {
			const string fname = infile.getFileName();
			const int firstpos = fname.rfind("/") + 1;
			const int len = fname.length() - firstpos - 4; //removes the ".txt" from the filename
			new_enemy.type = fname.substr(firstpos, len);
			first = false;
		}

		if (infile.key == "level") {
			// @ATTR level|integer|Level of the enemy
			new_enemy.level = toInt(infile.val);
		}
		else if (infile.key == "rarity") {
			// @ATTR rarity|[common,uncommon,rare]|Enemy rarity
			new_enemy.rarity = infile.val;
		}
		else if (infile.key == "categories") {
			// @ATTR categories|string,...|Comma separated list of enemy categories
			string cat;
			while ( (cat = infile.nextValue()) != "") {
				_categories[cat].push_back(new_enemy);
			}
		}
	}
	infile.close();
}
bool GameStateCutscene::load(std::string filename) {
	FileParser infile;

	// @CLASS Cutscene|Description of cutscenes in cutscenes/
	if (!infile.open(filename))
		return false;

	// parse the cutscene file
	while (infile.next()) {

		if (infile.new_section) {
			if (infile.section == "scene")
				scenes.push(Scene());
		}

		if (infile.section.empty()) {
			// allow having an empty section (globals such as scale_gfx might be set here
		}
		else if (infile.section == "scene") {
			SceneComponent sc = SceneComponent();

			if (infile.key == "caption") {
				// @ATTR scene.caption|string|A caption that will be shown.
				sc.type = infile.key;
				sc.s = msg->get(infile.val);
			}
			else if (infile.key == "image") {
				// @ATTR scene.image|string|An image that will be shown.
				sc.type = infile.key;
				sc.s = infile.val;
			}
			else if (infile.key == "pause") {
				// @ATTR scene.pause|duration|Pause before next component
				sc.type = infile.key;
				sc.x = parse_duration(infile.val);
			}
			else if (infile.key == "soundfx") {
				// @ATTR scene.soundfx|string|A sound that will be played
				sc.type = infile.key;
				sc.s = infile.val;
			}

			if (sc.type != "")
				scenes.back().components.push(sc);

		}
		else {
			fprintf(stderr, "unknown section %s in file %s\n", infile.section.c_str(), infile.getFileName().c_str());
		}

		if (infile.key == "scale_gfx") {
			// @ATTR scale_gfx|bool|The graphics will be scaled to fit screen width
			scale_graphics = toBool(infile.val);
		}
		else if (infile.key == "caption_margins") {
			// @ATTR caption_margins|[x,y]|Percentage-based margins for the caption text based on screen size
			caption_margins.x = toFloat(infile.nextValue())/100.0f;
			caption_margins.y = toFloat(infile.val)/100.0f;
		}
	}

	if (scenes.empty()) {
		fprintf(stderr, "No scenes defined in cutscene file %s\n", filename.c_str());
		return false;
	}

	return true;
}
void AnimationSet::load() {
	assert(!loaded);
	loaded = true;

	FileParser parser;
	if (!parser.open(name, true, true, "Error loading animation definition: " + name))
		return;

	string _name = "";
	int position = 0;
	int frames = 0;
	int duration = 0;
	Point render_size;
	Point render_offset;
	string type = "";
	string starting_animation = "";
	bool first_section=true;
	bool compressed_loading=false; // is reset every section to false, set by frame keyword
	Animation *newanim = NULL;
	vector<short> active_frames;

	// Parse the file and on each new section create an animation object from the data parsed previously
	parser.next();

	do {
		// create the animation if finished parsing a section
		if (parser.new_section) {
			if (!first_section && !compressed_loading) {
				Animation *a = new Animation(_name, type, sprite);
				a->setupUncompressed(render_size, render_offset, position, frames, duration);
				if (!active_frames.empty())
					a->setActiveFrames(active_frames);
				active_frames.clear();
				animations.push_back(a);
			}
			first_section = false;
			compressed_loading = false;
		}
		if (parser.key == "image") {
			if (sprite) {
				printf("multiple images specified in %s, dragons be here!\n", name.c_str());
				SDL_Quit();
				exit(128);
			}
			imagefile = parser.val;
			imag->increaseCount(imagefile);
			sprite = imag->getSurface(imagefile);
		}
		else if (parser.key == "position") {
			position = toInt(parser.val);
		}
		else if (parser.key == "frames") {
			frames = toInt(parser.val);
		}
		else if (parser.key == "duration") {
			duration = toInt(parser.val);

			// TEMP: if an animation is too fast, display one frame per fps anyway
			if (duration < 1) duration=1;
		}
		else if (parser.key == "type")
			type = parser.val;
		else if (parser.key == "render_size") {
			render_size.x = toInt(parser.nextValue());
			render_size.y = toInt(parser.nextValue());
		}
		else if (parser.key == "render_offset") {
			render_offset.x = toInt(parser.nextValue());
			render_offset.y = toInt(parser.nextValue());
		}
		else if (parser.key == "active_frame") {
			active_frames.clear();
			string nv = parser.nextValue();
			if (nv == "all") {
				active_frames.push_back(-1);
			}
			else {
				while (nv != "") {
					active_frames.push_back(toInt(nv));
					nv = parser.nextValue();
				}
				sort(active_frames.begin(), active_frames.end());
				active_frames.erase(unique(active_frames.begin(), active_frames.end()), active_frames.end());
			}
		}
		else if (parser.key == "frame") {
			if (compressed_loading == false) { // first frame statement in section
				newanim = new Animation(_name, type, sprite);
				newanim->setup(frames, duration);
				if (!active_frames.empty())
					newanim->setActiveFrames(active_frames);
				active_frames.clear();
				animations.push_back(newanim);
				compressed_loading = true;
			}
			// frame = index, direction, x, y, w, h, offsetx, offsety
			SDL_Rect r;
			Point offset;
			const int index = toInt(parser.nextValue());
			const int direction = toInt(parser.nextValue());
			r.x = toInt(parser.nextValue());
			r.y = toInt(parser.nextValue());
			r.w = toInt(parser.nextValue());
			r.h = toInt(parser.nextValue());
			offset.x = toInt(parser.nextValue());
			offset.y = toInt(parser.nextValue());
			newanim->addFrame(index, direction, r, offset);
		}
		else {
			fprintf(stderr, "animations definitions (%s): Key %s not supported!\n", parser.getFileName().c_str(), parser.key.c_str());
		}

		if (_name == "") {
			// This is the first animation
			starting_animation = parser.section;
		}
		_name = parser.section;
	}
	while (parser.next());

	if (!compressed_loading) {
		// add final animation
		Animation *a = new Animation(_name, type, sprite);
		a->setupUncompressed(render_size, render_offset, position, frames, duration);
		if (!active_frames.empty())
			a->setActiveFrames(active_frames);
		active_frames.clear();
		animations.push_back(a);
	}

	if (starting_animation != "") {
		Animation *a = getAnimation(starting_animation);
		delete defaultAnimation;
		defaultAnimation = a;
	}
}
void GameStatePlay::loadTitles() {
	FileParser infile;
	if (infile.open("engine/titles.txt")) {
		while (infile.next()) {
			if (infile.new_section && infile.section == "title") {
				Title t;
				titles.push_back(t);
			}

			if (titles.empty()) continue;

			if (infile.key == "title") titles.back().title = infile.val;
			else if (infile.key == "level") titles.back().level = toInt(infile.val);
			else if (infile.key == "power") titles.back().power = toInt(infile.val);
			else if (infile.key == "requires_status") titles.back().requires_status = infile.val;
			else if (infile.key == "requires_not_status") titles.back().requires_not = infile.val;
			else if (infile.key == "primary_stat") titles.back().primary_stat = infile.val;
			else fprintf(stderr, "GameStatePlay: Unknown key value in title definitons: %s in file %s in section %s\n", infile.key.c_str(), infile.getFileName().c_str(), infile.section.c_str());
		}
		infile.close();
	}
}
void PowerManager::loadPowers() {
	FileParser infile;

	// @CLASS Power|Description about powers...
	if (!infile.open("powers/powers.txt", true, false))
		return;

	int input_id = 0;
	bool skippingEntry = false;

	while (infile.next()) {
		// id needs to be the first component of each power.  That is how we write
		// data to the correct power.
		if (infile.key == "id") {
			// @ATTR id|integer|Uniq identifier for the power definition.
			input_id = toInt(infile.val);
			skippingEntry = input_id < 1;
			if (skippingEntry)
				fprintf(stderr, "Power index out of bounds 1-%d, skipping\n", INT_MAX);
			if (static_cast<int>(powers.size()) < input_id + 1)
				powers.resize(input_id + 1);
			continue;
		}
		if (skippingEntry)
			continue;

		if (infile.key == "type") {
			// @ATTR type|[fixed:missile:repeater:spawn:transform]|Defines the type of power definiton
			if (infile.val == "fixed") powers[input_id].type = POWTYPE_FIXED;
			else if (infile.val == "missile") powers[input_id].type = POWTYPE_MISSILE;
			else if (infile.val == "repeater") powers[input_id].type = POWTYPE_REPEATER;
			else if (infile.val == "spawn") powers[input_id].type = POWTYPE_SPAWN;
			else if (infile.val == "transform") powers[input_id].type = POWTYPE_TRANSFORM;
			else fprintf(stderr, "unknown type %s\n", infile.val.c_str());
		}
		else if (infile.key == "name")
			// @ATTR name|string|The name of the power
			powers[input_id].name = msg->get(infile.val);
		else if (infile.key == "description")
			// @ATTR description|string|Description of the power
			powers[input_id].description = msg->get(infile.val);
		else if (infile.key == "icon")
			// @ATTR icon|string|The icon to visually represent the power eg. in skill tree or action bar.
			powers[input_id].icon = toInt(infile.val);
		else if (infile.key == "new_state") {
			// @ATTR new_state|string|When power is used, hero or enemy will change to this state. Must be one of the states [block, instant, user defined]
			if (infile.val == "block") powers[input_id].new_state = POWSTATE_BLOCK;
			else if (infile.val == "instant") powers[input_id].new_state = POWSTATE_INSTANT;
			else {
				powers[input_id].new_state = POWSTATE_ATTACK;
				powers[input_id].attack_anim = infile.val;
			}
		}
		else if (infile.key == "face")
			// @ATTR face|bool|Power will make hero or enemy to face the target location.
			powers[input_id].face = toBool(infile.val);
		else if (infile.key == "source_type") {
			// @ATTR source_type|[hero:neutral:enemy]|
			if (infile.val == "hero") powers[input_id].source_type = SOURCE_TYPE_HERO;
			else if (infile.val == "neutral") powers[input_id].source_type = SOURCE_TYPE_NEUTRAL;
			else if (infile.val == "enemy") powers[input_id].source_type = SOURCE_TYPE_ENEMY;
			else fprintf(stderr, "unknown source_type %s\n", infile.val.c_str());
		}
		else if (infile.key == "beacon")
			// @ATTR beacon|bool|True if enemy is calling its allies.
			powers[input_id].beacon = toBool(infile.val);
		else if (infile.key == "count")
			// @ATTR count|integer|The count of hazards/effect or spawns to be created by this power.
			powers[input_id].count = toInt(infile.val);
		else if (infile.key == "passive")
			// @ATTR passive|bool|If power is unlocked when the hero or enemy spawns it will be automatically activated.
			powers[input_id].passive = toBool(infile.val);
		else if (infile.key == "passive_trigger") {
			// @ATTR passive_trigger|[on_block:on_hit:on_halfdeath:on_joincombat:on_death]|This will only activate a passive power under a certain condition.
			if (infile.val == "on_block") powers[input_id].passive_trigger = TRIGGER_BLOCK;
			else if (infile.val == "on_hit") powers[input_id].passive_trigger = TRIGGER_HIT;
			else if (infile.val == "on_halfdeath") powers[input_id].passive_trigger = TRIGGER_HALFDEATH;
			else if (infile.val == "on_joincombat") powers[input_id].passive_trigger = TRIGGER_JOINCOMBAT;
			else if (infile.val == "on_death") powers[input_id].passive_trigger = TRIGGER_DEATH;
			else fprintf(stderr, "unknown passive trigger %s\n", infile.val.c_str());
		}
		// power requirements
		else if (infile.key == "requires_flags") {
			std::string flag = popFirstString(infile.val);

			while (flag != "") {
				powers[input_id].requires_flags.insert(flag);
				flag = popFirstString(infile.val);
			}
		}
		else if (infile.key == "requires_mp")
			// @ATTR requires_mp|integer|Restrict power usage to a specified MP level.
			powers[input_id].requires_mp = toInt(infile.val);
		else if (infile.key == "requires_hp")
			// @ATTR requires_hp|integer|Restrict power usage to a specified HP level.
			powers[input_id].requires_hp = toInt(infile.val);
		else if (infile.key == "sacrifice")
			// @ATTR sacrifice|bool|
			powers[input_id].sacrifice = toBool(infile.val);
		else if (infile.key == "requires_los")
			// @ATTR requires_los|bool|Requires a line-of-sight to target.
			powers[input_id].requires_los = toBool(infile.val);
		else if (infile.key == "requires_empty_target")
			// @ATTR requires_empty_target|bool|The power can only be cast when target tile is empty.
			powers[input_id].requires_empty_target = toBool(infile.val);
		else if (infile.key == "requires_item")
			// @ATTR requires_item|item_id|Requires a specific item in inventory.
			powers[input_id].requires_item = toInt(infile.val);
		else if (infile.key == "requires_equipped_item")
			// @ATTR requires_equipped_item|item_id|Requires a specific item to be equipped on hero.
			powers[input_id].requires_equipped_item = toInt(infile.val);
		else if (infile.key == "requires_targeting")
			// @ATTR requires_targeting|bool|Power is only used when targeting using click-to-target.
			powers[input_id].requires_targeting = toBool(infile.val);
		else if (infile.key == "cooldown")
			// @ATTR cooldown|duration|Specify the duration for cooldown of the power.
			powers[input_id].cooldown = parse_duration(infile.val);
		// animation info
		else if (infile.key == "animation")
			// @ATTR animation|string|The name of power animation.
			powers[input_id].animation_name = infile.val;
		else if (infile.key == "soundfx")
			// @ATTR soundfx|string|Sound effect to play when use of power.
			powers[input_id].sfx_index = loadSFX(infile.val);
		else if (infile.key == "directional")
			// @ATTR directional|bool|The animation sprite sheet contains 8 directions, one per row.
			powers[input_id].directional = toBool(infile.val);
		else if (infile.key == "visual_random")
			// @ATTR visual_random|integer|The animation sprite sheet contains rows of random options
			powers[input_id].visual_random = toInt(infile.val);
		else if (infile.key == "visual_option")
			// @ATTR visual_option|integer|The animation sprite sheet containers rows of similar effects, use a specific option.
			powers[input_id].visual_option = toInt(infile.val);
		else if (infile.key == "aim_assist")
			// @ATTR aim_assist|bool|Power is aim assisted.
			powers[input_id].aim_assist = toBool(infile.val);
		else if (infile.key == "speed")
			// @ATTR speed|integer|The speed of missile hazard, the unit is defined as map units per frame.
			powers[input_id].speed = toFloat(infile.val) / MAX_FRAMES_PER_SEC;
		else if (infile.key == "lifespan")
			// @ATTR lifespan|duration|How long the hazard/animation lasts.
			powers[input_id].lifespan = parse_duration(infile.val);
		else if (infile.key == "floor")
			// @ATTR floor|bool|The hazard is drawn between the background and the object layer.
			powers[input_id].floor = toBool(infile.val);
		else if (infile.key == "complete_animation")
			// @ATTR complete_animation|bool|
			powers[input_id].complete_animation = toBool(infile.val);
		// hazard traits
		else if (infile.key == "use_hazard")
			// @ATTR use_hazard|bool|Power uses hazard.
			powers[input_id].use_hazard = toBool(infile.val);
		else if (infile.key == "no_attack")
			// @ATTR no_attack|bool|
			powers[input_id].no_attack = toBool(infile.val);
		else if (infile.key == "radius")
			// @ATTR radius|integer|Radius in pixels
			powers[input_id].radius = toFloat(infile.val);
		else if (infile.key == "base_damage") {
			// @ATTR base_damage|[melee:ranged:ment]|
			if (infile.val == "none")        powers[input_id].base_damage = BASE_DAMAGE_NONE;
			else if (infile.val == "melee")  powers[input_id].base_damage = BASE_DAMAGE_MELEE;
			else if (infile.val == "ranged") powers[input_id].base_damage = BASE_DAMAGE_RANGED;
			else if (infile.val == "ment")   powers[input_id].base_damage = BASE_DAMAGE_MENT;
			else fprintf(stderr, "unknown base_damage %s\n", infile.val.c_str());
		}
		else if (infile.key == "starting_pos") {
			// @ATTR starting_pos|[source, target, melee]|Start position for hazard
			if (infile.val == "source")      powers[input_id].starting_pos = STARTING_POS_SOURCE;
			else if (infile.val == "target") powers[input_id].starting_pos = STARTING_POS_TARGET;
			else if (infile.val == "melee")  powers[input_id].starting_pos = STARTING_POS_MELEE;
			else fprintf(stderr, "unknown starting_pos %s\n", infile.val.c_str());
		}
		else if (infile.key == "multitarget")
			// @ATTR multitarget|bool|
			powers[input_id].multitarget = toBool(infile.val);
		else if (infile.key == "trait_armor_penetration")
			// @ATTR trait_armor_penetration|bool|
			powers[input_id].trait_armor_penetration = toBool(infile.val);
		else if (infile.key == "trait_avoidance_ignore")
			// @ATTR trait_avoidance_ignore|bool|
			powers[input_id].trait_avoidance_ignore = toBool(infile.val);
		else if (infile.key == "trait_crits_impaired")
			// @ATTR trait_crits_impaired|bool|
			powers[input_id].trait_crits_impaired = toInt(infile.val);
		else if (infile.key == "trait_elemental") {
			// @ATTR, trait_elemental|string|
			for (unsigned int i=0; i<ELEMENTS.size(); i++) {
				if (infile.val == ELEMENTS[i].name) powers[input_id].trait_elemental = i;
			}
		}
		else if (infile.key == "target_range")
			// @ATTR, target_range|float||The distance from the caster that the power can be activated
			powers[input_id].target_range = toFloat(infile.nextValue());
		//steal effects
		else if (infile.key == "hp_steal")
			// @ATTR, hp_steal|integer|Percentage of damage to steal into HP
			powers[input_id].hp_steal = toInt(infile.val);
		else if (infile.key == "mp_steal")
			// @ATTR, mp_steal|integer|Percentage of damage to steal into MP
			powers[input_id].mp_steal = toInt(infile.val);
		//missile modifiers
		else if (infile.key == "missile_angle")
			// @ATTR missile_angle|integer|Angle of missile
			powers[input_id].missile_angle = toInt(infile.val);
		else if (infile.key == "angle_variance")
			// @ATTR angle_variance|integer|Percentage of variance added to missile angle
			powers[input_id].angle_variance = toInt(infile.val);
		else if (infile.key == "speed_variance")
			// @ATTR speed_variance|integer|Percentage of variance added to missile speed
			powers[input_id].speed_variance = toFloat(infile.val);
		//repeater modifiers
		else if (infile.key == "delay")
			// @ATTR delay|duration|Delay between repeats
			powers[input_id].delay = parse_duration(infile.val);
		// buff/debuff durations
		else if (infile.key == "transform_duration")
			// @ATTR transform_duration|duration|Duration for transform
			powers[input_id].transform_duration = toInt(infile.val);
		else if (infile.key == "manual_untransform")
			// @ATTR transform_duration|bool|Force manual untranform
			powers[input_id].manual_untransform = toBool(infile.val);
		else if (infile.key == "keep_equipment")
			// @ATTR keep_equipment|bool|Keep  equipment while transformed
			powers[input_id].keep_equipment = toBool(infile.val);
		// buffs
		else if (infile.key == "buff")
			// @ATTR buff|bool|
			powers[input_id].buff= toBool(infile.val);
		else if (infile.key == "buff_teleport")
			// @ATTR buff_teleport|bool|
			powers[input_id].buff_teleport = toBool(infile.val);
		else if (infile.key == "buff_party")
			// @ATTR buff_part|bool|
			powers[input_id].buff_party = toBool(infile.val);
		else if (infile.key == "buff_party_power_id")
			// @ATTR buff_part_power_id|bool|
			powers[input_id].buff_party_power_id = toInt(infile.val);
		else if (infile.key == "post_effect") {
			// @ATTR post_effect|[effect_id, magnitude (integer), duration (integer)]|Post effect.
			PostEffect pe;
			pe.id = popFirstString(infile.val);
			pe.magnitude = popFirstInt(infile.val);
			pe.duration = popFirstInt(infile.val);
			powers[input_id].post_effects.push_back(pe);
		}
		// pre and post power effects
		else if (infile.key == "post_power")
			// @ATTR post_power|power_id|Post power
			powers[input_id].post_power = toInt(infile.val);
		else if (infile.key == "wall_power")
			// @ATTR wall_power|power_id|Wall power
			powers[input_id].wall_power = toInt(infile.val);
		else if (infile.key == "allow_power_mod")
			// @ATTR allow_power_mod|bool|Allow power modifiers
			powers[input_id].allow_power_mod = toBool(infile.val);
		// spawn info
		else if (infile.key == "spawn_type")
			// @ATTR spawn_type|string|Type of spawn.
			powers[input_id].spawn_type = infile.val;
		else if (infile.key == "target_neighbor")
			// @ATTR target_neighbor|int|Neigbor target.
			powers[input_id].target_neighbor = toInt(infile.val);
		else if (infile.key == "spawn_limit") {
			// @ATTR spawn_limit|[fixed:stat:unlimited],stat[physical:mental:offense:defens]|
			std::string mode = popFirstString(infile.val);
			if (mode == "fixed") powers[input_id].spawn_limit_mode = SPAWN_LIMIT_MODE_FIXED;
			else if (mode == "stat") powers[input_id].spawn_limit_mode = SPAWN_LIMIT_MODE_STAT;
			else if (mode == "unlimited") powers[input_id].spawn_limit_mode = SPAWN_LIMIT_MODE_UNLIMITED;
			else fprintf(stderr, "unknown spawn_limit_mode %s\n", mode.c_str());

			if(powers[input_id].spawn_limit_mode != SPAWN_LIMIT_MODE_UNLIMITED) {
				powers[input_id].spawn_limit_qty = popFirstInt(infile.val);

				if(powers[input_id].spawn_limit_mode == SPAWN_LIMIT_MODE_STAT) {
					powers[input_id].spawn_limit_every = popFirstInt(infile.val);

					std::string stat = popFirstString(infile.val);
					if (stat == "physical") powers[input_id].spawn_limit_stat = SPAWN_LIMIT_STAT_PHYSICAL;
					else if (stat == "mental") powers[input_id].spawn_limit_stat = SPAWN_LIMIT_STAT_MENTAL;
					else if (stat == "offense") powers[input_id].spawn_limit_stat = SPAWN_LIMIT_STAT_OFFENSE;
					else if (stat == "defense") powers[input_id].spawn_limit_stat = SPAWN_LIMIT_STAT_DEFENSE;
					else fprintf(stderr, "unknown spawn_limit_stat %s\n", stat.c_str());
				}
			}
		}
		else if (infile.key == "spawn_level") {
			std::string mode = popFirstString(infile.val);
			if (mode == "default") powers[input_id].spawn_level_mode = SPAWN_LEVEL_MODE_DEFAULT;
			else if (mode == "fixed") powers[input_id].spawn_level_mode = SPAWN_LEVEL_MODE_FIXED;
			else if (mode == "stat") powers[input_id].spawn_level_mode = SPAWN_LEVEL_MODE_STAT;
			else if (mode == "level") powers[input_id].spawn_level_mode = SPAWN_LEVEL_MODE_LEVEL;
			else fprintf(stderr, "unknown spawn_level_mode %s\n", mode.c_str());

			if(powers[input_id].spawn_level_mode != SPAWN_LEVEL_MODE_DEFAULT) {
				powers[input_id].spawn_level_qty = popFirstInt(infile.val);

				if(powers[input_id].spawn_level_mode != SPAWN_LEVEL_MODE_FIXED) {
					powers[input_id].spawn_level_every = popFirstInt(infile.val);

					if(powers[input_id].spawn_level_mode == SPAWN_LEVEL_MODE_STAT) {
						std::string stat = popFirstString(infile.val);
						if (stat == "physical") powers[input_id].spawn_level_stat = SPAWN_LEVEL_STAT_PHYSICAL;
						else if (stat == "mental") powers[input_id].spawn_level_stat = SPAWN_LEVEL_STAT_MENTAL;
						else if (stat == "offense") powers[input_id].spawn_level_stat = SPAWN_LEVEL_STAT_OFFENSE;
						else if (stat == "defense") powers[input_id].spawn_level_stat = SPAWN_LEVEL_STAT_DEFENSE;
						else fprintf(stderr, "unknown spawn_level_stat %s\n", stat.c_str());
					}
				}
			}
		}
		else if (infile.key == "target_party")
			// @ATTR target_party|bool|
			powers[input_id].target_party = toBool(infile.val);
		else if (infile.key == "target_categories") {
			// @ATTR target_categories|string,...|
			string cat;
			while ((cat = infile.nextValue()) != "") {
				powers[input_id].target_categories.push_back(cat);
			}
		}
		else if (infile.key == "modifier_accuracy") {
			// @ATTR modifier_accuracy|[multiply:add:absolute], integer|
			std::string mode = popFirstString(infile.val);
			if(mode == "multiply") powers[input_id].mod_accuracy_mode = STAT_MODIFIER_MODE_MULTIPLY;
			else if(mode == "add") powers[input_id].mod_accuracy_mode = STAT_MODIFIER_MODE_ADD;
			else if(mode == "absolute") powers[input_id].mod_accuracy_mode = STAT_MODIFIER_MODE_ABSOLUTE;
			else fprintf(stderr, "unknown stat_modifier_mode %s\n", mode.c_str());

			powers[input_id].mod_accuracy_value = popFirstInt(infile.val);
		}
		else if (infile.key == "modifier_damage") {
			// @ATTR modifier_damage|[multiply:add:absolute], integer|
			std::string mode = popFirstString(infile.val);
			if(mode == "multiply") powers[input_id].mod_damage_mode = STAT_MODIFIER_MODE_MULTIPLY;
			else if(mode == "add") powers[input_id].mod_damage_mode = STAT_MODIFIER_MODE_ADD;
			else if(mode == "absolute") powers[input_id].mod_damage_mode = STAT_MODIFIER_MODE_ABSOLUTE;
			else fprintf(stderr, "unknown stat_modifier_mode %s\n", mode.c_str());

			powers[input_id].mod_damage_value_min = popFirstInt(infile.val);
			powers[input_id].mod_damage_value_max = popFirstInt(infile.val);
		}
		else if (infile.key == "modifier_critical") {
			// @ATTR modifier_critical|[multiply:add:absolute], integer|
			std::string mode = popFirstString(infile.val);
			if(mode == "multiply") powers[input_id].mod_crit_mode = STAT_MODIFIER_MODE_MULTIPLY;
			else if(mode == "add") powers[input_id].mod_crit_mode = STAT_MODIFIER_MODE_ADD;
			else if(mode == "absolute") powers[input_id].mod_crit_mode = STAT_MODIFIER_MODE_ABSOLUTE;
			else fprintf(stderr, "unknown stat_modifier_mode %s\n", mode.c_str());

			powers[input_id].mod_crit_value = popFirstInt(infile.val);
		}
		else
			fprintf(stderr, "ignoring unknown key %s set to %s in file %s\n",
					infile.key.c_str(), infile.val.c_str(), infile.getFileName().c_str());
	}
	infile.close();
}
void EventManager::loadEventComponent(FileParser &infile, Event* evnt, Event_Component* ec) {
	Event_Component *e = NULL;
	if (evnt) {
		evnt->components.push_back(Event_Component());
		e = &evnt->components.back();
	}
	else if (ec) {
		e = ec;
	}

	if (!e) return;

	e->type = infile.key;

	if (infile.key == "tooltip") {
		// @ATTR event.tooltip|string|Tooltip for event
		e->s = msg->get(infile.val);
	}
	else if (infile.key == "power_path") {
		// @ATTR event.power_path|[hero:[x,y]]|Event power path
		// x,y are src, if s=="hero" we target the hero,
		// else we'll use values in a,b as coordinates
		e->x = toInt(infile.nextValue());
		e->y = toInt(infile.nextValue());

		std::string dest = infile.nextValue();
		if (dest == "hero") {
			e->s = "hero";
		}
		else {
			e->a = toInt(dest);
			e->b = toInt(infile.nextValue());
		}
	}
	else if (infile.key == "power_damage") {
		// @ATTR event.power_damage|min(integer), max(integer)|Range of power damage
		e->a = toInt(infile.nextValue());
		e->b = toInt(infile.nextValue());
	}
	else if (infile.key == "intermap") {
		// @ATTR event.intermap|[map(string),x(integer),y(integer)]|Jump to specific map at location specified.
		e->s = infile.nextValue();
		e->x = toInt(infile.nextValue());
		e->y = toInt(infile.nextValue());
	}
	else if (infile.key == "intramap") {
		// @ATTR event.intramap|[x(integer),y(integer)]|Jump to specific position within current map.
		e->x = toInt(infile.nextValue());
		e->y = toInt(infile.nextValue());
	}
	else if (infile.key == "mapmod") {
		// @ATTR event.mapmod|[string,int,int,int],..|Modify map tiles
		e->s = infile.nextValue();
		e->x = toInt(infile.nextValue());
		e->y = toInt(infile.nextValue());
		e->z = toInt(infile.nextValue());

		// add repeating mapmods
		if (evnt) {
			std::string repeat_val = infile.nextValue();
			while (repeat_val != "") {
				evnt->components.push_back(Event_Component());
				e = &evnt->components.back();
				e->type = infile.key;
				e->s = repeat_val;
				e->x = toInt(infile.nextValue());
				e->y = toInt(infile.nextValue());
				e->z = toInt(infile.nextValue());

				repeat_val = infile.nextValue();
			}
		}
	}
	else if (infile.key == "soundfx") {
		// @ATTR event.soundfx|[soundfile(string),x(integer),y(integer)]|Play a sound at optional location
		e->s = infile.nextValue();
		e->x = e->y = -1;

		std::string s = infile.nextValue();
		if (s != "") e->x = toInt(s);

		s = infile.nextValue();
		if (s != "") e->y = toInt(s);

	}
	else if (infile.key == "loot") {
		// @ATTR event.loot|[string,x(integer),y(integer),drop_chance([fixed:chance(integer)]),quantity_min(integer),quantity_max(integer)],...|Add loot to the event
		e->s = infile.nextValue();
		e->x = toInt(infile.nextValue());
		e->y = toInt(infile.nextValue());

		// drop chance
		std::string chance = infile.nextValue();
		if (chance == "fixed") e->z = 0;
		else e->z = toInt(chance);

		// quantity min/max
		e->a = toInt(infile.nextValue());
		if (e->a < 1) e->a = 1;
		e->b = toInt(infile.nextValue());
		if (e->b < e->a) e->b = e->a;

		// add repeating loot
		if (evnt) {
			std::string repeat_val = infile.nextValue();
			while (repeat_val != "") {
				evnt->components.push_back(Event_Component());
				e = &evnt->components.back();
				e->type = infile.key;
				e->s = repeat_val;
				e->x = toInt(infile.nextValue());
				e->y = toInt(infile.nextValue());

				chance = infile.nextValue();
				if (chance == "fixed") e->z = 0;
				else e->z = toInt(chance);

				e->a = toInt(infile.nextValue());
				if (e->a < 1) e->a = 1;
				e->b = toInt(infile.nextValue());
				if (e->b < e->a) e->b = e->a;

				repeat_val = infile.nextValue();
			}
		}
	}
	else if (infile.key == "msg") {
		// @ATTR event.msg|string|Adds a message to be displayed for the event.
		e->s = msg->get(infile.val);
	}
	else if (infile.key == "shakycam") {
		// @ATTR event.shakycam|integer|
		e->x = toInt(infile.val);
	}
	else if (infile.key == "requires_status") {
		// @ATTR event.requires_status|string,...|Event requires list of statuses
		e->s = infile.nextValue();

		// add repeating requires_status
		if (evnt) {
			std::string repeat_val = infile.nextValue();
			while (repeat_val != "") {
				evnt->components.push_back(Event_Component());
				e = &evnt->components.back();
				e->type = infile.key;
				e->s = repeat_val;

				repeat_val = infile.nextValue();
			}
		}
	}
	else if (infile.key == "requires_not_status") {
		// @ATTR event.requires_not|string,...|Event requires not list of statuses
		e->s = infile.nextValue();

		// add repeating requires_not
		if (evnt) {
			std::string repeat_val = infile.nextValue();
			while (repeat_val != "") {
				evnt->components.push_back(Event_Component());
				e = &evnt->components.back();
				e->type = infile.key;
				e->s = repeat_val;

				repeat_val = infile.nextValue();
			}
		}
	}
	else if (infile.key == "requires_level") {
		// @ATTR event.requires_level|integer|Event requires hero level
		e->x = toInt(infile.nextValue());
	}
	else if (infile.key == "requires_not_level") {
		// @ATTR event.requires_not_level|integer|Event requires not hero level
		e->x = toInt(infile.nextValue());
	}
	else if (infile.key == "requires_currency") {
		// @ATTR event.requires_currency|integer|Event requires atleast this much currency
		e->x = toInt(infile.nextValue());
	}
	else if (infile.key == "requires_item") {
		// @ATTR event.requires_item|integer,...|Event requires specific item
		e->x = toInt(infile.nextValue());

		// add repeating requires_item
		if (evnt) {
			std::string repeat_val = infile.nextValue();
			while (repeat_val != "") {
				evnt->components.push_back(Event_Component());
				e = &evnt->components.back();
				e->type = infile.key;
				e->x = toInt(repeat_val);

				repeat_val = infile.nextValue();
			}
		}
	}
	else if (infile.key == "set_status") {
		// @ATTR event.set_status|string,...|Sets specified statuses
		e->s = infile.nextValue();

		// add repeating set_status
		if (evnt) {
			std::string repeat_val = infile.nextValue();
			while (repeat_val != "") {
				evnt->components.push_back(Event_Component());
				e = &evnt->components.back();
				e->type = infile.key;
				e->s = repeat_val;

				repeat_val = infile.nextValue();
			}
		}
	}
	else if (infile.key == "unset_status") {
		// @ATTR event.unset_status|string,...|Unsets specified statuses
		e->s = infile.nextValue();

		// add repeating unset_status
		if (evnt) {
			std::string repeat_val = infile.nextValue();
			while (repeat_val != "") {
				evnt->components.push_back(Event_Component());
				e = &evnt->components.back();
				e->type = infile.key;
				e->s = repeat_val;

				repeat_val = infile.nextValue();
			}
		}
	}
	else if (infile.key == "remove_currency") {
		// @ATTR event.remove_currency|integer|Removes specified amount of currency from hero inventory
		e->x = toInt(infile.val);
		clampFloor(e->x, 0);
	}
	else if (infile.key == "remove_item") {
		// @ATTR event.remove_item|integer,...|Removes specified item from hero inventory
		e->x = toInt(infile.nextValue());

		// add repeating remove_item
		if (evnt) {
			std::string repeat_val = infile.nextValue();
			while (repeat_val != "") {
				evnt->components.push_back(Event_Component());
				e = &evnt->components.back();
				e->type = infile.key;
				e->x = toInt(repeat_val);

				repeat_val = infile.nextValue();
			}
		}
	}
	else if (infile.key == "reward_xp") {
		// @ATTR event.reward_xp|integer|Reward hero with specified amount of experience points.
		e->x = toInt(infile.val);
		clampFloor(e->x, 0);
	}
	else if (infile.key == "reward_currency") {
		// @ATTR event.reward_currency|integer|Reward hero with specified amount of currency.
		e->x = toInt(infile.val);
		clampFloor(e->x, 0);
	}
	else if (infile.key == "reward_item") {
		// @ATTR event.reward_item|x(integer),y(integer)|Reward hero with y number of item x.
		e->x = toInt(infile.nextValue());
		e->y = toInt(infile.val);
		clampFloor(e->y, 0);
	}
	else if (infile.key == "restore") {
		// @ATTR event.restore|string|Restore the hero's HP, MP, and/or status.
		e->s = infile.val;
	}
	else if (infile.key == "power") {
		// @ATTR event.power|power_id|Specify power coupled with event.
		e->x = toInt(infile.val);
	}
	else if (infile.key == "spawn") {
		// @ATTR event.spawn|[string,x(integer),y(integer)], ...|Spawn specified enemies at location
		e->s = infile.nextValue();
		e->x = toInt(infile.nextValue());
		e->y = toInt(infile.nextValue());

		// add repeating spawn
		if (evnt) {
			std::string repeat_val = infile.nextValue();
			while (repeat_val != "") {
				evnt->components.push_back(Event_Component());
				e = &evnt->components.back();
				e->type = infile.key;

				e->s = repeat_val;
				e->x = toInt(infile.nextValue());
				e->y = toInt(infile.nextValue());

				repeat_val = infile.nextValue();
			}
		}
	}
	else if (infile.key == "stash") {
		// @ATTR event.stash|string|
		e->s = infile.val;
	}
	else if (infile.key == "npc") {
		// @ATTR event.npc|string|
		e->s = infile.val;
	}
	else if (infile.key == "music") {
		// @ATTR event.music|string|Change background music to specified file.
		e->s = infile.val;
	}
	else if (infile.key == "cutscene") {
		// @ATTR event.cutscene|string|Show specified cutscene.
		e->s = infile.val;
	}
	else if (infile.key == "repeat") {
		// @ATTR event.repeat|string|
		e->s = infile.val;
	}
	else {
		fprintf(stderr, "EventManager: Unknown key value: %s in file %s in section %s\n", infile.key.c_str(), infile.getFileName().c_str(), infile.section.c_str());
	}
}
void EventManager::loadEvent(FileParser &infile, Event* evnt) {
	if (!evnt) return;

	if (infile.key == "type") {
		// @ATTR event.type|[on_trigger:on_mapexit:on_leave:on_load:on_clear]|Type of map event.
		std::string type = infile.val;
		evnt->type = type;

		if      (type == "on_trigger");
		else if (type == "on_mapexit"); // no need to set keep_after_trigger to false correctly, it's ignored anyway
		else if (type == "on_leave");
		else if (type == "on_load") {
			evnt->keep_after_trigger = false;
		}
		else if (type == "on_clear") {
			evnt->keep_after_trigger = false;
		}
		else {
			fprintf(stderr, "EventManager: Loading event in file %s\nEvent type %s unknown, change to \"on_trigger\" to suppress this warning.\n", infile.getFileName().c_str(), type.c_str());
		}
	}
	else if (infile.key == "location") {
		// @ATTR event.location|[x,y,w,h]|Defines the location area for the event.
		evnt->location.x = toInt(infile.nextValue());
		evnt->location.y = toInt(infile.nextValue());
		evnt->location.w = toInt(infile.nextValue());
		evnt->location.h = toInt(infile.nextValue());

		evnt->center.x = evnt->location.x + (float)evnt->location.w/2;
		evnt->center.y = evnt->location.y + (float)evnt->location.h/2;
	}
	else if (infile.key == "hotspot") {
		//  @ATTR event.hotspot|[ [x, y, w, h] : location ]|Event uses location as hotspot or defined by rect.
		if (infile.val == "location") {
			evnt->hotspot.x = evnt->location.x;
			evnt->hotspot.y = evnt->location.y;
			evnt->hotspot.w = evnt->location.w;
			evnt->hotspot.h = evnt->location.h;
		}
		else {
			evnt->hotspot.x = toInt(infile.nextValue());
			evnt->hotspot.y = toInt(infile.nextValue());
			evnt->hotspot.w = toInt(infile.nextValue());
			evnt->hotspot.h = toInt(infile.nextValue());
		}
	}
	else if (infile.key == "cooldown") {
		// @ATTR event.cooldown|duration|Duration for event cooldown.
		evnt->cooldown = parse_duration(infile.val);
	}
	else if (infile.key == "reachable_from") {
		// @ATTR event.reachable_from|[x,y,w,h]|If the hero is inside this rectangle, they can activate the event.
		evnt->reachable_from.x = toInt(infile.nextValue());
		evnt->reachable_from.y = toInt(infile.nextValue());
		evnt->reachable_from.w = toInt(infile.nextValue());
		evnt->reachable_from.h = toInt(infile.nextValue());
	}
	else {
		loadEventComponent(infile, evnt, NULL);
	}
}
void AnimationSet::load() {
	assert(!loaded);
	loaded = true;

	FileParser parser;
	// @CLASS Animation|Description of animations in animations/
	if (!parser.open(name, true, "Error loading animation definition: " + name))
		return;

	string _name = "";
	int position = 0;
	int frames = 0;
	int duration = 0;
	Point render_size;
	Point render_offset;
	string type = "";
	string starting_animation = "";
	bool first_section=true;
	bool compressed_loading=false; // is reset every section to false, set by frame keyword
	Animation *newanim = NULL;
	vector<short> active_frames;

	// Parse the file and on each new section create an animation object from the data parsed previously
	while (parser.next()) {
		// create the animation if finished parsing a section
		if (parser.new_section) {
			if (!first_section && !compressed_loading) {
				Animation *a = new Animation(_name, type, sprite);
				a->setupUncompressed(render_size, render_offset, position, frames, duration);
				if (!active_frames.empty())
					a->setActiveFrames(active_frames);
				active_frames.clear();
				animations.push_back(a);
			}
			first_section = false;
			compressed_loading = false;
		}
		if (parser.key == "image") {
			// @ATTR image|string|Filename of sprite-sheet image.
			if (sprite != NULL) {
				printf("multiple images specified in %s, dragons be here!\n", name.c_str());
				SDL_Quit();
				exit(128);
			}

			sprite = render_device->loadImage(parser.val);
		}
		else if (parser.key == "position") {
			// @ATTR position|integer|Number of frames to the right to use as the first frame. Unpacked animations only.
			position = toInt(parser.val);
		}
		else if (parser.key == "frames") {
			// @ATTR frames|integer|The total number of frames
			frames = toInt(parser.val);
		}
		else if (parser.key == "duration") {
			// @ATTR duration|integer|The duration of each frame.
			duration = parse_duration(parser.val);
		}
		else if (parser.key == "type")
			// @ATTR type|[play_once, back_forth, looped]|How to loop (or not loop) this animation.
			type = parser.val;
		else if (parser.key == "render_size") {
			// @ATTR render_size|w (integer), h (integer)|Width and height of animation.
			render_size.x = toInt(parser.nextValue());
			render_size.y = toInt(parser.nextValue());
		}
		else if (parser.key == "render_offset") {
			// @ATTR render_offset|x (integer), y (integer)|Render x/y offset.
			render_offset.x = toInt(parser.nextValue());
			render_offset.y = toInt(parser.nextValue());
		}
		else if (parser.key == "active_frame") {
			// @ATTR active_frame|[all:frame (integer), ...]|A list of frames marked as "active". Also, "all" can be used to mark all frames as active.
			active_frames.clear();
			string nv = parser.nextValue();
			if (nv == "all") {
				active_frames.push_back(-1);
			}
			else {
				while (nv != "") {
					active_frames.push_back(toInt(nv));
					nv = parser.nextValue();
				}
				sort(active_frames.begin(), active_frames.end());
				active_frames.erase(unique(active_frames.begin(), active_frames.end()), active_frames.end());
			}
		}
		else if (parser.key == "frame") {
			// @ATTR frame|index (integer), direction (integer), x (integer), y (integer), w (integer), h (integer), x offset (integer), y offset (integer)|A single frame of a compressed animation.
			if (compressed_loading == false) { // first frame statement in section
				newanim = new Animation(_name, type, sprite);
				newanim->setup(frames, duration);
				if (!active_frames.empty())
					newanim->setActiveFrames(active_frames);
				active_frames.clear();
				animations.push_back(newanim);
				compressed_loading = true;
			}
			// frame = index, direction, x, y, w, h, offsetx, offsety
			Rect r;
			Point offset;
			const int index = toInt(parser.nextValue());
			const int direction = toInt(parser.nextValue());
			r.x = toInt(parser.nextValue());
			r.y = toInt(parser.nextValue());
			r.w = toInt(parser.nextValue());
			r.h = toInt(parser.nextValue());
			offset.x = toInt(parser.nextValue());
			offset.y = toInt(parser.nextValue());
			newanim->addFrame(index, direction, r, offset);
		}
		else {
			fprintf(stderr, "animations definitions (%s): Key %s not supported!\n", parser.getFileName().c_str(), parser.key.c_str());
		}

		if (_name == "") {
			// This is the first animation
			starting_animation = parser.section;
		}
		_name = parser.section;
	}

	if (!compressed_loading) {
		// add final animation
		Animation *a = new Animation(_name, type, sprite);
		a->setupUncompressed(render_size, render_offset, position, frames, duration);
		if (!active_frames.empty())
			a->setActiveFrames(active_frames);
		active_frames.clear();
		animations.push_back(a);
	}

	if (starting_animation != "") {
		Animation *a = getAnimation(starting_animation);
		delete defaultAnimation;
		defaultAnimation = a;
	}
}
Exemple #10
0
void Map::loadEventComponent(FileParser &infile) {
	// new event component
	events.back().components.push_back(Event_Component());
	Event_Component *e = &events.back().components.back();
	e->type = infile.key;

	if (infile.key == "tooltip") {
		e->s = msg->get(infile.val);
	}
	else if (infile.key == "power_path") {
		// x,y are src, if s=="hero" we target the hero,
		// else we'll use values in a,b as coordinates
		e->x = toInt(infile.nextValue());
		e->y = toInt(infile.nextValue());

		std::string dest = infile.nextValue();
		if (dest == "hero") {
			e->s = "hero";
		}
		else {
			e->a = toInt(dest);
			e->b = toInt(infile.nextValue());
		}
	}
	else if (infile.key == "power_damage") {
		e->a = toInt(infile.nextValue());
		e->b = toInt(infile.nextValue());
	}
	else if (infile.key == "intermap") {
		e->s = infile.nextValue();
		e->x = toInt(infile.nextValue());
		e->y = toInt(infile.nextValue());
	}
	else if (infile.key == "intramap") {
		e->x = toInt(infile.nextValue());
		e->y = toInt(infile.nextValue());
	}
	else if (infile.key == "mapmod") {
		e->s = infile.nextValue();
		e->x = toInt(infile.nextValue());
		e->y = toInt(infile.nextValue());
		e->z = toInt(infile.nextValue());

		// add repeating mapmods
		std::string repeat_val = infile.nextValue();
		while (repeat_val != "") {
			events.back().components.push_back(Event_Component());
			e = &events.back().components.back();
			e->type = infile.key;
			e->s = repeat_val;
			e->x = toInt(infile.nextValue());
			e->y = toInt(infile.nextValue());
			e->z = toInt(infile.nextValue());

			repeat_val = infile.nextValue();
		}
	}
	else if (infile.key == "soundfx") {
		e->s = infile.nextValue();
		e->x = e->y = -1;

		std::string s = infile.nextValue();
		if (s != "") e->x = toInt(s);

		s = infile.nextValue();
		if (s != "") e->y = toInt(s);

	}
	else if (infile.key == "loot") {
		e->s = infile.nextValue();
		e->x = toInt(infile.nextValue()) * UNITS_PER_TILE + UNITS_PER_TILE/2;
		e->y = toInt(infile.nextValue()) * UNITS_PER_TILE + UNITS_PER_TILE/2;

		// drop chance
		std::string chance = infile.nextValue();
		if (chance == "fixed") e->z = 0;
		else e->z = toInt(chance);

		// quantity min/max
		e->a = toInt(infile.nextValue());
		if (e->a < 1) e->a = 1;
		e->b = toInt(infile.nextValue());
		if (e->b < e->a) e->b = e->a;

		// add repeating loot
		std::string repeat_val = infile.nextValue();
		while (repeat_val != "") {
			events.back().components.push_back(Event_Component());
			e = &events.back().components.back();
			e->type = infile.key;
			e->s = repeat_val;
			e->x = toInt(infile.nextValue()) * UNITS_PER_TILE + UNITS_PER_TILE/2;
			e->y = toInt(infile.nextValue()) * UNITS_PER_TILE + UNITS_PER_TILE/2;

			chance = infile.nextValue();
			if (chance == "fixed") e->z = 0;
			else e->z = toInt(chance);

			e->a = toInt(infile.nextValue());
			if (e->a < 1) e->a = 1;
			e->b = toInt(infile.nextValue());
			if (e->b < e->a) e->b = e->a;

			repeat_val = infile.nextValue();
		}
	}
	else if (infile.key == "msg") {
		e->s = msg->get(infile.val);
	}
	else if (infile.key == "shakycam") {
		e->x = toInt(infile.val);
	}
	else if (infile.key == "requires_status") {
		e->s = infile.nextValue();

		// add repeating requires_status
		std::string repeat_val = infile.nextValue();
		while (repeat_val != "") {
			events.back().components.push_back(Event_Component());
			e = &events.back().components.back();
			e->type = infile.key;
			e->s = repeat_val;

			repeat_val = infile.nextValue();
		}
	}
	else if (infile.key == "requires_not") {
		e->s = infile.nextValue();

		// add repeating requires_not
		std::string repeat_val = infile.nextValue();
		while (repeat_val != "") {
			events.back().components.push_back(Event_Component());
			e = &events.back().components.back();
			e->type = infile.key;
			e->s = repeat_val;

			repeat_val = infile.nextValue();
		}
	}
	else if (infile.key == "requires_level") {
		e->x = toInt(infile.nextValue());
	}
	else if (infile.key == "requires_not_level") {
		e->x = toInt(infile.nextValue());
	}
	else if (infile.key == "requires_item") {
		e->x = toInt(infile.nextValue());

		// add repeating requires_item
		std::string repeat_val = infile.nextValue();
		while (repeat_val != "") {
			events.back().components.push_back(Event_Component());
			e = &events.back().components.back();
			e->type = infile.key;
			e->x = toInt(repeat_val);

			repeat_val = infile.nextValue();
		}
	}
	else if (infile.key == "set_status") {
		e->s = infile.nextValue();

		// add repeating set_status
		std::string repeat_val = infile.nextValue();
		while (repeat_val != "") {
			events.back().components.push_back(Event_Component());
			e = &events.back().components.back();
			e->type = infile.key;
			e->s = repeat_val;

			repeat_val = infile.nextValue();
		}
	}
	else if (infile.key == "unset_status") {
		e->s = infile.nextValue();

		// add repeating unset_status
		std::string repeat_val = infile.nextValue();
		while (repeat_val != "") {
			events.back().components.push_back(Event_Component());
			e = &events.back().components.back();
			e->type = infile.key;
			e->s = repeat_val;

			repeat_val = infile.nextValue();
		}
	}
	else if (infile.key == "remove_item") {
		e->x = toInt(infile.nextValue());

		// add repeating remove_item
		std::string repeat_val = infile.nextValue();
		while (repeat_val != "") {
			events.back().components.push_back(Event_Component());
			e = &events.back().components.back();
			e->type = infile.key;
			e->x = toInt(repeat_val);

			repeat_val = infile.nextValue();
		}
	}
	else if (infile.key == "reward_xp") {
		e->x = toInt(infile.val);
	}
	else if (infile.key == "power") {
		e->x = toInt(infile.val);
	}
	else if (infile.key == "spawn") {

		e->s = infile.nextValue();
		e->x = toInt(infile.nextValue()) * UNITS_PER_TILE + UNITS_PER_TILE/2;
		e->y = toInt(infile.nextValue()) * UNITS_PER_TILE + UNITS_PER_TILE/2;

		// add repeating spawn
		std::string repeat_val = infile.nextValue();
		while (repeat_val != "") {
			events.back().components.push_back(Event_Component());
			e = &events.back().components.back();
			e->type = infile.key;

			e->s = repeat_val;
			e->x = toInt(infile.nextValue()) * UNITS_PER_TILE + UNITS_PER_TILE/2;
			e->y = toInt(infile.nextValue()) * UNITS_PER_TILE + UNITS_PER_TILE/2;

			repeat_val = infile.nextValue();
		}
	}
	else if (infile.key == "stash") {
		e->s = infile.val;
	}
	else if (infile.key == "npc") {
		e->s = infile.val;
	}
	else if (infile.key == "music") {
		e->s = infile.val;
	}
	else if (infile.key == "cutscene") {
		e->s = infile.val;
	}
	else if (infile.key == "repeat") {
		e->s = infile.val;
	}
	else {
		fprintf(stderr, "Map: Unknown key value: %s in file %s in section %s\n", infile.key.c_str(), infile.getFileName().c_str(), infile.section.c_str());
	}
}
Exemple #11
0
void Map::loadEvent(FileParser &infile) {
	if (infile.key == "type") {
		std::string type = infile.val;
		events.back().type = type;

		if      (type == "on_trigger");
		else if (type == "on_mapexit"); // no need to set keep_after_trigger to false correctly, it's ignored anyway
		else if (type == "on_leave");
		else if (type == "on_load") {
			events.back().keep_after_trigger = false;
		}
		else if (type == "on_clear") {
			events.back().keep_after_trigger = false;
		}
		else {
			fprintf(stderr, "Map: Loading event in file %s\nEvent type %s unknown, change to \"on_trigger\" to suppress this warning.\n", infile.getFileName().c_str(), type.c_str());
		}
	}
	else if (infile.key == "location") {
		events.back().location.x = toInt(infile.nextValue());
		events.back().location.y = toInt(infile.nextValue());
		events.back().location.w = toInt(infile.nextValue());
		events.back().location.h = toInt(infile.nextValue());
	}
	else if (infile.key == "hotspot") {
		if (infile.val == "location") {
			events.back().hotspot.x = events.back().location.x;
			events.back().hotspot.y = events.back().location.y;
			events.back().hotspot.w = events.back().location.w;
			events.back().hotspot.h = events.back().location.h;
		}
		else {
			events.back().hotspot.x = toInt(infile.nextValue());
			events.back().hotspot.y = toInt(infile.nextValue());
			events.back().hotspot.w = toInt(infile.nextValue());
			events.back().hotspot.h = toInt(infile.nextValue());
		}
	}
	else if (infile.key == "cooldown") {
		events.back().cooldown = parse_duration(infile.val);
	}
	else {
		loadEventComponent(infile);
	}
}