UnsummonW Implementation + Tweaks

* UnsummonW can be used to remove the mob from existence.
* Mob groups now aggro together.
* Mobs space a little bit when chasing the player.
* Combat balance tweaked a bit, you can take out an entire boss group of scoria cephalopod with good nano usage with common tier weapons.
This commit is contained in:
Jade 2020-11-16 03:13:40 +00:00 committed by Gent S
parent 5e8b6eec6e
commit f1d04cec01
3 changed files with 88 additions and 20 deletions

View File

@ -284,6 +284,22 @@ void unsummonWCommand(std::string full, std::vector<std::string>& args, CNSocket
return; return;
} }
int leadId = ((Mob*)npc)->groupLeader;
if (leadId != 0) {
Mob* leadNpc = MobManager::Mobs[leadId];
for (int i = 0; i < 4; i++) {
if (leadNpc->groupMember[i] == 0)
break;
TableData::RunningMobs.erase(leadNpc->groupMember[i]);
NPCManager::destroyNPC(leadNpc->groupMember[i]);
}
TableData::RunningMobs.erase(leadId);
NPCManager::destroyNPC(leadId);
ChatManager::sendServerMessage(sock, "/unsummonW: Mob group destroyed.");
return;
}
ChatManager::sendServerMessage(sock, "/unsummonW: removed mob with type: " + std::to_string(npc->appearanceData.iNPCType) + ChatManager::sendServerMessage(sock, "/unsummonW: removed mob with type: " + std::to_string(npc->appearanceData.iNPCType) +
", id: " + std::to_string(npc->appearanceData.iNPC_ID)); ", id: " + std::to_string(npc->appearanceData.iNPC_ID));
@ -509,11 +525,11 @@ void notifyCommand(std::string full, std::vector<std::string>& args, CNSocket* s
Player *plr = PlayerManager::getPlayer(sock); Player *plr = PlayerManager::getPlayer(sock);
if (plr->notify) { if (plr->notify) {
plr->notify = false; plr->notify = false;
ChatManager::sendServerMessage(sock, "[ADMIN] No longer receiving join notifications"); ChatManager::sendServerMessage(sock, "[ADMIN] No longer receiving join notifications");
} else { } else {
plr->notify = true; plr->notify = true;
ChatManager::sendServerMessage(sock, "[ADMIN] Receiving join notifications"); ChatManager::sendServerMessage(sock, "[ADMIN] Receiving join notifications");
} }
} }
@ -523,14 +539,16 @@ void playersCommand(std::string full, std::vector<std::string>& args, CNSocket*
ChatManager::sendServerMessage(sock, PlayerManager::getPlayerName(pair.second)); ChatManager::sendServerMessage(sock, PlayerManager::getPlayerName(pair.second));
} }
void summonGroupWCommand(std::string full, std::vector<std::string>& args, CNSocket* sock) { void summonGroupCommand(std::string full, std::vector<std::string>& args, CNSocket* sock) {
if (args.size() < 4) { if (args.size() < 4) {
ChatManager::sendServerMessage(sock, "/summonGroupW <leadermob> <mob> <number> [distance]"); ChatManager::sendServerMessage(sock, "/summonGroup(W) <leadermob> <mob> <number> [distance]");
return; return;
} }
Player* plr = PlayerManager::getPlayer(sock); Player* plr = PlayerManager::getPlayer(sock);
char *rest; char *rest;
bool wCommand = (args[0] == "/summonGroupW");
int type = std::strtol(args[1].c_str(), &rest, 10); int type = std::strtol(args[1].c_str(), &rest, 10);
int type2 = std::strtol(args[2].c_str(), &rest, 10); int type2 = std::strtol(args[2].c_str(), &rest, 10);
int count = std::strtol(args[3].c_str(), &rest, 10); int count = std::strtol(args[3].c_str(), &rest, 10);
@ -586,7 +604,8 @@ void summonGroupWCommand(std::string full, std::vector<std::string>& args, CNSoc
} }
// re-enable respawning // re-enable respawning
((Mob*)npc)->summoned = false; if (wCommand)
((Mob*)npc)->summoned = false;
} else { } else {
npc = new BaseNPC(x, y, z + EXTRA_HEIGHT, 0, plr->instanceID, type, id); npc = new BaseNPC(x, y, z + EXTRA_HEIGHT, 0, plr->instanceID, type, id);
} }
@ -612,8 +631,8 @@ void summonGroupWCommand(std::string full, std::vector<std::string>& args, CNSoc
mob->offsetX = x - plr->x; mob->offsetX = x - plr->x;
mob->offsetY = y - plr->y; mob->offsetY = y - plr->y;
} }
if (wCommand)
((Mob*)npc)->summoned = false; ((Mob*)npc)->summoned = false;
} else { } else {
npc = new BaseNPC(x, y, z + EXTRA_HEIGHT, 0, MAPNUM(plr->instanceID), type, id); npc = new BaseNPC(x, y, z + EXTRA_HEIGHT, 0, MAPNUM(plr->instanceID), type, id);
} }
@ -624,9 +643,10 @@ void summonGroupWCommand(std::string full, std::vector<std::string>& args, CNSoc
NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, x, y, z); NPCManager::updateNPCPosition(npc->appearanceData.iNPC_ID, x, y, z);
} }
ChatManager::sendServerMessage(sock, "/summonGroupW: placed mob with type: " + std::to_string(type) + ChatManager::sendServerMessage(sock, "/summonGroup(W): placed mob with type: " + std::to_string(type) +
", id: " + std::to_string(npc->appearanceData.iNPC_ID)); ", id: " + std::to_string(npc->appearanceData.iNPC_ID));
TableData::RunningMobs[npc->appearanceData.iNPC_ID] = npc; // only record the one in the template if (wCommand)
TableData::RunningMobs[npc->appearanceData.iNPC_ID] = npc; // only record the one in the template
if (i == 0 && team == 2) { if (i == 0 && team == 2) {
type = type2; type = type2;
@ -666,7 +686,8 @@ void ChatManager::init() {
registerCommand("tasks", 30, tasksCommand, "list all active missions and their respective task ids."); registerCommand("tasks", 30, tasksCommand, "list all active missions and their respective task ids.");
registerCommand("notify", 30, notifyCommand, "receive a message whenever a player joins the server"); registerCommand("notify", 30, notifyCommand, "receive a message whenever a player joins the server");
registerCommand("players", 30, playersCommand, "print all players on the server"); registerCommand("players", 30, playersCommand, "print all players on the server");
registerCommand("summonGroupW", 30, summonGroupWCommand, "permanently summon group NPCs"); registerCommand("summonGroup", 30, summonGroupCommand, "summon group NPCs");
registerCommand("summonGroupW", 30, summonGroupCommand, "permanently summon group NPCs");
} }
void ChatManager::registerCommand(std::string cmd, int requiredLevel, CommandHandler handlr, std::string help) { void ChatManager::registerCommand(std::string cmd, int requiredLevel, CommandHandler handlr, std::string help) {

View File

@ -133,7 +133,7 @@ void MobManager::npcAttackPc(Mob *mob, time_t currTime) {
sP_FE2CL_NPC_ATTACK_PCs *pkt = (sP_FE2CL_NPC_ATTACK_PCs*)respbuf; sP_FE2CL_NPC_ATTACK_PCs *pkt = (sP_FE2CL_NPC_ATTACK_PCs*)respbuf;
sAttackResult *atk = (sAttackResult*)(respbuf + sizeof(sP_FE2CL_NPC_ATTACK_PCs)); sAttackResult *atk = (sAttackResult*)(respbuf + sizeof(sP_FE2CL_NPC_ATTACK_PCs));
auto damage = getDamage(450 + (int)mob->data["m_iPower"], plr->defense, false, false, -1, -1, rand() % plr->level + 1); auto damage = getDamage(450 + (int)mob->data["m_iPower"], plr->defense, false, false, -1, -1, 0);
plr->HP -= damage.first; plr->HP -= damage.first;
pkt->iNPC_ID = mob->appearanceData.iNPC_ID; pkt->iNPC_ID = mob->appearanceData.iNPC_ID;
@ -338,6 +338,9 @@ int MobManager::hitMob(CNSocket *sock, Mob *mob, int damage) {
mob->roamX = mob->appearanceData.iX; mob->roamX = mob->appearanceData.iX;
mob->roamY = mob->appearanceData.iY; mob->roamY = mob->appearanceData.iY;
mob->roamZ = mob->appearanceData.iZ; mob->roamZ = mob->appearanceData.iZ;
if (mob->groupLeader != 0)
followToCombat(mob);
} }
mob->appearanceData.iHP -= damage; mob->appearanceData.iHP -= damage;
@ -560,7 +563,14 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
if (mob->appearanceData.iConditionBitFlag & CSB_BIT_DN_MOVE_SPEED) if (mob->appearanceData.iConditionBitFlag & CSB_BIT_DN_MOVE_SPEED)
speed /= 2; speed /= 2;
auto targ = lerp(mob->appearanceData.iX, mob->appearanceData.iY, mob->target->plr->x, mob->target->plr->y,std::min(distance-(int)mob->data["m_iAtkRange"]+1, speed*2/5)); int targetX = mob->target->plr->x;
int targetY = mob->target->plr->y;
if (mob->groupLeader != 0) {
targetX += mob->offsetX*distance/(mob->sightRange+1);
targetY += mob->offsetY*distance/(mob->sightRange+1);
}
auto targ = lerp(mob->appearanceData.iX, mob->appearanceData.iY, targetX, targetY, std::min(distance-(int)mob->data["m_iAtkRange"]+1, speed*2/5));
NPCManager::updateNPCPosition(mob->appearanceData.iNPC_ID, targ.first, targ.second, mob->appearanceData.iZ, mob->instanceID, mob->appearanceData.iAngle); NPCManager::updateNPCPosition(mob->appearanceData.iNPC_ID, targ.first, targ.second, mob->appearanceData.iZ, mob->instanceID, mob->appearanceData.iAngle);
@ -570,10 +580,6 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
pkt.iSpeed = speed; pkt.iSpeed = speed;
pkt.iToX = mob->appearanceData.iX = targ.first; pkt.iToX = mob->appearanceData.iX = targ.first;
pkt.iToY = mob->appearanceData.iY = targ.second; pkt.iToY = mob->appearanceData.iY = targ.second;
if (mob->groupLeader != 0 && mob->groupLeader != mob->appearanceData.iNPC_ID) {
pkt.iToX += mob->offsetX;
pkt.iToY += mob->offsetY;
}
pkt.iToZ = mob->target->plr->z; pkt.iToZ = mob->target->plr->z;
pkt.iMoveStyle = 1; pkt.iMoveStyle = 1;
@ -972,7 +978,7 @@ std::pair<int,int> MobManager::getDamage(int attackPower, int defensePower, bool
// base calculation // base calculation
int damage = attackPower * attackPower / (attackPower + defensePower); int damage = attackPower * attackPower / (attackPower + defensePower);
//damage = std::max(10 + attackPower / 10, damage - defensePower * (4 + difficulty) / 100); damage = std::max(10 + attackPower / 10, damage - (defensePower - attackPower / 2) * difficulty / 72);
damage = damage * (rand() % 40 + 80) / 100; damage = damage * (rand() % 40 + 80) / 100;
// Adaptium/Blastons/Cosmix // Adaptium/Blastons/Cosmix
@ -1053,7 +1059,7 @@ void MobManager::pcAttackChars(CNSocket *sock, CNPacketData *data) {
else else
damage.first = plr->pointDamage; damage.first = plr->pointDamage;
damage = getDamage(damage.first, target->defense, true, (plr->batteryW > 6 + plr->level), -1, -1, 1); damage = getDamage(damage.first, target->defense, true, (plr->batteryW > 6 + plr->level), -1, -1, 0);
if (plr->batteryW >= 6 + plr->level) if (plr->batteryW >= 6 + plr->level)
plr->batteryW -= 6 + plr->level; plr->batteryW -= 6 + plr->level;
@ -1188,6 +1194,9 @@ bool MobManager::aggroCheck(Mob *mob, time_t currTime) {
mob->roamY = mob->appearanceData.iY; mob->roamY = mob->appearanceData.iY;
mob->roamZ = mob->appearanceData.iZ; mob->roamZ = mob->appearanceData.iZ;
if (mob->groupLeader != 0)
followToCombat(mob);
return true; return true;
} }
@ -1380,3 +1389,39 @@ void MobManager::projectileHit(CNSocket* sock, CNPacketData* data) {
Bullets[plr->iID].erase(resp->iBulletID); Bullets[plr->iID].erase(resp->iBulletID);
} }
void MobManager::followToCombat(Mob *mob) {
if (Mobs.find(mob->groupLeader) != Mobs.end()) {
Mob* leadMob = Mobs[mob->groupLeader];
for (int i = 0; i < 4; i++) {
if (leadMob->groupMember[i] == 0)
break;
if (Mobs.find(leadMob->groupMember[i]) == Mobs.end()) {
std::cout << "[WARN] roamingStep: leader can't find a group member!" << std::endl;
continue;
}
Mob* followerMob = Mobs[leadMob->groupMember[i]];
if (followerMob->state == MobState::COMBAT)
continue;
followerMob->target = mob->target;
followerMob->state = MobState::COMBAT;
followerMob->nextMovement = getTime();
followerMob->nextAttack = 0;
followerMob->roamX = followerMob->appearanceData.iX;
followerMob->roamY = followerMob->appearanceData.iY;
followerMob->roamZ = followerMob->appearanceData.iZ;
}
leadMob->target = mob->target;
leadMob->state = MobState::COMBAT;
leadMob->nextMovement = getTime();
leadMob->nextAttack = 0;
leadMob->roamX = leadMob->appearanceData.iX;
leadMob->roamY = leadMob->appearanceData.iY;
leadMob->roamZ = leadMob->appearanceData.iZ;
}
}

View File

@ -163,4 +163,6 @@ namespace MobManager {
/// returns bullet id /// returns bullet id
int8_t addBullet(Player* plr, bool isGrenade); int8_t addBullet(Player* plr, bool isGrenade);
void followToCombat(Mob *mob);
} }