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:
parent
bda5b80ae6
commit
a7b8fd4a5d
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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] ) {
|
||||
|
@ -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];
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user