瀏覽代碼

Added script command mail (#3043)

Lemongrass3110 7 年之前
父節點
當前提交
5b13dc7009
共有 3 個文件被更改,包括 340 次插入6 次删除
  1. 60 0
      doc/script_commands.txt
  2. 25 6
      src/char/int_mail.cpp
  3. 255 0
      src/map/script.cpp

+ 60 - 0
doc/script_commands.txt

@@ -5274,6 +5274,66 @@ invoking character.
 
 ---------------------------------------
 
+*mail <destination id>,"<sender name>","<title>","<body>"{,<zeny>{,<item id array>,<item amount array>{,<item card0 array>{,<item card1 array>{,<item card2 array>{,<item card3 array>
+		{,<random option id0 array>, <random option value0 array>, <random option paramter0 array>{,<random option id1 array>, <random option value1 array>, <random option paramter1 array>
+		{,<random option id2 array>, <random option value2 array>, <random option paramter2 array>{,<random option id3 array>, <random option value3 array>, <random option paramter3 array>
+		{,<random option id4 array>, <random option value4 array>, <random option paramter4 array>}}}}}}};
+
+This command will send mail to the <destination id> which is a character ID.
+A <sender name> can be specified but does not have to be from the direct creator
+of the mail and is limited to NAME_LENGTH (24) characters. Mail <title> is limited
+to MAIL_TITLE_LENGTH (40) characters. Mail <body> is limited to MAIL_BODY_LENGTH
+(200) characters for PACKETVER < 20150513 or 500 characters for later clients.
+
+Optional <zeny> and item data can be added to the mail as well. PACKETVER < 20150513
+is limited to 1 item while later clients are limited to MAIL_MAX_ITEM (5).
+
+The <item id array>, <item amount array>, <item card0 array>, <item card1 array>,
+<item card2 array>, and <item card3 array> should all be integer arrays.
+
+For random options there can be 5 arrays in pairs of 3 (ids, values, parameters) right after the cards.
+All of these arrays shall be integer arrays as well.
+
+Example of sending mail with zeny:
+	.@charid = getcharid(0);
+	.@sender$ = "Poring";
+	.@title$ = "Welcome";
+	.@body$ = "Hi! I'm a simple Poring from the Prontera fields! Welcome to Ragnarok!";
+	.@zeny = 5000;
+	mail .@charid, .@sender$, .@title$, .@body$, .@zeny;
+
+Example of sending mail with items:
+	.@charid = getcharid(0);
+	.@sender$ = "Angeling";
+	.@title$ = "Welcome";
+	.@body$ = "Hi! I'm a simple Angeling from the Prontera fields! Welcome to Ragnarok!";
+	.@zeny = 0;
+	setarray .@mailitem[0], 504, 505, 2220, 1214; // White Potion, Blue Potion, Hat, Dagger
+	setarray .@mailamount[0], 10, 5, 1, 1; // 10 White Potions, 5 Blue Potions, 1 Hat, 1 Dagger
+	setarray .@mailcard0[0], 0, 0, 4198, 4092; // Attach Maya Purple Card to the Hat, Attach Skeleton Worker Card to Dagger
+	setarray .@mailcard1[0], 0, 0, 0, 4092; // Attach Skeleton Worker Card to Dagger
+	setarray .@mailcard2[0], 0, 0, 0, 4092; // Attach Skeleton Worker Card to Dagger
+	mail .@charid, .@sender$, .@title$, .@body$, .@zeny, .@mailitem, .@mailamount, .@mailcard0, .@mailcard1, .@mailcard2;
+	
+Example of sending mail with items and random options:
+	.@charid = getcharid(0);
+	.@sender$ = "Angeling";
+	.@title$ = "Welcome";
+	.@body$ = "Hi! I'm a simple Angeling from the Prontera fields! Welcome to Ragnarok!";
+	.@zeny = 0;
+	setarray .@mailitem[0], 504, 505, 2220, 1214; // White Potion, Blue Potion, Hat, Dagger
+	setarray .@mailamount[0], 10, 5, 1, 1; // 10 White Potions, 5 Blue Potions, 1 Hat, 1 Dagger
+	setarray .@mailcard0[0], 0, 0, 4198, 4092; // Attach Maya Purple Card to the Hat, Attach Skeleton Worker Card to Dagger
+	setarray .@mailcard1[0], 0, 0, 0, 4092; // Attach Skeleton Worker Card to Dagger
+	setarray .@mailcard2[0], 0, 0, 0, 4092; // Attach Skeleton Worker Card to Dagger
+	setarray .@mailcard3[0], 0, 0, 0, 0; // Empty last slot
+	setarray .@mailrndopt_id0[0], 0, 0, 0, 0, RDMOPT_VAR_MAXHPAMOUNT; // Enchant the Dagger with increased HP option
+	setarray .@mailrndopt_val0[0], 0, 0, 0, 0, 1000; // Enchant the Dagger with increased HP option by 1000 points
+	setarray .@mailrndopt_prm0[0], 0, 0, 0, 0, 0; // Enchant the Dagger with increased HP option - does not need any parameter
+	mail .@charid, .@sender$, .@title$, .@body$, .@zeny, .@mailitem, .@mailamount, .@mailcard0, .@mailcard1, .@mailcard2, .@mailcard3, .@mailrndopt_id0, .@mailrndopt_val0, .@mailrndopt_prm0;
+
+---------------------------------------
+
 *openauction({<char_id>});
 
 This will open the Auction window on the client connected to the invoking character.

+ 25 - 6
src/char/int_mail.cpp

@@ -484,7 +484,8 @@ void mapif_Mail_return(int fd, uint32 char_id, int mail_id)
 		else if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", schema_config.mail_db, mail_id)
 			|| SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", schema_config.mail_attachment_db, mail_id) )
 			Sql_ShowDebug(sql_handle);
-		else
+		// If it was not sent by the server, since we do not want to return mails to the server
+		else if( msg.send_id != 0 )
 		{
 			char temp_[MAIL_TITLE_LENGTH];
 
@@ -543,20 +544,38 @@ void mapif_parse_Mail_send(int fd)
 {
 	struct mail_message msg;
 	char esc_name[NAME_LENGTH*2+1];
+	char *data;
+	size_t len;
 
 	if(RFIFOW(fd,2) != 8 + sizeof(struct mail_message))
 		return;
 
 	memcpy(&msg, RFIFOP(fd,8), sizeof(struct mail_message));
 
+	if( msg.dest_id != 0 ){
+		if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `char_id`, `name` FROM `%s` WHERE `char_id` = '%u'", schema_config.char_db, msg.dest_id) ){
+			Sql_ShowDebug(sql_handle);
+			return;
+		}
+		
+		msg.dest_id = 0;
+		msg.dest_name[0] = '\0';
+
+		if( SQL_SUCCESS == Sql_NextRow(sql_handle) ){
+			Sql_GetData(sql_handle, 0, &data, NULL);
+			msg.dest_id = atoi(data);
+			Sql_GetData(sql_handle, 1, &data, &len);
+			safestrncpy(msg.dest_name, data, NAME_LENGTH);
+		}
+
+		Sql_FreeResult(sql_handle);
+	}
+
 	// Try to find the Dest Char by Name
 	Sql_EscapeStringLen(sql_handle, esc_name, msg.dest_name, strnlen(msg.dest_name, NAME_LENGTH));
-	if ( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`, `char_id` FROM `%s` WHERE `name` = '%s'", schema_config.char_db, esc_name) )
+	if ( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`, `char_id` FROM `%s` WHERE `name` = '%s'", schema_config.char_db, esc_name) ){
 		Sql_ShowDebug(sql_handle);
-	else
-	if ( SQL_SUCCESS == Sql_NextRow(sql_handle) )
-	{
-		char *data;
+	}else if ( SQL_SUCCESS == Sql_NextRow(sql_handle) ){
 #if PACKETVER < 20150513
 		uint32 account_id = RFIFOL(fd,4);
 

+ 255 - 0
src/map/script.cpp

@@ -23802,6 +23802,260 @@ BUILDIN_FUNC(getequiptradability) {
 	return SCRIPT_CMD_SUCCESS;
 }
 
+static inline bool mail_sub( struct script_state *st, struct script_data *data, struct map_session_data *sd, int i, const char **out_name, unsigned int *start, unsigned int *end, int32 *id ){
+	const char *name;
+
+	// Check if it is a variable
+	if( !data_isreference(data) ){
+		ShowError("buildin_mail: argument %d is not a variable.\n", i );
+		return false;
+	}
+
+	name = reference_getname(data);
+
+	if( is_string_variable(name) ){
+		ShowError( "buildin_mail: variable \"%s\" is a string variable.\n", name );
+		return false;
+	}
+
+	// Check if the variable requires a player
+	if( not_server_variable(*name) && sd == NULL ){
+		// If no player is attached
+		if( !script_rid2sd(sd) ){
+			ShowError( "buildin_mail: variable \"%s\" was not a server variable, but no player was attached.\n", name );
+			return false;
+		}
+	}
+
+	// Try to find the array's source pointer
+	if( !script_array_src( st, sd, name, reference_getref(data) ) ){
+		ShowError( "buildin_mail: variable \"%s\" is not an array.\n" );
+		return false;
+	}
+
+	// Store the name for later usage
+	*out_name = name;
+
+	// Get the start and end indices of the array
+	*start = reference_getindex(data);
+	*end = script_array_highest_key( st, sd, name, reference_getref(data) );
+
+	// For getting the values we need the id of the array
+	*id = reference_getid(data);
+
+	return true;
+}
+
+BUILDIN_FUNC(mail){
+	const char *sender, *title, *body, *name;
+	struct mail_message msg;
+	struct script_data *data;
+	struct map_session_data *sd = NULL;
+	unsigned int i, j, k, num_items, start, end;
+	int32 id;
+
+	memset(&msg, 0, sizeof(struct mail_message));
+
+	msg.dest_id = script_getnum(st,2);
+
+	sender = script_getstr(st, 3);
+
+	if( strlen(sender) > NAME_LENGTH ){
+		ShowError( "buildin_mail: sender name can not be longer than %d characters.\n", NAME_LENGTH );
+		return SCRIPT_CMD_FAILURE;
+	}
+
+	safestrncpy(msg.send_name, sender, NAME_LENGTH);
+
+	title = script_getstr(st, 4);
+
+	if( strlen(title) > MAIL_TITLE_LENGTH ){
+		ShowError( "buildin_mail: title can not be longer than %d characters.\n", MAIL_TITLE_LENGTH );
+		return SCRIPT_CMD_FAILURE;
+	}
+
+	safestrncpy(msg.title, title, MAIL_TITLE_LENGTH);
+
+	body = script_getstr(st, 5);
+
+	if( strlen(body) > MAIL_BODY_LENGTH ){
+		ShowError( "buildin_mail: body can not be longer than %d characters.\n", MAIL_BODY_LENGTH );
+		return SCRIPT_CMD_FAILURE;
+	}
+
+	safestrncpy(msg.body, body, MAIL_BODY_LENGTH);
+
+	if( script_hasdata(st,6) ){
+		int zeny = script_getnum(st, 6);
+
+		if( zeny < 0 ){
+			ShowError( "buildin_mail: a negative amount of zeny can not be sent.\n" );
+			return SCRIPT_CMD_FAILURE;
+		}else if( zeny > MAX_ZENY ){
+			ShowError( "buildin_mail: amount of zeny %u is exceeding maximum of %u. Capping...\n", zeny, MAX_ZENY );
+			zeny = MAX_ZENY;
+		}
+
+		msg.zeny = zeny;
+	}
+
+	// Items
+	num_items = 0;
+	while( script_hasdata(st,7) ){
+		data = script_getdata(st,7);
+
+		if( !mail_sub( st, data, sd, 7, &name, &start, &end, &id ) ){
+			return SCRIPT_CMD_FAILURE;
+		}
+
+		num_items = end - start;
+
+		if( num_items == 0 ){
+			ShowWarning( "buildin_mail: array \"%s\" contained no items.\n", name );
+			break;
+		}
+
+		if( num_items > MAIL_MAX_ITEM ){
+			ShowWarning( "buildin_mail: array \"%s\" contained %d items, capping to maximum of %d...\n", name, num_items, MAIL_MAX_ITEM );
+			num_items = MAIL_MAX_ITEM;
+		}
+
+		for( i = 0; i < num_items && start < end; i++, start++ ){
+			msg.item[i].nameid = (int32)__64BPRTSIZE( get_val2( st, reference_uid( id, start ), reference_getref( data ) ) );
+			msg.item[i].identify = 1;
+
+			script_removetop(st, -1, 0);
+
+			if( !itemdb_exists(msg.item[i].nameid) ){
+				ShowError( "buildin_mail: invalid item id %hu.\n", msg.item[i].nameid );
+				return SCRIPT_CMD_FAILURE;
+			}
+		}
+
+		// Amounts
+		if( !script_hasdata(st,8) ){
+			ShowError("buildin_mail: missing item count variable at position %d.\n", 8);
+			return SCRIPT_CMD_FAILURE;
+		}
+
+		data = script_getdata(st,8);
+
+		if( !mail_sub( st, data, sd, 8, &name, &start, &end, &id ) ){
+			return SCRIPT_CMD_FAILURE;
+		}
+
+		for( i = 0; i < num_items && start < end; i++, start++ ){
+			struct item_data *item = itemdb_exists(msg.item[i].nameid);
+
+			msg.item[i].amount = (int32)__64BPRTSIZE( get_val2( st, reference_uid( id, start ), reference_getref( data ) ) );
+
+			script_removetop(st, -1, 0);
+
+			if( msg.item[i].amount <= 0 ){
+				ShowError( "buildin_mail: amount %d for item %hu is invalid.\n", msg.item[i].amount, msg.item[i].nameid );
+				return SCRIPT_CMD_FAILURE;
+			}else if( itemdb_isstackable2(item) ){
+				uint16 max = item->stack.amount > 0 ? item->stack.amount : MAX_AMOUNT;
+
+				if( msg.item[i].amount > max ){
+					ShowWarning( "buildin_mail: amount %d for item %hu is exceeding the maximum of %d. Capping...\n", msg.item[i].amount, msg.item[i].nameid, max );
+					msg.item[i].amount = max;
+				}
+			}else{
+				if( msg.item[i].amount > 1 ){
+					ShowWarning( "buildin_mail: amount %d is invalid for non-stackable item %hu.\n", msg.item[i].amount, msg.item[i].nameid );
+					msg.item[i].amount = 1;
+				}
+			}
+		}
+
+		// Cards
+		if( !script_hasdata(st,9) ){
+			break;
+		}
+
+		for( i = 0, j = 9; i < MAX_SLOTS && script_hasdata(st,j); i++, j++ ){
+			data = script_getdata(st,j);
+
+			if( !mail_sub( st, data, sd, j + 1, &name, &start, &end, &id ) ){
+				return SCRIPT_CMD_FAILURE;
+			}
+
+			for( k = 0; k < num_items && start < end; k++, start++ ){
+				msg.item[k].card[i] = (int32)__64BPRTSIZE( get_val2( st, reference_uid( id, start ), reference_getref( data ) ) );
+
+				script_removetop(st, -1, 0);
+
+				if( msg.item[k].card[i] != 0 && !itemdb_exists(msg.item[k].card[i]) ){
+					ShowError( "buildin_mail: invalid card id %hu.\n", msg.item[k].card[i] );
+					return SCRIPT_CMD_FAILURE;
+				}
+			}
+		}
+	
+		// Random Options
+		if( !script_hasdata(st,9 + MAX_SLOTS) ){
+			break;
+		}
+
+		for( i = 0, j = 9 + MAX_SLOTS; i < MAX_ITEM_RDM_OPT && script_hasdata(st,j) && script_hasdata(st,j + 1) && script_hasdata(st,j + 2); i++, j++ ){
+			// Option IDs
+			data = script_getdata(st, j);
+
+			if( !mail_sub( st, data, sd, j + 1, &name, &start, &end, &id ) ){
+				return SCRIPT_CMD_FAILURE;
+			}
+
+			for( k = 0; k < num_items && start < end; k++, start++ ){
+				msg.item[k].option[i].id = (int32)__64BPRTSIZE( get_val2( st, reference_uid( id, start ), reference_getref( data ) ) );
+
+				script_removetop(st, -1, 0);
+			}
+
+			j++;
+
+			// Option values
+			data = script_getdata(st, j);
+
+			if( !mail_sub( st, data, sd, j + 1, &name, &start, &end, &id ) ){
+				return SCRIPT_CMD_FAILURE;
+			}
+
+			for( k = 0; k < num_items && start < end; k++, start++ ){
+				msg.item[k].option[i].value = (int32)__64BPRTSIZE( get_val2( st, reference_uid( id, start ), reference_getref( data ) ) );
+
+				script_removetop(st, -1, 0);
+			}
+
+			j++;
+
+			// Option parameters
+			data = script_getdata(st, j);
+
+			if( !mail_sub( st, data, sd, j + 1, &name, &start, &end, &id ) ){
+				return SCRIPT_CMD_FAILURE;
+			}
+
+			for( k = 0; k < num_items && start < end; k++, start++ ){
+				msg.item[k].option[i].param = (int32)__64BPRTSIZE( get_val2( st, reference_uid( id, start ), reference_getref( data ) ) );
+
+				script_removetop(st, -1, 0);
+			}
+		}
+
+		// Break the pseudo scope
+		break;
+	}
+
+	msg.status = MAIL_NEW;
+	msg.type = MAIL_INBOX_NORMAL;
+	msg.timestamp = time(NULL);
+
+	intif_Mail_send(0, &msg);
+
+	return SCRIPT_CMD_SUCCESS;
+}
+
 #include "../custom/script.inc"
 
 // declarations that were supposed to be exported from npc_chat.c
@@ -24451,6 +24705,7 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF2(round, "ceil", "i"),
 	BUILDIN_DEF2(round, "floor", "i"),
 	BUILDIN_DEF(getequiptradability, "i?"),
+	BUILDIN_DEF(mail, "isss*"),
 #include "../custom/script_def.inc"
 
 	{NULL,NULL,NULL},