summaryrefslogblamecommitdiffstats
path: root/crawl-ref/source/mon-project.cc
blob: 6e00408a5455d02d47e9d03f885479a4a38782a7 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
















                                                   
                  
                    

                  
                     


                      
                  



                     


                                                  

                                  


                                                                
                                                                                 



                                  
                      









                                        








                                                                     
                                                                                
                                                                     
                                            


                                          
                        



                              
                                          







                                    





                                                                          


                                                 
                              


                                                   












                                                                          
                                                                          












                                                              

                                                                        
                     


                                                   
                 
                                  

                    




                                  
                                           







                                         
                                                               
                                  
                                







                                      
                           
                           
                                            
         
                                                      


                                                                     
                          

        
                                     


                       






























                                                                         










                                         

          












                                      






                                                        


                         

                                     
     

                               


                                                                            
         
                                                       
         





                                                                        

                          
 










                                                                                  
                                                                         











































                                                                           

                                

     

                  
                                                 



                                                         







                              
/*
 *  File:       mon-project.cc
 *  Summary:    Slow projectiles, done as monsters.
 *  Written by: Adam Borowski
 */

#include "AppHdr.h"

#include "mon-project.h"

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <cmath>

#include "externs.h"

#include "cloud.h"
#include "directn.h"
#include "coord.h"
#include "env.h"
#include "itemprop.h"
#include "mgen_data.h"
#include "mon-place.h"
#include "mon-stuff.h"
#include "shout.h"
#include "stuff.h"
#include "terrain.h"
#include "viewchar.h"

bool cast_iood(actor *caster, int pow, bolt *beam)
{
    int mtarg = mgrd(beam->target);
    if (beam->target == you.pos())
        mtarg = MHITYOU;
    
    int mind = mons_place(mgen_data(MONS_ORB_OF_DESTRUCTION,
                (caster->atype() == ACT_PLAYER) ? BEH_FRIENDLY :
                    ((monsters*)caster)->friendly() ? BEH_FRIENDLY : BEH_HOSTILE,
                caster,
                0,
                SPELL_IOOD,
                coord_def(-1, -1),
                mtarg,
                0,
                GOD_NO_GOD));
    if (mind == -1)
    {
        canned_msg(MSG_NOTHING_HAPPENS);
        return (false);
    }

    monsters &mon = menv[mind];
    const coord_def pos = caster->pos();
    beam->choose_ray();
    dprf("beam (%d,%d)+t*(%d,%d)  ray (%f,%f)+t*(%f,%f)",
        pos.x, pos.y, beam->target.x - pos.x, beam->target.y - pos.y,
        beam->ray.r.start.x - 0.5, beam->ray.r.start.y - 0.5,
        beam->ray.r.dir.x, beam->ray.r.dir.y);
    mon.props["iood_x"].get_float() = beam->ray.r.start.x - 0.5;
    mon.props["iood_y"].get_float() = beam->ray.r.start.y - 0.5;
    mon.props["iood_vx"].get_float() = beam->ray.r.dir.x;
    mon.props["iood_vy"].get_float() = beam->ray.r.dir.y;
    mon.props["iood_kc"].get_byte() = (caster->atype() == ACT_PLAYER) ? KC_YOU :
            ((monsters*)caster)->friendly() ? KC_FRIENDLY : KC_OTHER;
    mon.props["iood_pow"].get_short() = pow;
    mon.flags &= ~MF_JUST_SUMMONED;

    // Move away from the caster's square.
    iood_act(mon, true);
    mon.lose_energy(EUT_MOVE);
    return (true);
}

static void _normalize(float &x, float &y)
{
    const float d = sqrt(x*x + y*y);
    if (d <= 0.000001)
        return;
    x/=d;
    y/=d;
}

// angle measured in chord length
static bool _in_front(float vx, float vy, float dx, float dy, float angle)
{
    return ((dx-vx)*(dx-vx) + (dy-vy)*(dy-vy) <= (angle*angle));
}

void _iood_dissipate(monsters &mon)
{
    simple_monster_message(&mon, " dissipates.");
    dprf("iood: dissipating");
    monster_die(&mon, KILL_DISMISSED, NON_MONSTER);
}

// Alas, too much differs to reuse beam shield blocks :(
bool _iood_shielded(monsters &mon, actor &victim)
{
    if (!victim.shield() || victim.incapacitated())
        return (false);

    const int to_hit = 15 + mon.props["iood_pow"].get_short()/12;
    const int con_block = random2(to_hit + victim.shield_block_penalty());
    const int pro_block = victim.shield_bonus();
    dprf("iood shield: pro %d, con %d", pro_block, con_block);
    return (pro_block >= con_block);
}

bool _iood_hit(monsters &mon, const coord_def &pos, bool big_boom = false)
{
    bolt beam;
    beam.name = "orb of destruction";
    beam.flavour = BEAM_NUKE;
    beam.attitude = mon.attitude;
    beam.thrower = (mon.props["iood_kc"].get_byte() == KC_YOU)
                        ? KILL_YOU_MISSILE : KILL_MON_MISSILE;
    beam.colour = WHITE;
    beam.type = dchar_glyph(DCHAR_FIRED_BURST);
    beam.range = 1;
    beam.source = pos;
    beam.target = pos;
    beam.hit = AUTOMATIC_HIT;
    const int pow = mon.props["iood_pow"].get_short();
    beam.damage = dice_def(8, stepdown_value(pow, 30, 30, 200, -1) / 4);
    beam.ex_size = 1;

    monster_die(&mon, KILL_DISMISSED, NON_MONSTER);

    if (big_boom)
        beam.explode(true, false);
    else
        beam.fire();

    return (true);
}

// returns true if the orb is gone
bool iood_act(monsters &mon, bool no_trail)
{
    ASSERT(mons_is_projectile(mon.type));

    float x = mon.props["iood_x"];
    float y = mon.props["iood_y"];
    float vx = mon.props["iood_vx"];
    float vy = mon.props["iood_vy"];

    dprf("iood_act: pos=(%d,%d) rpos=(%f,%f) v=(%f,%f) foe=%d",
         mon.pos().x, mon.pos().y,
         x, y, vx, vy, mon.foe);

    if (!vx && !vy) // not initialized
    {
        _iood_dissipate(mon);
        return (true);
    }

    coord_def target(-1, -1);
    if (mon.foe == MHITYOU)
        target = you.pos();
    else if (invalid_monster_index(mon.foe))
        ;
    else if (invalid_monster_type(menv[mon.foe].type))
    {
        // Our target is gone.  Since picking a new one would require
        // intelligence, the orb continues on a ballistic course.
        mon.foe = MHITNOT;
    }
    else
        target = menv[mon.foe].pos();

    _normalize(vx, vy);

    if (target != coord_def(-1, -1))
    {
        float dx = target.x - x;
        float dy = target.y - y;
        _normalize(dx, dy);

        // Special case:
        // Moving diagonally when the orb is just about to hit you
        //      2
        //    ->*1
        // (from 1 to 2) would be a guaranteed escape.  This may be
        // realistic (strafing!), but since the game has no non-cheesy
        // means of waiting a small fraction of a turn, we don't want it.
        const int old_t_pos = mon.props["iood_tpos"].get_short();
        if (old_t_pos && old_t_pos != (256 * target.x + target.y)
            && (coord_def(round(x), round(y)) - target).rdist() <= 1
            // ... but following an orb is ok.
            && _in_front(vx, vy, dx, dy, 1.5)) // ~97 degrees
        {
            vx = dx;
            vy = dy;
        }
        mon.props["iood_tpos"].get_short() = 256 * target.x + target.y;

        if (!_in_front(vx, vy, dx, dy, 0.5)) // ~29 degrees
        {
            float ax, ay;
            if (dy*vx < dx*vy)
                ax = vy, ay = -vx, dprf("iood: veering left");
            else
                ax = -vy, ay = vx, dprf("iood: veering right");
            vx += ax * 0.3;
            vy += ay * 0.3;
        }
        else
            dprf("iood: keeping course");

        _normalize(vx, vy);
        mon.props["iood_vx"] = vx;
        mon.props["iood_vy"] = vy;
    }

reflected:

    x += vx;
    y += vy;

    mon.props["iood_x"] = x;
    mon.props["iood_y"] = y;

    coord_def pos(round(x), round(y));
    if (!in_bounds(pos))
    {
        _iood_dissipate(mon);
        return (true);
    }

    if (mon.props["iood_kc"].get_byte() == KC_YOU
        && (you.pos() - pos).rdist() >= LOS_RADIUS)
    { 	// not actual vision, because of the smoke trail
        _iood_dissipate(mon);
        return (true);
    }

    if (pos == mon.pos())
        return (false);

    actor *victim = actor_at(pos);
    if (cell_is_solid(pos) || victim)
    {
        if (cell_is_solid(pos))
        {
            if (you.see_cell(pos))
                mprf("%s hits %s", mon.name(DESC_CAP_THE, true).c_str(),
                     feature_description(pos, false, DESC_NOCAP_A).c_str());
        }
        if (victim && mons_is_projectile(victim->id()))
        {
            if (mon.observable())
                mpr("The orbs collide in a blinding explosion!");
            else
                noisy(40, pos, "You hear a loud magical explosion!");
            monster_die((monsters*)victim, KILL_DISMISSED, NON_MONSTER);
            _iood_hit(mon, pos, true);
            return (true);
        }

        if (victim && _iood_shielded(mon, *victim))
        {
            item_def *shield = victim->shield();
            if (!shield_reflects(*shield))
            {
                if (victim->atype() == ACT_PLAYER)
                {
                    mprf("You block %s.", mon.name(DESC_NOCAP_THE, true).c_str());
                }
                else
                {
                    simple_monster_message((monsters*)victim, (" blocks "
                        + mon.name(DESC_NOCAP_THE, true) + ".").c_str());
                }
                victim->shield_block_succeeded(&mon);
                _iood_dissipate(mon);
                return (true);
            }

            if (victim->atype() == ACT_PLAYER)
            {
                mprf("Your %s reflects %s!",
                    shield->name(DESC_PLAIN).c_str(),
                    mon.name(DESC_NOCAP_THE, true).c_str());
                ident_reflector(shield);
            }
            else if (you.see_cell(pos))
            {
                if (victim->observable())
                {
                    mprf("%s reflects %s with %s %s!",
                        victim->name(DESC_CAP_THE, true).c_str(),
                        mon.name(DESC_NOCAP_THE, true).c_str(),
                        mon.pronoun(PRONOUN_NOCAP_POSSESSIVE).c_str(),
                        shield->name(DESC_PLAIN).c_str());
                    ident_reflector(shield);
                }
                else
                {
                    mprf("%s bounces off thin air!",
                        mon.name(DESC_CAP_THE, true).c_str());
                }
            }
            victim->shield_block_succeeded(&mon);

            mon.props["iood_vx"] = vx = -vx;
            mon.props["iood_vy"] = vy = -vy;

            // Need to get out of the victim's square.

            // If you're next to the caster and both of you wear shields of
            // reflection, this can lead to a brief game of ping-pong, but
            // rapidly increasing shield penalties will make it short.
            goto reflected;
        }

        if (_iood_hit(mon, pos))
            return (true);
    }

    if (!no_trail)
    {
        place_cloud(CLOUD_MAGIC_TRAIL, mon.pos(),
                    2 + random2(3), mon.kill_alignment(),
                    KILL_MON_MISSILE);
    }

    if (!mon.move_to_pos(pos))
    {
        _iood_dissipate(mon);
        return (true);
    }

    return (false);
}