Combat Overhaul Alpha
- Yagiza
- Posts: 120
- Joined: Wed Jul 31, 2019 5:16 pm
Re: Combat Overhaul Alpha
This mod looks promising.
Do you plan to add left-hand weapon mirroring and maybe two-handed fight?
Do you plan to add left-hand weapon mirroring and maybe two-handed fight?
-
- Posts: 241
- Joined: Sat May 23, 2015 2:22 pm
Re: Combat Overhaul Alpha
I think we are having a disconnect here. I just want to shoot you renders so you can do your thing and thought id add some of my own perspectives.
1.) I'm using .blendr and .jpegs. I can export into whatever is needed.
2.) I am NOT suggesting importing models for weapons at all. I'm talking about handing you renders to work with with your combat overhaul mod.
3.) I was stating my philosophy as to my vision for what combat might look like. I'll make the models all the same, but I'd like to make em better is all.
Just let me know what render format you need to work with the game engine and whatever else you need and I'll try my best as a hobby. I'm not very good with blender , but I'll try my best. To me this is just a hobby because I love this game and I like what you're trying to do so I just thought I'd try to lend you a hand not take over your project
1.) I'm using .blendr and .jpegs. I can export into whatever is needed.
2.) I am NOT suggesting importing models for weapons at all. I'm talking about handing you renders to work with with your combat overhaul mod.
3.) I was stating my philosophy as to my vision for what combat might look like. I'll make the models all the same, but I'd like to make em better is all.
Just let me know what render format you need to work with the game engine and whatever else you need and I'll try my best as a hobby. I'm not very good with blender , but I'll try my best. To me this is just a hobby because I love this game and I like what you're trying to do so I just thought I'd try to lend you a hand not take over your project
-
- Posts: 241
- Joined: Sat May 23, 2015 2:22 pm
Re: Combat Overhaul Alpha
Now if you want me to shoot you the 3D file as well so you can play around with it and see how they animations working such I can do that too, I just was under the impression I was sending renders.
Did you want me to send you that file and try to like figure out the animation system for you?
Did you want me to send you that file and try to like figure out the animation system for you?
-
- Posts: 1409
- Joined: Mon Aug 12, 2019 4:32 pm
- Contact:
Re: Combat Overhaul Alpha
As of now, first release will consist of a separate DFU branch using the most updated DFU build; I'm still coding in the previous build version, but will move to the latest right before release. Just waiting to update to the newest, as another build might drop between now and my release.Hazelnut wrote: ↑Thu Oct 17, 2019 5:58 pmSo, if I understand you correctly here, you're planning to release custom builds of DFU for this mod rather than a dfmod file that can be managed by mod system? I can understand why, but wondering how this could be avoided. I'm planning to make factories for UI and manager classes, which would mean you would be able to register a custom WeaponManager script from a mod. Would this be enough? I assume your code is not in a public github repo so I could look myself?
I would prefer to have this as a stand alone mod, so players could use it with the main DFU build releases. However, I am positive most my animation tricks it can't be done right now because I'm having to hijack private routines and ienumerators to get UI to update weapon offsets between each frame render and ram in specific sprites on specific frames. When it comes to my custom weapon speed modifiers, that can easily be done as a stand alone mode. I'm not sure about my unique weapon ranges for the weapons, as I hijacked the itemhelper script to put in a whole new range var for all items.
I have the account setup; just need to link it to my current build and drop the link. Give me maybe a week, and I will get up a github for my current build, even if I haven't finished what I want; I would much rather work with the direction the main build is going than create a whole new DFU branch just for this mod. The code is also still semi-sloppy; I'm positive there are a few areas I can minimize cpu cycle demand with some better coding.
I'll post my fpsweapon script code below.
Code: Select all
// Project: Daggerfall Tools For Unity
// Copyright: Copyright (C) 2009-2019 Daggerfall Workshop
// Web Site: http://www.dfworkshop.net
// License: MIT License (http://www.opensource.org/licenses/mit-license.php)
// Source Code: https://github.com/Interkarma/daggerfall-unity
// Original Author: Gavin Clayton (interkarma@dfworkshop.net)
// Contributors: Hazelnut
//
// Notes:
//
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using DaggerfallConnect;
using DaggerfallWorkshop.Game.Items;
using DaggerfallConnect.Utility;
using DaggerfallConnect.Arena2;
using DaggerfallWorkshop.Utility;
using DaggerfallWorkshop.Utility.AssetInjection;
using DaggerfallWorkshop.Game.Entity;
using DaggerfallWorkshop.Game.Serialization;
using Random = UnityEngine.Random;
namespace DaggerfallWorkshop.Game
{
/// <summary>
/// Renders first-person weapons and attack animations.
/// Recommended for this component to be on its own game object.
/// Cam modify pitch of audio source for different weapon speed effects.
/// </summary>
[RequireComponent(typeof(DaggerfallAudioSource))]
public class FPSWeapon : MonoBehaviour
{
public bool ShowWeapon = true;
public int AttackSpeed = 0;
public bool FlipHorizontal = false;
public WeaponTypes WeaponType = WeaponTypes.None;
public MetalTypes MetalType = MetalTypes.None;
public float Reach = 2.5f;
public float AttackSpeedScale = 1.0f;
public float Cooldown = 0.0f;
public float time;
public float minrange;
public float maxrange;
public SoundClips DrawWeaponSound = SoundClips.DrawWeapon;
public SoundClips SwingWeaponSound = SoundClips.SwingMediumPitch;
WeaponTypes currentWeaponType;
MetalTypes currentMetalType;
const int nativeScreenWidth = 320;
const int nativeScreenHeight = 200;
readonly byte[] leftUnarmedAnims = { 0, 1, 2, 3, 4, 2, 1, 0 };
int leftUnarmedAnimIndex = 0;
DaggerfallUnity dfUnity;
CifRciFile cifFile;
Texture2D weaponAtlas;
Rect[] weaponRects;
RecordIndex[] weaponIndices;
Rect weaponPosition;
float weaponScaleX;
float weaponScaleY;
DaggerfallAudioSource dfAudioSource;
WeaponAnimation[] weaponAnims;
WeaponStates weaponState = WeaponStates.Idle;
int currentFrame = 0;
int animTicks = 0;
Rect curAnimRect;
public bool hitObject;
float timer = 0;
float bob = 0;
float posi = 0;
bool posiswitch = false;
bool bobSwitch;
PlayerEntity playerEntity;
readonly Dictionary<int, Texture2D> customTextures = new Dictionary<int, Texture2D>();
Texture2D curCustomTexture;
//bool usingRightHand = true;
//DaggerfallUnityItem currentRightHandWeapon = null;
//DaggerfallUnityItem currentLeftHandWeapon = null;
public bool HitObject
{
get { return hitObject; }
set { hitObject = value; }
}
#region Properties
public WeaponStates WeaponState { get { return weaponState; } }
#endregion
void Start()
{
dfUnity = DaggerfallUnity.Instance;
dfAudioSource = GetComponent<DaggerfallAudioSource>();
StartCoroutine(AnimateWeapon());
}
void OnGUI()
{
GUI.depth = 1;
// Must be ready and not loading the game
if (!ReadyCheck() || WeaponType == WeaponTypes.None || GameManager.IsGamePaused || SaveLoadManager.Instance.LoadInProgress)
return;
// Must have current weapon texture atlas
if (weaponAtlas == null || WeaponType != currentWeaponType || MetalType != currentMetalType)
{
LoadWeaponAtlas();
if (weaponAtlas == null)
return;
}
UpdateWeapon();
if (Event.current.type.Equals(EventType.Repaint) && ShowWeapon)
{
// Draw weapon texture behind other HUD elements
GUI.DrawTextureWithTexCoords(weaponPosition, curCustomTexture ? curCustomTexture : weaponAtlas, curAnimRect);
}
}
public void OnAttackDirection(WeaponManager.MouseDirections direction)
{
// Get state based on attack direction
WeaponStates state;
switch (direction)
{
case WeaponManager.MouseDirections.Down:
state = WeaponStates.StrikeDown;
break;
case WeaponManager.MouseDirections.DownLeft:
state = WeaponStates.StrikeDownLeft;
break;
case WeaponManager.MouseDirections.Left:
state = WeaponStates.StrikeLeft;
break;
case WeaponManager.MouseDirections.Right:
state = WeaponStates.StrikeRight;
break;
case WeaponManager.MouseDirections.DownRight:
state = WeaponStates.StrikeDownRight;
break;
case WeaponManager.MouseDirections.Up:
state = WeaponStates.StrikeUp;
break;
default:
return;
}
// Do not change if already playing attack animation, unless releasing an arrow (bow & state=up->down)
if (!IsPlayingOneShot() || (WeaponType == WeaponTypes.Bow && weaponState == WeaponStates.StrikeUp && state == WeaponStates.StrikeDown))
ChangeWeaponState(state);
}
public void ChangeWeaponState(WeaponStates state)
{
weaponState = state;
// Only reset frame to 0 for bows if idle state
if (WeaponType != WeaponTypes.Bow || state == WeaponStates.Idle)
currentFrame = animTicks = 0;
UpdateWeapon();
}
public bool IsAttacking()
{
return IsPlayingOneShot();
}
public int GetHitFrame()
{
if (WeaponType == WeaponTypes.Bow)
return 5;
else
return 2;
}
public int GetCurrentFrame()
{
return currentFrame;
}
public float GetAnimTime()
{
return animTicks * GetAnimTickTime();
}
public void PlayActivateSound()
{
if (dfAudioSource)
{
dfAudioSource.AudioSource.pitch = 1f;// *AttackSpeedScale;
dfAudioSource.PlayOneShot(DrawWeaponSound, 0);
}
}
public void PlaySwingSound()
{
if (dfAudioSource)
{
dfAudioSource.AudioSource.pitch = 1f * AttackSpeedScale;
dfAudioSource.PlayOneShot(SwingWeaponSound, 0, 1.1f);
}
}
public void PlayAttackVoice(SoundClips customSound = SoundClips.None)
{
if (dfAudioSource)
{
if (customSound == SoundClips.None)
{
PlayerEntity playerEntity = GameManager.Instance.PlayerEntity;
SoundClips sound = DaggerfallEntity.GetRaceGenderAttackSound(playerEntity.Race, playerEntity.Gender, true);
float pitch = dfAudioSource.AudioSource.pitch;
dfAudioSource.AudioSource.pitch = pitch + UnityEngine.Random.Range(0, 0.3f);
dfAudioSource.PlayOneShot(sound, 0, 1f);
dfAudioSource.AudioSource.pitch = pitch;
}
else
{
dfAudioSource.PlayOneShot(customSound, 0, 1f);
}
}
}
#region Private Methods
private bool IsPlayingOneShot()
{
return (weaponState != WeaponStates.Idle);
}
private void UpdateWeapon()
{
// Do nothing if weapon not ready
if (weaponAtlas == null || weaponAnims == null ||
weaponRects == null || weaponIndices == null)
{
return;
}
// Reset state if weapon not visible
if (!ShowWeapon || WeaponType == WeaponTypes.None)
{
currentFrame = 0;
}
// Handle bow with no arrows
if (!GameManager.Instance.WeaponManager.Sheathed && WeaponType == WeaponTypes.Bow && GameManager.Instance.PlayerEntity.Items.GetItem(Items.ItemGroups.Weapons, (int)Items.Weapons.Arrow) == null)
{
GameManager.Instance.WeaponManager.SheathWeapons();
DaggerfallUI.SetMidScreenText(UserInterfaceWindows.HardStrings.youHaveNoArrows);
}
// Store rect and anim
int weaponAnimRecordIndex;
if (WeaponType == WeaponTypes.Bow)
weaponAnimRecordIndex = 0; // Bow has only 1 animation
else
weaponAnimRecordIndex = (int)weaponState;
WeaponAnimation anim = weaponAnims[(int)weaponState];
try
{
bool isImported = customTextures.TryGetValue(MaterialReader.MakeTextureKey(0, (byte)weaponAnimRecordIndex, (byte)currentFrame), out curCustomTexture);
if (FlipHorizontal && (weaponState == WeaponStates.Idle || weaponState == WeaponStates.StrikeDown || weaponState == WeaponStates.StrikeUp))
{
// Mirror weapon rect
if (isImported)
{
curAnimRect = new Rect(1, 0, -1, 1);
}
else
{
Rect rect = weaponRects[weaponIndices[weaponAnimRecordIndex].startIndex + currentFrame];
curAnimRect = new Rect(rect.xMax, rect.yMin, -rect.width, rect.height);
}
}
else
{
if (weaponState == WeaponStates.Idle)
{
if ((InputManager.Instance.HasAction(InputManager.Actions.MoveRight) || InputManager.Instance.HasAction(InputManager.Actions.MoveLeft) || InputManager.Instance.HasAction(InputManager.Actions.MoveForwards) || InputManager.Instance.HasAction(InputManager.Actions.MoveBackwards)))
{
if (bob >= .10f && bobSwitch)
bobSwitch = false;
else if (bob <= 0 && !bobSwitch)
bobSwitch = true;
if (bobSwitch)
bob = bob + Random.Range(.0005f, .001f);
else
bob = bob - Random.Range(.0005f, .001f);
}
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[0].startIndex];
weaponAnimRecordIndex = 0;
anim.Offset = (bob / 1.5f) - .07f;
anim.Offsety = (bob * 1.5f) - .15f;
}
else
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[weaponAnimRecordIndex].startIndex + currentFrame];
if (weaponState == WeaponStates.StrikeLeft)
{
if (WeaponType == WeaponTypes.Flail || WeaponType == WeaponTypes.Flail_Magic)
{
if (GetCurrentFrame() == 1)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[3].startIndex + 3];
weaponAnimRecordIndex = 3;
anim.Offset = posi - .65f;
}
else if (GetCurrentFrame() == 2)
{
posi = posi + .002f;
Rect rect = weaponRects[weaponIndices[6].startIndex + 2];
curAnimRect = new Rect(rect.xMax, rect.yMin, -rect.width, rect.height);
weaponAnimRecordIndex = 6;
anim.Offset = posi + .1f;
}
else
{
anim.Offset = posi;
anim.Offsety = (posi / 2) * -1;
}
}
else if (WeaponType == WeaponTypes.Dagger || WeaponType == WeaponTypes.Dagger_Magic)
{
if (GetCurrentFrame() == 1)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[2].startIndex + 2];
weaponAnimRecordIndex = 2;
anim.Offset = posi - .25f;
}
else
{
anim.Offset = posi;
anim.Offsety = (posi / 2) * -1;
}
}
else if (WeaponType == WeaponTypes.Battleaxe_Magic)
{
if (currentFrame == 1)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[4].startIndex + currentFrame];
anim.Offset = posi;
anim.Offsety = (posi / 6) * -1;
}
else if (currentFrame == 2)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[4].startIndex + currentFrame];
anim.Offset = posi + .1f;
anim.Offsety = (posi / 6) * -1;
}
else if (currentFrame == 3)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[4].startIndex + currentFrame];
anim.Offset = posi + .2f;
anim.Offsety = (posi / 6) * -1;
}
else if (currentFrame == 4)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[4].startIndex + currentFrame];
anim.Offset = posi + .3f;
anim.Offsety = (posi / 6) * -1;
}
}
else
{
anim.Offset = posi;
anim.Offsety = (posi / 2) * -1;
}
}
else if (weaponState == WeaponStates.StrikeRight)
{
if (WeaponType == WeaponTypes.Flail || WeaponType == WeaponTypes.Flail_Magic)
{
if (currentFrame == 1)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[4].startIndex + 3];
weaponAnimRecordIndex = 4;
anim.Offset = posi - .65f;
}
else if (currentFrame == 2)
{
posi = posi + .003f;
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[6].startIndex + 2];
weaponAnimRecordIndex = 6;
anim.Offset = posi + .075f;
anim.Offsety = (posi / 2) -.1f;
}
else
{
anim.Offset = posi;
anim.Offsety = posi / 2;
}
}
else if (WeaponType == WeaponTypes.Dagger || WeaponType == WeaponTypes.Dagger_Magic)
{
if (currentFrame == 1)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[5].startIndex + 2];
weaponAnimRecordIndex = 5;
anim.Offsety = posi + .25f;
anim.Offset = posi - .25f;
}
else
{
anim.Offset = posi;
anim.Offsety = posi / 2;
}
}
else if (WeaponType == WeaponTypes.Battleaxe_Magic)
{
if (currentFrame == 1)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[5].startIndex + currentFrame];
anim.Offset = posi;
anim.Offsety = (posi / 4) * -1;
}
else if (currentFrame == 2)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[5].startIndex + currentFrame];
anim.Offset = posi + .1f;
anim.Offsety = (posi / 4) * -1;
}
else if (currentFrame == 3)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[5].startIndex + currentFrame];
anim.Offset = posi + .2f;
anim.Offsety = (posi / 4) * -1;
}
else if (currentFrame == 4)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[5].startIndex + currentFrame];
anim.Offset = posi + .3f;
anim.Offsety = (posi / 4) * -1;
}
}
else
{
anim.Offset = posi;
anim.Offsety = posi / 2;
}
}
else if (weaponState == WeaponStates.StrikeDown)
{
if (WeaponType == WeaponTypes.Flail || WeaponType == WeaponTypes.Flail_Magic)
{
if (currentFrame <= 1)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[2].startIndex + 1];
weaponAnimRecordIndex = 2;
anim.Offset = (posi / 4) - .27f;
anim.Offsety = .21f - posi;
}
else if (currentFrame == 2)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[6].startIndex + 3];
weaponAnimRecordIndex = 6;
anim.Offset = (posi / 4) - .10f;
anim.Offsety = .05f - (posi);
}
else if (currentFrame == 3)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[6].startIndex + 2];
weaponAnimRecordIndex = 6;
anim.Offset = posi / 4;
anim.Offsety = ((posi) * -1) - .05f;
}
else
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[6].startIndex + 1];
weaponAnimRecordIndex = 6;
anim.Offset = posi / 4;
anim.Offsety = ((posi) * -1) - .1f;
}
}
else if (WeaponType == WeaponTypes.Dagger || WeaponType == WeaponTypes.Dagger_Magic)
{
if (currentFrame <= 1)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[2].startIndex + 2];
weaponAnimRecordIndex = 2;
anim.Offset = (posi / 2) - .2f;
anim.Offsety = ((posi) * -1) + .05f;
}
else
{
anim.Offset = posi / 4;
anim.Offsety = (posi) * -1;
}
}
else if (WeaponType == WeaponTypes.Battleaxe || WeaponType == WeaponTypes.Battleaxe_Magic)
{
if (currentFrame <= 1)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[6].startIndex + 3];
weaponAnimRecordIndex = 1;
anim.Offset = (posi / 4) - .025f;
anim.Offsety = (posi * -1) + .1f;
}
else if (currentFrame == 2)
{
posi = posi + .003f;
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[6].startIndex + 4];
weaponAnimRecordIndex = 1;
anim.Offset = (posi / 4) + .05f;
anim.Offsety = (posi * -1) - .2f;
}
else if (currentFrame == 3)
{
posi = posi + .003f;
anim.Offset = (posi / 4) + .1f;
anim.Offsety = ((posi) * -1) - .05f;
}
else
{
posi = posi + .003f;
anim.Offset = (posi / 4) + .1f;
anim.Offsety = ((posi) * -1) - .15f;
}
}
else
{
if (currentFrame <= 1)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[6].startIndex + 3];
weaponAnimRecordIndex = 1;
anim.Offset = (posi / 4) - .125f;
anim.Offsety = (posi * -1) + .05f;
}
else if (currentFrame == 2)
{
posi = posi + .003f;
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[6].startIndex + 4];
weaponAnimRecordIndex = 1;
anim.Offset = (posi / 4) - .075f;
anim.Offsety = (posi * -1) - .2f;
}
else if (currentFrame == 3)
{
posi = posi + .003f;
anim.Offset = (posi / 4) + .1f;
anim.Offsety = ((posi) * -1) - .05f;
}
else
{
posi = posi + .003f;
anim.Offset = (posi / 4) + .1f;
anim.Offsety = ((posi) * -1) - .15f;
}
}
}
else if (weaponState == WeaponStates.StrikeUp)
{
if ((WeaponType == WeaponTypes.Flail || WeaponType == WeaponTypes.Flail_Magic) && currentFrame < 4)
{
anim.Offsety = (posi / 2) - .22f;
}
else if ((WeaponType == WeaponTypes.Flail || WeaponType == WeaponTypes.Flail_Magic) && currentFrame == 4)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[6].startIndex + 3];
anim.Offsety = (posi / 2) - .11f;
}
else
{
if (currentFrame < 2)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[0].startIndex];
weaponAnimRecordIndex = 0;
anim.Offsety = (posi * -1) * 2.2f;
}
else if (currentFrame == 2)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[6].startIndex + currentFrame];
weaponAnimRecordIndex = 6;
posi = posi + .004f;
anim.Offsety = posi - 1.125f;
}
else if (currentFrame == 3)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[6].startIndex + currentFrame];
weaponAnimRecordIndex = 6;
posi = posi + .004f;
anim.Offsety = posi - .8f;
}
else if (currentFrame == 4)
{
curAnimRect = isImported ? new Rect(0, 0, 1, 1) : weaponRects[weaponIndices[6].startIndex + currentFrame];
weaponAnimRecordIndex = 6;
posi = posi + .004f;
anim.Offsety = posi - .4f;
}
}
}
}
}
// Get weapon dimensions
int width = weaponIndices[weaponAnimRecordIndex].width;
int height = weaponIndices[weaponAnimRecordIndex].height;
// Get weapon scale
weaponScaleX = (float)Screen.width / (float)nativeScreenWidth;
weaponScaleY = (float)Screen.height / (float)nativeScreenHeight;
// Adjust scale to be slightly larger when not using point filtering
// This reduces the effect of filter shrink at edge of display
if (dfUnity.MaterialReader.MainFilterMode != FilterMode.Point)
{
weaponScaleX *= 1.01f;
weaponScaleY *= 1.01f;
}
// Source weapon images are designed to overlay a fixed 320x200 display.
// Some weapons need to align with both top, bottom, and right of display.
// This means they might be a little stretched on widescreen displays.
switch (anim.Alignment)
{
case WeaponAlignment.Left:
AlignLeft(anim, width, height);
break;
case WeaponAlignment.Center:
AlignCenter(anim, width, height);
break;
case WeaponAlignment.Right:
AlignRight(anim, width, height);
break;
}
}
catch (IndexOutOfRangeException)
{
DaggerfallUnity.LogMessage("Index out of range exception for weapon animation. Probably due to weapon breaking + being unequipped during animation.");
}
}
private void AlignLeft(WeaponAnimation anim, int width, int height)
{
weaponPosition = new Rect(
Screen.width * anim.Offset,
Screen.height - height * weaponScaleY,
width * weaponScaleX,
height * weaponScaleY);
}
private void AlignCenter(WeaponAnimation anim, int width, int height)
{
weaponPosition = new Rect(
Screen.width / 2f - (width * weaponScaleX) / 2f,
Screen.height - height * weaponScaleY,
width * weaponScaleX,
height * weaponScaleY);
}
private void AlignRight(WeaponAnimation anim, int width, int height)
{
if (FlipHorizontal && (weaponState == WeaponStates.Idle || weaponState == WeaponStates.StrikeDown || weaponState == WeaponStates.StrikeUp))
{
// Flip alignment
AlignLeft(anim, width, height);
return;
}
weaponPosition = new Rect(
(Screen.width * (1f - anim.Offset) - width * weaponScaleX),
(Screen.height * (1f - anim.Offsety) - height * weaponScaleY),
width * weaponScaleX,
height * weaponScaleY);
}
private bool ReadyCheck()
{
// Do nothing if DaggerfallUnity not ready
if (!dfUnity.IsReady)
{
DaggerfallUnity.LogMessage("FPSWeapon: DaggerfallUnity component is not ready. Have you set your Arena2 path?");
return false;
}
// Ensure cif reader is ready
if (cifFile == null)
{
cifFile = new CifRciFile();
cifFile.Palette.Load(Path.Combine(dfUnity.Arena2Path, cifFile.PaletteName));
}
//// Must have weapon texture atlas
//if (weaponAtlas == null ||
// WeaponType != lastWeaponType ||
// MetalType != lastMetalType)
//{
// LoadWeaponAtlas();
// if (weaponAtlas == null)
// return false;
// UpdateWeapon();
//}
return true;
}
//runs the image animation system frame by frame. This is where all custom animation triggers will go, like collision recoil.
IEnumerator AnimateWeapon()
{
while (true)
{
time = GetAnimTickTime();
minrange = .0025f / time;
maxrange = .005f / time;
time = time / 12;
if (posiswitch == false)
posi = 0;
if (weaponAnims != null && ShowWeapon && !GameManager.Instance.WeaponManager.hitobject)
{
int frameBeforeStepping = currentFrame;
// Special animation for unarmed attack to left
if ((WeaponType == WeaponTypes.Melee || WeaponType == WeaponTypes.Werecreature)
&& WeaponState == WeaponStates.StrikeLeft)
{
// Step frame
currentFrame = leftUnarmedAnims[leftUnarmedAnimIndex];
leftUnarmedAnimIndex++;
if (leftUnarmedAnimIndex >= leftUnarmedAnims.Length)
{
ChangeWeaponState(WeaponStates.Idle);
leftUnarmedAnimIndex = 0;
}
}
else if (WeaponType == WeaponTypes.Bow && weaponState == WeaponStates.StrikeUp)
{
// Step each frame for drawing the bow until reach frame for ready to release arrow
if (currentFrame < weaponAnims[(int)weaponState].NumFrames - 1)
currentFrame++;
// Record animation ticks for drawing and then holding a bow
animTicks++;
}
else
{
// Step frame
currentFrame++;
if (currentFrame >= weaponAnims[(int)weaponState].NumFrames)
{
if (IsPlayingOneShot())
{
ChangeWeaponState(WeaponStates.Idle); // If this is a one-shot anim go to queued weapon state
if (WeaponType == WeaponTypes.Bow)
ShowWeapon = false; // Immediately hide bow so its idle frame doesn't show before it is hidden for its cooldown
}
else if (WeaponType == WeaponTypes.Bow && !DaggerfallUnity.Settings.BowDrawback)
currentFrame = 3;
else
currentFrame = 0; // Otherwise keep looping frames
}
}
// Only update if the frame actually changed
if (frameBeforeStepping != currentFrame)
UpdateWeapon();
else
posiswitch = false;
}
//start parry animation if hitobject detected. Begins reversing frames at an increased speed.
//Goes to idle state at frame 0. This simulates the recoil of hitting an object with a large sword.
else if (weaponAnims != null && ShowWeapon && GameManager.Instance.WeaponManager.hitobject)
{
int frameBeforeStepping = currentFrame;
// Special animation for unarmed attack to left
if ((WeaponType == WeaponTypes.Melee || WeaponType == WeaponTypes.Werecreature)
&& WeaponState == WeaponStates.StrikeLeft)
{
// Step frame
currentFrame = leftUnarmedAnims[leftUnarmedAnimIndex];
--leftUnarmedAnimIndex;
if (leftUnarmedAnimIndex >= leftUnarmedAnims.Length)
{
ChangeWeaponState(WeaponStates.Idle);
leftUnarmedAnimIndex = 0;
}
}
//reverse attack animation if not on 0/idle frame.
else if (currentFrame >= 1)
{
// Step frame
--currentFrame;
}
//when recoil gets to frame 0, set weapon state to idle to stop attack looping.
else
{
currentFrame = 0;
posiswitch = false;
ChangeWeaponState(WeaponStates.Idle); // If this is a one-shot anim go to queued weapon state
}
// Only update if the frame actually changed
if (frameBeforeStepping != currentFrame)
UpdateWeapon();
}
//put wait timer into if then trigger. If then ensures player is in an attack animation then
//takes the attack time, divides it by 12, sets up 12 wait timers, and after each timer, offsets
//the animations x position using the global float posi (which I added).
if (WeaponState != WeaponStates.Idle && !GameManager.Instance.WeaponManager.hitobject && WeaponType != WeaponTypes.Bow)
{
yield return new WaitForSeconds(time);
posi = posi + Random.Range(minrange, maxrange);
yield return new WaitForSeconds(time);
posi = posi + Random.Range(minrange, maxrange);
yield return new WaitForSeconds(time);
posi = posi + Random.Range(minrange, maxrange);
yield return new WaitForSeconds(time);
posi = posi + Random.Range(minrange, maxrange);
yield return new WaitForSeconds(time);
posi = posi + Random.Range(minrange, maxrange);
yield return new WaitForSeconds(time);
posi = posi + Random.Range(minrange, maxrange);
yield return new WaitForSeconds(time);
posi = posi + Random.Range(minrange, maxrange);
yield return new WaitForSeconds(time);
posi = posi + Random.Range(minrange, maxrange);
yield return new WaitForSeconds(time);
posi = posi + Random.Range(minrange, maxrange);
yield return new WaitForSeconds(time);
posi = posi + Random.Range(minrange, maxrange);
yield return new WaitForSeconds(time);
posi = posi + Random.Range(minrange, maxrange);
yield return new WaitForSeconds(time);
posi = posi + Random.Range(minrange, maxrange);
}
else
{
yield return new WaitForSeconds(time);
}
}
}
private float GetAnimTickTime()
{
PlayerEntity player = GameManager.Instance.PlayerEntity;
float speed = 0;
if (WeaponType == WeaponTypes.Bow || player == null)
return GameManager.classicUpdateInterval;
else if (GameManager.Instance.WeaponManager.hitobject)
{
speed = 85;
return (speed / 980); // Approximation of classic frame update
}
else
{
speed = (3 * ((115 - player.Stats.LiveSpeed) + AttackSpeed));
return speed / 980; // Approximation of classic frame update
}
}
private void LoadWeaponAtlas()
{
// Get weapon filename
string filename = WeaponBasics.GetWeaponFilename(WeaponType);
// Load the weapon texture atlas
// Texture is dilated into a transparent coloured border to remove dark edges when filtered
// Important to use returned UV rects when drawing to get right dimensions
weaponAtlas = GetWeaponTextureAtlas(filename, MetalType, out weaponRects, out weaponIndices, 2, 2, true);
weaponAtlas.filterMode = dfUnity.MaterialReader.MainFilterMode;
// Get weapon anims
weaponAnims = (WeaponAnimation[])WeaponBasics.GetWeaponAnims(WeaponType).Clone();
// Store current weapon
currentWeaponType = WeaponType;
currentMetalType = MetalType;
}
#endregion
#region Texture Loading
private Texture2D GetWeaponTextureAtlas(
string filename,
MetalTypes metalType,
out Rect[] rectsOut,
out RecordIndex[] indicesOut,
int padding,
int border,
bool dilate = false)
{
// Load texture file
cifFile.Load(Path.Combine(dfUnity.Arena2Path, filename), FileUsage.UseMemory, true);
// Read every image in archive
Rect rect;
List<Texture2D> textures = new List<Texture2D>();
List<RecordIndex> indices = new List<RecordIndex>();
customTextures.Clear();
for (int record = 0; record < cifFile.RecordCount; record++)
{
int frames = cifFile.GetFrameCount(record);
DFSize size = cifFile.GetSize(record);
RecordIndex ri = new RecordIndex()
{
startIndex = textures.Count,
frameCount = frames,
width = size.Width,
height = size.Height,
};
indices.Add(ri);
for (int frame = 0; frame < frames; frame++)
{
textures.Add(GetWeaponTexture2D(filename, record, frame, metalType, out rect, border, dilate));
Texture2D tex;
if (TextureReplacement.TryImportCifRci(filename, record, frame, metalType, true, out tex))
{
tex.filterMode = dfUnity.MaterialReader.MainFilterMode;
tex.wrapMode = TextureWrapMode.Mirror;
customTextures.Add(MaterialReader.MakeTextureKey(0, (byte)record, (byte)frame), tex);
}
}
}
// Pack textures into atlas
Texture2D atlas = new Texture2D(2048, 2048, TextureFormat.ARGB32, false);
rectsOut = atlas.PackTextures(textures.ToArray(), padding, 2048);
indicesOut = indices.ToArray();
// Shrink UV rect to compensate for internal border
float ru = 1f / atlas.width;
float rv = 1f / atlas.height;
for (int i = 0; i < rectsOut.Length; i++)
{
Rect rct = rectsOut[i];
rct.xMin += border * ru;
rct.xMax -= border * ru;
rct.yMin += border * rv;
rct.yMax -= border * rv;
rectsOut[i] = rct;
}
return atlas;
}
private Texture2D GetWeaponTexture2D(
string filename,
int record,
int frame,
MetalTypes metalType,
out Rect rectOut,
int border = 0,
bool dilate = false)
{
// Get source bitmap
DFBitmap dfBitmap = cifFile.GetDFBitmap(record, frame);
// Tint based on metal type
// But not for steel as that is default colour in files
if (metalType != MetalTypes.Steel && metalType != MetalTypes.None)
dfBitmap = ImageProcessing.ChangeDye(dfBitmap, ImageProcessing.GetMetalDyeColor(metalType), DyeTargets.WeaponsAndArmor);
// Get Color32 array
DFSize sz;
Color32[] colors = cifFile.GetColor32(dfBitmap, 0, border, out sz);
// Dilate edges
if (border > 0 && dilate)
ImageProcessing.DilateColors(ref colors, sz);
// Create Texture2D
Texture2D texture = new Texture2D(sz.Width, sz.Height, TextureFormat.ARGB32, false);
texture.SetPixels32(colors);
texture.Apply(true);
// Shrink UV rect to compensate for internal border
float ru = 1f / sz.Width;
float rv = 1f / sz.Height;
rectOut = new Rect(border * ru, border * rv, (sz.Width - border * 2) * ru, (sz.Height - border * 2) * rv);
return texture;
}
#endregion
}
}
It also lacks proper coding notations, as I am sloppy and tend to add it after I am done coding. I found myself to many times changing or removing notes between development cycles anytime I code.
lastly, I have not updated the left handed routine to properly render these animations. I do plan on getting to it either right before first release or right after. I am a right hander, so it doesn't affect me.
If you have any questions how this is working let me know, but the simplest explanation is I recoded the ienumerator routine, which is what is constantly updating the FPS weapon UI on screen. Before, it would run a cycle 5 times for every sprite animation frame. I merely added 12 co-routines that wait a set amount of time based on the weapon animation speed. After each coroutine update, it picks a random number in a range and updates the posi variable. The Posi variable is then passed on to the weapon position routines, which assign where the UI sprite is shown on the players screen. And thus, you get appearance of a full animation only using 5 sprites.
I've been busy with wife, work, and life stuff, and then just got strip throat and bronchitis this week. It's no fun coding when your sick and hopped up on meds.
My Daggerfall Mod Github: l3lessed DFU Mod Github
My Beth Mods: l3lessed Nexus Page
Daggerfall Unity mods: Combat Overhaul Mod
Enjoy the free work I'm doing? Consider lending your support.
My Beth Mods: l3lessed Nexus Page
Daggerfall Unity mods: Combat Overhaul Mod
Enjoy the free work I'm doing? Consider lending your support.
-
- Posts: 1409
- Joined: Mon Aug 12, 2019 4:32 pm
- Contact:
Re: Combat Overhaul Alpha
I see. JPGS would work fine; they could be recompiled using the daggerfall image asset manager to create the .cif file. Then it is just updating the weaponbasics script file.1.) I'm using .blendr and .jpegs. I can export into whatever is needed.
2.) I am NOT suggesting importing models for weapons at all. I'm talking about handing you renders to work with with your combat overhaul mod.
However, you need to ensure the sprites match the same measurement dimensions as the original sprites or they will get really distorted and buggy. I believe they also need a transparent alpha layer for the background.
If you can, download the daggerfall image asset tool, open the cif files, go to the weapon sprites, and check their dimensions. As long as you match the original dimensions, then yes, I can take your set of images, recompile them into the cif so we have more frames, and then easily update the script files.
No problem on the disconnect. Happens all the time on online forums. You don't have all that extra social ques when speaking face to face.
My Daggerfall Mod Github: l3lessed DFU Mod Github
My Beth Mods: l3lessed Nexus Page
Daggerfall Unity mods: Combat Overhaul Mod
Enjoy the free work I'm doing? Consider lending your support.
My Beth Mods: l3lessed Nexus Page
Daggerfall Unity mods: Combat Overhaul Mod
Enjoy the free work I'm doing? Consider lending your support.
-
- Posts: 1409
- Joined: Mon Aug 12, 2019 4:32 pm
- Contact:
Re: Combat Overhaul Alpha
Two handed fighting was in the development cycle at one point and working, as I understand it, but they removed it. Not sure why. I imagine because it was to hard to balance. Also, two handed weapon fighting isn't a real thing for the most part. This is yet another pop culture myth. If a fighter had a second weapon, it was a smaller weapon, like a short sword or a wakazashi, and they were used as parry weapons to block attacks and allow a counter attack without exposing yourself as openly; They could also operate as a quick backup if your weapon got knocked out of your hand; or, every now and then, they could be used to get in a finished blow if the opponent opened themselves up to your offhand side. Point being, they never were used passed very specific instances.Do you plan to add left-hand weapon mirroring and maybe two-handed fight?
Their are a few mentions in historical texts, but very, very few, and they involved things like daggers. Very small weapons that wouldn't cause balance issues when duel wielding. Try to duel wield a 20 pound sword and a 10 pound sword and keep a proper stance and balance during combat; it isn't worth the trade off, so no one really did it.
I do plan on adding a parry type option, where you can use an offhand weapon to do a short parry and block enemy attacks. I do not know about duel wielding though because of the reasons above, plus balance issues, not to mention figuring out the numerous coding issues to ensure the animations don't overlap and cause glitches.
My Daggerfall Mod Github: l3lessed DFU Mod Github
My Beth Mods: l3lessed Nexus Page
Daggerfall Unity mods: Combat Overhaul Mod
Enjoy the free work I'm doing? Consider lending your support.
My Beth Mods: l3lessed Nexus Page
Daggerfall Unity mods: Combat Overhaul Mod
Enjoy the free work I'm doing? Consider lending your support.
-
- Posts: 541
- Joined: Thu Jul 18, 2019 3:08 am
Re: Combat Overhaul Alpha
Deleted
Last edited by Ommamar on Sat Apr 18, 2020 12:30 am, edited 1 time in total.
-
- Posts: 1409
- Joined: Mon Aug 12, 2019 4:32 pm
- Contact:
Re: Combat Overhaul Alpha
Wonderful read. Seems to backup my point, that outside very specific settings that usually involved an audience, rules, and some form of an arena, fighters chose either sword + shield or two handed swords. It also points out duel wielding doesn't show up until pretty late in the texts and lacks any real in-depth discussion or backing.
Yeah, I want it more like what you describe. A parry weapon for positioning and controlling your enemies attacks/movements and a main attack weapon for actually landing the killing blow.
The one exception I was thinking of for long term development is allow certain short, light weight weapons to be duel wielded, like short swords, daggers, or the sort. Not sure though, but if it would happen, that is how I would do it.
Yeah, I want it more like what you describe. A parry weapon for positioning and controlling your enemies attacks/movements and a main attack weapon for actually landing the killing blow.
The one exception I was thinking of for long term development is allow certain short, light weight weapons to be duel wielded, like short swords, daggers, or the sort. Not sure though, but if it would happen, that is how I would do it.
My Daggerfall Mod Github: l3lessed DFU Mod Github
My Beth Mods: l3lessed Nexus Page
Daggerfall Unity mods: Combat Overhaul Mod
Enjoy the free work I'm doing? Consider lending your support.
My Beth Mods: l3lessed Nexus Page
Daggerfall Unity mods: Combat Overhaul Mod
Enjoy the free work I'm doing? Consider lending your support.
- Yagiza
- Posts: 120
- Joined: Wed Jul 31, 2019 5:16 pm
Re: Combat Overhaul Alpha
The most important part was not two-handed fight, but mirroring of left-hand weapon.l3lessed wrote: ↑Fri Oct 18, 2019 9:46 pm Two handed fighting was in the development cycle at one point and working, as I understand it, but they removed it. Not sure why. I imagine because it was to hard to balance. Also, two handed weapon fighting isn't a real thing for the most part. This is yet another pop culture myth. If a fighter had a second weapon, it was a smaller weapon, like a short sword or a wakazashi, and they were used as parry weapons to block attacks and allow a counter attack without exposing yourself as openly; They could also operate as a quick backup if your weapon got knocked out of your hand; or, every now and then, they could be used to get in a finished blow if the opponent opened themselves up to your offhand side. Point being, they never were used passed very specific instances.
Their are a few mentions in historical texts, but very, very few, and they involved things like daggers. Very small weapons that wouldn't cause balance issues when duel wielding. Try to duel wield a 20 pound sword and a 10 pound sword and keep a proper stance and balance during combat; it isn't worth the trade off, so no one really did it.
I do plan on adding a parry type option, where you can use an offhand weapon to do a short parry and block enemy attacks. I do not know about duel wielding though because of the reasons above, plus balance issues, not to mention figuring out the numerous coding issues to ensure the animations don't overlap and cause glitches.
And maybe shield display.
-
- Posts: 541
- Joined: Thu Jul 18, 2019 3:08 am
Re: Combat Overhaul Alpha
l3lessed wrote: ↑Sat Oct 19, 2019 8:42 am Wonderful read. Seems to backup my point, that outside very specific settings that usually involved an audience, rules, and some form of an arena, fighters chose either sword + shield or two handed swords. It also points out duel wielding doesn't show up until pretty late in the texts and lacks any real in-depth discussion or backing.
Yeah, I want it more like what you describe. A parry weapon for positioning and controlling your enemies attacks/movements and a main attack weapon for actually landing the killing blow.
The one exception I was thinking of for long term development is allow certain short, light weight weapons to be duel wielded, like short swords, daggers, or the sort. Not sure though, but if it would happen, that is how I would do it.
Last edited by Ommamar on Sat Apr 18, 2020 12:31 am, edited 1 time in total.