mirror of
https://github.com/LBPUnion/ProjectLighthouse.git
synced 2025-05-15 22:22:27 +00:00
LittleBigPlanet 3 Adventure Support (#477)
* Baseline LBP3 Adventure slot support VERY unsafe and hacky to use as of now, this is just testing the waters. * ADC file type checking * Refactor & trimming This might need to be adjusted if any feature is found to be missing * isAdventure added to API * Prototype Adventure icons for Website I am not an artist, please make this more in line with the originals. * Override border radius for LBP3 Adventures * Cleaning * Remove WriteLine and unused property * Remove unused libraries * Handle tracking and submitting of Adventure scores * Check for null instead of 0 Non-adventure slots will report null, not 0 * Score for adventure slot instead of main slot * Tweaks for PR * Identify levels for photos by level resource Verify this doesn't break anything. * SlotCardPartial merge with main changes * PR resolution 2 * probably not what was wanted Use variables for style extension * Internal slots already properly identified * Return line breaks to end of Slot.cs * Remove line break added by Github thanks * Github. * Make this a one-liner * Reduce to two lines * This can also be one line * This can *also* be one line * Update ProjectLighthouse.Servers.Website/Pages/Partials/SlotCardPartial.cshtml Co-authored-by: Josh <josh@slendy.pw> * PR changes * Update ProjectLighthouse/Migrations/20220916141401_ScoreboardAdvSlot.cs Co-authored-by: Josh <josh@slendy.pw> Co-authored-by: Josh <josh@slendy.pw>
This commit is contained in:
parent
83a905c8a2
commit
dfd1d9b748
14 changed files with 107 additions and 22 deletions
|
@ -10,6 +10,7 @@ public struct MinimalSlot
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string IconHash { get; set; }
|
public string IconHash { get; set; }
|
||||||
public bool TeamPick { get; set; }
|
public bool TeamPick { get; set; }
|
||||||
|
public bool IsAdventure { get; set; }
|
||||||
public GameVersion GameVersion { get; set; }
|
public GameVersion GameVersion { get; set; }
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
public long FirstUploaded { get; set; }
|
public long FirstUploaded { get; set; }
|
||||||
|
@ -22,6 +23,7 @@ public struct MinimalSlot
|
||||||
Name = slot.Name,
|
Name = slot.Name,
|
||||||
IconHash = slot.IconHash,
|
IconHash = slot.IconHash,
|
||||||
TeamPick = slot.TeamPick,
|
TeamPick = slot.TeamPick,
|
||||||
|
IsAdventure = slot.IsAdventurePlanet,
|
||||||
GameVersion = slot.GameVersion,
|
GameVersion = slot.GameVersion,
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
FirstUploaded = slot.FirstUploaded,
|
FirstUploaded = slot.FirstUploaded,
|
||||||
|
|
|
@ -63,8 +63,12 @@ public class PhotosController : ControllerBase
|
||||||
{
|
{
|
||||||
case SlotType.User:
|
case SlotType.User:
|
||||||
{
|
{
|
||||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.Type == SlotType.User && s.SlotId == photoSlot.SlotId);
|
// We'll grab the slot by the RootLevel and see what happens from here.
|
||||||
if (slot != null && !string.IsNullOrEmpty(slot.RootLevel)) validLevel = true;
|
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.Type == SlotType.User && s.ResourceCollection.Contains(photoSlot.RootLevel));
|
||||||
|
if(slot == null) break;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(slot!.RootLevel)) validLevel = true;
|
||||||
|
if (slot.IsAdventurePlanet) photoSlot.SlotId = slot.SlotId;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SlotType.Pod:
|
case SlotType.Pod:
|
||||||
|
|
|
@ -40,7 +40,8 @@ public class PublishController : ControllerBase
|
||||||
GameToken gameToken = userAndToken.Value.Item2;
|
GameToken gameToken = userAndToken.Value.Item2;
|
||||||
|
|
||||||
Slot? slot = await this.getSlotFromBody();
|
Slot? slot = await this.getSlotFromBody();
|
||||||
if (slot == null) {
|
if (slot == null)
|
||||||
|
{
|
||||||
Logger.Warn("Rejecting level upload, slot is null", LogArea.Publish);
|
Logger.Warn("Rejecting level upload, slot is null", LogArea.Publish);
|
||||||
return this.BadRequest(); // if the level cant be parsed then it obviously cant be uploaded
|
return this.BadRequest(); // if the level cant be parsed then it obviously cant be uploaded
|
||||||
}
|
}
|
||||||
|
@ -135,10 +136,21 @@ public class PublishController : ControllerBase
|
||||||
return this.BadRequest();
|
return this.BadRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rootLevel.FileType != LbpFileType.Level)
|
if (!slot.IsAdventurePlanet)
|
||||||
{
|
{
|
||||||
Logger.Warn("Rejecting level upload, rootLevel is not a level", LogArea.Publish);
|
if (rootLevel.FileType != LbpFileType.Level)
|
||||||
return this.BadRequest();
|
{
|
||||||
|
Logger.Warn("Rejecting level upload, rootLevel is not a level", LogArea.Publish);
|
||||||
|
return this.BadRequest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (rootLevel.FileType != LbpFileType.Adventure)
|
||||||
|
{
|
||||||
|
Logger.Warn("Rejecting level upload, rootLevel is not a LBP 3 Adventure", LogArea.Publish);
|
||||||
|
return this.BadRequest();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GameVersion slotVersion = FileHelper.ParseLevelVersion(rootLevel);
|
GameVersion slotVersion = FileHelper.ParseLevelVersion(rootLevel);
|
||||||
|
@ -232,7 +244,7 @@ public class PublishController : ControllerBase
|
||||||
|
|
||||||
this.database.Slots.Add(slot);
|
this.database.Slots.Add(slot);
|
||||||
await this.database.SaveChangesAsync();
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
if (user.LevelVisibility == PrivacyType.All)
|
if (user.LevelVisibility == PrivacyType.All)
|
||||||
{
|
{
|
||||||
await WebhookHelper.SendWebhook("New level published!",
|
await WebhookHelper.SendWebhook("New level published!",
|
||||||
|
|
|
@ -26,7 +26,8 @@ public class ScoreController : ControllerBase
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("scoreboard/{slotType}/{id:int}")]
|
[HttpPost("scoreboard/{slotType}/{id:int}")]
|
||||||
public async Task<IActionResult> SubmitScore(string slotType, int id, [FromQuery] bool lbp1 = false, [FromQuery] bool lbp2 = false, [FromQuery] bool lbp3 = false)
|
[HttpPost("scoreboard/{slotType}/{id:int}/{childId:int}")]
|
||||||
|
public async Task<IActionResult> SubmitScore(string slotType, int id, int? childId, [FromQuery] bool lbp1 = false, [FromQuery] bool lbp2 = false, [FromQuery] bool lbp3 = false)
|
||||||
{
|
{
|
||||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||||
if (token == null) return this.StatusCode(403, "");
|
if (token == null) return this.StatusCode(403, "");
|
||||||
|
@ -78,6 +79,7 @@ public class ScoreController : ControllerBase
|
||||||
if (slotType == "developer") id = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer);
|
if (slotType == "developer") id = await SlotHelper.GetPlaceholderSlotId(this.database, id, SlotType.Developer);
|
||||||
|
|
||||||
score.SlotId = id;
|
score.SlotId = id;
|
||||||
|
score.ChildSlotId = childId;
|
||||||
|
|
||||||
Slot? slot = this.database.Slots.FirstOrDefault(s => s.SlotId == score.SlotId);
|
Slot? slot = this.database.Slots.FirstOrDefault(s => s.SlotId == score.SlotId);
|
||||||
if (slot == null)
|
if (slot == null)
|
||||||
|
@ -116,12 +118,13 @@ public class ScoreController : ControllerBase
|
||||||
Type = score.Type,
|
Type = score.Type,
|
||||||
Points = score.Points,
|
Points = score.Points,
|
||||||
SlotId = score.SlotId,
|
SlotId = score.SlotId,
|
||||||
|
ChildSlotId = score.ChildSlotId
|
||||||
};
|
};
|
||||||
|
|
||||||
IQueryable<Score> existingScore = this.database.Scores.Where(s => s.SlotId == playerScore.SlotId)
|
IQueryable<Score> existingScore = this.database.Scores.Where(s => s.SlotId == playerScore.SlotId)
|
||||||
.Where(s => s.PlayerIdCollection == playerScore.PlayerIdCollection)
|
.Where(s => childId != 0 || s.ChildSlotId == childId)
|
||||||
.Where(s => s.Type == playerScore.Type);
|
.Where(s => s.PlayerIdCollection == playerScore.PlayerIdCollection)
|
||||||
|
.Where(s => s.Type == playerScore.Type);
|
||||||
if (existingScore.Any())
|
if (existingScore.Any())
|
||||||
{
|
{
|
||||||
Score first = existingScore.First(s => s.SlotId == playerScore.SlotId);
|
Score first = existingScore.First(s => s.SlotId == playerScore.SlotId);
|
||||||
|
@ -137,13 +140,14 @@ public class ScoreController : ControllerBase
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
string myRanking = this.getScores(score.SlotId, score.Type, username, -1, 5, "scoreboardSegment");
|
string myRanking = this.getScores(score.SlotId, score.Type, username, -1, 5, "scoreboardSegment", childId: score.ChildSlotId);
|
||||||
|
|
||||||
return this.Ok(myRanking);
|
return this.Ok(myRanking);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("friendscores/{slotType}/{slotId:int}/{type:int}")]
|
[HttpGet("friendscores/{slotType}/{slotId:int}/{type:int}")]
|
||||||
public async Task<IActionResult> FriendScores(string slotType, int slotId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5)
|
[HttpGet("friendscores/{slotType}/{slotId:int}/{childId:int}/{type:int}")]
|
||||||
|
public async Task<IActionResult> FriendScores(string slotType, int slotId, int? childId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5)
|
||||||
{
|
{
|
||||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||||
if (token == null) return this.StatusCode(403, "");
|
if (token == null) return this.StatusCode(403, "");
|
||||||
|
@ -169,12 +173,13 @@ public class ScoreController : ControllerBase
|
||||||
if (friendUsername != null) friendNames.Add(friendUsername);
|
if (friendUsername != null) friendNames.Add(friendUsername);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.Ok(this.getScores(slotId, type, username, pageStart, pageSize, "scores", friendNames.ToArray()));
|
return this.Ok(this.getScores(slotId, type, username, pageStart, pageSize, "scores", friendNames.ToArray(), childId));
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("topscores/{slotType}/{slotId:int}/{type:int}")]
|
[HttpGet("topscores/{slotType}/{slotId:int}/{type:int}")]
|
||||||
|
[HttpGet("topscores/{slotType}/{slotId:int}/{childId:int}/{type:int}")]
|
||||||
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
||||||
public async Task<IActionResult> TopScores(string slotType, int slotId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5)
|
public async Task<IActionResult> TopScores(string slotType, int slotId, int? childId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5)
|
||||||
{
|
{
|
||||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||||
if (token == null) return this.StatusCode(403, "");
|
if (token == null) return this.StatusCode(403, "");
|
||||||
|
@ -187,7 +192,7 @@ public class ScoreController : ControllerBase
|
||||||
|
|
||||||
if (slotType == "developer") slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer);
|
if (slotType == "developer") slotId = await SlotHelper.GetPlaceholderSlotId(this.database, slotId, SlotType.Developer);
|
||||||
|
|
||||||
return this.Ok(this.getScores(slotId, type, username, pageStart, pageSize));
|
return this.Ok(this.getScores(slotId, type, username, pageStart, pageSize, childId: childId));
|
||||||
}
|
}
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
||||||
|
@ -199,7 +204,8 @@ public class ScoreController : ControllerBase
|
||||||
int pageStart = -1,
|
int pageStart = -1,
|
||||||
int pageSize = 5,
|
int pageSize = 5,
|
||||||
string rootName = "scores",
|
string rootName = "scores",
|
||||||
string[]? playerIds = null
|
string[]? playerIds = null,
|
||||||
|
int? childId = 0
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -207,8 +213,9 @@ public class ScoreController : ControllerBase
|
||||||
// var needed for Anonymous type returned from SELECT
|
// var needed for Anonymous type returned from SELECT
|
||||||
var rankedScores = this.database.Scores
|
var rankedScores = this.database.Scores
|
||||||
.Where(s => s.SlotId == slotId && s.Type == type)
|
.Where(s => s.SlotId == slotId && s.Type == type)
|
||||||
.AsEnumerable()
|
.Where(s => s.ChildSlotId == null || s.ChildSlotId == childId)
|
||||||
.Where(s => playerIds == null || playerIds.Any(id => s.PlayerIdCollection.Contains(id)))
|
.Where(s => playerIds == null || playerIds.Any(id => s.PlayerIdCollection.Contains(id)))
|
||||||
|
.AsEnumerable()
|
||||||
.OrderByDescending(s => s.Points)
|
.OrderByDescending(s => s.Points)
|
||||||
.ThenBy(s => s.ScoreId)
|
.ThenBy(s => s.ScoreId)
|
||||||
.ToList()
|
.ToList()
|
||||||
|
|
|
@ -37,7 +37,8 @@
|
||||||
{
|
{
|
||||||
case SlotType.User:
|
case SlotType.User:
|
||||||
<span>
|
<span>
|
||||||
in level <b><a href="/slot/@Model.SlotId">@HttpUtility.HtmlDecode(Model.Slot.Name)</a></b>
|
@(Model.Slot.IsAdventurePlanet ? "on an adventure in" : "in level")
|
||||||
|
<b><a href="/slot/@Model.SlotId">@HttpUtility.HtmlDecode(Model.Slot.Name)</a></b>
|
||||||
</span>
|
</span>
|
||||||
break;
|
break;
|
||||||
case SlotType.Developer:
|
case SlotType.Developer:
|
||||||
|
|
|
@ -39,11 +39,13 @@
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@{
|
@{
|
||||||
int size = isMobile || mini ? 50 : 100;
|
int size = isMobile || mini ? 50 : 100;
|
||||||
|
bool isAdventure = Model.IsAdventurePlanet;
|
||||||
|
string advenStyleExt = isAdventure ? "-webkit-mask-image: url(/assets/advSlotCardMask.png); -webkit-mask-size: contain; border-radius: 0%;" : "";
|
||||||
}
|
}
|
||||||
<div>
|
<div>
|
||||||
<img src="~/assets/slotCardOverlay.png" style="min-width: @(size)px; width: @(size)px; height: @(size)px; pointer-events: none; position: absolute; z-index: 3">
|
<img src=@(isAdventure ? "/assets/advSlotCardOverlay.png" : "/assets/slotCardOverlay.png") style="min-width: @(size)px; width: @(size)px; height: @(size)px; pointer-events: none; position: absolute; z-index: 3;">
|
||||||
<img src="~/assets/slotCardBackground.png" style="min-width: @(size)px; width: @(size)px; height: @(size)px; position: absolute; z-index: 1;">
|
<img src="~/assets/slotCardBackground.png" style="min-width: @(size)px; width: @(size)px; height: @(size)px; position: absolute; z-index: 1; @(advenStyleExt)">
|
||||||
<img class="cardIcon slotCardIcon" src="/gameAssets/@iconHash" style="min-width: @(size)px; width: @(size)px; height: @(size)px; position: relative; z-index: 2"
|
<img class="cardIcon slotCardIcon" src="/gameAssets/@iconHash" style="min-width: @(size)px; width: @(size)px; height: @(size)px; position: relative; z-index: 2; @(advenStyleExt)"
|
||||||
onerror="this.onerror='';this.src='/gameAssets/@ServerConfiguration.Instance.WebsiteConfiguration.MissingIconHash'">
|
onerror="this.onerror='';this.src='/gameAssets/@ServerConfiguration.Instance.WebsiteConfiguration.MissingIconHash'">
|
||||||
</div>
|
</div>
|
||||||
<div class="cardStats">
|
<div class="cardStats">
|
||||||
|
|
|
@ -64,6 +64,7 @@ public static class FileHelper
|
||||||
LbpFileType.Texture => true,
|
LbpFileType.Texture => true,
|
||||||
LbpFileType.Script => false,
|
LbpFileType.Script => false,
|
||||||
LbpFileType.Level => true,
|
LbpFileType.Level => true,
|
||||||
|
LbpFileType.Adventure => true,
|
||||||
LbpFileType.Voice => true,
|
LbpFileType.Voice => true,
|
||||||
LbpFileType.Quest => true,
|
LbpFileType.Quest => true,
|
||||||
LbpFileType.Plan => true,
|
LbpFileType.Plan => true,
|
||||||
|
@ -190,6 +191,8 @@ public static class FileHelper
|
||||||
"FSHb" => LbpFileType.Script,
|
"FSHb" => LbpFileType.Script,
|
||||||
"VOPb" => LbpFileType.Voice,
|
"VOPb" => LbpFileType.Voice,
|
||||||
"LVLb" => LbpFileType.Level,
|
"LVLb" => LbpFileType.Level,
|
||||||
|
"ADCb" => LbpFileType.Adventure,
|
||||||
|
"ADSb" => LbpFileType.Adventure,
|
||||||
"PLNb" => LbpFileType.Plan,
|
"PLNb" => LbpFileType.Plan,
|
||||||
"QSTb" => LbpFileType.Quest,
|
"QSTb" => LbpFileType.Quest,
|
||||||
_ => readAlternateHeader(reader),
|
_ => readAlternateHeader(reader),
|
||||||
|
|
|
@ -5,6 +5,7 @@ public enum LbpFileType
|
||||||
Script, // .ff, FSH
|
Script, // .ff, FSH
|
||||||
Texture, // TEX
|
Texture, // TEX
|
||||||
Level, // LVL
|
Level, // LVL
|
||||||
|
Adventure, // ADC, ADS
|
||||||
CrossLevel, // PRF, Cross controller level
|
CrossLevel, // PRF, Cross controller level
|
||||||
FileArchive, // .farc, (ends with FARC)
|
FileArchive, // .farc, (ends with FARC)
|
||||||
Plan, // PLN, uploaded with levels
|
Plan, // PLN, uploaded with levels
|
||||||
|
|
|
@ -60,6 +60,9 @@ public class Slot
|
||||||
[XmlElement("icon")]
|
[XmlElement("icon")]
|
||||||
public string IconHash { get; set; } = "";
|
public string IconHash { get; set; } = "";
|
||||||
|
|
||||||
|
[XmlElement("isAdventurePlanet")]
|
||||||
|
public bool IsAdventurePlanet { get; set; }
|
||||||
|
|
||||||
[XmlElement("rootLevel")]
|
[XmlElement("rootLevel")]
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public string RootLevel { get; set; } = "";
|
public string RootLevel { get; set; } = "";
|
||||||
|
@ -301,6 +304,7 @@ public class Slot
|
||||||
LbpSerializer.StringElement("initiallyLocked", this.InitiallyLocked) +
|
LbpSerializer.StringElement("initiallyLocked", this.InitiallyLocked) +
|
||||||
LbpSerializer.StringElement("isSubLevel", this.SubLevel) +
|
LbpSerializer.StringElement("isSubLevel", this.SubLevel) +
|
||||||
LbpSerializer.StringElement("isLBP1Only", this.Lbp1Only) +
|
LbpSerializer.StringElement("isLBP1Only", this.Lbp1Only) +
|
||||||
|
LbpSerializer.StringElement("isAdventurePlanet", this.IsAdventurePlanet) +
|
||||||
LbpSerializer.StringElement("background", this.BackgroundHash) +
|
LbpSerializer.StringElement("background", this.BackgroundHash) +
|
||||||
LbpSerializer.StringElement("shareable", this.Shareable) +
|
LbpSerializer.StringElement("shareable", this.Shareable) +
|
||||||
LbpSerializer.StringElement("authorLabels", this.AuthorLabels) +
|
LbpSerializer.StringElement("authorLabels", this.AuthorLabels) +
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
using LBPUnion.ProjectLighthouse;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace ProjectLighthouse.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(Database))]
|
||||||
|
[Migration("20220916141401_ScoreboardAdvSlot")]
|
||||||
|
public partial class CreateScoreboardAdvSlot : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "ChildSlotId",
|
||||||
|
table: "Scores",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
using LBPUnion.ProjectLighthouse;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace ProjectLighthouse.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(Database))]
|
||||||
|
[Migration("20220918154500_AddIsAdventureColumn")]
|
||||||
|
public partial class AddisAdventureColumn : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<long>(
|
||||||
|
name: "IsAdventurePlanet",
|
||||||
|
table: "Slots",
|
||||||
|
type: "bool",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,9 @@ public class Score
|
||||||
[ForeignKey(nameof(SlotId))]
|
[ForeignKey(nameof(SlotId))]
|
||||||
public Slot Slot { get; set; }
|
public Slot Slot { get; set; }
|
||||||
|
|
||||||
|
[XmlIgnore]
|
||||||
|
public int? ChildSlotId { get; set; }
|
||||||
|
|
||||||
[XmlElement("type")]
|
[XmlElement("type")]
|
||||||
public int Type { get; set; }
|
public int Type { get; set; }
|
||||||
|
|
||||||
|
|
BIN
ProjectLighthouse/StaticFiles/assets/advSlotCardMask.png
Normal file
BIN
ProjectLighthouse/StaticFiles/assets/advSlotCardMask.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
BIN
ProjectLighthouse/StaticFiles/assets/advSlotCardOverlay.png
Normal file
BIN
ProjectLighthouse/StaticFiles/assets/advSlotCardOverlay.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
Loading…
Add table
Add a link
Reference in a new issue