Sight Blaster and other skill fixes, magic reflect, crash, trap display and monster behavior fixes
- Fixed cast time of Sightrasher in pre-renewal (700ms -> 500ms) - Official Sight Blaster behavior (bugreport:6945, partially bugreport:144) * Sight Blaster's AoE is now 3x3 even in pre-renewal (it was originally larger so it hits traps before they trigger) * Sight Blaster will now prevent traps from triggering as long as they are knocked back * Fixed a bug that caused Sight Blaster to not work on traps and ice walls at all * Sight Blaster will no longer expire when the attack was reflected * Sight Blaster will now expire when hitting an ice wall * Sight Blaster will now properly protect you from being attacked from its AoE range - Sight, Ruwach and Sight Blaster will now check for a target every 20ms (previously every 250ms) - Step action will now be canceled when being knocked back (skills won't be executed anymore when knocked out of range) - When knock back magic is reflected it will no longer lead to the caster being knocked back (related to bugreport:6945) - Activated traps can no longer be hit - Fixed a problem that left "trap ghosts" forever on the screen when a trap was knocked out of the screen - Fixed three more potential map server crashes - Monster behavior fixes * Monsters will no longer be able to do normal attacks when hiding * If out of any reason a monster on "attack" state can't move and can't do normal attacks, it will now use "attack" state skills * The order of monster thought processing is now equal to official servers
This commit is contained in:
parent
41ec18eae7
commit
b88e95381d
@ -157,7 +157,7 @@
|
|||||||
//-- WZ_FIREPILLAR
|
//-- WZ_FIREPILLAR
|
||||||
80,3000:2700:2400:2100:1800:1500:1200:900:600:300,1000,0,30000,600:800:1000:1200:1400:1600:1800:2000:2200:2400,0
|
80,3000:2700:2400:2100:1800:1500:1200:900:600:300,1000,0,30000,600:800:1000:1200:1400:1600:1800:2000:2200:2400,0
|
||||||
//-- WZ_SIGHTRASHER
|
//-- WZ_SIGHTRASHER
|
||||||
81,700,2000,0,500,0,0
|
81,500,2000,0,500,0,0
|
||||||
//-- WZ_METEOR
|
//-- WZ_METEOR
|
||||||
83,15000,2000:3000:3000:4000:4000:5000:5000:6000:6000:7000,0,500,5000,0
|
83,15000,2000:3000:3000:4000:4000:5000:5000:6000:6000:7000,0,500,5000,0
|
||||||
//-- WZ_JUPITEL
|
//-- WZ_JUPITEL
|
||||||
|
@ -864,7 +864,7 @@
|
|||||||
1003,0,0,0,0,0,0,1,0,no,0,0x1,0,weapon,0,0x0, AS_SONICACCEL,Sonic Acceleration
|
1003,0,0,0,0,0,0,1,0,no,0,0x1,0,weapon,0,0x0, AS_SONICACCEL,Sonic Acceleration
|
||||||
1004,9,8,1,0,0x8,0,1,1,no,0,0x1,0,weapon,0,0x0, AS_VENOMKNIFE,Throw Venom Knife
|
1004,9,8,1,0,0x8,0,1,1,no,0,0x1,0,weapon,0,0x0, AS_VENOMKNIFE,Throw Venom Knife
|
||||||
1005,1,6,1,0,0x1,0,1,1,no,0,0x1,0,weapon,0,0x0, RG_CLOSECONFINE,Close Confine
|
1005,1,6,1,0,0x1,0,1,1,no,0,0x1,0,weapon,0,0x0, RG_CLOSECONFINE,Close Confine
|
||||||
1006,0,6,4,3,0,2,1,1,yes,0,0x1,0,magic,3,0x20, WZ_SIGHTBLASTER,Sight Blaster
|
1006,0,6,4,3,0,1,1,1,yes,0,0x1,0,magic,3,0x20, WZ_SIGHTBLASTER,Sight Blaster
|
||||||
1007,0,6,4,0,0x1,0,1,0,no,0,0x1,0,none,0,0x0, SA_CREATECON,Create Elemental Converter
|
1007,0,6,4,0,0x1,0,1,0,no,0,0x1,0,none,0,0x0, SA_CREATECON,Create Elemental Converter
|
||||||
1008,9,6,1,1,0x1,0,1,1,yes,0,0x1,0,magic,0,0x0, SA_ELEMENTWATER,Elemental Change Water
|
1008,9,6,1,1,0x1,0,1,1,yes,0,0x1,0,magic,0,0x0, SA_ELEMENTWATER,Elemental Change Water
|
||||||
1009,-9,6,1,0,0,0,1,1,no,0,0x1,0,weapon,3,0x0, HT_PHANTASMIC,Phantasmic Arrow
|
1009,-9,6,1,0,0,0,1,1,no,0,0x1,0,weapon,3,0x0, HT_PHANTASMIC,Phantasmic Arrow
|
||||||
|
@ -7147,7 +7147,7 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
|
|||||||
{
|
{
|
||||||
TBL_SKILL *su = (TBL_SKILL*)target;
|
TBL_SKILL *su = (TBL_SKILL*)target;
|
||||||
|
|
||||||
if( su && su->group && skill_get_inf2(su->group->skill_id)&INF2_TRAP ) { //Only a few skills can target traps...
|
if( su && su->group && skill_get_inf2(su->group->skill_id)&INF2_TRAP && su->group->unit_id != UNT_USED_TRAPS) { //Only a few skills can target traps...
|
||||||
switch( battle_getcurrentskill(src) ) {
|
switch( battle_getcurrentskill(src) ) {
|
||||||
case RK_DRAGONBREATH:// it can only hit traps in pvp/gvg maps
|
case RK_DRAGONBREATH:// it can only hit traps in pvp/gvg maps
|
||||||
case RK_DRAGONBREATH_WATER:
|
case RK_DRAGONBREATH_WATER:
|
||||||
@ -7251,10 +7251,9 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
|
|||||||
break;
|
break;
|
||||||
case BL_SKILL: {
|
case BL_SKILL: {
|
||||||
struct skill_unit *su = (struct skill_unit *)src;
|
struct skill_unit *su = (struct skill_unit *)src;
|
||||||
|
struct status_change* sc = status_get_sc(target);
|
||||||
if (!su || !su->group)
|
if (!su || !su->group)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (su->group->src_id == target->id) {
|
if (su->group->src_id == target->id) {
|
||||||
int inf2 = skill_get_inf2(su->group->skill_id);
|
int inf2 = skill_get_inf2(su->group->skill_id);
|
||||||
if (inf2&INF2_NO_TARGET_SELF)
|
if (inf2&INF2_NO_TARGET_SELF)
|
||||||
@ -7262,6 +7261,11 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
|
|||||||
if (inf2&INF2_TARGET_SELF)
|
if (inf2&INF2_TARGET_SELF)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
//Status changes that prevent traps from triggering
|
||||||
|
if (sc && sc->count && skill_get_inf2(su->group->skill_id)&INF2_TRAP) {
|
||||||
|
if( sc->data[SC_SIGHTBLASTER] && sc->data[SC_SIGHTBLASTER]->val2 > 0 && sc->data[SC_SIGHTBLASTER]->val4%2 == 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case BL_MER:
|
case BL_MER:
|
||||||
|
@ -4708,9 +4708,10 @@ int clif_outsight(struct block_list *bl,va_list ap)
|
|||||||
tsd = BL_CAST(BL_PC, tbl);
|
tsd = BL_CAST(BL_PC, tbl);
|
||||||
|
|
||||||
if (tsd && tsd->fd) { //tsd has lost sight of the bl object.
|
if (tsd && tsd->fd) { //tsd has lost sight of the bl object.
|
||||||
|
nullpo_ret(bl);
|
||||||
switch(bl->type){
|
switch(bl->type){
|
||||||
case BL_PC:
|
case BL_PC:
|
||||||
if (sd->vd.class_ != INVISIBLE_CLASS)
|
if(sd->vd.class_ != INVISIBLE_CLASS)
|
||||||
clif_clearunit_single(bl->id,CLR_OUTSIGHT,tsd->fd);
|
clif_clearunit_single(bl->id,CLR_OUTSIGHT,tsd->fd);
|
||||||
if(sd->chatID){
|
if(sd->chatID){
|
||||||
struct chat_data *cd;
|
struct chat_data *cd;
|
||||||
@ -4718,9 +4719,9 @@ int clif_outsight(struct block_list *bl,va_list ap)
|
|||||||
if(cd->usersd[0]==sd)
|
if(cd->usersd[0]==sd)
|
||||||
clif_dispchat(cd,tsd->fd);
|
clif_dispchat(cd,tsd->fd);
|
||||||
}
|
}
|
||||||
if( sd->state.vending )
|
if(sd->state.vending)
|
||||||
clif_closevendingboard(bl,tsd->fd);
|
clif_closevendingboard(bl,tsd->fd);
|
||||||
if( sd->state.buyingstore )
|
if(sd->state.buyingstore)
|
||||||
clif_buyingstore_disappear_entry_single(tsd, sd);
|
clif_buyingstore_disappear_entry_single(tsd, sd);
|
||||||
break;
|
break;
|
||||||
case BL_ITEM:
|
case BL_ITEM:
|
||||||
@ -4730,17 +4731,20 @@ int clif_outsight(struct block_list *bl,va_list ap)
|
|||||||
clif_clearchar_skillunit((struct skill_unit *)bl,tsd->fd);
|
clif_clearchar_skillunit((struct skill_unit *)bl,tsd->fd);
|
||||||
break;
|
break;
|
||||||
case BL_NPC:
|
case BL_NPC:
|
||||||
if( !(((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE) )
|
if(!(((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE))
|
||||||
clif_clearunit_single(bl->id,CLR_OUTSIGHT,tsd->fd);
|
clif_clearunit_single(bl->id,CLR_OUTSIGHT,tsd->fd);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if ((vd=status_get_viewdata(bl)) && vd->class_ != INVISIBLE_CLASS)
|
if((vd=status_get_viewdata(bl)) && vd->class_ != INVISIBLE_CLASS)
|
||||||
clif_clearunit_single(bl->id,CLR_OUTSIGHT,tsd->fd);
|
clif_clearunit_single(bl->id,CLR_OUTSIGHT,tsd->fd);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sd && sd->fd) { //sd is watching tbl go out of view.
|
if (sd && sd->fd) { //sd is watching tbl go out of view.
|
||||||
if (((vd=status_get_viewdata(tbl)) && vd->class_ != INVISIBLE_CLASS) &&
|
nullpo_ret(tbl);
|
||||||
|
if(tbl->type == BL_SKILL) //Trap knocked out of sight
|
||||||
|
clif_clearchar_skillunit((struct skill_unit *)tbl,sd->fd);
|
||||||
|
else if(((vd=status_get_viewdata(tbl)) && vd->class_ != INVISIBLE_CLASS) &&
|
||||||
!(tbl->type == BL_NPC && (((TBL_NPC*)tbl)->sc.option&OPTION_INVISIBLE)))
|
!(tbl->type == BL_NPC && (((TBL_NPC*)tbl)->sc.option&OPTION_INVISIBLE)))
|
||||||
clif_clearunit_single(tbl->id,CLR_OUTSIGHT,sd->fd);
|
clif_clearunit_single(tbl->id,CLR_OUTSIGHT,sd->fd);
|
||||||
}
|
}
|
||||||
|
@ -1625,8 +1625,8 @@ static bool mob_ai_sub_hard(struct mob_data *md, unsigned int tick)
|
|||||||
|
|
||||||
//Attempt to attack.
|
//Attempt to attack.
|
||||||
//At this point we know the target is attackable, we just gotta check if the range matches.
|
//At this point we know the target is attackable, we just gotta check if the range matches.
|
||||||
if (battle_check_range (&md->bl, tbl, md->status.rhw.range))
|
if (battle_check_range(&md->bl, tbl, md->status.rhw.range) && !(md->sc.option&OPTION_HIDE))
|
||||||
{ //Target within range, engage
|
{ //Target within range and able to use normal attack, engage
|
||||||
if (md->ud.target != tbl->id || md->ud.attacktimer == INVALID_TIMER)
|
if (md->ud.target != tbl->id || md->ud.attacktimer == INVALID_TIMER)
|
||||||
{ //Only attack if no more attack delay left
|
{ //Only attack if no more attack delay left
|
||||||
if(tbl->type == BL_PC)
|
if(tbl->type == BL_PC)
|
||||||
@ -1646,6 +1646,19 @@ static bool mob_ai_sub_hard(struct mob_data *md, unsigned int tick)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Monsters in berserk state, unable to use normal attacks, will always attempt a skill
|
||||||
|
if(md->ud.walktimer == INVALID_TIMER && (md->state.skillstate == MSS_BERSERK || md->state.skillstate == MSS_ANGRY))
|
||||||
|
{
|
||||||
|
if (DIFF_TICK(md->ud.canmove_tick, tick) <= MIN_MOBTHINKTIME && DIFF_TICK(md->ud.canact_tick, tick) < -MIN_MOBTHINKTIME*IDLE_SKILL_INTERVAL)
|
||||||
|
{ //Only use skill if able to walk on next tick and not used a skill the last second
|
||||||
|
mobskill_use(md, tick, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Target still in attack range, no need to chase the target
|
||||||
|
if(battle_check_range(&md->bl, tbl, md->status.rhw.range))
|
||||||
|
return true;
|
||||||
|
|
||||||
//Out of range...
|
//Out of range...
|
||||||
if (!(mode&MD_CANMOVE) || (!can_move && DIFF_TICK(tick, md->ud.canmove_tick) > 0))
|
if (!(mode&MD_CANMOVE) || (!can_move && DIFF_TICK(tick, md->ud.canmove_tick) > 0))
|
||||||
{ //Can't chase. Immobile and trapped mobs should unlock target and use an idle skill.
|
{ //Can't chase. Immobile and trapped mobs should unlock target and use an idle skill.
|
||||||
@ -1657,15 +1670,6 @@ static bool mob_ai_sub_hard(struct mob_data *md, unsigned int tick)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Before a monster starts to chase a target, it will check if it has a ranged "attack" skill to use on it.
|
|
||||||
if(md->ud.walktimer == INVALID_TIMER && (md->state.skillstate == MSS_BERSERK || md->state.skillstate == MSS_ANGRY))
|
|
||||||
{
|
|
||||||
if (DIFF_TICK(md->ud.canmove_tick, tick) <= MIN_MOBTHINKTIME && DIFF_TICK(md->ud.canact_tick, tick) < -MIN_MOBTHINKTIME*IDLE_SKILL_INTERVAL)
|
|
||||||
{ //Only use skill if able to walk on next tick and not used a skill the last second
|
|
||||||
mobskill_use(md, tick, -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (md->ud.walktimer != INVALID_TIMER && md->ud.target == tbl->id &&
|
if (md->ud.walktimer != INVALID_TIMER && md->ud.target == tbl->id &&
|
||||||
(
|
(
|
||||||
!(battle_config.mob_ai&0x1) ||
|
!(battle_config.mob_ai&0x1) ||
|
||||||
|
@ -2831,6 +2831,8 @@ void skill_attack_blow(struct block_list *src, struct block_list *dsrc, struct b
|
|||||||
* flag&0xFFF is passed to the underlying battle_calc_attack for processing
|
* flag&0xFFF is passed to the underlying battle_calc_attack for processing
|
||||||
* (usually holds number of targets, or just 1 for simple splash attacks)
|
* (usually holds number of targets, or just 1 for simple splash attacks)
|
||||||
*
|
*
|
||||||
|
* flag&0x1000 - Return 0 if damage was reflected
|
||||||
|
*
|
||||||
* Values from enum e_skill_display
|
* Values from enum e_skill_display
|
||||||
* Values from enum e_battle_check_target
|
* Values from enum e_battle_check_target
|
||||||
*-------------------------------------------------------------------------*/
|
*-------------------------------------------------------------------------*/
|
||||||
@ -2909,7 +2911,8 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list *
|
|||||||
tsc = NULL; //Don't need it.
|
tsc = NULL; //Don't need it.
|
||||||
/* bugreport:2564 flag&2 disables double casting trigger */
|
/* bugreport:2564 flag&2 disables double casting trigger */
|
||||||
flag |= 2;
|
flag |= 2;
|
||||||
|
//Reflected magic damage will not cause the caster to be knocked back [Playtester]
|
||||||
|
flag |= 4;
|
||||||
//Spirit of Wizard blocks Kaite's reflection
|
//Spirit of Wizard blocks Kaite's reflection
|
||||||
if( type == 2 && tsc && tsc->data[SC_SPIRIT] && tsc->data[SC_SPIRIT]->val2 == SL_WIZARD )
|
if( type == 2 && tsc && tsc->data[SC_SPIRIT] && tsc->data[SC_SPIRIT]->val2 == SL_WIZARD )
|
||||||
{ //Consume one Fragment per hit of the casted skill? [Skotlex]
|
{ //Consume one Fragment per hit of the casted skill? [Skotlex]
|
||||||
@ -3285,6 +3288,9 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list *
|
|||||||
|
|
||||||
map_freeblock_unlock();
|
map_freeblock_unlock();
|
||||||
|
|
||||||
|
if ((flag&0x1000) && rmdamage == 1)
|
||||||
|
return 0; //Should return 0 when damage was reflected
|
||||||
|
|
||||||
return damage;
|
return damage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6750,7 +6750,9 @@ int status_get_guild_id(struct block_list *bl)
|
|||||||
return ((TBL_NPC*)bl)->u.scr.guild_id;
|
return ((TBL_NPC*)bl)->u.scr.guild_id;
|
||||||
break;
|
break;
|
||||||
case BL_SKILL:
|
case BL_SKILL:
|
||||||
return ((TBL_SKILL*)bl)->group->guild_id;
|
if (((TBL_SKILL*)bl)->group)
|
||||||
|
return ((TBL_SKILL*)bl)->group->guild_id;
|
||||||
|
break;
|
||||||
case BL_ELEM:
|
case BL_ELEM:
|
||||||
if (((TBL_ELEM*)bl)->master)
|
if (((TBL_ELEM*)bl)->master)
|
||||||
return ((TBL_ELEM*)bl)->master->status.guild_id;
|
return ((TBL_ELEM*)bl)->master->status.guild_id;
|
||||||
@ -8701,8 +8703,8 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
|
|||||||
case SC_RUWACH:
|
case SC_RUWACH:
|
||||||
case SC_SIGHTBLASTER:
|
case SC_SIGHTBLASTER:
|
||||||
val3 = skill_get_splash(val2, val1); // Val2 should bring the skill-id.
|
val3 = skill_get_splash(val2, val1); // Val2 should bring the skill-id.
|
||||||
val2 = tick/250;
|
val2 = tick/20;
|
||||||
tick_time = 10; // [GodLesZ] tick time
|
tick_time = 20; // [GodLesZ] tick time
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SC_AUTOGUARD:
|
case SC_AUTOGUARD:
|
||||||
@ -11370,14 +11372,17 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data)
|
|||||||
case SC_SIGHT:
|
case SC_SIGHT:
|
||||||
case SC_RUWACH:
|
case SC_RUWACH:
|
||||||
case SC_SIGHTBLASTER:
|
case SC_SIGHTBLASTER:
|
||||||
if(type == SC_SIGHTBLASTER)
|
if(type == SC_SIGHTBLASTER) {
|
||||||
|
//Restore trap immunity
|
||||||
|
if(sce->val4%2)
|
||||||
|
sce->val4--;
|
||||||
map_foreachinrange( status_change_timer_sub, bl, sce->val3, BL_CHAR|BL_SKILL, bl, sce, type, tick);
|
map_foreachinrange( status_change_timer_sub, bl, sce->val3, BL_CHAR|BL_SKILL, bl, sce, type, tick);
|
||||||
else
|
} else
|
||||||
map_foreachinrange( status_change_timer_sub, bl, sce->val3, BL_CHAR, bl, sce, type, tick);
|
map_foreachinrange( status_change_timer_sub, bl, sce->val3, BL_CHAR, bl, sce, type, tick);
|
||||||
|
|
||||||
if( --(sce->val2)>0 ) {
|
if( --(sce->val2)>0 ) {
|
||||||
sce->val4 += 250; // Use for Shadow Form 2 seconds checking.
|
sce->val4 += 20; // Use for Shadow Form 2 seconds checking.
|
||||||
sc_timer_next(250+tick, status_change_timer, bl->id, data);
|
sc_timer_next(20+tick, status_change_timer, bl->id, data);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -12203,9 +12208,13 @@ int status_change_timer_sub(struct block_list* bl, va_list ap)
|
|||||||
if (battle_check_target( src, bl, BCT_ENEMY ) > 0 &&
|
if (battle_check_target( src, bl, BCT_ENEMY ) > 0 &&
|
||||||
status_check_skilluse(src, bl, WZ_SIGHTBLASTER, 2))
|
status_check_skilluse(src, bl, WZ_SIGHTBLASTER, 2))
|
||||||
{
|
{
|
||||||
if (sce && !(bl->type&BL_SKILL) // The hit is not counted if it's against a trap
|
struct skill_unit *su = (struct skill_unit *)bl;
|
||||||
&& skill_attack(BF_MAGIC,src,src,bl,WZ_SIGHTBLASTER,sce->val1,tick,0)) {
|
if (sce && skill_attack(BF_MAGIC,src,src,bl,WZ_SIGHTBLASTER,sce->val1,tick,0x1000)
|
||||||
|
&& (!su || !su->group || !(skill_get_inf2(su->group->skill_id)&INF2_TRAP))) { // The hit is not counted if it's against a trap
|
||||||
sce->val2 = 0; // This signals it to end.
|
sce->val2 = 0; // This signals it to end.
|
||||||
|
} else if((bl->type&BL_SKILL) && sce->val4%2 == 0) {
|
||||||
|
//Remove trap immunity temporarily so it triggers if you still stand on it
|
||||||
|
sce->val4++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1009,6 +1009,7 @@ int unit_blown(struct block_list* bl, int dx, int dy, int count, int flag)
|
|||||||
unit_stop_walking(bl, 0);
|
unit_stop_walking(bl, 0);
|
||||||
|
|
||||||
if( sd ) {
|
if( sd ) {
|
||||||
|
unit_stop_stepaction(bl); //Stop stepaction when knocked back
|
||||||
sd->ud.to_x = nx;
|
sd->ud.to_x = nx;
|
||||||
sd->ud.to_y = ny;
|
sd->ud.to_y = ny;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user