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 f = 0.0F;
071
072        for (int i = 0; i <= 15; ++i)
073        {
074            float f1 = 1.0F - (float)i / 15.0F;
075            this.lightBrightnessTable[i] = (1.0F - f1) / (f1 * 3.0F + 1.0F) * (1.0F - f) + f;
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 k = this.worldObj.getFirstUncoveredBlock(par1, par2);
101        return k == 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 j = (int)(par1 % 24000L);
110        float f1 = ((float)j + par3) / 24000.0F - 0.25F;
111
112        if (f1 < 0.0F)
113        {
114            ++f1;
115        }
116
117        if (f1 > 1.0F)
118        {
119            --f1;
120        }
121
122        float f2 = f1;
123        f1 = 1.0F - (float)((Math.cos((double)f1 * Math.PI) + 1.0D) / 2.0D);
124        f1 = f2 + (f1 - f2) / 3.0F;
125        return f1;
126    }
127
128    public int getMoonPhase(long par1)
129    {
130        return (int)(par1 / 24000L) % 8;
131    }
132
133    /**
134     * Returns 'true' if in the "main surface world", but 'false' if in the Nether or End dimensions.
135     */
136    public boolean isSurfaceWorld()
137    {
138        return true;
139    }
140
141    @SideOnly(Side.CLIENT)
142
143    /**
144     * Returns array with sunrise/sunset colors
145     */
146    public float[] calcSunriseSunsetColors(float par1, float par2)
147    {
148        float f2 = 0.4F;
149        float f3 = MathHelper.cos(par1 * (float)Math.PI * 2.0F) - 0.0F;
150        float f4 = -0.0F;
151
152        if (f3 >= f4 - f2 && f3 <= f4 + f2)
153        {
154            float f5 = (f3 - f4) / f2 * 0.5F + 0.5F;
155            float f6 = 1.0F - (1.0F - MathHelper.sin(f5 * (float)Math.PI)) * 0.99F;
156            f6 *= f6;
157            this.colorsSunriseSunset[0] = f5 * 0.3F + 0.7F;
158            this.colorsSunriseSunset[1] = f5 * f5 * 0.7F + 0.2F;
159            this.colorsSunriseSunset[2] = f5 * f5 * 0.0F + 0.2F;
160            this.colorsSunriseSunset[3] = f6;
161            return this.colorsSunriseSunset;
162        }
163        else
164        {
165            return null;
166        }
167    }
168
169    @SideOnly(Side.CLIENT)
170
171    /**
172     * Return Vec3D with biome specific fog color
173     */
174    public Vec3 getFogColor(float par1, float par2)
175    {
176        float f2 = MathHelper.cos(par1 * (float)Math.PI * 2.0F) * 2.0F + 0.5F;
177
178        if (f2 < 0.0F)
179        {
180            f2 = 0.0F;
181        }
182
183        if (f2 > 1.0F)
184        {
185            f2 = 1.0F;
186        }
187
188        float f3 = 0.7529412F;
189        float f4 = 0.84705883F;
190        float f5 = 1.0F;
191        f3 *= f2 * 0.94F + 0.06F;
192        f4 *= f2 * 0.94F + 0.06F;
193        f5 *= f2 * 0.91F + 0.09F;
194        return this.worldObj.getWorldVec3Pool().getVecFromPool((double)f3, (double)f4, (double)f5);
195    }
196
197    /**
198     * True if the player can respawn in this dimension (true = overworld, false = nether).
199     */
200    public boolean canRespawnHere()
201    {
202        return true;
203    }
204
205    public static WorldProvider getProviderForDimension(int par0)
206    {
207        return DimensionManager.createProviderFor(par0);
208    }
209
210    @SideOnly(Side.CLIENT)
211
212    /**
213     * the y level at which clouds are rendered.
214     */
215    public float getCloudHeight()
216    {
217        return 128.0F;
218    }
219
220    @SideOnly(Side.CLIENT)
221    public boolean isSkyColored()
222    {
223        return true;
224    }
225
226    /**
227     * Gets the hard-coded portal location to use when entering this dimension.
228     */
229    public ChunkCoordinates getEntrancePortalLocation()
230    {
231        return null;
232    }
233
234    public int getAverageGroundLevel()
235    {
236        return this.terrainType.getMinimumSpawnHeight(this.worldObj);
237    }
238
239    @SideOnly(Side.CLIENT)
240
241    /**
242     * returns true if this dimension is supposed to display void particles and pull in the far plane based on the
243     * user's Y offset.
244     */
245    public boolean getWorldHasVoidParticles()
246    {
247        return this.terrainType.hasVoidParticles(this.hasNoSky);
248    }
249
250    @SideOnly(Side.CLIENT)
251
252    /**
253     * Returns a double value representing the Y value relative to the top of the map at which void fog is at its
254     * maximum. The default factor of 0.03125 relative to 256, for example, means the void fog will be at its maximum at
255     * (256*0.03125), or 8.
256     */
257    public double getVoidFogYFactor()
258    {
259        return this.terrainType.voidFadeMagnitude();
260    }
261
262    @SideOnly(Side.CLIENT)
263
264    /**
265     * Returns true if the given X,Z coordinate should show environmental fog.
266     */
267    public boolean doesXZShowFog(int par1, int par2)
268    {
269        return false;
270    }
271
272    /**
273     * Returns the dimension's name, e.g. "The End", "Nether", or "Overworld".
274     */
275    public abstract String getDimensionName();
276
277    /*======================================= Forge Start =========================================*/
278    private IRenderHandler skyRenderer = null;
279    private IRenderHandler cloudRenderer = null;
280    
281    /**
282     * Sets the providers current dimension ID, used in default getSaveFolder()
283     * Added to allow default providers to be registered for multiple dimensions.
284     * 
285     * @param dim Dimension ID
286     */
287    public void setDimension(int dim)
288    {
289        this.dimensionId = dim;
290    }
291
292    /**
293     * Returns the sub-folder of the world folder that this WorldProvider saves to.
294     * EXA: DIM1, DIM-1
295     * @return The sub-folder name to save this world's chunks to.
296     */
297    public String getSaveFolder()
298    {
299        return (dimensionId == 0 ? null : "DIM" + dimensionId);
300    }
301
302    /**
303     * A message to display to the user when they transfer to this dimension.
304     *
305     * @return The message to be displayed
306     */
307    public String getWelcomeMessage()
308    {
309        if (this instanceof WorldProviderEnd)
310        {
311            return "Entering the End";
312        }
313        else if (this instanceof WorldProviderHell)
314        {
315            return "Entering the Nether";
316        }
317        return null;
318    }
319
320    /**
321     * A Message to display to the user when they transfer out of this dismension.
322     *
323     * @return The message to be displayed
324     */
325    public String getDepartMessage()
326    {
327        if (this instanceof WorldProviderEnd)
328        {
329            return "Leaving the End";
330        }
331        else if (this instanceof WorldProviderHell)
332        {
333            return "Leaving the Nether";
334        } 
335        return null;
336    }
337
338    /**
339     * The dimensions movement factor. Relative to normal overworld.
340     * It is applied to the players position when they transfer dimensions.
341     * Exa: Nether movement is 8.0
342     * @return The movement factor
343     */
344    public double getMovementFactor()
345    {
346        if (this instanceof WorldProviderHell)
347        {
348            return 8.0;
349        }
350        return 1.0;
351    }
352
353    @SideOnly(Side.CLIENT)
354    public IRenderHandler getSkyRenderer()
355    {
356        return this.skyRenderer;
357    }
358
359    @SideOnly(Side.CLIENT)
360    public void setSkyRenderer(IRenderHandler skyRenderer)
361    {
362        this.skyRenderer = skyRenderer;
363    }
364
365    @SideOnly(Side.CLIENT)
366    public IRenderHandler getCloudRenderer()
367    {
368        return cloudRenderer;
369    }
370
371    @SideOnly(Side.CLIENT)
372    public void setCloudRenderer(IRenderHandler renderer)
373    {
374        cloudRenderer = renderer;
375    }
376
377    public ChunkCoordinates getRandomizedSpawnPoint()
378    {
379        ChunkCoordinates chunkcoordinates = new ChunkCoordinates(this.worldObj.getSpawnPoint());
380
381        boolean isAdventure = worldObj.getWorldInfo().getGameType() == EnumGameType.ADVENTURE;
382        int spawnFuzz = terrainType.getSpawnFuzz();
383        int spawnFuzzHalf = spawnFuzz / 2;
384
385        if (!hasNoSky && !isAdventure)
386        {
387            chunkcoordinates.posX += this.worldObj.rand.nextInt(spawnFuzz) - spawnFuzzHalf;
388            chunkcoordinates.posZ += this.worldObj.rand.nextInt(spawnFuzz) - spawnFuzzHalf;
389            chunkcoordinates.posY = this.worldObj.getTopSolidOrLiquidBlock(chunkcoordinates.posX, chunkcoordinates.posZ);
390        }
391
392        return chunkcoordinates;
393    }
394    
395    /**
396     * Determine if the cusor on the map should 'spin' when rendered, like it does for the player in the nether.
397     * 
398     * @param entity The entity holding the map, playername, or frame-ENTITYID
399     * @param x X Position
400     * @param y Y Position
401     * @param z Z Postion
402     * @return True to 'spin' the cursor
403     */
404    public boolean shouldMapSpin(String entity, double x, double y, double z)
405    {
406        return dimensionId < 0;
407    }
408
409    /**
410     * Determines the dimension the player will be respawned in, typically this brings them back to the overworld.
411     * 
412     * @param player The player that is respawning
413     * @return The dimension to respawn the player in
414     */
415    public int getRespawnDimension(EntityPlayerMP player)
416    {
417        return 0;
418    }
419
420    /*======================================= Start Moved From World =========================================*/
421
422    public BiomeGenBase getBiomeGenForCoords(int x, int z)
423    {
424        return worldObj.getBiomeGenForCoordsBody(x, z);
425    }
426
427    public boolean isDaytime()
428    {
429        return worldObj.skylightSubtracted < 4;
430    }
431
432    @SideOnly(Side.CLIENT)
433    public Vec3 getSkyColor(Entity cameraEntity, float partialTicks)
434    {
435        return worldObj.getSkyColorBody(cameraEntity, partialTicks);
436    }
437
438    @SideOnly(Side.CLIENT)
439    public Vec3 drawClouds(float partialTicks)
440    {
441        return worldObj.drawCloudsBody(partialTicks);
442    }
443
444    @SideOnly(Side.CLIENT)
445    public float getStarBrightness(float par1)
446    {
447        return worldObj.getStarBrightnessBody(par1);
448    }
449
450    public void setAllowedSpawnTypes(boolean allowHostile, boolean allowPeaceful)
451    {
452        worldObj.spawnHostileMobs = allowHostile;
453        worldObj.spawnPeacefulMobs = allowPeaceful;
454    }
455
456    public void calculateInitialWeather()
457    {
458        worldObj.calculateInitialWeatherBody();
459    }
460
461    public void updateWeather()
462    {
463        worldObj.updateWeatherBody();
464    }
465
466    public void toggleRain()
467    {
468        worldObj.worldInfo.setRainTime(1);
469    }
470
471    public boolean canBlockFreeze(int x, int y, int z, boolean byWater)
472    {
473        return worldObj.canBlockFreezeBody(x, y, z, byWater);
474    }
475
476    public boolean canSnowAt(int x, int y, int z)
477    {
478        return worldObj.canSnowAtBody(x, y, z);
479    }
480
481    public void setWorldTime(long time)
482    {
483        worldObj.worldInfo.setWorldTime(time);
484    }
485
486    public long getSeed()
487    {
488        return worldObj.worldInfo.getSeed();
489    }
490
491    public long getWorldTime()
492    {
493        return worldObj.worldInfo.getWorldTime();
494    }
495
496    public ChunkCoordinates getSpawnPoint()
497    {
498        WorldInfo info = worldObj.worldInfo;
499        return new ChunkCoordinates(info.getSpawnX(), info.getSpawnY(), info.getSpawnZ());
500    }
501
502    public void setSpawnPoint(int x, int y, int z)
503    {
504        worldObj.worldInfo.setSpawnPosition(x, y, z);
505    }
506
507    public boolean canMineBlock(EntityPlayer player, int x, int y, int z)
508    {
509        return worldObj.canMineBlockBody(player, x, y, z);
510    }
511
512    public boolean isBlockHighHumidity(int x, int y, int z)
513    {
514        return worldObj.getBiomeGenForCoords(x, z).isHighHumidity();
515    }
516
517    public int getHeight()
518    {
519        return 256;
520    }
521
522    public int getActualHeight()
523    {
524        return hasNoSky ? 128 : 256;
525    }
526
527    public double getHorizon()
528    {
529        return worldObj.worldInfo.getTerrainType().getHorizon(worldObj);
530    }
531
532    public void resetRainAndThunder()
533    {
534        worldObj.worldInfo.setRainTime(0);
535        worldObj.worldInfo.setRaining(false);
536        worldObj.worldInfo.setThunderTime(0);
537        worldObj.worldInfo.setThundering(false);
538    }
539
540    public boolean canDoLightning(Chunk chunk)
541    {
542        return true;
543    }
544
545    public boolean canDoRainSnowIce(Chunk chunk)
546    {
547        return true;
548    }
549}