summaryrefslogblamecommitdiff
path: root/src/emap/battle.c
blob: 101e525616bcf8d9108dc4a7763455c14a3694d9 (plain) (tree)
1
2
3
4
5
6
7
8
9
10









                                                                         

                          
                          

                       
                    

                   

                               
                            
                           
                                 

























                                                                        
















                                                                 

                            


















                                                                           
                 
 
 

 

                                                                     

                                                                        
                                                              

                                                                                 













                                                                            

                                          
                                             



                                                     


                                                                                                    
          

     














                                                      


                     

                                                           




                                                   

                                                                     

                  
 





                                                                        
                                                              

                                                                                 



                                      









                                                                      




                                                           










































































                                                                                                            

                                                           












                                                                     





























                                                                                 
// Copyright (c) Copyright (c) Hercules Dev Team, licensed under GNU GPL.
// Copyright (c) 2014 - 2015 Evol developers

#include "common/hercules.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "common/HPMi.h"
#include "common/nullpo.h"
#include "common/utils.h"
#include "common/random.h"

#include "map/itemdb.h"
#include "map/mob.h"
#include "map/pc.h"

#include "plugins/HPMHooking.h"

#include "emap/data/itemd.h"
#include "emap/data/mobd.h"
#include "emap/struct/itemdext.h"
#include "emap/struct/mobdext.h"

// copy from common/utils.c
/**
 * Applies a percentual rate modifier.
 *
 * @param value The base value.
 * @param rate  The rate modifier to apply.
 * @param stdrate The rate modifier's divider (rate == stdrate => 100%).
 * @return The modified value.
 */
int64 apply_percentrate64(int64 value, int rate, int stdrate)
{
    Assert_ret(stdrate > 0);
    Assert_ret(rate >= 0);
    if (rate == stdrate)
        return value;
    if (rate == 0)
        return 0;
    if (INT64_MAX / rate < value)
    {
        // Give up some precision to prevent overflows
        return value / stdrate * rate;
    }
    return value * rate / stdrate;
}

bool ebattle_check_arrows_post(bool retVal,
                               struct map_session_data *sd)
{
    if (retVal == true)
    {
        if (!sd)
            return retVal;
        const int ammoIndex = sd->equip_index[EQI_AMMO];
        if (ammoIndex < 0)
            return true;

        const int ammoId = sd->inventory_data[ammoIndex]->nameid;
        if (ammoId <= 0)
            return true;

        const int weaponIndex = sd->equip_index[EQI_HAND_L];
        if (weaponIndex < 0)
            return true;

        struct ItemdExt *data = itemd_get(sd->inventory_data[weaponIndex]);
        if (!data)
            return true;

        const int sz = VECTOR_LENGTH(data->allowedAmmo);

        if (sz == 0) // allow any ammo if AllowedAmmo list is empty
            return true;

        for (int f = 0; f < sz; f ++)
        {
            const int id = VECTOR_INDEX(data->allowedAmmo, f);
            if (ammoId == id)
                return true;
        }
        clif->arrow_fail(sd, 0);
        return false;
    }
    return false;
}



struct Damage ebattle_calc_weapon_attack_post(struct Damage retVal,
                                              struct block_list *src,
                                              struct block_list *target,
                                              uint16 skill_id,
                                              uint16 skill_lv,
                                              int wflag __attribute__ ((unused)))
{
    // Rebuild Infinite Defense Rule
	struct status_data *tstatus = status->get_status_data(target);
    int infdef = 0;
	infdef = (tstatus->mode&MD_PLANT && skill_id != RA_CLUSTERBOMB?1:0);
#ifdef RENEWAL
	if (skill_id == HT_FREEZINGTRAP)
		infdef = 0;
#endif
	if (!infdef && target->type == BL_SKILL) {
		const struct skill_unit *su = BL_UCCAST(BL_SKILL, target);
		if (su->group->unit_id == UNT_REVERBERATION)
			infdef = 1; // Reverberation takes 1 damage
	}

    // ML: Crits ignore defense (estimate)
    // (regardless of source or target)
    if (!infdef && retVal.type == BDT_CRIT) {
        // Minimum damage for criticals (per ML rule)
        if (retVal.damage <= 10)
            retVal.damage=10;
        /* This needs fixing
        int pnerf;
        pnerf=battle->calc_defense(BF_WEAPON, src, target, skill_id, skill_lv, retVal.damage, 0, 0);
        retVal.damage+=retVal.damage-pnerf;
        */
    }

    if (src == NULL)
        return retVal;

    struct map_session_data *sd = BL_CAST(BL_PC, src);
    if (sd == NULL)
        return retVal;

    struct mob_data *md = BL_CAST(BL_MOB, target);
    if (md == NULL)
        return retVal;

    struct MobdExt *data = mobd_get_by_mob(md);
    if (data == NULL)
        return retVal;

    int mod = 0;
    if (skill_id > 0)
    {
        const int idx = skill->get_ele(skill_id, skill_lv);
        mod = data->eleResist[idx];
    }
    else
    {
        mod = data->weaponAttacks[sd->weapontype1];
    }
    retVal.damage = apply_percentrate64(retVal.damage, mod, 10000);
    retVal.damage2 = apply_percentrate64(retVal.damage2, mod, 10000);
    return retVal;
}



struct Damage ebattle_calc_magic_attack_post(struct Damage retVal,
                                              struct block_list *src,
                                              struct block_list *target,
                                              uint16 skill_id,
                                              uint16 skill_lv,
                                              int mflag __attribute__ ((unused)))
{
    // Skip if things went wrong
    if (src == NULL || target == NULL)
		return retVal;

    // Rebuild SD and SC data
	struct map_session_data *sd, *tsd;
	struct status_change *sc = status->get_sc(src);
	struct status_change *tsc = status->get_sc(target);
	struct status_data *sstatus = status->get_status_data(src);
	struct status_data *tstatus = status->get_status_data(target);

	sd = BL_CAST(BL_PC, src);
	tsd = BL_CAST(BL_PC, target);

    // Skip if things went wrong
    if (sd == NULL || tsd == NULL)
		return retVal;

    // Skip checking as there are no status changes active.
	if (sc && !sc->count)
		sc = NULL;
	if (tsc && !tsc->count)
		tsc = NULL;

    // Magic attacks can be blocked by shields.
    // This terminates everything
	if (tstatus->flee2 && rnd()%1000 < tstatus->flee2) {
		retVal.type = BDT_PDODGE;
		retVal.dmg_lv=ATK_LUCKY;
		if (retVal.div_ < 0) retVal.div_*=-1;
		retVal.damage=0;
		retVal.damage2=0;
		return retVal;
	}

    // Rebuild Infinite Defense Rule
    int infdef = 0;
	infdef = (tstatus->mode&MD_PLANT) ? 1 : 0;
	if (!infdef && target->type == BL_SKILL) {
		const struct skill_unit *su = BL_UCCAST(BL_SKILL, target);
		if (su->group->unit_id == UNT_REVERBERATION)
			infdef = 1; // Reverberation takes 1 damage
	}

    // Not infinite defense, extra calculations
    if (!infdef) {
        // Allow magic attacks to crit
    	if ( sstatus->cri ) {
    		int cri = sstatus->cri;

            // Race critical bonuses
			cri+= sd->critaddrace[tstatus->race];

            // Critical Defense
            cri -= status->get_lv(target) / 5 + (3 * status_get_luk(target))/2;

            // Magic deals in average, 3x less criticals
            cri = cri / 3;

            // Sleeping target?
		    if( tsc && tsc->data[SC_SLEEP] )
			    cri <<= 1;

            // Official critical defense calculation
		    if(tsd && tsd->bonus.critical_def)
			    cri = cri * ( 100 - tsd->bonus.critical_def ) / 100;

            // And finally, check if a crit!
		    if (rnd()%1000 < cri) {
                // Damage +40% (official behavior) and CRITDMG bonus
                // Magic Crits are 50% weaker, because they rarely miss
                retVal.damage=apply_percentrate64(retVal.damage, (40+sd->bonus.crit_atk_rate)/2+100, 100);
                retVal.damage2=apply_percentrate64(retVal.damage2, (40+sd->bonus.crit_atk_rate)/2+100, 100);
        		retVal.type = BDT_CRIT;
            }
        }

    // Err, enough maths for today
    }
    // Apply 4144 code if relevant
    if (src == NULL || sd == NULL)
        return retVal;

    struct mob_data *md = BL_CAST(BL_MOB, target);
    if (md == NULL)
        return retVal;

    struct MobdExt *data = mobd_get_by_mob(md);
    if (data == NULL)
        return retVal;

    int mod = 0;
    if (skill_id > 0)
    {
        const int idx = skill->get_ele(skill_id, skill_lv);
        mod = data->eleResist[idx];
    }
    else
    {
        mod = data->weaponAttacks[sd->weapontype1];
    }
    retVal.damage = apply_percentrate64(retVal.damage, mod, 10000);
    retVal.damage2 = apply_percentrate64(retVal.damage2, mod, 10000);

    return retVal;
}



enum damage_lv ebattle_weapon_attack_pre(struct block_list **srcPtr,
                                         struct block_list **targetPtr,
                                         int64 *tickPtr __attribute__ ((unused)),
                                         int *flagPtr __attribute__ ((unused)))
{
    struct block_list *src = *srcPtr;
    struct block_list *target = *targetPtr;

    nullpo_retr(ATK_NONE, src);
    nullpo_retr(ATK_NONE, target);

    struct map_session_data *sd = BL_CAST(BL_PC, src);
    if (sd == NULL)
        return ATK_NONE;

    const int weaponIndex = sd->equip_index[EQI_HAND_L];
    if (weaponIndex < 0)
        return ATK_NONE;

    struct ItemdExt *data = itemd_get(sd->inventory_data[weaponIndex]);
    if (!data)
        return ATK_NONE;

    if (distance_bl(src, target) < data->minRange)
    {   // if range between player and target > minRange, then dont attack
        hookStop();
        return ATK_NONE;
    }
    return ATK_NONE;
}