Crossfire Server, Branch 1.12  R12190
projectile.c
Go to the documentation of this file.
00001 /*
00002     CrossFire, A Multiplayer game for X-windows
00003 
00004     Copyright (C) 2007 Mark Wedel & Crossfire Development Team
00005     Copyright (C) 1992 Frank Tore Johansen
00006 
00007     This program is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 2 of the License, or
00010     (at your option) any later version.
00011 
00012     This program is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program; if not, write to the Free Software
00019     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00020 
00021     The authors can be reached via e-mail at crossfire-devel@real-time.com
00022 */
00027 #include <global.h>
00028 #include <sproto.h>
00029 #include <ob_methods.h>
00030 #include <ob_types.h>
00031 
00036 void stop_projectile(object *op) {
00037     /* Lauwenmark: Handle for plugin stop event */
00038     execute_event(op, EVENT_STOP, NULL, NULL, NULL, SCRIPT_FIX_NOTHING);
00039     if (op->inv) {
00040         object *payload = op->inv;
00041 
00042         remove_ob(payload);
00043         clear_owner(payload);
00044         insert_ob_in_map(payload, op->map, payload, 0);
00045         remove_ob(op);
00046         free_object(op);
00047     } else {
00048         op = fix_stopped_arrow(op);
00049         if (op)
00050             merge_ob(op, NULL);
00051     }
00052 }
00053 
00062 method_ret common_process_projectile(ob_methods *context, object *op) {
00063     object *tmp;
00064     sint16 new_x, new_y;
00065     int was_reflected, mflags;
00066     mapstruct *m;
00067 
00068     if (op->map == NULL) {
00069         LOG(llevError, "BUG: Projectile had no map.\n");
00070         remove_ob(op);
00071         free_object(op);
00072         return METHOD_ERROR;
00073     }
00074 
00075     /* Calculate target map square */
00076     new_x = op->x+DIRX(op);
00077     new_y = op->y+DIRY(op);
00078     was_reflected = 0;
00079 
00080     m = op->map;
00081     mflags = get_map_flags(m, &m, new_x, new_y, &new_x, &new_y);
00082 
00083     if (mflags&P_OUT_OF_MAP) {
00084         stop_projectile(op);
00085         return METHOD_OK;
00086     }
00087 
00088     /* only need to look for living creatures if this flag is set */
00089     if (mflags&P_IS_ALIVE) {
00090         for (tmp = GET_MAP_OB(m, new_x, new_y); tmp != NULL; tmp = tmp->above)
00091              if (QUERY_FLAG(tmp, FLAG_ALIVE))
00092                  break;
00093 
00094 
00095         /* Not really fair, but don't let monsters hit themselves with
00096          * their own arrow - this can be because they fire it then
00097          * move into it.
00098          */
00099 
00100         if (tmp != NULL && tmp != op->owner) {
00101             /* Found living object, but it is reflecting the missile.  Update
00102              * as below. (Note that for living creatures there is a small
00103              * chance that reflect_missile fails.)
00104              */
00105 
00106             if (QUERY_FLAG(tmp, FLAG_REFL_MISSILE)
00107             && (rndm(0, 99)) < (90-op->level/10)) {
00108                 int number = op->face->number;
00109 
00110                 op->direction = absdir(op->direction+4);
00111                 op->state = 0;
00112                 if (GET_ANIM_ID(op)) {
00113                     number += 4;
00114                     if (number > GET_ANIMATION(op, 8))
00115                         number -= 8;
00116                     op->face = &new_faces[number];
00117                 }
00118                 was_reflected = 1;   /* skip normal movement calculations */
00119             } else {
00120                 /* Attack the object. */
00121                 op = hit_with_arrow(op, tmp);
00122                 if (op == NULL)
00123                     return METHOD_OK;
00124              }
00125         } /* if this is not hitting its owner */
00126     } /* if there is something alive on this space */
00127 
00128     if (OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, new_x, new_y))) {
00129         int retry = 0;
00130 
00131         /* if the object doesn't reflect, stop the arrow from moving
00132          * note that this code will now catch cases where a monster is
00133          * on a wall but has reflecting - the arrow won't reflect.
00134          * Mapmakers shouldn't put monsters on top of wall in the first
00135          * place, so I don't consider that a problem.
00136          */
00137         if (!QUERY_FLAG(op, FLAG_REFLECTING) || !(rndm(0, 19))) {
00138             stop_projectile(op);
00139             return METHOD_OK;
00140         } else {
00141             /* If one of the major directions (n,s,e,w), just reverse it */
00142             if (op->direction&1) {
00143                 op->direction = absdir(op->direction+4);
00144                 retry = 1;
00145             }
00146             /* There were two blocks with identical code -
00147              * use this retry here to make this one block
00148              * that did the same thing.
00149              */
00150             while (retry < 2) {
00151                 int left, right, mflags;
00152                 mapstruct *m1;
00153                 sint16  x1, y1;
00154 
00155                 retry++;
00156 
00157                 /* Need to check for P_OUT_OF_MAP: if the arrow is tavelling
00158                  * over a corner in a tiled map, it is possible that
00159                  * op->direction is within an adjacent map but either
00160                  * op->direction-1 or op->direction+1 does not exist.
00161                  */
00162                 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);
00163                 left = (mflags&P_OUT_OF_MAP) ? 0 : OB_TYPE_MOVE_BLOCK(op, (GET_MAP_MOVE_BLOCK(m1, x1, y1)));
00164 
00165                 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);
00166                 right = (mflags&P_OUT_OF_MAP) ? 0 : OB_TYPE_MOVE_BLOCK(op, (GET_MAP_MOVE_BLOCK(m1, x1, y1)));
00167 
00168                 if (left == right)
00169                     op->direction = absdir(op->direction+4);
00170                 else if (left)
00171                     op->direction = absdir(op->direction+2);
00172                 else if (right)
00173                     op->direction = absdir(op->direction-2);
00174 
00175                 mflags = get_map_flags(op->map, &m1, op->x+DIRX(op), op->y+DIRY(op), &x1, &y1);
00176 
00177                 /* If this space is not out of the map and not blocked, valid space -
00178                  * don't need to retry again.
00179                  */
00180                 if (!(mflags&P_OUT_OF_MAP)
00181                 && !OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m1, x1, y1)))
00182                     break;
00183             }
00184             /* Couldn't find a direction to move the arrow to - just
00185              * top it from moving.
00186              */
00187             if (retry == 2) {
00188                 stop_projectile(op);
00189                 return METHOD_OK;
00190             }
00191             /* update object image for new facing */
00192             /* many thrown objects *don't *have more than one face */
00193             if (GET_ANIM_ID(op))
00194                 SET_ANIMATION(op, op->direction);
00195         } /* object is reflected */
00196     } /* object ran into a wall */
00197 
00198     /* Move the arrow. */
00199     remove_ob(op);
00200     op->x = new_x;
00201     op->y = new_y;
00202 
00203     /* decrease the speed as it flies. 0.05 means a standard bow will shoot
00204      * about 17 squares. Tune as needed.
00205      */
00206     op->speed -= 0.05;
00207     insert_ob_in_map(op, m, op, 0);
00208     return METHOD_OK;
00209 }
00210 
00219 method_ret common_projectile_move_on(ob_methods *context, object *trap, object *victim, object *originator) {
00220     if (common_pre_ob_move_on(trap, victim, originator) == METHOD_ERROR)
00221         return METHOD_OK;
00222     if (trap->inv == NULL) {
00223         common_post_ob_move_on(trap, victim, originator);
00224         return METHOD_OK;
00225     }
00226 
00227     /* bad bug: monster throw a object, make a step forwards, step on object ,
00228      * trigger this here and get hit by own missile - and will be own enemy.
00229      * Victim then is his own enemy and will start to kill herself (this is
00230      * removed) but we have not synced victim and his missile. To avoid senseless
00231      * action, we avoid hits here
00232      */
00233     if ((QUERY_FLAG(victim, FLAG_ALIVE) && trap->speed)
00234     && trap->owner != victim)
00235         hit_with_arrow(trap, victim);
00236     common_post_ob_move_on(trap, victim, originator);
00237     return METHOD_OK;
00238 }