Skip to content

Commit

Permalink
list, appearance, bugfixes
Browse files Browse the repository at this point in the history
  • Loading branch information
amylizzle committed Feb 19, 2025
1 parent dda9e5e commit 2b51722
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 35 deletions.
2 changes: 1 addition & 1 deletion DMCompiler/DMStandard/Types/Atoms/Movable.dm
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
//Undocumented var. "[x],[y]" or "[x],[y] to [x2],[y2]" based on bound_* vars
var/bounds as opendream_unimplemented

var/particles/particles as opendream_unimplemented
var/particles/particles

proc/Bump(atom/Obstacle)

Expand Down
48 changes: 24 additions & 24 deletions DMCompiler/DMStandard/Types/Particles.dm
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,34 @@
/particles
parent_type = /datum
//Particle vars that affect the entire set (generators are not allowed for these)
var/width = 100 as opendream_unimplemented //null defaults to 0. width is the size of the particle "image" ie particles within this width image will be rendered, if they are partially in they get partially cut. if they reenter this area after leaving it they reapper. image is centered on particle owner.
var/height = 100 as opendream_unimplemented //ditto
var/count = 100 as opendream_unimplemented // if null, uses the last set value. is checked BEFORE lifespan so (count 10, lifespan 10, spawning 1) will skip a pixel every 10 pixels
var/spawning = 1 as opendream_unimplemented // null is treated as 0
var/bound1 = -1000 as opendream_unimplemented // Usually list but if a number treated as list(bound1, bound1, bound1). if particles go above/below bound they will get immediately deleted regardless of lifespan. null is treated as the default value (-1000 and 1000)(this could be treated as infinity as well but 1000 is so large its hard to tell)
var/bound2 = 1000 as opendream_unimplemented // Ditto!
var/gravity as opendream_unimplemented // Usually list but if a number treated as list(gravity, gravity, gravity).
var/list/gradient = null as opendream_unimplemented // not cast as a list on byond as of 514.1580 despite only being able to be a list
var/transform as opendream_unimplemented // matrix or list. list can be simple matrix, complex matrix or projection matrix. thus: list(a, b, c, d, e, f) OR list(xx,xy,xz, yx,yy,yz, zx,zy,zz) OR list(xx,xy,xz, yx,yy,yz, zx,zy,zz, cx,cy,cz) OR list(xx,xy,xz,xw, yx,yy,yz,yw, zx,zy,zz,zw, wx,wy,wz,ww)
var/width = 100 //null defaults to 0. width is the size of the particle "image" ie particles within this width image will be rendered, if they are partially in they get partially cut. if they reenter this area after leaving it they reapper. image is centered on particle owner.
var/height = 100 //ditto
var/count = 100 // if null, uses the last set value. is checked BEFORE lifespan so (count 10, lifespan 10, spawning 1) will skip a pixel every 10 pixels
var/spawning = 1 // null is treated as 0
var/bound1 = -1000 // Usually list but if a number treated as list(bound1, bound1, bound1). if particles go above/below bound they will get immediately deleted regardless of lifespan. null is treated as the default value (-1000 and 1000)(this could be treated as infinity as well but 1000 is so large its hard to tell)
var/bound2 = 1000 // Ditto!
var/gravity // Usually list but if a number treated as list(gravity, gravity, gravity).
var/list/gradient = null // not cast as a list on byond as of 514.1580 despite only being able to be a list
var/transform // matrix or list. list can be simple matrix, complex matrix or projection matrix. thus: list(a, b, c, d, e, f) OR list(xx,xy,xz, yx,yy,yz, zx,zy,zz) OR list(xx,xy,xz, yx,yy,yz, zx,zy,zz, cx,cy,cz) OR list(xx,xy,xz,xw, yx,yy,yz,yw, zx,zy,zz,zw, wx,wy,wz,ww)

//Vars that apply when a particle spawns
var/lifespan as opendream_unimplemented // actual time a particle exists is fadein + lifespan + fade. thus this just the time it spends fully faded in. null is treated as
var/fade as opendream_unimplemented // null treated as 0
var/fadein as opendream_unimplemented // null treated as 0
var/icon as opendream_unimplemented // either icon or list(icon = weightofthisicon, icon = weightofthisicon) if null defaults to a 1x1 white pixel
var/icon_state as opendream_unimplemented // either string or list(string = weightofthisiconstate, string = weightofthisiconstate) if null defaults to a 1x1 white pixel
var/color as opendream_unimplemented // null treated as 0
var/color_change as opendream_unimplemented // null treated as 0
var/position as opendream_unimplemented // Usually list but if a number treated as list(position, position, position). null is treated as 0
var/velocity as opendream_unimplemented // Usually list but if a number treated as list(velocity, velocity, velocity). null is treated as 0
var/scale as opendream_unimplemented // if null defaults to 1, if number treated as list(scale, scale)
var/grow as opendream_unimplemented // if null defaults to 0, if number treated as list(grow, grow)
var/rotation as opendream_unimplemented // null treated as 0
var/spin as opendream_unimplemented // null treated as 0
var/friction as opendream_unimplemented // null treated as 0, numbers below 0 treated as 0
var/lifespan // actual time a particle exists is fadein + lifespan + fade. thus this just the time it spends fully faded in. null is treated as
var/fade // null treated as 0
var/fadein // null treated as 0
var/icon // either icon or list(icon = weightofthisicon, icon = weightofthisicon) if null defaults to a 1x1 white pixel
var/icon_state // either string or list(string = weightofthisiconstate, string = weightofthisiconstate) if null defaults to a 1x1 white pixel
var/color // null treated as 0
var/color_change // null treated as 0
var/position // Usually list but if a number treated as list(position, position, position). null is treated as 0
var/velocity // Usually list but if a number treated as list(velocity, velocity, velocity). null is treated as 0
var/scale // if null defaults to 1, if number treated as list(scale, scale)
var/grow // if null defaults to 0, if number treated as list(grow, grow)
var/rotation // null treated as 0
var/spin // null treated as 0
var/friction // null treated as 0, numbers below 0 treated as 0

//Vars that are evaluated every tick
var/drift as opendream_unimplemented // Usually list but if a number treated as list(drift, drift, drift)
var/drift // Usually list but if a number treated as list(drift, drift, drift)

//misc notes
// particle image height/width is not considered for TILE_BOUND-less atoms
Expand Down
1 change: 1 addition & 0 deletions DMCompiler/DMStandard/_Standard.dm
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ proc/flist(Path) as /list
proc/floor(A) as num
proc/fract(n) as num
proc/ftime(File, IsCreationTime = 0) as num
proc/generator(type, A, B, rand) as /generator
proc/get_step_to(Ref, Trg, Min=0) as num
proc/get_steps_to(Ref, Trg, Min=0) as /list
proc/gradient(A, index)
Expand Down
89 changes: 89 additions & 0 deletions OpenDreamRuntime/Objects/Types/DreamList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,95 @@ public override int GetLength() {
}
}

// atom.particles list
// Operates on an atom's appearance
public sealed class DreamParticlesList : DreamList {
[Dependency] private readonly AtomManager _atomManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
private readonly PvsOverrideSystem? _pvsOverrideSystem;

private readonly List<DreamObjectParticles> _particles = new();
private readonly DreamObject _atom;

public DreamParticlesList(DreamObjectDefinition listDef, PvsOverrideSystem? pvsOverrideSystem, DreamObject atom) : base(listDef, 0) {
IoCManager.InjectDependencies(this);

_pvsOverrideSystem = pvsOverrideSystem;
_atom = atom;
}

public override List<DreamValue> GetValues() {
var values = new List<DreamValue>(_particles.Count);

foreach (var particlesObject in _particles) {
values.Add(new(particlesObject));
}

return values;
}

public override void Cut(int start = 1, int end = 0) {
int count = _particles.Count + 1;
if (end == 0 || end > count) end = count;

_particles.RemoveRange(start - 1, end - start);
_atomManager.UpdateAppearance(_atom, appearance => {
appearance.Particles.RemoveRange(start - 1, end - start);
});
}

public override DreamValue GetValue(DreamValue key) {
if (!key.TryGetValueAsInteger(out var particlesIndex) || particlesIndex < 1)
throw new Exception($"Invalid index into particles list: {key}");
if (particlesIndex > _particles.Count)
throw new Exception($"Atom only has {_particles.Count} particles element(s), cannot index {particlesIndex}");

return new DreamValue(_particles[particlesIndex - 1]);
}

public override void SetValue(DreamValue key, DreamValue value, bool allowGrowth = false) {
throw new Exception("Cannot write to an index of a particles list");
}

public override void AddValue(DreamValue value) {
EntityUid entity;
if (value.TryGetValueAsDreamObject<DreamObjectParticles>(out var particles)) {
if (_particles.Contains(particles))
return; // particles cannot contain duplicates
_particles.Add(particles);
entity = particles.Entity;
} else if (value == DreamValue.Null) {
return; // particles cannot contain nulls
} else {
throw new Exception($"Cannot add {value} to a particles list");
}

// TODO: Only override the entity's visibility if its parent atom is visible
if (entity != EntityUid.Invalid)
_pvsOverrideSystem?.AddGlobalOverride(entity);

_atomManager.UpdateAppearance(_atom, appearance => {
// Add even an invalid UID to keep this and _visContents in sync
appearance.Particles.Add(_entityManager.GetNetEntity(entity));
});
}

public override void RemoveValue(DreamValue value) {
if (!value.TryGetValueAsDreamObject<DreamObjectParticles>(out var particles))
return;

_particles.Remove(particles);
_atomManager.UpdateAppearance(_atom, appearance => {
appearance.Particles.Remove(_entityManager.GetNetEntity(particles.Entity));
});
}

public override int GetLength() {
return _particles.Count;
}
}


// atom.filters list
// Operates on an object's appearance
public sealed class DreamFilterList : DreamList {
Expand Down
19 changes: 19 additions & 0 deletions OpenDreamRuntime/Objects/Types/DreamObjectAtom.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ public class DreamObjectAtom : DreamObject {
public readonly DreamOverlaysList Overlays;
public readonly DreamOverlaysList Underlays;
public readonly DreamVisContentsList VisContents;
public readonly DreamParticlesList Particles;
public readonly DreamFilterList Filters;
public DreamList? VisLocs; // TODO: Implement

public DreamObjectAtom(DreamObjectDefinition objectDefinition) : base(objectDefinition) {
Overlays = new(ObjectTree.List.ObjectDefinition, this, AppearanceSystem, false);
Underlays = new(ObjectTree.List.ObjectDefinition, this, AppearanceSystem, true);
VisContents = new(ObjectTree.List.ObjectDefinition, PvsOverrideSystem, this);
Particles = new(ObjectTree.List.ObjectDefinition, PvsOverrideSystem, this);
Filters = new(ObjectTree.List.ObjectDefinition, this);

AtomManager.AddAtom(this);
Expand Down Expand Up @@ -73,6 +75,9 @@ protected override bool TryGetVar(string varName, out DreamValue value) {
case "vis_contents":
value = new(VisContents);
return true;
case "particles":
value = new(Particles);
return true;

default:
if (AtomManager.IsValidAppearanceVar(varName)) {
Expand Down Expand Up @@ -146,6 +151,20 @@ protected override void SetVar(string varName, DreamValue value) {

break;
}
case "particles": {
Particles.Cut();

if (value.TryGetValueAsDreamList(out var valueList)) {
// TODO: This should postpone UpdateAppearance until after everything is added
foreach (DreamValue particlesValue in valueList.GetValues()) {
Particles.AddValue(particlesValue);
}
} else if (!value.IsNull) {
Particles.AddValue(value);
}

break;
}
case "filters": {
Filters.Cut();

Expand Down
10 changes: 2 additions & 8 deletions OpenDreamRuntime/Objects/Types/DreamObjectParticles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,8 @@ public DreamObjectParticles(DreamObjectDefinition objectDefinition) : base(objec
Entity = EntityManager.SpawnEntity(null, new MapCoordinates(0, 0, MapId.Nullspace)); //spawning an entity in nullspace means it never actually gets sent to any clients until it's placed on the map, or it gets a PVS override
ParticlesComponent = EntityManager.AddComponent<DreamParticlesComponent>(Entity);
//populate component with settings from type
//do set/get var to grab those also
//check if I need to manually send update events to the component?
//add entity array to appearance objects
//collect entities client-side for the rendermetadata
//set up a special list type on /atom for /particles
}

protected override void SetVar(string varName, DreamValue value) {
Expand Down Expand Up @@ -167,8 +164,7 @@ protected override void SetVar(string varName, DreamValue value) {
ParticlesComponent.SpawnPositionHigh = new Vector3(dreamValues[0].MustGetValueAsFloat(), dreamValues[1].MustGetValueAsFloat(), dreamValues[2].MustGetValueAsFloat());
dreamValues = dreamObjectGenerator.A.MustGetValueAsDreamList().GetValues();
ParticlesComponent.SpawnPositionLow = new Vector3(dreamValues[0].MustGetValueAsFloat(), dreamValues[1].MustGetValueAsFloat(), dreamValues[2].MustGetValueAsFloat());
ParticlesComponent.RotationLow = dreamObjectGenerator.A.MustGetValueAsFloat();
ParticlesComponent.RotationType = ParticlePropertyType.RandomUniform; //TODO all the other distributions
ParticlesComponent.SpawnPositionType = ParticlePropertyType.RandomUniform; //TODO all the other distributions
}
break;
case "velocity": //num, list, vector, or generator
Expand Down Expand Up @@ -288,9 +284,7 @@ protected override void SetVar(string varName, DreamValue value) {
ParticlesComponent.DriftType = ParticlePropertyType.RandomUniform; //TODO all the other distributions
}
break;
default:
base.SetVar(varName, value);
break;
}
base.SetVar(varName, value); //all calls should set the internal vars, so GetVar() can just be default also
}
}
1 change: 1 addition & 0 deletions OpenDreamRuntime/Procs/Native/DreamProcNative.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public static void SetupNativeProcs(DreamObjectTree objectTree) {
objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_floor);
objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_fract);
objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_ftime);
objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_generator);
objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_get_step_to);
objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_get_steps_to);
objectTree.SetGlobalNativeProc(DreamProcNativeRoot.NativeProc_hascall);
Expand Down
2 changes: 1 addition & 1 deletion OpenDreamRuntime/Procs/Native/DreamProcNativeRoot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1097,7 +1097,7 @@ public static DreamValue NativeProc_get_steps_to(NativeProc.Bundle bundle, Dream
[DreamProcParameter("type", Type = DreamValueTypeFlag.String)]
[DreamProcParameter("A", Type = DreamValueTypeFlag.DreamObject)]
[DreamProcParameter("B", Type = DreamValueTypeFlag.DreamObject)]
[DreamProcParameter("rand", Type = DreamValueTypeFlag.Float)]
[DreamProcParameter("rand", Type = DreamValueTypeFlag.Float, DefaultValue = 0)]
public static DreamValue NativeProc_generator(NativeProc.Bundle bundle, DreamObject? src, DreamObject? usr) {
string outputTypeString = bundle.GetArgument(0, "type").MustGetValueAsString();
var A = bundle.GetArgument(1, "A");
Expand Down
Loading

0 comments on commit 2b51722

Please sign in to comment.