Crossfire Server, Trunk
converter.cpp
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
19 #include "global.h"
20 
21 #include <string.h>
22 #include <stdlib.h>
23 
24 #include "ob_methods.h"
25 #include "ob_types.h"
26 #include "shop.h"
27 #include "sounds.h"
28 #include "sproto.h"
29 
30 /*
31  * convert_item() returns 1 if anything was converted, 0 if the item was not
32  * what the converter wants, -1 if the converter is broken.
33  */
34 #define CONV_FROM(xyz) xyz->slaying
35 #define CONV_TO(xyz) xyz->other_arch
36 #define CONV_NR(xyz) (unsigned char)xyz->stats.sp
37 #define CONV_LIMIT(xyz) ((xyz->stats.wc > 0) ? ((unsigned long)xyz->stats.wc) : INT32_MAX)
38 #define CONV_NEED(xyz) (unsigned long)xyz->stats.food
39 #define CONV_FROM_MATCH(xyz,_match) (CONV_FROM(xyz) == (_match) || ((strchr(CONV_FROM(xyz),'*')) && wildcard_match(CONV_FROM(xyz),(_match))))
40 
41 static int convert_item(object *item, object *converter);
42 
46 static int wildcard_match(const char *string,const char *candidate)
47 {
48  bool head=FALSE,tail=FALSE;
49  char *str;
50  char *m;
51  bool ret;
52 
53  if ( string[0]=='*' && string[1]==0 ) return 1; // Trivial match-all case
54  if ( string[0] == '*' ) head=TRUE;
55  if ( string[strlen(string)-1] == '*' ) tail=TRUE;
56  str=strdup(&string[head?1:0]);
57  if ( !str ) return FALSE;
58  if ( tail ) str[strlen(str)-1]=0;
59  /* 'str' is now the text to match without the wildcard */
60  ret=FALSE;
61  if ( head && tail ) {
62  m=strstr(str,candidate);
63  if ( m ) ret=TRUE;
64  } else if ( tail ) {
65  if ( strncmp(str,candidate,strlen(str)) == 0 ) ret=TRUE;
66  } else {
67  if ( strlen(candidate) >= strlen(str) &&
68  strcmp(&candidate[strlen(candidate)-strlen(str)],str) == 0 ) ret=TRUE;
69  }
70  free(str);
71  return ret;
72 }
73 
74 static method_ret converter_type_move_on(object *trap, object *victim, object *originator);
75 static method_ret converter_type_apply(object *converter, object *applier, int aflags);
76 
80 void init_type_converter(void) {
83 }
84 
96 static object *do_item_conversion(object *converter, object *item, int weight_rem, int *status) {
97  int nr = 0;
98  uint32_t price_in;
99  // item->weight holds the weight of the whole stack.
100  // Negative weight_diff means the output weighs less than the input, so it can be done indefinitely. Positive weight means that, if done from in-inventory, only enough conversions to make up the weight difference can be allowed.
101  // TODO: Handle in containers and the effective weight reduction that occurs there.
102  int32_t weight_diff = (CONV_NR(converter) ? CONV_NR(converter) : 1)*converter->other_arch->clone.weight - (CONV_NEED(converter) ? CONV_NEED(converter) : 1)*item->weight;
103  // -1 means we have no weight-imposed limit.
104  int32_t max_conv = weight_diff > 0 && weight_rem > 0 ? weight_rem / weight_diff : -1;
105  if (max_conv == 0) {
106  // TODO: Send a message to the player saying they cannot carry the converted item.
107  *status = 0;
108  return NULL;
109  }
110 
111  /* We make some assumptions - we assume if it takes money as it type,
112  * it wants some amount. We don't make change (ie, if something costs
113  * 3 gp and player drops a platinum, tough luck)
114  *
115  * We can also assert that changers that take "money" will not perform on apply events, due to added complexity.
116  */
117  if (!strcmp(CONV_FROM(converter), "money")) {
118  int cost;
119 
120  if (item->type != MONEY) {
121  *status = 0;
122  return NULL;
123  }
124 
125  nr = MIN(CONV_LIMIT(converter), (item->nrof*item->value)/CONV_NEED(converter));
126  if (!nr) {
127  *status = 0;
128  return NULL;
129  }
130 
131  cost = nr*CONV_NEED(converter)/item->value;
132  /* take into account rounding errors */
133  if (nr*CONV_NEED(converter)%item->value)
134  cost++;
136 
137  price_in = cost*item->value;
138  } else {
139  if (item->type == PLAYER
140  || !CONV_FROM_MATCH(converter,item->arch->name)
141  || (CONV_NEED(converter) && CONV_NEED(converter) > item->nrof)) {
142  *status = 0;
143  return NULL;
144  }
145 
146  /* silently burn unpaid items (only if they match what we want) */
147  if (QUERY_FLAG(item, FLAG_UNPAID)) {
150  item = create_archetype("burnout");
151  *status = 1;
152  return item;
153  }
154 
155  if (CONV_NEED(converter)) {
156  nr = MIN(CONV_LIMIT(converter), item->nrof/CONV_NEED(converter));
157  // If there is an inventory weight restriction, that supercedes the amount that could be created.
158  if (max_conv != -1)
159  nr = MIN(nr, max_conv);
160  object_decrease_nrof(item, nr*CONV_NEED(converter));
161  price_in = nr*CONV_NEED(converter)*item->value;
162  } else {
163  price_in = item->value;
166  }
167  }
168 
169  if (converter->inv != NULL) {
170  int i;
171  object *ob_to_copy;
172 
173  /* select random object from inventory to copy */
174  ob_to_copy = converter->inv;
175  i = 1;
176  FOR_BELOW_PREPARE(converter->inv, ob) {
177  if (rndm(0, i) == 0)
178  ob_to_copy = ob;
179  i++;
180  } FOR_BELOW_FINISH();
181  item = object_create_clone(ob_to_copy);
184  } else {
185  if (converter->other_arch == NULL) {
186  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);
187  *status = -1;
188  return NULL;
189  }
190  item = object_create_arch(converter->other_arch);
191  fix_generated_item(item, converter, 0, 0, GT_MINIMAL);
192  }
193 
194  if (CONV_NR(converter))
195  item->nrof = CONV_NR(converter);
196  if (nr)
197  item->nrof *= nr;
198  if (item->type != MONEY && shop_contains(converter))
200  else if (price_in < item->nrof*item->value && settings.allow_broken_converters == FALSE) {
201  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);
203  *status = -1;
204  return NULL;
205  }
206  // If we get here, the conversion was successful.
207  *status = 1;
208  return item;
209 }
210 
220 static int convert_item(object *item, object *converter) {
221  int status; // Return for the result status of good, bad, or otherwise.
222  item = do_item_conversion(converter, item, -1, &status);
223  if (status == 1)
224  object_insert_in_map_at(item, converter->map, converter, 0, converter->x, converter->y);
225  return status;
226 }
227 
235 static method_ret converter_type_move_on(object *trap, object *victim, object *originator) {
236  if (common_pre_ob_move_on(trap, victim, originator) == METHOD_ERROR)
237  return METHOD_OK;
238  if (convert_item(victim, trap) < 0) {
239  object *op;
240  char name[MAX_BUF];
241 
242  query_name(trap, name, MAX_BUF);
244  "The %s seems to be broken!", name);
245 
246  op = create_archetype("burnout");
247  if (op != NULL)
248  object_insert_in_map_at(op, trap->map, trap, 0, trap->x, trap->y);
249  }
250  common_post_ob_move_on(trap, victim, originator);
251  return METHOD_OK;
252 }
253 
265 static method_ret converter_type_apply(object *converter, object *applier, int aflags) {
266  (void)aflags; // Unused
267  object *conv_src = object_find_by_arch_name(applier, CONV_FROM(converter));
268  if (conv_src != NULL) {
269  int status;
270  // Handle player weight limits
271  int lim = applier->type == PLAYER ? get_weight_limit(applier->stats.Str) - applier->carrying - applier->weight : -1;
272  object *conv_res = do_item_conversion(converter, conv_src, lim, &status);
273  if (conv_res != NULL) {
274  // If the generated item is unpaid, then do not put it directly into the inventory. Otherwise, put it in the inventory.
275  if (QUERY_FLAG(conv_res, FLAG_UNPAID)) {
276  object_insert_in_map_at(conv_res, converter->map, converter, 0, converter->x, converter->y);
277  }
278  else {
279  object_insert_in_ob(conv_res, applier);
280  /* We really just need the weight/speed recalculation here,
281  * but this is what we can call without changing function
282  * access modifiers */
283  fix_object(applier);
284  }
285  }
286  }
287  return METHOD_OK;
288 }
PLAYER
@ PLAYER
Definition: object.h:112
global.h
settings
struct Settings settings
Definition: init.cpp:139
llevError
@ llevError
Definition: logger.h:11
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.cpp:58
Settings::allow_broken_converters
int allow_broken_converters
Definition: global.h:316
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
object::inv
object * inv
Definition: object.h:298
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
FALSE
#define FALSE
Definition: compat.h:14
register_apply
void register_apply(int ob_type, apply_func method)
Definition: ob_types.cpp:62
FOR_BELOW_PREPARE
#define FOR_BELOW_PREPARE(op_, it_)
Definition: define.h:704
METHOD_OK
#define METHOD_OK
Definition: ob_methods.h:15
if
if(!(yy_init))
Definition: loader.cpp:36428
object::x
int16_t x
Definition: object.h:335
object::map
struct mapstruct * map
Definition: object.h:305
guildjoin.ob
ob
Definition: guildjoin.py:42
register_move_on
void register_move_on(int ob_type, move_on_func method)
Definition: ob_types.cpp:89
draw_ext_info_format
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...) PRINTF_ARGS(6
fix_generated_item
void fix_generated_item(object *op, object *creator, int difficulty, int max_magic, int flags)
Definition: treasure.cpp:909
MIN
#define MIN(x, y)
Definition: compat.h:21
fix_object
void fix_object(object *op)
Definition: living.cpp:1125
converter_type_move_on
static method_ret converter_type_move_on(object *trap, object *victim, object *originator)
Definition: converter.cpp:235
rndm
int rndm(int min, int max)
Definition: utils.cpp:162
mapstruct::path
char path[HUGE_BUF]
Definition: map.h:353
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
Definition: object.cpp:2853
FOR_BELOW_FINISH
#define FOR_BELOW_FINISH()
Definition: define.h:711
object::carrying
int32_t carrying
Definition: object.h:377
FLAG_IS_A_TEMPLATE
#define FLAG_IS_A_TEMPLATE
Definition: define.h:366
object::y
int16_t y
Definition: object.h:335
m
static event_registration m
Definition: citylife.cpp:425
object_free_drop_inventory
void object_free_drop_inventory(object *ob)
Definition: object.cpp:1560
query_name
void query_name(const object *op, char *buf, size_t size)
Definition: item.cpp:588
CONV_NEED
#define CONV_NEED(xyz)
Definition: converter.cpp:38
archetype::clone
object clone
Definition: object.h:485
common_pre_ob_move_on
method_ret common_pre_ob_move_on(object *trap, object *victim, object *originator)
Definition: common_apply.cpp:35
make_face_from_files.str
str
Definition: make_face_from_files.py:30
CONV_FROM_MATCH
#define CONV_FROM_MATCH(xyz, _match)
Definition: converter.cpp:39
CONVERTER
@ CONVERTER
Definition: object.h:221
object::type
uint8_t type
Definition: object.h:348
object_create_arch
object * object_create_arch(archetype *at)
Definition: arch.cpp:298
CONV_NR
#define CONV_NR(xyz)
Definition: converter.cpp:36
sproto.h
nlohmann::detail::void
j template void())
Definition: json.hpp:4099
object_insert_in_map_at
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Definition: object.cpp:2100
object::other_arch
struct archetype * other_arch
Definition: object.h:423
object_create_clone
object * object_create_clone(object *asrc)
Definition: object.cpp:3905
do_item_conversion
static object * do_item_conversion(object *converter, object *item, int weight_rem, int *status)
Definition: converter.cpp:96
MAX_BUF
#define MAX_BUF
Definition: define.h:35
create_archetype
object * create_archetype(const char *name)
Definition: arch.cpp:278
get_weight_limit
uint32_t get_weight_limit(int stat)
Definition: living.cpp:2362
object::weight
int32_t weight
Definition: object.h:375
method_ret
char method_ret
Definition: ob_methods.h:14
ob_types.h
sounds.h
object_decrease_nrof
object * object_decrease_nrof(object *op, uint32_t i)
Definition: object.cpp:2672
NDI_UNIQUE
#define NDI_UNIQUE
Definition: newclient.h:251
object::name
sstring name
Definition: object.h:319
object_find_by_arch_name
object * object_find_by_arch_name(const object *who, const char *name)
Definition: object.cpp:4248
item
Definition: item.py:1
reputation.victim
victim
Definition: reputation.py:14
give.op
op
Definition: give.py:33
shop.h
object_unset_flag_inv
void object_unset_flag_inv(object *op, int flag)
Definition: object.cpp:3252
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:225
MSG_TYPE_APPLY_FAILURE
#define MSG_TYPE_APPLY_FAILURE
Definition: newclient.h:593
wildcard_match
static int wildcard_match(const char *string, const char *candidate)
Definition: converter.cpp:46
CONV_FROM
#define CONV_FROM(xyz)
Definition: converter.cpp:34
object_remove
void object_remove(object *op)
Definition: object.cpp:1833
FLAG_UNPAID
#define FLAG_UNPAID
Definition: define.h:236
init_type_converter
void init_type_converter(void)
Definition: converter.cpp:80
METHOD_ERROR
#define METHOD_ERROR
Definition: ob_methods.h:17
ob_methods.h
say.item
dictionary item
Definition: say.py:149
object::stats
living stats
Definition: object.h:378
diamondslots.cost
int cost
Definition: diamondslots.py:21
convert_item
static int convert_item(object *item, object *converter)
Definition: converter.cpp:220
shop_contains
bool shop_contains(object *ob)
Definition: shop.cpp:1296
TRUE
#define TRUE
Definition: compat.h:11
MSG_TYPE_APPLY
#define MSG_TYPE_APPLY
Definition: newclient.h:397
common_post_ob_move_on
void common_post_ob_move_on(object *trap, object *victim, object *originator)
Definition: common_apply.cpp:67
GT_MINIMAL
@ GT_MINIMAL
Definition: treasure.h:36
takeitem.status
status
Definition: takeitem.py:38
converter_type_apply
static method_ret converter_type_apply(object *converter, object *applier, int aflags)
Definition: converter.cpp:265
MONEY
@ MONEY
Definition: object.h:142
give.name
name
Definition: give.py:27
living::Str
int8_t Str
Definition: living.h:36
CONV_LIMIT
#define CONV_LIMIT(xyz)
Definition: converter.cpp:37