mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-26 15:00:06 +00:00
Implemented player tick (health/nano stamina).
* The player now heals while not in combat * Nanos lose stamina while active, regain it while resting * Using active nano powers drains stamina * Standing in FM patches/lakes now deals damage * Fixed a memory error in npcAttackPc() * Mobs now aggro when a player gets close * Mobs now give up the chase if the player gets out of the combat zone; they no longer try chasing until they themselves have left it * Added a few missing break statements in the loops in BuddyManager Other players are not yet instantly notified of health/stamina updates, as finding the correct way to do this has proven tricky. FM patch damage updates other player's views just fine, though.
This commit is contained in:
parent
43d268e142
commit
4fa6618abb
@ -38,6 +38,7 @@ void BuddyManager::requestBuddy(CNSocket* sock, CNPacketData* data) {
|
|||||||
for (auto pair : PlayerManager::players) {
|
for (auto pair : PlayerManager::players) {
|
||||||
if (pair.second.plr->PCStyle.iPC_UID == pkt->iBuddyPCUID) {
|
if (pair.second.plr->PCStyle.iPC_UID == pkt->iBuddyPCUID) {
|
||||||
otherSock = pair.first;
|
otherSock = pair.first;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,6 +74,7 @@ void BuddyManager::reqBuddyByName(CNSocket* sock, CNPacketData* data) {
|
|||||||
int sizeOfLNReq = sizeof(pair.second.plr->PCStyle.szLastName) / 17;
|
int sizeOfLNReq = sizeof(pair.second.plr->PCStyle.szLastName) / 17;
|
||||||
if (BuddyManager::firstNameCheck(pair.second.plr->PCStyle.szFirstName, pkt->szFirstName, sizeOfReq, sizeOfRes) == true && BuddyManager::lastNameCheck(pair.second.plr->PCStyle.szLastName, pkt->szLastName, sizeOfLNReq, sizeOfLNRes) == true) { //This long line of gorgeous parameters is to check if the player's name matches :eyes:
|
if (BuddyManager::firstNameCheck(pair.second.plr->PCStyle.szFirstName, pkt->szFirstName, sizeOfReq, sizeOfRes) == true && BuddyManager::lastNameCheck(pair.second.plr->PCStyle.szLastName, pkt->szLastName, sizeOfLNReq, sizeOfLNRes) == true) { //This long line of gorgeous parameters is to check if the player's name matches :eyes:
|
||||||
otherSock = pair.first;
|
otherSock = pair.first;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,6 +102,7 @@ void BuddyManager::reqAcceptBuddy(CNSocket* sock, CNPacketData* data) {
|
|||||||
for (auto pair : PlayerManager::players) {
|
for (auto pair : PlayerManager::players) {
|
||||||
if (pair.second.plr->PCStyle.iPC_UID == pkt->iBuddyPCUID) {
|
if (pair.second.plr->PCStyle.iPC_UID == pkt->iBuddyPCUID) {
|
||||||
otherSock = pair.first;
|
otherSock = pair.first;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,6 +169,7 @@ void BuddyManager::reqFindNameBuddyAccept(CNSocket* sock, CNPacketData* data) {
|
|||||||
int sizeOfLNReq = sizeof(pair.second.plr->PCStyle.szLastName) / 17;
|
int sizeOfLNReq = sizeof(pair.second.plr->PCStyle.szLastName) / 17;
|
||||||
if (BuddyManager::firstNameCheck(pair.second.plr->PCStyle.szFirstName, pkt->szFirstName, sizeOfReq, sizeOfRes) == true && BuddyManager::lastNameCheck(pair.second.plr->PCStyle.szLastName, pkt->szLastName, sizeOfLNReq, sizeOfLNRes) == true) {
|
if (BuddyManager::firstNameCheck(pair.second.plr->PCStyle.szFirstName, pkt->szFirstName, sizeOfReq, sizeOfRes) == true && BuddyManager::lastNameCheck(pair.second.plr->PCStyle.szLastName, pkt->szLastName, sizeOfLNReq, sizeOfLNRes) == true) {
|
||||||
otherSock = pair.first;
|
otherSock = pair.first;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,6 +427,9 @@ Player Database::DbToPlayer(DbPlayer player) {
|
|||||||
result.equippedNanos[1] = player.Nano2;
|
result.equippedNanos[1] = player.Nano2;
|
||||||
result.equippedNanos[2] = player.Nano3;
|
result.equippedNanos[2] = player.Nano3;
|
||||||
|
|
||||||
|
result.dotDamage = false;
|
||||||
|
result.inCombat = false;
|
||||||
|
|
||||||
result.iWarpLocationFlag = player.WarpLocationFlag;
|
result.iWarpLocationFlag = player.WarpLocationFlag;
|
||||||
result.aSkywayLocationFlag[0] = player.SkywayLocationFlag1;
|
result.aSkywayLocationFlag[0] = player.SkywayLocationFlag1;
|
||||||
result.aSkywayLocationFlag[1] = player.SkywayLocationFlag2;
|
result.aSkywayLocationFlag[1] = player.SkywayLocationFlag2;
|
||||||
|
@ -11,6 +11,7 @@ std::map<int32_t, Mob*> MobManager::Mobs;
|
|||||||
|
|
||||||
void MobManager::init() {
|
void MobManager::init() {
|
||||||
REGISTER_SHARD_TIMER(step, 200);
|
REGISTER_SHARD_TIMER(step, 200);
|
||||||
|
REGISTER_SHARD_TIMER(playerTick, 2000);
|
||||||
|
|
||||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ATTACK_NPCs, pcAttackNpcs);
|
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ATTACK_NPCs, pcAttackNpcs);
|
||||||
|
|
||||||
@ -103,7 +104,7 @@ void MobManager::npcAttackPc(Mob *mob) {
|
|||||||
atk->iHitFlag = 2;
|
atk->iHitFlag = 2;
|
||||||
|
|
||||||
mob->target->sendPacket((void*)respbuf, P_FE2CL_NPC_ATTACK_PCs, resplen);
|
mob->target->sendPacket((void*)respbuf, P_FE2CL_NPC_ATTACK_PCs, resplen);
|
||||||
PlayerManager::sendToViewable(mob->target, (void*)&pkt, P_FE2CL_NPC_ATTACK_PCs, resplen);
|
PlayerManager::sendToViewable(mob->target, (void*)respbuf, P_FE2CL_NPC_ATTACK_PCs, resplen);
|
||||||
|
|
||||||
if (plr->HP <= 0) {
|
if (plr->HP <= 0) {
|
||||||
mob->target = nullptr;
|
mob->target = nullptr;
|
||||||
@ -111,10 +112,6 @@ void MobManager::npcAttackPc(Mob *mob) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MobManager::combatBegin(CNSocket *sock, CNPacketData *data) {} // stub
|
|
||||||
void MobManager::combatEnd(CNSocket *sock, CNPacketData *data) {} // stub
|
|
||||||
void MobManager::dotDamageOnOff(CNSocket *sock, CNPacketData *data) {} // stub
|
|
||||||
|
|
||||||
void MobManager::giveReward(CNSocket *sock) {
|
void MobManager::giveReward(CNSocket *sock) {
|
||||||
Player *plr = PlayerManager::getPlayer(sock);
|
Player *plr = PlayerManager::getPlayer(sock);
|
||||||
|
|
||||||
@ -176,6 +173,7 @@ int MobManager::hitMob(CNSocket *sock, Mob *mob, int damage) {
|
|||||||
mob->target = sock;
|
mob->target = sock;
|
||||||
mob->state = MobState::COMBAT;
|
mob->state = MobState::COMBAT;
|
||||||
mob->nextMovement = getTime();
|
mob->nextMovement = getTime();
|
||||||
|
mob->nextAttack = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
mob->appearanceData.iHP -= damage;
|
mob->appearanceData.iHP -= damage;
|
||||||
@ -298,8 +296,8 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
|
|||||||
NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE));
|
NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE));
|
||||||
}
|
}
|
||||||
|
|
||||||
// retreat if kited too far
|
// retreat if the player leaves combat range
|
||||||
distance = hypot(mob->appearanceData.iX - mob->spawnX, mob->appearanceData.iY - mob->spawnY);
|
distance = hypot(plr->x - mob->spawnX, plr->y - mob->spawnY);
|
||||||
if (distance >= mob->data["m_iCombatRange"]) {
|
if (distance >= mob->data["m_iCombatRange"]) {
|
||||||
mob->target = nullptr;
|
mob->target = nullptr;
|
||||||
mob->state = MobState::RETREAT;
|
mob->state = MobState::RETREAT;
|
||||||
@ -313,6 +311,35 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
|
|||||||
* Bad Max, etc.) once those have been made.
|
* Bad Max, etc.) once those have been made.
|
||||||
*/
|
*/
|
||||||
void MobManager::roamingStep(Mob *mob, time_t currTime) {
|
void MobManager::roamingStep(Mob *mob, time_t currTime) {
|
||||||
|
/*
|
||||||
|
* We reuse nextAttack to avoid scanning for players all the time, but to still
|
||||||
|
* do so more often than if we waited for nextMovement (which is way too slow).
|
||||||
|
*/
|
||||||
|
if (mob->nextAttack == 0 || currTime < mob->nextAttack) {
|
||||||
|
mob->nextAttack = currTime + (int)mob->data["m_iDelayTime"] * 100;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Aggro on nearby players.
|
||||||
|
* Even if they're in range, we can't assume they're all in the same one chunk
|
||||||
|
* as the mob, since it might be near a chunk boundary.
|
||||||
|
*/
|
||||||
|
for (Chunk *chunk : mob->currentChunks) {
|
||||||
|
for (CNSocket *s : chunk->players) {
|
||||||
|
Player *plr = s->plr;
|
||||||
|
int distance = hypot(mob->appearanceData.iX - plr->x, mob->appearanceData.iY - plr->y);
|
||||||
|
if (distance > mob->data["m_iSightRange"])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// found player. engage.
|
||||||
|
mob->target = s;
|
||||||
|
mob->state = MobState::COMBAT;
|
||||||
|
mob->nextMovement = currTime;
|
||||||
|
mob->nextAttack = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// some mobs don't move (and we mustn't divide/modulus by zero)
|
// some mobs don't move (and we mustn't divide/modulus by zero)
|
||||||
if (mob->idleRange == 0)
|
if (mob->idleRange == 0)
|
||||||
return;
|
return;
|
||||||
@ -433,3 +460,107 @@ std::pair<int,int> MobManager::lerp(int x1, int y1, int x2, int y2, int speed) {
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MobManager::combatBegin(CNSocket *sock, CNPacketData *data) {
|
||||||
|
Player *plr = PlayerManager::getPlayer(sock);
|
||||||
|
plr->inCombat = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MobManager::combatEnd(CNSocket *sock, CNPacketData *data) {
|
||||||
|
Player *plr = PlayerManager::getPlayer(sock);
|
||||||
|
plr->inCombat = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MobManager::dotDamageOnOff(CNSocket *sock, CNPacketData *data) {
|
||||||
|
sP_CL2FE_DOT_DAMAGE_ONOFF *pkt = (sP_CL2FE_DOT_DAMAGE_ONOFF*)data->buf;
|
||||||
|
Player *plr = PlayerManager::getPlayer(sock);
|
||||||
|
|
||||||
|
plr->dotDamage = (bool)pkt->iFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MobManager::dealGooDamage(CNSocket *sock, int amount) {
|
||||||
|
size_t resplen = sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK) + sizeof(sSkillResult_DotDamage);
|
||||||
|
assert(resplen < CN_PACKET_BUFFER_SIZE - 8);
|
||||||
|
uint8_t respbuf[resplen];
|
||||||
|
|
||||||
|
memset(respbuf, 0, resplen);
|
||||||
|
|
||||||
|
sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK *pkt = (sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK*)respbuf;
|
||||||
|
sSkillResult_DotDamage *dmg = (sSkillResult_DotDamage*)(respbuf + sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK));
|
||||||
|
Player *plr = PlayerManager::getPlayer(sock);
|
||||||
|
|
||||||
|
// update player
|
||||||
|
plr->HP -= amount;
|
||||||
|
|
||||||
|
pkt->iID = plr->iID;
|
||||||
|
pkt->eCT = 1; // player
|
||||||
|
pkt->iTB_ID = ECSB_INFECTION; // sSkillResult_DotDamage
|
||||||
|
|
||||||
|
dmg->eCT = 1;
|
||||||
|
dmg->iID = plr->iID;
|
||||||
|
dmg->iDamage = amount;
|
||||||
|
dmg->iHP = plr->HP;
|
||||||
|
|
||||||
|
sock->sendPacket((void*)&respbuf, P_FE2CL_CHAR_TIME_BUFF_TIME_TICK, resplen);
|
||||||
|
PlayerManager::sendToViewable(sock, (void*)&respbuf, P_FE2CL_CHAR_TIME_BUFF_TIME_TICK, resplen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MobManager::playerTick(CNServer *serv, time_t currTime) {
|
||||||
|
static time_t lastHealTime = 0;
|
||||||
|
for (auto& pair : PlayerManager::players) {
|
||||||
|
CNSocket *sock = pair.first;
|
||||||
|
Player *plr = pair.second.plr;
|
||||||
|
bool transmit = false;
|
||||||
|
|
||||||
|
// fm patch/lake damage
|
||||||
|
if (plr->dotDamage)
|
||||||
|
dealGooDamage(sock, 150);
|
||||||
|
|
||||||
|
// a somewhat hacky way tick goo damage faster than heal, but eh
|
||||||
|
if (currTime - lastHealTime < 4000)
|
||||||
|
continue;
|
||||||
|
lastHealTime = currTime;
|
||||||
|
|
||||||
|
// heal
|
||||||
|
if (!plr->inCombat && plr->HP < PC_MAXHEALTH(plr->level)) {
|
||||||
|
plr->HP += 200;
|
||||||
|
if (plr->HP > PC_MAXHEALTH(plr->level))
|
||||||
|
plr->HP = PC_MAXHEALTH(plr->level);
|
||||||
|
transmit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
if (plr->activeNano != 0 && plr->equippedNanos[i] == plr->activeNano) { // spend stamina
|
||||||
|
plr->Nanos[plr->activeNano].iStamina -= 3;
|
||||||
|
|
||||||
|
if (plr->Nanos[plr->activeNano].iStamina < 0)
|
||||||
|
plr->activeNano = 0;
|
||||||
|
|
||||||
|
transmit = true;
|
||||||
|
} else if (plr->Nanos[plr->equippedNanos[i]].iStamina < 150) { // regain stamina
|
||||||
|
sNano& nano = plr->Nanos[plr->equippedNanos[i]];
|
||||||
|
nano.iStamina += 3;
|
||||||
|
|
||||||
|
if (nano.iStamina > 150)
|
||||||
|
nano.iStamina = 150;
|
||||||
|
|
||||||
|
transmit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transmit) {
|
||||||
|
INITSTRUCT(sP_FE2CL_REP_PC_TICK, pkt);
|
||||||
|
|
||||||
|
std::cout << "sending sP_FE2CL_REP_PC_TICK" << std::endl;
|
||||||
|
|
||||||
|
pkt.iHP = plr->HP;
|
||||||
|
pkt.iBatteryN = plr->batteryN;
|
||||||
|
|
||||||
|
pkt.aNano[0] = plr->Nanos[plr->equippedNanos[0]];
|
||||||
|
pkt.aNano[1] = plr->Nanos[plr->equippedNanos[1]];
|
||||||
|
pkt.aNano[2] = plr->Nanos[plr->equippedNanos[2]];
|
||||||
|
|
||||||
|
sock->sendPacket((void*)&pkt, P_FE2CL_REP_PC_TICK, sizeof(sP_FE2CL_REP_PC_TICK));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -86,6 +86,7 @@ namespace MobManager {
|
|||||||
|
|
||||||
void init();
|
void init();
|
||||||
void step(CNServer*, time_t);
|
void step(CNServer*, time_t);
|
||||||
|
void playerTick(CNServer*, time_t);
|
||||||
|
|
||||||
void deadStep(Mob*, time_t);
|
void deadStep(Mob*, time_t);
|
||||||
void combatStep(Mob*, time_t);
|
void combatStep(Mob*, time_t);
|
||||||
@ -96,6 +97,7 @@ namespace MobManager {
|
|||||||
void combatBegin(CNSocket *sock, CNPacketData *data);
|
void combatBegin(CNSocket *sock, CNPacketData *data);
|
||||||
void combatEnd(CNSocket *sock, CNPacketData *data);
|
void combatEnd(CNSocket *sock, CNPacketData *data);
|
||||||
void dotDamageOnOff(CNSocket *sock, CNPacketData *data);
|
void dotDamageOnOff(CNSocket *sock, CNPacketData *data);
|
||||||
|
void dealGooDamage(CNSocket *sock, int amount);
|
||||||
|
|
||||||
void npcAttackPc(Mob *mob);
|
void npcAttackPc(Mob *mob);
|
||||||
int hitMob(CNSocket *sock, Mob *mob, int damage);
|
int hitMob(CNSocket *sock, Mob *mob, int damage);
|
||||||
|
@ -518,10 +518,12 @@ void activePower(CNSocket *sock, CNPacketData *data,
|
|||||||
|
|
||||||
Player *plr = PlayerManager::getPlayer(sock);
|
Player *plr = PlayerManager::getPlayer(sock);
|
||||||
|
|
||||||
|
plr->Nanos[plr->activeNano].iStamina -= 40;
|
||||||
|
|
||||||
resp->iPC_ID = plr->iID;
|
resp->iPC_ID = plr->iID;
|
||||||
resp->iSkillID = skillId;
|
resp->iSkillID = skillId;
|
||||||
resp->iNanoID = nanoId;
|
resp->iNanoID = nanoId;
|
||||||
resp->iNanoStamina = 150;
|
resp->iNanoStamina = plr->Nanos[plr->activeNano].iStamina;
|
||||||
resp->eST = eSkillType;
|
resp->eST = eSkillType;
|
||||||
resp->iTargetCnt = pkt->iTargetCnt;
|
resp->iTargetCnt = pkt->iTargetCnt;
|
||||||
|
|
||||||
@ -570,7 +572,7 @@ void NanoManager::nanoBuff(CNSocket* sock, int16_t nanoId, int skillId, int16_t
|
|||||||
resp->iPC_ID = plr->iID;
|
resp->iPC_ID = plr->iID;
|
||||||
resp->iSkillID = skillId;
|
resp->iSkillID = skillId;
|
||||||
resp->iNanoID = nanoId;
|
resp->iNanoID = nanoId;
|
||||||
resp->iNanoStamina = 150;
|
resp->iNanoStamina = plr->Nanos[plr->activeNano].iStamina;
|
||||||
resp->eST = eSkillType;
|
resp->eST = eSkillType;
|
||||||
resp->iTargetCnt = 1;
|
resp->iTargetCnt = 1;
|
||||||
|
|
||||||
|
@ -44,6 +44,9 @@ struct Player {
|
|||||||
bool isTrading;
|
bool isTrading;
|
||||||
bool isTradeConfirm;
|
bool isTradeConfirm;
|
||||||
|
|
||||||
|
bool inCombat;
|
||||||
|
bool dotDamage;
|
||||||
|
|
||||||
int64_t aQuestFlag[16];
|
int64_t aQuestFlag[16];
|
||||||
int tasks[ACTIVE_MISSION_COUNT];
|
int tasks[ACTIVE_MISSION_COUNT];
|
||||||
int RemainingNPCCount[ACTIVE_MISSION_COUNT][3];
|
int RemainingNPCCount[ACTIVE_MISSION_COUNT][3];
|
||||||
|
Loading…
Reference in New Issue
Block a user