[Mod Request] Floor type affects movement speed

Talk about the mods or features you'd like to see in Daggerfall Unity. Give mod creators some ideas!
l3lessed
Posts: 752
Joined: Mon Aug 12, 2019 4:32 pm
Contact:

Re: [Mod Request] Floor type affects movement speed

Post by l3lessed »

Here is the two mods I'm hooking into these changes to mess with them.

Here is the code from my combat mod that changes move speed based on sheath/attack state

Code: Select all


        //sets players current movement using overrides and override float values. Ensures triggered only once when setting values to save update cycles.
        void movementModifier()
        {
            //the player isn't sheathed, idle, and restoredWalk hasn't been triggered by sheathing yet. This is default movement speed unsheathed modifier.
            if(!GameManager.Instance.WeaponManager.Sheathed && AttackState == 0 && !restoredWalk)
            {
                walkspeed = playerSpeedChanger.GetWalkSpeed(GameManager.Instance.PlayerEntity);
                playerSpeedChanger.StoreWalkSpeed();
                playerSpeedChanger.SetWalkSpeed(walkspeed * .45f, true);
                restoredWalk = true;
                attackApplied = false;
                return;
            }
            //if the player is attacking, is unsheathed, and hasn't already had movement modifier attackApplied, set walkspeed based.
            if (AttackState != 0 && !GameManager.Instance.WeaponManager.Sheathed && restoredWalk && !attackApplied)
            {
                walkspeed = playerSpeedChanger.GetWalkSpeed(GameManager.Instance.PlayerEntity);
                playerSpeedChanger.StoreWalkSpeed();
                playerSpeedChanger.SetWalkSpeed(walkspeed * .01f, true);
                attackApplied = true;                
                return;
            }
            //if weapon is sheathed and restore walk has been set to true, restore sheathed movement speed using the last walk speed value.
            if (GameManager.Instance.WeaponManager.Sheathed && restoredWalk)
            {
                restoredWalk = playerSpeedChanger.RestoreWalkSpeed();
                restoredWalk = false;
                attackApplied = false;
                Debug.Log("restore walkspeed");
                return;
            }
Here is the code I use for my interactive terrain tile. This will override the combat mods values anytime a second tile is loaded since it is grabbing the previous tile number for the additive model, and not the sheath state number.

Code: Select all

    // Update is called once per frame
    void Update()
    {
        if (OnTileChange())
        {
            List<float> TileProperties = TileProperty(lastTile);
            movementModifier(TileProperties[0]);
            Debug.Log("New Ground Tile: " + lastTile.ToString() + " | " + TileProperties[0].ToString());
        }
    }

    void movementModifier(float moveModifier)
    {
    //on tile change, update the current speed using an additive model to ensure it modifies based on any previous number being used.
        playerSpeedChanger.SetWalkSpeed(playerSpeedChanger.GetWalkSpeed(GameManager.Instance.PlayerEntity) * moveModifier, true);
        //store the new value so it can be restored and used when computed in future mod/code execution
        playerSpeedChanger.StoreWalkSpeed();
    }
Currently, they both change values properly, including doing proper additive changes, so you scale based from the previous set movementspeed by the last code/mod. The only issue is trying to ensure there is a way to set a base speed value, so even when mods change the value using the setWalkSpeed, it won't override the base value defined by the mod on the highest load priority. This would allow dynamic scaling and allow a permanent base change for mods that need it, like my terrain mod.
My Daggerfall Mods: l3lessed Nexus Page

Daggerfall Unity mods: Combat Overhaul Mod

Enjoy the free work I'm doing? Consider lending your support.

l3lessed
Posts: 752
Joined: Mon Aug 12, 2019 4:32 pm
Contact:

Re: [Mod Request] Floor type affects movement speed

Post by l3lessed »

Well, everything above is largely useless and overcomplicated as usual for my first pass. After going at the above approach for an afternoon, it is clear this is a dumb approach.

After thinking about it more, I decided using a list that stores the values modders are injecting and manipulating and hooking it to a for loop to properly loop and compute each subsequent speed adjustment value would be best and easiest and provide most accurate and sturdy speed scaling property.

It will consist of a list that contains float values; devs will push the value into it and then remove it when done with their move modifier effect. Each time the list updates or the base speed updates, the setSpeed will kick the for loop into gear and start sequently applying each modifier, starting with the base value and first modifier then going to the following modifier and the following updated current speed. At the end of the loop, we should have the proper modified speed.
My Daggerfall Mods: l3lessed Nexus Page

Daggerfall Unity mods: Combat Overhaul Mod

Enjoy the free work I'm doing? Consider lending your support.

l3lessed
Posts: 752
Joined: Mon Aug 12, 2019 4:32 pm
Contact:

Re: [Mod Request] Floor type affects movement speed

Post by l3lessed »

Okay, I have a working solution using dictionaries and GUID system object converted to a string to store modders unique values. It works in its current state, but is really basic.

You access the dictionary property by using a set of routines to ensure developers can easily store and remove custom speed modifier values. As before, once it detects a modifier value has been loaded, when refreshed, it will to a for loop to properly compute the end modified value after sequentially changing them based on the loaded dictionary values.

What I need to add for other modders.
  • ModifierPriority: This routine will allow you to shift your modifier value to where ever you wish in the dictionary so you can move up and down your modifier value. This is important if you want to increase it's priority in the sequential calculation, and the placing will have a large affect, since it reduces by the current already modified value. It will probably have an increase, decrease, first, last, middle settings, so modders can better place their value in the list.
  • ClearModiferList: This really won't be used allot, but will be put in for basic debugging and possible mod use. It will clear out the dictionary list completely of all loaded values. I will add a string array, so you can clear out all values, except the ones you want for your modifiers
  • RetrieveModifierList: Will output the current full modifier list for modders to load into their mods, change how they wish, and store.
  • ImportModifierDictionary: Will override the old modifier dictionary and allow you to quickly upload a whole new one.
  • AppendModifierDictionary: Will allow you to append your custom modifier dictionary to the current one, in case you have multiple values you're messing with on the fly.
  • Refresh processing methods: I want to provide some extra processing options too. Modders should be able to set if they want it computed as numbers are put in, or if they want it computed from largest to smallest values or smallest to largest values. Would this be helpful or just cause modders to switch player speed way to often by changing the base calculation method mid-play.
If you think of other things needed for developers, drop them here and I will get them hooked into this.

I still have debugging and refactoring to do to ensure devs can't break movement even if they mess up using this, update the console commands for the connected properties, and then push it.
Last edited by l3lessed on Thu Feb 25, 2021 11:13 pm, edited 2 times in total.
My Daggerfall Mods: l3lessed Nexus Page

Daggerfall Unity mods: Combat Overhaul Mod

Enjoy the free work I'm doing? Consider lending your support.

l3lessed
Posts: 752
Joined: Mon Aug 12, 2019 4:32 pm
Contact:

Re: [Mod Request] Floor type affects movement speed

Post by l3lessed »

Here is the code, if you have any thoughts or suggestions, let me know. I want to wrap this up in the next week and try to push it to base build, so I can use it in my mods on next release.

Code: Select all

        /// <summary>
        /// Get LiveSpeed adjusted for swimming, walking, crouching or riding
        /// </summary>
        /// <returns>Speed based on player.Stats.LiveSpeed</returns>
        public float GetBaseSpeed()
        {
            Entity.PlayerEntity player = GameManager.Instance.PlayerEntity;
            float baseSpeed = 0;
            float playerSpeed = player.Stats.LiveSpeed;
            if (playerMotor == null) // fixes null reference bug.
                playerMotor = GameManager.Instance.PlayerMotor;
            // crouching speed penalty doesn't apply if swimming.
            if (playerMotor.IsCrouching && !levitateMotor.IsSwimming)
                baseSpeed = (playerSpeed + dfCrouchBase) / classicToUnitySpeedUnitRatio;
            else if (playerMotor.IsRiding)
            {
                float rideSpeed = (GameManager.Instance.TransportManager.TransportMode == TransportModes.Cart) ? dfCartBase : dfRideBase;
                baseSpeed = (playerSpeed + rideSpeed) / classicToUnitySpeedUnitRatio;
            }
            else
            {
                baseSpeed = walkSpeedOverride;
            }
            return baseSpeed;
        }

        /// <summary>
        /// Add custom walk speed modifier to speed modifer dictionary. Returns unique ID for referencing of custom speedModifier for future manipulation.
        /// </summary>
        /// <param name="speedModifier">the amount to change players base walk speed by percentages. AKA, .75 will lower player movement by 25%. Using 0 or negatives will do nothing but return null.
        /// <param name="refreshWalkSpeed">will cause routine to also update the player speed using the list to sequentially multiply the current base value by the list modifier values.</param>
        /// <returns></returns>        
        public string AddWalkSpeedMod(float speedModifier = 0, bool refreshWalkSpeed = true)
        {
            string UUID = null;

            //if they set a speed modifier, grab the list index using count, and add item (which will be at the lastID index spot).
            if (speedModifier > 0)
            {
                UUID = System.Guid.NewGuid().ToString();
                walkSpeedModifier.Add(UUID, speedModifier);
            }

            if(refreshWalkSpeed)
                RefreshWalkSpeed();

            return UUID;
        }


        /// <summary>
        /// remove custom walk speed modifier from speed modifer dictionary using stored UID. Returns true if removed, false if not found.
        /// </summary>
        /// <param name="UUID">The Unique Universal ID created and provided when original value was added to dictionary.
        /// <param name="refreshWalkSpeed">will cause routine to also update the player speed using the list to sequentially multiply the current base value by the list modifier values.</param>
        /// <returns></returns>   
        public bool RemoveSpeedMod(string UUID, bool refreshWalkSpeed = true)
        {
            bool removed = false;

            if (UUID == "" || UUID == null)
                return removed;

            if (walkSpeedModifier.ContainsKey(UUID))
            {
                walkSpeedModifier.Remove(UUID);
                removed = true;
                Debug.Log("removed: " + UUID);
            }

            if (refreshWalkSpeed)
                RefreshWalkSpeed();

            return removed;
        }

        /// <summary>
        /// Updates the players walk speed using for loop and dictionary values to ensure proper sequential processing to get proper end speed.
        /// Processing of modifiers is processed by their addition order. First added by modder is multiplied first, and so on.
        /// </summary>
        public void RefreshWalkSpeed()
        {
            float baseWalkSpeed = GetWalkSpeed(GameManager.Instance.PlayerEntity);
            float overrideSpeed = 0;

            if(walkSpeedModifier.Count == 0)
            {
                walkSpeedOverride = baseWalkSpeed;
                return;
            }

            using (var modifierValue = walkSpeedModifier.GetEnumerator())
            {
                if (modifierValue.MoveNext())
                {
                    overrideSpeed = baseWalkSpeed * modifierValue.Current.Value;

                    Debug.Log("First Modified: " + overrideSpeed);

                    while (modifierValue.MoveNext())
                    {
                        Debug.Log(modifierValue.Current.Key.ToString() +  " Modified: " + overrideSpeed);
                        overrideSpeed = overrideSpeed * modifierValue.Current.Value;
                    }
                }
            }

            walkSpeedOverride = overrideSpeed;
        }
Here is how I use it between my two mods.

Interactive tile updates it on tile change. It has a string property to store the most recent unique ID number for the value it is shoving into the dictionary. Anytime a tile changes, it tries to remove the previous tile speed change by using the stored UID in the string property. If it's not made any changes yet, it will just return false and do nothing on remove. If it has pushed in a change with provided UID through the mod, SpeedChanger will remove it from the list and the new modifier is then added and UID is stored again by the mod for future manipulation.

Code: Select all

   // Update is called once per frame
    void Update()
    {
        if (OnTileChange())
        {
            List<float> TileProperties = TileProperty(lastTile);
            movementModifier(TileProperties[0]);
            Debug.Log("New Ground Tile: " + lastTile.ToString() + " | " + TileProperties[0].ToString());
        }
    }

    void movementModifier(float moveModifier)
    {
        Debug.Log(playerSpeedChanger.RemoveSpeedMod(modiferValueID).ToString());
        modiferValueID = playerSpeedChanger.AddWalkSpeedMod(moveModifier);
    }

    bool OnTileChange()
    {
        int currentTile = GameManager.Instance.StreamingWorld.PlayerTileMapIndex;
        if (currentTile != lastTile)
        {
            lastTile = currentTile;

            return true;
        }
        return false;
    }
For ambidexterity mod, I use the same add, store UID in private mod string, and remove method. The only difference is since this is tied to player states, I had to throw in some quick, sloppy bool triggers to ensure it triggers once and doesn't keep adding and removing from the dictionary.

Code: Select all

        //sets players current movement using overrides and override float values. Ensures triggered only once when setting values to save update cycles.
        void movementModifier()
        {
            //the player isn't sheathed, idle, and player unsheathed override don't match current unsheathed movement calculated number, set unsheathed movement speed.
            if(!GameManager.Instance.WeaponManager.Sheathed && AttackState == 0 && !restoreUnsheathed)
            {
                Debug.Log(playerSpeedChanger.RemoveSpeedMod(modiferValueID).ToString());
                modiferValueID = playerSpeedChanger.AddWalkSpeedMod(.5f);
                restoreUnsheathed = true;
                restoredWalk = false;
                return;
            }
            //if the player is attacking, and has unsheathed movement speed already set in override, set to attack movement speed.
            if (AttackState != 0 && !GameManager.Instance.WeaponManager.Sheathed && !attackApplied)
            {
                Debug.Log(playerSpeedChanger.RemoveSpeedMod(modiferValueID).ToString());
                modiferValueID = playerSpeedChanger.AddWalkSpeedMod(.25f);
                attackApplied = true;
                restoreUnsheathed = false;
                return;
            }
            //if using override speeds and player is sheathed, flip off override and return to default/classic speeds.
            if (GameManager.Instance.WeaponManager.Sheathed && !restoredWalk)
            {
                Debug.Log(playerSpeedChanger.RemoveSpeedMod(modiferValueID).ToString()); 
                restoredWalk = true;
                restoreUnsheathed = false;
                return;
            }
        }
My Daggerfall Mods: l3lessed Nexus Page

Daggerfall Unity mods: Combat Overhaul Mod

Enjoy the free work I'm doing? Consider lending your support.

l3lessed
Posts: 752
Joined: Mon Aug 12, 2019 4:32 pm
Contact:

Re: [Mod Request] Floor type affects movement speed

Post by l3lessed »

Worked on this for an hour this morning, when I had a break from work stuff. I think the code itself is bug proof and ready to go in base release. I just need to copy this for overriding running speed. I will keep walk and run separate values, as they are in the base build.

What I did today was some small refactoring improvements. The biggest is removing the refresh routine as a public routine to call and instead setup a bool switch that is flipped to true anytime the speed changes through add or remove routine calls or the player forces it themselves by flipping the public bool switch. This allows players to still load values up and then trigger them at a later time or when the next mod updates the walk speed, but doesn't expose the refresh routine to shenanigan's.

Now walkSpeedOverride property is the way modders grab current walk speed values, and there is no switch to turn on and off override. If modders haven't inserted custom values, it will return the base walk value like classic. If they add any modifiers to the list and hit the update switch, it will update walkSpeedOverride to the newest walk value after all added modifications. No more having two separate speeds to try and toggle between, while trying to keep the formula correct, base speed set, and ensure other modders aren't also screwing with it too. Just inject value, hit refresh bool switch, and forget it until you need to remove it.

Here's the code as of now.

Code: Select all

/// <summary>
        /// Updates the players walk speed using for loop and dictionary values to ensure proper sequential processing to get proper end speed.
        /// Processing of modifiers is processed by their addition order. First added by modder is multiplied first, and so on.
        /// </summary>
        private float RefreshWalkSpeed()
        {
            //setup and grab needed base values for computing end speed.
            float baseWalkSpeed = GetWalkSpeed(GameManager.Instance.PlayerEntity);
            float overrideSpeed = 0;

            //if there are no modifiers in the dictionary, return the base walk speed.
            if(walkSpeedModifier.Count == 0)
                return baseWalkSpeed;

            //if updateWalkSpeed switch is turned to true, update walk speed using an if then and while loop.
            if(updateWalkSpeed)
            {
                //shunt collection as a numerator into a object var to be used.
                using (var modifierValue = walkSpeedModifier.GetEnumerator())
                {
                    //if the first item is moved do this, calculate speed using base speed as the starting point.
                    if (modifierValue.MoveNext())
                    {
                        overrideSpeed = baseWalkSpeed * modifierValue.Current.Value;

                        //once first move next is done to get speed from base value, start while loop to sequentally calculate subsequent values.
                        while (modifierValue.MoveNext())
                        {
                            Debug.Log(modifierValue.Current.Key.ToString() + " Modified: " + overrideSpeed);
                            overrideSpeed = overrideSpeed * modifierValue.Current.Value;
                        }
                    }
                }
                //assign override speed and switch updateWalkSpeed to false to stop it from looping every time it is called.
                //only want it to loop when collection changes - AKA, add, remove, ect - or modder forces switch flip.
                walkSpeedOverride = overrideSpeed;
                updateWalkSpeed = false;
            }

            //return the final modified walk speed.
            return walkSpeedOverride;
        }

        /// <summary>
        /// Get LiveSpeed adjusted for walking
        /// </summary>
        /// <param name="player">the PlayerEntity to use</param>
        /// <returns></returns>
        public float GetWalkSpeed(Entity.PlayerEntity player)
        {
            float drag = 0.5f * (100 - (player.Stats.LiveSpeed >= 30 ? player.Stats.LiveSpeed : 30));
            return (player.Stats.LiveSpeed + dfWalkBase - drag) / classicToUnitySpeedUnitRatio;
        }
My Daggerfall Mods: l3lessed Nexus Page

Daggerfall Unity mods: Combat Overhaul Mod

Enjoy the free work I'm doing? Consider lending your support.

l3lessed
Posts: 752
Joined: Mon Aug 12, 2019 4:32 pm
Contact:

Re: [Mod Request] Floor type affects movement speed

Post by l3lessed »

Okay, added in the separate run speed modifier and supporting routines and properties. The previous properties and routines are still present and the same, outside the old override switches since they are not needed anymore.

Currently we have these public properties:
  • float walkSpeedOverride = The current walk speed after being computed sequentially.
  • float runSpeedOverride = The current runspeed after being computed sequentially.
  • bool updateWalkSpeed = turn true to recompute the walk speed; will flip false once done on next frame.
  • bool updateRunSpeed = turn true to recompute the run speed; will flip false once done on next frame.
Currently we have these public routines:
  • String AddWalkSpeedMod(): Add a percentage multiplier to the walk speed modifier list. Returns unique ID as string value to refer to/manipulate modifier at later point.
  • String AddRunSpeedMod(): Add a percentage multiplier to the runspeed modifier list. Returns unique ID as string value to refer to/manipulate modifier at later point.
  • bool RemoveSpeedMod(): Remove a speed modifier using previously generated unique ID string value. Allows you to select if your removing running or walking speed modifiers. Returns true or false based on if modifier was removed or not.
Using these properties and routines, anyone should be able to modify player speeds how they want without fear of overriding or breaking previous mod changes. As of now, whoever adds first to the list gets computed first, and so on. I think i'll keep it this way, as it makes the most sense, and allows players to shift mods to pick which takes priority when being computed.

Console commands still need updated to allow players to change these values in game, like with the old override methods.
My Daggerfall Mods: l3lessed Nexus Page

Daggerfall Unity mods: Combat Overhaul Mod

Enjoy the free work I'm doing? Consider lending your support.

Post Reply