diff --git a/assets/cubyz/particles/flame.zig.zon b/assets/cubyz/particles/flame.zig.zon index b5a0c4fbce..e47f95fdcc 100644 --- a/assets/cubyz/particles/flame.zig.zon +++ b/assets/cubyz/particles/flame.zig.zon @@ -3,4 +3,7 @@ .density = .{0.00065, 0.00085}, .rotationVelocity = .{20, 35}, .dragCoefficient = .{0.2, 0.3}, + .onTouch = .{ + .type = .destroy, + }, } diff --git a/src/callbacks/block/server/replace_block_type.zig b/src/callbacks/block/server/replace_block_type.zig index d39c96a86f..386ba31e73 100644 --- a/src/callbacks/block/server/replace_block_type.zig +++ b/src/callbacks/block/server/replace_block_type.zig @@ -8,6 +8,10 @@ blockType: u16, pub fn init(zon: main.ZonElement, creator: main.callbacks.Creator) ?*@This() { const replacedBlock = switch (creator) { .block => |b| b, + else => { + std.log.err("cannot use {} for replace_block_type", .{creator}); + return null; + }, }; const result = main.worldArena.create(@This()); const blockId = zon.get([]const u8, "block") orelse { diff --git a/src/callbacks/callbacks.zig b/src/callbacks/callbacks.zig index bfe870e40d..86a3e88f65 100644 --- a/src/callbacks/callbacks.zig +++ b/src/callbacks/callbacks.zig @@ -1,14 +1,17 @@ const std = @import("std"); const main = @import("main"); +const Particle = main.particles.Particle; const Block = main.blocks.Block; const vec = main.vec; +const Vec3f = vec.Vec3f; const Vec3i = vec.Vec3i; pub const ClientBlockCallback = Callback(struct { block: Block, chunk: *main.chunk.Chunk, blockPos: Vec3i }, @import("block/client/_list.zig")); pub const ServerBlockCallback = Callback(struct { block: Block, chunk: *main.chunk.ServerChunk, blockPos: main.chunk.BlockPos }, @import("block/server/_list.zig")); pub const BlockTouchCallback = Callback(struct { entity: *main.server.Entity, source: Block, blockPos: Vec3i, deltaTime: f64 }, @import("block/touch/_list.zig")); +pub const ParticleCollideCallback = Callback(struct { source: *Particle, pos: Vec3f }, @import("particles/_list.zig")); pub const Result = enum { handled, ignored }; @@ -16,10 +19,12 @@ pub fn init() void { ClientBlockCallback.globalInit(); ServerBlockCallback.globalInit(); BlockTouchCallback.globalInit(); + ParticleCollideCallback.globalInit(); } pub const Creator = union(enum) { block: main.blocks.Block, + particle: main.particles.Particle, }; fn Callback(_Params: type, list: type) type { diff --git a/src/callbacks/particles/_list.zig b/src/callbacks/particles/_list.zig new file mode 100644 index 0000000000..167ff5ec11 --- /dev/null +++ b/src/callbacks/particles/_list.zig @@ -0,0 +1 @@ +pub const destroy = @import("destroy.zig"); diff --git a/src/callbacks/particles/destroy.zig b/src/callbacks/particles/destroy.zig new file mode 100644 index 0000000000..9da6d5b6a2 --- /dev/null +++ b/src/callbacks/particles/destroy.zig @@ -0,0 +1,16 @@ +const std = @import("std"); + +const main = @import("main"); +const Block = main.blocks.Block; +const vec = main.vec; +const Vec3i = vec.Vec3i; +const ZonElement = main.ZonElement; + +pub fn init(_: ZonElement, _: main.callbacks.Creator) ?*@This() { + return @as(*@This(), undefined); +} + +pub fn run(_: *@This(), params: main.callbacks.ParticleCollideCallback.Params) main.callbacks.Result { + params.source.currentFrame = 0; + return .handled; +} diff --git a/src/particles.zig b/src/particles.zig index c3fc559593..ee653b07e1 100644 --- a/src/particles.zig +++ b/src/particles.zig @@ -73,6 +73,20 @@ pub const particle_manager = struct { // MARK: particle_manager .rotVel = rotVel, .dragCoefficient = RandomRange(f32).fromZon(zon.getChild("dragCoefficient")) orelse .init(0.5, 0.6), .loopTime = RandomRange(f32).fromZon(zon.getChild("loopTime")), + .onCollide = blk: { + break :blk main.callbacks.ParticleCollideCallback.init( + zon.getChildOrNull("onCollide") orelse break :blk .noop, + .{ + .particle = .{ + .typ = @intCast(types.items.len), + .pos = undefined, + }, + }, + ) orelse { + std.log.err("Failed to load onCollide event for particle {s}", .{id}); + break :blk .noop; + }; + }, }; particleTypeHashmap.put(main.worldArena.allocator, id, @intCast(types.items.len)) catch unreachable; @@ -226,13 +240,6 @@ pub const particle_system = struct { // MARK: particle_system while (i < particleCount) { const particle = &particles[i]; const particleLocal = &particlesLocal[i]; - particle.currentFrame -= particleLocal.frameRate*deltaTime; - if (particle.currentFrame < 0) { - particleCount -= 1; - particles[i] = particles[particleCount]; - particlesLocal[i] = particlesLocal[particleCount]; - continue; - } var pos: Vec3f = particle.pos; var rot = particle.rot; @@ -253,8 +260,10 @@ pub const particle_system = struct { // MARK: particle_system const posDelta = particleLocal.velAndRotationVel*vecDeltaTime; + var hasCollided = false; v3Pos[0] += posDelta[0]; if (physics.collision.collides(.client, .x, -posDelta[0], v3Pos, hitBox)) |box| { + hasCollided = true; if (posDelta[0] < 0) { v3Pos[0] = box.max[0] - hitBox.min[0] + physics.epsilon; } else { @@ -263,6 +272,7 @@ pub const particle_system = struct { // MARK: particle_system } v3Pos[1] += posDelta[1]; if (physics.collision.collides(.client, .y, -posDelta[1], v3Pos, hitBox)) |box| { + hasCollided = true; if (posDelta[1] < 0) { v3Pos[1] = box.max[1] - hitBox.min[1] + physics.epsilon; } else { @@ -271,6 +281,7 @@ pub const particle_system = struct { // MARK: particle_system } v3Pos[2] += posDelta[2]; if (physics.collision.collides(.client, .z, -posDelta[2], v3Pos, hitBox)) |box| { + hasCollided = true; if (posDelta[2] < 0) { v3Pos[2] = box.max[2] - hitBox.min[2] + physics.epsilon; } else { @@ -278,6 +289,7 @@ pub const particle_system = struct { // MARK: particle_system } } pos = @as(Vec3f, @floatCast(v3Pos - playerPos)); + _ = if (hasCollided) particle_manager.typesLocal.items[particle.typ].onCollide.run(.{.source = particle, .pos = pos}); } else { const posDelta = particleLocal.velAndRotationVel*vecDeltaTime; @@ -288,6 +300,14 @@ pub const particle_system = struct { // MARK: particle_system particle.rot = rot; particleLocal.velAndRotationVel[3] = rotVel; + particle.currentFrame -= particleLocal.frameRate*deltaTime; + if (particle.currentFrame < 0) { + particleCount -= 1; + particles[i] = particles[particleCount]; + particlesLocal[i] = particlesLocal[particleCount]; + continue; + } + const positionf64 = @as(Vec3d, @floatCast(pos)) + playerPos; const intPos: vec.Vec3i = @floor(positionf64); const light: [6]u8 = main.renderer.mesh_storage.getLight(intPos[0], intPos[1], intPos[2]) orelse @splat(0); @@ -556,6 +576,7 @@ pub const ParticleTypeLocal = struct { rotVel: RandomRange(f32), dragCoefficient: RandomRange(f32), loopTime: ?RandomRange(f32), + onCollide: main.callbacks.ParticleCollideCallback, }; pub const Particle = extern struct {