/** * 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; }
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); } }
/** * \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; }
/** * \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; }
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; }