/**
 * @file
 * @brief Brand/ench/etc effects that might alter something in an
 *             unexpected way.
**/

#ifndef FINEFF_H
#define FINEFF_H

class final_effect
{
public:
    final_effect(const actor *attack, const actor *defend, const coord_def &pos)
        : att(attack ? attack->mid : 0),
          def(defend ? defend->mid : 0),
          posn(pos)
    {
    }
    virtual ~final_effect() {}

    virtual bool mergeable(const final_effect &a) const = 0;
    virtual void merge(const final_effect &a)
    {
    }

    void schedule();
    virtual void fire() = 0;

protected:
    mid_t att, def;
    coord_def posn;
    actor *attacker() const { return actor_by_mid(att); }
    actor *defender() const { return actor_by_mid(def); }
};

class mirror_damage_fineff : public final_effect
{
public:
    mirror_damage_fineff(const actor *attack, const actor *defend, int dam)
        : final_effect(attack, defend, coord_def()), damage(dam)
    {
    }
    bool mergeable(const final_effect &a) const;
    void merge(const final_effect &a);
    void fire();
protected:
    int damage;
};

class trample_follow_fineff : public final_effect
{
public:
    trample_follow_fineff(const actor *attack, const coord_def &pos)
        : final_effect(attack, 0, pos)
    {
    }
    bool mergeable(const final_effect &a) const;
    void fire();
};

class blink_fineff : public final_effect
{
public:
    blink_fineff(const actor *blinker)
        : final_effect(0, blinker, coord_def())
    {
    }
    bool mergeable(const final_effect &a) const;
    void fire();
};

class distortion_tele_fineff : public final_effect
{
public:
    distortion_tele_fineff(const actor *defend)
        : final_effect(0, defend, coord_def())
    {
    }
    bool mergeable(const final_effect &a) const;
    void fire();
};

class trj_spawn_fineff : public final_effect
{
public:
    trj_spawn_fineff(const actor *attack, const actor *defend,
                     const coord_def &pos, int dam)
        : final_effect(attack, defend, pos), damage(dam)
    {
    }
    bool mergeable(const final_effect &a) const;
    void merge(const final_effect &a);
    void fire();
protected:
    int damage;
};

class blood_fineff : public final_effect
{
public:
    blood_fineff(const actor *defend, const coord_def &pos, int blood_amount)
        : final_effect(0, 0, pos), mtype(defend->type), blood(blood_amount)
    {
    }
    bool mergeable(const final_effect &a) const;
    void fire();
    void merge(const final_effect &a);
protected:
    monster_type mtype;
    int blood;
};

class deferred_damage_fineff : public final_effect
{
public:
    deferred_damage_fineff(const actor *attack, const actor *defend,
                           int dam, bool _attacker_effects, bool _fatal = true)
        : final_effect(attack, defend, coord_def()),
          damage(dam), attacker_effects(_attacker_effects), fatal(_fatal)
    {
    }
    bool mergeable(const final_effect &a) const;
    void merge(const final_effect &a);
    void fire();
protected:
    int damage;
    bool attacker_effects;
    bool fatal;
};

class starcursed_merge_fineff : public final_effect
{
public:
    starcursed_merge_fineff(const actor *merger)
            : final_effect(0, merger, coord_def())
    {
    }
    bool mergeable(const final_effect &a) const;
    void fire();
};

class shock_serpent_discharge_fineff : public final_effect
{
public:
    shock_serpent_discharge_fineff(const actor *serpent, coord_def pos, int pow)
            : final_effect(0, serpent, coord_def()), position(pos), power(pow),
                    attitude(mons_attitude(serpent->as_monster()))
    {
    }
    bool mergeable(const final_effect &a) const;
    void merge(const final_effect &a);
    void fire();
protected:
    coord_def position;
    int power;
    mon_attitude_type attitude;
};

// A fineff that triggers a daction; otherwise the daction
// occurs immediately (and then later) -- this might actually
// be too soon in some cases.
class delayed_action_fineff : public final_effect
{
public:
    delayed_action_fineff(daction_type _action, const char* _final_msg)
            : final_effect(0, 0, coord_def()),
              action(_action), final_msg(_final_msg)
    {
    }

    bool mergeable(const final_effect &a) const;
    virtual void fire();

protected:
    daction_type action;
    const char *final_msg;
};

class kirke_death_fineff : public delayed_action_fineff
{
public:
    kirke_death_fineff(const char *_final_msg)
            : delayed_action_fineff(DACT_KIRKE_HOGS, _final_msg)
    {
    }

    void fire();
};

class rakshasa_clone_fineff : public final_effect
{
public:
    rakshasa_clone_fineff(const actor *defend, const coord_def &pos)
        : final_effect(0, defend, pos)
    {
    }
    bool mergeable(const final_effect &a) const;
    void fire();
protected:
    int damage;
};
void fire_final_effects();

#endif
