root/games/rogue/throw.c
/*      $NetBSD: throw.c,v 1.12 2011/05/23 23:01:17 joerg Exp $ */

/*
 * Copyright (c) 1988, 1993
 *      The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Timothy C. Stoehr.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)throw.c     8.1 (Berkeley) 5/31/93";
#else
__RCSID("$NetBSD: throw.c,v 1.12 2011/05/23 23:01:17 joerg Exp $");
#endif
#endif /* not lint */

/*
 * throw.c
 *
 * This source herein may be modified and/or distributed by anybody who
 * so desires, with the following restrictions:
 *    1.)  No portion of this notice shall be removed.
 *    2.)  Credit shall not be taken for the creation of this source.
 *    3.)  This code is not to be traded, sold, or used for personal
 *         gain or profit.
 *
 */

#include "rogue.h"

static void flop_weapon(object *, short, short);
static object *get_thrown_at_monster(object *, short, short *, short *);
static boolean throw_at_monster(object *, object *);

void
throw(void)
{
        short wch, d;
        boolean first_miss = 1;
        object *weapon;
        short dir, row, col;
        object *monster;

        while (!is_direction(dir = rgetchar(), &d)) {
                sound_bell();
                if (first_miss) {
                        messagef(0, "direction? ");
                        first_miss = 0;
                }
        }
        check_message();
        if (dir == CANCEL) {
                return;
        }
        if ((wch = pack_letter("throw what?", WEAPON)) == CANCEL) {
                return;
        }
        check_message();

        if (!(weapon = get_letter_object(wch))) {
                messagef(0, "no such item.");
                return;
        }
        if ((weapon->in_use_flags & BEING_USED) && weapon->is_cursed) {
                messagef(0, "%s", curse_message);
                return;
        }
        row = rogue.row; col = rogue.col;

        if ((weapon->in_use_flags & BEING_WIELDED) && (weapon->quantity <= 1)) {
                unwield(rogue.weapon);
        } else if (weapon->in_use_flags & BEING_WORN) {
                mv_aquatars();
                unwear(rogue.armor);
                print_stats(STAT_ARMOR);
        } else if (weapon->in_use_flags & ON_EITHER_HAND) {
                un_put_on(weapon);
        }
        monster = get_thrown_at_monster(weapon, d, &row, &col);
        mvaddch(rogue.row, rogue.col, rogue.fchar);
        refresh();

        if (rogue_can_see(row, col) && ((row != rogue.row) || (col != rogue.col))){
                mvaddch(row, col, get_dungeon_char(row, col));
        }
        if (monster) {
                wake_up(monster);
                check_gold_seeker(monster);

                if (!throw_at_monster(monster, weapon)) {
                        flop_weapon(weapon, row, col);
                }
        } else {
                flop_weapon(weapon, row, col);
        }
        vanish(weapon, 1, &rogue.pack);
}

boolean
throw_at_monster(object *monster, object *weapon)
{
        short damage, hit_chance;
        short t;

        hit_chance = get_hit_chance(weapon);
        damage = get_weapon_damage(weapon);
        if ((weapon->which_kind == ARROW) &&
                (rogue.weapon && (rogue.weapon->which_kind == BOW))) {
                damage += get_weapon_damage(rogue.weapon);
                damage = ((damage * 2) / 3);
                hit_chance += (hit_chance / 3);
        } else if ((weapon->in_use_flags & BEING_WIELDED) &&
                ((weapon->which_kind == DAGGER) ||
                (weapon->which_kind == SHURIKEN) ||
                (weapon->which_kind == DART))) {
                damage = ((damage * 3) / 2);
                hit_chance += (hit_chance / 3);
        }
        t = weapon->quantity;
        weapon->quantity = 1;
        snprintf(hit_message, HIT_MESSAGE_SIZE, "the %s", name_of(weapon));
        weapon->quantity = t;

        if (!rand_percent(hit_chance)) {
                (void)strlcat(hit_message, "misses  ", HIT_MESSAGE_SIZE);
                return(0);
        }
        s_con_mon(monster);
        (void)strlcat(hit_message, "hit  ", HIT_MESSAGE_SIZE);
        (void)mon_damage(monster, damage);
        return(1);
}

object *
get_thrown_at_monster(object *obj, short dir, short *row, short *col)
{
        short orow, ocol;
        short i, ch;

        orow = *row; ocol = *col;

        ch = get_mask_char(obj->what_is);

        for (i = 0; i < 24; i++) {
                get_dir_rc(dir, row, col, 0);
                if (    (((*col <= 0) || (*col >= DCOLS-1)) ||
                                (dungeon[*row][*col] == NOTHING)) ||
                                ((dungeon[*row][*col] & (HORWALL | VERTWALL | HIDDEN)) &&
                                        (!(dungeon[*row][*col] & TRAP)))) {
                        *row = orow;
                        *col = ocol;
                        return(0);
                }
                if ((i != 0) && rogue_can_see(orow, ocol)) {
                        mvaddch(orow, ocol, get_dungeon_char(orow, ocol));
                }
                if (rogue_can_see(*row, *col)) {
                        if (!(dungeon[*row][*col] & MONSTER)) {
                                mvaddch(*row, *col, ch);
                        }
                        refresh();
                }
                orow = *row; ocol = *col;
                if (dungeon[*row][*col] & MONSTER) {
                        if (!imitating(*row, *col)) {
                                return(object_at(&level_monsters, *row, *col));
                        }
                }
                if (dungeon[*row][*col] & TUNNEL) {
                        i += 2;
                }
        }
        return(0);
}

void
flop_weapon(object *weapon, short row, short col)
{
        object *new_weapon, *monster;
        short i = 0;
        boolean found = 0;
        short mch, dch;
        unsigned short mon;

        if ((row < 0) || (row >= DROWS) || (col < 0) || (col >= DCOLS))
                clean_up("flop_weapon:  weapon landed outside of dungeon");

        while ((i < 9) && dungeon[row][col] & ~(FLOOR | TUNNEL | DOOR | MONSTER)) {
                rand_around(i++, &row, &col);
                if ((row > (DROWS-2)) || (row < MIN_ROW) ||
                        (col > (DCOLS-1)) || (col < 0) || (!dungeon[row][col]) ||
                        (dungeon[row][col] & ~(FLOOR | TUNNEL | DOOR | MONSTER))) {
                        continue;
                }
                found = 1;
                break;
        }

        if (found || (i == 0)) {
                new_weapon = alloc_object();
                *new_weapon = *weapon;
                new_weapon->in_use_flags = NOT_USED;
                new_weapon->quantity = 1;
                new_weapon->ichar = 'L';
                place_at(new_weapon, row, col);
                if (rogue_can_see(row, col) &&
                                ((row != rogue.row) || (col != rogue.col))) {
                        mon = dungeon[row][col] & MONSTER;
                        dungeon[row][col] &= (~MONSTER);
                        dch = get_dungeon_char(row, col);
                        if (mon) {
                                mch = mvinch(row, col);
                                if ((monster = object_at(&level_monsters,
                                    row, col)) != NULL) {
                                        monster->trail_char = dch;
                                }
                                if ((mch < 'A') || (mch > 'Z')) {
                                        mvaddch(row, col, dch);
                                }
                        } else {
                                mvaddch(row, col, dch);
                        }
                        dungeon[row][col] |= mon;
                }
        } else {
                short t;

                t = weapon->quantity;
                weapon->quantity = 1;
                messagef(0, "the %svanishes as it hits the ground",
                        name_of(weapon));
                weapon->quantity = t;
        }
}

void
rand_around(short i, short *r, short *c)
{
        static char pos[] = "\010\007\001\003\004\005\002\006\0";
        static short row, col;
        short j;

        if (i == 0) {
                short x, y, o, t;

                row = *r;
                col = *c;

                o = get_rand(1, 8);

                for (j = 0; j < 5; j++) {
                        x = get_rand(0, 8);
                        y = (x + o) % 9;
                        t = pos[x];
                        pos[x] = pos[y];
                        pos[y] = t;
                }
        }
        switch((short)pos[i]) {
        case 0:
                *r = row + 1;
                *c = col + 1;
                break;
        case 1:
                *r = row + 1;
                *c = col - 1;
                break;
        case 2:
                *r = row - 1;
                *c = col + 1;
                break;
        case 3:
                *r = row - 1;
                *c = col - 1;
                break;
        case 4:
                *r = row;
                *c = col + 1;
                break;
        case 5:
                *r = row + 1;
                *c = col;
                break;
        case 6:
                *r = row;
                *c = col;
                break;
        case 7:
                *r = row - 1;
                *c = col;
                break;
        case 8:
                *r = row;
                *c = col - 1;
                break;
        }
}