void PlayerInst::use_dngn_portal(GameState* gs, const GameAction& action) {
	if (!effective_stats().allowed_actions.can_use_stairs) {
		if (is_local_player()) {
			gs->game_chat().add_message(
					"You cannot use the exit in this state!");
		}
		return;
	}

	cooldowns().reset_stopaction_timeout(50);
	FeatureInst* portal = find_usable_portal(gs, this);
	portal->player_interact(gs, this);
	reset_rest_cooldown();

	std::string subject_and_verb = "You travel";
	if (!is_local_player()) {
		subject_and_verb = player_entry(gs).player_name + " travels";
	}

	const std::string& map_label =
			gs->game_world().get_level(current_floor)->label();

	bool label_has_digit = false; // Does it have a number in the label?
	for (int i = 0; i < map_label.size(); i++) {
		if (isdigit(map_label[i])) {
			label_has_digit = true;
			break;
		}
	}

	gs->game_chat().add_message(
			format("%s to %s%s", subject_and_verb.c_str(),
					label_has_digit ? "" : "the ", map_label.c_str()),
			is_local_player() ? COL_WHITE : COL_YELLOW);
}
void PlayerInst::use_weapon(GameState* gs, const GameAction& action) {
    WeaponEntry& wentry = weapon().weapon_entry();
    MTwist& mt = gs->rng();
    const int MAX_MELEE_HITS = 10;
    EffectiveStats& estats = effective_stats();
    if (!cooldowns().can_doaction()) {
        return;
    }

    Pos start(x, y);
    Pos actpos(action.action_x, action.action_y);

    if (wentry.uses_projectile && !equipment().has_projectile()) {
        return;
    }

    int cooldown = 0;

    if (equipment().has_projectile()) {
        const Projectile& projectile = equipment().projectile;
        ProjectileEntry& pentry = projectile.projectile_entry();
        item_id item = get_item_by_name(pentry.name.c_str());
        int weaprange = std::max(wentry.range, pentry.range);

        AttackStats weaponattack(weapon());

        bool wallbounce = false;
        int nbounces = 0;
        float movespeed = pentry.speed;

        cooldown = std::max(wentry.cooldown, pentry.cooldown);

        //XXX: Horrible hack REMOVE THIS LATER
        if (class_stats().class_type().name == "Archer"
                && pentry.weapon_class == "bows") {
            int xplevel = class_stats().xplevel;
            float movebonus = class_stats().xplevel / 4.0f;
            if (movebonus > 2) {
                movebonus = 2;
            }
            float cooldown_mult = 1.0f - (class_stats().xplevel - 1) / 20.0f;
            if (cooldown_mult <= 0.85) {
                cooldown_mult = 0.85;
            }
            cooldown *= cooldown_mult;
            movespeed += movebonus;
            if (xplevel >= 3 && core_stats().mp >= 5) {
                nbounces = 2;
                core_stats().mp -= 5;
            }
        }

        GameInst* bullet = new ProjectileInst(projectile,
                                              effective_atk_stats(mt, weaponattack), id, start, actpos,
                                              movespeed, weaprange, NONE, wallbounce, nbounces);
        gs->add_instance(bullet);
        equipment().use_ammo();
    } else {
        int weaprange = wentry.range + this->radius + TILE_SIZE / 2;
        float mag = distance_between(actpos, Pos(x, y));
        if (mag > weaprange) {
            float dx = actpos.x - x, dy = actpos.y - y;
            actpos = Pos(x + dx / mag * weaprange, y + dy / mag * weaprange);
        }

        GameInst* enemies[MAX_MELEE_HITS];

        int max_targets = std::min(MAX_MELEE_HITS, wentry.max_targets);

        int numhit = get_targets(gs, this, actpos.x, actpos.y, wentry.dmgradius,
                                 enemies, max_targets);

        if (numhit == 0) {
            return;
        }

        for (int i = 0; i < numhit; i++) {
            EnemyInst* e = (EnemyInst*)enemies[i];
            lua_hit_callback(gs->get_luastate(), wentry.on_hit_func, this, e);
            if (attack(gs, e, AttackStats(equipment().weapon))) {
                PlayerData& pc = gs->player_data();
                signal_killed_enemy();

                char buffstr[32];
                int amnt = round(
                               double(e->xpworth()) / pc.all_players().size());
                players_gain_xp(gs, amnt);
                snprintf(buffstr, 32, "%d XP", amnt);
                gs->add_instance(
                    new AnimatedInst(Pos(e->x - 5, e->y - 5), -1, 25,
                                     Posf(), Posf(), AnimatedInst::DEPTH, buffstr,
                                     Colour(255, 215, 11)));
            }
        }
        cooldown = wentry.cooldown;
    }

    cooldowns().reset_action_cooldown(cooldown * estats.cooldown_mult);

    reset_rest_cooldown();
}
void PlayerInst::use_item(GameState* gs, const GameAction& action) {
	if (!effective_stats().allowed_actions.can_use_items) {
		return;
	}
	itemslot_t slot = action.use_id;
	ItemSlot& itemslot = inventory().get(slot);
	Item& item = itemslot.item;
	ItemEntry& type = itemslot.item_entry();

	lua_State* L = gs->luastate();

	if (item.amount > 0) {
		if (item.is_equipment()) {
			if (itemslot.is_equipped()) {
				inventory().deequip(slot);
			} else {
				if (item.is_projectile()) {
					// Best-effort to equip, may not be possible:
					projectile_smart_equip(inventory(), slot);
				} else if (item.is_weapon()) {
					const Projectile& p = equipment().projectile();
					if (!p.empty()) {
						if (!p.projectile_entry().is_standalone()) {
							inventory().deequip_type(
									EquipmentEntry::AMMO);
						}
					}
					equipment().equip(slot);
					// Try and equip a projectile
					WeaponEntry& wentry = item.weapon_entry();
					if (wentry.uses_projectile) {
						const Projectile& p = equipment().projectile();
						if (p.empty()) {
							projectile_smart_equip(inventory(),
									wentry.weapon_class);
						}
					}
				} else {
					equipment().equip(slot);
				}

				if (item.is_weapon() || item.is_projectile()) {
					last_chosen_weaponclass =
							weapon().weapon_entry().weapon_class;
				}
			}
		} else if (equipment().valid_to_use(item)
				&& item_check_lua_prereq(L, type, this)) {
			item_do_lua_action(L, type, this,
					Pos(action.action_x, action.action_y), item.amount);
			if (is_local_player() && !type.inventory_use_message().empty()) {
				gs->game_chat().add_message(type.inventory_use_message(),
						Colour(100, 100, 255));
			}
			if (item.is_projectile())
				itemslot.clear();
			else
				item.remove_copies(1);
			reset_rest_cooldown();
		}
	}
}