예제 #1
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;
	}
}
예제 #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::use_move(GameState* gs, const GameAction& action) {
	perf_timer_begin(FUNCNAME);
	int dx = action.action_x;
	int dy = action.action_y;

	float mag = effective_stats().movespeed;

	float ddx = dx * mag;
	float ddy = dy * mag;

	EnemyInst* target = NULL;
//Enemy hitting test for melee
	gs->object_radius_test(this, (GameInst**)&target, 1, &enemy_colfilter,
			x + ddx * 2, y + ddy * 2);

//Smaller radius enemy pushing test, can intercept enemy radius but not too far
	EnemyInst* alreadyhitting[5] = { 0, 0, 0, 0, 0 };
	gs->object_radius_test(this, (GameInst**)alreadyhitting, 5,
			&enemy_colfilter, x, y, radius);
	bool already = false;
	for (int i = 0; i < 5; i++) {
		if (alreadyhitting[i]) {
			if (ddx < 0 == ((alreadyhitting[i]->x - x + ddx * 2) < 0)) {
				ddx = 0;
			}
			if (ddy < 0 == ((alreadyhitting[i]->y - y + ddy * 2) < 0)) {
				ddy = 0;
			}
			already = true;
		}
	}

	Pos newpos(round(rx + ddx), round(ry + ddy));

	if (!gs->tile_radius_test(newpos.x, newpos.y, radius)) {
		vx = ddx;
		vy = ddy;
	} else if (!gs->tile_radius_test(newpos.x, y, radius)) {
		vx = ddx;
	} else if (!gs->tile_radius_test(x, newpos.y, radius)) {
		vy = ddy;
	} else if (ddx != 0 && ddy != 0) {
		//Alternatives in opposite directions for x & y
		Pos newpos_alt1(round(vx + ddx), round(vy - ddy));
		Pos newpos_alt2(round(vx - ddx), round(vy + ddy));
		if (!gs->tile_radius_test(newpos_alt1.x, newpos_alt1.y, radius)) {
			vx += ddx;
			vy -= ddy;
		} else if (!gs->tile_radius_test(newpos_alt2.x, newpos_alt2.y,
				radius)) {
			vx -= ddx;
			vy += ddy;
		}

	}

	event_log("Player id: %d using move for turn %d, vx=%f, vy=%f\n", id, gs->frame(), vx, vy);
	perf_timer_end(FUNCNAME);
}
예제 #5
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);
}
예제 #6
0
bool CombatGameInst::damage(GameState* gs, const EffectiveAttackStats& attack) {

	int dmg = damage_formula(attack, effective_stats());

	if (gs->game_settings().verbose_output) {
		char buff[100];
		snprintf(buff, 100, "Attack: [dmg %d pow %d mag %d%%] -> Damage: %d",
				attack.damage, attack.power, int(attack.magic_percentage * 100),
				dmg);
		gs->game_chat().add_message(buff);

	}

	char dmgstr[32];
	snprintf(dmgstr, 32, "%d", dmg);
	gs->add_instance(
			new AnimatedInst(Pos(), -1, 25, Posf(), Posf(), AnimatedInst::DEPTH,
					dmgstr, Colour(255, 148, 120)));

	return damage(gs, dmg);
}
예제 #7
0
EffectiveAttackStats CombatGameInst::effective_atk_stats(MTwist& mt,
		const AttackStats& attack) {
	return effective_stats().with_attack(mt, attack);
}
예제 #8
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();
}
예제 #9
0
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();
		}
	}
}