Skip to content

Commit d7af24c

Browse files
committed
firsttry
1 parent 55ec088 commit d7af24c

19 files changed

Lines changed: 1885 additions & 38 deletions

logic/Gaming/ActionManager.cs

Lines changed: 133 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,139 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Text;
5-
using System.Threading.Tasks;
2+
using System.Collections.Concurrent;
3+
using System.Threading;
4+
using GameClass.GameObj;
5+
using GameClass.GameObj.Areas;
6+
using Preparation.Utility;
67

7-
namespace logic.Gaming
8+
namespace Game
89
{
9-
internal class ActionManager
10+
public partial class Game
1011
{
12+
private readonly ActionManager actionManager;
13+
14+
private sealed class ActionManager
15+
{
16+
private readonly Game game;
17+
private readonly ConcurrentDictionary<long, CancellationTokenSource> occupying = new(); // key: ComputeCenter.ID
18+
private readonly ConcurrentDictionary<(long playerId, long resourceId), CancellationTokenSource> harvesting = new();
19+
20+
public ActionManager(Game game)
21+
{
22+
this.game = game;
23+
}
24+
25+
public bool Harvest(long playerId, Resource resource, long durationMs)
26+
{
27+
if (resource == null) return false;
28+
if (!game.characterManager.TryGetCharacter(playerId, out var ch)) return false;
29+
// 仅当在资源点周围 3x3 格内可采
30+
if (!GameData.ApproachToInteract(ch.Position, resource.Position)) return false;
31+
32+
// 仅汽车可采集(每秒10单位),其他单位暂不支持
33+
int ratePerSec = ch.CharacterType == CharacterType.AUTONOMOUS_CAR ? 10 : 0;
34+
if (ratePerSec <= 0) return false;
35+
36+
var key = (playerId, resource.ID);
37+
if (harvesting.ContainsKey(key)) return false; // 已有进行中的采集
38+
39+
var cts = new CancellationTokenSource();
40+
if (!harvesting.TryAdd(key, cts)) { cts.Dispose(); return false; }
41+
42+
new Thread(() =>
43+
{
44+
try
45+
{
46+
int elapsed = 0;
47+
int step = 200; // 200ms tick
48+
while (!cts.IsCancellationRequested && elapsed < durationMs)
49+
{
50+
if (!game.Map.Timer.IsGaming) { Thread.Sleep(step); continue; }
51+
// 位置保持在 3x3 范围内,否则中断
52+
if (!GameData.ApproachToInteract(ch.Position, resource.Position)) break;
53+
54+
// 扣减资源:ratePerSec * step/1000
55+
long delta = (long)Math.Max(1, ratePerSec * step / 1000.0);
56+
resource.HP.SubPositiveV(delta);
57+
58+
// 资源耗尽:转为障碍(保留方块、Rigid=true,状态设为 HARVESTED),即形成阻挡
59+
if (resource.HP.GetValue() == 0)
60+
{
61+
resource.SetERState(ResourceState.HARVESTED);
62+
break;
63+
}
64+
65+
Thread.Sleep(step);
66+
elapsed += step;
67+
}
68+
}
69+
finally
70+
{
71+
harvesting.TryRemove(key, out var oldCts);
72+
oldCts?.Dispose();
73+
}
74+
})
75+
{ IsBackground = true }.Start();
76+
77+
return true;
78+
}
79+
80+
public bool Occupy(long playerId, ComputeCenter center)
81+
{
82+
if (center == null) return false;
83+
if (!game.characterManager.TryGetCharacter(playerId, out var ch)) return false;
84+
// 仅无人机/机器人可占领
85+
if (ch.CharacterType != CharacterType.DRONE && ch.CharacterType != CharacterType.ROBOT) return false;
86+
87+
// 已在占领
88+
if (occupying.ContainsKey(center.ID)) return false;
89+
// 需在中心范围内(使用 1 格邻近判定)
90+
if (!GameData.ApproachToInteract(ch.Position, center.Position)) return false;
91+
92+
var cts = new CancellationTokenSource();
93+
if (!occupying.TryAdd(center.ID, cts)) { cts.Dispose(); return false; }
94+
95+
new Thread(() =>
96+
{
97+
try
98+
{
99+
int occupyTimeMs = 10_000;
100+
int elapsed = 0;
101+
int tick = 200; // 200ms 检查
102+
while (!cts.IsCancellationRequested && elapsed < occupyTimeMs)
103+
{
104+
if (!game.Map.Timer.IsGaming) { Thread.Sleep(tick); continue; }
105+
// 离开范围则打断
106+
if (!GameData.ApproachToInteract(ch.Position, center.Position)) return;
107+
Thread.Sleep(tick);
108+
elapsed += tick;
109+
}
110+
if (elapsed >= occupyTimeMs && !cts.IsCancellationRequested)
111+
{
112+
center.SetOccupied(ch.PlayerID.Get());
113+
}
114+
}
115+
finally
116+
{
117+
occupying.TryRemove(center.ID, out var oldCts);
118+
oldCts?.Dispose();
119+
}
120+
})
121+
{ IsBackground = true }.Start();
122+
123+
return true;
124+
}
125+
126+
public bool AddGoods(long playerId, GoodsType type, int delta)
127+
{
128+
if (!game.characterManager.TryGetCharacter(playerId, out var ch)) return false;
129+
return ch.GoodsLoad.Add(type, delta);
130+
}
131+
132+
public bool SetGoods(long playerId, GoodsType type, int value)
133+
{
134+
if (!game.characterManager.TryGetCharacter(playerId, out var ch)) return false;
135+
return ch.GoodsLoad.Set(type, value);
136+
}
137+
}
11138
}
12139
}

logic/Gaming/AttackManager.cs

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,46 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Text;
5-
using System.Threading.Tasks;
2+
using GameClass.GameObj;
3+
using Preparation.Utility;
64

7-
namespace logic.Gaming
5+
namespace Game
86
{
9-
internal class AttackManager
7+
public partial class Game
108
{
9+
private readonly AttackManager attackManager;
10+
11+
private sealed class AttackManager
12+
{
13+
private readonly Game game;
14+
15+
public AttackManager(Game game)
16+
{
17+
this.game = game;
18+
}
19+
20+
public bool Attack(long attackerPlayerId, long targetPlayerId)
21+
{
22+
if (!game.characterManager.TryGetCharacter(attackerPlayerId, out var attacker)) return false;
23+
if (!game.characterManager.TryGetCharacter(targetPlayerId, out var target)) return false;
24+
if (attacker.TeamID.Get() == target.TeamID.Get()) return false;
25+
long damage = attacker.AttackPower.GetValue();
26+
target.HP.SubPositiveV(damage);
27+
if (target.HP.GetValue() == 0)
28+
{
29+
game.characterManager.Destroy(targetPlayerId, CharacterState.NULL_CHARACTER_STATE);
30+
}
31+
return true;
32+
}
33+
34+
public bool BeAttacked(long targetPlayerId, long damage)
35+
{
36+
if (!game.characterManager.TryGetCharacter(targetPlayerId, out var target)) return false;
37+
target.HP.SubPositiveV(damage);
38+
if (target.HP.GetValue() == 0)
39+
{
40+
game.characterManager.Destroy(targetPlayerId, CharacterState.NULL_CHARACTER_STATE);
41+
}
42+
return true;
43+
}
44+
}
1145
}
1246
}

logic/Gaming/CharacterManager.cs

Lines changed: 96 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,104 @@
11
using System;
2+
using System.Collections.Concurrent;
23
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Text;
5-
using System.Threading.Tasks;
4+
using GameClass.GameObj;
5+
using GameClass.GameObj.Areas;
6+
using GameEngine;
7+
using Preparation.Interface;
8+
using Preparation.Utility;
9+
using Preparation.Utility.Value;
610

7-
namespace logic.Gaming
11+
namespace Game
812
{
9-
internal class CharacterManager
13+
public partial class Game
1014
{
15+
private readonly CharacterManager characterManager;
1116

17+
private sealed class CharacterManager
18+
{
19+
private readonly Game game;
20+
private readonly MoveEngine moveEngine;
21+
// 索引1:按玩家ID检索
22+
private readonly ConcurrentDictionary<long, Character> characters = new(); // key: PlayerID
23+
// 索引2:按队伍ID分组
24+
private readonly ConcurrentDictionary<long, ConcurrentDictionary<long, Character>> teamCharacters = new(); // key: TeamID -> (PlayerID -> Character)
25+
26+
public CharacterManager(Game game)
27+
{
28+
this.game = game;
29+
moveEngine = new MoveEngine(
30+
game.Map,
31+
OnCollision,
32+
EndMove
33+
);
34+
}
35+
36+
public Character CreateCharacter(long teamId, long playerId, CharacterType type)
37+
{
38+
var ch = new Character(GameData.CharacterRadius, type);
39+
ch.TeamID.SetROri(teamId);
40+
ch.PlayerID.SetROri(playerId);
41+
characters[playerId] = ch;
42+
var teamDict = teamCharacters.GetOrAdd(teamId, _ => new ConcurrentDictionary<long, Character>());
43+
teamDict[playerId] = ch;
44+
return ch;
45+
}
46+
47+
public bool ActivateCharacter(long playerId, XY pos)
48+
{
49+
if (!characters.TryGetValue(playerId, out var ch)) return false;
50+
ch.IsRemoved.SetROri(false);
51+
ch.CanMove.SetROri(true);
52+
ch.ReSetPos(pos);
53+
ch.SetCharacterState(CharacterState.IDLE);
54+
return true;
55+
}
56+
57+
public bool Move(long playerId, int timeMs, double direction, long shoes = 0)
58+
{
59+
if (!characters.TryGetValue(playerId, out var ch)) return false;
60+
if (!ch.CanMove || ch.IsRemoved) return false;
61+
var state = ch.StateNum;
62+
moveEngine.MoveObj(ch, timeMs, direction, state, shoes);
63+
return true;
64+
}
65+
66+
public bool TryGetCharacter(long playerId, out Character character)
67+
=> characters.TryGetValue(playerId, out character!);
68+
69+
public IReadOnlyCollection<Character> GetTeamCharacters(long teamId)
70+
{
71+
if (teamCharacters.TryGetValue(teamId, out var dict))
72+
return dict.Values;
73+
return Array.Empty<Character>();
74+
}
75+
76+
public bool Destroy(long playerId, CharacterState state = CharacterState.NULL_CHARACTER_STATE)
77+
{
78+
if (!characters.TryGetValue(playerId, out var ch)) return false;
79+
if (!ch.TryToRemoveFromGame(state)) return false;
80+
ch.CanMove.SetROri(false);
81+
characters.TryRemove(playerId, out _);
82+
var teamId = ch.TeamID.Get();
83+
if (teamCharacters.TryGetValue(teamId, out var dict))
84+
{
85+
dict.TryRemove(playerId, out _);
86+
}
87+
return true;
88+
}
89+
90+
private MoveEngine.AfterCollision OnCollision(IMovable mover, IGameObj target, XY moveVec)
91+
{
92+
// 默认尽量移动到可行的最远距离;必要时可调用 game 的其他管理器决定逻辑。
93+
return MoveEngine.AfterCollision.MoveMax;
94+
}
95+
96+
private void EndMove(IMovable mover)
97+
{
98+
if (mover is not Character ch) return;
99+
// 移动收尾:仅恢复状态。可见性在 GetSnapshot 按观察者动态计算。
100+
ch.SetCharacterState(CharacterState.IDLE);
101+
}
102+
}
12103
}
13104
}

0 commit comments

Comments
 (0)