/**
* This is the crime handler.
* Keeps track of criminals.
* By Sandoz, 28th July 2001.
*/
#include <crime.h>
#define CRIME_SAVE_DIR HANDLER_SAVE_DIR "/crime/"
#define CHECK_TIME (24*60*60) // once a day.
string place;
mapping criminals; // ([ name : ([ type : times, type : times ]) ])
mapping sentences; // ([ name : times, name : times ])
mapping place_data; // any additional data for the place
private nosave mapping dying;
private nosave int _callout_id, dying_id;
/**
* This method will does the callout to the check_arrays function
* @ignore yes
*/
protected void setup_call_check_arrays( int time ) {
remove_call_out(_callout_id);
_callout_id = call_out( "check_arrays", time );
} /* setup_call_check_arrays() */
/** @ignore yes */
private void init_data( string place_name ) {
place = place_name;
criminals = ([ ]);
sentences = ([ ]);
place_data = ([ ]);
} /* init_data() */
/** @ignore yes */
private int get_data_file( string place_name ) {
place_name = lower_case(place_name);
if( place != place_name )
if( file_size( CRIME_SAVE_DIR + place_name+".o" ) > 0 ) {
unguarded( (: restore_object, CRIME_SAVE_DIR + place_name :) );
place = place_name;
if( !criminals )
criminals = ([ ]);
if( !sentences )
sentences = ([ ]);
if( !place_data )
place_data = ([ ]);
} else {
init_data( place_name );
return 0;
}
return 1;
} /* get_data_file() */
/** @ignore yes */
private void save_data_file( string place_name ) {
unguarded( (: save_object, CRIME_SAVE_DIR + lower_case(place_name) :) );
} /* save_data_file() */
/** ignore yes */
void create() {
/* check the criminals array after 10 minutes from startup */
setup_call_check_arrays( 600 );
} /* create() */
/**
* This method checks if the place is a valid place.
* @param place_name the place to check
* @return 1 if the place is valid, 0 if not
*/
int valid_place( string place_name ) {
return ( member_array( lower_case(place_name), ALL_PLACES ) != -1 );
} /* valid_place() */
/**
* This method if the crime type is valid.
* @param type the crime type to check
* @return 1 if the crime type is valid, 0 if not
*/
int valid_crime( int type ) {
return ( member_array( type, CRIME_TYPES ) != -1 );
} /* valid_crime() */
/** @ignore yes */
string get_player_name( mixed player ) {
if( objectp(player) )
return (string)player->query_name();
else if( stringp(player) )
return player;
else
return 0;
} /* get_player_name() */
/**
* This method checks if the player is a criminal
* @param place_name the name of the place to check
* @param player the name or object of the player to check
* @return 1 if they're a criminal, 0 if not
*/
int query_criminal( string place_name, mixed player ) {
string name;
if( !name = get_player_name(player) )
return 0;
get_data_file( place_name );
return !undefinedp(criminals[name]);
} /* query_criminal() */
/**
* This method adds a player into the criminals array
* @param place_name the name of the place to add the criminal in
* @param player the name or object of the player to add
* @param type the type of offense. They have been defined as follows -
* VANDALISM 1 - vandalism
* THEFT 2 - stealing
* SHOPLIFT 3 - shoplifting
* SLAUGHTER 4 - killing innocents
* RESIST 5 - resisting arrest
* GUARDKILL 6 - guard killing
* @return 1 if successfully added, 0 if not
* @see "/include/crime.h"
*/
int add_criminal( string place_name, mixed player, int type ) {
string name;
if( !valid_place( place_name ) || !intp(type) || !valid_crime( type ) ||
!( name = get_player_name(player) ) )
return 0;
get_data_file( place_name );
if( undefinedp(criminals[name]) )
criminals[name] = ([ ]);
criminals[name][type]++;
save_data_file( place_name );
return 1;
} /* add_criminal() */
/**
* This method removes a player from the criminals array
* @param place_name the name of the place to clear the player's crime ledger
* @param player the name or object of the player to remove
* @return 1 if successfully removed, 0 if not
*/
int remove_criminal( string place_name, mixed player ) {
string name;
if( !valid_place( place_name ) || !( name = get_player_name(player) ) )
return 0;
get_data_file( place_name );
if( undefinedp(criminals[name]) )
return 0;
map_delete( criminals, name );
save_data_file( place_name );
return 1;
} /* remove_criminal() */
/** @ignore yes */
void handle_death( string place_name, object killer, object who ) {
if( !valid_place(place_name) )
return;
if( !mapp(dying) )
dying = ([ ]);
if( !dying[who] )
add_criminal( place_name, killer, SLAUGHTER );
dying[who] = 1;
if( !dying_id ) {
dying_id = call_out( function() {
dying = ([ ]);
dying_id = 0;
}, 10 );
}
} /* handle_death() */
/**
* This method returns the types of crimes the person has committed
* @param place_name the name of the place to get the crimes for
* the player in
* @param player the name or object of the player to check
* @return the types of crimes the player has committed
*/
mapping query_crimes( string place_name, mixed player ) {
string name;
if( !name = get_player_name(player) )
return 0;
get_data_file( place_name );
return criminals[name];
} /* query_crimes() */
/** @ignore yes */
string parse_num( int number, int times ) {
if( number == 1 )
return ( times ? "once" : "one" );
if( number == 2 )
return ( times ? "twice" : "two" );
else
return query_num(number)+( times ? " times" : "" );
} /* parse_num() */
/**
* This method returns the types of crimes the person has committed
* in a nice string format
* @param place_name the name of the place to get the crime string
* for the player in
* @param player the name or object of the player to check
* @return the types of crimes the player has committed
*/
string query_crime_string( string place_name, mixed player ) {
mapping crimes;
string *ret, name;
if( !name = get_player_name(player) )
return 0;
get_data_file( place_name );
if( undefinedp(criminals[name]) || !sizeof( crimes = criminals[name] ) )
return 0;
ret = ({ });
foreach( int _type in sort_array( keys(crimes), 1 ) ) {
int i = crimes[_type];
ret += ({ replace( CRIME_MAP[_type][1], ({
"$times$", parse_num( i, 1 ),
"$number$", parse_num( i, 0 ),
"$s", ( i > 1 ? "s" : "" ) }) ) });
}
if( !sizeof(ret) )
return 0;
return query_multiple_short( ret );
} /* query_crime_string() */
/**
* This method raises a player's sentence count in the specified area
* @param place_name the name of the place to raise the count in
* @param player the name or object of the player to raise the count for
* @return 1 if successfully added, 0 if not
*/
int add_sentence( string place_name, mixed player ) {
string name;
if( !valid_place( place_name ) || !( name = get_player_name(player) ) )
return 0;
get_data_file( place_name );
sentences[name]++;
save_data_file( place_name );
return 1;
} /* add_sentence() */
/**
* This method nullifies a players' sentence count
* @param place_name the name of the place to clear the player's count in
* @param player the name or object of the player to remove
* @return 1 if successfully removed, 0 if not
*/
int remove_sentences( string place_name, mixed player ) {
string name;
if( !valid_place( place_name ) || ( !name = get_player_name(player) ) )
return 0;
get_data_file( place_name );
if( undefinedp(sentences[name]) )
return 0;
map_delete( sentences, name );
save_data_file( place_name );
return 1;
} /* remove_sentence() */
/**
* This method returns the sentence count of the player in the
* specified area
* @param place_name the name of the place to get the count for
* @param player the name or object of the player to check
* @return the types of crimes the player has committed
*/
int query_sentence_count( string place_name, mixed player ) {
string name;
if( !name = get_player_name(player) )
return 0;
get_data_file( place_name );
return sentences[name];
} /* query_sentence_count() */
/** @ignore yes */
int calc_sentence_length( mapping crimes ) {
int length, tmp, div;
tmp = SENTENCE_LENGTH_MOD;
div = SENTENCE_LENGTH_MOD / sizeof(crimes);
/* descending sort, harder crimes first */
foreach( int _type in sort_array( keys(crimes), -1 ) ) {
length += tmp * CRIME_MAP[_type][0] + sqrt( 2 * tmp * crimes[_type] );
tmp -= div;
}
return length;
} /* calc_sentence_lenght() */
/**
* This method returns the players sentence length based on
* their criminal ledger in the specified area
* @param place_name the name of the place to get their sentence
* length in
* @param player the name or object of the player to get the
* sentence length for
* @return the player's sentence length
*/
int query_sentence_length( string place_name, mixed player ) {
mapping crimes;
int length;
string name;
if( !name = get_player_name(player) )
return 0;
get_data_file( place_name );
if( undefinedp(criminals[name]) || !sizeof( crimes = criminals[name] ) )
return 0;
length = calc_sentence_length( crimes );
length += 30 * sqrt( sentences[name] );
return length;
} /* query_sentence_length() */
/**
* This method checks if the player is a guard killer
* @param place_name the name of the place to check if the
* player is a killer in
* @param player the name or object of the player to check
* @return 1 if they're a guard killer, 0 if not
*/
int query_killer( string place_name, mixed player ) {
mapping crimes;
string name;
if( !name = get_player_name(player) )
return 0;
get_data_file( place_name );
if( undefinedp(criminals[name]) || !sizeof( crimes = criminals[name] ) )
return 0;
return !undefinedp( crimes[GUARDKILL] );
} /* query_killer() */
/**
* @ignore yes
* This is used by query_sorted_criminals().
*/
int do_sort( int i, int j ) {
if( i < j ) return 1;
if( i > j ) return -1;
return 0;
} /* do_sort() */
/**
* This method returns the sorted array of criminals in a
* specified area
* @param place_name the name of the place to get the names for
* @return the sorted array of criminals
*/
string *query_sorted_criminals( string place_name ) {
string name;
mixed ret;
mapping crimes;
if( !valid_place( place_name ) )
return 0;
get_data_file( place_name );
if( !sizeof(criminals) )
return 0;
ret = ({ });
foreach( name, crimes in criminals )
ret += ({ ({ name, calc_sentence_length( crimes ) }) });
return map( sort_array( ret, (: do_sort( $1[1], $2[1] ) :) ), (: $1[0] :) );
} /* query_sorted_criminals() */
/**
* This method returns all the criminals in the specified place.
* @param place_name the name of the place to get the
* criminals' names for
* @return all current criminals' names
*/
string *query_criminals( string place_name ) {
get_data_file( place_name );
return ( sizeof(criminals) ? keys(criminals) : 0 );
} /* query_crimes() */
/**
* This method deletes the given player by name from all
* places' criminal arrays. This should be called by the
* refresh handler when the player refreshes or is deleted.
* @param player the player to delete
* @return 1 if the operation was successful, 0 if it failed
*/
void refresh_criminal( string player ) {
foreach( string place_name in ALL_PLACES ) {
remove_criminal( place_name, player );
remove_sentences( place_name, player );
}
} /* refresh_criminal() */
/**
* This method checks to make sure all the criminals still exist.
* It also removes the invalid criminals.
*/
void check_arrays() {
remove_call_out( _callout_id );
setup_call_check_arrays( CHECK_TIME );
foreach( string place_name in ALL_PLACES ) {
get_data_file( place_name );
foreach( string criminal in keys(criminals) )
if( !criminal || !sizeof(criminal) || !rank( criminal ) )
map_delete( criminals, criminal );
save_data_file( place_name );
}
} /* check_arrays() */
mixed *stats() {
return ({
({ "place", ( place ? place : 0 ) }),
({ "total criminals", ( criminals ? sizeof(keys(criminals)) : 0 ) }),
});
} /* stats() */