package game import "fmt" type EffectState struct { Active bool CardTypeID string CasterID string Step int WaitingFor string ChoiceType string ChoiceData map[string]interface{} TempData map[string]interface{} PendingResponses map[string]interface{} AllCollected bool } func (e *Engine) triggerEffect(caster *PlayerState, card *Card) bool { switch card.TypeID { case "student_council_president", "culprit", "alien": return false case "library_committee_member": return e.effectLibrary(caster) case "discipline_committee_member": return e.effectDiscipline(caster) case "health_committee_member": return e.effectHealth(caster) case "go_home_club": return e.effectGoHome(caster) case "accomplice": return e.effectAccomplice(caster) case "class_monitor": return e.effectClassMonitor(caster) case "young_lady": return e.effectYoungLady(caster) case "newspaper_club": return e.effectNewspaper(caster) case "honor_student": return e.effectHonorStudent(caster) case "infected_person": return e.effectInfected(caster) } return false } func (e *Engine) effectLibrary(caster *PlayerState) bool { if len(e.HarmonyZone) == 0 { return false } var cards []map[string]interface{} for _, c := range e.HarmonyZone { cards = append(cards, map[string]interface{}{ "uid": c.UID, "type_id": c.TypeID, "name": c.Name, "mp": c.MP, }) } e.Effect = &EffectState{ Active: true, CardTypeID: "library_committee_member", CasterID: caster.ID, WaitingFor: caster.ID, ChoiceType: "view_cards", Step: 0, ChoiceData: map[string]interface{}{"cards": cards, "message": "调和区的卡牌"}, } return true } func (e *Engine) effectDiscipline(caster *PlayerState) bool { var targets []map[string]interface{} for _, p := range e.Players { if p.ID != caster.ID && !p.IsExited && len(p.Hand) > 0 { targets = append(targets, map[string]interface{}{"player_id": p.ID, "nickname": p.Nickname}) } } if len(targets) == 0 { return false } e.Effect = &EffectState{ Active: true, CardTypeID: "discipline_committee_member", CasterID: caster.ID, WaitingFor: caster.ID, ChoiceType: "select_player", Step: 0, ChoiceData: map[string]interface{}{"targets": targets, "message": "选择要查看手牌的玩家"}, TempData: make(map[string]interface{}), } return true } func (e *Engine) effectHealth(caster *PlayerState) bool { var cards []map[string]interface{} for _, p := range e.Players { for _, c := range p.SkillZone { if c.TypeID != "health_committee_member" { cards = append(cards, map[string]interface{}{ "uid": c.UID, "type_id": c.TypeID, "name": c.Name, "owner_id": p.ID, }) } } } if len(cards) == 0 { return false } e.Effect = &EffectState{ Active: true, CardTypeID: "health_committee_member", CasterID: caster.ID, WaitingFor: caster.ID, ChoiceType: "select_skill_card", Step: 0, ChoiceData: map[string]interface{}{"cards": cards, "message": "选择一张已使用的牌加入手牌"}, } return true } func (e *Engine) effectGoHome(caster *PlayerState) bool { if len(e.HarmonyZone) == 0 || len(caster.Hand) == 0 { return false } var handCards []map[string]interface{} for _, c := range caster.Hand { handCards = append(handCards, map[string]interface{}{"uid": c.UID, "type_id": c.TypeID, "name": c.Name}) } e.Effect = &EffectState{ Active: true, CardTypeID: "go_home_club", CasterID: caster.ID, WaitingFor: caster.ID, ChoiceType: "select_card_from_hand", Step: 0, ChoiceData: map[string]interface{}{"cards": handCards, "message": "选择一张手牌与调和区交换"}, TempData: make(map[string]interface{}), } return true } func (e *Engine) effectAccomplice(caster *PlayerState) bool { var cards []map[string]interface{} for _, p := range e.Players { for _, c := range p.ChallengeZone { cards = append(cards, map[string]interface{}{ "uid": c.UID, "owner_id": p.ID, }) } } if len(cards) == 0 { return false } e.Effect = &EffectState{ Active: true, CardTypeID: "accomplice", CasterID: caster.ID, WaitingFor: caster.ID, ChoiceType: "select_challenge_card", Step: 0, ChoiceData: map[string]interface{}{"cards": cards, "message": "选择一张质疑区的牌"}, TempData: make(map[string]interface{}), } return true } func (e *Engine) effectClassMonitor(caster *PlayerState) bool { var targets []map[string]interface{} for _, p := range e.Players { if !p.IsExited && len(p.Hand) > 0 { targets = append(targets, map[string]interface{}{"player_id": p.ID, "nickname": p.Nickname}) } } if len(targets) < 2 { return false } e.Effect = &EffectState{ Active: true, CardTypeID: "class_monitor", CasterID: caster.ID, WaitingFor: caster.ID, ChoiceType: "select_player", Step: 0, ChoiceData: map[string]interface{}{"targets": targets, "message": "选择换牌的第一名玩家(可以是自己)"}, TempData: make(map[string]interface{}), } return true } func (e *Engine) effectYoungLady(caster *PlayerState) bool { var targets []map[string]interface{} for _, p := range e.Players { if p.ID != caster.ID && !p.IsExited && len(p.Hand) > 0 { targets = append(targets, map[string]interface{}{"player_id": p.ID, "nickname": p.Nickname}) } } if len(targets) == 0 { return false } e.Effect = &EffectState{ Active: true, CardTypeID: "young_lady", CasterID: caster.ID, WaitingFor: caster.ID, ChoiceType: "select_player", Step: 0, ChoiceData: map[string]interface{}{"targets": targets, "message": "选择一名玩家抽取手牌"}, TempData: make(map[string]interface{}), } return true } func (e *Engine) effectNewspaper(caster *PlayerState) bool { e.Effect = &EffectState{ Active: true, CardTypeID: "newspaper_club", CasterID: caster.ID, ChoiceType: "all_select_card", Step: 0, ChoiceData: map[string]interface{}{"message": "选择一张手牌传给左手边的玩家"}, PendingResponses: make(map[string]interface{}), } for _, p := range e.Players { if !p.IsExited && len(p.Hand) > 0 { e.Effect.WaitingFor = p.ID } } return true } func (e *Engine) effectHonorStudent(caster *PlayerState) bool { var culpritHolder, alienHolder string for _, p := range e.Players { if p.ID == caster.ID { continue } for _, c := range p.Hand { if c.TypeID == "culprit" { culpritHolder = p.ID } if c.TypeID == "alien" { alienHolder = p.ID } } } e.Effect = &EffectState{ Active: true, CardTypeID: "honor_student", CasterID: caster.ID, TempData: map[string]interface{}{ "culprit_holder": culpritHolder, "alien_holder": alienHolder, }, } if culpritHolder != "" { cp := e.GetPlayer(culpritHolder) if cp != nil && !cp.IsExited { e.Effect.Step = 0 e.Effect.WaitingFor = culpritHolder e.Effect.ChoiceType = "select_option" e.Effect.ChoiceData = map[string]interface{}{ "message": "有人使用了优等生!你是犯人,必须吐出舌头。", "options": []map[string]interface{}{ {"value": "long", "label": "吐长舌头"}, {"value": "short", "label": "吐短舌头"}, }, } return true } } if alienHolder != "" { ap := e.GetPlayer(alienHolder) if ap != nil && !ap.IsExited { e.Effect.Step = 1 e.Effect.WaitingFor = alienHolder e.Effect.ChoiceType = "select_option" e.Effect.ChoiceData = map[string]interface{}{ "message": "有人使用了优等生!你是外星人,是否要装作犯人吐舌头?", "options": []map[string]interface{}{ {"value": "long", "label": "吐长舌头"}, {"value": "short", "label": "吐短舌头"}, {"value": "none", "label": "不吐舌头"}, }, } return true } } e.Effect.Step = 99 e.Effect.WaitingFor = caster.ID e.Effect.ChoiceType = "view_cards" e.Effect.ChoiceData = map[string]interface{}{"message": "优等生效果:没有人吐舌头"} return true } func (e *Engine) effectInfected(caster *PlayerState) bool { if len(caster.Hand) <= 1 { return false } caster.InfectedActive = true return false } // HandleEffectResponse processes a player's response during effect resolution func (e *Engine) HandleEffectResponse(playerID string, response map[string]interface{}) error { ef := e.Effect if ef == nil || !ef.Active { return fmt.Errorf("no active effect") } switch ef.CardTypeID { case "library_committee_member", "student_council_president": return e.finishEffect() case "discipline_committee_member": return e.respondDiscipline(playerID, response) case "health_committee_member": return e.respondHealth(playerID, response) case "go_home_club": return e.respondGoHome(playerID, response) case "accomplice": return e.respondAccomplice(playerID, response) case "class_monitor": return e.respondClassMonitor(playerID, response) case "young_lady": return e.respondYoungLady(playerID, response) case "newspaper_club": return e.respondNewspaper(playerID, response) case "honor_student": return e.respondHonorStudent(playerID, response) case "infected_person": return e.respondInfected(playerID, response) } return e.finishEffect() } func (e *Engine) respondDiscipline(pid string, resp map[string]interface{}) error { ef := e.Effect if ef.Step == 0 { tid, _ := resp["player_id"].(string) target := e.GetPlayer(tid) if target == nil { return fmt.Errorf("invalid target") } var cards []map[string]interface{} for _, c := range target.Hand { cards = append(cards, map[string]interface{}{"uid": c.UID, "type_id": c.TypeID, "name": c.Name, "mp": c.MP}) } ef.Step = 1 ef.ChoiceType = "view_cards" ef.ChoiceData = map[string]interface{}{"cards": cards, "message": target.Nickname + " 的手牌"} return nil } return e.finishEffect() } func (e *Engine) respondHealth(pid string, resp map[string]interface{}) error { uid, _ := resp["card_uid"].(string) for _, p := range e.Players { for i, c := range p.SkillZone { if c.UID == uid && c.TypeID != "health_committee_member" { p.SkillZone = append(p.SkillZone[:i], p.SkillZone[i+1:]...) caster := e.GetPlayer(e.Effect.CasterID) caster.Hand = append(caster.Hand, c) return e.finishEffect() } } } return fmt.Errorf("card not found") } func (e *Engine) respondGoHome(pid string, resp map[string]interface{}) error { ef := e.Effect if ef.Step == 0 { uid, _ := resp["card_uid"].(string) ef.TempData["hand_card_uid"] = uid ef.Step = 1 var hCards []map[string]interface{} for i, c := range e.HarmonyZone { hCards = append(hCards, map[string]interface{}{"uid": c.UID, "index": i}) } ef.ChoiceType = "select_harmony_card" ef.ChoiceData = map[string]interface{}{"cards": hCards, "message": "选择调和区的一张牌"} return nil } hUID, _ := resp["card_uid"].(string) handUID, _ := ef.TempData["hand_card_uid"].(string) caster := e.GetPlayer(ef.CasterID) var handCard *Card for i, c := range caster.Hand { if c.UID == handUID { handCard = c caster.Hand = append(caster.Hand[:i], caster.Hand[i+1:]...) break } } var harmonyCard *Card for i, c := range e.HarmonyZone { if c.UID == hUID { harmonyCard = c e.HarmonyZone = append(e.HarmonyZone[:i], e.HarmonyZone[i+1:]...) break } } if handCard != nil { e.HarmonyZone = append(e.HarmonyZone, handCard) } if harmonyCard != nil { caster.Hand = append(caster.Hand, harmonyCard) } return e.finishEffect() } func (e *Engine) respondAccomplice(pid string, resp map[string]interface{}) error { ef := e.Effect if ef.Step == 0 { uid, _ := resp["card_uid"].(string) ef.TempData["challenge_uid"] = uid owner, _ := resp["owner_id"].(string) ef.TempData["challenge_owner"] = owner ef.Step = 1 var targets []map[string]interface{} for _, p := range e.Players { if p.ID != owner { targets = append(targets, map[string]interface{}{"player_id": p.ID, "nickname": p.Nickname}) } } ef.ChoiceType = "select_player" ef.ChoiceData = map[string]interface{}{"targets": targets, "message": "选择转移到哪个玩家面前"} return nil } targetID, _ := resp["player_id"].(string) cUID, _ := ef.TempData["challenge_uid"].(string) ownerID, _ := ef.TempData["challenge_owner"].(string) owner := e.GetPlayer(ownerID) target := e.GetPlayer(targetID) if owner == nil || target == nil { return e.finishEffect() } for i, c := range owner.ChallengeZone { if c.UID == cUID { owner.ChallengeZone = append(owner.ChallengeZone[:i], owner.ChallengeZone[i+1:]...) target.ChallengeZone = append(target.ChallengeZone, c) break } } return e.finishEffect() } func (e *Engine) respondClassMonitor(pid string, resp map[string]interface{}) error { ef := e.Effect if ef.Step == 0 { aid, _ := resp["player_id"].(string) ef.TempData["player_a_id"] = aid ef.Step = 1 var targets []map[string]interface{} for _, p := range e.Players { if p.ID != aid && !p.IsExited && len(p.Hand) > 0 { targets = append(targets, map[string]interface{}{"player_id": p.ID, "nickname": p.Nickname}) } } ef.WaitingFor = ef.CasterID ef.ChoiceType = "select_player" ef.ChoiceData = map[string]interface{}{"targets": targets, "message": "选择换牌的第二名玩家"} return nil } if ef.Step == 1 { bid, _ := resp["player_id"].(string) ef.TempData["player_b_id"] = bid ef.Step = 2 aid, _ := ef.TempData["player_a_id"].(string) playerA := e.GetPlayer(aid) ef.WaitingFor = aid var cards []map[string]interface{} for _, c := range playerA.Hand { cards = append(cards, map[string]interface{}{"uid": c.UID, "type_id": c.TypeID, "name": c.Name}) } ef.ChoiceType = "select_card_from_hand" ef.ChoiceData = map[string]interface{}{"cards": cards, "message": playerA.Nickname + ":选择一张手牌交给对方"} return nil } if ef.Step == 2 { uid, _ := resp["card_uid"].(string) ef.TempData["card_a_uid"] = uid ef.Step = 3 bid, _ := ef.TempData["player_b_id"].(string) playerB := e.GetPlayer(bid) ef.WaitingFor = bid var cards []map[string]interface{} for _, c := range playerB.Hand { cards = append(cards, map[string]interface{}{"uid": c.UID, "type_id": c.TypeID, "name": c.Name}) } ef.ChoiceType = "select_card_from_hand" ef.ChoiceData = map[string]interface{}{"cards": cards, "message": playerB.Nickname + ":选择一张手牌交给对方"} return nil } bUID, _ := resp["card_uid"].(string) aUID, _ := ef.TempData["card_a_uid"].(string) aid, _ := ef.TempData["player_a_id"].(string) bid, _ := ef.TempData["player_b_id"].(string) pA := e.GetPlayer(aid) pB := e.GetPlayer(bid) var cA, cB *Card for i, c := range pA.Hand { if c.UID == aUID { cA = c pA.Hand = append(pA.Hand[:i], pA.Hand[i+1:]...) break } } for i, c := range pB.Hand { if c.UID == bUID { cB = c pB.Hand = append(pB.Hand[:i], pB.Hand[i+1:]...) break } } if cA != nil { pB.Hand = append(pB.Hand, cA) } if cB != nil { pA.Hand = append(pA.Hand, cB) } return e.finishEffect() } func (e *Engine) respondYoungLady(pid string, resp map[string]interface{}) error { ef := e.Effect caster := e.GetPlayer(ef.CasterID) if ef.Step == 0 { tid, _ := resp["player_id"].(string) target := e.GetPlayer(tid) if target == nil || len(target.Hand) == 0 { return e.finishEffect() } drawn := target.Hand[e.rng.Intn(len(target.Hand))] e.removeFromHand(target, drawn.UID) caster.Hand = append(caster.Hand, drawn) ef.TempData["target_id"] = tid ef.Step = 1 ef.WaitingFor = ef.CasterID var cards []map[string]interface{} for _, c := range caster.Hand { cards = append(cards, map[string]interface{}{"uid": c.UID, "type_id": c.TypeID, "name": c.Name}) } ef.ChoiceType = "select_card_from_hand" ef.ChoiceData = map[string]interface{}{ "cards": cards, "message": "从 " + target.Nickname + " 抽到了「" + drawn.Name + "」,选择一张手牌反面返还", } return nil } uid, _ := resp["card_uid"].(string) tid, _ := ef.TempData["target_id"].(string) target := e.GetPlayer(tid) for i, c := range caster.Hand { if c.UID == uid { caster.Hand = append(caster.Hand[:i], caster.Hand[i+1:]...) if target != nil { target.Hand = append(target.Hand, c) } break } } return e.finishEffect() } func (e *Engine) respondNewspaper(pid string, resp map[string]interface{}) error { ef := e.Effect uid, _ := resp["card_uid"].(string) ef.PendingResponses[pid] = uid allDone := true for _, p := range e.Players { if !p.IsExited && len(p.Hand) > 0 { if _, ok := ef.PendingResponses[p.ID]; !ok { allDone = false break } } } if !allDone { return nil } type passInfo struct { from *PlayerState card *Card } var passes []passInfo activePlayers := e.getActivePlayers() for _, p := range activePlayers { uidStr, ok := ef.PendingResponses[p.ID].(string) if !ok { continue } for _, c := range p.Hand { if c.UID == uidStr { passes = append(passes, passInfo{from: p, card: c}) break } } } for _, pi := range passes { e.removeFromHand(pi.from, pi.card.UID) } for i, pi := range passes { nextIdx := (i + 1) % len(activePlayers) activePlayers[nextIdx].Hand = append(activePlayers[nextIdx].Hand, pi.card) } return e.finishEffect() } func (e *Engine) respondHonorStudent(pid string, resp map[string]interface{}) error { ef := e.Effect if ef.Step == 0 { tongue, _ := resp["value"].(string) culpritID, _ := ef.TempData["culprit_holder"].(string) cp := e.GetPlayer(culpritID) name := culpritID if cp != nil { name = cp.Nickname } tongues := []interface{}{map[string]interface{}{"nickname": name, "tongue": tongue}} ef.TempData["tongues"] = tongues alienHolder, _ := ef.TempData["alien_holder"].(string) if alienHolder != "" { ap := e.GetPlayer(alienHolder) if ap != nil && !ap.IsExited { ef.Step = 1 ef.WaitingFor = alienHolder ef.ChoiceType = "select_option" ef.ChoiceData = map[string]interface{}{ "message": "有人使用了优等生!你是外星人,是否要装作犯人吐舌头?", "options": []interface{}{ map[string]interface{}{"value": "long", "label": "吐长舌头"}, map[string]interface{}{"value": "short", "label": "吐短舌头"}, map[string]interface{}{"value": "none", "label": "不吐舌头"}, }, } return nil } } return e.showHonorResult() } if ef.Step == 1 { tongue, _ := resp["value"].(string) if tongue != "none" { alienHolder, _ := ef.TempData["alien_holder"].(string) ap := e.GetPlayer(alienHolder) name := alienHolder if ap != nil { name = ap.Nickname } tongues, _ := ef.TempData["tongues"].([]interface{}) tongues = append(tongues, map[string]interface{}{"nickname": name, "tongue": tongue}) ef.TempData["tongues"] = tongues } return e.showHonorResult() } return e.finishEffect() } func (e *Engine) showHonorResult() error { ef := e.Effect tongues, _ := ef.TempData["tongues"].([]interface{}) msg := fmt.Sprintf("优等生效果:%d人吐了舌头\n", len(tongues)) for _, t := range tongues { tm, _ := t.(map[string]interface{}) nick, _ := tm["nickname"].(string) tongue, _ := tm["tongue"].(string) label := "长舌头" if tongue == "short" { label = "短舌头" } msg += fmt.Sprintf(" • %s — %s\n", nick, label) } ef.Step = 99 ef.WaitingFor = ef.CasterID ef.ChoiceType = "view_cards" ef.ChoiceData = map[string]interface{}{"message": msg, "tongues": tongues} return nil } func (e *Engine) finishEffect() error { e.Effect = nil caster := e.GetPlayer(e.Players[e.CurrentTurn].ID) e.finishTurn(caster) return nil } func (e *Engine) getActivePlayers() []*PlayerState { var active []*PlayerState for _, p := range e.Players { if !p.IsExited && len(p.Hand) > 0 { active = append(active, p) } } return active } func (e *Engine) CheckInfectedEffect() bool { p := e.CurrentPlayer() if p == nil || !p.InfectedActive { return false } still := false for _, c := range p.SkillZone { if c.TypeID == "infected_person" { still = true break } } if !still || len(e.HarmonyZone) == 0 { p.InfectedActive = false return false } p.InfectedActive = false var hCards []map[string]interface{} for _, c := range e.HarmonyZone { hCards = append(hCards, map[string]interface{}{"uid": c.UID}) } e.Effect = &EffectState{ Active: true, CardTypeID: "infected_person", CasterID: p.ID, WaitingFor: p.ID, ChoiceType: "select_harmony_card", Step: 0, ChoiceData: map[string]interface{}{"cards": hCards, "message": "感染者效果:选择调和区的一张牌加入手牌"}, } return true } func (e *Engine) respondInfected(pid string, resp map[string]interface{}) error { uid, _ := resp["card_uid"].(string) caster := e.GetPlayer(e.Effect.CasterID) for i, c := range e.HarmonyZone { if c.UID == uid { e.HarmonyZone = append(e.HarmonyZone[:i], e.HarmonyZone[i+1:]...) caster.Hand = append(caster.Hand, c) break } } e.Effect = nil return nil }