This commit is contained in:
MuHua-123
2025-07-25 17:46:56 +08:00
parent 36c827fd24
commit b6adf24ff1
21 changed files with 326 additions and 184 deletions
+28
View File
@@ -91,3 +91,31 @@ public class DataProfession {
/// <summary> 累计生命点 </summary>
public List<int> hitPoints = new List<int>();
}
/// <summary>
/// 战斗角色 - 数据
/// </summary>
public class DataCombatRole : DataAttribute {
/// <summary> 角色数据 </summary>
public readonly DataCharacter character;
/// <summary> 战斗角色 </summary>
public DataCombatRole(DataCharacter character) => this.character = character;
/// <summary> 归属队伍 </summary>
public int team;
/// <summary> 角色名字 </summary>
public string name;
/// <summary> 战斗等级 </summary>
public int level;
/// <summary> 战场位置 </summary>
public int position;
/// <summary> 先攻顺序 </summary>
public int sequence;
/// <summary> 护甲等级 </summary>
public int armorClass;
/// <summary> 生命点 </summary>
public Vector2Int hitPoint;
/// <summary> 武器1 </summary>
public DataWeapon weapon1;
/// <summary> 武器2 </summary>
public DataWeapon weapon2;
}
@@ -1,46 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 战斗角色
/// </summary>
public class BattleCharacter : DataAttribute {
/// <summary> 角色数据 </summary>
public readonly DataCharacter character;
/// <summary> 归属队伍 </summary>
public int team;
/// <summary> 角色名字 </summary>
public string name;
/// <summary> 战斗等级 </summary>
public int level;
/// <summary> 战场位置 </summary>
public int position;
/// <summary> 先攻顺序 </summary>
public int sequence;
/// <summary> 护甲等级 </summary>
public int armorClass;
/// <summary> 生命点 </summary>
public Vector2Int hitPoint;
public BattleCharacter(DataCharacter character, int team) {
this.character = character;
AttributeTool.Cover(this, character);
this.team = team;
name = character.name;
level = character.Level;
position = 1;
hitPoint = new Vector2Int(character.HitPoint, character.HitPoint);
armorClass = character.ArmorClass;
}
/// <summary> 是否允许行动 </summary>
public bool IsAction() {
return hitPoint.x > 0;
}
/// <summary> 是否敌对目标 </summary>
public bool IsHostility(BattleCharacter target) {
return team != target.team && target.hitPoint.x > 0;
}
}
@@ -6,22 +6,15 @@ using UnityEngine;
/// 阶段类型
/// </summary>
public enum PhaseType {
,
,
,
,
,
, , , , ,
,
}
/// <summary>
/// 阶段
/// </summary>
public interface IPhase {
/// <summary> 开始阶段 </summary>
public void StartPhase();
/// <summary> 更新阶段 </summary>
public void UpdatePhase();
/// <summary> 退出阶段 </summary>
public void QuitPhase();
/// <summary> 执行阶段 </summary>
public void Execute();
}
/// <summary>
/// 战斗阶段
@@ -30,12 +23,12 @@ public abstract class BattlePhase : IPhase {
/// <summary> 模拟器 </summary>
public readonly BattleSimulator simulator;
/// <summary> 行动角色 </summary>
public DataCombatRole ActionRole => simulator.actionRole;
/// <summary> 战斗队列 </summary>
public BattleQueue BattleQueue => simulator.battleQueue;
public BattlePhase(BattleSimulator simulator) => this.simulator = simulator;
public abstract void StartPhase();
public abstract void UpdatePhase();
public abstract void QuitPhase();
public abstract void Execute();
}
@@ -6,55 +6,70 @@ using UnityEngine;
/// 行动阶段
/// </summary>
public class PhaseAction : BattlePhase {
/// <summary> 当前行动 </summary>
private BattleCharacter currentAction;
/// <summary> 当前目标 </summary>
private BattleCharacter currentTarget;
public PhaseAction(BattleSimulator simulator) : base(simulator) { }
public override void StartPhase() {
// throw new System.NotImplementedException();
}
public override void UpdatePhase() {
// 选择行动的角色,如果没有则进入下一轮
if (!SelectAction()) { return; }
// 选择攻击的目标,如果没有目标则结算战斗
if (!SelectTarget()) { return; }
// 命中检定
int hit = Dice.Roll20(currentAction.DexModifier);
int ac = currentTarget.armorClass;
bool isHit = hit > ac;
// 伤害计算
if (isHit) {
int damage = Dice.Roll8(currentAction.StrModifier);
currentTarget.hitPoint.x -= damage;
Debug.Log($"{currentAction.name}使用 普通攻击({hit}) 对 {currentTarget.name}({ac}) 造成 {damage} 点伤害!");
}
else {
Debug.Log($"{currentAction.name}使用 普通攻击({hit}) 对 {currentTarget.name}({ac}) 未命中!");
}
// TODO:记录器
// Debug.Log($"正式回合:{roundCount}");
}
public override void QuitPhase() {
// throw new System.NotImplementedException();
}
public override void Execute() {
/// <summary> 选择当前行动角色 </summary>
private bool SelectAction() {
// 选择行动的角色,如果没有则进入下一回合
if (!BattleQueue.Dequeue(out currentAction)) { simulator.Transition(PhaseType.); return false; }
// 判断是否可以行动
if (!currentAction.IsAction()) { return false; }
return true;
}
/// <summary> 选择当前目标角色 </summary>
private bool SelectTarget() {
// 选择一个可以攻击的目标
currentTarget = BattleQueue.FirstOrDefault(obj => currentAction.IsHostility(obj));
// 如果没有可以攻击的目标则结算战斗
if (currentTarget == null) { simulator.Transition(PhaseType.); }
return currentTarget != null;
}
}
/// <summary>
/// 行动角色选择
/// </summary>
public class PhaseActionRoleSelect : BattlePhase {
public PhaseActionRoleSelect(BattleSimulator simulator) : base(simulator) { }
public override void Execute() {
// 选择行动的角色,如果没有则进入下一回合
PhaseType phase = BattleQueue.Dequeue(out simulator.actionRole) ? PhaseType. : PhaseType.;
simulator.Transition(phase);
}
}
/// <summary>
/// 行动角色攻击
/// </summary>
public class PhaseActionRoleAttack : BattlePhase {
public PhaseActionRoleAttack(BattleSimulator simulator) : base(simulator) { }
public override void Execute() {
// 判断是否可以行动
if (ActionRole.hitPoint.x <= 0) { simulator.Transition(PhaseType.); return; }
// 选择可以攻击的目标
List<DataCombatRole> roles = AttackTarget();
// 如果没有可以攻击的目标则结算战斗
if (roles.Count == 0) { simulator.Transition(PhaseType.); return; }
// 攻击单体目标
int randomIndex = Random.Range(0, roles.Count);
DataCombatRole target = roles[randomIndex];
// 武器判断
// ActionRole.weapon1
// 命中检定
int hit = Dice.Roll20(ActionRole.StrModifier);
int armorClass = target.armorClass;
// 伤害计算
int damage = Dice.Roll8(ActionRole.StrModifier);
if (hit > armorClass) { target.hitPoint.x -= damage; }
// 生成战斗消息
MessageNormalAttack message = new MessageNormalAttack();
message.Settings(ActionRole, hit, damage);
message.Settings(target, armorClass);
Debug.Log(message);
simulator.Transition(PhaseType.);
}
/// <summary> 攻击目标 </summary>
private List<DataCombatRole> AttackTarget() {
return BattleQueue.Where(Hostility);
}
/// <summary> 敌对目标 </summary>
public bool Hostility(DataCombatRole role) {
return role.team != ActionRole.team && role.hitPoint.x > 0;
}
}
/// <summary>
/// 战斗行动
/// </summary>
public class BattleAction {
}
@@ -9,13 +9,7 @@ public class PhaseAssault : BattlePhase {
public PhaseAssault(BattleSimulator simulator) : base(simulator) { }
public override void StartPhase() {
// throw new System.NotImplementedException();
}
public override void UpdatePhase() {
// throw new System.NotImplementedException();
}
public override void QuitPhase() {
// throw new System.NotImplementedException();
public override void Execute() {
}
}
@@ -9,17 +9,10 @@ public class PhaseFinish : BattlePhase {
public PhaseFinish(BattleSimulator simulator) : base(simulator) { }
public override void StartPhase() {
// TODO:记录器
Debug.Log("结束战斗!");
}
public override void UpdatePhase() {
public override void Execute() {
// TODO:需要添加结算判断
// simulator.Transition(PhaseType.回合阶段);
// TODO:记录器
}
public override void QuitPhase() {
// throw new System.NotImplementedException();
Debug.Log("结束战斗!");
}
}
@@ -11,20 +11,14 @@ public class PhaseFormal : BattlePhase {
public PhaseFormal(BattleSimulator simulator) : base(simulator) { }
public override void StartPhase() {
// throw new System.NotImplementedException();
}
public override void UpdatePhase() {
public override void Execute() {
roundCount++;
BattleQueue.UpdateQueue();
simulator.Transition(PhaseType.);
// TODO:记录器
Debug.Log($"正式回合:{roundCount}");
string message = "存活";
BattleQueue.ForEach(obj => message += $" {obj.name}({obj.hitPoint.x})");
Debug.Log(message);
}
public override void QuitPhase() {
// throw new System.NotImplementedException();
simulator.Transition(PhaseType.);
}
}
@@ -9,20 +9,14 @@ public class PhaseInitiative : BattlePhase {
public PhaseInitiative(BattleSimulator simulator) : base(simulator) { }
public override void StartPhase() {
// throw new System.NotImplementedException();
}
public override void UpdatePhase() {
public override void Execute() {
BattleQueue.ForEach(obj => obj.sequence = Dice.Roll20(obj.DexModifier));
BattleQueue.OrderByDescending(c => c.sequence);
// TODO:需要添加突袭阶段
simulator.Transition(PhaseType.);
// TODO:记录器
string message = "先攻";
BattleQueue.ForEach(obj => message += $" {obj.name}({obj.sequence})");
Debug.Log(message);
}
public override void QuitPhase() {
// throw new System.NotImplementedException();
simulator.Transition(PhaseType.);
}
}
+10 -10
View File
@@ -9,40 +9,40 @@ using UnityEngine;
/// </summary>
public class BattleQueue {
/// <summary> 执行队列 </summary>
public Queue<BattleCharacter> queue = new Queue<BattleCharacter>();
public Queue<DataCombatRole> queue = new Queue<DataCombatRole>();
/// <summary> 战斗合集 </summary>
public List<BattleCharacter> characters = new List<BattleCharacter>();
public List<DataCombatRole> characters = new List<DataCombatRole>();
/// <summary> 添加角色 </summary>
public void Add(List<BattleCharacter> list) {
public void Add(List<DataCombatRole> list) {
characters.AddRange(list);
}
/// <summary> 添加角色 </summary>
public void Add(BattleCharacter character) {
public void Add(DataCombatRole character) {
characters.Add(character);
}
/// <summary> 遍历角色 </summary>
public void ForEach(Action<BattleCharacter> action) {
public void ForEach(Action<DataCombatRole> action) {
characters.ForEach(action);
}
// /// <summary> 排序:大到小 </summary>
public void OrderByDescending(Func<BattleCharacter, int> func) {
public void OrderByDescending(Func<DataCombatRole, int> func) {
characters = characters.OrderByDescending(func).ToList();
}
/// <summary> 根据条件查询元素 </summary>
public List<BattleCharacter> Where(Func<BattleCharacter, bool> predicate) {
public List<DataCombatRole> Where(Func<DataCombatRole, bool> predicate) {
return characters.Where(predicate).ToList();
}
/// <summary> 根据条件查询第一个匹配的元素 </summary>
public BattleCharacter FirstOrDefault(Func<BattleCharacter, bool> predicate) {
public DataCombatRole FirstOrDefault(Func<DataCombatRole, bool> predicate) {
return characters.FirstOrDefault(predicate);
}
/// <summary> 更新队列 </summary>
public void UpdateQueue() {
queue = new Queue<BattleCharacter>(characters);
queue = new Queue<DataCombatRole>(characters);
}
/// <summary> 取出一个 </summary>
public bool Dequeue(out BattleCharacter battle) {
public bool Dequeue(out DataCombatRole battle) {
battle = queue.Count > 0 ? queue.Dequeue() : null;
return battle != null;
}
@@ -0,0 +1,81 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 战斗报告
/// </summary>
public class BattleReport {
/// <summary> 战斗消息 </summary>
public List<BattleMessage> messages;
}
/// <summary>
/// 战斗消息
/// </summary>
public class BattleMessage {
}
/// <summary>
/// 普通攻击 - 战斗消息
/// </summary>
public class MessageNormalAttack : BattleMessage {
/// <summary> 攻击者 </summary>
public DataCombatRole attacker;
/// <summary> 命中值 </summary>
public int hit;
/// <summary> 伤害值 </summary>
public int damage;
/// <summary> 被攻击者 </summary>
public DataCombatRole attacked;
/// <summary> 护甲等级 </summary>
public int armorClass;
/// <summary> 设置攻击者 </summary>
public void Settings(DataCombatRole attacker, int hit, int damage) {
this.attacker = attacker;
this.hit = hit;
this.damage = damage;
}
/// <summary> 设置被攻击者 </summary>
public void Settings(DataCombatRole attacked, int armorClass) {
this.attacked = attacked;
this.armorClass = armorClass;
}
public override string ToString() {
return $"{attacker.name}{Dynamics()}{attacked.name}({armorClass}){attacked.name}{Result()}";
}
/// <summary> 动态修饰词 </summary>
private string Dynamics() {
return $"使用长剑劈向({hit})";
}
/// <summary> 结果修饰词 </summary>
private string Result() {
return hit <= armorClass ? Miss() : Hit();
}
// 未命中的多种情况
private string Miss() {
string[] missText = new string[] {
"轻松避开了这一击!",
"巧妙地闪开了!",
"险之又险地躲过了!",
"成功格挡住了!"
};
return RandomChoice(missText);
}
// 命中的情况,可根据伤害量或是否暴击细分
private string Hit() {
string[] hitText = new string[] {
$"躲避不及遭受到了{damage}点伤害!",
$"被狠狠击中,承受了{damage}点伤害!",
$"格挡失败受到了{damage}点伤害!",
};
return RandomChoice(hitText);
}
// 辅助方法:随机选择一个字符串,增加多样性
private string RandomChoice(params string[] options) {
if (options.Length == 0) return "";
return options[Random.Range(0, options.Length)];
}
}
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: b4cb406221da1364f97a333bf1e3442c
guid: 26a3fb20ae1a54143ab478049f4acd8e
MonoImporter:
externalObjects: {}
serializedVersion: 2
@@ -9,6 +9,8 @@ using UnityEngine;
/// 战斗 - 模拟器
/// </summary>
public class BattleSimulator {
/// <summary> 当前行动 </summary>
public DataCombatRole actionRole;
/// <summary> 战斗队列 </summary>
public BattleQueue battleQueue = new BattleQueue();
@@ -18,8 +20,11 @@ public class BattleSimulator {
public Dictionary<PhaseType, IPhase> dictionary = new Dictionary<PhaseType, IPhase>();
public BattleSimulator(BattleTeam team1, BattleTeam team2) {
team1.Initial(1);
team2.Initial(2);
team1.Initial();
team2.Initial();
team1.Settings(1, 1);
team2.Settings(2, 1);
battleQueue.Add(team1.battles);
battleQueue.Add(team2.battles);
@@ -28,27 +33,16 @@ public class BattleSimulator {
dictionary.Add(PhaseType., new PhaseAssault(this));
dictionary.Add(PhaseType., new PhaseFormal(this));
dictionary.Add(PhaseType., new PhaseAction(this));
dictionary.Add(PhaseType., new PhaseActionRoleSelect(this));
dictionary.Add(PhaseType., new PhaseActionRoleAttack(this));
dictionary.Add(PhaseType., new PhaseFinish(this));
Transition(PhaseType.);
}
public void Update() {
currentPhase?.UpdatePhase();
}
/// <summary> 阶段过渡 </summary>
public void Transition(PhaseType phaseType) {
// 检查阶段字典中是否存在指定的阶段类型
if (dictionary.TryGetValue(phaseType, out IPhase newPhase)) {
// 如果存在则更新当前阶段
currentPhase?.QuitPhase();
if (!dictionary.TryGetValue(phaseType, out IPhase newPhase)) { return; }
currentPhase = newPhase;
currentPhase?.StartPhase();
// Debug.Log($"战斗阶段已转换为: {phaseType}");
}
else {
// 不存在时输出警告信息
Debug.LogWarning($"阶段字典中不存在 {phaseType} 对应的阶段实现");
}
currentPhase?.Execute();
}
}
@@ -0,0 +1,10 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 战斗技能
/// </summary>
public class BattleSkill {
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f4f5e0db8e96cfd4d8d9243f4057f99a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+14 -4
View File
@@ -11,7 +11,7 @@ public class BattleTeam {
/// <summary> 原始数据 </summary>
public List<DataCharacter> characters = new List<DataCharacter>();
/// <summary> 战斗数据 </summary>
public List<BattleCharacter> battles = new List<BattleCharacter>();
public List<DataCombatRole> battles = new List<DataCombatRole>();
/// <summary> 添加角色 </summary>
public void Add(List<DataCharacter> list) {
@@ -26,8 +26,18 @@ public class BattleTeam {
characters.Remove(character);
}
/// <summary> 战斗初始化 </summary>
public void Initial(int id) {
characters.ForEach(obj => battles.Add(new BattleCharacter(obj, id)));
/// <summary> 初始化 </summary>
public void Initial() {
characters.ForEach(Initial);
}
/// <summary> 初始化 </summary>
public void Initial(DataCharacter character) {
DataCombatRole role = new DataCombatRole(character);
role.Initial();
battles.Add(role);
}
/// <summary> 战斗编队 </summary>
public void Settings(int team, int position) {
battles.ForEach(obj => obj.Settings(team, position));
}
}
@@ -15,6 +15,10 @@ public static class CharacterDictionary {
character.profession = ProfessionTool.Wizard();
character.profession.Initial(character);
character.equipment = new DataEquipment();
// 装备法杖
character.equipment.Wear(WeaponDictionary.Weapon302(), out DataWear old1);
// 装备法袍
character.equipment.Wear(ArmorDictionary.Armor101(), out DataWear old2);
return character;
}
/// <summary> 托尔吉 兽人 战士 </summary>
@@ -26,6 +30,11 @@ public static class CharacterDictionary {
character.profession = ProfessionTool.Warrior();
character.profession.Initial(character);
character.equipment = new DataEquipment();
// 装备木棒和木盾
character.equipment.Wear(WeaponDictionary.Weapon201(), out DataWear old1);
character.equipment.Wear(WeaponDictionary.Weapon401(), out DataWear old2);
// 装备板甲
character.equipment.Wear(ArmorDictionary.Armor401(), out DataWear old3);
return character;
}
/// <summary> 格伦布林 矮人 牧师 </summary>
@@ -37,6 +46,11 @@ public static class CharacterDictionary {
character.profession = ProfessionTool.Cleric();
character.profession.Initial(character);
character.equipment = new DataEquipment();
// 装备木棒和木盾
character.equipment.Wear(WeaponDictionary.Weapon201(), out DataWear old1);
character.equipment.Wear(WeaponDictionary.Weapon401(), out DataWear old2);
// 装备链甲
character.equipment.Wear(ArmorDictionary.Armor301(), out DataWear old3);
return character;
}
}
@@ -0,0 +1,28 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 战斗角色 - 工具
/// </summary>
public static class CombatRoleTool {
#region
/// <summary> 初始角色 </summary>
public static void Initial(this DataCombatRole role) {
AttributeTool.Cover(role, role.character);
role.name = role.character.name;
role.level = role.character.Level;
role.hitPoint = new Vector2Int(role.character.HitPoint, role.character.HitPoint);
role.armorClass = role.character.ArmorClass;
role.weapon1 = role.character.equipment.weapon1;
role.weapon2 = role.character.equipment.weapon2;
}
/// <summary> 设置队伍 </summary>
public static void Settings(this DataCombatRole role, int team, int position) {
role.team = team;
role.position = position;
}
#endregion
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2eb2cecc8838b1445a24dce7aa669bd3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -6,7 +6,7 @@ using UnityEngine;
/// 装备 - 字典
/// </summary>
public static class ArmorDictionary {
/// <summary> 武器 </summary>
/// <summary> 护甲 </summary>
public static DataArmor Armor(string name, ArmorType armorType) {
DataArmor armor = new DataArmor();
armor.name = name;
@@ -22,28 +22,50 @@ public static class WeaponDictionary {
addition.damageDices.Add(dice);
return addition;
}
/// <summary> 匕首 1d4 </summary>
public static DataWeapon Weapon101() {
DataWeapon weapon = Weapon("匕首", WeaponType.);
weapon.additions.Add(DamageDice(4, DamageType.穿));
return weapon;
}
/// <summary> 短弓 1d6 </summary>
public static DataWeapon Weapon102() {
DataWeapon weapon = Weapon("短弓", WeaponType.);
weapon.additions.Add(DamageDice(6, DamageType.穿));
return weapon;
}
/// <summary> 木棒 1d6 </summary>
public static DataWeapon Weapon201() {
DataWeapon weapon = Weapon("木棒", WeaponType.);
weapon.additions.Add(DamageDice(6, DamageType.));
return weapon;
}
/// <summary> 巨棒 1d8 </summary>
public static DataWeapon Weapon301() {
DataWeapon weapon = Weapon("巨棒", WeaponType.);
weapon.additions.Add(DamageDice(8, DamageType.));
return weapon;
}
/// <summary> 法杖 1d6 </summary>
public static DataWeapon Weapon302() {
DataWeapon weapon = Weapon("法杖", WeaponType.);
weapon.additions.Add(DamageDice(6, DamageType.));
return weapon;
}
/// <summary> 木盾 ac1 </summary>
public static DataWeapon Weapon401() {
DataWeapon weapon = Weapon("木盾", WeaponType.);
weapon.additions.Add(new DataAddition { armorClass = 1 });
return weapon;
}
/// <summary> 箭袋 +20 </summary>
public static DataWeapon Weapon402() {
DataWeapon weapon = Weapon("箭袋", WeaponType.);
weapon.additions.Add(new DataAddition { armorClass = 1 });
return weapon;
}
}
@@ -19,20 +19,16 @@ public class ManagerSimulator : ModuleSingle<ManagerSimulator> {
private void Start() {
team1 = new BattleTeam();
team1.Add(RandomCharacter("艾薇拉"));
team1.Add(RandomCharacter("托尔吉"));
team1.Add(CharacterDictionary.Character001());
team1.Add(CharacterDictionary.Character002());
team1.Add(CharacterDictionary.Character003());
team2 = new BattleTeam();
team2.Add(RandomCharacter("哥布林射手"));
team2.Add(RandomCharacter("哥布林战士"));
team2.Add(MonsterDictionary.Monster001());
team2.Add(MonsterDictionary.Monster002());
team2.Add(MonsterDictionary.Monster002());
battleSimulator = new BattleSimulator(team1, team2);
}
private void Update() {
battleSimulator.Update();
}
private DataCharacter RandomCharacter(string name) {
return CharacterTool.Create(name, RaceTool.Random(), ProfessionTool.Random());
battleSimulator.Transition(PhaseType.);
}
}