nwnxee / unified

Binaries available under the Releases tab on Github
https://nwnxee.github.io/unified
GNU General Public License v3.0
131 stars 92 forks source link

nwnx_creature #1792

Open Darsiniux opened 1 month ago

Darsiniux commented 1 month ago

Hi, great work on all these functions. I really appreciate what you guys did here. I'm posting because i'm having a bit of trouble understanding how i'm supposed to be using the NWNX_Creature_SetMovementRateFactor & NWNX_Creature_GetMovementRateFactor. No matter what I do I run into a laundry list of bugs. When I use these functions with the base NWN EffectMovementSpeedIncrease it causes issues with barbarian speed & monk speed among other things. They don't work well with the vanilla NWN haste property either. I literally attempted to rewrite every spell/feat, and even the haste itemproperty to make this work before realizing that I have no way of dealing with encumbrance causing a movement speed decrease which breaks everything.

Darsiniux commented 1 month ago

Example of one of the scripts I wrote to fix monk speed.

#include "nwnx_events"
#include "nwnx_creature"

void main()
{
    string sEvent = NWNX_Events_GetCurrentEvent();
    int iMonk = GetLevelByClass(CLASS_TYPE_MONK, OBJECT_SELF);
    int iFeat = StringToInt(NWNX_Events_GetEventData("FEAT_ID"));

    if(iFeat == FEAT_MONK_ENDURANCE)
    {
        if(sEvent == "NWNX_ON_USE_FEAT_BEFORE")
        {
            if(iMonk >= 3 && GetLocalInt(OBJECT_SELF, "ONE") == FALSE)
            {
                NWNX_Creature_SetMovementRateFactor(OBJECT_SELF, NWNX_Creature_GetMovementRateFactor(OBJECT_SELF)+0.10);
                SetLocalInt(OBJECT_SELF, "ONE", TRUE);
            }
            if(iMonk >= 6 && GetLocalInt(OBJECT_SELF, "TWO") == FALSE)
            {
                NWNX_Creature_SetMovementRateFactor(OBJECT_SELF, NWNX_Creature_GetMovementRateFactor(OBJECT_SELF)+0.10);
                SetLocalInt(OBJECT_SELF, "TWO", TRUE);
            }
            if(iMonk >= 9 && GetLocalInt(OBJECT_SELF, "THREE") == FALSE)
            {
                NWNX_Creature_SetMovementRateFactor(OBJECT_SELF, NWNX_Creature_GetMovementRateFactor(OBJECT_SELF)+0.10);
                SetLocalInt(OBJECT_SELF, "THREE", TRUE);
            }
            if(iMonk == 12 && GetLocalInt(OBJECT_SELF, "FOUR") == FALSE)
            {
                NWNX_Creature_SetMovementRateFactor(OBJECT_SELF, NWNX_Creature_GetMovementRateFactor(OBJECT_SELF)+0.10);
                SetLocalInt(OBJECT_SELF, "FOUR", TRUE);
            }
            if(iMonk == 15 && GetLocalInt(OBJECT_SELF, "FIVE") == FALSE)
            {
                NWNX_Creature_SetMovementRateFactor(OBJECT_SELF, NWNX_Creature_GetMovementRateFactor(OBJECT_SELF)+0.10);
                SetLocalInt(OBJECT_SELF, "FIVE", TRUE);
            }
            if(iMonk == 18 && GetLocalInt(OBJECT_SELF, "SIX") == FALSE)
            {
                NWNX_Creature_SetMovementRateFactor(OBJECT_SELF, NWNX_Creature_GetMovementRateFactor(OBJECT_SELF)+0.10);
                SetLocalInt(OBJECT_SELF, "SIX", TRUE);
            }
            if(iMonk == 21 && GetLocalInt(OBJECT_SELF, "SEVEN") == FALSE)
            {
                NWNX_Creature_SetMovementRateFactor(OBJECT_SELF, NWNX_Creature_GetMovementRateFactor(OBJECT_SELF)+0.10);
                SetLocalInt(OBJECT_SELF, "SEVEN", TRUE);
            }
            if(iMonk == 24 && GetLocalInt(OBJECT_SELF, "EIGHT") == FALSE)
            {
                NWNX_Creature_SetMovementRateFactor(OBJECT_SELF, NWNX_Creature_GetMovementRateFactor(OBJECT_SELF)+0.10);
                SetLocalInt(OBJECT_SELF, "EIGHT", TRUE);
            }
            if(iMonk == 27 && GetLocalInt(OBJECT_SELF, "NINE") == FALSE)
            {
                NWNX_Creature_SetMovementRateFactor(OBJECT_SELF, NWNX_Creature_GetMovementRateFactor(OBJECT_SELF)+0.10);
                SetLocalInt(OBJECT_SELF, "NINE", TRUE);
            }
            if(iMonk == 30 && GetLocalInt(OBJECT_SELF, "TEN") == FALSE)
            {
                NWNX_Creature_SetMovementRateFactor(OBJECT_SELF, NWNX_Creature_GetMovementRateFactor(OBJECT_SELF)+0.10);
                SetLocalInt(OBJECT_SELF, "TEN", TRUE);
            }
            if(iMonk == 33 && GetLocalInt(OBJECT_SELF, "ELEVEN") == FALSE)
            {
                NWNX_Creature_SetMovementRateFactor(OBJECT_SELF, NWNX_Creature_GetMovementRateFactor(OBJECT_SELF)+0.10);
                SetLocalInt(OBJECT_SELF, "ELEVEN", TRUE);
            }
            if(iMonk == 36 && GetLocalInt(OBJECT_SELF, "TWELVE") == FALSE)
            {
                NWNX_Creature_SetMovementRateFactor(OBJECT_SELF, NWNX_Creature_GetMovementRateFactor(OBJECT_SELF)+0.10);
                SetLocalInt(OBJECT_SELF, "TWELVE", TRUE);
            }
            if(iMonk == 39 && GetLocalInt(OBJECT_SELF, "THIRTEEN") == FALSE)
            {
                NWNX_Creature_SetMovementRateFactor(OBJECT_SELF, NWNX_Creature_GetMovementRateFactor(OBJECT_SELF)+0.10);
                SetLocalInt(OBJECT_SELF, "THIRTEEN", TRUE);
            }
        NWNX_Events_SkipEvent();
        }
    }
}
Darsiniux commented 1 month ago

Barbarian speed fix...

#include "nwnx_events"
#include "nwnx_creature"

void main()
{
    // Initialize a variable to check the event data. Checks if user of the event is using the correct feat.
    int iHasFeat = StringToInt(NWNX_Events_GetEventData("FEAT_ID"));
    string sEvent = NWNX_Events_GetCurrentEvent();

    if(sEvent == "NWNX_ON_USE_FEAT_BEFORE")
    {
        if(iHasFeat == FEAT_BARBARIAN_ENDURANCE)
        {
            float fSpeed = NWNX_Creature_GetMovementRateFactor(OBJECT_SELF) + 0.10;
            NWNX_Creature_SetMovementRateFactor(OBJECT_SELF, fSpeed);
            NWNX_Events_SkipEvent();
        }
    }
}
Darsiniux commented 1 month ago

While doing all this I also realized that the vanilla NWN monk speed is completely bugged at level 15 monk. It doesn't apply the correct movement speed. Also, when you use a haste item with barbarian, and then remove the haste item, the barbarian speed isn't retained where it should be at 1.1 without haste.

Darsiniux commented 1 month ago

Unfortunately, none of the above fixes work when a vanilla speed increase/decrease occurs from encumberance, haste, slow, grease, acid fog, or any other thing effecting speed in the base game. I was literally in the process of rewriting everything but it's pointless when you can't do anything about encumberance. I was about to redo everything including called shot leg.

Darsiniux commented 1 month ago

NWNX_Creature_SetMovementRateFactorCap causes lots of bugs to happen.

Darsiniux commented 1 month ago

I even re-wrote the vanilla nwn haste property.

#include "nwnx_events"
#include "nwnx_effect"
#include "nwnx_creature"

// Skips the haste item property on items and replaces it.
void main()
{
    string sEvent = NWNX_Events_GetCurrentEvent();
    int iProperty = StringToInt(NWNX_Events_GetEventData("PROPERTY"));
    object oPC = StringToObject(NWNX_Events_GetEventData("CREATURE"));
    int bLoading = StringToInt(NWNX_Events_GetEventData("LOADING_GAME"));
    effect eAPR = ExtraordinaryEffect(EffectModifyAttacks(1));
    effect eHaste = ExtraordinaryEffect(EffectIcon(EFFECT_ICON_HASTE));
    effect eHasteAC = ExtraordinaryEffect(EffectACIncrease(4));

    if(sEvent == NWNX_ON_ITEMPROPERTY_EFFECT_APPLIED_BEFORE)
    {
        if(StringToInt(NWNX_Events_GetEventData("PROPERTY")) == ITEM_PROPERTY_HASTE)
        {
            if(GetLocalInt(oPC, "SPEED") == FALSE)
            {
                if(!bLoading)
                {
                    ApplyEffectToObject(DURATION_TYPE_EQUIPPED, eAPR, oPC);
                    ApplyEffectToObject(DURATION_TYPE_EQUIPPED, eHaste, oPC);
                    ApplyEffectToObject(DURATION_TYPE_EQUIPPED, eHasteAC, oPC);
                    NWNX_Creature_SetMovementRateFactor(oPC, NWNX_Creature_GetMovementRateFactor(oPC) + 0.50);
                    SetLocalInt(oPC, "SPEED", TRUE);
                    //SendMessageToPC(oEquipped, "APPLIED BEFORE.");
                }
            }
            NWNX_Events_SkipEvent();
        }
    }

    if(sEvent == NWNX_ON_ITEMPROPERTY_EFFECT_REMOVED_AFTER)
    {
        if(iProperty == ITEM_PROPERTY_HASTE)
        {
            if(GetLocalInt(oPC, "SPEED") == TRUE)
            {
                RemoveEffect(oPC, ExtraordinaryEffect(EffectModifyAttacks(1)));
                RemoveEffect(oPC, ExtraordinaryEffect(EffectIcon(EFFECT_ICON_HASTE)));
                RemoveEffect(oPC, ExtraordinaryEffect(EffectACIncrease(4)));
                NWNX_Creature_SetMovementRateFactor(oPC, NWNX_Creature_GetMovementRateFactor(oPC) - 0.50);
                SetLocalInt(oPC, "SPEED", FALSE);
                //SendMessageToPC(oEquipped, "REMOVED AFTER.");
            }
        }
    }
}
Darsiniux commented 1 month ago

Am I just using these wrong? Is there a better way to do all this without running into problems? It's so bugged in so many ways that it begs the question what is this even used for?

Darsiniux commented 1 month ago

Need a way to replace the base games movement speed decrease with an event that uses the nwnx_creature functions to reduce the speed instead.

Either that or encumbrance events are needed like ENCUMBERED_BEFORE and NOT_ENCUMBERED_AFTER.

Darsiniux commented 1 month ago

Does anybody know what LimitMovementSpeed = 59 is?? I was hoping it was for encumber but it's not. https://github.com/nwnxee/unified/blob/master/NWNXLib/API/Constants/Effect.hpp#L8 |

Darsiniux commented 1 month ago

I incorrectly stated that the vanilla NWN stuff with movement speed is bugged. It's only bugged when you increase the movement rate cap with the nwnx_creature function, NWNX_Creature_SetMovementRateFactorCap(oPC, 3.0);

I thought it was vanilla NWN because I forgot to remove the cap increase.

jd28 commented 3 weeks ago

It would probably be simpler for you to hook GetMovementRateFactor and do your own thing there and not modify what the game thinks obj->cre_move_rate should be, no clue what the actual variable is named.

Here is an example in Lua from an older project:

local Hook = require 'solstice.hooks'
local ffi = require 'ffi'
local C = ffi.C
local max = math.max

-- CNWSCreature::GetMovementRateFactor(void) 0x08123FD8
local Orig_GetMovementRateFactor
local function Hook_GetMovementRateFactor(obj)
  local cre = Game.GetObjectByID(obj.obj.obj_id)
  if cre:GetType() == OBJECT_TYPE_CREATURE then
    local mo, ba, ta = 0, 0, 0
    local can, level = Rules.CanUseClassAbilities(cre, CLASS_TYPE_MONK)
    if can and level > 3 then
      mo = 0.2
    end

    if cre:GetLevelByClass(CLASS_TYPE_BARBARIAN) >= 1 then
      ba = 0.1
    end

    ta = (cre['TA_MOVE_SPEED'] or 0) / 100 -- This is where you'd add in your custom factors,  I used a local var

    local mr = obj.cre_move_rate
    if mr > 1.5 then mr = 1.5 end
    return mr + ta + max(mo, ba)

  end

  return Orig_GetMovementRateFactor(cre)
end

Orig_GetMovementRateFactor = Hook.hook {
  name = "GetMovementRateFactor",
  func = Hook_GetMovementRateFactor,
  length = 5,
  address = 0x08123FD8,
  type = 'double (*)(CNWSCreature *)',
}