mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-01-23 17:43:08 -05:00
102 lines
3.2 KiB
C#
102 lines
3.2 KiB
C#
// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
|
using System;
|
|
using ClassicalSharp.Entities;
|
|
using OpenTK;
|
|
|
|
#if USE16_BIT
|
|
using BlockID = System.UInt16;
|
|
#else
|
|
using BlockID = System.Byte;
|
|
#endif
|
|
|
|
namespace ClassicalSharp.Particles {
|
|
|
|
public abstract class CollidableParticle : Particle {
|
|
|
|
protected bool hitTerrain = false, throughLiquids = true;
|
|
|
|
public void ResetState(Vector3 pos, Vector3 velocity, double lifetime) {
|
|
Position = lastPos = nextPos = pos;
|
|
Velocity = velocity;
|
|
Lifetime = (float)lifetime;
|
|
hitTerrain = false;
|
|
}
|
|
|
|
protected bool Tick(Game game, float gravity, double delta) {
|
|
hitTerrain = false;
|
|
lastPos = Position = nextPos;
|
|
BlockID curBlock = GetBlock(game, (int)Position.X, (int)Position.Y, (int)Position.Z);
|
|
float minY = Utils.Floor(Position.Y) + game.BlockInfo.MinBB[curBlock].Y;
|
|
float maxY = Utils.Floor(Position.Y) + game.BlockInfo.MaxBB[curBlock].Y;
|
|
if (!CanPassThrough(game, curBlock) && Position.Y >= minY &&
|
|
Position.Y < maxY && CollideHor(game, curBlock))
|
|
return true;
|
|
|
|
Velocity.Y -= gravity * (float)delta;
|
|
int startY = (int)Math.Floor(Position.Y);
|
|
Position += Velocity * (float)delta * 3;
|
|
int endY = (int)Math.Floor(Position.Y);
|
|
|
|
if (Velocity.Y > 0) {
|
|
// don't test block we are already in
|
|
for (int y = startY + 1; y <= endY && TestY(game, y, false); y++);
|
|
} else {
|
|
for (int y = startY; y >= endY && TestY(game, y, true); y--);
|
|
}
|
|
nextPos = Position;
|
|
Position = lastPos;
|
|
return base.Tick(game, delta);
|
|
}
|
|
|
|
bool TestY(Game game, int y, bool topFace) {
|
|
if (y < 0) {
|
|
Position.Y = nextPos.Y = lastPos.Y = 0 + Entity.Adjustment;
|
|
Velocity = Vector3.Zero;
|
|
hitTerrain = true;
|
|
return false;
|
|
}
|
|
|
|
BlockID block = GetBlock(game, (int)Position.X, y, (int)Position.Z);
|
|
if (CanPassThrough(game, block)) return true;
|
|
Vector3 minBB = game.BlockInfo.MinBB[block];
|
|
Vector3 maxBB = game.BlockInfo.MaxBB[block];
|
|
float collideY = y + (topFace ? maxBB.Y : minBB.Y);
|
|
bool collideVer = topFace ? (Position.Y < collideY) : (Position.Y > collideY);
|
|
|
|
if (collideVer && CollideHor(game, block)) {
|
|
float adjust = topFace ? Entity.Adjustment : -Entity.Adjustment;
|
|
Position.Y = nextPos.Y = lastPos.Y = collideY + adjust;
|
|
Velocity = Vector3.Zero;
|
|
hitTerrain = true;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CanPassThrough(Game game, BlockID block) {
|
|
byte draw = game.BlockInfo.Draw[block];
|
|
return draw == DrawType.Gas || draw == DrawType.Sprite
|
|
|| (throughLiquids && game.BlockInfo.IsLiquid(block));
|
|
}
|
|
|
|
bool CollideHor(Game game, BlockID block) {
|
|
Vector3 min = game.BlockInfo.MinBB[block] + FloorHor(Position);
|
|
Vector3 max = game.BlockInfo.MaxBB[block] + FloorHor(Position);
|
|
return Position.X >= min.X && Position.Z >= min.Z &&
|
|
Position.X < max.X && Position.Z < max.Z;
|
|
}
|
|
|
|
BlockID GetBlock(Game game, int x, int y, int z) {
|
|
if (game.World.IsValidPos(x, y, z))
|
|
return game.World.GetBlock(x, y, z);
|
|
|
|
if (y >= game.World.Env.EdgeHeight) return Block.Air;
|
|
if (y >= game.World.Env.SidesHeight) return game.World.Env.EdgeBlock;
|
|
return game.World.Env.SidesBlock;
|
|
}
|
|
|
|
static Vector3 FloorHor(Vector3 v) {
|
|
return new Vector3(Utils.Floor(v.X), 0, Utils.Floor(v.Z));
|
|
}
|
|
}
|
|
}
|