+ Added phase handling and player termination + Fixed an issue with some commands not saving game data + Added game_status command + Updated readme with commands and new features + clippy + fmt
395 lines
11 KiB
Rust
395 lines
11 KiB
Rust
use serenity::framework::standard::macros::{command, group};
|
|
use serenity::framework::standard::{Args, CommandResult};
|
|
use serenity::framework::StandardFramework;
|
|
use serenity::model::id::ChannelId;
|
|
use serenity::model::prelude::{Message, UserId};
|
|
use serenity::prelude::Context;
|
|
use serenity::utils::MessageBuilder;
|
|
|
|
use crate::data::{GlobalData, MessageSource, Phase};
|
|
use crate::helper;
|
|
use crate::helper::{
|
|
build_system_message, clear_game_state, get_phase_end_timestamp, print_game_status,
|
|
save_game_state, send_msg_to_player_channels,
|
|
};
|
|
|
|
#[group]
|
|
#[commands(start, say, end, broadcast, next_phase, terminate, add_time)]
|
|
struct Host;
|
|
|
|
#[command]
|
|
#[only_in(guilds)]
|
|
#[allowed_roles("wolfx host")]
|
|
async fn start(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
|
msg.channel_id.say(&ctx.http, "Starting game").await?;
|
|
|
|
let mut data = ctx.data.write().await;
|
|
let global_data = data.get_mut::<GlobalData>().unwrap();
|
|
let guild = msg.guild(&ctx.cache).await.unwrap();
|
|
|
|
let mut global_data = global_data.lock().await;
|
|
|
|
clear_game_state(&mut global_data).unwrap();
|
|
|
|
let duration = match args.single::<u64>() {
|
|
Ok(d) => d,
|
|
Err(_) => {
|
|
msg.reply(&ctx.http, "Error parsing phase duration!")
|
|
.await
|
|
.unwrap();
|
|
return Ok(());
|
|
}
|
|
};
|
|
|
|
global_data.game_state.phase_end_time = get_phase_end_timestamp(duration);
|
|
|
|
for player in args.iter::<u64>().flatten() {
|
|
if let Some(discord_user) = guild.members.get(&UserId::from(player)) {
|
|
helper::add_user_to_game(ctx, &guild, &mut global_data, discord_user).await?;
|
|
} else {
|
|
msg.reply(
|
|
&ctx.http,
|
|
format!("User {} is invalid or not in this server!", player),
|
|
)
|
|
.await?;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
save_game_state(&global_data).unwrap();
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
#[only_in(guilds)]
|
|
#[allowed_roles("wolfx host")]
|
|
async fn end(ctx: &Context, msg: &Message, mut _args: Args) -> CommandResult {
|
|
let mut data = ctx.data.write().await;
|
|
let global_data = data.get_mut::<GlobalData>().unwrap();
|
|
let guild = msg.guild(&ctx.cache).await.unwrap();
|
|
|
|
let mut global_data = global_data.lock().await;
|
|
|
|
for player_data in &global_data.game_state.player_data {
|
|
let channel = guild
|
|
.channels
|
|
.get(&ChannelId::from(player_data.channel))
|
|
.unwrap();
|
|
|
|
channel.delete(&ctx.http).await?;
|
|
}
|
|
|
|
clear_game_state(&mut global_data).unwrap();
|
|
|
|
msg.reply(&ctx.http, "Game ended!").await.unwrap();
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
#[only_in(guilds)]
|
|
#[allowed_roles("wolfx host")]
|
|
async fn say(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|
let data = ctx.data.read().await;
|
|
let global_data = data.get::<GlobalData>().unwrap();
|
|
let guild = msg.guild(&ctx.cache).await.unwrap();
|
|
|
|
let global_data = global_data.lock().await;
|
|
|
|
let msg = format!("**wOxlf **> {}", args.rest());
|
|
|
|
send_msg_to_player_channels(ctx, &guild, &global_data, MessageSource::Host, &msg, false).await;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
#[only_in(guilds)]
|
|
#[allowed_roles("wolfx host")]
|
|
async fn broadcast(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|
let data = ctx.data.read().await;
|
|
let global_data = data.get::<GlobalData>().unwrap();
|
|
let guild = msg.guild(&ctx.cache).await.unwrap();
|
|
|
|
let global_data = global_data.lock().await;
|
|
|
|
let msg = build_system_message(args.rest());
|
|
|
|
send_msg_to_player_channels(ctx, &guild, &global_data, MessageSource::Host, &msg, true).await;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
#[only_in(guilds)]
|
|
#[allowed_roles("wolfx host")]
|
|
async fn next_phase(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
|
let mut data = ctx.data.write().await;
|
|
let global_data = data.get_mut::<GlobalData>().unwrap();
|
|
let guild = msg.guild(&ctx.cache).await.unwrap();
|
|
|
|
let mut global_data = global_data.lock().await;
|
|
|
|
let duration = match args.single::<u64>() {
|
|
Ok(d) => d,
|
|
Err(_) => {
|
|
msg.reply(&ctx.http, "Error parsing phase duration!")
|
|
.await
|
|
.unwrap();
|
|
return Ok(());
|
|
}
|
|
};
|
|
|
|
global_data.game_state.next_phase();
|
|
|
|
global_data.game_state.phase_end_time = get_phase_end_timestamp(duration);
|
|
|
|
let broadcast = MessageBuilder::new()
|
|
.push_line(args.rest())
|
|
.push_line("")
|
|
.push(print_game_status(&global_data.game_state))
|
|
.build();
|
|
|
|
let broadcast = build_system_message(&broadcast);
|
|
|
|
send_msg_to_player_channels(
|
|
ctx,
|
|
&guild,
|
|
&global_data,
|
|
MessageSource::Host,
|
|
&broadcast,
|
|
true,
|
|
)
|
|
.await;
|
|
|
|
if global_data.game_state.current_phase == Phase::Day {
|
|
let vote_channel = guild
|
|
.channels
|
|
.get(&ChannelId::from(global_data.cfg.vote_channel))
|
|
.unwrap();
|
|
vote_channel
|
|
.send_message(&ctx.http, |m| {
|
|
m.content(format!(
|
|
"**DAY {} VOTES:**",
|
|
global_data.game_state.phase_number
|
|
))
|
|
})
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
msg.reply(
|
|
&ctx.http,
|
|
format!(
|
|
"Phase has been cycled to {}.",
|
|
&global_data.game_state.current_phase
|
|
),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
save_game_state(&global_data).unwrap();
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
#[only_in(guilds)]
|
|
#[allowed_roles("wolfx host")]
|
|
async fn terminate(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|
let mut data = ctx.data.write().await;
|
|
let global_data = data.get_mut::<GlobalData>().unwrap();
|
|
let guild = msg.guild(&ctx.cache).await.unwrap();
|
|
|
|
let mut global_data = global_data.lock().await;
|
|
|
|
let target = args.rest().to_lowercase();
|
|
let index = global_data
|
|
.game_state
|
|
.player_data
|
|
.iter()
|
|
.position(|p| p.codename.to_lowercase() == target);
|
|
|
|
if let Some(index) = index {
|
|
let player = global_data.game_state.player_data.remove(index);
|
|
|
|
let player_channel = guild
|
|
.channels
|
|
.get(&ChannelId::from(player.channel))
|
|
.unwrap();
|
|
|
|
player_channel.delete(&ctx.http).await.unwrap();
|
|
|
|
msg.reply(
|
|
&ctx.http,
|
|
format!("{} has been terminated.", player.codename),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
} else {
|
|
msg.reply(&ctx.http, "No subject found with that codename.")
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
save_game_state(&global_data).unwrap();
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
#[only_in(guilds)]
|
|
#[allowed_roles("wolfx host")]
|
|
async fn add_time(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
|
let mut data = ctx.data.write().await;
|
|
let global_data = data.get_mut::<GlobalData>().unwrap();
|
|
let guild = msg.guild(&ctx.cache).await.unwrap();
|
|
|
|
let mut global_data = global_data.lock().await;
|
|
|
|
global_data.game_state.next_phase();
|
|
|
|
let duration = match args.single::<u64>() {
|
|
Ok(d) => d,
|
|
Err(_) => {
|
|
msg.reply(&ctx.http, "Error parsing phase duration!")
|
|
.await
|
|
.unwrap();
|
|
return Ok(());
|
|
}
|
|
};
|
|
|
|
global_data.game_state.add_time_to_phase(duration);
|
|
|
|
let broadcast = MessageBuilder::new()
|
|
.push_line("EXPERIMENT PHASE HAS BEEN EXTENDED!!!")
|
|
.push_line("")
|
|
.push(print_game_status(&global_data.game_state))
|
|
.build();
|
|
|
|
let broadcast = build_system_message(&broadcast);
|
|
|
|
send_msg_to_player_channels(
|
|
ctx,
|
|
&guild,
|
|
&global_data,
|
|
MessageSource::Host,
|
|
&broadcast,
|
|
true,
|
|
)
|
|
.await;
|
|
|
|
msg.reply(&ctx.http, "Phase has been updated")
|
|
.await
|
|
.unwrap();
|
|
|
|
save_game_state(&global_data).unwrap();
|
|
Ok(())
|
|
}
|
|
|
|
#[group]
|
|
#[commands(vote, status)]
|
|
struct Player;
|
|
|
|
#[command]
|
|
#[only_in(guilds)]
|
|
async fn vote(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
|
let mut data = ctx.data.write().await;
|
|
let global_data = data.get_mut::<GlobalData>().unwrap();
|
|
let guild = msg.guild(&ctx.cache).await.unwrap();
|
|
|
|
let mut global_data = global_data.lock().await;
|
|
|
|
if global_data.game_state.current_phase != Phase::Day {
|
|
msg.reply(
|
|
&ctx.http,
|
|
"You can only select subject for termination during the day!",
|
|
)
|
|
.await
|
|
.unwrap();
|
|
return Ok(());
|
|
}
|
|
|
|
if global_data
|
|
.game_state
|
|
.get_player_from_channel(msg.channel_id.0)
|
|
.is_some()
|
|
{
|
|
let target_player = global_data.game_state.get_player_by_codename(args.rest());
|
|
|
|
if let Some(target_player) = target_player {
|
|
let vote_channel = guild
|
|
.channels
|
|
.get(&ChannelId::from(global_data.cfg.vote_channel))
|
|
.unwrap();
|
|
|
|
let player_data = global_data
|
|
.game_state
|
|
.get_player_from_channel_mut(msg.channel_id.0)
|
|
.unwrap();
|
|
player_data.vote_target = Some(target_player.channel);
|
|
|
|
vote_channel
|
|
.send_message(&ctx.http, |m| {
|
|
m.content(format!(
|
|
"{} has selected {} for termination",
|
|
&player_data.codename, target_player.codename
|
|
))
|
|
})
|
|
.await
|
|
.unwrap();
|
|
} else {
|
|
msg.reply(&ctx.http, "Subject not found!").await.unwrap();
|
|
}
|
|
} else {
|
|
msg.reply(
|
|
&ctx.http,
|
|
"This command needs to be run in a game channel, goober",
|
|
)
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
save_game_state(&global_data).unwrap();
|
|
Ok(())
|
|
}
|
|
|
|
#[command]
|
|
#[only_in(guilds)]
|
|
async fn status(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
|
|
let data = ctx.data.read().await;
|
|
let global_data = data.get::<GlobalData>().unwrap();
|
|
|
|
let global_data = global_data.lock().await;
|
|
|
|
let mut msg_builder = MessageBuilder::new();
|
|
|
|
msg_builder.push(print_game_status(&global_data.game_state));
|
|
|
|
if global_data.game_state.current_phase == Phase::Day {
|
|
msg_builder.push_line("");
|
|
|
|
let vote_tallies = global_data.game_state.get_vote_tallies();
|
|
|
|
if vote_tallies.is_empty() {
|
|
msg_builder.push_line("NO TERMINATION VOTES HAVE BEEN CAST");
|
|
} else {
|
|
msg_builder.push_line("TERMINATION VOTE TALLIES:");
|
|
for (player, tally) in global_data.game_state.get_vote_tallies() {
|
|
msg_builder.push_line(format!("{}: {}", player, tally));
|
|
}
|
|
}
|
|
}
|
|
|
|
msg.reply(&ctx.http, msg_builder.build()).await.unwrap();
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn command_framework() -> StandardFramework {
|
|
StandardFramework::new()
|
|
.configure(|c| c.prefix("!"))
|
|
.group(&HOST_GROUP)
|
|
.group(&PLAYER_GROUP)
|
|
}
|