Go Back   EQEmulator Home > EQEmulator Forums > Development > Development::Development

Development::Development Forum for development topics and for those interested in EQEMu development. (Not a support forum)

Reply
 
Thread Tools Display Modes
  #1  
Old 02-29-2008, 07:22 AM
Bulle
Hill Giant
 
Join Date: Jan 2008
Posts: 102
Default

To put it simply, the difference between a rule and a hook is how you write it, and hence the power of it.

A rule is merely a number (simplifying here) allowing you to tweak a pre-set behaviour, which a developer has coded. It requires no special skill to use except understanding what the number's impact is.

A hook is a portion of code (let's say 10 lines long) that is called instead or in addition of a regular server computation. It requires you to know how to code a C++ function, but it is much more powerful. In the spirit it is like a short script, just it is in C++, so you need to compile the code yada yada.

Best is to use an example : resist chance for a spell.
Current rules allow you to specify a resist base percentage (ResistChance), a resist multiplier (ResisstMod), a level difference after which no resist is possible for the PC (AutoResistDiff) and the proportion of partial resists (PartialHitchance). The behaviour using these variables is written for you, you cannot change it.
On my test server, instead, I allowed replacing the full resist chance computation with a hook. By defining the hook and implementing in C++ as a function accepting a few parameters and returning the resist chance, I can compute it as I want. In my case I do something on the line of the Shards of Dalaya server, the CHA of the caster influences the resist chance (and the WIS of the target decreases it).

Why not just replace the existing server code ? Because keeping the changes to the original server code is important if you want to integrate new changes done on the main server easily in your custom server. Once the hook is in place (and it can be put in the main server code as by default it changes nothing to the "live-like" server behaviour) custom server developers do not need to touch the original code to significantly tune some rules.

That was my goal. I wanted to implement a score of custom rules, but still keep my server "close to original" EQEmu. In the end I could do most of my changes with hooks and implemented :
- overridding some player choices during character creation
- rewriting the resist chance for spells
- rewriting the melee to-hit chance, damage, mitigation
- rewriting the mana regen
- rewriting the fizzle chance
- rewriting the AC computation (the client cannot show the server-used score, too bad but inevitable)
- rewriting the EXP per level

With minimal and not disrupting changes to the current server code.

I hope it helped explain the difference between rule variables, hooks, and utter server code change. The idea is that hooks could be part of the regular server code "as options ready to be used", on the developer choice, and without having such checks like "if server is guild wars". It would be instead : if designer wants something special we call it, otherwise "live" implementation.
Reply With Quote
  #2  
Old 03-01-2008, 08:49 PM
Bulle
Hill Giant
 
Join Date: Jan 2008
Posts: 102
Default New hook version + Source/common/rulesys.cpp

Here is the latest version of the hooks implementation. This is meant to replace what has been already submitted in this post.
First post is the code enabling the use of hooks as rules. It defines some hook rules (the ones I use), but does not actually "wire" them in the server code.
In short, adding this code changes nothing to the server behaviour, it just introduces the capability. Next posts will add the code to put all the hooks I use in place, which the server development team may want to include or not.
Hooks not wired can be removed from ruletypes.h altogether without harm (those are all the RULE_HOOK entries).
A word of warning : I have not checked the Unix version in a long while, so do not rely on it. If this code makes it to the official server then I will take the time to complete/fix the Unix part.
On the other hand I have not had to touch the Windows version in a little while, so it is probably as stable as I can make it for the moment.

Index: Source/common/rulesys.cpp
================================================== =================
RCS file: /cvsroot/eqemulator/EQEmuCVS/Source/common/rulesys.cpp,v
retrieving revision 1.2.2.2
diff -u -b -B -r1.2.2.2 rulesys.cpp
--- Source/common/rulesys.cpp 31 Oct 2006 02:02:54 -0000 1.2.2.2
+++ Source/common/rulesys.cpp 2 Mar 2008 08:41:01 -0000
@@ -20,7 +20,10 @@
#include "logsys.h"
#include "database.h"
#include "MiscFunctions.h"
-
+#ifndef WIN32
+/* On Linux, include the headers for libdl for dynamic library loading */
+#include <dlfcn.h>
+#endif /* !WIN32 */
/*

FatherNitwit: Added new rules subsystem to allow game rules to be changed
@@ -73,7 +76,7 @@
"InvalidCategory"
};

-const RuleManager::RuleInfo RuleManager::s_RuleInfo[_IntRuleCount+_RealRuleCount+_BoolRuleCount+1] = {
+const RuleManager::RuleInfo RuleManager::s_RuleInfo[_IntRuleCount+_RealRuleCount+_BoolRuleCount+_HookR uleCount+1] = {
/* this is done in three steps so we can reliably get to them by index*/
#define RULE_INT(cat, rule, default_value) \
{ #cat ":" #rule, Category__##cat, IntRule, Int__##rule },
@@ -84,6 +87,9 @@
#define RULE_BOOL(cat, rule, default_value) \
{ #cat ":" #rule, Category__##cat, BoolRule, Bool__##rule },
#include "ruletypes.h"
+ #define RULE_HOOK(cat, rule, default_value) \
+ { #cat ":" #rule, Category__##cat, HookRule, Hook__##rule },
+ #include "ruletypes.h"
{ "Invalid Rule", _CatCount, IntRule }
};

@@ -135,6 +141,20 @@
if(rule_name == NULL || rule_value == NULL)
return(false);

+ /* World ignores rules starting with (zone), zone ignores rules starting with (world), both skip (world) or (zone) if at the beginning */
+#ifdef WORLD
+ if(strncmp(rule_name, "(zone) ", strlen("(zone) ")) == 0)
+ return true;
+#endif /* WORLD */
+#ifdef ZONE
+ if(strncmp(rule_name, "(world) ", strlen("(world) ")) == 0)
+ return true;
+#endif /* ZONE */
+ if(strncmp(rule_name, "(zone) ", strlen("(zone) ")) == 0)
+ rule_name = rule_name + strlen("(zone) ");
+ else if(strncmp(rule_name, "(world) ", strlen("(world) ")) == 0)
+ rule_name = rule_name + strlen("(world) ");
+
RuleType type;
uint16 index;
if(!_FindRule(rule_name, type, index))
@@ -149,6 +169,57 @@
m_RuleRealValues[index] = atof(rule_value);
_log(RULES__CHANGE, "Set rule %s to value %.13f", rule_name, m_RuleRealValues[index]);
break;
+ case HookRule:
+ { char *fLibraryName = NULL, *fFunctionName = NULL;
+ Hook ThisHook = NULL;
+
+ const char *Colon = strchr(rule_value, ':');
+ if(Colon == NULL)
+ _log(RULES__ERROR, "Badly-formed hook value '%s' (rule '%s'). It must contain a colon (. Ignoring this hook", rule_value, rule_name);
+ else
+ { fLibraryName = (char *) malloc(Colon - rule_value + 128 + 1); /* 128 = let's assume it is more than enough to append a file extension to the library if needed */
+ strncpy(fLibraryName, rule_value, Colon - rule_value); fLibraryName[Colon - rule_value] = '\0';
+ fFunctionName = (char *) malloc(strlen(rule_value) - (Colon - rule_value + 1) + 1);
+ strcpy(fFunctionName, Colon + 1);
+ }
+#ifdef WIN32
+ HMODULE ThisLibrary = NULL;
+ if(fLibraryName != NULL)
+ { strcat(fLibraryName, ".dll");
+ ThisLibrary = LoadLibrary(fLibraryName);
+ if(ThisLibrary == NULL)
+ _log(RULES__ERROR, "Cannot load library '%s' needed by hook '%s' (rule '%s'). Ignoring this hook", fLibraryName, rule_value, rule_name);
+ }
+
+ if(ThisLibrary != NULL)
+ { ThisHook = GetProcAddress(ThisLibrary, fFunctionName);
+ if(ThisHook == NULL)
+ _log(RULES__ERROR, "Cannot load library function '%s' needed by hook '%s' (rule '%s'). Ignoring this hook", fFunctionName, rule_value, rule_name);
+ }
+#else /* WIN32 */
+ void *ThisLibrary = NULL;
+ if(fLibraryName != NULL)
+ { strcat(fLibraryName, ".so");
+ ThisLibrary = dlopen(fLibraryName, RTLD_NOW | RTLD_GLOBAL);
+ if(ThisLibrary == NULL)
+ _log(RULES__ERROR, "Cannot load library '%s' needed by hook '%s' (rule '%s'). Ignoring this hook", fLibraryName, rule_value, rule_name);
+ }
+
+ if(ThisLibrary != NULL)
+ { ThisHook = dlsym(ThisLibrary, fFunctionName);
+ if(ThisHook == NULL)
+ _log(RULES__ERROR, "Cannot load library function '%s' needed by hook '%s' (rule '%s'). Ignoring this hook", fFunctionName, rule_value, rule_name);
+ }
+ /*_log(RULES__ERROR, "The rule hooks are not yet implemented on this platform. Ignoring hook '%s' for fule '%s'", rule_value, rule_name);*/
+#endif /* WIN32 */
+ if(ThisHook != NULL)
+ { m_RuleHookValues[index] = ThisHook;
+ _log(RULES__CHANGE, "Set rule %s to value %d", rule_name, (int) m_RuleHookValues[index]);
+ }
+ if(fLibraryName != NULL) { free(fLibraryName); fLibraryName = NULL; }
+ if(fFunctionName != NULL) { free(fFunctionName); fFunctionName = NULL; }
+ break;
+ }
case BoolRule:
bool val = false;
if(!strcasecmp(rule_value, "on") || !strcasecmp(rule_value, "true") || !strcasecmp(rule_value, "yes") || !strcasecmp(rule_value, "enabled") || !strcmp(rule_value, "1"))
@@ -172,6 +243,8 @@
m_RuleRealValues[ Real__##rule ] = default_value;
#define RULE_BOOL(cat, rule, default_value) \
m_RuleBoolValues[ Bool__##rule ] = default_value;
+ #define RULE_HOOK(cat, rule, default_value) \
+ m_RuleHookValues[ Hook__##rule ] = default_value;
#include "ruletypes.h"
}

@@ -202,6 +275,8 @@
return(s_RuleInfo[index+_IntRuleCount].name);
case BoolRule:
return(s_RuleInfo[index+_IntRuleCount+_RealRuleCount].name);
+ case HookRule:
+ return(s_RuleInfo[index+_IntRuleCount+_RealRuleCount+_BoolRuleCount].name);
}
//should never happen
return("InvalidRule??");
@@ -236,6 +311,9 @@
for(r = 0; r < _BoolRuleCount; r++) {
_SaveRule(db, BoolRule, r);
}
+ for(r = 0; r < _HookRuleCount; r++) {
+ _SaveRule(db, HookRule, r);
+ }
}


@@ -280,7 +358,10 @@
void RuleManager::_SaveRule(Database *db, RuleType type, uint16 index) {
char vstr[16];

- switch(type) {
+ if(type == HookRule)
+ _log(RULES__ERROR, "Impossible to save hook rule %s in the database. Hook rule saving is not supported", _GetRuleName(type, index));
+ else
+ { switch(type) {
case IntRule:
sprintf(vstr, "%d", m_RuleIntValues[index]);
break;
@@ -302,6 +383,7 @@
_log(RULES__ERROR, "Fauled to set rule in the database: %s: %s", query,errbuf);
}
safe_delete_array(query);
+ }
}
Reply With Quote
  #3  
Old 03-01-2008, 08:49 PM
Bulle
Hill Giant
 
Join Date: Jan 2008
Posts: 102
Default common/rulesys.h

Index: Source/common/rulesys.h
================================================== =================
RCS file: /cvsroot/eqemulator/EQEmuCVS/Source/common/rulesys.h,v
retrieving revision 1.2.2.2
diff -u -b -B -r1.2.2.2 rulesys.h
--- Source/common/rulesys.h 31 Oct 2006 02:02:54 -0000 1.2.2.2
+++ Source/common/rulesys.h 2 Mar 2008 08:41:02 -0000
@@ -35,6 +35,8 @@
rules->GetRealRule( RuleManager::Real__##rule )
#define RuleB(cat, rule) \
rules->GetBoolRule( RuleManager::Bool__##rule )
+#define RuleH(cat, rule) \
+ rules->GetHookRule( RuleManager::Hook__##rule )


#include <vector>
@@ -42,6 +44,7 @@
#include <map>

#include "types.h"
+#include "../common/default_Hooks.h"

class Database;

@@ -70,6 +73,13 @@
} BoolType;

typedef enum {
+ #define RULE_HOOK(cat, rule, default_value) \
+ Hook__##rule,
+ #include "ruletypes.h"
+ _HookRuleCount
+ } HookType;
+
+ typedef enum {
#define RULE_CATEGORY(catname) \
Category__##catname,
#include "ruletypes.h"
@@ -79,9 +89,10 @@
static const IntType InvalidInt = _IntRuleCount;
static const RealType InvalidReal = _RealRuleCount;
static const BoolType InvalidBool = _BoolRuleCount;
+ static const HookType InvalidHook = _HookRuleCount;
static const CategoryType InvalidCategory = _CatCount;

- static const uint32 _RulesCount = _IntRuleCount+_RealRuleCount+_BoolRuleCount;
+ static const uint32 _RulesCount = _IntRuleCount+_RealRuleCount+_BoolRuleCount+_HookR uleCount;

RuleManager();

@@ -89,11 +100,13 @@
inline int GetIntRule (IntType t) const { return(m_RuleIntValues[t] ); }
inline float GetRealRule(RealType t) const { return(m_RuleRealValues[t]); }
inline bool GetBoolRule(BoolType t) const { return(m_RuleBoolValues[t]); }
+ inline Hook GetHookRule(HookType t) const { return(m_RuleHookValues[t]); }

//management routines
static const char *GetRuleName(IntType t) { return(s_RuleInfo[t].name); }
static const char *GetRuleName(RealType t) { return(s_RuleInfo[t+_IntRuleCount].name); }
static const char *GetRuleName(BoolType t) { return(s_RuleInfo[t+_IntRuleCount+_RealRuleCount].name); }
+ static const char *GetRuleName(HookType t) { return(s_RuleInfo[t+_IntRuleCount+_RealRuleCount+_BoolRuleCount].name); }
static uint32 CountRules() { return(_RulesCount); }
static CategoryType FindCategory(const char *catname);
bool ListRules(const char *catname, std::vector<const char *> &into);
@@ -117,11 +130,13 @@
int m_RuleIntValues [_IntRuleCount ];
float m_RuleRealValues[_RealRuleCount];
bool m_RuleBoolValues[_BoolRuleCount];
+ Hook m_RuleHookValues[_HookRuleCount];

typedef enum {
IntRule,
RealRule,
- BoolRule
+ BoolRule,
+ HookRule
} RuleType;

static bool _FindRule(const char *rule_name, RuleType &type_into, uint16 &index_into);
Reply With Quote
  #4  
Old 03-01-2008, 08:50 PM
Bulle
Hill Giant
 
Join Date: Jan 2008
Posts: 102
Default common/ruletypes.h

Index: Source/common/ruletypes.h
================================================== =================
RCS file: /cvsroot/eqemulator/EQEmuCVS/Source/common/ruletypes.h,v
retrieving revision 1.3.2.8
diff -u -b -B -r1.3.2.8 ruletypes.h
--- Source/common/ruletypes.h 21 Feb 2007 16:04:19 -0000 1.3.2.8
+++ Source/common/ruletypes.h 2 Mar 2008 08:41:02 -0000
@@ -13,6 +13,9 @@
#ifndef RULE_BOOL
#define RULE_BOOL(cat, rule, default_value)
#endif
+#ifndef RULE_HOOK
+#define RULE_HOOK(cat, rule, default_value)
+#endif
#ifndef RULE_CATEGORY_END
#define RULE_CATEGORY_END()
#endif
@@ -20,6 +23,15 @@



+RULE_CATEGORY( Attack )
+#ifdef ZONE
+RULE_HOOK( Attack, ToHitChance, (Hook) NULL )
+RULE_HOOK( Attack, ClientDamageRange, (Hook) NULL )
+RULE_HOOK( Attack, NpcDamageRange, (Hook) NULL )
+RULE_HOOK( Attack, Mitigation, (Hook) NULL )
+#endif /* ZONE */
+RULE_CATEGORY_END()
+
RULE_CATEGORY( Character )
RULE_INT ( Character, MaxLevel, 65 )
RULE_INT ( Character, DeathExpLossLevel, 6 )
@@ -32,6 +44,13 @@
RULE_INT ( Character, ManaRegenMultiplier, 100)
RULE_INT ( Character, EnduranceRegenMultiplier, 100)
RULE_INT ( Character, ConsumptionMultiplier, 100) //item's hunger restored = this value * item's food level, 100 = normal, 50 = people eat 2x as fast, 200 = people eat 2x as slow
+#ifdef ZONE
+RULE_HOOK( Character, PostAddItemBonuses, (Hook) NULL )
+RULE_HOOK( Character, CalcAC, (Hook) NULL )
+RULE_HOOK( Character, EXPForLevel, (Hook) NULL )
+RULE_HOOK( Character, ManaRegen, (Hook) NULL )
+RULE_HOOK( Character, ChanceOfSkillIncrease, (Hook) NULL )
+#endif /* ZONE */
RULE_CATEGORY_END()

RULE_CATEGORY( Guild )
@@ -61,6 +80,10 @@
RULE_REAL (Spells, ResistChance, 2.0) //chance to resist given no resists and same level
RULE_REAL (Spells, ResistMod, 0.40) //multiplier, chance to resist = this * ResistAmount
RULE_REAL (Spells, PartialHitChance, 0.7) //The chance when a spell is resisted that it will partial hit.
+#ifdef ZONE
+RULE_HOOK( Spells, FizzleChance, (Hook) NULL )
+RULE_HOOK( Spells, FinalResistChance, (Hook) NULL )
+#endif /* ZONE */
RULE_CATEGORY_END()

RULE_CATEGORY( Combat )
@@ -71,10 +94,28 @@
RULE_REAL ( Combat, ClientBaseCritChance, 0.0 ) //The base crit chance for all clients, this will stack with warrior's/zerker's crit chance.
RULE_CATEGORY_END()

+RULE_CATEGORY( CharacterCreation )
+#ifdef WORLD
+RULE_HOOK( CharacterCreation, ChangeCreationInfo, (Hook) NULL )
+#endif /* WORLD */
+RULE_CATEGORY_END()
+
+RULE_CATEGORY( Random )
+RULE_HOOK( Random, RandomFloat, (Hook) NULL )
+RULE_CATEGORY_END()
+
+RULE_CATEGORY( XP )
+#ifdef ZONE
+RULE_HOOK( XP, PreChange, (Hook) NULL )
+#endif /* ZONE */
+RULE_CATEGORY_END()
+
+
#undef RULE_CATEGORY
#undef RULE_INT
#undef RULE_REAL
#undef RULE_BOOL
+#undef RULE_HOOK
#undef RULE_CATEGORY_END
Reply With Quote
  #5  
Old 03-01-2008, 08:55 PM
Bulle
Hill Giant
 
Join Date: Jan 2008
Posts: 102
Default Hook 1 : Tweak Player choices on character creation

Adds the code to the server to process this hook. No effect until the hook is actually implemented in a shared library and activated in the database.

Index: client.cpp
================================================== =================
RCS file: /cvsroot/eqemulator/EQEmuCVS/Source/world/client.cpp,v
retrieving revision 1.12.2.20
diff -u -b -B -r1.12.2.20 client.cpp
--- client.cpp 22 Nov 2006 14:03:34 -0000 1.12.2.20
+++ client.cpp 2 Mar 2008 08:53:44 -0000
@@ -907,6 +907,9 @@
return false;
}

+ if(RuleH(CharacterCreation, ChangeCreationInfo) != NULL)
+ ((Hook_CharacterCreation_ChangeCreationInfo) RuleH(CharacterCreation, ChangeCreationInfo))(cc);
+
// Convert incoming cc_s to the new PlayerProfile_Struct
memset(&pp, 0, sizeof(PlayerProfile_Struct)); // start building the profile
Reply With Quote
  #6  
Old 03-01-2008, 09:00 PM
Bulle
Hill Giant
 
Join Date: Jan 2008
Posts: 102
Default Hooks 2 : custom combat computations

Tweak the to-hit chance
Tweak the AC mitigation effect
Tweak the client and NPC damage ranges


Index: attack.cpp
================================================== =================
RCS file: /cvsroot/eqemulator/EQEmuCVS/Source/zone/attack.cpp,v
retrieving revision 1.28.2.57
diff -u -b -B -r1.28.2.57 attack.cpp
--- attack.cpp 21 Feb 2007 16:04:20 -0000 1.28.2.57
+++ attack.cpp 2 Mar 2008 08:58:41 -0000
@@ -166,7 +166,25 @@
int8 attacker_level = attacker->GetLevel() ? attacker->GetLevel() : 1;
int8 defender_level = defender->GetLevel() ? defender->GetLevel() : 1;

- //
+if(RuleH(Attack, ToHitChance) != NULL)
+{ struct _Hook_Attack_ToHitChance_Parameters Parameters;
+ Parameters.IsPvp = pvpmode;
+ Parameters.AttackerIsClient = attacker->IsClient();
+ Parameters.AttackerLevel = attacker_level;
+ Parameters.AttackerDex = attacker->GetDEX();
+ if(Parameters.AttackerIsClient)
+ { Parameters.AttackerWeaponSkill = attacker->GetSkill(skillinuse);
+ Parameters.AttackerOffense = attacker->GetSkill(OFFENSE);
+ }
+ Parameters.DefenderIsClient = defender->IsClient();
+ Parameters.DefenderLevel = defender_level;
+ Parameters.DefenderAgi = defender->GetAGI();
+ if(Parameters.DefenderIsClient)
+ Parameters.DefenderDefense = defender->GetSkill(DEFENSE);
+ chancetohit = ((Hook_Attack_ToHitChance) RuleH(Attack, ToHitChance))(&Parameters);
+}
+else
+{ //
// we start by giving them a base chance to hit
//
chancetohit = 50;
@@ -232,6 +250,7 @@
chancetohit += (float) ((float)attacker_dex * 0.15f);

mlog(COMBAT__TOHIT, "Applied Defending AGI (%d) and Attacking (DEX-50=%d) yeilding %.2f", defender_agi, attacker_dex, chancetohit);
+}

//divided these bonuses by 4... higher level battles were basically always 95%
//hit chance because of this 50% bonus...
@@ -319,7 +338,8 @@
//if chance to hit is crazy high, that means a discipline is in use, and let it stay there
} else if(chancetohit > 95) {
chancetohit = 95;
- } else if(chancetohit < 30) {
+ }
+ if(RuleH(Attack, ToHitChance) == NULL && chancetohit < 30) {
chancetohit = 30;
}

@@ -514,7 +534,24 @@
// Scorpious2k: Include AC in the calculation

// use serverop variables to set values
- int myac = GetAC();
+if(RuleH(Attack, Mitigation) != NULL)
+{ struct _Hook_Attack_Mitigation_Parameters Parameters;
+ Parameters.AttackerIsClient = other->IsClient();
+ Parameters.AttackerLevel = other->GetLevel();
+ if(Parameters.AttackerIsClient)
+ Parameters.AttackerOffense = GetSkill(OFFENSE);
+
+ Parameters.DefenderIsClient = other->IsClient();
+ Parameters.DefenderLevel = GetLevel();
+ Parameters.DefenderAC = GetAC();
+ if(Parameters.DefenderIsClient)
+ Parameters.DefenderDefense = GetSkill(DEFENSE);
+ Parameters.Damage = damage;
+ ((Hook_Attack_Mitigation) RuleH(Attack, Mitigation))(&Parameters);
+ damage = Parameters.Damage;
+}
+else
+{ int myac = GetAC();
if (damage > 0 && myac > 0) {
int acfail=1000;
char tmp[10];
@@ -551,6 +588,7 @@
mlog(COMBAT__DAMAGE, "AC Damage Reduction: fail chance %d%%. Did not fail.", acfail);
}
}
+}

int aaMit = 0;
switch(GetAA(aaCombatStability)){
@@ -780,7 +818,20 @@
mlog(COMBAT__ATTACKS, "Failed a finishing blow: AA at %d, other level %d, roll %.1f", aa_item, other->GetLevel(), tempchancerand);
}

- min_hit = 1;
+if(RuleH(Attack, ClientDamageRange) != NULL)
+{ struct _Hook_Attack_ClientDamageRange_Parameters Parameters;
+ Parameters.AttackerStr = this->GetSTR();
+ Parameters.AttackerLevel = this->GetLevel();
+ Parameters.AttackerWeaponDamage = weapon_damage;
+ Parameters.AttackerWeaponDelay = (weapon_item == NULL ? 20 : weapon_item->Delay);
+
+ Parameters.DefenderLevel = other->GetLevel();
+ ((Hook_Attack_ClientDamageRange) RuleH(Attack, ClientDamageRange))(&Parameters);
+ min_hit = Parameters.MinHit;
+ max_hit = Parameters.MaxHit;
+}
+else
+{ min_hit = 1;
//This needs to be researched, it seems terribly off. Changed to use offense skill for now instead of weapon since we know that is correct
max_hit = (weapon_damage * (((GetSTR()*20) + (GetSkill(OFFENSE)*15) + (mylevel*10)) / 1000)); // Apply damage formula

@@ -790,6 +841,7 @@
min_hit += damage_bonus;
max_hit += damage_bonus;
}
+}

min_hit = min_hit * (100 + itembonuses.MinDamageModifier + spellbonuses.MinDamageModifier) / 100;

@@ -1295,7 +1347,19 @@
otherlevel = otherlevel ? otherlevel : 1;
mylevel = mylevel ? mylevel : 1;

- //instead of calcing damage in floats lets just go straight to ints
+if(RuleH(Attack, ClientDamageRange) != NULL)
+{ struct _Hook_Attack_NpcDamageRange_Parameters Parameters;
+ Parameters.AttackerStr = this->GetSTR();
+ Parameters.AttackerLevel = this->GetLevel();
+ Parameters.AttackerMaxDamage = max_dmg;
+ Parameters.AttackerDelay = (int) (36.0 * (100.0f + attack_speed) / 100.0);
+
+ Parameters.DefenderLevel = other->GetLevel();
+ ((Hook_Attack_NpcDamageRange) RuleH(Attack, NpcDamageRange))(&Parameters);
+ damage = MakeRandomInt(Parameters.MinHit, Parameters.MaxHit);
+}
+else
+{ //instead of calcing damage in floats lets just go straight to ints
damage = MakeRandomInt(min_dmg, max_dmg);

//check if we're hitting above our max or below it.
@@ -1307,6 +1371,7 @@
mlog(COMBAT__DAMAGE, "Damage (%d) is above max (%d). Setting to max.", damage, max_dmg);
damage = max_dmg;
}
+}

//THIS IS WHERE WE CHECK TO SEE IF WE HIT:
if(other->IsClient() && other->CastToClient()->IsSitting()) {
Reply With Quote
  #7  
Old 03-01-2008, 09:01 PM
Bulle
Hill Giant
 
Join Date: Jan 2008
Posts: 102
Default

One word about the previous hooks (and some coming others) : I did not indent the hook code as should normally be. the reason is that it avoids getting changes in diffs on all the regular code I enclosed in "ifs".
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

   

All times are GMT -4. The time now is 04:30 PM.


 

Everquest is a registered trademark of Daybreak Game Company LLC.
EQEmulator is not associated or affiliated in any way with Daybreak Game Company LLC.
Except where otherwise noted, this site is licensed under a Creative Commons License.
       
Powered by vBulletin®, Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Template by Bluepearl Design and vBulletin Templates - Ver3.3