#include "../Platform/stdafx.h" #include "ItemStat.h" #include "Achievement.h" #include "Achievements.h" #include "../Util/DamageSource.h" #include "../Player/Player.h" #include "../Items/ItemInstance.h" #include "../Blocks/Tile.h" #include "../Items/Item.h" #include "../Level/Level.h" #include "../../Minecraft.Client/Minecraft.h" #include "../Level/LevelData.h" #include "../Level/Storage/LevelSettings.h" #include "../../Minecraft.Client/Player/LocalPlayer.h" #include "../../Minecraft.Client/Player/MultiPlayerLocalPlayer.h" #include "../Level/Storage/EntityIO.h" #include "../../Minecraft.Client/Platform/Durango/ServiceConfig/Events-XBLA.8-149E11AEEvents.h" #include "DurangoStats.h" /////////////////// // Ds Item Event // /////////////////// std::string DsItemEvent::nameMethods[] = {"NONE", "itemPickedUp", "itemCrafted", "itemTakenFromChest", "itemTakenFromEnderchest", "itemBought", "itemSmithed", "blockMined", "blockPlaced", "MAX"}; DsItemEvent::DsItemEvent(int id, const std::wstring& name) : Stat(id, name) {} bool DsItemEvent::onLeaderboard(ELeaderboardId leaderboard, eAcquisitionMethod methodId, Param* param) { switch (methodId) { case eAcquisitionMethod_Pickedup: switch (param->itemId) { case Item::egg_Id: case Tile::mushroom_brown_Id: case Tile::mushroom_red_Id: return leaderboard == eLeaderboardId_FARMING; } break; case eAcquisitionMethod_Mined: switch (param->itemId) { case Tile::dirt_Id: case Tile::stoneBrick_Id: case Tile::sand_Id: case Tile::stone_Id: case Tile::gravel_Id: case Tile::clay_Id: case Tile::obsidian_Id: return leaderboard == eLeaderboardId_MINING; case Tile::wheat_Id: case Tile::pumpkin_Id: case Tile::reeds_Id: return leaderboard == eLeaderboardId_FARMING; } break; } return false; } // 4J-JEV, for tiles/items we want to record stats together. int DsItemEvent::mergeIds(int itemId) { switch (itemId) { default: return itemId; case Tile::mushroom_brown_Id: case Tile::mushroom_red_Id: return Tile::mushroom_brown_Id; case Tile::dirt_Id: case Tile::grass_Id: case Tile::farmland_Id: return Tile::dirt_Id; case Tile::redstoneLight_Id: case Tile::redstoneLight_lit_Id: return Tile::redstoneLight_Id; case Tile::redStoneOre_Id: case Tile::redStoneOre_lit_Id: return Tile::redStoneOre_Id; } } void DsItemEvent::handleParamBlob(std::shared_ptr player, byteArray paramBlob) { if (paramBlob.length == sizeof(Param)) { Param* param = (Param*)paramBlob.data; // Combine block ids. param->itemId = mergeIds(param->itemId); // Send farming leaderboard updates. if ((param->methodId == eAcquisitionMethod_Pickedup && onLeaderboard(eLeaderboardId_FARMING, eAcquisitionMethod_Pickedup, param)) || (param->methodId == eAcquisitionMethod_Mined && onLeaderboard(eLeaderboardId_FARMING, eAcquisitionMethod_Mined, param))) { EventWriteLeaderboardTotals( DurangoStats::getUserId(player), // UserId DurangoStats::getPlayerSession(), // PlayerSessionId player->level->difficulty, // Difficulty, eLeaderboardId_FARMING, // ScoreboardId param->itemCount); app.DebugPrintf("<%ls>\tscoreboardFarming(%i:%i:%i)\n", DurangoStats::getUserId(player), player->level->difficulty, eLeaderboardId_FARMING, param->itemCount); } // Send mining leaderboard updates. if (param->methodId == eAcquisitionMethod_Mined && onLeaderboard(eLeaderboardId_MINING, eAcquisitionMethod_Mined, param)) { EventWriteLeaderboardTotals( DurangoStats::getUserId(player), // UserId DurangoStats::getPlayerSession(), // PlayerSessionId player->level->difficulty, // Difficulty, eLeaderboardId_MINING, // ScoreboardId param->itemCount); app.DebugPrintf("<%ls>\tscoreboardMining(%i:%i:%i)\n", DurangoStats::getUserId(player), player->level->difficulty, eLeaderboardId_MINING, param->itemCount); } // Debug printout. std::string method = nameMethods[(int)param->methodId]; app.DebugPrintf("<%ls>\t%s(%i:%i:%i)\n", DurangoStats::getUserId(player), method.c_str(), param->itemId, param->itemAux, param->itemCount); // Split on acquisition method, then send relevant events. if (param->methodId == eAcquisitionMethod_Placed) { EventWriteBlockPlaced(DurangoStats::getUserId(player), DurangoStats::getPlayerSession(), player->level->difficulty, param->itemId, param->itemAux, param->itemCount); } else if (param->methodId == eAcquisitionMethod_Mined) { EventWriteBlockBroken(DurangoStats::getUserId(player), DurangoStats::getPlayerSession(), player->level->difficulty, param->itemId, param->itemAux, param->itemCount); } else { EventWriteMcItemAcquired(DurangoStats::getUserId(player), 0, // TODO DurangoStats::getPlayerSession(), 0, 0, player->level->difficulty, param->itemId, param->methodId, 0, 0, 0, // (x,y,z) param->itemAux, param->itemCount); } } } /* 4J-JEV: note that mined events will only fire with 'Instant_Mine' on if you * are carrying an appropriate tool for the block that you are mining. */ byteArray DsItemEvent::createParamBlob(eAcquisitionMethod eMethod, int itemId, int itemAux, int itemCount) { byteArray output; Param param = {eMethod, itemId, itemAux, itemCount}; output.data = (uint8_t*)new Param(param); output.length = sizeof(Param); return output; } /////////////////// // Ds Mob Killed // /////////////////// DsMobKilled::DsMobKilled(int id, const std::wstring& name) : Stat(id, name) {} void DsMobKilled::handleParamBlob(std::shared_ptr player, byteArray paramBlob) { if (paramBlob.length == sizeof(Param)) { Param* param = (Param*)paramBlob.data; if (param->mobType < 0) return; if (EventWriteMobKilled(DurangoStats::getUserId(player), 0, DurangoStats::getPlayerSession(), DurangoStats::getMultiplayerCorrelationId(), 0, player->level->difficulty, DurangoStats::getPlayerSession(), // ROUND ID 0, param->weaponId, param->mobType, param->isRanged ? 1 : 0, 0, 0, 0, // (x,y,z), 0, param->distance, param->mobType) == 0) { app.DebugPrintf( "<%ls>\t%s(%i:%i:%i:%i)\n", DurangoStats::getUserId(player), (param->isRanged ? "mobShotWithEntity" : "mobKilledInMelee"), param->mobType, param->weaponId, param->distance, param->damage); } switch (EntityIO::getClass(param->mobType)) { case eTYPE_MONSTER: if (param->mobType != SPIDER_JOCKEY_ID) break; // Fallthrough is spider jockey case eTYPE_ZOMBIE: case eTYPE_SKELETON: case eTYPE_CREEPER: case eTYPE_SPIDER: case eTYPE_PIGZOMBIE: case eTYPE_SLIME: if (EventWriteLeaderboardTotals( DurangoStats::getUserId(player), // UserId DurangoStats::getPlayerSession(), // PlayerSessionId player->level->difficulty, // Difficulty, eLeaderboardId_KILLING, // ScoreboardId 1) // Count == 0) { app.DebugPrintf("<%ls>\tscoreboardKills(%i:%i:1)\n", DurangoStats::getUserId(player), player->level->difficulty, eLeaderboardId_KILLING); } } } } byteArray DsMobKilled::createParamBlob(std::shared_ptr player, std::shared_ptr mob, DamageSource* dmgSrc) { // 4J-JEV: Get the id we use for Durango Server Stats. int mob_networking_id; eINSTANCEOF mobEType = mob->GetType(); if ((mobEType == eTYPE_SPIDER) && (mob->rider.lock() != NULL) && (mob->rider.lock()->GetType() == eTYPE_SKELETON) && mob->rider.lock()->isAlive()) { mob_networking_id = SPIDER_JOCKEY_ID; // Spider jockey only a concept for leaderboards. } else if ((mobEType == eTYPE_SKELETON) && (mob->riding != NULL) && (mob->riding->GetType() == eTYPE_SPIDER) && mob->riding->isAlive()) { mob_networking_id = SPIDER_JOCKEY_ID; // Spider jockey only a concept for leaderboards. } else { mob_networking_id = EntityIO::eTypeToIoid(mobEType); } // Kill made with projectile, arrow/ghast/fireball/snowball. // NB: Snowball kills would make an awesome achievement. ("Not a snowball's // chance...") if (dmgSrc->isProjectile()) { byteArray output; Param param = { DsMobKilled::RANGED, mob_networking_id, EntityIO::eTypeToIoid(dmgSrc->getDirectEntity()->GetType()), mob->distanceTo(player->x, player->y, player->z), 0 /*not needed*/ }; output.data = (uint8_t*)new Param(param); output.length = sizeof(Param); return output; } // Kill made in melee, use itemInHand as weapon. std::shared_ptr item = player->getCarriedItem(); byteArray output; Param param = { DsMobKilled::MELEE, mob_networking_id, (item != NULL ? item->getItem()->id : 0), mob->distanceTo(player->x, player->y, player->z), 0 /*not needed*/ }; output.data = (uint8_t*)new Param(param); output.length = sizeof(Param); return output; } ///////////////////// // Ds Mob Interact // ///////////////////// std::string DsMobInteract::nameInteract[] = {"unknownMobInteraction", "mobBred", "mobTamed", "mobCured", "mobCrafted", "mobSheared"}; DsMobInteract::DsMobInteract(int id, const std::wstring& name) : Stat(id, name) {} void DsMobInteract::handleParamBlob(std::shared_ptr player, byteArray paramBlob) { if (paramBlob.length == sizeof(Param)) { Param* param = (Param*)paramBlob.data; if (param->mobId < 0) return; app.DebugPrintf("<%ls>\t%s(%i)\n", DurangoStats::getUserId(player), nameInteract[param->interactionType].c_str(), param->mobId); EventWriteMobInteract(DurangoStats::getUserId(player), DurangoStats::getPlayerSession(), param->mobId, param->interactionType); } } byteArray DsMobInteract::createParamBlob(eInteract interactionId, int entityId) { byteArray output; Param param = {interactionId, EntityIO::eTypeToIoid((eINSTANCEOF)entityId)}; output.data = (uint8_t*)new Param(param); output.length = sizeof(Param); return output; } /////////////// // Ds Travel // /////////////// std::string DsTravel::nameMethods[eMethod_MAX] = { "Walk", "Swim", "Fall", "Climb", "Cart", "Boat", "Pig", "Time"}; unsigned int DsTravel::CACHE_SIZES[eMethod_MAX] = { 40, // WALK - Meters? 20, // SWIM - Meters? 0, // FALL - Meters? - Fall event naturally only sends on land, no caching // necessary. 10, // CLIMB - Meters? 70, // CART - Meters? 70, // BOAT - Meters? 10, // PIG - Meters? 20 * 60 * 5, // TIME - GameTicks (20*60*5 ~ 5 mins) }; DsTravel::DsTravel(int id, const std::wstring& name) : Stat(id, name) { ZeroMemory(¶m_cache, sizeof(unsigned int) * eMethod_MAX * MAX_LOCAL_PLAYERS); } void DsTravel::handleParamBlob(std::shared_ptr player, byteArray paramBlob) { if (paramBlob.length == sizeof(Param)) { Param* param = (Param*)paramBlob.data; int newDistance = cache(player->GetXboxPad(), *param); if (newDistance > 0) write(player, param->method, newDistance); } } byteArray DsTravel::createParamBlob(eMethod method, int distance) { byteArray output; Param param = {method, distance}; output.data = (uint8_t*)new Param(param); output.length = sizeof(Param); return output; } int DsTravel::cache(int iPad, Param& param) { if ((eMethod_walk <= param.method) && (param.method < eMethod_MAX)) { param_cache[iPad][param.method] += param.distance; if (param_cache[iPad][param.method] > CACHE_SIZES[param.method]) { int out = param_cache[iPad][param.method]; param_cache[iPad][param.method] = 0; return out; } } return 0; } void DsTravel::flush(std::shared_ptr player) { int iPad = player->GetXboxPad(); for (int i = 0; i < eMethod_MAX; i++) { if (param_cache[iPad][i] > 0) { write(player, (eMethod)i, param_cache[iPad][i]); param_cache[iPad][i] = 0; } } } void DsTravel::write(std::shared_ptr player, eMethod method, int distance) { if (player == nullptr) return; app.DebugPrintf("<%ls>\t%s(%i)\n", DurangoStats::getUserId(player), nameMethods[method].c_str(), distance); if (method == DsTravel::eMethod_time) { EventWriteIncTimePlayed(DurangoStats::getUserId(player), DurangoStats::getPlayerSession(), player->level->difficulty, distance); } else if ((eMethod_walk <= method) && (method < eMethod_MAX)) { EventWriteIncDistanceTravelled( DurangoStats::getUserId(player), DurangoStats::getPlayerSession(), player->level->difficulty, distance, method); switch (method) { case eMethod_walk: case eMethod_fall: case eMethod_minecart: case eMethod_boat: EventWriteLeaderboardTotals( DurangoStats::getUserId(player), // UserId DurangoStats::getPlayerSession(), // PlayerSessionId player->level->difficulty, // Difficulty, eLeaderboardId_TRAVELLING, // ScoreboardId distance); break; } } } ////////////////// // Ds Item Used // ////////////////// DsItemUsed::DsItemUsed(int id, const std::wstring& name) : Stat(id, name) {} void DsItemUsed::handleParamBlob(std::shared_ptr player, byteArray paramBlob) { if (paramBlob.length == sizeof(Param)) { Param* param = (Param*)paramBlob.data; app.DebugPrintf("<%ls>\titemUsed(%i,%i,%i)\n", DurangoStats::getUserId(player), param->itemId, param->aux, param->count); EventWriteMcItemUsed(DurangoStats::getUserId(player), 0, // SectionId, DurangoStats::getPlayerSession(), 0, // MultiplayerCorrelationId, 0, // Gameplay Mode, player->level->difficulty, param->itemId, 0, 0, 0, // (x,y,z) param->aux, param->count, param->hunger); } } byteArray DsItemUsed::createParamBlob(int itemId, int aux, int count, int health, int hunger) { byteArray output; Param param = {itemId, aux, count, health, hunger}; output.data = (uint8_t*)new Param(param); output.length = sizeof(Param); return output; } //////////////////// // Ds Achievement // //////////////////// DsAchievement::DsAchievement(int id, const std::wstring& name) : Stat(id, name) {} void DsAchievement::handleParamBlob(std::shared_ptr player, byteArray paramBlob) { if (paramBlob.length == sizeof(SmallParam)) { SmallParam* paramS = (SmallParam*)paramBlob.data; assert(DurangoStats::binaryAchievement(paramS->award)); app.DebugPrintf("<%ls>\tAchievement(%i)\n", DurangoStats::getUserId(player), paramS->award); bool canAward = true; if (paramS->award == eAward_stayinFrosty) { canAward = !player->m_bHasAwardedStayinFrosty; player->m_bHasAwardedStayinFrosty = true; } if (canAward) { EventWriteAchievementGet(DurangoStats::getUserId(player), DurangoStats::getPlayerSession(), paramS->award); } } else if (paramBlob.length == sizeof(LargeParam)) { LargeParam* paramL = (LargeParam*)paramBlob.data; assert(DurangoStats::enhancedAchievement(paramL->award)); switch (paramL->award) { case eAward_musicToMyEars: app.DebugPrintf("<%ls>\tmusicToMyEars(%i)\n", DurangoStats::getUserId(player), paramL->count); EventWritePlayedMusicDisc(DurangoStats::getUserId(player), DurangoStats::getPlayerSession(), paramL->count); break; case eAward_chestfulOfCobblestone: app.DebugPrintf("<%ls>\tchestfulOfCobblestone(%i)\n", DurangoStats::getUserId(player), paramL->count); EventWriteChestfulOfCobblestone( DurangoStats::getUserId(player), DurangoStats::getPlayerSession(), paramL->count); break; case eAward_overkill: app.DebugPrintf("<%ls>\toverkill(%i)\n", DurangoStats::getUserId(player), paramL->count); EventWriteOverkill(DurangoStats::getUserId(player), DurangoStats::getPlayerSession(), paramL->count); break; case eAward_OnARail: app.DebugPrintf("<%ls>\nonARail(%i)\n", DurangoStats::getUserId(player), paramL->count); EventWriteOnARail(DurangoStats::getUserId(player), DurangoStats::getPlayerSession(), paramL->count); break; } } else assert(false); // Unsuitable paramBlob length. } byteArray DsAchievement::createSmallParamBlob(eAward award) { byteArray output; SmallParam param = {award}; output.data = (uint8_t*)new SmallParam(param); output.length = sizeof(SmallParam); return output; } byteArray DsAchievement::createLargeParamBlob(eAward award, int count) { byteArray output; LargeParam param = {award, count}; output.data = (uint8_t*)new LargeParam(param); output.length = sizeof(LargeParam); return output; } ////////////////////////// // Ds Changed Dimension // ////////////////////////// DsChangedDimension::DsChangedDimension(int id, const std::wstring& name) : Stat(id, name) {} void DsChangedDimension::handleParamBlob(std::shared_ptr player, byteArray paramBlob) { if (paramBlob.length == sizeof(Param)) { Param* param = (Param*)paramBlob.data; app.DebugPrintf("<%ls>\tchangedDimension(%i:%i)\n", DurangoStats::getUserId(player), param->fromDimId, param->toDimId); // No longer used. } } byteArray DsChangedDimension::createParamBlob(int fromDimId, int toDimId) { byteArray output; Param param = {fromDimId, toDimId}; output.data = (uint8_t*)new Param(param); output.length = sizeof(Param); return output; } ////////////////////// // Ds Entered Biome // ////////////////////// DsEnteredBiome::DsEnteredBiome(int id, const std::wstring& name) : Stat(id, name) {} void DsEnteredBiome::handleParamBlob(std::shared_ptr player, byteArray paramBlob) { if (paramBlob.length == sizeof(Param)) { Param* param = (Param*)paramBlob.data; app.DebugPrintf("<%ls>\tenteredBiome(%i)\n", DurangoStats::getUserId(player), param->biomeId); EventWriteEnteredNewBiome(DurangoStats::getUserId(player), DurangoStats::getPlayerSession(), param->biomeId); } } byteArray DsEnteredBiome::createParamBlob(int biomeId) { byteArray output; Param param = {biomeId}; output.data = (uint8_t*)new Param(param); output.length = sizeof(Param); return output; } //////////////////////// // DURANGO STATISTICS // //////////////////////// DurangoStats::DurangoStats() { // Hopefully only using the first parameter itemsAcquired = new DsItemEvent(itemsAcquired_Id, L"itemsAcquired"); itemsAcquired->postConstruct(); itemUsed = new DsItemUsed(itemUsed_Id, L"itemUsed"); itemUsed->postConstruct(); travel = new DsTravel(travel_Id, L"travel"); travel->setAwardLocallyOnly()->postConstruct(); mobKilled = new DsMobKilled(mobKilled_Id, L"mobKilled"); mobKilled->postConstruct(); mobInteract = new DsMobInteract(mobInteract_Id, L"mobInteract"); mobInteract->postConstruct(); changedDimension = new DsChangedDimension(changedDimension_Id, L"changedDimension"); changedDimension->postConstruct(); enteredBiome = new DsEnteredBiome(enteredBiome_Id, L"enteredBiome"); enteredBiome->postConstruct(); achievement = new DsAchievement(binAchievement_Id, L"achievement"); achievement->postConstruct(); achievementLocal = new DsAchievement(binAchievementLocal_Id, L"achievementLocal"); achievementLocal->setAwardLocallyOnly(); achievementLocal->postConstruct(); EventRegisterXBLA_149E11AE(); } DurangoStats::~DurangoStats() { EventUnregisterXBLA_149E11AE(); } Stat* DurangoStats::get_stat(int i) { switch (i) { case itemsAcquired_Id: return (Stat*)itemsAcquired; case itemUsed_Id: return (Stat*)itemUsed; case travel_Id: return (Stat*)travel; case mobKilled_Id: return (Stat*)mobKilled; case mobInteract_Id: return (Stat*)mobInteract; case changedDimension_Id: return (Stat*)changedDimension; case enteredBiome_Id: return (Stat*)enteredBiome; case binAchievement_Id: return (Stat*)achievement; case binAchievementLocal_Id: return (Stat*)achievementLocal; // Unrecognised stat id default: assert(false); break; } return NULL; } Stat* DurangoStats::get_walkOneM() { return travel; } Stat* DurangoStats::get_swimOneM() { return travel; } Stat* DurangoStats::get_fallOneM() { return travel; } Stat* DurangoStats::get_climbOneM() { return travel; } Stat* DurangoStats::get_minecartOneM() { return travel; } Stat* DurangoStats::get_boatOneM() { return travel; } Stat* DurangoStats::get_pigOneM() { return travel; } Stat* DurangoStats::get_cowsMilked() { return get_itemsCrafted(Item::bucket_milk_Id); } Stat* DurangoStats::get_killMob() { return mobKilled; } Stat* DurangoStats::get_breedEntity(eINSTANCEOF entityId) { return mobInteract; } Stat* DurangoStats::get_tamedEntity(eINSTANCEOF entityId) { return mobInteract; } Stat* DurangoStats::get_curedEntity(eINSTANCEOF entityId) { return mobInteract; } Stat* DurangoStats::get_craftedEntity(eINSTANCEOF entityId) { return mobInteract; } Stat* DurangoStats::get_shearedEntity(eINSTANCEOF entityId) { return mobInteract; } Stat* DurangoStats::get_timePlayed() { return travel; } Stat* DurangoStats::get_blocksPlaced(int blockId) { return (Stat*)itemsAcquired; } Stat* DurangoStats::get_blocksMined(int blockId) { return (Stat*)itemsAcquired; } Stat* DurangoStats::get_itemsCollected(int itemId, int itemAux) { return (Stat*)itemsAcquired; } Stat* DurangoStats::get_itemsCrafted(int itemId) { switch (itemId) { // 4J-JEV: These items can be crafted trivially to and from their // block equivalents, // 'Acquire Hardware' also relies on 'Count_Crafted(IronIngot) == // Count_Forged(IronIngot)" on the Stats server. case Item::ironIngot_Id: case Item::goldIngot_Id: case Item::diamond_Id: case Item::redStone_Id: case Item::emerald_Id: return NULL; case Item::dye_powder_Id: default: return (Stat*)itemsAcquired; } } Stat* DurangoStats::get_itemsSmelted(int itemId) { // 4J-JEV: Context needed for itemCrafted for iron/gold being smelted. return (Stat*)itemsAcquired; } Stat* DurangoStats::get_itemsUsed(int itemId) { return (Stat*)itemUsed; } Stat* DurangoStats::get_itemsBought(int itemId) { return (Stat*)itemsAcquired; } Stat* DurangoStats::get_changedDimension(int from, int to) { return (Stat*)changedDimension; } Stat* DurangoStats::get_enteredBiome(int biomeId) { return (Stat*)enteredBiome; } Stat* DurangoStats::get_achievement(eAward achievementId) { // Special case for 'binary' achievements. if (binaryAchievement(achievementId) || enhancedAchievement(achievementId)) { switch (achievementId) { case eAward_chestfulOfCobblestone: case eAward_TakingInventory: return achievementLocal; default: return achievement; } } else if (achievementId == eAward_zombieDoctor) { return get_curedEntity(eTYPE_ZOMBIE); } // Other achievements awarded through more detailed generic events. return NULL; } byteArray DurangoStats::getParam_walkOneM(int distance) { return DsTravel::createParamBlob(DsTravel::eMethod_walk, distance); } byteArray DurangoStats::getParam_swimOneM(int distance) { return DsTravel::createParamBlob(DsTravel::eMethod_swim, distance); } byteArray DurangoStats::getParam_fallOneM(int distance) { return DsTravel::createParamBlob(DsTravel::eMethod_fall, distance); } byteArray DurangoStats::getParam_climbOneM(int distance) { return DsTravel::createParamBlob(DsTravel::eMethod_climb, distance); } byteArray DurangoStats::getParam_minecartOneM(int distance) { return DsTravel::createParamBlob(DsTravel::eMethod_minecart, distance); } byteArray DurangoStats::getParam_boatOneM(int distance) { return DsTravel::createParamBlob(DsTravel::eMethod_boat, distance); } byteArray DurangoStats::getParam_pigOneM(int distance) { return DsTravel::createParamBlob(DsTravel::eMethod_pig, distance); } byteArray DurangoStats::getParam_cowsMilked() { return DsItemEvent::createParamBlob(DsItemEvent::eAcquisitionMethod_Crafted, Item::bucket_milk_Id, 0, 1); } byteArray DurangoStats::getParam_blocksPlaced(int blockId, int data, int count) { return DsItemEvent::createParamBlob(DsItemEvent::eAcquisitionMethod_Placed, blockId, data, count); } byteArray DurangoStats::getParam_blocksMined(int blockId, int data, int count) { return DsItemEvent::createParamBlob(DsItemEvent::eAcquisitionMethod_Mined, blockId, data, count); } byteArray DurangoStats::getParam_itemsCollected(int id, int aux, int count) { return DsItemEvent::createParamBlob( DsItemEvent::eAcquisitionMethod_Pickedup, id, aux, count); } byteArray DurangoStats::getParam_itemsCrafted(int id, int aux, int count) { return DsItemEvent::createParamBlob(DsItemEvent::eAcquisitionMethod_Crafted, id, aux, count); } byteArray DurangoStats::getParam_itemsUsed(std::shared_ptr player, std::shared_ptr itm) { return DsItemUsed::createParamBlob(itm->getItem()->id, itm->getAuxValue(), itm->GetCount(), player->getHealth(), player->getFoodData()->getFoodLevel()); } byteArray DurangoStats::getParam_itemsBought(int id, int aux, int count) { return DsItemEvent::createParamBlob(DsItemEvent::eAcquisitionMethod_Bought, id, aux, count); } byteArray DurangoStats::getParam_mobKill(std::shared_ptr player, std::shared_ptr mob, DamageSource* dmgSrc) { return DsMobKilled::createParamBlob(player, mob, dmgSrc); } byteArray DurangoStats::getParam_breedEntity(eINSTANCEOF entityId) { return DsMobInteract::createParamBlob(DsMobInteract::eInteract_Breed, entityId); } byteArray DurangoStats::getParam_tamedEntity(eINSTANCEOF entityId) { return DsMobInteract::createParamBlob(DsMobInteract::eInteract_Tamed, entityId); } byteArray DurangoStats::getParam_curedEntity(eINSTANCEOF entityId) { return DsMobInteract::createParamBlob(DsMobInteract::eInteract_Cured, entityId); } byteArray DurangoStats::getParam_craftedEntity(eINSTANCEOF entityId) { return DsMobInteract::createParamBlob(DsMobInteract::eInteract_Crafted, entityId); } byteArray DurangoStats::getParam_shearedEntity(eINSTANCEOF entityId) { return DsMobInteract::createParamBlob(DsMobInteract::eInteract_Sheared, entityId); } byteArray DurangoStats::getParam_time(int timediff) { return DsTravel::createParamBlob(DsTravel::eMethod_time, timediff); } byteArray DurangoStats::getParam_changedDimension(int from, int to) { return DsChangedDimension::createParamBlob(from, to); } byteArray DurangoStats::getParam_enteredBiome(int biomeId) { return DsEnteredBiome::createParamBlob(biomeId); } byteArray DurangoStats::getParam_achievement(eAward id) { if (binaryAchievement(id)) { return DsAchievement::createSmallParamBlob(id); } else if (enhancedAchievement(id)) { assert(false); // Should be calling the appropriate getParam function. } else if (id == eAward_zombieDoctor) { return getParam_curedEntity(eTYPE_ZOMBIE); } // If its not a binary achievement, // don't bother constructing the param blob. return getParam_noArgs(); } byteArray DurangoStats::getParam_onARail(int dist) { return DsAchievement::createLargeParamBlob(eAward_OnARail, dist); } byteArray DurangoStats::getParam_chestfulOfCobblestone(int count) { return DsAchievement::createLargeParamBlob(eAward_chestfulOfCobblestone, count); } byteArray DurangoStats::getParam_overkill(int dmg) { return DsAchievement::createLargeParamBlob(eAward_overkill, dmg); } byteArray DurangoStats::getParam_musicToMyEars(int recordId) { return DsAchievement::createLargeParamBlob(eAward_musicToMyEars, recordId); } bool DurangoStats::binaryAchievement(eAward achievementId) { switch (achievementId) { case eAward_InToTheNether: case eAward_theEnd: case eAward_WhenPigsFly: case eAward_diamondsToYou: case eAward_stayinFrosty: case eAward_renewableEnergy: case eAward_ironMan: case eAward_winGame: case /*maybe*/ eAward_TakingInventory: return true; default: return false; } } /** 4J-JEV, Basically achievements with an inconsequential extra parameter that I thought best not to / prefered not to / couldn't be bothered to make class handlers for. (Motivation: it would be nice for players to see how close they were/are to achieving these things). */ bool DurangoStats::enhancedAchievement(eAward achievementId) { switch (achievementId) { // case eAward_TakingInventory: case eAward_musicToMyEars: case eAward_chestfulOfCobblestone: case eAward_overkill: case eAward_OnARail: return true; default: return false; } } void DurangoStats::generatePlayerSession() { DurangoStats* dsInstance = (DurangoStats*)GenericStats::getInstance(); CoCreateGuid(&dsInstance->playerSessionId); } LPCGUID DurangoStats::getPlayerSession() { DurangoStats* dsInstance = (DurangoStats*)GenericStats::getInstance(); LPCGUID lpcguid = &dsInstance->playerSessionId; return lpcguid; } void DurangoStats::setMultiplayerCorrelationId(Platform::String ^ mcpId) { ((DurangoStats*)GenericStats::getInstance())->multiplayerCorrelationId = mcpId; } LPCWSTR DurangoStats::getMultiplayerCorrelationId() { return ((DurangoStats*)GenericStats::getInstance()) ->multiplayerCorrelationId->Data(); } LPCWSTR DurangoStats::getUserId(std::shared_ptr player) { return getUserId(player->GetXboxPad()); } LPCWSTR DurangoStats::getUserId(int iPad) { static std::wstring cache = L""; PlayerUID uid = INVALID_XUID; ProfileManager.GetXUID(iPad, &uid, true); cache = uid.toString(); return cache.c_str(); } void DurangoStats::playerSessionStart(PlayerUID uid, std::shared_ptr plr) { if (plr != NULL && plr->level != NULL && plr->level->getLevelData() != NULL) { // wprintf(uid.toString().c_str()); // EventWritePlayerSessionStart( app.DebugPrintf(">>>\tPlayerSessionStart(%ls,%s,%ls,%i,%i)\n", uid.toString(), DurangoStats::getPlayerSession(), DurangoStats::getMultiplayerCorrelationId(), plr->level->getLevelData()->getGameType()->isSurvival(), plr->level->difficulty); EventWritePlayerSessionStart( uid.toString().c_str(), DurangoStats::getPlayerSession(), DurangoStats::getMultiplayerCorrelationId(), plr->level->getLevelData()->getGameType()->isSurvival(), plr->level->difficulty); } } void DurangoStats::playerSessionStart(int iPad) { PlayerUID puid; std::shared_ptr plr; ProfileManager.GetXUID(iPad, &puid, true); plr = Minecraft::GetInstance()->localplayers[iPad]; playerSessionStart(puid, plr); } void DurangoStats::playerSessionPause(int iPad) { std::shared_ptr plr = Minecraft::GetInstance()->localplayers[iPad]; if (plr != NULL && plr->level != NULL && plr->level->getLevelData() != NULL) { PlayerUID puid; ProfileManager.GetXUID(iPad, &puid, true); // EventWritePlayerSessionPause( app.DebugPrintf(">>>\tPlayerSessionPause(%ls,%s,%ls)\n", puid.toString().c_str(), DurangoStats::getPlayerSession(), DurangoStats::getMultiplayerCorrelationId()); EventWritePlayerSessionPause( puid.toString().c_str(), DurangoStats::getPlayerSession(), DurangoStats::getMultiplayerCorrelationId()); } } void DurangoStats::playerSessionResume(int iPad) { std::shared_ptr plr = Minecraft::GetInstance()->localplayers[iPad]; if (plr != NULL && plr->level != NULL && plr->level->getLevelData() != NULL) { PlayerUID puid; ProfileManager.GetXUID(iPad, &puid, true); // EventWritePlayerSessionResume( app.DebugPrintf(">>>\tPlayerSessionResume(%ls,%s,%ls,%i,%i)\n", puid.toString().c_str(), DurangoStats::getPlayerSession(), DurangoStats::getMultiplayerCorrelationId(), plr->level->getLevelData()->getGameType()->isSurvival(), plr->level->difficulty); EventWritePlayerSessionResume( puid.toString().c_str(), DurangoStats::getPlayerSession(), DurangoStats::getMultiplayerCorrelationId(), plr->level->getLevelData()->getGameType()->isSurvival(), plr->level->difficulty); } } void DurangoStats::playerSessionEnd(int iPad) { std::shared_ptr plr = Minecraft::GetInstance()->localplayers[iPad]; if (plr != NULL) { DurangoStats::getInstance()->travel->flush(plr); } }