void mytank_chain_refire (edict_t *self) { float r, range; // is enemy still valid? if (G_ValidTarget(self, self->enemy, true)) { r = random(); range = entdist(self, self->enemy); // medium range = 50% chance to continue attack if (range <= 512) { if (r <= 0.5) self->monsterinfo.currentmove = &mytank_move_attack_chain; else self->monsterinfo.currentmove = &mytank_move_attack_chain_end; } // long range = 80% chance to continue attack else if (r <= 0.8) self->monsterinfo.currentmove = &mytank_move_attack_chain; // end attack sequence else self->monsterinfo.currentmove = &mytank_move_attack_chain_end; } else { self->monsterinfo.currentmove = &mytank_move_attack_chain_end; } // don't call the attack function again for awhile! self->monsterinfo.attack_finished = level.time + 2.0 + random(); }
void magmine_think(edict_t *self) { // check for valid position if (gi.pointcontents(self->s.origin) & CONTENTS_SOLID) { gi.dprintf("WARNING: A mag mine was removed from map due to invalid position.\n"); safe_cprintf(self->creator, PRINT_HIGH, "Your mag mine was removed.\n"); self->creator->magmine = NULL; G_FreeEdict(self); return; } if (!self->enemy) { if (magmine_findtarget(self)) { magmine_attack(self); magmine_throwsparks(self); } } else if (G_ValidTarget(self, self->enemy, true) && (entdist(self, self->enemy) <= self->dmg_radius)) { magmine_attack(self); magmine_throwsparks(self); } else { self->enemy = NULL; } //magmine_seteffects(self); M_SetEffects(self);//4.5 self->nextthink = level.time + FRAMETIME; }
void mytank_reattack_blaster (edict_t *self) { float r, range; if (G_ValidTarget(self, self->enemy, true)) { r = random(); range = entdist(self, self->enemy); // medium range = 80% chance to continue attack if (range <= 512) { if (r <= 0.8) self->monsterinfo.currentmove = &mytank_move_reattack_blast; else self->monsterinfo.currentmove = &mytank_move_attack_post_blast; } // long range = 50% chance to continue attack else { if (r <= 0.5) self->monsterinfo.currentmove = &mytank_move_reattack_blast; else self->monsterinfo.currentmove = &mytank_move_attack_post_blast; } } else { self->monsterinfo.currentmove = &mytank_move_attack_post_blast; } // don't call the attack function again for awhile! self->monsterinfo.attack_finished = level.time + 2.0 + random(); }
void PlagueCloudSpawn (edict_t *ent) { float radius; edict_t *e=NULL; if (ent->myskills.abilities[PLAGUE].disable) return; if (!V_CanUseAbilities(ent, PLAGUE, 0, false)) return; if ((ent->myskills.class_num == CLASS_POLTERGEIST) && !ent->mtype && !PM_PlayerHasMonster(ent)) return; // can't use this in human form radius = PLAGUE_DEFAULT_RADIUS+PLAGUE_ADDON_RADIUS*ent->myskills.abilities[PLAGUE].current_level; if (radius > PLAGUE_MAX_RADIUS) radius = PLAGUE_MAX_RADIUS; // find someone nearby to infect while ((e = findradius(e, ent->s.origin, radius)) != NULL) { if (!G_ValidTarget(ent, e, true)) continue; // if (HasActiveCurse(e, CURSE_PLAGUE)) if (que_typeexists(e->curses, CURSE_PLAGUE)) continue; // holy water grants temporary immunity to curses if (e->holywaterProtection > level.time) continue; PlagueCloud(ent, e); } }
void mytank_refire_rocket (edict_t *self) { float r, range; if (G_ValidTarget(self, self->enemy, true)) { r = random(); range = entdist(self, self->enemy); // medium range if (range <= 512) { if (r <= 0.8) self->monsterinfo.currentmove = &mytank_move_attack_fire_rocket; else self->monsterinfo.currentmove = &mytank_move_attack_post_rocket; } // long range else { if (r <= 0.5) self->monsterinfo.currentmove = &mytank_move_attack_fire_rocket; else self->monsterinfo.currentmove = &mytank_move_attack_post_rocket; } } // end attack sequence else { self->monsterinfo.currentmove = &mytank_move_attack_post_rocket; } // don't call the attack function again for awhile! self->monsterinfo.attack_finished = level.time + 2.5 + random(); }
void p_berserk_crush (edict_t *self, int damage, float range, int mod) { trace_t tr; edict_t *other=NULL; vec3_t v; // must be on the ground to punch if (!self->groundentity) return; self->lastsound = level.framenum; gi.sound (self, CHAN_AUTO, gi.soundindex ("tank/tnkatck5.wav"), 1, ATTN_NORM, 0); while ((other = findradius(other, self->s.origin, range)) != NULL) { if (!G_ValidTarget(self, other, true)) continue; if (!nearfov(self, other, 0, 30)) continue; VectorSubtract(other->s.origin, self->s.origin, v); VectorNormalize(v); tr = gi.trace(self->s.origin, NULL, NULL, other->s.origin, self, (MASK_PLAYERSOLID | MASK_MONSTERSOLID)); T_Damage (other, self, self, v, other->s.origin, tr.plane.normal, damage, damage, 0, mod); other->velocity[2] += damage / 2; } }
void mytank_meleeattack (edict_t *self) { int damage; trace_t tr; edict_t *other=NULL; vec3_t v; // tank must be on the ground to punch if (!self->groundentity) return; self->lastsound = level.framenum; damage = 100+20*self->monsterinfo.level; gi.sound (self, CHAN_AUTO, gi.soundindex ("tank/tnkatck5.wav"), 1, ATTN_NORM, 0); while ((other = findradius(other, self->s.origin, 128)) != NULL) { if (!G_ValidTarget(self, other, true)) continue; // bosses don't have to be facing their enemy, others do //if ((self->monsterinfo.control_cost < 3) && !nearfov(self, other, 0, 60))//!infront(self, other)) // continue; VectorSubtract(other->s.origin, self->s.origin, v); VectorNormalize(v); tr = gi.trace(self->s.origin, NULL, NULL, other->s.origin, self, (MASK_PLAYERSOLID | MASK_MONSTERSOLID)); T_Damage (other, self, self, v, tr.endpos, tr.plane.normal, damage, 200, 0, MOD_TANK_PUNCH); //other->velocity[2] += 200;//damage / 2; } }
void plague_think (edict_t *self) { int dmg; float radius; edict_t *e=NULL; // plague self-terminates if: if (!G_EntIsAlive(self->owner) || !G_EntIsAlive(self->enemy) //someone dies || (self->owner->flags & FL_WORMHOLE) // owner enters a wormhole || (self->owner->client->tball_delay > level.time) //owner tballs away || (self->owner->flags & FL_CHATPROTECT) //3.0 owner is in chatprotect || ((self->owner->myskills.class_num == CLASS_POLTERGEIST) && (!self->owner->mtype) && !PM_PlayerHasMonster(self->owner)) //3.0 poltergeist is in human form || que_findtype(self->enemy->curses, NULL, HEALING) != NULL) //3.0 player is blessed with healing { que_removeent(self->enemy->curses, self, true); return; } VectorCopy(self->enemy->s.origin, self->s.origin); // follow enemy radius = PLAGUE_DEFAULT_RADIUS+PLAGUE_ADDON_RADIUS*self->owner->myskills.abilities[PLAGUE].current_level; if (radius > PLAGUE_MAX_RADIUS) radius = PLAGUE_MAX_RADIUS; // find someone nearby to infect while ((e = findradius(e, self->s.origin, radius)) != NULL) { if (e == self->enemy) continue; if (!G_ValidTarget(self, e, true)) continue; // don't allow more than one curse of the same type if (que_typeexists(e->curses, CURSE_PLAGUE)) continue; // holy water grants temporary immunity to curses if (e->holywaterProtection > level.time) continue; // spawn another plague cloud on this entity PlagueCloud(self->owner, e); } if (level.time > self->wait) { dmg = (float)self->owner->myskills.abilities[PLAGUE].current_level/10 * ((float)self->enemy->max_health/20); if (!self->enemy->client && strcmp(self->enemy->classname, "player_tank") != 0) dmg *= 2; // non-clients take double damage (helps with pvm) if (dmg < 1) dmg = 1; if (dmg > 100) dmg = 100; T_Damage(self->enemy, self->enemy, self->owner, vec3_origin, self->enemy->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ABILITIES, MOD_PLAGUE); // hurt 'em self->wait = level.time + PLAGUE_DELAY; } self->nextthink = level.time + FRAMETIME; }
void gunner_attack_grenade (edict_t *self) { // continue attack sequence unless enemy is no longer valid if (G_ValidTarget(self, self->enemy, true)) self->monsterinfo.currentmove = &mygunner_move_attack_grenade; else mygunnerrun(self); }
void mytank_attack_chain (edict_t *self) { // continue attack sequence unless enemy is no longer valid if (G_ValidTarget(self, self->enemy, true)) self->monsterinfo.currentmove = &mytank_move_attack_chain; else mytank_run(self); }
void MindAbsorb(edict_t *ent) { edict_t *target = NULL; int radius; int take; int total; int abilityLevel = ent->myskills.abilities[MIND_ABSORB].current_level; if(ent->myskills.abilities[MIND_ABSORB].disable) return; if (!V_CanUseAbilities(ent, MIND_ABSORB, 0, false)) return; //Cloaking and chat protected players can't steal anything if ((ent->flags & FL_CHATPROTECT) || (ent->svflags & SVF_NOCLIENT)) return; take = MIND_ABSORB_AMOUNT_BASE + (MIND_ABSORB_AMOUNT_BONUS * abilityLevel); radius = MIND_ABSORB_RADIUS_BASE + (MIND_ABSORB_RADIUS_BONUS * abilityLevel); // scan for targets while ((target = findclosestradius(target, ent->s.origin, radius)) != NULL) { if (target == ent) continue; if (!G_ValidTarget(ent, target, true)) continue; total = 0; if (target->client) { if (target->client->pers.inventory[power_cube_index] < take) total += target->client->pers.inventory[power_cube_index]; else total += take; target->client->pers.inventory[power_cube_index] -= total; // a bit of amnesia too target->client->ability_delay = level.time + 0.1 * abilityLevel; } else { if (target->health < take) total += target->health; else total += take; } //Cap cube count to max cubes if (ent->client->pers.inventory[power_cube_index] + total < MAX_POWERCUBES(ent)) ent->client->pers.inventory[power_cube_index] += total; else if (ent->client->pers.inventory[power_cube_index] < MAX_POWERCUBES(ent)) ent->client->pers.inventory[power_cube_index] = MAX_POWERCUBES(ent); // those powercubes hurt! T_Damage(target, ent, ent, vec3_origin, vec3_origin, vec3_origin, total, 0, DAMAGE_NO_ARMOR, MOD_MINDABSORB); } }
void m_soldier_attack1_refire1 (edict_t *self) { // continue firing if the enemy is still close, or we are standing ground if (G_ValidTarget(self, self->enemy, true) && (random() <= 0.9) && ((entdist(self, self->enemy) <= 512) || (self->monsterinfo.aiflags & AI_STAND_GROUND))) { self->s.frame = FRAME_attak102; self->monsterinfo.attack_finished = level.time + 2; } }
void Cmd_BombPlayer(edict_t *ent, float skill_mult, float cost_mult) { int cost=COST_FOR_BOMB*cost_mult; vec3_t forward, right, start, end, offset; trace_t tr; // edict_t *other=NULL; if (debuginfo->value) gi.dprintf("DEBUG: %s just called Cmd_BombPlayer()\n", ent->client->pers.netname); //gi.dprintf("%s\n", gi.args()); if(ent->myskills.abilities[BOMB_SPELL].disable) return; if (!G_CanUseAbilities(ent, ent->myskills.abilities[BOMB_SPELL].current_level, cost)) return; ent->client->ability_delay = level.time + DELAY_BOMB/* * cost_mult*/; // write a nice effect so everyone knows we've cast a spell gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_TELEPORT_EFFECT); gi.WritePosition (ent->s.origin); gi.multicast (ent->s.origin, MULTICAST_PVS); ent->lastsound = level.framenum; // bomb an area //if (!Q_strcasecmp(gi.args(), "forward")) if (strstr(gi.args(), "forward")) { CarpetBomb(ent, skill_mult, cost_mult); return; } // bomb ahead of us //if (!Q_strcasecmp(gi.args(), "area")) if (strstr(gi.args(), "area")) { BombArea(ent, skill_mult, cost_mult); return; } ent->client->pers.inventory[power_cube_index] -= cost; AngleVectors (ent->client->v_angle, forward, right, NULL); VectorScale (forward, -3, ent->client->kick_origin); VectorSet(offset, 0, 7, ent->viewheight-8); P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start); VectorMA(start, BOMBPERSON_RANGE, forward, end); tr = gi.trace(start, NULL, NULL, end, ent, MASK_SHOT); if (G_ValidTarget(ent, tr.ent, false)) BombPerson(tr.ent, ent, skill_mult); }
void mygunner_continue (edict_t *self) { if (G_ValidTarget(self, self->enemy, true) && (random() <= 0.9) && (entdist(self, self->enemy) <= 512)) self->monsterinfo.currentmove = &mygunner_move_runandshoot; else self->monsterinfo.currentmove = &mygunnermove_run; // don't call the attack function again for awhile! self->monsterinfo.attack_finished = level.time + 1.0; }
void gunner_refire_grenade (edict_t *self) { // continue firing unless enemy is no longer valid or out of range if (G_ValidTarget(self, self->enemy, true) && (random() <= 0.8) && (entdist(self, self->enemy) <= 384)) self->monsterinfo.currentmove = &mygunner_move_attack_grenade; else self->monsterinfo.currentmove = &mygunner_move_attack_grenade_end; // don't call the attack function again for awhile! self->monsterinfo.attack_finished = level.time + 0.5; }
void gladiator_refire (edict_t *self) { // if our enemy is still valid, then continue firing if (G_ValidTarget(self, self->enemy, true) && (random() <= 0.8)) { self->s.frame = 49; GladiatorGun(self); return; } self->monsterinfo.attack_finished = level.time + 1.0; }
void minisentry_pain (edict_t *self, edict_t *other, float kick, int damage) { if (!self->enemy && G_ValidTarget(self, other, true) && (entdist(self, other)<SENTRY_ATTACK_RANGE)) self->enemy = other; if (level.time < self->pain_debounce_time) return; self->pain_debounce_time = level.time + 2; gi.sound (self, CHAN_VOICE, gi.soundindex ("tank/tnkpain2.wav"), 1, ATTN_NORM, 0); }
void m_soldier_runandshoot_continue (edict_t *self) { if (G_ValidTarget(self, self->enemy, true) && (random() <= 0.9) && (entdist(self, self->enemy) <= 512)) { self->monsterinfo.currentmove = &m_soldier_move_runandshoot; self->monsterinfo.attack_finished = level.time + 2; return; } self->monsterinfo.currentmove = &m_soldier_move_run; }
void mymedic_refire (edict_t *self) { if (G_ValidTarget(self, self->enemy, true) && (random() <= 0.5) && (entdist(self, self->enemy) <= 512)) { self->s.frame = FRAME_attack19; return; } self->monsterinfo.currentmove = &mymedic_move_run; self->monsterinfo.attack_finished = level.time + random() + 1; }
qboolean magmine_findtarget(edict_t *self) { edict_t *other = NULL; while ((other = findclosestradius(other, self->s.origin, self->dmg_radius)) != NULL) { if (!G_ValidTarget(self, other, true)) continue; //if (!BrainValidTarget(self, other)) // continue; self->enemy = other; return true; } return false; }
// search for nearby enemies, return true if one is found // this is to make the medic stop healing if there is a higher priority target // this function is the same as drone_findtarget(), except that it skips the medic check qboolean mymedic_findenemy (edict_t *self) { edict_t *target=NULL; while ((target = findclosestradius (target, self->s.origin, 1024)) != NULL) { if (!G_ValidTarget(self, target, true)) continue; self->enemy = target; drone_wakeallies(self); return true; } return false; }
void mygunner_refire_chain(edict_t *self) { // keep firing if (G_ValidTarget(self, self->enemy, true) && (random() <= 0.8) && entdist(self, self->enemy) > 128) self->monsterinfo.currentmove = &mygunner_move_fire_chain; else self->monsterinfo.currentmove = &mygunner_move_endfire_chain; //mygunner_delay(self); // don't call the attack function again for awhile! self->monsterinfo.attack_finished = level.time + 0.5; }
void mychick_rerocket(edict_t *self) { if (G_ValidTarget(self, self->enemy, true)) { if (random() <= 0.8 && (entdist(self, self->enemy) <= 512 || (self->monsterinfo.aiflags & AI_STAND_GROUND))) self->monsterinfo.currentmove = &mychick_move_attack1; else self->monsterinfo.currentmove = &mychick_move_end_attack1; } else self->monsterinfo.currentmove = &mychick_move_end_attack1; // don't call the attack function again for awhile! self->monsterinfo.attack_finished = level.time + 0.5; }
void mytank_restrike (edict_t *self) { if (G_ValidTarget(self, self->enemy, true) && (random() <= 0.6 || !self->enemy->client) && (entdist(self, self->enemy) < 128)) { self->monsterinfo.currentmove = &mytank_move_strike; } else { self->monsterinfo.currentmove = &mytank_move_post_strike; } // don't call the attack function again for awhile! self->monsterinfo.attack_finished = level.time + 0.5 + random(); }
void gladiator_cleaver_refire (edict_t *self) { if (self->monsterinfo.bonus_flags & BF_UNIQUE_LIGHTNING) return; if (G_ValidTarget(self, self->enemy, true) && (entdist(self, self->enemy) <= 96) && (random() <= 0.8)) { self->s.frame = 41; gladiator_cleaver_swing(self); return; } self->monsterinfo.melee_finished = level.time + GetRandom(5, 10)*FRAMETIME; }
void burn_person (edict_t *target, edict_t *owner, int damage) { edict_t *flame; que_t *slot=NULL; if (level.time < pregame_time->value) return; if (que_typeexists(target->curses, CURSE_FROZEN)) return; // attacker is frozen! if (!G_ValidTarget(owner, target, false)) return; while ((slot = que_findtype(target->curses, slot, CURSE_BURN)) != NULL) { if (slot->time-level.time >= 9) return; // only allow 1 burn per second } flame = G_Spawn(); flame->movetype = MOVETYPE_NOCLIP; flame->solid = SOLID_NOT; VectorClear(flame->mins); VectorClear(flame->maxs); flame->owner = owner; flame->enemy = target; flame->mtype = CURSE_BURN; flame->delay = level.time + 10; flame->nextthink = level.time + FRAMETIME; flame->PlasmaDelay = level.time + FRAMETIME; flame->think = fire_think; flame->classname = "fire"; flame->s.sound = gi.soundindex ("weapons/bfg__l1a.wav"); flame->dmg = damage; VectorCopy(target->s.origin, flame->s.origin); gi.linkentity (flame); if (!que_addent(target->curses, flame, 10)) { G_FreeEdict(flame); return; } gi.sound (target, CHAN_ITEM, gi.soundindex ("misc/needlite.wav"), 1, ATTN_NORM, 0); }
void WaterTotem_think(edict_t *self, edict_t *caster) { edict_t *target = NULL; //Find players in radius and attack them. while ((target = findclosestradius(target, self->s.origin, TOTEM_MAX_RANGE)) != NULL) { // (apple) // Since ice talent and watertotem work concurrently now, // checking for chill_duration will throttle ice talent's refire. if (G_ValidTarget(self, target, true)) { vec3_t normal; int talentLevel; float duration = WATERTOTEM_DURATION_BASE + self->monsterinfo.level * WATERTOTEM_DURATION_MULT; //Get a directional vector from the totem to the target. VectorSubtract(self->s.origin, target->s.origin, normal); //Talent: Ice. Damages players. talentLevel = getTalentLevel(caster, TALENT_ICE); if(talentLevel > 0) { int damage = GetRandom(10, 20) * talentLevel; vec3_t normal; //Damage the target VectorSubtract(target->s.origin, self->s.origin, normal); T_Damage(target, self, self, vec3_origin, self->s.origin, normal, damage, 0, DAMAGE_NO_KNOCKBACK, MOD_WATERTOTEM); } //Chill the target. target->chill_level = self->monsterinfo.level; target->chill_time = level.time + duration; //gi.dprintf("chilled %s for %.1f seconds at level %d\n", target->classname, duration, self->monsterinfo.level); } } //Next think. self->delay = level.time + WATERTOTEM_REFIRE_BASE + WATERTOTEM_REFIRE_MULT * self->monsterinfo.level; }
void mybrain_continue (edict_t *self) { float dist, chance; vec3_t start, forward; if (!G_ValidTarget(self, self->enemy, true)) return; // higher chance to continue attack if our enemy is close dist = entdist(self, self->enemy); chance = 1-dist/1024.0; AngleVectors(self->s.angles, forward, NULL, NULL); VectorMA(self->s.origin, self->maxs[1]+8, forward , start); if (G_IsClearPath(self->enemy, MASK_SHOT, start, self->enemy->s.origin) && (random() <= chance) && (dist <= 512)) self->monsterinfo.nextframe = FRAME_attak206; mybrain_suxor(self); }
qboolean minisentry_findtarget (edict_t *self) { edict_t *target=NULL; // don't retarget too quickly if (self->last_move_time > level.time) return false; while ((target = findclosestradius (target, self->s.origin, SENTRY_TARGET_RANGE)) != NULL) { if (!G_ValidTarget(self, target, true)) continue; if (!infov(self, target, SENTRY_FOV)) continue; self->enemy = target; self->last_move_time = level.time + 1.0; gi.sound(self, CHAN_AUTO, gi.soundindex("weapons/turrspot.wav"), 1, ATTN_NORM, 0); return true; } return false; }
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; }