purchase optimization

This commit is contained in:
Spirtix 2024-07-15 11:17:28 +02:00
parent a0f7b1ba3e
commit b9b17b9f27
3 changed files with 141 additions and 106 deletions

View File

@ -1,14 +1,12 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.EntityFrameworkCore;
using Org.BouncyCastle.Security.Certificates;
using sodoff.Attributes;
using sodoff.Model;
using sodoff.Schema;
using sodoff.Services;
using sodoff.Util;
using sodoff.Configuration;
using System;
using System.Globalization;
namespace sodoff.Controllers.Common;
@ -1207,64 +1205,9 @@ public class ContentController : Controller {
[VikingSession(UseLock = true)]
public IActionResult PurchaseItems(Viking viking, [FromForm] string purchaseItemRequest) {
PurchaseStoreItemRequest request = XmlUtil.DeserializeXml<PurchaseStoreItemRequest>(purchaseItemRequest);
List<CommonInventoryResponseItem> items = new List<CommonInventoryResponseItem>();
Gender gender = XmlUtil.DeserializeXml<AvatarData>(viking.AvatarSerialized).GenderType;
bool success = true;
for (int i = 0; i < request.Items.Length; i++) {
int itemId = request.Items[i];
ItemData item = itemService.GetItem(itemId);
UserGameCurrency currency = achievementService.GetUserCurrency(viking);
int coinCost = (int)Math.Round(item.FinalDiscoutModifier * item.Cost);
int gemCost = (int)Math.Round(item.FinalDiscoutModifier * item.CashCost);
if (currency.GameCurrency - coinCost < 0 && currency.CashCurrency - gemCost < 0) {
success = false;
break;
}
achievementService.AddAchievementPoints(viking, AchievementPointTypes.GameCurrency, -coinCost);
achievementService.AddAchievementPoints(viking, AchievementPointTypes.CashCurrency, -gemCost);
if (request.AddMysteryBoxToInventory) {
// force add boxes as item (without "opening")
items.Add(inventoryService.AddItemToInventoryAndGetResponse(viking, itemId, 1));
} else if (itemService.IsBundleItem(itemId)) {
// open and add bundle
ItemData bundleItem = itemService.GetItem(itemId);
foreach (var reward in bundleItem.Relationship.Where(e => e.Type == "Bundle")) {
int quantity = itemService.GetItemQuantity(reward);
for (int j=0; j<quantity; ++j)
items.Add(inventoryService.AddItemToInventoryAndGetResponse(viking, reward.ItemId, 1));
}
} else if (itemService.IsGemBundle(itemId, out int gems)) {
achievementService.AddAchievementPoints(viking, AchievementPointTypes.CashCurrency, gems);
items.Add(new CommonInventoryResponseItem {
CommonInventoryID = 0,
ItemID = itemId,
Quantity = 0
});
} else if (itemService.IsCoinBundle(itemId, out int coins)) {
achievementService.AddAchievementPoints(viking, AchievementPointTypes.GameCurrency, coins);
items.Add(new CommonInventoryResponseItem {
CommonInventoryID = 0,
ItemID = itemId,
Quantity = 0
});
}
else {
// check for mystery box ... open if need
itemService.CheckAndOpenBox(itemId, gender, out itemId, out int quantity);
for (int j=0; j<quantity; ++j) {
items.Add(inventoryService.AddItemToInventoryAndGetResponse(viking, itemId, 1));
}
}
// NOTE: The quantity of purchased items is always 0 and the items are instead duplicated in both the request and the response.
// Call AddItemToInventoryAndGetResponse with Quantity == 1 we get response with quantity == 0.
}
var itemsToPurchase = request.Items.GroupBy(id => id).ToDictionary(g => g.Key, g => g.Count());
CommonInventoryResponse response = new CommonInventoryResponse {
Success = success,
CommonInventoryIDs = items.ToArray(),
UserGameCurrency = achievementService.GetUserCurrency(viking)
};
return Ok(response);
return Ok(PurchaseItemsImpl(viking, itemsToPurchase, request.AddMysteryBoxToInventory));
}
[HttpPost]
@ -1273,51 +1216,9 @@ public class ContentController : Controller {
[VikingSession(UseLock = true)]
public IActionResult PurchaseItemsV1(Viking viking, [FromForm] string itemIDArrayXml) {
int[] itemIdArr = XmlUtil.DeserializeXml<int[]>(itemIDArrayXml);
List<CommonInventoryResponseItem> items = new List<CommonInventoryResponseItem>();
Gender gender = XmlUtil.DeserializeXml<AvatarData>(viking.AvatarSerialized).GenderType;
bool success = true;
for (int i = 0; i < itemIdArr.Length; i++) {
ItemData item = itemService.GetItem(itemIdArr[i]);
UserGameCurrency currency = achievementService.GetUserCurrency(viking);
int coinCost = (int)Math.Round(item.FinalDiscoutModifier * item.Cost);
int gemCost = (int)Math.Round(item.FinalDiscoutModifier * item.CashCost);
if (currency.GameCurrency - coinCost < 0 && currency.CashCurrency - gemCost < 0) {
success = false;
break;
}
achievementService.AddAchievementPoints(viking, AchievementPointTypes.GameCurrency, -coinCost);
achievementService.AddAchievementPoints(viking, AchievementPointTypes.CashCurrency, -gemCost);
if (itemService.IsGemBundle(itemIdArr[i], out int gems)) {
achievementService.AddAchievementPoints(viking, AchievementPointTypes.CashCurrency, gems);
items.Add(new CommonInventoryResponseItem {
CommonInventoryID = 0,
ItemID = itemIdArr[i],
Quantity = 0
});
} else if (itemService.IsCoinBundle(itemIdArr[i], out int coins)) {
achievementService.AddAchievementPoints(viking, AchievementPointTypes.GameCurrency, coins);
items.Add(new CommonInventoryResponseItem {
CommonInventoryID = 0,
ItemID = itemIdArr[i],
Quantity = 0
});
}
else {
itemService.CheckAndOpenBox(itemIdArr[i], gender, out int itemId, out int quantity);
for (int j=0; j<quantity; ++j) {
items.Add(inventoryService.AddItemToInventoryAndGetResponse(viking, itemId, 1));
// NOTE: The quantity of purchased items is always 0 and the items are instead duplicated in both the request and the response.
// Call AddItemToInventoryAndGetResponse with Quantity == 1 we get response with quantity == 0.
}
}
}
var itemsToPurchase = itemIdArr.GroupBy(id => id).ToDictionary(g => g.Key, g => g.Count());
CommonInventoryResponse response = new CommonInventoryResponse {
Success = success,
CommonInventoryIDs = items.ToArray(),
UserGameCurrency = achievementService.GetUserCurrency(viking)
};
return Ok(response);
return Ok(PurchaseItemsImpl(viking, itemsToPurchase, false));
}
[HttpPost]
@ -2293,4 +2194,113 @@ public class ContentController : Controller {
return adjectives[rand.Next(adjectives.Length)] + name;
return name;
}
private CommonInventoryResponse PurchaseItemsImpl(Viking viking, Dictionary<int, int> itemsToPurchase, bool mysteryBox) {
// Viking information
UserGameCurrency currency = achievementService.GetUserCurrency(viking);
Gender gender = XmlUtil.DeserializeXml<AvatarData>(viking.AvatarSerialized).GenderType;
// Purchase information
int totalCoinCost = 0, totalGemCost = 0, coinsToAdd = 0, gemsToAdd = 0;
Dictionary<int, int> inventoryItemsToAdd = new(); // dict of items to add to the inventory
Dictionary<int, int> itemsToSendBack = new(); // dict of items that are sent back in the response
foreach (var i in itemsToPurchase) {
ItemData item = itemService.GetItem(i.Key);
// Calculate cost
totalCoinCost += (int)Math.Round(item.FinalDiscoutModifier * item.Cost) * i.Value;
totalGemCost += (int)Math.Round(item.FinalDiscoutModifier * item.CashCost) * i.Value;
// Resolve items to purchase
if (mysteryBox) {
// add mystery box to inventory
inventoryItemsToAdd.TryAdd(i.Key, 0);
inventoryItemsToAdd[i.Key] += i.Value;
itemsToSendBack.TryAdd(i.Key, 0);
itemsToSendBack[i.Key] += i.Value;
}
else if (itemService.IsGemBundle(i.Key, out int gemValue)) {
// get gem value
gemsToAdd += gemValue * i.Value;
itemsToSendBack.TryAdd(i.Key, 0);
itemsToSendBack[i.Key] += i.Value;
}
else if (itemService.IsCoinBundle(i.Key, out int coinValue)) {
// get coin value
coinsToAdd += coinValue * i.Value;
itemsToSendBack.TryAdd(i.Key, 0);
itemsToSendBack[i.Key] += i.Value;
}
else if (itemService.IsBundleItem(i.Key)) {
ItemData bundleItem = itemService.GetItem(i.Key);
// resolve items in the bundle
foreach (var reward in bundleItem.Relationship.Where(e => e.Type == "Bundle")) {
int quantity = itemService.GetBulkItemQuantity(reward, i.Value);
inventoryItemsToAdd.TryAdd(reward.ItemId, 0);
inventoryItemsToAdd[reward.ItemId] += quantity;
itemsToSendBack.TryAdd(reward.ItemId, 0);
itemsToSendBack[reward.ItemId] += quantity;
}
}
else if (itemService.IsBoxItem(i.Key)) {
// open boxes individually
for (int j = 0; j < i.Value; j++) {
itemService.OpenBox(i.Key, gender, out int itemId, out int quantity);
inventoryItemsToAdd.TryAdd(itemId, 0);
inventoryItemsToAdd[itemId] += quantity;
itemsToSendBack.TryAdd(itemId, 0);
itemsToSendBack[itemId] += quantity;
}
}
else {
// add item to inventory
inventoryItemsToAdd.TryAdd(i.Key, 0);
inventoryItemsToAdd[i.Key] += i.Value;
itemsToSendBack.TryAdd(i.Key, 0);
itemsToSendBack[i.Key] += i.Value;
}
}
// check if the user can afford the purchase
if (currency.GameCurrency - totalCoinCost < 0 && currency.CashCurrency - totalGemCost < 0) {
return new CommonInventoryResponse {
Success = false,
CommonInventoryIDs = new CommonInventoryResponseItem[0],
UserGameCurrency = achievementService.GetUserCurrency(viking)
};
}
// deduct the cost of the purchase
achievementService.AddAchievementPoints(viking, AchievementPointTypes.GameCurrency, -totalCoinCost);
achievementService.AddAchievementPoints(viking, AchievementPointTypes.CashCurrency, -totalGemCost);
// add items to the inventory (database)
var addedItems = inventoryService.AddItemsToInventoryBulk(viking, inventoryItemsToAdd);
// add gems and coins that were purchased
if (gemsToAdd != 0)
achievementService.AddAchievementPoints(viking, AchievementPointTypes.CashCurrency, gemsToAdd);
if (coinsToAdd != 0)
achievementService.AddAchievementPoints(viking, AchievementPointTypes.GameCurrency, coinsToAdd);
// build response
List<CommonInventoryResponseItem> items = new List<CommonInventoryResponseItem>();
foreach (var i in itemsToSendBack) {
items.AddRange(Enumerable.Repeat(
new CommonInventoryResponseItem {
CommonInventoryID = addedItems.ContainsKey(i.Key) ? addedItems[i.Key] : 0, // return inventory id if this item was added to the DB
ItemID = i.Key,
Quantity = 0
}, i.Value));
}
// NOTE: The quantity of purchased items is always 0 and the items are instead duplicated in both the request and the response.
// Call AddItemToInventoryAndGetResponse with Quantity == 1 we get response with quantity == 0.
return new CommonInventoryResponse {
Success = true,
CommonInventoryIDs = items.ToArray(),
UserGameCurrency = achievementService.GetUserCurrency(viking)
};
}
}

View File

@ -57,6 +57,23 @@ namespace sodoff.Services {
};
}
public Dictionary<int, int> AddItemsToInventoryBulk(Viking viking, Dictionary<int,int> itemsWithQuantity) {
List<InventoryItem> items = new();
Dictionary<int, int> itemsWithInventoryId = new();
List<CommonInventoryResponseItem> responses = new();
foreach (var i in itemsWithQuantity) {
items.Add(AddItemToInventory(viking, i.Key, i.Value));
}
ctx.SaveChanges();
foreach (var item in items) {
itemsWithInventoryId[item.ItemId] = item.Id;
}
return itemsWithInventoryId;
}
public InventoryItemStatsMap AddBattleItemToInventory(Viking viking, int itemId, int itemTier, ItemStat[] itemStat = null) {
// get item data
ItemData itemData = itemService.GetItem(itemId);

View File

@ -42,13 +42,21 @@ namespace sodoff.Services {
}
public int GetItemQuantity(ItemDataRelationship itemData) {
return GetItemQuantityImpl(itemData);
}
public int GetBulkItemQuantity(ItemDataRelationship itemData, int quantity) {
return GetItemQuantityImpl(itemData, quantity);
}
private int GetItemQuantityImpl(ItemDataRelationship itemData, int repeat = 1) {
if (itemData.MaxQuantity is null || itemData.MaxQuantity < 2 || itemData.MaxQuantity == itemData.Quantity) {
if (itemData.Quantity == 0)
return 1;
return 1 * repeat;
else
return itemData.Quantity;
return itemData.Quantity * repeat;
}
return random.Next(1, (int)itemData.MaxQuantity + 1);
return random.Next(1 * repeat, (int)itemData.MaxQuantity * repeat + 1);
}
public ItemDataRelationship OpenBox(ItemData boxItem, Gender gender) {