-
Notifications
You must be signed in to change notification settings - Fork 275
/
AtmosphereHandler.java
451 lines (400 loc) · 16.7 KB
/
AtmosphereHandler.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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
package zmaster587.advancedRocketry.atmosphere;
import net.minecraft.block.Blocks;
import net.minecraft.block.FlowingFluidBlock;
import net.minecraft.block.material.Material;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.DamageSource;
import net.minecraft.util.Direction;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerChangedDimensionEvent;
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fluids.IFluidBlock;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import zmaster587.advancedRocketry.api.AreaBlob;
import zmaster587.advancedRocketry.api.ARConfiguration;
import zmaster587.advancedRocketry.api.IAtmosphere;
import zmaster587.advancedRocketry.api.event.AtmosphereEvent;
import zmaster587.advancedRocketry.api.util.IBlobHandler;
import zmaster587.advancedRocketry.dimension.DimensionManager;
import zmaster587.advancedRocketry.dimension.DimensionProperties;
import zmaster587.advancedRocketry.network.PacketAtmSync;
import zmaster587.advancedRocketry.util.AtmosphereBlob;
import zmaster587.libVulpes.network.PacketHandler;
import zmaster587.libVulpes.util.HashedBlockPosition;
import zmaster587.libVulpes.util.ZUtils;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
public class AtmosphereHandler {
public static final DamageSource vacuumDamage = new DamageSource("vacuum").setDamageBypassesArmor().setDamageIsAbsolute();
public static final DamageSource lowOxygenDamage = new DamageSource("lowoxygen").setDamageBypassesArmor().setDamageIsAbsolute();
public static final DamageSource heatDamage = new DamageSource("heat").setDamageBypassesArmor().setDamageIsAbsolute();
public static final DamageSource oxygenToxicityDamage = new DamageSource("oxygentoxicity").setDamageBypassesArmor().setDamageIsAbsolute();
public static long lastSuffocationTime = Integer.MIN_VALUE;
private static final int MAX_BLOB_RADIUS = ((ARConfiguration.getCurrentConfig().atmosphereHandleBitMask.get() & 1) == 1) ? 256 : ARConfiguration.getCurrentConfig().oxygenVentSize.get();
private static final HashMap<ResourceLocation, AtmosphereHandler> dimensionOxygen = new HashMap<>();
private static final HashMap<PlayerEntity, IAtmosphere> prevAtmosphere = new HashMap<>();
private final HashMap<IBlobHandler,AreaBlob> blobs;
ResourceLocation dimId;
//Stores current Atm on the CLIENT
public static IAtmosphere currentAtm;
public static int currentPressure;
/**
* Registers the Atmosphere handler for the dimension given
* @param world the world to register the atmosphere handler for
*/
public static void registerWorld(World world) {
ResourceLocation dimId = ZUtils.getDimensionIdentifier(world);
//If O2 is allowed and
DimensionProperties dimProp = DimensionManager.getInstance().getDimensionProperties(dimId);
if(ARConfiguration.getCurrentConfig().enableOxygen.get() && dimProp.hasSurface() && (dimId != ARConfiguration.getCurrentConfig().MoonId || dimProp.isNativeDimension)) {
dimensionOxygen.put(dimId, new AtmosphereHandler(dimId));
MinecraftForge.EVENT_BUS.register(dimensionOxygen.get(dimId));
}
}
/**
* Unregisters the Atmosphere handler for the world given
* @param world the world to unregister the atmosphere handler for
*/
public static void unregisterWorld(World world) {
AtmosphereHandler handler = dimensionOxygen.remove(ZUtils.getDimensionIdentifier(world));
if(ARConfiguration.getCurrentConfig().enableOxygen.get() && handler != null) {
MinecraftForge.EVENT_BUS.unregister(handler);
}
}
private AtmosphereHandler(ResourceLocation dimId) {
this.dimId = dimId;
blobs = new HashMap<>();
}
@SubscribeEvent
public void onTick(LivingUpdateEvent event) {
Entity entity = event.getEntity();
if(!entity.world.isRemote && ZUtils.getDimensionIdentifier(entity.world) == this.dimId) {
IAtmosphere atmosType = getAtmosphereType(entity);
if(entity instanceof PlayerEntity && atmosType != prevAtmosphere.get(entity)) {
PacketHandler.sendToPlayer(new PacketAtmSync(atmosType.getUnlocalizedName(), getAtmospherePressure(entity)), (PlayerEntity)entity);
prevAtmosphere.put((PlayerEntity)entity, atmosType);
}
if(atmosType.canTick() && !(event.getEntityLiving().isInLava() || event.getEntityLiving().isInWater()) ) {
AtmosphereEvent event2 = new AtmosphereEvent.AtmosphereTickEvent(entity, atmosType);
MinecraftForge.EVENT_BUS.post(event2);
if(!event2.isCanceled() && !atmosType.isImmune(event.getEntity().getClass()))
atmosType.onTick(event.getEntityLiving());
}
}
}
@SubscribeEvent
public void onPlayerChangeDim(PlayerChangedDimensionEvent event) {
prevAtmosphere.remove(event.getPlayer());
}
@SubscribeEvent
public void onPlayerLogoutEvent(PlayerLoggedOutEvent event) {
prevAtmosphere.remove(event.getPlayer());
}
private void onBlockRemove(HashedBlockPosition pos) {
List<AreaBlob> blobs = getBlobWithinRadius(pos, MAX_BLOB_RADIUS);
for(AreaBlob blob : blobs) {
//Make sure that a block can actually be attached to the blob
for(Direction dir : Direction.values())
if(blob.contains(pos.getPositionAtOffset(dir))) {
blob.addBlock(pos, blobs);
break;
}
}
}
/**
* @return true if the dimension has an AtmosphereHandler Object associated with it
*/
public static boolean hasAtmosphereHandler(ResourceLocation dimId) {
return dimensionOxygen.containsKey(dimId);
}
public static boolean hasAtmosphereHandler(World dimId) {
return dimensionOxygen.containsKey(ZUtils.getDimensionIdentifier(dimId));
}
//Called from setBlock in World.class
public static void onBlockChange(@Nonnull World world, @Nonnull BlockPos bpos) {
if(ARConfiguration.getCurrentConfig().enableOxygen.get() && !world.isRemote && world.isBlockLoaded(new BlockPos(bpos))) {
HashedBlockPosition pos = new HashedBlockPosition(bpos);
AtmosphereHandler handler = getOxygenHandler(ZUtils.getDimensionIdentifier(world));
//Bonus chests cause world gen to begin before loading the world
//Because atmosphere handlers are created at world load time
//there is a possibility handler can be null here
if(handler == null)
return; //WTF
//Block handling for what should and shouldn't exist or what should be on fire
//Things should be on fire
if (handler.getAtmosphereType(bpos) == AtmosphereType.SUPERHEATED) {
if(world.getBlockState(bpos).getMaterial().isFlammable()) {
world.setBlockState(bpos, Blocks.FIRE.getDefaultState());
} else if (world.getBlockState(bpos).getMaterial() == Material.WEB) {
world.setBlockState(bpos, Blocks.FIRE.getDefaultState());
} else if (world.getBlockState(bpos).getMaterial() == Material.GOURD) {
world.setBlockState(bpos, Blocks.FIRE.getDefaultState());
}
}
//Plants should die
else if(!handler.getAtmosphereType(bpos).allowsCombustion()) {
if(world.getBlockState(bpos).getMaterial() == Material.LEAVES) {
world.setBlockState(bpos, Blocks.AIR.getDefaultState());
} else if (world.getBlockState(bpos).getMaterial() == Material.FIRE) {
world.setBlockState(bpos, Blocks.AIR.getDefaultState());
} else if (world.getBlockState(bpos).getMaterial() == Material.CACTUS) {
world.setBlockState(bpos, Blocks.AIR.getDefaultState());
} else if (world.getBlockState(bpos).getMaterial() == Material.PLANTS && world.getBlockState(bpos).getBlock() != Blocks.DEAD_BUSH) {
world.setBlockState(bpos, Blocks.DEAD_BUSH.getDefaultState());
} else if (world.getBlockState(bpos).getMaterial() == Material.TALL_PLANTS) {
world.setBlockState(bpos, Blocks.AIR.getDefaultState());
} else if (world.getBlockState(bpos).getBlock() == Blocks.GRASS_BLOCK) {
world.setBlockState(bpos, Blocks.DIRT.getDefaultState());
}
}
//Gasses should automatically vaporize and dissipate
if (handler.getAtmosphereType(bpos) == AtmosphereType.VACUUM) {
if (world.getBlockState(bpos).getMaterial() == Material.WATER && !(world.getBlockState(bpos).getBlock() instanceof FlowingFluidBlock)) {
IFluidBlock fluidblock = (IFluidBlock)world.getBlockState(bpos).getBlock();
if (fluidblock.getFluid().getAttributes().isGaseous())
world.setBlockState(bpos, Blocks.AIR.getDefaultState());
}
}
//Water blocks should also vaporize and disappear
if (handler.getAtmosphereType(bpos) == AtmosphereType.SUPERHEATED || handler.getAtmosphereType(bpos) == AtmosphereType.SUPERHEATEDNOO2 || handler.getAtmosphereType(bpos) == AtmosphereType.VERYHOT || handler.getAtmosphereType(bpos) == AtmosphereType.VERYHOTNOO2) {
if (world.getBlockState(bpos).getMaterial() == Material.WATER) {
world.setBlockState(bpos, Blocks.AIR.getDefaultState());
}
}
List<AreaBlob> nearbyBlobs = handler.getBlobWithinRadius(pos, MAX_BLOB_RADIUS);
for(AreaBlob blob : nearbyBlobs) {
if(blob.getBlobMaxRadius() > pos.getDistance(blob.getRootPosition())) {
if(world.isAirBlock(bpos))
handler.onBlockRemove(pos);
else {
//Place block
if( blob.contains(pos) && !blob.isPositionAllowed(world, pos, nearbyBlobs)) {
blob.removeBlock(pos);
}
else if(!blob.contains(blob.getRootPosition())) {
blob.addBlock(blob.getRootPosition(), nearbyBlobs);
}
else if(!blob.contains(pos) && blob.isPositionAllowed(world, pos, nearbyBlobs))//isFulBlock(world, pos.getBlockPos()))
blob.addBlock(pos, nearbyBlobs);
}
}
}
}
}
/**
* Gets a list of AreaBlobs within a radius
* @param pos position
* @param radius distance from the position to find blobs within
* @return List of AreaBlobs within the radius from the position
*/
@Nonnull
protected List<AreaBlob> getBlobWithinRadius(@Nonnull HashedBlockPosition pos, int radius) {
LinkedList<AreaBlob> list = new LinkedList<>();
for(AreaBlob blob : blobs.values()) {
if(blob.getRootPosition().getDistance(pos) - radius <= 0) {
list.add(blob);
}
}
return list;
}
/**
* @param dimNumber dimension number for which to get the oxygenhandler
* @return the oxygen handler for the planet or null if none exists
*/
@Nullable
public static AtmosphereHandler getOxygenHandler(ResourceLocation dimNumber) {
//Get your oxyclean!
return dimensionOxygen.get(dimNumber);
}
/**
* @param world dimension number for which to get the oxygenhandler
* @return the oxygen handler for the planet or null if none exists
*/
public static AtmosphereHandler getOxygenHandler(World world) {
//Get your oxyclean!
return getOxygenHandler(ZUtils.getDimensionIdentifier(world));
}
/**
* Registers a Blob with the atmosphere handler.
* Must be called before use
* @param handler IBlobHander to register with
* @param pos
*/
public void registerBlob(@Nonnull IBlobHandler handler, BlockPos pos) {
AreaBlob blob = blobs.get(handler);
if(blob == null) {
blob = new AtmosphereBlob(handler);
blobs.put(handler, blob);
blob.setData(AtmosphereType.PRESSURIZEDAIR);
}
}
/**
* Registers a Blob with provided blob type
* Must be called before use
* @param handler IBlobHander to register with
* @param pos
* @param blob2
*/
public void registerBlob(@Nonnull IBlobHandler handler, BlockPos pos, @Nonnull AreaBlob blob2) {
AreaBlob blob = blobs.get(handler);
if(blob == null) {
blob = blob2;
blobs.put(handler, blob);
blob.setData(AtmosphereType.PRESSURIZEDAIR);
}
}
/**
* Unregisters a blob from the atmosphere handler
* @param handler IBlobHandlerObject the blob is associated with
*/
public void unregisterBlob(@Nonnull IBlobHandler handler) {
blobs.remove(handler);
}
/**
* Removes all blocks from the blob associated with this handler
* @param handler the handler associated with this blob
*/
public void clearBlob(@Nonnull IBlobHandler handler) {
if(blobs.containsKey(handler)) {
blobs.get(handler).clearBlob();
}
}
/**
* Adds a block to the blob
* @param handler the handler to register the block with
* @param x the x position of the block
* @param y the y position of the block
* @param z the z position of the block
*/
public void addBlock(@Nonnull IBlobHandler handler, int x, int y, int z){
addBlock(handler, new HashedBlockPosition(x, y, z));
}
/**
* Adds a block to the blob
* @param handler the handler to register the block with
* @param pos the HashedBlockPosition of the block
* @return true if blob addition is successful
*/
public boolean addBlock(@Nonnull IBlobHandler handler, @Nonnull HashedBlockPosition pos){
AreaBlob blob = blobs.get(handler);
blob.addBlock(pos, getBlobWithinRadius(pos, MAX_BLOB_RADIUS));
return !blob.getLocations().isEmpty();
}
/**
* @param pos2 the position to get the AtmosphereType at
* @return AtmosphereType at this location
*/
@Nonnull
public IAtmosphere getAtmosphereType(BlockPos pos2) {
if(ARConfiguration.getCurrentConfig().enableOxygen.get()) {
HashedBlockPosition pos = new HashedBlockPosition(pos2);
for(AreaBlob blob : blobs.values()) {
if(blob.contains(pos)) {
IAtmosphere atmosphere = (IAtmosphere)blob.getData();
if(atmosphere != null)
return atmosphere;
}
}
return getDefaultAtmosphereType();
}
return AtmosphereType.AIR;
}
/**
* @return the default atmosphere type used by this planet
*/
@Nonnull
public IAtmosphere getDefaultAtmosphereType() {
return DimensionManager.getInstance().getDimensionProperties(dimId).getAtmosphere();
}
/**
* Gets the atmosphere type at the location of this entity
* @param entity the entity to check against
* @return The atmosphere type this entity is inside of
*/
@Nullable
public IAtmosphere getAtmosphereType(Entity entity) {
if(ARConfiguration.getCurrentConfig().enableOxygen.get()) {
HashedBlockPosition pos = new HashedBlockPosition((int)Math.floor(entity.getPosX()), (int)Math.ceil(entity.getPosY()), (int)Math.floor(entity.getPosZ()));
for(AreaBlob blob : blobs.values()) {
if(blob.contains(pos)) {
return (IAtmosphere)blob.getData();
}
}
return DimensionManager.getInstance().getDimensionProperties(dimId).getAtmosphere();
}
return AtmosphereType.AIR;
}
/**
* Gets the pressure at the location of this entity
* @param entity the entity to check against
* @return The atmosphere pressure this entity is inside of, or -1 to use default
*/
public int getAtmospherePressure(@Nonnull Entity entity) {
if(ARConfiguration.getCurrentConfig().enableOxygen.get()) {
HashedBlockPosition pos = new HashedBlockPosition((int)Math.floor(entity.getPosX()), (int)Math.ceil(entity.getPosY()), (int)Math.floor(entity.getPosZ()));
for(AreaBlob blob : blobs.values()) {
if(blob.contains(pos) && blob instanceof AtmosphereBlob) {
return ((AtmosphereBlob)blob).getPressure();
}
}
}
return -1;
}
/**
* @param entity entity to check against
* @return true if the entity can breathe in the this atmosphere
*/
public boolean canEntityBreathe(@Nonnull LivingEntity entity) {
if(ARConfiguration.getCurrentConfig().enableOxygen.get()) {
HashedBlockPosition pos = new HashedBlockPosition((int)Math.floor(entity.getPosX()), (int)Math.ceil(entity.getPosY()), (int)Math.floor(entity.getPosZ()));
for(AreaBlob blob : blobs.values()) {
IAtmosphere atmosphere = (IAtmosphere)blob.getData();
if(blob.contains(pos) && atmosphere != null && atmosphere.isImmune(entity)) {
return true;
}
}
return DimensionManager.getInstance().getDimensionProperties(dimId).getAtmosphere().isImmune(entity);
}
return true;
}
/**
* @param handler the handler registered to this blob
* @return The current size of the blob
*/
public int getBlobSize(@Nonnull IBlobHandler handler) {
return blobs.get(handler).getBlobSize();
}
/**
* Changes the atmosphere type of this blob
* @param handler the handler for the blob
* @param data the AtmosphereType to set this blob to.
*/
public void setAtmosphereType(@Nonnull IBlobHandler handler, @Nonnull IAtmosphere data) {
blobs.get(handler).setData(data);
}
/**
* Gets the atmosphere type of this blob
* @param handler the handler for the blob
*/
@Nonnull
public IAtmosphere getAtmosphereType(@Nonnull IBlobHandler handler) {
if(ARConfiguration.getCurrentConfig().enableOxygen.get()) {
IAtmosphere atmosphere = (IAtmosphere) blobs.get(handler).getData();
if(atmosphere != null)
return atmosphere;
else
return getDefaultAtmosphereType();
}
return AtmosphereType.AIR;
}
}