mirror of
https://github.com/EQEmu/Server.git
synced 2026-01-04 03:13:52 +00:00
Implemented Physical Resists consistent with live.
SQL to add new column 'PhR' to npc_types Values to populate table based on extensive parsing. Fixes for spell projectile code.
This commit is contained in:
parent
2cdd50b9e9
commit
4b14ec53f1
@ -1,5 +1,11 @@
|
||||
EQEMu Changelog (Started on Sept 24, 2003 15:50)
|
||||
-------------------------------------------------------
|
||||
== 04/04/2014 ==
|
||||
Kayen: Implemented 'Physical Resists' (Resist Type 9) to be consistent with live based on extensive parsing.
|
||||
SQL will add new field to npc_types 'PhR' and fill in database with values consistent with observations.
|
||||
|
||||
Optional SQL: utils/sql/git/optional/2014_04_04_PhysicalResists.sql
|
||||
|
||||
== 04/03/2014 ==
|
||||
Kayen: Implemented live like spell projectiles (ie. Mage Bolts).
|
||||
|
||||
|
||||
@ -258,7 +258,7 @@ typedef enum {
|
||||
#define SE_Familiar 108 // implemented
|
||||
#define SE_SummonItemIntoBag 109 // implemented - summons stuff into container
|
||||
//#define SE_IncreaseArchery 110 // not used
|
||||
#define SE_ResistAll 111 // implemented
|
||||
#define SE_ResistAll 111 // implemented - Note: Physical Resists are not modified by this effect.
|
||||
#define SE_CastingLevel 112 // implemented
|
||||
#define SE_SummonHorse 113 // implemented
|
||||
#define SE_ChangeAggro 114 // implemented - Hate modifing buffs(ie horrifying visage)
|
||||
|
||||
7
utils/sql/git/required/2014_04_04_PhysicalResist.txt
Normal file
7
utils/sql/git/required/2014_04_04_PhysicalResist.txt
Normal file
@ -0,0 +1,7 @@
|
||||
ALTER TABLE `npc_types` ADD `PhR` smallint( 5 ) UNSIGNED NOT NULL DEFAULT '0' AFTER `Corrup`;
|
||||
|
||||
-- Approximate baseline live npc values based on extensive parsing.
|
||||
UPDATE npc_types SET PhR = 10 WHERE PhR = 0 AND level <= 50;
|
||||
UPDATE npc_types SET PhR = (10 + (level - 50)) WHERE PhR = 0 AND (level > 50 AND level <= 60);
|
||||
UPDATE npc_types SET PhR = (20 + ((level - 60)*4)) WHERE PhR = 0 AND level > 60;
|
||||
|
||||
@ -262,6 +262,7 @@
|
||||
#define DISCIPLINE_REUSE_MSG 5807 //You can use the ability %1 again in %2 hour(s) %3 minute(s) %4 seconds.
|
||||
#define DISCIPLINE_REUSE_MSG2 5808 //You can use the ability %1 again in %2 minute(s) %3 seconds.
|
||||
#define FAILED_TAUNT 5811 //You have failed to taunt your target.
|
||||
#define PHYSICAL_RESIST_FAIL 5817 //Your target avoided your %1 ability.
|
||||
#define AA_NO_TARGET 5825 //You must first select a target for this ability!
|
||||
#define FORAGE_MASTERY 6012 //Your forage mastery has enabled you to find something else!
|
||||
#define GUILD_BANK_CANNOT_DEPOSIT 6097 // Cannot deposit this item. Containers must be empty, and only one of each LORE and no NO TRADE or TEMPORARY items may be deposited.
|
||||
|
||||
@ -192,6 +192,7 @@ public:
|
||||
virtual int32 GetActSpellCasttime(uint16 spell_id, int32 casttime);
|
||||
float ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override = false,
|
||||
int resist_override = 0, bool CharismaCheck = false, bool CharmTick = false, bool IsRoot = false);
|
||||
int ResistPhysical(int level_diff, uint8 caster_level);
|
||||
uint16 GetSpecializeSkillValue(uint16 spell_id) const;
|
||||
void SendSpellBarDisable();
|
||||
void SendSpellBarEnable(uint16 spellid);
|
||||
@ -222,6 +223,8 @@ public:
|
||||
uint16 CastingSpellID() const { return casting_spell_id; }
|
||||
bool DoCastingChecks();
|
||||
bool TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier);
|
||||
void SpellProjectileEffect();
|
||||
bool TrySpellProjectile(Mob* spell_target, uint16 spell_id);
|
||||
|
||||
//Buff
|
||||
void BuffProcess();
|
||||
@ -252,7 +255,6 @@ public:
|
||||
void SendBuffsToClient(Client *c);
|
||||
inline Buffs_Struct* GetBuffs() { return buffs; }
|
||||
void DoGravityEffect();
|
||||
void SpellProjectileEffect();
|
||||
void DamageShield(Mob* other, bool spell_ds = false);
|
||||
int32 RuneAbsorb(int32 damage, uint16 type);
|
||||
bool FindBuff(uint16 spellid);
|
||||
@ -338,6 +340,7 @@ public:
|
||||
inline virtual int16 GetPR() const { return PR + itembonuses.PR + spellbonuses.PR; }
|
||||
inline virtual int16 GetCR() const { return CR + itembonuses.CR + spellbonuses.CR; }
|
||||
inline virtual int16 GetCorrup() const { return Corrup + itembonuses.Corrup + spellbonuses.Corrup; }
|
||||
inline virtual int16 GetPhR() const { return PhR; }
|
||||
inline StatBonuses GetItemBonuses() const { return itembonuses; }
|
||||
inline StatBonuses GetSpellBonuses() const { return spellbonuses; }
|
||||
inline StatBonuses GetAABonuses() const { return aabonuses; }
|
||||
@ -916,6 +919,7 @@ protected:
|
||||
int16 DR;
|
||||
int16 PR;
|
||||
int16 Corrup;
|
||||
int16 PhR;
|
||||
bool moving;
|
||||
int targeted;
|
||||
bool findable;
|
||||
|
||||
@ -158,6 +158,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
|
||||
FR = d->FR;
|
||||
PR = d->PR;
|
||||
Corrup = d->Corrup;
|
||||
PhR = d->PhR;
|
||||
|
||||
STR = d->STR;
|
||||
STA = d->STA;
|
||||
@ -2062,6 +2063,8 @@ void NPC::CalcNPCResists() {
|
||||
PR = (GetLevel() * 11)/10;
|
||||
if (!Corrup)
|
||||
Corrup = 15;
|
||||
if (!PhR)
|
||||
PhR = 10;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -6020,3 +6020,73 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id){
|
||||
|
||||
/*For mage 'Bolt' line and other various spells.
|
||||
-This is mostly accurate for how the modern clients handle this effect.
|
||||
-It was changed at some point to use an actual projectile as done here (opposed to a particle effect in classic)
|
||||
-The projectile graphic appears to be that of 'Ball of Sunlight' ID 80648 and will be visible to anyone in SoF+
|
||||
-There is no LOS check to prevent a bolt from being cast. If you don't have LOS your bolt simply goes into whatever barrier
|
||||
and you lose your mana. If there is LOS the bolt will lock onto your target and the damage is applied when it hits the target.
|
||||
-If your target moves the bolt moves with it in any direction or angle (consistent with other projectiles).
|
||||
-The way this is written once a bolt is cast a timer checks the distance from the initial cast to the target repeatedly
|
||||
and calculates at what predicted time the bolt should hit that target in client_process (therefore accounting for any target movement).
|
||||
When bolt hits its predicted point the damage is then done to target.
|
||||
Note: Projectile speed of 1 takes 3 seconds to go 100 distance units. Calculations are based on this constant.
|
||||
Live Bolt speed: Projectile speed of X takes 5 seconds to go 300 distance units.
|
||||
Pending Implementation: What this code can not do is prevent damage if the bolt hits a barrier after passing the initial LOS check
|
||||
because the target has moved while the bolt is in motion. (it is rare to actual get this to occur on live in normal game play)
|
||||
*/
|
||||
|
||||
if (!spell_target)
|
||||
return false;
|
||||
|
||||
uint8 anim = spells[spell_id].CastingAnim;
|
||||
int bolt_id = -1;
|
||||
|
||||
//Make sure there is an avialable bolt to be cast.
|
||||
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) {
|
||||
if (projectile_spell_id[i] == 0){
|
||||
bolt_id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bolt_id < 0)
|
||||
return false;
|
||||
|
||||
if (CheckLosFN(spell_target)) {
|
||||
|
||||
projectile_spell_id[bolt_id] = spell_id;
|
||||
projectile_target_id[bolt_id] = spell_target->GetID();
|
||||
projectile_x[bolt_id] = GetX(), projectile_y[bolt_id] = GetY(), projectile_z[bolt_id] = GetZ();
|
||||
projectile_increment[bolt_id] = 1;
|
||||
projectile_timer.Start(250);
|
||||
}
|
||||
|
||||
//Only use fire graphic for fire spells.
|
||||
if (spells[spell_id].resisttype == RESIST_FIRE) {
|
||||
|
||||
if (IsClient()){
|
||||
if (CastToClient()->GetClientVersionBit() <= 4) //Titanium needs alternate graphic.
|
||||
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, 1.5);
|
||||
else
|
||||
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, 1.5);
|
||||
}
|
||||
|
||||
else
|
||||
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, 1.5);
|
||||
|
||||
if (spells[spell_id].CastingAnim == 64)
|
||||
anim = 44; //Corrects for animation error.
|
||||
}
|
||||
|
||||
//Pending other types of projectile graphics. (They will function but with a default arrow graphic for now)
|
||||
else
|
||||
ProjectileAnimation(spell_target,0, 1, 1.5);
|
||||
|
||||
DoAnim(anim, 0, true, IsClient() ? FilterPCSpells : FilterNPCSpells); //Override the default projectile animation.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
230
zone/spells.cpp
230
zone/spells.cpp
@ -1868,70 +1868,6 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
|
||||
}
|
||||
}
|
||||
|
||||
/*For mage 'Bolt' line and other various spells.
|
||||
-This is mostly accurate for how the modern clients handle this effect.
|
||||
-It was changed at some point to use an actual projectile as done here (opposed to a particle effect in classic)
|
||||
-The projectile graphic appears to be that of 'Ball of Sunlight' ID 80648 and will be visible to anyone in SoF+
|
||||
-There is no LOS check to prevent a bolt from being cast. If you don't have LOS your bolt simply goes into whatever barrier
|
||||
and you lose your mana. If there is LOS the bolt will lock onto your target and the damage is applied when it hits the target.
|
||||
-If your target moves the bolt moves with it in any direction or angle (consistent with other projectiles).
|
||||
-The way this is written once a bolt is cast a timer checks the distance from the initial cast to the target repeatedly
|
||||
and calculates at what predicted time the bolt should hit that target in client_process (therefore accounting for any target movement).
|
||||
When bolt hits its predicted point the damage is then done to target.
|
||||
Note: Projectile speed of 1 takes 3 seconds to go 100 distance units. Calculations are based on this constant.
|
||||
Live Bolt speed: Projectile speed of X takes 5 seconds to go 300 distance units.
|
||||
Pending Implementation: What this code can not do is prevent damage if the bolt hits a barrier after passing the initial LOS check
|
||||
because the target has moved while the bolt is in motion. (it is rare to actual get this to occur on live in normal game play)
|
||||
*/
|
||||
if (spell_target && spells[spell_id].targettype == ST_TargetOptional){
|
||||
|
||||
uint8 anim = spells[spell_id].CastingAnim;
|
||||
int bolt_id = -1;
|
||||
|
||||
//Make sure there is an avialable bolt to be cast.
|
||||
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) {
|
||||
if (projectile_spell_id[i] == 0){
|
||||
bolt_id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bolt_id < 0)
|
||||
return false;
|
||||
|
||||
if (CheckLosFN(spell_target)) {
|
||||
|
||||
projectile_spell_id[bolt_id] = spell_id;
|
||||
projectile_target_id[bolt_id] = spell_target->GetID();
|
||||
projectile_x[bolt_id] = GetX(), projectile_y[bolt_id] = GetY(), projectile_z[bolt_id] = GetZ();
|
||||
projectile_increment[bolt_id] = 1;
|
||||
projectile_timer.Start(250);
|
||||
}
|
||||
|
||||
//Only use fire graphic for fire spells.
|
||||
if (spells[spell_id].resisttype == RESIST_FIRE) {
|
||||
|
||||
if (IsClient()){
|
||||
if (CastToClient()->GetClientVersionBit() <= 4) //Titanium needs alternate graphic.
|
||||
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, 1.5);
|
||||
else
|
||||
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, 1.5);
|
||||
}
|
||||
else
|
||||
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, 1.5);
|
||||
|
||||
if (spells[spell_id].CastingAnim == 64)
|
||||
anim = 44; //Corrects for animation error.
|
||||
}
|
||||
|
||||
//Pending other types of projectile graphics. (They will function but with a default arrow graphic for now)
|
||||
else
|
||||
ProjectileAnimation(spell_target,0, 1, 1.5);
|
||||
|
||||
DoAnim(anim, 0, true, IsClient() ? FilterPCSpells : FilterNPCSpells);
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Switch #2 - execute the spell
|
||||
//
|
||||
@ -1959,7 +1895,12 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
|
||||
if (isproc) {
|
||||
SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, true);
|
||||
} else {
|
||||
if(!SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, false)) {
|
||||
if (spells[spell_id].targettype == ST_TargetOptional){
|
||||
if (!TrySpellProjectile(spell_target, spell_id))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if(!SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, false)) {
|
||||
if(IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id)) {
|
||||
// Prevent mana usage/timers being set for beneficial buffs
|
||||
if(casting_spell_type == 1)
|
||||
@ -1968,6 +1909,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(IsPlayerIllusionSpell(spell_id)
|
||||
&& IsClient()
|
||||
&& CastToClient()->CheckAAEffect(aaEffectProjectIllusion)){
|
||||
@ -3504,8 +3446,15 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
|
||||
if(spell_effectiveness == 0 || !IsPartialCapableSpell(spell_id) )
|
||||
{
|
||||
mlog(SPELLS__RESISTS, "Spell %d was completely resisted by %s", spell_id, spelltar->GetName());
|
||||
Message_StringID(MT_SpellFailure, TARGET_RESISTED, spells[spell_id].name);
|
||||
spelltar->Message_StringID(MT_SpellFailure, YOU_RESIST, spells[spell_id].name);
|
||||
|
||||
if (spells[spell_id].resisttype == RESIST_PHYSICAL){
|
||||
Message_StringID(MT_SpellFailure, PHYSICAL_RESIST_FAIL,spells[spell_id].name);
|
||||
spelltar->Message_StringID(MT_SpellFailure, YOU_RESIST, spells[spell_id].name);
|
||||
}
|
||||
else {
|
||||
Message_StringID(MT_SpellFailure, TARGET_RESISTED, spells[spell_id].name);
|
||||
spelltar->Message_StringID(MT_SpellFailure, YOU_RESIST, spells[spell_id].name);
|
||||
}
|
||||
|
||||
if(spelltar->IsAIControlled()){
|
||||
int32 aggro = CheckAggroAmount(spell_id);
|
||||
@ -4254,67 +4203,83 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
}
|
||||
break;
|
||||
case RESIST_PHYSICAL:
|
||||
{
|
||||
if (IsNPC())
|
||||
target_resist = GetPhR();
|
||||
else
|
||||
target_resist = 0;
|
||||
}
|
||||
default:
|
||||
//This is guessed but the others are right
|
||||
target_resist = (GetSTA() / 4);
|
||||
|
||||
target_resist = 0;
|
||||
}
|
||||
|
||||
//Setup our base resist chance.
|
||||
int resist_chance = 0;
|
||||
int level_mod = 0;
|
||||
|
||||
//Adjust our resist chance based on level modifiers
|
||||
int temp_level_diff = GetLevel() - caster->GetLevel();
|
||||
if(IsNPC() && GetLevel() >= RuleI(Casting,ResistFalloff))
|
||||
{
|
||||
int a = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel();
|
||||
if(a > 0)
|
||||
|
||||
//Physical Resists are calclated using their own formula derived from extensive parsing.
|
||||
if (resist_type == RESIST_PHYSICAL) {
|
||||
level_mod = ResistPhysical(temp_level_diff, caster->GetLevel());
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
if(IsNPC() && GetLevel() >= RuleI(Casting,ResistFalloff))
|
||||
{
|
||||
temp_level_diff = a;
|
||||
}
|
||||
else
|
||||
{
|
||||
temp_level_diff = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(IsClient() && GetLevel() >= 21 && temp_level_diff > 15)
|
||||
{
|
||||
temp_level_diff = 15;
|
||||
}
|
||||
|
||||
if(IsNPC() && temp_level_diff < -9)
|
||||
{
|
||||
temp_level_diff = -9;
|
||||
}
|
||||
|
||||
int level_mod = temp_level_diff * temp_level_diff / 2;
|
||||
if(temp_level_diff < 0)
|
||||
{
|
||||
level_mod = -level_mod;
|
||||
}
|
||||
|
||||
if(IsNPC() && (caster->GetLevel() - GetLevel()) < -20)
|
||||
{
|
||||
level_mod = 1000;
|
||||
}
|
||||
|
||||
//Even more level stuff this time dealing with damage spells
|
||||
if(IsNPC() && IsDamageSpell(spell_id) && GetLevel() >= 17)
|
||||
{
|
||||
int level_diff;
|
||||
if(GetLevel() >= RuleI(Casting,ResistFalloff))
|
||||
{
|
||||
level_diff = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel();
|
||||
if(level_diff < 0)
|
||||
int a = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel();
|
||||
if(a > 0)
|
||||
{
|
||||
level_diff = 0;
|
||||
temp_level_diff = a;
|
||||
}
|
||||
else
|
||||
{
|
||||
temp_level_diff = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if(IsClient() && GetLevel() >= 21 && temp_level_diff > 15)
|
||||
{
|
||||
level_diff = GetLevel() - caster->GetLevel();
|
||||
temp_level_diff = 15;
|
||||
}
|
||||
|
||||
if(IsNPC() && temp_level_diff < -9)
|
||||
{
|
||||
temp_level_diff = -9;
|
||||
}
|
||||
|
||||
level_mod = temp_level_diff * temp_level_diff / 2;
|
||||
if(temp_level_diff < 0)
|
||||
{
|
||||
level_mod = -level_mod;
|
||||
}
|
||||
|
||||
if(IsNPC() && (caster->GetLevel() - GetLevel()) < -20)
|
||||
{
|
||||
level_mod = 1000;
|
||||
}
|
||||
|
||||
//Even more level stuff this time dealing with damage spells
|
||||
if(IsNPC() && IsDamageSpell(spell_id) && GetLevel() >= 17)
|
||||
{
|
||||
int level_diff;
|
||||
if(GetLevel() >= RuleI(Casting,ResistFalloff))
|
||||
{
|
||||
level_diff = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel();
|
||||
if(level_diff < 0)
|
||||
{
|
||||
level_diff = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
level_diff = GetLevel() - caster->GetLevel();
|
||||
}
|
||||
level_mod += (2 * level_diff);
|
||||
}
|
||||
level_mod += (2 * level_diff);
|
||||
}
|
||||
|
||||
if (CharismaCheck)
|
||||
@ -4461,6 +4426,43 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
}
|
||||
}
|
||||
|
||||
int Mob::ResistPhysical(int level_diff, uint8 caster_level)
|
||||
{
|
||||
/* Physical resists use the standard level mod calculation in
|
||||
conjunction with a resist fall off formula that greatly prevents you
|
||||
from landing abilities on mobs that are higher level than you.
|
||||
After level 12, every 4 levels gained the max level you can hit
|
||||
your target without a sharp resist penalty is raised by 1.
|
||||
Extensive parsing confirms this, along with baseline phyiscal resist rates used.
|
||||
*/
|
||||
|
||||
|
||||
if (level_diff == 0)
|
||||
return level_diff;
|
||||
|
||||
int level_mod = 0;
|
||||
|
||||
if (level_diff > 0) {
|
||||
|
||||
int ResistFallOff = 0;
|
||||
|
||||
if (caster_level <= 12)
|
||||
ResistFallOff = 3;
|
||||
else
|
||||
ResistFallOff = caster_level/4;
|
||||
|
||||
if (level_diff > ResistFallOff || level_diff >= 15)
|
||||
level_mod = ((level_diff * 10) + level_diff)*2;
|
||||
else
|
||||
level_mod = level_diff * level_diff / 2;
|
||||
}
|
||||
|
||||
else
|
||||
level_mod = -(level_diff * level_diff / 2);
|
||||
|
||||
return level_mod;
|
||||
}
|
||||
|
||||
int16 Mob::CalcResistChanceBonus()
|
||||
{
|
||||
int resistchance = spellbonuses.ResistSpellChance + itembonuses.ResistSpellChance;
|
||||
|
||||
@ -1047,6 +1047,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
||||
"npc_types.FR,"
|
||||
"npc_types.PR,"
|
||||
"npc_types.Corrup,"
|
||||
"npc_types.PhR,"
|
||||
"npc_types.mindmg,"
|
||||
"npc_types.maxdmg,"
|
||||
"npc_types.attack_count,"
|
||||
@ -1143,6 +1144,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
||||
tmpNPCType->FR = atoi(row[r++]);
|
||||
tmpNPCType->PR = atoi(row[r++]);
|
||||
tmpNPCType->Corrup = atoi(row[r++]);
|
||||
tmpNPCType->PhR = atoi(row[r++]);
|
||||
tmpNPCType->min_dmg = atoi(row[r++]);
|
||||
tmpNPCType->max_dmg = atoi(row[r++]);
|
||||
tmpNPCType->attack_count = atoi(row[r++]);
|
||||
|
||||
@ -75,6 +75,7 @@ struct NPCType
|
||||
int16 PR;
|
||||
int16 DR;
|
||||
int16 Corrup;
|
||||
int16 PhR;
|
||||
uint8 haircolor;
|
||||
uint8 beardcolor;
|
||||
uint8 eyecolor1; // the eyecolors always seem to be the same, maybe left and right eye?
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user