Crossfire Server, Trunk
lightable.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2021 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 
22 #include "global.h"
23 
24 #include "ob_methods.h"
25 #include "sproto.h"
26 
27 static method_ret lightable_type_apply(object *lightable, object *applier, int aflags);
28 
32 void init_type_lightable(void) {
34 }
35 
56 static uint8_t is_better_lighter(object *new, object *old) {
57  // If old_tool is not provided, then of course the new one is better.
58  if (!old)
59  return 1;
60  // If the old tool is a weapon, just don't even argue
61  if (old->type == WEAPON)
62  return 0;
63  // If the new tool is a weapon, and the old one is not, we will replace.
64  if (new->type == WEAPON)
65  return 1;
66  // If the old tool is a clawing or flame touch skill, we do not beat it.
67  // Only weapons have higher priority, and they have already been handled
68  if (old->type == SKILL && (old->subtype == SK_CLAWING || old->subtype == SK_FLAME_TOUCH))
69  return 0;
70  // But, if the new tool is one of these, then we will replace.
71  if (new->type == SKILL && (new->subtype == SK_CLAWING || old->subtype == SK_FLAME_TOUCH))
72  return 1;
73  // If old tool is a lighter and has higher or equal speed than a lighter as the new tool, then it is the better tool.
74  if (old->type == LIGHTER && (new->type != LIGHTER || old->speed >= new->speed))
75  return 0;
76  // And if the new tool has those same criteria, then it is better.
77  if (new->type == LIGHTER && (old->type != LIGHTER || new->speed > old->speed))
78  return 1;
79  // For spells, either take the lower mana cost between spells or supercede a skill or skill tool.
80  if (old->type == SPELL && (new->type != SPELL || old->stats.sp + old->stats.grace <= new->stats.sp + new->stats.grace))
81  return 0;
82  if (new->type == SPELL && (old->type != SPELL || new->stats.sp + new->stats.grace < old->stats.sp + old->stats.grace))
83  return 1;
84  // And, if the old tool is a skill or skill tool, then it is one of the magic ones that uses 10 mana. As with new_tool.
85  if (old->type == SKILL || old->type == SKILL_TOOL)
86  return 0;
87  // If we get here, something got messed up, so we want to ditch old_tool.
88  return 2;
89 }
90 
106 static method_ret lightable_type_apply(object *lightable, object *applier, int aflags){
107  // We will find our lighter tool with this.
108  // Note that we can use some things with fire attacktype that aren't lighters, because reasons.
109  // wannabe_tool is used as a kind of temporary storage for tools that may become the selected tool, but might not as well.
110  // unused_tool is a storage for when an item could be the tool of choice, but curses prevent something else from being unapplied.
111  object *tool = NULL, *wannabe_tool = find_marked_object(applier), *unused_tool = NULL;
112  int current_weapon_is_cursed = 0;
113  // First, use the marked item if it is a lighter or has fire attacktype.
114  if (wannabe_tool && ((wannabe_tool->attacktype & AT_FIRE) || wannabe_tool->type == LIGHTER)) {
115  tool = wannabe_tool;
116  }
117  // Next, let us look at the current weapon of the applier. If it has a fire attacktype, use that.
118  else if (applier->current_weapon && (applier->current_weapon->attacktype & AT_FIRE)) {
119  tool = applier->current_weapon;
120  }
121  else {
122  // We're done using wannabe_tool for the marked object. Clear it out.
123  wannabe_tool = NULL;
124  // Look for other ways to light the item.
125  // Loop through the inventory for a lighter, the pyromancy skill, or a fire spell.
126  FOR_INV_PREPARE(applier, tmp) {
127  switch (tmp->type) {
128  case LIGHTER:
129  // Skip over lighters that ran out of charges
130  if (tmp->stats.food > 0)
131  wannabe_tool = tmp;
132  break;
133  case SKILL:
134  case SKILL_TOOL:
135  // FIRE_MAGIC isn't used AFAIK, but account for it in case it ever is.
136  // Either way, we need 10 mana to use this directly.
137  if ((tmp->subtype == SK_PYROMANCY || tmp->subtype == SK_FIRE_MAGIC) && applier->stats.sp >= 10)
138  wannabe_tool = tmp;
139  // Clawing with fire attacktype and flame touch should also work.
140  if ((tmp->subtype == SK_CLAWING || tmp->subtype == SK_FLAME_TOUCH) && (tmp->attacktype & AT_FIRE))
141  wannabe_tool = tmp;
142  break;
143  case WEAPON:
144  if (tmp->attacktype & AT_FIRE) {
145  // Make sure our wielded weapon is not cursed or damned.
146  // If it is, we can't set it down to use this one for a sec.
147  if (!applier->current_weapon || !(QUERY_FLAG(applier->current_weapon, FLAG_CURSED) || QUERY_FLAG(applier->current_weapon, FLAG_DAMNED))) {
148  wannabe_tool = tmp;
149  }
150  else {
151  // Note that the equipped weapon is cursed. We will print a message at the end.
152  current_weapon_is_cursed = 1;
153  unused_tool = tmp;
154  }
155  }
156  break;
157  case SPELL:
158  // Don't choose a spell if it is too expensive to cast.
159  if (tmp->attacktype & AT_FIRE && tmp->stats.sp <= applier->stats.sp && tmp->stats.grace <= applier->stats.grace) {
160  wannabe_tool = tmp;
161  }
162  break;
163  }
164  // Determine the tool to use
165  //
166  // This prevents us from wasting mana or lighter charges if we have another option.
167  if (wannabe_tool && is_better_lighter(wannabe_tool, tool)) {
168  tool = wannabe_tool;
169  }
170  // Weapon has the shallowest cost, so if we found that, then use it.
171  if (tool && tool->type == WEAPON)
172  break;
173  } FOR_INV_FINISH();
174  }
175  // All searching is done. Time to see what we got.
176  if (tool) {
177  if (applier->type == PLAYER && current_weapon_is_cursed) {
178  // Print a message to players telling them they can't unequip current_weapon to wield something else.
179  draw_ext_info_format(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE, "Could not use %s on %s because your %s is cursed!",
180  unused_tool->name, lightable->name, applier->current_weapon->name);
181  }
182  // Actually attempt to thaw/light the lightable item.
183  // If we are using a lighter, then go all the way through the lighter apply code.
184  // This way we aren't having to rewrite any extra code here.
185  if (tool->type == LIGHTER) {
186  if (applier->type == PLAYER) {
187  // Store the marked item for safekeeping, then make the lightable be our marked object.
188  object *old_marked = find_marked_object(applier);
189  // Don't do anything special if old_marked is the same as lightable.
190  if (old_marked == lightable) {
191  // Just set to null so when we are done, we set the mark pointer to null.
192  old_marked = NULL;
193  }
194  else {
195  applier->contr->mark = lightable;
196  applier->contr->mark_count = lightable->count;
197  }
198  // Apply the lighter.
199  apply_manual(applier, tool, aflags);
200  // When done, return the marked object back to its original object.
201  applier->contr->mark = old_marked;
202  if (old_marked)
203  applier->contr->mark_count = old_marked->count;
204  else
205  applier->contr->mark_count = 0;
206  // Note we don't call do_light here -- it is called from the lighter apply
207  }
208  else {
209  // TODO: Implement directly, since monsters can't mark objects.
210  }
211  }
212  else {
213  do_light(lightable, tool->name, applier);
214  // We already checked for available mana, so we should be able to just deduct mana outright here and remain above zero.
215  if (tool->type == SPELL) {
216  // Make sure we handle both magic and prayer costs.
217  applier->stats.sp -= tool->stats.sp;
218  applier->stats.grace -= tool->stats.grace;
219  }
220  else if (tool->type == SKILL || tool->type == SKILL_TOOL) {
221  // Clawing and flame touch do not use mana
222  if (tool->subtype == SK_PYROMANCY || tool->subtype == SK_FIRE_MAGIC)
223  applier->stats.sp -= 10;
224  }
225  }
226  }
227  else if (applier->type == PLAYER) {
228  draw_ext_info_format(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE, "You have no source of heat to light %s.", lightable->name);
229  }
230 
231  // We're done looking,
232  return METHOD_OK;
233 }
PLAYER
@ PLAYER
Definition: object.h:107
global.h
SK_FIRE_MAGIC
@ SK_FIRE_MAGIC
Definition: skills.h:62
obj::count
tag_t count
Definition: object.h:302
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
SK_CLAWING
@ SK_CLAWING
Definition: skills.h:50
is_better_lighter
static uint8_t is_better_lighter(object *new, object *old)
Definition: lightable.c:56
METHOD_OK
#define METHOD_OK
Definition: ob_methods.h:15
WEAPON
@ WEAPON
Definition: object.h:119
SKILL
@ SKILL
Definition: object.h:143
Ice.tmp
int tmp
Definition: Ice.py:207
apply_manual
int apply_manual(object *op, object *tmp, int aflag)
Definition: apply.c:597
pl::mark
object * mark
Definition: player.h:212
obj::current_weapon
struct obj * current_weapon
Definition: object.h:375
obj::name
sstring name
Definition: object.h:314
lightable_type_apply
static method_ret lightable_type_apply(object *lightable, object *applier, int aflags)
Definition: lightable.c:106
LIGHTABLE
@ LIGHTABLE
Definition: object.h:250
FLAG_DAMNED
#define FLAG_DAMNED
Definition: define.h:317
FOR_INV_FINISH
#define FOR_INV_FINISH()
Definition: define.h:677
obj::speed
float speed
Definition: object.h:332
sproto.h
SK_FLAME_TOUCH
@ SK_FLAME_TOUCH
Definition: skills.h:37
register_apply
void register_apply(int ob_type, apply_func method)
Definition: ob_types.c:62
method_ret
char method_ret
Definition: ob_methods.h:14
do_light
void do_light(object *item, const char *lighter_name, object *applier)
Definition: light_object.c:36
obj::type
uint8_t type
Definition: object.h:343
NDI_UNIQUE
#define NDI_UNIQUE
Definition: newclient.h:262
obj::stats
living stats
Definition: object.h:373
obj::contr
struct pl * contr
Definition: object.h:279
liv::grace
int16_t grace
Definition: living.h:44
LIGHTER
@ LIGHTER
Definition: object.h:190
SKILL_TOOL
@ SKILL_TOOL
Definition: object.h:189
obj::subtype
uint8_t subtype
Definition: object.h:344
MSG_TYPE_APPLY_FAILURE
#define MSG_TYPE_APPLY_FAILURE
Definition: newclient.h:604
pl::mark_count
uint32_t mark_count
Definition: player.h:211
SK_PYROMANCY
@ SK_PYROMANCY
Definition: skills.h:53
init_type_lightable
void init_type_lightable(void)
Definition: lightable.c:32
ob_methods.h
obj::attacktype
uint32_t attacktype
Definition: object.h:347
SPELL
@ SPELL
Definition: object.h:214
liv::sp
int16_t sp
Definition: living.h:42
MSG_TYPE_APPLY
#define MSG_TYPE_APPLY
Definition: newclient.h:408
FLAG_CURSED
#define FLAG_CURSED
Definition: define.h:316
find_marked_object
object * find_marked_object(object *op)
Definition: c_object.c:1520
FOR_INV_PREPARE
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:670
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,...)
Definition: main.c:319
AT_FIRE
#define AT_FIRE
Definition: attack.h:78