Add support for automatic news article posting

This commit is contained in:
Ritchie Frodomar 2024-06-21 11:59:53 -04:00
parent bd2d61b315
commit 6deabb0c3d
13 changed files with 148 additions and 17 deletions

View file

@ -1,10 +1,11 @@
# NOTE: This was generated by ChatGPT as dummy text to test the asset importer.
id test
title Breaking News\: Tragedy Strikes as Fictional City of Rivertown Explodes
headline Breaking News\: Tragedy Strikes as Fictional City of Rivertown Explodes
excerpt In a devastating turn of events, the tranquil city of Rivertown has been rocked by a catastrophic explosion, leaving residents and authorities reeling in shock and disbelief. The incident, which occurred in the early hours of this morning, has resulted in widespread damage and loss of life, marking a dark day in the history of this once-thriving community.
host newciphertoday.com
author testuser
flags old
Eyewitnesses reported a deafening blast that shattered windows and sent plumes of smoke billowing into the sky. The explosion, centered in the heart of Rivertown\'s downtown district, reportedly originated from a gas leak in a commercial building, triggering a chain reaction of destruction that engulfed neighboring structures within moments.

View file

@ -1,5 +1,7 @@
#nullable enable
using System;
using Core.Scripting;
using DevTools;
using GameplaySystems.Social;
namespace Editor.CustomImporters
@ -13,7 +15,7 @@ namespace Editor.CustomImporters
this.asset = asset;
}
[Function("title")]
[Function("headline")]
private void SetTitle(string[] args)
{
asset.Title = string.Join(" ", args);
@ -42,5 +44,21 @@ namespace Editor.CustomImporters
{
asset.Excerpt = string.Join(" ", text);
}
[Function("flags")]
public void SetFlags(string[] flags)
{
ArticleFlags newFlags = default;
foreach (string flag in flags)
{
if (!Enum.TryParse(flag, true, out ArticleFlags flagValue))
throw new InvalidOperationException($"'{flag}' is not a valid news article flag.");
newFlags |= flagValue;
}
asset.Flags = newFlags;
}
}
}

View file

@ -68,6 +68,12 @@ namespace Core.DataManagement
return this[narrativeIdLookup[narrativeId]];
}
/// <inheritdoc />
public bool ContainsNarrativeId(string narrativeId)
{
return this.narrativeIdLookup.ContainsKey(narrativeId);
}
/// <inheritdoc />
public IEnumerator<TDataElement> GetEnumerator()
{

View file

@ -0,0 +1,14 @@
#nullable enable
using System;
namespace DevTools
{
[Flags]
public enum ArticleFlags : byte
{
None = 0,
Scripted = 1,
Old = Scripted << 1,
Hidden = Old << 1,
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6bc13aff85c842889285de8e643bfcbc
timeCreated: 1718984474

View file

@ -0,0 +1,21 @@
#nullable enable
using System.Threading.Tasks;
using Core.WorldData.Data;
using UnityEngine;
namespace DevTools
{
public interface IArticleAsset
{
string NarrativeId { get; }
string HostName { get; }
string Title { get; }
string NarrativeAuthorId { get; }
string Excerpt { get; }
DocumentElement[] Body { get; }
ArticleFlags Flags { get; }
Task<Texture2D?> GetFeaturedImage();
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b0edd7966f274e86b364cd36d1d22505
timeCreated: 1718984375

View file

@ -2,25 +2,10 @@
using System.Threading.Tasks;
using ContentManagement;
using Core;
using Core.WorldData.Data;
using Modules;
using UnityEngine;
namespace DevTools
{
public interface IArticleAsset
{
string NarrativeId { get; }
string HostName { get; }
string Title { get; }
string NarrativeAuthorId { get; }
string Excerpt { get; }
DocumentElement[] Body { get; }
Task<Texture2D?> GetFeaturedImage();
}
public interface ICharacterGenerator : IGameContent
{
Task GenerateNpcs(IWorldManager world);

View file

@ -18,5 +18,7 @@ namespace Core
where TDataElement : struct, IWorldData, IDataWithId, INarrativeObject
{
TDataElement GetNarrativeObject(string narrativeId);
bool ContainsNarrativeId(string narrativeId);
}
}

View file

@ -1,5 +1,6 @@
#nullable enable
using System;
using System.Linq;
using ContentManagement;
using Core.WorldData.Data;
using DevTools;
@ -42,6 +43,8 @@ namespace GameplaySystems.Social
internal void Update(WorldNewsData data, IContentManager contentManager)
{
Date = data.Date;
articleAsset = contentManager.GetContentOfType<IArticleAsset>().FirstOrDefault(x => x.NarrativeId == data.NarrativeId);
}
}
}

View file

@ -29,6 +29,9 @@ namespace GameplaySystems.Social
[SerializeField]
private DocumentElement[] body = Array.Empty<DocumentElement>();
[SerializeField]
private ArticleFlags articleFlags;
/// <inheritdoc />
public string NarrativeId
{
@ -83,6 +86,16 @@ namespace GameplaySystems.Social
#endif
}
/// <inheritdoc />
public ArticleFlags Flags
{
get => articleFlags;
#if UNITY_EDITOR
set => articleFlags = value;
#endif
}
/// <inheritdoc />
public Task<Texture2D?> GetFeaturedImage()
{

View file

@ -1,10 +1,13 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using ContentManagement;
using Core;
using Core.WorldData.Data;
using DevTools;
using Social;
using Time = UnityEngine.Time;
namespace GameplaySystems.Social
{
@ -16,6 +19,10 @@ namespace GameplaySystems.Social
private readonly List<NewsArticle> articles = new();
private readonly Dictionary<ObjectId, int> articlesById = new();
private const float UpdateIntervalSeconds = 2;
private float updateTime = 0;
public NewsManager(ISocialService socialService, IWorldManager worldManager, IContentManager contentManager)
{
this.socialService = socialService;
@ -96,5 +103,55 @@ namespace GameplaySystems.Social
return articles.Where(x => x.HostName == hostname)
.OrderByDescending(x => x.Date);
}
internal void Update()
{
updateTime += Time.deltaTime;
if (updateTime < UpdateIntervalSeconds)
return;
updateTime = 0;
PostNewArticles();
}
private void PostNewArticles()
{
foreach (IArticleAsset asset in contentManager.GetContentOfType<IArticleAsset>())
{
if (asset.Flags.HasFlag(ArticleFlags.Scripted))
continue;
bool shouldPost = asset.Flags.HasFlag(ArticleFlags.Old);
if (shouldPost)
{
if (worldManager.World.NewsArticles.ContainsNarrativeId(asset.NarrativeId))
continue;
ObjectId id = worldManager.GetNextObjectId();
var data = new WorldNewsData()
{
InstanceId = id,
NarrativeId = asset.NarrativeId,
Date = asset.Flags.HasFlag(ArticleFlags.Old)
? DateTime.MinValue.AddMonths(id.Id)
: worldManager.World.GlobalWorldState.Value.Now
};
worldManager.World.NewsArticles.Add(data);
}
else
{
if (!worldManager.World.NewsArticles.ContainsNarrativeId(asset.NarrativeId))
continue;
WorldNewsData data = worldManager.World.NewsArticles.GetNarrativeObject(asset.NarrativeId);
worldManager.World.NewsArticles.Remove(data);
}
}
}
}
}

View file

@ -78,6 +78,11 @@ namespace GameplaySystems.Social
newsManager = new NewsManager(this, worldManager, game.ContentManager);
}
private void Update()
{
newsManager.Update();
}
private void OnDestroy()
{
game.ScriptSystem.UnregisterHookListener(CommonScriptHooks.AfterWorldStateUpdate, characterUpdateHook);