Example #1
0
/**
 * Whenever a hazard collides with an entity, this function resolves the effect
 * Called by HazardManager
 *
 * Returns false on miss
 */
bool Entity::takeHit(const Hazard &h) {

	//check if this enemy should be affected by this hazard based on the category
	if(!powers->powers[h.power_index].target_categories.empty() && !stats.hero) {
		//the power has a target category requirement, so if it doesnt match, dont continue
		bool match_found = false;
		for (unsigned int i=0; i<stats.categories.size(); i++) {
			if(std::find(powers->powers[h.power_index].target_categories.begin(), powers->powers[h.power_index].target_categories.end(), stats.categories[i]) != powers->powers[h.power_index].target_categories.end()) {
				match_found = true;
			}
		}
		if(!match_found)
			return false;
	}

	//if the target is already dead, they cannot be hit
	if ((stats.cur_state == ENEMY_DEAD || stats.cur_state == ENEMY_CRITDEAD) && !stats.hero)
		return false;

	if(stats.cur_state == AVATAR_DEAD && stats.hero)
		return false;

	//if the target is an enemy and they are not already in combat, activate a beacon to draw other enemies into battle
	if (!stats.in_combat && !stats.hero && !stats.hero_ally) {
		stats.join_combat = true;
		stats.in_combat = true;
		powers->activate(stats.power_index[BEACON], &stats, stats.pos); //emit beacon
	}

	// exit if it was a beacon (to prevent stats.targeted from being set)
	if (powers->powers[h.power_index].beacon) return false;

	// prepare the combat text
	CombatText *combat_text = comb;

	// if it's a miss, do nothing
	int accuracy = h.accuracy;
	if(powers->powers[h.power_index].mod_accuracy_mode == STAT_MODIFIER_MODE_MULTIPLY)
		accuracy = accuracy * powers->powers[h.power_index].mod_accuracy_value / 100;
	else if(powers->powers[h.power_index].mod_accuracy_mode == STAT_MODIFIER_MODE_ADD)
		accuracy += powers->powers[h.power_index].mod_accuracy_value;
	else if(powers->powers[h.power_index].mod_accuracy_mode == STAT_MODIFIER_MODE_ABSOLUTE)
		accuracy = powers->powers[h.power_index].mod_accuracy_value;

	int avoidance = 0;
	if(!powers->powers[h.power_index].trait_avoidance_ignore) {
		avoidance = stats.get(STAT_AVOIDANCE);
		if (stats.effects.triggered_block) avoidance *= 2;
	}

	int true_avoidance = 100 - (accuracy + 25 - avoidance);
	//if we are using an absolute accuracy, offset the constant 25 added to the accuracy
	if(powers->powers[h.power_index].mod_accuracy_mode == STAT_MODIFIER_MODE_ABSOLUTE)
		true_avoidance += 25;
	clampFloor(true_avoidance, MIN_AVOIDANCE);
	clampCeil(true_avoidance, MAX_AVOIDANCE);

	if (percentChance(true_avoidance)) {
		combat_text->addMessage(msg->get("miss"), stats.pos, COMBAT_MESSAGE_MISS);
		return false;
	}

	// calculate base damage
	int dmg = randBetween(h.dmg_min, h.dmg_max);

	if(powers->powers[h.power_index].mod_damage_mode == STAT_MODIFIER_MODE_MULTIPLY)
		dmg = dmg * powers->powers[h.power_index].mod_damage_value_min / 100;
	else if(powers->powers[h.power_index].mod_damage_mode == STAT_MODIFIER_MODE_ADD)
		dmg += powers->powers[h.power_index].mod_damage_value_min;
	else if(powers->powers[h.power_index].mod_damage_mode == STAT_MODIFIER_MODE_ABSOLUTE)
		dmg = randBetween(powers->powers[h.power_index].mod_damage_value_min, powers->powers[h.power_index].mod_damage_value_max);

	// apply elemental resistance
	if (h.trait_elemental >= 0 && unsigned(h.trait_elemental) < stats.vulnerable.size()) {
		unsigned i = h.trait_elemental;
		int vulnerable = stats.vulnerable[i];
		clampFloor(vulnerable,MIN_RESIST);
		if (stats.vulnerable[i] < 100)
			clampCeil(vulnerable,MAX_RESIST);
		dmg = (dmg * vulnerable) / 100;
	}

	if (!h.trait_armor_penetration) { // armor penetration ignores all absorption
		// substract absorption from armor
		int absorption = randBetween(stats.get(STAT_ABS_MIN), stats.get(STAT_ABS_MAX));

		if (stats.effects.triggered_block) {
			absorption += absorption + stats.get(STAT_ABS_MAX); // blocking doubles your absorb amount
		}

		if (absorption > 0 && dmg > 0) {
			int abs = absorption;
			if ((abs*100)/dmg < MIN_BLOCK)
				absorption = (dmg * MIN_BLOCK) /100;
			if ((abs*100)/dmg > MAX_BLOCK)
				absorption = (dmg * MAX_BLOCK) /100;
			if ((abs*100)/dmg < MIN_ABSORB && !stats.effects.triggered_block)
				absorption = (dmg * MIN_ABSORB) /100;
			if ((abs*100)/dmg > MAX_ABSORB && !stats.effects.triggered_block)
				absorption = (dmg * MAX_ABSORB) /100;

			// Sometimes, the absorb limits cause absorbtion to drop to 1
			// This could be confusing to a player that has something with an absorb of 1 equipped
			// So we round absorption up in this case
			if (absorption == 0) absorption = 1;
		}

		dmg = dmg - absorption;
		if (dmg <= 0) {
			dmg = 0;
			if (h.trait_elemental < 0) {
				if (stats.effects.triggered_block && MAX_BLOCK < 100) dmg = 1;
				else if (!stats.effects.triggered_block && MAX_ABSORB < 100) dmg = 1;
			}
			else {
				if (MAX_RESIST < 100) dmg = 1;
			}
			play_sfx_block = true;
			if (activeAnimation->getName() == "block")
				resetActiveAnimation();
		}
	}

	// check for crits
	int true_crit_chance = h.crit_chance;

	if(powers->powers[h.power_index].mod_crit_mode == STAT_MODIFIER_MODE_MULTIPLY)
		true_crit_chance = true_crit_chance * powers->powers[h.power_index].mod_crit_value / 100;
	else if(powers->powers[h.power_index].mod_crit_mode == STAT_MODIFIER_MODE_ADD)
		true_crit_chance += powers->powers[h.power_index].mod_crit_value;
	else if(powers->powers[h.power_index].mod_crit_mode == STAT_MODIFIER_MODE_ABSOLUTE)
		true_crit_chance = powers->powers[h.power_index].mod_crit_value;

	if (stats.effects.stun || stats.effects.speed < 100)
		true_crit_chance += h.trait_crits_impaired;

	bool crit = percentChance(true_crit_chance);
	if (crit) {
		dmg = dmg + h.dmg_max;
		if(!stats.hero)
			mapr->shaky_cam_ticks = MAX_FRAMES_PER_SEC/2;
	}

	if(stats.hero)
		combat_text->addMessage(dmg, stats.pos, COMBAT_MESSAGE_TAKEDMG);
	else {
		if(crit)
			combat_text->addMessage(dmg, stats.pos, COMBAT_MESSAGE_CRIT);
		else
			combat_text->addMessage(dmg, stats.pos, COMBAT_MESSAGE_GIVEDMG);
	}

	// temporarily save the current HP for calculating HP/MP steal on final blow
	int prev_hp = stats.hp;

	// apply damage
	stats.takeDamage(dmg);

	// after effects
	if (dmg > 0) {

		// damage always breaks stun
		stats.effects.removeEffectType("stun");

		if (stats.hp > 0) {
			powers->effect(&stats, h.src_stats, h.power_index,h.source_type);
		}

		if (!stats.effects.immunity) {
			if (h.hp_steal != 0) {
				int steal_amt = (std::min(dmg, prev_hp) * h.hp_steal) / 100;
				if (steal_amt == 0) steal_amt = 1;
				combat_text->addMessage(msg->get("+%d HP",steal_amt), h.src_stats->pos, COMBAT_MESSAGE_BUFF);
				h.src_stats->hp = std::min(h.src_stats->hp + steal_amt, h.src_stats->get(STAT_HP_MAX));
			}
			if (h.mp_steal != 0) {
				int steal_amt = (std::min(dmg, prev_hp) * h.mp_steal) / 100;
				if (steal_amt == 0) steal_amt = 1;
				combat_text->addMessage(msg->get("+%d MP",steal_amt), h.src_stats->pos, COMBAT_MESSAGE_BUFF);
				h.src_stats->mp = std::min(h.src_stats->mp + steal_amt, h.src_stats->get(STAT_MP_MAX));
			}
		}
	}

	// post effect power
	if (h.post_power > 0 && dmg > 0) {
		powers->activate(h.post_power, h.src_stats, stats.pos);
	}

	// loot
	if (dmg > 0 && !h.loot.empty()) {
		for (unsigned i=0; i<h.loot.size(); i++) {
			powers->loot.push_back(h.loot[i]);
			powers->loot.back().x = (int)stats.pos.x;
			powers->loot.back().y = (int)stats.pos.y;
		}
	}

	// interrupted to new state
	if (dmg > 0) {
		bool chance_poise = percentChance(stats.get(STAT_POISE));

		if(stats.hp <= 0) {
			stats.effects.triggered_death = true;
			if(stats.hero)
				stats.cur_state = AVATAR_DEAD;
			else {
				doRewards(h.source_type);
				if (crit)
					stats.cur_state = ENEMY_CRITDEAD;
				else
					stats.cur_state = ENEMY_DEAD;
				mapr->collider.unblock(stats.pos.x,stats.pos.y);
			}
		}
		// don't go through a hit animation if stunned
		else if (!stats.effects.stun && !chance_poise) {
			play_sfx_hit = true;

			if(!chance_poise && stats.cooldown_hit_ticks == 0) {
				if(stats.hero)
					stats.cur_state = AVATAR_HIT;
				else
					stats.cur_state = ENEMY_HIT;
				stats.cooldown_hit_ticks = stats.cooldown_hit;

				if (stats.untransform_on_hit)
					stats.transform_duration = 0;
			}
			// roll to see if the enemy's ON_HIT power is casted
			if (percentChance(stats.power_chance[ON_HIT])) {
				powers->activate(stats.power_index[ON_HIT], &stats, stats.pos);
			}
		}
		// just play the hit sound
		else
			play_sfx_hit = true;
	}

	return true;
}
Example #2
0
void LootManager::checkLoot(std::vector<Event_Component> &loot_table, FPoint *pos, std::vector<ItemStack> *itemstack_vec) {
	if (hero == NULL) {
		logError("LootManager: checkLoot() failed, no hero.");
		return;
	}

	FPoint p;
	Event_Component *ec;
	ItemStack new_loot;
	std::vector<Event_Component*> possible_ids;

	int chance = rand() % 100;

	// first drop any 'fixed' (0% chance) items
	for (size_t i = loot_table.size(); i > 0; i--) {
		ec = &loot_table[i-1];
		if (ec->z == 0) {
			Point src;
			if (pos) {
				src = FPointToPoint(*pos);
			}
			else {
				src.x = ec->x;
				src.y = ec->y;
			}
			p.x = static_cast<float>(src.x) + 0.5f;
			p.y = static_cast<float>(src.y) + 0.5f;

			if (!mapr->collider.is_valid_position(p.x, p.y, MOVEMENT_NORMAL, false)) {
				p = mapr->collider.get_random_neighbor(src, drop_radius);

				if (!mapr->collider.is_valid_position(p.x, p.y, MOVEMENT_NORMAL, false)) {
					p = hero->pos;
				}
				else {
					if (src.x == static_cast<int>(p.x) && src.y == static_cast<int>(p.y))
						p = hero->pos;

					mapr->collider.block(p.x, p.y, false);
					tiles_to_unblock.push_back(FPointToPoint(p));
				}
			}

			new_loot.quantity = randBetween(ec->a,ec->b);

			// an item id of 0 means we should drop currency instead
			if (ec->c == 0 || ec->c == CURRENCY_ID) {
				new_loot.item = CURRENCY_ID;
				new_loot.quantity = new_loot.quantity * (100 + hero->get(STAT_CURRENCY_FIND)) / 100;
			}
			else {
				new_loot.item = ec->c;
			}

			if (itemstack_vec)
				itemstack_vec->push_back(new_loot);
			else
				addLoot(new_loot, p);

			loot_table.erase(loot_table.begin()+i-1);
		}
	}

	// now pick up to 1 random item to drop
	int threshold = hero->get(STAT_ITEM_FIND) + 100;
	for (unsigned i = 0; i < loot_table.size(); i++) {
		ec = &loot_table[i];

		int real_chance = ec->z;

		if (ec->c != 0 && ec->c != CURRENCY_ID) {
			real_chance = static_cast<int>(static_cast<float>(ec->z) * static_cast<float>(hero->get(STAT_ITEM_FIND) + 100) / 100.f);
		}

		if (real_chance >= chance) {
			if (real_chance <= threshold) {
				if (real_chance != threshold) {
					possible_ids.clear();
				}

				threshold = real_chance;
			}

			if (chance <= threshold) {
				possible_ids.push_back(ec);
			}
		}
	}

	if (!possible_ids.empty()) {
		// if there was more than one item with the same chance, randomly pick one of them
		size_t chosen_loot = static_cast<size_t>(rand()) % possible_ids.size();

		ec = possible_ids[chosen_loot];

		Point src;
		if (pos) {
			src = FPointToPoint(*pos);
		}
		else {
			src.x = ec->x;
			src.y = ec->y;
		}
		p.x = static_cast<float>(src.x) + 0.5f;
		p.y = static_cast<float>(src.y) + 0.5f;

		if (!mapr->collider.is_valid_position(p.x, p.y, MOVEMENT_NORMAL, false)) {
			p = mapr->collider.get_random_neighbor(src, drop_radius);

			if (!mapr->collider.is_valid_position(p.x, p.y, MOVEMENT_NORMAL, false)) {
				p = hero->pos;
			}
			else {
				if (src.x == static_cast<int>(p.x) && src.y == static_cast<int>(p.y))
					p = hero->pos;

				mapr->collider.block(p.x, p.y, false);
				tiles_to_unblock.push_back(FPointToPoint(p));
			}
		}

		new_loot.quantity = randBetween(ec->a,ec->b);

		// an item id of 0 means we should drop currency instead
		if (ec->c == 0 || ec->c == CURRENCY_ID) {
			new_loot.item = CURRENCY_ID;
			new_loot.quantity = new_loot.quantity * (100 + hero->get(STAT_CURRENCY_FIND)) / 100;
		}
		else {
			new_loot.item = ec->c;
		}

		if (itemstack_vec)
			itemstack_vec->push_back(new_loot);
		else
			addLoot(new_loot, p);
	}
}
Example #3
0
/**     
 * \brief  跳转
 *
 * 重载了done函数,使用户跳转到特定地图位置
 *      
 * \param user: 触发动作的用户
 * \param vars: 用户所带的该任务相关变量
 * \return SUCCESS表示成功,FAILED表示失败,DISABLE表示禁用某项功能
 */
int GotoAction::done (SceneUser* user,Vars* vars)
{
  
  if (_name.length()/*strcmp(user->scene->name,_name.c_str())*/)  {
    
    //Xlogger->debug("换地图:%s",_name.c_str());
    std::string tmpStr = "name=" + _name + " pos=" +  _pos;
    bool bret=false;
    switch(randBetween(1,4))
    {
      case 1:
        if (_pos1.length() >3) {tmpStr = "name=" + _name + " pos=" +  _pos1;bret=true;}
        break;
      case 2:
        if (_pos2.length() >3) {tmpStr = "name=" + _name + " pos=" +  _pos2;bret=true;}
        break;
      case 3:
        if (_pos3.length() >3) {tmpStr = "name=" + _name + " pos=" +  _pos3;bret=true;}
        break;
      case 4:
        if (_pos4.length() >3) {tmpStr = "name=" + _name + " pos=" +  _pos4;bret=true;}
        break;
    }
    if (!bret)
    {
      if (_cpos.length() >4 )
      {
        if (_rlen.length( ) > 4)
          tmpStr = "name=" + _name + " cpos=" + _cpos + " rlen=" + _rlen;
        else
          tmpStr = "name=" + _name + " cpos=" + _cpos;
      }
    }
    // */
    Gm::gomap(user,tmpStr.c_str());
  }
  //Xlogger->debug("换坐标:%s",_pos.c_str());
  else
  {
    std::string tmpStr = _pos;

    switch(randBetween(1,4))
    {
      case 1:
        if (_pos1.length() >3) tmpStr = _pos1;
        break;
      case 2:
        if (_pos2.length() >3) tmpStr = _pos2;
        break;
      case 3:
        if (_pos3.length() >3) tmpStr = _pos3;
        break;
      case 4:
        if (_pos4.length() >3) tmpStr = _pos4;
        break;
    }
    Gm::goTo(user,tmpStr.c_str());  
  }
  
  return Action::SUCCESS;
}
Example #4
0
/**     
 * \brief  添加镖车
 *
 * 重载了done函数,在地图上添加一辆镖车
 *      
 * \param user: 触发动作的用户
 * \param vars: 用户所带的该任务相关变量
 * \return SUCCESS表示成功,FAILED表示失败,DISABLE表示禁用某项功能
 */
int AddGuardAction::done(SceneUser* user,Vars* vars)
{
  DWORD i=_id;

  DWORD o = randBetween(1,100);
  if (_id3 && (o>(_odds1+_odds2)*100/(_odds1+_odds2+_odds3)))
    i=_id3;
  else if (_id2 && (o>_odds1*100/(_odds1+_odds2+_odds3)))
  {
    i=_id2;
    Channel::sendSys(user,Cmd::INFO_TYPE_GAME,"恭喜!你接到了一辆无敌镖车!");
  }

  zNpcB *base = npcbm.get(i);
  if (!base) return  Action::FAILED;

  if (user->guard) {
    Channel::sendSys(user,Cmd::INFO_TYPE_FAIL,"你还在护镖中!");
    return  Action::FAILED;
  }

  t_NpcDefine define;
  define.id = base->id;
  //_snprintf(define.name,MAX_NAMESIZE,"%s的%s",user->name,base->name );
  strcpy(define.name,base->name);
  define.pos = _ltpos;
  define.width = _rbpos.x - _ltpos.x;
  define.height = _rbpos.y - _ltpos.y;
  define.num = _num;
  define.interval = 30;
  define.initstate = zSceneEntry::SceneEntry_Normal;
  //define.scriptID = _script;
  //define.setPath(_path.c_str());
  user->scene->initRegion(define.region,define.pos,define.width,define.height);

  //if (!_s) {
  //  return  Action::FAILED;
  //}

  user->scene->zPosRevaluate(define.pos);

  GuardNpc* npc = user->scene->summonOneNpc<GuardNpc>(define,define.pos,base,user->dupIndex);
  if (!npc)
  {
    Xlogger->error("召唤 %s 失败",define.name);
    return Action::FAILED;
  }
  npc->setMaster(user);
  npc->setPetType(Cmd::PET_TYPE_GUARDNPC);
  user->guard = npc;
  //t_NpcAIDefine  ai;
  //ai.type = NPC_AI_PATROL;
  //npc->setAI(ai);
  //npc->sendData();
  /*
  GuardNpc* npc = user->scene->summonPet(user,Cmd::PET_TYPE_GUARDNPC,define,define.pos,base);
  if (!npc) {
    Xlogger->error("召唤 %s 失败",define.name);
    return Action::FAILED;
  }
  npc->setMaster(user);
  user->pets.push_back(npc);

  //set owner
  */
  npc->gold(_gold);
  if (user->venterSeptGuard.size())//家族运镖
  {
    DWORD m=0;
    for (DWORD i=0; i<user->venterSeptGuard.size(); i++)
      m += user->venterSeptGuard[i].money;
    npc->gold(m);
    npc->isSeptGuard = true;
    Xlogger->debug("%s(%u) 增加家族镖车 gold=%u",user->name,user->id,m);
  }
  npc->owner(user);
  npc->dest(_dest);
  npc->map(_map);
  npc->exp(_exp);
  SceneNpcManager::getMe().addSpecialNpc(npc);

  return Action::SUCCESS;
}
Example #5
0
bool Rush::summonPet()
{
  if (!boss) return false;
  if (!canSummon) return false;

  int die = randBetween(0,100);

  for (DWORD index=0; index<servantVector.size(); index++)
  {
    if (servantVector[index].rate<die)
      continue;
    
    zNpcB *base = npcbm.get(servantVector[index].id);
    if (NULL == base)
    {
      Xlogger->debug("召唤 %s 的宠物时,未找到该怪物 id=%d",boss->name,servantVector[index].id);
      return false;
    }

    t_NpcDefine define;
    define.id = base->id;
    strncpy(define.name,base->name,sizeof(define.name));
    zPos pos(boss->getPos());
    pos -= zPos(summon_servant_region*2,summon_servant_region*2);
    define.pos = pos;
    define.num = servantVector[index].num;
    define.interval = 0x0fffffff;
    define.initstate = zSceneEntry::SceneEntry_Normal;
    define.width = summon_servant_region*4;
    define.height = summon_servant_region*4;
    //define.pos -= zPos(summon_servant_region,summon_servant_region);

    Scene * scene = SceneManager::getInstance().getSceneByName(mapName);
    if (!scene)
    {
      Xlogger->debug("召唤 %s 的仆人时,未找到该地图 name=%s",boss->name,mapName);
      return false;
    }
    scene->initRegion(define.region,define.pos,define.width,define.height);

    ScenePet * servant;
    int count = 0;
    for (int i=0; i<servantVector[index].num; i++)
    {
      servant = scene->summonOneNpc<ScenePet>(define,pos,base,0);
      if (servant)
      {
        servant->isRushNpc = true;
        servants.push_back(servant);

        servant->setMaster(boss);
        boss->totems.push_back(servant);
        count++;
      }
    }

    summonTime = SceneTimeTick::currentTime;
    summonTime.addDelay(servantVector[index].interval*1000);
    Xlogger->debug("%s summonPet:summon %u 次,servant %u 个",rushName,count,servants.size());
  }
  
  return true;
}