1 /*
2 
3 Copyright (C) 2015-2018 Night Dive Studios, LLC.
4 
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 
18 */
19 /*
20  * $Source: r:/prj/cit/src/RCS/ai.c $
21  * $Revision: 1.167 $
22  * $Author: xemu $
23  * $Date: 1994/10/18 19:50:51 $
24  */
25 
26 #define __AI_SRC
27 
28 #include <stdlib.h>
29 
30 #include "Headers/ai.h"
31 #include "aiflags.h"
32 #include "objects.h"
33 #include "objsim.h"
34 #include "objcrit.h"
35 #include "objwpn.h"
36 #include "physics.h"
37 #include "faketime.h"
38 #include "player.h"
39 #include "damage.h"
40 #include "diffq.h"
41 #include "combat.h"
42 #include "grenades.h"
43 #include "musicai.h"
44 #include "otrip.h"
45 #include "objprop.h"
46 #include "objbit.h"
47 #include "mapflags.h"
48 #include "tilename.h"
49 #include "mainloop.h"
50 #include "physunit.h"
51 #include "tools.h"
52 #include "gamestrn.h"
53 #include "safeedms.h"
54 #include "treasure.h"
55 #include "fullscrn.h"
56 #include "game_screen.h"
57 #include "sfxlist.h"
58 
59 #include "ice.h"
60 #include "cyber.h"
61 
62 #define AI_EDMS
63 
64 // errtype ai_fire_slow_projectile(ObjID src, int proj_triple, ObjLoc src_loc, ObjLoc target_loc, uchar a, int
65 // duration);  errtype ai_throw_grenade(ObjID src, int proj_triple, ObjLoc src_loc, ObjLoc target_loc);
66 errtype ai_fire_special(ObjID src, ObjID target, int proj_triple, ObjLoc src_loc, ObjLoc target_loc, uchar a,
67                         int duration);
68 
69 #define SLOW_PROJECTILE_DURATION 1000
70 #define SLOW_PROJECTILE_SPEED    fix_make(5, 0)
71 #define ATTACK_GRENADE_GRAVITY   fix_make(0, 0x8000)
72 #define ATTACK_GRENADE_SPEED     fix_make(7, 0)
73 
74 // tolerance to completion of an EDMS-driven AI maneuver
75 #define AI_COMPLETE_TOLERANCE fix_make(0, 0x5fff)
76 
77 // Number of frames beyond which, if we haven't seen the player,
78 // we stop trying to shoot at 'im.
79 #define UNSEEN_FIRE_THRESHOLD 10
80 
81 fix there_yet;
82 ObjLoc last_known_loc;
83 
84 // -----------
85 //  PROTOTYPES
86 // -----------
87 errtype set_posture_safe(ObjSpecID osid, ubyte new_pos);
88 errtype set_posture_movesafe(ObjSpecID osid, ubyte new_pos);
89 errtype clear_critter_controls(ObjSpecID osid);
90 errtype apply_EDMS_controls(ObjSpecID osid);
91 errtype roll_on_dnd_treasure_tables(int *pcont, char treasure_type);
92 
93 #define AI_HEAD_HIT_CHANCE 0x40
ai_find_player(ObjID id)94 void ai_find_player(ObjID id) {
95     State st;
96     extern void state_to_objloc(State * s, ObjLoc * l);
97     extern void get_phys_state(int ph, State *new_state, ObjID id);
98 
99     if (global_fullmap->cyber)
100         last_known_loc = objs[PLAYER_OBJ].loc;
101     else {
102         uchar slow_proj = FALSE;
103         CritterProp cp = CritterProps[CPNUM(id)];
104         // Some of the time they shoot at yer head, the other times at yer body.
105         // Unless they have a slow projectile, in which case they always shoot at yer head.
106         if ((cp.attacks[0].slow_proj != 0) || ((cp.alt_perc > 0) && (cp.attacks[1].slow_proj)))
107             slow_proj = TRUE;
108         if ((slow_proj) || ((rand() & 0xFF) < AI_HEAD_HIT_CHANCE) || (global_fullmap->cyber))
109             get_phys_state(objs[PLAYER_OBJ].info.ph, &st, PLAYER_OBJ);
110         //         EDMS_get_pelvic_viewpoint(objs[PLAYER_OBJ].info.ph, &st);
111         else
112             EDMS_get_state(objs[PLAYER_OBJ].info.ph, &st);
113         state_to_objloc(&st, &last_known_loc);
114         //      Warning(("player loc = %x, %x, %x\n",last_known_loc.x,last_known_loc.y,last_known_loc.z));
115     }
116 }
117 
set_posture(ObjSpecID osid,ubyte new_pos)118 errtype set_posture(ObjSpecID osid, ubyte new_pos) {
119     if (new_pos != get_crit_posture(osid)) {
120         set_crit_posture(osid, new_pos);
121         objs[objCritters[osid].id].info.current_frame = 0;
122     }
123     return (OK);
124 }
125 
set_posture_safe(ObjSpecID osid,ubyte new_pos)126 errtype set_posture_safe(ObjSpecID osid, ubyte new_pos) {
127     if ((get_crit_posture(osid) == STANDING_CRITTER_POSTURE) || (get_crit_posture(osid) == MOVING_CRITTER_POSTURE))
128         return (set_posture(osid, new_pos));
129     return (OK);
130 }
131 
set_posture_movesafe(ObjSpecID osid,ubyte new_pos)132 errtype set_posture_movesafe(ObjSpecID osid, ubyte new_pos) {
133     if ((get_crit_posture(osid) != STANDING_CRITTER_POSTURE) && (get_crit_posture(osid) != MOVING_CRITTER_POSTURE))
134         return (set_posture(osid, new_pos));
135     return (OK);
136 }
137 
clear_critter_controls(ObjSpecID osid)138 errtype clear_critter_controls(ObjSpecID osid) {
139     objCritters[osid].des_heading = 0;
140     objCritters[osid].des_speed = 0;
141     objCritters[osid].urgency = 0;
142     objCritters[osid].sidestep = 0;
143     return (OK);
144 }
145 
146 // tolerance between "standing" and "walking" anims
147 // when all your dots is greater than this, you are considered moving
148 //#define MOVE_TOLERANCE  fix_make(0,0x0700)
149 fix move_tolerance = fix_make(0, 0x0400);
150 
151 // copied in newai.c
152 #define DEFAULT_URGENCY fix_make(0x30, 0)
153 
apply_EDMS_controls(ObjSpecID osid)154 errtype apply_EDMS_controls(ObjSpecID osid) {
155 #ifdef AI_EDMS
156     State crit_state;
157     ObjCritter *pcrit = &objCritters[osid];
158     ObjID id = pcrit->id;
159     fix curr_ai_dist = FIX_UNIT;
160     fix use_speed, use_step;
161 
162     // Apply controls from last frame, see how close we came
163     if (CHECK_OBJ_PH(id)) {
164         safe_EDMS_get_state(objs[id].info.ph, &crit_state);
165 
166         // Hmm, I wonder whether there is a faster way to do this.
167         if (fix_abs(crit_state.X_dot) + fix_abs(crit_state.Y_dot) + fix_abs(crit_state.Z_dot) > move_tolerance) {
168             if ((get_crit_posture(osid) == STANDING_CRITTER_POSTURE) ||
169                 (get_crit_posture(osid) == ATTACK_REST_CRITTER_POSTURE)) {
170                 set_posture_safe(osid, MOVING_CRITTER_POSTURE);
171             }
172         } else {
173             set_posture_safe(osid, STANDING_CRITTER_POSTURE);
174         }
175 
176         if ((pcrit->urgency == 0) && !(pcrit->flags & AI_FLAG_TRANQ))
177             pcrit->urgency = DEFAULT_URGENCY;
178 
179         if (pcrit->orders == AI_ORDERS_NOMOVE) {
180             use_speed = 0;
181             use_step = 0;
182         } else {
183             use_speed = pcrit->des_speed;
184             use_step = pcrit->sidestep;
185         }
186 
187         safe_EDMS_ai_control_robot(objs[id].info.ph, pcrit->des_heading, use_speed, pcrit->sidestep, pcrit->urgency,
188                                    &there_yet, curr_ai_dist);
189     }
190 #endif
191     return (OK);
192 }
193 
194 #define SHODAN_AVATAR_HOSAGE_DISTANCE 0x4
195 #define AGITATED_ICE_DIST 100
196 
197 // percentage of hits to take in one attack in order to play SFX
198 #define HURT_SOUND_THRESHOLD 25
199 
200 // All critters within ANGER_RADIUS of a critter that takes damage
201 // become angry unless their loner bit is set.
202 #define ANGER_RADIUS 3
203 
ai_critter_seen(void)204 void ai_critter_seen(void) {
205     extern int mai_combat_length;
206     mlimbs_combat = player_struct.game_time + mai_combat_length;
207 }
208 
ai_critter_hit(ObjSpecID osid,short damage,uchar tranq,uchar stun)209 errtype ai_critter_hit(ObjSpecID osid, short damage, uchar tranq, uchar stun) {
210     char i, j;
211     short r;
212     char x1, x2, y1, y2;
213     ObjRefID oref;
214     ObjID oid;
215     char diff;
216 
217     // become unconfused & untranqed
218     // woo hoo, watch me pull odds out of my butt
219     if ((objCritters[osid].flags & (AI_FLAG_CONFUSED | AI_FLAG_TRANQ)) && ((rand() & 0xF) <= 6))
220         objCritters[osid].flags &= ~(AI_FLAG_CONFUSED | AI_FLAG_TRANQ);
221 
222     // If we are truly asleep, let the enemy pound on us...
223     if (ai_critter_sleeping(osid))
224         return (OK);
225 
226     oid = objCritters[osid].id;
227     switch (ID2TRIP(oid)) {
228     case ROBOBABE_TRIPLE: {
229         extern uchar *shodan_bitmask;
230         extern void shodan_phase_in(uchar * bitmask, short x, short y, short w, short h, short num, uchar dir);
231         shodan_phase_in(shodan_bitmask, 0, 0, FULL_VIEW_WIDTH, FULL_VIEW_HEIGHT, damage << 4, FALSE);
232     } break;
233     }
234 
235     // Play a sound effect if hurt enough
236     if ((damage * 100 / ObjProps[OPNUM(objCritters[osid].id)].hit_points) > HURT_SOUND_THRESHOLD)
237         play_digi_fx_obj(CritterProps[CPNUM(objCritters[osid].id)].hurt_sound, 1, objCritters[osid].id);
238 
239     // Depending on how disruptable we are, we might be disrupted
240     // that means we have to restart our attack timer
241     r = rand() % 255;
242     //   Spew(DSRC_AI_Combat, ("r = %d, dp = %d + %d =
243     //   %d\n",r,CritterProps[CPNUM(objCritters[osid].id)].disrupt_perc,damage >> 2,
244     //      CritterProps[CPNUM(objCritters[osid].id)].disrupt_perc + (damage >> 2)));
245     if (r < CritterProps[CPNUM(objCritters[osid].id)].disrupt_perc + (damage >> (5 - QUESTVAR_GET(COMBAT_DIFF_QVAR)))) {
246         set_posture(osid, DISRUPT_CRITTER_POSTURE);
247         objCritters[osid].attack_count =
248             player_struct.game_time + CritterProps[CPNUM(objCritters[osid].id)].attacks[0].speed;
249     }
250 
251     // And boy, are we ticked.
252     objCritters[osid].mood = AI_MOOD_HOSTILE;
253 
254     // We get to butt in line, and we know exactly where the player is...
255     ai_find_player(oid);
256     objCritters[osid].wait_frames = 0;
257 
258     if (tranq) {
259         objCritters[osid].flags |= AI_FLAG_TRANQ;
260         objCritters[osid].des_speed = 0;
261         objCritters[osid].urgency = 0;
262         objCritters[osid].sidestep = 0;
263         apply_gravity_to_one_object(oid, STANDARD_GRAVITY);
264         if (objs[oid].info.ph != -1) {
265             EDMS_control_robot(objs[oid].info.ph, fix_make(0, 0x0010), 0, 0);
266         }
267         if (get_crit_posture(osid) >= ATTACKING_CRITTER_POSTURE) {
268             set_crit_posture(osid, STANDING_CRITTER_POSTURE);
269             objs[oid].info.current_frame = 0;
270         }
271     }
272     if (stun)
273         objCritters[osid].flags |= AI_FLAG_CONFUSED;
274 
275     diff = QUESTVAR_GET((global_fullmap->cyber) ? CYBER_DIFF_QVAR : COMBAT_DIFF_QVAR);
276     if (diff > 0) {
277         // Hey, we'll get our buddies in on the matter too...
278         // We anger all critters within ANGER_RADIUS who are the same
279         // loner-ness as ourselves.
280         // The radius someday might want to be objprop dependant rather
281         // than a constant.
282         // Oh, and we also don't have any effect on critters with a mood of ISOLATION
283 
284         oid = objCritters[osid].id;
285         x1 = OBJ_LOC_BIN_X(objs[oid].loc) - ANGER_RADIUS;
286         x2 = x1 + (2 * ANGER_RADIUS);
287         y1 = OBJ_LOC_BIN_Y(objs[oid].loc) - ANGER_RADIUS;
288         y2 = y1 + (2 * ANGER_RADIUS);
289 
290         for (j = y1; j <= y2; j++) {
291             for (i = x1; i <= x2; i++) {
292                 oref = me_objref(MAP_GET_XY(i, j));
293                 while (oref != OBJ_REF_NULL) {
294                     oid = objRefs[oref].obj;
295                     if ((objs[oid].ref = oref) && (objs[oid].obclass == CLASS_CRITTER)) {
296                         if ((objCritters[objs[oid].specID].mood != AI_MOOD_ISOLATION) && (!ai_critter_sleeping(osid))) {
297                             // Only get upset if loner-ness matches our own.
298                             if ((objs[oid].info.inst_flags & CLASS_INST_FLAG) ==
299                                 (objs[objCritters[osid].id].info.inst_flags & CLASS_INST_FLAG)) {
300                                 objCritters[objs[oid].specID].mood = AI_MOOD_HOSTILE;
301                             }
302                         }
303                     }
304                     oref = objRefs[oref].next;
305                 }
306             }
307         }
308     }
309     return (OK);
310 }
311 
312 errtype ai_autobomb_explode(ObjID id, ObjSpecID osid);
313 
ai_critter_die(ObjSpecID osid)314 errtype ai_critter_die(ObjSpecID osid) {
315     extern ObjID damage_sound_id;
316     extern char damage_sound_fx;
317     ObjID id = objCritters[osid].id;
318 
319     if (ID2TRIP(id) == AUTOBOMB_TRIPLE)
320         ai_autobomb_explode(id, osid);
321 
322     set_posture(osid, DEATH_CRITTER_POSTURE);
323     if (CritterProps[CPNUM(id)].death_sound != 255) {
324         damage_sound_fx = CritterProps[CPNUM(id)].death_sound;
325         damage_sound_id = id;
326     }
327     //      play_digi_fx_obj(CritterProps[CPNUM(id)].death_sound,1,id);
328 
329     // If we were flying, we ain't no more!
330     if (CritterProps[CPNUM(id)].flags & AI_FLAG_FLYING)
331         apply_gravity_to_one_object(id, STANDARD_GRAVITY);
332     return (OK);
333 }
334 
335 #define FIRST_CORPSE_TTYPE 11
336 
337 int treasure_table[NUM_TREASURE_TYPES][NUM_TREASURE_SLOTS][NUM_TREASURE_ENTRIES] = {
338     // No Treasure
339     {{100, NOTHING_TRIPLE}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
340     // Humanoid (1)
341     {{7, LSD_DRUG_TRIPLE}, {15, MEDI_DRUG_TRIPLE}, {13, BEV_CONT_TRIPLE}, {65, NOTHING_TRIPLE}, {0, 0}, {0, 0}, {0, 0}},
342     // Drone (2)
343     {{14, SPAMMO_TRIPLE},
344      {8, TEFAMMO_TRIPLE},
345      {17, NNAMMO_TRIPLE},
346      {6, MEDI_DRUG_TRIPLE},
347      {5, FRAG_G_TRIPLE},
348      {50, NOTHING_TRIPLE},
349      {0, 0}},
350     // Assassin (3)
351     {{40, SPAMMO_TRIPLE},
352      {15, NNAMMO_TRIPLE},
353      {8, TEFAMMO_TRIPLE},
354      {7, TNAMMO_TRIPLE},
355      {30, NOTHING_TRIPLE},
356      {0, 0},
357      {0, 0}},
358     // Warrior Cyborg (4)
359     {{25, SPAMMO_TRIPLE},
360      {25, TEFAMMO_TRIPLE},
361      {15, HNAMMO_TRIPLE},
362      {5, SPLAMMO_TRIPLE},
363      {5, FRAG_G_TRIPLE},
364      {25, NOTHING_TRIPLE},
365      {0, 0}},
366     // Flier Bots (5)
367     {{30, SPAMMO_TRIPLE},
368      {10, NNAMMO_TRIPLE},
369      {5, TEFAMMO_TRIPLE},
370      {5, HNAMMO_TRIPLE},
371      {50, NOTHING_TRIPLE},
372      {0, 0},
373      {0, 0}},
374     // Security 1 Bots (6)
375     {{20, HNAMMO_TRIPLE},
376      {20, HTAMMO_TRIPLE},
377      {15, SPLAMMO_TRIPLE},
378      {15, MRAMMO_TRIPLE},
379      {10, TEFAMMO_TRIPLE},
380      {10, HSAMMO_TRIPLE},
381      {10, NOTHING_TRIPLE}},
382     // Exec Bots (7)
383     {{25, NOTHING_TRIPLE},
384      {25, SPLAMMO_TRIPLE},
385      {15, HTAMMO_TRIPLE},
386      {10, HNAMMO_TRIPLE},
387      {10, MRAMMO_TRIPLE},
388      {7, HSAMMO_TRIPLE},
389      {8, NOTHING_TRIPLE}},
390     // Cyborg Enforcer (8)
391     {{10, EMP_G_TRIPLE},
392      {40, MRAMMO_TRIPLE},
393      {13, SLGAMMO_TRIPLE},
394      {12, BGAMMO_TRIPLE},
395      {8, MEDI_DRUG_TRIPLE},
396      {2, AIDKIT_TRIPLE},
397      {15, STAMINA_DRUG_TRIPLE}},
398     // Security II Bot (9)
399     {{40, HTAMMO_TRIPLE},
400      {40, HSAMMO_TRIPLE},
401      {10, MRAMMO_TRIPLE},
402      {7, NOTHING_TRIPLE},
403      {3, PRAMMO_TRIPLE}, // no not impart prammo in favor of nothing
404      {0, 0},
405      {0, 0}},
406     // Elite Cyborg (10)
407     {{15, RGAMMO_TRIPLE},
408      {20, BGAMMO_TRIPLE},
409      {20, HSAMMO_TRIPLE},
410      {11, MEDI_DRUG_TRIPLE},
411      {2, AIDKIT_TRIPLE},
412      {27, NOTHING_TRIPLE},
413      {5, PRAMMO_TRIPLE}}, // yeah, what he said
414                           // Standard Corpse (11)
415     {{80, NOTHING_TRIPLE},
416      {5, HELMET_TRIPLE},
417      {5, BEV_CONT_TRIPLE},
418      {5, WRAPPER_TRIPLE},
419      {2, LSD_DRUG_TRIPLE},
420      {2, PHASER_TRIPLE},
421      {1, STAMINA_DRUG_TRIPLE}},
422     // loot-oriented corpse (12)
423     {{10, SPAMMO_TRIPLE},
424      {10, STAMINA_DRUG_TRIPLE},
425      {5, BATTERY_TRIPLE},
426      {11, MEDI_DRUG_TRIPLE},
427      {64, NOTHING_TRIPLE},
428      {0, 0},
429      {0, 0}},
430     // electro-stuff treasure (maint & repair bots)
431     {{5, EPICK_TRIPLE}, {15, BATTERY_TRIPLE}, {80, NOTHING_TRIPLE}, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
432     // serv-bot treasure
433     {{35, BEV_CONT_TRIPLE},
434      {5, MEDI_DRUG_TRIPLE},
435      {15, BEAKER_CONT_TRIPLE},
436      {15, FLASK_CONT_TRIPLE},
437      {12, BATTERY_TRIPLE},
438      {1, SKULL_TRIPLE},
439      {17, NOTHING_TRIPLE}},
440 };
441 
roll_on_dnd_treasure_tables(int * pcont,char treasure_type)442 errtype roll_on_dnd_treasure_tables(int *pcont, char treasure_type) {
443     char perc;
444     char count = 0;
445     uchar give, done = FALSE;
446     int chance, trip;
447 
448     perc = rand() % 100;
449     while (!done && (count < NUM_TREASURE_SLOTS)) {
450         give = FALSE;
451         chance = treasure_table[treasure_type][count][0];
452         trip = treasure_table[treasure_type][count][1];
453         if (chance == 0)
454             done = TRUE;
455         else {
456             if (TRIP2CL(trip) == CLASS_AMMO) {
457                 if (!player_struct.cartridges[get_nth_from_triple(trip)])
458                     give = TRUE;
459             }
460             if (give || perc < chance) {
461                 if (trip != NOTHING_TRIPLE) {
462                     if ((QUESTVAR_GET(COMBAT_DIFF_QVAR) <= 2) || ((rand() & 0xFF) < 0x80))
463                         *pcont = obj_create_base(trip);
464                 }
465                 done = TRUE;
466             } else
467                 perc -= chance;
468         }
469         count++;
470     }
471     return (OK);
472 }
473 
474 // Distribute loot as appropriate.  If we go to loot being contained
475 // "in" corpses, this is the procedure to change.
do_regular_loot(ObjSpecID source_critter,ObjID corpse)476 errtype do_regular_loot(ObjSpecID source_critter, ObjID corpse) {
477     ObjID l1, l2 = OBJ_NULL;
478     ObjSpecID osid = objs[corpse].specID;
479 
480     l1 = objCritters[source_critter].loot1;
481     if (objCritters[source_critter].orders != AI_ORDERS_HIGHWAY)
482         l2 = objCritters[source_critter].loot2;
483 
484     if (l2 != OBJ_NULL && objs[l2].active) {
485         objContainers[osid].contents1 = OBJ_NULL; // in case we had set it to a triple for later random generation
486         objContainers[osid].contents2 = l2;
487         // unset freshness flag
488         objs[corpse].info.inst_flags &= ~CLASS_INST_FLAG;
489         if (objs[l2].info.current_hp == 0)
490             objs[corpse].info.current_hp = 0;
491     }
492 
493     if (l1 != OBJ_NULL && objs[l1].active) {
494         objContainers[osid].contents1 = l1;
495         // unset freshness flag
496         objs[corpse].info.inst_flags &= ~CLASS_INST_FLAG;
497         if (objs[l1].info.current_hp == 0)
498             objs[corpse].info.current_hp = 0;
499     }
500 
501     return (OK);
502 }
503 
do_random_loot(ObjID corpse)504 errtype do_random_loot(ObjID corpse) {
505     ObjSpecID osid = objs[corpse].specID;
506     int *pc1, *pc2;
507     uchar t_type;
508     //   char buf[80];
509 
510     if ((((ID2TRIP(corpse) >= MUT_CORPSE1_TRIPLE) && (ID2TRIP(corpse) <= OTH_CORPSE8_TRIPLE)) ||
511          ((ID2TRIP(corpse) >= CORPSE1_TRIPLE) && (ID2TRIP(corpse) <= CORPSE8_TRIPLE))) &&
512         (objs[corpse].info.inst_flags & CLASS_INST_FLAG)) {
513         switch (objs[corpse].obclass) {
514         case CLASS_CONTAINER:
515             t_type = CritterProps[CPTRIP(objContainers[osid].contents1)].treasure_type;
516             pc1 = &objContainers[osid].contents1;
517             pc2 = &objContainers[osid].contents2;
518             *pc1 = 0;
519             *pc2 = 0;
520             break;
521         case CLASS_SMALLSTUFF:
522             t_type = FIRST_CORPSE_TTYPE + objSmallstuffs[osid].cosmetic_value;
523             pc1 = &objSmallstuffs[osid].data1;
524             pc2 = &objSmallstuffs[osid].data2;
525             break;
526         }
527 
528         if (*pc1 == 0) {
529             roll_on_dnd_treasure_tables(pc1, t_type);
530             switch (QUESTVAR_GET(COMBAT_DIFF_QVAR)) {
531             case 0:
532             case 1:
533                 if (*pc1 == 0)
534                     roll_on_dnd_treasure_tables(pc1, t_type);
535                 break;
536             }
537         }
538         if (*pc2 == 0) {
539             roll_on_dnd_treasure_tables(pc2, t_type);
540             switch (QUESTVAR_GET(COMBAT_DIFF_QVAR)) {
541             case 0:
542             case 1:
543                 if (*pc2 == 0)
544                     roll_on_dnd_treasure_tables(pc2, t_type);
545                 break;
546             }
547         }
548 
549         // unset the freshness bit
550         objs[corpse].info.inst_flags &= ~CLASS_INST_FLAG;
551     }
552     return (OK);
553 }
554 
ai_critter_really_dead(ObjSpecID osid)555 errtype ai_critter_really_dead(ObjSpecID osid) {
556     int corpse_trip;
557     char f;
558     extern errtype obj_floor_func(ObjID id);
559 
560     corpse_trip = CritterProps[CPNUM(objCritters[osid].id)].corpse;
561     if (corpse_trip != 0) {
562         ObjID new_obj;
563         new_obj = obj_create_base(corpse_trip);
564         if (new_obj) {
565             if ((f = FRAME_NUM_3D(ObjProps[OPNUM(new_obj)].bitmap_3d)))
566                 objs[new_obj].info.current_frame = rand() % (f + 1);
567             else
568                 objs[new_obj].info.current_frame = 0;
569             obj_move_to(new_obj, &objs[objCritters[osid].id].loc, TRUE);
570             //         obj_floor_func(new_obj);
571 
572             if (objCritters[osid].flags & AI_FLAG_NOLOOT) {
573                 objContainers[objs[new_obj].specID].contents1 = OBJ_NULL;
574                 objs[new_obj].info.inst_flags &= ~CLASS_INST_FLAG;
575             } else {
576                 // set our contents1 to the triple so that we know how to generate loot right later
577                 objContainers[objs[new_obj].specID].contents1 = ID2TRIP(objCritters[osid].id);
578 
579                 // fresh kill, yum!
580                 objs[new_obj].info.inst_flags |= CLASS_INST_FLAG;
581             }
582 
583             // if we have regular loot, however, do what we do
584             do_regular_loot(osid, new_obj);
585         }
586     }
587     return (OK);
588 }
589 
590 uchar pacifism_on;
591 #define AUTOBOMB_RANGE fix_make(3, 0)
592 
ai_misses(ObjSpecID osid)593 void ai_misses(ObjSpecID osid) {
594     // We aren't hitting, so get closer sometimes
595     if (rand() % 4 == 1) {
596         objs[objCritters[osid].id].info.inst_flags |= CLASS_INST_FLAG2;
597         objCritters[osid].mood = AI_MOOD_HOSTILE;
598     } else
599         objs[objCritters[osid].id].info.inst_flags &= ~CLASS_INST_FLAG2;
600 }
601 
ai_autobomb_explode(ObjID id,ObjSpecID osid)602 errtype ai_autobomb_explode(ObjID id, ObjSpecID osid) {
603     CritterAttack ca;
604     ExplosionData edata;
605     extern void critter_light_world(ObjID id);
606 
607     ca = CritterProps[CPNUM(id)].attacks[0];
608 
609     edata.radius = AUTOBOMB_RANGE;
610     edata.radius_change = (AUTOBOMB_RANGE >> 1);
611     edata.damage_mod = ca.damage_modifier;
612     edata.damage_change = ca.damage_modifier >> 1;
613     edata.dtype = ca.damage_type;
614     edata.knock_mass = ca.attack_mass;
615     edata.offense = ca.offense_value;
616     edata.penet = ca.penetration;
617 
618     // Do an explosion!
619     do_explosion(objs[id].loc, id, FALSE, &edata);
620     play_digi_fx_obj(SFX_EXPLOSION_1, 1, id);
621 
622     // Make us dying....
623     set_posture(osid, DEATH_CRITTER_POSTURE);
624 
625     // make us light up the world
626     critter_light_world(id);
627 
628     return (OK);
629 }
630 
ai_attack_player(ObjSpecID osid,char a)631 errtype ai_attack_player(ObjSpecID osid, char a) {
632     int wpnflags, wpnpower;
633     ObjID hit_obj = OBJ_NULL;
634     ObjID id = objCritters[osid].id;
635     ObjLoc dest_loc;
636     int cp_num;
637     fix attack_mass; // cause of new ray casting prototype - minman
638 
639     if (pacifism_on)
640         return (OK);
641 
642     cp_num = CPNUM(objCritters[osid].id);
643 
644     // If we are an autobomb, don't attack as usual, instead go boom!
645     switch (ID2TRIP(id)) {
646     case AUTOBOMB_TRIPLE: {
647         return (ai_autobomb_explode(id, osid));
648     } break;
649     }
650 
651     wpnpower = 100; // Full strength attack!
652     wpnflags = 0;   // Normal attack
653     attack_mass = fix_make(CritterProps[cp_num].attacks[a].attack_mass, 0) * 20;
654 
655 #ifdef CRITTER_ALWAYS_ACCURATE
656     hit_obj = ray_cast_objects(objCritters[osid].id, PLAYER_OBJ, attack_mass,
657                                fix_make(0, CritterProps[cp_num].attacks[a].attack_size),
658                                fix_make(CritterProps[cp_num].attacks[a].attack_velocity, 0),
659                                fix_make(CritterProps[cp_num].attacks[a].att_range, 0));
660 #else
661     // If we have NO idea where the player is (all failed detection rolls)
662     // then don't bother firing.
663     if (last_known_loc.x != 255) {
664         short miss_amt = ((255 - CritterProps[cp_num].attacks[a].accuracy) - rand() % 255);
665 
666         // Play sound effect
667         play_digi_fx_obj(CritterProps[cp_num].attack_sound, 1, objCritters[osid].id);
668         objs[objCritters[osid].id].info.inst_flags |= UNLIT_FLAG;
669 
670         // Shoot at player's last known location.
671         // Also, modify where we fire by our accuracy variable
672         dest_loc = last_known_loc;
673 
674         if (miss_amt > 0) {
675             // We've failed our accuracy roll, so let's perturb
676             // our target by an amount proportional to the amount
677             // that we missed by.
678             dest_loc.x += (rand() % miss_amt - (miss_amt / 2));
679             dest_loc.y += (rand() % miss_amt - (miss_amt / 2));
680             dest_loc.z += rand() % miss_amt;
681         }
682         if (CritterProps[cp_num].attacks[a].slow_proj == 0) {
683 #ifdef PLAYTEST
684             extern uchar prevent_ray_spew;
685             prevent_ray_spew = FALSE;
686 #endif
687             hit_obj = ray_cast_attack(objCritters[osid].id, dest_loc, attack_mass, RAYCAST_ATTACK_SIZE,
688                                       fix_make(CritterProps[cp_num].attacks[a].attack_velocity, 0),
689                                       fix_make(CritterProps[cp_num].attacks[a].att_range, 0));
690 #ifdef PLAYTEST
691             prevent_ray_spew = TRUE;
692 #endif
693             if (hit_obj != OBJ_NULL) {
694                 attack_object(hit_obj, CritterProps[cp_num].attacks[a].damage_type,
695                               CritterProps[cp_num].attacks[a].damage_modifier,
696                               CritterProps[cp_num].attacks[a].offense_value,
697                               CritterProps[cp_num].attacks[a].penetration, wpnflags, wpnpower, NULL, 0, 0, NULL);
698 
699                 objs[objCritters[osid].id].info.inst_flags &= ~CLASS_INST_FLAG2;
700             } else
701                 ai_misses(osid);
702         } else {
703             ai_fire_special(objCritters[osid].id, PLAYER_OBJ, CritterProps[cp_num].attacks[a].slow_proj,
704                             objs[objCritters[osid].id].loc, dest_loc, a, SLOW_PROJECTILE_DURATION);
705         }
706     } else {
707         objCritters[osid].mood = AI_MOOD_HOSTILE;
708     }
709 #endif
710     return (OK);
711 }
712 
713 #define SLOW_PROJ_RAY_MASS (fix_make(0, 0x1000))
714 #define SLOW_PROJ_RAY_SIZE (fix_make(0, 0x1800))
715 #define SLOW_PROJ_RAY_RANGE (fix_make(20, 0))
716 extern void get_phys_state(int ph, State *new_state, ObjID id);
717 
ai_fire_special(ObjID src,ObjID target,int proj_triple,ObjLoc src_loc,ObjLoc target_loc,uchar a,int duration)718 errtype ai_fire_special(ObjID src, ObjID target, int proj_triple, ObjLoc src_loc, ObjLoc target_loc, uchar a,
719                         int duration) {
720     ObjID proj_id;
721     fix xvel, yvel, zvel;
722     fix xdiff, ydiff, zdiff;
723     fix dist;
724     fix fire_speed;
725     fixang new_angle;
726     ubyte head;
727     State new_state;
728     Robot da_robot;
729     extern void activate_grenade(ObjSpecID osid);
730 
731     if (!global_fullmap->cyber) {
732         // let's attack only if we think we're going to hit our target
733         if (ray_cast_attack(src, target_loc, SLOW_PROJ_RAY_MASS, SLOW_PROJ_RAY_SIZE, NO_RAYCAST_KICKBACK_SPEED,
734                             SLOW_PROJ_RAY_RANGE) != target)
735             return (OK);
736     }
737 
738     proj_id = obj_create_base(proj_triple);
739     if (proj_id == OBJ_NULL) {
740         WARN("%s: Could not create slow projectile!", __FUNCTION__);
741         return (OK);
742     }
743 
744     if (TRIP2CL(proj_triple) == CLASS_PHYSICS) {
745         objPhysicss[objs[proj_id].specID].owner = src;
746         objPhysicss[objs[proj_id].specID].bullet_triple = a;
747         objPhysicss[objs[proj_id].specID].duration = player_struct.game_time + duration;
748         fire_speed = SLOW_PROJECTILE_SPEED;
749     } else {
750         activate_grenade(objs[proj_id].specID);
751         fire_speed = ATTACK_GRENADE_SPEED;
752     }
753 
754 #ifdef AI_EDMS
755 
756     if ((objs[src].obclass == CLASS_CRITTER) && (CritterProps[CPNUM(src)].proj_offset))
757         src_loc.z += (CritterProps[CPNUM(src)].proj_offset >> (SLOPE_SHIFT_D - 2));
758 
759     get_phys_state(objs[PLAYER_OBJ].info.ph, &new_state, PLAYER_OBJ);
760 
761     // Compute distance
762     xdiff = fix_from_obj_coord(target_loc.x) - fix_from_obj_coord(src_loc.x);
763     ydiff = fix_from_obj_coord(target_loc.y) - fix_from_obj_coord(src_loc.y);
764     zdiff = new_state.Z - fix_from_obj_height_val(src_loc.z);
765     dist = fix_fast_pyth_dist(xdiff, ydiff);
766 
767     if (global_fullmap->cyber) {
768         // let's get heading
769         new_angle = fix_atan2(ydiff, xdiff);
770         head = (ubyte)obj_angle_from_fixang(new_angle); //(fix_div(new_angle, FIXANG_PI) >> 9);
771 
772         // let's start the work for pitch
773         new_angle = fix_atan2(zdiff, dist);
774         //      pitch = (ubyte) (fix_div(new_angle, FIXANG_PI) >> 9);
775 
776         // shift coordinate frames for heading
777         src_loc.h = (ubyte)((320L - head) % 256);
778         src_loc.p = obj_angle_from_fixang(new_angle); // pitch;
779         src_loc.b = 0;
780     }
781 
782     xvel = fix_mul_div(xdiff, fire_speed, dist);
783     yvel = fix_mul_div(ydiff, fire_speed, dist);
784 
785     // LET'S HACK HACK HACK THE WAY GRENADES ARE THROWN!!!!
786     // let's guess on the zvel!
787     if (TRIP2CL(proj_triple) == CLASS_PHYSICS)
788         zvel = fix_mul_div(zdiff, fire_speed, dist);
789     else
790         zvel = (zdiff < fix_make(0, 0x4000)) ? fix_mul(fix_make(0, 0x3800), dist) : fix_mul(zdiff, fix_make(4, 0));
791 
792     obj_move_to_vel(proj_id, &src_loc, TRUE, xvel, yvel, zvel);
793     EDMS_ignore_collisions(objs[src].info.ph, objs[proj_id].info.ph);
794     if (TRIP2CL(proj_triple) == CLASS_PHYSICS)
795         apply_gravity_to_one_object(proj_id, SLOW_PROJECTILE_GRAVITY);
796     else
797         apply_gravity_to_one_object(proj_id, ATTACK_GRENADE_GRAVITY);
798 
799     // don't ask me why i have to do this - but i do
800     EDMS_get_robot_parameters(objs[proj_id].info.ph, &da_robot);
801     da_robot.cyber_space = -1;
802     EDMS_set_robot_parameters(objs[proj_id].info.ph, &da_robot);
803 
804 #endif
805     return (OK);
806 }
807 
808 /* KLC - these don't do anything.
809 errtype ai_freeze_tag()
810 {
811    return(OK);
812 }
813 
814 errtype ai_time_passes(ulong *ticks_passed)
815 {
816    return(OK);
817 }
818 */
819