void flyer_kamikaze_check(edict_t *self) { float dist; if (!self) { return; } /* this needed because we could have gone away before we get here (blocked code) */ if (!self->inuse) { return; } if ((!self->enemy) || (!self->enemy->inuse)) { flyer_kamikaze_explode(self); return; } self->goalentity = self->enemy; dist = realrange(self, self->enemy); if (dist < 90) { flyer_kamikaze_explode(self); } }
void flyer_kamikaze_check(edict_t *self) { // PMM - this needed because we could have gone away before we get here (blocked code) if (!self->inuse) return; if (!self->enemy || !self->enemy->inuse || VectorCompare(self->s.old_origin, self->s.origin)) //mxd. Also explode when stuck... { flyer_kamikaze_explode(self); return; } self->goalentity = self->enemy; self->s.effects |= EF_ROCKET; VectorCopy(self->s.origin, self->s.old_origin); //mxd. Store old position... //mxd. Shrink bounding box to reduce the chance of getting STUK... if (self->maxs[0] != 8) { VectorSet(self->mins, -8, -8, -8); VectorSet(self->maxs, 8, 8, 8); gi.linkentity(self); } const float dist = realrange(self, self->enemy); if (dist < 90) flyer_kamikaze_explode(self); else if (dist < 128 || (level.framenum % 2 == 0)) //mxd. Play beep sound gi.sound(self, CHAN_VOICE, sound_suicide_beep, 1, ATTN_NORM, 0); }
/* ---------------------------------------------------------------------------- function: myexp_() author: Andrew Cave creation date: 09-Feb-1987 last modification: ##-###-#### arguments: none . description: See PostScript reference manual page 223. ---------------------------------------------------------------------------- */ Bool myexp_(ps_context_t *pscontext) { SYSTEMVALUE args[ 2 ] ; SYSTEMVALUE result ; UNUSED_PARAM(ps_context_t *, pscontext) ; if ( ! stack_get_numeric(&operandstack, args, 2) ) return FALSE ; if (( args[ 0 ] == OINFINITY_VALUE ) || ( args[ 1 ] == OINFINITY_VALUE )) return TRUE ; if (( args[ 0 ] < 0.0 ) && ( args[ 1 ] - (( int32 ) args[ 1 ] ) != 0.0 )) return error_handler( UNDEFINEDRESULT ) ; result = ( SYSTEMVALUE )pow(( double ) args[ 0 ] , ( double ) args[ 1 ] ) ; if ( ! realrange( result )) return error_handler( RANGECHECK ) ; if ( ! realprecision( result )) result = 0.0 ; npop( 2 , & operandstack ) ; return stack_push_real( result, &operandstack ) ; }
edict_t *medic_FindDeadMonster (edict_t *self) { float radius; edict_t *ent = NULL; edict_t *best = NULL; if (self->monsterinfo.aiflags & AI_STAND_GROUND) radius = MEDIC_MAX_HEAL_DISTANCE; else radius = 1024; while ((ent = findradius(ent, self->s.origin, radius)) != NULL) { if (ent == self) continue; if (!(ent->svflags & SVF_MONSTER)) continue; if (ent->monsterinfo.aiflags & AI_GOOD_GUY) continue; // check to make sure we haven't bailed on this guy already if ((ent->monsterinfo.badMedic1 == self) || (ent->monsterinfo.badMedic2 == self)) continue; if (ent->monsterinfo.healer) // FIXME - this is correcting a bug that is somewhere else // if the healer is a monster, and it's in medic mode .. continue .. otherwise // we will override the healer, if it passes all the other tests if ((ent->monsterinfo.healer->inuse) && (ent->monsterinfo.healer->health > 0) && (ent->monsterinfo.healer->svflags & SVF_MONSTER) && (ent->monsterinfo.healer->monsterinfo.aiflags & AI_MEDIC)) continue; if (ent->health > 0) continue; if ((ent->nextthink) && !((ent->think == M_FliesOn) || (ent->think == M_FliesOff))) continue; if (!visible(self, ent)) // if (!canReach(self, ent)) continue; if (!strncmp(ent->classname, "player", 6)) // stop it from trying to heal player_noise entities continue; // FIXME - there's got to be a better way .. // make sure we don't spawn people right on top of us if (realrange(self, ent) <= MEDIC_MIN_DISTANCE) continue; if (!best) { best = ent; continue; } if (ent->max_health <= best->max_health) continue; best = ent; } if (best) self->timestamp = level.time + MEDIC_TRY_TIME; return best; }
/* ---------------------------------------------------------------------- */ Bool xforr(ps_context_t *pscontext) { register OBJECT *theo , *other ; SYSTEMVALUE inc , current , limit ; UNUSED_PARAM(ps_context_t *, pscontext) ; theo = stackindex( 2 , & executionstack ) ; if ( oType(*theo) == OREAL ) limit = oReal(*theo) ; else limit = OINFINITY_VALUE ; theo = stackindex( 4 , & executionstack ) ; if ( oType(*theo) == OREAL ) current = oReal(*theo) ; else current = OINFINITY_VALUE ; other = stackindex( 3 , & executionstack ) ; if ( oType(*other) == OREAL ) inc = oReal(*other) ; else inc = OINFINITY_VALUE ; if (( inc > 0.0 ) ? ( current > limit ) : ( current < limit )) { npop( 5 , & executionstack ) ; return TRUE ; } if ( ! stack_push_real( current, &operandstack )) return FALSE ; current += inc ; if ( realrange( current )) { if ( ! realprecision( current )) oReal(*theo) = 0.0f ; else oReal(*theo) = ( USERVALUE )current ; } else theTags(*theo) = OINFINITY | LITERAL ; theo = stackindex( 1 , & executionstack ) ; if ( oExecutable(*theo) ) return push( theo , & executionstack ) ; else return push( theo , & operandstack ) ; }
void widow_attack (edict_t *self) { float luck; qboolean rail_frames = false, blaster_frames = false, blocked = false, anger = false; self->movetarget = NULL; if (self->monsterinfo.aiflags & AI_BLOCKED) { blocked = true; self->monsterinfo.aiflags &= ~AI_BLOCKED; } if (self->monsterinfo.aiflags & AI_TARGET_ANGER) { anger = true; self->monsterinfo.aiflags &= ~AI_TARGET_ANGER; } if ((!self->enemy) || (!self->enemy->inuse)) return; if (self->bad_area) { if ((random() < 0.1) || (level.time < self->timestamp)) self->monsterinfo.currentmove = &widow_move_attack_pre_blaster; else { gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &widow_move_attack_pre_rail; } return; } if ((self->s.frame == FRAME_walk13) || ((self->s.frame >= FRAME_walk01) && (self->s.frame <= FRAME_walk03))) rail_frames = true; if ((self->s.frame >= FRAME_walk09) && (self->s.frame <= FRAME_walk12)) blaster_frames = true; WidowCalcSlots(self); // if we can't see the target, spawn stuff regardless of frame if ((self->monsterinfo.attack_state == AS_BLIND) && (SELF_SLOTS_LEFT >= 2)) { self->monsterinfo.currentmove = &widow_move_spawn; return; } // accept bias towards spawning regardless of frame if (blocked && (SELF_SLOTS_LEFT >= 2)) { self->monsterinfo.currentmove = &widow_move_spawn; return; } if ((realrange(self, self->enemy) > 300) && (!anger) && (random() < 0.5) && (!blocked)) { self->monsterinfo.currentmove = &widow_move_run_attack; return; } if (blaster_frames) { if (SELF_SLOTS_LEFT >= 2) { self->monsterinfo.currentmove = &widow_move_spawn; return; } else if (self->monsterinfo.pausetime + BLASTER_TIME <= level.time) { self->monsterinfo.currentmove = &widow_move_attack_pre_blaster; return; } } if (rail_frames) { if (!(level.time < self->timestamp)) { gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &widow_move_attack_pre_rail; } } if ((rail_frames) || (blaster_frames)) return; luck = random(); if (SELF_SLOTS_LEFT >= 2) { if ((luck <= 0.40) && (self->monsterinfo.pausetime + BLASTER_TIME <= level.time)) self->monsterinfo.currentmove = &widow_move_attack_pre_blaster; else if ((luck <= 0.7) && !(level.time < self->timestamp)) { gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &widow_move_attack_pre_rail; } else self->monsterinfo.currentmove = &widow_move_spawn; } else { if (level.time < self->timestamp) self->monsterinfo.currentmove = &widow_move_attack_pre_blaster; else if ((luck <= 0.50) || (level.time + BLASTER_TIME >= self->monsterinfo.pausetime)) { gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &widow_move_attack_pre_rail; } else // holdout to blaster self->monsterinfo.currentmove = &widow_move_attack_pre_blaster; } }
qboolean Widow_CheckAttack (edict_t *self) { vec3_t spot1, spot2; vec3_t temp; float chance = 0; trace_t tr; int enemy_range; float enemy_yaw; float real_enemy_range; if (!self->enemy) return false; WidowPowerups(self); if (self->monsterinfo.currentmove == &widow_move_run) { // if we're in run, make sure we're in a good frame for attacking before doing anything else // frames 1,2,3,9,10,11,13 good to fire switch (self->s.frame) { case FRAME_walk04: case FRAME_walk05: case FRAME_walk06: case FRAME_walk07: case FRAME_walk08: case FRAME_walk12: { return false; } default: break; } } // give a LARGE bias to spawning things when we have room // use AI_BLOCKED as a signal to attack to spawn 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_range = range(self, self->enemy); VectorSubtract (self->enemy->s.origin, self->s.origin, temp); enemy_yaw = vectoyaw2(temp); self->ideal_yaw = enemy_yaw; real_enemy_range = realrange (self, self->enemy); if (real_enemy_range <= (MELEE_DISTANCE+20)) { // don't always melee 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_MELEE) { chance = 0.8; } else if (enemy_range == RANGE_NEAR) { chance = 0.7; } else if (enemy_range == RANGE_MID) { chance = 0.6; } 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; return true; } return false; }
qboolean medic_checkattack (edict_t *self) { if (self->monsterinfo.aiflags & AI_MEDIC) { // if our target went away if ((!self->enemy) || (!self->enemy->inuse)) { // if (g_showlogic && g_showlogic->value) // gi.dprintf ("aborting heal target due to gib\n"); abortHeal (self, true, false, false); return false; } // if we ran out of time, give up if (self->timestamp < level.time) { // if (g_showlogic && g_showlogic->value) // gi.dprintf ("aborting heal target (%s) due to time\n", self->enemy->classname); abortHeal (self, true, false, true); self->timestamp = 0; return false; } if (realrange(self, self->enemy) < MEDIC_MAX_HEAL_DISTANCE+10) { medic_attack(self); return true; } else { self->monsterinfo.attack_state = AS_STRAIGHT; return false; } } if (self->enemy->client && !visible (self, self->enemy) && (self->monsterinfo.monster_slots > 2)) { self->monsterinfo.attack_state = AS_BLIND; return true; } // give a LARGE bias to spawning things when we have room // use AI_BLOCKED as a signal to attack to spawn if ((random() < 0.8) && (self->monsterinfo.monster_slots > 5) && (realrange(self, self->enemy) > 150)) { self->monsterinfo.aiflags |= AI_BLOCKED; self->monsterinfo.attack_state = AS_MISSILE; return true; } // ROGUE // since his idle animation looks kinda bad in combat, if we're not in easy mode, always attack // when he's on a combat point if (skill->value > 0) if (self->monsterinfo.aiflags & AI_STAND_GROUND) { self->monsterinfo.attack_state = AS_MISSILE; return true; } return M_CheckAttack (self); }
void T_RadiusNukeDamage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod) { float points; edict_t *ent = NULL; vec3_t v; vec3_t dir; float len; float killzone, killzone2; trace_t tr; float dist; killzone = radius; killzone2 = radius*2.0; while ((ent = findradius(ent, inflictor->s.origin, killzone2)) != NULL) { // ignore nobody if (ent == ignore) continue; if (!ent->takedamage) continue; if (!ent->inuse) continue; if (!(ent->client || (ent->svflags & SVF_MONSTER) || (ent->svflags & SVF_DAMAGEABLE))) continue; VectorAdd (ent->mins, ent->maxs, v); VectorMA (ent->s.origin, 0.5, v, v); VectorSubtract (inflictor->s.origin, v, v); len = VectorLength(v); if (len <= killzone) { if (ent->client) ent->flags |= FL_NOGIB; points = 10000; } else if (len <= killzone2) points = (damage/killzone)*(killzone2 - len); else points = 0; if (points > 0) { if (ent->client) ent->client->nuke_framenum = level.framenum + 20; VectorSubtract (ent->s.origin, inflictor->s.origin, dir); T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod); } } ent = g_edicts+1; // skip the worldspawn // cycle through players while (ent) { if ((ent->client) && (ent->client->nuke_framenum != level.framenum+20) && (ent->inuse)) { tr = gi.trace (inflictor->s.origin, NULL, NULL, ent->s.origin, inflictor, MASK_SOLID); if (tr.fraction == 1.0) { ent->client->nuke_framenum = level.framenum + 20; } else { dist = realrange (ent, inflictor); if (dist < 2048) ent->client->nuke_framenum = max(ent->client->nuke_framenum,level.framenum + 15); else ent->client->nuke_framenum = max(ent->client->nuke_framenum,level.framenum + 10); } ent++; } else ent = NULL; } }
qboolean medic_checkattack (edict_t *self) { if (!(self->monsterinfo.aiflags & AI_MEDIC)) { if( medic_FindDeadMonster(self) ) return false; } if (self->monsterinfo.aiflags & AI_MEDIC) { float r; vec3_t forward, right, offset, start; trace_t tr; // if we have 5 seconds or less before a timeout, // look for a hint_path to the target if ( (self->timestamp < level.time + 5) && (self->monsterinfo.last_hint_time + 5 < level.time) ) { // check for hint_paths. self->monsterinfo.last_hint_time = level.time; if (hintcheck_monsterlost(self)) { if(developer->value) gi.dprintf("medic at %s using hint_paths to find %s\n", vtos(self->s.origin), self->enemy->classname); self->timestamp = level.time + MEDIC_TRY_TIME; return false; } } // if we ran out of time, give up if (self->timestamp < level.time) { //if(developer->value) // gi.dprintf("medic at %s timed out, abort heal\n",vtos(self->s.origin)); abortHeal (self, true); self->timestamp = 0; return false; } // if our target went away if ((!self->enemy) || (!self->enemy->inuse)) { abortHeal (self,false); return false; } // if target is embedded in a solid if (embedded(self->enemy)) { abortHeal (self,false); return false; } r = realrange(self,self->enemy); if (r > MEDIC_MAX_HEAL_DISTANCE+10) { self->monsterinfo.attack_state = AS_STRAIGHT; //abortHeal(self,false); return false; } else if(r < MEDIC_MIN_DISTANCE) { abortHeal(self,false); return false; } // Lazarus 1.6.2.3: if point-to-point vector from cable to // target is blocked by a solid AngleVectors (self->s.angles, forward, right, NULL); // Offset [8] has the largest displacement to the left... not a sure // thing but this one should be the most severe test. VectorCopy (medic_cable_offsets[8], offset); G_ProjectSource (self->s.origin, offset, forward, right, start); tr = gi.trace(start,NULL,NULL,self->enemy->s.origin,self,MASK_SHOT|MASK_WATER); if (tr.fraction < 1.0 && tr.ent != self->enemy) return false; medic_attack(self); return true; } // Lazarus: NEVER attack other monsters if ((self->enemy) && (self->enemy->svflags & SVF_MONSTER)) { self->enemy = self->oldenemy; self->oldenemy = NULL; if(self->enemy && self->enemy->inuse) { if(visible(self,self->enemy)) FoundTarget(self); else HuntTarget(self); } return false; } return M_CheckAttack (self); }
qboolean hintcheck_monsterlost (edict_t *monster) { edict_t *ent; edict_t *monster_node_list, *target_node_list, *prev_node; edict_t *closest_node, *start_node, *dest_node; int i; float node_dist, closest_dist; qboolean monster_pathchains_listed[MAX_HINT_CHAINS], target_pathchains_listed[MAX_HINT_CHAINS]; qboolean nodes_found = false; // If monster is standing at post, has no enemy, // or there are no hint_path chains in this map, get outta here. if ((monster->monsterinfo.aiflags & AI_STAND_GROUND) || !monster->enemy || !hint_chains_exist) return false; monster_node_list = NULL; // Create linked list of all hint_path nodes in level for (i=0; i < hint_chain_count; i++) { ent = hint_chain_starts[i]; while (ent) { // Clean up previous monster_hint_chain pointers if (ent->monster_hint_chain) ent->monster_hint_chain = NULL; if (!monster_node_list) // add first node { monster_node_list = ent; prev_node = ent; } else { prev_node->monster_hint_chain = ent; prev_node = ent; } ent = ent->hint_chain; } } // Remove inaccessible to monster nodes from monster_node_list linked list. ent = monster_node_list; prev_node = NULL; while (ent) { node_dist = realrange (monster, ent); if (node_dist > HINT_NODE_RANGE || !visible(monster, ent)) { if (!prev_node) { edict_t *temp = ent; ent = ent->monster_hint_chain; temp->monster_hint_chain = NULL; // Since there is no previous valid node, move start pointer up to our new position. monster_node_list = ent; continue; } else { prev_node->monster_hint_chain = ent->monster_hint_chain; ent->monster_hint_chain = NULL; ent = prev_node->monster_hint_chain; continue; } } nodes_found = true; prev_node = ent; ent = ent->monster_hint_chain; } // If no hint_path nodes are accessible to the monster, get outta here. if (!nodes_found) return false; /* We now have a linked list of all hint_path nodes accessible to the monster. The next step is to create a list of pathchains that the monster can access. */ // Go through all monster- accessible nodes to see which pathchains have nodes in the linked list. for (i=0; i < hint_chain_count; i++) monster_pathchains_listed[i] = false; ent = monster_node_list; while (ent) { // catch errors if ((ent->hint_chain_id < 0) || (ent->hint_chain_id > hint_chain_count)) return false; monster_pathchains_listed[ent->hint_chain_id] = true; ent = ent->monster_hint_chain; } // Build a linked list of all nodes in the pathchains accessible to the monster. // This will be used to find nodes that are accessible to the monster's enemy. target_node_list = NULL; prev_node = NULL; for (i=0; i < hint_chain_count; i++) { if (monster_pathchains_listed[i]) // If pathchain is listed, add it to our new linked list. { ent = hint_chain_starts[i]; while (ent) { if (!target_node_list) // add first node { target_node_list = ent; prev_node = ent; } else { prev_node->target_hint_chain = ent; prev_node = ent; } ent = ent->hint_chain; } } } // Remove inaccessible to monster's enemy nodes from target_node_list linked list. ent = target_node_list; prev_node = NULL; nodes_found = false; while (ent) { node_dist = realrange (monster->enemy, ent); if (node_dist > HINT_NODE_RANGE || !visible(monster->enemy, ent)) { if (!prev_node) { edict_t *temp = ent; ent = ent->target_hint_chain; temp->target_hint_chain = NULL; // Since there is no previous valid node, move start pointer up to our new position. target_node_list = ent; continue; } else { prev_node->target_hint_chain = ent->target_hint_chain; ent->target_hint_chain = NULL; ent = prev_node->target_hint_chain; continue; } } nodes_found = true; prev_node = ent; ent = ent->target_hint_chain; } // If no hint_path nodes would bring us to our enemy, get outta here. if (!nodes_found) return false; /* We now have two linked lists- one of hint_path nodes accessible to the monster, and one of hint_path nodes near the monster's enemy that are accessible from nodes near the monster. The next step is to find the closest node near the monster that will lead it to its enemy, */ // Go through all monster's enemy-accessible nodes to see which pathchains have nodes in the linked list. for (i=0; i < hint_chain_count; i++) target_pathchains_listed[i] = false; ent = target_node_list; while (ent) { // catch errors if ((ent->hint_chain_id < 0) || (ent->hint_chain_id > hint_chain_count)) return false; target_pathchains_listed[ent->hint_chain_id] = true; ent = ent->target_hint_chain; } // Go through monster_node_list linked list to find the closest node to us // that leads to the monster's enemy (on the list). closest_dist = HINT_NODE_RANGE; closest_node = NULL; ent = monster_node_list; while (ent) { if (target_pathchains_listed[ent->hint_chain_id]) { node_dist = realrange(monster, ent); if (node_dist < closest_dist) { closest_node = ent; closest_dist = node_dist; } } ent = ent->monster_hint_chain; } // If there are no nodes close enough that take us to our enemy, get outta here. if (!closest_node) return false; start_node = closest_node; // Finally we go through target_node_list linked list to find the node closest to // our target that is on the pathchain the monster will be using. closest_dist = HINT_NODE_RANGE; closest_node = NULL; ent = target_node_list; while (ent) { if (ent->hint_chain_id == start_node->hint_chain_id) { node_dist = realrange(monster->enemy, ent); if (node_dist < closest_dist) { closest_node = ent; closest_dist = node_dist; } } ent = ent->target_hint_chain; } // If there is no node close enough to our enemy, get outta here. if (!closest_node) return false; dest_node = closest_node; monster->monsterinfo.goal_hint = dest_node; hintpath_start (monster, start_node); return true; }
void widow2_attack(edict_t *self) { float range, luck; qboolean blocked = false; if (!self) { return; } if (self->monsterinfo.aiflags & AI_BLOCKED) { blocked = true; self->monsterinfo.aiflags &= ~AI_BLOCKED; } if (!self->enemy) { return; } if (self->bad_area) { if ((random() < 0.75) || (level.time < self->monsterinfo.attack_finished)) { self->monsterinfo.currentmove = &widow2_move_attack_pre_beam; } else { self->monsterinfo.currentmove = &widow2_move_attack_disrupt; } return; } WidowCalcSlots(self); /* if we can't see the target, spawn stuff */ if ((self->monsterinfo.attack_state == AS_BLIND) && (SELF_SLOTS_LEFT >= 2)) { self->monsterinfo.currentmove = &widow2_move_spawn; return; } /* accept bias towards spawning */ if (blocked && (SELF_SLOTS_LEFT >= 2)) { self->monsterinfo.currentmove = &widow2_move_spawn; return; } range = realrange(self, self->enemy); if (range < 600) { luck = random(); if (SELF_SLOTS_LEFT >= 2) { if (luck <= 0.40) { self->monsterinfo.currentmove = &widow2_move_attack_pre_beam; } else if ((luck <= 0.7) && !(level.time < self->monsterinfo.attack_finished)) { self->monsterinfo.currentmove = &widow2_move_attack_disrupt; } else { self->monsterinfo.currentmove = &widow2_move_spawn; } } else { if ((luck <= 0.50) || (level.time < self->monsterinfo.attack_finished)) { self->monsterinfo.currentmove = &widow2_move_attack_pre_beam; } else { self->monsterinfo.currentmove = &widow2_move_attack_disrupt; } } } else { luck = random(); if (SELF_SLOTS_LEFT >= 2) { if (luck < 0.3) { self->monsterinfo.currentmove = &widow2_move_attack_pre_beam; } else if ((luck < 0.65) || (level.time < self->monsterinfo.attack_finished)) { self->monsterinfo.currentmove = &widow2_move_spawn; } else { self->monsterinfo.currentmove = &widow2_move_attack_disrupt; } } else { if ((luck < 0.45) || (level.time < self->monsterinfo.attack_finished)) { self->monsterinfo.currentmove = &widow2_move_attack_pre_beam; } else { self->monsterinfo.currentmove = &widow2_move_attack_disrupt; } } } }
qboolean Widow2_CheckAttack(edict_t *self) { vec3_t spot1, spot2; vec3_t temp; float chance = 0; trace_t tr; int enemy_range; float enemy_yaw; float real_enemy_range; vec3_t f, r, u; if (!self) { return false; } 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; } if ((self->enemy->solid != SOLID_NOT) || (tr.fraction < 1.0)) { return false; } } } 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)) { /* 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; } if ((random() < chance) || (self->enemy->solid == SOLID_NOT)) { self->monsterinfo.attack_state = AS_MISSILE; return true; } return false; }
/* ====================== M_MoveToGoal ====================== */ void M_MoveToGoal (edict_t *ent, float dist) { edict_t *goal; goal = ent->goalentity; if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM))) return; // Lazarus range checks if (!(ent->monsterinfo.aiflags & (AI_CHASE_THING | AI_CHICKEN))) { if (ent->enemy && (ent->monsterinfo.min_range > 0) && ((goal==ent->enemy) || !goal) ) { float dist; dist = realrange(ent,ent->enemy); if(dist < ent->monsterinfo.min_range) { ent->monsterinfo.aiflags |= (AI_STAND_GROUND | AI_RANGE_PAUSE); ent->monsterinfo.rangetime = level.time + 0.5; ent->monsterinfo.stand(ent); return; } } if ((ent->enemy) && (level.time > ent->monsterinfo.rangetime + 0.5) && ((goal==ent->enemy) || !goal) ) { float dist; dist = realrange(ent,ent->enemy); if((dist < ent->monsterinfo.ideal_range[0]) && (rand() & 3)) { ent->monsterinfo.aiflags |= (AI_STAND_GROUND | AI_RANGE_PAUSE); ent->monsterinfo.rangetime = level.time + 1.0; ent->monsterinfo.stand(ent); return; } if((dist < ent->monsterinfo.ideal_range[1]) && (dist > ent->monsterinfo.ideal_range[0]) && (rand() & 1)) { ent->monsterinfo.aiflags |= (AI_STAND_GROUND | AI_RANGE_PAUSE); ent->monsterinfo.rangetime = level.time + 0.2; ent->monsterinfo.stand(ent); return; } } } if( (ent->monsterinfo.aiflags & AI_FOLLOW_LEADER) && (ent->movetarget) && (ent->movetarget->inuse) && (ent->movetarget->health > 0) ) { if(ent->enemy) ent->monsterinfo.currentmove = &actor_move_run; else { float R; R = realrange(ent,ent->movetarget); if(R > ACTOR_FOLLOW_RUN_RANGE) ent->monsterinfo.currentmove = &actor_move_run; else if(R < ACTOR_FOLLOW_STAND_RANGE && ent->movetarget->client) { ent->monsterinfo.pausetime = level.time + 0.5; ent->monsterinfo.currentmove = &actor_move_stand; return; } else ent->monsterinfo.currentmove = &actor_move_walk; } } // If the next step hits the enemy, return immediately. Don't do this for // AI_CHASE_THING, since we want monster to actually touch or pass through // "thing" if (ent->enemy && !(ent->monsterinfo.aiflags & AI_CHASE_THING) && SV_CloseEnough (ent, ent->enemy, dist) ) return; // bump around... if ( (rand()&3)==1 || !SV_StepDirection (ent, ent->ideal_yaw, dist)) { if (ent->inuse) SV_NewChaseDir (ent, goal, dist); } }