Quest System Update
* Added support for the new quests that grant item drops from mobs.
This commit is contained in:
parent
2fc20c460d
commit
039e1b65a3
@ -32,6 +32,7 @@
|
||||
// 0x080000 - (F) Removed bound items when guild/party is broken
|
||||
// 0x100000 - (Y) Roulette Lottery
|
||||
// 0x200000 - (Z) Merged items from item mergers process.
|
||||
// 0x400000 - (Q) Log items given from quest-granted drops.
|
||||
// Example: Log trades+vending+script items+created items: 1+2+32+1024 = 1059
|
||||
// Please note that moving items from inventory to cart and back is not logged by design.
|
||||
enable_logs: 0xFFFFFF
|
||||
|
5979
db/quest_db.txt
5979
db/quest_db.txt
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@
|
||||
# (C)onsumable Items, (A)dministrators Create/Delete, Sto(R)age, (G)uild Storage,
|
||||
# (E)mail attachment,(B)uying Store, Pr(O)duced Items/Ingredients, Auct(I)oned Items,
|
||||
# (X) Other, (D) Stolen from mobs, (U) MVP Prizes, (F) Guild/Party Bound retrieval
|
||||
# Lotter(Y), (Z) Merged Items
|
||||
# Lotter(Y), (Z) Merged Items, (Q)uest
|
||||
|
||||
#Database: ragnarok
|
||||
#Table: picklog
|
||||
@ -11,7 +11,7 @@ CREATE TABLE IF NOT EXISTS `picklog` (
|
||||
`id` int(11) NOT NULL auto_increment,
|
||||
`time` datetime NOT NULL default '0000-00-00 00:00:00',
|
||||
`char_id` int(11) NOT NULL default '0',
|
||||
`type` enum('M','P','L','T','V','S','N','C','A','R','G','E','B','O','I','X','D','U','$','F','Z') NOT NULL default 'P',
|
||||
`type` enum('M','P','L','T','V','S','N','C','A','R','G','E','B','O','I','X','D','U','$','F','Z','Q') NOT NULL default 'P',
|
||||
`nameid` smallint(5) unsigned NOT NULL default '0',
|
||||
`amount` int(11) NOT NULL default '1',
|
||||
`refine` tinyint(3) unsigned NOT NULL default '0',
|
||||
|
1
sql-files/upgrades/upgrade_20150917_log.sql
Normal file
1
sql-files/upgrades/upgrade_20150917_log.sql
Normal file
@ -0,0 +1 @@
|
||||
ALTER TABLE `picklog` MODIFY `type` enum('M','P','L','T','V','S','N','C','A','R','G','E','B','O','I','X','D','U','$','F','Z','Q') NOT NULL default 'P';
|
@ -70,6 +70,7 @@
|
||||
#define MAX_GUILDLEVEL 50 ///Max Guild level
|
||||
#define MAX_GUARDIANS 8 ///Local max per castle. If this value is increased, need to add more fields on MySQL `guild_castle` table [Skotlex]
|
||||
#define MAX_QUEST_OBJECTIVES 3 ///Max quest objectives for a quest
|
||||
#define MAX_QUEST_DROPS 3 ///Max quest drops for a quest
|
||||
#define MAX_PC_BONUS_SCRIPT 50 ///Max bonus script can be fetched from `bonus_script` table on player load [Cydh]
|
||||
|
||||
// for produce
|
||||
|
@ -15608,20 +15608,35 @@ void clif_parse_PartyTick(int fd, struct map_session_data* sd)
|
||||
|
||||
/// Sends list of all quest states (ZC_ALL_QUEST_LIST).
|
||||
/// 02b1 <packet len>.W <num>.L { <quest id>.L <active>.B }*num
|
||||
void clif_quest_send_list(struct map_session_data * sd)
|
||||
void clif_quest_send_list(struct map_session_data *sd)
|
||||
{
|
||||
int fd = sd->fd;
|
||||
int i;
|
||||
int len = sd->avail_quests*5+8;
|
||||
|
||||
#if PACKETVER >= 20141022
|
||||
int info_len = 15;
|
||||
int len = sd->avail_quests*info_len+8;
|
||||
WFIFOHEAD(fd,len);
|
||||
WFIFOW(fd, 0) = 0x97a;
|
||||
#else
|
||||
int info_len = 5;
|
||||
int len = sd->avail_quests*info_len+8;
|
||||
WFIFOHEAD(fd,len);
|
||||
WFIFOW(fd, 0) = 0x2b1;
|
||||
#endif
|
||||
WFIFOW(fd, 2) = len;
|
||||
WFIFOL(fd, 4) = sd->avail_quests;
|
||||
|
||||
for( i = 0; i < sd->avail_quests; i++ ) {
|
||||
WFIFOL(fd, i*5+8) = sd->quest_log[i].quest_id;
|
||||
WFIFOB(fd, i*5+12) = sd->quest_log[i].state;
|
||||
for (i = 0; i < sd->avail_quests; i++) {
|
||||
#if PACKETVER >= 20141022
|
||||
struct quest_db *qi = quest_search(sd->quest_log[i].quest_id);
|
||||
#endif
|
||||
WFIFOL(fd, i*info_len+8) = sd->quest_log[i].quest_id;
|
||||
WFIFOB(fd, i*info_len+12) = sd->quest_log[i].state;
|
||||
#if PACKETVER >= 20141022
|
||||
WFIFOL(fd, i*info_len+13) = sd->quest_log[i].time - qi->time;
|
||||
WFIFOL(fd, i*info_len+17) = sd->quest_log[i].time;
|
||||
WFIFOW(fd, i*info_len+21) = qi->objectives_count;
|
||||
#endif
|
||||
}
|
||||
|
||||
WFIFOSET(fd, len);
|
||||
@ -15630,7 +15645,7 @@ void clif_quest_send_list(struct map_session_data * sd)
|
||||
|
||||
/// Sends list of all quest missions (ZC_ALL_QUEST_MISSION).
|
||||
/// 02b2 <packet len>.W <num>.L { <quest id>.L <start time>.L <expire time>.L <mobs>.W { <mob id>.L <mob count>.W <mob name>.24B }*3 }*num
|
||||
void clif_quest_send_mission(struct map_session_data * sd)
|
||||
void clif_quest_send_mission(struct map_session_data *sd)
|
||||
{
|
||||
int fd = sd->fd;
|
||||
int i, j;
|
||||
@ -15642,18 +15657,18 @@ void clif_quest_send_mission(struct map_session_data * sd)
|
||||
WFIFOW(fd, 2) = len;
|
||||
WFIFOL(fd, 4) = sd->avail_quests;
|
||||
|
||||
for( i = 0; i < sd->avail_quests; i++ ) {
|
||||
for (i = 0; i < sd->avail_quests; i++) {
|
||||
struct quest_db *qi = quest_search(sd->quest_log[i].quest_id);
|
||||
|
||||
WFIFOL(fd, i*104+8) = sd->quest_log[i].quest_id;
|
||||
WFIFOL(fd, i*104+12) = sd->quest_log[i].time - qi->time;
|
||||
WFIFOL(fd, i*104+16) = sd->quest_log[i].time;
|
||||
WFIFOW(fd, i*104+20) = qi->num_objectives;
|
||||
WFIFOW(fd, i*104+20) = qi->objectives_count;
|
||||
|
||||
for( j = 0 ; j < qi->num_objectives; j++ ) {
|
||||
WFIFOL(fd, i*104+22+j*30) = qi->mob[j];
|
||||
for (j = 0 ; j < qi->objectives_count; j++) {
|
||||
WFIFOL(fd, i*104+22+j*30) = qi->objectives[j].mob;
|
||||
WFIFOW(fd, i*104+26+j*30) = sd->quest_log[i].count[j];
|
||||
mob = mob_db(qi->mob[j]);
|
||||
mob = mob_db(qi->objectives[j].mob);
|
||||
memcpy(WFIFOP(fd, i*104+28+j*30), mob->jname, NAME_LENGTH);
|
||||
}
|
||||
}
|
||||
@ -15664,7 +15679,7 @@ void clif_quest_send_mission(struct map_session_data * sd)
|
||||
|
||||
/// Notification about a new quest (ZC_ADD_QUEST).
|
||||
/// 02b3 <quest id>.L <active>.B <start time>.L <expire time>.L <mobs>.W { <mob id>.L <mob count>.W <mob name>.24B }*3
|
||||
void clif_quest_add(struct map_session_data * sd, struct quest * qd)
|
||||
void clif_quest_add(struct map_session_data *sd, struct quest *qd)
|
||||
{
|
||||
int fd = sd->fd;
|
||||
int i;
|
||||
@ -15674,15 +15689,15 @@ void clif_quest_add(struct map_session_data * sd, struct quest * qd)
|
||||
WFIFOW(fd, 0) = 0x2b3;
|
||||
WFIFOL(fd, 2) = qd->quest_id;
|
||||
WFIFOB(fd, 6) = qd->state;
|
||||
WFIFOB(fd, 7) = qi->time;
|
||||
WFIFOB(fd, 7) = qd->time - qi->time;
|
||||
WFIFOL(fd, 11) = qd->time;
|
||||
WFIFOW(fd, 15) = qi->num_objectives;
|
||||
WFIFOW(fd, 15) = qi->objectives_count;
|
||||
|
||||
for( i = 0; i < qi->num_objectives; i++ ) {
|
||||
for (i = 0; i < qi->objectives_count; i++) {
|
||||
struct mob_db *mob;
|
||||
WFIFOL(fd, i*30+17) = qi->mob[i];
|
||||
WFIFOL(fd, i*30+17) = qi->objectives[i].mob;
|
||||
WFIFOW(fd, i*30+21) = qd->count[i];
|
||||
mob = mob_db(qi->mob[i]);
|
||||
mob = mob_db(qi->objectives[i].mob);
|
||||
memcpy(WFIFOP(fd, i*30+23), mob->jname, NAME_LENGTH);
|
||||
}
|
||||
|
||||
@ -15692,7 +15707,7 @@ void clif_quest_add(struct map_session_data * sd, struct quest * qd)
|
||||
|
||||
/// Notification about a quest being removed (ZC_DEL_QUEST).
|
||||
/// 02b4 <quest id>.L
|
||||
void clif_quest_delete(struct map_session_data * sd, int quest_id)
|
||||
void clif_quest_delete(struct map_session_data *sd, int quest_id)
|
||||
{
|
||||
int fd = sd->fd;
|
||||
|
||||
@ -15705,22 +15720,22 @@ void clif_quest_delete(struct map_session_data * sd, int quest_id)
|
||||
|
||||
/// Notification of an update to the hunting mission counter (ZC_UPDATE_MISSION_HUNT).
|
||||
/// 02b5 <packet len>.W <mobs>.W { <quest id>.L <mob id>.L <total count>.W <current count>.W }*3
|
||||
void clif_quest_update_objective(struct map_session_data * sd, struct quest * qd)
|
||||
void clif_quest_update_objective(struct map_session_data *sd, struct quest *qd)
|
||||
{
|
||||
int fd = sd->fd;
|
||||
int i;
|
||||
struct quest_db *qi = quest_search(qd->quest_id);
|
||||
int len = qi->num_objectives * 12 + 6;
|
||||
int len = qi->objectives_count * 12 + 6;
|
||||
|
||||
WFIFOHEAD(fd, len);
|
||||
WFIFOW(fd, 0) = 0x2b5;
|
||||
WFIFOW(fd, 2) = len;
|
||||
WFIFOW(fd, 4) = qi->num_objectives;
|
||||
WFIFOW(fd, 4) = qi->objectives_count;
|
||||
|
||||
for( i = 0; i < qi->num_objectives; i++ ) {
|
||||
for (i = 0; i < qi->objectives_count; i++) {
|
||||
WFIFOL(fd, i*12+6) = qd->quest_id;
|
||||
WFIFOL(fd, i*12+10) = qi->mob[i];
|
||||
WFIFOW(fd, i*12+14) = qi->count[i];
|
||||
WFIFOL(fd, i*12+10) = qi->objectives[i].mob;
|
||||
WFIFOW(fd, i*12+14) = qi->objectives[i].count;
|
||||
WFIFOW(fd, i*12+16) = qd->count[i];
|
||||
}
|
||||
|
||||
@ -15730,7 +15745,7 @@ void clif_quest_update_objective(struct map_session_data * sd, struct quest * qd
|
||||
|
||||
/// Request to change the state of a quest (CZ_ACTIVE_QUEST).
|
||||
/// 02b6 <quest id>.L <active>.B
|
||||
void clif_parse_questStateAck(int fd, struct map_session_data * sd)
|
||||
void clif_parse_questStateAck(int fd, struct map_session_data *sd)
|
||||
{
|
||||
struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
|
||||
quest_update_status(sd, RFIFOL(fd,info->pos[0]),
|
||||
@ -15740,7 +15755,7 @@ void clif_parse_questStateAck(int fd, struct map_session_data * sd)
|
||||
|
||||
/// Notification about the change of a quest state (ZC_ACTIVE_QUEST).
|
||||
/// 02b7 <quest id>.L <active>.B
|
||||
void clif_quest_update_status(struct map_session_data * sd, int quest_id, bool active)
|
||||
void clif_quest_update_status(struct map_session_data *sd, int quest_id, bool active)
|
||||
{
|
||||
int fd = sd->fd;
|
||||
|
||||
|
@ -77,6 +77,7 @@ static char log_picktype2char(e_log_pick_type type)
|
||||
case LOG_TYPE_BOUND_REMOVAL: return 'F'; // Removed bound items when guild/party is broken
|
||||
case LOG_TYPE_ROULETTE: return 'Y'; // Roulette Lotter(Y)
|
||||
case LOG_TYPE_MERGE_ITEM: return 'Z'; // Merged Item
|
||||
case LOG_TYPE_QUEST: return 'Q'; // (Q)uest Item
|
||||
}
|
||||
|
||||
// should not get here, fallback
|
||||
|
@ -46,6 +46,7 @@ typedef enum e_log_pick_type
|
||||
LOG_TYPE_BOUND_REMOVAL = 0x080000,
|
||||
LOG_TYPE_ROULETTE = 0x100000,
|
||||
LOG_TYPE_MERGE_ITEM = 0x200000,
|
||||
LOG_TYPE_QUEST = 0x400000,
|
||||
// combinations
|
||||
LOG_TYPE_LOOT = LOG_TYPE_PICKDROP_MONSTER|LOG_TYPE_CONSUME,
|
||||
// all
|
||||
|
@ -80,6 +80,14 @@ struct s_mob_skill {
|
||||
};
|
||||
static DBMap *mob_skill_db; /// Monster skill temporary db. s_mob_skill -> mobid
|
||||
|
||||
struct mob_db *mobdb_exists(uint16 mob_id) {
|
||||
struct mob_db *db = mob_db(mob_id);
|
||||
|
||||
if (db == mob_dummy)
|
||||
return NULL;
|
||||
return db;
|
||||
}
|
||||
|
||||
static struct eri *item_drop_ers; //For loot drops delay structures.
|
||||
static struct eri *item_drop_list_ers;
|
||||
|
||||
|
@ -254,7 +254,8 @@ struct item_drop_list {
|
||||
struct item_drop* item; // linked list of drops
|
||||
};
|
||||
|
||||
struct mob_db* mob_db(int mob_id);
|
||||
struct mob_db *mob_db(int mob_id);
|
||||
struct mob_db *mobdb_exists(uint16 mob_id);
|
||||
int mobdb_searchname(const char *str);
|
||||
int mobdb_searchname_array(struct mob_db** data, int size, const char *str);
|
||||
int mobdb_checkid(const int id);
|
||||
|
226
src/map/quest.c
226
src/map/quest.c
@ -5,18 +5,22 @@
|
||||
#include "../common/socket.h"
|
||||
#include "../common/malloc.h"
|
||||
#include "../common/nullpo.h"
|
||||
#include "../common/random.h"
|
||||
#include "../common/showmsg.h"
|
||||
#include "../common/strlib.h"
|
||||
|
||||
#include "itemdb.h"
|
||||
#include "map.h"
|
||||
#include "pc.h"
|
||||
#include "party.h"
|
||||
#include "quest.h"
|
||||
#include "chrif.h"
|
||||
#include "intif.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
static DBMap *questdb;
|
||||
static void questdb_free_sub(struct quest_db *quest, bool free);
|
||||
|
||||
/**
|
||||
* Searches a quest by ID.
|
||||
@ -205,30 +209,30 @@ int quest_delete(TBL_PC *sd, int quest_id)
|
||||
int quest_update_objective_sub(struct block_list *bl, va_list ap)
|
||||
{
|
||||
struct map_session_data *sd;
|
||||
int mob, party;
|
||||
int mob_id, party_id;
|
||||
|
||||
nullpo_ret(bl);
|
||||
nullpo_ret(sd = (struct map_session_data *)bl);
|
||||
|
||||
party = va_arg(ap,int);
|
||||
mob = va_arg(ap,int);
|
||||
party_id = va_arg(ap,int);
|
||||
mob_id = va_arg(ap,int);
|
||||
|
||||
if( !sd->avail_quests )
|
||||
return 0;
|
||||
if( sd->status.party_id != party )
|
||||
if( sd->status.party_id != party_id )
|
||||
return 0;
|
||||
|
||||
quest_update_objective(sd, mob);
|
||||
quest_update_objective(sd, mob_id);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the quest objectives for a character after killing a monster.
|
||||
* Updates the quest objectives for a character after killing a monster, including the handling of quest-granted drops.
|
||||
* @param sd : Character's data
|
||||
* @param mob_id : Monster ID
|
||||
*/
|
||||
void quest_update_objective(TBL_PC *sd, int mob)
|
||||
void quest_update_objective(TBL_PC *sd, int mob_id)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
@ -240,13 +244,46 @@ void quest_update_objective(TBL_PC *sd, int mob)
|
||||
|
||||
qi = quest_search(sd->quest_log[i].quest_id);
|
||||
|
||||
for( j = 0; j < qi->num_objectives; j++ ) {
|
||||
if( qi->mob[j] == mob && sd->quest_log[i].count[j] < qi->count[j] ) {
|
||||
for( j = 0; j < qi->objectives_count; j++ ) {
|
||||
if( qi->objectives[j].mob == mob_id && sd->quest_log[i].count[j] < qi->objectives[j].count ) {
|
||||
sd->quest_log[i].count[j]++;
|
||||
sd->save_quest = true;
|
||||
clif_quest_update_objective(sd, &sd->quest_log[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// process quest-granted extra drop bonuses
|
||||
for (j = 0; j < qi->dropitem_count; j++) {
|
||||
struct quest_dropitem *dropitem = &qi->dropitem[j];
|
||||
struct item item;
|
||||
int temp;
|
||||
|
||||
if (dropitem->mob_id != 0 && dropitem->mob_id != mob_id)
|
||||
continue;
|
||||
// TODO: Should this be affected by server rates?
|
||||
if (dropitem->rate < 10000 && rnd()%10000 >= dropitem->rate)
|
||||
continue;
|
||||
if (!itemdb_exists(dropitem->nameid))
|
||||
continue;
|
||||
|
||||
memset(&item,0,sizeof(item));
|
||||
item.nameid = dropitem->nameid;
|
||||
item.identify = itemdb_isidentified(dropitem->nameid);
|
||||
item.amount = dropitem->count;
|
||||
#ifdef BOUND_ITEMS
|
||||
item.bound = dropitem->bound;
|
||||
#endif
|
||||
if (dropitem->isGUID)
|
||||
item.unique_id = pc_generate_unique_id(sd);
|
||||
if ((temp = pc_additem(sd, &item, 1, LOG_TYPE_QUEST)) != 0) // Failed to obtain the item
|
||||
clif_additem(sd, 0, 0, temp);
|
||||
else if (dropitem->isAnnounced) {
|
||||
char output[CHAT_SIZE_MAX];
|
||||
|
||||
sprintf(output, msg_txt(sd, 717), sd->status.name, itemdb_jname(item.nameid), StringBuf_Value(&qi->name));
|
||||
intif_broadcast(output, strlen(output) + 1, BC_DEFAULT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,7 +310,7 @@ int quest_update_status(TBL_PC *sd, int quest_id, enum quest_state status)
|
||||
sd->save_quest = true;
|
||||
|
||||
if( status < Q_COMPLETE ) {
|
||||
clif_quest_update_status(sd, quest_id, status == Q_ACTIVE);
|
||||
clif_quest_update_status(sd, quest_id, status == Q_ACTIVE ? true : false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -326,8 +363,8 @@ int quest_check(TBL_PC *sd, int quest_id, enum quest_check_type type)
|
||||
int j;
|
||||
struct quest_db *qi = quest_search(sd->quest_log[i].quest_id);
|
||||
|
||||
ARR_FIND(0, MAX_QUEST_OBJECTIVES, j, sd->quest_log[i].count[j] < qi->count[j]);
|
||||
if( j == MAX_QUEST_OBJECTIVES )
|
||||
ARR_FIND(0, qi->objectives_count, j, sd->quest_log[i].count[j] < qi->objectives[j].count);
|
||||
if( j == qi->objectives_count )
|
||||
return 2;
|
||||
if( sd->quest_log[i].time < (unsigned int)time(NULL) )
|
||||
return 1;
|
||||
@ -342,11 +379,12 @@ int quest_check(TBL_PC *sd, int quest_id, enum quest_check_type type)
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads quests from the quest db.
|
||||
* Loads quests from the quest db.txt
|
||||
* @return Number of loaded quests, or -1 if the file couldn't be read.
|
||||
*/
|
||||
int quest_read_db(void)
|
||||
int quest_read_txtdb(void)
|
||||
{
|
||||
uint32 count = 0;
|
||||
const char* dbsubpath[] = {
|
||||
"",
|
||||
DBIMPORT"/",
|
||||
@ -356,71 +394,106 @@ int quest_read_db(void)
|
||||
for (f = 0; f < ARRAYLENGTH(dbsubpath); f++) {
|
||||
FILE *fp;
|
||||
char line[1024];
|
||||
uint32 count = 0;
|
||||
uint32 ln = 0;
|
||||
char filename[256];
|
||||
|
||||
sprintf(filename, "%s/%s%s", db_path, dbsubpath[f], "quest_db.txt");
|
||||
if( (fp = fopen(filename, "r")) == NULL ) {
|
||||
if ((fp = fopen(filename, "r")) == NULL) {
|
||||
if (f == 0)
|
||||
ShowError("Can't read %s\n", filename);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
while( fgets(line, sizeof(line), fp) ) {
|
||||
struct quest_db *quest = NULL, entry;
|
||||
char *str[9], *p, *np;
|
||||
while(fgets(line, sizeof(line), fp)) {
|
||||
struct quest_db *quest = NULL;
|
||||
char *str[19], *p;
|
||||
uint16 quest_id = 0;
|
||||
uint8 i;
|
||||
|
||||
if( line[0] == '/' && line[1] == '/' )
|
||||
++ln;
|
||||
if (line[0] == '\0' || (line[0] == '/' && line[1] == '/'))
|
||||
continue;
|
||||
|
||||
p = trim(line);
|
||||
|
||||
if (*p == '\0')
|
||||
continue; // empty line
|
||||
|
||||
memset(str, 0, sizeof(str));
|
||||
for(i = 0, p = line; i < 18 && p; i++) {
|
||||
str[i] = p;
|
||||
p = strchr(p,',');
|
||||
if (p)
|
||||
*p++ = 0;
|
||||
}
|
||||
if (str[0] == NULL)
|
||||
continue;
|
||||
if (i < 18) {
|
||||
ShowError("quest_read_txtdb: Insufficient columns in line %d (%d of %d)\n", ln, i, 18);
|
||||
continue;
|
||||
}
|
||||
|
||||
for( i = 0, p = line; i < 8; i++ ) {
|
||||
if( (np = strchr(p, ',')) != NULL ) {
|
||||
str[i] = p;
|
||||
*np = 0;
|
||||
p = np + 1;
|
||||
} else if( str[0] == NULL )
|
||||
break;
|
||||
else {
|
||||
ShowError("quest_read_db: Insufficient columns in line %s\n", line);
|
||||
quest_id = atoi(str[0]);
|
||||
|
||||
if (quest_id < 0 || quest_id >= INT_MAX) {
|
||||
ShowError("quest_read_txtdb: Invalid quest ID '%d' in line '%s' (min: 0, max: %d.)\n", quest_id, ln, INT_MAX);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(quest = (struct quest_db *)idb_get(questdb, quest_id)))
|
||||
CREATE(quest, struct quest_db, 1);
|
||||
else {
|
||||
if (quest->objectives) {
|
||||
aFree(quest->objectives);
|
||||
quest->objectives = NULL;
|
||||
quest->objectives_count = 0;
|
||||
}
|
||||
if (quest->dropitem) {
|
||||
aFree(quest->dropitem);
|
||||
quest->dropitem = NULL;
|
||||
quest->dropitem_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
quest->time = atoi(str[1]);
|
||||
|
||||
for(i = 0; i < MAX_QUEST_OBJECTIVES; i++) {
|
||||
uint16 mob_id = (uint16)atoi(str[2 * i + 2]);
|
||||
|
||||
if (!mob_id)
|
||||
continue;
|
||||
if (mobdb_exists(mob_id) == NULL) {
|
||||
ShowWarning("quest_read_txtdb: Invalid monster as objective '%d' in line %d.\n", mob_id, ln);
|
||||
continue;
|
||||
}
|
||||
RECREATE(quest->objectives, struct quest_objective, quest->objectives_count++);
|
||||
quest->objectives[quest->objectives_count].mob = mob_id;
|
||||
quest->objectives[quest->objectives_count].count = (uint16)atoi(str[2 * i + 3]);
|
||||
quest->objectives_count++;
|
||||
}
|
||||
|
||||
if( str[0] == NULL )
|
||||
continue;
|
||||
for(i = 0; i < MAX_QUEST_DROPS; i++) {
|
||||
uint16 mob_id = (uint16)atoi(str[3 * i + (2 * MAX_QUEST_OBJECTIVES + 2)]), nameid = (uint16)atoi(str[3 * i + (2 * MAX_QUEST_OBJECTIVES + 3)]);
|
||||
|
||||
memset(&entry, 0, sizeof(struct quest_db));
|
||||
entry.id = atoi(str[0]);
|
||||
|
||||
if( entry.id < 0 || entry.id >= INT_MAX ) {
|
||||
ShowError("quest_read_db: Invalid quest ID '%d' in line '%s' (min: 0, max: %d.)\n", entry.id, line, INT_MAX);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(quest = (struct quest_db *)idb_get(questdb, entry.id))) {
|
||||
CREATE(quest, struct quest_db, 1);
|
||||
}
|
||||
|
||||
entry.time = atoi(str[1]);
|
||||
|
||||
for( i = 0; i < MAX_QUEST_OBJECTIVES; i++ ) {
|
||||
entry.mob[i] = (uint16)atoi(str[2 * i + 2]);
|
||||
entry.count[i] = (uint16)atoi(str[2 * i + 3]);
|
||||
|
||||
if( !entry.mob[i] || !entry.count[i] )
|
||||
break;
|
||||
}
|
||||
entry.num_objectives = i;
|
||||
if (!nameid)
|
||||
continue;
|
||||
if (!itemdb_exists(nameid) || (mob_id && mobdb_exists(mob_id) == NULL)) {
|
||||
ShowWarning("quest_read_txtdb: Invalid item reward '%d' (mob %d, optional) in line %d.\n", nameid, mob_id, ln);
|
||||
continue;
|
||||
}
|
||||
RECREATE(quest->dropitem, struct quest_dropitem, quest->dropitem_count++);
|
||||
quest->dropitem[quest->dropitem_count].mob_id = mob_id;
|
||||
quest->dropitem[quest->dropitem_count].nameid = nameid;
|
||||
quest->dropitem[quest->dropitem_count].rate = atoi(str[3 * i + (2 * MAX_QUEST_OBJECTIVES + 4)]);
|
||||
quest->dropitem_count++;
|
||||
}
|
||||
|
||||
//StringBuf_Init(&entry.name);
|
||||
//StringBuf_Printf(&entry.name, "%s", str[7]);
|
||||
//StringBuf_Printf(&entry.name, "%s", str[17]);
|
||||
|
||||
if (!quest->id) {
|
||||
memcpy(quest, &entry, sizeof(entry));
|
||||
quest->id = quest_id;
|
||||
idb_put(questdb, quest->id, quest);
|
||||
}
|
||||
count++;
|
||||
@ -430,7 +503,15 @@ int quest_read_db(void)
|
||||
ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filename);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads Quest DB
|
||||
*/
|
||||
static void quest_read_db(void)
|
||||
{
|
||||
quest_read_txtdb();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -471,17 +552,40 @@ int quest_reload_check_sub(struct map_session_data *sd, va_list ap)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear quest single entry
|
||||
* @param quest
|
||||
* @param free Will free quest from memory
|
||||
**/
|
||||
static void questdb_free_sub(struct quest_db *quest, bool free)
|
||||
{
|
||||
if (quest->objectives) {
|
||||
aFree(quest->objectives);
|
||||
quest->objectives = NULL;
|
||||
quest->objectives_count = 0;
|
||||
}
|
||||
if (quest->dropitem) {
|
||||
aFree(quest->dropitem);
|
||||
quest->dropitem = NULL;
|
||||
quest->dropitem_count = 0;
|
||||
}
|
||||
if (&quest->name)
|
||||
StringBuf_Destroy(&quest->name);
|
||||
if (free)
|
||||
aFree(quest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the quest database for shutdown or reload.
|
||||
*/
|
||||
|
||||
static int questdb_free(DBKey key, DBData *data, va_list ap) {
|
||||
static int questdb_free(DBKey key, DBData *data, va_list ap)
|
||||
{
|
||||
struct quest_db *quest = db_data2ptr(data);
|
||||
|
||||
if (!quest)
|
||||
return 0;
|
||||
//if (&quest->name)
|
||||
// StringBuf_Destroy(&quest->name);
|
||||
aFree(quest);
|
||||
|
||||
questdb_free_sub(quest, true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -6,13 +6,29 @@
|
||||
|
||||
#define MAX_QUEST_DB (62238 + 1) // Highest quest ID + 1
|
||||
|
||||
struct quest_dropitem {
|
||||
uint16 nameid;
|
||||
uint16 count;
|
||||
uint16 rate;
|
||||
uint16 mob_id;
|
||||
uint8 bound;
|
||||
bool isAnnounced;
|
||||
bool isGUID;
|
||||
};
|
||||
|
||||
struct quest_objective {
|
||||
uint16 mob;
|
||||
uint16 count;
|
||||
};
|
||||
|
||||
struct quest_db {
|
||||
int id;
|
||||
uint16 id;
|
||||
unsigned int time;
|
||||
uint16 mob[MAX_QUEST_OBJECTIVES];
|
||||
uint16 count[MAX_QUEST_OBJECTIVES];
|
||||
uint8 num_objectives;
|
||||
//StringBuf name;
|
||||
uint8 objectives_count;
|
||||
struct quest_objective *objectives;
|
||||
uint8 dropitem_count;
|
||||
struct quest_dropitem *dropitem;
|
||||
StringBuf name;
|
||||
};
|
||||
|
||||
struct quest_db quest_dummy; ///< Dummy entry for invalid quest lookups
|
||||
@ -30,7 +46,7 @@ int quest_add(TBL_PC * sd, int quest_id);
|
||||
int quest_delete(TBL_PC * sd, int quest_id);
|
||||
int quest_change(TBL_PC * sd, int qid1, int qid2);
|
||||
int quest_update_objective_sub(struct block_list *bl, va_list ap);
|
||||
void quest_update_objective(TBL_PC * sd, int mob);
|
||||
void quest_update_objective(TBL_PC * sd, int mob_id);
|
||||
int quest_update_status(TBL_PC * sd, int quest_id, enum quest_state status);
|
||||
int quest_check(TBL_PC * sd, int quest_id, enum quest_check_type type);
|
||||
void quest_clear(void);
|
||||
|
Loading…
x
Reference in New Issue
Block a user