Beispiel #1
0
/*---------------------------------------------------------------*/
static MATRIX calcTasUVectorFromAngles(tasReflection r){
  double theta, om;

  theta = calcTheta(r.qe.ki,r.qe.kf,r.angles.sample_two_theta);
  om = r.angles.a3 - theta;
  return uFromAngles(om,r.angles.sgu, r.angles.sgl);
}
Beispiel #2
0
unsigned char calcDirection(float x0, float y0, float x1, float y1) {
	float theta = calcTheta(x0, y0, x1, y1);
	float val = theta / (static_cast<float>(M_PI)/4);
	int dir = static_cast<int>(((val < 0) ? ceil(val-0.5) : floor(val+0.5)) + 4);
	dir = (dir + 1) % 8;
	if (dir >= 0 && dir < 8)
		return static_cast<unsigned char>(dir);
	else
		return 0;
}
Beispiel #3
0
/**
 * The activated power creates a group of missile hazards (e.g. arrow, thrown knife, firebolt).
 * Each individual missile is a single animated hazard that travels from the caster position to the
 * mouse target position.
 *
 * @param power_index The activated power ID
 * @param src_stats The StatBlock of the power activator
 * @param target The mouse cursor position in map coordinates
 * return boolean true if successful
 */
bool PowerManager::missile(int power_index, StatBlock *src_stats, Point target) {
	float pi = 3.1415926535898;

	Point src;
	if (powers[power_index].starting_pos == STARTING_POS_TARGET) {
		src.x = target.x;
		src.y = target.y;
	}
	else {
		src.x = src_stats->pos.x;
		src.y = src_stats->pos.y;
	}

	Hazard *haz;

	// calculate polar coordinates angle
	float theta = calcTheta(src.x, src.y, target.x, target.y);
	
	//generate hazards
	for (int i=0; i < powers[power_index].missile_num; i++) {
		haz = new Hazard();

		//calculate individual missile angle
		float offset_angle = ((1.0 - powers[power_index].missile_num)/2 + i) * (powers[power_index].missile_angle * pi / 180.0);
		float variance = 0;
		if (powers[power_index].angle_variance != 0)
			variance = pow(-1.0f, (rand() % 2) - 1) * (rand() % powers[power_index].angle_variance) * pi / 180.0; //random between 0 and angle_variance away
		float alpha = theta + offset_angle + variance;
		while (alpha >= pi+pi) alpha -= pi+pi;
		while (alpha < 0.0) alpha += pi+pi;

		initHazard(power_index, src_stats, target, haz);

		//calculate the missile velocity
		int speed_var = 0;
		if (powers[power_index].speed_variance != 0)
			speed_var = (int)(pow(-1.0f, (rand() % 2) - 1) * (rand() % powers[power_index].speed_variance + 1) - 1);
		haz->speed.x = (haz->base_speed + speed_var) * cos(alpha);
		haz->speed.y = (haz->base_speed + speed_var) * sin(alpha);
		
		//calculate direction based on trajectory, not actual target (UNITS_PER_TILE reduces round off error)
		if (powers[power_index].directional)
			haz->direction = calcDirection(src.x, src.y, src.x + UNITS_PER_TILE * haz->speed.x, src.y + UNITS_PER_TILE * haz->speed.y);
		
		hazards.push(haz);
	}

	// if all else succeeded, pay costs
	if (src_stats->hero && powers[power_index].requires_mp > 0) src_stats->mp -= powers[power_index].requires_mp;
	if (src_stats->hero && powers[power_index].requires_item != -1) used_item = powers[power_index].requires_item;

	playSound(power_index, src_stats);
	return true;
}
Beispiel #4
0
/**
 * Repeaters are multiple hazards that spawn in a straight line
 */
bool PowerManager::repeater(int power_index, StatBlock *src_stats, Point target) {

	
	// pay costs
	if (powers[power_index].requires_mp>0) src_stats->mp-=powers[power_index].requires_mp;
	used_item = powers[power_index].requires_item;
	
	//initialize variables
	Hazard *haz[10];
	FPoint location_iterator;
	FPoint speed;
	int delay_iterator;
	int map_speed = 64;

	// calculate polor coordinates angle
	float theta = calcTheta(src_stats->pos.x, src_stats->pos.y, target.x, target.y);

	speed.x = (float)map_speed * cos(theta);
	speed.y = (float)map_speed * sin(theta);

	location_iterator.x = (float)src_stats->pos.x;
	location_iterator.y = (float)src_stats->pos.y;
	delay_iterator = 0;

	playSound(power_index, src_stats);

	for (int i=0; i<powers[power_index].repeater_num; i++) {

		location_iterator.x += speed.x;
		location_iterator.y += speed.y;

		// only travels until it hits a wall
		if (collider->is_wall((int)location_iterator.x, (int)location_iterator.y)) {
			break; // no more hazards
		}
		
		haz[i] = new Hazard();
		initHazard(power_index, src_stats, target, haz[i]);

		haz[i]->pos.x = location_iterator.x;
		haz[i]->pos.y = location_iterator.y;
		haz[i]->delay_frames = delay_iterator;
		delay_iterator += powers[power_index].delay;
		
		haz[i]->frame = powers[power_index].start_frame; // start at bottom frame
		
		hazards.push(haz[i]);
	}

	return true;
	
}
Beispiel #5
0
/**
 * The activated power creates a group of missile hazards (e.g. arrow, thrown knife, firebolt).
 * Each individual missile is a single animated hazard that travels from the caster position to the
 * mouse target position.
 *
 * @param power_index The activated power ID
 * @param src_stats The StatBlock of the power activator
 * @param target The mouse cursor position in map coordinates
 * return boolean true if successful
 */
bool PowerManager::missile(int power_index, StatBlock *src_stats, Point target) {
	float pi = 3.1415926535898;

	Hazard *haz[powers[power_index].missile_num];

	// calculate polor coordinates angle
	float theta = calcTheta(src_stats->pos.x, src_stats->pos.y, target.x, target.y);
	
	//generate hazards
	for (int i=0; i < powers[power_index].missile_num; i++) {
		haz[i] = new Hazard();
		Point rot_target;

		//calculate individual missile angle
		float offset_angle = ((1.0 - powers[power_index].missile_num)/2 + i) * (powers[power_index].missile_angle * pi / 180.0);
		float variance = 0;
		if (powers[power_index].angle_variance != 0)
			variance = pow(-1, (rand() % 2) - 1) * (rand() % powers[power_index].angle_variance) * pi / 180.0; //random between 0 and angle_variance away
		float alpha = theta + offset_angle + variance;
		while (alpha >= pi+pi) alpha -= pi+pi;
		while (alpha < 0.0) alpha += pi+pi;

		//calculate animation direction (the UNITS_PER_TILE just reduces round-off error)
		rot_target.x = src_stats->pos.x + UNITS_PER_TILE * cos(alpha);
		rot_target.y = src_stats->pos.y + UNITS_PER_TILE * sin(alpha);

		initHazard(power_index, src_stats, rot_target, haz[i]);

		//calculate the missile velocity
		int speed_var = 0;
		if (powers[power_index].speed_variance != 0)
			speed_var = pow(-1, (rand() % 2) - 1) * (rand() % powers[power_index].speed_variance + 1) - 1;
		haz[i]->speed.x = (haz[0]->base_speed + speed_var) * cos(alpha);
		haz[i]->speed.y = (haz[0]->base_speed + speed_var) * sin(alpha);
		hazards.push(haz[i]);
	}

	// pay costs
	if (powers[power_index].requires_mp>0) src_stats->mp-=powers[power_index].requires_mp;
	used_item = powers[power_index].requires_item;

	playSound(power_index, src_stats);
	return true;
}
Beispiel #6
0
/*-------------------------------------------------------------------------------*/
int calcTasQAngles(MATRIX UB, MATRIX planeNormal, int ss,  tasQEPosition qe, 
		   ptasAngles angles){
  MATRIX R, QC;
  double om, q, theta, cos2t;
  int errorCode = 1;

  R = buildRMatrix(UB, planeNormal, qe, &errorCode);
  if(R == NULL){
    return errorCode;
  }
  

  angles->sgl = Asind(-R[2][0]);
  if(ABS(angles->sgl -90.) < .5){
  	mat_free(R);
    return BADUBORQ;
  }
  /*
    Now, this is slightly different then in the publication by M. Lumsden.
    The reason is that the atan2 helps to determine the sign of om
    whereas the sin, cos formula given by M. Lumsden yield ambiguous signs 
    especially for om.
    sgu = atan(R[2][1],R[2][2]) where:
      R[2][1] = cos(sgl)sin(sgu)
      R[2][2] = cos(sgu)cos(sgl)
    om = atan(R[1][0],R[0][0]) where:
      R[1][0] = sin(om)cos(sgl)
      R[0][0] = cos(om)cos(sgl)
    The definitions of th R components are taken from M. Lumsden
    R-matrix definition.
  */

  om = Atan2d(R[1][0],R[0][0]);
  angles->sgu = Atan2d(R[2][1],R[2][2]);

  QC = tasReflectionToQC(qe,UB);
  if(QC == NULL){
  	mat_free(R);
    return UBNOMEMORY;
  }

  q = vectorLength(QC);
  q = 2.*PI*vectorLength(QC); 
  cos2t = (qe.ki*qe.ki + qe.kf*qe.kf - q*q)/(2. * ABS(qe.ki) * ABS(qe.kf));
  if(ABS(cos2t) > 1.){
  	mat_free(R);
  	killVector(QC);
    return TRIANGLENOTCLOSED;
  }
  angles->sample_two_theta = ss*Acosd(cos2t);
  
  theta = calcTheta(qe.ki, qe.kf,angles->sample_two_theta);
  
  angles->a3 = om + theta;
  /*
    put a3 into -180, 180 properly. We cal always turn by 180 because the
    scattering geometry is symmetric in this respect. It is like looking at
    the scattering plane from the other side
  */
  angles->a3 -= 180.;
  if(angles->a3 < -180.){
    angles->a3 += 360.;
  }

  killVector(QC);
  mat_free(R);

  return 1;
}
Beispiel #7
0
/**
 * Process per-frame actions
 */
void StatBlock::logic() {
	if (hp <= 0 && !effects.triggered_death && !effects.revive) alive = false;
	else alive = true;

	// handle party buffs
	if (enemym && powers) {
		while (!party_buffs.empty()) {
			int power_index = party_buffs.front();
			party_buffs.pop();
			Power *buff_power = &powers->powers[power_index];

			for (size_t i=0; i < enemym->enemies.size(); ++i) {
				if(enemym->enemies[i]->stats.hp > 0 &&
				   ((enemym->enemies[i]->stats.hero_ally && hero) || (enemym->enemies[i]->stats.enemy_ally && enemym->enemies[i]->stats.summoner == this)) &&
				   (buff_power->buff_party_power_id == 0 || buff_power->buff_party_power_id == enemym->enemies[i]->stats.summoned_power_index)
				) {
					powers->effect(&enemym->enemies[i]->stats, this, power_index, (hero ? SOURCE_TYPE_HERO : SOURCE_TYPE_ENEMY));
				}
			}
		}
	}

	// handle effect timers
	effects.logic();

	// apply bonuses from items/effects to base stats
	applyEffects();

	if (hero && effects.refresh_stats) {
		refresh_stats = true;
		effects.refresh_stats = false;
	}

	// preserve ratio on maxmp and maxhp changes
	float ratio;
	if (prev_maxhp != get(STAT_HP_MAX)) {
		ratio = static_cast<float>(pres_hp) / static_cast<float>(prev_maxhp);
		hp = static_cast<int>(ratio * static_cast<float>(get(STAT_HP_MAX)));
	}
	if (prev_maxmp != get(STAT_MP_MAX)) {
		ratio = static_cast<float>(pres_mp) / static_cast<float>(prev_maxmp);
		mp = static_cast<int>(ratio * static_cast<float>(get(STAT_MP_MAX)));
	}

	// handle cooldowns
	if (cooldown_ticks > 0) cooldown_ticks--; // global cooldown

	for (size_t i=0; i<powers_ai.size(); ++i) { // NPC/enemy powerslot cooldown
		if (powers_ai[i].ticks > 0) powers_ai[i].ticks--;
	}

	// HP regen
	if (get(STAT_HP_REGEN) > 0 && hp < get(STAT_HP_MAX) && hp > 0) {
		hp_ticker++;
		if (hp_ticker >= (60 * MAX_FRAMES_PER_SEC)/get(STAT_HP_REGEN)) {
			hp++;
			hp_ticker = 0;
		}
	}

	// MP regen
	if (get(STAT_MP_REGEN) > 0 && mp < get(STAT_MP_MAX) && hp > 0) {
		mp_ticker++;
		if (mp_ticker >= (60 * MAX_FRAMES_PER_SEC)/get(STAT_MP_REGEN)) {
			mp++;
			mp_ticker = 0;
		}
	}

	// handle buff/debuff durations
	if (transform_duration > 0)
		transform_duration--;

	// apply bleed
	if (effects.damage > 0 && hp > 0) {
		takeDamage(effects.damage);
		comb->addInt(effects.damage, pos, COMBAT_MESSAGE_TAKEDMG);
	}
	if (effects.damage_percent > 0 && hp > 0) {
		int damage = (get(STAT_HP_MAX)*effects.damage_percent)/100;
		takeDamage(damage);
		comb->addInt(damage, pos, COMBAT_MESSAGE_TAKEDMG);
	}

	if(effects.death_sentence)
		hp = 0;

	if(cooldown_hit_ticks > 0)
		cooldown_hit_ticks--;

	if (effects.stun) {
		// stun stops charge attacks
		state_ticks = 0;
		charge_speed = 0;
	}
	else if (state_ticks > 0) {
		state_ticks--;
	}

	// apply healing over time
	if (effects.hpot > 0) {
		comb->addString(msg->get("+%d HP",effects.hpot), pos, COMBAT_MESSAGE_BUFF);
		hp += effects.hpot;
		if (hp > get(STAT_HP_MAX)) hp = get(STAT_HP_MAX);
	}
	if (effects.hpot_percent > 0) {
		int hpot = (get(STAT_HP_MAX)*effects.hpot_percent)/100;
		comb->addString(msg->get("+%d HP",hpot), pos, COMBAT_MESSAGE_BUFF);
		hp += hpot;
		if (hp > get(STAT_HP_MAX)) hp = get(STAT_HP_MAX);
	}
	if (effects.mpot > 0) {
		comb->addString(msg->get("+%d MP",effects.mpot), pos, COMBAT_MESSAGE_BUFF);
		mp += effects.mpot;
		if (mp > get(STAT_MP_MAX)) mp = get(STAT_MP_MAX);
	}
	if (effects.mpot_percent > 0) {
		int mpot = (get(STAT_MP_MAX)*effects.mpot_percent)/100;
		comb->addString(msg->get("+%d MP",mpot), pos, COMBAT_MESSAGE_BUFF);
		mp += mpot;
		if (mp > get(STAT_MP_MAX)) mp = get(STAT_MP_MAX);
	}

	// set movement type
	// some creatures may shift between movement types
	if (intangible) movement_type = MOVEMENT_INTANGIBLE;
	else if (flying) movement_type = MOVEMENT_FLYING;
	else movement_type = MOVEMENT_NORMAL;

	if (hp == 0)
		removeSummons();

	if (effects.knockback_speed != 0) {
		float theta = calcTheta(knockback_srcpos.x, knockback_srcpos.y, knockback_destpos.x, knockback_destpos.y);
		knockback_speed.x = effects.knockback_speed * cosf(theta);
		knockback_speed.y = effects.knockback_speed * sinf(theta);

		mapr->collider.unblock(pos.x, pos.y);
		mapr->collider.move(pos.x, pos.y, knockback_speed.x, knockback_speed.y, movement_type, hero);
		mapr->collider.block(pos.x, pos.y, hero_ally);
	}
	else if (charge_speed != 0.0f) {
		float tmp_speed = charge_speed * speedMultiplyer[direction];
		float dx = tmp_speed * static_cast<float>(directionDeltaX[direction]);
		float dy = tmp_speed * static_cast<float>(directionDeltaY[direction]);

		mapr->collider.unblock(pos.x, pos.y);
		mapr->collider.move(pos.x, pos.y, dx, dy, movement_type, hero);
		mapr->collider.block(pos.x, pos.y, hero_ally);
	}
}
Beispiel #8
0
/**
 * Process per-frame actions
 */
void StatBlock::logic() {
	if (hp <= 0 && !effects.triggered_death && !effects.revive) alive = false;
	else alive = true;

	// handle effect timers
	effects.logic();

	// apply bonuses from items/effects to base stats
	applyEffects();

	// preserve ratio on maxmp and maxhp changes
	float ratio;
	if (prev_maxhp != get(STAT_HP_MAX)) {
		ratio = static_cast<float>(pres_hp) / static_cast<float>(prev_maxhp);
		hp = static_cast<int>(ratio * static_cast<float>(get(STAT_HP_MAX)));
	}
	if (prev_maxmp != get(STAT_MP_MAX)) {
		ratio = static_cast<float>(pres_mp) / static_cast<float>(prev_maxmp);
		mp = static_cast<int>(ratio * static_cast<float>(get(STAT_MP_MAX)));
	}

	// handle cooldowns
	if (cooldown_ticks > 0) cooldown_ticks--; // global cooldown

	for (size_t i=0; i<powers_ai.size(); ++i) { // NPC/enemy powerslot cooldown
		if (powers_ai[i].ticks > 0) powers_ai[i].ticks--;
	}

	// HP regen
	if (get(STAT_HP_REGEN) > 0 && hp < get(STAT_HP_MAX) && hp > 0) {
		hp_ticker++;
		if (hp_ticker >= (60 * MAX_FRAMES_PER_SEC)/get(STAT_HP_REGEN)) {
			hp++;
			hp_ticker = 0;
		}
	}

	// MP regen
	if (get(STAT_MP_REGEN) > 0 && mp < get(STAT_MP_MAX) && hp > 0) {
		mp_ticker++;
		if (mp_ticker >= (60 * MAX_FRAMES_PER_SEC)/get(STAT_MP_REGEN)) {
			mp++;
			mp_ticker = 0;
		}
	}

	// handle buff/debuff durations
	if (transform_duration > 0)
		transform_duration--;

	// apply bleed
	if (effects.damage > 0 && hp > 0) {
		takeDamage(effects.damage);
		comb->addMessage(effects.damage, pos, COMBAT_MESSAGE_TAKEDMG);
	}
	if (effects.damage_percent > 0 && hp > 0) {
		int damage = (get(STAT_HP_MAX)*effects.damage_percent)/100;
		takeDamage(damage);
		comb->addMessage(damage, pos, COMBAT_MESSAGE_TAKEDMG);
	}

	if(effects.death_sentence)
		hp = 0;

	if(cooldown_hit_ticks > 0)
		cooldown_hit_ticks--;

	// apply healing over time
	if (effects.hpot > 0) {
		comb->addMessage(msg->get("+%d HP",effects.hpot), pos, COMBAT_MESSAGE_BUFF);
		hp += effects.hpot;
		if (hp > get(STAT_HP_MAX)) hp = get(STAT_HP_MAX);
	}
	if (effects.hpot_percent > 0) {
		int hpot = (get(STAT_HP_MAX)*effects.hpot_percent)/100;
		comb->addMessage(msg->get("+%d HP",hpot), pos, COMBAT_MESSAGE_BUFF);
		hp += hpot;
		if (hp > get(STAT_HP_MAX)) hp = get(STAT_HP_MAX);
	}
	if (effects.mpot > 0) {
		comb->addMessage(msg->get("+%d MP",effects.mpot), pos, COMBAT_MESSAGE_BUFF);
		mp += effects.mpot;
		if (mp > get(STAT_MP_MAX)) mp = get(STAT_MP_MAX);
	}
	if (effects.mpot_percent > 0) {
		int mpot = (get(STAT_MP_MAX)*effects.mpot_percent)/100;
		comb->addMessage(msg->get("+%d MP",mpot), pos, COMBAT_MESSAGE_BUFF);
		mp += mpot;
		if (mp > get(STAT_MP_MAX)) mp = get(STAT_MP_MAX);
	}

	// set movement type
	// some creatures may shift between movement types
	if (intangible) movement_type = MOVEMENT_INTANGIBLE;
	else if (flying) movement_type = MOVEMENT_FLYING;
	else movement_type = MOVEMENT_NORMAL;

	if (hp == 0)
		removeSummons();

	if (effects.knockback_speed != 0) {
		float theta = calcTheta(knockback_srcpos.x, knockback_srcpos.y, knockback_destpos.x, knockback_destpos.y);
		knockback_speed.x = effects.knockback_speed * static_cast<float>(cos(theta));
		knockback_speed.y = effects.knockback_speed * static_cast<float>(sin(theta));
	}

	if (effects.knockback_speed != 0) {
		mapr->collider.unblock(pos.x, pos.y);
		mapr->collider.move(pos.x, pos.y, knockback_speed.x, knockback_speed.y, movement_type, hero);
		mapr->collider.block(pos.x, pos.y, hero_ally);
	}
}