Ejemplo n.º 1
0
void Cmd_CreateSupplyStation_f (edict_t *ent)
{
	edict_t *depot;
	vec3_t	start;

	int talentLevel, cost=STATION_COST;
	float skill_mult=1.0, cost_mult=1.0, delay_mult=1.0;//Talent: Rapid Assembly & Precision Tuning

	if (debuginfo->value)
		gi.dprintf("%s just called Cmd_CreateSupplyStation_f\n", ent->client->pers.netname);

	if (ent->supplystation)
	{
		depot_remove(ent->supplystation, ent, true);
		return;
	}

	if(ent->myskills.abilities[SUPPLY_STATION].disable)
		return;

	//Talent: Rapid Assembly
	talentLevel = getTalentLevel(ent, TALENT_RAPID_ASSEMBLY);
	if (talentLevel > 0)
		delay_mult -= 0.1 * talentLevel;
	//Talent: Precision Tuning
	else if ((talentLevel = getTalentLevel(ent, TALENT_PRECISION_TUNING)) > 0)
	{
		cost_mult += PRECISION_TUNING_COST_FACTOR * talentLevel;
		delay_mult += PRECISION_TUNING_DELAY_FACTOR * talentLevel;
		skill_mult += PRECISION_TUNING_SKILL_FACTOR * talentLevel;
	}
	cost *= cost_mult;

	if (!G_CanUseAbilities(ent, ent->myskills.abilities[SUPPLY_STATION].current_level, cost))
		return;
	//BuildSupplyStation(ent, cost, skill_mult, delay_mult);

	depot = BuildDepot(ent, skill_mult, delay_mult);
	if (!G_GetSpawnLocation(ent, 100, depot->mins, depot->maxs, start))
	{
		safe_cprintf(ent, PRINT_HIGH, "Not enough room to spawn supply station.\n");
		ent->supplystation = NULL;
		G_FreeEdict(depot);
		return;
	}

	VectorCopy(start, depot->s.origin);
	VectorCopy(ent->s.angles, depot->s.angles);
	depot->s.angles[PITCH] = 0;
	depot->s.angles[ROLL] = 0;
	gi.linkentity(depot);

	ent->client->ability_delay = level.time + DEPOT_DELAY * delay_mult;
	ent->client->pers.inventory[power_cube_index] -= cost;
	ent->holdtime = level.time + DEPOT_BUILD_TIME * delay_mult;
	gi.sound(depot, CHAN_ITEM, gi.soundindex("weapons/repair.wav"), 1, ATTN_NORM, 0);
}
Ejemplo n.º 2
0
void Cmd_Amnesia(edict_t *ent)
{
	int radius, talentLevel, cost=AMNESIA_COST;
	float duration;
	edict_t *target = NULL;

	if (debuginfo->value)
		gi.dprintf("DEBUG: %s just called Cmd_Amnesia()\n", ent->client->pers.netname);

	if(ent->myskills.abilities[AMNESIA].disable)
		return;

	//Talent: Cheaper Curses
	if ((talentLevel = getTalentLevel(ent, TALENT_CHEAPER_CURSES)) > 0)
		cost *= 1.0 - 0.1 * talentLevel;

	if (!G_CanUseAbilities(ent, ent->myskills.abilities[AMNESIA].current_level, cost))
		return;

	radius = SHAMAN_CURSE_RADIUS_BASE + (SHAMAN_CURSE_RADIUS_BONUS * ent->myskills.abilities[AMNESIA].current_level);
	duration = AMNESIA_DURATION_BASE + (AMNESIA_DURATION_BONUS * ent->myskills.abilities[AMNESIA].current_level);

	//Talent: Evil curse
	talentLevel = getTalentLevel(ent, TALENT_EVIL_CURSE);
	if(talentLevel > 0)
		duration *= 1.0 + 0.25 * talentLevel;

	target = curse_Attack(ent, AMNESIA, radius, duration, true);
	if (target != NULL)
	{
		//Finish casting the spell
		ent->client->ability_delay = level.time + AMNESIA_DELAY;
		ent->client->pers.inventory[power_cube_index] -= cost;

		//disable some abilities if the player was using them
		V_DisableAllSkills(ent);

		//Notify the target
		if ((target->client) && !(target->svflags & SVF_MONSTER))
		{
			gi.cprintf(target, PRINT_HIGH, "YOU HAVE BEEN CURSED WITH AMNESIA!! (%0.1f seconds)\n", duration);
			gi.cprintf(ent, PRINT_HIGH, "Cursed %s with amnesia for %0.1f seconds.\n", target->myskills.player_name, duration);
		}
		else
		{
			gi.cprintf(ent, PRINT_HIGH, "Cursed %s with amnesia for %0.1f seconds.\n", target->classname, duration);
		}
		//Play the spell sound!
		gi.sound(target, CHAN_ITEM, gi.soundindex("curses/amnesia.wav"), 1, ATTN_NORM, 0);
	}
}
Ejemplo n.º 3
0
void Cmd_BuildLaser (edict_t *ent)
{
	int talentLevel, cost=LASER_COST;
	float skill_mult=1.0, cost_mult=1.0, delay_mult=1.0;//Talent: Rapid Assembly & Precision Tuning

	if(ent->myskills.abilities[BUILD_LASER].disable)
		return;

	if (Q_strcasecmp (gi.args(), "remove") == 0)
	{
		RemoveLasers(ent);
		gi.cprintf(ent, PRINT_HIGH, "All lasers removed.\n");
		return;
	}

	// cost is doubled if you are a flyer or cacodemon below skill level 5
	if ((ent->mtype == MORPH_FLYER && ent->myskills.abilities[FLYER].current_level < 5) 
		|| (ent->mtype == MORPH_CACODEMON && ent->myskills.abilities[CACODEMON].current_level < 5))
		cost *= 2;

	//Talent: Rapid Assembly
	talentLevel = getTalentLevel(ent, TALENT_RAPID_ASSEMBLY);
	if (talentLevel > 0)
		delay_mult -= 0.1 * talentLevel;
	//Talent: Precision Tuning
	else if ((talentLevel = getTalentLevel(ent, TALENT_PRECISION_TUNING)) > 0)
	{
		cost_mult += PRECISION_TUNING_COST_FACTOR * talentLevel;
		delay_mult += PRECISION_TUNING_DELAY_FACTOR * talentLevel;
		skill_mult += PRECISION_TUNING_SKILL_FACTOR * talentLevel;
	}
	cost *= cost_mult;

	if (!G_CanUseAbilities(ent, ent->myskills.abilities[BUILD_LASER].current_level, cost))
		return;

	if (ent->num_lasers >= MAX_LASERS)
	{
		gi.cprintf(ent, PRINT_HIGH, "Can't build any more lasers.\n");
		return;
	}

	if (ctf->value && (CTF_DistanceFromBase(ent, NULL, CTF_GetEnemyTeam(ent->teamnum)) < CTF_BASE_DEFEND_RANGE))
	{
		gi.cprintf(ent, PRINT_HIGH, "Can't build in enemy base!\n");
		return;
	}

	SpawnLaser(ent, cost, skill_mult, delay_mult);
}
Ejemplo n.º 4
0
//Upgrades the talent with a matching talentID
void upgradeTalent(edict_t *ent, int talentID)
{
	int slot = getTalentSlot(ent, talentID);
	talent_t *talent;

	if(slot == -1)
		return;

	talent = &ent->myskills.talents.talent[slot];

	// check for conflicting talents
	if (talentID == TALENT_RAPID_ASSEMBLY && getTalentLevel(ent, TALENT_PRECISION_TUNING) > 0)
	{
		gi.cprintf(ent, PRINT_HIGH, "Rapid Assembly can't be combined with Precision Tuning.\n");
		return;
	}
	if (talentID == TALENT_PRECISION_TUNING && getTalentLevel(ent, TALENT_RAPID_ASSEMBLY) > 0)
	{
		gi.cprintf(ent, PRINT_HIGH, "Precision Tuning can't be combined with Rapid Assembly.\n");
		return;
	}
	if (talentID == TALENT_CORPULENCE && getTalentLevel(ent, TALENT_LIFE_TAP) > 0)
	{
		gi.cprintf(ent, PRINT_HIGH, "Corpulence can't be combined with Life Tap.\n");
		return;
	}
	if (talentID == TALENT_LIFE_TAP && getTalentLevel(ent, TALENT_CORPULENCE) > 0)
	{
		gi.cprintf(ent, PRINT_HIGH, "Life Tap can't be combined with Corpulence.\n");
		return;
	}

	if(talent->upgradeLevel == talent->maxLevel)
	{
		gi.cprintf(ent, PRINT_HIGH, "You can not upgrade this talent any further.\n");
		return;
	}
	if(ent->myskills.talents.talentPoints < 1)
	{
		gi.cprintf(ent, PRINT_HIGH, "You do not have enough talent points.\n");
		return;
	}

    //We can upgrade.
	talent->upgradeLevel++;
	ent->myskills.talents.talentPoints--;
	gi.cprintf(ent, PRINT_HIGH, va("%s upgraded to level %d/%d.\n", GetTalentString(talent->id), talent->upgradeLevel, talent->maxLevel));
	gi.cprintf(ent, PRINT_HIGH, va("Talent points remaining: %d\n", ent->myskills.talents.talentPoints));
	savePlayer(ent);
}
Ejemplo n.º 5
0
int depot_getmax (edict_t *self, int item_index)
{
	int skill_level;

	skill_level = self->monsterinfo.level 
		* (1.0 + (0.2 * getTalentLevel(self->creator, TALENT_STORAGE_UPGRADE)));//Talent: Storage Upgrade

	// body armor
	if (item_index == body_armor_index)
		return (100 * skill_level);

	// bullets and cells (200-1200 max)
	if (item_index == bullet_index || item_index == cell_index)
		return (100 * skill_level);

	// shells (100-600 max)
	if (item_index == shell_index)
		return (50 * skill_level);

	// rockets, grenades, and slugs (50-300 max)
	if (item_index == rocket_index || item_index == grenade_index || item_index == slug_index)
		return 25 * skill_level;

	return 0;
}
Ejemplo n.º 6
0
float monster_increaseDamageByTalent(edict_t *owner, float damage)
{
	if(owner && owner->client)
	{
		//Talent: Corpulence
		int talentLevel = getTalentLevel(owner, TALENT_CORPULENCE);
		// corpulence increases health but reduces damage
		if(talentLevel > 0)	damage *= 1 / (1 + 0.1 * talentLevel);
		//Talent: Life Tap
		talentLevel = getTalentLevel(owner, TALENT_LIFE_TAP);
		if (talentLevel > 0) damage *= 1 + 0.05 * talentLevel;

		
	}
	return damage;
}
Ejemplo n.º 7
0
void Cmd_SpawnMagmine_f (edict_t *ent)
{
	int talentLevel,cost=MAGMINE_COST;
	float skill_mult=1.0, cost_mult=1.0, delay_mult=1.0;//Talent: Rapid Assembly & Precision Tuning
	char *opt = gi.argv(1);

	if (ent->myskills.abilities[MAGMINE].disable)
		return;

	//Talent: Rapid Assembly
	talentLevel = getTalentLevel(ent, TALENT_RAPID_ASSEMBLY);
	if (talentLevel > 0)
		delay_mult -= 0.1 * talentLevel;
	//Talent: Precision Tuning
	else if ((talentLevel = getTalentLevel(ent, TALENT_PRECISION_TUNING)) > 0)
	{
		cost_mult += PRECISION_TUNING_COST_FACTOR * talentLevel;
		delay_mult += PRECISION_TUNING_DELAY_FACTOR * talentLevel;
		skill_mult += PRECISION_TUNING_SKILL_FACTOR * talentLevel;
	}
	cost *= cost_mult;

	if (!G_CanUseAbilities(ent, ent->myskills.abilities[MAGMINE].current_level, cost))
		return;

	if (!strcmp(opt, "self"))
	{
		if (getTalentLevel(ent, TALENT_MAGMINESELF))
		{
			ent->automag = !ent->automag;
			safe_cprintf(ent, PRINT_HIGH, "Auto Magmine %s\n", ent->automag? "enabled" : "disabled");
		}else
			safe_cprintf(ent, PRINT_HIGH, "You haven't upgraded this talent.\n");

		return;
	}

	if (ent->magmine && ent->magmine->inuse)
	{
		safe_cprintf(ent, PRINT_HIGH, "Removed mag mine.\n");
		BecomeExplosion1(ent->magmine);
		ent->magmine = NULL;
		return;
	}

	magmine_spawn(ent, cost, skill_mult, delay_mult);
}
Ejemplo n.º 8
0
void Cmd_Curse(edict_t *ent)
{
	int range, radius, talentLevel, cost=CURSE_COST;
	float duration;
	edict_t *target = NULL;

	if (debuginfo->value)
		gi.dprintf("DEBUG: %s just called Cmd_Curse()\n", ent->client->pers.netname);

	//Talent: Cheaper Curses
	if ((talentLevel = getTalentLevel(ent, TALENT_CHEAPER_CURSES)) > 0)
		cost *= 1.0 - 0.1 * talentLevel;

	if (!V_CanUseAbilities(ent, CURSE, cost, true))
		return;

	range = CURSE_DEFAULT_INITIAL_RANGE + CURSE_DEFAULT_ADDON_RANGE * ent->myskills.abilities[CURSE].current_level;
	radius = CURSE_DEFAULT_INITIAL_RADIUS + CURSE_DEFAULT_ADDON_RADIUS * ent->myskills.abilities[CURSE].current_level;
	duration = CURSE_DURATION_BASE + (CURSE_DURATION_BONUS * ent->myskills.abilities[CURSE].current_level);

	//Talent: Evil curse
	talentLevel = getTalentLevel(ent, TALENT_EVIL_CURSE);
	if(talentLevel > 0)
		duration *= 1.0 + 0.25 * talentLevel;

	if (duration < 1)
		duration = 1;

	CurseRadiusAttack(ent, CURSE, range, radius, duration, true);

	//Finish casting the spell
	//ent->client->ability_delay = level.time + CURSE_DELAY;
	ent->myskills.abilities[CURSE].delay = level.time + CURSE_DELAY;
	ent->client->pers.inventory[power_cube_index] -= cost;

	target = curse_Attack(ent, CURSE, radius, duration, true);

	//Play the spell sound!
	gi.sound(ent, CHAN_ITEM, gi.soundindex("curses/curse.wav"), 1, ATTN_NORM, 0);
	
}
Ejemplo n.º 9
0
void Cmd_LowerResist (edict_t *ent)
{
	int range, radius, talentLevel, cost=LOWER_RESIST_COST;
	float duration;

	if (debuginfo->value)
		gi.dprintf("DEBUG: %s just called Cmd_LowerResist()\n", ent->client->pers.netname);

	//Talent: Cheaper Curses
	if ((talentLevel = getTalentLevel(ent, TALENT_CHEAPER_CURSES)) > 0)
		cost *= 1.0 - 0.1 * talentLevel;

	if (!V_CanUseAbilities(ent, LOWER_RESIST, cost, true))
		return;

	range = LOWER_RESIST_INITIAL_RANGE + LOWER_RESIST_ADDON_RANGE * ent->myskills.abilities[LOWER_RESIST].current_level;
	radius = LOWER_RESIST_INITIAL_RADIUS + LOWER_RESIST_ADDON_RADIUS * ent->myskills.abilities[LOWER_RESIST].current_level;
	duration = LOWER_RESIST_INITIAL_DURATION + LOWER_RESIST_ADDON_DURATION * ent->myskills.abilities[LOWER_RESIST].current_level;

	// evil curse talent
	talentLevel = getTalentLevel(ent, TALENT_EVIL_CURSE);
	if(talentLevel > 0)
		duration *= 1.0 + 0.25 * talentLevel;

	if (duration < 1)
		duration = 1;

	CurseRadiusAttack(ent, LOWER_RESIST, range, radius, duration, true);

	//Finish casting the spell
	//ent->client->ability_delay = level.time + LOWER_RESIST_DELAY;
	ent->myskills.abilities[LOWER_RESIST].delay = level.time + LOWER_RESIST_DELAY;
	ent->client->pers.inventory[power_cube_index] -= cost;

	//Play the spell sound!
	gi.sound(ent, CHAN_ITEM, gi.soundindex("curses/lowerresist.wav"), 1, ATTN_NORM, 0);

}
Ejemplo n.º 10
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;
}
Ejemplo n.º 11
0
void Cmd_Purge_f (edict_t *ent)
{
	//Talent: Purge
	int talentLevel = getTalentLevel(ent, TALENT_PURGE);

	if (talentLevel < 1)
	{
		safe_cprintf(ent, PRINT_HIGH, "You need to upgrade this talent before you can use it!\n");
		return;
	}
	
	if (ent->client->ability_delay > level.time)
	{
		safe_cprintf(ent, PRINT_HIGH, "You must wait %.1f seconds before you can use this talent.\n", 
			ent->client->ability_delay-level.time);
		return;
	}
	if (ent->client->pers.inventory[power_cube_index] < PURGE_COST)
	{
		safe_cprintf(ent, PRINT_HIGH, "You need %d cubes before you can use this talent.\n", 
			PURGE_COST-ent->client->pers.inventory[power_cube_index]);
		return;
	}

	//Give them a short period of total immunity
	ent->client->invincible_framenum = level.framenum + 3*talentLevel; //up to 2 seconds at level 5

	//Give them a short period of curse immunity
	ent->holywaterProtection = level.time + talentLevel; //up to 5 seconds at level 5

	//You can only drink 1/sec
	ent->client->ability_delay = level.time + PURGE_DELAY;

	//Remove all curses
	CurseRemove(ent, 0);

	ent->client->pers.inventory[power_cube_index] -= PURGE_COST;
	ent->client->ability_delay = level.time + PURGE_DELAY;

	gi.sound(ent, CHAN_AUTO, gi.soundindex("spells/purification.wav"), 1, ATTN_NORM, 0);
}
Ejemplo n.º 12
0
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;
}
Ejemplo n.º 13
0
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);
}
Ejemplo n.º 14
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);
}
Ejemplo n.º 15
0
void base_createturret (edict_t *self)
{
	edict_t		*sentry;
	vec3_t		end;
	trace_t		tr;
	int			casterlevel, talentLevel;
	float		ammo_mult=1.0;

	// make sure sentry has settled down
	if (!G_EntIsAlive(self->creator) || !minisentry_checkposition(self))
	{
		if (self->creator)
		{
			self->creator->num_sentries--;
			if (self->creator->num_sentries < 0)
				self->creator->num_sentries = 0;
		}
		BecomeExplosion1(self);
		return;
	}
	self->movetype = MOVETYPE_NONE; // lock down base
	self->takedamage = DAMAGE_NO; // the base is invulnerable

	// 3.8 base bbox no longer necessary, turret takes over
	VectorClear(self->mins);
	VectorClear(self->maxs);
	self->solid = SOLID_NOT;

	// create basic ent for sentry
	sentry = G_Spawn();
	sentry->creator = self->creator;
	sentry->owner = self; // the base becomes the owner
	VectorCopy(self->s.angles, sentry->s.angles);
	sentry->think = minisentry_think;
	sentry->nextthink = level.time + FRAMETIME;
	sentry->s.modelindex = gi.modelindex ("models/weapons/g_bfg/tris.md2");
	sentry->s.renderfx |= RF_IR_VISIBLE;
	// who really wanted to chase sentries anyway
	// sentry->flags |= FL_CHASEABLE; // 3.65 indicates entity can be chase cammed
	sentry->solid = SOLID_BBOX;
	sentry->movetype = MOVETYPE_NONE;
	sentry->clipmask = MASK_MONSTERSOLID;
	sentry->mass = 100;
	sentry->classname = "msentrygun";
	//sentry->viewheight = 16;
	sentry->takedamage = DAMAGE_AIM;
	sentry->mtype = M_MINISENTRY;
	sentry->touch = minisentry_touch;
	
	sentry->monsterinfo.power_armor_type = POWER_ARMOR_SHIELD;

	//Talent: Storage Upgrade
	talentLevel = getTalentLevel(self->creator, TALENT_STORAGE_UPGRADE);
	ammo_mult += 0.2 * talentLevel;

	// set ammo
	sentry->monsterinfo.jumpdn = SENTRY_MAX_AMMO * ammo_mult; // max ammo
	sentry->light_level = sentry->monsterinfo.jumpdn; // current ammo

	//get player's current_level for BUILD_SENTRY
	casterlevel = self->monsterinfo.level;
	sentry->monsterinfo.level = casterlevel; // used for adding monster exp
	sentry->monsterinfo.control_cost = 20; // used for adding monster exp

	//set health
	sentry->health = MINISENTRY_INITIAL_HEALTH + (MINISENTRY_ADDON_HEALTH * casterlevel);
	sentry->monsterinfo.power_armor_power = MINISENTRY_INITIAL_ARMOR + (MINISENTRY_ADDON_ARMOR * casterlevel);
//	if (sentry->health > MINISENTRY_MAX_HEALTH)
//		sentry->health = MINISENTRY_MAX_HEALTH;
	//if (sentry->monsterinfo.power_armor_power > MINISENTRY_MAX_ARMOR)
	//	sentry->monsterinfo.power_armor_power = MINISENTRY_MAX_ARMOR;
	sentry->max_health = sentry->health;
	sentry->monsterinfo.max_armor = sentry->monsterinfo.power_armor_power;

	//set damage
	sentry->dmg = MINISENTRY_INITIAL_BULLET + (MINISENTRY_ADDON_BULLET * casterlevel);// bullet damage
	sentry->radius_dmg = MINISENTRY_INITIAL_ROCKET + (MINISENTRY_ADDON_ROCKET * casterlevel); // rocket damage
	if (sentry->dmg > MINISENTRY_MAX_BULLET)
		sentry->dmg = MINISENTRY_MAX_BULLET;
	if (sentry->radius_dmg > MINISENTRY_MAX_ROCKET)
		sentry->radius_dmg = MINISENTRY_MAX_ROCKET;

	sentry->die = minisentry_die;
	sentry->pain = minisentry_pain;
	sentry->yaw_speed = 5;

	if (self->style == SENTRY_UPRIGHT)
	{
		VectorSet(sentry->mins, -28, -28, -12);
		VectorSet(sentry->maxs, 28, 28, 24);
		VectorCopy(self->s.origin, end);
		//end[2] += self->maxs[2] + sentry->mins[2] + 1;
		end[2] += abs(sentry->mins[2])+1;
	}
	else
	{
		VectorSet(sentry->mins, -28, -28, -24);
		VectorSet(sentry->maxs, 28, 28, 12);
		VectorCopy(self->s.origin, end);
		//end[2] -= abs(self->mins[2]) + sentry->maxs[2] + 1;
		end[2] -= sentry->maxs[2]+1;
	}

	// make sure position is valid
	tr = gi.trace(end, sentry->mins, sentry->maxs, end, sentry, MASK_SHOT);
	if (tr.contents & MASK_SHOT)
	{
		if (self->creator)
		{
			self->creator->num_sentries--;
			if (self->creator->num_sentries < 0)
				self->creator->num_sentries = 0;
		}
		//gi.dprintf("%s\n", tr.ent?tr.ent->classname:"null");
		BecomeExplosion1(self);
		BecomeExplosion1(sentry);
		return;
	}
	VectorCopy(tr.endpos, sentry->s.origin);
	VectorCopy(sentry->s.angles, sentry->move_angles);// save for idle animation
	gi.linkentity(sentry);
	
	gi.sound(sentry, CHAN_VOICE, gi.soundindex("weapons/turrset.wav"), 1, ATTN_NORM, 0);
	self->think = base_think; // base is done creating gun
	self->nextthink = level.time + FRAMETIME;
}
Ejemplo n.º 16
0
void Cmd_MiniSentry_f (edict_t *ent)
{
	int talentLevel, sentries=0, cost=SENTRY_COST;
	float skill_mult=1.0, cost_mult=1.0, delay_mult=1.0;//Talent: Rapid Assembly & Precision Tuning
	edict_t *scan=NULL;

	if (debuginfo->value)
		gi.dprintf("%s just called Cmd_MiniSentry_f\n", ent->client->pers.netname);

	if (ent->myskills.abilities[BUILD_SENTRY].disable)
		return;

	if (!Q_strcasecmp(gi.args(), "remove"))
	{
		RemoveMiniSentries(ent);
		return;
	}

	// 3.9 double sentry cost if there are too many sentries in CTF
	if (ctf->value)
	{
		sentries += 2*CTF_GetNumSummonable("Sentry_Gun", ent->teamnum);
		sentries += CTF_GetNumSummonable("msentrygun", ent->teamnum);

		if (sentries > MAX_MINISENTRIES)
			cost *= 2;
	}

	// cost is doubled if you are a flyer or cacodemon below skill level 5
	if ((ent->mtype == MORPH_FLYER && ent->myskills.abilities[FLYER].current_level < 5) 
		|| (ent->mtype == MORPH_CACODEMON && ent->myskills.abilities[CACODEMON].current_level < 5))
		cost *= 2;

	//Talent: Rapid Assembly
	talentLevel = getTalentLevel(ent, TALENT_RAPID_ASSEMBLY);
	if (talentLevel > 0)
		delay_mult -= 0.1 * talentLevel;
	//Talent: Precision Tuning
	else if ((talentLevel = getTalentLevel(ent, TALENT_PRECISION_TUNING)) > 0)
	{
		cost_mult += PRECISION_TUNING_COST_FACTOR * talentLevel;
		delay_mult += PRECISION_TUNING_DELAY_FACTOR * talentLevel;
		skill_mult += PRECISION_TUNING_SKILL_FACTOR * talentLevel;
	}
	cost *= cost_mult;

	if (!G_CanUseAbilities(ent, ent->myskills.abilities[BUILD_SENTRY].current_level, cost))
		return;

	if (ent->num_sentries >= MAX_MINISENTRIES)
	{
		safe_cprintf(ent, PRINT_HIGH, "You have reached the max of %d sentries\n", MAX_MINISENTRIES);
		return;
	}

	if (ctf->value && (CTF_DistanceFromBase(ent, NULL, CTF_GetEnemyTeam(ent->teamnum)) < CTF_BASE_DEFEND_RANGE))
	{
		safe_cprintf(ent, PRINT_HIGH, "Can't build in enemy base!\n");
		return;
	}

	SpawnMiniSentry(ent, cost, skill_mult, delay_mult);
}
Ejemplo n.º 17
0
void FireTotem_think(edict_t *self, edict_t *caster)
{
	int talentLevel;
	float chance;
	edict_t *target = NULL;

	//Totem should not work underwater (gee I wonder why).
	if(!self->waterlevel)
	{
		//Talent: Volcanic
		talentLevel = getTalentLevel(caster, TALENT_VOLCANIC);
		chance = 0.02 * talentLevel;

		//Find players in radius and attack them.
		while ((target = findclosestradius(target, self->s.origin, TOTEM_MAX_RANGE)) != NULL)
		{
			if (G_ValidTarget(self, target, true) && (self->s.origin[2]+64>target->s.origin[2]))
			{
				vec3_t forward, end;
				int damage = FIRETOTEM_DAMAGE_BASE + self->monsterinfo.level * FIRETOTEM_DAMAGE_MULT;
				int count = 10 + self->monsterinfo.level;
				int speed = 600;
				float	val, dist;
				qboolean fireball=false;

				// copy target location
				G_EntMidPoint(target, end);

				// calculate distance to target
				dist = distance(end, self->s.origin);

				// move our target point based on projectile and enemy velocity
				VectorMA(end, (float)dist/speed, target->velocity, end);

				//Talent: Volcanic - chance to shoot a fireball
				if (talentLevel > 0 && chance > random())
					fireball = true;

				// aim up
				if (fireball)
					val = ((dist/2048)*(dist/2048)*2048) + (end[2]-self->s.origin[2]);//4.4
				else
					val = ((dist/512)*(dist/512)*512) + (end[2]-self->s.origin[2]);
				if (val < 0)
					val = 0;
				end[2] += val;

				// calculate direction vector to target
				VectorSubtract(end, self->s.origin, forward);
				VectorNormalize(forward);
				
				// don't fire in a perfectly straight line
				forward[1] += 0.05*crandom(); 

				// spawn flames
				if (fireball)
					fire_fireball(self, self->s.origin, forward, 200, 125, 600, 5, 20);//4.4
				else
					ThrowFlame(self, self->s.origin, forward, distance(self->s.origin, target->s.origin), speed, damage, GetRandom(2, 4));
				
				self->lastsound = level.framenum;

				// refire delay
				self->delay = level.time + FRAMETIME;
			}
		}
	}

}
Ejemplo n.º 18
0
void SpawnLaser (edict_t *ent, int cost, float skill_mult, float delay_mult)
{
	int		talentLevel = getTalentLevel(ent, TALENT_RAPID_ASSEMBLY);//Talent: Rapid Assembly
	float	delay;
	vec3_t	forward, right, start, end, offset;
	trace_t	tr;
	edict_t *grenade, *laser;

	// get starting position and forward vector
	AngleVectors (ent->client->v_angle, forward, right, NULL);
	VectorSet(offset, 0, 8,  ent->viewheight-8);
	P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start);
	// get end position
	VectorMA(start, 64, forward, end);

	tr = gi.trace (start, NULL, NULL, end, ent, MASK_SOLID);

	// can't build a laser on sky
	if (tr.surface && (tr.surface->flags & SURF_SKY))
		return;

	if (tr.fraction == 1)
	{
		gi.cprintf(ent, PRINT_HIGH, "Too far from wall.\n");
		return;
	}

	if (NearbyLasers(ent, tr.endpos))
	{
		gi.cprintf(ent, PRINT_HIGH, "Too close to another laser.\n");
		return;
	}

	if (NearbyProxy(ent, tr.endpos))
	{
		gi.cprintf(ent, PRINT_HIGH, "Too close to a proxy grenade.\n");
		return;
	}

	laser = G_Spawn();
	grenade = G_Spawn();

	// create the laser beam
	laser->monsterinfo.level = ent->myskills.abilities[BUILD_LASER].current_level * skill_mult;
	laser->dmg = LASER_INITIAL_DAMAGE+LASER_ADDON_DAMAGE*laser->monsterinfo.level;
	laser->health = LASER_INITIAL_HEALTH+LASER_ADDON_HEALTH*laser->monsterinfo.level;

	// nerf lasers in CTF and invasion
	//if (ctf->value || invasion->value)
	//	laser->health *= 0.5;

	laser->max_health = laser->health;

	// set beam diameter
	if (laser->monsterinfo.level >= 10)
		laser->s.frame = 4;
	else
		laser->s.frame = 2;

	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";
    laser->owner = laser->activator = ent; // link to player
	laser->creator = grenade; // link to grenade
	laser->s.skinnum = 0xf2f2f0f0; // red beam color
    laser->think = laser_beam_think;
	laser->nextthink = level.time + LASER_SPAWN_DELAY * delay_mult;
	VectorCopy(ent->s.origin, laser->s.origin);
	VectorCopy(tr.endpos, laser->s.old_origin);
	VectorCopy(tr.endpos, laser->pos1); // beam origin
	vectoangles(tr.plane.normal, laser->s.angles);
	gi.linkentity(laser);

	delay = LASER_TIMEOUT_DELAY+GetRandom(0, (int)(0.5*LASER_TIMEOUT_DELAY));

	// laser times out faster in CTF because it's too strong
	//if (ctf->value || invasion->value)
	//	delay *= 0.5;

	// create the laser emmitter (grenade)
    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->activator = ent; // link to player
    grenade->creator = laser; // link to laser
	grenade->classname = "emitter";
	grenade->mtype = M_LASER;//4.5
	grenade->nextthink = level.time+FRAMETIME;//delay; // time before self-destruct
	//grenade->delay = level.time + delay;//4.4 time before self destruct
	grenade->think = emitter_think;//laser_remove;
	grenade->touch = laser_touch;//4.4
	gi.linkentity(grenade);

	// cost is doubled if you are a flyer or cacodemon below skill level 5
	if ((ent->mtype == MORPH_FLYER && ent->myskills.abilities[FLYER].current_level < 5) 
		|| (ent->mtype == MORPH_CACODEMON && ent->myskills.abilities[CACODEMON].current_level < 5))
		cost *= 2;

	ent->num_lasers++;
	gi.cprintf(ent, PRINT_HIGH, "Laser built. You have %d/%d lasers.\n", ent->num_lasers, MAX_LASERS);
	ent->client->pers.inventory[power_cube_index] -= cost;
	ent->client->ability_delay = level.time + 0.5 * delay_mult;
	ent->holdtime = level.time + 0.5 * delay_mult;

	ent->lastsound = level.framenum;
}
Ejemplo n.º 19
0
void NatureTotem_think(edict_t *self, edict_t *caster)
{
	edict_t *target = NULL;
	qboolean isSomeoneHealed = false;
	float cdmult = 1.0;

	//Find players in radius and attack them.
	while ((target = findradius(target, self->s.origin, TOTEM_MAX_RANGE)) != NULL)
	{
		if (G_ValidAlliedTarget(self, target, true))
		{
			int maxHP;// = MAX_HEALTH(target);
			int maxArmor;// = MAX_ARMOR(target);
			int maxcubes;// = target->client->pers.max_powercubes;
			int *armor;// = &target->client->pers.inventory[body_armor_index];
			int *cubes;// = &target->client->pers.inventory[ITEM_INDEX(Fdi_POWERCUBE)];
			int	regen_frames;//4.2 regen for non-clients
			
			if (!target->client)
			{
				regen_frames = 1000 / self->monsterinfo.level; // full-regeneration in 15 seconds at level 10
				M_Regenerate(target, regen_frames, 50, 1.0, true, true, false, &target->monsterinfo.regen_delay2);
				continue;
			}
			
			maxHP = MAX_HEALTH(target);
			maxArmor = MAX_ARMOR(target);
			maxcubes = target->client->pers.max_powercubes;
			armor = &target->client->pers.inventory[body_armor_index];
			cubes = &target->client->pers.inventory[ITEM_INDEX(Fdi_POWERCUBE)];

			//Heal their health.
			if(target->health < maxHP)
			{
				target->health += NATURETOTEM_HEALTH_BASE + self->monsterinfo.level * NATURETOTEM_HEALTH_MULT;
				if(target->health > maxHP)
					target->health = maxHP;
				isSomeoneHealed = true;
			}
			//Heal their armor.
			if(*armor < maxArmor)
			{
				*armor += NATURETOTEM_ARMOR_BASE + self->monsterinfo.level * NATURETOTEM_ARMOR_MULT;
				if(*armor > maxArmor)
					*armor = maxArmor;
				isSomeoneHealed = true;
			}

			//Talent: Peace.
			if(*cubes < maxcubes)
			{
				//Give them 5 cubes per talent point.
				*cubes += getTalentLevel(caster, TALENT_PEACE) * 10;
				if(*cubes > maxcubes)
					*cubes = maxcubes;
				isSomeoneHealed = true;
			}

			// We remove curses. -az
			CurseRemove(target, 0);
		}
	}

	//Play a sound or something.
	if(isSomeoneHealed)
	{
		gi.sound(self, CHAN_ITEM, gi.soundindex("items/n_health.wav"), 1, ATTN_NORM, 0);
	}

	cdmult = level.time + NATURETOTEM_REFIRE_BASE + NATURETOTEM_REFIRE_MULT * self->monsterinfo.level;

	if (cdmult <= level.time)
		cdmult = level.time + 0.1;

	//Next think.
	self->delay = cdmult;
}
Ejemplo n.º 20
0
void SpawnTotem(edict_t *ent, int abilityID)
{
	int			talentLevel, cost=TOTEM_COST;
	edict_t		*totem;
	int			totemType;
	vec3_t		start;//GHz 4.32

	// cost is doubled if you are a flyer or cacodemon below skill level 5
	if ((ent->mtype == MORPH_FLYER && ent->myskills.abilities[FLYER].current_level < 5) 
		|| (ent->mtype == MORPH_CACODEMON && ent->myskills.abilities[CACODEMON].current_level < 5))
		cost *= 2;

	if(!V_CanUseAbilities(ent, abilityID, cost, true))
		return;
	
	if (ctf->value && abilityID == FIRE_TOTEM 
		&& (CTF_DistanceFromBase(ent, NULL, CTF_GetEnemyTeam(ent->teamnum)) < CTF_BASE_DEFEND_RANGE))
	{
		safe_cprintf(ent, PRINT_HIGH, "Can't build in enemy base!\n");
		return;
	}

	//Determine the totem type.
	switch(abilityID)
	{
	case FIRE_TOTEM:		totemType = TOTEM_FIRE;		break;
	case WATER_TOTEM:		totemType = TOTEM_WATER;	break;
	case AIR_TOTEM:			totemType = TOTEM_AIR;		break;
	case EARTH_TOTEM:		totemType = TOTEM_EARTH;	break;
	case DARK_TOTEM:		totemType = TOTEM_DARKNESS;	break;
	case NATURE_TOTEM:		totemType = TOTEM_NATURE;	break;
	default: return;
	}

	//Can't create too many totems.
	if(ent->totem1)
	{
		//Can't have more than one totem without the talent.
		/*if(getTalentLevel(ent, TALENT_TOTEM) < 1)
		{
			safe_cprintf(ent, PRINT_HIGH, "You already have a totem active.\n");
			return;
		}
		//Can't have more than two totems.
		else*/ if(ent->totem2)
		{
			safe_cprintf(ent, PRINT_HIGH, "You already have two totems active.\n");
			return;
		}
		//Can't have two totems of opposite alignment.
		else
		{
			int opposite = 0;
			switch(ent->totem1->mtype)
			{
			case TOTEM_FIRE:		opposite = TOTEM_WATER;		break;
			case TOTEM_WATER:		opposite = TOTEM_FIRE;		break;
			case TOTEM_AIR:			opposite = TOTEM_EARTH;		break;
			case TOTEM_EARTH:		opposite = TOTEM_AIR;		break;
			case TOTEM_DARKNESS:	opposite = TOTEM_NATURE;	break;
			case TOTEM_NATURE:		opposite = TOTEM_DARKNESS;	break;
			}
			if(totemType == opposite)
			{
				safe_cprintf(ent, PRINT_HIGH, "You can't create two totems of opposite elemental alignment.\n");
				return;
			}
			else if(totemType == ent->totem1->mtype)
			{
				safe_cprintf(ent, PRINT_HIGH, "You can't create totems of the same type.\n");
				return;
			}
		}
	}

	//Drop a totem.
	totem = DropTotem(ent);
	totem->mtype = totemType;
	totem->monsterinfo.level = ent->myskills.abilities[abilityID].current_level;
	totem->classname = "totem";
	/*totem->owner =*/ totem->activator = ent;
	totem->think = totem_general_think;
	totem->touch = totem_touch;
	totem->nextthink = level.time + FRAMETIME*2;
	totem->delay = level.time + 0.5;
	totem->die = totem_die;

	//TODO: update this with the new model.
	totem->s.modelindex = gi.modelindex("models/items/mega_h/tris.md2");
	//totem->s.angles[ROLL] = 270;
	VectorSet (totem->mins, -8, -8, -12);
	VectorSet (totem->maxs, 8, 8, 16);
	VectorCopy(ent->s.origin, totem->s.origin);
	
	totem->health = TOTEM_HEALTH_BASE + TOTEM_HEALTH_MULT * totem->monsterinfo.level;

	//Talent: Totemic Focus - increases totem health
	if((talentLevel = getTalentLevel(ent, TALENT_TOTEM)) > 0)
		totem->health *= 1 + 0.1666 * talentLevel;

	if (totemType == TOTEM_FIRE)
	{
		// fire totem is much tougher
		totem->health *= 2;
		// fire totem has a longer delay
		totem->delay = level.time + 2.0;
	}

	totem->max_health = totem->health*2;

	//Not sure if this stuff is needed (Archer)
	totem->svflags |= SVF_MONSTER;
	totem->takedamage = DAMAGE_AIM;
	totem->clipmask = MASK_MONSTERSOLID;

	//Back to stuff we need
	totem->mass = 200;
	totem->movetype = MOVETYPE_TOSS;//MOVETYPE_WALK;
	totem->deadflag = DEAD_NO;
	totem->svflags &= ~SVF_DEADMONSTER;	
	totem->solid = SOLID_BBOX;

	//Graphical effects
	//TODO: update this to make it look better.
	totem->s.effects |= EF_PLASMA | EF_COLOR_SHELL | EF_SPHERETRANS;
	switch(totemType)
	{
	case TOTEM_FIRE:
		totem->s.effects |= 262144;		//red radius light
		totem->s.renderfx |= RF_SHELL_RED;
		break;
	case TOTEM_WATER:
		totem->s.effects |= 524288;		//blue radius light
		totem->s.renderfx |= RF_SHELL_CYAN;
		break;
	case TOTEM_AIR:
		totem->s.effects |= 64;			//bright light radius
		totem->s.renderfx |= RF_SHELL_RED | RF_SHELL_BLUE | RF_SHELL_GREEN;		
		break;
	case TOTEM_EARTH:
		totem->s.renderfx |= RF_SHELL_YELLOW;
		break;
	case TOTEM_DARKNESS:
		totem->s.effects |= 2147483648;		//strange darkness effect
		//totem->s.renderfx |= RF_SHELL_RED | RF_SHELL_BLUE;
		break;
	case TOTEM_NATURE:
		totem->s.effects |= 128;	//green radius light
		totem->s.renderfx |= RF_SHELL_GREEN;
		break;
	}
//GHz 4.32
	if (!G_GetSpawnLocation(ent, 64, totem->mins, totem->maxs, start))
	{
		G_FreeEdict(totem);
		return;
	}

	VectorCopy(start, totem->s.origin);
	gi.linkentity(totem);
//GHz
	if(!ent->totem1)	ent->totem1 = totem;
	else				ent->totem2 = totem;

	ent->client->pers.inventory[ITEM_INDEX(Fdi_POWERCUBE)] -= cost;

	// calling entity made a sound, used to alert monsters
	ent->lastsound = level.framenum;
	ent->client->ability_delay = level.time + 1.3;
}