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; }
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; } }
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); }
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(); }
// dx & dy indicates moving direction, useful for choosing melee attack targets bool PlayerInst::enqueue_io_spell_and_attack_actions(GameState* gs, float dx, float dy) { GameView& view = gs->view(); WeaponEntry& wentry = weapon().weapon_entry(); 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(), frame = gs->frame(); bool is_moving = (dx != 0.0f || dy != 0.0f); IOController& io = gs->io_controller(); bool attack_used = enqueue_io_spell_actions(gs); bool autotarget = io.query_event(IOEvent::AUTOTARGET_CURRENT_ACTION) || io.query_event(IOEvent::ACTIVATE_SPELL_N); bool mousetarget = io.query_event(IOEvent::MOUSETARGET_CURRENT_ACTION); bool weaponuse = spell_selected() == -1; // choose & use weapon if (io.query_event(IOEvent::USE_WEAPON)) { queued_actions.push_back( game_action(gs, this, GameAction::CHOSE_SPELL, -1)); autotarget = true; weaponuse = true; } if (spell_selected() >= 0 && spells_known().get_entry(spell_selected()).mp_cost > core_stats().mp) { weaponuse = true; } // weapon use if (!attack_used && weaponuse && (autotarget || mousetarget)) { bool is_projectile = wentry.uses_projectile || equipment().has_projectile(); MonsterController& mc = gs->monster_controller(); GameInst* curr_target = gs->get_instance(current_target); GameInst* target = NULL; Pos targ_pos; if (is_projectile) { if (mousetarget) { targ_pos = Pos(rmx, rmy); } else if (autotarget && curr_target) { targ_pos = curr_target->pos(); } } else { if (mousetarget) { dx = rmx - x, dy = rmy - y; } target = get_weapon_autotarget(gs, this, curr_target, dx, dy); if (target) { targ_pos = Pos(target->x, target->y); } if (!is_moving && !target && !mousetarget && spell_selected() == -1 && curr_target && !is_projectile) { int vx, vy; GameInst* closest = get_closest_monster(gs, this); if (closest && decide_attack_movement(pos(), closest->pos(), TILE_SIZE / 4, vx, vy)) { queued_actions.push_back( game_action(gs, this, GameAction::MOVE, spellselect, round(vx), round(vy))); } } } if (target || (is_projectile && (mousetarget || curr_target))) { queued_actions.push_back( game_action(gs, this, GameAction::USE_WEAPON, spellselect, targ_pos.x, targ_pos.y)); attack_used = true; } } return attack_used; }
bool PlayerInst::enqueue_io_spell_actions(GameState* gs) { GameView& view = gs->view(); IOController& io = gs->io_controller(); SpellsKnown& spells = spells_known(); bool perform_spell = false; bool chose_spell = false; bool triggered_already = false; int newspell = spell_selected(); //Spell choice for (int i = 0; i < spells.amount(); i++) { IOEvent event(IOEvent::ACTIVATE_SPELL_N, i); if (io.query_event(event, &triggered_already)) { chose_spell = true; // Use the remembered choice to determine if its appropriate to cast this spell // This makes sure the key must be hit once to switch spell, and again to use it if (spell_selected() == i && previous_spellselect == spell_selected()) { //Double hit a spell switch to quick-perform it perform_spell = true; } else if (!triggered_already) { newspell = i; } break; } } if (!chose_spell) { // If we did not switch a spell with one of the quick-select keys, remember our choice previous_spellselect = spell_selected(); if (io.query_event(IOEvent::TOGGLE_ACTION_UP)) { if (spells.amount() > 0) { newspell = (spell_selected() + 1) % spells.amount(); } } else if (io.query_event(IOEvent::TOGGLE_ACTION_DOWN)) { if (spell_selected() <= 0) { newspell = spells.amount() - 1; } else { newspell = spell_selected() - 1; } } } if (newspell != spell_selected()) { queued_actions.push_back( game_action(gs, this, GameAction::CHOSE_SPELL, newspell)); } bool auto_target = true; // We don't auto-target unless a mouse is not used if (!perform_spell && io.query_event(IOEvent::MOUSETARGET_CURRENT_ACTION, &triggered_already)) { perform_spell = true; auto_target = false; } else if (!perform_spell) { perform_spell = io.query_event(IOEvent::AUTOTARGET_CURRENT_ACTION, &triggered_already); } if (spell_selected() > -1 && perform_spell) { SpellEntry& spl_entry = spells.get_entry(spell_selected()); Pos target; bool can_trigger = !triggered_already || spl_entry.can_cast_with_held_key; bool can_target; if (auto_target) { can_target = lua_spell_get_target(gs, this, spl_entry.autotarget_func, target); } else { int rmx = view.x + gs->mouse_x(), rmy = view.y + gs->mouse_y(); target = Pos(rmx, rmy); can_target = true; } if (spl_entry.mp_cost > core_stats().mp) { if (!triggered_already && can_target) { enqueue_not_enough_mana_actions(gs); } return false; } else { autouse_mana_potion_try_count = 0; } if (can_trigger && can_target) { bool can_use = lua_spell_check_prereq(gs, this, spl_entry, spl_entry.prereq_func, target); if (can_use) { queued_actions.push_back( game_action(gs, this, GameAction::USE_SPELL, spell_selected(), target.x, target.y)); return true; } else if (!auto_target) { gs->game_chat().add_message("Target location is not valid."); } } else if (!triggered_already && !can_target) { gs->game_chat().add_message( "Cannot currently auto-target spell. Use manual controls (with mouse)."); } } return false; }
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)); } }