void parasite_drain_attack (edict_t *self) { vec3_t offset, start, f, r, end, dir; trace_t tr; int damage; AngleVectors (self->s.angles, f, r, NULL); VectorSet (offset, 24, 0, 6); G_ProjectSource (self->s.origin, offset, f, r, start); VectorCopy (self->enemy->s.origin, end); if (!parasite_drain_attack_ok(start, end)) { end[2] = self->enemy->s.origin[2] + self->enemy->maxs[2] - 8; if (!parasite_drain_attack_ok(start, end)) { end[2] = self->enemy->s.origin[2] + self->enemy->mins[2] + 8; if (!parasite_drain_attack_ok(start, end)) return; } } VectorCopy (self->enemy->s.origin, end); tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT); if (tr.ent != self->enemy) return; if (self->s.frame == FRAME_drain03) { damage = 5; gi.sound (self->enemy, CHAN_AUTO, sound_impact, 1, ATTN_NORM, 0); } else { if (self->s.frame == FRAME_drain04) gi.sound (self, CHAN_WEAPON, sound_suck, 1, ATTN_NORM, 0); damage = 2; } gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_PARASITE_ATTACK); gi.WriteShort (self - g_edicts); gi.WritePosition (start); gi.WritePosition (end); gi.multicast (self->s.origin, MULTICAST_PVS); VectorSubtract (start, end, dir); T_Damage (self->enemy, self, self, dir, self->enemy->s.origin, vec3_origin, damage, 0, DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN); }
/* * ===================== * check_shot_blocked * * chance_attack: 0-1, probability that monster will attack if it has a clear shot * ===================== */ qboolean check_shot_blocked(edict_t *monster, float chance_attack) { // only check for players, and random check if (!monster->enemy || !monster->enemy->client || (random() < chance_attack)) { return false; } // special case for parasite if (strcmp(monster->classname, "monster_parasite") == 0) { trace_t trace; vec3_t forward, right, start, end, probeOffset; int i; VectorSet(probeOffset, 24, 0, 6); VectorCopy(monster->enemy->s.origin, end); AngleVectors(monster->s.angles, forward, right, NULL); G_ProjectSource(monster->s.origin, probeOffset, forward, right, start); for (i = 0; i < 3; i++) { switch (i) { case 0: break; case 1: end[2] = monster->enemy->s.origin[2] + monster->enemy->maxs[2] - 8; break; case 2: end[2] = monster->enemy->s.origin[2] + monster->enemy->mins[2] + 8; break; } if (parasite_drain_attack_ok(start, end)) { break; } if (i == 2) { return false; } } VectorCopy(monster->enemy->s.origin, end); trace = gi.trace(start, NULL, NULL, end, monster, MASK_SHOT); if (trace.ent != monster->enemy) { monster->monsterinfo.aiflags |= AI_BLOCKED; if (monster->monsterinfo.attack) { monster->monsterinfo.attack(monster); } monster->monsterinfo.aiflags &= ~AI_BLOCKED; return true; } } return false; }
qboolean parasite_checkattack (edict_t *self) { vec3_t f, r, offset, start, end; trace_t tr; qboolean retval; retval = M_CheckAttack (self); if (!retval) return false; AngleVectors (self->s.angles, f, r, NULL); VectorSet (offset, 24, 0, 6); G_ProjectSource (self->s.origin, offset, f, r, start); VectorCopy (self->enemy->s.origin, end); if (!parasite_drain_attack_ok(start, end)) { end[2] = self->enemy->s.origin[2] + self->enemy->maxs[2] - 8; if (!parasite_drain_attack_ok(start, end)) { end[2] = self->enemy->s.origin[2] + self->enemy->mins[2] + 8; if (!parasite_drain_attack_ok(start, end)) return false; } } VectorCopy (self->enemy->s.origin, end); tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT); if (tr.ent != self->enemy) { self->monsterinfo.aiflags |= AI_BLOCKED; if(self->monsterinfo.attack) self->monsterinfo.attack(self); self->monsterinfo.aiflags &= ~AI_BLOCKED; return true; } return true; // Knightmare- warning fix }