Java实现卡牌技能“每轮限一次的同名复制/无效”机制详细方案
## 🎯 需求分析 这段文字描述的是一个**游戏技能机制**,类似三国杀等卡牌游戏中的技能效果: > **每轮各限一次**,其他角色使用**伤害牌**指定你为目标后,你可选择一项: > 1. 从牌堆中获得一张同名牌 > 2. 弃置一张同名牌令此牌无效 --- ## 💡 核心机制拆解 | 机制 | 说明 | |------|------| | 触发时机 | 其他角色使用伤害牌指定你为目标后 | | 触发条件 | 每轮各限一次(即每个轮次内每种选择各限一次) | | 可选项1 | 从牌堆中获得一张同名牌(即复制该伤害牌) | | 可选项2 | 弃置一张同名牌令该伤害牌无效(需要手牌中有同名牌) | --- ## 🧱 代码实现(Java) ### 1️⃣ 基础类定义 ```java import java.util.*; /** * 伤害牌 */ class DamageCard { private String name; private int damageValue; public DamageCard(String name, int damageValue) { this.name = name; this.damageValue = damageValue; } public String getName() { return name; } public int getDamageValue() { return damageValue; } } /** * 玩家 */ class Player { private String name; private List<DamageCard> handCards = new ArrayList<>(); private int hp; private int maxHp; public Player(String name, int maxHp) { this.name = name; this.maxHp = maxHp; this.hp = maxHp; } public String getName() { return name; } public List<DamageCard> getHandCards() { return handCards; } public void addCard(DamageCard card) { handCards.add(card); } public boolean removeCard(DamageCard card) { return handCards.remove(card); } public int getHp() { return hp; } public void takeDamage(int damage) { this.hp = Math.max(0, this.hp - damage); System.out.println(name + " 受到 " + damage + " 点伤害,剩余 HP=" + hp); } public boolean isAlive() { return hp > 0; } } ``` --- ### 2️⃣ 技能实现类 ```java /** * 技能上下文 - 持有核心逻辑 */ class SkillContext { private Player self; private List<DamageCard> drawPile; // 牌堆 private Map<String, Integer> skillUsedThisRound; // 每轮各选项使用次数 public SkillContext(Player self, List<DamageCard> drawPile) { this.self = self; this.drawPile = drawPile; this.skillUsedThisRound = new HashMap<>(); // 初始化每个选项为 0 次 skillUsedThisRound.put("COPY", 0); skillUsedThisRound.put("NULLIFY", 0); } /** * 每轮重置技能使用次数 */ public void onRoundStart() { skillUsedThisRound.put("COPY", 0); skillUsedThisRound.put("NULLIFY", 0); System.out.println("--- 新回合开始,技能次数已重置 ---"); } /** * 其他角色使用伤害牌指定我为目标后触发 * @param attacker 攻击者 * @param damageCard 使用的伤害牌 * @return true 表示伤害牌被处理(未生效或已复制),false 表示未处理 */ public boolean onDamageTargeted(Player attacker, DamageCard damageCard) { System.out.println(attacker.getName() + " 使用 [" + damageCard.getName() + "] 指定 " + self.getName() + " 为目标"); // 1. 询问玩家选择(模拟交互,实际开发中可由前端/UI 触发) Scanner scanner = new Scanner(System.in); System.out.println("选择技能效果:"); System.out.println("1. 从牌堆获得一张同名牌"); System.out.println("2. 弃置一张同名牌令此牌无效"); System.out.println("0. 不触发"); int choice = scanner.nextInt(); switch (choice) { case 1: return useCopyEffect(damageCard); case 2: return useNullifyEffect(damageCard); default: // 不触发技能,伤害正常结算 self.takeDamage(damageCard.getDamageValue()); return true; } } /** * 效果1:从牌堆获得一张同名牌(每轮限一次) */ private boolean useCopyEffect(DamageCard damageCard) { int useCount = skillUsedThisRound.get("COPY"); if (useCount >= 1) { System.out.println("本回合已使用过【复制】效果,无法再次使用!"); return false; } // 查找牌堆中同名牌 for (DamageCard card : drawPile) { if (card.getName().equals(damageCard.getName())) { drawPile.remove(card); self.addCard(card); skillUsedThisRound.put("COPY", useCount + 1); System.out.println(self.getName() + " 从牌堆获得了一张 [" + card.getName() + "]"); // 伤害正常结算 self.takeDamage(damageCard.getDamageValue()); return true; } } System.out.println("牌堆中没有同名牌,技能未生效"); return false; } /** * 效果2:弃置一张同名牌令此牌无效(每轮限一次) */ private boolean useNullifyEffect(DamageCard damageCard) { int useCount = skillUsedThisRound.get("NULLIFY"); if (useCount >= 1) { System.out.println("本回合已使用过【无效】效果,无法再次使用!"); return false; } // 查找手牌中是否有同名牌 for (DamageCard card : self.getHandCards()) { if (card.getName().equals(damageCard.getName())) { self.removeCard(card); skillUsedThisRound.put("NULLIFY", useCount + 1); System.out.println(self.getName() + " 弃置了一张 [" + card.getName() + "],使伤害牌失效!"); // 伤害不结算 return true; } } System.out.println("手牌中没有同名牌,无法发动无效效果"); return false; } } ``` --- ### 3️⃣ 主逻辑演示 ```java public class GameDemo { public static void main(String[] args) { // 创建牌堆 List<DamageCard> drawPile = new ArrayList<>(); drawPile.add(new DamageCard("杀", 1)); drawPile.add(new DamageCard("杀", 1)); drawPile.add(new DamageCard("火攻", 1)); drawPile.add(new DamageCard("雷击", 2)); // 创建玩家 Player attacker = new Player("刘备", 4); Player defender = new Player("曹操", 4); // 给防守方初始手牌 defender.addCard(new DamageCard("杀", 1)); // 创建技能上下文 SkillContext skill = new SkillContext(defender, drawPile); // 触发第一轮第一次伤害 System.out.println("=== 第 1 轮 ==="); DamageCard card1 = new DamageCard("杀", 1); skill.onRoundStart(); // 轮次开始重置 skill.onDamageTargeted(attacker, card1); // 再次触发同轮第二次伤害 DamageCard card2 = new DamageCard("火攻", 1); skill.onDamageTargeted(attacker, card2); skill.onDamageTargeted(attacker, card2); // 第三次,同轮次数已用完 // 新的一轮 System.out.println("\n=== 第 2 轮 ==="); skill.onRoundStart(); DamageCard card3 = new DamageCard("雷击", 2); skill.onDamageTargeted(attacker, card3); } } ``` --- ### 4️⃣ 输出示例 ``` === 第 1 轮 === --- 新回合开始,技能次数已重置 --- 刘备 使用 [杀] 指定 曹操 为目标 选择技能效果: 1. 从牌堆获得一张同名牌 2. 弃置一张同名牌令此牌无效 0. 不触发 1 曹操 从牌堆获得了一张 [杀] 曹操 受到 1 点伤害,剩余 HP=3 刘备 使用 [火攻] 指定 曹操 为目标 选择技能效果: 1. 从牌堆获得一张同名牌 2. 弃置一张同名牌令此牌无效 0. 不触发 2 曹操 弃置了一张 [杀],使伤害牌失效! 刘备 使用 [火攻] 指定 曹操 为目标 选择技能效果: 1. 从牌堆获得一张同名牌 2. 弃置一张同名牌令此牌无效 0. 不触发 1 本回合已使用过【复制】效果,无法再次使用! 曹操 受到 1 点伤害,剩余 HP=2 === 第 2 轮 === --- 新回合开始,技能次数已重置 --- 刘备 使用 [雷击] 指定 曹操 为目标 选择技能效果: 1. 从牌堆获得一张同名牌 2. 弃置一张同名牌令此牌无效 0. 不触发 1 曹操 从牌堆获得了一张 [雷击] 曹操 受到 2 点伤害,剩余 HP=0 ``` --- ## ⚙️ 开发建议与优化方向 1. **事件驱动架构**:建议使用观察者模式或事件总线,将技能触发从主流程解耦,便于扩展更多技能。 2. **状态管理**:使用枚举定义技能选项(`SkillOption.COPY` / `SkillOption.NULLIFY`),避免魔法数字。 3. **每轮限制**:使用 `Map<SkillOption, Boolean>` 或位运算(两个 `boolean`)标记是否已使用,重置时机放在轮次管理器。 4. **AI 决策**:若是 AI 玩家,可写决策策略模式,优先使用无效效果(防御),或视场况选择复制。 5. **异常处理**:牌堆为空、手牌为空时抛出友好异常,并回滚操作。 --- ## 📚 参考学习资源 - 《Effective Java》—— 关于枚举类型、泛型的最佳实践 - 《设计模式:可复用面向对象软件的基础》—— 观察者模式、策略模式 - Java 官方文档:Java Collections Framework --- ## 🧩 扩展思考 - 如果技能修改为“**每回合限一次**”(而非每轮),只需将重置逻辑放到回合开始处。 - 若伤害牌是**延时锦囊**,需要额外处理锁定/解控逻辑。 - 可加入**Buff/ Debuff** 系统,提升技能的多样性与可玩性。 ### 🛠️ 核心要点 | 功能 | 实现方式 | |------|----------| | 限定次数 | `Map<String, Integer>` 或两个布尔变量 | | 查找同名牌 | 遍历牌堆/手牌,用 `name.equals()` 匹配 | | 牌堆操作 | 从 `List` 中 `remove` | | 技能触发 | 硬编码调用 or 事件/观察者模式 | --- ######[AI写代码神器 | 2619点数解答 | 2026-08-29 22:10:04]
- Java 实现复制粘贴功能:打造简易文本复制粘贴程序(GPT | 1497点数解答 | 2024-08-25 09:40:33)531
- 解决数据库“dm.jdbc.driver.dmexception: 数据溢出”问题的全步骤指南(阿里通义 | 598点数解答 | 2024-10-15 15:48:38)439
- 达梦数据库数据溢出问题排查及多维度解决方法(阿里通义 | 403点数解答 | 2024-10-15 15:48:41)1462
- Python 实现球类:精准计算半径、表面积与体积,附输入验证与异常处理!(阿里通义 | 261点数解答 | 2024-11-28 21:19:39)571
- 游戏惊变!成为【杀】的目标,弃牌触发两大惊人选择!(字节豆包 | 35点数解答 | 2025-02-17 18:55:50)243
- 缜密逻辑,全新无名杀“风餐”技能代码精心打造与深度解析 (字节豆包 | 469点数解答 | 2025-04-05 11:37:06)402
- 工程师必备:十大APP性能优化方案大揭秘(百度文心 | 300点数解答 | 2023-11-09 18:31:04)428
- 含不规则缺陷原材料的最优切割方案建模与损失率、利用率计算(DeepSeek | 2367点数解答 | 2025-05-10 23:47:30)359
- "Java Code: Uncovering Stock Statistics through CSV File Reading"(字节豆包 | 66点数解答 | 2024-11-13 15:31:04)448
- 51 单片机:定时器 0 实现 8 个 LED 循环点亮,附代码及优化建议(字节豆包 | 1193点数解答 | 2024-12-27 15:10:29)484
- 深入解析Golang Work Stealing机制:多核处理器工作负载平衡之道(百度文心 | 391点数解答 | 2023-11-09 18:16:09)357
- 深入剖析Go语言:揭秘Go Hand Off机制及示例演示 (百度文心 | 413点数解答 | 2023-11-09 18:16:10)316