forked from Shardstone/trail-into-darkness
added full characte creation support
This commit is contained in:
@@ -81,10 +81,10 @@ namespace Nox.Core {
|
|||||||
var characterRegistry = Addressables.LoadAssetAsync<CharacterRegistry>("CharacterRegistry").WaitForCompletion();
|
var characterRegistry = Addressables.LoadAssetAsync<CharacterRegistry>("CharacterRegistry").WaitForCompletion();
|
||||||
var modifiersRegistry = Addressables.LoadAssetAsync<ModifiersRegistry>("ModifiersRegistry").WaitForCompletion();
|
var modifiersRegistry = Addressables.LoadAssetAsync<ModifiersRegistry>("ModifiersRegistry").WaitForCompletion();
|
||||||
var partySettings = Addressables.LoadAssetAsync<PartySettings>("DefaultPartySettings").WaitForCompletion();
|
var partySettings = Addressables.LoadAssetAsync<PartySettings>("DefaultPartySettings").WaitForCompletion();
|
||||||
var portraitsHolder = Addressables.LoadAssetAsync<PortraitsHolder>("PortraitsHolder").WaitForCompletion();
|
var portraitsHolder = Addressables.LoadAssetAsync<PortraitsHolder>(assetHandle.Result.portraitsHolder).WaitForCompletion();
|
||||||
var characterSystems = CharacterSystemsFactory.Create(partySettings, characterBaseSettings, perKRegistry, characterRegistry, modifiersRegistry);
|
var characterSystems = CharacterSystemsFactory.Create(partySettings, characterBaseSettings, perKRegistry, characterRegistry, modifiersRegistry);
|
||||||
|
|
||||||
mainMenuView = new MainMenuView(assetHandle.Result, menuGameStateData, saveSystem, gameDataState, partySettings, characterSystems, portraitsHolder);
|
mainMenuView = new MainMenuView(assetHandle.Result, menuGameStateData, saveSystem, gameDataState, partySettings, characterSystems, portraitsHolder, characterBaseSettings);
|
||||||
mainMenuView.Initialize();
|
mainMenuView.Initialize();
|
||||||
mainMenuView.Show();
|
mainMenuView.Show();
|
||||||
IsGameStateInitialized = true;
|
IsGameStateInitialized = true;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace Nox.Game {
|
|||||||
attributes = attributes.attributes.AsValueEnumerable()
|
attributes = attributes.attributes.AsValueEnumerable()
|
||||||
.Select(a => {
|
.Select(a => {
|
||||||
var modifiers = modifierResolver.CollectModifiers(entityDefinition, a.attribute);
|
var modifiers = modifierResolver.CollectModifiers(entityDefinition, a.attribute);
|
||||||
return new Attribute(a.attribute, modifierResolver.Resolve(a.value, modifiers));
|
return new Attribute(a.attribute, modifierResolver.Resolve(a.value, modifiers, entityDefinition));
|
||||||
})
|
})
|
||||||
.ToArray()
|
.ToArray()
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ namespace Nox.Game {
|
|||||||
public sealed class CharacterTemplate : IEntityDefinition {
|
public sealed class CharacterTemplate : IEntityDefinition {
|
||||||
public Guid Id { get; set; } = Guid.NewGuid();
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
public string Name { get; set; } = "New Character";
|
public string Name { get; set; } = "New Character";
|
||||||
|
public int PortraitIndex { get; set; }
|
||||||
public CharacterRace Race { get; set; } = (CharacterRace)GetRandomInt(1, Enum.GetValues(typeof(CharacterRace)).Length-1);
|
public CharacterRace Race { get; set; } = (CharacterRace)GetRandomInt(1, Enum.GetValues(typeof(CharacterRace)).Length-1);
|
||||||
public CharacterClass Class { get; set; } = (CharacterClass)GetRandomInt(1, Enum.GetValues(typeof(CharacterClass)).Length-1);
|
public CharacterClass Class { get; set; } = (CharacterClass)GetRandomInt(1, Enum.GetValues(typeof(CharacterClass)).Length-1);
|
||||||
public CharacterRole Role { get; set; } = CharacterRole.Companion;
|
public CharacterRole Role { get; set; } = CharacterRole.Companion;
|
||||||
@@ -38,6 +39,7 @@ namespace Nox.Game {
|
|||||||
public sealed class CharacterCreationRequest : IEntityDefinition {
|
public sealed class CharacterCreationRequest : IEntityDefinition {
|
||||||
public Guid Id { get; set; } = Guid.Empty;
|
public Guid Id { get; set; } = Guid.Empty;
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
public int PortraitIndex { get; set; }
|
||||||
public CharacterRace Race { get; set; }
|
public CharacterRace Race { get; set; }
|
||||||
public CharacterClass Class { get; set; }
|
public CharacterClass Class { get; set; }
|
||||||
public CharacterRole Role { get; set; } = CharacterRole.Protagonist;
|
public CharacterRole Role { get; set; } = CharacterRole.Protagonist;
|
||||||
@@ -78,6 +80,7 @@ namespace Nox.Game {
|
|||||||
Race = request.Race,
|
Race = request.Race,
|
||||||
Class = request.Class,
|
Class = request.Class,
|
||||||
Role = CharacterRole.Protagonist,
|
Role = CharacterRole.Protagonist,
|
||||||
|
PortraitIndex = request.PortraitIndex,
|
||||||
Attributes = attributes,
|
Attributes = attributes,
|
||||||
Stats = stats,
|
Stats = stats,
|
||||||
Perks = request.Perks ?? new PerksData(),
|
Perks = request.Perks ?? new PerksData(),
|
||||||
|
|||||||
@@ -23,16 +23,16 @@ namespace Nox.Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var healthModifiers = modifierResolver.CollectModifiers(entityDefinition, StatType.Health);
|
var healthModifiers = modifierResolver.CollectModifiers(entityDefinition, StatType.Health);
|
||||||
var staminaModifiers = modifierResolver.CollectModifiers(entityDefinition, StatType.Stamina);
|
var staminaModifiers = modifierResolver.CollectModifiers(entityDefinition, StatType.Mana);
|
||||||
var levelModifiers = modifierResolver.CollectModifiers(entityDefinition, StatType.Level);
|
var levelModifiers = modifierResolver.CollectModifiers(entityDefinition, StatType.Level);
|
||||||
var experienceModifiers = modifierResolver.CollectModifiers(entityDefinition, StatType.Experience);
|
var experienceModifiers = modifierResolver.CollectModifiers(entityDefinition, StatType.Experience);
|
||||||
|
|
||||||
return new EntityStats {
|
return new EntityStats {
|
||||||
stats = new[] {
|
stats = new[] {
|
||||||
new Stat(StatType.Health, modifierResolver.Resolve(baseSettings.defaultEntityStats.GetValue(StatType.Health), healthModifiers)),
|
new Stat(StatType.Health, modifierResolver.Resolve(baseSettings.defaultEntityStats.GetValue(StatType.Health), healthModifiers, entityDefinition)),
|
||||||
new Stat(StatType.Stamina, modifierResolver.Resolve(baseSettings.defaultEntityStats.GetValue(StatType.Stamina), staminaModifiers)),
|
new Stat(StatType.Mana, modifierResolver.Resolve(baseSettings.defaultEntityStats.GetValue(StatType.Mana), staminaModifiers, entityDefinition)),
|
||||||
new Stat(StatType.Level, modifierResolver.Resolve(baseSettings.defaultEntityStats.GetValue(StatType.Level), levelModifiers)),
|
new Stat(StatType.Level, modifierResolver.Resolve(baseSettings.defaultEntityStats.GetValue(StatType.Level), levelModifiers, entityDefinition)),
|
||||||
new Stat(StatType.Experience, modifierResolver.Resolve(baseSettings.defaultEntityStats.GetValue(StatType.Experience), experienceModifiers))
|
new Stat(StatType.Experience, modifierResolver.Resolve(baseSettings.defaultEntityStats.GetValue(StatType.Experience), experienceModifiers, entityDefinition))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ namespace Nox.Game {
|
|||||||
public interface IEntityDefinition {
|
public interface IEntityDefinition {
|
||||||
Guid Id { get; }
|
Guid Id { get; }
|
||||||
string Name { get; }
|
string Name { get; }
|
||||||
|
int PortraitIndex { get; }
|
||||||
CharacterRace Race { get; }
|
CharacterRace Race { get; }
|
||||||
CharacterClass Class { get; }
|
CharacterClass Class { get; }
|
||||||
CharacterRole Role { get; }
|
CharacterRole Role { get; }
|
||||||
@@ -22,7 +23,7 @@ namespace Nox.Game {
|
|||||||
public enum StatType {
|
public enum StatType {
|
||||||
None,
|
None,
|
||||||
Health,
|
Health,
|
||||||
Stamina,
|
Mana,
|
||||||
Level,
|
Level,
|
||||||
Experience
|
Experience
|
||||||
}
|
}
|
||||||
@@ -129,6 +130,7 @@ namespace Nox.Game {
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
public class CharacterDefinition : IEntityDefinition {
|
public class CharacterDefinition : IEntityDefinition {
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
|
public int PortraitIndex { get; set; } = 0;
|
||||||
[field: SerializeField] public string Name { get; set; }
|
[field: SerializeField] public string Name { get; set; }
|
||||||
[field: SerializeField] public CharacterRace Race { get; set; }
|
[field: SerializeField] public CharacterRace Race { get; set; }
|
||||||
[field: SerializeField] public CharacterClass Class { get; set; }
|
[field: SerializeField] public CharacterClass Class { get; set; }
|
||||||
@@ -156,17 +158,18 @@ namespace Nox.Game {
|
|||||||
Id = p.Id,
|
Id = p.Id,
|
||||||
Name = p.Name,
|
Name = p.Name,
|
||||||
Modifiers = p.Modifiers
|
Modifiers = p.Modifiers
|
||||||
}).ToList() ?? new()
|
}).ToList() ?? new List<PerkDefinition>()
|
||||||
},
|
},
|
||||||
Modifiers = new ModifiersData {
|
Modifiers = new ModifiersData {
|
||||||
modifiers = Modifiers?.modifiers?.AsValueEnumerable().Select(m => new ModifierDefinition {
|
modifiers = Modifiers?.modifiers?.AsValueEnumerable().Select(m => new ModifierDefinition {
|
||||||
Id = m.Id,
|
Id = m.Id,
|
||||||
Name = m.Name,
|
Name = m.Name,
|
||||||
StatType = m.StatType,
|
Target = m.Target,
|
||||||
AttributeType = m.AttributeType,
|
ScalingSource = m.ScalingSource,
|
||||||
Operation = m.Operation,
|
Operation = m.Operation,
|
||||||
Value = m.Value
|
Value = m.Value,
|
||||||
}).ToList() ?? new()
|
Requirements = m.Requirements?.ToList() ?? new List<ModifierRequirement>()
|
||||||
|
}).ToList() ?? new List<ModifierDefinition>()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ namespace Nox.Game {
|
|||||||
public interface IModifier {
|
public interface IModifier {
|
||||||
string Name { get; }
|
string Name { get; }
|
||||||
Guid Id { get; }
|
Guid Id { get; }
|
||||||
StatType StatType { get; }
|
ModifierTarget Target { get; }
|
||||||
AttributeType AttributeType { get; }
|
ModifierTarget ScalingSource { get; }
|
||||||
ModifierOperation Operation { get; }
|
ModifierOperation Operation { get; }
|
||||||
float Value { get; }
|
float Value { get; }
|
||||||
|
IReadOnlyList<ModifierRequirement> Requirements { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IModifiersFactory {
|
public interface IModifiersFactory {
|
||||||
@@ -30,20 +31,73 @@ namespace Nox.Game {
|
|||||||
Percentage
|
Percentage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum ModifierTargetType {
|
||||||
|
None,
|
||||||
|
Attribute,
|
||||||
|
Stat,
|
||||||
|
CombatScore
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public sealed class ModifierTarget {
|
||||||
|
[field: SerializeField] public ModifierTargetType Type { get; set; }
|
||||||
|
[field: SerializeField] public AttributeType AttributeType { get; set; }
|
||||||
|
[field: SerializeField] public StatType StatType { get; set; }
|
||||||
|
[field: SerializeField] public CombatScoreType CombatScoreType { get; set; }
|
||||||
|
|
||||||
|
public bool Matches(StatType statType) {
|
||||||
|
return Type == ModifierTargetType.Stat && StatType == statType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Matches(AttributeType attributeType) {
|
||||||
|
return Type == ModifierTargetType.Attribute && AttributeType == attributeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Matches(CombatScoreType combatScoreType) {
|
||||||
|
return Type == ModifierTargetType.CombatScore && CombatScoreType == combatScoreType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsSet => Type != ModifierTargetType.None;
|
||||||
|
|
||||||
|
public override string ToString() {
|
||||||
|
return Type switch {
|
||||||
|
ModifierTargetType.Attribute => AttributeType.ToString(),
|
||||||
|
ModifierTargetType.Stat => StatType.ToString(),
|
||||||
|
ModifierTargetType.CombatScore => CombatScoreType.ToString(),
|
||||||
|
_ => "None"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public sealed class ModifierRequirement {
|
||||||
|
[field: SerializeField] public AttributeType Attribute { get; set; }
|
||||||
|
[field: SerializeField] public int MinimumValue { get; set; }
|
||||||
|
|
||||||
|
public bool IsMet(EntityAttributes attributes) {
|
||||||
|
if(Attribute == AttributeType.None || attributes?.attributes == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return attributes.GetValue(Attribute) >= MinimumValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public sealed class ModifierDefinition : IModifier {
|
public sealed class ModifierDefinition : IModifier {
|
||||||
[field: SerializeField] public string Name { get; set; }
|
[field: SerializeField] public string Name { get; set; }
|
||||||
public Guid Id { get; set; } = Guid.NewGuid();
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
[field: SerializeField] public StatType StatType { get; set; }
|
[field: SerializeField] public ModifierTarget Target { get; set; }
|
||||||
[field: SerializeField] public AttributeType AttributeType { get; set; }
|
[field: SerializeField] public ModifierTarget ScalingSource { get; set; }
|
||||||
[field: SerializeField] public ModifierOperation Operation { get; set; }
|
[field: SerializeField] public ModifierOperation Operation { get; set; }
|
||||||
[field: SerializeField] public CombatScoreType CombatScoreType { get; set; }
|
|
||||||
[field: SerializeField] public float Value { get; set; }
|
[field: SerializeField] public float Value { get; set; }
|
||||||
|
[field: SerializeField] public List<ModifierRequirement> Requirements { get; set; } = new();
|
||||||
|
|
||||||
|
IReadOnlyList<ModifierRequirement> IModifier.Requirements => Requirements;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public sealed class ModifiersData {
|
public sealed class ModifiersData {
|
||||||
public List<ModifierDefinition> modifiers = new ();
|
public List<ModifierDefinition> modifiers = new();
|
||||||
|
|
||||||
public override string ToString() {
|
public override string ToString() {
|
||||||
return $"Modifiers: {string.Join(", ", modifiers.Select(modifier => $"{modifier.Name}"))}";
|
return $"Modifiers: {string.Join(", ", modifiers.Select(modifier => $"{modifier.Name}"))}";
|
||||||
@@ -52,7 +106,7 @@ namespace Nox.Game {
|
|||||||
|
|
||||||
public class ModifiersFactory : IModifiersFactory {
|
public class ModifiersFactory : IModifiersFactory {
|
||||||
private readonly ModifiersRegistry modifiersRegistry;
|
private readonly ModifiersRegistry modifiersRegistry;
|
||||||
private readonly Dictionary<Guid, IModifier> modifierPool = new ();
|
private readonly Dictionary<Guid, IModifier> modifierPool = new();
|
||||||
|
|
||||||
public ModifiersFactory(ModifiersRegistry modifiersRegistry) {
|
public ModifiersFactory(ModifiersRegistry modifiersRegistry) {
|
||||||
this.modifiersRegistry = modifiersRegistry;
|
this.modifiersRegistry = modifiersRegistry;
|
||||||
@@ -92,10 +146,11 @@ namespace Nox.Game {
|
|||||||
character.Modifiers.modifiers.Add(new ModifierDefinition {
|
character.Modifiers.modifiers.Add(new ModifierDefinition {
|
||||||
Id = modifier.Id,
|
Id = modifier.Id,
|
||||||
Name = modifier.Name,
|
Name = modifier.Name,
|
||||||
StatType = modifier.StatType,
|
Target = modifier.Target,
|
||||||
AttributeType = modifier.AttributeType,
|
ScalingSource = modifier.ScalingSource,
|
||||||
Operation = modifier.Operation,
|
Operation = modifier.Operation,
|
||||||
Value = modifier.Value
|
Value = modifier.Value,
|
||||||
|
Requirements = modifier.Requirements?.ToList() ?? new List<ModifierRequirement>()
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ using ZLinq;
|
|||||||
namespace Nox.Game {
|
namespace Nox.Game {
|
||||||
|
|
||||||
public interface IModifierResolver {
|
public interface IModifierResolver {
|
||||||
int Resolve(int baseValue, IEnumerable<IModifier> modifiers);
|
int Resolve(int baseValue, IEnumerable<IModifier> modifiers, IEntityDefinition entity = null);
|
||||||
IEnumerable<IModifier> CollectModifiers(IEntityDefinition entity, StatType statType);
|
IEnumerable<IModifier> CollectModifiers(IEntityDefinition entity, StatType statType);
|
||||||
IEnumerable<IModifier> CollectModifiers(IEntityDefinition entity, AttributeType attributeType);
|
IEnumerable<IModifier> CollectModifiers(IEntityDefinition entity, AttributeType attributeType);
|
||||||
|
IEnumerable<IModifier> CollectModifiers(IEntityDefinition entity, CombatScoreType combatScoreType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -19,9 +20,14 @@ namespace Nox.Game {
|
|||||||
/// 2. Addition — summed and added to the running total
|
/// 2. Addition — summed and added to the running total
|
||||||
/// 3. Percentage — summed into a single multiplier applied to the post-addition total
|
/// 3. Percentage — summed into a single multiplier applied to the post-addition total
|
||||||
/// 4. Multiplication — each factor applied sequentially to the running total
|
/// 4. Multiplication — each factor applied sequentially to the running total
|
||||||
|
///
|
||||||
|
/// If a modifier has a ScalingSource set, its Value is multiplied by the entity's
|
||||||
|
/// current value for that source before being applied. For example, a modifier with
|
||||||
|
/// Target=Health, ScalingSource=Might, Operation=Addition, Value=2 means "+2 Health
|
||||||
|
/// per point of Might".
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class ModifierResolver : IModifierResolver {
|
public sealed class ModifierResolver : IModifierResolver {
|
||||||
public int Resolve(int baseValue, IEnumerable<IModifier> modifiers) {
|
public int Resolve(int baseValue, IEnumerable<IModifier> modifiers, IEntityDefinition entity = null) {
|
||||||
if(modifiers == null) {
|
if(modifiers == null) {
|
||||||
return baseValue;
|
return baseValue;
|
||||||
}
|
}
|
||||||
@@ -37,19 +43,21 @@ namespace Nox.Game {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var effectiveValue = ResolveScaling(m, entity);
|
||||||
|
|
||||||
switch(m.Operation) {
|
switch(m.Operation) {
|
||||||
case ModifierOperation.Flat:
|
case ModifierOperation.Flat:
|
||||||
flatSum += m.Value;
|
flatSum += effectiveValue;
|
||||||
hasFlat = true;
|
hasFlat = true;
|
||||||
break;
|
break;
|
||||||
case ModifierOperation.Addition:
|
case ModifierOperation.Addition:
|
||||||
addSum += m.Value;
|
addSum += effectiveValue;
|
||||||
break;
|
break;
|
||||||
case ModifierOperation.Percentage:
|
case ModifierOperation.Percentage:
|
||||||
pctSum += m.Value;
|
pctSum += effectiveValue;
|
||||||
break;
|
break;
|
||||||
case ModifierOperation.Multiplication:
|
case ModifierOperation.Multiplication:
|
||||||
mulValues.Add(m.Value);
|
mulValues.Add(effectiveValue);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,34 +73,31 @@ namespace Nox.Game {
|
|||||||
return (int)Math.Round(result);
|
return (int)Math.Round(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static float ResolveScaling(IModifier modifier, IEntityDefinition entity) {
|
||||||
|
var value = modifier.Value;
|
||||||
|
if(entity == null || modifier.ScalingSource == null || !modifier.ScalingSource.IsSet) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var source = modifier.ScalingSource;
|
||||||
|
var sourceValue = source.Type switch {
|
||||||
|
ModifierTargetType.Attribute when entity.Attributes?.attributes != null =>
|
||||||
|
entity.Attributes.GetValue(source.AttributeType),
|
||||||
|
ModifierTargetType.Stat when entity.Stats?.stats != null =>
|
||||||
|
entity.Stats.GetValue(source.StatType),
|
||||||
|
_ => 0
|
||||||
|
};
|
||||||
|
|
||||||
|
return value * sourceValue;
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerable<IModifier> CollectModifiers(IEntityDefinition entity, StatType statType) {
|
public IEnumerable<IModifier> CollectModifiers(IEntityDefinition entity, StatType statType) {
|
||||||
if(entity == null) {
|
if(entity == null) {
|
||||||
return Array.Empty<IModifier>();
|
return Array.Empty<IModifier>();
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = new List<IModifier>();
|
var result = new List<IModifier>();
|
||||||
|
CollectFromEntity(entity, result, m => m.Target != null && m.Target.Matches(statType));
|
||||||
if(entity.Modifiers?.modifiers != null) {
|
|
||||||
foreach(var m in entity.Modifiers.modifiers) {
|
|
||||||
if(m != null && m.StatType == statType) {
|
|
||||||
result.Add(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(entity.Perks?.perks != null) {
|
|
||||||
foreach(var p in entity.Perks.perks) {
|
|
||||||
if(p?.Modifiers?.modifiers == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
foreach(var m in p.Modifiers.modifiers) {
|
|
||||||
if(m != null && m.StatType == statType) {
|
|
||||||
result.Add(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,10 +107,37 @@ namespace Nox.Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var result = new List<IModifier>();
|
var result = new List<IModifier>();
|
||||||
|
CollectFromEntity(entity, result, m => m.Target != null && m.Target.Matches(attributeType));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<IModifier> CollectModifiers(IEntityDefinition entity, CombatScoreType combatScoreType) {
|
||||||
|
if(entity == null) {
|
||||||
|
return Array.Empty<IModifier>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = new List<IModifier>();
|
||||||
|
CollectFromEntity(entity, result, m => m.Target != null && m.Target.Matches(combatScoreType));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool MeetsRequirements(IModifier modifier, IEntityDefinition entity) {
|
||||||
|
var requirements = modifier.Requirements;
|
||||||
|
if(requirements == null || requirements.Count == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for(int i = 0; i < requirements.Count; i++) {
|
||||||
|
if(!requirements[i].IsMet(entity.Attributes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CollectFromEntity(IEntityDefinition entity, List<IModifier> result, Func<IModifier, bool> predicate) {
|
||||||
if(entity.Modifiers?.modifiers != null) {
|
if(entity.Modifiers?.modifiers != null) {
|
||||||
foreach(var m in entity.Modifiers.modifiers) {
|
foreach(var m in entity.Modifiers.modifiers) {
|
||||||
if(m != null && m.AttributeType == attributeType) {
|
if(m != null && predicate(m) && MeetsRequirements(m, entity)) {
|
||||||
result.Add(m);
|
result.Add(m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,14 +149,12 @@ namespace Nox.Game {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
foreach(var m in p.Modifiers.modifiers) {
|
foreach(var m in p.Modifiers.modifiers) {
|
||||||
if(m != null && m.AttributeType == attributeType) {
|
if(m != null && predicate(m)) {
|
||||||
result.Add(m);
|
result.Add(m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,7 @@ namespace Nox.Game {
|
|||||||
throw new System.ArgumentException("Too many characters requested.");
|
throw new System.ArgumentException("Too many characters requested.");
|
||||||
}
|
}
|
||||||
var protagonist = characterFactory.CreateProtagonist(characterCreationRequests.Find(r => r.Role == CharacterRole.Protagonist));
|
var protagonist = characterFactory.CreateProtagonist(characterCreationRequests.Find(r => r.Role == CharacterRole.Protagonist));
|
||||||
var companions = characterCreationRequests.FindAll(r => r.Role != CharacterRole.Protagonist).Select(r => characterFactory.CreateProtagonist(r));
|
return partyFactory.Create(protagonist);
|
||||||
return partyFactory.Create(protagonist, companions);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,24 +13,13 @@ namespace Nox.Game {
|
|||||||
public ModifiersData defaultModifiersData;
|
public ModifiersData defaultModifiersData;
|
||||||
|
|
||||||
[Header("General Racial Bonuses and Perks per Class")]
|
[Header("General Racial Bonuses and Perks per Class")]
|
||||||
public CharacterRace race;
|
|
||||||
|
|
||||||
public CharacterClass @class;
|
|
||||||
public RacialBonuses [] racialBonuses;
|
public RacialBonuses [] racialBonuses;
|
||||||
public ClassBonuses [] classBonuses;
|
public ClassBonuses [] classBonuses;
|
||||||
|
|
||||||
|
|
||||||
private void OnEnable() {
|
|
||||||
race = (CharacterRace)Random.Range(0, Enum.GetNames(typeof(CharacterRace)).Length-1);
|
|
||||||
@class = (CharacterClass)Random.Range(0, Enum.GetNames(typeof(CharacterClass)).Length-1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public sealed class RacialBonuses {
|
public sealed class RacialBonuses {
|
||||||
public CharacterRace race;
|
public CharacterRace race;
|
||||||
public EntityAttributes bonusAttributes;
|
|
||||||
public EntityStats bonusStats;
|
|
||||||
public PerksData startingPerks;
|
public PerksData startingPerks;
|
||||||
public ModifiersData permanentModifiers;
|
public ModifiersData permanentModifiers;
|
||||||
}
|
}
|
||||||
@@ -38,8 +27,6 @@ namespace Nox.Game {
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
public sealed class ClassBonuses {
|
public sealed class ClassBonuses {
|
||||||
public CharacterClass @class;
|
public CharacterClass @class;
|
||||||
public EntityAttributes bonusAttributes;
|
|
||||||
public EntityStats bonusStats;
|
|
||||||
public PerksData startingPerks;
|
public PerksData startingPerks;
|
||||||
public ModifiersData permanentModifiers;
|
public ModifiersData permanentModifiers;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
|
using TMPro;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
public class AttributeReference : MonoBehaviour
|
namespace Nox.UI {
|
||||||
{
|
public class AttributeReference : MonoBehaviour {
|
||||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
public Button removePointsButton;
|
||||||
void Start()
|
public Button addPointsButton;
|
||||||
{
|
public TextMeshProUGUI attributeName;
|
||||||
|
public TextMeshProUGUI attributeValue;
|
||||||
}
|
|
||||||
|
|
||||||
// Update is called once per frame
|
|
||||||
void Update()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,69 @@
|
|||||||
using Jovian.InGameLogging;
|
using Jovian.InGameLogging;
|
||||||
using Jovian.InGameLogging.UI;
|
using Jovian.InGameLogging.UI;
|
||||||
using Jovian.Logger;
|
|
||||||
using Jovian.SaveSystem;
|
using Jovian.SaveSystem;
|
||||||
using Nox.Core;
|
using Nox.Core;
|
||||||
using Nox.Game;
|
using Nox.Game;
|
||||||
using Nox.Game.UI;
|
using Nox.Game.UI;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using TMPro;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using ZLinq;
|
||||||
|
using Attribute = Nox.Game.Attribute;
|
||||||
using PlayMode = Nox.Core.PlayMode;
|
using PlayMode = Nox.Core.PlayMode;
|
||||||
|
|
||||||
namespace Nox.UI {
|
namespace Nox.UI {
|
||||||
public class CharacterCreationView : IGameLifecycle, IMenuView {
|
public class CharacterCreationView : IGameLifecycle, IMenuView {
|
||||||
public ISaveSystem SaveSystem { get; }
|
public ISaveSystem SaveSystem { get; }
|
||||||
|
|
||||||
private readonly CharacterCreationReference characterCreationReference;
|
private readonly CharacterCreationReference characterCreationReference;
|
||||||
private readonly MenuGameStateData menuGameStateData;
|
private readonly MenuGameStateData menuGameStateData;
|
||||||
private readonly GameDataState gameDataState;
|
private readonly GameDataState gameDataState;
|
||||||
private readonly PartySettings partySettings;
|
private readonly PartySettings partySettings;
|
||||||
private readonly ICharacterSystems characterSystems;
|
private readonly ICharacterSystems characterSystems;
|
||||||
private readonly PortraitsHolder portraitsHolder;
|
private readonly PortraitsHolder portraitsHolder;
|
||||||
|
private readonly StarterCharacterSettings starterCharacterSettings;
|
||||||
|
|
||||||
private List<CharacterCreationRequest> characterCreationRequests;
|
// Logger
|
||||||
private Action canStartCheck;
|
|
||||||
private GameLogView gameLogView;
|
private GameLogView gameLogView;
|
||||||
private InGameLogger inGameLogger;
|
private InGameLogger inGameLogger;
|
||||||
|
|
||||||
|
// Working state
|
||||||
|
private CharacterRace selectedRace;
|
||||||
|
private CharacterClass selectedClass;
|
||||||
|
private int currentPortraitIndex;
|
||||||
|
private int remainingPoints;
|
||||||
|
private readonly int[] allocatedPoints = new int[4]; // Might, Reflex, Knowledge, Perception (AttributeType 1-4)
|
||||||
|
private int previousHealth;
|
||||||
|
private int previousStamina;
|
||||||
|
|
||||||
|
// Modifier source tracking
|
||||||
|
private PerksData racialPerks = new();
|
||||||
|
private ModifiersData racialModifiers = new();
|
||||||
|
private PerksData classPerks = new();
|
||||||
|
private ModifiersData classModifiers = new();
|
||||||
|
private readonly List<PerkDefinition> playerPerks = new();
|
||||||
|
private List<IPerk> availablePerks = new();
|
||||||
|
|
||||||
|
// Computed state
|
||||||
|
private EntityAttributes workingAttributes;
|
||||||
|
private EntityStats workingStats;
|
||||||
|
|
||||||
|
// Output
|
||||||
|
private List<CharacterCreationRequest> characterCreationRequests;
|
||||||
|
private Action canStartCheck;
|
||||||
|
|
||||||
|
// Back confirmation (null until popup is implemented)
|
||||||
|
private Action confirmBackAction;
|
||||||
|
|
||||||
public CharacterCreationView(CharacterCreationReference characterCreationReference,
|
public CharacterCreationView(CharacterCreationReference characterCreationReference,
|
||||||
MenuGameStateData menuGameStateData,
|
MenuGameStateData menuGameStateData,
|
||||||
ISaveSystem saveSystem,
|
ISaveSystem saveSystem,
|
||||||
GameDataState gameDataState,
|
GameDataState gameDataState,
|
||||||
PartySettings partySettings,
|
PartySettings partySettings,
|
||||||
ICharacterSystems characterSystems,
|
ICharacterSystems characterSystems,
|
||||||
PortraitsHolder portraitsHolder) {
|
PortraitsHolder portraitsHolder,
|
||||||
|
StarterCharacterSettings starterCharacterSettings) {
|
||||||
SaveSystem = saveSystem;
|
SaveSystem = saveSystem;
|
||||||
this.characterCreationReference = characterCreationReference;
|
this.characterCreationReference = characterCreationReference;
|
||||||
this.menuGameStateData = menuGameStateData;
|
this.menuGameStateData = menuGameStateData;
|
||||||
@@ -39,77 +71,529 @@ namespace Nox.UI {
|
|||||||
this.partySettings = partySettings;
|
this.partySettings = partySettings;
|
||||||
this.characterSystems = characterSystems;
|
this.characterSystems = characterSystems;
|
||||||
this.portraitsHolder = portraitsHolder;
|
this.portraitsHolder = portraitsHolder;
|
||||||
|
this.starterCharacterSettings = starterCharacterSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize() {
|
public void Initialize() {
|
||||||
|
// Logger
|
||||||
var store = new GameLogStore(500);
|
var store = new GameLogStore(500);
|
||||||
gameLogView = characterCreationReference.gameLogView;
|
gameLogView = characterCreationReference.gameLogView;
|
||||||
gameLogView.Initialize(store);
|
gameLogView.Initialize(store);
|
||||||
inGameLogger = new InGameLogger(store, LogChannel.CharacterCreation);
|
inGameLogger = new InGameLogger(store, LogChannel.CharacterCreation);
|
||||||
inGameLogger.Enable();
|
inGameLogger.Enable();
|
||||||
|
|
||||||
|
// Start Game button
|
||||||
canStartCheck = () => {
|
canStartCheck = () => {
|
||||||
var canStart = characterCreationRequests is { Count: > 0 };
|
var canStart = characterCreationRequests is { Count: > 0 };
|
||||||
characterCreationReference.startGameButton.interactable = canStart;
|
characterCreationReference.startGameButton.interactable = canStart;
|
||||||
};
|
};
|
||||||
|
|
||||||
characterCreationReference.startGameButton.interactable = false;
|
characterCreationReference.startGameButton.interactable = false;
|
||||||
characterCreationReference.startGameButton.onClick.AddListener(() => {
|
characterCreationReference.startGameButton.onClick.AddListener(() => {
|
||||||
Hide();
|
Hide();
|
||||||
menuGameStateData.startGameRequests?.Invoke(PlayMode.Adventure);
|
menuGameStateData.startGameRequests?.Invoke(PlayMode.Adventure);
|
||||||
});
|
});
|
||||||
characterCreationReference.backButton.onClick.AddListener(Hide);
|
|
||||||
characterCreationReference.backButtonCenter.onClick.AddListener(Hide);
|
|
||||||
characterCreationReference.acceptButton.onClick.AddListener(() => {
|
|
||||||
if(characterCreationRequests == null || characterCreationRequests.Count == 0) {
|
|
||||||
GlobalLogger.LogWarning("No characters selected. Creating party from the test party definition sets", LogCategory.GameLogic);
|
|
||||||
var randomIndex = UnityEngine.Random.Range(0, partySettings.testPartyDefinitionSets.Count - 1);
|
|
||||||
var protagonist = partySettings.testPartyDefinitionSets[randomIndex].partyDefinition.Protagonist;
|
|
||||||
characterCreationRequests = new List<CharacterCreationRequest> {
|
|
||||||
new() {
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Name = protagonist.Name,
|
|
||||||
Race = protagonist.Race,
|
|
||||||
Class = protagonist.Class,
|
|
||||||
Role = CharacterRole.Protagonist,
|
|
||||||
Attributes = protagonist.Attributes,
|
|
||||||
Stats = protagonist.Stats,
|
|
||||||
Perks = protagonist.Perks,
|
|
||||||
Modifiers = protagonist.Modifiers
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
CreateParty();
|
|
||||||
canStartCheck.Invoke();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// Back buttons with popup check
|
||||||
|
characterCreationReference.backButton.onClick.AddListener(OnBackClicked);
|
||||||
|
characterCreationReference.backButtonCenter.onClick.AddListener(OnBackClicked);
|
||||||
|
|
||||||
|
// Accept button
|
||||||
|
characterCreationReference.acceptButton.onClick.AddListener(OnAcceptClicked);
|
||||||
|
|
||||||
|
// Race dropdown
|
||||||
|
PopulateEnumDropdown<CharacterRace>(characterCreationReference.raceDropdown);
|
||||||
|
characterCreationReference.raceDropdown.onValueChanged.AddListener(OnRaceChanged);
|
||||||
|
|
||||||
|
// Class dropdown
|
||||||
|
PopulateEnumDropdown<CharacterClass>(characterCreationReference.classDropdown);
|
||||||
|
characterCreationReference.classDropdown.onValueChanged.AddListener(OnClassChanged);
|
||||||
|
|
||||||
|
// Perks dropdown
|
||||||
|
PopulatePerksDropdown();
|
||||||
|
characterCreationReference.perksDropdown.onValueChanged.AddListener(OnPerkSelected);
|
||||||
|
|
||||||
|
// Attribute +/- buttons
|
||||||
|
var attrTypes = new[] { AttributeType.Might, AttributeType.Reflex, AttributeType.Knowledge, AttributeType.Perception };
|
||||||
|
var attrRefs = characterCreationReference.attributeReference;
|
||||||
|
for(int i = 0; i < attrRefs.Length && i < attrTypes.Length; i++) {
|
||||||
|
var type = attrTypes[i];
|
||||||
|
attrRefs[i].attributeName.text = type.ToString();
|
||||||
|
attrRefs[i].addPointsButton.onClick.AddListener(() => OnAttributeAdd(type));
|
||||||
|
attrRefs[i].removePointsButton.onClick.AddListener(() => OnAttributeRemove(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Portrait navigation
|
||||||
|
currentPortraitIndex = 0;
|
||||||
|
if(portraitsHolder != null && portraitsHolder.portraits.Length > 0) {
|
||||||
|
characterCreationReference.portraitImage.sprite = portraitsHolder.portraits[0];
|
||||||
|
}
|
||||||
|
characterCreationReference.portraitSelectionLeftButton.onClick.AddListener(OnPortraitLeft);
|
||||||
|
characterCreationReference.portraitSelectionRightButton.onClick.AddListener(OnPortraitRight);
|
||||||
|
|
||||||
|
// Initial state
|
||||||
|
selectedRace = CharacterRace.Human;
|
||||||
|
selectedClass = CharacterClass.Warrior;
|
||||||
|
characterCreationReference.raceDropdown.SetValueWithoutNotify(0);
|
||||||
|
characterCreationReference.classDropdown.SetValueWithoutNotify(0);
|
||||||
|
ResetWorkingState();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Dropdown helpers ---
|
||||||
|
|
||||||
|
private void PopulateEnumDropdown<T>(TMP_Dropdown dropdown) where T : Enum {
|
||||||
|
dropdown.ClearOptions();
|
||||||
|
var options = new List<string>();
|
||||||
|
foreach(T value in Enum.GetValues(typeof(T))) {
|
||||||
|
if(Convert.ToInt32(value) == 0) {
|
||||||
|
continue; // skip None
|
||||||
|
}
|
||||||
|
options.Add(value.ToString());
|
||||||
|
}
|
||||||
|
dropdown.AddOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PopulatePerksDropdown() {
|
||||||
|
var dropdown = characterCreationReference.perksDropdown;
|
||||||
|
dropdown.ClearOptions();
|
||||||
|
availablePerks = new List<IPerk>(characterSystems.PerkFactory.GetAll());
|
||||||
|
|
||||||
|
var options = new List<string> { "Select a perk..." };
|
||||||
|
foreach(var perk in availablePerks) {
|
||||||
|
options.Add(perk.Name);
|
||||||
|
}
|
||||||
|
dropdown.AddOptions(options);
|
||||||
|
dropdown.SetValueWithoutNotify(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- State management ---
|
||||||
|
|
||||||
|
private void ResetWorkingState() {
|
||||||
|
Array.Clear(allocatedPoints, 0, allocatedPoints.Length);
|
||||||
|
racialPerks = new PerksData();
|
||||||
|
racialModifiers = new ModifiersData();
|
||||||
|
classPerks = new PerksData();
|
||||||
|
classModifiers = new ModifiersData();
|
||||||
|
playerPerks.Clear();
|
||||||
|
|
||||||
|
ApplyRacialBonuses();
|
||||||
|
ApplyClassBonuses();
|
||||||
|
UpdateRemainingPoints();
|
||||||
|
RecalculateAll();
|
||||||
|
|
||||||
|
// Initialize previous values so first change doesn't log a delta from 0
|
||||||
|
previousHealth = workingStats.GetValue(StatType.Health);
|
||||||
|
previousStamina = workingStats.GetValue(StatType.Mana);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyRacialBonuses() {
|
||||||
|
racialPerks = new PerksData();
|
||||||
|
racialModifiers = new ModifiersData();
|
||||||
|
var bonuses = starterCharacterSettings.racialBonuses;
|
||||||
|
if(bonuses == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach(var rb in bonuses) {
|
||||||
|
if(rb.race == selectedRace) {
|
||||||
|
racialPerks = rb.startingPerks ?? new PerksData();
|
||||||
|
racialModifiers = rb.permanentModifiers ?? new ModifiersData();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyClassBonuses() {
|
||||||
|
classPerks = new PerksData();
|
||||||
|
classModifiers = new ModifiersData();
|
||||||
|
var bonuses = starterCharacterSettings.classBonuses;
|
||||||
|
if(bonuses == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach(var cb in bonuses) {
|
||||||
|
if(cb.@class == selectedClass) {
|
||||||
|
classPerks = cb.startingPerks ?? new PerksData();
|
||||||
|
classModifiers = cb.permanentModifiers ?? new ModifiersData();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateRemainingPoints() {
|
||||||
|
var totalPoints = 0;
|
||||||
|
if(starterCharacterSettings.distributionPointsPerClass != null) {
|
||||||
|
foreach(var dpc in starterCharacterSettings.distributionPointsPerClass) {
|
||||||
|
if(dpc.@class == selectedClass) {
|
||||||
|
totalPoints = dpc.points;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var spent = 0;
|
||||||
|
for(int i = 0; i < allocatedPoints.Length; i++) {
|
||||||
|
spent += allocatedPoints[i];
|
||||||
|
}
|
||||||
|
remainingPoints = totalPoints - spent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Core calculation ---
|
||||||
|
|
||||||
|
private void RecalculateAll() {
|
||||||
|
// 1. Start from default attributes + player allocations
|
||||||
|
var baseAttrs = starterCharacterSettings.defaultEntityAttributes;
|
||||||
|
var attrTypes = new[] { AttributeType.Might, AttributeType.Reflex, AttributeType.Knowledge, AttributeType.Perception };
|
||||||
|
var finalAttrs = new Attribute[attrTypes.Length];
|
||||||
|
for(int i = 0; i < attrTypes.Length; i++) {
|
||||||
|
var baseVal = baseAttrs.GetValue(attrTypes[i]);
|
||||||
|
finalAttrs[i] = new Attribute(attrTypes[i], baseVal + allocatedPoints[i]);
|
||||||
|
}
|
||||||
|
workingAttributes = new EntityAttributes { attributes = finalAttrs };
|
||||||
|
|
||||||
|
// 2. Build combined perks and modifiers (defaults + racial + class + player)
|
||||||
|
// Racial/class attribute and stat bonuses flow through permanentModifiers
|
||||||
|
var combinedPerks = BuildCombinedPerks();
|
||||||
|
var combinedModifiers = BuildCombinedModifiers();
|
||||||
|
|
||||||
|
// 3. Build temp entity for modifier collection
|
||||||
|
var tempEntity = new CharacterCreationRequest {
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
Race = selectedRace,
|
||||||
|
Class = selectedClass,
|
||||||
|
Role = CharacterRole.Protagonist,
|
||||||
|
Attributes = workingAttributes,
|
||||||
|
Perks = combinedPerks,
|
||||||
|
Modifiers = combinedModifiers
|
||||||
|
};
|
||||||
|
|
||||||
|
// 4. Resolve attributes through modifiers (racial/class attribute bonuses come via modifiers)
|
||||||
|
var resolver = characterSystems.ModifierResolver;
|
||||||
|
var resolvedAttrs = new Attribute[attrTypes.Length];
|
||||||
|
for(int i = 0; i < attrTypes.Length; i++) {
|
||||||
|
var mods = resolver.CollectModifiers(tempEntity, attrTypes[i]);
|
||||||
|
resolvedAttrs[i] = new Attribute(attrTypes[i], resolver.Resolve(finalAttrs[i].value, mods, tempEntity));
|
||||||
|
}
|
||||||
|
workingAttributes = new EntityAttributes { attributes = resolvedAttrs };
|
||||||
|
tempEntity.Attributes = workingAttributes;
|
||||||
|
|
||||||
|
// 5. Calculate stats through modifiers (racial/class stat bonuses come via modifiers)
|
||||||
|
var baseStats = starterCharacterSettings.defaultEntityStats;
|
||||||
|
var statTypes = new[] { StatType.Health, StatType.Mana, StatType.Level, StatType.Experience };
|
||||||
|
var resolvedStats = new Stat[statTypes.Length];
|
||||||
|
for(int i = 0; i < statTypes.Length; i++) {
|
||||||
|
var baseVal = baseStats.GetValue(statTypes[i]);
|
||||||
|
var mods = resolver.CollectModifiers(tempEntity, statTypes[i]);
|
||||||
|
resolvedStats[i] = new Stat(statTypes[i], resolver.Resolve(baseVal, mods, tempEntity));
|
||||||
|
}
|
||||||
|
workingStats = new EntityStats { stats = resolvedStats };
|
||||||
|
|
||||||
|
// 9. Update UI
|
||||||
|
UpdateAttributeUI();
|
||||||
|
UpdateStatUI();
|
||||||
|
UpdatePointsDisplay();
|
||||||
|
|
||||||
|
// 10. Log stat deltas
|
||||||
|
var newHealth = workingStats.GetValue(StatType.Health);
|
||||||
|
var newStamina = workingStats.GetValue(StatType.Mana);
|
||||||
|
if(newHealth != previousHealth && previousHealth != 0) {
|
||||||
|
var delta = newHealth - previousHealth;
|
||||||
|
var sign = delta > 0 ? "+" : "";
|
||||||
|
inGameLogger.Log($"Health: {previousHealth} -> {newHealth} ({sign}{delta})", "#87CEEB");
|
||||||
|
}
|
||||||
|
if(newStamina != previousStamina && previousStamina != 0) {
|
||||||
|
var delta = newStamina - previousStamina;
|
||||||
|
var sign = delta > 0 ? "+" : "";
|
||||||
|
inGameLogger.Log($"Stamina: {previousStamina} -> {newStamina} ({sign}{delta})", "#FFFF99");
|
||||||
|
}
|
||||||
|
previousHealth = newHealth;
|
||||||
|
previousStamina = newStamina;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PerksData BuildCombinedPerks() {
|
||||||
|
// Start with defaults, add racial/class/player perks (deduplicate by Id)
|
||||||
|
var combined = new PerksData { perks = new List<PerkDefinition>() };
|
||||||
|
var seenIds = new HashSet<Guid>();
|
||||||
|
|
||||||
|
void AddPerks(PerksData source) {
|
||||||
|
if(source?.perks == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach(var perk in source.perks) {
|
||||||
|
if(perk != null && seenIds.Add(perk.Id)) {
|
||||||
|
combined.perks.Add(perk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddPerks(starterCharacterSettings.defaultPerksData);
|
||||||
|
AddPerks(racialPerks);
|
||||||
|
AddPerks(classPerks);
|
||||||
|
foreach(var perk in playerPerks) {
|
||||||
|
if(perk != null && seenIds.Add(perk.Id)) {
|
||||||
|
combined.perks.Add(perk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return combined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ModifiersData BuildCombinedModifiers() {
|
||||||
|
// Start with defaults. Racial/class modifiers override defaults that target the same
|
||||||
|
// thing, but ONLY if the override's requirements are currently met. If requirements
|
||||||
|
// are not met, the default stays and the override is still added — the resolver will
|
||||||
|
// skip the unqualified override at resolution time, leaving the default active.
|
||||||
|
var combined = new ModifiersData { modifiers = new List<ModifierDefinition>() };
|
||||||
|
|
||||||
|
// Seed with defaults
|
||||||
|
if(starterCharacterSettings.defaultModifiersData?.modifiers != null) {
|
||||||
|
combined.modifiers.AddRange(starterCharacterSettings.defaultModifiersData.modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override with racial modifiers
|
||||||
|
OverrideModifiers(combined, racialModifiers, workingAttributes);
|
||||||
|
|
||||||
|
// Override with class modifiers
|
||||||
|
OverrideModifiers(combined, classModifiers, workingAttributes);
|
||||||
|
|
||||||
|
return combined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OverrideModifiers(ModifiersData combined, ModifiersData overrides, EntityAttributes currentAttributes) {
|
||||||
|
if(overrides?.modifiers == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach(var mod in overrides.modifiers) {
|
||||||
|
if(mod?.Target == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Only remove the default if the override's requirements are currently met.
|
||||||
|
// Both are added regardless — the resolver skips unqualified modifiers at
|
||||||
|
// resolution time, so if requirements aren't met, the default still applies.
|
||||||
|
var requirementsMet = AreRequirementsMet(mod, currentAttributes);
|
||||||
|
if(requirementsMet) {
|
||||||
|
for(int i = combined.modifiers.Count - 1; i >= 0; i--) {
|
||||||
|
var existing = combined.modifiers[i];
|
||||||
|
if(existing?.Target != null && TargetsMatch(existing.Target, mod.Target)) {
|
||||||
|
combined.modifiers.RemoveAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
combined.modifiers.Add(mod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool AreRequirementsMet(ModifierDefinition mod, EntityAttributes attributes) {
|
||||||
|
if(mod.Requirements == null || mod.Requirements.Count == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
foreach(var req in mod.Requirements) {
|
||||||
|
if(!req.IsMet(attributes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TargetsMatch(ModifierTarget a, ModifierTarget b) {
|
||||||
|
if(a.Type != b.Type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return a.Type switch {
|
||||||
|
ModifierTargetType.Attribute => a.AttributeType == b.AttributeType,
|
||||||
|
ModifierTargetType.Stat => a.StatType == b.StatType,
|
||||||
|
ModifierTargetType.CombatScore => a.CombatScoreType == b.CombatScoreType,
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- UI updates ---
|
||||||
|
|
||||||
|
private void UpdateAttributeUI() {
|
||||||
|
var attrTypes = new[] { AttributeType.Might, AttributeType.Reflex, AttributeType.Knowledge, AttributeType.Perception };
|
||||||
|
var attrRefs = characterCreationReference.attributeReference;
|
||||||
|
for(int i = 0; i < attrRefs.Length && i < attrTypes.Length; i++) {
|
||||||
|
attrRefs[i].attributeValue.text = workingAttributes.GetValue(attrTypes[i]).ToString();
|
||||||
|
attrRefs[i].addPointsButton.interactable = remainingPoints > 0;
|
||||||
|
attrRefs[i].removePointsButton.interactable = allocatedPoints[i] > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateStatUI() {
|
||||||
|
var statTypes = new[] { StatType.Health, StatType.Mana };
|
||||||
|
var statRefs = characterCreationReference.statReference;
|
||||||
|
for(int i = 0; i < statRefs.Length && i < statTypes.Length; i++) {
|
||||||
|
var value = workingStats.GetValue(statTypes[i]);
|
||||||
|
statRefs[i].statName.text = statTypes[i].ToString();
|
||||||
|
statRefs[i].statValue.text = value.ToString();
|
||||||
|
statRefs[i].statBar.fillAmount = Mathf.Clamp01(value / 200f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdatePointsDisplay() {
|
||||||
|
characterCreationReference.pointsToDistribute.text = remainingPoints.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Event handlers ---
|
||||||
|
|
||||||
|
private void OnRaceChanged(int index) {
|
||||||
|
selectedRace = (CharacterRace)(index + 1);
|
||||||
|
Array.Clear(allocatedPoints, 0, allocatedPoints.Length);
|
||||||
|
ApplyRacialBonuses();
|
||||||
|
UpdateRemainingPoints();
|
||||||
|
RecalculateAll();
|
||||||
|
inGameLogger.Log($"Race changed to {selectedRace}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnClassChanged(int index) {
|
||||||
|
selectedClass = (CharacterClass)(index + 1);
|
||||||
|
Array.Clear(allocatedPoints, 0, allocatedPoints.Length);
|
||||||
|
ApplyClassBonuses();
|
||||||
|
UpdateRemainingPoints();
|
||||||
|
RecalculateAll();
|
||||||
|
inGameLogger.Log($"Class changed to {selectedClass}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPerkSelected(int index) {
|
||||||
|
if(index <= 0 || index > availablePerks.Count) {
|
||||||
|
return; // "Select a perk..." placeholder
|
||||||
|
}
|
||||||
|
|
||||||
|
var perkIndex = index - 1; // offset for placeholder
|
||||||
|
var perk = availablePerks[perkIndex];
|
||||||
|
playerPerks.Add(new PerkDefinition {
|
||||||
|
Id = perk.Id,
|
||||||
|
Name = perk.Name,
|
||||||
|
Modifiers = perk.Modifiers
|
||||||
|
});
|
||||||
|
availablePerks.RemoveAt(perkIndex);
|
||||||
|
PopulatePerksDropdown();
|
||||||
|
RecalculateAll();
|
||||||
|
inGameLogger.Log($"Perk added: {perk.Name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAttributeAdd(AttributeType type) {
|
||||||
|
if(remainingPoints <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var idx = (int)type - 1;
|
||||||
|
allocatedPoints[idx]++;
|
||||||
|
remainingPoints--;
|
||||||
|
RecalculateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAttributeRemove(AttributeType type) {
|
||||||
|
var idx = (int)type - 1;
|
||||||
|
if(allocatedPoints[idx] <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
allocatedPoints[idx]--;
|
||||||
|
remainingPoints++;
|
||||||
|
RecalculateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPortraitLeft() {
|
||||||
|
if(portraitsHolder == null || portraitsHolder.portraits.Length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentPortraitIndex--;
|
||||||
|
if(currentPortraitIndex < 0) {
|
||||||
|
currentPortraitIndex = portraitsHolder.portraits.Length - 1;
|
||||||
|
}
|
||||||
|
characterCreationReference.portraitImage.sprite = portraitsHolder.portraits[currentPortraitIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPortraitRight() {
|
||||||
|
if(portraitsHolder == null || portraitsHolder.portraits.Length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentPortraitIndex++;
|
||||||
|
if(currentPortraitIndex >= portraitsHolder.portraits.Length) {
|
||||||
|
currentPortraitIndex = 0;
|
||||||
|
}
|
||||||
|
characterCreationReference.portraitImage.sprite = portraitsHolder.portraits[currentPortraitIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBackClicked() {
|
||||||
|
if(confirmBackAction != null) {
|
||||||
|
confirmBackAction();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Accept ---
|
||||||
|
|
||||||
|
private void OnAcceptClicked() {
|
||||||
|
var errors = new List<string>();
|
||||||
|
if(selectedRace == CharacterRace.None) {
|
||||||
|
errors.Add("Race must be selected");
|
||||||
|
}
|
||||||
|
if(selectedClass == CharacterClass.None) {
|
||||||
|
errors.Add("Class must be selected");
|
||||||
|
}
|
||||||
|
if(remainingPoints > 0) {
|
||||||
|
errors.Add($"{remainingPoints} distribution points remaining");
|
||||||
|
}
|
||||||
|
var characterName = characterCreationReference.nameInputField.text;
|
||||||
|
if(string.IsNullOrWhiteSpace(characterName)) {
|
||||||
|
errors.Add("Name cannot be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(errors.Count > 0) {
|
||||||
|
foreach(var error in errors) {
|
||||||
|
inGameLogger.Log(error, "#FF4444");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = new CharacterCreationRequest {
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
Name = characterName,
|
||||||
|
Race = selectedRace,
|
||||||
|
Class = selectedClass,
|
||||||
|
Role = CharacterRole.Protagonist,
|
||||||
|
PortraitIndex = currentPortraitIndex,
|
||||||
|
Attributes = workingAttributes,
|
||||||
|
Stats = workingStats,
|
||||||
|
Perks = BuildCombinedPerks(),
|
||||||
|
Modifiers = BuildCombinedModifiers()
|
||||||
|
};
|
||||||
|
|
||||||
|
characterCreationRequests = new List<CharacterCreationRequest> { request };
|
||||||
|
|
||||||
|
// Log full breakdown
|
||||||
|
inGameLogger.Log("--- Character Accepted ---");
|
||||||
|
inGameLogger.Log($"Name: {request.Name}", "#FFBF00");
|
||||||
|
inGameLogger.Log($"Race: {request.Race}");
|
||||||
|
inGameLogger.Log($"Class: {request.Class}");
|
||||||
|
inGameLogger.Log($"Portrait: #{request.PortraitIndex}");
|
||||||
|
inGameLogger.Log($"{request.Attributes}");
|
||||||
|
inGameLogger.Log($"{request.Stats}");
|
||||||
|
if(request.Perks?.perks != null && request.Perks.perks.Count > 0) {
|
||||||
|
foreach(var perk in request.Perks.perks) {
|
||||||
|
inGameLogger.Log($"Perk: {perk.Name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateParty();
|
||||||
|
canStartCheck.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateParty() {
|
private void CreateParty() {
|
||||||
var partyCreatorModel = new PartyCreatorModel(characterSystems.CharacterFactory, characterSystems.PartyFactory, characterCreationRequests, partySettings);
|
var partyCreatorModel = new PartyCreatorModel(characterSystems.CharacterFactory, characterSystems.PartyFactory, characterCreationRequests, partySettings);
|
||||||
var party = partyCreatorModel.CreatePartyForNewRun();
|
var party = partyCreatorModel.CreatePartyForNewRun();
|
||||||
gameDataState.ActiveParty = party;
|
gameDataState.ActiveParty = party;
|
||||||
|
|
||||||
inGameLogger.Log("Character Creation Results:");
|
|
||||||
inGameLogger.Log($"Protagonist: {party.Protagonist.Name}", "#FFBF00");
|
|
||||||
inGameLogger.Log($"Protagonist Race: {party.Protagonist.Race}");
|
|
||||||
inGameLogger.Log($"Protagonist Class: {party.Protagonist.Class}");
|
|
||||||
inGameLogger.Log($"Companions: {party.Companions.Count}");
|
|
||||||
inGameLogger.Log($"{party.Protagonist.Attributes}");
|
|
||||||
inGameLogger.Log($"{party.Protagonist.Stats}");
|
|
||||||
inGameLogger.Log($"{party.Protagonist.Perks}");
|
|
||||||
inGameLogger.Log($"{party.Protagonist.Modifiers}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Lifecycle ---
|
||||||
|
|
||||||
public void Tick() {
|
public void Tick() {
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Show() {
|
public void Show() {
|
||||||
characterCreationReference.gameObject.SetActive(true);
|
characterCreationReference.gameObject.SetActive(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Hide() {
|
public void Hide() {
|
||||||
characterCreationReference.gameObject.SetActive(false);
|
characterCreationReference.gameObject.SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
inGameLogger.Disable();
|
inGameLogger.Disable();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
[CreateAssetMenu(fileName = "PortraitsHolder", menuName = "Nox/Database/UI/PortraitsHolder")]
|
namespace Nox.UI {
|
||||||
public class PortraitsHolder : ScriptableObject {
|
[CreateAssetMenu(fileName = "PortraitsHolder", menuName = "Nox/Database/UI/PortraitsHolder")]
|
||||||
public Sprite[] portraits;
|
public class PortraitsHolder : ScriptableObject {
|
||||||
|
public Sprite[] portraits;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ using TMPro;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
|
|
||||||
public class StatReference : MonoBehaviour {
|
namespace Nox.UI {
|
||||||
public TextMeshProUGUI statName;
|
public class StatReference : MonoBehaviour {
|
||||||
public TextMeshProUGUI statValue;
|
public TextMeshProUGUI statName;
|
||||||
public Image statBar;
|
public TextMeshProUGUI statValue;
|
||||||
|
public Image statBar;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ namespace Nox.UI {
|
|||||||
private CharacterCreationReference characterCreationReference;
|
private CharacterCreationReference characterCreationReference;
|
||||||
private CharacterCreationView characterCreationView;
|
private CharacterCreationView characterCreationView;
|
||||||
private AsyncOperationHandle<GameObject> charCreationHandle;
|
private AsyncOperationHandle<GameObject> charCreationHandle;
|
||||||
|
private readonly StarterCharacterSettings starterCharacterSettings;
|
||||||
|
|
||||||
public MainMenuView(MenuPrefabsContainer menuPrefabsContainer,
|
public MainMenuView(MenuPrefabsContainer menuPrefabsContainer,
|
||||||
MenuGameStateData menuGameStateData,
|
MenuGameStateData menuGameStateData,
|
||||||
@@ -29,7 +30,8 @@ namespace Nox.UI {
|
|||||||
GameDataState gameDataState,
|
GameDataState gameDataState,
|
||||||
PartySettings partySettings,
|
PartySettings partySettings,
|
||||||
ICharacterSystems characterSystems,
|
ICharacterSystems characterSystems,
|
||||||
PortraitsHolder portraitsHolder) {
|
PortraitsHolder portraitsHolder,
|
||||||
|
StarterCharacterSettings starterCharacterSettings) {
|
||||||
this.menuPrefabsContainer = menuPrefabsContainer;
|
this.menuPrefabsContainer = menuPrefabsContainer;
|
||||||
this.menuGameStateData = menuGameStateData;
|
this.menuGameStateData = menuGameStateData;
|
||||||
this.saveSystem = saveSystem;
|
this.saveSystem = saveSystem;
|
||||||
@@ -37,6 +39,7 @@ namespace Nox.UI {
|
|||||||
this.partySettings = partySettings;
|
this.partySettings = partySettings;
|
||||||
this.characterSystems = characterSystems;
|
this.characterSystems = characterSystems;
|
||||||
this.portraitsHolder = portraitsHolder;
|
this.portraitsHolder = portraitsHolder;
|
||||||
|
this.starterCharacterSettings = starterCharacterSettings;
|
||||||
}
|
}
|
||||||
public void Initialize() {
|
public void Initialize() {
|
||||||
if(!mainMenuReference) {
|
if(!mainMenuReference) {
|
||||||
@@ -65,7 +68,15 @@ namespace Nox.UI {
|
|||||||
charCreationHandle = Addressables.InstantiateAsync(menuPrefabsContainer.characterCreationReference);
|
charCreationHandle = Addressables.InstantiateAsync(menuPrefabsContainer.characterCreationReference);
|
||||||
var result = charCreationHandle.WaitForCompletion();
|
var result = charCreationHandle.WaitForCompletion();
|
||||||
characterCreationReference =result.GetComponent<CharacterCreationReference>();
|
characterCreationReference =result.GetComponent<CharacterCreationReference>();
|
||||||
characterCreationView = new CharacterCreationView(characterCreationReference, menuGameStateData, saveSystem, gameDataState, partySettings, characterSystems, portraitsHolder);
|
characterCreationView = new CharacterCreationView(
|
||||||
|
characterCreationReference,
|
||||||
|
menuGameStateData,
|
||||||
|
saveSystem,
|
||||||
|
gameDataState,
|
||||||
|
partySettings,
|
||||||
|
characterSystems,
|
||||||
|
portraitsHolder,
|
||||||
|
starterCharacterSettings);
|
||||||
characterCreationView.Initialize();
|
characterCreationView.Initialize();
|
||||||
characterCreationView.Show();
|
characterCreationView.Show();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,20 +36,258 @@ MonoBehaviour:
|
|||||||
- stat: 1
|
- stat: 1
|
||||||
value: 1
|
value: 1
|
||||||
- stat: 2
|
- stat: 2
|
||||||
value: 0
|
value: 1
|
||||||
- stat: 3
|
- stat: 3
|
||||||
value: 0
|
value: 1
|
||||||
- stat: 4
|
- stat: 4
|
||||||
value: 0
|
value: 1
|
||||||
defaultPerksData:
|
defaultPerksData:
|
||||||
perks: []
|
perks: []
|
||||||
racialBonuses: []
|
defaultModifiersData:
|
||||||
classBonuses:
|
modifiers:
|
||||||
- class: 1
|
- <Name>k__BackingField: MGT_health_multiplier
|
||||||
bonusAttributes:
|
<Target>k__BackingField:
|
||||||
attributes: []
|
<Type>k__BackingField: 2
|
||||||
bonusStats:
|
<AttributeType>k__BackingField: 0
|
||||||
stats: []
|
<StatType>k__BackingField: 1
|
||||||
|
<CombatScoreType>k__BackingField: 0
|
||||||
|
<ScalingSource>k__BackingField:
|
||||||
|
<Type>k__BackingField: 1
|
||||||
|
<AttributeType>k__BackingField: 1
|
||||||
|
<StatType>k__BackingField: 0
|
||||||
|
<CombatScoreType>k__BackingField: 0
|
||||||
|
<Operation>k__BackingField: 2
|
||||||
|
<Value>k__BackingField: 3
|
||||||
|
<Requirements>k__BackingField:
|
||||||
|
- <Attribute>k__BackingField: 1
|
||||||
|
<MinimumValue>k__BackingField: 0
|
||||||
|
- <Name>k__BackingField: MGT_mana_multiplier
|
||||||
|
<Target>k__BackingField:
|
||||||
|
<Type>k__BackingField: 2
|
||||||
|
<AttributeType>k__BackingField: 0
|
||||||
|
<StatType>k__BackingField: 2
|
||||||
|
<CombatScoreType>k__BackingField: 0
|
||||||
|
<ScalingSource>k__BackingField:
|
||||||
|
<Type>k__BackingField: 1
|
||||||
|
<AttributeType>k__BackingField: 1
|
||||||
|
<StatType>k__BackingField: 0
|
||||||
|
<CombatScoreType>k__BackingField: 0
|
||||||
|
<Operation>k__BackingField: 2
|
||||||
|
<Value>k__BackingField: 1
|
||||||
|
<Requirements>k__BackingField:
|
||||||
|
- <Attribute>k__BackingField: 1
|
||||||
|
<MinimumValue>k__BackingField: 0
|
||||||
|
- <Name>k__BackingField: KNO_mana_multiplier
|
||||||
|
<Target>k__BackingField:
|
||||||
|
<Type>k__BackingField: 2
|
||||||
|
<AttributeType>k__BackingField: 0
|
||||||
|
<StatType>k__BackingField: 2
|
||||||
|
<CombatScoreType>k__BackingField: 0
|
||||||
|
<ScalingSource>k__BackingField:
|
||||||
|
<Type>k__BackingField: 1
|
||||||
|
<AttributeType>k__BackingField: 3
|
||||||
|
<StatType>k__BackingField: 0
|
||||||
|
<CombatScoreType>k__BackingField: 0
|
||||||
|
<Operation>k__BackingField: 2
|
||||||
|
<Value>k__BackingField: 2
|
||||||
|
<Requirements>k__BackingField:
|
||||||
|
- <Attribute>k__BackingField: 3
|
||||||
|
<MinimumValue>k__BackingField: 0
|
||||||
|
- <Name>k__BackingField: PER_ATK_multiplier
|
||||||
|
<Target>k__BackingField:
|
||||||
|
<Type>k__BackingField: 3
|
||||||
|
<AttributeType>k__BackingField: 0
|
||||||
|
<StatType>k__BackingField: 2
|
||||||
|
<CombatScoreType>k__BackingField: 1
|
||||||
|
<ScalingSource>k__BackingField:
|
||||||
|
<Type>k__BackingField: 1
|
||||||
|
<AttributeType>k__BackingField: 4
|
||||||
|
<StatType>k__BackingField: 0
|
||||||
|
<CombatScoreType>k__BackingField: 0
|
||||||
|
<Operation>k__BackingField: 2
|
||||||
|
<Value>k__BackingField: 1
|
||||||
|
<Requirements>k__BackingField: []
|
||||||
|
- <Name>k__BackingField: REF_ATK_multiplier
|
||||||
|
<Target>k__BackingField:
|
||||||
|
<Type>k__BackingField: 3
|
||||||
|
<AttributeType>k__BackingField: 0
|
||||||
|
<StatType>k__BackingField: 2
|
||||||
|
<CombatScoreType>k__BackingField: 1
|
||||||
|
<ScalingSource>k__BackingField:
|
||||||
|
<Type>k__BackingField: 1
|
||||||
|
<AttributeType>k__BackingField: 2
|
||||||
|
<StatType>k__BackingField: 0
|
||||||
|
<CombatScoreType>k__BackingField: 0
|
||||||
|
<Operation>k__BackingField: 3
|
||||||
|
<Value>k__BackingField: 1.05
|
||||||
|
<Requirements>k__BackingField: []
|
||||||
|
racialBonuses:
|
||||||
|
- race: 1
|
||||||
startingPerks:
|
startingPerks:
|
||||||
perks: []
|
perks: []
|
||||||
maxPartySize: 4
|
permanentModifiers:
|
||||||
|
modifiers:
|
||||||
|
- <Name>k__BackingField: KNO_bonus
|
||||||
|
<Target>k__BackingField:
|
||||||
|
<Type>k__BackingField: 1
|
||||||
|
<AttributeType>k__BackingField: 3
|
||||||
|
<StatType>k__BackingField: 0
|
||||||
|
<CombatScoreType>k__BackingField: 0
|
||||||
|
<ScalingSource>k__BackingField:
|
||||||
|
<Type>k__BackingField: 0
|
||||||
|
<AttributeType>k__BackingField: 0
|
||||||
|
<StatType>k__BackingField: 0
|
||||||
|
<CombatScoreType>k__BackingField: 0
|
||||||
|
<Operation>k__BackingField: 2
|
||||||
|
<Value>k__BackingField: 1
|
||||||
|
<Requirements>k__BackingField: []
|
||||||
|
- race: 2
|
||||||
|
startingPerks:
|
||||||
|
perks: []
|
||||||
|
permanentModifiers:
|
||||||
|
modifiers:
|
||||||
|
- <Name>k__BackingField: REF_bonus
|
||||||
|
<Target>k__BackingField:
|
||||||
|
<Type>k__BackingField: 1
|
||||||
|
<AttributeType>k__BackingField: 2
|
||||||
|
<StatType>k__BackingField: 0
|
||||||
|
<CombatScoreType>k__BackingField: 0
|
||||||
|
<ScalingSource>k__BackingField:
|
||||||
|
<Type>k__BackingField: 0
|
||||||
|
<AttributeType>k__BackingField: 0
|
||||||
|
<StatType>k__BackingField: 0
|
||||||
|
<CombatScoreType>k__BackingField: 0
|
||||||
|
<Operation>k__BackingField: 2
|
||||||
|
<Value>k__BackingField: 1
|
||||||
|
<Requirements>k__BackingField: []
|
||||||
|
- race: 3
|
||||||
|
startingPerks:
|
||||||
|
perks: []
|
||||||
|
permanentModifiers:
|
||||||
|
modifiers:
|
||||||
|
- <Name>k__BackingField: MGT_bonus
|
||||||
|
<Target>k__BackingField:
|
||||||
|
<Type>k__BackingField: 0
|
||||||
|
<AttributeType>k__BackingField: 0
|
||||||
|
<StatType>k__BackingField: 0
|
||||||
|
<CombatScoreType>k__BackingField: 0
|
||||||
|
<ScalingSource>k__BackingField:
|
||||||
|
<Type>k__BackingField: 0
|
||||||
|
<AttributeType>k__BackingField: 0
|
||||||
|
<StatType>k__BackingField: 0
|
||||||
|
<CombatScoreType>k__BackingField: 0
|
||||||
|
<Operation>k__BackingField: 2
|
||||||
|
<Value>k__BackingField: 1
|
||||||
|
<Requirements>k__BackingField: []
|
||||||
|
classBonuses:
|
||||||
|
- class: 1
|
||||||
|
startingPerks:
|
||||||
|
perks: []
|
||||||
|
permanentModifiers:
|
||||||
|
modifiers:
|
||||||
|
- <Name>k__BackingField: warrior_might_health_bonus
|
||||||
|
<Target>k__BackingField:
|
||||||
|
<Type>k__BackingField: 2
|
||||||
|
<AttributeType>k__BackingField: 0
|
||||||
|
<StatType>k__BackingField: 1
|
||||||
|
<CombatScoreType>k__BackingField: 0
|
||||||
|
<ScalingSource>k__BackingField:
|
||||||
|
<Type>k__BackingField: 1
|
||||||
|
<AttributeType>k__BackingField: 1
|
||||||
|
<StatType>k__BackingField: 0
|
||||||
|
<CombatScoreType>k__BackingField: 0
|
||||||
|
<Operation>k__BackingField: 2
|
||||||
|
<Value>k__BackingField: 4
|
||||||
|
<Requirements>k__BackingField:
|
||||||
|
- <Attribute>k__BackingField: 1
|
||||||
|
<MinimumValue>k__BackingField: 5
|
||||||
|
- class: 2
|
||||||
|
startingPerks:
|
||||||
|
perks: []
|
||||||
|
permanentModifiers:
|
||||||
|
modifiers:
|
||||||
|
- <Name>k__BackingField: rogue_REF_bonus
|
||||||
|
<Target>k__BackingField:
|
||||||
|
<Type>k__BackingField: 3
|
||||||
|
<AttributeType>k__BackingField: 0
|
||||||
|
<StatType>k__BackingField: 0
|
||||||
|
<CombatScoreType>k__BackingField: 2
|
||||||
|
<ScalingSource>k__BackingField:
|
||||||
|
<Type>k__BackingField: 1
|
||||||
|
<AttributeType>k__BackingField: 2
|
||||||
|
<StatType>k__BackingField: 0
|
||||||
|
<CombatScoreType>k__BackingField: 0
|
||||||
|
<Operation>k__BackingField: 2
|
||||||
|
<Value>k__BackingField: 1
|
||||||
|
<Requirements>k__BackingField:
|
||||||
|
- <Attribute>k__BackingField: 2
|
||||||
|
<MinimumValue>k__BackingField: 4
|
||||||
|
- class: 2
|
||||||
|
startingPerks:
|
||||||
|
perks: []
|
||||||
|
permanentModifiers:
|
||||||
|
modifiers:
|
||||||
|
- <Name>k__BackingField: rogue_PER_bonus
|
||||||
|
<Target>k__BackingField:
|
||||||
|
<Type>k__BackingField: 3
|
||||||
|
<AttributeType>k__BackingField: 0
|
||||||
|
<StatType>k__BackingField: 0
|
||||||
|
<CombatScoreType>k__BackingField: 1
|
||||||
|
<ScalingSource>k__BackingField:
|
||||||
|
<Type>k__BackingField: 1
|
||||||
|
<AttributeType>k__BackingField: 4
|
||||||
|
<StatType>k__BackingField: 0
|
||||||
|
<CombatScoreType>k__BackingField: 0
|
||||||
|
<Operation>k__BackingField: 2
|
||||||
|
<Value>k__BackingField: 1
|
||||||
|
<Requirements>k__BackingField:
|
||||||
|
- <Attribute>k__BackingField: 4
|
||||||
|
<MinimumValue>k__BackingField: 3
|
||||||
|
- class: 3
|
||||||
|
startingPerks:
|
||||||
|
perks: []
|
||||||
|
permanentModifiers:
|
||||||
|
modifiers:
|
||||||
|
- <Name>k__BackingField: rmage_KNO_bonus
|
||||||
|
<Target>k__BackingField:
|
||||||
|
<Type>k__BackingField: 3
|
||||||
|
<AttributeType>k__BackingField: 0
|
||||||
|
<StatType>k__BackingField: 0
|
||||||
|
<CombatScoreType>k__BackingField: 1
|
||||||
|
<ScalingSource>k__BackingField:
|
||||||
|
<Type>k__BackingField: 1
|
||||||
|
<AttributeType>k__BackingField: 3
|
||||||
|
<StatType>k__BackingField: 0
|
||||||
|
<CombatScoreType>k__BackingField: 0
|
||||||
|
<Operation>k__BackingField: 2
|
||||||
|
<Value>k__BackingField: 1
|
||||||
|
<Requirements>k__BackingField:
|
||||||
|
- <Attribute>k__BackingField: 3
|
||||||
|
<MinimumValue>k__BackingField: 5
|
||||||
|
- class: 4
|
||||||
|
startingPerks:
|
||||||
|
perks: []
|
||||||
|
permanentModifiers:
|
||||||
|
modifiers:
|
||||||
|
- <Name>k__BackingField: herald_ALL_bonus
|
||||||
|
<Target>k__BackingField:
|
||||||
|
<Type>k__BackingField: 3
|
||||||
|
<AttributeType>k__BackingField: 0
|
||||||
|
<StatType>k__BackingField: 0
|
||||||
|
<CombatScoreType>k__BackingField: 3
|
||||||
|
<ScalingSource>k__BackingField:
|
||||||
|
<Type>k__BackingField: 1
|
||||||
|
<AttributeType>k__BackingField: 3
|
||||||
|
<StatType>k__BackingField: 0
|
||||||
|
<CombatScoreType>k__BackingField: 0
|
||||||
|
<Operation>k__BackingField: 2
|
||||||
|
<Value>k__BackingField: 2
|
||||||
|
<Requirements>k__BackingField:
|
||||||
|
- <Attribute>k__BackingField: 1
|
||||||
|
<MinimumValue>k__BackingField: 2
|
||||||
|
- <Attribute>k__BackingField: 2
|
||||||
|
<MinimumValue>k__BackingField: 2
|
||||||
|
- <Attribute>k__BackingField: 3
|
||||||
|
<MinimumValue>k__BackingField: 2
|
||||||
|
- <Attribute>k__BackingField: 4
|
||||||
|
<MinimumValue>k__BackingField: 2
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ MonoBehaviour:
|
|||||||
m_Script: {fileID: 11500000, guid: f51aaf11e81876845b289f5f7d310469, type: 3}
|
m_Script: {fileID: 11500000, guid: f51aaf11e81876845b289f5f7d310469, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::AttributeReference
|
m_EditorClassIdentifier: Assembly-CSharp::AttributeReference
|
||||||
|
removePointsButton: {fileID: 111231332404835723}
|
||||||
|
addPointsButton: {fileID: 7007629831261223577}
|
||||||
|
attributeName: {fileID: 6232133109646223137}
|
||||||
|
attributeValue: {fileID: 7864923222111671344}
|
||||||
--- !u!1 &602367721906152228
|
--- !u!1 &602367721906152228
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|||||||
@@ -1876,10 +1876,6 @@ PrefabInstance:
|
|||||||
propertyPath: m_AnchorMax.y
|
propertyPath: m_AnchorMax.y
|
||||||
value: 0
|
value: 0
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 374280998538979084, guid: 1b41f907ca960b644ae3af6e1942b9fb, type: 3}
|
|
||||||
propertyPath: m_AnchorMin.y
|
|
||||||
value: 0
|
|
||||||
objectReference: {fileID: 0}
|
|
||||||
- target: {fileID: 1525412503934350410, guid: 1b41f907ca960b644ae3af6e1942b9fb, type: 3}
|
- target: {fileID: 1525412503934350410, guid: 1b41f907ca960b644ae3af6e1942b9fb, type: 3}
|
||||||
propertyPath: m_Name
|
propertyPath: m_Name
|
||||||
value: LogContainer
|
value: LogContainer
|
||||||
@@ -1972,38 +1968,6 @@ PrefabInstance:
|
|||||||
propertyPath: m_LocalEulerAnglesHint.z
|
propertyPath: m_LocalEulerAnglesHint.z
|
||||||
value: 0
|
value: 0
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 2688140319784364735, guid: 1b41f907ca960b644ae3af6e1942b9fb, type: 3}
|
|
||||||
propertyPath: m_AnchorMax.x
|
|
||||||
value: 0
|
|
||||||
objectReference: {fileID: 0}
|
|
||||||
- target: {fileID: 2688140319784364735, guid: 1b41f907ca960b644ae3af6e1942b9fb, type: 3}
|
|
||||||
propertyPath: m_SizeDelta.x
|
|
||||||
value: 0
|
|
||||||
objectReference: {fileID: 0}
|
|
||||||
- target: {fileID: 4070132176653801724, guid: 1b41f907ca960b644ae3af6e1942b9fb, type: 3}
|
|
||||||
propertyPath: m_AnchorMax.y
|
|
||||||
value: 0
|
|
||||||
objectReference: {fileID: 0}
|
|
||||||
- target: {fileID: 4070132176653801724, guid: 1b41f907ca960b644ae3af6e1942b9fb, type: 3}
|
|
||||||
propertyPath: m_SizeDelta.y
|
|
||||||
value: 0
|
|
||||||
objectReference: {fileID: 0}
|
|
||||||
- target: {fileID: 6286131802514671469, guid: 1b41f907ca960b644ae3af6e1942b9fb, type: 3}
|
|
||||||
propertyPath: m_AnchorMax.x
|
|
||||||
value: 0
|
|
||||||
objectReference: {fileID: 0}
|
|
||||||
- target: {fileID: 6286131802514671469, guid: 1b41f907ca960b644ae3af6e1942b9fb, type: 3}
|
|
||||||
propertyPath: m_AnchorMax.y
|
|
||||||
value: 0
|
|
||||||
objectReference: {fileID: 0}
|
|
||||||
- target: {fileID: 6286131802514671469, guid: 1b41f907ca960b644ae3af6e1942b9fb, type: 3}
|
|
||||||
propertyPath: m_SizeDelta.x
|
|
||||||
value: 0
|
|
||||||
objectReference: {fileID: 0}
|
|
||||||
- target: {fileID: 6286131802514671469, guid: 1b41f907ca960b644ae3af6e1942b9fb, type: 3}
|
|
||||||
propertyPath: m_SizeDelta.y
|
|
||||||
value: 0
|
|
||||||
objectReference: {fileID: 0}
|
|
||||||
m_RemovedComponents: []
|
m_RemovedComponents: []
|
||||||
m_RemovedGameObjects: []
|
m_RemovedGameObjects: []
|
||||||
m_AddedGameObjects: []
|
m_AddedGameObjects: []
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ MonoBehaviour:
|
|||||||
scrollRect: {fileID: 2554617498295089481}
|
scrollRect: {fileID: 2554617498295089481}
|
||||||
content: {fileID: 3059084814696477984}
|
content: {fileID: 3059084814696477984}
|
||||||
entryPrefab: {fileID: 4832918257971952213, guid: 9d1c7837b0b5a9f45baa84f326fc247c, type: 3}
|
entryPrefab: {fileID: 4832918257971952213, guid: 9d1c7837b0b5a9f45baa84f326fc247c, type: 3}
|
||||||
poolSize: 20
|
poolSize: 200
|
||||||
--- !u!1 &2405629305058117087
|
--- !u!1 &2405629305058117087
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -153,6 +153,7 @@ GameObject:
|
|||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 3059084814696477984}
|
- component: {fileID: 3059084814696477984}
|
||||||
- component: {fileID: 1744225011043606462}
|
- component: {fileID: 1744225011043606462}
|
||||||
|
- component: {fileID: 4069423691573659896}
|
||||||
m_Layer: 5
|
m_Layer: 5
|
||||||
m_Name: Content
|
m_Name: Content
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
@@ -176,8 +177,8 @@ RectTransform:
|
|||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
m_AnchorMin: {x: 0, y: 1}
|
m_AnchorMin: {x: 0, y: 1}
|
||||||
m_AnchorMax: {x: 1, y: 1}
|
m_AnchorMax: {x: 1, y: 1}
|
||||||
m_AnchoredPosition: {x: 20.186, y: 0.00002861023}
|
m_AnchoredPosition: {x: 20.186, y: 0}
|
||||||
m_SizeDelta: {x: -40.374, y: 1085.3}
|
m_SizeDelta: {x: -40.374, y: 0}
|
||||||
m_Pivot: {x: 0, y: 1}
|
m_Pivot: {x: 0, y: 1}
|
||||||
--- !u!114 &1744225011043606462
|
--- !u!114 &1744225011043606462
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
@@ -197,7 +198,7 @@ MonoBehaviour:
|
|||||||
m_Top: 0
|
m_Top: 0
|
||||||
m_Bottom: 0
|
m_Bottom: 0
|
||||||
m_ChildAlignment: 0
|
m_ChildAlignment: 0
|
||||||
m_Spacing: 2
|
m_Spacing: 4.6
|
||||||
m_ChildForceExpandWidth: 1
|
m_ChildForceExpandWidth: 1
|
||||||
m_ChildForceExpandHeight: 0
|
m_ChildForceExpandHeight: 0
|
||||||
m_ChildControlWidth: 1
|
m_ChildControlWidth: 1
|
||||||
@@ -205,6 +206,20 @@ MonoBehaviour:
|
|||||||
m_ChildScaleWidth: 0
|
m_ChildScaleWidth: 0
|
||||||
m_ChildScaleHeight: 0
|
m_ChildScaleHeight: 0
|
||||||
m_ReverseArrangement: 0
|
m_ReverseArrangement: 0
|
||||||
|
--- !u!114 &4069423691573659896
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 2405629305058117087}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.ContentSizeFitter
|
||||||
|
m_HorizontalFit: 0
|
||||||
|
m_VerticalFit: 2
|
||||||
--- !u!1 &3952660362247579954
|
--- !u!1 &3952660362247579954
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -537,7 +552,7 @@ MonoBehaviour:
|
|||||||
m_Horizontal: 0
|
m_Horizontal: 0
|
||||||
m_Vertical: 1
|
m_Vertical: 1
|
||||||
m_MovementType: 2
|
m_MovementType: 2
|
||||||
m_Elasticity: 0.23
|
m_Elasticity: 0.05
|
||||||
m_Inertia: 1
|
m_Inertia: 1
|
||||||
m_DecelerationRate: 0.135
|
m_DecelerationRate: 0.135
|
||||||
m_ScrollSensitivity: 20
|
m_ScrollSensitivity: 20
|
||||||
@@ -671,8 +686,8 @@ MonoBehaviour:
|
|||||||
m_TargetGraphic: {fileID: 6879256843609484833}
|
m_TargetGraphic: {fileID: 6879256843609484833}
|
||||||
m_HandleRect: {fileID: 374280998538979084}
|
m_HandleRect: {fileID: 374280998538979084}
|
||||||
m_Direction: 2
|
m_Direction: 2
|
||||||
m_Value: 1
|
m_Value: 0
|
||||||
m_Size: 0.9999999
|
m_Size: 1
|
||||||
m_NumberOfSteps: 0
|
m_NumberOfSteps: 0
|
||||||
m_OnValueChanged:
|
m_OnValueChanged:
|
||||||
m_PersistentCalls:
|
m_PersistentCalls:
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ GameObject:
|
|||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 382400732949652569}
|
- component: {fileID: 382400732949652569}
|
||||||
- component: {fileID: 4832918257971952213}
|
- component: {fileID: 4832918257971952213}
|
||||||
- component: {fileID: 1727094465477629914}
|
- component: {fileID: 4055037707474935581}
|
||||||
|
- component: {fileID: 5541913713772788578}
|
||||||
|
- component: {fileID: 440716831877687606}
|
||||||
m_Layer: 0
|
m_Layer: 0
|
||||||
m_Name: LogEntry
|
m_Name: LogEntry
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
@@ -29,14 +31,13 @@ RectTransform:
|
|||||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
m_ConstrainProportionsScale: 0
|
m_ConstrainProportionsScale: 0
|
||||||
m_Children:
|
m_Children: []
|
||||||
- {fileID: 8597194705437786868}
|
|
||||||
m_Father: {fileID: 0}
|
m_Father: {fileID: 0}
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
m_AnchorMin: {x: 0, y: 1}
|
m_AnchorMin: {x: 0, y: 1}
|
||||||
m_AnchorMax: {x: 0, y: 1}
|
m_AnchorMax: {x: 0, y: 1}
|
||||||
m_AnchoredPosition: {x: 0, y: 10.8254}
|
m_AnchoredPosition: {x: 499.7, y: -12}
|
||||||
m_SizeDelta: {x: 1093, y: 65.3898}
|
m_SizeDelta: {x: 999.4, y: 0}
|
||||||
m_Pivot: {x: 0.5, y: 0.5}
|
m_Pivot: {x: 0.5, y: 0.5}
|
||||||
--- !u!114 &4832918257971952213
|
--- !u!114 &4832918257971952213
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
@@ -50,8 +51,16 @@ MonoBehaviour:
|
|||||||
m_Script: {fileID: 11500000, guid: 5526887cdf77a54439357be8b5754ffc, type: 3}
|
m_Script: {fileID: 11500000, guid: 5526887cdf77a54439357be8b5754ffc, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier: Jovian.InGameLogging::Jovian.InGameLogging.UI.LogEntryView
|
m_EditorClassIdentifier: Jovian.InGameLogging::Jovian.InGameLogging.UI.LogEntryView
|
||||||
messageText: {fileID: 4259574353180979179}
|
messageText: {fileID: 5541913713772788578}
|
||||||
--- !u!114 &1727094465477629914
|
--- !u!222 &4055037707474935581
|
||||||
|
CanvasRenderer:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 6501593483943143564}
|
||||||
|
m_CullTransparentMesh: 1
|
||||||
|
--- !u!114 &5541913713772788578
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
@@ -60,71 +69,6 @@ MonoBehaviour:
|
|||||||
m_GameObject: {fileID: 6501593483943143564}
|
m_GameObject: {fileID: 6501593483943143564}
|
||||||
m_Enabled: 1
|
m_Enabled: 1
|
||||||
m_EditorHideFlags: 0
|
m_EditorHideFlags: 0
|
||||||
m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}
|
|
||||||
m_Name:
|
|
||||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.LayoutElement
|
|
||||||
m_IgnoreLayout: 0
|
|
||||||
m_MinWidth: -1
|
|
||||||
m_MinHeight: -1
|
|
||||||
m_PreferredWidth: -1
|
|
||||||
m_PreferredHeight: -1
|
|
||||||
m_FlexibleWidth: 1
|
|
||||||
m_FlexibleHeight: 1
|
|
||||||
m_LayoutPriority: 1
|
|
||||||
--- !u!1 &8033193440249993941
|
|
||||||
GameObject:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
serializedVersion: 6
|
|
||||||
m_Component:
|
|
||||||
- component: {fileID: 8597194705437786868}
|
|
||||||
- component: {fileID: 8331827018725656388}
|
|
||||||
- component: {fileID: 4259574353180979179}
|
|
||||||
m_Layer: 0
|
|
||||||
m_Name: Text (TMP)
|
|
||||||
m_TagString: Untagged
|
|
||||||
m_Icon: {fileID: 0}
|
|
||||||
m_NavMeshLayer: 0
|
|
||||||
m_StaticEditorFlags: 0
|
|
||||||
m_IsActive: 1
|
|
||||||
--- !u!224 &8597194705437786868
|
|
||||||
RectTransform:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 8033193440249993941}
|
|
||||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
|
||||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
|
||||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
|
||||||
m_ConstrainProportionsScale: 0
|
|
||||||
m_Children: []
|
|
||||||
m_Father: {fileID: 382400732949652569}
|
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
|
||||||
m_AnchorMin: {x: 0, y: 0}
|
|
||||||
m_AnchorMax: {x: 1, y: 1}
|
|
||||||
m_AnchoredPosition: {x: 0, y: 0}
|
|
||||||
m_SizeDelta: {x: 0, y: 0}
|
|
||||||
m_Pivot: {x: 0.5, y: 0.5}
|
|
||||||
--- !u!222 &8331827018725656388
|
|
||||||
CanvasRenderer:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 8033193440249993941}
|
|
||||||
m_CullTransparentMesh: 1
|
|
||||||
--- !u!114 &4259574353180979179
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 8033193440249993941}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI
|
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI
|
||||||
@@ -163,10 +107,10 @@ MonoBehaviour:
|
|||||||
m_faceColor:
|
m_faceColor:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
rgba: 4294967295
|
rgba: 4294967295
|
||||||
m_fontSize: 24.55
|
m_fontSize: 24
|
||||||
m_fontSizeBase: 36
|
m_fontSizeBase: 24
|
||||||
m_fontWeight: 400
|
m_fontWeight: 400
|
||||||
m_enableAutoSizing: 1
|
m_enableAutoSizing: 0
|
||||||
m_fontSizeMin: 5
|
m_fontSizeMin: 5
|
||||||
m_fontSizeMax: 24.55
|
m_fontSizeMax: 24.55
|
||||||
m_fontStyle: 0
|
m_fontStyle: 0
|
||||||
@@ -208,3 +152,17 @@ MonoBehaviour:
|
|||||||
m_hasFontAssetChanged: 0
|
m_hasFontAssetChanged: 0
|
||||||
m_baseMaterial: {fileID: 0}
|
m_baseMaterial: {fileID: 0}
|
||||||
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
|
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
--- !u!114 &440716831877687606
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 6501593483943143564}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.ContentSizeFitter
|
||||||
|
m_HorizontalFit: 0
|
||||||
|
m_VerticalFit: 2
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
@@ -12,6 +13,8 @@ namespace Jovian.InGameLogging.UI {
|
|||||||
IGameLogStore store;
|
IGameLogStore store;
|
||||||
LogChannel? channelFilter;
|
LogChannel? channelFilter;
|
||||||
bool autoScroll = true;
|
bool autoScroll = true;
|
||||||
|
bool scrollingToBottom;
|
||||||
|
Coroutine scrollCoroutine;
|
||||||
|
|
||||||
readonly List<LogEntryView> activeEntries = new();
|
readonly List<LogEntryView> activeEntries = new();
|
||||||
readonly Stack<LogEntryView> pool = new();
|
readonly Stack<LogEntryView> pool = new();
|
||||||
@@ -76,11 +79,27 @@ namespace Jovian.InGameLogging.UI {
|
|||||||
activeEntries.Add(view);
|
activeEntries.Add(view);
|
||||||
|
|
||||||
if(autoScroll) {
|
if(autoScroll) {
|
||||||
Canvas.ForceUpdateCanvases();
|
RequestScrollToBottom();
|
||||||
scrollRect.verticalNormalizedPosition = 0f;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RequestScrollToBottom() {
|
||||||
|
if(scrollCoroutine != null) {
|
||||||
|
StopCoroutine(scrollCoroutine);
|
||||||
|
}
|
||||||
|
scrollCoroutine = StartCoroutine(ScrollToBottomRoutine());
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator ScrollToBottomRoutine() {
|
||||||
|
scrollingToBottom = true;
|
||||||
|
yield return null;
|
||||||
|
LayoutRebuilder.ForceRebuildLayoutImmediate(content);
|
||||||
|
scrollRect.verticalNormalizedPosition = 0f;
|
||||||
|
yield return null;
|
||||||
|
scrollingToBottom = false;
|
||||||
|
scrollCoroutine = null;
|
||||||
|
}
|
||||||
|
|
||||||
void HandleCleared() {
|
void HandleCleared() {
|
||||||
for(int i = activeEntries.Count - 1; i >= 0; i--) {
|
for(int i = activeEntries.Count - 1; i >= 0; i--) {
|
||||||
ReturnToPool(activeEntries[i]);
|
ReturnToPool(activeEntries[i]);
|
||||||
@@ -104,12 +123,14 @@ namespace Jovian.InGameLogging.UI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(autoScroll) {
|
if(autoScroll) {
|
||||||
Canvas.ForceUpdateCanvases();
|
RequestScrollToBottom();
|
||||||
scrollRect.verticalNormalizedPosition = 0f;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleScrollChanged(Vector2 position) {
|
void HandleScrollChanged(Vector2 position) {
|
||||||
|
if(scrollingToBottom) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
autoScroll = position.y <= 0.01f;
|
autoScroll = position.y <= 0.01f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user