Browse Source

Added support for homunculus autofeeding (#3007)

* Added support for homunculus autofeeding
This feature is available in 2017-09-20bRagexeRE or later

Credits to @Asheraf in HerculesWS/Hercules#1898
Lemongrass3110 7 years ago
parent
commit
aff70f6482

+ 8 - 0
conf/battle/feature.conf

@@ -67,3 +67,11 @@ feature.roulette: off
 // Achievement (Note 1)
 // Achievement (Note 1)
 // Requires: 2015-05-13aRagexe or later
 // Requires: 2015-05-13aRagexe or later
 feature.achievement: on
 feature.achievement: on
+
+// Homunculues Autofeeding (Note 1)
+// Requires: 2017-09-20bRagexeRE or later
+feature.homunculus_autofeed: off
+
+// At which rate should homunculus autofeeding trigger?
+// Default: 30
+feature.homunculus_autofeed_rate: 30

+ 1 - 0
sql-files/main.sql

@@ -608,6 +608,7 @@ CREATE TABLE IF NOT EXISTS `homunculus` (
   `alive` tinyint(2) NOT NULL default '1',
   `alive` tinyint(2) NOT NULL default '1',
   `rename_flag` tinyint(2) NOT NULL default '0',
   `rename_flag` tinyint(2) NOT NULL default '0',
   `vaporize` tinyint(2) NOT NULL default '0',
   `vaporize` tinyint(2) NOT NULL default '0',
+  `autofeed` tinyint(2) NOT NULL default '0',
   PRIMARY KEY  (`homun_id`)
   PRIMARY KEY  (`homun_id`)
 ) ENGINE=MyISAM;
 ) ENGINE=MyISAM;
 
 

+ 2 - 0
sql-files/upgrades/upgrade_20180404.sql

@@ -0,0 +1,2 @@
+ALTER TABLE `homunculus`
+	ADD COLUMN `autofeed` tinyint(2) NOT NULL default '0' AFTER `vaporize`;

+ 1 - 1
src/char/char.cpp

@@ -2453,7 +2453,7 @@ bool char_checkdb(void){
 	}
 	}
 	//checking homunculus_db
 	//checking homunculus_db
 	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT  `homun_id`,`char_id`,`class`,`prev_class`,`name`,`level`,`exp`,`intimacy`,`hunger`,"
 	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT  `homun_id`,`char_id`,`class`,`prev_class`,`name`,`level`,`exp`,`intimacy`,`hunger`,"
-		"`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hp`,`max_hp`,`sp`,`max_sp`,`skill_point`,`alive`,`rename_flag`,`vaporize` "
+		"`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hp`,`max_hp`,`sp`,`max_sp`,`skill_point`,`alive`,`rename_flag`,`vaporize`,`autofeed` "
 		" FROM `%s` LIMIT 1;", schema_config.homunculus_db) ){
 		" FROM `%s` LIMIT 1;", schema_config.homunculus_db) ){
 		Sql_ShowDebug(sql_handle);
 		Sql_ShowDebug(sql_handle);
 		return false;
 		return false;

+ 7 - 6
src/char/int_homun.cpp

@@ -93,10 +93,10 @@ bool mapif_homunculus_save(struct s_homunculus* hd)
 	if( hd->hom_id == 0 )
 	if( hd->hom_id == 0 )
 	{// new homunculus
 	{// new homunculus
 		if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s` "
 		if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s` "
-			"(`char_id`, `class`,`prev_class`,`name`,`level`,`exp`,`intimacy`,`hunger`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `hp`,`max_hp`,`sp`,`max_sp`,`skill_point`, `rename_flag`, `vaporize`) "
-			"VALUES ('%d', '%d', '%d', '%s', '%d', '%u', '%u', '%d', '%d', %d, '%d', '%d', '%d', '%d', '%u', '%u', '%u', '%u', '%d', '%d', '%d')",
+			"(`char_id`, `class`,`prev_class`,`name`,`level`,`exp`,`intimacy`,`hunger`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `hp`,`max_hp`,`sp`,`max_sp`,`skill_point`, `rename_flag`, `vaporize`, `autofeed`) "
+			"VALUES ('%d', '%d', '%d', '%s', '%d', '%u', '%u', '%d', '%d', %d, '%d', '%d', '%d', '%d', '%u', '%u', '%u', '%u', '%d', '%d', '%d', '%d')",
 			schema_config.homunculus_db, hd->char_id, hd->class_, hd->prev_class, esc_name, hd->level, hd->exp, hd->intimacy, hd->hunger, hd->str, hd->agi, hd->vit, hd->int_, hd->dex, hd->luk,
 			schema_config.homunculus_db, hd->char_id, hd->class_, hd->prev_class, esc_name, hd->level, hd->exp, hd->intimacy, hd->hunger, hd->str, hd->agi, hd->vit, hd->int_, hd->dex, hd->luk,
-			hd->hp, hd->max_hp, hd->sp, hd->max_sp, hd->skillpts, hd->rename_flag, hd->vaporize) )
+			hd->hp, hd->max_hp, hd->sp, hd->max_sp, hd->skillpts, hd->rename_flag, hd->vaporize, hd->autofeed) )
 		{
 		{
 			Sql_ShowDebug(sql_handle);
 			Sql_ShowDebug(sql_handle);
 			flag = false;
 			flag = false;
@@ -108,9 +108,9 @@ bool mapif_homunculus_save(struct s_homunculus* hd)
 	}
 	}
 	else
 	else
 	{
 	{
-		if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `char_id`='%d', `class`='%d',`prev_class`='%d',`name`='%s',`level`='%d',`exp`='%u',`intimacy`='%u',`hunger`='%d', `str`='%d', `agi`='%d', `vit`='%d', `int`='%d', `dex`='%d', `luk`='%d', `hp`='%u',`max_hp`='%u',`sp`='%u',`max_sp`='%u',`skill_point`='%d', `rename_flag`='%d', `vaporize`='%d' WHERE `homun_id`='%d'",
+		if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `char_id`='%d', `class`='%d',`prev_class`='%d',`name`='%s',`level`='%d',`exp`='%u',`intimacy`='%u',`hunger`='%d', `str`='%d', `agi`='%d', `vit`='%d', `int`='%d', `dex`='%d', `luk`='%d', `hp`='%u',`max_hp`='%u',`sp`='%u',`max_sp`='%u',`skill_point`='%d', `rename_flag`='%d', `vaporize`='%d', `autofeed`='%d' WHERE `homun_id`='%d'",
 			schema_config.homunculus_db, hd->char_id, hd->class_, hd->prev_class, esc_name, hd->level, hd->exp, hd->intimacy, hd->hunger, hd->str, hd->agi, hd->vit, hd->int_, hd->dex, hd->luk,
 			schema_config.homunculus_db, hd->char_id, hd->class_, hd->prev_class, esc_name, hd->level, hd->exp, hd->intimacy, hd->hunger, hd->str, hd->agi, hd->vit, hd->int_, hd->dex, hd->luk,
-			hd->hp, hd->max_hp, hd->sp, hd->max_sp, hd->skillpts, hd->rename_flag, hd->vaporize, hd->hom_id) )
+			hd->hp, hd->max_hp, hd->sp, hd->max_sp, hd->skillpts, hd->rename_flag, hd->vaporize, hd->autofeed, hd->hom_id) )
 		{
 		{
 			Sql_ShowDebug(sql_handle);
 			Sql_ShowDebug(sql_handle);
 			flag = false;
 			flag = false;
@@ -155,7 +155,7 @@ bool mapif_homunculus_load(int homun_id, struct s_homunculus* hd)
 
 
 	memset(hd, 0, sizeof(*hd));
 	memset(hd, 0, sizeof(*hd));
 
 
-	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `homun_id`,`char_id`,`class`,`prev_class`,`name`,`level`,`exp`,`intimacy`,`hunger`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `hp`,`max_hp`,`sp`,`max_sp`,`skill_point`,`rename_flag`, `vaporize` FROM `%s` WHERE `homun_id`='%u'",
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `homun_id`,`char_id`,`class`,`prev_class`,`name`,`level`,`exp`,`intimacy`,`hunger`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `hp`,`max_hp`,`sp`,`max_sp`,`skill_point`,`rename_flag`, `vaporize`, `autofeed` FROM `%s` WHERE `homun_id`='%u'",
 		schema_config.homunculus_db, homun_id) )
 		schema_config.homunculus_db, homun_id) )
 	{
 	{
 		Sql_ShowDebug(sql_handle);
 		Sql_ShowDebug(sql_handle);
@@ -196,6 +196,7 @@ bool mapif_homunculus_load(int homun_id, struct s_homunculus* hd)
 	Sql_GetData(sql_handle, 19, &data, NULL); hd->skillpts = atoi(data);
 	Sql_GetData(sql_handle, 19, &data, NULL); hd->skillpts = atoi(data);
 	Sql_GetData(sql_handle, 20, &data, NULL); hd->rename_flag = atoi(data);
 	Sql_GetData(sql_handle, 20, &data, NULL); hd->rename_flag = atoi(data);
 	Sql_GetData(sql_handle, 21, &data, NULL); hd->vaporize = atoi(data);
 	Sql_GetData(sql_handle, 21, &data, NULL); hd->vaporize = atoi(data);
+	Sql_GetData(sql_handle, 22, &data, NULL); hd->autofeed = atoi(data);
 	Sql_FreeResult(sql_handle);
 	Sql_FreeResult(sql_handle);
 
 
 	hd->intimacy = umin(hd->intimacy,100000);
 	hd->intimacy = umin(hd->intimacy,100000);

+ 1 - 0
src/common/mmo.h

@@ -434,6 +434,7 @@ struct s_homunculus {	//[orn]
 	int luk_value;
 	int luk_value;
 
 
 	char spiritball; //for homun S [lighta]
 	char spiritball; //for homun S [lighta]
+	bool autofeed;
 };
 };
 
 
 struct s_mercenary {
 struct s_mercenary {

+ 9 - 0
src/map/battle.cpp

@@ -8503,6 +8503,8 @@ static const struct _battle_data {
 	{ "autoloot_adjust",                    &battle_config.autoloot_adjust,                 0,      0,      1,              },
 	{ "autoloot_adjust",                    &battle_config.autoloot_adjust,                 0,      0,      1,              },
 	{ "broadcast_hide_name",                &battle_config.broadcast_hide_name,             2,      0,      NAME_LENGTH,    },
 	{ "broadcast_hide_name",                &battle_config.broadcast_hide_name,             2,      0,      NAME_LENGTH,    },
 	{ "skill_drop_items_full",              &battle_config.skill_drop_items_full,           0,      0,      1,              },
 	{ "skill_drop_items_full",              &battle_config.skill_drop_items_full,           0,      0,      1,              },
+	{ "feature.homunculus_autofeed",        &battle_config.feature_homunculus_autofeed,     1,      0,      1,              },
+	{ "feature.homunculus_autofeed_rate",   &battle_config.feature_homunculus_autofeed_rate,30,     0,    100,              },
 
 
 #include "../custom/battle_config_init.inc"
 #include "../custom/battle_config_init.inc"
 };
 };
@@ -8633,6 +8635,13 @@ void battle_adjust_conf()
 	}
 	}
 #endif
 #endif
 
 
+#if PACKETVER < 20170920
+	if( battle_config.feature_homunculus_autofeed ){
+		ShowWarning("conf/battle/feature.conf homunculus autofeeding is enabled but it requires PACKETVER 2017-09-20 or newer, disabling...\n");
+		battle_config.feature_homunculus_autofeed = 0;
+	}
+#endif
+
 #ifndef CELL_NOSTACK
 #ifndef CELL_NOSTACK
 	if (battle_config.custom_cell_stack_limit != 1)
 	if (battle_config.custom_cell_stack_limit != 1)
 		ShowWarning("Battle setting 'custom_cell_stack_limit' takes no effect as this server was compiled without Cell Stack Limit support.\n");
 		ShowWarning("Battle setting 'custom_cell_stack_limit' takes no effect as this server was compiled without Cell Stack Limit support.\n");

+ 2 - 0
src/map/battle.hpp

@@ -639,6 +639,8 @@ struct Battle_Config
 	int autoloot_adjust;
 	int autoloot_adjust;
 	int broadcast_hide_name;
 	int broadcast_hide_name;
 	int skill_drop_items_full;
 	int skill_drop_items_full;
+	int feature_homunculus_autofeed;
+	int feature_homunculus_autofeed_rate;
 
 
 #include "../custom/battle_config_struct.inc"
 #include "../custom/battle_config_struct.inc"
 };
 };

+ 47 - 23
src/map/clif.cpp

@@ -9765,23 +9765,21 @@ void clif_feel_hate_reset(struct map_session_data *sd)
 }
 }
 
 
 
 
-/// Equip window (un)tick ack (ZC_CONFIG).
-/// 02d9 <type>.L <value>.L
-/// type:
-///     0 = open equip window
-///     value:
-///         0 = disabled
-///         1 = enabled
-void clif_equiptickack(struct map_session_data* sd, int flag)
-{
+/// Send out reply to configuration change
+/// 02d9 <type>.L <value>.L (ZC_CONFIG)
+/// type: see enum e_config_type
+/// value:
+///		false = disabled
+///		true = enabled
+void clif_configuration( struct map_session_data* sd, enum e_config_type type, bool enabled ){
 	int fd;
 	int fd;
 	nullpo_retv(sd);
 	nullpo_retv(sd);
 	fd = sd->fd;
 	fd = sd->fd;
 
 
 	WFIFOHEAD(fd, packet_len(0x2d9));
 	WFIFOHEAD(fd, packet_len(0x2d9));
 	WFIFOW(fd, 0) = 0x2d9;
 	WFIFOW(fd, 0) = 0x2d9;
-	WFIFOL(fd, 2) = 0;
-	WFIFOL(fd, 6) = flag;
+	WFIFOL(fd, 2) = type;
+	WFIFOL(fd, 6) = enabled;
 	WFIFOSET(fd, packet_len(0x2d9));
 	WFIFOSET(fd, packet_len(0x2d9));
 }
 }
 
 
@@ -10449,6 +10447,13 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
 		clif_partyinvitationstate(sd);
 		clif_partyinvitationstate(sd);
 		clif_equipcheckbox(sd);
 		clif_equipcheckbox(sd);
 #endif
 #endif
+#if PACKETVER >= 20170920
+		if( sd->hd ){
+			clif_configuration( sd, CONFIG_HOMUNCULUS_AUTOFEED, sd->hd->homunculus.autofeed );
+		}else{
+			clif_configuration( sd, CONFIG_HOMUNCULUS_AUTOFEED, false );
+		}
+#endif
 
 
 		if (sd->guild && battle_config.guild_notice_changemap == 1)
 		if (sd->guild && battle_config.guild_notice_changemap == 1)
 			clif_guild_notice(sd); // Displays after VIP
 			clif_guild_notice(sd); // Displays after VIP
@@ -16543,19 +16548,38 @@ void clif_parse_ViewPlayerEquip(int fd, struct map_session_data* sd)
 }
 }
 
 
 
 
-/// Request to change equip window tick (CZ_CONFIG).
-/// 02d8 <type>.L <flag>.L
-/// type:
-///     0 = open equip window
+/// Request to a configuration.
+/// 02d8 <type>.L <flag>.L (CZ_CONFIG)
+/// type: see enum e_config_type
 /// flag:
 /// flag:
-///     0 = disabled
-///     1 = enabled
-void clif_parse_EquipTick(int fd, struct map_session_data* sd)
-{
-	//int type = RFIFOL(fd,packet_db[cmd].pos[0]);
-	bool flag = RFIFOL(fd,packet_db[RFIFOW(fd,0)].pos[1]) != 0;
-	sd->status.show_equip = flag;
-	clif_equiptickack(sd, flag);
+///     false = disabled
+///     true = enabled
+void clif_parse_configuration( int fd, struct map_session_data* sd ){
+	int cmd = RFIFOW(fd,0);
+	int type = RFIFOL(fd,packet_db[cmd].pos[0]);
+	bool flag = RFIFOL(fd,packet_db[cmd].pos[1]) != 0;
+
+	switch( type ){
+		case CONFIG_OPEN_EQUIPMENT_WINDOW:
+			sd->status.show_equip = flag;
+			break;
+		case CONFIG_PET_AUTOFEED:
+			// TODO: Implement with pet evolution system
+			break;
+		case CONFIG_HOMUNCULUS_AUTOFEED:
+			// Player can not click this if he does not have a homunculus or it is vaporized
+			if( sd->hd == nullptr || sd->hd->homunculus.vaporize ){
+				return;
+			}
+
+			sd->hd->homunculus.autofeed = flag;
+			break;
+		default:
+			ShowWarning( "clif_parse_configuration: received unknown configuration type '%d'...\n", type );
+			return;
+	}
+
+	clif_configuration( sd, static_cast<e_config_type>(type), flag );
 }
 }
 
 
 /// Request to change party invitation tick.
 /// Request to change party invitation tick.

+ 8 - 1
src/map/clif.hpp

@@ -536,6 +536,13 @@ enum e_damage_type : uint8_t {
 	DMG_TOUCH,				/// (touch skill?)
 	DMG_TOUCH,				/// (touch skill?)
 };
 };
 
 
+enum e_config_type : uint32 {
+	CONFIG_OPEN_EQUIPMENT_WINDOW = 0,
+	// Unknown
+	CONFIG_PET_AUTOFEED = 2,
+	CONFIG_HOMUNCULUS_AUTOFEED
+};
+
 int clif_setip(const char* ip);
 int clif_setip(const char* ip);
 void clif_setbindip(const char* ip);
 void clif_setbindip(const char* ip);
 void clif_setport(uint16 port);
 void clif_setport(uint16 port);
@@ -862,7 +869,7 @@ void clif_homskillup(struct map_session_data *sd, uint16 skill_id);	//[orn]
 int clif_hom_food(struct map_session_data *sd,int foodid,int fail);	//[orn]
 int clif_hom_food(struct map_session_data *sd,int foodid,int fail);	//[orn]
 void clif_send_homdata(struct map_session_data *sd, int state, int param);	//[orn]
 void clif_send_homdata(struct map_session_data *sd, int state, int param);	//[orn]
 
 
-void clif_equiptickack(struct map_session_data* sd, int flag);
+void clif_configuration( struct map_session_data* sd, enum e_config_type type, bool enabled );
 void clif_partytickack(struct map_session_data* sd, bool flag);
 void clif_partytickack(struct map_session_data* sd, bool flag);
 void clif_viewequip_ack(struct map_session_data* sd, struct map_session_data* tsd);
 void clif_viewequip_ack(struct map_session_data* sd, struct map_session_data* tsd);
 void clif_equipcheckbox(struct map_session_data* sd);
 void clif_equipcheckbox(struct map_session_data* sd);

+ 1 - 1
src/map/clif_packetdb.hpp

@@ -1123,7 +1123,7 @@
 	packet(0x02d5,2);
 	packet(0x02d5,2);
 	parseable_packet(0x02d6,6,clif_parse_ViewPlayerEquip,2);
 	parseable_packet(0x02d6,6,clif_parse_ViewPlayerEquip,2);
 	packet(0x02d7,-1);
 	packet(0x02d7,-1);
-	parseable_packet(0x02d8,10,clif_parse_EquipTick,2,6);
+	parseable_packet(0x02d8,10,clif_parse_configuration,2,6);
 	packet(0x02d9,10);
 	packet(0x02d9,10);
 	packet(0x02da,3);
 	packet(0x02da,3);
 	parseable_packet(0x02db,-1,clif_parse_BattleChat,2,4);
 	parseable_packet(0x02db,-1,clif_parse_BattleChat,2,4);

+ 1 - 0
src/map/clif_shuffle.hpp

@@ -4008,6 +4008,7 @@
 	parseable_packet(0x0281,6,clif_parse_GetCharNameRequest,2);
 	parseable_packet(0x0281,6,clif_parse_GetCharNameRequest,2);
 // 2017-09-13bRagexeRE
 // 2017-09-13bRagexeRE
 #elif PACKETVER == 20170913
 #elif PACKETVER == 20170913
+	parseable_packet(0x0281,6,clif_parse_GetCharNameRequest,2);
 	parseable_packet(0x035F,26,clif_parse_FriendsListAdd,2);
 	parseable_packet(0x035F,26,clif_parse_FriendsListAdd,2);
 	parseable_packet(0x0437,-1,clif_parse_SearchStoreInfo,2,4,5,9,13,14,15);
 	parseable_packet(0x0437,-1,clif_parse_SearchStoreInfo,2,4,5,9,13,14,15);
 	parseable_packet(0x07E4,8,clif_parse_MoveFromKafra,2,4);
 	parseable_packet(0x07E4,8,clif_parse_MoveFromKafra,2,4);

+ 4 - 0
src/map/homunculus.cpp

@@ -892,6 +892,10 @@ static int hom_hungry(int tid, unsigned int tick, int id, intptr_t data)
 		clif_emotion(&hd->bl, ET_OK);
 		clif_emotion(&hd->bl, ET_OK);
 	}
 	}
 
 
+	if( battle_config.feature_homunculus_autofeed && hd->homunculus.autofeed && hd->homunculus.hunger <= battle_config.feature_homunculus_autofeed_rate ){
+		hom_food( sd, hd );
+	}
+
 	if (hd->homunculus.hunger < 0) {
 	if (hd->homunculus.hunger < 0) {
 		hd->homunculus.hunger = 0;
 		hd->homunculus.hunger = 0;
 		// Delete the homunculus if intimacy <= 100
 		// Delete the homunculus if intimacy <= 100