2 MesRegex = /^<div class="message"><div class="([a-z]+)(\d+)">/
3 ExtRegex = /^<div class="message"><!--([a-z]+)(\d+)-->/
5 attr_reader :name, :date, :vid, :state, :players, :rule
6 attr_reader :userid, :winner, :phase, :rooms, :voting
7 attr_reader :first_restart, :guard
9 attr_accessor :update_time
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
16 @players = Players.new
18 @period = S[:std_period] # minute
19 # night_period = @period / 2
26 addlog(announce(OPENING))
28 player = Player.new(1, MASTER, 0)
29 #2011/02/23 mod:tkt for anniversary start
30 add_player(player, specified_start_message('GERT_ENTRY'))
31 #2011/02/23 mod:tkt for anniversary end
35 return if @state >= State::Progress
37 if discussions(@date, @players.player(1)).size > S[:log_max]
39 change_state_sync(State::End)
42 @players.all.each {|p|
45 if (p.lastmsg_time && \
46 p.lastmsg_time < Time.now() - (S[:restart_minute] * 60))
51 change_state_sync(State::Welcome)
54 #addlog(wsystem(c(AUTO_PARTING, pl_dels.join('、')))) if pl_dels != [] mod 2009/01/17
55 addlog(wsystem(c(AUTO_PARTING, pl_dels.join('、'),Time.now.strftime(PARTING_TIME)))) if pl_dels != []
57 while @update_time < Time.now do
58 @update_time += S[:restart_minute] * 60
61 DB::Villages.restart(@vid, @update_time)
65 def add_player(player, message)
67 change_state_sync(State::Ready) if @players.size == @players.max
69 addlog(wsystem(c(JOINING, player.name)))
70 record("say", player, message)
73 def delete_player(player)
74 #addlog(wsystem(c(PARTING, player.name))) mod 2009/01/17 tkt
75 addlog(wsystem(c(PARTING, player.name,Time.now.strftime(PARTING_TIME))))
78 change_state_sync(State::Welcome)
81 def change_state_sync(st)
83 DB::Villages.change_state(@vid, st)
87 (@state < State::End && @update_time.to_i < Time.now.to_i)
91 if (@state == State::Welcome && !@players.adv_ready?)
92 if (@first_restart && @players.ready?)
104 (0..@date).collect {|i| datestr(i) }
107 def discussion_size(date) # use this, prologue only.
108 IO.readlines("db/vil/#{@vid}_#{date}.html").size
111 def discussions(date, player, reverse = false)
112 ary = IO.readlines("db/vil/#{@vid}_#{date}.html")
113 ary.reverse! if reverse
116 if (@state < State::Party && (line =~ MesRegex || line =~ ExtRegex))
117 next if (!player && $1 != 'say')
119 if (player && !player.admin?)
121 # night pid is room-number
123 when 'whisper' then next unless player.can_whisper
124 when 'god' then next unless (player.pid == pid || player.can_whisper)
125 when 'groan' then next unless player.dead?
126 when 'night' then next unless @rooms.in?(date, pid, player.pid)
127 when 'nextra' then next unless @rooms.in?(date, pid, player.pid)
136 !@voting.empty? && @phase != Phase::Apology
140 def record(type, player, msg, face = nil)
141 face = "face#{player.color}" if !face
142 player.cnt[@date] += 1 if (@phase == Phase::Sun && type == 'say')
143 player.lastmsg_time = Time.now() if @state < State::Progress
145 # PreVote || Vote || FinalVote || Room || AfterRoom || Morning
146 if (player.live? && (type == 'say' || type == 'night') && [1, 2, 4, 5, 6, 8].index(@phase))
147 raise ErrorMsg.new('フェイズと発言種類が合致しません')
150 type = 'night' if (@phase == Phase::Night && type == 'say')
151 type = 'groan' if (player.dead? && (type == 'say' || type =='night'))
152 type = 'say' if (player.live? && type == 'groan')
153 type = 'say' if (@state >= State::Party && type != 'say')
157 @rooms.last.each_with_index {|ary, i|
158 c = i if ary.index(player.pid)
161 typec = type + c.to_s
163 # don't insert \n in message, cause this line in convert to JSON.
165 %Q(<div class="#{typec}">) + \
166 %Q(<div class="#{type}">#{player.name}) + \
167 %Q(<table class="message_box"><tr>) + \
168 %Q(<td width="40"><img src="#{S[:char_image_dir]}#{face}.jpg"></td>) + \
169 %Q(<td width="16"><img src="#{S[:image_dir]}#{type}00.jpg"></td>) + \
170 %Q(<td><div class="mes_#{type}_body0">) + \
171 %Q(<div class="mes_#{type}_body1">#{msg}</div>) + \
173 %Q(</tr></table></div></div>\n)
175 raise ErrorMsg.new('夜ではありません') if (@phase != Phase::Night && type == 'night')
181 @_targets || @players.lives()
188 #######################################################
191 addlog(announce(SYSTEM_EXIT))
194 @players.all().each {|p|
195 alive = (p.dead == 0) ? '生存' : '死亡'
196 sk = (@date == 0) ? '役職決定前' : p.skill.name
198 a.push(c(TRUTH, p.name, p.userid, alive, sk))
201 addlog(announce(a.join('<br>')))
207 addlog(announce('終了しました'))
208 DB::Villages.finish(@vid, @players.keys.size)
209 DB::Users.registlast(@vid, @players.keys)
214 # Internal periodic work methods
215 #######################################################
218 @players.silents().each {|p|
219 p.sudden_death(@date)
220 addlog(announce(c(SUDDEN_DEATH, p.name)))
227 @players.votes.each {|ary|
229 s << [p.name, t.name].join(' » ')
230 votes[t] = votes[t].to_i + 1
231 max = votes[t] if (votes[t] > max)
235 result = votes.select {|k, v| v == max }
236 result.collect! {|ary| ary[0] }
237 votes.each_pair {|p, n| s << c(ANON_VOTING, n, p.name) }
238 addlog(announce(s.join('<br>')))
246 wcnt = @players.wolves().size
247 vcnt = @players.villagers().size
249 if (wcnt == 0 || vcnt == 0)
250 if (@players.select_skill(7).size > 0)
251 w = (wcnt == 0) ? YOKO_WIN_F : YOKO_WIN_W
256 addlog(announce(FOLK_WIN))
259 addlog(announce(WOLF_WIN))
262 elsif @date == @guard
264 addlog(announce(GUARD_WIN))
266 elsif @players.lives().size < 3
267 exe = @players.select_skill(Skill::Exorcist)
268 pas = @players.select_skill(Skill::Pastor)
270 if ((exe.empty? || exe.first.skill.pass == false) && \
271 ((pas.empty? || pas.first.skill.pass == false) || \
272 (pas.first.skill.pass && @date + 1 != @guard)))
275 addlog(announce(WOLF_WIN))
278 addlog(announce(FOLK_WIN))
284 change_state_sync(State::Party)
287 @players.all().each {|p|
288 alive = (p.dead == 0) ? '生存' : '死亡'
289 a.push(c(TRUTH, p.name, p.userid, alive, p.skill.name))
291 addlog(announce(a.join('<br>')))
293 @players.open_party()
295 #2011/02/23 add:tkt for anniversary start
297 addlog(wsystem(c(specified_message('GUARD_DATE'), @guard - 1, @guard)))
299 #2011/02/23 add:tkt for anniversary end
303 def up_showlives(include_gert = true)
304 # Players@show_lives(), wtls
305 pls = @players.lives()
306 pls.delete(@players.player(1)) unless include_gert
307 s = c(LIVES, pls.collect {|p| p.name }.join('、'), pls.size)
311 def up_uptime(period = @period)
312 $logger.debug('from: ' + @update_time.inspect)
313 if @update_time > Time.now
314 @update_time = Time.now + (period * 60)
316 @update_time += period * 60
318 while (@update_time && @update_time.to_i < Time.now.to_i) do
319 @update_time += period * 60
321 $logger.debug('to: ' + @update_time.inspect)
326 #######################################################
328 def update(nocnt = nil)
329 return if (@state == State::End)
331 if (@state == State::Party || nocnt)
332 change_state_sync(State::End)
333 (nocnt) ? do_quit() : do_end()
338 (@date == 0) ? first_update() : _update()
357 (@prevote) ? start_room() : start_vote()
365 if picked.size == @players.lives().size
366 addlog(announce(STOP_EXECUTION))
369 @_targets = picked.dup
370 if ((@players.lives().size - picked.size) % 2) == 0
371 start_apology(picked)
386 when Phase::FinalVote
405 when Phase::AfterRoom
410 if @state == State::Party
412 elsif @players.lives().size > 2
426 @players.reset_count(@date)
428 @players.skill_mapping()
429 #2011/02/23 mod:tkt for anniversary start
430 @guard = guard_date()
431 #2011/02/23 mod:tkt for anniversary end
432 @rule = Regulation.rule(@players.size)
433 change_state_sync(State::Progress)
435 #2011/02/23 mod:tkt for anniversary start
436 addlog(announce(specified_message('START')))
437 #2011/02/23 mod:tkt for anniversary end
440 #2011/02/23 mod:tkt for anniversary start
441 $logger.debug("specified?(#{specified?}) specified_lost?(#{specified_lost?}) guard(#{@guard}) date(#{@date}) limit(#{(@guard - @date)}->#{(@guard - @date + 1)})")
442 record('say', @players.player(1), c(specified_message('GERT_FIRST'), @guard - @date, @guard - @date + 1))
443 #2011/02/23 mod:tkt for anniversary end
444 @players.player(1).dead = 1 # die, not use Player#kill()
446 wolves = @players.wolves()
447 whisper = (wolves.size == 1) ? FIRST_WHISPER_LW : FIRST_WHISPER
448 wolves.each {|w| record('whisper', w, whisper) }
453 #######################################################
456 #mod 2008/11/08 tkt : for epilogue time change, yes, i know this code is not good...
457 #to-do attached plyaers.size and period time
458 #period = (@state == State::Party) ? S[:epilogue_period] : @period
460 if @state == State::Party
461 period = 1 * @players.size * (@date - 1)
462 $logger.debug("epilogue_period#{period} plaersize#{@players.size} date#{(@date - 1)}")
463 if period < S[:epilogue_period_min]
464 period = S[:epilogue_period_min]
465 elsif (@players.size >= 8 || (period > S[:epilogue_period]))
466 period = S[:epilogue_period]
468 #period = (@state == State::Party) ? period_epi : @period
476 unless @state == State::Party
477 limit = @guard - @date
478 #2011/02/23 mod:tkt for anniversary start
479 addlog(wsystem(c(specified_message('SUN_PHASE'), limit)))
480 #2011/02/23 mod:tkt for anniversary end
485 up_sudden_death() unless $DEBUG
486 addlog(wsystem(SUNSET_PHASE))
490 up_uptime(S[:vote_period])
491 @phase = Phase::PreVote
492 @voting = @players.lives().shuffle
493 addlog(wsystem(c(PREVOTE_PHASE, @voting.collect {|p| p.sname }.join(' » '))))
494 addlog(vote(c(ANN_VOTING, @voting.first.name)))
498 up_uptime(S[:vote_period])
500 res = (pl.prevote) ? '賛成' : '反対'
501 @players.vote_map([pl, res])
502 addlog(vote_res(c(VOTING, pl.name, res)))
503 addlog(vote(c(ANN_VOTING, @voting.first.name))) unless @voting.empty?
507 result = @players.collect_prevote()
509 @prevote = (vote['result']) ? false : true
512 @players.votes.each {|pl, |
513 t = (pl.prevote) ? '賛成した' : '反対した'
514 s << %Q(#{pl.name}は、投票に#{t}<br />)
516 s << "<br />賛成に#{vote['approve']}票、反対に#{vote['object']}票、"
517 t = (vote['result']) ? '処刑を実施することになった。' : '処刑は保留された。'
524 up_uptime(S[:vote_period])
527 @voting = @players.lives().shuffle
528 addlog(wsystem(c(VOTE_PHASE, @voting.collect {|p| p.sname }.join(' » '))))
529 addlog(vote(c(ANN_VOTING, @voting.first.name)))
533 up_uptime(S[:vote_period])
535 pl.vote_suffle(@players.lives()) unless pl.vote
536 target = @players.player(pl.vote)
537 @players.vote_map([pl, target])
538 addlog(vote_res(c(VOTING, pl.name, target.name)))
539 addlog(vote(c(ANN_VOTING, @voting.first.name))) unless @voting.empty?
546 addlog(announce(c(EXECUTION, picked.first.name)))
547 picked.first.execute(@date)
553 def start_apology(targets)
554 up_uptime(@period / 3.0)
555 @phase = Phase::Apology
556 @voting = @_targets.shuffle
557 x = targets.collect {|t| t.name}.join('と、')
558 addlog(wsystem(c(APOLOGY_PHASE, x, @voting.collect {|p| p.sname }.join(' » '))))
559 addlog(apology(c(APOLOGY_START, @voting.first.name)))
563 up_uptime(@period / 3.0)
565 addlog(apology(c(APOLOGY_START, @voting.first.name))) unless @voting.empty?
572 up_uptime(S[:vote_period])
574 @phase = Phase::FinalVote
575 @voting = @players.lives().shuffle - @_targets
576 x = @_targets.collect {|t| t.name}.join('と、')
577 addlog(wsystem(c(FINALVOTE_PHASE, x, @voting.collect {|p| p.sname }.join(' » '))))
578 addlog(vote(c(ANN_VOTING, @voting.first.name)))
582 up_uptime(S[:vote_period])
584 pl.vote_suffle(vtargets()) unless pl.vote
585 target = @players.player(pl.vote)
586 @players.vote_map([pl, target])
587 addlog(vote_res(c(VOTING, pl.name, target.name)))
588 addlog(vote(c(ANN_VOTING, @voting.first.name))) unless @voting.empty?
595 addlog(announce(c(EXECUTION, picked.first.name)))
596 picked.first.execute(@date)
598 addlog(announce(STOP_EXECUTION))
607 @voting = @players.lives().shuffle
608 addlog(wsystem(c(ROOM_PHASE, @voting.collect {|p| p.sname }.join(' » '))))
614 up_uptime(S[:vote_period])
615 addlog(room(c(ANN_ROOM_VOTING, @voting.first.name)))
621 if (!pl.vote || pl.vote == pl.pid)
622 targets = @voting.select {|t| !pl.yesterday_mate.index(t.pid) }
623 pl.vote_suffle(targets)
625 mate = @players.player(pl.vote)
626 addlog(room_res(c(ROOM_VOTING, pl.name, mate.name)))
629 #if (@rule == Rule::Standard || @date != 1) mod 2008/11/08 tkt:change rule for 3 members room
630 if (@date != 1 || (@rule == Rule::Standard && (@players.size % 2 == 0)))
631 @players.room_map([pl, mate])
633 last_room = @players.mates.last
634 if (last_room && last_room.size == 2)
637 names = lr.map {|pl| pl.name }
638 addlog(room_res2(c(ROOM_RESULT_ADV, names.join('と、'))))
639 @players.room_remap(lr)
640 # last room mate added, go next
642 @players.room_map([pl, mate])
643 @voting.unshift(mate) unless @voting.size < 3
644 # not filled in the room
648 unless (@voting.empty? || @voting.size <= 3)
649 up_uptime(S[:vote_period])
650 addlog(room(c(ANN_ROOM_VOTING, @voting.first.name)))
652 @players.vote_suffle(@voting) unless @voting.empty?
656 @players.room_map(@voting.dup)
657 x = @voting.collect {|p| p.name}.join('と、')
658 addlog(room_res(c(BUSH_ROOM, x)))
678 result = @players.room_mapping()
679 result.each {|room_mates| @rooms.last << room_mates.collect {|x| x.pid } }
683 result.each {|room_mates|
684 s << room_mates.collect {|x| x.name }.join(' » ') + '<br />'
686 s << last.collect {|x| x.name }.join('、') + '<br />'
692 up_uptime(S[:vote_period])
693 @phase = Phase::AfterRoom
694 addlog(wsystem(AFTERROOM_PHASE))
702 @phase = Phase::Night
703 addlog(wsystem(NIGHT_PHASE))
707 @players.end_action(self)
711 @players.reset_count(@date)
712 @prevote = false if (@prevote && @players.deads().size > 1)
715 yesterday_room = @rooms[@date - 2].dup
716 last = yesterday_room.pop
717 yesterday_room.each {|room_mates|
718 s << room_mates.collect {|x| @players.player(x).name }.join(' » ') + '<br />'
720 s << last.collect {|x| @players.player(x).name }.join('、') + '<br />'
724 deads = @players.killed(@date-1)
726 addlog(announce(KILLMISS))
728 _ = deads.collect {|pl| pl.name }.join('と、')
729 addlog(announce(c(KILLED, _)))
734 return if @state == 3
740 up_uptime(S[:vote_period] + 0.2)
741 @phase = Phase::Morning
742 addlog(wsystem(MORNING_PHASE))
750 #######################################################
753 File.open("db/vil/#{@vid}_#{@date}.html", 'a') {|of|
754 of.flock(File::LOCK_EX)
756 of.flock(File::LOCK_UN)
759 public :addlog # FIXME! ==> sysrecord
762 $logger.debug('restructuring log...')
763 s = File.read("db/vil/#{@vid}_#{@date}.html")
766 File.open("db/vil/#{@vid}_#{@date}.html", 'w') {|of|
767 of.flock(File::LOCK_EX)
769 of.flock(File::LOCK_UN)
775 ary = str.split("\n")
780 break if line =~ /^<div class="message"><div class="system">#{NIGHT_PHASE}/o
783 night_hash = _logremap_night(ary)
784 keys = night_hash.keys
786 keys.sort.each {|key|
787 members = @rooms.members(@date, key).collect {|i| @players.player(i).name }
788 _ << wsystem(c(ROOM_HEAD, members.join('、'))) + "\n"
789 _ << night_hash[key].join("\n") + "\n"
791 _ << wsystem(GROAN_HEAD) + "\n"
792 _ << night_hash['groan'].join("\n") + "\n"
796 def _logremap_night(night_ary)
798 night_ary.each {|line|
799 if (line =~ MesRegex || line =~ ExtRegex)
802 when /(night)|(nextra)/
803 h[pid] = [] unless h.has_key?(pid)
805 when /(whisper)|(god)/
806 num = @rooms.number(@date, pid)
807 h[num] = [] unless h.has_key?(num)
812 $logger.debug('state lock bug?')
814 # raise 'Must not happen.'
817 $logger.debug('multiple logremap bug?')
819 # raise 'Must not happen.'
828 elsif (@state == State::End && date + 1 >= @date)
829 (date == @date) ? '終了' : 'エピローグ'
830 elsif (@state == State::Party && date == @date)
837 #2011/02/23 add:tkt for anniversary start
839 S[:specified_vils].index(@vid.to_s) != nil
843 S[:specified_lost] && specified?
846 def specified_message(default)
847 if @rule == Rule::Advance
848 specified_start_message(default)
854 def specified_start_message(default)
856 eval(default + '_LOST')
863 date = Regulation.guard(@players.size, @players.wolves().size)
871 #2011/02/23 add:tkt for anniversary end