Atrinik Server  4.0
quest.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 
32 #include <global.h>
33 #include <toolkit/string.h>
34 #include <plugin.h>
35 #include <arch.h>
36 #include <player.h>
37 #include <object.h>
38 
49 static object *quest_find(object *quest, shstr *quest_name)
50 {
51  object *tmp;
52 
53  /* Go through the objects in the quest container */
54  for (tmp = quest->inv; tmp != NULL; tmp = tmp->below) {
55  if (tmp->name == quest_name) {
56  return tmp;
57  }
58  }
59 
60  return NULL;
61 }
62 
80 static int quest_item_check(object *op, object *quest_item, int flag,
81  int64_t *num)
82 {
83  object *tmp;
84 
85  HARD_ASSERT(op != NULL);
86  HARD_ASSERT(quest_item != NULL);
87 
88  /* Go through the objects in the object's inventory. */
89  for (tmp = op->inv; tmp != NULL; tmp = tmp->below) {
90  /* Compare the values. */
91  if (tmp->name == quest_item->name &&
92  tmp->arch->name == quest_item->arch->name &&
93  (flag == -1 || QUERY_FLAG(tmp, flag))) {
94  if (num != NULL) {
95  *num += MAX(1, tmp->nrof);
96  } else {
97  return 1;
98  }
99  }
100 
101  /* If it has inventory and is not a system object, check the
102  * inventory as well. */
103  if (tmp->inv && !QUERY_FLAG(tmp, FLAG_SYS_OBJECT)) {
104  int ret;
105 
106  ret = quest_item_check(tmp, quest_item, flag, num);
107 
108  if (ret && num == NULL) {
109  return 1;
110  }
111  }
112  }
113 
114  return 0;
115 }
116 
128 static void quest_check_item_drop(object *op, object *quest, object *quest_pl,
129  object *item)
130 {
131  object *clone;
132  char buf[MAX_BUF];
133 
134  if (item == NULL) {
135  LOG(BUG, "Quest '%s' without an item.", quest->name);
136  return;
137  }
138 
139  if (!QUERY_FLAG(item, FLAG_ONE_DROP) &&
140  quest_item_check(op, item, -1, NULL)) {
141  /* Not one-drop, but we already have the quest object (keys, for
142  * example). */
143  return;
144  } else if (QUERY_FLAG(item, FLAG_ONE_DROP) && quest_pl != NULL) {
145  /* One-drop and we already found this quest object before (Rhun's cloak,
146  * for example). */
147  return;
148  }
149 
150  /* Create the one-drop item. */
151  clone = object_get();
152  object_copy_full(clone, item);
153  SET_FLAG(clone, FLAG_IDENTIFIED);
154 
155  /* Insert the quest item inside the player. */
156  object_insert_into(clone, op, 0);
157 
158  if (QUERY_FLAG(item, FLAG_ONE_DROP)) {
159  /* Create a quest object in the player's container, so that the item
160  * will never drop for them again. */
162 
163  /* Mark this quest as completed. */
164  quest_pl->magic = QUEST_STATUS_COMPLETED;
165  /* Store the quest UID and name. */
166  FREE_AND_COPY_HASH(quest_pl->name, quest->name);
167  FREE_AND_COPY_HASH(quest_pl->race, QUEST_NAME(quest));
168  /* Insert it inside player's quest container. */
169  object_insert_into(quest_pl, CONTR(op)->quest_container, 0);
170 
171  snprintf(VS(buf), "You solved the one drop quest %s!\n",
172  QUEST_NAME(quest_pl));
173  } else {
174  char *name = object_get_short_name_s(clone, op);
175  snprintf(VS(buf), "You found the special drop %s!\n", name);
176  efree(name);
177  }
178 
179  draw_map_text_anim(op, COLOR_NAVY, buf);
180  draw_info(COLOR_NAVY, op, buf);
181  play_sound_player_only(CONTR(op), CMD_SOUND_EFFECT, "event01.ogg",
182  0, 0, 0, 0);
183 }
184 
196 static void quest_check_kill(object *op, object *quest, object *quest_pl,
197  object *item)
198 {
199  if (item != NULL) {
200  LOG(BUG, "Quest '%s' with an item.", quest->name);
201  return;
202  }
203 
204  if (quest_pl == NULL || quest_pl->magic != QUEST_STATUS_STARTED) {
205  /* Quest must be started. */
206  return;
207  }
208 
209  /* The +1 makes it so no messages are displayed when player
210  * kills more same monsters. */
211  if (quest_pl->last_sp <= quest_pl->last_grace + 1) {
212  quest_pl->last_sp++;
213  }
214 
215  /* Display an informative message about the quest status. */
216  if (quest_pl->last_sp <= quest_pl->last_grace) {
217  char buf[MAX_BUF];
218 
219  snprintf(VS(buf), "Quest [b]%s[/b]", QUEST_NAME(quest_pl));
220 
221  if (quest_pl->last_sp == quest_pl->last_grace) {
222  play_sound_player_only(CONTR(op), CMD_SOUND_EFFECT, "event01.ogg",
223  0, 0, 0, 0);
224  snprintfcat(VS(buf), " completed!\n");
225  } else {
226  snprintfcat(VS(buf), " %d/%d.\n", quest_pl->last_sp,
227  quest_pl->last_grace);
228  }
229 
230  draw_map_text_anim(op, COLOR_NAVY, buf);
231  draw_info(COLOR_NAVY, op, buf);
232  }
233 }
234 
246 static void quest_check_item(object *op, object *quest, object *quest_pl,
247  object *item)
248 {
249  object *clone;
250  int64_t num;
251  char buf[MAX_BUF];
252 
253  if (item == NULL) {
254  LOG(BUG, "Quest '%s' without an item.", quest->name);
255  return;
256  }
257 
258  if (quest_pl == NULL || quest_pl->magic != QUEST_STATUS_STARTED) {
259  /* Quest must be started. */
260  return;
261  }
262 
263  /* Make sure we haven't found a suitable number of these items yet. */
264  if (quest_pl->last_grace > 1) {
265  num = 0;
266  quest_item_check(op, item, FLAG_QUEST_ITEM, &num);
267 
268  if (num >= quest_pl->last_grace) {
269  return;
270  }
271  } else {
272  if (quest_item_check(op, item, FLAG_QUEST_ITEM, NULL)) {
273  return;
274  }
275  }
276 
277  /* Create a new quest item. */
278  clone = object_get();
279  object_copy_full(clone, item);
280  SET_FLAG(clone, FLAG_QUEST_ITEM);
281  SET_FLAG(clone, FLAG_STARTEQUIP);
282  CLEAR_FLAG(clone, FLAG_SYS_OBJECT);
283 
284  char *name = object_get_base_name_s(clone, op);
285  snprintf(VS(buf), "Quest [b]%s[/b]: You found the quest item %s",
286  QUEST_NAME(quest_pl), name);
287  efree(name);
288 
289  if (quest_pl->last_grace > 1) {
290  snprintfcat(VS(buf), " (%"PRId64"/%d)", num + MAX(1, clone->nrof),
291  quest_pl->last_grace);
292  }
293 
294  snprintfcat(VS(buf), "!\n");
295 
296  draw_map_text_anim(op, COLOR_NAVY, buf);
297  draw_info(COLOR_NAVY, op, buf);
298 
299  /* Insert the quest item inside the player. */
300  object_insert_into(clone, op, 0);
301  play_sound_player_only(CONTR(op), CMD_SOUND_EFFECT, "event01.ogg",
302  0, 0, 0, 0);
303 }
304 
314 static void quest_object_handle(object *op, object *quest, object *quest_pl)
315 {
316  object *item;
317 
318  HARD_ASSERT(op != NULL);
319  HARD_ASSERT(quest != NULL);
320 
321  SOFT_ASSERT(op->type == PLAYER, "Object is not a player: %s",
322  object_get_str(op));
323  SOFT_ASSERT(quest->type == QUEST_CONTAINER, "Quest is not a quest "
324  "container: %s", object_get_str(quest));
325  SOFT_ASSERT(quest_pl == NULL || quest_pl->type == QUEST_CONTAINER,
326  "Invalid quest_pl supplied: %p", quest_pl);
327 
328  /* Trigger the TRIGGER event */
330  op,
331  quest,
332  quest_pl,
333  NULL,
334  0,
335  0,
336  0,
337  0) != 0) {
338  return;
339  }
340 
341  for (item = quest->inv; item != NULL; item = item->below) {
342  if (!QUERY_FLAG(item, FLAG_SYS_OBJECT)) {
343  break;
344  }
345  }
346 
347  switch (quest->sub_type) {
349  quest_check_item_drop(op, quest, quest_pl, item);
350  break;
351 
352  case QUEST_TYPE_KILL:
353  quest_check_kill(op, quest, quest_pl, item);
354  break;
355 
356  case QUEST_TYPE_ITEM:
357  quest_check_item(op, quest, quest_pl, item);
358  break;
359 
360  default:
361  LOG(BUG, "Quest '%s' has unknown sub type: %d",
362  quest->name, quest->sub_type);
363  break;
364  }
365 }
366 
377 void quest_handle(object *op, object *quest)
378 {
379  object *quest_pl;
380 
381  HARD_ASSERT(op != NULL);
382  HARD_ASSERT(quest != NULL);
383 
384  SOFT_ASSERT(op->type == PLAYER, "Object is not a player: %s",
385  object_get_str(op));
386  SOFT_ASSERT(quest->type == QUEST_CONTAINER, "Quest is not a quest "
387  "container: %s", object_get_str(quest));
388 
389  quest_pl = quest_find(CONTR(op)->quest_container, quest->name);
390 
391  /* Handle multi-part quests. */
392  if (quest->sub_type == QUEST_TYPE_NONE) {
393  object *tmp;
394 
395  /* Player doesn't have such quest. */
396  if (quest_pl == NULL) {
397  return;
398  }
399 
400  for (tmp = quest->inv; tmp != NULL; tmp = tmp->below) {
401  quest_object_handle(op, tmp, quest_find(quest_pl, tmp->name));
402  }
403  } else {
404  quest_object_handle(op, quest, quest_pl);
405  }
406 }
#define FREE_AND_COPY_HASH(_sv_, _nv_)
Definition: global.h:100
#define FLAG_SYS_OBJECT
Definition: define.h:1243
const char * race
Definition: object.h:174
static void quest_check_item(object *op, object *quest, object *quest_pl, object *item)
Definition: quest.c:246
#define QUEST_CONTAINER
Definition: define.h:489
int trigger_event(int event_type, object *const activator, object *const me, object *const other, const char *msg, int parm1, int parm2, int parm3, int flags)
Definition: plugins.c:510
object * object_get(void)
Definition: object.c:993
void quest_handle(object *op, object *quest)
Definition: quest.c:377
int16_t last_sp
Definition: object.h:313
object * arch_get(const char *name)
Definition: arch.c:430
#define PLAYER
Definition: define.h:122
#define QUERY_FLAG(xyz, p)
Definition: define.h:761
#define QUEST_TYPE_ITEM_DROP
Definition: define.h:1541
struct archetype * arch
Definition: object.h:225
static void quest_object_handle(object *op, object *quest, object *quest_pl)
Definition: quest.c:314
#define QUEST_CONTAINER_ARCHETYPE
Definition: define.h:1503
#define FLAG_STARTEQUIP
Definition: define.h:1004
#define EVENT_TRIGGER
Definition: plugin.h:93
static int quest_item_check(object *op, object *quest_item, int flag, int64_t *num)
Definition: quest.c:80
#define QUEST_TYPE_KILL
Definition: define.h:1534
const char * object_get_str(const object *op)
Definition: object.c:3151
#define FLAG_QUEST_ITEM
Definition: define.h:1233
char * object_get_base_name_s(const object *op, const object *caller)
Definition: item.c:534
object * object_insert_into(object *op, object *where, int flag)
Definition: object.c:2158
char * object_get_short_name_s(const object *op, const object *caller)
Definition: item.c:453
void play_sound_player_only(player *pl, int type, const char *filename, int x, int y, int loop, int volume)
Definition: sounds.c:66
const char * name
Definition: object.h:168
#define SET_FLAG(xyz, p)
Definition: define.h:741
struct obj * below
Definition: object.h:114
int16_t last_grace
Definition: object.h:316
uint32_t nrof
Definition: object.h:264
#define FLAG_ONE_DROP
Definition: define.h:1302
static object * quest_find(object *quest, shstr *quest_name)
Definition: quest.c:49
uint64_t num
Number of successful updates.
Definition: metaserver.c:43
#define QUEST_NAME(_quest)
Definition: define.h:1509
static void quest_check_item_drop(object *op, object *quest, object *quest_pl, object *item)
Definition: quest.c:128
#define QUEST_STATUS_COMPLETED
Definition: define.h:1522
#define QUEST_STATUS_STARTED
Definition: define.h:1520
uint8_t type
Definition: object.h:360
#define CLEAR_FLAG(xyz, p)
Definition: define.h:751
shstr * name
More definite name, like "kobold".
Definition: arch.h:46
static void quest_check_kill(object *op, object *quest, object *quest_pl, object *item)
Definition: quest.c:196
struct obj * inv
Definition: object.h:123
#define QUEST_TYPE_ITEM
Definition: define.h:1536
#define QUEST_TYPE_NONE
Definition: define.h:1532
void object_copy_full(object *op, const object *src)
Definition: object.c:970
int8_t magic
Definition: object.h:341
#define FLAG_IDENTIFIED
Definition: define.h:980
uint8_t sub_type
Definition: object.h:363