001package net.minecraft.world;
002
003import cpw.mods.fml.relauncher.Side;
004import cpw.mods.fml.relauncher.SideOnly;
005import net.minecraft.block.Block;
006import net.minecraft.entity.Entity;
007import net.minecraft.entity.player.EntityPlayer;
008import net.minecraft.entity.player.EntityPlayerMP;
009import net.minecraft.util.ChunkCoordinates;
010import net.minecraft.util.MathHelper;
011import net.minecraft.util.Vec3;
012import net.minecraft.world.biome.BiomeGenBase;
013import net.minecraft.world.biome.WorldChunkManager;
014import net.minecraft.world.biome.WorldChunkManagerHell;
015import net.minecraft.world.chunk.Chunk;
016import net.minecraft.world.chunk.IChunkProvider;
017import net.minecraft.world.gen.ChunkProviderFlat;
018import net.minecraft.world.gen.ChunkProviderGenerate;
019import net.minecraft.world.gen.FlatGeneratorInfo;
020import net.minecraft.world.storage.WorldInfo;
021import net.minecraftforge.client.IRenderHandler;
022import net.minecraftforge.common.DimensionManager;
023
024public abstract class WorldProvider
025{
026    /** world object being used */
027    public World worldObj;
028    public WorldType terrainType;
029    public String field_82913_c;
030
031    /** World chunk manager being used to generate chunks */
032    public WorldChunkManager worldChunkMgr;
033
034    /**
035     * States whether the Hell world provider is used(true) or if the normal world provider is used(false)
036     */
037    public boolean isHellWorld = false;
038
039    /**
040     * A boolean that tells if a world does not have a sky. Used in calculating weather and skylight
041     */
042    public boolean hasNoSky = false;
043
044    /** Light to brightness conversion table */
045    public float[] lightBrightnessTable = new float[16];
046
047    /** The id for the dimension (ex. -1: Nether, 0: Overworld, 1: The End) */
048    public int dimensionId = 0;
049
050    /** Array for sunrise/sunset colors (RGBA) */
051    private float[] colorsSunriseSunset = new float[4];
052
053    /**
054     * associate an existing world with a World provider, and setup its lightbrightness table
055     */
056    public final void registerWorld(World par1World)
057    {
058        this.worldObj = par1World;
059        this.terrainType = par1World.getWorldInfo().getTerrainType();
060        this.field_82913_c = par1World.getWorldInfo().getGeneratorOptions();
061        this.registerWorldChunkManager();
062        this.generateLightBrightnessTable();
063    }
064
065    /**
066     * Creates the light to brightness table
067     */
068    protected void generateLightBrightnessTable()
069    {
070        float var1 = 0.0F;
071
072        for (int var2 = 0; var2 <= 15; ++var2)
073        {
074            float var3 = 1.0F - (float)var2 / 15.0F;
075            this.lightBrightnessTable[var2] = (1.0F - var3) / (var3 * 3.0F + 1.0F) * (1.0F - var1) + var1;
076        }
077    }
078
079    /**
080     * creates a new world chunk manager for WorldProvider
081     */
082    protected void registerWorldChunkManager()
083    {
084        worldChunkMgr = terrainType.getChunkManager(worldObj);
085    }
086
087    /**
088     * Returns a new chunk provider which generates chunks for this world
089     */
090    public IChunkProvider createChunkGenerator()
091    {
092        return terrainType.getChunkGenerator(worldObj, field_82913_c);
093    }
094
095    /**
096     * Will check if the x, z position specified is alright to be set as the map spawn point
097     */
098    public boolean canCoordinateBeSpawn(int par1, int par2)
099    {
100        int var3 = this.worldObj.getFirstUncoveredBlock(par1, par2);
101        return var3 == Block.grass.blockID;
102    }
103
104    /**
105     * Calculates the angle of sun and moon in the sky relative to a specified time (usually worldTime)
106     */
107    public float calculateCelestialAngle(long par1, float par3)
108    {
109        int var4 = (int)(par1 % 24000L);
110        float var5 = ((float)var4 + par3) / 24000.0F - 0.25F;
111
112        if (var5 < 0.0F)
113        {
114            ++var5;
115        }
116
117        if (var5 > 1.0F)
118        {
119            --var5;
120        }
121
122        float var6 = var5;
123        var5 = 1.0F - (float)((Math.cos((double)var5 * Math.PI) + 1.0D) / 2.0D);
124        var5 = var6 + (var5 - var6) / 3.0F;
125        return var5;
126    }
127
128    @SideOnly(Side.CLIENT)
129    public int getMoonPhase(long par1, float par3)
130    {
131        return (int)(par1 / 24000L) % 8;
132    }
133
134    /**
135     * Returns 'true' if in the "main surface world", but 'false' if in the Nether or End dimensions.
136     */
137    public boolean isSurfaceWorld()
138    {
139        return true;
140    }
141
142    @SideOnly(Side.CLIENT)
143
144    /**
145     * Returns array with sunrise/sunset colors
146     */
147    public float[] calcSunriseSunsetColors(float par1, float par2)
148    {
149        float var3 = 0.4F;
150        float var4 = MathHelper.cos(par1 * (float)Math.PI * 2.0F) - 0.0F;
151        float var5 = -0.0F;
152
153        if (var4 >= var5 - var3 && var4 <= var5 + var3)
154        {
155            float var6 = (var4 - var5) / var3 * 0.5F + 0.5F;
156            float var7 = 1.0F - (1.0F - MathHelper.sin(var6 * (float)Math.PI)) * 0.99F;
157            var7 *= var7;
158            this.colorsSunriseSunset[0] = var6 * 0.3F + 0.7F;
159            this.colorsSunriseSunset[1] = var6 * var6 * 0.7F + 0.2F;
160            this.colorsSunriseSunset[2] = var6 * var6 * 0.0F + 0.2F;
161            this.colorsSunriseSunset[3] = var7;
162            return this.colorsSunriseSunset;
163        }
164        else
165        {
166            return null;
167        }
168    }
169
170    @SideOnly(Side.CLIENT)
171
172    /**
173     * Return Vec3D with biome specific fog color
174     */
175    public Vec3 getFogColor(float par1, float par2)
176    {
177        float var3 = MathHelper.cos(par1 * (float)Math.PI * 2.0F) * 2.0F + 0.5F;
178
179        if (var3 < 0.0F)
180        {
181            var3 = 0.0F;
182        }
183
184        if (var3 > 1.0F)
185        {
186            var3 = 1.0F;
187        }
188
189        float var4 = 0.7529412F;
190        float var5 = 0.84705883F;
191        float var6 = 1.0F;
192        var4 *= var3 * 0.94F + 0.06F;
193        var5 *= var3 * 0.94F + 0.06F;
194        var6 *= var3 * 0.91F + 0.09F;
195        return this.worldObj.getWorldVec3Pool().getVecFromPool((double)var4, (double)var5, (double)var6);
196    }
197
198    /**
199     * True if the player can respawn in this dimension (true = overworld, false = nether).
200     */
201    public boolean canRespawnHere()
202    {
203        return true;
204    }
205
206    public static WorldProvider getProviderForDimension(int par0)
207    {
208        return DimensionManager.createProviderFor(par0);
209    }
210
211    @SideOnly(Side.CLIENT)
212
213    /**
214     * the y level at which clouds are rendered.
215     */
216    public float getCloudHeight()
217    {
218        return 128.0F;
219    }
220
221    @SideOnly(Side.CLIENT)
222    public boolean isSkyColored()
223    {
224        return true;
225    }
226
227    /**
228     * Gets the hard-coded portal location to use when entering this dimension.
229     */
230    public ChunkCoordinates getEntrancePortalLocation()
231    {
232        return null;
233    }
234
235    public int getAverageGroundLevel()
236    {
237        return this.terrainType.getMinimumSpawnHeight(this.worldObj);
238    }
239
240    @SideOnly(Side.CLIENT)
241
242    /**
243     * returns true if this dimension is supposed to display void particles and pull in the far plane based on the
244     * user's Y offset.
245     */
246    public boolean getWorldHasVoidParticles()
247    {
248        return this.terrainType.hasVoidParticles(this.hasNoSky);
249    }
250
251    @SideOnly(Side.CLIENT)
252
253    /**
254     * Returns a double value representing the Y value relative to the top of the map at which void fog is at its
255     * maximum. The default factor of 0.03125 relative to 256, for example, means the void fog will be at its maximum at
256     * (256*0.03125), or 8.
257     */
258    public double getVoidFogYFactor()
259    {
260        return this.terrainType.voidFadeMagnitude();
261    }
262
263    @SideOnly(Side.CLIENT)
264
265    /**
266     * Returns true if the given X,Z coordinate should show environmental fog.
267     */
268    public boolean doesXZShowFog(int par1, int par2)
269    {
270        return false;
271    }
272
273    /**
274     * Returns the dimension's name, e.g. "The End", "Nether", or "Overworld".
275     */
276    public abstract String getDimensionName();
277
278    /*======================================= Forge Start =========================================*/
279    private IRenderHandler skyRenderer = null;
280    private IRenderHandler cloudRenderer = null;
281    
282    /**
283     * Sets the providers current dimension ID, used in default getSaveFolder()
284     * Added to allow default providers to be registered for multiple dimensions.
285     * 
286     * @param dim Dimension ID
287     */
288    public void setDimension(int dim)
289    {
290        this.dimensionId = dim;
291    }
292
293    /**
294     * Returns the sub-folder of the world folder that this WorldProvider saves to.
295     * EXA: DIM1, DIM-1
296     * @return The sub-folder name to save this world's chunks to.
297     */
298    public String getSaveFolder()
299    {
300        return (dimensionId == 0 ? null : "DIM" + dimensionId);
301    }
302
303    /**
304     * A message to display to the user when they transfer to this dimension.
305     *
306     * @return The message to be displayed
307     */
308    public String getWelcomeMessage()
309    {
310        if (this instanceof WorldProviderEnd)
311        {
312            return "Entering the End";
313        }
314        else if (this instanceof WorldProviderHell)
315        {
316            return "Entering the Nether";
317        }
318        return null;
319    }
320
321    /**
322     * A Message to display to the user when they transfer out of this dismension.
323     *
324     * @return The message to be displayed
325     */
326    public String getDepartMessage()
327    {
328        if (this instanceof WorldProviderEnd)
329        {
330            return "Leaving the End";
331        }
332        else if (this instanceof WorldProviderHell)
333        {
334            return "Leaving the Nether";
335        } 
336        return null;
337    }
338
339    /**
340     * The dimensions movement factor. Relative to normal overworld.
341     * It is applied to the players position when they transfer dimensions.
342     * Exa: Nether movement is 8.0
343     * @return The movement factor
344     */
345    public double getMovementFactor()
346    {
347        if (this instanceof WorldProviderHell)
348        {
349            return 8.0;
350        }
351        return 1.0;
352    }
353
354    @SideOnly(Side.CLIENT)
355    public IRenderHandler getSkyRenderer()
356    {
357        return this.skyRenderer;
358    }
359
360    @SideOnly(Side.CLIENT)
361    public void setSkyRenderer(IRenderHandler skyRenderer)
362    {
363        this.skyRenderer = skyRenderer;
364    }
365
366    @SideOnly(Side.CLIENT)
367    public IRenderHandler getCloudRenderer()
368    {
369        return cloudRenderer;
370    }
371
372    @SideOnly(Side.CLIENT)
373    public void setCloudRenderer(IRenderHandler renderer)
374    {
375        cloudRenderer = renderer;
376    }
377
378    public ChunkCoordinates getRandomizedSpawnPoint()
379    {
380        ChunkCoordinates var5 = new ChunkCoordinates(this.worldObj.getSpawnPoint());
381
382        boolean isAdventure = worldObj.getWorldInfo().getGameType() == EnumGameType.ADVENTURE;
383        int spawnFuzz = terrainType.getSpawnFuzz();
384        int spawnFuzzHalf = spawnFuzz / 2;
385
386        if (!hasNoSky && !isAdventure)
387        {
388            var5.posX += this.worldObj.rand.nextInt(spawnFuzz) - spawnFuzzHalf;
389            var5.posZ += this.worldObj.rand.nextInt(spawnFuzz) - spawnFuzzHalf;
390            var5.posY = this.worldObj.getTopSolidOrLiquidBlock(var5.posX, var5.posZ);
391        }
392
393        return var5;
394    }
395    
396    /**
397     * Determine if the cusor on the map should 'spin' when rendered, like it does for the player in the nether.
398     * 
399     * @param entity The entity holding the map, playername, or frame-ENTITYID
400     * @param x X Position
401     * @param y Y Position
402     * @param z Z Postion
403     * @return True to 'spin' the cursor
404     */
405    public boolean shouldMapSpin(String entity, double x, double y, double z)
406    {
407        return dimensionId < 0;
408    }
409
410    /**
411     * Determines the dimension the player will be respawned in, typically this brings them back to the overworld.
412     * 
413     * @param player The player that is respawning
414     * @return The dimension to respawn the player in
415     */
416    public int getRespawnDimension(EntityPlayerMP player)
417    {
418        return 0;
419    }
420
421    /*======================================= Start Moved From World =========================================*/
422
423    public BiomeGenBase getBiomeGenForCoords(int x, int z)
424    {
425        return worldObj.getBiomeGenForCoordsBody(x, z);
426    }
427
428    public boolean isDaytime()
429    {
430        return worldObj.skylightSubtracted < 4;
431    }
432
433    @SideOnly(Side.CLIENT)
434    public Vec3 getSkyColor(Entity cameraEntity, float partialTicks)
435    {
436        return worldObj.getSkyColorBody(cameraEntity, partialTicks);
437    }
438
439    @SideOnly(Side.CLIENT)
440    public Vec3 drawClouds(float partialTicks)
441    {
442        return worldObj.drawCloudsBody(partialTicks);
443    }
444
445    @SideOnly(Side.CLIENT)
446    public float getStarBrightness(float par1)
447    {
448        return worldObj.getStarBrightnessBody(par1);
449    }
450
451    public void setAllowedSpawnTypes(boolean allowHostile, boolean allowPeaceful)
452    {
453        worldObj.spawnHostileMobs = allowHostile;
454        worldObj.spawnPeacefulMobs = allowPeaceful;
455    }
456
457    public void calculateInitialWeather()
458    {
459        worldObj.calculateInitialWeatherBody();
460    }
461
462    public void updateWeather()
463    {
464        worldObj.updateWeatherBody();
465    }
466
467    public void toggleRain()
468    {
469        worldObj.worldInfo.setRainTime(1);
470    }
471
472    public boolean canBlockFreeze(int x, int y, int z, boolean byWater)
473    {
474        return worldObj.canBlockFreezeBody(x, y, z, byWater);
475    }
476
477    public boolean canSnowAt(int x, int y, int z)
478    {
479        return worldObj.canSnowAtBody(x, y, z);
480    }
481
482    public void setWorldTime(long time)
483    {
484        worldObj.worldInfo.setWorldTime(time);
485    }
486
487    public long getSeed()
488    {
489        return worldObj.worldInfo.getSeed();
490    }
491
492    public long getWorldTime()
493    {
494        return worldObj.worldInfo.getWorldTime();
495    }
496
497    public ChunkCoordinates getSpawnPoint()
498    {
499        WorldInfo info = worldObj.worldInfo;
500        return new ChunkCoordinates(info.getSpawnX(), info.getSpawnY(), info.getSpawnZ());
501    }
502
503    public void setSpawnPoint(int x, int y, int z)
504    {
505        worldObj.worldInfo.setSpawnPosition(x, y, z);
506    }
507
508    public boolean canMineBlock(EntityPlayer player, int x, int y, int z)
509    {
510        return worldObj.canMineBlockBody(player, x, y, z);
511    }
512
513    public boolean isBlockHighHumidity(int x, int y, int z)
514    {
515        return worldObj.getBiomeGenForCoords(x, z).isHighHumidity();
516    }
517
518    public int getHeight()
519    {
520        return 256;
521    }
522
523    public int getActualHeight()
524    {
525        return hasNoSky ? 128 : 256;
526    }
527
528    public double getHorizon()
529    {
530        return worldObj.worldInfo.getTerrainType().getHorizon(worldObj);
531    }
532
533    public void resetRainAndThunder()
534    {
535        worldObj.worldInfo.setRainTime(0);
536        worldObj.worldInfo.setRaining(false);
537        worldObj.worldInfo.setThunderTime(0);
538        worldObj.worldInfo.setThundering(false);
539    }
540
541    public boolean canDoLightning(Chunk chunk)
542    {
543        return true;
544    }
545
546    public boolean canDoRainSnowIce(Chunk chunk)
547    {
548        return true;
549    }
550}