pidgin/purple-plugin-pack

Add the turtles target to keep the foot clan at bay
default tip
13 months ago, Gary Kramlich
63ad7e4f10b4
Add the turtles target to keep the foot clan at bay

Testing Done:
Ran `ninja turtles` and verified it worked correctly.

Reviewed at https://reviews.imfreedom.org/r/2409/
/*
* Adds a command to roll an arbitrary number of dice with an arbitrary
* number of sides
* Copyright (C) 2005-2008 Gary Kramlich <grim@reaperworld.com>
* Copyright (C) 2007 Lucas <reilithion@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
*/
/* If you can't figure out what this line is for, DON'T TOUCH IT. */
#include "../common/pp_internal.h"
#include <time.h>
#include <stdlib.h>
#include <cmds.h>
#include <conversation.h>
#include <debug.h>
#include <plugin.h>
#define DEFAULT_DICE 2
#define DEFAULT_SIDES 6
#define BOUNDS_CHECK(var, min, min_def, max, max_def) { \
if((var) < (min)) \
(var) = (min_def); \
else if((var) > (max)) \
(var) = (max_def); \
}
#define ROUND(val) ((gdouble)(val) + 0.5f)
static PurpleCmdId dice_cmd_id = 0;
static gchar *
old_school_roll(gint dice, gint sides) {
GString *str = g_string_new("");
gchar *ret = NULL;
gint c = 0, v = 0;
BOUNDS_CHECK(dice, 1, 2, 15, 15);
BOUNDS_CHECK(sides, 2, 2, 999, 999);
g_string_append_printf(str, "%d %d-sided %s:",
dice, sides,
(dice == 1) ? "die" : "dice");
for(c = 0; c < dice; c++) {
v = rand() % sides + 1;
g_string_append_printf(str, " %d", v);
}
ret = str->str;
g_string_free(str, FALSE);
return ret;
}
static inline gboolean
is_dice_notation(const gchar *str) {
return (g_utf8_strchr(str, -1, 'd') != NULL);
}
static gchar *
dice_notation_roll_helper(const gchar *dn, gint *value) {
GString *str = g_string_new("");
gchar *ret = NULL, *ms = NULL;
gchar op = '\0';
gint dice = 0, sides = 0, i = 0, t = 0, v = 0;
gdouble multiplier = 1.0;
if(!dn || *dn == '\0')
return NULL;
/* at this point, all we have is +/- number for our bonus, so we add it to
* our value
*/
if(!is_dice_notation(dn)) {
gint bonus = atoi(dn);
*value += bonus;
/* the + makes sure we always have a + or - */
g_string_append_printf(str, "%s %d",
(bonus < 0) ? "-" : "+",
ABS(bonus));
ret = str->str;
g_string_free(str, FALSE);
return ret;
}
/**************************************************************************
* Process our block
*************************************************************************/
purple_debug_info("dice", "processing '%s'\n", dn);
/* get the number of dice */
dice = atoi(dn);
BOUNDS_CHECK(dice, 1, 1, 999, 999);
/* find and move to the character after the d */
dn = g_utf8_strchr(dn, -1, 'd');
dn++;
/* get the number of sides */
sides = atoi(dn);
BOUNDS_CHECK(sides, 2, 2, 999, 999);
/* i've struggled with a better way to determine the next operator, i've
* opted for this.
*/
for(t = sides; t > 0; t /= 10) {
dn++;
purple_debug_info("dice", "looking for the next operator: %s\n", dn);
}
purple_debug_info("dice", "next operator: %s\n", dn);
/* check if we're multiplying or dividing this block */
if(*dn == 'x' || *dn == '/') {
op = *dn;
dn++;
multiplier = v = atof(dn);
ms = g_strdup_printf("%d", (gint)multiplier);
/* move past our multiplier */
for(t = v; t > 0; t /= 10) {
purple_debug_info("dice", "moving past the multiplier: %s\n", dn);
dn++;
}
if(op == '/')
multiplier = 1 / multiplier;
}
purple_debug_info("dice", "d=%d;s=%d;m=%f;\n", dice, sides, multiplier);
/* calculate and output our block */
g_string_append_printf(str, " (");
for(i = 0; i < dice; i++) {
t = rand() % sides + 1;
v = ROUND(t * multiplier);
g_string_append_printf(str, "%s%d", (i > 0) ? " " : "", t);
purple_debug_info("dice", "die %d: %d(%d)\n", i, v, t);
*value += v;
}
g_string_append_printf(str, ")");
/* if we have a multiplier, we need to output it as well */
if(multiplier != 1.0)
g_string_append_printf(str, "%c(%s)", op, ms);
/* free our string of the multiplier */
g_free(ms);
purple_debug_info("dice", "value=%d;str=%s\n", *value, str->str);
/* we have more in our string, recurse! */
if(*dn != '\0') {
gchar *s = dice_notation_roll_helper(dn, value);
if(s)
str = g_string_append(str, s);
g_free(s);
}
ret = str->str;
g_string_free(str, FALSE);
return ret;
}
static gchar *
dice_notation_roll(const gchar *dn) {
GString *str = g_string_new("");
gchar *ret = NULL, *normalized = NULL;
gint value = 0;
g_string_append_printf(str, "%s:", dn);
/* normalize the input and process it */
normalized = g_utf8_strdown(dn, -1);
g_string_append_printf(str, "%s",
dice_notation_roll_helper(normalized, &value));
g_free(normalized);
g_string_append_printf(str, " = %d", value);
ret = str->str;
g_string_free(str, FALSE);
return ret;
}
static PurpleCmdRet
roll(PurpleConversation *conv, const gchar *cmd, gchar **args, gchar *error,
void *data)
{
PurpleCmdStatus ret;
gchar *str = NULL, *newcmd = NULL;
if(!args[0]) {
str = old_school_roll(DEFAULT_DICE, DEFAULT_SIDES);
} else {
if(is_dice_notation(args[0])) {
str = dice_notation_roll(args[0]);
} else {
gint dice, sides;
dice = atoi(args[0]);
sides = (args[1]) ? atoi(args[1]) : DEFAULT_SIDES;
str = old_school_roll(dice, sides);
}
}
#if 0
i = 1; /* Abuse that iterator! We're saying "We think this is dice notation!" */
splitted = g_strsplit(args[0], "d", 2); /* Split the description into two parts: (1)d(20+5); discard the 'd'. */
dice = atoi(splitted[0]); /* We should have the number of dice easily now. */
if(g_strstr_len(splitted[1], -1, "+") != NULL) /* If our second half contained a '+' (20+5) */
{
resplitted = g_strsplit(splitted[1], "+", 2); /* Split again: (20)+(5); discard the '+'. */
sides = atoi(resplitted[0]); /* Number of sides on the left. */
bonus += atoi(resplitted[1]); /* Bonus on the right. */
g_strfreev(resplitted); /* Free memory from the split. */
}
else if(g_strstr_len(splitted[1], -1, "-") != NULL) /* If our second half contained a '-' (20-3) */
{
resplitted = g_strsplit(splitted[1], "-", 2); /* Split again: (20)-(3); discard the '-'. */
sides = atoi(resplitted[0]); /* Number of sides on the left. */
bonus -= atoi(resplitted[1]); /* Penalty on the right. */
g_strfreev(resplitted); /* Free memory from the split. */
}
else /* There was neither a '+' nor a '-' in the second half. */
sides = atoi(splitted[1]); /* We're assuming it's just a number, then. Number of sides. */
g_strfreev(splitted); /* Free the original split. */
}
}
if(args[1] && i == 0) /* If there was a second argument, and we care about it (not dice notation) */
sides = atoi(args[1]); /* Grab it and make it the number of sides the dice have. */
str = g_string_new("");
if(i) /* Show the output in dice notation format. */
{
g_string_append_printf(str, "%dd%d", dice, sides); /* For example, 1d20 */
if(bonus > 0)
g_string_append_printf(str, "+%d", bonus); /* 1d20+5 */
else if(bonus < 0)
g_string_append_printf(str, "%d", bonus); /* 1d20-3 (saying "-%d" would be redundant, since the '-' gets output with bonus automatically) */
g_string_append_printf(str, ":"); /* Final colon. 1d20-4: */
}
for(i = 0; i < dice; i++) /* For each die... */
{
roll = rand() % sides + 1; /* Roll, and add bonus. */
accumulator += roll; /* Accumulate our rolls */
g_string_append_printf(str, " %d", roll); /* Append the result of our roll to our output string. */
}
if(bonus != 0) /* If we had a bonus */
{
accumulator += bonus; /* Accumulate our bonus/penalty */
g_string_append_printf(str, " %s%d = %d", (bonus < 0) ? "penalty " : "bonus +", bonus, accumulator); /* Append our bonus/penalty to the output string */
}
else if(dice > 1) /* Or if we had more than one die */
{
g_string_append_printf(str, " = %d", accumulator); /* Append our accumulator */
}
#endif
newcmd = g_strdup_printf("me rolls %s", str);
ret = purple_cmd_do_command(conv, newcmd, newcmd, &error);
g_free(str);
g_free(newcmd);
return ret;
}
static gboolean
plugin_load(PurplePlugin *plugin)
{
const gchar *help;
help = _("dice [dice] [sides]: rolls dice number of sides sided dice OR\n"
"dice [XdY+-Z]: rolls X number of Y sided dice, giving a Z "
"bonus/penalty to each. e.g. 1d20+2");
dice_cmd_id = purple_cmd_register("dice", "wws", PURPLE_CMD_P_PLUGIN,
PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
NULL, PURPLE_CMD_FUNC(roll),
help, NULL);
/* we only want to seed this off of the seconds since the epoch once. If
* we do it every time, we'll give the same results for each time we
* process a roll within the same second. This is bad because it's not
* really random then.
*/
srand(time(NULL));
return TRUE;
}
static gboolean
plugin_unload(PurplePlugin *plugin)
{
purple_cmd_unregister(dice_cmd_id);
return TRUE;
}
static PurplePluginInfo info =
{
PURPLE_PLUGIN_MAGIC,
PURPLE_MAJOR_VERSION,
PURPLE_MINOR_VERSION,
PURPLE_PLUGIN_STANDARD,
NULL,
0,
NULL,
PURPLE_PRIORITY_DEFAULT,
"core-plugin_pack-dice",
NULL,
PP_VERSION,
NULL,
NULL,
"Gary Kramlich <grim@reaperworld.com>",
PP_WEBSITE,
plugin_load,
plugin_unload,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
static void
init_plugin(PurplePlugin *plugin)
{
#ifdef ENABLE_NLS
bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR);
bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
#endif /* ENABLE_NLS */
info.name = _("Dice");
info.summary = _("Rolls dice in a chat or im");
info.description = _("Adds a command (/dice) to roll an arbitrary "
"number of dice with an arbitrary number of sides. "
"Now supports dice notation! /help dice for details");
}
PURPLE_INIT_PLUGIN(dice, init_plugin, info)