From ab4cfba7a31f382b175bf1b08ffe41a603234f36 Mon Sep 17 00:00:00 2001 From: notify Date: Fri, 21 Apr 2023 17:49:30 +0800 Subject: [PATCH] Overview UI (#130) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化了卡牌一览/武将一览的UI --- lua/client/i18n/zh_CN.lua | 2 + packages/maneuvering/init.lua | 13 ++ packages/standard_cards/i18n/zh_CN.lua | 33 +++ qml/Pages/CardsOverview.qml | 282 +++++++++++++++++++++++-- qml/Pages/GeneralsOverview.qml | 218 +++++++++++++------ test.lua | 7 - 6 files changed, 467 insertions(+), 88 deletions(-) delete mode 100644 test.lua diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index e731b08a..95d0bc27 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -35,6 +35,8 @@ Fk:loadTranslationTable{ ["Generals Overview"] = "武将一览", ["Cards Overview"] = "卡牌一览", + ["Special card skills:"] = "卡牌的特殊用法:", + ["Every suit & number:"] = "所有的花色和点数:", ["Scenarios Overview"] = "玩法一览", ["Replay"] = "录像", ["About"] = "关于", diff --git a/packages/maneuvering/init.lua b/packages/maneuvering/init.lua index 42206f74..5655cab1 100644 --- a/packages/maneuvering/init.lua +++ b/packages/maneuvering/init.lua @@ -483,19 +483,32 @@ Fk:loadTranslationTable{ ["maneuvering"] = "军争", ["thunder__slash"] = "雷杀", + [":thunder__slash"] = "基本牌
时机:出牌阶段
目标:攻击范围内的一名角色
效果:对目标角色造成1点雷电伤害。", ["fire__slash"] = "火杀", + [":fire__slash"] = "基本牌
时机:出牌阶段
目标:攻击范围内的一名角色
效果:对目标角色造成1点火焰伤害。", ["analeptic"] = "酒", + [":analeptic"] = "基本牌
时机:出牌阶段/你处于濒死状态时
目标:你
效果:目标角色本回合使用的下一张【杀】将要造成的伤害+1/目标角色回复1点体力。", ["iron_chain"] = "铁锁连环", + [":iron_chain"] = "锦囊牌
时机:出牌阶段
目标:一至两名角色
效果:横置或重置目标角色的武将牌。", ["_normal_use"] = "正常使用", ["recast"] = "重铸", + [":recast"] = "你可以将此牌置入弃牌堆,然后摸一张牌。", ["fire_attack"] = "火攻", + ["fire_attack_skill"] = "火攻", + [":fire_attack"] = "锦囊牌
时机:出牌阶段
目标:一名有手牌的角色
效果:目标角色展示一张手牌,然后你可以弃置一张与所展示牌花色相同的手牌令其受到1点火焰伤害。", ["supply_shortage"] = "兵粮寸断", + [":supply_shortage"] = "延时锦囊牌
时机:出牌阶段
目标:距离1的一名其他角色
效果:将此牌置于目标角色判定区内。其判定阶段进行判定:若结果不为梅花,其跳过摸牌阶段。然后将【兵粮寸断】置入弃牌堆。", ["guding_blade"] = "古锭刀", + [":guding_blade"] = "装备牌·武器
攻击范围:2
武器技能:锁定技。每当你使用【杀】对目标角色造成伤害时,若该角色没有手牌,此伤害+1。", ["fan"] = "朱雀羽扇", + [":fan"] = "装备牌·武器
攻击范围:4
武器技能:你可以将一张普通【杀】当火【杀】使用。", ["#fan_skill"] = "朱雀羽扇", ["vine"] = "藤甲", + [":vine"] = "装备牌·防具
防具技能:锁定技。【南蛮入侵】、【万箭齐发】和普通【杀】对你无效。每当你受到火焰伤害时,此伤害+1。", ["silver_lion"] = "白银狮子", + [":silver_lion"] = "装备牌·防具
防具技能:锁定技。每当你受到伤害时,若此伤害大于1点,防止多余的伤害。每当你失去装备区里的【白银狮子】后,你回复1点体力。", ["hualiu"] = "骅骝", + [":hualiu"] = "装备牌·坐骑
坐骑技能:其他角色与你的距离+1。", } return extension diff --git a/packages/standard_cards/i18n/zh_CN.lua b/packages/standard_cards/i18n/zh_CN.lua index 54ab41dd..868acace 100644 --- a/packages/standard_cards/i18n/zh_CN.lua +++ b/packages/standard_cards/i18n/zh_CN.lua @@ -16,78 +16,111 @@ Fk:loadTranslationTable{ ["diamond"] = "方块", ["slash"] = "杀", + [":slash"] = "基本牌
时机:出牌阶段
目标:攻击范围内的一名其他角色
效果:对目标角色造成1点伤害。", ["#slash-jink"] = "%src 对你使用了杀,请使用 %arg 张闪", ["jink"] = "闪", + [":jink"] = "基本牌
时机:【杀】对你生效时
目标:此【杀】
效果:抵消此【杀】的效果。", ["peach"] = "桃", + [":peach"] = "基本牌
时机:出牌阶段/一名角色处于濒死状态时
目标:已受伤的你/处于濒死状态的角色
效果:目标角色回复1点体力。", ["dismantlement"] = "过河拆桥", + [":dismantlement"] = "锦囊牌
时机:出牌阶段
目标:一名区域内有牌的其他角色。
效果:你弃置目标角色区域内的一张牌。", ["dismantlement_skill"] = "过河拆桥", ["snatch"] = "顺手牵羊", + [":snatch"] = "锦囊牌
时机:出牌阶段
目标:距离1的一名区域内有牌的角色
效果:你获得目标角色区域内的一张牌。", ["snatch_skill"] = "顺手牵羊", ["duel"] = "决斗", + [":duel"] = "锦囊牌
时机:出牌阶段
目标:一名其他角色
效果:由目标角色开始,你与其轮流:打出一张【杀】,否则受到对方的1点伤害并结束此牌结算。", ["collateral"] = "借刀杀人", + [":collateral"] = "锦囊牌
时机:出牌阶段
目标:装备区内有武器牌且攻击范围内有【杀】的合法目标的一名其他角色A(你需要选择一名A攻击范围内的【杀】的合法目标B)
效果:A须对B使用一张【杀】,否则你获得A装备区内的武器牌。", ["ex_nihilo"] = "无中生有", + [":ex_nihilo"] = "锦囊牌
时机:出牌阶段
目标:你
效果:目标角色摸两张牌。", ["nullification"] = "无懈可击", + [":nullification"] = "锦囊牌
时机:锦囊牌对目标角色生效前,或一张【无懈可击】生效前
目标:该锦囊牌
效果:抵消该锦囊牌对该角色产生的效果,或抵消另一张【无懈可击】产生的效果。", ["savage_assault"] = "南蛮入侵", + [":savage_assault"] = "锦囊牌
时机:出牌阶段
目标:所有其他角色
效果:每名目标角色须打出一张【杀】,否则受到1点伤害。", ["archery_attack"] = "万箭齐发", + [":archery_attack"] = "锦囊牌
时机:出牌阶段
目标:所有其他角色
效果:每名目标角色须打出一张【闪】,否则受到1点伤害。", ["god_salvation"] = "桃园结义", + [":god_salvation"] = "锦囊牌
时机:出牌阶段
目标:所有角色
效果:每名目标角色回复1点体力。", ["amazing_grace"] = "五谷丰登", + [":amazing_grace"] = "锦囊牌
时机:出牌阶段
目标:所有角色
效果:你亮出牌堆顶等于角色数的牌,每名目标角色获得其中一张牌,然后将其余的牌置入弃牌堆。", ["amazing_grace_skill"] = "五谷选牌", ["lightning"] = "闪电", + [":lightning"] = "延时锦囊牌
时机:出牌阶段
目标:你
效果:将此牌置于目标角色判定区内。其判定阶段进行判定:若结果为黑桃2-9,其受到3点雷电伤害并将【闪电】置入弃牌堆,否则将【闪电】移动至其下家判定区内。", ["indulgence"] = "乐不思蜀", + [":indulgence"] = "延时锦囊牌
时机:出牌阶段
目标:一名其他角色
效果:将此牌置于目标角色判定区内。其判定阶段进行判定:若结果不为红桃,其跳过出牌阶段。然后将【乐不思蜀】置入弃牌堆。", ["crossbow"] = "诸葛连弩", + [":crossbow"] = "装备牌·武器
攻击范围:1
武器技能:锁定技。你于出牌阶段内使用【杀】无次数限制。", ["qinggang_sword"] = "青釭剑", + [":qinggang_sword"] = "装备牌·武器
攻击范围:2
武器技能:锁定技。你的【杀】无视目标角色的防具。", ["ice_sword"] = "寒冰剑", + [":ice_sword"] = "装备牌·武器
攻击范围:2
武器技能:每当你使用【杀】对目标角色造成伤害时,若该角色有牌,你可以防止此伤害,然后依次弃置其两张牌。", ["#ice_sword_skill"] = "寒冰剑", ["double_swords"] = "雌雄双股剑", + [":double_swords"] = "装备牌·武器
攻击范围:2
武器技能:每当你指定异性角色为【杀】的目标后,你可以令其选择一项:弃置一张手牌,或令你摸一张牌。", ["#double_swords_skill"] = "雌雄双股剑", ["blade"] = "青龙偃月刀", + [":blade"] = "装备牌·武器
攻击范围:3
武器技能:每当你使用的【杀】被【闪】抵消后,你可以对该角色再使用一张【杀】(无距离限制且不能选择额外目标)。", ["#blade_skill"] = "青龙偃月刀", ["#blade_slash"] = "你可以发动“青龙偃月刀”对 %src 再使用一张杀", ["spear"] = "丈八蛇矛", + [":spear"] = "装备牌·武器
攻击范围:3
武器技能:你可以将两张手牌当【杀】使用或打出。", ["spear_skill"] = "丈八矛", + [":spear_skill"] = "你可以将两张手牌当【杀】使用或打出。", ["axe"] = "贯石斧", + [":axe"] = "装备牌·武器
攻击范围:3
武器技能:每当你使用的【杀】被【闪】抵消后,你可以弃置两张牌,则此【杀】继续造成伤害。", ["#axe_skill"] = "贯石斧", ["halberd"] = "方天画戟", + [":halberd"] = "装备牌·武器
攻击范围:4
武器技能:锁定技。你使用最后的手牌【杀】可以额外选择至多两名目标。", ["kylin_bow"] = "麒麟弓", + [":kylin_bow"] = "装备牌·武器
攻击范围:5
武器技能:每当你使用【杀】对目标角色造成伤害时,你可以弃置其装备区内的一张坐骑牌。", ["#kylin_bow_skill"] = "麒麟弓", ["eight_diagram"] = "八卦阵", + [":eight_diagram"] = "装备牌·防具
防具技能:每当你需要使用或打出一张【闪】时,你可以进行判定:若结果为红色,视为你使用或打出了一张【闪】。", ["#eight_diagram_skill"] = "八卦阵", ["nioh_shield"] = "仁王盾", + [":nioh_shield"] = "装备牌·防具
防具技能:锁定技。黑色【杀】对你无效。", ["dilu"] = "的卢", + [":dilu"] = "装备牌·坐骑
坐骑技能:其他角色与你的距离+1。", ["jueying"] = "绝影", + [":jueying"] = "装备牌·坐骑
坐骑技能:其他角色与你的距离+1。", ["zhuahuangfeidian"] = "爪黄飞电", + [":zhuahuangfeidian"] = "装备牌·坐骑
坐骑技能:其他角色与你的距离+1。", ["chitu"] = "赤兔", + [":chitu"] = "装备牌·坐骑
坐骑技能:你与其他角色的距离-1。", ["dayuan"] = "大宛", + [":dayuan"] = "装备牌·坐骑
坐骑技能:你与其他角色的距离-1。", ["zixing"] = "紫骍", + [":zixing"] = "装备牌·坐骑
坐骑技能:你与其他角色的距离-1。", } diff --git a/qml/Pages/CardsOverview.qml b/qml/Pages/CardsOverview.qml index 8caee7f4..99ac3a0f 100644 --- a/qml/Pages/CardsOverview.qml +++ b/qml/Pages/CardsOverview.qml @@ -10,29 +10,281 @@ Item { property bool loaded: false + Rectangle { + anchors.fill: listView + color: "#88EEEEEE" + radius: 6 + } + ListView { - width: Math.floor(root.width / 98) * 98 - height: parent.height - anchors.centerIn: parent + id: listView + width: 130 + height: parent.height - 20 + y: 10 ScrollBar.vertical: ScrollBar {} model: ListModel { id: packages } - delegate: ColumnLayout { - Text { text: Backend.translate(name) } - GridLayout { - columns: root.width / 98 - Repeater { - model: JSON.parse(Backend.callLuaFunction("GetCards", [name])) - CardItem { - autoBack: false - Component.onCompleted: { - let data = JSON.parse(Backend.callLuaFunction("GetCardData", [modelData])); - setData(data); - } + highlight: Rectangle { color: "#E91E63"; radius: 5 } + highlightMoveDuration: 500 + + delegate: Item { + width: listView.width + height: 40 + + Text { + text: Backend.translate(name) + anchors.centerIn: parent + } + + TapHandler { + onTapped: { + listView.currentIndex = index; + } + } + } + + onCurrentIndexChanged: { vanishAnim.start(); } + } + + GridView { + id: gridView + width: root.width - listView.width - cardDetail.width - 16 + height: parent.height - 20 + y: 10 + anchors.left: listView.right + anchors.leftMargin: 8 + (width % 100) / 2 + cellHeight: 140 + cellWidth: 100 + + delegate: CardItem { + autoBack: false + property int dupCount: 0 + + Text { + anchors.right: parent.right + anchors.bottom: parent.bottom + text: parent.dupCount ? ("x" + parent.dupCount.toString()) : "" + font.pixelSize: 36 + color: "white" + style: Text.Outline + } + + Component.onCompleted: { + let data = modelData; + if (!data.cards) { + name = data.name; + suit = data.suit; + number = data.number; + cid = data.cid; + } else { + name = data.name; + cid = data.cid; + suit = ""; + number = 0; + color = ""; + dupCount = data.cards.length; + } + } + + onClicked: { + cardDetail.cid = modelData.cid; + cardDetail.cards = modelData.cards; + cardDetail.updateCard(); + } + } + } + + ParallelAnimation { + id: vanishAnim + PropertyAnimation { + target: gridView + property: "opacity" + to: 0 + duration: 150 + easing.type: Easing.InOutQuad + } + PropertyAnimation { + target: gridView + property: "y" + to: 30 + duration: 150 + easing.type: Easing.InOutQuad + } + onFinished: { + let pkg = [listView.model.get(listView.currentIndex).name]; + let idList = JSON.parse(Backend.callLuaFunction("GetCards", pkg)); + let cardList = idList.map(id => JSON.parse(Backend.callLuaFunction + ("GetCardData",[id]))); + + let groupedCardList = []; + let groupedCards = {}; + cardList.forEach(c => { + let name = c.name; + if (!groupedCards[name]) { + groupedCardList.push(name); + groupedCards[name] = []; + } + groupedCards[name].push({ + cid: c.cid, + suit: c.suit, + number: c.number, + }); + }); + + let model = []; + groupedCardList.forEach(name => { + let cards = groupedCards[name]; + if (cards.length === 1) { + model.push({ + name: name, + extension: pkg, + suit: cards[0].suit, + number: cards[0].number, + cid: cards[0].cid, + }); + } else { + model.push({ + name: name, + cid: cards[0].cid, + cards: cards, + }) + } + }); + gridView.model = model; + appearAnim.start(); + } + } + + SequentialAnimation { + id: appearAnim + PauseAnimation { duration: 200 } + ParallelAnimation { + PropertyAnimation { + target: gridView + property: "opacity" + to: 1 + duration: 150 + easing.type: Easing.InOutQuad + } + PropertyAnimation { + target: gridView + property: "y" + from: 20 + to: 10 + duration: 150 + easing.type: Easing.InOutQuad + } + } + } + + Rectangle { + id: cardDetail + width: 310 + height: parent.height - 20 + y: 10 + anchors.right: parent.right + anchors.rightMargin: 10 + color: "#88EEEEEE" + radius: 8 + + property int cid: 1 + property var cards + function updateCard() { + let data = JSON.parse(Backend.callLuaFunction("GetCardData", [cid])); + const suitTable = { + spade: "♠", heart: '', + club: "♣", diamond: '', + } + const getNumString = n => { + switch (n) { + case 1: + return "A"; + case 11: + return "J"; + case 12: + return "Q"; + case 13: + return "K"; + default: + return n.toString(); + } + } + + if (!cards) { + detailCard.setData(data); + detailCard.dupCount = 0; + } else { + detailCard.cid = cid; + detailCard.color = ""; + detailCard.suit = ""; + detailCard.number = 0; + detailCard.dupCount = cards.length; + } + detailCard.known = true; + cardText.clear(); + cardText.append(Backend.translate(":" + data.name)); + + let skills = JSON.parse(Backend.callLuaFunction + ("GetCardSpecialSkills", [cid])); + if (skills.length > 0) { + cardText.append("
" + Backend.translate("Special card skills:")); + skills.forEach(t => { + cardText.append("" + Backend.translate(t) + ": " + + Backend.translate(":" + t)); + }); + } + + if (cards) { + cardText.append("
" + Backend.translate("Every suit & number:")); + cardText.append(cards.map(c => { + return (suitTable[c.suit] + getNumString(c.number)) + }).join(", ")); + } + } + + Flickable { + flickableDirection: Flickable.VerticalFlick + contentHeight: detailLayout.height + width: parent.width - 40 + height: parent.height - 40 + clip: true + anchors.centerIn: parent + ScrollBar.vertical: ScrollBar {} + + ColumnLayout { + id: detailLayout + width: parent.width + + CardItem { + id: detailCard + Layout.alignment: Qt.AlignHCenter + cid: 1 + known: false + + property int dupCount: 0 + Text { + anchors.right: parent.right + anchors.bottom: parent.bottom + text: parent.dupCount ? ("x" + parent.dupCount.toString()) : "" + font.pixelSize: 36 + color: "white" + style: Text.Outline } } + + TextEdit { + id: cardText + + Layout.fillWidth: true + readOnly: true + selectByKeyboard: true + selectByMouse: false + wrapMode: TextEdit.WordWrap + textFormat: TextEdit.RichText + font.pixelSize: 16 + } } } } diff --git a/qml/Pages/GeneralsOverview.qml b/qml/Pages/GeneralsOverview.qml index bfad994b..3b69b0a1 100644 --- a/qml/Pages/GeneralsOverview.qml +++ b/qml/Pages/GeneralsOverview.qml @@ -10,31 +10,163 @@ Item { property bool loaded: false + Rectangle { + anchors.fill: listView + color: "#88EEEEEE" + radius: 6 + } + ListView { - width: Math.floor(root.width / 98) * 98 - height: parent.height - anchors.centerIn: parent + id: listView + width: 130 + height: parent.height - 20 + y: 10 ScrollBar.vertical: ScrollBar {} model: ListModel { id: packages } - delegate: ColumnLayout { - Text { text: Backend.translate(name) } - GridLayout { - columns: root.width / 98 - Repeater { - model: JSON.parse(Backend.callLuaFunction("GetGenerals", [name])) - GeneralCardItem { - autoBack: false - name: modelData - onClicked: { - generalText.clear(); - generalText.general = modelData; - generalText.updateGeneral(); - generalDetail.open(); - } - } + highlight: Rectangle { color: "#E91E63"; radius: 5 } + highlightMoveDuration: 500 + + delegate: Item { + width: listView.width + height: 40 + + Text { + text: Backend.translate(name) + anchors.centerIn: parent + } + + TapHandler { + onTapped: { + listView.currentIndex = index; + } + } + } + + onCurrentIndexChanged: { vanishAnim.start(); } + } + + GridView { + id: gridView + width: root.width - listView.width - generalDetail.width - 16 + height: parent.height - 20 + y: 10 + anchors.left: listView.right + anchors.leftMargin: 8 + (width % 100) / 2 + cellHeight: 140 + cellWidth: 100 + + delegate: GeneralCardItem { + autoBack: false + name: modelData + onClicked: { + generalText.clear(); + generalDetail.general = modelData; + generalDetail.updateGeneral(); + // generalDetail.open(); + } + } + } + + ParallelAnimation { + id: vanishAnim + PropertyAnimation { + target: gridView + property: "opacity" + to: 0 + duration: 150 + easing.type: Easing.InOutQuad + } + PropertyAnimation { + target: gridView + property: "y" + to: 30 + duration: 150 + easing.type: Easing.InOutQuad + } + onFinished: { + gridView.model = JSON.parse(Backend.callLuaFunction("GetGenerals", + [listView.model.get(listView.currentIndex).name])); + appearAnim.start(); + } + } + + SequentialAnimation { + id: appearAnim + PauseAnimation { duration: 200 } + ParallelAnimation { + PropertyAnimation { + target: gridView + property: "opacity" + to: 1 + duration: 150 + easing.type: Easing.InOutQuad + } + PropertyAnimation { + target: gridView + property: "y" + from: 20 + to: 10 + duration: 150 + easing.type: Easing.InOutQuad + } + } + } + + Rectangle { + id: generalDetail + width: 310 + height: parent.height - 20 + y: 10 + anchors.right: parent.right + anchors.rightMargin: 10 + color: "#88EEEEEE" + radius: 8 + + property string general: "caocao" + function updateGeneral() { + detailGeneralCard.name = general; + let data = JSON.parse(Backend.callLuaFunction("GetGeneralDetail", [general])); + generalText.clear(); + data.skill.forEach(t => { + generalText.append("" + Backend.translate(t.name) + ": " + t.description) + }); + data.related_skill.forEach(t => { + generalText.append("" + Backend.translate(t.name) + ": " + t.description + "") + }); + } + + Flickable { + flickableDirection: Flickable.VerticalFlick + contentHeight: detailLayout.height + width: parent.width - 40 + height: parent.height - 40 + clip: true + anchors.centerIn: parent + ScrollBar.vertical: ScrollBar {} + + ColumnLayout { + id: detailLayout + width: parent.width + + GeneralCardItem { + id: detailGeneralCard + Layout.alignment: Qt.AlignHCenter + name: "caocao" + } + + TextEdit { + id: generalText + + Layout.fillWidth: true + readOnly: true + selectByKeyboard: true + selectByMouse: false + wrapMode: TextEdit.WordWrap + textFormat: TextEdit.RichText + font.pixelSize: 16 } } } @@ -48,57 +180,11 @@ Item { } } - Drawer { - id: generalDetail - edge: Qt.RightEdge - width: parent.width * 0.4 / mainWindow.scale - height: parent.height / mainWindow.scale - dim: false - clip: true - dragMargin: 0 - scale: mainWindow.scale - transformOrigin: Item.TopRight - - Flickable { - flickableDirection: Flickable.VerticalFlick - contentWidth: generalText.width - contentHeight: generalText.height - width: parent.width * 0.8 - height: parent.height * 0.8 - clip: true - anchors.centerIn: parent - ScrollBar.vertical: ScrollBar {} - - TextEdit { - id: generalText - - property string general: "" - width: generalDetail.width * 0.75 - readOnly: true - selectByKeyboard: true - selectByMouse: true - wrapMode: TextEdit.WordWrap - textFormat: TextEdit.RichText - font.pixelSize: 16 - - function updateGeneral() { - let data = JSON.parse(Backend.callLuaFunction("GetGeneralDetail", [general])); - this.append(Backend.translate(data.kingdom) + " " + Backend.translate(general) + " " + data.hp + "/" + data.maxHp); - data.skill.forEach(t => { - this.append("" + Backend.translate(t.name) + ": " + t.description) - }); - data.related_skill.forEach(t => { - this.append("" + Backend.translate(t.name) + ": " + t.description + "") - }); - } - } - } - } - function loadPackages() { if (loaded) return; let packs = JSON.parse(Backend.callLuaFunction("GetAllGeneralPack", [])); packs.forEach((name) => packages.append({ name: name })); + generalDetail.updateGeneral(); loaded = true; } } diff --git a/test.lua b/test.lua deleted file mode 100644 index 6cc9298d..00000000 --- a/test.lua +++ /dev/null @@ -1,7 +0,0 @@ -fk={}pcall(function()dofile'lua/freekill.lua'end) -a=Exppattern:Parse('slash,asd,df,^sd,sfff,^(xzc,afsd)|34,23|.|.|.|.|^(23~32)') -p(a.matchers) -c = { trueName = 'slash', number = 4, id = 155 } -p(a:match(c)) -p(a:matchExp('^jink')) -print(a)