server-1.12/types/common/projectile.c

239 lines
8.6 KiB
C

/*
CrossFire, A Multiplayer game for X-windows
Copyright (C) 2007 Mark Wedel & Crossfire Development Team
Copyright (C) 1992 Frank Tore Johansen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
The authors can be reached via e-mail at crossfire-devel@real-time.com
*/
/** @file projectile.c
* This file contains code common to projectile objects. For now it is limited
* to arrows and thrown objects.
*/
#include <global.h>
#include <sproto.h>
#include <ob_methods.h>
#include <ob_types.h>
/**
* Handle an arrow or thrown object stopping.
* @param op The arrow or thrown object that is stopping.
*/
void stop_projectile(object *op) {
/* Lauwenmark: Handle for plugin stop event */
execute_event(op, EVENT_STOP, NULL, NULL, NULL, SCRIPT_FIX_NOTHING);
if (op->inv) {
object *payload = op->inv;
remove_ob(payload);
clear_owner(payload);
insert_ob_in_map(payload, op->map, payload, 0);
remove_ob(op);
free_object(op);
} else {
op = fix_stopped_arrow(op);
if (op)
merge_ob(op, NULL);
}
}
/**
* Move an arrow or thrown object along its course.
* @param context The method context
* @param op The arrow or thrown object being moved.
* @todo Split this function up.
* @return METHOD_ERROR if op is not in a map, otherwise METHOD_OK
*/
method_ret common_process_projectile(ob_methods *context, object *op) {
object *tmp;
sint16 new_x, new_y;
int was_reflected, mflags;
mapstruct *m;
if (op->map == NULL) {
LOG(llevError, "BUG: Projectile had no map.\n");
remove_ob(op);
free_object(op);
return METHOD_ERROR;
}
/* Calculate target map square */
new_x = op->x+DIRX(op);
new_y = op->y+DIRY(op);
was_reflected = 0;
m = op->map;
mflags = get_map_flags(m, &m, new_x, new_y, &new_x, &new_y);
if (mflags&P_OUT_OF_MAP) {
stop_projectile(op);
return METHOD_OK;
}
/* only need to look for living creatures if this flag is set */
if (mflags&P_IS_ALIVE) {
for (tmp = GET_MAP_OB(m, new_x, new_y); tmp != NULL; tmp = tmp->above)
if (QUERY_FLAG(tmp, FLAG_ALIVE))
break;
/* Not really fair, but don't let monsters hit themselves with
* their own arrow - this can be because they fire it then
* move into it.
*/
if (tmp != NULL && tmp != op->owner) {
/* Found living object, but it is reflecting the missile. Update
* as below. (Note that for living creatures there is a small
* chance that reflect_missile fails.)
*/
if (QUERY_FLAG(tmp, FLAG_REFL_MISSILE)
&& (rndm(0, 99)) < (90-op->level/10)) {
int number = op->face->number;
op->direction = absdir(op->direction+4);
op->state = 0;
if (GET_ANIM_ID(op)) {
number += 4;
if (number > GET_ANIMATION(op, 8))
number -= 8;
op->face = &new_faces[number];
}
was_reflected = 1; /* skip normal movement calculations */
} else {
/* Attack the object. */
op = hit_with_arrow(op, tmp);
if (op == NULL)
return METHOD_OK;
}
} /* if this is not hitting its owner */
} /* if there is something alive on this space */
if (OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, new_x, new_y))) {
int retry = 0;
/* if the object doesn't reflect, stop the arrow from moving
* note that this code will now catch cases where a monster is
* on a wall but has reflecting - the arrow won't reflect.
* Mapmakers shouldn't put monsters on top of wall in the first
* place, so I don't consider that a problem.
*/
if (!QUERY_FLAG(op, FLAG_REFLECTING) || !(rndm(0, 19))) {
stop_projectile(op);
return METHOD_OK;
} else {
/* If one of the major directions (n,s,e,w), just reverse it */
if (op->direction&1) {
op->direction = absdir(op->direction+4);
retry = 1;
}
/* There were two blocks with identical code -
* use this retry here to make this one block
* that did the same thing.
*/
while (retry < 2) {
int left, right, mflags;
mapstruct *m1;
sint16 x1, y1;
retry++;
/* Need to check for P_OUT_OF_MAP: if the arrow is tavelling
* over a corner in a tiled map, it is possible that
* op->direction is within an adjacent map but either
* op->direction-1 or op->direction+1 does not exist.
*/
mflags = get_map_flags(op->map, &m1, op->x+freearr_x[absdir(op->direction-1)], op->y+freearr_y[absdir(op->direction-1)], &x1, &y1);
left = (mflags&P_OUT_OF_MAP) ? 0 : OB_TYPE_MOVE_BLOCK(op, (GET_MAP_MOVE_BLOCK(m1, x1, y1)));
mflags = get_map_flags(op->map, &m1, op->x+freearr_x[absdir(op->direction+1)], op->y+freearr_y[absdir(op->direction+1)], &x1, &y1);
right = (mflags&P_OUT_OF_MAP) ? 0 : OB_TYPE_MOVE_BLOCK(op, (GET_MAP_MOVE_BLOCK(m1, x1, y1)));
if (left == right)
op->direction = absdir(op->direction+4);
else if (left)
op->direction = absdir(op->direction+2);
else if (right)
op->direction = absdir(op->direction-2);
mflags = get_map_flags(op->map, &m1, op->x+DIRX(op), op->y+DIRY(op), &x1, &y1);
/* If this space is not out of the map and not blocked, valid space -
* don't need to retry again.
*/
if (!(mflags&P_OUT_OF_MAP)
&& !OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m1, x1, y1)))
break;
}
/* Couldn't find a direction to move the arrow to - just
* top it from moving.
*/
if (retry == 2) {
stop_projectile(op);
return METHOD_OK;
}
/* update object image for new facing */
/* many thrown objects *don't *have more than one face */
if (GET_ANIM_ID(op))
SET_ANIMATION(op, op->direction);
} /* object is reflected */
} /* object ran into a wall */
/* Move the arrow. */
remove_ob(op);
op->x = new_x;
op->y = new_y;
/* decrease the speed as it flies. 0.05 means a standard bow will shoot
* about 17 squares. Tune as needed.
*/
op->speed -= 0.05;
insert_ob_in_map(op, m, op, 0);
return METHOD_OK;
}
/**
* Move on this Thrown Object object.
* @param context The method context
* @param trap The thrown object or arrow we're moving on
* @param victim The object moving over this one
* @param originator The object that caused the move_on event
* @return METHOD_OK
*/
method_ret common_projectile_move_on(ob_methods *context, object *trap, object *victim, object *originator) {
if (common_pre_ob_move_on(trap, victim, originator) == METHOD_ERROR)
return METHOD_OK;
if (trap->inv == NULL) {
common_post_ob_move_on(trap, victim, originator);
return METHOD_OK;
}
/* bad bug: monster throw a object, make a step forwards, step on object ,
* trigger this here and get hit by own missile - and will be own enemy.
* Victim then is his own enemy and will start to kill herself (this is
* removed) but we have not synced victim and his missile. To avoid senseless
* action, we avoid hits here
*/
if ((QUERY_FLAG(victim, FLAG_ALIVE) && trap->speed)
&& trap->owner != victim)
hit_with_arrow(trap, victim);
common_post_ob_move_on(trap, victim, originator);
return METHOD_OK;
}