amongrass/main.c
2024-05-30 23:22:23 +00:00

360 lines
7.5 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <irc.h>
#ifdef ENABLE_SSL
#include <openssl/err.h>
#endif
enum {
CREWMATE,
IMPOSTER,
DIED,
COUNT
};
enum {
LOBBY,
GAME,
DISCUSSION
};
typedef struct {
char *nick;
int num;
int type;
int admin;
int map;
int votes;
} CLIENT;
CLIENT *players;
size_t now_players;
size_t size_players = 10;
time_t timer;
time_t voting_timer;
int current_mode;
int GetByNick(const char *nick, size_t *ret) {
for (size_t i = 0; i < now_players; i++)
if (!strcmp(nick, players[i].nick)) {
*ret = i;
return 0;
}
return 1;
}
void sort(size_t ptr){
for (size_t i = ptr; i < now_players; i++)
if (i + 1 <= now_players)
players[i] = players[i + 1];
}
void clear(void) {
for (size_t i = 0; i < size_players; i++)
if (players[i].nick)
free(players[i].nick);
memset(players, 0, sizeof(CLIENT) * size_players);
current_mode = LOBBY;
now_players = 0;
}
void broadcast(IRCC_client *irc, const char *ign, const char *msg) {
for (size_t i = 0; i < now_players; i++)
if (strcmp(ign, players[i].nick))
IRCC_send(irc, players[i].nick, msg);
}
int test_player(IRCC_client *irc, const char *nick, size_t *res) {
size_t ret = 0;
if (GetByNick(nick, &ret)) {
IRCC_send(irc, nick, "Firstly join!");
return 1;
}
*res = ret;
return 0;
}
void player_quit(IRCC_client *irc, char *buf, size_t size, const char *nick) {
size_t ret = 0;
if (test_player(irc, nick, &ret))
return;
snprintf(buf, size, "Player [%d] left", players[ret].num);
broadcast(irc, nick, buf);
if (players[ret].admin) {
CLIENT *client;
if (ret + 1 < now_players)
client = &players[ret + 1];
else
client = &players[ret - 1];
client->admin = 1;
snprintf(buf, size, "New admin is [%d]", client->num);
broadcast(irc, nick, buf);
}
if (players[ret].type == IMPOSTER) {
snprintf(buf, size, "[%d] was imposter. Game over!", players[ret].num);
broadcast(irc, nick, buf);
clear();
return;
}
free(players[ret].nick);
sort(ret);
now_players--;
}
int gameloop(IRCC_client *irc) {
int irc_ret = IRCC_recv(irc);
if (irc_ret == IRCC_DISCONNECTED)
return 1;
irc_ret = IRCC_parse(irc);
if (irc->irc_msg == NULL || irc->irc_nick == NULL || irc->irc_channel == NULL)
return 0;
/* Common buffer */
char buf[512];
char *nick = strdup(irc->irc_nick);
if (nick == NULL)
return 0;
if (irc_ret == IRCC_PRIVMSG) {
if (!strncmp(irc->irc_msg, "!join", 5)) {
if (current_mode != LOBBY) {
IRCC_send(irc, nick, "Game started");
goto END;
}
/* Check player */
if (now_players == size_players) {
IRCC_send(irc, nick, "Sorry, max players");
goto END;
}
size_t ret = 0;
if (!GetByNick(nick, &ret)) {
IRCC_send(irc, nick, "You already joined");
goto END;
}
/* Register */
players[now_players].nick = strdup(nick);
if (players[now_players].nick == NULL)
goto END;
/* In game nickname */
players[now_players].num = rand();
/* Give admin permission */
int admin_flag = 0;
if (now_players == 0)
admin_flag = 1;
players[now_players].admin = admin_flag;
/* Other */
players[now_players].map = 0;
players[now_players].type = 0;
/* Inform all */
snprintf(buf, sizeof(buf), "New player [%d]! Total: %zu/%zu", players[now_players].num, now_players + 1, size_players);
broadcast(irc, nick, buf);
snprintf(buf, sizeof(buf), "You joined with id [%d]", players[now_players].num);
IRCC_send(irc, nick, buf);
now_players++;
}
else if (!strncmp(irc->irc_msg, "!say", 4)) {
if (current_mode == GAME)
goto END;
size_t ret = 0;
if (test_player(irc, nick, &ret))
goto END;
if (players[ret].type == DIED) {
IRCC_send(irc, nick, "You died! You cant send messages");
goto END;
}
snprintf(buf, sizeof(buf), "[%d]:%s", players[ret].num, irc->irc_msg + 4);
broadcast(irc, nick, buf);
}
else if (!strncmp(irc->irc_msg, "!exit", 5)) {
IRCC_send(irc, nick, "Goodbye!");
player_quit(irc, buf, sizeof(buf), nick);
}
else if (!strncmp(irc->irc_msg, "!status", 7)) {
size_t ret = 0;
if (test_player(irc, nick, &ret))
goto END;
snprintf(buf, sizeof(buf), "Status: Your id [%d] | Players [%zu/%zu]", players[ret].num, now_players, size_players);
IRCC_send(irc, nick, buf);
}
else if (!strncmp(irc->irc_msg, "!start", 6)) {
if (current_mode != LOBBY) {
IRCC_send(irc, nick, "Game already started");
goto END;
}
size_t ret = 0;
if (GetByNick(nick, &ret)) {
IRCC_send(irc, nick, "You are not admin");
goto END;
}
current_mode = GAME;
timer = time(NULL);
for (size_t i = 0; i < now_players; i++)
players[i].type = CREWMATE;
size_t im = rand() % now_players;
players[im].type = IMPOSTER;
/* Inform all */
broadcast(irc, players[im].nick, "You crewmate");
IRCC_send(irc, players[im].nick, "You imposter");
}
else if (!strncmp(irc->irc_msg, "!emergency", 10)) {
size_t ret = 0;
if (test_player(irc, nick, &ret))
goto END;
current_mode = DISCUSSION;
snprintf(buf, sizeof(buf), "Player [%d] pressed emergency button! You have 60 second to vote or skip", players[ret].num);
voting_timer = time(NULL);
}
else if (!strncmp(irc->irc_msg, "!help", 5))
IRCC_send(irc, nick, "[!start] [!help] [!say MSG] [!join] [!exit] [!status] InGame: [!kill] [!go NUM] [!emergency] [!vote NUM] [!skip]");
}
else if (irc_ret == IRCC_QUIT)
player_quit(irc, buf, sizeof(buf), nick);
END:
free(nick);
return 0;
}
int main(int argc, char **argv) {
char *server = "127.0.0.1";
char *nick = "amongrass";
unsigned int port = 6667;
int usessl = 0;
int opt;
while ((opt = getopt(argc, argv, "h:n:p:m:s")) != -1) {
switch (opt) {
case 'm':
sscanf(optarg, "%zu", &size_players);
if (size_players < 1 || size_players > 100) {
fprintf(stderr, "amongrass: failed to set players count\n");
return 1;
}
break;
case 'h':
server = optarg;
break;
case 'p':
port = atoi(optarg);
break;
case 'n':
nick = optarg;
break;
case 's':
usessl = 1;
break;
default:
printf("./amongrass [snpm]\n\t-h IP Server ip Default: %s\n\t-n NICK Bot nickname Default: %s\n\t-p PORT Server port Default: %d\n\t-m N Max players in game Default: %zu\n\t-s Use SSL Default: false\n", server, nick, port, size_players);
return 0;
}
}
players = malloc(sizeof(CLIENT) * size_players);
if (players == NULL) {
fprintf(stderr, "amongrass: malloc failed: %s", strerror(errno));
return 1;
}
IRCC_client irc;
if (IRCC_connect(&irc, server, port) == IRCC_ERROR) {
fprintf(stderr, "amongrass: failed connect to %s: %s\n", server, strerror(errno));
goto EXIT_MAIN;
}
if (usessl) {
#ifdef ENABLE_SSL
if (IRCC_initssl(&irc) == IRCC_ERROR) {
fprintf(stderr, "amongrass: irc_ssl: %s\n", ERR_error_string(ERR_get_error(), NULL));
goto EXIT_MAIN;
}
#endif
}
if (IRCC_register(&irc, nick) == IRCC_DISCONNECTED) {
fprintf(stderr, "amongrass: irc: disconnected\n");
goto EXIT_MAIN;
}
timer = time(NULL);
current_mode = LOBBY;
srand(getpid());
fprintf(stderr, "amongrass: Successful connected\namongrass: Nickname: %s\n", nick);
while (1) {
if (time(NULL) - timer >= 60) {
broadcast(&irc, "", "Time is over!");
clear();
}
if (gameloop(&irc))
break;
}
IRCC_close(&irc);
for (size_t i = 0; i < size_players; i++)
if (players[i].nick)
free(players[i].nick);
EXIT_MAIN:
free(players);
return 1;
}