#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; int move_a_weight( string weight_size, string pan_from, string pan_to ); void setup() { set_name( "balance" ); set_short( "weighing balance" ); add_adjective( "weighing" ); add_alias( ({ "pan", "pans", "weight", "weights" }) ); set_read_mess( "Thys balanss ackurayte to one-nyneth of ae pounde." ); set_weight( 200 ); reset_get(); doing = ({ }); pans = DEFAULT_PANS; } /* setup() */ string *query_doing() { return doing; } mapping query_pans() { return pans; } 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() */ 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 from <string'position'> pan to <string'position'> pan", (:this_object()->move_a_weight($4[0], $4[1], $4[2]):)); add_command("move", "<string'size'> weight to <string'position'> pan", (:this_object()->move_a_specific_weight($4[0], $4[1]):)); } /* init() */ 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.\n"+ "Try \"look\"ing 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 \"look\"ing 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() */ 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 "+ "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 \"move\"d 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() */ 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 = ({ }); pans = DEFAULT_PANS; } 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 ) { object person; if ( sizeof( doing ) ) { person = find_player( doing[ 0 ] ); if ( !person ) { doing = ({ }); pans = DEFAULT_PANS; } 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_from ] ) { add_failed_mess( "There is a left pan, a middle pan and a right pan, but "+ "no "+ pan_from +" pan.\n" ); return 0; } 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; } if ( !pans[ pan_from ][ weight_size ] ) { add_failed_mess( "There isn't "+ add_a( weight_size ) +" weight in the "+ pan_from +" pan.\n" ); return 0; } 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 ]; map_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() */