/*
** The way this used to be, if the player trying to work the quest
** suddenly buggered off in the middle of it, the quest related
** weights would still be there, but if another player came along and
** moved one of the weights, the quest related one would vanish. This
** has puzzled players, and cased at least one bug report. I've changed
** it so that the quest related weight hangs around until a player resets
** the weights or actually starts the quest using the figure command.
**
** Also removed some code that has been commented out for two years, and
** some other bits that were redundant because of this piece of unused
** code. And did some general Spring Cleaning. It seems to be working fine,
** but feel free to revert it if it breaks horribly. --Tilly
*/
#include <library.h>
#define DEFAULT_PANS ([ \
"left" : ([ ]), \
"middle" : ([ \
"very small" : ({ "1/9", 1 }), \
"small" : ({ "1/3", 3 }), \
"medium" : ({ "1", 9 }), \
"large" : ({ "3", 27 }), \
"very large" : ({ "9", 81 }) ]), \
"right" : ([ ]) ])
inherit "/std/object";
private nosave int balance;
private nosave string *doing;
private nosave mapping pans;
/* Start of function prototypes */
void setup();
void init();
string *query_doing();
mapping query_pans();
string long( string words, int dark );
string pans_look();
void recalculate_balance();
int reset_weights();
string weight_string( int weight );
int weigh_something( object* obs );
int figure_it_out();
int move_a_specific_weight( string weight_size, string pan_to );
int move_a_weight( string weight_size, string pan_from, string pan_to );
void it_is_balanced( object person );
/* End of function prototypes */
void setup() {
set_name( "balance" );
set_short( "weighing balance" );
add_adjective( "weighing" );
add_alias( ({ "pan", "pans", "weight", "weights" }) );
set_weight( 200 );
reset_get();
doing = ({ });
pans = DEFAULT_PANS;
} /* setup() */
void init() {
add_command("weigh", "<indirect:object:me'thing(s)'> on <direct:object>",
(:this_object()->weigh_something($1):));
add_command("reset", "weights on balance", (: reset_weights() :));
add_command("figure", "", (:this_object()->figure_it_out():));
add_command("move",
"<string'size'> weight to <string'position'> pan",
(:this_object()->move_a_specific_weight($4[0], $4[1]):));
} /* init() */
string *query_doing() { return doing; }
mapping query_pans() { return pans; }
string long( string words, int dark ) {
int i, j;
string long;
string *bits;
string *places;
string *weights;
if (!words) {
words = "balance";
}
bits = explode( words, " " );
switch ( bits[ sizeof( bits ) - 1 ] ) {
case "balance" :
return "This is a largish bronze balance, securely bolted in "
"place. The main part of the balance is a long arm which "
"pivots at its centre. There is a pan hanging from each "
"end of the arm such that it will be level when the weights "
"in the pans are equal. A third pan is fixed to a stationary "
"part of the balance where the weights can be held when not "
"in use.\n" + pans_look() + "You could probably use the balance "
"to weigh something.\nThere appears to be something "
"written on it.\n";
case "pan" :
case "pans" :
return "There are three pans. One pan hangs from the left end of "
"the arm, one from the right end and there is a third pan "
"in the middle.\n"+ pans_look();
case "weight" :
case "weights" :
long = "There are weights of many different sizes in the pans. "
"They are:\n";
bits = ({ });
places = m_indices( pans );
for ( i = 0; i < sizeof( places ); i++ ) {
if ( !m_sizeof( pans[ places[ i ] ] ) ) {
continue;
}
weights = m_indices( pans[ places[ i ] ] );
for ( j = 0; j < sizeof( weights ); j++ ) {
if ( sizeof( doing ) ) {
if ( doing[ 1 ] == weights[ j ] ) {
continue;
}
}
bits += ({ add_a( weights[ j ] ) + " weight marked with \""
+ pans[ places[ i ] ][ weights[ j ] ][ 0 ] +" lb\"" });
}
}
long += " " + implode( bits[ 0 .. sizeof( bits ) - 2 ],
",\n " ) +",\n and "+ bits[ sizeof( bits ) - 1 ] + ".\n";
long += "The weights can be moved from one pan to another and "
"reset back to their starting positions.\n";
return long;
}
return "You're not quite sure what you're looking at.\n";
} /* long() */
string pans_look() {
int i;
int j;
string pans_status;
string *places;
string *weights;
pans_status = "";
places = m_indices( pans );
for ( i = 0; i < sizeof( places ); i++ ) {
pans_status += "The "+ places[ i ] +" pan ";
if ( !m_sizeof( pans[ places[ i ] ] ) )
pans_status += "is empty.\n";
else {
weights = m_indices( pans[ places[ i ] ] );
for ( j = 0; j < sizeof( weights ); j++ )
weights[ j ] = add_a( weights[ j ] ) +" weight";
pans_status += "holds "+ query_multiple_short( weights ) +".\n";
}
}
switch ( balance ) {
case -1 :
pans_status += "The left pan hangs lower than the right pan.\n";
break;
case 0 :
pans_status += "The left pan hangs level with the right pan.\n";
break;
case 1 :
pans_status += "The left pan hangs higher than the right pan.\n";
break;
}
return pans_status;
} /* pans_look() */
void recalculate_balance() {
int i;
int j;
int old_balance;
string *places;
string *weights;
mapping pan_weights;
pan_weights = ([ ]);
places = m_indices( pans );
for ( i = 0; i < sizeof( places ); i++ ) {
pan_weights[ places[ i ] ] = 0;
if ( !m_sizeof( pans[ places[ i ] ] ) ) {
continue;
}
weights = m_indices( pans[ places[ i ] ] );
for ( j = 0; j < sizeof( weights ); j++ ) {
pan_weights[ places[ i ] ] +=
pans[ places[ i ] ][ weights[ j ] ][ 1 ];
}
}
old_balance = balance;
if ( pan_weights[ "left" ] > pan_weights[ "right" ] ) {
balance = -1;
} else {
if ( pan_weights[ "left" ] == pan_weights[ "right" ] ) {
balance = 0;
} else {
balance = 1;
}
}
if ( old_balance == balance ) {
switch ( balance ) {
case -1 :
tell_room( environment(), "The arm rocks a bit but steadies, "
"with the left pan still hanging lowest.\n" );
break;
case 1 :
tell_room( environment(), "The arm rocks a bit but steadies, "
"with the right pan still hanging lowest.\n" );
break;
}
return;
}
switch ( balance ) {
case -1 :
tell_room( environment(), "The arm of the balance tips and the "
"left pan ends up hanging lowest.\n" );
break;
case 0 :
tell_room( environment(), "The arm of the balance levels out, "
"with the left and right pans hanging level.\n" );
break;
case 1 :
tell_room( environment(), "The arm of the balance tips and the "
"right pan ends up hanging lowest.\n" );
break;
}
} /* recalculate_balance() */
int reset_weights() {
pans = DEFAULT_PANS;
if (sizeof(doing)) {
pans[ "left" ][ doing[ 1 ] ] = ({ "?", 1 + random( 121 ) });
}
add_succeeded_mess("$N $V the weights on $D.\n");
recalculate_balance();
return 1;
} /* reset_weights() */
string weight_string( int weight ) {
int wholes;
int ninths;
wholes = weight / 9;
ninths = weight % 9;
if ( wholes && ninths ) {
return wholes + " " + ninths + "/9 lb";
}
if ( wholes ) {
return wholes + " lb";
}
return ninths + "/9 lb";
} /* weight_string() */
int weigh_something( object* obs ) {
int i;
int info;
int weight;
object person;
if ( sizeof( doing ) ) {
person = find_player( doing[ 0 ] );
if ( person == this_player() ) {
add_failed_mess( "Hold your horses, you're trying to get the hang "
"of it still.\n" );
return 0;
}
if ( person && ( environment( person ) == environment() ) ) {
add_failed_mess( (string)person->one_short() + " is using "
"the balance at the moment. Come back when "
+ (string)person->query_pronoun() + " has finished.\n" );
return 0;
}
doing = ({ });
pans = DEFAULT_PANS;
}
info = (int)LIBRARY->query_player_quest_info(
(string)this_player()->query_name(), "balance" );
switch ( info ) {
case 0 :
add_failed_mess( "You don't know how the balance works to weigh "
"anything.\nTry looking at the balance, the pans and "
"the weights, and then maybe you can figure it out.\n" );
return 0;
case 1 :
add_failed_mess( "You're still not too sure how the balance works "
"to weigh anything.\nTry looking at the balance, the pans and "
"the weights, and then maybe you can figure it out.\n" );
return 0;
case 2 :
add_failed_mess( "You're very nearly sure how the balance works, "
"but maybe you should try to figure it out once more before "
"you weigh anything.\n" );
return 0;
}
for ( i = 0; i < sizeof( obs ); i++ ) {
weight = obs[ i ]->query_complete_weight();
if ( !weight ) {
write( obs[ i ]->the_short() + " doesn't weigh anything.\n" );
continue;
}
if ( weight > 121 ) {
write( obs[ i ]->the_short() +
" is heavier than all the weights available put together.\n" );
continue;
}
write( obs[ i ]->the_short() +
" weighs "+ weight_string( weight ) +".\n" );
}
add_succeeded_mess(({ "", "$N $V $I on $D.\n" }), obs);
return 1;
} /* weigh_something() */
int figure_it_out() {
int info;
object person;
if ( sizeof( doing ) ) {
person = find_player( doing[ 0 ] );
if ( person == this_player() ) {
notify_fail( "You're already engaged in figuring out how the "
"balance can be used to weigh something.\n" );
return 0;
}
if ( person && ( environment( person ) == environment() ) ) {
notify_fail( (string)person->one_short() + " is using "
"the balance at the moment. Come back when "
+ (string)person->query_pronoun() + " has finished.\n" );
return 0;
}
doing = ({ });
pans = DEFAULT_PANS;
}
info = (int)LIBRARY->query_player_quest_info(
(string)this_player()->query_name(), "balance" );
if ( info > 2 ) {
write( "You already know how the balance works.\n" );
return 1;
}
doing = ({ (string)this_player()->query_name(), ({ "red", "green",
"blue" })[ info ] });
write( "You see " + add_a( doing[ 1 ] ) + " weight in the middle pan "
"that you hadn't noticed before. Maybe you could use this to "
"experiment, so you place it in the left pan.\n" );
say( (string)this_player()->one_short() + " moves "
+ add_a( doing[ 1 ] ) + " weight from the middle pan to the "
"left pan.\n" );
pans[ "left" ][ doing[ 1 ] ] = ({ "?", 1 + random( 121 ) });
recalculate_balance();
return 1;
} /* figure_it_out() */
int move_a_specific_weight( string weight_size, string pan_to ) {
object person;
string pan_from;
if ( sizeof( doing ) ) {
person = find_player( doing[ 0 ] );
if ( !person ) {
doing = ({ });
} else {
if ( person != this_player() ) {
if ( environment( person ) == environment() ) {
add_failed_mess( (string)person->one_short() + " is "
"using the balance at the moment. Come back when "
+ (string)person->query_pronoun() + " has finished.\n" );
return 0;
} else {
doing = ({ });
pans = DEFAULT_PANS;
}
}
}
}
if ( !pans[ pan_to ] ) {
add_failed_mess( "There is a left pan, a middle pan and a right pan, "
"but no " + pan_to + " pan.\n" );
return 0;
}
foreach (pan_from in keys(pans)) {
if ( pans[ pan_from ][ weight_size ] ) {
return move_a_weight(weight_size, pan_from, pan_to);
}
}
add_failed_mess("Unable to find the " + weight_size + " weight.\n");
return 0;
} /* move_a_specific_weight() */
int move_a_weight( string weight_size, string pan_from, string pan_to ) {
if ( pan_from == pan_to ) {
add_failed_mess( "The " + weight_size + " weight is already in the "
+ pan_to + " pan.\n" );
return 0;
}
if ( sizeof( doing ) ) {
if ( weight_size == doing[ 1 ] ) {
notify_fail( "You don't feel like moving the " + doing[ 1 ]
+ " weight since that's what you're trying to weigh.\n" );
return 0;
}
}
pans[ pan_to ][ weight_size ] = pans[ pan_from ][ weight_size ];
pans[ pan_from ] = m_delete( pans[ pan_from ], weight_size );
write( "You move the "+ weight_size +" weight from the "+ pan_from
+ " pan to the "+ pan_to + " pan.\n" );
say( (string)this_player()->one_short() + " moves "
+ add_a( weight_size ) +" weight from the "+ pan_from + " pan "
"of the balance to the " + pan_to + " pan.\n" );
recalculate_balance();
if ( sizeof( doing ) && !balance ) {
call_out( "it_is_balanced", 0, this_player() );
}
return 1;
} /* move_a_weight() */
void it_is_balanced( object person ) {
int info;
tell_object( person, "You feel a small surge of self-esteem to have found "
"that the "+ doing[ 1 ] +" weight weighs "
+ weight_string( pans[ "left" ][ doing[ 1 ] ][ 1 ] ) +".\n" );
info = (int)LIBRARY->query_player_quest_info( (string)person->query_name(),
"balance" );
info++;
switch ( info ) {
case 1 :
tell_object( person, "You've made a good start at working out "
"how the balance operates. You think you should practice "
"with it a couple of times more to get the hang of it, "
"though.\n" );
person->adjust_xp( 5000 );
break;
case 2 :
tell_object( person, "You're definitely getting to understand how "
"the balance operates. You think you should practice with "
"it once more to get used to it completely, though.\n" );
person->adjust_xp( 10000 );
break;
case 3 :
tell_object( person, "You're now adept at using the balance and "
"can use it to weigh anything.\n" );
/* Quest!
* Name: balance quest
* Title: Expert Balancer
* Story: discovered how to weigh things using powers of three
* Level: 3
*/
if ( interactive( person ) ) {
if ( !LIBRARY->query_quest_done( (string)person->query_name(),
"balance quest" ) ) {
LIBRARY->set_quest( (string)person->query_name(),
"balance quest" );
}
}
break;
case 4:
info--;
break; //if people want to keep moving the weights, let them.
default :
tell_object( person, "Something has gone wrong with the balance. "
"Please contact Wodan about it.\n" );
}
LIBRARY->set_player_quest_info( (string)person->query_name(), "balance",
info );
tell_object( person, "You put all the weights back into the middle "
"pan.\n" );
tell_room( environment(), (string)person->the_short() +
" seems satisfied with "+ (string)person->query_objective() +"self, "
"and returns all of the weights to the middle pan.\n", person );
doing = ({ });
pans = DEFAULT_PANS;
} /* it_is_balanced() */