2023-04-23 13:10:07 +00:00
|
|
|
|
local heg_description = [==[
|
|
|
|
|
# 国战简介
|
|
|
|
|
|
|
|
|
|
《三国杀·国战》是一款可以支持2\~12人(线上版4\~8人)同时参与的桌面卡牌游戏。在游戏中,每名玩家将甄选三国时代中包括魏国、蜀国、吴国、群雄在内的各位名将,组成自己的战斗团队,利用他们珠联璧合的组合技能发起进攻,消灭其他各方势力,赢得最终的胜利。
|
|
|
|
|
|
|
|
|
|
## 准备游戏
|
|
|
|
|
|
|
|
|
|
**挑选武将:**
|
|
|
|
|
|
|
|
|
|
发给每位玩家四张武将牌(会员5张),选出两张势力相同的武将牌并背面朝上放置,称为“暗置”(参考段落“明置和暗置”)。
|
|
|
|
|
|
|
|
|
|
靠近体力牌的武将视为副将,另一个视为主将。
|
|
|
|
|
|
|
|
|
|
游戏中,每名玩家扮演的角色由两张武将牌组成。
|
|
|
|
|
|
|
|
|
|
**分发体力牌:**
|
|
|
|
|
|
|
|
|
|
每位玩家拿取一张体力牌,翻到对应体力上限的一面,放置在武将牌旁边。体力上限为两张武将牌上的完整阴阳鱼的数量之和。两个单独的阴阳鱼可以组成一个完整的阴阳鱼。
|
|
|
|
|
|
|
|
|
|
注:当一名角色的两张武将牌第一次均明置时,若其武将牌上有单独的阴阳鱼没有组成1 点体力,则他可以立即摸一张牌。
|
|
|
|
|
|
|
|
|
|
扣减体力时,用主将挡住扣减的体力,露出当前体力值。
|
|
|
|
|
|
|
|
|
|
## 进行游戏
|
|
|
|
|
|
|
|
|
|
随机选择一名玩家作为起始玩家。由该玩家开始,按逆时针方向以回合的方式进行。即每名玩家有一个自己的回合,一名玩家的回合结束后, 右边玩家的回合开始,依次轮流进行。
|
|
|
|
|
|
|
|
|
|
每个玩家的回合可以分为六个阶段:
|
|
|
|
|
|
|
|
|
|
回合开始阶段 -> 判定阶段 -> 摸牌阶段 -> 出牌阶段 -> 弃牌阶段 -> 回合结束阶段
|
|
|
|
|
|
|
|
|
|
**回合开始阶段:**
|
|
|
|
|
|
|
|
|
|
有些技能可以在此阶段发动。你的暗置的武将牌也可于此阶段明置。
|
|
|
|
|
|
|
|
|
|
**判定阶段:**
|
|
|
|
|
|
|
|
|
|
若你的面前横置着延时类锦囊,你必须依次对这些延时类锦囊进行判定。若有多张延时类锦囊,先判定最后放置的那张,然后以此类推。
|
|
|
|
|
|
|
|
|
|
**摸牌阶段:**
|
|
|
|
|
|
|
|
|
|
你从牌堆顶摸两张牌。
|
|
|
|
|
|
|
|
|
|
**出牌阶段:**
|
|
|
|
|
|
|
|
|
|
你可以使用任意张牌,但必须遵守以下两条规则:
|
|
|
|
|
|
|
|
|
|
1. 每个出牌阶段仅限使用一次【杀】。
|
|
|
|
|
2. 任何一名角色面前的判定区里不能放有两张同名的牌。
|
|
|
|
|
|
|
|
|
|
每使用一张牌,即执行该牌之效果,详见“游戏牌详解”。如无特殊说明,游戏牌在使用后均需置入弃牌堆。
|
|
|
|
|
|
|
|
|
|
**弃牌阶段:**
|
|
|
|
|
|
|
|
|
|
在出牌阶段中,不想出或没法出牌时,就进入弃牌阶段。此时检查你的手牌数是否超出你当前的体力值( 你的手牌上限等于你当前的体力值), 每超出一张,须弃置一张手牌。
|
|
|
|
|
|
|
|
|
|
**回合结束阶段:**
|
|
|
|
|
|
|
|
|
|
有些技能可以在此阶段发动。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 角色死亡
|
|
|
|
|
|
|
|
|
|
当一名角色的体力降到0 时,即进入濒死状态,除非该角色在此时使用【酒】,或有角色使用【桃】来挽救该角色,否则该角色死亡。
|
|
|
|
|
|
|
|
|
|
死亡的角色明置其武将牌,弃置该角色所有牌及其判定区里的牌,然后执行奖惩。
|
|
|
|
|
|
|
|
|
|
奖惩方式:
|
|
|
|
|
|
|
|
|
|
1. 已经确定势力的角色杀死相同势力的角色须弃置所有手牌和装备区的牌;
|
|
|
|
|
2. 已经确定势力的角色杀死不同势力的角色,从牌堆摸取等同于该势力人数(包括刚刚杀死的角色)张牌。
|
|
|
|
|
|
|
|
|
|
例:“蜀”势力角色杀死了一名“魏”势力角色,此时还有其他两名“魏”势力角色存活,则该“蜀”势力角色摸三张牌。
|
|
|
|
|
|
|
|
|
|
注:若被杀死的角色还没有明置武将牌(即没确定势力),则须明置验明势力。 没有势力的角色(即武将牌没有明置的角色)杀死其他角色没有奖惩。
|
|
|
|
|
|
|
|
|
|
## 胜负结算
|
|
|
|
|
|
|
|
|
|
玩家的游戏目标与势力有关:消灭所有与自己不同势力的角色。
|
|
|
|
|
|
|
|
|
|
特殊的:野心家需要消灭所有其他角色。
|
|
|
|
|
|
|
|
|
|
当全场所有角色均确定势力后, 才可以进行胜利条件的判断:
|
|
|
|
|
当全场只剩下一种势力存活时, 该势力的角色获胜( 没有确定势力的角色无法取得游戏胜利, 即使与存活的其他角色为同一势力)。
|
|
|
|
|
|
|
|
|
|
## 暗将规则
|
|
|
|
|
|
|
|
|
|
处于暗置状态的武将牌没有任何武将技能、性别以及势力。当暗置的武将牌发动技能时,将武将牌明置,然后发动相应的技能。
|
|
|
|
|
|
|
|
|
|
暗置的武将牌只有两个时机可以将武将牌明置:1. 回合开始阶段开始时;2. 发动武将牌的技能时。
|
|
|
|
|
|
|
|
|
|
例:郭嘉、司马懿等,受到伤害后发动技能时明置武将牌;
|
|
|
|
|
马超、黄忠等,使用【杀】指定一名角色为目标后,发动技能并明置武将牌;
|
|
|
|
|
孙权、甘宁等,在出牌阶段发动技能时明置武将牌;
|
|
|
|
|
|
|
|
|
|
没有明置武将牌的角色没有性别,任何与性别有关的技能和武器效果均不能对其发动。
|
|
|
|
|
|
|
|
|
|
有一张武将牌明置时,角色性别与明置的武将牌相同。当一名角色的两张武将牌均亮明后,性别与主将的武将牌相同。
|
|
|
|
|
|
|
|
|
|
没有明置武将牌的角色没有势力,明置一张武将牌后确定势力:与武将牌左上角所示的势力相同,或成为野心家。野心家用“野心家牌”表示。
|
|
|
|
|
|
|
|
|
|
野心家规则:
|
|
|
|
|
|
|
|
|
|
当一名角色明置武将牌确定势力时,若该势力的角色超过了游戏总玩家数的一半,则他视为野心家,拿取一张野心家牌表示。若之后仍然有该势力的角色明置武将牌,均视为野心家。野心家为单独的一种势力,与其他角色的势力均不同。他(们)需要杀死所有其他角色,成为唯一的存活者。
|
|
|
|
|
|
|
|
|
|
注意:野心家与野心家之间也是不同势力。
|
|
|
|
|
|
|
|
|
|
例:
|
|
|
|
|
|
|
|
|
|
★ 6 人、7 人游戏时,当出现第四名同势力角色时,该角色及之后明置的该势力角色均成为野心家。
|
|
|
|
|
|
|
|
|
|
★ 8 人、9 人游戏时,当出现第五名同势力角色时,该角色及之后明置的该势力角色均成为野心家。
|
|
|
|
|
|
|
|
|
|
## 珠联璧合
|
|
|
|
|
|
|
|
|
|
珠联璧合表示了部分武将之间的特殊联系。
|
|
|
|
|
|
|
|
|
|
武将牌中下方的其他武将姓名表示了可以和此武将牌形成珠联璧合的其他武将。
|
|
|
|
|
|
|
|
|
|
若你选择的两张武将牌形成珠联璧合,则在第一次两张武将牌均明置时,可以立即选择摸两张牌或回复1 点体力。
|
|
|
|
|
|
|
|
|
|
若触发珠联璧合时正在进行其他事件的结算,则先进行“珠联璧合”的选择,再继续结算该事件。
|
|
|
|
|
]==]
|
|
|
|
|
|
|
|
|
|
local heg
|
|
|
|
|
|
|
|
|
|
---@class HegLogic: GameLogic
|
|
|
|
|
local HegLogic = {}
|
|
|
|
|
|
|
|
|
|
function HegLogic:assignRoles()
|
|
|
|
|
local room = self.room
|
|
|
|
|
for _, p in ipairs(room.players) do
|
|
|
|
|
p.role_shown = true
|
|
|
|
|
p.role = "hidden"
|
|
|
|
|
room:broadcastProperty(p, "role")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- for adjustSeats
|
|
|
|
|
room.players[1].role = "lord"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function HegLogic:chooseGenerals()
|
|
|
|
|
local room = self.room
|
|
|
|
|
local generalNum = math.max(room.settings.generalNum, 6)
|
|
|
|
|
|
|
|
|
|
local lord = room:getLord()
|
|
|
|
|
room.current = lord
|
|
|
|
|
lord.role = "hidden"
|
|
|
|
|
|
|
|
|
|
local nonlord = room.players
|
|
|
|
|
local generals = Fk:getGeneralsRandomly(#nonlord * generalNum)
|
|
|
|
|
-- table.shuffle(generals)
|
|
|
|
|
for _, p in ipairs(nonlord) do
|
|
|
|
|
local arg = { map = table.map }
|
|
|
|
|
for i = 1, generalNum do
|
|
|
|
|
table.insert(arg, table.remove(generals, 1))
|
|
|
|
|
end
|
|
|
|
|
table.sort(arg, function(a, b) return a.kingdom > b.kingdom end)
|
|
|
|
|
|
|
|
|
|
for idx, _ in ipairs(arg) do
|
|
|
|
|
if arg[idx].kingdom == arg[idx + 1].kingdom then
|
|
|
|
|
p.default_reply = { arg[idx].name, arg[idx + 1].name }
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
arg = arg:map(function(g) return g.name end)
|
|
|
|
|
p.request_data = json.encode({ arg, 2, true })
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
room:notifyMoveFocus(nonlord, "AskForGeneral")
|
|
|
|
|
room:doBroadcastRequest("AskForGeneral", nonlord)
|
|
|
|
|
for _, p in ipairs(nonlord) do
|
|
|
|
|
local general, deputy
|
|
|
|
|
if p.general == "" and p.reply_ready then
|
|
|
|
|
local generals = json.decode(p.client_reply)
|
|
|
|
|
general = generals[1]
|
|
|
|
|
deputy = generals[2]
|
|
|
|
|
room:setPlayerGeneral(p, general, true)
|
|
|
|
|
room:setDeputyGeneral(p, deputy)
|
|
|
|
|
else
|
|
|
|
|
general = p.default_reply[1]
|
|
|
|
|
deputy = p.default_reply[2]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
p:setMark("__heg_general", general)
|
|
|
|
|
p:setMark("__heg_deputy", deputy)
|
|
|
|
|
p:doNotify("SetPlayerMark", json.encode{ p.id, "__heg_general", general})
|
|
|
|
|
p:doNotify("SetPlayerMark", json.encode{ p.id, "__heg_deputy", deputy})
|
|
|
|
|
|
|
|
|
|
room:setPlayerGeneral(p, "anjiang", true)
|
|
|
|
|
room:setDeputyGeneral(p, "anjiang")
|
|
|
|
|
|
|
|
|
|
p.default_reply = ""
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function HegLogic:broadcastGeneral()
|
|
|
|
|
local room = self.room
|
|
|
|
|
local players = room.players
|
|
|
|
|
|
|
|
|
|
for _, p in ipairs(players) do
|
|
|
|
|
assert(p.general ~= "")
|
|
|
|
|
local general = Fk.generals[p:getMark("__heg_general")]
|
|
|
|
|
local deputy = Fk.generals[p:getMark("__heg_deputy")]
|
|
|
|
|
p.maxHp = math.floor((deputy.maxHp + general.maxHp) / 2)
|
|
|
|
|
p.hp = math.floor((deputy.hp + general.hp) / 2)
|
|
|
|
|
-- p.shield = math.min(general.shield + deputy.shield, 5)
|
|
|
|
|
p.shield = 0
|
|
|
|
|
-- TODO: setup AI here
|
|
|
|
|
|
|
|
|
|
room:broadcastProperty(p, "general")
|
|
|
|
|
room:broadcastProperty(p, "deputyGeneral")
|
|
|
|
|
room:broadcastProperty(p, "maxHp")
|
|
|
|
|
room:broadcastProperty(p, "hp")
|
|
|
|
|
room:broadcastProperty(p, "shield")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function HegLogic:attachSkillToPlayers()
|
|
|
|
|
local room = self.room
|
|
|
|
|
local players = room.players
|
|
|
|
|
|
|
|
|
|
room:handleAddLoseSkills(players[1], "#_heg_invalid", nil, false, true)
|
|
|
|
|
|
|
|
|
|
local addHegSkills = function(player, skillName)
|
|
|
|
|
local skill = Fk.skills[skillName]
|
|
|
|
|
if skill.lordSkill and (player.role ~= "lord" or #room.players < 5) then
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
2023-08-09 14:25:15 +00:00
|
|
|
|
player:addFakeSkill(skill)
|
2023-04-23 13:10:07 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
for _, p in ipairs(room.alive_players) do
|
|
|
|
|
local general = Fk.generals[p:getMark("__heg_general")]
|
|
|
|
|
local skills = general.skills
|
|
|
|
|
for _, s in ipairs(skills) do
|
|
|
|
|
addHegSkills(p, s.name)
|
|
|
|
|
end
|
|
|
|
|
for _, sname in ipairs(general.other_skills) do
|
|
|
|
|
addHegSkills(p, sname)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local deputy = Fk.generals[p:getMark("__heg_deputy")]
|
|
|
|
|
if deputy then
|
|
|
|
|
skills = deputy.skills
|
|
|
|
|
for _, s in ipairs(skills) do
|
|
|
|
|
addHegSkills(p, s.name)
|
|
|
|
|
end
|
|
|
|
|
for _, sname in ipairs(deputy.other_skills) do
|
|
|
|
|
addHegSkills(p, sname)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
room:setTag("SkipNormalDeathProcess", true)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local heg_getlogic = function()
|
|
|
|
|
local h = GameLogic:subclass("HegLogic")
|
|
|
|
|
for k, v in pairs(HegLogic) do
|
|
|
|
|
h[k] = v
|
|
|
|
|
end
|
|
|
|
|
return h
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local heg_invalid = fk.CreateInvaliditySkill{
|
|
|
|
|
name = "#_heg_invalid",
|
|
|
|
|
invalidity_func = function(self, player, skill)
|
|
|
|
|
end,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
local function getWinnerHeg(victim)
|
|
|
|
|
local room = victim.room
|
|
|
|
|
local alive = room.alive_players
|
|
|
|
|
if #alive == 1 then
|
|
|
|
|
local p = alive[1]
|
|
|
|
|
p:revealGeneral(false)
|
|
|
|
|
p:revealGeneral(true)
|
|
|
|
|
return p.kingdom
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local winner = alive[1].kingdom
|
|
|
|
|
if winner == "unknown" then return "" end
|
|
|
|
|
for _, p in ipairs(alive) do
|
|
|
|
|
if p.kingdom ~= winner or p.kingdom == "wild" then
|
|
|
|
|
return ""
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return winner
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local heg_rule = fk.CreateTriggerSkill{
|
|
|
|
|
name = "#heg_rule",
|
|
|
|
|
priority = 0.001,
|
|
|
|
|
events = {fk.TurnStart, fk.GameOverJudge, fk.BuryVictim},
|
|
|
|
|
can_trigger = function(self, event, target, player, data)
|
|
|
|
|
return target == player
|
|
|
|
|
end,
|
|
|
|
|
on_trigger = function(self, event, target, player, data)
|
|
|
|
|
local room = player.room
|
|
|
|
|
if event == fk.TurnStart then
|
|
|
|
|
local choices = {}
|
|
|
|
|
if player.general == "anjiang" then
|
|
|
|
|
table.insert(choices, "revealMain")
|
|
|
|
|
end
|
|
|
|
|
if player.deputyGeneral == "anjiang" then
|
|
|
|
|
table.insert(choices, "revealDeputy")
|
|
|
|
|
end
|
|
|
|
|
if #choices == 0 then return end
|
|
|
|
|
if #choices == 2 then table.insert(choices, "revealAll") end
|
|
|
|
|
table.insert(choices, "Cancel")
|
|
|
|
|
|
|
|
|
|
local choice = room:askForChoice(player, choices, self.name)
|
|
|
|
|
if choice == "revealMain" then player:revealGeneral(false)
|
|
|
|
|
elseif choice == "revealDeputy" then player:revealGeneral(true)
|
|
|
|
|
elseif choice == "revealAll" then
|
|
|
|
|
player:revealGeneral(false)
|
|
|
|
|
player:revealGeneral(true)
|
|
|
|
|
end
|
|
|
|
|
elseif event == fk.GameOverJudge then
|
|
|
|
|
player:revealGeneral(false)
|
|
|
|
|
player:revealGeneral(true)
|
|
|
|
|
local winner = getWinnerHeg(player)
|
|
|
|
|
if winner ~= "" then
|
|
|
|
|
room:gameOver("hidden")
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
room:setTag("SkipGameRule", true)
|
|
|
|
|
elseif event == fk.BuryVictim then
|
|
|
|
|
local damage = data.damage
|
|
|
|
|
if damage and damage.from then
|
|
|
|
|
local killer = damage.from
|
|
|
|
|
if killer.kingdom == "unknown" then return end
|
|
|
|
|
|
|
|
|
|
local victim = damage.to
|
2023-04-27 06:19:36 +00:00
|
|
|
|
if killer.kingdom ~= "wild" and killer.kingdom == victim.kingdom then
|
2023-04-23 13:10:07 +00:00
|
|
|
|
killer:throwAllCards("he")
|
|
|
|
|
else
|
|
|
|
|
killer:drawCards(victim.kingdom == "wild" and 1 or
|
|
|
|
|
#table.filter(room.alive_players, function(p)
|
|
|
|
|
return p.kingdom == victim.kingdom
|
|
|
|
|
end) + 1)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end,
|
|
|
|
|
}
|
|
|
|
|
Fk:addSkill(heg_rule)
|
|
|
|
|
|
|
|
|
|
heg = fk.CreateGameMode{
|
|
|
|
|
name = "heg_mode",
|
|
|
|
|
minPlayer = 2,
|
|
|
|
|
maxPlayer = 8,
|
|
|
|
|
rule = heg_rule,
|
|
|
|
|
logic = heg_getlogic,
|
2023-08-03 13:08:01 +00:00
|
|
|
|
is_counted = Util.FalseFunc
|
2023-04-23 13:10:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Fk:loadTranslationTable{
|
|
|
|
|
["heg_mode"] = "国战测试版",
|
|
|
|
|
[":heg_mode"] = heg_description,
|
|
|
|
|
["wild"] = "野心家",
|
|
|
|
|
["#heg_rule"] = "国战规则",
|
|
|
|
|
["revealMain"] = "明置主将",
|
|
|
|
|
["revealDeputy"] = "明置副将",
|
|
|
|
|
["revealAll"] = "背水:全部明置",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return heg
|