Merge pull request #73 from cydh/item_group

Item Group
This commit is contained in:
Akkarinage 2014-05-23 10:49:55 +01:00
commit 2d53a782fa
13 changed files with 353 additions and 227 deletions

View File

@ -2598,7 +2598,7 @@
4567,Alphoccio_Card,Alphoccio Card,6,20,,10,,,,,,,,4,,,,,{ bonus bFlee,10; if(BaseJob==Job_Bard) { bonus bMaxHPrate,10; bonus bMaxSPrate,5;} },{},{}
4568,Celia_Card,Celia Card,6,20,,10,,,,,,,,4,,,,,{ bonus bFlee,10; skill "SA_ABRACADABRA",1; },{},{}
4569,Chen_Card,Chen Card,6,20,,10,,,,,,,,4,,,,,{ bonus bFlee,10; skill "MO_CALLSPIRITS",2; },{},{}
4570,Flamel_Card,Flamel Card,6,20,,10,,,,,,,,4,,,,,{ bonus bFlee,10; bonus2 bAddItemHealRate,70,200; },{},{}
4570,Flamel_Card,Flamel Card,6,20,,10,,,,,,,,4,,,,,{ bonus bFlee,10; bonus2 bAddItemHealRate,IG_Flamel_Card,200; },{},{}
4571,Gertie_Card,Gertie Card,6,20,,10,,,,,,,,4,,,,,{ bonus bFlee,10; skill "RG_CLOSECONFINE",1; },{},{}
4572,Randel_Card,Randel Card,6,20,,10,,,,,,,,4,,,,,{ bonus bFlee,10; skill "CR_AUTOGUARD",3; },{},{}
4573,Trentini_Card,Trentini Card,6,20,,10,,,,,,,,4,,,,,{ bonus bFlee,10; if(BaseJob==Job_Dancer) { bonus bMaxHPrate,10; bonus bMaxSPrate,5;} },{},{}

View File

@ -706,7 +706,7 @@
61,1473,1 // Wizardy_Staff
61,1474,1 // Gae_Bolg
61,1477,1 // Spectral_Spear
61,1478,1 // Ahlspiess,
61,1478,1 // Ahlspiess
61,1479,1 // Spectral_Spear_
61,1480,1 // Gae_Bolg_
61,1481,1 // Zephyrus_

View File

@ -1149,24 +1149,24 @@ IG_Ox_Tail_Scroll,Eyes_Of_Ifrit,5,1,1,1,0,1
IG_Ox_Tail_Scroll,Majoruros_Horn,100,1,1,1,0,1
IG_Ox_Tail_Scroll,Sealed_D_Lord_Card,5,1,1,1,0,1
IG_Buddah_Scroll,Mental_Potion,1,3,1,0,0,0
IG_Buddah_Scroll,Mental_Potion,0,3,0,0,0,0
IG_Buddah_Scroll,Bubble_Gum_Box,1400,1,1,0,0,0
IG_Buddah_Scroll,Str_Dish_Box,1400,1,1,0,0,0
IG_Buddah_Scroll,Megaphone_Box,1400,1,1,0,0,0
IG_Buddah_Scroll,Battle_Manual_Box,1352,1,,0,0,0,0
IG_Buddah_Scroll,Battle_Manual_Box,1352,1,1,0,0,0,0
IG_Buddah_Scroll,Token_Of_Siegfried_Box,1250,1,1,0,0,0
IG_Buddah_Scroll,Shadow_Armor_S_Box10,1400,1,1,0,0,0
IG_Buddah_Scroll,Guyak_Pudding,1400,10,1,0,0,0
IG_Buddah_Scroll,Poker_Card_In_Mouth,30,1,0,0,0,0
IG_Buddah_Scroll,Sleeping_Kitty_Cat,100,1,0,0,0,0
IG_Buddah_Scroll,18600,10,1,0,0,0,0
IG_Buddah_Scroll,Guarantee_Weapon_5Up,70,1,0,0,0,0
IG_Buddah_Scroll,Red_Hood,100,0,1,0,0,0
IG_Buddah_Scroll,Spirit_Of_Chung_E,20,1,0,0,0,0
IG_Buddah_Scroll,Guarantee_Weapon_9Up,5,1,0,0,0,0
IG_Buddah_Scroll,Sealed_Samurai_Card,3,1,0,0,0,0
IG_Buddah_Scroll,Kirin_Wing,10,1,0,0,0,0
IG_Buddah_Scroll,Unbreak_Weap_Box,50,1,0,0,0,0
IG_Buddah_Scroll,Poker_Card_In_Mouth,30,1,1,1,0,0
IG_Buddah_Scroll,Sleeping_Kitty_Cat,100,1,1,1,0,0
IG_Buddah_Scroll,18600,10,1,1,1,0,0
IG_Buddah_Scroll,Guarantee_Weapon_5Up,70,1,1,1,0,0
IG_Buddah_Scroll,Red_Hood,100,1,1,1,0,0
IG_Buddah_Scroll,Spirit_Of_Chung_E,20,1,1,1,0,0
IG_Buddah_Scroll,Guarantee_Weapon_9Up,5,1,1,1,0,0
IG_Buddah_Scroll,Sealed_Samurai_Card,3,1,1,1,0,0
IG_Buddah_Scroll,Kirin_Wing,10,1,1,1,0,0
IG_Buddah_Scroll,Unbreak_Weap_Box,50,1,1,1,0,0
IG_Evil_Incarnation,Dead_Tree_Branch_Box2,224,1,1,0,0,1
IG_Evil_Incarnation,Guyak_Pudding,124,10,1,0,0,1

View File

@ -2629,7 +2629,7 @@ REPLACE INTO `item_db_re` VALUES (4566,'Gypsy_Trentini_Card','Gypsy Trentini Car
REPLACE INTO `item_db_re` VALUES (4567,'Alphoccio_Card','Alphoccio Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,4,NULL,NULL,NULL,NULL,'bonus bFlee,10; if(BaseJob==Job_Bard) { bonus bMaxHPrate,10; bonus bMaxSPrate,5;}',NULL,NULL);
REPLACE INTO `item_db_re` VALUES (4568,'Celia_Card','Celia Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,4,NULL,NULL,NULL,NULL,'bonus bFlee,10; skill "SA_ABRACADABRA",1;',NULL,NULL);
REPLACE INTO `item_db_re` VALUES (4569,'Chen_Card','Chen Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,4,NULL,NULL,NULL,NULL,'bonus bFlee,10; skill "MO_CALLSPIRITS",2;',NULL,NULL);
REPLACE INTO `item_db_re` VALUES (4570,'Flamel_Card','Flamel Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,4,NULL,NULL,NULL,NULL,'bonus bFlee,10; bonus2 bAddItemHealRate,70,200;',NULL,NULL);
REPLACE INTO `item_db_re` VALUES (4570,'Flamel_Card','Flamel Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,4,NULL,NULL,NULL,NULL,'bonus bFlee,10; bonus2 bAddItemHealRate,IG_Flamel_Card,200;',NULL,NULL);
REPLACE INTO `item_db_re` VALUES (4571,'Gertie_Card','Gertie Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,4,NULL,NULL,NULL,NULL,'bonus bFlee,10; skill "RG_CLOSECONFINE",1;',NULL,NULL);
REPLACE INTO `item_db_re` VALUES (4572,'Randel_Card','Randel Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,4,NULL,NULL,NULL,NULL,'bonus bFlee,10; skill "CR_AUTOGUARD",3;',NULL,NULL);
REPLACE INTO `item_db_re` VALUES (4573,'Trentini_Card','Trentini Card',6,20,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,4,NULL,NULL,NULL,NULL,'bonus bFlee,10; if(BaseJob==Job_Dancer) { bonus bMaxHPrate,10; bonus bMaxSPrate,5;}',NULL,NULL);

View File

@ -67,6 +67,7 @@ static int cashshop_parse_dbrow( char** str, const char* source, int line ){
/*
* Reads database from TXT format,
* parses lines and sends them to parse_dbrow.
* TODO: Change to sv_readdb
*/
static void cashshop_read_db_txt( void ){
const char* filename[] = { DBPATH"item_cash_db.txt", DBIMPORT"/item_cash_db.txt" };
@ -102,7 +103,7 @@ static void cashshop_read_db_txt( void ){
if( *p == '\0' )
continue;
for( i = 0; i < 2; ++i ){
for( i = 0; i < 3; ++i ){
str[i] = p;
p = strchr( p, ',' );
@ -130,7 +131,7 @@ static void cashshop_read_db_txt( void ){
fclose(fp);
ShowStatus( "Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filename[fi] );
ShowStatus( "Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, path );
}
}

View File

@ -17025,7 +17025,7 @@ void clif_ackworldinfo(struct map_session_data* sd) {
WFIFOHEAD(fd,packet_len(0x979));
WFIFOW(fd,0)=0x979;
//AID -> world name ?
safestrncpy((char*)WFIFOP(fd,2), '\0' /* World name */, 24);
safestrncpy((char*)WFIFOP(fd,2), "" /* World name */, 24);
safestrncpy((char*)WFIFOP(fd,26), sd->status.name, NAME_LENGTH);
WFIFOSET(fd,packet_len(0x979));
}

View File

@ -19,18 +19,20 @@
#include <string.h>
static struct item_data* itemdb_array[MAX_ITEMDB];
static DBMap* itemdb_other;// int nameid -> struct item_data*
static struct s_item_group_db itemgroup_db[MAX_ITEMGROUP];
struct item_data dummy_item; //This is the default dummy item used for non-existant items. [Skotlex]
static DBMap* itemdb_other;// int nameid -> struct item_data*
static DBMap *itemdb_combo;
static DBMap *itemdb_group;
DBMap * itemdb_get_combodb(){
return itemdb_combo;
}
DBMap * itemdb_get_groupdb() {
return itemdb_group;
}
/**
* Search for item name
* name = item alias, so we should find items aliases first. if not found then look for "jname" (full name)
@ -150,9 +152,10 @@ int itemdb_searchname_array(struct item_data** data, int size, const char *str)
*/
unsigned short itemdb_searchrandomid(uint16 group_id, uint8 sub_group)
{
struct s_item_group_db *group = (struct s_item_group_db *) idb_get(itemdb_group, group_id);
if (sub_group)
sub_group -= 1;
if (!group_id || group_id >= MAX_ITEMGROUP || !&itemgroup_db[group_id]) {
if (!group) {
ShowError("itemdb_searchrandomid: Invalid group id %d\n", group_id);
return UNKNOWN_ITEM_ID;
}
@ -160,8 +163,8 @@ unsigned short itemdb_searchrandomid(uint16 group_id, uint8 sub_group)
ShowError("itemdb_searchrandomid: Invalid sub_group %d\n", sub_group+1);
return UNKNOWN_ITEM_ID;
}
if (&itemgroup_db[group_id].random[sub_group] && itemgroup_db[group_id].random[sub_group].data_qty)
return itemgroup_db[group_id].random[sub_group].data[rand()%itemgroup_db[group_id].random[sub_group].data_qty].nameid;
if (&group->random[sub_group] && group->random[sub_group].data_qty)
return group->random[sub_group].data[rand()%group->random[sub_group].data_qty].nameid;
ShowError("itemdb_searchrandomid: No item entries for group id %d and sub group %d\n", group_id, sub_group+1);
return UNKNOWN_ITEM_ID;
@ -177,9 +180,11 @@ unsigned short itemdb_searchrandomid(uint16 group_id, uint8 sub_group)
*/
uint16 itemdb_get_randgroupitem_count(uint16 group_id, uint8 sub_group, uint16 nameid) {
uint16 i, amt = 1;
struct s_item_group_db *group = (struct s_item_group_db *) idb_get(itemdb_group, group_id);
if (sub_group)
sub_group -= 1;
if (!group_id || group_id >= MAX_ITEMGROUP || !&itemgroup_db[group_id]) {
if (!group) {
ShowError("itemdb_get_randgroupitem_count: Invalid group id %d\n", group_id);
return amt;
}
@ -187,11 +192,12 @@ uint16 itemdb_get_randgroupitem_count(uint16 group_id, uint8 sub_group, uint16 n
ShowError("itemdb_get_randgroupitem_count: Invalid sub_group id %d\n", group_id+1);
return amt;
}
if (!(&itemgroup_db[group_id].random[sub_group]) || !itemgroup_db[group_id].random[sub_group].data_qty)
if (!(&group->random[sub_group]) || !group->random[sub_group].data_qty)
return amt;
ARR_FIND(0,itemgroup_db[group_id].random[sub_group].data_qty,i,itemgroup_db[group_id].random[sub_group].data[i].nameid == nameid);
if (i < itemgroup_db[group_id].random[sub_group].data_qty)
amt = itemgroup_db[group_id].random[sub_group].data[i].amount;
for (i = 0; i < group->random[sub_group].data_qty; i++) {
if (group->random[sub_group].data[i].nameid == nameid)
return group->random[sub_group].data[i].amount;
}
return amt;
}
@ -201,37 +207,37 @@ uint16 itemdb_get_randgroupitem_count(uint16 group_id, uint8 sub_group, uint16 n
* @param group_id: The group ID of item that obtained by player
* @param *group: struct s_item_group from itemgroup_db[group_id].random[idx] or itemgroup_db[group_id].must[sub_group][idx]
*/
static void itemdb_pc_get_itemgroup_sub(struct map_session_data *sd, uint16 group_id, struct s_item_group *group) {
static void itemdb_pc_get_itemgroup_sub(struct map_session_data *sd, struct s_item_group_entry *data) {
uint16 i;
struct item tmp;
nullpo_retv(group);
nullpo_retv(data);
memset(&tmp,0,sizeof(tmp));
memset(&tmp, 0, sizeof(tmp));
tmp.nameid = group->nameid;
tmp.amount = (itemdb_isstackable(group->nameid)) ? group->amount : 1;
tmp.bound = group->bound;
tmp.nameid = data->nameid;
tmp.amount = (itemdb_isstackable(data->nameid)) ? data->amount : 1;
tmp.bound = data->bound;
tmp.identify = 1;
tmp.expire_time = (group->duration) ? (unsigned int)(time(NULL) + group->duration*60) : 0;
if (group->isNamed) {
tmp.card[0] = itemdb_isequip(group->nameid) ? CARD0_FORGE : CARD0_CREATE;
tmp.expire_time = (data->duration) ? (unsigned int)(time(NULL) + data->duration*60) : 0;
if (data->isNamed) {
tmp.card[0] = itemdb_isequip(data->nameid) ? CARD0_FORGE : CARD0_CREATE;
tmp.card[1] = 0;
tmp.card[2] = GetWord(sd->status.char_id, 0);
tmp.card[3] = GetWord(sd->status.char_id, 1);
}
//Do loop for non-stackable item
for (i = 0; i < group->amount; i++) {
int flag;
if ((flag = pc_additem(sd,&tmp,tmp.amount,LOG_TYPE_SCRIPT)))
clif_additem(sd,0,0,flag);
else if (!flag && group->isAnnounced) { ///TODO: Move this broadcast to proper behavior (it should on at different packet)
// Do loop for non-stackable item
for (i = 0; i < data->amount; i++) {
char flag;
if ((flag = pc_additem(sd, &tmp, tmp.amount, LOG_TYPE_SCRIPT)))
clif_additem(sd, 0, 0, flag);
else if (!flag && data->isAnnounced) { ///TODO: Move this broadcast to proper behavior (it should on at different packet)
char output[CHAT_SIZE_MAX];
sprintf(output,msg_txt(NULL,717),sd->status.name,itemdb_jname(group->nameid),itemdb_jname(sd->itemid));
clif_broadcast(&sd->bl,output,strlen(output),BC_DEFAULT,ALL_CLIENT);
sprintf(output, msg_txt(NULL, 717), sd->status.name, itemdb_jname(data->nameid), itemdb_jname(sd->itemid));
clif_broadcast(&sd->bl, output, strlen(output), BC_DEFAULT, ALL_CLIENT);
//clif_broadcast_obtain_special_item();
}
if (itemdb_isstackable(group->nameid))
if (itemdb_isstackable(data->nameid))
break;
}
}
@ -244,53 +250,36 @@ static void itemdb_pc_get_itemgroup_sub(struct map_session_data *sd, uint16 grou
*/
char itemdb_pc_get_itemgroup(uint16 group_id, struct map_session_data *sd) {
uint16 i = 0;
struct s_item_group_db *group;
nullpo_retr(1,sd);
if (!group_id || group_id >= MAX_ITEMGROUP) {
if (!(group = (struct s_item_group_db *) idb_get(itemdb_group, group_id))) {
ShowError("itemdb_pc_get_itemgroup: Invalid group id '%d' specified.",group_id);
return 2;
}
//Get the 'must' item(s)
if (itemgroup_db[group_id].must_qty)
for (i = 0; i < itemgroup_db[group_id].must_qty; i++)
if (&itemgroup_db[group_id].must[i] && itemdb_exists(itemgroup_db[group_id].must[i].nameid))
itemdb_pc_get_itemgroup_sub(sd,group_id,&itemgroup_db[group_id].must[i]);
// Get the 'must' item(s)
if (group->must_qty) {
for (i = 0; i < group->must_qty; i++)
if (&group->must[i])
itemdb_pc_get_itemgroup_sub(sd,&group->must[i]);
}
//Get the 'random' item each random group
// Get the 'random' item each random group
for (i = 0; i < MAX_ITEMGROUP_RANDGROUP; i++) {
uint16 rand;
if (!(&itemgroup_db[group_id].random[i]) || !itemgroup_db[group_id].random[i].data_qty) //Skip empty random group
if (!(&group->random[i]) || !group->random[i].data_qty) //Skip empty random group
continue;
rand = rnd()%itemgroup_db[group_id].random[i].data_qty;
//Woops, why is the data empty? Every check should be done when load the item group! So this is bad day for the player :P
if (!&itemgroup_db[group_id].random[i].data[rand] || !itemgroup_db[group_id].random[i].data[rand].nameid) {
rand = rnd()%group->random[i].data_qty;
if (!(&group->random[i].data[rand]) || !group->random[i].data[rand].nameid)
continue;
}
if (itemdb_exists(itemgroup_db[group_id].random[i].data[rand].nameid))
itemdb_pc_get_itemgroup_sub(sd,group_id,&itemgroup_db[group_id].random[i].data[rand]);
itemdb_pc_get_itemgroup_sub(sd,&group->random[i].data[rand]);
}
return 0;
}
/*==========================================
* Calculates total item-group related bonuses for the given item
*------------------------------------------*/
int itemdb_group_bonus(struct map_session_data* sd, int itemid)
{
int bonus = 0, i, j;
for (i=0; i < MAX_ITEMGROUP; i++) {
if (!sd->itemgrouphealrate[i])
continue;
ARR_FIND(0,itemgroup_db[i].random[0].data_qty,j,itemgroup_db[i].random[0].data[j].nameid == itemid );
if( j < itemgroup_db[i].random[0].data_qty )
bonus += sd->itemgrouphealrate[i];
}
return bonus;
}
/// Searches for the item_data.
/// Returns the item_data or NULL if it does not exist.
struct item_data* itemdb_exists(int nameid)
@ -672,11 +661,14 @@ static void itemdb_read_itemgroup_sub(const char* filename, bool silent)
}
while (fgets(line,sizeof(line),fp)) {
uint16 nameid;
int j, group_id, prob = 1, amt = 1, rand_group = 1, announced = 0, dur = 0, named = 0, bound = 0;
char *str[3], *p;
int group_id = -1;
unsigned int j, prob = 1;
uint16 nameid, amt = 1, dur = 0;
uint8 rand_group = 1;
char *str[9], *p, announced = 0, named = 0, bound = 0;
struct s_item_group_random *random = NULL;
struct s_item_group_db *group = NULL;
bool found = false;
struct s_item_group_random *random;
ln++;
if (line[0] == '/' && line[1] == '/')
@ -692,44 +684,45 @@ static void itemdb_read_itemgroup_sub(const char* filename, bool silent)
}
}
memset(str,0,sizeof(str));
for (j = 0, p = line; j < 3 && p;j++) {
for (j = 0, p = line; j < 9 && p;j++) {
str[j] = p;
if (j == 2)
sscanf(str[j],"%d,%d,%d,%d,%d,%d,%d",&prob,&amt,&rand_group,&announced,&dur,&named,&bound);
p = strchr(p,',');
if (p) *p++=0;
}
if (str[0] == NULL)
if (str[0] == NULL) //Empty Group ID
continue;
if (j < 3) {
if (j > 1) //Or else it barks on blank lines...
if (j > 1) // Or else it barks on blank lines...
ShowWarning("itemdb_read_itemgroup: Insufficient fields for entry at %s:%d\n", filename, ln);
continue;
}
//Checking group_id
// Checking group_id
trim(str[0]);
if (ISDIGIT(str[0][0]))
group_id = atoi(str[0]);
else //Try reads group id by const
script_get_constant(trim(str[0]),&group_id);
if (!group_id || group_id >= MAX_ITEMGROUP) {
ShowWarning("itemdb_read_itemgroup: Cannot save '%s' because invalid group id or group db is overflow in %s:%d\n", str[0], filename, ln);
else // Try reads group id by const
script_get_constant(trim(str[0]), &group_id);
if (group_id < 0) {
ShowWarning("itemdb_read_itemgroup: Invlaid Group ID '%s' (%s:%d)\n", str[0], filename, ln);
continue;
}
//Checking sub group
// Checking sub group
prob = atoi(str[2]);
if (str[4] != NULL) rand_group = atoi(str[4]);
if (rand_group < 0 || rand_group > MAX_ITEMGROUP_RANDGROUP) {
ShowWarning("itemdb_read_itemgroup: Invalid sub group %d for group '%s' in %s:%d\n", rand_group, str[0], filename, ln);
ShowWarning("itemdb_read_itemgroup: Invalid sub group '%d' for group '%s' in %s:%d\n", rand_group, str[0], filename, ln);
continue;
}
if (rand_group && prob < 1) {
ShowWarning("itemdb_read_itemgroup: Invalid probaility for group '%s' sub: %d in %s:%d\n", str[0], rand_group, filename, ln);
if (rand_group != 0 && prob < 1) {
ShowWarning("itemdb_read_itemgroup: Random item must has probability. Group '%s' in %s:%d\n", str[0], filename, ln);
continue;
}
//Checking item
// Checking item
trim(str[1]);
if (ISDIGIT(str[1][0]) && itemdb_exists((nameid = atoi(str[1]))))
found = true;
@ -742,27 +735,40 @@ static void itemdb_read_itemgroup_sub(const char* filename, bool silent)
continue;
}
amt = cap_value(amt,1,MAX_AMOUNT);
dur = cap_value(dur,0,UINT16_MAX);
bound = cap_value(bound,0,4);
if (str[3] != NULL) amt = cap_value(atoi(str[3]),1,MAX_AMOUNT);
if (str[5] != NULL) announced = atoi(str[5]);
if (str[6] != NULL) dur = cap_value(atoi(str[6]),0,UINT16_MAX);
if (str[7] != NULL) named = atoi(str[7]);
if (str[8] != NULL) bound = cap_value(atoi(str[8]),0,4);
//Must item (rand_group == 0), place it here
found = true;
if (!(group = (struct s_item_group_db *) idb_get(itemdb_group, group_id))) {
found = false;
CREATE(group, struct s_item_group_db, 1);
group->id = group_id;
}
// Must item (rand_group == 0), place it here
if (!rand_group) {
uint16 idx = itemgroup_db[group_id].must_qty;
uint16 idx = group->must_qty;
if (!idx)
CREATE(itemgroup_db[group_id].must,struct s_item_group,1);
CREATE(group->must, struct s_item_group_entry, 1);
else
RECREATE(itemgroup_db[group_id].must,struct s_item_group,idx+1);
RECREATE(group->must, struct s_item_group_entry, idx+1);
itemgroup_db[group_id].must[idx].nameid = nameid;
itemgroup_db[group_id].must[idx].amount = amt;
itemgroup_db[group_id].must[idx].isAnnounced = announced;
itemgroup_db[group_id].must[idx].duration = dur;
itemgroup_db[group_id].must[idx].isNamed = named;
itemgroup_db[group_id].must[idx].bound = bound;
itemgroup_db[group_id].must_qty++;
//If 'must' item isn't set as random item, skip the next process
group->must[idx].nameid = nameid;
group->must[idx].amount = amt;
group->must[idx].isAnnounced = announced;
group->must[idx].duration = dur;
group->must[idx].isNamed = named;
group->must[idx].bound = bound;
group->must_qty++;
// If 'must' item isn't set as random item, skip the next process
if (!prob) {
if (!found) {
idb_put(itemdb_group, group->id, group);
itemdb_itemgroup_count++;
}
entries++;
continue;
}
@ -771,15 +777,16 @@ static void itemdb_read_itemgroup_sub(const char* filename, bool silent)
else
rand_group -= 1;
random = &itemgroup_db[group_id].random[rand_group];
random = &group->random[rand_group];
//Check, if the entry for this random group already created or not
// Check, if the entry for this random group already created or not
if (!random->data_qty) {
CREATE(random->data,struct s_item_group,prob);
CREATE(random->data, struct s_item_group_entry, prob);
random->data_qty = 0;
}
else
RECREATE(random->data,struct s_item_group,random->data_qty+prob);
RECREATE(random->data, struct s_item_group_entry, random->data_qty+prob);
//Now put the entry to its rand_group
for (j = random->data_qty; j < random->data_qty+prob; j++) {
random->data[j].nameid = nameid;
@ -790,6 +797,11 @@ static void itemdb_read_itemgroup_sub(const char* filename, bool silent)
random->data[j].bound = bound;
}
random->data_qty += prob;
if (!found) {
idb_put(itemdb_group, group->id, group);
itemdb_itemgroup_count++;
}
entries++;
}
fclose(fp);
@ -1590,7 +1602,7 @@ static void itemdb_read(void) {
else
itemdb_readdb();
memset(&itemgroup_db, 0, sizeof(itemgroup_db));
itemdb_itemgroup_count = 0;
for(i=0; i<ARRAYLENGTH(dbsubpath); i++){
uint8 n1 = strlen(db_path)+strlen(dbsubpath[i])+1;
@ -1675,6 +1687,24 @@ static int itemdb_final_sub(DBKey key, DBData *data, va_list ap)
return 0;
}
static int itemdb_group_free(DBKey key, DBData *data, va_list ap) {
struct s_item_group_db *group = db_data2ptr(data);
uint8 j;
if (!group)
return 0;
if (group->must_qty)
aFree(group->must);
group->must_qty = 0;
for (j = 0; j < MAX_ITEMGROUP_RANDGROUP; j++) {
if (!group->random[j].data_qty || !(&group->random[j]))
continue;
aFree(group->random[j].data);
group->random[j].data_qty = 0;
}
aFree(group);
return 0;
}
/**
* Reload Item DB
*/
@ -1689,19 +1719,7 @@ void itemdb_reload(void) {
if( itemdb_array[i] )
destroy_item_data(itemdb_array[i], true);
for (i = 0; i < MAX_ITEMGROUP; i++) {
uint8 j;
if (!(&itemgroup_db[i]))
continue;
if (itemgroup_db[i].must_qty)
aFree(itemgroup_db[i].must);
for (j = 0; j < MAX_ITEMGROUP_RANDGROUP; j++) {
if (!(&itemgroup_db[i].random[j]) || !itemgroup_db[i].random[j].data_qty)
continue;
aFree(itemgroup_db[i].random[j].data);
}
}
itemdb_group->clear(itemdb_group, itemdb_group_free);
itemdb_other->clear(itemdb_other, itemdb_final_sub);
db_clear(itemdb_combo);
@ -1769,20 +1787,8 @@ void do_final_itemdb(void) {
for( i = 0; i < ARRAYLENGTH(itemdb_array); ++i )
if( itemdb_array[i] )
destroy_item_data(itemdb_array[i], true);
for (i = 0; i < MAX_ITEMGROUP; i++) {
uint8 j;
if (!(&itemgroup_db[i]))
continue;
if (itemgroup_db[i].must_qty)
aFree(itemgroup_db[i].must);
for (j = 0; j < MAX_ITEMGROUP_RANDGROUP; j++) {
if (!(&itemgroup_db[i].random[j]) || !itemgroup_db[i].random[j].data_qty)
continue;
aFree(itemgroup_db[i].random[j].data);
}
}
itemdb_group->destroy(itemdb_group, itemdb_group_free);
itemdb_other->destroy(itemdb_other, itemdb_final_sub);
destroy_item_data(&dummy_item, false);
db_destroy(itemdb_combo);
@ -1795,6 +1801,7 @@ void do_init_itemdb(void) {
memset(itemdb_array, 0, sizeof(itemdb_array));
itemdb_other = idb_alloc(DB_OPT_BASE);
itemdb_combo = idb_alloc(DB_OPT_BASE);
itemdb_group = idb_alloc(DB_OPT_BASE);
create_dummy_data(); //Dummy data item.
itemdb_read();

View File

@ -23,8 +23,6 @@
#define IG_FINDINGORE 6
#define IG_POTION 37
#define MAX_ITEMGROUP 420 ///The max. item group count (increase this when needed). TODO: Remove this limit and use dynamic size or DBMap if needed
#define MAX_ITEMGROUP_RANDGROUP 4 ///Max group for random item (increase this when needed). TODO: Remove this limit and use dynamic size if needed
#define CARD0_FORGE 0x00FF
@ -318,6 +316,33 @@ struct item_combo {
bool isRef;/* whether this struct is a reference or the master */
};
/// Struct of item group entry
struct s_item_group_entry {
uint16 nameid, /// Item ID
duration; /// Duration if item as rental item (in minutes)
uint16 amount; /// Amount of item will be obtained
bool isAnnounced, /// Broadcast if player get this item
isNamed; /// Named the item (if possible)
char bound; /// Makes the item as bound item (according to bound type)
};
/// Struct of random group
struct s_item_group_random {
struct s_item_group_entry *data;
uint16 data_qty;
};
/// Struct of item group that will be used for db
struct s_item_group_db {
uint16 id;
struct s_item_group_entry *must;
uint16 must_qty;
struct s_item_group_random random[MAX_ITEMGROUP_RANDGROUP]; //! TODO: Move this fixed array to dynamic size if needed.
};
static uint16 itemdb_itemgroup_count; /// Number of Item Groups
///Main item data struct
struct item_data {
uint16 nameid;
@ -384,29 +409,6 @@ struct item_data {
unsigned char combos_count;
};
/* Struct of item group entry */
struct s_item_group {
uint16 nameid, ///item id
duration; ///duration if item as rental item (in minute)
uint16 amount; ///amount of item will be obtained
bool isAnnounced, ///broadcast if player get this item
isNamed; ///named the item (if possible)
char bound; ///makes the item as bound item (according to bound type)
};
/* Struct of random group */
struct s_item_group_random {
struct s_item_group *data;
uint16 data_qty;
};
/* Struct of item group that will be used for db */
struct s_item_group_db {
struct s_item_group *must;
uint16 must_qty;
struct s_item_group_random random[MAX_ITEMGROUP_RANDGROUP]; //! TODO: Move this fixed array to dynamic size if needed.
};
struct item_data* itemdb_searchname(const char *name);
int itemdb_searchname_array(struct item_data** data, int size, const char *str);
struct item_data* itemdb_load(int nameid);
@ -440,7 +442,6 @@ struct item_data* itemdb_exists(int nameid);
const char* itemdb_typename(enum item_types type);
const char *itemdb_typename_ammo (enum e_item_ammo ammo);
int itemdb_group_bonus(struct map_session_data* sd, int itemid);
unsigned short itemdb_searchrandomid(uint16 group_id, uint8 sub_group);
#define itemdb_value_buy(n) itemdb_search(n)->value_buy
@ -479,6 +480,7 @@ char itemdb_pc_get_itemgroup(uint16 group_id, struct map_session_data *sd);
uint16 itemdb_get_randgroupitem_count(uint16 group_id, uint8 sub_group, uint16 nameid);
DBMap * itemdb_get_combodb();
DBMap * itemdb_get_groupdb();
void itemdb_reload(void);

View File

@ -66,6 +66,7 @@ int day_timer_tid = INVALID_TIMER;
int night_timer_tid = INVALID_TIMER;
struct eri *pc_sc_display_ers = NULL;
struct eri *pc_itemgrouphealrate_ers = NULL;
int pc_expiration_tid = INVALID_TIMER;
struct fame_list smith_fame_list[MAX_FAME_LIST];
@ -2154,6 +2155,56 @@ int pc_bonus_subele(struct map_session_data* sd, unsigned char ele, short rate,
return 0;
}
/** Add item group heal rate bonus to player
* @param sd Player
* @param group_id Item Group ID
* @param rate
* @author Cydh
*/
void pc_itemgrouphealrate(struct map_session_data *sd, uint16 group_id, short rate) {
struct s_pc_itemgrouphealrate *entry;
uint8 i;
for (i = 0; i < sd->itemgrouphealrate_count; i++) {
if (sd->itemgrouphealrate[i]->group_id == group_id)
break;
}
if (i != sd->itemgrouphealrate_count) {
sd->itemgrouphealrate[i]->rate += rate;
return;
}
if (i >= UINT8_MAX) {
ShowError("pc_itemgrouphealrate_add: Reached max (%d) possible bonuses for this player %d\n", UINT8_MAX);
return;
}
entry = ers_alloc(pc_itemgrouphealrate_ers, struct s_pc_itemgrouphealrate);
entry->group_id = group_id;
entry->rate = rate;
RECREATE(sd->itemgrouphealrate, struct s_pc_itemgrouphealrate *, sd->itemgrouphealrate_count+1);
sd->itemgrouphealrate[sd->itemgrouphealrate_count++] = entry;
}
/** Clear item group heal rate from player
* @param sd Player
* @author Cydh
*/
void pc_itemgrouphealrate_clear(struct map_session_data *sd) {
if (!sd || !sd->itemgrouphealrate_count)
return;
else {
uint8 i;
for( i = 0; i < sd->itemgrouphealrate_count; i++ )
ers_free(pc_itemgrouphealrate_ers, sd->itemgrouphealrate[i]);
sd->itemgrouphealrate_count = 0;
aFree(sd->itemgrouphealrate);
sd->itemgrouphealrate = NULL;
}
}
/*==========================================
* Add a bonus(type) to player sd
*------------------------------------------*/
@ -3217,8 +3268,8 @@ int pc_bonus2(struct map_session_data *sd,int type,int type2,int val)
case SP_ADD_ITEM_HEAL_RATE:
if(sd->state.lr_flag == 2)
break;
if (type2 < MAX_ITEMGROUP) { //Group bonus
sd->itemgrouphealrate[type2] += val;
if (type2 < itemdb_itemgroup_count) { //Group bonus
pc_itemgrouphealrate(sd, type2, val);
break;
}
//Standard item bonus.
@ -7616,7 +7667,7 @@ int pc_itemheal(struct map_session_data *sd,int itemid, int hp,int sp)
//All item bonuses.
bonus += sd->bonus.itemhealrate2;
//Item Group bonuses
bonus += bonus*itemdb_group_bonus(sd, itemid)/100;
bonus += bonus*pc_get_itemgroup_bonus(sd, itemid)/100;
//Individual item bonuses.
for(i = 0; i < ARRAYLENGTH(sd->itemhealrate) && sd->itemhealrate[i].nameid; i++)
{
@ -10758,16 +10809,63 @@ short pc_maxparameter(struct map_session_data *sd, enum e_params param) {
((class_&JOBL_UPPER) ? battle_config.max_trans_parameter : battle_config.max_parameter)));
}
/**
* Calculates total item-group related bonuses for the given item
* @param sd Player
* @param nameid Item ID
* @return Heal rate
**/
short pc_get_itemgroup_bonus(struct map_session_data* sd, uint16 nameid) {
short bonus = 0;
uint8 i;
if (!sd->itemgrouphealrate_count)
return bonus;
for (i = 0; i < sd->itemgrouphealrate_count; i++) {
uint16 group_id = sd->itemgrouphealrate[i]->group_id, j;
struct s_item_group_db *group = (struct s_item_group_db *) idb_get(itemdb_get_groupdb(), group_id);
if (!group)
continue;
for (j = 0; j < group->random[0].data_qty; j++) {
if (group->random[0].data[j].nameid == nameid) {
bonus += sd->itemgrouphealrate[i]->rate;
break;
}
}
}
return bonus;
}
/**
* Calculates total item-group related bonuses for the given item group
* @param sd Player
* @param group_id Item Group ID
* @return Heal rate
**/
short pc_get_itemgroup_bonus_group(struct map_session_data* sd, uint16 group_id) {
short bonus = 0;
uint8 i;
if (!sd->itemgrouphealrate_count)
return bonus;
for (i = 0; i < sd->itemgrouphealrate_count; i++) {
if (sd->itemgrouphealrate[i]->group_id == group_id)
return sd->itemgrouphealrate[i]->rate;
}
return bonus;
}
/*==========================================
* pc Init/Terminate
*------------------------------------------*/
void do_final_pc(void) {
db_destroy(itemcd_db);
do_final_pc_groups();
ers_destroy(pc_sc_display_ers);
ers_destroy(pc_itemgrouphealrate_ers);
}
void do_init_pc(void) {
@ -10808,4 +10906,5 @@ void do_init_pc(void) {
do_init_pc_groups();
pc_sc_display_ers = ers_new(sizeof(struct sc_display_entry), "pc.c:pc_sc_display_ers", ERS_OPT_NONE);
pc_itemgrouphealrate_ers = ers_new(sizeof(struct s_pc_itemgrouphealrate), "pc.c:pc_itemgrouphealrate_ers", ERS_OPT_NONE);
}

View File

@ -149,6 +149,12 @@ enum npc_timeout_type {
NPCT_WAIT = 2,
};
/// Item Group heal rate struct
struct s_pc_itemgrouphealrate {
uint16 group_id; /// Item Group ID
short rate; /// Rate
};
struct map_session_data {
struct block_list bl;
struct unit_data ud;
@ -323,7 +329,6 @@ struct map_session_data {
int ignore_mdef_by_race[RC_MAX];
int ignore_mdef_by_class[CLASS_MAX];
int ignore_def_by_race[RC_MAX];
int itemgrouphealrate[MAX_ITEMGROUP];
short sp_gain_race[RC_MAX];
short sp_gain_race_attack[RC_MAX];
short hp_gain_race_attack[RC_MAX];
@ -595,13 +600,18 @@ struct map_session_data {
int16 icon;
int tid;
} bonus_script[MAX_PC_BONUS_SCRIPT];
struct s_pc_itemgrouphealrate **itemgrouphealrate; /// List of Item Group Heal rate bonus
uint8 itemgrouphealrate_count; /// Number of rate bonuses
/* Expiration Timer ID */
int expiration_tid;
time_t expiration_time;
};
struct eri *pc_sc_display_ers;
struct eri *pc_sc_display_ers; /// Player's SC display table
struct eri *pc_itemgrouphealrate_ers; /// Player's Item Group Heal Rate table
/* Global Expiration Timer ID */
extern int pc_expiration_tid;
@ -1105,6 +1115,10 @@ void pc_bonus_script_clear(struct map_session_data *sd, uint16 flag);
void pc_cell_basilica(struct map_session_data *sd);
void pc_itemgrouphealrate_clear(struct map_session_data *sd);
short pc_get_itemgroup_bonus(struct map_session_data* sd, uint16 nameid);
short pc_get_itemgroup_bonus_group(struct map_session_data* sd, uint16 group_id);
#if defined(RENEWAL_DROP) || defined(RENEWAL_EXP)
int pc_level_penalty_mod(struct map_session_data *sd, int mob_level, uint32 mob_class, int type);
#endif

View File

@ -6965,66 +6965,67 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
}
case AM_BERSERKPITCHER:
case AM_POTIONPITCHER: {
int i,hp = 0,sp = 0;
if( dstmd && dstmd->mob_id == MOBID_EMPERIUM ) {
map_freeblock_unlock();
return 1;
}
if( sd ) {
int x,bonus=100;
struct skill_condition require = skill_get_requirement(sd, skill_id, skill_lv);
x = skill_lv%11 - 1;
i = pc_search_inventory(sd, require.itemid[x]);
if (i < 0 || require.itemid[x] <= 0) {
clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
case AM_POTIONPITCHER:
{
int i,hp = 0,sp = 0;
if( dstmd && dstmd->mob_id == MOBID_EMPERIUM ) {
map_freeblock_unlock();
return 1;
}
if (sd->inventory_data[i] == NULL || sd->status.inventory[i].amount < require.amount[x]) {
clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
map_freeblock_unlock();
return 1;
}
if( skill_id == AM_BERSERKPITCHER ) {
if( dstsd && dstsd->status.base_level < (unsigned int)sd->inventory_data[i]->elv ) {
if( sd ) {
int x,bonus=100;
struct skill_condition require = skill_get_requirement(sd, skill_id, skill_lv);
x = skill_lv%11 - 1;
i = pc_search_inventory(sd, require.itemid[x]);
if (i < 0 || require.itemid[x] <= 0) {
clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
map_freeblock_unlock();
return 1;
}
}
potion_flag = 1;
potion_hp = potion_sp = potion_per_hp = potion_per_sp = 0;
potion_target = bl->id;
run_script(sd->inventory_data[i]->script,0,sd->bl.id,0);
potion_flag = potion_target = 0;
if( sd->sc.data[SC_SPIRIT] && sd->sc.data[SC_SPIRIT]->val2 == SL_ALCHEMIST )
bonus += sd->status.base_level;
if( potion_per_hp > 0 || potion_per_sp > 0 ) {
hp = tstatus->max_hp * potion_per_hp / 100;
hp = hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
if( dstsd ) {
sp = dstsd->status.max_sp * potion_per_sp / 100;
sp = sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
if (sd->inventory_data[i] == NULL || sd->status.inventory[i].amount < require.amount[x]) {
clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
map_freeblock_unlock();
return 1;
}
} else {
if( potion_hp > 0 ) {
hp = potion_hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
hp = hp * (100 + (tstatus->vit<<1)) / 100;
if( dstsd )
hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10) / 100;
if( skill_id == AM_BERSERKPITCHER ) {
if( dstsd && dstsd->status.base_level < (unsigned int)sd->inventory_data[i]->elv ) {
clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
map_freeblock_unlock();
return 1;
}
}
if( potion_sp > 0 ) {
sp = potion_sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
sp = sp * (100 + (tstatus->int_<<1)) / 100;
if( dstsd )
sp = sp * (100 + pc_checkskill(dstsd,MG_SRECOVERY)*10) / 100;
potion_flag = 1;
potion_hp = potion_sp = potion_per_hp = potion_per_sp = 0;
potion_target = bl->id;
run_script(sd->inventory_data[i]->script,0,sd->bl.id,0);
potion_flag = potion_target = 0;
if( sd->sc.data[SC_SPIRIT] && sd->sc.data[SC_SPIRIT]->val2 == SL_ALCHEMIST )
bonus += sd->status.base_level;
if( potion_per_hp > 0 || potion_per_sp > 0 ) {
hp = tstatus->max_hp * potion_per_hp / 100;
hp = hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
if( dstsd ) {
sp = dstsd->status.max_sp * potion_per_sp / 100;
sp = sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
}
} else {
if( potion_hp > 0 ) {
hp = potion_hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
hp = hp * (100 + (tstatus->vit<<1)) / 100;
if( dstsd )
hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10) / 100;
}
if( potion_sp > 0 ) {
sp = potion_sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
sp = sp * (100 + (tstatus->int_<<1)) / 100;
if( dstsd )
sp = sp * (100 + pc_checkskill(dstsd,MG_SRECOVERY)*10) / 100;
}
}
}
if (sd->itemgrouphealrate[IG_POTION]>0) {
hp += hp * sd->itemgrouphealrate[IG_POTION] / 100;
sp += sp * sd->itemgrouphealrate[IG_POTION] / 100;
if ((bonus = pc_get_itemgroup_bonus_group(sd, IG_POTION))) {
hp += hp * bonus / 100;
sp += sp * bonus / 100;
}
if( (i = pc_skillheal_bonus(sd, skill_id)) ) {

View File

@ -2866,7 +2866,6 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt)
+ sizeof(sd->ignore_def_by_race)
+ sizeof(sd->ignore_mdef_by_race)
+ sizeof(sd->ignore_mdef_by_class)
+ sizeof(sd->itemgrouphealrate)
+ sizeof(sd->sp_gain_race)
+ sizeof(sd->sp_gain_race_attack)
+ sizeof(sd->hp_gain_race_attack)
@ -2938,6 +2937,8 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt)
pc_delautobonus(sd,sd->autobonus2,ARRAYLENGTH(sd->autobonus2),true);
pc_delautobonus(sd,sd->autobonus3,ARRAYLENGTH(sd->autobonus3),true);
pc_itemgrouphealrate_clear(sd);
npc_script_event(sd, NPCE_STATCALC);
// Parse equipment

View File

@ -2664,6 +2664,7 @@ int unit_free(struct block_list *bl, clr_type clrtype)
sd->quest_log = NULL;
sd->num_quests = sd->avail_quests = 0;
}
pc_itemgrouphealrate_clear(sd);
break;
}
case BL_PET: