void monster_fire_blaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, int proj_type, float duration, qboolean bounce, int flashtype) { int mod; float chance; qboolean hyperblaster = false; // holy freeze reduces firing rate by 50% if (que_typeexists(self->curses, AURA_HOLYFREEZE)) { if (random() <= 0.5) return; } // chill effect reduces attack rate/refire if (self->chill_time > level.time) { chance = 1 / (1 + CHILL_DEFAULT_BASE + CHILL_DEFAULT_ADDON * self->chill_level); if (random() > chance) return; } if (proj_type == BLASTER_PROJ_BLAST) mod = MOD_BLASTER; else mod = MOD_HYPERBLASTER; damage = monster_increaseDamageByTalent(self->activator, damage); fire_blaster(self, start, dir, damage, speed, effect, proj_type, mod, duration, bounce); gi.WriteByte (svc_muzzleflash2); gi.WriteShort (self - g_edicts); gi.WriteByte (flashtype); gi.multicast (start, MULTICAST_PVS); }
qboolean G_CurseValidTarget (edict_t *self, edict_t *target, qboolean vis, qboolean isCurse) { if (!G_EntIsAlive(target)) return false; // don't target players with invulnerability if (target->client && (target->client->invincible_framenum > level.framenum)) return false; // don't target spawning players if (target->client && (target->client->respawn_time > level.time)) return false; // don't target players in chat-protect if (!ptr->value && target->client && (target->flags & FL_CHATPROTECT)) return false; // don't target spawning world monsters if (target->activator && !target->activator->client && (target->svflags & SVF_MONSTER) && (target->deadflag != DEAD_DEAD) && (target->nextthink-level.time > 2*FRAMETIME)) return false; // don't target cloaked players if (target->client && target->svflags & SVF_NOCLIENT) return false; if (vis && !visible(self, target)) return false; if(que_typeexists(target->curses, CURSE_FROZEN)) return false; if (isCurse && (target->flags & FL_GODMODE || OnSameTeam(self, target))) return false; if (target == self) return false; return true; }
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 Cmd_HolyFreeze(edict_t *ent) { qboolean sameaura=false; if (debuginfo->value) gi.dprintf("DEBUG: %s just called Cmd_HolyFreeze()\n", ent->client->pers.netname); if(ent->myskills.abilities[HOLY_FREEZE].disable) return; if (!G_CanUseAbilities(ent, ent->myskills.abilities[HOLY_FREEZE].current_level, 0)) return; // if we already had an aura on, remove it if (que_typeexists(ent->auras, AURA_HOLYFREEZE)) { safe_cprintf(ent, PRINT_HIGH, "Holy freeze removed.\n"); AuraRemove(ent, AURA_HOLYFREEZE); return; } ent->client->ability_delay = level.time + DEFAULT_AURA_DELAY; // do we have enough power cubes? if (ent->client->pers.inventory[power_cube_index] < DEFAULT_AURA_INIT_COST) { safe_cprintf(ent, PRINT_HIGH, "You need more %d power cubes to use this ability.\n", DEFAULT_AURA_INIT_COST-ent->client->pers.inventory[power_cube_index]); return; } ent->client->pers.inventory[power_cube_index] -= DEFAULT_AURA_INIT_COST; gi.sound(ent, CHAN_ITEM, gi.soundindex("auras/holywind.wav"), 1, ATTN_NORM, 0); safe_cprintf(ent, PRINT_HIGH, "Now using holy freeze aura.\n"); aura_holyfreeze(ent); }
void monster_fire_shotgun (edict_t *self, vec3_t start, vec3_t aimdir, float damage, int kick, int hspread, int vspread, int count, int flashtype) { float chance; // holy freeze reduces firing rate by 50% if (que_typeexists(self->curses, AURA_HOLYFREEZE)) { if (random() <= 0.5) return; } // chill effect reduces attack rate/refire if (self->chill_time > level.time) { chance = 1 / (1 + CHILL_DEFAULT_BASE + CHILL_DEFAULT_ADDON * self->chill_level); if (random() > chance) return; } damage = monster_increaseDamageByTalent(self->activator, damage); fire_shotgun (self, start, aimdir, damage, kick, hspread, vspread, count, MOD_UNKNOWN); gi.WriteByte (svc_muzzleflash2); gi.WriteShort (self - g_edicts); gi.WriteByte (flashtype); gi.multicast (start, MULTICAST_PVS); }
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; }
static qboolean parasite_cantarget (edict_t *self, edict_t *target) { int para_range = PARASITE_ATTACK_RANGE; return (G_EntExists(target) && !que_typeexists(target->curses, CURSE_FROZEN) && !OnSameTeam(self, target) && visible(self, target) && nearfov(self, target, 45, 45) && (entdist(self, target) <= para_range)); }
void CursedPlayer (edict_t *ent) { int i; vec3_t forward; if (!G_EntIsAlive(ent)) return; if (!ent->client) return; if (!que_typeexists(ent->curses, CURSE)) { // reset roll angles ent->client->ps.pmove.delta_angles[ROLL] = 0; return; } if (level.time > ent->curse_delay) { ent->curse_dir = GetRandom(1, 8); ent->curse_delay = level.time + 2*random(); } // copy current viewing angles VectorCopy(ent->client->v_angle, forward); // choose which direction to move angles switch (ent->curse_dir) { case 1: forward[PITCH]+=CURSE_MOVEMENT; break; // down case 2: forward[PITCH]-=CURSE_MOVEMENT; break; // up case 3: forward[YAW]+=CURSE_MOVEMENT; break; // left case 4: forward[YAW]-=CURSE_MOVEMENT; break; // right case 5: forward[YAW]+=CURSE_MOVEMENT; forward[PITCH]-=CURSE_MOVEMENT; break; case 6: forward[YAW]+=CURSE_MOVEMENT; forward[PITCH]+=CURSE_MOVEMENT; break; case 7: forward[YAW]-=CURSE_MOVEMENT; forward[PITCH]-=CURSE_MOVEMENT; break; case 8: forward[YAW]+=CURSE_MOVEMENT; forward[PITCH]+=CURSE_MOVEMENT; break; } // change roll angles if (ent->curse_dir <= 4) forward[ROLL] +=CURSE_MOVEMENT; else forward[ROLL] -=CURSE_MOVEMENT; // don't roll too much if ((forward[ROLL] > 0) && (forward[ROLL] > CURSE_MAX_ROLL)) forward[ROLL] = CURSE_MAX_ROLL; else if ((forward[ROLL] < 0) && (forward[ROLL] < -CURSE_MAX_ROLL)) forward[ROLL] = -CURSE_MAX_ROLL; // set view angles for (i = 0 ; i < 3 ; i++) ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(forward[i]-ent->client->resp.cmd_angles[i]); VectorCopy(forward, ent->client->ps.viewangles); VectorCopy(forward, ent->client->v_angle); }
qboolean ParasiteCanAttack (edict_t *ent) { // if a player initiated a jump and they are not in the water, then don't let them attack if (!ent->groundentity && ent->waterlevel < 1 && ent->monsterinfo.jumpup) return false; if (level.time < ent->wait) return false; // re-fire time if (ent->monsterinfo.attack_finished > level.time) return false; return ((level.time > pregame_time->value) && (level.time > ent->client->respawn_time) && (!que_typeexists(ent->curses, CURSE_FROZEN)) && (level.time > ent->holdtime)); }
void wormhole_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { if (!other || !other->inuse) return; // point to monster's pilot if (PM_MonsterHasPilot(other)) other = other->activator; if ((other == self->creator) || IsAlly(self->creator, other)) { float time; // can't enter wormhole with the flag if (HasFlag(other)) return; // can't enter wormhole while being hurt if (other->lasthurt + DAMAGE_ESCAPE_DELAY > level.time) return; // can't enter wormhole while cursed (this includes healing, bless) if (que_typeexists(other->curses, -1)) return; // can't stay in wormhole long if we're warring if (SPREE_WAR == true && SPREE_DUDE == other) time = 10.0; else time = BLACKHOLE_EXIT_TIME; // reset railgun sniper frames other->client->refire_frames = 0; VortexRemovePlayerSummonables(other); V_RestoreMorphed(other, 50); // un-morph other->flags |= FL_WORMHOLE; other->movetype = MOVETYPE_NOCLIP; other->svflags |= SVF_NOCLIENT; other->client->wormhole_time = level.time + BLACKHOLE_EXIT_TIME; // must exit wormhole by this time self->nextthink = level.time + FRAMETIME; // close immediately } }
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 minisentry_lockon (edict_t *self) { float max, temp; vec3_t angles, v; // curse causes minisentry to fail to lock-on to enemy if ((que_typeexists(self->curses, CURSE)) && random() <= 0.8) return; temp = self->yaw_speed; self->yaw_speed *= 3; VectorSubtract(self->enemy->s.origin, self->s.origin, v); vectoangles(v, angles); self->ideal_yaw = vectoyaw(v); M_ChangeYaw(self); self->yaw_speed = temp; // restore original yaw speed self->s.angles[PITCH] = angles[PITCH]; ValidateAngles(self->s.angles); // maximum pitch is 65 degrees in either direction if (self->enemy->s.origin[2] > self->s.origin[2]) // if the enemy is above { if (self->owner && self->owner->style == SENTRY_FLIPPED) max = 340; // allow 20 degrees up else max = 315; // allow 45 degrees up if (self->s.angles[PITCH] < max) self->s.angles[PITCH] = max; } else { if (self->owner && self->owner->style == SENTRY_FLIPPED) max = 45; // allow 45 degrees down else max = 20; // allow 20 degrees down if (self->s.angles[PITCH] > max) self->s.angles[PITCH] = max; } }
void monster_fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype) { float radius, chance; // holy freeze reduces firing rate by 50% if (que_typeexists(self->curses, AURA_HOLYFREEZE)) { if (random() <= 0.5) return; } // chill effect reduces attack rate/refire if (self->chill_time > level.time) { chance = 1 / (1 + CHILL_DEFAULT_BASE + CHILL_DEFAULT_ADDON * self->chill_level); if (random() > chance) return; } radius = damage; // cap damage radius if (damage > 125) radius = 125; // cap speed if (speed > 1000) speed = 1000; damage = monster_increaseDamageByTalent(self->activator, damage); fire_rocket (self, start, dir, damage, speed, radius, damage); gi.WriteByte (svc_muzzleflash2); gi.WriteShort (self - g_edicts); gi.WriteByte (flashtype); gi.multicast (start, MULTICAST_PVS); }
qboolean CanCurseTarget (edict_t *caster, edict_t *target, int type, qboolean isCurse, qboolean vis) { if (!G_EntIsAlive(target)) return false; // don't target players with invulnerability if (target->client && (target->client->invincible_framenum > level.framenum)) return false; // don't target spawning players if (target->client && (target->client->respawn_time > level.time)) return false; // don't target players in chat-protect if (!ptr->value && target->client && (target->flags & FL_CHATPROTECT)) return false; // don't target spawning world monsters if (target->activator && !target->activator->client && (target->svflags & SVF_MONSTER) && (target->deadflag != DEAD_DEAD) && (target->nextthink-level.time > 2*FRAMETIME)) return false; // don't target cloaked players if (target->client && target->svflags & SVF_NOCLIENT) return false; if (vis && !visible(caster, target)) return false; if(que_typeexists(target->curses, CURSE_FROZEN)) return false; if (isCurse && (target->flags & FL_GODMODE || OnSameTeam(caster, target))) return false; if (target == caster) return false; // holywater gives immunity to curses for a short time. if (target->holywaterProtection > level.time && type != BLESS && type != HEALING) return false; // don't allow bless on flag carrier if ((type == BLESS) && target->client && HasFlag(target)) return false; return true; }
/* ============= P_WorldEffects ============= */ void P_WorldEffects (void) { qboolean breather; qboolean envirosuit; int waterlevel, old_waterlevel; if (current_player->movetype == MOVETYPE_NOCLIP) { current_player->air_finished = level.time + 12; // don't need air return; } //K03 Begin //if (HasActiveCurse(current_player, CURSE_FROZEN)) if (que_typeexists(current_player->curses, CURSE_FROZEN)) current_player->air_finished = level.time + 6; //K03 End waterlevel = current_player->waterlevel; old_waterlevel = current_client->old_waterlevel; current_client->old_waterlevel = waterlevel; breather = current_client->breather_framenum > level.framenum; envirosuit = current_client->enviro_framenum > level.framenum; // // if just entered a water volume, play a sound // if (!old_waterlevel && waterlevel) { if ((current_player->client->pers.inventory[ITEM_INDEX(FindItem("Stealth Boots"))] < 1)) { if((current_player->myskills.abilities[CLOAK].disable) || (current_player->myskills.abilities[CLOAK].current_level)) { current_player->lastsound = level.framenum; // trigger monsters PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); if (current_player->watertype & CONTENTS_LAVA) gi.sound (current_player, CHAN_BODY, gi.soundindex("player/lava_in.wav"), 1, ATTN_NORM, 0); else if (current_player->watertype & CONTENTS_SLIME) gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0); else if (current_player->watertype & CONTENTS_WATER) gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0); } } current_player->flags |= FL_INWATER; // clear damage_debounce, so the pain sound will play immediately current_player->damage_debounce_time = level.time - 1; } // // if just completely exited a water volume, play a sound // if (old_waterlevel && ! waterlevel) { if ((current_player->client->pers.inventory[ITEM_INDEX(FindItem("Stealth Boots"))]<1) && !current_player->mtype) { if((current_player->myskills.abilities[CLOAK].disable) || (current_player->myskills.abilities[CLOAK].current_level < 1)) { current_player->lastsound = level.framenum; // trigger monsters PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0); } } current_player->flags &= ~FL_INWATER; } // // check for head just going under water // if (old_waterlevel != 3 && waterlevel == 3) { if (current_player->client)//K03 gi.sound (current_player, CHAN_BODY, gi.soundindex("player/watr_un.wav"), 1, ATTN_NORM, 0); } // // check for head just coming out of water // if (old_waterlevel == 3 && waterlevel != 3) { if (current_player->air_finished < level.time) { // gasp for air if (current_player->client)//K03 { gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp1.wav"), 1, ATTN_NORM, 0); PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); } } else if (current_player->air_finished < level.time + 11) { // just break surface if (current_player->client)//K03 gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/gasp2.wav"), 1, ATTN_NORM, 0); } } // // check for drowning // if (waterlevel == 3) { // breather or envirosuit give air if (breather || envirosuit) { current_player->air_finished = level.time + 10; if (((int)(current_client->breather_framenum - level.framenum) % 25) == 0) { if (current_player->client)//K03 { if (!current_client->breather_sound) gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath1.wav"), 1, ATTN_NORM, 0); else gi.sound (current_player, CHAN_AUTO, gi.soundindex("player/u_breath2.wav"), 1, ATTN_NORM, 0); current_client->breather_sound ^= 1; PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); } //FIXME: release a bubble? } } // if out of air, start drowning if ((current_player->air_finished < level.time) && !((current_player->myskills.class_num == CLASS_POLTERGEIST) && (current_player->mtype == 0)) && (current_player->myskills.abilities[WORLD_RESIST].current_level < 1 || HasFlag(current_player)) && !(current_player->flags & FL_GODMODE)) { // drown! if (current_player->client->next_drown_time < level.time && current_player->health > 0) { current_player->client->next_drown_time = level.time + 1; // take more damage the longer underwater current_player->dmg += 2; if (current_player->dmg > 15) current_player->dmg = 15; // play a gurp sound instead of a normal pain sound if (current_player->client) { if (current_player->health <= current_player->dmg) gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/drown1.wav"), 1, ATTN_NORM, 0); else if (rand()&1) gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp1.wav"), 1, ATTN_NORM, 0); else gi.sound (current_player, CHAN_VOICE, gi.soundindex("*gurp2.wav"), 1, ATTN_NORM, 0); } current_player->pain_debounce_time = level.time; T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, current_player->dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER); } } } else { current_player->air_finished = level.time + 12; current_player->dmg = 2; } // // check for sizzle damage // if (waterlevel && (current_player->watertype & (CONTENTS_LAVA|CONTENTS_SLIME)) && (current_player->myskills.abilities[WORLD_RESIST].current_level < 1 || HasFlag(current_player)) && !(current_player->flags & FL_GODMODE) && !((current_player->myskills.class_num == CLASS_POLTERGEIST) && (current_player->mtype == 0))) { if (current_player->watertype & CONTENTS_LAVA) { if (current_player->health > 0 && current_player->pain_debounce_time <= level.time && current_client->invincible_framenum < level.framenum) { if (current_player->client)//K03 { if (rand()&1) gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn1.wav"), 1, ATTN_NORM, 0); else gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/burn2.wav"), 1, ATTN_NORM, 0); } current_player->pain_debounce_time = level.time + 1; } if (envirosuit) // take 1/3 damage with envirosuit T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_LAVA); else T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 3*waterlevel, 0, 0, MOD_LAVA); } if (current_player->watertype & CONTENTS_SLIME) { if (!envirosuit) { // no damage from slime with envirosuit T_Damage (current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1*waterlevel, 0, 0, MOD_SLIME); } } } }
void Cmd_PlayerToMedic_f (edict_t *ent) { vec3_t boxmin, boxmax; trace_t tr; int cost = MEDIC_INIT_COST; //Talent: More Ammo int talentLevel = getTalentLevel(ent, TALENT_MORE_AMMO); if (debuginfo->value) gi.dprintf("DEBUG: %s just called Cmd_PlayerToMedic_f()\n", ent->client->pers.netname); // try to switch back if (ent->mtype || PM_PlayerHasMonster(ent)) { // don't let a player-tank unmorph if they are cocooned if (ent->owner && ent->owner->inuse && ent->owner->movetype == MOVETYPE_NONE) return; if (que_typeexists(ent->curses, 0)) { safe_cprintf(ent, PRINT_HIGH, "You can't morph while cursed!\n"); return; } V_RestoreMorphed(ent, 0); return; } //Talent: Morphing if(getTalentSlot(ent, TALENT_MORPHING) != -1) cost *= 1.0 - 0.25 * getTalentLevel(ent, TALENT_MORPHING); // if (!G_CanUseAbilities(ent, ent->myskills.abilities[MEDIC].current_level, cost)) // return; if (!V_CanUseAbilities(ent, MEDIC, cost, true)) return; if (HasFlag(ent)) { safe_cprintf(ent, PRINT_HIGH, "Can't morph while carrying flag!\n"); return; } // make sure don't get stuck in a wall VectorSet (boxmin, -24, -24, -24); VectorSet (boxmax, 24, 24, 32); tr = gi.trace(ent->s.origin, boxmin, boxmax, ent->s.origin, ent, MASK_SHOT); if (tr.fraction<1) { safe_cprintf(ent, PRINT_HIGH, "Not enough room to morph!\n"); return; } V_ModifyMorphedHealth(ent, MORPH_MEDIC, true); VectorCopy(boxmin, ent->mins); VectorCopy(boxmax, ent->maxs); ent->monsterinfo.attack_finished = level.time + 0.5;// can't attack immediately ent->client->pers.inventory[power_cube_index] -= cost; ent->client->ability_delay = level.time + MEDIC_DELAY; ent->mtype = MORPH_MEDIC; ent->s.modelindex = gi.modelindex ("models/monsters/medic/tris.md2"); ent->s.modelindex2 = 0; if (!ent->myskills.administrator) ent->s.skinnum = 0; else ent->s.skinnum = 2; // commander // set maximum hyperblaster ammo ent->myskills.abilities[MEDIC].max_ammo = MEDIC_HB_INITIAL_AMMO+MEDIC_HB_ADDON_AMMO *ent->myskills.abilities[MEDIC].current_level; // Talent: More Ammo // increases ammo 10% per talent level if(talentLevel > 0) ent->myskills.abilities[MEDIC].max_ammo *= 1.0 + 0.1*talentLevel; // give them some starting ammo ent->myskills.abilities[MEDIC].ammo = MEDIC_HB_START_AMMO; ent->client->refire_frames = 0; // reset charged weapon ent->client->weapon_mode = 0; // reset weapon mode lasersight_off(ent); gi.sound (ent, CHAN_WEAPON, gi.soundindex("spells/morph.wav") , 1, ATTN_NORM, 0); }
void Cmd_PlayerToParasite_f (edict_t *ent) { int para_cubecost = PARASITE_INIT_COST; if (debuginfo->value) gi.dprintf("DEBUG: %s just called Cmd_PlayerToParasite_f()\n", ent->client->pers.netname); // try to switch back if (ent->mtype || PM_PlayerHasMonster(ent)) { // don't let a player-tank unmorph if they are cocooned if (ent->owner && ent->owner->inuse && ent->owner->movetype == MOVETYPE_NONE) return; if (que_typeexists(ent->curses, 0)) { gi.cprintf(ent, PRINT_HIGH, "You can't morph while cursed!\n"); return; } V_RestoreMorphed(ent, 0); return; } //Talent: Morphing if(getTalentSlot(ent, TALENT_MORPHING) != -1) para_cubecost *= 1.0 - 0.25 * getTalentLevel(ent, TALENT_MORPHING); // if (!G_CanUseAbilities(ent, ent->myskills.abilities[BLOOD_SUCKER].current_level, para_cubecost)) // return; if (!V_CanUseAbilities(ent, BLOOD_SUCKER, para_cubecost, true)) return; if (HasFlag(ent)) { gi.cprintf(ent, PRINT_HIGH, "Can't morph while carrying flag!\n"); return; } V_ModifyMorphedHealth(ent, M_MYPARASITE, true); ent->wait = level.time + 0.5;// can't attack immediately ent->client->pers.inventory[power_cube_index] -= para_cubecost; ent->client->ability_delay = level.time + PARASITE_DELAY; ent->mtype = M_MYPARASITE; ent->s.modelindex = gi.modelindex ("models/monsters/parasite/tris.md2"); ent->s.modelindex2 = 0; ent->s.skinnum = 0; // decloak ent->svflags &= ~SVF_NOCLIENT; ent->client->cloaking = false; ent->client->cloakable = 0; ent->maxs[2] = 8; ent->viewheight = 0; ent->client->refire_frames = 0; // reset charged weapon ent->client->weapon_mode = 0; // reset weapon mode lasersight_off(ent); gi.sound (ent, CHAN_WEAPON, gi.soundindex("spells/morph.wav") , 1, ATTN_NORM, 0); }
void Cmd_PlayerToBerserk_f (edict_t *ent) { int cost = BERSERK_COST; if (debuginfo->value) gi.dprintf("DEBUG: %s just called Cmd_PlayerToBerserk_f()\n", ent->client->pers.netname); // try to switch back if (ent->mtype || PM_PlayerHasMonster(ent)) { // don't let a player-tank unmorph if they are cocooned if (ent->owner && ent->owner->inuse && ent->owner->movetype == MOVETYPE_NONE) return; if (que_typeexists(ent->curses, 0)) { safe_cprintf(ent, PRINT_HIGH, "You can't morph while cursed!\n"); return; } V_RestoreMorphed(ent, 0); return; } //Talent: Morphing if (vrx_get_talent_slot(ent, TALENT_MORPHING) != -1) cost *= 1.0 - 0.25 * vrx_get_talent_level(ent, TALENT_MORPHING); if (!V_CanUseAbilities(ent, BERSERK, cost, true)) return; if (HasFlag(ent) && !hw->value) { safe_cprintf(ent, PRINT_HIGH, "Can't morph while carrying flag!\n"); return; } V_ModifyMorphedHealth(ent, MORPH_BERSERK, true); ent->monsterinfo.attack_finished = level.time + 0.5;// can't attack immediately ent->client->pers.inventory[power_cube_index] -= cost; ent->client->ability_delay = level.time + BERSERK_DELAY; ent->mtype = MORPH_BERSERK; ent->s.modelindex = gi.modelindex ("models/monsters/berserk/tris.md2"); ent->s.modelindex2 = 0; ent->s.skinnum = 0; // undo crouching / ducked state // try asking their client to get up stuffcmd(ent, "-movedown\n"); // if their client ignores the command, force them up if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) { ent->client->ps.pmove.pm_flags &= ~PMF_DUCKED; ent->viewheight = 22; ent->maxs[2] += 28; } ent->client->refire_frames = 0; // reset charged weapon ent->client->weapon_mode = 0; // reset weapon mode ent->client->pers.weapon = NULL; ent->client->ps.gunindex = 0; lasersight_off(ent); gi.sound(ent, CHAN_WEAPON, gi.soundindex("abilities/morph.wav"), 1, ATTN_NORM, 0); }
void cmd_Drink(edict_t *ent, int itemtype, int index) { int i; item_t *slot = NULL; qboolean found = false; //Don't drink too many potions too quickly if (ent->client->ability_delay > level.time) return; if (ctf->value && ctf_enable_balanced_fc->value && HasFlag(ent)) return; if (index) { slot = &ent->myskills.items[index-1]; found = true; } else { //Find item in inventory for (i = 3; i < MAX_VRXITEMS; ++i) { if (ent->myskills.items[i].itemtype == itemtype) { slot = &ent->myskills.items[i]; found = true; break; } } } if (!found) { safe_cprintf(ent, PRINT_HIGH, "You have none in stock.\n"); return; } //use it switch(itemtype) { case ITEM_POTION: { int max_hp = MAX_HEALTH(ent); if (ent->health < max_hp) { //Use the potion ent->health += max_hp/* / 3*/; // make them useful once again vrx chile 1.4 if (ent->health > max_hp) ent->health = max_hp; //You can only drink 1/sec ent->client->ability_delay = level.time + 3.3; //Play sound gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/potiondrink.wav"), 1, ATTN_NORM, 0); //Consume a potion slot->quantity--; safe_cprintf(ent, PRINT_HIGH, "You drank a potion. Potions left: %d\n", slot->quantity); if (slot->quantity < 1) V_ItemClear(slot); } else safe_cprintf(ent, PRINT_HIGH, "You don't need to drink this yet, you have lots of health.\n"); return; } break; case ITEM_ANTIDOTE: { if (que_typeexists(ent->curses, 0)) { int i; //Use the potion for (i = 0; i < QUE_MAXSIZE; ++i) { que_t *curse = &ent->curses[i]; if ((curse->ent && curse->ent->inuse) && (curse->ent->atype != HEALING && curse->ent->atype != BLESS)) { //destroy the curse if (curse->ent->enemy && (curse->ent->enemy == ent)) G_FreeEdict(curse->ent); // remove entry from the queue curse->time = 0; curse->ent = NULL; } } //Give them a short period of curse immunity ent->holywaterProtection = level.time + 5.0; //5 seconds immunity //You can only drink 1/sec ent->client->ability_delay = level.time + 1.0; //Play sound gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/rare.wav"), 1, ATTN_NORM, 0); //Consume a potion slot->quantity--; safe_cprintf(ent, PRINT_HIGH, "You used some holywater. Vials left: %d\n", slot->quantity); if (slot->quantity < 1) V_ItemClear(slot); } else safe_cprintf(ent, PRINT_HIGH, "You are not cursed, so you don't need to use this yet.\n"); return; } break; } }
void ParasiteAttack (edict_t *ent) { int damage, kick; vec3_t forward, right, start, end, offset; trace_t tr; if (debuginfo->value) gi.dprintf("%s just called ParasiteAttack()\n", ent->client->pers.userinfo); // terminate attack if (!G_EntIsAlive(ent) || (ent->parasite_frames > PARASITE_MAXFRAMES) || que_typeexists(ent->curses, CURSE_FROZEN)) { parasite_endattack(ent); return; } // calculate starting point AngleVectors (ent->client->v_angle, forward, right, NULL); VectorSet(offset, 0, 7, ent->viewheight-8); P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start); // do we already have a valid target? if (G_ValidTarget(ent, ent->parasite_target, true) && (entdist(ent, ent->parasite_target) <= PARASITE_RANGE) && infov(ent, ent->parasite_target, 90)) { VectorSubtract(ent->parasite_target->s.origin, start, forward); VectorNormalize(forward); } VectorMA(start, PARASITE_RANGE, forward, end); tr = gi.trace(start, NULL, NULL, end, ent, MASK_SHOT); // did we hit something? if (G_EntIsAlive(tr.ent) && !OnSameTeam(ent, tr.ent)) { if (ent->parasite_frames == 0) { ent->client->pers.inventory[power_cube_index] -= PARASITE_COST; gi.sound (ent, CHAN_AUTO, gi.soundindex("parasite/paratck3.wav"), 1, ATTN_NORM, 0); ent->client->ability_delay = level.time + PARASITE_DELAY; } ent->parasite_target = tr.ent; damage = 2*ent->myskills.abilities[BLOOD_SUCKER].current_level; if (tr.ent->groundentity) kick = -100; else kick = -50; T_Damage(tr.ent, ent, ent, forward, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_NO_ABILITIES, MOD_PARASITE); gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_PARASITE_ATTACK); gi.WriteShort(ent-g_edicts); gi.WritePosition(start); gi.WritePosition(tr.endpos); gi.multicast(ent->s.origin, MULTICAST_PVS); ent->parasite_frames++; } else if (ent->parasite_frames) parasite_endattack(ent); }
void minisentry_attack (edict_t *self) { int speed, dmg_radius; float chance; vec3_t forward, start, aim; qboolean slowed=false; minisentry_lockon(self); if (self->light_level < 1) return; // out of ammo // are we affected by holy freeze? //if (HasActiveCurse(self, AURA_HOLYFREEZE)) if (que_typeexists(self->curses, AURA_HOLYFREEZE)) slowed = true; // calculate muzzle location AngleVectors(self->s.angles, forward, NULL, NULL); VectorCopy(self->s.origin, start); if (self->owner && self->owner->style == SENTRY_FLIPPED) start[2] -= abs(self->mins[2]); else start[2] += self->maxs[2]; VectorMA(start, (self->maxs[0] + 16), forward, start); if ((level.time > self->wait) && infov(self, self->enemy, 30)) { speed = 650 + 35*self->creator->myskills.abilities[BUILD_SENTRY].current_level; MonsterAim(self, -1, speed, true, 0, aim, start); dmg_radius = self->radius_dmg; if (dmg_radius > 150) dmg_radius = 150; fire_rocket (self, start, aim, self->radius_dmg, speed, dmg_radius, self->radius_dmg); if (slowed) self->wait = level.time + 2.0; else if (self->chill_time > level.time) self->wait = level.time + (1.0 * (1 + CHILL_DEFAULT_BASE + CHILL_DEFAULT_ADDON * self->chill_level)); else self->wait = level.time + 1.0; } self->light_level--; // decrease ammo if (slowed && !(level.framenum%2)) return; // chill effect reduces attack rate/refire if (self->chill_time > level.time) { chance = 1 / (1 + CHILL_DEFAULT_BASE + CHILL_DEFAULT_ADDON * self->chill_level); if (random() > chance) return; } fire_bullet (self, start, forward, self->dmg, 2*self->dmg, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_SENTRY); gi.sound(self, CHAN_WEAPON, gi.soundindex("weapons/plaser.wav"), 1, ATTN_NORM, 0); gi.WriteByte (svc_muzzleflash); gi.WriteShort (self-g_edicts); gi.WriteByte (MZ_IONRIPPER|MZ_SILENCED); gi.multicast (start, MULTICAST_PVS); }