diff options
Diffstat (limited to '')
-rw-r--r-- | VC2008/MCServer.vcproj | 6 | ||||
-rw-r--r-- | source/Blocks/BlockFire.h | 190 | ||||
-rw-r--r-- | source/Blocks/BlockHandler.cpp | 2 | ||||
-rw-r--r-- | source/Blocks/BlockPortal.h | 108 |
4 files changed, 303 insertions, 3 deletions
diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj index cb8cf9f81..a5b43348a 100644 --- a/VC2008/MCServer.vcproj +++ b/VC2008/MCServer.vcproj @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?> +<?xml version="1.0" encoding="UTF-8"?> <VisualStudioProject ProjectType="Visual C++" Version="9,00" @@ -2260,6 +2260,10 @@ > </File> <File + RelativePath="..\source\blocks\BlockPortal.h" + > + </File> + <File RelativePath="..\source\Blocks\BlockPumpkin.h" > </File> diff --git a/source/Blocks/BlockFire.h b/source/Blocks/BlockFire.h index d3ba499b1..36ec6bbb3 100644 --- a/source/Blocks/BlockFire.h +++ b/source/Blocks/BlockFire.h @@ -16,6 +16,28 @@ public: { } + /// Portal boundary and direction variables + int XZP, XZM, Dir; // For wont of a better name... + + virtual void OnPlaced(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override + { + /* + PORTAL FINDING ALGORITH + ======================= + -Get clicked base block + -Trace upwards to find first obsidian block; aborts if anything other than obsidian or air is encountered. + Uses this value as a reference (the 'ceiling') + -For both directions (if one fails, try the other), BASE (clicked) block: + -Go in one direction, only stop if a non obsidian block is encountered (abort) OR a portal border is encountered (FindObsidianCeiling returns -1) + -If a border was encountered, go the other direction and repeat above + -Write borders to XZP and XZM, write direction portal faces to Dir + -Loop through boundary variables, and fill with portal blocks based on Dir with meta from Dir + */ + + a_BlockY--; // Because we want the block below the fire + FindAndSetPortalFrame(a_BlockX, a_BlockY, a_BlockZ, a_World); // Brought to you by Aperture Science + } + virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override { a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ); @@ -30,12 +52,176 @@ public: { return true; } - + virtual const char * GetStepSound(void) override { return "step.wood"; } -} ; + + /// Traces along YP until it finds an obsidian block, returns Y difference or 0 if no portal, and -1 for border + /// Takes the X, Y, and Z of the base block; with an optional MaxY for portal border finding + int FindObsidianCeiling(int X, int Y, int Z, cWorld * a_World, int MaxY = 0) + { + if (a_World->GetBlock(X, Y, Z) != E_BLOCK_OBSIDIAN) + { + return 0; + } + + int newY = Y + 1; + + for (newY; newY < cChunkDef::Height; newY++) + { + BLOCKTYPE Block = a_World->GetBlock(X, newY, Z); + if ((Block == E_BLOCK_AIR) || (Block == E_BLOCK_FIRE)) + { + continue; + } + else if (Block == E_BLOCK_OBSIDIAN) + { + // We found an obsidian ceiling + // Make sure MaxY has a value and newY ('ceiling' location) is at one above the base block + // This is because the frame is a solid obsidian pillar + if ((MaxY != 0) && (newY == Y + 1)) + { + return EvaluatePortalBorder(X, newY, Z, MaxY, a_World); + } + else + { + // Return ceiling Y, whoever called this function will decide if it's part of a portal or not + return newY; + } + } + else { return 0; } + } + + return 0; + } + + /// Evaluates if coords have a valid border on top, based on MaxY + int EvaluatePortalBorder(int X, int FoundObsidianY, int Z, int MaxY, cWorld * a_World) + { + for (int checkBorder = FoundObsidianY + 1; checkBorder <= MaxY - 1; checkBorder++) // FoundObsidianY + 1: FoundObsidianY has already been checked in FindObsidianCeiling; MaxY - 1: portal doesn't need corners + { + if (a_World->GetBlock(X, checkBorder, Z) != E_BLOCK_OBSIDIAN) + { + // Base obsidian, base + 1 obsidian, base + x NOT obsidian -> not complete portal + return 0; + } + } + // Everything was obsidian, found a border! + return -1; // Return -1 for a frame border + } + + /// Finds entire frame in any direction with the coordinates of a base block and fills hole with nether portal (START HERE) + void FindAndSetPortalFrame(int X, int Y, int Z, cWorld * a_World) + { + int MaxY = FindObsidianCeiling(X, Y, Z, a_World); // Get topmost obsidian block as reference for all other checks + int X1 = X + 1, Z1 = Z + 1, X2 = X - 1, Z2 = Z - 1; // Duplicate XZ values, add/subtract one as we've checked the original already the line above + + if (MaxY == 0) // Oh noes! Not a portal coordinate :( + { + return; + } + + if (!FindPortalSliceX(X1, X2, Y, Z, MaxY, a_World)) + { + if (!FindPortalSliceZ(X, Y, Z1, Z2, MaxY, a_World)) + { + return; // No eligible portal construct, abort abort abort!! + } + } + + for (int Height = Y + 1; Height <= MaxY - 1; Height++) // Loop through boundary to set portal blocks + { + for (int Width = XZM; Width <= XZP; Width++) + { + if (Dir == 1) + { + a_World->SetBlock(Width, Height, Z, E_BLOCK_NETHER_PORTAL, Dir); + } + else + { + a_World->SetBlock(X, Height, Width, E_BLOCK_NETHER_PORTAL, Dir); + } + } + } + + return; + } + + /// Evaluates if coordinates are a portal going XP/XM; returns true if so, and writes boundaries to variable + /// Takes coordinates of base block and Y coord of target obsidian ceiling + bool FindPortalSliceX(int X1, int X2, int Y, int Z, int MaxY, cWorld * a_World) + { + Dir = 1; // Set assumed direction (will change if portal turns out to be facing the other direction) + bool FoundFrameXP = false, FoundFrameXM = false; + for (X1; ((a_World->GetBlock(X1, Y, Z) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X1, Y + 1, Z) == E_BLOCK_OBSIDIAN)); X1++) // Check XP for obsidian blocks, exempting corners + { + int Value = FindObsidianCeiling(X1, Y, Z, a_World, MaxY); + int ValueTwo = FindObsidianCeiling(X1, Y + 1, Z, a_World, MaxY); // For corners without obsidian + if ((Value == -1) || (ValueTwo == -1)) // FindObsidianCeiling returns -1 upon frame-find + { + FoundFrameXP = true; // Found a frame border in this direction, proceed in other direction (don't go further) + break; + } + else if ((Value != MaxY) && (ValueTwo != MaxY)) // Make sure that there is a valid portal 'slice' + { + return false; // Not valid slice, no portal can be formed + } + } XZM = X1 - 2; // Set boundary of frame interior (hence the -2) + for (X2; ((a_World->GetBlock(X2, Y, Z) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X2, Y + 1, Z) == E_BLOCK_OBSIDIAN)); X2--) // Go the other direction (XM) + { + int Value = FindObsidianCeiling(X2, Y, Z, a_World, MaxY); + int ValueTwo = FindObsidianCeiling(X2, Y + 1, Z, a_World, MaxY); + if ((Value == -1) || (ValueTwo == -1)) + { + FoundFrameXM = true; + break; + } + else if ((Value != MaxY) && (ValueTwo != MaxY)) + { + return false; + } + } XZP = X2 + 2; // Set boundary, see previous + return (FoundFrameXP && FoundFrameXM); + } + + /// Evaluates if coords are a portal going ZP/ZM; returns true if so, and writes boundaries to variable + bool FindPortalSliceZ(int X, int Y, int Z1, int Z2, int MaxY, cWorld * a_World) + { + Dir = 2; + bool FoundFrameZP = false, FoundFrameZM = false; + for (Z1; ((a_World->GetBlock(X, Y, Z1) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X, Y + 1, Z1) == E_BLOCK_OBSIDIAN)); Z1++) + { + int Value = FindObsidianCeiling(X, Y, Z1, a_World, MaxY); + int ValueTwo = FindObsidianCeiling(X, Y + 1, Z1, a_World, MaxY); + if ((Value == -1) || (ValueTwo == -1)) + { + FoundFrameZP = true; + continue; + } + else if ((Value != MaxY) && (ValueTwo != MaxY)) + { + return false; + } + } XZP = Z1 - 2; + for (Z2; ((a_World->GetBlock(X, Y, Z2) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X, Y + 1, Z2) == E_BLOCK_OBSIDIAN)); Z2--) + { + int Value = FindObsidianCeiling(X, Y, Z2, a_World, MaxY); + int ValueTwo = FindObsidianCeiling(X, Y + 1, Z2, a_World, MaxY); + if ((Value == -1) || (ValueTwo == -1)) + { + FoundFrameZM = true; + continue; + } + else if ((Value != MaxY) && (ValueTwo != MaxY)) + { + return false; + } + } XZM = Z2 + 2; + return (FoundFrameZP && FoundFrameZM); + } +}; diff --git a/source/Blocks/BlockHandler.cpp b/source/Blocks/BlockHandler.cpp index e59fee8ee..cd07b3021 100644 --- a/source/Blocks/BlockHandler.cpp +++ b/source/Blocks/BlockHandler.cpp @@ -44,6 +44,7 @@ #include "BlockOre.h" #include "BlockPiston.h" #include "BlockPlanks.h" +#include "BlockPortal.h" #include "BlockPumpkin.h" #include "BlockRail.h" #include "BlockRedstone.h" @@ -159,6 +160,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) case E_BLOCK_PISTON: return new cBlockPistonHandler (a_BlockType); case E_BLOCK_PISTON_EXTENSION: return new cBlockPistonHeadHandler ( ); case E_BLOCK_PLANKS: return new cBlockPlanksHandler (a_BlockType); + case E_BLOCK_NETHER_PORTAL: return new cBlockPortalHandler (a_BlockType); case E_BLOCK_PUMPKIN: return new cBlockPumpkinHandler (a_BlockType); case E_BLOCK_JACK_O_LANTERN: return new cBlockPumpkinHandler (a_BlockType); case E_BLOCK_PUMPKIN_STEM: return new cBlockStemsHandler (a_BlockType); diff --git a/source/Blocks/BlockPortal.h b/source/Blocks/BlockPortal.h new file mode 100644 index 000000000..c56f0cbc8 --- /dev/null +++ b/source/Blocks/BlockPortal.h @@ -0,0 +1,108 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockPortalHandler : + public cBlockHandler +{ +public: + cBlockPortalHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + // We set to zero so MCS doesn't stop you from building weird portals like vanilla does + // CanBeAt doesn't do anything if meta is zero + // We set to zero because the client sends meta = 2 to the server (it calculates rotation itself) + + a_BlockType = m_BlockType; + a_BlockMeta = 0; + return true; + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + return; // No pickups + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + if ((a_RelY - 1 < 0) || (a_RelY + 1 > cChunkDef::Height)) + { + return false; // In case someone places a portal with meta 1 or 2 at boundaries, and server tries to get invalid coords at Y - 1 or Y + 1 + } + + switch (a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)) + { + case 0x1: + { + static const struct + { + int x, y, z; + } PortalCheck[] = + { + { 0, 1, 0}, + { 0,-1, 0}, + { 1, 0, 0}, + {-1, 0, 0}, + } ; + + for (int i = 0; i < ARRAYCOUNT(PortalCheck); i++) + { + BLOCKTYPE Block; + a_Chunk.UnboundedRelGetBlockType(a_RelX + PortalCheck[i].x, a_RelY + PortalCheck[i].y, a_RelZ + PortalCheck[i].z, Block); + + if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN)) + { + return false; + } + } + break; + } + case 0x2: + { + static const struct + { + int x, y, z; + } PortalCheck[] = + { + { 0, 1, 0}, + { 0,-1, 0}, + { 0, 0, -1}, + { 0, 0, 1}, + } ; + + for (int i = 0; i < ARRAYCOUNT(PortalCheck); i++) + { + BLOCKTYPE Block; + a_Chunk.UnboundedRelGetBlockType(a_RelX + PortalCheck[i].x, a_RelY + PortalCheck[i].y, a_RelZ + PortalCheck[i].z, Block); + + if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN)) + { + return false; + } + } + break; + } + } + return true; + } +} ; + + + + |