Atrinik Server  4.0
disease.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
36 #include <global.h>
37 #include <arch.h>
38 #include <exp.h>
39 #include <object.h>
40 #include <object_methods.h>
41 #include <disease.h>
42 
53 static bool
54 disease_is_susceptible (object *op, object *victim)
55 {
56  HARD_ASSERT(op != NULL);
57  HARD_ASSERT(victim != NULL);
58 
59  if (!IS_LIVE(victim)) {
60  return false;
61  }
62 
63  if (!QUERY_FLAG(victim, FLAG_UNDEAD) && strstr(op->race, "*") != NULL) {
64  return true;
65  }
66 
67  if (QUERY_FLAG(victim, FLAG_UNDEAD) && strstr(op->race, "undead") != NULL) {
68  return true;
69  }
70 
71  if ((victim->race != NULL && strstr(op->race, victim->race) != NULL) ||
72  strstr(op->race, victim->name) != NULL) {
73  return true;
74  }
75 
76  return false;
77 }
78 
87 static object *
89 {
90  HARD_ASSERT(op != NULL);
91 
92  FOR_INV_PREPARE(op->env, tmp) {
93  if (tmp->type == SYMPTOM && strcmp(tmp->name, op->name) == 0) {
94  return tmp;
95  }
96  } FOR_INV_FINISH();
97 
98  return NULL;
99 }
100 
107 static void
109 {
110  HARD_ASSERT(op != NULL);
111 
112  object *symptom = disease_find_symptom(op);
113  if (symptom != NULL) {
114  object_destruct(symptom);
115  }
116 }
117 
124 static void
126 {
127  HARD_ASSERT(op != NULL);
128 
129  int x, y;
130  mapstruct *map;
131  if (op->env != NULL) {
132  x = op->env->x;
133  y = op->env->y;
134  map = op->env->map;
135  } else {
136  x = op->x;
137  y = op->y;
138  map = op->map;
139  }
140 
141  if (map == NULL) {
142  return;
143  }
144 
145  int range = abs(op->magic);
146  for (int i = -range; i <= range; i++) {
147  for (int j = -range; j <= range; j++) {
148  int xt = x + i;
149  int yt = y + j;
150  mapstruct *m = get_map_from_coord(map, &xt, &yt);
151  if (m == NULL) {
152  continue;
153  }
154 
155  if (!(GET_MAP_FLAGS(m, xt, yt) & (P_IS_MONSTER | P_IS_PLAYER))) {
156  continue;
157  }
158 
159  FOR_MAP_PREPARE(m, xt, yt, tmp) {
160  if (!IS_LIVE(tmp)) {
161  continue;
162  }
163 
164  rv_vector rv;
165  if (!get_rangevector(op->env ? op->env : op, tmp, &rv, 0) ||
166  !obj_in_line_of_sight(tmp, &rv)) {
167  continue;
168  }
169 
170  disease_infect(op, tmp, 0);
171  } FOR_MAP_FINISH();
172  }
173  }
174 }
175 
184 static void
186 {
187  HARD_ASSERT(op != NULL);
188 
189  object *victim = op->env;
190  /* No-one to inflict symptoms on. */
191  if (victim == NULL) {
192  return;
193  }
194 
195  victim = HEAD(victim);
196  object *symptom = disease_find_symptom(op);
197 
198  /* No symptom? Generate one. */
199  if (symptom == NULL) {
200  /* First check and see if the carrier of the disease
201  * is immune. If so, no symptoms! */
202  if (!disease_is_susceptible(op, victim)) {
203  return;
204  }
205 
206  FOR_INV_PREPARE(victim, tmp) {
207  if (tmp->type != SIGN) {
208  continue;
209  }
210 
211  if (strcmp(tmp->name, op->name) == 0 && tmp->level >= op->level) {
212  return;
213  }
214  } FOR_INV_FINISH();
215 
216  object *new_symptom = arch_get("symptom");
217 
218  /* Something special done with dam. We want diseases to be more
219  * random in what they'll kill, so we'll make the damage they
220  * do random. */
221  if (op->stats.dam != 0) {
222  /* Reduce the damage, on average, 50%, and making things random. */
223  int16_t dam = rndm(1, FABS(op->stats.dam));
224  if (op->stats.dam < 0) {
225  dam = -dam;
226  }
227  new_symptom->stats.dam = dam;
228  }
229 
230  new_symptom->stats.maxsp = op->stats.maxsp;
231  new_symptom->stats.food = -1;
232 
233  FREE_AND_COPY_HASH(new_symptom->name, op->name);
234  new_symptom->level = op->level;
235  new_symptom->speed = op->speed;
236  new_symptom->value = 0;
237  new_symptom->stats.Str = op->stats.Str;
238  new_symptom->stats.Dex = op->stats.Dex;
239  new_symptom->stats.Con = op->stats.Con;
240  new_symptom->stats.Int = op->stats.Int;
241  new_symptom->stats.Pow = op->stats.Pow;
242  new_symptom->stats.sp = op->stats.sp;
243  new_symptom->stats.food = op->last_eat;
244  new_symptom->stats.maxsp = op->stats.maxsp;
245  new_symptom->last_sp = op->last_sp;
246  new_symptom->stats.exp = 0;
247  new_symptom->stats.hp = op->stats.hp;
248  FREE_AND_COPY_HASH(new_symptom->msg, op->msg);
249  new_symptom->other_arch = op->other_arch;
250 
251  for (int i = 0; i < NROFATTACKS; i++) {
252  if (op->attack[i] != 0) {
253  new_symptom->attack[i] = op->attack[i];
254  }
255  }
256 
257  if (object_owner(op) != NULL) {
258  object_owner_set(new_symptom, op->owner);
259  }
260 
261  /* Unfortunately, set_owner does the wrong thing to the skills pointers
262  * resulting in exp going into the owners *current* chosen skill. */
263  new_symptom->chosen_skill = op->chosen_skill;
264 
265  CLEAR_FLAG(new_symptom, FLAG_NO_PASS);
266  object_insert_into(new_symptom, victim, 0);
267  return;
268  }
269 
270  /* Now deal with progressing diseases: we increase the negative effects
271  * caused by the symptoms. */
272  if (op->stats.ac != 0) {
273  symptom->value += op->stats.ac;
274  double scale = 1.0 + symptom->value / 100.0;
275 
276  symptom->stats.Str *= scale;
277  symptom->stats.Dex *= scale;
278  symptom->stats.Con *= scale;
279  symptom->stats.Int *= scale;
280  symptom->stats.Pow *= scale;
281  symptom->stats.dam *= scale;
282  symptom->stats.sp *= scale;
283  symptom->stats.food = scale * op->last_eat;
284  symptom->stats.maxsp *= scale;
285  symptom->last_sp *= scale;
286  symptom->stats.exp = 0;
287  symptom->stats.hp *= scale;
288  FREE_AND_COPY_HASH(symptom->msg, op->msg);
289  symptom->other_arch = op->other_arch;
290  }
291 
292  SET_FLAG(symptom, FLAG_APPLIED);
293  living_update(victim);
294 }
295 
302 static void
304 {
305  HARD_ASSERT(op != NULL);
306  HARD_ASSERT(op->env != NULL);
307 
308  /* Don't give immunity to this disease if last_heal is set. */
309  if (op->last_heal != 0) {
310  return;
311  }
312 
313  /* Try to update an existing immunity. */
314  FOR_INV_PREPARE(op->env, tmp) {
315  if (tmp->type == SIGN && strcmp(op->name, tmp->name) == 0) {
316  tmp->level = op->level;
317  return;
318  }
319  } FOR_INV_FINISH();
320 
321  object *immunity = arch_get("immunity");
322  FREE_AND_COPY_HASH(immunity->name, op->name);
323  immunity->level = op->level;
324  CLEAR_FLAG(immunity, FLAG_NO_PASS);
325  object_insert_into(immunity, op->env, 0);
326 }
327 
329 static void
330 process_func (object *op)
331 {
332  HARD_ASSERT(op != NULL);
333 
334  /* Determine if the disease is inside or outside of someone.
335  * If outside, we decrement 'value' until we're gone. */
336  if (op->env == NULL) {
337  op->value--;
338 
339  if (op->value == 0) {
340  /* Drop inv since disease may carry secondary infections. */
341  object_destruct(op);
342  return;
343  }
344  } else {
345  /* If we're inside a person, have the disease run its course.
346  * Negative foods denote "perpetual" diseases. */
347  if (op->stats.food > 0 && disease_is_susceptible(op, op->env)) {
348  op->stats.food--;
349 
350  if (op->stats.food == 0) {
351  /* Remove the symptoms of this disease. */
354  /* Drop inv since disease may carry secondary infections. */
355  object_destruct(op);
356  return;
357  }
358  }
359  }
360 
361  /* Check to see if we infect others. */
363 
364  /* Impose or modify the symptoms of the disease. */
365  if (op->env != NULL) {
367  }
368 }
369 
374 {
375  OBJECT_METHODS(DISEASE)->process_func = process_func;
376 }
377 
395 bool
396 disease_infect (object *op, object *victim, bool force)
397 {
398  HARD_ASSERT(op != NULL);
399  HARD_ASSERT(victim != NULL);
400 
401  victim = HEAD(victim);
402 
403  if (!IS_LIVE(victim)) {
404  return false;
405  }
406 
407  object *owner = object_owner(op);
408  if (owner != NULL && is_friend_of(owner, victim)) {
409  return false;
410  }
411 
412  if (!disease_is_susceptible(op, victim)) {
413  return false;
414  }
415 
416  if (!force && rndm(0, 126) >= op->stats.wc) {
417  return false;
418  }
419 
420  FOR_INV_PREPARE(victim, tmp) {
421  if (tmp->type == SIGN || tmp->type == DISEASE) {
422  if (strcmp(tmp->name, op->name) == 0 && tmp->level >= op->level) {
423  return false;
424  }
425  }
426  } FOR_INV_FINISH();
427 
428  /* If we've gotten this far, go ahead and infect the victim. */
429  object *new_disease = object_get();
430  object_copy(new_disease, op, false);
431  new_disease->stats.food = -1;
432  new_disease->value = op->stats.maxhp;
433  /* self-limiting factor */
434  new_disease->stats.wc -= op->last_grace;
435 
436  /* Unfortunately, set_owner does the wrong thing to the skills pointers
437  * resulting in exp going into the owners *current* chosen skill. */
438  if (owner != NULL) {
439  object_owner_set(new_disease, op->owner);
440  new_disease->chosen_skill = op->chosen_skill;
441  }
442 
443  new_disease = object_insert_into(new_disease, victim, 0);
444  SOFT_ASSERT_RC(new_disease != NULL, false,
445  "Failed to insert disease into %s",
446  object_get_str(victim));
447  CLEAR_FLAG(new_disease, FLAG_NO_PASS);
448 
449  if (new_disease->owner != NULL && new_disease->owner->type == PLAYER) {
450  char buf[MAX_BUF];
451 
452  /* if the disease has a title, it has a special infection message */
453  if (new_disease->title != NULL) {
454  snprintf(VS(buf), "%s %s!!", op->title, victim->name);
455  } else {
456  snprintf(VS(buf), "You infect %s with your disease, %s!",
457  victim->name, new_disease->name);
458  }
459 
460  if (victim->type == PLAYER) {
461  draw_info(COLOR_RED, new_disease->owner, buf);
462  } else {
463  draw_info(COLOR_WHITE, new_disease->owner, buf);
464  }
465  }
466 
467  if (victim->type == PLAYER) {
468  draw_info(COLOR_RED, victim, "You suddenly feel ill.");
469  }
470 
471  return true;
472 }
473 
482 void
483 disease_physically_infect (object *op, object *hitter)
484 {
485  HARD_ASSERT(op != NULL);
486  HARD_ASSERT(hitter != NULL);
487 
488  FOR_INV_PREPARE(hitter, tmp) {
489  if (tmp->type == DISEASE) {
490  disease_infect(tmp, op, 0);
491  }
492  } FOR_INV_FINISH();
493 }
494 
506 bool
507 disease_cure (object *op, object *caster)
508 {
509  HARD_ASSERT(op != NULL);
510 
511  if (caster != NULL) {
512  draw_info_format(COLOR_WHITE, op, "%s casts cure disease on you!",
513  caster->name);
514  }
515 
516  bool success = false;
517  bool is_diseased = false;
518  FOR_INV_PREPARE(op, tmp) {
519  if (tmp->type != DISEASE) {
520  continue;
521  }
522 
523  is_diseased = true;
524 
525  int diff;
526  if (caster == NULL) {
527  diff = 1;
528  } else {
529  diff = tmp->level - caster->level;
530  }
531 
532  if (diff > 1 && !rndm_chance(diff)) {
533  draw_info_format(COLOR_WHITE, op,
534  "The disease %s resists the cure spell!",
535  tmp->name);
536 
537  if (caster != NULL) {
538  draw_info_format(COLOR_WHITE, caster,
539  "The disease %s resists the cure spell!",
540  tmp->name);
541  }
542 
543  continue;
544  }
545 
546  draw_info_format(COLOR_WHITE, op,
547  "You are healed from disease %s.",
548  tmp->name);
549 
550  if (caster != NULL) {
551  draw_info_format(COLOR_WHITE, caster,
552  "You heal %s from disease %s.",
553  op->name, tmp->name);
554  }
555 
557  object_remove(tmp, 0);
558  object_destroy(tmp);
559  success = true;
560  } FOR_INV_FINISH();
561 
562  if (!is_diseased) {
563  draw_info(COLOR_WHITE, op, "You are not diseased!");
564 
565  if (caster != NULL) {
566  draw_info_format(COLOR_WHITE, caster, "%s is not diseased!",
567  op->name);
568  }
569  }
570 
571  return success;
572 }
573 
584 bool
585 disease_reduce_symptoms (object *op, int reduction)
586 {
587  HARD_ASSERT(op != NULL);
588 
589  bool success = false;
590  FOR_INV_PREPARE(op, tmp) {
591  if (tmp->type != SYMPTOM) {
592  continue;
593  }
594 
595  success = true;
596  tmp->value = MAX(0, tmp->value - 2 * reduction);
597  /* Give the disease time to modify this symptom,
598  * and reduce its severity. */
599  tmp->speed_left = 0;
600  } FOR_INV_FINISH();
601 
602  if (success) {
603  draw_info(COLOR_WHITE, op, "Your illness seems less severe.");
604  }
605 
606  return success;
607 }
#define FREE_AND_COPY_HASH(_sv_, _nv_)
Definition: global.h:100
void object_destroy(object *ob)
Definition: object.c:1441
int16_t ac
Definition: living.h:93
int get_rangevector(object *op1, object *op2, rv_vector *retval, int flags)
Definition: map.c:2238
const char * race
Definition: object.h:174
#define DISEASE
Definition: define.h:538
object * object_get(void)
Definition: object.c:993
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:1691
#define FOR_INV_FINISH()
Definition: define.h:1698
mapstruct * get_map_from_coord(mapstruct *m, int *x, int *y)
Definition: map.c:1869
int16_t last_heal
Definition: object.h:310
void object_copy(object *op, const object *src, bool no_speed)
Definition: object.c:886
int16_t last_sp
Definition: object.h:313
object * arch_get(const char *name)
Definition: arch.c:430
int64_t exp
Definition: living.h:69
int obj_in_line_of_sight(object *obj, rv_vector *rv)
Definition: los.c:427
void object_destruct(object *op)
Definition: object.c:1521
#define IS_LIVE(op)
Definition: define.h:841
#define PLAYER
Definition: define.h:122
bool disease_cure(object *op, object *caster)
Definition: disease.c:507
int16_t sp
Definition: living.h:78
static bool disease_is_susceptible(object *op, object *victim)
Definition: disease.c:54
#define QUERY_FLAG(xyz, p)
Definition: define.h:761
struct archetype * other_arch
Definition: object.h:228
int8_t Int
Definition: living.h:112
void object_remove(object *op, int flags)
Definition: object.c:1623
uint8_t attack[NROFATTACKS]
Definition: object.h:436
int16_t maxsp
Definition: living.h:81
int8_t Con
Definition: living.h:109
int32_t hp
Definition: living.h:72
struct obj * chosen_skill
Definition: object.h:210
const char * title
Definition: object.h:171
int16_t y
Definition: object.h:276
int32_t maxhp
Definition: living.h:75
#define P_IS_PLAYER
Definition: map.h:256
const char * object_get_str(const object *op)
Definition: object.c:3151
int is_friend_of(object *op, object *obj)
Definition: monster.c:1685
static void process_func(object *op)
Definition: disease.c:330
object * object_insert_into(object *op, object *where, int flag)
Definition: object.c:2158
struct mapdef * map
Definition: object.h:139
int16_t dam
Definition: living.h:87
const char * name
Definition: object.h:168
#define SET_FLAG(xyz, p)
Definition: define.h:741
struct obj * env
Definition: object.h:130
int16_t last_grace
Definition: object.h:316
void disease_physically_infect(object *op, object *hitter)
Definition: disease.c:483
double speed
Definition: object.h:469
#define SIGN
Definition: define.h:409
#define HEAD(op)
Definition: object.h:657
#define FOR_MAP_FINISH()
Definition: define.h:1759
static void disease_check_infection(object *op)
Definition: disease.c:125
int16_t x
Definition: object.h:273
int16_t last_eat
Definition: object.h:319
int16_t wc
Definition: living.h:90
#define FLAG_APPLIED
Definition: define.h:1182
bool disease_reduce_symptoms(object *op, int reduction)
Definition: disease.c:585
void object_owner_set(object *op, object *owner)
Definition: object.c:788
struct obj * owner
Definition: object.h:207
living stats
Definition: object.h:481
int8_t Dex
Definition: living.h:106
uint8_t type
Definition: object.h:360
#define CLEAR_FLAG(xyz, p)
Definition: define.h:751
#define OBJECT_METHODS(type)
object * object_owner(object *op)
Definition: object.c:857
const char * msg
Definition: object.h:183
OBJECT_TYPE_INIT_DEFINE(disease)
Definition: disease.c:373
int living_update(object *op)
Definition: living.c:1661
static void disease_remove_symptoms(object *op)
Definition: disease.c:108
int8_t Str
Definition: living.h:103
int8_t Pow
Definition: living.h:115
#define SYMPTOM
Definition: define.h:542
#define P_IS_MONSTER
Definition: map.h:258
#define FLAG_UNDEAD
Definition: define.h:1012
#define FLAG_NO_PASS
Definition: define.h:908
static object * disease_find_symptom(object *op)
Definition: disease.c:88
Definition: map.h:536
int8_t level
Definition: object.h:347
static void disease_do_symptoms(object *op)
Definition: disease.c:185
int64_t value
Definition: object.h:240
static void disease_grant_immunity(object *op)
Definition: disease.c:303
int8_t magic
Definition: object.h:341
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Definition: define.h:1752
int16_t food
Definition: living.h:84
bool disease_infect(object *op, object *victim, bool force)
Definition: disease.c:396