From 7b2f7c0503cb5be8bc45f57353d838a4dbf84d7b Mon Sep 17 00:00:00 2001 From: DracoRPG Date: Mon, 17 Jul 2006 00:15:33 +0000 Subject: Orn's and Albator's Homunculus system, finally, YAY!! git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/trunk@7706 54d463be-8e91-2dee-dedb-b68131a5f0ec --- src/map/atcommand.c | 169 +++++++ src/map/atcommand.h | 6 + src/map/battle.c | 29 +- src/map/battle.h | 2 + src/map/charsave.c | 41 +- src/map/charsave.h | 5 + src/map/chrif.c | 1 + src/map/clif.c | 297 +++++++++---- src/map/clif.h | 10 +- src/map/intif.c | 170 ++++++- src/map/intif.h | 7 + src/map/map.c | 4 + src/map/map.h | 37 +- src/map/mercenary.c | 1217 ++++++++++++++++++++++++++++++++++++++++++--------- src/map/mercenary.h | 79 +++- src/map/mob.c | 71 ++- src/map/pc.c | 26 ++ src/map/script.c | 22 +- src/map/skill.c | 382 +++++++++++++++- src/map/skill.h | 3 + src/map/status.c | 410 ++++++++++++----- src/map/status.h | 2 + src/map/unit.c | 24 +- 23 files changed, 2552 insertions(+), 462 deletions(-) (limited to 'src/map') diff --git a/src/map/atcommand.c b/src/map/atcommand.c index 7a9c2f604..5c84d0d9b 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -27,6 +27,7 @@ #include "skill.h" #include "mob.h" #include "pet.h" +#include "mercenary.h" //[orn] #include "battle.h" #include "party.h" #include "guild.h" @@ -298,6 +299,13 @@ ACMD_FUNC(commands); // [Skotlex] ACMD_FUNC(noask); //LuzZza ACMD_FUNC(request); //[Skotlex] +ACMD_FUNC(homlevel); //[orn] +ACMD_FUNC(homevolution); //[orn] +ACMD_FUNC(makehomun); //[orn] +ACMD_FUNC(homfriendly); //[orn] +ACMD_FUNC(homhungry); //[orn] +ACMD_FUNC(homtalk); //[orn] + /*========================================== *AtCommandInfo atcommand_info[]構造体の定義 *------------------------------------------ @@ -616,6 +624,13 @@ static AtCommandInfo atcommand_info[] = { { AtCommand_NoAsk, "@noask", 1, atcommand_noask }, // [LuzZza] { AtCommand_Request, "@request", 20, atcommand_request }, // [Skotlex] + { AtCommand_HomLevel, "@homlvup", 60, atcommand_homlevel }, + { AtCommand_HomEvolution, "@homevolution", 60, atcommand_homevolution }, + { AtCommand_MakeHomun, "@makehomun", 60, atcommand_makehomun }, + { AtCommand_HomFriendly, "@homfriendly", 60, atcommand_homfriendly }, + { AtCommand_HomHungry, "@homhungry", 60, atcommand_homhungry }, + { AtCommand_HomTalk, "@homtalk", 0, atcommand_homtalk }, + // add new commands before this line { AtCommand_Unknown, NULL, 1, NULL } }; @@ -9425,6 +9440,160 @@ int atcommand_mobinfo( return 0; } +/*========================================== + * homunculus level up [orn] + *------------------------------------------ + */ +int atcommand_homlevel( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int level = 0, i = 0; + + nullpo_retr(-1, sd); + + if (!message || !*message) + return -1; + + if ( sd->status.hom_id == 0 || !sd->homunculus.alive || sd->homunculus.vaporize ) + return 1 ; + + level = atoi(message); + if ( ( level + sd->homunculus.level ) > MAX_LEVEL ) + level = MAX_LEVEL - sd->homunculus.level ; + if (level >= 1) { + for (i = 1; i <= level ; i++){ + merc_hom_levelup(sd->hd) ; + } + clif_misceffect2(&sd->hd->bl,568) ; + } + + return 0; +} + +/*========================================== + * homunculus evolution H [orn] + *------------------------------------------ + */ +int atcommand_homevolution( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + nullpo_retr(-1, sd); + + if (sd->hd && sd->hd->homunculusDB->evo_class) + { + merc_hom_evolution(sd->hd) ; + } + return 0; +} + +/*========================================== + * call choosen homunculus [orn] + *------------------------------------------ + */ +int +atcommand_makehomun( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int homunid; + nullpo_retr(-1, sd); + if(sscanf(message, "%d", &homunid)<1) + return -1; + if( homunid < 6001 || homunid > 6016 ) + return -1; + if(sd->status.hom_id == 0) + { + merc_create_homunculus(sd,homunid); + } + else + { + clif_displaymessage(fd,msg_txt(144)); + } + return 0; +} + +/*========================================== + * modify homunculus intimacy [orn] + *------------------------------------------ + */ +int atcommand_homfriendly( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int friendly = 0; + + nullpo_retr(-1, sd); + + if (!message || !*message) + return -1; + + friendly = atoi(message); + if (sd->status.hom_id > 0 && sd->hd) { + if (friendly > 0 && friendly <= 1000) { + sd->homunculus.intimacy = friendly * 100 ; + clif_send_homdata(sd,SP_INTIMATE,friendly); + } else { + return -1; + } + } + + return 0; +} + +/*========================================== + * modify homunculus hunger [orn] + *------------------------------------------ + */ +int atcommand_homhungry( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int hungry = 0; + + nullpo_retr(-1, sd); + + if (!message || !*message) + return -1; + + hungry = atoi(message); + if (sd->status.hom_id > 0 && sd->hd) { + if (hungry >= 0 && hungry <= 100) { + sd->homunculus.hunger = hungry; + clif_send_homdata(sd,SP_HUNGRY,sd->homunculus.hunger); + } else { + return -1; + } + } + + return 0; +} + +/*========================================== + * modify homunculus hunger [orn] + *------------------------------------------ + */ +int atcommand_homtalk( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char mes[100],temp[100]; + + nullpo_retr(-1, sd); + + if(!sd->status.hom_id || !sd->hd || !sd->homunculus.alive ) + return -1; + + if (sscanf(message, "%99[^\n]", mes) < 1) + return -1; + + snprintf(temp, sizeof temp ,"%s : %s",sd->homunculus.name,mes); + clif_message(&sd->hd->bl, temp); + + return 0; +} + /*========================================== * Show Items DB Info v 1.0 * originally by [Lupus] eAthena diff --git a/src/map/atcommand.h b/src/map/atcommand.h index 60b20c4ab..11cf113f0 100644 --- a/src/map/atcommand.h +++ b/src/map/atcommand.h @@ -271,6 +271,12 @@ enum AtCommandType { AtCommand_Commands, // [Skotlex] AtCommand_NoAsk, // [LuzZza] AtCommand_Request, // [Skotlex], supposedly taken from Freya (heard the command was there, but I haven't seen the code yet) + AtCommand_HomLevel, //[orn] + AtCommand_HomEvolution, //[orn] + AtCommand_MakeHomun, //[orn] + AtCommand_HomFriendly, //[orn] + AtCommand_HomHungry, //[orn] + AtCommand_HomTalk, //[orn] // end <- Ahem, guys, don't place AtCommands after AtCommand_Unknown! [Skotlex] AtCommand_Unknown, AtCommand_MAX diff --git a/src/map/battle.c b/src/map/battle.c index cee888fe0..929b4494c 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -761,6 +761,7 @@ static struct Damage battle_calc_weapon_attack( short i; struct map_session_data *sd, *tsd; + struct homun_data *hd; //[orn] struct Damage wd; struct status_change *sc = status_get_sc(src); struct status_change *tsc = status_get_sc(target); @@ -813,6 +814,7 @@ static struct Damage battle_calc_weapon_attack( BL_CAST(BL_PC, src, sd); BL_CAST(BL_PC, target, tsd); + BL_CAST(BL_HOMUNCULUS, src, hd); //[orn] if(sd) { if (sd->skillblown[0].id != 0) @@ -1029,6 +1031,7 @@ static struct Damage battle_calc_weapon_attack( case NPC_MENTALBREAKER: case GS_GROUNDDRIFT: case NJ_TATAMIGAESHI: + case HVAN_EXPLOSION: //[orn] flag.hit = 1; break; case CR_SHIELDBOOMERANG: @@ -1177,6 +1180,14 @@ static struct Damage battle_calc_weapon_attack( ATK_ADD(sd->inventory_data[index]->weight/10); break; } + case HFLI_SBR44: //[orn] + if(hd){ + wd.damage = hd->master->homunculus.intimacy ; + wd.damage2 = hd->master->homunculus.intimacy ; + hd->master->homunculus.intimacy = 200; + clif_send_homdata(hd->master,0x100,hd->master->homunculus.intimacy/100); + } + break; default: { i = (flag.cri?1:0)|(flag.arrow?2:0)|(skill_num == HW_MAGICCRASHER?4:0)|(skill_num == MO_EXTREMITYFIST?8:0); @@ -1499,6 +1510,12 @@ static struct Damage battle_calc_weapon_attack( case MO_BALKYOUNG: skillratio += 200; break; + case HFLI_MOON: //[orn] + skillratio += ( 110 * (skill_lv + 1) ) - 100 ; + skillratio /= wd.div_ ; + break; + case HFLI_SBR44: //[orn] + skillratio += 100 * skill_lv ; } ATK_RATE(skillratio); @@ -2468,6 +2485,7 @@ struct Damage battle_calc_misc_attack( case CR_ACIDDEMONSTRATION: md.flag = (md.flag&~BF_RANGEMASK)|BF_LONG; break; + case HVAN_EXPLOSION: //[orn] case NPC_SELFDESTRUCTION: case NPC_SMOKING: flag.elefix = flag.cardfix = 0; @@ -2553,6 +2571,9 @@ struct Damage battle_calc_misc_attack( case GS_FLING: md.damage = sd?sd->status.job_level:status_get_lv(src); break; + case HVAN_EXPLOSION: //[orn] + md.damage = sstatus->hp * (50 + 50 * skill_lv) / 100 ; + break ; } damage_div_fix(md.damage, md.div_); @@ -3619,6 +3640,7 @@ static const struct battle_data_short { { "autospell_stacking", &battle_config.autospell_stacking }, { "override_mob_names", &battle_config.override_mob_names }, { "min_chat_delay", &battle_config.min_chat_delay }, + { "homunculus_show_growth", &battle_config.homunculus_show_growth }, //[orn] }; static const struct battle_data_int { @@ -3662,7 +3684,7 @@ static const struct battle_data_int { { "max_heal", &battle_config.max_heal }, { "mob_remove_delay", &battle_config.mob_remove_delay }, { "sg_miracle_skill_duration", &battle_config.sg_miracle_skill_duration }, - + { "hvan_explosion_intimate", &battle_config.hvan_explosion_intimate }, //[orn] }; int battle_set_value(char *w1, char *w2) { @@ -4045,6 +4067,8 @@ void battle_set_defaults() { battle_config.autospell_stacking = 0; battle_config.override_mob_names = 0; battle_config.min_chat_delay = 0; + battle_config.hvan_explosion_intimate = 45000; //[orn] + battle_config.homunculus_show_growth = 0; //[orn] } void battle_validate_conf() { @@ -4257,6 +4281,9 @@ void battle_validate_conf() { if (battle_config.cell_stack_limit != 1) ShowWarning("Battle setting 'cell_stack_limit' takes no effect as this server was compiled without Cell Stack Limit support.\n"); #endif + + if(battle_config.hvan_explosion_intimate > 100000) //[orn] + battle_config.hvan_explosion_intimate = 100000; } /*========================================== diff --git a/src/map/battle.h b/src/map/battle.h index a4533395a..d1af883a6 100644 --- a/src/map/battle.h +++ b/src/map/battle.h @@ -436,6 +436,8 @@ extern struct Battle_Config { unsigned short autospell_stacking; //Enables autospell cards to stack. [Skotlex] unsigned short override_mob_names; //Enables overriding spawn mob names with the mob_db names. [Skotlex] unsigned short min_chat_delay; //Minimum time between client messages. [Skotlex] + unsigned int hvan_explosion_intimate ; // fix [albator] + unsigned short homunculus_show_growth ; //[orn] } battle_config; diff --git a/src/map/charsave.c b/src/map/charsave.c index 3f5241900..41a3293e4 100644 --- a/src/map/charsave.c +++ b/src/map/charsave.c @@ -26,6 +26,7 @@ struct mmo_charstatus *charsave_loadchar(int charid){ char *str_p; friends = 0; + ShowDebug("charsave_loadchar : charid = %d | hd->master->status.char_id = %d\n", charid) ; c = (struct mmo_charstatus *)aCalloc(1,sizeof(struct mmo_charstatus)); if(charid <= 0){ @@ -33,9 +34,9 @@ struct mmo_charstatus *charsave_loadchar(int charid){ aFree(c); return NULL; } - + // add homun_id [albator] //Tested, Mysql 4.1.9+ has no problems with the long query, the buf is 65k big and the sql server needs for it 0.00009 secs on an athlon xp 2400+ WinXP (1GB Mem) .. [Sirius] - sprintf(tmp_sql, "SELECT `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`, `str`,`agi`,`vit`,`int`,`dex`,`luk`, `max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point`, `option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`hair`,`hair_color`, `clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`, `last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`, `partner_id`, `father`, `mother`, `child`, `fame` FROM `char` WHERE `char_id` = '%d'", charid); + sprintf(tmp_sql, "SELECT `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`, `str`,`agi`,`vit`,`int`,`dex`,`luk`, `max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point`, `option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`hair`,`hair_color`, `clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`, `last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`, `partner_id`, `father`, `mother`, `child`, `fame`, `homun_id` FROM `char` WHERE `char_id` = '%d'", charid); if(mysql_query(&charsql_handle, tmp_sql)){ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); @@ -102,9 +103,10 @@ struct mmo_charstatus *charsave_loadchar(int charid){ c->mother = atoi(charsql_row[44]); c->child = atoi(charsql_row[45]); c->fame = atoi(charsql_row[46]); - + c->hom_id = atoi(charsql_row[47]); // albator mysql_free_result(charsql_res); + //Check for '0' Savepoint / LastPoint if (c->last_point.x == 0 || c->last_point.y == 0 || c->last_point.map == 0){ c->last_point.map = mapindex_name2id(MAP_PRONTERA); @@ -237,8 +239,10 @@ struct mmo_charstatus *charsave_loadchar(int charid){ c->global_reg_num = i; } */ + + //Shamelessly stolen from its_sparky (ie: thanks) and then assimilated by [Skotlex] - //Friend list + //Friend list sprintf(tmp_sql, "SELECT f.friend_account, f.friend_id, c.name FROM friends f LEFT JOIN `char` c ON f.friend_account=c.account_id AND f.friend_id=c.char_id WHERE f.char_id='%d'", charid); if(mysql_query(&charsql_handle, tmp_sql)){ @@ -248,7 +252,7 @@ struct mmo_charstatus *charsave_loadchar(int charid){ } else sql_res = mysql_store_result(&charsql_handle); - + if(sql_res) { for(i = 0; (sql_row = mysql_fetch_row(sql_res)) && imaster->status.char_id = %d\n", charid) ; sprintf(tmp_sql ,"UPDATE `char` SET `class`='%d', `base_level`='%d', `job_level`='%d'," "`base_exp`='%d', `job_exp`='%d', `zeny`='%d'," "`max_hp`='%d',`hp`='%d',`max_sp`='%d',`sp`='%d',`status_point`='%d',`skill_point`='%d'," @@ -280,7 +285,7 @@ int charsave_savechar(int charid, struct mmo_charstatus *c){ "`option`='%d',`karma`='%d',`manner`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d'," "`hair`='%d',`hair_color`='%d',`clothes_color`='%d',`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d'," "`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d'," - "`partner_id`='%d', `father`='%d', `mother`='%d', `child`='%d', `fame`='%d'" + "`partner_id`='%d', `father`='%d', `mother`='%d', `child`='%d', `fame`='%d', `homun_id`='%d'" "WHERE `account_id`='%d' AND `char_id` = '%d'", c->class_, c->base_level, c->job_level, c->base_exp, c->job_exp, c->zeny, @@ -291,7 +296,7 @@ int charsave_savechar(int charid, struct mmo_charstatus *c){ c->weapon, c->shield, c->head_top, c->head_mid, c->head_bottom, mapindex_id2name(c->last_point.map), c->last_point.x, c->last_point.y, mapindex_id2name(c->save_point.map), c->save_point.x, c->save_point.y, c->partner_id, c->father, c->mother, - c->child, c->fame, c->account_id, c->char_id + c->child, c->fame, c->hom_id, c->account_id, c->char_id ); if(mysql_query(&charsql_handle, tmp_sql)){ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); @@ -311,16 +316,16 @@ int charsave_savechar(int charid, struct mmo_charstatus *c){ str_p += sprintf(str_p, "INSERT INTO `inventory` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`"); for (j = 0; j < MAX_SLOTS; j++) str_p += sprintf(str_p, ", `card%d`", j); - + str_p += sprintf(str_p, ") VALUES ('%d', '%d', '%d', '%d', '%d', '%d', '%d'", charid, c->inventory[i].nameid, c->inventory[i].amount, c->inventory[i].equip, c->inventory[i].identify, c->inventory[i].refine, c->inventory[i].attribute); for (j = 0; j < MAX_SLOTS; j++) str_p += sprintf(str_p, ", '%d'", c->inventory[i].card[j]); - + strcat(tmp_sql,")"); - + if(mysql_query(&charsql_handle, tmp_sql)){ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); @@ -340,16 +345,16 @@ int charsave_savechar(int charid, struct mmo_charstatus *c){ str_p += sprintf(str_p, "INSERT INTO `cart_inventory` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`"); for (j = 0; j < MAX_SLOTS; j++) str_p += sprintf(str_p, ", `card%d`", j); - + str_p += sprintf(str_p, ") VALUES ('%d', '%d', '%d', '%d', '%d', '%d', '%d'", charid, c->cart[i].nameid, c->cart[i].amount, c->cart[i].equip, c->cart[i].identify, c->cart[i].refine, c->cart[i].attribute); for (j = 0; j < MAX_SLOTS; j++) str_p += sprintf(str_p, ", '%d'", c->cart[i].card[j]); - + strcat(tmp_sql,")"); - + if(mysql_query(&charsql_handle, tmp_sql)){ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); @@ -436,7 +441,7 @@ int charsave_savechar(int charid, struct mmo_charstatus *c){ int charsave_load_scdata(int account_id, int char_id) { //Loads character's sc_data struct map_session_data *sd; - + sd = map_id2sd(account_id); if (!sd) { @@ -450,7 +455,7 @@ int charsave_load_scdata(int account_id, int char_id) } sprintf(tmp_sql, "SELECT `type`, `tick`, `val1`, `val2`, `val3`, `val4` FROM `sc_data`" "WHERE `account_id`='%d' AND `char_id`='%d'", account_id, char_id); - + if(mysql_query(&charsql_handle, tmp_sql)){ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle)); ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); @@ -490,7 +495,7 @@ void charsave_save_scdata(int account_id, int char_id, struct status_change* sc_ char *p = tmp_sql; p += sprintf(p, "INSERT INTO `sc_data` (`account_id`, `char_id`, `type`, `tick`, `val1`, `val2`, `val3`, `val4`) VALUES "); - + for(i = 0; i < max_sc; i++) { if (sc_data->data[i].timer == -1) @@ -498,10 +503,10 @@ void charsave_save_scdata(int account_id, int char_id, struct status_change* sc_ timer = get_timer(sc_data->data[i].timer); if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick,tick) < 0) continue; - + p += sprintf(p, " ('%d','%d','%hu','%d','%d','%d','%d','%d'),", account_id, char_id, i, DIFF_TICK(timer->tick,tick), sc_data->data[i].val1, sc_data->data[i].val2, sc_data->data[i].val3, sc_data->data[i].val4); - + count++; } if (count > 0) diff --git a/src/map/charsave.h b/src/map/charsave.h index 6fa119e14..049efebda 100644 --- a/src/map/charsave.h +++ b/src/map/charsave.h @@ -7,6 +7,11 @@ #include "status.h" #ifndef TXT_ONLY + int charsave_loadHomunculus(int hom_id, struct homun_data *p); + int charsave_saveHomunculus(struct homun_data *hd); + int charsave_saveHomunculusSkills(struct homun_data *hd); + int charsave_deleteHomunculus(struct homun_data *hd); + struct mmo_charstatus *charsave_loadchar(int charid); int charsave_savechar(int charid, struct mmo_charstatus *c); int charsave_load_scdata(int account_id, int char_id); diff --git a/src/map/chrif.c b/src/map/chrif.c index 3f253d5a2..6f7415099 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -22,6 +22,7 @@ #include "npc.h" #include "pc.h" #include "status.h" +#include "mercenary.h" #ifndef TXT_ONLY #include "charsave.h" #endif diff --git a/src/map/clif.c b/src/map/clif.c index 5cc780656..bfa2f7eb7 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -41,6 +41,7 @@ #include "guild.h" #include "vending.h" #include "pet.h" +#include "mercenary.h" //[orn] #include "log.h" #include "irc.h" @@ -1449,93 +1450,89 @@ int clif_spawn(struct block_list *bl) } return 0; } -/*========================================== - * Homunculus [blackhole89] - *------------------------------------------ - */ -// Can somebody tell me why exactly I have commented this lot of stuff out? -// acknowledge client it has a homunculus -int clif_homunack(struct map_session_data *sd) -{ - struct homun_data *hd = sd->hd; - unsigned char buf[64]; - - nullpo_retr(0, sd); - nullpo_retr(0, sd->hd); - //memset(buf,0,packet_len_table[0x230]); - memset(buf,0,12); //not yet set that stuff - WBUFW(buf,0)=0x230; - WBUFL(buf,4)=hd->bl.id; - ShowError("in clif_homunack~\n"); - clif_send(buf,/*packet_len_table[0x230]*/12,&sd->bl,SELF); - - return 0; -} - -// homunculus stats et al -int clif_homuninfo(struct map_session_data *sd) +//[orn] +int clif_hominfo(struct map_session_data *sd, int flag) { struct homun_data *hd = sd->hd; struct status_data *status; unsigned char buf[128]; nullpo_retr(0, hd); + +// if ( sd->hd ) +// return 0 ; + status = &hd->battle_status; memset(buf,0,71); //packet_len_table[0x22e]); WBUFW(buf,0)=0x22e; - memcpy(WBUFP(buf,2),hd->name,NAME_LENGTH); - WBUFW(buf,27)=hd->level; - WBUFW(buf,29)=hd->hunger_rate; - WBUFL(buf,31)=0xFF; //intimacy, leave it as is - WBUFW(buf,35)=status->batk; + memcpy(WBUFP(buf,2),hd->master->homunculus.name,NAME_LENGTH); + WBUFB(buf,26)=hd->master->homunculus.rename_flag * 2; + WBUFW(buf,27)=hd->master->homunculus.level; + WBUFW(buf,29)=hd->master->homunculus.hunger; + WBUFW(buf,31)=(unsigned short) (hd->master->homunculus.intimacy / 100) ; + WBUFW(buf,33)=0; // equip id + WBUFW(buf,35)=status->rhw.atk2; WBUFW(buf,37)=status->matk_max; WBUFW(buf,39)=status->hit; WBUFW(buf,41)=status->cri/10; //crit is a +1 decimal value! - WBUFW(buf,43)=status->def; + WBUFW(buf,43)=status->def + status->vit ; WBUFW(buf,45)=status->mdef; WBUFW(buf,47)=status->flee; - WBUFW(buf,49)=status->amotion; + WBUFW(buf,49)=(flag)?0:status->amotion; WBUFW(buf,51)=status->hp; WBUFW(buf,53)=status->max_hp; WBUFW(buf,55)=status->sp; WBUFW(buf,57)=status->max_sp; - WBUFL(buf,59)=hd->exp; + WBUFL(buf,59)=hd->master->homunculus.exp; WBUFL(buf,63)=hd->exp_next; - WBUFW(buf,67)=hd->skillpts; - WBUFW(buf,69)=0x21; + WBUFW(buf,67)=hd->master->homunculus.skillpts; + WBUFW(buf,69)=hd->attackable; clif_send(buf,/*packet_len_table[0x22e]*/71,&sd->bl,SELF); return 0; } +/*========================================== + * + *------------------------------------------ + */ +void clif_send_homdata(struct map_session_data *sd, int type, int param) { //[orn] + int fd; + nullpo_retv(sd); + nullpo_retv(sd->hd); + + fd=sd->fd; + WFIFOW(fd,0)=0x230; + WFIFOW(fd,2)=type; + WFIFOL(fd,4)=sd->hd->bl.id; + WFIFOL(fd,8)=param; + WFIFOSET(fd,packet_len_table[0x230]); + + return; +} // like skillinfoblock, just for homunculi. -int clif_homunskillinfoblock(struct map_session_data *sd) -{ +int clif_homskillinfoblock(struct map_session_data *sd) { //[orn] int fd; - int i,c,len=4,id/*, inf2*/; + int i,j,c,len=4,id/*, inf2*/; nullpo_retr(0, sd); nullpo_retr(0, sd->hd); + if ( !sd->hd ) + return 0 ; + fd=sd->fd; - WFIFOHEAD(fd, 4 * 37 + 4); WFIFOW(fd,0)=0x235; - for ( i = c = 0; i < 4; i++){ - if( (id=sd->hd->hskill[i].id)!=0 ){ - WFIFOW(fd,len ) = id; - WFIFOW(fd,len+2) = skill_get_inf(id-7300); // H. skills mapped to 700 and above - WFIFOW(fd,len+4) = 0; - WFIFOW(fd,len+6) = sd->hd->hskill[i].level; - WFIFOW(fd,len+8) = skill_get_sp(id,sd->hd->hskill[i].level); - WFIFOW(fd,len+10)= skill_get_range2(&sd->bl, id,sd->hd->hskill[i].level); - strncpy(WFIFOP(fd,len+12), /*merc_skill_get_name(id)*/ "", NAME_LENGTH); // can somebody tell me what exactly that function was good for anyway - /* inf2 = skill_get_inf2(id); - if(((!(inf2&INF2_QUEST_SKILL) || battle_config.quest_skill_learn) && - !(inf2&(INF2_WEDDING_SKILL|INF2_SPIRIT_SKILL))) || - (battle_config.gm_allskill > 0 && pc_isGM(sd) >= battle_config.gm_allskill) ) - //WFIFOB(fd,len+36)= (sd->status.skill[i].lv < skill_get_max(id) && sd->status.skill[i].flag ==0 )? 1:0; - WFIFOB(fd,len+36)= (sd->status.skill[i].lv < skill_tree_get_max(id, sd->status.class_) && sd->status.skill[i].flag ==0 )? 1:0; - else */ + for ( i = c = 0; i < MAX_HOMUNSKILL; i++){ + if( (id = sd->homunculus.hskill[i].id) != 0 ){ + j = id - HM_SKILLBASE - 1 ; + WFIFOW(fd,len ) = id ; + WFIFOW(fd,len+2) = skill_get_inf(id) ; + WFIFOW(fd,len+4) = 0 ; + WFIFOW(fd,len+6) = sd->homunculus.hskill[j].lv ; + WFIFOW(fd,len+8) = skill_get_sp(id,sd->homunculus.hskill[j].lv) ; + WFIFOW(fd,len+10)= skill_get_range2(&sd->hd->bl, id,sd->homunculus.hskill[j].lv) ; + strncpy(WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH) ; WFIFOB(fd,len+36) = 1;//0; len+=37; c++; @@ -1547,49 +1544,146 @@ int clif_homunskillinfoblock(struct map_session_data *sd) return 0; } +void clif_homskillup(struct map_session_data *sd, int skill_num) { //[orn] + int range,fd,skillid; + + nullpo_retv(sd); + skillid = skill_num - HM_SKILLBASE ; + + fd=sd->fd; + WFIFOW(fd,0) = 0x10e; + WFIFOW(fd,2) = skill_num; + WFIFOW(fd,4) = sd->homunculus.hskill[skillid].lv; + WFIFOW(fd,6) = skill_get_sp(skill_num,sd->homunculus.hskill[skillid].lv); + range = skill_get_range(skill_num,sd->homunculus.hskill[skillid].lv); + if(range < 0) + range = status_get_range(&sd->bl) - (range + 1); + WFIFOW(fd,8) = range; + WFIFOB(fd,10) = (sd->homunculus.hskill[skillid].lv < skill_get_max(sd->homunculus.hskill[skillid].id)) ? 1 : 0; + WFIFOSET(fd,packet_len_table[0x10e]); + + return; +} + // Request a Homunculus name change -void clif_parse_ChangeHomunculusName(int fd, struct map_session_data *sd) { +void clif_parse_ChangeHomunculusName(int fd, struct map_session_data *sd) { //[orn] RFIFOHEAD(fd); nullpo_retv(sd); nullpo_retv(sd->hd); - memcpy(sd->hd->name,RFIFOP(fd,2),24); - clif_homuninfo(sd); + memcpy(sd->homunculus.name,RFIFOP(fd,2),24); + sd->homunculus.rename_flag = 1 ; + clif_hominfo(sd,0); clif_charnameack(sd->fd,&sd->hd->bl); } // Somebody who is less lazy than me rename this to ReturnToMaster or something -void clif_parse_QueryHomunPos(int fd, struct map_session_data *sd) { +void clif_parse_HomMoveToMaster(int fd, struct map_session_data *sd) { //[orn] RFIFOHEAD(fd); nullpo_retv(sd); nullpo_retv(sd->hd); + + if ( sd->hd && status_isdead(&sd->hd->bl) ) + return ; + unit_walktoxy(&sd->hd->bl, sd->bl.x,sd->bl.y-1, 0); //move to master - //clif_homunposack(sd->hd); +} + +// player spend a skillpoint for homunculus +void clif_parse_HomUseSKillPoint(int fd, struct map_session_data *sd) { //[orn] + int skillid ; + nullpo_retv(sd); + nullpo_retv(sd->hd); + + if ( !sd->hd ) + return ; + skillid = RFIFOW(fd,2); + + merc_hom_skillup(sd->hd, skillid); } // Request a Homunculus move-to-position -void clif_parse_HMoveTo(int fd,struct map_session_data *sd) { +void clif_parse_HomMoveTo(int fd,struct map_session_data *sd) { //[orn] int x,y,cmd; nullpo_retv(sd); nullpo_retv(sd->hd); + if ( sd->hd && status_isdead(&sd->hd->bl) ) + return ; + cmd = RFIFOW(fd,0); x = RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[0]) * 4 + (RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[0] + 1) >> 6); y = ((RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[0]+1) & 0x3f) << 4) + (RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[0] + 2) >> 4); - unit_walktoxy(&sd->hd->bl,x,y,0); + unit_walktoxy(&(sd->hd->bl),x,y,0); } // Request the Homunculus attacking a bl -void clif_parse_HAttack(int fd,struct map_session_data *sd) { +void clif_parse_HomAttack(int fd,struct map_session_data *sd) { //[orn] + struct block_list *target; nullpo_retv(sd); nullpo_retv(sd->hd); + + target=map_id2bl(RFIFOL(fd,6)); + + if ( sd->hd && target && ( status_isdead(&sd->hd->bl) || status_isdead(target) ) ) + return ; if(sd->hd->bl.id != RFIFOL(fd,2)) return; - - printf("unit_attack returned: %d\n",unit_attack(&sd->hd->bl,RFIFOL(fd,6),0)); + merc_stop_walking(sd->hd, 1); + merc_stop_attack(sd->hd); + if ( sd->hd && target ) { + sd->hd->target_id = RFIFOL(fd,6) ; + unit_attack(&sd->hd->bl,RFIFOL(fd,6),1) ; + } +} + +void clif_parse_HomMenu(int fd, struct map_session_data *sd) { //[orn] + int cmd; + cmd = RFIFOW(fd,0); + RFIFOHEAD(fd); + if ( sd->hd && status_isdead(&sd->hd->bl) ) + return ; + merc_menu(sd,RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[0])); +} + +int clif_hom_food(struct map_session_data *sd,int foodid,int fail) //[orn] +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOHEAD(fd,packet_len_table[0x22f]); + WFIFOW(fd,0)=0x22f; + WFIFOB(fd,2)=fail; + WFIFOW(fd,3)=foodid; + WFIFOSET(fd,packet_len_table[0x22f]); + + return 0; +} + +/*========================================== + * orn + *------------------------------------------ + */ +int clif_hwalkok(struct homun_data *hd) +{ + int fd; + + nullpo_retr(0, hd); + + fd=hd->master->fd; + WFIFOHEAD(fd, packet_len_table[0x87]); + WFIFOW(fd,0)=0x87; + WFIFOL(fd,2)=gettick(); + WFIFOPOS2(fd,6,hd->bl.x,hd->bl.y,hd->ud.to_x,hd->ud.to_y); + WFIFOB(fd,11)=0x88; + WFIFOSET(fd,packet_len_table[0x87]); + + return 0; } /*========================================== @@ -7751,7 +7845,7 @@ int clif_charnameack (int fd, struct block_list *bl) break; //[blackhole89] case BL_HOMUNCULUS: - memcpy(WBUFP(buf,6), ((struct homun_data*)bl)->name, NAME_LENGTH); + memcpy(WBUFP(buf,6), ((struct homun_data*)bl)->master->homunculus.name, NAME_LENGTH); break; case BL_PET: memcpy(WBUFP(buf,6), ((struct pet_data*)bl)->name, NAME_LENGTH); @@ -8210,10 +8304,11 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd) if(sd->hd && sd->hd->battle_status.hp) { map_addblock(&sd->hd->bl); clif_spawn(&sd->hd->bl); - clif_homunack(sd); - clif_homuninfo(sd); - clif_homuninfo(sd); //for some reason, at least older clients want this sent twice - clif_homunskillinfoblock(sd); +// clif_homunack(sd); + clif_hominfo(sd,1); + clif_hominfo(sd,0); //for some reason, at least older clients want this sent twice + clif_send_homdata(sd,0,0); + clif_homskillinfoblock(sd); } if(sd->state.connect_new) { @@ -9612,6 +9707,9 @@ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) { if (skillnotok(skillnum, sd)) return; + if (sd->hd && skillnotok_hom(skillnum, sd->hd)) //[orn] + return; + if (sd->bl.id != target_id && !sd->state.skill_flag && skill_get_inf(skillnum)&INF_SELF_SKILL) @@ -9680,12 +9778,19 @@ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) { if (skillnum >= GD_SKILLBASE && sd->state.gmaster_flag) skilllv = guild_checkskill(sd->state.gmaster_flag, skillnum); - if ((lv = pc_checkskill(sd, skillnum)) > 0) { - if (skilllv > lv) - skilllv = lv; - unit_skilluse_id(&sd->bl, target_id, skillnum, skilllv); - if (sd->state.skill_flag) - sd->state.skill_flag = 0; + if ( ( skillnum >= HM_SKILLBASE ) && sd->status.hom_id && sd->homunculus.alive && !sd->homunculus.vaporize ) { //[orn] + if ( ( lv = merc_hom_checkskill(sd, skillnum) ) > 0 ) + if (skilllv > lv) + skilllv = lv; + unit_skilluse_id(&sd->hd->bl, target_id, skillnum, skilllv); + } else { + if ((lv = pc_checkskill(sd, skillnum)) > 0) { + if (skilllv > lv) + skilllv = lv; + unit_skilluse_id(&sd->bl, target_id, skillnum, skilllv); + if (sd->state.skill_flag) + sd->state.skill_flag = 0; + } } } } @@ -11559,6 +11664,36 @@ int clif_parse(int fd) { #if DUMP_ALL_PACKETS dump = 1; + int i; + FILE *fp; + char packet_txt[256] = "save/packet.txt"; + time_t now; + dump = 1; + + if ((fp = fopen(packet_txt, "a")) == NULL) { + ShowError("clif.c: cant write [%s] !!! data is lost !!!\n", packet_txt); + return 1; + } else { + time(&now); + if (sd && sd->state.auth) { + if (sd->status.name != NULL) + fprintf(fp, "%sPlayer with account ID %d (character ID %d, player name %s) sent packet:\n", + asctime(localtime(&now)), sd->status.account_id, sd->status.char_id, sd->status.name); + else + fprintf(fp, "%sPlayer with account ID %d sent wrong packet:\n", asctime(localtime(&now)), sd->bl.id); + } else if (sd) // not authentified! (refused by char-server or disconnect before to be authentified) + fprintf(fp, "%sPlayer with account ID %d sent wrong packet:\n", asctime(localtime(&now)), sd->bl.id); + + fprintf(fp, "\tsession #%d, packet 0x%04x, length %d, version %d\n", fd, cmd, packet_len, packet_ver); + fprintf(fp, "\t---- 00-01-02-03-04-05-06-07-08-09-0A-0B-0C-0D-0E-0F"); + for(i = 0; i < packet_len; i++) { + if ((i & 15) == 0) + fprintf(fp, "\n\t%04X ", i); + fprintf(fp, "%02X ", RFIFOB(fd,i)); + } + fprintf(fp, "\n\n"); + fclose(fp); + } #endif if (sd && sd->state.auth == 1 && sd->state.waitingdisconnect == 1) { // 切断待ちの場合パケットを処理しない @@ -11777,11 +11912,13 @@ static int packetdb_readdb(void) {clif_parse_FeelSaveOk,"feelsaveok"}, {clif_parse_AdoptRequest,"adopt"}, {clif_parse_debug,"debug"}, - //[blackhole89] + //[blackhole89] //[orn] {clif_parse_ChangeHomunculusName,"changehomunculusname"}, - {clif_parse_QueryHomunPos,"queryhomunpos"}, - {clif_parse_HMoveTo,"hmoveto"}, - {clif_parse_HAttack,"hattack"}, + {clif_parse_HomMoveToMaster,"hommovetomaster"}, + {clif_parse_HomMoveTo,"hommoveto"}, + {clif_parse_HomAttack,"homattack"}, + {clif_parse_HomUseSKillPoint,"homuseskillpoint"}, + {clif_parse_HomMenu,"hommenu"}, {NULL,NULL} }; diff --git a/src/map/clif.h b/src/map/clif.h index 53a6332c3..b89471985 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -354,9 +354,13 @@ void clif_mission_mob(struct map_session_data *sd, unsigned short mob_id, unsign // [blackhole89] int clif_spawnhomun(struct homun_data *hd); -int clif_homunack(struct map_session_data *sd); -int clif_homuninfo(struct map_session_data *sd); -int clif_homunskillinfoblock(struct map_session_data *sd); +int clif_hominfo(struct map_session_data *sd, int flag); +int clif_homskillinfoblock(struct map_session_data *sd); +void clif_homskillup(struct map_session_data *sd, int skill_num) ; //[orn] +int clif_hom_food(struct map_session_data *sd,int foodid,int fail); //[orn] +void clif_send_homdata(struct map_session_data *sd, int type, int param); //[orn] +int clif_hwalkok(struct homun_data *hd); //[orn] + #endif diff --git a/src/map/intif.c b/src/map/intif.c index 9f20d3a9c..bb12fa673 100644 --- a/src/map/intif.c +++ b/src/map/intif.c @@ -24,6 +24,7 @@ #include "guild.h" #include "pet.h" #include "atcommand.h" +#include "mercenary.h" //albator static const int packet_len_table[]={ -1,-1,27,-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3800-0x380f @@ -35,6 +36,7 @@ static const int packet_len_table[]={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11,-1, 7, 3, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3880 + 16,-1, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x3890 Homunculus [albator] }; extern int char_fd; // inter serverのfdはchar_fdを使う @@ -132,13 +134,13 @@ int intif_GMmessage(char* mes,int len,int flag) // Send to the local players clif_GMmessage(NULL, mes, len, flag); - + if (CheckForCharServer()) return 0; - + if (other_mapserver_count < 1) return 0; //No need to send. - + WFIFOHEAD(inter_fd,lp + len + 4); WFIFOW(inter_fd,0) = 0x3000; WFIFOW(inter_fd,2) = lp + len + 4; @@ -156,13 +158,13 @@ int intif_announce(char* mes,int len, unsigned long color, int flag) clif_MainChatMessage(mes); else clif_announce(NULL, mes, len, color, flag); - + if (CheckForCharServer()) return 0; if (other_mapserver_count < 1) return 0; //No need to send. - + WFIFOHEAD(inter_fd, 8 + len); WFIFOW(inter_fd,0) = 0x3000; WFIFOW(inter_fd,2) = 8 + len; @@ -182,8 +184,8 @@ int intif_wis_message(struct map_session_data *sd, char *nick, char *mes, int me { //Character not found. clif_wis_end(sd->fd, 1); return 0; - } - + } + WFIFOHEAD(inter_fd,mes_len + 52); WFIFOW(inter_fd,0) = 0x3001; WFIFOW(inter_fd,2) = mes_len + 52; @@ -236,7 +238,7 @@ int intif_wis_message_to_gm(char *Wisp_name, int min_gm_level, char *mes) { int intif_regtostr(char* str, struct global_reg *reg, int qty) { int len =0, i; - + for (i = 0; i < qty; i++) { len+= sprintf(str+len, "%s", reg[i].str)+1; //We add 1 to consider the '\0' in place. len+= sprintf(str+len, "%s", reg[i].value)+1; @@ -252,7 +254,7 @@ int intif_saveregistry(struct map_session_data *sd, int type) if (CheckForCharServer()) return -1; - + switch (type) { case 3: //Character reg reg = sd->save_reg.global; @@ -311,7 +313,7 @@ int intif_request_registry(struct map_session_data *sd, int flag) WFIFOW(inter_fd,0) = 0x3005; WFIFOL(inter_fd,2) = sd->status.account_id; WFIFOL(inter_fd,6) = sd->status.char_id; - WFIFOB(inter_fd,10) = (flag&1?1:0); //Request Acc Reg 2 + WFIFOB(inter_fd,10) = (flag&1?1:0); //Request Acc Reg 2 WFIFOB(inter_fd,11) = (flag&2?1:0); //Request Acc Reg WFIFOB(inter_fd,12) = (flag&4?1:0); //Request Char Reg WFIFOSET(inter_fd,13); @@ -405,7 +407,7 @@ int intif_party_addmember(int party_id,struct party_member *member) { if (CheckForCharServer()) return 0; - + WFIFOHEAD(inter_fd,42); WFIFOW(inter_fd,0)=0x3022; WFIFOW(inter_fd,2)=8+sizeof(struct party_member); @@ -448,7 +450,7 @@ int intif_party_changemap(struct map_session_data *sd,int online) return 0; if(!sd) return 0; - + WFIFOHEAD(inter_fd,19); WFIFOW(inter_fd,0)=0x3025; WFIFOL(inter_fd,2)=sd->status.party_id; @@ -776,6 +778,72 @@ int intif_guild_castle_datasave(int castle_id,int index, int value) return 0; } +//----------------------------------------------------------------- +// Homunculus Packets send to Inter server [albator] +//----------------------------------------------------------------- + +int intif_homunculus_create(int account_id, struct s_homunculus *sh) +{ + if (CheckForCharServer()) + return 0; + WFIFOHEAD(inter_fd, 44+NAME_LENGHT); + WFIFOW(inter_fd, 0) = 0x3090; + WFIFOL(inter_fd, 2) = account_id; + WFIFOL(inter_fd, 6) = sh->char_id; + WFIFOW(inter_fd, 10) = sh->class_; + WFIFOL(inter_fd,12) = sh->max_hp; + WFIFOL(inter_fd,16) = sh->max_sp; + memcpy(WFIFOP(inter_fd,20), sh->name, NAME_LENGTH); + WFIFOL(inter_fd,44) = sh->str; + WFIFOL(inter_fd,48) = sh->agi; + WFIFOL(inter_fd,52) = sh->vit; + WFIFOL(inter_fd,56) = sh->int_; + WFIFOL(inter_fd,60) = sh->dex; + WFIFOL(inter_fd,64) = sh->luk; + WFIFOSET(inter_fd, 44+NAME_LENGTH); + + return 0; +} + +int intif_homunculus_requestload(int account_id, int homun_id) +{ + if (CheckForCharServer()) + return 0; + WFIFOHEAD(inter_fd, 10); + WFIFOW(inter_fd,0) = 0x3091; + WFIFOL(inter_fd,2) = account_id; + WFIFOL(inter_fd,6) = homun_id; + WFIFOSET(inter_fd, 10); + return 0; +} + +int intif_homunculus_requestsave(int account_id, struct s_homunculus* sh) +{ + if (CheckForCharServer()) + return 0; + WFIFOHEAD(inter_fd, sizeof(struct s_homunculus)+10); + WFIFOW(inter_fd,0) = 0x3092; + WFIFOL(inter_fd,2) = sizeof(struct s_homunculus)+10; + WFIFOL(inter_fd,6) = account_id; + memcpy(WFIFOP(inter_fd,10),sh,sizeof(struct s_homunculus)); + WFIFOSET(inter_fd, sizeof(struct s_homunculus)+10); + return 0; + +} + +int intif_homunculus_requestdelete(int homun_id) +{ + if (CheckForCharServer()) + return 0; + WFIFOHEAD(inter_fd, 6); + WFIFOW(inter_fd, 0) = 0x3093; + WFIFOL(inter_fd,2) = homun_id; + WFIFOSET(inter_fd,6); + return 0; + +} + + //----------------------------------------------------------------- // Packets receive from inter server @@ -881,9 +949,9 @@ int intif_parse_Registers(int fd) { if (RFIFOB(fd,12) == 3 && sd->status.char_id != RFIFOL(fd,8)) return 1; //Character registry from another character. - + flag = (sd->save_reg.global_num == -1 || sd->save_reg.account_num == -1 || sd->save_reg.account2_num == -1); - + switch (RFIFOB(fd,12)) { case 3: //Character Registry reg = sd->save_reg.global; @@ -1370,6 +1438,74 @@ int intif_parse_RenamePetOk(int fd) return 0; } +//---------------------------------------------------------------- +// Homunculus recv packets [albator] + +int intif_parse_CreateHomunculus(int fd) +{ + struct map_session_data *sd = NULL; + RFIFOHEAD(fd); + + if((sd=map_id2sd(RFIFOL(fd,2)))==NULL || + sd->status.char_id != RFIFOL(fd,6)) + return 0; + + if(RFIFOW(fd,10)==1) + { + ShowInfo("Homunculus created successfully\n"); + sd->status.hom_id = sd->homunculus.hom_id = RFIFOL(fd,12); + merc_hom_recv_data(RFIFOL(fd,2), &sd->homunculus, 1) ; + } + else + { + ShowError("intif_parse_CreateHomunculus: failed to create homunculus\n"); + clif_displaymessage(sd->fd, "[debug] fail to create homunculus"); // display error message.. + } + + return 0; +} + +int intif_parse_RecvHomunculusData(int fd) +{ + struct s_homunculus sh; + int len; + + RFIFOHEAD(fd); + len=RFIFOW(fd,2); + + if(sizeof(struct s_homunculus)!=len-9) { + if(battle_config.etc_log) + ShowError("intif: homun data: data size error %d %d\n",sizeof(struct s_homunculus),len-9); + } + else{ + memcpy(&sh,RFIFOP(fd,9),sizeof(struct s_homunculus)); + merc_hom_recv_data(RFIFOL(fd,4),&sh,RFIFOB(fd,8)); + } + + return 0; + +} + +int intif_parse_SaveHomunculusOk(int fd) +{ + RFIFOHEAD(fd); + if(RFIFOB(fd,2) != 1) { + if(battle_config.error_log) + ShowError("homunculus data save failure\n"); + } + return 0; +} + +int intif_parse_DeleteHomunculusOk(int fd) +{ + RFIFOHEAD(fd); + if(RFIFOB(fd,2) != 1) { + if(battle_config.error_log) + ShowError("Homunculus data delete failure\n"); + } + + return 0; +} //----------------------------------------------------------------- // inter serverからの通信 // エラーがあれば0(false)を返すこと @@ -1398,7 +1534,7 @@ int intif_parse(int fd) } // 処理分岐 switch(cmd){ - case 0x3800: + case 0x3800: if (RFIFOL(fd,4) == 0xFF000000) //Normal announce. clif_GMmessage(NULL,(char *) RFIFOP(fd,8),packet_len-8,0); else if (RFIFOL(fd,4) == 0xFE000000) //Main chat message [LuzZza] @@ -1445,6 +1581,10 @@ int intif_parse(int fd) case 0x3882: intif_parse_SavePetOk(fd); break; case 0x3883: intif_parse_DeletePetOk(fd); break; case 0x3884: intif_parse_RenamePetOk(fd); break; + case 0x3890: intif_parse_CreateHomunculus(fd); break; + case 0x3891: intif_parse_RecvHomunculusData(fd); break; + case 0x3892: intif_parse_SaveHomunculusOk(fd); break; + case 0x3893: intif_parse_DeleteHomunculusOk(fd); break; default: if(battle_config.error_log) ShowError("intif_parse : unknown packet %d %x\n",fd,RFIFOW(fd,0)); diff --git a/src/map/intif.h b/src/map/intif.h index 38a2cace7..0351642b1 100644 --- a/src/map/intif.h +++ b/src/map/intif.h @@ -60,6 +60,13 @@ int intif_save_petdata(int account_id, struct s_pet *p); int intif_delete_petdata(int pet_id); int intif_rename_pet(struct map_session_data *sd, char *name); + +int intif_homunculus_create(int account_id, struct s_homunculus *sh); +int intif_homunculus_requestload(int account_id, int homun_id); +int intif_homunculus_requestsave(int account_id, struct s_homunculus* sh); +int intif_homunculus_requestdelete(int homun_id); + + int CheckForCharServer(void); #endif diff --git a/src/map/map.c b/src/map/map.c index 15a41f4c1..a34805997 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -39,6 +39,7 @@ #include "script.h" #include "guild.h" #include "pet.h" +#include "mercenary.h" //[orn] #include "atcommand.h" #include "charcommand.h" @@ -1664,6 +1665,8 @@ int map_quit(struct map_session_data *sd) { sd->state.waitingdisconnect = 1; if (sd->pd) unit_free(&sd->pd->bl); + if(sd->status.hom_id > 0 && sd->hd) //[orn] + merc_hom_delete(sd->hd, 0) ; unit_free(&sd->bl); chrif_save(sd,1); } else { //Try to free some data, without saving anything (this could be invoked on map server change. [Skotlex] @@ -3886,6 +3889,7 @@ int do_init(int argc, char *argv[]) { do_init_storage(); do_init_skill(); do_init_pet(); + do_init_merc(); //[orn] do_init_npc(); do_init_unit(); #ifndef TXT_ONLY /* mail system [Valaris] */ diff --git a/src/map/map.h b/src/map/map.h index 75c6fe95a..a82f8bee1 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -485,6 +485,7 @@ struct party_data { struct npc_data; struct pet_db; +struct homunculus_db; //[orn] struct item_data; struct square; @@ -747,6 +748,7 @@ struct map_session_data { struct s_pet pet; struct pet_data *pd; + struct s_homunculus homunculus ; //[orn] struct homun_data *hd; // [blackhole89] struct{ @@ -932,26 +934,35 @@ struct homun_data { struct view_data *vd; struct status_data base_status, battle_status; struct status_change sc; - - char name[NAME_LENGTH]; - int id; - short class_; + struct homunculus_db *homunculusDB; //[orn] struct map_session_data *master; //pointer back to its master - short hunger_rate; + int hungry_timer; //[orn] - struct { - int id; //0 = none - int level; - } hskill[4]; //skills (max. 4 for now) int target_id,attacked_id; + short attackable; - short level; - short regenhp,regensp; - unsigned long exp,exp_next; - short skillpts; + int natural_heal_timer; //[orn] + int hp_sub,sp_sub; + int inchealhptick,inchealsptick; + int nhealhp,nhealsp,nshealhp,nshealsp,nsshealhp,nsshealsp; + short hp_loss_value; + short sp_loss_value; + short hp_loss_type; + short sp_gain_value; + short hp_gain_value; + int hp_loss_tick; + int sp_loss_tick; + int hp_loss_rate; + int sp_loss_rate; + unsigned int canregen_tick; + + + unsigned short regenhp,regensp; + unsigned long exp_next; + char blockskill[MAX_SKILL]; // [orn] }; struct pet_data { diff --git a/src/map/mercenary.c b/src/map/mercenary.c index 8f599be43..49ebbcde1 100644 --- a/src/map/mercenary.c +++ b/src/map/mercenary.c @@ -32,29 +32,19 @@ #include "unit.h" #include "mercenary.h" -// Homunculus and future Mercenary system code go here [Celest] +#include "charsave.h" + typedef char char32[32]; -// everything below is crappy code by [blackhole89]. - -/* -HLIF_HEAL#ト。タッタヌ _ユ+(ネ)# -HLIF_AVOID#++゙ネクヌヌ# -HLIF_BRAIN#___# -HLIF_CHANGE#ク倏> テ_タホチ# -HAMI_CASTLE#ト___ク# -HAMI_DEFENCE#ニ訐コ# -HAMI_SKIN#_ニ_ルククニ_ソ _コナ_# -HAMI_BLOODLUST#コキッ キッ_コニR# -HFLI_MOON#ケRカタフニR# -HFLI_FLEET#ヌテク_ ケ<コ# -HFLI_SPEED#ソタケ _コヌヌ# -HFLI_SBR44#S.B.R.44# -HVAN_CAPRICE#ト<ヌチクR_コ# -HVAN_CHAOTIC#ト<ソタニ_ コ__ラ_ヌ# -HVAN_INSTRUCT#テ_タホチ タホ_コニRキー_ヌ# -HVAN_EXPLOSION#ケルタフソタ タヘ_コヌテキホチッ# -*/ +static int dirx[8]={0,-1,-1,-1,0,1,1,1}; //[orn] +static int diry[8]={1,1,0,-1,-1,-1,0,1}; //[orn] + +//Better equiprobability than rand()% [orn] +#define rand(a, b) a+(int) ((float)(b-a+1)*rand()/(RAND_MAX+1.0)) + +struct homunculus_db homunculus_db[MAX_HOMUNCULUS_CLASS]; //[orn] +struct skill_tree_entry hskill_tree[MAX_HOMUNCULUS_CLASS][MAX_SKILL_TREE]; + char32 merc_skillname[20] = {"NULL","HLIF_HEAL","HLIF_AVOID","HLIF_BRAIN","HLIF_CHANGE", "HAMI_CASTLE","HAMI_DEFENCE","HAMI_SKIN","HAMI_BLOODLUST", "HFLI_MOON","HFLI_FLEET","HFLI_SPEED","HFLI_SBR44", @@ -62,106 +52,509 @@ char32 merc_skillname[20] = {"NULL","HLIF_HEAL","HLIF_AVOID","HLIF_BRAIN","HLIF_ void merc_load_exptables(void); int mercskill_castend_id( int tid, unsigned int tick, int id,int data ); +static int merc_hom_hungry(int tid,unsigned int tick,int id,int data); int do_init_merc (void) { merc_load_exptables(); + memset(homunculus_db,0,sizeof(homunculus_db)); //[orn] + read_homunculusdb(); //[orn] return 0; } -static unsigned long hexptbl[126]; +static unsigned long hexptbl[MAX_LEVEL+1]; void merc_load_exptables(void) { - FILE *fl; - int i; + FILE *fp; + char line[1024]; + int i,k; + int j=0; + int lines; + char *filename[]={"exp_homun.txt","exp_homun2.txt"}; + char *str[32],*h,*nh; - fl=fopen("db/hexptbl.txt","rb"); - if(!fl) return; - - ShowInfo("reading db/hexptbl.txt\n"); - for(i=0;i<125;++i) - { - fscanf(fl,"%lu,",&(hexptbl[i])); + j = 0; + memset(hexptbl,0,sizeof(hexptbl)); + for(i=0;i<2;i++){ + sprintf(line, "%s/%s", db_path, filename[i]); + fp=fopen(line,"r"); + if(fp==NULL){ + if(i>0) + continue; + ShowError("can't read %s\n",line); + return ; + } + lines = 0; + while(fgets(line,sizeof(line)-1,fp) && j <= MAX_LEVEL){ + + lines++; + + if(line[0] == '/' && line[1] == '/') + continue; + + for(k=0,h=line;k<20;k++){ + if((nh=strchr(h,','))!=NULL){ + str[k]=h; + *nh=0; + h=nh+1; + } else { + str[k]=h; + h+=strlen(h); + } + } + + hexptbl[j]= atoi(str[0]); + + j++; + } + if (j >= MAX_LEVEL) + ShowWarning("read_hexptbl: Reached max level in exp_homun [%d]. Remaining lines were not read.\n ", MAX_HOMUNCULUS_CLASS); + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",filename[i]); } - fclose(fl); + + return ; + } -char *merc_skill_get_name(int id) +char *merc_hom_skill_get_name(int id) { return merc_skillname[id-HM_SKILLBASE]; } void merc_damage(struct homun_data *hd,struct block_list *src,int hp,int sp) { - clif_homuninfo(hd->master); + nullpo_retv(hd); + + if( hd->battle_status.hp < 0) + hd->battle_status.hp = 0; + if( hd->battle_status.sp < 0) + hd->battle_status.sp = 0; + hd->master->homunculus.hp = hd->battle_status.hp ; + hd->master->homunculus.sp = hd->battle_status.sp ; + clif_hominfo(hd->master,0); } -int merc_dead(struct homun_data *hd, struct block_list *src) +int merc_hom_dead(struct homun_data *hd, struct block_list *src) { - //dead lol - merc_save(hd); + hd->master->homunculus.intimacy -= 100 ; + hd->master->homunculus.alive = 0 ; + if(hd->master->homunculus.intimacy <= 0) { + merc_stop_walking(hd, 1); + merc_stop_attack(hd); + clif_emotion(&hd->master->bl, 23) ; //omg + merc_hom_delete(hd,1) ; + return 1 ; + } + clif_emotion(&hd->bl, 16) ; //wah + clif_emotion(&hd->master->bl, 28) ; //sob + merc_hom_delete(hd, 0); + return 3; //Remove it from map. } -void merc_skillup(struct map_session_data *sd,short skillnum) +int merc_hom_delete(struct homun_data *hd, int flag) +{ + nullpo_retr(0, hd); + // Delete homunculus + if ( flag&1 ) { //sabbath + intif_homunculus_requestdelete(hd->master->homunculus.hom_id) ; + merc_stop_walking(hd, 1); + merc_stop_attack(hd); + clif_emotion(&hd->bl, 28) ; //sob + hd->master->status.hom_id = 0; + hd->master->homunculus.hom_id = 0; + chrif_save(hd->master,0); + } + else { + merc_save(hd) ; + } + if ( !unit_free(&hd->bl) ) + return 0 ; + aFree(hd); + + return 1; +} + +int merc_hom_calc_skilltree(struct map_session_data *sd) +{ + int i,id=0 ; + int j,f=1; + int c=0; + + nullpo_retr(0, sd); + c = sd->homunculus.class_ - 6001 ; + + for(i=0;i < MAX_SKILL_TREE && (id = hskill_tree[c][i].id) > 0;i++){ + if(sd->homunculus.hskill[id-HM_SKILLBASE-1].id) + continue; //Skill already known. + if(!battle_config.skillfree) { + for(j=0;j<5;j++) { + if( hskill_tree[c][i].need[j].id && + merc_hom_checkskill(sd,hskill_tree[c][i].need[j].id) < + hskill_tree[c][i].need[j].lv) { + f=0; + break; + } + } + } + if (f){ + sd->homunculus.hskill[id-HM_SKILLBASE-1].id = id ; + } + } + + return 0; +} + +int merc_hom_checkskill(struct map_session_data *sd,int skill_id) +{ + int i = skill_id - HM_SKILLBASE - 1 ; + if(sd == NULL) return 0; + + if(sd->homunculus.hskill[i].id == skill_id) + return (sd->homunculus.hskill[i].lv); + + return 0; +} + +static int merc_skill_tree_get_max(int id, int b_class){ + int i, skillid; + for(i=0;(skillid=hskill_tree[b_class-6001][i].id)>0;i++) + if (id == skillid) return hskill_tree[b_class-6001][i].max; + return skill_get_max (id); +} + +void merc_hom_skillup(struct homun_data *hd,int skillnum) +{ + int i = 0 ; + nullpo_retv(hd); + + if( hd->master->homunculus.vaporize == 0) { + i = skillnum - HM_SKILLBASE - 1 ; + if( hd->master->homunculus.skillpts > 0 && + hd->master->homunculus.hskill[i].id && + ( hd->master->homunculus.hskill[i].flag == 0 ) && //Don't allow raising while you have granted skills. [Skotlex] + hd->master->homunculus.hskill[i].lv < merc_skill_tree_get_max(skillnum, hd->master->homunculus.class_) + ) + { + hd->master->homunculus.hskill[i].lv++ ; + hd->master->homunculus.skillpts-- ; + status_calc_homunculus(hd,1) ; + clif_homskillup(hd->master, skillnum) ; + clif_hominfo(hd->master,0) ; + clif_homskillinfoblock(hd->master) ; + } + } +} + +int merc_hom_levelup(struct homun_data *hd) { - nullpo_retv(sd); - nullpo_retv(sd->hd); - if(!sd->hd->skillpts) return; //no skill points left + int growth_str, growth_agi, growth_vit, growth_int, growth_dex, growth_luk ; + int growth_max_hp, growth_max_sp ; + char output[256] ; + + if (hd->master->homunculus.level == MAX_LEVEL) return 0 ; + + hd->master->homunculus.level++ ; + if ( ( (hd->master->homunculus.level) % 3 ) == 0 ) hd->master->homunculus.skillpts++ ; //1 skillpoint each 3 base level + + hd->master->homunculus.exp -= hd->exp_next ; + hd->exp_next = hexptbl[hd->master->homunculus.level - 1] ; + + if ( hd->homunculusDB->gmaxHP <= hd->homunculusDB->gminHP ) + growth_max_hp = hd->homunculusDB->gminHP ; + else + growth_max_hp = rand(hd->homunculusDB->gminHP, hd->homunculusDB->gmaxHP) ; + if ( hd->homunculusDB->gmaxSP <= hd->homunculusDB->gminSP ) + growth_max_sp = hd->homunculusDB->gminSP ; + else + growth_max_sp = rand(hd->homunculusDB->gminSP, hd->homunculusDB->gmaxSP) ; + if ( hd->homunculusDB->gmaxSTR <= hd->homunculusDB->gminSTR ) + growth_str = hd->homunculusDB->gminSTR ; + else + growth_str = rand(hd->homunculusDB->gminSTR, hd->homunculusDB->gmaxSTR) ; + if ( hd->homunculusDB->gmaxAGI <= hd->homunculusDB->gminAGI ) + growth_agi = hd->homunculusDB->gminAGI ; + else + growth_agi = rand(hd->homunculusDB->gminAGI, hd->homunculusDB->gmaxAGI) ; + if ( hd->homunculusDB->gmaxVIT <= hd->homunculusDB->gminVIT ) + growth_vit = hd->homunculusDB->gminVIT ; + else + growth_vit = rand(hd->homunculusDB->gminVIT, hd->homunculusDB->gmaxVIT) ; + if ( hd->homunculusDB->gmaxDEX <= hd->homunculusDB->gminDEX ) + growth_dex = hd->homunculusDB->gminDEX ; + else + growth_dex = rand(hd->homunculusDB->gminDEX, hd->homunculusDB->gmaxDEX) ; + if ( hd->homunculusDB->gmaxINT <= hd->homunculusDB->gminINT ) + growth_int = hd->homunculusDB->gminINT ; + else + growth_int = rand(hd->homunculusDB->gminINT, hd->homunculusDB->gmaxINT) ; + if ( hd->homunculusDB->gmaxLUK <= hd->homunculusDB->gminLUK ) + growth_luk = hd->homunculusDB->gminLUK ; + else + growth_luk = rand(hd->homunculusDB->gminLUK, hd->homunculusDB->gmaxLUK) ; + + hd->base_status.max_hp += growth_max_hp ; + hd->base_status.max_sp += growth_max_sp ; + hd->master->homunculus.max_hp = hd->base_status.max_hp ; + hd->master->homunculus.max_sp = hd->base_status.max_sp ; + hd->master->homunculus.str += growth_str ; + hd->master->homunculus.agi += growth_agi ; + hd->master->homunculus.vit += growth_vit ; + hd->master->homunculus.dex += growth_dex ; + hd->master->homunculus.int_ += growth_int ; + hd->master->homunculus.luk += growth_luk ; + + if ( battle_config.homunculus_show_growth ) { + sprintf(output, + "Growth : hp:%d sp:%d str(%.2f) agi(%.2f) vit(%.2f) int(%.2f) dex(%.2f) luk(%.2f) ", growth_max_hp, growth_max_sp, growth_str/(float)10, growth_agi/(float)10, growth_vit/(float)10, growth_int/(float)10, growth_dex/(float)10, growth_luk/(float)10 ) ; + clif_disp_onlyself(hd->master,output,strlen(output)); + } + + hd->base_status.str = (int) (hd->master->homunculus.str / 10) ; + hd->base_status.agi = (int) (hd->master->homunculus.agi / 10) ; + hd->base_status.vit = (int) (hd->master->homunculus.vit / 10) ; + hd->base_status.dex = (int) (hd->master->homunculus.dex / 10) ; + hd->base_status.int_ = (int) (hd->master->homunculus.int_ / 10) ; + hd->base_status.luk = (int) (hd->master->homunculus.luk / 10) ; + + memcpy(&hd->battle_status, &hd->base_status, sizeof(struct status_data)) ; + status_calc_homunculus(hd,1) ; - sd->hd->hskill[(skillnum-HM_SKILLBASE)%4].id=skillnum; - sd->hd->hskill[(skillnum-HM_SKILLBASE)%4].level+=1; - sd->hd->skillpts-=1; + status_percent_heal(&hd->bl, 100, 100); +// merc_save(hd) ; //not necessary + + return 1 ; +} - clif_homuninfo(sd); - clif_homunskillinfoblock(sd); - clif_skillup(sd, skillnum); +int merc_hom_evolution(struct homun_data *hd) +{ + nullpo_retr(0, hd); - merc_save(sd->hd); + if(hd && hd->homunculusDB->evo_class) + { + hd->master->homunculus.class_ = hd->homunculusDB->evo_class; + hd->master->homunculus.vaporize = 1; + merc_stop_walking(hd, 1); + merc_stop_attack(hd); + merc_hom_delete(hd, 0) ; + merc_call_homunculus(hd->master); + clif_emotion(&hd->master->bl, 21) ; //no1 + clif_misceffect2(&hd->bl,568); + return 1 ; + } else { + clif_emotion(&hd->bl, 4) ; //swt + return 0 ; + } } -int merc_gainexp(struct homun_data *hd,int exp) +int merc_hom_gainexp(struct homun_data *hd,int exp) { - hd->exp += exp; + if(hd->master->homunculus.vaporize) + return 1; + + if( hd->exp_next == 0 ) { + hd->master->homunculus.exp = 0 ; + return 0; + } + + hd->master->homunculus.exp += exp; - if(hd->exp < hd->exp_next) + if(hd->master->homunculus.exp < hd->exp_next) { + clif_hominfo(hd->master,0); return 0; - //levelup + } + + //levelup do { - hd->exp-=hd->exp_next; - hd->exp_next=hexptbl[hd->level]; - hd->level++; + merc_hom_levelup(hd) ; } - while(hd->exp > hd->exp_next); + while(hd->master->homunculus.exp > hd->exp_next && hd->exp_next != 0 ); - clif_misceffect(&hd->bl,0); - status_calc_homunculus(hd,0); - status_percent_heal(&hd->bl, 100, 100); - clif_homuninfo(hd->master); + if( hd->exp_next == 0 ) { + hd->master->homunculus.exp = 0 ; + } + + status_calc_homunculus(hd,1); + clif_misceffect2(&hd->bl,568); + status_calc_homunculus(hd,1); return 0; } -void merc_heal(struct homun_data *hd,int hp,int sp) +int merc_hom_heal(struct homun_data *hd,int hp,int sp) { - clif_homuninfo(hd->master); + nullpo_retr(0, hd); + + if( hd->battle_status.max_hp < hd->battle_status.hp ) + hd->battle_status.hp = hd->battle_status.max_hp; + else if (hd->battle_status.hp <= 0) { + hd->battle_status.hp = 1; + } + + if( hd->battle_status.max_sp < hd->battle_status.sp ) + hd->battle_status.sp = hd->battle_status.max_sp; + else if (hd->battle_status.sp < 0) { + hd->battle_status.sp = 0; + } + + if ( (hd->battle_status.hp != hd->base_status.hp) || + (hd->battle_status.sp != hd->base_status.sp) ) + { + clif_hominfo(hd->master,0); + } + hd->master->homunculus.hp = hd->base_status.hp = hd->battle_status.hp ; + hd->master->homunculus.sp = hd->base_status.sp = hd->battle_status.sp ; + + return 1; } -#ifndef TXT_ONLY -void merc_save(struct homun_data *hd) +static unsigned int natural_heal_prev_tick,natural_heal_diff_tick; +static void merc_natural_heal_hp(struct homun_data *hd) { - sprintf(tmp_sql, "UPDATE `homunculus` SET `class`='%d',`name`='%s',`level`='%d',`exp`='%lu',`hunger`='%d',`hp`='%u',`sp`='%u',`skill1lv`='%d',`skill2lv`='%d',`skill3lv`='%d',`skill4lv`='%d',`skillpts`='%d' WHERE `id` = '%d'", - hd->class_,hd->name,hd->level,hd->exp,hd->hunger_rate, - hd->battle_status.hp,hd->battle_status.sp, - hd->hskill[0].level,hd->hskill[1].level,hd->hskill[2].level,hd->hskill[3].level, - hd->skillpts,hd->id); - if(mysql_query(&mmysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + + nullpo_retv(hd); + +// ShowDebug("merc_natural_heal_hp (1) : homunculus = %s | hd->ud.walktimer = %d |\n", hd->name, hd->ud.walktimer) ; + if(hd->ud.walktimer == -1) { + hd->inchealhptick += natural_heal_diff_tick; + } + else { + hd->inchealhptick = 0; return; } + +// ShowDebug("merc_natural_heal_hp (2) : homunculus = %s | hd->regenhp = %d |\n", hd->name, hd->regenhp) ; + if (hd->battle_status.hp != hd->battle_status.max_hp) { + if ((unsigned int)status_heal(&hd->bl, hd->regenhp, 0, 1) < hd->regenhp) + { //At full. + hd->inchealhptick = 0; + return; + } + } + + return; +} + +static void merc_natural_heal_sp(struct homun_data *hd) +{ + + nullpo_retv(hd); + +// ShowDebug("merc_natural_heal_sp (1) : homunculus = %s | hd->regensp = %d |\n", hd->name, hd->regensp) ; + if (hd->battle_status.sp != hd->battle_status.max_sp) { + if ((unsigned int)status_heal(&hd->bl, 0, hd->regensp, 1) < hd->regensp) + { //At full. + hd->inchealsptick = 0; + return; + } + } + + return; +} + +static void merc_bleeding (struct homun_data *hd) +{ + int hp = 0, sp = 0; + + if (hd->hp_loss_value > 0) { + hd->hp_loss_tick += natural_heal_diff_tick; + if (hd->hp_loss_tick >= hd->hp_loss_rate) { + do { + hp += hd->hp_loss_value; + hd->hp_loss_tick -= hd->hp_loss_rate; + } while (hd->hp_loss_tick >= hd->hp_loss_rate); + hd->hp_loss_tick = 0; + } + } + + if (hd->sp_loss_value > 0) { + hd->sp_loss_tick += natural_heal_diff_tick; + if (hd->sp_loss_tick >= hd->sp_loss_rate) { + do { + sp += hd->sp_loss_value; + hd->sp_loss_tick -= hd->sp_loss_rate; + } while (hd->sp_loss_tick >= hd->sp_loss_rate); + hd->sp_loss_tick = 0; + } + } + + if (hp > 0 || sp > 0) + status_zap(&hd->bl, hp, sp); + + return; +} + +/*========================================== + * HP/SP natural heal + *------------------------------------------ + */ + +//static int merc_natural_heal_sub(struct homun_data *hd,va_list ap) { +static int merc_natural_heal_sub(struct homun_data *hd,int tick) { +// int tick; + + nullpo_retr(0, hd); +// tick = va_arg(ap,int); + +// -- moonsoul (if conditions below altered to disallow natural healing if under berserk status) + if ( hd && ( status_isdead(&hd->bl) || + ( ( hd->sc.count ) && + ( (hd->sc.data[SC_POISON].timer != -1 ) || ( hd->sc.data[SC_BLEEDING].timer != -1 ) ) + ) ) + ) { //Cannot heal neither natural or special. + hd->hp_sub = hd->inchealhptick = 0; + hd->sp_sub = hd->inchealsptick = 0; + } else { + if ( DIFF_TICK (tick, hd->canregen_tick)<0 ) { + hd->hp_sub = hd->inchealhptick = 0; + hd->sp_sub = hd->inchealsptick = 0; + } else { //natural heal + merc_natural_heal_hp(hd); + merc_natural_heal_sp(hd); + hd->canregen_tick = tick; + } + } + if (hd->hp_loss_value > 0 || hd->sp_loss_value > 0) + merc_bleeding(hd); + else + hd->hp_loss_tick = hd->sp_loss_tick = 0; + + return 0; +} + +/*========================================== + * orn + *------------------------------------------ + */ +int merc_natural_heal(int tid,unsigned int tick,int id,int data) +{ + struct map_session_data *sd; + + sd=map_id2sd(id); + + nullpo_retr(0, sd); + + if(sd->homunculus.vaporize) + return 1; + + if(sd && sd->hd) { + natural_heal_diff_tick = DIFF_TICK(tick,natural_heal_prev_tick); + merc_natural_heal_sub(sd->hd, tick); + + natural_heal_prev_tick = tick; + sd->hd->natural_heal_timer = add_timer(gettick()+battle_config.natural_healhp_interval, merc_natural_heal,sd->bl.id,0); + } + return 0; +} + +#ifndef TXT_ONLY +void merc_save(struct homun_data *hd) +{ + intif_homunculus_requestsave(hd->master->status.account_id, &hd->master->homunculus) ; + } #else void merc_save(struct homun_data *hd) @@ -170,151 +563,581 @@ void merc_save(struct homun_data *hd) } #endif -static void merc_load_sub(struct homun_data *hd, struct map_session_data *sd) +static int merc_calc_pos(struct homun_data *hd,int tx,int ty,int dir) //[orn] +{ + int x,y,dx,dy; + int i,k; + + nullpo_retr(0, hd); + + hd->ud.to_x = tx; + hd->ud.to_y = ty; + + if(dir < 0 || dir >= 8) + return 1; + + dx = -dirx[dir]*2; + dy = -diry[dir]*2; + x = tx + dx; + y = ty + dy; + if(!unit_can_reach_pos(&hd->bl,x,y,0)) { + if(dx > 0) x--; + else if(dx < 0) x++; + if(dy > 0) y--; + else if(dy < 0) y++; + if(!unit_can_reach_pos(&hd->bl,x,y,0)) { + for(i=0;i<12;i++) { + k = rand(1, 8); +// k = rand()%8; + dx = -dirx[k]*2; + dy = -diry[k]*2; + x = tx + dx; + y = ty + dy; + if(unit_can_reach_pos(&hd->bl,x,y,0)) + break; + else { + if(dx > 0) x--; + else if(dx < 0) x++; + if(dy > 0) y--; + else if(dy < 0) y++; + if(unit_can_reach_pos(&hd->bl,x,y,0)) + break; + } + } + if(i>=12) { + x = tx; + y = ty; + if(!unit_can_reach_pos(&hd->bl,x,y,0)) + return 1; + } + } + } + hd->ud.to_x = x; + hd->ud.to_y = y; + return 0; +} + +int merc_menu(struct map_session_data *sd,int menunum) +{ + nullpo_retr(0, sd); + if (sd->hd == NULL) + return 1; + + switch(menunum) { + case 0: + merc_hom_food(sd, sd->hd); + break; + case 1: + merc_hom_food(sd, sd->hd); + break; + case 2: + merc_hom_delete(sd->hd, 1); + break; + default: + ShowError("merc_menu : unknown menu choice : %d\n", menunum) ; + break; + } + return 0; +} + +int merc_hom_food(struct map_session_data *sd, struct homun_data *hd) +{ + int i, k, emotion; + + if(hd->master->homunculus.vaporize) + return 1 ; + + k=hd->homunculusDB->foodID; + i=pc_search_inventory(sd,k); + if(i < 0) { + clif_hom_food(sd,k,0); + return 1; + } + pc_delitem(sd,i,1,0); + + if ( hd->master->homunculus.hunger >= 91 ) { + hd->master->homunculus.intimacy -= 50 ; + emotion = 16 ; + } else if ( hd->master->homunculus.hunger >= 76 ) { + hd->master->homunculus.intimacy -= 30 ; + emotion = 19 ; + } else if ( hd->master->homunculus.hunger >= 26 ) { + hd->master->homunculus.intimacy += 80 ; + emotion = 2 ; + } else if ( hd->master->homunculus.hunger >= 11 ) { + hd->master->homunculus.intimacy += 100 ; + emotion = 2 ; + } else { + hd->master->homunculus.intimacy += 50 ; + emotion = 2 ; + } + if(hd->master->homunculus.intimacy > 100000) + hd->master->homunculus.intimacy = 100000; + if(hd->master->homunculus.intimacy < 0) + hd->master->homunculus.intimacy = 0 ; + + emotion = 5 ; // Thanks + hd->master->homunculus.hunger += 10; //dunno increase value for each food + if(hd->master->homunculus.hunger > 100) + hd->master->homunculus.hunger = 100; + + clif_emotion(&hd->bl,emotion) ; + clif_send_homdata(sd,SP_HUNGRY,sd->homunculus.hunger); + clif_send_homdata(sd,SP_INTIMATE,sd->homunculus.intimacy / 100); + clif_hom_food(sd,hd->homunculusDB->foodID,1); + + return 0; +} + +static int merc_hom_hungry(int tid,unsigned int tick,int id,int data) +{ + struct map_session_data *sd; + struct homun_data *hd; + + sd=map_id2sd(id); + if(!sd) + return 1; + + if(!sd->status.hom_id || !sd->hd) + return 1; + + hd = sd->hd; + if(hd->hungry_timer != tid){ + if(battle_config.error_log) + ShowError("merc_hom_hungry_timer %d != %d\n",hd->hungry_timer,tid); + return 0 ; + } + hd->master->homunculus.hunger-- ; + if(hd->master->homunculus.hunger >= 0 && hd->master->homunculus.hunger <= 10) { + clif_emotion(&hd->bl, 6) ; //an + } + if(hd->master->homunculus.hunger == 25) { + clif_emotion(&hd->bl, 20) ; //hmm + } + if(hd->master->homunculus.hunger == 75) { + clif_emotion(&hd->bl, 33) ; //ok + } + if(hd->master->homunculus.hunger < 0) { + hd->master->homunculus.hunger = 0; + hd->master->homunculus.intimacy -= 100 ; + clif_send_homdata(sd,SP_INTIMATE,sd->homunculus.intimacy / 100); + if ( hd->master->homunculus.intimacy <= 0 ) { + merc_stop_walking(hd, 1); + merc_stop_attack(hd); + clif_emotion(&hd->master->bl, 23) ; //omg + merc_hom_delete(hd,1) ; + } + return 0 ; + } else { + clif_send_homdata(sd,SP_HUNGRY,sd->homunculus.hunger); + + hd->hungry_timer = add_timer(tick+hd->homunculusDB->hungryDelay,merc_hom_hungry,sd->bl.id,0); //simple Fix albator + return 1 ; + } + + +} + +int merc_hom_hungry_timer_delete(struct homun_data *hd) +{ + nullpo_retr(0, hd); + if(hd->hungry_timer != -1) { + delete_timer(hd->hungry_timer,merc_hom_hungry); + hd->hungry_timer = -1; + } + + return 1; +} + +int merc_natural_heal_timer_delete(struct homun_data *hd) +{ + nullpo_retr(0, hd); + if(hd->natural_heal_timer != -1) { + delete_timer(hd->natural_heal_timer,merc_natural_heal); + hd->natural_heal_timer = -1; + } + + return 1; +} + +int search_homunculusDB_index(int key,int type) +{ + int i; + + for(i=0;istatus.hom_id == 0 || sd->hd == 0) || sd->hd->master == sd); + + i = search_homunculusDB_index(sd->homunculus.class_,HOMUNCULUS_CLASS); + if(i < 0) { + sd->status.hom_id = 0; + return 1; + } + sd->hd = hd = (struct homun_data *)aCalloc(1,sizeof(struct homun_data)); + hd->homunculusDB = &homunculus_db[i]; + merc_calc_pos(hd,sd->bl.x,sd->bl.y,sd->ud.dir); + hd->bl.x = hd->ud.to_x; + hd->bl.y = hd->ud.to_y; + hd->master = sd; + + sd->status.hom_id = sd->homunculus.hom_id ; + hd->bl.m=sd->bl.m; hd->bl.x=sd->bl.x; - hd->bl.y=sd->bl.y; + hd->bl.y=sd->bl.y - 1 ; + hd->bl.subtype = MONS; hd->bl.type=BL_HOMUNCULUS; hd->bl.id= npc_get_new_npc_id(); hd->bl.prev=NULL; hd->bl.next=NULL; + hd->exp_next=hexptbl[hd->master->homunculus.level - 1]; + hd->ud.attacktimer=-1; + hd->ud.attackabletime=gettick(); + hd->target_id = 0 ; + hd->attackable = 1 ; - status_set_viewdata(&hd->bl, hd->class_); + for(i=0;isc.data[i].timer=-1; + hd->sc.data[i].val1 = hd->sc.data[i].val2 = hd->sc.data[i].val3 = hd->sc.data[i].val4 = 0; + } + + hd->base_status.hp = hd->master->homunculus.hp ; + hd->base_status.max_hp = hd->master->homunculus.max_hp ; + hd->base_status.sp = hd->master->homunculus.sp ; + hd->base_status.max_sp = hd->master->homunculus.max_sp ; + hd->base_status.str = (int) (hd->master->homunculus.str / 10) ; + hd->base_status.agi = (int) (hd->master->homunculus.agi / 10) ; + hd->base_status.vit = (int) (hd->master->homunculus.vit / 10) ; + hd->base_status.int_ = (int) (hd->master->homunculus.int_ / 10) ; + hd->base_status.dex = (int) (hd->master->homunculus.dex / 10) ; + hd->base_status.luk = (int) (hd->master->homunculus.luk / 10) ; + + memcpy(&hd->battle_status, &hd->base_status, sizeof(struct status_data)) ; + + status_set_viewdata(&hd->bl, hd->master->homunculus.class_); status_change_init(&hd->bl); + hd->ud.dir = sd->ud.dir; unit_dataset(&hd->bl); - + map_addiddb(&hd->bl); - status_calc_homunculus(hd,1); //this function will have more sense later on + status_calc_homunculus(hd,1); + //timer + hd->hungry_timer = add_timer(gettick()+hd->homunculusDB->hungryDelay,merc_hom_hungry,sd->bl.id,0); + natural_heal_prev_tick = gettick(); + hd->natural_heal_timer = add_timer(gettick()+battle_config.natural_healhp_interval, merc_natural_heal,sd->bl.id,0); + + return 0; } -#ifndef TXT_ONLY -void merc_load(struct map_session_data *sd) +// FIX call_homunculus [albator] +int merc_call_homunculus(struct map_session_data *sd) { - struct homun_data *hd; - sd->hd=NULL; - - sprintf(tmp_sql, "SELECT `id`,`class`,`name`,`level`,`exp`,`hunger`,`hp`,`sp`,`skill1lv`,`skill2lv`,`skill3lv`,`skill4lv`,`skillpts` FROM `homunculus` WHERE `char_id` = '%d'", sd->char_id); - if(mysql_query(&mmysql_handle, tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return; + int class_ = 0 ; + nullpo_retr(0, sd); + + // call vaporized homunculus [albator] + if(sd->homunculus.vaporize == 1) + { + sd->homunculus.vaporize = 0; + merc_hom_data_init(sd); + + if ( sd->homunculus.alive && sd->hd && sd->bl.prev != NULL) { + map_addblock(&sd->hd->bl); + clif_spawn(&sd->hd->bl); + clif_send_homdata(sd,SP_ACK,0); + clif_hominfo(sd,1); + clif_hominfo(sd,0); // send this x2. dunno why, but kRO does that [blackhole89] + clif_homskillinfoblock(sd); + } + // save + merc_save(sd->hd); + return 1; + } - sql_res = mysql_store_result(&mmysql_handle); - if(!sql_res) - return; + if ( sd->status.hom_id ) { + return merc_hom_recv_data(sd->status.account_id, &sd->homunculus, 1 ) ; + } else { + class_ = 6000 + rand(1, 8) ; + return merc_create_homunculus(sd, class_) ; + } + + +} +// Albator +// Recv data of an homunculus after it loading +int merc_hom_recv_data(int account_id, struct s_homunculus *sh, int flag) +{ + struct map_session_data *sd ; - if(mysql_num_rows(sql_res) <= 0){ - mysql_free_result(sql_res); - return; //no homunculus for this char + sd = map_id2sd(account_id); + if(sd == NULL) + return 0; + + if(flag == 0) { + sd->status.hom_id = 0; + return 0; } - sql_row = mysql_fetch_row(sql_res); + memcpy(&sd->homunculus, sh, sizeof(struct s_homunculus)); + - //dummy code - hd=(struct homun_data *) aCalloc(1, sizeof(struct homun_data)); - sd->hd=hd; //pointer from master to homunculus - memset(hd,0,sizeof(struct homun_data)); - hd->master=sd; //pointer from homunculus to master - hd->id=atoi(sql_row[0]); - hd->class_=atoi(sql_row[1]); - hd->level=atoi(sql_row[3]); - hd->battle_status.hp=atoi(sql_row[6]); - hd->battle_status.sp=atoi(sql_row[7]); - hd->exp=atoi(sql_row[4]); - hd->hunger_rate=atoi(sql_row[5]); - hd->hskill[0].level=atoi(sql_row[8]); - hd->hskill[1].level=atoi(sql_row[9]); - hd->hskill[2].level=atoi(sql_row[10]); - hd->hskill[3].level=atoi(sql_row[11]); - hd->skillpts=atoi(sql_row[12]); - hd->exp_next=hexptbl[hd->level-1]; - strncpy(hd->name,sql_row[2],NAME_LENGTH); - mysql_free_result(sql_res); - merc_load_sub(hd, sd); -} -#else -void merc_load(struct map_session_data *sd) + if ( flag == 2 ) { + sh->hp = 1 ; + sd->homunculus.alive = 1 ; + } + if(sd->homunculus.alive && sh->vaporize!=1) + { + merc_hom_data_init(sd); + + if ( sd->hd && sd->bl.prev != NULL) { + map_addblock(&sd->hd->bl); + clif_spawn(&sd->hd->bl); + clif_hominfo(sd,1); + clif_hominfo(sd,0); // send this x2. dunno why, but kRO does that [blackhole89] + clif_homskillinfoblock(sd); + clif_hominfo(sd,0); + clif_send_homdata(sd,SP_ACK,0); + } + } + + return 1; + +} + +int merc_create_homunculus(struct map_session_data *sd, int class_) { - struct homun_data *hd; - int id,charid,class_,level,exp,hunger,hp,sp; - char name[24]; - FILE *fl=fopen("save/homunculus.txt","r"); - sd->hd=NULL; - - if(!fl) return; //Unable to open file. - ShowInfo("Looking up Homunculus for %d...\n",sd->char_id); - do { - fscanf(fl,"%d,%d,%d,%s ,%d,%d,%d,%d,%d\n",&id,&charid,&class_,name,&level,&exp,&hunger,&hp,&sp); - ShowInfo("%d",charid); - if(charid==sd->char_id) break; - } while(charid!=0); - if (!charid) - return; //none found - ShowInfo("found it!\n"); + int i=0 ; + + nullpo_retr(1, sd); + + i = search_homunculusDB_index(class_,HOMUNCULUS_CLASS); + if(i < 0) { + sd->status.hom_id = 0; + return 0; + } + memcpy(sd->homunculus.name, homunculus_db[i].name, NAME_LENGTH-1); + + sd->homunculus.class_ = class_; + sd->homunculus.level=1; + sd->homunculus.intimacy = 21; + sd->homunculus.hunger = 32; + sd->homunculus.exp = 0; + sd->homunculus.rename_flag = 0; + sd->homunculus.skillpts = 0; + sd->homunculus.char_id = sd->status.char_id; + sd->homunculus.vaporize = 0; // albator + sd->homunculus.alive = 1 ; - //dummy code - hd=(struct homun_data *) aCalloc(1, sizeof(struct homun_data)); - sd->hd=hd; //pointer from master to homunculus - memset(hd,0,sizeof(struct homun_data)); - hd->master=sd; //pointer from homunculus to master - hd->id=id; - hd->class_=class_; - hd->level=level; - hd->exp=exp; - hd->hunger_rate=hunger; - hd->battle_status.hp=hp; - hd->battle_status.sp=sp; - hd->exp_next=hexptbl[hd->level-1]; - strncpy(hd->name,name,NAME_LENGTH); - merc_load_sub(hd, sd); -} -#endif - -int merc_create_homunculus(struct map_session_data *sd,int id,int m,int x,int y) -{ -/* struct homun_data *hd; - //dummy code - hd=(struct homun_data *) aCalloc(1, sizeof(struct homun_data)); - sd->hd=hd; //pointer from master to homunculus - memset(hd,0,sizeof(struct homun_data)); - hd->master=sd; //pointer from homunculus to master - hd->class_=id; - hd->speed=0x96; - hd->level=1; - hd->bl.m=m; - hd->bl.x=hd->to_x=x; - hd->bl.y=hd->to_y=y; - hd->to_x+=2; - hd->bl.type=BL_HOMUNCULUS; - hd->bl.id= npc_get_new_npc_id(); - hd->bl.prev=NULL; - hd->bl.next=NULL; - map_addiddb(&hd->bl); - hd->max_hp=500; - hd->hp=400; - hd->max_sp=300; - hd->sp=200; - hd->atk=15; - hd->matk=2; - hd->hit=3; - hd->crit=90; - hd->def=5; - hd->mdef=6; - hd->flee=7; - hd->exp=10; - hd->exp_next=100; - hd->hunger_rate=32; - hd->walktimer=-1; - memcpy(hd->name,"Homunculus\0",11); - merc_calc_stats(hd); - hd->attackabletime=0; - - merc_save(hd); - - clif_spawnhomun(hd); - clif_homunack(sd); - clif_homuninfo(sd); - clif_homuninfo(sd);*/ // send this x2. dunno why, but kRO does that [blackhole89] + sd->homunculus.hp = 10 ; + sd->homunculus.sp = 0 ; + sd->homunculus.max_hp = homunculus_db[i].basemaxHP ; + sd->homunculus.max_sp = homunculus_db[i].basemaxSP ; + sd->homunculus.str = homunculus_db[i].baseSTR ; + sd->homunculus.agi = homunculus_db[i].baseAGI ; + sd->homunculus.vit = homunculus_db[i].baseVIT; + sd->homunculus.int_ = homunculus_db[i].baseINT ; + sd->homunculus.dex = homunculus_db[i].baseDEX ; + sd->homunculus.luk = homunculus_db[i].baseLUK ; + sd->homunculus.str *= 10 ; + sd->homunculus.agi *= 10 ; + sd->homunculus.vit *= 10 ; + sd->homunculus.int_ *= 10 ; + sd->homunculus.dex *= 10 ; + sd->homunculus.luk *= 10 ; + + for(i=0;ihomunculus.hskill[i].id = sd->homunculus.hskill[i].lv = sd->homunculus.hskill[i].flag = 0; + + intif_homunculus_create(sd->status.account_id, &sd->homunculus); // request homunculus creation + + return 1; +} + +int merc_hom_revive(struct map_session_data *sd, int per) +{ + nullpo_retr(0, sd); + + sd->homunculus.alive = 1; + merc_hom_data_init(sd); + + if ( sd->hd && sd->bl.prev != NULL) { + sd->homunculus.hp = sd->hd->base_status.hp = sd->hd->battle_status.hp = 1 ; + status_heal(&sd->hd->bl, sd->homunculus.max_hp*per/100, 0, 1) ; + map_addblock(&sd->hd->bl); + clif_spawn(&sd->hd->bl); + clif_send_homdata(sd,SP_ACK,0); + clif_hominfo(sd,1); + clif_hominfo(sd,0); + clif_homskillinfoblock(sd); + clif_specialeffect(&sd->hd->bl,77,AREA) ; //resurrection angel + } + + return 1 ; +} + +int read_homunculusdb() +{ + FILE *fp; + char line[1024], *p; + int i,k,l; + int j=0; + int c = 0 ; + int lines; + char *filename[]={"homunculus_db.txt","homunculus_db2.txt"}; + char *str[36],*h,*nh; + + + j = 0; + memset(homunculus_db,0,sizeof(homunculus_db)); + for(i=0;i<2;i++){ + sprintf(line, "%s/%s", db_path, filename[i]); + fp=fopen(line,"r"); + if(fp==NULL){ + if(i>0) + continue; + ShowError("can't read %s\n",line); + return -1; + } + lines = 0; + while(fgets(line,sizeof(line)-1,fp) && j < MAX_HOMUNCULUS_CLASS){ + + lines++; + + if(line[0] == '/' && line[1] == '/') + continue; + + for(k=0,h=line;k<36;k++){ + if((nh=strchr(h,','))!=NULL){ + str[k]=h; + *nh=0; + h=nh+1; + } else { + str[k]=h; + h+=strlen(h); + } + } + + if(atoi(str[0]) < 6001 || atoi(str[0]) > 6099) + continue; + + //Class,Homunculus,HP,SP,ATK,MATK,HIT,CRI,DEF,MDEF,FLEE,ASPD,STR,AGI,VIT,INT,DEX,LUK + homunculus_db[j].class_ = atoi(str[0]); + memcpy(homunculus_db[j].name,str[1],NAME_LENGTH-1); + homunculus_db[j].basemaxHP = atoi(str[2]); + homunculus_db[j].basemaxSP = atoi(str[3]); + homunculus_db[j].baseSTR = atoi(str[4]); + homunculus_db[j].baseAGI = atoi(str[5]); + homunculus_db[j].baseVIT = atoi(str[6]); + homunculus_db[j].baseINT = atoi(str[7]); + homunculus_db[j].baseDEX = atoi(str[8]); + homunculus_db[j].baseLUK = atoi(str[9]); + homunculus_db[j].baseIntimacy = atoi(str[10]); + homunculus_db[j].baseHungry = atoi(str[11]); + homunculus_db[j].hungryDelay = atoi(str[12]); + homunculus_db[j].foodID = atoi(str[13]); + homunculus_db[j].gminHP = atoi(str[14]); + homunculus_db[j].gmaxHP = atoi(str[15]); + homunculus_db[j].gminSP = atoi(str[16]); + homunculus_db[j].gmaxSP = atoi(str[17]); + homunculus_db[j].gminSTR = atoi(str[18]); + homunculus_db[j].gmaxSTR = atoi(str[19]); + homunculus_db[j].gminAGI = atoi(str[20]); + homunculus_db[j].gmaxAGI = atoi(str[21]); + homunculus_db[j].gminVIT = atoi(str[22]); + homunculus_db[j].gmaxVIT = atoi(str[23]); + homunculus_db[j].gminINT = atoi(str[24]); + homunculus_db[j].gmaxINT = atoi(str[25]); + homunculus_db[j].gminDEX = atoi(str[26]); + homunculus_db[j].gmaxDEX = atoi(str[27]); + homunculus_db[j].gminLUK = atoi(str[28]); + homunculus_db[j].gmaxLUK = atoi(str[29]); + homunculus_db[j].evo_class = atoi(str[30]); + homunculus_db[j].baseASPD = atoi(str[31]); + homunculus_db[j].size = atoi(str[32]); + homunculus_db[j].race = atoi(str[33]); + homunculus_db[j].element = atoi(str[34]); + homunculus_db[j].accessID = atoi(str[35]); + + j++; + } + if (j > MAX_HOMUNCULUS_CLASS) + ShowWarning("read_homunculusdb: Reached max number of homunculus [%d]. Remaining homunculus were not read.\n ", MAX_HOMUNCULUS_CLASS); + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' homunculus in '"CL_WHITE"db/%s"CL_RESET"'.\n",j,filename[i]); + } + + memset(hskill_tree,0,sizeof(hskill_tree)); + sprintf(line, "%s/homun_skill_tree.txt", db_path); + fp=fopen(line,"r"); + if(fp==NULL){ + ShowError("can't read %s\n", line); + return 1; + } + + while(fgets(line, sizeof(line)-1, fp)){ + char *split[50]; + int f=0, m=3; + if(line[0]=='/' && line[1]=='/') + continue; + for(j=0,p=line;j<14 && p;j++){ + split[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + if(j<13) + continue; + if (j == 14) { + f=1; // MinJobLvl has been added + m++; + } + // check for bounds [celest] + c = atoi(split[0]) ; + l = c - 6001 ; + if ( l >= MAX_HOMUNCULUS_CLASS ) + continue; + k = atoi(split[1]); //This is to avoid adding two lines for the same skill. [Skotlex] + for(j = 0; j < MAX_SKILL_TREE && hskill_tree[l][j].id && hskill_tree[l][j].id != k; j++); + if (j == MAX_SKILL_TREE) + { + ShowWarning("Unable to load skill %d into homunculus %d's tree. Maximum number of skills per class has been reached.\n", k, l); + continue; + } + hskill_tree[l][j].id=k; + hskill_tree[l][j].max=atoi(split[2]); + if (f) hskill_tree[l][j].joblv=atoi(split[3]); + + for(k=0;k<5;k++){ + hskill_tree[l][j].need[k].id=atoi(split[k*2+m]); + hskill_tree[l][j].need[k].lv=atoi(split[k*2+m+1]); + } + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","homun_skill_tree.txt"); + + return 0; } diff --git a/src/map/mercenary.h b/src/map/mercenary.h index 84b578245..b55cba8e1 100644 --- a/src/map/mercenary.h +++ b/src/map/mercenary.h @@ -1,13 +1,78 @@ // Homunculus and future Mercenary system code go here [Celest] +// implemented by [orn] +struct homunculus_db { + int class_ ; + char name[NAME_LENGTH]; + int basemaxHP ; + int basemaxSP ; + int baseSTR ; + int baseAGI ; + int baseVIT ; + int baseINT ; + int baseDEX ; + int baseLUK ; + int foodID ; + int baseIntimacy ; + short baseHungry ; + long hungryDelay ; + int gminHP ; + int gmaxHP ; + int gminSP ; + int gmaxSP ; + int gminSTR ; + int gmaxSTR ; + int gminAGI ; + int gmaxAGI ; + int gminVIT ; + int gmaxVIT ; + int gminINT ; + int gmaxINT ; + int gminDEX ; + int gmaxDEX ; + int gminLUK ; + int gmaxLUK ; + int evo_class ; + int baseASPD ; + //short size ; + //short race ; + //short element ; + unsigned char element, race, size; // albator + int accessID ; +}; +extern struct homunculus_db homuncumlus_db[MAX_HOMUNCULUS_CLASS]; +enum { HOMUNCULUS_CLASS, HOMUNCULUS_FOOD }; +enum { + SP_ACK = 0x00, + SP_INTIMATE = 0x100, + SP_HUNGRY = 0x200 +}; int do_init_merc (void); +int merc_hom_recv_data(int account_id, struct s_homunculus *sh, int flag); //albator +void merc_load_sub(struct homun_data *hd, struct map_session_data *sd); void merc_load_exptables(void); -char *merc_skill_get_name(int id); +char *merc_hom_skill_get_name(int id); void merc_damage(struct homun_data *hd,struct block_list *src,int hp,int sp); -int merc_dead(struct homun_data *hd, struct block_list *src); -void merc_skillup(struct map_session_data *sd,short skillnum); -int merc_gainexp(struct homun_data *hd,int exp); -void merc_heal(struct homun_data *hd,int hp,int sp); +int merc_hom_dead(struct homun_data *hd, struct block_list *src); +void merc_hom_skillup(struct homun_data *hd,int skillnum); +int merc_hom_calc_skilltree(struct map_session_data *sd) ; +int merc_hom_checkskill(struct map_session_data *sd,int skill_id) ; +int merc_hom_gainexp(struct homun_data *hd,int exp) ; +int merc_hom_levelup(struct homun_data *hd) ; +int merc_hom_evolution(struct homun_data *hd) ; +int merc_hom_heal(struct homun_data *hd,int hp,int sp); +int merc_hom_delete(struct homun_data *hd, int flag) ; +int merc_hom_revive(struct map_session_data *sd, int per); void merc_save(struct homun_data *hd); -void merc_load(struct map_session_data *sd); -int merc_create_homunculus(struct map_session_data *sd,int id,int m,int x,int y); +int merc_call_homunculus(struct map_session_data *sd); +int merc_create_homunculus(struct map_session_data *sd, int class_); +int search_homunculusDB_index(int key,int type); +int merc_menu(struct map_session_data *sd,int menunum); +int merc_hom_food(struct map_session_data *sd, struct homun_data *hd); +int merc_hom_hungry_timer_delete(struct homun_data *hd); +int merc_natural_heal_timer_delete(struct homun_data *hd); +#define merc_checkoverhp(hd) (hd->battle_status.hp == hd->battle_status.max_hp) +#define merc_checkoversp(hd) (hd->battle_status.sp == hd->battle_status.max_sp) +#define merc_stop_walking(hd, type) { if((hd)->ud.walktimer != -1) unit_stop_walking(&(hd)->bl, type); } +#define merc_stop_attack(hd) { if((hd)->ud.attacktimer != -1) unit_stop_attack(&(hd)->bl); hd->ud.target = 0; } +int read_homunculusdb(void); diff --git a/src/map/mob.c b/src/map/mob.c index 147b424cd..3e425f595 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -22,6 +22,7 @@ #include "pc.h" #include "status.h" #include "mob.h" +#include "mercenary.h" //[orn] #include "guild.h" #include "itemdb.h" #include "skill.h" @@ -761,6 +762,7 @@ int mob_target(struct mob_data *md,struct block_list *bl,int dist) */ static int mob_ai_sub_hard_activesearch(struct block_list *bl,va_list ap) { + struct map_session_data *sd; struct mob_data *md; struct block_list **target; int dist; @@ -783,9 +785,15 @@ static int mob_ai_sub_hard_activesearch(struct block_list *bl,va_list ap) switch (bl->type) { case BL_PC: + { if (((TBL_PC*)bl)->state.gangsterparadise && !(status_get_mode(&md->bl)&MD_BOSS)) return 0; //Gangster paradise protection. + sd = (TBL_PC*)bl; //[orn] monster target homunculus while hunting + if (sd->hd && sd->homunculus.alive && (distance_bl(&md->bl, &sd->hd->bl ) < md->db->range2 ) ) // + return 0; //Gangster paradise protection. + } + case BL_HOMUNCULUS: //[orn] case BL_MOB: if((dist=distance_bl(&md->bl, bl)) < md->db->range2 && ((*target) == NULL || !check_distance_bl(&md->bl, *target, dist)) && @@ -824,6 +832,7 @@ static int mob_ai_sub_hard_changechase(struct block_list *bl,va_list ap) switch (bl->type) { case BL_PC: + case BL_HOMUNCULUS: //[orn] case BL_MOB: if(check_distance_bl(&md->bl, bl, md->status.rhw.range) && battle_check_range (&md->bl, bl, md->status.rhw.range) @@ -1167,13 +1176,13 @@ static int mob_ai_sub_hard(struct block_list *bl,va_list ap) (mode&MD_ANGRY && md->state.skillstate == MSS_FOLLOW) ) { map_foreachinrange (mob_ai_sub_hard_activesearch, &md->bl, - view_range, md->special_state.ai?BL_CHAR:BL_PC, md, &tbl); + view_range, md->special_state.ai?BL_CHAR:BL_PC|BL_HOMUNCULUS, md, &tbl); //[orn] if(!tbl && mode&MD_ANGRY && !md->state.aggressive) md->state.aggressive = 1; //Restore angry state when no targets are visible. } else if (mode&MD_CHANGECHASE && (md->state.skillstate == MSS_RUSH || md->state.skillstate == MSS_FOLLOW)) { search_size = view_rangestatus.rhw.range ? view_range:md->status.rhw.range; map_foreachinrange (mob_ai_sub_hard_changechase, &md->bl, - search_size, (md->special_state.ai?BL_CHAR:BL_PC), md, &tbl); + search_size, (md->special_state.ai?BL_CHAR:BL_PC|BL_HOMUNCULUS), md, &tbl); //[orn] } if (tbl) @@ -1596,7 +1605,16 @@ void mob_damage(struct mob_data *md, struct block_list *src, int damage) case BL_PC: { struct map_session_data *sd = (TBL_PC*)src; - id = sd->status.char_id; +// id = sd->status.char_id; + id = sd->bl.id; //[orn] + if(rand()%1000 < 1000/md->attacked_players) + md->attacked_id = src->id; + break; + } + case BL_HOMUNCULUS: //[orn] + { + struct homun_data *hd = (TBL_HOMUNCULUS*)src; + id = hd->bl.id; if(rand()%1000 < 1000/md->attacked_players) md->attacked_id = src->id; break; @@ -1605,7 +1623,8 @@ void mob_damage(struct mob_data *md, struct block_list *src, int damage) { struct pet_data *pd = (TBL_PET*)src; if (battle_config.pet_attack_exp_to_master) { - id = pd->msd->status.char_id; +// id = pd->msd->status.char_id; + id = pd->msd->bl.id; //[orn] damage=(damage*battle_config.pet_attack_exp_rate)/100; //Modify logged damage accordingly. } //Let mobs retaliate against the pet's master [Skotlex] @@ -1618,7 +1637,8 @@ void mob_damage(struct mob_data *md, struct block_list *src, int damage) struct mob_data* md2 = (TBL_MOB*)src; if(md2->special_state.ai && md2->master_id) { struct map_session_data* msd = map_id2sd(md2->master_id); - if (msd) id = msd->status.char_id; +// if (msd) id = msd->status.char_id; + if (msd) id = msd->bl.id; //[orn] } if(rand()%1000 < 1000/md->attacked_players) { //Let players decide whether to retaliate versus the master or the mob. [Skotlex] @@ -1670,8 +1690,9 @@ void mob_damage(struct mob_data *md, struct block_list *src, int damage) int mob_dead(struct mob_data *md, struct block_list *src, int type) { struct status_data *status; - struct map_session_data *sd = NULL,*tmpsd[DAMAGELOG_SIZE], + struct map_session_data *sd = NULL,/**tmpsd[DAMAGELOG_SIZE],*/ *mvp_sd = NULL, *second_sd = NULL,*third_sd = NULL; + struct block_list *tmpbl[DAMAGELOG_SIZE] ; //[orn] struct { struct party_data *p; @@ -1706,7 +1727,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) map_freeblock_lock(); - memset(tmpsd,0,sizeof(tmpsd)); + memset(tmpbl,0,sizeof(tmpbl)); memset(pt,0,sizeof(pt)); if(src && src->type == BL_MOB) @@ -1735,16 +1756,19 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) for(temp=0,i=0,mvp_damage=0;idmglog[i].id;i++) { - tmpsd[temp] = map_charid2sd(md->dmglog[i].id); - if(tmpsd[temp] == NULL) + tmpbl[temp] = (struct block_list*)map_id2bl(md->dmglog[i].id); + if(tmpbl[temp] == NULL) continue; - if(tmpsd[temp]->bl.m != md->bl.m || pc_isdead(tmpsd[temp])) + if( (tmpbl[temp])->m != md->bl.m || status_isdead(tmpbl[temp])) continue; if(mvp_damage<(unsigned int)md->dmglog[i].dmg){ third_sd = second_sd; second_sd = mvp_sd; - mvp_sd=tmpsd[temp]; + if ( (tmpbl[temp])->type == BL_HOMUNCULUS ) { + mvp_sd = (struct map_session_data *) ((struct homun_data *)tmpbl[temp])->master ; + } else + mvp_sd=(struct map_session_data *)tmpbl[temp]; mvp_damage=md->dmglog[i].dmg; } @@ -1758,7 +1782,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) (!map[md->bl.m].flag.nobaseexp || !map[md->bl.m].flag.nojobexp) //Gives Exp ) { //Experience calculation. - for(i=0;istatus.party_id)>0) + if( (tmpbl[i]->type == BL_PC) && (temp = ((struct map_session_data *)tmpbl[i])->status.party_id )>0 ) //only pc have party [orn] { int j; for(j=0;jbl, base_exp,job_exp); - if(zeny) // zeny from mobs [Valaris] - pc_getzeny(tmpsd[i], zeny); + if(flag) { //homunculus aren't considered in party [orn] + switch( (tmpbl[i])->type ) { + case BL_PC: + if(base_exp || job_exp) + pc_gainexp((struct map_session_data *)tmpbl[i], &md->bl, base_exp,job_exp); + if(zeny) // zeny from mobs [Valaris] + pc_getzeny((struct map_session_data *)tmpbl[i], zeny); + break ; + case BL_HOMUNCULUS: + if(base_exp) + merc_hom_gainexp((struct homun_data *)tmpbl[i], base_exp); + if(zeny) //homunculus give zeny to master + pc_getzeny((struct map_session_data *)((struct homun_data *)tmpbl[i])->master, zeny); + break ; + + } } } diff --git a/src/map/pc.c b/src/map/pc.c index edd95ae8a..dc1ecbde7 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -24,6 +24,7 @@ #include "npc.h" #include "mob.h" #include "pet.h" +#include "mercenary.h" //orn #include "itemdb.h" #include "script.h" #include "battle.h" @@ -676,6 +677,10 @@ int pc_authok(struct map_session_data *sd, int login_id2, time_t connect_until_t if (sd->status.pet_id > 0) intif_request_petdata(sd->status.account_id, sd->status.char_id, sd->status.pet_id); + // Homunculus [albator] + if (sd->status.hom_id > 0) + intif_homunculus_requestload(sd->status.account_id, sd->status.hom_id); + // パ?ティ、ギルドデ?タの要求 if (sd->status.party_id > 0 && party_search(sd->status.party_id) == NULL) party_request_info(sd->status.party_id); @@ -3257,6 +3262,10 @@ int pc_setpos(struct map_session_data *sd,unsigned short mapindex,int x,int y,in unit_remove_map(&sd->pd->bl, clrtype); intif_save_petdata(sd->status.account_id,&sd->pet); } + if(sd->status.hom_id > 0 && sd->hd) { //orn + unit_remove_map(&sd->hd->bl, clrtype); + intif_homunculus_requestsave(sd->status.account_id, &sd->homunculus); + } chrif_save(sd,2); chrif_changemapserver(sd, mapindex, x, y, ip, (short)port); return 0; @@ -3289,6 +3298,8 @@ int pc_setpos(struct map_session_data *sd,unsigned short mapindex,int x,int y,in unit_remove_map(&sd->bl, clrtype); if(sd->status.pet_id > 0 && sd->pd) unit_remove_map(&sd->pd->bl, clrtype); + if(sd->status.hom_id > 0 && sd->hd) //orn + unit_remove_map(&sd->hd->bl, clrtype); clif_changemap(sd,map[m].index,x,y); // [MouseJstr] } else if(sd->state.auth) //Tag player for rewarping after map-loading is done. [Skotlex] @@ -3306,6 +3317,13 @@ int pc_setpos(struct map_session_data *sd,unsigned short mapindex,int x,int y,in sd->pd->ud.dir = sd->ud.dir; } + if(sd->status.hom_id > 0 && sd->hd ) { //orn + sd->hd->bl.m = m; + sd->hd->bl.x = sd->hd->ud.to_x = x; + sd->hd->bl.y = sd->hd->ud.to_y = y; + sd->hd->ud.dir = sd->ud.dir; + } + return 0; } @@ -4717,6 +4735,14 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) pet_unlocktarget(sd->pd); } + if(sd->status.hom_id > 0 && sd->hd) //orn + { + merc_stop_walking(sd->hd, 1) ; + merc_stop_attack(sd->hd) ; + merc_hom_delete(sd->hd,0); + } + + // Leave duel if you die [LuzZza] if(battle_config.duel_autoleave_when_die) { if(sd->duel_group > 0) diff --git a/src/map/script.c b/src/map/script.c index 338a987a1..dbcf96be9 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -36,6 +36,7 @@ #include "mob.h" #include "npc.h" #include "pet.h" +#include "mercenary.h" //[orn] #include "intif.h" #include "skill.h" #include "chat.h" @@ -441,6 +442,7 @@ int buildin_getvariableofnpc(struct script_state *st); // [blackhole89] --> int buildin_warpportal(struct script_state *st); // <-- [blackhole89] +int buildin_homunculus_evolution(struct script_state *st) ; //[orn] void push_val(struct script_stack *stack,int type,int val); int run_func(struct script_state *st); @@ -785,6 +787,7 @@ struct { // [blackhole89] --> {buildin_warpportal,"warpportal","iisii"}, // <--- [blackhole89] + {buildin_homunculus_evolution,"homevolution",""}, //[orn] {NULL,NULL,NULL}, }; @@ -6516,6 +6519,21 @@ int buildin_catchpet(struct script_state *st) return 0; } +/*========================================== + * [orn] + *------------------------------------------ + */ +int buildin_homunculus_evolution(struct script_state *st) +{ + struct map_session_data *sd; + sd=script_rid2sd(st); + if ( sd->hd && sd->hd->homunculusDB->evo_class && sd->homunculus.intimacy > 91000 ) { + return merc_hom_evolution(sd->hd) ; + } + clif_emotion(&sd->hd->bl, 4) ; //swt + return 0; +} + /*========================================== *携帯卵孵化機使用 *------------------------------------------ @@ -10388,7 +10406,7 @@ int buildin_rid2name(struct script_state *st){ push_str(st->stack,C_CONSTSTR,((struct pet_data *)bl)->name); break; case BL_HOMUNCULUS: - push_str(st->stack,C_CONSTSTR,((struct homun_data *)bl)->name); + push_str(st->stack,C_CONSTSTR,((struct homun_data *)bl)->master->homunculus.name); break; default: ShowError("buildin_rid2name: BL type unknown.\n"); @@ -10810,7 +10828,7 @@ int buildin_unittalk(struct script_state *st) memcpy(message, ((TBL_NPC *)bl)->name, NAME_LENGTH); break; case BL_HOMUNCULUS: - memcpy(message, ((TBL_HOMUNCULUS *)bl)->name, NAME_LENGTH); + memcpy(message, ((TBL_HOMUNCULUS *)bl)->master->homunculus.name, NAME_LENGTH); break; case BL_PET: memcpy(message, ((TBL_PET *)bl)->name, NAME_LENGTH); diff --git a/src/map/skill.c b/src/map/skill.c index be9343d7a..b501e9b64 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -20,6 +20,7 @@ #include "pc.h" #include "status.h" #include "pet.h" +#include "mercenary.h" //[orn] #include "mob.h" #include "battle.h" #include "party.h" @@ -706,6 +707,8 @@ const char* skill_get_name( int id ){ return "UNKNOWN_SKILL"; if (id >= GD_SKILLBASE) id = GD_SKILLRANGEMIN + id - GD_SKILLBASE; + if (id >= HM_SKILLBASE) //[orn] + id = HM_SKILLRANGEMIN + id - HM_SKILLBASE; if (id < 1 || id > MAX_SKILL_DB || skill_db[id].name==NULL) return "UNKNOWN_SKILL"; //Can't use skill_chk because we return a string. return skill_db[id].name; @@ -809,6 +812,8 @@ int skill_calc_heal (struct block_list *bl, int skill_lv) if(bl->type == BL_PC && (skill = pc_checkskill((TBL_PC*)bl, HP_MEDITATIO)) > 0) heal += heal * skill * 2 / 100; + if(bl->type == BL_HOMUNCULUS && (skill = merc_hom_checkskill( ((TBL_HOMUNCULUS*)bl)->master, HLIF_BRAIN)) > 0) //[orn] + heal += heal * skill * 2 / 100; return heal; } @@ -846,6 +851,8 @@ int skillnotok (int skillid, struct map_session_data *sd) if (i >= GD_SKILLBASE) i = GD_SKILLRANGEMIN + i - GD_SKILLBASE; + if (i >= HM_SKILLBASE) //[orn] + i = HM_SKILLRANGEMIN + i - HM_SKILLBASE; if (i > MAX_SKILL || i < 0) return 1; @@ -919,6 +926,49 @@ int skillnotok (int skillid, struct map_session_data *sd) return (map[sd->bl.m].flag.noskill); } +// [orn] - skill ok to cast? and when? //homunculus +int skillnotok_hom (int skillid, struct homun_data *hd) +{ + int i = skillid; + nullpo_retr (1, hd); + //if (sd == 0) + //return 0; + //return 1; + // I think it was meant to be "no skills allowed when not a valid sd" + + if (skillid >= GD_SKILLRANGEMIN && skillid <= GD_SKILLRANGEMAX) + return 1; + + if (i >= GD_SKILLBASE) + i = GD_SKILLRANGEMIN + i - GD_SKILLBASE; + if (i >= HM_SKILLBASE) //[orn] + i = HM_SKILLRANGEMIN + i - HM_SKILLBASE; + + if (i > MAX_SKILL || i < 0) + return 1; + + if (hd->blockskill[i] > 0) + return 1; + + // Check skill restrictions [Celest] + if(!map_flag_vs(hd->bl.m) && skill_get_nocast (skillid) & 1) + return 1; + if(map[hd->bl.m].flag.pvp) { + if(!battle_config.pk_mode && skill_get_nocast (skillid) & 2) + return 1; + if(battle_config.pk_mode && skill_get_nocast (skillid) & 16) + return 1; + } + if(map_flag_gvg(hd->bl.m) && skill_get_nocast (skillid) & 4) + return 1; + if(agit_flag && skill_get_nocast (skillid) & 8) + return 1; + if(map[hd->bl.m].flag.restricted && map[hd->bl.m].zone && skill_get_nocast (skillid) & (8*map[hd->bl.m].zone)) + return 1; + + return (map[hd->bl.m].flag.noskill); +} + /* 繧ケ繧ュ繝ォ繝ヲ繝九ャ繝医ョ驟咲スョ諠蝣ア繧定ソ斐☆ */ struct skill_unit_layout skill_unit_layout[MAX_SKILL_UNIT_LAYOUT]; int firewall_unit_pos; @@ -2263,6 +2313,99 @@ int skill_guildaura_sub (struct block_list *bl, va_list ap) return 0; } +/*========================================== + * [orn] + * Checks that you have the requirements for casting a skill for homunculus. + * Flag: + * &1: finished casting the skill (invoke hp/sp/item consumption) + * &2: picked menu entry (Warp Portal, Teleport and other menu based skills) + *------------------------------------------ + */ +static int skill_check_condition_hom (struct homun_data *hd, int skill, int lv, int type) +{ + struct status_data *status; + struct status_change *sc; + int j,hp,sp,hp_rate,sp_rate,state,mhp ; + + nullpo_retr(0, hd); + + if (lv <= 0) return 0; + + status = &hd->battle_status; + sc = &hd->sc; + if (!sc->count) + sc = NULL; + + // for the guild skills [celest] + if (skill >= HM_SKILLBASE) //[orn] + j = HM_SKILLRANGEMIN + skill - HM_SKILLBASE; + else + j = skill; + if (j < 0 || j >= MAX_SKILL_DB) + return 0; + //Code speedup, rather than using skill_get_* over and over again. + if (lv < 1 || lv > MAX_SKILL_LEVEL) + return 0; + hp = skill_db[j].hp[lv-1]; + sp = skill_db[j].sp[lv-1]; + hp_rate = skill_db[j].hp_rate[lv-1]; + sp_rate = skill_db[j].sp_rate[lv-1]; + state = skill_db[j].state; + mhp = skill_db[j].mhp[lv-1]; + if(mhp > 0) + hp += (status->max_hp * mhp)/100; + if(hp_rate > 0) + hp += (status->hp * hp_rate)/100; + else + hp += (status->max_hp * (-hp_rate))/100; + if(sp_rate > 0) + sp += (status->sp * sp_rate)/100; + else + sp += (status->max_sp * (-sp_rate))/100; + + switch(skill) { // Check for cost reductions due to skills & SCs + case HFLI_SBR44: + if(hd->master->homunculus.intimacy < 200) + return 0; + break; + case HVAN_EXPLOSION: + if(hd->master->homunculus.intimacy < battle_config.hvan_explosion_intimate) + return 0; + break; + } + if(!(type&2)){ + if( hp>0 && status->hp <= (unsigned int)hp) { + clif_skill_fail(hd->master,skill,2,0); + return 0; + } + if( sp>0 && status->sp < (unsigned int)sp) { + clif_skill_fail(hd->master,skill,1,0); + return 0; + } + } + + switch(state) { + case ST_MOVE_ENABLE: + //Check only on begin casting. [Skotlex] + if(!type && !unit_can_move(&hd->bl)) { + clif_skill_fail(hd->master,skill,0,0); + return 0; + } + break; + } + + if(!(type&1)) + return 1; + + if(type&2) + return 1; + + if(sp || hp) + status_zap(&hd->bl, hp, sp); + + return 1; +} + /*========================================================================= * 遽蝗イ繧ケ繧ュ繝ォ菴ソ逕ィ蜃ヲ逅蟆丞縺代%縺薙°繧 */ @@ -2454,6 +2597,7 @@ static int skill_reveal_trap (struct block_list *bl, va_list ap) int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int skillid, int skilllv, unsigned int tick, int flag) { struct map_session_data *sd = NULL, *tsd = NULL; + struct homun_data *hd = NULL ; //[orn] struct status_data *tstatus; struct status_change *sc; @@ -2472,6 +2616,8 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int sd = (struct map_session_data *)src; if (bl->type == BL_PC) tsd = (struct map_session_data *)bl; + if (bl->type == BL_HOMUNCULUS) //[orn] + hd = (struct homun_data *)bl; if (status_isdead(src) || (src != bl && status_isdead(bl))) return 1; @@ -2568,6 +2714,8 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int case TK_DOWNKICK: case TK_COUNTER: case ASC_BREAKER: + case HFLI_MOON: //[orn] + case HFLI_SBR44: //[orn] skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); break; @@ -2725,8 +2873,6 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int skill_castend_damage_id); } break; - - case SM_MAGNUM: if(flag&1) skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); @@ -2974,6 +3120,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int case TF_THROWSTONE: case NPC_SMOKING: case NPC_SELFDESTRUCTION: + case HVAN_EXPLOSION: //[orn] case GS_FLING: case NJ_ZENYNAGE: skill_attack(BF_MISC,src,src,bl,skillid,skilllv,tick,flag); @@ -3112,6 +3259,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, int skillid, int skilllv, unsigned int tick, int flag) { struct map_session_data *sd = NULL; + struct homun_data *hd = NULL; struct map_session_data *dstsd = NULL; struct status_data *sstatus, *tstatus; struct status_change *tsc; @@ -3129,6 +3277,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in if (src->type == BL_PC) { sd = (struct map_session_data *)src; + } else if (src->type == BL_HOMUNCULUS) { //[orn] + hd = (struct homun_data *)src; } else if (src->type == BL_MOB) { md = (struct mob_data *)src; } @@ -3151,7 +3301,12 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in //Check for undead skills that convert a no-damage skill into a damage one. [Skotlex] switch (skillid) { - case AL_HEAL: + case HLIF_HEAL: //[orn] + if ( !hd ) { + clif_skill_fail(hd->master,skillid,0,0) ; + break ; + } + case AL_HEAL: case ALL_RESURRECTION: case PR_ASPERSIO: if (battle_check_undead(tstatus->race,tstatus->def_ele)) { @@ -3184,6 +3339,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in map_freeblock_lock(); switch(skillid) { + case HLIF_HEAL: //[orn] case AL_HEAL: /* 繝偵シ繝ォ */ { int heal = skill_calc_heal(src, skilllv); @@ -3971,6 +4127,19 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in skill_castend_damage_id); status_damage(src, src, sstatus->max_hp,0,0,1); break; + case HVAN_EXPLOSION: //[orn] + ShowDebug("skill_castend_nodamage_id : intimacy = %d\n", hd->master->homunculus.intimacy) ; //ORN DEBUG + clif_skill_nodamage(src, src, skillid, -1, 1); + map_foreachinrange(skill_area_sub, bl, + skill_get_splash(skillid, skilllv), BL_CHAR, + src, skillid, skilllv, tick, flag|BCT_ENEMY, + skill_castend_damage_id); + if(hd){ + hd->master->homunculus.intimacy = 200; + clif_send_homdata(hd->master,0x100,hd->master->homunculus.intimacy/100); + } + status_damage(src, src, sstatus->max_hp,0,0,1); + break; /* 繝代シ繝繧」繧ケ繧ュ繝ォ */ case AL_ANGELUS: /* 繧ィ繝ウ繧ク繧ァ繝ゥ繧ケ */ @@ -5397,6 +5566,149 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in pc_delspiritball(sd,1,0); } break; + + case AM_CALLHOMUN: //[orn] + { + int i = 0; + if (sd && (sd->status.hom_id == 0 || sd->homunculus.vaporize == 1)) { + if (sd->status.hom_id == 0) { + i = pc_search_inventory(sd,7142); + if(i < 0) { + clif_skill_fail(sd,skillid,0,0); + break ; + } + pc_delitem(sd,i,1,0); + } + if (merc_call_homunculus(sd)) + break; + } + + clif_skill_fail(sd,skillid,0,0); + break; + } + case AM_REST: //[orn] + { + if (sd && sd->hd && ( sd->hd->battle_status.hp >= (sd->hd->battle_status.max_hp * 80 / 100 ) ) ) { + sd->homunculus.vaporize = 1; + merc_hom_delete(sd->hd, 0) ; + } else if ( sd ) + { + clif_skill_fail(sd,skillid,0,0); + } + + break; + } + case AM_RESURRECTHOMUN: //[orn] + { + if ( sd && sd->status.hom_id ) { + if( map_flag_gvg(bl->m) ) + { //No reviving in WoE grounds! + clif_skill_fail(sd,skillid,0,0); + break; + } + if ( sd->homunculus.alive == 0 ) { + int per = 10 * skilllv; + + if (merc_hom_revive(sd, per) ) + { + clif_skill_nodamage(src,&sd->hd->bl,AM_RESURRECTHOMUN,skilllv,1); + } else { + clif_skill_fail(sd,skillid,0,0); + } + } else { + clif_skill_fail(sd,skillid,0,0); + } + + } + break; + } + + case HAMI_CASTLE: //[orn] + { + if(hd && rand()%100 < 20*skilllv) + { + int x,y; + struct walkpath_data wpd; + struct map_session_data *sd = hd->master; + if( path_search(&wpd,hd->bl.m,hd->bl.x,hd->bl.y,sd->bl.x,sd->bl.y,0) != 0 ) { + clif_skill_fail(sd,skillid,0,0); + break; + } + + clif_skill_nodamage(&hd->bl,&sd->bl,skillid,skilllv,1); + + x = hd->bl.x; + y = hd->bl.y; + + unit_movepos(&hd->bl,sd->bl.x,sd->bl.y,0,0); + unit_movepos(&sd->bl,x,y,0,0); + clif_fixpos(&hd->bl) ; + clif_fixpos(&sd->bl) ; + + map_foreachinarea(skill_chastle_mob_changetarget,hd->bl.m, + hd->bl.x-AREA_SIZE,hd->bl.y-AREA_SIZE, + hd->bl.x+AREA_SIZE,hd->bl.y+AREA_SIZE, + BL_MOB,&hd->master->bl,&hd->bl); + } + } + break; + case HVAN_CHAOTIC: //[orn] + { + if(hd){ + //HOM,PC,MOB + struct block_list* heal_target=NULL; + int heal = skill_calc_heal( src, 1+rand()%skilllv ); + static const int per[10][2]={{20,50},{50,60},{25,75},{60,64},{34,67}, + {34,67},{34,67},{34,67},{34,67},{34,67}}; + int rnd = rand()%100; + if(rndbl; + }else if(rndmaster->bl)) + heal_target = &hd->master->bl; + else + heal_target = &hd->bl; + }else{//MOB + heal_target = map_id2bl(hd->target_id); + if(heal_target==NULL) + heal_target = &hd->bl; + } + clif_skill_nodamage(src,heal_target,AL_HEAL,heal,1); + clif_skill_nodamage(src,heal_target,skillid,heal,1); + status_heal(heal_target, heal, 0, 0); + skill_blockmerc_start(hd, skillid, skill_get_time2(skillid,skilllv)) ; + } + } + break; + case HLIF_AVOID: //[orn] + case HAMI_DEFENCE: //[orn] + if ( hd ) { + clif_skill_nodamage(src,&hd->master->bl,skillid,skilllv, + sc_start(&hd->master->bl,type,100,skilllv,skill_get_time(skillid,skilllv))) ; + } + case HAMI_BLOODLUST: //[orn] + case HFLI_FLEET: //[orn] + case HFLI_SPEED: //[orn] + if ( hd ) { + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(&hd->bl,type,100,skilllv,skill_get_time(skillid,skilllv))) ; + skill_blockmerc_start(hd, skillid, skill_get_time2(skillid,skilllv)) ; + } + else + clif_skill_fail(hd->master,skillid,0,0); + break; + case HLIF_CHANGE: //[orn] + if ( hd ) { + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(&hd->bl,type,100,skilllv,skill_get_time(skillid,skilllv))) ; + status_heal(&hd->bl, hd->master->homunculus.max_hp, 0, 0); + skill_blockmerc_start(hd, skillid, skill_get_time2(skillid,skilllv)) ; + } + else + clif_skill_fail(hd->master,skillid,0,0); + break; default: ShowWarning("skill_castend_nodamage_id: Unknown skill used:%d\n",skillid); @@ -5421,6 +5733,7 @@ int skill_castend_id (int tid, unsigned int tick, int id, int data) { struct block_list *target, *src = map_id2bl(id); struct map_session_data* sd = NULL; + struct homun_data* hd = NULL; //[orn] struct mob_data* md = NULL; struct unit_data* ud = unit_bl2ud(src); struct status_change *sc; @@ -5429,6 +5742,7 @@ int skill_castend_id (int tid, unsigned int tick, int id, int data) nullpo_retr(0, ud); BL_CAST( BL_PC, src, sd); + BL_CAST( BL_HOMUNCULUS, src, hd); //[orn] BL_CAST( BL_MOB, src, md); if( src->prev == NULL ) { @@ -5538,6 +5852,9 @@ int skill_castend_id (int tid, unsigned int tick, int id, int data) if(sd && !skill_check_condition(sd,ud->skillid, ud->skilllv,1)) /* 菴ソ逕ィ譚。莉カ繝√ぉ繝繧ッ */ break; + if(hd && !skill_check_condition_hom(hd,ud->skillid, ud->skilllv,1)) //[orn] + break; + if (ud->walktimer != -1 && ud->skillid != TK_RUN) unit_stop_walking(src,1); @@ -5585,12 +5902,14 @@ int skill_castend_pos (int tid, unsigned int tick, int id, int data) struct block_list* src = map_id2bl(id); int maxcount; struct map_session_data *sd = NULL; + struct homun_data *hd = NULL; //[orn] struct unit_data *ud = unit_bl2ud(src); struct mob_data *md = NULL; nullpo_retr(0, ud); BL_CAST( BL_PC , src, sd); + BL_CAST( BL_HOMUNCULUS , src, hd); //[orn] BL_CAST( BL_MOB, src, md); if( src->prev == NULL ) { @@ -5651,6 +5970,9 @@ int skill_castend_pos (int tid, unsigned int tick, int id, int data) if(sd && !skill_check_condition(sd,ud->skillid, ud->skilllv, 1)) /* 菴ソ逕ィ譚。莉カ繝√ぉ繝繧ッ */ break; + if(hd && !skill_check_condition_hom(hd,ud->skillid, ud->skilllv, 1)) //[orn] + break; + if(md) { md->last_thinktime=tick + (tid==-1?md->status.adelay:md->status.amotion); if(md->skillidx >= 0) { @@ -7586,6 +7908,8 @@ int skill_check_condition (struct map_session_data *sd, int skill, int lv, int t // for the guild skills [celest] if (skill >= GD_SKILLBASE) j = GD_SKILLRANGEMIN + skill - GD_SKILLBASE; + else if (skill >= HM_SKILLBASE) //[orn] + j = HM_SKILLRANGEMIN + skill - HM_SKILLBASE; else j = skill; if (j < 0 || j >= MAX_SKILL_DB) @@ -9034,6 +9358,23 @@ int skill_ganbatein (struct block_list *bl, va_list ap) return 1; } +/*========================================== + * キャスリングのターゲット変更 + *------------------------------------------ + */ +int skill_chastle_mob_changetarget(struct block_list *bl,va_list ap) +{ + struct mob_data* md; + struct block_list *from_bl; + struct block_list *to_bl; + nullpo_retr(0, md = (struct mob_data*)bl); + nullpo_retr(0, from_bl = va_arg(ap,struct block_list *)); + nullpo_retr(0, to_bl = va_arg(ap,struct block_list *)); + if(md->target_id == from_bl->id) + md->target_id = to_bl->id; + return 0; +} + /*========================================== * 謖螳夂ッ蝗イ蜀縺ァsrc縺ォ蟇セ縺励※譛牙柑縺ェ繧ソ繝シ繧イ繝繝医ョbl縺ョ謨ー繧呈焚縺医k(foreachinarea) *------------------------------------------ @@ -10303,6 +10644,8 @@ int skill_blockpc_start(struct map_session_data *sd, int skillid, int tick) if (skillid >= GD_SKILLBASE) skillid = GD_SKILLRANGEMIN + skillid - GD_SKILLBASE; + if (skillid >= HM_SKILLBASE) //[orn] + skillid = HM_SKILLRANGEMIN + skillid - HM_SKILLBASE; if (skillid < 1 || skillid > MAX_SKILL) return -1; @@ -10310,6 +10653,31 @@ int skill_blockpc_start(struct map_session_data *sd, int skillid, int tick) return add_timer(gettick()+tick,skill_blockpc_end,sd->bl.id,skillid); } +int skill_blockmerc_end (int tid, unsigned int tick, int id, int data) //[orn] +{ + struct homun_data *hd = (TBL_HOMUNCULUS*) map_id2bl(id); + if (data <= 0 || data >= MAX_SKILL) + return 0; + if (hd) hd->blockskill[data] = 0; + + return 1; +} + +int skill_blockmerc_start(struct homun_data *hd, int skillid, int tick) //[orn] +{ + nullpo_retr (-1, hd); + + if (skillid >= GD_SKILLBASE) + skillid = GD_SKILLRANGEMIN + skillid - GD_SKILLBASE; + if (skillid >= HM_SKILLBASE) //[orn] + skillid = HM_SKILLRANGEMIN + skillid - HM_SKILLBASE; + if (skillid < 1 || skillid > MAX_SKILL) + return -1; + + hd->blockskill[skillid] = 1; + return add_timer(gettick()+tick,skill_blockmerc_end,hd->bl.id,skillid); +} + /*---------------------------------------------------------------------------- * 蛻晄悄蛹也ウサ @@ -10641,6 +11009,8 @@ int skill_readdb (void) } if (i >= GD_SKILLBASE) i = GD_SKILLRANGEMIN + i - GD_SKILLBASE; + if (i >= HM_SKILLBASE) //[orn] + i = HM_SKILLRANGEMIN + i - HM_SKILLBASE; if(i<=0 || i>MAX_SKILL_DB) continue; @@ -10697,6 +11067,8 @@ int skill_readdb (void) i=atoi(split[0]); if (i >= GD_SKILLBASE) i = GD_SKILLRANGEMIN + i - GD_SKILLBASE; + if (i >= HM_SKILLBASE) //[orn] + i = HM_SKILLRANGEMIN + i - HM_SKILLBASE; if(i<=0 || i>MAX_SKILL_DB) continue; @@ -10784,6 +11156,8 @@ int skill_readdb (void) i=atoi(split[0]); if (i >= GD_SKILLBASE) i = GD_SKILLRANGEMIN + i - GD_SKILLBASE; + if (i >= HM_SKILLBASE) //[orn] + i = HM_SKILLRANGEMIN + i - HM_SKILLBASE; if(i<=0 || i>MAX_SKILL_DB) continue; @@ -10816,6 +11190,8 @@ int skill_readdb (void) i=atoi(split[0]); if (i >= GD_SKILLBASE) i = GD_SKILLRANGEMIN + i - GD_SKILLBASE; + if (i >= HM_SKILLBASE) //[orn] + i = HM_SKILLRANGEMIN + i - HM_SKILLBASE; if(i<=0 || i>MAX_SKILL_DB) continue; skill_db[i].unit_id[0] = strtol(split[1],NULL,16); diff --git a/src/map/skill.h b/src/map/skill.h index accc6dfdf..48e88033d 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -237,6 +237,8 @@ int skill_check_cloaking(struct block_list *bl, struct status_change *sc); // ステ?タス異常 int skill_enchant_elemental_end(struct block_list *bl, int type); int skillnotok(int skillid, struct map_session_data *sd); +int skillnotok_hom (int skillid, struct homun_data *hd) ; //[orn] +int skill_chastle_mob_changetarget(struct block_list *bl,va_list ap); //[orn] // アイテム作成 int skill_can_produce_mix( struct map_session_data *sd, int nameid, int trigger, int qty); @@ -250,6 +252,7 @@ int skill_castend_nodamage_id( struct block_list *src, struct block_list *bl,int int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag ); int skill_castend_pos2( struct block_list *src, int x,int y,int skillid,int skilllv,unsigned int tick,int flag); int skill_blockpc_start (struct map_session_data*,int,int); // [celest] +int skill_blockmerc_start (struct homun_data*,int,int); //[orn] // スキル攻?一括?理 int skill_attack( int attack_type, struct block_list* src, struct block_list *dsrc, diff --git a/src/map/status.c b/src/map/status.c index a349bf08e..09aed9ced 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -54,13 +54,14 @@ int current_equip_item_index; //Contains inventory index of an equipped item. To int current_equip_card_id; //To prevent card-stacking (from jA) [Skotlex] //we need it for new cards 15 Feb 2005, to check if the combo cards are insrerted into the CURRENT weapon only //to avoid cards exploits +void status_calc_bl_sub_hom(struct homun_data *hd, unsigned long flag); //[orn] static void add_sc(int skill, int sc) { int sk = skill; if (sk > GD_SKILLBASE) sk = skill - GD_SKILLBASE + SC_GD_BASE; else - if (sk > HM_SKILLBASE) sk = skill - HM_SKILLBASE + SC_HM_BASE; + if (sk >= HM_SKILLBASE) sk = skill - HM_SKILLBASE + SC_HM_BASE; if (sk < 0 || sk >= MAX_SKILL) { if (battle_config.error_log) ShowError("add_sc: Unsupported skill id %d\n", skill); @@ -374,6 +375,8 @@ void initChangeTables(void) { set_sc(HLIF_CHANGE, SC_CHANGE, SI_BLANK, SCB_INT); set_sc(HAMI_BLOODLUST, SC_BLOODLUST, SI_BLANK, SCB_BATK|SCB_WATK); set_sc(HFLI_FLEET, SC_FLEET, SI_BLANK, SCB_ASPD|SCB_BATK|SCB_WATK); + set_sc(HFLI_SPEED, SC_SPEED, SI_BLANK, SCB_FLEE); //[orn] + set_sc(HAMI_DEFENCE, SC_DEFENCE, SI_BLANK, SCB_DEF); //[orn] set_sc(GD_LEADERSHIP, SC_GUILDAURA, SI_GUILDAURA, SCB_STR|SCB_AGI|SCB_VIT|SCB_DEX); set_sc(GD_BATTLEORDER, SC_BATTLEORDERS, SI_BATTLEORDERS, SCB_STR|SCB_INT|SCB_DEX); @@ -452,7 +455,7 @@ int SkillStatusChangeTable(int skill) int sk = skill; if (sk > GD_SKILLBASE) sk = skill - GD_SKILLBASE + SC_GD_BASE; else - if (sk > HM_SKILLBASE) sk = skill - HM_SKILLBASE + SC_HM_BASE; + if (sk >= HM_SKILLBASE) sk = skill - HM_SKILLBASE + SC_HM_BASE; if (sk < 0 || sk >= MAX_SKILL) { if (battle_config.error_log) ShowError("add_sc: Unsupported skill id %d\n", skill); @@ -501,8 +504,8 @@ int status_damage(struct block_list *src,struct block_list *target,int hp, int s struct status_data *status; struct status_change *sc; - if(sp && target->type != BL_PC) - sp = 0; //Only players get SP damage. + if(sp && target->type != BL_PC && target->type != BL_HOMUNCULUS) //[orn] + sp = 0; //Only players and Homunculus get SP damage. if (hp < 0) { //Assume absorbed damage. status_heal(target, -hp, 0, 1); @@ -641,7 +644,7 @@ int status_damage(struct block_list *src,struct block_list *target,int hp, int s flag = pc_dead((TBL_PC*)target,src); break; case BL_HOMUNCULUS: - flag = merc_dead((TBL_HOMUNCULUS*)target,src); + flag = merc_hom_dead((TBL_HOMUNCULUS*)target,src); break; default: //Unhandled case, do nothing to object. flag = 0; @@ -738,7 +741,7 @@ int status_heal(struct block_list *bl,int hp,int sp, int flag) pc_heal((TBL_PC*)bl,hp,sp,flag&2?1:0); break; case BL_HOMUNCULUS: - merc_heal((TBL_HOMUNCULUS*)bl,hp,sp); + merc_hom_heal((TBL_HOMUNCULUS*)bl,hp,sp); break; } return hp+sp; @@ -819,7 +822,9 @@ int status_revive(struct block_list *bl, unsigned char per_hp, unsigned char per break; case BL_PC: pc_revive((TBL_PC*)bl, hp, sp); - break; +// case BL_HOMUNCULUS: //[orn] +// merc_hom_revive((TBL_HOMUNCULUS*)bl, hp, sp); +// break; } return 1; } @@ -1360,97 +1365,6 @@ int status_calc_pet(struct pet_data *pd, int first) return 1; } -int status_calc_homunculus(struct homun_data *hd, int first) -{ - struct status_data *status = &hd->base_status; - int lv, i; - /* very proprietary */ - lv=hd->level; - memset(status, 0, sizeof(struct status_data)); - switch(hd->class_) - { - case 6001: //LIF ~ int,dex,vit - status->str = 3+lv/7; - status->agi = 3+2*lv/5; - status->vit = 4+lv; - status->int_ = 4+3*lv/4; - status->dex = 4+2*lv/3; - status->luk = 3+lv/4; - for(i=8001;i<8005;++i) - { - hd->hskill[i-8001].id=i; - //hd->hskill[i-8001].level=1; - } - break; - case 6003: //FILIR ~ str,agi,dex - status->str = 4+3*lv/4; - status->agi = 4+2*lv/3; - status->vit = 3+2*lv/5; - status->int_ = 3+lv/4; - status->dex = 4+lv; - status->luk = 3+lv/7; - for(i=8009;i<8013;++i) - { - hd->hskill[i-8009].id=i; - //hd->hskill[i-8009].level=1; - } - break; - case 6002: //AMISTR ~ str,vit,luk - status->str = 4+lv; - status->agi = 3+lv/4; - status->vit = 3+3*lv/4; - status->int_ = 3+lv/10; - status->dex = 3+2*lv/5; - status->luk = 4+2*lv/3; - for(i=8005;i<8009;++i) - { - hd->hskill[i-8005].id=i; - //hd->hskill[i-8005].level=1; - } - break; - case 6004: //VANILMIRTH ~ int,dex,luk - status->str = 3+lv/4; - status->agi = 3+lv/7; - status->vit = 3+2*lv/5; - status->int_ = 4+lv; - status->dex = 4+2*lv/3; - status->luk = 4+3*lv/4; - for(i=8013;i<8017;++i) - { - hd->hskill[i-8013].id=i; - //hd->hskill[i-8013].level=1; - } - break; - default: - if (battle_config.error_log) - ShowError("status_calc_homun: Unknown class %d\n", hd->class_); - memcpy(status, &dummy_status, sizeof(struct status_data)); - break; - } - status->hp = 10; //Revive HP/SP? - status->sp = 0; - status->max_hp=500+lv*10+lv*lv; - status->max_sp=300+lv*11+lv*lv*90/100; - status->aspd_rate = 1000; - status->speed=0x96; - status->batk = status_base_atk(&hd->bl, status); - status_calc_misc(status, hd->level); - - // hp recovery - hd->regenhp = 1 + (status->vit/5) + (status->max_hp/200); - - // sp recovery - hd->regensp = 1 + (status->int_/6) + (status->max_sp/100); - if(status->int_ >= 120) - hd->regensp += ((status->int_-120)>>1) + 4; - - status->amotion = 1800 - (1800 * status->agi / 250 + 1800 * status->dex / 1000); - status->amotion -= 200; - status->dmotion=status->amotion; - status_calc_bl(&hd->bl, SCB_ALL); - return 1; -} - static unsigned int status_base_pc_maxhp(struct map_session_data* sd, struct status_data *status) { unsigned int val; @@ -2230,6 +2144,59 @@ int status_calc_pc(struct map_session_data* sd,int first) return 0; } +int status_calc_homunculus(struct homun_data *hd, int first) +{ + struct status_data b_status, *status; + memcpy(&b_status, &hd->base_status, sizeof(struct status_data)); + status = &hd->base_status; + + status->def_ele = b_status.def_ele = hd->homunculusDB->element ; //[orn] + status->ele_lv = b_status.ele_lv = 1 ; //[orn] + status->race = b_status.race = hd->homunculusDB->race ; //[orn] + status->size = b_status.size = hd->homunculusDB->size ; //[orn] + status->rhw.range = b_status.rhw.range = 1 + hd->homunculusDB->size ; //[orn] + status->mode = b_status.mode = MD_CANMOVE|MD_CANATTACK|MD_ASSIST|MD_AGGRESSIVE|MD_CASTSENSOR; //[orn] + status->speed = b_status.speed = DEFAULT_WALK_SPEED; + status->aspd_rate = b_status.aspd_rate = 1000; + + merc_hom_calc_skilltree(hd->master); // + + status_cpy(&b_status, status); + status_calc_misc(status, hd->master->homunculus.level); + status_calc_bl(&hd->bl, SCB_ALL); //Status related changes. + + if ( (b_status.str != status->str) || + (b_status.agi != status->agi) || + (b_status.vit != status->vit) || + (b_status.int_ != status->int_) || + (b_status.dex != status->dex) || + (b_status.luk != status->luk) || + (b_status.hit != status->hit) || + (b_status.flee != status->flee) || + (b_status.amotion != status->amotion) || + (b_status.rhw.atk != status->rhw.atk) || + (b_status.def != status->def) || + (b_status.rhw.atk2 != status->rhw.atk2) || + (b_status.def2 != status->def2) || + (b_status.flee2 != status->flee2) || + (b_status.cri != status->cri) || + (b_status.matk_max != status->matk_max) || + (b_status.matk_min != status->matk_min) || + (b_status.mdef != status->mdef) || + (b_status.mdef2 != status->mdef2) || + (b_status.rhw.range != status->rhw.range) || + (b_status.max_hp != status->max_hp) || + (b_status.max_sp != status->max_sp) || + (b_status.hp != status->hp) || + (b_status.sp != status->sp) + ) + { + clif_hominfo(hd->master,0) ; + } + + return 1; +} + static unsigned short status_calc_str(struct block_list *,struct status_change *,int); static unsigned short status_calc_agi(struct block_list *,struct status_change *,int); static unsigned short status_calc_vit(struct block_list *,struct status_change *,int); @@ -2552,12 +2519,210 @@ void status_calc_bl_sub_pc(struct map_session_data *sd, unsigned long flag) clif_updatestatus(sd,SP_MAXSP); } +//Calculates some attributes that depends on modified stats from status changes. +void status_calc_bl_sub_hom(struct homun_data *hd, unsigned long flag) //[orn] +{ + struct status_data *status = &hd->battle_status, *b_status = &hd->base_status; + int skill = 0; + + if(flag&(SCB_MAXHP|SCB_VIT)) + { + flag|=SCB_MAXHP; //Ensures client-side refresh + + // Apply relative modifiers from equipment + if(status->max_hp > (unsigned int)battle_config.max_hp) + status->max_hp = battle_config.max_hp; + else if(!status->max_hp) + status->max_hp = 1; + + // hp recovery + hd->regenhp = 1 + (status->vit/5) + (status->max_hp/200); + + if(hd->regenhp < 1) hd->regenhp = 1; + + // Skill-related Adamantium Skin + if((skill=merc_hom_checkskill(hd->master,HAMI_SKIN)) > 0) { + status->max_hp = hd->master->homunculus.max_hp + skill * 2 * hd->master->homunculus.max_hp / 100 ; + hd->regenhp += skill * 5 * hd->regenhp / 100 ; + } + + status->max_hp = status_calc_maxhp(&hd->bl, &hd->sc, status->max_hp); + + } + if(flag&SCB_DEF) + { + status->def = hd->master->homunculus.level / 10 + status->vit / 5 ; + if(hd->sc.data[SC_DEFENCE].timer != -1) + status->def += hd->sc.data[SC_DEFENCE].val2; + if((skill=merc_hom_checkskill(hd->master,HAMI_SKIN)) > 0) { + status->def += skill * 4 ; + } + } + if(flag&(SCB_MAXSP|SCB_INT)) + { + flag|=SCB_MAXSP; + + // Skill-related Instruction Change + if((skill = merc_hom_checkskill(hd->master,HVAN_INSTRUCT)) > 0) { + if ( skill == 5 ) { + status->int_ += 3 ; + } else if ( skill == 1 ) { + status->int_ += 1 ; + } else { + status->int_ += 2 ; + } + if ( skill > 3 ) { + status->str += 4 ; + } else if ( skill == 3 ) { + status->str += 3 ; + } else { + status->str += 1 ; + } + } + + if((skill = merc_hom_checkskill(hd->master,HLIF_BRAIN)) > 0) { + status->max_sp = hd->master->homunculus.max_sp + skill * 2 * hd->master->homunculus.max_sp / 100 ; + hd->regensp += skill * 3 * hd->regensp / 100 ; + if ( skill == 5 ) { + status->max_sp *= 103 / 100 ; + } else if ( skill == 1 ) { + status->max_sp *= 101 / 100 ; + } else { + status->max_sp *= 102 / 100 ; + } + } + + status->mdef = hd->master->homunculus.level / 10 + status->int_ / 5 ; + status->max_sp = status_calc_maxsp(&hd->bl, &hd->sc, status->max_sp); + + if(status->max_sp > (unsigned int)battle_config.max_sp) + status->max_sp = battle_config.max_sp; + else if(!status->max_sp) + status->max_sp = 1; + + if(status->sp > status->max_sp) { + status->sp = status->max_sp; + } + + // sp recovery + hd->regensp = 1 + (status->int_/6) + (status->max_sp/100); + if(status->int_ >= 120) + hd->regensp += ((status->int_-120)>>1) + 4; + + if(hd->regensp < 1) hd->regensp = 1; + + } + + if(flag&(SCB_BATK|SCB_WATK)) { + status->rhw.atk = status->rhw.atk2 = status->str + ( status->str / 10 ) * ( status->str / 10 ) ; + status->rhw.atk += status->dex ; + if ( (status->str + hd->master->homunculus.level) > status->dex ) + status->rhw.atk2 += status->str + hd->master->homunculus.level ; + else + status->rhw.atk2 += status->dex ; + + if(hd->sc.data[SC_FLEET].timer!=-1) + status->rhw.atk2 += status->rhw.atk2 * hd->sc.data[SC_FLEET].val3/100; + } + + if(flag&SCB_MATK) { + status->matk_min = status->int_+(status->int_/7)*(status->int_/7); + status->matk_max = status->int_+(status->int_/5)*(status->int_/5); + + status->matk_min = status_calc_matk(&hd->bl, &hd->sc, status->matk_min); + status->matk_max = status_calc_matk(&hd->bl, &hd->sc, status->matk_max); + + } + + if(flag&SCB_HIT) { + if(status->hit < 1) status->hit = 1; + } + + if(flag&SCB_FLEE) { + if(status->flee < 1) status->flee = 1; + } + + if(flag&SCB_DEF2) { + if(status->def2 < 1) status->def2 = 1; + } + + if(flag&SCB_MDEF2) { + if(status->mdef2 < 1) status->mdef2 = 1; + } + + if(flag&SCB_SPEED) { + if(status->speed < battle_config.max_walk_speed) + status->speed = battle_config.max_walk_speed; + } + if(flag&(SCB_ASPD|SCB_AGI|SCB_DEX)) { + flag|=SCB_ASPD; + status->amotion = hd->homunculusDB->baseASPD - ((status->agi*4+status->dex)* hd->homunculusDB->baseASPD / 1000); + + status->aspd_rate = status_calc_aspd_rate(&hd->bl, &hd->sc , b_status->aspd_rate); + if(status->aspd_rate != 1000) + status->amotion = status->amotion *status->aspd_rate/1000; + + status->amotion = cap_value(status->amotion,battle_config.max_aspd,2000); + + status->adelay = 2*status->amotion; + } + + if(flag&(SCB_AGI|SCB_DSPD)) { + //Even though people insist this is too slow, packet data reports this is the actual real equation. + skill = 800-status->agi*4; + status->dmotion = cap_value(skill, 400, 800); + + if(battle_config.pc_damage_delay_rate != 100) + status->dmotion = status->dmotion*battle_config.pc_damage_delay_rate/100; + status->dmotion = status_calc_dmotion(&hd->bl, &hd->sc, b_status->dmotion); + } + + if(flag&SCB_CRI) + { + if(status->cri < 10) status->cri = 10; + } + + if(flag&SCB_FLEE2) { + if(status->flee2 < 10) status->flee2 = 10; + } + if (flag == SCB_ALL) + return; //Refresh is done on invoking function (status_calc_hom) + + if ( (flag&SCB_SPEED) || + (flag&SCB_STR) || + (flag&SCB_AGI) || + (flag&SCB_VIT) || + (flag&SCB_INT) || + (flag&SCB_DEX) || + (flag&SCB_LUK) || + (flag&SCB_HIT) || + (flag&SCB_FLEE) || + (flag&SCB_ASPD) || + (flag&(SCB_BATK|SCB_WATK)) || + (flag&SCB_DEF) || + (flag&SCB_WATK) || + (flag&SCB_DEF2) || + (flag&SCB_FLEE2) || + (flag&SCB_CRI) || + (flag&SCB_MATK) || + (flag&SCB_MDEF) || + (flag&SCB_MDEF2) || + (flag&SCB_RANGE) || + (flag&SCB_MAXHP) || + (flag&SCB_MAXSP) + ) + { + clif_hominfo(hd->master,0); + } +} + void status_calc_bl(struct block_list *bl, unsigned long flag) { struct status_data *b_status, *status; struct status_change *sc; int temp; TBL_PC *sd; + TBL_HOMUNCULUS *hd; b_status = status_get_base_status(bl); status = status_get_status_data(bl); sc = status_get_sc(bl); @@ -2566,6 +2731,7 @@ void status_calc_bl(struct block_list *bl, unsigned long flag) return; BL_CAST(BL_PC,bl,sd); + BL_CAST(BL_HOMUNCULUS,bl,hd); if(sd && flag&SCB_PC) { //Recalc everything. @@ -2573,7 +2739,8 @@ void status_calc_bl(struct block_list *bl, unsigned long flag) return; } - if(!sd && (!sc || !sc->count)) { //No difference. +// if(!sd && (!sc || !sc->count)) { //No difference. + if( (!sd && !hd ) && (!sc || !sc->count)) { //No difference. status_cpy(status, b_status); return; } @@ -2716,6 +2883,12 @@ void status_calc_bl(struct block_list *bl, unsigned long flag) return; } + if(hd) { + //The remaining are handled quite different by homunculus, so use their own function. + status_calc_bl_sub_hom(hd, flag); + return; + } + if(flag&SCB_MAXHP) { status->max_hp = status_calc_maxhp(bl, sc, b_status->max_hp); if (status->hp > status->max_hp) //FIXME: Should perhaps a status_zap should be issued? @@ -2985,8 +3158,6 @@ static unsigned short status_calc_batk(struct block_list *bl, struct status_chan batk += batk * 3; if(sc->data[SC_BLOODLUST].timer!=-1) batk += batk * sc->data[SC_BLOODLUST].val2/100; - if(sc->data[SC_FLEET].timer!=-1) - batk += batk * sc->data[SC_FLEET].val3/100; if(sc->data[SC_JOINTBEAT].timer!=-1 && sc->data[SC_JOINTBEAT].val2==4) batk -= batk * 25/100; if(sc->data[SC_CURSE].timer!=-1) @@ -3032,8 +3203,6 @@ static unsigned short status_calc_watk(struct block_list *bl, struct status_chan } if(sc->data[SC_BLOODLUST].timer!=-1) watk += watk * sc->data[SC_BLOODLUST].val2/100; - if(sc->data[SC_FLEET].timer!=-1) - watk += watk * sc->data[SC_FLEET].val3/100; if(sc->data[SC_CURSE].timer!=-1) watk -= watk * 25/100; if(sc->data[SC_STRIPWEAPON].timer!=-1) @@ -3140,6 +3309,8 @@ static signed short status_calc_flee(struct block_list *bl, struct status_change flee += 30; if(sc->data[SC_GATLINGFEVER].timer!=-1) flee -= sc->data[SC_GATLINGFEVER].val1*5; + if(sc->data[SC_SPEED].timer!=-1) + flee += 10 + sc->data[SC_SPEED].val1 * 10 ; return cap_value(flee,0,SHRT_MAX); } @@ -3166,6 +3337,8 @@ static signed char status_calc_def(struct block_list *bl, struct status_change * return 100; if(sc->data[SC_SKA].timer != -1) return sc->data[SC_SKA].val3; + if (sc->data[SC_DEFENCE].timer != -1) //[orn] + def += sc->data[SC_DEFENCE].val2 ; if(sc->data[SC_STEELBODY].timer!=-1) return 90; if(sc->data[SC_DRUMBATTLE].timer!=-1) @@ -3586,6 +3759,8 @@ int status_get_class(struct block_list *bl) return ((struct map_session_data *)bl)->status.class_; if(bl->type==BL_PET) return ((struct pet_data *)bl)->class_; + if(bl->type==BL_HOMUNCULUS) + return ((struct homun_data *)bl)->master->homunculus.class_; return 0; } /*========================================== @@ -3603,7 +3778,7 @@ int status_get_lv(struct block_list *bl) if(bl->type==BL_PET) return ((TBL_PET*)bl)->msd->pet.level; if(bl->type==BL_HOMUNCULUS) - return ((TBL_HOMUNCULUS*)bl)->level; + return ((TBL_HOMUNCULUS*)bl)->master->homunculus.level; return 1; } @@ -3699,6 +3874,16 @@ int status_get_party_id(struct block_list *bl) } return 0; //No party. } + if(bl->type==BL_HOMUNCULUS){ //[orn] + struct homun_data *hd=(struct homun_data *)bl; + if( hd->master->bl.id>0 ) + { + if ( hd->master != NULL) + return hd->master->status.party_id; + return -1; + } + return 0; //No party. + } if(bl->type==BL_SKILL) return ((struct skill_unit *)bl)->group->party_id; return 0; @@ -3721,6 +3906,16 @@ int status_get_guild_id(struct block_list *bl) return msd->status.guild_id; //Alchemist's mobs [Skotlex] return 0; //No guild. } + if(bl->type==BL_HOMUNCULUS){ //[orn] + struct homun_data *hd=(struct homun_data *)bl; + if( hd->master->bl.id>0 ) + { + if ( hd->master != NULL) + return hd->master->status.guild_id; + return -1; + } + return 0; //No guild. + } if (bl->type == BL_NPC && bl->subtype == SCRIPT) return ((TBL_NPC*)bl)->u.scr.guild_id; if(bl->type==BL_SKILL) @@ -4133,6 +4328,7 @@ int status_get_sc_tick(struct block_list *bl, int type, int tick) int status_change_start(struct block_list *bl,int type,int rate,int val1,int val2,int val3,int val4,int tick,int flag) { struct map_session_data *sd = NULL; + struct homun_data *hd = NULL; struct status_change* sc; struct status_data *status; int opt_flag , calc_flag, undead_flag; @@ -4149,6 +4345,9 @@ int status_change_start(struct block_list *bl,int type,int rate,int val1,int val case BL_PC: sd=(struct map_session_data *)bl; break; + case BL_HOMUNCULUS: + hd=(struct homun_data *)bl; //[orn] + break; case BL_MOB: if (((struct mob_data*)bl)->class_ == MOBID_EMPERIUM && type != SC_SAFETYWALL) return 0; //Emperium can't be afflicted by status changes. @@ -5188,6 +5387,9 @@ int status_change_start(struct block_list *bl,int type,int rate,int val1,int val case SC_AVOID: val2 = 10*val1; //Speed change rate. break; + case SC_DEFENCE: + val2 = 2*val1; //Def bonus + break; case SC_BLOODLUST: val2 = 20+10*val1; //Atk rate change. break; diff --git a/src/map/status.h b/src/map/status.h index 526444ab7..2f35a5fbb 100644 --- a/src/map/status.h +++ b/src/map/status.h @@ -253,6 +253,8 @@ enum { SC_CHANGE, SC_BLOODLUST, SC_FLEET, + SC_SPEED, //[orn] + SC_DEFENCE, //[orn] SC_INCAGIRATE, SC_INCDEXRATE, SC_MAX, //Automatically updated max, used in for's and at startup to check we are within bounds. [Skotlex] diff --git a/src/map/unit.c b/src/map/unit.c index 2845ff60b..9dd7b33fc 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -15,6 +15,7 @@ #include "pc.h" #include "mob.h" #include "pet.h" +#include "mercenary.h" ///[orn] #include "skill.h" #include "clif.h" #include "npc.h" @@ -38,6 +39,7 @@ struct unit_data* unit_bl2ud(struct block_list *bl) { if( bl->type == BL_MOB) return &((struct mob_data*)bl)->ud; if( bl->type == BL_PET) return &((struct pet_data*)bl)->ud; if( bl->type == BL_NPC) return &((struct npc_data*)bl)->ud; + if( bl->type == BL_HOMUNCULUS) return &((struct homun_data*)bl)->ud; //[orn] return NULL; } @@ -100,6 +102,7 @@ static int unit_walktoxy_timer(int tid,unsigned int tick,int id,int data) struct block_list *bl; struct map_session_data *sd = NULL; struct mob_data *md = NULL; + struct homun_data *hd = NULL; //[orn] struct unit_data *ud = NULL; bl=map_id2bl(id); @@ -109,6 +112,8 @@ static int unit_walktoxy_timer(int tid,unsigned int tick,int id,int data) ud = &sd->ud; } else if( BL_CAST( BL_MOB, bl, md ) ) { ud = &md->ud; + } else if( BL_CAST( BL_HOMUNCULUS, bl, hd ) ) { //[orn] + ud = &hd->ud; } else ud = unit_bl2ud(bl); @@ -262,6 +267,9 @@ int unit_walktoxy( struct block_list *bl, int x, int y, int easy) { nullpo_retr(0, bl); + if ( status_isdead(bl) ) //[orn] + return 0; + ud = unit_bl2ud(bl); if( ud == NULL) return 0; @@ -707,6 +715,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, int skill_num, int struct status_data *tstatus; struct status_change *sc; struct map_session_data *sd = NULL; + struct homun_data *hd = NULL; //[orn] struct block_list * target = NULL; unsigned int tick = gettick(); int temp; @@ -717,6 +726,8 @@ int unit_skilluse_id2(struct block_list *src, int target_id, int skill_num, int if( BL_CAST( BL_PC, src, sd ) ) { ud = &sd->ud; + } else if( BL_CAST( BL_HOMUNCULUS, src, hd ) ) { //[orn] + ud = &hd->ud; } else ud = unit_bl2ud(src); @@ -1184,6 +1195,7 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t struct status_data *sstatus; struct map_session_data *sd = NULL; struct mob_data *md = NULL; + struct homun_data *hd = NULL; //[orn] int range; if((ud=unit_bl2ud(src))==NULL) @@ -1195,6 +1207,7 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t } BL_CAST( BL_PC , src, sd); BL_CAST( BL_MOB, src, md); + BL_CAST( BL_HOMUNCULUS, src, hd); //[orn] ud->attacktimer=-1; target=map_id2bl(ud->target); @@ -1704,6 +1717,16 @@ int unit_free(struct block_list *bl) { } if(mob_is_clone(md->class_)) mob_clone_delete(md->class_); + } else if( bl->type == BL_HOMUNCULUS ) { //[orn] + struct homun_data *hd = (struct homun_data*)bl; + struct map_session_data *sd = hd->master; + merc_hom_hungry_timer_delete(hd); + merc_natural_heal_timer_delete(hd) ; + if (sd) { +// if(hd->intimacy > 0) +// intif_save_mercdata(sd->status.account_id,&sd->hom); + sd->hd = NULL; + } } skill_clear_unitgroup(bl); @@ -1729,4 +1752,3 @@ int do_final_unit(void) { // nothing to do return 0; } - -- cgit v1.2.3-70-g09d2