/**
 * Add a new message to the log
 */
void MenuHUDLog::add(const std::string& s, bool prevent_spam) {
	hide_overlay = false;

	// Make sure we don't spam the same message repeatedly
	if (log_msg.empty() || log_msg.back() != s || !prevent_spam) {
		// add new message
		log_msg.push_back(substituteVarsInString(s, pc));
		msg_age.push_back(calcDuration(log_msg.back()));

		// render the log entry and store it in a buffer
		font->setFont("font_regular");
		Point size = font->calc_size(log_msg.back(), window_area.w - (paragraph_spacing*2));
		Image *graphics = render_device->createImage(size.x, size.y);
		font->renderShadowed(log_msg.back(), 0, 0, JUSTIFY_LEFT, graphics, window_area.w - (paragraph_spacing*2), color_normal);
		msg_buffer.push_back(graphics->createSprite());
		graphics->unref();
	}
	else if (!msg_age.empty()) {
		msg_age.back() = calcDuration(log_msg.back());
	}

	// force HUD messages to vanish in order
	if (msg_age.size() > 1) {
		const size_t last = msg_age.size()-1;
		if (msg_age[last] < msg_age[last-1])
			msg_age[last] = msg_age[last-1];
	}

}
void MenuTalker::createBuffer() {
    if (static_cast<unsigned>(dialog_node) >= npc->dialog.size() || event_cursor >= npc->dialog[dialog_node].size())
        return;

    std::string line;

    // speaker name
    EVENT_COMPONENT_TYPE etype = npc->dialog[dialog_node][event_cursor].type;
    std::string who;

    if (etype == EC_NPC_DIALOG_THEM) {
        who = npc->name;
    }
    else if (etype == EC_NPC_DIALOG_YOU) {
        who = hero_name;
    }

    label_name->set(window_area.x+text_pos.x+text_offset.x, window_area.y+text_pos.y+text_offset.y, JUSTIFY_LEFT, VALIGN_TOP, who, color_normal, font_who);


    line = substituteVarsInString(npc->dialog[dialog_node][event_cursor].s, pc);

    // render dialog text to the scrollbox buffer
    Point line_size = font->calc_size(line,textbox->pos.w-(text_offset.x*2));
    textbox->resize(textbox->pos.w, line_size.y);
    textbox->line_height = font->getLineHeight();
    font->setFont(font_dialog);
    font->render(
        line,
        text_offset.x,
        0,
        JUSTIFY_LEFT,
        textbox->contents->getGraphics(),
        text_pos.w - text_offset.x*2,
        color_normal
    );

    align();
}
Exemple #3
0
/**
 * Add a new message to the log.
 */
void MenuLog::add(const std::string& s, int log_type, bool prevent_spam, Color* color, int style) {
	log[log_type]->add(substituteVarsInString(s, pc), prevent_spam, color, style);
}
Exemple #4
0
/**
 * logic()
 * Handle a single frame.  This includes:
 * - move the avatar based on buttons pressed
 * - calculate the next frame of animation
 * - calculate camera position based on avatar position
 *
 * @param action The actionbar power activated and the target.  action.power == 0 means no power.
 * @param restrict_power_use Whether or not to allow power usage on mouse1
 * @param npc True if the player is talking to an NPC. Can limit ability to move/attack in certain conditions
 */
void Avatar::logic(std::vector<ActionData> &action_queue, bool restrict_power_use, bool npc) {

	// hazards are processed after Avatar and Enemy[]
	// so process and clear sound effects from previous frames
	// check sound effects
	if (AUDIO) {
		if (play_sfx_phys)
			snd->play(sound_melee, GLOBAL_VIRTUAL_CHANNEL, stats.pos, false);
		if (play_sfx_ment)
			snd->play(sound_mental, GLOBAL_VIRTUAL_CHANNEL, stats.pos, false);
		if (play_sfx_hit)
			snd->play(sound_hit, GLOBAL_VIRTUAL_CHANNEL, stats.pos, false);
		if (play_sfx_die)
			snd->play(sound_die, GLOBAL_VIRTUAL_CHANNEL, stats.pos, false);
		if (play_sfx_critdie)
			snd->play(sound_die, GLOBAL_VIRTUAL_CHANNEL, stats.pos, false);
		if(play_sfx_block)
			snd->play(sound_block, GLOBAL_VIRTUAL_CHANNEL, stats.pos, false);

		// clear sound flags
		play_sfx_hit = false;
		play_sfx_phys = false;
		play_sfx_ment = false;
		play_sfx_die = false;
		play_sfx_critdie = false;
		play_sfx_block = false;
	}

	// clear current space to allow correct movement
	mapr->collider.unblock(stats.pos.x, stats.pos.y);

	// turn on all passive powers
	if ((stats.hp > 0 || stats.effects.triggered_death) && !respawn && !transform_triggered) powers->activatePassives(&stats);
	if (transform_triggered) transform_triggered = false;

	// handle when the player stops blocking
	if (stats.effects.triggered_block && !stats.blocking) {
		stats.cur_state = AVATAR_STANCE;
		stats.effects.triggered_block = false;
		stats.effects.clearTriggerEffects(TRIGGER_BLOCK);
		stats.refresh_stats = true;
	}

	stats.logic();

	bool allowed_to_move;
	bool allowed_to_use_power = true;

	// check for revive
	if (stats.hp <= 0 && stats.effects.revive) {
		stats.hp = stats.get(STAT_HP_MAX);
		stats.alive = true;
		stats.corpse = false;
		stats.cur_state = AVATAR_STANCE;
	}

	// check level up
	if (stats.level < static_cast<int>(stats.xp_table.size()) && stats.xp >= stats.xp_table[stats.level]) {
		stats.level_up = true;
		stats.level++;
		std::stringstream ss;
		ss << msg->get("Congratulations, you have reached level %d!", stats.level);
		if (stats.level < stats.max_spendable_stat_points) {
			ss << " " << msg->get("You may increase one attribute through the Character Menu.");
			newLevelNotification = true;
		}
		log_msg = ss.str();
		stats.recalc();
		snd->play(sound_levelup);

		// if the player managed to level up while dead (e.g. via a bleeding creature), restore to life
		if (stats.cur_state == AVATAR_DEAD) {
			stats.cur_state = AVATAR_STANCE;
		}
	}

	// check for bleeding to death
	if (stats.hp == 0 && !(stats.cur_state == AVATAR_DEAD)) {
		stats.effects.triggered_death = true;
		stats.cur_state = AVATAR_DEAD;
	}

	// assist mouse movement
	if (!inpt->pressing[MAIN1]) {
		drag_walking = false;
	}

	// block some interactions when attacking
	if (!inpt->pressing[MAIN1] && !inpt->pressing[MAIN2]) {
		stats.attacking = false;
	}
	else if((inpt->pressing[MAIN1] && !inpt->lock[MAIN1]) || (inpt->pressing[MAIN2] && !inpt->lock[MAIN2])) {
		stats.attacking = true;
	}

	// handle animation
	if (!stats.effects.stun) {
		activeAnimation->advanceFrame();
		for (unsigned i=0; i < anims.size(); i++) {
			if (anims[i] != NULL)
				anims[i]->advanceFrame();
		}
	}

	if (target_anim && target_anim->getTimesPlayed() >= 1) {
		target_visible = false;
		target_anim->reset();
	}

	if (target_anim && target_visible)
		target_anim->advanceFrame();

	// change the cursor if we're attacking
	if (action_queue.empty()) {
		lock_cursor = false;
	}
	else if (lock_cursor) {
		curs->setCursor(CURSOR_ATTACK);
	}

	// save a valid tile position in the event that we untransform on an invalid tile
	if (stats.transformed && mapr->collider.is_valid_position(stats.pos.x,stats.pos.y,MOVEMENT_NORMAL, true)) {
		transform_pos = stats.pos;
		transform_map = mapr->getFilename();
	}

	if (!stats.effects.stun) {
		switch(stats.cur_state) {
			case AVATAR_STANCE:

				setAnimation("stance");

				// allowed to move or use powers?
				if (MOUSE_MOVE) {
					allowed_to_move = restrict_power_use && (!inpt->lock[MAIN1] || drag_walking) && !lockAttack && !npc;
					allowed_to_use_power = !allowed_to_move;
				}
				else {
					allowed_to_move = true;
					allowed_to_use_power = true;
				}

				// handle transitions to RUN
				if (allowed_to_move)
					set_direction();

				if (pressing_move() && allowed_to_move) {
					if (MOUSE_MOVE && inpt->pressing[MAIN1]) {
						inpt->lock[MAIN1] = true;
						drag_walking = true;
					}

					if (move()) { // no collision
						stats.cur_state = AVATAR_RUN;
					}
				}

				if (MOUSE_MOVE && !inpt->pressing[MAIN1]) {
					inpt->lock[MAIN1] = false;
					lockAttack = false;
				}

				break;

			case AVATAR_RUN:

				setAnimation("run");

				if (!sound_steps.empty()) {
					int stepfx = rand() % static_cast<int>(sound_steps.size());

					if (activeAnimation->isFirstFrame() || activeAnimation->isActiveFrame())
						snd->play(sound_steps[stepfx]);
				}

				// allowed to move or use powers?
				if (MOUSE_MOVE) {
					allowed_to_use_power = !(restrict_power_use && !inpt->lock[MAIN1]);
				}
				else {
					allowed_to_use_power = true;
				}

				// handle direction changes
				set_direction();

				// handle transition to STANCE
				if (!pressing_move()) {
					stats.cur_state = AVATAR_STANCE;
					break;
				}
				else if (!move()) { // collide with wall
					stats.cur_state = AVATAR_STANCE;
					break;
				}

				if (activeAnimation->getName() != "run")
					stats.cur_state = AVATAR_STANCE;

				break;

			case AVATAR_ATTACK:

				setAnimation(attack_anim);

				if (MOUSE_MOVE) lockAttack = true;

				if (activeAnimation->isFirstFrame()) {
					if (attack_anim == "swing")
						snd->play(sound_melee);
					else if (attack_anim == "cast")
						snd->play(sound_mental);

					power_cast_duration[current_power] = activeAnimation->getDuration();
					power_cast_ticks[current_power] = power_cast_duration[current_power];
				}

				// do power
				if (activeAnimation->isActiveFrame() && !stats.hold_state) {
					// some powers check if the caster is blocking a tile
					// so we block the player tile prematurely here
					mapr->collider.block(stats.pos.x, stats.pos.y, false);

					powers->activate(current_power, &stats, act_target);
					hero_cooldown[current_power] = powers->getPower(current_power).cooldown;

					if (stats.state_ticks > 0)
						stats.hold_state = true;
				}

				if ((activeAnimation->isLastFrame() && stats.state_ticks == 0) || activeAnimation->getName() != attack_anim) {
					stats.cur_state = AVATAR_STANCE;
					stats.cooldown_ticks = stats.cooldown;
					allowed_to_use_power = false;
				}

				break;

			case AVATAR_BLOCK:

				setAnimation("block");

				stats.blocking = false;

				break;

			case AVATAR_HIT:

				setAnimation("hit");

				if (activeAnimation->isFirstFrame()) {
					stats.effects.triggered_hit = true;
				}

				if (activeAnimation->getTimesPlayed() >= 1 || activeAnimation->getName() != "hit") {
					stats.cur_state = AVATAR_STANCE;
				}

				break;

			case AVATAR_DEAD:
				allowed_to_use_power = false;

				if (stats.effects.triggered_death) break;

				if (stats.transformed) {
					stats.transform_duration = 0;
					untransform();
				}

				setAnimation("die");

				if (!stats.corpse && activeAnimation->isFirstFrame() && activeAnimation->getTimesPlayed() < 1) {
					stats.effects.clearEffects();

					// reset power cooldowns
					for (size_t i = 0; i < hero_cooldown.size(); i++) {
						hero_cooldown[i] = 0;
						power_cast_ticks[i] = 0;
					}

					// raise the death penalty flag.  Another module will read this and reset.
					stats.death_penalty = true;

					// close menus in GameStatePlay
					close_menus = true;

					snd->play(sound_die);

					if (stats.permadeath) {
						log_msg = msg->get("You are defeated. Game over! ${INPUT_CONTINUE} to exit to Title.");
						log_msg = substituteVarsInString(log_msg, this);
					}
					else {
						log_msg = msg->get("You are defeated. ${INPUT_CONTINUE} to continue.");
						log_msg = substituteVarsInString(log_msg, this);
					}

					// if the player is attacking, we need to block further input
					if (inpt->pressing[MAIN1])
						inpt->lock[MAIN1] = true;
				}

				if (activeAnimation->getTimesPlayed() >= 1 || activeAnimation->getName() != "die") {
					stats.corpse = true;
				}

				// allow respawn with Accept if not permadeath
				if (inpt->pressing[ACCEPT] || (TOUCHSCREEN && inpt->pressing[MAIN1] && !inpt->lock[MAIN1])) {
					if (inpt->pressing[ACCEPT]) inpt->lock[ACCEPT] = true;
					if (TOUCHSCREEN && inpt->pressing[MAIN1]) inpt->lock[MAIN1] = true;
					mapr->teleportation = true;
					mapr->teleport_mapname = mapr->respawn_map;
					if (stats.permadeath) {
						// set these positions so it doesn't flash before jumping to Title
						mapr->teleport_destination.x = stats.pos.x;
						mapr->teleport_destination.y = stats.pos.y;
					}
					else {
						respawn = true;

						// set teleportation variables.  GameEngine acts on these.
						mapr->teleport_destination.x = mapr->respawn_point.x;
						mapr->teleport_destination.y = mapr->respawn_point.y;
					}
				}

				break;

			default:
				break;
		}

		// handle power usage
		if (allowed_to_use_power)
			handlePower(action_queue);
	}

	// calc new cam position from player position
	// cam is focused at player position
	mapr->cam.x = stats.pos.x;
	mapr->cam.y = stats.pos.y;

	// check for map events
	mapr->checkEvents(stats.pos);

	// decrement all cooldowns
	for (unsigned i = 0; i < hero_cooldown.size(); i++) {
		hero_cooldown[i]--;
		if (hero_cooldown[i] < 0) hero_cooldown[i] = 0;

		power_cast_ticks[i]--;
		if (power_cast_ticks[i] < 0) power_cast_ticks[i] = 0;
	}

	// make the current square solid
	mapr->collider.block(stats.pos.x, stats.pos.y, false);

	if (stats.state_ticks == 0 && stats.hold_state)
		stats.hold_state = false;

	if (stats.cur_state != AVATAR_ATTACK && stats.charge_speed != 0.0f)
		stats.charge_speed = 0.0f;
}
Exemple #5
0
void MenuPowers::createTooltip(TooltipData* tip, int slot_num, const std::vector<Power_Menu_Cell>& power_cells, bool show_unlock_prompt) {
	if (power_cells[slot_num].upgrade_level > 0)
		tip->addText(powers->powers[power_cells[slot_num].id].name + " (" + msg->get("Level %d", power_cells[slot_num].upgrade_level) + ")");
	else
		tip->addText(powers->powers[power_cells[slot_num].id].name);

	if (powers->powers[power_cells[slot_num].id].passive) tip->addText(msg->get("Passive"));
	tip->addColoredText(substituteVarsInString(powers->powers[power_cells[slot_num].id].description, pc), color_flavor);

	// add mana cost
	if (powers->powers[power_cells[slot_num].id].requires_mp > 0) {
		tip->addText(msg->get("Costs %d MP", powers->powers[power_cells[slot_num].id].requires_mp));
	}
	// add health cost
	if (powers->powers[power_cells[slot_num].id].requires_hp > 0) {
		tip->addText(msg->get("Costs %d HP", powers->powers[power_cells[slot_num].id].requires_hp));
	}
	// add cooldown time
	if (powers->powers[power_cells[slot_num].id].cooldown > 0) {
		std::stringstream ss;
		ss << msg->get("Cooldown:") << " " << getDurationString(powers->powers[power_cells[slot_num].id].cooldown);
		tip->addText(ss.str());
	}

	const Power &pwr = powers->powers[power_cells[slot_num].id];
	for (size_t i=0; i<pwr.post_effects.size(); ++i) {
		std::stringstream ss;
		EffectDef* effect_ptr = powers->getEffectDef(pwr.post_effects[i].id);

		// base stats
		if (effect_ptr == NULL) {
			if (pwr.post_effects[i].magnitude > 0) {
				ss << "+";
			}

			ss << pwr.post_effects[i].magnitude;
			bool found_key = false;

			for (size_t j=0; j<STAT_COUNT; ++j) {
				if (pwr.post_effects[i].id == STAT_KEY[j]) {
					if (STAT_PERCENT[j])
						ss << "%";

					ss << " " << STAT_NAME[j];

					found_key = true;
					break;
				}
			}

			if (!found_key) {
				for (size_t j=0; j<ELEMENTS.size(); ++j) {
					if (pwr.post_effects[i].id == ELEMENTS[j].id + "_resist") {
						ss << "% " << msg->get("%s Resistance", ELEMENTS[j].name.c_str());
						found_key = true;
						break;
					}
				}
			}

			if (!found_key) {
				for (size_t j=0; j<PRIMARY_STATS.size(); ++j) {
					if (pwr.post_effects[i].id == PRIMARY_STATS[j].id) {
						ss << " " << PRIMARY_STATS[j].name;
						found_key = true;
						break;
					}
				}
			}

			if (!found_key) {
				for (size_t j=0; j<DAMAGE_TYPES.size(); ++j) {
					if (pwr.post_effects[i].id == DAMAGE_TYPES[j].min) {
						ss << " " << DAMAGE_TYPES[j].name_min;
						found_key = true;
						break;
					}
					else if (pwr.post_effects[i].id == DAMAGE_TYPES[j].max) {
						ss << " " << DAMAGE_TYPES[j].name_max;
						found_key = true;
						break;
					}
				}
			}
		}
		else {
			if (effect_ptr->type == "damage") {
				ss << pwr.post_effects[i].magnitude << " " << msg->get("Damage per second");
			}
			else if (effect_ptr->type == "damage_percent") {
				ss << pwr.post_effects[i].magnitude << "% " << msg->get("Damage per second");
			}
			else if (effect_ptr->type == "hpot") {
				ss << pwr.post_effects[i].magnitude << " " << msg->get("HP per second");
			}
			else if (effect_ptr->type == "hpot_percent") {
				ss << pwr.post_effects[i].magnitude << "% " << msg->get("HP per second");
			}
			else if (effect_ptr->type == "mpot") {
				ss << pwr.post_effects[i].magnitude << " " << msg->get("MP per second");
			}
			else if (effect_ptr->type == "mpot_percent") {
				ss << pwr.post_effects[i].magnitude << "% " << msg->get("MP per second");
			}
			else if (effect_ptr->type == "speed") {
				if (pwr.post_effects[i].magnitude == 0)
					ss << msg->get("Immobilize");
				else
					ss << msg->get("%d%% Speed", pwr.post_effects[i].magnitude);
			}
			else if (effect_ptr->type == "attack_speed") {
				ss << msg->get("%d%% Attack Speed", pwr.post_effects[i].magnitude);
			}
			else if (effect_ptr->type == "immunity") {
				ss << msg->get("Immunity");
			}
			else if (effect_ptr->type == "immunity_damage") {
				ss << msg->get("Immunity to damage over time");
			}
			else if (effect_ptr->type == "immunity_slow") {
				ss << msg->get("Immunity to slow");
			}
			else if (effect_ptr->type == "immunity_stun") {
				ss << msg->get("Immunity to stun");
			}
			else if (effect_ptr->type == "immunity_hp_steal") {
				ss << msg->get("Immunity to HP steal");
			}
			else if (effect_ptr->type == "immunity_mp_steal") {
				ss << msg->get("Immunity to MP steal");
			}
			else if (effect_ptr->type == "immunity_knockback") {
				ss << msg->get("Immunity to knockback");
			}
			else if (effect_ptr->type == "immunity_damage_reflect") {
				ss << msg->get("Immunity to damage reflection");
			}
			else if (effect_ptr->type == "stun") {
				ss << msg->get("Stun");
			}
			else if (effect_ptr->type == "revive") {
				ss << msg->get("Automatic revive on death");
			}
			else if (effect_ptr->type == "convert") {
				ss << msg->get("Convert");
			}
			else if (effect_ptr->type == "fear") {
				ss << msg->get("Fear");
			}
			else if (effect_ptr->type == "death_sentence") {
				ss << msg->get("Lifespan");
			}
			else if (effect_ptr->type == "shield") {
				if (pwr.base_damage == DAMAGE_TYPES.size())
					continue;

				if (pwr.mod_damage_mode == STAT_MODIFIER_MODE_MULTIPLY) {
					int magnitude = stats->getDamageMax(pwr.base_damage) * pwr.mod_damage_value_min / 100;
					ss << magnitude;
				}
				else if (pwr.mod_damage_mode == STAT_MODIFIER_MODE_ADD) {
					int magnitude = stats->getDamageMax(pwr.base_damage) + pwr.mod_damage_value_min;
					ss << magnitude;
				}
				else if (pwr.mod_damage_mode == STAT_MODIFIER_MODE_ABSOLUTE) {
					if (pwr.mod_damage_value_max == 0 || pwr.mod_damage_value_min == pwr.mod_damage_value_max)
						ss << pwr.mod_damage_value_min;
					else
						ss << pwr.mod_damage_value_min << "-" << pwr.mod_damage_value_max;
				}
				else {
					ss << stats->getDamageMax(pwr.base_damage);
				}

				ss << " " << msg->get("Magical Shield");
			}
			else if (effect_ptr->type == "heal") {
				if (pwr.base_damage == DAMAGE_TYPES.size())
					continue;

				int mag_min = stats->getDamageMin(pwr.base_damage);
				int mag_max = stats->getDamageMax(pwr.base_damage);

				if (pwr.mod_damage_mode == STAT_MODIFIER_MODE_MULTIPLY) {
					mag_min = mag_min * pwr.mod_damage_value_min / 100;
					mag_max = mag_max * pwr.mod_damage_value_min / 100;
					ss << mag_min << "-" << mag_max;
				}
				else if (pwr.mod_damage_mode == STAT_MODIFIER_MODE_ADD) {
					mag_min = mag_min + pwr.mod_damage_value_min;
					mag_max = mag_max + pwr.mod_damage_value_min;
					ss << mag_min << "-" << mag_max;
				}
				else if (pwr.mod_damage_mode == STAT_MODIFIER_MODE_ABSOLUTE) {
					if (pwr.mod_damage_value_max == 0 || pwr.mod_damage_value_min == pwr.mod_damage_value_max)
						ss << pwr.mod_damage_value_min;
					else
						ss << pwr.mod_damage_value_min << "-" << pwr.mod_damage_value_max;
				}
				else {
					ss << mag_min << "-" << mag_max;
				}

				ss << " " << msg->get("Healing");
			}
			else if (effect_ptr->type == "knockback") {
				ss << pwr.post_effects[i].magnitude << " " << msg->get("Knockback");
			}
			else if (pwr.post_effects[i].magnitude == 0) {
				// nothing
			}
		}

		if (!ss.str().empty()) {
			if (pwr.post_effects[i].duration > 0) {
				if (effect_ptr && effect_ptr->type == "death_sentence") {
					ss << ": " << getDurationString(pwr.post_effects[i].duration);
				}
				else {
					ss << " (" << getDurationString(pwr.post_effects[i].duration) << ")";
				}

				if (pwr.post_effects[i].chance != 100)
					ss << " ";
			}
			if (pwr.post_effects[i].chance != 100) {
				ss << "(" << msg->get("%d%% chance", pwr.post_effects[i].chance) << ")";
			}

			tip->addColoredText(ss.str(), color_bonus);
		}
	}

	if (pwr.use_hazard || pwr.type == POWTYPE_REPEATER) {
		std::stringstream ss;

		// modifier_damage
		if (pwr.mod_damage_mode > -1) {
			if (pwr.mod_damage_mode == STAT_MODIFIER_MODE_ADD && pwr.mod_damage_value_min > 0)
				ss << "+";

			if (pwr.mod_damage_value_max == 0 || pwr.mod_damage_value_min == pwr.mod_damage_value_max) {
				ss << pwr.mod_damage_value_min;
			}
			else {
				ss << pwr.mod_damage_value_min << "-" << pwr.mod_damage_value_max;
			}

			if (pwr.mod_damage_mode == STAT_MODIFIER_MODE_MULTIPLY) {
				ss << "%";
			}
			ss << " ";

			if (pwr.base_damage != DAMAGE_TYPES.size()) {
				ss << DAMAGE_TYPES[pwr.base_damage].name;
			}

			if (pwr.count > 1 && pwr.type != POWTYPE_REPEATER)
				ss << " (x" << pwr.count << ")";

			if (!ss.str().empty())
				tip->addColoredText(ss.str(), color_bonus);
		}

		// modifier_accuracy
		if (pwr.mod_accuracy_mode > -1) {
			ss.str("");

			if (pwr.mod_accuracy_mode == STAT_MODIFIER_MODE_ADD && pwr.mod_accuracy_value > 0)
				ss << "+";

			ss << pwr.mod_accuracy_value;

			if (pwr.mod_accuracy_mode == STAT_MODIFIER_MODE_MULTIPLY) {
				ss << "%";
			}
			ss << " ";

			ss << msg->get("Base Accuracy");

			if (!ss.str().empty())
				tip->addColoredText(ss.str(), color_bonus);
		}

		// modifier_critical
		if (pwr.mod_crit_mode > -1) {
			ss.str("");

			if (pwr.mod_crit_mode == STAT_MODIFIER_MODE_ADD && pwr.mod_crit_value > 0)
				ss << "+";

			ss << pwr.mod_crit_value;

			if (pwr.mod_crit_mode == STAT_MODIFIER_MODE_MULTIPLY) {
				ss << "%";
			}
			ss << " ";

			ss << msg->get("Base Critical Chance");

			if (!ss.str().empty())
				tip->addColoredText(ss.str(), color_bonus);
		}

		if (pwr.trait_armor_penetration) {
			ss.str("");
			ss << msg->get("Ignores Absorbtion");
			tip->addColoredText(ss.str(), color_bonus);
		}
		if (pwr.trait_avoidance_ignore) {
			ss.str("");
			ss << msg->get("Ignores Avoidance");
			tip->addColoredText(ss.str(), color_bonus);
		}
		if (pwr.trait_crits_impaired > 0) {
			ss.str("");
			ss << msg->get("%d%% Chance to crit slowed targets", pwr.trait_crits_impaired);
			tip->addColoredText(ss.str(), color_bonus);
		}
		if (pwr.trait_elemental > -1) {
			ss.str("");
			ss << msg->get("%s Elemental Damage", ELEMENTS[pwr.trait_elemental].name.c_str());
			tip->addColoredText(ss.str(), color_bonus);
		}
	}

	std::set<std::string>::iterator it;
	for (it = powers->powers[power_cells[slot_num].id].requires_flags.begin(); it != powers->powers[power_cells[slot_num].id].requires_flags.end(); ++it) {
		for (size_t i=0; i<EQUIP_FLAGS.size(); ++i) {
			if ((*it) == EQUIP_FLAGS[i].id) {
				tip->addText(msg->get("Requires a %s", msg->get(EQUIP_FLAGS[i].name)));
			}
		}
	}

	// add requirement
	for (size_t i = 0; i < PRIMARY_STATS.size(); ++i) {
		if (power_cells[slot_num].requires_primary[i] > 0) {
			if (stats->get_primary(i) < power_cells[slot_num].requires_primary[i])
				tip->addColoredText(msg->get("Requires %s %d", power_cells[slot_num].requires_primary[i], PRIMARY_STATS[i].name.c_str()), color_penalty);
			else
				tip->addText(msg->get("Requires %s %d", power_cells[slot_num].requires_primary[i], PRIMARY_STATS[i].name.c_str()));
		}
	}

	// Draw required Level Tooltip
	if ((power_cells[slot_num].requires_level > 0) && stats->level < power_cells[slot_num].requires_level) {
		tip->addColoredText(msg->get("Requires Level %d", power_cells[slot_num].requires_level), color_penalty);
	}
	else if ((power_cells[slot_num].requires_level > 0) && stats->level >= power_cells[slot_num].requires_level) {
		tip->addText(msg->get("Requires Level %d", power_cells[slot_num].requires_level));
	}

	for (size_t j=0; j < power_cells[slot_num].requires_power.size(); ++j) {
		if (power_cells[slot_num].requires_power[j] == 0) continue;

		int req_index = getCellByPowerIndex(power_cells[slot_num].requires_power[j], power_cell_all);
		if (req_index == -1) continue;

		std::string req_power_name;
		if (power_cell_all[req_index].upgrade_level > 0)
			req_power_name = powers->powers[power_cell_all[req_index].id].name + " (" + msg->get("Level %d", power_cell_all[req_index].upgrade_level) + ")";
		else
			req_power_name = powers->powers[power_cell_all[req_index].id].name;


		// Required Power Tooltip
		int req_cell_index = getCellByPowerIndex(power_cells[slot_num].requires_power[j], power_cell_all);
		if (!checkUnlocked(req_cell_index)) {
			tip->addColoredText(msg->get("Requires Power: %s", req_power_name), color_penalty);
		}
		else {
			tip->addText(msg->get("Requires Power: %s", req_power_name));
		}

	}

	// Draw unlock power Tooltip
	if (power_cells[slot_num].requires_point && !(std::find(stats->powers_list.begin(), stats->powers_list.end(), power_cells[slot_num].id) != stats->powers_list.end())) {
		int unlock_id = getCellByPowerIndex(power_cells[slot_num].id, power_cell_all);
		if (show_unlock_prompt && points_left > 0 && checkUnlock(unlock_id)) {
			tip->addColoredText(msg->get("Click to Unlock (uses 1 Skill Point)"), color_bonus);
		}
		else {
			if (power_cells[slot_num].requires_point && points_left < 1)
				tip->addColoredText(msg->get("Requires 1 Skill Point"), color_penalty);
			else
				tip->addText(msg->get("Requires 1 Skill Point"));
		}
	}
}
Exemple #6
0
/**
 * Create detailed tooltip showing all relevant item info
 */
TooltipData ItemManager::getTooltip(ItemStack stack, StatBlock *stats, int context) {
	TooltipData tip;

	if (stack.empty()) return tip;

	Color color = getItemColor(stack.item);

	// name
	std::stringstream ss;
	if (stack.quantity == 1)
		ss << getItemName(stack.item);
	else
		ss << getItemName(stack.item) << " (" << stack.quantity << ")";
	tip.addText(ss.str(), color);

	// quest item
	if (items[stack.item].quest_item) {
		tip.addText(msg->get("Quest Item"), color_bonus);
	}

	// only show the name of the currency item
	if (stack.item == CURRENCY_ID)
		return tip;

	// flavor text
	if (items[stack.item].flavor != "") {
		tip.addText(substituteVarsInString(items[stack.item].flavor, pc), color_flavor);
	}

	// level
	if (items[stack.item].level != 0) {
		tip.addText(msg->get("Level %d", items[stack.item].level));
	}

	// type
	if (items[stack.item].type != "") {
		tip.addText(msg->get(getItemType(items[stack.item].type)));
	}

	// item quality text for colorblind users
	if (COLORBLIND && items[stack.item].quality != "") {
		color = color_normal;
		for (size_t i=0; i<item_qualities.size(); ++i) {
			if (item_qualities[i].id == items[stack.item].quality) {
				tip.addText(msg->get("Quality: %s", msg->get(item_qualities[i].name)), color);
				break;
			}
		}
	}

	// damage
	if (items[stack.item].dmg_melee_max > 0) {
		if (items[stack.item].dmg_melee_min < items[stack.item].dmg_melee_max)
			tip.addText(msg->get("Melee damage: %d-%d", items[stack.item].dmg_melee_min, items[stack.item].dmg_melee_max));
		else
			tip.addText(msg->get("Melee damage: %d", items[stack.item].dmg_melee_max));
	}
	if (items[stack.item].dmg_ranged_max > 0) {
		if (items[stack.item].dmg_ranged_min < items[stack.item].dmg_ranged_max)
			tip.addText(msg->get("Ranged damage: %d-%d", items[stack.item].dmg_ranged_min, items[stack.item].dmg_ranged_max));
		else
			tip.addText(msg->get("Ranged damage: %d", items[stack.item].dmg_ranged_max));
	}
	if (items[stack.item].dmg_ment_max > 0) {
		if (items[stack.item].dmg_ment_min < items[stack.item].dmg_ment_max)
			tip.addText(msg->get("Mental damage: %d-%d", items[stack.item].dmg_ment_min, items[stack.item].dmg_ment_max));
		else
			tip.addText(msg->get("Mental damage: %d", items[stack.item].dmg_ment_max));
	}

	// absorb
	if (items[stack.item].abs_max > 0) {
		if (items[stack.item].abs_min < items[stack.item].abs_max)
			tip.addText(msg->get("Absorb: %d-%d", items[stack.item].abs_min, items[stack.item].abs_max));
		else
			tip.addText(msg->get("Absorb: %d", items[stack.item].abs_max));
	}

	// bonuses
	unsigned bonus_counter = 0;
	while (bonus_counter < items[stack.item].bonus.size()) {
		ss.str("");

		BonusData* bdata = &items[stack.item].bonus[bonus_counter];

		if (bdata->is_speed) {
			ss << msg->get("%d%% Speed", bdata->value);
			if (bdata->value >= 100) color = color_bonus;
			else color = color_penalty;
		}
		else {
			if (bdata->value > 0) {
				color = color_bonus;
			}
			else {
				color = color_penalty;
			}

			getBonusString(ss, bdata);
		}

		tip.addText(ss.str(), color);
		bonus_counter++;
	}

	// power
	if (items[stack.item].power_desc != "") {
		tip.addText(items[stack.item].power_desc, color_bonus);
	}

	// level requirement
	if (items[stack.item].requires_level > 0) {
		if (stats->level < items[stack.item].requires_level) color = color_requirements_not_met;
		else color = color_normal;
		tip.addText(msg->get("Requires Level %d", items[stack.item].requires_level), color);
	}

	// base stat requirement
	for (unsigned i=0; i<items[stack.item].req_stat.size(); ++i) {
		if (items[stack.item].req_val[i] > 0) {
			if (items[stack.item].req_stat[i] == REQUIRES_PHYS) {
				if (stats->get_physical() < items[stack.item].req_val[i]) color = color_requirements_not_met;
				else color = color_normal;
				tip.addText(msg->get("Requires Physical %d", items[stack.item].req_val[i]), color);
			}
			else if (items[stack.item].req_stat[i] == REQUIRES_MENT) {
				if (stats->get_mental() < items[stack.item].req_val[i]) color = color_requirements_not_met;
				else color = color_normal;
				tip.addText(msg->get("Requires Mental %d", items[stack.item].req_val[i]), color);
			}
			else if (items[stack.item].req_stat[i] == REQUIRES_OFF) {
				if (stats->get_offense() < items[stack.item].req_val[i]) color = color_requirements_not_met;
				else color = color_normal;
				tip.addText(msg->get("Requires Offense %d", items[stack.item].req_val[i]), color);
			}
			else if (items[stack.item].req_stat[i] == REQUIRES_DEF) {
				if (stats->get_defense() < items[stack.item].req_val[i]) color = color_requirements_not_met;
				else color = color_normal;
				tip.addText(msg->get("Requires Defense %d", items[stack.item].req_val[i]), color);
			}
		}
	}

	// requires class
	if (items[stack.item].requires_class != "") {
		if (items[stack.item].requires_class != stats->character_class) color = color_requirements_not_met;
		else color = color_normal;
		tip.addText(msg->get("Requires Class: %s", msg->get(items[stack.item].requires_class)), color);
	}

	// buy or sell price
	if (items[stack.item].getPrice() > 0 && stack.item != CURRENCY_ID) {

		int price_per_unit;
		if (context == VENDOR_BUY) {
			price_per_unit = items[stack.item].getPrice();
			if (stats->currency < price_per_unit) color = color_requirements_not_met;
			else color = color_normal;
			if (items[stack.item].max_quantity <= 1)
				tip.addText(msg->get("Buy Price: %d %s", price_per_unit, CURRENCY), color);
			else
				tip.addText(msg->get("Buy Price: %d %s each", price_per_unit, CURRENCY), color);
		}
		else if (context == VENDOR_SELL) {
			price_per_unit = items[stack.item].getSellPrice();
			if (stats->currency < price_per_unit) color = color_requirements_not_met;
			else color = color_normal;
			if (items[stack.item].max_quantity <= 1)
				tip.addText(msg->get("Buy Price: %d %s", price_per_unit, CURRENCY), color);
			else
				tip.addText(msg->get("Buy Price: %d %s each", price_per_unit, CURRENCY), color);
		}
		else if (context == PLAYER_INV) {
			price_per_unit = items[stack.item].getSellPrice();
			if (price_per_unit == 0) price_per_unit = 1;
			if (items[stack.item].max_quantity <= 1)
				tip.addText(msg->get("Sell Price: %d %s", price_per_unit, CURRENCY));
			else
				tip.addText(msg->get("Sell Price: %d %s each", price_per_unit, CURRENCY));
		}
	}

	if (items[stack.item].set > 0) {
		// item set bonuses
		ItemSet set = item_sets[items[stack.item].set];
		bonus_counter = 0;

		tip.addText("\n" + msg->get("Set: ") + msg->get(item_sets[items[stack.item].set].name), set.color);

		while (bonus_counter < set.bonus.size()) {
			ss.str("");

			Set_bonus* bdata = &set.bonus[bonus_counter];

			ss << msg->get("%d items: ", bdata->requirement);

			if (bdata->is_speed) {
				ss << msg->get("%d%% Speed", bdata->value);
			}
			else {
				getBonusString(ss, bdata);
			}

			tip.addText(ss.str(), set.color);
			bonus_counter++;
		}
	}

	return tip;
}