NXEngine v1.0.0.3
[NXEngine.git] / ai / ai.cpp
blobd476f5240ceefdb7a27c1a477fe16ab1f1113912
2 #include "stdai.h"
3 #include "ai.fdh"
5 InitList AIRoutines;
8 bool ai_init(void)
10 // setup function pointers to AI routines
11 for(int i=0;i<OBJ_LAST;i++)
12 memset(&objprop[i].ai_routines, 0, sizeof(objprop[i].ai_routines));
14 if (load_npc_tbl()) return 1;
16 // OBJ_NULL has flags set in npc.tbl, but shouldn't be set in our engine
17 objprop[OBJ_NULL].defaultflags = 0;
18 memcpy(&objprop[OBJ_SKULLHEAD_CARRIED], &objprop[OBJ_SKULLHEAD], sizeof(ObjProp));
20 objprop[OBJ_POLISH].initial_hp = 24; // is the value of 120 in npc.tbl really wrong? if so why?
21 objprop[OBJ_POLISH].death_sound = 25; // not sure why this is apparently wrong in file
23 // call all the INITFUNC() routines you find at the beginning
24 // of every AI-related module which assign AI logic to objects.
25 if (AIRoutines.CallFunctions())
27 staterr("ai_init: failed to initilize AIRoutines function pointers");
28 return 1;
31 return 0;
35 bool load_npc_tbl(void)
37 const int smoke_amounts[] = { 0, 3, 7, 12 };
38 const int nEntries = 361;
39 int i;
41 FILE *fp = fileopen("data/npc.tbl", "rb");
42 if (!fp) { staterr("load_npc_tbl: data/npc.tbl is missing"); return 1; }
44 stat("Reading npc.tbl...");
46 for(i=0;i<nEntries;i++) objprop[i].defaultflags = fgeti(fp);
47 for(i=0;i<nEntries;i++) objprop[i].initial_hp = fgeti(fp);
49 // next is a spritesheet # of something--but we don't use it, so skip
50 //for(i=0;i<nEntries;i++) fgetc(fp); // spritesheet # or something--but we don't use it
51 fseek(fp, (nEntries * 2 * 2) + nEntries, SEEK_SET);
53 for(i=0;i<nEntries;i++) objprop[i].death_sound = fgetc(fp);
54 for(i=0;i<nEntries;i++) objprop[i].hurt_sound = fgetc(fp);
55 for(i=0;i<nEntries;i++) objprop[i].death_smoke_amt = smoke_amounts[fgetc(fp)];
56 for(i=0;i<nEntries;i++) objprop[i].xponkill = fgetl(fp);
57 for(i=0;i<nEntries;i++) objprop[i].damage = fgetl(fp);
59 /*for(i=0;i<nEntries;i++)
61 int left = fgetc(fp);
62 int top = fgetc(fp);
63 int right = fgetc(fp);
64 int bottom = fgetc(fp);
66 if (i == 59)
68 stat("%d %d %d %d", left, top, right, bottom);
69 stat("sprite %d", objprop[i].sprite);
71 }*/
73 fclose(fp);
74 return 0;//1;
78 void c------------------------------() {}
81 // spawn an object at an enemies action point
82 Object *SpawnObjectAtActionPoint(Object *o, int otype)
84 int x, y;
85 Object *newObject;
87 x = o->x + (sprites[o->sprite].frame[o->frame].dir[o->dir].actionpoint.x << CSF);
88 y = o->y + (sprites[o->sprite].frame[o->frame].dir[o->dir].actionpoint.y << CSF);
89 newObject = CreateObject(x, y, otype);
90 newObject->dir = o->dir;
91 return newObject;
95 // destroys all objects of type "otype".
96 // creates a BoomFlash and smoke, but no bonuses.
97 void KillObjectsOfType(int type)
99 Object *o = firstobject;
100 while(o)
102 if (o->type == type)
104 SmokeClouds(o, 1, 0, 0);
105 effect(o->CenterX(), o->CenterY(), EFFECT_BOOMFLASH);
107 o->Delete();
110 o = o->next;
114 // deletes all objects of type "otype" silently, without any smoke or other effects.
115 void DeleteObjectsOfType(int type)
117 Object *o = firstobject;
118 while(o)
120 if (o->type == type)
122 o->Delete();
125 o = o->next;
130 void c------------------------------() {}
134 // handles object blinking: at random intervals forces object o's frame to blinkframe
135 // for blinktime frames.
136 void randblink(Object *o, int blinkframe, int blinktime, int prob)
138 if (o->blinktimer)
140 o->blinktimer--;
141 o->frame = blinkframe;
143 else if (random(0, prob) == 0)
145 o->frame = blinkframe;
146 o->blinktimer = 8;
150 // call this in an object's aftermove routine if it's an object
151 // which is being carried by the player like a puppy or curly.
152 // x_left: offset from p's action point when he faces left
153 // x_right: when he faces right
154 // off_y: vertical offset from p's action point
155 void StickToPlayer(Object *o, int x_left, int x_right, int off_y)
157 int x, y, frame;
159 // needed for puppy in chest
160 o->flags &= ~FLAG_SCRIPTONACTIVATE;
162 // by offsetting from the player's action point, where he holds his gun, we
163 // already have set up for us a nice up-and-down 1 pixel as he walks
164 frame = player->frame;
165 // the p's "up" frames have unusually placed action points so we have to cancel those out
166 if (frame >= 3 && frame <= 5) frame -= 3;
168 x = (player->x >> CSF) + sprites[player->sprite].frame[frame].dir[player->dir].actionpoint.x;
169 y = (player->y >> CSF) + sprites[player->sprite].frame[frame].dir[player->dir].actionpoint.y;
170 y += off_y;
172 if (player->dir == RIGHT)
174 x += x_right;
175 o->dir = RIGHT;
177 else
179 x += x_left;
180 o->dir = LEFT;
183 o->x = (x << CSF);
184 o->y = (y << CSF);
188 // used for some bosses with subobjects
189 void transfer_damage(Object *o, Object *target)
191 if (o->hp < 1000)
193 // if you forget to set hp to 1000 when creating the puppet object,
194 // it can immediately destroy the main object, possibly leading to crashes.
195 #ifdef DEBUG
196 ASSERT(o->hp != 0);
197 #endif
199 target->DealDamage(1000 - o->hp);
200 o->hp = 1000;
205 void c------------------------------() {}
208 // do the "teleport in" effect for object o.
209 // when complete, returns true.
210 // this function uses o->timer and assume o->timer starts at 0.
211 bool DoTeleportIn(Object *o, int slowness)
213 if (teleffect(o, slowness, false))
215 o->clip_enable = false;
216 return true;
219 return false;
222 // does a teleport out effect.
223 // When complete, returns true.
224 // this function uses o->timer and assume o->timer starts at 0.
225 bool DoTeleportOut(Object *o, int slowness)
227 return teleffect(o, slowness, true);
230 // common code for DoTeleportIn and DoTeleportOut
231 // returns true when teleport is complete
232 static bool teleffect(Object *o, int slowness, bool teleporting_out)
234 o->display_xoff = random(-1, 1);
236 if (!o->timer)
238 sound(SND_TELEPORT);
239 o->clip_enable = true;
240 o->clipy1 = 0;
243 if (++o->timer >= (sprites[o->sprite].h << slowness))
245 o->clip_enable = false;
246 o->display_xoff = 0;
247 return true;
249 else
251 int amt = (o->timer >> slowness);
253 if (teleporting_out)
254 o->clipy2 = sprites[o->sprite].h - amt;
255 else
256 o->clipy2 = amt;
258 return false;
263 void c------------------------------() {}
266 void ai_animate1(Object *o) { if (++o->frame >= sprites[o->sprite].nframes) o->frame = 0; }
267 void ai_animate2(Object *o) { simpleanim(o, 2); }
268 void ai_animate3(Object *o) { simpleanim(o, 3); }
269 void ai_animate4(Object *o) { simpleanim(o, 4); }
270 void ai_animate5(Object *o) { simpleanim(o, 5); }
272 static void simpleanim(Object *o, int spd)
274 if (++o->animtimer >= spd)
276 o->animtimer = 0;
277 if (++o->frame >= sprites[o->sprite].nframes) o->frame = 0;
282 void c------------------------------() {}
285 // aftermove routine which sticks the object to the action point of the NPC that's carrying it
286 void aftermove_StickToLinkedActionPoint(Object *o)
288 Object *link = o->linkedobject;
289 int dir;
291 if (link)
293 dir = (link->dir ^ o->carry.flip);
295 o->x = ((link->x >> CSF) + sprites[link->sprite].frame[link->frame].dir[dir].actionpoint.x) << CSF;
296 o->y = ((link->y >> CSF) + sprites[link->sprite].frame[link->frame].dir[dir].actionpoint.y) << CSF;
297 o->dir = dir;
299 else
301 o->Delete();
305 void onspawn_snap_to_ground(Object *o)
307 o->SnapToGround();
310 void onspawn_set_frame_from_id2(Object *o)
312 o->frame = o->id2;
316 void c------------------------------() {}