void deflect_think (edict_t *self) { edict_t *player = G_GetClient(self->enemy); //Find my slot que_t *slot = NULL; slot = que_findtype(self->enemy->curses, NULL, DEFLECT); // Blessing self-terminates if the enemy dies or the duration expires if (!slot || !que_valident(slot)) { if (player && level.time >= self->monsterinfo.selected_time) gi.cprintf(player, PRINT_HIGH, "Deflect has expired\n"); que_removeent(self->enemy->curses, self, true); return; } // warn player that deflect is about to expire // if (player && !(level.framenum % 10) && (level.time >= slot->time - 5)) // gi.cprintf(player, PRINT_HIGH, "Deflect will expire in %.0f seconds\n", slot->time - level.time); //Stick with the target VectorCopy(self->enemy->s.origin, self->s.origin); gi.linkentity(self); DeflectProjectiles(self->enemy, self->random, false); //Next think self->nextthink = level.time + FRAMETIME; }
void curse_think(edict_t *self) { //Find my curse slot que_t *slot = NULL; slot = que_findtype(self->enemy->curses, NULL, self->atype); // curse self-terminates if the enemy dies or the duration expires if (!slot || !que_valident(slot)) { que_removeent(self->enemy->curses, self, true); return; } CurseEffects(self->enemy, 10, 242); //Stick with the target VectorCopy(self->enemy->s.origin, self->s.origin); gi.linkentity(self); //Next think time self->nextthink = level.time + FRAMETIME; LifeDrain(self);// 3.5 this must be called last, because it may free the curse ent Bleed(self);//4.2 }
void Bless_think(edict_t *self) { //Find my slot que_t *slot = NULL; slot = que_findtype(self->enemy->curses, NULL, BLESS); // Blessing self-terminates if the enemy dies or the duration expires if (!slot || !que_valident(slot)) { self->enemy->superspeed = false; que_removeent(self->enemy->curses, self, true); return; } //Stick with the target VectorCopy(self->enemy->s.origin, self->s.origin); gi.linkentity(self); //give them super speed self->enemy->superspeed = true; //Next think self->nextthink = level.time + FRAMETIME; }
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 salvation_think (edict_t *self) { int radius; edict_t *other=NULL; que_t *slot=NULL; // check status of owner if (!CheckAuraOwner(self, COST_FOR_SALVATION)) { que_removeent(self->owner->auras, self, true); return; } // use cubes if (!(level.framenum % DEFAULT_AURA_FRAMES)) { int cube_cost = DEFAULT_AURA_COST; self->owner->client->pers.inventory[power_cube_index] -= cube_cost; } que_addent(self->owner->auras, self, DEFAULT_AURA_DURATION); // move aura with owner VectorCopy(self->owner->s.origin,self->s.origin); self->nextthink = level.time + FRAMETIME; if (level.framenum % DEFAULT_AURA_SCAN_FRAMES) return; radius = 256; // scan for targets while ((other = findradius (other, self->s.origin, radius)) != NULL) { slot = NULL; if (other == self->owner) continue; if (!G_EntExists(other)) continue; if (other->health < 1) continue; if (OnSameTeam(self->owner, other) < 2) continue; if (!visible(self->owner, other)) continue; slot = que_findtype(other->auras, slot, AURA_SALVATION); if (slot && (slot->ent->owner != self->owner)) continue; que_addent(other->auras, self, DEFAULT_AURA_DURATION); } }
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 Cmd_Salvation(edict_t *ent) { que_t *slot=NULL; qboolean sameaura=false; if (debuginfo->value) gi.dprintf("DEBUG: %s just called Cmd_Salvation()\n", ent->client->pers.netname); if(ent->myskills.abilities[SALVATION].disable) return; if (!G_CanUseAbilities(ent, ent->myskills.abilities[SALVATION].current_level, 0)) return; // if we already had an aura on, remove it if ((slot = que_findtype(ent->auras, slot, AURA_SALVATION)) != NULL) { // owner is turning off his own aura if (slot->ent && slot->ent->owner && slot->ent->owner->inuse && slot->ent->owner == ent) { AuraRemove(ent, AURA_SALVATION); safe_cprintf(ent, PRINT_HIGH, "Salvation removed.\n"); return; } AuraRemove(ent, AURA_SALVATION); } 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/salvation.wav"), 1, ATTN_NORM, 0); safe_cprintf(ent, PRINT_HIGH, "Now using salvation aura.\n"); aura_salvation(ent); }
void PlaceLaser (edict_t *ent) { edict_t *laser, *grenade; edict_t *blip = NULL;//GHz vec3_t forward, wallp, start, end; trace_t tr; trace_t endTrace; int health=0; if (debuginfo->value) gi.dprintf("DEBUG: %s just called PlaceLaser()\n", ent->client->pers.netname); health = LASER_INITIAL_HEALTH+LASER_ADDON_HEALTH*ent->myskills.abilities[BUILD_LASER].current_level; // valid ent ? if ((!ent->client) || (ent->health<=0)) return; if ((deathmatch->value) && (level.time < pregame_time->value)) { if (ent->client) safe_cprintf(ent, PRINT_HIGH, "You cannot use this ability in pre-game!\n"); return; } if (Q_strcasecmp (gi.args(), "remove") == 0) { RemoveLaserDefense(ent); return; } if(ent->myskills.abilities[BUILD_LASER].disable) return; //3.0 amnesia disables lasers if (que_findtype(ent->curses, NULL, AMNESIA) != NULL) return; if (ent->myskills.abilities[BUILD_LASER].current_level < 1) { safe_cprintf(ent, PRINT_HIGH, "You can't make lasers due to not training in it!\n"); return; } // cells for laser ? if (ent->client->pers.inventory[power_cube_index] < LASER_COST) { safe_cprintf(ent, PRINT_HIGH, "Not enough Power Cubes for laser.\n"); return; } if (ent->client->ability_delay > level.time) { safe_cprintf (ent, PRINT_HIGH, "You can't use abilities for another %2.1f seconds\n", ent->client->ability_delay - level.time); return; } ent->client->ability_delay = level.time + DELAY_LASER; //gi.dprintf("DEBUG: %s is attempting to place a laser...\n", ent->client->pers.netname); // GHz: Reached max number allowed ? if (ent->num_lasers >= MAX_LASERS) { safe_cprintf(ent, PRINT_HIGH, "You have reached the max of %d lasers\n", MAX_LASERS); return; } // Setup "little look" to close wall VectorCopy(ent->s.origin,wallp); // Cast along view angle AngleVectors (ent->client->v_angle, forward, NULL, NULL); // Setup end point wallp[0]=ent->s.origin[0]+forward[0]*128; wallp[1]=ent->s.origin[1]+forward[1]*128; wallp[2]=ent->s.origin[2]+forward[2]*128; // trace tr = gi.trace (ent->s.origin, NULL, NULL, wallp, ent, MASK_SOLID); // Line complete ? (ie. no collision) if (tr.fraction == 1.0) { safe_cprintf (ent, PRINT_HIGH, "Too far from wall.\n"); return; } // Hit sky ? if (tr.surface) if (tr.surface->flags & SURF_SKY) return; /* while (blip = findradius (blip, ent->s.origin, 64)) { if (!visible(ent, blip)) continue; if ( (!strcmp(blip->classname, "worldspawn") ) || (!strcmp(blip->classname, "info_player_start") ) || (!strcmp(blip->classname, "info_player_deathmatch") ) || (!strcmp(blip->classname, "item_flagreturn_team1") ) || (!strcmp(blip->classname, "item_flagreturn_team2") ) || (!strcmp(blip->classname, "misc_teleporter_dest") ) || (!strcmp(blip->classname, "info_teleport_destination") ) ) { safe_cprintf (ent, PRINT_HIGH, "Laser is too close to a spawnpoint or flag.\n"); return ; } } */ // Ok, lets stick one on then ... safe_cprintf (ent, PRINT_HIGH, "Laser attached.\n"); /* if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) ) { ent->client->pers.inventory[power_cube_index] -= LASER_COST; ent->client->pers.inventory[ITEM_INDEX(FindItem("Lasers"))]--; }*/ // get entities for both objects grenade = G_Spawn(); laser = G_Spawn(); // setup the Grenade VectorClear (grenade->mins); VectorClear (grenade->maxs); VectorCopy (tr.endpos, grenade->s.origin); vectoangles(tr.plane.normal, grenade->s.angles); grenade -> movetype = MOVETYPE_NONE; grenade -> clipmask = MASK_SHOT; grenade->solid = SOLID_BBOX; VectorSet(grenade->mins, -3, -3, 0); VectorSet(grenade->maxs, 3, 3, 6); grenade -> takedamage = DAMAGE_NO; grenade -> s.modelindex = gi.modelindex ("models/objects/grenade2/tris.md2"); grenade -> owner = ent; grenade -> creator = laser; grenade -> monsterinfo.aiflags = AI_NOSTEP; grenade -> classname = "laser_defense_gr"; grenade -> nextthink = level.time + LASER_TIMEUP+GetRandom(0,30);//GetRandom((LASER_TIMEUP/2),(2*LASER_TIMEUP)); grenade -> think = laser_cleanup; // Now lets find the other end of the laser // by starting at the grenade position VectorCopy (grenade->s.origin, start); // setup laser movedir (projection of laser) G_SetMovedir (grenade->s.angles, laser->movedir); gi.linkentity (grenade); VectorMA (start, 2048, laser->movedir, end); endTrace = gi.trace (start, NULL, NULL, end, ent, MASK_SOLID); // ----------- // Setup laser // ----------- laser->movetype = MOVETYPE_NONE; laser->solid = SOLID_NOT; laser->s.renderfx = RF_BEAM|RF_TRANSLUCENT; laser->s.modelindex = 1; // must be non-zero laser->s.sound = gi.soundindex ("world/laser.wav"); laser->classname = "laser_defense"; laser->s.frame = 2 /* ent->myskills.build_lasers*/; // as it gets higher in levels, the bigger it gets. beam diameter laser->owner = laser; laser->s.skinnum = laser_colour[LASER_DEFENSE_COLOR]; laser->dmg = LASER_INITIAL_DMG+LASER_ADDON_DMG*ent->myskills.abilities[BUILD_LASER].current_level; laser->think = pre_target_laser_def_think; //laser->delay = level.time + LASER_TIMEUP; laser->health = health; laser->creator = grenade; laser->activator = ent; // start off ... target_laser_off (laser); VectorCopy (endTrace.endpos, laser->s.old_origin); // ... but make automatically come on laser -> nextthink = level.time + 2; // Set orgin of laser to point of contact with wall VectorCopy(endTrace.endpos,laser->s.origin); /* while (blip = findradius (blip, laser->s.origin, 64)) { if (!visible(laser, blip)) continue; if ( (!strcmp(blip->classname, "worldspawn") ) || (!strcmp(blip->classname, "info_player_start") ) || (!strcmp(blip->classname, "info_player_deathmatch") ) || (!strcmp(blip->classname, "item_flagreturn_team1") ) || (!strcmp(blip->classname, "item_flagreturn_team2") ) || (!strcmp(blip->classname, "misc_teleporter_dest") ) || (!strcmp(blip->classname, "info_teleport_destination") ) ) { safe_cprintf (ent, PRINT_HIGH, "Laser is too close to a spawnpoint or flag.\nLaser Removed.\n"); G_FreeEdict(laser); G_FreeEdict(grenade); return ; } } */ // convert normal at point of contact to laser angles vectoangles(tr.plane.normal,laser->s.angles); // setup laser movedir (projection of laser) G_SetMovedir (laser->s.angles, laser->movedir); VectorSet (laser->mins, -18, -18, -18); VectorSet (laser->maxs, 18, 18, 18); // link to world gi.linkentity (laser); ent->num_lasers++; // GHz: add to laser counter //If you use this spell, you uncloak! ent->svflags &= ~SVF_NOCLIENT; ent->client->cloaking = false; ent->client->cloakable = 0; ent->client->pers.inventory[power_cube_index] -= LASER_COST; //gi.dprintf("DEBUG: %s successfully created a laser.\n", ent->client->pers.netname); }
void M_MoveFrame (edict_t *self) { mmove_t *move; int index; // int frames; float temp; // edict_t *curse; que_t *slot=NULL; if (!self->inuse) return; move = self->monsterinfo.currentmove; self->nextthink = level.time + FRAMETIME; if ((self->monsterinfo.nextframe) && (self->monsterinfo.nextframe >= move->firstframe) && (self->monsterinfo.nextframe <= move->lastframe)) { self->s.frame = self->monsterinfo.nextframe; self->monsterinfo.nextframe = 0; } else { if (self->s.frame == move->lastframe) { if (move->endfunc) { move->endfunc (self); // regrab move, endfunc is very likely to change it move = self->monsterinfo.currentmove; // check for death if (self->svflags & SVF_DEADMONSTER) return; } } if (self->s.frame < move->firstframe || self->s.frame > move->lastframe) { self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; self->s.frame = move->firstframe; } else { if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME)) { self->s.frame++; if (self->s.frame > move->lastframe) self->s.frame = move->firstframe; } } } index = self->s.frame - move->firstframe; if (move->frame[index].aifunc) if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME)) { self->monsterinfo.scale = 1.0; //4.5 monster bonus flags if (self->monsterinfo.bonus_flags & BF_FANATICAL) self->monsterinfo.scale *= 2.0; if (self->monsterinfo.bonus_flags & BF_GHOSTLY) self->monsterinfo.scale *= 0.5; // is this monster slowed by the holyfreeze aura? slot = que_findtype(self->curses, slot, AURA_HOLYFREEZE); if (slot) { temp = 1 / (1 + 0.1 * slot->ent->owner->myskills.abilities[HOLY_FREEZE].current_level); if (temp < 0.25) temp = 0.25; self->monsterinfo.scale *= temp; } // chill effect slows monster movement rate if(self->chill_time > level.time) self->monsterinfo.scale *= 1 / (1 + CHILL_DEFAULT_BASE + CHILL_DEFAULT_ADDON * self->chill_level); // 3.5 weaken slows down target if ((slot = que_findtype(self->curses, NULL, WEAKEN)) != NULL) { temp = 1 / (1 + WEAKEN_SLOW_BASE + WEAKEN_SLOW_BONUS * slot->ent->owner->myskills.abilities[WEAKEN].current_level); self->monsterinfo.scale *= temp; } //caltrops if (self->slowed_time > level.time) self->monsterinfo.scale *= self->slowed_factor; move->frame[index].aifunc (self, move->frame[index].dist * self->monsterinfo.scale); } else { // we're not going anywhere! move->frame[index].aifunc (self, 0); } if (move->frame[index].thinkfunc) move->frame[index].thinkfunc (self); }
void holyfreeze_think (edict_t *self) { int radius; edict_t *target=NULL, *curse=NULL; que_t *slot; // check status of owner if (!CheckAuraOwner(self, DEFAULT_AURA_COST)) { //gi.dprintf("aura removed itself\n"); que_removeent(self->owner->auras, self, true); return; } // owner has an active aura que_addent(self->owner->auras, self, DEFAULT_AURA_DURATION); // use cubes if (!(level.framenum % DEFAULT_AURA_FRAMES)) { int cube_cost = DEFAULT_AURA_COST; self->owner->client->pers.inventory[power_cube_index] -= cube_cost; } // move aura with owner VectorCopy(self->owner->s.origin,self->s.origin); self->nextthink = level.time + FRAMETIME; if (level.framenum % DEFAULT_AURA_SCAN_FRAMES) return; // scan for targets radius = DEFAULT_AURA_MIN_RADIUS+self->owner->myskills.abilities[HOLY_FREEZE].current_level*DEFAULT_AURA_ADDON_RADIUS; if (radius > DEFAULT_AURA_MAX_RADIUS) radius = DEFAULT_AURA_MAX_RADIUS; while ((target = findradius (target, self->s.origin, radius)) != NULL) { slot = NULL; if (target == self->owner) continue; if (!G_ValidTarget(self->owner, target, true)) continue; // FIXME: make this into a loop search if we plan to allow // more than one curse of the same type slot = que_findtype(target->curses, slot, AURA_HOLYFREEZE); if (slot && (slot->ent->owner != self->owner)) { // gi.dprintf("already slowed by someone else\n"); continue; // already slowed by someone else } if (!slot) // aura doesn't exist in que or timed out { if (random() > 0.5) gi.sound(target, CHAN_ITEM, gi.soundindex("spells/blue1.wav"), 1, ATTN_NORM, 0); else gi.sound(target, CHAN_ITEM, gi.soundindex("spells/blue3.wav"), 1, ATTN_NORM, 0); } que_addent(target->curses, self, DEFAULT_AURA_DURATION); } }
void minisentry_think (edict_t *self) { float modifier, temp; que_t *slot=NULL; if (!self->owner || !self->owner->inuse) { minisentry_remove(self); return; } else if (self->removetime > 0) { qboolean converted=false; if (self->flags & FL_CONVERTED) converted = true; if (level.time > self->removetime) { // if we were converted, try to convert back to previous owner if (converted && self->prev_owner && self->prev_owner->inuse) { if (!ConvertOwner(self->prev_owner, self, 0, false)) { minisentry_remove(self); return; } } else { minisentry_remove(self); return; } } // warn the converted monster's current owner else if (converted && self->creator && self->creator->inuse && self->creator->client && (level.time > self->removetime-5) && !(level.framenum%10)) safe_cprintf(self->creator, PRINT_HIGH, "%s conversion will expire in %.0f seconds\n", V_GetMonsterName(self), self->removetime-level.time); } // sentry is stunned if (self->holdtime > level.time) { M_SetEffects(self); self->nextthink = level.time + FRAMETIME; return; } // toggle sentry spotlight if (level.daytime && self->flashlight) FL_make(self); else if (!level.daytime && !self->flashlight) FL_make(self); // is the sentry slowed by holy freeze? temp = self->yaw_speed; slot = que_findtype(self->curses, slot, AURA_HOLYFREEZE); if (slot) { modifier = 1 / (1 + 0.1 * slot->ent->owner->myskills.abilities[HOLY_FREEZE].current_level); if (modifier < 0.25) modifier = 0.25; self->yaw_speed *= modifier; } // chill effect slows sentry rotation speed if(self->chill_time > level.time) self->yaw_speed *= 1 / (1 + CHILL_DEFAULT_BASE + CHILL_DEFAULT_ADDON * self->chill_level); if (!self->enemy) { minisentry_regenerate(self); if (minisentry_findtarget(self)) minisentry_attack(self); else minisentry_idle(self); } else { if (G_ValidTarget(self, self->enemy, true) && (entdist(self, self->enemy)<=SENTRY_ATTACK_RANGE)) { minisentry_attack(self); } else { self->enemy = NULL; if (minisentry_findtarget(self)) { minisentry_attack(self); } else { minisentry_idle(self); VectorCopy(self->move_angles, self->s.angles); } } } self->yaw_speed = temp; // restore original yaw speed M_SetEffects(self); self->nextthink = level.time + FRAMETIME; }
void Cmd_Bless(edict_t *ent) { int radius; float duration, cooldown; edict_t *target = NULL; if (debuginfo->value) gi.dprintf("DEBUG: %s just called Cmd_Bless()\n", ent->client->pers.netname); if(ent->myskills.abilities[BLESS].disable) return; //if (!G_CanUseAbilities(ent, ent->myskills.abilities[BLESS].current_level, BLESS_COST)) // return; if (!V_CanUseAbilities(ent, BLESS, BLESS_COST, true)) return; radius = SHAMAN_CURSE_RADIUS_BASE + (SHAMAN_CURSE_RADIUS_BONUS * ent->myskills.abilities[BLESS].current_level); duration = BLESS_DURATION_BASE + (BLESS_DURATION_BONUS * ent->myskills.abilities[BLESS].current_level); //Blessing self? if (Q_strcasecmp(gi.argv(1), "self") == 0) { if (HasFlag(ent)) { gi.cprintf(ent, PRINT_HIGH, "Can't use this while carrying the flag!\n"); return; } if (!curse_add(ent, ent, BLESS, 0, duration)) { gi.cprintf(ent, PRINT_HIGH, "Unable to bless self.\n"); return; } target = ent; } else { target = curse_Attack(ent, BLESS, radius, duration, false); } if (target != NULL) { que_t *slot = NULL; //Finish casting the spell ent->client->ability_delay = level.time + BLESS_DELAY; ent->client->pers.inventory[power_cube_index] -= BLESS_COST; cooldown = 2.0 * duration; if (cooldown > 10.0) cooldown = 10.0; ent->myskills.abilities[BLESS].delay = level.time + cooldown; //Change the curse think to the bless think slot = que_findtype(target->curses, NULL, BLESS); if (slot) { slot->ent->think = Bless_think; slot->ent->nextthink = level.time + FRAMETIME; } //Notify the target if (target == ent) { gi.cprintf(target, PRINT_HIGH, "YOU HAVE BEEN BLESSED FOR %0.1f seconds!!\n", duration); } else if ((target->client) && !(target->svflags & SVF_MONSTER)) { gi.cprintf(target, PRINT_HIGH, "YOU HAVE BEEN BLESSED FOR %0.1f seconds!!\n", duration); gi.cprintf(ent, PRINT_HIGH, "Blessed %s for %0.1f seconds.\n", target->myskills.player_name, duration); } else { gi.cprintf(ent, PRINT_HIGH, "Blessed %s for %0.1f seconds.\n", target->classname, duration); } //Play the spell sound! gi.sound(target, CHAN_ITEM, gi.soundindex("curses/bless.wav"), 1, ATTN_NORM, 0); } }
void Cmd_Healing(edict_t *ent) { int radius; float duration; edict_t *target = NULL; if (debuginfo->value) gi.dprintf("DEBUG: %s just called Cmd_Healing()\n", ent->client->pers.netname); if(ent->myskills.abilities[HEALING].disable) return; if (!G_CanUseAbilities(ent, ent->myskills.abilities[HEALING].current_level, HEALING_COST)) return; radius = SHAMAN_CURSE_RADIUS_BASE + (SHAMAN_CURSE_RADIUS_BONUS * ent->myskills.abilities[HEALING].current_level); duration = HEALING_DURATION_BASE + (HEALING_DURATION_BONUS * ent->myskills.abilities[HEALING].current_level); //Blessing self? if (Q_strcasecmp(gi.argv(1), "self") == 0) { if (!curse_add(ent, ent, HEALING, 0, duration)) { gi.cprintf(ent, PRINT_HIGH, "Unable to bless self.\n"); return; } target = ent; } else { target = curse_Attack(ent, HEALING, radius, duration, false); } if (target != NULL) { que_t *slot = NULL; //Finish casting the spell ent->client->ability_delay = level.time + HEALING_DELAY; ent->client->pers.inventory[power_cube_index] -= HEALING_COST; //Change the curse think to the healing think slot = que_findtype(target->curses, NULL, HEALING); if (slot) { slot->ent->think = Healing_think; slot->ent->nextthink = level.time + FRAMETIME; } //Notify the target if (target == ent) { gi.cprintf(target, PRINT_HIGH, "YOU HAVE BEEN BLESSED WITH %0.1f seconds OF HEALING!!\n", duration); } else if ((target->client) && !(target->svflags & SVF_MONSTER)) { gi.cprintf(target, PRINT_HIGH, "YOU HAVE BEEN BLESSED WITH %0.1f seconds OF HEALING!!\n", duration); gi.cprintf(ent, PRINT_HIGH, "Blessed %s with healing for %0.1f seconds.\n", target->myskills.player_name, duration); } else { gi.cprintf(ent, PRINT_HIGH, "Blessed %s with healing for %0.1f seconds.\n", target->classname, duration); } //Play the spell sound! gi.sound(target, CHAN_ITEM, gi.soundindex("curses/prayer.wav"), 1, ATTN_NORM, 0); } }
void Healing_think(edict_t *self) { //Find my slot que_t *slot = NULL; int heal_amount = HEALING_HEAL_BASE + HEALING_HEAL_BONUS * self->owner->myskills.abilities[HEALING].current_level; float cooldown = 1.0; slot = que_findtype(self->enemy->curses, NULL, HEALING); // Blessing self-terminates if the enemy dies or the duration expires if (!slot || !que_valident(slot)) { que_removeent(self->enemy->curses, self, true); return; } //Stick with the target VectorCopy(self->enemy->s.origin, self->s.origin); gi.linkentity(self); //Next think time self->nextthink = level.time + cooldown; //Heal the target's armor if (!self->enemy->client) { //Check to make sure it's a monster if (!self->enemy->mtype) return; heal_amount = self->enemy->max_health * (0.01 * self->owner->myskills.abilities[HEALING].current_level); // 1% healed per level if (heal_amount > 100) heal_amount = 100; //Heal the momster's health self->enemy->health += heal_amount; if (self->enemy->health > self->enemy->max_health) self->enemy->health = self->enemy->max_health; if (self->enemy->monsterinfo.power_armor_type) { heal_amount = self->enemy->monsterinfo.power_armor_power * (0.01 * self->owner->myskills.abilities[HEALING].current_level); // 1% healed per level if (heal_amount > 100) heal_amount = 100; //Heal the monster's armor self->enemy->monsterinfo.power_armor_power += heal_amount; if (self->enemy->monsterinfo.power_armor_power > self->enemy->monsterinfo.max_armor) self->enemy->monsterinfo.power_armor_power = self->enemy->monsterinfo.max_armor; } } else { if (self->enemy->health < MAX_HEALTH(self->enemy)) { //Heal health self->enemy->health += heal_amount; if (self->enemy->health > MAX_HEALTH(self->enemy)) self->enemy->health = MAX_HEALTH(self->enemy); } if (self->enemy->client->pers.inventory[body_armor_index] < MAX_ARMOR(self->enemy)) { //Heal armor heal_amount *= 0.5; // don't heal as much armor if (heal_amount < 1) heal_amount = 1; self->enemy->client->pers.inventory[body_armor_index] += heal_amount; if (self->enemy->client->pers.inventory[body_armor_index] > MAX_ARMOR(self->enemy)) self->enemy->client->pers.inventory[body_armor_index] = MAX_ARMOR(self->enemy); } } }
qboolean curse_add(edict_t *target, edict_t *caster, int type, int curse_level, float duration) { edict_t *curse; que_t *slot = NULL; //Find out if this curse already exists slot = que_findtype(target->curses, NULL, type); if(slot != NULL) { //If the current curse in effect has a level greater than the caster's curse level //if (slot->ent->owner->myskills.abilities[type].current_level > caster->myskills.abilities[type].current_level) if (slot->ent->monsterinfo.level > curse_level)//4.4 //Can't re-curse this player return false; else { //Refresh the curse with the new level/ent/duration return que_addent(target->curses, slot->ent, duration); } } /* //Talent: Evil curse (improves curse duration) talentLevel = getTalentLevel(caster, TALENT_EVIL_CURSE); if(talentLevel > 0) { //Curses only if(type != BLESS && type != HEALING) duration *= 1.0 + 0.25 * talentLevel; } */ //Create the curse entity curse=G_Spawn(); curse->classname = "curse"; curse->solid = SOLID_NOT; curse->svflags |= SVF_NOCLIENT; curse->monsterinfo.level = curse_level;//4.2 curse->monsterinfo.selected_time = duration;//4.2 VectorClear(curse->velocity); VectorClear(curse->mins); VectorClear(curse->maxs); //Set curse type, target, and caster curse->owner = caster; curse->enemy = target; curse->atype = type; //First think in 1/2 a second curse->nextthink = level.time + FRAMETIME; curse->think = curse_think; //Set origin to target's origin VectorCopy(target->s.origin, curse->s.origin); gi.linkentity(curse); //Try to add the curse to the que if (!que_addent(target->curses, curse, duration)) { G_FreeEdict(curse); return false; } return true; }
void ApplyThrust (edict_t *ent) { int talentLevel, cost = JETPACK_AMMO; vec3_t forward, right; vec3_t pack_pos, jet_vector; if(ent->myskills.abilities[JETPACK].disable && level.time > pregame_time->value) return; //Talent: Flight if ((talentLevel = getTalentLevel(ent, TALENT_FLIGHT)) > 0) { int num; num = 0.4 * talentLevel; if (num < 1) num = 1; cost -= num; } //4.0 better jetpack check. if ((level.time > pregame_time->value) && !trading->value) // allow jetpack in pregame and trading if (!G_CanUseAbilities (ent, ent->myskills.abilities[JETPACK].current_level, cost) ) return; //can't use abilities (spawning sentry gun/drone/etc...) if (ent->holdtime > level.time) return; //4.07 can't use jetpack while being hurt if (ent->lasthurt+DAMAGE_ESCAPE_DELAY > level.time) return; //amnesia disables jetpack if (que_findtype(ent->curses, NULL, AMNESIA) != NULL) return; if (HasFlag(ent)) { safe_cprintf(ent, PRINT_HIGH, "Can't use this ability while carrying the flag!\n"); return; } if (ent->client->snipertime >= level.time) { safe_cprintf(ent, PRINT_HIGH, "You can't use jetpack while trying to snipe!\n"); return; } if (ent->client->pers.inventory[power_cube_index] >= cost || level.time < pregame_time->value) // pregame. { ent->client->thrustdrain ++; if (ent->client->thrustdrain == JETPACK_DRAIN) { if (level.time > pregame_time->value) // not pregame ent->client->pers.inventory[power_cube_index] -= cost; ent->client->thrustdrain = 0; } } else { ent->client->thrusting=0; return; } if (ent->velocity[2] < 350) { if (ent->groundentity) ent->velocity[2] = 150; ent->velocity[2] += 150; } AngleVectors(ent->client->v_angle, forward, right, NULL); VectorScale (forward, -7, pack_pos); VectorAdd (pack_pos, ent->s.origin, pack_pos); pack_pos[2] += (ent->viewheight); VectorScale (forward, -50, jet_vector); if (ent->client->next_thrust_sound < level.time) { // wow this check is stupid. /*if (ent->client) */ gi.sound (ent, CHAN_BODY, gi.soundindex("weapons/rockfly.wav"), 1, ATTN_NORM, 0); ent->client->next_thrust_sound=level.time+1.0; } ent->lastsound = level.framenum; }
void Cmd_Deflect_f(edict_t *ent) { float duration; edict_t *target = ent; // default target is self if (!V_CanUseAbilities(ent, DEFLECT, DEFLECT_COST, true)) return; duration = DEFLECT_INITIAL_DURATION + DEFLECT_ADDON_DURATION * ent->myskills.abilities[DEFLECT].current_level; // bless the tank instead of the noclipped player if (PM_PlayerHasMonster(ent)) target = target->owner; //Blessing self? if (Q_strcasecmp(gi.argv(1), "self") == 0) { if (!curse_add(target, ent, DEFLECT, 0, duration)) { gi.cprintf(ent, PRINT_HIGH, "Unable to bless self.\n"); return; } //target = ent; } else { target = curse_Attack(ent, DEFLECT, 512.0, duration, false); } if (target != NULL) { que_t *slot = NULL; //Finish casting the spell ent->client->ability_delay = level.time + DEFLECT_DELAY; ent->client->pers.inventory[power_cube_index] -= DEFLECT_COST; // ent->myskills.abilities[DEFLECT].delay = level.time + duration + DEFLECT_DELAY; //Change the curse think to the deflect think slot = que_findtype(target->curses, NULL, DEFLECT); if (slot) { slot->ent->think = deflect_think; slot->ent->nextthink = level.time + FRAMETIME; slot->ent->random = DEFLECT_INITIAL_PROJECTILE_CHANCE+DEFLECT_ADDON_HITSCAN_CHANCE*ent->myskills.abilities[DEFLECT].current_level; if (slot->ent->random > DEFLECT_MAX_PROJECTILE_CHANCE) slot->ent->random = DEFLECT_MAX_PROJECTILE_CHANCE; } //Notify the target if (target == ent) { gi.cprintf(target, PRINT_HIGH, "You have been blessed with deflect for %0.1f seconds!\n", duration); } else if ((target->client) && !(target->svflags & SVF_MONSTER)) { gi.cprintf(target, PRINT_HIGH, "You have been blessed with deflect for %0.1f seconds!\n\n", duration); gi.cprintf(ent, PRINT_HIGH, "Blessed %s with deflect for %0.1f seconds.\n", target->myskills.player_name, duration); } else { gi.cprintf(ent, PRINT_HIGH, "Blessed %s with deflect for %0.1f seconds.\n", V_GetMonsterName(target), duration); } //Play the spell sound! gi.sound(target, CHAN_ITEM, gi.soundindex("curses/prayer.wav"), 1, ATTN_NORM, 0); } }