예제 #1
0
bool CombatGameInst::projectile_attack(GameState* gs, CombatGameInst* inst,
		const Weapon& weapon, const Projectile& projectile) {
	if (!cooldowns().can_doaction())
		return false;
	MTwist& mt = gs->rng();

	WeaponEntry& wentry = weapon.weapon_entry();
	ProjectileEntry& pentry = projectile.projectile_entry();
	AttackStats attack;
	if (!pentry.is_unarmed()) {
		attack.weapon = weapon;
	}
	attack.projectile = projectile;
	EffectiveAttackStats atkstats = effective_atk_stats(mt,
			AttackStats(weapon, projectile));

	Pos p(inst->x, inst->y);
	p.x += gs->rng().rand(-12, +13);
	p.y += gs->rng().rand(-12, +13);
	if (gs->tile_radius_test(p.x, p.y, 10)) {
		p.x = inst->x;
		p.y = inst->y;
	}

	GameInst* bullet = new ProjectileInst(projectile, atkstats, id, Pos(x, y),
			p, pentry.speed, pentry.range);
	gs->add_instance(bullet);
	cooldowns().reset_action_cooldown(pentry.cooldown);
	cooldowns().action_cooldown += gs->rng().rand(-4, 5);
	return false;
}
예제 #2
0
void CombatGameInst::draw(GameState *gs) {
	GameView& view = gs->view();
	SpriteEntry& spr = game_sprite_data[sprite];
	Colour draw_colour = effects().effected_colour();

	if (cooldowns().is_hurting()) {
		float s = 1 - hurt_alpha_value(cooldowns().hurt_cooldown);
		draw_colour = draw_colour.multiply(Colour(255, 255 * s, 255 * s));
	}

	int sx = x - spr.width() / 2, sy = y - spr.height() / 2;
	gl_draw_sprite(view, sprite, sx, sy, vx, vy, gs->frame(), draw_colour);

	effects().draw_effect_sprites(gs, Pos(sx, sy));

	if (is_resting) {
		GLimage& restimg =
				game_sprite_data[get_sprite_by_name("resting")].img();
		gl_draw_image(view, restimg, x - spr.width() / 2, y - spr.height() / 2);
	}
	CoreStats& ecore = effective_stats().core;

	//Draw health bar
	int healthbar_offsety = 20;
	if (target_radius > 16)
		healthbar_offsety = target_radius + 8;
	if (ecore.hp < ecore.max_hp) {
		const BBox statbox(x - 10, y - healthbar_offsety, x + 10,
				y - healthbar_offsety + 5);
		gl_draw_statbar(view, statbox, ecore.hp, ecore.max_hp);
	}
}
예제 #3
0
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);
}
예제 #4
0
void PlayerInst::pickup_item(GameState* gs, const GameAction& action) {
	const int PICKUP_RATE = 10;
	GameInst* inst = gs->get_instance(action.use_id);
	if (!inst) {
		return;
	}
	ItemInst* iteminst = dynamic_cast<ItemInst*>(inst);
	LANARTS_ASSERT(iteminst);

	const Item& type = iteminst->item_type();
	int amnt = iteminst->item_quantity();

	bool inventory_full = false;
	if (type.id == get_item_by_name("Gold")) {
		gold() += amnt;
	} else {
		itemslot_t slot = inventory().add(type);
		if (slot == -1) {
			inventory_full = true;
		} else if (projectile_should_autowield(equipment(), type,
				this->last_chosen_weaponclass)) {
			projectile_smart_equip(inventory(), slot);
		}
	}

	if (!inventory_full) {
		cooldowns().reset_pickup_cooldown(PICKUP_RATE);
		gs->remove_instance(iteminst);
	}
}
예제 #5
0
bool CombatGameInst::damage(GameState* gs, int dmg) {
	if (core_stats().hurt(dmg)) {
		die(gs);
		return true;
	}
	signal_was_damaged();
	cooldowns().reset_hurt_cooldown(HURT_COOLDOWN);
	return false;
}
예제 #6
0
void PlayerInst::use_spell(GameState* gs, const GameAction& action) {
    if (!effective_stats().allowed_actions.can_use_spells) {
        return;
    }
    MTwist& mt = gs->rng();
    EffectiveStats& estats = effective_stats();

    spell_id spell = spells_known().get(action.use_id);
    SpellEntry& spl_entry = game_spell_data.at(spell);

    if (spl_entry.mp_cost > core_stats().mp
            || (!spl_entry.can_cast_with_cooldown && !cooldowns().can_doaction())) {
        return;
    }

    Pos target = Pos(action.action_x, action.action_y);
    player_use_spell(gs, this, spl_entry, target);

    cooldowns().action_cooldown *= estats.cooldown_mult;
    cooldowns().reset_rest_cooldown(REST_COOLDOWN);
}
예제 #7
0
bool CombatGameInst::melee_attack(GameState* gs, CombatGameInst* inst,
		const Weapon& weapon) {
	bool isdead = false;
	if (!cooldowns().can_doaction())
		return false;

	MTwist& mt = gs->rng();

	AttackStats attack(weapon);
	EffectiveAttackStats atkstats = effective_atk_stats(mt,
			AttackStats(weapon));

	int damage = damage_formula(atkstats, inst->effective_stats());

	if (dynamic_cast<PlayerInst*>(this) || !gs->game_settings().invincible) {
		isdead = inst->damage(gs, damage);
	}

	char dmgstr[32];
	snprintf(dmgstr, 32, "%d", damage);
	float rx, ry;
	direction_towards(Pos(x, y), Pos(inst->x, inst->y), rx, ry, 0.5);
	gs->add_instance(
			new AnimatedInst(Pos(inst->x - 5 + rx * 5, inst->y + ry * 5), -1,
					25, Posf(rx, ry), Posf(), AnimatedInst::DEPTH, dmgstr,
					Colour(255, 148, 120)));

	cooldowns().reset_action_cooldown(atkstats.cooldown);
	cooldowns().action_cooldown += gs->rng().rand(-4, 5);

	WeaponEntry wentry = weapon.weapon_entry();
	if (wentry.name != "none") {
		gs->add_instance(
				new AnimatedInst(inst->pos(), wentry.attack_sprite, 25));
	}

	signal_attacked_successfully();

	return isdead;
}
예제 #8
0
void PlayerInst::use_rest(GameState* gs, const GameAction& action) {
	if (!effective_stats().allowed_actions.can_use_rest) {
		return;
	}
	CoreStats& ecore = effective_stats().core;
	int emax_hp = ecore.max_hp, emax_mp = ecore.max_mp;
	bool atfull = core_stats().hp >= emax_hp && core_stats().mp >= emax_mp;
	if (cooldowns().can_rest() && !atfull && effects().can_rest()) {
		core_stats().heal_hp(ecore.hpregen * 8, emax_hp);
		core_stats().heal_mp(ecore.mpregen * 8, emax_mp);
		ecore.hp = core_stats().hp;
		ecore.mp = core_stats().mp;
		is_resting = true;
	}
}
예제 #9
0
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();
}
예제 #10
0
void PlayerInst::enqueue_io_actions(GameState* gs) {
	LANARTS_ASSERT(is_local_player() && gs->local_player() == this);

	if (actions_set_for_turn) {
		return;
	}

	bool single_player = (gs->player_data().all_players().size() <= 1);

	actions_set_for_turn = true;

	GameSettings& settings = gs->game_settings();
	GameView& view = gs->view();

	PlayerDataEntry& pde = gs->player_data().local_player_data();

	if (pde.action_queue.has_actions_for_frame(gs->frame())) {
		pde.action_queue.extract_actions_for_frame(queued_actions, gs->frame());
		event_log("Player %d has %d actions", player_entry(gs).net_id,
				(int) queued_actions.size());
		return;
	}
	if (!single_player) {
		gs->set_repeat_actions_counter(settings.frame_action_repeat);
	}

	int dx = 0, dy = 0;
	bool mouse_within = gs->mouse_x() < gs->view().width;
	int rmx = view.x + gs->mouse_x(), rmy = view.y + gs->mouse_y();

	bool was_moving = moving, do_stopaction = false;
	IOController& io = gs->io_controller();

	if (!settings.loadreplay_file.empty()) {
		load_actions(gs, queued_actions);
	}

	enqueue_io_movement_actions(gs, dx, dy);

	if (was_moving && !moving && cooldowns().can_do_stopaction()) {
		do_stopaction = true;
	}
//Shifting target
	if (gs->key_press_state(SDLK_k)) {
		shift_autotarget(gs);
	}

	if (gs->key_press_state(SDLK_m))
		spellselect = -1;

	bool attack_used = false;
	if (!gs->game_hud().handle_io(gs, queued_actions)) {
		attack_used = enqueue_io_spell_and_attack_actions(gs, dx, dy);
		enqueue_io_equipment_actions(gs, do_stopaction);
	}

	bool action_usage = io.query_event(IOEvent::ACTIVATE_SPELL_N)
			|| io.query_event(IOEvent::USE_WEAPON)
			|| io.query_event(IOEvent::AUTOTARGET_CURRENT_ACTION)
			|| io.query_event(IOEvent::MOUSETARGET_CURRENT_ACTION);
	if ((do_stopaction && !action_usage) || gs->key_down_state(SDLK_PERIOD)
			|| gs->mouse_downwheel()) {
		queue_portal_use(gs, this, queued_actions);
	}

// If we haven't done anything, rest
	if (queued_actions.empty()) {
		queued_actions.push_back(game_action(gs, this, GameAction::USE_REST));
	}

	ActionQueue only_passive_actions;

	for (int i = 0; i < queued_actions.size(); i++) {
		GameAction::action_t act = queued_actions[i].act;
		if (act == GameAction::MOVE || act == GameAction::USE_REST) {
			only_passive_actions.push_back(queued_actions[i]);
		}
	}

	GameNetConnection& net = gs->net_connection();
	if (net.is_connected()) {
		net_send_player_actions(net, gs->frame(),
				player_get_playernumber(gs, this), queued_actions);
	}

	int repeat = single_player ? 0 : settings.frame_action_repeat;
	for (int i = 1; i <= repeat; i++) {
		for (int j = 0; j < only_passive_actions.size(); j++) {
			only_passive_actions[j].frame = gs->frame() + i;
		}
		pde.action_queue.queue_actions_for_frame(only_passive_actions,
				gs->frame() + i);

		if (net.is_connected()) {
			net_send_player_actions(net, gs->frame() + i,
					player_get_playernumber(gs, this), only_passive_actions);
		}
	}

}
예제 #11
0
void PlayerInst::enqueue_io_equipment_actions(GameState* gs,
		bool do_stopaction) {
	GameView& view = gs->view();
	bool mouse_within = gs->mouse_x() < gs->view().width;
	int rmx = view.x + gs->mouse_x(), rmy = view.y + gs->mouse_y();

	int level = gs->get_level()->id();
	int frame = gs->frame();
	bool item_used = false;
	IOController& io = gs->io_controller();

	Pos p(gs->mouse_x() + view.x, gs->mouse_y() + view.y);
	obj_id target = this->current_target;
	GameInst* targetted = gs->get_instance(target);
	if (targetted)
		p = Pos(targetted->x, targetted->y);

// We may have already used an item eg due to auto-use of items
	bool used_item = false;

//Item use
	for (int i = 0; i < 9 && !used_item; i++) {
		if (io.query_event(IOEvent(IOEvent::USE_ITEM_N, i))) {
			if (inventory().get(i).amount() > 0) {
				item_used = true;
				queued_actions.push_back(
						GameAction(id, GameAction::USE_ITEM, frame, level, i,
								p.x, p.y));
			}
		}
	}
	if (!used_item && gs->game_settings().autouse_health_potions
			&& core_stats().hp < AUTOUSE_HEALTH_POTION_THRESHOLD) {
		int item_slot = inventory().find_slot(
				get_item_by_name("Health Potion"));
		if (item_slot > -1) {
			queued_actions.push_back(
					game_action(gs, this, GameAction::USE_ITEM, item_slot));
			used_item = true;
		}
	}

//Item pickup
	GameInst* inst = NULL;
	if (cooldowns().can_pickup()
			&& gs->object_radius_test(this, &inst, 1, &item_colfilter)) {
		ItemInst* iteminst = (ItemInst*)inst;
		Item& item = iteminst->item_type();

		bool was_dropper = iteminst->last_held_by() == id;
		bool dropper_autopickup = iteminst->autopickup_held();

		bool autopickup = (item.is_normal_item() && !was_dropper
				&& !dropper_autopickup) || (was_dropper && dropper_autopickup);

		bool wieldable_projectile = projectile_should_autowield(equipment(),
				item, this->last_chosen_weaponclass);

		bool pickup_io = gs->key_down_state(SDLK_LSHIFT)
				|| gs->key_down_state(SDLK_RSHIFT);

		if (do_stopaction || wieldable_projectile || pickup_io || autopickup)
			queued_actions.push_back(
					GameAction(id, GameAction::PICKUP_ITEM, frame, level,
							iteminst->id));
	}
}