1. 修复木马、真名无懈可击
2. 增加亮将、暗将时机
3. 增加武将珠联璧合关系
4. 增加武将选择框禁止替换
5. 增加变换武将不改变体力上限
6. 增加亮将禁止、不计入距离和座次的MarkEnum,相应的player函数
7. 状态技默认锁定技
8. 修复拼点
9. 增加出牌阶段亮将技能
10. 增加暗置武将函数

---------

Signed-off-by: Mechanel <nyutanislavsky@qq.com>
This commit is contained in:
Nyutanislavsky 2023-08-24 21:37:06 +08:00 committed by GitHub
parent 6dd1eded9a
commit d0913e42ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 319 additions and 411 deletions

View File

@ -40,6 +40,14 @@ Flickable {
extra_data.generals.forEach((g) => {
const data = JSON.parse(Backend.callLuaFunction("GetGeneralDetail", [g]));
skillDesc.append(Backend.translate(data.kingdom) + " " + Backend.translate(g) + " " + data.hp + "/" + data.maxHp);
if (data.companions.length > 0){
let ret = '';
ret += "<font color=\"slategrey\"><b>" + Backend.translate("Companions") + "</b>: ";
data.companions.forEach(t => {
ret += Backend.translate(t) + ' '
});
skillDesc.append(ret)
}
data.skill.forEach(t => {
skillDesc.append("<b>" + Backend.translate(t.name) + "</b>: " + t.description)
});

View File

@ -188,6 +188,16 @@ Item {
const data = JSON.parse(Backend.callLuaFunction("GetGeneralDetail", [general]));
generalText.clear();
audioModel.clear();
if (data.companions.length > 0){
let ret = '';
ret += "<font color=\"slategrey\"><b>" + Backend.translate("Companions") + "</b>: ";
data.companions.forEach(t => {
ret += Backend.translate(t) + ' '
});
generalText.append(ret)
}
data.skill.forEach(t => {
generalText.append("<b>" + Backend.translate(t.name) +
"</b>: " + t.description);

View File

@ -796,7 +796,8 @@ callbacks["AskForGeneral"] = (jsonData) => {
const data = JSON.parse(jsonData);
const generals = data[0];
const n = data[1];
const heg = data[2];
const convert = data[2];
const heg = data[3];
roomScene.setPrompt(Backend.translate("#AskForGeneral"), true);
roomScene.state = "replying";
roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/ChooseGeneralBox.qml");
@ -805,6 +806,7 @@ callbacks["AskForGeneral"] = (jsonData) => {
replyToServer(JSON.stringify(box.choices));
});
box.choiceNum = n;
box.convertDisabled = !!convert;
box.needSameKingdom = !!heg;
for (let i = 0; i < generals.length; i++)
box.generalList.append({ "name": generals[i] });

View File

@ -11,6 +11,7 @@ GraphicsBox {
property var choices: []
property var selectedItem: []
property bool loaded: false
property bool convertDisabled: false
property bool needSameKingdom: false
ListModel {
@ -91,6 +92,7 @@ GraphicsBox {
MetroButton {
id: convertBtn
visible: !convertDisabled
text: Backend.translate("Same General Convert")
onClicked: roomScene.startCheat("SameConvert", { cards: generalList });
}

View File

@ -33,7 +33,8 @@ function GetGeneralDetail(name)
maxHp = general.maxHp,
gender = general.gender,
skill = {},
related_skill = {}
related_skill = {},
companions = general.companions
}
for _, s in ipairs(general.skills) do
table.insert(ret.skill, {
@ -59,6 +60,11 @@ function GetGeneralDetail(name)
description = Fk:getDescription(s)
})
end
for _, g in pairs(Fk.generals) do
if table.contains(g.companions, general.trueName) then
table.insertIfNeed(ret.companions, g.trueName)
end
end
return json.encode(ret)
end

View File

@ -175,6 +175,7 @@ FreeKill使用的是libgit2的C API与此同时使用Git完成拓展包的下
-- ["Quit"] = "退出",
["BanGeneral"] = "禁将",
["ResumeGeneral"] = "解禁",
["Companions"] = "珠联璧合",
["Death audio"] = "阵亡",
["$WelcomeToLobby"] = "欢迎进入新月杀游戏大厅!",
@ -436,6 +437,9 @@ Fk:loadTranslationTable{
["#KillPlayerWithNoKiller"] = "%from [%arg] 阵亡,无伤害来源",
["#Revive"] = "%from 竟然复活了",
-- change hero
["#ChangeHero"] = "%from 的 %arg3 %arg 变更为 %arg2",
-- misc
["#GuanxingResult"] = "%from 的观星结果为 %arg 上 %arg2 下",
["#ChainStateChange"] = "%from %arg 了武将牌",

View File

@ -14,14 +14,17 @@
---@field public subkingdom string @ 武将副势力
---@field public hp integer @ 武将初始体力
---@field public maxHp integer @ 武将初始最大体力
---@field public mainMaxHpAdjustedValue integer @ 主将体力上限调整
---@field public deputyMaxHpAdjustedValue integer @ 副将体力上限调整
---@field public shield integer @ 初始护甲
---@field public gender Gender @ 武将性别
---@field public skills Skill[] @ 武将技能
---@field public other_skills string[] @ 武将身上属于其他武将的技能,通过字符串调用
---@field public related_skills Skill[] @ 武将相关的不属于其他武将的技能,例如邓艾的急袭
---@field public related_other_skills string [] @ 武将相关的属于其他武将的技能,例如孙策的英姿
---@field public hidden boolean
---@field public total_hidden boolean
---@field public companions string [] @ 有珠联璧合关系的武将
---@field public hidden boolean @ 不在选将框里出现,可以点将,可以在武将一览里查询到
---@field public total_hidden boolean @ 完全隐藏
General = class("General")
---@alias Gender integer
@ -48,6 +51,8 @@ function General:initialize(package, name, kingdom, hp, maxHp, gender)
self.hp = hp
self.maxHp = maxHp or hp
self.gender = gender or General.Male
self.mainMaxHpAdjustedValue = 0
self.deputyMaxHpAdjustedValue = 0
self.shield = 0
self.subkingdom = nil
@ -56,6 +61,8 @@ function General:initialize(package, name, kingdom, hp, maxHp, gender)
self.related_skills = {} -- skills related to this general, but not first added to it, e.g. "jixi" of dengai
self.related_other_skills = {} -- skills related to this general and belong to other generals, e.g. "yingzi" of sunce
self.companions = {}
package:addGeneral(self)
end
@ -96,4 +103,14 @@ function General:getSkillNameList(include_lord)
return ret
end
--- 为武将增加珠联璧合关系武将1个或多个只需写trueName。
---@param name string[] @ 武将真名(表)
function General:addCompanions(name)
if type(name) == "table" then
table.insertTable(self.companions, name)
elseif type(name) == "string" then
table.insert(self.companions, name)
end
end
return General

View File

@ -140,9 +140,9 @@ function Player:getGeneralMaxHp()
local deputy = Fk.generals[type(self:getMark("__heg_deputy")) == "string" and self:getMark("__heg_deputy") or self.deputy]
if not deputy then
return general.maxHp
return general.maxHp + general.mainMaxHpAdjustedValue
else
return (general.maxHp + deputy.maxHp) // 2
return (general.maxHp + general.mainMaxHpAdjustedValue + deputy.maxHp + deputy.deputyMaxHpAdjustedValue) // 2
end
end
@ -459,6 +459,13 @@ function Player:getAttackRange()
return math.max(baseAttackRange, 0)
end
--- 获取角色是否被移除。
function Player:isRemoved()
return self:getMark(MarkEnum.PlayerRemoved) ~= 0 or table.find(MarkEnum.TempMarkSuffix, function(s)
return self:getMark(MarkEnum.PlayerRemoved .. s) ~= 0
end)
end
--- 修改玩家与其他角色的固定距离。
---@param other Player @ 其他玩家
---@param num integer @ 距离数
@ -484,16 +491,19 @@ function Player:distanceTo(other, mode, ignore_dead)
assert(other:isInstanceOf(Player))
mode = mode or "both"
if other == self then return 0 end
if ignore_dead and other.dead then
if not ignore_dead and other.dead then
print(other.name .. " is dead!")
return -1
end
if self:isRemoved() or other:isRemoved() then
return -1
end
local right = 0
local temp = self
local try_time = 10
for _ = 0, try_time do
if temp == other then break end
if ignore_dead or not temp.dead then
if (ignore_dead or not temp.dead) and not temp:isRemoved() then
right = right + 1
end
temp = temp.next
@ -501,7 +511,7 @@ function Player:distanceTo(other, mode, ignore_dead)
if temp ~= other then
print("Distance malfunction: start and end does not matched.")
end
local left = #(ignore_dead and Fk:currentRoom().players or Fk:currentRoom().alive_players) - right
local left = #(ignore_dead and Fk:currentRoom().players or Fk:currentRoom().alive_players) - right - #table.filter(Fk:currentRoom().alive_players, function(p) return p:isRemoved() end)
local ret = 0
if mode == "left" then
ret = left
@ -534,7 +544,7 @@ end
---@param fixLimit number|null @ 卡牌距离限制增加专用
function Player:inMyAttackRange(other, fixLimit)
assert(other:isInstanceOf(Player))
if self == other or (other and other.dead) then
if self == other or (other and (other.dead or other:isRemoved())) or self:isRemoved() then
return false
end
@ -551,13 +561,20 @@ function Player:inMyAttackRange(other, fixLimit)
return self:distanceTo(other) <= (baseAttackRange + fixLimit)
end
function Player:getNextAlive()
if Fk:currentRoom().alive_players == 0 then
--- 获取下家。
---@param ignoreRemoved bool @ 忽略被移除
---@return ServerPlayer
function Player:getNextAlive(ignoreRemoved)
if #Fk:currentRoom().alive_players == 0 then
return self
end
local doNotIgnore = not ignoreRemoved
if doNotIgnore and table.every(Fk:currentRoom().alive_players, function(p) return p:isRemoved() end) then
return self
end
local ret = self.next
while ret.dead do
while ret.dead or (doNotIgnore and ret:isRemoved()) do
ret = ret.next
end
return ret
@ -884,6 +901,22 @@ function Player:prohibitDiscard(card)
return false
end
--- 确认角色是否被禁止亮将。
function Player:prohibitReveal(isDeputy)
local place = isDeputy and "d" or "m"
if type(self:getMark(MarkEnum.RevealProhibited)) == "table" and table.contains(self:getMark(MarkEnum.RevealProhibited), place) then
return true
end
for _, m in ipairs(table.map(MarkEnum.TempMarkSuffix, function(s)
return self:getMark(MarkEnum.RevealProhibited .. s)
end)) do
if type(m) == "table" and table.contains(m, place) then
return true
end
end
return false
end
--转换技状态阳
fk.SwitchYang = 0
--转换技状态阴

View File

@ -13,6 +13,7 @@
---@field public anim_type string @ 技能类型定义
---@field public related_skills Skill[] @ 和本技能相关的其他技能,有时候一个技能实际上是通过好几个技能拼接而实现的。
---@field public attached_equip string @ 属于什么装备的技能?
---@field public relate_to_place string @ 主将技/副将技
---@field public switchSkillName string @ 转换技名字
local Skill = class("Skill")
@ -56,6 +57,7 @@ function Skill:initialize(name, frequency)
end
self.attached_equip = nil
self.relate_to_place = nil
end
function Skill:__index(k)

View File

@ -5,7 +5,7 @@
local StatusSkill = Skill:subclass("StatusSkill")
function StatusSkill:initialize(name, frequency)
frequency = frequency or Skill.NotFrequent
frequency = frequency or Skill.Compulsory
Skill.initialize(self, name, frequency)
self.global = false
end

View File

@ -38,6 +38,11 @@ local function readCommonSpecToSkill(skill, spec)
assert(type(spec.switch_skill_name) == "string")
skill.switchSkillName = spec.switch_skill_name
end
if spec.relate_to_place then
assert(type(spec.relate_to_place) == "string")
skill.relate_to_place = spec.relate_to_place
end
end
local function readUsableSpecToSkill(skill, spec)

View File

@ -125,4 +125,7 @@ fk.AfterSkillEffect = 82
fk.AreaAborted = 87
fk.AreaResumed = 88
fk.NumOfEvents = 89
fk.GeneralRevealed = 89
fk.GeneralHidden = 90
fk.NumOfEvents = 91

View File

@ -154,8 +154,8 @@ GameEvent.functions[GameEvent.Round] = function(self)
p = room.current
GameEvent(GameEvent.Turn, p):exec()
if room.game_finished then break end
room.current = room.current:getNextAlive()
until p.seat >= p:getNextAlive().seat
room.current = room.current:getNextAlive(true)
until p.seat >= p:getNextAlive(true).seat
logic:trigger(fk.RoundEnd, p)
end

View File

@ -25,9 +25,25 @@ GameEvent.functions[GameEvent.Pindian] = function(self)
local data = { "choose_cards_skill", prompt, true, json.encode(extraData) }
local targets = {}
local moveInfos = {}
if not pindianData.fromCard then
table.insert(targets, pindianData.from)
pindianData.from.request_data = json.encode(data)
else
local _pindianCard = pindianData.fromCard
local pindianCard = _pindianCard:clone(_pindianCard.suit, _pindianCard.number)
pindianCard:addSubcard(_pindianCard.id)
pindianData.fromCard = pindianCard
table.insert(moveInfos, {
ids = { _pindianCard.id },
fromArea = room:getCardArea(_pindianCard.id),
toArea = Card.Processing,
moveReason = fk.ReasonPut,
skillName = pindianData.reason,
moveVisible = true,
})
end
for _, to in ipairs(pindianData.tos) do
if not (pindianData.results[to.id] and pindianData.results[to.id].toCard) then
@ -39,7 +55,6 @@ GameEvent.functions[GameEvent.Pindian] = function(self)
room:notifyMoveFocus(targets, "AskForPindian")
room:doBroadcastRequest("AskForUseActiveSkill", targets)
local moveInfos = {}
for _, p in ipairs(targets) do
local _pindianCard
if p.reply_ready then

View File

@ -183,8 +183,7 @@ function GameLogic:broadcastGeneral()
assert(p.general ~= "")
local general = Fk.generals[p.general]
local deputy = Fk.generals[p.deputyGeneral]
p.maxHp = deputy and math.floor((deputy.maxHp + general.maxHp) / 2)
or general.maxHp
p.maxHp = p:getGeneralMaxHp()
p.hp = deputy and math.floor((deputy.hp + general.hp) / 2) or general.hp
p.shield = math.min(general.shield + (deputy and deputy.shield or 0), 5)
-- TODO: setup AI here

View File

@ -29,6 +29,10 @@ MarkEnum.BypassTimesLimitTo = "BypassTimesLimitTo"
MarkEnum.BypassDistancesLimitTo = "BypassDistancesLimitTo"
---非锁定技失效,可带清除标记后缀
MarkEnum.UncompulsoryInvalidity = "UncompulsoryInvalidity"
---不可明置可带清除标记后缀值为表m - 主将, d - 副将)
MarkEnum.RevealProhibited = "RevealProhibited"
---不计入距离、座次后缀,可带清除标记后缀
MarkEnum.PlayerRemoved = "PlayerRemoved"
---各种清除标记后缀
MarkEnum.TempMarkSuffix = { "-phase", "-turn", "-round" }

View File

@ -576,18 +576,25 @@ end
---@param full bool @ 是否血量满状态变身
---@param isDeputy bool @ 是否变的是副将
---@param sendLog bool @ 是否发Log
function Room:changeHero(player, new_general, full, isDeputy, sendLog)
---@param maxHpChange bool @ 是否改变体力上限,默认改变
function Room:changeHero(player, new_general, full, isDeputy, sendLog, maxHpChange)
local orig = isDeputy and (player.deputyGeneral or "") or player.general
orig = Fk.generals[orig]
local orig_skills = orig and orig:getSkillNameList() or Util.DummyTable
local orig_skills = orig and orig:getSkillNameList()
local new = Fk.generals[new_general] or Fk.generals["sunce"] or Fk.generals["blank_shibing"]
local new_skills = table.map(orig_skills, function(e)
return "-" .. e
end)
local new_skills = {}
for _, sname in ipairs(new:getSkillNameList()) do
local s = Fk.skills[sname]
if not s.relate_to_place or s.relate_to_place == (isDeputy and "d" or "m") then
table.insert(new_skills, sname)
end
end
table.insertTable(new_skills, new:getSkillNameList())
table.insertTable(new_skills, table.map(orig_skills, function(e)
return "-" .. e
end))
self:handleAddLoseSkills(player, table.concat(new_skills, "|"), nil, false)
@ -599,10 +606,23 @@ function Room:changeHero(player, new_general, full, isDeputy, sendLog)
self:setPlayerProperty(player, "kingdom", new.kingdom)
end
self:setPlayerProperty(player, "maxHp", player:getGeneralMaxHp())
maxHpChange = (maxHpChange == nil) and true or maxHpChange
if maxHpChange then
self:setPlayerProperty(player, "maxHp", player:getGeneralMaxHp())
end
if full or player.hp > player.maxHp then
self:setPlayerProperty(player, "hp", player.maxHp)
end
if sendLog then
self:sendLog{
type = "#ChangeHero",
from = player.id,
arg = orig.name,
arg2 = new.name,
arg3 = isDeputy and "deputyGeneral" or "mainGeneral"
}
end
end
------------------------------------------------------------------------
@ -1271,8 +1291,9 @@ end
---@param player ServerPlayer @ 询问目标
---@param generals string[] @ 可选武将
---@param n integer @ 可选数量默认为1
---@param noConvert bool @ 可否变更,默认可
---@return string|string[] @ 选择的武将
function Room:askForGeneral(player, generals, n)
function Room:askForGeneral(player, generals, n, noConvert)
local command = "AskForGeneral"
self:notifyMoveFocus(player, command)
@ -1281,7 +1302,7 @@ function Room:askForGeneral(player, generals, n)
local defaultChoice = table.random(generals, n)
if (player.serverplayer:getState() == fk.Player_Online) then
local result = self:doRequest(player, command, json.encode{ generals, n })
local result = self:doRequest(player, command, json.encode{ generals, n, noConvert })
local choices
if result == "" then
choices = defaultChoice
@ -2425,10 +2446,10 @@ function Room:handleCardEffect(event, cardEffectEvent)
local players = {}
Fk.currentResponsePattern = "nullification"
for _, p in ipairs(self.alive_players) do
local cards = p:getCardIds(Player.Hand)
local cards = p:getHandlyIds(true)
for _, cid in ipairs(cards) do
if
Fk:getCardById(cid).name == "nullification" and
Fk:getCardById(cid).trueName == "nullification" and
not (
table.contains(cardEffectEvent.disresponsiveList or Util.DummyTable, p.id) or
table.contains(cardEffectEvent.unoffsetableList or Util.DummyTable, p.id)

View File

@ -720,7 +720,10 @@ end
---@param skill Skill
function ServerPlayer:addFakeSkill(skill)
assert(skill:isInstanceOf(Skill))
assert(type(skill) == "string" or skill:isInstanceOf(Skill))
if type(skill) == "string" then
skill = Fk.skills[skill]
end
if table.contains(self._fake_skills, skill) then return end
table.insert(self._fake_skills, skill)
@ -736,7 +739,10 @@ end
---@param skill Skill
function ServerPlayer:loseFakeSkill(skill)
assert(skill:isInstanceOf(Skill))
assert(type(skill) == "string" or skill:isInstanceOf(Skill))
if type(skill) == "string" then
skill = Fk.skills[skill]
end
if not table.contains(self._fake_skills, skill) then return end
table.removeOne(self._fake_skills, skill)
@ -784,7 +790,9 @@ function ServerPlayer:prelightSkill(skill, isPrelight)
self:doNotify("PrelightSkill", json.encode{ skill.name, isPrelight })
end
function ServerPlayer:revealGeneral(isDeputy)
---@param isDeputy bool
---@param no_trigger bool
function ServerPlayer:revealGeneral(isDeputy, no_trigger)
local room = self.room
local generalName
if isDeputy then
@ -801,19 +809,34 @@ function ServerPlayer:revealGeneral(isDeputy)
self:loseFakeSkill(skill)
end
local ret = true
if not ((isDeputy and self.general ~= "anjiang") or (not isDeputy and self.deputyGeneral ~= "anjiang")) then
local other = Fk.generals[self:getMark(isDeputy and "__heg_general" or "__heg_deputy")]
for _, sname in ipairs(other:getSkillNameList()) do
local s = Fk.skills[sname]
if s.frequency == Skill.Compulsory and s.relate_to_place ~= (isDeputy and "m" or "d") then
ret = false
break
end
end
end
if ret then
self:loseFakeSkill("reveal_skill")
end
local oldKingdom = self.kingdom
room:changeHero(self, generalName, false, isDeputy)
local kingdom = general.kingdom
self.kingdom = kingdom
if oldKingdom == "unknown" and #table.filter(room:getOtherPlayers(self),
function(p)
return p.kingdom == kingdom
end) >= #room.players // 2 then
self.kingdom = "wild"
room:broadcastProperty(self, "kingdom")
end
room:broadcastProperty(self, "kingdom")
if self.gender ~= General.Male and self.gender ~= General.Female then
if self.gender == General.Agender then
self.gender = general.gender
end
@ -824,7 +847,9 @@ function ServerPlayer:revealGeneral(isDeputy)
arg2 = generalName,
}
-- TODO: 阴阳鱼摸牌
if not no_trigger then
room.logic:trigger(fk.GeneralRevealed, self, generalName)
end
end
function ServerPlayer:revealBySkillName(skill_name)
@ -848,6 +873,47 @@ function ServerPlayer:revealBySkillName(skill_name)
end
end
function ServerPlayer:hideGeneral(isDeputy)
local room = self.room
local generalName = isDeputy and self.deputyGeneral or self.general
local mark = isDeputy and "__heg_deputy" or "__heg_general"
self:setMark(mark, generalName)
self:doNotify("SetPlayerMark", json.encode{ self.id, mark, generalName})
if isDeputy then
room:setDeputyGeneral(self, "anjiang")
room:broadcastProperty(self, "deputyGeneral")
else
room:setPlayerGeneral(self, "anjiang", false)
room:broadcastProperty(self, "general")
end
local general = Fk.generals[generalName]
local skills = general.skills
local place = isDeputy and "m" or "d"
for _, s in ipairs(skills) do
room:handleAddLoseSkills(self, "-" .. s.name, nil, false, true)
if s.relate_to_place ~= place then
if s.frequency == Skill.Compulsory then
self:addFakeSkill("reveal_skill")
end
self:addFakeSkill(s)
end
end
for _, sname in ipairs(general.other_skills) do
room:handleAddLoseSkills(self, "-" .. sname, nil, false, true)
local s = Fk.skills[sname]
if s.relate_to_place ~= place then
if s.frequency == Skill.Compulsory then
self:addFakeSkill("reveal_skill")
end
self:addFakeSkill(s)
end
end
room.logic:trigger(fk.GeneralHidden, room, generalName)
end
-- 神貂蝉
---@param p ServerPlayer

View File

@ -145,6 +145,79 @@ local uncompulsoryInvalidity = fk.CreateInvaliditySkill {
end
}
local revealProhibited = fk.CreateInvaliditySkill {
name = "reveal_prohibited",
global = true,
invalidity_func = function(self, from, skill)
local generals = {}
if type(from:getMark(MarkEnum.RevealProhibited)) == "table" then
generals = from:getMark(MarkEnum.RevealProhibited)
end
for _, m in ipairs(table.map(MarkEnum.TempMarkSuffix, function(s)
return from:getMark(MarkEnum.RevealProhibited .. s)
end)) do
if type(m) == "table" then
for _, g in ipairs(m) do
table.insertIfNeed(generals, g)
end
end
end
if #generals == 0 then return false end
if type(from._fake_skills) == "table" and not table.contains(from._fake_skills, skill) then return false end
local sname = skill.name
for _, g in ipairs(generals) do
local ret = g == "m" and from:getMark("__heg_general") or from:getMark("__heg_deputy")
local general = Fk.generals[ret]
if table.contains(general:getSkillNameList(), sname) then
return true
end
end
return false
end
}
-- 亮将
local revealSkill = fk.CreateActiveSkill{
name = "reveal_skill",
prompt = "#reveal_skill",
interaction = function(self)
local choiceList = {}
if (Self.general == "anjiang" and not Self:prohibitReveal()) then
local general = Fk.generals[Self:getMark("__heg_general")]
for _, sname in ipairs(general:getSkillNameList()) do
local s = Fk.skills[sname]
if s.frequency == Skill.Compulsory and s.relate_to_place ~= "m" then
table.insert(choiceList, "revealMain")
break
end
end
end
if (Self.deputyGeneral == "anjiang" and not Self:prohibitReveal(true)) then
local general = Fk.generals[Self:getMark("__heg_deputy")]
for _, sname in ipairs(general:getSkillNameList()) do
local s = Fk.skills[sname]
if s.frequency == Skill.Compulsory and s.relate_to_place ~= "d" then
table.insert(choiceList, "revealDeputy")
break
end
end
end
if #choiceList == 0 then return false end
return UI.ComboBox { choices = choiceList}
end,
target_num = 0,
card_num = 0,
card_filter = Util.FalseFunc,
on_use = function(self, room, effect)
local player = room:getPlayerById(effect.from)
local choice = self.interaction.data
if not choice then return false
elseif choice == "revealMain" then player:revealGeneral(false)
elseif choice == "revealDeputy" then player:revealGeneral(true) end
end,
}
AuxSkills = {
discardSkill,
chooseCardsSkill,
@ -152,4 +225,6 @@ AuxSkills = {
maxCardsSkill,
choosePlayersToMoveCardInBoardSkill,
uncompulsoryInvalidity,
revealProhibited,
revealSkill
}

View File

@ -1,372 +1,6 @@
local heg_description = [==[
#
-- SPDX-License-Identifier: GPL-3.0-or-later
·2\~12(线4\~8)
-- deprecated file, only for update
##
-- current hegemony repository is at https://gitee.com/qsgs-fans/hegemony
****
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
player:addFakeSkill(skill)
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
if killer.kingdom ~= "wild" and killer.kingdom == victim.kingdom then
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,
is_counted = Util.FalseFunc
}
Fk:loadTranslationTable{
["heg_mode"] = "国战测试版",
[":heg_mode"] = heg_description,
["wild"] = "野心家",
["#heg_rule"] = "国战规则",
["revealMain"] = "明置主将",
["revealDeputy"] = "明置副将",
["revealAll"] = "背水:全部明置",
}
return heg

View File

@ -460,6 +460,9 @@ Fk:loadTranslationTable{
["choose_cards_skill"] = "选牌",
["choose_players_skill"] = "选择角色",
["choose_players_to_move_card_in_board"] = "选择角色",
["reveal_skill"] = "亮将",
["#reveal_skill"] = "选择一个武将亮将(点击左侧选择框展开)",
[":reveal_skill"] = "出牌阶段,你可亮出一张有锁定技的武将。",
["game_rule"] = "弃牌阶段",
}

View File

@ -532,6 +532,7 @@ guanyu:addSkill(wusheng)
local paoxiaoAudio = fk.CreateTriggerSkill{
name = "#paoxiaoAudio",
visible = false,
refresh_events = {fk.CardUsing},
can_refresh = function(self, event, target, player, data)
return target == player and player:hasSkill(self.name) and
@ -549,6 +550,7 @@ local paoxiaoAudio = fk.CreateTriggerSkill{
}
local paoxiao = fk.CreateTargetModSkill{
name = "paoxiao",
frequency = Skill.Compulsory,
bypass_times = function(self, player, skill, scope)
if player:hasSkill(self.name) and skill.trueName == "slash_skill"
and scope == Player.HistoryPhase then
@ -1283,9 +1285,6 @@ Fk:loadTranslationTable{
["anjiang"] = "暗将",
}
-- local heg_mode = require "packages.standard.hegemony"
-- extension:addGameMode(heg_mode)
-- load translations of this package
dofile "packages/standard/i18n/init.lua"

View File

@ -692,7 +692,7 @@ local lightningSkill = fk.CreateActiveSkill{
local to = room:getPlayerById(effect.to)
local nextp = to
repeat
nextp = nextp:getNextAlive()
nextp = nextp:getNextAlive(true)
if nextp == to then break end
until not nextp:hasDelayedTrick("lightning") and not nextp:isProhibited(nextp, effect.card)