Revert "Merge pull request #4478 from Aryn/master"

This reverts commit 86941a58f0, reversing
changes made to 65b8215ec7.
This commit is contained in:
Mloc-Hibernia
2014-03-24 13:55:41 +00:00
parent 34c39e773a
commit d4324901d3
19 changed files with 29 additions and 611 deletions

View File

@@ -74,11 +74,13 @@
#include "code\ATMOSPHERICS\components\unary\unary_base.dm"
#include "code\ATMOSPHERICS\components\unary\vent_pump.dm"
#include "code\ATMOSPHERICS\components\unary\vent_scrubber.dm"
#include "code\controllers\_DynamicAreaLighting_TG.dm"
#include "code\controllers\autotransfer.dm"
#include "code\controllers\configuration.dm"
#include "code\controllers\failsafe.dm"
#include "code\controllers\hooks-defs.dm"
#include "code\controllers\hooks.dm"
#include "code\controllers\lighting_controller.dm"
#include "code\controllers\master_controller.dm"
#include "code\controllers\shuttle_controller.dm"
#include "code\controllers\verbs.dm"
@@ -1283,12 +1285,6 @@
#include "code\WorkInProgress\explosion_particles.dm"
#include "code\WorkInProgress\periodic_news.dm"
#include "code\WorkInProgress\Apples\artifacts.dm"
#include "code\WorkInProgress\Aryn\Lighting\Compatibility.dm"
#include "code\WorkInProgress\Aryn\Lighting\Controller.dm"
#include "code\WorkInProgress\Aryn\Lighting\Engine.dm"
#include "code\WorkInProgress\Aryn\Lighting\Light.dm"
#include "code\WorkInProgress\Aryn\Lighting\Lightpoint.dm"
#include "code\WorkInProgress\Aryn\Lighting\Math.dm"
#include "code\WorkInProgress\Cael_Aislinn\Jungle\falsewall.dm"
#include "code\WorkInProgress\Cael_Aislinn\Jungle\jungle.dm"
#include "code\WorkInProgress\Cael_Aislinn\Jungle\jungle_animals.dm"

View File

@@ -1,45 +0,0 @@
area/var/lighting_use_dynamic
turf/space/is_outside = 1
turf/simulated/shuttle/is_outside = 1
/datum/controller/lighting/var/processing = 1
/datum/controller/lighting/var/iteration = 0
//Because so many objects jump the gun.
proc/lighting_ready()
return lighting_controller && lighting_controller.started
turf_light_data
var/light_overlay
var/lightNW
var/lightSW
var/lightNE
var/lightSE
var/lit_by
turf_light_data/proc/copy_from(turf/T)
light_overlay = T.light_overlay
lightNW = T.lightNW
lightSW = T.lightSW
lightNE = T.lightNE
lightSE = T.lightSE
lit_by = T.lit_by
turf_light_data/proc/copy_to(turf/T)
T.light_overlay = light_overlay
T.lightNW = lightNW
T.lightSW = lightSW
T.lightNE = lightNE
T.lightSE = lightSE
T.lit_by = lit_by
//T.ResetValue()
atom/proc/SetLuminosity(n)
n = min(n,10) //Caelcode.
if(n > 0)
SetLight(max(1,n>>1),n)
else
SetLight(0,0)
luminosity = n
//else lighting_controller.initial_lights.Add(src)

View File

@@ -1,119 +0,0 @@
/*
Overview:
Unlike the previous lighting controller, this is mostly here to hold global lighting procs and vars.
It does not process every tick, because not even DAL did something so stupid.
Global Vars:
initial_lights - This holds all lights formed before the lighting controller started up. It becomes null on initialization.
Class Vars:
starlight - The light value of space.
icon_updates - The list of turfs which need an update to their overlays.
light_border - Space turfs which are adjacent to non-space turfs.
Class Procs:
Initialize()
Starts the lighting system, creating all light points and turf overlays.
StarLight(n)
Sets the light produced by space. If a solar eclipse suddenly happens, it'll probably lag.
MarkIconUpdate(turf/T)
Called when a turf needs an update to its light icon. Ensures that it only gets calculated once per turf.
FlushIconUpdates()
Called when a light is done marking icon updates. Updates every marked turf.
AddBorder(turf/T) & RemoveBorder(turf/T)
Called by turf/CheckForOpaqueObjects() to modify the light_border list.
*/
var/datum/controller/lighting/lighting_controller
var/all_lightpoints_made = 0
var/list/lit_z_levels = list(1,5)
/datum/controller/lighting
var/starlight = 4
var/list/icon_updates = list()
var/list/light_border = list()
var/started = 0
//var/icon/border = icon('Icons/Test.dmi', "border")
/datum/controller/lighting/New()
lighting_controller = src
/datum/controller/lighting/proc/Initialize()
set background = 1
var/start_time = world.timeofday
world << "<b><font color=red>Processing lights...</font></b>"
sleep(1)
var/turfs_updated = 0
var/total_turfs = world.maxx*world.maxy*lit_z_levels.len
for(var/z in lit_z_levels)
for(var/y = 0, y <= world.maxy, y++)
for(var/x = 0, x <= world.maxx, x++)
if(x > 0 && y > 0)
turfs_updated++
if((turfs_updated % (total_turfs>>2)) == 0)
sleep(1)
world << "<font color=red>Progress: [round((turfs_updated/total_turfs)*100, 0.01)]% ([turfs_updated]/[total_turfs])"
var/turf/T = locate(x,y,z)
if(!T.light_overlay && !T.is_outside)
T.light_overlay = new(T)
//T.ResetValue()
if(!all_lightpoints_made) new/lightpoint(x+0.5,y+0.5,z)
//world << "[x],[y],[z]"
all_lightpoints_made = 1
started = 1
for(var/turf/T)
if(T.light_overlay)
T.ResetValue()
T.UpdateLight()
world << "<b><font color=red>Lighting initialization took [(world.timeofday-start_time)/world.fps] seconds.</font></b>"
world << "<font color=red>Updated [turfs_updated] turfs.</font>"
/datum/controller/lighting/proc/MarkIconUpdate(turf/T)
if(!T.needs_light_update)
icon_updates.Add(T)
T.needs_light_update = 1
/datum/controller/lighting/proc/FlushIconUpdates()
for(var/turf/T in icon_updates)
T.UpdateLight()
T.needs_light_update = 0
icon_updates = list()
/datum/controller/lighting/proc/AddBorder(turf/T)
if(!T.is_border)
light_border.Add(T)
T.is_border = 1
//T.overlays.Add(border)
/datum/controller/lighting/proc/RemoveBorder(turf/T)
if(T.is_border)
light_border.Remove(T)
T.is_border = 0

View File

@@ -1,218 +0,0 @@
/*
Overview:
Procs given to atom and turf by the lighting engine, as well as the lighting overlay object.
Atom Vars:
light - Contains the light object this atom is currently shining with.
Turf Vars:
light_overlay - Contains an object showing the lighting icon over this turf.
lit_value - Stores how brightly lit the turf is.
has_opaque - A cached value updated by CheckForOpaqueObjects()
is_outside - Any turf with this set to true will be considered as bright as space.
needs_light_update - Turf is marked for icon updates when true.
lightNE, lightSE, lightNW, lightSW - Hold the lightpoints on the four corners of this turf. See Lightpoint.dm
lit_by - A list of lights that are lighting this turf.
Atom Procs:
SetLight(intensity, radius)
A more versatile SetLuminosity() that allows independent control of intensity and radius.
Called behind the scenes of SetLuminosity().
SetOpacity(opacity)
Does the same thing as DAL.
Turf Procs:
UpdateLight()
Called by the lighting controller. It is advisable not to call this manually due to the cost of lightpoint/max_value()
AddLight(light/light)
Called by light/Reset() to light this turf with a particular light.
RemoveLight(light/light)
Called by light/Off() to unlight turfs that were lit by it.
ResetValue()
Called when lights are reset or starlight is changed.
ResetCachedValues()
Resets cached values of all four light points. Called by ResetValue().
CheckForOpaqueObjects()
Called by lighting_controller.Initialize(), SetOpacity() or when a turf might change opacity.
Resets the opacity cache and looks for opaque objects. Also responsible for adding and removing borders to space.
*/
#define LIGHTCLAMP(x) ( max(0,min(3,round(x,1))) )
obj/effect/lighting_overlay
//anchored = 1
layer = 9
mouse_opacity = 0
icon = 'icons/effects/ArynLights.dmi'
icon_state = "0000"
invisibility = INVISIBILITY_LIGHTING
atom/var/light/light
turf/var/obj/effect/lighting_overlay/light_overlay
turf/var/lit_value = 0
turf/var/max_brightness = 0
turf/var/has_opaque = 0
turf/var/is_outside = 0
turf/var/is_border = 0
turf/var/needs_light_update = 0
turf/var/lightpoint/lightNE
turf/var/lightpoint/lightNW
turf/var/lightpoint/lightSE
turf/var/lightpoint/lightSW
turf/var/list/lit_by
atom/movable/New()
. = ..()
if(luminosity)
if(!light)
SetLight(luminosity,luminosity)
else
light.Reset()
if(opacity)
if(lighting_ready())
opacity = 0
SetOpacity(1)
atom/movable/Move()
var/o = opacity
if(o) SetOpacity(0)
. = ..()
if(.)
if(o) SetOpacity(1)
if(light)
light.Reset()
if(lighting_ready()) lighting_controller.FlushIconUpdates()
atom/proc/SetLight(intensity, radius)
//if(lights_verbose) world << "SetLight([intensity],[radius])"
if(!intensity)
if(!light || !light.intensity)
//if(lights_verbose) world << "Still off."
return
//if(lights_verbose) world << "Shut off light with [light.lit_turfs.len] turfs lit."
light.Off()
light.intensity = 0
if(lighting_ready()) lighting_controller.FlushIconUpdates()
return
if(!light)
//if(lights_verbose) world << "New light."
light = new(src)
if(light.intensity == intensity)
//if(lights_verbose) world << "Same intensity."
return
light.radius = min(radius,15)
light.intensity = intensity
light.Reset()
if(lighting_ready()) lighting_controller.FlushIconUpdates()
atom/proc/SetOpacity(o)
if(o == opacity) return
opacity = o
var/turf/T = loc
if(isturf(T))
for(var/light/A in T.lit_by)
A.Reset()
lighting_controller.FlushIconUpdates()
turf/proc/UpdateLight()
if(light_overlay)
light_overlay.icon_state = "[lightSE.max_value()][lightSW.max_value()][lightNW.max_value()][lightNE.max_value()]"
turf/proc/AddLight(light/light)
if(is_outside) return
var/brightness = light.CalculateBrightness(src)
if(brightness <= 0) return
if(!lit_by) lit_by = list()
lit_by.Add(light)
lit_by[light] = brightness
if(!has_opaque && lighting_ready())
if(brightness > max_brightness)
lit_value = LIGHTCLAMP(brightness)
max_brightness = brightness
ResetCachedValues()
for(var/turf/T in range(1,src))
lighting_controller.MarkIconUpdate(T)
turf/proc/RemoveLight(light/light)
if(lit_by)
var/brightness = lit_by[light]
lit_by.Remove(light)
if(brightness == max_brightness)
ResetValue()
if(!lit_by.len) lit_by = null
//Only called by ChangeTurf, because it really needs it.
turf/proc/ResetAllLights()
for(var/light/light in lit_by)
light.Reset()
turf/proc/ResetValue()
if(is_outside)
max_brightness = lighting_controller.starlight
lit_value = LIGHTCLAMP(lighting_controller.starlight)
return
CheckForOpaqueObjects()
if(has_opaque)
lit_value = 0
else
the_part_where_I_calculate_brightness()
if(lighting_ready())
the_part_where_I_use_range()
turf/proc
the_part_where_I_calculate_brightness()
max_brightness = 0
for(var/light/light in lit_by)
var/brightness = lit_by[light]//light.CalculateBrightness(src)
if(brightness > max_brightness)
max_brightness = brightness
lit_value = LIGHTCLAMP(max_brightness)
the_part_where_I_use_range()
ResetCachedValues()
for(var/turf/T in range(1,src))
lighting_controller.MarkIconUpdate(T)
turf/proc/ResetCachedValues()
if(lightNE)
lightNE.cached_value = -1
if(lightNW)
lightNW.cached_value = -1
if(lightSE)
lightSE.cached_value = -1
if(lightSW)
lightSW.cached_value = -1
turf/proc/CheckForOpaqueObjects()
has_opaque = opacity
if(!opacity)
for(var/atom/movable/M in contents)
if(M.opacity)
has_opaque = 1
break
#undef LIGHTCLAMP

View File

@@ -1,73 +0,0 @@
/*
Overview:
This object functions similarly to /tg/'s /light. It is responsible for calculating what turfs are lit by it.
Class Vars:
radius - Set by atom/SetLight(). This stores how far out turfs will be lit up.
intensity - Set by atom/SetLight(). Stores the amount of light generated at the center.
lit_turfs - A list of turfs being lit by this light.
atom - The atom this light is attached to.
Class Procs:
Reset()
This is called whenever the light changes, or the underlying atom changes position.
Off()
A quick way to turn off a light. Removes the light from all turfs in lit_turfs.
CalculateBrightness(turf/T)
Returns the brightness that should be displayed by this light on a specific turf.
*/
light/var/radius = 0
light/var/intensity = 0
light/var/list/lit_turfs
light/var/atom/atom
light/New(atom/atom)
src.atom = atom
light/proc/Reset()
//if(atom.lights_verbose) world << "light.Reset()"
Off()
if(intensity > 0)
//if(atom.lights_verbose) world << "Restoring light."
for(var/turf/T in view(get_turf(atom),radius+1))
if(!T.is_outside)
T.AddLight(src)
lit_turfs.Add(T)
//if(atom.lights_verbose) world << "[lit_turfs.len] turfs added."
light/proc/Off()
//if(atom.lights_verbose) world << "light.Off()"
for(var/turf/T in lit_turfs)
T.RemoveLight(src)
lit_turfs = list()
light/proc/CalculateBrightness(turf/T)
var/square = get_square_dist(atom.x,atom.y,atom.z,T.x,T.y,T.z)
if(square > (radius+2)*(radius+2)) return 0
//+2 offset gives an ambient light effect.
var/value = ((radius)/(2*fsqrt(square) + 1)) * intensity - 0.48
/*
lightRadius
---------------- * lightValue - 0.48
2 * distance + 1
The light decreases by twice the distance, starting from the radius.
The + 1 causes the graph to shift to the left one unit so that division by zero is prevented on the source tile.
This is then multiplied by the light value to give the final result.
The -0.48 offset causes the value to be near zero at the radius.
This gives a result which is likely close to the inverse-square law in two dimensions instead of three.
*/
return max(min( value , intensity), 0) //Ensure the value never goes above the maximum light value or below zero.
//return cos(90 * sqrt(square) / max(1,lightRadius)) * lightValue

View File

@@ -1,60 +0,0 @@
/*
Overview:
Perhaps the most cryptic datum in the lighting engine, there are four of these at the corners of every turf.
Any two turfs that share a corner will also have the same lightpoint. Because of the nature of the icons used,
light is shown at the corner of the turf rather than in the middle, necessitating some way to keep track of what
icon state to use.
Class Vars:
x, y, z - The position of the lightpoint. x and y will usually be expressed in terms of 0.5 due to its location on the corner.
NE, NW, SE, SW - The turfs that are in these directions relative to the lightpoint.
cached_value - A cached value of max_value().
Class Procs:
max_value()
The maximum of the light amounts on the four turfs of this light point.
*/
lightpoint
var/x
var/y
var/z
var/turf/NE
var/turf/NW
var/turf/SW
var/turf/SE
var/cached_value = -1
New(x,y,z)
var/turf/T = locate(x+0.5,y+0.5,z)
if(T)
NE = T
T.lightSW = src
T = locate(x-0.5,y+0.5,z)
if(T)
NW = T
T.lightSE = src
T = locate(x-0.5,y-0.5,z)
if(T)
SW = T
T.lightNE = src
T = locate(x+0.5,y-0.5,z)
if(T)
SE = T
T.lightNW = src
proc/max_value()
if(cached_value < 0)
var
valueA = (NW?(NW.lit_value):0)
valueB = (NE?(NE.lit_value):0)
valueC = (SW?(SW.lit_value):0)
valueD = (SE?(SE.lit_value):0)
cached_value = max(valueA,valueB,valueC,valueD)
return cached_value

View File

@@ -1,22 +0,0 @@
/*
Some math procs used by lighting, including ul's fastroot.
*/
var/list/fastroot = list(0, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7)
proc/get_square_dist(Ax,Ay,Az,Bx,By,Bz)
var/X = (Ax - Bx)
var/Y = (Ay - By)
var/Z = (Az - Bz)
return (X * X + Y * Y + Z * Z)
proc/fsqrt(n)
if (n > fastroot.len)
//world << "Adding [n-fastroot.len] entries to root table."
for(var/i = fastroot.len, i <= n, i++)
fastroot += round(sqrt(i))
return fastroot[n + 1]

View File

@@ -1,33 +0,0 @@
var/icon/lighting_dbg = icon('icons/Testing/Zone.dmi', "created")
atom/var/lights_verbose = 0
obj/machinery/light/verb/ShowInfluence()
set src in world
lights_verbose = 1
usr << "<b>[src]</b>"
if(light)
usr << "Intensity: [light.intensity]"
usr << "Radius: [light.radius]"
for(var/turf/T in light.lit_turfs)
T.overlays += lighting_dbg
spawn(50)
for(var/turf/T in light.lit_turfs)
T.overlays -= lighting_dbg
turf/verb/ShowData()
set src in world
usr << "<b>[src]</b>"
usr << "Lit Value: [lit_value]"
usr << "Max Brightness: [max_brightness]"
usr << "Lit By: "
for(var/light/light in lit_by)
usr << " - [light.atom] \[[lit_by[light]]\][(lit_by[light] == max_brightness ? "(MAX)" : "")]"
light.atom.overlays += lighting_dbg
spawn(50)
for(var/light/light in lit_by)
light.atom.overlays -= lighting_dbg

View File

@@ -45,8 +45,7 @@ var/datum/controller/failsafe/Failsafe
MC_defcon = 0
MC_iteration = controller_iteration
//Lighting controller now neither processes nor iterates.
/*if(lighting_controller.processing)
if(lighting_controller.processing)
if(lighting_iteration == lighting_controller.iteration) //master_controller hasn't finished processing in the defined interval
switch(lighting_defcon)
if(0 to 3)
@@ -61,7 +60,7 @@ var/datum/controller/failsafe/Failsafe
lighting_defcon = 0
else
lighting_defcon = 0
lighting_iteration = lighting_controller.iteration*/
lighting_iteration = lighting_controller.iteration
else
MC_defcon = 0
lighting_defcon = 0

View File

@@ -78,7 +78,6 @@ datum/controller/game_controller/proc/setup()
if(ticker)
ticker.pregame()
new/datum/controller/lighting()
lighting_controller.Initialize()

View File

@@ -19,7 +19,7 @@
feedback_add_details("admin_verb","RFailsafe")
if("Lighting")
new /datum/controller/lighting()
//lighting_controller.process()
lighting_controller.process()
feedback_add_details("admin_verb","RLighting")
if("Supply Shuttle")
supply_shuttle.process()

View File

@@ -26,16 +26,20 @@
// lighting_state = 4
//has_gravity = 0 // Space has gravity. Because.. because.
if(!requires_power)
if(requires_power)
luminosity = 0
else
power_light = 0 //rastaf0
power_equip = 0 //rastaf0
power_environ = 0 //rastaf0
luminosity = 1
lighting_use_dynamic = 0
..()
// spawn(15)
power_change() // all machines set to current power level, also updates lighting icon
InitializeLighting()
/area/proc/poweralert(var/state, var/obj/source as obj)

View File

@@ -148,7 +148,7 @@ var/global/datum/controller/gameticker/ticker
supply_shuttle.process() //Start the supply shuttle regenerating points -- TLE
master_controller.process() //Start master_controller.process()
//lighting_controller.process() //Start processing DynamicAreaLighting updates
lighting_controller.process() //Start processing DynamicAreaLighting updates
for(var/obj/multiz/ladder/L in world) L.connect() //Lazy hackfix for ladders. TODO: move this to an actual controller. ~ Z

View File

@@ -133,6 +133,9 @@ move an amendment</a> to the drawing.</p>
move_turfs_to_area(turfs, A)
A.always_unpowered = 0
for(var/turf/T in A.contents)
T.lighting_changed = 1
lighting_controller.changed_turfs += T
spawn(5)
//ma = A.master ? "[A.master]" : "(null)"

View File

@@ -195,8 +195,6 @@
if(L)
del L
var/turf_light_data/old_lights = new
//Creates a new turf
/turf/proc/ChangeTurf(var/turf/N)
if (!N)
@@ -216,12 +214,10 @@ var/turf_light_data/old_lights = new
return W
///// Z-Level Stuff
var/old_lumcount = lighting_lumcount - initial(lighting_lumcount)
//world << "Replacing [src.type] with [N]"
var/old_opacity = opacity
old_lights.copy_from(src)
if(connections) connections.erase_all()
if(istype(src,/turf/simulated))
@@ -242,12 +238,10 @@ var/turf_light_data/old_lights = new
var/turf/simulated/W = new N( locate(src.x, src.y, src.z) )
//W.Assimilate_Air()
old_lights.copy_to(W)
W.ResetAllLights()
if(old_opacity)
W.opacity = 1
W.SetOpacity(0)
W.lighting_lumcount += old_lumcount
if(old_lumcount != W.lighting_lumcount)
W.lighting_changed = 1
lighting_controller.changed_turfs += W
if (istype(W,/turf/simulated/floor))
W.RemoveLattice()
@@ -265,9 +259,10 @@ var/turf_light_data/old_lights = new
// zone.SetStatus(ZONE_ACTIVE)
var/turf/W = new N( locate(src.x, src.y, src.z) )
old_lights.copy_to(W)
W.ResetAllLights()
W.lighting_lumcount += old_lumcount
if(old_lumcount != W.lighting_lumcount)
W.lighting_changed = 1
lighting_controller.changed_turfs += W
if(air_master)
air_master.mark_for_update(src)

View File

@@ -900,7 +900,7 @@
var/turf/T = loc
var/area/A = T.loc
if(A)
if(A.lighting_use_dynamic) light_amount = min(10,T.lit_value) - 5 //hardcapped so it's not abused by having a ton of flashlights
if(A.lighting_use_dynamic) light_amount = min(10,T.lighting_lumcount) - 5 //hardcapped so it's not abused by having a ton of flashlights
else light_amount = 5
nutrition += light_amount
traumatic_shock -= light_amount
@@ -920,7 +920,7 @@
var/turf/T = loc
var/area/A = T.loc
if(A)
if(A.lighting_use_dynamic) light_amount = T.lit_value
if(A.lighting_use_dynamic) light_amount = T.lighting_lumcount
else light_amount = 10
if(light_amount > 2) //if there's enough light, start dying
take_overall_damage(1,1)
@@ -1404,7 +1404,7 @@
//0.1% chance of playing a scary sound to someone who's in complete darkness
if(isturf(loc) && rand(1,1000) == 1)
var/turf/currentTurf = loc
if(!currentTurf.lit_value)
if(!currentTurf.lighting_lumcount)
playsound_local(src,pick(scarySounds),50, 1, -1)
proc/handle_virus_updates()

View File

@@ -447,7 +447,7 @@
var/turf/T = loc
var/area/A = T.loc
if(A)
if(A.lighting_use_dynamic) light_amount = min(10,T.lit_value) - 5 //hardcapped so it's not abused by having a ton of flashlights
if(A.lighting_use_dynamic) light_amount = min(10,T.lighting_lumcount) - 5 //hardcapped so it's not abused by having a ton of flashlights
else light_amount = 5
nutrition += light_amount

View File

@@ -76,14 +76,6 @@ should be listed in the changelog upon commit though. Thanks. -->
</ul>
</div>
<div class='commit sansserif'>
<h2 class='date'>24 February 2014</h2>
<h3 class='author'>Aryn updated:</h3>
<ul class='changes bgimages16'>
<li class='rscadd'>New lighting system! Credit goes to Forum_account for the code that generated the icons it uses.</li>
</ul>
</div>
<div class='commit sansserif'>
<h2 class='date'>19 February 2014</h2>
<h3 class='author'>Aryn updated:</h3>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB