//------------------------------------------------------------------------------
// spell_shapeshift
//------------------------------------------------------------------------------
/*
Generic Shapeshifting Script
*/
//------------------------------------------------------------------------------
// georg / emmanuel
//==============================================================================
#include "log_h"
#include "ability_h"
#include "combat_h"
#include "2da_constants_h"
#include "2da_data_h"
#include "utility_h"
#include "sys_autoscale_h"
#include "spell_constants_h"
/* --------------------------------------------------------------------------->>
_ _ _ ____ ____ ____ _ ____ ____
| | | |/ _ |/ ___) _ \\\\| | _ \\\\ / _ |
| | | ( ( | | | | | | | | | | ( ( | |
\\\\____|\\\\_||_|_| |_| |_|_|_| |_|\\\\_|| |
(_____|
Shapeshifting is rather complex and can cause character corruption
loss of abilities) if applied incorrectly.
Before making any change here, please consult georg or emmanuel.
<< ----------------------------------------------------------------------------*/
const int SHAPESHIFT_IN_SMOKE = 1135;
const int SHAPESHIFT_OUT_SMOKE = 1136;
const int SWARM_CRUST_VFX = 1087;
const int SWARM_ATTACK_VFX = 1014;
const float SWARM_DAMAGE = 5.0f;
const float SWARM_SPEED = 2.0;
const int FLYING_SWARM_AOE = 228;
const float SWARM_VISUAL_ATTACK_DURATION = 3.0;
const float SWARM_RADIUS = 3.8f;
const float SWARM_QUARTER_DAMAGE_DIST = 4.5f;
const float SWARM_HALF_DAMAGE_DIST = 3.5f;
const resource FLYING_SWARM_SCRIPT = R"spell_shapeshift.ncs";
// -----------------------------------------------------------------------------
// constant to read columns in shapechange.xls 2da
// -----------------------------------------------------------------------------
const string SHAPECHANGE_STRENGTH_BASE = "StrengthBase";
const string SHAPECHANGE_DEXTERITY_BASE = "DexterityBase";
const string SHAPECHANGE_CONSTITUTION_BASE = "ConstitutionBase";
const string SHAPECHANGE_ARMOR_BASE = "ArmorBase";
const string SHAPECHANGE_DAMAGE_BASE = "DamageBase";
const string SHAPECHANGE_STRENGTH_MOD = "StrengthPerLevel";
const string SHAPECHANGE_DEXTERITY_MOD = "DexterityPerLevel";
const string SHAPECHANGE_CONSTITUTION_MOD = "ConstitutionPerLevel";
const string SHAPECHANGE_ARMOR_MOD = "ArmorPerLevel";
const string SHAPECHANGE_DAMAGE_MOD = "DamagePerLevel";
const string SHAPECHANGE_AP_BASE = "APBase";
const string SHAPECHANGE_AP_MOD = "APLevel";
const int gender = 0;
// -----------------------------------------------------------------------------
// Compute ideal stats for the shapeshifted form and returns
// a stat bonus effect if needed
// -----------------------------------------------------------------------------
effect _GetNeededModificationEffect(object oCaster, int nShapeshift, int nProperty, int bMastery);
effect _GetNeededModificationEffect(object oCaster, int nShapeshift, int nProperty, int bMastery)
{
int nPropertyType;
string sAttributeBase;
string sAttributePerLevel;
switch (nProperty)
{
case PROPERTY_ATTRIBUTE_STRENGTH:
{
nPropertyType = PROPERTY_VALUE_BASE;
sAttributeBase = SHAPECHANGE_STRENGTH_BASE;
sAttributePerLevel = SHAPECHANGE_STRENGTH_MOD;
}
break;
case PROPERTY_ATTRIBUTE_CONSTITUTION:
{
nPropertyType = PROPERTY_VALUE_BASE;
sAttributeBase = SHAPECHANGE_CONSTITUTION_BASE;
sAttributePerLevel = SHAPECHANGE_CONSTITUTION_MOD;
}
break;
case PROPERTY_ATTRIBUTE_DEXTERITY:
{
nPropertyType = PROPERTY_VALUE_BASE;
sAttributeBase = SHAPECHANGE_DEXTERITY_BASE;
sAttributePerLevel = SHAPECHANGE_DEXTERITY_MOD;
}
break;
case PROPERTY_ATTRIBUTE_ARMOR:
{
nPropertyType = PROPERTY_VALUE_TOTAL;
sAttributeBase = SHAPECHANGE_ARMOR_BASE;
sAttributePerLevel = SHAPECHANGE_ARMOR_MOD;
}
break;
case PROPERTY_ATTRIBUTE_DAMAGE_BONUS:
{
nPropertyType = PROPERTY_VALUE_TOTAL;
sAttributeBase = SHAPECHANGE_DAMAGE_BASE;
sAttributePerLevel = SHAPECHANGE_DAMAGE_MOD;
}
case PROPERTY_ATTRIBUTE_AP:
{
nPropertyType = PROPERTY_VALUE_TOTAL;
sAttributeBase = SHAPECHANGE_AP_BASE;
sAttributePerLevel = SHAPECHANGE_AP_MOD;
}
break;
}
// -------------------------------------------------------------------------
// Plot forms (broken circle) work differently
// -------------------------------------------------------------------------
int bPlotForm = GetM2DAInt(TABLE_SHAPECHANGE,"IsPlotShape", nShapeshift);
float fLevel = bPlotForm? IntToFloat(GetLevel(oCaster)) : (GetCreatureSpellPower(oCaster) / 5.0f);
if (bMastery)
{
fLevel +=2.0f;
}
float fRealAttribute = GetCreatureProperty(oCaster, nProperty, nPropertyType);
float fComputedAttribute = GetM2DAFloat(TABLE_SHAPECHANGE, sAttributeBase, nShapeshift) + (fLevel * GetM2DAFloat(TABLE_SHAPECHANGE, sAttributePerLevel, nShapeshift));
float fDelta = fComputedAttribute - fRealAttribute;
effect eMod;
// -------------------------------------------------------------------------
// only create positive effects. If the caster's stat is bigger than the ideal one, he does not
// get penalized.
// -------------------------------------------------------------------------
if (fDelta > 0.0)
{
// round up the bonus
int nDelta = FloatToInt(fDelta+0.5);
fDelta = nDelta*1.0;
eMod = EffectModifyProperty(nProperty, fDelta);
}
else
{
eMod = Effect(EFFECT_TYPE_INVALID);
}
return eMod;
}
void _ActivateModalAbility(object oCaster, int nAbility)
{
effect[] eEffects;
eEffects[0] = EffectShapechange(nAbility, oCaster);
int bMaster = HasAbility(oCaster, ABILITY_SPELL_SHAPESHIFTER);
int nShape = 0;
[b]//check gender for shapeshifting
int nCharGen = GetCreatureGender(oCaster);
SetLocalInt(oCaster, "CHARGEN", nCharGen);
if (nCharGen != GetLocalInt(oCaster, "CHARGEN"))
{
SetLocalInt(oCaster, "CHARGEN", GetCreatureGender(oCaster));
int nLocalCharGen = GetLocalInt(oCaster, "CHARGEN");
DisplayFloatyMessage(oCaster,"detected chargen is " + ToString(nCharGen), FLOATY_MESSAGE, 1677215/*0xffffff white*/,5.0f);
DisplayFloatyMessage(oCaster,"stored chargen is " + ToString(nLocalCharGen), FLOATY_MESSAGE, 1677215/*0xffffff white*/,5.0f);
}
if (nCharGen != 1)
{
SetCreatureGender(oCaster, 1);
}[/b]
if (bMaster)
{
nShape = GetM2DAInt(TABLE_SHAPECHANGE, "UpgradeApr", nAbility);
}
else
{
nShape = GetM2DAInt(TABLE_SHAPECHANGE, "Appearance", nAbility);
}
if (nShape == 0)
{
Warning("Shape for ability " + ToString(nAbility) + " was 0");
}
ApplyEffectVisualEffect(oCaster, oCaster, SHAPESHIFT_OUT_SMOKE, EFFECT_DURATION_TYPE_INSTANT, 0.0f, nAbility);
int nModIndex = 0;
// additional shape effects
switch (nAbility)
{
// ---------------------------------------------------------------------
// Mouse:
// - Very poor at combat; misdirection hex effect
// ---------------------------------------------------------------------
case ABILITY_SKILL_SKILL_PLOT_SHAPESHIFT_MOUSE:
{
nModIndex++; eEffects[nModIndex] = Effect(EFFECT_TYPE_MISDIRECTION_HEX);
break;
}
// ---------------------------------------------------------------------
// Swarm:
// - Much higher movement speed
// - AoE damage aura
// - Mana Shield
// - No mana regeneration
// ---------------------------------------------------------------------
case ABILITY_SPELL_FLYING_SWARM:
{
nModIndex++; eEffects[nModIndex] = EffectModifyMovementSpeed(SWARM_SPEED);
nModIndex++; eEffects[nModIndex] = EffectAreaOfEffect(FLYING_SWARM_AOE, FLYING_SWARM_SCRIPT);
nModIndex++; eEffects[nModIndex] = Effect(EFFECT_TYPE_MANA_SHIELD);
nModIndex++; eEffects[nModIndex] = EffectModifyProperty(PROPERTY_ATTRIBUTE_REGENERATION_STAMINA, REGENERATION_STAMINA_EXPLORE_NULL + 1.0f, PROPERTY_ATTRIBUTE_REGENERATION_STAMINA_COMBAT, REGENERATION_STAMINA_COMBAT_NULL);
break;
}
}
// Bring up physical attributes and stats to what they should be for each form
// As per shapechange.xls
effect eStrengthMod = _GetNeededModificationEffect(oCaster, nAbility, PROPERTY_ATTRIBUTE_STRENGTH, bMaster);
if (GetEffectType(eStrengthMod) != EFFECT_TYPE_INVALID)
{
nModIndex++; eEffects[nModIndex] = eStrengthMod;
}
effect eConstitutionMod = _GetNeededModificationEffect(oCaster, nAbility, PROPERTY_ATTRIBUTE_CONSTITUTION, bMaster);
if (GetEffectType(eConstitutionMod) != EFFECT_TYPE_INVALID)
{
nModIndex++; eEffects[nModIndex] = eConstitutionMod;
}
effect eDexterityMod = _GetNeededModificationEffect(oCaster, nAbility, PROPERTY_ATTRIBUTE_DEXTERITY, bMaster);
if (GetEffectType(eDexterityMod) != EFFECT_TYPE_INVALID)
{
nModIndex++; eEffects[nModIndex] = eDexterityMod;
}
effect eArmorMod = _GetNeededModificationEffect(oCaster, nAbility, PROPERTY_ATTRIBUTE_ARMOR, bMaster);
if (GetEffectType(eArmorMod) != EFFECT_TYPE_INVALID)
{
nModIndex++; eEffects[nModIndex] = eArmorMod;
}
effect eDamageMod = _GetNeededModificationEffect(oCaster, nAbility, PROPERTY_ATTRIBUTE_DAMAGE_BONUS, bMaster);
if (GetEffectType(eDamageMod) != EFFECT_TYPE_INVALID)
{
nModIndex++; eEffects[nModIndex] = eDamageMod;
}
effect eApMod = _GetNeededModificationEffect(oCaster, nAbility, PROPERTY_ATTRIBUTE_AP, bMaster);
if (GetEffectType(eApMod) != EFFECT_TYPE_INVALID)
{
nModIndex++; eEffects[nModIndex] = eApMod;
}
float fHealthPctBefore = _GetRelativeResourceLevel(oCaster, PROPERTY_DEPLETABLE_HEALTH);
Ability_ApplyUpkeepEffects(oCaster, nAbility, eEffects);
// -------------------------------------------------------------------------
// Restore the character's health to the relative level it was before shifting.
// -------------------------------------------------------------------------
float fNewHealth = GetMaxHealth(oCaster) * fHealthPctBefore;
float fCurrentHealth = GetCurrentHealth(oCaster);
if (fCurrentHealth< fNewHealth)
{
HealCreature(oCaster, TRUE, fNewHealth -fCurrentHealth, FALSE);
}
}
void _DeactivateModalAbility(object oCaster, int nAbility)
{
Log_Trace_Spell("_DeactivateModalAbility","deactivate.",nAbility, OBJECT_INVALID);
effect[] eEffects = GetEffectsByAbilityId(oCaster, nAbility);
// re-equip the weapons
int nWeaponSet = GetActiveWeaponSet(oCaster);
/*object oMainWeapon = GetEffectObject(eEffects[0], 0);
object oOffWeapon = GetEffectObject(eEffects[0], 1);
EquipItem(oCaster, oMainWeapon, INVENTORY_SLOT_MAIN);
EquipItem(oCaster, oOffWeapon, INVENTORY_SLOT_OFFHAND);*/
[b]int nLocalCharGen = GetLocalInt(oCaster, "CHARGEN");
int nCharGen = GetCreatureGender(oCaster);[/b]
[b]DisplayFloatyMessage(OBJECT_SELF,"stored chargender is " + ToString(nLocalCharGen), FLOATY_MESSAGE, 1677215/*0xffffff white*/,5.0f);[/b]
// special for bear shape
if (nAbility == ABILITY_SPELL_BEAR)
{
eEffects = GetEffectsByAbilityId(oCaster, ABILITY_TALENT_MONSTER_BEAR_RAGE);
RemoveEffectArray(oCaster, eEffects);
}
// special for mouse shape
if (nAbility == ABILITY_SKILL_SKILL_PLOT_SHAPESHIFT_MOUSE)
{
eEffects = GetEffectsByAbilityId(oCaster, ABILITY_SKILL_STEALTH_1);
RemoveEffectArray(oCaster, eEffects);
}
// RemoveEffectArray(oCaster, eEffects);
Effects_RemoveUpkeepEffect(oCaster, nAbility);
[b]if (nCharGen != nLocalCharGen)
{
DisplayFloatyMessage(oCaster,"stored chargender is " + ToString(nLocalCharGen), FLOATY_MESSAGE, 1677215/*0xffffff white*/,5.0f);
SetCreatureGender(oCaster, nLocalCharGen); //restore creature gender to original
}
//end gender check[/b]
ApplyEffectVisualEffect(oCaster, oCaster, SHAPESHIFT_OUT_SMOKE, EFFECT_DURATION_TYPE_INSTANT, 0.0f, nAbility);
[b]SetCreatureGender(oCaster, nLocalCharGen); //restore creature gender to original [/b]
}
void main()
{
event evEvent = GetCurrentEvent();
int nEventType = GetEventType(evEvent);
switch( nEventType )
{
case EVENT_TYPE_SPELLSCRIPT_PENDING:
{
// Get a structure with the event parameters
struct EventSpellScriptPendingStruct stEvent = Events_GetEventSpellScriptPendingParameters(evEvent);
LogTrace(LOG_CHANNEL_TEMP, "Shapechange Pending (" + ToString(stEvent.nAbility) + ")");
// Setting Return Value
Ability_SetSpellscriptPendingEventResult(COMMAND_RESULT_SUCCESS);
break;
}
case EVENT_TYPE_SPELLSCRIPT_CAST:
{
// Get a structure with the event parameters
struct EventSpellScriptCastStruct stEvent = Events_GetEventSpellScriptCastParameters(evEvent);
#ifdef DEBUG
LogTrace(LOG_CHANNEL_TEMP, "Shapechange Cast (" + ToString(stEvent.nAbility) + ")");
#endif
// Message the Attack result back to the engine
SetAbilityResult(stEvent.oCaster, stEvent.nResistanceCheckResult);
break;
}
case EVENT_TYPE_SPELLSCRIPT_IMPACT:
{
// Get a structure with the event parameters
struct EventSpellScriptImpactStruct stEvent = Events_GetEventSpellScriptImpactParameters(evEvent);
#ifdef DEBUG
LogTrace(LOG_CHANNEL_TEMP, "Shapechange Impact (" + ToString(stEvent.nAbility) + ")");
#endif
// remove existing modal abilities
effect[] eEffects = GetEffects(stEvent.oCaster, EFFECT_TYPE_UPKEEP);
RemoveEffectArray(stEvent.oCaster, eEffects);
ApplyEffectVisualEffect(stEvent.oCaster, stEvent.oCaster, SHAPESHIFT_IN_SMOKE, EFFECT_DURATION_TYPE_INSTANT, 0.0f, stEvent.nAbility);
// remove any previously existing effects from same spellid to avoid stacking
Ability_PreventAbilityEffectStacking(stEvent.oTarget, stEvent.oCaster, stEvent.nAbility);
_ActivateModalAbility(stEvent.oCaster, stEvent.nAbility);
break;
}
case EVENT_TYPE_SPELLSCRIPT_DEACTIVATE:
{
// Get a structure with the event parameters
struct EventSpellScriptDeactivateStruct stEvent = Events_GetEventSpellScriptDeactivateParameters(evEvent);
// run if not already active
if (IsModalAbilityActive(stEvent.oCaster, stEvent.nAbility))
{
_DeactivateModalAbility(stEvent.oCaster, stEvent.nAbility);
}
ApplyEffectVisualEffect(stEvent.oCaster, stEvent.oCaster, SHAPESHIFT_IN_SMOKE, EFFECT_DURATION_TYPE_INSTANT, 0.0f, stEvent.nAbility);
// Setting Return Value (abort means we aborted the ability)
Ability_SetSpellscriptPendingEventResult(COMMAND_RESULT_INVALID);
break;
}
case EVENT_TYPE_AOE_HEARTBEAT:
{
#ifdef DEBUG
Log_Trace(LOG_CHANNEL_EFFECTS, "spell_shapeshift.EVENT_TYPE_AOE_HEARTBEAT", "EVENT FIRED!");
#endif
int nAbility = GetEventInteger(GetCurrentEvent(),0);
object oCreator = GetEventCreator(GetCurrentEvent());
object oAOE = OBJECT_SELF;
object[] oTargets = GetCreaturesInAOE(oAOE);
int nCount = 0;
int bMaster = HasAbility(oCreator, ABILITY_SPELL_SHAPESHIFTER);
float fSpellPower = GetCreatureSpellPower(oCreator)/3.0;
int nMax = GetArraySize(oTargets);
for (nCount = 0; nCount < nMax; nCount++)
{
object oTarget = oTargets[nCount];
// If it's hostile to the caster
if (IsObjectHostile(oCreator, oTarget) == TRUE)
{
// And if it's still alive
if (IsDead(oTarget) == FALSE)
{
// Deal damage to the target
#ifdef DEBUG
Log_Trace(LOG_CHANNEL_EFFECTS, "spell_shapeshift.EVENT_TYPE_AOE_HEARTBEAT", ToString(oTarget)+" is a target!", oTarget);
#endif
float fDamage = RandFF(5.0 + (fSpellPower / 2.0f), SWARM_DAMAGE + (fSpellPower/2.0f)) ;
float fDist = GetDistanceBetween(oCreator, oTarget);
if (fDist > SWARM_QUARTER_DAMAGE_DIST)
{
fDamage *= 0.25;
}
else if (fDist > SWARM_HALF_DAMAGE_DIST)
{
fDamage *= 0.50;
}
if (fDamage >0.0f)
{
Effects_ApplyInstantEffectDamage(oTarget, oCreator, fDamage, DAMAGE_TYPE_NATURE, bMaster?DAMAGE_EFFECT_FLAG_LEECH_25 : DAMAGE_EFFECT_FLAG_NONE, nAbility, SWARM_ATTACK_VFX);
ApplyEffectVisualEffect(oCreator, oTarget, SWARM_CRUST_VFX, EFFECT_DURATION_TYPE_TEMPORARY, SWARM_VISUAL_ATTACK_DURATION);
}
}
}
}
// Repeat AoE heartbeat
DelayEvent(1.5f, oAOE, GetCurrentEvent());
break;
}
}
}
Modifié par etfeet, 18 novembre 2009 - 10:26 .





Guest_etfeet_*
Retour en haut







