diff options
Diffstat (limited to 'engines/glk/adrift/scrunner.cpp')
-rw-r--r-- | engines/glk/adrift/scrunner.cpp | 3469 |
1 files changed, 1681 insertions, 1788 deletions
diff --git a/engines/glk/adrift/scrunner.cpp b/engines/glk/adrift/scrunner.cpp index ab6bfd8305..9e979d1193 100644 --- a/engines/glk/adrift/scrunner.cpp +++ b/engines/glk/adrift/scrunner.cpp @@ -45,396 +45,427 @@ static const sc_char *const SEPARATORS = ".,"; * and handled. */ static sc_bool -run_is_task_function (const sc_char *pattern, sc_gameref_t game) -{ - const sc_prop_setref_t bundle = gs_get_bundle (game); - const sc_var_setref_t vars = gs_get_vars (game); - sc_vartype_t vt_key[3]; - sc_int room, object; - sc_char *argument; - - /* Simple comparison against the one known task expression. */ - argument = (sc_char *)sc_malloc (strlen (pattern) + 1); - if (sscanf (pattern, " # %%object%% = getdynfromroom (%[^)])", argument) == 0) - { - sc_free (argument); - return FALSE; - } - - /* - * Compare the argument read in against known room names. - * - * TODO Is this simple room name comparison good enough? - */ - vt_key[0].string = "Rooms"; - for (room = 0; room < gs_room_count (game); room++) - { - const sc_char *name; - - vt_key[1].integer = room; - vt_key[2].string = "Short"; - name = prop_get_string (bundle, "S<-sis", vt_key); - if (sc_strcasecmp (name, argument) == 0) - break; - } - sc_free (argument); - if (room == gs_room_count (game)) - return FALSE; - - /* - * Select a dynamic object from the room. - * - * TODO What are the selection criteria supposed to be? Here we use "on - * the floor". - */ - vt_key[0].string = "Objects"; - for (object = 0; object < gs_object_count (game); object++) - { - sc_bool bstatic; - - vt_key[1].integer = object; - vt_key[2].string = "Static"; - bstatic = prop_get_boolean (bundle, "B<-sis", vt_key); - if (!bstatic && obj_directly_in_room (game, object, room)) - break; - } - if (object == gs_object_count (game)) - return FALSE; - - /* Set this object reference, unambiguously, as if %object% match. */ - gs_clear_object_references (game); - game->object_references[object] = TRUE; - var_set_ref_object (vars, object); - - return TRUE; +run_is_task_function(const sc_char *pattern, sc_gameref_t game) { + const sc_prop_setref_t bundle = gs_get_bundle(game); + const sc_var_setref_t vars = gs_get_vars(game); + sc_vartype_t vt_key[3]; + sc_int room, object; + sc_char *argument; + + /* Simple comparison against the one known task expression. */ + argument = (sc_char *)sc_malloc(strlen(pattern) + 1); + if (sscanf(pattern, " # %%object%% = getdynfromroom (%[^)])", argument) == 0) { + sc_free(argument); + return FALSE; + } + + /* + * Compare the argument read in against known room names. + * + * TODO Is this simple room name comparison good enough? + */ + vt_key[0].string = "Rooms"; + for (room = 0; room < gs_room_count(game); room++) { + const sc_char *name; + + vt_key[1].integer = room; + vt_key[2].string = "Short"; + name = prop_get_string(bundle, "S<-sis", vt_key); + if (sc_strcasecmp(name, argument) == 0) + break; + } + sc_free(argument); + if (room == gs_room_count(game)) + return FALSE; + + /* + * Select a dynamic object from the room. + * + * TODO What are the selection criteria supposed to be? Here we use "on + * the floor". + */ + vt_key[0].string = "Objects"; + for (object = 0; object < gs_object_count(game); object++) { + sc_bool bstatic; + + vt_key[1].integer = object; + vt_key[2].string = "Static"; + bstatic = prop_get_boolean(bundle, "B<-sis", vt_key); + if (!bstatic && obj_directly_in_room(game, object, room)) + break; + } + if (object == gs_object_count(game)) + return FALSE; + + /* Set this object reference, unambiguously, as if %object% match. */ + gs_clear_object_references(game); + game->object_references[object] = TRUE; + var_set_ref_object(vars, object); + + return TRUE; } /* Structure used to associate a pattern with a handler function. */ -typedef struct sc_commands_s -{ - const sc_char *const command; - sc_bool (*const handler) (sc_gameref_t game); +typedef struct sc_commands_s { + const sc_char *const command; + sc_bool(*const handler)(sc_gameref_t game); } sc_commands_t; typedef sc_commands_t *sc_commandsref_t; /* Movement commands for the four point compass. */ static sc_commands_t MOVE_COMMANDS_4[] = { - {"{go {to {the}}} [north/n]", lib_cmd_go_north}, - {"{go {to {the}}} [east/e]", lib_cmd_go_east}, - {"{go {to {the}}} [south/s]", lib_cmd_go_south}, - {"{go {to {the}}} [west/w]", lib_cmd_go_west}, - {"{go {to {the}}} [up/u]", lib_cmd_go_up}, - {"{go {to {the}}} [down/d]", lib_cmd_go_down}, - {"{go {to {the}}} [in]", lib_cmd_go_in}, - {"{go {to {the}}} [out/o]", lib_cmd_go_out}, - {NULL, NULL} + {"{go {to {the}}} [north/n]", lib_cmd_go_north}, + {"{go {to {the}}} [east/e]", lib_cmd_go_east}, + {"{go {to {the}}} [south/s]", lib_cmd_go_south}, + {"{go {to {the}}} [west/w]", lib_cmd_go_west}, + {"{go {to {the}}} [up/u]", lib_cmd_go_up}, + {"{go {to {the}}} [down/d]", lib_cmd_go_down}, + {"{go {to {the}}} [in]", lib_cmd_go_in}, + {"{go {to {the}}} [out/o]", lib_cmd_go_out}, + {NULL, NULL} }; /* Movement commands for the eight point compass. */ static sc_commands_t MOVE_COMMANDS_8[] = { - {"{go {to {the}}} [north/n]", lib_cmd_go_north}, - {"{go {to {the}}} [east/e]", lib_cmd_go_east}, - {"{go {to {the}}} [south/s]", lib_cmd_go_south}, - {"{go {to {the}}} [west/w]", lib_cmd_go_west}, - {"{go {to {the}}} [up/u]", lib_cmd_go_up}, - {"{go {to {the}}} [down/d]", lib_cmd_go_down}, - {"{go {to {the}}} [in]", lib_cmd_go_in}, - {"{go {to {the}}} [out/o]", lib_cmd_go_out}, - {"{go {to {the}}} [northeast/north-east/ne]", lib_cmd_go_northeast}, - {"{go {to {the}}} [southeast/south-east/se]", lib_cmd_go_southeast}, - {"{go {to {the}}} [northwest/north-west/nw]", lib_cmd_go_northwest}, - {"{go {to {the}}} [southwest/south-west/sw]", lib_cmd_go_southwest}, - {NULL, NULL} + {"{go {to {the}}} [north/n]", lib_cmd_go_north}, + {"{go {to {the}}} [east/e]", lib_cmd_go_east}, + {"{go {to {the}}} [south/s]", lib_cmd_go_south}, + {"{go {to {the}}} [west/w]", lib_cmd_go_west}, + {"{go {to {the}}} [up/u]", lib_cmd_go_up}, + {"{go {to {the}}} [down/d]", lib_cmd_go_down}, + {"{go {to {the}}} [in]", lib_cmd_go_in}, + {"{go {to {the}}} [out/o]", lib_cmd_go_out}, + {"{go {to {the}}} [northeast/north-east/ne]", lib_cmd_go_northeast}, + {"{go {to {the}}} [southeast/south-east/se]", lib_cmd_go_southeast}, + {"{go {to {the}}} [northwest/north-west/nw]", lib_cmd_go_northwest}, + {"{go {to {the}}} [southwest/south-west/sw]", lib_cmd_go_southwest}, + {NULL, NULL} }; /* "Priority" library commands, may take precedence over the game. */ static sc_commands_t PRIORITY_COMMANDS[] = { - /* Acquisition of and disposal of inventory. */ - {"[[get/take/remove/extract] [all/everything] from/empty] %object%", - lib_cmd_take_all_from}, - {"[[get/take/remove/extract] [all/everything] from/empty] %object%" - " [[except/but] {for}/apart from] %text%", - lib_cmd_take_from_except_multiple}, - {"[get/take/remove/extract] [all/everything]" - " [[except/but] {for}/apart from] %text% from %object%", - lib_cmd_take_from_except_multiple}, - {"[get/take/remove/extract] %text% from %object%", - lib_cmd_take_from_multiple}, - {"[get/take] [all/everything] from %character%", lib_cmd_take_all_from_npc}, - {"[get/take] [all/everything] from %character%" - " [[except/but] {for}/apart from] %text%", - lib_cmd_take_from_npc_except_multiple}, - {"[get/take] [all/everything]" - " [[except/but] {for}/apart from] %text% from %character%", - lib_cmd_take_from_npc_except_multiple}, - {"[get/take] %text% from %character%", lib_cmd_take_from_npc_multiple}, - {"[[get/take/pick up] [all/everything]/pick [all/everything] up]", - lib_cmd_take_all}, - {"[get/take/pick up] [all/everything] [[except/but] {for}/apart from] %text%", - lib_cmd_take_except_multiple}, - {"[get/take/pick up] %text%", lib_cmd_take_multiple}, - {"pick %text% up", lib_cmd_take_multiple}, - {"[[drop/put down] [all/everything]/put [all/everything] down]", - lib_cmd_drop_all}, - {"[drop/put down] [all/everything] [[except/but] {for}/apart from] %text%", - lib_cmd_drop_except_multiple}, - {"[drop/put down] %text%", lib_cmd_drop_multiple}, - {"put %text% down", lib_cmd_drop_multiple}, - {NULL, NULL} + /* Acquisition of and disposal of inventory. */ + { + "[[get/take/remove/extract] [all/everything] from/empty] %object%", + lib_cmd_take_all_from + }, + { + "[[get/take/remove/extract] [all/everything] from/empty] %object%" + " [[except/but] {for}/apart from] %text%", + lib_cmd_take_from_except_multiple + }, + { + "[get/take/remove/extract] [all/everything]" + " [[except/but] {for}/apart from] %text% from %object%", + lib_cmd_take_from_except_multiple + }, + { + "[get/take/remove/extract] %text% from %object%", + lib_cmd_take_from_multiple + }, + {"[get/take] [all/everything] from %character%", lib_cmd_take_all_from_npc}, + { + "[get/take] [all/everything] from %character%" + " [[except/but] {for}/apart from] %text%", + lib_cmd_take_from_npc_except_multiple + }, + { + "[get/take] [all/everything]" + " [[except/but] {for}/apart from] %text% from %character%", + lib_cmd_take_from_npc_except_multiple + }, + {"[get/take] %text% from %character%", lib_cmd_take_from_npc_multiple}, + { + "[[get/take/pick up] [all/everything]/pick [all/everything] up]", + lib_cmd_take_all + }, + { + "[get/take/pick up] [all/everything] [[except/but] {for}/apart from] %text%", + lib_cmd_take_except_multiple + }, + {"[get/take/pick up] %text%", lib_cmd_take_multiple}, + {"pick %text% up", lib_cmd_take_multiple}, + { + "[[drop/put down] [all/everything]/put [all/everything] down]", + lib_cmd_drop_all + }, + { + "[drop/put down] [all/everything] [[except/but] {for}/apart from] %text%", + lib_cmd_drop_except_multiple + }, + {"[drop/put down] %text%", lib_cmd_drop_multiple}, + {"put %text% down", lib_cmd_drop_multiple}, + {NULL, NULL} }; /* Standard library commands, other than movement and priority above. */ static sc_commands_t STANDARD_COMMANDS[] = { - /* Inventory, and general investigation of surroundings. */ - {"[inventory/inv/i]", lib_cmd_inventory}, - {"[x/ex/exam/examine/l/look {at}] {{the} [room/location]}", lib_cmd_look}, - {"[x/ex/exam/examine/look {at/in}] %object%", lib_cmd_examine_object}, - {"[x/ex/exam/examine/look {at}] %character%", lib_cmd_examine_npc}, - {"[x/ex/exam/examine/look {at}] [me/self/myself]", lib_cmd_examine_self}, - {"[x/ex/exam/examine/look {at}] all", lib_cmd_examine_all}, - - /* Attempted acquisition of and disposal of NPCs. */ - {"[get/take/pick up] %character%", lib_cmd_take_npc}, - {"pick %character% up", lib_cmd_take_npc}, - - /* Manipulating selected objects. */ - {"put [all/everything] [in/into/inside {of}] %object%", lib_cmd_put_all_in}, - {"put [all/everything] [[except/but] {for}/apart from] %text%" - " [in/into/inside {of}] %object%", lib_cmd_put_in_except_multiple}, - {"put %text% [in/into/inside {of}] %object%", lib_cmd_put_in_multiple}, - {"put [all/everything] [on/onto/on top of] %object%", lib_cmd_put_all_on}, - {"put [all/everything] [[except/but] {for}/apart from] %text%" - " [on/onto/on top of] %object%", lib_cmd_put_on_except_multiple}, - {"put %text% [on/onto/on top of] %object%", lib_cmd_put_on_multiple}, - {"open %object%", lib_cmd_open_object}, - {"close %object%", lib_cmd_close_object}, - {"unlock %object% with %text%", lib_cmd_unlock_object_with}, - {"lock %object% with %text%", lib_cmd_lock_object_with}, - {"unlock %object%", lib_cmd_unlock_object}, - {"lock %object%", lib_cmd_lock_object}, - {"read %object%", lib_cmd_read_object}, - {"read *", lib_cmd_read_other}, - {"give %object% to %character%", lib_cmd_give_object_npc}, - {"sit {down/up} [on/in] %object%", lib_cmd_sit_on_object}, - {"stand {up/down} [on/in] %object%", lib_cmd_stand_on_object}, - {"[lie/lay] on %object%", lib_cmd_lie_on_object}, - {"get {down/up} off %object%", lib_cmd_get_off_object}, - {"get off", lib_cmd_get_off}, - {"sit {down/up} {[on/in] {the} [ground/floor]}", lib_cmd_sit_on_floor}, - {"stand {up/down} {[on/in] {the} [ground/floor]}", lib_cmd_stand_on_floor}, - {"[lie/lay] {down/up} {[on/in] {the} [ground/floor]}", lib_cmd_lie_on_floor}, - {"eat %object%", lib_cmd_eat_object}, - - /* Dressing up, and dressing down. */ - {"[[wear/put on/don] [all/everything]/put [all/everything] on]", - lib_cmd_wear_all}, - {"[wear/put on/don] [all/everything] [[except/but] {for}/apart from] %text%", - lib_cmd_wear_except_multiple}, - {"[wear/put on/don] %text%", lib_cmd_wear_multiple}, - {"put %text% on", lib_cmd_wear_multiple}, - {"[[remove/take off/doff] [all/everything]/take [all/everything] off/strip]", - lib_cmd_remove_all}, - {"[remove/take off/doff] [all/everything]" - " [[except/but] {for}/apart from] %text%", - lib_cmd_remove_except_multiple}, - {"[remove/take off/doff] %text%", lib_cmd_remove_multiple}, - {"take %text% off", lib_cmd_remove_multiple}, - - /* Selected NPC interactions and conversation. */ - {"ask %character% about %text%", lib_cmd_ask_npc_about}, - {"[attack/hit/kick/slap/shoot/stab] %character% with %object%", - lib_cmd_attack_npc_with}, - {"[attack/shoot] %character%", lib_cmd_attack_npc}, - - /* More movement, waiting, and miscellaneous administrative commands. */ - {"[goto/go {to}] %text%", lib_cmd_go_room}, - {"[goto/go {to}] *", lib_cmd_print_room_exits}, - {"[exit/exits/directions/where]", lib_cmd_print_room_exits}, - {"[wait/z] %number%", lib_cmd_wait_number}, - {"[wait/z]", lib_cmd_wait}, - {"save", lib_cmd_save}, - {"[restore/load]", lib_cmd_restore}, - {"restart", lib_cmd_restart}, - {"[again/g]", lib_cmd_again}, - {"[redo /!]%number%", lib_cmd_redo_number}, - {"[redo /!]%text%", lib_cmd_redo_text}, - {"[redo/!]", lib_cmd_redo_last}, - {"[quit/q]", lib_cmd_quit}, - {"turns", lib_cmd_turns}, - {"score", lib_cmd_score}, - {"undo", lib_cmd_undo}, - {"[hist/history] %number%", lib_cmd_history_number}, - {"[hist/history]", lib_cmd_history}, - {"[hint/hints]", lib_cmd_hints}, - {"verbose", lib_cmd_verbose}, - {"brief", lib_cmd_brief}, - {"[notify/notification] %text%", lib_cmd_notify_on_off}, - {"[notify/notification]", lib_cmd_notify}, - {"time", lib_cmd_time}, - {"date", lib_cmd_date}, - {"[help/commands]", lib_cmd_help}, - {"[gpl/license]", lib_cmd_license}, - {"[about/info/information/author]", lib_cmd_information}, - {"[clear/cls/clr]", lib_cmd_clear}, - {"status{line}", lib_cmd_statusline}, - {"version", lib_cmd_version}, - - {"[locate/where {is/are}/find] %object%", lib_cmd_locate_object}, - {"[locate/where {is}/find] %character%", lib_cmd_locate_npc}, - - {"[count/num]", lib_cmd_count}, - - /* Standard response commands; no real action, just output. */ - {"[get/take/pick up] *", lib_cmd_get_what}, - {"open *", lib_cmd_open_what}, - {"close *", lib_cmd_close_other}, - {"give %object% *", lib_cmd_give_object}, - {"give *", lib_cmd_give_what}, - {"lock %text%", lib_cmd_lock_other}, - {"lock", lib_cmd_lock_what}, - {"unlock %text%", lib_cmd_unlock_other}, - {"unlock", lib_cmd_unlock_what}, - {"sit {down/up} [on/in] *", lib_cmd_sit_other}, - {"stand {up/down} [on/in] *", lib_cmd_stand_other}, - {"[lie/lay] {down/up} [on/in] *", lib_cmd_lie_other}, - {"[remove/take off/doff] *", lib_cmd_remove_what}, - {"[drop/put down] *", lib_cmd_drop_what}, - {"[wear/put on/don] *", lib_cmd_wear_what}, - {"[shit/fuck/bastard/cunt/crap/hell/shag/bollocks/bollox/bugger] *", - lib_cmd_profanity}, - {"[x/examine/look {at}] *", lib_cmd_examine_other}, - {"[locate/where {is/are}/find] *", lib_cmd_locate_other}, - {"[cp/mv/ln/ls] *", lib_cmd_unix_like}, - {"dir *", lib_cmd_dos_like}, - {"ask %character% *", lib_cmd_ask_npc}, - {"ask %object% *", lib_cmd_ask_object}, - {"ask *", lib_cmd_ask_other}, - {"block %object% *", lib_cmd_block_object}, - {"block %text%", lib_cmd_block_other}, - {"block", lib_cmd_block_what}, - {"[break/destroy/smash] %object% *", lib_cmd_break_object}, - {"[break/destroy/smash] %text%", lib_cmd_break_other}, - {"break", lib_cmd_break_what}, - {"destroy", lib_cmd_destroy_what}, - {"smash", lib_cmd_smash_what}, - {"buy %object% *", lib_cmd_buy_object}, - {"buy %text%", lib_cmd_buy_other}, - {"buy", lib_cmd_buy_what}, - {"clean %object% *", lib_cmd_clean_object}, - {"clean %text%", lib_cmd_clean_other}, - {"clean", lib_cmd_clean_what}, - {"climb %object% *", lib_cmd_climb_object}, - {"climb %text%", lib_cmd_climb_other}, - {"climb", lib_cmd_climb_what}, - {"cry *", lib_cmd_cry}, - {"cut %object% *", lib_cmd_cut_object}, - {"cut %text%", lib_cmd_cut_other}, - {"cut", lib_cmd_cut_what}, - {"dance *", lib_cmd_dance}, - {"drink %object% *", lib_cmd_drink_object}, - {"drink %text%", lib_cmd_drink_other}, - {"drink", lib_cmd_drink_what}, - {"eat *", lib_cmd_eat_other}, - {"feed *", lib_cmd_feed}, - {"feel *", lib_cmd_feel}, - {"fight *", lib_cmd_fight}, - {"fix %object% *", lib_cmd_fix_object}, - {"fix %text%", lib_cmd_fix_other}, - {"fix", lib_cmd_fix_what}, - {"fly *", lib_cmd_fly}, - {"hint *", lib_cmd_hint}, - {"hit %character%", lib_cmd_attack_npc}, - {"hit %object% *", lib_cmd_hit_object}, - {"hit %text%", lib_cmd_hit_other}, - {"hit", lib_cmd_hit_what}, - {"hum *", lib_cmd_hum}, - {"jump *", lib_cmd_jump}, - {"kick %character%", lib_cmd_attack_npc}, - {"kick %object% *", lib_cmd_kick_object}, - {"kick %text%", lib_cmd_kick_other}, - {"kick", lib_cmd_kick_what}, - {"kiss %character% *", lib_cmd_kiss_npc}, - {"kiss %object% *", lib_cmd_kiss_object}, - {"kiss *", lib_cmd_kiss_other}, - {"kill *", lib_cmd_kill_other}, - {"lift %object% *", lib_cmd_lift_object}, - {"lift %text%", lib_cmd_lift_other}, - {"lift", lib_cmd_lift_what}, - {"light %object% *", lib_cmd_light_object}, - {"light %text%", lib_cmd_light_other}, - {"light", lib_cmd_light_what}, - {"listen *", lib_cmd_listen}, - {"mend %object% *", lib_cmd_mend_object}, - {"mend %text%", lib_cmd_mend_other}, - {"mend", lib_cmd_mend_what}, - {"move %object% *", lib_cmd_move_object}, - {"move %text%", lib_cmd_move_other}, - {"move", lib_cmd_move_what}, - {"please *", lib_cmd_please}, - {"press %object% *", lib_cmd_press_object}, - {"press %text%", lib_cmd_press_other}, - {"press", lib_cmd_press_what}, - {"pull %object% *", lib_cmd_pull_object}, - {"pull %text%", lib_cmd_pull_other}, - {"pull", lib_cmd_pull_what}, - {"punch *", lib_cmd_punch}, - {"push %object% *", lib_cmd_push_object}, - {"push %text%", lib_cmd_push_other}, - {"push", lib_cmd_push_what}, - {"repair %object% *", lib_cmd_repair_object}, - {"repair %text%", lib_cmd_repair_other}, - {"repair", lib_cmd_repair_what}, - {"rub %object% *", lib_cmd_rub_object}, - {"rub %text%", lib_cmd_rub_other}, - {"rub", lib_cmd_rub_what}, - {"run *", lib_cmd_run}, - {"say *", lib_cmd_say}, - {"sell %object% *", lib_cmd_sell_object}, - {"sell %text%", lib_cmd_sell_other}, - {"sell", lib_cmd_sell_what}, - {"shake %object% *", lib_cmd_shake_object}, - {"shake %text%", lib_cmd_shake_other}, - {"shake", lib_cmd_shake_what}, - {"shout *", lib_cmd_shout}, - {"sing *", lib_cmd_sing}, - {"sleep *", lib_cmd_sleep}, - {"smell %object% *", lib_cmd_smell_object}, - {"smell *", lib_cmd_smell_other}, - {"stop %object% *", lib_cmd_stop_object}, - {"stop %text%", lib_cmd_stop_other}, - {"stop", lib_cmd_stop_what}, - {"suck %object% *", lib_cmd_suck_object}, - {"suck %text%", lib_cmd_suck_other}, - {"suck", lib_cmd_suck_what}, - {"talk *", lib_cmd_talk}, - {"thank *", lib_cmd_thank}, - {"turn %object% *", lib_cmd_turn_object}, - {"turn %text%", lib_cmd_turn_other}, - {"turn", lib_cmd_turn_what}, - {"touch %object% *", lib_cmd_touch_object}, - {"touch %text%", lib_cmd_touch_other}, - {"touch", lib_cmd_touch_what}, - {"unblock %object% *", lib_cmd_unblock_object}, - {"unblock %text%", lib_cmd_unblock_other}, - {"unblock", lib_cmd_unblock_what}, - {"wash %object% *", lib_cmd_wash_object}, - {"wash %text%", lib_cmd_wash_other}, - {"wash", lib_cmd_wash_what}, - {"whistle *", lib_cmd_whistle}, - {"[why/when/what/can/how] *", lib_cmd_interrogation}, - {"xyzzy *", lib_cmd_xyzzy}, - {"campbell", lib_cmd_egotistic}, - {"[yes/no] *", lib_cmd_yes_or_no}, - {"* %object% *", lib_cmd_verb_object}, - {"* %character% *", lib_cmd_verb_npc}, - - /* SCARE debugger hook command, placed last just in case... */ - {"{#}debug{ger}", debug_cmd_debugger}, - - {NULL, NULL} + /* Inventory, and general investigation of surroundings. */ + {"[inventory/inv/i]", lib_cmd_inventory}, + {"[x/ex/exam/examine/l/look {at}] {{the} [room/location]}", lib_cmd_look}, + {"[x/ex/exam/examine/look {at/in}] %object%", lib_cmd_examine_object}, + {"[x/ex/exam/examine/look {at}] %character%", lib_cmd_examine_npc}, + {"[x/ex/exam/examine/look {at}] [me/self/myself]", lib_cmd_examine_self}, + {"[x/ex/exam/examine/look {at}] all", lib_cmd_examine_all}, + + /* Attempted acquisition of and disposal of NPCs. */ + {"[get/take/pick up] %character%", lib_cmd_take_npc}, + {"pick %character% up", lib_cmd_take_npc}, + + /* Manipulating selected objects. */ + {"put [all/everything] [in/into/inside {of}] %object%", lib_cmd_put_all_in}, + { + "put [all/everything] [[except/but] {for}/apart from] %text%" + " [in/into/inside {of}] %object%", lib_cmd_put_in_except_multiple + }, + {"put %text% [in/into/inside {of}] %object%", lib_cmd_put_in_multiple}, + {"put [all/everything] [on/onto/on top of] %object%", lib_cmd_put_all_on}, + { + "put [all/everything] [[except/but] {for}/apart from] %text%" + " [on/onto/on top of] %object%", lib_cmd_put_on_except_multiple + }, + {"put %text% [on/onto/on top of] %object%", lib_cmd_put_on_multiple}, + {"open %object%", lib_cmd_open_object}, + {"close %object%", lib_cmd_close_object}, + {"unlock %object% with %text%", lib_cmd_unlock_object_with}, + {"lock %object% with %text%", lib_cmd_lock_object_with}, + {"unlock %object%", lib_cmd_unlock_object}, + {"lock %object%", lib_cmd_lock_object}, + {"read %object%", lib_cmd_read_object}, + {"read *", lib_cmd_read_other}, + {"give %object% to %character%", lib_cmd_give_object_npc}, + {"sit {down/up} [on/in] %object%", lib_cmd_sit_on_object}, + {"stand {up/down} [on/in] %object%", lib_cmd_stand_on_object}, + {"[lie/lay] on %object%", lib_cmd_lie_on_object}, + {"get {down/up} off %object%", lib_cmd_get_off_object}, + {"get off", lib_cmd_get_off}, + {"sit {down/up} {[on/in] {the} [ground/floor]}", lib_cmd_sit_on_floor}, + {"stand {up/down} {[on/in] {the} [ground/floor]}", lib_cmd_stand_on_floor}, + {"[lie/lay] {down/up} {[on/in] {the} [ground/floor]}", lib_cmd_lie_on_floor}, + {"eat %object%", lib_cmd_eat_object}, + + /* Dressing up, and dressing down. */ + { + "[[wear/put on/don] [all/everything]/put [all/everything] on]", + lib_cmd_wear_all + }, + { + "[wear/put on/don] [all/everything] [[except/but] {for}/apart from] %text%", + lib_cmd_wear_except_multiple + }, + {"[wear/put on/don] %text%", lib_cmd_wear_multiple}, + {"put %text% on", lib_cmd_wear_multiple}, + { + "[[remove/take off/doff] [all/everything]/take [all/everything] off/strip]", + lib_cmd_remove_all + }, + { + "[remove/take off/doff] [all/everything]" + " [[except/but] {for}/apart from] %text%", + lib_cmd_remove_except_multiple + }, + {"[remove/take off/doff] %text%", lib_cmd_remove_multiple}, + {"take %text% off", lib_cmd_remove_multiple}, + + /* Selected NPC interactions and conversation. */ + {"ask %character% about %text%", lib_cmd_ask_npc_about}, + { + "[attack/hit/kick/slap/shoot/stab] %character% with %object%", + lib_cmd_attack_npc_with + }, + {"[attack/shoot] %character%", lib_cmd_attack_npc}, + + /* More movement, waiting, and miscellaneous administrative commands. */ + {"[goto/go {to}] %text%", lib_cmd_go_room}, + {"[goto/go {to}] *", lib_cmd_print_room_exits}, + {"[exit/exits/directions/where]", lib_cmd_print_room_exits}, + {"[wait/z] %number%", lib_cmd_wait_number}, + {"[wait/z]", lib_cmd_wait}, + {"save", lib_cmd_save}, + {"[restore/load]", lib_cmd_restore}, + {"restart", lib_cmd_restart}, + {"[again/g]", lib_cmd_again}, + {"[redo /!]%number%", lib_cmd_redo_number}, + {"[redo /!]%text%", lib_cmd_redo_text}, + {"[redo/!]", lib_cmd_redo_last}, + {"[quit/q]", lib_cmd_quit}, + {"turns", lib_cmd_turns}, + {"score", lib_cmd_score}, + {"undo", lib_cmd_undo}, + {"[hist/history] %number%", lib_cmd_history_number}, + {"[hist/history]", lib_cmd_history}, + {"[hint/hints]", lib_cmd_hints}, + {"verbose", lib_cmd_verbose}, + {"brief", lib_cmd_brief}, + {"[notify/notification] %text%", lib_cmd_notify_on_off}, + {"[notify/notification]", lib_cmd_notify}, + {"time", lib_cmd_time}, + {"date", lib_cmd_date}, + {"[help/commands]", lib_cmd_help}, + {"[gpl/license]", lib_cmd_license}, + {"[about/info/information/author]", lib_cmd_information}, + {"[clear/cls/clr]", lib_cmd_clear}, + {"status{line}", lib_cmd_statusline}, + {"version", lib_cmd_version}, + + {"[locate/where {is/are}/find] %object%", lib_cmd_locate_object}, + {"[locate/where {is}/find] %character%", lib_cmd_locate_npc}, + + {"[count/num]", lib_cmd_count}, + + /* Standard response commands; no real action, just output. */ + {"[get/take/pick up] *", lib_cmd_get_what}, + {"open *", lib_cmd_open_what}, + {"close *", lib_cmd_close_other}, + {"give %object% *", lib_cmd_give_object}, + {"give *", lib_cmd_give_what}, + {"lock %text%", lib_cmd_lock_other}, + {"lock", lib_cmd_lock_what}, + {"unlock %text%", lib_cmd_unlock_other}, + {"unlock", lib_cmd_unlock_what}, + {"sit {down/up} [on/in] *", lib_cmd_sit_other}, + {"stand {up/down} [on/in] *", lib_cmd_stand_other}, + {"[lie/lay] {down/up} [on/in] *", lib_cmd_lie_other}, + {"[remove/take off/doff] *", lib_cmd_remove_what}, + {"[drop/put down] *", lib_cmd_drop_what}, + {"[wear/put on/don] *", lib_cmd_wear_what}, + { + "[shit/fuck/bastard/cunt/crap/hell/shag/bollocks/bollox/bugger] *", + lib_cmd_profanity + }, + {"[x/examine/look {at}] *", lib_cmd_examine_other}, + {"[locate/where {is/are}/find] *", lib_cmd_locate_other}, + {"[cp/mv/ln/ls] *", lib_cmd_unix_like}, + {"dir *", lib_cmd_dos_like}, + {"ask %character% *", lib_cmd_ask_npc}, + {"ask %object% *", lib_cmd_ask_object}, + {"ask *", lib_cmd_ask_other}, + {"block %object% *", lib_cmd_block_object}, + {"block %text%", lib_cmd_block_other}, + {"block", lib_cmd_block_what}, + {"[break/destroy/smash] %object% *", lib_cmd_break_object}, + {"[break/destroy/smash] %text%", lib_cmd_break_other}, + {"break", lib_cmd_break_what}, + {"destroy", lib_cmd_destroy_what}, + {"smash", lib_cmd_smash_what}, + {"buy %object% *", lib_cmd_buy_object}, + {"buy %text%", lib_cmd_buy_other}, + {"buy", lib_cmd_buy_what}, + {"clean %object% *", lib_cmd_clean_object}, + {"clean %text%", lib_cmd_clean_other}, + {"clean", lib_cmd_clean_what}, + {"climb %object% *", lib_cmd_climb_object}, + {"climb %text%", lib_cmd_climb_other}, + {"climb", lib_cmd_climb_what}, + {"cry *", lib_cmd_cry}, + {"cut %object% *", lib_cmd_cut_object}, + {"cut %text%", lib_cmd_cut_other}, + {"cut", lib_cmd_cut_what}, + {"dance *", lib_cmd_dance}, + {"drink %object% *", lib_cmd_drink_object}, + {"drink %text%", lib_cmd_drink_other}, + {"drink", lib_cmd_drink_what}, + {"eat *", lib_cmd_eat_other}, + {"feed *", lib_cmd_feed}, + {"feel *", lib_cmd_feel}, + {"fight *", lib_cmd_fight}, + {"fix %object% *", lib_cmd_fix_object}, + {"fix %text%", lib_cmd_fix_other}, + {"fix", lib_cmd_fix_what}, + {"fly *", lib_cmd_fly}, + {"hint *", lib_cmd_hint}, + {"hit %character%", lib_cmd_attack_npc}, + {"hit %object% *", lib_cmd_hit_object}, + {"hit %text%", lib_cmd_hit_other}, + {"hit", lib_cmd_hit_what}, + {"hum *", lib_cmd_hum}, + {"jump *", lib_cmd_jump}, + {"kick %character%", lib_cmd_attack_npc}, + {"kick %object% *", lib_cmd_kick_object}, + {"kick %text%", lib_cmd_kick_other}, + {"kick", lib_cmd_kick_what}, + {"kiss %character% *", lib_cmd_kiss_npc}, + {"kiss %object% *", lib_cmd_kiss_object}, + {"kiss *", lib_cmd_kiss_other}, + {"kill *", lib_cmd_kill_other}, + {"lift %object% *", lib_cmd_lift_object}, + {"lift %text%", lib_cmd_lift_other}, + {"lift", lib_cmd_lift_what}, + {"light %object% *", lib_cmd_light_object}, + {"light %text%", lib_cmd_light_other}, + {"light", lib_cmd_light_what}, + {"listen *", lib_cmd_listen}, + {"mend %object% *", lib_cmd_mend_object}, + {"mend %text%", lib_cmd_mend_other}, + {"mend", lib_cmd_mend_what}, + {"move %object% *", lib_cmd_move_object}, + {"move %text%", lib_cmd_move_other}, + {"move", lib_cmd_move_what}, + {"please *", lib_cmd_please}, + {"press %object% *", lib_cmd_press_object}, + {"press %text%", lib_cmd_press_other}, + {"press", lib_cmd_press_what}, + {"pull %object% *", lib_cmd_pull_object}, + {"pull %text%", lib_cmd_pull_other}, + {"pull", lib_cmd_pull_what}, + {"punch *", lib_cmd_punch}, + {"push %object% *", lib_cmd_push_object}, + {"push %text%", lib_cmd_push_other}, + {"push", lib_cmd_push_what}, + {"repair %object% *", lib_cmd_repair_object}, + {"repair %text%", lib_cmd_repair_other}, + {"repair", lib_cmd_repair_what}, + {"rub %object% *", lib_cmd_rub_object}, + {"rub %text%", lib_cmd_rub_other}, + {"rub", lib_cmd_rub_what}, + {"run *", lib_cmd_run}, + {"say *", lib_cmd_say}, + {"sell %object% *", lib_cmd_sell_object}, + {"sell %text%", lib_cmd_sell_other}, + {"sell", lib_cmd_sell_what}, + {"shake %object% *", lib_cmd_shake_object}, + {"shake %text%", lib_cmd_shake_other}, + {"shake", lib_cmd_shake_what}, + {"shout *", lib_cmd_shout}, + {"sing *", lib_cmd_sing}, + {"sleep *", lib_cmd_sleep}, + {"smell %object% *", lib_cmd_smell_object}, + {"smell *", lib_cmd_smell_other}, + {"stop %object% *", lib_cmd_stop_object}, + {"stop %text%", lib_cmd_stop_other}, + {"stop", lib_cmd_stop_what}, + {"suck %object% *", lib_cmd_suck_object}, + {"suck %text%", lib_cmd_suck_other}, + {"suck", lib_cmd_suck_what}, + {"talk *", lib_cmd_talk}, + {"thank *", lib_cmd_thank}, + {"turn %object% *", lib_cmd_turn_object}, + {"turn %text%", lib_cmd_turn_other}, + {"turn", lib_cmd_turn_what}, + {"touch %object% *", lib_cmd_touch_object}, + {"touch %text%", lib_cmd_touch_other}, + {"touch", lib_cmd_touch_what}, + {"unblock %object% *", lib_cmd_unblock_object}, + {"unblock %text%", lib_cmd_unblock_other}, + {"unblock", lib_cmd_unblock_what}, + {"wash %object% *", lib_cmd_wash_object}, + {"wash %text%", lib_cmd_wash_other}, + {"wash", lib_cmd_wash_what}, + {"whistle *", lib_cmd_whistle}, + {"[why/when/what/can/how] *", lib_cmd_interrogation}, + {"xyzzy *", lib_cmd_xyzzy}, + {"campbell", lib_cmd_egotistic}, + {"[yes/no] *", lib_cmd_yes_or_no}, + {"* %object% *", lib_cmd_verb_object}, + {"* %character% *", lib_cmd_verb_npc}, + + /* SCARE debugger hook command, placed last just in case... */ + {"{#}debug{ger}", debug_cmd_debugger}, + + {NULL, NULL} }; @@ -457,61 +488,53 @@ static sc_commands_t STANDARD_COMMANDS[] = { * object acquisition take precedence over game commands. */ static sc_bool -run_priority_commands (sc_gameref_t game, const sc_char *string) -{ - sc_commandsref_t command; - - for (command = PRIORITY_COMMANDS; command->command; command++) - { - if (uip_match (command->command, string, game)) - { - if (command->handler (game)) - return TRUE; - } - } - - /* Nothing matched match the string. Or if it did, its handler failed. */ - return FALSE; +run_priority_commands(sc_gameref_t game, const sc_char *string) { + sc_commandsref_t command; + + for (command = PRIORITY_COMMANDS; command->command; command++) { + if (uip_match(command->command, string, game)) { + if (command->handler(game)) + return TRUE; + } + } + + /* Nothing matched match the string. Or if it did, its handler failed. */ + return FALSE; } static sc_bool -run_standard_commands (sc_gameref_t game, const sc_char *string) -{ - const sc_prop_setref_t bundle = gs_get_bundle (game); - sc_vartype_t vt_key[2]; - sc_bool eightpointcompass; - sc_commandsref_t command; - - /* Select the appropriate movement commands. */ - vt_key[0].string = "Globals"; - vt_key[1].string = "EightPointCompass"; - eightpointcompass = prop_get_boolean (bundle, "B<-ss", vt_key); - command = eightpointcompass ? MOVE_COMMANDS_8 : MOVE_COMMANDS_4; - - /* - * Search movement commands first, returning TRUE if any matching command - * handler succeeded. Then repeat for standard library commands. - */ - for (; command->command; command++) - { - if (uip_match (command->command, string, game)) - { - if (command->handler (game)) - return TRUE; - } - } - - for (command = STANDARD_COMMANDS; command->command; command++) - { - if (uip_match (command->command, string, game)) - { - if (command->handler (game)) - return TRUE; - } - } - - /* Nothing matched match the string. Or if it did, its handler failed. */ - return FALSE; +run_standard_commands(sc_gameref_t game, const sc_char *string) { + const sc_prop_setref_t bundle = gs_get_bundle(game); + sc_vartype_t vt_key[2]; + sc_bool eightpointcompass; + sc_commandsref_t command; + + /* Select the appropriate movement commands. */ + vt_key[0].string = "Globals"; + vt_key[1].string = "EightPointCompass"; + eightpointcompass = prop_get_boolean(bundle, "B<-ss", vt_key); + command = eightpointcompass ? MOVE_COMMANDS_8 : MOVE_COMMANDS_4; + + /* + * Search movement commands first, returning TRUE if any matching command + * handler succeeded. Then repeat for standard library commands. + */ + for (; command->command; command++) { + if (uip_match(command->command, string, game)) { + if (command->handler(game)) + return TRUE; + } + } + + for (command = STANDARD_COMMANDS; command->command; command++) { + if (uip_match(command->command, string, game)) { + if (command->handler(game)) + return TRUE; + } + } + + /* Nothing matched match the string. Or if it did, its handler failed. */ + return FALSE; } @@ -521,43 +544,40 @@ run_standard_commands (sc_gameref_t game, const sc_char *string) * Update the game's current room and status line strings. */ static void -run_update_status (sc_gameref_t game) -{ - const sc_prop_setref_t bundle = gs_get_bundle (game); - const sc_var_setref_t vars = gs_get_vars (game); - sc_vartype_t vt_key[2]; - const sc_char *name, *status; - sc_char *filtered; - sc_bool statusbox; - - /* Get the current room name, and filter and untag it. */ - name = lib_get_room_name (game, gs_playerroom (game)); - filtered = pf_filter (name, vars, bundle); - pf_strip_tags (filtered); - - /* Free any existing room name, then save this room name. */ - sc_free (game->current_room_name); - game->current_room_name = filtered; - - /* See if the game does a status box. */ - vt_key[0].string = "Globals"; - vt_key[1].string = "StatusBox"; - statusbox = prop_get_boolean (bundle, "B<-ss", vt_key); - if (statusbox) - { - /* Get the status line, and filter and untag it. */ - vt_key[1].string = "StatusBoxText"; - status = prop_get_string (bundle, "S<-ss", vt_key); - filtered = pf_filter (status, vars, bundle); - pf_strip_tags (filtered); - } - else - /* No status line, so use NULL. */ - filtered = NULL; - - /* Free any existing status line, then save this status text. */ - sc_free (game->status_line); - game->status_line = filtered; +run_update_status(sc_gameref_t game) { + const sc_prop_setref_t bundle = gs_get_bundle(game); + const sc_var_setref_t vars = gs_get_vars(game); + sc_vartype_t vt_key[2]; + const sc_char *name, *status; + sc_char *filtered; + sc_bool statusbox; + + /* Get the current room name, and filter and untag it. */ + name = lib_get_room_name(game, gs_playerroom(game)); + filtered = pf_filter(name, vars, bundle); + pf_strip_tags(filtered); + + /* Free any existing room name, then save this room name. */ + sc_free(game->current_room_name); + game->current_room_name = filtered; + + /* See if the game does a status box. */ + vt_key[0].string = "Globals"; + vt_key[1].string = "StatusBox"; + statusbox = prop_get_boolean(bundle, "B<-ss", vt_key); + if (statusbox) { + /* Get the status line, and filter and untag it. */ + vt_key[1].string = "StatusBoxText"; + status = prop_get_string(bundle, "S<-ss", vt_key); + filtered = pf_filter(status, vars, bundle); + pf_strip_tags(filtered); + } else + /* No status line, so use NULL. */ + filtered = NULL; + + /* Free any existing status line, then save this status text. */ + sc_free(game->status_line); + game->status_line = filtered; } @@ -570,36 +590,32 @@ run_update_status (sc_gameref_t game) * output ahead of buffered printfilter text. */ static void -run_notify_score_change (sc_gameref_t game) -{ - const sc_gameref_t undo = game->undo; - sc_char buffer[32]; - assert (gs_is_game_valid (undo)); - - /* - * Do nothing if no undo available, or if notification is off, or if we've - * already done this once this turn. - */ - if (!game->undo_available - || !game->notify_score_change || game->has_notified) - return; - - /* Note any change in the score. */ - if (game->score > undo->score) - { - if_print_string ("(Your score has increased by "); - sprintf (buffer, "%ld", game->score - undo->score); - if_print_string (buffer); - if_print_string (")\n"); - } - else if (game->score < undo->score) - { - if_print_string ("(Your score has decreased by "); - sprintf (buffer, "%ld", undo->score - game->score); - if_print_string (buffer); - if_print_string (")\n"); - } - game->has_notified = TRUE; +run_notify_score_change(sc_gameref_t game) { + const sc_gameref_t undo = game->undo; + sc_char buffer[32]; + assert(gs_is_game_valid(undo)); + + /* + * Do nothing if no undo available, or if notification is off, or if we've + * already done this once this turn. + */ + if (!game->undo_available + || !game->notify_score_change || game->has_notified) + return; + + /* Note any change in the score. */ + if (game->score > undo->score) { + if_print_string("(Your score has increased by "); + sprintf(buffer, "%ld", game->score - undo->score); + if_print_string(buffer); + if_print_string(")\n"); + } else if (game->score < undo->score) { + if_print_string("(Your score has decreased by "); + sprintf(buffer, "%ld", undo->score - game->score); + if_print_string(buffer); + if_print_string(")\n"); + } + game->has_notified = TRUE; } @@ -615,81 +631,73 @@ run_notify_score_change (sc_gameref_t game) * are selected by 'forwards'. */ static sc_bool -run_match_task_common (sc_gameref_t game, - sc_int task, const sc_char *string, sc_bool forwards, - sc_bool is_library, sc_bool is_normal) -{ - const sc_prop_setref_t bundle = gs_get_bundle (game); - sc_vartype_t vt_key[4]; - sc_int command_count, command; - sc_bool is_matched; - - /* Get the count of task commands. */ - vt_key[0].string = "Tasks"; - vt_key[1].integer = task; - vt_key[2].string = forwards ? "Command" : "ReverseCommand"; - command_count = prop_get_child_count (bundle, "I<-sis", vt_key); - - /* Iterate over commands, looking for patterns that match string. */ - is_matched = FALSE; - for (command = 0; command < command_count; command++) - { - const sc_char *pattern; - sc_int first; - - /* Retrieve the pattern for this command, find its first character. */ - vt_key[3].integer = command; - pattern = prop_get_string (bundle, "S<-sisi", vt_key); - first = strspn (pattern, WHITESPACE); - - /* Match using either the parser, or the special function matcher. */ - if (is_normal) - { - if (pattern[first] != SPECIAL_PATTERN) - { - /* - * Make a special case of library calls and commands that begin - * with a wildcard; these we ignore for this match attempt. - */ - if (is_library && pattern[first] == WILDCARD_PATTERN) - is_matched = FALSE; - else - is_matched = uip_match (pattern, string, game); - } - } - else - { - if (pattern[first] == SPECIAL_PATTERN) - is_matched = run_is_task_function (pattern, game); - } - - /* Stop searching if we find a match. */ - if (is_matched) - break; - } - - /* Return TRUE if we found a pattern match. */ - return is_matched; +run_match_task_common(sc_gameref_t game, + sc_int task, const sc_char *string, sc_bool forwards, + sc_bool is_library, sc_bool is_normal) { + const sc_prop_setref_t bundle = gs_get_bundle(game); + sc_vartype_t vt_key[4]; + sc_int command_count, command; + sc_bool is_matched; + + /* Get the count of task commands. */ + vt_key[0].string = "Tasks"; + vt_key[1].integer = task; + vt_key[2].string = forwards ? "Command" : "ReverseCommand"; + command_count = prop_get_child_count(bundle, "I<-sis", vt_key); + + /* Iterate over commands, looking for patterns that match string. */ + is_matched = FALSE; + for (command = 0; command < command_count; command++) { + const sc_char *pattern; + sc_int first; + + /* Retrieve the pattern for this command, find its first character. */ + vt_key[3].integer = command; + pattern = prop_get_string(bundle, "S<-sisi", vt_key); + first = strspn(pattern, WHITESPACE); + + /* Match using either the parser, or the special function matcher. */ + if (is_normal) { + if (pattern[first] != SPECIAL_PATTERN) { + /* + * Make a special case of library calls and commands that begin + * with a wildcard; these we ignore for this match attempt. + */ + if (is_library && pattern[first] == WILDCARD_PATTERN) + is_matched = FALSE; + else + is_matched = uip_match(pattern, string, game); + } + } else { + if (pattern[first] == SPECIAL_PATTERN) + is_matched = run_is_task_function(pattern, game); + } + + /* Stop searching if we find a match. */ + if (is_matched) + break; + } + + /* Return TRUE if we found a pattern match. */ + return is_matched; } static sc_bool -run_match_task_commands (sc_gameref_t game, - sc_int task, const sc_char *string, - sc_bool forwards, sc_bool is_library) -{ - /* - * Match tasks using the normal pattern matcher, with or without any note - * about whether the call is from the library. - */ - return run_match_task_common (game, task, string, forwards, is_library, TRUE); +run_match_task_commands(sc_gameref_t game, + sc_int task, const sc_char *string, + sc_bool forwards, sc_bool is_library) { + /* + * Match tasks using the normal pattern matcher, with or without any note + * about whether the call is from the library. + */ + return run_match_task_common(game, task, string, forwards, is_library, TRUE); } static sc_bool -run_match_task_functions (sc_gameref_t game, - sc_int task, const sc_char *string, sc_bool forwards) -{ - /* Match tasks against "task command functions". */ - return run_match_task_common (game, task, string, forwards, FALSE, FALSE); +run_match_task_functions(sc_gameref_t game, + sc_int task, const sc_char *string, sc_bool forwards) { + /* Match tasks against "task command functions". */ + return run_match_task_common(game, task, string, forwards, FALSE, FALSE); } @@ -706,46 +714,42 @@ run_match_task_functions (sc_gameref_t game, * and don't change state. */ static sc_bool -run_task_is_unrestricted (sc_gameref_t game, sc_int task) -{ - sc_bool restrictions_passed; - const sc_char *fail_message; - - /* - * Evaluate task restrictions, and if they fail to parse for some reason, - * return as if restrictions did not pass. - */ - if (!restr_eval_task_restrictions (game, task, - &restrictions_passed, &fail_message)) - { - sc_error ("run_task_is_unrestricted: restrictions error, %ld\n", task); - return FALSE; - } - - /* Return TRUE if the task is unrestricted. */ - return restrictions_passed; +run_task_is_unrestricted(sc_gameref_t game, sc_int task) { + sc_bool restrictions_passed; + const sc_char *fail_message; + + /* + * Evaluate task restrictions, and if they fail to parse for some reason, + * return as if restrictions did not pass. + */ + if (!restr_eval_task_restrictions(game, task, + &restrictions_passed, &fail_message)) { + sc_error("run_task_is_unrestricted: restrictions error, %ld\n", task); + return FALSE; + } + + /* Return TRUE if the task is unrestricted. */ + return restrictions_passed; } static sc_bool -run_task_is_loudly_restricted (sc_gameref_t game, sc_int task) -{ - sc_bool restrictions_passed; - const sc_char *fail_message; - - /* - * Evaluate task restrictions, and if they fail to parse for some reason, - * return as if restrictions did not pass. - */ - if (!restr_eval_task_restrictions (game, task, - &restrictions_passed, &fail_message)) - { - sc_error ("run_task_is_loudly_restricted:" - " restrictions error, %ld\n", task); - return TRUE; - } - - /* Return TRUE if the task is restricted and indicates why. */ - return !restrictions_passed && (fail_message != NULL); +run_task_is_loudly_restricted(sc_gameref_t game, sc_int task) { + sc_bool restrictions_passed; + const sc_char *fail_message; + + /* + * Evaluate task restrictions, and if they fail to parse for some reason, + * return as if restrictions did not pass. + */ + if (!restr_eval_task_restrictions(game, task, + &restrictions_passed, &fail_message)) { + sc_error("run_task_is_loudly_restricted:" + " restrictions error, %ld\n", task); + return TRUE; + } + + /* Return TRUE if the task is restricted and indicates why. */ + return !restrictions_passed && (fail_message != NULL); } @@ -785,134 +789,119 @@ run_task_is_loudly_restricted (sc_gameref_t game, sc_int task) * handlers and get_all/drop_all handlers. No pressure, then. */ static sc_bool -run_game_commands_common (sc_gameref_t game, const sc_char *string, - sc_bool include_restrictions, sc_bool is_library) -{ - sc_bool is_matched = FALSE, is_handled = FALSE; - sc_bool *is_matching; - sc_int task_count, task, direction; - - /* - * Matching is expensive, so it helps to use a cache of results from the - * first loop in the second. If we're using the second, that is. - */ - task_count = gs_task_count (game); - if (include_restrictions) - { - is_matching = (sc_bool *)sc_malloc (task_count * sizeof (*is_matching)); - memset (is_matching, FALSE, task_count * sizeof (*is_matching)); - } - else - is_matching = NULL; - - /* - * Iterate over every task, ignoring those not runnable. For each runnable - * task, try matching task commands, and on matches, check restrictions and - * if they pass, try running the task. - */ - for (task = 0; task < task_count; task++) - { - if (!task_can_run_task (game, task)) - continue; - - /* - * Try matching forwards and reverse commands. If there's a match for - * unrestricted tasks, run the task, and if it runs (defined as printing - * some game output), we're done; otherwise, note the command match but - * keep searching for other possible matches. - */ - for (direction = 0; direction < 2; direction++) - { - const sc_bool is_forwards = !direction; - - if (task_can_run_task_directional (game, task, is_forwards) - && run_match_task_commands (game, task, string, - is_forwards, is_library)) - { - if (run_task_is_unrestricted (game, task)) - { - if (task_run_task (game, task, is_forwards)) - is_handled = TRUE; - is_matched = TRUE; - break; - } - - if (is_matching) - is_matching[task] = TRUE; - } - } - if (is_matched) - break; - } - - /* - * If no match, and we've been asked to consider failing restrictions, look - * through all of the runnable tasks again, this time searching for - * restricted ones with a fail message. Use the cache built above to weed - * out matches that are certain to fail. - */ - if (!is_handled && !is_matched && include_restrictions) - { - for (task = 0; task < task_count; task++) - { - if (!is_matching[task] || !task_can_run_task (game, task)) - continue; - - /* - * Check matches of forwards and reverse commands. If there's a - * match for restricted tasks (ones that have and will print a fail - * message if we try to run them), run the task to get the print of - * the fail message, and we're done. - */ - for (direction = 0; direction < 2; direction++) - { - const sc_bool is_forwards = !direction; - - if (task_can_run_task_directional (game, task, is_forwards) - && run_match_task_commands (game, task, string, - is_forwards, is_library)) - { - if (run_task_is_loudly_restricted (game, task)) - { - if (task_run_task (game, task, is_forwards)) - { - is_handled = TRUE; - break; - } - } - } - } - if (is_handled) - break; - } - } - - /* Return TRUE if any game task handled the command in some way. */ - sc_free (is_matching); - return is_handled; +run_game_commands_common(sc_gameref_t game, const sc_char *string, + sc_bool include_restrictions, sc_bool is_library) { + sc_bool is_matched = FALSE, is_handled = FALSE; + sc_bool *is_matching; + sc_int task_count, task, direction; + + /* + * Matching is expensive, so it helps to use a cache of results from the + * first loop in the second. If we're using the second, that is. + */ + task_count = gs_task_count(game); + if (include_restrictions) { + is_matching = (sc_bool *)sc_malloc(task_count * sizeof(*is_matching)); + memset(is_matching, FALSE, task_count * sizeof(*is_matching)); + } else + is_matching = NULL; + + /* + * Iterate over every task, ignoring those not runnable. For each runnable + * task, try matching task commands, and on matches, check restrictions and + * if they pass, try running the task. + */ + for (task = 0; task < task_count; task++) { + if (!task_can_run_task(game, task)) + continue; + + /* + * Try matching forwards and reverse commands. If there's a match for + * unrestricted tasks, run the task, and if it runs (defined as printing + * some game output), we're done; otherwise, note the command match but + * keep searching for other possible matches. + */ + for (direction = 0; direction < 2; direction++) { + const sc_bool is_forwards = !direction; + + if (task_can_run_task_directional(game, task, is_forwards) + && run_match_task_commands(game, task, string, + is_forwards, is_library)) { + if (run_task_is_unrestricted(game, task)) { + if (task_run_task(game, task, is_forwards)) + is_handled = TRUE; + is_matched = TRUE; + break; + } + + if (is_matching) + is_matching[task] = TRUE; + } + } + if (is_matched) + break; + } + + /* + * If no match, and we've been asked to consider failing restrictions, look + * through all of the runnable tasks again, this time searching for + * restricted ones with a fail message. Use the cache built above to weed + * out matches that are certain to fail. + */ + if (!is_handled && !is_matched && include_restrictions) { + for (task = 0; task < task_count; task++) { + if (!is_matching[task] || !task_can_run_task(game, task)) + continue; + + /* + * Check matches of forwards and reverse commands. If there's a + * match for restricted tasks (ones that have and will print a fail + * message if we try to run them), run the task to get the print of + * the fail message, and we're done. + */ + for (direction = 0; direction < 2; direction++) { + const sc_bool is_forwards = !direction; + + if (task_can_run_task_directional(game, task, is_forwards) + && run_match_task_commands(game, task, string, + is_forwards, is_library)) { + if (run_task_is_loudly_restricted(game, task)) { + if (task_run_task(game, task, is_forwards)) { + is_handled = TRUE; + break; + } + } + } + } + if (is_handled) + break; + } + } + + /* Return TRUE if any game task handled the command in some way. */ + sc_free(is_matching); + return is_handled; } static sc_bool -run_game_commands_in_parser_context (sc_gameref_t game, const sc_char *string, - sc_bool include_restrictions) -{ - /* - * Try game commands, either with or without restrictions, and all full and - * complete parse matching (no special case for game commands that begin - * with a '*' wildcard). - */ - return run_game_commands_common (game, string, include_restrictions, FALSE); +run_game_commands_in_parser_context(sc_gameref_t game, const sc_char *string, + sc_bool include_restrictions) { + /* + * Try game commands, either with or without restrictions, and all full and + * complete parse matching (no special case for game commands that begin + * with a '*' wildcard). + */ + return run_game_commands_common(game, string, include_restrictions, FALSE); } static sc_bool -run_game_commands_in_library_context (sc_gameref_t game, const sc_char *string) -{ - /* - * Try game commands, including restrictions, and noting that this is a - * library call so that the parse matcher can exclude game commands that - * begin with a '*' wildcard. - */ - return run_game_commands_common (game, string, TRUE, TRUE); +run_game_commands_in_library_context(sc_gameref_t game, const sc_char *string) { + /* + * Try game commands, including restrictions, and noting that this is a + * library call so that the parse matcher can exclude game commands that + * begin with a '*' wildcard. + */ + return run_game_commands_common(game, string, TRUE, TRUE); } @@ -924,33 +913,29 @@ run_game_commands_in_library_context (sc_gameref_t game, const sc_char *string) * command matches, so we try them as a separate action. */ static void -run_game_functions (sc_gameref_t game, const sc_char *string) -{ - sc_int task_count, task, direction; - - /* Iterate over every task, ignoring those not runnable. */ - task_count = gs_task_count (game); - for (task = 0; task < task_count; task++) - { - if (!task_can_run_task (game, task)) - continue; - - /* - * Try matching forwards and reverse commands. I don't know if it's - * valid to put a function in a reverse command, but nevertheless... - */ - for (direction = 0; direction < 2; direction++) - { - const sc_bool is_forwards = !direction; - - if (task_can_run_task_directional (game, task, is_forwards) - && run_match_task_functions (game, task, string, is_forwards)) - { - if (run_task_is_unrestricted (game, task)) - task_run_task (game, task, is_forwards); - } - } - } +run_game_functions(sc_gameref_t game, const sc_char *string) { + sc_int task_count, task, direction; + + /* Iterate over every task, ignoring those not runnable. */ + task_count = gs_task_count(game); + for (task = 0; task < task_count; task++) { + if (!task_can_run_task(game, task)) + continue; + + /* + * Try matching forwards and reverse commands. I don't know if it's + * valid to put a function in a reverse command, but nevertheless... + */ + for (direction = 0; direction < 2; direction++) { + const sc_bool is_forwards = !direction; + + if (task_can_run_task_directional(game, task, is_forwards) + && run_match_task_functions(game, task, string, is_forwards)) { + if (run_task_is_unrestricted(game, task)) + task_run_task(game, task, is_forwards); + } + } + } } @@ -963,73 +948,70 @@ run_game_functions (sc_gameref_t game, const sc_char *string) * game commands that override standard actions. */ static sc_bool -run_all_commands (sc_gameref_t game, const sc_char *string) -{ - const sc_prop_setref_t bundle = gs_get_bundle (game); - sc_bool status; - - /* - * Adrift command matching is just weird, perhaps broken. In theory, a - * game can override system commands with a properly constructed task and - * set of command matchers. However, the Runner isn't terribly consistent - * in when this will work and when not, and some games rely on that in- - * consistency. In particular, a game with a "* object" task that has - * failing restrictions will not be able to override the system's "take - * object", whereas a game's "take object", under the same circumstances, - * will. Yet if the restrictions pass, a game's "* object" overrides the - * system's "take object" with no apparent difficulty. - * - * For example, "The Woods Are Dark" has a "* ball *" task with the - * restriction "must be holding ball". Without special casing it, there's - * no way to get the ball in the first place. - * - * Trying to find the right way to do things here, then, has been tricky. - * Here's the current process: First, run game commands, ignoring any - * cases where restrictions fail to let the task run. Next, try "priority" - * system commands; ones that move objects to inventory. These system - * commands will call back into trying game commands for objects taken or - * dropped, and in those tries, allow overrides only if the game task is - * explicit about what it's doing (that is, doesn't start with "*"), and - * handle restrictions in those tries. After that, retry all game commands - * again with restrictions enabled. And finally, try all other standard - * library commands. - * - * TODO This is the fourth or fifth attempt at getting this to match the - * Runner, which is surprisingly inconsistent in this area. What on earth - * is the real behavior supposed to be? - */ - status = run_game_commands_in_parser_context (game, string, FALSE); - if (!status) - status = run_priority_commands (game, string); - if (!status) - status = run_game_commands_in_parser_context (game, string, TRUE); - if (!status) - status = run_standard_commands (game, string); - - /* - * For version 4.0 games, it seems that if any command succeeded, we need - * need to scan for and run any matching "task command functions", in - * addition to anything done above. - */ - if (status && !game->is_admin) - { - sc_vartype_t vt_key; - sc_int version; - - /* Check "task command functions" for version 4.0 only. */ - vt_key.string = "Version"; - version = prop_get_integer (bundle, "I<-s", &vt_key); - if (version == TAF_VERSION_400) - run_game_functions (game, string); - } - - return status; +run_all_commands(sc_gameref_t game, const sc_char *string) { + const sc_prop_setref_t bundle = gs_get_bundle(game); + sc_bool status; + + /* + * Adrift command matching is just weird, perhaps broken. In theory, a + * game can override system commands with a properly constructed task and + * set of command matchers. However, the Runner isn't terribly consistent + * in when this will work and when not, and some games rely on that in- + * consistency. In particular, a game with a "* object" task that has + * failing restrictions will not be able to override the system's "take + * object", whereas a game's "take object", under the same circumstances, + * will. Yet if the restrictions pass, a game's "* object" overrides the + * system's "take object" with no apparent difficulty. + * + * For example, "The Woods Are Dark" has a "* ball *" task with the + * restriction "must be holding ball". Without special casing it, there's + * no way to get the ball in the first place. + * + * Trying to find the right way to do things here, then, has been tricky. + * Here's the current process: First, run game commands, ignoring any + * cases where restrictions fail to let the task run. Next, try "priority" + * system commands; ones that move objects to inventory. These system + * commands will call back into trying game commands for objects taken or + * dropped, and in those tries, allow overrides only if the game task is + * explicit about what it's doing (that is, doesn't start with "*"), and + * handle restrictions in those tries. After that, retry all game commands + * again with restrictions enabled. And finally, try all other standard + * library commands. + * + * TODO This is the fourth or fifth attempt at getting this to match the + * Runner, which is surprisingly inconsistent in this area. What on earth + * is the real behavior supposed to be? + */ + status = run_game_commands_in_parser_context(game, string, FALSE); + if (!status) + status = run_priority_commands(game, string); + if (!status) + status = run_game_commands_in_parser_context(game, string, TRUE); + if (!status) + status = run_standard_commands(game, string); + + /* + * For version 4.0 games, it seems that if any command succeeded, we need + * need to scan for and run any matching "task command functions", in + * addition to anything done above. + */ + if (status && !game->is_admin) { + sc_vartype_t vt_key; + sc_int version; + + /* Check "task command functions" for version 4.0 only. */ + vt_key.string = "Version"; + version = prop_get_integer(bundle, "I<-s", &vt_key); + if (version == TAF_VERSION_400) + run_game_functions(game, string); + } + + return status; } sc_bool -run_game_task_commands (sc_gameref_t game, const sc_char *string) -{ - return run_game_commands_in_library_context (game, string); +run_game_task_commands(sc_gameref_t game, const sc_char *string) { + return run_game_commands_in_library_context(game, string); } @@ -1051,238 +1033,219 @@ run_game_task_commands (sc_gameref_t game, const sc_char *string) * just return. Sorry about the ugliness. */ static sc_bool -run_player_input (sc_gameref_t game) -{ - static sc_char line_buffer[LINE_BUFFER_SIZE]; - static sc_char prior_element[LINE_BUFFER_SIZE]; - static sc_char line_element[LINE_BUFFER_SIZE]; - - const sc_filterref_t filter = gs_get_filter (game); - const sc_prop_setref_t bundle = gs_get_bundle (game); - const sc_var_setref_t vars = gs_get_vars (game); - const sc_memo_setref_t memento = gs_get_memento (game); - sc_bool is_rerunning, was_undo_available, status; - sc_char *filtered, *replaced; - const sc_char *command; - - /* Special case; reset statics if the game isn't running. */ - if (!game->is_running) - { - memset (line_buffer, NUL, sizeof (line_buffer)); - memset (prior_element, NUL, sizeof (prior_element)); - memset (line_element, NUL, sizeof (line_element)); - return TRUE; - } - - /* - * Save the settings of the game's do_again and undo_available flags for - * later checks. - */ - is_rerunning = game->do_again; - was_undo_available = game->undo_available; - - /* See if the player asked to rerun a command element. */ - if (game->do_again) - { - game->do_again = FALSE; - - /* Check there is a last element to repeat. */ - if (prior_element[0] == NUL) - { - pf_buffer_string (filter, "You can hardly repeat that.\n"); - return FALSE; - } - - /* Make the last element the current input element. */ - strcpy (line_element, prior_element); - } - else - { - sc_int length, extent; - - /* - * If there's none buffered, read a new line of player input. Other- - * wise, separate output so far with a newline. - */ - if (line_buffer[0] == NUL) - if_read_line (line_buffer, sizeof (line_buffer)); - else - if_print_character ('\n'); - - /* - * Find the length of the next input line element. Unless the line - * buffer is empty, we always take the first character, even if it's a - * separator. This catches odd input like "." and turns it into a - * parser complaint, rather than treating it as two empty commands with - * a separator between them; this makes it close to what Inform does - * with similar inputs. - */ - length = (line_buffer[0] == NUL) ? 0 : 1; - while (line_buffer[length] != NUL - && strchr (SEPARATORS, line_buffer[length]) == NULL) - length++; - - /* - * Make this the current input element, and remove it, the separator, - * and any trailing whitespace, from the front of the line buffer. - * Removing whitespace prevents "i. ." looking like "i" and ""; it - * instead looks like "i" and ".", and results in a parser complaint. - */ - memcpy (line_element, line_buffer, length); - line_element[length] = NUL; - - extent = length; - extent += (line_buffer[length] == NUL - || strchr (SEPARATORS, line_buffer[length]) == NULL) ? 0 : 1; - extent += strspn (line_buffer + extent, WHITESPACE); - memmove (line_buffer, - line_buffer + extent, strlen (line_buffer) - extent + 1); - } - - /* Copy the current game to the temporary undo buffer. */ - gs_copy (game->temporary, game); - - /* Filter the input element for synonyms, then for pronouns. */ - filtered = pf_filter_input (line_element, bundle); - replaced = uip_replace_pronouns (game, filtered ? filtered : line_element); - - /* - * If filtering didn't replace synonyms, or no pronouns were replaced, use - * the original line element. - */ - command = replaced ? sc_normalize_string (replaced) - : (filtered ? sc_normalize_string (filtered) : line_element); - if (command != line_element) - { - if_print_tag (SC_TAG_ITALICS, ""); - if_print_character ('['); - if_print_string (command); - if_print_character (']'); - if_print_tag (SC_TAG_ENDITALICS, ""); - if_print_character ('\n'); - } - - /* Try the command line element against command matchers. */ - status = run_all_commands (game, command); - if (!status) - { - /* Only complain on non-empty command input line elements. */ - if (!sc_strempty (command)) - { - sc_vartype_t vt_key[2]; - sc_char *escaped; - const sc_char *message; - - /* Command line element not understood. */ - escaped = pf_escape (sc_normalize_string (line_element)); - var_set_ref_text (vars, escaped); - sc_free (escaped); - vt_key[0].string = "Globals"; - vt_key[1].string = "DontUnderstand"; - message = prop_get_string (bundle, "S<-ss", vt_key); - pf_buffer_string (filter, message); - pf_buffer_character (filter, '\n'); - - /* - * On a line element that's not understood, throw out any remaining - * input line elements. - */ - line_buffer[0] = NUL; - sc_free (filtered); - sc_free (replaced); - return status; - } - } - else - { - /* - * Unless administrative, back up any valid undo, copy the temporary - * game into the undo buffer, flag the undo buffer as available, and - * assign any pronouns used in the command ready for the next iteration. - */ - if (!game->is_admin) - { - if (game->undo_available) - memo_save_game (memento, game->undo); - - gs_copy (game->undo, game->temporary); - game->undo_available = TRUE; - - uip_assign_pronouns (game, command); - } - } - sc_free (filtered); - sc_free (replaced); - - /* - * If do_again is set, we'll come round with the prior command in line - * element in a moment, so save nothing for that case. Otherwise save the - * command in the history. - */ - if (!sc_strempty (line_element) && !game->do_again) - { - /* - * If this is a failed redo, redo_sequence will be set but do_again will - * be clear. Suppress the save for this special case; otherwise, failed - * redo commands get into the history, where they can cause problems - * later on. - */ - if (game->redo_sequence == 0) - { - sc_int timestamp; - - timestamp = var_get_elapsed_seconds (vars); - memo_save_command (memento, line_element, timestamp, game->turns); - } - else - game->redo_sequence = 0; - } - - /* - * Special case restart and restore commands; throw out any remaining input - * and return straight away. Do the same if this was an undo, detected by - * noting that undo is no longer available, where it was on entry. - */ - if (game->do_restart || game->do_restore - || (was_undo_available && !game->undo_available)) - { - line_buffer[0] = NUL; - return status; - } - - /* If not empty, consider as saving for "again" calls and in the history. */ - if (!sc_strempty (line_element)) - { - /* - * Unless "again", note this line element as prior input. "Again" shows - * up as do_again set in the game, where it wasn't when we entered here. - */ - if (!game->do_again && !is_rerunning) - strcpy (prior_element, line_element); - - /* - * If this was a request to run a command from the history, copy that - * command into the prior_element for the next iteration. The library - * should have verified the value in redo_sequence, so fetching the - * command string should not fail. - */ - if (game->do_again && game->redo_sequence != 0) - { - const sc_char *redo_command; - - redo_command = memo_find_command (memento, game->redo_sequence); - if (redo_command) - strcpy (prior_element, redo_command); - else - { - sc_error ("run_player_input: invalid redo sequence request\n"); - game->do_again = FALSE; - } - game->redo_sequence = 0; - } - } - - return status; +run_player_input(sc_gameref_t game) { + static sc_char line_buffer[LINE_BUFFER_SIZE]; + static sc_char prior_element[LINE_BUFFER_SIZE]; + static sc_char line_element[LINE_BUFFER_SIZE]; + + const sc_filterref_t filter = gs_get_filter(game); + const sc_prop_setref_t bundle = gs_get_bundle(game); + const sc_var_setref_t vars = gs_get_vars(game); + const sc_memo_setref_t memento = gs_get_memento(game); + sc_bool is_rerunning, was_undo_available, status; + sc_char *filtered, *replaced; + const sc_char *command; + + /* Special case; reset statics if the game isn't running. */ + if (!game->is_running) { + memset(line_buffer, NUL, sizeof(line_buffer)); + memset(prior_element, NUL, sizeof(prior_element)); + memset(line_element, NUL, sizeof(line_element)); + return TRUE; + } + + /* + * Save the settings of the game's do_again and undo_available flags for + * later checks. + */ + is_rerunning = game->do_again; + was_undo_available = game->undo_available; + + /* See if the player asked to rerun a command element. */ + if (game->do_again) { + game->do_again = FALSE; + + /* Check there is a last element to repeat. */ + if (prior_element[0] == NUL) { + pf_buffer_string(filter, "You can hardly repeat that.\n"); + return FALSE; + } + + /* Make the last element the current input element. */ + strcpy(line_element, prior_element); + } else { + sc_int length, extent; + + /* + * If there's none buffered, read a new line of player input. Other- + * wise, separate output so far with a newline. + */ + if (line_buffer[0] == NUL) + if_read_line(line_buffer, sizeof(line_buffer)); + else + if_print_character('\n'); + + /* + * Find the length of the next input line element. Unless the line + * buffer is empty, we always take the first character, even if it's a + * separator. This catches odd input like "." and turns it into a + * parser complaint, rather than treating it as two empty commands with + * a separator between them; this makes it close to what Inform does + * with similar inputs. + */ + length = (line_buffer[0] == NUL) ? 0 : 1; + while (line_buffer[length] != NUL + && strchr(SEPARATORS, line_buffer[length]) == NULL) + length++; + + /* + * Make this the current input element, and remove it, the separator, + * and any trailing whitespace, from the front of the line buffer. + * Removing whitespace prevents "i. ." looking like "i" and ""; it + * instead looks like "i" and ".", and results in a parser complaint. + */ + memcpy(line_element, line_buffer, length); + line_element[length] = NUL; + + extent = length; + extent += (line_buffer[length] == NUL + || strchr(SEPARATORS, line_buffer[length]) == NULL) ? 0 : 1; + extent += strspn(line_buffer + extent, WHITESPACE); + memmove(line_buffer, + line_buffer + extent, strlen(line_buffer) - extent + 1); + } + + /* Copy the current game to the temporary undo buffer. */ + gs_copy(game->temporary, game); + + /* Filter the input element for synonyms, then for pronouns. */ + filtered = pf_filter_input(line_element, bundle); + replaced = uip_replace_pronouns(game, filtered ? filtered : line_element); + + /* + * If filtering didn't replace synonyms, or no pronouns were replaced, use + * the original line element. + */ + command = replaced ? sc_normalize_string(replaced) + : (filtered ? sc_normalize_string(filtered) : line_element); + if (command != line_element) { + if_print_tag(SC_TAG_ITALICS, ""); + if_print_character('['); + if_print_string(command); + if_print_character(']'); + if_print_tag(SC_TAG_ENDITALICS, ""); + if_print_character('\n'); + } + + /* Try the command line element against command matchers. */ + status = run_all_commands(game, command); + if (!status) { + /* Only complain on non-empty command input line elements. */ + if (!sc_strempty(command)) { + sc_vartype_t vt_key[2]; + sc_char *escaped; + const sc_char *message; + + /* Command line element not understood. */ + escaped = pf_escape(sc_normalize_string(line_element)); + var_set_ref_text(vars, escaped); + sc_free(escaped); + vt_key[0].string = "Globals"; + vt_key[1].string = "DontUnderstand"; + message = prop_get_string(bundle, "S<-ss", vt_key); + pf_buffer_string(filter, message); + pf_buffer_character(filter, '\n'); + + /* + * On a line element that's not understood, throw out any remaining + * input line elements. + */ + line_buffer[0] = NUL; + sc_free(filtered); + sc_free(replaced); + return status; + } + } else { + /* + * Unless administrative, back up any valid undo, copy the temporary + * game into the undo buffer, flag the undo buffer as available, and + * assign any pronouns used in the command ready for the next iteration. + */ + if (!game->is_admin) { + if (game->undo_available) + memo_save_game(memento, game->undo); + + gs_copy(game->undo, game->temporary); + game->undo_available = TRUE; + + uip_assign_pronouns(game, command); + } + } + sc_free(filtered); + sc_free(replaced); + + /* + * If do_again is set, we'll come round with the prior command in line + * element in a moment, so save nothing for that case. Otherwise save the + * command in the history. + */ + if (!sc_strempty(line_element) && !game->do_again) { + /* + * If this is a failed redo, redo_sequence will be set but do_again will + * be clear. Suppress the save for this special case; otherwise, failed + * redo commands get into the history, where they can cause problems + * later on. + */ + if (game->redo_sequence == 0) { + sc_int timestamp; + + timestamp = var_get_elapsed_seconds(vars); + memo_save_command(memento, line_element, timestamp, game->turns); + } else + game->redo_sequence = 0; + } + + /* + * Special case restart and restore commands; throw out any remaining input + * and return straight away. Do the same if this was an undo, detected by + * noting that undo is no longer available, where it was on entry. + */ + if (game->do_restart || game->do_restore + || (was_undo_available && !game->undo_available)) { + line_buffer[0] = NUL; + return status; + } + + /* If not empty, consider as saving for "again" calls and in the history. */ + if (!sc_strempty(line_element)) { + /* + * Unless "again", note this line element as prior input. "Again" shows + * up as do_again set in the game, where it wasn't when we entered here. + */ + if (!game->do_again && !is_rerunning) + strcpy(prior_element, line_element); + + /* + * If this was a request to run a command from the history, copy that + * command into the prior_element for the next iteration. The library + * should have verified the value in redo_sequence, so fetching the + * command string should not fail. + */ + if (game->do_again && game->redo_sequence != 0) { + const sc_char *redo_command; + + redo_command = memo_find_command(memento, game->redo_sequence); + if (redo_command) + strcpy(prior_element, redo_command); + else { + sc_error("run_player_input: invalid redo sequence request\n"); + game->do_again = FALSE; + } + game->redo_sequence = 0; + } + } + + return status; } @@ -1292,190 +1255,179 @@ run_player_input (sc_gameref_t game) * Main interpreter loop. */ static void -run_main_loop (sc_gameref_t game) -{ - const sc_filterref_t filter = gs_get_filter (game); - const sc_var_setref_t vars = gs_get_vars (game); - const sc_prop_setref_t bundle = gs_get_bundle (game); - - /* - * This may not be the very first time this game has been used, for example - * saving a game right at the start, or undo-ing back to the start through - * memos. Caught by looking to see if the player room is marked as seen. - */ - if (!gs_room_seen (game, gs_playerroom (game))) - { - sc_vartype_t vt_key[2]; - const sc_char *gamename, *startuptext; - sc_bool disp_first_room, battle_system; - - /* If battle system and no debugger display a warning. */ - vt_key[0].string = "Globals"; - vt_key[1].string = "BattleSystem"; - battle_system = prop_get_boolean (bundle, "B<-ss", vt_key); - if (battle_system && !debug_get_enabled (game)) - { - if_print_tag (SC_TAG_CLS, ""); - lib_warn_battle_system (); - } - - /* Initial clear screen. */ - pf_buffer_tag (filter, SC_TAG_CLS); - - /* Print the game name. */ - vt_key[0].string = "Globals"; - vt_key[1].string = "GameName"; - gamename = prop_get_string (bundle, "S<-ss", vt_key); - pf_buffer_string (filter, gamename); - pf_buffer_character (filter, '\n'); - - /* Print the game header. */ - vt_key[0].string = "Header"; - vt_key[1].string = "StartupText"; - startuptext = prop_get_string (bundle, "S<-ss", vt_key); - pf_buffer_string (filter, startuptext); - pf_buffer_character (filter, '\n'); - - /* If flagged, describe the initial room. */ - vt_key[0].string = "Globals"; - vt_key[1].string = "DispFirstRoom"; - disp_first_room = prop_get_boolean (bundle, "B<-ss", vt_key); - if (disp_first_room) - lib_cmd_look (game); - - /* Handle any introductory resources. */ - vt_key[0].string = "Globals"; - vt_key[1].string = "IntroRes"; - res_handle_resource (game, "ss", vt_key); - - /* Set initial values for NPC and object states. */ - npc_setup_initial (game); - obj_setup_initial (game); - - /* Nudge events and NPCs. */ - evt_tick_events (game); - npc_tick_npcs (game); - - /* - * Notify the debugger that the game has started. This is a chance to - * set watchpoints to catch game startup actions. Done before setting - * the initial room visited as this is how the debugger differentiates - * restarts from restore or undo back to game start. - */ - debug_game_started (game); - - /* Note the initial room as visited. */ - gs_set_room_seen (game, gs_playerroom (game), TRUE); - } - else - { - /* Notify the debugger that the game has restarted. */ - debug_game_started (game); - } - - /* - * Game loop, exits either when a command parser handler sets the game - * running flag to FALSE, or by call to run_quit(). - */ - game->is_running &= !g_vm->shouldQuit(); - while (game->is_running) - { - sc_bool status; - - /* - * Synchronize any resources in use; do this before flushing so that any - * appropriate graphics/sound appear before waits or waitkey tag delays - * invoked by flushing the printfilter. Also, print any score change - * notifications. - */ - res_sync_resources (game); - run_notify_score_change (game); - - /* - * Flush printfilter of any accumulated output, and clear any prior - * notion of administrative commands from input. - */ - pf_flush (filter, vars, bundle); - game->is_admin = FALSE; - - /* If waitcounter is zero, accept and try a command. */ - if (game->waitcounter == 0) - { - /* Not waiting, so handle a player input line. */ - run_update_status (game); - status = run_player_input (game); - - /* - * If waitcounter is now set, decrement it, as this turn counts as - * one of them. - */ - if (game->waitcounter > 0) - game->waitcounter--; - } - else - { - /* - * Currently "waiting"; decrement wait turns, then run a turn having - * taken no input. - */ - game->waitcounter--; - status = TRUE; - } - - /* - * Do usual turn stuff unless either something stopped the game, or the - * last command didn't match, or the last command did match but was - * administrative. - */ - if (status && !game->is_admin) - { - /* Increment turn counter, and clear notifications done flag. */ - game->turns++; - game->has_notified = FALSE; - - if (game->is_running) - { - /* Nudge events and NPCs. */ - evt_tick_events (game); - npc_tick_npcs (game); - - /* Update NPC and object states. */ - npc_turn_update (game); - obj_turn_update (game); - - /* Note the current room as visited. */ - gs_set_room_seen (game, gs_playerroom (game), TRUE); - - /* Give the debugger a chance to catch watchpoints. */ - debug_turn_update (game); - } - } - - game->is_running &= !g_vm->shouldQuit(); - } - - /* - * Final status update, for games that vary it on completion, then notify - * the debugger that the game has ended, to let it make a last watchpoint - * scan and offer the dialog if appropriate. - */ - run_update_status (game); - debug_game_ended (game); - - /* - * Final resource sync, score change notification and printfilter flush - * on game-instigated loop exit. - */ - res_sync_resources (game); - run_notify_score_change (game); - pf_flush (filter, vars, bundle); - - /* - * Reset static variables inside run_player_input() with a call to it with - * is_running false; this is a special case. - */ - assert (!game->is_running); - run_player_input (game); +run_main_loop(sc_gameref_t game) { + const sc_filterref_t filter = gs_get_filter(game); + const sc_var_setref_t vars = gs_get_vars(game); + const sc_prop_setref_t bundle = gs_get_bundle(game); + + /* + * This may not be the very first time this game has been used, for example + * saving a game right at the start, or undo-ing back to the start through + * memos. Caught by looking to see if the player room is marked as seen. + */ + if (!gs_room_seen(game, gs_playerroom(game))) { + sc_vartype_t vt_key[2]; + const sc_char *gamename, *startuptext; + sc_bool disp_first_room, battle_system; + + /* If battle system and no debugger display a warning. */ + vt_key[0].string = "Globals"; + vt_key[1].string = "BattleSystem"; + battle_system = prop_get_boolean(bundle, "B<-ss", vt_key); + if (battle_system && !debug_get_enabled(game)) { + if_print_tag(SC_TAG_CLS, ""); + lib_warn_battle_system(); + } + + /* Initial clear screen. */ + pf_buffer_tag(filter, SC_TAG_CLS); + + /* Print the game name. */ + vt_key[0].string = "Globals"; + vt_key[1].string = "GameName"; + gamename = prop_get_string(bundle, "S<-ss", vt_key); + pf_buffer_string(filter, gamename); + pf_buffer_character(filter, '\n'); + + /* Print the game header. */ + vt_key[0].string = "Header"; + vt_key[1].string = "StartupText"; + startuptext = prop_get_string(bundle, "S<-ss", vt_key); + pf_buffer_string(filter, startuptext); + pf_buffer_character(filter, '\n'); + + /* If flagged, describe the initial room. */ + vt_key[0].string = "Globals"; + vt_key[1].string = "DispFirstRoom"; + disp_first_room = prop_get_boolean(bundle, "B<-ss", vt_key); + if (disp_first_room) + lib_cmd_look(game); + + /* Handle any introductory resources. */ + vt_key[0].string = "Globals"; + vt_key[1].string = "IntroRes"; + res_handle_resource(game, "ss", vt_key); + + /* Set initial values for NPC and object states. */ + npc_setup_initial(game); + obj_setup_initial(game); + + /* Nudge events and NPCs. */ + evt_tick_events(game); + npc_tick_npcs(game); + + /* + * Notify the debugger that the game has started. This is a chance to + * set watchpoints to catch game startup actions. Done before setting + * the initial room visited as this is how the debugger differentiates + * restarts from restore or undo back to game start. + */ + debug_game_started(game); + + /* Note the initial room as visited. */ + gs_set_room_seen(game, gs_playerroom(game), TRUE); + } else { + /* Notify the debugger that the game has restarted. */ + debug_game_started(game); + } + + /* + * Game loop, exits either when a command parser handler sets the game + * running flag to FALSE, or by call to run_quit(). + */ + game->is_running &= !g_vm->shouldQuit(); + while (game->is_running) { + sc_bool status; + + /* + * Synchronize any resources in use; do this before flushing so that any + * appropriate graphics/sound appear before waits or waitkey tag delays + * invoked by flushing the printfilter. Also, print any score change + * notifications. + */ + res_sync_resources(game); + run_notify_score_change(game); + + /* + * Flush printfilter of any accumulated output, and clear any prior + * notion of administrative commands from input. + */ + pf_flush(filter, vars, bundle); + game->is_admin = FALSE; + + /* If waitcounter is zero, accept and try a command. */ + if (game->waitcounter == 0) { + /* Not waiting, so handle a player input line. */ + run_update_status(game); + status = run_player_input(game); + + /* + * If waitcounter is now set, decrement it, as this turn counts as + * one of them. + */ + if (game->waitcounter > 0) + game->waitcounter--; + } else { + /* + * Currently "waiting"; decrement wait turns, then run a turn having + * taken no input. + */ + game->waitcounter--; + status = TRUE; + } + + /* + * Do usual turn stuff unless either something stopped the game, or the + * last command didn't match, or the last command did match but was + * administrative. + */ + if (status && !game->is_admin) { + /* Increment turn counter, and clear notifications done flag. */ + game->turns++; + game->has_notified = FALSE; + + if (game->is_running) { + /* Nudge events and NPCs. */ + evt_tick_events(game); + npc_tick_npcs(game); + + /* Update NPC and object states. */ + npc_turn_update(game); + obj_turn_update(game); + + /* Note the current room as visited. */ + gs_set_room_seen(game, gs_playerroom(game), TRUE); + + /* Give the debugger a chance to catch watchpoints. */ + debug_turn_update(game); + } + } + + game->is_running &= !g_vm->shouldQuit(); + } + + /* + * Final status update, for games that vary it on completion, then notify + * the debugger that the game has ended, to let it make a last watchpoint + * scan and offer the dialog if appropriate. + */ + run_update_status(game); + debug_game_ended(game); + + /* + * Final resource sync, score change notification and printfilter flush + * on game-instigated loop exit. + */ + res_sync_resources(game); + run_notify_score_change(game); + pf_flush(filter, vars, bundle); + + /* + * Reset static variables inside run_player_input() with a call to it with + * is_running false; this is a special case. + */ + assert(!game->is_running); + run_player_input(game); } @@ -1485,66 +1437,63 @@ run_main_loop (sc_gameref_t game) * Create a game context from a callback. */ sc_gameref_t -run_create (sc_read_callbackref_t callback, void *opaque) -{ - sc_tafref_t taf; - sc_prop_setref_t bundle; - sc_var_setref_t vars, temporary_vars, undo_vars; - sc_filterref_t filter; - sc_gameref_t game, temporary_game, undo_game; - assert (callback); - - /* Create a new TAF using the callback; return NULL if this fails. */ - taf = taf_create (callback, opaque); - if (!taf) - return NULL; - else if (if_get_trace_flag (SC_DUMP_TAF)) - taf_debug_dump (taf); - - /* Create a properties bundle, and parse the TAF data into it. */ - bundle = prop_create (taf); - if (!bundle) - { - sc_error ("run_create: error parsing game data\n"); - taf_destroy (taf); - return NULL; - } - else if (if_get_trace_flag (SC_DUMP_PROPERTIES)) - prop_debug_dump (bundle); - - /* Try to set an interpreter locale from the properties bundle. */ - loc_detect_game_locale (bundle); - if (if_get_trace_flag (SC_DUMP_LOCALE_TABLES)) - loc_debug_dump (); - - /* Create a set of variables from the bundle. */ - vars = var_create (bundle); - if (if_get_trace_flag (SC_DUMP_VARIABLES)) - var_debug_dump (vars); - - /* Create a printfilter for the game. */ - filter = pf_create (); - - /* - * Create an initial game state, and register it with variables. Also, - * create undo buffers, and initialize them in the same way. - */ - game = gs_create (vars, bundle, filter); - var_register_game (vars, game); - - temporary_vars = var_create (bundle); - temporary_game = gs_create (temporary_vars, bundle, filter); - var_register_game (temporary_vars, temporary_game); - - undo_vars = var_create (bundle); - undo_game = gs_create (undo_vars, bundle, filter); - var_register_game (undo_vars, undo_game); - - /* Add the undo buffers and memos to the game, and return it. */ - game->temporary = temporary_game; - game->undo = undo_game; - game->memento = memo_create (); - return game; +run_create(sc_read_callbackref_t callback, void *opaque) { + sc_tafref_t taf; + sc_prop_setref_t bundle; + sc_var_setref_t vars, temporary_vars, undo_vars; + sc_filterref_t filter; + sc_gameref_t game, temporary_game, undo_game; + assert(callback); + + /* Create a new TAF using the callback; return NULL if this fails. */ + taf = taf_create(callback, opaque); + if (!taf) + return NULL; + else if (if_get_trace_flag(SC_DUMP_TAF)) + taf_debug_dump(taf); + + /* Create a properties bundle, and parse the TAF data into it. */ + bundle = prop_create(taf); + if (!bundle) { + sc_error("run_create: error parsing game data\n"); + taf_destroy(taf); + return NULL; + } else if (if_get_trace_flag(SC_DUMP_PROPERTIES)) + prop_debug_dump(bundle); + + /* Try to set an interpreter locale from the properties bundle. */ + loc_detect_game_locale(bundle); + if (if_get_trace_flag(SC_DUMP_LOCALE_TABLES)) + loc_debug_dump(); + + /* Create a set of variables from the bundle. */ + vars = var_create(bundle); + if (if_get_trace_flag(SC_DUMP_VARIABLES)) + var_debug_dump(vars); + + /* Create a printfilter for the game. */ + filter = pf_create(); + + /* + * Create an initial game state, and register it with variables. Also, + * create undo buffers, and initialize them in the same way. + */ + game = gs_create(vars, bundle, filter); + var_register_game(vars, game); + + temporary_vars = var_create(bundle); + temporary_game = gs_create(temporary_vars, bundle, filter); + var_register_game(temporary_vars, temporary_game); + + undo_vars = var_create(bundle); + undo_game = gs_create(undo_vars, bundle, filter); + var_register_game(undo_vars, undo_game); + + /* Add the undo buffers and memos to the game, and return it. */ + game->temporary = temporary_game; + game->undo = undo_game; + game->memento = memo_create(); + return game; } @@ -1554,44 +1503,43 @@ run_create (sc_read_callbackref_t callback, void *opaque) * Return a game context to initial states to restart a game. */ static void -run_restart_handler (sc_gameref_t game) -{ - const sc_filterref_t filter = gs_get_filter (game); - const sc_prop_setref_t bundle = gs_get_bundle (game); - sc_gameref_t new_game; - sc_var_setref_t new_vars; - - /* - * Create a fresh set of variables from the current game properties, - * then a new game using these variables and existing properties and - * printfilter. - */ - new_vars = var_create (bundle); - new_game = gs_create (new_vars, bundle, filter); - var_register_game (new_vars, new_game); - - /* - * Overwrite the dynamic parts of the current game with the new one. - */ - new_game->temporary = game->temporary; - new_game->undo = game->undo; - gs_copy (game, new_game); - - /* Destroy invalid game status strings. */ - sc_free (game->current_room_name); - game->current_room_name = NULL; - sc_free (game->status_line); - game->status_line = NULL; - - /* - * Now it's safely copied, destroy the temporary new game, and its - * associated variable set. - */ - gs_destroy (new_game); - var_destroy (new_vars); - - /* Reset resources handling. */ - res_cancel_resources (game); +run_restart_handler(sc_gameref_t game) { + const sc_filterref_t filter = gs_get_filter(game); + const sc_prop_setref_t bundle = gs_get_bundle(game); + sc_gameref_t new_game; + sc_var_setref_t new_vars; + + /* + * Create a fresh set of variables from the current game properties, + * then a new game using these variables and existing properties and + * printfilter. + */ + new_vars = var_create(bundle); + new_game = gs_create(new_vars, bundle, filter); + var_register_game(new_vars, new_game); + + /* + * Overwrite the dynamic parts of the current game with the new one. + */ + new_game->temporary = game->temporary; + new_game->undo = game->undo; + gs_copy(game, new_game); + + /* Destroy invalid game status strings. */ + sc_free(game->current_room_name); + game->current_room_name = NULL; + sc_free(game->status_line); + game->status_line = NULL; + + /* + * Now it's safely copied, destroy the temporary new game, and its + * associated variable set. + */ + gs_destroy(new_game); + var_destroy(new_vars); + + /* Reset resources handling. */ + res_cancel_resources(game); } @@ -1601,20 +1549,19 @@ run_restart_handler (sc_gameref_t game) * Adjust a game context for continuation after restoring a game. */ static void -run_restore_handler (sc_gameref_t game) -{ - /* Invalidate the undo buffer. */ - game->undo_available = FALSE; - - /* - * Resources handling? Arguably we should re-offer resources active when - * the game was saved, but I can't see how this can be achieved with Adrift - * the way it is. Canceling is too broad, so I'll go here with just - * stopping sounds (in case looping). - * - * TODO Rationalize what happens here. - */ - game->stop_sound = TRUE; +run_restore_handler(sc_gameref_t game) { + /* Invalidate the undo buffer. */ + game->undo_available = FALSE; + + /* + * Resources handling? Arguably we should re-offer resources active when + * the game was saved, but I can't see how this can be achieved with Adrift + * the way it is. Canceling is too broad, so I'll go here with just + * stopping sounds (in case looping). + * + * TODO Rationalize what happens here. + */ + game->stop_sound = TRUE; } @@ -1624,25 +1571,24 @@ run_restore_handler (sc_gameref_t game) * Tidy up printfilter and input statics on game quit. */ static void -run_quit_handler (sc_gameref_t game) -{ - const sc_filterref_t filter = gs_get_filter (game); - const sc_var_setref_t vars = gs_get_vars (game); - const sc_prop_setref_t bundle = gs_get_bundle (game); - - /* Flush printfilter and notifications of any dangling output. */ - run_notify_score_change (game); - pf_flush (filter, vars, bundle); - - /* Cancel any active resources. */ - res_cancel_resources (game); - - /* - * Make the special call to reset all of the static variables inside - * run_player_input(). - */ - assert (!game->is_running); - run_player_input (game); +run_quit_handler(sc_gameref_t game) { + const sc_filterref_t filter = gs_get_filter(game); + const sc_var_setref_t vars = gs_get_vars(game); + const sc_prop_setref_t bundle = gs_get_bundle(game); + + /* Flush printfilter and notifications of any dangling output. */ + run_notify_score_change(game); + pf_flush(filter, vars, bundle); + + /* Cancel any active resources. */ + res_cancel_resources(game); + + /* + * Make the special call to reset all of the static variables inside + * run_player_input(). + */ + assert(!game->is_running); + run_player_input(game); } @@ -1652,60 +1598,52 @@ run_quit_handler (sc_gameref_t game) * Intepret the game in a game context. */ void -run_interpret (sc_gameref_t game) -{ - assert (gs_is_game_valid (game)); - - /* Verify the game is not already running, and is runnable. */ - if (game->is_running) - { - sc_error ("run_interpret: game is already running\n"); - return; - } - if (game->has_completed) - { - sc_error ("run_interpret: game has already completed\n"); - return; - } - - /* Refuse to run a game with no rooms. */ - if (gs_room_count (game) == 0) - { - sc_error ("run_interpret: game contains no rooms\n"); - return; - } - - /* Run the main interpreter loop until no more restarts. */ - game->is_running = TRUE; - do - { - /* Run the game until some form of halt is requested. */ - if (setjmp (game->quitter) == 0) - run_main_loop (game); - - /* - * If the halt was a restart or restore, cancel the request, handle - * restart or restore game adjustments, and set the game running - * again. - */ - if (game->do_restart) - { - game->do_restart = FALSE; - run_restart_handler (game); - game->is_running = TRUE; - } - - if (game->do_restore) - { - game->do_restore = FALSE; - run_restore_handler (game); - game->is_running = TRUE; - } - } - while (game->is_running); - - /* Tidy up the printfilter and input statics. */ - run_quit_handler (game); +run_interpret(sc_gameref_t game) { + assert(gs_is_game_valid(game)); + + /* Verify the game is not already running, and is runnable. */ + if (game->is_running) { + sc_error("run_interpret: game is already running\n"); + return; + } + if (game->has_completed) { + sc_error("run_interpret: game has already completed\n"); + return; + } + + /* Refuse to run a game with no rooms. */ + if (gs_room_count(game) == 0) { + sc_error("run_interpret: game contains no rooms\n"); + return; + } + + /* Run the main interpreter loop until no more restarts. */ + game->is_running = TRUE; + do { + /* Run the game until some form of halt is requested. */ + if (setjmp(game->quitter) == 0) + run_main_loop(game); + + /* + * If the halt was a restart or restore, cancel the request, handle + * restart or restore game adjustments, and set the game running + * again. + */ + if (game->do_restart) { + game->do_restart = FALSE; + run_restart_handler(game); + game->is_running = TRUE; + } + + if (game->do_restore) { + game->do_restore = FALSE; + run_restore_handler(game); + game->is_running = TRUE; + } + } while (game->is_running); + + /* Tidy up the printfilter and input statics. */ + run_quit_handler(game); } @@ -1715,52 +1653,50 @@ run_interpret (sc_gameref_t game) * Destroy a game context, and free all resources. */ void -run_destroy (sc_gameref_t game) -{ - assert (gs_is_game_valid (game)); - - /* Can't destroy the context of a running game. */ - if (game->is_running) - { - sc_error ("run_destroy: game is running, stop it first\n"); - return; - } - - /* - * Cancel any game state debugger -- this frees its resources. Only the - * primary game may have acquired a debugger. - */ - debug_set_enabled (game, FALSE); - assert (!debug_get_enabled (game->temporary)); - assert (!debug_get_enabled (game->undo)); - - /* - * Destroy the game state, variables, properties bundle, memos, undo - * buffers and their variables, and filter. The bundle and printfilter - * are shared by the main game, the undo game, and the temporary game, so - * destroy these only once! The main game has a memento, but it is not - * visible to these other two games, neither of which have one. - */ - assert (gs_get_bundle (game->temporary) == gs_get_bundle (game)); - assert (gs_get_filter (game->temporary) == gs_get_filter (game)); - assert (gs_get_vars (game->temporary) != gs_get_vars (game)); - assert (!gs_get_memento (game->temporary)); - var_destroy (gs_get_vars (game->temporary)); - gs_destroy (game->temporary); - - assert (gs_get_bundle (game->undo) == gs_get_bundle (game)); - assert (gs_get_filter (game->undo) == gs_get_filter (game)); - assert (gs_get_vars (game->undo) != gs_get_vars (game)); - assert (!gs_get_memento (game->undo)); - var_destroy (gs_get_vars (game->undo)); - gs_destroy (game->undo); - - prop_destroy (gs_get_bundle (game)); - pf_destroy (gs_get_filter (game)); - var_destroy (gs_get_vars (game)); - memo_destroy (gs_get_memento (game)); - - gs_destroy (game); +run_destroy(sc_gameref_t game) { + assert(gs_is_game_valid(game)); + + /* Can't destroy the context of a running game. */ + if (game->is_running) { + sc_error("run_destroy: game is running, stop it first\n"); + return; + } + + /* + * Cancel any game state debugger -- this frees its resources. Only the + * primary game may have acquired a debugger. + */ + debug_set_enabled(game, FALSE); + assert(!debug_get_enabled(game->temporary)); + assert(!debug_get_enabled(game->undo)); + + /* + * Destroy the game state, variables, properties bundle, memos, undo + * buffers and their variables, and filter. The bundle and printfilter + * are shared by the main game, the undo game, and the temporary game, so + * destroy these only once! The main game has a memento, but it is not + * visible to these other two games, neither of which have one. + */ + assert(gs_get_bundle(game->temporary) == gs_get_bundle(game)); + assert(gs_get_filter(game->temporary) == gs_get_filter(game)); + assert(gs_get_vars(game->temporary) != gs_get_vars(game)); + assert(!gs_get_memento(game->temporary)); + var_destroy(gs_get_vars(game->temporary)); + gs_destroy(game->temporary); + + assert(gs_get_bundle(game->undo) == gs_get_bundle(game)); + assert(gs_get_filter(game->undo) == gs_get_filter(game)); + assert(gs_get_vars(game->undo) != gs_get_vars(game)); + assert(!gs_get_memento(game->undo)); + var_destroy(gs_get_vars(game->undo)); + gs_destroy(game->undo); + + prop_destroy(gs_get_bundle(game)); + pf_destroy(gs_get_filter(game)); + var_destroy(gs_get_vars(game)); + memo_destroy(gs_get_memento(game)); + + gs_destroy(game); } @@ -1771,21 +1707,19 @@ run_destroy (sc_gameref_t game) * run_main_loop() returned, and so never returns to its caller. */ void -run_quit (sc_gameref_t game) -{ - assert (gs_is_game_valid (game)); - - /* Disallow quitting a non-running game. */ - if (!game->is_running) - { - sc_error ("run_quit: game is not running\n"); - return; - } - - /* Exit the main loop with a longjump. */ - game->is_running = FALSE; - longjmp (game->quitter, 1); - sc_fatal ("run_quit: unable to quit cleanly\n"); +run_quit(sc_gameref_t game) { + assert(gs_is_game_valid(game)); + + /* Disallow quitting a non-running game. */ + if (!game->is_running) { + sc_error("run_quit: game is not running\n"); + return; + } + + /* Exit the main loop with a longjump. */ + game->is_running = FALSE; + longjmp(game->quitter, 1); + sc_fatal("run_quit: unable to quit cleanly\n"); } @@ -1797,25 +1731,23 @@ run_quit (sc_gameref_t game) * never returns to its caller. For stopped games, it returns. */ void -run_restart (sc_gameref_t game) -{ - assert (gs_is_game_valid (game)); - - /* - * If the game is running, stop it, request a restart, and exit the main - * loop with a longjump. - */ - if (game->is_running) - { - game->is_running = FALSE; - game->do_restart = TRUE; - longjmp (game->quitter, 1); - sc_fatal ("run_restart: unable to restart cleanly\n"); - } - - /* Restart locally, and ensure that the game remains stopped. */ - run_restart_handler (game); - game->is_running = FALSE; +run_restart(sc_gameref_t game) { + assert(gs_is_game_valid(game)); + + /* + * If the game is running, stop it, request a restart, and exit the main + * loop with a longjump. + */ + if (game->is_running) { + game->is_running = FALSE; + game->do_restart = TRUE; + longjmp(game->quitter, 1); + sc_fatal("run_restart: unable to restart cleanly\n"); + } + + /* Restart locally, and ensure that the game remains stopped. */ + run_restart_handler(game); + game->is_running = FALSE; } @@ -1826,20 +1758,18 @@ run_restart (sc_gameref_t game) * Saves either a running or a stopped game. */ void -run_save (sc_gameref_t game, sc_write_callbackref_t callback, void *opaque) -{ - assert (gs_is_game_valid (game)); - assert (callback); +run_save(sc_gameref_t game, sc_write_callbackref_t callback, void *opaque) { + assert(gs_is_game_valid(game)); + assert(callback); - ser_save_game (game, callback, opaque); + ser_save_game(game, callback, opaque); } sc_bool -run_save_prompted (sc_gameref_t game) -{ - assert (gs_is_game_valid (game)); +run_save_prompted(sc_gameref_t game) { + assert(gs_is_game_valid(game)); - return ser_save_game_prompted (game); + return ser_save_game_prompted(game); } @@ -1855,56 +1785,51 @@ run_save_prompted (sc_gameref_t game) * FALSE if restore failed. */ static sc_bool -run_restore_common (sc_gameref_t game, - sc_read_callbackref_t callback, void *opaque) -{ - sc_bool is_running, status; - - /* - * Save the game running flag, and call the restore appropriate for the - * caller. The indication of a call from run_restore_prompted() is a - * callback of NULL; callback cannot be NULL for run_restore() calls. - */ - is_running = game->is_running; - status = callback ? ser_load_game (game, callback, opaque) - : ser_load_game_prompted (game); - if (status) - { - /* Loading a game clears is_running -- restore it here. */ - game->is_running = is_running; - - /* - * If the game is (was) running, set flags so that the interpreter - * loop cycles, and exit the main loop with a longjump. - */ - if (game->is_running) - { - game->is_running = FALSE; - game->do_restore = TRUE; - longjmp (game->quitter, 1); - sc_fatal ("run_restore_common: unable to restart cleanly\n"); - } - } - - /* Return TRUE on successful restore of a stopped game, FALSE on error. */ - return status; +run_restore_common(sc_gameref_t game, + sc_read_callbackref_t callback, void *opaque) { + sc_bool is_running, status; + + /* + * Save the game running flag, and call the restore appropriate for the + * caller. The indication of a call from run_restore_prompted() is a + * callback of NULL; callback cannot be NULL for run_restore() calls. + */ + is_running = game->is_running; + status = callback ? ser_load_game(game, callback, opaque) + : ser_load_game_prompted(game); + if (status) { + /* Loading a game clears is_running -- restore it here. */ + game->is_running = is_running; + + /* + * If the game is (was) running, set flags so that the interpreter + * loop cycles, and exit the main loop with a longjump. + */ + if (game->is_running) { + game->is_running = FALSE; + game->do_restore = TRUE; + longjmp(game->quitter, 1); + sc_fatal("run_restore_common: unable to restart cleanly\n"); + } + } + + /* Return TRUE on successful restore of a stopped game, FALSE on error. */ + return status; } sc_bool -run_restore (sc_gameref_t game, sc_read_callbackref_t callback, void *opaque) -{ - assert (gs_is_game_valid (game)); - assert (callback); +run_restore(sc_gameref_t game, sc_read_callbackref_t callback, void *opaque) { + assert(gs_is_game_valid(game)); + assert(callback); - return run_restore_common (game, callback, opaque); + return run_restore_common(game, callback, opaque); } sc_bool -run_restore_prompted (sc_gameref_t game) -{ - assert (gs_is_game_valid (game)); +run_restore_prompted(sc_gameref_t game) { + assert(gs_is_game_valid(game)); - return run_restore_common (game, NULL, NULL); + return run_restore_common(game, NULL, NULL); } @@ -1915,58 +1840,54 @@ run_restore_prompted (sc_gameref_t game) * successful undo, FALSE if no undo buffer is available. */ sc_bool -run_undo (sc_gameref_t game) -{ - const sc_memo_setref_t memento = gs_get_memento (game); - sc_bool is_running; - assert (gs_is_game_valid (game)); - - /* Save the game's running state, so we can restore it later. */ - is_running = game->is_running; - - /* If there's an undo buffer available, restore it. */ - if (game->undo_available) - { - /* Restore the undo buffer, and then restore running flag. */ - gs_copy (game, game->undo); - game->undo_available = FALSE; - game->is_running = is_running; - - /* Location may have changed; update status. */ - run_update_status (game); - - /* Bring resources into line with the revised game. */ - res_sync_resources (game); - return TRUE; - } - - /* - * If there is no undo buffer, try to restore one saved previously in a - * memo. Handle as if restoring from a file. - */ - if (memo_load_game (memento, game)) - { - /* Loading a game clears is_running -- restore it here. */ - game->is_running = is_running; - - /* - * If the game is (was) running, set flags so that the interpreter - * loop cycles, and exit the main loop with a longjump. - */ - if (game->is_running) - { - game->is_running = FALSE; - game->do_restore = TRUE; - longjmp (game->quitter, 1); - sc_fatal ("run_undo: unable to restart cleanly\n"); - } - - /* Game undo on non-running game accomplished with memos. */ - return TRUE; - } - - /* No undo buffer and no memos available. */ - return FALSE; +run_undo(sc_gameref_t game) { + const sc_memo_setref_t memento = gs_get_memento(game); + sc_bool is_running; + assert(gs_is_game_valid(game)); + + /* Save the game's running state, so we can restore it later. */ + is_running = game->is_running; + + /* If there's an undo buffer available, restore it. */ + if (game->undo_available) { + /* Restore the undo buffer, and then restore running flag. */ + gs_copy(game, game->undo); + game->undo_available = FALSE; + game->is_running = is_running; + + /* Location may have changed; update status. */ + run_update_status(game); + + /* Bring resources into line with the revised game. */ + res_sync_resources(game); + return TRUE; + } + + /* + * If there is no undo buffer, try to restore one saved previously in a + * memo. Handle as if restoring from a file. + */ + if (memo_load_game(memento, game)) { + /* Loading a game clears is_running -- restore it here. */ + game->is_running = is_running; + + /* + * If the game is (was) running, set flags so that the interpreter + * loop cycles, and exit the main loop with a longjump. + */ + if (game->is_running) { + game->is_running = FALSE; + game->do_restore = TRUE; + longjmp(game->quitter, 1); + sc_fatal("run_undo: unable to restart cleanly\n"); + } + + /* Game undo on non-running game accomplished with memos. */ + return TRUE; + } + + /* No undo buffer and no memos available. */ + return FALSE; } @@ -1976,11 +1897,10 @@ run_undo (sc_gameref_t game) * Query the game running state. */ sc_bool -run_is_running (sc_gameref_t game) -{ - assert (gs_is_game_valid (game)); +run_is_running(sc_gameref_t game) { + assert(gs_is_game_valid(game)); - return game->is_running; + return game->is_running; } @@ -1991,11 +1911,10 @@ run_is_running (sc_gameref_t game) * since they've run the exit task and thus have nowhere to go. */ sc_bool -run_has_completed (sc_gameref_t game) -{ - assert (gs_is_game_valid (game)); +run_has_completed(sc_gameref_t game) { + assert(gs_is_game_valid(game)); - return game->has_completed; + return game->has_completed; } @@ -2005,12 +1924,11 @@ run_has_completed (sc_gameref_t game) * Query the game turn undo buffer and memo availability. */ sc_bool -run_is_undo_available (sc_gameref_t game) -{ - const sc_memo_setref_t memento = gs_get_memento (game); - assert (gs_is_game_valid (game)); +run_is_undo_available(sc_gameref_t game) { + const sc_memo_setref_t memento = gs_get_memento(game); + assert(gs_is_game_valid(game)); - return game->undo_available || memo_is_load_available (memento); + return game->undo_available || memo_is_load_available(memento); } @@ -2021,110 +1939,99 @@ run_is_undo_available (sc_gameref_t game) * Get and set selected game attributes. */ void -run_get_attributes (sc_gameref_t game, - const sc_char **game_name, const sc_char **game_author, - const sc_char **game_compile_date, - sc_int *turns, sc_int *score, sc_int *max_score, - const sc_char **current_room_name, - const sc_char **status_line, const sc_char **preferred_font, - sc_bool *bold_room_names, sc_bool *verbose, - sc_bool *notify_score_change) -{ - const sc_prop_setref_t bundle = gs_get_bundle (game); - const sc_var_setref_t vars = gs_get_vars (game); - sc_vartype_t vt_key[2]; - assert (gs_is_game_valid (game)); - - /* Return the game name, author, and compile date if requested. */ - if (game_name) - { - if (!game->title) - { - const sc_char *gamename; - sc_char *filtered; - - vt_key[0].string = "Globals"; - vt_key[1].string = "GameName"; - gamename = prop_get_string (bundle, "S<-ss", vt_key); - - filtered = pf_filter_for_info (gamename, vars); - pf_strip_tags (filtered); - game->title = filtered; - } - *game_name = game->title; - } - if (game_author) - { - if (!game->author) - { - const sc_char *gameauthor; - sc_char *filtered; - - vt_key[0].string = "Globals"; - vt_key[1].string = "GameAuthor"; - gameauthor = prop_get_string (bundle, "S<-ss", vt_key); - - filtered = pf_filter_for_info (gameauthor, vars); - pf_strip_tags (filtered); - game->author = filtered; - } - *game_author = game->author; - } - if (game_compile_date) - { - vt_key[0].string = "CompileDate"; - *game_compile_date = prop_get_string (bundle, "S<-s", vt_key); - } - - /* Return the current room name and status line if requested. */ - if (current_room_name) - *current_room_name = game->current_room_name; - if (status_line) - *status_line = game->status_line; - - /* Return any game preferred font, or NULL if none. */ - if (preferred_font) - { - vt_key[0].string = "CustomFont"; - if (prop_get_boolean (bundle, "B<-s", vt_key)) - { - vt_key[0].string = "FontNameSize"; - *preferred_font = prop_get_string (bundle, "S<-s", vt_key); - } - else - *preferred_font = NULL; - } - - /* Return any other selected game attributes. */ - if (turns) - *turns = game->turns; - if (score) - *score = game->score; - if (max_score) - { - vt_key[0].string = "Globals"; - vt_key[1].string = "MaxScore"; - *max_score = prop_get_integer (bundle, "I<-ss", vt_key); - } - if (bold_room_names) - *bold_room_names = game->bold_room_names; - if (verbose) - *verbose = game->verbose; - if (notify_score_change) - *notify_score_change = game->notify_score_change; +run_get_attributes(sc_gameref_t game, + const sc_char **game_name, const sc_char **game_author, + const sc_char **game_compile_date, + sc_int *turns, sc_int *score, sc_int *max_score, + const sc_char **current_room_name, + const sc_char **status_line, const sc_char **preferred_font, + sc_bool *bold_room_names, sc_bool *verbose, + sc_bool *notify_score_change) { + const sc_prop_setref_t bundle = gs_get_bundle(game); + const sc_var_setref_t vars = gs_get_vars(game); + sc_vartype_t vt_key[2]; + assert(gs_is_game_valid(game)); + + /* Return the game name, author, and compile date if requested. */ + if (game_name) { + if (!game->title) { + const sc_char *gamename; + sc_char *filtered; + + vt_key[0].string = "Globals"; + vt_key[1].string = "GameName"; + gamename = prop_get_string(bundle, "S<-ss", vt_key); + + filtered = pf_filter_for_info(gamename, vars); + pf_strip_tags(filtered); + game->title = filtered; + } + *game_name = game->title; + } + if (game_author) { + if (!game->author) { + const sc_char *gameauthor; + sc_char *filtered; + + vt_key[0].string = "Globals"; + vt_key[1].string = "GameAuthor"; + gameauthor = prop_get_string(bundle, "S<-ss", vt_key); + + filtered = pf_filter_for_info(gameauthor, vars); + pf_strip_tags(filtered); + game->author = filtered; + } + *game_author = game->author; + } + if (game_compile_date) { + vt_key[0].string = "CompileDate"; + *game_compile_date = prop_get_string(bundle, "S<-s", vt_key); + } + + /* Return the current room name and status line if requested. */ + if (current_room_name) + *current_room_name = game->current_room_name; + if (status_line) + *status_line = game->status_line; + + /* Return any game preferred font, or NULL if none. */ + if (preferred_font) { + vt_key[0].string = "CustomFont"; + if (prop_get_boolean(bundle, "B<-s", vt_key)) { + vt_key[0].string = "FontNameSize"; + *preferred_font = prop_get_string(bundle, "S<-s", vt_key); + } else + *preferred_font = NULL; + } + + /* Return any other selected game attributes. */ + if (turns) + *turns = game->turns; + if (score) + *score = game->score; + if (max_score) { + vt_key[0].string = "Globals"; + vt_key[1].string = "MaxScore"; + *max_score = prop_get_integer(bundle, "I<-ss", vt_key); + } + if (bold_room_names) + *bold_room_names = game->bold_room_names; + if (verbose) + *verbose = game->verbose; + if (notify_score_change) + *notify_score_change = game->notify_score_change; } void -run_set_attributes (sc_gameref_t game, - sc_bool bold_room_names, sc_bool verbose, - sc_bool notify_score_change) -{ - assert (gs_is_game_valid (game)); - - /* Set game options. */ - game->bold_room_names = bold_room_names; - game->verbose = verbose; - game->notify_score_change = notify_score_change; +run_set_attributes(sc_gameref_t game, + sc_bool bold_room_names, sc_bool verbose, + sc_bool notify_score_change) { + assert(gs_is_game_valid(game)); + + /* Set game options. */ + game->bold_room_names = bold_room_names; + game->verbose = verbose; + game->notify_score_change = notify_score_change; } @@ -2138,40 +2045,36 @@ run_set_attributes (sc_gameref_t game, * the client as a void*. */ sc_hintref_t -run_hint_iterate (sc_gameref_t game, sc_hintref_t hint) -{ - sc_int task; - assert (gs_is_game_valid (game)); - - /* - * Hint is a pointer to a task state; convert to a task index, adding one - * to move on to the next task, or start at the first task if null. - */ - if (!hint) - task = 0; - else - { - /* Convert into pointer, and range check. */ - task = hint - game->tasks; - if (task < 0 || task >= gs_task_count (game)) - { - sc_error ("run_hint_iterate: invalid iteration hint\n"); - return NULL; - } - - /* Advance beyond current task. */ - task++; - } - - /* Scan for the next runnable task that offers a hint. */ - for (; task < gs_task_count (game); task++) - { - if (task_can_run_task (game, task) && task_has_hints (game, task)) - break; - } - - /* Return a pointer to the state of the task identified, or NULL. */ - return task < gs_task_count (game) ? game->tasks + task : NULL; +run_hint_iterate(sc_gameref_t game, sc_hintref_t hint) { + sc_int task; + assert(gs_is_game_valid(game)); + + /* + * Hint is a pointer to a task state; convert to a task index, adding one + * to move on to the next task, or start at the first task if null. + */ + if (!hint) + task = 0; + else { + /* Convert into pointer, and range check. */ + task = hint - game->tasks; + if (task < 0 || task >= gs_task_count(game)) { + sc_error("run_hint_iterate: invalid iteration hint\n"); + return NULL; + } + + /* Advance beyond current task. */ + task++; + } + + /* Scan for the next runnable task that offers a hint. */ + for (; task < gs_task_count(game); task++) { + if (task_can_run_task(game, task) && task_has_hints(game, task)) + break; + } + + /* Return a pointer to the state of the task identified, or NULL. */ + return task < gs_task_count(game) ? game->tasks + task : NULL; } @@ -2189,66 +2092,56 @@ run_hint_iterate (sc_gameref_t game, sc_hintref_t hint) * Hint strings are NULL if empty (not defined by the game). */ static const sc_char * -run_get_hint_common (sc_gameref_t game, sc_hintref_t hint, - const sc_char *(*handler) (sc_gameref_t, sc_int)) -{ - const sc_prop_setref_t bundle = gs_get_bundle (game); - const sc_var_setref_t vars = gs_get_vars (game); - sc_int task; - const sc_char *string; - assert (gs_is_game_valid (game)); - - /* Verify the caller passed in a valid hint. */ - task = hint - game->tasks; - if (task < 0 || task >= gs_task_count (game)) - { - sc_error ("run_get_hint_common: invalid iteration hint\n"); - return NULL; - } - else if (!task_has_hints (game, task)) - { - sc_error ("run_get_hint_common: task has no hint\n"); - return NULL; - } - - /* Get the required game text by calling the given handler function. */ - string = handler (game, task); - if (!sc_strempty (string)) - { - sc_char *filtered; - - /* Filter and strip tags, note in game. */ - filtered = pf_filter (string, vars, bundle); - pf_strip_tags_for_hints (filtered); - sc_free (game->hint_text); - game->hint_text = filtered; - } - else - { - /* Hint text is empty; drop any text noted in game. */ - sc_free (game->hint_text); - game->hint_text = NULL; - } - - return game->hint_text; +run_get_hint_common(sc_gameref_t game, sc_hintref_t hint, + const sc_char * (*handler)(sc_gameref_t, sc_int)) { + const sc_prop_setref_t bundle = gs_get_bundle(game); + const sc_var_setref_t vars = gs_get_vars(game); + sc_int task; + const sc_char *string; + assert(gs_is_game_valid(game)); + + /* Verify the caller passed in a valid hint. */ + task = hint - game->tasks; + if (task < 0 || task >= gs_task_count(game)) { + sc_error("run_get_hint_common: invalid iteration hint\n"); + return NULL; + } else if (!task_has_hints(game, task)) { + sc_error("run_get_hint_common: task has no hint\n"); + return NULL; + } + + /* Get the required game text by calling the given handler function. */ + string = handler(game, task); + if (!sc_strempty(string)) { + sc_char *filtered; + + /* Filter and strip tags, note in game. */ + filtered = pf_filter(string, vars, bundle); + pf_strip_tags_for_hints(filtered); + sc_free(game->hint_text); + game->hint_text = filtered; + } else { + /* Hint text is empty; drop any text noted in game. */ + sc_free(game->hint_text); + game->hint_text = NULL; + } + + return game->hint_text; } const sc_char * -run_get_hint_question (sc_gameref_t game, sc_hintref_t hint) -{ - return run_get_hint_common (game, hint, task_get_hint_question); +run_get_hint_question(sc_gameref_t game, sc_hintref_t hint) { + return run_get_hint_common(game, hint, task_get_hint_question); } const sc_char * -run_get_subtle_hint (sc_gameref_t game, sc_hintref_t hint) -{ - return run_get_hint_common (game, hint, task_get_hint_subtle); +run_get_subtle_hint(sc_gameref_t game, sc_hintref_t hint) { + return run_get_hint_common(game, hint, task_get_hint_subtle); } const sc_char * -run_get_unsubtle_hint (sc_gameref_t game, sc_hintref_t hint) -{ - return run_get_hint_common (game, hint, task_get_hint_unsubtle); +run_get_unsubtle_hint(sc_gameref_t game, sc_hintref_t hint) { + return run_get_hint_common(game, hint, task_get_hint_unsubtle); } } // End of namespace Adrift |