From c159a3c4ffd414d7423b3504a282cce1111b7874 Mon Sep 17 00:00:00 2001 From: shennetsind Date: Wed, 30 Oct 2013 18:42:58 -0200 Subject: Account-wide Exp/Drop/Death Modifiers Attempting to mimic the official as suggested in http://hercules.ws/board/topic/250-official-vip-system/ The variables can be read and modified thru scripting as well as sql, the patch introduces 3 new pc-arams 'ModExp','ModDrop' and 'ModDeath' for that purpose. The OnLogin modifier display is not the real deal though -- wasn't able to get it to output properly (though that might have been my client files failt) Up for review. Signed-off-by: shennetsind --- db/const.txt | 3 + sql-files/main.sql | 4 + sql-files/upgrades/2013-10-30--19-53.sql | 5 + sql-files/upgrades/index.txt | 3 +- src/char/char.c | 20 ++- src/common/mmo.h | 3 + src/map/clif.c | 15 ++ src/map/clif.h | 2 + src/map/map.h | 3 + src/map/mob.c | 6 + src/map/pc.c | 296 +++++++++++++++++-------------- 11 files changed, 216 insertions(+), 144 deletions(-) create mode 100644 sql-files/upgrades/2013-10-30--19-53.sql diff --git a/db/const.txt b/db/const.txt index e5874f52f..a534d683c 100644 --- a/db/const.txt +++ b/db/const.txt @@ -418,6 +418,9 @@ killerrid 121 1 killedrid 122 1 SlotChange 123 1 CharRename 124 1 +ModExp 124 1 +ModDrop 124 1 +ModDeath 124 1 bMaxHP 6 bMaxSP 8 diff --git a/sql-files/main.sql b/sql-files/main.sql index 8bd28c414..29717155c 100644 --- a/sql-files/main.sql +++ b/sql-files/main.sql @@ -660,6 +660,7 @@ INSERT INTO `sql_updates` (`timestamp`) VALUES (1366078541); INSERT INTO `sql_updates` (`timestamp`) VALUES (1381354728); INSERT INTO `sql_updates` (`timestamp`) VALUES (1381423003); INSERT INTO `sql_updates` (`timestamp`) VALUES (1382892428); +INSERT INTO `sql_updates` (`timestamp`) VALUES (1383162785); -- -- Table structure for table `sstatus` @@ -713,6 +714,9 @@ INSERT INTO `interreg` (`varname`, `value`) VALUES CREATE TABLE IF NOT EXISTS `account_data` ( `account_id` int(11) unsigned NOT NULL default '0', `bank_vault` int(11) unsigned NOT NULL default '0', + `base_exp` TINYINT( 4 ) UNSIGNED NOT NULL default '0', + `base_drop` TINYINT( 4 ) UNSIGNED NOT NULL default '0', + `base_death` TINYINT( 4 ) UNSIGNED NOT NULL default '0', PRIMARY KEY (`account_id`) ) ENGINE=MyISAM; diff --git a/sql-files/upgrades/2013-10-30--19-53.sql b/sql-files/upgrades/2013-10-30--19-53.sql new file mode 100644 index 000000000..05481cff4 --- /dev/null +++ b/sql-files/upgrades/2013-10-30--19-53.sql @@ -0,0 +1,5 @@ +#1383162785 +ALTER TABLE `account_data` ADD `base_exp` TINYINT( 4 ) UNSIGNED NOT NULL default '0'; +ALTER TABLE `account_data` ADD `base_drop` TINYINT( 4 ) UNSIGNED NOT NULL default '0'; +ALTER TABLE `account_data` ADD `base_death` TINYINT( 4 ) UNSIGNED NOT NULL default '0'; +INSERT INTO `sql_updates` (`timestamp`) VALUES (1383162785); \ No newline at end of file diff --git a/sql-files/upgrades/index.txt b/sql-files/upgrades/index.txt index 4afcc5a0d..2cce6f493 100644 --- a/sql-files/upgrades/index.txt +++ b/sql-files/upgrades/index.txt @@ -6,4 +6,5 @@ 2013-04-16--01-24.sql 2013-10-09--21-38.sql 2013-10-10--16-36.sql -2013-10-27--16-47.sql \ No newline at end of file +2013-10-27--16-47.sql +2013-10-30--19-53.sql \ No newline at end of file diff --git a/src/char/char.c b/src/char/char.c index 310163e3a..240acab53 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -499,12 +499,12 @@ int mmo_char_tosql(int char_id, struct mmo_charstatus* p) strcat(save_status, " status"); } - if( p->bank_vault != cp->bank_vault ) { - if( SQL_ERROR == SQL->Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`bank_vault`) VALUES ('%d','%d')",account_data_db,p->account_id,p->bank_vault) ) { + if( p->bank_vault != cp->bank_vault || p->mod_exp != cp->mod_exp || p->mod_drop != cp->mod_drop || p->mod_death != p->mod_death ) { + if( SQL_ERROR == SQL->Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`bank_vault`,`base_exp`,`base_drop`,`base_death`) VALUES ('%d','%d','%d','%d','%d')",account_data_db,p->account_id,p->bank_vault,p->mod_exp,p->mod_drop,p->mod_death) ) { Sql_ShowDebug(sql_handle); errors++; } else - strcat(save_status, " bank"); + strcat(save_status, " accdata"); } //Values that will seldom change (to speed up saving) @@ -1352,15 +1352,21 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything mercenary_owner_fromsql(char_id, p); strcat(t_msg, " mercenary"); - //`account_data` (`account_id`,`bank_vault`) - if( SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT `bank_vault` FROM `%s` WHERE `account_id`=? LIMIT 1", account_data_db) + /* default */ + p->mod_exp = p->mod_drop = p->mod_death = 100; + + //`account_data` (`account_id`,`bank_vault`,`base_exp`,`base_drop`,`base_death`) + if( SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT `bank_vault`,`base_exp`,`base_drop`,`base_death` FROM `%s` WHERE `account_id`=? LIMIT 1", account_data_db) || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &account_id, 0) || SQL_ERROR == SQL->StmtExecute(stmt) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &p->bank_vault, 0, NULL, NULL) ) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &p->bank_vault, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_USHORT, &p->mod_exp, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_USHORT, &p->mod_drop, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_USHORT, &p->mod_death, 0, NULL, NULL) ) SqlStmt_ShowDebug(stmt); if( SQL_SUCCESS == SQL->StmtNextRow(stmt) ) - strcat(t_msg, " bank"); + strcat(t_msg, " accdata"); if (save_log) ShowInfo("Loaded char (%d - %s): %s\n", char_id, p->name, t_msg); //ok. all data load successfuly! SQL->StmtFree(stmt); diff --git a/src/common/mmo.h b/src/common/mmo.h index 9281314dc..13748a787 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -414,6 +414,9 @@ struct mmo_charstatus { unsigned short slotchange; time_t delete_date; + + /* `account_data` modifiers */ + unsigned short mod_exp,mod_drop,mod_death; }; typedef enum mail_status { diff --git a/src/map/clif.c b/src/map/clif.c index 957f75d99..a5e92b998 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -17798,6 +17798,19 @@ void clif_bank_withdraw(struct map_session_data *sd,enum e_BANKING_WITHDRAW_ACK clif->send(&p,sizeof(p), &sd->bl, SELF); } +/* TODO: official response packet (tried 0x8cb/0x97b but the display was quite screwed up.) */ +/* currently mimicing */ +void clif_show_modifiers (struct map_session_data *sd) { + + if( sd->status.mod_exp != 100 || sd->status.mod_drop != 100 || sd->status.mod_death != 100 ) { + char output[128]; + + snprintf(output,128,"Base EXP : %d%% | Base Drop: %d%% | Base Death Penalty: %d%%", + sd->status.mod_exp,sd->status.mod_drop,sd->status.mod_death); + clif->broadcast2(&sd->bl,output, strlen(output) + 1, 0xffbc90, 0x190, 12, 0, 0, SELF); + } + +} /* */ unsigned short clif_decrypt_cmd( int cmd, struct map_session_data *sd ) { @@ -18597,6 +18610,8 @@ void clif_defaults(void) { /* Bank System [Yommy/Hercules] */ clif->bank_deposit = clif_bank_deposit; clif->bank_withdraw = clif_bank_withdraw; + /* */ + clif->show_modifiers = clif_show_modifiers; /*------------------------ *- Parse Incoming Packet *------------------------*/ diff --git a/src/map/clif.h b/src/map/clif.h index cbe3fa857..d2ac9213a 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -1006,6 +1006,8 @@ struct clif_interface { /* Bank System [Yommy/Hercules] */ void (*bank_deposit) (struct map_session_data *sd, enum e_BANKING_DEPOSIT_ACK reason); void (*bank_withdraw) (struct map_session_data *sd,enum e_BANKING_WITHDRAW_ACK reason); + /* */ + void (*show_modifiers) (struct map_session_data *sd); /*------------------------ *- Parse Incoming Packet *------------------------*/ diff --git a/src/map/map.h b/src/map/map.h index 6b7d2a630..06c3829b1 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -360,6 +360,9 @@ enum _sp { SP_KILLEDRID=122, SP_SLOTCHANGE=123, SP_CHARRENAME=124, + SP_MOD_EXP=125, + SP_MOD_DROP=126, + SP_MOD_DEATH=127, // Mercenaries SP_MERCFLEE=165, SP_MERCKILLS=189, SP_MERCFAITH=190, diff --git a/src/map/mob.c b/src/map/mob.c index 97f8ea6c1..778fc1dfa 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -2332,6 +2332,12 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) { drop_rate = 1; } #endif + if( sd && sd->status.mod_drop != 100 ) { + drop_rate = drop_rate * sd->status.mod_drop / 100; + if( drop_rate < 1 ) + drop_rate = 1; + } + // attempt to drop the item if (rnd() % 10000 >= drop_rate) continue; diff --git a/src/map/pc.c b/src/map/pc.c index 0244c6c84..564bc42d7 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -5827,6 +5827,9 @@ void pc_calcexp(struct map_session_data *sd, unsigned int *base_exp, unsigned in if (sd->sc.data[SC_CASH_PLUSEXP]) bonus += sd->sc.data[SC_CASH_PLUSEXP]->val1; + + if( sd->status.mod_exp != 100 ) + bonus += sd->status.mod_exp - 100; *base_exp = (unsigned int) cap_value(*base_exp + (double)*base_exp * bonus/100., 1, UINT_MAX); @@ -6861,8 +6864,9 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) { && !map->list[sd->bl.m].flag.noexppenalty && !map_flag_gvg2(sd->bl.m) && !sd->sc.data[SC_BABY] && !sd->sc.data[SC_CASH_DEATHPENALTY] ) { - unsigned int base_penalty =0; + unsigned int base_penalty = 0; if (battle_config.death_penalty_base > 0) { + switch (battle_config.death_penalty_type) { case 1: base_penalty = (unsigned int) ((double)pc->nextbaseexp(sd) * (double)battle_config.death_penalty_base/10000); @@ -6871,16 +6875,20 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) { base_penalty = (unsigned int) ((double)sd->status.base_exp * (double)battle_config.death_penalty_base/10000); break; } + if(base_penalty) { if (battle_config.pk_mode && src && src->type==BL_PC) base_penalty*=2; + if( sd->status.mod_death != 100 ) + base_penalty = base_penalty * sd->status.mod_death / 100; sd->status.base_exp -= min(sd->status.base_exp, base_penalty); clif->updatestatus(sd,SP_BASEEXP); } } - if(battle_config.death_penalty_job > 0) - { + + if(battle_config.death_penalty_job > 0) { base_penalty = 0; + switch (battle_config.death_penalty_type) { case 1: base_penalty = (unsigned int) ((double)pc->nextjobexp(sd) * (double)battle_config.death_penalty_job/10000); @@ -6889,15 +6897,18 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) { base_penalty = (unsigned int) ((double)sd->status.job_exp * (double)battle_config.death_penalty_job/10000); break; } + if(base_penalty) { if (battle_config.pk_mode && src && src->type==BL_PC) base_penalty*=2; + if( sd->status.mod_death != 100 ) + base_penalty = base_penalty * sd->status.mod_death / 100; sd->status.job_exp -= min(sd->status.job_exp, base_penalty); clif->updatestatus(sd,SP_JOBEXP); } } - if(battle_config.zeny_penalty > 0 && !map->list[sd->bl.m].flag.nozenypenalty) - { + + if(battle_config.zeny_penalty > 0 && !map->list[sd->bl.m].flag.nozenypenalty) { base_penalty = (unsigned int)((double)sd->status.zeny * (double)battle_config.zeny_penalty / 10000.); if(base_penalty) pc->payzeny(sd, base_penalty, LOG_TYPE_PICKDROP_PLAYER, NULL); @@ -7049,6 +7060,9 @@ int pc_readparam(struct map_session_data* sd,int type) case SP_KILLEDRID: val = sd->killedrid; break; case SP_SLOTCHANGE: val = sd->status.slotchange; break; case SP_CHARRENAME: val = sd->status.rename; break; + case SP_MOD_EXP: val = sd->status.mod_exp; break; + case SP_MOD_DROP: val = sd->status.mod_drop; break; + case SP_MOD_DEATH: val = sd->status.mod_death; break; case SP_CRITICAL: val = sd->battle_status.cri/10; break; case SP_ASPD: val = (2000-sd->battle_status.amotion)/10; break; case SP_BASE_ATK: val = sd->battle_status.batk; break; @@ -7166,139 +7180,148 @@ int pc_setparam(struct map_session_data *sd,int type,int val) nullpo_ret(sd); switch(type){ - case SP_BASELEVEL: - if ((unsigned int)val > pc->maxbaselv(sd)) //Capping to max - val = pc->maxbaselv(sd); - if ((unsigned int)val > sd->status.base_level) { - int stat=0; - for (i = 0; i < (int)((unsigned int)val - sd->status.base_level); i++) - stat += pc->gets_status_point(sd->status.base_level + i); - sd->status.status_point += stat; - } - sd->status.base_level = (unsigned int)val; - sd->status.base_exp = 0; - // clif->updatestatus(sd, SP_BASELEVEL); // Gets updated at the bottom - clif->updatestatus(sd, SP_NEXTBASEEXP); - clif->updatestatus(sd, SP_STATUSPOINT); - clif->updatestatus(sd, SP_BASEEXP); - status_calc_pc(sd, 0); - if(sd->status.party_id) - { - party->send_levelup(sd); - } - break; - case SP_JOBLEVEL: - if ((unsigned int)val >= sd->status.job_level) { - if ((unsigned int)val > pc->maxjoblv(sd)) val = pc->maxjoblv(sd); - sd->status.skill_point += val - sd->status.job_level; - clif->updatestatus(sd, SP_SKILLPOINT); - } - sd->status.job_level = (unsigned int)val; - sd->status.job_exp = 0; - // clif->updatestatus(sd, SP_JOBLEVEL); // Gets updated at the bottom - clif->updatestatus(sd, SP_NEXTJOBEXP); - clif->updatestatus(sd, SP_JOBEXP); - status_calc_pc(sd, 0); - break; - case SP_SKILLPOINT: - sd->status.skill_point = val; - break; - case SP_STATUSPOINT: - sd->status.status_point = val; - break; - case SP_ZENY: - if( val < 0 ) - return 0;// can't set negative zeny - logs->zeny(sd, LOG_TYPE_SCRIPT, sd, -(sd->status.zeny - cap_value(val, 0, MAX_ZENY))); - sd->status.zeny = cap_value(val, 0, MAX_ZENY); - break; - case SP_BASEEXP: - if(pc->nextbaseexp(sd) > 0) { - sd->status.base_exp = val; - pc->checkbaselevelup(sd); - } - break; - case SP_JOBEXP: - if(pc->nextjobexp(sd) > 0) { - sd->status.job_exp = val; - pc->checkjoblevelup(sd); - } - break; - case SP_SEX: - sd->status.sex = val ? SEX_MALE : SEX_FEMALE; - break; - case SP_WEIGHT: - sd->weight = val; - break; - case SP_MAXWEIGHT: - sd->max_weight = val; - break; - case SP_HP: - sd->battle_status.hp = cap_value(val, 1, (int)sd->battle_status.max_hp); - break; - case SP_MAXHP: - sd->battle_status.max_hp = cap_value(val, 1, battle_config.max_hp); + case SP_BASELEVEL: + if ((unsigned int)val > pc->maxbaselv(sd)) //Capping to max + val = pc->maxbaselv(sd); + if ((unsigned int)val > sd->status.base_level) { + int stat=0; + for (i = 0; i < (int)((unsigned int)val - sd->status.base_level); i++) + stat += pc->gets_status_point(sd->status.base_level + i); + sd->status.status_point += stat; + } + sd->status.base_level = (unsigned int)val; + sd->status.base_exp = 0; + // clif->updatestatus(sd, SP_BASELEVEL); // Gets updated at the bottom + clif->updatestatus(sd, SP_NEXTBASEEXP); + clif->updatestatus(sd, SP_STATUSPOINT); + clif->updatestatus(sd, SP_BASEEXP); + status_calc_pc(sd, 0); + if(sd->status.party_id) + { + party->send_levelup(sd); + } + break; + case SP_JOBLEVEL: + if ((unsigned int)val >= sd->status.job_level) { + if ((unsigned int)val > pc->maxjoblv(sd)) val = pc->maxjoblv(sd); + sd->status.skill_point += val - sd->status.job_level; + clif->updatestatus(sd, SP_SKILLPOINT); + } + sd->status.job_level = (unsigned int)val; + sd->status.job_exp = 0; + // clif->updatestatus(sd, SP_JOBLEVEL); // Gets updated at the bottom + clif->updatestatus(sd, SP_NEXTJOBEXP); + clif->updatestatus(sd, SP_JOBEXP); + status_calc_pc(sd, 0); + break; + case SP_SKILLPOINT: + sd->status.skill_point = val; + break; + case SP_STATUSPOINT: + sd->status.status_point = val; + break; + case SP_ZENY: + if( val < 0 ) + return 0;// can't set negative zeny + logs->zeny(sd, LOG_TYPE_SCRIPT, sd, -(sd->status.zeny - cap_value(val, 0, MAX_ZENY))); + sd->status.zeny = cap_value(val, 0, MAX_ZENY); + break; + case SP_BASEEXP: + if(pc->nextbaseexp(sd) > 0) { + sd->status.base_exp = val; + pc->checkbaselevelup(sd); + } + break; + case SP_JOBEXP: + if(pc->nextjobexp(sd) > 0) { + sd->status.job_exp = val; + pc->checkjoblevelup(sd); + } + break; + case SP_SEX: + sd->status.sex = val ? SEX_MALE : SEX_FEMALE; + break; + case SP_WEIGHT: + sd->weight = val; + break; + case SP_MAXWEIGHT: + sd->max_weight = val; + break; + case SP_HP: + sd->battle_status.hp = cap_value(val, 1, (int)sd->battle_status.max_hp); + break; + case SP_MAXHP: + sd->battle_status.max_hp = cap_value(val, 1, battle_config.max_hp); - if( sd->battle_status.max_hp < sd->battle_status.hp ) - { - sd->battle_status.hp = sd->battle_status.max_hp; - clif->updatestatus(sd, SP_HP); - } - break; - case SP_SP: - sd->battle_status.sp = cap_value(val, 0, (int)sd->battle_status.max_sp); - break; - case SP_MAXSP: - sd->battle_status.max_sp = cap_value(val, 1, battle_config.max_sp); + if( sd->battle_status.max_hp < sd->battle_status.hp ) + { + sd->battle_status.hp = sd->battle_status.max_hp; + clif->updatestatus(sd, SP_HP); + } + break; + case SP_SP: + sd->battle_status.sp = cap_value(val, 0, (int)sd->battle_status.max_sp); + break; + case SP_MAXSP: + sd->battle_status.max_sp = cap_value(val, 1, battle_config.max_sp); - if( sd->battle_status.max_sp < sd->battle_status.sp ) - { - sd->battle_status.sp = sd->battle_status.max_sp; - clif->updatestatus(sd, SP_SP); - } - break; - case SP_STR: - sd->status.str = cap_value(val, 1, pc_maxparameter(sd)); - break; - case SP_AGI: - sd->status.agi = cap_value(val, 1, pc_maxparameter(sd)); - break; - case SP_VIT: - sd->status.vit = cap_value(val, 1, pc_maxparameter(sd)); - break; - case SP_INT: - sd->status.int_ = cap_value(val, 1, pc_maxparameter(sd)); - break; - case SP_DEX: - sd->status.dex = cap_value(val, 1, pc_maxparameter(sd)); - break; - case SP_LUK: - sd->status.luk = cap_value(val, 1, pc_maxparameter(sd)); - break; - case SP_KARMA: - sd->status.karma = val; - break; - case SP_MANNER: - sd->status.manner = val; - break; - case SP_FAME: - sd->status.fame = val; - break; - case SP_KILLERRID: - sd->killerrid = val; - return 1; - case SP_KILLEDRID: - sd->killedrid = val; - return 1; - case SP_SLOTCHANGE: - sd->status.slotchange = val; - return 1; - case SP_CHARRENAME: - sd->status.rename = val; - return 1; - default: - ShowError("pc_setparam: Attempted to set unknown parameter '%d'.\n", type); - return 0; + if( sd->battle_status.max_sp < sd->battle_status.sp ) + { + sd->battle_status.sp = sd->battle_status.max_sp; + clif->updatestatus(sd, SP_SP); + } + break; + case SP_STR: + sd->status.str = cap_value(val, 1, pc_maxparameter(sd)); + break; + case SP_AGI: + sd->status.agi = cap_value(val, 1, pc_maxparameter(sd)); + break; + case SP_VIT: + sd->status.vit = cap_value(val, 1, pc_maxparameter(sd)); + break; + case SP_INT: + sd->status.int_ = cap_value(val, 1, pc_maxparameter(sd)); + break; + case SP_DEX: + sd->status.dex = cap_value(val, 1, pc_maxparameter(sd)); + break; + case SP_LUK: + sd->status.luk = cap_value(val, 1, pc_maxparameter(sd)); + break; + case SP_KARMA: + sd->status.karma = val; + break; + case SP_MANNER: + sd->status.manner = val; + break; + case SP_FAME: + sd->status.fame = val; + break; + case SP_KILLERRID: + sd->killerrid = val; + return 1; + case SP_KILLEDRID: + sd->killedrid = val; + return 1; + case SP_SLOTCHANGE: + sd->status.slotchange = val; + return 1; + case SP_CHARRENAME: + sd->status.rename = val; + return 1; + case SP_MOD_EXP: + sd->status.mod_exp = val; + return 1; + case SP_MOD_DROP: + sd->status.mod_drop = val; + return 1; + case SP_MOD_DEATH: + sd->status.mod_death = val; + return 1; + default: + ShowError("pc_setparam: Attempted to set unknown parameter '%d'.\n", type); + return 0; } clif->updatestatus(sd,type); @@ -10140,6 +10163,7 @@ void pc_bank_withdraw(struct map_session_data *sd, int money) { /* status change data arrived from char-server */ void pc_scdata_received(struct map_session_data *sd) { pc->inventory_rentals(sd); + clif->show_modifiers(sd); } /*========================================== -- cgit v1.2.3-70-g09d2