diff options
author | Haru <haru@dotalux.com> | 2018-06-29 11:33:11 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-06-29 11:33:11 +0200 |
commit | 9d24045d743a11eda0c825ea3c989fc5e78bc8af (patch) | |
tree | 91d8f55d2aed6fb58661fd7fada8f3f6e53ed814 | |
parent | c419726752ebd1b532f487e5683f63232a6c237b (diff) | |
parent | 9876ae283e9ca68e60de3a0745a765e460d4c434 (diff) | |
download | hercules-9d24045d743a11eda0c825ea3c989fc5e78bc8af.tar.gz hercules-9d24045d743a11eda0c825ea3c989fc5e78bc8af.tar.bz2 hercules-9d24045d743a11eda0c825ea3c989fc5e78bc8af.tar.xz hercules-9d24045d743a11eda0c825ea3c989fc5e78bc8af.zip |
Merge pull request #2063 from dastgirp/2-petevol
Implemented Pet Evolution and Pet Feeding
-rw-r--r-- | conf/map/battle/feature.conf | 5 | ||||
-rw-r--r-- | db/pre-re/pet_db.conf | 7 | ||||
-rw-r--r-- | db/re/item_db.conf | 203 | ||||
-rw-r--r-- | db/re/pet_db.conf | 377 | ||||
-rw-r--r-- | sql-files/main.sql | 2 | ||||
-rw-r--r-- | sql-files/upgrades/2018-06-05--12-02.sql | 24 | ||||
-rw-r--r-- | sql-files/upgrades/index.txt | 1 | ||||
-rw-r--r-- | src/char/int_pet.c | 15 | ||||
-rw-r--r-- | src/common/mmo.h | 1 | ||||
-rw-r--r-- | src/map/battle.c | 1 | ||||
-rw-r--r-- | src/map/battle.h | 1 | ||||
-rw-r--r-- | src/map/clif.c | 144 | ||||
-rw-r--r-- | src/map/clif.h | 15 | ||||
-rw-r--r-- | src/map/packets.h | 2 | ||||
-rw-r--r-- | src/map/packets_struct.h | 12 | ||||
-rw-r--r-- | src/map/pet.c | 163 | ||||
-rw-r--r-- | src/map/pet.h | 15 | ||||
-rw-r--r-- | tools/petevolutionconverter.py | 248 | ||||
-rw-r--r-- | tools/utils/common.py | 15 |
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) |