pidgin/purple-plugin-pack
Clone
Summary
Browse
Changes
Graph
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
)