2021-07-11 00:00:22 +02:00
# File: Spinach.pm
#
# Purpose: Trivial game engine with a twist. Game is played in rounds. Each
# round players choose a category of questions. Then a random question from
# that category is shown. All players then privately submit a "lie" to the
# bot. Then all "lies" are revealed along with the true answer. Players
# gain points every time another player picks their lie. Very fun!
2024-11-10 05:54:10 +01:00
# SPDX-FileCopyrightText: 2018-2024 Pragmatic Software <pragma78@gmail.com>
2021-07-11 00:00:22 +02:00
# SPDX-License-Identifier: MIT
2018-01-29 06:53:40 +01:00
2021-07-14 04:45:56 +02:00
package PBot::Plugin::Spinach ;
use parent 'PBot::Plugin::Base' ;
2018-01-29 06:53:40 +01:00
2021-06-19 06:23:34 +02:00
use PBot::Imports ;
2021-09-12 20:11:44 +02:00
use PBot::Core::Storage::HashObject ;
2018-01-29 06:53:40 +01:00
2021-07-14 04:45:56 +02:00
use PBot::Plugin::Spinach::Stats ;
use PBot::Plugin::Spinach::Rank ;
2019-07-11 03:40:53 +02:00
2018-01-29 06:53:40 +01:00
use JSON ;
2018-02-04 05:42:27 +01:00
use Lingua::EN::Fractions qw/fraction2words/ ;
use Lingua::EN::Numbers qw/num2en num2en_ordinal/ ;
use Lingua::EN::Numbers::Years qw/year2en/ ;
use Lingua::Stem qw/stem/ ;
2018-02-09 21:47:06 +01:00
use Lingua::EN::ABC qw/b2a/ ;
2018-02-04 05:42:27 +01:00
2018-02-25 03:17:55 +01:00
use Time::Duration qw/concise duration/ ;
2019-05-07 11:19:03 +02:00
use Text::Unidecode ;
use Encode ;
2024-11-09 08:29:52 +01:00
use Text::Levenshtein::XS 'distance' ;
2018-01-30 06:54:52 +01:00
use Data::Dumper ;
2020-02-15 23:38:32 +01:00
$ Data:: Dumper:: Sortkeys = sub {
my ( $ h ) = @ _ ; my @ a = sort grep { not /^(?:seen_questions|alternativeSpellings)$/ } keys %$ h ; \ @ a ;
} ;
2019-04-24 02:59:39 +02:00
2021-06-19 06:23:34 +02:00
$ Data:: Dumper:: Useqq = 1 ;
2019-04-24 12:55:48 +02:00
2023-04-14 02:01:23 +02:00
sub initialize ($self, %conf) {
2024-11-09 08:29:52 +01:00
$ self - > { pbot } - > { commands } - > add (
name = > 'spinach' ,
help = > 'Trivia game based on Fibbage' ,
subref = > sub { $ self - > cmd_spinach ( @ _ ) } ,
) ;
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { event_dispatcher } - > register_handler ( 'irc.part' , sub { $ self - > on_departure ( @ _ ) } ) ;
$ self - > { pbot } - > { event_dispatcher } - > register_handler ( 'irc.quit' , sub { $ self - > on_departure ( @ _ ) } ) ;
$ self - > { pbot } - > { event_dispatcher } - > register_handler ( 'irc.kick' , sub { $ self - > on_kick ( @ _ ) } ) ;
2018-02-11 00:57:58 +01:00
2020-02-15 23:38:32 +01:00
$ self - > { channel } = $ self - > { pbot } - > { registry } - > get_value ( 'spinach' , 'channel' ) // '##spinach' ;
2019-04-24 12:55:48 +02:00
2020-02-15 23:38:32 +01:00
my $ default_file = $ self - > { pbot } - > { registry } - > get_value ( 'spinach' , 'file' ) // 'trivia.json' ;
$ self - > { questions_filename } = $ self - > { pbot } - > { registry } - > get_value ( 'general' , 'data_dir' ) . "/spinach/$default_file" ;
$ self - > { stopwords_filename } = $ self - > { pbot } - > { registry } - > get_value ( 'general' , 'data_dir' ) . '/spinach/stopwords' ;
$ self - > { metadata_filename } = $ self - > { pbot } - > { registry } - > get_value ( 'general' , 'data_dir' ) . '/spinach/metadata' ;
$ self - > { stats_filename } = $ self - > { pbot } - > { registry } - > get_value ( 'general' , 'data_dir' ) . '/spinach/stats.sqlite' ;
2019-04-24 02:59:39 +02:00
2024-11-09 08:29:52 +01:00
$ self - > { metadata } = PBot::Core::Storage::HashObject - > new (
pbot = > $ self - > { pbot } ,
name = > 'Spinach Metadata' ,
filename = > $ self - > { metadata_filename }
) ;
2020-02-15 23:38:32 +01:00
$ self - > { metadata } - > load ;
$ self - > set_metadata_defaults ;
2018-01-29 06:53:40 +01:00
2024-11-09 08:29:52 +01:00
$ self - > { stats } = PBot::Plugin::Spinach::Stats - > new (
pbot = > $ self - > { pbot } ,
filename = > $ self - > { stats_filename }
) ;
$ self - > { rankcmd } = PBot::Plugin::Spinach::Rank - > new (
pbot = > $ self - > { pbot } ,
filename = > $ self - > { stats_filename } ,
channel = > $ self - > { channel }
) ;
2019-04-24 12:55:48 +02:00
2020-02-15 23:38:32 +01:00
$ self - > create_states ;
$ self - > load_questions ;
$ self - > load_stopwords ;
2018-01-29 06:53:40 +01:00
2024-11-09 08:29:52 +01:00
# seconds between tocks
$ self - > { tock_duration } = 30 ;
# total tocks for choosecategory/picktruth
$ self - > { choosecategory_max_tocks } = 4 ;
$ self - > { picktruth_max_tocks } = 4 ;
2018-01-29 06:53:40 +01:00
}
2023-04-14 02:01:23 +02:00
sub unload ($self) {
2024-11-09 08:29:52 +01:00
$ self - > { pbot } - > { commands } - > remove ( 'spinach' ) ;
2021-06-22 02:26:24 +02:00
$ self - > { pbot } - > { event_queue } - > dequeue_event ( 'spinach loop' ) ;
2020-02-15 23:38:32 +01:00
$ self - > { stats } - > end if $ self - > { stats_running } ;
$ self - > { pbot } - > { event_dispatcher } - > remove_handler ( 'irc.part' ) ;
$ self - > { pbot } - > { event_dispatcher } - > remove_handler ( 'irc.quit' ) ;
$ self - > { pbot } - > { event_dispatcher } - > remove_handler ( 'irc.kick' ) ;
2018-01-29 06:53:40 +01:00
}
2023-04-14 02:01:23 +02:00
sub on_kick ($self, $event_type, $event) {
2023-01-31 14:44:34 +01:00
my ( $ nick , $ user , $ host ) = ( $ event - > nick , $ event - > user , $ event - > host ) ;
my ( $ victim , $ reason ) = ( $ event - > to , $ event - > { args } [ 1 ] ) ;
my $ channel = $ event - > { args } [ 0 ] ;
2020-02-15 23:38:32 +01:00
return 0 if lc $ channel ne $ self - > { channel } ;
$ self - > player_left ( $ nick , $ user , $ host ) ;
return 0 ;
2018-01-30 07:15:08 +01:00
}
2023-04-14 02:01:23 +02:00
sub on_departure ($self, $event_type, $event) {
2023-01-31 14:44:34 +01:00
my ( $ nick , $ user , $ host , $ channel ) = ( $ event - > nick , $ event - > user , $ event - > host , $ event - > to ) ;
my $ type = uc $ event - > type ;
2020-02-15 23:38:32 +01:00
return 0 if $ type ne 'QUIT' and lc $ channel ne $ self - > { channel } ;
$ self - > player_left ( $ nick , $ user , $ host ) ;
return 0 ;
2018-01-30 07:15:08 +01:00
}
2023-04-14 06:04:12 +02:00
sub load_questions ($self, $filename = undef) {
2023-04-14 02:01:23 +02:00
if ( not defined $ filename ) {
$ filename = exists $ self - > { loaded_filename } ? $ self - > { loaded_filename } : $ self - > { questions_filename } ;
} else {
$ filename = $ self - > { pbot } - > { registry } - > get_value ( 'general' , 'data_dir' ) . "/spinach/$filename" ;
}
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "Spinach: Loading questions from $filename...\n" ) ;
my $ contents = do {
open my $ fh , '<' , $ filename or do {
$ self - > { pbot } - > { logger } - > log ( "Spinach: Failed to open $filename: $!\n" ) ;
return "Failed to load $filename" ;
} ;
local $/ ;
my $ text = <$fh> ;
close $ fh ;
$ text ;
2018-01-29 06:53:40 +01:00
} ;
2020-02-15 23:38:32 +01:00
$ self - > { loaded_filename } = $ filename ;
2018-02-27 01:54:34 +01:00
2020-02-15 23:38:32 +01:00
$ self - > { questions } = decode_json $ contents ;
$ self - > { categories } = ( ) ;
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
my $ questions ;
foreach my $ key ( keys % { $ self - > { questions } } ) {
foreach my $ question ( @ { $ self - > { questions } - > { $ key } } ) {
$ question - > { category } = uc $ question - > { category } ;
$ self - > { categories } { $ question - > { category } } { $ question - > { id } } = $ question ;
2019-05-03 08:13:27 +02:00
2024-11-09 08:29:52 +01:00
$ question - > { seen_timestamp } // = 0 ;
$ question - > { value } // = 0 ;
2020-02-15 23:38:32 +01:00
$ questions + + ;
}
2018-01-29 06:53:40 +01:00
}
2020-02-15 23:38:32 +01:00
my $ categories ;
foreach my $ category ( sort { keys % { $ self - > { categories } { $ b } } <=> keys % { $ self - > { categories } { $ a } } } keys % { $ self - > { categories } } ) {
2024-11-09 08:29:52 +01:00
# my $count = keys %{$self->{categories}{$category}};
2020-02-15 23:38:32 +01:00
# $self->{pbot}->{logger}->log("Category [$category]: $count\n");
$ categories + + ;
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "Spinach: Loaded $questions questions in $categories categories.\n" ) ;
return "Loaded $questions questions in $categories categories." ;
2018-01-29 06:53:40 +01:00
}
2023-04-14 02:01:23 +02:00
sub save_questions ($self) {
2020-02-15 23:38:32 +01:00
my $ json = JSON - > new ;
my $ json_text = $ json - > pretty - > canonical - > utf8 - > encode ( $ self - > { questions } ) ;
my $ filename = exists $ self - > { loaded_filename } ? $ self - > { loaded_filename } : $ self - > { questions_filename } ;
open my $ fh , '>' , $ filename or do {
$ self - > { pbot } - > { logger } - > log ( "Failed to open Spinach file $filename: $!\n" ) ;
return ;
} ;
print $ fh "$json_text\n" ;
close $ fh ;
2019-05-03 08:13:27 +02:00
}
2023-04-14 02:01:23 +02:00
sub load_stopwords ($self) {
2020-02-15 23:38:32 +01:00
open my $ fh , '<' , $ self - > { stopwords_filename } or do {
$ self - > { pbot } - > { logger } - > log ( "Spinach: Failed to open $self->{stopwords_filename}: $!\n" ) ;
return ;
} ;
2018-02-11 02:13:26 +01:00
2020-02-15 23:38:32 +01:00
foreach my $ word ( <$fh> ) {
chomp $ word ;
$ self - > { stopwords } { $ word } = 1 ;
}
close $ fh ;
2018-02-11 02:13:26 +01:00
}
2023-04-14 02:01:23 +02:00
sub set_metadata_defaults ($self) {
2020-02-15 23:38:32 +01:00
my $ defaults = {
category_choices = > 7 ,
category_autopick = > 0 ,
min_players = > 2 ,
stats = > 1 ,
seen_expiry = > 432000 ,
min_difficulty = > 0 ,
max_difficulty = > 25000 ,
max_missed_inputs = > 3 ,
debug_state = > 0 ,
2024-11-10 05:54:10 +01:00
rounds = > 3 ,
questions = > 3 ,
bonus_rounds = > 1 ,
2020-02-15 23:38:32 +01:00
} ;
2024-11-10 05:54:10 +01:00
foreach my $ key ( keys %$ defaults ) {
if ( not $ self - > { metadata } - > exists ( 'settings' , $ key ) ) {
$ self - > { metadata } - > set ( 'settings' , $ key , $ defaults - > { $ key } , 1 ) ;
2020-02-15 23:38:32 +01:00
}
}
2019-04-23 00:51:09 +02:00
}
2018-02-01 07:11:26 +01:00
my % color = (
2020-02-15 23:38:32 +01:00
white = > "\x0300" ,
black = > "\x0301" ,
blue = > "\x0302" ,
green = > "\x0303" ,
red = > "\x0304" ,
maroon = > "\x0305" ,
purple = > "\x0306" ,
orange = > "\x0307" ,
yellow = > "\x0308" ,
lightgreen = > "\x0309" ,
teal = > "\x0310" ,
cyan = > "\x0311" ,
lightblue = > "\x0312" ,
magneta = > "\x0313" ,
gray = > "\x0314" ,
lightgray = > "\x0315" ,
bold = > "\x02" ,
italics = > "\x1D" ,
underline = > "\x1F" ,
reverse = > "\x16" ,
reset = > "\x0F" ,
2018-02-01 07:11:26 +01:00
) ;
2023-04-14 02:01:23 +02:00
sub cmd_spinach ($self, $context) {
2020-05-04 22:21:35 +02:00
my $ arguments = $ context - > { arguments } ;
2020-02-15 23:38:32 +01:00
$ arguments =~ s/^\s+|\s+$//g ;
2018-01-29 06:53:40 +01:00
2024-11-09 08:29:52 +01:00
my $ usage = "Usage: spinach join|exit|ready|unready|choose|lie|reroll|skip|keep|score|show|rank|categories|filter|set|unset|load|state|edit|kick|abort; for more information about a command: spinach help <command>" ;
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
my $ command ;
( $ command , $ arguments ) = split / / , $ arguments , 2 ;
$ command = defined $ command ? lc $ command : '' ;
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
my ( $ channel , $ result ) ;
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
given ( $ command ) {
2018-02-04 05:42:27 +01:00
when ( 'help' ) {
2024-11-10 05:54:10 +01:00
given ( $ arguments ) {
2020-02-15 23:38:32 +01:00
when ( 'help' ) { return "Seriously?" ; }
2018-02-04 05:42:27 +01:00
2020-02-15 23:38:32 +01:00
when ( 'join' ) { return "Help is coming soon." ; }
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
when ( 'ready' ) { return "Help is coming soon." ; }
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
when ( 'exit' ) { return "Help is coming soon." ; }
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
when ( 'skip' ) { return "Use `skip` to skip a question and return to the \"choose category\" stage. A majority of the players must agree to skip." ; }
2018-02-15 07:13:54 +01:00
2020-02-15 23:38:32 +01:00
when ( 'keep' ) { return "Use `keep` to vote to prevent the current question from being rerolled or skipped." ; }
2019-05-05 04:28:34 +02:00
2020-02-15 23:38:32 +01:00
when ( 'abort' ) { return "Help is coming soon." ; }
2018-01-29 06:53:40 +01:00
2024-11-09 08:29:52 +01:00
when ( 'load' ) { return "Help is coming soon." ; }
when ( 'edit' ) { return "Help is coming soon." ; }
when ( 'state' ) { return "Help is coming soon." ; }
2020-02-15 23:38:32 +01:00
when ( 'reroll' ) { return "Use `reroll` to get a different question from the same category." ; }
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
when ( 'kick' ) { return "Help is coming soon." ; }
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
when ( 'players' ) { return "Help is coming soon." ; }
2018-02-02 05:15:54 +01:00
2020-02-15 23:38:32 +01:00
when ( 'score' ) { return "Help is coming soon." ; }
2018-02-01 07:11:26 +01:00
2020-02-15 23:38:32 +01:00
when ( 'choose' ) { return "Help is coming soon." ; }
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
when ( 'lie' ) { return "Help is coming soon." ; }
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
when ( 'truth' ) { return "Help is coming soon." ; }
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
when ( 'show' ) { return "Show the current question again." ; }
2019-04-22 03:56:46 +02:00
2020-02-15 23:38:32 +01:00
when ( 'categories' ) { return "Help is coming soon." ; }
2019-04-22 03:56:46 +02:00
2020-02-15 23:38:32 +01:00
when ( 'filter' ) { return "Help is coming soon." ; }
2018-01-30 06:54:52 +01:00
2020-02-15 23:38:32 +01:00
when ( 'set' ) { return "Help is coming soon." ; }
2019-04-24 03:22:52 +02:00
2020-02-15 23:38:32 +01:00
when ( 'unset' ) { return "Help is coming soon." ; }
2019-04-24 03:22:52 +02:00
2020-02-15 23:38:32 +01:00
when ( 'rank' ) { return "Help is coming soon." ; }
2019-04-24 12:55:48 +02:00
2020-02-15 23:38:32 +01:00
default {
2023-04-14 02:01:23 +02:00
if ( length $ arguments ) {
return "Spinach has no such command '$arguments'. I can't help you with that." ;
} else {
return "Usage: spinach help <command>" ;
}
2020-02-15 23:38:32 +01:00
}
}
2018-01-29 06:53:40 +01:00
}
2018-02-15 04:09:57 +01:00
2020-02-15 23:38:32 +01:00
when ( 'edit' ) {
2020-05-04 22:21:35 +02:00
my $ admin = $ self - > { pbot } - > { users } - > loggedin_admin ( $ self - > { channel } , $ context - > { hostmask } ) ;
2018-02-15 04:09:57 +01:00
2024-11-09 08:29:52 +01:00
if ( not $ admin ) { return "$context->{nick}: Only admins may edit questions." ; }
2018-02-15 04:09:57 +01:00
2020-02-15 23:38:32 +01:00
my ( $ id , $ key , $ value ) = split /\s+/ , $ arguments , 3 ;
2018-02-15 04:09:57 +01:00
2020-02-15 23:38:32 +01:00
if ( not defined $ id ) { return "Usage: spinach edit <question id> [key [value]]" ; }
2018-02-15 04:09:57 +01:00
2020-02-15 23:38:32 +01:00
$ id =~ s/,//g ;
2018-02-15 04:09:57 +01:00
2020-02-15 23:38:32 +01:00
my $ question ;
foreach my $ q ( @ { $ self - > { questions } - > { questions } } ) {
if ( $ q - > { id } == $ id ) {
$ question = $ q ;
last ;
}
}
2018-02-15 04:09:57 +01:00
2020-05-04 22:21:35 +02:00
if ( not defined $ question ) { return "$context->{nick}: No such question." ; }
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
if ( not defined $ key ) {
my $ dump = Dumper $ question ;
$ dump =~ s/\$VAR\d+ = \{\s*// ;
$ dump =~ s/ \};\s*$// ;
2020-05-04 22:21:35 +02:00
return "$context->{nick}: Question $id: $dump" ;
2020-02-15 23:38:32 +01:00
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
if ( not defined $ value ) {
my $ v = $ question - > { $ key } // 'unset' ;
2020-05-04 22:21:35 +02:00
return "$context->{nick}: Question $id: $key => $v" ;
2020-02-15 23:38:32 +01:00
}
2018-01-29 06:53:40 +01:00
2020-05-04 22:21:35 +02:00
if ( $ key !~ m/^(?:question|answer|category)$/i ) { return "$context->{nick}: You may not edit that key." ; }
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
$ question - > { $ key } = $ value ;
$ self - > save_questions ;
2020-05-04 22:21:35 +02:00
return "$context->{nick}: Question $id: $key set to $value" ;
2018-01-29 06:53:40 +01:00
}
2020-02-15 23:38:32 +01:00
when ( 'load' ) {
2020-05-04 22:21:35 +02:00
my $ u = $ self - > { pbot } - > { users } - > loggedin ( $ self - > { channel } , $ context - > { hostmask } ) ;
2023-04-14 02:01:23 +02:00
if ( not $ u or not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'botowner' ) ) {
2024-11-09 08:29:52 +01:00
return "$context->{nick}: Only botowners may reload the questions." ;
2023-04-14 02:01:23 +02:00
}
2018-02-12 08:37:37 +01:00
2020-02-15 23:38:32 +01:00
$ arguments = undef if not length $ arguments ;
return $ self - > load_questions ( $ arguments ) ;
2018-01-29 06:53:40 +01:00
}
2020-02-15 23:38:32 +01:00
when ( 'join' ) {
if ( $ self - > { current_state } eq 'nogame' ) {
2024-11-09 08:29:52 +01:00
$ self - > start_game ;
2020-02-15 23:38:32 +01:00
}
2018-02-12 08:37:37 +01:00
2024-11-09 08:29:52 +01:00
return $ self - > player_join ( $ context - > { nick } , $ context - > { user } , $ context - > { host } ) ;
2018-02-11 00:57:58 +01:00
}
2020-02-15 23:38:32 +01:00
when ( 'ready' ) {
2020-05-04 22:21:35 +02:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account_ancestor ( $ context - > { nick } , $ context - > { user } , $ context - > { host } ) ;
2020-02-15 23:38:32 +01:00
foreach my $ player ( @ { $ self - > { state_data } - > { players } } ) {
if ( $ player - > { id } == $ id ) {
2024-11-09 08:29:52 +01:00
if ( $ self - > { current_state } ne 'getplayers' ) {
return "/msg $context->{nick} This is not the time to use `ready`." ;
}
2020-02-15 23:38:32 +01:00
if ( $ player - > { ready } == 0 ) {
$ player - > { ready } = 1 ;
$ player - > { score } = 0 ;
2020-05-04 22:21:35 +02:00
return "/msg $self->{channel} $context->{nick} is ready!" ;
2020-02-15 23:38:32 +01:00
} else {
2020-05-04 22:21:35 +02:00
return "/msg $context->{nick} You are already ready." ;
2020-02-15 23:38:32 +01:00
}
}
}
2018-01-29 06:53:40 +01:00
2020-05-04 22:21:35 +02:00
return "$context->{nick}: You haven't joined this game yet. Use `j` to play now!" ;
2018-01-29 06:53:40 +01:00
}
2020-02-15 23:38:32 +01:00
when ( 'unready' ) {
2020-05-04 22:21:35 +02:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account_ancestor ( $ context - > { nick } , $ context - > { user } , $ context - > { host } ) ;
2019-05-07 12:26:40 +02:00
2020-02-15 23:38:32 +01:00
foreach my $ player ( @ { $ self - > { state_data } - > { players } } ) {
if ( $ player - > { id } == $ id ) {
2024-11-09 08:29:52 +01:00
if ( $ self - > { current_state } ne 'getplayers' ) {
return "/msg $context->{nick} This is not the time to use `unready`." ;
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
if ( $ player - > { ready } != 0 ) {
$ player - > { ready } = 0 ;
2020-05-04 22:21:35 +02:00
return "/msg $self->{channel} $context->{nick} is no longer ready!" ;
2020-02-15 23:38:32 +01:00
} else {
2020-05-04 22:21:35 +02:00
return "/msg $context->{nick} You are already not ready." ;
2020-02-15 23:38:32 +01:00
}
}
}
2018-01-29 06:53:40 +01:00
2020-05-04 22:21:35 +02:00
return "$context->{nick}: You haven't joined this game yet. Use `j` to play now!" ;
2018-02-12 08:37:37 +01:00
}
2020-02-15 23:38:32 +01:00
when ( 'exit' ) {
2020-05-04 22:21:35 +02:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account_ancestor ( $ context - > { nick } , $ context - > { user } , $ context - > { host } ) ;
2020-02-15 23:38:32 +01:00
my $ removed = 0 ;
for ( my $ i = 0 ; $ i < @ { $ self - > { state_data } - > { players } } ; $ i + + ) {
if ( $ self - > { state_data } - > { players } - > [ $ i ] - > { id } == $ id ) {
splice @ { $ self - > { state_data } - > { players } } , $ i - - , 1 ;
$ removed = 1 ;
}
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
if ( $ removed ) {
2023-04-14 02:01:23 +02:00
if ( $ self - > { state_data } - > { current_player } >= @ { $ self - > { state_data } - > { players } } ) {
$ self - > { state_data } - > { current_player } = @ { $ self - > { state_data } - > { players } } - 1 ;
}
2020-02-15 23:38:32 +01:00
if ( not @ { $ self - > { state_data } - > { players } } ) {
$ self - > { current_state } = 'nogame' ;
2021-06-22 02:26:24 +02:00
$ self - > { pbot } - > { event_queue } - > update_repeating ( 'spinach loop' , 0 ) ;
2020-05-04 22:21:35 +02:00
return "/msg $self->{channel} $context->{nick} has left the game! All players have left. The game has been stopped." ;
2020-02-15 23:38:32 +01:00
} else {
2020-05-04 22:21:35 +02:00
return "/msg $self->{channel} $context->{nick} has left the game!" ;
2020-02-15 23:38:32 +01:00
}
} else {
2020-05-04 22:21:35 +02:00
return "$context->{nick}: But you are not even playing the game." ;
2020-02-15 23:38:32 +01:00
}
2018-01-29 06:53:40 +01:00
}
2020-02-15 23:38:32 +01:00
when ( 'abort' ) {
2023-04-14 02:01:23 +02:00
if ( not $ self - > { pbot } - > { users } - > loggedin_admin ( $ self - > { channel } , $ context - > { hostmask } ) ) {
2024-11-09 08:29:52 +01:00
return "$context->{nick}: Only admins may abort the game." ;
2023-04-14 02:01:23 +02:00
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
$ self - > { current_state } = 'gameover' ;
2020-05-04 22:21:35 +02:00
return "/msg $self->{channel} $context->{nick}: The game has been aborted." ;
2020-02-15 23:38:32 +01:00
}
2018-02-11 02:13:26 +01:00
2020-02-15 23:38:32 +01:00
when ( $ _ eq 'score' or $ _ eq 'players' ) {
if ( $ self - > { current_state } eq 'getplayers' ) {
my @ names ;
foreach my $ player ( @ { $ self - > { state_data } - > { players } } ) {
2023-04-14 02:01:23 +02:00
if ( not $ player - > { ready } ) {
push @ names , "$player->{name} $color{red}(not ready)$color{reset}" ;
} else {
push @ names , $ player - > { name } ;
}
2020-02-15 23:38:32 +01:00
}
2018-02-11 02:13:26 +01:00
2020-02-15 23:38:32 +01:00
my $ players = join ', ' , @ names ;
$ players = 'none' if not @ names ;
return "Current players: $players" ;
}
2018-02-26 09:46:59 +01:00
2020-02-15 23:38:32 +01:00
# score
2024-11-09 08:29:52 +01:00
if ( not @ { $ self - > { state_data } - > { players } } ) {
return "There is nobody playing right now." ;
}
2018-02-26 09:46:59 +01:00
2020-02-15 23:38:32 +01:00
my $ text = '' ;
my $ comma = '' ;
foreach my $ player ( sort { $ b - > { score } <=> $ a - > { score } } @ { $ self - > { state_data } - > { players } } ) {
$ text . = "$comma$player->{name}: " . $ self - > commify ( $ player - > { score } ) ;
$ comma = '; ' ;
}
return $ text ;
2018-02-26 09:46:59 +01:00
}
2020-02-15 23:38:32 +01:00
when ( 'kick' ) {
2023-04-14 02:01:23 +02:00
if ( not $ self - > { pbot } - > { users } - > loggedin_admin ( $ self - > { channel } , $ context - > { hostmask } ) ) {
2024-11-09 08:29:52 +01:00
return "$context->{nick}: Only admins may kick people from the game." ;
2023-04-14 02:01:23 +02:00
}
2018-02-26 09:46:59 +01:00
2020-02-15 23:38:32 +01:00
if ( not length $ arguments ) { return "Usage: spinach kick <nick>" ; }
2018-02-26 09:46:59 +01:00
2020-02-15 23:38:32 +01:00
my $ removed = 0 ;
2018-02-15 07:13:54 +01:00
2020-02-15 23:38:32 +01:00
for ( my $ i = 0 ; $ i < @ { $ self - > { state_data } - > { players } } ; $ i + + ) {
if ( lc $ self - > { state_data } - > { players } - > [ $ i ] - > { name } eq $ arguments ) {
splice @ { $ self - > { state_data } - > { players } } , $ i - - , 1 ;
$ removed = 1 ;
}
}
2018-02-15 07:13:54 +01:00
2020-02-15 23:38:32 +01:00
if ( $ removed ) {
2023-04-14 02:01:23 +02:00
if ( $ self - > { state_data } - > { current_player } >= @ { $ self - > { state_data } - > { players } } ) {
$ self - > { state_data } - > { current_player } = @ { $ self - > { state_data } - > { players } } - 1 ;
}
2020-05-04 22:21:35 +02:00
return "/msg $self->{channel} $context->{nick}: $arguments has been kicked from the game." ;
2020-02-15 23:38:32 +01:00
} else {
2020-05-04 22:21:35 +02:00
return "$context->{nick}: $arguments isn't even in the game." ;
2020-02-15 23:38:32 +01:00
}
2018-02-15 07:13:54 +01:00
}
2020-02-15 23:38:32 +01:00
when ( 'n' ) { return $ self - > normalize_text ( $ arguments ) ; }
2018-02-15 07:13:54 +01:00
2020-02-15 23:38:32 +01:00
when ( 'v' ) {
my ( $ truth , $ lie ) = split /;/ , $ arguments ;
return $ self - > validate_lie ( $ self - > normalize_text ( $ truth ) , $ self - > normalize_text ( $ lie ) ) ;
2018-02-15 07:13:54 +01:00
}
2020-02-15 23:38:32 +01:00
when ( 'reroll' ) {
2024-11-09 08:29:52 +01:00
if ( $ self - > { current_state } eq 'getlies' ) {
2020-05-04 22:21:35 +02:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account_ancestor ( $ context - > { nick } , $ context - > { user } , $ context - > { host } ) ;
2020-02-15 23:38:32 +01:00
my $ player ;
my $ rerolled = 0 ;
my $ keep ;
foreach my $ i ( @ { $ self - > { state_data } - > { players } } ) {
if ( $ i - > { id } == $ id ) {
$ i - > { reroll } = 1 ;
delete $ i - > { keep } ;
$ rerolled + + ;
$ player = $ i ;
} elsif ( $ i - > { reroll } ) {
$ rerolled + + ;
} elsif ( $ i - > { keep } ) {
$ keep + + ;
}
}
2020-05-04 22:21:35 +02:00
if ( not $ player ) { return "$context->{nick}: You are not playing in this game. Use `j` to start playing now!" ; }
2020-02-15 23:38:32 +01:00
my $ needed = int ( @ { $ self - > { state_data } - > { players } } / 2 ) + 1 ;
$ needed -= $ rerolled ;
$ needed += $ keep ;
my $ votes_needed ;
if ( $ needed == 1 ) { $ votes_needed = "$needed more vote to reroll!" ; }
elsif ( $ needed > 1 ) { $ votes_needed = "$needed more votes to reroll!" ; }
else { $ votes_needed = "Rerolling..." ; }
2020-05-04 22:21:35 +02:00
return "/msg $self->{channel} $color{red}$context->{nick} has voted to reroll for another question from the same category! $color{reset}$votes_needed" ;
2020-02-15 23:38:32 +01:00
} else {
2020-05-04 22:21:35 +02:00
return "$context->{nick}: This command can be used only during the \"submit lies\" stage." ;
2020-02-15 23:38:32 +01:00
}
2019-05-05 04:28:34 +02:00
}
2020-02-15 23:38:32 +01:00
when ( 'skip' ) {
2024-11-09 08:29:52 +01:00
if ( $ self - > { current_state } eq 'getlies' ) {
2020-05-04 22:21:35 +02:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account_ancestor ( $ context - > { nick } , $ context - > { user } , $ context - > { host } ) ;
2020-02-15 23:38:32 +01:00
my $ player ;
my $ skipped = 0 ;
my $ keep = 0 ;
foreach my $ i ( @ { $ self - > { state_data } - > { players } } ) {
if ( $ i - > { id } == $ id ) {
$ i - > { skip } = 1 ;
delete $ i - > { keep } ;
$ skipped + + ;
$ player = $ i ;
} elsif ( $ i - > { skip } ) {
$ skipped + + ;
} elsif ( $ i - > { keep } ) {
$ keep + + ;
}
}
2020-05-04 22:21:35 +02:00
if ( not $ player ) { return "$context->{nick}: You are not playing in this game. Use `j` to start playing now!" ; }
2020-02-15 23:38:32 +01:00
my $ needed = int ( @ { $ self - > { state_data } - > { players } } / 2 ) + 1 ;
$ needed -= $ skipped ;
$ needed += $ keep ;
my $ votes_needed ;
if ( $ needed == 1 ) { $ votes_needed = "$needed more vote to skip!" ; }
elsif ( $ needed > 1 ) { $ votes_needed = "$needed more votes to skip!" ; }
else { $ votes_needed = "Skipping..." ; }
2020-05-04 22:21:35 +02:00
return "/msg $self->{channel} $color{red}$context->{nick} has voted to skip this category! $color{reset}$votes_needed" ;
2020-02-15 23:38:32 +01:00
} else {
2020-05-04 22:21:35 +02:00
return "$context->{nick}: This command can be used only during the \"submit lies\" stage." ;
2020-02-15 23:38:32 +01:00
}
2019-05-05 04:28:34 +02:00
}
2020-02-15 23:38:32 +01:00
when ( 'keep' ) {
2024-11-09 08:29:52 +01:00
if ( $ self - > { current_state } eq 'getlies' ) {
2020-05-04 22:21:35 +02:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account_ancestor ( $ context - > { nick } , $ context - > { user } , $ context - > { host } ) ;
2020-02-15 23:38:32 +01:00
my $ player ;
foreach my $ i ( @ { $ self - > { state_data } - > { players } } ) {
if ( $ i - > { id } == $ id ) {
$ i - > { keep } = 1 ;
delete $ i - > { skip } ;
delete $ i - > { reroll } ;
$ player = $ i ;
last ;
}
}
2024-11-09 08:29:52 +01:00
if ( not $ player ) {
return "$context->{nick}: You are not playing in this game. Use `j` to start playing now!" ;
}
2020-02-15 23:38:32 +01:00
2020-05-04 22:21:35 +02:00
return "/msg $self->{channel} $color{green}$context->{nick} has voted to keep playing the current question!" ;
2020-02-15 23:38:32 +01:00
} else {
2020-05-04 22:21:35 +02:00
return "$context->{nick}: This command can be used only during the \"submit lies\" stage." ;
2020-02-15 23:38:32 +01:00
}
2018-02-12 08:37:37 +01:00
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
when ( $ _ eq 'lie' or $ _ eq 'truth' or $ _ eq 'choose' ) {
2023-04-14 06:04:12 +02:00
$ arguments // = '' ;
2020-02-15 23:38:32 +01:00
$ arguments = lc $ arguments ;
2024-11-09 08:29:52 +01:00
if ( $ self - > { current_state } eq 'choosecategory' ) {
2020-02-15 23:38:32 +01:00
if ( not length $ arguments ) { return "Usage: spinach choose <integer>" ; }
2018-01-29 06:53:40 +01:00
2020-05-04 22:21:35 +02:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account_ancestor ( $ context - > { nick } , $ context - > { user } , $ context - > { host } ) ;
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
if ( not @ { $ self - > { state_data } - > { players } } or $ id != $ self - > { state_data } - > { players } - > [ $ self - > { state_data } - > { current_player } ] - > { id } ) {
2020-05-04 22:21:35 +02:00
return "$context->{nick}: It is not your turn to choose a category." ;
2020-02-15 23:38:32 +01:00
}
2018-01-29 06:53:40 +01:00
2023-04-14 02:01:23 +02:00
if ( $ arguments !~ /^[0-9]+$/ ) {
return "$context->{nick}: Please choose a category number. $self->{state_data}->{categories_text}" ;
}
2018-02-12 08:37:37 +01:00
2020-02-15 23:38:32 +01:00
$ arguments - - ;
2018-02-12 08:37:37 +01:00
2020-02-15 23:38:32 +01:00
if ( $ arguments < 0 or $ arguments >= @ { $ self - > { state_data } - > { category_options } } ) {
2020-05-04 22:21:35 +02:00
return "$context->{nick}: Choice out of range. Please choose a valid category. $self->{state_data}->{categories_text}" ;
2020-02-15 23:38:32 +01:00
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
if ( $ arguments == @ { $ self - > { state_data } - > { category_options } } - 2 ) {
$ arguments = ( @ { $ self - > { state_data } - > { category_options } } - 2 ) * rand ;
$ self - > { state_data } - > { current_category } = $ self - > { state_data } - > { category_options } - > [ $ arguments ] ;
2020-05-04 22:21:35 +02:00
return "/msg $self->{channel} $context->{nick} has chosen RANDOM CATEGORY! Randomly choosing category: $self->{state_data}->{current_category}!" ;
2020-02-15 23:38:32 +01:00
} elsif ( $ arguments == @ { $ self - > { state_data } - > { category_options } } - 1 ) {
$ self - > { state_data } - > { reroll_category } = 1 ;
2020-05-04 22:21:35 +02:00
return "/msg $self->{channel} $context->{nick} has chosen REROLL CATEGORIES! Rerolling categories..." ;
2020-02-15 23:38:32 +01:00
} else {
$ self - > { state_data } - > { current_category } = $ self - > { state_data } - > { category_options } - > [ $ arguments ] ;
2020-05-04 22:21:35 +02:00
return "/msg $self->{channel} $context->{nick} has chosen $self->{state_data}->{current_category}!" ;
2020-02-15 23:38:32 +01:00
}
}
2018-02-04 05:42:27 +01:00
2024-11-09 08:29:52 +01:00
if ( $ self - > { current_state } eq 'getlies' ) {
if ( not length $ arguments ) { return 'Usage: spinach lie <text>' ; }
2018-01-29 06:53:40 +01:00
2020-05-04 22:21:35 +02:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account_ancestor ( $ context - > { nick } , $ context - > { user } , $ context - > { host } ) ;
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
my $ player ;
foreach my $ i ( @ { $ self - > { state_data } - > { players } } ) {
if ( $ i - > { id } == $ id ) {
$ player = $ i ;
last ;
}
}
2018-02-12 08:37:37 +01:00
2024-11-09 08:29:52 +01:00
if ( not $ player ) {
return "$context->{nick}: You are not playing in this game. Use `j` to start playing now!" ;
}
2018-02-12 08:37:37 +01:00
2020-02-15 23:38:32 +01:00
$ arguments = $ self - > normalize_text ( $ arguments ) ;
my @ truth_count = split /\s/ , $ self - > { state_data } - > { current_question } - > { answer } ;
my @ lie_count = split /\s/ , $ arguments ;
2018-01-29 06:53:40 +01:00
2024-11-09 08:29:52 +01:00
my $ validate = $ self - > validate_lie ( $ self - > { state_data } - > { current_question } - > { answer } , $ arguments ) ;
2018-01-29 06:53:40 +01:00
2024-11-09 08:29:52 +01:00
# don't check alternate answers if exact match to first answer is found
unless ( $ validate == 0 ) {
# check alternative answers
foreach my $ alt ( @ { $ self - > { state_data } - > { current_question } - > { alternativeSpellings } } ) {
$ validate = self - > validate_lie ( $ alt , $ arguments ) ;
# end loop if exact match to truth
last if $ validate == 0 ;
2020-02-15 23:38:32 +01:00
}
}
2018-02-28 04:50:30 +01:00
2024-11-09 08:29:52 +01:00
if ( $ validate != 1 ) {
if ( + + $ player - > { lie_count } > 2 ) {
return "/msg $context->{nick} You cannot change your lie again this round." ;
}
2018-01-29 06:53:40 +01:00
2024-11-09 08:29:52 +01:00
if ( $ validate == 0 ) {
$ self - > send_message ( $ self - > { channel } , "$color{yellow}$context->{nick} has found the truth!$color{reset}" ) ;
} elsif ( $ validate == - 1 ) {
$ self - > send_message ( $ self - > { channel } , "$color{yellow}$context->{nick} has found part of the truth!$color{reset}" ) ;
} else {
$ self - > send_message ( $ self - > { channel } , "$color{yellow}$context->{nick} has misspelled the truth!$color{reset}" ) ;
}
2020-05-04 22:21:35 +02:00
return "$context->{nick}: Your lie is too similar to the truth! Please submit a different lie." ;
2020-02-15 23:38:32 +01:00
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
my $ changed = exists $ player - > { lie } ;
$ player - > { lie } = $ arguments ;
2018-01-29 06:53:40 +01:00
2020-05-04 22:21:35 +02:00
if ( $ changed ) { return "/msg $self->{channel} $context->{nick} has changed their lie!" ; }
else { return "/msg $self->{channel} $context->{nick} has submitted a lie!" ; }
2020-02-15 23:38:32 +01:00
}
2018-01-29 06:53:40 +01:00
2024-11-09 08:29:52 +01:00
if ( $ self - > { current_state } eq 'findtruth' ) {
if ( not length $ arguments ) { return 'Usage: spinach truth <integer>' ; }
2018-01-29 06:53:40 +01:00
2020-05-04 22:21:35 +02:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account_ancestor ( $ context - > { nick } , $ context - > { user } , $ context - > { host } ) ;
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
my $ player ;
foreach my $ i ( @ { $ self - > { state_data } - > { players } } ) {
if ( $ i - > { id } == $ id ) {
$ player = $ i ;
last ;
}
}
2018-01-29 06:53:40 +01:00
2023-04-14 02:01:23 +02:00
if ( not $ player ) {
return "$context->{nick}: You are not playing in this game. Use `j` to start playing now!" ;
}
2018-01-29 06:53:40 +01:00
2023-04-14 02:01:23 +02:00
if ( $ arguments !~ /^[0-9]+$/ ) {
return "$context->{nick}: Please select a truth number. $self->{state_data}->{current_choices_text}" ;
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
$ arguments - - ;
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
if ( $ arguments < 0 or $ arguments >= @ { $ self - > { state_data } - > { current_choices } } ) {
2020-05-04 22:21:35 +02:00
return "$context->{nick}: Selection out of range. Please select a valid truth. $self->{state_data}->{current_choices_text}" ;
2020-02-15 23:38:32 +01:00
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
my $ changed = exists $ player - > { truth } ;
$ player - > { truth } = uc $ self - > { state_data } - > { current_choices } - > [ $ arguments ] ;
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
if ( $ player - > { truth } eq $ player - > { lie } ) {
delete $ player - > { truth } ;
2020-05-04 22:21:35 +02:00
return "$context->{nick}: You cannot select your own lie!" ;
2020-02-15 23:38:32 +01:00
}
2020-05-04 22:21:35 +02:00
if ( $ changed ) { return "/msg $self->{channel} $context->{nick} has selected a different truth!" ; }
else { return "/msg $self->{channel} $context->{nick} has selected a truth!" ; }
2020-02-15 23:38:32 +01:00
}
2020-05-04 22:21:35 +02:00
return "$context->{nick}: It is not time to use this command." ;
2018-02-12 08:37:37 +01:00
}
2020-02-15 23:38:32 +01:00
when ( 'show' ) {
2024-11-09 08:29:52 +01:00
if ( $ self - > { current_state } =~ /(?:getlies|findtruth|showlies)/ ) {
$ self - > showquestion_helper ( $ self - > { state_data } , 1 ) ;
2024-11-08 04:46:30 +01:00
return '' ;
2020-02-15 23:38:32 +01:00
}
2018-01-29 06:53:40 +01:00
2020-05-04 22:21:35 +02:00
return "$context->{nick}: There is nothing to show right now." ;
2020-02-15 23:38:32 +01:00
}
2018-01-30 06:54:52 +01:00
2020-02-15 23:38:32 +01:00
when ( 'categories' ) {
if ( not length $ arguments ) { return "Usage: spinach categories <regex>" ; }
2018-01-30 06:54:52 +01:00
2020-02-15 23:38:32 +01:00
my $ result = eval {
use re::engine::RE2 - strict = > 1 ;
my @ categories = grep { /$arguments/i } keys % { $ self - > { categories } } ;
if ( not @ categories ) { return "No categories found." ; }
2019-04-22 03:56:46 +02:00
2020-02-15 23:38:32 +01:00
my $ text = "" ;
my $ comma = "" ;
foreach my $ cat ( sort @ categories ) {
$ text . = "$comma$cat: " . keys % { $ self - > { categories } { $ cat } } ;
$ comma = ", " ;
}
return $ text ;
} ;
2019-04-22 03:56:46 +02:00
2020-02-15 23:38:32 +01:00
return "$arguments: $@" if $@ ;
return $ result ;
2019-04-22 03:56:46 +02:00
}
2020-02-15 23:38:32 +01:00
when ( 'filter' ) {
my ( $ cmd , $ args ) = split / / , $ arguments , 2 ;
$ cmd = lc $ cmd ;
2019-04-22 03:56:46 +02:00
2020-02-15 23:38:32 +01:00
if ( not length $ cmd ) { return "Usage: spinach filter include <regex> | exclude <regex> | show | clear" ; }
2019-04-22 03:56:46 +02:00
2020-02-15 23:38:32 +01:00
given ( $ cmd ) {
when ( $ _ eq 'include' or $ _ eq 'exclude' ) {
if ( not length $ args ) { return "Usage: spinach filter $_ <regex>" ; }
2019-04-22 03:56:46 +02:00
2020-02-15 23:38:32 +01:00
eval { "" =~ /$args/ } ;
return "Bad filter $args: $@" if $@ ;
2019-04-22 03:56:46 +02:00
2020-02-15 23:38:32 +01:00
my @ categories = grep { /$args/i } keys % { $ self - > { categories } } ;
if ( not @ categories ) { return "Bad filter: No categories match. Try again." ; }
2019-04-22 03:56:46 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { metadata } - > set ( 'filter' , "category_" . $ _ . "_filter" , $ args ) ;
return "Spinach $_ filter set." ;
}
2019-04-22 03:56:46 +02:00
2020-02-15 23:38:32 +01:00
when ( 'clear' ) {
$ self - > { metadata } - > remove ( 'filter' ) ;
return "Spinach filter cleared." ;
}
2019-04-22 03:56:46 +02:00
2020-02-15 23:38:32 +01:00
when ( 'show' ) {
if ( not $ self - > { metadata } - > exists ( 'filter' , 'category_include_filter' ) and not $ self - > { metadata } - > exists ( 'filter' , 'category_exclude_filter' ) ) {
return "There is no Spinach filter set." ;
}
2019-04-22 03:56:46 +02:00
2020-02-15 23:38:32 +01:00
my $ text = "Spinach " ;
my $ comma = "" ;
2019-04-22 03:56:46 +02:00
2020-02-15 23:38:32 +01:00
if ( $ self - > { metadata } - > exists ( 'filter' , 'category_include_filter' ) ) {
$ text . = "include filter set to: " . $ self - > { metadata } - > get_data ( 'filter' , 'category_include_filter' ) ;
$ comma = "; " ;
}
2019-04-22 05:48:07 +02:00
2020-02-15 23:38:32 +01:00
if ( $ self - > { metadata } - > exists ( 'filter' , 'category_exclude_filter' ) ) {
$ text . = $ comma . "exclude filter set to: " . $ self - > { metadata } - > get_data ( 'filter' , 'category_exclude_filter' ) ;
}
2019-04-22 05:48:07 +02:00
2020-02-15 23:38:32 +01:00
return $ text ;
}
2019-04-22 03:56:46 +02:00
2020-02-15 23:38:32 +01:00
default { return "Unknown filter command '$cmd'." ; }
}
2019-04-22 03:56:46 +02:00
}
2020-02-15 23:38:32 +01:00
when ( 'state' ) {
my ( $ command , $ args ) = split /\s+/ , $ arguments ;
2019-05-08 10:37:00 +02:00
2020-02-15 23:38:32 +01:00
if ( $ command eq 'show' ) {
2024-11-09 08:29:52 +01:00
return "Previous state: $self->{previous_state}; current state: $self->{current_state}; previous result: $self->{state_data}->{previous_result}" ;
2020-02-15 23:38:32 +01:00
}
2019-05-08 10:37:00 +02:00
2020-02-15 23:38:32 +01:00
if ( $ command eq 'set' ) {
if ( not length $ args ) { return "Usage: spinach state set <new state>" ; }
2019-05-08 10:37:00 +02:00
2020-05-04 22:21:35 +02:00
my $ u = $ self - > { pbot } - > { users } - > loggedin ( $ self - > { channel } , $ context - > { hostmask } ) ;
2024-11-09 08:29:52 +01:00
if ( not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'admin' ) ) { return "$context->{nick}: Only admins may set game state." ; }
2019-05-08 10:37:00 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { previous_state } = $ self - > { current_state } ;
$ self - > { current_state } = $ args ;
return "State set to $args" ;
}
2019-05-08 10:37:00 +02:00
2020-02-15 23:38:32 +01:00
if ( $ command eq 'result' ) {
if ( not length $ args ) { return "Usage: spinach state result <current state result>" ; }
2019-05-08 10:37:00 +02:00
2020-05-04 22:21:35 +02:00
my $ admin = $ self - > { pbot } - > { users } - > loggedin_admin ( $ self - > { channel } , $ context - > { hostmask } ) ;
2024-11-09 08:29:52 +01:00
if ( not $ admin ) { return "$context->{nick}: Only admins may set game state." ; }
2019-05-08 10:37:00 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { state_data } - > { previous_result } = $ self - > { state_data } - > { result } ;
$ self - > { state_data } - > { result } = $ args ;
return "State result set to $args" ;
}
2019-05-08 10:37:00 +02:00
2020-02-15 23:38:32 +01:00
return "Usage: spinach state show | set <new state> | result <current state result>" ;
}
2019-05-08 10:37:00 +02:00
2020-02-15 23:38:32 +01:00
when ( 'set' ) {
my ( $ index , $ key , $ value ) = split /\s+/ , $ arguments ;
2019-04-24 03:22:52 +02:00
2020-02-15 23:38:32 +01:00
if ( not defined $ index ) { return "Usage: spinach set <metadata> [key [value]]" ; }
2019-04-28 09:21:15 +02:00
2020-02-15 23:38:32 +01:00
if ( lc $ index eq 'settings' and $ key and lc $ key eq 'stats' and defined $ value and $ self - > { current_state } ne 'nogame' ) {
return "Spinach stats setting cannot be modified while a game is in progress." ;
}
2019-04-28 09:21:15 +02:00
2020-05-04 22:21:35 +02:00
my $ admin = $ self - > { pbot } - > { users } - > loggedin_admin ( $ self - > { channel } , $ context - > { hostmask } ) ;
2024-11-09 08:29:52 +01:00
if ( defined $ value and not $ admin ) { return "$context->{nick}: Only admins may set game settings." ; }
2019-05-08 10:37:00 +02:00
2020-02-15 23:38:32 +01:00
return $ self - > { metadata } - > set ( $ index , $ key , $ value ) ;
}
2019-04-24 03:22:52 +02:00
2020-02-15 23:38:32 +01:00
when ( 'unset' ) {
my ( $ index , $ key ) = split /\s+/ , $ arguments ;
2019-04-24 03:22:52 +02:00
2020-02-15 23:38:32 +01:00
if ( not defined $ index or not defined $ key ) { return "Usage: spinach unset <metadata> <key>" ; }
2019-04-28 09:21:15 +02:00
2020-02-15 23:38:32 +01:00
if ( lc $ index eq 'settings' and lc $ key eq 'stats' and $ self - > { current_state } ne 'nogame' ) {
return "Spinach stats setting cannot be modified while a game is in progress." ;
}
2019-04-28 09:21:15 +02:00
2020-05-04 22:21:35 +02:00
my $ admin = $ self - > { pbot } - > { users } - > loggedin_admin ( $ self - > { channel } , $ context - > { hostmask } ) ;
2024-11-09 08:29:52 +01:00
if ( not $ admin ) { return "$context->{nick}: Only admins may set game settings." ; }
2019-05-08 10:37:00 +02:00
2020-02-15 23:38:32 +01:00
return $ self - > { metadata } - > unset ( $ index , $ key ) ;
}
2019-04-24 03:22:52 +02:00
2020-02-15 23:38:32 +01:00
when ( 'rank' ) { return $ self - > { rankcmd } - > rank ( $ arguments ) ; }
2019-04-24 12:55:48 +02:00
2020-02-15 23:38:32 +01:00
default { return $ usage ; }
2018-01-29 06:53:40 +01:00
}
2020-02-15 23:38:32 +01:00
return $ result ;
2018-01-29 06:53:40 +01:00
}
2024-11-09 08:29:52 +01:00
sub start_game ($self) {
$ self - > { state_data } = {
2024-11-10 05:54:10 +01:00
players = > [] ,
bonus_round = > 0 ,
2024-11-09 08:29:52 +01:00
} ;
$ self - > { current_state } = 'getplayers' ;
$ self - > { pbot } - > { event_queue } - > enqueue_event (
sub {
$ self - > run_one_state ;
} , 1 , 'spinach loop' , 1
) ;
}
sub player_join ($self, $nick, $user, $host) {
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account_ancestor ( $ nick , $ user , $ host ) ;
foreach my $ player ( @ { $ self - > { state_data } - > { players } } ) {
if ( $ player - > { id } == $ id ) {
return "$nick: You have already joined this game." ;
}
}
my $ player = {
id = > $ id ,
name = > $ nick ,
score = > 0 ,
ready = > $ self - > { current_state } eq 'getplayers' ? 0 : 1 ,
missedinputs = > 0
} ;
push @ { $ self - > { state_data } - > { players } } , $ player ;
# reset tocks to give player time to choose/lie
$ self - > { state_data } - > { tocks } = 0 ;
return "/msg $self->{channel} $nick has joined the game!" ;
}
2023-04-14 02:01:23 +02:00
sub player_left ($self, $nick, $user, $host) {
2024-11-09 08:29:52 +01:00
my $ id = $ self - > { pbot } - > { messagehistory } - > { database } - > get_message_account_ancestor ( $ nick , $ user , $ host ) ;
2018-01-30 07:15:08 +01:00
2020-02-15 23:38:32 +01:00
for ( my $ i = 0 ; $ i < @ { $ self - > { state_data } - > { players } } ; $ i + + ) {
if ( $ self - > { state_data } - > { players } - > [ $ i ] - > { id } == $ id ) {
splice @ { $ self - > { state_data } - > { players } } , $ i - - , 1 ;
2018-02-02 05:15:54 +01:00
2024-11-09 08:29:52 +01:00
if ( $ self - > { state_data } - > { current_player } >= @ { $ self - > { state_data } - > { players } } ) {
$ self - > { state_data } - > { current_player } = @ { $ self - > { state_data } - > { players } } - 1 ;
}
$ self - > send_message ( $ self - > { channel } , "$nick has left the game!" ) ;
last ;
2023-04-14 02:01:23 +02:00
}
2018-02-02 05:15:54 +01:00
}
2018-01-30 07:15:08 +01:00
}
2023-04-14 02:01:23 +02:00
sub send_message ($self, $to, $text, $delay = 0) {
2020-02-15 23:38:32 +01:00
my $ botnick = $ self - > { pbot } - > { registry } - > get_value ( 'irc' , 'botnick' ) ;
my $ message = {
2021-06-21 00:10:16 +02:00
nick = > $ botnick ,
user = > 'spinach' ,
host = > 'localhost' ,
hostmask = > "$botnick!spinach\@localhost" ,
2024-11-10 05:54:10 +01:00
keyword = > 'spinach' ,
2021-06-21 00:10:16 +02:00
command = > 'spinach' ,
checkflood = > 1 ,
message = > $ text
2020-02-15 23:38:32 +01:00
} ;
$ self - > { pbot } - > { interpreter } - > add_message_to_output_queue ( $ to , $ message , $ delay ) ;
2018-02-16 19:57:22 +01:00
}
2023-04-14 02:01:23 +02:00
sub add_new_suggestions ($self, $state) {
2020-02-15 23:38:32 +01:00
my $ question = undef ;
my $ modified = 0 ;
2018-02-01 07:11:26 +01:00
2020-02-15 23:38:32 +01:00
foreach my $ player ( @ { $ state - > { players } } ) {
if ( $ player - > { deceived } ) {
$ self - > { pbot } - > { logger } - > log ( "Adding new suggestion for $state->{current_question}->{id}: $state->{current_question}->{question}: $player->{deceived}\n" ) ;
if ( not grep { lc $ _ eq lc $ player - > { deceived } } @ { $ state - > { current_question } - > { suggestions } } ) {
if ( not defined $ question ) {
foreach my $ q ( @ { $ self - > { questions } - > { questions } } ) {
if ( $ q - > { id } == $ state - > { current_question } - > { id } ) {
$ question = $ q ;
last ;
}
}
}
push @ { $ question - > { suggestions } } , uc $ player - > { deceived } ;
$ modified = 1 ;
2018-02-01 07:11:26 +01:00
}
}
}
2020-02-15 23:38:32 +01:00
if ( $ modified ) { $ self - > save_questions ; }
2018-02-01 07:11:26 +01:00
}
2024-03-07 00:07:09 +01:00
sub commify ($self, $value) {
my $ text = reverse $ value ;
2020-02-15 23:38:32 +01:00
$ text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g ;
return scalar reverse $ text ;
2018-02-15 04:09:57 +01:00
}
2023-04-14 02:01:23 +02:00
sub normalize_question ($self, $text) {
2020-02-15 23:38:32 +01:00
my @ words = split / / , $ text ;
my $ uc = 0 ;
2023-04-14 02:01:23 +02:00
2020-02-15 23:38:32 +01:00
foreach my $ word ( @ words ) {
if ( $ word =~ m/^[A-Z]/ ) { $ uc + + ; }
2019-05-02 21:06:45 +02:00
}
2023-04-14 02:01:23 +02:00
if ( $ uc >= @ words * .8 ) {
$ text = ucfirst lc $ text ;
}
2019-05-02 21:06:45 +02:00
2020-02-15 23:38:32 +01:00
return $ text ;
2019-05-02 21:06:45 +02:00
}
2023-04-14 02:01:23 +02:00
sub normalize_text ($self, $text) {
2020-02-15 23:38:32 +01:00
$ text = unidecode $ text ;
$ text =~ s/^\s+|\s+$//g ;
$ text =~ s/\s+/ /g ;
$ text =~ s/^(the|a|an) //i ;
$ text =~ s/&/ AND /g ;
$ text = lc substr ( $ text , 0 , 80 ) ;
$ text =~ s/\$\s+(\d)/\$$1/g ;
$ text =~ s/\s*%$// ;
$ text =~ s/(\d),(\d\d\d)/$1$2/g ;
$ text =~ s/(\D,)(\D)/$1 $2/g ;
my @ words = split / / , $ text ;
my @ result ;
foreach my $ word ( @ words ) {
my $ punct = $ 1 if $ word =~ s/(\p{PosixPunct}+)$// ;
my $ newword = $ word ;
if ( $ word =~ m/^\d{4}$/ and $ word >= 1700 and $ word <= 2100 ) { $ newword = year2en ( $ word ) ; }
elsif ( $ word =~ m/^-?\d+$/ ) {
$ newword = num2en ( $ word ) ;
if ( defined $ punct and $ punct eq '%' ) {
$ newword . = " percent" ;
$ punct = undef ;
}
} elsif ( $ word =~ m/^(-?\d+)(?:st|nd|rd|th)$/i ) {
$ newword = num2en_ordinal ( $ 1 ) ;
} elsif ( $ word =~ m/^(-)?\$(\d+)?(\.\d+)?$/i ) {
my ( $ neg , $ dollars , $ cents ) = ( $ 1 , $ 2 , $ 3 ) ;
$ newword = '' ;
$ dollars = "$neg$dollars" if defined $ neg and defined $ dollars ;
if ( defined $ dollars ) {
$ word = num2en ( $ dollars ) ;
$ newword = "$word " . ( abs $ dollars == 1 ? "dollar" : "dollars" ) ;
}
if ( defined $ cents ) {
$ cents =~ s/^\.0*// ;
$ cents = "$neg$cents" if defined $ neg and not defined $ dollars ;
$ word = num2en ( $ cents ) ;
$ newword . = " and " if defined $ dollars ;
$ newword . = ( abs $ cents == 1 ? "$word cent" : "$word cents" ) ;
}
} elsif ( $ word =~ m/^(-?\d*\.\d+)(?:st|nd|rd|th)?$/i ) {
$ newword = num2en ( $ 1 ) ;
} elsif ( $ word =~ m {^(-?\d+\s*/\s*-?\d+)(?:st|nd|rd|th)?$}i ) {
$ newword = fraction2words ( $ 1 ) ;
}
$ newword . = $ punct if defined $ punct ;
push @ result , $ newword ;
}
$ text = uc b2a ( "@result" , s = > 1 ) ;
$ text =~ s/([A-Z])\./$1/g ;
$ text =~ s/-/ /g ;
$ text =~ s/["'?!]//g ;
$ text =~ s/\s+/ /g ;
$ text =~ s/^\s+|\s+$//g ;
return substr $ text , 0 , 80 ;
2018-02-04 05:42:27 +01:00
}
2023-04-14 02:01:23 +02:00
sub validate_lie ($self, $truth, $lie) {
2020-02-15 23:38:32 +01:00
my % truth_words = @ { stem map { $ _ = > 1 } grep { /^\w+$/ and not exists $ self - > { stopwords } { lc $ _ } } split /\b/ , $ truth } ;
my $ truth_word_count = keys % truth_words ;
2018-02-04 05:42:27 +01:00
2020-02-15 23:38:32 +01:00
my % lie_words = @ { stem map { $ _ = > 1 } grep { /^\w+$/ and not exists $ self - > { stopwords } { lc $ _ } } split /\b/ , $ lie } ;
my $ lie_word_count = keys % lie_words ;
2018-02-04 05:42:27 +01:00
2020-02-15 23:38:32 +01:00
my $ count = 0 ;
foreach my $ word ( keys % lie_words ) {
if ( exists $ truth_words { $ word } ) { $ count + + ; }
2018-02-04 05:42:27 +01:00
}
2024-11-09 08:29:52 +01:00
if ( $ count >= $ lie_word_count ) {
if ( $ count == $ truth_word_count ) {
return 0 ;
} else {
return - 1 ;
}
}
2018-02-04 05:42:27 +01:00
2020-02-15 23:38:32 +01:00
my $ stripped_truth = $ truth ;
$ stripped_truth =~ s/(?:\s|\p{PosixPunct})+//g ;
my $ stripped_lie = $ lie ;
$ stripped_lie =~ s/(?:\s|\p{PosixPunct})+//g ;
2018-02-04 05:42:27 +01:00
2020-02-15 23:38:32 +01:00
if ( $ stripped_truth eq $ stripped_lie ) { return 0 ; }
2018-02-04 05:42:27 +01:00
2024-11-09 08:29:52 +01:00
my $ distance = distance ( $ stripped_truth , $ stripped_lie ) ;
my $ length = ( length $ stripped_truth > length $ stripped_lie ) ? length $ stripped_truth : length $ stripped_lie ;
2018-01-29 06:53:40 +01:00
2024-11-09 08:29:52 +01:00
# if difference is 20% or less then they're too similar
if ( $ distance / $ length <= 0.20 ) { return - 2 ; }
2019-04-22 05:48:07 +02:00
2024-11-09 08:29:52 +01:00
return 1 ;
}
2018-01-29 06:53:40 +01:00
2024-11-09 08:29:52 +01:00
sub showquestion_helper ($self, $state, $show_category = undef) {
return if $ state - > { reroll_category } ;
2018-01-29 06:53:40 +01:00
2024-11-09 08:29:52 +01:00
if ( exists $ state - > { current_question } ) {
my $ category = '' ;
my $ value = '' ;
2020-02-15 23:38:32 +01:00
2024-11-09 08:29:52 +01:00
if ( $ show_category ) { $ category = "[$state->{current_category}] " ; }
2018-02-25 03:17:55 +01:00
2024-11-09 08:29:52 +01:00
if ( $ state - > { current_question } - > { value } ) { $ value = "[$state->{current_question}->{value}] " ; }
2019-05-07 06:08:09 +02:00
2024-11-09 08:29:52 +01:00
$ self - > send_message (
$ self - > { channel } ,
"$color{green}Current question:$color{reset} " . $ self - > commify ( $ state - > { current_question } - > { id } ) . ") $category$value$state->{current_question}->{question}"
) ;
} else {
$ self - > send_message ( $ self - > { channel } , "There is no current question." ) ;
}
}
2019-05-07 06:08:09 +02:00
2024-11-09 08:29:52 +01:00
# state machine
2019-05-07 06:08:09 +02:00
2024-11-09 08:29:52 +01:00
sub run_one_state ($self) {
# check for naughty or missing players
2024-11-10 05:54:10 +01:00
for ( my $ i = 0 ; $ i < @ { $ self - > { state_data } - > { players } } ; $ i + + ) {
if ( $ self - > { state_data } - > { players } - > [ $ i ] - > { missedinputs } >= $ self - > { metadata } - > get_data ( 'settings' , 'max_missed_inputs' ) ) {
$ self - > send_message (
$ self - > { channel } ,
"$color{red}$self->{state_data}->{players}->[$i]->{name} has missed too many prompts and has been ejected from the game!$color{reset}"
) ;
2018-01-29 06:53:40 +01:00
2024-11-10 05:54:10 +01:00
splice @ { $ self - > { state_data } - > { players } } , $ i - - , 1 ;
2019-05-07 06:08:09 +02:00
2024-11-10 05:54:10 +01:00
if ( $ self - > { state_data } - > { current_player } >= @ { $ self - > { state_data } - > { players } } ) {
$ self - > { state_data } - > { current_player } = @ { $ self - > { state_data } - > { players } } - 1 ;
}
2020-02-15 23:38:32 +01:00
}
2024-11-09 08:29:52 +01:00
}
2018-01-29 06:53:40 +01:00
2024-11-10 05:54:10 +01:00
if ( not @ { $ self - > { state_data } - > { players } } ) {
$ self - > send_message ( $ self - > { channel } , "All players have left the game!" ) ;
2024-11-09 08:29:52 +01:00
$ self - > { current_state } = 'nogame' ;
$ self - > { pbot } - > { event_queue } - > update_repeating ( 'spinach loop' , 0 ) ;
return ;
2018-02-26 10:32:02 +01:00
}
2024-11-10 05:54:10 +01:00
my $ state_data = $ self - > { state_data } ;
2024-11-09 08:29:52 +01:00
# transitioned to a brand new state; prepare first tock
if ( $ self - > { previous_state } ne $ self - > { current_state } ) {
$ state_data - > { newstate } = 1 ;
$ state_data - > { ticks } = 1 ;
2018-01-29 06:53:40 +01:00
2024-11-09 08:29:52 +01:00
if ( exists $ state_data - > { tick_drift } ) {
$ state_data - > { ticks } += $ state_data - > { tick_drift } ;
delete $ state_data - > { tick_drift } ;
}
$ state_data - > { first_tock } = 1 ;
} else {
$ state_data - > { newstate } = 0 ;
}
# dump new state data for logging/debugging
if ( $ state_data - > { newstate } and $ self - > { metadata } - > get_data ( 'settings' , 'debug_state' ) ) {
$ self - > { pbot } - > { logger } - > log ( "Spinach: New state: $self->{previous_state} ($state_data->{previous_result}) --> $self->{current_state}\n" . Dumper ( $ state_data ) . "\n" ) ;
}
# run one state/tick
2024-11-10 05:54:10 +01:00
my $ should_trans = $ self - > { states } { $ self - > { current_state } } { sub } ( $ state_data ) ;
2024-11-09 08:29:52 +01:00
if ( $ state_data - > { tocked } ) {
delete $ state_data - > { tocked } ;
delete $ state_data - > { first_tock } ;
$ state_data - > { ticks } = 0 ;
}
2024-11-10 05:54:10 +01:00
# prepare to transition to next state
2024-11-09 08:29:52 +01:00
$ state_data - > { previous_result } = $ state_data - > { result } ;
$ self - > { previous_state } = $ self - > { current_state } ;
2024-11-10 05:54:10 +01:00
if ( $ should_trans ) {
# sanity check to ensure edits to state machine didn't break anything
if ( not exists $ self - > { states } { $ self - > { current_state } } { trans } { $ state_data - > { result } } ) {
$ self - > { pbot } - > { logger } - > log ( "Spinach: State broke: no such transition to $state_data->{result} for state $self->{current_state}\n" ) ;
$ self - > send_message ( $ self - > { channel } , "Spinach state broke: no such transition to $state_data->{result} for state $self->{current_state}" ) ;
$ self - > { current_state } = 'nogame' ;
$ self - > { pbot } - > { event_queue } - > update_repeating ( 'spinach loop' , 0 ) ;
return ;
}
2024-11-09 08:29:52 +01:00
2024-11-10 05:54:10 +01:00
# transition to next state
$ self - > { current_state } = $ self - > { states } { $ self - > { current_state } } { trans } { $ state_data - > { result } } ;
# this shouldn't happen
if ( not defined $ self - > { current_state } ) {
$ self - > { pbot } - > { logger } - > log ( "Spinach state broke.\n" ) ;
$ self - > send_message ( $ self - > { channel } , "Spinach state broke." ) ;
$ self - > { current_state } = 'nogame' ;
$ self - > { pbot } - > { event_queue } - > update_repeating ( 'spinach loop' , 0 ) ;
return ;
}
}
2024-11-09 08:29:52 +01:00
# next tick
$ self - > { state_data } - > { ticks } + + ;
}
# state transitions
sub create_states ($self) {
$ self - > { pbot } - > { logger } - > log ( "Spinach: Creating game state machine\n" ) ;
$ self - > { previous_state } = '' ;
$ self - > { previous_result } = '' ;
$ self - > { current_state } = 'nogame' ;
# no game running || game ended
$ self - > { states } { 'nogame' } { sub } = sub { $ self - > nogame ( @ _ ) } ;
$ self - > { states } { 'nogame' } { trans } { start } = 'getplayers' ;
# waiting for players to join/ready
$ self - > { states } { 'getplayers' } { sub } = sub { $ self - > getplayers ( @ _ ) } ;
$ self - > { states } { 'getplayers' } { trans } { stop } = 'nogame' ;
$ self - > { states } { 'getplayers' } { trans } { allready } = 'roundinit' ;
# initialize round scoring, etc
$ self - > { states } { 'roundinit' } { sub } = sub { $ self - > roundinit ( @ _ ) } ;
$ self - > { states } { 'roundinit' } { trans } { finalscore } = 'finalscore' ;
$ self - > { states } { 'roundinit' } { trans } { next } = 'roundstart' ;
# start round (announce current round info)
$ self - > { states } { 'roundstart' } { sub } = sub { $ self - > roundstart ( @ _ ) } ;
$ self - > { states } { 'roundstart' } { trans } { next } = 'choosecategory' ;
$ self - > { states } { 'choosecategory' } { sub } = sub { $ self - > choosecategory ( @ _ ) } ;
$ self - > { states } { 'choosecategory' } { trans } { next } = 'showquestion' ;
$ self - > { states } { 'showquestion' } { sub } = sub { $ self - > showquestion ( @ _ ) } ;
$ self - > { states } { 'showquestion' } { trans } { next } = 'getlies' ;
$ self - > { states } { 'getlies' } { sub } = sub { $ self - > getlies ( @ _ ) } ;
$ self - > { states } { 'getlies' } { trans } { reroll } = 'showquestion' ;
$ self - > { states } { 'getlies' } { trans } { skip } = 'roundstart' ;
$ self - > { states } { 'getlies' } { trans } { next } = 'findtruth' ;
$ self - > { states } { 'findtruth' } { sub } = sub { $ self - > findtruth ( @ _ ) } ;
$ self - > { states } { 'findtruth' } { trans } { next } = 'showlies' ;
$ self - > { states } { 'showlies' } { sub } = sub { $ self - > showlies ( @ _ ) } ;
$ self - > { states } { 'showlies' } { trans } { next } = 'showtruth' ;
$ self - > { states } { 'showtruth' } { sub } = sub { $ self - > showtruth ( @ _ ) } ;
$ self - > { states } { 'showtruth' } { trans } { next } = 'reveallies' ;
$ self - > { states } { 'reveallies' } { sub } = sub { $ self - > reveallies ( @ _ ) } ;
$ self - > { states } { 'reveallies' } { trans } { next } = 'showscore' ;
$ self - > { states } { 'showscore' } { sub } = sub { $ self - > showscore ( @ _ ) } ;
$ self - > { states } { 'showscore' } { trans } { next } = 'roundinit' ;
$ self - > { states } { 'finalscore' } { sub } = sub { $ self - > finalscore ( @ _ ) } ;
$ self - > { states } { 'finalscore' } { trans } { next } = 'gameover' ;
$ self - > { states } { 'gameover' } { sub } = sub { $ self - > gameover ( @ _ ) } ;
$ self - > { states } { 'gameover' } { trans } { next } = 'getplayers' ;
}
# state subroutines
sub nogame ($self, $state) {
if ( $ self - > { stats_running } ) {
$ self - > { stats } - > end ;
delete $ self - > { stats_running } ;
}
$ self - > { pbot } - > { event_queue } - > update_repeating ( 'spinach loop' , 0 ) ;
$ state - > { result } = 'nogame' ;
2024-11-10 05:54:10 +01:00
return 0 ;
2024-11-09 08:29:52 +01:00
}
sub getplayers ($self, $state) {
my $ players = $ state - > { players } ;
my @ names ;
my $ unready = @$ players ? @$ players : 1 ;
foreach my $ player ( @$ players ) {
if ( not $ player - > { ready } ) {
push @ names , "$player->{name} $color{red}(not ready)$color{reset}" ;
} else {
$ unready - - ;
push @ names , $ player - > { name } ;
}
}
my $ min_players = $ self - > { metadata } - > get_data ( 'settings' , 'min_players' ) // 2 ;
if ( @$ players >= $ min_players and not $ unready ) {
$ self - > send_message ( $ self - > { channel } , "All players ready!" ) ;
if ( $ self - > { metadata } - > get_data ( 'settings' , 'stats' ) ) {
$ self - > { stats } - > begin ;
$ self - > { stats_running } = 1 ;
}
$ self - > { game } = {
2024-11-10 05:54:10 +01:00
rounds = > $ self - > { metadata } - > get_data ( 'settings' , 'rounds' ) ,
questions = > $ self - > { metadata } - > get_data ( 'settings' , 'questions' ) ,
2024-11-09 08:29:52 +01:00
round = > 1 , # current round
question = > 0 , # current question
} ;
$ self - > { scoring } = {
1 = > {
truth = > 500 ,
lie = > 1000 ,
} ,
2 = > {
truth = > 750 ,
lie = > 1500 ,
} ,
3 = > { # rounds 3 and greater use this scoring
truth = > 1000 ,
lie = > 2000 ,
} ,
2024-11-10 05:54:10 +01:00
bonus = > { # bonus rounds are when round >= rounds + 1
2024-11-09 08:29:52 +01:00
truth = > 2000 ,
lie = > 3000 ,
}
} ;
$ state - > { result } = 'allready' ;
2024-11-10 05:54:10 +01:00
return 1 ;
2024-11-09 08:29:52 +01:00
}
my $ tock ;
if ( $ state - > { first_tock } ) {
# first get-players announcement in default tock-duration seconds
$ tock = $ self - > { tock_duration } ;
} else {
# 5 minutes between get-players announcements
$ tock = 300 ;
}
if ( $ state - > { ticks } % $ tock == 0 ) {
$ state - > { tocked } = 1 ;
if ( not $ unready ) {
$ self - > send_message ( $ self - > { channel } , "Game cannot begin with one player." ) ;
}
if ( + + $ state - > { tocks } > 6 ) {
$ self - > send_message ( $ self - > { channel } , "Not all players were ready in time. The game has been stopped." ) ;
$ state - > { players } = [] ;
$ state - > { result } = 'stop' ;
2024-11-10 05:54:10 +01:00
return 1 ;
2024-11-09 08:29:52 +01:00
}
$ players = join ', ' , @ names ;
if ( not @ names ) {
$ players = 'none' ;
if ( $ state - > { tocks } >= 0 ) {
$ self - > send_message ( $ self - > { channel } , "All players have left the queue. The game has been stopped." ) ;
$ self - > { pbot } - > { event_queue } - > update_repeating ( 'spinach loop' , 0 ) ;
$ self - > { current_state } = 'nogame' ;
$ self - > { result } = 'nogame' ;
return ;
}
}
my $ msg = "Waiting for more players or for all players to ready up. Current players: $players" ;
$ self - > send_message ( $ self - > { channel } , $ msg ) ;
}
$ state - > { result } = 'wait' ;
2024-11-10 05:54:10 +01:00
return 0 ;
2024-11-09 08:29:52 +01:00
}
sub roundinit ($self, $state) {
$ self - > { game } - > { question } + + ;
if ( $ self - > { game } - > { question } > $ self - > { game } - > { questions } ) {
$ self - > { game } - > { round } + + ;
$ self - > { game } - > { question } = 1 ;
}
my $ round_scoring = $ self - > { game } - > { round } ;
2024-11-10 05:54:10 +01:00
if ( $ round_scoring >= $ self - > { game } - > { rounds } + 1 ) {
2024-11-09 08:29:52 +01:00
$ state - > { random_category } = 1 ;
2024-11-10 05:54:10 +01:00
$ state - > { bonus_round } + + ;
$ round_scoring = 'bonus' ;
2024-11-09 08:29:52 +01:00
} elsif ( $ round_scoring > 3 ) {
$ round_scoring = 3 ;
}
$ state - > { truth_points } = $ self - > { scoring } - > { $ round_scoring } - > { truth } ;
$ state - > { lie_points } = $ self - > { scoring } - > { $ round_scoring } - > { lie } ;
2024-11-10 05:54:10 +01:00
if ( $ state - > { bonus_round } > $ self - > { metadata } - > get_data ( 'settings' , 'bonus_rounds' ) ) {
2024-11-09 08:29:52 +01:00
$ state - > { result } = 'finalscore' ;
} else {
$ state - > { result } = 'next' ;
}
2024-11-10 05:54:10 +01:00
return 1 ;
2024-11-09 08:29:52 +01:00
}
sub roundstart ($self, $state) {
if ( $ state - > { ticks } % 2 == 0 || $ state - > { reroll_category } ) {
$ state - > { init } = 1 ;
$ state - > { tocks } = 0 ;
$ state - > { max_tocks } = $ self - > { choosecategory_max_tocks } ;
unless ( $ state - > { reroll_category } ) {
my $ round = $ self - > { game } - > { round } ;
my $ rounds = $ self - > { game } - > { rounds } ;
my $ question = $ self - > { game } - > { question } ;
my $ questions = $ self - > { game } - > { questions } ;
my $ announce ;
2024-11-10 05:54:10 +01:00
if ( $ round >= $ self - > { game } - > { rounds } + 1 ) {
2024-11-09 08:29:52 +01:00
$ announce = 'BONUS ROUND! BONUS QUESTION!' ;
} else {
$ announce = "Round $round/$rounds, question $question/$questions!" ;
}
$ self - > send_message ( $ self - > { channel } , "$announce $state->{lie_points} for each lie. $state->{truth_points} for the truth." )
}
$ state - > { result } = 'next' ;
2024-11-10 05:54:10 +01:00
return 1 ;
2024-11-09 08:29:52 +01:00
} else {
$ state - > { result } = 'wait' ;
2024-11-10 05:54:10 +01:00
return 0 ;
2024-11-09 08:29:52 +01:00
}
}
sub choosecategory ($self, $state) {
if ( $ state - > { init } or $ state - > { reroll_category } ) {
delete $ state - > { current_category } ;
$ state - > { current_player } + + unless $ state - > { reroll_category } ;
if ( $ state - > { current_player } >= @ { $ state - > { players } } ) {
$ state - > { current_player } = 0 ;
}
my @ choices ;
my @ categories ;
if ( $ self - > { metadata } - > exists ( 'filter' , 'category_include_filter' ) and length $ self - > { metadata } - > get_data ( 'filter' , 'category_include_filter' ) ) {
my $ filter = $ self - > { metadata } - > get_data ( 'filter' , 'category_include_filter' ) ;
@ categories = grep { /$filter/i } keys % { $ self - > { categories } } ;
} else {
@ categories = keys % { $ self - > { categories } } ;
}
if ( $ self - > { metadata } - > exists ( 'filter' , 'category_exclude_filter' ) and length $ self - > { metadata } - > get_data ( 'filter' , 'category_exclude_filter' ) ) {
my $ filter = $ self - > { metadata } - > get_data ( 'filter' , 'category_exclude_filter' ) ;
@ categories = grep { $ _ !~ /$filter/i } @ categories ;
}
my $ attempts = 0 ;
while ( 1 ) {
last if + + $ attempts > 10000 ;
my $ cat = $ categories [ rand @ categories ] ;
my @ questions = keys % { $ self - > { categories } { $ cat } } ;
if ( not @ questions ) {
$ self - > { pbot } - > { logger } - > log ( "No questions for category $cat\n" ) ;
next ;
}
if ( $ self - > { metadata } - > exists ( 'settings' , 'min_difficulty' ) ) {
@ questions = grep { $ self - > { categories } { $ cat } { $ _ } - > { value } >= $ self - > { metadata } - > get_data ( 'settings' , 'min_difficulty' ) } @ questions ;
}
if ( $ self - > { metadata } - > exists ( 'settings' , 'max_difficulty' ) ) {
@ questions = grep { $ self - > { categories } { $ cat } { $ _ } - > { value } <= $ self - > { metadata } - > get_data ( 'settings' , 'max_difficulty' ) } @ questions ;
}
if ( $ self - > { metadata } - > exists ( 'settings' , 'seen_expiry' ) ) {
my $ now = time ;
@ questions = grep { $ now - $ self - > { categories } { $ cat } { $ _ } - > { seen_timestamp } >= $ self - > { metadata } - > get_data ( 'settings' , 'seen_expiry' ) } @ questions ;
}
next if not @ questions ;
if ( not grep { $ _ eq $ cat } @ choices ) {
push @ choices , $ cat ;
}
last if @ choices == $ self - > { metadata } - > get_data ( 'settings' , 'category_choices' )
or @ categories < $ self - > { metadata } - > get_data ( 'settings' , 'category_choices' ) ;
}
if ( not @ choices ) {
$ self - > { pbot } - > { logger } - > log ( "Out of questions with current settings!\n" ) ;
$ self - > send_message ( $ self - > { channel } , "Out of questions with current settings! This will probably break something." ) ;
# XXX: do something useful here
}
push @ choices , 'RANDOM CATEGORY' ;
push @ choices , 'REROLL CATEGORIES' ;
$ state - > { categories_text } = '' ;
my $ i = 1 ;
my $ comma = '' ;
foreach my $ choice ( @ choices ) {
$ state - > { categories_text } . = "$comma$color{green}$i)$color{reset} " . $ choice ;
$ i + + ;
$ comma = "; " ;
}
if ( $ state - > { reroll_category } and not $ self - > { metadata } - > get_data ( 'settings' , 'category_autopick' ) ) {
$ self - > send_message ( $ self - > { channel } , $ state - > { categories_text } ) ;
}
$ state - > { category_options } = \ @ choices ;
$ state - > { category_rerolls } = 0 if $ state - > { init } ;
delete $ state - > { init } ;
delete $ state - > { reroll_category } ;
}
if ( exists $ state - > { current_category } or not @ { $ state - > { players } } ) {
$ state - > { result } = 'next' ;
2024-11-10 05:54:10 +01:00
return 1 ;
2024-11-09 08:29:52 +01:00
}
my $ tock ;
if ( $ state - > { first_tock } ) { $ tock = 2 ; }
2020-02-15 23:38:32 +01:00
else { $ tock = $ self - > { tock_duration } ; }
2018-02-02 05:15:54 +01:00
2020-02-15 23:38:32 +01:00
if ( $ state - > { ticks } % $ tock == 0 ) {
$ state - > { tocked } = 1 ;
2018-01-30 06:54:52 +01:00
2020-02-15 23:38:32 +01:00
if ( exists $ state - > { random_category } or $ self - > { metadata } - > get_data ( 'settings' , 'category_autopick' ) ) {
delete $ state - > { random_category } ;
my $ category = $ state - > { category_options } - > [ rand ( @ { $ state - > { category_options } } - 2 ) ] ;
my $ questions = scalar keys % { $ self - > { categories } { $ category } } ;
$ self - > send_message ( $ self - > { channel } , "$color{green}Category:$color{reset} $category! ($questions questions)" ) ;
$ state - > { current_category } = $ category ;
2024-11-09 08:29:52 +01:00
$ state - > { result } = 'next' ;
2024-11-10 05:54:10 +01:00
return 1 ;
2020-02-15 23:38:32 +01:00
}
2018-02-02 05:15:54 +01:00
2024-11-09 08:29:52 +01:00
if ( + + $ state - > { tocks } > $ state - > { max_tocks } ) {
2020-02-15 23:38:32 +01:00
# $state->{players}->[$state->{current_player}]->{missedinputs}++;
my $ name = $ state - > { players } - > [ $ state - > { current_player } ] - > { name } ;
my $ category = $ state - > { category_options } - > [ rand ( @ { $ state - > { category_options } } - 2 ) ] ;
$ self - > send_message ( $ self - > { channel } , "$name took too long to choose. Randomly choosing: $category!" ) ;
$ state - > { current_category } = $ category ;
2024-11-09 08:29:52 +01:00
$ state - > { result } = 'next' ;
2024-11-10 05:54:10 +01:00
return 1 ;
2020-02-15 23:38:32 +01:00
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
my $ name = $ state - > { players } - > [ $ state - > { current_player } ] - > { name } ;
my $ warning ;
2024-11-09 08:29:52 +01:00
if ( $ state - > { tocks } == $ state - > { max_tocks } ) { $ warning = $ color { red } ; }
elsif ( $ state - > { tocks } == $ state - > { max_tocks } - 1 ) { $ warning = $ color { yellow } ; }
else { $ warning = '' ; }
2018-02-25 03:17:55 +01:00
2024-11-09 08:29:52 +01:00
my $ remaining = $ self - > { tock_duration } * $ state - > { max_tocks } ;
$ remaining -= $ self - > { tock_duration } * ( $ state - > { tocks } - 1 ) ;
2020-02-15 23:38:32 +01:00
$ remaining = "(" . ( concise duration $ remaining ) . " remaining)" ;
2018-02-25 03:17:55 +01:00
2024-11-09 08:29:52 +01:00
my $ botnick = $ self - > { pbot } - > { registry } - > get_value ( 'irc' , 'botnick' ) ;
$ self - > send_message ( $ self - > { channel } , "$name: $warning$remaining Choose a category via `/msg $botnick c <number>`:$color{reset}" ) ;
2020-02-15 23:38:32 +01:00
$ self - > send_message ( $ self - > { channel } , "$state->{categories_text}" ) ;
2024-11-09 08:29:52 +01:00
$ state - > { result } = 'wait' ;
2024-11-10 05:54:10 +01:00
return 0 ;
2020-02-15 23:38:32 +01:00
}
2018-01-29 06:53:40 +01:00
2024-11-10 05:54:10 +01:00
if ( exists $ state - > { current_category } ) {
$ state - > { result } = 'next' ;
return 1 ;
} else {
$ state - > { result } = 'wait' ;
return 0 ;
}
2018-01-29 06:53:40 +01:00
}
2024-11-09 08:29:52 +01:00
sub showquestion ($self, $state) {
if ( $ state - > { ticks } % 2 == 0 ) {
2020-02-15 23:38:32 +01:00
my @ questions = keys % { $ self - > { categories } { $ state - > { current_category } } } ;
2018-02-25 03:17:55 +01:00
2020-02-15 23:38:32 +01:00
if ( exists $ state - > { seen_questions } - > { $ state - > { current_category } } ) {
my @ seen = keys % { $ state - > { seen_questions } - > { $ state - > { current_category } } } ;
my % seen = map { $ _ = > 1 } @ seen ;
@ questions = grep { ! defined $ seen { $ _ } } @ questions ;
}
2018-02-26 09:46:59 +01:00
2024-11-09 08:29:52 +01:00
@ questions = sort { $ self - > { categories } { $ state - > { current_category } } { $ a } - > { seen_timestamp } <=> $ self - > { categories } { $ state - > { current_category } } { $ b } - > { seen_timestamp } } @ questions ;
2020-02-15 23:38:32 +01:00
my $ now = time ;
@ questions = grep { $ now - $ self - > { categories } { $ state - > { current_category } } { $ _ } - > { seen_timestamp } >= $ self - > { metadata } - > get_data ( 'settings' , 'seen_expiry' ) } @ questions ;
2019-05-03 08:13:27 +02:00
2020-02-15 23:38:32 +01:00
if ( $ self - > { metadata } - > exists ( 'settings' , 'min_difficulty' ) ) {
@ questions = grep { $ self - > { categories } { $ state - > { current_category } } { $ _ } - > { value } >= $ self - > { metadata } - > get_data ( 'settings' , 'min_difficulty' ) } @ questions ;
}
2019-05-06 08:11:45 +02:00
2020-02-15 23:38:32 +01:00
if ( $ self - > { metadata } - > exists ( 'settings' , 'max_difficulty' ) ) {
@ questions = grep { $ self - > { categories } { $ state - > { current_category } } { $ _ } - > { value } <= $ self - > { metadata } - > get_data ( 'settings' , 'max_difficulty' ) } @ questions ;
}
2019-05-06 08:11:45 +02:00
2020-02-15 23:38:32 +01:00
if ( not @ questions ) {
$ self - > send_message ( $ self - > { channel } , "No more questions available in category $state->{current_category}! Picking new category..." ) ;
delete $ state - > { seen_questions } - > { $ state - > { current_category } } ;
@ questions = keys % { $ self - > { categories } { $ state - > { current_category } } } ;
$ state - > { reroll_category } = 1 ;
}
2018-02-25 03:17:55 +01:00
2020-02-15 23:38:32 +01:00
if ( $ state - > { reroll_question } ) {
delete $ state - > { reroll_question } ;
2019-05-07 11:19:03 +02:00
2020-02-15 23:38:32 +01:00
unless ( $ state - > { reroll_category } ) {
my $ count = @ questions ;
$ self - > send_message (
$ self - > { channel } ,
"Rerolling new question from $state->{current_category} (" . $ self - > commify ( $ count ) . " question" . ( $ count == 1 ? '' : 's' ) . " remaining)\n"
) ;
}
}
2018-02-26 09:46:59 +01:00
2020-02-15 23:38:32 +01:00
$ state - > { current_question } = $ self - > { categories } { $ state - > { current_category } } { $ questions [ 0 ] } ;
$ state - > { current_question } - > { question } = $ self - > normalize_question ( $ state - > { current_question } - > { question } ) ;
$ state - > { current_question } - > { answer } = $ self - > normalize_text ( $ state - > { current_question } - > { answer } ) ;
2018-02-04 05:42:27 +01:00
2020-02-15 23:38:32 +01:00
$ state - > { current_question } - > { seen_timestamp } = time unless $ state - > { reroll_category } ;
2019-05-03 08:13:27 +02:00
2020-02-15 23:38:32 +01:00
my @ alts = map { $ self - > normalize_text ( $ _ ) } @ { $ state - > { current_question } - > { alternativeSpellings } } ;
$ state - > { current_question } - > { alternativeSpellings } = \ @ alts ;
2018-02-04 05:42:27 +01:00
2020-02-15 23:38:32 +01:00
$ state - > { seen_questions } - > { $ state - > { current_category } } - > { $ state - > { current_question } - > { id } } = 1 ;
2018-02-26 09:46:59 +01:00
2020-02-15 23:38:32 +01:00
foreach my $ player ( @ { $ state - > { players } } ) {
delete $ player - > { lie } ;
delete $ player - > { lie_count } ;
delete $ player - > { truth } ;
delete $ player - > { good_lie } ;
delete $ player - > { deceived } ;
delete $ player - > { skip } ;
delete $ player - > { reroll } ;
delete $ player - > { keep } ;
}
2019-05-03 08:13:27 +02:00
2024-11-09 08:29:52 +01:00
$ state - > { current_choices_text } = '' ;
2019-05-06 08:11:45 +02:00
2024-11-09 08:29:52 +01:00
$ self - > showquestion_helper ( $ state ) ;
2019-05-06 08:11:45 +02:00
2024-11-09 08:29:52 +01:00
$ state - > { max_tocks } = $ self - > { picktruth_max_tocks } ;
$ state - > { tocks } = 0 ;
$ state - > { init } = 1 ;
$ state - > { current_lie_player } = 0 ;
2019-05-06 08:11:45 +02:00
2024-11-09 08:29:52 +01:00
$ state - > { result } = 'next' ;
2024-11-10 05:54:10 +01:00
return 1 ;
2020-02-15 23:38:32 +01:00
} else {
2024-11-09 08:29:52 +01:00
$ state - > { result } = 'wait' ;
2024-11-10 05:54:10 +01:00
return 0 ;
2020-02-15 23:38:32 +01:00
}
2018-01-29 06:53:40 +01:00
}
2023-04-14 02:01:23 +02:00
sub getlies ($self, $state) {
2024-11-09 08:29:52 +01:00
if ( $ state - > { reroll_category } ) {
$ state - > { result } = 'skip' ;
2024-11-10 05:54:10 +01:00
return 1 ;
2024-11-09 08:29:52 +01:00
}
2020-02-15 23:38:32 +01:00
my $ tock ;
2024-11-09 08:29:52 +01:00
if ( $ state - > { first_tock } ) { $ tock = 2 ; }
2020-02-15 23:38:32 +01:00
else { $ tock = $ self - > { tock_duration } ; }
my @ nolies ;
2024-11-09 08:29:52 +01:00
my $ reveallies = '. Revealing lies! ' ;
2020-02-15 23:38:32 +01:00
my $ lies = 0 ;
my $ comma = '' ;
my @ keeps ;
my @ rerolls ;
my @ skips ;
foreach my $ player ( @ { $ state - > { players } } ) {
2024-11-09 08:29:52 +01:00
if ( not exists $ player - > { lie } ) {
push @ nolies , $ player - > { name } ;
} else {
2020-02-15 23:38:32 +01:00
$ lies + + ;
$ reveallies . = "$comma$player->{name}: $player->{lie}" ;
$ comma = '; ' ;
}
if ( $ player - > { reroll } ) { push @ rerolls , $ player - > { name } ; }
if ( $ player - > { skip } ) { push @ skips , $ player - > { name } ; }
if ( $ player - > { keep } ) { push @ keeps , $ player - > { name } ; }
2018-02-01 07:11:26 +01:00
}
2024-11-09 08:29:52 +01:00
# advance to next state if everyone has submitted a lie
if ( not @ nolies ) {
$ state - > { result } = 'next' ;
2024-11-10 05:54:10 +01:00
return 1 ;
2024-11-09 08:29:52 +01:00
}
2020-02-15 23:38:32 +01:00
2024-11-09 08:29:52 +01:00
$ reveallies = '' if not $ lies ;
2020-02-15 23:38:32 +01:00
if ( @ rerolls ) {
my $ needed = int ( @ { $ state - > { players } } / 2 ) + 1 ;
$ needed += @ keeps ;
$ needed -= @ rerolls ;
if ( $ needed <= 0 ) {
$ state - > { reroll_question } = 1 ;
$ self - > send_message ( $ self - > { channel } , "The answer was: " . uc ( $ state - > { current_question } - > { answer } ) . $ reveallies ) ;
2024-11-09 08:29:52 +01:00
$ state - > { result } = 'reroll' ;
2024-11-10 05:54:10 +01:00
return 1 ;
2020-02-15 23:38:32 +01:00
}
2018-02-26 09:46:59 +01:00
}
2019-05-05 04:28:34 +02:00
2020-02-15 23:38:32 +01:00
if ( @ skips ) {
my $ needed = int ( @ { $ state - > { players } } / 2 ) + 1 ;
$ needed += @ keeps ;
$ needed -= @ skips ;
if ( $ needed <= 0 ) {
$ self - > send_message ( $ self - > { channel } , "The answer was: " . uc ( $ state - > { current_question } - > { answer } ) . $ reveallies ) ;
2024-11-09 08:29:52 +01:00
$ state - > { result } = 'skip' ;
2024-11-10 05:54:10 +01:00
return 1 ;
2020-02-15 23:38:32 +01:00
}
2019-05-05 04:28:34 +02:00
}
2020-02-15 23:38:32 +01:00
if ( $ state - > { ticks } % $ tock == 0 ) {
$ state - > { tocked } = 1 ;
2024-11-09 08:29:52 +01:00
if ( + + $ state - > { tocks } > $ state - > { max_tocks } ) {
2020-02-15 23:38:32 +01:00
my @ missedinputs ;
foreach my $ player ( @ { $ state - > { players } } ) {
if ( not exists $ player - > { lie } ) {
push @ missedinputs , $ player - > { name } ;
$ player - > { missedinputs } + + ;
}
}
if ( @ missedinputs ) {
my $ missed = join ', ' , @ missedinputs ;
$ self - > send_message ( $ self - > { channel } , "$missed failed to submit a lie in time!" ) ;
}
2024-11-09 08:29:52 +01:00
$ state - > { tocks } = 0 ;
$ state - > { init } = 1 ;
$ state - > { result } = 'next' ;
2024-11-10 05:54:10 +01:00
return 1 ;
2020-02-15 23:38:32 +01:00
}
my $ players = join ', ' , @ nolies ;
my $ warning ;
2024-11-09 08:29:52 +01:00
if ( $ state - > { tocks } == $ state - > { max_tocks } ) { $ warning = $ color { red } ; }
elsif ( $ state - > { tocks } == $ state - > { max_tocks } - 1 ) { $ warning = $ color { yellow } ; }
else { $ warning = '' ; }
2020-02-15 23:38:32 +01:00
2024-11-09 08:29:52 +01:00
my $ remaining = $ self - > { tock_duration } * $ state - > { max_tocks } ;
$ remaining -= $ self - > { tock_duration } * ( $ state - > { tocks } - 1 ) ;
2020-02-15 23:38:32 +01:00
$ remaining = "(" . ( concise duration $ remaining ) . " remaining)" ;
2024-11-09 08:29:52 +01:00
my $ botnick = $ self - > { pbot } - > { registry } - > get_value ( 'irc' , 'botnick' ) ;
$ self - > send_message ( $ self - > { channel } , "$players: $warning$remaining Submit your lie now via `/msg $botnick lie <your lie>`!" ) ;
2019-05-05 04:28:34 +02:00
}
2018-02-26 09:46:59 +01:00
2024-11-09 08:29:52 +01:00
$ state - > { result } = 'wait' ;
2024-11-10 05:54:10 +01:00
return 0 ;
2020-02-15 23:38:32 +01:00
}
2023-04-14 02:01:23 +02:00
sub findtruth ($self, $state) {
2020-02-15 23:38:32 +01:00
my $ tock ;
2024-11-09 08:29:52 +01:00
if ( $ state - > { first_tock } ) { $ tock = 2 ; }
2020-02-15 23:38:32 +01:00
else { $ tock = $ self - > { tock_duration } ; }
2019-05-14 02:59:15 +02:00
2020-02-15 23:38:32 +01:00
my @ notruth ;
2024-11-09 08:29:52 +01:00
2020-02-15 23:38:32 +01:00
foreach my $ player ( @ { $ state - > { players } } ) {
if ( not exists $ player - > { truth } ) { push @ notruth , $ player - > { name } ; }
2018-02-26 09:46:59 +01:00
}
2024-11-09 08:29:52 +01:00
if ( not @ notruth ) {
# all players have selected a truth
$ state - > { result } = 'next' ;
2024-11-10 05:54:10 +01:00
return 1 ;
2024-11-09 08:29:52 +01:00
}
2020-02-15 23:38:32 +01:00
if ( $ state - > { init } ) {
delete $ state - > { init } ;
my @ choices ;
my @ suggestions = @ { $ state - > { current_question } - > { suggestions } } ;
my @ lies ;
foreach my $ player ( @ { $ state - > { players } } ) {
if ( $ player - > { lie } ) {
if ( not grep { $ _ eq $ player - > { lie } } @ lies ) {
push @ lies , uc $ player - > { lie } ;
}
}
}
while ( 1 ) {
my $ limit = @ { $ state - > { players } } < 5 ? 5 : @ { $ state - > { players } } ;
last if @ choices >= $ limit ;
if ( @ lies ) {
my $ random = rand @ lies ;
push @ choices , $ lies [ $ random ] ;
splice @ lies , $ random , 1 ;
next ;
}
if ( @ suggestions ) {
my $ random = rand @ suggestions ;
my $ suggestion = uc $ suggestions [ $ random ] ;
push @ choices , $ suggestion if not grep { $ _ eq $ suggestion } @ choices ;
splice @ suggestions , $ random , 1 ;
next ;
}
last ;
}
2024-11-09 08:29:52 +01:00
splice @ choices , rand @ choices + 1 , 0 , $ state - > { current_question } - > { answer } ;
$ state - > { correct_answer } = $ state - > { current_question } - > { answer } ;
2020-02-15 23:38:32 +01:00
my $ i = 0 ;
my $ comma = '' ;
my $ text = '' ;
foreach my $ choice ( @ choices ) {
+ + $ i ;
$ text . = "$comma$color{green}$i) $color{reset}$choice" ;
$ comma = '; ' ;
}
$ state - > { current_choices_text } = $ text ;
$ state - > { current_choices } = \ @ choices ;
2019-05-09 09:16:37 +02:00
}
2018-02-15 07:13:54 +01:00
2020-02-15 23:38:32 +01:00
if ( $ state - > { ticks } % $ tock == 0 ) {
$ state - > { tocked } = 1 ;
2024-11-09 08:29:52 +01:00
if ( + + $ state - > { tocks } > $ state - > { max_tocks } ) {
2020-02-15 23:38:32 +01:00
my @ missedinputs ;
foreach my $ player ( @ { $ state - > { players } } ) {
if ( not exists $ player - > { truth } ) {
push @ missedinputs , $ player - > { name } ;
$ player - > { missedinputs } + + ;
$ player - > { score } -= $ state - > { lie_points } ;
}
}
2018-02-01 07:11:26 +01:00
2020-02-15 23:38:32 +01:00
if ( @ missedinputs ) {
my $ missed = join ', ' , @ missedinputs ;
$ self - > send_message ( $ self - > { channel } , "$missed failed to find the truth in time! They lose $state->{lie_points} points!" ) ;
}
2024-11-09 08:29:52 +01:00
$ state - > { result } = 'next' ;
2024-11-10 05:54:10 +01:00
return 1 ;
2018-01-29 06:53:40 +01:00
}
2020-02-15 23:38:32 +01:00
my $ players = join ', ' , @ notruth ;
my $ warning ;
2024-11-09 08:29:52 +01:00
if ( $ state - > { tocks } == $ state - > { max_tocks } ) { $ warning = $ color { red } ; }
elsif ( $ state - > { tocks } == $ state - > { max_tocks } - 1 ) { $ warning = $ color { yellow } ; }
else { $ warning = '' ; }
2020-02-15 23:38:32 +01:00
2024-11-09 08:29:52 +01:00
my $ remaining = $ self - > { tock_duration } * $ state - > { max_tocks } ;
$ remaining -= $ self - > { tock_duration } * ( $ state - > { tocks } - 1 ) ;
2020-02-15 23:38:32 +01:00
$ remaining = "(" . ( concise duration $ remaining ) . " remaining)" ;
2024-11-09 08:29:52 +01:00
my $ botnick = $ self - > { pbot } - > { registry } - > get_value ( 'irc' , 'botnick' ) ;
$ self - > send_message ( $ self - > { channel } , "$players: $warning$remaining Find the truth now via `/msg $botnick c <number>`!$color{reset}" ) ;
2020-02-15 23:38:32 +01:00
$ self - > send_message ( $ self - > { channel } , "$state->{current_choices_text}" ) ;
2018-01-29 06:53:40 +01:00
}
2024-11-09 08:29:52 +01:00
$ state - > { result } = 'wait' ;
2024-11-10 05:54:10 +01:00
return 0 ;
2020-02-15 23:38:32 +01:00
}
2023-04-14 02:01:23 +02:00
sub showlies ($self, $state) {
2020-02-15 23:38:32 +01:00
my @ liars ;
my $ player ;
my $ tock ;
2024-11-09 08:29:52 +01:00
if ( $ state - > { first_tock } ) { $ tock = 2 ; }
2020-02-15 23:38:32 +01:00
else { $ tock = 3 ; }
2019-04-28 09:21:15 +02:00
2020-02-15 23:38:32 +01:00
if ( $ state - > { ticks } % $ tock == 0 ) {
$ state - > { tocked } = 1 ;
2024-11-09 08:29:52 +01:00
2020-02-15 23:38:32 +01:00
while ( $ state - > { current_lie_player } < @ { $ state - > { players } } ) {
$ player = $ state - > { players } - > [ $ state - > { current_lie_player } ] ;
$ state - > { current_lie_player } + + ;
next if not exists $ player - > { truth } ;
2018-02-25 03:17:55 +01:00
2020-02-15 23:38:32 +01:00
foreach my $ liar ( @ { $ state - > { players } } ) {
next if $ liar - > { id } == $ player - > { id } ;
next if not exists $ liar - > { lie } ;
2018-02-25 03:17:55 +01:00
2024-11-09 08:29:52 +01:00
if ( $ liar - > { lie } eq $ player - > { truth } ) {
push @ liars , $ liar ;
}
2020-02-15 23:38:32 +01:00
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
last if @ liars ;
if ( $ player - > { truth } ne $ state - > { correct_answer } ) {
if ( $ self - > { metadata } - > get_data ( 'settings' , 'stats' ) ) {
my $ player_id = $ self - > { stats } - > get_player_id ( $ player - > { name } , $ self - > { channel } ) ;
my $ player_data = $ self - > { stats } - > get_player_data ( $ player_id ) ;
$ player_data - > { bad_guesses } + + ;
$ self - > { stats } - > update_player_data ( $ player_id , $ player_data ) ;
}
2024-11-10 05:54:10 +01:00
$ player - > { score } -= $ state - > { lie_points } ;
2020-02-15 23:38:32 +01:00
$ player - > { deceived } = $ player - > { truth } ;
2024-11-10 05:54:10 +01:00
$ self - > send_message ( $ self - > { channel } , "$player->{name} fell for my lie: \"$player->{truth}\". -$state->{lie_points} points!" ) ;
2024-11-09 08:29:52 +01:00
if ( $ state - > { current_lie_player } < @ { $ state - > { players } } ) {
$ state - > { result } = 'wait' ;
2024-11-10 05:54:10 +01:00
return 0 ;
2024-11-09 08:29:52 +01:00
} else {
$ state - > { result } = 'next' ;
2024-11-10 05:54:10 +01:00
return 1 ;
2024-11-09 08:29:52 +01:00
}
2020-02-15 23:38:32 +01:00
}
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
if ( @ liars ) {
my $ liars_text = '' ;
my $ liars_no_apostrophe = '' ;
my $ lie = $ player - > { truth } ;
my $ gains = @ liars == 1 ? 'gains' : 'gain' ;
my $ comma = '' ;
2018-01-30 06:54:52 +01:00
2020-02-15 23:38:32 +01:00
foreach my $ liar ( @ liars ) {
if ( $ self - > { metadata } - > get_data ( 'settings' , 'stats' ) ) {
my $ player_id = $ self - > { stats } - > get_player_id ( $ liar - > { name } , $ self - > { channel } ) ;
my $ player_data = $ self - > { stats } - > get_player_data ( $ player_id ) ;
$ player_data - > { players_deceived } + + ;
$ self - > { stats } - > update_player_data ( $ player_id , $ player_data ) ;
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
$ liars_text . = "$comma$liar->{name}'s" ;
$ liars_no_apostrophe . = "$comma$liar->{name}" ;
$ comma = ', ' ;
$ liar - > { score } += $ state - > { lie_points } ;
$ liar - > { good_lie } = 1 ;
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
if ( $ self - > { metadata } - > get_data ( 'settings' , 'stats' ) ) {
my $ player_id = $ self - > { stats } - > get_player_id ( $ player - > { name } , $ self - > { channel } ) ;
my $ player_data = $ self - > { stats } - > get_player_data ( $ player_id ) ;
$ player_data - > { bad_guesses } + + ;
$ self - > { stats } - > update_player_data ( $ player_id , $ player_data ) ;
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
$ self - > send_message ( $ self - > { channel } , "$player->{name} fell for $liars_text lie: \"$lie\". $liars_no_apostrophe $gains +$state->{lie_points} points!" ) ;
$ player - > { deceived } = $ lie ;
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
if ( $ state - > { current_lie_player } >= @ { $ state - > { players } } ) {
if ( @ liars ) { delete $ state - > { tick_drift } ; }
else { $ state - > { tick_drift } = $ tock - 1 ; }
2024-11-09 08:29:52 +01:00
$ state - > { result } = 'next' ;
2024-11-10 05:54:10 +01:00
return 1 ;
2020-02-15 23:38:32 +01:00
} else {
2024-11-09 08:29:52 +01:00
$ state - > { result } = 'wait' ;
2024-11-10 05:54:10 +01:00
return 0 ;
2018-01-29 06:53:40 +01:00
}
2018-02-20 09:16:22 +01:00
}
2018-01-29 06:53:40 +01:00
2024-11-09 08:29:52 +01:00
$ state - > { result } = 'wait' ;
2024-11-10 05:54:10 +01:00
return 0 ;
2020-02-15 23:38:32 +01:00
}
2018-02-20 09:16:22 +01:00
2023-04-14 02:01:23 +02:00
sub showtruth ($self, $state) {
2020-02-15 23:38:32 +01:00
if ( $ state - > { ticks } % 3 == 0 ) {
my $ player_id ;
my $ player_data ;
my $ players ;
my $ comma = '' ;
my $ count = 0 ;
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
foreach my $ player ( @ { $ state - > { players } } ) {
if ( $ self - > { metadata } - > get_data ( 'settings' , 'stats' ) ) {
$ player_id = $ self - > { stats } - > get_player_id ( $ player - > { name } , $ self - > { channel } ) ;
$ player_data = $ self - > { stats } - > get_player_data ( $ player_id ) ;
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
$ player_data - > { questions_played } + + ;
2024-11-09 08:29:52 +01:00
# update nick in stats database once per question (nick changes, etc)
$ player_data - > { nick } = $ player - > { name } ;
2020-02-15 23:38:32 +01:00
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
if ( exists $ player - > { deceived } ) {
2024-11-09 08:29:52 +01:00
if ( $ self - > { metadata } - > get_data ( 'settings' , 'stats' ) ) {
$ self - > { stats } - > update_player_data ( $ player_id , $ player_data ) ;
}
2020-02-15 23:38:32 +01:00
next ;
}
2018-02-20 09:16:22 +01:00
2020-02-15 23:38:32 +01:00
if ( exists $ player - > { truth } and $ player - > { truth } eq $ state - > { correct_answer } ) {
if ( $ self - > { metadata } - > get_data ( 'settings' , 'stats' ) ) {
$ player_data - > { good_guesses } + + ;
$ self - > { stats } - > update_player_data ( $ player_id , $ player_data ) ;
}
$ count + + ;
$ players . = "$comma$player->{name}" ;
$ comma = ', ' ;
$ player - > { score } += $ state - > { truth_points } ;
}
2018-02-01 07:11:26 +01:00
}
2024-11-09 08:29:52 +01:00
if ( $ count ) {
$ self - > send_message ( $ self - > { channel } , "$players got the correct answer: \"$state->{correct_answer}\". +$state->{truth_points} points!" ) ;
} else {
$ self - > send_message ( $ self - > { channel } , "Nobody found the truth! The answer was: $state->{correct_answer}" ) ;
}
2018-02-01 07:11:26 +01:00
2020-02-15 23:38:32 +01:00
$ self - > add_new_suggestions ( $ state ) ;
2024-11-09 08:29:52 +01:00
$ state - > { result } = 'next' ;
2024-11-10 05:54:10 +01:00
return 1 ;
2019-04-28 09:21:15 +02:00
} else {
2024-11-09 08:29:52 +01:00
$ state - > { result } = 'wait' ;
2024-11-10 05:54:10 +01:00
return 0 ;
2019-04-28 09:21:15 +02:00
}
2020-02-15 23:38:32 +01:00
}
2018-02-25 03:17:55 +01:00
2023-04-14 02:01:23 +02:00
sub reveallies ($self, $state) {
2020-02-15 23:38:32 +01:00
if ( $ state - > { ticks } % 3 == 0 ) {
my $ text = 'Revealing lies! ' ;
my $ comma = '' ;
foreach my $ player ( @ { $ state - > { players } } ) {
next if not exists $ player - > { lie } ;
$ text . = "$comma$player->{name}: $player->{lie}" ;
$ comma = '; ' ;
if ( $ player - > { good_lie } ) {
if ( $ self - > { metadata } - > get_data ( 'settings' , 'stats' ) ) {
my $ player_id = $ self - > { stats } - > get_player_id ( $ player - > { name } , $ self - > { channel } ) ;
my $ player_data = $ self - > { stats } - > get_player_data ( $ player_id ) ;
$ player_data - > { good_lies } + + ;
$ self - > { stats } - > update_player_data ( $ player_id , $ player_data ) ;
}
}
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
$ self - > send_message ( $ self - > { channel } , "$text" ) ;
2024-11-09 08:29:52 +01:00
$ state - > { result } = 'next' ;
2024-11-10 05:54:10 +01:00
return 1 ;
2018-02-20 09:16:22 +01:00
} else {
2024-11-09 08:29:52 +01:00
$ state - > { result } = 'wait' ;
2024-11-10 05:54:10 +01:00
return 0 ;
2018-02-20 09:16:22 +01:00
}
2018-01-29 06:53:40 +01:00
}
2023-04-14 02:01:23 +02:00
sub showscore ($self, $state) {
2024-11-09 08:29:52 +01:00
# skip showing scores if bonus round so finalscore state does it
2024-11-10 05:54:10 +01:00
if ( $ self - > { game } - > { round } >= $ self - > { game } - > { rounds } + 1 ) {
2024-11-09 08:29:52 +01:00
$ state - > { result } = 'next' ;
2024-11-10 05:54:10 +01:00
return 1 ;
}
# skip showing scores if no bonus round and final round/question
if ( $ self - > { metadata } - > get_data ( 'settings' , 'bonus_rounds' ) == 0
&& $ self - > { game } - > { round } >= $ self - > { game } - > { rounds }
&& $ self - > { game } - > { question } >= $ self - > { game } - > { questions } )
{
$ state - > { result } = 'next' ;
return 1 ;
2024-11-09 08:29:52 +01:00
}
2020-02-15 23:38:32 +01:00
if ( $ state - > { ticks } % 3 == 0 ) {
my $ text = '' ;
my $ comma = '' ;
foreach my $ player ( sort { $ b - > { score } <=> $ a - > { score } } @ { $ state - > { players } } ) {
$ text . = "$comma$player->{name}: " . $ self - > commify ( $ player - > { score } ) ;
$ comma = '; ' ;
2019-04-28 09:21:15 +02:00
}
2019-04-24 12:55:48 +02:00
2024-11-09 08:29:52 +01:00
$ text = 'none' if not length $ text ;
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
$ self - > send_message ( $ self - > { channel } , "$color{green}Scores:$color{reset} $text" ) ;
2024-11-09 08:29:52 +01:00
$ state - > { result } = 'next' ;
2024-11-10 05:54:10 +01:00
return 1 ;
2018-01-30 06:54:52 +01:00
} else {
2024-11-09 08:29:52 +01:00
$ state - > { result } = 'wait' ;
2024-11-10 05:54:10 +01:00
return 0 ;
2018-01-30 06:54:52 +01:00
}
2020-02-15 23:38:32 +01:00
}
2018-01-29 06:53:40 +01:00
2024-11-09 08:29:52 +01:00
sub finalscore ($self, $state) {
2020-02-15 23:38:32 +01:00
if ( $ state - > { newstate } ) {
my $ player_id ;
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
my $ player_data ;
2024-11-09 08:29:52 +01:00
my $ mentions = '' ;
my $ text = '' ;
my $ comma = '' ;
2020-02-15 23:38:32 +01:00
my $ i = @ { $ state - > { players } } ;
2019-04-24 12:55:48 +02:00
2020-02-15 23:38:32 +01:00
$ state - > { finalscores } = [] ;
2024-11-09 08:29:52 +01:00
2020-02-15 23:38:32 +01:00
foreach my $ player ( sort { $ a - > { score } <=> $ b - > { score } } @ { $ state - > { players } } ) {
if ( $ self - > { metadata } - > get_data ( 'settings' , 'stats' ) ) {
$ player_id = $ self - > { stats } - > get_player_id ( $ player - > { name } , $ self - > { channel } ) ;
$ player_data = $ self - > { stats } - > get_player_data ( $ player_id ) ;
2018-01-30 06:54:52 +01:00
2020-02-15 23:38:32 +01:00
$ player_data - > { games_played } + + ;
$ player_data - > { avg_score } *= $ player_data - > { games_played } - 1 ;
$ player_data - > { avg_score } += $ player - > { score } ;
$ player_data - > { avg_score } /= $ player_data - > { games_played } ;
$ player_data - > { low_score } = $ player - > { score } if $ player_data - > { low_score } == 0 ;
2018-01-30 06:54:52 +01:00
2024-11-09 08:29:52 +01:00
if ( $ player - > { score } > $ player_data - > { high_score } ) {
$ player_data - > { high_score } = $ player - > { score } ;
} elsif ( $ player - > { score } < $ player_data - > { low_score } ) {
$ player_data - > { low_score } = $ player - > { score } ;
}
2020-02-15 23:38:32 +01:00
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
if ( $ i >= 4 ) {
$ mentions = "$player->{name}: " . $ self - > commify ( $ player - > { score } ) . "$comma$mentions" ;
$ comma = "; " ;
2024-11-09 08:29:52 +01:00
if ( $ i == 4 ) {
$ mentions = "Honorable mentions: $mentions" ;
}
if ( $ self - > { metadata } - > get_data ( 'settings' , 'stats' ) ) {
$ self - > { stats } - > update_player_data ( $ player_id , $ player_data ) ;
}
2020-02-15 23:38:32 +01:00
$ i - - ;
next ;
} elsif ( $ i == 3 ) {
$ player_data - > { times_third } + + ;
$ text = sprintf ( "%15s%-13s%7s" , "Third place: " , $ player - > { name } , $ self - > commify ( $ player - > { score } ) ) ;
} elsif ( $ i == 2 ) {
$ player_data - > { times_second } + + ;
$ text = sprintf ( "%15s%-13s%7s" , "Second place: " , $ player - > { name } , $ self - > commify ( $ player - > { score } ) ) ;
} elsif ( $ i == 1 ) {
$ player_data - > { times_first } + + ;
$ text = sprintf ( "%15s%-13s%7s" , "WINNER: " , $ player - > { name } , $ self - > commify ( $ player - > { score } ) ) ;
}
2018-01-29 06:53:40 +01:00
2024-11-09 08:29:52 +01:00
if ( $ self - > { metadata } - > get_data ( 'settings' , 'stats' ) ) {
$ self - > { stats } - > update_player_data ( $ player_id , $ player_data ) ;
}
2020-02-15 23:38:32 +01:00
push @ { $ state - > { finalscores } } , $ text ;
$ i - - ;
}
push @ { $ state - > { finalscores } } , $ mentions if length $ mentions ;
2018-01-30 06:54:52 +01:00
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
my $ tock ;
2024-11-09 08:29:52 +01:00
if ( $ state - > { first_tock } ) {
$ tock = 2 ;
} else {
$ tock = 3 ;
}
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
if ( $ state - > { ticks } % $ tock == 0 ) {
$ state - > { tocked } = 1 ;
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
if ( not @ { $ state - > { finalscores } } ) {
$ self - > send_message ( $ self - > { channel } , "$color{green}Final scores: $color{reset}none" ) ;
2024-11-10 05:54:10 +01:00
$ state - > { result } = 'next' ;
return 1 ;
2020-02-15 23:38:32 +01:00
}
if ( $ state - > { first_tock } ) {
$ self - > send_message ( $ self - > { channel } , "$color{green}Final scores:$color{reset}" ) ;
2024-11-10 05:54:10 +01:00
$ state - > { result } = 'wait' ;
return 0 ;
2020-02-15 23:38:32 +01:00
}
my $ text = shift @ { $ state - > { finalscores } } ;
$ self - > send_message ( $ self - > { channel } , "$text" ) ;
2024-11-09 08:29:52 +01:00
if ( not @ { $ state - > { finalscores } } ) {
$ state - > { result } = 'next' ;
2024-11-10 05:54:10 +01:00
return 1 ;
2020-02-15 23:38:32 +01:00
}
}
2024-11-10 05:54:10 +01:00
$ state - > { result } = 'wait' ;
return 0 ;
2018-01-29 06:53:40 +01:00
}
2023-04-14 02:01:23 +02:00
sub gameover ($self, $state) {
2020-02-15 23:38:32 +01:00
if ( $ state - > { ticks } % 3 == 0 ) {
2024-11-09 08:29:52 +01:00
$ self - > send_message ( $ self - > { channel } , 'Game over!' ) ;
2018-01-29 06:53:40 +01:00
2020-02-15 23:38:32 +01:00
my $ players = $ state - > { players } ;
foreach my $ player ( @$ players ) {
$ player - > { ready } = 0 ;
$ player - > { missedinputs } = 0 ;
}
2018-02-04 05:42:27 +01:00
2020-02-15 23:38:32 +01:00
# save updated seen_timestamps
$ self - > save_questions ;
2019-05-03 08:13:27 +02:00
2024-11-10 05:54:10 +01:00
# reset some state data
delete $ state - > { random_category } ;
$ state - > { tocks } = 0 ;
$ state - > { bonus_round } = 0 ;
2020-02-15 23:38:32 +01:00
$ state - > { result } = 'next' ;
2024-11-10 05:54:10 +01:00
return 1 ;
2020-02-15 23:38:32 +01:00
} else {
$ state - > { result } = 'wait' ;
2024-11-10 05:54:10 +01:00
return 0 ;
2020-02-15 23:38:32 +01:00
}
2018-01-29 06:53:40 +01:00
}
1 ;