2 * Summarize game information
\r
4 * Copyright(c) 2009 olyutorskii
\r
5 * $Id: GameSummary.java 1028 2010-05-13 10:15:11Z olyutorskii $
\r
8 package jp.sourceforge.jindolf;
\r
10 import java.net.MalformedURLException;
\r
11 import java.net.URI;
\r
12 import java.net.URISyntaxException;
\r
13 import java.net.URL;
\r
14 import java.text.DateFormat;
\r
15 import java.util.Collections;
\r
16 import java.util.Comparator;
\r
17 import java.util.Date;
\r
18 import java.util.EnumMap;
\r
19 import java.util.HashMap;
\r
20 import java.util.LinkedList;
\r
21 import java.util.List;
\r
22 import java.util.Map;
\r
23 import java.util.Set;
\r
24 import jp.sourceforge.jindolf.corelib.Destiny;
\r
25 import jp.sourceforge.jindolf.corelib.GameRole;
\r
26 import jp.sourceforge.jindolf.corelib.SysEventType;
\r
27 import jp.sourceforge.jindolf.corelib.Team;
\r
28 import jp.sourceforge.jindolf.corelib.VillageState;
\r
33 public class GameSummary{
\r
35 /** キャスティング表示用Comparator。 */
\r
36 public static final Comparator<Player> COMPARATOR_CASTING =
\r
37 new CastingComparator();
\r
40 * プレイヤーのリストから役職バランス文字列を得る。
\r
42 * @param players プレイヤーのリスト
\r
45 public static String getRoleBalanceSequence(List<Player> players){
\r
46 List<GameRole> roleList = new LinkedList<GameRole>();
\r
47 for(Player player : players){
\r
48 GameRole role = player.getRole();
\r
51 Collections.sort(roleList, GameRole.getPowerBalanceComparator());
\r
53 StringBuilder result = new StringBuilder();
\r
54 for(GameRole role : roleList){
\r
55 char ch = role.getShortName();
\r
59 return result.toString();
\r
62 private final Map<Avatar, Player> playerMap =
\r
63 new HashMap<Avatar, Player>();
\r
64 private final List<Player> playerList =
\r
65 new LinkedList<Player>();
\r
66 private final Map<SysEventType, List<SysEvent>> eventMap =
\r
67 new EnumMap<SysEventType, List<SysEvent>>(SysEventType.class);
\r
69 private final Village village;
\r
72 private Team winner;
\r
75 private int ctScryVillage = 0;
\r
76 private int ctScryHamster = 0;
\r
77 private int ctScryMadman = 0;
\r
78 private int ctScryWolf = 0;
\r
81 private int ctGuardVillage = 0;
\r
82 private int ctGuardHamster = 0;
\r
83 private int ctGuardMadman = 0;
\r
84 private int ctGuardWolf = 0;
\r
85 private int ctGuardVillageGJ = 0;
\r
86 private int ctGuardHamsterGJ = 0;
\r
87 private int ctGuardMadmanGJ = 0;
\r
88 private int ctGuardFakeGJ = 0;
\r
91 private long talk1stTimeMs = -1;
\r
92 private long talkLastTimeMs = -1;
\r
98 public GameSummary(Village village){
\r
101 VillageState state = village.getState();
\r
102 if( state != VillageState.EPILOGUE
\r
103 && state != VillageState.GAMEOVER){
\r
104 throw new IllegalStateException();
\r
107 this.village = village;
\r
117 private void summarize(){
\r
122 summarizePlayers();
\r
124 for(Period period : this.village.getPeriodList()){
\r
125 summarizePeriod(period);
\r
135 * SysEventの種別ごとに集計する。
\r
137 private void buildEventMap(){
\r
138 for(SysEventType type : SysEventType.values()){
\r
139 List<SysEvent> eventList = new LinkedList<SysEvent>();
\r
140 this.eventMap.put(type, eventList);
\r
143 for(Period period : this.village.getPeriodList()){
\r
144 for(Topic topic : period.getTopicList()){
\r
145 if( ! (topic instanceof SysEvent) ) continue;
\r
146 SysEvent event = (SysEvent) topic;
\r
147 SysEventType type = event.getSysEventType();
\r
148 List<SysEvent> eventList = this.eventMap.get(type);
\r
149 eventList.add(event);
\r
159 private void summarizeWinner(){
\r
160 List<SysEvent> eventList;
\r
162 eventList = this.eventMap.get(SysEventType.WINVILLAGE);
\r
163 if( ! eventList.isEmpty() ){
\r
164 this.winner = Team.VILLAGE;
\r
167 eventList = this.eventMap.get(SysEventType.WINWOLF);
\r
168 if( ! eventList.isEmpty() ){
\r
169 this.winner = Team.WOLF;
\r
172 eventList = this.eventMap.get(SysEventType.WINHAMSTER);
\r
173 if( ! eventList.isEmpty() ){
\r
174 this.winner = Team.HAMSTER;
\r
177 if(this.winner == null) assert false;
\r
185 private void summarizePlayers(){
\r
186 List<SysEvent> eventList;
\r
188 List<Avatar> avatarList;
\r
189 List<GameRole> roleList;
\r
190 List<Integer> integerList;
\r
191 List<CharSequence> textList;
\r
193 eventList = this.eventMap.get(SysEventType.ONSTAGE);
\r
194 for(SysEvent event : eventList){
\r
195 avatarList = event.getAvatarList();
\r
196 integerList = event.getIntegerList();
\r
197 Avatar onstageAvatar = avatarList.get(0);
\r
198 Player onstagePlayer = registPlayer(onstageAvatar);
\r
199 onstagePlayer.setEntryNo(integerList.get(0));
\r
202 eventList = this.eventMap.get(SysEventType.PLAYERLIST);
\r
203 assert eventList.size() == 1;
\r
204 SysEvent event = eventList.get(0);
\r
206 avatarList = event.getAvatarList();
\r
207 roleList = event.getRoleList();
\r
208 integerList = event.getIntegerList();
\r
209 textList = event.getCharSequenceList();
\r
210 int avatarNum = avatarList.size();
\r
211 for(int idx = 0; idx < avatarNum; idx++){
\r
212 Avatar avatar = avatarList.get(idx);
\r
213 GameRole role = roleList.get(idx);
\r
214 CharSequence urlText = textList.get(idx * 2);
\r
215 CharSequence idName = textList.get(idx * 2 + 1);
\r
216 int liveOrDead = integerList.get(idx);
\r
218 Player player = registPlayer(avatar);
\r
219 player.setRole(role);
\r
220 player.setUrlText(urlText.toString());
\r
221 player.setIdName(idName.toString());
\r
222 if(liveOrDead != 0){ // 生存
\r
223 player.setObitDay(-1);
\r
224 player.setDestiny(Destiny.ALIVE);
\r
227 this.playerList.add(player);
\r
235 * @param period Period
\r
237 private void summarizePeriod(Period period){
\r
238 int day = period.getDay();
\r
239 for(Topic topic : period.getTopicList()){
\r
240 if(topic instanceof SysEvent){
\r
241 SysEvent sysEvent = (SysEvent) topic;
\r
242 summarizeDestiny(day, sysEvent);
\r
252 * @param sysEvent システムイベント
\r
254 private void summarizeDestiny(int day, SysEvent sysEvent){
\r
255 List<Avatar> avatarList = sysEvent.getAvatarList();
\r
256 List<Integer> integerList = sysEvent.getIntegerList();
\r
258 int avatarTotal = avatarList.size();
\r
259 Avatar lastAvatar = null;
\r
260 if(avatarTotal > 0) lastAvatar = avatarList.get(avatarTotal - 1);
\r
262 SysEventType eventType = sysEvent.getSysEventType();
\r
264 case EXECUTION: // G国のみ
\r
265 if(integerList.get(avatarTotal - 1) > 0) break; // 処刑無し
\r
266 Player executedPl = registPlayer(lastAvatar);
\r
267 executedPl.setDestiny(Destiny.EXECUTED);
\r
268 executedPl.setObitDay(day);
\r
271 Avatar suddenDeathAvatar = avatarList.get(0);
\r
272 Player suddenDeathPlayer = registPlayer(suddenDeathAvatar);
\r
273 suddenDeathPlayer.setDestiny(Destiny.SUDDENDEATH);
\r
274 suddenDeathPlayer.setObitDay(day);
\r
276 case COUNTING: // G国COUNTING2は運命に関係なし
\r
277 if(avatarTotal % 2 == 0) break; // 処刑無し
\r
278 Player executedPlayer = registPlayer(lastAvatar);
\r
279 executedPlayer.setDestiny(Destiny.EXECUTED);
\r
280 executedPlayer.setObitDay(day);
\r
283 for(Avatar avatar : avatarList){
\r
284 Player player = registPlayer(avatar);
\r
285 player.setDestiny(Destiny.EATEN);
\r
286 player.setObitDay(day);
\r
288 // TODO E国ハム溶け処理は後回し
\r
300 private void summarizeTime(){
\r
301 for(Period period : this.village.getPeriodList()){
\r
302 for(Topic topic : period.getTopicList()){
\r
303 if( ! (topic instanceof Talk) ) continue;
\r
304 Talk talk = (Talk) topic;
\r
306 long epoch = talk.getTimeFromID();
\r
308 if(this.talk1stTimeMs < 0) this.talk1stTimeMs = epoch;
\r
309 if(this.talkLastTimeMs < 0) this.talkLastTimeMs = epoch;
\r
311 if(epoch < this.talk1stTimeMs ) this.talk1stTimeMs = epoch;
\r
312 if(epoch > this.talkLastTimeMs) this.talkLastTimeMs = epoch;
\r
322 private void summarizeJudge(){
\r
323 List<SysEvent> eventList = this.eventMap.get(SysEventType.JUDGE);
\r
325 for(SysEvent event : eventList){
\r
326 List<Avatar> avatarList = event.getAvatarList();
\r
327 Avatar avatar = avatarList.get(1);
\r
328 Player seered = getPlayer(avatar);
\r
329 GameRole role = seered.getRole();
\r
331 case WOLF: this.ctScryWolf++; break;
\r
332 case MADMAN: this.ctScryMadman++; break;
\r
333 case HAMSTER: this.ctScryHamster++; break;
\r
334 default: this.ctScryVillage++; break;
\r
345 public CharSequence dumpSeerActivity(){
\r
346 StringBuilder result = new StringBuilder();
\r
348 if(this.ctScryVillage > 0){
\r
349 result.append("村陣営を");
\r
350 result.append(this.ctScryVillage);
\r
351 result.append("回");
\r
354 if(this.ctScryHamster > 0){
\r
355 if(result.length() > 0) result.append('、');
\r
356 result.append("ハムスターを");
\r
357 result.append(this.ctScryHamster);
\r
358 result.append("回");
\r
361 if(this.ctScryMadman > 0){
\r
362 if(result.length() > 0) result.append('、');
\r
363 result.append("狂人を");
\r
364 result.append(this.ctScryMadman);
\r
365 result.append("回");
\r
368 if(this.ctScryWolf > 0){
\r
369 if(result.length() > 0) result.append('、');
\r
370 result.append("人狼を");
\r
371 result.append(this.ctScryWolf);
\r
372 result.append("回");
\r
375 if(result.length() <= 0) result.append("誰も占わなかった。");
\r
376 else result.append("占った。");
\r
378 CharSequence seq = WolfBBS.escapeWikiSyntax(result);
\r
386 private void summarizeGuard(){
\r
387 List<SysEvent> eventList;
\r
389 eventList = this.eventMap.get(SysEventType.GUARD);
\r
390 for(SysEvent event : eventList){
\r
391 List<Avatar> avatarList = event.getAvatarList();
\r
392 Avatar avatar = avatarList.get(1);
\r
393 Player guarded = getPlayer(avatar);
\r
394 GameRole guardedRole = guarded.getRole();
\r
395 switch(guardedRole){
\r
396 case WOLF: this.ctGuardWolf++; break;
\r
397 case MADMAN: this.ctGuardMadman++; break;
\r
398 case HAMSTER: this.ctGuardHamster++; break;
\r
399 default: this.ctGuardVillage++; break;
\r
403 for(Period period : this.village.getPeriodList()){
\r
404 summarizeGjPeriod(period);
\r
414 private void summarizeGjPeriod(Period period){
\r
415 if(period.getDay() <= 2) return;
\r
417 boolean hasAssaultTried = period.hasAssaultTried();
\r
418 boolean hunterAlive = false;
\r
421 Set<Avatar> voters = period.getVoterSet();
\r
422 for(Avatar avatar : voters){
\r
423 Player player = getPlayer(avatar);
\r
424 switch(player.getRole()){
\r
425 case HUNTER: hunterAlive = true; break;
\r
426 case WOLF: wolfNum++; break;
\r
431 Avatar executed = period.getExecutedAvatar();
\r
432 if(executed != null){
\r
433 Player player = getPlayer(executed);
\r
434 switch(player.getRole()){
\r
435 case HUNTER: hunterAlive = false; break;
\r
436 case WOLF: wolfNum--; break;
\r
441 if( ! hunterAlive || wolfNum <= 0) return;
\r
445 sysEvent = period.getTypedSysEvent(SysEventType.NOMURDER);
\r
446 if(sysEvent == null) return;
\r
448 sysEvent = period.getTypedSysEvent(SysEventType.GUARD);
\r
449 if(sysEvent == null) return;
\r
451 if(hasAssaultTried){
\r
452 Avatar guarded = sysEvent.getAvatarList().get(1);
\r
453 Player guardedPlayer = getPlayer(guarded);
\r
454 GameRole guardedRole = guardedPlayer.getRole();
\r
455 switch(guardedRole){
\r
456 case MADMAN: this.ctGuardMadmanGJ++; break;
\r
457 case HAMSTER: this.ctGuardHamsterGJ++; break;
\r
458 default: this.ctGuardVillageGJ++; break;
\r
461 this.ctGuardFakeGJ++; // 偽装GJ
\r
471 public CharSequence dumpHunterActivity(){
\r
472 StringBuilder result = new StringBuilder();
\r
475 if(this.ctGuardFakeGJ > 0) atLeast = "少なくとも";
\r
478 if(this.ctGuardVillage > 0){
\r
479 result.append(atLeast);
\r
480 result.append("村陣営を");
\r
481 result.append(this.ctGuardVillage);
\r
482 result.append("回護衛し");
\r
483 if(this.ctGuardVillageGJ > 0){
\r
484 result.append("GJを");
\r
485 result.append(this.ctGuardVillageGJ);
\r
486 result.append("回出した。");
\r
488 result.append("た。");
\r
492 if(this.ctGuardHamster > 0){
\r
493 result.append(atLeast);
\r
494 result.append("ハムスターを");
\r
495 result.append(this.ctGuardHamster);
\r
496 result.append("回護衛し");
\r
497 if(this.ctGuardHamsterGJ > 0){
\r
498 result.append("GJを");
\r
499 result.append(this.ctGuardHamsterGJ);
\r
500 result.append("回出した。");
\r
502 result.append("た。");
\r
506 if(this.ctGuardMadman > 0){
\r
507 result.append(atLeast);
\r
508 result.append("狂人を");
\r
509 result.append(this.ctGuardMadman);
\r
510 result.append("回護衛し");
\r
511 if(this.ctGuardMadmanGJ > 0){
\r
512 result.append("GJを");
\r
513 result.append(this.ctGuardMadmanGJ);
\r
514 result.append("回出した。");
\r
516 result.append("た。");
\r
520 if(this.ctGuardWolf > 0){
\r
521 result.append(atLeast);
\r
522 result.append("人狼を");
\r
523 result.append(this.ctGuardWolf);
\r
524 result.append("回護衛した。");
\r
527 if(this.ctGuardFakeGJ > 0){
\r
528 result.append("護衛先は不明ながら偽装GJが");
\r
529 result.append(this.ctGuardFakeGJ);
\r
530 result.append("回あった。");
\r
533 if(result.length() <= 0) result.append("誰も護衛できなかった");
\r
535 CharSequence seq = WolfBBS.escapeWikiSyntax(result);
\r
542 * @return 文字列化した処刑概観
\r
544 public CharSequence dumpExecutionInfo(){
\r
545 StringBuilder result = new StringBuilder();
\r
549 int exeVillage = 0;
\r
550 for(Player player : this.playerList){
\r
551 Destiny destiny = player.getDestiny();
\r
552 if(destiny != Destiny.EXECUTED) continue;
\r
553 GameRole role = player.getRole();
\r
555 case WOLF: exeWolf++; break;
\r
556 case MADMAN: exeMad++; break;
\r
557 default: exeVillage++; break;
\r
561 if(exeVillage > 0){
\r
562 result.append("▼村陣営×").append(exeVillage).append("回");
\r
565 if(result.length() > 0) result.append("、");
\r
566 result.append("▼狂×").append(exeMad).append("回");
\r
569 if(result.length() > 0) result.append("、");
\r
570 result.append("▼狼×").append(exeWolf).append("回");
\r
572 if(result.length() <= 0) result.append("なし");
\r
574 CharSequence seq = WolfBBS.escapeWikiSyntax(result);
\r
581 * @return 文字列化した襲撃概観
\r
583 public CharSequence dumpAssaultInfo(){
\r
584 StringBuilder result = new StringBuilder();
\r
587 int eatVillage = 0;
\r
588 for(Player player : this.playerList){
\r
589 if(player.getAvatar() == Avatar.AVATAR_GERD){
\r
590 result.append("▲ゲルト");
\r
593 Destiny destiny = player.getDestiny();
\r
594 if(destiny != Destiny.EATEN) continue;
\r
595 GameRole role = player.getRole();
\r
597 case MADMAN: eatMad++; break;
\r
598 default: eatVillage++; break;
\r
602 if(eatVillage > 0){
\r
603 if(result.length() > 0) result.append("、");
\r
604 result.append("▲村陣営×").append(eatVillage).append("回");
\r
607 if(result.length() > 0) result.append("、");
\r
608 result.append("▲狂×").append(eatMad).append("回");
\r
611 if(result.length() <= 0) result.append("襲撃なし");
\r
613 CharSequence seq = WolfBBS.escapeWikiSyntax(result);
\r
619 * まとめサイト用投票Boxを生成する。
\r
620 * @return 投票BoxのWikiテキスト
\r
622 public CharSequence dumpVoteBox(){
\r
623 StringBuilder wikiText = new StringBuilder();
\r
625 for(Player player : getCastingPlayerList()){
\r
626 Avatar avatar = player.getAvatar();
\r
627 if(avatar == Avatar.AVATAR_GERD) continue;
\r
628 GameRole role = player.getRole();
\r
629 CharSequence fullName = avatar.getFullName();
\r
630 CharSequence roleName = role.getRoleName();
\r
631 StringBuilder line = new StringBuilder();
\r
632 line.append("[").append(roleName).append("] ").append(fullName);
\r
633 if(wikiText.length() > 0) wikiText.append(',');
\r
634 wikiText.append(WolfBBS.escapeWikiSyntax(line));
\r
635 wikiText.append("[0]");
\r
638 wikiText.insert(0, "#vote(").append(")\n");
\r
644 * まとめサイト用キャスト表を生成する。
\r
645 * @param iconSet 顔アイコンセット
\r
646 * @return キャスト表のWikiテキスト
\r
648 public CharSequence dumpCastingBoard(FaceIconSet iconSet){
\r
649 StringBuilder wikiText = new StringBuilder();
\r
651 String vName = this.village.getVillageFullName();
\r
652 String generator = Jindolf.TITLE + " Ver." + Jindolf.VERSION;
\r
653 String author = iconSet.getAuthor() + "氏"
\r
654 +" [ "+iconSet.getUrlText()+" ]";
\r
656 wikiText.append(WolfBBS.COMMENTLINE);
\r
657 wikiText.append("// ↓キャスト表開始\n");
\r
658 wikiText.append("// Village : " + vName + "\n");
\r
659 wikiText.append("// Generator : " + generator + "\n");
\r
660 wikiText.append("// アイコン作者 : " + author + '\n');
\r
661 wikiText.append("// ※アイコン画像の著作財産権保持者"
\r
662 +"および画像サーバ運営者から\n");
\r
663 wikiText.append("// 新しい意向が示された場合、"
\r
664 +"そちらを最優先で尊重してください。\n");
\r
665 wikiText.append(WolfBBS.COMMENTLINE);
\r
667 wikiText.append("|配役")
\r
674 wikiText.append(WolfBBS.COMMENTLINE);
\r
676 for(Player player : getCastingPlayerList()){
\r
677 Avatar avatar = player.getAvatar();
\r
678 GameRole role = player.getRole();
\r
679 Destiny destiny = player.getDestiny();
\r
680 int obitDay = player.getObitDay();
\r
681 String name = player.getIdName();
\r
682 String urlText = player.getUrlText();
\r
683 if(urlText == null) urlText = "";
\r
684 urlText = urlText.replace("~", "%7e");
\r
685 urlText = urlText.replace(" ", "%20");
\r
687 URL url = new URL(urlText);
\r
688 URI uri = url.toURI();
\r
689 urlText = uri.toASCIIString();
\r
690 }catch(MalformedURLException e){
\r
692 }catch(URISyntaxException e){
\r
695 // PukiWikiではURL内の&のエスケープは不要?
\r
697 wikiText.append("// ========== ");
\r
698 wikiText.append(name + " acts as [" + avatar.getName() + "]");
\r
699 wikiText.append(" ==========\n");
\r
701 String teamColor = "BGCOLOR("
\r
702 + WolfBBS.getTeamWikiColor(role)
\r
705 String avatarIcon = iconSet.getAvatarIconWiki(avatar);
\r
707 wikiText.append('|').append(teamColor);
\r
708 wikiText.append(avatarIcon).append("&br;");
\r
710 wikiText.append("[[").append(avatar.getName()).append("]]");
\r
712 wikiText.append('|').append(teamColor);
\r
713 wikiText.append("[[").append(WolfBBS.escapeWikiBracket(name));
\r
714 if(urlText != null && urlText.length() > 0){
\r
715 wikiText.append('>').append(urlText);
\r
717 wikiText.append("]]");
\r
719 wikiText.append('|').append(teamColor);
\r
720 wikiText.append(WolfBBS.getRoleIconWiki(role));
\r
721 wikiText.append("&br;");
\r
722 wikiText.append("[[");
\r
723 wikiText.append(role.getRoleName());
\r
724 wikiText.append("]]");
\r
726 String destinyColor = WolfBBS.getDestinyColorWiki(destiny);
\r
727 wikiText.append('|');
\r
728 wikiText.append("BGCOLOR(").append(destinyColor).append("):");
\r
729 if(destiny == Destiny.ALIVE){
\r
730 wikiText.append("最後まで&br;生存");
\r
732 wikiText.append(obitDay).append("日目").append("&br;");
\r
733 wikiText.append(destiny.getMessage());
\r
736 wikiText.append('|');
\r
737 wikiText.append(avatar.getJobTitle()).append('。');
\r
739 if(avatar == Avatar.AVATAR_GERD){
\r
740 wikiText.append("寝てばかりいた。");
\r
741 }else if(role == GameRole.HUNTER){
\r
742 CharSequence report = dumpHunterActivity();
\r
743 wikiText.append(report);
\r
744 }else if(role == GameRole.SEER){
\r
745 CharSequence report = dumpSeerActivity();
\r
746 wikiText.append(report);
\r
749 wikiText.append("|\n");
\r
753 wikiText.append("|>|>|>|>|");
\r
754 wikiText.append("RIGHT:");
\r
755 wikiText.append("顔アイコン提供 : [[");
\r
756 wikiText.append(WolfBBS.escapeWikiBracket(iconSet.getAuthor()));
\r
757 wikiText.append(">" + iconSet.getUrlText());
\r
758 wikiText.append("]]氏");
\r
759 wikiText.append("|\n");
\r
761 wikiText.append(WolfBBS.COMMENTLINE);
\r
762 wikiText.append("// ↑キャスト表ここまで\n");
\r
763 wikiText.append(WolfBBS.COMMENTLINE);
\r
772 public CharSequence dumpVillageWiki(){
\r
773 StringBuilder wikiText = new StringBuilder();
\r
776 DateFormat.getDateTimeInstance(DateFormat.FULL,
\r
779 String vName = this.village.getVillageFullName();
\r
780 String generator = Jindolf.TITLE + " Ver." + Jindolf.VERSION;
\r
782 wikiText.append(WolfBBS.COMMENTLINE);
\r
783 wikiText.append("// ↓村詳細開始\n");
\r
784 wikiText.append("// Village : " + vName + "\n");
\r
785 wikiText.append("// Generator : " + generator + "\n");
\r
787 wikiText.append("* 村の詳細\n");
\r
789 wikiText.append(WolfBBS.COMMENTLINE);
\r
790 wikiText.append("- 勝者\n");
\r
791 Team winnerTeam = getWinnerTeam();
\r
792 String wonTeam = winnerTeam.getTeamName();
\r
793 wikiText.append(wonTeam).append('\n');
\r
795 wikiText.append(WolfBBS.COMMENTLINE);
\r
796 wikiText.append("- エントリー開始時刻\n");
\r
797 Date date = get1stTalkDate();
\r
798 String talk1st = dform.format(date);
\r
799 wikiText.append(talk1st).append('\n');
\r
801 wikiText.append(WolfBBS.COMMENTLINE);
\r
802 wikiText.append("- 参加人数\n");
\r
803 int avatarNum = countAvatarNum();
\r
804 String totalMember = "ゲルト + " + (avatarNum - 1) + "名 = "
\r
806 wikiText.append(WolfBBS.escapeWikiSyntax(totalMember))
\r
809 wikiText.append(WolfBBS.COMMENTLINE);
\r
810 wikiText.append("- 役職内訳\n");
\r
811 StringBuilder roleMsg = new StringBuilder();
\r
812 for(GameRole role : GameRole.values()){
\r
813 List<Player> players = getRoledPlayerList(role);
\r
814 String roleName = role.getRoleName();
\r
815 if(players.size() <= 0) continue;
\r
816 if(roleMsg.length() > 0) roleMsg.append('、');
\r
817 roleMsg.append(roleName)
\r
819 .append(players.size())
\r
822 wikiText.append(WolfBBS.escapeWikiSyntax(roleMsg)).append('\n');
\r
824 wikiText.append(WolfBBS.COMMENTLINE);
\r
825 wikiText.append("- 処刑内訳\n");
\r
826 wikiText.append(dumpExecutionInfo()).append('\n');
\r
828 wikiText.append(WolfBBS.COMMENTLINE);
\r
829 wikiText.append("- 襲撃内訳\n");
\r
830 wikiText.append(dumpAssaultInfo()).append('\n');
\r
832 wikiText.append(WolfBBS.COMMENTLINE);
\r
833 wikiText.append("- 突然死\n");
\r
834 wikiText.append(countSuddenDeath()).append("名").append('\n');
\r
836 wikiText.append(WolfBBS.COMMENTLINE);
\r
837 wikiText.append("- 人口推移\n");
\r
838 for(int day = 1; day < this.village.getPeriodSize(); day++){
\r
839 List<Player> players = getSurvivorList(day);
\r
840 CharSequence roleSeq =
\r
841 GameSummary.getRoleBalanceSequence(players);
\r
843 Period period = this.village.getPeriod(day);
\r
844 daySeq = period.getCaption();
\r
845 wikiText.append('|')
\r
852 wikiText.append(WolfBBS.COMMENTLINE);
\r
853 wikiText.append("- 占い師の成績\n");
\r
854 wikiText.append(dumpSeerActivity()).append('\n');
\r
856 wikiText.append(WolfBBS.COMMENTLINE);
\r
857 wikiText.append("- 狩人の成績\n");
\r
858 wikiText.append(dumpHunterActivity()).append('\n');
\r
860 wikiText.append(WolfBBS.COMMENTLINE);
\r
861 wikiText.append("// ↑村詳細ここまで\n");
\r
862 wikiText.append(WolfBBS.COMMENTLINE);
\r
871 public Date get1stTalkDate(){
\r
872 return new Date(this.talk1stTimeMs);
\r
879 public Date getLastTalkDate(){
\r
880 return new Date(this.talkLastTimeMs);
\r
888 public List<Player> getSurvivorList(int day){
\r
889 if(day < 0 || this.village.getPeriodSize() <= day){
\r
890 throw new IndexOutOfBoundsException();
\r
893 List<Player> result = new LinkedList<Player>();
\r
895 Period period = this.village.getPeriod(day);
\r
897 if( period.isPrologue()
\r
898 || (period.isProgress() && day == 1) ){
\r
899 result.addAll(this.playerList);
\r
903 if(period.isEpilogue()){
\r
904 for(Player player : this.playerList){
\r
905 if(player.getDestiny() == Destiny.ALIVE){
\r
906 result.add(player);
\r
912 for(Topic topic : period.getTopicList()){
\r
913 if( ! (topic instanceof SysEvent) ) continue;
\r
914 SysEvent sysEvent = (SysEvent) topic;
\r
915 if(sysEvent.getSysEventType() == SysEventType.SURVIVOR){
\r
916 List<Avatar> avatarList = sysEvent.getAvatarList();
\r
917 for(Avatar avatar : avatarList){
\r
918 Player player = getPlayer(avatar);
\r
919 result.add(player);
\r
930 * @return プレイヤーのリスト
\r
932 public List<Player> getPlayerList(){
\r
933 List<Player> result = Collections.unmodifiableList(this.playerList);
\r
938 * キャスティング表用にソートされたプレイヤー一覧を得る。
\r
939 * @return プレイヤーのリスト
\r
941 public List<Player> getCastingPlayerList(){
\r
942 List<Player> sortedPlayers =
\r
943 new LinkedList<Player>();
\r
944 sortedPlayers.addAll(this.playerList);
\r
945 Collections.sort(sortedPlayers, COMPARATOR_CASTING);
\r
946 return sortedPlayers;
\r
950 * 指定された役職のプレイヤー一覧を得る。
\r
952 * @return 役職に合致するプレイヤーのリスト
\r
954 public List<Player> getRoledPlayerList(GameRole role){
\r
955 List<Player> result = new LinkedList<Player>();
\r
957 for(Player player : this.playerList){
\r
958 if(player.getRole() == role){
\r
959 result.add(player);
\r
970 public Team getWinnerTeam(){
\r
971 return this.winner;
\r
978 public int countSuddenDeath(){
\r
979 int suddenDeath = 0;
\r
980 for(Player player : this.playerList){
\r
981 if(player.getDestiny() == Destiny.SUDDENDEATH) suddenDeath++;
\r
983 return suddenDeath;
\r
990 public int countAvatarNum(){
\r
991 int playerNum = this.playerList.size();
\r
996 * AvatarからPlayerを得る。
\r
997 * 参加していないAvatarならnullを返す。
\r
998 * @param avatar Avatar
\r
1001 public final Player getPlayer(Avatar avatar){
\r
1002 Player player = this.playerMap.get(avatar);
\r
1007 * AvatarからPlayerを得る。
\r
1009 * @param avatar Avatar
\r
1012 private Player registPlayer(Avatar avatar){
\r
1013 Player player = getPlayer(avatar);
\r
1014 if(player == null){
\r
1015 player = new Player();
\r
1016 player.setAvatar(avatar);
\r
1017 this.playerMap.put(avatar, player);
\r
1026 private static final class CastingComparator
\r
1027 implements Comparator<Player> {
\r
1032 private CastingComparator(){
\r
1039 * @param p1 {@inheritDoc}
\r
1040 * @param p2 {@inheritDoc}
\r
1041 * @return {@inheritDoc}
\r
1043 public int compare(Player p1, Player p2){
\r
1044 if(p1 == p2) return 0;
\r
1045 if(p1 == null) return -1;
\r
1046 if(p2 == null) return +1;
\r
1049 Avatar avatar1 = p1.getAvatar();
\r
1050 Avatar avatar2 = p2.getAvatar();
\r
1051 if(avatar1.equals(avatar2)) return 0;
\r
1052 if(avatar1 == Avatar.AVATAR_GERD) return -1;
\r
1053 if(avatar2 == Avatar.AVATAR_GERD) return +1;
\r
1056 Destiny dest1 = p1.getDestiny();
\r
1057 Destiny dest2 = p2.getDestiny();
\r
1058 if(dest1 != dest2){
\r
1059 if (dest1 == Destiny.ALIVE) return +1;
\r
1060 else if(dest2 == Destiny.ALIVE) return -1;
\r
1064 int obitDay1 = p1.getObitDay();
\r
1065 int obitDay2 = p2.getObitDay();
\r
1066 if(obitDay1 > obitDay2) return +1;
\r
1067 if(obitDay1 < obitDay2) return -1;
\r
1070 int destinyOrder = dest1.compareTo(dest2);
\r
1071 if(destinyOrder != 0) return destinyOrder;
\r
1074 int entryOrder = p1.getEntryNo() - p2.getEntryNo();
\r
1076 return entryOrder;
\r