2006-03-20 17:12:09 +00:00
/*
2014-06-01 17:55:23 +00:00
* Portions of this file are copyright Rebirth contributors and licensed as
* described in COPYING . txt .
* Portions of this file are copyright Parallax Software and licensed
* according to the Parallax license below .
* See COPYING . txt for license details .
2006-03-20 17:12:09 +00:00
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ( " PARALLAX " ) . PARALLAX , IN DISTRIBUTING THE CODE TO
END - USERS , AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN , GRANTS A
ROYALTY - FREE , PERPETUAL LICENSE TO SUCH END - USERS FOR USE BY SUCH END - USERS
IN USING , DISPLAYING , AND CREATING DERIVATIVE WORKS THEREOF , SO LONG AS
SUCH USE , DISPLAY OR CREATION IS FOR NON - COMMERCIAL , ROYALTY OR REVENUE
FREE PURPOSES . IN NO EVENT SHALL THE END - USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE - BEARING PURPOSES . THE END - USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE .
COPYRIGHT 1993 - 1999 PARALLAX SOFTWARE CORPORATION . ALL RIGHTS RESERVED .
*/
/*
*
* Escort robot behavior .
*
*/
# include <stdio.h> // for printf()
# include <stdlib.h> // for rand() and qsort()
# include <string.h> // for memset()
2010-01-09 01:56:38 +00:00
# include "window.h"
2008-04-06 20:23:28 +00:00
# include "console.h"
2006-03-20 17:12:09 +00:00
# include "vecmat.h"
# include "gr.h"
2013-12-26 04:18:28 +00:00
# include "gameseg.h"
2006-03-20 17:12:09 +00:00
# include "3d.h"
# include "palette.h"
2010-03-26 14:05:40 +00:00
# include "timer.h"
2013-12-26 04:18:28 +00:00
# include "u_mem.h"
2006-03-20 17:12:09 +00:00
# include "object.h"
2012-07-07 18:35:06 +00:00
# include "dxxerror.h"
2006-03-20 17:12:09 +00:00
# include "ai.h"
# include "robot.h"
# include "fvi.h"
# include "physics.h"
# include "wall.h"
# include "player.h"
# include "fireball.h"
# include "game.h"
# include "powerup.h"
2015-04-19 04:18:51 +00:00
# include "hudmsg.h"
2006-03-20 17:12:09 +00:00
# include "cntrlcen.h"
# include "gauges.h"
2015-10-09 02:46:10 +00:00
# include "event.h"
2006-03-20 17:12:09 +00:00
# include "key.h"
# include "fuelcen.h"
# include "sounds.h"
# include "screens.h"
# include "text.h"
# include "gamefont.h"
# include "newmenu.h"
# include "playsave.h"
# include "gameseq.h"
# include "automap.h"
# include "laser.h"
# include "escort.h"
2014-07-29 03:21:53 +00:00
# include "segiter.h"
2015-01-12 00:26:03 +00:00
# include "compiler-exchange.h"
2014-07-29 03:21:53 +00:00
# include "compiler-range_for.h"
2014-10-12 23:05:46 +00:00
# include "highest_valid.h"
2014-10-21 03:15:12 +00:00
# include "partial_range.h"
2014-07-29 03:21:53 +00:00
2006-03-20 17:12:09 +00:00
# ifdef EDITOR
# include "editor/editor.h"
# endif
2015-04-19 04:18:49 +00:00
static void show_escort_menu ( const array < char , 300 > & ) ;
2015-06-13 22:42:15 +00:00
static void say_escort_goal ( escort_goal_t goal_num ) ;
2006-03-20 17:12:09 +00:00
2015-06-13 22:42:15 +00:00
static const array < char [ 12 ] , ESCORT_GOAL_MARKER9 > Escort_goal_text = { {
2006-03-20 17:12:09 +00:00
" BLUE KEY " ,
" YELLOW KEY " ,
" RED KEY " ,
" REACTOR " ,
" EXIT " ,
" ENERGY " ,
" ENERGYCEN " ,
" SHIELD " ,
" POWERUP " ,
" ROBOT " ,
" HOSTAGES " ,
" SPEW " ,
" SCRAM " ,
" EXIT " ,
" BOSS " ,
" MARKER 1 " ,
" MARKER 2 " ,
" MARKER 3 " ,
" MARKER 4 " ,
" MARKER 5 " ,
" MARKER 6 " ,
" MARKER 7 " ,
" MARKER 8 " ,
" MARKER 9 " ,
// -- too much work -- "KAMIKAZE "
2015-06-13 22:42:15 +00:00
} } ;
2006-03-20 17:12:09 +00:00
2015-01-12 00:26:04 +00:00
static int Max_escort_length = 200 ;
2006-03-20 17:12:09 +00:00
int Escort_kill_object = - 1 ;
2013-12-12 23:48:34 +00:00
stolen_items_t Stolen_items ;
2006-03-20 17:12:09 +00:00
int Stolen_item_index ;
2010-12-22 00:17:59 +00:00
fix64 Escort_last_path_created = 0 ;
2015-01-12 00:26:03 +00:00
static int Buddy_messages_suppressed ;
2015-06-13 22:42:15 +00:00
escort_goal_t Escort_goal_object = ESCORT_GOAL_UNSPECIFIED ;
escort_goal_t Escort_special_goal = ESCORT_GOAL_UNSPECIFIED ;
2010-12-22 00:17:59 +00:00
fix64 Buddy_sorry_time ;
2013-12-29 04:28:07 +00:00
objnum_t Escort_goal_index , Buddy_objnum ;
int Buddy_allowed_to_talk ;
2015-01-12 00:26:04 +00:00
static int Looking_for_marker ;
static int Last_buddy_key ;
2006-03-20 17:12:09 +00:00
2015-01-12 00:26:04 +00:00
static fix64 Last_buddy_message_time ;
2006-03-20 17:12:09 +00:00
void init_buddy_for_level ( void )
{
Buddy_allowed_to_talk = 0 ;
Escort_goal_object = ESCORT_GOAL_UNSPECIFIED ;
2015-06-13 22:42:15 +00:00
Escort_special_goal = ESCORT_GOAL_UNSPECIFIED ;
2013-12-29 04:28:07 +00:00
Escort_goal_index = object_none ;
2006-03-20 17:12:09 +00:00
Buddy_messages_suppressed = 0 ;
2015-01-12 00:26:04 +00:00
Buddy_objnum = find_escort ( ) ;
2006-03-20 17:12:09 +00:00
Buddy_sorry_time = - F1_0 ;
Looking_for_marker = - 1 ;
Last_buddy_key = - 1 ;
}
// -----------------------------------------------------------------------------
// See if segment from curseg through sidenum is reachable.
// Return true if it is reachable, else return false.
2014-10-02 03:02:34 +00:00
static int segment_is_reachable ( const vcsegptr_t segp , int sidenum )
2006-03-20 17:12:09 +00:00
{
2014-09-21 22:11:51 +00:00
int rval ;
2006-03-20 17:12:09 +00:00
if ( ! IS_CHILD ( segp - > children [ sidenum ] ) )
return 0 ;
2014-09-21 22:11:51 +00:00
auto wall_num = segp - > sides [ sidenum ] . wall_num ;
2006-03-20 17:12:09 +00:00
// If no wall, then it is reachable
2014-09-21 22:11:51 +00:00
if ( wall_num = = wall_none )
2006-03-20 17:12:09 +00:00
return 1 ;
2014-10-02 03:02:34 +00:00
rval = ai_door_is_openable ( nullptr , segp , sidenum ) ;
2006-03-20 17:12:09 +00:00
return rval ;
// -- MK, 10/17/95 --
// -- MK, 10/17/95 -- // Hmm, a closed wall. I think this mean not reachable.
// -- MK, 10/17/95 -- if (Walls[wall_num].type == WALL_CLOSED)
// -- MK, 10/17/95 -- return 0;
// -- MK, 10/17/95 --
// -- MK, 10/17/95 -- if (Walls[wall_num].type == WALL_DOOR) {
// -- MK, 10/17/95 -- if (Walls[wall_num].keys == KEY_NONE) {
// -- MK, 10/17/95 -- return 1; // @MK, 10/17/95: Be consistent with ai_door_is_openable
// -- MK, 10/17/95 -- // -- if (Walls[wall_num].flags & WALL_DOOR_LOCKED)
// -- MK, 10/17/95 -- // -- return 0;
// -- MK, 10/17/95 -- // -- else
// -- MK, 10/17/95 -- // -- return 1;
// -- MK, 10/17/95 -- } else if (Walls[wall_num].keys == KEY_BLUE)
// -- MK, 10/17/95 -- return (Players[Player_num].flags & PLAYER_FLAGS_BLUE_KEY);
// -- MK, 10/17/95 -- else if (Walls[wall_num].keys == KEY_GOLD)
// -- MK, 10/17/95 -- return (Players[Player_num].flags & PLAYER_FLAGS_GOLD_KEY);
// -- MK, 10/17/95 -- else if (Walls[wall_num].keys == KEY_RED)
// -- MK, 10/17/95 -- return (Players[Player_num].flags & PLAYER_FLAGS_RED_KEY);
// -- MK, 10/17/95 -- else
// -- MK, 10/17/95 -- Int3(); // Impossible! Doesn't have no key, but doesn't have any key!
// -- MK, 10/17/95 -- } else
// -- MK, 10/17/95 -- return 1;
// -- MK, 10/17/95 --
// -- MK, 10/17/95 -- Int3(); // Hmm, thought 'if' above had to return!
// -- MK, 10/17/95 -- return 0;
}
// -----------------------------------------------------------------------------
// Create a breadth-first list of segments reachable from current segment.
// max_segs is maximum number of segments to search. Use MAX_SEGMENTS to search all.
// On exit, *length <= max_segs.
// Input:
// start_seg
// Output:
// bfs_list: array of shorts, each reachable segment. Includes start segment.
// length: number of elements in bfs_list
2015-01-28 03:42:52 +00:00
std : : size_t create_bfs_list ( segnum_t start_seg , segnum_t * const bfs_list , std : : size_t max_segs )
2006-03-20 17:12:09 +00:00
{
2015-01-28 03:42:52 +00:00
std : : size_t head = 0 , tail = 0 ;
2013-12-18 23:40:02 +00:00
visited_segment_bitarray_t visited ;
2006-03-20 17:12:09 +00:00
bfs_list [ head + + ] = start_seg ;
2013-12-18 23:40:02 +00:00
visited [ start_seg ] = true ;
2006-03-20 17:12:09 +00:00
while ( ( head ! = tail ) & & ( head < max_segs ) ) {
segment * cursegp ;
2014-11-20 03:00:36 +00:00
auto curseg = bfs_list [ tail + + ] ;
2006-03-20 17:12:09 +00:00
cursegp = & Segments [ curseg ] ;
2014-09-26 02:42:16 +00:00
for ( int i = 0 ; i < MAX_SIDES_PER_SEGMENT ; i + + ) {
2014-11-20 03:00:36 +00:00
auto connected_seg = cursegp - > children [ i ] ;
2006-03-20 17:12:09 +00:00
2013-12-18 23:40:02 +00:00
if ( IS_CHILD ( connected_seg ) & & ( ! visited [ connected_seg ] ) ) {
2014-10-02 03:02:34 +00:00
if ( segment_is_reachable ( cursegp , i ) ) {
2006-03-20 17:12:09 +00:00
bfs_list [ head + + ] = connected_seg ;
if ( head > = max_segs )
break ;
2013-12-18 23:40:02 +00:00
visited [ connected_seg ] = true ;
2006-03-20 17:12:09 +00:00
Assert ( head < MAX_SEGMENTS ) ;
}
}
}
}
2015-01-28 03:42:52 +00:00
return head ;
2006-03-20 17:12:09 +00:00
}
// -----------------------------------------------------------------------------
// Return true if ok for buddy to talk, else return false.
// Buddy is allowed to talk if the segment he is in does not contain a blastable wall that has not been blasted
// AND he has never yet, since being initialized for level, been allowed to talk.
2013-10-27 22:00:14 +00:00
static int ok_for_buddy_to_talk ( void )
2006-03-20 17:12:09 +00:00
{
segment * segp ;
2013-12-26 22:21:16 +00:00
if ( Buddy_objnum = = object_none )
2011-09-26 16:58:12 +00:00
return 0 ;
2014-10-02 03:02:34 +00:00
const vobjptridx_t buddy = vobjptridx ( Buddy_objnum ) ;
2014-08-16 23:18:17 +00:00
if ( buddy - > type ! = OBJ_ROBOT ) {
2006-03-20 17:12:09 +00:00
Buddy_allowed_to_talk = 0 ;
return 0 ;
}
if ( Buddy_allowed_to_talk )
return 1 ;
2014-08-16 23:18:17 +00:00
segp = & Segments [ buddy - > segnum ] ;
2006-03-20 17:12:09 +00:00
2014-09-26 02:42:16 +00:00
for ( int i = 0 ; i < MAX_SIDES_PER_SEGMENT ; i + + ) {
2014-09-21 22:11:51 +00:00
auto wall_num = segp - > sides [ i ] . wall_num ;
2006-03-20 17:12:09 +00:00
2014-09-21 22:11:51 +00:00
if ( wall_num ! = wall_none ) {
2006-03-20 17:12:09 +00:00
if ( ( Walls [ wall_num ] . type = = WALL_BLASTABLE ) & & ! ( Walls [ wall_num ] . flags & WALL_BLASTED ) )
return 0 ;
}
// Check one level deeper.
if ( IS_CHILD ( segp - > children [ i ] ) ) {
segment * csegp = & Segments [ segp - > children [ i ] ] ;
2015-02-05 03:03:49 +00:00
range_for ( const auto j , csegp - > sides )
2015-01-12 00:26:04 +00:00
{
auto wall2 = j . wall_num ;
2014-10-04 15:04:44 +00:00
if ( wall2 ! = wall_none ) {
2006-03-20 17:12:09 +00:00
if ( ( Walls [ wall2 ] . type = = WALL_BLASTABLE ) & & ! ( Walls [ wall2 ] . flags & WALL_BLASTED ) )
return 0 ;
}
}
}
}
Buddy_allowed_to_talk = 1 ;
return 1 ;
}
2012-12-09 05:07:45 +00:00
static void record_escort_goal_accomplished ( )
{
if ( ok_for_buddy_to_talk ( ) ) {
digi_play_sample_once ( SOUND_BUDDY_MET_GOAL , F1_0 ) ;
2013-12-29 04:28:07 +00:00
Escort_goal_index = object_none ;
2012-12-09 05:07:45 +00:00
Escort_goal_object = ESCORT_GOAL_UNSPECIFIED ;
2015-06-13 22:42:15 +00:00
Escort_special_goal = ESCORT_GOAL_UNSPECIFIED ;
2012-12-09 05:07:45 +00:00
Looking_for_marker = - 1 ;
}
}
2006-03-20 17:12:09 +00:00
// --------------------------------------------------------------------------------------------
2012-12-08 21:28:59 +00:00
void detect_escort_goal_fuelcen_accomplished ( )
{
if ( ! Buddy_allowed_to_talk )
return ;
if ( Escort_special_goal = = ESCORT_GOAL_ENERGYCEN )
record_escort_goal_accomplished ( ) ;
}
2014-12-23 04:20:27 +00:00
void detect_escort_goal_accomplished ( const vobjptridx_t index )
2006-03-20 17:12:09 +00:00
{
if ( ! Buddy_allowed_to_talk )
return ;
// If goal is to go away, how can it be achieved?
if ( Escort_special_goal = = ESCORT_GOAL_SCRAM )
return ;
// See if goal found was a key. Need to handle default goals differently.
// Note, no buddy_met_goal sound when blow up reactor or exit. Not great, but ok
// since for reactor, noisy, for exit, buddy is disappearing.
2015-06-13 22:42:15 +00:00
if ( Escort_special_goal = = ESCORT_GOAL_UNSPECIFIED & & Escort_goal_index = = index )
{
2012-12-09 05:07:45 +00:00
record_escort_goal_accomplished ( ) ;
return ;
2006-03-20 17:12:09 +00:00
}
2013-12-26 23:12:54 +00:00
if ( index - > type = = OBJ_POWERUP ) {
if ( index - > id = = POW_KEY_BLUE ) {
2015-06-13 22:42:15 +00:00
if ( Escort_goal_object = = ESCORT_GOAL_BLUE_KEY ) {
2012-12-09 05:07:45 +00:00
record_escort_goal_accomplished ( ) ;
return ;
2006-03-20 17:12:09 +00:00
}
2013-12-26 23:12:54 +00:00
} else if ( index - > id = = POW_KEY_GOLD ) {
2015-06-13 22:42:15 +00:00
if ( Escort_goal_object = = ESCORT_GOAL_GOLD_KEY ) {
2012-12-09 05:07:45 +00:00
record_escort_goal_accomplished ( ) ;
return ;
2006-03-20 17:12:09 +00:00
}
2013-12-26 23:12:54 +00:00
} else if ( index - > id = = POW_KEY_RED ) {
2015-06-13 22:42:15 +00:00
if ( Escort_goal_object = = ESCORT_GOAL_RED_KEY ) {
2012-12-09 05:07:45 +00:00
record_escort_goal_accomplished ( ) ;
return ;
2006-03-20 17:12:09 +00:00
}
}
}
2015-06-13 22:42:15 +00:00
if ( Escort_special_goal ! = ESCORT_GOAL_UNSPECIFIED )
2006-03-20 17:12:09 +00:00
{
if ( Escort_special_goal = = ESCORT_GOAL_ENERGYCEN ) {
2013-12-26 23:12:54 +00:00
} else if ( ( index - > type = = OBJ_POWERUP ) & & ( Escort_special_goal = = ESCORT_GOAL_POWERUP ) )
2012-12-09 05:07:45 +00:00
record_escort_goal_accomplished ( ) ; // Any type of powerup picked up will do.
2013-12-26 23:12:54 +00:00
else if ( ( index - > type = = Objects [ Escort_goal_index ] . type ) & & ( index - > id = = Objects [ Escort_goal_index ] . id ) ) {
2006-03-20 17:12:09 +00:00
// Note: This will help a little bit in making the buddy believe a goal is satisfied. Won't work for a general goal like "find any powerup"
// because of the insistence of both type and id matching.
2012-12-09 05:07:45 +00:00
record_escort_goal_accomplished ( ) ;
2006-03-20 17:12:09 +00:00
}
}
}
void change_guidebot_name ( )
{
int item ;
2015-01-12 00:26:02 +00:00
auto text = PlayerCfg . GuidebotName ;
2015-03-22 18:49:21 +00:00
array < newmenu_item , 1 > m { {
2015-01-18 01:58:31 +00:00
nm_item_input ( text ) ,
2015-03-22 18:49:21 +00:00
} } ;
2015-01-18 01:58:32 +00:00
item = newmenu_do ( NULL , " Enter Guide-bot name: " , m , unused_newmenu_subfunction , unused_newmenu_userdata ) ;
2006-03-20 17:12:09 +00:00
if ( item ! = - 1 ) {
2015-01-12 00:26:02 +00:00
PlayerCfg . GuidebotName = PlayerCfg . GuidebotNameReal = text ;
2006-03-20 17:12:09 +00:00
write_player_file ( ) ;
}
}
// -----------------------------------------------------------------------------
2013-12-08 23:37:40 +00:00
static int show_buddy_message ( )
2006-03-20 17:12:09 +00:00
{
if ( Buddy_messages_suppressed )
2013-12-08 23:37:40 +00:00
return 0 ;
2006-03-20 17:12:09 +00:00
if ( Game_mode & GM_MULTI )
2013-12-08 23:37:40 +00:00
return 0 ;
2006-03-20 17:12:09 +00:00
2010-12-22 00:17:59 +00:00
if ( Last_buddy_message_time + F1_0 < GameTime64 ) {
2013-12-08 23:37:40 +00:00
if ( ok_for_buddy_to_talk ( ) )
return 1 ;
}
return 0 ;
}
static void _buddy_message ( const char * str )
{
2015-01-12 00:26:02 +00:00
HUD_init_message ( HM_DEFAULT , " %c%c%s:%c%c %s " , CC_COLOR , BM_XRGB ( 28 , 0 , 0 ) , static_cast < const char * > ( PlayerCfg . GuidebotName ) , CC_COLOR , BM_XRGB ( 0 , 31 , 0 ) , str ) ;
2013-12-08 23:37:40 +00:00
Last_buddy_message_time = GameTime64 ;
}
void ( buddy_message ) ( const char * format , . . . )
{
if ( ! show_buddy_message ( ) )
return ;
2012-06-24 21:05:03 +00:00
char new_format [ 128 ] ;
2006-03-20 17:12:09 +00:00
va_list args ;
va_start ( args , format ) ;
2012-06-24 21:05:03 +00:00
vsnprintf ( new_format , sizeof ( new_format ) , format , args ) ;
2006-03-20 17:12:09 +00:00
va_end ( args ) ;
2013-12-08 23:37:40 +00:00
_buddy_message ( new_format ) ;
}
2006-03-20 17:12:09 +00:00
2013-12-08 23:37:40 +00:00
void buddy_message_str ( const char * str )
{
if ( ! show_buddy_message ( ) )
return ;
_buddy_message ( str ) ;
2006-03-20 17:12:09 +00:00
}
// -----------------------------------------------------------------------------
2013-12-08 23:37:40 +00:00
static void thief_message_str ( const char * str ) __attribute_nonnull ( ) ;
static void thief_message_str ( const char * str )
{
HUD_init_message ( HM_DEFAULT , " %c%cTHIEF:%c%c %s " , 1 , BM_XRGB ( 28 , 0 , 0 ) , 1 , BM_XRGB ( 0 , 31 , 0 ) , str ) ;
}
2013-06-27 02:35:22 +00:00
static void thief_message ( const char * format , . . . ) __attribute_format_printf ( 1 , 2 ) ;
static void thief_message ( const char * format , . . . )
2013-12-08 23:37:40 +00:00
# define thief_message(F,...) dxx_call_printf_checked(thief_message,thief_message_str,(),(F),##__VA_ARGS__)
2006-03-20 17:12:09 +00:00
{
2012-06-24 21:05:03 +00:00
char new_format [ 128 ] ;
2006-03-20 17:12:09 +00:00
va_list args ;
va_start ( args , format ) ;
2012-06-24 21:05:03 +00:00
vsnprintf ( new_format , sizeof ( new_format ) , format , args ) ;
2006-03-20 17:12:09 +00:00
va_end ( args ) ;
2013-12-08 23:37:40 +00:00
thief_message_str ( new_format ) ;
2006-03-20 17:12:09 +00:00
}
// -----------------------------------------------------------------------------
// Return true if marker #id has been placed.
2013-10-27 22:00:14 +00:00
static int marker_exists_in_mine ( int id )
2006-03-20 17:12:09 +00:00
{
2015-02-05 03:03:49 +00:00
range_for ( const auto i , highest_valid ( Objects ) )
2015-06-13 22:42:16 +00:00
{
const auto & & objp = vcobjptr ( static_cast < objnum_t > ( i ) ) ;
if ( objp - > type = = OBJ_MARKER )
if ( objp - > id = = id )
2006-03-20 17:12:09 +00:00
return 1 ;
2015-06-13 22:42:16 +00:00
}
2006-03-20 17:12:09 +00:00
return 0 ;
}
// -----------------------------------------------------------------------------
void set_escort_special_goal ( int special_key )
{
int marker_key ;
Buddy_messages_suppressed = 0 ;
if ( ! Buddy_allowed_to_talk ) {
ok_for_buddy_to_talk ( ) ;
if ( ! Buddy_allowed_to_talk ) {
2015-01-12 00:26:04 +00:00
auto o = find_escort ( ) ;
if ( o = = object_none )
HUD_init_message_literal ( HM_DEFAULT , " No Guide-Bot in mine. " ) ;
else
HUD_init_message ( HM_DEFAULT , " %s has not been released. " , static_cast < const char * > ( PlayerCfg . GuidebotName ) ) ;
2006-03-20 17:12:09 +00:00
return ;
}
}
special_key = special_key & ( ~ KEY_SHIFTED ) ;
marker_key = special_key ;
if ( Last_buddy_key = = special_key )
{
if ( ( Looking_for_marker = = - 1 ) & & ( special_key ! = KEY_0 ) ) {
if ( marker_exists_in_mine ( marker_key - KEY_1 ) )
Looking_for_marker = marker_key - KEY_1 ;
else {
Last_buddy_message_time = 0 ; // Force this message to get through.
buddy_message ( " Marker %i not placed. " , marker_key - KEY_1 + 1 ) ;
Looking_for_marker = - 1 ;
}
} else {
Looking_for_marker = - 1 ;
}
}
Last_buddy_key = special_key ;
if ( special_key = = KEY_0 )
Looking_for_marker = - 1 ;
if ( Looking_for_marker ! = - 1 ) {
2015-06-13 22:42:15 +00:00
Escort_special_goal = static_cast < escort_goal_t > ( ESCORT_GOAL_MARKER1 + marker_key - KEY_1 ) ;
2006-03-20 17:12:09 +00:00
} else {
switch ( special_key ) {
case KEY_1 : Escort_special_goal = ESCORT_GOAL_ENERGY ; break ;
case KEY_2 : Escort_special_goal = ESCORT_GOAL_ENERGYCEN ; break ;
case KEY_3 : Escort_special_goal = ESCORT_GOAL_SHIELD ; break ;
case KEY_4 : Escort_special_goal = ESCORT_GOAL_POWERUP ; break ;
case KEY_5 : Escort_special_goal = ESCORT_GOAL_ROBOT ; break ;
case KEY_6 : Escort_special_goal = ESCORT_GOAL_HOSTAGE ; break ;
case KEY_7 : Escort_special_goal = ESCORT_GOAL_SCRAM ; break ;
case KEY_8 : Escort_special_goal = ESCORT_GOAL_PLAYER_SPEW ; break ;
case KEY_9 : Escort_special_goal = ESCORT_GOAL_EXIT ; break ;
2015-06-13 22:42:15 +00:00
case KEY_0 : Escort_special_goal = ESCORT_GOAL_UNSPECIFIED ; break ;
2006-03-20 17:12:09 +00:00
default :
Int3 ( ) ; // Oops, called with illegal key value.
}
}
2010-12-22 00:17:59 +00:00
Last_buddy_message_time = GameTime64 - 2 * F1_0 ; // Allow next message to come through.
2006-03-20 17:12:09 +00:00
say_escort_goal ( Escort_special_goal ) ;
// -- Escort_goal_object = escort_set_goal_object();
Escort_goal_object = ESCORT_GOAL_UNSPECIFIED ;
}
// -----------------------------------------------------------------------------
// Return id of boss.
2013-10-27 22:00:14 +00:00
static int get_boss_id ( void )
2006-03-20 17:12:09 +00:00
{
2015-02-05 03:03:49 +00:00
range_for ( const auto i , highest_valid ( Objects ) )
2015-06-13 22:42:16 +00:00
{
const auto & & objp = vcobjptr ( static_cast < objnum_t > ( i ) ) ;
if ( objp - > type = = OBJ_ROBOT )
if ( Robot_info [ get_robot_id ( objp ) ] . boss_flag )
return objp - > id ;
}
2006-03-20 17:12:09 +00:00
return - 1 ;
}
// -----------------------------------------------------------------------------
// Return object index if object of objtype, objid exists in mine, else return -1
// "special" is used to find objects spewed by player which is hacked into flags field of powerup.
2014-11-23 04:58:45 +00:00
static objnum_t exists_in_mine_2 ( const vcsegptridx_t segp , int objtype , int objid , int special )
2006-03-20 17:12:09 +00:00
{
2015-02-05 03:03:49 +00:00
range_for ( const auto curobjp , objects_in ( segp ) )
2014-07-29 03:21:53 +00:00
{
const auto & objnum = curobjp ;
2015-01-28 03:42:52 +00:00
if ( special = = ESCORT_GOAL_PLAYER_SPEW & & curobjp - > type = = OBJ_POWERUP )
{
2006-03-20 17:12:09 +00:00
if ( curobjp - > flags & OF_PLAYER_DROPPED )
return objnum ;
}
if ( curobjp - > type = = objtype ) {
// Don't find escort robots if looking for robot!
if ( ( curobjp - > type = = OBJ_ROBOT ) & & ( Robot_info [ curobjp - > id ] . companion ) )
;
else if ( objid = = - 1 ) {
if ( ( objtype = = OBJ_POWERUP ) & & ( curobjp - > id ! = POW_KEY_BLUE ) & & ( curobjp - > id ! = POW_KEY_GOLD ) & & ( curobjp - > id ! = POW_KEY_RED ) )
return objnum ;
else
return objnum ;
} else if ( curobjp - > id = = objid )
return objnum ;
}
2015-01-28 03:42:52 +00:00
if ( objtype = = OBJ_POWERUP & & curobjp - > type = = OBJ_ROBOT )
2006-03-20 17:12:09 +00:00
if ( curobjp - > contains_count )
if ( curobjp - > contains_type = = OBJ_POWERUP )
if ( curobjp - > contains_id = = objid )
return objnum ;
}
2013-12-26 22:21:16 +00:00
return object_none ;
2006-03-20 17:12:09 +00:00
}
// -----------------------------------------------------------------------------
2013-12-29 04:28:07 +00:00
static segnum_t exists_fuelcen_in_mine ( segnum_t start_seg )
2006-03-20 17:12:09 +00:00
{
2015-01-28 03:42:52 +00:00
array < segnum_t , MAX_SEGMENTS > bfs_list ;
const auto length = create_bfs_list ( start_seg , bfs_list ) ;
2014-10-21 03:15:12 +00:00
auto predicate = [ ] ( const segnum_t & s ) { return Segments [ s ] . special = = SEGMENT_IS_FUELCEN ; } ;
{
auto rb = partial_range ( bfs_list , length ) ;
auto i = std : : find_if ( rb . begin ( ) , rb . end ( ) , predicate ) ;
if ( i ! = rb . end ( ) )
return * i ;
}
{
auto rh = highest_valid ( Segments ) ;
if ( std : : find_if ( rh . begin ( ) , rh . end ( ) , predicate ) ! = rh . end ( ) )
return segment_exit ;
}
2013-12-29 04:28:07 +00:00
return segment_none ;
2012-12-08 23:36:56 +00:00
}
// Return nearest object of interest.
// If special == ESCORT_GOAL_PLAYER_SPEW, then looking for any object spewed by player.
// -1 means object does not exist in mine.
// -2 means object does exist in mine, but buddy-bot can't reach it (eg, behind triggered wall)
2013-12-29 04:28:07 +00:00
static objnum_t exists_in_mine ( segnum_t start_seg , int objtype , int objid , int special )
2012-12-08 23:36:56 +00:00
{
2015-01-28 03:42:52 +00:00
array < segnum_t , MAX_SEGMENTS > bfs_list ;
const auto length = create_bfs_list ( start_seg , bfs_list ) ;
2012-12-08 23:36:56 +00:00
2015-02-05 03:03:49 +00:00
range_for ( const auto segnum , partial_range ( bfs_list , length ) )
2015-01-12 00:26:04 +00:00
{
auto objnum = exists_in_mine_2 ( vcsegptridx ( segnum ) , objtype , objid , special ) ;
2013-12-26 22:21:16 +00:00
if ( objnum ! = object_none )
2006-03-20 17:12:09 +00:00
return objnum ;
}
// Couldn't find what we're looking for by looking at connectivity.
// See if it's in the mine. It could be hidden behind a trigger or switch
// which the buddybot doesn't understand.
2015-02-05 03:03:49 +00:00
range_for ( const auto segnum , highest_valid ( Segments ) )
2014-10-12 23:10:05 +00:00
{
2014-11-26 03:39:21 +00:00
auto objnum = exists_in_mine_2 ( vsegptridx ( segnum ) , objtype , objid , special ) ;
2013-12-26 22:21:16 +00:00
if ( objnum ! = object_none )
return object_guidebot_cannot_reach ;
2006-03-20 17:12:09 +00:00
}
2013-12-26 22:21:16 +00:00
return object_none ;
2006-03-20 17:12:09 +00:00
}
// -----------------------------------------------------------------------------
// Return true if it happened, else return false.
2013-12-29 04:28:07 +00:00
static segnum_t find_exit_segment ( void )
2006-03-20 17:12:09 +00:00
{
// ---------- Find exit doors ----------
2015-02-05 03:03:49 +00:00
range_for ( const auto i , highest_valid ( Segments ) )
2015-06-13 22:42:16 +00:00
{
const auto & & segp = vcsegptr ( static_cast < segnum_t > ( i ) ) ;
range_for ( const auto j , segp - > children )
2015-01-12 00:26:04 +00:00
if ( j = = segment_exit )
2006-03-20 17:12:09 +00:00
return i ;
2015-06-13 22:42:16 +00:00
}
2013-12-26 22:21:16 +00:00
return segment_none ;
2006-03-20 17:12:09 +00:00
}
// -----------------------------------------------------------------------------
2015-06-13 22:42:15 +00:00
static void say_escort_goal ( escort_goal_t goal_num )
2006-03-20 17:12:09 +00:00
{
if ( Player_is_dead )
return ;
switch ( goal_num ) {
2012-12-08 21:35:30 +00:00
default :
case ESCORT_GOAL_UNSPECIFIED :
break ;
2006-03-20 17:12:09 +00:00
case ESCORT_GOAL_BLUE_KEY : buddy_message ( " Finding BLUE KEY " ) ; break ;
case ESCORT_GOAL_GOLD_KEY : buddy_message ( " Finding YELLOW KEY " ) ; break ;
case ESCORT_GOAL_RED_KEY : buddy_message ( " Finding RED KEY " ) ; break ;
case ESCORT_GOAL_CONTROLCEN : buddy_message ( " Finding REACTOR " ) ; break ;
case ESCORT_GOAL_EXIT : buddy_message ( " Finding EXIT " ) ; break ;
case ESCORT_GOAL_ENERGY : buddy_message ( " Finding ENERGY " ) ; break ;
case ESCORT_GOAL_ENERGYCEN : buddy_message ( " Finding ENERGY CENTER " ) ; break ;
case ESCORT_GOAL_SHIELD : buddy_message ( " Finding a SHIELD " ) ; break ;
case ESCORT_GOAL_POWERUP : buddy_message ( " Finding a POWERUP " ) ; break ;
case ESCORT_GOAL_ROBOT : buddy_message ( " Finding a ROBOT " ) ; break ;
case ESCORT_GOAL_HOSTAGE : buddy_message ( " Finding a HOSTAGE " ) ; break ;
case ESCORT_GOAL_SCRAM : buddy_message ( " Staying away... " ) ; break ;
case ESCORT_GOAL_BOSS : buddy_message ( " Finding BOSS robot " ) ; break ;
case ESCORT_GOAL_PLAYER_SPEW : buddy_message ( " Finding your powerups " ) ; break ;
case ESCORT_GOAL_MARKER1 :
case ESCORT_GOAL_MARKER2 :
case ESCORT_GOAL_MARKER3 :
case ESCORT_GOAL_MARKER4 :
case ESCORT_GOAL_MARKER5 :
case ESCORT_GOAL_MARKER6 :
case ESCORT_GOAL_MARKER7 :
case ESCORT_GOAL_MARKER8 :
case ESCORT_GOAL_MARKER9 :
2013-12-28 18:56:34 +00:00
buddy_message ( " Finding marker %i: '%.24s' " , goal_num - ESCORT_GOAL_MARKER1 + 1 , & MarkerMessage [ goal_num - ESCORT_GOAL_MARKER1 ] [ 0 ] ) ;
2006-03-20 17:12:09 +00:00
break ;
}
}
2015-06-13 22:42:15 +00:00
static void clear_escort_goals ( )
{
Escort_goal_object = ESCORT_GOAL_UNSPECIFIED ;
Escort_special_goal = ESCORT_GOAL_UNSPECIFIED ;
}
static void escort_goal_does_not_exist ( escort_goal_t goal )
{
Last_buddy_message_time = 0 ; // Force this message to get through.
buddy_message ( " No %s in mine. " , Escort_goal_text [ goal - 1 ] ) ;
Looking_for_marker = - 1 ;
clear_escort_goals ( ) ;
}
static void escort_goal_unreachable ( escort_goal_t goal )
{
Last_buddy_message_time = 0 ; // Force this message to get through.
buddy_message ( " Can't reach %s. " , Escort_goal_text [ goal - 1 ] ) ;
Looking_for_marker = - 1 ;
clear_escort_goals ( ) ;
}
static void escort_go_to_goal ( const vobjptridx_t objp , ai_static * aip , segnum_t goal_seg )
{
create_path_to_segment ( objp , goal_seg , Max_escort_length , 1 ) ; // MK!: Last parm (safety_flag) used to be 1!!
if ( aip - > path_length > 3 )
aip - > path_length = polish_path ( objp , & Point_segs [ aip - > hide_index ] , aip - > path_length ) ;
if ( ( aip - > path_length > 0 ) & & ( Point_segs [ aip - > hide_index + aip - > path_length - 1 ] . segnum ! = goal_seg ) ) {
fix dist_to_player ;
Last_buddy_message_time = 0 ; // Force this message to get through.
buddy_message ( " Can't reach %s. " , Escort_goal_text [ Escort_goal_object - 1 ] ) ;
Looking_for_marker = - 1 ;
Escort_goal_object = ESCORT_GOAL_SCRAM ;
dist_to_player = find_connected_distance ( objp - > pos , objp - > segnum , Believed_player_pos , Believed_player_seg , 100 , WID_FLY_FLAG ) ;
if ( dist_to_player > MIN_ESCORT_DISTANCE )
create_path_to_player ( objp , Max_escort_length , 1 ) ; // MK!: Last parm used to be 1!
else {
create_n_segment_path ( objp , 8 + d_rand ( ) * 8 , segment_none ) ;
aip - > path_length = polish_path ( objp , & Point_segs [ aip - > hide_index ] , aip - > path_length ) ;
}
}
}
2006-03-20 17:12:09 +00:00
// -----------------------------------------------------------------------------
2014-10-02 03:02:34 +00:00
static void escort_create_path_to_goal ( const vobjptridx_t objp )
2006-03-20 17:12:09 +00:00
{
2013-12-29 04:28:07 +00:00
segnum_t goal_seg = segment_none ;
2006-03-20 17:12:09 +00:00
ai_static * aip = & objp - > ctype . ai_info ;
2013-12-25 03:16:41 +00:00
ai_local * ailp = & objp - > ctype . ai_info . ail ;
2006-03-20 17:12:09 +00:00
2015-06-13 22:42:15 +00:00
if ( Escort_special_goal ! = ESCORT_GOAL_UNSPECIFIED )
2006-03-20 17:12:09 +00:00
Escort_goal_object = Escort_special_goal ;
Escort_kill_object = - 1 ;
if ( Looking_for_marker ! = - 1 ) {
Escort_goal_index = exists_in_mine ( objp - > segnum , OBJ_MARKER , Escort_goal_object - ESCORT_GOAL_MARKER1 , - 1 ) ;
2013-12-29 04:28:07 +00:00
if ( Escort_goal_index ! = object_none )
2006-03-20 17:12:09 +00:00
goal_seg = Objects [ Escort_goal_index ] . segnum ;
} else {
switch ( Escort_goal_object ) {
case ESCORT_GOAL_BLUE_KEY :
Escort_goal_index = exists_in_mine ( objp - > segnum , OBJ_POWERUP , POW_KEY_BLUE , - 1 ) ;
2013-12-29 04:28:07 +00:00
if ( Escort_goal_index ! = object_none ) goal_seg = Objects [ Escort_goal_index ] . segnum ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_GOLD_KEY :
Escort_goal_index = exists_in_mine ( objp - > segnum , OBJ_POWERUP , POW_KEY_GOLD , - 1 ) ;
2013-12-29 04:28:07 +00:00
if ( Escort_goal_index ! = object_none ) goal_seg = Objects [ Escort_goal_index ] . segnum ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_RED_KEY :
Escort_goal_index = exists_in_mine ( objp - > segnum , OBJ_POWERUP , POW_KEY_RED , - 1 ) ;
2013-12-29 04:28:07 +00:00
if ( Escort_goal_index ! = object_none ) goal_seg = Objects [ Escort_goal_index ] . segnum ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_CONTROLCEN :
Escort_goal_index = exists_in_mine ( objp - > segnum , OBJ_CNTRLCEN , - 1 , - 1 ) ;
2013-12-29 04:28:07 +00:00
if ( Escort_goal_index ! = object_none ) goal_seg = Objects [ Escort_goal_index ] . segnum ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_EXIT :
goal_seg = find_exit_segment ( ) ;
Escort_goal_index = goal_seg ;
break ;
case ESCORT_GOAL_ENERGY :
Escort_goal_index = exists_in_mine ( objp - > segnum , OBJ_POWERUP , POW_ENERGY , - 1 ) ;
2013-12-29 04:28:07 +00:00
if ( Escort_goal_index ! = object_none ) goal_seg = Objects [ Escort_goal_index ] . segnum ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_ENERGYCEN :
2012-12-08 23:36:56 +00:00
goal_seg = exists_fuelcen_in_mine ( objp - > segnum ) ;
2015-06-13 22:42:15 +00:00
if ( goal_seg = = segment_none )
escort_goal_does_not_exist ( ESCORT_GOAL_ENERGYCEN ) ;
else if ( goal_seg = = segment_exit )
escort_goal_unreachable ( ESCORT_GOAL_ENERGYCEN ) ;
else
escort_go_to_goal ( objp , aip , goal_seg ) ;
return ;
2006-03-20 17:12:09 +00:00
case ESCORT_GOAL_SHIELD :
Escort_goal_index = exists_in_mine ( objp - > segnum , OBJ_POWERUP , POW_SHIELD_BOOST , - 1 ) ;
2013-12-29 04:28:07 +00:00
if ( Escort_goal_index ! = object_none ) goal_seg = Objects [ Escort_goal_index ] . segnum ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_POWERUP :
Escort_goal_index = exists_in_mine ( objp - > segnum , OBJ_POWERUP , - 1 , - 1 ) ;
2013-12-29 04:28:07 +00:00
if ( Escort_goal_index ! = object_none ) goal_seg = Objects [ Escort_goal_index ] . segnum ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_ROBOT :
Escort_goal_index = exists_in_mine ( objp - > segnum , OBJ_ROBOT , - 1 , - 1 ) ;
2013-12-29 04:28:07 +00:00
if ( Escort_goal_index ! = object_none ) goal_seg = Objects [ Escort_goal_index ] . segnum ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_HOSTAGE :
Escort_goal_index = exists_in_mine ( objp - > segnum , OBJ_HOSTAGE , - 1 , - 1 ) ;
2013-12-29 04:28:07 +00:00
if ( Escort_goal_index ! = object_none ) goal_seg = Objects [ Escort_goal_index ] . segnum ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_PLAYER_SPEW :
Escort_goal_index = exists_in_mine ( objp - > segnum , - 1 , - 1 , ESCORT_GOAL_PLAYER_SPEW ) ;
2013-12-29 04:28:07 +00:00
if ( Escort_goal_index ! = object_none ) goal_seg = Objects [ Escort_goal_index ] . segnum ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_SCRAM :
2012-11-24 17:59:53 +00:00
Escort_goal_index = - 3 ; // Kinda a hack.
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_BOSS : {
int boss_id ;
boss_id = get_boss_id ( ) ;
Assert ( boss_id ! = - 1 ) ;
Escort_goal_index = exists_in_mine ( objp - > segnum , OBJ_ROBOT , boss_id , - 1 ) ;
2013-12-29 04:28:07 +00:00
if ( Escort_goal_index ! = object_none ) goal_seg = Objects [ Escort_goal_index ] . segnum ;
2006-03-20 17:12:09 +00:00
break ;
}
default :
Int3 ( ) ; // Oops, Illegal value in Escort_goal_object.
goal_seg = 0 ;
break ;
}
}
2013-12-29 04:28:07 +00:00
if ( Escort_goal_index = = object_none ) {
2015-06-13 22:42:15 +00:00
escort_goal_does_not_exist ( Escort_goal_object ) ;
2013-12-29 04:28:07 +00:00
} else if ( Escort_goal_index = = object_guidebot_cannot_reach ) {
2015-06-13 22:42:15 +00:00
escort_goal_unreachable ( Escort_goal_object ) ;
2006-03-20 17:12:09 +00:00
} else {
2012-11-24 17:59:53 +00:00
if ( Escort_goal_object = = ESCORT_GOAL_SCRAM ) {
2013-12-29 04:28:07 +00:00
create_n_segment_path ( objp , 16 + d_rand ( ) * 16 , segment_none ) ;
2006-03-20 17:12:09 +00:00
aip - > path_length = polish_path ( objp , & Point_segs [ aip - > hide_index ] , aip - > path_length ) ;
} else {
2015-06-13 22:42:15 +00:00
escort_go_to_goal ( objp , aip , goal_seg ) ;
2006-03-20 17:12:09 +00:00
}
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_GOTO_OBJECT ;
2006-03-20 17:12:09 +00:00
say_escort_goal ( Escort_goal_object ) ;
}
}
// -----------------------------------------------------------------------------
// Escort robot chooses goal object based on player's keys, location.
// Returns goal object.
2015-06-13 22:42:15 +00:00
static escort_goal_t escort_set_goal_object ( void )
2006-03-20 17:12:09 +00:00
{
2015-06-13 22:42:15 +00:00
if ( Escort_special_goal ! = ESCORT_GOAL_UNSPECIFIED )
2006-03-20 17:12:09 +00:00
return ESCORT_GOAL_UNSPECIFIED ;
2015-05-28 03:08:40 +00:00
else if ( ! ( ConsoleObject - > flags & PLAYER_FLAGS_BLUE_KEY ) & & ( exists_in_mine ( ConsoleObject - > segnum , OBJ_POWERUP , POW_KEY_BLUE , - 1 ) ! = object_none ) )
2006-03-20 17:12:09 +00:00
return ESCORT_GOAL_BLUE_KEY ;
2015-05-28 03:08:40 +00:00
else if ( ! ( ConsoleObject - > flags & PLAYER_FLAGS_GOLD_KEY ) & & ( exists_in_mine ( ConsoleObject - > segnum , OBJ_POWERUP , POW_KEY_GOLD , - 1 ) ! = object_none ) )
2006-03-20 17:12:09 +00:00
return ESCORT_GOAL_GOLD_KEY ;
2015-05-28 03:08:40 +00:00
else if ( ! ( ConsoleObject - > flags & PLAYER_FLAGS_RED_KEY ) & & ( exists_in_mine ( ConsoleObject - > segnum , OBJ_POWERUP , POW_KEY_RED , - 1 ) ! = object_none ) )
2006-03-20 17:12:09 +00:00
return ESCORT_GOAL_RED_KEY ;
else if ( Control_center_destroyed = = 0 ) {
2013-12-11 23:59:36 +00:00
if ( Boss_teleport_segs . count ( ) )
2006-03-20 17:12:09 +00:00
return ESCORT_GOAL_BOSS ;
else
return ESCORT_GOAL_CONTROLCEN ;
} else
return ESCORT_GOAL_EXIT ;
}
# define MAX_ESCORT_TIME_AWAY (F1_0*4)
2010-12-22 00:17:59 +00:00
fix64 Buddy_last_seen_player = 0 , Buddy_last_player_path_created ;
2006-03-20 17:12:09 +00:00
// -----------------------------------------------------------------------------
2014-10-02 03:02:34 +00:00
static int time_to_visit_player ( const vobjptr_t objp , ai_local * ailp , ai_static * aip )
2006-03-20 17:12:09 +00:00
{
// Note: This one has highest priority because, even if already going towards player,
// might be necessary to create a new path, as player can move.
2010-12-22 00:17:59 +00:00
if ( GameTime64 - Buddy_last_seen_player > MAX_ESCORT_TIME_AWAY )
if ( GameTime64 - Buddy_last_player_path_created > F1_0 )
2006-03-20 17:12:09 +00:00
return 1 ;
2015-04-26 20:15:51 +00:00
if ( ailp - > mode = = ai_mode : : AIM_GOTO_PLAYER )
2006-03-20 17:12:09 +00:00
return 0 ;
if ( objp - > segnum = = ConsoleObject - > segnum )
return 0 ;
if ( aip - > cur_path_index < aip - > path_length / 2 )
return 0 ;
return 1 ;
}
2010-12-22 00:17:59 +00:00
fix64 Last_come_back_message_time = 0 ;
2006-03-20 17:12:09 +00:00
2010-12-22 00:17:59 +00:00
fix64 Buddy_last_missile_time ;
2006-03-20 17:12:09 +00:00
// -----------------------------------------------------------------------------
2014-10-02 03:02:34 +00:00
static void bash_buddy_weapon_info ( const vobjptridx_t objp )
2006-03-20 17:12:09 +00:00
{
objp - > ctype . laser_info . parent_num = ConsoleObject - Objects ;
objp - > ctype . laser_info . parent_type = OBJ_PLAYER ;
objp - > ctype . laser_info . parent_signature = ConsoleObject - > signature ;
}
// -----------------------------------------------------------------------------
2014-10-02 03:02:34 +00:00
static int maybe_buddy_fire_mega ( const vobjptridx_t objp )
2006-03-20 17:12:09 +00:00
{
2014-10-02 03:02:34 +00:00
const vobjptridx_t buddy_objp = vobjptridx ( Buddy_objnum ) ;
2006-03-20 17:12:09 +00:00
fix dist , dot ;
2014-10-29 03:24:31 +00:00
auto vec_to_robot = vm_vec_sub ( buddy_objp - > pos , objp - > pos ) ;
2014-09-28 21:11:04 +00:00
dist = vm_vec_normalize_quick ( vec_to_robot ) ;
2006-03-20 17:12:09 +00:00
if ( dist > F1_0 * 100 )
return 0 ;
2014-09-28 21:11:48 +00:00
dot = vm_vec_dot ( vec_to_robot , buddy_objp - > orient . fvec ) ;
2006-03-20 17:12:09 +00:00
if ( dot < F1_0 / 2 )
return 0 ;
if ( ! object_to_object_visibility ( buddy_objp , objp , FQ_TRANSWALL ) )
return 0 ;
if ( Weapon_info [ MEGA_ID ] . render_type = = 0 ) {
2013-12-07 00:47:27 +00:00
con_printf ( CON_VERBOSE , " Buddy can't fire mega (shareware) " ) ;
2006-03-20 17:12:09 +00:00
buddy_message ( " CLICK! " ) ;
return 0 ;
}
buddy_message ( " GAHOOGA! " ) ;
2014-10-02 03:02:34 +00:00
const objptridx_t weapon_objnum = Laser_create_new_easy ( buddy_objp - > orient . fvec , buddy_objp - > pos , objp , MEGA_ID , 1 ) ;
2006-03-20 17:12:09 +00:00
2013-12-26 22:21:16 +00:00
if ( weapon_objnum ! = object_none )
2006-03-20 17:12:09 +00:00
bash_buddy_weapon_info ( weapon_objnum ) ;
return 1 ;
}
//-----------------------------------------------------------------------------
2014-10-02 03:02:34 +00:00
static int maybe_buddy_fire_smart ( const vobjptridx_t objp )
2006-03-20 17:12:09 +00:00
{
2014-10-02 03:02:34 +00:00
const vobjptridx_t buddy_objp = vobjptridx ( Buddy_objnum ) ;
2006-03-20 17:12:09 +00:00
fix dist ;
2014-10-01 02:28:41 +00:00
dist = vm_vec_dist_quick ( buddy_objp - > pos , objp - > pos ) ;
2006-03-20 17:12:09 +00:00
if ( dist > F1_0 * 80 )
return 0 ;
if ( ! object_to_object_visibility ( buddy_objp , objp , FQ_TRANSWALL ) )
return 0 ;
buddy_message ( " WHAMMO! " ) ;
2014-10-02 03:02:34 +00:00
const objptridx_t weapon_objnum = Laser_create_new_easy ( buddy_objp - > orient . fvec , buddy_objp - > pos , objp , SMART_ID , 1 ) ;
2006-03-20 17:12:09 +00:00
2013-12-26 22:21:16 +00:00
if ( weapon_objnum ! = object_none )
2006-03-20 17:12:09 +00:00
bash_buddy_weapon_info ( weapon_objnum ) ;
return 1 ;
}
// -----------------------------------------------------------------------------
2013-10-27 22:00:14 +00:00
static void do_buddy_dude_stuff ( void )
2006-03-20 17:12:09 +00:00
{
if ( ! ok_for_buddy_to_talk ( ) )
return ;
2010-12-22 00:17:59 +00:00
if ( Buddy_last_missile_time > GameTime64 )
2006-03-20 17:12:09 +00:00
Buddy_last_missile_time = 0 ;
2010-12-22 00:17:59 +00:00
if ( Buddy_last_missile_time + F1_0 * 2 < GameTime64 ) {
2006-03-20 17:12:09 +00:00
// See if a robot potentially in view cone
2015-02-05 03:03:49 +00:00
range_for ( const auto i , highest_valid ( Objects ) )
2014-11-26 03:39:21 +00:00
{
const auto objp = vobjptridx ( i ) ;
if ( ( objp - > type = = OBJ_ROBOT ) & & ! Robot_info [ get_robot_id ( objp ) ] . companion )
if ( maybe_buddy_fire_mega ( objp ) ) {
2010-12-22 00:17:59 +00:00
Buddy_last_missile_time = GameTime64 ;
2006-03-20 17:12:09 +00:00
return ;
}
2014-11-26 03:39:21 +00:00
}
2006-03-20 17:12:09 +00:00
// See if a robot near enough that buddy should fire smart missile
2015-02-05 03:03:49 +00:00
range_for ( const auto i , highest_valid ( Objects ) )
2014-11-26 03:39:21 +00:00
{
const auto objp = vobjptridx ( i ) ;
if ( ( objp - > type = = OBJ_ROBOT ) & & ! Robot_info [ get_robot_id ( objp ) ] . companion )
if ( maybe_buddy_fire_smart ( objp ) ) {
2010-12-22 00:17:59 +00:00
Buddy_last_missile_time = GameTime64 ;
2006-03-20 17:12:09 +00:00
return ;
}
2014-11-26 03:39:21 +00:00
}
2006-03-20 17:12:09 +00:00
}
}
// -----------------------------------------------------------------------------
// Called every frame (or something).
2014-10-02 03:02:34 +00:00
void do_escort_frame ( const vobjptridx_t objp , fix dist_to_player , int player_visibility )
2006-03-20 17:12:09 +00:00
{
ai_static * aip = & objp - > ctype . ai_info ;
2013-12-25 03:16:41 +00:00
ai_local * ailp = & objp - > ctype . ai_info . ail ;
2006-03-20 17:12:09 +00:00
2013-12-24 04:53:59 +00:00
Buddy_objnum = objp ;
2006-03-20 17:12:09 +00:00
if ( player_visibility ) {
2010-12-22 00:17:59 +00:00
Buddy_last_seen_player = GameTime64 ;
2015-09-26 21:17:13 +00:00
auto & player = get_local_player ( ) ;
if ( player . flags & PLAYER_FLAGS_HEADLIGHT_ON ) // DAMN! MK, stupid bug, fixed 12/08/95, changed PLAYER_FLAGS_HEADLIGHT to PLAYER_FLAGS_HEADLIGHT_ON
if ( f2i ( player . energy ) < 40 )
if ( ( f2i ( player . energy ) / 2 ) & 2 )
2006-03-20 17:12:09 +00:00
if ( ! Player_is_dead )
buddy_message ( " Hey, your headlight's on! " ) ;
}
2011-02-14 21:27:07 +00:00
if ( cheats . buddyangry )
2006-03-20 17:12:09 +00:00
do_buddy_dude_stuff ( ) ;
2010-12-22 00:17:59 +00:00
if ( Buddy_sorry_time + F1_0 > GameTime64 ) {
2006-03-20 17:12:09 +00:00
Last_buddy_message_time = 0 ; // Force this message to get through.
2010-12-22 00:17:59 +00:00
if ( Buddy_sorry_time < GameTime64 + F1_0 * 2 )
2006-03-20 17:12:09 +00:00
buddy_message ( " Oops, sorry 'bout that... " ) ;
Buddy_sorry_time = - F1_0 * 2 ;
}
// If buddy not allowed to talk, then he is locked in his room. Make him mostly do nothing unless you're nearby.
if ( ! Buddy_allowed_to_talk )
if ( dist_to_player > F1_0 * 100 )
aip - > SKIP_AI_COUNT = ( F1_0 / 4 ) / FrameTime ;
2015-04-26 20:15:51 +00:00
// ai_mode::AIM_WANDER has been co-opted for buddy behavior (didn't want to modify aistruct.h)
2006-03-20 17:12:09 +00:00
// It means the object has been told to get lost and has come to the end of its path.
// If the player is now visible, then create a path.
2015-04-26 20:15:51 +00:00
if ( ailp - > mode = = ai_mode : : AIM_WANDER )
2006-03-20 17:12:09 +00:00
if ( player_visibility ) {
2013-12-26 22:21:16 +00:00
create_n_segment_path ( objp , 16 + d_rand ( ) * 16 , segment_none ) ;
2006-03-20 17:12:09 +00:00
aip - > path_length = polish_path ( objp , & Point_segs [ aip - > hide_index ] , aip - > path_length ) ;
}
if ( Escort_special_goal = = ESCORT_GOAL_SCRAM ) {
if ( player_visibility )
2010-12-22 00:17:59 +00:00
if ( Escort_last_path_created + F1_0 * 3 < GameTime64 ) {
2006-03-20 17:12:09 +00:00
create_n_segment_path ( objp , 10 + d_rand ( ) * 16 , ConsoleObject - > segnum ) ;
2010-12-22 00:17:59 +00:00
Escort_last_path_created = GameTime64 ;
2006-03-20 17:12:09 +00:00
}
return ;
}
// Force checking for new goal every 5 seconds, and create new path, if necessary.
2010-12-22 00:17:59 +00:00
if ( ( ( Escort_special_goal ! = ESCORT_GOAL_SCRAM ) & & ( ( Escort_last_path_created + F1_0 * 5 ) < GameTime64 ) ) | |
( ( Escort_special_goal = = ESCORT_GOAL_SCRAM ) & & ( ( Escort_last_path_created + F1_0 * 15 ) < GameTime64 ) ) ) {
2006-03-20 17:12:09 +00:00
Escort_goal_object = ESCORT_GOAL_UNSPECIFIED ;
2010-12-22 00:17:59 +00:00
Escort_last_path_created = GameTime64 ;
2006-03-20 17:12:09 +00:00
}
if ( ( Escort_special_goal ! = ESCORT_GOAL_SCRAM ) & & time_to_visit_player ( objp , ailp , aip ) ) {
int max_len ;
2010-12-22 00:17:59 +00:00
Buddy_last_player_path_created = GameTime64 ;
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_GOTO_PLAYER ;
2006-03-20 17:12:09 +00:00
if ( ! player_visibility ) {
2010-12-22 00:17:59 +00:00
if ( ( Last_come_back_message_time + F1_0 < GameTime64 ) | | ( Last_come_back_message_time > GameTime64 ) ) {
2006-03-20 17:12:09 +00:00
buddy_message ( " Coming back to get you. " ) ;
2010-12-22 00:17:59 +00:00
Last_come_back_message_time = GameTime64 ;
2006-03-20 17:12:09 +00:00
}
}
// No point in Buddy creating very long path if he's not allowed to talk. Really kills framerate.
max_len = Max_escort_length ;
if ( ! Buddy_allowed_to_talk )
max_len = 3 ;
create_path_to_player ( objp , max_len , 1 ) ; // MK!: Last parm used to be 1!
aip - > path_length = polish_path ( objp , & Point_segs [ aip - > hide_index ] , aip - > path_length ) ;
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_GOTO_PLAYER ;
2010-12-22 00:17:59 +00:00
} else if ( GameTime64 - Buddy_last_seen_player > MAX_ESCORT_TIME_AWAY ) {
2006-03-20 17:12:09 +00:00
// This is to prevent buddy from looking for a goal, which he will do because we only allow path creation once/second.
return ;
2015-04-26 20:15:51 +00:00
} else if ( ( ailp - > mode = = ai_mode : : AIM_GOTO_PLAYER ) & & ( dist_to_player < MIN_ESCORT_DISTANCE ) ) {
2006-03-20 17:12:09 +00:00
Escort_goal_object = escort_set_goal_object ( ) ;
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_GOTO_OBJECT ; // May look stupid to be before path creation, but ai_door_is_openable uses mode to determine what doors can be got through
2006-03-20 17:12:09 +00:00
escort_create_path_to_goal ( objp ) ;
aip - > path_length = polish_path ( objp , & Point_segs [ aip - > hide_index ] , aip - > path_length ) ;
if ( aip - > path_length < 3 ) {
create_n_segment_path ( objp , 5 , Believed_player_seg ) ;
}
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_GOTO_OBJECT ;
2006-03-20 17:12:09 +00:00
} else if ( Escort_goal_object = = ESCORT_GOAL_UNSPECIFIED ) {
2015-04-26 20:15:51 +00:00
if ( ( ailp - > mode ! = ai_mode : : AIM_GOTO_PLAYER ) | | ( dist_to_player < MIN_ESCORT_DISTANCE ) ) {
2006-03-20 17:12:09 +00:00
Escort_goal_object = escort_set_goal_object ( ) ;
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_GOTO_OBJECT ; // May look stupid to be before path creation, but ai_door_is_openable uses mode to determine what doors can be got through
2006-03-20 17:12:09 +00:00
escort_create_path_to_goal ( objp ) ;
aip - > path_length = polish_path ( objp , & Point_segs [ aip - > hide_index ] , aip - > path_length ) ;
if ( aip - > path_length < 3 ) {
create_n_segment_path ( objp , 5 , Believed_player_seg ) ;
}
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_GOTO_OBJECT ;
2006-03-20 17:12:09 +00:00
}
2008-04-06 20:23:28 +00:00
}
2006-03-20 17:12:09 +00:00
}
void invalidate_escort_goal ( void )
{
2015-06-13 22:42:15 +00:00
Escort_goal_object = ESCORT_GOAL_UNSPECIFIED ;
2006-03-20 17:12:09 +00:00
}
// -------------------------------------------------------------------------------------------------
2014-10-02 03:02:34 +00:00
void do_snipe_frame ( const vobjptridx_t objp , fix dist_to_player , int player_visibility , const vms_vector & vec_to_player )
2006-03-20 17:12:09 +00:00
{
2013-12-25 03:16:41 +00:00
ai_local * ailp = & objp - > ctype . ai_info . ail ;
2006-03-20 17:12:09 +00:00
fix connected_distance ;
if ( dist_to_player > F1_0 * 500 )
return ;
switch ( ailp - > mode ) {
2015-04-26 20:15:51 +00:00
case ai_mode : : AIM_SNIPE_WAIT :
2006-03-20 17:12:09 +00:00
if ( ( dist_to_player > F1_0 * 50 ) & & ( ailp - > next_action_time > 0 ) )
return ;
ailp - > next_action_time = SNIPE_WAIT_TIME ;
2014-10-22 02:46:03 +00:00
connected_distance = find_connected_distance ( objp - > pos , objp - > segnum , Believed_player_pos , Believed_player_seg , 30 , WID_FLY_FLAG ) ;
2006-03-20 17:12:09 +00:00
if ( connected_distance < F1_0 * 500 ) {
create_path_to_player ( objp , 30 , 1 ) ;
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_SNIPE_ATTACK ;
2006-03-20 17:12:09 +00:00
ailp - > next_action_time = SNIPE_ATTACK_TIME ; // have up to 10 seconds to find player.
}
break ;
2015-04-26 20:15:51 +00:00
case ai_mode : : AIM_SNIPE_RETREAT :
case ai_mode : : AIM_SNIPE_RETREAT_BACKWARDS :
2006-03-20 17:12:09 +00:00
if ( ailp - > next_action_time < 0 ) {
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_SNIPE_WAIT ;
2006-03-20 17:12:09 +00:00
ailp - > next_action_time = SNIPE_WAIT_TIME ;
} else if ( ( player_visibility = = 0 ) | | ( ailp - > next_action_time > SNIPE_ABORT_RETREAT_TIME ) ) {
2014-10-28 01:45:53 +00:00
ai_follow_path ( objp , player_visibility , & vec_to_player ) ;
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_SNIPE_RETREAT_BACKWARDS ;
2006-03-20 17:12:09 +00:00
} else {
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_SNIPE_FIRE ;
2006-03-20 17:12:09 +00:00
ailp - > next_action_time = SNIPE_FIRE_TIME / 2 ;
}
break ;
2015-04-26 20:15:51 +00:00
case ai_mode : : AIM_SNIPE_ATTACK :
2006-03-20 17:12:09 +00:00
if ( ailp - > next_action_time < 0 ) {
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_SNIPE_RETREAT ;
2006-03-20 17:12:09 +00:00
ailp - > next_action_time = SNIPE_WAIT_TIME ;
} else {
2014-10-28 01:45:53 +00:00
ai_follow_path ( objp , player_visibility , & vec_to_player ) ;
2006-03-20 17:12:09 +00:00
if ( player_visibility ) {
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_SNIPE_FIRE ;
2006-03-20 17:12:09 +00:00
ailp - > next_action_time = SNIPE_FIRE_TIME ;
} else
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_SNIPE_ATTACK ;
2006-03-20 17:12:09 +00:00
}
break ;
2015-04-26 20:15:51 +00:00
case ai_mode : : AIM_SNIPE_FIRE :
2006-03-20 17:12:09 +00:00
if ( ailp - > next_action_time < 0 ) {
ai_static * aip = & objp - > ctype . ai_info ;
create_n_segment_path ( objp , 10 + d_rand ( ) / 2048 , ConsoleObject - > segnum ) ;
aip - > path_length = polish_path ( objp , & Point_segs [ aip - > hide_index ] , aip - > path_length ) ;
if ( d_rand ( ) < 8192 )
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_SNIPE_RETREAT_BACKWARDS ;
2006-03-20 17:12:09 +00:00
else
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_SNIPE_RETREAT ;
2006-03-20 17:12:09 +00:00
ailp - > next_action_time = SNIPE_RETREAT_TIME ;
} else {
}
break ;
default :
Int3 ( ) ; // Oops, illegal mode for snipe behavior.
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_SNIPE_ATTACK ;
2006-03-20 17:12:09 +00:00
ailp - > next_action_time = F1_0 ;
break ;
}
}
# define THIEF_DEPTH 20
// ------------------------------------------------------------------------------------------------------
// Choose segment to recreate thief in.
2013-12-29 04:28:07 +00:00
static segnum_t choose_thief_recreation_segment ( )
2006-03-20 17:12:09 +00:00
{
2013-12-29 04:28:07 +00:00
segnum_t segnum = segment_none ;
2006-03-20 17:12:09 +00:00
int cur_drop_depth ;
cur_drop_depth = THIEF_DEPTH ;
2013-12-26 22:21:16 +00:00
while ( ( segnum = = segment_none ) & & ( cur_drop_depth > THIEF_DEPTH / 2 ) ) {
2015-07-25 23:10:45 +00:00
segnum = pick_connected_segment ( & get_local_plrobj ( ) , cur_drop_depth ) ;
2015-09-29 02:41:22 +00:00
if ( segnum ! = segment_none & & vcsegptr ( segnum ) - > special = = SEGMENT_IS_CONTROLCEN )
2013-12-26 22:21:16 +00:00
segnum = segment_none ;
2006-03-20 17:12:09 +00:00
cur_drop_depth - - ;
}
2013-12-26 22:21:16 +00:00
if ( segnum = = segment_none ) {
2006-03-20 17:12:09 +00:00
return ( d_rand ( ) * Highest_segment_index ) > > 15 ;
} else
return segnum ;
}
2013-11-28 00:23:40 +00:00
static fix64 Re_init_thief_time = 0x3f000000 ;
2006-03-20 17:12:09 +00:00
// ----------------------------------------------------------------------
2014-10-02 03:02:34 +00:00
void recreate_thief ( const vobjptr_t objp )
2006-03-20 17:12:09 +00:00
{
2013-12-29 04:28:07 +00:00
segnum_t segnum ;
2006-03-20 17:12:09 +00:00
segnum = choose_thief_recreation_segment ( ) ;
2015-07-12 01:04:22 +00:00
const auto & & segp = vsegptridx ( segnum ) ;
const auto & & center_point = compute_segment_center ( segp ) ;
2006-03-20 17:12:09 +00:00
2015-07-12 01:04:22 +00:00
const auto & & new_obj = create_morph_robot ( segp , center_point , objp - > id ) ;
2014-09-13 22:01:17 +00:00
if ( new_obj = = object_none )
return ;
2015-04-02 02:36:57 +00:00
init_ai_object ( new_obj , ai_behavior : : AIB_SNIPE , segment_none ) ;
2010-12-22 00:17:59 +00:00
Re_init_thief_time = GameTime64 + F1_0 * 10 ; // In 10 seconds, re-initialize thief.
2006-03-20 17:12:09 +00:00
}
// ----------------------------------------------------------------------------
# define THIEF_ATTACK_TIME (F1_0*10)
2013-11-28 00:23:40 +00:00
static const fix Thief_wait_times [ NDL ] = { F1_0 * 30 , F1_0 * 25 , F1_0 * 20 , F1_0 * 15 , F1_0 * 10 } ;
2006-03-20 17:12:09 +00:00
// -------------------------------------------------------------------------------------------------
2014-10-02 03:02:34 +00:00
void do_thief_frame ( const vobjptridx_t objp , fix dist_to_player , int player_visibility , const vms_vector & vec_to_player )
2006-03-20 17:12:09 +00:00
{
2013-12-25 03:16:41 +00:00
ai_local * ailp = & objp - > ctype . ai_info . ail ;
2006-03-20 17:12:09 +00:00
fix connected_distance ;
2010-12-22 00:17:59 +00:00
if ( ( Current_level_num < 0 ) & & ( Re_init_thief_time < GameTime64 ) ) {
if ( Re_init_thief_time > GameTime64 - F1_0 * 2 )
2006-03-20 17:12:09 +00:00
init_thief_for_level ( ) ;
Re_init_thief_time = 0x3f000000 ;
}
if ( ( dist_to_player > F1_0 * 500 ) & & ( ailp - > next_action_time > 0 ) )
return ;
if ( Player_is_dead )
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_THIEF_RETREAT ;
2006-03-20 17:12:09 +00:00
switch ( ailp - > mode ) {
2015-04-26 20:15:51 +00:00
case ai_mode : : AIM_THIEF_WAIT :
2015-04-02 02:36:53 +00:00
if ( ailp - > player_awareness_type > = player_awareness_type_t : : PA_PLAYER_COLLISION ) {
ailp - > player_awareness_type = player_awareness_type_t : : PA_NONE ;
2006-03-20 17:12:09 +00:00
create_path_to_player ( objp , 30 , 1 ) ;
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_THIEF_ATTACK ;
2006-03-20 17:12:09 +00:00
ailp - > next_action_time = THIEF_ATTACK_TIME / 2 ;
return ;
} else if ( player_visibility ) {
create_n_segment_path ( objp , 15 , ConsoleObject - > segnum ) ;
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_THIEF_RETREAT ;
2006-03-20 17:12:09 +00:00
return ;
}
if ( ( dist_to_player > F1_0 * 50 ) & & ( ailp - > next_action_time > 0 ) )
return ;
ailp - > next_action_time = Thief_wait_times [ Difficulty_level ] / 2 ;
2014-10-22 02:46:03 +00:00
connected_distance = find_connected_distance ( objp - > pos , objp - > segnum , Believed_player_pos , Believed_player_seg , 30 , WID_FLY_FLAG ) ;
2006-03-20 17:12:09 +00:00
if ( connected_distance < F1_0 * 500 ) {
create_path_to_player ( objp , 30 , 1 ) ;
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_THIEF_ATTACK ;
2006-03-20 17:12:09 +00:00
ailp - > next_action_time = THIEF_ATTACK_TIME ; // have up to 10 seconds to find player.
}
break ;
2015-04-26 20:15:51 +00:00
case ai_mode : : AIM_THIEF_RETREAT :
2006-03-20 17:12:09 +00:00
if ( ailp - > next_action_time < 0 ) {
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_THIEF_WAIT ;
2006-03-20 17:12:09 +00:00
ailp - > next_action_time = Thief_wait_times [ Difficulty_level ] ;
2015-04-02 02:36:53 +00:00
} else if ( ( dist_to_player < F1_0 * 100 ) | | player_visibility | | ( ailp - > player_awareness_type > = player_awareness_type_t : : PA_PLAYER_COLLISION ) ) {
2014-10-02 03:02:36 +00:00
ai_follow_path ( objp , player_visibility , & vec_to_player ) ;
2015-04-02 02:36:53 +00:00
if ( ( dist_to_player < F1_0 * 100 ) | | ( ailp - > player_awareness_type > = player_awareness_type_t : : PA_PLAYER_COLLISION ) ) {
2006-03-20 17:12:09 +00:00
ai_static * aip = & objp - > ctype . ai_info ;
if ( ( ( aip - > cur_path_index < = 1 ) & & ( aip - > PATH_DIR = = - 1 ) ) | | ( ( aip - > cur_path_index > = aip - > path_length - 1 ) & & ( aip - > PATH_DIR = = 1 ) ) ) {
2015-04-02 02:36:53 +00:00
ailp - > player_awareness_type = player_awareness_type_t : : PA_NONE ;
2006-03-20 17:12:09 +00:00
create_n_segment_path ( objp , 10 , ConsoleObject - > segnum ) ;
// If path is real short, try again, allowing to go through player's segment
if ( aip - > path_length < 4 ) {
2013-12-26 22:21:16 +00:00
create_n_segment_path ( objp , 10 , segment_none ) ;
2013-10-07 23:52:33 +00:00
} else if ( objp - > shields * 4 < Robot_info [ get_robot_id ( objp ) ] . strength ) {
2006-03-20 17:12:09 +00:00
// If robot really low on hits, will run through player with even longer path
if ( aip - > path_length < 8 ) {
2013-12-26 22:21:16 +00:00
create_n_segment_path ( objp , 10 , segment_none ) ;
2006-03-20 17:12:09 +00:00
}
}
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_THIEF_RETREAT ;
2006-03-20 17:12:09 +00:00
}
} else
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_THIEF_RETREAT ;
2006-03-20 17:12:09 +00:00
}
break ;
// This means the thief goes from wherever he is to the player.
// Note: When thief successfully steals something, his action time is forced negative and his mode is changed
// to retreat to get him out of attack mode.
2015-04-26 20:15:51 +00:00
case ai_mode : : AIM_THIEF_ATTACK :
2015-04-02 02:36:53 +00:00
if ( ailp - > player_awareness_type > = player_awareness_type_t : : PA_PLAYER_COLLISION ) {
ailp - > player_awareness_type = player_awareness_type_t : : PA_NONE ;
2006-03-20 17:12:09 +00:00
if ( d_rand ( ) > 8192 ) {
create_n_segment_path ( objp , 10 , ConsoleObject - > segnum ) ;
2013-12-25 03:16:41 +00:00
ai_local * ailp = & objp - > ctype . ai_info . ail ;
ailp - > next_action_time = Thief_wait_times [ Difficulty_level ] / 2 ;
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_THIEF_RETREAT ;
2006-03-20 17:12:09 +00:00
}
} else if ( ailp - > next_action_time < 0 ) {
// This forces him to create a new path every second.
ailp - > next_action_time = F1_0 ;
create_path_to_player ( objp , 100 , 0 ) ;
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_THIEF_ATTACK ;
2006-03-20 17:12:09 +00:00
} else {
if ( player_visibility & & ( dist_to_player < F1_0 * 100 ) ) {
// If the player is close to looking at the thief, thief shall run away.
// No more stupid thief trying to sneak up on you when you're looking right at him!
if ( dist_to_player > F1_0 * 60 ) {
2014-10-02 03:02:36 +00:00
fix dot = vm_vec_dot ( vec_to_player , ConsoleObject - > orient . fvec ) ;
2006-03-20 17:12:09 +00:00
if ( dot < - F1_0 / 2 ) { // Looking at least towards thief, so thief will run!
create_n_segment_path ( objp , 10 , ConsoleObject - > segnum ) ;
2013-12-25 03:16:41 +00:00
ai_local * ailp = & objp - > ctype . ai_info . ail ;
ailp - > next_action_time = Thief_wait_times [ Difficulty_level ] / 2 ;
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_THIEF_RETREAT ;
2006-03-20 17:12:09 +00:00
}
}
ai_turn_towards_vector ( vec_to_player , objp , F1_0 / 4 ) ;
move_towards_player ( objp , vec_to_player ) ;
} else {
ai_static * aip = & objp - > ctype . ai_info ;
// If path length == 0, then he will keep trying to create path, but he is probably stuck in his closet.
2012-05-14 17:06:28 +00:00
if ( ( aip - > path_length > 1 ) | | ( ( d_tick_count & 0x0f ) = = 0 ) ) {
2014-10-02 03:02:36 +00:00
ai_follow_path ( objp , player_visibility , & vec_to_player ) ;
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_THIEF_ATTACK ;
2006-03-20 17:12:09 +00:00
}
}
}
break ;
default :
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_THIEF_ATTACK ;
2006-03-20 17:12:09 +00:00
ailp - > next_action_time = F1_0 ;
break ;
}
}
// ----------------------------------------------------------------------------
// Return true if this item (whose presence is indicated by Players[player_num].flags) gets stolen.
2013-10-27 22:00:14 +00:00
static int maybe_steal_flag_item ( int player_num , int flagval )
2006-03-20 17:12:09 +00:00
{
if ( Players [ player_num ] . flags & flagval ) {
if ( d_rand ( ) < THIEF_PROBABILITY ) {
2015-09-26 21:17:13 +00:00
int powerup_index ;
const char * msg ;
2006-03-20 17:12:09 +00:00
Players [ player_num ] . flags & = ( ~ flagval ) ;
switch ( flagval ) {
case PLAYER_FLAGS_INVULNERABLE :
powerup_index = POW_INVULNERABILITY ;
2015-09-26 21:17:13 +00:00
msg = " Invulnerability stolen! " ;
2006-03-20 17:12:09 +00:00
break ;
case PLAYER_FLAGS_CLOAKED :
powerup_index = POW_CLOAK ;
2015-09-26 21:17:13 +00:00
msg = " Cloak stolen! " ;
2006-03-20 17:12:09 +00:00
break ;
case PLAYER_FLAGS_MAP_ALL :
powerup_index = POW_FULL_MAP ;
2015-09-26 21:17:13 +00:00
msg = " Full map stolen! " ;
2006-03-20 17:12:09 +00:00
break ;
case PLAYER_FLAGS_QUAD_LASERS :
powerup_index = POW_QUAD_FIRE ;
2015-09-26 21:17:13 +00:00
msg = " Quad lasers stolen! " ;
2006-03-20 17:12:09 +00:00
break ;
case PLAYER_FLAGS_AFTERBURNER :
powerup_index = POW_AFTERBURNER ;
2015-09-26 21:17:13 +00:00
msg = " Afterburner stolen! " ;
2006-03-20 17:12:09 +00:00
break ;
case PLAYER_FLAGS_CONVERTER :
powerup_index = POW_CONVERTER ;
2015-09-26 21:17:13 +00:00
msg = " Converter stolen! " ;
2006-03-20 17:12:09 +00:00
break ;
2015-09-26 21:17:13 +00:00
case PLAYER_FLAGS_HEADLIGHT | PLAYER_FLAGS_HEADLIGHT_ON :
2006-03-20 17:12:09 +00:00
powerup_index = POW_HEADLIGHT ;
2015-09-26 21:17:13 +00:00
msg = " Headlight stolen! " ;
2006-03-20 17:12:09 +00:00
break ;
2015-09-26 21:17:13 +00:00
default :
assert ( false ) ;
return 0 ;
2006-03-20 17:12:09 +00:00
}
Stolen_items [ Stolen_item_index ] = powerup_index ;
2015-09-26 21:17:13 +00:00
thief_message_str ( msg ) ;
2006-03-20 17:12:09 +00:00
digi_play_sample_once ( SOUND_WEAPON_STOLEN , F1_0 ) ;
return 1 ;
}
}
return 0 ;
}
// ----------------------------------------------------------------------------
2013-10-27 22:00:14 +00:00
static int maybe_steal_secondary_weapon ( int player_num , int weapon_num )
2006-03-20 17:12:09 +00:00
{
2013-10-26 19:52:41 +00:00
if ( ( Players [ player_num ] . secondary_weapon_flags & HAS_SECONDARY_FLAG ( weapon_num ) ) & & Players [ player_num ] . secondary_ammo [ weapon_num ] )
2006-03-20 17:12:09 +00:00
if ( d_rand ( ) < THIEF_PROBABILITY ) {
2013-09-21 22:01:38 +00:00
if ( weapon_index_is_player_bomb ( weapon_num ) )
{
2006-03-20 17:12:09 +00:00
if ( d_rand ( ) > 8192 ) // Come in groups of 4, only add 1/4 of time.
return 0 ;
2013-09-21 22:01:38 +00:00
}
2006-03-20 17:12:09 +00:00
// Smart mines and proxbombs don't get dropped because they only come in 4 packs.
2013-09-21 22:01:38 +00:00
else
2006-03-20 17:12:09 +00:00
Stolen_items [ Stolen_item_index ] = Secondary_weapon_to_powerup [ weapon_num ] ;
2013-09-21 22:01:38 +00:00
Players [ player_num ] . secondary_ammo [ weapon_num ] - - ;
2006-03-20 17:12:09 +00:00
2012-07-01 01:14:25 +00:00
thief_message ( " %s stolen! " , SECONDARY_WEAPON_NAMES ( weapon_num ) ) ; // Danger! Danger! Use of literal! Danger!
2015-07-25 23:10:46 +00:00
if ( get_local_player ( ) . secondary_ammo [ weapon_num ] = = 0 )
2015-04-26 20:15:51 +00:00
auto_select_secondary_weapon ( ) ;
2006-03-20 17:12:09 +00:00
// -- compress_stolen_items();
digi_play_sample_once ( SOUND_WEAPON_STOLEN , F1_0 ) ;
return 1 ;
}
return 0 ;
}
// ----------------------------------------------------------------------------
2013-10-27 22:00:14 +00:00
static int maybe_steal_primary_weapon ( int player_num , int weapon_num )
2006-03-20 17:12:09 +00:00
{
2013-10-26 19:52:41 +00:00
if ( ( Players [ player_num ] . primary_weapon_flags & HAS_PRIMARY_FLAG ( weapon_num ) ) & & ( ! weapon_index_uses_vulcan_ammo ( weapon_num ) | | Players [ player_num ] . vulcan_ammo ) ) {
2006-03-20 17:12:09 +00:00
if ( d_rand ( ) < THIEF_PROBABILITY ) {
if ( weapon_num = = 0 ) {
if ( Players [ player_num ] . laser_level > 0 ) {
if ( Players [ player_num ] . laser_level > 3 ) {
Stolen_items [ Stolen_item_index ] = POW_SUPER_LASER ;
} else {
Stolen_items [ Stolen_item_index ] = Primary_weapon_to_powerup [ weapon_num ] ;
}
2012-07-01 01:14:25 +00:00
thief_message ( " %s level decreased! " , PRIMARY_WEAPON_NAMES ( weapon_num ) ) ; // Danger! Danger! Use of literal! Danger!
2015-04-26 20:15:56 +00:00
- - Players [ player_num ] . laser_level ;
2006-03-20 17:12:09 +00:00
digi_play_sample_once ( SOUND_WEAPON_STOLEN , F1_0 ) ;
return 1 ;
}
2013-10-26 19:52:41 +00:00
} else if ( Players [ player_num ] . primary_weapon_flags & HAS_PRIMARY_FLAG ( weapon_num ) ) {
Players [ player_num ] . primary_weapon_flags & = ~ HAS_PRIMARY_FLAG ( weapon_num ) ;
2006-03-20 17:12:09 +00:00
Stolen_items [ Stolen_item_index ] = Primary_weapon_to_powerup [ weapon_num ] ;
2012-07-01 01:14:25 +00:00
thief_message ( " %s stolen! " , PRIMARY_WEAPON_NAMES ( weapon_num ) ) ; // Danger! Danger! Use of literal! Danger!
2015-04-26 20:15:51 +00:00
auto_select_primary_weapon ( ) ;
2006-03-20 17:12:09 +00:00
digi_play_sample_once ( SOUND_WEAPON_STOLEN , F1_0 ) ;
return 1 ;
}
}
}
return 0 ;
}
// ----------------------------------------------------------------------------
// Called for a thief-type robot.
// If a item successfully stolen, returns true, else returns false.
// If a wapon successfully stolen, do everything, removing it from player,
// updating Stolen_items information, deselecting, etc.
2014-10-02 03:02:34 +00:00
static int attempt_to_steal_item_3 ( const vobjptr_t objp , int player_num )
2006-03-20 17:12:09 +00:00
{
2013-12-25 03:16:41 +00:00
ai_local * ailp = & objp - > ctype . ai_info . ail ;
2015-04-26 20:15:51 +00:00
if ( ailp - > mode ! = ai_mode : : AIM_THIEF_ATTACK )
2006-03-20 17:12:09 +00:00
return 0 ;
// First, try to steal equipped items.
if ( maybe_steal_flag_item ( player_num , PLAYER_FLAGS_INVULNERABLE ) )
return 1 ;
// If primary weapon = laser, first try to rip away those nasty quad lasers!
2015-04-26 20:15:57 +00:00
if ( Primary_weapon = = primary_weapon_index_t : : LASER_INDEX )
2006-03-20 17:12:09 +00:00
if ( maybe_steal_flag_item ( player_num , PLAYER_FLAGS_QUAD_LASERS ) )
return 1 ;
// Makes it more likely to steal primary than secondary.
2014-09-26 02:42:16 +00:00
for ( int i = 0 ; i < 2 ; i + + )
2006-03-20 17:12:09 +00:00
if ( maybe_steal_primary_weapon ( player_num , Primary_weapon ) )
return 1 ;
if ( maybe_steal_secondary_weapon ( player_num , Secondary_weapon ) )
return 1 ;
// See what the player has and try to snag something.
// Try best things first.
if ( maybe_steal_flag_item ( player_num , PLAYER_FLAGS_INVULNERABLE ) )
return 1 ;
if ( maybe_steal_flag_item ( player_num , PLAYER_FLAGS_CLOAKED ) )
return 1 ;
if ( maybe_steal_flag_item ( player_num , PLAYER_FLAGS_QUAD_LASERS ) )
return 1 ;
if ( maybe_steal_flag_item ( player_num , PLAYER_FLAGS_AFTERBURNER ) )
return 1 ;
if ( maybe_steal_flag_item ( player_num , PLAYER_FLAGS_CONVERTER ) )
return 1 ;
// -- if (maybe_steal_flag_item(player_num, PLAYER_FLAGS_AMMO_RACK)) // Can't steal because what if have too many items, say 15 homing missiles?
// -- return 1;
2015-09-26 21:17:13 +00:00
if ( maybe_steal_flag_item ( player_num , PLAYER_FLAGS_HEADLIGHT | PLAYER_FLAGS_HEADLIGHT_ON ) )
2006-03-20 17:12:09 +00:00
return 1 ;
if ( maybe_steal_flag_item ( player_num , PLAYER_FLAGS_MAP_ALL ) )
return 1 ;
2014-09-26 02:42:16 +00:00
for ( int i = MAX_SECONDARY_WEAPONS - 1 ; i > = 0 ; i - - ) {
2006-03-20 17:12:09 +00:00
if ( maybe_steal_primary_weapon ( player_num , i ) )
return 1 ;
if ( maybe_steal_secondary_weapon ( player_num , i ) )
return 1 ;
}
return 0 ;
}
// ----------------------------------------------------------------------------
2014-10-02 03:02:34 +00:00
static int attempt_to_steal_item_2 ( const vobjptr_t objp , int player_num )
2006-03-20 17:12:09 +00:00
{
int rval ;
rval = attempt_to_steal_item_3 ( objp , player_num ) ;
if ( rval ) {
Stolen_item_index = ( Stolen_item_index + 1 ) % MAX_STOLEN_ITEMS ;
if ( d_rand ( ) > 20000 ) // Occasionally, boost the value again
Stolen_item_index = ( Stolen_item_index + 1 ) % MAX_STOLEN_ITEMS ;
}
return rval ;
}
// ----------------------------------------------------------------------------
// Called for a thief-type robot.
// If a item successfully stolen, returns true, else returns false.
// If a wapon successfully stolen, do everything, removing it from player,
// updating Stolen_items information, deselecting, etc.
2014-10-02 03:02:34 +00:00
int attempt_to_steal_item ( const vobjptridx_t objp , int player_num )
2006-03-20 17:12:09 +00:00
{
int rval = 0 ;
if ( objp - > ctype . ai_info . dying_start_time )
return 0 ;
rval + = attempt_to_steal_item_2 ( objp , player_num ) ;
2014-09-26 02:42:16 +00:00
for ( int i = 0 ; i < 3 ; i + + ) {
2006-03-20 17:12:09 +00:00
if ( ! rval | | ( d_rand ( ) < 11000 ) ) { // about 1/3 of time, steal another item
rval + = attempt_to_steal_item_2 ( objp , player_num ) ;
} else
break ;
}
create_n_segment_path ( objp , 10 , ConsoleObject - > segnum ) ;
2013-12-25 03:16:41 +00:00
ai_local * ailp = & objp - > ctype . ai_info . ail ;
ailp - > next_action_time = Thief_wait_times [ Difficulty_level ] / 2 ;
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_THIEF_RETREAT ;
2006-03-20 17:12:09 +00:00
if ( rval ) {
PALETTE_FLASH_ADD ( 30 , 15 , - 20 ) ;
update_laser_weapon_info ( ) ;
// digi_link_sound_to_pos( SOUND_NASTY_ROBOT_HIT_1, objp->segnum, 0, &objp->pos, 0 , DEFAULT_ROBOT_SOUND_VOLUME);
// I removed this to make the "steal sound" more obvious -AP
if ( Game_mode & GM_NETWORK )
multi_send_stolen_items ( ) ;
}
return rval ;
}
// --------------------------------------------------------------------------------------------------------------
// Indicate no items have been stolen.
void init_thief_for_level ( void )
{
2013-12-12 23:48:34 +00:00
Stolen_items . fill ( 255 ) ;
2006-03-20 17:12:09 +00:00
Assert ( MAX_STOLEN_ITEMS > = 3 * 2 ) ; // Oops! Loop below will overwrite memory!
if ( ! ( Game_mode & GM_MULTI ) )
2014-09-26 02:42:16 +00:00
for ( int i = 0 ; i < 3 ; i + + ) {
2006-03-20 17:12:09 +00:00
Stolen_items [ 2 * i ] = POW_SHIELD_BOOST ;
Stolen_items [ 2 * i + 1 ] = POW_ENERGY ;
}
Stolen_item_index = 0 ;
}
// --------------------------------------------------------------------------------------------------------------
2014-10-02 03:02:34 +00:00
void drop_stolen_items ( const vcobjptr_t objp )
2006-03-20 17:12:09 +00:00
{
2015-01-12 00:26:03 +00:00
range_for ( auto & i , Stolen_items )
{
if ( i ! = 255 )
2013-12-12 23:48:34 +00:00
{
2015-09-26 21:17:13 +00:00
drop_powerup ( OBJ_POWERUP , exchange ( i , 255 ) , 1 , objp - > mtype . phys_info . velocity , objp - > pos , objp - > segnum ) ;
2013-12-12 23:48:34 +00:00
}
2006-03-20 17:12:09 +00:00
}
}
// --------------------------------------------------------------------------------------------------------------
2015-08-12 03:11:46 +00:00
namespace {
2014-09-07 18:06:59 +00:00
struct escort_menu : ignore_window_pointer_t
2006-03-20 17:12:09 +00:00
{
2015-04-19 04:18:49 +00:00
array < char , 300 > msg ;
2013-12-22 22:03:07 +00:00
} ;
2010-01-09 01:56:38 +00:00
2015-08-12 03:11:46 +00:00
}
2015-04-26 20:15:56 +00:00
static window_event_result escort_menu_keycommand ( window * , const d_event & event , escort_menu * )
2010-01-09 01:56:38 +00:00
{
2006-03-20 17:12:09 +00:00
int key ;
2010-01-20 11:04:09 +00:00
2011-01-14 09:51:13 +00:00
key = event_key_get ( event ) ;
2010-01-09 01:56:38 +00:00
switch ( key ) {
case KEY_0 :
case KEY_1 :
case KEY_2 :
case KEY_3 :
case KEY_4 :
case KEY_5 :
case KEY_6 :
case KEY_7 :
case KEY_8 :
case KEY_9 :
Looking_for_marker = - 1 ;
Last_buddy_key = - 1 ;
set_escort_special_goal ( key ) ;
Last_buddy_key = - 1 ;
2014-08-06 02:10:49 +00:00
return window_event_result : : close ;
2010-01-09 01:56:38 +00:00
case KEY_ESC :
case KEY_ENTER :
2014-08-06 02:10:49 +00:00
return window_event_result : : close ;
2010-01-09 01:56:38 +00:00
case KEY_T : {
2015-01-12 00:26:03 +00:00
auto temp = exchange ( Buddy_messages_suppressed , 0 ) ;
buddy_message ( " Messages %s. " , temp ? " enabled " : " suppressed " ) ;
Buddy_messages_suppressed = ~ temp ;
2014-08-06 02:10:49 +00:00
return window_event_result : : close ;
2010-01-09 01:56:38 +00:00
}
default :
break ;
}
2014-08-06 02:10:49 +00:00
return window_event_result : : ignored ;
2010-01-20 11:04:09 +00:00
}
2014-10-04 21:47:13 +00:00
static window_event_result escort_menu_handler ( window * wind , const d_event & event , escort_menu * menu )
2010-01-20 11:04:09 +00:00
{
2014-10-04 21:47:13 +00:00
switch ( event . type )
2010-01-20 11:04:09 +00:00
{
2010-02-25 08:00:15 +00:00
case EVENT_WINDOW_ACTIVATED :
game_flush_inputs ( ) ;
break ;
2010-03-26 14:05:40 +00:00
case EVENT_KEY_COMMAND :
return escort_menu_keycommand ( wind , event , menu ) ;
2010-01-20 11:04:09 +00:00
case EVENT_IDLE :
2010-03-26 14:05:40 +00:00
timer_delay2 ( 50 ) ;
2010-01-20 11:04:09 +00:00
break ;
case EVENT_WINDOW_DRAW :
show_escort_menu ( menu - > msg ) ; //TXT_PAUSE);
break ;
case EVENT_WINDOW_CLOSE :
2015-02-28 22:34:07 +00:00
d_free ( menu ) ;
2014-08-06 02:10:49 +00:00
return window_event_result : : ignored ; // continue closing
2010-01-20 11:04:09 +00:00
default :
2014-08-06 02:10:49 +00:00
return window_event_result : : ignored ;
2010-01-20 11:04:09 +00:00
}
2014-08-06 02:10:49 +00:00
return window_event_result : : handled ;
2010-01-09 01:56:38 +00:00
}
void do_escort_menu ( void )
{
2006-03-20 17:12:09 +00:00
int next_goal ;
2012-12-08 21:18:08 +00:00
char goal_str [ 32 ] ;
2012-12-08 21:21:09 +00:00
const char * goal_txt ;
2012-12-08 21:18:08 +00:00
const char * tstr ;
2010-01-09 01:56:38 +00:00
escort_menu * menu ;
window * wind ;
2006-03-20 17:12:09 +00:00
if ( Game_mode & GM_MULTI ) {
2013-06-23 16:27:34 +00:00
HUD_init_message_literal ( HM_DEFAULT , " No Guide-Bot in Multiplayer! " ) ;
2006-03-20 17:12:09 +00:00
return ;
}
2015-01-12 00:26:04 +00:00
auto buddy = find_escort ( ) ;
if ( buddy = = object_none )
{
HUD_init_message_literal ( HM_DEFAULT , " No Guide-Bot present in mine! " ) ;
return ;
2006-03-20 17:12:09 +00:00
}
ok_for_buddy_to_talk ( ) ; // Needed here or we might not know buddy can talk when he can.
if ( ! Buddy_allowed_to_talk ) {
2015-01-12 00:26:02 +00:00
HUD_init_message ( HM_DEFAULT , " %s has not been released. " , static_cast < const char * > ( PlayerCfg . GuidebotName ) ) ;
2006-03-20 17:12:09 +00:00
return ;
}
2010-01-09 01:56:38 +00:00
MALLOC ( menu , escort_menu , 1 ) ;
if ( ! menu )
return ;
// Just make it the full screen size and let show_escort_menu figure it out
2013-12-01 05:21:47 +00:00
wind = window_create ( & grd_curscreen - > sc_canvas , 0 , 0 , SWIDTH , SHEIGHT , escort_menu_handler , menu ) ;
2010-01-09 01:56:38 +00:00
if ( ! wind )
{
d_free ( menu ) ;
return ;
}
2006-03-20 17:12:09 +00:00
// This prevents the buddy from coming back if you've told him to scram.
// If we don't set next_goal, we get garbage there.
if ( Escort_special_goal = = ESCORT_GOAL_SCRAM ) {
2015-06-13 22:42:15 +00:00
Escort_special_goal = ESCORT_GOAL_UNSPECIFIED ; // Else setting next goal might fail.
2006-03-20 17:12:09 +00:00
next_goal = escort_set_goal_object ( ) ;
Escort_special_goal = ESCORT_GOAL_SCRAM ;
} else {
2015-06-13 22:42:15 +00:00
Escort_special_goal = ESCORT_GOAL_UNSPECIFIED ; // Else setting next goal might fail.
2006-03-20 17:12:09 +00:00
next_goal = escort_set_goal_object ( ) ;
}
switch ( next_goal ) {
2012-12-08 21:21:09 +00:00
default :
2006-03-20 17:12:09 +00:00
case ESCORT_GOAL_UNSPECIFIED :
Int3 ( ) ;
2012-12-08 21:21:09 +00:00
goal_txt = " ERROR " ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_BLUE_KEY :
2012-12-08 21:21:09 +00:00
goal_txt = " blue key " ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_GOLD_KEY :
2012-12-08 21:21:09 +00:00
goal_txt = " yellow key " ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_RED_KEY :
2012-12-08 21:21:09 +00:00
goal_txt = " red key " ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_CONTROLCEN :
2012-12-08 21:21:09 +00:00
goal_txt = " reactor " ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_BOSS :
2012-12-08 21:21:09 +00:00
goal_txt = " boss " ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_EXIT :
2012-12-08 21:21:09 +00:00
goal_txt = " exit " ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_MARKER1 :
case ESCORT_GOAL_MARKER2 :
case ESCORT_GOAL_MARKER3 :
case ESCORT_GOAL_MARKER4 :
case ESCORT_GOAL_MARKER5 :
case ESCORT_GOAL_MARKER6 :
case ESCORT_GOAL_MARKER7 :
case ESCORT_GOAL_MARKER8 :
case ESCORT_GOAL_MARKER9 :
2012-12-08 21:21:09 +00:00
goal_txt = goal_str ;
snprintf ( goal_str , sizeof ( goal_str ) , " marker %i " , next_goal - ESCORT_GOAL_MARKER1 + 1 ) ;
2006-03-20 17:12:09 +00:00
break ;
}
if ( ! Buddy_messages_suppressed )
2012-12-08 21:18:08 +00:00
tstr = " Suppress " ;
2006-03-20 17:12:09 +00:00
else
2012-12-08 21:18:08 +00:00
tstr = " Enable " ;
2006-03-20 17:12:09 +00:00
2015-04-19 04:18:49 +00:00
snprintf ( menu - > msg . data ( ) , menu - > msg . size ( ) , " Select Guide-Bot Command: \n \n \n "
2006-03-20 17:12:09 +00:00
" 0. Next Goal: %s " CC_LSPACING_S " 3 \n \n "
" \x84 . Find Energy Powerup " CC_LSPACING_S " 3 \n \n "
" 2. Find Energy Center " CC_LSPACING_S " 3 \n \n "
" 3. Find Shield Powerup " CC_LSPACING_S " 3 \n \n "
" 4. Find Any Powerup " CC_LSPACING_S " 3 \n \n "
" 5. Find a Robot " CC_LSPACING_S " 3 \n \n "
" 6. Find a Hostage " CC_LSPACING_S " 3 \n \n "
" 7. Stay Away From Me " CC_LSPACING_S " 3 \n \n "
" 8. Find My Powerups " CC_LSPACING_S " 3 \n \n "
" 9. Find the exit \n \n "
2008-02-24 14:41:27 +00:00
" T. %s Messages "
2006-03-20 17:12:09 +00:00
// -- "9. Find the exit" CC_LSPACING_S "3\n"
2012-12-08 21:21:09 +00:00
, goal_txt , tstr ) ;
2006-03-20 17:12:09 +00:00
}
// -------------------------------------------------------------------------------
// Show the Buddy menu!
2015-04-19 04:18:49 +00:00
void show_escort_menu ( const array < char , 300 > & amsg )
2006-03-20 17:12:09 +00:00
{
2015-04-19 04:18:49 +00:00
const auto msg = amsg . data ( ) ;
2015-09-29 02:41:22 +00:00
int w , h ;
2006-03-20 17:12:09 +00:00
int x , y ;
2007-03-21 11:32:33 +00:00
gr_set_current_canvas ( NULL ) ;
2006-03-20 17:12:09 +00:00
gr_set_curfont ( GAME_FONT ) ;
2015-09-29 02:41:22 +00:00
gr_get_string_size ( msg , & w , & h , nullptr ) ;
2006-03-20 17:12:09 +00:00
2008-02-24 14:41:27 +00:00
x = ( SWIDTH - w ) / 2 ;
y = ( SHEIGHT - h ) / 2 ;
2006-03-20 17:12:09 +00:00
2008-02-24 14:41:27 +00:00
gr_set_fontcolor ( BM_XRGB ( 0 , 28 , 0 ) , - 1 ) ;
2006-03-20 17:12:09 +00:00
2008-02-24 14:41:27 +00:00
nm_draw_background ( x - BORDERX , y - BORDERY , x + w + BORDERX , y + h + BORDERY ) ;
2007-03-22 11:32:22 +00:00
gr_ustring ( x , y , msg ) ;
2006-03-20 17:12:09 +00:00
reset_cockpit ( ) ;
}