File: converter.c

package info (click to toggle)
crossfire 1.75.0-9
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 24,168 kB
  • sloc: ansic: 83,169; sh: 4,659; perl: 1,736; lex: 1,443; makefile: 1,199; python: 43
file content (206 lines) | stat: -rw-r--r-- 7,123 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
/*
 * Crossfire -- cooperative multi-player graphical RPG and adventure game
 *
 * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
 * Copyright (c) 1992 Frank Tore Johansen
 *
 * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
 * welcome to redistribute it under certain conditions. For details, please
 * see COPYING and LICENSE.
 *
 * The authors can be reached via e-mail at <crossfire@metalforge.org>.
 */

/**
 * @file
 * The implementation of the Converter class of objects.
 */
#include "global.h"

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

#include "ob_methods.h"
#include "ob_types.h"
#include "shop.h"
#include "sounds.h"
#include "sproto.h"

/*
 * 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
#define CONV_FROM_MATCH(xyz,_match) (CONV_FROM(xyz) == (_match) || (strchr(CONV_FROM(xyz),'*')) && wildcard_match(CONV_FROM(xyz),(_match)))

static int convert_item(object *item, object *converter);

/**
 * Wildcard match where 'string' contains a '*' at the start, end, or both
 */
static int wildcard_match(const char *string,const char *candidate)
{
   bool head=FALSE,tail=FALSE;
   char *str;
   char *m;
   bool ret;

   if ( string[0]=='*' && string[1]==0 ) return 1; // Trivial match-all case
   if ( string[0] == '*' ) head=TRUE;
   if ( string[strlen(string)-1] == '*' ) tail=TRUE;
   str=strdup(&string[head?1:0]);
   if ( !str ) return FALSE;
   if ( tail ) str[strlen(str)-1]=0;
   /* 'str' is now the text to match without the wildcard */
   ret=FALSE;
   if ( head && tail ) {
      m=strstr(str,candidate);
      if ( m ) ret=TRUE;
   } else if ( tail ) {
      if ( strncmp(str,candidate,strlen(str)) == 0 ) ret=TRUE;
   } else {
      if ( strlen(candidate) >= strlen(str) &&
           strcmp(&candidate[strlen(candidate)-strlen(str)],str) == 0 ) ret=TRUE;
   }
   free(str);
   return ret;
}

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_t 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++;
        object_decrease_nrof(item, cost);

        price_in = cost*item->value;
    } else {
        if (item->type == PLAYER
        || !CONV_FROM_MATCH(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)) {
            object_remove(item);
            object_free_drop_inventory(item);
            item = create_archetype("burnout");
            if (item != NULL)
                object_insert_in_map_at(item, converter->map, converter, 0, converter->x, converter->y);
            return 1;
        }

        if (CONV_NEED(converter)) {
            nr = item->nrof/CONV_NEED(converter);
            object_decrease_nrof(item, nr*CONV_NEED(converter));
            price_in = nr*CONV_NEED(converter)*item->value;
        } else {
            price_in = item->value;
            object_remove(item);
            object_free_drop_inventory(item);
        }
    }

    if (converter->inv != NULL) {
        int i;
        object *ob_to_copy;

        /* select random object from inventory to copy */
        ob_to_copy = converter->inv;
        i = 1;
        FOR_BELOW_PREPARE(converter->inv, ob) {
            if (rndm(0, i) == 0)
                ob_to_copy = ob;
            i++;
        } FOR_BELOW_FINISH();
        item = object_create_clone(ob_to_copy);
        CLEAR_FLAG(item, FLAG_IS_A_TEMPLATE);
        object_unset_flag_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 (item->type != MONEY && shop_contains(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);
        object_free_drop_inventory(item);
        return -1;
    }
    object_insert_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!", name);

        op = create_archetype("burnout");
        if (op != NULL)
            object_insert_in_map_at(op, trap->map, trap, 0, trap->x, trap->y);
    }
    common_post_ob_move_on(trap, victim, originator);
    return METHOD_OK;
}