Skip to content

Commit

Permalink
Start work on set src and atom-as-a-first-arg in verbs
Browse files Browse the repository at this point in the history
  • Loading branch information
wixoaGit committed Feb 10, 2024
1 parent b427cd7 commit 6b3563d
Show file tree
Hide file tree
Showing 21 changed files with 345 additions and 98 deletions.
2 changes: 2 additions & 0 deletions DMCompiler/DM/DMProc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public DMProcScope(DMProcScope? parentScope) {
public int Id;
public Dictionary<string, int> GlobalVariables = new();

public VerbSrc? VerbSrc;
public string? VerbName;
public string? VerbCategory = string.Empty;
public string? VerbDesc;
Expand Down Expand Up @@ -140,6 +141,7 @@ public ProcDefinitionJson GetJsonRepresentation() {
procDefinition.Attributes = Attributes;
}

procDefinition.VerbSrc = VerbSrc;
procDefinition.VerbName = VerbName;
// Normally VerbCategory is "" by default and null to hide it, but we invert those during (de)serialization to reduce JSON size
VerbCategory = VerbCategory switch {
Expand Down
15 changes: 15 additions & 0 deletions DMCompiler/DM/VerbSrc.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace DMCompiler.DM;

/// <summary>
/// The value of "set src = ..." in a verb
/// </summary>
public enum VerbSrc {
View,
OView,
World,
WorldContents,
Usr,
UsrContents,
UsrLoc,
UsrGroup
}
49 changes: 47 additions & 2 deletions DMCompiler/DM/Visitors/DMProcBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,54 @@ public void ProcessStatementBreak(DMASTProcStatementBreak statementBreak) {
public void ProcessStatementSet(DMASTProcStatementSet statementSet) {
var attribute = statementSet.Attribute.ToLower();

// TODO deal with "src"
if(attribute == "src") {
DMCompiler.UnimplementedWarning(statementSet.Location, "'set src' is unimplemented");
// TODO: Would be much better if the parser was just more strict with the expression
switch (statementSet.Value) {
case DMASTIdentifier {Identifier: "usr"}:
_proc.VerbSrc = VerbSrc.Usr;
break;
case DMASTDereference {Expression: DMASTIdentifier{Identifier: "usr"}, Operations: var operations}:
if (operations is not [DMASTDereference.FieldOperation {Identifier: var deref}])
goto default;

if (deref == "contents") {
_proc.VerbSrc = VerbSrc.UsrContents;
DMCompiler.UnimplementedWarning(statementSet.Location,
"'set src = usr.contents' is unimplemented");
} else if (deref == "loc") {
_proc.VerbSrc = VerbSrc.UsrLoc;
DMCompiler.UnimplementedWarning(statementSet.Location,
"'set src = usr.loc' is unimplemented");
} else if (deref == "group") {
_proc.VerbSrc = VerbSrc.UsrGroup;
DMCompiler.UnimplementedWarning(statementSet.Location,
"'set src = usr.group' is unimplemented");
} else {
goto default;
}

break;
case DMASTIdentifier {Identifier: "world"}:
_proc.VerbSrc = VerbSrc.World;
DMCompiler.UnimplementedWarning(statementSet.Location,
"'set src = world' is unimplemented");
break;
case DMASTDereference {Expression: DMASTIdentifier{Identifier: "world"}, Operations: var operations}:
if (operations is not [DMASTDereference.FieldOperation {Identifier: "contents"}])
goto default;

_proc.VerbSrc = VerbSrc.WorldContents;
DMCompiler.UnimplementedWarning(statementSet.Location,
"'set src = world.contents' is unimplemented");
break;
case DMASTProcCall {Callable: DMASTCallableProcIdentifier {Identifier: { } viewType and ("view" or "oview")}}:
_proc.VerbSrc = viewType == "view" ? VerbSrc.View : VerbSrc.OView; // TODO: Ranges
break;
default:
DMCompiler.Emit(WarningCode.BadExpression, statementSet.Value.Location, "Invalid verb src");
break;
}

return;
}

Expand Down
3 changes: 2 additions & 1 deletion DMCompiler/Json/DreamProcJson.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ public sealed class ProcDefinitionJson {
public List<SourceInfoJson> SourceInfo { get; set; }
public byte[]? Bytecode { get; set; }

public VerbSrc? VerbSrc { get; set; }
public string? VerbName { get; set; }
public string? VerbCategory { get; set; } = null;
public string? VerbCategory { get; set; }
public string? VerbDesc { get; set; }
public sbyte Invisibility { get; set; }
}
Expand Down
1 change: 1 addition & 0 deletions OpenDream.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=sendmaps/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=splicetext/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=splittext/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=src_0027s/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=statpanel/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=timeofday/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=typesof/@EntryIndexedValue">True</s:Boolean>
Expand Down
94 changes: 76 additions & 18 deletions OpenDreamClient/ClientVerbSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using OpenDreamClient.Rendering;
using OpenDreamShared.Dream;
using OpenDreamShared.Rendering;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Asynchronous;
using Robust.Shared.Timing;
Expand All @@ -15,6 +16,7 @@ public sealed class ClientVerbSystem : VerbSystem {
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly ITaskManager _taskManager = default!;
[Dependency] private readonly ITimerManager _timerManager = default!;
[Dependency] private readonly IOverlayManager _overlayManager = default!;

private EntityQuery<DMISpriteComponent> _spriteQuery;
private EntityQuery<DreamMobSightComponent> _sightQuery;
Expand Down Expand Up @@ -68,46 +70,102 @@ public IEnumerable<VerbInfo> GetAllVerbs() {
/// Find all the verbs the client is currently capable of executing
/// </summary>
/// <param name="ignoreHiddenAttr">Whether to ignore "set hidden = TRUE"</param>
/// <returns>The ID, target, and information of every executable verb</returns>
/// <returns>The ID, src, and information of every executable verb</returns>
public IEnumerable<(int Id, ClientObjectReference Src, VerbInfo VerbInfo)> GetExecutableVerbs(bool ignoreHiddenAttr = false) {
DMISpriteComponent? playerSprite = null;
ClientObjectReference? ourMob = null;
sbyte? seeInvisibility = null;
if (_playerManager.LocalEntity != null) {
playerSprite = _spriteQuery.GetComponent(_playerManager.LocalEntity.Value);
ourMob = new ClientObjectReference(_entityManager.GetNetEntity(_playerManager.LocalEntity.Value));
seeInvisibility = _sightQuery.GetComponent(_playerManager.LocalEntity.Value).SeeInvisibility;
}

bool CanSee(VerbInfo verb, ClientObjectReference src) {
if (verb.IsHidden(ignoreHiddenAttr, seeInvisibility ?? 0))
return false; // TODO: How do invisible client verbs work when you don't have a mob?

// Check the verb's "set src" allows us to execute this
switch (verb.Accessibility) {
case VerbAccessibility.Usr:
if (!src.Equals(ourMob))
return false;

return true;
default:
// TODO: All the other kinds
return true;
}
}

// First, the verbs attached to our client
if (_clientVerbs != null) {
foreach (var verbId in _clientVerbs) {
if (!_verbs.TryGetValue(verbId, out var verb))
continue;
if (verb.IsHidden(ignoreHiddenAttr, seeInvisibility ?? 0))
continue; // TODO: How do invisible client verbs work when you don't have a mob?
if (!CanSee(verb, ClientObjectReference.Client))
continue;

yield return (verbId, ClientObjectReference.Client, verb);
}
}

// Then, the verbs attached to our mob
if (playerSprite?.Icon.Appearance is { } playerAppearance) {
var playerNetEntity = _entityManager.GetNetEntity(_playerManager.LocalEntity);
// Then, the verbs on objects around us
var viewOverlay = _overlayManager.GetOverlay<DreamViewOverlay>();
foreach (var entity in viewOverlay.EntitiesInView) {
if (!_spriteQuery.TryGetComponent(entity, out var sprite))
continue;
if (sprite.Icon.Appearance is not { } appearance)
continue;

if (playerNetEntity != null) {
foreach (var verbId in playerAppearance.Verbs) {
if (!_verbs.TryGetValue(verbId, out var verb))
continue;
if (verb.IsHidden(ignoreHiddenAttr, seeInvisibility!.Value))
continue;
foreach (var verbId in appearance.Verbs) {
if (!_verbs.TryGetValue(verbId, out var verb))
continue;

var src = new ClientObjectReference(_entityManager.GetNetEntity(entity));
if (!CanSee(verb, src))
continue;

yield return (verbId, new(playerNetEntity.Value), verb);
}
yield return (verbId, src, verb);
}
}

// TODO: Turfs, Areas
}

public bool TryGetVerbInfo(int verbId, out VerbInfo verbInfo) {
return _verbs.TryGetValue(verbId, out verbInfo);
/// <summary>
/// Find all the verbs the client is currently capable of executing on the given target
/// </summary>
/// <param name="target">The target of the verb</param>
/// <returns>The ID, src, and information of every executable verb</returns>
public IEnumerable<(int Id, ClientObjectReference Src, VerbInfo VerbInfo)> GetExecutableVerbs(ClientObjectReference target) {
foreach (var verb in GetExecutableVerbs()) {
DreamValueType? targetType = verb.VerbInfo.GetTargetType();

if (targetType == null) {
// TODO: Ignore the verb if "set src =" is used instead of "set src in"
if (verb.Src.Equals(target))
yield return verb;

continue;
}

switch (target.Type) {
case ClientObjectReference.RefType.Entity:
var entity = _entityManager.GetEntity(target.Entity);
var isMob = _entityManager.HasComponent<DreamMobSightComponent>(entity);

if ((targetType & DreamValueType.Mob) != 0x0 && isMob)
yield return verb;
if ((targetType & DreamValueType.Obj) != 0x0 && !isMob)
yield return verb;

break;
case ClientObjectReference.RefType.Turf:
if ((targetType & DreamValueType.Turf) != 0x0)
yield return verb;

break;
}
}
}

/// <summary>
Expand Down
7 changes: 4 additions & 3 deletions OpenDreamClient/Input/ContextMenu/ContextMenuItem.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using OpenDreamClient.Rendering;
using OpenDreamShared.Dream;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
Expand All @@ -10,17 +11,17 @@ namespace OpenDreamClient.Input.ContextMenu;
internal sealed partial class ContextMenuItem : PanelContainer {
private static readonly StyleBox HoverStyle = new StyleBoxFlat(Color.Gray);

public readonly EntityUid Entity;
public readonly ClientObjectReference Target;
public readonly MetaDataComponent EntityMetaData;
public readonly DMISpriteComponent? EntitySprite;

private readonly ContextMenuPopup _menu;

public ContextMenuItem(ContextMenuPopup menu, EntityUid entity, MetaDataComponent metadata, DMISpriteComponent sprite) {
public ContextMenuItem(ContextMenuPopup menu, ClientObjectReference target, MetaDataComponent metadata, DMISpriteComponent sprite) {
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);

Entity = entity;
Target = target;
EntityMetaData = metadata;
EntitySprite = sprite;
_menu = menu;
Expand Down
4 changes: 2 additions & 2 deletions OpenDreamClient/Input/ContextMenu/ContextMenuPopup.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public void RepopulateEntities(IEnumerable<EntityUid> entities) {

var metadata = _entityManager.GetComponent<MetaDataComponent>(entity);

ContextMenu.AddChild(new ContextMenuItem(this, entity, metadata, sprite));
ContextMenu.AddChild(new ContextMenuItem(this, new(_entityManager.GetNetEntity(entity)), metadata, sprite));
}
}

Expand All @@ -59,7 +59,7 @@ public void SetActiveItem(ContextMenuItem item) {
_uiManager.ModalRoot.RemoveChild(_currentVerbMenu);
}

_currentVerbMenu = new VerbMenuPopup(_entityManager, _verbSystem, GetSeeInvisible(), item.Entity, item.EntityMetaData, item.EntitySprite);
_currentVerbMenu = new VerbMenuPopup(_verbSystem, GetSeeInvisible(), item.Target, item.EntityMetaData, item.EntitySprite);

_currentVerbMenu.OnVerbSelected += Close;

Expand Down
25 changes: 11 additions & 14 deletions OpenDreamClient/Input/ContextMenu/VerbMenuPopup.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,15 @@ internal sealed partial class VerbMenuPopup : Popup {

public VerbSelectedHandler? OnVerbSelected;

private readonly IEntityManager _entityManager;
private readonly ClientVerbSystem? _verbSystem;

private readonly EntityUid _entity;
private readonly ClientObjectReference _target;

public VerbMenuPopup(IEntityManager entityManager, ClientVerbSystem? verbSystem, sbyte seeInvisible, EntityUid entity, MetaDataComponent? entityMetaData, DMISpriteComponent? entitySprite) {
public VerbMenuPopup(ClientVerbSystem? verbSystem, sbyte seeInvisible, ClientObjectReference target, MetaDataComponent? entityMetaData, DMISpriteComponent? entitySprite) {
RobustXamlLoader.Load(this);

_entityManager = entityManager;
_verbSystem = verbSystem;
_entity = entity;
_target = target;

if (entityMetaData != null && !string.IsNullOrEmpty(entityMetaData.EntityDescription)) {
DescLabel.Margin = new Thickness(4, 0, 4, 0);
Expand All @@ -31,26 +29,25 @@ public VerbMenuPopup(IEntityManager entityManager, ClientVerbSystem? verbSystem,
Desc.Visible = false;
}

if (verbSystem != null && entitySprite?.Icon.Appearance?.Verbs is { } verbIds) {
foreach (var verbId in verbIds) {
if (!verbSystem.TryGetVerbInfo(verbId, out var verbInfo))
continue;
if (verbInfo.IsHidden(false, seeInvisible))
if (verbSystem != null) {
foreach (var verb in verbSystem.GetExecutableVerbs(_target)) {
if (verb.VerbInfo.IsHidden(false, seeInvisible))
continue;

AddVerb(verbId, verbInfo);
AddVerb(verb.Id, verb.Src, verb.VerbInfo);
}
}
}

private void AddVerb(int verbId, VerbSystem.VerbInfo verbInfo) {
var netEntity = _entityManager.GetNetEntity(_entity);
private void AddVerb(int verbId, ClientObjectReference verbSrc, VerbSystem.VerbInfo verbInfo) {
var button = new Button {
Text = verbInfo.Name
};

var takesTargetArg = verbInfo.GetTargetType() != null && !verbSrc.Equals(_target);

button.OnPressed += _ => {
_verbSystem?.ExecuteVerb(new(netEntity), verbId);
_verbSystem?.ExecuteVerb(verbSrc, verbId, takesTargetArg ? [_target] : []);
Close();
OnVerbSelected?.Invoke();
};
Expand Down
6 changes: 2 additions & 4 deletions OpenDreamClient/Interface/Controls/ControlChild.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@

namespace OpenDreamClient.Interface.Controls;

// todo: robust needs GridSplitter.
// and a non-shit grid control.
internal sealed class ControlChild : InterfaceControl {
// todo: robust needs GridSplitter.
// and a non-shit grid control.


private ControlDescriptorChild ChildDescriptor => (ControlDescriptorChild)ElementDescriptor;

private SplitContainer _grid;
Expand Down
Loading

0 comments on commit 6b3563d

Please sign in to comment.