Y++ 社区

示例

Y++开发连连看游戏

示例 浏览 2 下载 0 管理员

Y++连连看游戏中文编程

Y++中文编程语言-Y语言

// 连连看:多造型排布(参考 QQ 连连看)+ 强特效 + 连击演出 + 可替换图块
// 图块:资源/图块/01.png … 24.png · 音效:资源/音效/*.wav
// 棋盘随难度变大:简单10×7 / 普通12×8 / 困难14×9(外圈空行仍保留)

整数 最大内W = 14
整数 最大内H = 9
整数 内W = 10
整数 内H = 7
整数 GW = 16
整数 GH = 11
整数 CELL = 44
整数 边 = 34
整数 边X = 34
整数 边Y = 34
整数 画W = 780
整数 画H = 580
整数 消毫秒 = 170
整数 线毫秒 = 220

整数 板[] = []
整数 AI板[] = []
逻辑 座[] = []
整数 布局号 = 0
文本 布局名 = "满盘"
// 副桌=真 时读格/写格/连通判定走 AI板(双桌竞速)
逻辑 副桌 = 假
整数 AI边X = 34
整数 AI边Y = 34
整数 绘边X = 34
整数 绘边Y = 34
整数 AI剩余 = 0

整数 选x = -1
整数 选y = -1
整数 分 = 0
整数 剩余 = 0
整数 连击 = 0
整数 最大连击 = 0
小数 上次消 = 0
逻辑 已就绪 = 假
逻辑 锁输入 = 假

整数 线x[] = []
整数 线y[] = []
整数 线点数 = 0
小数 线消时 = 0

逻辑 消中 = 假
整数 消x1 = 0
整数 消y1 = 0
整数 消x2 = 0
整数 消y2 = 0
整数 消种 = 0
小数 消起 = 0
整数 消连击 = 1

文本 连击字 = ""
小数 连击字消 = 0
整数 连击档 = 0
小数 闪屏消 = 0

逻辑 通关中 = 假
小数 通关起 = 0
文本 通关字 = ""
文本 通关副 = ""
// 0无 1通关/你赢 2失败(AI赢) 3平局 4时间到
整数 结局 = 0

整数 提示x1 = -1
整数 提示y1 = -1
整数 提示x2 = -1
整数 提示y2 = -1
小数 提示消 = 0
整数 提示次 = 0

逻辑 图有[] = []
整数 图加载数 = 0

// 0简单 1普通 2困难
整数 难度 = 1
整数 种数 = 18
整数 最大种数 = 24
整数 AI分 = 0
逻辑 AI开 = 假
小数 AI下次 = 0
整数 AI间隔 = 1600
整数 最大AI = 4
整数 AI数 = 0
整数 当前AI = 0
整数 AI分表[] = []
整数 AI剩表[] = []
整数 AI消对表[] = []
整数 AI连击表[] = []
小数 AI上次消表[] = []
整数 AIx1表[] = []
整数 AIy1表[] = []
整数 AIx2表[] = []
整数 AIy2表[] = []
逻辑 AI消中表[] = []
小数 AI消起表[] = []
整数 AI消x1表[] = []
整数 AI消y1表[] = []
整数 AI消x2表[] = []
整数 AI消y2表[] = []
整数 AI消种表[] = []
小数 AI下次表[] = []
整数 AI间隔表[] = []
整数 消对跳过 = 0
整数 玩家CELL = 44
整数 右起 = 0
整数 AI格 = 16
整数 AI列 = 1
整数 AI行 = 1
整数 AI格区宽 = 0
整数 AI格区高 = 0
// AI 瞄准鬼影(与玩家提示分离)
整数 AIx1 = -1
整数 AIy1 = -1
整数 AIx2 = -1
整数 AIy2 = -1
整数 AI瞄种 = 0
逻辑 AI消中 = 假
小数 AI消起 = 0
整数 AI消x1 = 0
整数 AI消y1 = 0
整数 AI消x2 = 0
整数 AI消y2 = 0
整数 AI消种 = 0
整数 玩家消对 = 0
整数 AI消对 = 0
整数 开局对数 = 0
// 每步倒计时(消一对后重置,超时失败)
整数 步限秒 = 28
整数 步剩秒 = 28
小数 步倒计时锚 = 0
整数 上次警告秒 = -1

// 联机:0单机 1房主 2加入
整数 联机角色 = 0
逻辑 联机连接中 = 假
整数 联机端口 = 17888
文本 联机IP = "127.0.0.1"
TCP类 房服
TCP类 房客
整数 客连接ID = -1

定时器类 钟
绘制类 绘
字体类 字连击
字体类 字通关大
字体类 字通关中
字体类 字通关小
字体类 字图块
字体类 字步时
字体类 字开场
逻辑 字体就绪 = 假
逻辑 难度同步中 = 假
逻辑 倒计时中 = 假
小数 倒计时起 = 0

函数 确保字体 () {
如果 (字体就绪) { 返回 () }
字连击.置名称("Microsoft YaHei UI")
字连击.置字号(30)
字连击.置粗体(真)
字通关大.置名称("Microsoft YaHei UI")
字通关大.置字号(42)
字通关大.置粗体(真)
字通关中.置名称("Microsoft YaHei UI")
字通关中.置字号(20)
字通关中.置粗体(真)
字通关小.置名称("Microsoft YaHei UI")
字通关小.置字号(15)
字通关小.置粗体(假)
字图块.置名称("Microsoft YaHei UI")
字图块.置字号(14)
字图块.置粗体(真)
字步时.置名称("Microsoft YaHei UI")
字步时.置字号(12)
字步时.置粗体(假)
字开场.置名称("Microsoft YaHei UI")
字开场.置字号(132)
字开场.置粗体(真)
字体就绪 = 真
}

函数 画描边字 (整数 x, 整数 y, 文本 内容, 文本 正色, 文本 描色) {
画布1.置填充(描色)
画布1.画文本(x - 2, y, 内容)
画布1.画文本(x + 2, y, 内容)
画布1.画文本(x, y - 2, 内容)
画布1.画文本(x, y + 2, 内容)
画布1.画文本(x - 1, y - 1, 内容)
画布1.画文本(x + 1, y - 1, 内容)
画布1.画文本(x - 1, y + 1, 内容)
画布1.画文本(x + 1, y + 1, 内容)
画布1.置填充(正色)
画布1.画文本(x, y, 内容)
}

函数 画开场大字 (整数 x, 整数 y, 文本 内容, 文本 正色, 文本 描色) {
// 加厚描边,开场数字 / GO 更醒目
画布1.置填充(描色)
整数 d = 0
循环 (5, d) {
整数 o = d + 1
画布1.画文本(x - o, y, 内容)
画布1.画文本(x + o, y, 内容)
画布1.画文本(x, y - o, 内容)
画布1.画文本(x, y + o, 内容)
画布1.画文本(x - o, y - o, 内容)
画布1.画文本(x + o, y - o, 内容)
画布1.画文本(x - o, y + o, 内容)
画布1.画文本(x + o, y + o, 内容)
}
画布1.置填充(正色)
画布1.画文本(x, y, 内容)
}

函数 开场阶段 (小数 now) 整数 {
// 0=3 1=2 2=1 3=GO 4=结束
小数 age = now - 倒计时起
如果 (age < 1000) { 返回 (0) }
如果 (age < 2000) { 返回 (1) }
如果 (age < 3000) { 返回 (2) }
如果 (age < 3400) { 返回 (3) }
返回 (4)
}

函数 画开场倒计时 (小数 now) {
如果 (倒计时中 == 假) { 返回 () }
确保字体()
整数 stage = 开场阶段(now)
如果 (stage >= 4) { 返回 () }
小数 age = now - 倒计时起
小数 段内 = age
如果 (stage == 1) { 段内 = age - 1000 }
如果 (stage == 2) { 段内 = age - 2000 }
如果 (stage == 3) { 段内 = age - 3000 }
整数 pulse = 到整数(段内) % 1000
如果 (pulse > 500) { pulse = 1000 - pulse }
整数 glow = 10 + pulse / 40

画布1.置笔触("#00FFFFFF", 0)
画布1.置填充("#99050A12")
画布1.画矩形(0, 0, 画W, 画H)

整数 cx = 画W / 2
整数 cy = 画H / 2
文本 正 = "#FFFFF176"
文本 描 = "#CC000000"
文本 词 = "3"
整数 ox = cx - 40
如果 (stage == 0) {
词 = "3"
正 = "#FFFF8A80"
ox = cx - 40
} 否则如果 (stage == 1) {
词 = "2"
正 = "#FFFFB74D"
ox = cx - 40
} 否则如果 (stage == 2) {
词 = "1"
正 = "#FFFFF176"
ox = cx - 40
} 否则 {
词 = "GO!"
正 = "#FF69F0AE"
ox = cx - 110
画布1.置填充("#3369F0AE")
画布1.画椭圆(cx - 130 - glow, cy - 90 - glow, 260 + glow 2, 180 + glow 2)
}
如果 (stage < 3) {
画布1.置填充("#33FFFFFF")
画布1.画椭圆(cx - 90 - glow, cy - 90 - glow, 180 + glow 2, 180 + glow 2)
}
画布1.置字体(字开场)
画开场大字(ox, cy - 90, 词, 正, 描)
画布1.置字体(字通关中)
画描边字(cx - 100, cy + 70, 模式名() + " · " + 难度名(), "#FFFFFFFF", "#88000000")
}

函数 下标 (整数 x, 整数 y) 整数 {
返回 (y * GW + x)
}

函数 座下标 (整数 x, 整数 y) 整数 {
// 固定按最大宽存,切换难度时座位含义不乱
返回 ((y - 1) * 最大内W + (x - 1))
}

函数 确保板 () {
当 (数组长度(板) < GW * GH) {
数组添加(板, 0)
}
当 (数组长度(AI板) < 最大AI GW GH) {
数组添加(AI板, 0)
}
当 (数组长度(座) < 最大内W * 最大内H) {
数组添加(座, 真)
}
当 (数组长度(AI分表) < 最大AI) {
数组添加(AI分表, 0)
数组添加(AI剩表, 0)
数组添加(AI消对表, 0)
数组添加(AI连击表, 0)
数组添加(AI上次消表, 0)
数组添加(AIx1表, -1)
数组添加(AIy1表, -1)
数组添加(AIx2表, -1)
数组添加(AIy2表, -1)
数组添加(AI消中表, 假)
数组添加(AI消起表, 0)
数组添加(AI消x1表, 0)
数组添加(AI消y1表, 0)
数组添加(AI消x2表, 0)
数组添加(AI消y2表, 0)
数组添加(AI消种表, 0)
数组添加(AI下次表, 0)
数组添加(AI间隔表, 1600)
}
}

函数 AI手速档 (整数 档) 整数 {
// 档 0最快 … 3最慢,再叠随机抖动
整数 base = AI间隔
如果 (档 == 0) { base = AI间隔 - 280 }
否则如果 (档 == 1) { base = AI间隔 - 40 }
否则如果 (档 == 2) { base = AI间隔 + 220 }
否则 { base = AI间隔 + 480 }
base = base + 随机数(0, 320) - 120
如果 (base < 420) { base = 420 }
如果 (base > 4200) { base = 4200 }
返回 (base)
}

函数 洗牌AI手速 () {
// 手速档随机分配,避免永远 AI1 最快
整数 档[] = []
整数 i = 0
当 (i < 最大AI) {
数组添加(档, i)
i = i + 1
}
i = 最大AI - 1
当 (i > 0) {
整数 j = 随机数(0, i)
整数 t = 档[i]
档[i] = 档[j]
档[j] = t
i = i - 1
}
i = 0
当 (i < AI数) {
AI间隔表[i] = AI手速档(档[i])
i = i + 1
}
}

函数 AI基址 () 整数 {
返回 (当前AI GW GH)
}

函数 读格 (整数 x, 整数 y) 整数 {
如果 (副桌) { 返回 (AI板[AI基址() + 下标(x, y)]) }
返回 (板[下标(x, y)])
}

函数 写格 (整数 x, 整数 y, 整数 v) {
如果 (副桌) {
AI板[AI基址() + 下标(x, y)] = v
} 否则 {
板[下标(x, y)] = v
}
}

函数 对局人数 () 整数 {
返回 (1 + AI数)
}

函数 连击得分 (整数 c) 整数 {
如果 (c < 1) { c = 1 }
整数 人 = 对局人数()
整数 加 = (10 + (c - 1) 8) 人
如果 (c >= 5) { 加 = 加 + 20 * 人 }
如果 (c >= 8) { 加 = 加 + 40 * 人 }
返回 (加)
}

函数 模式名 () 文本 {
如果 (AI数 <= 0) { 返回 ("单人") }
返回 (到文本(对局人数()) + "人竞速")
}

函数 刷新AI按钮 () {
如果 (AI数 <= 0) {
AI开 = 假
按钮AI.标题 = "加AI"
} 否则如果 (AI数 >= 最大AI) {
AI开 = 真
按钮AI.标题 = "清空AI"
} 否则 {
AI开 = 真
按钮AI.标题 = "+AI×" + 到文本(AI数)
}
}

函数 AI最高分 () 整数 {
整数 m = 0
整数 i = 0
当 (i < AI数) {
如果 (AI分表[i] > m) { m = AI分表[i] }
i = i + 1
}
返回 (m)
}

函数 同步副桌开局 () {
确保板()
洗牌AI手速()
整数 bak剩 = 剩余
整数 s = 0
当 (s < AI数) {
整数 b = s GW GH
整数 i = 0
循环 (GW * GH, i) { AI板[b + i] = 板[i] }
// 每人独立洗牌,路线不同,不会齐步走
当前AI = s
副桌 = 真
洗牌内区()
剩余 = 统计剩余()
确保可解()
AI剩表[s] = 统计剩余()
副桌 = 假
AI分表[s] = 0
AI消对表[s] = 0
AI连击表[s] = 0
AI上次消表[s] = 0
AIx1表[s] = -1
AIy1表[s] = -1
AIx2表[s] = -1
AIy2表[s] = -1
AI消中表[s] = 假
// 起步错开 + 随机,谁先动手不固定
AI下次表[s] = 取高精度毫秒() + 到小数(随机数(180, 1100) + s * 随机数(40, 160))
s = s + 1
}
剩余 = bak剩
当前AI = 0
副桌 = 假
AI剩余 = AI剩表[0]
AI分 = 0
}

函数 两位 (整数 n) 文本 {
如果 (n < 10) { 返回 ("0" + 到文本(n)) }
返回 (到文本(n))
}

函数 图路径相对 (整数 t) 文本 {
返回 ("资源/图块/" + 两位(t) + ".png")
}

函数 图路径 (整数 t) 文本 {
文本 rel = 图路径相对(t)
文本 abs = 合并路径(取程序目录(), rel)
如果 (文件存在(abs)) { 返回 (abs) }
如果 (文件存在(rel)) { 返回 (rel) }
返回 (abs)
}

函数 扫描图块 () {
数组清空(图有)
数组添加(图有, 假)
图加载数 = 0
整数 i = 1
当 (i <= 最大种数) {
文本 rel = 图路径相对(i)
文本 abs = 合并路径(取程序目录(), rel)
逻辑 ok = 文件存在(abs)
如果 (ok == 假) { ok = 文件存在(rel) }
数组添加(图有, ok)
如果 (ok) { 图加载数 = 图加载数 + 1 }
i = i + 1
}
刷新步时抬头()
}

函数 有图 (整数 t) 逻辑 {
如果 (t < 1 或 t > 最大种数) { 返回 (假) }
如果 (数组长度(图有) <= t) { 返回 (假) }
返回 (图有[t])
}

函数 刷新步时抬头 () {
标签图.标题 = "图" + 到文本(图加载数) + "/" + 到文本(最大种数) + " · 步" + 到文本(步剩秒) + "s"
}

函数 刷新抬头 () {
标签分.标题 = "分 " + 到文本(分)
如果 (AI开) {
标签剩.标题 = "你剩 " + 到文本(剩余) + " · " + 到文本(对局人数()) + "人"
标签AI分.标题 = "AI最高 " + 到文本(AI最高分())
} 否则 {
标签剩.标题 = "剩余 " + 到文本(剩余)
标签AI分.标题 = "AI —"
}
标签连击.标题 = "连击 " + 到文本(连击) + " (最高 " + 到文本(最大连击) + ")"
刷新步时抬头()
}

函数 图案色 (整数 t) 文本 {
如果 (t == 1) { 返回 ("#42A5F5") }
如果 (t == 2) { 返回 ("#66BB6A") }
如果 (t == 3) { 返回 ("#FFA726") }
如果 (t == 4) { 返回 ("#AB47BC") }
如果 (t == 5) { 返回 ("#EF5350") }
如果 (t == 6) { 返回 ("#26C6DA") }
如果 (t == 7) { 返回 ("#FFEE58") }
如果 (t == 8) { 返回 ("#FF7043") }
如果 (t == 9) { 返回 ("#5C6BC0") }
如果 (t == 10) { 返回 ("#8D6E63") }
如果 (t == 11) { 返回 ("#FFEE58") }
如果 (t == 12) { 返回 ("#9CCC65") }
如果 (t == 13) { 返回 ("#29B6F6") }
如果 (t == 14) { 返回 ("#78909C") }
如果 (t == 15) { 返回 ("#EC407A") }
如果 (t == 16) { 返回 ("#26A69A") }
如果 (t == 17) { 返回 ("#FFA726") }
如果 (t == 18) { 返回 ("#7E57C2") }
如果 (t == 19) { 返回 ("#81D4FA") }
如果 (t == 20) { 返回 ("#00ACC1") }
如果 (t == 21) { 返回 ("#EF5350") }
如果 (t == 22) { 返回 ("#FFCA28") }
如果 (t == 23) { 返回 ("#42A5F5") }
如果 (t == 24) { 返回 ("#AB47BC") }
返回 ("#455A64")
}

函数 图案字 (整数 t) 文本 {
如果 (t == 1) { 返回 ("●") }
如果 (t == 2) { 返回 ("■") }
如果 (t == 3) { 返回 ("▲") }
如果 (t == 4) { 返回 ("◆") }
如果 (t == 5) { 返回 ("★") }
如果 (t == 6) { 返回 ("✚") }
如果 (t == 7) { 返回 ("⬡") }
如果 (t == 8) { 返回 ("♥") }
如果 (t == 9) { 返回 ("◎") }
如果 (t == 10) { 返回 ("☽") }
如果 (t == 11) { 返回 ("⚡") }
如果 (t == 12) { 返回 ("☘") }
如果 (t == 13) { 返回 ("💧") }
如果 (t == 14) { 返回 ("⚙") }
如果 (t == 15) { 返回 ("◎") }
如果 (t == 16) { 返回 ("▮") }
如果 (t == 17) { 返回 ("☀") }
如果 (t == 18) { 返回 ("▲") }
如果 (t == 19) { 返回 ("☁") }
如果 (t == 20) { 返回 ("🐟") }
如果 (t == 21) { 返回 ("⚑") }
如果 (t == 22) { 返回 ("♛") }
如果 (t == 23) { 返回 ("⛨") }
如果 (t == 24) { 返回 ("♪") }
返回 ("")
}

函数 音效路径 (文本 名) 文本 {
文本 rel = "资源/音效/" + 名
文本 abs = 合并路径(取程序目录(), rel)
如果 (文件存在(abs)) { 返回 (abs) }
如果 (文件存在(rel)) { 返回 (rel) }
返回 (abs)
}

函数 播音效 (文本 名) {
文本 p = 音效路径(名)
如果 (文件存在(p) == 假) { 返回 () }
播放音频(p, 真, 假, 78)
}

函数 播连击音 (整数 c) {
如果 (c <= 1) {
播音效("clear.wav")
} 否则如果 (c == 2) {
播音效("combo2.wav")
} 否则如果 (c <= 4) {
播音效("combo3.wav")
} 否则如果 (c <= 7) {
播音效("combo5.wav")
} 否则 {
播音效("combo8.wav")
}
}

函数 格中心X (整数 x) 整数 { 返回 (绘边X + x * CELL + CELL / 2) }
函数 格中心Y (整数 y) 整数 { 返回 (绘边Y + y * CELL + CELL / 2) }

函数 刷新边距 () {
// 你的桌占大头;右边按 AI 数量切小屏
整数 topPad = 22
整数 左宽 = 画W
如果 (AI数 > 0) {
整数 右宽 = 画W * 22 / 100
如果 (AI数 >= 2) { 右宽 = 画W * 28 / 100 }
如果 (AI数 >= 3) { 右宽 = 画W * 34 / 100 }
如果 (右宽 < 150) { 右宽 = 150 }
如果 (右宽 > 画W / 3) { 右宽 = 画W / 3 }
左宽 = 画W - 右宽
右起 = 左宽
AI列 = 1
AI行 = AI数
如果 (AI数 >= 3) {
AI列 = 2
AI行 = (AI数 + 1) / 2
}
AI格区宽 = 右宽 / AI列
AI格区高 = 画H / AI行
整数 acw = (AI格区宽 - 8) / (内W + 2)
整数 ach = (AI格区高 - 22) / (内H + 2)
整数 ac = acw
如果 (ach < ac) { ac = ach }
如果 (ac < 8) { ac = 8 }
如果 (ac > 26) { ac = 26 }
AI格 = ac
} 否则 {
右起 = 画W
AI格 = 12
}
整数 cw = (左宽 - 16) / (内W + 2)
整数 ch = (画H - topPad - 8) / (内H + 2)
整数 c = cw
如果 (ch < c) { c = ch }
如果 (c < 36) { c = 36 }
如果 (c > 78) { c = 78 }
玩家CELL = c
CELL = 玩家CELL
边X = (左宽 - (内W + 2) * CELL) / 2
边Y = topPad + (画H - topPad - (内H + 2) * CELL) / 2
如果 (边X < 4) { 边X = 4 }
如果 (边Y < topPad) { 边Y = topPad }
边 = 边X
绘边X = 边X
绘边Y = 边Y
}

函数 定位AI屏 (整数 i) {
整数 col = 0
整数 row = i
如果 (AI列 > 1) {
col = i % AI列
row = i / AI列
}
整数 ox = 右起 + col * AI格区宽
整数 oy = row * AI格区高
整数 bw = (内W + 2) * AI格
整数 bh = (内H + 2) * AI格
AI边X = ox + (AI格区宽 - bw) / 2
AI边Y = oy + 16 + (AI格区高 - 18 - bh) / 2
如果 (AI边X < ox) { AI边X = ox }
如果 (AI边Y < oy + 14) { AI边Y = oy + 14 }
绘边X = AI边X
绘边Y = AI边Y
CELL = AI格
当前AI = i
副桌 = 真
}

函数 还原玩家格 () {
CELL = 玩家CELL
绘边X = 边X
绘边Y = 边Y
副桌 = 假
}

函数 玩家桌宽 () 整数 { 返回 ((内W + 2) * 玩家CELL) }
函数 玩家桌高 () 整数 { 返回 ((内H + 2) * 玩家CELL) }
函数 玩家桌中心X () 整数 { 返回 (边X + 玩家桌宽() / 2) }
函数 玩家桌中心Y () 整数 { 返回 (边Y + 玩家桌高() / 2) }

函数 画结算榜 (整数 bx, 整数 by, 整数 bw) {
// 按分数从高到低画:你 + 全部 AI
确保字体()
画布1.置字体(字通关小)
整数 名[] = []
整数 分表[] = []
数组添加(名, 0)
数组添加(分表, 分)
整数 i = 0
当 (i < AI数) {
数组添加(名, i + 1)
数组添加(分表, AI分表[i])
i = i + 1
}
整数 n = 数组长度(分表)
// 简单冒泡:高分在前
整数 a = 0
当 (a < n - 1) {
整数 b = 0
当 (b < n - 1 - a) {
如果 (分表[b] < 分表[b + 1]) {
整数 tf = 分表[b]
分表[b] = 分表[b + 1]
分表[b + 1] = tf
整数 tid = 名[b]
名[b] = 名[b + 1]
名[b + 1] = tid
}
b = b + 1
}
a = a + 1
}
整数 row = 0
当 (row < n) {
文本 who = "你"
文本 col = "#FFFFD54F"
如果 (名[row] > 0) {
who = "AI" + 到文本(名[row])
col = "#FF4DD0E1"
}
文本 冠 = " "
如果 (row == 0) { 冠 = "★ " }
画布1.置填充(col)
画布1.画文本(bx + 24, by + row * 20, 冠 + 到文本(row + 1) + ". " + who + " " + 到文本(分表[row]) + " 分")
row = row + 1
}
}

函数 同步画布尺寸 () {
// 布局把画布拉大后,绘制必须跟实际宽高走,否则内容钉死在左上角
整数 w = 到整数(画布1.宽度)
整数 h = 到整数(画布1.高度)
如果 (w < 120) { w = 120 }
如果 (h < 120) { h = 120 }
如果 (w == 画W 且 h == 画H) {
// 尺寸未变也可能刚切 AI 双桌,仍要重算边距
刷新边距()
返回 ()
}
画W = w
画H = h
刷新边距()
}

函数 是座位 (整数 x, 整数 y) 逻辑 {
如果 (x < 1 或 y < 1 或 x > 内W 或 y > 内H) { 返回 (假) }
返回 (座[座下标(x, y)])
}

函数 清座位 (逻辑 v) {
确保板()
整数 i = 0
循环 (最大内W * 最大内H, i) { 座[i] = v }
}

函数 座位计数 () 整数 {
整数 n = 0
整数 y = 1
当 (y <= 内H) {
整数 x = 1
当 (x <= 内W) {
如果 (座[座下标(x, y)]) { n = n + 1 }
x = x + 1
}
y = y + 1
}
返回 (n)
}

函数 挖掉使成偶数 () {
// 若座位数为奇数,随机关掉一个座位
整数 n = 座位计数()
如果 (n % 2 == 0) { 返回 () }
整数 试 = 0
当 (试 < 80) {
整数 x = 随机数(1, 内W)
整数 y = 随机数(1, 内H)
如果 (是座位(x, y)) {
座[座下标(x, y)] = 假
返回 ()
}
试 = 试 + 1
}
}

函数 应用布局 (整数 id) {
// 布局随 内W/内H 缩放;默认偏满盘,保证各难度图块总数够多
清座位(真)
布局号 = id
整数 midX = (内W + 1) / 2
整数 midY = (内H + 1) / 2
如果 (id == 0) {
布局名 = "满盘"
} 否则如果 (id == 1) {
布局名 = "回字"
整数 x0 = 内W / 3
整数 x1 = 内W - 内W / 3 + 1
整数 y0 = 内H / 3
整数 y1 = 内H - 内H / 3 + 1
整数 y = y0
当 (y <= y1) {
整数 x = x0
当 (x <= x1) {
座[座下标(x, y)] = 假
x = x + 1
}
y = y + 1
}
} 否则如果 (id == 2) {
布局名 = "十字"
清座位(假)
整数 x = 1
当 (x <= 内W) {
座[座下标(x, midY)] = 真
如果 (midY + 1 <= 内H) { 座[座下标(x, midY + 1)] = 真 }
x = x + 1
}
整数 y = 1
当 (y <= 内H) {
座[座下标(midX, y)] = 真
如果 (midX + 1 <= 内W) { 座[座下标(midX + 1, y)] = 真 }
y = y + 1
}
} 否则如果 (id == 3) {
布局名 = "菱形"
清座位(假)
整数 r = 内W / 2
如果 (内H / 2 + 1 > r) { r = 内H / 2 + 1 }
整数 y = 1
当 (y <= 内H) {
整数 x = 1
当 (x <= 内W) {
整数 dx = x - midX
如果 (dx < 0) { dx = 0 - dx }
整数 dy = y - midY
如果 (dy < 0) { dy = 0 - dy }
如果 (dx + dy <= r) { 座[座下标(x, y)] = 真 }
x = x + 1
}
y = y + 1
}
} 否则如果 (id == 4) {
布局名 = "棋盘洞"
整数 y = 1
当 (y <= 内H) {
整数 x = 1
当 (x <= 内W) {
如果 ((x + y) % 2 == 0) { 座[座下标(x, y)] = 假 }
x = x + 1
}
y = y + 1
}
} 否则如果 (id == 5) {
布局名 = "心形"
清座位(假)
// 近似心形:上半双峰 + 下半收尖(整数)
整数 y = 1
当 (y <= 内H) {
整数 x = 1
当 (x <= 内W) {
逻辑 ok = 假
如果 (y <= 内H / 3) {
整数 L = 内W / 5
整数 R = 内W - 内W / 5
如果 ((x >= L 且 x <= midX - 1) 或 (x >= midX + 1 且 x <= R)) { ok = 真 }
} 否则如果 (y <= 内H * 2 / 3) {
如果 (x >= 2 且 x <= 内W - 1) { ok = 真 }
} 否则 {
整数 shrink = (y - 内H 2 / 3) (内W / 4) / (内H / 3 + 1)
如果 (x >= 2 + shrink 且 x <= 内W - 1 - shrink) { ok = 真 }
}
如果 (ok) { 座[座下标(x, y)] = 真 }
x = x + 1
}
y = y + 1
}
} 否则如果 (id == 6) {
布局名 = "双塔"
清座位(假)
整数 w = 内W / 5
如果 (w < 2) { w = 2 }
整数 L = 2
整数 R = 内W - w
整数 y = 1
当 (y <= 内H) {
整数 x = L
当 (x < L + w) {
座[座下标(x, y)] = 真
x = x + 1
}
x = R
当 (x <= 内W 且 x < R + w) {
座[座下标(x, y)] = 真
x = x + 1
}
y = y + 1
}
} 否则如果 (id == 7) {
布局名 = "边框"
清座位(假)
整数 thick = 2
如果 (内W >= 12) { thick = 3 }
整数 x = 1
当 (x <= 内W) {
整数 t = 1
当 (t <= thick) {
座[座下标(x, t)] = 真
座[座下标(x, 内H - t + 1)] = 真
t = t + 1
}
x = x + 1
}
整数 y = 1
当 (y <= 内H) {
整数 t = 1
当 (t <= thick) {
座[座下标(t, y)] = 真
座[座下标(内W - t + 1, y)] = 真
t = t + 1
}
y = y + 1
}
} 否则 {
布局名 = "随机洞"
整数 挖 = 内W * 内H / 10
如果 (挖 < 4) { 挖 = 4 }
如果 (挖 > 20) { 挖 = 20 }
整数 k = 0
当 (k < 挖) {
整数 x = 随机数(1, 内W)
整数 y = 随机数(1, 内H)
座[座下标(x, y)] = 假
k = k + 1
}
}
挖掉使成偶数()
如果 (座位计数() < 内W * 内H / 3) {
清座位(真)
挖掉使成偶数()
布局名 = "满盘"
布局号 = 0
}
}

函数 置线 (整数 n, 整数 x0, 整数 y0, 整数 x1, 整数 y1, 整数 x2, 整数 y2, 整数 x3, 整数 y3) {
数组清空(线x)
数组清空(线y)
线点数 = n
如果 (n >= 1) {
数组添加(线x, x0)
数组添加(线y, y0)
}
如果 (n >= 2) {
数组添加(线x, x1)
数组添加(线y, y1)
}
如果 (n >= 3) {
数组添加(线x, x2)
数组添加(线y, y2)
}
如果 (n >= 4) {
数组添加(线x, x3)
数组添加(线y, y3)
}
线消时 = 取高精度毫秒() + 到小数(线毫秒)
}

函数 直线通 (整数 x1, 整数 y1, 整数 x2, 整数 y2) 逻辑 {
如果 (x1 == x2) {
整数 a = y1
整数 b = y2
如果 (a > b) {
整数 t = a
a = b
b = t
}
整数 y = a + 1
当 (y < b) {
如果 (读格(x1, y) != 0) { 返回 (假) }
y = y + 1
}
返回 (真)
}
如果 (y1 == y2) {
整数 a = x1
整数 b = x2
如果 (a > b) {
整数 t = a
a = b
b = t
}
整数 x = a + 1
当 (x < b) {
如果 (读格(x, y1) != 0) { 返回 (假) }
x = x + 1
}
返回 (真)
}
返回 (假)
}

函数 空格 (整数 x, 整数 y) 逻辑 {
如果 (x < 0 或 y < 0 或 x >= GW 或 y >= GH) { 返回 (假) }
返回 (读格(x, y) == 0)
}

函数 两折可达 (整数 x1, 整数 y1, 整数 x2, 整数 y2) 逻辑 {
// 静默检测(不画线):标准最多两折
// 注意:勿取名「路径存在」,会撞基础库「路径存在(文本)」
如果 (x1 == x2 且 y1 == y2) { 返回 (假) }
整数 t1 = 读格(x1, y1)
整数 t2 = 读格(x2, y2)
如果 (t1 == 0 或 t1 != t2) { 返回 (假) }
如果 (直线通(x1, y1, x2, y2)) { 返回 (真) }
整数 cx = x1
整数 cy = y2
如果 (空格(cx, cy) 且 直线通(x1, y1, cx, cy) 且 直线通(cx, cy, x2, y2)) { 返回 (真) }
cx = x2
cy = y1
如果 (空格(cx, cy) 且 直线通(x1, y1, cx, cy) 且 直线通(cx, cy, x2, y2)) { 返回 (真) }
整数 y = 0
循环 (GH, y) {
整数 x = 0
循环 (GW, x) {
如果 (空格(x, y) == 假) { 继续() }
如果 (直线通(x1, y1, x, y) == 假) { 继续() }
如果 (直线通(x, y, x2, y2)) { 返回 (真) }
整数 ux = x
整数 uy = y2
如果 (空格(ux, uy) 且 直线通(x, y, ux, uy) 且 直线通(ux, uy, x2, y2)) { 返回 (真) }
ux = x2
uy = y
如果 (空格(ux, uy) 且 直线通(x, y, ux, uy) 且 直线通(ux, uy, x2, y2)) { 返回 (真) }
}
}
返回 (假)
}

函数 可连通 (整数 x1, 整数 y1, 整数 x2, 整数 y2) 逻辑 {
// 与两折可达相同,但会写下折线用于特效
如果 (两折可达(x1, y1, x2, y2) == 假) { 返回 (假) }
如果 (直线通(x1, y1, x2, y2)) {
置线(2, x1, y1, x2, y2, 0, 0, 0, 0)
返回 (真)
}
整数 cx = x1
整数 cy = y2
如果 (空格(cx, cy) 且 直线通(x1, y1, cx, cy) 且 直线通(cx, cy, x2, y2)) {
置线(3, x1, y1, cx, cy, x2, y2, 0, 0)
返回 (真)
}
cx = x2
cy = y1
如果 (空格(cx, cy) 且 直线通(x1, y1, cx, cy) 且 直线通(cx, cy, x2, y2)) {
置线(3, x1, y1, cx, cy, x2, y2, 0, 0)
返回 (真)
}
整数 y = 0
循环 (GH, y) {
整数 x = 0
循环 (GW, x) {
如果 (空格(x, y) == 假) { 继续() }
如果 (直线通(x1, y1, x, y) == 假) { 继续() }
如果 (直线通(x, y, x2, y2)) {
置线(3, x1, y1, x, y, x2, y2, 0, 0)
返回 (真)
}
整数 ux = x
整数 uy = y2
如果 (空格(ux, uy) 且 直线通(x, y, ux, uy) 且 直线通(ux, uy, x2, y2)) {
置线(4, x1, y1, x, y, ux, uy, x2, y2)
返回 (真)
}
ux = x2
uy = y
如果 (空格(ux, uy) 且 直线通(x, y, ux, uy) 且 直线通(ux, uy, x2, y2)) {
置线(4, x1, y1, x, y, ux, uy, x2, y2)
返回 (真)
}
}
}
返回 (假)
}

函数 还有可消 () 逻辑 {
整数 y1 = 1
当 (y1 <= 内H) {
整数 x1 = 1
当 (x1 <= 内W) {
如果 (读格(x1, y1) != 0) {
整数 y2 = y1
当 (y2 <= 内H) {
整数 x2 = 1
当 (x2 <= 内W) {
逻辑 可试 = 真
如果 (y2 == y1 且 x2 <= x1) { 可试 = 假 }
如果 (可试 且 读格(x2, y2) != 0) {
如果 (两折可达(x1, y1, x2, y2)) { 返回 (真) }
}
x2 = x2 + 1
}
y2 = y2 + 1
}
}
x1 = x1 + 1
}
y1 = y1 + 1
}
返回 (假)
}

函数 取一对可消 (整数 ox1, 整数 oy1, 整数 ox2, 整数 oy2) 逻辑 {
// 输出参数用全局 提示* 暂存,成功返回真
整数 y1 = 1
当 (y1 <= 内H) {
整数 x1 = 1
当 (x1 <= 内W) {
如果 (读格(x1, y1) != 0) {
整数 y2 = y1
当 (y2 <= 内H) {
整数 x2 = 1
当 (x2 <= 内W) {
逻辑 可试 = 真
如果 (y2 == y1 且 x2 <= x1) { 可试 = 假 }
如果 (可试 且 读格(x2, y2) != 0) {
如果 (两折可达(x1, y1, x2, y2)) {
如果 (消对跳过 > 0) {
消对跳过 = 消对跳过 - 1
} 否则 {
提示x1 = x1
提示y1 = y1
提示x2 = x2
提示y2 = y2
返回 (真)
}
}
}
x2 = x2 + 1
}
y2 = y2 + 1
}
}
x1 = x1 + 1
}
y1 = y1 + 1
}
返回 (假)
}

函数 确保可解 () {
// 开局/重排后若无解则反复洗牌
整数 试 = 0
当 (试 < 40) {
如果 (剩余 <= 0) { 返回 () }
如果 (还有可消()) { 返回 () }
洗牌内区()
剩余 = 统计剩余()
试 = 试 + 1
}
}

函数 死局则重排 () {
副桌 = 假
如果 (通关中) { 返回 () }
如果 (剩余 <= 0) { 返回 () }
如果 (还有可消()) { 返回 () }
// 典型「2×2 对角死局」:两折规则下本就连不通,不是漏判
洗牌内区()
确保可解()
选x = -1
选y = -1
线点数 = 0
剩余 = 统计剩余()
重置步时()
刷新抬头()
标签状态.标题 = "死局已自动重排(对角堵住时两折无法连通)"
}

函数 统计剩余 () 整数 {
整数 n = 0
整数 i = 0
循环 (GW * GH, i) {
如果 (副桌) {
如果 (AI板[当前AI GW GH + i] != 0) { n = n + 1 }
} 否则 {
如果 (板[i] != 0) { n = n + 1 }
}
}
返回 (n)
}

函数 洗牌内区 () {
整数 池[] = []
整数 y = 1
当 (y <= 内H) {
整数 x = 1
当 (x <= 内W) {
如果 (是座位(x, y)) {
数组添加(池, 读格(x, y))
}
x = x + 1
}
y = y + 1
}
整数 n = 数组长度(池)
整数 i = n - 1
当 (i > 0) {
整数 j = 随机数(0, i)
整数 t = 池[i]
池[i] = 池[j]
池[j] = t
i = i - 1
}
整数 k = 0
y = 1
当 (y <= 内H) {
整数 x = 1
当 (x <= 内W) {
如果 (是座位(x, y)) {
写格(x, y, 池[k])
k = k + 1
} 否则 {
写格(x, y, 0)
}
x = x + 1
}
y = y + 1
}
}

函数 难度名 () 文本 {
如果 (难度 == 0) { 返回 ("简单") }
如果 (难度 == 1) { 返回 ("普通") }
返回 ("困难")
}

函数 进入结局 (整数 种, 文本 主, 文本 副) {
通关中 = 真
倒计时中 = 假
通关起 = 取高精度毫秒()
结局 = 种
通关字 = 主
通关副 = 副
连击字 = ""
锁输入 = 真
选x = -1
选y = -1
提示x1 = -1
线点数 = 0
AIx1 = -1
AI消中 = 假
}

函数 步时上限 () 整数 {
// 难度越高略紧,但整体加长
如果 (难度 == 0) { 返回 (40) }
如果 (难度 == 1) { 返回 (30) }
返回 (22)
}

函数 重置步时 () {
步限秒 = 步时上限()
步剩秒 = 步限秒
步倒计时锚 = 取高精度毫秒()
上次警告秒 = -1
刷新步时抬头()
}

函数 刷新难度档 () {
// 难度 = 图种更多 + 棋盘更大(总数量更多)
如果 (难度 == 0) {
内W = 10
内H = 7
种数 = 12
AI间隔 = 2400
} 否则如果 (难度 == 1) {
内W = 12
内H = 8
种数 = 18
AI间隔 = 1500
} 否则 {
内W = 14
内H = 9
种数 = 24
AI间隔 = 900
}
如果 (种数 > 最大种数) { 种数 = 最大种数 }
刷新边距()
如果 (难度同步中) { 返回 () }
如果 (下拉难度.当前索引 != 难度) {
难度同步中 = 真
下拉难度.当前索引 = 难度
难度同步中 = 假
}
}

函数 填充座位 () {
整数 槽 = 座位计数()
整数 池[] = []
整数 对 = 槽 / 2
整数 i = 0
循环 (对, i) {
整数 t = (i % 种数) + 1
数组添加(池, t)
数组添加(池, t)
}
整数 n = 数组长度(池)
i = n - 1
当 (i > 0) {
整数 j = 随机数(0, i)
整数 tmp = 池[i]
池[i] = 池[j]
池[j] = tmp
i = i - 1
}
整数 k = 0
整数 y = 1
当 (y <= 内H) {
整数 x = 1
当 (x <= 内W) {
如果 (是座位(x, y)) {
板[下标(x, y)] = 池[k]
k = k + 1
} 否则 {
板[下标(x, y)] = 0
}
x = x + 1
}
y = y + 1
}
}

函数 新局 () {
副桌 = 假
确保板()
刷新难度档()
整数 i = 0
循环 (最大AI GW GH, i) {
如果 (i < GW * GH) { 板[i] = 0 }
AI板[i] = 0
}

整数 lid = 0
如果 (难度 == 0) {
// 简单:满盘或轻挖,保证图块多
整数 r = 随机数(0, 2)
如果 (r == 0) { lid = 0 }
否则如果 (r == 1) { lid = 1 }
否则 { lid = 8 }
} 否则如果 (难度 == 2) {
// 困难:强制满盘,对数最多(14×9=63 对)
lid = 0
} 否则 {
// 普通:满盘为主,偶发造型
整数 r = 随机数(0, 5)
如果 (r <= 2) { lid = 0 }
否则 { lid = r }
}
应用布局(lid)
填充座位()
剩余 = 统计剩余()
确保可解()
剩余 = 统计剩余()
如果 (AI开) {
同步副桌开局()
} 否则 {
AI剩余 = 0
}

选x = -1
选y = -1
分 = 0
AI分 = 0
玩家消对 = 0
AI消对 = 0
AIx1 = -1
AIy1 = -1
AIx2 = -1
AIy2 = -1
AI瞄种 = 0
AI消中 = 假
连击 = 0
最大连击 = 0
上次消 = 0
提示次 = 0
线点数 = 0
消中 = 假
锁输入 = 假
提示x1 = -1
连击字 = ""
闪屏消 = 0
通关中 = 假
通关字 = ""
通关副 = ""
结局 = 0
数组清空(线x)
数组清空(线y)
开局对数 = 剩余 / 2
已就绪 = 真
倒计时中 = 真
倒计时起 = 取高精度毫秒()
锁输入 = 真
扫描图块()
刷新边距()
刷新抬头()
标签状态.标题 = "准备 · " + 模式名() + " · " + 难度名()
按钮新局.标题 = "准备"
按钮新局.背景色 = "#EF6C00"
画布1.刷新()
}

函数 开局放行 () {
倒计时中 = 假
锁输入 = 假
重置步时()
小数 now = 取高精度毫秒()
整数 s = 0
洗牌AI手速()
当 (s < AI数) {
AI下次表[s] = now + 到小数(随机数(200, 900) + AI间隔表[s] / 8)
s = s + 1
}
如果 (AI开) { AI瞄准更新() }
按钮新局.标题 = "新局"
按钮新局.背景色 = "#007ACC"
标签状态.标题 = 难度名() + " · " + 布局名 + " · " + 模式名() + " · 分×" + 到文本(对局人数())
}

函数 进入待命 () {
已就绪 = 假
倒计时中 = 假
通关中 = 假
锁输入 = 真
消中 = 假
选x = -1
选y = -1
提示x1 = -1
AIx1 = -1
AI消中 = 假
连击字 = ""
线点数 = 0
按钮新局.标题 = "开始"
按钮新局.背景色 = "#43A047"
文本 mode = 模式名()
标签状态.标题 = "先选难度和模式,再点「开始」· " + 难度名() + " · " + mode
标签分.标题 = "分 0"
标签AI分.标题 = "AI —"
AI剩余 = 0
标签连击.标题 = "连击 0"
标签剩.标题 = "剩余 —"
标签图.标题 = "图" + 到文本(图加载数) + "/" + 到文本(最大种数) + " · 待命"
画布1.刷新()
}

函数 重排 () {
如果 (已就绪 == 假 或 锁输入 或 通关中 或 倒计时中) { 返回 () }
洗牌内区()
剩余 = 统计剩余()
确保可解()
选x = -1
选y = -1
线点数 = 0
提示x1 = -1
剩余 = 统计剩余()
刷新抬头()
重置步时()
标签状态.标题 = "已重排并确保有解 · 「" + 布局名 + "」"
画布1.刷新()
}

函数 找提示 () 逻辑 {
如果 (取一对可消(0, 0, 0, 0) == 假) { 返回 (假) }
// 取一对可消已写入 提示*,再走可连通画线
如果 (可连通(提示x1, 提示y1, 提示x2, 提示y2)) {
提示消 = 取高精度毫秒() + 1400
线消时 = 取高精度毫秒() + 1400
返回 (真)
}
返回 (假)
}

函数 用提示 () {
如果 (已就绪 == 假 或 锁输入 或 通关中 或 倒计时中) { 返回 () }
线点数 = 0
如果 (找提示()) {
提示次 = 提示次 + 1
标签状态.标题 = "提示 · " + 布局名 + " · 第 " + 到文本(提示次) + " 次"
选x = -1
选y = -1
画布1.刷新()
} 否则 {
标签状态.标题 = "无解,请重排"
线点数 = 0
}
}

函数 像素到格X (整数 px) 整数 { 返回 ((px - 边X) / CELL) }
函数 像素到格Y (整数 py) 整数 { 返回 ((py - 边Y) / CELL) }

函数 触发连击字 (整数 c) {
消连击 = c
如果 (c <= 1) {
连击字 = "消除!"
连击档 = 0
} 否则如果 (c == 2) {
连击字 = "连击 ×2"
连击档 = 1
} 否则如果 (c == 3) {
连击字 = "连击 ×3 不错!"
连击档 = 2
} 否则如果 (c == 4) {
连击字 = "连击 ×4 漂亮!"
连击档 = 3
} 否则如果 (c == 5) {
连击字 = "连击 ×5 火热!!"
连击档 = 4
闪屏消 = 取高精度毫秒() + 120
} 否则如果 (c < 8) {
连击字 = "连击 ×" + 到文本(c) + " 疯狂!!!"
连击档 = 5
闪屏消 = 取高精度毫秒() + 160
} 否则 {
连击字 = "连击 ×" + 到文本(c) + " 无敌!!!!"
连击档 = 6
闪屏消 = 取高精度毫秒() + 220
}
连击字消 = 取高精度毫秒() + 700
播连击音(c)
}

函数 开始消除 (整数 x1, 整数 y1, 整数 x2, 整数 y2) {
副桌 = 假
消种 = 板[下标(x1, y1)]
消x1 = x1
消y1 = y1
消x2 = x2
消y2 = y2
消起 = 取高精度毫秒()
消中 = 真
锁输入 = 真
板[下标(x1, y1)] = 0
板[下标(x2, y2)] = 0
选x = -1
选y = -1
提示x1 = -1

小数 now = 取高精度毫秒()
如果 (上次消 > 0 且 now - 上次消 < 3000) {
连击 = 连击 + 1
} 否则 {
连击 = 1
}
上次消 = now
如果 (连击 > 最大连击) { 最大连击 = 连击 }
触发连击字(连击)
整数 加 = 连击得分(连击)
分 = 分 + 加
玩家消对 = 玩家消对 + 1
剩余 = 统计剩余()
刷新抬头()
如果 (剩余 <= 0) {
闪屏消 = 取高精度毫秒() + 280
连击字 = ""
如果 (AI开) {
进入结局(1, "胜利!", "你先清完自己的桌 · 「" + 布局名 + "」")
播音效("win.wav")
} 否则 {
进入结局(1, "通关!", "全部清空 · 「" + 布局名 + "」")
播音效("win.wav")
}
标签状态.标题 = 通关字 + " · 你剩0 · AI剩" + 到文本(AI剩余)
} 否则 {
重置步时()
标签状态.标题 = "你的桌 · +" + 到文本(加) + " · 连击×" + 到文本(连击) + " · " + 到文本(对局人数()) + "人"
死局则重排()
}
}

函数 AI死局则重排 () {
如果 (通关中 或 AI开 == 假) { 返回 () }
整数 s = 当前AI
副桌 = 真
AI剩表[s] = 统计剩余()
AI剩余 = AI剩表[s]
如果 (AI剩表[s] <= 0) {
副桌 = 假
返回 ()
}
如果 (还有可消()) {
副桌 = 假
返回 ()
}
整数 bak剩 = 剩余
剩余 = AI剩表[s]
洗牌内区()
确保可解()
AI剩表[s] = 统计剩余()
AI剩余 = AI剩表[s]
剩余 = bak剩
AIx1表[s] = -1
AI连击表[s] = 0
AI上次消表[s] = 0
副桌 = 假
}

函数 AI瞄准更新 () {
如果 (AI开 == 假 或 通关中 或 已就绪 == 假) {
返回 ()
}
整数 s = 当前AI
副桌 = 真
如果 (AIx1表[s] >= 0) {
整数 a = 读格(AIx1表[s], AIy1表[s])
整数 b = 读格(AIx2表[s], AIy2表[s])
如果 (a != 0 且 a == b 且 两折可达(AIx1表[s], AIy1表[s], AIx2表[s], AIy2表[s])) {
AI瞄种 = a
副桌 = 假
返回 ()
}
}
整数 bx1 = 提示x1
整数 by1 = 提示y1
整数 bx2 = 提示x2
整数 by2 = 提示y2
// 随机跳过前几对,避免所有 AI 永远消同一路线
消对跳过 = 随机数(0, 5)
如果 (取一对可消(0, 0, 0, 0)) {
AIx1表[s] = 提示x1
AIy1表[s] = 提示y1
AIx2表[s] = 提示x2
AIy2表[s] = 提示y2
AI瞄种 = 读格(AIx1表[s], AIy1表[s])
} 否则 {
消对跳过 = 0
如果 (取一对可消(0, 0, 0, 0)) {
AIx1表[s] = 提示x1
AIy1表[s] = 提示y1
AIx2表[s] = 提示x2
AIy2表[s] = 提示y2
AI瞄种 = 读格(AIx1表[s], AIy1表[s])
} 否则 {
AIx1表[s] = -1
AIy1表[s] = -1
AIx2表[s] = -1
AIy2表[s] = -1
AI瞄种 = 0
}
}
消对跳过 = 0
提示x1 = bx1
提示y1 = by1
提示x2 = bx2
提示y2 = by2
副桌 = 假
}

函数 AI消除一对 () {
整数 s = 当前AI
如果 (AIx1表[s] < 0) { AI瞄准更新() }
如果 (AIx1表[s] < 0) { 返回 () }
副桌 = 真
整数 t1 = 读格(AIx1表[s], AIy1表[s])
整数 t2 = 读格(AIx2表[s], AIy2表[s])
如果 (t1 == 0 或 t1 != t2) {
副桌 = 假
AIx1表[s] = -1
AI瞄准更新()
返回 ()
}
如果 (两折可达(AIx1表[s], AIy1表[s], AIx2表[s], AIy2表[s]) == 假) {
副桌 = 假
AIx1表[s] = -1
AI瞄准更新()
返回 ()
}
AI消种表[s] = t1
AI消x1表[s] = AIx1表[s]
AI消y1表[s] = AIy1表[s]
AI消x2表[s] = AIx2表[s]
AI消y2表[s] = AIy2表[s]
AI消起表[s] = 取高精度毫秒()
AI消中表[s] = 真
写格(AIx1表[s], AIy1表[s], 0)
写格(AIx2表[s], AIy2表[s], 0)
AIx1表[s] = -1
小数 now = 取高精度毫秒()
如果 (AI上次消表[s] > 0 且 now - AI上次消表[s] < 3000) {
AI连击表[s] = AI连击表[s] + 1
} 否则 {
AI连击表[s] = 1
}
AI上次消表[s] = now
整数 加 = 连击得分(AI连击表[s])
AI分表[s] = AI分表[s] + 加
AI分 = AI最高分()
AI消对表[s] = AI消对表[s] + 1
AI消对 = AI消对表[s]
AI剩表[s] = 统计剩余()
AI剩余 = AI剩表[s]
副桌 = 假
刷新抬头()
如果 (AI剩表[s] <= 0) {
进入结局(2, "惜败", "AI" + 到文本(s + 1) + " 先清完 · 「" + 布局名 + "」")
播音效("fail.wav")
标签状态.标题 = 通关字 + " · AI" + 到文本(s + 1) + " " + 到文本(AI分表[s]) + "分"
} 否则 {
标签状态.标题 = "AI" + 到文本(s + 1) + " +" + 到文本(加) + " 连击×" + 到文本(AI连击表[s]) + " 剩" + 到文本(AI剩表[s])
AI死局则重排()
AI瞄准更新()
}
}

函数 AI行动 () {
如果 (AI开 == 假 或 通关中 或 已就绪 == 假) { 返回 () }
AI瞄准更新()
整数 s = 当前AI
如果 (AIx1表[s] < 0) {
AI死局则重排()
AI瞄准更新()
如果 (AIx1表[s] < 0) { 返回 () }
}
AI消除一对()
}

函数 点格 (整数 gx, 整数 gy) {
如果 (已就绪 == 假 或 锁输入 或 通关中) { 返回 () }
副桌 = 假
如果 (是座位(gx, gy) == 假) { 返回 () }
如果 (板[下标(gx, gy)] == 0) { 返回 () }

如果 (选x < 0) {
选x = gx
选y = gy
标签状态.标题 = "已选中 · " + 布局名
画布1.刷新()
返回 ()
}
如果 (选x == gx 且 选y == gy) {
选x = -1
选y = -1
标签状态.标题 = "取消选择"
画布1.刷新()
返回 ()
}
如果 (可连通(选x, 选y, gx, gy)) {
开始消除(选x, 选y, gx, gy)
} 否则 {
选x = gx
选y = gy
线点数 = 0
// 2×2 对角在「最多两折」规则下经常本就无解,不是漏判
如果 (还有可消() == 假) {
死局则重排()
} 否则 {
标签状态.标题 = "这两块两折连不通,已改选(可点提示)"
}
}
画布1.刷新()
}

函数 帧更新 () {
小数 now = 取高精度毫秒()
如果 (消中) {
如果 (now - 消起 >= 到小数(消毫秒)) {
消中 = 假
// 已进结局则保持锁死,禁止继续点棋盘
如果 (通关中 == 假 且 倒计时中 == 假) { 锁输入 = 假 }
线点数 = 0
数组清空(线x)
数组清空(线y)
}
} 否则如果 (线点数 > 0 且 now >= 线消时) {
线点数 = 0
数组清空(线x)
数组清空(线y)
}
如果 (提示x1 >= 0 且 now >= 提示消) {
提示x1 = -1
线点数 = 0
}
如果 (连击字 != "" 且 now >= 连击字消) {
连击字 = ""
}
整数 s = 0
当 (s < AI数) {
如果 (AI消中表[s] 且 now - AI消起表[s] >= 220) {
AI消中表[s] = 假
}
s = s + 1
}
如果 (倒计时中) {
如果 (now - 倒计时起 >= 3400) { 开局放行() }
}
// 每步倒计时(全难度,时限随难度变)
如果 (通关中 == 假 且 已就绪 且 锁输入 == 假 且 倒计时中 == 假) {
如果 (now - 步倒计时锚 >= 1000) {
步倒计时锚 = now
步剩秒 = 步剩秒 - 1
如果 (步剩秒 <= 5 且 步剩秒 != 上次警告秒 且 步剩秒 > 0) {
上次警告秒 = 步剩秒
播音效("tick.wav")
}
如果 (步剩秒 <= 0) {
步剩秒 = 0
进入结局(4, "超时", "一步超时 · 难度「" + 难度名() + "」限" + 到文本(步限秒) + "秒")
播音效("fail.wav")
如果 (AI开) {
标签状态.标题 = "一步超时 · 你剩" + 到文本(剩余) + " AI剩" + 到文本(AI剩余)
} 否则 {
标签状态.标题 = "一步超时 · 分" + 到文本(分)
}
}
刷新步时抬头()
}
}
// 各 AI 各自计时,只动自己的小屏
如果 (AI开 且 通关中 == 假 且 已就绪 且 倒计时中 == 假) {
整数 ai = 0
当 (ai < AI数) {
如果 (now >= AI下次表[ai]) {
当前AI = ai
// 偶发「犹豫」:慢手像真人想一下
如果 (随机数(0, 11) == 0) {
AI下次表[ai] = now + 到小数(随机数(180, 520))
} 否则 {
AI行动()
整数 gap = AI间隔表[ai] + 随机数(0, 360) - 100
如果 (gap < 380) { gap = 380 }
AI下次表[ai] = now + 到小数(gap)
}
}
ai = ai + 1
}
还原玩家格()
}
画布1.刷新()
}

函数 画一枚 (整数 px, 整数 py, 整数 种, 整数 边宽, 文本 边色, 小数 缩放) {
确保字体()
整数 sz = 到整数(到小数(CELL - 8) * 缩放)
整数 ox = px + (CELL - sz) / 2
整数 oy = py + (CELL - sz) / 2
如果 (边宽 >= 3) {
文本 glow = 边色
画布1.置填充(glow)
画布1.置笔触(glow, 0)
画布1.画矩形(ox - 4, oy - 4, sz + 8, sz + 8)
}
如果 (有图(种)) {
// 有 PNG 只画图,不再叠白色符号(大字号残留会露出齿轮/星形白影)
文本 path = 图路径(种)
绘.画图像(ox, oy, sz, sz, path)
} 否则 {
文本 c = 图案色(种)
画布1.置填充(c)
画布1.置笔触("#FFFFFF", 1)
画布1.画矩形(ox, oy, sz, sz)
画布1.置字体(字图块)
画布1.置填充("#FFFFFFFF")
画布1.画文本(ox + sz / 3, oy + sz / 4, 图案字(种))
}
}

函数 连击色 () 文本 {
如果 (连击档 <= 0) { 返回 ("#FFF59D") }
如果 (连击档 == 1) { 返回 ("#FFD54F") }
如果 (连击档 == 2) { 返回 ("#FFB74D") }
如果 (连击档 == 3) { 返回 ("#FF8A65") }
如果 (连击档 == 4) { 返回 ("#EF5350") }
如果 (连击档 == 5) { 返回 ("#E040FB") }
返回 ("#FF1744")
}

函数 画粒子沿线 () {
如果 (线点数 < 2) { 返回 () }
小数 now = 取高精度毫秒()
整数 phase = 到整数(now) % 100
整数 i = 0
循环 (线点数 - 1, i) {
整数 x1 = 格中心X(线x[i])
整数 y1 = 格中心Y(线y[i])
整数 x2 = 格中心X(线x[i + 1])
整数 y2 = 格中心Y(线y[i + 1])
整数 s = 0
循环 (6, s) {
小数 t = (到小数(s) + 到小数(phase) / 100.0) / 6.0
如果 (t > 1) { t = t - 1.0 }
整数 px = 到整数(到小数(x1) + (到小数(x2 - x1)) * t)
整数 py = 到整数(到小数(y1) + (到小数(y2 - y1)) * t)
整数 rr = 3 + (s % 3)
画布1.置填充("#FFFFF176")
画布1.置笔触("#00FFFFFF", 0)
画布1.画椭圆(px - rr, py - rr, rr 2, rr 2)
}
}
}

函数 联机广播 (文本 msg) {
文本 行 = msg + "\n"
如果 (联机角色 == 1 且 客连接ID >= 0) {
房服.发送(客连接ID, 行)
} 否则如果 (联机角色 == 2) {
房客.发送(行)
}
}

函数 服务进入 (整数 连接ID, 文本 IP, 整数 端口, 文本 IP端口) {
客连接ID = 连接ID
标签状态.标题 = "玩家加入:" + IP端口
联机广播("HELLO|HOST")
}

函数 服务数据 (整数 连接ID, 文本 IP, 整数 端口, 文本 IP端口, 文本 数据) {
标签状态.标题 = "收到对手消息(联机骨架)"
}

函数 服务离开 (整数 连接ID, 文本 IP, 整数 端口, 文本 IP端口) {
客连接ID = -1
标签状态.标题 = "对手已离开"
}

函数 连连看.尺寸改变 (整数 宽度, 整数 高度) {
如果 (宽度 == 画W 且 高度 == 画H) { 返回 () }
同步画布尺寸()
画布1.刷新()
}

函数 连连看.创建完毕 () {
画布1.置编辑模式(0)
画布1.允许平移 = 假
画布1.允许缩放 = 假
绘.绑画布(画布1)
确保字体()
确保板()
同步画布尺寸()
刷新难度档()
扫描图块()
刷新AI按钮()
进入待命()
钟.启动(40, &帧更新)
}

函数 按钮新局.单击 (整数 X, 整数 Y) {
如果 (倒计时中) { 返回 () }
新局()
}
函数 按钮重排.单击 (整数 X, 整数 Y) { 重排() }
函数 按钮提示.单击 (整数 X, 整数 Y) { 用提示() }

函数 下拉难度.选中项 (整数 当前索引, 文本 项目) {
如果 (难度同步中) { 返回 () }
整数 idx = 当前索引
如果 (idx < 0) { 返回 () }
如果 (idx > 2) { idx = 2 }
如果 (idx == 难度) { 返回 () }
难度 = idx
刷新难度档()
如果 (已就绪 或 倒计时中) {
进入待命()
标签状态.标题 = "难度已改为「" + 难度名() + "」· 再点「开始」"
} 否则 {
标签状态.标题 = "难度「" + 难度名() + "」· " + 模式名() + " · 点「开始」"
画布1.刷新()
}
}

函数 按钮AI.单击 (整数 X, 整数 Y) {
如果 (AI数 >= 最大AI) {
AI数 = 0
} 否则 {
AI数 = AI数 + 1
}
刷新AI按钮()
刷新边距()
如果 (已就绪 或 倒计时中) {
进入待命()
标签状态.标题 = "模式「" + 模式名() + "」· 再点「开始」"
} 否则 {
标签状态.标题 = "已选 " + 模式名() + " · 分×" + 到文本(对局人数()) + " · 点「开始」"
画布1.刷新()
}
}

函数 画待命界面 (小数 now) {
确保字体()
画布1.置笔触("#00FFFFFF", 0)
画布1.置填充("#070910")
画布1.画矩形(0, 0, 画W, 画H)
整数 pulse = 到整数(now) % 1200
如果 (pulse > 600) { pulse = 1200 - pulse }
整数 glow = 8 + pulse / 70
整数 cx = 画W / 2
整数 cy = 画H / 2 - 20
画布1.置填充("#2243A047")
画布1.画椭圆(cx - 140 - glow, cy - 100 - glow, 280 + glow 2, 200 + glow 2)
画布1.置填充("#F0121A24")
画布1.置笔触("#AA43A047", 3)
画布1.画矩形(cx - 220, cy - 120, 440, 250)
画布1.置笔触("#66FFD54F", 1)
画布1.画矩形(cx - 210, cy - 110, 420, 230)

画布1.置字体(字通关大)
画描边字(cx - 100, cy - 95, "连连看", "#FFFFF176", "#CC000000")
画布1.置字体(字通关中)
画描边字(cx - 150, cy - 40, "① 选难度与对战模式", "#FFFFFFFF", "#88000000")
画描边字(cx - 150, cy - 8, "② 再点「开始」· 3秒后 GO!", "#FFB0BEC5", "#88000000")

画布1.置字体(字通关小)
画布1.置填充("#FFFFD54F")
画布1.画文本(cx - 160, cy + 40, "难度 " + 难度名() + " · " + 到文本(内W) + "×" + 到文本(内H) + " · " + 到文本(种数) + "种")
画布1.置填充("#FF4DD0E1")
画布1.画文本(cx - 160, cy + 68, "模式 " + 模式名() + " · 分×" + 到文本(对局人数()))
画布1.置填充("#FF81C784")
画布1.画文本(cx - 140, cy + 98, "上方「加AI」可加对手 · 再点「开始」")
}

函数 画AI鬼影格 (整数 gx, 整数 gy, 小数 now) {
如果 (gx < 0) { 返回 () }
整数 px = 绘边X + gx * CELL
整数 py = 绘边Y + gy * CELL
整数 pulse = 到整数(now) % 700
如果 (pulse > 350) { pulse = 700 - pulse }
整数 pad = 2 + pulse / 120
画布1.置笔触("#00FFFFFF", 0)
画布1.置填充("#5540C4FF")
画布1.画矩形(px + 3, py + 3, CELL - 6, CELL - 6)
画布1.置笔触("#FF40C4FF", 2)
画布1.置填充("#2240C4FF")
画布1.画矩形(px + pad, py + pad, CELL - pad 2, CELL - pad 2)
}

函数 画桌标题 (整数 ox, 整数 oy, 文本 名, 文本 色, 整数 剩块) {
确保字体()
画布1.置字体(字步时)
画布1.置填充(色)
画布1.画文本(ox + CELL, oy - 16, 名 + " 剩" + 到文本(剩块))
}

函数 画一桌图块 (逻辑 是AI桌, 小数 now) {
如果 (是AI桌) {
绘边X = AI边X
绘边Y = AI边Y
副桌 = 真
} 否则 {
绘边X = 边X
绘边Y = 边Y
副桌 = 假
}

整数 gy = 1
当 (gy <= 内H) {
整数 gx = 1
当 (gx <= 内W) {
整数 px = 绘边X + gx * CELL
整数 py = 绘边Y + gy * CELL
如果 (是座位(gx, gy)) {
如果 (是AI桌) {
画布1.置填充("#0E1820")
画布1.置笔触("#1A3040", 1)
} 否则 {
画布1.置填充("#121826")
画布1.置笔触("#1E2638", 1)
}
画布1.画矩形(px + 2, py + 2, CELL - 4, CELL - 4)
}
gx = gx + 1
}
gy = gy + 1
}

整数 pv = 到整数(now) % 800
小数 缩放选 = 1.0
如果 (pv < 400) {
缩放选 = 1.0 + 到小数(pv) / 3500.0
} 否则 {
缩放选 = 1.12 - 到小数(pv - 400) / 3500.0
}

整数 y = 0
循环 (GH, y) {
整数 x = 0
循环 (GW, x) {
整数 v = 读格(x, y)
如果 (v == 0) { 继续() }
整数 px = 绘边X + x * CELL
整数 py = 绘边Y + y * CELL
逻辑 是选 = 假
逻辑 是提示 = 假
逻辑 是AI瞄 = 假
如果 (是AI桌 == 假) {
是选 = (选x == x 且 选y == y)
是提示 = (提示x1 == x 且 提示y1 == y) 或 (提示x2 == x 且 提示y2 == y)
} 否则 {
是AI瞄 = (AIx1表[当前AI] == x 且 AIy1表[当前AI] == y) 或 (AIx2表[当前AI] == x 且 AIy2表[当前AI] == y)
}
小数 sc = 1.0
文本 border = "#B0BEC5"
整数 bw = 1
如果 (是选) {
sc = 缩放选
border = "#FFEA00"
bw = 4
} 否则如果 (是提示) {
sc = 1.08
border = "#EA80FC"
bw = 4
} 否则如果 (是AI瞄) {
sc = 1.06
border = "#40C4FF"
bw = 3
}
画一枚(px, py, v, bw, border, sc)
}
}
副桌 = 假
}

函数 画AI进度面板 (小数 now) {
如果 (AI开 == 假 或 通关中) { 返回 () }
确保字体()
整数 pw = 200
整数 ph = 78
整数 px = 画W / 2 - pw / 2
整数 py = 画H - ph - 6
画布1.置笔触("#6640C4FF", 1)
画布1.置填充("#CC0D1520")
画布1.画矩形(px, py, pw, ph)
画布1.置字体(字步时)
画布1.置填充("#FF4DD0E1")
画布1.画文本(px + 8, py + 4, "双桌进度")
画布1.置填充("#FFFFD54F")
画布1.画文本(px + 80, py + 4, "你" + 到文本(玩家消对) + " : " + 到文本(AI消对) + "AI")

整数 barW = pw - 16
整数 maxP = 开局对数
如果 (maxP < 1) { maxP = 1 }
整数 youW = barW * 玩家消对 / maxP
整数 aiW = barW * AI消对 / maxP
如果 (youW > barW) { youW = barW }
如果 (aiW > barW) { aiW = barW }
画布1.置笔触("#00FFFFFF", 0)
画布1.置填充("#332A3142")
画布1.画矩形(px + 8, py + 22, barW, 7)
画布1.置填充("#88FFD54F")
画布1.画矩形(px + 8, py + 22, youW, 7)
画布1.置填充("#332A3142")
画布1.画矩形(px + 8, py + 33, barW, 7)
画布1.置填充("#8840C4FF")
画布1.画矩形(px + 8, py + 33, aiW, 7)

小数 left = AI下次 - now
如果 (left < 0) { left = 0 }
整数 fill = barW
如果 (AI间隔 > 0) {
fill = barW - 到整数(left * 到小数(barW) / 到小数(AI间隔))
}
如果 (fill < 0) { fill = 0 }
如果 (fill > barW) { fill = barW }
画布1.置填充("#332A3142")
画布1.画矩形(px + 8, py + 48, barW, 8)
画布1.置填充("#FF26C6DA")
画布1.画矩形(px + 8, py + 48, fill, 8)
画布1.置填充("#FF90A4AE")
整数 sec = 到整数(left / 1000.0) + 1
如果 (left <= 0) { sec = 0 }
画布1.画文本(px + 8, py + 58, "AI下手 " + 到文本(sec) + "s · 你剩" + 到文本(剩余) + " AI剩" + 到文本(AI剩余))
}

函数 按钮开房.单击 (整数 X, 整数 Y) {
房服.客户进入(&服务进入)
房服.数据到达(&服务数据)
房服.客户离开(&服务离开)
如果 (房服.启动(联机端口, "0.0.0.0")) {
联机角色 = 1
AI开 = 假
AI数 = 0
刷新AI按钮()
标签状态.标题 = "已开房 端口" + 到文本(联机端口) + " · 对方点加入连你的IP"
} 否则 {
标签状态.标题 = "开房失败:" + 房服.取错误()
}
}

函数 客进入 (整数 连接ID, 文本 IP, 整数 端口, 文本 IP端口) {
联机连接中 = 假
联机角色 = 2
AI开 = 假
AI数 = 0
刷新AI按钮()
房客.发送("HELLO|JOIN\n")
标签状态.标题 = "已加入 " + IP端口 + "(棋盘同步可继续扩展)"
}

函数 客失败 (文本 原因) {
联机连接中 = 假
联机角色 = 0
标签状态.标题 = "加入失败:" + 原因 + " · 先让对方开房"
}

函数 按钮加入.单击 (整数 X, 整数 Y) {
// 默认连本机;真机对战把 联机IP 改成房主局域网 IP
// 连接走后台,避免界面线程卡在连接_同步里(端口不通时最长约 30 秒,窗口会未响应)
如果 (联机连接中) { 返回 () }
联机连接中 = 真
房客.置超时(2000)
房客.客户进入(&客进入)
房客.连接失败(&客失败)
标签状态.标题 = "正在连接 " + 联机IP + ":" + 到文本(联机端口)
如果 (房客.连接(联机IP, 联机端口) == 假) {
联机连接中 = 假
标签状态.标题 = "无法发起连接:" + 房客.取错误()
}
}

函数 画布1.鼠标按下 (整数 X, 整数 Y, 逻辑 按下) {
如果 (按下 == 假) { 返回 () }
如果 (倒计时中 或 已就绪 == 假) { 返回 () }
// AI对战只能点左桌(你的桌)
如果 (AI数 > 0 且 X >= 右起) { 返回 () }
点格(像素到格X(X), 像素到格Y(Y))
}

函数 画布1.绘制 () {
确保板()
同步画布尺寸()
绘.绑画布(画布1)
小数 now = 取高精度毫秒()

// 未开始:只画待命页
如果 (已就绪 == 假) {
画待命界面(now)
返回 ()
}

画布1.置填充("#070910")
画布1.置笔触("#070910", 0)
画布1.画矩形(0, 0, 画W, 画H)
// 高连击闪白:只闪你的桌,不盖 AI 小屏
如果 (now < 闪屏消) {
画布1.置填充("#33FFFFFF")
画布1.置笔触("#00FFFFFF", 0)
画布1.画矩形(边X, 边Y, 玩家桌宽(), 玩家桌高())
}

// 步时进度条(细条 + 小字,避免抢视觉)
如果 (通关中 == 假 且 已就绪 且 步限秒 > 0 且 倒计时中 == 假) {
确保字体()
整数 barW = 画W - 40
整数 fillW = barW * 步剩秒 / 步限秒
如果 (fillW < 0) { fillW = 0 }
如果 (fillW > barW) { fillW = barW }
画布1.置填充("#2A3142")
画布1.置笔触("#00FFFFFF", 0)
画布1.画矩形(20, 4, barW, 6)
文本 barCol = "#66BB6A"
如果 (步剩秒 <= 5) {
barCol = "#EF5350"
} 否则如果 (步剩秒 <= 步限秒 / 2) {
barCol = "#FFA726"
}
画布1.置填充(barCol)
画布1.画矩形(20, 4, fillW, 6)
画布1.置字体(字步时)
画布1.置填充("#FF90A4AE")
画布1.画文本(22, 12, 到文本(步剩秒) + "s " + 难度名() + " " + 到文本(内W) + "×" + 到文本(内H) + " " + 到文本(种数) + "种")
}

// 画图块前强制小字号,防止连击大字污染残留
确保字体()
画布1.置字体(字图块)

还原玩家格()
画桌标题(边X, 边Y, "你 · " + 到文本(对局人数()) + "人", "#FFFFD54F", 剩余)
画一桌图块(假, now)

如果 (AI数 > 0) {
画布1.置笔触("#3340C4FF", 2)
画布1.画线(右起, 4, 右起, 画H - 4)
整数 si = 0
当 (si < AI数) {
定位AI屏(si)
画布1.置笔触("#224055", 1)
画布1.置填充("#080C12")
画布1.画矩形(右起 + (si % AI列) AI格区宽 + 2, (si / AI列) AI格区高 + 2, AI格区宽 - 4, AI格区高 - 4)
定位AI屏(si)
画一桌图块(真, now)
确保字体()
画布1.置字体(字步时)
画布1.置填充("#FF4DD0E1")
画布1.画文本(AI边X, AI边Y - 14, "AI" + 到文本(si + 1) + " " + 到文本(AI分表[si]) + " ×" + 到文本(AI连击表[si]) + " 剩" + 到文本(AI剩表[si]))
如果 (通关中 == 假 且 AIx1表[si] >= 0) {
定位AI屏(si)
画AI鬼影格(AIx1表[si], AIy1表[si], now)
画AI鬼影格(AIx2表[si], AIy2表[si], now)
}
如果 (AI消中表[si]) {
定位AI屏(si)
小数 p = (now - AI消起表[si]) / 220.0
如果 (p < 0) { p = 0 }
如果 (p > 1) { p = 1 }
小数 sc = 1.1 - p * 0.7
画一枚(绘边X + AI消x1表[si] CELL, 绘边Y + AI消y1表[si] CELL, AI消种表[si], 2, "#AA40C4FF", sc)
画一枚(绘边X + AI消x2表[si] CELL, 绘边Y + AI消y2表[si] CELL, AI消种表[si], 2, "#AA40C4FF", sc)
}
// AI 连击字:落在该 AI 小屏中心
如果 (通关中 == 假 且 AI消中表[si] 且 AI连击表[si] >= 2) {
定位AI屏(si)
整数 acx = AI边X + (内W + 2) * AI格 / 2 - 28
整数 acy = AI边Y + (内H + 2) * AI格 / 2 - 10
画布1.置字体(字步时)
画描边字(acx, acy, "×" + 到文本(AI连击表[si]), "#FF69F0AE", "#CC000000")
}
si = si + 1
}
}

还原玩家格()
如果 (消中) {
小数 p = (now - 消起) / 到小数(消毫秒)
如果 (p < 0) { p = 0 }
如果 (p > 1) { p = 1 }
小数 sc = 1.15 - p * 0.9
文本 glow = "#AAFFEA00"
画一枚(绘边X + 消x1 CELL, 绘边Y + 消y1 CELL, 消种, 3, glow, sc)
画一枚(绘边X + 消x2 CELL, 绘边Y + 消y2 CELL, 消种, 3, glow, sc)
整数 cx1 = 格中心X(消x1)
整数 cy1 = 格中心Y(消y1)
整数 cx2 = 格中心X(消x2)
整数 cy2 = 格中心Y(消y2)
整数 base = 8 + 消连击 * 3
整数 r = 到整数(到小数(base) + p 到小数(22 + 消连击 4))
画布1.置笔触("#00FFFFFF", 0)
画布1.置填充("#66FFEA00")
画布1.画椭圆(cx1 - r, cy1 - r, r 2, r 2)
画布1.画椭圆(cx2 - r, cy2 - r, r 2, r 2)
画布1.置填充("#44FFFFFF")
整数 r2 = r / 2
画布1.画椭圆(cx1 - r2, cy1 - r2, r2 2, r2 2)
画布1.画椭圆(cx2 - r2, cy2 - r2, r2 2, r2 2)
// 四散火花
整数 k = 0
循环 (8, k) {
整数 ang = k 45 + 到整数(p 90)
整数 dist = 到整数(10 + p 到小数(28 + 消连击 3))
// 用简单正交近似代替三角函数
整数 ox = 0
整数 oy = 0
如果 (k == 0 或 k == 7 或 k == 1) { ox = dist }
如果 (k == 3 或 k == 4 或 k == 5) { ox = 0 - dist }
如果 (k == 1 或 k == 2 或 k == 3) { oy = 0 - dist }
如果 (k == 5 或 k == 6 或 k == 7) { oy = dist }
画布1.置填充("#FFFFF59D")
画布1.画椭圆(cx1 + ox - 3, cy1 + oy - 3, 6, 6)
画布1.画椭圆(cx2 + ox - 3, cy2 + oy - 3, 6, 6)
}
}

// 折线电光
如果 (线点数 >= 2 且 now < 线消时) {
画布1.置笔触("#66FF6F00", 14)
整数 i = 0
循环 (线点数 - 1, i) {
画布1.画线(格中心X(线x[i]), 格中心Y(线y[i]), 格中心X(线x[i + 1]), 格中心Y(线y[i + 1]))
}
画布1.置笔触("#AAFFEA00", 8)
i = 0
循环 (线点数 - 1, i) {
画布1.画线(格中心X(线x[i]), 格中心Y(线y[i]), 格中心X(线x[i + 1]), 格中心Y(线y[i + 1]))
}
画布1.置笔触("#FFFFFFFF", 3)
i = 0
循环 (线点数 - 1, i) {
画布1.画线(格中心X(线x[i]), 格中心Y(线y[i]), 格中心X(线x[i + 1]), 格中心Y(线y[i + 1]))
}
画粒子沿线()
i = 0
循环 (线点数, i) {
整数 cx = 格中心X(线x[i])
整数 cy = 格中心Y(线y[i])
画布1.置填充("#FFFFEA00")
画布1.置笔触("#FFFFFFFF", 2)
画布1.画椭圆(cx - 7, cy - 7, 14, 14)
}
}

// 连击大字:落在你的桌中心(分屏后不再用整屏正中)
如果 (连击字 != "" 且 now < 连击字消 且 通关中 == 假) {
确保字体()
文本 col = 连击色()
画布1.置字体(字连击)
整数 cx = 玩家桌中心X() - 80
整数 cy = 玩家桌中心Y() - 40
如果 (消连击 >= 3) {
画描边字(cx + 20, cy - 40, "COMBO", col, "#CC000000")
}
画描边字(cx - 40, cy, 连击字, col, "#CC000000")
}

// 结局面板:相对你的桌居中,并列出全员得分
如果 (通关中) {
确保字体()
小数 age = now - 通关起
逻辑 是失败 = 假
如果 (结局 == 2 或 结局 == 4) { 是失败 = 真 }
整数 榜行 = 1 + AI数
整数 榜高 = 榜行 * 20
如果 (榜高 < 20) { 榜高 = 20 }

如果 (是失败) {
// —— 失败:只压暗你的桌 + 裂痕 X + 结算卡 ——
整数 px0 = 边X
整数 py0 = 边Y
整数 pw0 = 玩家桌宽()
整数 ph0 = 玩家桌高()
画布1.置笔触("#00FFFFFF", 0)
画布1.置填充("#CC0A0608")
画布1.画矩形(px0, py0, pw0, ph0)

画布1.置笔触("#66FF1744", 4)
画布1.画线(px0 + 20, py0 + 30, px0 + pw0 - 20, py0 + ph0 - 40)
画布1.置笔触("#44FF1744", 2)
画布1.画线(px0 + 40, py0 + 20, px0 + pw0 - 40, py0 + ph0 - 50)

整数 shake = 到整数(age) % 200
如果 (shake > 100) { shake = 200 - shake }
整数 sx = shake / 25
整数 mx = 玩家桌中心X()
整数 my = 玩家桌中心Y() - 40
整数 arm = pw0 / 4
如果 (arm < 60) { arm = 60 }
如果 (arm > 140) { arm = 140 }
画布1.置笔触("#AAFF1744", 10)
画布1.画线(mx - arm - sx, my - arm / 2, mx + arm + sx, my + arm / 2)
画布1.画线(mx + arm + sx, my - arm / 2, mx - arm - sx, my + arm / 2)
画布1.置笔触("#FFFF5252", 4)
画布1.画线(mx - arm + 8 - sx, my - arm / 2 + 8, mx + arm - 8 + sx, my + arm / 2 - 8)
画布1.画线(mx + arm - 8 + sx, my - arm / 2 + 8, mx - arm + 8 - sx, my + arm / 2 - 8)

整数 cw = 460
整数 ch = 130 + 榜高
如果 (ch > ph0 - 20) { ch = ph0 - 20 }
整数 cx = mx - cw / 2
整数 cy = py0 + ph0 - ch - 12
如果 (cy < py0 + 8) { cy = py0 + 8 }
画布1.置填充("#EE1A1216")
画布1.置笔触("#88B71C1C", 2)
画布1.画矩形(cx, cy, cw, ch)
画布1.置填充("#44B71C1C")
画布1.置笔触("#00FFFFFF", 0)
画布1.画矩形(cx, cy, cw, 6)

画布1.置字体(字通关大)
画描边字(cx + 150, cy + 10, 通关字, "#FFFF8A80", "#AA000000")
画布1.置字体(字通关中)
画描边字(cx + 36, cy + 52, 通关副, "#FFB0BEC5", "#88000000")
画结算榜(cx, cy + 82, cw)
画布1.置字体(字通关小)
画布1.置填充("#FFFF5252")
画布1.画文本(cx + 100, cy + ch - 24, "本局已结束 · 点「新局」重开")
} 否则如果 (结局 == 3) {
整数 mx = 玩家桌中心X()
整数 my = 玩家桌中心Y()
画布1.置笔触("#00FFFFFF", 0)
画布1.置填充("#880A1520")
画布1.画矩形(边X, 边Y, 玩家桌宽(), 玩家桌高())
整数 cw = 460
整数 ch = 150 + 榜高
整数 cx = mx - cw / 2
整数 cy = my - ch / 2
画布1.置填充("#EE15202A")
画布1.置笔触("#8840C4FF", 3)
画布1.画矩形(cx, cy, cw, ch)
画布1.置填充("#FF40C4FF")
画布1.置笔触("#00FFFFFF", 0)
画布1.画矩形(cx + 28, cy + 52, cw - 56, 3)
画布1.置字体(字通关大)
画描边字(cx + 150, cy + 12, 通关字, "#FF80D8FF", "#CC000000")
画布1.置字体(字通关中)
画描边字(cx + 48, cy + 58, 通关副, "#FFFFFFFF", "#88000000")
画结算榜(cx, cy + 90, cw)
画布1.置字体(字通关小)
画布1.置填充("#FF40C4FF")
画布1.画文本(cx + 120, cy + ch - 26, "▶ 点「新局」再来一盘")
} 否则 {
整数 pulse = 到整数(age) % 1000
整数 breath = pulse
如果 (breath > 500) { breath = 1000 - breath }
整数 glow = 8 + breath / 40
整数 mx = 玩家桌中心X()
整数 my = 玩家桌中心Y()

画布1.置笔触("#00FFFFFF", 0)
画布1.置填充("#44000000")
画布1.画矩形(边X, 边Y, 玩家桌宽(), 24)
画布1.画矩形(边X, 边Y + 玩家桌高() - 24, 玩家桌宽(), 24)

画布1.置填充("#33FFEA00")
画布1.画椭圆(mx - 110 - glow, my - 70 - glow, 220 + glow 2, 140 + glow 2)

整数 cw = 460
整数 ch = 160 + 榜高
整数 cx = mx - cw / 2
整数 cy = my - ch / 2
画布1.置填充("#33000000")
画布1.画矩形(cx + 5, cy + 6, cw, ch)
画布1.置填充("#EE1A2233")
画布1.置笔触("#88FFD54F", 3)
画布1.画矩形(cx, cy, cw, ch)
画布1.置笔触("#FFFFD54F", 1)
画布1.画矩形(cx + 8, cy + 8, cw - 16, ch - 16)
画布1.置填充("#FFFFD54F")
画布1.置笔触("#00FFFFFF", 0)
画布1.画矩形(cx + 28, cy + 54, cw - 56, 3)

画布1.置字体(字通关大)
画描边字(cx + 140, cy + 12, 通关字, "#FFFFF176", "#CC000000")
画布1.置字体(字通关中)
画描边字(cx + 40, cy + 58, 通关副, "#FFFFFFFF", "#88000000")
画结算榜(cx, cy + 90, cw)
画布1.置字体(字通关小)
画布1.置填充("#FFFFD54F")
画布1.画文本(cx + 110, cy + ch - 26, "▶ 点「新局」再来一盘")
}
}

// 开场 3·2·1·GO 盖在棋盘上
如果 (倒计时中) {
画开场倒计时(now)
}
}

s7.png

s6.png

附件下载

  • .zip (4,246.5 KB · 下载 0)