Atrinik Server 2.5
server/quest.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00030 #include <global.h>
00031 
00037 static void add_one_drop_quest_item(object *op, const char *quest_name)
00038 {
00039     object *quest_container = get_archetype(QUEST_CONTAINER_ARCHETYPE);
00040 
00041     /* Mark this quest as completed. */
00042     quest_container->magic = QUEST_STATUS_COMPLETED;
00043     /* Store the quest name. */
00044     FREE_AND_COPY_HASH(quest_container->name, quest_name);
00045     /* Insert it inside player's quest container. */
00046     insert_ob_in_ob(quest_container, CONTR(op)->quest_container);
00047 }
00048 
00055 static object *find_quest(object *where, const char *quest_name)
00056 {
00057     object *tmp;
00058 
00059     /* Go through the objects in the quest container */
00060     for (tmp = where->inv; tmp; tmp = tmp->below)
00061     {
00062         if (tmp->name == quest_name)
00063         {
00064             return tmp;
00065         }
00066     }
00067 
00068     return NULL;
00069 }
00070 
00082 static int has_quest_item(object *op, object *quest_item, sint32 flag, sint64 *num)
00083 {
00084     object *tmp;
00085 
00086     /* Go through the objects in the object's inventory. */
00087     for (tmp = op->inv; tmp; tmp = tmp->below)
00088     {
00089         /* Compare the values. */
00090         if (tmp->name == quest_item->name && tmp->arch->name == quest_item->arch->name && (!flag || QUERY_FLAG(tmp, flag)))
00091         {
00092             if (num)
00093             {
00094                 *num += MAX(1, tmp->nrof);
00095             }
00096             else
00097             {
00098                 return 1;
00099             }
00100         }
00101 
00102         /* If it has inventory and is not a system object, go on recursively. */
00103         if (tmp->inv && !QUERY_FLAG(tmp, FLAG_SYS_OBJECT))
00104         {
00105             int ret = has_quest_item(tmp, quest_item, flag, num);
00106 
00107             if (ret && !num)
00108             {
00109                 return 1;
00110             }
00111         }
00112     }
00113 
00114     return 0;
00115 }
00116 
00122 static void check_quest_container(object *op, object *quest_container, object *quest_object)
00123 {
00124     char buf[HUGE_BUF];
00125     object *tmp;
00126 
00127     /* If this is not a one-drop item quest, it must first be accepted. */
00128     if (quest_container->sub_type != QUEST_TYPE_ITEM && (!quest_object || quest_object->magic == QUEST_STATUS_COMPLETED))
00129     {
00130         return;
00131     }
00132 
00133     tmp = quest_container->inv;
00134 
00135     /* Check for events in this quest container. */
00136     if (HAS_EVENT(quest_container, EVENT_TRIGGER))
00137     {
00138         /* Advance through the quest object's inventory, skipping the
00139          * event object. */
00140         if (tmp->type == EVENT_OBJECT)
00141         {
00142             tmp = tmp->below;
00143         }
00144 
00145         if (trigger_event(EVENT_TRIGGER, op, quest_container, tmp, NULL, 0, 0, 0, SCRIPT_FIX_NOTHING))
00146         {
00147             return;
00148         }
00149     }
00150 
00151     switch (quest_container->sub_type)
00152     {
00153         case QUEST_TYPE_ITEM:
00154         {
00155             object *clone;
00156             int one_drop;
00157 
00158             if (!tmp)
00159             {
00160                 return;
00161             }
00162 
00163             one_drop = QUERY_FLAG(tmp, FLAG_ONE_DROP);
00164 
00165             /* Not one-drop, but we already have the quest object (keys,
00166              * for example). */
00167             if (!one_drop && has_quest_item(op, tmp, 0, NULL))
00168             {
00169                 return;
00170             }
00171             /* One-drop and we already found this quest object before
00172              * (Rhun's cloak, for example). */
00173             else if (one_drop && quest_object)
00174             {
00175                 return;
00176             }
00177 
00178             clone = get_object();
00179             copy_object_with_inv(tmp, clone);
00180             SET_FLAG(clone, FLAG_IDENTIFIED);
00181             /* Insert the quest item inside the player. */
00182             insert_ob_in_ob(clone, op);
00183             esrv_send_item(op, clone);
00184 
00185             if (one_drop)
00186             {
00187                 /* So the item will never drop again for this player. */
00188                 add_one_drop_quest_item(op, quest_container->name);
00189                 snprintf(buf, sizeof(buf), "You solved the one drop quest %s!\n", quest_container->name);
00190             }
00191             else
00192             {
00193                 snprintf(buf, sizeof(buf), "You found the special drop %s!\n", query_short_name(clone, NULL));
00194             }
00195 
00196             new_draw_info(NDI_ANIM, COLOR_NAVY, op, buf);
00197             play_sound_player_only(CONTR(op), CMD_SOUND_EFFECT, "event01.ogg", 0, 0, 0, 0);
00198             break;
00199         }
00200 
00201         case QUEST_TYPE_KILL:
00202             /* The +1 makes it so no messages are displayed when player
00203              * kills more same monsters. */
00204             if (quest_object->last_sp <= quest_object->last_grace + 1)
00205             {
00206                 quest_object->last_sp++;
00207             }
00208 
00209             /* Display an informative message about the quest status. */
00210             if (quest_object->last_sp <= quest_object->last_grace)
00211             {
00212                 if (quest_object->last_sp == quest_object->last_grace)
00213                 {
00214                     if (quest_object->env->type == QUEST_CONTAINER && quest_object->env->sub_type == QUEST_TYPE_MULTI)
00215                     {
00216                         snprintf(buf, sizeof(buf), "Quest '%s: %s' completed!\n", quest_object->env->name, quest_container->name);
00217                     }
00218                     else
00219                     {
00220                         snprintf(buf, sizeof(buf), "Quest '%s' completed!\n", quest_container->name);
00221                     }
00222 
00223                     play_sound_player_only(CONTR(op), CMD_SOUND_EFFECT, "event01.ogg", 0, 0, 0, 0);
00224                 }
00225                 else
00226                 {
00227                     if (quest_object->env->type == QUEST_CONTAINER && quest_object->env->sub_type == QUEST_TYPE_MULTI)
00228                     {
00229                         snprintf(buf, sizeof(buf), "Quest %s (%s): %d/%d.\n", quest_object->env->name, quest_container->name, quest_object->last_sp, quest_object->last_grace);
00230                     }
00231                     else
00232                     {
00233                         snprintf(buf, sizeof(buf), "Quest %s: %d/%d.\n", quest_container->name, quest_object->last_sp, quest_object->last_grace);
00234                     }
00235                 }
00236 
00237                 new_draw_info(NDI_ANIM, COLOR_NAVY, op, buf);
00238             }
00239 
00240             break;
00241 
00242         case QUEST_TYPE_KILL_ITEM:
00243         {
00244             object *clone;
00245             sint64 num = 0;
00246 
00247             /* Have we found this item already? */
00248             if (!tmp || (!quest_object->last_grace && has_quest_item(op, tmp, FLAG_QUEST_ITEM, NULL)))
00249             {
00250                 return;
00251             }
00252 
00253             if (quest_object->last_grace)
00254             {
00255                 has_quest_item(op, tmp, FLAG_QUEST_ITEM, &num);
00256 
00257                 if (num >= quest_object->last_grace)
00258                 {
00259                     return;
00260                 }
00261             }
00262 
00263             clone = get_object();
00264             copy_object_with_inv(tmp, clone);
00265             SET_FLAG(clone, FLAG_QUEST_ITEM);
00266             SET_FLAG(clone, FLAG_STARTEQUIP);
00267             CLEAR_FLAG(clone, FLAG_SYS_OBJECT);
00268             /* Insert the quest item inside the player. */
00269             clone = insert_ob_in_ob(clone, op);
00270             esrv_send_item(op, clone);
00271 
00272             if (quest_object->env->type == QUEST_CONTAINER && quest_object->env->sub_type == QUEST_TYPE_MULTI)
00273             {
00274                 new_draw_info_format(NDI_ANIM, COLOR_NAVY, op, "Quest %s (%s): You found the quest item %s (%"FMT64"/%d)!\n", quest_object->env->name, quest_container->name, query_base_name(clone, NULL), num + 1, quest_object->last_grace);
00275             }
00276             else
00277             {
00278                 new_draw_info_format(NDI_ANIM, COLOR_NAVY, op, "Quest %s: You found the quest item %s!\n", quest_container->name, query_base_name(clone, NULL));
00279             }
00280 
00281             play_sound_player_only(CONTR(op), CMD_SOUND_EFFECT, "event01.ogg", 0, 0, 0, 0);
00282             break;
00283         }
00284 
00285         default:
00286             LOG(llevBug, "Quest object '%s' has unknown sub type (%d).\n", quest_container->name, quest_container->sub_type);
00287     }
00288 }
00289 
00298 void check_quest(object *op, object *quest_container)
00299 {
00300     object *quest_object = find_quest(CONTR(op)->quest_container, quest_container->name);
00301 
00302     /* Handle multi-part quests. */
00303     if (quest_container->sub_type == QUEST_TYPE_MULTI)
00304     {
00305         object *tmp;
00306 
00307         /* No quest object, can't go on. */
00308         if (!quest_object)
00309         {
00310             return;
00311         }
00312 
00313         for (tmp = quest_container->inv; tmp; tmp = tmp->below)
00314         {
00315             check_quest_container(op, tmp, find_quest(quest_object, tmp->name));
00316         }
00317     }
00318     else
00319     {
00320         check_quest_container(op, quest_container, quest_object);
00321     }
00322 }