Rewrote Quest Log system.

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@13959 54d463be-8e91-2dee-dedb-b68131a5f0ec
This commit is contained in:
Inkfish 2009-07-25 20:55:35 +00:00
parent caadb5a546
commit 698e7a0fc0
21 changed files with 531 additions and 289 deletions

View File

@ -3,6 +3,8 @@ Date Added
AS OF SVN REV. 5091, WE ARE NOW USING TRUNK. ALL UNTESTED BUGFIXES/FEATURES GO INTO TRUNK. AS OF SVN REV. 5091, WE ARE NOW USING TRUNK. ALL UNTESTED BUGFIXES/FEATURES GO INTO TRUNK.
IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK. IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK.
09/07/25
* Rewrote Quest Log system.
09/07/21 09/07/21
* Added proper bounds of INT_MIN to INT_MAX for hp/sp when being sent to/from status_damage/status_heal for negative values because of INT_MAX being (-INT_MIN - 1). [Paradox924X] * Added proper bounds of INT_MIN to INT_MAX for hp/sp when being sent to/from status_damage/status_heal for negative values because of INT_MAX being (-INT_MIN - 1). [Paradox924X]
09/07/17 09/07/17

View File

@ -120,8 +120,7 @@ pet_db: pet
friend_db: friends friend_db: friends
mail_db: mail mail_db: mail
auction_db: auction auction_db: auction
quest_db: quest quest_db: questlog
quest_obj_db: quest_objective
// Map Database Tables // Map Database Tables
item_db_db: item_db item_db_db: item_db

View File

@ -1781,3 +1781,7 @@ TW_TOWER 812
4_M_MOCASS1 997 4_M_MOCASS1 997
4_M_MOCASS2 998 4_M_MOCASS2 998
4_M_MUT1 999 4_M_MUT1 999
HAVEQUEST 0
PLAYTIME 1
HUNTING 2

View File

@ -1003,7 +1003,7 @@ packet_ver: 22
0x02b3,107 0x02b3,107
0x02b4,6 0x02b4,6
0x02b5,-1 0x02b5,-1
0x02b6,7 0x02b6,7,queststate,2:6
0x02b7,7 0x02b7,7
0x02b8,22 0x02b8,22
0x02b9,191 0x02b9,191
@ -1017,7 +1017,7 @@ packet_ver: 22
0x02c4,26,partyinvite2,2 0x02c4,26,partyinvite2,2
0x02c5,30 0x02c5,30
0x02c6,30 0x02c6,30
0x02c7,7,replypartyinvite2,2,6 0x02c7,7,replypartyinvite2,2:6
0x02c8,3 0x02c8,3
0x02c9,3 0x02c9,3
0x02ca,3 0x02ca,3

View File

@ -130,6 +130,8 @@
//= Extended the behaviour of duplicates (warps/shops/cashshops). [FlavioJS] //= Extended the behaviour of duplicates (warps/shops/cashshops). [FlavioJS]
//= 3.26.20090702 //= 3.26.20090702
//= Replaced 'bonusautoscript' by 'autobonus'. [Inkfish] //= Replaced 'bonusautoscript' by 'autobonus'. [Inkfish]
//= 3.27.20090725
//= Added Quest Log related commands. [Inkfish]
//========================================================= //=========================================================
This document is a reference manual for all the scripting commands and functions This document is a reference manual for all the scripting commands and functions
@ -6527,5 +6529,57 @@ instance times out while inactive.
--------------------------------------- ---------------------------------------
========================
|8.- Quest Log commands.|
========================
---------------------------------------
*setquest <ID>;
Place quest of <ID> in the users quest log, the state of which is "active".
---------------------------------------
*completequest <ID>;
Change the state for the given quest <ID> to "complete" and remove from the users quest log.
---------------------------------------
*erasequest <ID>;
Remove the quest of the given <ID> from the user's quest log.
---------------------------------------
*changequest <ID>,<ID2>;
Remove quest of the given <ID> from the user's quest log, and change state to "complete".
Add quest of the <ID2> to the the quest log, and the state is "active".
---------------------------------------
checkquest(<ID>{,PLAYTIME|HUNTING});
If no additonal argument supplied, return the state of the quest:
-1 = Quest not started (not in quest log)
0 = Quest has been given, but the state is "inactive"
1 = Quest has been given, and the state is "active"
2 = Quest comepleted
If parameter "PLAYTIME" is supplied:
-1 = Quest not started (not in quest log)
0 = the time limit has not yet been reached
1 = the time limit has not been reached but the quest is marked as complete
2 = the time limit has been reached
If parameter "HUNTING" is supplied:
-1 = Quest not started (not in quest log)
0 = you haven't killed all of the target monsters and the time limit has not been reached.
1 = you haven't killed all of the target monsters but the time limit has been reached.
2 = you've killed all of the target monsters
---------------------------------------
Whew. Whew.
That's about all of them. That's about all of them.

View File

@ -58,8 +58,7 @@ char mail_db[256] = "mail"; // MAIL SYSTEM
char auction_db[256] = "auction"; // Auctions System char auction_db[256] = "auction"; // Auctions System
char friend_db[256] = "friends"; char friend_db[256] = "friends";
char hotkey_db[256] = "hotkey"; char hotkey_db[256] = "hotkey";
char quest_db[256] = "quest"; char quest_db[256] = "questlog";
char quest_obj_db[256] = "quest_objective";
//If your code editor is having problems syntax highlighting this file, uncomment this and RECOMMENT IT BEFORE COMPILING //If your code editor is having problems syntax highlighting this file, uncomment this and RECOMMENT IT BEFORE COMPILING
//#undef TXT_SQL_CONVERT //#undef TXT_SQL_CONVERT
@ -3639,8 +3638,6 @@ void sql_config_read(const char* cfgName)
strcpy(hotkey_db,w2); strcpy(hotkey_db,w2);
else if(!strcmpi(w1,"quest_db")) else if(!strcmpi(w1,"quest_db"))
strcpy(quest_db,w2); strcpy(quest_db,w2);
else if(!strcmpi(w1,"quest_obj_db"))
strcpy(quest_obj_db, w2);
#ifndef TXT_SQL_CONVERT #ifndef TXT_SQL_CONVERT
else if(!strcmpi(w1,"db_path")) else if(!strcmpi(w1,"db_path"))
strcpy(db_path,w2); strcpy(db_path,w2);

View File

@ -59,7 +59,6 @@ extern char pet_db[256];
extern char mail_db[256]; extern char mail_db[256];
extern char auction_db[256]; extern char auction_db[256];
extern char quest_db[256]; extern char quest_db[256];
extern char quest_obj_db[256];
extern int db_use_sqldbs; // added for sql item_db read for char server [Valaris] extern int db_use_sqldbs; // added for sql item_db read for char server [Valaris]

View File

@ -20,10 +20,8 @@
//Load entire questlog for a character //Load entire questlog for a character
int mapif_quests_fromsql(int char_id, struct quest questlog[]) int mapif_quests_fromsql(int char_id, struct quest questlog[])
{ {
int i;
int count, i, j, num;
struct quest tmp_quest; struct quest tmp_quest;
struct quest_objective tmp_quest_objective;
SqlStmt * stmt; SqlStmt * stmt;
stmt = SqlStmt_Malloc(sql_handle); stmt = SqlStmt_Malloc(sql_handle);
@ -34,42 +32,29 @@ int mapif_quests_fromsql(int char_id, struct quest questlog[])
} }
memset(&tmp_quest, 0, sizeof(struct quest)); memset(&tmp_quest, 0, sizeof(struct quest));
memset(&tmp_quest_objective, 0, sizeof(struct quest_objective));
if( SQL_ERROR == SqlStmt_Prepare(stmt, "SELECT `quest_id`, `state` FROM `%s` WHERE `char_id`=? LIMIT %d", quest_db, MAX_QUEST) if( SQL_ERROR == SqlStmt_Prepare(stmt, "SELECT `quest_id`, `state`, `time`, `mob1`, `count1`, `mob2`, `count2`, `mob3`, `count3` FROM `%s` WHERE `char_id`=? LIMIT %d", quest_db, MAX_QUEST)
|| SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0) || SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0)
|| SQL_ERROR == SqlStmt_Execute(stmt) || SQL_ERROR == SqlStmt_Execute(stmt)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 0, SQLDT_INT, &tmp_quest.quest_id, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 0, SQLDT_INT, &tmp_quest.quest_id, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 1, SQLDT_INT, &tmp_quest.state, 0, NULL, NULL) ) || SQL_ERROR == SqlStmt_BindColumn(stmt, 1, SQLDT_INT, &tmp_quest.state, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 2, SQLDT_UINT, &tmp_quest.time, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 3, SQLDT_INT, &tmp_quest.mob[0], 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 4, SQLDT_INT, &tmp_quest.count[0], 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 5, SQLDT_INT, &tmp_quest.mob[1], 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 6, SQLDT_INT, &tmp_quest.count[1], 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 7, SQLDT_INT, &tmp_quest.mob[2], 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 8, SQLDT_INT, &tmp_quest.count[2], 0, NULL, NULL) )
SqlStmt_ShowDebug(stmt); SqlStmt_ShowDebug(stmt);
for( i = 0; i < MAX_QUEST && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i ) for( i = 0; i < MAX_QUEST && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i )
{ {
memcpy(&questlog[i], &tmp_quest, sizeof(tmp_quest)); memcpy(&questlog[i], &tmp_quest, sizeof(tmp_quest));
questlog[i].num_objectives = (!questlog[i].mob[0] ? 0 : !questlog[i].mob[1] ? 1 : !questlog[i].mob[2] ? 2 : 3);
} }
count = i;
for( i = 0; i < count; ++i )
{
if( SQL_ERROR == SqlStmt_Prepare(stmt, "SELECT `num`, `name`, `count` FROM `%s` WHERE `char_id`=? AND `quest_id`=? LIMIT %d", quest_obj_db, MAX_QUEST_OBJECTIVES)
|| SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0)
|| SQL_ERROR == SqlStmt_BindParam(stmt, 1, SQLDT_INT, &questlog[i].quest_id, 0)
|| SQL_ERROR == SqlStmt_Execute(stmt)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 0, SQLDT_INT, &num, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 1, SQLDT_STRING, &tmp_quest_objective.name, NAME_LENGTH, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 2, SQLDT_INT, &tmp_quest_objective.count, 0, NULL, NULL) )
SqlStmt_ShowDebug(stmt);
for( j = 0; j < MAX_QUEST && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++j )
{
memcpy(&questlog[i].objectives[num], &tmp_quest_objective, sizeof(struct quest_objective));
}
questlog[i].num_objectives = j;
}
SqlStmt_Free(stmt); SqlStmt_Free(stmt);
return count; return i;
} }
//Delete a quest //Delete a quest
@ -86,12 +71,6 @@ int mapif_parse_quest_delete(int fd)
success = false; success = false;
} }
if ( success && SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `quest_id` = '%d' AND `char_id` = '%d'", quest_obj_db, quest_id, char_id) )
{
Sql_ShowDebug(sql_handle);
success = false;
}
WFIFOHEAD(fd,11); WFIFOHEAD(fd,11);
WFIFOW(fd,0) = 0x3862; WFIFOW(fd,0) = 0x3862;
WFIFOL(fd,2) = char_id; WFIFOL(fd,2) = char_id;
@ -111,13 +90,11 @@ int mapif_parse_quest_add(int fd)
bool success = true; bool success = true;
int char_id = RFIFOL(fd,4); int char_id = RFIFOL(fd,4);
struct quest qd; struct quest qd;
int i;
memcpy(&qd, RFIFOP(fd,8), RFIFOW(fd,2)-8); memcpy(&qd, RFIFOP(fd,8), RFIFOW(fd,2)-8);
StringBuf_Init(&buf); StringBuf_Init(&buf);
StringBuf_Printf(&buf, "INSERT INTO `%s`(`quest_id`, `char_id`, `state`) VALUES ('%d', '%d', '%d')", quest_db, qd.quest_id, char_id, qd.state); StringBuf_Printf(&buf, "INSERT INTO `%s`(`quest_id`, `char_id`, `state`, `time`, `mob1`, `count1`, `mob2`, `count2`, `mob3`, `count3`) VALUES ('%d', '%d', '%d','%d', '%d', '%d', '%d', '%d', '%d', '%d')", quest_db, qd.quest_id, char_id, qd.state, qd.time, qd.mob[0], qd.count[0], qd.mob[1], qd.count[1], qd.mob[2], qd.count[2]);
if ( SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) ) if ( SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) )
{ {
@ -125,20 +102,6 @@ int mapif_parse_quest_add(int fd)
success = false; success = false;
} }
for(i=0; i<qd.num_objectives && success; i++)
{
StringBuf_Clear(&buf);
StringBuf_Printf(&buf, "INSERT INTO `%s`(`quest_id`, `char_id`, `num`, `name`, `count`) VALUES ('%d', '%d', '%d', '%s', '%d')",
quest_obj_db, qd.quest_id, char_id, i, qd.objectives[i].name, qd.objectives[i].count);
if ( success && SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) )
{
Sql_ShowDebug(sql_handle);
success = false;
}
}
WFIFOHEAD(fd,11); WFIFOHEAD(fd,11);
WFIFOW(fd,0) = 0x3861; WFIFOW(fd,0) = 0x3861;
WFIFOL(fd,2) = char_id; WFIFOL(fd,2) = char_id;
@ -152,17 +115,48 @@ int mapif_parse_quest_add(int fd)
} }
//Update a questlog
int mapif_parse_quest_update(int fd)
{
StringBuf buf;
bool success = true;
int char_id = RFIFOL(fd,4);
struct quest qd;
memcpy(&qd, RFIFOP(fd,8), RFIFOW(fd,2)-8);
StringBuf_Init(&buf);
StringBuf_Printf(&buf, "UPDATE `%s` SET `state`='%d', `count1`='%d', `count2`='%d', `count3`='%d' WHERE `quest_id` = '%d' AND `char_id` = '%d'", quest_db, qd.state, qd.count[0], qd.count[1], qd.count[2], qd.quest_id, char_id);
if ( SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) )
{
Sql_ShowDebug(sql_handle);
success = false;
}
WFIFOHEAD(fd,11);
WFIFOW(fd,0) = 0x3863;
WFIFOL(fd,2) = char_id;
WFIFOL(fd,6) = qd.quest_id;
WFIFOB(fd,10) = success?1:0;
WFIFOSET(fd,11);
StringBuf_Destroy(&buf);
return 0;
}
//Send questlog to map server //Send questlog to map server
int mapif_send_quests(int fd, int char_id) int mapif_send_quests(int fd, int char_id)
{ {
struct quest tmp_questlog[MAX_QUEST]; struct quest tmp_questlog[MAX_QUEST];
int num_quests, i; int num_quests, i, num_complete = 0;
int complete[MAX_QUEST];
for(i=0; i<MAX_QUEST; i++) memset(tmp_questlog, 0, sizeof(tmp_questlog));
{ memset(complete, 0, sizeof(complete));
memset(&tmp_questlog[i], 0, sizeof(struct quest));
}
num_quests = mapif_quests_fromsql(char_id, tmp_questlog); num_quests = mapif_quests_fromsql(char_id, tmp_questlog);
@ -171,10 +165,20 @@ int mapif_send_quests(int fd, int char_id)
WFIFOW(fd,2) = num_quests*sizeof(struct quest)+8; WFIFOW(fd,2) = num_quests*sizeof(struct quest)+8;
WFIFOL(fd,4) = char_id; WFIFOL(fd,4) = char_id;
for(i=0; i<num_quests; i++) //Active and inactive quests
for( i = 0; i < num_quests; i++ )
{ {
memcpy(WFIFOP(fd,i*sizeof(struct quest)+8), &tmp_questlog[i], sizeof(struct quest)); if( tmp_questlog[i].state == Q_COMPLETE )
{
complete[num_complete++] = i;
continue;
} }
memcpy(WFIFOP(fd,(i-num_complete)*sizeof(struct quest)+8), &tmp_questlog[i], sizeof(struct quest));
}
// Completed quests
for( i = num_quests - num_complete; i < num_quests; i++ )
memcpy(WFIFOP(fd,i*sizeof(struct quest)+8), &tmp_questlog[complete[i-num_quests+num_complete]], sizeof(struct quest));
WFIFOSET(fd,num_quests*sizeof(struct quest)+8); WFIFOSET(fd,num_quests*sizeof(struct quest)+8);
@ -196,6 +200,7 @@ int inter_quest_parse_frommap(int fd)
case 0x3060: mapif_parse_loadquestrequest(fd); break; case 0x3060: mapif_parse_loadquestrequest(fd); break;
case 0x3061: mapif_parse_quest_add(fd); break; case 0x3061: mapif_parse_quest_add(fd); break;
case 0x3062: mapif_parse_quest_delete(fd); break; case 0x3062: mapif_parse_quest_delete(fd); break;
case 0x3063: mapif_parse_quest_update(fd); break;
default: default:
return 0; return 0;
} }

View File

@ -51,7 +51,7 @@ int inter_recv_packet_length[] = {
-1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 18,19,186,-1, // 3030- -1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 18,19,186,-1, // 3030-
5, 9, 0, 0, 0, 0, 0, 0, 7, 6,10,10, 10,-1, 0, 0, // 3040- 5, 9, 0, 0, 0, 0, 0, 0, 7, 6,10,10, 10,-1, 0, 0, // 3040-
-1,-1,10,10, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3050- Auction System [Zephyrus] -1,-1,10,10, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3050- Auction System [Zephyrus]
6,-1,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3060- Quest system [Kevin] 6,-1,10,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3060- Quest system [Kevin] [Inkfish]
-1,10, 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3070- Mercenary packets [Zephyrus] -1,10, 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3070- Mercenary packets [Zephyrus]
48,14,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3080- 48,14,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3080-
-1,10,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3090- Homunculus packets [albator] -1,10,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3090- Homunculus packets [albator]

View File

@ -50,7 +50,7 @@
#define MAX_GUILDCASTLE 34 // Updated to include new entries for WoE:SE. [L0ne_W0lf] #define MAX_GUILDCASTLE 34 // Updated to include new entries for WoE:SE. [L0ne_W0lf]
#define MAX_GUILDLEVEL 50 #define MAX_GUILDLEVEL 50
#define MAX_GUARDIANS 8 //Local max per castle. [Skotlex] #define MAX_GUARDIANS 8 //Local max per castle. [Skotlex]
#define MAX_QUEST 25 //Max quests for a PC #define MAX_QUEST_DB 500 //Max quests that the server will load
#define MAX_QUEST_OBJECTIVES 3 //Max quest objectives for a quest #define MAX_QUEST_OBJECTIVES 3 //Max quest objectives for a quest
#define MIN_HAIR_STYLE battle_config.min_hair_style #define MIN_HAIR_STYLE battle_config.min_hair_style
@ -126,24 +126,17 @@ enum item_types {
}; };
//Questlog system [Kevin] //Questlog system [Kevin] [Inkfish]
typedef enum quest_state { Q_INACTIVE, Q_ACTIVE } quest_state; typedef enum quest_state { Q_INACTIVE, Q_ACTIVE, Q_COMPLETE } quest_state;
struct quest_objective {
char name[NAME_LENGTH];
int count;
};
struct quest { struct quest {
int quest_id; int quest_id;
unsigned int time;
int mob[MAX_QUEST_OBJECTIVES];
int count[MAX_QUEST_OBJECTIVES];
quest_state state; quest_state state;
int num_objectives;
int time;
struct quest_objective objectives[MAX_QUEST_OBJECTIVES];
int num_objectives;
}; };
struct item { struct item {

View File

@ -288,6 +288,8 @@ int chrif_save(struct map_session_data *sd, int flag)
merc_save(sd->hd); merc_save(sd->hd);
if( sd->md && mercenary_get_lifetime(sd->md) > 0 ) if( sd->md && mercenary_get_lifetime(sd->md) > 0 )
mercenary_save(sd->md); mercenary_save(sd->md);
if( sd->num_quests )
quest_save(sd);
return 0; return 0;
} }

View File

@ -8189,8 +8189,6 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
mail_clear(sd); mail_clear(sd);
#endif #endif
quest_pc_login(sd);
if(map[sd->bl.m].flag.loadevent) // Lance if(map[sd->bl.m].flag.loadevent) // Lance
npc_script_event(sd, NPCE_LOADMAP); npc_script_event(sd, NPCE_LOADMAP);
@ -12548,39 +12546,28 @@ void clif_parse_EquipTick(int fd, struct map_session_data* sd)
} }
/*========================================== /*==========================================
* Questlog System [Kevin] * Questlog System [Kevin] [Inkfish]
* 02B5 <packet_len>.W <ignored>.L { }.10B* <-- UNKOWN PACKET
* 02B6 <quest_id>.L <state>.B
*------------------------------------------*/ *------------------------------------------*/
void clif_parse_questStateAck(int fd, struct map_session_data * sd)
{
}
//Send simple list of quests upon login //Send simple list of quests upon login
//* 02B1 <packet_len>.W <ignored>.L { <quest_id>.L <state>.B }.5B* //* 02B1 <packet_len>.W <ignored>.L { <quest_id>.L <state>.B }.5B*
void clif_send_questlog(struct map_session_data * sd) void clif_send_questlog(struct map_session_data * sd)
{ {
int fd = sd->fd; int fd = sd->fd;
int i; int i;
int len = sd->avail_quests*5+8;
WFIFOHEAD(fd,sd->num_quests*5+8); WFIFOHEAD(fd,len);
WFIFOW(fd, 0) = 0x02B1; WFIFOW(fd, 0) = 0x02B1;
WFIFOW(fd, 2) = sd->num_quests*5+8; WFIFOW(fd, 2) = len;
WFIFOL(fd, 4) = sd->avail_quests;
for(i=0; i<MAX_QUEST; i++) for( i = 0; i < sd->avail_quests; i++ )
{ {
if(!sd->quest_log[i].quest_id)
continue;
WFIFOL(fd, i*5+8) = sd->quest_log[i].quest_id; WFIFOL(fd, i*5+8) = sd->quest_log[i].quest_id;
WFIFOB(fd, i*5+12) = sd->quest_log[i].state; WFIFOB(fd, i*5+12) = sd->quest_log[i].state;
} }
WFIFOSET(fd, WFIFOW(fd, 2)); WFIFOSET(fd, len);
} }
@ -12590,31 +12577,29 @@ void clif_send_questlog_info(struct map_session_data * sd)
{ {
int fd = sd->fd; int fd = sd->fd;
int i, j; int i, j;
int len = sd->avail_quests*104+8;
struct mob_db *mob;
WFIFOHEAD(fd,sd->num_quests*104+8); WFIFOHEAD(fd, len);
WFIFOW(fd, 0) = 0x02B2; WFIFOW(fd, 0) = 0x02B2;
WFIFOW(fd, 2) = sd->num_quests*104+8; WFIFOW(fd, 2) = len;
WFIFOL(fd, 4) = sd->avail_quests;
for(i=0; i<MAX_QUEST; i++) for( i = 0; i < sd->avail_quests; i++ )
{ {
if(!sd->quest_log[i].quest_id)
continue;
WFIFOL(fd, i*104+8) = sd->quest_log[i].quest_id; WFIFOL(fd, i*104+8) = sd->quest_log[i].quest_id;
WFIFOL(fd, i*104+16) = sd->quest_log[i].time;
// I have no idea what the time field does [Kevin]
WFIFOL(fd, i*104+16) = 0;
WFIFOW(fd, i*104+20) = sd->quest_log[i].num_objectives; WFIFOW(fd, i*104+20) = sd->quest_log[i].num_objectives;
for(j=0; j<sd->quest_log[i].num_objectives; j++) for( j = 0 ; j < sd->quest_log[i].num_objectives; j++ )
{ {
WFIFOW(fd, i*104+26+j*30) = sd->quest_log[i].objectives[j].count; WFIFOW(fd, i*104+26+j*30) = sd->quest_log[i].count[j];
memcpy(WFIFOP(fd, i*104+28+j*30), sd->quest_log[i].objectives[j].name, NAME_LENGTH); mob = mob_db(sd->quest_log[i].mob[j]);
memcpy(WFIFOP(fd, i*104+28+j*30), mob?mob->jname:"NULL", NAME_LENGTH);
}
} }
} WFIFOSET(fd, len);
WFIFOSET(fd, WFIFOW(fd, 2));
} }
//Send info when objective info needs an update //Send info when objective info needs an update
@ -12623,23 +12608,24 @@ void clif_send_quest_info(struct map_session_data * sd, struct quest * qd)
{ {
int fd = sd->fd; int fd = sd->fd;
int i; int i;
//int len = MAX_QUEST_OBJECTIVES*30+17;
struct mob_db *mob;
WFIFOHEAD(fd,qd->num_objectives*30+17); WFIFOHEAD(fd, packet_len(0x02B3));
WFIFOW(fd, 0) = 0x02B3; WFIFOW(fd, 0) = 0x02B3;
WFIFOL(fd, 2) = qd->quest_id; WFIFOL(fd, 2) = qd->quest_id;
WFIFOB(fd, 6) = qd->state; WFIFOB(fd, 6) = qd->state;
WFIFOL(fd, 11) = qd->time;
//Same time value thing
WFIFOW(fd, 11) = 0;
WFIFOW(fd, 15) = qd->num_objectives; WFIFOW(fd, 15) = qd->num_objectives;
for(i=0; i<qd->num_objectives; i++) for( i = 0; i < qd->num_objectives; i++ )
{ {
WFIFOW(fd, i*30+21) = qd->objectives[i].count; WFIFOW(fd, i*30+21) = qd->count[i];
memcpy(WFIFOP(fd, i*30+23), qd->objectives[i].name, NAME_LENGTH); mob = mob_db(qd->mob[i]);
memcpy(WFIFOP(fd, i*30+23), mob?mob->jname:"NULL", NAME_LENGTH);
} }
WFIFOSET(fd, qd->num_objectives*30+17); WFIFOSET(fd, packet_len(0x02B3));
} }
//Send delete msg //Send delete msg
@ -12648,11 +12634,18 @@ void clif_send_quest_delete(struct map_session_data * sd, int quest_id)
{ {
int fd = sd->fd; int fd = sd->fd;
WFIFOHEAD(fd, 6); WFIFOHEAD(fd, packet_len(0x02B4));
WFIFOW(fd, 0) = 0x02B4; WFIFOW(fd, 0) = 0x02B4;
WFIFOL(fd, 2) = quest_id; WFIFOL(fd, 2) = quest_id;
WFIFOSET(fd, 6); WFIFOSET(fd, packet_len(0x02B4));
}
// * 02B5 <packet_len>.W <ignored>.L { }.10B* <-- UNKOWN PACKET
// * 02B6 <quest_id>.L <state>.B
void clif_parse_questStateAck(int fd, struct map_session_data * sd)
{
quest_update_status(sd, RFIFOL(fd,2), RFIFOB(fd,6)?Q_ACTIVE:Q_INACTIVE);
} }
//Change active state of the quest //Change active state of the quest
@ -12661,11 +12654,11 @@ void clif_send_quest_status(struct map_session_data * sd, int quest_id, bool act
{ {
int fd = sd->fd; int fd = sd->fd;
WFIFOHEAD(fd, 7); WFIFOHEAD(fd, packet_len(0x02B7));
WFIFOW(fd, 0) = 0x02B7; WFIFOW(fd, 0) = 0x02B7;
WFIFOL(fd, 2) = quest_id; WFIFOL(fd, 2) = quest_id;
WFIFOB(fd, 6) = active?1:0; WFIFOB(fd, 6) = active;
WFIFOSET(fd, 7); WFIFOSET(fd, packet_len(0x02B7));
} }
/*========================================== /*==========================================
@ -13416,7 +13409,7 @@ static int packetdb_readdb(void)
0, 0, 0, 6, 0, 0, 0, 0, 0, 8, 18, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 8, 18, 0, 0, 0, 0, 0,
0, 4, 0, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,191, 0, 0, 0, 0, 0, 0, 0, 0, 0,117, 6, 0, 7, 7, 0,191, 0, 0, 0, 0, 0, 0,
//#0x02C0 //#0x02C0
0, 0, 0, 0, 0, 30, 0, 0, 0, 3, 0, 65, 4, 71, 10, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 3, 0, 65, 4, 71, 10, 0,
0, 0, 0, 0, 0, 0, 6, -1, 10, 10, 3, 0, -1, 32, 6, 0, 0, 0, 0, 0, 0, 0, 6, -1, 10, 10, 3, 0, -1, 32, 6, 0,
@ -13632,6 +13625,8 @@ static int packetdb_readdb(void)
{clif_parse_Auction_cancel,"auctioncancel"}, {clif_parse_Auction_cancel,"auctioncancel"},
{clif_parse_Auction_close,"auctionclose"}, {clif_parse_Auction_close,"auctionclose"},
{clif_parse_Auction_bid,"auctionbid"}, {clif_parse_Auction_bid,"auctionbid"},
// Quest Log System
{clif_parse_questStateAck,"queststate"},
#endif #endif
{clif_parse_cashshop_buy,"cashshopbuy"}, {clif_parse_cashshop_buy,"cashshopbuy"},
{clif_parse_ViewPlayerEquip,"viewplayerequip"}, {clif_parse_ViewPlayerEquip,"viewplayerequip"},

View File

@ -431,14 +431,13 @@ void clif_viewequip_ack(struct map_session_data* sd, struct map_session_data* ts
void clif_viewequip_fail(struct map_session_data* sd); void clif_viewequip_fail(struct map_session_data* sd);
void clif_equipcheckbox(struct map_session_data* sd); void clif_equipcheckbox(struct map_session_data* sd);
//quest system [Kevin] //quest system [Kevin] [Inkfish]
void clif_send_questlog(struct map_session_data * sd); void clif_send_questlog(struct map_session_data * sd);
void clif_send_questlog_info(struct map_session_data * sd); void clif_send_questlog_info(struct map_session_data * sd);
void clif_send_quest_info(struct map_session_data * sd, struct quest * qd); void clif_send_quest_info(struct map_session_data * sd, struct quest * qd);
void clif_send_quest_delete(struct map_session_data * sd, int quest_id); void clif_send_quest_delete(struct map_session_data * sd, int quest_id);
void clif_send_quest_status(struct map_session_data * sd, int quest_id, bool active); void clif_send_quest_status(struct map_session_data * sd, int quest_id, bool active);
int clif_send(const uint8* buf, int len, struct block_list* bl, enum send_target type); int clif_send(const uint8* buf, int len, struct block_list* bl, enum send_target type);
int do_final_clif(void); int do_final_clif(void);
int do_init_clif(void); int do_init_clif(void);

View File

@ -38,7 +38,7 @@ static const int packet_len_table[]={
10,-1,15, 0, 79,19, 7,-1, 0,-1,-1,-1, 14,67,186,-1, //0x3830 10,-1,15, 0, 79,19, 7,-1, 0,-1,-1,-1, 14,67,186,-1, //0x3830
9, 9,-1,14, 0, 0, 0, 0, -1,74,-1,11, 11,-1, 0, 0, //0x3840 9, 9,-1,14, 0, 0, 0, 0, -1,74,-1,11, 11,-1, 0, 0, //0x3840
-1,-1, 7, 7, 7,11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3850 Auctions [Zephyrus] -1,-1, 7, 7, 7,11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3850 Auctions [Zephyrus]
-1,11,11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3860 Quests [Kevin] -1,11,11,11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3860 Quests [Kevin] [Inkfish]
-1, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3870 Mercenaries [Zephyrus] -1, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3870 Mercenaries [Zephyrus]
11,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3880 11,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3880
-1,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3890 Homunculus [albator] -1,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3890 Homunculus [albator]
@ -1340,21 +1340,26 @@ int intif_request_questlog(TBL_PC *sd)
int intif_parse_questlog(int fd) int intif_parse_questlog(int fd)
{ {
int num_quests = (RFIFOB(fd, 2)-8)/sizeof(struct quest);
int char_id = RFIFOL(fd, 4); int char_id = RFIFOL(fd, 4);
int i; int i;
TBL_PC * sd = map_charid2sd(char_id); TBL_PC * sd = map_charid2sd(char_id);
//User not online anymore //User not online anymore
if(!sd) if(!sd)
return 0; return -1;
for(i=0; i<num_quests; i++) sd->avail_quests = sd->num_quests = (RFIFOB(fd, 2)-8)/sizeof(struct quest);
memset(&sd->quest_log, 0, sizeof(sd->quest_log));
for( i = 0; i < sd->num_quests; i++ )
{ {
memcpy(&sd->quest_log[i], RFIFOP(fd, i*sizeof(struct quest)+8), sizeof(struct quest)); memcpy(&sd->quest_log[i], RFIFOP(fd, i*sizeof(struct quest)+8), sizeof(struct quest));
if( sd->quest_log[i].state == Q_COMPLETE )
sd->avail_quests--;
} }
sd->num_quests = num_quests;
quest_pc_login(sd);
return 0; return 0;
} }
@ -1401,6 +1406,27 @@ int intif_quest_add(int char_id, struct quest * qd)
return 0; return 0;
} }
int intif_quest_save(int char_id, struct quest * qd)
{
if(CheckForCharServer())
return 0;
WFIFOHEAD(inter_fd, sizeof(struct quest) + 8);
WFIFOW(inter_fd,0) = 0x3063;
WFIFOW(inter_fd,2) = sizeof(struct quest) + 8;
WFIFOL(inter_fd,4) = char_id;
memcpy(WFIFOP(inter_fd,8), qd, sizeof(struct quest));
WFIFOSET(inter_fd, WFIFOW(inter_fd,2));
return 0;
}
int intif_parse_questSave(int fd)
{
quest_save_ack(RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOB(fd, 10));
return 0;
}
#ifndef TXT_ONLY #ifndef TXT_ONLY
/*========================================== /*==========================================
@ -2038,6 +2064,7 @@ int intif_parse(int fd)
case 0x3860: intif_parse_questlog(fd); break; case 0x3860: intif_parse_questlog(fd); break;
case 0x3861: intif_parse_questAdd(fd); break; case 0x3861: intif_parse_questAdd(fd); break;
case 0x3862: intif_parse_questDelete(fd); break; case 0x3862: intif_parse_questDelete(fd); break;
case 0x3863: intif_parse_questSave(fd); break;
#ifndef TXT_ONLY #ifndef TXT_ONLY
// Mail System // Mail System

View File

@ -77,6 +77,7 @@ int intif_homunculus_requestdelete(int homun_id);
int intif_request_questlog(struct map_session_data * sd); int intif_request_questlog(struct map_session_data * sd);
int intif_quest_delete(int char_id, int quest_id); int intif_quest_delete(int char_id, int quest_id);
int intif_quest_add(int char_id, struct quest * qd); int intif_quest_add(int char_id, struct quest * qd);
int intif_quest_save(int char_id, struct quest * qd);
// MERCENARY SYSTEM // MERCENARY SYSTEM
int intif_mercenary_create(struct s_mercenary *merc); int intif_mercenary_create(struct s_mercenary *merc);

View File

@ -32,6 +32,7 @@
#include "unit.h" #include "unit.h"
#include "battle.h" #include "battle.h"
#include "battleground.h" #include "battleground.h"
#include "quest.h"
#include "script.h" #include "script.h"
#include "mapreg.h" #include "mapreg.h"
#include "guild.h" #include "guild.h"
@ -4081,6 +4082,7 @@ int do_init(int argc, char *argv[])
do_init_pet(); do_init_pet();
do_init_merc(); do_init_merc();
do_init_mercenary(); do_init_mercenary();
do_init_quest();
do_init_npc(); do_init_npc();
do_init_unit(); do_init_unit();
do_init_battleground(); do_init_battleground();

View File

@ -32,6 +32,8 @@
#include "script.h" #include "script.h"
#include "atcommand.h" #include "atcommand.h"
#include "date.h" #include "date.h"
#include "quest.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -1992,6 +1994,8 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
} }
pc_setglobalreg(sd,"TK_MISSION_COUNT", sd->mission_count); pc_setglobalreg(sd,"TK_MISSION_COUNT", sd->mission_count);
} }
if( sd->avail_quests )
quest_update_objective(sd, md->class_);
} }
// filter out entries not eligible for exp distribution // filter out entries not eligible for exp distribution

View File

@ -377,9 +377,11 @@ struct map_session_data {
bool changed; // if true, should sync with charserver on next mailbox request bool changed; // if true, should sync with charserver on next mailbox request
} mail; } mail;
//Quest log system [Kevin] //Quest log system [Kevin] [Inkfish]
int num_quests; int num_quests;
struct quest quest_log[MAX_QUEST]; int avail_quests;
bool save_quest[MAX_QUEST_DB];
struct quest quest_log[MAX_QUEST_DB];
// temporary debug [flaviojs] // temporary debug [flaviojs]
const char* debug_file; const char* debug_file;

View File

@ -33,46 +33,61 @@
#include <stdarg.h> #include <stdarg.h>
#include <time.h> #include <time.h>
#define MAX_QUEST 25
//Send quest info on login //Send quest info on login
int quest_pc_login(TBL_PC * sd) int quest_pc_login(TBL_PC * sd)
{ {
if(sd->num_quests == 0) if(sd->num_quests == 0)
return 1; return 1;
clif_send_questlog(sd); clif_send_questlog(sd);
clif_send_questlog_info(sd); clif_send_questlog_info(sd);
return 0; return 0;
} }
int quest_add(TBL_PC * sd, struct quest * qd) int quest_add(TBL_PC * sd, int quest_id)
{ {
int i; int i, j, count;
//Search to see if this quest exists if( quest_check_quest(sd, quest_id, HAVEQUEST) >= 0 )
ARR_FIND(0, MAX_QUEST, i, sd->quest_log[i].quest_id == qd->quest_id); {
ShowError("quest_add: you already have quest %d.\n",quest_id);
//Already have this quest
if(i!=MAX_QUEST)
return 1;
//Find empty quest log spot
ARR_FIND(0, MAX_QUEST, i, sd->quest_log[i].quest_id == 0);
//Quest log is full
if(i == MAX_QUEST)
return -1; return -1;
}
if( (j = quest_search_db(quest_id)) < 0 )
{
ShowError("quest_add: quest %d not found in DB.\n",quest_id);
return -1;
}
if( sd->num_quests >= MAX_QUEST_DB || sd->avail_quests >= MAX_QUEST )
{
ShowError("quest_add: your quest log is full.(max quests: %d, max incompleted quests: %d)\n", MAX_QUEST_DB, MAX_QUEST);
return 1;
}
i = sd->avail_quests;
memmove(&sd->quest_log[i+1], &sd->quest_log[i], sizeof(struct quest)*(sd->num_quests-sd->avail_quests));
memset(&sd->quest_log[i], 0, sizeof(struct quest));
sd->quest_log[i].quest_id = quest_db[j].id;
if( quest_db[j].time )
sd->quest_log[i].time = (unsigned int)(time(NULL) + quest_db[j].time);
sd->quest_log[i].state = Q_ACTIVE;
for( count = 0; count < MAX_QUEST_OBJECTIVES && quest_db[j].mob[count]; count++ )
sd->quest_log[i].mob[count] = quest_db[j].mob[count];
sd->quest_log[i].num_objectives = count;
//Copy over quest data
memcpy(&sd->quest_log[i], qd, sizeof(struct quest));
sd->num_quests++; sd->num_quests++;
sd->avail_quests++;
//Notify inter server //Notify inter server
intif_quest_add(sd->status.char_id, qd); intif_quest_add(sd->status.char_id, &sd->quest_log[i]);
return 0; return 0;
} }
int quest_add_ack(int char_id, int quest_id, int success) int quest_add_ack(int char_id, int quest_id, int success)
@ -84,25 +99,20 @@ int quest_add_ack(int char_id, int quest_id, int success)
if(!sd) if(!sd)
return -1; return -1;
//Search for quest ARR_FIND(0, sd->avail_quests, i, sd->quest_log[i].quest_id == quest_id);
ARR_FIND(0, MAX_QUEST, i, sd->quest_log[i].quest_id == quest_id); if( i == sd->avail_quests )
return -1; //Shouldn't happen!
//Quest not found, shouldn't happen? if( success )
if(i == MAX_QUEST) clif_send_quest_info(sd, &sd->quest_log[i]); //Notify client
return -1;
if(success)
{
//Notify client
clif_send_quest_info(sd, &sd->quest_log[i]);
}
else else
{ {
sd->avail_quests--;
if( sd->num_quests < MAX_QUEST_DB && sd->quest_log[i+1].quest_id )
memmove(&sd->quest_log[i], &sd->quest_log[i+1], sizeof(struct quest)*(sd->num_quests-i));
memset(&sd->quest_log[--sd->num_quests], 0, sizeof(struct quest));
ShowError("Quest %d for character %d could not be added!\n", quest_id, char_id); ShowError("Quest %d for character %d could not be added!\n", quest_id, char_id);
return 1;
//Zero quest
memset(&sd->quest_log[i], 0, sizeof(struct quest));
sd->num_quests--;
} }
return 0; return 0;
@ -110,23 +120,117 @@ int quest_add_ack(int char_id, int quest_id, int success)
int quest_delete(TBL_PC * sd, int quest_id) int quest_delete(TBL_PC * sd, int quest_id)
{ {
int i;
//Search for quest //Search for quest
ARR_FIND(0, MAX_QUEST, i, sd->quest_log[i].quest_id == quest_id); if( quest_check_quest(sd, quest_id, HAVEQUEST) < 0 ) //Quest not found
{
//Quest not found ShowError("quest_delete: quest %d not found in your quest log.\n",quest_id);
if(i == MAX_QUEST)
return -1; return -1;
}
intif_quest_delete(sd->status.char_id, quest_id); intif_quest_delete(sd->status.char_id, quest_id);
return 0; return 0;
} }
int quest_delete_ack(int char_id, int quest_id, int success) int quest_delete_ack(int char_id, int quest_id, int success)
{
int i;
TBL_PC * sd = map_charid2sd(char_id);
///Player no longer on map
if(!sd)
return -1;
//Search for quest
ARR_FIND(0, sd->num_quests, i, sd->quest_log[i].quest_id == quest_id);
if(i == sd->num_quests) //Shouldn't happen!
return -1;
if(success)
{
if( sd->quest_log[i].state != Q_COMPLETE )
sd->avail_quests--;
if( sd->num_quests < MAX_QUEST_DB && sd->quest_log[i+1].quest_id )
memmove(&sd->quest_log[i], &sd->quest_log[i+1], sizeof(struct quest)*(sd->num_quests-i));
memset(&sd->quest_log[--sd->num_quests], 0, sizeof(struct quest));
clif_send_quest_delete(sd, quest_id);
}
else
{
ShowError("Quest %d for character %d could not be deleted!\n", quest_id, char_id);
return 1;
}
return 0;
}
void quest_update_objective(TBL_PC * sd, int mob)
{
int i,j;
for( i = 0; i < sd->avail_quests; i++ )
{
if( sd->quest_log[i].state != Q_ACTIVE )
continue;
for( j = 0; j < MAX_QUEST_OBJECTIVES; j++ )
if( sd->quest_log[i].mob[j] == mob )
{
sd->quest_log[i].count[j]++;
sd->save_quest[i] = true;
//clif_send_quest_info(sd, &sd->quest_log[i]); //TODO: Figure out the real packet [Inkfish]
//break;
}
}
}
int quest_update_status(TBL_PC * sd, int quest_id, int status)
{
int i;
//Search for quest
ARR_FIND(0, sd->avail_quests, i, sd->quest_log[i].quest_id == quest_id); // Only status of active and inactive quests can't be updated and completed quests can't [Inkfish]
if(i == sd->avail_quests) //Quest not found
{
ShowError("Quest %d not found in your quest log!\n", quest_id);
return -1;
}
sd->quest_log[i].state = (quest_state)status;
if( status != Q_COMPLETE )
{
clif_send_quest_status(sd, quest_id, (bool)status);
sd->save_quest[i] = true;
}
else
{
struct quest tmp_quest;
clif_send_quest_delete(sd, quest_id);
sd->avail_quests--;
memcpy(&tmp_quest, &sd->quest_log[i],sizeof(struct quest));
memcpy(&sd->quest_log[i], &sd->quest_log[sd->avail_quests],sizeof(struct quest));
memcpy(&sd->quest_log[sd->avail_quests], &tmp_quest,sizeof(struct quest));
sd->save_quest[sd->avail_quests] = true;
}
return 0;
}
int quest_save(TBL_PC * sd)
{
int i;
for( i = 0; i < sd->num_quests; i++ )
if( sd->save_quest[i] )
intif_quest_save(sd->status.char_id, &sd->quest_log[i]);
return 0;
}
int quest_save_ack(int char_id, int quest_id, int success)
{ {
int i; int i;
@ -137,65 +241,130 @@ int quest_delete_ack(int char_id, int quest_id, int success)
return -1; return -1;
//Search for quest //Search for quest
ARR_FIND(0, MAX_QUEST, i, sd->quest_log[i].quest_id == quest_id); ARR_FIND(0, sd->num_quests, i, sd->quest_log[i].quest_id == quest_id);
if(i == sd->num_quests) //Quest not found
//Quest not found
if(i == MAX_QUEST)
return -1; return -1;
if(success) if(success)
{ sd->save_quest[i] = false;
//Zero quest
memset(&sd->quest_log[i], 0, sizeof(struct quest));
sd->num_quests--;
//Notify client
clif_send_quest_delete(sd, quest_id);
return 1;
}
else else
ShowError("Quest %d for character %d could not be deleted!\n", quest_id, char_id); {
ShowError("Quest %d for character %d could not be saved!\n", quest_id, char_id);
return 1;
}
return 0; return 0;
} }
int quest_update_objective(TBL_PC * sd, int quest_id, int objective_num, const char * name, int count) int quest_check_quest(TBL_PC * sd, int quest_id, quest_check_type type)
{ {
int i; int i;
//Search for quest ARR_FIND(0, sd->num_quests, i, sd->quest_log[i].quest_id == quest_id);
ARR_FIND(0, MAX_QUEST, i, sd->quest_log[i].quest_id == quest_id); if(i == sd->num_quests)
//Quest not found
if(i == MAX_QUEST)
return -1; return -1;
memcpy(&sd->quest_log[i].objectives[objective_num].name, name, NAME_LENGTH); switch( type )
sd->quest_log[i].objectives[objective_num].count = count; {
case HAVEQUEST:
return sd->quest_log[i].state;
case PLAYTIME:
return (sd->quest_log[i].time < (unsigned int)time(NULL) ? 2 : sd->quest_log[i].state == Q_COMPLETE ? 1 : 0);
case HUNTING:
{
int j = quest_search_db(quest_id);
//Notify client if( j < 0 )
clif_send_quest_info(sd, &sd->quest_log[i]); {
ShowError("quest_check_quest: quest not found in DB\n");
return -1;
}
if( sd->quest_log[i].count[0] < quest_db[j].count[0] || sd->quest_log[i].count[1] < quest_db[j].count[1] || sd->quest_log[i].count[2] < quest_db[j].count[2] )
{
if( sd->quest_log[i].time < (unsigned int)time(NULL) )
return 1;
return 0; return 0;
}
return 2;
}
default:
ShowError("quest_check_quest: Unknown parameter %d",type);
break;
}
return -1;
} }
bool quest_has_quest(TBL_PC * sd, int quest_id) int quest_search_db(int quest_id)
{ {
int i; int i;
ARR_FIND(0, MAX_QUEST, i, sd->quest_log[i].quest_id == quest_id); ARR_FIND(0, MAX_QUEST_DB,i,quest_id == quest_db[i].id);
if( i == MAX_QUEST_DB )
return -1;
return (i != MAX_QUEST); return i;
} }
int quest_update_status(TBL_PC * sd, int quest_id, bool status) int quest_read_db(void)
{ {
FILE *fp;
char line[1024];
int j,k = 0;
char *str[20],*p,*np;
sprintf(line, "%s/quest_db.txt", db_path);
if( (fp=fopen(line,"r"))==NULL ){
ShowError("can't read %s\n", line);
return -1;
}
while(fgets(line, sizeof(line), fp))
{
if(line[0]=='/' && line[1]=='/')
continue;
memset(str,0,sizeof(str));
for( j = 0, p = line; j < 8;j++ )
{
if((np=strchr(p,','))!=NULL)
{
str[j] = p;
*np = 0;
p = np + 1;
}
else
{
ShowError("quest_read_db: insufficient columes in line %s\n", line);
continue;
}
}
if(str[0]==NULL)
continue;
memset(&quest_db[k], 0, sizeof(quest_db[0]));
quest_db[k].id = atoi(str[0]);
quest_db[k].time = atoi(str[1]);
quest_db[k].mob[0] = atoi(str[2]);
quest_db[k].count[0] = atoi(str[3]);
quest_db[k].mob[1] = atoi(str[4]);
quest_db[k].count[1] = atoi(str[5]);
quest_db[k].mob[2] = atoi(str[6]);
quest_db[k].count[2] = atoi(str[7]);
//memcpy(quest_db[k].name, str[8], sizeof(str[8]));
k++;
}
fclose(fp);
ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","quest_db.txt");
return 0; return 0;
} }
void do_init_quest(void)
{
quest_read_db();
}

View File

@ -4,19 +4,34 @@
#ifndef _QUEST_H_ #ifndef _QUEST_H_
#define _QUEST_H_ #define _QUEST_H_
int quest_pc_login(TBL_PC * sd); typedef enum quest_check_type { HAVEQUEST, PLAYTIME, HUNTING } quest_check_type;
int quest_load_info(TBL_PC * sd, struct mmo_charstatus * st);
int quest_make_savedata(TBL_PC * sd);
int quest_add(TBL_PC * sd, struct quest * qd); struct s_quest_db {
int id;
unsigned int time;
int mob[MAX_QUEST_OBJECTIVES];
int count[MAX_QUEST_OBJECTIVES];
//char name[NAME_LENGTH];
};
struct s_quest_db quest_db[MAX_QUEST_DB];
int quest_pc_login(TBL_PC * sd);
int quest_add(TBL_PC * sd, int quest_id);
int quest_add_ack(int char_id, int quest_id, int success); int quest_add_ack(int char_id, int quest_id, int success);
int quest_delete(TBL_PC * sd, int quest_id); int quest_delete(TBL_PC * sd, int quest_id);
int quest_delete_ack(int char_id, int quest_id, int success); int quest_delete_ack(int char_id, int quest_id, int success);
int quest_update_objective(TBL_PC * sd, int quest_id, int objective_num, const char * name, int count); void quest_update_objective(TBL_PC * sd, int mob);
int quest_update_status(TBL_PC * sd, int quest_id, bool status); int quest_update_status(TBL_PC * sd, int quest_id, int status);
int quest_save(TBL_PC * sd);
int quest_save_ack(int char_id, int quest_id, int success);
bool quest_has_quest(TBL_PC * sd, int quest_id); int quest_check_quest(TBL_PC * sd, int quest_id, quest_check_type type);
int quest_search_db(int quest_id);
void do_init_quest();
#endif #endif

View File

@ -13426,81 +13426,53 @@ BUILDIN_FUNC(readbook)
Questlog script commands Questlog script commands
*******************/ *******************/
BUILDIN_FUNC(getquest) BUILDIN_FUNC(setquest)
{ {
TBL_PC * sd = script_rid2sd(st); TBL_PC * sd = script_rid2sd(st);
struct quest qd;
int i, count = 0;
char * temp;
memset(&qd, 0, sizeof(struct quest)); quest_add(sd, script_getnum(st, 2));
return 0;
}
qd.quest_id = script_getnum(st, 2); BUILDIN_FUNC(erasequest)
qd.time = script_getnum(st, 3); {
qd.state = Q_ACTIVE; TBL_PC * sd = script_rid2sd(st);
for(i=0; i<(script_lastdata(st)-3) && (i/2) < MAX_QUEST_OBJECTIVES; i+=2) quest_delete(sd, script_getnum(st, 2));
return 0;
}
BUILDIN_FUNC(completequest)
{
TBL_PC * sd = script_rid2sd(st);
quest_update_status(sd, script_getnum(st, 2), Q_COMPLETE);
return 0;
}
BUILDIN_FUNC(changequest)
{
TBL_PC * sd = script_rid2sd(st);
int q1 = script_getnum(st, 2), q2 = script_getnum(st, 3);
if( quest_check_quest(sd, q1, HAVEQUEST) == Q_ACTIVE && quest_add(sd, q2) >= 0 )
{ {
temp = (char*)script_getstr(st, i+4); quest_update_status(sd, q1, Q_COMPLETE);
memcpy(&qd.objectives[i/2].name, temp, NAME_LENGTH); intif_quest_save(sd->status.char_id, &sd->quest_log[sd->avail_quests]);
temp = NULL;
qd.objectives[i/2].count = script_getnum(st, i+5);
count++;
} }
qd.num_objectives = count;
quest_add(sd, &qd);
return 0;
}
BUILDIN_FUNC(deletequest)
{
TBL_PC * sd = script_rid2sd(st);
int qid = script_getnum(st, 2);
quest_delete(sd, qid);
return 0;
}
BUILDIN_FUNC(setquestobjective)
{
TBL_PC * sd = script_rid2sd(st);
int qid = script_getnum(st, 2);
int num = script_getnum(st, 3);
const char * str = script_getstr(st, 4);
int count = script_getnum(st, 5);
quest_update_objective(sd, qid, num, str, count);
return 0; return 0;
} }
BUILDIN_FUNC(hasquest) BUILDIN_FUNC(checkquest)
{ {
TBL_PC * sd = script_rid2sd(st); TBL_PC * sd = script_rid2sd(st);
int qid = script_getnum(st, 2); quest_check_type type = HAVEQUEST;
script_pushint(st, quest_has_quest(sd, qid)?1:0); if( script_hasdata(st, 3) )
type = (quest_check_type)script_getnum(st, 3);
return 0; script_pushint(st, quest_check_quest(sd, script_getnum(st, 2), type));
}
BUILDIN_FUNC(setqueststatus)
{
TBL_PC * sd = script_rid2sd(st);
int qid = script_getnum(st, 2);
bool active = script_getnum(st, 3)?true:false;
quest_update_status(sd, qid, active);
return 0; return 0;
} }
@ -14472,11 +14444,6 @@ struct script_function buildin_func[] = {
BUILDIN_DEF(openauction,""), BUILDIN_DEF(openauction,""),
BUILDIN_DEF(checkcell,"siii"), BUILDIN_DEF(checkcell,"siii"),
BUILDIN_DEF(setcell,"siiiiii"), BUILDIN_DEF(setcell,"siiiiii"),
BUILDIN_DEF(getquest, "ii*"),
BUILDIN_DEF(deletequest, "i"),
BUILDIN_DEF(setquestobjective, "iisi"),
BUILDIN_DEF(setqueststatus, "ii"),
BUILDIN_DEF(hasquest, "i"),
BUILDIN_DEF(setwall,"siiiiis"), BUILDIN_DEF(setwall,"siiiiis"),
BUILDIN_DEF(delwall,"s"), BUILDIN_DEF(delwall,"s"),
BUILDIN_DEF(mercenary_create,"ii"), BUILDIN_DEF(mercenary_create,"ii"),
@ -14519,5 +14486,11 @@ struct script_function buildin_func[] = {
BUILDIN_DEF(instance_id,"?"), BUILDIN_DEF(instance_id,"?"),
BUILDIN_DEF(instance_warpall,"sii"), BUILDIN_DEF(instance_warpall,"sii"),
BUILDIN_DEF(instance_set_timeout,"ii?"), BUILDIN_DEF(instance_set_timeout,"ii?"),
//Quest Log System [Inkfish]
BUILDIN_DEF(setquest, "i"),
BUILDIN_DEF(erasequest, "i"),
BUILDIN_DEF(completequest, "i"),
BUILDIN_DEF(checkquest, "i*"),
BUILDIN_DEF(changequest, "ii"),
{NULL,NULL,NULL}, {NULL,NULL,NULL},
}; };