17 Commits
1.2 ... 1.2.1

Author SHA1 Message Date
CakeLancelot
d840b0bbd0 Update tdata ref 2020-10-25 13:14:08 -05:00
CakeLancelot
4da178d16c Remove mention of random characters from README
The random characters feature hasn't been available for a while now. I also clarified some information regarding usage and the public server.
2020-10-24 23:57:17 -05:00
CakeLancelot
0d65fc2653 Update tdata submodule 2020-10-24 18:34:47 -05:00
Titan
6d97aaa1d0 Make nano skill change consume FM & power items 2020-10-24 18:31:07 -04:00
Gent
4ab686bc46 Fix abused unordered map 2020-10-24 18:30:02 -04:00
CakeLancelot
2302c28ac5 Merge pull request #141 from CakeLancelot/orm-update
Update sqlite_orm to 1.6
2020-10-24 14:31:57 -05:00
c8497a4856 Implement two more mission types + tweaks & fixes
* Weapons will consume your batteries fully.
* Nerfed enemy damage at lower levels.
* Further reworked drain, uses a static variable as a timer (lastDrainTime)
* resendMobHP has been repurposed to drainMobHP.
* Players heal faster after a sizable cooldown.
* Nano type advantage is more noticeable during combat.

Implemented two more mission types + Tweaks

* Item delivery quests now work.
* Timed missions now work.
* All escort missions (type 6) are skipped.
* /minfo now also prints the terminator npc.
* Weapon battery consumption tweaked
* Fixed indentations.
* Heal nanos have better output (25% -> 35%)
* Damage formula had a slight tweak.
* Bugfixed weapon equipping.
* Other tweaks
2020-10-24 14:04:42 -04:00
177c5f0f17 Nano Drain, Debuffs are timed
* Nano drain power works, currently does 30% damage over a period of 3 seconds.
* Stun, Sleep and Snare powers will now run out of time on mobs.
* A few other adjustments to mob mobility.
2020-10-24 14:00:00 -04:00
2782706355 Group warping & mob movement smoothing
* Warping into IZs and Fusion Lairs now will also take into account your group members.
* MobManager lerp does not confusingly divide speed by 2 anymore.
* Mobs pursue their targets more smoothly, they will avoid phasing into the player during combat.
* Nerfed retreat speed by a factor of 1.5, normal mobs retreated way too quickly however mobs like Don Doom and Bad Max do not retreat fast enough.
* Bugfixed sendPlayerTo, it did not call updatePlayerPosition leaving undesirable anomalies.
2020-10-24 14:00:00 -04:00
CakeLancelot
a969988b5c Update sqlite_orm to 1.6 2020-10-24 09:47:46 -05:00
CakeLancelot
bf3c19764b Merge pull request #140 from JadeShrineMaiden/chunk-desync-fix
Possibly fixed all chunking desyncs
2020-10-23 22:06:20 -05:00
cc06fdcf60 Possibly fixed all chunking desyncs
* Players/NPCs that initialize chunks now correctly pull their main chunk into their viewdata.
* Tested on the public server, seems to have solved the chunking issues.
2020-10-24 03:47:34 +01:00
3b5af415fb Fixed the NULL Player* in PlayerManager::players bug. 2020-10-23 05:32:14 +02:00
Gent
2b650b0bed Cleaned up warp code. 2020-10-22 13:14:24 -04:00
CakeLancelot
512647974d Fix Numbuh 3's sneak and revive abilities being swapped
Also fix a small typo
2020-10-21 10:05:48 -05:00
c9f9b093f4 Bugfixes.
* Add newly created chunks to nearby players and NPCs. This fixes the
slider/static path mob pop-in problem.
* Update a player's chunks when resurrecting. This fixes a mob desync
problem.
* Use a private instance for the Time Lab
* Spawn a slider for every stop
* Fix mobs in private lairs using the template chunk mobs's current
health for their max health
* Don't call into the JSON lib in the loop in aggroCheck(). This is an
optimization found after using gprof.
* Don't print NPC deletions to console. This stops the spam when a
private instance is deleted.
* Changed default view distance to half the length of a map tile, so
chunks are aligned to map tiles
* Update tdata reference
2020-10-21 02:38:30 +02:00
Gent
49d8ed2e36 Slider path, fixes, tweaks 2020-10-20 10:40:50 -04:00
22 changed files with 495 additions and 235 deletions

1
.gitignore vendored
View File

@@ -13,3 +13,4 @@ build/
*.db
version.h
infer-out
gmon.out

View File

@@ -16,14 +16,11 @@ tl;dr:
From then on, any time you want to run the "game":
3. Run `Server/winfusion.exe`
3. Run `OpenFusion/winfusion.exe` (optional if you're using the public server)
4. Run `FreeClient/OpenFusionClient.exe`
Currently the client by default connects to a public server hosted by Cake. Change the loginInfo.php to point to your own server if you want to host your own.
You have two randomized characters available to you on the Character Selection screen, one boy, one girl.
You can also make your own character and play through the tutorial. The tutorial can be skipped by pressing the ~ key.
If you want, [compiled binaries (artifacts) for each new commit can be found on AppVeyor.](https://ci.appveyor.com/project/OpenFusionProject/openfusion)
For a more detailed overview of the game's architecture and how to configure it, read the following sections.

View File

@@ -21,7 +21,7 @@ port=8002
ip=127.0.0.1
# distance at which other players and NPCs become visible.
# this value is used for calculating chunk size
viewdistance=30000
viewdistance=25600
# time, in milliseconds, to wait before kicking a non-responsive client
# default is 1 minute
timeout=60000

View File

@@ -31,7 +31,8 @@ void CNShardServer::handlePacket(CNSocket* sock, CNPacketData* data) {
else if (settings::VERBOSITY > 0)
std::cerr << "OpenFusion: SHARD UNIMPLM ERR. PacketType: " << Defines::p2str(CL2FE, data->type) << " (" << data->type << ")" << std::endl;
PlayerManager::players[sock].lastHeartbeat = getTime();
if (PlayerManager::players.find(sock) != PlayerManager::players.end())
PlayerManager::players[sock].lastHeartbeat = getTime();
}
void CNShardServer::keepAliveTimer(CNServer* serv, time_t currTime) {

View File

@@ -411,6 +411,7 @@ void minfoCommand(std::string full, std::vector<std::string>& args, CNSocket* so
ChatManager::sendServerMessage(sock, "[MINFO] Current task ID: " + std::to_string(plr->tasks[i]));
ChatManager::sendServerMessage(sock, "[MINFO] Current task type: " + std::to_string((int)(task["m_iHTaskType"])));
ChatManager::sendServerMessage(sock, "[MINFO] Current waypoint NPC ID: " + std::to_string((int)(task["m_iSTGrantWayPoint"])));
ChatManager::sendServerMessage(sock, "[MINFO] Current terminator NPC ID: " + std::to_string((int)(task["m_iHTerminatorNPCID"])));
for (int j = 0; j < 3; j++)
if ((int)(task["m_iCSUEnemyID"][j]) != 0)

View File

@@ -8,34 +8,72 @@ std::map<std::tuple<int, int, uint64_t>, Chunk*> ChunkManager::chunks;
void ChunkManager::init() {} // stubbed
void ChunkManager::newChunk(std::tuple<int, int, uint64_t> pos) {
Chunk *chunk = new Chunk();
chunk->players = std::set<CNSocket*>();
chunk->NPCs = std::set<int32_t>();
chunks[pos] = chunk;
}
void ChunkManager::populateNewChunk(Chunk* chunk, std::tuple<int, int, uint64_t> pos) {// add the new chunk to every player and mob that's near it
for (Chunk *c : grabChunks(pos)) {
if (c == chunk)
continue;
for (CNSocket *s : c->players)
PlayerManager::players[s].currentChunks.push_back(chunk);
for (int32_t id : c->NPCs)
NPCManager::NPCs[id]->currentChunks.push_back(chunk);
}
}
void ChunkManager::addNPC(int posX, int posY, uint64_t instanceID, int32_t id) {
std::tuple<int, int, uint64_t> pos = grabChunk(posX, posY, instanceID);
bool newChunkUsed = false;
// make chunk if it doesn't exist!
if (chunks.find(pos) == chunks.end()) {
chunks[pos] = new Chunk();
chunks[pos]->players = std::set<CNSocket*>();
chunks[pos]->NPCs = std::set<int32_t>();
newChunk(pos);
newChunkUsed = true;
}
Chunk* chunk = chunks[pos];
if (newChunkUsed)
NPCManager::NPCs[id]->currentChunks.push_back(chunk);
chunk->NPCs.insert(id);
// we must update other players after the NPC is added to chunk
if (newChunkUsed)
populateNewChunk(chunk, pos);
}
void ChunkManager::addPlayer(int posX, int posY, uint64_t instanceID, CNSocket* sock) {
std::tuple<int, int, uint64_t> pos = grabChunk(posX, posY, instanceID);
bool newChunkUsed = false;
// make chunk if it doesn't exist!
if (chunks.find(pos) == chunks.end()) {
chunks[pos] = new Chunk();
chunks[pos]->players = std::set<CNSocket*>();
chunks[pos]->NPCs = std::set<int32_t>();
newChunk(pos);
newChunkUsed = true;
}
Chunk* chunk = chunks[pos];
if (newChunkUsed)
PlayerManager::players[sock].currentChunks.push_back(chunk);
chunk->players.insert(sock);
// we must update other players after this player is added to chunk
if (newChunkUsed)
populateNewChunk(chunk, pos);
}
bool ChunkManager::removePlayer(std::tuple<int, int, uint64_t> chunkPos, CNSocket* sock) {
@@ -212,7 +250,7 @@ void ChunkManager::createInstance(uint64_t instanceID) {
BaseNPC* baseNPC = NPCManager::NPCs[npcID];
if (baseNPC->npcClass == NPC_MOB) {
Mob* newMob = new Mob(baseNPC->appearanceData.iX, baseNPC->appearanceData.iY, baseNPC->appearanceData.iZ, baseNPC->appearanceData.iAngle,
instanceID, baseNPC->appearanceData.iNPCType, baseNPC->appearanceData.iHP, NPCManager::NPCData[baseNPC->appearanceData.iNPCType], newID);
instanceID, baseNPC->appearanceData.iNPCType, ((Mob*)baseNPC)->maxHealth, NPCManager::NPCData[baseNPC->appearanceData.iNPCType], newID);
NPCManager::NPCs[newID] = newMob;
MobManager::Mobs[newID] = newMob;
} else {

View File

@@ -26,6 +26,8 @@ namespace ChunkManager {
extern std::map<std::tuple<int, int, uint64_t>, Chunk*> chunks;
void newChunk(std::tuple<int, int, uint64_t> pos);
void populateNewChunk(Chunk* chunk, std::tuple<int, int, uint64_t> pos);
void addNPC(int posX, int posY, uint64_t instanceID, int32_t id);
void addPlayer(int posX, int posY, uint64_t instanceID, CNSocket* sock);
bool removePlayer(std::tuple<int, int, uint64_t> chunkPos, CNSocket* sock);

View File

@@ -29,12 +29,12 @@ void GroupManager::requestGroup(CNSocket* sock, CNPacketData* data) {
Player* otherPlr = PlayerManager::getPlayerFromID(recv->iID_To);
if (plr == nullptr || otherPlr == nullptr)
return;
return;
otherPlr = PlayerManager::getPlayerFromID(otherPlr->iIDGroup);
if (otherPlr == nullptr)
return;
return;
// fail if the group is full or the other player is already in a group
if (plr->groupCnt >= 4 || otherPlr->groupCnt > 1) {
@@ -50,9 +50,9 @@ void GroupManager::requestGroup(CNSocket* sock, CNPacketData* data) {
INITSTRUCT(sP_FE2CL_PC_GROUP_INVITE, resp);
resp.iHostID = plr->iIDGroup;
resp.iHostID = plr->iIDGroup;
otherSock->sendPacket((void*)&resp, P_FE2CL_PC_GROUP_INVITE, sizeof(sP_FE2CL_PC_GROUP_INVITE));
otherSock->sendPacket((void*)&resp, P_FE2CL_PC_GROUP_INVITE, sizeof(sP_FE2CL_PC_GROUP_INVITE));
}
void GroupManager::refuseGroup(CNSocket* sock, CNPacketData* data) {
@@ -61,7 +61,7 @@ void GroupManager::refuseGroup(CNSocket* sock, CNPacketData* data) {
sP_CL2FE_REQ_PC_GROUP_INVITE_REFUSE* recv = (sP_CL2FE_REQ_PC_GROUP_INVITE_REFUSE*)data->buf;
CNSocket* otherSock = PlayerManager::getSockFromID(recv->iID_From);
CNSocket* otherSock = PlayerManager::getSockFromID(recv->iID_From);
if (otherSock == nullptr)
return;
@@ -69,13 +69,13 @@ void GroupManager::refuseGroup(CNSocket* sock, CNPacketData* data) {
Player* plr = PlayerManager::getPlayer(sock);
if (plr == nullptr)
return;
return;
INITSTRUCT(sP_FE2CL_PC_GROUP_INVITE_REFUSE, resp);
resp.iID_To = plr->iID;
otherSock->sendPacket((void*)&resp, P_FE2CL_PC_GROUP_INVITE_REFUSE, sizeof(sP_FE2CL_PC_GROUP_INVITE_REFUSE));
otherSock->sendPacket((void*)&resp, P_FE2CL_PC_GROUP_INVITE_REFUSE, sizeof(sP_FE2CL_PC_GROUP_INVITE_REFUSE));
}
void GroupManager::joinGroup(CNSocket* sock, CNPacketData* data) {
@@ -87,12 +87,12 @@ void GroupManager::joinGroup(CNSocket* sock, CNPacketData* data) {
Player* otherPlr = PlayerManager::getPlayerFromID(recv->iID_From);
if (plr == nullptr || otherPlr == nullptr)
return;
return;
otherPlr = PlayerManager::getPlayerFromID(otherPlr->iIDGroup);
if (otherPlr == nullptr)
return;
return;
// fail if the group is full or the other player is already in a group
if (plr->groupCnt > 1 || plr->iIDGroup != plr->iID || otherPlr->groupCnt >= 4) {
@@ -172,7 +172,7 @@ void GroupManager::chatGroup(CNSocket* sock, CNPacketData* data) {
Player* otherPlr = PlayerManager::getPlayerFromID(plr->iIDGroup);
if (plr == nullptr || otherPlr == nullptr)
return;
return;
// send to client
INITSTRUCT(sP_FE2CL_REP_SEND_ALL_GROUP_FREECHAT_MESSAGE_SUCC, resp);
@@ -184,14 +184,14 @@ void GroupManager::chatGroup(CNSocket* sock, CNPacketData* data) {
void GroupManager::menuChatGroup(CNSocket* sock, CNPacketData* data) {
if (data->size != sizeof(sP_CL2FE_REQ_SEND_ALL_GROUP_MENUCHAT_MESSAGE))
return; // malformed packet
return; // malformed packet
sP_CL2FE_REQ_SEND_ALL_GROUP_MENUCHAT_MESSAGE* chat = (sP_CL2FE_REQ_SEND_ALL_GROUP_MENUCHAT_MESSAGE*)data->buf;
Player* plr = PlayerManager::getPlayer(sock);
Player* otherPlr = PlayerManager::getPlayerFromID(plr->iIDGroup);
if (plr == nullptr || otherPlr == nullptr)
return;
return;
// send to client
INITSTRUCT(sP_FE2CL_REP_SEND_ALL_GROUP_MENUCHAT_MESSAGE_SUCC, resp);
@@ -276,7 +276,7 @@ void GroupManager::groupKickPlayer(Player* plr) {
Player* otherPlr = PlayerManager::getPlayerFromID(plr->iIDGroup);
if (otherPlr == nullptr)
return;
return;
if (!validOutVarPacket(sizeof(sP_FE2CL_PC_GROUP_LEAVE), otherPlr->groupCnt - 1, sizeof(sPCGroupMemberInfo))) {
std::cout << "[WARN] bad sP_FE2CL_PC_GROUP_LEAVE packet size\n";
@@ -340,7 +340,7 @@ void GroupManager::groupKickPlayer(Player* plr) {
CNSocket* sock = PlayerManager::getSockFromID(plr->iID);
if (sock == nullptr)
return;
return;
INITSTRUCT(sP_FE2CL_PC_GROUP_LEAVE_SUCC, resp1);
sock->sendPacket((void*)&resp1, P_FE2CL_PC_GROUP_LEAVE_SUCC, sizeof(sP_FE2CL_PC_GROUP_LEAVE_SUCC));

View File

@@ -140,12 +140,12 @@ void ItemManager::itemMoveHandler(CNSocket* sock, CNPacketData* data) {
INITSTRUCT(sP_FE2CL_PC_EQUIP_CHANGE, equipChange);
equipChange.iPC_ID = plr.plr->iID;
if (itemmove->eFrom == (int)SlotType::EQUIP) {
equipChange.iEquipSlotNum = itemmove->iFromSlotNum;
equipChange.EquipSlotItem = resp.ToSlotItem;
} else {
if (itemmove->eTo == (int)SlotType::EQUIP) {
equipChange.iEquipSlotNum = itemmove->iToSlotNum;
equipChange.EquipSlotItem = resp.FromSlotItem;
} else {
equipChange.iEquipSlotNum = itemmove->iFromSlotNum;
equipChange.EquipSlotItem = resp.ToSlotItem;
}
// unequip vehicle if equip slot 8 is 0

View File

@@ -77,20 +77,27 @@ void MissionManager::taskStart(CNSocket* sock, CNPacketData* data) {
return;
}
TaskData& task = *Tasks[missionData->iTaskNum];
response.iTaskNum = missionData->iTaskNum;
response.iRemainTime = task["m_iSTGrantTimer"];
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_TASK_START_SUCC, sizeof(sP_FE2CL_REP_PC_TASK_START_SUCC));
// HACK: auto-succeed Eduardo escort task
// TODO: maybe check for iTaskType == 6 and skip all escort missions?
if (missionData->iTaskNum == 576) {
// HACK: auto-succeed escort task
if (task["m_iHTaskType"] == 6) {
std::cout << "Sending Eduardo success packet" << std::endl;
INITSTRUCT(sP_FE2CL_REP_PC_TASK_END_SUCC, response);
endTask(sock, 576);
response.iTaskNum = 576;
endTask(sock, missionData->iTaskNum);
response.iTaskNum = missionData->iTaskNum;
sock->sendPacket((void*)&response, P_FE2CL_REP_PC_TASK_END_SUCC, sizeof(sP_FE2CL_REP_PC_TASK_END_SUCC));
}
// Give player their delivery items at the start.
for (int i = 0; i < 3; i++)
if (task["m_iSTItemID"][i] != 0 && task["m_iSTItemNumNeeded"][i] > 0)
dropQuestItem(sock, missionData->iTaskNum, task["m_iSTItemNumNeeded"][i], task["m_iSTItemID"][i], 0);
}
void MissionManager::taskEnd(CNSocket* sock, CNPacketData* data) {
@@ -98,6 +105,25 @@ void MissionManager::taskEnd(CNSocket* sock, CNPacketData* data) {
return; // malformed packet
sP_CL2FE_REQ_PC_TASK_END* missionData = (sP_CL2FE_REQ_PC_TASK_END*)data->buf;
// failed timed missions give an iNPC_ID of 0
if (missionData->iNPC_ID == 0) {
TaskData* task = MissionManager::Tasks[missionData->iTaskNum];
// double-checking
if (task->task["m_iHTaskType"] == 3) {
Player* plr = PlayerManager::getPlayer(sock);
int failTaskID = task->task["m_iFOutgoingTask"];
if (failTaskID != 0) {
MissionManager::quitTask(sock, missionData->iTaskNum, false);
for (int i = 0; i < 6; i++)
if (plr->tasks[i] == missionData->iTaskNum)
plr->tasks[i] = failTaskID;
return;
}
}
}
INITSTRUCT(sP_FE2CL_REP_PC_TASK_END_SUCC, response);
response.iTaskNum = missionData->iTaskNum;

View File

@@ -85,11 +85,12 @@ void MobManager::pcAttackNpcs(CNSocket *sock, CNPacketData *data) {
damage.first = plr->pointDamage;
int difficulty = (int)mob->data["m_iNpcLevel"];
damage = getDamage(damage.first, (int)mob->data["m_iProtection"], true, (plr->batteryW >= 11 + difficulty), NanoManager::nanoStyle(plr->activeNano), (int)mob->data["m_iNpcStyle"], difficulty);
if (plr->batteryW >= 11 + difficulty)
plr->batteryW -= 11 + difficulty;
damage = getDamage(damage.first, (int)mob->data["m_iProtection"], true, (plr->batteryW > 6 + difficulty), NanoManager::nanoStyle(plr->activeNano), (int)mob->data["m_iNpcStyle"], difficulty);
if (plr->batteryW >= 6 + difficulty)
plr->batteryW -= 6 + difficulty;
else
plr->batteryW = 0;
damage.first = hitMob(sock, mob, damage.first);
@@ -126,7 +127,7 @@ void MobManager::npcAttackPc(Mob *mob, time_t currTime) {
sP_FE2CL_NPC_ATTACK_PCs *pkt = (sP_FE2CL_NPC_ATTACK_PCs*)respbuf;
sAttackResult *atk = (sAttackResult*)(respbuf + sizeof(sP_FE2CL_NPC_ATTACK_PCs));
auto damage = getDamage(475 + (int)mob->data["m_iPower"], plr->defense, false, false, -1, -1, 1);
auto damage = getDamage(450 + (int)mob->data["m_iPower"], plr->defense, false, false, -1, -1, rand() % plr->level + 1);
plr->HP -= damage.first;
pkt->iNPC_ID = mob->appearanceData.iNPC_ID;
@@ -330,8 +331,15 @@ int MobManager::hitMob(CNSocket *sock, Mob *mob, int damage) {
mob->appearanceData.iHP -= damage;
// wake up sleeping monster
// TODO: remove client-side bit somehow
mob->appearanceData.iConditionBitFlag &= ~CSB_BIT_MEZ;
if (mob->appearanceData.iConditionBitFlag & CSB_BIT_MEZ) {
mob->appearanceData.iConditionBitFlag &= ~CSB_BIT_MEZ;
INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, pkt1);
pkt1.eCT = 2;
pkt1.iID = mob->appearanceData.iNPC_ID;
pkt1.iConditionBitFlag = mob->appearanceData.iConditionBitFlag;
NPCManager::sendToViewable(mob, &pkt1, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT));
}
if (mob->appearanceData.iHP <= 0)
killMob(mob->target, mob);
@@ -467,7 +475,15 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
if (distance <= (int)mob->data["m_iAtkRange"]) {
// attack logic
if (mob->nextAttack == 0) {
mob->nextAttack = currTime + (int)mob->data["m_iInitalTime"] * 100; // I *think* this is what this is
INITSTRUCT(sP_FE2CL_NPC_MOVE, pkt);
pkt.iNPC_ID = mob->appearanceData.iNPC_ID;
pkt.iSpeed = (int)mob->data["m_iRunSpeed"];
pkt.iToX = mob->appearanceData.iX;
pkt.iToY = mob->appearanceData.iY;
pkt.iToZ = mob->target->plr->z;
NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE));
mob->nextAttack = currTime + (int)mob->data["m_iInitalTime"] * 100; //I *think* this is what this is
npcAttackPc(mob, currTime);
} else if (mob->nextAttack != 0 && currTime >= mob->nextAttack) {
mob->nextAttack = currTime + (int)mob->data["m_iDelayTime"] * 100;
@@ -477,7 +493,9 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
// movement logic
if (mob->nextMovement != 0 && currTime < mob->nextMovement)
return;
mob->nextMovement = currTime + 500;
mob->nextMovement = currTime + 400;
if (currTime >= mob->nextAttack)
mob->nextAttack = 0;
int speed = mob->data["m_iRunSpeed"];
@@ -485,7 +503,7 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
if (mob->appearanceData.iConditionBitFlag & CSB_BIT_DN_MOVE_SPEED)
speed /= 2;
auto targ = lerp(mob->appearanceData.iX, mob->appearanceData.iY, mob->target->plr->x, mob->target->plr->y, speed);
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));
NPCManager::updateNPCPosition(mob->appearanceData.iNPC_ID, targ.first, targ.second, mob->appearanceData.iZ);
@@ -495,7 +513,7 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
pkt.iSpeed = speed;
pkt.iToX = mob->appearanceData.iX = targ.first;
pkt.iToY = mob->appearanceData.iY = targ.second;
pkt.iToZ = mob->appearanceData.iZ;
pkt.iToZ = mob->target->plr->z;
// notify all nearby players
NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE));
@@ -578,25 +596,25 @@ void MobManager::roamingStep(Mob *mob, time_t currTime) {
}
void MobManager::retreatStep(Mob *mob, time_t currTime) {
// distance between spawn point and current location
if (mob->nextMovement != 0 && currTime < mob->nextMovement)
return;
mob->nextMovement = currTime + 500;
mob->nextMovement = currTime + 400;
// distance between spawn point and current location
int distance = hypot(mob->appearanceData.iX - mob->roamX, mob->appearanceData.iY - mob->roamY);
//if (distance > mob->data["m_iIdleRange"]) {
if (distance > 10) {
INITSTRUCT(sP_FE2CL_NPC_MOVE, pkt);
auto targ = lerp(mob->appearanceData.iX, mob->appearanceData.iY, mob->roamX, mob->roamY, (int)mob->data["m_iRunSpeed"] * 3);
auto targ = lerp(mob->appearanceData.iX, mob->appearanceData.iY, mob->roamX, mob->roamY, (int)mob->data["m_iRunSpeed"]*4/5);
pkt.iNPC_ID = mob->appearanceData.iNPC_ID;
pkt.iSpeed = (int)mob->data["m_iRunSpeed"] * 3;
pkt.iSpeed = (int)mob->data["m_iRunSpeed"] * 2;
pkt.iToX = mob->appearanceData.iX = targ.first;
pkt.iToY = mob->appearanceData.iY = targ.second;
pkt.iToZ = mob->appearanceData.iZ;
pkt.iToZ = mob->appearanceData.iZ = mob->spawnZ;
// notify all nearby players
NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE));
@@ -610,12 +628,15 @@ void MobManager::retreatStep(Mob *mob, time_t currTime) {
mob->killedTime = 0;
mob->nextAttack = 0;
mob->appearanceData.iConditionBitFlag = 0;
resendMobHP(mob);
// HACK: we haven't found a better way to refresh a mob's client-side status
drainMobHP(mob, 0);
}
}
void MobManager::step(CNServer *serv, time_t currTime) {
static time_t lastDrainTime = 0;
for (auto& pair : Mobs) {
int x = pair.second->appearanceData.iX;
int y = pair.second->appearanceData.iY;
@@ -624,6 +645,30 @@ void MobManager::step(CNServer *serv, time_t currTime) {
if (!ChunkManager::inPopulatedChunks(x, y, pair.second->instanceID))
continue;
// drain
if (currTime - lastDrainTime >= 600 && pair.second->appearanceData.iConditionBitFlag & CSB_BIT_BOUNDINGBALL) {
drainMobHP(pair.second, pair.second->maxHealth * 3 / 50); // lose 10% every second
}
// unbuffing
std::unordered_map<int32_t, time_t>::iterator it = pair.second->unbuffTimes.begin();
while (it != pair.second->unbuffTimes.end()) {
if (currTime >= it->second) {
pair.second->appearanceData.iConditionBitFlag &= ~it->first;
INITSTRUCT(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT, pkt1);
pkt1.eCT = 2;
pkt1.iID = pair.second->appearanceData.iNPC_ID;
pkt1.iConditionBitFlag = pair.second->appearanceData.iConditionBitFlag;
NPCManager::sendToViewable(pair.second, &pkt1, P_FE2CL_CHAR_TIME_BUFF_TIME_OUT, sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_OUT));
it = pair.second->unbuffTimes.erase(it);
} else {
it++;
}
}
// skip mob movement and combat if disabled
if (!simulateMobs && pair.second->state != MobState::DEAD
&& pair.second->state != MobState::RETREAT)
@@ -652,6 +697,9 @@ void MobManager::step(CNServer *serv, time_t currTime) {
break;
}
}
if (currTime - lastDrainTime >= 600)
lastDrainTime = currTime;
// deallocate all NPCs queued for removal
while (RemovalQueue.size() > 0) {
@@ -667,8 +715,6 @@ void MobManager::step(CNServer *serv, time_t currTime) {
std::pair<int,int> MobManager::lerp(int x1, int y1, int x2, int y2, int speed) {
std::pair<int,int> ret = {x1, y1};
speed /= 2;
if (speed == 0)
return ret;
@@ -714,8 +760,10 @@ void MobManager::combatBegin(CNSocket *sock, CNPacketData *data) {
void MobManager::combatEnd(CNSocket *sock, CNPacketData *data) {
Player *plr = PlayerManager::getPlayer(sock);
if (plr != nullptr)
if (plr != nullptr) {
plr->inCombat = false;
plr->healCooldown = 4000;
}
}
void MobManager::dotDamageOnOff(CNSocket *sock, CNPacketData *data) {
@@ -810,11 +858,14 @@ void MobManager::playerTick(CNServer *serv, time_t currTime) {
dealGooDamage(sock, PC_MAXHEALTH(plr->level) * 3 / 20);
// heal
if (currTime - lastHealTime >= 6000 && !plr->inCombat && plr->HP < PC_MAXHEALTH(plr->level)) {
plr->HP += PC_MAXHEALTH(plr->level) / 5;
if (plr->HP > PC_MAXHEALTH(plr->level))
plr->HP = PC_MAXHEALTH(plr->level);
transmit = true;
if (currTime - lastHealTime >= 4000 && !plr->inCombat && plr->HP < PC_MAXHEALTH(plr->level)) {
if (currTime - lastHealTime - plr->healCooldown >= 4000) {
plr->HP += PC_MAXHEALTH(plr->level) / 5;
if (plr->HP > PC_MAXHEALTH(plr->level))
plr->HP = PC_MAXHEALTH(plr->level);
transmit = true;
} else
plr->healCooldown -= 4000;
}
for (int i = 0; i < 3; i++) {
@@ -856,7 +907,7 @@ void MobManager::playerTick(CNServer *serv, time_t currTime) {
}
// if this was a heal tick, update the counter outside of the loop
if (currTime - lastHealTime >= 6000)
if (currTime - lastHealTime >= 4000)
lastHealTime = currTime;
}
@@ -869,15 +920,15 @@ std::pair<int,int> MobManager::getDamage(int attackPower, int defensePower, bool
// base calculation
int damage = attackPower * attackPower / (attackPower + defensePower);
damage = std::max(std::max(29, attackPower / 7), damage - defensePower * (12 + difficulty) / 65);
damage = std::max(10 + attackPower / 10, damage - defensePower * (4 + difficulty) / 40);
damage = damage * (rand() % 40 + 80) / 100;
// Adaptium/Blastons/Cosmix
if (attackerStyle != -1 && defenderStyle != -1 && attackerStyle != defenderStyle) {
if (attackerStyle < defenderStyle || attackerStyle - defenderStyle == 2)
damage = damage * 5 / 4;
if (attackerStyle < defenderStyle || attackerStyle - defenderStyle == 2)
damage = damage * 3 / 2;
else
damage = damage * 4 / 5;
damage = damage * 2 / 3;
}
// weapon boosts
@@ -950,10 +1001,12 @@ void MobManager::pcAttackChars(CNSocket *sock, CNPacketData *data) {
else
damage.first = plr->pointDamage;
damage = getDamage(damage.first, target->defense, true, (plr->batteryW >= 12), -1, -1, 1);
damage = getDamage(damage.first, target->defense, true, (plr->batteryW > 6 + plr->level), -1, -1, 1);
if (plr->batteryW >= 12)
plr->batteryW -= 12;
if (plr->batteryW >= 6 + plr->level)
plr->batteryW -= 6 + plr->level;
else
plr->batteryW = 0;
target->HP -= damage.first;
@@ -979,11 +1032,13 @@ void MobManager::pcAttackChars(CNSocket *sock, CNPacketData *data) {
int difficulty = (int)mob->data["m_iNpcLevel"];
damage = getDamage(damage.first, (int)mob->data["m_iProtection"], true, (plr->batteryW >= 11 + difficulty),
damage = getDamage(damage.first, (int)mob->data["m_iProtection"], true, (plr->batteryW > 6 + difficulty),
NanoManager::nanoStyle(plr->activeNano), (int)mob->data["m_iNpcStyle"], difficulty);
if (plr->batteryW >= 11 + difficulty)
plr->batteryW -= 11 + difficulty;
if (plr->batteryW >= 6 + difficulty)
plr->batteryW -= 6 + difficulty;
else
plr->batteryW = 0;
damage.first = hitMob(sock, mob, damage.first);
@@ -1007,25 +1062,24 @@ void MobManager::pcAttackChars(CNSocket *sock, CNPacketData *data) {
PlayerManager::sendToViewable(sock, (void*)respbuf, P_FE2CL_PC_ATTACK_CHARs, resplen);
}
// HACK: we haven't found a better way to refresh a mob's client-side status
void MobManager::resendMobHP(Mob *mob) {
size_t resplen = sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK) + sizeof(sSkillResult_Heal_HP);
void MobManager::drainMobHP(Mob *mob, int amount) {
size_t resplen = sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK) + sizeof(sSkillResult_Damage);
assert(resplen < CN_PACKET_BUFFER_SIZE - 8);
uint8_t respbuf[CN_PACKET_BUFFER_SIZE];
memset(respbuf, 0, resplen);
sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK *pkt = (sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK*)respbuf;
sSkillResult_Heal_HP *heal = (sSkillResult_Heal_HP*)(respbuf + sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK));
sSkillResult_Damage *drain = (sSkillResult_Damage*)(respbuf + sizeof(sP_FE2CL_CHAR_TIME_BUFF_TIME_TICK));
pkt->iID = mob->appearanceData.iNPC_ID;
pkt->eCT = 4; // mob
pkt->iTB_ID = ECSB_HEAL; // sSkillResult_Heal_HP
pkt->iTB_ID = ECSB_BOUNDINGBALL;
heal->eCT = 4;
heal->iID = mob->appearanceData.iNPC_ID;
heal->iHealHP = 0;
heal->iHP = mob->appearanceData.iHP;
drain->eCT = 4;
drain->iID = mob->appearanceData.iNPC_ID;
drain->iDamage = amount;
drain->iHP = mob->appearanceData.iHP -= amount;
NPCManager::sendToViewable(mob, (void*)&respbuf, P_FE2CL_CHAR_TIME_BUFF_TIME_TICK, resplen);
}
@@ -1046,7 +1100,7 @@ bool MobManager::aggroCheck(Mob *mob, time_t currTime) {
if (plr->HP <= 0)
continue;
int mobRange = mob->data["m_iSightRange"];
int mobRange = mob->sightRange;
if (plr->iConditionBitFlag & CSB_BIT_UP_STEALTH)
mobRange /= 3;

View File

@@ -8,6 +8,7 @@
#include "contrib/JSON.hpp"
#include <map>
#include <unordered_map>
#include <queue>
enum class MobState {
@@ -27,6 +28,8 @@ struct Mob : public BaseNPC {
int spawnZ;
int level;
std::unordered_map<int32_t,time_t> unbuffTimes;
// dead
time_t killedTime = 0;
time_t regenTime;
@@ -35,13 +38,15 @@ struct Mob : public BaseNPC {
// roaming
int idleRange;
const int sightRange;
time_t nextMovement = 0;
bool staticPath = false;
int roamX, roamY, roamZ;
// combat
CNSocket *target = nullptr;
time_t nextAttack = 0;
int roamX, roamY, roamZ;
// drop
int dropType;
@@ -50,7 +55,9 @@ struct Mob : public BaseNPC {
nlohmann::json data;
Mob(int x, int y, int z, int angle, uint64_t iID, int type, int hp, nlohmann::json d, int32_t id)
: BaseNPC(x, y, z, angle, iID, type, id), maxHealth(hp) {
: BaseNPC(x, y, z, angle, iID, type, id),
maxHealth(hp),
sightRange(d["m_iSightRange"]) {
state = MobState::ROAMING;
data = d;
@@ -132,7 +139,7 @@ namespace MobManager {
std::pair<int,int> getDamage(int, int, bool, bool, int, int, int);
void pcAttackChars(CNSocket *sock, CNPacketData *data);
void resendMobHP(Mob *mob);
void drainMobHP(Mob *mob, int amount);
void incNextMovement(Mob *mob, time_t currTime=0);
bool aggroCheck(Mob *mob, time_t currTime);
}

View File

@@ -107,7 +107,7 @@ void NPCManager::addNPC(std::vector<Chunk*> viewableChunks, int32_t id) {
void NPCManager::destroyNPC(int32_t id) {
// sanity check
if (NPCs.find(id) == NPCs.end()) {
std::cout << "npc not found : " << id << std::endl;
std::cout << "npc not found: " << id << std::endl;
return;
}
@@ -133,8 +133,6 @@ void NPCManager::destroyNPC(int32_t id) {
// finally, remove it from the map and free it
NPCs.erase(id);
delete entity;
std::cout << "npc removed!" << std::endl;
}
void NPCManager::updateNPCPosition(int32_t id, int X, int Y, int Z, int angle) {
@@ -586,29 +584,37 @@ void NPCManager::handleWarp(CNSocket* sock, int32_t warpId) {
if (Warps.find(warpId) == Warps.end())
return;
MissionManager::failInstancedMissions(sock); // fail any missions that require the player's current instance
uint64_t fromInstance = plrv.plr->instanceID; // saved for post-warp
if (plrv.plr->instanceID == 0) {
// save last uninstanced coords
plrv.plr->lastX = plrv.plr->x;
plrv.plr->lastY = plrv.plr->y;
plrv.plr->lastZ = plrv.plr->z;
plrv.plr->lastAngle = plrv.plr->angle;
}
// std::cerr << "Warped to Map Num:" << Warps[warpId].instanceID << " NPC ID " << Warps[warpId].npcID << std::endl;
if (Warps[warpId].isInstance) {
uint64_t instanceID = Warps[warpId].instanceID;
if (Warps[warpId].limitTaskID != 0) { // if warp requires you to be on a mission, it's gotta be a unique instance
// if warp requires you to be on a mission, it's gotta be a unique instance
if (Warps[warpId].limitTaskID != 0 || instanceID == 14) { // 14 is a special case for the Time Lab
instanceID += ((uint64_t)plrv.plr->iIDGroup << 32); // upper 32 bits are leader ID
ChunkManager::createInstance(instanceID);
}
PlayerManager::sendPlayerTo(sock, Warps[warpId].x, Warps[warpId].y, Warps[warpId].z, instanceID);
} else {
INITSTRUCT(sP_FE2CL_REP_PC_WARP_USE_NPC_SUCC, resp); // Can only be used for exiting instances because it sets the instance flag to false
if (plrv.plr->iID == plrv.plr->iIDGroup && plrv.plr->groupCnt == 1)
PlayerManager::sendPlayerTo(sock, Warps[warpId].x, Warps[warpId].y, Warps[warpId].z, instanceID);
else {
Player* leaderPlr = PlayerManager::getPlayerFromID(plrv.plr->iIDGroup);
for (int i = 0; i < leaderPlr->groupCnt; i++) {
Player* otherPlr = PlayerManager::getPlayerFromID(leaderPlr->groupIDs[i]);
CNSocket* sockTo = PlayerManager::getSockFromID(leaderPlr->groupIDs[i]);
if (otherPlr == nullptr || sockTo == nullptr)
continue;
PlayerManager::sendPlayerTo(sockTo, Warps[warpId].x, Warps[warpId].y, Warps[warpId].z, instanceID);
}
}
}
else
{
INITSTRUCT(sP_FE2CL_REP_PC_WARP_USE_NPC_SUCC, resp); //Can only be used for exiting instances because it sets the instance flag to false
resp.iX = Warps[warpId].x;
resp.iY = Warps[warpId].y;
resp.iZ = Warps[warpId].z;

View File

@@ -22,7 +22,7 @@ std::set<int> LeechPowers = {24, 51, 89};
std::set<int> SleepPowers = {28, 30, 32, 49, 70, 71, 81, 85, 94};
// passive powers
std::set<int> ScavangePowers = {3, 50, 99};
std::set<int> ScavengePowers = {3, 50, 99};
std::set<int> RunPowers = {4, 68, 86};
std::set<int> GroupRunPowers = {8, 62, 73};
std::set<int> BonusPowers = {6, 54, 104};
@@ -33,9 +33,9 @@ std::set<int> FreedomPowers = {31, 39, 107};
std::set<int> GroupFreedomPowers = {15, 55, 77};
std::set<int> JumpPowers = {16, 44, 88};
std::set<int> GroupJumpPowers = {35, 60, 100};
std::set<int> SelfRevivePowers = {22, 48, 83};
std::set<int> SelfRevivePowers = {22, 48, 84};
std::set<int> SneakPowers = {29, 72, 80};
std::set<int> GroupSneakPowers = {23, 65, 84};
std::set<int> GroupSneakPowers = {23, 65, 83};
std::set<int> TreasureFinderPowers = {26, 40, 74};
/*
@@ -46,6 +46,7 @@ std::set<int> TreasureFinderPowers = {26, 40, 74};
}; // namespace
std::map<int32_t, NanoData> NanoManager::NanoTable;
std::map<int32_t, NanoTuning> NanoManager::NanoTunings;
void NanoManager::init() {
REGISTER_SHARD_PACKET(P_CL2FE_REQ_NANO_ACTIVE, nanoSummonHandler);
@@ -187,7 +188,7 @@ void NanoManager::nanoSkillSetHandler(CNSocket* sock, CNPacketData* data) {
return; // malformed packet
sP_CL2FE_REQ_NANO_TUNE* skill = (sP_CL2FE_REQ_NANO_TUNE*)data->buf;
setNanoSkill(sock, skill->iNanoID, skill->iTuneID);
setNanoSkill(sock, skill);
}
void NanoManager::nanoSkillSetGMHandler(CNSocket* sock, CNPacketData* data) {
@@ -195,7 +196,7 @@ void NanoManager::nanoSkillSetGMHandler(CNSocket* sock, CNPacketData* data) {
return; // malformed packet
sP_CL2FE_REQ_NANO_TUNE* skillGM = (sP_CL2FE_REQ_NANO_TUNE*)data->buf;
setNanoSkill(sock, skillGM->iNanoID, skillGM->iTuneID);
setNanoSkill(sock, skillGM);
}
void NanoManager::nanoRecallHandler(CNSocket* sock, CNPacketData* data) {
@@ -347,8 +348,8 @@ void NanoManager::summonNano(CNSocket *sock, int slot) {
plr->activeNano = nanoId;
}
void NanoManager::setNanoSkill(CNSocket* sock, int16_t nanoId, int16_t skillId) {
if (nanoId > 36)
void NanoManager::setNanoSkill(CNSocket* sock, sP_CL2FE_REQ_NANO_TUNE* skill) {
if (skill->iNanoID > 36)
return;
Player *plr = PlayerManager::getPlayer(sock);
@@ -356,25 +357,68 @@ void NanoManager::setNanoSkill(CNSocket* sock, int16_t nanoId, int16_t skillId)
if (plr == nullptr)
return;
if (plr->activeNano > 0 && plr->activeNano == nanoId)
if (plr->activeNano > 0 && plr->activeNano == skill->iNanoID)
summonNano(sock, -1); // just unsummon the nano to prevent infinite buffs
sNano nano = plr->Nanos[nanoId];
nano.iSkillID = skillId;
plr->Nanos[nanoId] = nano;
sNano nano = plr->Nanos[skill->iNanoID];
nano.iSkillID = skill->iTuneID;
plr->Nanos[skill->iNanoID] = nano;
// Send to client
INITSTRUCT(sP_FE2CL_REP_NANO_TUNE_SUCC, resp);
resp.iNanoID = nanoId;
resp.iSkillID = skillId;
resp.iNanoID = skill->iNanoID;
resp.iSkillID = skill->iTuneID;
resp.iPC_FusionMatter = plr->fusionmatter;
resp.aItem[9] = plr->Inven[0]; // temp fix for a bug TODO: Use this for nano power changing later
resp.aItem[9] = plr->Inven[0]; // quick fix to make sure item in slot 0 doesn't get yeeted by default
// check if there's any garbage in the item slot array (this'll happen when a nano station isn't used)
for (int i = 0; i < 10; i++) {
if (skill->aiNeedItemSlotNum[i] < 0 || skill->aiNeedItemSlotNum[i] >= AINVEN_COUNT) {
sock->sendPacket((void*)&resp, P_FE2CL_REP_NANO_TUNE_SUCC, sizeof(sP_FE2CL_REP_NANO_TUNE_SUCC));
return; // stop execution, don't run consumption logic
}
}
if (plr->fusionmatter < (int)MissionManager::AvatarGrowth[plr->level]["m_iReqBlob_NanoTune"]) // sanity check
return;
plr->fusionmatter -= (int)MissionManager::AvatarGrowth[plr->level]["m_iReqBlob_NanoTune"];
int reqItemCount = NanoTunings[skill->iTuneID].reqItemCount;
int reqItemID = NanoTunings[skill->iTuneID].reqItems;
int i = 0;
while (reqItemCount > 0 && i < 10) {
sItemBase& item = plr->Inven[skill->aiNeedItemSlotNum[i]];
if (item.iType == 7 && item.iID == reqItemID) {
if (item.iOpt > reqItemCount) {
item.iOpt -= reqItemCount;
reqItemCount = 0;
}
else {
reqItemCount -= item.iOpt;
item.iID = 0;
item.iType = 0;
item.iOpt = 0;
}
}
i++; // next slot
}
resp.iPC_FusionMatter = plr->fusionmatter; // update fusion matter in packet
// update items clientside
for (int i = 0; i < 10; i++) {
if (skill->aiNeedItemSlotNum[i]) { // non-zero check
resp.aItem[i] = plr->Inven[skill->aiNeedItemSlotNum[i]];
resp.aiItemSlotNum[i] = skill->aiNeedItemSlotNum[i];
}
}
sock->sendPacket((void*)&resp, P_FE2CL_REP_NANO_TUNE_SUCC, sizeof(sP_FE2CL_REP_NANO_TUNE_SUCC));
DEBUGLOG(
std::cout << U16toU8(plr->PCStyle.szFirstName) << U16toU8(plr->PCStyle.szLastName) << " set skill id " << skillId << " for nano: " << nanoId << std::endl;
std::cout << U16toU8(plr->PCStyle.szFirstName) << U16toU8(plr->PCStyle.szLastName) << " set skill id " << skill->iTuneID << " for nano: " << skill->iNanoID << std::endl;
)
}
@@ -407,14 +451,14 @@ bool doDebuff(CNSocket *sock, int32_t *pktdata, sSkillResult_Damage_N_Debuff *re
Mob* mob = MobManager::Mobs[pktdata[i]];
int damage = MobManager::hitMob(sock, mob, amount);
int damage = MobManager::hitMob(sock, mob, 0); // using amount for something else
respdata[i].eCT = 4;
respdata[i].iDamage = damage;
respdata[i].iID = mob->appearanceData.iNPC_ID;
respdata[i].iHP = mob->appearanceData.iHP;
respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag |= iCBFlag;
mob->unbuffTimes[iCBFlag] = getTime() + amount;
std::cout << (int)mob->appearanceData.iNPC_ID << " was debuffed" << std::endl;
return true;
@@ -428,10 +472,12 @@ bool doBuff(CNSocket *sock, int32_t *pktdata, sSkillResult_Buff *respdata, int i
}
Mob* mob = MobManager::Mobs[pktdata[i]];
MobManager::hitMob(sock, mob, 0);
respdata[i].eCT = 4;
respdata[i].iID = mob->appearanceData.iNPC_ID;
respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag |= iCBFlag;
mob->unbuffTimes[iCBFlag] = getTime() + amount;
std::cout << (int)mob->appearanceData.iNPC_ID << " was debuffed" << std::endl;
@@ -669,15 +715,15 @@ void activePower(CNSocket *sock, CNPacketData *data,
// active nano power dispatch table
std::vector<ActivePower> ActivePowers = {
ActivePower(StunPowers, activePower<sSkillResult_Damage_N_Debuff, doDebuff>, EST_STUN, CSB_BIT_STUN, 0),
ActivePower(HealPowers, activePower<sSkillResult_Heal_HP, doHeal>, EST_HEAL_HP, CSB_BIT_NONE, 25),
ActivePower(GroupHealPowers, activePower<sSkillResult_Heal_HP, doGroupHeal, GHEAL>,EST_HEAL_HP, CSB_BIT_NONE, 25),
ActivePower(StunPowers, activePower<sSkillResult_Damage_N_Debuff, doDebuff>, EST_STUN, CSB_BIT_STUN, 2250),
ActivePower(HealPowers, activePower<sSkillResult_Heal_HP, doHeal>, EST_HEAL_HP, CSB_BIT_NONE, 35),
ActivePower(GroupHealPowers, activePower<sSkillResult_Heal_HP, doGroupHeal, GHEAL>,EST_HEAL_HP, CSB_BIT_NONE, 20),
// TODO: Recall
ActivePower(DrainPowers, activePower<sSkillResult_Buff, doBuff>, EST_BOUNDINGBALL, CSB_BIT_BOUNDINGBALL, 0),
ActivePower(SnarePowers, activePower<sSkillResult_Damage_N_Debuff, doDebuff>, EST_SNARE, CSB_BIT_DN_MOVE_SPEED, 0),
ActivePower(DrainPowers, activePower<sSkillResult_Buff, doBuff>, EST_BOUNDINGBALL, CSB_BIT_BOUNDINGBALL, 3000),
ActivePower(SnarePowers, activePower<sSkillResult_Damage_N_Debuff, doDebuff>, EST_SNARE, CSB_BIT_DN_MOVE_SPEED, 4500),
ActivePower(DamagePowers, activePower<sSkillResult_Damage, doDamage>, EST_DAMAGE, CSB_BIT_NONE, 12),
ActivePower(LeechPowers, activePower<sSkillResult_Heal_HP, doLeech, LEECH>, EST_BLOODSUCKING, CSB_BIT_NONE, 18),
ActivePower(SleepPowers, activePower<sSkillResult_Damage_N_Debuff, doDebuff>, EST_SLEEP, CSB_BIT_MEZ, 0),
ActivePower(SleepPowers, activePower<sSkillResult_Damage_N_Debuff, doDebuff>, EST_SLEEP, CSB_BIT_MEZ, 4500),
};
}; // namespace
@@ -825,7 +871,7 @@ int NanoManager::nanoStyle(int nanoId) {
namespace NanoManager {
std::vector<PassivePower> PassivePowers = {
PassivePower(ScavangePowers, EST_REWARDBLOB, CSB_BIT_REWARD_BLOB, ECSB_REWARD_BLOB, 0, false),
PassivePower(ScavengePowers, EST_REWARDBLOB, CSB_BIT_REWARD_BLOB, ECSB_REWARD_BLOB, 0, false),
PassivePower(RunPowers, EST_RUN, CSB_BIT_UP_MOVE_SPEED, ECSB_UP_MOVE_SPEED, 200, false),
PassivePower(GroupRunPowers, EST_RUN, CSB_BIT_UP_MOVE_SPEED, ECSB_UP_MOVE_SPEED, 200, true),
PassivePower(BonusPowers, EST_REWARDCASH, CSB_BIT_REWARD_CASH, ECSB_REWARD_CASH, 0, false),

View File

@@ -39,10 +39,16 @@ struct NanoData {
int style;
};
struct NanoTuning {
int reqItemCount;
int reqItems;
};
namespace NanoManager {
extern std::vector<ActivePower> ActivePowers;
extern std::vector<PassivePower> PassivePowers;
extern std::map<int32_t, NanoData> NanoTable;
extern std::map<int32_t, NanoTuning> NanoTunings;
void init();
void nanoSummonHandler(CNSocket* sock, CNPacketData* data);
@@ -58,7 +64,7 @@ namespace NanoManager {
// Helper methods
void addNano(CNSocket* sock, int16_t nanoId, int16_t slot, bool spendfm=false);
void summonNano(CNSocket* sock, int slot);
void setNanoSkill(CNSocket* sock, int16_t nanoId, int16_t skillId);
void setNanoSkill(CNSocket* sock, sP_CL2FE_REQ_NANO_TUNE* skill);
void resetNanoSkill(CNSocket* sock, int16_t nanoId);
void nanoBuff(CNSocket* sock, int16_t nanoId, int skillId, int16_t eSkillType, int32_t iCBFlag, int16_t eCharStatusTimeBuffID, int16_t iValue = 0, bool groupPower = false);

View File

@@ -50,6 +50,7 @@ struct Player {
bool inCombat;
bool passiveNanoOut;
int healCooldown;
int pointDamage;
int groupDamage;

View File

@@ -222,15 +222,25 @@ void PlayerManager::updatePlayerChunk(CNSocket* sock, int X, int Y, uint64_t ins
// now, add all the new npcs & players!
addPlayerToChunks(ChunkManager::getDeltaChunks(allChunks, view.currentChunks), sock);
ChunkManager::addPlayer(X, Y, view.plr->instanceID, sock); // takes care of adding the player to the chunk if it exists or not
view.chunkPos = newPos;
view.currentChunks = allChunks;
ChunkManager::addPlayer(X, Y, view.plr->instanceID, sock); // takes care of adding the player to the chunk if it exists or not
}
void PlayerManager::sendPlayerTo(CNSocket* sock, int X, int Y, int Z, uint64_t I) {
PlayerView& plrv = PlayerManager::players[sock];
Player* plr = plrv.plr;
if (plrv.plr->instanceID == 0) {
// save last uninstanced coords
plrv.plr->lastX = plrv.plr->x;
plrv.plr->lastY = plrv.plr->y;
plrv.plr->lastZ = plrv.plr->z;
plrv.plr->lastAngle = plrv.plr->angle;
}
MissionManager::failInstancedMissions(sock); // fail any instanced missions
uint64_t fromInstance = plr->instanceID;
plr->instanceID = I;
@@ -242,7 +252,6 @@ void PlayerManager::sendPlayerTo(CNSocket* sock, int X, int Y, int Z, uint64_t I
sendPlayerTo(sock, X, Y, Z);
} else {
// annoying but necessary to set the flag back
MissionManager::failInstancedMissions(sock); // fail any instanced missions
INITSTRUCT(sP_FE2CL_REP_PC_WARP_USE_NPC_SUCC, resp);
resp.iX = X;
resp.iY = Y;
@@ -252,6 +261,7 @@ void PlayerManager::sendPlayerTo(CNSocket* sock, int X, int Y, int Z, uint64_t I
PlayerManager::removePlayerFromChunks(plrv.currentChunks, sock);
plrv.currentChunks.clear();
sock->sendPacket((void*)&resp, P_FE2CL_REP_PC_WARP_USE_NPC_SUCC, sizeof(sP_FE2CL_REP_PC_WARP_USE_NPC_SUCC));
updatePlayerPosition(sock, X, Y, Z);
}
ChunkManager::destroyInstanceIfEmpty(fromInstance);
@@ -702,7 +712,6 @@ void PlayerManager::gotoPlayer(CNSocket* sock, CNPacketData* data) {
std::cout << "\tZ: " << gotoData->iToZ << std::endl;
)
MissionManager::failInstancedMissions(sock); // this ensures warping by command still fails instanced missions
sendPlayerTo(sock, gotoData->iToX, gotoData->iToY, gotoData->iToZ, 0);
}
@@ -793,9 +802,7 @@ void PlayerManager::revivePlayer(CNSocket* sock, CNPacketData* data) {
NanoManager::nanoUnbuff(sock, CSB_BIT_PHOENIX, ECSB_PHOENIX, 0, false);
plr->HP = PC_MAXHEALTH(plr->level);
} else {
plr->x = target.x;
plr->y = target.y;
plr->z = target.z;
updatePlayerPosition(sock, target.x, target.y, target.z);
if (reviveData->iRegenType != 5)
plr->HP = PC_MAXHEALTH(plr->level);

View File

@@ -175,6 +175,18 @@ void TableData::init() {
}
std::cout << "[INFO] Loaded " << NanoManager::NanoTable.size() << " nanos" << std::endl;
nlohmann::json nanoTuneInfo = xdtData["m_pNanoTable"]["m_pNanoTuneData"];
for (nlohmann::json::iterator _nano = nanoTuneInfo.begin(); _nano != nanoTuneInfo.end(); _nano++) {
auto nano = _nano.value();
NanoTuning nanoData;
nanoData.reqItems = nano["m_iReqItemID"];
nanoData.reqItemCount = nano["m_iReqItemCount"];
NanoManager::NanoTunings[nano["m_iSkillID"]] = nanoData;
}
std::cout << "[INFO] Loaded " << NanoManager::NanoTable.size() << " nano tunings" << std::endl;
}
catch (const std::exception& err) {
std::cerr << "[FATAL] Malformed xdt.json file! Reason:" << err.what() << std::endl;
@@ -258,19 +270,22 @@ void TableData::loadPaths(int* nextId) {
std::cout << "[INFO] Loaded " << TransportManager::SkywayPaths.size() << " skyway paths" << std::endl;
// slider circuit
int sliders = 0;
int stops = 0;
int pos = 0;
nlohmann::json pathDataSlider = pathData["slider"];
for (nlohmann::json::iterator _sliderPoint = pathDataSlider.begin(); _sliderPoint != pathDataSlider.end(); _sliderPoint++) {
auto sliderPoint = _sliderPoint.value();
if (sliderPoint["stop"] && sliders % 2 == 0) { // check if this point in the circuit is a stop
if (sliderPoint["stop"]) { // check if this point in the circuit is a stop
// spawn a slider
BaseNPC* slider = new BaseNPC(sliderPoint["iX"], sliderPoint["iY"], sliderPoint["iZ"], 0, INSTANCE_OVERWORLD, 1, (*nextId)++, NPC_BUS);
NPCManager::NPCs[slider->appearanceData.iNPC_ID] = slider;
NPCManager::updateNPCPosition(slider->appearanceData.iNPC_ID, slider->appearanceData.iX, slider->appearanceData.iY, slider->appearanceData.iZ);
// set slider path to a rotation of the circuit
constructPathSlider(pathDataSlider, 0, slider->appearanceData.iNPC_ID);
sliders++;
constructPathSlider(pathDataSlider, pos, slider->appearanceData.iNPC_ID);
stops++;
}
pos++;
}
// npc paths
@@ -452,13 +467,14 @@ void TableData::constructPathSlider(nlohmann::json points, int rotations, int sl
for (int i = 0; i < stopTime + 1; i++) // repeat point if it's a stop
route.push(from); // add point A to the queue
WarpLocation to = { point["iX"] , point["iY"] , point["iZ"] }; // point B coords
// we may need to change this later; right now, the speed is cut before and after stops (no accel)
float curve = 1;
if (stopTime > 0) { // point A is a stop
curve = 2.0f;
curve = 0.375f;//2.0f;
} else if (point["stop"]) { // point B is a stop
curve = 0.35f;
curve = 0.375f;//0.35f;
}
TransportManager::lerp(&route, from, to, SLIDER_SPEED, curve); // lerp from A to B (arbitrary speed)
TransportManager::lerp(&route, from, to, SLIDER_SPEED * curve, 1); // lerp from A to B (arbitrary speed)
from = to; // update point A
stopTime = point["stop"] ? SLIDER_STOP_TICKS : 0;
}

View File

@@ -6,7 +6,7 @@
#include <unordered_map>
const int SLIDER_SPEED = 1200;
const int SLIDER_STOP_TICKS = 8;
const int SLIDER_STOP_TICKS = 16;
struct WarpLocation;

View File

@@ -236,8 +236,8 @@ namespace sqlite_orm {
template<size_t N, size_t I, class L, class R>
void move_tuple_impl(L &lhs, R &rhs) {
std::get<I>(lhs) = std::move(std::get<I>(rhs));
internal::static_if<std::integral_constant<bool, N != I + 1>{}>([](auto &lhs, auto &rhs) {
move_tuple_impl<N, I + 1>(lhs, rhs);
internal::static_if<std::integral_constant<bool, N != I + 1>{}>([](auto &l, auto &r) {
move_tuple_impl<N, I + 1>(l, r);
})(lhs, rhs);
}
}
@@ -247,8 +247,8 @@ namespace sqlite_orm {
template<size_t N, class L, class R>
void move_tuple(L &lhs, R &rhs) {
using bool_type = std::integral_constant<bool, N != 0>;
static_if<bool_type{}>([](auto &lhs, auto &rhs) {
tuple_helper::move_tuple_impl<N, 0>(lhs, rhs);
static_if<bool_type{}>([](auto &l, auto &r) {
tuple_helper::move_tuple_impl<N, 0>(l, r);
})(lhs, rhs);
}
@@ -577,8 +577,8 @@ namespace sqlite_orm {
const foreign_key_type &fk;
on_update_delete_t(decltype(fk) fk_, decltype(update) update, foreign_key_action action_) :
on_update_delete_base{update}, fk(fk_), _action(action_) {}
on_update_delete_t(decltype(fk) fk_, decltype(update) update_, foreign_key_action action_) :
on_update_delete_base{update_}, fk(fk_), _action(action_) {}
foreign_key_action _action = foreign_key_action::none;
@@ -691,8 +691,8 @@ namespace sqlite_orm {
foreign_key_intermediate_t(tuple_type columns_) : columns(std::move(columns_)) {}
template<class... Rs>
foreign_key_t<std::tuple<Cs...>, std::tuple<Rs...>> references(Rs... references) {
return {std::move(this->columns), std::make_tuple(std::forward<Rs>(references)...)};
foreign_key_t<std::tuple<Cs...>, std::tuple<Rs...>> references(Rs... refs) {
return {std::move(this->columns), std::make_tuple(std::forward<Rs>(refs)...)};
}
};
#endif
@@ -1544,12 +1544,12 @@ namespace sqlite_orm {
*/
constraints_type constraints;
column_t(std::string name,
column_t(std::string name_,
member_pointer_t member_pointer_,
getter_type getter_,
setter_type setter_,
constraints_type constraints_) :
column_base{std::move(name)},
column_base{std::move(name_)},
member_pointer(member_pointer_), getter(getter_), setter(setter_), constraints(move(constraints_)) {}
/**
@@ -1922,7 +1922,7 @@ namespace sqlite_orm {
T expr;
internal::collate_argument argument;
collate_t(T expr_, internal::collate_argument argument_) : expr(expr_), argument(argument_) {}
collate_t(T expr_, internal::collate_argument argument_) : expr(std::move(expr_)), argument(argument_) {}
operator std::string() const {
return constraints::collate_t{this->argument};
@@ -2205,7 +2205,7 @@ namespace sqlite_orm {
L l; // left expression
A arg; // in arg
in_t(L l_, A arg_, bool negative) : in_base{negative}, l(l_), arg(std::move(arg_)) {}
in_t(L l_, A arg_, bool negative_) : in_base{negative_}, l(l_), arg(std::move(arg_)) {}
};
struct is_null_string {
@@ -2344,8 +2344,8 @@ namespace sqlite_orm {
struct dynamic_order_by_entry_t : order_by_base {
std::string name;
dynamic_order_by_entry_t(decltype(name) name_, int asc_desc, std::string collate_argument) :
order_by_base{asc_desc, move(collate_argument)}, name(move(name_)) {}
dynamic_order_by_entry_t(decltype(name) name_, int asc_desc_, std::string collate_argument_) :
order_by_base{asc_desc_, move(collate_argument_)}, name(move(name_)) {}
};
/**
@@ -2463,8 +2463,8 @@ namespace sqlite_orm {
sqlite_orm::internal::optional_container<escape_t>
arg3; // not escape cause escape exists as a function here
like_t(arg_t arg_, pattern_t pattern_, sqlite_orm::internal::optional_container<escape_t> escape) :
arg(std::move(arg_)), pattern(std::move(pattern_)), arg3(std::move(escape)) {}
like_t(arg_t arg_, pattern_t pattern_, sqlite_orm::internal::optional_container<escape_t> escape_) :
arg(std::move(arg_)), pattern(std::move(pattern_)), arg3(std::move(escape_)) {}
template<class C>
like_t<A, T, C> escape(C c) const {
@@ -4332,8 +4332,8 @@ namespace sqlite_orm {
using left_type = typename compound_operator<L, R>::left_type;
using right_type = typename compound_operator<L, R>::right_type;
union_t(left_type l, right_type r, decltype(all) all) :
compound_operator<L, R>(std::move(l), std::move(r)), union_base{all} {}
union_t(left_type l, right_type r, decltype(all) all_) :
compound_operator<L, R>(std::move(l), std::move(r)), union_base{all_} {}
union_t(left_type l, right_type r) : union_t(std::move(l), std::move(r), false) {}
};
@@ -4979,14 +4979,14 @@ namespace sqlite_orm {
std::transform(str.begin(), str.end(), std::back_inserter(upper_str), [](char c) {
return static_cast<char>(std::toupper(static_cast<int>(c)));
});
static std::array<journal_mode, 6> all = {
static std::array<journal_mode, 6> all = {{
journal_mode::DELETE,
journal_mode::TRUNCATE,
journal_mode::PERSIST,
journal_mode::MEMORY,
journal_mode::WAL,
journal_mode::OFF,
};
}};
for(auto j: all) {
if(to_string(j) == upper_str) {
return std::make_unique<journal_mode>(j);
@@ -5400,8 +5400,8 @@ namespace sqlite_orm {
using columns_type = std::tuple<Cols...>;
using object_type = void;
index_t(std::string name, bool unique, columns_type columns_) :
index_base{move(name), unique}, columns(move(columns_)) {}
index_t(std::string name_, bool unique_, columns_type columns_) :
index_base{move(name_), unique_}, columns(move(columns_)) {}
columns_type columns;
};
@@ -5990,22 +5990,22 @@ namespace sqlite_orm {
// Make static_if have at least one input as a workaround for GCC bug:
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095
if(!res) {
static_if<std::is_same<C, member_pointer_t>{}>([&res, &obj, &col](const C &c) {
if(compare_any(col.member_pointer, c)) {
static_if<std::is_same<C, member_pointer_t>{}>([&res, &obj, &col](const C &c_) {
if(compare_any(col.member_pointer, c_)) {
res = &(obj.*col.member_pointer);
}
})(c);
}
if(!res) {
static_if<std::is_same<C, getter_type>{}>([&res, &obj, &col](const C &c) {
if(compare_any(col.getter, c)) {
static_if<std::is_same<C, getter_type>{}>([&res, &obj, &col](const C &c_) {
if(compare_any(col.getter, c_)) {
res = &((obj).*(col.getter))();
}
})(c);
}
if(!res) {
static_if<std::is_same<C, setter_type>{}>([&res, &obj, &col](const C &c) {
if(compare_any(col.setter, c)) {
static_if<std::is_same<C, setter_type>{}>([&res, &obj, &col](const C &c_) {
if(compare_any(col.setter, c_)) {
res = &((obj).*(col.getter))();
}
})(c);
@@ -6271,7 +6271,7 @@ namespace sqlite_orm {
struct storage_impl_base {
bool table_exists(const std::string &tableName, sqlite3 *db) const {
auto res = false;
auto result = false;
std::stringstream ss;
ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = '"
<< "table"
@@ -6287,13 +6287,13 @@ namespace sqlite_orm {
}
return 0;
},
&res,
&result,
nullptr);
if(rc != SQLITE_OK) {
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()),
sqlite3_errmsg(db));
}
return res;
return result;
}
void rename_table(sqlite3 *db, const std::string &oldName, const std::string &newName) const {
@@ -6355,7 +6355,7 @@ namespace sqlite_orm {
}
std::vector<table_info> get_table_info(const std::string &tableName, sqlite3 *db) const {
std::vector<table_info> res;
std::vector<table_info> result;
auto query = "PRAGMA table_info('" + tableName + "')";
auto rc = sqlite3_exec(
db,
@@ -6375,13 +6375,13 @@ namespace sqlite_orm {
}
return 0;
},
&res,
&result,
nullptr);
if(rc != SQLITE_OK) {
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()),
sqlite3_errmsg(db));
}
return res;
return result;
}
};
@@ -7184,8 +7184,8 @@ namespace sqlite_orm {
expression_type t;
prepared_statement_t(T t_, sqlite3_stmt *stmt, connection_ref con_) :
prepared_statement_base{stmt, std::move(con_)}, t(std::move(t_)) {}
prepared_statement_t(T t_, sqlite3_stmt *stmt_, connection_ref con_) :
prepared_statement_base{stmt_, std::move(con_)}, t(std::move(t_)) {}
};
template<class T>
@@ -7973,8 +7973,8 @@ namespace sqlite_orm {
template<class L>
void operator()(const node_type &c, const L &l) const {
c.case_expression.apply([&l](auto &c) {
iterate_ast(c, l);
c.case_expression.apply([&l](auto &c_) {
iterate_ast(c_, l);
});
iterate_tuple(c.args, [&l](auto &pair) {
iterate_ast(pair.first, l);
@@ -8082,6 +8082,16 @@ namespace sqlite_orm {
}
};
template<class T>
struct ast_iterator<collate_t<T>, void> {
using node_type = collate_t<T>;
template<class L>
void operator()(const node_type &node, const L &l) const {
iterate_ast(node.expr, l);
}
};
}
}
@@ -8189,6 +8199,14 @@ namespace sqlite_orm {
pragma_t(get_connection_t get_connection_) : get_connection(std::move(get_connection_)) {}
void busy_timeout(int value) {
this->set_pragma("busy_timeout", value);
}
int busy_timeout() {
return this->get_pragma<int>("busy_timeout");
}
sqlite_orm::journal_mode journal_mode() {
return this->get_pragma<sqlite_orm::journal_mode>("journal_mode");
}
@@ -8237,7 +8255,7 @@ namespace sqlite_orm {
T get_pragma(const std::string &name) {
auto connection = this->get_connection();
auto query = "PRAGMA " + name;
T res;
T result;
auto db = connection.get();
auto rc = sqlite3_exec(
db,
@@ -8249,10 +8267,10 @@ namespace sqlite_orm {
}
return 0;
},
&res,
&result,
nullptr);
if(rc == SQLITE_OK) {
return res;
return result;
} else {
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()),
sqlite3_errmsg(db));
@@ -8700,13 +8718,11 @@ namespace sqlite_orm {
bool transaction(const std::function<bool()> &f) {
this->begin_transaction();
auto con = this->get_connection();
auto db = con.get();
auto shouldCommit = f();
if(shouldCommit) {
this->commit(db);
this->commit();
} else {
this->rollback(db);
this->rollback();
}
return shouldCommit;
}
@@ -8744,10 +8760,10 @@ namespace sqlite_orm {
db,
sql.c_str(),
[](void *data, int argc, char **argv, char * * /*columnName*/) -> int {
auto &tableNames = *(data_t *)data;
auto &tableNames_ = *(data_t *)data;
for(int i = 0; i < argc; i++) {
if(argv[i]) {
tableNames.push_back(argv[i]);
tableNames_.push_back(argv[i]);
}
}
return 0;
@@ -8863,6 +8879,27 @@ namespace sqlite_orm {
return this->connection->filename;
}
/**
* Checks whether connection to database is opened right now.
* Returns always `true` for in memory databases.
*/
bool is_opened() const {
return this->connection->retain_count() > 0;
}
int busy_handler(std::function<int(int)> handler) {
_busy_handler = move(handler);
if(this->is_opened()) {
if(_busy_handler) {
return sqlite3_busy_handler(this->connection->get(), busy_handler_callback, this);
} else {
return sqlite3_busy_handler(this->connection->get(), nullptr, nullptr);
}
} else {
return SQLITE_OK;
}
}
protected:
storage_base(const std::string &filename_, int foreignKeysCount) :
pragma(std::bind(&storage_base::get_connection, this)),
@@ -8900,6 +8937,7 @@ namespace sqlite_orm {
std::unique_ptr<connection_holder> connection;
std::map<std::string, collating_function> collatingFunctions;
const int cachedForeignKeysCount;
std::function<int(int)> _busy_handler;
connection_ref get_connection() {
connection_ref res{*this->connection};
@@ -8924,7 +8962,7 @@ namespace sqlite_orm {
bool foreign_keys(sqlite3 *db) {
std::string query = "PRAGMA foreign_keys";
auto res = false;
auto result = false;
auto rc = sqlite3_exec(
db,
query.c_str(),
@@ -8935,13 +8973,13 @@ namespace sqlite_orm {
}
return 0;
},
&res,
&result,
nullptr);
if(rc != SQLITE_OK) {
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()),
sqlite3_errmsg(db));
}
return res;
return result;
}
#endif
@@ -8972,6 +9010,10 @@ namespace sqlite_orm {
sqlite3_limit(db, p.first, p.second);
}
if(_busy_handler) {
sqlite3_busy_handler(this->connection->get(), busy_handler_callback, this);
}
if(this->on_open) {
this->on_open(db);
}
@@ -9035,7 +9077,7 @@ namespace sqlite_orm {
}
std::string current_timestamp(sqlite3 *db) {
std::string res;
std::string result;
std::stringstream ss;
ss << "SELECT CURRENT_TIMESTAMP";
auto query = ss.str();
@@ -9051,13 +9093,13 @@ namespace sqlite_orm {
}
return 0;
},
&res,
&result,
nullptr);
if(rc != SQLITE_OK) {
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()),
sqlite3_errmsg(db));
}
return res;
return result;
}
void drop_table_internal(const std::string &tableName, sqlite3 *db) {
@@ -9087,6 +9129,15 @@ namespace sqlite_orm {
return f(leftLen, lhs, rightLen, rhs);
}
static int busy_handler_callback(void *selfPointer, int triesCount) {
auto &storage = *static_cast<storage_base *>(selfPointer);
if(storage._busy_handler) {
return storage._busy_handler(triesCount);
} else {
return 0;
}
}
// returns foreign keys count in storage definition
template<class T>
static int foreign_keys_count(T &storageImpl) {
@@ -9859,8 +9910,8 @@ namespace sqlite_orm {
std::string operator()(const statement_type &c, const C &context) const {
std::stringstream ss;
ss << "CASE ";
c.case_expression.apply([&ss, context](auto &c) {
ss << serialize(c, context) << " ";
c.case_expression.apply([&ss, context](auto &c_) {
ss << serialize(c_, context) << " ";
});
iterate_tuple(c.args, [&ss, context](auto &pair) {
ss << "WHEN " << serialize(pair.first, context) << " ";
@@ -10545,17 +10596,17 @@ namespace sqlite_orm {
}
ss << "VALUES ";
auto valuesString = [columnNamesCount] {
std::stringstream ss;
ss << "(";
std::stringstream ss_;
ss_ << "(";
for(size_t i = 0; i < columnNamesCount; ++i) {
ss << "?";
ss_ << "?";
if(i < columnNamesCount - 1) {
ss << ", ";
ss_ << ", ";
} else {
ss << ")";
ss_ << ")";
}
}
return ss.str();
return ss_.str();
}();
auto valuesCount = static_cast<int>(std::distance(rep.range.first, rep.range.second));
for(auto i = 0; i < valuesCount; ++i) {
@@ -10600,17 +10651,17 @@ namespace sqlite_orm {
}
ss << "VALUES ";
auto valuesString = [columnNamesCount] {
std::stringstream ss;
ss << "(";
std::stringstream ss_;
ss_ << "(";
for(size_t i = 0; i < columnNamesCount; ++i) {
ss << "?";
ss_ << "?";
if(i < columnNamesCount - 1) {
ss << ", ";
ss_ << ", ";
} else {
ss << ")";
ss_ << ")";
}
}
return ss.str();
return ss_.str();
}();
auto valuesCount = static_cast<int>(std::distance(statement.range.first, statement.range.second));
for(auto i = 0; i < valuesCount; ++i) {
@@ -11980,7 +12031,7 @@ namespace sqlite_orm {
}
template<class T, class... Args>
prepared_statement_t<get_all_t<T, Args...>> prepare(get_all_t<T, Args...> get) {
prepared_statement_t<get_all_t<T, Args...>> prepare(get_all_t<T, Args...> get_) {
auto con = this->get_connection();
sqlite3_stmt *stmt;
auto db = con.get();
@@ -11988,9 +12039,9 @@ namespace sqlite_orm {
context_t context{this->impl};
context.skip_table_name = false;
context.replace_bindable_with_question = true;
auto query = serialize(get, context);
auto query = serialize(get_, context);
if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
return {std::move(get), stmt, con};
return {std::move(get_), stmt, con};
} else {
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()),
sqlite3_errmsg(db));
@@ -11998,7 +12049,7 @@ namespace sqlite_orm {
}
template<class T, class... Args>
prepared_statement_t<get_all_pointer_t<T, Args...>> prepare(get_all_pointer_t<T, Args...> get) {
prepared_statement_t<get_all_pointer_t<T, Args...>> prepare(get_all_pointer_t<T, Args...> get_) {
auto con = this->get_connection();
sqlite3_stmt *stmt;
auto db = con.get();
@@ -12006,9 +12057,9 @@ namespace sqlite_orm {
context_t context{this->impl};
context.skip_table_name = false;
context.replace_bindable_with_question = true;
auto query = serialize(get, context);
auto query = serialize(get_, context);
if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
return {std::move(get), stmt, con};
return {std::move(get_), stmt, con};
} else {
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()),
sqlite3_errmsg(db));
@@ -12017,7 +12068,7 @@ namespace sqlite_orm {
#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED
template<class T, class R, class... Args>
prepared_statement_t<get_all_optional_t<T, R, Args...>> prepare(get_all_optional_t<T, R, Args...> get) {
prepared_statement_t<get_all_optional_t<T, R, Args...>> prepare(get_all_optional_t<T, R, Args...> get_) {
auto con = this->get_connection();
sqlite3_stmt *stmt;
auto db = con.get();
@@ -12025,9 +12076,9 @@ namespace sqlite_orm {
context_t context{this->impl};
context.skip_table_name = false;
context.replace_bindable_with_question = true;
auto query = serialize(get, context);
auto query = serialize(get_, context);
if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
return {std::move(get), stmt, con};
return {std::move(get_), stmt, con};
} else {
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()),
sqlite3_errmsg(db));
@@ -12073,7 +12124,7 @@ namespace sqlite_orm {
}
template<class T, class... Ids>
prepared_statement_t<get_t<T, Ids...>> prepare(get_t<T, Ids...> get) {
prepared_statement_t<get_t<T, Ids...>> prepare(get_t<T, Ids...> get_) {
auto con = this->get_connection();
sqlite3_stmt *stmt;
auto db = con.get();
@@ -12081,9 +12132,9 @@ namespace sqlite_orm {
context_t context{this->impl};
context.skip_table_name = false;
context.replace_bindable_with_question = true;
auto query = serialize(get, context);
auto query = serialize(get_, context);
if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
return {std::move(get), stmt, con};
return {std::move(get_), stmt, con};
} else {
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()),
sqlite3_errmsg(db));
@@ -12091,7 +12142,7 @@ namespace sqlite_orm {
}
template<class T, class... Ids>
prepared_statement_t<get_pointer_t<T, Ids...>> prepare(get_pointer_t<T, Ids...> get) {
prepared_statement_t<get_pointer_t<T, Ids...>> prepare(get_pointer_t<T, Ids...> get_) {
auto con = this->get_connection();
sqlite3_stmt *stmt;
auto db = con.get();
@@ -12099,9 +12150,9 @@ namespace sqlite_orm {
context_t context{this->impl};
context.skip_table_name = false;
context.replace_bindable_with_question = true;
auto query = serialize(get, context);
auto query = serialize(get_, context);
if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
return {std::move(get), stmt, con};
return {std::move(get_), stmt, con};
} else {
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()),
sqlite3_errmsg(db));
@@ -12110,7 +12161,7 @@ namespace sqlite_orm {
#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED
template<class T, class... Ids>
prepared_statement_t<get_optional_t<T, Ids...>> prepare(get_optional_t<T, Ids...> get) {
prepared_statement_t<get_optional_t<T, Ids...>> prepare(get_optional_t<T, Ids...> get_) {
auto con = this->get_connection();
sqlite3_stmt *stmt;
auto db = con.get();
@@ -12118,9 +12169,9 @@ namespace sqlite_orm {
context_t context{this->impl};
context.skip_table_name = false;
context.replace_bindable_with_question = true;
auto query = serialize(get, context);
auto query = serialize(get_, context);
if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
return {std::move(get), stmt, con};
return {std::move(get_), stmt, con};
} else {
throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()),
sqlite3_errmsg(db));
@@ -13315,8 +13366,8 @@ namespace sqlite_orm {
++index;
}
if(index == N) {
internal::static_if<std::is_same<result_tupe, node_type>{}>([](auto &result, auto &node) {
result = const_cast<typename std::remove_reference<decltype(result)>::type>(&node);
internal::static_if<std::is_same<result_tupe, node_type>{}>([](auto &r, auto &n) {
r = const_cast<typename std::remove_reference<decltype(r)>::type>(&n);
})(result, node);
}
});
@@ -13338,8 +13389,8 @@ namespace sqlite_orm {
++index;
}
if(index == N) {
internal::static_if<std::is_same<result_tupe, node_type>{}>([](auto &result, auto &node) {
result = const_cast<typename std::remove_reference<decltype(result)>::type>(&node);
internal::static_if<std::is_same<result_tupe, node_type>{}>([](auto &r, auto &n) {
r = const_cast<typename std::remove_reference<decltype(r)>::type>(&n);
})(result, node);
}
});

View File

@@ -12,7 +12,7 @@ int settings::DBSAVEINTERVAL = 240;
int settings::SHARDPORT = 8002;
std::string settings::SHARDSERVERIP = "127.0.0.1";
time_t settings::TIMEOUT = 60000;
int settings::VIEWDISTANCE = 40000;
int settings::VIEWDISTANCE = 25600;
bool settings::SIMULATEMOBS = true;
// default spawn point is Sector V (future)

2
tdata

Submodule tdata updated: aa4338202e...c77e75308d