1 /*
2  * Portions of this file are copyright Rebirth contributors and licensed as
3  * described in COPYING.txt.
4  * Portions of this file are copyright Parallax Software and licensed
5  * according to the Parallax license below.
6  * See COPYING.txt for license details.
7 
8 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
9 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
10 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
11 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
12 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
13 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
14 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
15 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
16 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
17 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
18 */
19 
20 /*
21  *
22  * Structs and constants for AI system.
23  * object.h depends on this.
24  * ai.h depends on object.h.
25  * Get it?
26  *
27  */
28 
29 #pragma once
30 
31 #include <physfs.h>
32 
33 #ifdef __cplusplus
34 #include "polyobj.h"
35 #include "pack.h"
36 #include "objnum.h"
37 #include "fwd-segment.h"
38 
39 #define GREEN_GUY   1
40 
41 #define MAX_SEGMENTS_PER_PATH       20
42 
43 namespace dcx {
44 
45 enum class player_awareness_type_t : uint8_t
46 {
47 	PA_NONE,
48 	PA_NEARBY_ROBOT_FIRED		= 1,  // Level of robot awareness after nearby robot fires a weapon
49 	PA_WEAPON_WALL_COLLISION	= 2,  // Level of robot awareness after player weapon hits nearby wall
50 //	PA_PLAYER_VISIBLE			= 2,  // Level of robot awareness if robot is looking towards player, and player not hidden
51 	PA_PLAYER_COLLISION			= 3,  // Level of robot awareness after player bumps into robot
52 	PA_WEAPON_ROBOT_COLLISION	= 4,  // Level of robot awareness after player weapon hits nearby robot
53 };
54 
55 enum class player_visibility_state : int8_t
56 {
57 	no_line_of_sight,
58 	visible_not_in_field_of_view,
59 	visible_and_in_field_of_view,
60 };
61 
player_is_visible(const player_visibility_state s)62 static inline unsigned player_is_visible(const player_visibility_state s)
63 {
64 	return static_cast<unsigned>(s) > 0;
65 }
66 
67 }
68 
69 // Constants indicating currently moving forward or backward through
70 // path.  Note that you can add aip->direction to aip_path_index to
71 // get next segment on path.
72 #define AI_DIR_FORWARD  1
73 #define AI_DIR_BACKWARD (-AI_DIR_FORWARD)
74 
75 #ifdef dsx
76 namespace dsx {
77 
78 enum class ai_behavior : uint8_t
79 {
80 // Behaviors
81 	AIB_STILL = 0x80,
82 	AIB_NORMAL = 0x81,
83 	AIB_RUN_FROM = 0x83,
84 	AIB_STATION = 0x85,
85 #if defined(DXX_BUILD_DESCENT_I)
86 	AIB_HIDE = 0x82,
87 	AIB_FOLLOW_PATH = 0x84,
88 #elif defined(DXX_BUILD_DESCENT_II)
89 	AIB_BEHIND = 0x82,
90 	AIB_SNIPE = 0x84,
91 	AIB_FOLLOW = 0x86,
92 #endif
93 };
94 
95 #define MIN_BEHAVIOR    0x80
96 #if defined(DXX_BUILD_DESCENT_I)
97 #define	MAX_BEHAVIOR	0x85
98 #elif defined(DXX_BUILD_DESCENT_II)
99 #define MAX_BEHAVIOR    0x86
100 #endif
101 
102 enum class ai_mode : uint8_t
103 {
104 //  Modes
105 	AIM_STILL = 0,
106 	AIM_WANDER = 1,
107 	AIM_FOLLOW_PATH = 2,
108 	AIM_CHASE_OBJECT = 3,
109 	AIM_RUN_FROM_OBJECT = 4,
110 	AIM_FOLLOW_PATH_2 = 6,
111 	AIM_OPEN_DOOR = 7,
112 #if defined(DXX_BUILD_DESCENT_I)
113 	AIM_HIDE = 5,
114 #elif defined(DXX_BUILD_DESCENT_II)
115 	AIM_BEHIND = 5,
116 	AIM_GOTO_PLAYER = 8,   //  Only for escort behavior
117 	AIM_GOTO_OBJECT = 9,   //  Only for escort behavior
118 
119 	AIM_SNIPE_ATTACK = 10,
120 	AIM_SNIPE_FIRE = 11,
121 	AIM_SNIPE_RETREAT = 12,
122 	AIM_SNIPE_RETREAT_BACKWARDS = 13,
123 	AIM_SNIPE_WAIT = 14,
124 
125 	AIM_THIEF_ATTACK = 15,
126 	AIM_THIEF_RETREAT = 16,
127 	AIM_THIEF_WAIT = 17,
128 #endif
129 };
130 
131 }
132 #endif
133 
134 #define AISM_GOHIDE                 0
135 #define AISM_HIDING                 1
136 
137 #define AI_MAX_STATE    7
138 #define AI_MAX_EVENT    4
139 
140 #define AIS_NONE        0
141 #define AIS_REST        1
142 #define AIS_SRCH        2
143 #define AIS_LOCK        3
144 #define AIS_FLIN        4
145 #define AIS_FIRE        5
146 #define AIS_RECO        6
147 #define AIS_ERR_        7
148 
149 #define AIE_FIRE        0
150 #define AIE_HITT        1
151 #define AIE_COLL        2
152 #define AIE_HURT        3
153 
154 //typedef struct opath {
155 //	sbyte   path_index;     // current index of path
156 //	sbyte   path_direction; // current path direction
157 //	sbyte   path_length;    // length of current path
158 //	sbyte   nothing;
159 //	short   path[MAX_SEGMENTS_PER_PATH];
160 //	short   always_0xabc;   // If this is ever not 0xabc, then someone overwrote
161 //} opath;
162 //
163 //typedef struct oai_state {
164 //	short   mode;               //
165 //	short   counter;            // kind of a hack, frame countdown until switch modes
166 //	opath   paths[2];
167 //	vms_vector movement_vector; // movement vector for one second
168 //} oai_state;
169 
170 #if defined(DXX_BUILD_DESCENT_II)
171 #define SUB_FLAGS_GUNSEG        0x01
172 #define SUB_FLAGS_SPROX         0x02    // If set, then this bot drops a super prox, not a prox, when it's time to drop something
173 #define SUB_FLAGS_CAMERA_AWAKE  0x04    // If set, a camera (on a missile) woke this robot up, so don't fire at player.  Can look real stupid!
174 #endif
175 
176 //  Constants defining meaning of flags in ai_state
177 #define MAX_AI_FLAGS    11          // This MUST cause word (4 bytes) alignment in ai_static, allowing for one byte mode
178 
179 #define CURRENT_GUN     flags[0]    // This is the last gun the object fired from
180 #define CURRENT_STATE   flags[1]    // current behavioral state
181 #define GOAL_STATE      flags[2]    // goal state
182 #define PATH_DIR        flags[3]    // direction traveling path, 1 = forward, -1 = backward, other = error!
183 #if defined(DXX_BUILD_DESCENT_I)
184 #define	SUBMODE			flags[4]			//	submode, eg AISM_HIDING if mode == AIM_HIDE
185 #elif defined(DXX_BUILD_DESCENT_II)
186 #define SUB_FLAGS       flags[4]    // bit 0: Set -> Robot's current gun in different segment than robot's center.
187 #endif
188 #define GOALSIDE        flags[5]    // for guys who open doors, this is the side they are going after.
189 #define CLOAKED         flags[6]    // Cloaked now.
190 #define SKIP_AI_COUNT   flags[7]    // Skip AI this frame, but decrement in do_ai_frame.
191 #define  REMOTE_OWNER   flags[8]    // Who is controlling this remote AI object (multiplayer use only)
192 #define  REMOTE_SLOT_NUM flags[9]   // What slot # is this robot in for remote control purposes (multiplayer use only)
193 
194 // This is the stuff that is permanent for an AI object.
195 #ifdef dsx
196 namespace dsx {
197 
198 // Rather temporal AI stuff.
199 struct ai_local : public prohibit_void_ptr<ai_local>
200 {
201 // These used to be bytes, changed to ints so I could set watchpoints on them.
202 	player_awareness_type_t player_awareness_type = player_awareness_type_t::PA_NONE;           // type of awareness of player
203 	uint8_t retry_count = 0;                     // number of retries in physics last time this object got moved.
204 	uint8_t consecutive_retries = 0;             // number of retries in consecutive frames (ie, without a retry_count of 0)
205 	player_visibility_state previous_visibility{};             // Visibility of player last time we checked.
206 	uint8_t rapidfire_count = 0;                 // number of shots fired rapidly
207 	ai_mode mode{};                            // current mode within behavior
208 	segnum_t      goal_segment{};                    // goal segment for current path
209 	fix        next_action_time = 0;              // time in seconds until something happens, mode dependent
210 	fix        next_fire = 0;                     // time in seconds until can fire again
211 #if defined(DXX_BUILD_DESCENT_II)
212 	fix        next_fire2 = 0;                    // time in seconds until can fire again from second weapon
213 #endif
214 	fix        player_awareness_time = 0;         // time in seconds robot will be aware of player, 0 means not aware of player
215 	fix        time_since_processed = 0;          // time since this robot last processed in do_ai_frame
216 	fix64      time_player_seen = 0;              // absolute time in seconds at which player was last seen, might cause to go into follow_path mode
217 	fix64      time_player_sound_attacked = 0;    // absolute time in seconds at which player was last seen with visibility of 2.
218 	fix64      next_misc_sound_time = 0;          // absolute time in seconds at which this robot last made an angry or lurking sound.
219 	std::array<vms_angvec, MAX_SUBMODELS> goal_angles{};    // angles for each subobject
220 	std::array<vms_angvec, MAX_SUBMODELS> delta_angles{};   // angles for each subobject
221 	std::array<sbyte, MAX_SUBMODELS> goal_state{};     // Goal state for this sub-object
222 	std::array<sbyte, MAX_SUBMODELS> achieved_state{}; // Last achieved state
223 };
224 
225 struct ai_static : public prohibit_void_ptr<ai_static>
226 {
227 	ai_behavior behavior = static_cast<ai_behavior>(0);               //
228 	std::array<sbyte, MAX_AI_FLAGS> flags{};    // various flags, meaning defined by constants
229 	segnum_t hide_segment{};           // Segment to go to for hiding.
230 	short hide_index{};             // Index in Path_seg_points
231 	short path_length{};            // Length of hide path.
232 #if defined(DXX_BUILD_DESCENT_I)
233 	short cur_path_index{};         // Current index in path.
234 #elif defined(DXX_BUILD_DESCENT_II)
235 	sbyte cur_path_index{};         // Current index in path.
236 	sbyte dying_sound_playing{};    // !0 if this robot is playing its dying sound.
237 #endif
238 	objnum_t danger_laser_num{};
239 	object_signature_t danger_laser_signature;
240 #if defined(DXX_BUILD_DESCENT_II)
241 	fix64 dying_start_time{};       // Time at which this robot started dying.
242 #endif
243 	ai_local ail;
244 };
245 
246 // Same as above but structure Savegames/Multiplayer objects expect
247 struct ai_static_rw
248 {
249 	ubyte   behavior;               //
250 	sbyte   flags[MAX_AI_FLAGS];    // various flags, meaning defined by constants
251 	short   hide_segment;           // Segment to go to for hiding.
252 	short   hide_index;             // Index in Path_seg_points
253 	short   path_length;            // Length of hide path.
254 #if defined(DXX_BUILD_DESCENT_I)
255 	short   cur_path_index;         // Current index in path.
256 	short   follow_path_start_seg;  // Start segment for robot which follows path.
257 	short   follow_path_end_seg;    // End segment for robot which follows path.
258 	int     danger_laser_signature;
259 	short   danger_laser_num;
260 #elif defined(DXX_BUILD_DESCENT_II)
261 	sbyte   cur_path_index;         // Current index in path.
262 	sbyte   dying_sound_playing;    // !0 if this robot is playing its dying sound.
263 	short   danger_laser_num;
264 	int     danger_laser_signature;
265 	fix     dying_start_time;       // Time at which this robot started dying.
266 #endif
267 } __pack__;
268 
269 // Same as above but structure Savegames expect
270 struct ai_local_rw
271 {
272 // These used to be bytes, changed to ints so I could set watchpoints on them.
273 #if defined(DXX_BUILD_DESCENT_I)
274 	sbyte      player_awareness_type;           // type of awareness of player
275 	sbyte      retry_count;                     // number of retries in physics last time this object got moved.
276 	sbyte      consecutive_retries;             // number of retries in consecutive frames (ie, without a retry_count of 0)
277 	sbyte      mode;                            // current mode within behavior
278 	sbyte      previous_visibility;             // Visibility of player last time we checked.
279 	sbyte      rapidfire_count;                 // number of shots fired rapidly
280 	short      goal_segment;                    // goal segment for current path
281 	fix        last_see_time, last_attack_time; // For sound effects, time at which player last seen, attacked
282 #elif defined(DXX_BUILD_DESCENT_II)
283 	int        player_awareness_type;         // type of awareness of player
284 	int        retry_count;                   // number of retries in physics last time this object got moved.
285 	int        consecutive_retries;           // number of retries in consecutive frames (ie, without a retry_count of 0)
286 	int        mode;                          // current mode within behavior
287 	int        previous_visibility;           // Visibility of player last time we checked.
288 	int        rapidfire_count;               // number of shots fired rapidly
289 	int        goal_segment;                  // goal segment for current path
290 #endif
291 	fix        next_action_time;              // time in seconds until something happens, mode dependent
292 	fix        next_fire;                     // time in seconds until can fire again
293 #if defined(DXX_BUILD_DESCENT_II)
294 	fix        next_fire2;                    // time in seconds until can fire again from second weapon
295 #endif
296 	fix        player_awareness_time;         // time in seconds robot will be aware of player, 0 means not aware of player
297 	fix        time_player_seen;              // absolute time in seconds at which player was last seen, might cause to go into follow_path mode
298 	fix        time_player_sound_attacked;    // absolute time in seconds at which player was last seen with visibility of 2.
299 	fix        next_misc_sound_time;          // absolute time in seconds at which this robot last made an angry or lurking sound.
300 	fix        time_since_processed;          // time since this robot last processed in do_ai_frame
301 	vms_angvec goal_angles[MAX_SUBMODELS];    // angles for each subobject
302 	vms_angvec delta_angles[MAX_SUBMODELS];   // angles for each subobject
303 	sbyte      goal_state[MAX_SUBMODELS];     // Goal state for this sub-object
304 	sbyte      achieved_state[MAX_SUBMODELS]; // Last achieved state
305 };
306 
307 struct ai_cloak_info : public prohibit_void_ptr<ai_cloak_info>
308 {
309 	fix64       last_time;
310 #if defined(DXX_BUILD_DESCENT_II)
311 	segnum_t         last_segment;
312 #endif
313 	vms_vector  last_position;
314 };
315 
316 // Same as above but structure Savegames expect
317 struct ai_cloak_info_rw
318 {
319 	fix         last_time;
320 #if defined(DXX_BUILD_DESCENT_II)
321 	int         last_segment;
322 #endif
323 	vms_vector  last_position;
324 };
325 
326 #if defined(DXX_BUILD_DESCENT_II)
327 
328 // Maximum number kept track of, will keep stealing, causes stolen weapons to be lost!
329 struct d_thief_unique_state
330 {
331 	unsigned Stolen_item_index;   // Used in ai.c for controlling rate of Thief flare firing.
332 	std::array<uint8_t, 10> Stolen_items;
333 };
334 
335 #endif
336 }
337 #endif
338 
339 namespace dcx {
340 
341 struct point_seg : prohibit_void_ptr<point_seg> {
342 	segnum_t         segnum;
343 	vms_vector  point;
344 };
345 
346 struct seg_seg
347 {
348 	segnum_t       start, end;
349 };
350 
351 constexpr std::integral_constant<unsigned, 2500> MAX_POINT_SEGS{};
352 
353 }
354 
355 // These are the information for a robot describing the location of
356 // the player last time he wasn't cloaked, and the time at which he
357 // was uncloaked.  We should store this for each robot, but that's
358 // memory expensive.
359 //extern fix        Last_uncloaked_time;
360 //extern vms_vector Last_uncloaked_position;
361 
362 #ifdef dsx
363 namespace dsx {
364 extern void ai_do_cloak_stuff(void);
365 }
366 #endif
367 
368 #endif
369