/
PortalColorHandler.java
162 lines (139 loc) · 5.78 KB
/
PortalColorHandler.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package knightminer.inspirations.tweaks.client;
import knightminer.inspirations.Inspirations;
import knightminer.inspirations.library.Util;
import net.minecraft.block.Block;
import net.minecraft.block.BlockStainedGlass;
import net.minecraft.block.BlockStainedGlassPane;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.renderer.color.IBlockColor;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.ChunkCache;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.IWorldEventListener;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import java.util.HashSet;
import java.util.Set;
/**
* Logic to handle getting the color for a portal from stained glass below
*/
public class PortalColorHandler implements IBlockColor, IWorldEventListener {
private static final int DEFAULT_COLOR = 0x9928FF;
private static final Set<Block> BEACON_COLOR_BLACKLIST = new HashSet<>();
public static final PortalColorHandler INSTANCE = new PortalColorHandler();
private PortalColorHandler() {}
@Override
public int colorMultiplier(IBlockState state, @Nullable IBlockAccess world, @Nullable BlockPos pos, int tintIndex) {
if (world == null || pos == null) {
return DEFAULT_COLOR;
}
// iterate down until the first non-portal block
// can skip every other block as it takes at least 2 from a portal to below a portal
pos = pos.down();
while(world.getBlockState(pos).getBlock() == Blocks.PORTAL) {
pos = pos.down();
}
return getColorValue(world, pos.down());
}
/**
* Gets the color for a block in the world, uses the same logic as beacon beam colors
* @param access Block access
* @param pos Block pos
* @return
*/
private static int getColorValue(IBlockAccess access, BlockPos pos) {
IBlockState state = access.getBlockState(pos);
Block block = state.getBlock();
// stained glass
if (block == Blocks.STAINED_GLASS) {
return state.getValue(BlockStainedGlass.COLOR).colorValue;
}
if (block == Blocks.STAINED_GLASS_PANE) {
return state.getValue(BlockStainedGlassPane.COLOR).colorValue;
}
// beacon color fallback
if (!BEACON_COLOR_BLACKLIST.contains(block)) {
World world = null;
if (access instanceof World) {
world = (World)access;
} else if (access instanceof ChunkCache) {
world = ((ChunkCache)access).world;
}
if (world != null) {
try {
float[] color = block.getBeaconColorMultiplier(state, world, pos, pos);
if (color != null && color.length == 3) {
return Util.getColorInteger(color);
}
} catch (ClassCastException e) {
Inspirations.log.error("Error getting beacon color for block", e);
BEACON_COLOR_BLACKLIST.add(block);
}
}
}
return DEFAULT_COLOR;
}
@Override
public void notifyBlockUpdate(World world, BlockPos pos, IBlockState oldState, IBlockState newState, int flags) {
// during world load viewFrustum is null, but its used by notifyBlockUpdate without safety checks
if (Minecraft.getMinecraft().renderGlobal.viewFrustum == null) {
return;
}
pos = pos.up(2);
if (world.getBlockState(pos).getBlock() == Blocks.PORTAL) {
updatePortal(world, pos, flags);
}
}
private void updatePortal(World world, BlockPos pos, int flags) {
RenderGlobal render = Minecraft.getMinecraft().renderGlobal;
int relative = pos.getY() & 15;
// at Y=2, the color block is in the same chunk as the portal
// at Y=1, the color block is next to the border so it updates this chunk
// ay Y=0, this chunk does not update
if(relative == 0) {
render.notifyBlockUpdate(null, pos, null, null, flags);
}
// update the bottom of the chunk above, lowest position that might have the portal
BlockPos update = pos.up(16 - relative);
if(world.getBlockState(update).getBlock() == Blocks.PORTAL) {
render.notifyBlockUpdate(null, update, null, null, flags);
// portals are at most 21 blocks tall, only possible at positions 12 and above
if(relative >= 12) {
update = update.up(16);
if(world.getBlockState(update).getBlock() == Blocks.PORTAL) {
render.notifyBlockUpdate(null, update, null, null, flags);
}
}
}
}
/* Necessary methods for the interface */
@Override
public void notifyLightSet(BlockPos pos) {}
@Override
public void markBlockRangeForRenderUpdate(int x1, int y1, int z1, int x2, int y2, int z2) { }
@Override
public void playSoundToAllNearExcept(@Nullable EntityPlayer player, SoundEvent soundIn, SoundCategory category, double x, double y, double z, float volume, float pitch) { }
@Override
public void playRecord(SoundEvent soundIn, BlockPos pos) { }
@Override
public void spawnParticle(int particleID, boolean ignoreRange, double xCoord, double yCoord, double zCoord, double xSpeed, double ySpeed, double zSpeed, int... parameters) { }
@Override
public void spawnParticle(int id, boolean ignoreRange, boolean b, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed, int... parameters) { }
@Override
public void onEntityAdded(Entity entityIn) {}
@Override
public void onEntityRemoved(Entity entityIn) {}
@Override
public void broadcastSound(int soundID, BlockPos pos, int data) {}
@Override
public void playEvent(EntityPlayer player, int type, BlockPos blockPosIn, int data) {}
@Override
public void sendBlockBreakProgress(int breakerId, BlockPos pos, int progress) {}
}