feat: 支持多站位战斗控制绑定模式
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful

This commit is contained in:
xinian
2026-04-05 00:03:32 +08:00
committed by cnb
parent 2eba4b7915
commit f473c54880
4 changed files with 491 additions and 52 deletions

View File

@@ -0,0 +1,194 @@
# Fight Input 控制绑定说明
日期2026-04-04
## 1. 背景
当前战斗模型中一个 `Input` 对应一个战斗站位`actorIndex`
每个 `Input` 通过 `Input.Player` 绑定操作者
当前建战主路径已收敛为`WithFightInputs(ourInputs, oppInputs)`
先由调用方创建并组装双方 `Input`再传给战斗模块
为了同时支持以下两种玩法新增了可配置绑定策略
1. 双打一个玩家控制多个站位单人多 `Input`
2. 组队一个玩家控制一个站位每人一个 `Input`
## 2. 绑定策略
文件`logic/service/fight/new_options.go`
- `InputControllerBindingKeep`
- 含义保持输入中已有 `Input.Player` 绑定不覆盖
- 适用调用方已手动构造 `Input` 绑定
- `InputControllerBindingSingle`
- 含义单侧全部站位统一绑定为 `players[0]`
- 适用双打中一个人控制多个站位
- `InputControllerBindingPerSlot`
- 含义按站位顺序绑定为 `players[i]`
- 适用组队中一人一个站位
- 说明 `players` 数量不足时回退绑定 `players[0]`
## 3. 选项接口
文件`logic/service/fight/new_options.go`
新增选项
```go
WithInputControllerBinding(mode int)
```
## 4. 生效时机
文件`logic/service/fight/new.go`
`buildFight` 构建完 `Our/Opp` 输入后先执行控制绑定再执行上下文绑定
1. `bindInputControllers(f.Our, f.OurPlayers, opts.controllerBinding)`
2. `bindInputControllers(f.Opp, f.OppPlayers, opts.controllerBinding)`
3. `bindInputFightContext(...)`
4. `linkTeamViews()`
5. `linkOppInputs()`
## 5. 使用示例
### 5.1 双打单人控多站位
```go
fight.NewFightWithOptions(
fight.WithFightPlayersOnSide(
[]common.PlayerI{ourPlayer},
[]common.PlayerI{oppPlayer},
),
fight.WithFightInputs(ourInputs, oppInputs),
fight.WithInputControllerBinding(fight.InputControllerBindingSingle),
)
```
### 5.2 组队一人一个站位
```go
fight.NewFightWithOptions(
fight.WithFightPlayersOnSide(
[]common.PlayerI{ourP1, ourP2},
[]common.PlayerI{oppP1, oppP2},
),
fight.WithFightInputs(ourInputs, oppInputs),
fight.WithInputControllerBinding(fight.InputControllerBindingPerSlot),
)
```
### 5.3 仅传已绑定 Input推荐灵活接入
```go
ourInputs := []*input.Input{
input.NewInput(nil, ourP1), // 站位0
input.NewInput(nil, ourP2), // 站位1
}
oppInputs := []*input.Input{
input.NewInput(nil, oppP1), // 站位0
input.NewInput(nil, oppP2), // 站位1
}
fc, err := fight.NewFightWithOptions(
fight.WithFightInputs(ourInputs, oppInputs),
// 不传 WithFightPlayersOnSide 也可
// owner/opponent 与 side players 会从 inputs 自动提取
)
_ = fc
_ = err
```
说明`InputControllerBindingSingle/PerSlot` 会覆盖 `ourInputs/oppInputs` 中原有的 `Input.Player` 绑定`Keep` 不覆盖
## 6. 新模式绑定实例逐模式
以下示例假设我方有两个站位`ourInputs[0]``ourInputs[1]`
### 6.1 Keep保持输入原绑定
调用
```go
fight.NewFightWithOptions(
fight.WithFightInputs(ourInputs, oppInputs),
fight.WithInputControllerBinding(fight.InputControllerBindingKeep),
)
```
输入调用前
- `ourInputs[0].Player = ourP1`
- `ourInputs[1].Player = ourP2`
结果调用后
- `ourInputs[0].Player = ourP1`
- `ourInputs[1].Player = ourP2`
适用调用方已提前把每个站位绑定好不希望框架覆盖
### 6.2 Single单人控制全部站位
调用
```go
fight.NewFightWithOptions(
fight.WithFightPlayersOnSide(
[]common.PlayerI{ourCaptain},
[]common.PlayerI{oppCaptain},
),
fight.WithFightInputs(ourInputs, oppInputs),
fight.WithInputControllerBinding(fight.InputControllerBindingSingle),
)
```
输入调用前
- `ourInputs[0].Player = ourP1`
- `ourInputs[1].Player = ourP2`
结果调用后
- `ourInputs[0].Player = ourCaptain`
- `ourInputs[1].Player = ourCaptain`
适用双打或多站位由同一玩家操作
### 6.3 PerSlot按站位顺序绑定玩家
调用
```go
fight.NewFightWithOptions(
fight.WithFightPlayersOnSide(
[]common.PlayerI{ourP1, ourP2},
[]common.PlayerI{oppP1, oppP2},
),
fight.WithFightInputs(ourInputs, oppInputs),
fight.WithInputControllerBinding(fight.InputControllerBindingPerSlot),
)
```
输入调用前
- `ourInputs[0].Player = anyA`
- `ourInputs[1].Player = anyB`
结果调用后
- `ourInputs[0].Player = ourP1`
- `ourInputs[1].Player = ourP2`
补位规则 `players` 数量不足例如只传一个 `ourP1`剩余站位回退绑定 `players[0]`
## 7. 注意事项
1. 默认模式是 `InputControllerBindingKeep`不影响现有调用
2. 若传入 `WithFightInputs(...)` 且每个 `Input.Player` 已预先绑定可继续用默认模式
3. 仅传 `WithFightInputs(...)` 也可工作框架会从 `ourInputs/oppInputs` 自动提取 `ourPlayers/oppPlayers`并以各侧首位玩家作为 owner/opponent
4. 推荐在新组队逻辑中显式传 `WithInputControllerBinding(...)`避免调用方歧义