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-21 03:15:12 +00:00
# include "partial_range.h"
2014-07-29 03:21:53 +00:00
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
# include "editor/editor.h"
# endif
2015-12-22 04:18:50 +00:00
namespace dsx {
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 ) ;
2016-07-14 01:59:02 +00:00
static fix64 Last_come_back_message_time ;
static fix64 Buddy_last_missile_time ;
2006-03-20 17:12:09 +00:00
2016-07-16 16:52:04 +00:00
constexpr 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.
2017-02-19 19:33:45 +00:00
static int segment_is_reachable ( const vobjptr_t robot , const vcsegptr_t segp , int sidenum , const player_flags powerup_flags )
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
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 ;
2017-02-19 19:33:45 +00:00
rval = ai_door_is_openable ( robot , powerup_flags , 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
2017-02-19 19:33:45 +00:00
std : : size_t create_bfs_list ( const vobjptr_t robot , const vcsegidx_t start_seg , const player_flags powerup_flags , 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 ) ) {
2014-11-20 03:00:36 +00:00
auto curseg = bfs_list [ tail + + ] ;
2016-01-09 16:38:13 +00:00
const auto & & cursegp = vcsegptr ( 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 ] ) ) {
2017-02-19 19:33:45 +00:00
if ( segment_is_reachable ( robot , cursegp , i , powerup_flags ) ) {
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
{
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 ;
2016-09-04 00:02:50 +00:00
const segment * const segp = vcsegptr ( 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 ) {
2016-02-12 04:02:28 +00:00
auto & w = * vcwallptr ( wall_num ) ;
if ( w . type = = WALL_BLASTABLE & & ! ( w . flags & WALL_BLASTED ) )
2006-03-20 17:12:09 +00:00
return 0 ;
}
// Check one level deeper.
if ( IS_CHILD ( segp - > children [ i ] ) ) {
2016-09-03 17:30:18 +00:00
range_for ( const auto & j , vcsegptr ( segp - > children [ i ] ) - > 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 ) {
2016-02-12 04:02:28 +00:00
auto & w = * vcwallptr ( wall2 ) ;
if ( w . type = = WALL_BLASTABLE & & ! ( w . flags & WALL_BLASTED ) )
2006-03-20 17:12:09 +00:00
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 ;
2016-11-29 18:30:16 +00:00
// If goal is not an object (FUELCEN, EXIT, SCRAM), bail. FUELCEN is handled in detect_escort_goal_fuelcen_accomplished(), EXIT and SCRAM are never accomplished.
if ( Escort_goal_index = = object_none )
2006-03-20 17:12:09 +00:00
return ;
2016-11-29 18:30:16 +00:00
// 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 )
{
2016-11-29 18:30:16 +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 ) {
2015-11-27 03:56:13 +00:00
const auto index_id = get_powerup_id ( index ) ;
escort_goal_t goal_key ;
if ( ( index_id = = POW_KEY_BLUE & & ( goal_key = ESCORT_GOAL_BLUE_KEY , true ) ) | |
( index_id = = POW_KEY_GOLD & & ( goal_key = ESCORT_GOAL_GOLD_KEY , true ) ) | |
( index_id = = POW_KEY_RED & & ( goal_key = ESCORT_GOAL_RED_KEY , true ) )
)
{
if ( Escort_goal_object = = goal_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
{
2016-11-29 18:30:16 +00:00
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.
2017-01-29 18:54:52 +00:00
else if ( Escort_goal_index ! = object_guidebot_cannot_reach )
2015-12-03 03:26:49 +00:00
{
const auto & & egi_objp = vcobjptr ( Escort_goal_index ) ;
if ( index - > type = = egi_objp - > type & & index - > id = = egi_objp - > id )
2016-11-29 18:30:16 +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.
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 )
{
Last_buddy_message_time = GameTime64 ;
2016-07-14 01:59: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
}
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
{
2016-02-12 04:02:28 +00:00
range_for ( const auto & & objp , vcobjptr )
2015-06-13 22:42:16 +00:00
{
if ( objp - > type = = OBJ_MARKER )
2015-11-27 03:56:13 +00:00
if ( get_marker_id ( objp ) = = 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
{
2016-02-12 04:02:28 +00:00
range_for ( const auto & & objp , vcobjptr )
2015-06-13 22:42:16 +00:00
{
if ( objp - > type = = OBJ_ROBOT )
2015-11-27 03:56:13 +00:00
{
const auto objp_id = get_robot_id ( objp ) ;
if ( Robot_info [ objp_id ] . boss_flag )
return objp_id ;
}
2015-06-13 22:42:16 +00:00
}
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.
2016-10-08 03:34:17 +00:00
static objnum_t exists_in_mine_2 ( const vcsegptr_t segp , const int objtype , const int objid , const 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!
2015-11-27 03:56:13 +00:00
if ( ( curobjp - > type = = OBJ_ROBOT ) & & ( Robot_info [ get_robot_id ( curobjp ) ] . companion ) )
2006-03-20 17:12:09 +00:00
;
else if ( objid = = - 1 ) {
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
}
// -----------------------------------------------------------------------------
2016-10-15 00:53:20 +00:00
static segnum_t exists_fuelcen_in_mine ( const vcsegidx_t start_seg , const player_flags powerup_flags )
2006-03-20 17:12:09 +00:00
{
2015-01-28 03:42:52 +00:00
array < segnum_t , MAX_SEGMENTS > bfs_list ;
2017-02-19 19:33:45 +00:00
const auto length = create_bfs_list ( vobjptr ( Buddy_objnum ) , start_seg , powerup_flags , bfs_list ) ;
2015-12-03 03:26:49 +00:00
auto predicate = [ ] ( const segnum_t & s ) { return vcsegptr ( s ) - > special = = SEGMENT_IS_FUELCEN ; } ;
2014-10-21 03:15:12 +00:00
{
2016-02-12 04:02:28 +00:00
const auto & & rb = partial_const_range ( bfs_list , length ) ;
2014-10-21 03:15:12 +00:00
auto i = std : : find_if ( rb . begin ( ) , rb . end ( ) , predicate ) ;
if ( i ! = rb . end ( ) )
return * i ;
}
{
2016-02-06 22:12:54 +00:00
const auto & rh = vcsegptr ;
2015-12-22 04:18:51 +00:00
const auto a = [ ] ( const vcsegptr_t & s ) {
return s - > special = = SEGMENT_IS_FUELCEN ;
} ;
if ( std : : find_if ( rh . begin ( ) , rh . end ( ) , a ) ! = rh . end ( ) )
2014-10-21 03:15:12 +00:00
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)
2016-10-15 00:53:20 +00:00
static objnum_t exists_in_mine ( const vcsegidx_t start_seg , const int objtype , const int objid , const int special , const player_flags powerup_flags )
2012-12-08 23:36:56 +00:00
{
2015-01-28 03:42:52 +00:00
array < segnum_t , MAX_SEGMENTS > bfs_list ;
2017-02-19 19:33:45 +00:00
const auto length = create_bfs_list ( vobjptr ( Buddy_objnum ) , start_seg , powerup_flags , bfs_list ) ;
2012-12-08 23:36:56 +00:00
2016-02-12 04:02:28 +00:00
range_for ( const auto segnum , partial_const_range ( bfs_list , length ) )
2015-01-12 00:26:04 +00:00
{
2016-10-08 03:34:17 +00:00
const auto & & objnum = exists_in_mine_2 ( vcsegptr ( 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.
2016-10-08 03:34:17 +00:00
range_for ( const auto & & segnum , vcsegptr )
2014-10-12 23:10:05 +00:00
{
2016-10-08 03:34:17 +00:00
const auto & & objnum = exists_in_mine_2 ( 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.
2016-10-08 03:34:17 +00:00
static segidx_t find_exit_segment ( )
2006-03-20 17:12:09 +00:00
{
// ---------- Find exit doors ----------
2016-02-12 04:02:28 +00:00
range_for ( const auto & & segp , vcsegptridx )
2015-06-13 22:42:16 +00:00
{
range_for ( const auto j , segp - > children )
2015-01-12 00:26:04 +00:00
if ( j = = segment_exit )
2015-12-22 04:18:51 +00:00
return segp ;
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
{
2015-12-15 04:09:35 +00:00
if ( Player_dead_state ! = player_dead_state : : no )
2006-03-20 17:12:09 +00:00
return ;
2016-07-14 01:59:02 +00:00
const char * str ;
2006-03-20 17:12:09 +00:00
switch ( goal_num ) {
2012-12-08 21:35:30 +00:00
default :
case ESCORT_GOAL_UNSPECIFIED :
2016-07-14 01:59:02 +00:00
return ;
case ESCORT_GOAL_BLUE_KEY :
str = " Finding BLUE KEY " ;
break ;
case ESCORT_GOAL_GOLD_KEY :
str = " Finding YELLOW KEY " ;
break ;
case ESCORT_GOAL_RED_KEY :
str = " Finding RED KEY " ;
break ;
case ESCORT_GOAL_CONTROLCEN :
str = " Finding REACTOR " ;
break ;
case ESCORT_GOAL_EXIT :
str = " Finding EXIT " ;
break ;
case ESCORT_GOAL_ENERGY :
str = " Finding ENERGY " ;
break ;
case ESCORT_GOAL_ENERGYCEN :
str = " Finding ENERGY CENTER " ;
break ;
case ESCORT_GOAL_SHIELD :
str = " Finding a SHIELD " ;
break ;
case ESCORT_GOAL_POWERUP :
str = " Finding a POWERUP " ;
break ;
case ESCORT_GOAL_ROBOT :
str = " Finding a ROBOT " ;
break ;
case ESCORT_GOAL_HOSTAGE :
str = " Finding a HOSTAGE " ;
break ;
case ESCORT_GOAL_SCRAM :
str = " Staying away... " ;
break ;
case ESCORT_GOAL_BOSS :
str = " Finding BOSS robot " ;
break ;
case ESCORT_GOAL_PLAYER_SPEW :
str = " Finding your powerups " ;
2012-12-08 21:35:30 +00:00
break ;
2006-03-20 17:12:09 +00:00
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 ] ) ;
2016-07-14 01:59:02 +00:00
return ;
2006-03-20 17:12:09 +00:00
}
2016-07-14 01:59:02 +00:00
buddy_message_str ( str ) ;
2006-03-20 17:12:09 +00:00
}
2015-06-13 22:42:15 +00:00
static void clear_escort_goals ( )
{
2016-07-14 01:59:02 +00:00
Looking_for_marker = - 1 ;
2015-06-13 22:42:15 +00:00
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 ] ) ;
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 ] ) ;
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 ;
2016-01-09 16:38:14 +00:00
dist_to_player = find_connected_distance ( objp - > pos , vsegptridx ( objp - > segnum ) , Believed_player_pos , vsegptridx ( Believed_player_seg ) , 100 , WID_FLY_FLAG ) ;
2015-06-13 22:42:15 +00:00
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
// -----------------------------------------------------------------------------
2016-12-05 00:26:10 +00:00
static segidx_t escort_get_goal_segment ( const vcobjptr_t objp , int objtype , int objid , const player_flags powerup_flags )
2015-12-03 03:26:49 +00:00
{
2016-10-15 00:53:20 +00:00
const auto egi = exists_in_mine ( objp - > segnum , objtype , objid , - 1 , powerup_flags ) ;
2015-12-03 03:26:49 +00:00
Escort_goal_index = egi ;
2016-10-05 03:55:19 +00:00
if ( egi ! = object_none & & egi ! = object_guidebot_cannot_reach )
2015-12-03 03:26:49 +00:00
return vcobjptr ( egi ) - > segnum ;
return segment_none ;
}
2016-10-28 03:39:41 +00:00
static void escort_create_path_to_goal ( const vobjptridx_t objp , const player_info & player_info )
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 ;
2016-10-15 00:53:20 +00:00
const auto powerup_flags = player_info . powerup_flags ;
2006-03-20 17:12:09 +00:00
if ( Looking_for_marker ! = - 1 ) {
2016-10-15 00:53:20 +00:00
goal_seg = escort_get_goal_segment ( objp , OBJ_MARKER , Escort_goal_object - ESCORT_GOAL_MARKER1 , powerup_flags ) ;
2006-03-20 17:12:09 +00:00
} else {
switch ( Escort_goal_object ) {
case ESCORT_GOAL_BLUE_KEY :
2016-10-15 00:53:20 +00:00
goal_seg = escort_get_goal_segment ( objp , OBJ_POWERUP , POW_KEY_BLUE , powerup_flags ) ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_GOLD_KEY :
2016-10-15 00:53:20 +00:00
goal_seg = escort_get_goal_segment ( objp , OBJ_POWERUP , POW_KEY_GOLD , powerup_flags ) ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_RED_KEY :
2016-10-15 00:53:20 +00:00
goal_seg = escort_get_goal_segment ( objp , OBJ_POWERUP , POW_KEY_RED , powerup_flags ) ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_CONTROLCEN :
2016-10-15 00:53:20 +00:00
goal_seg = escort_get_goal_segment ( objp , OBJ_CNTRLCEN , - 1 , powerup_flags ) ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_EXIT :
goal_seg = find_exit_segment ( ) ;
2016-11-29 18:30:16 +00:00
Escort_goal_index = object_none ;
if ( goal_seg = = segment_none )
escort_goal_does_not_exist ( ESCORT_GOAL_EXIT ) ;
else if ( goal_seg = = segment_exit )
escort_goal_unreachable ( ESCORT_GOAL_EXIT ) ;
else
escort_go_to_goal ( objp , aip , goal_seg ) ;
return ;
2006-03-20 17:12:09 +00:00
case ESCORT_GOAL_ENERGY :
2016-10-15 00:53:20 +00:00
goal_seg = escort_get_goal_segment ( objp , OBJ_POWERUP , POW_ENERGY , powerup_flags ) ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_ENERGYCEN :
2016-10-15 00:53:20 +00:00
goal_seg = exists_fuelcen_in_mine ( objp - > segnum , powerup_flags ) ;
2016-11-29 18:30:16 +00:00
Escort_goal_index = object_none ;
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 :
2016-10-15 00:53:20 +00:00
goal_seg = escort_get_goal_segment ( objp , OBJ_POWERUP , POW_SHIELD_BOOST , powerup_flags ) ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_POWERUP :
2016-10-15 00:53:20 +00:00
goal_seg = escort_get_goal_segment ( objp , OBJ_POWERUP , - 1 , powerup_flags ) ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_ROBOT :
2016-10-15 00:53:20 +00:00
goal_seg = escort_get_goal_segment ( objp , OBJ_ROBOT , - 1 , powerup_flags ) ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_HOSTAGE :
2016-10-15 00:53:20 +00:00
goal_seg = escort_get_goal_segment ( objp , OBJ_HOSTAGE , - 1 , powerup_flags ) ;
2006-03-20 17:12:09 +00:00
break ;
case ESCORT_GOAL_PLAYER_SPEW :
2016-10-15 00:53:20 +00:00
Escort_goal_index = exists_in_mine ( objp - > segnum , - 1 , - 1 , ESCORT_GOAL_PLAYER_SPEW , powerup_flags ) ;
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 :
2016-11-29 18:30:16 +00:00
Escort_goal_index = object_none ;
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 ) ;
2016-10-15 00:53:20 +00:00
goal_seg = escort_get_goal_segment ( objp , OBJ_ROBOT , boss_id , powerup_flags ) ;
2006-03-20 17:12:09 +00:00
break ;
}
default :
Int3 ( ) ; // Oops, Illegal value in Escort_goal_object.
goal_seg = 0 ;
2016-11-29 18:30:16 +00:00
Escort_goal_index = object_none ;
2006-03-20 17:12:09 +00:00
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.
2016-10-02 00:34:41 +00:00
static escort_goal_t escort_set_goal_object ( const player_flags pl_flags )
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 ;
2016-11-21 01:57:08 +00:00
if ( ! ( pl_flags & PLAYER_FLAGS_BLUE_KEY ) & & exists_in_mine ( ConsoleObject - > segnum , OBJ_POWERUP , POW_KEY_BLUE , - 1 , pl_flags ) ! = object_none )
return ESCORT_GOAL_BLUE_KEY ;
else if ( ! ( pl_flags & PLAYER_FLAGS_GOLD_KEY ) & & exists_in_mine ( ConsoleObject - > segnum , OBJ_POWERUP , POW_KEY_GOLD , - 1 , pl_flags ) ! = object_none )
return ESCORT_GOAL_GOLD_KEY ;
else if ( ! ( pl_flags & PLAYER_FLAGS_RED_KEY ) & & exists_in_mine ( ConsoleObject - > segnum , OBJ_POWERUP , POW_KEY_RED , - 1 , pl_flags ) ! = object_none )
return ESCORT_GOAL_RED_KEY ;
else if ( Control_center_destroyed = = 0 )
2016-10-15 00:53:20 +00:00
{
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 ;
}
// -----------------------------------------------------------------------------
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
{
2016-07-14 01:59:02 +00:00
auto & laser_info = objp - > ctype . laser_info ;
const auto console = ConsoleObject ;
laser_info . parent_num = vobjptridx ( console ) ;
laser_info . parent_type = OBJ_PLAYER ;
laser_info . parent_signature = console - > signature ;
2006-03-20 17:12:09 +00:00
}
// -----------------------------------------------------------------------------
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
{
2015-12-22 04:18:51 +00:00
const auto & & buddy_objp = objp . absolute_sibling ( 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 ;
2015-12-03 03:26:49 +00:00
if ( Weapon_info [ weapon_id_type : : 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! " ) ;
2015-12-03 03:26:49 +00:00
const objptridx_t weapon_objnum = Laser_create_new_easy ( buddy_objp - > orient . fvec , buddy_objp - > pos , objp , weapon_id_type : : 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
{
2015-12-22 04:18:51 +00:00
const auto & & buddy_objp = objp . absolute_sibling ( 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! " ) ;
2015-12-03 03:26:49 +00:00
const objptridx_t weapon_objnum = Laser_create_new_easy ( buddy_objp - > orient . fvec , buddy_objp - > pos , objp , weapon_id_type : : 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
2016-02-06 22:12:54 +00:00
const auto & rh = vobjptridx ;
2015-12-22 04:18:51 +00:00
range_for ( const auto & & objp , rh )
2014-11-26 03:39:21 +00:00
{
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-12-22 04:18:51 +00:00
range_for ( const auto & & objp , rh )
2014-11-26 03:39:21 +00:00
{
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).
2016-10-02 00:34:41 +00:00
void do_escort_frame ( const vobjptridx_t objp , const object & plrobj , 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
2016-10-28 03:39:41 +00:00
auto & player_info = plrobj . ctype . player_info ;
2006-03-20 17:12:09 +00:00
if ( player_visibility ) {
2010-12-22 00:17:59 +00:00
Buddy_last_seen_player = GameTime64 ;
2016-10-28 03:39:41 +00:00
if ( player_info . powerup_flags & PLAYER_FLAGS_HEADLIGHT_ON ) // DAMN! MK, stupid bug, fixed 12/08/95, changed PLAYER_FLAGS_HEADLIGHT to PLAYER_FLAGS_HEADLIGHT_ON
2015-11-07 21:55:58 +00:00
{
2016-10-28 03:39:41 +00:00
const auto energy = player_info . energy ;
2015-11-07 21:55:58 +00:00
const auto ienergy = f2i ( energy ) ;
if ( ienergy < 40 )
if ( ienergy & 4 )
2006-03-20 17:12:09 +00:00
buddy_message ( " Hey, your headlight's on! " ) ;
2015-11-07 21:55:58 +00:00
}
2006-03-20 17:12:09 +00:00
}
2011-02-14 21:27:07 +00:00
if ( cheats . buddyangry )
2006-03-20 17:12:09 +00:00
do_buddy_dude_stuff ( ) ;
2016-07-14 01:59:02 +00:00
{
const auto buddy_sorry_time = Buddy_sorry_time ;
if ( buddy_sorry_time + F1_0 > GameTime64 )
{
Buddy_sorry_time = - F1_0 * 2 ;
if ( buddy_sorry_time < GameTime64 + F1_0 * 2 )
{
2006-03-20 17:12:09 +00:00
Last_buddy_message_time = 0 ; // Force this message to get through.
buddy_message ( " Oops, sorry 'bout that... " ) ;
2016-07-14 01:59:02 +00:00
}
}
2006-03-20 17:12:09 +00:00
}
// 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 ) ) {
2016-10-28 03:39:41 +00:00
Escort_goal_object = escort_set_goal_object ( player_info . powerup_flags ) ;
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
2016-10-28 03:39:41 +00:00
escort_create_path_to_goal ( objp , player_info ) ;
2006-03-20 17:12:09 +00:00
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 ) ) {
2016-10-28 03:39:41 +00:00
Escort_goal_object = escort_set_goal_object ( player_info . powerup_flags ) ;
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
2016-10-28 03:39:41 +00:00
escort_create_path_to_goal ( objp , player_info ) ;
2006-03-20 17:12:09 +00:00
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 ;
2016-01-09 16:38:14 +00:00
connected_distance = find_connected_distance ( objp - > pos , vsegptridx ( objp - > segnum ) , Believed_player_pos , vsegptridx ( 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.
2016-12-05 00:26:10 +00:00
static vsegidx_t choose_thief_recreation_segment ( const vcsegidx_t plrseg )
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 ) ) {
2016-10-02 00:34:41 +00:00
segnum = pick_connected_segment ( plrseg , 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
// ----------------------------------------------------------------------
2016-04-06 03:34:15 +00:00
void recreate_thief ( const uint8_t thief_id )
2006-03-20 17:12:09 +00:00
{
2016-10-02 00:34:41 +00:00
const auto segnum = choose_thief_recreation_segment ( ConsoleObject - > segnum ) ;
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
2016-04-06 03:34:15 +00:00
const auto & & new_obj = create_morph_robot ( segp , center_point , thief_id ) ;
2014-09-13 22:01:17 +00:00
if ( new_obj = = object_none )
return ;
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)
2016-07-16 16:52:04 +00:00
constexpr array < fix , NDL > Thief_wait_times = { {
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 ;
2015-12-15 04:09:35 +00:00
if ( Player_dead_state ! = player_dead_state : : no )
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 ;
2016-01-09 16:38:14 +00:00
connected_distance = find_connected_distance ( objp - > pos , vsegptridx ( objp - > segnum ) , Believed_player_pos , vsegptridx ( 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
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
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.
2015-10-30 02:52:55 +00:00
static int maybe_steal_flag_item ( const vobjptr_t playerobjp , const PLAYER_FLAG flagval )
2006-03-20 17:12:09 +00:00
{
2015-11-07 21:55:59 +00:00
auto & plr_flags = playerobjp - > ctype . player_info . powerup_flags ;
2015-10-10 03:44:14 +00:00
if ( plr_flags & flagval )
{
2006-03-20 17:12:09 +00:00
if ( d_rand ( ) < THIEF_PROBABILITY ) {
2015-09-26 21:17:13 +00:00
int powerup_index ;
const char * msg ;
2015-10-10 03:44:14 +00:00
plr_flags & = ( ~ flagval ) ;
2006-03-20 17:12:09 +00:00
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-10-30 02:52:55 +00:00
case PLAYER_FLAG : : HEADLIGHT_PRESENT_AND_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 ;
}
// ----------------------------------------------------------------------------
2015-10-10 03:44:14 +00:00
static int maybe_steal_secondary_weapon ( const vobjptr_t playerobjp , int weapon_num )
2006-03-20 17:12:09 +00:00
{
2016-10-02 00:34:43 +00:00
auto & player_info = playerobjp - > ctype . player_info ;
if ( auto & secondary_ammo = player_info . 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 ] ;
2012-07-01 01:14:25 +00:00
thief_message ( " %s stolen! " , SECONDARY_WEAPON_NAMES ( weapon_num ) ) ; // Danger! Danger! Use of literal! Danger!
2015-10-10 03:44:14 +00:00
if ( - - secondary_ammo = = 0 )
2016-10-02 00:34:43 +00:00
auto_select_secondary_weapon ( player_info ) ;
2006-03-20 17:12:09 +00:00
// -- compress_stolen_items();
digi_play_sample_once ( SOUND_WEAPON_STOLEN , F1_0 ) ;
return 1 ;
}
return 0 ;
}
// ----------------------------------------------------------------------------
2015-10-10 03:44:14 +00:00
static int maybe_steal_primary_weapon ( const vobjptr_t playerobjp , int weapon_num )
2006-03-20 17:12:09 +00:00
{
2015-11-07 21:55:59 +00:00
auto & player_info = playerobjp - > ctype . player_info ;
if ( ! ( player_info . primary_weapon_flags & HAS_PRIMARY_FLAG ( weapon_num ) ) )
2015-10-10 03:44:14 +00:00
return 0 ;
2015-11-07 21:55:59 +00:00
if ( ! weapon_index_uses_vulcan_ammo ( weapon_num ) | | player_info . vulcan_ammo )
2015-10-10 03:44:14 +00:00
{
2006-03-20 17:12:09 +00:00
if ( d_rand ( ) < THIEF_PROBABILITY ) {
2015-10-18 21:01:18 +00:00
if ( weapon_num = = primary_weapon_index_t : : LASER_INDEX )
2015-10-10 03:44:14 +00:00
{
2015-11-07 21:55:59 +00:00
if ( auto & laser_level = player_info . laser_level )
2015-10-10 03:44:14 +00:00
{
2016-07-14 01:59:02 +00:00
if ( laser_level > MAX_LASER_LEVEL )
2015-10-10 03:44:14 +00:00
{
2006-03-20 17:12:09 +00:00
Stolen_items [ Stolen_item_index ] = POW_SUPER_LASER ;
} else {
Stolen_items [ Stolen_item_index ] = Primary_weapon_to_powerup [ weapon_num ] ;
}
2015-10-10 03:44:14 +00:00
- - laser_level ;
2016-07-14 01:59:02 +00:00
thief_message ( " %s level decreased! " , PRIMARY_WEAPON_NAMES ( weapon_num ) ) ; // Danger! Danger! Use of literal! Danger!
2006-03-20 17:12:09 +00:00
digi_play_sample_once ( SOUND_WEAPON_STOLEN , F1_0 ) ;
return 1 ;
}
2015-10-10 03:44:14 +00:00
}
else
{
2015-11-07 21:55:59 +00:00
player_info . 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!
2016-10-02 00:34:43 +00:00
auto_select_primary_weapon ( player_info ) ;
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.
2015-10-10 03:44:14 +00:00
static int attempt_to_steal_item_3 ( const vobjptr_t objp , const vobjptr_t 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.
2017-02-26 00:00:01 +00:00
if ( auto r = maybe_steal_flag_item ( player_num , PLAYER_FLAGS_INVULNERABLE ) )
return r ;
2006-03-20 17:12:09 +00:00
// If primary weapon = laser, first try to rip away those nasty quad lasers!
2016-08-28 22:41:49 +00:00
const auto Primary_weapon = player_num - > ctype . player_info . Primary_weapon ;
2015-04-26 20:15:57 +00:00
if ( Primary_weapon = = primary_weapon_index_t : : LASER_INDEX )
2017-02-26 00:00:01 +00:00
if ( auto r = maybe_steal_flag_item ( player_num , PLAYER_FLAGS_QUAD_LASERS ) )
return r ;
2006-03-20 17:12:09 +00:00
// Makes it more likely to steal primary than secondary.
2014-09-26 02:42:16 +00:00
for ( int i = 0 ; i < 2 ; i + + )
2017-02-26 00:00:02 +00:00
if ( auto r = maybe_steal_primary_weapon ( player_num , Primary_weapon ) )
return r ;
2006-03-20 17:12:09 +00:00
2017-02-26 00:00:01 +00:00
if ( auto r = maybe_steal_secondary_weapon ( player_num , player_num - > ctype . player_info . Secondary_weapon ) )
return r ;
2006-03-20 17:12:09 +00:00
// See what the player has and try to snag something.
// Try best things first.
2017-02-26 00:00:01 +00:00
if ( auto r = maybe_steal_flag_item ( player_num , PLAYER_FLAGS_INVULNERABLE ) )
return r ;
if ( auto r = maybe_steal_flag_item ( player_num , PLAYER_FLAGS_CLOAKED ) )
return r ;
if ( auto r = maybe_steal_flag_item ( player_num , PLAYER_FLAGS_QUAD_LASERS ) )
return r ;
if ( auto r = maybe_steal_flag_item ( player_num , PLAYER_FLAGS_AFTERBURNER ) )
return r ;
if ( auto r = maybe_steal_flag_item ( player_num , PLAYER_FLAGS_CONVERTER ) )
return r ;
2006-03-20 17:12:09 +00:00
// -- 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;
2017-02-26 00:00:01 +00:00
if ( auto r = maybe_steal_flag_item ( player_num , PLAYER_FLAG : : HEADLIGHT_PRESENT_AND_ON ) )
return r ;
if ( auto r = maybe_steal_flag_item ( player_num , PLAYER_FLAGS_MAP_ALL ) )
return r ;
2006-03-20 17:12:09 +00:00
2014-09-26 02:42:16 +00:00
for ( int i = MAX_SECONDARY_WEAPONS - 1 ; i > = 0 ; i - - ) {
2017-02-26 00:00:02 +00:00
if ( auto r = maybe_steal_primary_weapon ( player_num , i ) )
return r ;
2017-02-26 00:00:01 +00:00
if ( auto r = maybe_steal_secondary_weapon ( player_num , i ) )
return r ;
2006-03-20 17:12:09 +00:00
}
return 0 ;
}
// ----------------------------------------------------------------------------
2015-10-10 03:44:14 +00:00
static int attempt_to_steal_item_2 ( const vobjptr_t objp , const vobjptr_t player_num )
2006-03-20 17:12:09 +00:00
{
int rval ;
rval = attempt_to_steal_item_3 ( objp , player_num ) ;
if ( rval ) {
2016-07-14 01:59:02 +00:00
auto i = Stolen_item_index ;
2006-03-20 17:12:09 +00:00
if ( d_rand ( ) > 20000 ) // Occasionally, boost the value again
2016-07-14 01:59:02 +00:00
+ + i ;
constexpr auto size = Stolen_items . size ( ) ;
if ( + + i > = size )
i - = size ;
Stolen_item_index = i ;
2006-03-20 17:12:09 +00:00
}
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.
2015-10-10 03:44:14 +00:00
int attempt_to_steal_item ( const vobjptridx_t objp , const vobjptr_t 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
2016-07-10 04:11:34 +00:00
constexpr unsigned iterations = 3 ;
static_assert ( Stolen_items . size ( ) > = iterations * 2 , " Stolen_items too small " ) ; // Oops! Loop below will overwrite memory!
2006-03-20 17:12:09 +00:00
if ( ! ( Game_mode & GM_MULTI ) )
2016-07-10 04:11:34 +00:00
for ( unsigned i = 0 ; i < iterations ; 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
{
2016-02-06 22:12:55 +00:00
const auto & & segp = vsegptridx ( objp - > segnum ) ;
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
{
2016-02-06 22:12:55 +00:00
drop_powerup ( OBJ_POWERUP , exchange ( i , 255 ) , 1 , objp - > mtype . phys_info . velocity , objp - > pos , segp , true ) ;
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 ;
2016-10-29 23:16:15 +00:00
static window_event_result event_handler ( window * wind , const d_event & event , escort_menu * menu ) ;
static window_event_result event_key_command ( const d_event & event ) ;
2013-12-22 22:03:07 +00:00
} ;
2010-01-09 01:56:38 +00:00
2015-08-12 03:11:46 +00:00
}
2016-10-29 23:16:15 +00:00
window_event_result escort_menu : : event_key_command ( const d_event & event )
2010-01-09 01:56:38 +00:00
{
2016-10-29 23:16:15 +00:00
switch ( const auto key = event_key_get ( event ) )
{
2010-01-09 01:56:38 +00:00
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
}
2016-10-29 23:16:15 +00:00
window_event_result escort_menu : : event_handler ( window * , 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 :
2016-10-29 23:16:15 +00:00
return event_key_command ( event ) ;
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 ;
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
2016-10-29 23:16:15 +00:00
const auto wind = window_create ( grd_curscreen - > sc_canvas , 0 , 0 , SWIDTH , SHEIGHT , & escort_menu : : event_handler , menu ) ;
2010-01-09 01:56:38 +00:00
if ( ! wind )
{
d_free ( menu ) ;
return ;
}
2016-10-02 00:34:41 +00:00
auto & plrobj = get_local_plrobj ( ) ;
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.
2016-10-02 00:34:41 +00:00
next_goal = escort_set_goal_object ( plrobj . ctype . player_info . powerup_flags ) ;
2006-03-20 17:12:09 +00:00
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.
2016-10-02 00:34:41 +00:00
next_goal = escort_set_goal_object ( plrobj . ctype . player_info . powerup_flags ) ;
2006-03-20 17:12:09 +00:00
}
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
2017-02-11 21:42:37 +00:00
auto & canvas = * grd_curcanv ;
2017-02-11 21:42:38 +00:00
gr_set_curfont ( canvas , GAME_FONT ) ;
2017-02-11 21:42:37 +00:00
gr_get_string_size ( * canvas . cv_font , 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
2017-02-11 21:42:37 +00:00
gr_set_fontcolor ( canvas , 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
2017-02-11 21:42:37 +00:00
gr_ustring ( canvas , x , y , msg ) ;
2006-03-20 17:12:09 +00:00
reset_cockpit ( ) ;
}
2015-12-22 04:18:50 +00:00
}