Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 7 additions & 16 deletions OpenDreamClient/Interface/DreamInterfaceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1048,28 +1048,19 @@ public sealed class CursorHolder(IClyde clyde) {
public readonly bool AllStateSet;

public CursorHolder(IClyde clyde, DMIResource resource) : this(clyde) {
var allState = resource.GetStateAsImage("all", AtomDirection.South);
var allCursor = resource.GetStateAsImage(clyde, "all");

if (allState is not null) { //all overrides all possible states
BaseCursor = clyde.CreateCursor(allState, new(32, 32));
if (allCursor is not null) { //all overrides all possible states
BaseCursor = allCursor;
DragCursor = BaseCursor;
DropCursor = BaseCursor;
OverCursor = BaseCursor;
AllStateSet = true;
} else {
var baseState = resource.GetStateAsImage("", AtomDirection.South);
var overState = resource.GetStateAsImage("over", AtomDirection.South);
var dragState = resource.GetStateAsImage("drag", AtomDirection.South);
var dropState = resource.GetStateAsImage("drop", AtomDirection.South);

if (baseState is not null)
BaseCursor = clyde.CreateCursor(baseState, new(32, 32));
if (overState is not null)
OverCursor = clyde.CreateCursor(overState, new(32, 32));
if (dragState is not null)
DragCursor = clyde.CreateCursor(dragState, new(32, 32));
if (dropState is not null)
DropCursor = clyde.CreateCursor(dropState, new(32, 32));
BaseCursor = resource.GetStateAsImage(clyde, "");
OverCursor = resource.GetStateAsImage(clyde, "over");
DragCursor = resource.GetStateAsImage(clyde, "drag");
DropCursor = resource.GetStateAsImage(clyde, "drop");
}
}
}
Expand Down
21 changes: 13 additions & 8 deletions OpenDreamClient/Resources/ResourceTypes/DMIResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,26 @@ private void ProcessDMIData() {
return _states[stateName];
}

public Image<Rgba32>? GetStateAsImage(string? stateName, AtomDirection dir) {
using Stream dmiStream = new MemoryStream(Data);
DMIParser.ParsedDMIDescription description = DMIParser.ParseDMI(dmiStream);
public ICursor? GetStateAsImage(IClyde clyde, string? stateName) {
using var dmiStream = new MemoryStream(Data);
var description = DMIParser.ParseDMI(dmiStream);

dmiStream.Seek(0, SeekOrigin.Begin);

Image<Rgba32> image = Image.Load<Rgba32>(dmiStream);
if (!(description.GetStateOrDefault(stateName)?.Directions.TryGetValue(dir, out var state) ?? false))
var state = description.GetStateOrDefault(stateName);
if (!(state?.Directions.TryGetValue(AtomDirection.South, out var frames) ?? false))
return null;

var result = image.Clone(clone => {
clone.Resize(new Size(description.Width, description.Height));
clone.Crop(new Rectangle(state[0].X, state[0].Y, state[0].X + description.Width, state[0].Y + description.Height));
var stateImage = image.Clone(clone => {
var frame = frames[0];

clone.Crop(new Rectangle(frame.X, frame.Y, frame.X + description.Width, frame.Y + description.Height));
});
return result;

var hotspot = state.Hotspot ?? (0, stateImage.Height - 1); // Default to the top-left
var cursor = clyde.CreateCursor(stateImage, hotspot);
return cursor;
}

public struct State {
Expand Down
16 changes: 15 additions & 1 deletion OpenDreamShared/Resources/DMIParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ public sealed class ParsedDMIState(string name) {
public bool Loop = true;
public bool Rewind;

/// <summary>
/// The part of the image considered the tip when this is used as a custom cursor
/// </summary>
public Vector2i? Hotspot;

// TODO: This can only contain either 1, 4, or 8 directions. Enforcing this could simplify some things.
public readonly Dictionary<AtomDirection, ParsedDMIFrame[]> Directions = new();

Expand Down Expand Up @@ -450,7 +455,16 @@ private static ParsedDMIDescription ParseDMIDescription(string dmiDescription, u
//TODO
break;
case "hotspot":
//TODO
if (currentState is null) break;
var hotspotValues = value.Split(',');
if (hotspotValues.Length != 3)
throw new Exception($"Invalid hotspot value \"{value}\"");

var hotspotX = int.Parse(hotspotValues[0]);
var hotspotY = int.Parse(hotspotValues[1]);
// TODO: 3rd value? Something to do with what frames the hotspot applies to apparently

currentState.Hotspot = (hotspotX, hotspotY);
break;
default:
throw new Exception($"Invalid key \"{key}\" in DMI description");
Expand Down
Loading