Crossfire Server, Trunk  R20513
spellbook.c
Go to the documentation of this file.
1 /*
2  CrossFire, A Multiplayer game for X-windows
3 
4  Copyright (C) 2007 Crossfire Development Team
5  Copyright (C) 1992 Frank Tore Johansen
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21  The authors can be reached via e-mail at crossfire-devel@real-time.com
22 */
23 
28 #include <global.h>
29 #include <ob_methods.h>
30 #include <ob_types.h>
31 #include <sounds.h>
32 #include <sproto.h>
33 
34 #include "living.h"
35 
36 static method_ret spellbook_type_apply(ob_methods *context, object *lighter, object *applier, int aflags);
37 
41 void init_type_spellbook(void) {
43 }
44 
66 static method_ret spellbook_type_apply(ob_methods *context, object *book, object *applier, int aflags) {
67  object *skapplier, *spell, *spell_skill;
68  char level[100];
69 
70  /* Must be applied by a player. */
71  if (applier->type == PLAYER) {
72  if (QUERY_FLAG(applier, FLAG_BLIND) && !QUERY_FLAG(applier, FLAG_WIZ)) {
74  "You are unable to read while blind.");
75  return METHOD_OK;
76  }
77 
78  spell = book->inv;
79  if (!spell) {
80  LOG(llevError, "apply_spellbook: Book %s has no spell in it!\n", book->name);
82  "The spellbook symbols make no sense.");
83  return METHOD_OK;
84  }
85 
86  if (QUERY_FLAG(book, FLAG_CURSED) || QUERY_FLAG(book, FLAG_DAMNED)) {
87  char name[MAX_BUF];
88  /* Player made a mistake, let's shake her/him :) */
89  int failure = -35;
90 
92  failure = -rndm(35, 100);
93  query_name(book, name, MAX_BUF);
95  "The %s was %s!",
96  name, QUERY_FLAG(book, FLAG_DAMNED) ? "damned" : "cursed");
97  scroll_failure(applier, failure, (spell->level+4)*7);
98  if (QUERY_FLAG(book, FLAG_DAMNED)
99  && check_spell_known(applier, spell->name)
100  && die_roll(1, 10, applier, 1) < 2)
101  /* Really unlucky player, better luck next time */
102  do_forget_spell(applier, spell->name);
103  book = object_decrease_nrof_by_one(book);
104  if (book && (!QUERY_FLAG(book, FLAG_IDENTIFIED))) {
105  /* Well, not everything is lost, player now knows the
106  * book is cursed/damned. */
107  book = identify(book);
108  if (book->env)
109  esrv_update_item(UPD_FLAGS|UPD_NAME, applier, book);
110  else
111  applier->contr->socket.update_look = 1;
112  }
113  return METHOD_OK;
114  }
115 
116  /* This section moved before literacy check */
117  if (check_spell_known(applier, spell->name)) {
118  // If we already know the spell, it makes sense we know what the spell is.
119  if (book && (!QUERY_FLAG(book, FLAG_IDENTIFIED))) {
120  book = identify(book);
121  if (book->env)
122  esrv_update_item(UPD_FLAGS|UPD_NAME, applier, book);
123  else
124  applier->contr->socket.update_look = 1;
125  }
126  draw_ext_info_format(NDI_UNIQUE, 0, applier,
128  "You already know the spell %s.\n", spell->name);
129  return METHOD_OK;
130  }
131  /* check they have the right skills to learn the spell in the first place */
132  if (spell->skill) {
133  spell_skill = find_skill_by_name(applier, spell->skill);
134  if (!spell_skill) {
135  draw_ext_info_format(NDI_UNIQUE, 0, applier,
137  "You lack the skill %s to use this spell",
138  spell->skill);
139  return METHOD_OK;
140  }
141 
142  int skill_lev_diff = spell->level - spell_skill->level;
143  if (skill_lev_diff > 0) {
144  if (skill_lev_diff < 2)
146  "The spell described in this book is just beyond your skill in %s.", spell->skill);
147  else if (skill_lev_diff < 3)
149  "The spell described in this book is slightly beyond your skill in %s.", spell->skill);
150  else if (skill_lev_diff < 5)
152  "The spell described in this book is beyond your skill in %s.", spell->skill);
153  else if (skill_lev_diff < 8)
155  "The spell described in this book is quite a bit beyond your skill in %s.", spell->skill);
156  else if (skill_lev_diff < 15)
158  "The spell described in this book is way beyond your skill in %s.", spell->skill);
159  else
161  "The spell described in this book is totally beyond your skill in %s.", spell->skill);
162  return METHOD_OK;
163  }
164  }
165 
166  /* need a literacy skill to learn spells. Also, having a literacy level
167  * lower than the spell will make learning the spell more difficult */
168  skapplier = find_skill_by_name(applier, book->skill);
169  if (!skapplier) {
171  "You can't read! You will need this skill before you can comprehend the ideas written within.");
172  return METHOD_OK;
173  }
174 
175  /* We know the player has all the right skills so check how well they can read. */
176  int read_level;
177  read_level = skapplier->level;
178 
179  /* blessed books are easier to read */
180  if (QUERY_FLAG(book, FLAG_BLESSED))
181  read_level += 5;
182 
183  /* If the players read level is less than 10 levels lower than the spellbook, they can't read it */
184  int lev_diff;
185  lev_diff = spell->level - (read_level+10);
186  if (!QUERY_FLAG(applier, FLAG_WIZ) && lev_diff > 0) {
187  if (lev_diff < 2)
189  "You recognise most of the words but this book is just beyond your comprehension.");
190  else if (lev_diff < 3)
192  "You recognise many of the words but this book is slightly beyond your comprehension.");
193  else if (lev_diff < 5)
195  "You recognise some of the words but this book is slightly beyond your comprehension.");
196  else if (lev_diff < 8)
198  "You recognise some of the words but this book is beyond your comprehension.");
199  else if (lev_diff < 15)
201  "You recognise a few of the words but this book is beyond your comprehension.");
202  else
204  "You recognise a few of the words but this book is totally beyond your comprehension.");
205  return METHOD_OK;
206  }
207 
208  get_levelnumber(spell->level, level, sizeof(level));
210  "The spellbook contains the %s level spell %s.",
211  level, spell->name);
212 
213  if (!QUERY_FLAG(book, FLAG_IDENTIFIED)) {
214  book = identify(book);
215  if (book->env)
216  esrv_update_item(UPD_FLAGS|UPD_NAME, applier, book);
217  else
218  applier->contr->socket.update_look = 1;
219  spell = book->inv;
220  }
221 
222  /* Player has the right skills and enough skill to attempt to learn the spell with the logic as follows:
223  *
224  * 1- MU spells use Int to learn, Cleric spells use Wisdom
225  *
226  * 2- The learner's skill level in literacy adjusts the chance
227  * to learn a spell.
228  *
229  * 3 -Automatically fail to learn if you read while confused
230  *
231  * Overall, chances are the same but a player will find having a high
232  * literacy rate very useful! -b.t.
233  */
234  char desc[MAX_BUF];
235  const readable_message_type *msgType = get_readable_message_type(book);
236 
237  if (QUERY_FLAG(applier, FLAG_CONFUSED)) {
239  "In your confused state you flub the wording of the text!");
240  scroll_failure(applier, 0-random_roll(0, spell->level, applier, PREFER_LOW), MAX(spell->stats.sp, spell->stats.grace));
241  } else if (QUERY_FLAG(book, FLAG_STARTEQUIP)
242  || (random_roll(0, 100, applier, PREFER_LOW)-(5*read_level)) < get_learn_spell(spell->stats.grace ? applier->stats.Wis : applier->stats.Int)) {
244  "You succeed in learning the spell!");
245  if (spell->msg != NULL) {
246  draw_ext_info_format(NDI_UNIQUE|NDI_NAVY, 0, applier, msgType->message_type, msgType->message_subtype,
247  "You open the %s and start reading.\n%s",
248  ob_describe(book, applier, desc, sizeof(desc)), spell->msg);
249  }
250  do_learn_spell(applier, spell, 0);
251 
252  /* xp gain to literacy for spell learning */
253  if (!QUERY_FLAG(book, FLAG_STARTEQUIP))
254  change_exp(applier, calc_skill_exp(applier, book, skapplier), skapplier->skill, 0);
255  } else {
256  play_sound_player_only(applier->contr, SOUND_TYPE_SPELL, book, 0, "fumble");
258  "You fail to learn the spell.\n");
259  }
261  }
262  return METHOD_OK;
263 }
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Sends message to player(s).
Definition: main.c:315
Error, serious thing.
Definition: logger.h:11
int8_t Int
Definition: living.h:35
Sound-related defines.
#define FLAG_DAMNED
The object is very cursed.
Definition: define.h:318
int64_t calc_skill_exp(const object *who, const object *op, const object *skill)
Calculates amount of experience can be gained for successful use of a skill.
Definition: skill_util.c:658
object * check_spell_known(object *op, const char *name)
Checks to see if player knows the spell.
Definition: spell_util.c:435
Typedefs for ob_methods.
Definition: ob_methods.h:45
void do_forget_spell(object *op, const char *spell)
Erases spell from player&#39;s inventory.
Definition: apply.c:432
#define MSG_TYPE_APPLY_FAILURE
Apply OK, but no/bad result.
Definition: newclient.h:599
void get_levelnumber(int i, char *buf, size_t size)
Formats a level.
Definition: item.c:446
static method_ret spellbook_type_apply(ob_methods *context, object *lighter, object *applier, int aflags)
Applies a spellbook.
Definition: spellbook.c:66
void esrv_update_item(int flags, object *pl, object *op)
Updates object *op for player *pl.
Definition: main.c:342
See Spellbook.
Definition: object.h:203
socket_struct socket
Socket information for this player.
Definition: player.h:94
#define PREFER_LOW
Definition: define.h:600
#define FLAG_CONFUSED
Will also be unable to cast spells.
Definition: define.h:312
#define FLAG_BLESSED
Item has a blessing, opposite of cursed/damned.
Definition: define.h:378
uint8_t message_type
Message type to be sent to the client.
Definition: book.h:37
#define TRUE
Definition: compat.h:10
#define MAX(x, y)
Definition: compat.h:20
int16_t sp
Spell points.
Definition: living.h:41
Global type definitions and header inclusions.
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Sends message to player(s).
Definition: main.c:310
#define MSG_TYPE_APPLY
Applying objects.
Definition: newclient.h:384
#define NDI_NAVY
Definition: newclient.h:223
char method_ret
Define some standard return values for callbacks which don&#39;t need to return any other results...
Definition: ob_methods.h:14
int rndm(int min, int max)
Returns a number between min and max.
Definition: utils.c:161
uint32_t update_look
If true, we need to send the look window.
Definition: newserver.h:115
#define METHOD_OK
Definition: ob_methods.h:15
int8_t Wis
Definition: living.h:35
#define MSG_TYPE_APPLY_ERROR
Definition: newclient.h:596
#define FLAG_IDENTIFIED
Player knows full info about item.
Definition: define.h:261
int die_roll(int num, int size, const object *op, int goodbad)
Roll a number of dice (2d3, 4d6).
Definition: utils.c:121
const char * name
The name of the object, obviously...
Definition: object.h:311
struct obj * env
Pointer to the object which is the environment.
Definition: object.h:293
Struct to store the message_type and message_subtype for signs and books used by the player...
Definition: book.h:36
#define UPD_FLAGS
Definition: newclient.h:290
void do_learn_spell(object *op, object *spell, int special_prayer)
Actually makes op learn spell.
Definition: apply.c:391
void register_apply(int ob_type, apply_func method)
Registers the apply method for the given type.
Definition: ob_types.c:62
struct pl * contr
Pointer to the player which control this object.
Definition: object.h:276
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
#define FLAG_WIZ
Object has special privilegies.
Definition: define.h:231
void change_exp(object *op, int64_t exp, const char *skill_name, int flag)
Changes experience to a player/monster.
Definition: living.c:2076
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
char * ob_describe(const object *op, const object *observer, char *buf, size_t size)
Returns the description of an object, as seen by the given observer.
Definition: ob_methods.c:85
const char * skill
Name of the skill this object uses/grants.
Definition: object.h:321
Object type variables.
#define FLAG_CURSED
The object is cursed.
Definition: define.h:317
See Player.
Definition: object.h:107
#define FLAG_BLIND
If set, object cannot see (visually)
Definition: define.h:337
#define object_decrease_nrof_by_one(xyz)
Definition: compat.h:28
void play_sound_player_only(player *pl, int8_t sound_type, object *emitter, int dir, const char *action)
Plays a sound for specified player only.
Definition: sounds.c:51
int16_t grace
Grace.
Definition: living.h:43
living stats
Str, Con, Dex, etc.
Definition: object.h:368
uint8_t type
PLAYER, BULLET, etc.
Definition: object.h:338
struct Settings settings
Server settings.
Definition: init.c:40
void scroll_failure(object *op, int failure, int power)
op made some mistake with a scroll, this takes care of punishment.
Definition: apply.c:1509
#define UPD_NAME
Definition: newclient.h:293
#define MSG_TYPE_APPLY_SUCCESS
Was able to apply object.
Definition: newclient.h:598
const char * msg
If this is a book/sign/magic mouth/etc.
Definition: object.h:322
#define FLAG_STARTEQUIP
Object was given to player at start.
Definition: define.h:268
object * identify(object *op)
Identifies an item.
Definition: item.c:1437
struct obj * inv
Pointer to the first object in the inventory.
Definition: object.h:290
const readable_message_type * get_readable_message_type(object *readable)
Get the readable type for an object (hopefully book).
Definition: readable.c:2200
#define NDI_UNIQUE
Print immediately, don&#39;t buffer.
Definition: newclient.h:245
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.c:51
int get_learn_spell(int stat)
Definition: living.c:2259
uint8_t message_subtype
Message subtype to be sent to the client.
Definition: book.h:38
Structure containing object statistics.
void query_name(const object *op, char *buf, size_t size)
Describes an item.
Definition: item.c:625
int random_roll(int min, int max, const object *op, int goodbad)
Roll a random number between min and max.
Definition: utils.c:42
Object type functions and variables.
int16_t level
Level of creature or object.
Definition: object.h:351
uint8_t spell_failure_effects
Nasty backlash to spell failures.
Definition: global.h:267
#define SOUND_TYPE_SPELL
Definition: newclient.h:309
object * find_skill_by_name(object *who, const char *name)
This returns the skill pointer of the given name (the one that accumulates exp, has the level...
Definition: skill_util.c:213
void init_type_spellbook(void)
Initializer for the SPELLBOOK object type.
Definition: spellbook.c:41