void widow2_reattack_beam(edict_t *self) { if (!self) { return; } self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING; if (infront(self, self->enemy)) { if (random() <= 0.5) { if ((random() < 0.7) || (SELF_SLOTS_LEFT < 2)) { self->monsterinfo.currentmove = &widow2_move_attack_beam; } else { self->monsterinfo.currentmove = &widow2_move_spawn; } } else { self->monsterinfo.currentmove = &widow2_move_attack_post_beam; } } else { self->monsterinfo.currentmove = &widow2_move_attack_post_beam; } }
/* ================= findclosestreticle Returns the entity that matches the classname and is closest to the reticle findclosestreticle (ent, radius, classname) ================= */ edict_t *findclosestreticle (edict_t *ent, float rad, unsigned int classid) { edict_t *found = NULL, *from; float fdot = 0.8, dot; vec3_t vec, forward; for (from = g_edicts ; from < &g_edicts[globals.num_edicts]; from++) { if (!from->inuse) continue; if (from->solid == SOLID_NOT) continue; if (from->classid != classid) continue; if (!visible(ent, from) || !infront(ent, from)) continue; // is this edict "better" than the last one? AngleVectors (ent->s.angles, forward, NULL, NULL); VectorSubtract (from->s.origin, ent->s.origin, vec); VectorNormalize (vec); dot = DotProduct (vec, forward); if (dot > fdot) { fdot = dot; found = from; } } return found; }
/* * This is a support routine used when a client is firing * a non-instant attack weapon. It checks to see if a * monster's dodge function should be called. */ void check_dodge ( edict_t *self, vec3_t start, vec3_t dir, q_int32_t speed ) { vec3_t end; vec3_t v; trace_t tr; float eta; if ( !self ) { return; } /* easy mode only ducks one quarter the time */ if ( skill->value == 0 ) { if ( random() > 0.25 ) { return; } } VectorMA ( start, 8192, dir, end ); tr = gi.trace ( start, NULL, NULL, end, self, MASK_SHOT ); if ( ( tr.ent ) && ( tr.ent->svflags & SVF_MONSTER ) && ( tr.ent->health > 0 ) && ( tr.ent->monsterinfo.dodge ) && infront ( tr.ent, self ) ) { VectorSubtract ( tr.endpos, start, v ); eta = ( VectorLength ( v ) - tr.ent->maxs[0] ) / speed; tr.ent->monsterinfo.dodge ( tr.ent, self, eta ); } }
void carrier_reattack_mg(edict_t *self) { if (!self) { return; } CarrierCoopCheck(self); if (infront(self, self->enemy)) { if (random() <= 0.5) { if ((random() < 0.7) || (self->monsterinfo.monster_slots <= 2)) { self->monsterinfo.currentmove = &carrier_move_attack_mg; } else { self->monsterinfo.currentmove = &carrier_move_spawn; } } else { self->monsterinfo.currentmove = &carrier_move_attack_post_mg; } } else { self->monsterinfo.currentmove = &carrier_move_attack_post_mg; } }
/////////////////////////////////////////////////////////////////////// // Pick best goal based on importance and range. This function // overrides the long range goal selection for items that // are very close to the bot and are reachable. /////////////////////////////////////////////////////////////////////// void ACEAI_PickShortRangeGoal(edict_t *self) { edict_t *target; float weight,best_weight=0.0; edict_t *best; int index; // look for a target (should make more efficent later) target = findradius(NULL, self->s.origin, 200); while (target) { if (target->classname == NULL) return; // Missle avoidance code // Set our movetarget to be the rocket or grenade fired at us. if (strcmp(target->classname,"rocket")==0 || strcmp(target->classname,"grenade")==0 || strcmp(target->classname,"homing rocket")==0) { if(debug_mode) debug_printf("ROCKET ALERT!\n"); self->movetarget = target; return; } if (ACEIT_IsReachable(self,target->s.origin)) { if (infront(self, target)) { index = ACEIT_ClassnameToIndex(target->classname); weight = ACEIT_ItemNeed(self, index); if(weight > best_weight) { best_weight = weight; best = target; } } } // next target target = findradius(target, self->s.origin, 200); } if (best_weight) { self->movetarget = best; if(debug_mode && self->goalentity != self->movetarget) debug_printf("%s selected a %s for SR goal.\n",self->client->pers.netname, self->movetarget->classname); self->goalentity = best; } }
int CheckTarget( gedict_t * Target ) { int r; if ( tg_data.sg_allow_find == TG_SG_FIND_IGNORE_ALL ) return 0; if ( !Target ) return 0; if ( Target == self ) return 0; if ( Target->has_disconnected == 1 ) return 0; if ( !Target->s.v.takedamage ) return 0; if ( Target->is_feigning ) return 0; if ( ( int ) Target->s.v.flags & FL_NOTARGET ) return 0; if ( ( int ) Target->s.v.items & IT_INVISIBILITY ) return 0; if ( !visible( Target ) ) return 0; r = range( Target ); if ( r == 3 ) return 0; else { if ( r == 2 && !infront( Target ) ) return 0; } if ( tg_data.sg_allow_find == TG_SG_FIND_IGNORE_OFF ) return 1; if ( teamplay ) { if ( self->team_no && TeamFortress_isTeamsAllied (Target->team_no , self->team_no) ) { if ( tg_data.sg_allow_find == TG_SG_FIND_IGNORE_TEAM ) return 0; } if ( self->team_no && TeamFortress_isTeamsAllied(Target->undercover_team , self->team_no)) { if ( tg_data.sg_allow_find == TG_SG_FIND_IGNORE_TEAM ) return 0; } } if ( Target == self->real_owner ) { if ( tg_data.sg_allow_find == TG_SG_FIND_IGNORE_OWNER ) return 0; } return 1; }
void boss2_reattack_mg (edict_t *self) { if ( infront(self, self->enemy) ) if (random() <= 0.7) self->monsterinfo.currentmove = &boss2_move_attack_mg; else self->monsterinfo.currentmove = &boss2_move_attack_post_mg; else self->monsterinfo.currentmove = &boss2_move_attack_post_mg; }
void soldier_attack5_refire (edict_t *self) { if ((!self->enemy) || (self->enemy->health <= 0)) return; if (!visible(self, self->enemy) || !infront(self, self->enemy)) return; if (random() < (0.3 + self->monsterinfo.skill * 0.05)) || (range(self, self->enemy) == RANGE_MELEE) ) self->monsterinfo.nextframe = FRAME_attak505; else
void mybrain_jumpattack_landing (edict_t *self) { // expand the bbox again, we're standing up self->maxs[2] = 32; self->takedamage = DAMAGE_AIM; gi.linkentity (self); // attack right away if enemy is valid, visible, infront, and is moving if (G_EntIsAlive(self->enemy) && visible(self, self->enemy) && infront(self, self->enemy) && self->enemy->movetype) mybrain_attack3(self); }
void soldier_attack3_refire (edict_t *self) { if (!self->enemy) return; if (!visible(self, self->enemy) || !infront(self, self->enemy)) return; if ((level.time + 0.4) < self->monsterinfo.pausetime) self->monsterinfo.nextframe = FRAME_attak303; else self->monsterinfo.attack_finished = level.time + 1.0 + random(); }
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); } };
qboolean G_EmplacedGunIsMountable( gentity_t* ent, gentity_t* other ) { if ( Q_stricmp( ent->classname, "misc_mg42" ) ) { return qfalse; } if ( !other->client ) { return qfalse; } if ( BG_IsScopedWeapon( other->client->ps.weapon ) ) { return qfalse; } if ( other->client->ps.pm_flags & PMF_DUCKED ) { return qfalse; } if ( other->client->ps.persistant[PERS_HWEAPON_USE] ) { return qfalse; } if ( ent->r.currentOrigin[2] - other->r.currentOrigin[2] >= 40 ) { return qfalse; } if ( ent->r.currentOrigin[2] - other->r.currentOrigin[2] < 0 ) { return qfalse; } if ( ent->s.frame != 0 ) { return qfalse; } if ( ent->active ) { return qfalse; } if ( other->client->ps.grenadeTimeLeft ) { return qfalse; } if ( infront( ent, other ) ) { return qfalse; } return qtrue; }
void soldier_attack2_refire2 (edict_t *self) { if (self->s.skinnum < 2) return; if (!self->enemy) return; if (self->enemy->health <= 0) return; if (!visible(self, self->enemy) || !infront(self, self->enemy)) return; if ( ((self->monsterinfo.skill > 1) && (random() < 0.5)) || (range(self, self->enemy) == RANGE_MELEE) ) self->monsterinfo.attack_finished = level.time + 1.0 + random(); self->monsterinfo.nextframe = FRAME_attak204; }
void CurseRadiusAttack (edict_t *caster, int type, int range, int radius, float duration, qboolean isCurse) { edict_t *e=NULL, *f=NULL; // write a nice effect so everyone knows we've cast a spell gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_TELEPORT_EFFECT); gi.WritePosition (caster->s.origin); gi.multicast (caster->s.origin, MULTICAST_PVS); caster->client->idle_frames = 0; caster->client->ability_delay = level.time;// for monster hearing (check if ability was recently used/cast) // find a target closest to the caster's reticle while ((e = findclosestreticle(e, caster, range)) != NULL) { if (!CanCurseTarget(caster, e, type, isCurse, true)) continue; if (entdist(caster, e) > range) continue; if (!infront(caster, e)) continue; if (!curse_add(e, caster, type, 0, duration)) continue; CurseMessage(caster, e, type, duration, isCurse); // target anything in-range of this entity while ((f = findradius(f, e->s.origin, radius)) != NULL) { if (!CanCurseTarget(caster, f, type, isCurse, false)) continue; if (f == e) continue; if (!visible(e, f)) continue; if (!curse_add(f, caster, type, 0, duration)) continue; CurseMessage(caster, f, type, duration, isCurse); } break; } }
qboolean boss_findtarget (edict_t *boss) { edict_t *target = NULL; while ((target = findclosestradius(target, boss->s.origin, BOSS_TARGET_RADIUS)) != NULL) { if (!G_ValidTarget(boss, target, true)) continue; if (!infront(boss, target)) continue; boss->enemy = target; //if (target->client) // gi.dprintf("found %s\n", target->client->pers.netname); //else // gi.dprintf("found target %s\n", target->classname); // gi.sound (boss, CHAN_WEAPON, gi.soundindex ("tank/sight1.wav"), 1, ATTN_NORM, 0); return true; } return false; }
void carrier_reattack_gren(edict_t *self) { if (!self) { return; } CarrierCoopCheck(self); if (infront(self, self->enemy)) { if (self->timestamp + 1.3 > level.time) /* four grenades */ { self->monsterinfo.currentmove = &carrier_move_attack_gren; return; } } self->monsterinfo.currentmove = &carrier_move_attack_post_gren; }
bool Player::trypull( Direction dir, Map &map, vector<Movable*> &movables ) { Movable *box; if( engine.input.keys[ Key::Space ].down && direction() != dir ) { if(( box = infront( map, movables )) != NULL && (( CHAIR_TYPE <= box->type() && box->type() <= DESK_TYPE && level() > ( box->type() - CHAIR_TYPE )) || ( ZOMBIE_MALE_TYPE <= box->type() && box->type() <= ZOMBIE_FEMALE_TYPE))){ rdir = direction(); restore = true; direction( dir ); trymove( map, movables ); if( moving()) { box->direction( direction()); box->trymove( map, movables ); // test if there is a wall or block between future position if( !box->moving()) { x( rx()); y( ry()); moving( 0 ); } else { if( map( x(), y()).block & block_dir[ rdir ] || map( box->x(), box->y()).block & block_rdir[ rdir ] ) { x( rx()); y( ry()); moving( 0 ); box->moving( 0 ); box->x( box->rx()); box->y( box->ry()); } } } return true; } } return false; }
void boss2_reattack_mg(edict_t *self) { if (!self) { return; } if (infront(self, self->enemy)) { if (random() <= 0.7) { self->monsterinfo.currentmove = &boss2_move_attack_mg; } else { self->monsterinfo.currentmove = &boss2_move_attack_post_mg; } } else { self->monsterinfo.currentmove = &boss2_move_attack_post_mg; } }
qboolean Carrier_CheckAttack(edict_t *self) { vec3_t spot1, spot2; vec3_t temp; float chance = 0; trace_t tr; qboolean enemy_infront, enemy_inback, enemy_below; int enemy_range; float enemy_yaw; if (!self) { return false; } if (self->enemy->health > 0) { /* see if any entities are in the way of the shot */ VectorCopy(self->s.origin, spot1); spot1[2] += self->viewheight; VectorCopy(self->enemy->s.origin, spot2); spot2[2] += self->enemy->viewheight; tr = gi.trace(spot1, NULL, NULL, spot2, self, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_SLIME | CONTENTS_LAVA); /* do we have a clear shot? */ if (tr.ent != self->enemy) { /* go ahead and spawn stuff if we're mad a a client */ if (self->enemy->client && (self->monsterinfo.monster_slots > 2)) { self->monsterinfo.attack_state = AS_BLIND; return true; } /* we want them to go ahead and shoot at info_notnulls if they can. */ if ((self->enemy->solid != SOLID_NOT) || (tr.fraction < 1.0)) { return false; } } } enemy_infront = infront(self, self->enemy); enemy_inback = inback(self, self->enemy); enemy_below = below(self, self->enemy); enemy_range = range(self, self->enemy); VectorSubtract(self->enemy->s.origin, self->s.origin, temp); enemy_yaw = vectoyaw2(temp); self->ideal_yaw = enemy_yaw; /* shoot out the back if appropriate */ if ((enemy_inback) || (!enemy_infront && enemy_below)) { /* this is using wait because the attack is supposed to be independent */ if (level.time >= self->wait) { self->wait = level.time + CARRIER_ROCKET_TIME; self->monsterinfo.attack(self); if (random() < 0.6) { self->monsterinfo.attack_state = AS_SLIDING; } else { self->monsterinfo.attack_state = AS_STRAIGHT; } return true; } } /* melee attack */ if (enemy_range == RANGE_MELEE) { self->monsterinfo.attack_state = AS_MISSILE; return true; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { chance = 0.4; } else if (enemy_range == RANGE_MELEE) { chance = 0.8; } else if (enemy_range == RANGE_NEAR) { chance = 0.8; } else if (enemy_range == RANGE_MID) { chance = 0.8; } else if (enemy_range == RANGE_FAR) { chance = 0.5; } /* go ahead and shoot every time if it's a info_notnull */ if ((random() < chance) || (self->enemy->solid == SOLID_NOT)) { self->monsterinfo.attack_state = AS_MISSILE; return true; } if (self->flags & FL_FLY) { if (random() < 0.6) { self->monsterinfo.attack_state = AS_SLIDING; } else { self->monsterinfo.attack_state = AS_STRAIGHT; } } return false; }
void carrier_attack(edict_t *self) { vec3_t vec; float range, luck; qboolean enemy_inback, enemy_infront, enemy_below; if (!self) { return; } self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; if ((!self->enemy) || (!self->enemy->inuse)) { return; } enemy_inback = inback(self, self->enemy); enemy_infront = infront(self, self->enemy); enemy_below = below(self, self->enemy); if (self->bad_area) { if ((enemy_inback) || (enemy_below)) { self->monsterinfo.currentmove = &carrier_move_attack_rocket; } else if ((random() < 0.1) || (level.time < self->monsterinfo.attack_finished)) { self->monsterinfo.currentmove = &carrier_move_attack_pre_mg; } else { gi.sound(self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &carrier_move_attack_rail; } return; } if (self->monsterinfo.attack_state == AS_BLIND) { self->monsterinfo.currentmove = &carrier_move_spawn; return; } if (!enemy_inback && !enemy_infront && !enemy_below) /* to side and not under */ { if ((random() < 0.1) || (level.time < self->monsterinfo.attack_finished)) { self->monsterinfo.currentmove = &carrier_move_attack_pre_mg; } else { gi.sound(self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &carrier_move_attack_rail; } return; } if (enemy_infront) { VectorSubtract(self->enemy->s.origin, self->s.origin, vec); range = VectorLength(vec); if (range <= 125) { if ((random() < 0.8) || (level.time < self->monsterinfo.attack_finished)) { self->monsterinfo.currentmove = &carrier_move_attack_pre_mg; } else { gi.sound(self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &carrier_move_attack_rail; } } else if (range < 600) { luck = random(); if (self->monsterinfo.monster_slots > 2) { if (luck <= 0.20) { self->monsterinfo.currentmove = &carrier_move_attack_pre_mg; } else if (luck <= 0.40) { self->monsterinfo.currentmove = &carrier_move_attack_pre_gren; } else if ((luck <= 0.7) && !(level.time < self->monsterinfo.attack_finished)) { gi.sound(self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &carrier_move_attack_rail; } else { self->monsterinfo.currentmove = &carrier_move_spawn; } } else { if (luck <= 0.30) { self->monsterinfo.currentmove = &carrier_move_attack_pre_mg; } else if (luck <= 0.65) { self->monsterinfo.currentmove = &carrier_move_attack_pre_gren; } else if (level.time >= self->monsterinfo.attack_finished) { gi.sound(self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &carrier_move_attack_rail; } else { self->monsterinfo.currentmove = &carrier_move_attack_pre_mg; } } } else /* won't use grenades at this range */ { luck = random(); if (self->monsterinfo.monster_slots > 2) { if (luck < 0.3) { self->monsterinfo.currentmove = &carrier_move_attack_pre_mg; } else if ((luck < 0.65) && !(level.time < self->monsterinfo.attack_finished)) { gi.sound(self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0); VectorCopy(self->enemy->s.origin, self->pos1); /* save for aiming the shot */ self->pos1[2] += self->enemy->viewheight; self->monsterinfo.currentmove = &carrier_move_attack_rail; } else { self->monsterinfo.currentmove = &carrier_move_spawn; } } else { if ((luck < 0.45) || (level.time < self->monsterinfo.attack_finished)) { self->monsterinfo.currentmove = &carrier_move_attack_pre_mg; } else { gi.sound(self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &carrier_move_attack_rail; } } } } else if ((enemy_below) || (enemy_inback)) { self->monsterinfo.currentmove = &carrier_move_attack_rocket; } }
//======================================================================= void WhatIsIt (edict_t *ent) { float range; int i, num; edict_t *touch[MAX_EDICTS]; edict_t *who, *best; trace_t tr; vec3_t dir, end, entp, forward, mins, maxs, start, viewp; /* Check for looking directly at a player or other non-trigger entity */ VectorCopy(ent->s.origin, start); start[2] += ent->viewheight; AngleVectors(ent->client->v_angle, forward, NULL, NULL); VectorMA(start, 8192, forward, end); tr = gi.trace(start, NULL, NULL, end, ent, MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA); if (tr.ent > world) { if(tr.ent->common_name) ent->client->whatsit = tr.ent->common_name; // else // ent->client->whatsit = tr.ent->classname; return; } /* Check for looking directly at a pickup item */ VectorCopy(ent->s.origin,viewp); viewp[2] += ent->viewheight; AngleVectors(ent->client->v_angle, forward, NULL, NULL); VectorSet(mins,-4096,-4096,-4096); VectorSet(maxs, 4096, 4096, 4096); num = gi.BoxEdicts (mins, maxs, touch, MAX_EDICTS, AREA_TRIGGERS); best = NULL; for (i=0 ; i<num ; i++) { who = touch[i]; if (!who->inuse) continue; if (!who->item) continue; if (!visible(ent,who)) continue; if (!infront(ent,who)) continue; VectorSubtract(who->s.origin,viewp,dir); range = VectorLength(dir); VectorMA(viewp, range, forward, entp); if(entp[0] < who->s.origin[0] - 17) continue; if(entp[1] < who->s.origin[1] - 17) continue; if(entp[2] < who->s.origin[2] - 17) continue; if(entp[0] > who->s.origin[0] + 17) continue; if(entp[1] > who->s.origin[1] + 17) continue; if(entp[2] > who->s.origin[2] + 17) continue; best = who; break; } if(best) { ent->client->whatsit = best->item->pickup_name; return; } }
qboolean Makron_CheckAttack (edict_t *self) { vec3_t spot1, spot2; vec3_t temp; float chance; trace_t tr; qboolean enemy_infront; int enemy_range; float enemy_yaw; if (self->enemy->health > 0) { // see if any entities are in the way of the shot VectorCopy (self->s.origin, spot1); spot1[2] += self->viewheight; VectorCopy (self->enemy->s.origin, spot2); spot2[2] += self->enemy->viewheight; //Knightmare- don't shoot from behind a window tr = gi.trace (spot1, NULL, NULL, spot2, self, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA|CONTENTS_WINDOW); // do we have a clear shot? if (tr.ent != self->enemy) return false; } enemy_infront = infront(self, self->enemy); enemy_range = range(self, self->enemy); VectorSubtract (self->enemy->s.origin, self->s.origin, temp); enemy_yaw = vectoyaw(temp); self->ideal_yaw = enemy_yaw; // melee attack if (enemy_range == RANGE_MELEE) { if (self->monsterinfo.melee) self->monsterinfo.attack_state = AS_MELEE; else self->monsterinfo.attack_state = AS_MISSILE; return true; } // missile attack if (!self->monsterinfo.attack) return false; if (level.time < self->monsterinfo.attack_finished) return false; if (enemy_range == RANGE_FAR) return false; if (self->monsterinfo.aiflags & AI_STAND_GROUND) { chance = 0.4; } else if (enemy_range == RANGE_MELEE) { chance = 0.8; } else if (enemy_range == RANGE_NEAR) { chance = 0.4; } else if (enemy_range == RANGE_MID) { chance = 0.2; } else { return false; } if (random () < chance) { self->monsterinfo.attack_state = AS_MISSILE; self->monsterinfo.attack_finished = level.time + 2*random(); return true; } if (self->flags & FL_FLY) { if (random() < 0.3) self->monsterinfo.attack_state = AS_SLIDING; else self->monsterinfo.attack_state = AS_STRAIGHT; } return false; }
qboolean Widow2_CheckAttack (edict_t *self) { vec3_t spot1, spot2; vec3_t temp; float chance; trace_t tr; qboolean enemy_infront; int enemy_range; float enemy_yaw; float real_enemy_range; vec3_t f, r, u; if (!self->enemy) return false; WidowPowerups(self); if ((random() < 0.8) && (SELF_SLOTS_LEFT >= 2) && (realrange(self, self->enemy) > 150)) { self->monsterinfo.aiflags |= AI_BLOCKED; self->monsterinfo.attack_state = AS_MISSILE; return true; } if (self->enemy->health > 0) { // see if any entities are in the way of the shot VectorCopy (self->s.origin, spot1); spot1[2] += self->viewheight; VectorCopy (self->enemy->s.origin, spot2); spot2[2] += self->enemy->viewheight; tr = gi.trace (spot1, NULL, NULL, spot2, self, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA); // do we have a clear shot? if (tr.ent != self->enemy) { // go ahead and spawn stuff if we're mad a a client if (self->enemy->client && SELF_SLOTS_LEFT >= 2) { self->monsterinfo.attack_state = AS_BLIND; return true; } // PGM - we want them to go ahead and shoot at info_notnulls if they can. if(self->enemy->solid != SOLID_NOT || tr.fraction < 1.0) //PGM return false; } } enemy_infront = infront(self, self->enemy); enemy_range = range(self, self->enemy); VectorSubtract (self->enemy->s.origin, self->s.origin, temp); enemy_yaw = vectoyaw2(temp); self->ideal_yaw = enemy_yaw; // melee attack if (self->timestamp < level.time) { real_enemy_range = realrange (self, self->enemy); if (real_enemy_range < 300) { AngleVectors (self->s.angles, f, r, u); G_ProjectSource2 (self->s.origin, offsets[0], f, r, u, spot1); VectorCopy (self->enemy->s.origin, spot2); if (widow2_tongue_attack_ok(spot1, spot2, 256)) { // melee attack ok // be nice in easy mode if (skill->value == 0 && (rand()&3) ) return false; if (self->monsterinfo.melee) self->monsterinfo.attack_state = AS_MELEE; else self->monsterinfo.attack_state = AS_MISSILE; return true; } } } if (level.time < self->monsterinfo.attack_finished) return false; if (self->monsterinfo.aiflags & AI_STAND_GROUND) { chance = 0.4; } else if (enemy_range == RANGE_NEAR) { chance = 0.8; } else if (enemy_range == RANGE_MID) { chance = 0.8; } else if (enemy_range == RANGE_FAR) { chance = 0.5; } // PGM - go ahead and shoot every time if it's a info_notnull if ((random () < chance) || (self->enemy->solid == SOLID_NOT)) { self->monsterinfo.attack_state = AS_MISSILE; // self->monsterinfo.attack_finished = level.time + 1.0 + 2*random(); return true; } return false; }
// draws the line void Tline::draw( Tsector *owner ) { coord3d x1r,y1r,x2r,y2r; coord3d t1,t2; coord2d x1,x2,x1c,x2c; Tmonotone mp,mph; Twall **w; coord3d ox1=verts[v1].x; coord3d oy1=verts[v1].y; coord3d ox2=verts[v2].x; coord3d oy2=verts[v2].y; // if the line is behind the portal, do nothing if (!infront(ox1,oy1) && !infront(ox2,oy2)) return; coord3d _portx1=portx1; coord3d _porty1=porty1; coord3d _portdx=portdx; coord3d _portdy=portdy; rotatez(ox1,oy1,&x1r,&y1r); // find the first vertex in camera space rotatez(ox2,oy2,&x2r,&y2r); // find the second vertex in camera space coord3d orx1=x1r; coord3d orx2=x2r; // calculates the vectors for the texture mapping Tvector a={x1r,y1r,view.z}; Tvector p={512*(x2r-x1r)/len,512*(y2r-y1r)/len,0}; Tvector q={0,0,512}; rotateyxv(&a); rotateyxv(&p); rotateyxv(&q); int clipfl=0; // checks if the line is visible on the screen if (y1r<=MINZ && y2r<=MINZ) { if (y1r<0 && y2r<0) return; if (x1r<0 && x2r<0) return; if (x1r>0 && x2r>0) return; if (y1r*fabs(x2r)+y2r*fabs(x1r)<0) return; x1c=x2c=0; // if the line is too close and is visible, it fills the whole screen clipfl=clLEFT|clRIGHT; } else { // if the line crosses the near plane, clip it if (y1r<MINZ || y2r<MINZ) { if (y1r<MINZ) { clipfl=clLEFT; t1=MINZ-y1r; t2=y2r-MINZ; x1r=splitab(x1r,x2r,t1,t2); y1r=MINZ; } if (y2r<MINZ) { clipfl=clRIGHT; t1=y1r-MINZ; t2=MINZ-y2r; x2r=splitab(x1r,x2r,t1,t2); y2r=MINZ; } } x1=projectx(x1r,y1r); x2=projectx(x2r,y2r); x1c=x1; x2c=x2; if (x1c<cur_clip->xmin) { x1c=cur_clip->xmin; clipfl&=~clDRAWLEFT; } if (x2c>cur_clip->xmax) { x2c=cur_clip->xmax; clipfl&=~clDRAWRIGHT; } } // if the line is perpendicular to the screen, or backfacing if (x1c>=x2c) { if (clipfl&clDRAW) { if (clipfl&clDRAWLEFT) x1c=cur_clip->xmin; if (clipfl&clDRAWRIGHT) x2c=cur_clip->xmax; if (x1c>=x2c) return; coord3d z0=(owner->getzf(ox1,oy1)*orx2-owner->getzf(ox2,oy2)*orx1)/(orx2-orx1); w=walls; for (int wi=0;wi<wallsnum;wi++,NEXTWALL(w)) { Twall *wp=*w; coord3d z1=(wp->z1c*orx2-wp->z2c*orx1)/(orx2-orx1); if (z0<=view.z && z1>=view.z) { // if there is a visible wall, fill the whole clip with it if (is_visible(wp)) goto dr; else break; } z0=z1; } return; dr: // fills the whole clip portx1=ox1; porty1=oy1; portdx=ox2-ox1; portdy=oy2-oy1; setdrawdata(&a,&p,&q); cur_clip->cut(x1c,x2c); cur_clip->clip(x1c,x2c,gymax,gymin,gymax,gymin,&mp); (*w)->draw(&mp); deltraps(mp.traps); cur_clip->last=mp.traps=new Ttrap(x2c,gymin,gymax,gymin,gymax,0,0,NULL); cur_clip->restore(mp.traps); portx1=_portx1; porty1=_porty1; portdx=_portdx; portdy=_portdy; return; } return; } portx1=ox1; porty1=oy1; portdx=ox2-ox1; portdy=oy2-oy1; // clips the line in world space (x and y in world space are needed to calculate the correct z) if (clipfl&clCLIPLEFT) { ox1=splitab(ox1,ox2,t1,t2); oy1=splitab(oy1,oy2,t1,t2); } else if (clipfl&clCLIPRIGHT) { ox2=splitab(ox1,ox2,t1,t2); oy2=splitab(oy1,oy2,t1,t2); } coord3d zf1=owner->getzf(ox1,oy1); coord3d zf2=owner->getzf(ox2,oy2); coord3d zc1=owner->getzc(ox1,oy1); coord3d zc2=owner->getzc(ox2,oy2); // finds the screen y coordinates (the screen x-es are x1c and x2c) coord2d pf1=projectz(zf1,y1r);// floor coord2d pf2=projectz(zf2,y2r); coord2d pc1=projectz(zc1,y1r);// ceiling coord2d pc2=projectz(zc2,y2r); // interpolates the z-s if the x-es are changed by cur_clip if (x1<x1c) { pf1=splitab(pf1,pf2,x1c-x1,x2-x1c); pc1=splitab(pc1,pc2,x1c-x1,x2-x1c); } if (x2>x2c) { pf2=splitab(pf1,pf2,x2c-x1c,x2-x2c); pc2=splitab(pc1,pc2,x2c-x1c,x2-x2c); } // cuts the portion of cur_clip that is over the walls cur_clip->cut(x1c,x2c); // clips the floor polygon with cur_clip cur_clip->clip(x1c,x2c,pf1,cur_clip->ymin,pf2,cur_clip->ymin,&mp); owner->draw_floor(&mp); deltraps(mp.traps); // clips the ceiling polygon with cur_clip cur_clip->clip(x1c,x2c,cur_clip->ymax,pc1,cur_clip->ymax,pc2,&mp); owner->draw_ceiling(&mp); deltraps(mp.traps); mph.traps=NULL; int wi; w=walls; // draws all the walls for (wi=0;wi<wallsnum;wi++,NEXTWALL(w)) { Twall *wp=*w; coord3d zq1=wp->z1c; coord3d zq2=wp->z2c; // interpolates z-s if necessary if (clipfl&clCLIPLEFT) zq1=splitab(zq1,zq2,t1,t2); if (clipfl&clCLIPRIGHT) zq2=splitab(zq1,zq2,t1,t2); // project the z-s coord2d pq1=projectz(zq1,y1r); coord2d pq2=projectz(zq2,y2r); // interpolates the projection if (x1<x1c) pq1=splitab(pq1,pq2,x1c-x1,x2-x1c); if (x2>x2c) pq2=splitab(pq1,pq2,x2c-x1c,x2-x2c); if (is_visible(wp)) { // if the wall is visible (wall or portal), draw it cur_clip->clip(x1c,x2c,pq1,pf1,pq2,pf2,&mp); if ((clipfl&clDRAW)) { if ((clipfl&clDRAWLEFT) && pq1>0 && pf1<0) cur_clip->addbeg(&mp); else if ((clipfl&clDRAWRIGHT) && pq2>0 && pf2<0) cur_clip->addend(&mp); } setdrawdata(&a,&p,&q); (*w)->draw(&mp); deltraps(mp.traps); } else { // if the wall is a hole, clip it only cur_clip->clip(x1c,x2c,pq1,pf1,pq2,pf2,&mph); } pf1=pq1; pf2=pq2; } // restores cur_clip if (mph.traps) { Ttrap *t; for (t=mph.traps;t->next;t=t->next); cur_clip->last=t; } else { cur_clip->last=mph.traps=new Ttrap(x2c,gymin,gymax,gymin,gymax,0,0,NULL); } cur_clip->restore(mph.traps); portx1=_portx1; porty1=_porty1; portdx=_portdx; portdy=_portdy; }
edict_t *LookingAt(edict_t *ent, int filter, vec3_t endpos, float *range) { edict_t *who; edict_t *trigger[MAX_EDICTS]; edict_t *ignore; trace_t tr; vec_t r; vec3_t end, forward, start; vec3_t dir, entp, mins, maxs; int i, num; if(!ent->client) { if(endpos) VectorClear(endpos); if(range) *range = 0; return NULL; } VectorClear(end); if (ent->client->chasetoggle) { AngleVectors(ent->client->v_angle, forward, NULL, NULL); VectorCopy(ent->client->chasecam->s.origin,start); ignore = ent->client->chasecam; } else if(ent->client->spycam) { AngleVectors(ent->client->ps.viewangles, forward, NULL, NULL); VectorCopy(ent->s.origin,start); ignore = ent->client->spycam; } else { AngleVectors(ent->client->v_angle, forward, NULL, NULL); VectorCopy(ent->s.origin, start); start[2] += ent->viewheight; ignore = ent; } VectorMA(start, 8192, forward, end); /* First check for looking directly at a pickup item */ VectorSet(mins,-4096,-4096,-4096); VectorSet(maxs, 4096, 4096, 4096); num = gi.BoxEdicts (mins, maxs, trigger, MAX_EDICTS, AREA_TRIGGERS); for (i=0 ; i<num ; i++) { who = trigger[i]; if (!who->inuse) continue; if (!who->item) continue; if (!visible(ent,who)) continue; if (!infront(ent,who)) continue; VectorSubtract(who->s.origin,start,dir); r = VectorLength(dir); VectorMA(start, r, forward, entp); if(entp[0] < who->s.origin[0] - 17) continue; if(entp[1] < who->s.origin[1] - 17) continue; if(entp[2] < who->s.origin[2] - 17) continue; if(entp[0] > who->s.origin[0] + 17) continue; if(entp[1] > who->s.origin[1] + 17) continue; if(entp[2] > who->s.origin[2] + 17) continue; if(endpos) VectorCopy(who->s.origin,endpos); if (range) *range = r; return who; } tr = gi.trace (start, NULL, NULL, end, ignore, MASK_SHOT); if (tr.fraction == 1.0) { // too far away gi.sound (ent, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0); return NULL; } if(!tr.ent) { // no hit gi.sound (ent, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0); return NULL; } if(!tr.ent->classname) { // should never happen gi.sound (ent, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0); return NULL; } if((strstr(tr.ent->classname,"func_") != NULL) && (filter & LOOKAT_NOBRUSHMODELS)) { // don't hit on brush models gi.sound (ent, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0); return NULL; } if((Q_strcasecmp(tr.ent->classname,"worldspawn") == 0) && (filter & LOOKAT_NOWORLD)) { // world brush gi.sound (ent, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0); return NULL; } if(endpos) { endpos[0] = tr.endpos[0]; endpos[1] = tr.endpos[1]; endpos[2] = tr.endpos[2]; } if(range) { VectorSubtract(tr.endpos,start,start); *range = VectorLength(start); } return tr.ent; }
void heat_think(edict_t *self) { edict_t *target = NULL; edict_t *aquire = NULL; vec3_t vec; int len; int oldlen = 0; if (!self) { return; } VectorClear(vec); /* aquire new target */ while ((target = findradius(target, self->s.origin, 1024)) != NULL) { if (self->owner == target) { continue; } if (!(target->svflags & SVF_MONSTER)) { continue; } if (!target->client) { continue; } if (target->health <= 0) { continue; } if (!visible(self, target)) { continue; } if (!infront(self, target)) { continue; } VectorSubtract(self->s.origin, target->s.origin, vec); len = VectorLength(vec); if ((aquire == NULL) || (len < oldlen)) { aquire = target; self->target_ent = aquire; oldlen = len; } } if (aquire != NULL) { VectorSubtract(aquire->s.origin, self->s.origin, vec); vectoangles(vec, self->s.angles); VectorNormalize(vec); VectorCopy(vec, self->movedir); VectorScale(vec, 500, self->velocity); } self->nextthink = level.time + 0.1; }
/* ============= zSchoolAllVisiable Create list of monsters of the same schooling type that are ahead of you. ============== */ int zSchoolAllVisiable(edict_t *self) { int max; edict_t *head, *list; max = 0; zCreateRaduisList(self); head = self->zRaduisList; list = self; while (head) { if(strcmp(head->classname, self->classname) == 0 && (self->monsterinfo.aiflags & AI_SCHOOLING) && (head->health > 0) && (head->zDistance <= self->monsterinfo.zSchoolSightRadius) && (visible(self, head)) && (infront(self, head))) { list->zSchoolChain = head; list = head; max++; } head = head->zRaduisList; } list->zSchoolChain = NULL; return max; }
qboolean Boss2_CheckAttack (edict_t *self) { vec3_t spot1, spot2; vec3_t temp; float chance; trace_t tr; qboolean enemy_infront; int enemy_range; float enemy_yaw; if (self->enemy->health > 0) { // see if any entities are in the way of the shot VectorCopy (self->s.origin, spot1); spot1[2] += self->viewheight; VectorCopy (self->enemy->s.origin, spot2); spot2[2] += self->enemy->viewheight; tr = gi.trace (spot1, NULL, NULL, spot2, self, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA); // do we have a clear shot? if (tr.ent != self->enemy) { // PGM - we want them to go ahead and shoot at info_notnulls if they can. if(self->enemy->solid != SOLID_NOT || tr.fraction < 1.0) //PGM return false; } } enemy_infront = infront(self, self->enemy); enemy_range = range(self, self->enemy); VectorSubtract (self->enemy->s.origin, self->s.origin, temp); enemy_yaw = vectoyaw(temp); self->ideal_yaw = enemy_yaw; // melee attack if (enemy_range == RANGE_MELEE) { if (self->monsterinfo.melee) self->monsterinfo.attack_state = AS_MELEE; else self->monsterinfo.attack_state = AS_MISSILE; return true; } // missile attack if (!self->monsterinfo.attack) return false; if (level.time < self->monsterinfo.attack_finished) return false; if (enemy_range == RANGE_FAR) return false; if (self->monsterinfo.aiflags & AI_STAND_GROUND) { chance = 0.4; } else if (enemy_range == RANGE_MELEE) { chance = 0.8; } else if (enemy_range == RANGE_NEAR) { chance = 0.8; } else if (enemy_range == RANGE_MID) { chance = 0.8; } else { return false; } // PGM - go ahead and shoot every time if it's a info_notnull if ((random () < chance) || (self->enemy->solid == SOLID_NOT)) { self->monsterinfo.attack_state = AS_MISSILE; self->monsterinfo.attack_finished = level.time + 2*random(); return true; } if (self->flags & FL_FLY) { if (random() < 0.3) self->monsterinfo.attack_state = AS_SLIDING; else self->monsterinfo.attack_state = AS_STRAIGHT; } return false; }
void heat_think (edict_t *self) { edict_t *target = NULL; edict_t *aquire = NULL; vec3_t vec; vec3_t oldang; int len; int oldlen = 0; VectorClear (vec); // aquire new target while (( target = findradius (target, self->s.origin, 1024)) != NULL) { if (self->owner == target) continue; if (!target->svflags & SVF_MONSTER) continue; if (!target->client) continue; if (target->health <= 0) continue; if (!visible (self, target)) continue; // if we need to reduce the tracking cone /* { vec3_t vec; float dot; vec3_t forward; AngleVectors (self->s.angles, forward, NULL, NULL); VectorSubtract (target->s.origin, self->s.origin, vec); VectorNormalize (vec); dot = DotProduct (vec, forward); if (dot > 0.6) continue; } */ if (!infront (self, target)) continue; VectorSubtract (self->s.origin, target->s.origin, vec); len = VectorLength (vec); if (aquire == NULL || len < oldlen) { aquire = target; self->target_ent = aquire; oldlen = len; } } if (aquire != NULL) { VectorCopy (self->s.angles, oldang); VectorSubtract (aquire->s.origin, self->s.origin, vec); vectoangles (vec, self->s.angles); VectorNormalize (vec); VectorCopy (vec, self->movedir); VectorScale (vec, 500, self->velocity); } self->nextthink = level.time + 0.1; }
/* ============ AICast_Die ============ */ void AICast_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { int contents; int killer; cast_state_t *cs; qboolean nogib = qtrue; // print debugging message if ( aicast_debug.integer == 2 && attacker->s.number == 0 ) { G_Printf( "killed %s\n", self->aiName ); } cs = AICast_GetCastState( self->s.number ); if ( attacker ) { killer = attacker->s.number; } else { killer = ENTITYNUM_WORLD; } // record the sighting (FIXME: silent weapons shouldn't do this, but the AI should react in some way) if ( attacker->client ) { AICast_UpdateVisibility( self, attacker, qtrue, qtrue ); } // the zombie should show special effect instead of gibbing if ( self->aiCharacter == AICHAR_ZOMBIE && cs->secondDeadTime ) { if ( cs->secondDeadTime > 1 ) { // we are already totally dead self->health += damage; // don't drop below gib_health if we weren't already below it return; } /* if (!cs->rebirthTime) { self->health = -999; damage = 999; } else if ( self->health >= GIB_HEALTH ) { // while waiting for rebirth, we only "die" if we drop below gib health return; } */ // always gib self->health = -999; damage = 999; } // Zombies are very fragile against highly explosives if ( self->aiCharacter == AICHAR_ZOMBIE && damage > 20 && inflictor != attacker ) { self->health = -999; damage = 999; } // process the event if ( self->client->ps.pm_type == PM_DEAD ) { // already dead if ( self->health < GIB_HEALTH ) { if ( self->aiCharacter == AICHAR_ZOMBIE ) { // RF, changed this so Zombies always gib now GibEntity( self, killer ); nogib = qfalse; /* // Zombie has special exploding cloud effect if (attacker != inflictor || attacker->s.weapon == WP_VENOM) { GibEntity( self, killer ); nogib = qfalse; } else { // Zombie will decompose upon dying self->client->ps.eFlags |= EF_MONSTER_EFFECT2; self->s.effect2Time = level.time+200; self->health = -1; } */ self->takedamage = qfalse; self->r.contents = 0; cs->secondDeadTime = 2; cs->rebirthTime = 0; cs->revivingTime = 0; } else { body_die( self, inflictor, attacker, damage, meansOfDeath ); return; } } } else { // this is our first death, so set everything up if ( level.intermissiontime ) { return; } self->client->ps.pm_type = PM_DEAD; self->enemy = attacker; // drop a weapon? // if client is in a nodrop area, don't drop anything contents = trap_PointContents( self->r.currentOrigin, -1 ); if ( !( contents & CONTENTS_NODROP ) ) { TossClientItems( self ); } // make sure the client doesn't forget about this entity until it's set to "dead" frame // otherwise it might replay it's death animation if it goes out and into client view self->r.svFlags |= SVF_BROADCAST; self->takedamage = qtrue; // can still be gibbed self->s.weapon = WP_NONE; self->s.powerups = 0; self->r.contents = CONTENTS_CORPSE; self->s.angles[0] = 0; self->s.angles[1] = self->client->ps.viewangles[1]; self->s.angles[2] = 0; VectorCopy( self->s.angles, self->client->ps.viewangles ); self->s.loopSound = 0; self->r.maxs[2] = -8; self->client->ps.maxs[2] = self->r.maxs[2]; // remove powerups memset( self->client->ps.powerups, 0, sizeof( self->client->ps.powerups ) ); //cs->rebirthTime = 0; // never gib in a nodrop if ( self->health <= GIB_HEALTH ) { if ( self->aiCharacter == AICHAR_ZOMBIE ) { // RF, changed this so Zombies always gib now GibEntity( self, killer ); nogib = qfalse; /* // Zombie has special exploding cloud effect if (attacker != inflictor || attacker->s.weapon == WP_VENOM) { GibEntity( self, killer ); nogib = qfalse; self->takedamage = qfalse; self->r.contents = 0; cs->secondDeadTime = 2; } else { self->client->ps.eFlags |= EF_MONSTER_EFFECT2; self->s.effect2Time = level.time+200; self->takedamage = qfalse; self->r.contents = 0; self->health = -1; cs->secondDeadTime = 2; } */ } else if ( !( contents & CONTENTS_NODROP ) ) { body_die( self, inflictor, attacker, damage, meansOfDeath ); //GibEntity( self, killer ); nogib = qfalse; } } // if we are a zombie, and lying down during our first death, then we should just die if ( !( self->aiCharacter == AICHAR_ZOMBIE && cs->secondDeadTime && cs->rebirthTime ) ) { // set enemy weapon BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_WEAPON, 0, qfalse ); if ( attacker->client ) { BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_WEAPON, inflictor->s.weapon, qtrue ); } else { BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_WEAPON, 0, qfalse ); } // set enemy location BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_POSITION, 0, qfalse ); if ( infront( self, inflictor ) ) { BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_POSITION, POSITION_INFRONT, qtrue ); } else { BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_POSITION, POSITION_BEHIND, qtrue ); } // play the animation BG_AnimScriptEvent( &self->client->ps, ANIM_ET_DEATH, qfalse, qtrue ); // set this flag so no other anims override us self->client->ps.eFlags |= EF_DEAD; self->s.eFlags |= EF_DEAD; } } if ( nogib ) { // set for rebirth if ( self->aiCharacter == AICHAR_ZOMBIE ) { if ( !cs->secondDeadTime ) { cs->rebirthTime = level.time + 5000 + rand() % 2000; cs->secondDeadTime = qtrue; cs->revivingTime = 0; } else if ( cs->secondDeadTime > 1 ) { cs->rebirthTime = 0; cs->revivingTime = 0; cs->deathTime = level.time; } } else { // the body can still be gibbed self->die = body_die; } } trap_LinkEntity( self ); // mark the time of death cs->deathTime = level.time; // dying ai's can trigger a target if ( !cs->rebirthTime ) { G_UseTargets( self, self ); // really dead now, so call the script AICast_ScriptEvent( cs, "death", "" ); // call the deathfunc for this cast, so we can play associated sounds, or do any character-specific things if ( !( cs->aiFlags & AIFL_DENYACTION ) && cs->deathfunc ) { cs->deathfunc( self, attacker, damage, meansOfDeath ); //----(SA) added mod } } else { // really dead now, so call the script AICast_ScriptEvent( cs, "fakedeath", "" ); // call the deathfunc for this cast, so we can play associated sounds, or do any character-specific things if ( !( cs->aiFlags & AIFL_DENYACTION ) && cs->deathfunc ) { cs->deathfunc( self, attacker, damage, meansOfDeath ); //----(SA) added mod } } }