mirror of
https://github.com/SoDOff-Project/sodoff.git
synced 2025-10-11 08:18:49 -07:00
initial support for XP points (#16)
* GetPetAchievementsByUserID and enum for PointTypes * add RankService * use AchievementPointTypes for PointTypeID ... insted of int in schema * support for player XP, fix dragon XP - database table - return correct value in API call - save XP from mission * rename RankService to AchievementService * use addAchievementPoints for all non item reward this could be a good place for wallet servicing too ... so currency reward too * return const XP value for farming and fishing we don't have gathering method for those XPs yet * fix avatar schema, fix coding style
This commit is contained in:
parent
fbc9b8a201
commit
406ebe20c2
@ -4,22 +4,42 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using sodoff.Attributes;
|
||||
using sodoff.Model;
|
||||
using sodoff.Schema;
|
||||
using sodoff.Services;
|
||||
using sodoff.Util;
|
||||
|
||||
namespace sodoff.Controllers.Common;
|
||||
public class AchievementController : Controller {
|
||||
|
||||
private readonly DBContext ctx;
|
||||
public AchievementController(DBContext ctx) {
|
||||
private AchievementService achievementService;
|
||||
public AchievementController(DBContext ctx, AchievementService achievementService) {
|
||||
this.ctx = ctx;
|
||||
this.achievementService = achievementService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
//[Produces("application/xml")]
|
||||
[Produces("application/xml")]
|
||||
[Route("AchievementWebService.asmx/GetPetAchievementsByUserID")]
|
||||
public IActionResult GetPetAchievementsByUserID() {
|
||||
// TODO, this is a placeholder
|
||||
return Ok("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ArrayOfUserAchievementInfo xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://api.jumpstart.com/\" />");
|
||||
public IActionResult GetPetAchievementsByUserID([FromForm] string apiToken, [FromForm] string userId) {
|
||||
// TODO: check session
|
||||
|
||||
Viking? viking = ctx.Vikings.FirstOrDefault(e => e.Id == userId);
|
||||
if (viking is null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<UserAchievementInfo> dragonsAchievement = new List<UserAchievementInfo>();
|
||||
foreach (Dragon dragon in viking.Dragons) {
|
||||
dragonsAchievement.Add(
|
||||
achievementService.CreateUserAchievementInfo(dragon.EntityId, dragon.PetXP, AchievementPointTypes.DragonXP)
|
||||
);
|
||||
}
|
||||
|
||||
ArrayOfUserAchievementInfo arrAchievements = new ArrayOfUserAchievementInfo {
|
||||
UserAchievementInfo = dragonsAchievement.ToArray()
|
||||
};
|
||||
|
||||
return Ok(arrAchievements);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
@ -50,27 +70,19 @@ public class AchievementController : Controller {
|
||||
[Produces("application/xml")]
|
||||
[Route("AchievementWebService.asmx/GetAchievementsByUserID")]
|
||||
public IActionResult GetAchievementsByUserID([FromForm] string userId) {
|
||||
// TODO: this is a placeholder
|
||||
// TODO: check session
|
||||
|
||||
Viking? viking = ctx.Vikings.FirstOrDefault(e => e.Id == userId);
|
||||
|
||||
if (viking is null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ArrayOfUserAchievementInfo arrAchievements = new ArrayOfUserAchievementInfo {
|
||||
UserAchievementInfo = new UserAchievementInfo[]{
|
||||
new UserAchievementInfo {
|
||||
UserID = Guid.Parse(userId),
|
||||
AchievementPointTotal = 5000,
|
||||
RankID = 30,
|
||||
PointTypeID = 1
|
||||
},
|
||||
new UserAchievementInfo {
|
||||
UserID = Guid.Parse(userId),
|
||||
AchievementPointTotal = 5000,
|
||||
RankID = 30,
|
||||
PointTypeID = 9
|
||||
},
|
||||
new UserAchievementInfo {
|
||||
UserID = Guid.Parse(userId),
|
||||
AchievementPointTotal = 5000,
|
||||
RankID = 30,
|
||||
PointTypeID = 10
|
||||
},
|
||||
achievementService.CreateUserAchievementInfo(viking, AchievementPointTypes.PlayerXP),
|
||||
achievementService.CreateUserAchievementInfo(viking.Id, 60000, AchievementPointTypes.PlayerFarmingXP), // TODO: placeholder until there is no leveling for farm XP
|
||||
achievementService.CreateUserAchievementInfo(viking.Id, 20000, AchievementPointTypes.PlayerFishingXP), // TODO: placeholder until there is no leveling for fishing XP
|
||||
}
|
||||
};
|
||||
|
||||
@ -86,7 +98,7 @@ public class AchievementController : Controller {
|
||||
return Ok(new AchievementReward[1] {
|
||||
new AchievementReward {
|
||||
Amount = 5,
|
||||
PointTypeID = 5,
|
||||
PointTypeID = AchievementPointTypes.CashCurrency,
|
||||
EntityID = Guid.Parse(viking.Id),
|
||||
EntityTypeID = 1,
|
||||
RewardID = 552
|
||||
@ -94,7 +106,7 @@ public class AchievementController : Controller {
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost]
|
||||
[Produces("application/xml")]
|
||||
[Route("V2/AchievementWebService.asmx/SetUserAchievementTask")]
|
||||
[DecryptRequest("achievementTaskSetRequest")]
|
||||
@ -112,7 +124,7 @@ public class AchievementController : Controller {
|
||||
AchievementRewards = new AchievementReward[1] {
|
||||
new AchievementReward {
|
||||
Amount = 25,
|
||||
PointTypeID = 1,
|
||||
PointTypeID = AchievementPointTypes.PlayerXP,
|
||||
RewardID = 910,
|
||||
EntityTypeID =1
|
||||
}
|
||||
@ -130,7 +142,7 @@ public class AchievementController : Controller {
|
||||
return Ok(new AchievementReward[1] {
|
||||
new AchievementReward {
|
||||
Amount = 25,
|
||||
PointTypeID = 1,
|
||||
PointTypeID = AchievementPointTypes.PlayerXP,
|
||||
EntityID = Guid.Parse(viking.Id),
|
||||
EntityTypeID = 1,
|
||||
RewardID = 552
|
||||
|
@ -428,7 +428,7 @@ public class ContentController : Controller {
|
||||
return null;
|
||||
}
|
||||
|
||||
RaisedPetData[] dragons = viking.Dragons
|
||||
RaisedPetData[] dragons = viking.Dragons // TODO (multiplayer) we should use userId
|
||||
.Where(d => d.RaisedPetData is not null)
|
||||
.Select(GetRaisedPetDataFromDragon)
|
||||
.ToArray();
|
||||
|
@ -2,14 +2,17 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using sodoff.Model;
|
||||
using sodoff.Schema;
|
||||
using sodoff.Services;
|
||||
using sodoff.Util;
|
||||
|
||||
namespace sodoff.Controllers.Common;
|
||||
public class ProfileController : Controller {
|
||||
|
||||
private readonly DBContext ctx;
|
||||
public ProfileController(DBContext ctx) {
|
||||
private AchievementService achievementService;
|
||||
public ProfileController(DBContext ctx, AchievementService achievementService) {
|
||||
this.ctx = ctx;
|
||||
this.achievementService = achievementService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
@ -141,24 +144,9 @@ public class ProfileController : Controller {
|
||||
RankID = 0, // placeholder
|
||||
AchievementInfo = null, // placeholder
|
||||
Achievements = new UserAchievementInfo[] {
|
||||
new UserAchievementInfo {
|
||||
UserID = Guid.Parse(viking.Id),
|
||||
AchievementPointTotal = 5000,
|
||||
RankID = 30,
|
||||
PointTypeID = 1
|
||||
},
|
||||
new UserAchievementInfo {
|
||||
UserID = Guid.Parse(viking.Id),
|
||||
AchievementPointTotal = 5000,
|
||||
RankID = 30,
|
||||
PointTypeID = 9
|
||||
},
|
||||
new UserAchievementInfo {
|
||||
UserID = Guid.Parse(viking.Id),
|
||||
AchievementPointTotal = 5000,
|
||||
RankID = 30,
|
||||
PointTypeID = 10
|
||||
},
|
||||
achievementService.CreateUserAchievementInfo(viking, AchievementPointTypes.PlayerXP),
|
||||
achievementService.CreateUserAchievementInfo(viking.Id, 60000, AchievementPointTypes.PlayerFarmingXP), // TODO: placeholder until there is no leveling for farm XP
|
||||
achievementService.CreateUserAchievementInfo(viking.Id, 20000, AchievementPointTypes.PlayerFishingXP), // TODO: placeholder until there is no leveling for fishing XP
|
||||
}
|
||||
};
|
||||
|
||||
|
12
src/Model/AchievementPoints.cs
Normal file
12
src/Model/AchievementPoints.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace sodoff.Model;
|
||||
public class AchievementPoints {
|
||||
public string VikingId { get; set; }
|
||||
|
||||
public int Type { get; set; }
|
||||
|
||||
public int Value { get; set; }
|
||||
|
||||
public virtual Viking? Viking { get; set; }
|
||||
}
|
@ -46,6 +46,9 @@ public class DBContext : DbContext {
|
||||
builder.Entity<Viking>().HasMany(v => v.Rooms)
|
||||
.WithOne(e => e.Viking);
|
||||
|
||||
builder.Entity<Viking>().HasMany(v => v.AchievementPoints)
|
||||
.WithOne(e => e.Viking);
|
||||
|
||||
builder.Entity<Viking>().HasOne(s => s.User)
|
||||
.WithMany(e => e.Vikings)
|
||||
.HasForeignKey(e => e.UserId);
|
||||
@ -130,5 +133,12 @@ public class DBContext : DbContext {
|
||||
builder.Entity<RoomItem>().HasOne(i => i.Room)
|
||||
.WithMany(r => r.Items)
|
||||
.HasForeignKey(e => e.RoomId);
|
||||
|
||||
builder.Entity<AchievementPoints>().HasKey(e => new { e.VikingId, e.Type });
|
||||
|
||||
builder.Entity<AchievementPoints>()
|
||||
.HasOne(e => e.Viking)
|
||||
.WithMany(e => e.AchievementPoints)
|
||||
.HasForeignKey(e => e.VikingId);
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,8 @@ public class Dragon {
|
||||
|
||||
public string? RaisedPetData { get; set; }
|
||||
|
||||
public int? PetXP { get; set; }
|
||||
|
||||
public virtual Viking Viking { get; set; } = null!;
|
||||
public virtual Viking SelectedViking { get; set; } = null!;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ public class Viking {
|
||||
public virtual ICollection<Image> Images { get; set; } = null!;
|
||||
public virtual ICollection<MissionState> MissionStates { get; set; } = null!;
|
||||
public virtual ICollection<Room> Rooms { get; set; } = null!;
|
||||
public virtual ICollection<AchievementPoints> AchievementPoints { get; set; } = null!;
|
||||
public virtual Dragon? SelectedDragon { get; set; }
|
||||
|
||||
public int InventoryId { get; set; }
|
||||
|
@ -18,6 +18,7 @@ builder.Services.AddSingleton<MissionStoreSingleton>();
|
||||
builder.Services.AddScoped<MissionService>();
|
||||
builder.Services.AddSingleton<StoreService>();
|
||||
builder.Services.AddScoped<RoomService>();
|
||||
builder.Services.AddSingleton<AchievementService>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
|
35
src/Schema/AchievementPointTypes.cs
Normal file
35
src/Schema/AchievementPointTypes.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace sodoff.Schema;
|
||||
|
||||
public enum AchievementPointTypes {
|
||||
[XmlEnum("1")]
|
||||
PlayerXP = 1,
|
||||
|
||||
[XmlEnum("2")]
|
||||
GameCurrency = 2, // gold
|
||||
|
||||
[XmlEnum("4")]
|
||||
Unknown4 = 4,
|
||||
|
||||
[XmlEnum("5")]
|
||||
CashCurrency = 5, // gems
|
||||
|
||||
[XmlEnum("6")]
|
||||
ItemReward = 6,
|
||||
|
||||
[XmlEnum("8")]
|
||||
DragonXP = 8,
|
||||
|
||||
[XmlEnum("9")]
|
||||
PlayerFarmingXP = 9,
|
||||
|
||||
[XmlEnum("10")]
|
||||
PlayerFishingXP = 10,
|
||||
|
||||
[XmlEnum("12")]
|
||||
UDTPoints = 12,
|
||||
|
||||
[XmlEnum("13")]
|
||||
Unknown13 = 13,
|
||||
}
|
@ -13,7 +13,7 @@ public class AchievementReward
|
||||
public int? Amount;
|
||||
|
||||
[XmlElement(ElementName = "p", IsNullable = true)]
|
||||
public int? PointTypeID;
|
||||
public AchievementPointTypes? PointTypeID;
|
||||
|
||||
[XmlElement(ElementName = "ii")]
|
||||
public int ItemID;
|
||||
|
11
src/Schema/ArrayOfUserRank.cs
Normal file
11
src/Schema/ArrayOfUserRank.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using System.Diagnostics;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace sodoff.Schema;
|
||||
|
||||
[XmlRoot(ElementName = "ArrayOfUserRank", Namespace = "http://api.jumpstart.com/")]
|
||||
[Serializable]
|
||||
public class ArrayOfUserRank {
|
||||
[XmlElement(ElementName = "UserRank")]
|
||||
public UserRank[] UserRank;
|
||||
}
|
@ -7,6 +7,7 @@ namespace sodoff.Schema;
|
||||
public class AvatarData
|
||||
{
|
||||
[XmlElement(ElementName = "IsSuggestedAvatarName", IsNullable = true)]
|
||||
public bool? IsSuggestedAvatarName;
|
||||
|
||||
public int? Id;
|
||||
|
||||
|
@ -10,7 +10,7 @@ public class ItemStateCriteriaReplenishable : ItemStateCriteria
|
||||
public bool ApplyRank;
|
||||
|
||||
[XmlElement(ElementName = "PointTypeID", IsNullable = true)]
|
||||
public int? PointTypeID;
|
||||
public AchievementPointTypes? PointTypeID;
|
||||
|
||||
[XmlElement(ElementName = "ReplenishableRates")]
|
||||
public List<ReplenishableRate> ReplenishableRates;
|
||||
|
@ -7,7 +7,7 @@ namespace sodoff.Schema;
|
||||
public class RewardMultiplier
|
||||
{
|
||||
[XmlElement(ElementName = "PT")]
|
||||
public int PointTypeID;
|
||||
public AchievementPointTypes PointTypeID;
|
||||
|
||||
[XmlElement(ElementName = "MF")]
|
||||
public int MultiplierFactor;
|
||||
|
@ -19,7 +19,7 @@ public class UserAchievementInfo
|
||||
public int RankID;
|
||||
|
||||
[XmlElement(ElementName = "p")]
|
||||
public int? PointTypeID;
|
||||
public AchievementPointTypes? PointTypeID;
|
||||
|
||||
[XmlElement(ElementName = "FBUID", IsNullable = true)]
|
||||
public long? FacebookUserID;
|
||||
|
20
src/Schema/UserRank.cs
Normal file
20
src/Schema/UserRank.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System.Diagnostics;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace sodoff.Schema;
|
||||
|
||||
[XmlRoot(ElementName = "UserRank", Namespace = "")]
|
||||
[Serializable]
|
||||
public class UserRank {
|
||||
[XmlElement(ElementName = "PointTypeID")]
|
||||
public AchievementPointTypes PointTypeID;
|
||||
|
||||
[XmlElement(ElementName = "Value")]
|
||||
public int Value;
|
||||
|
||||
[XmlElement(ElementName = "RankID")]
|
||||
public int? RankID;
|
||||
|
||||
[XmlElement(ElementName = "GlobalRankID")]
|
||||
public int? GlobalRankID;
|
||||
}
|
56
src/Services/AchievementService.cs
Normal file
56
src/Services/AchievementService.cs
Normal file
@ -0,0 +1,56 @@
|
||||
using sodoff.Schema;
|
||||
using sodoff.Model;
|
||||
using sodoff.Util;
|
||||
using System.Reflection;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace sodoff.Services {
|
||||
public class AchievementService {
|
||||
|
||||
Dictionary<AchievementPointTypes, UserRank[]> ranks = new();
|
||||
|
||||
public AchievementService() {
|
||||
ArrayOfUserRank allranks = XmlUtil.DeserializeXml<ArrayOfUserRank>(XmlUtil.ReadResourceXmlString("allranks"));
|
||||
|
||||
foreach (var pointType in Enum.GetValues<AchievementPointTypes>()) {
|
||||
ranks[pointType] = allranks.UserRank.Where(r => r.PointTypeID == pointType).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public int GetRankFromXP(int? xpPoints, AchievementPointTypes type) {
|
||||
return ranks[type].Count(r => r.Value <= xpPoints);
|
||||
}
|
||||
|
||||
public UserAchievementInfo CreateUserAchievementInfo(string userId, int? value, AchievementPointTypes type) {
|
||||
if (value is null)
|
||||
value = 0;
|
||||
return new UserAchievementInfo {
|
||||
UserID = Guid.Parse(userId),
|
||||
AchievementPointTotal = value,
|
||||
RankID = GetRankFromXP(value, type),
|
||||
PointTypeID = type
|
||||
};
|
||||
}
|
||||
|
||||
public UserAchievementInfo CreateUserAchievementInfo(Viking viking, AchievementPointTypes type) {
|
||||
return CreateUserAchievementInfo(viking.Id, viking.AchievementPoints.FirstOrDefault(a => a.Type == (int)type)?.Value, type);
|
||||
}
|
||||
|
||||
public void AddAchievementPoints(Viking viking, AchievementPointTypes? type, int? value) {
|
||||
if (type == AchievementPointTypes.DragonXP) {
|
||||
viking.SelectedDragon.PetXP = (viking.SelectedDragon.PetXP ?? 0) + (value ?? 0);
|
||||
} else if (type != null) {
|
||||
AchievementPoints xpPoints = viking.AchievementPoints.FirstOrDefault(a => a.Type == (int)type);
|
||||
if (xpPoints is null) {
|
||||
xpPoints = new AchievementPoints {
|
||||
Type = (int)type,
|
||||
Value = 0
|
||||
};
|
||||
viking.AchievementPoints.Add(xpPoints);
|
||||
}
|
||||
xpPoints.Value += value ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -8,8 +8,9 @@ public class MissionService {
|
||||
|
||||
private readonly DBContext ctx;
|
||||
private MissionStoreSingleton missionStore;
|
||||
private AchievementService achievementService;
|
||||
|
||||
public MissionService(DBContext ctx, MissionStoreSingleton missionStore) {
|
||||
public MissionService(DBContext ctx, MissionStoreSingleton missionStore, AchievementService achievementService) {
|
||||
this.ctx = ctx;
|
||||
this.missionStore = missionStore;
|
||||
}
|
||||
@ -49,7 +50,7 @@ public class MissionService {
|
||||
missionState.UserAccepted = null;
|
||||
}
|
||||
foreach (var reward in mission.Rewards) {
|
||||
if (reward.PointTypeID == 6) {
|
||||
if (reward.PointTypeID == AchievementPointTypes.ItemReward) {
|
||||
// TODO: This is not a pretty solution. Use inventory service in the future
|
||||
InventoryItem? ii = viking.Inventory.InventoryItems.FirstOrDefault(x => x.ItemId == reward.ItemID);
|
||||
if (ii is null) {
|
||||
@ -60,6 +61,8 @@ public class MissionService {
|
||||
viking.Inventory.InventoryItems.Add(ii);
|
||||
}
|
||||
ii.Quantity += (int)reward.Amount!;
|
||||
} else { // currencies, all types of player XP and dragon XP
|
||||
achievementService.AddAchievementPoints(viking, reward.PointTypeID, reward.Amount);
|
||||
}
|
||||
}
|
||||
ctx.SaveChanges();
|
||||
|
@ -125,7 +125,7 @@ public class RoomService {
|
||||
if (rewards != null) {
|
||||
response.Rewards = rewards;
|
||||
foreach (var reward in rewards) {
|
||||
if (reward.PointTypeID == 6) {
|
||||
if (reward.PointTypeID == AchievementPointTypes.ItemReward) {
|
||||
// TODO: This is not a pretty solution. Use inventory service in the future
|
||||
InventoryItem? ii = item.Room.Viking.Inventory.InventoryItems.FirstOrDefault(x => x.ItemId == reward.ItemID);
|
||||
if (ii is null) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user