diff options
Diffstat (limited to 'src/map/skill.c')
-rw-r--r-- | src/map/skill.c | 170 |
1 files changed, 111 insertions, 59 deletions
diff --git a/src/map/skill.c b/src/map/skill.c index be899d0dc..55bf30338 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -2089,7 +2089,7 @@ int skill_blown(struct block_list* src, struct block_list* target, int count, in case BL_MOB: { const struct mob_data *md = BL_UCCAST(BL_MOB, target); - if (md->class_ == MOBID_EMPELIUM) + if (md->status.mode&MD_NOKNOCKBACK) return 0; if (src != target && is_boss(target)) // Bosses can't be knocked-back return 0; @@ -2833,7 +2833,7 @@ int skill_attack(int attack_type, struct block_list* src, struct block_list *dsr if ( ssc->data[SC_POISONINGWEAPON]->val1 == 9 )// Oblivion Curse gives a 2nd success chance after the 1st one passes which is reducible. [Rytech] rate = 100 - tstatus->int_ * 4 / 5; sc_start(src, bl,ssc->data[SC_POISONINGWEAPON]->val2,rate,ssc->data[SC_POISONINGWEAPON]->val1,skill->get_time2(GC_POISONINGWEAPON,1) - (tstatus->vit + tstatus->luk) / 2 * 1000); - status_change_end(src,SC_POISONINGWEAPON,-1); + status_change_end(src, SC_POISONINGWEAPON, INVALID_TIMER); clif->skill_nodamage(src,bl,skill_id,skill_lv,1); } } @@ -5123,7 +5123,7 @@ int skill_castend_id(int tid, int64 tick, int id, intptr_t data) { unit->set_walkdelay(src, tick, battle_config.default_walk_delay+skill->get_walkdelay(ud->skill_id, ud->skill_lv), 1); if(battle_config.skill_log && battle_config.skill_log&src->type) - ShowInfo("Type %d, ID %d skill castend id [id =%d, lv=%d, target ID %d]\n", + ShowInfo("Type %u, ID %d skill castend id [id =%d, lv=%d, target ID %d]\n", src->type, src->id, ud->skill_id, ud->skill_lv, target->id); map->freeblock_lock(); @@ -5401,12 +5401,12 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin type = status->skill2sc(skill_id); tsc = status->get_sc(bl); - tsce = (tsc && type != -1)?tsc->data[type]:NULL; + tsce = (tsc != NULL && type != SC_NONE) ? tsc->data[type] : NULL; - if (src!=bl && type > -1 && - (element = skill->get_ele(skill_id, skill_lv)) > ELE_NEUTRAL && - skill->get_inf(skill_id) != INF_SUPPORT_SKILL && - battle->attr_fix(NULL, NULL, 100, element, tstatus->def_ele, tstatus->ele_lv) <= 0) + if (src != bl && type > SC_NONE + && (element = skill->get_ele(skill_id, skill_lv)) > ELE_NEUTRAL + && skill->get_inf(skill_id) != INF_SUPPORT_SKILL + && battle->attr_fix(NULL, NULL, 100, element, tstatus->def_ele, tstatus->ele_lv) <= 0) return 1; //Skills that cause an status should be blocked if the target element blocks its element. map->freeblock_lock(); @@ -7297,7 +7297,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case NPC_REBIRTH: if( md && md->state.rebirth ) break; // only works once - sc_start(src,bl,type,100,skill_lv,-1); + sc_start(src, bl, type, 100, skill_lv, INFINITE_DURATION); break; case NPC_DARKBLESSING: @@ -8648,7 +8648,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin break; } - sc_start(src, bl, SC_STOP, 100, skill_lv, INVALID_TIMER); //Can't move while selecting a spellbook. + sc_start(src, bl, SC_STOP, 100, skill_lv, INFINITE_DURATION); //Can't move while selecting a spellbook. clif->spellbook_list(sd); clif->skill_nodamage(src, bl, skill_id, skill_lv, 1); } @@ -8777,7 +8777,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin if( sd ) { int idx1 = skill->get_index(sd->reproduceskill_id), idx2 = skill->get_index(sd->cloneskill_id); if( sd->status.skill[idx1].id || sd->status.skill[idx2].id ) { - sc_start(src,src,SC_STOP,100,skill_lv,-1);// The skill_lv is stored in val1 used in skill_select_menu to determine the used skill lvl [Xazax] + sc_start(src, src, SC_STOP, 100, skill_lv, INFINITE_DURATION); // The skill_lv is stored in val1 used in skill_select_menu to determine the used skill lvl [Xazax] clif->autoshadowspell_list(sd); clif->skill_nodamage(src,bl,skill_id,1,1); } @@ -8894,7 +8894,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin splashrange = 3; switch( opt ) { case 1: - sc_start(src,bl,SC_SHIELDSPELL_DEF,100,opt,INVALID_TIMER); //Splash AoE ATK + sc_start(src, bl, SC_SHIELDSPELL_DEF, 100, opt, INFINITE_DURATION); // Splash AoE ATK clif->skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, BDT_SKILL); map->foreachinrange(skill->area_sub,src,splashrange,BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill->castend_damage_id); status_change_end(bl,SC_SHIELDSPELL_DEF,INVALID_TIMER); @@ -8921,7 +8921,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin splashrange = 3; switch( opt ) { case 1: - sc_start(src,bl,SC_SHIELDSPELL_MDEF,100,opt,INVALID_TIMER); //Splash AoE MATK + sc_start(src, bl, SC_SHIELDSPELL_MDEF, 100, opt, INFINITE_DURATION); // Splash AoE MATK clif->skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, BDT_SKILL); map->foreachinrange(skill->area_sub,src,splashrange,BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill->castend_damage_id); status_change_end(bl,SC_SHIELDSPELL_MDEF,INVALID_TIMER); @@ -8958,7 +8958,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin sc_start(src,bl,SC_SCRESIST,100,rate,shield->refine * 30000)); break; case 3: - sc_start(src,bl,SC_SHIELDSPELL_REF,100,opt,INVALID_TIMER); //HP Recovery + sc_start(src, bl, SC_SHIELDSPELL_REF, 100, opt, INFINITE_DURATION); // HP Recovery val = sstatus->max_hp * ((status->get_lv(src) / 10) + (shield->refine + 1)) / 100; status->heal(bl, val, 0, 2); status_change_end(bl,SC_SHIELDSPELL_REF,INVALID_TIMER); @@ -9431,7 +9431,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case SO_EL_CONTROL: if( sd ) { - int mode = EL_MODE_PASSIVE; // Standard mode. + uint32 mode = EL_MODE_PASSIVE; // Standard mode. if( !sd->ed ) break; @@ -9443,7 +9443,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin case 2: mode = EL_MODE_ASSIST; break; case 3: mode = EL_MODE_AGGRESSIVE; break; } - if( !elemental->change_mode(sd->ed,mode) ) { + if (!elemental->change_mode(sd->ed, mode)) { clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); break; } @@ -10023,7 +10023,7 @@ int skill_castend_pos(int tid, int64 tick, int id, intptr_t data) } if(battle_config.skill_log && battle_config.skill_log&src->type) - ShowInfo("Type %d, ID %d skill castend pos [id =%d, lv=%d, (%d,%d)]\n", + ShowInfo("Type %u, ID %d skill castend pos [id =%d, lv=%d, (%d,%d)]\n", src->type, src->id, ud->skill_id, ud->skill_lv, ud->skillx, ud->skilly); if (ud->walktimer != INVALID_TIMER) @@ -10267,7 +10267,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui sc = status->get_sc(src); type = status->skill2sc(skill_id); - sce = (sc && type != -1)?sc->data[type]:NULL; + sce = (sc != NULL && type != SC_NONE) ? sc->data[type] : NULL; switch (skill_id) { //Skill effect. case WZ_METEOR: @@ -11670,7 +11670,7 @@ int skill_unit_onplace(struct skill_unit *src, struct block_list *bl, int64 tick return 0; type = status->skill2sc(sg->skill_id); - sce = (sc && type != -1)?sc->data[type]:NULL; + sce = (sc != NULL && type != SC_NONE) ? sc->data[type] : NULL; skill_id = sg->skill_id; //In case the group is deleted, we need to return the correct skill id, still. switch (sg->unit_id) { case UNT_SPIDERWEB: @@ -11777,6 +11777,10 @@ int skill_unit_onplace(struct skill_unit *src, struct block_list *bl, int64 tick sc_start4(ss,bl,type,100,sg->skill_lv,sg->val1,sg->val2,0,sg->limit); break; case UNT_APPLEIDUN: + // If Aegis, apple of idun doesn't update its effect + if (!battle_config.song_timer_reset && sc && sce) + return 0; + // Let it fall through case UNT_WHISTLE: case UNT_ASSASSINCROSS: case UNT_POEMBRAGI: @@ -11784,19 +11788,34 @@ int skill_unit_onplace(struct skill_unit *src, struct block_list *bl, int64 tick case UNT_DONTFORGETME: case UNT_FORTUNEKISS: case UNT_SERVICEFORYOU: + // Don't buff themselves without link! if (sg->src_id==bl->id && !(sc && sc->data[SC_SOULLINK] && sc->data[SC_SOULLINK]->val2 == SL_BARDDANCER)) return 0; if (!sc) return 0; if (!sce) sc_start4(ss,bl,type,100,sg->skill_lv,sg->val1,sg->val2,0,sg->limit); + // From here songs are already active else if (battle_config.song_timer_reset && sce->val4 == 1) { - //Readjust timers since the effect will not last long. + // eA style: + // Readjust timers since the effect will not last long. sce->val4 = 0; timer->delete(sce->timer, status->change_timer); sce->timer = timer->add(tick+sg->limit, status->change_timer, bl->id, type); + } else if (!battle_config.song_timer_reset) { + // Aegis style: + // Songs won't renew unless finished + const struct TimerData *td = timer->get(sce->timer); + if (DIFF_TICK32(td->tick, timer->gettick()) < sg->interval) { + // Update with new values as the current one will vanish soon + timer->delete(sce->timer, status->change_timer); + sce->timer = timer->add(tick+sg->limit, status->change_timer, bl->id, type); + sce->val1 = sg->skill_lv; // Why are we storing skill_lv as val1? + sce->val2 = sg->val1; + sce->val3 = sg->val2; + sce->val4 = 0; + } } - break; case UNT_FOGWALL: @@ -11931,7 +11950,7 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 case UNT_MANHOLE: return 0; default: - ShowError("skill_unit_onplace_timer: interval error (unit id %x)\n", sg->unit_id); + ShowError("skill_unit_onplace_timer: interval error (unit id %x)\n", (unsigned int)sg->unit_id); return 0; } } @@ -12201,18 +12220,25 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 if (md && md->class_ == MOBID_EMPELIUM) break; #endif - if ((sg->src_id == bl->id && !(tsc && tsc->data[SC_SOULLINK] && tsc->data[SC_SOULLINK]->val2 == SL_BARDDANCER)) - || (!(battle_config.song_timer_reset) && tsc && tsc->data[type] && tsc->data[type]->val4 == 1)) + // Don't buff themselves! + if ((sg->src_id == bl->id && !(tsc && tsc->data[SC_SOULLINK] && tsc->data[SC_SOULLINK]->val2 == SL_BARDDANCER))) break; - heal = skill->calc_heal(ss,bl,sg->skill_id, sg->skill_lv, true); - if( tsc && tsc->data[SC_AKAITSUKI] && heal ) - heal = ~heal + 1; - clif->skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1); - status->heal(bl, heal, 0, 0); - - if (!battle_config.song_timer_reset) + // Aegis style + // Check if the remaining time is enough to survive the next update + if (!battle_config.song_timer_reset + && !(tsc && tsc->data[type] && tsc->data[type]->val4 == 1)) { + // Apple of Idun is not active. Start it now sc_start4(ss, bl, type, 100, sg->skill_lv, sg->val1, sg->val2, 0, sg->limit); + } + + if (tstatus->hp < tstatus->max_hp) { + heal = skill->calc_heal(ss,bl,sg->skill_id, sg->skill_lv, true); + if( tsc && tsc->data[SC_AKAITSUKI] && heal ) + heal = ~heal + 1; + clif->skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1); + status->heal(bl, heal, 0, 0); + } } break; case UNT_POEMBRAGI: @@ -12222,12 +12248,30 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6 case UNT_DONTFORGETME: case UNT_FORTUNEKISS: case UNT_SERVICEFORYOU: - if (battle_config.song_timer_reset - || (!(battle_config.song_timer_reset) && tsc && tsc->data[type] && tsc->data[type]->val4 == 1) - || (sg->src_id == bl->id && !(tsc && tsc->data[SC_SOULLINK] && tsc->data[SC_SOULLINK]->val2 == SL_BARDDANCER)) - ) + // eA style: doesn't need this + if (battle_config.song_timer_reset) + break; + // Don't let buff themselves! + if (sg->src_id == bl->id && !(tsc && tsc->data[SC_SOULLINK] && tsc->data[SC_SOULLINK]->val2 == SL_BARDDANCER)) break; + // Aegis style + // Check if song has enough time to survive the next check + if (!(battle_config.song_timer_reset) && tsc && tsc->data[type] && tsc->data[type]->val4 == 1) { + const struct TimerData *td = timer->get(tsc->data[type]->timer); + if (DIFF_TICK32(td->tick, timer->gettick()) < sg->interval) { + // Update with new values as the current one will vanish + timer->delete(tsc->data[type]->timer, status->change_timer); + tsc->data[type]->timer = timer->add(tick+sg->limit, status->change_timer, bl->id, type); + tsc->data[type]->val1 = sg->skill_lv; + tsc->data[type]->val2 = sg->val1; + tsc->data[type]->val3 = sg->val2; + tsc->data[type]->val4 = 0; + } + break; // Had enough time or not, it now has. Exit + } + + // Song was not active. Start it now sc_start4(ss, bl, type, 100, sg->skill_lv, sg->val1, sg->val2, 0, sg->limit); break; case UNT_TATAMIGAESHI: @@ -12624,7 +12668,7 @@ int skill_unit_onout(struct skill_unit *src, struct block_list *bl, int64 tick) nullpo_ret(sg=src->group); sc = status->get_sc(bl); type = status->skill2sc(sg->skill_id); - sce = (sc && type != -1)?sc->data[type]:NULL; + sce = (sc != NULL && type != SC_NONE) ? sc->data[type] : NULL; if( bl->prev == NULL || (status->isdead(bl) && sg->unit_id != UNT_ANKLESNARE && sg->unit_id != UNT_SPIDERWEB && sg->unit_id != UNT_THORNS_TRAP) @@ -12688,7 +12732,7 @@ int skill_unit_onleft(uint16 skill_id, struct block_list *bl, int64 tick) { sc = NULL; type = status->skill2sc(skill_id); - sce = (sc && type != -1)?sc->data[type]:NULL; + sce = (sc != NULL && type != SC_NONE) ? sc->data[type] : NULL; switch (skill_id) { case WZ_QUAGMIRE: @@ -12749,8 +12793,9 @@ int skill_unit_onleft(uint16 skill_id, struct block_list *bl, int64 tick) { case DC_DONTFORGETME: case DC_FORTUNEKISS: case DC_SERVICEFORYOU: - if ((battle_config.song_timer_reset && sce) // athena style - || (!battle_config.song_timer_reset && sce && sce->val4 != 1) + + if ((battle_config.song_timer_reset && sce) // eAthena style: update everytime + || (!battle_config.song_timer_reset && sce && sce->val4 != 1) // Aegis style: update only when it was not a reduced effect ) { timer->delete(sce->timer, status->change_timer); //NOTE: It'd be nice if we could get the skill_lv for a more accurate extra time, but alas... @@ -16474,7 +16519,7 @@ struct skill_unit_group_tickset *skill_unitgrouptickset_search(struct block_list } if (j == -1) { - ShowWarning ("skill_unitgrouptickset_search: tickset is full. ( failed for skill '%s' on unit %d )\n",skill->get_name(group->skill_id),bl->type); + ShowWarning ("skill_unitgrouptickset_search: tickset is full. ( failed for skill '%s' on unit %u )\n", skill->get_name(group->skill_id), bl->type); j = id % MAX_SKILLUNITGROUPTICKSET; } @@ -17680,7 +17725,7 @@ int skill_poisoningweapon( struct map_session_data *sd, int nameid) { return 0; } - status_change_end(&sd->bl, SC_POISONINGWEAPON, -1);//Status must be forced to end so that a new poison will be applied if a player decides to change poisons. [Rytech] + status_change_end(&sd->bl, SC_POISONINGWEAPON, INVALID_TIMER); // Status must be forced to end so that a new poison will be applied if a player decides to change poisons. [Rytech] chance = 2 + 2 * sd->menuskill_val; // 2 + 2 * skill_lv sc_start4(&sd->bl, &sd->bl, SC_POISONINGWEAPON, 100, pc->checkskill(sd, GC_RESEARCHNEWPOISON), //in Aegis it store the level of GC_RESEARCHNEWPOISON in val1 type, chance, 0, skill->get_time(GC_POISONINGWEAPON, sd->menuskill_val)); @@ -17800,13 +17845,13 @@ int skill_spellbook (struct map_session_data *sd, int nameid) { for(i = SC_SPELLBOOK7; i >= SC_SPELLBOOK1; i--){ // This is how official saves spellbook. [malufett] if( !sc->data[i] ){ sc->data[SC_READING_SB]->val2 += point; // increase points - sc_start4(&sd->bl,&sd->bl, (sc_type)i, 100, skill_id, pc->checkskill(sd,skill_id), point, 0, INVALID_TIMER); + sc_start4(&sd->bl, &sd->bl, (sc_type)i, 100, skill_id, pc->checkskill(sd, skill_id), point, 0, INFINITE_DURATION); break; } } - }else{ - sc_start2(&sd->bl,&sd->bl, SC_READING_SB, 100, 0, point, INVALID_TIMER); - sc_start4(&sd->bl,&sd->bl, SC_SPELLBOOK7, 100, skill_id, pc->checkskill(sd,skill_id), point, 0, INVALID_TIMER); + } else { + sc_start2(&sd->bl, &sd->bl, SC_READING_SB, 100, 0, point, INFINITE_DURATION); + sc_start4(&sd->bl, &sd->bl, SC_SPELLBOOK7, 100, skill_id, pc->checkskill(sd, skill_id), point, 0, INFINITE_DURATION); } return 1; @@ -17834,27 +17879,31 @@ int skill_select_menu(struct map_session_data *sd,uint16 skill_id) { sc_start4(&sd->bl,&sd->bl,SC__AUTOSHADOWSPELL,100,id,lv,prob,0,skill->get_time(SC_AUTOSHADOWSPELL,aslvl)); return 0; } -int skill_elementalanalysis(struct map_session_data* sd, int n, uint16 skill_lv, unsigned short* item_list) { + +int skill_elementalanalysis(struct map_session_data *sd, uint16 skill_lv, const struct itemlist *item_list) +{ int i; nullpo_ret(sd); nullpo_ret(item_list); - if( n <= 0 ) + if (VECTOR_LENGTH(*item_list) <= 0) return 1; - for (i = 0; i < n; i++) { - int nameid, add_amount, del_amount, idx, product; + for (i = 0; i < VECTOR_LENGTH(*item_list); i++) { struct item tmp_item; - - idx = item_list[i*2+0]-2; - del_amount = item_list[i*2+1]; + const struct itemlist_entry *entry = &VECTOR_INDEX(*item_list, i); + int nameid, add_amount, product; + int del_amount = entry->amount; + int idx = entry->id; if( skill_lv == 2 ) del_amount -= (del_amount % 10); add_amount = (skill_lv == 1) ? del_amount * (5 + rnd()%5) : del_amount / 10 ; - if( (nameid = sd->status.inventory[idx].nameid) <= 0 || del_amount > sd->status.inventory[idx].amount ) { + if (idx < 0 || idx >= MAX_INVENTORY + || (nameid = sd->status.inventory[idx].nameid) <= 0 + || del_amount < 0 || del_amount > sd->status.inventory[idx].amount) { clif->skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0); return 1; } @@ -17904,7 +17953,8 @@ int skill_elementalanalysis(struct map_session_data* sd, int n, uint16 skill_lv, return 0; } -int skill_changematerial(struct map_session_data *sd, int n, unsigned short *item_list) { +int skill_changematerial(struct map_session_data *sd, const struct itemlist *item_list) +{ int i, j, k, c, p = 0, nameid, amount; nullpo_ret(sd); @@ -17919,11 +17969,13 @@ int skill_changematerial(struct map_session_data *sd, int n, unsigned short *ite // Verification of overlap between the objects required and the list submitted. for( j = 0; j < MAX_PRODUCE_RESOURCE; j++ ) { if( skill->dbs->produce_db[i].mat_id[j] > 0 ) { - for( k = 0; k < n; k++ ) { - int idx = item_list[k*2+0]-2; + for (k = 0; k < VECTOR_LENGTH(*item_list); k++) { + const struct itemlist_entry *entry = &VECTOR_INDEX(*item_list, k); + int idx = entry->id; + Assert_ret(idx >= 0 && idx < MAX_INVENTORY); + amount = entry->amount; nameid = sd->status.inventory[idx].nameid; - amount = item_list[k*2+1]; - if( nameid > 0 && sd->status.inventory[idx].identify == 0 ){ + if (nameid > 0 && sd->status.inventory[idx].identify == 0) { clif->msgtable_skill(sd, GN_CHANGEMATERIAL, MSG_SKILL_ITEM_NEED_IDENTIFY); return 0; } @@ -17936,7 +17988,7 @@ int skill_changematerial(struct map_session_data *sd, int n, unsigned short *ite break; // No more items required } p++; - } while(n == j && c == n); + } while (c == j && VECTOR_LENGTH(*item_list) == c); p--; if ( p > 0 ) { skill->produce_mix(sd,GN_CHANGEMATERIAL,skill->dbs->produce_db[i].nameid,0,0,0,p); |