Bug Fixes

* Fixes #696 - Fixed an issue with ammo type fail message for bows and guns.
* Fixes #708 - Fixed Neutral Barrier and Stealth Field not staying with the player through warp portals on the same map. Thanks to @exneval.
* Added the new Pile Bunker S/P/T items to the Pile Bunker skill equipment check.
* Updated Exeed Break damage formula.
* Added a check for skill_require_db parsing to skip requirements on invalid item IDs.
This commit is contained in:
aleos89 2015-11-06 10:55:00 -05:00
parent bda5b80ae6
commit a7b8fd4a5d
8 changed files with 67 additions and 35 deletions

View File

@ -680,7 +680,7 @@
//****
// NC Mechanic
2256,0,0,3:6:9:12:15,0,0,0,99,0,0,mado,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //NC_BOOSTKNUCKLE
2257,0,0,50,0,0,0,99,0,0,mado,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1549 //NC_PILEBUNKER
2257,0,0,50,0,0,0,99,0,0,mado,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1549:16030:16031:16032 //NC_PILEBUNKER
2258,0,0,2:4:6,0,0,0,99,0,0,mado,0,0,6145,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //NC_VULCANARM
2259,0,0,20,0,0,0,99,0,0,mado,0,0,2139,0,6146,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //NC_FLAMELAUNCHER
2260,0,0,20,0,0,0,99,0,0,mado,0,0,6146,1,6147,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //NC_COLDSLOWER

View File

@ -123,7 +123,7 @@
2254,0xd7, , 0, 1,1000,enemy, 0x8002 //RA_ICEBOUNDTRAP
2273,0xe2, , 2, 0, -1,all, 0x000 //NC_NEUTRALBARRIER
2274,0xe3, , 2, 0, -1,friend,0x000 //NC_STEALTHFIELD
2274,0xe3, , 2, 0, -1,ally, 0x000 //NC_STEALTHFIELD
2299,0xcc, , 0, 1,1000,all, 0x8006 //SC_MANHOLE
2300,0xcd, , 0, 0,1000,all, 0xC006 //SC_DIMENSIONDOOR

View File

@ -680,7 +680,7 @@
//****
// NC Mechanic
2256,0,0,3:6:9:12:15,0,0,0,99,0,0,mado,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //NC_BOOSTKNUCKLE
2257,0,0,50,0,0,0,99,0,0,mado,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1549 //NC_PILEBUNKER
2257,0,0,50,0,0,0,99,0,0,mado,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1549:16030:16031:16032 //NC_PILEBUNKER
2258,0,0,2:4:6,0,0,0,99,0,0,mado,0,0,6145,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //NC_VULCANARM
2259,0,0,20,0,0,0,99,0,0,mado,0,0,2139,0,6146,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //NC_FLAMELAUNCHER
2260,0,0,20,0,0,0,99,0,0,mado,0,0,6146,1,6147,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //NC_COLDSLOWER

View File

@ -125,7 +125,7 @@
2254,0xd7, , 0, 1,1000,enemy, 0x8002 //RA_ICEBOUNDTRAP
2273,0xe2, , 2, 0, -1,all, 0x000 //NC_NEUTRALBARRIER
2274,0xe3, , 2, 0, -1,friend,0x000 //NC_STEALTHFIELD
2274,0xe3, , 2, 0, -1,ally, 0x000 //NC_STEALTHFIELD
2299,0xcc, , 0, 1,1000,all, 0x8006 //SC_MANHOLE
2300,0xcd, , 0, 0,1000,all, 0xC006 //SC_DIMENSIONDOOR

View File

@ -6909,7 +6909,7 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t
{
short index = sd->equip_index[EQI_AMMO];
if (index < 0) {
if (sd->weapontype1 > W_KATAR || sd->weapontype1 < W_HUUMA)
if (sd->weapontype1 > W_KATAR && sd->weapontype1 < W_HUUMA)
clif_skill_fail(sd,0,USESKILL_FAIL_NEED_MORE_BULLET,0);
else
clif_arrow_fail(sd,0);
@ -7054,7 +7054,7 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t
if( sc && sc->count ) {
if (sc->data[SC_EXEEDBREAK]) {
wd.damage *= sc->data[SC_EXEEDBREAK]->val1 / 100;
wd.damage *= sc->data[SC_EXEEDBREAK]->val2 / 100;
status_change_end(src, SC_EXEEDBREAK, INVALID_TIMER);
}
if( sc->data[SC_SPELLFIST] ) {

View File

@ -12910,12 +12910,16 @@ static int skill_unit_onplace(struct skill_unit *unit, struct block_list *bl, un
case UNT_STEALTHFIELD:
if( bl->id == sg->src_id )
break; // Dont work on Self (video shows that)
case UNT_NEUTRALBARRIER:
break; // Doesn't work on self (video shows that)
if (!sce)
sc_start(ss, bl,type,100,sg->skill_lv,sg->limit);
break;
case UNT_NEUTRALBARRIER:
if (!sce)
status_change_start(ss, bl, type, 10000, sg->skill_lv, 0, 0, 0, sg->limit, SCSTART_NOICON);
break;
case UNT_GD_LEADERSHIP:
case UNT_GD_GLORYWOUNDS:
case UNT_GD_SOULCOLD:
@ -17470,6 +17474,8 @@ int skill_delunitgroup_(struct skill_unit_group *group, const char* file, int li
case DC_DONTFORGETME:
case DC_FORTUNEKISS:
case DC_SERVICEFORYOU:
case NC_NEUTRALBARRIER:
case NC_STEALTHFIELD:
skill_usave_add(((TBL_PC*)src), group->skill_id, group->skill_lv);
break;
}
@ -19441,33 +19447,42 @@ int skill_blockmerc_start(struct mercenary_data *md, uint16 skill_id, int tick)
}
/**
* Adds a new skill unit entry for this player to recast after map load
* @param sd: Player
* @param skill_id: Skill ID to save
* @param skill_lv: Skill level to save
*/
void skill_usave_add(struct map_session_data * sd, uint16 skill_id, uint16 skill_lv) {
struct skill_usave * sus = NULL;
void skill_usave_add(struct map_session_data *sd, uint16 skill_id, uint16 skill_lv)
{
struct skill_usave *sus = NULL;
if( idb_exists(skillusave_db,sd->status.char_id) ) {
if (idb_exists(skillusave_db,sd->status.char_id))
idb_remove(skillusave_db,sd->status.char_id);
}
CREATE(sus, struct skill_usave, 1);
idb_put(skillusave_db, sd->status.char_id, sus);
sus->skill_id = skill_id;
sus->skill_lv = skill_lv;
return;
}
void skill_usave_trigger(struct map_session_data *sd) {
struct skill_usave * sus = NULL;
if( ! (sus = (struct skill_usave *)idb_get(skillusave_db,sd->status.char_id)) )
/**
* Loads saved skill unit entries for this player after map load
* @param sd: Player
*/
void skill_usave_trigger(struct map_session_data *sd)
{
struct skill_usave *sus = NULL;
struct skill_unit_group *group = NULL;
if (!(sus = idb_get(skillusave_db,sd->status.char_id)))
return;
skill_unitsetting(&sd->bl,sus->skill_id,sus->skill_lv,sd->bl.x,sd->bl.y,0);
idb_remove(skillusave_db,sd->status.char_id);
return;
if ((group = skill_unitsetting(&sd->bl, sus->skill_id, sus->skill_lv, sd->bl.x, sd->bl.y, 0)))
if (sus->skill_id == NC_NEUTRALBARRIER || sus->skill_id == NC_STEALTHFIELD )
sc_start2(&sd->bl, &sd->bl, (sus->skill_id == NC_NEUTRALBARRIER ? SC_NEUTRALBARRIER_MASTER : SC_STEALTHFIELD_MASTER), 100, sus->skill_lv, group->group_id, skill_get_time(sus->skill_id, sus->skill_lv));
idb_remove(skillusave_db, sd->status.char_id);
}
/*
*
*/
@ -20150,7 +20165,7 @@ static bool skill_parse_row_skilldb(char* split[], int columns, int current)
}
/**
* Split string to int by constanta value (const.txt) or atoi()
* Split string to int by constant value (const.txt) or atoi()
* @param *str: String input
* @param *val: Temporary storage
* @param *delim: Delimiter (for multiple value support)
@ -20164,6 +20179,7 @@ uint8 skill_split_atoi2(char *str, int *val, const char *delim, int min_value, u
while (p != NULL) {
int n = min_value;
trim(p);
if (ISDIGIT(p[0])) // If using numeric
@ -20200,7 +20216,7 @@ static void skill_destroy_requirement(uint16 idx) {
/**
* Read skill requirement from skill_require_db.txt
* Structure: skill_id,HPCost,MaxHPTrigger,SPCost,HPRateCost,SPRateCost,ZenyCost,RequiredWeapons,RequiredAmmoTypes,RequiredAmmoAmount,RequiredState,RequiredStatuss,SpiritSphereCost,RequiredItemID1,RequiredItemAmount1,RequiredItemID2,RequiredItemAmount2,RequiredItemID3,RequiredItemAmount3,RequiredItemID4,RequiredItemAmount4,RequiredItemID5,RequiredItemAmount5,RequiredItemID6,RequiredItemAmount6,RequiredItemID7,RequiredItemAmount7,RequiredItemID8,RequiredItemAmount8,RequiredItemID9,RequiredItemAmount9,RequiredItemID10,RequiredItemAmount10
* Structure: skill_id,HPCost,MaxHPTrigger,SPCost,HPRateCost,SPRateCost,ZenyCost,RequiredWeapons,RequiredAmmoTypes,RequiredAmmoAmount,RequiredState,RequiredStatuses,SpiritSphereCost,RequiredItemID1,RequiredItemAmount1,RequiredItemID2,RequiredItemAmount2,RequiredItemID3,RequiredItemAmount3,RequiredItemID4,RequiredItemAmount4,RequiredItemID5,RequiredItemAmount5,RequiredItemID6,RequiredItemAmount6,RequiredItemID7,RequiredItemAmount7,RequiredItemID8,RequiredItemAmount8,RequiredItemID9,RequiredItemAmount9,RequiredItemID10,RequiredItemAmount10,RequiredEquipment
*/
static bool skill_parse_row_requiredb(char* split[], int columns, int current)
{
@ -20269,6 +20285,7 @@ static bool skill_parse_row_requiredb(char* split[], int columns, int current)
trim(split[11]);
if (split[11][0] != '\0' || atoi(split[11])) {
int require[MAX_SKILL_STATUS_REQUIRE];
if ((skill_db[idx]->require.status_count = skill_split_atoi2(split[11], require, ":", SC_STONE, ARRAYLENGTH(require)))) {
CREATE(skill_db[idx]->require.status, enum sc_type, skill_db[idx]->require.status_count);
for (i = 0; i < skill_db[idx]->require.status_count; i++)
@ -20279,16 +20296,25 @@ static bool skill_parse_row_requiredb(char* split[], int columns, int current)
skill_split_atoi(split[12],skill_db[idx]->require.spiritball);
for( i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++ ) {
if (atoi(split[13 + 2 * i]) > 0 && !itemdb_exists(atoi(split[13 + 2 * i]))) {
ShowError("skill_parse_row_requiredb: Invalid item %d for skill %d.\n", atoi(split[13 + 2 * i]), atoi(split[0]));
return false;
}
skill_db[idx]->require.itemid[i] = atoi(split[13+ 2*i]);
skill_db[idx]->require.amount[i] = atoi(split[14+ 2*i]);
}
//Equipped Item requirements.
//NOTE: We don't check the item is exist or not here
trim(split[33]);
if (split[33][0] != '\0' || atoi(split[33])) {
int require[MAX_SKILL_EQUIP_REQUIRE];
if ((skill_db[idx]->require.eqItem_count = skill_split_atoi2(split[33], require, ":", 500, ARRAYLENGTH(require)))) {
if (require[i] > 0 && !itemdb_exists(require[i])) {
ShowError("skill_parse_row_requiredb: Invalid item %d for skill %d.\n", require[i], atoi(split[0]));
return false;
}
CREATE(skill_db[idx]->require.eqItem, uint16, skill_db[idx]->require.eqItem_count);
for (i = 0; i < skill_db[idx]->require.eqItem_count; i++)
skill_db[idx]->require.eqItem[i] = require[i];

View File

@ -1166,6 +1166,7 @@ void initChangeTables(void)
StatusDisplayType[SC_SPHERE_4] = true;
StatusDisplayType[SC_SPHERE_5] = true;
StatusDisplayType[SC_CAMOUFLAGE] = true;
StatusDisplayType[SC_STEALTHFIELD] = true;
StatusDisplayType[SC_DUPLELIGHT] = true;
StatusDisplayType[SC_ORATIO] = true;
StatusDisplayType[SC_FREEZING] = true;
@ -6168,7 +6169,7 @@ static unsigned short status_calc_speed(struct block_list *bl, struct status_cha
if( sc->data[SC_CAMOUFLAGE] && (sc->data[SC_CAMOUFLAGE]->val3&1) == 0 )
val = max( val, sc->data[SC_CAMOUFLAGE]->val1 < 3 ? 0 : 25 * (5 - sc->data[SC_CAMOUFLAGE]->val1) );
if( sc->data[SC_STEALTHFIELD] )
val = max( val, sc->data[SC_STEALTHFIELD]->val2 );
val = max( val, 20 );
if( sc->data[SC__LAZINESS] )
val = max( val, 25 );
if( sc->data[SC_BANDING_DEFENCE] )
@ -9823,14 +9824,14 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
tick_time = 10000; // [GodLesZ] tick time
break;
case SC_EXEEDBREAK:
val1 = 100 * val1;
val2 = 150 * val1;
if (sd) { // Players
short index = sd->equip_index[EQI_HAND_R];
if (index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == IT_WEAPON)
val1 += 10 * sd->status.job_level + sd->inventory_data[index]->weight / 10 * sd->inventory_data[index]->wlv * status_get_lv(bl) / 100;
val2 += 15 * sd->status.job_level + sd->inventory_data[index]->weight / 10 * sd->inventory_data[index]->wlv * status_get_lv(bl) / 100;
} else // Monster
val1 += 500;
val2 += 750;
break;
case SC_PRESTIGE:
val2 = (status->int_ + status->luk) * val1 / 20 * status_get_lv(bl) / 200 + val1; // Chance to evade magic damage.
@ -10162,12 +10163,12 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
break;
case SC_STEALTHFIELD:
val2 = 30; // Speed reduction
tick_time = tick;
tick = -1;
break;
case SC_STEALTHFIELD_MASTER:
val3 = 3; // Reduces SP 3%
tick_time = 3000;
val4 = tick/tick_time;
tick_time = val3 = 2000 + 1000 * val1;
val4 = tick / tick_time;
break;
case SC_VACUUM_EXTREME:
// Suck target at n second, only if the n second is lower than the duration
@ -10392,6 +10393,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
case SC_CHASEWALK:
case SC_WEIGHT90:
case SC_CAMOUFLAGE:
case SC_STEALTHFIELD:
case SC_VOICEOFSIREN:
case SC_HEAT_BARREL_AFTER:
case SC_WEDDING:
@ -10531,6 +10533,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
case SC__INVISIBILITY:
sc->option |= OPTION_CLOAK;
case SC_CAMOUFLAGE:
case SC_STEALTHFIELD:
opt_flag = 2;
break;
case SC_CHASEWALK:
@ -11426,6 +11429,7 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const
case SC__INVISIBILITY:
sc->option &= ~OPTION_CLOAK;
case SC_CAMOUFLAGE:
case SC_STEALTHFIELD:
opt_flag |= 2;
break;
case SC_CHASEWALK:
@ -12506,10 +12510,10 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data)
break;
case SC_STEALTHFIELD_MASTER:
if (--(sce->val4) >= 0) {
int sp = (status->max_sp * sce->val3) / 100;
if (!status_charge(bl,0,sp))
if (!status_charge(bl, 0, status->max_sp * 3 / 100))
break;
sc_timer_next(3000 + tick, status_change_timer, bl->id, data);
sc_timer_next(sce->val3 + tick, status_change_timer, bl->id, data);
return 0;
}
break;
case SC_VACUUM_EXTREME:

View File

@ -2860,6 +2860,8 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file,
status_change_end(bl, SC_STOP, INVALID_TIMER);
status_change_end(bl, SC_WUGDASH, INVALID_TIMER);
status_change_end(bl, SC_CAMOUFLAGE, INVALID_TIMER);
status_change_end(bl, SC_NEUTRALBARRIER_MASTER, INVALID_TIMER);
status_change_end(bl, SC_STEALTHFIELD_MASTER, INVALID_TIMER);
status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER);
status_change_end(bl, SC__MANHOLE, INVALID_TIMER);
status_change_end(bl, SC_VACUUM_EXTREME, INVALID_TIMER);