mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-12-23 11:50:04 +00:00
implemented basic eggs functionality
This commit is contained in:
parent
de99522340
commit
674d5112f3
@ -7,6 +7,7 @@
|
||||
#include "NanoManager.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#define M_PI 3.14159265358979323846
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
#include <fstream>
|
||||
@ -21,6 +22,8 @@ std::map<int32_t, WarpLocation> NPCManager::Warps;
|
||||
std::vector<WarpLocation> NPCManager::RespawnPoints;
|
||||
/// sock, CBFlag -> until
|
||||
std::map<std::pair<CNSocket*, int32_t>, time_t> NPCManager::EggBuffs;
|
||||
std::unordered_map<int, EggType> NPCManager::EggTypes;
|
||||
std::unordered_map<int, Egg*> NPCManager::Eggs;
|
||||
nlohmann::json NPCManager::NPCData;
|
||||
|
||||
/*
|
||||
@ -43,8 +46,10 @@ void NPCManager::init() {
|
||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VENDOR_ITEM_RESTORE_BUY, npcVendorBuyback);
|
||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_VENDOR_BATTERY_BUY, npcVendorBuyBattery);
|
||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_ITEM_COMBINATION, npcCombineItems);
|
||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_SHINY_SUMMON, eggSummon);
|
||||
REGISTER_SHARD_PACKET(P_CL2FE_REQ_SHINY_PICKUP, eggPickup);
|
||||
|
||||
REGISTER_SHARD_TIMER(buffStep, 1000);
|
||||
REGISTER_SHARD_TIMER(eggStep, 1000);
|
||||
}
|
||||
|
||||
void NPCManager::removeNPC(std::vector<Chunk*> viewableChunks, int32_t id) {
|
||||
@ -63,6 +68,17 @@ void NPCManager::removeNPC(std::vector<Chunk*> viewableChunks, int32_t id) {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NPC_EGG:
|
||||
INITSTRUCT(sP_FE2CL_SHINY_EXIT, exitEggData);
|
||||
exitEggData.iShinyID = id;
|
||||
|
||||
for (Chunk* chunk : viewableChunks) {
|
||||
for (CNSocket* sock : chunk->players) {
|
||||
// send to socket
|
||||
sock->sendPacket((void*)&exitEggData, P_FE2CL_SHINY_EXIT, sizeof(sP_FE2CL_SHINY_EXIT));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// create struct
|
||||
INITSTRUCT(sP_FE2CL_NPC_EXIT, exitData);
|
||||
@ -94,6 +110,17 @@ void NPCManager::addNPC(std::vector<Chunk*> viewableChunks, int32_t id) {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NPC_EGG:
|
||||
INITSTRUCT(sP_FE2CL_SHINY_ENTER, enterEggData);
|
||||
npcDataToEggData(&npc->appearanceData, &enterEggData.ShinyAppearanceData);
|
||||
|
||||
for (Chunk* chunk : viewableChunks) {
|
||||
for (CNSocket* sock : chunk->players) {
|
||||
// send to socket
|
||||
sock->sendPacket((void*)&enterEggData, P_FE2CL_SHINY_ENTER, sizeof(sP_FE2CL_SHINY_ENTER));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// create struct
|
||||
INITSTRUCT(sP_FE2CL_NPC_ENTER, enterData);
|
||||
@ -135,6 +162,10 @@ void NPCManager::destroyNPC(int32_t id) {
|
||||
if (MobManager::Mobs.find(id) != MobManager::Mobs.end())
|
||||
MobManager::Mobs.erase(id);
|
||||
|
||||
// remove from eggs
|
||||
if (Eggs.find(id) != Eggs.end())
|
||||
Eggs.erase(id);
|
||||
|
||||
// finally, remove it from the map and free it
|
||||
NPCs.erase(id);
|
||||
delete entity;
|
||||
@ -654,7 +685,6 @@ BaseNPC* NPCManager::getNearestNPC(std::vector<Chunk*> chunks, int X, int Y, int
|
||||
return npc;
|
||||
}
|
||||
|
||||
|
||||
int NPCManager::eggBuffPlayer(CNSocket* sock, int skillId, int duration) {
|
||||
|
||||
Player* plr = PlayerManager::getPlayer(sock);
|
||||
@ -702,12 +732,12 @@ int NPCManager::eggBuffPlayer(CNSocket* sock, int skillId, int duration) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void NPCManager::buffStep(CNServer* serv, time_t currTime) {
|
||||
|
||||
void NPCManager::eggStep(CNServer* serv, time_t currTime) {
|
||||
// tick buffs
|
||||
auto it = EggBuffs.begin();
|
||||
while (it != EggBuffs.end()) {
|
||||
// check remaining time
|
||||
if (it->second > getTimestamp())
|
||||
if (it->second > currTime)
|
||||
it++;
|
||||
|
||||
// if time reached 0
|
||||
@ -747,4 +777,148 @@ void NPCManager::buffStep(CNServer* serv, time_t currTime) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// check dead eggs
|
||||
for (auto egg : Eggs) {
|
||||
if (!egg.second->dead)
|
||||
return;
|
||||
if (egg.second->deadUntil <= currTime) {
|
||||
// respawn it
|
||||
addNPC(egg.second->currentChunks, egg.first);
|
||||
egg.second->dead = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void NPCManager::npcDataToEggData(sNPCAppearanceData* npc, sShinyAppearanceData* egg) {
|
||||
egg->iX = npc->iX;
|
||||
egg->iY = npc->iY;
|
||||
egg->iZ = npc->iZ;
|
||||
// client doesn't care about egg->iMapNum
|
||||
egg->iShinyType = npc->iNPCType;
|
||||
egg->iShiny_ID = npc->iNPC_ID;
|
||||
}
|
||||
|
||||
void NPCManager::eggSummon(CNSocket* sock, CNPacketData* data) {
|
||||
if (data->size != sizeof(sP_CL2FE_REQ_SHINY_SUMMON))
|
||||
return; // malformed packet
|
||||
|
||||
sP_CL2FE_REQ_SHINY_SUMMON* summon = (sP_CL2FE_REQ_SHINY_SUMMON*)data->buf;
|
||||
|
||||
assert(NPCManager::nextId < INT32_MAX);
|
||||
int id = NPCManager::nextId++;
|
||||
|
||||
Player* plr = PlayerManager::getPlayer(sock);
|
||||
|
||||
if (plr == nullptr)
|
||||
return;
|
||||
|
||||
/*
|
||||
* the packet sends us player position with a random offset,
|
||||
* instead we're using some math to place the egg right in front of the player
|
||||
*/
|
||||
|
||||
int addX = -500.0f * sin(plr->angle / 180.0f * M_PI);
|
||||
int addY = -500.0f * cos(plr->angle / 180.0f * M_PI);
|
||||
|
||||
Egg* egg = new Egg (plr->x + addX, plr->y + addY, plr->z, plr->instanceID, summon->iShinyType, id, false);
|
||||
NPCManager::NPCs[id] = egg;
|
||||
NPCManager::Eggs[id] = egg;
|
||||
NPCManager::updateNPCPosition(id, plr->x + addX, plr->y + addY, plr->z, plr->instanceID);
|
||||
|
||||
}
|
||||
|
||||
void NPCManager::eggPickup(CNSocket* sock, CNPacketData* data) {
|
||||
if (data->size != sizeof(sP_CL2FE_REQ_SHINY_PICKUP))
|
||||
return; // malformed packet
|
||||
|
||||
sP_CL2FE_REQ_SHINY_PICKUP* pickup = (sP_CL2FE_REQ_SHINY_PICKUP*)data->buf;
|
||||
Player* plr = PlayerManager::getPlayer(sock);
|
||||
|
||||
if (plr == nullptr)
|
||||
return;
|
||||
|
||||
int eggId = pickup->iShinyID;
|
||||
|
||||
if (Eggs.find(eggId) == Eggs.end()) {
|
||||
std::cout << "[WARN] Player tried to open non existing egg?!" << std::endl;
|
||||
return;
|
||||
}
|
||||
Egg* egg = Eggs[eggId];
|
||||
|
||||
if (egg->dead) {
|
||||
std::cout << "[WARN] Player tried to open a dead egg?!" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
int typeId = egg->appearanceData.iNPCType;
|
||||
if (EggTypes.find(typeId) == EggTypes.end()) {
|
||||
if (egg->npcClass != NPCClass::NPC_EGG) {
|
||||
std::cout << "[WARN] Egg Type "<< typeId <<" not found!" << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
EggType* type = &EggTypes[typeId];
|
||||
|
||||
// response packet
|
||||
INITSTRUCT(sP_FE2CL_REP_SHINY_PICKUP_SUCC, resp);
|
||||
resp.iSkillID = type->effectId;
|
||||
if (resp.iSkillID == 0)
|
||||
resp.iSkillID = -1;
|
||||
|
||||
// this packet is only responsible for showing UI effect stuff
|
||||
sock->sendPacket((void*)&resp, P_FE2CL_REP_SHINY_PICKUP_SUCC, sizeof(sP_FE2CL_REP_SHINY_PICKUP_SUCC));
|
||||
|
||||
// buff
|
||||
if (type->effectId != 0)
|
||||
eggBuffPlayer(sock, type->effectId, type->duration);
|
||||
|
||||
// drop
|
||||
if (type->dropCrateId != 0) {
|
||||
const size_t resplen = sizeof(sP_FE2CL_REP_REWARD_ITEM) + sizeof(sItemReward);
|
||||
assert(resplen < CN_PACKET_BUFFER_SIZE - 8);
|
||||
// we know it's only one trailing struct, so we can skip full validation
|
||||
|
||||
uint8_t respbuf[resplen]; // not a variable length array, don't worry
|
||||
sP_FE2CL_REP_REWARD_ITEM* reward = (sP_FE2CL_REP_REWARD_ITEM*)respbuf;
|
||||
sItemReward* item = (sItemReward*)(respbuf + sizeof(sP_FE2CL_REP_REWARD_ITEM));
|
||||
|
||||
// don't forget to zero the buffer!
|
||||
memset(respbuf, 0, resplen);
|
||||
|
||||
// send back player's stats
|
||||
reward->m_iCandy = plr->money;
|
||||
reward->m_iFusionMatter = plr->fusionmatter;
|
||||
reward->m_iBatteryN = plr->batteryN;
|
||||
reward->m_iBatteryW = plr->batteryW;
|
||||
reward->iFatigue = 100; // prevents warning message
|
||||
reward->iFatigue_Level = 1;
|
||||
reward->iItemCnt = 1; // remember to update resplen if you change this
|
||||
|
||||
int slot = ItemManager::findFreeSlot(plr);
|
||||
|
||||
// no space for drop
|
||||
if (slot != -1) {
|
||||
|
||||
// item reward
|
||||
item->sItem.iType = 9;
|
||||
item->sItem.iOpt = 1;
|
||||
item->sItem.iID = type->dropCrateId;
|
||||
item->iSlotNum = slot;
|
||||
item->eIL = 1; // Inventory Location. 1 means player inventory.
|
||||
|
||||
// update player
|
||||
plr->Inven[slot] = item->sItem;
|
||||
sock->sendPacket((void*)respbuf, P_FE2CL_REP_REWARD_ITEM, resplen);
|
||||
}
|
||||
}
|
||||
|
||||
if (egg->summoned)
|
||||
destroyNPC(eggId);
|
||||
else {
|
||||
removeNPC(egg->currentChunks, eggId);
|
||||
egg->dead = true;
|
||||
egg->deadUntil = getTimestamp() + type->regen;
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "contrib/JSON.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#define RESURRECT_HEIGHT 400
|
||||
@ -16,11 +17,32 @@ struct WarpLocation {
|
||||
int x, y, z, instanceID, isInstance, limitTaskID, npcID;
|
||||
};
|
||||
|
||||
struct Egg : public BaseNPC {
|
||||
bool summoned;
|
||||
bool dead = false;
|
||||
time_t deadUntil;
|
||||
|
||||
Egg(int x, int y, int z, uint64_t iID, int type, int32_t id, bool summon)
|
||||
: BaseNPC(x, y, z, 0, iID, type, id) {
|
||||
summoned = summon;
|
||||
npcClass = NPCClass::NPC_EGG;
|
||||
}
|
||||
};
|
||||
|
||||
struct EggType {
|
||||
int dropCrateId;
|
||||
int effectId;
|
||||
int duration;
|
||||
int regen;
|
||||
};
|
||||
|
||||
namespace NPCManager {
|
||||
extern std::map<int32_t, BaseNPC*> NPCs;
|
||||
extern std::map<int32_t, WarpLocation> Warps;
|
||||
extern std::vector<WarpLocation> RespawnPoints;
|
||||
extern std::vector<WarpLocation> RespawnPoints;
|
||||
extern std::unordered_map<int, Egg*> Eggs;
|
||||
extern std::map<std::pair<CNSocket*, int32_t>, time_t> EggBuffs;
|
||||
extern std::unordered_map<int, EggType> EggTypes;
|
||||
extern nlohmann::json NPCData;
|
||||
extern int32_t nextId;
|
||||
void init();
|
||||
@ -54,5 +76,8 @@ namespace NPCManager {
|
||||
|
||||
/// returns -1 on fail
|
||||
int eggBuffPlayer(CNSocket* sock, int skillId, int duration);
|
||||
void buffStep(CNServer* serv, time_t currTime);
|
||||
void eggStep(CNServer* serv, time_t currTime);
|
||||
void npcDataToEggData(sNPCAppearanceData* npc, sShinyAppearanceData* egg);
|
||||
void eggSummon(CNSocket* sock, CNPacketData* data);
|
||||
void eggPickup(CNSocket* sock, CNPacketData* data);
|
||||
}
|
||||
|
@ -23,15 +23,15 @@ std::set<int> SleepPowers = {28, 30, 32, 49, 70, 71, 81, 85, 94};
|
||||
|
||||
// passive powers
|
||||
std::set<int> ScavengePowers = {3, 50, 99};
|
||||
std::set<int> RunPowers = {4, 68, 86};
|
||||
std::set<int> RunPowers = {4, 68, 86, 132};
|
||||
std::set<int> GroupRunPowers = {8, 62, 73};
|
||||
std::set<int> BonusPowers = {6, 54, 104};
|
||||
std::set<int> GuardPowers = {9, 57, 76};
|
||||
std::set<int> RadarPowers = {11, 67, 95};
|
||||
std::set<int> AntidotePowers = {14, 58, 102};
|
||||
std::set<int> FreedomPowers = {31, 39, 107};
|
||||
std::set<int> GuardPowers = {9, 57, 76, 157};
|
||||
std::set<int> RadarPowers = {11, 67, 95, 156};
|
||||
std::set<int> AntidotePowers = {14, 58, 102, 159};
|
||||
std::set<int> FreedomPowers = {31, 39, 107, 158};
|
||||
std::set<int> GroupFreedomPowers = {15, 55, 77};
|
||||
std::set<int> JumpPowers = {16, 44, 88};
|
||||
std::set<int> JumpPowers = {16, 44, 88, 154};
|
||||
std::set<int> GroupJumpPowers = {35, 60, 100};
|
||||
std::set<int> SelfRevivePowers = {22, 48, 84};
|
||||
std::set<int> SneakPowers = {29, 72, 80};
|
||||
|
@ -117,6 +117,11 @@ bool PlayerManager::removePlayerFromChunks(std::vector<Chunk*> chunks, CNSocket*
|
||||
exitBusData.iT_ID = id;
|
||||
sock->sendPacket((void*)&exitBusData, P_FE2CL_TRANSPORTATION_EXIT, sizeof(sP_FE2CL_TRANSPORTATION_EXIT));
|
||||
break;
|
||||
case NPC_EGG:
|
||||
INITSTRUCT(sP_FE2CL_SHINY_EXIT, exitEggData);
|
||||
exitEggData.iShinyID = id;
|
||||
sock->sendPacket((void*)&exitEggData, P_FE2CL_SHINY_EXIT, sizeof(sP_FE2CL_SHINY_EXIT));
|
||||
break;
|
||||
default:
|
||||
INITSTRUCT(sP_FE2CL_NPC_EXIT, exitData);
|
||||
exitData.iNPC_ID = id;
|
||||
@ -151,6 +156,11 @@ void PlayerManager::addPlayerToChunks(std::vector<Chunk*> chunks, CNSocket* sock
|
||||
enterBusData.AppearanceData = { 3, npc->appearanceData.iNPC_ID, npc->appearanceData.iNPCType, npc->appearanceData.iX, npc->appearanceData.iY, npc->appearanceData.iZ };
|
||||
sock->sendPacket((void*)&enterBusData, P_FE2CL_TRANSPORTATION_ENTER, sizeof(sP_FE2CL_TRANSPORTATION_ENTER));
|
||||
break;
|
||||
case NPC_EGG:
|
||||
INITSTRUCT(sP_FE2CL_SHINY_ENTER, enterEggData);
|
||||
NPCManager::npcDataToEggData(&npc->appearanceData, &enterEggData.ShinyAppearanceData);
|
||||
sock->sendPacket((void*)&enterEggData, P_FE2CL_SHINY_ENTER, sizeof(sP_FE2CL_SHINY_ENTER));
|
||||
break;
|
||||
default:
|
||||
INITSTRUCT(sP_FE2CL_NPC_ENTER, enterData);
|
||||
enterData.NPCAppearanceData = NPCManager::NPCs[id]->appearanceData;
|
||||
|
@ -223,6 +223,8 @@ void TableData::init() {
|
||||
|
||||
loadDrops();
|
||||
|
||||
loadEggs();
|
||||
|
||||
loadPaths(&nextId); // load paths
|
||||
|
||||
loadGruntwork(&nextId);
|
||||
@ -433,6 +435,35 @@ void TableData::loadDrops() {
|
||||
}
|
||||
}
|
||||
|
||||
void TableData::loadEggs() {
|
||||
try {
|
||||
std::ifstream inFile(settings::EGGSJSON);
|
||||
nlohmann::json eggData;
|
||||
|
||||
// read file into json
|
||||
inFile >> eggData;
|
||||
|
||||
// EggTypes
|
||||
nlohmann::json eggTypes = eggData["EggTypes"];
|
||||
for (nlohmann::json::iterator _eggType = eggTypes.begin(); _eggType != eggTypes.end(); _eggType++) {
|
||||
auto eggType = _eggType.value();
|
||||
EggType toAdd = {};
|
||||
toAdd.dropCrateId = (int)eggType["DropCrateId"];
|
||||
toAdd.effectId = (int)eggType["EffectId"];
|
||||
toAdd.duration = (int)eggType["Duration"];
|
||||
toAdd.regen= (int)eggType["Regen"];
|
||||
NPCManager::EggTypes[(int)eggType["Id"]] = toAdd;
|
||||
}
|
||||
|
||||
std::cout << "[INFO] Loaded Egg Data" <<std::endl;
|
||||
|
||||
}
|
||||
catch (const std::exception& err) {
|
||||
std::cerr << "[FATAL] Malformed eggs.json file! Reason:" << err.what() << std::endl;
|
||||
terminate(0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a full and properly-paced path by interpolating between keyframes.
|
||||
*/
|
||||
|
@ -18,6 +18,7 @@ namespace TableData {
|
||||
int getItemType(int);
|
||||
void loadPaths(int*);
|
||||
void loadDrops();
|
||||
void loadEggs();
|
||||
void constructPathSkyway(nlohmann::json::iterator);
|
||||
void constructPathSlider(nlohmann::json, int, int);
|
||||
void constructPathNPC(nlohmann::json::iterator, int id=0);
|
||||
|
@ -25,6 +25,7 @@ std::string settings::XDTJSON = "tdata/xdt.json";
|
||||
std::string settings::MOBJSON = "tdata/mobs.json";
|
||||
std::string settings::PATHJSON = "tdata/paths.json";
|
||||
std::string settings::DROPSJSON = "tdata/drops.json";
|
||||
std::string settings::EGGSJSON = "tdata/eggs.json";
|
||||
std::string settings::GRUNTWORKJSON = "tdata/gruntwork.json";
|
||||
std::string settings::MOTDSTRING = "Welcome to OpenFusion!";
|
||||
int settings::ACCLEVEL = 1;
|
||||
@ -62,6 +63,7 @@ void settings::init() {
|
||||
XDTJSON = reader.Get("shard", "xdtdata", XDTJSON);
|
||||
MOBJSON = reader.Get("shard", "mobdata", MOBJSON);
|
||||
DROPSJSON = reader.Get("shard", "dropdata", DROPSJSON);
|
||||
EGGSJSON = reader.Get("shard", "eggdata", EGGSJSON);
|
||||
PATHJSON = reader.Get("shard", "pathdata", PATHJSON);
|
||||
GRUNTWORKJSON = reader.Get("shard", "gruntwork", GRUNTWORKJSON);
|
||||
MOTDSTRING = reader.Get("shard", "motd", MOTDSTRING);
|
||||
|
@ -21,6 +21,7 @@ namespace settings {
|
||||
extern std::string MOBJSON;
|
||||
extern std::string PATHJSON;
|
||||
extern std::string DROPSJSON;
|
||||
extern std::string EGGSJSON;
|
||||
extern std::string GRUNTWORKJSON;
|
||||
extern int EVENTMODE;
|
||||
extern int EVENTCRATECHANCE;
|
||||
|
Loading…
Reference in New Issue
Block a user