Skip to content

Commit

Permalink
colored light
Browse files Browse the repository at this point in the history
- change 1 value lightLevel to 3 values rgb light
- change torch color to orange
- add froglights
- add skyLightColor to world
  • Loading branch information
j0code committed Mar 23, 2024
1 parent 95a7bb2 commit 67530a4
Show file tree
Hide file tree
Showing 11 changed files with 137 additions and 30 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion public/blocks.yson
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
water: { type: "fluid", soundMaterial: "water" },
grass_block: { soundMaterial: "grass" },
chest: { type: "container", soundMaterial: "wood" },
torch: { full: false, lightLevel: 15, soundMaterial: "wood" }
torch: { full: false, light: [15, 13, 0], soundMaterial: "wood" },
ochre_froglight: { full: false, light: [13, 12, 8], soundMaterial: "wood" },
verdant_froglight: { full: false, light: [9, 12, 9], soundMaterial: "wood" },
pearlescent_froglight: { full: false, light: [15, 8, 11], soundMaterial: "wood" }
}
}
14 changes: 12 additions & 2 deletions src/Graphics.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import LightColor from "./util/LightColor.js"
import TextRenderer, { type TextRenderingOptions } from "./util/TextRenderer.js"

export default class Graphics {
Expand All @@ -15,7 +16,7 @@ export default class Graphics {
})[]

constructor(readonly canvas: HTMLCanvasElement | OffscreenCanvas, private readonly blockSize: number) {
this.ctx = canvas.getContext("2d")!
this.ctx = canvas.getContext("2d", { alpha: false })!
this.filters = {}
this.filterSaves = []
}
Expand Down Expand Up @@ -87,8 +88,17 @@ export default class Graphics {
return TextRenderer.drawText(this.ctx, text, 0, 0, options)
}

drawImage(image: CanvasImageSource, w: number = 1, h: number = 1) {
drawImage(image: CanvasImageSource, w: number = 1, h: number = 1, light?: LightColor) {
this.ctx.save()

if (light) {
this.ctx.fillStyle = light.toString()
this.ctx.fillRect(0, 0, w * this.blockSize, -h * this.blockSize)
this.ctx.globalCompositeOperation = "multiply"
}

this.ctx.drawImage(image, 0, 0, w * this.blockSize, -h * this.blockSize)
this.ctx.restore()
}

drawPartialImage(image: CanvasImageSource, sx: number, sy: number, sWidth: number, sHeight: number, dWidth: number = 1, dHeight: number = 1) {
Expand Down
44 changes: 28 additions & 16 deletions src/block/Block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import BoundingBox from "../util/BoundingBox.js"
import Dim2 from "../dim/Dim2.js"
import Dim3 from "../dim/Dim3.js"
import Graphics from "../Graphics.js"
import LightColor from "../util/LightColor.js"
import World from "../world/World.js"

export default class Block implements HasData {
Expand All @@ -18,7 +19,7 @@ export default class Block implements HasData {

protected readonly def: BlockDef

private readonly light: { sky: number, block: number }
private readonly light: { sky: number, block: LightColor }

constructor(def: BlockDef | NamespacedId) {
if (def instanceof BlockDef) this.def = def
Expand All @@ -31,7 +32,7 @@ export default class Block implements HasData {
}
}

this.light = { sky: 0, block: 0 }
this.light = { sky: 0, block: new LightColor(0, 0, 0) }
}

get id() {
Expand Down Expand Up @@ -59,11 +60,17 @@ export default class Block implements HasData {
}

get blockLight() {
return Math.max(this.light.block, this.def.lightLevel)
if (this.def.light) return this.light.block.max(this.def.light)

return this.light.block
}

get lightLevel() {
return Math.max(this.skyLight, this.blockLight)
return Math.max(this.skyLight, this.blockLight.level)
}

lightColor(world: World) {
return this.light.block.max(world.skyLight.scale(this.light.sky))
}

hasInventory() {
Expand Down Expand Up @@ -95,7 +102,8 @@ export default class Block implements HasData {
this.light.block = updateBlockLight(world, this, x, y, z, this.light.block)
}

draw(g: Graphics, x: number, y: number, z: number) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
draw(g: Graphics, world: World, x: number, y: number, z: number) {
if (this.def.id == "tiny:air") {
if (debug.showDebugScreen && debug.showAirLightLevel) overlayLightLevel(g, x, y, this.lightLevel)

Expand All @@ -105,17 +113,21 @@ export default class Block implements HasData {
g.save()
g.translate(x, y)

let light = this.lightLevel / 15
/* let light = this.lightLevel / 15
const FLUID_TRANSLUCENCY = 0.35
if (this.type == "fluid") g.globalAlpha = 1 - (1 - FLUID_TRANSLUCENCY) * light
if (z < 0) light *= 0.75
*/

let light = this.lightColor(world)
if (z < 0) light = light.scale(12)

// console.log(light)
g.brightness(light)
this.texture?.draw(g)
// g.brightness(light)
this.texture?.draw(g, 1, 1, false, light)

g.restore()
}
Expand Down Expand Up @@ -180,9 +192,8 @@ function updateSkyLight(world: World, block: Block, x: number, y: number, z: num
return skyLight
}

function updateBlockLight(world: World, block: Block, x: number, y: number, z: number, blockLightBefore: number) {
const derivLight = []
let blockLight: number = blockLightBefore
function updateBlockLight(world: World, block: Block, x: number, y: number, z: number, blockLightBefore: LightColor) {
const derivLight: LightColor[] = []

derivLight.push(deriveBlockLight(world, x-1, y, z))
derivLight.push(deriveBlockLight(world, x+1, y, z))
Expand All @@ -194,8 +205,9 @@ function updateBlockLight(world: World, block: Block, x: number, y: number, z: n
derivLight.push(deriveBlockLight(world, x, y, z-1))
}

blockLight = Math.floor(Math.max(...derivLight, 0))
if (blockLight != blockLightBefore) {
const blockLight = LightColor.max(derivLight)

if (!blockLight.equals(blockLightBefore)) {
world.scheduleBlockUpdate(x-1, y, z)
world.scheduleBlockUpdate(x+1, y, z)
world.scheduleBlockUpdate(x, y-1, z)
Expand All @@ -217,12 +229,12 @@ function deriveSkyLight(world: World, x: number, y: number, z: number) {
return other.skyLight - 1 // water
}

function deriveBlockLight(world: World, x: number, y: number, z: number) {
function deriveBlockLight(world: World, x: number, y: number, z: number): LightColor {
const other = world.getBlock(x, y, z)

if (!other || (other.isSolid() && other.full)) return 0
if (!other || (other.isSolid() && other.full)) return new LightColor(0, 0, 0)

return other.blockLight - 1
return other.blockLight.decrement()
}

function overlayLightLevel(g: Graphics, x: number, y: number, level: number) {
Expand Down
13 changes: 7 additions & 6 deletions src/defs/BlockDef.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { equalsAny, isIntInRange, isObject, isPosInt, validateProperty } from "../util/typecheck.js"
import { equalsAny, isIntInRange, isObject, isPosInt, validateArray, validateProperty } from "../util/typecheck.js"
import Base from "./Base.js"
import { type Flatten } from "../util/interfaces.js"
import LightColor from "../util/LightColor.js"
import Sound from "../sound/Sound.js"
import TinyError from "../TinyError.js"
import { getSound } from "../main.js"
Expand All @@ -11,7 +12,7 @@ export default class BlockDef extends Base {
readonly maxItemStack: number
readonly full: boolean
readonly soundMaterial: string
readonly lightLevel: number
readonly light: LightColor | null
readonly inventorySlots: number | null
readonly inventoryColumns: number | null

Expand Down Expand Up @@ -46,8 +47,8 @@ export default class BlockDef extends Base {
this.inventoryColumns = data.inventoryColumns
} else this.inventorySlots = this.inventoryColumns = null

if (data.type == "block") this.lightLevel = data.lightLevel
else this.lightLevel = 0
if (data.type == "block") this.light = new LightColor(...data.light)
else this.light = null
}

get assetsPath() {
Expand Down Expand Up @@ -77,7 +78,7 @@ type BlockDefData = Flatten<{
full: boolean
} & ({
type: "block",
lightLevel: number
light: [number, number, number]
} | {
type: "container",
inventorySlots: number
Expand All @@ -101,7 +102,7 @@ function validate(data: unknown): data is BlockDefData {
}

if (data.type == "block") {
validateProperty(data, "lightLevel", isIntInRange(0, 16), 0)
validateProperty(data, "light", validateArray(isIntInRange(0, 16)), [0, 0, 0])
}

if (data.type == "container") {
Expand Down
5 changes: 3 additions & 2 deletions src/texture/Texture.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Graphics from "../Graphics.js"
import LightColor from "../util/LightColor.js"
import Subtexture from "./Subtexture.js"

export default class Texture {
Expand Down Expand Up @@ -44,10 +45,10 @@ export default class Texture {
return this.#state == Texture.LOADED
}

draw(g: Graphics, w?: number, h?: number, global: boolean = false) {
draw(g: Graphics, w?: number, h?: number, global: boolean = false, light?: LightColor) {
if (!this.ready) return
if (global) g.globalDrawImage(this.image, w, h)
else g.drawImage(this.image, w, h)
else g.drawImage(this.image, w, h, light)
}

getSubtexture(x: number, y: number, w: number, h: number) {
Expand Down
4 changes: 2 additions & 2 deletions src/util/DebugScreen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ function gameInfo(g: Graphics, world: World, player: Player) {
lines.push(``)
lines.push(`looking at block: ${mouseBlock.x}, ${mouseBlock.y}, ${lookingAt.z}`)
lines.push(`${lookingAt.block.id}`)
lines.push(`light: ${lookingAt.block.lightLevel} (${lookingAt.block.skyLight} sky, ${lookingAt.block.blockLight} block)`)
lines.push(`light: ${lookingAt.block.lightColor(world).debug} (${lookingAt.block.skyLight} sky, ${lookingAt.block.blockLight.debug} block)`)
}

if (lookingAtFluid.block && lookingAtFluid.block.type == "fluid") {
lines.push(``)
lines.push(`looking at fluid: ${mouseBlock.x}, ${mouseBlock.y}, ${lookingAtFluid.z}`)
lines.push(`${lookingAtFluid.block.id}`)
lines.push(`light: ${lookingAtFluid.block.lightLevel} (${lookingAtFluid.block.skyLight} sky, ${lookingAtFluid.block.blockLight} block)`)
lines.push(`light: ${lookingAtFluid.block.lightColor(world).debug} (${lookingAtFluid.block.skyLight} sky, ${lookingAtFluid.block.blockLight.debug} block)`)
}

const offset = g.drawText(lines[0], { drawBg: true, padding: 3, font: { size: 20 }, shadow: false })
Expand Down
73 changes: 73 additions & 0 deletions src/util/LightColor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { isIntInRange } from "./typecheck.js"

const rangeCheck = isIntInRange(0, 16)

export default class LightColor {

static max(lights: LightColor[]) {
let maxRed = 0
let maxGreen = 0
let maxBlue = 0

lights.forEach(color => {
if (color.red > maxRed) maxRed = color.red
if (color.green > maxGreen) maxGreen = color.green
if (color.blue > maxBlue) maxBlue = color.blue
})

return new LightColor(maxRed, maxGreen, maxBlue)
}

readonly red: number
readonly green: number
readonly blue: number

constructor(red: number, green: number, blue: number) {
this.red = rangeCheck(red) ? red : 0
this.green = rangeCheck(green) ? green : 0
this.blue = rangeCheck(blue) ? blue : 0
}

get level() {
return Math.floor((this.red + this.green + this.blue) / 3)
}

get debug() {
return `${this.red} ${this.green} ${this.blue}`
}

max(other: LightColor) {
return new LightColor(Math.max(this.red, other.red), Math.max(this.green, other.green), Math.max(this.blue, other.blue))
}

multiply(other: LightColor) {
const red = (this.red * other.red) / 15
const green = (this.green * other.green) / 15
const blue = (this.blue * other.blue) / 15

return new LightColor(red, green, blue)
}

scale(x: number) {
x /= 15

return new LightColor(Math.floor(this.red * x), Math.floor(this.green * x), Math.floor(this.blue * x))
}

decrement() {
return new LightColor(this.red - 1, this.green - 1, this.blue - 1)
}

equals(other: LightColor) {
return this.red == other.red && this.green == other.green && this.blue == other.blue
}

toString() {
const r = (this.red * 17).toString(16).padStart(2, "0")
const g = (this.green * 17).toString(16).padStart(2, "0")
const b = (this.blue * 17).toString(16).padStart(2, "0")

return `#${r}${g}${b}`
}

}
9 changes: 8 additions & 1 deletion src/world/World.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Player, { type PlayerData } from "../entity/Player.js"
import { blockSize, cam, createBlock, createEntity, gameOffset, getFirstBlock } from "../gui/state/ingame.js"
import Dim2 from "../dim/Dim2.js"
import Graphics from "../Graphics.js"
import LightColor from "../util/LightColor.js"
import { type NamespacedId } from "../util/interfaces.js"
import { game } from "../main.js"
import { isNamespacedId } from "../util/typecheck.js"
Expand Down Expand Up @@ -77,6 +78,7 @@ export default class World {
private blocks: Map<`${number},${number},${number}`, Block>
private entities: Set<Entity>
private tickCount: number
private skyLightColor: LightColor
readonly minX: number
readonly maxX: number
readonly minY: number
Expand All @@ -92,6 +94,7 @@ export default class World {
this.blocks = new Map()
this.entities = new Set()
this.tickCount = 0
this.skyLightColor = new LightColor(0, 2, 3)

for (let x = this.minX; x <= this.maxX; x++) {
for (let y = this.minY; y <= this.maxY; y++) {
Expand All @@ -109,6 +112,10 @@ export default class World {
return this.tickCount
}

get skyLight() {
return this.skyLightColor
}

validBlockPosition(x: number, y: number, z: number) {
x = Math.floor(x)
y = Math.floor(y)
Expand Down Expand Up @@ -236,7 +243,7 @@ export default class World {
for (let x = Math.max(this.minX, left); x <= Math.min(this.maxX, right); x++) {
const frontBlock = getFirstBlock(this, x, y, undefined, block => block.full)

if (z >= frontBlock.z) this.getBlock(x, y, z)?.draw(g, x, y, z)
if (z >= frontBlock.z) this.getBlock(x, y, z)?.draw(g, this, x, y, z)
}
}
}
Expand Down

0 comments on commit 67530a4

Please sign in to comment.