mirror of
https://github.com/HbmMods/Hbm-s-Nuclear-Tech-GIT.git
synced 2026-01-25 10:32:49 +00:00
reactor balancing, unloading, radiation decal toggle and gui desc
This commit is contained in:
parent
9c98d96f4c
commit
c5d4b80886
@ -8,12 +8,16 @@ import cpw.mods.fml.relauncher.SideOnly;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.client.renderer.texture.IIconRegister;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class BlockReactor extends Block {
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
private IIcon iconTop;
|
||||
@SideOnly(Side.CLIENT)
|
||||
private IIcon iconAlt;
|
||||
|
||||
public BlockReactor(Material p_i45394_1_) {
|
||||
super(p_i45394_1_);
|
||||
@ -22,6 +26,9 @@ public class BlockReactor extends Block {
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void registerBlockIcons(IIconRegister iconRegister) {
|
||||
|
||||
this.iconAlt = iconRegister.registerIcon(RefStrings.MODID + ":code");
|
||||
|
||||
if(this == ModBlocks.reactor_conductor)
|
||||
{
|
||||
this.iconTop = iconRegister.registerIcon(RefStrings.MODID + ":reactor_conductor_top");
|
||||
@ -36,6 +43,7 @@ public class BlockReactor extends Block {
|
||||
{
|
||||
this.iconTop = iconRegister.registerIcon(RefStrings.MODID + ":reactor_element_top");
|
||||
this.blockIcon = iconRegister.registerIcon(RefStrings.MODID + ":reactor_element_side");
|
||||
this.iconAlt = iconRegister.registerIcon(RefStrings.MODID + ":reactor_element_base");
|
||||
}
|
||||
if(this == ModBlocks.fusion_conductor)
|
||||
{
|
||||
@ -97,7 +105,32 @@ public class BlockReactor extends Block {
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public IIcon getIcon(int side, int metadata) {
|
||||
|
||||
if(this == ModBlocks.reactor_element && metadata == 1)
|
||||
return side == 1 ? this.iconTop : (side == 0 ? this.iconTop : this.iconAlt);
|
||||
|
||||
return side == 1 ? this.iconTop : (side == 0 ? this.iconTop : this.blockIcon);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float hitX, float hitY, float hitZ) {
|
||||
|
||||
if(this != ModBlocks.reactor_element)
|
||||
return super.onBlockActivated(world, x, y, z, player, side, hitX, hitY, hitZ);
|
||||
|
||||
if(player.isSneaking())
|
||||
{
|
||||
if(world.getBlockMetadata(x, y, z) == 0) {
|
||||
world.setBlockMetadataWithNotify(x, y, z, 1, 3);
|
||||
} else {
|
||||
world.setBlockMetadataWithNotify(x, y, z, 0, 3);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -34,8 +34,11 @@ public class GUIReactorMultiblock extends GuiInfoContainer {
|
||||
public void drawScreen(int mouseX, int mouseY, float f) {
|
||||
super.drawScreen(mouseX, mouseY, f);
|
||||
|
||||
diFurnace.tanks[0].renderTankInfo(this, mouseX, mouseY, guiLeft + 8, guiTop + 70 - 52, 16, 52);
|
||||
diFurnace.tanks[1].renderTankInfo(this, mouseX, mouseY, guiLeft + 26, guiTop + 70 - 52, 16, 52);
|
||||
diFurnace.tanks[0].renderTankInfo(this, mouseX, mouseY, guiLeft + 8, guiTop + 88 - 52, 16, 52);
|
||||
diFurnace.tanks[1].renderTankInfo(this, mouseX, mouseY, guiLeft + 26, guiTop + 88 - 52, 16, 52);
|
||||
diFurnace.tanks[2].renderTankInfo(this, mouseX, mouseY, guiLeft + 80, guiTop + 108, 88, 4);
|
||||
this.drawCustomInfo(this, mouseX, mouseY, guiLeft + 80, guiTop + 114, 88, 4, new String[] { "Hull Temperature:", " " + Math.round((diFurnace.hullHeat) * 0.00001 * 980 + 20) + "°C" });
|
||||
this.drawCustomInfo(this, mouseX, mouseY, guiLeft + 80, guiTop + 120, 88, 4, new String[] { "Core Temperature:", " " + Math.round((diFurnace.coreHeat) * 0.00002 * 980 + 20) + "°C" });
|
||||
|
||||
this.drawCustomInfo(this, mouseX, mouseY, guiLeft + 115, guiTop + 17, 18, 90, new String[] { "Operating Level: " + diFurnace.rods + "%" });
|
||||
|
||||
@ -64,6 +67,18 @@ public class GUIReactorMultiblock extends GuiInfoContainer {
|
||||
|
||||
String[] text0 = new String[] { diFurnace.rods > 0 ? "Reactor is ON" : "Reactor is OFF"};
|
||||
this.drawCustomInfoStat(mouseX, mouseY, guiLeft + 52, guiTop + 53, 18, 18, mouseX, mouseY, text0);
|
||||
|
||||
String s = "0";
|
||||
|
||||
switch(diFurnace.tanks[2].getTankType()) {
|
||||
case STEAM: s = "1x"; break;
|
||||
case HOTSTEAM:s = "10x"; break;
|
||||
case SUPERHOTSTEAM: s = "100x"; break;
|
||||
}
|
||||
|
||||
String[] text4 = new String[] { "Steam compression switch",
|
||||
"Current compression level: " + s};
|
||||
this.drawCustomInfoStat(mouseX, mouseY, guiLeft + 63, guiTop + 107, 14, 18, mouseX, mouseY, text4);
|
||||
}
|
||||
|
||||
@SuppressWarnings("incomplete-switch")
|
||||
|
||||
@ -1600,41 +1600,41 @@ public class MainRegistry
|
||||
HazmatRegistry.instance.registerHazmat(ModItems.euphemium_legs, 9F);
|
||||
HazmatRegistry.instance.registerHazmat(ModItems.euphemium_boots, 3F);
|
||||
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(1, ReactorFuelType.URANIUM, false, ModItems.nugget_uranium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(9, ReactorFuelType.URANIUM, false, ModItems.ingot_uranium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(6, ReactorFuelType.URANIUM, false, ModItems.rod_uranium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(12, ReactorFuelType.URANIUM, false, ModItems.rod_dual_uranium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(24, ReactorFuelType.URANIUM, false, ModItems.rod_quad_uranium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(6, ReactorFuelType.URANIUM, true, ModItems.rod_uranium_fuel_depleted);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(12, ReactorFuelType.URANIUM, true, ModItems.rod_dual_uranium_fuel_depleted);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(24, ReactorFuelType.URANIUM, true, ModItems.rod_quad_uranium_fuel_depleted);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(1, ReactorFuelType.URANIUM, ModItems.nugget_uranium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(9, ReactorFuelType.URANIUM, ModItems.ingot_uranium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(6, ReactorFuelType.URANIUM, ModItems.rod_uranium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(12, ReactorFuelType.URANIUM, ModItems.rod_dual_uranium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(24, ReactorFuelType.URANIUM, ModItems.rod_quad_uranium_fuel);
|
||||
TileEntityMachineReactorLarge.registerWasteEntry(6, ReactorFuelType.URANIUM, ModItems.rod_empty, ModItems.rod_uranium_fuel_depleted);
|
||||
TileEntityMachineReactorLarge.registerWasteEntry(12, ReactorFuelType.URANIUM, ModItems.rod_dual_empty, ModItems.rod_dual_uranium_fuel_depleted);
|
||||
TileEntityMachineReactorLarge.registerWasteEntry(24, ReactorFuelType.URANIUM, ModItems.rod_quad_empty, ModItems.rod_quad_uranium_fuel_depleted);
|
||||
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(1, ReactorFuelType.PLUTONIUM, false, ModItems.nugget_plutonium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(9, ReactorFuelType.PLUTONIUM, false, ModItems.ingot_plutonium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(6, ReactorFuelType.PLUTONIUM, false, ModItems.rod_plutonium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(12, ReactorFuelType.PLUTONIUM, false, ModItems.rod_dual_plutonium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(24, ReactorFuelType.PLUTONIUM, false, ModItems.rod_quad_plutonium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(6, ReactorFuelType.PLUTONIUM, true, ModItems.rod_plutonium_fuel_depleted);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(12, ReactorFuelType.PLUTONIUM, true, ModItems.rod_dual_plutonium_fuel_depleted);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(24, ReactorFuelType.PLUTONIUM, true, ModItems.rod_quad_plutonium_fuel_depleted);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(1, ReactorFuelType.PLUTONIUM, ModItems.nugget_plutonium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(9, ReactorFuelType.PLUTONIUM, ModItems.ingot_plutonium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(6, ReactorFuelType.PLUTONIUM, ModItems.rod_plutonium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(12, ReactorFuelType.PLUTONIUM, ModItems.rod_dual_plutonium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(24, ReactorFuelType.PLUTONIUM, ModItems.rod_quad_plutonium_fuel);
|
||||
TileEntityMachineReactorLarge.registerWasteEntry(6, ReactorFuelType.PLUTONIUM, ModItems.rod_empty, ModItems.rod_plutonium_fuel_depleted);
|
||||
TileEntityMachineReactorLarge.registerWasteEntry(12, ReactorFuelType.PLUTONIUM, ModItems.rod_dual_empty, ModItems.rod_dual_plutonium_fuel_depleted);
|
||||
TileEntityMachineReactorLarge.registerWasteEntry(24, ReactorFuelType.PLUTONIUM, ModItems.rod_quad_empty, ModItems.rod_quad_plutonium_fuel_depleted);
|
||||
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(1, ReactorFuelType.MOX, false, ModItems.nugget_mox_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(9, ReactorFuelType.MOX, false, ModItems.ingot_mox_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(6, ReactorFuelType.MOX, false, ModItems.rod_mox_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(12, ReactorFuelType.MOX, false, ModItems.rod_dual_mox_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(24, ReactorFuelType.MOX, false, ModItems.rod_quad_mox_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(6, ReactorFuelType.MOX, true, ModItems.rod_mox_fuel_depleted);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(12, ReactorFuelType.MOX, true, ModItems.rod_dual_mox_fuel_depleted);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(24, ReactorFuelType.MOX, true, ModItems.rod_quad_mox_fuel_depleted);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(1, ReactorFuelType.MOX, ModItems.nugget_mox_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(9, ReactorFuelType.MOX, ModItems.ingot_mox_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(6, ReactorFuelType.MOX, ModItems.rod_mox_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(12, ReactorFuelType.MOX, ModItems.rod_dual_mox_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(24, ReactorFuelType.MOX, ModItems.rod_quad_mox_fuel);
|
||||
TileEntityMachineReactorLarge.registerWasteEntry(6, ReactorFuelType.MOX, ModItems.rod_empty, ModItems.rod_mox_fuel_depleted);
|
||||
TileEntityMachineReactorLarge.registerWasteEntry(12, ReactorFuelType.MOX, ModItems.rod_dual_empty, ModItems.rod_dual_mox_fuel_depleted);
|
||||
TileEntityMachineReactorLarge.registerWasteEntry(24, ReactorFuelType.MOX, ModItems.rod_quad_empty, ModItems.rod_quad_mox_fuel_depleted);
|
||||
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(1, ReactorFuelType.SCHRABIDIUM, false, ModItems.nugget_schrabidium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(9, ReactorFuelType.SCHRABIDIUM, false, ModItems.ingot_schrabidium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(6, ReactorFuelType.SCHRABIDIUM, false, ModItems.rod_schrabidium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(12, ReactorFuelType.SCHRABIDIUM, false, ModItems.rod_dual_schrabidium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(24, ReactorFuelType.SCHRABIDIUM, false, ModItems.rod_quad_schrabidium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(6, ReactorFuelType.SCHRABIDIUM, true, ModItems.rod_schrabidium_fuel_depleted);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(12, ReactorFuelType.SCHRABIDIUM, true, ModItems.rod_dual_schrabidium_fuel_depleted);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(24, ReactorFuelType.SCHRABIDIUM, true, ModItems.rod_quad_schrabidium_fuel_depleted);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(1, ReactorFuelType.SCHRABIDIUM, ModItems.nugget_schrabidium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(9, ReactorFuelType.SCHRABIDIUM, ModItems.ingot_schrabidium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(6, ReactorFuelType.SCHRABIDIUM, ModItems.rod_schrabidium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(12, ReactorFuelType.SCHRABIDIUM, ModItems.rod_dual_schrabidium_fuel);
|
||||
TileEntityMachineReactorLarge.registerFuelEntry(24, ReactorFuelType.SCHRABIDIUM, ModItems.rod_quad_schrabidium_fuel);
|
||||
TileEntityMachineReactorLarge.registerWasteEntry(6, ReactorFuelType.SCHRABIDIUM, ModItems.rod_empty, ModItems.rod_schrabidium_fuel_depleted);
|
||||
TileEntityMachineReactorLarge.registerWasteEntry(12, ReactorFuelType.SCHRABIDIUM, ModItems.rod_dual_empty, ModItems.rod_dual_schrabidium_fuel_depleted);
|
||||
TileEntityMachineReactorLarge.registerWasteEntry(24, ReactorFuelType.SCHRABIDIUM, ModItems.rod_quad_empty, ModItems.rod_quad_schrabidium_fuel_depleted);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
|
||||
@ -322,7 +322,7 @@ public class TileEntityMachineReactorLarge extends TileEntity
|
||||
fuel -= consumption;
|
||||
waste += consumption;
|
||||
|
||||
int heat = consumption * type.heat / fuelMult;
|
||||
int heat = (consumption / size) * type.heat / fuelMult;
|
||||
|
||||
this.coreHeat += heat;
|
||||
|
||||
@ -361,6 +361,7 @@ public class TileEntityMachineReactorLarge extends TileEntity
|
||||
tanks[0].loadTank(0, 1, slots);
|
||||
tanks[1].loadTank(2, 3, slots);
|
||||
|
||||
//Change fuel type if empty
|
||||
if(fuel == 0) {
|
||||
|
||||
if(slots[4] != null && !getFuelType(slots[4].getItem()).toString().equals(ReactorFuelType.UNKNOWN.toString())) {
|
||||
@ -370,6 +371,7 @@ public class TileEntityMachineReactorLarge extends TileEntity
|
||||
}
|
||||
}
|
||||
|
||||
//Load fuel
|
||||
if(slots[4] != null && getFuelContent(slots[4].getItem(), type) > 0) {
|
||||
|
||||
int cont = getFuelContent(slots[4].getItem(), type) * fuelMult;
|
||||
@ -402,6 +404,32 @@ public class TileEntityMachineReactorLarge extends TileEntity
|
||||
}
|
||||
}
|
||||
|
||||
//Unload waste
|
||||
if(slots[6] != null && getWasteAbsorbed(slots[6].getItem(), type) > 0) {
|
||||
|
||||
int absorbed = getWasteAbsorbed(slots[6].getItem(), type) * fuelMult;
|
||||
|
||||
if(absorbed <= waste) {
|
||||
|
||||
if(slots[7] == null) {
|
||||
|
||||
waste -= absorbed;
|
||||
slots[7] = new ItemStack(getWaste(slots[6].getItem(), type));
|
||||
slots[6].stackSize--;
|
||||
|
||||
} else if(slots[7] != null && slots[7].getItem() == getWaste(slots[6].getItem(), type) && slots[7].stackSize < slots[7].getMaxStackSize()) {
|
||||
|
||||
waste -= absorbed;
|
||||
slots[7].stackSize++;
|
||||
slots[6].stackSize--;
|
||||
}
|
||||
|
||||
if(slots[6].stackSize == 0)
|
||||
slots[6] = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(rods > 0)
|
||||
generate();
|
||||
|
||||
@ -453,7 +481,10 @@ public class TileEntityMachineReactorLarge extends TileEntity
|
||||
|
||||
//function of SHS produced per tick
|
||||
//maxes out at heat% * tank capacity / 20
|
||||
double steam = (((double)hullHeat / (double)maxHullHeat) * ((double)tanks[2].getMaxFill() / 50D)) * size;
|
||||
|
||||
double statSteMaFiFiLe = 8000;
|
||||
|
||||
double steam = (((double)hullHeat / (double)maxHullHeat) * (/*(double)tanks[2].getMaxFill()*/statSteMaFiFiLe / 50D)) * size;
|
||||
|
||||
double water = steam;
|
||||
|
||||
@ -633,27 +664,47 @@ public class TileEntityMachineReactorLarge extends TileEntity
|
||||
|
||||
int value;
|
||||
ReactorFuelType type;
|
||||
boolean isWaste;
|
||||
Item item;
|
||||
|
||||
public ReactorFuelEntry(int value, ReactorFuelType type, boolean isWaste, Item item) {
|
||||
public ReactorFuelEntry(int value, ReactorFuelType type, Item item) {
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
this.isWaste = isWaste;
|
||||
this.item = item;
|
||||
}
|
||||
}
|
||||
|
||||
static List<ReactorFuelEntry> entries = new ArrayList();
|
||||
static class ReactorWasteEntry {
|
||||
|
||||
public static void registerFuelEntry(int nuggets, ReactorFuelType type, boolean isWaste, Item fuel) {
|
||||
entries.add(new ReactorFuelEntry(nuggets, type, isWaste, fuel));
|
||||
int value;
|
||||
ReactorFuelType type;
|
||||
Item in;
|
||||
Item out;
|
||||
|
||||
public ReactorWasteEntry(int value, ReactorFuelType type, Item in, Item out) {
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
this.in = in;
|
||||
this.out = out;
|
||||
}
|
||||
}
|
||||
|
||||
static List<ReactorFuelEntry> fuels = new ArrayList();
|
||||
static List<ReactorWasteEntry> wastes = new ArrayList();
|
||||
|
||||
public static void registerFuelEntry(int nuggets, ReactorFuelType type, Item fuel) {
|
||||
|
||||
fuels.add(new ReactorFuelEntry(nuggets, type, fuel));
|
||||
}
|
||||
|
||||
public static void registerWasteEntry(int nuggets, ReactorFuelType type, Item in, Item out) {
|
||||
|
||||
wastes.add(new ReactorWasteEntry(nuggets, type, in, out));
|
||||
}
|
||||
|
||||
public static int getFuelContent(Item item, ReactorFuelType type) {
|
||||
|
||||
for(ReactorFuelEntry ent : entries) {
|
||||
if(!ent.isWaste && ent.item == item && type.toString().equals(ent.type.toString()))
|
||||
for(ReactorFuelEntry ent : fuels) {
|
||||
if(ent.item == item && type.toString().equals(ent.type.toString()))
|
||||
return ent.value;
|
||||
}
|
||||
|
||||
@ -662,11 +713,31 @@ public class TileEntityMachineReactorLarge extends TileEntity
|
||||
|
||||
public static ReactorFuelType getFuelType(Item item) {
|
||||
|
||||
for(ReactorFuelEntry ent : entries) {
|
||||
if(!ent.isWaste && ent.item == item)
|
||||
for(ReactorFuelEntry ent : fuels) {
|
||||
if(ent.item == item)
|
||||
return ent.type;
|
||||
}
|
||||
|
||||
return ReactorFuelType.UNKNOWN;
|
||||
}
|
||||
|
||||
public static Item getWaste(Item item, ReactorFuelType type) {
|
||||
|
||||
for(ReactorWasteEntry ent : wastes) {
|
||||
if(ent.in == item && type.toString().equals(ent.type.toString()))
|
||||
return ent.out;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int getWasteAbsorbed(Item item, ReactorFuelType type) {
|
||||
|
||||
for(ReactorWasteEntry ent : wastes) {
|
||||
if(ent.in == item && type.toString().equals(ent.type.toString()))
|
||||
return ent.value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user