summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--conf/map/battle/feature.conf5
-rw-r--r--db/pre-re/pet_db.conf7
-rw-r--r--db/re/item_db.conf203
-rw-r--r--db/re/pet_db.conf377
-rw-r--r--sql-files/main.sql2
-rw-r--r--sql-files/upgrades/2018-06-05--12-02.sql24
-rw-r--r--sql-files/upgrades/index.txt1
-rw-r--r--src/char/int_pet.c15
-rw-r--r--src/common/mmo.h1
-rw-r--r--src/map/battle.c1
-rw-r--r--src/map/battle.h1
-rw-r--r--src/map/clif.c144
-rw-r--r--src/map/clif.h15
-rw-r--r--src/map/packets.h2
-rw-r--r--src/map/packets_struct.h12
-rw-r--r--src/map/pet.c163
-rw-r--r--src/map/pet.h15
-rw-r--r--tools/petevolutionconverter.py248
-rw-r--r--tools/utils/common.py15
19 files changed, 1213 insertions, 38 deletions
diff --git a/conf/map/battle/feature.conf b/conf/map/battle/feature.conf
index 8d19539b4..285633209 100644
--- a/conf/map/battle/feature.conf
+++ b/conf/map/battle/feature.conf
@@ -71,6 +71,11 @@ features: {
// false: disable
enable_homun_autofeed: true
+ // Allow Pet autofeeding
+ // true: enable (Default)
+ // false: disable
+ enable_pet_autofeed: true
+
// Enable Attendance System for clients >= 2018-03-07bRagexeRE or 2018-04-04bRagexe or 2018-04-11aRagexe_zero
// true: enable (Default)
// false: disable
diff --git a/db/pre-re/pet_db.conf b/db/pre-re/pet_db.conf
index 3ce78e546..91f9cb8e8 100644
--- a/db/pre-re/pet_db.conf
+++ b/db/pre-re/pet_db.conf
@@ -56,6 +56,13 @@ pet_db:(
AttackRate: attack rate (int, defaults to 0)
DefendRate: Defence attack (int, defaults to 0)
ChangeTargetRate: change target (int, defaults to 0)
+ Evolve: {
+ EggID: { (string, Evolved Pet EggID)
+ Name: Amount (items required to perform evolution)
+ ...
+ }
+ }
+ AutoFeed: true/false (boolean, defaults to false)
PetScript: <" Pet Script (can also be multi-line) ">
EquipScript: <" Equip Script (can also be multi-line) ">
},
diff --git a/db/re/item_db.conf b/db/re/item_db.conf
index bc3470fd9..3974a911e 100644
--- a/db/re/item_db.conf
+++ b/db/re/item_db.conf
@@ -83540,6 +83540,209 @@ item_db: (
Type: "IT_PETEGG"
Buy: 20
},
+{
+ Id: 9069
+ AegisName: "Mastering_Egg"
+ Name: "Mastering Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9070
+ AegisName: "Savage_Egg"
+ Name: "Savage Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9071
+ AegisName: "Grand_Peco_Peco_Egg"
+ Name: "Grand Peco Peco Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9087
+ AegisName: "High_Orc_Egg"
+ Name: "High Orc Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9088
+ AegisName: "Angeling_Egg"
+ Name: "Angeling Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9089
+ AegisName: "Am_Mut_Egg"
+ Name: "Am Mut Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9090
+ AegisName: "Little_Isis_Egg"
+ Name: "Little Isis Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9091
+ AegisName: "Choco_Egg"
+ Name: "Choco Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9092
+ AegisName: "Eggring_Egg"
+ Name: "Eggring Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9093
+ AegisName: "Hyegun_Egg"
+ Name: "Hyegun Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9094
+ AegisName: "Leaf_Lunatic_Egg"
+ Name: "Leaf Lunatic Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9095
+ AegisName: "Nine_Tails_Egg"
+ Name: "Nine Tails Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9096
+ AegisName: "Cat_o_Nine_Tails_Egg"
+ Name: "Cat o' Nine Tails Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9097
+ AegisName: "Diabolic_Egg_"
+ Name: "Diabolic Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9098
+ AegisName: "Earth_Deleter_Egg"
+ Name: "Earth Deleter Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9099
+ AegisName: "Teddy_Bear_Egg"
+ Name: "Teddy Bear Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9100
+ AegisName: "Gremlin_Egg"
+ Name: "Gremlin Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9101
+ AegisName: "Scatelon_Egg"
+ Name: "Scatleton Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9102
+ AegisName: "Mummy_Egg"
+ Name: "Mummy Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9103
+ AegisName: "Willow_Egg"
+ Name: "Willow Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9104
+ AegisName: "Roween_Egg"
+ Name: "Roween Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9105
+ AegisName: "Hodremlin_Egg"
+ Name: "Hodremlin Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9106
+ AegisName: "Metaller_Egg"
+ Name: "Metaller Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9107
+ AegisName: "Ancient_Mummy_Egg"
+ Name: "Ancient Mummy Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9108
+ AegisName: "Abandoned_Teddy_Bear_Egg"
+ Name: "Abandoned Teddy Bear Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9109
+ AegisName: "Sweet_Drops_Egg"
+ Name: "Sweet Drops Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9111
+ AegisName: "Phreeoni_Egg"
+ Name: "Phreeoni Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9112
+ AegisName: "Moonlight_Flower_Egg"
+ Name: "Moonlight Flower Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
+{
+ Id: 9113
+ AegisName: "Skelion_Egg"
+ Name: "Skelion Egg"
+ Type: "IT_PETEGG"
+ Buy: 20
+},
//== Pet Accessories =======================================
{
diff --git a/db/re/pet_db.conf b/db/re/pet_db.conf
index 6d6083e59..a28da61ff 100644
--- a/db/re/pet_db.conf
+++ b/db/re/pet_db.conf
@@ -56,6 +56,13 @@ pet_db:(
AttackRate: attack rate (int, defaults to 0)
DefendRate: Defence attack (int, defaults to 0)
ChangeTargetRate: change target (int, defaults to 0)
+ Evolve: {
+ EggID: { (string, Evolved Pet EggID)
+ Name: Amount (items required to perform evolution)
+ ...
+ }
+ }
+ AutoFeed: true/false (boolean, defaults to false)
PetScript: <" Pet Script (can also be multi-line) ">
EquipScript: <" Equip Script (can also be multi-line) ">
},
@@ -87,6 +94,12 @@ pet_db:(
bonus(bLuk, 2);
bonus(bCritical, 1);
">
+ Evolve: {
+ Mastering_Egg: {
+ Leaf_Of_Yggdrasil: 10
+ Unripe_Apple: 3
+ }
+ }
},
{
Id: 1011
@@ -170,6 +183,15 @@ pet_db:(
bonus(bMaxHP, 150);
bonus(bMaxSP, -10);
">
+ Evolve: {
+ Grand_Peco_Peco_Egg: {
+ Pet_Food: 10
+ Fatty_Chubby_Earthworm: 3
+ Peco_Wing_Feather: 300
+ Pecopeco_Card: 1
+ Fruit_Of_Mastela: 10
+ }
+ }
},
{
Id: 1023
@@ -198,6 +220,15 @@ pet_db:(
bonus(bAtk, 10);
bonus(bDef, -3);
">
+ Evolve: {
+ High_Orc_Egg: {
+ Horror_Of_Tribe: 3
+ Orcish_Sword: 1
+ Orcish_Voucher: 500
+ Cigar: 1
+ Orc_Warrior_Card: 1
+ }
+ }
},
{
Id: 1026
@@ -252,6 +283,14 @@ pet_db:(
bonus(bMatkRate, -1);
bonus(bAtkRate, 1);
">
+ Evolve: {
+ Little_Isis_Egg: {
+ Armlet_Of_Obedience: 3
+ Queens_Hair_Ornament: 1
+ Shining_Scales: 300
+ Crystal_Jewel__: 6
+ }
+ }
},
{
Id: 1031
@@ -391,6 +430,14 @@ pet_db:(
bonus(bHPrecovRate, 5);
bonus(bMaxHP, 25);
">
+ Evolve: {
+ Metaller_Egg: {
+ Singing_Plant: 3
+ Grasshoppers_Leg: 777
+ Yellow_Herb: 200
+ Metaller_Card: 1
+ }
+ }
},
{
Id: 1056
@@ -447,6 +494,14 @@ pet_db:(
bonus(bCritical, 3);
bonus(bLuk, -1);
">
+ Evolve: {
+ Choco_Egg: {
+ Tropical_Banana: 3
+ Monkey_Doll: 2
+ Cacao: 300
+ Yoyo_Card: 1
+ }
+ }
},
{
Id: 1063
@@ -474,6 +529,14 @@ pet_db:(
bonus(bCritical, 2);
bonus(bAtk, 2);
">
+ Evolve: {
+ Leaf_Lunatic_Egg: {
+ Great_Leaf: 100
+ Clover: 250
+ Four_Leaf_Clover: 30
+ Leaf_Lunatic_Card: 1
+ }
+ }
},
{
Id: 1077
@@ -585,6 +648,14 @@ pet_db:(
bonus(bMaxHPrate, -3);
bonus(bMaxSPrate, -3);
">
+ Evolve: {
+ Diabolic_Egg_: {
+ Contracts_In_Shadow: 3
+ Petite_DiablOfs_Wing: 250
+ Sacred_Marks: 30
+ Deviruchi_Card: 1
+ }
+ }
},
{
Id: 1110
@@ -612,6 +683,14 @@ pet_db:(
bonus(bMatkRate, 1);
bonus(bAtkRate, -1);
">
+ Evolve: {
+ Am_Mut_Egg: {
+ Old_Broom: 3
+ Violet_Dyestuffs: 3
+ Dokkaebi_Horn: 300
+ Gold: 3
+ }
+ }
},
{
Id: 1113
@@ -640,6 +719,22 @@ pet_db:(
bonus(bHit, 3);
bonus(bAtk, 3);
">
+ Evolve: {
+ Eggring_Egg: {
+ Piece_Of_Egg_Shell: 20
+ Old_Frying_Pan: 10
+ Apple_Juice: 3
+ Eggring_Card: 1
+ }
+/*
+ Sweet_Drops_Egg: {
+ 25290: 500
+ Candy: 50
+ Candy_Striper: 50
+ Drops_Card: 1
+ }
+*/
+ }
},
{
Id: 1155
@@ -668,6 +763,14 @@ pet_db:(
bonus(bMdef, -2);
bonus(bAspdRate, 1);
">
+ Evolve: {
+ Earth_Deleter_Egg: {
+ Shining_Stone: 3
+ Petti_Tail: 100
+ Aloebera: 150
+ Deleter_Card: 1
+ }
+ }
},
{
Id: 1167
@@ -695,6 +798,14 @@ pet_db:(
bonus(bVit, 1);
bonus(bMaxHP, 50);
">
+ Evolve: {
+ Savage_Egg: {
+ Pet_Food: 10
+ Sweet_Milk: 3
+ Meat: 100
+ Feather: 50
+ }
+ }
},
{
Id: 1170
@@ -750,6 +861,14 @@ pet_db:(
bonus(bVit, 1);
bonus2(bResEff, Eff_Stun, 100);
">
+ Evolve: {
+ Hyegun_Egg: {
+ Hyegun_Hat: 1
+ Munak_Doll: 100
+ Old_Portrait: 50
+ Hyegun_Card: 1
+ }
+ }
},
{
Id: 1200
@@ -1646,4 +1765,262 @@ pet_db:(
bonus(bCritical, 1);
">
},
+// New Pets [Need Info]
+{
+ Id: 1090
+ SpriteName: "MASTERING"
+ Name: "Mastering"
+ EggItem: "Mastering_Egg"
+ AutoFeed: true
+ Evolve: {
+ Angeling_Egg: {
+ Yellow_Potion: 20
+ Spirit_Chain: 1
+ White_Herb: 50
+ Jellopy: 200
+ }
+ }
+},
+{
+ Id: 1096
+ SpriteName: "ANGELING"
+ Name: "Angeling"
+ EggItem: "Angeling_Egg"
+ AutoFeed: true
+},
+{
+ Id: 1301
+ SpriteName: "AM_MUT"
+ Name: "Am Mut"
+ EggItem: "Am_Mut_Egg"
+ AutoFeed: true
+},
+/*
+{
+ Id: 3636
+ SpriteName: "LITTLE_ISIS"
+ Name: "Little Isis"
+ EggItem: "Little_Isis_Egg"
+ AutoFeed: true
+},
+*/
+{
+ Id: 1214
+ SpriteName: "CHOCO"
+ Name: "Choco"
+ EggItem: "Choco_Egg"
+ AutoFeed: true
+},
+/*
+{
+ Id: 3495
+ SpriteName: "DR_EGGRING"
+ Name: "Eggring"
+ EggItem: "Eggring_Egg"
+ AutoFeed: true
+},
+*/
+{
+ Id: 1512
+ SpriteName: "HYEGUN"
+ Name: "Hyegun"
+ EggItem: "Hyegun_Egg"
+ AutoFeed: true
+},
+/*
+{
+ Id: 3496
+ SpriteName: "DR_LUNATIC"
+ Name: "Leaf Lunatic"
+ EggItem: "Leaf_Lunatic_Egg"
+ AutoFeed: true
+},
+*/
+{
+ Id: 1180
+ SpriteName: "NINE_TAIL"
+ Name: "Nine Tails"
+ EggItem: "Nine_Tails_Egg"
+ AutoFeed: true
+ Evolve: {
+/*
+ Cat_o_Nine_Tails_Egg: {
+ 23187: 3
+ Fox_Tail: 999
+ Punisher: 1
+ Nine_Tail_Card: 1
+ }
+*/
+ }
+},
+{
+ Id: 1307
+ SpriteName: "CAT_O_NINE_TAIL"
+ Name: "Cat o' Nine Tails"
+ EggItem: "Cat_o_Nine_Tails_Egg"
+ AutoFeed: true
+ Evolve: {
+/*
+ Moonlight_Flower_Egg: {
+ 25375: 30
+ Nine_Tail_Card: 10
+ Sohee_Card: 10
+ Munak_Card: 10
+ }
+*/
+ }
+},
+/*
+{
+ Id: 3669
+ SpriteName: "DIABOLIC2"
+ Name: "Diabolic"
+ EggItem: "Diabolic_Egg_"
+ AutoFeed: true
+},
+*/
+/*
+{
+ Id: 3670
+ SpriteName: "DELETER_2"
+ Name: "Earth Deleter"
+ EggItem: "Earth_Deleter_Egg"
+ AutoFeed: true
+},
+*/
+{
+ Id: 1622
+ SpriteName: "TEDDY_BEAR"
+ Name: "Teddy Bear"
+ EggItem: "Teddy_Bear_Egg"
+ AutoFeed: true
+ Evolve: {
+/*
+ Abandoned_Teddy_Bear_Egg: {
+ 23189: 3
+ Cursed_Seal: 300
+ Cardinal_Jewel_: 50
+ Teddy_Bear_Card: 1
+ }
+*/
+ }
+},
+{
+ Id: 1632
+ SpriteName: "GREMLIN"
+ Name: "Gremlin"
+ EggItem: "Gremlin_Egg"
+ AutoFeed: true
+ Evolve: {
+/*
+ Hodremlin_Egg: {
+ 23188: 3
+ Damp_Darkness: 50
+ Will_Of_Darkness: 200
+ Hodremlin_Card: 1
+ }
+*/
+ }
+},
+/*
+{
+ Id: 3731
+ SpriteName: "SCATLETON"
+ Name: "Scatleton Crate"
+ EggItem: "Scatleton_Crate"
+ AutoFeed: true
+},
+*/
+{
+ Id: 1041
+ SpriteName: "MUMMY"
+ Name: "Mummy"
+ EggItem: "Mummy_Egg"
+ AutoFeed: true
+ Evolve: {
+/*
+ Ancient_Mummy_Egg: {
+ 23256: 3
+ Rune_Of_Darkness: 200
+ Gold: 30
+ Ancient_Mummy_Card: 1
+ }
+*/
+ }
+},
+{
+ Id: 1010
+ SpriteName: "WILOW"
+ Name: "Willow"
+ EggItem: "Willow_Egg"
+ AutoFeed: true
+},
+{
+ Id: 1782
+ SpriteName: "ROWEEN"
+ Name: "Roween"
+ EggItem: "Roween_Egg"
+ AutoFeed: true
+},
+{
+ Id: 1773
+ SpriteName: "HODREMLIN"
+ Name: "Hodremlin"
+ EggItem: "Hodremlin_Egg"
+ AutoFeed: true
+},
+{
+ Id: 1058
+ SpriteName: "METALLER"
+ Name: "Metaller"
+ EggItem: "Metaller_Egg"
+ AutoFeed: true
+},
+{
+ Id: 1297
+ SpriteName: "ANCIENT_MUMMY"
+ Name: "Ancient Mummy"
+ EggItem: "Ancient_Mummy_Egg"
+ AutoFeed: true
+},
+/*{
+ Id: 2995
+ SpriteName: "XM_TEDDY_BEAR"
+ Name: "Abandoned Teddy Bear"
+ EggItem: "Abandoned_Teddy_Bear_Egg"
+ AutoFeed: true
+},
+*/
+/* UNKNOWN MONSTER
+{
+ Id: 0
+ SpriteName: "X"
+ Name: "Sweet Drops"
+ EggItem: "Sweet_Drops_Egg"
+ AutoFeed: true
+},
+*/
+{
+ Id: 1159
+ SpriteName: "PHREEONI"
+ Name: "Phreeoni"
+ EggItem: "Phreeoni_Egg"
+ AutoFeed: true
+},
+{
+ Id: 1150
+ SpriteName: "MOONLIGHT"
+ Name: "Moonlight Flower"
+ EggItem: "Moonlight_Flower_Egg"
+ AutoFeed: true
+},
+/*
+{
+ Id: 3971
+ SpriteName: "SKELION"
+ Name: "Skelion"
+ EggItem: "Skelion_Egg"
+ AutoFeed: true
+},
+*/
)
diff --git a/sql-files/main.sql b/sql-files/main.sql
index 2e51d6a12..8f7e5355b 100644
--- a/sql-files/main.sql
+++ b/sql-files/main.sql
@@ -782,6 +782,7 @@ CREATE TABLE IF NOT EXISTS `pet` (
`hungry` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',
`rename_flag` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0',
`incubate` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+ `autofeed` TINYINT(2) UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY (`pet_id`)
) ENGINE=MyISAM;
@@ -893,6 +894,7 @@ INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1496588700); -- 2017-06-0
INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1509835214); -- 2017-11-04--10-39.sql
INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1519671456); -- 2018-02-26--15-57.sql
INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1520654809); -- 2018-03-10--04-06.sql
+INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1528180320); -- 2018-06-05--12-02.sql
--
-- Table structure for table `storage`
--
diff --git a/sql-files/upgrades/2018-06-05--12-02.sql b/sql-files/upgrades/2018-06-05--12-02.sql
new file mode 100644
index 000000000..26c22243f
--- /dev/null
+++ b/sql-files/upgrades/2018-06-05--12-02.sql
@@ -0,0 +1,24 @@
+#1528180320
+
+-- This file is part of Hercules.
+-- http://herc.ws - http://github.com/HerculesWS/Hercules
+--
+-- Copyright (C) 2018 Hercules Dev Team
+-- Copyright (C) 2018 Dastgir
+--
+-- Hercules is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ALTER TABLE `pet` ADD COLUMN `autofeed` TINYINT(2) UNSIGNED NOT NULL DEFAULT '0';
+
+INSERT INTO `sql_updates` (`timestamp`, `ignored`) VALUES (1528180320 , 'No');
diff --git a/sql-files/upgrades/index.txt b/sql-files/upgrades/index.txt
index dac60b6aa..5a737f93a 100644
--- a/sql-files/upgrades/index.txt
+++ b/sql-files/upgrades/index.txt
@@ -45,3 +45,4 @@
2017-11-04--10-39.sql
2018-02-26--15-57.sql
2018-03-10--04-06.sql
+2018-06-05--12-02.sql
diff --git a/src/char/int_pet.c b/src/char/int_pet.c
index 0ece11b51..f270f205d 100644
--- a/src/char/int_pet.c
+++ b/src/char/int_pet.c
@@ -66,19 +66,19 @@ int inter_pet_tosql(const struct s_pet *p)
if (p->pet_id == 0) {
// New pet.
if (SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` "
- "(`class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incubate`) "
- "VALUES ('%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')",
+ "(`class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incubate`, `autofeed`) "
+ "VALUES ('%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')",
pet_db, p->class_, esc_name, p->account_id, p->char_id, p->level, p->egg_id,
- p->equip, intimate, hungry, p->rename_flag, p->incubate)) {
+ p->equip, intimate, hungry, p->rename_flag, p->incubate, p->autofeed)) {
Sql_ShowDebug(inter->sql_handle);
return 0;
}
pet_id = (int)SQL->LastInsertId(inter->sql_handle);
} else {
// Update pet.
- if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `class`='%d',`name`='%s',`account_id`='%d',`char_id`='%d',`level`='%d',`egg_id`='%d',`equip`='%d',`intimate`='%d',`hungry`='%d',`rename_flag`='%d',`incubate`='%d' WHERE `pet_id`='%d'",
+ if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `class`='%d',`name`='%s',`account_id`='%d',`char_id`='%d',`level`='%d',`egg_id`='%d',`equip`='%d',`intimate`='%d',`hungry`='%d',`rename_flag`='%d',`incubate`='%d', `autofeed`='%d' WHERE `pet_id`='%d'",
pet_db, p->class_, esc_name, p->account_id, p->char_id, p->level, p->egg_id,
- p->equip, intimate, hungry, p->rename_flag, p->incubate, p->pet_id)) {
+ p->equip, intimate, hungry, p->rename_flag, p->incubate, p->autofeed, p->pet_id)) {
Sql_ShowDebug(inter->sql_handle);
return 0;
}
@@ -102,9 +102,9 @@ int inter_pet_fromsql(int pet_id, struct s_pet* p)
nullpo_ret(p);
memset(p, 0, sizeof(struct s_pet));
- //`pet` (`pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incubate`)
+ //`pet` (`pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incubate`, `autofeed`)
- if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incubate` FROM `%s` WHERE `pet_id`='%d'", pet_db, pet_id) )
+ if( SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `pet_id`, `class`,`name`,`account_id`,`char_id`,`level`,`egg_id`,`equip`,`intimate`,`hungry`,`rename_flag`,`incubate`,`autofeed` FROM `%s` WHERE `pet_id`='%d'", pet_db, pet_id) )
{
Sql_ShowDebug(inter->sql_handle);
return 0;
@@ -124,6 +124,7 @@ int inter_pet_fromsql(int pet_id, struct s_pet* p)
SQL->GetData(inter->sql_handle, 9, &data, NULL); p->hungry = atoi(data);
SQL->GetData(inter->sql_handle, 10, &data, NULL); p->rename_flag = atoi(data);
SQL->GetData(inter->sql_handle, 11, &data, NULL); p->incubate = atoi(data);
+ SQL->GetData(inter->sql_handle, 12, &data, NULL); p->autofeed = atoi(data);
SQL->FreeResult(inter->sql_handle);
diff --git a/src/common/mmo.h b/src/common/mmo.h
index 74d48dd47..0b4ba4a45 100644
--- a/src/common/mmo.h
+++ b/src/common/mmo.h
@@ -544,6 +544,7 @@ struct s_pet {
char name[NAME_LENGTH];
char rename_flag;
char incubate;
+ int autofeed;
};
struct s_homunculus { //[orn]
diff --git a/src/map/battle.c b/src/map/battle.c
index 6a961afeb..4d320704a 100644
--- a/src/map/battle.c
+++ b/src/map/battle.c
@@ -7319,6 +7319,7 @@ static const struct battle_data {
{ "features/rodex", &battle_config.feature_rodex, 1, 0, 1, },
{ "features/rodex_use_accountmail", &battle_config.feature_rodex_use_accountmail, 0, 0, 1, },
{ "features/enable_homun_autofeed", &battle_config.feature_enable_homun_autofeed, 1, 0, 1, },
+ { "features/enable_pet_autofeed", &battle_config.feature_enable_pet_autofeed, 1, 0, 1, },
{ "storage_use_item", &battle_config.storage_use_item, 0, 0, 1, },
{ "features/enable_attendance_system", &battle_config.feature_enable_attendance_system,1, 0, 1, },
{ "features/feature_attendance_endtime",&battle_config.feature_attendance_endtime, 1, 0, 99999999, },
diff --git a/src/map/battle.h b/src/map/battle.h
index c325daf0d..f4176f142 100644
--- a/src/map/battle.h
+++ b/src/map/battle.h
@@ -563,6 +563,7 @@ struct Battle_Config {
int feature_rodex_use_accountmail;
int feature_enable_homun_autofeed;
+ int feature_enable_pet_autofeed;
int storage_use_item;
diff --git a/src/map/clif.c b/src/map/clif.c
index aeaf03e43..c085c4fe4 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -9093,7 +9093,7 @@ void clif_feel_hate_reset(struct map_session_data *sd)
/// value:
/// 0 = disabled
/// 1 = enabled
-void clif_zc_config(struct map_session_data* sd, int type, int flag)
+void clif_zc_config(struct map_session_data* sd, enum CZ_CONFIG type, int flag)
{
int fd;
nullpo_retv(sd);
@@ -13755,6 +13755,115 @@ void clif_parse_ChangePetName(int fd, struct map_session_data *sd)
pet->change_name(sd, RFIFOP(fd,2));
}
+void clif_parse_pet_evolution(int fd, struct map_session_data *sd) __attribute__((nonnull (2)));
+/// Request to Evolve the pet (CZ_PET_EVOLUTION) [Dastgir/Hercules]
+/// 09fb <Length>.W <EvolvedPetEggID>.W {<index>.W <amount>.W}*items
+void clif_parse_pet_evolution(int fd, struct map_session_data *sd)
+{
+ const struct PACKET_CZ_PET_EVOLUTION *p = RP2PTR(fd);
+ int i = 0, idx, petIndex;
+
+ Assert_retv(p->PacketLength >= (uint16)sizeof(struct PACKET_CZ_PET_EVOLUTION));
+
+ if (sd->status.pet_id == 0) {
+ clif->petEvolutionResult(fd, PET_EVOL_NO_CALLPET);
+ return;
+ }
+
+ ARR_FIND(0, MAX_INVENTORY, idx, sd->status.inventory[idx].card[0] == CARD0_PET &&
+ sd->status.pet_id == MakeDWord(sd->status.inventory[idx].card[1], sd->status.inventory[idx].card[2]));
+
+ if (idx == MAX_INVENTORY) {
+ clif->petEvolutionResult(fd, PET_EVOL_NO_PETEGG);
+ return;
+ }
+
+ // Not Loyal Yet
+ if (sd->pd == NULL || sd->pd->pet.intimate < 900) {
+ clif->petEvolutionResult(fd, PET_EVOL_RG_FAMILIAR);
+ return;
+ }
+
+ ARR_FIND(0, MAX_PET_DB, petIndex, pet->db[petIndex].class_ == sd->pd->pet.class_);
+
+ if (petIndex == MAX_PET_DB) {
+ // Which error?
+ clif->petEvolutionResult(fd, PET_EVOL_UNKNOWN);
+ return;
+ }
+
+ // Client side validation is not done as it is insecure.
+ for (i = 0; i < VECTOR_LENGTH(pet->db[petIndex].evolve_data); i++) {
+ struct pet_evolve_data *ped = &VECTOR_INDEX(pet->db[petIndex].evolve_data, i);
+ if (ped->petEggId == p->EvolvedPetEggID) {
+ int j;
+ int pet_id;
+
+ if (VECTOR_LENGTH(ped->items) == 0) {
+ clif->petEvolutionResult(fd, PET_EVOL_NO_RECIPE);
+ return;
+ }
+ for (j = 0; j < VECTOR_LENGTH(ped->items); j++) {
+ struct itemlist_entry *list = &VECTOR_INDEX(ped->items, j);
+ int n = pc->search_inventory(sd, list->id);
+
+ if (n == INDEX_NOT_FOUND) {
+ clif->petEvolutionResult(fd, PET_EVOL_NO_MATERIAL);
+ return;
+ }
+ }
+
+ for (j = 0; j < VECTOR_LENGTH(ped->items); j++) {
+ struct itemlist_entry *list = &VECTOR_INDEX(ped->items, j);
+ int n = pc->search_inventory(sd, list->id);
+
+ if (pc->delitem(sd, n, list->amount, 0, DELITEM_NORMAL, LOG_TYPE_EGG) == 1) {
+ clif->petEvolutionResult(fd, PET_EVOL_NO_MATERIAL);
+ return;
+ }
+ }
+
+ // Return to Egg
+ pet->return_egg(sd, sd->pd);
+
+ if (pc->delitem(sd, idx, 1, 0, DELITEM_NORMAL, LOG_TYPE_EGG) == 1) {
+ clif->petEvolutionResult(fd, PET_EVOL_NO_PETEGG);
+ return;
+ }
+
+ pet_id = pet->search_petDB_index(ped->petEggId, PET_EGG);
+ if (pet_id >= 0) {
+ sd->catch_target_class = pet->db[pet_id].class_;
+
+ intif->create_pet(
+ sd->status.account_id, sd->status.char_id,
+ (short)pet->db[pet_id].class_, (short)mob->db(pet->db[pet_id].class_)->lv,
+ (short)pet->db[pet_id].EggID, 0, (short)pet->db[pet_id].intimate,
+ 100, 0, 1, pet->db[pet_id].jname);
+ clif->petEvolutionResult(fd, PET_EVOL_SUCCESS);
+ } else {
+ clif->petEvolutionResult(fd, PET_EVOL_UNKNOWN);
+ }
+ return;
+ }
+ }
+
+ clif->petEvolutionResult(fd, PET_EVOL_UNKNOWN);
+}
+
+/**
+ * Result of Pet Evolution (ZC_PET_EVOLUTION_RESULT)
+ * 0x9fc <Result>.L
+ */
+void clif_pet_evolution_result(int fd, enum pet_evolution_result result) {
+#if PACKETVER >= 20140122
+ WFIFOHEAD(fd, packet_len(0x9fc));
+ WFIFOW(fd, 0) = 0x9fc;
+ WFIFOL(fd, 2) = result;
+ WFIFOSET(fd, packet_len(0x9fc));
+#endif
+}
+
void clif_parse_GMKick(int fd, struct map_session_data *sd) __attribute__((nonnull (2)));
/// /kill (CZ_DISCONNECT_CHARACTER).
/// Request to disconnect a character.
@@ -16154,24 +16263,38 @@ void clif_parse_cz_config(int fd, struct map_session_data *sd) __attribute__((no
/// 02d8 <type>.L <value>.L
/// type:
/// 0 = open equip window
+/// 2 = pet autofeeding
/// 3 = homunculus autofeeding
/// value:
/// 0 = disabled
/// 1 = enabled
void clif_parse_cz_config(int fd, struct map_session_data *sd)
{
- int type = RFIFOL(fd, 2);
+ enum CZ_CONFIG type = RFIFOL(fd, 2);
int flag = RFIFOL(fd, 6);
- if (type == CZ_CONFIG_OPEN_EQUIPMENT_WINDOW) {
+ switch (type) {
+ case CZ_CONFIG_OPEN_EQUIPMENT_WINDOW:
sd->status.show_equip = flag;
- } else if (type == CZ_CONFIG_HOMUNCULUS_AUTOFEEDING) {
- struct homun_data *hd;
- hd = sd->hd;
+ break;
+ case CZ_CONFIG_PET_AUTOFEEDING: {
+ struct pet_data *pd = sd->pd;
+ nullpo_retv(pd);
+ if (pd->petDB->autofeed == 0) {
+ clif->message(fd, "Autofeed is disabled for this pet.");
+ return;
+ }
+ pd->pet.autofeed = flag;
+ break;
+ }
+ case CZ_CONFIG_HOMUNCULUS_AUTOFEEDING: {
+ struct homun_data *hd = sd->hd;
nullpo_retv(hd);
hd->homunculus.autofeed = flag;
- } else {
- ShowWarning("clif_parse_cz_config: Unsupported type has been received (%d).", type);
+ break;
+ }
+ default:
+ ShowWarning("clif_parse_cz_config: Unsupported type has been received (%u).\n", type);
return;
}
clif->zc_config(sd, type, flag);
@@ -21991,4 +22114,9 @@ void clif_defaults(void) {
clif->pReqStyleChange = clif_parse_cz_req_style_change;
clif->cz_req_style_change_sub = clif_cz_req_style_change_sub;
clif->style_change_response = clif_style_change_response;
+
+ // -- Pet Evolution
+ clif->pPetEvolution = clif_parse_pet_evolution;
+ clif->petEvolutionResult = clif_pet_evolution_result;
+
}
diff --git a/src/map/clif.h b/src/map/clif.h
index 4b625023f..eb9881533 100644
--- a/src/map/clif.h
+++ b/src/map/clif.h
@@ -592,6 +592,17 @@ enum private_airship {
P_AIRSHIP_ITEM_INVALID
};
+/** Pet Evolution Results */
+enum pet_evolution_result {
+ PET_EVOL_UNKNOWN = 0x0,
+ PET_EVOL_NO_CALLPET = 0x1,
+ PET_EVOL_NO_PETEGG = 0x2,
+ PET_EVOL_NO_RECIPE = 0x3,
+ PET_EVOL_NO_MATERIAL = 0x4,
+ PET_EVOL_RG_FAMILIAR = 0x5,
+ PET_EVOL_SUCCESS = 0x6,
+};
+
/**
* Structures
**/
@@ -834,7 +845,7 @@ struct clif_interface {
void (*mission_info) (struct map_session_data *sd, int mob_id, unsigned char progress);
void (*feel_hate_reset) (struct map_session_data *sd);
void (*partytickack) (struct map_session_data* sd, bool flag);
- void (*zc_config) (struct map_session_data *sd, int type, int flag);
+ void (*zc_config) (struct map_session_data *sd, enum CZ_CONFIG type, int flag);
void (*viewequip_ack) (struct map_session_data* sd, struct map_session_data* tsd);
void (*equpcheckbox) (struct map_session_data* sd);
void (*displayexp) (struct map_session_data *sd, uint64 exp, char type, bool is_quest);
@@ -1484,6 +1495,8 @@ struct clif_interface {
void (*pReqStyleChange) (int fd, struct map_session_data *sd);
void (*cz_req_style_change_sub) (struct map_session_data *sd, int type, int16 idx, bool isitem);
void (*style_change_response) (struct map_session_data *sd, enum stylist_shop flag);
+ void (*pPetEvolution) (int fd, struct map_session_data *sd);
+ void (*petEvolutionResult) (int fd, enum pet_evolution_result result);
};
#ifdef HERCULES_CORE
diff --git a/src/map/packets.h b/src/map/packets.h
index e5fda598d..ebd971005 100644
--- a/src/map/packets.h
+++ b/src/map/packets.h
@@ -3029,7 +3029,7 @@ packet(0x96e,-1,clif->ackmergeitems);
// 2014-01-22aRagexeRE
#if PACKETVER >= 20140122
// new packets
- packet(0x09fb,-1,clif->pDull/*,XXX*/); // CZ_PET_EVOLUTION
+ packet(0x09fb,-1,clif->pPetEvolution); // CZ_PET_EVOLUTION
packet(0x09fc,6); // ZC_PET_EVOLUTION_RESULT
packet(0x09fd,-1); // ZC_NOTIFY_MOVEENTRY11
packet(0x09fe,-1); // ZC_NOTIFY_NEWENTRY11
diff --git a/src/map/packets_struct.h b/src/map/packets_struct.h
index d152ffd2a..bcdf1061a 100644
--- a/src/map/packets_struct.h
+++ b/src/map/packets_struct.h
@@ -1762,6 +1762,18 @@ struct PACKET_ZC_STYLE_CHANGE_RES {
int8 flag;
} __attribute__((packed));
+struct pet_evolution_items {
+ int16 index;
+ int16 amount;
+} __attribute__((packed));
+
+struct PACKET_CZ_PET_EVOLUTION {
+ int16 PacketType;
+ uint16 PacketLength;
+ int16 EvolvedPetEggID;
+ // struct pet_evolution_items items[];
+} __attribute__((packed));
+
#if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute
#pragma pack(pop)
#endif // not NetBSD < 6 / Solaris
diff --git a/src/map/pet.c b/src/map/pet.c
index 4bac79dc8..58c26d1ce 100644
--- a/src/map/pet.c
+++ b/src/map/pet.c
@@ -85,8 +85,21 @@ void pet_set_intimate(struct pet_data *pd, int value)
sd = pd->msd;
pd->pet.intimate = value;
+
if( (intimate >= battle_config.pet_equip_min_friendly && pd->pet.intimate < battle_config.pet_equip_min_friendly) || (intimate < battle_config.pet_equip_min_friendly && pd->pet.intimate >= battle_config.pet_equip_min_friendly) )
status_calc_pc(sd,SCO_NONE);
+
+ /* Pet is lost, delete the egg */
+ if (value <= 0) {
+ int i;
+
+ ARR_FIND(0, MAX_INVENTORY, i, sd->status.inventory[i].card[0] == CARD0_PET &&
+ pd->pet.pet_id == MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2]));
+
+ if (i != MAX_INVENTORY) {
+ pc->delitem(sd, i, 1, 0, DELITEM_NORMAL, LOG_TYPE_EGG);
+ }
+ }
}
int pet_create_egg(struct map_session_data *sd, int item_id)
@@ -233,6 +246,13 @@ int pet_hungry(int tid, int64 tick, int id, intptr_t data) {
return 1; //You lost the pet already, the rest is irrelevant.
pd->pet.hungry--;
+ /* Pet Autofeed */
+ if (battle_config.feature_enable_homun_autofeed != 0) {
+ if (pd->petDB->autofeed == 1 && pd->pet.autofeed == 1 && pd->pet.hungry <= 25) {
+ pet->food(sd, pd);
+ }
+ }
+
if( pd->pet.hungry < 0 )
{
pet_stop_attack(pd);
@@ -311,23 +331,21 @@ int pet_performance(struct map_session_data *sd, struct pet_data *pd)
int pet_return_egg(struct map_session_data *sd, struct pet_data *pd)
{
- struct item tmp_item;
- int flag;
+ int i;
nullpo_retr(1, sd);
nullpo_retr(1, pd);
pet->lootitem_drop(pd,sd);
- memset(&tmp_item,0,sizeof(tmp_item));
- tmp_item.nameid = pd->petDB->EggID;
- tmp_item.identify = 1;
- tmp_item.card[0] = CARD0_PET;
- tmp_item.card[1] = GetWord(pd->pet.pet_id,0);
- tmp_item.card[2] = GetWord(pd->pet.pet_id,1);
- tmp_item.card[3] = pd->pet.rename_flag;
- if((flag = pc->additem(sd,&tmp_item,1,LOG_TYPE_EGG))) {
- clif->additem(sd,0,0,flag);
- map->addflooritem(&sd->bl, &tmp_item, 1, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0, false);
+
+ // Pet Evolution
+ ARR_FIND(0, MAX_INVENTORY, i, sd->status.inventory[i].card[0] == CARD0_PET &&
+ pd->pet.pet_id == MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2]));
+
+ if (i != MAX_INVENTORY) {
+ sd->status.inventory[i].identify = 1;
+ sd->status.inventory[i].bound = IBT_NONE;
}
+
pd->pet.incubate = 1;
unit->free(&pd->bl,CLR_OUTSIGHT);
@@ -462,19 +480,23 @@ int pet_recv_petdata(int account_id,struct s_pet *p,int flag) {
}
if(p->incubate == 1) {
int i;
- //Delete egg from inventory. [Skotlex]
- for (i = 0; i < MAX_INVENTORY; i++) {
- if(sd->status.inventory[i].card[0] == CARD0_PET &&
- p->pet_id == MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2]))
- break;
- }
- if(i >= MAX_INVENTORY) {
+ // Get Egg Index
+ ARR_FIND(0, MAX_INVENTORY, i, sd->status.inventory[i].card[0] == CARD0_PET &&
+ p->pet_id == MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2]));
+
+ if(i == MAX_INVENTORY) {
ShowError("pet_recv_petdata: Hatching pet (%d:%s) aborted, couldn't find egg in inventory for removal!\n",p->pet_id, p->name);
sd->status.pet_id = 0;
return 1;
}
- if (!pet->birth_process(sd,p)) //Pet hatched. Delete egg.
- pc->delitem(sd, i, 1, 0, DELITEM_NORMAL, LOG_TYPE_EGG);
+
+
+ if (!pet->birth_process(sd,p)) {
+ // Pet Evolution, Hide the egg by setting identify to 0 [Dastgir/Hercules]
+ sd->status.inventory[i].identify = 0;
+ // bind the egg to the character to avoid moving it via forged packets [Asheraf]
+ sd->status.inventory[i].bound = IBT_CHARACTER;
+ }
} else {
pet->data_init(sd,p);
if(sd->pd && sd->bl.prev != NULL) {
@@ -1359,6 +1381,14 @@ int pet_read_db_sub(struct config_setting_t *it, int n, const char *source)
if (libconfig->setting_lookup_int(it, "ChangeTargetRate", &i32))
pet->db[n].change_target_rate = i32;
+ // Pet Evolution
+ if ((t = libconfig->setting_get_member(it, "Evolve")) && config_setting_is_group(t)) {
+ pet->read_db_sub_evolution(t, n);
+ }
+
+ if ((t = libconfig->setting_get_member(it, "AutoFeed")) && (i32 = libconfig->setting_get_bool(t)))
+ pet->db[n].autofeed = i32;
+
if (libconfig->setting_lookup_string(it, "PetScript", &str))
pet->db[n].pet_script = *str ? script->parse(str, source, -pet->db[n].class_, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL;
@@ -1368,6 +1398,81 @@ int pet_read_db_sub(struct config_setting_t *it, int n, const char *source)
return pet->db[n].class_;
}
+/**
+ * Read Pet Evolution Database [Dastgir/Hercules]
+ * @param t libconfig setting
+ * @param n Pet DB Index
+ */
+void pet_read_db_sub_evolution(struct config_setting_t *t, int n)
+{
+ struct config_setting_t *pett;
+ int i = 0;
+ const char *str = NULL;
+
+ nullpo_retv(t);
+ Assert_retv(n >= 0 && n < MAX_PET_DB);
+
+ VECTOR_INIT(pet->db[n].evolve_data);
+
+ while ((pett = libconfig->setting_get_elem(t, i))) {
+ if (config_setting_is_group(pett)) {
+ struct pet_evolve_data ped;
+ struct item_data *data;
+ struct config_setting_t *item;
+ int j = 0, i32 = 0;
+
+ str = config_setting_name(pett);
+
+ if (!(data = itemdb->name2id(str))) {
+ ShowWarning("pet_read_evolve_db_sub: Invalid Egg '%s' in Pet #%d, skipping.\n", str, pet->db[n].class_);
+ return;
+ } else {
+ ped.petEggId = data->nameid;
+ }
+
+ VECTOR_INIT(ped.items);
+
+ while ((item = libconfig->setting_get_elem(pett, j))) {
+ struct itemlist_entry list = { 0 };
+ int quantity = 0;
+
+ str = config_setting_name(item);
+ data = itemdb->search_name(str);
+
+ if (!data) {
+ ShowWarning("pet_read_evolve_db_sub: required item %s not found in egg %d\n", str, ped.petEggId);
+ j++;
+ continue;
+ }
+
+ list.id = data->nameid;
+
+ if (mob->get_const(item, &i32) && i32 >= 0) {
+ quantity = i32;
+ }
+
+ if (quantity <= 0) {
+ ShowWarning("pet_read_evolve_db_sub: invalid quantity %d for egg %d\n", quantity, ped.petEggId);
+ j++;
+ continue;
+ }
+
+ list.amount = quantity;
+
+ VECTOR_ENSURE(ped.items, 1, 1);
+ VECTOR_PUSH(ped.items, list);
+
+ j++;
+
+ }
+
+ VECTOR_ENSURE(pet->db[n].evolve_data, 1, 1);
+ VECTOR_PUSH(pet->db[n].evolve_data, ped);
+ }
+ i++;
+ }
+}
+
bool pet_read_db_sub_intimacy(int idx, struct config_setting_t *t)
{
int i32 = 0;
@@ -1396,6 +1501,7 @@ void pet_read_db_clear(void)
// Remove any previous scripts in case reloaddb was invoked.
for (i = 0; i < MAX_PET_DB; i++) {
+ int j;
if (pet->db[i].pet_script) {
script->free_code(pet->db[i].pet_script);
pet->db[i].pet_script = NULL;
@@ -1404,6 +1510,11 @@ void pet_read_db_clear(void)
script->free_code(pet->db[i].equip_script);
pet->db[i].equip_script = NULL;
}
+
+ for (j = 0; j < VECTOR_LENGTH(pet->db[i].evolve_data); j++) {
+ VECTOR_CLEAR(VECTOR_INDEX(pet->db[i].evolve_data, j).items);
+ }
+ VECTOR_CLEAR(pet->db[i].evolve_data);
}
memset(pet->db, 0, sizeof(pet->db));
return;
@@ -1437,6 +1548,7 @@ int do_final_pet(void)
int i;
for( i = 0; i < MAX_PET_DB; i++ )
{
+ int j;
if( pet->db[i].pet_script )
{
script->free_code(pet->db[i].pet_script);
@@ -1447,9 +1559,16 @@ int do_final_pet(void)
script->free_code(pet->db[i].equip_script);
pet->db[i].equip_script = NULL;
}
+
+ /* Pet Evolution [Dastgir/Hercules] */
+ for (j = 0; j < VECTOR_LENGTH(pet->db[i].evolve_data); j++) {
+ VECTOR_CLEAR(VECTOR_INDEX(pet->db[i].evolve_data, j).items);
+ }
+ VECTOR_CLEAR(pet->db[i].evolve_data);
}
ers_destroy(pet->item_drop_ers);
ers_destroy(pet->item_drop_list_ers);
+
return 0;
}
void pet_defaults(void) {
@@ -1503,4 +1622,6 @@ void pet_defaults(void) {
pet->read_db_sub = pet_read_db_sub;
pet->read_db_sub_intimacy = pet_read_db_sub_intimacy;
pet->read_db_clear = pet_read_db_clear;
+
+ pet->read_db_sub_evolution = pet_read_db_sub_evolution;
}
diff --git a/src/map/pet.h b/src/map/pet.h
index d341be97c..b3a16c5d7 100644
--- a/src/map/pet.h
+++ b/src/map/pet.h
@@ -30,6 +30,12 @@
#define MAX_PET_DB 300
#define MAX_PETLOOT_SIZE 30
+/** Pet Evolution [Dastgir/Hercules] */
+struct pet_evolve_data {
+ int petEggId;
+ VECTOR_DECL(struct itemlist_entry) items;
+};
+
struct s_pet_db {
short class_;
char name[NAME_LENGTH],jname[NAME_LENGTH];
@@ -50,8 +56,12 @@ struct s_pet_db {
int attack_rate;
int defence_attack_rate;
int change_target_rate;
+ int autofeed;
struct script_code *equip_script;
struct script_code *pet_script;
+
+ /* Pet Evolution */
+ VECTOR_DECL(struct pet_evolve_data) evolve_data;
};
enum { PET_CLASS,PET_CATCH,PET_EGG,PET_EQUIP,PET_FOOD };
@@ -127,6 +137,7 @@ struct pet_interface {
struct s_pet_db db[MAX_PET_DB];
struct eri *item_drop_ers; //For loot drops delay structures.
struct eri *item_drop_list_ers;
+
/* */
int (*init) (bool minimal);
int (*final) (void);
@@ -172,6 +183,10 @@ struct pet_interface {
int (*read_db_sub) (struct config_setting_t *it, int n, const char *source);
bool (*read_db_sub_intimacy) (int idx, struct config_setting_t *t);
void (*read_db_clear) (void);
+
+ /* Pet Evolution [Dastgir/Hercules] */
+ void (*read_db_sub_evolution) (struct config_setting_t *t, int n);
+
};
#ifdef HERCULES_CORE
diff --git a/tools/petevolutionconverter.py b/tools/petevolutionconverter.py
new file mode 100644
index 000000000..0ccc71314
--- /dev/null
+++ b/tools/petevolutionconverter.py
@@ -0,0 +1,248 @@
+#!/usr/bin/python
+# -*- coding: utf8 -*-
+#
+# This file is part of Hercules.
+# http://herc.ws - http://github.com/HerculesWS/Hercules
+#
+# Copyright (C) 2018 Hercules Dev Team
+# Copyright (C) 2018 Dastgir
+#
+# Hercules is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Usage:
+# python petevolutionconverter.py PetEvolutionCln.lub re ../ > pet_evolve_db.conf
+
+import re
+import sys
+import utils.common as Tools
+
+def printHeader():
+ print('''//================= Hercules Database =====================================
+//= _ _ _
+//= | | | | | |
+//= | |_| | ___ _ __ ___ _ _| | ___ ___
+//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __|
+//= | | | | __/ | | (__| |_| | | __/\__ \
+//= \_| |_/\___|_| \___|\__,_|_|\___||___/
+//================= License ===============================================
+//= This file is part of Hercules.
+//= http://herc.ws - http://github.com/HerculesWS/Hercules
+//=
+//= Copyright (C) 2018 Hercules Dev Team
+//=
+//= Hercules is free software: you can redistribute it and/or modify
+//= it under the terms of the GNU General Public License as published by
+//= the Free Software Foundation, either version 3 of the License, or
+//= (at your option) any later version.
+//=
+//= This program is distributed in the hope that it will be useful,
+//= but WITHOUT ANY WARRANTY; without even the implied warranty of
+//= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//= GNU General Public License for more details.
+//=
+//= You should have received a copy of the GNU General Public License
+//= along with this program. If not, see <http://www.gnu.org/licenses/>.
+//=========================================================================
+//= Pets Database
+//=========================================================================
+
+pet_db:(
+/**************************************************************************
+ ************* Entry structure ********************************************
+ **************************************************************************
+{
+ // ================ Mandatory fields ==============================
+ Id: ID (int)
+ SpriteName: "Sprite_Name" (string)
+ Name: "Pet Name" (string)
+ // ================ Optional fields ===============================
+ TamingItem: Taming Item (string, defaults to 0)
+ EggItem: Egg Id (string, defaults to 0)
+ AccessoryItem: Equipment Id (string, defaults to 0)
+ FoodItem: Food Id (string, defaults to 0)
+ FoodEffectiveness: hunger points (int, defaults to 0)
+ HungerDelay: hunger time (int, defaults to 0)
+ Intimacy: {
+ Initial: start intimacy (int, defaults to 0)
+ FeedIncrement: feeding intimacy (int, defaults to 0)
+ OverFeedDecrement: overfeeding intimacy (int, defaults to 0)
+ OwnerDeathDecrement: owner die intimacy (int, defaults to 0)
+ }
+ CaptureRate: capture rate (int, defaults to 0)
+ Speed: speed (int, defaults to 0)
+ SpecialPerformance: true/false (boolean, defaults to false)
+ TalkWithEmotes: convert talk (boolean, defaults to false)
+ AttackRate: attack rate (int, defaults to 0)
+ DefendRate: Defence attack (int, defaults to 0)
+ ChangeTargetRate: change target (int, defaults to 0)
+ Evolve: {
+ EggID: { (string, Evolved Pet EggID)
+ Name: Amount (items required to perform evolution)
+ ...
+ }
+ }
+ AutoFeed: true/false (boolean, defaults to false)
+ PetScript: <" Pet Script (can also be multi-line) ">
+ EquipScript: <" Equip Script (can also be multi-line) ">
+},
+**************************************************************************/''')
+
+def printID(db, name, tabSize = 1):
+ if (name not in db or int(db[name]) == 0):
+ return
+ print('{}{}: {}'.format('\t'*tabSize, name, db[name]))
+
+def printString(db, name, tabSize = 1):
+ if (name not in db or db[name].strip() == ""):
+ return
+ print('{}{}: "{}"'.format('\t'*tabSize, name, db[name]))
+
+def printBool(db, name):
+ if (name not in db or db[name] == '0'):
+ return
+ print('\t{}: true'.format(name))
+
+def printScript(db, name):
+ if (name not in db or db[name].strip() == ""):
+ return
+ print('\t{}: <{}>'.format(name, db[name]))
+
+def printEntry(ItemDB, EvolveDB, autoFeedDB, entry, mode, serverpath):
+ PetDB = Tools.LoadDB('pet_db', mode, serverpath)
+
+ for i, db in enumerate(PetDB):
+ print('{')
+ printID(db, 'Id')
+ printString(db, 'SpriteName')
+ printString(db, 'Name')
+
+ printString(db, 'TamingItem')
+ printString(db, 'EggItem')
+ printString(db, 'AccessoryItem')
+ printString(db, 'FoodItem')
+ printID(db, 'FoodEffectiveness')
+ printID(db, 'HungerDelay')
+
+ if ('Intimacy' in db and (db['Intimacy']['Initial'] != 0 or db['Intimacy']['FeedIncrement'] != 0 or
+ db['Intimacy']['OverFeedDecrement'] != 0 or db['Intimacy']['OwnerDeathDecrement'] != 0)):
+ print('\tIntimacy: {')
+ printID(db['Intimacy'], 'Initial', 2)
+ printID(db['Intimacy'], 'FeedIncrement', 2)
+ printID(db['Intimacy'], 'OverFeedDecrement', 2)
+ printID(db['Intimacy'], 'OwnerDeathDecrement', 2)
+ print('\t}')
+ #
+ printID(db, 'CaptureRate')
+ printID(db, 'Speed')
+ printBool(db, 'SpecialPerformance')
+ printBool(db, 'TalkWithEmotes')
+ printID(db, 'AttackRate')
+ printID(db, 'DefendRate')
+ printID(db, 'ChangeTargetRate')
+ if (str(db['Id']) in autoFeedDB):
+ print('\tAutoFeed: true')
+ else:
+ print('\tAutoFeed: false')
+ printScript(db, 'PetScript')
+ printScript(db, 'EquipScript')
+
+ if (db['EggItem'] in EvolveDB):
+ entry = EvolveDB[db['EggItem']]
+ print('\tEvolve: {')
+
+ for evolve in entry:
+ if ('comment' in evolve):
+ print('/*')
+ print('\t\t{}: {'.format(evolve['Id']))
+
+ for items in evolve['items']:
+ print('\t\t\t{}: {}'.format(items[0], items[1]))
+
+ print('\t\t}')
+ if ('comment' in evolve):
+ print('*/')
+
+ print('\t}')
+ print('},')
+
+def saveEntry(EvolveDB, entry):
+ if (entry['from'] not in EvolveDB):
+ EvolveDB[entry['from']] = list()
+ EvolveDB[entry['from']].append(entry)
+ return EvolveDB
+
+def getItemConstant(entry, ItemDB, itemID):
+ if (itemID in ItemDB):
+ return ItemDB[itemID]
+ print(itemID, "not found", entry)
+ entry['comment'] = 1
+ return itemID
+
+def ConvertDB(luaName, mode, serverpath):
+ ItemDB = Tools.LoadDBConsts('item_db', mode, serverpath)
+ f = open(luaName)
+ content = f.read()
+ f.close()
+
+ recipeDB = re.findall(r'InsertEvolutionRecipeLGU\((\d+),\s*(\d+),\s*(\d+),\s*(\d+)\)', content)
+ autoFeedDB = re.findall(r'InsertPetAutoFeeding\((\d+)\)', content)
+
+ current = 0
+
+ entry = dict()
+ EvolveDB = dict()
+
+ printHeader()
+ for recipe in recipeDB:
+ fromEgg = getItemConstant(entry, ItemDB, int(recipe[0]))
+ petEgg = getItemConstant(entry, ItemDB, int(recipe[1]))
+
+ if (current == 0):
+ entry = {
+ 'Id': petEgg,
+ 'from': fromEgg,
+ 'items': list()
+ }
+ current = petEgg
+
+ if (current != petEgg):
+ EvolveDB = saveEntry(EvolveDB, entry)
+ entry = {
+ 'Id': petEgg,
+ 'from': fromEgg,
+ 'items': list()
+ }
+ entry['id'] = petEgg
+ entry['items'] = list()
+ current = petEgg
+
+ itemConst = getItemConstant(entry, ItemDB, int(recipe[2]))
+ quantity = int(recipe[3])
+
+ entry['items'].append((itemConst, quantity))
+ saveEntry(EvolveDB, entry)
+
+ printEntry(ItemDB, EvolveDB, autoFeedDB, entry, mode, serverpath)
+ print(')')
+
+
+if len(sys.argv) != 4:
+ print('Pet Evolution Lua to DB')
+ print('Usage:')
+ print(' petevolutionconverter.py lua mode serverpath')
+ print("example:")
+ print(' petevolutionconverter.py PetEvolutionCln.lua pre-re ../')
+ exit(1)
+
+ConvertDB(sys.argv[1], sys.argv[2], sys.argv[3])
diff --git a/tools/utils/common.py b/tools/utils/common.py
index 1a398d544..7b7811654 100644
--- a/tools/utils/common.py
+++ b/tools/utils/common.py
@@ -48,3 +48,18 @@ def LoadDBConsts(DBname, mode, serverpath):
print('LoadDBConsts: invalid database name {}'.format(DBname))
exit(1)
return consts
+
+def LoadDB(DBname, mode, serverpath):
+ filenames = [serverpath + 'db/{}/{}.conf'.format(mode, DBname)]
+
+ if os.path.isfile(serverpath + 'db/{}2.conf'.format(DBname)):
+ filenames.append(serverpath + 'db/{}2.conf'.format(DBname))
+
+ consts = dict()
+ for filename in filenames:
+ with io.open(filename) as f:
+ config = libconf.load(f)
+ db = config[DBname]
+ return db
+ print('LoadDB: invalid database name {}'.format(DBname))
+ exit(1)