#include <broadcaster.h>
#include <command.h>
#include <group_handler.h>
// Magic numbers for messages.
#define BROADCASTER_NAME 0
#define MESSAGE 1
#define DATA 0
#define TIME 1
#define TO_BROADCASTER 0
#define TO_OTHERS 1
inherit "/std/effect_shadow";
int _time_joined;
string _group, _parsed_prefix;
object _player, *_assisting, *_assisters, *_preparing_to_attack;
int add_assister( object member );
int remove_assister( object member );
void rebuild_parsed_prefix();
/** @ignore yes */
object attach_to_player( object p, int i ) {
_player = p;
_time_joined = time();
_assisting = ({ });
_assisters = ({ });
_preparing_to_attack = ({ });
return ::attach_to_player( p, i );
} /* attach_to_player() */
/** @ignore yes */
void event_quit( object me ) {
object tmp;
tmp = _player; // Store it locally.
GROUP->remove_member( _group, _player );
tmp->event_quit( me );
} /* event_quit() */
/** @ignore yes */
string query_group() { return _group; }
// This function is called always when a player receives
// membership to a group. Whether it's a completely new
// group, or the new name of a renamed group.
void group_membership_added( string group ) {
_group = group;
rebuild_parsed_prefix();
} /* group_membership_added() */
/** @ignore yes */
void group_membership_removed() { remove_this_effect(); }
/** @ignore yes */
void rebuild_parsed_prefix() {
string channel_name;
tell_creator( _player, "[DEBUG]: Rebuilding channel prefix.\n");
if( sizeof( _group ) <= GROUP_CHANNEL_MAX_CHARS ||
GROUP_CHANNEL_MAX_CHARS <= 3 ) {
channel_name = _group;
} else {
channel_name = _group[ 0..( GROUP_CHANNEL_MAX_CHARS - 4 ) ];
channel_name += "...";
}
_parsed_prefix = GROUP_NOTIFY_PREFIX;
_parsed_prefix = replace( _parsed_prefix,
({
"$channel$", channel_name,
"$colour$", _player->colour_event( GROUP_COLOUR_EVENT, "" )
}) );
} /* rebuild_parsed_prefix() */
/** @ignore yes */
string query_parsed_prefix() { return _parsed_prefix; }
// Group members are visible to the group's leader.
int query_visible( object looker ) {
string looker_group;
// Safe guard for creators.
if( _player->query_invis() && !looker->query_property("test character") )
return _player->query_visible( looker );
looker_group = looker->query_group();
// If I'm a member of the looker's group, and he's using a group
// command, then I'm visible to him.
if( looker != _player && query_verb() == "group" &&
GROUP->is_member( looker_group, _player ) )
return 1;
// Else return default.
return _player->query_visible( looker );
} /* query_visible() */
void set_my_colours( string event, string colour ) {
_player->set_my_colours( event, colour );
if( event == GROUP_COLOUR_EVENT )
rebuild_parsed_prefix();
} /* set_my_colours() */
int attack_by( object attacker ) {
object assists_me;
int delay;
int retval;
retval = player->attack_by( attacker );
if( !retval )
return retval;
_assisters -= ({ 0 });
foreach( assists_me in _assisters ) {
// He's already fighting the attacker.
if( member_array( attacker, assists_me->query_attacker_list() ) != -1 )
continue;
// He's not in my group.
if( !GROUP->is_member( _group, assists_me ) ) {
remove_assister( assists_me );
continue;
}
// He's not in the same room.
if( ENV( assists_me ) != ENV( _player ) )
continue;
if( pk_check( assists_me, attacker ) )
continue;
delay = 16 - sqrt( assists_me->query_skill_bonus("general.perception") );
delay -= random( 3 );
if( delay < 1 )
delay = 1;
delay = delay * sizeof( assists_me->query_assisting() );
tell_creator( assists_me, "(DEBUG) Moving in to assist %s "
"fighting %s in %i seconds.\n", _player->query_cap_name(),
file_name( attacker ), delay );
assists_me->commence_assist( _player, attacker, delay );
}
return retval;
} /* attack_by() */
void commence_assist( object person_to_help, object target_to_attack,
int delay ) {
if( member_array( target_to_attack, _preparing_to_attack ) != -1 )
return;
_preparing_to_attack += ({ target_to_attack });
call_out("assist_member", delay, target_to_attack, person_to_help );
} /* commence_assist() */
void assist_member( object attacker, object assisting ) {
object *my_attacker_list, *his_attacker_list;
_preparing_to_attack -= ({ attacker, 0 });
if( !assisting || !attacker )
return;
my_attacker_list = _player->query_attacker_list();
his_attacker_list = assisting->query_attacker_list();
// I'm already fighting this guy, or at least
// preparing to fight.
if( member_array( attacker, my_attacker_list ) != -1 )
return;
// He's no longer fighting this guy.
if( member_array( attacker, his_attacker_list ) == -1 )
return;
if( attacker == _player || assisting == _player ||
ENV( attacker ) != ENV( _player ) )
return;
// We're not assisting them any more.
if( member_array( assisting, _assisting ) == -1 )
return;
if( _player->attack_ob( attacker ) ) {
if( !sizeof( my_attacker_list | _preparing_to_attack ) ) {
_player->add_message("Noting the intentions of "+
attacker->the_short()+", you move in to assist "+
assisting->the_short()+".\n", ({ }) );
return;
}
_player->add_message("You note the intentions of "+
attacker->the_short()+" and prepare to attack "+
attacker->HIM+" as well.\n", ({ }) );
}
} /* assist_member() */
int add_assister( object member ) {
_assisters -= ({ 0 });
if( member_array( member, _assisters ) != -1 )
return 0;
_assisters += ({ member });
member->add_assisting( _player );
return 1;
} /* add_assister() */
void add_assisting( object member ) {
_assisting -= ({ 0 });
if( member_array( member, _assisting ) != -1 )
return;
_assisting += ({ member });
} /* add_assisting() */
int remove_assister( object member ) {
_assisters -= ({ 0 });
if( member_array( member, _assisters ) == -1 )
return 0;
_assisters -= ({ member });
member->remove_assisting( _player );
return 1;
} /* remove_assister() */
void remove_assisting( object member ) {
_assisting -= ({ 0 });
_assisting -= ({ member });
} /* remove_assisting() */
object *query_assisting() {
_assisting -= ({ 0 });
return _assisting;
} /* query_assisting() */
object *query_assisters() {
_assisters -= ({ 0 });
return _assisters;
} /* query_assisters() */
void event_channel_message( object broadcaster, string channel, mixed data ) {
mixed message;
string msg, colour;
message = data[ 0 ];
if( channel != "group_"+_group ) {
call_out( (: remove_this_effect() :), 1 );
return;
}
if( !GROUP->is_member( _group, _player ) ) {
call_out( (: remove_this_effect() :), 1 );
return;
}
colour = _player->colour_event( GROUP_COLOUR_EVENT, "" );
msg = "";
if( stringp( message ) ) {
msg += message;
} else if( arrayp( message ) ) {
if( broadcaster == _player ) {
if( message[ TO_BROADCASTER ] ) {
msg += message[ TO_BROADCASTER ];
} else {
return;
}
} else if( message[ TO_OTHERS ] ) {
msg += message[ TO_OTHERS ];
} else {
return;
}
}
msg += "\n";
msg = replace( msg, ({ "%^RESET%^", ( "%^RESET%^" + colour ),
"\n", "%^RESET%^\n"}) );
msg = _parsed_prefix + colour + msg;
tell_object( _player, msg );
} /* event_channel_message() */
void display_group_history() {
int indent_a, indent_b;
mixed *bit, *history;
string whole, message, prefix, suffix;
/* This is a convoluted function because of the hack-job I
* did with the message structure. Basically, this is
* what one message consists of:
* bit =
* ({
* [0] broadcaster's name (capitalized),
* [1] ({
* [0] either array: ({ msg_to_broadcaster, msg_to_rest }) or
* msg_to_all,
* [1] time() when the broadcast was made
* })
* })
*/
if( !GROUP->is_member( _group, _player ) ) {
call_out( (: remove_this_effect() :), 0 );
return;
}
history = BROADCASTER->query_channel_history( "group_" + _group );
if( !sizeof( history ) ) {
tell_object( this_player(), "Nothing has been said on your "
"group's channel.\n" );
return;
}
if( TP != _player ) {
tell_object( TP, "Shame on you.\n");
return;
}
prefix = _parsed_prefix;
// This is a bit of a hack, but I don't feel like rewriting
// the messaging system. And it's not that ugly.
sscanf( GROUP_NOTIFY_PREFIX, "$I$+%d,+%d=", indent_a, indent_b );
suffix = "$I$-" + indent_a + ",-" + indent_b + "=\n";
whole = "";
foreach( bit in history ) {
if( bit[ MESSAGE ][ TIME ] < _time_joined ) {
// The message was broadcasted before this member
// joined the group. We skip it.
continue;
}
// message = "** " + ctime( bit[ MESSAGE ][ TIME ] ) + " **:\n";
message = ctime(bit[MESSAGE][TIME])[11..18];
message += prefix;
if( bit[ MESSAGE ][ DATA ] && stringp( bit[ MESSAGE ][ DATA ] ) ) {
// The message is general to everyone.
message += bit[ MESSAGE ][ DATA ];
message += "$I$-5,-0=\n";
whole += message;
continue;
}
if( !arrayp( bit[ MESSAGE ][ DATA ] ) ) {
// Something's gone wrong.
continue;
}
// The message is an array divided to two parts: the first
// element is the message the broadcaster received ("You..."),
// the second is what everyone else received.
if( bit[ BROADCASTER_NAME ] &&
lower_case( bit[ BROADCASTER_NAME ] ) == _player->query_name() ) {
// The broadcaster is the person listening.
if( bit[ MESSAGE ][ DATA ][ TO_BROADCASTER ] ) {
// We give him msg_to_broadcaster, the first.
message += bit[ MESSAGE ][ DATA ][ TO_BROADCASTER ];
}
} else if( bit[ MESSAGE ][ DATA ][ TO_OTHERS ] ) {
// The broadcaster is someone else. We give him msg_to_rest.
message += bit[ MESSAGE ][ DATA ][ TO_OTHERS ];
}
whole += message + suffix;
}
tell_object( TP, whole );
} /* display_channel_history() */
varargs int adjust_xp( int original_xp, int shared ) {
int plvl, olvl, xp, share;
object other, *others;
if( original_xp < 0 || !shared )
return ( int )player->adjust_xp( original_xp );
others = GROUP->members_of( _group );
if( !sizeof( others ) ) {
call_out( (: remove_this_effect() :), 1 );
return ( int )player->adjust_xp( original_xp );
}
if( sizeof( others ) == 1 )
return ( int )player->adjust_xp( original_xp );
// the more people in the team the smaller the shared Xp.
xp = ( original_xp * 2 ) / sizeof(others);
plvl = (int)player->query_level() / 2;
foreach( other in others - ({ _player }) ) {
if( !objectp( other ) )
continue;
if( ENV( other ) != ENV( _player ) )
continue;
olvl = (int)other->query_level();
if( olvl <= plvl && plvl ) {
share = ( xp * ( 1 + ( 99 * olvl ) / plvl ) ) / 100;
} else {
share = xp;
}
tell_creator( _player, "%s's share: %i\n", other->query_name(),
share );
other->adjust_xp( share, 0 );
}
tell_creator( _player, "%s's share: %i\n", _player->query_name(),
original_xp );
return ( int )player->adjust_xp( original_xp );
} /* adjust_xp() */
/**
* This bit coded completely by Ceres. Or was it Deutha? In
* any case, thanks to them.
*/
varargs int add_skill_level( string skill, int lvl, mixed exp ) {
int diff_0, diff_1, diff, width_0, width, ret;
object other, *others;
ret = _player->add_skill_level( skill, lvl, exp );
if( !ret ) {
// If they didn't actually get an advance due to hitting a limit or
// whatever then don't give their team an advance either.
return ret;
}
// Not sharing already shared TMs, TMs from contemplate, skill
// level advancements from 'advance', perception and points TMs.
if( objectp( exp ) && base_name( exp ) == base_name( TO ) ||
( PO && file_name( PO ) == CONTEMPLATE ) || lvl != 1 ||
intp( exp ) || skill == "general.perception" ||
( skill[ <7.. ] == ".points" ) )
return ret;
if( !_group ) {
// Something's messed up.
call_out( (: remove_this_effect() :), 1 );
return ret;
}
others = (object *)GROUP->members_of( _group );
others -= ({ 0 });
if( !sizeof( others ) ) {
// Something's REALLY messed up.
call_out( (: remove_this_effect() :), 1 );
return ret;
}
others -= ({ _player });
if( !sizeof( others ) ) {
// He's soloing.
return ret;
}
diff_0 = (int)_player->query_skill_bonus( skill );
diff_1 = (int)_player->query_skill( skill );
width_0 = (int)_player->stat_modify( 25 * 25, skill );
foreach ( other in others ) {
if( !objectp( other ) )
continue;
if( ENV( other ) != ENV( _player ) )
continue;
if( diff_0 - (int)other->query_skill_bonus( skill ) >
diff_1 - (int)other->query_skill( skill ) ) {
diff = diff_0 - (int)other->query_skill_bonus( skill );
} else {
diff = diff_1 - (int)other->query_skill( skill );
}
width = (int)other->stat_modify( width_0, skill );
if( random( width + diff * diff +
sqrt( other->query_skill( skill ) ) ) < ( width / 2 ) &&
other->add_skill_level( skill, lvl, TO ) ) {
tell_object( other, "%^YELLOW%^By watching "+
(string)_player->the_short()+", you feel you have learnt "
"something.%^RESET%^\n");
}
}
return 1;
} /* add_skill_level() */