/* fixbot.c */ #include "g_local.h" #include "m_fixbot.h" #define MZ2_fixbot_BLASTER_1 MZ2_HOVER_BLASTER_1 qboolean visible (edict_t *self, edict_t *other); qboolean infront (edict_t *self, edict_t *other); static int sound_pain1; static int sound_die; static int sound_weld1; static int sound_weld2; static int sound_weld3; void fixbot_run (edict_t *self); void fixbot_stand (edict_t *self); void fixbot_dead (edict_t *self); void fixbot_attack (edict_t *self); void fixbot_fire_blaster (edict_t *self); void fixbot_fire_welder (edict_t *self); void fixbot_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point); void use_scanner (edict_t *self); void change_to_roam (edict_t *self); void fly_vertical (edict_t *self); extern mmove_t fixbot_move_forward; extern mmove_t fixbot_move_stand; extern mmove_t fixbot_move_stand2; extern mmove_t fixbot_move_roamgoal; extern mmove_t fixbot_move_weld_start; extern mmove_t fixbot_move_weld; extern mmove_t fixbot_move_weld_end; extern mmove_t fixbot_move_takeoff; extern mmove_t fixbot_move_landing; extern mmove_t fixbot_move_turn; extern void roam_goal (edict_t *self); void ED_CallSpawn (edict_t *ent); float crand(void) { return (rand()&32767)*(2.0/32767)-1; }; edict_t *fixbot_FindDeadMonster (edict_t *self) { edict_t *ent = NULL; edict_t *best = NULL; while ((ent = findradius(ent, self->s.origin, 1024)) != NULL) { if (ent == self) continue; if (!(ent->svflags & SVF_MONSTER)) continue; if (ent->monsterinfo.aiflags & AI_GOOD_GUY) continue; if (ent->owner) continue; if (ent->health > 0) continue; if (ent->nextthink) continue; if (!visible(self, ent)) continue; if (!best) { best = ent; continue; } if (ent->max_health <= best->max_health) continue; best = ent; } return best; } int fixbot_search (edict_t *self) { edict_t *ent; if (!self->goalentity) { ent = fixbot_FindDeadMonster(self); if (ent) { self->oldenemy = self->enemy; self->enemy = ent; self->enemy->owner = self; self->monsterinfo.aiflags |= AI_MEDIC; FoundTarget (self); return (1); } } return (0); } void landing_goal (edict_t *self) { trace_t tr; vec3_t forward, right, up; vec3_t end; edict_t *ent; ent = G_Spawn (); ent->classname = "bot_goal"; ent->solid = SOLID_BBOX; ent->owner = self; gi.linkentity (ent); // Knightmare- shrunk bbox, was (-32, -32, -24), (32, 32, 34) VectorSet (ent->mins, -24, -24, -16); VectorSet (ent->maxs, 24, 24, 16); AngleVectors (self->s.angles, forward, right, up); VectorMA (self->s.origin, 32, forward, end); VectorMA (self->s.origin, -8096, up, end); tr = gi.trace (self->s.origin, ent->mins, ent->maxs, end, self, MASK_MONSTERSOLID); VectorCopy (tr.endpos, ent->s.origin); self->goalentity = self->enemy = ent; self->monsterinfo.currentmove = &fixbot_move_landing; } void takeoff_goal (edict_t *self) { trace_t tr; vec3_t forward, right, up; vec3_t end; edict_t *ent; ent = G_Spawn (); ent->classname = "bot_goal"; ent->solid = SOLID_BBOX; ent->owner = self; gi.linkentity (ent); // Knightmare- shrunk bbox, was (-32, -32, -24), (32, 32, 34) VectorSet (ent->mins, -24, -24, -16); VectorSet (ent->maxs, 24, 24, 16); AngleVectors (self->s.angles, forward, right, up); VectorMA (self->s.origin, 32, forward, end); VectorMA (self->s.origin, 128, up, end); tr = gi.trace (self->s.origin, ent->mins, ent->maxs, end, self, MASK_MONSTERSOLID); VectorCopy (tr.endpos, ent->s.origin); self->goalentity = self->enemy = ent; self->monsterinfo.currentmove = &fixbot_move_takeoff; } void change_to_roam (edict_t *self) { if (fixbot_search(self)) return; self->monsterinfo.currentmove = &fixbot_move_roamgoal; if (self->spawnflags & 16) { landing_goal (self); self->monsterinfo.currentmove = &fixbot_move_landing; self->spawnflags &= ~16; self->spawnflags = 32; } if (self->spawnflags & 8) { takeoff_goal (self); self->monsterinfo.currentmove = &fixbot_move_takeoff; self->spawnflags &= ~8; self->spawnflags = 32; } if (self->spawnflags & 4) { self->monsterinfo.currentmove = &fixbot_move_roamgoal; self->spawnflags &= ~4; self->spawnflags = 32; } if (!self->spawnflags) { self->monsterinfo.currentmove = &fixbot_move_stand2; } } void roam_goal (edict_t *self) { trace_t tr; vec3_t forward, right, up; vec3_t end; edict_t *ent; vec3_t dang; int len, oldlen, whichi, i; vec3_t vec; vec3_t whichvec; ent = G_Spawn (); ent->classname = "bot_goal"; ent->solid = SOLID_BBOX; ent->owner = self; gi.linkentity (ent); oldlen = 0; whichi = 0; for (i=0; i<12; i++) { VectorCopy (self->s.angles, dang); if (i < 6) dang[YAW] += 30 * i; else dang[YAW] -= 30 * (i-6); AngleVectors (dang, forward, right, up); VectorMA (self->s.origin, WORLD_SIZE, forward, end); // was 8192 tr = gi.trace (self->s.origin, NULL, NULL, end, self, MASK_SHOT); VectorSubtract (self->s.origin, tr.endpos, vec); len = VectorNormalize (vec); if (len > oldlen) { oldlen=len; VectorCopy (tr.endpos, whichvec); } } VectorCopy (whichvec, ent->s.origin); self->goalentity = self->enemy = ent; self->monsterinfo.currentmove = &fixbot_move_turn; } void use_scanner (edict_t *self) { edict_t *ent = NULL; float radius = 1024; vec3_t vec; int len; int oldlen = 0x10000; edict_t *tempent = NULL; while ((ent = findradius(ent, self->s.origin, radius)) != NULL) { if (ent->health >= 100) { if (strcmp (ent->classname, "object_repair") == 0) { if (visible(self, ent)) { // remove the old one if (strcmp (self->goalentity->classname, "bot_goal") == 0) { self->goalentity->nextthink = level.time + 0.1; self->goalentity->think = G_FreeEdict; } self->goalentity = self->enemy = ent; VectorSubtract (self->s.origin, self->goalentity->s.origin, vec); len = VectorNormalize (vec); if (len < 32) { self->monsterinfo.currentmove = &fixbot_move_weld_start; return; } return; } } } } VectorSubtract (self->s.origin, self->goalentity->s.origin, vec); len = VectorLength (vec); if (len < 32) { if (strcmp (self->goalentity->classname, "object_repair") == 0) { self->monsterinfo.currentmove = &fixbot_move_weld_start; } else { self->goalentity->nextthink = level.time + 0.1; self->goalentity->think = G_FreeEdict; self->goalentity = self->enemy = NULL; self->monsterinfo.currentmove = &fixbot_move_stand; } return; } VectorSubtract (self->s.origin, self->s.old_origin, vec); len = VectorLength (vec); /* bot is stuck get new goalentity */ if (len == 0) { if (strcmp (self->goalentity->classname , "object_repair") == 0) { self->monsterinfo.currentmove = &fixbot_move_stand; } else { self->goalentity->nextthink = level.time + 0.1; self->goalentity->think = G_FreeEdict; self->goalentity = self->enemy = NULL; self->monsterinfo.currentmove = &fixbot_move_stand; } } } /* when the bot has found a landing pad it will proceed to its goalentity just above the landing pad and decend translated along the z the current frames are at 10fps */ void blastoff (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread) { trace_t tr; vec3_t dir; vec3_t forward, right, up; vec3_t end; float r; float u; vec3_t water_start; qboolean water = false; int content_mask = MASK_SHOT | MASK_WATER; hspread+= (self->s.frame - FRAME_takeoff_01); vspread+= (self->s.frame - FRAME_takeoff_01); tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT); if (!(tr.fraction < 1.0)) { vectoangles (aimdir, dir); AngleVectors (dir, forward, right, up); r = crandom()*hspread; u = crandom()*vspread; VectorMA (start, WORLD_SIZE, forward, end); // was 8192 VectorMA (end, r, right, end); VectorMA (end, u, up, end); if (gi.pointcontents (start) & MASK_WATER) { water = true; VectorCopy (start, water_start); content_mask &= ~MASK_WATER; } tr = gi.trace (start, NULL, NULL, end, self, content_mask); // see if we hit water if (tr.contents & MASK_WATER) { int color; water = true; VectorCopy (tr.endpos, water_start); if (!VectorCompare (start, tr.endpos)) { if (tr.contents & CONTENTS_WATER) { if (strcmp(tr.surface->name, "*brwater") == 0) color = SPLASH_BROWN_WATER; else color = SPLASH_BLUE_WATER; } else if (tr.contents & CONTENTS_SLIME) color = SPLASH_SLIME; else if (tr.contents & CONTENTS_LAVA) color = SPLASH_LAVA; else color = SPLASH_UNKNOWN; if (color != SPLASH_UNKNOWN) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_SPLASH); gi.WriteByte (8); gi.WritePosition (tr.endpos); gi.WriteDir (tr.plane.normal); gi.WriteByte (color); gi.multicast (tr.endpos, MULTICAST_PVS); } // change bullet's course when it enters water VectorSubtract (end, start, dir); vectoangles (dir, dir); AngleVectors (dir, forward, right, up); r = crandom()*hspread*2; u = crandom()*vspread*2; VectorMA (water_start, WORLD_SIZE, forward, end); // was 8192 VectorMA (end, r, right, end); VectorMA (end, u, up, end); } // re-trace ignoring water this time tr = gi.trace (water_start, NULL, NULL, end, self, MASK_SHOT); } } // send gun puff / flash if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) { if (tr.fraction < 1.0) { if (tr.ent->takedamage) { T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_BULLET, MOD_BLASTOFF); } else { if (strncmp (tr.surface->name, "sky", 3) != 0) { gi.WriteByte (svc_temp_entity); gi.WriteByte (te_impact); gi.WritePosition (tr.endpos); gi.WriteDir (tr.plane.normal); gi.multicast (tr.endpos, MULTICAST_PVS); if (self->client) PlayerNoise(self, tr.endpos, PNOISE_IMPACT); } } } } // if went through water, determine where the end and make a bubble trail if (water) { vec3_t pos; VectorSubtract (tr.endpos, water_start, dir); VectorNormalize (dir); VectorMA (tr.endpos, -2, dir, pos); if (gi.pointcontents (pos) & MASK_WATER) VectorCopy (pos, tr.endpos); else tr = gi.trace (pos, NULL, NULL, water_start, tr.ent, MASK_WATER); VectorAdd (water_start, tr.endpos, pos); VectorScale (pos, 0.5, pos); gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_BUBBLETRAIL); gi.WritePosition (water_start); gi.WritePosition (tr.endpos); gi.multicast (pos, MULTICAST_PVS); } } void fly_vertical (edict_t *self) { int i; vec3_t v; vec3_t forward, right, up; vec3_t start; vec3_t tempvec; VectorSubtract (self->goalentity->s.origin, self->s.origin, v); self->ideal_yaw = vectoyaw(v); M_ChangeYaw (self); if (self->s.frame == FRAME_landing_58 || self->s.frame == FRAME_takeoff_16) { self->goalentity->nextthink = level.time + 0.1; self->goalentity->think = G_FreeEdict; self->monsterinfo.currentmove = &fixbot_move_stand; self->goalentity = self->enemy = NULL; } // kick up some particles VectorCopy (self->s.angles, tempvec); tempvec[PITCH] += 90; AngleVectors (tempvec, forward, right, up); VectorCopy (self->s.origin, start); for (i=0; i< 10; i++) blastoff (self, start, forward, 2, 1, TE_SHOTGUN, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD); // needs sound } void fly_vertical2 (edict_t *self) { vec3_t v; int len; VectorSubtract (self->goalentity->s.origin, self->s.origin, v); len = VectorLength (v); self->ideal_yaw = vectoyaw(v); M_ChangeYaw (self); if (len < 32) { self->goalentity->nextthink = level.time + 0.1; self->goalentity->think = G_FreeEdict; self->monsterinfo.currentmove = &fixbot_move_stand; self->goalentity = self->enemy = NULL; } // needs sound } mframe_t fixbot_frames_landing [] = { ai_move, 0, NULL, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2, ai_move, 0, fly_vertical2 }; mmove_t fixbot_move_landing = { FRAME_landing_01, FRAME_landing_58, fixbot_frames_landing, NULL }; /* generic ambient stand */ mframe_t fixbot_frames_stand [] = { ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, change_to_roam }; mmove_t fixbot_move_stand = {FRAME_ambient_01, FRAME_ambient_19, fixbot_frames_stand, NULL}; mframe_t fixbot_frames_stand2 [] = { ai_stand, 0, NULL, ai_stand, 0, NULL, ai_stand, 0, NULL, ai_stand, 0, NULL, ai_stand, 0, NULL, ai_stand, 0, NULL, ai_stand, 0, NULL, ai_stand, 0, NULL, ai_stand, 0, NULL, ai_stand, 0, NULL, ai_stand, 0, NULL, ai_stand, 0, NULL, ai_stand, 0, NULL, ai_stand, 0, NULL, ai_stand, 0, NULL, ai_stand, 0, NULL, ai_stand, 0, NULL, ai_stand, 0, NULL, ai_stand, 0, NULL }; mmove_t fixbot_move_stand2 = {FRAME_ambient_01, FRAME_ambient_19, fixbot_frames_stand2, NULL}; /* will need the pickup offset for the front pincers object will need to stop forward of the object and take the object with it ( this may require a variant of liftoff and landing ) */ mframe_t fixbot_frames_pickup [] = { ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL }; mmove_t fixbot_move_pickup = { FRAME_pickup_01, FRAME_pickup_27, fixbot_frames_pickup, NULL}; /* generic frame to move bot */ mframe_t fixbot_frames_roamgoal [] = { ai_move, 0, roam_goal }; mmove_t fixbot_move_roamgoal = {FRAME_freeze_01, FRAME_freeze_01, fixbot_frames_roamgoal, NULL}; void ai_facing (edict_t *self, float dist) { vec3_t v; if (infront (self, self->goalentity)) self->monsterinfo.currentmove = &fixbot_move_forward; else { VectorSubtract (self->goalentity->s.origin, self->s.origin, v); self->ideal_yaw = vectoyaw(v); M_ChangeYaw (self); } }; mframe_t fixbot_frames_turn [] = { ai_facing, 0, NULL }; mmove_t fixbot_move_turn = {FRAME_freeze_01, FRAME_freeze_01, fixbot_frames_turn, NULL}; void go_roam (edict_t *self) { self->monsterinfo.currentmove = &fixbot_move_stand; } /* takeoff */ mframe_t fixbot_frames_takeoff [] = { ai_move, 0.01, fly_vertical, ai_move, 0.01, fly_vertical, ai_move, 0.01, fly_vertical, ai_move, 0.01, fly_vertical, ai_move, 0.01, fly_vertical, ai_move, 0.01, fly_vertical, ai_move, 0.01, fly_vertical, ai_move, 0.01, fly_vertical, ai_move, 0.01, fly_vertical, ai_move, 0.01, fly_vertical, ai_move, 0.01, fly_vertical, ai_move, 0.01, fly_vertical, ai_move, 0.01, fly_vertical, ai_move, 0.01, fly_vertical, ai_move, 0.01, fly_vertical, ai_move, 0.01, fly_vertical }; mmove_t fixbot_move_takeoff = {FRAME_takeoff_01, FRAME_takeoff_16, fixbot_frames_takeoff, NULL}; /* findout what this is */ mframe_t fixbot_frames_paina [] = { ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL }; mmove_t fixbot_move_paina = {FRAME_paina_01, FRAME_paina_06, fixbot_frames_paina, fixbot_run}; /* findout what this is */ mframe_t fixbot_frames_painb [] = { ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL, ai_move, 0, NULL }; mmove_t fixbot_move_painb = {FRAME_painb_01, FRAME_painb_08, fixbot_frames_painb, fixbot_run}; /* backup from pain call a generic painsound some spark effects */ mframe_t fixbot_frames_pain3 [] = { ai_move, -1, NULL }; mmove_t fixbot_move_pain3 = {FRAME_freeze_01, FRAME_freeze_01, fixbot_frames_pain3, fixbot_run}; /* bot has compleated landing and is now on the grownd ( may need second land if the bot is releasing jib into jib vat ) */ mframe_t fixbot_frames_land [] = { ai_move, 0, NULL }; mmove_t fixbot_move_land = {FRAME_freeze_01, FRAME_freeze_01, fixbot_frames_land, NULL}; void M_MoveToGoal (edict_t *ent, float dist); void ai_movetogoal (edict_t *self, float dist) { M_MoveToGoal (self, dist); } /* */ mframe_t fixbot_frames_forward [] = { ai_movetogoal, 5, use_scanner }; mmove_t fixbot_move_forward = {FRAME_freeze_01, FRAME_freeze_01, fixbot_frames_forward, NULL}; /* */ mframe_t fixbot_frames_walk [] = { ai_walk, 5, NULL }; mmove_t fixbot_move_walk = {FRAME_freeze_01, FRAME_freeze_01, fixbot_frames_walk, NULL}; /* */ mframe_t fixbot_frames_run [] = { ai_run, 10, NULL }; mmove_t fixbot_move_run = {FRAME_freeze_01, FRAME_freeze_01, fixbot_frames_run, NULL}; /* raf note to self they could have a timer that will cause the bot to explode on countdown */ mframe_t fixbot_frames_death1 [] = { ai_move, 0, NULL }; mmove_t fixbot_move_death1 = {FRAME_freeze_01, FRAME_freeze_01, fixbot_frames_death1, fixbot_dead}; // mframe_t fixbot_frames_backward [] = { ai_move, 0, NULL }; mmove_t fixbot_move_backward = {FRAME_freeze_01, FRAME_freeze_01, fixbot_frames_backward, NULL}; // mframe_t fixbot_frames_start_attack [] = { ai_charge, 0, NULL }; mmove_t fixbot_move_start_attack = {FRAME_freeze_01, FRAME_freeze_01, fixbot_frames_start_attack, fixbot_attack}; /* TBD: need to get laser attack anim attack with the laser blast */ mframe_t fixbot_frames_attack1 [] = { ai_charge, 0, NULL, ai_charge, 0, NULL, ai_charge, 0, NULL, ai_charge, 0, NULL, ai_charge, 0, NULL, ai_charge, -10, fixbot_fire_blaster }; mmove_t fixbot_move_attack1 = {FRAME_shoot_01, FRAME_shoot_06, fixbot_frames_attack1, NULL}; int check_telefrag (edict_t *self) { vec3_t start = { 0, 0, 0 }; vec3_t forward, right, up; trace_t tr; AngleVectors (self->enemy->s.angles, forward, right, up); VectorMA (start, 48, up, start); tr = gi.trace (self->enemy->s.origin, self->enemy->mins, self->enemy->maxs, start, self, MASK_MONSTERSOLID); if (tr.ent->takedamage) { tr.ent->health = -1000; return (0); } return (1); } void fixbot_fire_laser (edict_t *self) { vec3_t forward, right, up; vec3_t tempang, start; vec3_t dir, angles, end; edict_t *ent; // critter dun got blown up while bein' fixed if (self->enemy->health <= self->enemy->gib_health) { self->monsterinfo.currentmove = &fixbot_move_stand; self->monsterinfo.aiflags &= ~AI_MEDIC; return; } gi.sound(self, CHAN_AUTO, gi.soundindex("misc/lasfly.wav"), 1, ATTN_STATIC, 0); VectorCopy (self->s.origin, start); VectorCopy (self->enemy->s.origin, end); VectorSubtract (end, start, dir); vectoangles (dir, angles); ent = G_Spawn (); VectorCopy (self->s.origin, ent->s.origin); VectorCopy (angles, tempang); AngleVectors (tempang, forward, right, up); VectorCopy (tempang, ent->s.angles); VectorCopy (ent->s.origin, start); VectorMA (start, 16, forward, start); VectorCopy (start, ent->s.origin); ent->enemy = self->enemy; ent->owner = self; ent->dmg = -1; monster_fire_dabeam (ent); if (self->enemy->health > (self->enemy->mass/10)) { // sorry guys but had to fix the problem this way // if it doesn't do this then two creatures can share the same space // and its real bad. if (check_telefrag (self)) { self->enemy->spawnflags = 0; self->enemy->monsterinfo.aiflags = 0; self->enemy->target = NULL; self->enemy->targetname = NULL; self->enemy->combattarget = NULL; self->enemy->deathtarget = NULL; self->enemy->owner = self; //Knightmare added self->enemy->monsterinfo.monsterflags |= MFL_DO_NOT_COUNT; self->enemy->item = NULL; // Lazarus: reset initially dead monsters to use the INVERSE of their // initial health, and force gib_health to default value if (self->enemy->max_health < 0) { self->enemy->max_health = -self->enemy->max_health; self->enemy->gib_health = 0; } self->enemy->health = self->enemy->max_health; self->enemy->s.skinnum = 0; //end Knightmare ED_CallSpawn (self->enemy); self->enemy->owner = NULL; self->s.origin[2] += 1; self->enemy->monsterinfo.aiflags &= ~AI_RESURRECTING; self->monsterinfo.currentmove = &fixbot_move_stand; self->monsterinfo.aiflags &= ~AI_MEDIC; } } else self->enemy->monsterinfo.aiflags |= AI_RESURRECTING; } mframe_t fixbot_frames_laserattack [] = { ai_charge, 0, fixbot_fire_laser, ai_charge, 0, fixbot_fire_laser, ai_charge, 0, fixbot_fire_laser, ai_charge, 0, fixbot_fire_laser, ai_charge, 0, fixbot_fire_laser, ai_charge, 0, fixbot_fire_laser }; mmove_t fixbot_move_laserattack = {FRAME_shoot_01, FRAME_shoot_06, fixbot_frames_laserattack, NULL}; /* need to get forward translation data for the charge attack */ mframe_t fixbot_frames_attack2 [] = { ai_charge, 0, NULL, ai_charge, 0, NULL, ai_charge, 0, NULL, ai_charge, 0, NULL, ai_charge, 0, NULL, ai_charge, 0, NULL, ai_charge, 0, NULL, ai_charge, 0, NULL, ai_charge, 0, NULL, ai_charge, 0, NULL, ai_charge, -10, NULL, ai_charge, -10, NULL, ai_charge, -10, NULL, ai_charge, -10, NULL, ai_charge, -10, NULL, ai_charge, -10, NULL, ai_charge, -10, NULL, ai_charge, -10, NULL, ai_charge, -10, NULL, ai_charge, -10, NULL, ai_charge, 0, fixbot_fire_blaster, ai_charge, 0, NULL, ai_charge, 0, NULL, ai_charge, 0, NULL, ai_charge, 0, NULL, ai_charge, 0, NULL, ai_charge, 0, NULL, ai_charge, 0, NULL, ai_charge, 0, NULL, ai_charge, 0, NULL, ai_charge, 0, NULL }; mmove_t fixbot_move_attack2 = {FRAME_charging_01, FRAME_charging_31, fixbot_frames_attack2, fixbot_run}; void weldstate (edict_t *self) { if (self->s.frame == FRAME_weldstart_10) self->monsterinfo.currentmove = &fixbot_move_weld; else if (self->s.frame == FRAME_weldmiddle_07) { if (self->goalentity->health < 0) { self->enemy->owner = NULL; self->monsterinfo.currentmove = &fixbot_move_weld_end; } else self->goalentity->health-=10; } else { self->goalentity = self->enemy = NULL; self->monsterinfo.currentmove = &fixbot_move_stand; } } void ai_move2 (edict_t *self, float dist) { vec3_t v; if (dist) M_walkmove (self, self->s.angles[YAW], dist); VectorSubtract (self->goalentity->s.origin, self->s.origin, v); self->ideal_yaw = vectoyaw(v); M_ChangeYaw (self); }; mframe_t fixbot_frames_weld_start [] = { ai_move2, 0, NULL, ai_move2, 0, NULL, ai_move2, 0, NULL, ai_move2, 0, NULL, ai_move2, 0, NULL, ai_move2, 0, NULL, ai_move2, 0, NULL, ai_move2, 0, NULL, ai_move2, 0, NULL, ai_move2, 0, weldstate }; mmove_t fixbot_move_weld_start = {FRAME_weldstart_01, FRAME_weldstart_10, fixbot_frames_weld_start, NULL}; mframe_t fixbot_frames_weld [] = { ai_move2, 0, fixbot_fire_welder, ai_move2, 0, fixbot_fire_welder, ai_move2, 0, fixbot_fire_welder, ai_move2, 0, fixbot_fire_welder, ai_move2, 0, fixbot_fire_welder, ai_move2, 0, fixbot_fire_welder, ai_move2, 0, weldstate }; mmove_t fixbot_move_weld = {FRAME_weldmiddle_01, FRAME_weldmiddle_07, fixbot_frames_weld, NULL}; mframe_t fixbot_frames_weld_end [] = { ai_move2, -2, NULL, ai_move2, -2, NULL, ai_move2, -2, NULL, ai_move2, -2, NULL, ai_move2, -2, NULL, ai_move2, -2, NULL, ai_move2, -2, weldstate }; mmove_t fixbot_move_weld_end = {FRAME_weldend_01, FRAME_weldend_07, fixbot_frames_weld_end, NULL}; void fixbot_fire_welder (edict_t *self) { vec3_t start; vec3_t forward, right, up; vec3_t end; vec3_t dir; int count = 2; vec3_t vec; float r; if (!self->enemy) return; vec[0]=24.0; vec[1]=-0.8; vec[2]=-10.0; AngleVectors (self->s.angles, forward, right, up); G_ProjectSource (self->s.origin, vec, forward, right, start); VectorCopy (self->enemy->s.origin, end); VectorSubtract (end, start, dir); gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_WELDING_SPARKS); gi.WriteByte (10); gi.WritePosition (start); gi.WriteDir (vec3_origin); gi.WriteByte (0xe0 + (rand()&7)); gi.multicast (self->s.origin, MULTICAST_PVS); if (random() > 0.8) { r = random(); if (r < 0.33) gi.sound (self, CHAN_VOICE, sound_weld1, 1, ATTN_IDLE, 0); else if (r < 0.66) gi.sound (self, CHAN_VOICE, sound_weld2, 1, ATTN_IDLE, 0); else gi.sound (self, CHAN_VOICE, sound_weld3, 1, ATTN_IDLE, 0); } } void fixbot_fire_blaster (edict_t *self) { vec3_t start; vec3_t forward, right, up; vec3_t end; vec3_t dir; if (!visible(self, self->enemy)) { self->monsterinfo.currentmove = &fixbot_move_run; } AngleVectors (self->s.angles, forward, right, up); G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_fixbot_BLASTER_1], forward, right, start); VectorCopy (self->enemy->s.origin, end); end[2] += self->enemy->viewheight; // Lazarus fog reduction of accuracy if(self->monsterinfo.visibility < FOG_CANSEEGOOD) { end[0] += crandom() * 640 * (FOG_CANSEEGOOD - self->monsterinfo.visibility); end[1] += crandom() * 640 * (FOG_CANSEEGOOD - self->monsterinfo.visibility); end[2] += crandom() * 320 * (FOG_CANSEEGOOD - self->monsterinfo.visibility); } VectorSubtract (end, start, dir); monster_fire_blaster (self, start, dir, 15, 1000, MZ2_fixbot_BLASTER_1, EF_BLASTER, BLASTER_ORANGE); } void fixbot_stand (edict_t *self) { self->monsterinfo.currentmove = &fixbot_move_stand; } void fixbot_run (edict_t *self) { if (self->monsterinfo.aiflags & AI_STAND_GROUND) self->monsterinfo.currentmove = &fixbot_move_stand; else self->monsterinfo.currentmove = &fixbot_move_run; } void fixbot_walk (edict_t *self) { vec3_t vec; int len; if (strcmp (self->goalentity->classname, "object_repair") == 0) { VectorSubtract (self->s.origin, self->goalentity->s.origin, vec); len = VectorLength (vec); if (len < 32) { self->monsterinfo.currentmove = &fixbot_move_weld_start; return; } } self->monsterinfo.currentmove = &fixbot_move_walk; } void fixbot_start_attack (edict_t *self) { self->monsterinfo.currentmove = &fixbot_move_start_attack; } void fixbot_attack(edict_t *self) { vec3_t vec; int len; if (self->monsterinfo.aiflags & AI_MEDIC) { if (!visible (self, self->goalentity)) return; VectorSubtract (self->s.origin, self->enemy->s.origin, vec); len = VectorLength(vec); if (len > 128) return; else self->monsterinfo.currentmove = &fixbot_move_laserattack; } else self->monsterinfo.currentmove = &fixbot_move_attack2; } void fixbot_pain (edict_t *self, edict_t *other, float kick, int damage) { // if (self->health < (self->max_health / 2)) // self->s.skinnum = 1; // gi.dprintf("pain\n"); if (level.time < self->pain_debounce_time) return; self->pain_debounce_time = level.time + 3; gi.sound (self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0); if (damage <= 10) self->monsterinfo.currentmove = &fixbot_move_pain3; else if (damage <= 25) self->monsterinfo.currentmove = &fixbot_move_painb; else self->monsterinfo.currentmove = &fixbot_move_paina; } void fixbot_dead (edict_t *self) { VectorSet (self->mins, -16, -16, -24); VectorSet (self->maxs, 16, 16, -8); self->movetype = MOVETYPE_TOSS; self->svflags |= SVF_DEADMONSTER; self->nextthink = 0; gi.linkentity (self); } void fixbot_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) { int n; //Knightmare added // shards #ifdef KMQUAKE2_ENGINE_MOD // use fewer unique models for stock engine for (n = 0; n < 8; n++) ThrowGib (self, "models/objects/mech_gibs/gib2.md2", 0, 0, damage, GIB_METALLIC); for (n = 0; n < 4; n++) ThrowGib (self, "models/objects/mech_gibs/gib5.md2", 0, 0, damage, GIB_METALLIC); #else for (n = 0; n < 8; n++) ThrowGib (self, "models/objects/debris1/tris.md2", 0, 0, damage, GIB_METALLIC); for (n = 0; n < 4; n++) ThrowGib (self, "models/objects/debris2/tris.md2", 0, 0, damage, GIB_METALLIC); #endif gi.sound (self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0); BecomeExplosion1(self); } /*QUAKED monster_fixbot (1 .5 0) (-32 -32 -24) (32 32 24) Ambush Trigger_Spawn Fixit Takeoff Landing */ void SP_monster_fixbot (edict_t *self) { if (deathmatch->value) { G_FreeEdict (self); return; } // Lazarus: special purpose skins if ( self->style ) { PatchMonsterModel("models/monsters/fixbot/tris.md2"); self->s.skinnum = self->style; } // precache gibs #ifdef KMQUAKE2_ENGINE_MOD // use fewer unique models for stock engine gi.modelindex ("models/objects/mech_gibs/gib2.md2"); gi.modelindex ("models/objects/mech_gibs/gib5.md2"); #else gi.modelindex ("models/objects/debris1/tris.md2"); gi.modelindex ("models/objects/debris2/tris.md2"); #endif sound_pain1 = gi.soundindex ("flyer/flypain1.wav"); sound_die = gi.soundindex ("flyer/flydeth1.wav"); sound_weld1 = gi.soundindex ("misc/welder1.wav"); sound_weld2 = gi.soundindex ("misc/welder2.wav"); sound_weld3 = gi.soundindex ("misc/welder3.wav"); self->s.modelindex = gi.modelindex ("models/monsters/fixbot/tris.md2"); // Knightmare- shrunk bbox, was (-32, -32, -24), (32, 32, 34) VectorSet (self->mins, -24, -24, -16); VectorSet (self->maxs, 24, 24, 16); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; if(!self->health) self->health = 150; if(!self->mass) self->mass = 150; self->pain = fixbot_pain; self->die = fixbot_die; self->monsterinfo.stand = fixbot_stand; self->monsterinfo.walk = fixbot_walk; self->monsterinfo.run = fixbot_run; self->monsterinfo.attack = fixbot_attack; //Knightmare - sparks, not blood, on damage if (!self->blood_type) self->blood_type = 2; //smoking, not bloody gibs // Lazarus if(self->powerarmor) { if (self->powerarmortype == 1) self->monsterinfo.power_armor_type = POWER_ARMOR_SCREEN; else self->monsterinfo.power_armor_type = POWER_ARMOR_SHIELD; self->monsterinfo.power_armor_power = self->powerarmor; } self->common_name = "Repair Bot"; gi.linkentity (self); self->monsterinfo.currentmove = &fixbot_move_stand; self->monsterinfo.scale = MODEL_SCALE; flymonster_start (self); }