/* 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 converter.c * The implementation of the Converter class of objects. */ #include #include #include #include #include /* * convert_item() returns 1 if anything was converted, 0 if the item was not * what the converter wants, -1 if the converter is broken. */ #define CONV_FROM(xyz) xyz->slaying #define CONV_TO(xyz) xyz->other_arch #define CONV_NR(xyz) (unsigned char)xyz->stats.sp #define CONV_NEED(xyz) (unsigned long)xyz->stats.food static int convert_item(object *item, object *converter); static method_ret converter_type_move_on(ob_methods *context, object *trap, object *victim, object *originator); /** * Initializer for the CONVERTER object type. */ void init_type_converter(void) { register_move_on(CONVERTER, converter_type_move_on); } /** * Transforms an item into another item. * @param item The object that triggered the converter - if it isn't of a type * accepted by the converter, nothing will happen * @param converter The object that is doing the conversion * @retval -1 If something went wrong when attempting the conversion * @retval 0 If the item was not converted * @retval 1 If the item got converted */ static int convert_item(object *item, object *converter) { int nr = 0; uint32 price_in; /* We make some assumptions - we assume if it takes money as it type, * it wants some amount. We don't make change (ie, if something costs * 3 gp and player drops a platinum, tough luck) */ if (!strcmp(CONV_FROM(converter), "money")) { int cost; if (item->type != MONEY) return 0; nr = (item->nrof*item->value)/CONV_NEED(converter); if (!nr) return 0; cost = nr*CONV_NEED(converter)/item->value; /* take into account rounding errors */ if (nr*CONV_NEED(converter)%item->value) cost++; decrease_ob_nr(item, cost); price_in = cost*item->value; } else { if (item->type == PLAYER || CONV_FROM(converter) != item->arch->name || (CONV_NEED(converter) && CONV_NEED(converter) > item->nrof)) return 0; /* silently burn unpaid items (only if they match what we want) */ if (QUERY_FLAG(item, FLAG_UNPAID)) { remove_ob(item); free_object(item); item = create_archetype("burnout"); if (item != NULL) insert_ob_in_map_at(item, converter->map, converter, 0, converter->x, converter->y); return 1; } if (CONV_NEED(converter)) { nr = item->nrof/CONV_NEED(converter); decrease_ob_nr(item, nr*CONV_NEED(converter)); price_in = nr*CONV_NEED(converter)*item->value; } else { price_in = item->value; remove_ob(item); free_object(item); } } if (converter->inv != NULL) { object *ob; int i; object *ob_to_copy; /* select random object from inventory to copy */ ob_to_copy = converter->inv; for (ob = converter->inv->below, i = 1; ob != NULL; ob = ob->below, i++) { if (rndm(0, i) == 0) ob_to_copy = ob; } item = object_create_clone(ob_to_copy); CLEAR_FLAG(item, FLAG_IS_A_TEMPLATE); unflag_inv(item, FLAG_IS_A_TEMPLATE); } else { if (converter->other_arch == NULL) { LOG(llevError, "move_creator: Converter doesn't have other arch set: %s (%s, %d, %d)\n", converter->name ? converter->name : "(null)", converter->map->path, converter->x, converter->y); return -1; } item = object_create_arch(converter->other_arch); fix_generated_item(item, converter, 0, 0, GT_MINIMAL); } if (CONV_NR(converter)) item->nrof = CONV_NR(converter); if (nr) item->nrof *= nr; if (is_in_shop(converter)) SET_FLAG(item, FLAG_UNPAID); else if (price_in < item->nrof*item->value && settings.allow_broken_converters == FALSE) { LOG(llevError, "Broken converter %s at %s (%d, %d) in value %d, out value %d for %s\n", converter->name, converter->map->path, converter->x, converter->y, price_in, item->nrof*item->value, item->name); free_object(item); return -1; } insert_ob_in_map_at(item, converter->map, converter, 0, converter->x, converter->y); return 1; } /** * Move on this Converter object. * @param context The method context * @param trap The Converter 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 */ static method_ret converter_type_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 (convert_item(victim, trap) < 0) { object *op; char name[MAX_BUF]; query_name(trap, name, MAX_BUF); draw_ext_info_format(NDI_UNIQUE, 0, originator, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE, "The %s seems to be broken!", "The %s seems to be broken!", name); op = create_archetype("burnout"); if (op != NULL) { op->x = trap->x; op->y = trap->y; insert_ob_in_map(op, trap->map, trap, 0); } } common_post_ob_move_on(trap, victim, originator); return METHOD_OK; }