OSDN Git Service

7人及び9人の編成の初日部屋割りを3人部屋ベースに変更
[wolvez/wolvez.git] / trunk / lib / ms / village.rb
1 class Vil
2         MesRegex = /^<div class="message"><div class="([a-z]+)(\d+)">/
3         ExtRegex = /^<div class="message"><!--([a-z]+)(\d+)-->/
4
5         attr_reader :name, :date, :vid, :state, :players, :rule
6         attr_reader :userid, :winner, :phase, :rooms, :voting
7         attr_reader :first_restart, :guard
8
9         attr_accessor :update_time
10
11         def initialize(name, vid, userid, update_time)
12                 @name, @vid, @date, @state = name, vid, 0, 0
13                 @userid, @update_time = userid, update_time
14                 @first_restart = false
15
16                 @players = Players.new
17                 @rooms = Rooms.new
18                 @period = S[:std_period] # minute
19                 # night_period = @period / 2
20                 @phase = Phase::Sun
21                 @prevote = true
22                 @voting = []
23         end
24
25         def start
26                 addlog(announce(OPENING))
27
28                 player = Player.new(1, MASTER, 0)
29                 add_player(player, GERT_ENTRY)
30         end
31
32         def restart
33                 return if @state >= State::Progress
34
35                 if discussions(@date, @players.player(1)).size > S[:log_max]
36                         do_quit()
37                         change_state_sync(State::End)
38                 else
39                         pl_dels = []
40                         @players.all.each {|p|
41                                 next if p.pid == 1
42
43                                 if (p.lastmsg_time && \
44                                          p.lastmsg_time < Time.now() - (S[:restart_minute] * 60))
45
46                                         pl_dels << p.name
47                                         @players.exit(p)
48                                 end
49                                 change_state_sync(State::Welcome)
50                         }
51                         addlog(wsystem(c(AUTO_PARTING, pl_dels.join('、')))) if pl_dels != []
52
53                         while @update_time < Time.now do
54                                 @update_time += S[:restart_minute] * 60
55                         end
56
57                         DB::Villages.restart(@vid, @update_time)
58                 end
59         end
60
61         def add_player(player, message)
62                 @players.add(player)
63                 change_state_sync(State::Ready) if @players.size == @players.max
64
65                 addlog(wsystem(c(JOINING, player.name)))
66                 record("say", player, message)
67         end
68
69         def delete_player(player)
70                 addlog(wsystem(c(PARTING, player.name)))
71
72                 @players.exit(player)
73                 change_state_sync(State::Welcome)
74         end
75
76         def change_state_sync(st)
77                 @state = st
78                 DB::Villages.change_state(@vid, st)
79         end
80
81         def need_sync?
82                 (@state < State::End && @update_time.to_i < Time.now.to_i)
83         end
84
85         def sync
86                 if (@state == State::Welcome && !@players.adv_ready?)
87                         if (@first_restart && @players.ready?)
88                                 update()
89                         else
90                                 @first_restart = true
91                                 restart()
92                         end
93                 else
94                         update()
95                 end
96         end
97
98         def timeline
99                 (0..@date).collect {|i| datestr(i) }
100         end
101
102         def discussion_size(date)  # use this, prologue only.
103                 IO.readlines("db/vil/#{@vid}_#{date}.html").size
104         end
105
106         def discussions(date, player, reverse = false)
107                 ary = IO.readlines("db/vil/#{@vid}_#{date}.html")
108                 ary.reverse! if reverse
109
110                 ary.collect {|line|
111                         if (@state < State::Party && (line =~ MesRegex || line =~ ExtRegex))
112                                 next if (!player && $1 != 'say')
113
114                                 if (player && !player.admin?)
115                                         pid = $2.to_i
116                                         # night pid is room-number
117                                         case $1
118                                         when 'whisper' then next unless player.can_whisper
119                                         when 'god'     then next unless (player.pid == pid || player.can_whisper)
120                                         when 'groan'   then next unless player.dead?
121                                         when 'night'   then next unless @rooms.in?(date, pid, player.pid)
122                                         when 'nextra'  then next unless @rooms.in?(date, pid, player.pid)
123                                         end
124                                 end
125                         end
126                         line.chomp + "\n"
127                 }.compact
128         end
129
130         def now_voting?
131                 !@voting.empty? && @phase != Phase::Apology
132         end
133         private :now_voting?
134
135         def record(type, player, msg, face = nil)
136                 face = "face#{player.color}" if !face
137                 player.cnt[@date] += 1 if (@phase == Phase::Sun && type == 'say')
138                 player.lastmsg_time = Time.now() if @state < State::Progress
139
140                 # PreVote || Vote || FinalVote || Room || AfterRoom || Morning
141                 if (player.live? && (type == 'say' || type == 'night') && [1, 2, 4, 5, 6, 8].index(@phase))
142                         raise ErrorMsg.new('フェイズと発言種類が合致しません')
143                 end
144
145                 type = 'night' if (@phase == Phase::Night && type == 'say')
146                 type = 'groan' if (player.dead? && (type == 'say' || type =='night'))
147                 type = 'say' if (player.live? && type == 'groan')
148                 type = 'say' if (@state >= State::Party && type != 'say')
149
150                 c = player.pid
151                 if type == 'night'
152                         @rooms.last.each_with_index {|ary, i|
153                                 c  = i if ary.index(player.pid)
154                         }
155                 end
156                 typec = type + c.to_s
157
158                 # don't insert \n in message, cause this line in convert to JSON.
159                 s = \
160                 %Q(<div class="#{typec}">) + \
161                 %Q(<div class="#{type}">#{player.name}) + \
162                 %Q(<table class="message_box"><tr>) + \
163                 %Q(<td width="40"><img src="#{S[:image_dir]}#{face}.jpg"></td>) + \
164                 %Q(<td width="16"><img src="#{S[:image_dir]}#{type}00.jpg"></td>) + \
165                 %Q(<td><div class="mes_#{type}_body0">) + \
166                 %Q(<div class="mes_#{type}_body1">#{msg}</div>) + \
167                 %Q(</div></td>) + \
168                 %Q(</tr></table></div></div>\n)
169
170                 raise ErrorMsg.new('夜ではありません') if (@phase != Phase::Night && type == 'night')
171
172                 addlog(message(s))
173         end
174
175         def vtargets
176                 @_targets || @players.lives()
177         end
178
179
180         private
181
182         # End or Quit
183         #######################################################
184
185         def do_quit
186                 addlog(announce(SYSTEM_EXIT))
187                 a = Array.new
188
189                 @players.all().each {|p|
190                         alive = (p.dead == 0) ? '生存' : '死亡'
191                         sk = (@date == 0) ? '役職決定前' : p.skill.name
192
193                         a.push(c(TRUTH, p.name, p.userid, alive, sk))
194                 }
195
196                 addlog(announce(a.join('<br>')))
197                 do_end()
198         end
199
200         def do_end
201                 @date += 1
202                 addlog(announce('終了しました'))
203                 DB::Villages.finish(@vid, @players.keys.size)
204                 DB::Users.registlast(@vid, @players.keys)
205                 Sweeper.sweep(self)
206         end
207
208
209         # Internal periodic work methods
210         #######################################################
211
212         def up_sudden_death
213                 @players.silents().each {|p|
214                         p.sudden_death(@date)
215                         addlog(announce(c(SUDDEN_DEATH, p.name)))
216                 }
217         end
218
219         def up_vote()
220                 s, votes = [], {}
221                 max = 0
222                 @players.votes.each {|ary|
223                         p, t = ary
224                         s << [p.name, t.name].join(' &raquo; ')
225                         votes[t] = votes[t].to_i + 1
226                         max = votes[t] if (votes[t] > max)
227                 }
228
229                 s << ''
230                 result = votes.select {|k, v| v == max }
231                 result.collect! {|ary| ary[0] }
232                 votes.each_pair {|p, n| s << c(ANON_VOTING, n, p.name) }
233                 addlog(announce(s.join('<br>')))
234
235                 result
236         end
237
238         def up_gameover
239                 do_end = false
240
241                 wcnt = @players.wolves().size
242                 vcnt = @players.villagers().size
243
244                 if (wcnt == 0 || vcnt == 0)
245                         if (@players.select_skill(7).size > 0)
246                                 w = (wcnt == 0) ? YOKO_WIN_F : YOKO_WIN_W
247                                 @winner = '妖魔'
248                                 addlog(announce(w))
249                         elsif (wcnt == 0)
250                                 @winner = '村人'
251                                 addlog(announce(FOLK_WIN))
252                         else
253                                 @winner = '人狼'
254                                 addlog(announce(WOLF_WIN))
255                         end
256                         do_end = true
257                 elsif @date == @guard
258                         @winner = '村人'
259                         addlog(announce(GUARD_WIN))
260                         do_end = true
261                 elsif @players.lives().size < 3
262                         exe = @players.select_skill(Skill::Exorcist)
263                         pas = @players.select_skill(Skill::Pastor)
264
265                         if ((exe.empty? || exe.first.skill.pass == false) && \
266                                 ((pas.empty? || pas.first.skill.pass == false) || \
267                                  (pas.first.skill.pass && @date + 1 != @guard)))
268
269                                 @winner = '人狼'
270                                 addlog(announce(WOLF_WIN))
271                         else
272                                 @winner = '村人'
273                                 addlog(announce(FOLK_WIN))
274                         end
275                         do_end = true
276                 end
277
278                 if do_end
279                         change_state_sync(State::Party)
280
281                         a = Array.new
282                         @players.all().each {|p|
283                                 alive = (p.dead == 0) ? '生存' : '死亡'
284                                 a.push(c(TRUTH, p.name, p.userid, alive, p.skill.name))
285                         }
286                         addlog(announce(a.join('<br>')))
287
288                         @players.open_party()
289                 end
290         end
291
292         def up_showlives(include_gert = true)
293                 # Players@show_lives(), wtls
294                 pls = @players.lives()
295                 pls.delete(@players.player(1)) unless include_gert
296                 s = c(LIVES, pls.collect {|p| p.name }.join('、'), pls.size)
297                 addlog(announce(s))
298         end
299
300         def up_uptime(period = @period)
301                 $logger.debug('from: ' + @update_time.inspect)
302                 if @update_time > Time.now
303                         @update_time = Time.now + (period * 60)
304                 else
305                         @update_time += period * 60
306                 end
307                 while (@update_time && @update_time.to_i < Time.now.to_i) do
308                         @update_time += period * 60
309                 end
310                 $logger.debug('to: ' + @update_time.inspect)
311         end
312
313
314         # Update methods
315         #######################################################
316
317         def update(nocnt = nil)
318                 return if (@state == State::End)
319
320                 if (@state == State::Party || nocnt)
321                         change_state_sync(State::End)
322                         (nocnt) ? do_quit() : do_end()
323                         up_uptime()
324                         return
325                 end
326
327                 (@date == 0) ? first_update() : _update()
328         end
329         public :update
330
331         def _update
332                 case @phase
333                 when Phase::Sun
334                         end_sun()
335                         if @date == 1
336                                 start_room()
337                         elsif @prevote
338                                 start_prevote()
339                         else
340                                 start_vote()
341                         end
342                 when Phase::PreVote
343                         do_prevote()
344                         if @voting.empty?
345                                 end_prevote()
346                                 (@prevote) ? start_room() : start_vote()
347                         end
348                 when Phase::Vote
349                         do_vote()
350                         if @voting.empty?
351                                 picked = end_vote()
352
353                                 if picked.size > 1
354                                         if picked.size == @players.lives().size
355                                                 addlog(announce(STOP_EXECUTION))
356                                                 start_room()
357                                         else
358                                                 @_targets = picked.dup
359                                                 if ((@players.lives().size - picked.size) % 2) == 0
360                                                         start_apology(picked)
361                                                 else
362                                                         start_finalvote()
363                                                 end
364                                         end
365                                 else
366                                         start_room()
367                                 end
368                         end
369                 when Phase::Apology
370                         do_apology()
371                         if @voting.empty?
372                                 end_apology()
373                                 start_finalvote()
374                         end
375                 when Phase::FinalVote
376                         do_finalvote()
377                         if @voting.empty?
378                                 end_finalvote()
379                                 start_room()
380                         end
381                 when Phase::Room
382                         if @voting.empty?
383                                 end_room()
384                                 start_afterroom()
385                         else
386                                 do_room()
387
388                                 if @voting.size <= 3
389                                         finalize_room()
390                                         end_room()
391                                         start_afterroom()
392                                 end
393                         end
394                 when Phase::AfterRoom
395                         end_afterroom()
396                         start_night()
397                 when Phase::Night
398                         end_night()
399                         if @state == State::Party
400                                 start_sun()
401                         elsif @players.lives().size > 2
402                                 start_morning()
403                         else
404                                 start_sun()
405                         end
406                 when Phase::Morning
407                         end_morning()
408                         start_sun()
409                 end
410         end
411
412         def first_update
413                 up_uptime()
414                 @date += 1
415                 @players.reset_count(@date)
416
417                 @players.skill_mapping()
418                 @guard = Regulation.guard(@players.size, @players.wolves().size)
419                 @rule = Regulation.rule(@players.size)
420                 change_state_sync(State::Progress)
421
422                 addlog(announce(START))
423                 up_showlives(false)
424                 record('say', @players.player(1), c(GERT_FIRST, @guard - @date, @guard - @date + 1))
425                 @players.player(1).dead = 1  # die, not use Player#kill()
426
427                 wolves = @players.wolves()
428                 whisper = (wolves.size == 1) ? FIRST_WHISPER_LW : FIRST_WHISPER
429                 wolves.each {|w| record('whisper', w, whisper) }
430         end
431
432
433         # Phase methods
434         #######################################################
435
436         def start_sun
437                 #mod 2008/11/08 tkt : for epilogue time change, yes, i know this code is not good...
438                 #to-do attached plyaers.size and period time
439                 #period = (@state == State::Party) ? S[:epilogue_period] : @period
440                 period = @period
441                 if @state == State::Party
442                         period = 1 * @players.size * (@date - 1)
443                         $logger.debug('epilogue_period#{period}  plaersize#{@players.size} date#{(@date - 1)}')
444                         if period < S[:epilogue_period_min]
445                                 period = S[:epilogue_period_min]
446                         elsif (@players.size >= 8 || (period > S[:epilogue_period]))
447                                 period = S[:epilogue_period]
448                         end
449                         #period = (@state == State::Party) ? period_epi : @period               
450                 end
451                 
452                 #mod 2008/11/08 end
453                 
454                 up_uptime(period)
455                 # up_uptime(@period)
456                 @phase = Phase::Sun
457                 unless @state == State::Party
458                         limit = @guard - @date
459                         addlog(wsystem(c(SUN_PHASE, limit)))
460                 end
461         end
462
463         def end_sun
464                 up_sudden_death() unless $DEBUG
465                 addlog(wsystem(SUNSET_PHASE))
466         end
467
468         def start_prevote
469                 up_uptime(S[:vote_period])
470                 @phase = Phase::PreVote
471                 @voting = @players.lives().shuffle
472                 addlog(wsystem(c(PREVOTE_PHASE, @voting.collect {|p| p.sname }.join(' &raquo; '))))
473                 addlog(vote(c(ANN_VOTING, @voting.first.name)))
474         end
475
476         def do_prevote
477                 up_uptime(S[:vote_period])
478                 pl = @voting.shift
479                 res = (pl.prevote) ? '賛成' : '反対'
480                 @players.vote_map([pl, res])
481                 addlog(vote_res(c(VOTING, pl.name, res)))
482                 addlog(vote(c(ANN_VOTING, @voting.first.name))) unless @voting.empty?
483         end
484
485         def end_prevote
486                 result = @players.collect_prevote()
487                 vote = result.pop
488                 @prevote = (vote['result']) ? false : true
489
490                 s = ''
491                 @players.votes.each {|pl, |
492                         t = (pl.prevote) ? '賛成した' : '反対した'
493                         s << %Q(#{pl.name}は、投票に#{t}<br />)
494                 }
495                 s << "<br />賛成に#{vote['approve']}票、反対に#{vote['object']}票、"
496                 t = (vote['result']) ? '処刑を実施することになった。' : '処刑は保留された。'
497                 s << t + '<br />'
498
499                 addlog(announce(s))
500         end
501
502         def start_vote
503                 up_uptime(S[:vote_period])
504                 @players.reset_cmd()
505                 @phase = Phase::Vote
506                 @voting = @players.lives().shuffle
507                 addlog(wsystem(c(VOTE_PHASE, @voting.collect {|p| p.sname }.join(' &raquo; '))))
508                 addlog(vote(c(ANN_VOTING, @voting.first.name)))
509         end
510
511         def do_vote
512                 up_uptime(S[:vote_period])
513                 pl = @voting.shift
514                 pl.vote_suffle(@players.lives()) unless pl.vote
515                 target = @players.player(pl.vote)
516                 @players.vote_map([pl, target])
517                 addlog(vote_res(c(VOTING, pl.name, target.name)))
518                 addlog(vote(c(ANN_VOTING, @voting.first.name))) unless @voting.empty?
519         end
520
521         def end_vote
522                 picked = up_vote()
523
524                 if picked.size == 1
525                         addlog(announce(c(EXECUTION, picked.first.name)))
526                         picked.first.execute(@date)
527                 end
528
529                 picked
530         end
531
532         def start_apology(targets)
533                 up_uptime(@period / 3.0)
534                 @phase = Phase::Apology
535                 @voting = @_targets.shuffle
536                 x = targets.collect {|t| t.name}.join('と、')
537                 addlog(wsystem(c(APOLOGY_PHASE, x, @voting.collect {|p| p.sname }.join(' &raquo; '))))
538                 addlog(apology(c(APOLOGY_START, @voting.first.name)))
539         end
540
541         def do_apology
542                 up_uptime(@period / 3.0)
543                 @voting.shift
544                 addlog(apology(c(APOLOGY_START, @voting.first.name))) unless @voting.empty?
545         end
546
547         def end_apology
548         end
549
550         def start_finalvote
551                 up_uptime(S[:vote_period])
552                 @players.reset_cmd()
553                 @phase = Phase::FinalVote
554                 @voting = @players.lives().shuffle - @_targets
555                 x = @_targets.collect {|t| t.name}.join('と、')
556                 addlog(wsystem(c(FINALVOTE_PHASE, x, @voting.collect {|p| p.sname }.join(' &raquo; '))))
557                 addlog(vote(c(ANN_VOTING, @voting.first.name)))
558         end
559
560         def do_finalvote
561                 up_uptime(S[:vote_period])
562                 pl = @voting.shift
563                 pl.vote_suffle(vtargets()) unless pl.vote
564                 target = @players.player(pl.vote)
565                 @players.vote_map([pl, target])
566                 addlog(vote_res(c(VOTING, pl.name, target.name)))
567                 addlog(vote(c(ANN_VOTING, @voting.first.name))) unless @voting.empty?
568         end
569
570         def end_finalvote
571                 picked = up_vote()
572
573                 if picked.size == 1
574                         addlog(announce(c(EXECUTION, picked.first.name)))
575                         picked.first.execute(@date)
576                 else
577                         addlog(announce(STOP_EXECUTION))
578                 end
579
580                 @_targets = nil
581         end
582
583         def start_room
584                 @phase = Phase::Room
585                 @players.reset_cmd()
586                 @voting = @players.lives().shuffle
587                 addlog(wsystem(c(ROOM_PHASE, @voting.collect {|p| p.sname }.join(' &raquo; '))))
588                 if @voting.size <= 3
589                         finalize_room()
590                         end_room()
591                         start_afterroom()
592                 else
593                         up_uptime(S[:vote_period])
594                         addlog(room(c(ANN_ROOM_VOTING, @voting.first.name)))
595                 end
596         end
597
598         def do_room
599                 pl = @voting.shift
600                 if (!pl.vote || pl.vote == pl.pid)
601                         targets = @voting.select {|t| !pl.yesterday_mate.index(t.pid) }
602                         pl.vote_suffle(targets)
603                 end
604                 mate = @players.player(pl.vote)
605                 addlog(room_res(c(ROOM_VOTING, pl.name, mate.name)))
606
607                 @voting.delete(mate)
608                 #if (@rule == Rule::Standard || @date != 1) mod 2008/11/08 tkt:change rule for 3 members room
609                 if (@date != 1 || (@rule == Rule::Standard && (@players.size % 2 == 0)))
610                         @players.room_map([pl, mate])
611                 else # Advance
612                         last_room = @players.mates.last
613                         if (last_room && last_room.size == 2)
614                                 lr = last_room.dup
615                                 lr.push(mate)
616                                 names = lr.map {|pl| pl.name }
617                                 addlog(room_res2(c(ROOM_RESULT_ADV, names.join('と、'))))
618                                 @players.room_remap(lr)
619                                 # last room mate added, go next
620                         else
621                                 @players.room_map([pl, mate])
622                                 @voting.unshift(mate) unless @voting.size < 3
623                                 # not filled in the room
624                         end
625                 end
626
627                 unless (@voting.empty? || @voting.size <= 3)
628                         up_uptime(S[:vote_period])
629                         addlog(room(c(ANN_ROOM_VOTING, @voting.first.name)))
630                 end
631                 @players.vote_suffle(@voting) unless @voting.empty? 
632         end
633
634         def finalize_room
635                 @players.room_map(@voting.dup)
636                 x = @voting.collect {|p| p.name}.join('と、')
637                 addlog(room_res(c(BUSH_ROOM, x)))
638                 last = nil
639
640                 until @voting.empty?
641                         if @voting.size == 1
642                                 pl = @voting.shift
643                                 pl.vote = last.pid
644                         else
645                                 pl = @voting.shift
646                                 mate = @voting.shift
647                                 pl.vote = mate.pid
648                                 mate.vote = pl.pid
649
650                                 last = mate
651                         end
652                 end
653         end
654
655         def end_room
656                 @rooms.push([])
657                 result = @players.room_mapping()
658                 result.each {|room_mates| @rooms.last << room_mates.collect {|x| x.pid } }
659
660                 s = ROOM_RESULT
661                 last = result.pop
662                 result.each {|room_mates|
663                         s << room_mates.collect {|x| x.name }.join(' &raquo; ') + '<br />'
664                 }
665                 s << last.collect {|x| x.name }.join('、') + '<br />'
666
667                 addlog(announce(s))
668         end
669
670         def start_afterroom
671                 up_uptime(S[:vote_period])
672                 @phase = Phase::AfterRoom
673                 addlog(wsystem(AFTERROOM_PHASE))
674         end
675
676         def end_afterroom
677         end
678
679         def start_night
680                 up_uptime()
681                 @phase = Phase::Night
682                 addlog(wsystem(NIGHT_PHASE))
683         end
684
685         def end_night
686                 @players.end_action(self)
687                 logremap()
688
689                 @date += 1
690                 @players.reset_count(@date)
691                 @prevote = false if (@prevote && @players.deads().size > 1)
692
693                 s = ROOM_YESTERDAY
694                 yesterday_room = @rooms[@date - 2].dup
695                 last = yesterday_room.pop
696                 yesterday_room.each {|room_mates|
697                         s << room_mates.collect {|x| @players.player(x).name }.join(' &raquo; ') + '<br />'
698                 }
699                 s << last.collect {|x| @players.player(x).name }.join('、') + '<br />'
700
701                 addlog(wsystem(s))
702
703                 deads = @players.killed(@date-1)
704                 if deads.empty?
705                         addlog(announce(KILLMISS))
706                 else
707                         _ = deads.collect {|pl| pl.name }.join('と、')
708                         addlog(announce(c(KILLED, _)))
709                 end
710
711                 up_gameover()
712
713                 return if @state == 3
714
715                 up_showlives()
716         end
717
718         def start_morning
719                 up_uptime(S[:vote_period] + 0.2)
720                 @phase = Phase::Morning
721                 addlog(wsystem(MORNING_PHASE))
722         end
723
724         def end_morning
725         end
726
727
728         # Misc methods
729         #######################################################
730
731         def addlog(msg)
732                 File.open("db/vil/#{@vid}_#{@date}.html", 'a') {|of|
733                         of.flock(File::LOCK_EX)
734                         of.print(msg)
735                         of.flock(File::LOCK_UN)
736                 }
737         end
738         public :addlog  # FIXME! ==> sysrecord
739
740         def logremap
741                 $logger.debug('restructuring log...')
742                 s = File.read("db/vil/#{@vid}_#{@date}.html")
743                 msgs = _logremap(s)
744
745                 File.open("db/vil/#{@vid}_#{@date}.html", 'w') {|of|
746                         of.flock(File::LOCK_EX)
747                         of.print(msgs)
748                         of.flock(File::LOCK_UN)
749                 }
750         end
751         
752         def _logremap(str)
753                 _ = ''
754                 ary = str.split("\n")
755                 loop {
756                         line = ary.shift
757                         _ << line + "\n"
758
759                         break if line =~ /^<div class="message"><div class="system">#{NIGHT_PHASE}/o
760                 }
761
762                 night_hash = _logremap_night(ary)
763                 keys = night_hash.keys
764                 keys.delete('groan')
765                 keys.sort.each {|key|
766                         members = @rooms.members(@date, key).collect {|i| @players.player(i).name }
767                         _ << wsystem(c(ROOM_HEAD, members.join('、'))) + "\n"
768                         _ << night_hash[key].join("\n") + "\n"
769                 }
770                 _ << wsystem(GROAN_HEAD) + "\n"
771                 _ << night_hash['groan'].join("\n") + "\n"
772                 _
773         end
774
775         def _logremap_night(night_ary)
776                 h = {'groan' => []}
777                 night_ary.each {|line|
778                         if (line =~ MesRegex || line =~ ExtRegex)
779                                 pid = $2.to_i
780                                 case $1
781                                 when /(night)|(nextra)/
782                                         h[pid] = [] unless h.has_key?(pid)
783                                         h[pid] << line
784                                 when /(whisper)|(god)/
785                                         num = @rooms.number(@date, pid)
786                                         h[num] = [] unless h.has_key?(num)
787                                         h[num] << line
788                                 when 'groan'
789                                         h['groan'] << line
790                                 when 'say'
791                                         $logger.debug('state lock bug?')
792                                         $logger.debug(line)
793                                         # raise 'Must not happen.'
794                                 end
795                         else
796                                 $logger.debug('multiple logremap bug?')
797                                 $logger.debug(line)
798                                 # raise 'Must not happen.'
799                         end
800                 }
801                 h
802         end
803
804         def datestr(date)
805                 if date == 0
806                         'プロローグ'
807                 elsif (@state == State::End && date + 1 >= @date)
808                         (date == @date) ? '終了' : 'エピローグ'
809                 elsif (@state == State::Party && date == @date)
810                         'エピローグ'
811                 else
812                         "#{date}日目"
813                 end
814         end
815
816 end