init basic signal player chinese chess game

This commit is contained in:
ZZY
2024-06-15 18:07:20 +08:00
commit 9ba2e1af6b
71 changed files with 2752 additions and 0 deletions

View File

@ -0,0 +1,100 @@
// Chessboard.cs
using Godot;
public partial class ChessBoard : Node2D {
VirtualBoard Board = null;
MoveRecords Records = null;
// Called when the node enters the scene tree for the first time.
public Vector2 selectedNodePos = Vector2.Inf;
public override void _Ready() {
Board = new VirtualBoard(this as Node);
Records = new MoveRecords(onUndoRecordCallback: (newNode, oldNode, newPos, oldPos) => {
GD.Print("Undo: ", newNode, "->", newNode, ":", newPos, "->", oldPos);
ChessPiece newPiece = newNode as ChessPiece;
ChessPiece oldPiece = oldNode as ChessPiece;
Board.MoveNode(oldPos, newPos);
if (newPiece != null) {
Board.InsertNode(newPiece, newPos);
}
// if (oldPiece != null) {
// Board.RemoveNode(oldPos);
// }
});
Board.InitChessBoard();
// this.AddChild();
}
public override void _Input(InputEvent @event) {
if (@event is InputEventMouseButton mouseEvent &&
mouseEvent.Pressed &&
mouseEvent.ButtonIndex == MouseButton.Left) {
// HandleMouseClick(GetGlobalTransformWithCanvas().AffineInverse() * mouseButton.Position);
// HandleMouseClick(GetLocalMousePosition());
ActionPos(
(PosTrans.transArrToPix.AffineInverse() *
GetLocalMousePosition()).Round()
);
}
}
public void ActionPos(Vector2 clickArrPos) {
if (VirtualBoard.ArrPosOutOfRange(clickArrPos)) return;
GD.Print($"{clickArrPos} mouse clicked");
ChessPiece NowChess = Board.getNodeFromBoard(clickArrPos) as ChessPiece;
ChessPiece SelChess = Board.getNodeFromBoard(selectedNodePos) as ChessPiece;
if (Vector2.Inf.Equals(selectedNodePos)) {
if (NowChess == null) {
return;
}
NowChess.Selected();
GD.Print($"{clickArrPos} is selected");
selectedNodePos = clickArrPos;
} else if (selectedNodePos == clickArrPos) {
if (SelChess != null) {
SelChess.DeSelected();
}
GD.Print($"{selectedNodePos} is deselected");
selectedNodePos = Vector2.Inf;
} else {
GD.Print($"{selectedNodePos} move to {clickArrPos}");
if (SelChess != null) {
SelChess.DeSelected();
GD.Print($"{selectedNodePos} is deselected");
}
Node NowNode;
if (NowChess != null) {
NowNode = NowChess.Duplicate();
Board.RemoveNode(clickArrPos);
} else {
NowNode = NowChess as Node;
}
Records.AddRecord(NowNode, SelChess, clickArrPos, selectedNodePos);
Board.MoveNode(clickArrPos, selectedNodePos);
selectedNodePos = Vector2.Inf;
// GD.Print($"End: {selectedNodePos} {SelChess} move to {clickArrPos} {NowChess}");
}
}
public void undo() {
Records.Undo();
}
public void redo() {
Records.Clear();
Board.Clear();
Board.InitChessBoard();
}
private void HandleMouseClick(Vector2 clickPosition) {
// 如果有棋子被选中,则进行后续操作
// if (nowSelectedChess == null) {
// seletedNode = null;
// } else if (nowSelectedChess != null && seletedNode != null) {
// if (seletedNode is ChessPiece chessPiece) MoveChess(arrayPos, chessPiece.arrPos);
// }
}
}

View File

@ -0,0 +1,77 @@
// Chesspiece.cs
using Godot;
public partial class ChessPiece : Sprite2D {
// 文字内容
[Export]
public string PieceLabel { get; set; } = null;
// 文字颜色(可导出以编辑器调整)
[Export]
public Color LabelColor { get; set; } = new Color("black");
private Vector2 textureSize;
private Label labelOfChessName;
private Vector2 arrPos = new Vector2(); // 注意这个坐标的非像素坐标而是棋盘坐标
public void movePos(Vector2 newPos) {
this.arrPos = newPos;
this.Position = PosTrans.transArrToPix * newPos;
}
public Vector2 getPos() {
return arrPos;
}
// 是否被选中
public bool isSelected = false;
Transform2D transToSeleted = new Transform2D(
new Vector2(1.2f, 0),
new Vector2(0, 1.2f),
new Vector2(0, 0)
);
public void Selected() {
if (isSelected) {
return;
}
this.Transform *= transToSeleted;
this.isSelected = true;
}
public void DeSelected() {
if (!isSelected) {
return;
}
this.Transform *= transToSeleted.AffineInverse();
this.isSelected = false;
}
// Called when the node enters the scene tree for the first time.
public override void _Ready() {
InitLabel();
}
private void InitLabel() {
// this.Texture.ResourcePath = "res://Asserts/ChesspieceBase.tres";
this.Texture = (Texture2D)ResourceLoader.Load("res://Asserts/ChesspieceBase.tres");
textureSize = this.Texture.GetSize();
Vector2 labalPosition = new(
-textureSize.X / 2,
-textureSize.Y / 2);
labelOfChessName = new Label {
Text = PieceLabel,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Modulate = LabelColor,
Position = labalPosition,
Size = textureSize,
};
// labelOfChessName.SetAnchorsPreset(Control.LayoutPreset.FullRect);
AddChild(labelOfChessName);
}
interface IVarify {
bool CanMove(Vector2 newPosition);
}
}

View File

@ -0,0 +1,20 @@
// 帅/将 (General) - 代表双方的最高统帅。
// 子类名ChessGeneral
// 仕/士 (Advisor) - 保护帅/将的近身侍卫。
// 子类名ChessAdvisor
// 相/象 (Elephant) - 行动受限,走田字格,不能过河。
// 子类名ChessElephant
// 車/车 (Chariot) - 横竖移动,威力巨大。
// 子类名ChessChariot
// 馬/马 (Horse) - 走日字形,跳跃式移动。
// 子类名ChessHorse
// 砲/炮 (Cannon) - 需要隔子才能吃子,直线移动。
// 子类名ChessCannon
// 兵/卒 (Pawn) - 最基础的棋子,过河后可横移。
// 子类名ChessPawn

View File

@ -0,0 +1,57 @@
using System.Collections.Generic;
using System;
using Godot;
using System.Linq;
public class MoveRecords {
private readonly LinkedList<MoveRecord> records = new LinkedList<MoveRecord>(); // 使用队列替换栈
private readonly int maxRecords; // 记录上限
private Action<Node, Node, Vector2, Vector2> onAddRecordCallback; // 添加记录时的回调
private Action<Node, Node, Vector2, Vector2> onUndoRecordCallback; // 撤销记录时的回调
public MoveRecords(
Action<Node, Node, Vector2, Vector2> onAddRecordCallback = null,
Action<Node, Node, Vector2, Vector2> onUndoRecordCallback = null,
int maxRecords = 32) {
this.maxRecords = maxRecords;
this.onAddRecordCallback = onAddRecordCallback;
this.onUndoRecordCallback = onUndoRecordCallback;
}
public void AddRecord(Node newNode, Node oldNode, Vector2 newPos, Vector2 oldPos) {
// 达到记录上限时,移除最远的记录(队首元素)
if (records.Count >= maxRecords) {
records.RemoveFirst();
}
var record = new MoveRecord(newNode, oldNode, newPos, oldPos);
// 触发添加记录的回调
onAddRecordCallback?.Invoke(newNode, oldNode, newPos, oldPos);
// GD.Print("In func Addrecord: ", record.NewNode, "->", record.OldNode, ":", record.NewPos, "->", record.OldPos);
records.AddLast(record); // 将新记录加入队尾
}
public void Undo() {
if (records.Count == 0) return;
MoveRecord record = records.Last.Value; // 移除并获取队首的记录以执行撤销操作
records.RemoveLast();
// 触发撤销记录的回调
onUndoRecordCallback?.Invoke(record.NewNode, record.OldNode, record.NewPos, record.OldPos);
}
public void Clear() {
records.Clear();
}
private class MoveRecord {
public Node NewNode { get; }
public Node OldNode { get; }
public Vector2 NewPos { get; }
public Vector2 OldPos { get; }
public MoveRecord(Node newNode, Node oldNode, Vector2 newPos, Vector2 oldPos) {
this.NewNode = newNode;
this.OldNode = oldNode;
this.OldPos = oldPos;
this.NewPos = newPos;
}
}
}

View File

@ -0,0 +1,10 @@
using Godot;
public static class PosTrans {
private static readonly int pixGripSize = 32;
public static Transform2D transArrToPix = new(
new Vector2(pixGripSize, 0),
new Vector2(0, pixGripSize),
new Vector2(-4, -4.5f) * pixGripSize
);
}

View File

@ -0,0 +1,316 @@
using System.Dynamic;
using Godot;
class VirtualBoard {
private static readonly int boradRows = 9;
private static readonly int boradCols = 10;
private readonly Node BoardRoot;
private readonly Node[,] nodesBorad = new Node[boradRows, boradCols];
public static bool ArrPosOutOfRange(Vector2 arrayPos) {
return arrayPos.X < 0 || arrayPos.X >= boradRows || arrayPos.Y < 0 || arrayPos.Y >= boradCols;
}
public Node getNodeFromBoard(Vector2 arrayPos) {
if (ArrPosOutOfRange(arrayPos)) return null;
return nodesBorad[(int)arrayPos.X, (int)arrayPos.Y];
}
public void setNodeToBoard(Vector2 arrayPos, Node node) {
if (ArrPosOutOfRange(arrayPos)) return;
nodesBorad[(int)arrayPos.X, (int)arrayPos.Y] = node;
}
public VirtualBoard(Node root = null) {
//?? //GetTree().CurrentScene; // 如果未提供root默认为当前场景
BoardRoot = root;
}
public bool MoveNode(Vector2 newPos, Vector2 oldPos) {
if (ArrPosOutOfRange(newPos) || ArrPosOutOfRange(oldPos)) {
return false;
}
if (getNodeFromBoard(newPos) != null) {
return false;
}
if (getNodeFromBoard(oldPos) is ChessPiece chessPiece) {
chessPiece.movePos(newPos);
}
setNodeToBoard(newPos, getNodeFromBoard(oldPos));
setNodeToBoard(oldPos, null);
return true;
}
public bool RemoveNode(Vector2 arrayPos) {
if (ArrPosOutOfRange(arrayPos)) return false;
if (getNodeFromBoard(arrayPos) is ChessPiece chessPiece) {
chessPiece.QueueFree();
}
setNodeToBoard(arrayPos, null);
return true;
}
public void InsertNode(ChessPiece chess, Vector2 arrayPos) {
chess.Position = PosTrans.transArrToPix * arrayPos;
setNodeToBoard(arrayPos, chess);
BoardRoot.AddChild(chess);
}
public void Clear() {
foreach (var node in BoardRoot.GetChildren()) {
if (node is ChessPiece chessPiece) {
chessPiece.QueueFree();
}
}
}
// using Godot;
// public enum ChessType
// {
// Carriage,
// Horse,
// Elephant,
// Advisor,
// General,
// Cannon,
// Pawn
// }
// public class VirtualBoard
// {
// // ...其他原有成员变量...
// public VirtualBoard(Node root = null)
// {
// BoardRoot = root ?? GetTree().CurrentScene; // 如果未提供root默认为当前场景
// }
// public void InitChessBoard()
// {
// // 定义黑色和红色棋子的初始位置和类型
// var positions = new (Vector2 position, ChessType type)[]
// {
// // 黑方棋子初始化...
// // 示例省略具体位置和类型,你需要根据实际情况填写
// // (new Vector2(x, y), ChessType.Pawn),
// // 红方棋子初始化...
// // 同上
// };
// // 初始化棋子
// foreach (var (position, type) in positions)
// {
// var color = position.Y == 0 ? new Color("black") : new Color("red"); // 根据行判断颜色
// var piece = CreateChessPiece(type, color);
// InsertChess(piece, position);
// }
// }
// private ChessPiece CreateChessPiece(ChessType type, Color labelColor)
// {
// // 根据ChessType创建对应的棋子实例并设置Label和颜色
// // 这里需要你实现具体的逻辑例如switch case或映射表来决定创建哪种棋子
// // 返回创建的棋子实例
// }
// // ...其他原有方法...
// }
public void InitChessBoard() {
InsertNode(new ChessPiece
{
PieceLabel = "车",
LabelColor = new Color("black"),
}, new Vector2(0, 0));
InsertNode(new ChessPiece
{
PieceLabel = "马",
LabelColor = new Color("black"),
}, new Vector2(1, 0));
InsertNode(new ChessPiece
{
PieceLabel = "象",
LabelColor = new Color("black"),
}, new Vector2(2, 0));
InsertNode(new ChessPiece
{
PieceLabel = "士",
LabelColor = new Color("black"),
}, new Vector2(3, 0));
InsertNode(new ChessPiece
{
PieceLabel = "将",
LabelColor = new Color("black"),
}, new Vector2(4, 0));
InsertNode(new ChessPiece
{
PieceLabel = "士",
LabelColor = new Color("black"),
}, new Vector2(5, 0));
InsertNode(new ChessPiece
{
PieceLabel = "象",
LabelColor = new Color("black"),
}, new Vector2(6, 0));
InsertNode(new ChessPiece
{
PieceLabel = "马",
LabelColor = new Color("black"),
}, new Vector2(7, 0));
InsertNode(new ChessPiece
{
PieceLabel = "车",
LabelColor = new Color("black"),
}, new Vector2(8, 0));
InsertNode(new ChessPiece
{
PieceLabel = "炮",
LabelColor = new Color("black"),
}, new Vector2(1, 2));
InsertNode(new ChessPiece
{
PieceLabel = "炮",
LabelColor = new Color("black"),
}, new Vector2(7, 2));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("black"),
}, new Vector2(0, 3));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("black"),
}, new Vector2(2, 3));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("black"),
}, new Vector2(4, 3));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("black"),
}, new Vector2(6, 3));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("black"),
}, new Vector2(8, 3));
// -----------------------------------
InsertNode(new ChessPiece
{
PieceLabel = "车",
LabelColor = new Color("red"),
}, new Vector2(0, 9));
InsertNode(new ChessPiece
{
PieceLabel = "马",
LabelColor = new Color("red"),
}, new Vector2(1, 9));
InsertNode(new ChessPiece
{
PieceLabel = "象",
LabelColor = new Color("red"),
}, new Vector2(2, 9));
InsertNode(new ChessPiece
{
PieceLabel = "士",
LabelColor = new Color("red"),
}, new Vector2(3, 9));
InsertNode(new ChessPiece
{
PieceLabel = "将",
LabelColor = new Color("red"),
}, new Vector2(4, 9));
InsertNode(new ChessPiece
{
PieceLabel = "士",
LabelColor = new Color("red"),
}, new Vector2(5, 9));
InsertNode(new ChessPiece
{
PieceLabel = "象",
LabelColor = new Color("red"),
}, new Vector2(6, 9));
InsertNode(new ChessPiece
{
PieceLabel = "马",
LabelColor = new Color("red"),
}, new Vector2(7, 9));
InsertNode(new ChessPiece
{
PieceLabel = "车",
LabelColor = new Color("red"),
}, new Vector2(8, 9));
InsertNode(new ChessPiece
{
PieceLabel = "炮",
LabelColor = new Color("red"),
}, new Vector2(1, 7));
InsertNode(new ChessPiece
{
PieceLabel = "炮",
LabelColor = new Color("red"),
}, new Vector2(7, 7));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("red"),
}, new Vector2(0, 6));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("red"),
}, new Vector2(2, 6));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("red"),
}, new Vector2(4, 6));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("red"),
}, new Vector2(6, 6));
InsertNode(new ChessPiece
{
PieceLabel = "卒",
LabelColor = new Color("red"),
}, new Vector2(8, 6));
}
}