summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/AllToLua.bat28
-rw-r--r--source/Bindings.cpp731
-rw-r--r--source/Bindings.h2
-rw-r--r--source/BlockArea.cpp4248
-rw-r--r--source/BlockArea.h620
-rw-r--r--source/BlockEntities/BlockEntityWithItems.h172
-rw-r--r--source/BlockEntities/DispenserEntity.cpp450
-rw-r--r--source/BlockEntities/DispenserEntity.h82
-rw-r--r--source/BlockEntities/DropSpenserEntity.cpp490
-rw-r--r--source/BlockEntities/DropSpenserEntity.h178
-rw-r--r--source/BlockEntities/DropperEntity.cpp84
-rw-r--r--source/BlockEntities/DropperEntity.h98
-rw-r--r--source/BlockEntities/HopperEntity.cpp1046
-rw-r--r--source/BlockEntities/HopperEntity.h204
-rw-r--r--source/BlockEntities/JukeboxEntity.cpp246
-rw-r--r--source/BlockEntities/JukeboxEntity.h86
-rw-r--r--source/BlockEntities/NoteEntity.cpp318
-rw-r--r--source/BlockEntities/NoteEntity.h104
-rw-r--r--source/Blocks/BlockBed.cpp170
-rw-r--r--source/Blocks/BlockBed.h146
-rw-r--r--source/Blocks/BlockBrewingStand.h64
-rw-r--r--source/Blocks/BlockCactus.h176
-rw-r--r--source/Blocks/BlockCauldron.h118
-rw-r--r--source/Blocks/BlockChest.h444
-rw-r--r--source/Blocks/BlockCloth.h68
-rw-r--r--source/Blocks/BlockCobWeb.h60
-rw-r--r--source/Blocks/BlockCrops.h216
-rw-r--r--source/Blocks/BlockDeadBush.h94
-rw-r--r--source/Blocks/BlockDirt.h176
-rw-r--r--source/Blocks/BlockDoor.cpp176
-rw-r--r--source/Blocks/BlockDoor.h210
-rw-r--r--source/Blocks/BlockDropSpenser.h78
-rw-r--r--source/Blocks/BlockEnderchest.h56
-rw-r--r--source/Blocks/BlockEntity.h62
-rw-r--r--source/Blocks/BlockFarmland.h198
-rw-r--r--source/Blocks/BlockFenceGate.h120
-rw-r--r--source/Blocks/BlockFire.h84
-rw-r--r--source/Blocks/BlockFlower.h106
-rw-r--r--source/Blocks/BlockFlowerPot.h210
-rw-r--r--source/Blocks/BlockFluid.h100
-rw-r--r--source/Blocks/BlockFurnace.h94
-rw-r--r--source/Blocks/BlockGlass.h52
-rw-r--r--source/Blocks/BlockGlowstone.h60
-rw-r--r--source/Blocks/BlockGravel.h54
-rw-r--r--source/Blocks/BlockHandler.cpp906
-rw-r--r--source/Blocks/BlockHandler.h316
-rw-r--r--source/Blocks/BlockHopper.h92
-rw-r--r--source/Blocks/BlockIce.h74
-rw-r--r--source/Blocks/BlockLadder.h230
-rw-r--r--source/Blocks/BlockLeaves.h368
-rw-r--r--source/Blocks/BlockLever.cpp96
-rw-r--r--source/Blocks/BlockLever.h122
-rw-r--r--source/Blocks/BlockMelon.h70
-rw-r--r--source/Blocks/BlockMushroom.h142
-rw-r--r--source/Blocks/BlockMycelium.h54
-rw-r--r--source/Blocks/BlockNote.h26
-rw-r--r--source/Blocks/BlockOre.h160
-rw-r--r--source/Blocks/BlockPiston.cpp138
-rw-r--r--source/Blocks/BlockPiston.h56
-rw-r--r--source/Blocks/BlockRail.h842
-rw-r--r--source/Blocks/BlockRedstone.cpp54
-rw-r--r--source/Blocks/BlockRedstone.h92
-rw-r--r--source/Blocks/BlockRedstoneRepeater.cpp94
-rw-r--r--source/Blocks/BlockRedstoneRepeater.h120
-rw-r--r--source/Blocks/BlockRedstoneTorch.h72
-rw-r--r--source/Blocks/BlockSand.h56
-rw-r--r--source/Blocks/BlockSapling.h138
-rw-r--r--source/Blocks/BlockSign.h157
-rw-r--r--source/Blocks/BlockSlab.h140
-rw-r--r--source/Blocks/BlockSnow.h104
-rw-r--r--source/Blocks/BlockStairs.h306
-rw-r--r--source/Blocks/BlockStems.h116
-rw-r--r--source/Blocks/BlockStone.h58
-rw-r--r--source/Blocks/BlockSugarcane.h192
-rw-r--r--source/Blocks/BlockTallGrass.h102
-rw-r--r--source/Blocks/BlockTorch.h518
-rw-r--r--source/Blocks/BlockVine.h400
-rw-r--r--source/Blocks/BlockWood.h54
-rw-r--r--source/Blocks/BlockWorkbench.h86
-rw-r--r--source/ByteBuffer.cpp1322
-rw-r--r--source/ByteBuffer.h242
-rw-r--r--source/ClientHandle.cpp64
-rw-r--r--source/ClientHandle.h137
-rw-r--r--source/CommandOutput.cpp142
-rw-r--r--source/CommandOutput.h164
-rw-r--r--source/Defines.h4
-rw-r--r--source/Enchantments.cpp596
-rw-r--r--source/Enchantments.h228
-rw-r--r--source/FallingBlock.cpp206
-rw-r--r--source/FallingBlock.h90
-rw-r--r--source/FastRandom.cpp348
-rw-r--r--source/FastRandom.h114
-rw-r--r--source/Generating/Caves.cpp1940
-rw-r--r--source/Generating/Caves.h204
-rw-r--r--source/Generating/ChunkDesc.cpp1156
-rw-r--r--source/Generating/ComposableGenerator.cpp1048
-rw-r--r--source/Generating/ComposableGenerator.h350
-rw-r--r--source/Generating/DistortedHeightmap.cpp838
-rw-r--r--source/Generating/DistortedHeightmap.h204
-rw-r--r--source/Generating/EndGen.cpp434
-rw-r--r--source/Generating/EndGen.h138
-rw-r--r--source/Generating/MineShafts.cpp2846
-rw-r--r--source/Generating/MineShafts.h122
-rw-r--r--source/Generating/Noise3DGenerator.cpp1162
-rw-r--r--source/Generating/Noise3DGenerator.h212
-rw-r--r--source/Generating/Ravines.cpp1062
-rw-r--r--source/Generating/Ravines.h92
-rw-r--r--source/ItemGrid.cpp1324
-rw-r--r--source/ItemGrid.h382
-rw-r--r--source/Items/ItemBed.h112
-rw-r--r--source/Items/ItemBrewingStand.h82
-rw-r--r--source/Items/ItemBucket.h320
-rw-r--r--source/Items/ItemCauldron.h82
-rw-r--r--source/Items/ItemCloth.h46
-rw-r--r--source/Items/ItemDoor.h90
-rw-r--r--source/Items/ItemDye.h88
-rw-r--r--source/Items/ItemFlowerPot.h82
-rw-r--r--source/Items/ItemFood.h118
-rw-r--r--source/Items/ItemHandler.cpp997
-rw-r--r--source/Items/ItemHandler.h191
-rw-r--r--source/Items/ItemHoe.h60
-rw-r--r--source/Items/ItemLeaves.h82
-rw-r--r--source/Items/ItemLighter.h112
-rw-r--r--source/Items/ItemMinecart.h160
-rw-r--r--source/Items/ItemPickaxe.h150
-rw-r--r--source/Items/ItemRedstoneDust.h76
-rw-r--r--source/Items/ItemRedstoneRepeater.h80
-rw-r--r--source/Items/ItemSapling.h84
-rw-r--r--source/Items/ItemSeeds.h130
-rw-r--r--source/Items/ItemShears.h126
-rw-r--r--source/Items/ItemShovel.h80
-rw-r--r--source/Items/ItemSign.h102
-rw-r--r--source/Items/ItemSlab.h104
-rw-r--r--source/Items/ItemSpawnEgg.h104
-rw-r--r--source/Items/ItemSugarcane.h78
-rw-r--r--source/Items/ItemSword.h40
-rw-r--r--source/Items/ItemWood.h44
-rw-r--r--source/LinearInterpolation.cpp502
-rw-r--r--source/LinearInterpolation.h120
-rw-r--r--source/LinearUpscale.h488
-rw-r--r--source/LuaScript.cpp278
-rw-r--r--source/LuaScript.h63
-rw-r--r--source/LuaWindow.cpp350
-rw-r--r--source/LuaWindow.h192
-rw-r--r--source/ManualBindings.cpp61
-rw-r--r--source/Minecart.cpp320
-rw-r--r--source/Minecart.h234
-rw-r--r--source/Mobs/Bat.h54
-rw-r--r--source/Mobs/Blaze.cpp54
-rw-r--r--source/Mobs/Blaze.h50
-rw-r--r--source/Mobs/Magmacube.cpp54
-rw-r--r--source/Mobs/Magmacube.h62
-rw-r--r--source/Mobs/Mooshroom.cpp66
-rw-r--r--source/Mobs/Mooshroom.h50
-rw-r--r--source/Mobs/Ocelot.h54
-rw-r--r--source/Mobs/Villager.cpp34
-rw-r--r--source/Mobs/Villager.h46
-rw-r--r--source/Mobs/Witch.cpp64
-rw-r--r--source/Mobs/Witch.h50
-rw-r--r--source/OSSupport/GZipFile.cpp214
-rw-r--r--source/OSSupport/GZipFile.h104
-rw-r--r--source/OSSupport/ListenThread.cpp462
-rw-r--r--source/OSSupport/ListenThread.h166
-rw-r--r--source/Player.cpp2750
-rw-r--r--source/Player.h64
-rw-r--r--source/Plugin.cpp12
-rw-r--r--source/Plugin.h75
-rw-r--r--source/PluginManager.cpp43
-rw-r--r--source/PluginManager.h84
-rw-r--r--source/Plugin_NewLua.cpp108
-rw-r--r--source/Plugin_NewLua.h79
-rw-r--r--source/Plugin_Squirrel.cpp818
-rw-r--r--source/Plugin_Squirrel.h138
-rw-r--r--source/ProbabDistrib.cpp284
-rw-r--r--source/ProbabDistrib.h148
-rw-r--r--source/Protocol/ChunkDataSerializer.cpp352
-rw-r--r--source/Protocol/ChunkDataSerializer.h96
-rw-r--r--source/Protocol/Protocol.h386
-rw-r--r--source/Protocol/Protocol125.cpp3295
-rw-r--r--source/Protocol/Protocol125.h306
-rw-r--r--source/Protocol/Protocol132.cpp1842
-rw-r--r--source/Protocol/Protocol132.h202
-rw-r--r--source/Protocol/Protocol14x.cpp506
-rw-r--r--source/Protocol/Protocol14x.h126
-rw-r--r--source/Protocol/Protocol15x.cpp274
-rw-r--r--source/Protocol/Protocol15x.h76
-rw-r--r--source/Protocol/Protocol16x.cpp507
-rw-r--r--source/Protocol/Protocol16x.h147
-rw-r--r--source/Protocol/ProtocolRecognizer.cpp1562
-rw-r--r--source/Protocol/ProtocolRecognizer.h262
-rw-r--r--source/RCONServer.cpp664
-rw-r--r--source/RCONServer.h218
-rw-r--r--source/SQLite/urls.txt16
-rw-r--r--source/Server.h2
-rw-r--r--source/Simulator/DelayedFluidSimulator.cpp316
-rw-r--r--source/Simulator/DelayedFluidSimulator.h164
-rw-r--r--source/Simulator/FloodyFluidSimulator.cpp668
-rw-r--r--source/Simulator/FloodyFluidSimulator.h106
-rw-r--r--source/Simulator/NoopFluidSimulator.h72
-rw-r--r--source/Simulator/VaporizeFluidSimulator.cpp106
-rw-r--r--source/Simulator/VaporizeFluidSimulator.h68
-rw-r--r--source/SquirrelCommandBinder.cpp236
-rw-r--r--source/SquirrelCommandBinder.h102
-rw-r--r--source/TNTEntity.cpp144
-rw-r--r--source/TNTEntity.h68
-rw-r--r--source/UI/SlotArea.cpp1630
-rw-r--r--source/UI/SlotArea.h606
-rw-r--r--source/WebAdmin.cpp287
-rw-r--r--source/WebAdmin.h91
-rw-r--r--source/WebPlugin.cpp2
-rw-r--r--source/WebPlugin.h9
-rw-r--r--source/World.cpp18
-rw-r--r--source/World.h14
-rw-r--r--source/WorldStorage/NBTChunkSerializer.cpp918
-rw-r--r--source/WorldStorage/NBTChunkSerializer.h220
-rw-r--r--source/XMLParser.h1402
-rw-r--r--source/squirrelbindings/SquirrelArray.h112
-rw-r--r--source/squirrelbindings/SquirrelBaseClass.h132
-rw-r--r--source/squirrelbindings/SquirrelBindings.cpp390
-rw-r--r--source/squirrelbindings/SquirrelBindings.h64
-rw-r--r--source/squirrelbindings/SquirrelFunctions.cpp188
-rw-r--r--source/squirrelbindings/SquirrelFunctions.h56
-rw-r--r--source/squirrelbindings/SquirrelObject.h110
-rw-r--r--source/virtual_method_hooks.lua1012
224 files changed, 35266 insertions, 33800 deletions
diff --git a/source/AllToLua.bat b/source/AllToLua.bat
index 584f57423..75a028228 100644
--- a/source/AllToLua.bat
+++ b/source/AllToLua.bat
@@ -1,3 +1,25 @@
-"tolua++.exe" -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
-
-if %ALLTOLUA_WAIT%N == N pause
+
+:: AllToLua.bat
+
+:: This scripts updates the automatically-generates Lua bindings in Bindings.cpp / Bindings.h
+
+
+
+
+
+:: If there was a Git conflict, resolve it by resetting to HEAD; we're regenerating the files from scratch anyway
+git checkout -- Bindings.cpp
+git checkout -- Bindings.h
+
+
+
+
+
+:: Regenerate the files:
+"tolua++.exe" -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
+
+
+
+
+
+if %ALLTOLUA_WAIT%N == N pause
diff --git a/source/Bindings.cpp b/source/Bindings.cpp
index fd85abba9..df45cb1b3 100644
--- a/source/Bindings.cpp
+++ b/source/Bindings.cpp
@@ -1,6 +1,6 @@
/*
** Lua binding: AllToLua
-** Generated automatically by tolua++-1.0.92 on 07/26/13 21:48:46.
+** Generated automatically by tolua++-1.0.92 on 08/02/13 08:41:18.
*/
#ifndef __cplusplus
@@ -70,9 +70,9 @@ static int tolua_collect_cItem (lua_State* tolua_S)
return 0;
}
-static int tolua_collect_Vector3f (lua_State* tolua_S)
+static int tolua_collect_cFurnaceEntity (lua_State* tolua_S)
{
- Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0);
+ cFurnaceEntity* self = (cFurnaceEntity*) tolua_tousertype(tolua_S,1,0);
Mtolua_delete(self);
return 0;
}
@@ -140,9 +140,9 @@ static int tolua_collect_cPickup (lua_State* tolua_S)
return 0;
}
-static int tolua_collect_cItems (lua_State* tolua_S)
+static int tolua_collect_sWebAdminPage (lua_State* tolua_S)
{
- cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0);
+ sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0);
Mtolua_delete(self);
return 0;
}
@@ -161,16 +161,16 @@ static int tolua_collect_cTracer (lua_State* tolua_S)
return 0;
}
-static int tolua_collect_Vector3i (lua_State* tolua_S)
+static int tolua_collect_Vector3f (lua_State* tolua_S)
{
- Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0);
+ Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0);
Mtolua_delete(self);
return 0;
}
-static int tolua_collect_cFurnaceEntity (lua_State* tolua_S)
+static int tolua_collect_Vector3i (lua_State* tolua_S)
{
- cFurnaceEntity* self = (cFurnaceEntity*) tolua_tousertype(tolua_S,1,0);
+ Vector3i* self = (Vector3i*) tolua_tousertype(tolua_S,1,0);
Mtolua_delete(self);
return 0;
}
@@ -182,6 +182,13 @@ static int tolua_collect_cIniFile (lua_State* tolua_S)
return 0;
}
+static int tolua_collect_cItems (lua_State* tolua_S)
+{
+ cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0);
+ Mtolua_delete(self);
+ return 0;
+}
+
static int tolua_collect_Vector3d (lua_State* tolua_S)
{
Vector3d* self = (Vector3d*) tolua_tousertype(tolua_S,1,0);
@@ -205,32 +212,34 @@ static void tolua_reg_types (lua_State* tolua_S)
tolua_usertype(tolua_S,"cInventory");
tolua_usertype(tolua_S,"cRoot");
tolua_usertype(tolua_S,"cWindow");
+ tolua_usertype(tolua_S,"cCraftingGrid");
+ tolua_usertype(tolua_S,"cTracer");
tolua_usertype(tolua_S,"cPickup");
tolua_usertype(tolua_S,"cItems");
- tolua_usertype(tolua_S,"cCraftingGrid");
+ tolua_usertype(tolua_S,"cGroup");
tolua_usertype(tolua_S,"cClientHandle");
tolua_usertype(tolua_S,"cChunkDesc");
tolua_usertype(tolua_S,"cFurnaceRecipe");
- tolua_usertype(tolua_S,"cGroup");
- tolua_usertype(tolua_S,"cChatColor");
- tolua_usertype(tolua_S,"cTracer");
tolua_usertype(tolua_S,"cCuboid");
- tolua_usertype(tolua_S,"Lua__cWebPlugin");
+ tolua_usertype(tolua_S,"cChatColor");
tolua_usertype(tolua_S,"Vector3i");
tolua_usertype(tolua_S,"cEntity");
+ tolua_usertype(tolua_S,"Lua__cWebPlugin");
+ tolua_usertype(tolua_S,"cPlugin");
+ tolua_usertype(tolua_S,"cCraftingRecipes");
tolua_usertype(tolua_S,"cItem");
tolua_usertype(tolua_S,"Vector3f");
- tolua_usertype(tolua_S,"cWebAdmin");
+ tolua_usertype(tolua_S,"Lua__cPickup");
tolua_usertype(tolua_S,"cDropSpenserEntity");
tolua_usertype(tolua_S,"Lua__cPlayer");
- tolua_usertype(tolua_S,"cCraftingRecipes");
+ tolua_usertype(tolua_S,"cWebPlugin");
tolua_usertype(tolua_S,"cChestEntity");
tolua_usertype(tolua_S,"cDispenserEntity");
- tolua_usertype(tolua_S,"cPlugin");
+ tolua_usertype(tolua_S,"cWebAdmin");
tolua_usertype(tolua_S,"cBlockEntity");
tolua_usertype(tolua_S,"cCriticalSection");
- tolua_usertype(tolua_S,"Lua__cPickup");
- tolua_usertype(tolua_S,"cWebPlugin");
+ tolua_usertype(tolua_S,"HTTPTemplateRequest");
+ tolua_usertype(tolua_S,"sWebAdminPage");
tolua_usertype(tolua_S,"HTTPRequest");
tolua_usertype(tolua_S,"HTTPFormData");
tolua_usertype(tolua_S,"cFurnaceEntity");
@@ -8439,194 +8448,162 @@ static int tolua_AllToLua_cPlayer_GetGameMode00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
-/* method: GetIP of class cPlayer */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetIP00
-static int tolua_AllToLua_cPlayer_GetIP00(lua_State* tolua_S)
+/* method: SetGameMode of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetGameMode00
+static int tolua_AllToLua_cPlayer_SetGameMode00(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
!tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,2,&tolua_err)
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
)
goto tolua_lerror;
else
#endif
{
cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ eGameMode a_GameMode = ((eGameMode) (int) tolua_tonumber(tolua_S,2,0));
#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetIP'", NULL);
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetGameMode'", NULL);
#endif
{
- std::string tolua_ret = (std::string) self->GetIP();
- tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ self->SetGameMode(a_GameMode);
}
}
- return 1;
+ return 0;
#ifndef TOLUA_RELEASE
tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'GetIP'.",&tolua_err);
+ tolua_error(tolua_S,"#ferror in function 'SetGameMode'.",&tolua_err);
return 0;
#endif
}
#endif //#ifndef TOLUA_DISABLE
-/* method: GetLastBlockActionTime of class cPlayer */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetLastBlockActionTime00
-static int tolua_AllToLua_cPlayer_GetLastBlockActionTime00(lua_State* tolua_S)
+/* method: IsGameModeCreative of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsGameModeCreative00
+static int tolua_AllToLua_cPlayer_IsGameModeCreative00(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
- !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
!tolua_isnoobj(tolua_S,2,&tolua_err)
)
goto tolua_lerror;
else
#endif
{
- cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLastBlockActionTime'", NULL);
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeCreative'", NULL);
#endif
{
- float tolua_ret = (float) self->GetLastBlockActionTime();
- tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ bool tolua_ret = (bool) self->IsGameModeCreative();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
}
}
return 1;
#ifndef TOLUA_RELEASE
tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'GetLastBlockActionTime'.",&tolua_err);
+ tolua_error(tolua_S,"#ferror in function 'IsGameModeCreative'.",&tolua_err);
return 0;
#endif
}
#endif //#ifndef TOLUA_DISABLE
-/* method: GetLastBlockActionCnt of class cPlayer */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetLastBlockActionCnt00
-static int tolua_AllToLua_cPlayer_GetLastBlockActionCnt00(lua_State* tolua_S)
+/* method: IsGameModeSurvival of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsGameModeSurvival00
+static int tolua_AllToLua_cPlayer_IsGameModeSurvival00(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
- !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
!tolua_isnoobj(tolua_S,2,&tolua_err)
)
goto tolua_lerror;
else
#endif
{
- cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetLastBlockActionCnt'", NULL);
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeSurvival'", NULL);
#endif
{
- int tolua_ret = (int) self->GetLastBlockActionCnt();
- tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ bool tolua_ret = (bool) self->IsGameModeSurvival();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
}
}
return 1;
#ifndef TOLUA_RELEASE
tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'GetLastBlockActionCnt'.",&tolua_err);
- return 0;
-#endif
-}
-#endif //#ifndef TOLUA_DISABLE
-
-/* method: SetLastBlockActionCnt of class cPlayer */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetLastBlockActionCnt00
-static int tolua_AllToLua_cPlayer_SetLastBlockActionCnt00(lua_State* tolua_S)
-{
-#ifndef TOLUA_RELEASE
- tolua_Error tolua_err;
- if (
- !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
- !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,3,&tolua_err)
- )
- goto tolua_lerror;
- else
-#endif
- {
- cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
- int tolua_var_1 = ((int) tolua_tonumber(tolua_S,2,0));
-#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetLastBlockActionCnt'", NULL);
-#endif
- {
- self->SetLastBlockActionCnt(tolua_var_1);
- }
- }
- return 0;
-#ifndef TOLUA_RELEASE
- tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'SetLastBlockActionCnt'.",&tolua_err);
+ tolua_error(tolua_S,"#ferror in function 'IsGameModeSurvival'.",&tolua_err);
return 0;
#endif
}
#endif //#ifndef TOLUA_DISABLE
-/* method: SetLastBlockActionTime of class cPlayer */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetLastBlockActionTime00
-static int tolua_AllToLua_cPlayer_SetLastBlockActionTime00(lua_State* tolua_S)
+/* method: IsGameModeAdventure of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsGameModeAdventure00
+static int tolua_AllToLua_cPlayer_IsGameModeAdventure00(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
- !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
!tolua_isnoobj(tolua_S,2,&tolua_err)
)
goto tolua_lerror;
else
#endif
{
- cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetLastBlockActionTime'", NULL);
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeAdventure'", NULL);
#endif
{
- self->SetLastBlockActionTime();
+ bool tolua_ret = (bool) self->IsGameModeAdventure();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
}
}
- return 0;
+ return 1;
#ifndef TOLUA_RELEASE
tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'SetLastBlockActionTime'.",&tolua_err);
+ tolua_error(tolua_S,"#ferror in function 'IsGameModeAdventure'.",&tolua_err);
return 0;
#endif
}
#endif //#ifndef TOLUA_DISABLE
-/* method: SetGameMode of class cPlayer */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetGameMode00
-static int tolua_AllToLua_cPlayer_SetGameMode00(lua_State* tolua_S)
+/* method: GetIP of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_GetIP00
+static int tolua_AllToLua_cPlayer_GetIP00(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
- !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) ||
- !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,3,&tolua_err)
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
)
goto tolua_lerror;
else
#endif
{
- cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
- eGameMode a_GameMode = ((eGameMode) (int) tolua_tonumber(tolua_S,2,0));
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetGameMode'", NULL);
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetIP'", NULL);
#endif
{
- self->SetGameMode(a_GameMode);
+ AString tolua_ret = (AString) self->GetIP();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
}
}
- return 0;
+ return 1;
#ifndef TOLUA_RELEASE
tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'SetGameMode'.",&tolua_err);
+ tolua_error(tolua_S,"#ferror in function 'GetIP'.",&tolua_err);
return 0;
#endif
}
@@ -9334,6 +9311,38 @@ static int tolua_AllToLua_cPlayer_GetFoodPoisonedTicksRemaining00(lua_State* tol
}
#endif //#ifndef TOLUA_DISABLE
+/* method: IsSatiated of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsSatiated00
+static int tolua_AllToLua_cPlayer_IsSatiated00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsSatiated'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsSatiated();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsSatiated'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
/* method: SetFoodLevel of class cPlayer */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_SetFoodLevel00
static int tolua_AllToLua_cPlayer_SetFoodLevel00(lua_State* tolua_S)
@@ -9601,6 +9610,38 @@ static int tolua_AllToLua_cPlayer_FoodPoison00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
+/* method: IsEating of class cPlayer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_IsEating00
+static int tolua_AllToLua_cPlayer_IsEating00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsEating'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsEating();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsEating'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
/* method: Respawn of class cPlayer */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlayer_Respawn00
static int tolua_AllToLua_cPlayer_Respawn00(lua_State* tolua_S)
@@ -11092,6 +11133,38 @@ static int tolua_AllToLua_cServer_SendMessage00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
+/* method: GetServerID of class cServer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_GetServerID00
+static int tolua_AllToLua_cServer_GetServerID00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cServer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cServer* self = (const cServer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetServerID'", NULL);
+#endif
+ {
+ const AString tolua_ret = (const AString) self->GetServerID();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetServerID'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
/* method: GetClassStatic of class cWorld */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetClassStatic00
static int tolua_AllToLua_cWorld_GetClassStatic00(lua_State* tolua_S)
@@ -11375,6 +11448,102 @@ static int tolua_AllToLua_cWorld_GetGameMode00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
+/* method: IsGameModeCreative of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsGameModeCreative00
+static int tolua_AllToLua_cWorld_IsGameModeCreative00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeCreative'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsGameModeCreative();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsGameModeCreative'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsGameModeSurvival of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsGameModeSurvival00
+static int tolua_AllToLua_cWorld_IsGameModeSurvival00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeSurvival'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsGameModeSurvival();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsGameModeSurvival'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: IsGameModeAdventure of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsGameModeAdventure00
+static int tolua_AllToLua_cWorld_IsGameModeAdventure00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsGameModeAdventure'", NULL);
+#endif
+ {
+ bool tolua_ret = (bool) self->IsGameModeAdventure();
+ tolua_pushboolean(tolua_S,(bool)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'IsGameModeAdventure'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
/* method: IsPVPEnabled of class cWorld */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_IsPVPEnabled00
static int tolua_AllToLua_cWorld_IsPVPEnabled00(lua_State* tolua_S)
@@ -18410,7 +18579,7 @@ static int tolua_set_HTTPRequest_Method(lua_State* tolua_S)
if (!tolua_iscppstring(tolua_S,2,0,&tolua_err))
tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
#endif
- self->Method = ((std::string) tolua_tocppstring(tolua_S,2,0))
+ self->Method = ((AString) tolua_tocppstring(tolua_S,2,0))
;
return 0;
}
@@ -18440,7 +18609,7 @@ static int tolua_set_HTTPRequest_Path(lua_State* tolua_S)
if (!tolua_iscppstring(tolua_S,2,0,&tolua_err))
tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
#endif
- self->Path = ((std::string) tolua_tocppstring(tolua_S,2,0))
+ self->Path = ((AString) tolua_tocppstring(tolua_S,2,0))
;
return 0;
}
@@ -18470,12 +18639,303 @@ static int tolua_set_HTTPRequest_Username(lua_State* tolua_S)
if (!tolua_iscppstring(tolua_S,2,0,&tolua_err))
tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
#endif
- self->Username = ((std::string) tolua_tocppstring(tolua_S,2,0))
+ self->Username = ((AString) tolua_tocppstring(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Request of class HTTPTemplateRequest */
+#ifndef TOLUA_DISABLE_tolua_get_HTTPTemplateRequest_Request
+static int tolua_get_HTTPTemplateRequest_Request(lua_State* tolua_S)
+{
+ HTTPTemplateRequest* self = (HTTPTemplateRequest*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Request'",NULL);
+#endif
+ tolua_pushusertype(tolua_S,(void*)&self->Request,"HTTPRequest");
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: Request of class HTTPTemplateRequest */
+#ifndef TOLUA_DISABLE_tolua_set_HTTPTemplateRequest_Request
+static int tolua_set_HTTPTemplateRequest_Request(lua_State* tolua_S)
+{
+ HTTPTemplateRequest* self = (HTTPTemplateRequest*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Request'",NULL);
+ if ((tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"HTTPRequest",0,&tolua_err)))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->Request = *((HTTPRequest*) tolua_tousertype(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: Content of class sWebAdminPage */
+#ifndef TOLUA_DISABLE_tolua_get_sWebAdminPage_Content
+static int tolua_get_sWebAdminPage_Content(lua_State* tolua_S)
+{
+ sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Content'",NULL);
+#endif
+ tolua_pushcppstring(tolua_S,(const char*)self->Content);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: Content of class sWebAdminPage */
+#ifndef TOLUA_DISABLE_tolua_set_sWebAdminPage_Content
+static int tolua_set_sWebAdminPage_Content(lua_State* tolua_S)
+{
+ sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'Content'",NULL);
+ if (!tolua_iscppstring(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->Content = ((AString) tolua_tocppstring(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: PluginName of class sWebAdminPage */
+#ifndef TOLUA_DISABLE_tolua_get_sWebAdminPage_PluginName
+static int tolua_get_sWebAdminPage_PluginName(lua_State* tolua_S)
+{
+ sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'PluginName'",NULL);
+#endif
+ tolua_pushcppstring(tolua_S,(const char*)self->PluginName);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: PluginName of class sWebAdminPage */
+#ifndef TOLUA_DISABLE_tolua_set_sWebAdminPage_PluginName
+static int tolua_set_sWebAdminPage_PluginName(lua_State* tolua_S)
+{
+ sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'PluginName'",NULL);
+ if (!tolua_iscppstring(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->PluginName = ((AString) tolua_tocppstring(tolua_S,2,0))
+;
+ return 0;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* get function: TabName of class sWebAdminPage */
+#ifndef TOLUA_DISABLE_tolua_get_sWebAdminPage_TabName
+static int tolua_get_sWebAdminPage_TabName(lua_State* tolua_S)
+{
+ sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'TabName'",NULL);
+#endif
+ tolua_pushcppstring(tolua_S,(const char*)self->TabName);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* set function: TabName of class sWebAdminPage */
+#ifndef TOLUA_DISABLE_tolua_set_sWebAdminPage_TabName
+static int tolua_set_sWebAdminPage_TabName(lua_State* tolua_S)
+{
+ sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'TabName'",NULL);
+ if (!tolua_iscppstring(tolua_S,2,0,&tolua_err))
+ tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err);
+#endif
+ self->TabName = ((AString) tolua_tocppstring(tolua_S,2,0))
;
return 0;
}
#endif //#ifndef TOLUA_DISABLE
+/* method: GetPort of class cWebAdmin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetPort00
+static int tolua_AllToLua_cWebAdmin_GetPort00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWebAdmin",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPort'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetPort();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetPort'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetPage of class cWebAdmin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetPage00
+static int tolua_AllToLua_cWebAdmin_GetPage00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWebAdmin",0,&tolua_err) ||
+ (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const HTTPRequest",0,&tolua_err)) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0);
+ const HTTPRequest* a_Request = ((const HTTPRequest*) tolua_tousertype(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPage'", NULL);
+#endif
+ {
+ sWebAdminPage tolua_ret = (sWebAdminPage) self->GetPage(*a_Request);
+ {
+#ifdef __cplusplus
+ void* tolua_obj = Mtolua_new((sWebAdminPage)(tolua_ret));
+ tolua_pushusertype(tolua_S,tolua_obj,"sWebAdminPage");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#else
+ void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(sWebAdminPage));
+ tolua_pushusertype(tolua_S,tolua_obj,"sWebAdminPage");
+ tolua_register_gc(tolua_S,lua_gettop(tolua_S));
+#endif
+ }
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetPage'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetBaseURL of class cWebAdmin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetBaseURL00
+static int tolua_AllToLua_cWebAdmin_GetBaseURL00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWebAdmin",0,&tolua_err) ||
+ !tolua_iscppstring(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0);
+ const AString a_URL = ((const AString) tolua_tocppstring(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBaseURL'", NULL);
+#endif
+ {
+ AString tolua_ret = (AString) self->GetBaseURL(a_URL);
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ tolua_pushcppstring(tolua_S,(const char*)a_URL);
+ }
+ }
+ return 2;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetBaseURL'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetMemoryUsage of class cWebAdmin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetMemoryUsage00
+static int tolua_AllToLua_cWebAdmin_GetMemoryUsage00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertable(tolua_S,1,"cWebAdmin",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ {
+ AString tolua_ret = (AString) cWebAdmin::GetMemoryUsage();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetMemoryUsage'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetWebTitle of class cWebPlugin */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebPlugin_GetWebTitle00
+static int tolua_AllToLua_cWebPlugin_GetWebTitle00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cWebPlugin",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cWebPlugin* self = (const cWebPlugin*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWebTitle'", NULL);
+#endif
+ {
+ const AString tolua_ret = (const AString) self->GetWebTitle();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetWebTitle'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
/* method: HandleWebRequest of class cWebPlugin */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebPlugin_HandleWebRequest00
static int tolua_AllToLua_cWebPlugin_HandleWebRequest00(lua_State* tolua_S)
@@ -18484,7 +18944,7 @@ static int tolua_AllToLua_cWebPlugin_HandleWebRequest00(lua_State* tolua_S)
tolua_Error tolua_err;
if (
!tolua_isusertype(tolua_S,1,"cWebPlugin",0,&tolua_err) ||
- !tolua_isusertype(tolua_S,2,"HTTPRequest",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,2,"const HTTPRequest",0,&tolua_err) ||
!tolua_isnoobj(tolua_S,3,&tolua_err)
)
goto tolua_lerror;
@@ -18492,7 +18952,7 @@ static int tolua_AllToLua_cWebPlugin_HandleWebRequest00(lua_State* tolua_S)
#endif
{
cWebPlugin* self = (cWebPlugin*) tolua_tousertype(tolua_S,1,0);
- HTTPRequest* a_Request = ((HTTPRequest*) tolua_tousertype(tolua_S,2,0));
+ const HTTPRequest* a_Request = ((const HTTPRequest*) tolua_tousertype(tolua_S,2,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'HandleWebRequest'", NULL);
#endif
@@ -18543,9 +19003,25 @@ static int tolua_AllToLua_cWebPlugin_SafeString00(lua_State* tolua_S)
class Lua__cWebPlugin : public cWebPlugin, public ToluaBase {
public:
- AString HandleWebRequest( HTTPRequest* a_Request) {
+ const AString GetWebTitle( void )const {
+ if (push_method("GetWebTitle", tolua_AllToLua_cWebPlugin_GetWebTitle00)) {
+ ToluaBase::dbcall(lua_state, 1, 1);
+ const AString tolua_ret = ( const AString )tolua_tocppstring(lua_state, -1, 0);
+ lua_pop(lua_state, 1);
+ return tolua_ret;
+ } else {
+ if (lua_state)
+ LOG("pure-virtual method cWebPlugin::GetWebTitle not implemented.");
+ else {
+ LOG("pure-virtual method cWebPlugin::GetWebTitle called with no lua_state. Aborting");
+ ::abort();
+ };
+ return ( const AString )0;
+ };
+ };
+ AString HandleWebRequest( const HTTPRequest* a_Request) {
if (push_method("HandleWebRequest", tolua_AllToLua_cWebPlugin_HandleWebRequest00)) {
- tolua_pushusertype(lua_state, (void*)a_Request, "HTTPRequest");
+ tolua_pushusertype(lua_state, (void*)a_Request, "const HTTPRequest");
ToluaBase::dbcall(lua_state, 2, 1);
AString tolua_ret = ( AString )tolua_tocppstring(lua_state, -1, 0);
lua_pop(lua_state, 1);
@@ -28665,6 +29141,8 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_constant(tolua_S,"gmSurvival",gmSurvival);
tolua_constant(tolua_S,"gmCreative",gmCreative);
tolua_constant(tolua_S,"gmAdventure",gmAdventure);
+ tolua_constant(tolua_S,"gmMax",gmMax);
+ tolua_constant(tolua_S,"gmMin",gmMin);
tolua_constant(tolua_S,"eWeather_Sunny",eWeather_Sunny);
tolua_constant(tolua_S,"eWeather_Rain",eWeather_Rain);
tolua_constant(tolua_S,"eWeather_ThunderStorm",eWeather_ThunderStorm);
@@ -28892,6 +29370,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_beginmodule(tolua_S,"cPlayer");
tolua_constant(tolua_S,"MAX_HEALTH",cPlayer::MAX_HEALTH);
tolua_constant(tolua_S,"MAX_FOOD_LEVEL",cPlayer::MAX_FOOD_LEVEL);
+ tolua_constant(tolua_S,"EATING_TICKS",cPlayer::EATING_TICKS);
tolua_function(tolua_S,"Initialize",tolua_AllToLua_cPlayer_Initialize00);
tolua_function(tolua_S,"GetEyeHeight",tolua_AllToLua_cPlayer_GetEyeHeight00);
tolua_function(tolua_S,"GetEyePosition",tolua_AllToLua_cPlayer_GetEyePosition00);
@@ -28900,12 +29379,11 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"GetInventory",tolua_AllToLua_cPlayer_GetInventory00);
tolua_function(tolua_S,"GetEquippedItem",tolua_AllToLua_cPlayer_GetEquippedItem00);
tolua_function(tolua_S,"GetGameMode",tolua_AllToLua_cPlayer_GetGameMode00);
- tolua_function(tolua_S,"GetIP",tolua_AllToLua_cPlayer_GetIP00);
- tolua_function(tolua_S,"GetLastBlockActionTime",tolua_AllToLua_cPlayer_GetLastBlockActionTime00);
- tolua_function(tolua_S,"GetLastBlockActionCnt",tolua_AllToLua_cPlayer_GetLastBlockActionCnt00);
- tolua_function(tolua_S,"SetLastBlockActionCnt",tolua_AllToLua_cPlayer_SetLastBlockActionCnt00);
- tolua_function(tolua_S,"SetLastBlockActionTime",tolua_AllToLua_cPlayer_SetLastBlockActionTime00);
tolua_function(tolua_S,"SetGameMode",tolua_AllToLua_cPlayer_SetGameMode00);
+ tolua_function(tolua_S,"IsGameModeCreative",tolua_AllToLua_cPlayer_IsGameModeCreative00);
+ tolua_function(tolua_S,"IsGameModeSurvival",tolua_AllToLua_cPlayer_IsGameModeSurvival00);
+ tolua_function(tolua_S,"IsGameModeAdventure",tolua_AllToLua_cPlayer_IsGameModeAdventure00);
+ tolua_function(tolua_S,"GetIP",tolua_AllToLua_cPlayer_GetIP00);
tolua_function(tolua_S,"MoveTo",tolua_AllToLua_cPlayer_MoveTo00);
tolua_function(tolua_S,"GetWindow",tolua_AllToLua_cPlayer_GetWindow00);
tolua_function(tolua_S,"CloseWindow",tolua_AllToLua_cPlayer_CloseWindow00);
@@ -28927,6 +29405,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"GetFoodTickTimer",tolua_AllToLua_cPlayer_GetFoodTickTimer00);
tolua_function(tolua_S,"GetFoodExhaustionLevel",tolua_AllToLua_cPlayer_GetFoodExhaustionLevel00);
tolua_function(tolua_S,"GetFoodPoisonedTicksRemaining",tolua_AllToLua_cPlayer_GetFoodPoisonedTicksRemaining00);
+ tolua_function(tolua_S,"IsSatiated",tolua_AllToLua_cPlayer_IsSatiated00);
tolua_function(tolua_S,"SetFoodLevel",tolua_AllToLua_cPlayer_SetFoodLevel00);
tolua_function(tolua_S,"SetFoodSaturationLevel",tolua_AllToLua_cPlayer_SetFoodSaturationLevel00);
tolua_function(tolua_S,"SetFoodTickTimer",tolua_AllToLua_cPlayer_SetFoodTickTimer00);
@@ -28935,6 +29414,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"Feed",tolua_AllToLua_cPlayer_Feed00);
tolua_function(tolua_S,"AddFoodExhaustion",tolua_AllToLua_cPlayer_AddFoodExhaustion00);
tolua_function(tolua_S,"FoodPoison",tolua_AllToLua_cPlayer_FoodPoison00);
+ tolua_function(tolua_S,"IsEating",tolua_AllToLua_cPlayer_IsEating00);
tolua_function(tolua_S,"Respawn",tolua_AllToLua_cPlayer_Respawn00);
tolua_function(tolua_S,"SetVisible",tolua_AllToLua_cPlayer_SetVisible00);
tolua_function(tolua_S,"IsVisible",tolua_AllToLua_cPlayer_IsVisible00);
@@ -28979,6 +29459,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_constant(tolua_S,"HOOK_PLAYER_PLACED_BLOCK",cPluginManager::HOOK_PLAYER_PLACED_BLOCK);
tolua_constant(tolua_S,"HOOK_PLAYER_PLACING_BLOCK",cPluginManager::HOOK_PLAYER_PLACING_BLOCK);
tolua_constant(tolua_S,"HOOK_PLAYER_RIGHT_CLICK",cPluginManager::HOOK_PLAYER_RIGHT_CLICK);
+ tolua_constant(tolua_S,"HOOK_PLAYER_RIGHT_CLICKING_ENTITY",cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY);
tolua_constant(tolua_S,"HOOK_PLAYER_SHOOTING",cPluginManager::HOOK_PLAYER_SHOOTING);
tolua_constant(tolua_S,"HOOK_PLAYER_SPAWNED",cPluginManager::HOOK_PLAYER_SPAWNED);
tolua_constant(tolua_S,"HOOK_PLAYER_TOSSING_ITEM",cPluginManager::HOOK_PLAYER_TOSSING_ITEM);
@@ -29025,6 +29506,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_beginmodule(tolua_S,"cServer");
tolua_function(tolua_S,"BroadcastChat",tolua_AllToLua_cServer_BroadcastChat00);
tolua_function(tolua_S,"SendMessage",tolua_AllToLua_cServer_SendMessage00);
+ tolua_function(tolua_S,"GetServerID",tolua_AllToLua_cServer_GetServerID00);
tolua_endmodule(tolua_S);
tolua_cclass(tolua_S,"cWorld","cWorld","",NULL);
tolua_beginmodule(tolua_S,"cWorld");
@@ -29037,6 +29519,9 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"SetTimeOfDay",tolua_AllToLua_cWorld_SetTimeOfDay00);
tolua_function(tolua_S,"SetWorldTime",tolua_AllToLua_cWorld_SetWorldTime00);
tolua_function(tolua_S,"GetGameMode",tolua_AllToLua_cWorld_GetGameMode00);
+ tolua_function(tolua_S,"IsGameModeCreative",tolua_AllToLua_cWorld_IsGameModeCreative00);
+ tolua_function(tolua_S,"IsGameModeSurvival",tolua_AllToLua_cWorld_IsGameModeSurvival00);
+ tolua_function(tolua_S,"IsGameModeAdventure",tolua_AllToLua_cWorld_IsGameModeAdventure00);
tolua_function(tolua_S,"IsPVPEnabled",tolua_AllToLua_cWorld_IsPVPEnabled00);
tolua_function(tolua_S,"IsDeepSnowEnabled",tolua_AllToLua_cWorld_IsDeepSnowEnabled00);
tolua_function(tolua_S,"GetDimension",tolua_AllToLua_cWorld_GetDimension00);
@@ -29372,8 +29857,30 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_variable(tolua_S,"Path",tolua_get_HTTPRequest_Path,tolua_set_HTTPRequest_Path);
tolua_variable(tolua_S,"Username",tolua_get_HTTPRequest_Username,tolua_set_HTTPRequest_Username);
tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"HTTPTemplateRequest","HTTPTemplateRequest","",NULL);
+ tolua_beginmodule(tolua_S,"HTTPTemplateRequest");
+ tolua_variable(tolua_S,"Request",tolua_get_HTTPTemplateRequest_Request,tolua_set_HTTPTemplateRequest_Request);
+ tolua_endmodule(tolua_S);
+ #ifdef __cplusplus
+ tolua_cclass(tolua_S,"sWebAdminPage","sWebAdminPage","",tolua_collect_sWebAdminPage);
+ #else
+ tolua_cclass(tolua_S,"sWebAdminPage","sWebAdminPage","",NULL);
+ #endif
+ tolua_beginmodule(tolua_S,"sWebAdminPage");
+ tolua_variable(tolua_S,"Content",tolua_get_sWebAdminPage_Content,tolua_set_sWebAdminPage_Content);
+ tolua_variable(tolua_S,"PluginName",tolua_get_sWebAdminPage_PluginName,tolua_set_sWebAdminPage_PluginName);
+ tolua_variable(tolua_S,"TabName",tolua_get_sWebAdminPage_TabName,tolua_set_sWebAdminPage_TabName);
+ tolua_endmodule(tolua_S);
+ tolua_cclass(tolua_S,"cWebAdmin","cWebAdmin","",NULL);
+ tolua_beginmodule(tolua_S,"cWebAdmin");
+ tolua_function(tolua_S,"GetPort",tolua_AllToLua_cWebAdmin_GetPort00);
+ tolua_function(tolua_S,"GetPage",tolua_AllToLua_cWebAdmin_GetPage00);
+ tolua_function(tolua_S,"GetBaseURL",tolua_AllToLua_cWebAdmin_GetBaseURL00);
+ tolua_function(tolua_S,"GetMemoryUsage",tolua_AllToLua_cWebAdmin_GetMemoryUsage00);
+ tolua_endmodule(tolua_S);
tolua_cclass(tolua_S,"cWebPlugin","cWebPlugin","",NULL);
tolua_beginmodule(tolua_S,"cWebPlugin");
+ tolua_function(tolua_S,"GetWebTitle",tolua_AllToLua_cWebPlugin_GetWebTitle00);
tolua_function(tolua_S,"HandleWebRequest",tolua_AllToLua_cWebPlugin_HandleWebRequest00);
tolua_function(tolua_S,"SafeString",tolua_AllToLua_cWebPlugin_SafeString00);
tolua_endmodule(tolua_S);
diff --git a/source/Bindings.h b/source/Bindings.h
index c21612525..6dfe855d3 100644
--- a/source/Bindings.h
+++ b/source/Bindings.h
@@ -1,6 +1,6 @@
/*
** Lua binding: AllToLua
-** Generated automatically by tolua++-1.0.92 on 07/26/13 21:48:46.
+** Generated automatically by tolua++-1.0.92 on 08/02/13 08:41:19.
*/
/* Exported function */
diff --git a/source/BlockArea.cpp b/source/BlockArea.cpp
index 6134843bb..5c15adfef 100644
--- a/source/BlockArea.cpp
+++ b/source/BlockArea.cpp
@@ -1,2124 +1,2124 @@
-
-// BlockArea.cpp
-
-// Implements the cBlockArea object representing an area of block data that can be queried from cWorld and then accessed again without further queries
-// The object also supports writing the blockdata back into cWorld, even into other coords
-
-#include "Globals.h"
-#include "BlockArea.h"
-#include "World.h"
-#include "OSSupport/GZipFile.h"
-#include "WorldStorage/FastNBT.h"
-#include "Blocks/BlockHandler.h"
-
-
-
-
-
-// This wild construct allows us to pass a function argument and still have it inlined by the compiler :)
-/// Merges two blocktypes and blockmetas of the specified sizes and offsets using the specified combinator function
-template<typename Combinator> void InternalMergeBlocks(
- BLOCKTYPE * a_DstTypes, const BLOCKTYPE * a_SrcTypes,
- NIBBLETYPE * a_DstMetas, const NIBBLETYPE * a_SrcMetas,
- int a_SizeX, int a_SizeY, int a_SizeZ,
- int a_SrcOffX, int a_SrcOffY, int a_SrcOffZ,
- int a_DstOffX, int a_DstOffY, int a_DstOffZ,
- int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ,
- int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ,
- Combinator a_Combinator
-)
-{
- for (int y = 0; y < a_SizeY; y++)
- {
- int SrcBaseY = (y + a_SrcOffY) * a_SrcSizeX * a_SrcSizeZ;
- int DstBaseY = (y + a_DstOffY) * a_DstSizeX * a_DstSizeZ;
- for (int z = 0; z < a_SizeZ; z++)
- {
- int SrcBaseZ = SrcBaseY + (z + a_SrcOffZ) * a_SrcSizeX;
- int DstBaseZ = DstBaseY + (z + a_DstOffZ) * a_DstSizeX;
- int SrcIdx = SrcBaseZ + a_SrcOffX;
- int DstIdx = DstBaseZ + a_DstOffX;
- for (int x = 0; x < a_SizeX; x++)
- {
- a_Combinator(a_DstTypes[DstIdx], a_SrcTypes[SrcIdx], a_DstMetas[DstIdx], a_SrcMetas[SrcIdx]);
- ++DstIdx;
- ++SrcIdx;
- } // for x
- } // for z
- } // for y
-}
-
-
-
-
-
-/// Combinator used for cBlockArea::msOverwrite merging
-static void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
-{
- a_DstType = a_SrcType;
- a_DstMeta = a_SrcMeta;
-}
-
-
-
-
-
-/// Combinator used for cBlockArea::msFillAir merging
-static void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
-{
- if (a_DstType == E_BLOCK_AIR)
- {
- a_DstType = a_SrcType;
- a_DstMeta = a_SrcMeta;
- }
- // "else" is the default, already in place
-}
-
-
-
-
-
-/// Combinator used for cBlockArea::msImprint merging
-static void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
-{
- if (a_SrcType != E_BLOCK_AIR)
- {
- a_DstType = a_SrcType;
- a_DstMeta = a_SrcMeta;
- }
- // "else" is the default, already in place
-}
-
-
-
-
-
-/// Combinator used for cBlockArea::msLake merging
-static void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
-{
- // Sponge is the NOP block
- if (a_SrcType == E_BLOCK_SPONGE)
- {
- return;
- }
-
- // Air is always hollowed out
- if (a_SrcType == E_BLOCK_AIR)
- {
- a_DstType = E_BLOCK_AIR;
- a_DstMeta = 0;
- return;
- }
-
- // Water and lava are never overwritten
- switch (a_DstType)
- {
- case E_BLOCK_WATER:
- case E_BLOCK_STATIONARY_WATER:
- case E_BLOCK_LAVA:
- case E_BLOCK_STATIONARY_LAVA:
- {
- return;
- }
- }
-
- // Water and lava always overwrite
- switch (a_SrcType)
- {
- case E_BLOCK_WATER:
- case E_BLOCK_STATIONARY_WATER:
- case E_BLOCK_LAVA:
- case E_BLOCK_STATIONARY_LAVA:
- {
- a_DstType = a_SrcType;
- a_DstMeta = a_DstMeta;
- return;
- }
- }
-
- if (a_SrcType == E_BLOCK_STONE)
- {
- switch (a_DstType)
- {
- case E_BLOCK_DIRT:
- case E_BLOCK_GRASS:
- case E_BLOCK_MYCELIUM:
- {
- a_DstType = E_BLOCK_STONE;
- a_DstMeta = 0;
- return;
- }
- }
- }
- // Everything else is left as it is
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cBlockArea:
-
-cBlockArea::cBlockArea(void) :
- m_SizeX(0),
- m_SizeY(0),
- m_SizeZ(0),
- m_BlockTypes(NULL),
- m_BlockMetas(NULL),
- m_BlockLight(NULL),
- m_BlockSkyLight(NULL)
-{
-}
-
-
-
-
-
-cBlockArea::~cBlockArea()
-{
- Clear();
-}
-
-
-
-
-
-void cBlockArea::Clear(void)
-{
- delete[] m_BlockTypes; m_BlockTypes = NULL;
- delete[] m_BlockMetas; m_BlockMetas = NULL;
- delete[] m_BlockLight; m_BlockLight = NULL;
- delete[] m_BlockSkyLight; m_BlockSkyLight = NULL;
- m_SizeX = 0;
- m_SizeY = 0;
- m_SizeZ = 0;
-}
-
-
-
-
-
-void cBlockArea::Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
-{
- Clear();
- int BlockCount = a_SizeX * a_SizeY * a_SizeZ;
- if ((a_DataTypes & baTypes) != 0)
- {
- m_BlockTypes = new BLOCKTYPE[BlockCount];
- for (int i = 0; i < BlockCount; i++)
- {
- m_BlockTypes[i] = E_BLOCK_AIR;
- }
- }
- if ((a_DataTypes & baMetas) != 0)
- {
- m_BlockMetas = new NIBBLETYPE[BlockCount];
- for (int i = 0; i < BlockCount; i++)
- {
- m_BlockMetas[i] = 0;
- }
- }
- if ((a_DataTypes & baLight) != 0)
- {
- m_BlockLight = new NIBBLETYPE[BlockCount];
- for (int i = 0; i < BlockCount; i++)
- {
- m_BlockLight[i] = 0;
- }
- }
- if ((a_DataTypes & baSkyLight) != 0)
- {
- m_BlockSkyLight = new NIBBLETYPE[BlockCount];
- for (int i = 0; i < BlockCount; i++)
- {
- m_BlockSkyLight[i] = 0x0f;
- }
- }
- m_SizeX = a_SizeX;
- m_SizeY = a_SizeY;
- m_SizeZ = a_SizeZ;
- m_OriginX = 0;
- m_OriginY = 0;
- m_OriginZ = 0;
-}
-
-
-
-
-
-void cBlockArea::SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ)
-{
- m_OriginX = a_OriginX;
- m_OriginY = a_OriginY;
- m_OriginZ = a_OriginZ;
-}
-
-
-
-
-
-bool cBlockArea::Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes)
-{
- // Normalize the coords:
- if (a_MinBlockX > a_MaxBlockX)
- {
- std::swap(a_MinBlockX, a_MaxBlockX);
- }
- if (a_MinBlockY > a_MaxBlockY)
- {
- std::swap(a_MinBlockY, a_MaxBlockY);
- }
- if (a_MinBlockZ > a_MaxBlockZ)
- {
- std::swap(a_MinBlockZ, a_MaxBlockZ);
- }
-
- // Include the Max coords:
- a_MaxBlockX += 1;
- a_MaxBlockY += 1;
- a_MaxBlockZ += 1;
-
- // Check coords validity:
- if (a_MinBlockY < 0)
- {
- LOGWARNING("%s: MinBlockY less than zero, adjusting to zero", __FUNCTION__);
- a_MinBlockY = 0;
- }
- else if (a_MinBlockY >= cChunkDef::Height)
- {
- LOGWARNING("%s: MinBlockY more than chunk height, adjusting to chunk height", __FUNCTION__);
- a_MinBlockY = cChunkDef::Height - 1;
- }
- if (a_MaxBlockY < 0)
- {
- LOGWARNING("%s: MaxBlockY less than zero, adjusting to zero", __FUNCTION__);
- a_MaxBlockY = 0;
- }
- else if (a_MaxBlockY >= cChunkDef::Height)
- {
- LOGWARNING("%s: MaxBlockY more than chunk height, adjusting to chunk height", __FUNCTION__);
- a_MaxBlockY = cChunkDef::Height - 1;
- }
-
- // Allocate the needed memory:
- Clear();
- if (!SetSize(a_MaxBlockX - a_MinBlockX, a_MaxBlockY - a_MinBlockY, a_MaxBlockZ - a_MinBlockZ, a_DataTypes))
- {
- return false;
- }
- m_OriginX = a_MinBlockX;
- m_OriginY = a_MinBlockY;
- m_OriginZ = a_MinBlockZ;
- cChunkReader Reader(*this);
-
- // Convert block coords to chunks coords:
- int MinChunkX, MaxChunkX;
- int MinChunkZ, MaxChunkZ;
- cChunkDef::AbsoluteToRelative(a_MinBlockX, a_MinBlockY, a_MinBlockZ, MinChunkX, MinChunkZ);
- cChunkDef::AbsoluteToRelative(a_MaxBlockX, a_MaxBlockY, a_MaxBlockZ, MaxChunkX, MaxChunkZ);
-
- // Query block data:
- if (!a_World->ForEachChunkInRect(MinChunkX, MaxChunkX, MinChunkZ, MaxChunkZ, Reader))
- {
- Clear();
- return false;
- }
-
- return true;
-}
-
-
-
-
-
-bool cBlockArea::Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes)
-{
- ASSERT((a_DataTypes & GetDataTypes()) == a_DataTypes); // Are you requesting only the data that I have?
- a_DataTypes = a_DataTypes & GetDataTypes(); // For release builds, silently cut off the datatypes that I don't have
-
- // Check coords validity:
- if (a_MinBlockY < 0)
- {
- LOGWARNING("%s: MinBlockY less than zero, adjusting to zero", __FUNCTION__);
- a_MinBlockY = 0;
- }
- else if (a_MinBlockY >= cChunkDef::Height - m_SizeY)
- {
- LOGWARNING("%s: MinBlockY + m_SizeY more than chunk height, adjusting to chunk height", __FUNCTION__);
- a_MinBlockY = cChunkDef::Height - m_SizeY - 1;
- }
-
- return a_World->WriteBlockArea(*this, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes);
-}
-
-
-
-
-
-void cBlockArea::CopyTo(cBlockArea & a_Into) const
-{
- if (&a_Into == this)
- {
- LOGWARNING("Trying to copy a cBlockArea into self, ignoring.");
- return;
- }
-
- a_Into.Clear();
- a_Into.SetSize(m_SizeX, m_SizeY, m_SizeZ, GetDataTypes());
- a_Into.m_OriginX = m_OriginX;
- a_Into.m_OriginY = m_OriginY;
- a_Into.m_OriginZ = m_OriginZ;
- int BlockCount = GetBlockCount();
- if (HasBlockTypes())
- {
- memcpy(a_Into.m_BlockTypes, m_BlockTypes, BlockCount * sizeof(BLOCKTYPE));
- }
- if (HasBlockMetas())
- {
- memcpy(a_Into.m_BlockMetas, m_BlockMetas, BlockCount * sizeof(NIBBLETYPE));
- }
- if (HasBlockLights())
- {
- memcpy(a_Into.m_BlockLight, m_BlockLight, BlockCount * sizeof(NIBBLETYPE));
- }
- if (HasBlockSkyLights())
- {
- memcpy(a_Into.m_BlockSkyLight, m_BlockSkyLight, BlockCount * sizeof(NIBBLETYPE));
- }
-}
-
-
-
-
-
-void cBlockArea::CopyFrom(const cBlockArea & a_From)
-{
- a_From.CopyTo(*this);
-}
-
-
-
-
-
-void cBlockArea::DumpToRawFile(const AString & a_FileName)
-{
- cFile f;
- if (!f.Open(a_FileName, cFile::fmWrite))
- {
- LOGWARNING("cBlockArea: Cannot open file \"%s\" for raw dump", a_FileName.c_str());
- return;
- }
- UInt32 SizeX = ntohl(m_SizeX);
- UInt32 SizeY = ntohl(m_SizeY);
- UInt32 SizeZ = ntohl(m_SizeZ);
- f.Write(&SizeX, 4);
- f.Write(&SizeY, 4);
- f.Write(&SizeZ, 4);
- unsigned char DataTypes = GetDataTypes();
- f.Write(&DataTypes, 1);
- int NumBlocks = GetBlockCount();
- if (HasBlockTypes())
- {
- f.Write(m_BlockTypes, NumBlocks * sizeof(BLOCKTYPE));
- }
- if (HasBlockMetas())
- {
- f.Write(m_BlockMetas, NumBlocks);
- }
- if (HasBlockLights())
- {
- f.Write(m_BlockLight, NumBlocks);
- }
- if (HasBlockSkyLights())
- {
- f.Write(m_BlockSkyLight, NumBlocks);
- }
-}
-
-
-
-
-
-bool cBlockArea::LoadFromSchematicFile(const AString & a_FileName)
-{
- // Un-GZip the contents:
- AString Contents;
- cGZipFile File;
- if (!File.Open(a_FileName, cGZipFile::fmRead))
- {
- LOG("Cannot open the schematic file \"%s\".", a_FileName.c_str());
- return false;
- }
- int NumBytesRead = File.ReadRestOfFile(Contents);
- if (NumBytesRead < 0)
- {
- LOG("Cannot read GZipped data in the schematic file \"%s\", error %d", a_FileName.c_str(), NumBytesRead);
- return false;
- }
- File.Close();
-
- // Parse the NBT:
- cParsedNBT NBT(Contents.data(), Contents.size());
- if (!NBT.IsValid())
- {
- LOG("Cannot parse the NBT in the schematic file \"%s\".", a_FileName.c_str());
- return false;
- }
-
- return LoadFromSchematicNBT(NBT);
-}
-
-
-
-
-
-bool cBlockArea::SaveToSchematicFile(const AString & a_FileName)
-{
- cFastNBTWriter Writer("Schematic");
- Writer.AddShort("Width", m_SizeX);
- Writer.AddShort("Height", m_SizeY);
- Writer.AddShort("Length", m_SizeZ);
- Writer.AddString("Materials", "Alpha");
- if (HasBlockTypes())
- {
- Writer.AddByteArray("Blocks", (const char *)m_BlockTypes, GetBlockCount());
- }
- else
- {
- AString Dummy(GetBlockCount(), 0);
- Writer.AddByteArray("Blocks", Dummy.data(), Dummy.size());
- }
- if (HasBlockMetas())
- {
- Writer.AddByteArray("Data", (const char *)m_BlockMetas, GetBlockCount());
- }
- else
- {
- AString Dummy(GetBlockCount(), 0);
- Writer.AddByteArray("Data", Dummy.data(), Dummy.size());
- }
- // TODO: Save entities and block entities
- Writer.BeginList("Entities", TAG_Compound);
- Writer.EndList();
- Writer.BeginList("TileEntities", TAG_Compound);
- Writer.EndList();
- Writer.Finish();
-
- // Save to file
- cGZipFile File;
- if (!File.Open(a_FileName, cGZipFile::fmWrite))
- {
- LOG("Cannot open file \"%s\" for writing.", a_FileName.c_str());
- return false;
- }
- if (!File.Write(Writer.GetResult()))
- {
- LOG("Cannot write data to file \"%s\".", a_FileName.c_str());
- return false;
- }
- return true;
-}
-
-
-
-
-
-void cBlockArea::Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ)
-{
- if (
- (a_AddMinX + a_SubMaxX >= m_SizeX) ||
- (a_AddMinY + a_SubMaxY >= m_SizeY) ||
- (a_AddMinZ + a_SubMaxZ >= m_SizeZ)
- )
- {
- LOGWARNING("cBlockArea:Crop called with more croping than the dimensions: %d x %d x %d with cropping %d, %d and %d",
- m_SizeX, m_SizeY, m_SizeZ,
- a_AddMinX + a_SubMaxX, a_AddMinY + a_SubMaxY, a_AddMinZ + a_SubMaxZ
- );
- return;
- }
-
- if (HasBlockTypes())
- {
- CropBlockTypes(a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
- }
- if (HasBlockMetas())
- {
- CropNibbles(m_BlockMetas, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
- }
- if (HasBlockLights())
- {
- CropNibbles(m_BlockLight, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
- }
- if (HasBlockSkyLights())
- {
- CropNibbles(m_BlockSkyLight, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
- }
- m_OriginX += a_AddMinX;
- m_OriginY += a_AddMinY;
- m_OriginZ += a_AddMinZ;
- m_SizeX -= a_AddMinX + a_SubMaxX;
- m_SizeY -= a_AddMinY + a_SubMaxY;
- m_SizeZ -= a_AddMinZ + a_SubMaxZ;
-}
-
-
-
-
-
-void cBlockArea::Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ)
-{
- if (HasBlockTypes())
- {
- ExpandBlockTypes(a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
- }
- if (HasBlockMetas())
- {
- ExpandNibbles(m_BlockMetas, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
- }
- if (HasBlockLights())
- {
- ExpandNibbles(m_BlockLight, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
- }
- if (HasBlockSkyLights())
- {
- ExpandNibbles(m_BlockSkyLight, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
- }
- m_OriginX -= a_SubMinX;
- m_OriginY -= a_SubMinY;
- m_OriginZ -= a_SubMinZ;
- m_SizeX += a_SubMinX + a_AddMaxX;
- m_SizeY += a_SubMinY + a_AddMaxY;
- m_SizeZ += a_SubMinZ + a_AddMaxZ;
-}
-
-
-
-
-
-void cBlockArea::Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy)
-{
- // Block types are compulsory, block metas are voluntary
- if (!HasBlockTypes() || !a_Src.HasBlockTypes())
- {
- LOGWARNING("%s: cannot merge because one of the areas doesn't have blocktypes.", __FUNCTION__);
- return;
- }
-
- // Dst is *this, Src is a_Src
- int SrcOffX = std::max(0, -a_RelX); // Offset in Src where to start reading
- int DstOffX = std::max(0, a_RelX); // Offset in Dst where to start writing
- int SizeX = std::min(a_Src.GetSizeX() - SrcOffX, GetSizeX() - DstOffX); // How many blocks to copy
-
- int SrcOffY = std::max(0, -a_RelY); // Offset in Src where to start reading
- int DstOffY = std::max(0, a_RelY); // Offset in Dst where to start writing
- int SizeY = std::min(a_Src.GetSizeY() - SrcOffY, GetSizeY() - DstOffY); // How many blocks to copy
-
- int SrcOffZ = std::max(0, -a_RelZ); // Offset in Src where to start reading
- int DstOffZ = std::max(0, a_RelZ); // Offset in Dst where to start writing
- int SizeZ = std::min(a_Src.GetSizeZ() - SrcOffZ, GetSizeZ() - DstOffZ); // How many blocks to copy
-
- const NIBBLETYPE * SrcMetas = a_Src.GetBlockMetas();
- NIBBLETYPE * DstMetas = m_BlockMetas;
- bool IsDummyMetas = ((SrcMetas == NULL) || (DstMetas == NULL));
-
- if (IsDummyMetas)
- {
- SrcMetas = new NIBBLETYPE[a_Src.GetBlockCount()];
- DstMetas = new NIBBLETYPE[GetBlockCount()];
- }
-
- switch (a_Strategy)
- {
- case msOverwrite:
- {
- InternalMergeBlocks(
- m_BlockTypes, a_Src.GetBlockTypes(),
- DstMetas, SrcMetas,
- SizeX, SizeY, SizeZ,
- SrcOffX, SrcOffY, SrcOffZ,
- DstOffX, DstOffY, DstOffZ,
- a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
- m_SizeX, m_SizeY, m_SizeZ,
- MergeCombinatorOverwrite
- );
- break;
- } // case msOverwrite
-
- case msFillAir:
- {
- InternalMergeBlocks(
- m_BlockTypes, a_Src.GetBlockTypes(),
- DstMetas, SrcMetas,
- SizeX, SizeY, SizeZ,
- SrcOffX, SrcOffY, SrcOffZ,
- DstOffX, DstOffY, DstOffZ,
- a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
- m_SizeX, m_SizeY, m_SizeZ,
- MergeCombinatorFillAir
- );
- break;
- } // case msFillAir
-
- case msImprint:
- {
- InternalMergeBlocks(
- m_BlockTypes, a_Src.GetBlockTypes(),
- DstMetas, SrcMetas,
- SizeX, SizeY, SizeZ,
- SrcOffX, SrcOffY, SrcOffZ,
- DstOffX, DstOffY, DstOffZ,
- a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
- m_SizeX, m_SizeY, m_SizeZ,
- MergeCombinatorImprint
- );
- break;
- } // case msImprint
-
- case msLake:
- {
- InternalMergeBlocks(
- m_BlockTypes, a_Src.GetBlockTypes(),
- DstMetas, SrcMetas,
- SizeX, SizeY, SizeZ,
- SrcOffX, SrcOffY, SrcOffZ,
- DstOffX, DstOffY, DstOffZ,
- a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
- m_SizeX, m_SizeY, m_SizeZ,
- MergeCombinatorLake
- );
- break;
- } // case msLake
-
- default:
- {
- LOGWARNING("Unknown block area merge strategy: %d", a_Strategy);
- ASSERT(!"Unknown block area merge strategy");
- break;
- }
- } // switch (a_Strategy)
-
- if (IsDummyMetas)
- {
- delete[] SrcMetas;
- delete[] DstMetas;
- }
-}
-
-
-
-
-
-void cBlockArea::Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight)
-{
- if ((a_DataTypes & GetDataTypes()) != a_DataTypes)
- {
- LOGWARNING("%s: requested datatypes that are not present in the BlockArea object, trimming those away (req 0x%x, stor 0x%x)",
- __FUNCTION__, a_DataTypes, GetDataTypes()
- );
- a_DataTypes = a_DataTypes & GetDataTypes();
- }
-
- int BlockCount = GetBlockCount();
- if ((a_DataTypes & baTypes) != 0)
- {
- for (int i = 0; i < BlockCount; i++)
- {
- m_BlockTypes[i] = a_BlockType;
- }
- }
- if ((a_DataTypes & baMetas) != 0)
- {
- for (int i = 0; i < BlockCount; i++)
- {
- m_BlockMetas[i] = a_BlockMeta;
- }
- }
- if ((a_DataTypes & baLight) != 0)
- {
- for (int i = 0; i < BlockCount; i++)
- {
- m_BlockLight[i] = a_BlockLight;
- }
- }
- if ((a_DataTypes & baSkyLight) != 0)
- {
- for (int i = 0; i < BlockCount; i++)
- {
- m_BlockSkyLight[i] = a_BlockSkyLight;
- }
- }
-}
-
-
-
-
-
-void cBlockArea::FillRelCuboid(int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ,
- int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
- NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
-)
-{
- if ((a_DataTypes & GetDataTypes()) != a_DataTypes)
- {
- LOGWARNING("%s: requested datatypes that are not present in the BlockArea object, trimming those away (req 0x%x, stor 0x%x)",
- __FUNCTION__, a_DataTypes, GetDataTypes()
- );
- a_DataTypes = a_DataTypes & GetDataTypes();
- }
-
- if ((a_DataTypes & baTypes) != 0)
- {
- for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
- {
- m_BlockTypes[MakeIndex(x, y, z)] = a_BlockType;
- } // for x, z, y
- }
- if ((a_DataTypes & baMetas) != 0)
- {
- for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
- {
- m_BlockMetas[MakeIndex(x, y, z)] = a_BlockMeta;
- } // for x, z, y
- }
- if ((a_DataTypes & baLight) != 0)
- {
- for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
- {
- m_BlockLight[MakeIndex(x, y, z)] = a_BlockLight;
- } // for x, z, y
- }
- if ((a_DataTypes & baSkyLight) != 0)
- {
- for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
- {
- m_BlockSkyLight[MakeIndex(x, y, z)] = a_BlockSkyLight;
- } // for x, z, y
- }
-}
-
-
-
-
-
-void cBlockArea::RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int a_RelY2, int a_RelZ2,
- int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
- NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
-)
-{
- // Bresenham-3D algorithm for drawing lines:
- int dx = abs(a_RelX2 - a_RelX1);
- int dy = abs(a_RelY2 - a_RelY1);
- int dz = abs(a_RelZ2 - a_RelZ1);
- int sx = (a_RelX1 < a_RelX2) ? 1 : -1;
- int sy = (a_RelY1 < a_RelY2) ? 1 : -1;
- int sz = (a_RelZ1 < a_RelZ2) ? 1 : -1;
- int err = dx - dz;
-
- if (dx >= std::max(dy, dz)) // x dominant
- {
- int yd = dy - dx / 2;
- int zd = dz - dx / 2;
-
- while (true)
- {
- RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight);
-
- if (a_RelX1 == a_RelX2)
- {
- break;
- }
-
- if (yd >= 0) // move along y
- {
- a_RelY1 += sy;
- yd -= dx;
- }
-
- if (zd >= 0) // move along z
- {
- a_RelZ1 += sz;
- zd -= dx;
- }
-
- // move along x
- a_RelX1 += sx;
- yd += dy;
- zd += dz;
- }
- }
- else if (dy >= std::max(dx, dz)) // y dominant
- {
- int xd = dx - dy / 2;
- int zd = dz - dy / 2;
-
- while (true)
- {
- RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight);
-
- if (a_RelY1 == a_RelY2)
- {
- break;
- }
-
- if (xd >= 0) // move along x
- {
- a_RelX1 += sx;
- xd -= dy;
- }
-
- if (zd >= 0) // move along z
- {
- a_RelZ1 += sz;
- zd -= dy;
- }
-
- // move along y
- a_RelY1 += sy;
- xd += dx;
- zd += dz;
- }
- }
- else
- {
- // z dominant
- ASSERT(dz >= std::max(dx, dy));
- int xd = dx - dz / 2;
- int yd = dy - dz / 2;
-
- while (true)
- {
- RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight);
-
- if (a_RelZ1 == a_RelZ2)
- {
- break;
- }
-
- if (xd >= 0) // move along x
- {
- a_RelX1 += sx;
- xd -= dz;
- }
-
- if (yd >= 0) // move along y
- {
- a_RelY1 += sy;
- yd -= dz;
- }
-
- // move along z
- a_RelZ1 += sz;
- xd += dx;
- yd += dy;
- }
- } // if (which dimension is dominant)
-}
-
-
-
-
-
-void cBlockArea::RotateCCW(void)
-{
- if (!HasBlockTypes())
- {
- LOGWARNING("cBlockArea: Cannot rotate blockmeta without blocktypes!");
- return;
- }
-
- if (!HasBlockMetas())
- {
- // There are no blockmetas to rotate, just use the NoMeta function
- RotateCCWNoMeta();
- return;
- }
-
- // We are guaranteed that both blocktypes and blockmetas exist; rotate both at the same time:
- BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ];
- NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ];
- for (int x = 0; x < m_SizeX; x++)
- {
- int NewZ = m_SizeX - x - 1;
- for (int z = 0; z < m_SizeZ; z++)
- {
- int NewX = z;
- for (int y = 0; y < m_SizeY; y++)
- {
- int NewIdx = NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ;
- int OldIdx = MakeIndex(x, y, z);
- NewTypes[NewIdx] = m_BlockTypes[OldIdx];
- NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCCW(m_BlockMetas[OldIdx]);
- } // for y
- } // for z
- } // for x
- std::swap(m_BlockTypes, NewTypes);
- std::swap(m_BlockMetas, NewMetas);
- delete[] NewTypes;
- delete[] NewMetas;
-
- std::swap(m_SizeX, m_SizeZ);
-}
-
-
-
-
-
-void cBlockArea::RotateCW(void)
-{
- if (!HasBlockTypes())
- {
- LOGWARNING("cBlockArea: Cannot rotate blockmeta without blocktypes!");
- return;
- }
-
- if (!HasBlockMetas())
- {
- // There are no blockmetas to rotate, just use the NoMeta function
- RotateCWNoMeta();
- return;
- }
-
- // We are guaranteed that both blocktypes and blockmetas exist; rotate both at the same time:
- BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ];
- NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ];
- for (int x = 0; x < m_SizeX; x++)
- {
- int NewZ = x;
- for (int z = 0; z < m_SizeZ; z++)
- {
- int NewX = m_SizeZ - z - 1;
- for (int y = 0; y < m_SizeY; y++)
- {
- int NewIdx = NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ;
- int OldIdx = MakeIndex(x, y, z);
- NewTypes[NewIdx] = m_BlockTypes[OldIdx];
- NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCW(m_BlockMetas[OldIdx]);
- } // for y
- } // for z
- } // for x
- std::swap(m_BlockTypes, NewTypes);
- std::swap(m_BlockMetas, NewMetas);
- delete[] NewTypes;
- delete[] NewMetas;
-
- std::swap(m_SizeX, m_SizeZ);
-}
-
-
-
-
-
-void cBlockArea::MirrorXY(void)
-{
- if (!HasBlockTypes())
- {
- LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!");
- return;
- }
-
- if (!HasBlockMetas())
- {
- // There are no blockmetas to mirror, just use the NoMeta function
- MirrorXYNoMeta();
- return;
- }
-
- // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time:
- int HalfZ = m_SizeZ / 2;
- int MaxZ = m_SizeZ - 1;
- for (int y = 0; y < m_SizeY; y++)
- {
- for (int z = 0; z < HalfZ; z++)
- {
- for (int x = 0; x < m_SizeX; x++)
- {
- int Idx1 = MakeIndex(x, y, z);
- int Idx2 = MakeIndex(x, y, MaxZ - z);
- std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]);
- NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorXY(m_BlockMetas[Idx1]);
- NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorXY(m_BlockMetas[Idx2]);
- m_BlockMetas[Idx1] = Meta2;
- m_BlockMetas[Idx2] = Meta1;
- } // for x
- } // for z
- } // for y
-}
-
-
-
-
-
-void cBlockArea::MirrorXZ(void)
-{
- if (!HasBlockTypes())
- {
- LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!");
- return;
- }
-
- if (!HasBlockMetas())
- {
- // There are no blockmetas to mirror, just use the NoMeta function
- MirrorXZNoMeta();
- return;
- }
-
- // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time:
- int HalfY = m_SizeY / 2;
- int MaxY = m_SizeY - 1;
- for (int y = 0; y < HalfY; y++)
- {
- for (int z = 0; z < m_SizeZ; z++)
- {
- for (int x = 0; x < m_SizeX; x++)
- {
- int Idx1 = MakeIndex(x, y, z);
- int Idx2 = MakeIndex(x, MaxY - y, z);
- std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]);
- NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorXZ(m_BlockMetas[Idx1]);
- NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorXZ(m_BlockMetas[Idx2]);
- m_BlockMetas[Idx1] = Meta2;
- m_BlockMetas[Idx2] = Meta1;
- } // for x
- } // for z
- } // for y
-}
-
-
-
-
-
-void cBlockArea::MirrorYZ(void)
-{
- if (!HasBlockTypes())
- {
- LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!");
- return;
- }
-
- if (!HasBlockMetas())
- {
- // There are no blockmetas to mirror, just use the NoMeta function
- MirrorYZNoMeta();
- return;
- }
-
- // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time:
- int HalfX = m_SizeX / 2;
- int MaxX = m_SizeX - 1;
- for (int y = 0; y < m_SizeY; y++)
- {
- for (int z = 0; z < m_SizeZ; z++)
- {
- for (int x = 0; x < HalfX; x++)
- {
- int Idx1 = MakeIndex(x, y, z);
- int Idx2 = MakeIndex(MaxX - x, y, z);
- std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]);
- NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorYZ(m_BlockMetas[Idx1]);
- NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorYZ(m_BlockMetas[Idx2]);
- m_BlockMetas[Idx1] = Meta2;
- m_BlockMetas[Idx2] = Meta1;
- } // for x
- } // for z
- } // for y
-}
-
-
-
-
-
-void cBlockArea::RotateCCWNoMeta(void)
-{
- if (HasBlockTypes())
- {
- BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ];
- for (int x = 0; x < m_SizeX; x++)
- {
- int NewZ = m_SizeX - x - 1;
- for (int z = 0; z < m_SizeZ; z++)
- {
- int NewX = z;
- for (int y = 0; y < m_SizeY; y++)
- {
- NewTypes[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)];
- } // for y
- } // for z
- } // for x
- std::swap(m_BlockTypes, NewTypes);
- delete[] NewTypes;
- }
- if (HasBlockMetas())
- {
- NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ];
- for (int x = 0; x < m_SizeX; x++)
- {
- int NewZ = m_SizeX - x - 1;
- for (int z = 0; z < m_SizeZ; z++)
- {
- int NewX = z;
- for (int y = 0; y < m_SizeY; y++)
- {
- NewMetas[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)];
- } // for y
- } // for z
- } // for x
- std::swap(m_BlockMetas, NewMetas);
- delete[] NewMetas;
- }
- std::swap(m_SizeX, m_SizeZ);
-}
-
-
-
-
-
-void cBlockArea::RotateCWNoMeta(void)
-{
- if (HasBlockTypes())
- {
- BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ];
- for (int z = 0; z < m_SizeZ; z++)
- {
- int NewX = m_SizeZ - z - 1;
- for (int x = 0; x < m_SizeX; x++)
- {
- int NewZ = x;
- for (int y = 0; y < m_SizeY; y++)
- {
- NewTypes[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)];
- } // for y
- } // for x
- } // for z
- std::swap(m_BlockTypes, NewTypes);
- delete[] NewTypes;
- }
- if (HasBlockMetas())
- {
- NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ];
- for (int z = 0; z < m_SizeZ; z++)
- {
- int NewX = m_SizeZ - z - 1;
- for (int x = 0; x < m_SizeX; x++)
- {
- int NewZ = x;
- for (int y = 0; y < m_SizeY; y++)
- {
- NewMetas[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)];
- } // for y
- } // for x
- } // for z
- std::swap(m_BlockMetas, NewMetas);
- delete[] NewMetas;
- }
- std::swap(m_SizeX, m_SizeZ);
-}
-
-
-
-
-
-void cBlockArea::MirrorXYNoMeta(void)
-{
- int HalfZ = m_SizeZ / 2;
- int MaxZ = m_SizeZ - 1;
- if (HasBlockTypes())
- {
- for (int y = 0; y < m_SizeY; y++)
- {
- for (int z = 0; z < HalfZ; z++)
- {
- for (int x = 0; x < m_SizeX; x++)
- {
- std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(x, y, MaxZ - z)]);
- } // for x
- } // for z
- } // for y
- } // if (HasBlockTypes)
-
- if (HasBlockMetas())
- {
- for (int y = 0; y < m_SizeY; y++)
- {
- for (int z = 0; z < HalfZ; z++)
- {
- for (int x = 0; x < m_SizeX; x++)
- {
- std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(x, y, MaxZ - z)]);
- } // for x
- } // for z
- } // for y
- } // if (HasBlockMetas)
-}
-
-
-
-
-
-void cBlockArea::MirrorXZNoMeta(void)
-{
- int HalfY = m_SizeY / 2;
- int MaxY = m_SizeY - 1;
- if (HasBlockTypes())
- {
- for (int y = 0; y < HalfY; y++)
- {
- for (int z = 0; z < m_SizeZ; z++)
- {
- for (int x = 0; x < m_SizeX; x++)
- {
- std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(x, MaxY - y, z)]);
- } // for x
- } // for z
- } // for y
- } // if (HasBlockTypes)
-
- if (HasBlockMetas())
- {
- for (int y = 0; y < HalfY; y++)
- {
- for (int z = 0; z < m_SizeZ; z++)
- {
- for (int x = 0; x < m_SizeX; x++)
- {
- std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(x, MaxY - y, z)]);
- } // for x
- } // for z
- } // for y
- } // if (HasBlockMetas)
-}
-
-
-
-
-
-void cBlockArea::MirrorYZNoMeta(void)
-{
- int HalfX = m_SizeX / 2;
- int MaxX = m_SizeX - 1;
- if (HasBlockTypes())
- {
- for (int y = 0; y < m_SizeY; y++)
- {
- for (int z = 0; z < m_SizeZ; z++)
- {
- for (int x = 0; x < HalfX; x++)
- {
- std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(MaxX - x, y, z)]);
- } // for x
- } // for z
- } // for y
- } // if (HasBlockTypes)
-
- if (HasBlockMetas())
- {
- for (int y = 0; y < m_SizeY; y++)
- {
- for (int z = 0; z < m_SizeZ; z++)
- {
- for (int x = 0; x < HalfX; x++)
- {
- std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(MaxX - x, y, z)]);
- } // for x
- } // for z
- } // for y
- } // if (HasBlockMetas)
-}
-
-
-
-
-
-void cBlockArea::SetRelBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType)
-{
- if (m_BlockTypes == NULL)
- {
- LOGWARNING("cBlockArea: BlockTypes have not been read!");
- return;
- }
- m_BlockTypes[MakeIndex(a_RelX, a_RelY, a_RelZ)] = a_BlockType;
-}
-
-
-
-
-
-void cBlockArea::SetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType)
-{
- SetRelBlockType(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_BlockType);
-}
-
-
-
-
-
-void cBlockArea::SetRelBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta)
-{
- SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockMeta, m_BlockMetas);
-}
-
-
-
-
-
-void cBlockArea::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta)
-{
- SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta, m_BlockMetas);
-}
-
-
-
-
-
-void cBlockArea::SetRelBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockLight)
-{
- SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockLight, m_BlockLight);
-}
-
-
-
-
-
-void cBlockArea::SetBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockLight)
-{
- SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockLight, m_BlockLight);
-}
-
-
-
-
-
-void cBlockArea::SetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockSkyLight)
-{
- SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockSkyLight, m_BlockSkyLight);
-}
-
-
-
-
-
-void cBlockArea::SetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockSkyLight)
-{
- SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockSkyLight, m_BlockSkyLight);
-}
-
-
-
-
-
-BLOCKTYPE cBlockArea::GetRelBlockType(int a_RelX, int a_RelY, int a_RelZ) const
-{
- if (m_BlockTypes == NULL)
- {
- LOGWARNING("cBlockArea: BlockTypes have not been read!");
- return E_BLOCK_AIR;
- }
- return m_BlockTypes[MakeIndex(a_RelX, a_RelY, a_RelZ)];
-}
-
-
-
-
-
-BLOCKTYPE cBlockArea::GetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ) const
-{
- return GetRelBlockType(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ);
-}
-
-
-
-
-
-NIBBLETYPE cBlockArea::GetRelBlockMeta(int a_RelX, int a_RelY, int a_RelZ) const
-{
- return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockMetas);
-}
-
-
-
-
-
-NIBBLETYPE cBlockArea::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ) const
-{
- return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockMetas);
-}
-
-
-
-
-
-NIBBLETYPE cBlockArea::GetRelBlockLight(int a_RelX, int a_RelY, int a_RelZ) const
-{
- return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockLight);
-}
-
-
-
-
-
-NIBBLETYPE cBlockArea::GetBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ) const
-{
- return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockLight);
-}
-
-
-
-
-
-NIBBLETYPE cBlockArea::GetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ) const
-{
- return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockSkyLight);
-}
-
-
-
-
-
-NIBBLETYPE cBlockArea::GetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ) const
-{
- return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockSkyLight);
-}
-
-
-
-
-
-void cBlockArea::SetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
-{
- SetRelBlockTypeMeta(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_BlockType, a_BlockMeta);
-}
-
-
-
-
-
-void cBlockArea::SetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
-{
- int idx = MakeIndex(a_RelX, a_RelY, a_RelZ);
- if (m_BlockTypes == NULL)
- {
- LOGWARNING("%s: BlockTypes not available but requested to be written to.", __FUNCTION__);
- }
- else
- {
- m_BlockTypes[idx] = a_BlockType;
- }
- if (m_BlockMetas == NULL)
- {
- LOGWARNING("%s: BlockMetas not available but requested to be written to.", __FUNCTION__);
- }
- else
- {
- m_BlockMetas[idx] = a_BlockMeta;
- }
-}
-
-
-
-
-
-void cBlockArea::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const
-{
- return GetRelBlockTypeMeta(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_BlockType, a_BlockMeta);
-}
-
-
-
-
-
-void cBlockArea::GetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const
-{
- int idx = MakeIndex(a_RelX, a_RelY, a_RelZ);
- if (m_BlockTypes == NULL)
- {
- LOGWARNING("cBlockArea: BlockTypes have not been read!");
- a_BlockType = E_BLOCK_AIR;
- }
- else
- {
- a_BlockType = m_BlockTypes[idx];
- }
-
- if (m_BlockMetas == NULL)
- {
- LOGWARNING("cBlockArea: BlockMetas have not been read!");
- a_BlockMeta = 0;
- }
- else
- {
- a_BlockMeta = m_BlockMetas[idx];
- }
-}
-
-
-
-
-
-int cBlockArea::GetDataTypes(void) const
-{
- int res = 0;
- if (m_BlockTypes != NULL)
- {
- res |= baTypes;
- }
- if (m_BlockMetas != NULL)
- {
- res |= baMetas;
- }
- if (m_BlockLight != NULL)
- {
- res |= baLight;
- }
- if (m_BlockSkyLight != NULL)
- {
- res |= baSkyLight;
- }
- return res;
-}
-
-
-
-
-
-bool cBlockArea::SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
-{
- ASSERT(m_BlockTypes == NULL); // Has been cleared
-
- if (a_DataTypes & baTypes)
- {
- m_BlockTypes = new BLOCKTYPE[a_SizeX * a_SizeY * a_SizeZ];
- if (m_BlockTypes == NULL)
- {
- return false;
- }
- }
- if (a_DataTypes & baMetas)
- {
- m_BlockMetas = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ];
- if (m_BlockMetas == NULL)
- {
- delete[] m_BlockTypes;
- return false;
- }
- }
- if (a_DataTypes & baLight)
- {
- m_BlockLight = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ];
- if (m_BlockLight == NULL)
- {
- delete[] m_BlockMetas;
- delete[] m_BlockTypes;
- return false;
- }
- }
- if (a_DataTypes & baSkyLight)
- {
- m_BlockSkyLight = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ];
- if (m_BlockSkyLight == NULL)
- {
- delete[] m_BlockLight;
- delete[] m_BlockMetas;
- delete[] m_BlockTypes;
- return false;
- }
- }
- m_SizeX = a_SizeX;
- m_SizeY = a_SizeY;
- m_SizeZ = a_SizeZ;
- return true;
-}
-
-
-
-
-
-int cBlockArea::MakeIndex(int a_RelX, int a_RelY, int a_RelZ) const
-{
- ASSERT(a_RelX >= 0);
- ASSERT(a_RelX < m_SizeX);
- ASSERT(a_RelY >= 0);
- ASSERT(a_RelY < m_SizeY);
- ASSERT(a_RelZ >= 0);
- ASSERT(a_RelZ < m_SizeZ);
-
- return a_RelX + a_RelZ * m_SizeX + a_RelY * m_SizeX * m_SizeZ;
-}
-
-
-
-
-
-void cBlockArea::SetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array)
-{
- if (a_Array == NULL)
- {
- LOGWARNING("cBlockArea: datatype has not been read!");
- return;
- }
- a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)] = a_Value;
-}
-
-
-
-
-
-void cBlockArea::SetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array)
-{
- SetRelNibble(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_Value, a_Array);
-}
-
-
-
-
-
-NIBBLETYPE cBlockArea::GetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE * a_Array) const
-{
- if (a_Array == NULL)
- {
- LOGWARNING("cBlockArea: datatype has not been read!");
- return 16;
- }
- return a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)];
-}
-
-
-
-
-
-NIBBLETYPE cBlockArea::GetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE * a_Array) const
-{
- return GetRelNibble(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_Array);
-}
-
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cBlockArea::cChunkReader:
-
-cBlockArea::cChunkReader::cChunkReader(cBlockArea & a_Area) :
- m_Area(a_Area),
- m_OriginX(a_Area.m_OriginX),
- m_OriginY(a_Area.m_OriginY),
- m_OriginZ(a_Area.m_OriginZ)
-{
-}
-
-
-
-
-
-void cBlockArea::cChunkReader::CopyNibbles(NIBBLETYPE * a_AreaDst, const NIBBLETYPE * a_ChunkSrc)
-{
- int SizeY = m_Area.m_SizeY;
- int MinY = m_OriginY;
-
- // SizeX, SizeZ are the dmensions of the block data to copy from the current chunk (size of the geometric union)
- // OffX, OffZ are the offsets of the current chunk data from the area origin
- // BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders
- int SizeX = cChunkDef::Width;
- int SizeZ = cChunkDef::Width;
- int OffX, OffZ;
- int BaseX, BaseZ;
- OffX = m_CurrentChunkX * cChunkDef::Width - m_OriginX;
- if (OffX < 0)
- {
- BaseX = -OffX;
- SizeX += OffX; // SizeX is decreased, OffX is negative
- OffX = 0;
- }
- else
- {
- BaseX = 0;
- }
- OffZ = m_CurrentChunkZ * cChunkDef::Width - m_OriginZ;
- if (OffZ < 0)
- {
- BaseZ = -OffZ;
- SizeZ += OffZ; // SizeZ is decreased, OffZ is negative
- OffZ = 0;
- }
- else
- {
- BaseZ = 0;
- }
- // If the chunk extends beyond the area in the X or Z axis, cut off the Size:
- if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_OriginX + m_Area.m_SizeX)
- {
- SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_OriginX + m_Area.m_SizeX);
- }
- if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_OriginZ + m_Area.m_SizeZ)
- {
- SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_OriginZ + m_Area.m_SizeZ);
- }
-
- for (int y = 0; y < SizeY; y++)
- {
- int ChunkY = MinY + y;
- int AreaY = y;
- for (int z = 0; z < SizeZ; z++)
- {
- int ChunkZ = BaseZ + z;
- int AreaZ = OffZ + z;
- for (int x = 0; x < SizeX; x++)
- {
- int ChunkX = BaseX + x;
- int AreaX = OffX + x;
- a_AreaDst[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = cChunkDef::GetNibble(a_ChunkSrc, ChunkX, ChunkY, ChunkZ);
- } // for x
- } // for z
- } // for y
-}
-
-
-
-
-
-bool cBlockArea::cChunkReader::Coords(int a_ChunkX, int a_ChunkZ)
-{
- m_CurrentChunkX = a_ChunkX;
- m_CurrentChunkZ = a_ChunkZ;
- return true;
-}
-
-
-
-
-
-void cBlockArea::cChunkReader::BlockTypes(const BLOCKTYPE * a_BlockTypes)
-{
- if (m_Area.m_BlockTypes == NULL)
- {
- // Don't want BlockTypes
- return;
- }
-
- int SizeY = m_Area.m_SizeY;
- int MinY = m_OriginY;
-
- // SizeX, SizeZ are the dmensions of the block data to copy from the current chunk (size of the geometric union)
- // OffX, OffZ are the offsets of the current chunk data from the area origin
- // BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders
- int SizeX = cChunkDef::Width;
- int SizeZ = cChunkDef::Width;
- int OffX, OffZ;
- int BaseX, BaseZ;
- OffX = m_CurrentChunkX * cChunkDef::Width - m_OriginX;
- if (OffX < 0)
- {
- BaseX = -OffX;
- SizeX += OffX; // SizeX is decreased, OffX is negative
- OffX = 0;
- }
- else
- {
- BaseX = 0;
- }
- OffZ = m_CurrentChunkZ * cChunkDef::Width - m_OriginZ;
- if (OffZ < 0)
- {
- BaseZ = -OffZ;
- SizeZ += OffZ; // SizeZ is decreased, OffZ is negative
- OffZ = 0;
- }
- else
- {
- BaseZ = 0;
- }
- // If the chunk extends beyond the area in the X or Z axis, cut off the Size:
- if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_OriginX + m_Area.m_SizeX)
- {
- SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_OriginX + m_Area.m_SizeX);
- }
- if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_OriginZ + m_Area.m_SizeZ)
- {
- SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_OriginZ + m_Area.m_SizeZ);
- }
-
- for (int y = 0; y < SizeY; y++)
- {
- int ChunkY = MinY + y;
- int AreaY = y;
- for (int z = 0; z < SizeZ; z++)
- {
- int ChunkZ = BaseZ + z;
- int AreaZ = OffZ + z;
- for (int x = 0; x < SizeX; x++)
- {
- int ChunkX = BaseX + x;
- int AreaX = OffX + x;
- m_Area.m_BlockTypes[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = cChunkDef::GetBlock(a_BlockTypes, ChunkX, ChunkY, ChunkZ);
- } // for x
- } // for z
- } // for y
-}
-
-
-
-
-
-void cBlockArea::cChunkReader::BlockMeta(const NIBBLETYPE * a_BlockMetas)
-{
- if (m_Area.m_BlockMetas == NULL)
- {
- // Don't want metas
- return;
- }
- CopyNibbles(m_Area.m_BlockMetas, a_BlockMetas);
-}
-
-
-
-
-
-void cBlockArea::cChunkReader::BlockLight(const NIBBLETYPE * a_BlockLight)
-{
- if (m_Area.m_BlockLight == NULL)
- {
- // Don't want light
- return;
- }
- CopyNibbles(m_Area.m_BlockLight, a_BlockLight);
-}
-
-
-
-
-
-void cBlockArea::cChunkReader::BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight)
-{
- if (m_Area.m_BlockSkyLight == NULL)
- {
- // Don't want skylight
- return;
- }
- CopyNibbles(m_Area.m_BlockSkyLight, a_BlockSkyLight);
-}
-
-
-
-
-
-void cBlockArea::CropBlockTypes(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ)
-{
- int NewSizeX = GetSizeX() - a_AddMinX - a_SubMaxX;
- int NewSizeY = GetSizeY() - a_AddMinY - a_SubMaxY;
- int NewSizeZ = GetSizeZ() - a_AddMinZ - a_SubMaxZ;
- BLOCKTYPE * NewBlockTypes = new BLOCKTYPE[NewSizeX * NewSizeY * NewSizeZ];
- int idx = 0;
- for (int y = 0; y < NewSizeY; y++)
- {
- for (int z = 0; z < NewSizeZ; z++)
- {
- for (int x = 0; x < NewSizeX; x++)
- {
- int OldIndex = MakeIndex(x + a_AddMinX, y + a_AddMinY, z + a_AddMinZ);
- NewBlockTypes[idx++] = m_BlockTypes[OldIndex];
- } // for x
- } // for z
- } // for y
- delete m_BlockTypes;
- m_BlockTypes = NewBlockTypes;
-}
-
-
-
-
-
-void cBlockArea::CropNibbles(NIBBLEARRAY & a_Array, int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ)
-{
- int NewSizeX = GetSizeX() - a_AddMinX - a_SubMaxX;
- int NewSizeY = GetSizeY() - a_AddMinY - a_SubMaxY;
- int NewSizeZ = GetSizeZ() - a_AddMinZ - a_SubMaxZ;
- NIBBLETYPE * NewNibbles = new NIBBLETYPE[NewSizeX * NewSizeY * NewSizeZ];
- int idx = 0;
- for (int y = 0; y < NewSizeY; y++)
- {
- for (int z = 0; z < NewSizeZ; z++)
- {
- for (int x = 0; x < NewSizeX; x++)
- {
- NewNibbles[idx++] = a_Array[MakeIndex(x + a_AddMinX, y + a_AddMinY, z + a_AddMinZ)];
- } // for x
- } // for z
- } // for y
- delete a_Array;
- a_Array = NewNibbles;
-}
-
-
-
-
-
-void cBlockArea::ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ)
-{
- int NewSizeX = m_SizeX + a_SubMinX + a_AddMaxX;
- int NewSizeY = m_SizeY + a_SubMinY + a_AddMaxY;
- int NewSizeZ = m_SizeZ + a_SubMinZ + a_AddMaxZ;
- int BlockCount = NewSizeX * NewSizeY * NewSizeZ;
- BLOCKTYPE * NewBlockTypes = new BLOCKTYPE[BlockCount];
- memset(NewBlockTypes, 0, BlockCount * sizeof(BLOCKTYPE));
- int OldIndex = 0;
- for (int y = 0; y < m_SizeY; y++)
- {
- int IndexBaseY = (y + a_SubMinY) * m_SizeX * m_SizeZ;
- for (int z = 0; z < m_SizeZ; z++)
- {
- int IndexBaseZ = IndexBaseY + (z + a_SubMinZ) * m_SizeX;
- int idx = IndexBaseZ + a_SubMinX;
- for (int x = 0; x < m_SizeX; x++)
- {
- NewBlockTypes[idx++] = m_BlockTypes[OldIndex++];
- } // for x
- } // for z
- } // for y
- delete m_BlockTypes;
- m_BlockTypes = NewBlockTypes;
-}
-
-
-
-
-
-void cBlockArea::ExpandNibbles(NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ)
-{
- int NewSizeX = m_SizeX + a_SubMinX + a_AddMaxX;
- int NewSizeY = m_SizeY + a_SubMinY + a_AddMaxY;
- int NewSizeZ = m_SizeZ + a_SubMinZ + a_AddMaxZ;
- int BlockCount = NewSizeX * NewSizeY * NewSizeZ;
- NIBBLETYPE * NewNibbles = new NIBBLETYPE[BlockCount];
- memset(NewNibbles, 0, BlockCount * sizeof(NIBBLETYPE));
- int OldIndex = 0;
- for (int y = 0; y < m_SizeY; y++)
- {
- int IndexBaseY = (y + a_SubMinY) * m_SizeX * m_SizeZ;
- for (int z = 0; z < m_SizeZ; z++)
- {
- int IndexBaseZ = IndexBaseY + (z + a_SubMinZ) * m_SizeX;
- int idx = IndexBaseZ + a_SubMinX;
- for (int x = 0; x < m_SizeX; x++)
- {
- NewNibbles[idx++] = a_Array[OldIndex++];
- } // for x
- } // for z
- } // for y
- delete a_Array;
- a_Array = NewNibbles;
-}
-
-
-
-
-
-bool cBlockArea::LoadFromSchematicNBT(cParsedNBT & a_NBT)
-{
- int TMaterials = a_NBT.FindChildByName(a_NBT.GetRoot(), "Materials");
- if ((TMaterials > 0) && (a_NBT.GetType(TMaterials) == TAG_String))
- {
- AString Materials = a_NBT.GetString(TMaterials);
- if (Materials.compare("Alpha") != 0)
- {
- LOG("Materials tag is present and \"%s\" instead of \"Alpha\". Possibly a wrong-format schematic file.", Materials.c_str());
- return false;
- }
- }
- int TSizeX = a_NBT.FindChildByName(a_NBT.GetRoot(), "Width");
- int TSizeY = a_NBT.FindChildByName(a_NBT.GetRoot(), "Height");
- int TSizeZ = a_NBT.FindChildByName(a_NBT.GetRoot(), "Length");
- if (
- (TSizeX < 0) || (TSizeY < 0) || (TSizeZ < 0) ||
- (a_NBT.GetType(TSizeX) != TAG_Short) ||
- (a_NBT.GetType(TSizeY) != TAG_Short) ||
- (a_NBT.GetType(TSizeZ) != TAG_Short)
- )
- {
- LOG("Dimensions are missing from the schematic file (%d, %d, %d), (%d, %d, %d)",
- TSizeX, TSizeY, TSizeZ,
- a_NBT.GetType(TSizeX), a_NBT.GetType(TSizeY), a_NBT.GetType(TSizeZ)
- );
- return false;
- }
-
- int SizeX = a_NBT.GetShort(TSizeX);
- int SizeY = a_NBT.GetShort(TSizeY);
- int SizeZ = a_NBT.GetShort(TSizeZ);
- if ((SizeX < 1) || (SizeY < 1) || (SizeZ < 1))
- {
- LOG("Dimensions are invalid in the schematic file: %d, %d, %d", SizeX, SizeY, SizeZ);
- return false;
- }
-
- int TBlockTypes = a_NBT.FindChildByName(a_NBT.GetRoot(), "Blocks");
- int TBlockMetas = a_NBT.FindChildByName(a_NBT.GetRoot(), "Data");
- if ((TBlockTypes < 0) || (a_NBT.GetType(TBlockTypes) != TAG_ByteArray))
- {
- LOG("BlockTypes are invalid in the schematic file: %d", TBlockTypes);
- return false;
- }
- bool AreMetasPresent = (TBlockMetas > 0) && (a_NBT.GetType(TBlockMetas) == TAG_ByteArray);
-
- Clear();
- SetSize(SizeX, SizeY, SizeZ, AreMetasPresent ? (baTypes | baMetas) : baTypes);
-
- // Copy the block types and metas:
- int NumBytes = m_SizeX * m_SizeY * m_SizeZ;
- if (a_NBT.GetDataLength(TBlockTypes) < NumBytes)
- {
- LOG("BlockTypes truncated in the schematic file (exp %d, got %d bytes). Loading partial.",
- NumBytes, a_NBT.GetDataLength(TBlockTypes)
- );
- NumBytes = a_NBT.GetDataLength(TBlockTypes);
- }
- memcpy(m_BlockTypes, a_NBT.GetData(TBlockTypes), NumBytes);
-
- if (AreMetasPresent)
- {
- int NumBytes = m_SizeX * m_SizeY * m_SizeZ;
- if (a_NBT.GetDataLength(TBlockMetas) < NumBytes)
- {
- LOG("BlockMetas truncated in the schematic file (exp %d, got %d bytes). Loading partial.",
- NumBytes, a_NBT.GetDataLength(TBlockMetas)
- );
- NumBytes = a_NBT.GetDataLength(TBlockMetas);
- }
- memcpy(m_BlockMetas, a_NBT.GetData(TBlockMetas), NumBytes);
- }
-
- return true;
-}
-
-
-
-
-void cBlockArea::RelSetData(
- int a_RelX, int a_RelY, int a_RelZ,
- int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
- NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
-)
-{
- int Index = MakeIndex(a_RelX, a_RelY, a_RelZ);
- if ((a_DataTypes & baTypes) != 0)
- {
- m_BlockTypes[Index] = a_BlockType;
- }
- if ((a_DataTypes & baMetas) != 0)
- {
- m_BlockMetas[Index] = a_BlockMeta;
- }
- if ((a_DataTypes & baLight) != 0)
- {
- m_BlockLight[Index] = a_BlockLight;
- }
- if ((a_DataTypes & baSkyLight) != 0)
- {
- m_BlockSkyLight[Index] = a_BlockSkyLight;
- }
-}
-
-
-
-
+
+// BlockArea.cpp
+
+// Implements the cBlockArea object representing an area of block data that can be queried from cWorld and then accessed again without further queries
+// The object also supports writing the blockdata back into cWorld, even into other coords
+
+#include "Globals.h"
+#include "BlockArea.h"
+#include "World.h"
+#include "OSSupport/GZipFile.h"
+#include "WorldStorage/FastNBT.h"
+#include "Blocks/BlockHandler.h"
+
+
+
+
+
+// This wild construct allows us to pass a function argument and still have it inlined by the compiler :)
+/// Merges two blocktypes and blockmetas of the specified sizes and offsets using the specified combinator function
+template<typename Combinator> void InternalMergeBlocks(
+ BLOCKTYPE * a_DstTypes, const BLOCKTYPE * a_SrcTypes,
+ NIBBLETYPE * a_DstMetas, const NIBBLETYPE * a_SrcMetas,
+ int a_SizeX, int a_SizeY, int a_SizeZ,
+ int a_SrcOffX, int a_SrcOffY, int a_SrcOffZ,
+ int a_DstOffX, int a_DstOffY, int a_DstOffZ,
+ int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ,
+ int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ,
+ Combinator a_Combinator
+)
+{
+ for (int y = 0; y < a_SizeY; y++)
+ {
+ int SrcBaseY = (y + a_SrcOffY) * a_SrcSizeX * a_SrcSizeZ;
+ int DstBaseY = (y + a_DstOffY) * a_DstSizeX * a_DstSizeZ;
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ int SrcBaseZ = SrcBaseY + (z + a_SrcOffZ) * a_SrcSizeX;
+ int DstBaseZ = DstBaseY + (z + a_DstOffZ) * a_DstSizeX;
+ int SrcIdx = SrcBaseZ + a_SrcOffX;
+ int DstIdx = DstBaseZ + a_DstOffX;
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ a_Combinator(a_DstTypes[DstIdx], a_SrcTypes[SrcIdx], a_DstMetas[DstIdx], a_SrcMetas[SrcIdx]);
+ ++DstIdx;
+ ++SrcIdx;
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+/// Combinator used for cBlockArea::msOverwrite merging
+static void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
+{
+ a_DstType = a_SrcType;
+ a_DstMeta = a_SrcMeta;
+}
+
+
+
+
+
+/// Combinator used for cBlockArea::msFillAir merging
+static void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
+{
+ if (a_DstType == E_BLOCK_AIR)
+ {
+ a_DstType = a_SrcType;
+ a_DstMeta = a_SrcMeta;
+ }
+ // "else" is the default, already in place
+}
+
+
+
+
+
+/// Combinator used for cBlockArea::msImprint merging
+static void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
+{
+ if (a_SrcType != E_BLOCK_AIR)
+ {
+ a_DstType = a_SrcType;
+ a_DstMeta = a_SrcMeta;
+ }
+ // "else" is the default, already in place
+}
+
+
+
+
+
+/// Combinator used for cBlockArea::msLake merging
+static void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
+{
+ // Sponge is the NOP block
+ if (a_SrcType == E_BLOCK_SPONGE)
+ {
+ return;
+ }
+
+ // Air is always hollowed out
+ if (a_SrcType == E_BLOCK_AIR)
+ {
+ a_DstType = E_BLOCK_AIR;
+ a_DstMeta = 0;
+ return;
+ }
+
+ // Water and lava are never overwritten
+ switch (a_DstType)
+ {
+ case E_BLOCK_WATER:
+ case E_BLOCK_STATIONARY_WATER:
+ case E_BLOCK_LAVA:
+ case E_BLOCK_STATIONARY_LAVA:
+ {
+ return;
+ }
+ }
+
+ // Water and lava always overwrite
+ switch (a_SrcType)
+ {
+ case E_BLOCK_WATER:
+ case E_BLOCK_STATIONARY_WATER:
+ case E_BLOCK_LAVA:
+ case E_BLOCK_STATIONARY_LAVA:
+ {
+ a_DstType = a_SrcType;
+ a_DstMeta = a_DstMeta;
+ return;
+ }
+ }
+
+ if (a_SrcType == E_BLOCK_STONE)
+ {
+ switch (a_DstType)
+ {
+ case E_BLOCK_DIRT:
+ case E_BLOCK_GRASS:
+ case E_BLOCK_MYCELIUM:
+ {
+ a_DstType = E_BLOCK_STONE;
+ a_DstMeta = 0;
+ return;
+ }
+ }
+ }
+ // Everything else is left as it is
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cBlockArea:
+
+cBlockArea::cBlockArea(void) :
+ m_SizeX(0),
+ m_SizeY(0),
+ m_SizeZ(0),
+ m_BlockTypes(NULL),
+ m_BlockMetas(NULL),
+ m_BlockLight(NULL),
+ m_BlockSkyLight(NULL)
+{
+}
+
+
+
+
+
+cBlockArea::~cBlockArea()
+{
+ Clear();
+}
+
+
+
+
+
+void cBlockArea::Clear(void)
+{
+ delete[] m_BlockTypes; m_BlockTypes = NULL;
+ delete[] m_BlockMetas; m_BlockMetas = NULL;
+ delete[] m_BlockLight; m_BlockLight = NULL;
+ delete[] m_BlockSkyLight; m_BlockSkyLight = NULL;
+ m_SizeX = 0;
+ m_SizeY = 0;
+ m_SizeZ = 0;
+}
+
+
+
+
+
+void cBlockArea::Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
+{
+ Clear();
+ int BlockCount = a_SizeX * a_SizeY * a_SizeZ;
+ if ((a_DataTypes & baTypes) != 0)
+ {
+ m_BlockTypes = new BLOCKTYPE[BlockCount];
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockTypes[i] = E_BLOCK_AIR;
+ }
+ }
+ if ((a_DataTypes & baMetas) != 0)
+ {
+ m_BlockMetas = new NIBBLETYPE[BlockCount];
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockMetas[i] = 0;
+ }
+ }
+ if ((a_DataTypes & baLight) != 0)
+ {
+ m_BlockLight = new NIBBLETYPE[BlockCount];
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockLight[i] = 0;
+ }
+ }
+ if ((a_DataTypes & baSkyLight) != 0)
+ {
+ m_BlockSkyLight = new NIBBLETYPE[BlockCount];
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockSkyLight[i] = 0x0f;
+ }
+ }
+ m_SizeX = a_SizeX;
+ m_SizeY = a_SizeY;
+ m_SizeZ = a_SizeZ;
+ m_OriginX = 0;
+ m_OriginY = 0;
+ m_OriginZ = 0;
+}
+
+
+
+
+
+void cBlockArea::SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ)
+{
+ m_OriginX = a_OriginX;
+ m_OriginY = a_OriginY;
+ m_OriginZ = a_OriginZ;
+}
+
+
+
+
+
+bool cBlockArea::Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes)
+{
+ // Normalize the coords:
+ if (a_MinBlockX > a_MaxBlockX)
+ {
+ std::swap(a_MinBlockX, a_MaxBlockX);
+ }
+ if (a_MinBlockY > a_MaxBlockY)
+ {
+ std::swap(a_MinBlockY, a_MaxBlockY);
+ }
+ if (a_MinBlockZ > a_MaxBlockZ)
+ {
+ std::swap(a_MinBlockZ, a_MaxBlockZ);
+ }
+
+ // Include the Max coords:
+ a_MaxBlockX += 1;
+ a_MaxBlockY += 1;
+ a_MaxBlockZ += 1;
+
+ // Check coords validity:
+ if (a_MinBlockY < 0)
+ {
+ LOGWARNING("%s: MinBlockY less than zero, adjusting to zero", __FUNCTION__);
+ a_MinBlockY = 0;
+ }
+ else if (a_MinBlockY >= cChunkDef::Height)
+ {
+ LOGWARNING("%s: MinBlockY more than chunk height, adjusting to chunk height", __FUNCTION__);
+ a_MinBlockY = cChunkDef::Height - 1;
+ }
+ if (a_MaxBlockY < 0)
+ {
+ LOGWARNING("%s: MaxBlockY less than zero, adjusting to zero", __FUNCTION__);
+ a_MaxBlockY = 0;
+ }
+ else if (a_MaxBlockY >= cChunkDef::Height)
+ {
+ LOGWARNING("%s: MaxBlockY more than chunk height, adjusting to chunk height", __FUNCTION__);
+ a_MaxBlockY = cChunkDef::Height - 1;
+ }
+
+ // Allocate the needed memory:
+ Clear();
+ if (!SetSize(a_MaxBlockX - a_MinBlockX, a_MaxBlockY - a_MinBlockY, a_MaxBlockZ - a_MinBlockZ, a_DataTypes))
+ {
+ return false;
+ }
+ m_OriginX = a_MinBlockX;
+ m_OriginY = a_MinBlockY;
+ m_OriginZ = a_MinBlockZ;
+ cChunkReader Reader(*this);
+
+ // Convert block coords to chunks coords:
+ int MinChunkX, MaxChunkX;
+ int MinChunkZ, MaxChunkZ;
+ cChunkDef::AbsoluteToRelative(a_MinBlockX, a_MinBlockY, a_MinBlockZ, MinChunkX, MinChunkZ);
+ cChunkDef::AbsoluteToRelative(a_MaxBlockX, a_MaxBlockY, a_MaxBlockZ, MaxChunkX, MaxChunkZ);
+
+ // Query block data:
+ if (!a_World->ForEachChunkInRect(MinChunkX, MaxChunkX, MinChunkZ, MaxChunkZ, Reader))
+ {
+ Clear();
+ return false;
+ }
+
+ return true;
+}
+
+
+
+
+
+bool cBlockArea::Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes)
+{
+ ASSERT((a_DataTypes & GetDataTypes()) == a_DataTypes); // Are you requesting only the data that I have?
+ a_DataTypes = a_DataTypes & GetDataTypes(); // For release builds, silently cut off the datatypes that I don't have
+
+ // Check coords validity:
+ if (a_MinBlockY < 0)
+ {
+ LOGWARNING("%s: MinBlockY less than zero, adjusting to zero", __FUNCTION__);
+ a_MinBlockY = 0;
+ }
+ else if (a_MinBlockY >= cChunkDef::Height - m_SizeY)
+ {
+ LOGWARNING("%s: MinBlockY + m_SizeY more than chunk height, adjusting to chunk height", __FUNCTION__);
+ a_MinBlockY = cChunkDef::Height - m_SizeY - 1;
+ }
+
+ return a_World->WriteBlockArea(*this, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes);
+}
+
+
+
+
+
+void cBlockArea::CopyTo(cBlockArea & a_Into) const
+{
+ if (&a_Into == this)
+ {
+ LOGWARNING("Trying to copy a cBlockArea into self, ignoring.");
+ return;
+ }
+
+ a_Into.Clear();
+ a_Into.SetSize(m_SizeX, m_SizeY, m_SizeZ, GetDataTypes());
+ a_Into.m_OriginX = m_OriginX;
+ a_Into.m_OriginY = m_OriginY;
+ a_Into.m_OriginZ = m_OriginZ;
+ int BlockCount = GetBlockCount();
+ if (HasBlockTypes())
+ {
+ memcpy(a_Into.m_BlockTypes, m_BlockTypes, BlockCount * sizeof(BLOCKTYPE));
+ }
+ if (HasBlockMetas())
+ {
+ memcpy(a_Into.m_BlockMetas, m_BlockMetas, BlockCount * sizeof(NIBBLETYPE));
+ }
+ if (HasBlockLights())
+ {
+ memcpy(a_Into.m_BlockLight, m_BlockLight, BlockCount * sizeof(NIBBLETYPE));
+ }
+ if (HasBlockSkyLights())
+ {
+ memcpy(a_Into.m_BlockSkyLight, m_BlockSkyLight, BlockCount * sizeof(NIBBLETYPE));
+ }
+}
+
+
+
+
+
+void cBlockArea::CopyFrom(const cBlockArea & a_From)
+{
+ a_From.CopyTo(*this);
+}
+
+
+
+
+
+void cBlockArea::DumpToRawFile(const AString & a_FileName)
+{
+ cFile f;
+ if (!f.Open(a_FileName, cFile::fmWrite))
+ {
+ LOGWARNING("cBlockArea: Cannot open file \"%s\" for raw dump", a_FileName.c_str());
+ return;
+ }
+ UInt32 SizeX = ntohl(m_SizeX);
+ UInt32 SizeY = ntohl(m_SizeY);
+ UInt32 SizeZ = ntohl(m_SizeZ);
+ f.Write(&SizeX, 4);
+ f.Write(&SizeY, 4);
+ f.Write(&SizeZ, 4);
+ unsigned char DataTypes = GetDataTypes();
+ f.Write(&DataTypes, 1);
+ int NumBlocks = GetBlockCount();
+ if (HasBlockTypes())
+ {
+ f.Write(m_BlockTypes, NumBlocks * sizeof(BLOCKTYPE));
+ }
+ if (HasBlockMetas())
+ {
+ f.Write(m_BlockMetas, NumBlocks);
+ }
+ if (HasBlockLights())
+ {
+ f.Write(m_BlockLight, NumBlocks);
+ }
+ if (HasBlockSkyLights())
+ {
+ f.Write(m_BlockSkyLight, NumBlocks);
+ }
+}
+
+
+
+
+
+bool cBlockArea::LoadFromSchematicFile(const AString & a_FileName)
+{
+ // Un-GZip the contents:
+ AString Contents;
+ cGZipFile File;
+ if (!File.Open(a_FileName, cGZipFile::fmRead))
+ {
+ LOG("Cannot open the schematic file \"%s\".", a_FileName.c_str());
+ return false;
+ }
+ int NumBytesRead = File.ReadRestOfFile(Contents);
+ if (NumBytesRead < 0)
+ {
+ LOG("Cannot read GZipped data in the schematic file \"%s\", error %d", a_FileName.c_str(), NumBytesRead);
+ return false;
+ }
+ File.Close();
+
+ // Parse the NBT:
+ cParsedNBT NBT(Contents.data(), Contents.size());
+ if (!NBT.IsValid())
+ {
+ LOG("Cannot parse the NBT in the schematic file \"%s\".", a_FileName.c_str());
+ return false;
+ }
+
+ return LoadFromSchematicNBT(NBT);
+}
+
+
+
+
+
+bool cBlockArea::SaveToSchematicFile(const AString & a_FileName)
+{
+ cFastNBTWriter Writer("Schematic");
+ Writer.AddShort("Width", m_SizeX);
+ Writer.AddShort("Height", m_SizeY);
+ Writer.AddShort("Length", m_SizeZ);
+ Writer.AddString("Materials", "Alpha");
+ if (HasBlockTypes())
+ {
+ Writer.AddByteArray("Blocks", (const char *)m_BlockTypes, GetBlockCount());
+ }
+ else
+ {
+ AString Dummy(GetBlockCount(), 0);
+ Writer.AddByteArray("Blocks", Dummy.data(), Dummy.size());
+ }
+ if (HasBlockMetas())
+ {
+ Writer.AddByteArray("Data", (const char *)m_BlockMetas, GetBlockCount());
+ }
+ else
+ {
+ AString Dummy(GetBlockCount(), 0);
+ Writer.AddByteArray("Data", Dummy.data(), Dummy.size());
+ }
+ // TODO: Save entities and block entities
+ Writer.BeginList("Entities", TAG_Compound);
+ Writer.EndList();
+ Writer.BeginList("TileEntities", TAG_Compound);
+ Writer.EndList();
+ Writer.Finish();
+
+ // Save to file
+ cGZipFile File;
+ if (!File.Open(a_FileName, cGZipFile::fmWrite))
+ {
+ LOG("Cannot open file \"%s\" for writing.", a_FileName.c_str());
+ return false;
+ }
+ if (!File.Write(Writer.GetResult()))
+ {
+ LOG("Cannot write data to file \"%s\".", a_FileName.c_str());
+ return false;
+ }
+ return true;
+}
+
+
+
+
+
+void cBlockArea::Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ)
+{
+ if (
+ (a_AddMinX + a_SubMaxX >= m_SizeX) ||
+ (a_AddMinY + a_SubMaxY >= m_SizeY) ||
+ (a_AddMinZ + a_SubMaxZ >= m_SizeZ)
+ )
+ {
+ LOGWARNING("cBlockArea:Crop called with more croping than the dimensions: %d x %d x %d with cropping %d, %d and %d",
+ m_SizeX, m_SizeY, m_SizeZ,
+ a_AddMinX + a_SubMaxX, a_AddMinY + a_SubMaxY, a_AddMinZ + a_SubMaxZ
+ );
+ return;
+ }
+
+ if (HasBlockTypes())
+ {
+ CropBlockTypes(a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
+ }
+ if (HasBlockMetas())
+ {
+ CropNibbles(m_BlockMetas, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
+ }
+ if (HasBlockLights())
+ {
+ CropNibbles(m_BlockLight, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
+ }
+ if (HasBlockSkyLights())
+ {
+ CropNibbles(m_BlockSkyLight, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
+ }
+ m_OriginX += a_AddMinX;
+ m_OriginY += a_AddMinY;
+ m_OriginZ += a_AddMinZ;
+ m_SizeX -= a_AddMinX + a_SubMaxX;
+ m_SizeY -= a_AddMinY + a_SubMaxY;
+ m_SizeZ -= a_AddMinZ + a_SubMaxZ;
+}
+
+
+
+
+
+void cBlockArea::Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ)
+{
+ if (HasBlockTypes())
+ {
+ ExpandBlockTypes(a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
+ }
+ if (HasBlockMetas())
+ {
+ ExpandNibbles(m_BlockMetas, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
+ }
+ if (HasBlockLights())
+ {
+ ExpandNibbles(m_BlockLight, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
+ }
+ if (HasBlockSkyLights())
+ {
+ ExpandNibbles(m_BlockSkyLight, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
+ }
+ m_OriginX -= a_SubMinX;
+ m_OriginY -= a_SubMinY;
+ m_OriginZ -= a_SubMinZ;
+ m_SizeX += a_SubMinX + a_AddMaxX;
+ m_SizeY += a_SubMinY + a_AddMaxY;
+ m_SizeZ += a_SubMinZ + a_AddMaxZ;
+}
+
+
+
+
+
+void cBlockArea::Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy)
+{
+ // Block types are compulsory, block metas are voluntary
+ if (!HasBlockTypes() || !a_Src.HasBlockTypes())
+ {
+ LOGWARNING("%s: cannot merge because one of the areas doesn't have blocktypes.", __FUNCTION__);
+ return;
+ }
+
+ // Dst is *this, Src is a_Src
+ int SrcOffX = std::max(0, -a_RelX); // Offset in Src where to start reading
+ int DstOffX = std::max(0, a_RelX); // Offset in Dst where to start writing
+ int SizeX = std::min(a_Src.GetSizeX() - SrcOffX, GetSizeX() - DstOffX); // How many blocks to copy
+
+ int SrcOffY = std::max(0, -a_RelY); // Offset in Src where to start reading
+ int DstOffY = std::max(0, a_RelY); // Offset in Dst where to start writing
+ int SizeY = std::min(a_Src.GetSizeY() - SrcOffY, GetSizeY() - DstOffY); // How many blocks to copy
+
+ int SrcOffZ = std::max(0, -a_RelZ); // Offset in Src where to start reading
+ int DstOffZ = std::max(0, a_RelZ); // Offset in Dst where to start writing
+ int SizeZ = std::min(a_Src.GetSizeZ() - SrcOffZ, GetSizeZ() - DstOffZ); // How many blocks to copy
+
+ const NIBBLETYPE * SrcMetas = a_Src.GetBlockMetas();
+ NIBBLETYPE * DstMetas = m_BlockMetas;
+ bool IsDummyMetas = ((SrcMetas == NULL) || (DstMetas == NULL));
+
+ if (IsDummyMetas)
+ {
+ SrcMetas = new NIBBLETYPE[a_Src.GetBlockCount()];
+ DstMetas = new NIBBLETYPE[GetBlockCount()];
+ }
+
+ switch (a_Strategy)
+ {
+ case msOverwrite:
+ {
+ InternalMergeBlocks(
+ m_BlockTypes, a_Src.GetBlockTypes(),
+ DstMetas, SrcMetas,
+ SizeX, SizeY, SizeZ,
+ SrcOffX, SrcOffY, SrcOffZ,
+ DstOffX, DstOffY, DstOffZ,
+ a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
+ m_SizeX, m_SizeY, m_SizeZ,
+ MergeCombinatorOverwrite
+ );
+ break;
+ } // case msOverwrite
+
+ case msFillAir:
+ {
+ InternalMergeBlocks(
+ m_BlockTypes, a_Src.GetBlockTypes(),
+ DstMetas, SrcMetas,
+ SizeX, SizeY, SizeZ,
+ SrcOffX, SrcOffY, SrcOffZ,
+ DstOffX, DstOffY, DstOffZ,
+ a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
+ m_SizeX, m_SizeY, m_SizeZ,
+ MergeCombinatorFillAir
+ );
+ break;
+ } // case msFillAir
+
+ case msImprint:
+ {
+ InternalMergeBlocks(
+ m_BlockTypes, a_Src.GetBlockTypes(),
+ DstMetas, SrcMetas,
+ SizeX, SizeY, SizeZ,
+ SrcOffX, SrcOffY, SrcOffZ,
+ DstOffX, DstOffY, DstOffZ,
+ a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
+ m_SizeX, m_SizeY, m_SizeZ,
+ MergeCombinatorImprint
+ );
+ break;
+ } // case msImprint
+
+ case msLake:
+ {
+ InternalMergeBlocks(
+ m_BlockTypes, a_Src.GetBlockTypes(),
+ DstMetas, SrcMetas,
+ SizeX, SizeY, SizeZ,
+ SrcOffX, SrcOffY, SrcOffZ,
+ DstOffX, DstOffY, DstOffZ,
+ a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
+ m_SizeX, m_SizeY, m_SizeZ,
+ MergeCombinatorLake
+ );
+ break;
+ } // case msLake
+
+ default:
+ {
+ LOGWARNING("Unknown block area merge strategy: %d", a_Strategy);
+ ASSERT(!"Unknown block area merge strategy");
+ break;
+ }
+ } // switch (a_Strategy)
+
+ if (IsDummyMetas)
+ {
+ delete[] SrcMetas;
+ delete[] DstMetas;
+ }
+}
+
+
+
+
+
+void cBlockArea::Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight)
+{
+ if ((a_DataTypes & GetDataTypes()) != a_DataTypes)
+ {
+ LOGWARNING("%s: requested datatypes that are not present in the BlockArea object, trimming those away (req 0x%x, stor 0x%x)",
+ __FUNCTION__, a_DataTypes, GetDataTypes()
+ );
+ a_DataTypes = a_DataTypes & GetDataTypes();
+ }
+
+ int BlockCount = GetBlockCount();
+ if ((a_DataTypes & baTypes) != 0)
+ {
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockTypes[i] = a_BlockType;
+ }
+ }
+ if ((a_DataTypes & baMetas) != 0)
+ {
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockMetas[i] = a_BlockMeta;
+ }
+ }
+ if ((a_DataTypes & baLight) != 0)
+ {
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockLight[i] = a_BlockLight;
+ }
+ }
+ if ((a_DataTypes & baSkyLight) != 0)
+ {
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockSkyLight[i] = a_BlockSkyLight;
+ }
+ }
+}
+
+
+
+
+
+void cBlockArea::FillRelCuboid(int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ,
+ int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
+ NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
+)
+{
+ if ((a_DataTypes & GetDataTypes()) != a_DataTypes)
+ {
+ LOGWARNING("%s: requested datatypes that are not present in the BlockArea object, trimming those away (req 0x%x, stor 0x%x)",
+ __FUNCTION__, a_DataTypes, GetDataTypes()
+ );
+ a_DataTypes = a_DataTypes & GetDataTypes();
+ }
+
+ if ((a_DataTypes & baTypes) != 0)
+ {
+ for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
+ {
+ m_BlockTypes[MakeIndex(x, y, z)] = a_BlockType;
+ } // for x, z, y
+ }
+ if ((a_DataTypes & baMetas) != 0)
+ {
+ for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
+ {
+ m_BlockMetas[MakeIndex(x, y, z)] = a_BlockMeta;
+ } // for x, z, y
+ }
+ if ((a_DataTypes & baLight) != 0)
+ {
+ for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
+ {
+ m_BlockLight[MakeIndex(x, y, z)] = a_BlockLight;
+ } // for x, z, y
+ }
+ if ((a_DataTypes & baSkyLight) != 0)
+ {
+ for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
+ {
+ m_BlockSkyLight[MakeIndex(x, y, z)] = a_BlockSkyLight;
+ } // for x, z, y
+ }
+}
+
+
+
+
+
+void cBlockArea::RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int a_RelY2, int a_RelZ2,
+ int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
+ NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
+)
+{
+ // Bresenham-3D algorithm for drawing lines:
+ int dx = abs(a_RelX2 - a_RelX1);
+ int dy = abs(a_RelY2 - a_RelY1);
+ int dz = abs(a_RelZ2 - a_RelZ1);
+ int sx = (a_RelX1 < a_RelX2) ? 1 : -1;
+ int sy = (a_RelY1 < a_RelY2) ? 1 : -1;
+ int sz = (a_RelZ1 < a_RelZ2) ? 1 : -1;
+ int err = dx - dz;
+
+ if (dx >= std::max(dy, dz)) // x dominant
+ {
+ int yd = dy - dx / 2;
+ int zd = dz - dx / 2;
+
+ while (true)
+ {
+ RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight);
+
+ if (a_RelX1 == a_RelX2)
+ {
+ break;
+ }
+
+ if (yd >= 0) // move along y
+ {
+ a_RelY1 += sy;
+ yd -= dx;
+ }
+
+ if (zd >= 0) // move along z
+ {
+ a_RelZ1 += sz;
+ zd -= dx;
+ }
+
+ // move along x
+ a_RelX1 += sx;
+ yd += dy;
+ zd += dz;
+ }
+ }
+ else if (dy >= std::max(dx, dz)) // y dominant
+ {
+ int xd = dx - dy / 2;
+ int zd = dz - dy / 2;
+
+ while (true)
+ {
+ RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight);
+
+ if (a_RelY1 == a_RelY2)
+ {
+ break;
+ }
+
+ if (xd >= 0) // move along x
+ {
+ a_RelX1 += sx;
+ xd -= dy;
+ }
+
+ if (zd >= 0) // move along z
+ {
+ a_RelZ1 += sz;
+ zd -= dy;
+ }
+
+ // move along y
+ a_RelY1 += sy;
+ xd += dx;
+ zd += dz;
+ }
+ }
+ else
+ {
+ // z dominant
+ ASSERT(dz >= std::max(dx, dy));
+ int xd = dx - dz / 2;
+ int yd = dy - dz / 2;
+
+ while (true)
+ {
+ RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight);
+
+ if (a_RelZ1 == a_RelZ2)
+ {
+ break;
+ }
+
+ if (xd >= 0) // move along x
+ {
+ a_RelX1 += sx;
+ xd -= dz;
+ }
+
+ if (yd >= 0) // move along y
+ {
+ a_RelY1 += sy;
+ yd -= dz;
+ }
+
+ // move along z
+ a_RelZ1 += sz;
+ xd += dx;
+ yd += dy;
+ }
+ } // if (which dimension is dominant)
+}
+
+
+
+
+
+void cBlockArea::RotateCCW(void)
+{
+ if (!HasBlockTypes())
+ {
+ LOGWARNING("cBlockArea: Cannot rotate blockmeta without blocktypes!");
+ return;
+ }
+
+ if (!HasBlockMetas())
+ {
+ // There are no blockmetas to rotate, just use the NoMeta function
+ RotateCCWNoMeta();
+ return;
+ }
+
+ // We are guaranteed that both blocktypes and blockmetas exist; rotate both at the same time:
+ BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ];
+ NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ];
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int NewZ = m_SizeX - x - 1;
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int NewX = z;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ int NewIdx = NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ;
+ int OldIdx = MakeIndex(x, y, z);
+ NewTypes[NewIdx] = m_BlockTypes[OldIdx];
+ NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCCW(m_BlockMetas[OldIdx]);
+ } // for y
+ } // for z
+ } // for x
+ std::swap(m_BlockTypes, NewTypes);
+ std::swap(m_BlockMetas, NewMetas);
+ delete[] NewTypes;
+ delete[] NewMetas;
+
+ std::swap(m_SizeX, m_SizeZ);
+}
+
+
+
+
+
+void cBlockArea::RotateCW(void)
+{
+ if (!HasBlockTypes())
+ {
+ LOGWARNING("cBlockArea: Cannot rotate blockmeta without blocktypes!");
+ return;
+ }
+
+ if (!HasBlockMetas())
+ {
+ // There are no blockmetas to rotate, just use the NoMeta function
+ RotateCWNoMeta();
+ return;
+ }
+
+ // We are guaranteed that both blocktypes and blockmetas exist; rotate both at the same time:
+ BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ];
+ NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ];
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int NewZ = x;
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int NewX = m_SizeZ - z - 1;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ int NewIdx = NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ;
+ int OldIdx = MakeIndex(x, y, z);
+ NewTypes[NewIdx] = m_BlockTypes[OldIdx];
+ NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCW(m_BlockMetas[OldIdx]);
+ } // for y
+ } // for z
+ } // for x
+ std::swap(m_BlockTypes, NewTypes);
+ std::swap(m_BlockMetas, NewMetas);
+ delete[] NewTypes;
+ delete[] NewMetas;
+
+ std::swap(m_SizeX, m_SizeZ);
+}
+
+
+
+
+
+void cBlockArea::MirrorXY(void)
+{
+ if (!HasBlockTypes())
+ {
+ LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!");
+ return;
+ }
+
+ if (!HasBlockMetas())
+ {
+ // There are no blockmetas to mirror, just use the NoMeta function
+ MirrorXYNoMeta();
+ return;
+ }
+
+ // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time:
+ int HalfZ = m_SizeZ / 2;
+ int MaxZ = m_SizeZ - 1;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ for (int z = 0; z < HalfZ; z++)
+ {
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int Idx1 = MakeIndex(x, y, z);
+ int Idx2 = MakeIndex(x, y, MaxZ - z);
+ std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]);
+ NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorXY(m_BlockMetas[Idx1]);
+ NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorXY(m_BlockMetas[Idx2]);
+ m_BlockMetas[Idx1] = Meta2;
+ m_BlockMetas[Idx2] = Meta1;
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+void cBlockArea::MirrorXZ(void)
+{
+ if (!HasBlockTypes())
+ {
+ LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!");
+ return;
+ }
+
+ if (!HasBlockMetas())
+ {
+ // There are no blockmetas to mirror, just use the NoMeta function
+ MirrorXZNoMeta();
+ return;
+ }
+
+ // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time:
+ int HalfY = m_SizeY / 2;
+ int MaxY = m_SizeY - 1;
+ for (int y = 0; y < HalfY; y++)
+ {
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int Idx1 = MakeIndex(x, y, z);
+ int Idx2 = MakeIndex(x, MaxY - y, z);
+ std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]);
+ NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorXZ(m_BlockMetas[Idx1]);
+ NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorXZ(m_BlockMetas[Idx2]);
+ m_BlockMetas[Idx1] = Meta2;
+ m_BlockMetas[Idx2] = Meta1;
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+void cBlockArea::MirrorYZ(void)
+{
+ if (!HasBlockTypes())
+ {
+ LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!");
+ return;
+ }
+
+ if (!HasBlockMetas())
+ {
+ // There are no blockmetas to mirror, just use the NoMeta function
+ MirrorYZNoMeta();
+ return;
+ }
+
+ // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time:
+ int HalfX = m_SizeX / 2;
+ int MaxX = m_SizeX - 1;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ for (int x = 0; x < HalfX; x++)
+ {
+ int Idx1 = MakeIndex(x, y, z);
+ int Idx2 = MakeIndex(MaxX - x, y, z);
+ std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]);
+ NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorYZ(m_BlockMetas[Idx1]);
+ NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorYZ(m_BlockMetas[Idx2]);
+ m_BlockMetas[Idx1] = Meta2;
+ m_BlockMetas[Idx2] = Meta1;
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+void cBlockArea::RotateCCWNoMeta(void)
+{
+ if (HasBlockTypes())
+ {
+ BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ];
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int NewZ = m_SizeX - x - 1;
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int NewX = z;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ NewTypes[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)];
+ } // for y
+ } // for z
+ } // for x
+ std::swap(m_BlockTypes, NewTypes);
+ delete[] NewTypes;
+ }
+ if (HasBlockMetas())
+ {
+ NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ];
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int NewZ = m_SizeX - x - 1;
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int NewX = z;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ NewMetas[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)];
+ } // for y
+ } // for z
+ } // for x
+ std::swap(m_BlockMetas, NewMetas);
+ delete[] NewMetas;
+ }
+ std::swap(m_SizeX, m_SizeZ);
+}
+
+
+
+
+
+void cBlockArea::RotateCWNoMeta(void)
+{
+ if (HasBlockTypes())
+ {
+ BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ];
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int NewX = m_SizeZ - z - 1;
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int NewZ = x;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ NewTypes[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)];
+ } // for y
+ } // for x
+ } // for z
+ std::swap(m_BlockTypes, NewTypes);
+ delete[] NewTypes;
+ }
+ if (HasBlockMetas())
+ {
+ NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ];
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int NewX = m_SizeZ - z - 1;
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int NewZ = x;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ NewMetas[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)];
+ } // for y
+ } // for x
+ } // for z
+ std::swap(m_BlockMetas, NewMetas);
+ delete[] NewMetas;
+ }
+ std::swap(m_SizeX, m_SizeZ);
+}
+
+
+
+
+
+void cBlockArea::MirrorXYNoMeta(void)
+{
+ int HalfZ = m_SizeZ / 2;
+ int MaxZ = m_SizeZ - 1;
+ if (HasBlockTypes())
+ {
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ for (int z = 0; z < HalfZ; z++)
+ {
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(x, y, MaxZ - z)]);
+ } // for x
+ } // for z
+ } // for y
+ } // if (HasBlockTypes)
+
+ if (HasBlockMetas())
+ {
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ for (int z = 0; z < HalfZ; z++)
+ {
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(x, y, MaxZ - z)]);
+ } // for x
+ } // for z
+ } // for y
+ } // if (HasBlockMetas)
+}
+
+
+
+
+
+void cBlockArea::MirrorXZNoMeta(void)
+{
+ int HalfY = m_SizeY / 2;
+ int MaxY = m_SizeY - 1;
+ if (HasBlockTypes())
+ {
+ for (int y = 0; y < HalfY; y++)
+ {
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(x, MaxY - y, z)]);
+ } // for x
+ } // for z
+ } // for y
+ } // if (HasBlockTypes)
+
+ if (HasBlockMetas())
+ {
+ for (int y = 0; y < HalfY; y++)
+ {
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(x, MaxY - y, z)]);
+ } // for x
+ } // for z
+ } // for y
+ } // if (HasBlockMetas)
+}
+
+
+
+
+
+void cBlockArea::MirrorYZNoMeta(void)
+{
+ int HalfX = m_SizeX / 2;
+ int MaxX = m_SizeX - 1;
+ if (HasBlockTypes())
+ {
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ for (int x = 0; x < HalfX; x++)
+ {
+ std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(MaxX - x, y, z)]);
+ } // for x
+ } // for z
+ } // for y
+ } // if (HasBlockTypes)
+
+ if (HasBlockMetas())
+ {
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ for (int x = 0; x < HalfX; x++)
+ {
+ std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(MaxX - x, y, z)]);
+ } // for x
+ } // for z
+ } // for y
+ } // if (HasBlockMetas)
+}
+
+
+
+
+
+void cBlockArea::SetRelBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType)
+{
+ if (m_BlockTypes == NULL)
+ {
+ LOGWARNING("cBlockArea: BlockTypes have not been read!");
+ return;
+ }
+ m_BlockTypes[MakeIndex(a_RelX, a_RelY, a_RelZ)] = a_BlockType;
+}
+
+
+
+
+
+void cBlockArea::SetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType)
+{
+ SetRelBlockType(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_BlockType);
+}
+
+
+
+
+
+void cBlockArea::SetRelBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta)
+{
+ SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockMeta, m_BlockMetas);
+}
+
+
+
+
+
+void cBlockArea::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta)
+{
+ SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta, m_BlockMetas);
+}
+
+
+
+
+
+void cBlockArea::SetRelBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockLight)
+{
+ SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockLight, m_BlockLight);
+}
+
+
+
+
+
+void cBlockArea::SetBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockLight)
+{
+ SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockLight, m_BlockLight);
+}
+
+
+
+
+
+void cBlockArea::SetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockSkyLight)
+{
+ SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockSkyLight, m_BlockSkyLight);
+}
+
+
+
+
+
+void cBlockArea::SetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockSkyLight)
+{
+ SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockSkyLight, m_BlockSkyLight);
+}
+
+
+
+
+
+BLOCKTYPE cBlockArea::GetRelBlockType(int a_RelX, int a_RelY, int a_RelZ) const
+{
+ if (m_BlockTypes == NULL)
+ {
+ LOGWARNING("cBlockArea: BlockTypes have not been read!");
+ return E_BLOCK_AIR;
+ }
+ return m_BlockTypes[MakeIndex(a_RelX, a_RelY, a_RelZ)];
+}
+
+
+
+
+
+BLOCKTYPE cBlockArea::GetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ) const
+{
+ return GetRelBlockType(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ);
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetRelBlockMeta(int a_RelX, int a_RelY, int a_RelZ) const
+{
+ return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockMetas);
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ) const
+{
+ return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockMetas);
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetRelBlockLight(int a_RelX, int a_RelY, int a_RelZ) const
+{
+ return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockLight);
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ) const
+{
+ return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockLight);
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ) const
+{
+ return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockSkyLight);
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ) const
+{
+ return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockSkyLight);
+}
+
+
+
+
+
+void cBlockArea::SetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ SetRelBlockTypeMeta(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_BlockType, a_BlockMeta);
+}
+
+
+
+
+
+void cBlockArea::SetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ int idx = MakeIndex(a_RelX, a_RelY, a_RelZ);
+ if (m_BlockTypes == NULL)
+ {
+ LOGWARNING("%s: BlockTypes not available but requested to be written to.", __FUNCTION__);
+ }
+ else
+ {
+ m_BlockTypes[idx] = a_BlockType;
+ }
+ if (m_BlockMetas == NULL)
+ {
+ LOGWARNING("%s: BlockMetas not available but requested to be written to.", __FUNCTION__);
+ }
+ else
+ {
+ m_BlockMetas[idx] = a_BlockMeta;
+ }
+}
+
+
+
+
+
+void cBlockArea::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const
+{
+ return GetRelBlockTypeMeta(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_BlockType, a_BlockMeta);
+}
+
+
+
+
+
+void cBlockArea::GetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const
+{
+ int idx = MakeIndex(a_RelX, a_RelY, a_RelZ);
+ if (m_BlockTypes == NULL)
+ {
+ LOGWARNING("cBlockArea: BlockTypes have not been read!");
+ a_BlockType = E_BLOCK_AIR;
+ }
+ else
+ {
+ a_BlockType = m_BlockTypes[idx];
+ }
+
+ if (m_BlockMetas == NULL)
+ {
+ LOGWARNING("cBlockArea: BlockMetas have not been read!");
+ a_BlockMeta = 0;
+ }
+ else
+ {
+ a_BlockMeta = m_BlockMetas[idx];
+ }
+}
+
+
+
+
+
+int cBlockArea::GetDataTypes(void) const
+{
+ int res = 0;
+ if (m_BlockTypes != NULL)
+ {
+ res |= baTypes;
+ }
+ if (m_BlockMetas != NULL)
+ {
+ res |= baMetas;
+ }
+ if (m_BlockLight != NULL)
+ {
+ res |= baLight;
+ }
+ if (m_BlockSkyLight != NULL)
+ {
+ res |= baSkyLight;
+ }
+ return res;
+}
+
+
+
+
+
+bool cBlockArea::SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
+{
+ ASSERT(m_BlockTypes == NULL); // Has been cleared
+
+ if (a_DataTypes & baTypes)
+ {
+ m_BlockTypes = new BLOCKTYPE[a_SizeX * a_SizeY * a_SizeZ];
+ if (m_BlockTypes == NULL)
+ {
+ return false;
+ }
+ }
+ if (a_DataTypes & baMetas)
+ {
+ m_BlockMetas = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ];
+ if (m_BlockMetas == NULL)
+ {
+ delete[] m_BlockTypes;
+ return false;
+ }
+ }
+ if (a_DataTypes & baLight)
+ {
+ m_BlockLight = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ];
+ if (m_BlockLight == NULL)
+ {
+ delete[] m_BlockMetas;
+ delete[] m_BlockTypes;
+ return false;
+ }
+ }
+ if (a_DataTypes & baSkyLight)
+ {
+ m_BlockSkyLight = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ];
+ if (m_BlockSkyLight == NULL)
+ {
+ delete[] m_BlockLight;
+ delete[] m_BlockMetas;
+ delete[] m_BlockTypes;
+ return false;
+ }
+ }
+ m_SizeX = a_SizeX;
+ m_SizeY = a_SizeY;
+ m_SizeZ = a_SizeZ;
+ return true;
+}
+
+
+
+
+
+int cBlockArea::MakeIndex(int a_RelX, int a_RelY, int a_RelZ) const
+{
+ ASSERT(a_RelX >= 0);
+ ASSERT(a_RelX < m_SizeX);
+ ASSERT(a_RelY >= 0);
+ ASSERT(a_RelY < m_SizeY);
+ ASSERT(a_RelZ >= 0);
+ ASSERT(a_RelZ < m_SizeZ);
+
+ return a_RelX + a_RelZ * m_SizeX + a_RelY * m_SizeX * m_SizeZ;
+}
+
+
+
+
+
+void cBlockArea::SetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array)
+{
+ if (a_Array == NULL)
+ {
+ LOGWARNING("cBlockArea: datatype has not been read!");
+ return;
+ }
+ a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)] = a_Value;
+}
+
+
+
+
+
+void cBlockArea::SetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array)
+{
+ SetRelNibble(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_Value, a_Array);
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE * a_Array) const
+{
+ if (a_Array == NULL)
+ {
+ LOGWARNING("cBlockArea: datatype has not been read!");
+ return 16;
+ }
+ return a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)];
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE * a_Array) const
+{
+ return GetRelNibble(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_Array);
+}
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cBlockArea::cChunkReader:
+
+cBlockArea::cChunkReader::cChunkReader(cBlockArea & a_Area) :
+ m_Area(a_Area),
+ m_OriginX(a_Area.m_OriginX),
+ m_OriginY(a_Area.m_OriginY),
+ m_OriginZ(a_Area.m_OriginZ)
+{
+}
+
+
+
+
+
+void cBlockArea::cChunkReader::CopyNibbles(NIBBLETYPE * a_AreaDst, const NIBBLETYPE * a_ChunkSrc)
+{
+ int SizeY = m_Area.m_SizeY;
+ int MinY = m_OriginY;
+
+ // SizeX, SizeZ are the dmensions of the block data to copy from the current chunk (size of the geometric union)
+ // OffX, OffZ are the offsets of the current chunk data from the area origin
+ // BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders
+ int SizeX = cChunkDef::Width;
+ int SizeZ = cChunkDef::Width;
+ int OffX, OffZ;
+ int BaseX, BaseZ;
+ OffX = m_CurrentChunkX * cChunkDef::Width - m_OriginX;
+ if (OffX < 0)
+ {
+ BaseX = -OffX;
+ SizeX += OffX; // SizeX is decreased, OffX is negative
+ OffX = 0;
+ }
+ else
+ {
+ BaseX = 0;
+ }
+ OffZ = m_CurrentChunkZ * cChunkDef::Width - m_OriginZ;
+ if (OffZ < 0)
+ {
+ BaseZ = -OffZ;
+ SizeZ += OffZ; // SizeZ is decreased, OffZ is negative
+ OffZ = 0;
+ }
+ else
+ {
+ BaseZ = 0;
+ }
+ // If the chunk extends beyond the area in the X or Z axis, cut off the Size:
+ if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_OriginX + m_Area.m_SizeX)
+ {
+ SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_OriginX + m_Area.m_SizeX);
+ }
+ if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_OriginZ + m_Area.m_SizeZ)
+ {
+ SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_OriginZ + m_Area.m_SizeZ);
+ }
+
+ for (int y = 0; y < SizeY; y++)
+ {
+ int ChunkY = MinY + y;
+ int AreaY = y;
+ for (int z = 0; z < SizeZ; z++)
+ {
+ int ChunkZ = BaseZ + z;
+ int AreaZ = OffZ + z;
+ for (int x = 0; x < SizeX; x++)
+ {
+ int ChunkX = BaseX + x;
+ int AreaX = OffX + x;
+ a_AreaDst[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = cChunkDef::GetNibble(a_ChunkSrc, ChunkX, ChunkY, ChunkZ);
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+bool cBlockArea::cChunkReader::Coords(int a_ChunkX, int a_ChunkZ)
+{
+ m_CurrentChunkX = a_ChunkX;
+ m_CurrentChunkZ = a_ChunkZ;
+ return true;
+}
+
+
+
+
+
+void cBlockArea::cChunkReader::BlockTypes(const BLOCKTYPE * a_BlockTypes)
+{
+ if (m_Area.m_BlockTypes == NULL)
+ {
+ // Don't want BlockTypes
+ return;
+ }
+
+ int SizeY = m_Area.m_SizeY;
+ int MinY = m_OriginY;
+
+ // SizeX, SizeZ are the dmensions of the block data to copy from the current chunk (size of the geometric union)
+ // OffX, OffZ are the offsets of the current chunk data from the area origin
+ // BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders
+ int SizeX = cChunkDef::Width;
+ int SizeZ = cChunkDef::Width;
+ int OffX, OffZ;
+ int BaseX, BaseZ;
+ OffX = m_CurrentChunkX * cChunkDef::Width - m_OriginX;
+ if (OffX < 0)
+ {
+ BaseX = -OffX;
+ SizeX += OffX; // SizeX is decreased, OffX is negative
+ OffX = 0;
+ }
+ else
+ {
+ BaseX = 0;
+ }
+ OffZ = m_CurrentChunkZ * cChunkDef::Width - m_OriginZ;
+ if (OffZ < 0)
+ {
+ BaseZ = -OffZ;
+ SizeZ += OffZ; // SizeZ is decreased, OffZ is negative
+ OffZ = 0;
+ }
+ else
+ {
+ BaseZ = 0;
+ }
+ // If the chunk extends beyond the area in the X or Z axis, cut off the Size:
+ if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_OriginX + m_Area.m_SizeX)
+ {
+ SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_OriginX + m_Area.m_SizeX);
+ }
+ if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_OriginZ + m_Area.m_SizeZ)
+ {
+ SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_OriginZ + m_Area.m_SizeZ);
+ }
+
+ for (int y = 0; y < SizeY; y++)
+ {
+ int ChunkY = MinY + y;
+ int AreaY = y;
+ for (int z = 0; z < SizeZ; z++)
+ {
+ int ChunkZ = BaseZ + z;
+ int AreaZ = OffZ + z;
+ for (int x = 0; x < SizeX; x++)
+ {
+ int ChunkX = BaseX + x;
+ int AreaX = OffX + x;
+ m_Area.m_BlockTypes[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = cChunkDef::GetBlock(a_BlockTypes, ChunkX, ChunkY, ChunkZ);
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+void cBlockArea::cChunkReader::BlockMeta(const NIBBLETYPE * a_BlockMetas)
+{
+ if (m_Area.m_BlockMetas == NULL)
+ {
+ // Don't want metas
+ return;
+ }
+ CopyNibbles(m_Area.m_BlockMetas, a_BlockMetas);
+}
+
+
+
+
+
+void cBlockArea::cChunkReader::BlockLight(const NIBBLETYPE * a_BlockLight)
+{
+ if (m_Area.m_BlockLight == NULL)
+ {
+ // Don't want light
+ return;
+ }
+ CopyNibbles(m_Area.m_BlockLight, a_BlockLight);
+}
+
+
+
+
+
+void cBlockArea::cChunkReader::BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight)
+{
+ if (m_Area.m_BlockSkyLight == NULL)
+ {
+ // Don't want skylight
+ return;
+ }
+ CopyNibbles(m_Area.m_BlockSkyLight, a_BlockSkyLight);
+}
+
+
+
+
+
+void cBlockArea::CropBlockTypes(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ)
+{
+ int NewSizeX = GetSizeX() - a_AddMinX - a_SubMaxX;
+ int NewSizeY = GetSizeY() - a_AddMinY - a_SubMaxY;
+ int NewSizeZ = GetSizeZ() - a_AddMinZ - a_SubMaxZ;
+ BLOCKTYPE * NewBlockTypes = new BLOCKTYPE[NewSizeX * NewSizeY * NewSizeZ];
+ int idx = 0;
+ for (int y = 0; y < NewSizeY; y++)
+ {
+ for (int z = 0; z < NewSizeZ; z++)
+ {
+ for (int x = 0; x < NewSizeX; x++)
+ {
+ int OldIndex = MakeIndex(x + a_AddMinX, y + a_AddMinY, z + a_AddMinZ);
+ NewBlockTypes[idx++] = m_BlockTypes[OldIndex];
+ } // for x
+ } // for z
+ } // for y
+ delete m_BlockTypes;
+ m_BlockTypes = NewBlockTypes;
+}
+
+
+
+
+
+void cBlockArea::CropNibbles(NIBBLEARRAY & a_Array, int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ)
+{
+ int NewSizeX = GetSizeX() - a_AddMinX - a_SubMaxX;
+ int NewSizeY = GetSizeY() - a_AddMinY - a_SubMaxY;
+ int NewSizeZ = GetSizeZ() - a_AddMinZ - a_SubMaxZ;
+ NIBBLETYPE * NewNibbles = new NIBBLETYPE[NewSizeX * NewSizeY * NewSizeZ];
+ int idx = 0;
+ for (int y = 0; y < NewSizeY; y++)
+ {
+ for (int z = 0; z < NewSizeZ; z++)
+ {
+ for (int x = 0; x < NewSizeX; x++)
+ {
+ NewNibbles[idx++] = a_Array[MakeIndex(x + a_AddMinX, y + a_AddMinY, z + a_AddMinZ)];
+ } // for x
+ } // for z
+ } // for y
+ delete a_Array;
+ a_Array = NewNibbles;
+}
+
+
+
+
+
+void cBlockArea::ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ)
+{
+ int NewSizeX = m_SizeX + a_SubMinX + a_AddMaxX;
+ int NewSizeY = m_SizeY + a_SubMinY + a_AddMaxY;
+ int NewSizeZ = m_SizeZ + a_SubMinZ + a_AddMaxZ;
+ int BlockCount = NewSizeX * NewSizeY * NewSizeZ;
+ BLOCKTYPE * NewBlockTypes = new BLOCKTYPE[BlockCount];
+ memset(NewBlockTypes, 0, BlockCount * sizeof(BLOCKTYPE));
+ int OldIndex = 0;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ int IndexBaseY = (y + a_SubMinY) * m_SizeX * m_SizeZ;
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int IndexBaseZ = IndexBaseY + (z + a_SubMinZ) * m_SizeX;
+ int idx = IndexBaseZ + a_SubMinX;
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ NewBlockTypes[idx++] = m_BlockTypes[OldIndex++];
+ } // for x
+ } // for z
+ } // for y
+ delete m_BlockTypes;
+ m_BlockTypes = NewBlockTypes;
+}
+
+
+
+
+
+void cBlockArea::ExpandNibbles(NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ)
+{
+ int NewSizeX = m_SizeX + a_SubMinX + a_AddMaxX;
+ int NewSizeY = m_SizeY + a_SubMinY + a_AddMaxY;
+ int NewSizeZ = m_SizeZ + a_SubMinZ + a_AddMaxZ;
+ int BlockCount = NewSizeX * NewSizeY * NewSizeZ;
+ NIBBLETYPE * NewNibbles = new NIBBLETYPE[BlockCount];
+ memset(NewNibbles, 0, BlockCount * sizeof(NIBBLETYPE));
+ int OldIndex = 0;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ int IndexBaseY = (y + a_SubMinY) * m_SizeX * m_SizeZ;
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int IndexBaseZ = IndexBaseY + (z + a_SubMinZ) * m_SizeX;
+ int idx = IndexBaseZ + a_SubMinX;
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ NewNibbles[idx++] = a_Array[OldIndex++];
+ } // for x
+ } // for z
+ } // for y
+ delete a_Array;
+ a_Array = NewNibbles;
+}
+
+
+
+
+
+bool cBlockArea::LoadFromSchematicNBT(cParsedNBT & a_NBT)
+{
+ int TMaterials = a_NBT.FindChildByName(a_NBT.GetRoot(), "Materials");
+ if ((TMaterials > 0) && (a_NBT.GetType(TMaterials) == TAG_String))
+ {
+ AString Materials = a_NBT.GetString(TMaterials);
+ if (Materials.compare("Alpha") != 0)
+ {
+ LOG("Materials tag is present and \"%s\" instead of \"Alpha\". Possibly a wrong-format schematic file.", Materials.c_str());
+ return false;
+ }
+ }
+ int TSizeX = a_NBT.FindChildByName(a_NBT.GetRoot(), "Width");
+ int TSizeY = a_NBT.FindChildByName(a_NBT.GetRoot(), "Height");
+ int TSizeZ = a_NBT.FindChildByName(a_NBT.GetRoot(), "Length");
+ if (
+ (TSizeX < 0) || (TSizeY < 0) || (TSizeZ < 0) ||
+ (a_NBT.GetType(TSizeX) != TAG_Short) ||
+ (a_NBT.GetType(TSizeY) != TAG_Short) ||
+ (a_NBT.GetType(TSizeZ) != TAG_Short)
+ )
+ {
+ LOG("Dimensions are missing from the schematic file (%d, %d, %d), (%d, %d, %d)",
+ TSizeX, TSizeY, TSizeZ,
+ a_NBT.GetType(TSizeX), a_NBT.GetType(TSizeY), a_NBT.GetType(TSizeZ)
+ );
+ return false;
+ }
+
+ int SizeX = a_NBT.GetShort(TSizeX);
+ int SizeY = a_NBT.GetShort(TSizeY);
+ int SizeZ = a_NBT.GetShort(TSizeZ);
+ if ((SizeX < 1) || (SizeY < 1) || (SizeZ < 1))
+ {
+ LOG("Dimensions are invalid in the schematic file: %d, %d, %d", SizeX, SizeY, SizeZ);
+ return false;
+ }
+
+ int TBlockTypes = a_NBT.FindChildByName(a_NBT.GetRoot(), "Blocks");
+ int TBlockMetas = a_NBT.FindChildByName(a_NBT.GetRoot(), "Data");
+ if ((TBlockTypes < 0) || (a_NBT.GetType(TBlockTypes) != TAG_ByteArray))
+ {
+ LOG("BlockTypes are invalid in the schematic file: %d", TBlockTypes);
+ return false;
+ }
+ bool AreMetasPresent = (TBlockMetas > 0) && (a_NBT.GetType(TBlockMetas) == TAG_ByteArray);
+
+ Clear();
+ SetSize(SizeX, SizeY, SizeZ, AreMetasPresent ? (baTypes | baMetas) : baTypes);
+
+ // Copy the block types and metas:
+ int NumBytes = m_SizeX * m_SizeY * m_SizeZ;
+ if (a_NBT.GetDataLength(TBlockTypes) < NumBytes)
+ {
+ LOG("BlockTypes truncated in the schematic file (exp %d, got %d bytes). Loading partial.",
+ NumBytes, a_NBT.GetDataLength(TBlockTypes)
+ );
+ NumBytes = a_NBT.GetDataLength(TBlockTypes);
+ }
+ memcpy(m_BlockTypes, a_NBT.GetData(TBlockTypes), NumBytes);
+
+ if (AreMetasPresent)
+ {
+ int NumBytes = m_SizeX * m_SizeY * m_SizeZ;
+ if (a_NBT.GetDataLength(TBlockMetas) < NumBytes)
+ {
+ LOG("BlockMetas truncated in the schematic file (exp %d, got %d bytes). Loading partial.",
+ NumBytes, a_NBT.GetDataLength(TBlockMetas)
+ );
+ NumBytes = a_NBT.GetDataLength(TBlockMetas);
+ }
+ memcpy(m_BlockMetas, a_NBT.GetData(TBlockMetas), NumBytes);
+ }
+
+ return true;
+}
+
+
+
+
+void cBlockArea::RelSetData(
+ int a_RelX, int a_RelY, int a_RelZ,
+ int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
+ NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
+)
+{
+ int Index = MakeIndex(a_RelX, a_RelY, a_RelZ);
+ if ((a_DataTypes & baTypes) != 0)
+ {
+ m_BlockTypes[Index] = a_BlockType;
+ }
+ if ((a_DataTypes & baMetas) != 0)
+ {
+ m_BlockMetas[Index] = a_BlockMeta;
+ }
+ if ((a_DataTypes & baLight) != 0)
+ {
+ m_BlockLight[Index] = a_BlockLight;
+ }
+ if ((a_DataTypes & baSkyLight) != 0)
+ {
+ m_BlockSkyLight[Index] = a_BlockSkyLight;
+ }
+}
+
+
+
+
diff --git a/source/BlockArea.h b/source/BlockArea.h
index 76c4f749a..075cc99ec 100644
--- a/source/BlockArea.h
+++ b/source/BlockArea.h
@@ -1,310 +1,310 @@
-
-// BlockArea.h
-
-// Interfaces to the cBlockArea object representing an area of block data that can be queried from cWorld and then accessed again without further queries
-// The object also supports writing the blockdata back into cWorld, even into other coords
-
-// NOTE: All Nibble values (meta, blocklight, skylight) are stored one-nibble-per-byte for faster access / editting!
-
-
-
-
-
-#pragma once
-
-
-
-
-
-// fwd: World.h
-class cWorld;
-
-// fwd: FastNBT.h
-class cParsedNBT;
-
-
-
-
-
-// tolua_begin
-class cBlockArea
-{
- // tolua_end
- DISALLOW_COPY_AND_ASSIGN(cBlockArea);
- // tolua_begin
-
-public:
-
- /// What data is to be queried (bit-mask)
- enum
- {
- baTypes = 1,
- baMetas = 2,
- baLight = 4,
- baSkyLight = 8,
- } ;
-
- enum eMergeStrategy
- {
- msOverwrite,
- msFillAir,
- msImprint,
- msLake,
- } ;
-
- cBlockArea(void);
- ~cBlockArea();
-
- /// Clears the data stored to reclaim memory
- void Clear(void);
-
- /** Creates a new area of the specified size and contents.
- Origin is set to all zeroes.
- BlockTypes are set to air, block metas to zero, blocklights to zero and skylights to full light.
- */
- void Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes = baTypes | baMetas);
-
- /// Resets the origin. No other changes are made, contents are untouched.
- void SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ);
-
- /// Reads an area of blocks specified. Returns true if successful. All coords are inclusive.
- bool Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes = baTypes | baMetas);
-
- // TODO: Write() is not too good an interface: if it fails, there's no way to repeat only for the parts that didn't write
- // A better way may be to return a list of cBlockAreas for each part that didn't succeed writing, so that the caller may try again
-
- /// Writes the area back into cWorld at the coords specified. Returns true if successful in all chunks, false if only partially / not at all
- bool Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes = baTypes | baMetas);
-
- /// Copies this object's contents into the specified BlockArea.
- void CopyTo(cBlockArea & a_Into) const;
-
- /// Copies the contents from the specified BlockArea into this object.
- void CopyFrom(const cBlockArea & a_From);
-
- /// For testing purposes only, dumps the area into a file.
- void DumpToRawFile(const AString & a_FileName);
-
- /// Loads an area from a .schematic file. Returns true if successful
- bool LoadFromSchematicFile(const AString & a_FileName);
-
- /// Saves the area into a .schematic file. Returns true if successful
- bool SaveToSchematicFile(const AString & a_FileName);
-
- /// Crops the internal contents by the specified amount of blocks from each border.
- void Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ);
-
- /// Expands the internal contents by the specified amount of blocks from each border
- void Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
-
- /** Merges another block area into this one, using the specified block combinating strategy
- This function combines another BlockArea into the current object.
- The strategy parameter specifies how individual blocks are combined together, using the table below.
-
- | area block | result |
- | this | Src | msOverwrite | msFillAir | msImprint |
- +------+-----+-------------+-----------+-----------+
- | air | air | air | air | air |
- | A | air | air | A | A |
- | air | B | B | B | B |
- | A | B | B | A | B |
-
- So to sum up:
- - msOverwrite completely overwrites all blocks with the Src's blocks
- - msFillAir overwrites only those blocks that were air
- - msImprint overwrites with only those blocks that are non-air
-
- Special strategies:
- msLake (evaluate top-down, first match wins):
- | area block | |
- | this | Src | result |
- +----------+--------+--------+
- | A | sponge | A | Sponge is the NOP block
- | * | air | air | Air always gets hollowed out, even under the oceans
- | water | * | water | Water is never overwritten
- | lava | * | lava | Lava is never overwritten
- | * | water | water | Water always overwrites anything
- | * | lava | lava | Lava always overwrites anything
- | dirt | stone | stone | Stone overwrites dirt
- | grass | stone | stone | ... and grass
- | mycelium | stone | stone | ... and mycelium
- | A | stone | A | ... but nothing else
- | A | * | A | Everything else is left as it is
-
- */
- void Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy);
-
- /// Fills the entire block area with the specified data
- void Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0, NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f);
-
- /// Fills a cuboid inside the block area with the specified data
- void FillRelCuboid(int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ,
- int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0,
- NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f
- );
-
- /// Draws a line from between two points with the specified data
- void RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int a_RelY2, int a_RelZ2,
- int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0,
- NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f
- );
-
- /// Rotates the entire area counter-clockwise around the Y axis
- void RotateCCW(void);
-
- /// Rotates the entire area clockwise around the Y axis
- void RotateCW(void);
-
- /// Mirrors the entire area around the XY plane
- void MirrorXY(void);
-
- /// Mirrors the entire area around the XZ plane
- void MirrorXZ(void);
-
- /// Mirrors the entire area around the YZ plane
- void MirrorYZ(void);
-
- /// Rotates the entire area counter-clockwise around the Y axis, doesn't use blockhandlers for block meta
- void RotateCCWNoMeta(void);
-
- /// Rotates the entire area clockwise around the Y axis, doesn't use blockhandlers for block meta
- void RotateCWNoMeta(void);
-
- /// Mirrors the entire area around the XY plane, doesn't use blockhandlers for block meta
- void MirrorXYNoMeta(void);
-
- /// Mirrors the entire area around the XZ plane, doesn't use blockhandlers for block meta
- void MirrorXZNoMeta(void);
-
- /// Mirrors the entire area around the YZ plane, doesn't use blockhandlers for block meta
- void MirrorYZNoMeta(void);
-
- // Setters:
- void SetRelBlockType (int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType);
- void SetBlockType (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType);
- void SetRelBlockMeta (int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta);
- void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta);
- void SetRelBlockLight (int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockLight);
- void SetBlockLight (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockLight);
- void SetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockSkyLight);
- void SetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockSkyLight);
-
- // Getters:
- BLOCKTYPE GetRelBlockType (int a_RelX, int a_RelY, int a_RelZ) const;
- BLOCKTYPE GetBlockType (int a_BlockX, int a_BlockY, int a_BlockZ) const;
- NIBBLETYPE GetRelBlockMeta (int a_RelX, int a_RelY, int a_RelZ) const;
- NIBBLETYPE GetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ) const;
- NIBBLETYPE GetRelBlockLight (int a_RelX, int a_RelY, int a_RelZ) const;
- NIBBLETYPE GetBlockLight (int a_BlockX, int a_BlockY, int a_BlockZ) const;
- NIBBLETYPE GetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ) const;
- NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ) const;
-
- void SetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
- void SetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
- void GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const;
- void GetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const;
-
- int GetSizeX(void) const { return m_SizeX; }
- int GetSizeY(void) const { return m_SizeY; }
- int GetSizeZ(void) const { return m_SizeZ; }
-
- int GetOriginX(void) const { return m_OriginX; }
- int GetOriginY(void) const { return m_OriginY; }
- int GetOriginZ(void) const { return m_OriginZ; }
-
- /// Returns the datatypes that are stored in the object (bitmask of baXXX values)
- int GetDataTypes(void) const;
-
- bool HasBlockTypes (void) const { return (m_BlockTypes != NULL); }
- bool HasBlockMetas (void) const { return (m_BlockMetas != NULL); }
- bool HasBlockLights (void) const { return (m_BlockLight != NULL); }
- bool HasBlockSkyLights(void) const { return (m_BlockSkyLight != NULL); }
-
- // tolua_end
-
- // Clients can use these for faster access to all blocktypes. Be careful though!
- /// Returns the internal pointer to the block types
- BLOCKTYPE * GetBlockTypes (void) const { return m_BlockTypes; }
- NIBBLETYPE * GetBlockMetas (void) const { return m_BlockMetas; } // NOTE: one byte per block!
- NIBBLETYPE * GetBlockLight (void) const { return m_BlockLight; } // NOTE: one byte per block!
- NIBBLETYPE * GetBlockSkyLight(void) const { return m_BlockSkyLight; } // NOTE: one byte per block!
- int GetBlockCount(void) const { return m_SizeX * m_SizeY * m_SizeZ; }
- int MakeIndex(int a_RelX, int a_RelY, int a_RelZ) const;
-
-protected:
- friend class cChunkDesc;
-
- class cChunkReader :
- public cChunkDataCallback
- {
- public:
- cChunkReader(cBlockArea & a_Area);
-
- protected:
- cBlockArea & m_Area;
- int m_OriginX;
- int m_OriginY;
- int m_OriginZ;
- int m_CurrentChunkX;
- int m_CurrentChunkZ;
-
- void CopyNibbles(NIBBLETYPE * a_AreaDst, const NIBBLETYPE * a_ChunkSrc);
-
- // cChunkDataCallback overrides:
- virtual bool Coords (int a_ChunkX, int a_ChunkZ) override;
- virtual void BlockTypes (const BLOCKTYPE * a_BlockTypes) override;
- virtual void BlockMeta (const NIBBLETYPE * a_BlockMetas) override;
- virtual void BlockLight (const NIBBLETYPE * a_BlockLight) override;
- virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override;
- } ;
-
- typedef NIBBLETYPE * NIBBLEARRAY;
-
-
- int m_OriginX;
- int m_OriginY;
- int m_OriginZ;
- int m_SizeX;
- int m_SizeY;
- int m_SizeZ;
-
- BLOCKTYPE * m_BlockTypes;
- NIBBLETYPE * m_BlockMetas; // Each meta is stored as a separate byte for faster access
- NIBBLETYPE * m_BlockLight; // Each light value is stored as a separate byte for faster access
- NIBBLETYPE * m_BlockSkyLight; // Each light value is stored as a separate byte for faster access
-
- /// Clears the data stored and prepares a fresh new block area with the specified dimensions
- bool SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes);
-
- // Basic Setters:
- void SetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array);
- void SetNibble (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array);
-
- // Basic Getters:
- NIBBLETYPE GetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE * a_Array) const;
- NIBBLETYPE GetNibble (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE * a_Array) const;
-
- // Crop helpers:
- void CropBlockTypes(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ);
- void CropNibbles (NIBBLEARRAY & a_Array, int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ);
-
- // Expand helpers:
- void ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
- void ExpandNibbles (NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
-
- /// Loads the area from a schematic file uncompressed and parsed into a NBT tree. Returns true if successful.
- bool LoadFromSchematicNBT(cParsedNBT & a_NBT);
-
- /// Sets the specified datatypes at the specified location.
- void RelSetData(
- int a_RelX, int a_RelY, int a_RelZ,
- int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
- NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
- );
- // tolua_begin
-} ;
-// tolua_end
-
-
-
-
+
+// BlockArea.h
+
+// Interfaces to the cBlockArea object representing an area of block data that can be queried from cWorld and then accessed again without further queries
+// The object also supports writing the blockdata back into cWorld, even into other coords
+
+// NOTE: All Nibble values (meta, blocklight, skylight) are stored one-nibble-per-byte for faster access / editting!
+
+
+
+
+
+#pragma once
+
+
+
+
+
+// fwd: World.h
+class cWorld;
+
+// fwd: FastNBT.h
+class cParsedNBT;
+
+
+
+
+
+// tolua_begin
+class cBlockArea
+{
+ // tolua_end
+ DISALLOW_COPY_AND_ASSIGN(cBlockArea);
+ // tolua_begin
+
+public:
+
+ /// What data is to be queried (bit-mask)
+ enum
+ {
+ baTypes = 1,
+ baMetas = 2,
+ baLight = 4,
+ baSkyLight = 8,
+ } ;
+
+ enum eMergeStrategy
+ {
+ msOverwrite,
+ msFillAir,
+ msImprint,
+ msLake,
+ } ;
+
+ cBlockArea(void);
+ ~cBlockArea();
+
+ /// Clears the data stored to reclaim memory
+ void Clear(void);
+
+ /** Creates a new area of the specified size and contents.
+ Origin is set to all zeroes.
+ BlockTypes are set to air, block metas to zero, blocklights to zero and skylights to full light.
+ */
+ void Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes = baTypes | baMetas);
+
+ /// Resets the origin. No other changes are made, contents are untouched.
+ void SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ);
+
+ /// Reads an area of blocks specified. Returns true if successful. All coords are inclusive.
+ bool Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes = baTypes | baMetas);
+
+ // TODO: Write() is not too good an interface: if it fails, there's no way to repeat only for the parts that didn't write
+ // A better way may be to return a list of cBlockAreas for each part that didn't succeed writing, so that the caller may try again
+
+ /// Writes the area back into cWorld at the coords specified. Returns true if successful in all chunks, false if only partially / not at all
+ bool Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes = baTypes | baMetas);
+
+ /// Copies this object's contents into the specified BlockArea.
+ void CopyTo(cBlockArea & a_Into) const;
+
+ /// Copies the contents from the specified BlockArea into this object.
+ void CopyFrom(const cBlockArea & a_From);
+
+ /// For testing purposes only, dumps the area into a file.
+ void DumpToRawFile(const AString & a_FileName);
+
+ /// Loads an area from a .schematic file. Returns true if successful
+ bool LoadFromSchematicFile(const AString & a_FileName);
+
+ /// Saves the area into a .schematic file. Returns true if successful
+ bool SaveToSchematicFile(const AString & a_FileName);
+
+ /// Crops the internal contents by the specified amount of blocks from each border.
+ void Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ);
+
+ /// Expands the internal contents by the specified amount of blocks from each border
+ void Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
+
+ /** Merges another block area into this one, using the specified block combinating strategy
+ This function combines another BlockArea into the current object.
+ The strategy parameter specifies how individual blocks are combined together, using the table below.
+
+ | area block | result |
+ | this | Src | msOverwrite | msFillAir | msImprint |
+ +------+-----+-------------+-----------+-----------+
+ | air | air | air | air | air |
+ | A | air | air | A | A |
+ | air | B | B | B | B |
+ | A | B | B | A | B |
+
+ So to sum up:
+ - msOverwrite completely overwrites all blocks with the Src's blocks
+ - msFillAir overwrites only those blocks that were air
+ - msImprint overwrites with only those blocks that are non-air
+
+ Special strategies:
+ msLake (evaluate top-down, first match wins):
+ | area block | |
+ | this | Src | result |
+ +----------+--------+--------+
+ | A | sponge | A | Sponge is the NOP block
+ | * | air | air | Air always gets hollowed out, even under the oceans
+ | water | * | water | Water is never overwritten
+ | lava | * | lava | Lava is never overwritten
+ | * | water | water | Water always overwrites anything
+ | * | lava | lava | Lava always overwrites anything
+ | dirt | stone | stone | Stone overwrites dirt
+ | grass | stone | stone | ... and grass
+ | mycelium | stone | stone | ... and mycelium
+ | A | stone | A | ... but nothing else
+ | A | * | A | Everything else is left as it is
+
+ */
+ void Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy);
+
+ /// Fills the entire block area with the specified data
+ void Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0, NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f);
+
+ /// Fills a cuboid inside the block area with the specified data
+ void FillRelCuboid(int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ,
+ int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0,
+ NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f
+ );
+
+ /// Draws a line from between two points with the specified data
+ void RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int a_RelY2, int a_RelZ2,
+ int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0,
+ NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f
+ );
+
+ /// Rotates the entire area counter-clockwise around the Y axis
+ void RotateCCW(void);
+
+ /// Rotates the entire area clockwise around the Y axis
+ void RotateCW(void);
+
+ /// Mirrors the entire area around the XY plane
+ void MirrorXY(void);
+
+ /// Mirrors the entire area around the XZ plane
+ void MirrorXZ(void);
+
+ /// Mirrors the entire area around the YZ plane
+ void MirrorYZ(void);
+
+ /// Rotates the entire area counter-clockwise around the Y axis, doesn't use blockhandlers for block meta
+ void RotateCCWNoMeta(void);
+
+ /// Rotates the entire area clockwise around the Y axis, doesn't use blockhandlers for block meta
+ void RotateCWNoMeta(void);
+
+ /// Mirrors the entire area around the XY plane, doesn't use blockhandlers for block meta
+ void MirrorXYNoMeta(void);
+
+ /// Mirrors the entire area around the XZ plane, doesn't use blockhandlers for block meta
+ void MirrorXZNoMeta(void);
+
+ /// Mirrors the entire area around the YZ plane, doesn't use blockhandlers for block meta
+ void MirrorYZNoMeta(void);
+
+ // Setters:
+ void SetRelBlockType (int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType);
+ void SetBlockType (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType);
+ void SetRelBlockMeta (int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta);
+ void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta);
+ void SetRelBlockLight (int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockLight);
+ void SetBlockLight (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockLight);
+ void SetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockSkyLight);
+ void SetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockSkyLight);
+
+ // Getters:
+ BLOCKTYPE GetRelBlockType (int a_RelX, int a_RelY, int a_RelZ) const;
+ BLOCKTYPE GetBlockType (int a_BlockX, int a_BlockY, int a_BlockZ) const;
+ NIBBLETYPE GetRelBlockMeta (int a_RelX, int a_RelY, int a_RelZ) const;
+ NIBBLETYPE GetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ) const;
+ NIBBLETYPE GetRelBlockLight (int a_RelX, int a_RelY, int a_RelZ) const;
+ NIBBLETYPE GetBlockLight (int a_BlockX, int a_BlockY, int a_BlockZ) const;
+ NIBBLETYPE GetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ) const;
+ NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ) const;
+
+ void SetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+ void SetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+ void GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const;
+ void GetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const;
+
+ int GetSizeX(void) const { return m_SizeX; }
+ int GetSizeY(void) const { return m_SizeY; }
+ int GetSizeZ(void) const { return m_SizeZ; }
+
+ int GetOriginX(void) const { return m_OriginX; }
+ int GetOriginY(void) const { return m_OriginY; }
+ int GetOriginZ(void) const { return m_OriginZ; }
+
+ /// Returns the datatypes that are stored in the object (bitmask of baXXX values)
+ int GetDataTypes(void) const;
+
+ bool HasBlockTypes (void) const { return (m_BlockTypes != NULL); }
+ bool HasBlockMetas (void) const { return (m_BlockMetas != NULL); }
+ bool HasBlockLights (void) const { return (m_BlockLight != NULL); }
+ bool HasBlockSkyLights(void) const { return (m_BlockSkyLight != NULL); }
+
+ // tolua_end
+
+ // Clients can use these for faster access to all blocktypes. Be careful though!
+ /// Returns the internal pointer to the block types
+ BLOCKTYPE * GetBlockTypes (void) const { return m_BlockTypes; }
+ NIBBLETYPE * GetBlockMetas (void) const { return m_BlockMetas; } // NOTE: one byte per block!
+ NIBBLETYPE * GetBlockLight (void) const { return m_BlockLight; } // NOTE: one byte per block!
+ NIBBLETYPE * GetBlockSkyLight(void) const { return m_BlockSkyLight; } // NOTE: one byte per block!
+ int GetBlockCount(void) const { return m_SizeX * m_SizeY * m_SizeZ; }
+ int MakeIndex(int a_RelX, int a_RelY, int a_RelZ) const;
+
+protected:
+ friend class cChunkDesc;
+
+ class cChunkReader :
+ public cChunkDataCallback
+ {
+ public:
+ cChunkReader(cBlockArea & a_Area);
+
+ protected:
+ cBlockArea & m_Area;
+ int m_OriginX;
+ int m_OriginY;
+ int m_OriginZ;
+ int m_CurrentChunkX;
+ int m_CurrentChunkZ;
+
+ void CopyNibbles(NIBBLETYPE * a_AreaDst, const NIBBLETYPE * a_ChunkSrc);
+
+ // cChunkDataCallback overrides:
+ virtual bool Coords (int a_ChunkX, int a_ChunkZ) override;
+ virtual void BlockTypes (const BLOCKTYPE * a_BlockTypes) override;
+ virtual void BlockMeta (const NIBBLETYPE * a_BlockMetas) override;
+ virtual void BlockLight (const NIBBLETYPE * a_BlockLight) override;
+ virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override;
+ } ;
+
+ typedef NIBBLETYPE * NIBBLEARRAY;
+
+
+ int m_OriginX;
+ int m_OriginY;
+ int m_OriginZ;
+ int m_SizeX;
+ int m_SizeY;
+ int m_SizeZ;
+
+ BLOCKTYPE * m_BlockTypes;
+ NIBBLETYPE * m_BlockMetas; // Each meta is stored as a separate byte for faster access
+ NIBBLETYPE * m_BlockLight; // Each light value is stored as a separate byte for faster access
+ NIBBLETYPE * m_BlockSkyLight; // Each light value is stored as a separate byte for faster access
+
+ /// Clears the data stored and prepares a fresh new block area with the specified dimensions
+ bool SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes);
+
+ // Basic Setters:
+ void SetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array);
+ void SetNibble (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array);
+
+ // Basic Getters:
+ NIBBLETYPE GetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE * a_Array) const;
+ NIBBLETYPE GetNibble (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE * a_Array) const;
+
+ // Crop helpers:
+ void CropBlockTypes(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ);
+ void CropNibbles (NIBBLEARRAY & a_Array, int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ);
+
+ // Expand helpers:
+ void ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
+ void ExpandNibbles (NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
+
+ /// Loads the area from a schematic file uncompressed and parsed into a NBT tree. Returns true if successful.
+ bool LoadFromSchematicNBT(cParsedNBT & a_NBT);
+
+ /// Sets the specified datatypes at the specified location.
+ void RelSetData(
+ int a_RelX, int a_RelY, int a_RelZ,
+ int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
+ NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
+ );
+ // tolua_begin
+} ;
+// tolua_end
+
+
+
+
diff --git a/source/BlockEntities/BlockEntityWithItems.h b/source/BlockEntities/BlockEntityWithItems.h
index 587b10461..0846ae17e 100644
--- a/source/BlockEntities/BlockEntityWithItems.h
+++ b/source/BlockEntities/BlockEntityWithItems.h
@@ -1,86 +1,86 @@
-
-// BlockEntityWithItems.h
-
-// Declares the cBlockEntityWithItems class representing a common ancestor for all block entities that have an ItemGrid
-
-
-
-
-
-#pragma once
-
-#include "BlockEntity.h"
-#include "../ItemGrid.h"
-
-
-
-
-
-// tolua_begin
-class cBlockEntityWithItems :
- public cBlockEntity
- // tolua_end
- // tolua doesn't seem to support multiple inheritance?
- , public cItemGrid::cListener
- // tolua_begin
-{
- typedef cBlockEntity super;
-
-public:
- // tolua_end
-
- cBlockEntityWithItems(
- BLOCKTYPE a_BlockType, // Type of the block that the entity represents
- int a_BlockX, int a_BlockY, int a_BlockZ, // Position of the block entity
- int a_ItemGridWidth, int a_ItemGridHeight, // Dimensions of the ItemGrid
- cWorld * a_World // Optional world to assign to the entity
- ) :
- super(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World),
- m_Contents(a_ItemGridWidth, a_ItemGridHeight)
- {
- m_Contents.AddListener(*this);
- }
-
- virtual void Destroy(void) override
- {
- // Drop the contents as pickups:
- ASSERT(m_World != NULL);
- cItems Pickups;
- m_Contents.CopyToItems(Pickups);
- m_Contents.Clear();
- m_World->SpawnItemPickups(Pickups, m_PosX, m_PosY, m_PosZ);
- }
-
- // tolua_begin
-
- const cItem & GetSlot(int a_SlotNum) const { return m_Contents.GetSlot(a_SlotNum); }
- const cItem & GetSlot(int a_X, int a_Y) const { return m_Contents.GetSlot(a_X, a_Y); }
-
- void SetSlot(int a_SlotNum, const cItem & a_Item) { m_Contents.SetSlot(a_SlotNum, a_Item); }
- void SetSlot(int a_X, int a_Y, const cItem & a_Item) { m_Contents.SetSlot(a_X, a_Y, a_Item); }
-
- /// Returns the ItemGrid used for storing the contents
- cItemGrid & GetContents(void) { return m_Contents; }
-
- // tolua_end
-
- /// Const version of the GetContents() function for C++ type-safety
- const cItemGrid & GetContents(void) const { return m_Contents; }
-
-protected:
- cItemGrid m_Contents;
-
- // cItemGrid::cListener overrides:
- virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum)
- {
- ASSERT(a_Grid == &m_Contents);
- if (m_World != NULL)
- {
- m_World->MarkChunkDirty(GetChunkX(), GetChunkZ());
- }
- }
-} ; // tolua_export
-
-
-
-
+
+// BlockEntityWithItems.h
+
+// Declares the cBlockEntityWithItems class representing a common ancestor for all block entities that have an ItemGrid
+
+
+
+
+
+#pragma once
+
+#include "BlockEntity.h"
+#include "../ItemGrid.h"
+
+
+
+
+
+// tolua_begin
+class cBlockEntityWithItems :
+ public cBlockEntity
+ // tolua_end
+ // tolua doesn't seem to support multiple inheritance?
+ , public cItemGrid::cListener
+ // tolua_begin
+{
+ typedef cBlockEntity super;
+
+public:
+ // tolua_end
+
+ cBlockEntityWithItems(
+ BLOCKTYPE a_BlockType, // Type of the block that the entity represents
+ int a_BlockX, int a_BlockY, int a_BlockZ, // Position of the block entity
+ int a_ItemGridWidth, int a_ItemGridHeight, // Dimensions of the ItemGrid
+ cWorld * a_World // Optional world to assign to the entity
+ ) :
+ super(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World),
+ m_Contents(a_ItemGridWidth, a_ItemGridHeight)
+ {
+ m_Contents.AddListener(*this);
+ }
+
+ virtual void Destroy(void) override
+ {
+ // Drop the contents as pickups:
+ ASSERT(m_World != NULL);
+ cItems Pickups;
+ m_Contents.CopyToItems(Pickups);
+ m_Contents.Clear();
+ m_World->SpawnItemPickups(Pickups, m_PosX, m_PosY, m_PosZ);
+ }
+
+ // tolua_begin
+
+ const cItem & GetSlot(int a_SlotNum) const { return m_Contents.GetSlot(a_SlotNum); }
+ const cItem & GetSlot(int a_X, int a_Y) const { return m_Contents.GetSlot(a_X, a_Y); }
+
+ void SetSlot(int a_SlotNum, const cItem & a_Item) { m_Contents.SetSlot(a_SlotNum, a_Item); }
+ void SetSlot(int a_X, int a_Y, const cItem & a_Item) { m_Contents.SetSlot(a_X, a_Y, a_Item); }
+
+ /// Returns the ItemGrid used for storing the contents
+ cItemGrid & GetContents(void) { return m_Contents; }
+
+ // tolua_end
+
+ /// Const version of the GetContents() function for C++ type-safety
+ const cItemGrid & GetContents(void) const { return m_Contents; }
+
+protected:
+ cItemGrid m_Contents;
+
+ // cItemGrid::cListener overrides:
+ virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum)
+ {
+ ASSERT(a_Grid == &m_Contents);
+ if (m_World != NULL)
+ {
+ m_World->MarkChunkDirty(GetChunkX(), GetChunkZ());
+ }
+ }
+} ; // tolua_export
+
+
+
+
diff --git a/source/BlockEntities/DispenserEntity.cpp b/source/BlockEntities/DispenserEntity.cpp
index ab4c8e519..cde67bb41 100644
--- a/source/BlockEntities/DispenserEntity.cpp
+++ b/source/BlockEntities/DispenserEntity.cpp
@@ -1,225 +1,225 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "DispenserEntity.h"
-#include "../Player.h"
-#include "../Simulator/FluidSimulator.h"
-#include "../Chunk.h"
-
-
-
-
-
-cDispenserEntity::cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ) :
- super(E_BLOCK_DISPENSER, a_BlockX, a_BlockY, a_BlockZ, NULL)
-{
- SetBlockEntity(this); // cBlockEntityWindowOwner
-}
-
-
-
-
-
-cDispenserEntity::cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
- super(E_BLOCK_DISPENSER, a_BlockX, a_BlockY, a_BlockZ, a_World)
-{
- SetBlockEntity(this); // cBlockEntityWindowOwner
-}
-
-
-
-
-
-void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
-{
- int DispX = m_RelX;
- int DispY = m_PosY;
- int DispZ = m_RelZ;
- NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
- AddDropSpenserDir(DispX, DispY, DispZ, Meta);
- cChunk * DispChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(DispX, DispZ);
- if (DispChunk == NULL)
- {
- // Would dispense into / interact with a non-loaded chunk, ignore the tick
- return;
- }
- BLOCKTYPE DispBlock = DispChunk->GetBlock(DispX, DispY, DispZ);
-
- // Dispense the item:
- switch (m_Contents.GetSlot(a_SlotNum).m_ItemType)
- {
- case E_ITEM_BUCKET:
- {
- LOGD("Dispensing empty bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock);
- switch (DispBlock)
- {
- case E_BLOCK_STATIONARY_WATER:
- case E_BLOCK_WATER:
- {
- if (ScoopUpLiquid(a_SlotNum, E_ITEM_WATER_BUCKET))
- {
- DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_AIR, 0);
- }
- break;
- }
- case E_BLOCK_STATIONARY_LAVA:
- case E_BLOCK_LAVA:
- {
- if (ScoopUpLiquid(a_SlotNum, E_ITEM_LAVA_BUCKET))
- {
- DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_AIR, 0);
- }
- break;
- }
- default:
- {
- DropFromSlot(a_Chunk, a_SlotNum);
- break;
- }
- }
- break;
- } // E_ITEM_BUCKET
-
- case E_ITEM_WATER_BUCKET:
- {
- LOGD("Dispensing water bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock);
- if (EmptyLiquidBucket(DispBlock, a_SlotNum))
- {
- DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_WATER, 0);
- }
- else
- {
- DropFromSlot(a_Chunk, a_SlotNum);
- }
- break;
- }
-
- case E_ITEM_LAVA_BUCKET:
- {
- LOGD("Dispensing lava bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock);
- if (EmptyLiquidBucket(DispBlock, a_SlotNum))
- {
- DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_LAVA, 0);
- }
- else
- {
- DropFromSlot(a_Chunk, a_SlotNum);
- }
- break;
- }
-
- case E_ITEM_SPAWN_EGG:
- {
- double MobX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width);
- double MobZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width);
- if (m_World->SpawnMob(MobX, DispY, MobZ, m_Contents.GetSlot(a_SlotNum).m_ItemDamage) >= 0)
- {
- m_Contents.ChangeSlotCount(a_SlotNum, -1);
- }
- break;
- }
-
- case E_BLOCK_TNT:
- {
- // Spawn a primed TNT entity, if space allows:
- if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR)
- {
- double TNTX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width);
- double TNTZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width);
- m_World->SpawnPrimedTNT(TNTX, DispY + 0.5, TNTZ, 4, 0); // 4 seconds fuse, no initial velocity
- m_Contents.ChangeSlotCount(a_SlotNum, -1);
- }
- break;
- }
-
- case E_ITEM_FLINT_AND_STEEL:
- {
- // Spawn fire if the block in front is air.
- if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR)
- {
- DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_FIRE, 0);
- m_Contents.SetSlot(a_SlotNum, m_Contents.GetSlot(a_SlotNum).m_ItemType, m_Contents.GetSlot(a_SlotNum).m_ItemCount, m_Contents.GetSlot(a_SlotNum).m_ItemDamage + 1);
- // If the durability has run out destroy the item.
- if (m_Contents.GetSlot(a_SlotNum).m_ItemDamage > 64)
- {
- m_Contents.ChangeSlotCount(a_SlotNum, -1);
- }
- }
- break;
- }
-
- default:
- {
- DropFromSlot(a_Chunk, a_SlotNum);
- break;
- }
- } // switch (ItemType)
-}
-
-
-
-
-
-
-bool cDispenserEntity::ScoopUpLiquid(int a_SlotNum, short a_BucketItemType)
-{
- cItem LiquidBucket(a_BucketItemType, 1);
- if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1)
- {
- // Special case: replacing one empty bucket with one full bucket
- m_Contents.SetSlot(a_SlotNum, LiquidBucket);
- return true;
- }
-
- // There are stacked buckets at the selected slot, see if a full bucket will fit somewhere else
- if (m_Contents.HowManyCanFit(LiquidBucket) < 1)
- {
- // Cannot fit into m_Contents
- return false;
- }
-
- m_Contents.ChangeSlotCount(a_SlotNum, -1);
- m_Contents.AddItem(LiquidBucket);
- return true;
-}
-
-
-
-
-
-bool cDispenserEntity::EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum)
-{
- if (
- (a_BlockInFront != E_BLOCK_AIR) &&
- !IsBlockLiquid(a_BlockInFront) &&
- !cFluidSimulator::CanWashAway(a_BlockInFront)
- )
- {
- // Not a suitable block in front
- return false;
- }
-
- cItem EmptyBucket(E_ITEM_BUCKET, 1);
- if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1)
- {
- // Change the single full bucket present into a single empty bucket
- m_Contents.SetSlot(a_SlotNum, EmptyBucket);
- return true;
- }
-
- // There are full buckets stacked at this slot, check if we can fit in the empty bucket
- if (m_Contents.HowManyCanFit(EmptyBucket) < 1)
- {
- // The empty bucket wouldn't fit into m_Contents
- return false;
- }
-
- // The empty bucket fits in, remove one full bucket and add the empty one
- m_Contents.ChangeSlotCount(a_SlotNum, -1);
- m_Contents.AddItem(EmptyBucket);
- return true;
-}
-
-
-
-
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "DispenserEntity.h"
+#include "../Player.h"
+#include "../Simulator/FluidSimulator.h"
+#include "../Chunk.h"
+
+
+
+
+
+cDispenserEntity::cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ) :
+ super(E_BLOCK_DISPENSER, a_BlockX, a_BlockY, a_BlockZ, NULL)
+{
+ SetBlockEntity(this); // cBlockEntityWindowOwner
+}
+
+
+
+
+
+cDispenserEntity::cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
+ super(E_BLOCK_DISPENSER, a_BlockX, a_BlockY, a_BlockZ, a_World)
+{
+ SetBlockEntity(this); // cBlockEntityWindowOwner
+}
+
+
+
+
+
+void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
+{
+ int DispX = m_RelX;
+ int DispY = m_PosY;
+ int DispZ = m_RelZ;
+ NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
+ AddDropSpenserDir(DispX, DispY, DispZ, Meta);
+ cChunk * DispChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(DispX, DispZ);
+ if (DispChunk == NULL)
+ {
+ // Would dispense into / interact with a non-loaded chunk, ignore the tick
+ return;
+ }
+ BLOCKTYPE DispBlock = DispChunk->GetBlock(DispX, DispY, DispZ);
+
+ // Dispense the item:
+ switch (m_Contents.GetSlot(a_SlotNum).m_ItemType)
+ {
+ case E_ITEM_BUCKET:
+ {
+ LOGD("Dispensing empty bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock);
+ switch (DispBlock)
+ {
+ case E_BLOCK_STATIONARY_WATER:
+ case E_BLOCK_WATER:
+ {
+ if (ScoopUpLiquid(a_SlotNum, E_ITEM_WATER_BUCKET))
+ {
+ DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_AIR, 0);
+ }
+ break;
+ }
+ case E_BLOCK_STATIONARY_LAVA:
+ case E_BLOCK_LAVA:
+ {
+ if (ScoopUpLiquid(a_SlotNum, E_ITEM_LAVA_BUCKET))
+ {
+ DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_AIR, 0);
+ }
+ break;
+ }
+ default:
+ {
+ DropFromSlot(a_Chunk, a_SlotNum);
+ break;
+ }
+ }
+ break;
+ } // E_ITEM_BUCKET
+
+ case E_ITEM_WATER_BUCKET:
+ {
+ LOGD("Dispensing water bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock);
+ if (EmptyLiquidBucket(DispBlock, a_SlotNum))
+ {
+ DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_WATER, 0);
+ }
+ else
+ {
+ DropFromSlot(a_Chunk, a_SlotNum);
+ }
+ break;
+ }
+
+ case E_ITEM_LAVA_BUCKET:
+ {
+ LOGD("Dispensing lava bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock);
+ if (EmptyLiquidBucket(DispBlock, a_SlotNum))
+ {
+ DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_LAVA, 0);
+ }
+ else
+ {
+ DropFromSlot(a_Chunk, a_SlotNum);
+ }
+ break;
+ }
+
+ case E_ITEM_SPAWN_EGG:
+ {
+ double MobX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width);
+ double MobZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width);
+ if (m_World->SpawnMob(MobX, DispY, MobZ, m_Contents.GetSlot(a_SlotNum).m_ItemDamage) >= 0)
+ {
+ m_Contents.ChangeSlotCount(a_SlotNum, -1);
+ }
+ break;
+ }
+
+ case E_BLOCK_TNT:
+ {
+ // Spawn a primed TNT entity, if space allows:
+ if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR)
+ {
+ double TNTX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width);
+ double TNTZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width);
+ m_World->SpawnPrimedTNT(TNTX, DispY + 0.5, TNTZ, 4, 0); // 4 seconds fuse, no initial velocity
+ m_Contents.ChangeSlotCount(a_SlotNum, -1);
+ }
+ break;
+ }
+
+ case E_ITEM_FLINT_AND_STEEL:
+ {
+ // Spawn fire if the block in front is air.
+ if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR)
+ {
+ DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_FIRE, 0);
+ m_Contents.SetSlot(a_SlotNum, m_Contents.GetSlot(a_SlotNum).m_ItemType, m_Contents.GetSlot(a_SlotNum).m_ItemCount, m_Contents.GetSlot(a_SlotNum).m_ItemDamage + 1);
+ // If the durability has run out destroy the item.
+ if (m_Contents.GetSlot(a_SlotNum).m_ItemDamage > 64)
+ {
+ m_Contents.ChangeSlotCount(a_SlotNum, -1);
+ }
+ }
+ break;
+ }
+
+ default:
+ {
+ DropFromSlot(a_Chunk, a_SlotNum);
+ break;
+ }
+ } // switch (ItemType)
+}
+
+
+
+
+
+
+bool cDispenserEntity::ScoopUpLiquid(int a_SlotNum, short a_BucketItemType)
+{
+ cItem LiquidBucket(a_BucketItemType, 1);
+ if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1)
+ {
+ // Special case: replacing one empty bucket with one full bucket
+ m_Contents.SetSlot(a_SlotNum, LiquidBucket);
+ return true;
+ }
+
+ // There are stacked buckets at the selected slot, see if a full bucket will fit somewhere else
+ if (m_Contents.HowManyCanFit(LiquidBucket) < 1)
+ {
+ // Cannot fit into m_Contents
+ return false;
+ }
+
+ m_Contents.ChangeSlotCount(a_SlotNum, -1);
+ m_Contents.AddItem(LiquidBucket);
+ return true;
+}
+
+
+
+
+
+bool cDispenserEntity::EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum)
+{
+ if (
+ (a_BlockInFront != E_BLOCK_AIR) &&
+ !IsBlockLiquid(a_BlockInFront) &&
+ !cFluidSimulator::CanWashAway(a_BlockInFront)
+ )
+ {
+ // Not a suitable block in front
+ return false;
+ }
+
+ cItem EmptyBucket(E_ITEM_BUCKET, 1);
+ if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1)
+ {
+ // Change the single full bucket present into a single empty bucket
+ m_Contents.SetSlot(a_SlotNum, EmptyBucket);
+ return true;
+ }
+
+ // There are full buckets stacked at this slot, check if we can fit in the empty bucket
+ if (m_Contents.HowManyCanFit(EmptyBucket) < 1)
+ {
+ // The empty bucket wouldn't fit into m_Contents
+ return false;
+ }
+
+ // The empty bucket fits in, remove one full bucket and add the empty one
+ m_Contents.ChangeSlotCount(a_SlotNum, -1);
+ m_Contents.AddItem(EmptyBucket);
+ return true;
+}
+
+
+
+
diff --git a/source/BlockEntities/DispenserEntity.h b/source/BlockEntities/DispenserEntity.h
index 09270e7f8..5e3327f18 100644
--- a/source/BlockEntities/DispenserEntity.h
+++ b/source/BlockEntities/DispenserEntity.h
@@ -1,41 +1,41 @@
-
-#pragma once
-
-#include "DropSpenserEntity.h"
-
-
-
-
-
-// tolua_begin
-class cDispenserEntity :
- public cDropSpenserEntity
-{
- typedef cDropSpenserEntity super;
-
-public:
-
- /// Constructor used while generating a chunk; sets m_World to NULL
- cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ);
-
- // tolua_end
-
- /// Constructor used for normal operation
- cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
-
- static const char * GetClassStatic(void) { return "cDispenserEntity"; }
-
-private:
- // cDropSpenser overrides:
- virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) override;
-
- /// If such a bucket can fit, adds it to m_Contents and returns true
- bool ScoopUpLiquid(int a_SlotNum, short a_BucketItemType);
-
- /// If the a_BlockInFront is liquidable and the empty bucket can fit, does the m_Contents processing and returns true
- bool EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum);
-} ; // tolua_export
-
-
-
-
+
+#pragma once
+
+#include "DropSpenserEntity.h"
+
+
+
+
+
+// tolua_begin
+class cDispenserEntity :
+ public cDropSpenserEntity
+{
+ typedef cDropSpenserEntity super;
+
+public:
+
+ /// Constructor used while generating a chunk; sets m_World to NULL
+ cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ // tolua_end
+
+ /// Constructor used for normal operation
+ cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
+
+ static const char * GetClassStatic(void) { return "cDispenserEntity"; }
+
+private:
+ // cDropSpenser overrides:
+ virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) override;
+
+ /// If such a bucket can fit, adds it to m_Contents and returns true
+ bool ScoopUpLiquid(int a_SlotNum, short a_BucketItemType);
+
+ /// If the a_BlockInFront is liquidable and the empty bucket can fit, does the m_Contents processing and returns true
+ bool EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum);
+} ; // tolua_export
+
+
+
+
diff --git a/source/BlockEntities/DropSpenserEntity.cpp b/source/BlockEntities/DropSpenserEntity.cpp
index a9860d812..eb8257a21 100644
--- a/source/BlockEntities/DropSpenserEntity.cpp
+++ b/source/BlockEntities/DropSpenserEntity.cpp
@@ -1,245 +1,245 @@
-
-// DropSpenserEntity.cpp
-
-// Declares the cDropSpenserEntity class representing a common ancestor to the cDispenserEntity and cDropperEntity
-// The dropper and dispenser only needs to override the DropSpenseFromSlot() function to provide the specific item behavior
-
-#include "Globals.h"
-#include "DropSpenserEntity.h"
-#include "../Player.h"
-#include "../Chunk.h"
-
-
-
-
-
-cDropSpenserEntity::cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
- super(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World),
- m_ShouldDropSpense(false),
- m_IsPowered(false)
-{
- SetBlockEntity(this); // cBlockEntityWindowOwner
-}
-
-
-
-
-
-cDropSpenserEntity::~cDropSpenserEntity()
-{
- // Tell window its owner is destroyed
- cWindow * Window = GetWindow();
- if (Window != NULL)
- {
- Window->OwnerDestroyed();
- }
-}
-
-
-
-
-
-void cDropSpenserEntity::AddDropSpenserDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_Direction)
-{
- switch (a_Direction)
- {
- case E_META_DROPSPENSER_FACING_YM: a_BlockY--; return;
- case E_META_DROPSPENSER_FACING_YP: a_BlockY++; return;
- case E_META_DROPSPENSER_FACING_ZM: a_BlockZ--; return;
- case E_META_DROPSPENSER_FACING_ZP: a_BlockZ++; return;
- case E_META_DROPSPENSER_FACING_XM: a_BlockX--; return;
- case E_META_DROPSPENSER_FACING_XP: a_BlockX++; return;
- }
- LOGWARNING("%s: Unhandled direction: %d", __FUNCTION__, a_Direction);
- return;
-}
-
-
-
-
-
-void cDropSpenserEntity::DropSpense(cChunk & a_Chunk)
-{
- // Pick one of the occupied slots:
- int OccupiedSlots[9];
- int SlotsCnt = 0;
- for (int i = m_Contents.GetNumSlots() - 1; i >= 0; i--)
- {
- if (!m_Contents.GetSlot(i).IsEmpty())
- {
- OccupiedSlots[SlotsCnt] = i;
- SlotsCnt++;
- }
- } // for i - m_Contents[]
-
- if (SlotsCnt == 0)
- {
- // Nothing in the dropspenser, play the click sound
- m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.2f);
- return;
- }
-
- int RandomSlot = m_World->GetTickRandomNumber(SlotsCnt - 1);
-
- // DropSpense the item, using the specialized behavior in the subclasses:
- DropSpenseFromSlot(a_Chunk, OccupiedSlots[RandomSlot]);
-
- // Broadcast a smoke and click effects:
- NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
- int SmokeDir = 0;
- switch (Meta)
- {
- case E_META_DROPSPENSER_FACING_XM: SmokeDir = 3; break;
- case E_META_DROPSPENSER_FACING_XP: SmokeDir = 5; break;
- case E_META_DROPSPENSER_FACING_ZM: SmokeDir = 1; break;
- case E_META_DROPSPENSER_FACING_ZP: SmokeDir = 7; break;
- }
- m_World->BroadcastSoundParticleEffect(2000, m_PosX * 8, m_PosY * 8, m_PosZ * 8, SmokeDir);
- m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.0f);
-
- // Update the UI window, if open:
- cWindow * Window = GetWindow();
- if (Window != NULL)
- {
- Window->BroadcastWholeWindow();
- }
-}
-
-
-
-
-
-void cDropSpenserEntity::Activate(void)
-{
- m_ShouldDropSpense = true;
-}
-
-
-
-
-
-void cDropSpenserEntity::SetRedstonePower(bool a_IsPowered)
-{
- if (a_IsPowered && !m_IsPowered)
- {
- Activate();
- }
- m_IsPowered = a_IsPowered;
-}
-
-
-
-
-
-bool cDropSpenserEntity::Tick(float a_Dt, cChunk & a_Chunk)
-{
- if (!m_ShouldDropSpense)
- {
- return false;
- }
-
- m_ShouldDropSpense = false;
- DropSpense(a_Chunk);
- return true;
-}
-
-
-
-
-
-bool cDropSpenserEntity::LoadFromJson(const Json::Value & a_Value)
-{
- m_PosX = a_Value.get("x", 0).asInt();
- m_PosY = a_Value.get("y", 0).asInt();
- m_PosZ = a_Value.get("z", 0).asInt();
-
- Json::Value AllSlots = a_Value.get("Slots", 0);
- int SlotIdx = 0;
- for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr)
- {
- cItem Contents;
- Contents.FromJson(*itr);
- m_Contents.SetSlot(SlotIdx, Contents);
- SlotIdx++;
- if (SlotIdx >= m_Contents.GetNumSlots())
- {
- return true;
- }
- }
-
- return true;
-}
-
-
-
-
-
-void cDropSpenserEntity::SaveToJson(Json::Value & a_Value)
-{
- a_Value["x"] = m_PosX;
- a_Value["y"] = m_PosY;
- a_Value["z"] = m_PosZ;
-
- Json::Value AllSlots;
- int NumSlots = m_Contents.GetNumSlots();
- for (int i = 0; i < NumSlots; i++)
- {
- Json::Value Slot;
- m_Contents.GetSlot(i).GetJson(Slot);
- AllSlots.append(Slot);
- }
- a_Value["Slots"] = AllSlots;
-}
-
-
-
-
-
-void cDropSpenserEntity::SendTo(cClientHandle & a_Client)
-{
- // Nothing needs to be sent
- UNUSED(a_Client);
-}
-
-
-
-
-
-void cDropSpenserEntity::UsedBy(cPlayer * a_Player)
-{
- cWindow * Window = GetWindow();
- if (Window == NULL)
- {
- OpenWindow(new cDropSpenserWindow(m_PosX, m_PosY, m_PosZ, this));
- Window = GetWindow();
- }
-
- if (Window != NULL)
- {
- if (a_Player->GetWindow() != Window)
- {
- a_Player->OpenWindow(Window);
- }
- }
-}
-
-
-
-
-
-void cDropSpenserEntity::DropFromSlot(cChunk & a_Chunk, int a_SlotNum)
-{
- int DispX = m_PosX;
- int DispY = m_PosY;
- int DispZ = m_PosZ;
- NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
- AddDropSpenserDir(DispX, DispY, DispZ, Meta);
-
- cItems Pickups;
- Pickups.push_back(m_Contents.RemoveOneItem(a_SlotNum));
- m_World->SpawnItemPickups(Pickups, DispX, DispY, DispZ);
-}
-
-
-
-
+
+// DropSpenserEntity.cpp
+
+// Declares the cDropSpenserEntity class representing a common ancestor to the cDispenserEntity and cDropperEntity
+// The dropper and dispenser only needs to override the DropSpenseFromSlot() function to provide the specific item behavior
+
+#include "Globals.h"
+#include "DropSpenserEntity.h"
+#include "../Player.h"
+#include "../Chunk.h"
+
+
+
+
+
+cDropSpenserEntity::cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
+ super(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World),
+ m_ShouldDropSpense(false),
+ m_IsPowered(false)
+{
+ SetBlockEntity(this); // cBlockEntityWindowOwner
+}
+
+
+
+
+
+cDropSpenserEntity::~cDropSpenserEntity()
+{
+ // Tell window its owner is destroyed
+ cWindow * Window = GetWindow();
+ if (Window != NULL)
+ {
+ Window->OwnerDestroyed();
+ }
+}
+
+
+
+
+
+void cDropSpenserEntity::AddDropSpenserDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_Direction)
+{
+ switch (a_Direction)
+ {
+ case E_META_DROPSPENSER_FACING_YM: a_BlockY--; return;
+ case E_META_DROPSPENSER_FACING_YP: a_BlockY++; return;
+ case E_META_DROPSPENSER_FACING_ZM: a_BlockZ--; return;
+ case E_META_DROPSPENSER_FACING_ZP: a_BlockZ++; return;
+ case E_META_DROPSPENSER_FACING_XM: a_BlockX--; return;
+ case E_META_DROPSPENSER_FACING_XP: a_BlockX++; return;
+ }
+ LOGWARNING("%s: Unhandled direction: %d", __FUNCTION__, a_Direction);
+ return;
+}
+
+
+
+
+
+void cDropSpenserEntity::DropSpense(cChunk & a_Chunk)
+{
+ // Pick one of the occupied slots:
+ int OccupiedSlots[9];
+ int SlotsCnt = 0;
+ for (int i = m_Contents.GetNumSlots() - 1; i >= 0; i--)
+ {
+ if (!m_Contents.GetSlot(i).IsEmpty())
+ {
+ OccupiedSlots[SlotsCnt] = i;
+ SlotsCnt++;
+ }
+ } // for i - m_Contents[]
+
+ if (SlotsCnt == 0)
+ {
+ // Nothing in the dropspenser, play the click sound
+ m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.2f);
+ return;
+ }
+
+ int RandomSlot = m_World->GetTickRandomNumber(SlotsCnt - 1);
+
+ // DropSpense the item, using the specialized behavior in the subclasses:
+ DropSpenseFromSlot(a_Chunk, OccupiedSlots[RandomSlot]);
+
+ // Broadcast a smoke and click effects:
+ NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
+ int SmokeDir = 0;
+ switch (Meta)
+ {
+ case E_META_DROPSPENSER_FACING_XM: SmokeDir = 3; break;
+ case E_META_DROPSPENSER_FACING_XP: SmokeDir = 5; break;
+ case E_META_DROPSPENSER_FACING_ZM: SmokeDir = 1; break;
+ case E_META_DROPSPENSER_FACING_ZP: SmokeDir = 7; break;
+ }
+ m_World->BroadcastSoundParticleEffect(2000, m_PosX * 8, m_PosY * 8, m_PosZ * 8, SmokeDir);
+ m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.0f);
+
+ // Update the UI window, if open:
+ cWindow * Window = GetWindow();
+ if (Window != NULL)
+ {
+ Window->BroadcastWholeWindow();
+ }
+}
+
+
+
+
+
+void cDropSpenserEntity::Activate(void)
+{
+ m_ShouldDropSpense = true;
+}
+
+
+
+
+
+void cDropSpenserEntity::SetRedstonePower(bool a_IsPowered)
+{
+ if (a_IsPowered && !m_IsPowered)
+ {
+ Activate();
+ }
+ m_IsPowered = a_IsPowered;
+}
+
+
+
+
+
+bool cDropSpenserEntity::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ if (!m_ShouldDropSpense)
+ {
+ return false;
+ }
+
+ m_ShouldDropSpense = false;
+ DropSpense(a_Chunk);
+ return true;
+}
+
+
+
+
+
+bool cDropSpenserEntity::LoadFromJson(const Json::Value & a_Value)
+{
+ m_PosX = a_Value.get("x", 0).asInt();
+ m_PosY = a_Value.get("y", 0).asInt();
+ m_PosZ = a_Value.get("z", 0).asInt();
+
+ Json::Value AllSlots = a_Value.get("Slots", 0);
+ int SlotIdx = 0;
+ for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr)
+ {
+ cItem Contents;
+ Contents.FromJson(*itr);
+ m_Contents.SetSlot(SlotIdx, Contents);
+ SlotIdx++;
+ if (SlotIdx >= m_Contents.GetNumSlots())
+ {
+ return true;
+ }
+ }
+
+ return true;
+}
+
+
+
+
+
+void cDropSpenserEntity::SaveToJson(Json::Value & a_Value)
+{
+ a_Value["x"] = m_PosX;
+ a_Value["y"] = m_PosY;
+ a_Value["z"] = m_PosZ;
+
+ Json::Value AllSlots;
+ int NumSlots = m_Contents.GetNumSlots();
+ for (int i = 0; i < NumSlots; i++)
+ {
+ Json::Value Slot;
+ m_Contents.GetSlot(i).GetJson(Slot);
+ AllSlots.append(Slot);
+ }
+ a_Value["Slots"] = AllSlots;
+}
+
+
+
+
+
+void cDropSpenserEntity::SendTo(cClientHandle & a_Client)
+{
+ // Nothing needs to be sent
+ UNUSED(a_Client);
+}
+
+
+
+
+
+void cDropSpenserEntity::UsedBy(cPlayer * a_Player)
+{
+ cWindow * Window = GetWindow();
+ if (Window == NULL)
+ {
+ OpenWindow(new cDropSpenserWindow(m_PosX, m_PosY, m_PosZ, this));
+ Window = GetWindow();
+ }
+
+ if (Window != NULL)
+ {
+ if (a_Player->GetWindow() != Window)
+ {
+ a_Player->OpenWindow(Window);
+ }
+ }
+}
+
+
+
+
+
+void cDropSpenserEntity::DropFromSlot(cChunk & a_Chunk, int a_SlotNum)
+{
+ int DispX = m_PosX;
+ int DispY = m_PosY;
+ int DispZ = m_PosZ;
+ NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
+ AddDropSpenserDir(DispX, DispY, DispZ, Meta);
+
+ cItems Pickups;
+ Pickups.push_back(m_Contents.RemoveOneItem(a_SlotNum));
+ m_World->SpawnItemPickups(Pickups, DispX, DispY, DispZ);
+}
+
+
+
+
diff --git a/source/BlockEntities/DropSpenserEntity.h b/source/BlockEntities/DropSpenserEntity.h
index ca9f7dfa3..f2f1eba36 100644
--- a/source/BlockEntities/DropSpenserEntity.h
+++ b/source/BlockEntities/DropSpenserEntity.h
@@ -1,89 +1,89 @@
-
-// DropSpenser.h
-
-// Declares the cDropSpenser class representing a common ancestor to the cDispenserEntity and cDropperEntity
-// The dropper and dispenser only needs to override the DropSpenseFromSlot() function to provide the specific item behavior
-
-
-
-
-
-#pragma once
-
-#include "BlockEntityWithItems.h"
-#include "../UI/WindowOwner.h"
-
-
-
-
-
-namespace Json
-{
- class Value;
-}
-
-class cClientHandle;
-class cServer;
-
-
-
-
-
-// tolua_begin
-class cDropSpenserEntity :
- public cBlockEntityWithItems,
- public cBlockEntityWindowOwner
-{
- typedef cBlockEntityWithItems super;
-
-public:
- enum {
- ContentsHeight = 3,
- ContentsWidth = 3,
- } ;
-
- // tolua_end
-
- cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
- virtual ~cDropSpenserEntity();
-
- static const char * GetClassStatic(void) { return "cDropSpenserEntity"; }
-
- bool LoadFromJson(const Json::Value & a_Value);
-
- // cBlockEntity overrides:
- virtual void SaveToJson(Json::Value & a_Value) override;
- virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
- virtual void SendTo(cClientHandle & a_Client) override;
- virtual void UsedBy(cPlayer * a_Player) override;
-
- // tolua_begin
-
- /// Modifies the block coords to match the dropspenser direction given (where the dropspensed pickups should materialize)
- void AddDropSpenserDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_Direction);
-
- /// Sets the dropspenser to dropspense an item in the next tick
- void Activate(void);
-
- /// Sets the internal redstone power flag to "on" or "off", depending on the parameter. Calls Activate() if appropriate
- void SetRedstonePower(bool a_IsPowered);
-
- // tolua_end
-
-protected:
- bool m_ShouldDropSpense; ///< If true, the dropspenser will dropspense an item in the next tick
- bool m_IsPowered; ///< Set to true when the dropspenser receives redstone power.
-
- /// Does the actual work on dropspensing an item. Chooses the slot, calls DropSpenseFromSlot() and handles smoke / sound effects
- void DropSpense(cChunk & a_Chunk);
-
- /// Override this function to provide the specific behavior for item dropspensing (drop / shoot / pour / ...)
- virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) = 0;
-
- /// Helper function, drops one item from the specified slot (like a dropper)
- void DropFromSlot(cChunk & a_Chunk, int a_SlotNum);
-} ; // tolua_export
-
-
-
-
+
+// DropSpenser.h
+
+// Declares the cDropSpenser class representing a common ancestor to the cDispenserEntity and cDropperEntity
+// The dropper and dispenser only needs to override the DropSpenseFromSlot() function to provide the specific item behavior
+
+
+
+
+
+#pragma once
+
+#include "BlockEntityWithItems.h"
+#include "../UI/WindowOwner.h"
+
+
+
+
+
+namespace Json
+{
+ class Value;
+}
+
+class cClientHandle;
+class cServer;
+
+
+
+
+
+// tolua_begin
+class cDropSpenserEntity :
+ public cBlockEntityWithItems,
+ public cBlockEntityWindowOwner
+{
+ typedef cBlockEntityWithItems super;
+
+public:
+ enum {
+ ContentsHeight = 3,
+ ContentsWidth = 3,
+ } ;
+
+ // tolua_end
+
+ cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
+ virtual ~cDropSpenserEntity();
+
+ static const char * GetClassStatic(void) { return "cDropSpenserEntity"; }
+
+ bool LoadFromJson(const Json::Value & a_Value);
+
+ // cBlockEntity overrides:
+ virtual void SaveToJson(Json::Value & a_Value) override;
+ virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void SendTo(cClientHandle & a_Client) override;
+ virtual void UsedBy(cPlayer * a_Player) override;
+
+ // tolua_begin
+
+ /// Modifies the block coords to match the dropspenser direction given (where the dropspensed pickups should materialize)
+ void AddDropSpenserDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_Direction);
+
+ /// Sets the dropspenser to dropspense an item in the next tick
+ void Activate(void);
+
+ /// Sets the internal redstone power flag to "on" or "off", depending on the parameter. Calls Activate() if appropriate
+ void SetRedstonePower(bool a_IsPowered);
+
+ // tolua_end
+
+protected:
+ bool m_ShouldDropSpense; ///< If true, the dropspenser will dropspense an item in the next tick
+ bool m_IsPowered; ///< Set to true when the dropspenser receives redstone power.
+
+ /// Does the actual work on dropspensing an item. Chooses the slot, calls DropSpenseFromSlot() and handles smoke / sound effects
+ void DropSpense(cChunk & a_Chunk);
+
+ /// Override this function to provide the specific behavior for item dropspensing (drop / shoot / pour / ...)
+ virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) = 0;
+
+ /// Helper function, drops one item from the specified slot (like a dropper)
+ void DropFromSlot(cChunk & a_Chunk, int a_SlotNum);
+} ; // tolua_export
+
+
+
+
diff --git a/source/BlockEntities/DropperEntity.cpp b/source/BlockEntities/DropperEntity.cpp
index ebb52c8a1..44d679e02 100644
--- a/source/BlockEntities/DropperEntity.cpp
+++ b/source/BlockEntities/DropperEntity.cpp
@@ -1,42 +1,42 @@
-
-// DropperEntity.cpp
-
-// Implements the cRtopperEntity class representing a Dropper block entity
-
-#include "Globals.h"
-#include "DropperEntity.h"
-#include "../Player.h"
-#include "../Simulator/FluidSimulator.h"
-
-
-
-
-
-cDropperEntity::cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ) :
- super(E_BLOCK_DROPPER, a_BlockX, a_BlockY, a_BlockZ, NULL)
-{
- SetBlockEntity(this); // cBlockEntityWindowOwner
-}
-
-
-
-
-
-cDropperEntity::cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
- super(E_BLOCK_DROPPER, a_BlockX, a_BlockY, a_BlockZ, a_World)
-{
- SetBlockEntity(this); // cBlockEntityWindowOwner
-}
-
-
-
-
-
-void cDropperEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
-{
- DropFromSlot(a_Chunk, a_SlotNum);
-}
-
-
-
-
+
+// DropperEntity.cpp
+
+// Implements the cRtopperEntity class representing a Dropper block entity
+
+#include "Globals.h"
+#include "DropperEntity.h"
+#include "../Player.h"
+#include "../Simulator/FluidSimulator.h"
+
+
+
+
+
+cDropperEntity::cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ) :
+ super(E_BLOCK_DROPPER, a_BlockX, a_BlockY, a_BlockZ, NULL)
+{
+ SetBlockEntity(this); // cBlockEntityWindowOwner
+}
+
+
+
+
+
+cDropperEntity::cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
+ super(E_BLOCK_DROPPER, a_BlockX, a_BlockY, a_BlockZ, a_World)
+{
+ SetBlockEntity(this); // cBlockEntityWindowOwner
+}
+
+
+
+
+
+void cDropperEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
+{
+ DropFromSlot(a_Chunk, a_SlotNum);
+}
+
+
+
+
diff --git a/source/BlockEntities/DropperEntity.h b/source/BlockEntities/DropperEntity.h
index ed29bfe95..af74e7f7c 100644
--- a/source/BlockEntities/DropperEntity.h
+++ b/source/BlockEntities/DropperEntity.h
@@ -1,49 +1,49 @@
-
-// DropperEntity.h
-
-// Declares the cDropperEntity class representing a dropper block entity
-
-
-
-
-
-#pragma once
-
-#include "DropSpenserEntity.h"
-
-
-
-
-
-// tolua_begin
-class cDropperEntity :
- public cDropSpenserEntity
-{
- typedef cDropSpenserEntity super;
-
-public:
-
- /// Constructor used while generating a chunk; sets m_World to NULL
- cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ);
-
- // tolua_end
-
- /// Constructor used for normal operation
- cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
-
- static const char * GetClassStatic(void) { return "cDropperEntity"; }
-
-protected:
- // cDropSpenserEntity overrides:
- virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) override;
-
- /** Takes an item from slot a_SlotNum and puts it into the container in front of the dropper.
- Called when there's a container directly in front of the dropper,
- so the dropper should store items there, rather than dropping.
- */
- void PutIntoContainer(cChunk & a_Chunk, int a_SlotNum, BLOCKTYPE a_ContainerBlock, int a_ContainerX, int a_ContainerY, int a_ContainerZ);
-} ; // tolua_export
-
-
-
-
+
+// DropperEntity.h
+
+// Declares the cDropperEntity class representing a dropper block entity
+
+
+
+
+
+#pragma once
+
+#include "DropSpenserEntity.h"
+
+
+
+
+
+// tolua_begin
+class cDropperEntity :
+ public cDropSpenserEntity
+{
+ typedef cDropSpenserEntity super;
+
+public:
+
+ /// Constructor used while generating a chunk; sets m_World to NULL
+ cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ // tolua_end
+
+ /// Constructor used for normal operation
+ cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
+
+ static const char * GetClassStatic(void) { return "cDropperEntity"; }
+
+protected:
+ // cDropSpenserEntity overrides:
+ virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) override;
+
+ /** Takes an item from slot a_SlotNum and puts it into the container in front of the dropper.
+ Called when there's a container directly in front of the dropper,
+ so the dropper should store items there, rather than dropping.
+ */
+ void PutIntoContainer(cChunk & a_Chunk, int a_SlotNum, BLOCKTYPE a_ContainerBlock, int a_ContainerX, int a_ContainerY, int a_ContainerZ);
+} ; // tolua_export
+
+
+
+
diff --git a/source/BlockEntities/HopperEntity.cpp b/source/BlockEntities/HopperEntity.cpp
index 3fadf727c..23e4b8096 100644
--- a/source/BlockEntities/HopperEntity.cpp
+++ b/source/BlockEntities/HopperEntity.cpp
@@ -1,523 +1,523 @@
-
-// HopperEntity.cpp
-
-// Implements the cHopperEntity representing a hopper block entity
-
-#include "Globals.h"
-#include "HopperEntity.h"
-#include "../Chunk.h"
-#include "../Player.h"
-#include "ChestEntity.h"
-#include "DropSpenserEntity.h"
-#include "FurnaceEntity.h"
-
-
-
-
-
-cHopperEntity::cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ) :
- super(E_BLOCK_HOPPER, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, NULL),
- m_LastMoveItemsInTick(0),
- m_LastMoveItemsOutTick(0)
-{
-}
-
-
-
-
-
-cHopperEntity::cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
- super(E_BLOCK_HOPPER, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World),
- m_LastMoveItemsInTick(0),
- m_LastMoveItemsOutTick(0)
-{
-}
-
-
-
-
-
-/** Returns the block coords of the block receiving the output items, based on the meta
-Returns false if unattached
-*/
-bool cHopperEntity::GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, int & a_OutputY, int & a_OutputZ)
-{
- a_OutputX = m_PosX;
- a_OutputY = m_PosY;
- a_OutputZ = m_PosZ;
- switch (a_BlockMeta)
- {
- case E_META_HOPPER_FACING_XM: a_OutputX--; return true;
- case E_META_HOPPER_FACING_XP: a_OutputX++; return true;
- case E_META_HOPPER_FACING_YM: a_OutputY--; return true;
- case E_META_HOPPER_FACING_ZM: a_OutputZ--; return true;
- case E_META_HOPPER_FACING_ZP: a_OutputZ++; return true;
- default:
- {
- // Not attached
- return false;
- }
- }
-}
-
-
-
-
-
-bool cHopperEntity::Tick(float a_Dt, cChunk & a_Chunk)
-{
- Int64 CurrentTick = a_Chunk.GetWorld()->GetWorldAge();
-
- bool res = false;
- res = MoveItemsIn (a_Chunk, CurrentTick) || res;
- res = MovePickupsIn(a_Chunk, CurrentTick) || res;
- res = MoveItemsOut (a_Chunk, CurrentTick) || res;
- return res;
-}
-
-
-
-
-
-void cHopperEntity::SaveToJson(Json::Value & a_Value)
-{
- // TODO
- LOGWARNING("%s: Not implemented yet", __FUNCTION__);
-}
-
-
-
-
-
-void cHopperEntity::SendTo(cClientHandle & a_Client)
-{
- // The hopper entity doesn't need anything sent to the client when it's created / gets in the viewdistance
- // All the actual handling is in the cWindow UI code that gets called when the hopper is rclked
-
- UNUSED(a_Client);
-}
-
-
-
-
-
-void cHopperEntity::UsedBy(cPlayer * a_Player)
-{
- // If the window is not created, open it anew:
- cWindow * Window = GetWindow();
- if (Window == NULL)
- {
- OpenNewWindow();
- Window = GetWindow();
- }
-
- // Open the window for the player:
- if (Window != NULL)
- {
- if (a_Player->GetWindow() != Window)
- {
- a_Player->OpenWindow(Window);
- }
- }
-
- // This is rather a hack
- // Instead of marking the chunk as dirty upon chest contents change, we mark it dirty now
- // We cannot properly detect contents change, but such a change doesn't happen without a player opening the chest first.
- // The few false positives aren't much to worry about
- int ChunkX, ChunkZ;
- cChunkDef::BlockToChunk(m_PosX, m_PosY, m_PosZ, ChunkX, ChunkZ);
- m_World->MarkChunkDirty(ChunkX, ChunkZ);
-}
-
-
-
-
-
-/// Opens a new window UI for this hopper
-void cHopperEntity::OpenNewWindow(void)
-{
- OpenWindow(new cHopperWindow(m_PosX, m_PosY, m_PosZ, this));
-}
-
-
-
-
-
-/// Moves items from the container above it into this hopper. Returns true if the contents have changed.
-bool cHopperEntity::MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick)
-{
- if (m_PosY >= cChunkDef::Height)
- {
- // This hopper is at the top of the world, no more blocks above
- return false;
- }
-
- if (a_CurrentTick - m_LastMoveItemsInTick < TICKS_PER_TRANSFER)
- {
- // Too early after the previous transfer
- return false;
- }
-
- // Try moving an item in:
- bool res = false;
- switch (a_Chunk.GetBlock(m_RelX, m_PosY + 1, m_RelZ))
- {
- case E_BLOCK_CHEST: res = MoveItemsFromChest(a_Chunk); break;
- case E_BLOCK_FURNACE: res = MoveItemsFromFurnace(a_Chunk); break;
- case E_BLOCK_DISPENSER:
- case E_BLOCK_DROPPER: res = MoveItemsFromGrid(((cDropSpenserEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))->GetContents()); break;
- case E_BLOCK_HOPPER: res = MoveItemsFromGrid(((cHopperEntity *) a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))->GetContents()); break;
- case E_BLOCK_LIT_FURNACE: res = MoveItemsFromFurnace(a_Chunk); break;
- }
-
- // If the item has been moved, reset the last tick:
- if (res)
- {
- m_LastMoveItemsInTick = a_CurrentTick;
- }
-
- return res;
-}
-
-
-
-
-
-/// Moves pickups from above this hopper into it. Returns true if the contents have changed.
-bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick)
-{
- // TODO
- return false;
-}
-
-
-
-
-
-/// Moves items out from this hopper into the destination. Returns true if the contents have changed.
-bool cHopperEntity::MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick)
-{
- if (a_CurrentTick - m_LastMoveItemsOutTick < TICKS_PER_TRANSFER)
- {
- // Too early after the previous transfer
- return false;
- }
-
- int bx, by, bz;
- NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
- if (!GetOutputBlockPos(Meta, bx, by, bz))
- {
- // Not attached to another container
- return false;
- }
- if (by < 0)
- {
- // Cannot output below the zero-th block level
- return false;
- }
-
- // Convert coords to relative:
- int rx = bx - a_Chunk.GetPosX() * cChunkDef::Width;
- int rz = bz - a_Chunk.GetPosZ() * cChunkDef::Width;
- cChunk * DestChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(rx, rz);
- if (DestChunk == NULL)
- {
- // The destination chunk has been unloaded, don't tick
- return false;
- }
-
- // Call proper moving function, based on the blocktype present at the coords:
- bool res = false;
- switch (DestChunk->GetBlock(rx, by, rz))
- {
- case E_BLOCK_CHEST: res = MoveItemsToChest(*DestChunk, bx, by, bz); break;
- case E_BLOCK_FURNACE: res = MoveItemsToFurnace(*DestChunk, bx, by, bz, Meta); break;
- case E_BLOCK_DISPENSER:
- case E_BLOCK_DROPPER: res = MoveItemsToGrid(((cDropSpenserEntity *)DestChunk->GetBlockEntity(bx, by, bz))->GetContents()); break;
- case E_BLOCK_HOPPER: res = MoveItemsToGrid(((cHopperEntity *) DestChunk->GetBlockEntity(bx, by, bz))->GetContents()); break;
- case E_BLOCK_LIT_FURNACE: res = MoveItemsToFurnace(*DestChunk, bx, by, bz, Meta); break;
- }
-
- // If the item has been moved, reset the last tick:
- if (res)
- {
- m_LastMoveItemsOutTick = a_CurrentTick;
- }
-
- return res;
-}
-
-
-
-
-
-/// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed.
-bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk)
-{
- if (MoveItemsFromGrid(((cChestEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))->GetContents()))
- {
- // Moved the item from the chest directly above the hopper
- return true;
- }
-
- // Check if the chest is a double-chest, if so, try to move from there:
- static const struct
- {
- int x, z;
- }
- Coords [] =
- {
- {1, 0},
- {-1, 0},
- {0, 1},
- {0, -1},
- } ;
- for (int i = 0; i < ARRAYCOUNT(Coords); i++)
- {
- int x = m_RelX + Coords[i].x;
- int z = m_RelZ + Coords[i].z;
- cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z);
- if (
- (Neighbor == NULL) ||
- (Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST)
- )
- {
- continue;
- }
- if (MoveItemsFromGrid(((cChestEntity *)Neighbor->GetBlockEntity(x, m_PosY, z))->GetContents()))
- {
- return true;
- }
- return false;
- }
-
- // The chest was single and nothing could be moved
- return false;
-}
-
-
-
-
-
-/// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed.
-bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk)
-{
- cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ);
- ASSERT(Furnace != NULL);
-
- // Try move from the output slot:
- if (MoveItemsFromSlot(Furnace->GetOutputSlot(), true))
- {
- cItem NewOutput(Furnace->GetOutputSlot());
- Furnace->SetOutputSlot(NewOutput.AddCount(-1));
- return true;
- }
-
- // No output moved, check if we can move an empty bucket out of the fuel slot:
- if (Furnace->GetFuelSlot().m_ItemType == E_ITEM_BUCKET)
- {
- if (MoveItemsFromSlot(Furnace->GetFuelSlot(), true))
- {
- Furnace->SetFuelSlot(cItem());
- return true;
- }
- }
-
- // Nothing can be moved
- return false;
-}
-
-
-
-
-
-/// Moves items from the specified ItemGrid into this hopper. Returns true if contents have changed.
-bool cHopperEntity::MoveItemsFromGrid(cItemGrid & a_Grid)
-{
- int NumSlots = a_Grid.GetNumSlots();
-
- // First try adding items of types already in the hopper:
- for (int i = 0; i < NumSlots; i++)
- {
- if (a_Grid.IsSlotEmpty(i))
- {
- continue;
- }
- if (MoveItemsFromSlot(a_Grid.GetSlot(i), false))
- {
- a_Grid.ChangeSlotCount(i, -1);
- return true;
- }
- }
-
- // No already existing stack can be topped up, try again with allowing new stacks:
- for (int i = 0; i < NumSlots; i++)
- {
- if (a_Grid.IsSlotEmpty(i))
- {
- continue;
- }
- if (MoveItemsFromSlot(a_Grid.GetSlot(i), true))
- {
- a_Grid.ChangeSlotCount(i, -1);
- return true;
- }
- }
- return false;
-}
-
-
-
-
-
-/// Moves one of the specified itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack.
-bool cHopperEntity::MoveItemsFromSlot(const cItem & a_ItemStack, bool a_AllowNewStacks)
-{
- cItem One(a_ItemStack.CopyOne());
- if (m_Contents.AddItem(One, a_AllowNewStacks) > 0)
- {
- return true;
- }
- return false;
-}
-
-
-
-
-
-/// Moves items to the chest at the specified coords. Returns true if contents have changed
-bool cHopperEntity::MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ)
-{
- // Try the chest directly connected to the hopper:
- if (MoveItemsToGrid(((cChestEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))->GetContents()))
- {
- return true;
- }
-
- // Check if the chest is a double-chest, if so, try to move into the other half:
- static const struct
- {
- int x, z;
- }
- Coords [] =
- {
- {1, 0},
- {-1, 0},
- {0, 1},
- {0, -1},
- } ;
- for (int i = 0; i < ARRAYCOUNT(Coords); i++)
- {
- int x = m_RelX + Coords[i].x;
- int z = m_RelZ + Coords[i].z;
- cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z);
- if (
- (Neighbor == NULL) ||
- (Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST)
- )
- {
- continue;
- }
- if (MoveItemsToGrid(((cChestEntity *)Neighbor->GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))->GetContents()))
- {
- return true;
- }
- return false;
- }
-
- // The chest was single and nothing could be moved
- return false;
-}
-
-
-
-
-
-/// Moves items to the furnace at the specified coords. Returns true if contents have changed
-bool cHopperEntity::MoveItemsToFurnace(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_HopperMeta)
-{
- cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ);
- if (a_HopperMeta == E_META_HOPPER_FACING_YM)
- {
- // Feed the input slot of the furnace
- return MoveItemsToSlot(Furnace->GetContents(), cFurnaceEntity::fsInput);
- }
- else
- {
- // Feed the fuel slot of the furnace
- return MoveItemsToSlot(Furnace->GetContents(), cFurnaceEntity::fsFuel);
- }
- return false;
-}
-
-
-
-
-
-/// Moves items to the specified ItemGrid. Returns true if contents have changed
-bool cHopperEntity::MoveItemsToGrid(cItemGrid & a_ItemGrid)
-{
- // Iterate through our slots, try to move from each one:
- for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
- {
- const cItem & SrcItem = m_Contents.GetSlot(i);
- if (SrcItem.IsEmpty())
- {
- continue;
- }
-
- cItem ToAdd = SrcItem.CopyOne();
- if (a_ItemGrid.AddItem(ToAdd) > 0)
- {
- m_Contents.ChangeSlotCount(i, -1);
- return true;
- }
- }
- return false;
-}
-
-
-
-
-
-/// Moves one piece to the specified ItemGrid's slot. Returns true if contents have changed.
-bool cHopperEntity::MoveItemsToSlot(cItemGrid & a_ItemGrid, int a_DestSlotNum)
-{
- if (a_ItemGrid.IsSlotEmpty(a_DestSlotNum))
- {
- // The slot is empty, move the first non-empty slot from our contents:
- for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
- {
- if (!m_Contents.IsSlotEmpty(i))
- {
- a_ItemGrid.SetSlot(a_DestSlotNum, m_Contents.GetSlot(i).CopyOne());
- m_Contents.ChangeSlotCount(i, -1);
- return true;
- }
- }
- return false;
- }
- else
- {
- // The slot is taken, try to top it up:
- const cItem & DestSlot = a_ItemGrid.GetSlot(a_DestSlotNum);
- if (DestSlot.IsFullStack())
- {
- return false;
- }
- for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
- {
- if (m_Contents.GetSlot(i).IsStackableWith(DestSlot))
- {
- a_ItemGrid.ChangeSlotCount(a_DestSlotNum, 1);
- m_Contents.ChangeSlotCount(i, -1);
- return true;
- }
- }
- return false;
- }
-}
-
-
-
-
+
+// HopperEntity.cpp
+
+// Implements the cHopperEntity representing a hopper block entity
+
+#include "Globals.h"
+#include "HopperEntity.h"
+#include "../Chunk.h"
+#include "../Player.h"
+#include "ChestEntity.h"
+#include "DropSpenserEntity.h"
+#include "FurnaceEntity.h"
+
+
+
+
+
+cHopperEntity::cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ) :
+ super(E_BLOCK_HOPPER, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, NULL),
+ m_LastMoveItemsInTick(0),
+ m_LastMoveItemsOutTick(0)
+{
+}
+
+
+
+
+
+cHopperEntity::cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
+ super(E_BLOCK_HOPPER, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World),
+ m_LastMoveItemsInTick(0),
+ m_LastMoveItemsOutTick(0)
+{
+}
+
+
+
+
+
+/** Returns the block coords of the block receiving the output items, based on the meta
+Returns false if unattached
+*/
+bool cHopperEntity::GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, int & a_OutputY, int & a_OutputZ)
+{
+ a_OutputX = m_PosX;
+ a_OutputY = m_PosY;
+ a_OutputZ = m_PosZ;
+ switch (a_BlockMeta)
+ {
+ case E_META_HOPPER_FACING_XM: a_OutputX--; return true;
+ case E_META_HOPPER_FACING_XP: a_OutputX++; return true;
+ case E_META_HOPPER_FACING_YM: a_OutputY--; return true;
+ case E_META_HOPPER_FACING_ZM: a_OutputZ--; return true;
+ case E_META_HOPPER_FACING_ZP: a_OutputZ++; return true;
+ default:
+ {
+ // Not attached
+ return false;
+ }
+ }
+}
+
+
+
+
+
+bool cHopperEntity::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ Int64 CurrentTick = a_Chunk.GetWorld()->GetWorldAge();
+
+ bool res = false;
+ res = MoveItemsIn (a_Chunk, CurrentTick) || res;
+ res = MovePickupsIn(a_Chunk, CurrentTick) || res;
+ res = MoveItemsOut (a_Chunk, CurrentTick) || res;
+ return res;
+}
+
+
+
+
+
+void cHopperEntity::SaveToJson(Json::Value & a_Value)
+{
+ // TODO
+ LOGWARNING("%s: Not implemented yet", __FUNCTION__);
+}
+
+
+
+
+
+void cHopperEntity::SendTo(cClientHandle & a_Client)
+{
+ // The hopper entity doesn't need anything sent to the client when it's created / gets in the viewdistance
+ // All the actual handling is in the cWindow UI code that gets called when the hopper is rclked
+
+ UNUSED(a_Client);
+}
+
+
+
+
+
+void cHopperEntity::UsedBy(cPlayer * a_Player)
+{
+ // If the window is not created, open it anew:
+ cWindow * Window = GetWindow();
+ if (Window == NULL)
+ {
+ OpenNewWindow();
+ Window = GetWindow();
+ }
+
+ // Open the window for the player:
+ if (Window != NULL)
+ {
+ if (a_Player->GetWindow() != Window)
+ {
+ a_Player->OpenWindow(Window);
+ }
+ }
+
+ // This is rather a hack
+ // Instead of marking the chunk as dirty upon chest contents change, we mark it dirty now
+ // We cannot properly detect contents change, but such a change doesn't happen without a player opening the chest first.
+ // The few false positives aren't much to worry about
+ int ChunkX, ChunkZ;
+ cChunkDef::BlockToChunk(m_PosX, m_PosY, m_PosZ, ChunkX, ChunkZ);
+ m_World->MarkChunkDirty(ChunkX, ChunkZ);
+}
+
+
+
+
+
+/// Opens a new window UI for this hopper
+void cHopperEntity::OpenNewWindow(void)
+{
+ OpenWindow(new cHopperWindow(m_PosX, m_PosY, m_PosZ, this));
+}
+
+
+
+
+
+/// Moves items from the container above it into this hopper. Returns true if the contents have changed.
+bool cHopperEntity::MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick)
+{
+ if (m_PosY >= cChunkDef::Height)
+ {
+ // This hopper is at the top of the world, no more blocks above
+ return false;
+ }
+
+ if (a_CurrentTick - m_LastMoveItemsInTick < TICKS_PER_TRANSFER)
+ {
+ // Too early after the previous transfer
+ return false;
+ }
+
+ // Try moving an item in:
+ bool res = false;
+ switch (a_Chunk.GetBlock(m_RelX, m_PosY + 1, m_RelZ))
+ {
+ case E_BLOCK_CHEST: res = MoveItemsFromChest(a_Chunk); break;
+ case E_BLOCK_FURNACE: res = MoveItemsFromFurnace(a_Chunk); break;
+ case E_BLOCK_DISPENSER:
+ case E_BLOCK_DROPPER: res = MoveItemsFromGrid(((cDropSpenserEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))->GetContents()); break;
+ case E_BLOCK_HOPPER: res = MoveItemsFromGrid(((cHopperEntity *) a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))->GetContents()); break;
+ case E_BLOCK_LIT_FURNACE: res = MoveItemsFromFurnace(a_Chunk); break;
+ }
+
+ // If the item has been moved, reset the last tick:
+ if (res)
+ {
+ m_LastMoveItemsInTick = a_CurrentTick;
+ }
+
+ return res;
+}
+
+
+
+
+
+/// Moves pickups from above this hopper into it. Returns true if the contents have changed.
+bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick)
+{
+ // TODO
+ return false;
+}
+
+
+
+
+
+/// Moves items out from this hopper into the destination. Returns true if the contents have changed.
+bool cHopperEntity::MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick)
+{
+ if (a_CurrentTick - m_LastMoveItemsOutTick < TICKS_PER_TRANSFER)
+ {
+ // Too early after the previous transfer
+ return false;
+ }
+
+ int bx, by, bz;
+ NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
+ if (!GetOutputBlockPos(Meta, bx, by, bz))
+ {
+ // Not attached to another container
+ return false;
+ }
+ if (by < 0)
+ {
+ // Cannot output below the zero-th block level
+ return false;
+ }
+
+ // Convert coords to relative:
+ int rx = bx - a_Chunk.GetPosX() * cChunkDef::Width;
+ int rz = bz - a_Chunk.GetPosZ() * cChunkDef::Width;
+ cChunk * DestChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(rx, rz);
+ if (DestChunk == NULL)
+ {
+ // The destination chunk has been unloaded, don't tick
+ return false;
+ }
+
+ // Call proper moving function, based on the blocktype present at the coords:
+ bool res = false;
+ switch (DestChunk->GetBlock(rx, by, rz))
+ {
+ case E_BLOCK_CHEST: res = MoveItemsToChest(*DestChunk, bx, by, bz); break;
+ case E_BLOCK_FURNACE: res = MoveItemsToFurnace(*DestChunk, bx, by, bz, Meta); break;
+ case E_BLOCK_DISPENSER:
+ case E_BLOCK_DROPPER: res = MoveItemsToGrid(((cDropSpenserEntity *)DestChunk->GetBlockEntity(bx, by, bz))->GetContents()); break;
+ case E_BLOCK_HOPPER: res = MoveItemsToGrid(((cHopperEntity *) DestChunk->GetBlockEntity(bx, by, bz))->GetContents()); break;
+ case E_BLOCK_LIT_FURNACE: res = MoveItemsToFurnace(*DestChunk, bx, by, bz, Meta); break;
+ }
+
+ // If the item has been moved, reset the last tick:
+ if (res)
+ {
+ m_LastMoveItemsOutTick = a_CurrentTick;
+ }
+
+ return res;
+}
+
+
+
+
+
+/// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed.
+bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk)
+{
+ if (MoveItemsFromGrid(((cChestEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))->GetContents()))
+ {
+ // Moved the item from the chest directly above the hopper
+ return true;
+ }
+
+ // Check if the chest is a double-chest, if so, try to move from there:
+ static const struct
+ {
+ int x, z;
+ }
+ Coords [] =
+ {
+ {1, 0},
+ {-1, 0},
+ {0, 1},
+ {0, -1},
+ } ;
+ for (int i = 0; i < ARRAYCOUNT(Coords); i++)
+ {
+ int x = m_RelX + Coords[i].x;
+ int z = m_RelZ + Coords[i].z;
+ cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z);
+ if (
+ (Neighbor == NULL) ||
+ (Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST)
+ )
+ {
+ continue;
+ }
+ if (MoveItemsFromGrid(((cChestEntity *)Neighbor->GetBlockEntity(x, m_PosY, z))->GetContents()))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ // The chest was single and nothing could be moved
+ return false;
+}
+
+
+
+
+
+/// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed.
+bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk)
+{
+ cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ);
+ ASSERT(Furnace != NULL);
+
+ // Try move from the output slot:
+ if (MoveItemsFromSlot(Furnace->GetOutputSlot(), true))
+ {
+ cItem NewOutput(Furnace->GetOutputSlot());
+ Furnace->SetOutputSlot(NewOutput.AddCount(-1));
+ return true;
+ }
+
+ // No output moved, check if we can move an empty bucket out of the fuel slot:
+ if (Furnace->GetFuelSlot().m_ItemType == E_ITEM_BUCKET)
+ {
+ if (MoveItemsFromSlot(Furnace->GetFuelSlot(), true))
+ {
+ Furnace->SetFuelSlot(cItem());
+ return true;
+ }
+ }
+
+ // Nothing can be moved
+ return false;
+}
+
+
+
+
+
+/// Moves items from the specified ItemGrid into this hopper. Returns true if contents have changed.
+bool cHopperEntity::MoveItemsFromGrid(cItemGrid & a_Grid)
+{
+ int NumSlots = a_Grid.GetNumSlots();
+
+ // First try adding items of types already in the hopper:
+ for (int i = 0; i < NumSlots; i++)
+ {
+ if (a_Grid.IsSlotEmpty(i))
+ {
+ continue;
+ }
+ if (MoveItemsFromSlot(a_Grid.GetSlot(i), false))
+ {
+ a_Grid.ChangeSlotCount(i, -1);
+ return true;
+ }
+ }
+
+ // No already existing stack can be topped up, try again with allowing new stacks:
+ for (int i = 0; i < NumSlots; i++)
+ {
+ if (a_Grid.IsSlotEmpty(i))
+ {
+ continue;
+ }
+ if (MoveItemsFromSlot(a_Grid.GetSlot(i), true))
+ {
+ a_Grid.ChangeSlotCount(i, -1);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+/// Moves one of the specified itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack.
+bool cHopperEntity::MoveItemsFromSlot(const cItem & a_ItemStack, bool a_AllowNewStacks)
+{
+ cItem One(a_ItemStack.CopyOne());
+ if (m_Contents.AddItem(One, a_AllowNewStacks) > 0)
+ {
+ return true;
+ }
+ return false;
+}
+
+
+
+
+
+/// Moves items to the chest at the specified coords. Returns true if contents have changed
+bool cHopperEntity::MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ // Try the chest directly connected to the hopper:
+ if (MoveItemsToGrid(((cChestEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))->GetContents()))
+ {
+ return true;
+ }
+
+ // Check if the chest is a double-chest, if so, try to move into the other half:
+ static const struct
+ {
+ int x, z;
+ }
+ Coords [] =
+ {
+ {1, 0},
+ {-1, 0},
+ {0, 1},
+ {0, -1},
+ } ;
+ for (int i = 0; i < ARRAYCOUNT(Coords); i++)
+ {
+ int x = m_RelX + Coords[i].x;
+ int z = m_RelZ + Coords[i].z;
+ cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z);
+ if (
+ (Neighbor == NULL) ||
+ (Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST)
+ )
+ {
+ continue;
+ }
+ if (MoveItemsToGrid(((cChestEntity *)Neighbor->GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))->GetContents()))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ // The chest was single and nothing could be moved
+ return false;
+}
+
+
+
+
+
+/// Moves items to the furnace at the specified coords. Returns true if contents have changed
+bool cHopperEntity::MoveItemsToFurnace(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_HopperMeta)
+{
+ cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ);
+ if (a_HopperMeta == E_META_HOPPER_FACING_YM)
+ {
+ // Feed the input slot of the furnace
+ return MoveItemsToSlot(Furnace->GetContents(), cFurnaceEntity::fsInput);
+ }
+ else
+ {
+ // Feed the fuel slot of the furnace
+ return MoveItemsToSlot(Furnace->GetContents(), cFurnaceEntity::fsFuel);
+ }
+ return false;
+}
+
+
+
+
+
+/// Moves items to the specified ItemGrid. Returns true if contents have changed
+bool cHopperEntity::MoveItemsToGrid(cItemGrid & a_ItemGrid)
+{
+ // Iterate through our slots, try to move from each one:
+ for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
+ {
+ const cItem & SrcItem = m_Contents.GetSlot(i);
+ if (SrcItem.IsEmpty())
+ {
+ continue;
+ }
+
+ cItem ToAdd = SrcItem.CopyOne();
+ if (a_ItemGrid.AddItem(ToAdd) > 0)
+ {
+ m_Contents.ChangeSlotCount(i, -1);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+/// Moves one piece to the specified ItemGrid's slot. Returns true if contents have changed.
+bool cHopperEntity::MoveItemsToSlot(cItemGrid & a_ItemGrid, int a_DestSlotNum)
+{
+ if (a_ItemGrid.IsSlotEmpty(a_DestSlotNum))
+ {
+ // The slot is empty, move the first non-empty slot from our contents:
+ for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
+ {
+ if (!m_Contents.IsSlotEmpty(i))
+ {
+ a_ItemGrid.SetSlot(a_DestSlotNum, m_Contents.GetSlot(i).CopyOne());
+ m_Contents.ChangeSlotCount(i, -1);
+ return true;
+ }
+ }
+ return false;
+ }
+ else
+ {
+ // The slot is taken, try to top it up:
+ const cItem & DestSlot = a_ItemGrid.GetSlot(a_DestSlotNum);
+ if (DestSlot.IsFullStack())
+ {
+ return false;
+ }
+ for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
+ {
+ if (m_Contents.GetSlot(i).IsStackableWith(DestSlot))
+ {
+ a_ItemGrid.ChangeSlotCount(a_DestSlotNum, 1);
+ m_Contents.ChangeSlotCount(i, -1);
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+
+
+
diff --git a/source/BlockEntities/HopperEntity.h b/source/BlockEntities/HopperEntity.h
index 7f5eb23d1..8fc17b631 100644
--- a/source/BlockEntities/HopperEntity.h
+++ b/source/BlockEntities/HopperEntity.h
@@ -1,102 +1,102 @@
-
-// HopperEntity.h
-
-// Declares the cHopperEntity representing a hopper block entity
-
-
-
-
-
-#pragma once
-
-#include "BlockEntityWithItems.h"
-#include "../UI/WindowOwner.h"
-
-
-
-
-
-class cHopperEntity : // tolua_export
- public cBlockEntityWindowOwner,
- // tolua_begin
- public cBlockEntityWithItems
-{
- typedef cBlockEntityWithItems super;
-
-public:
- enum {
- ContentsHeight = 1,
- ContentsWidth = 5,
- TICKS_PER_TRANSFER = 8, ///< How many ticks at minimum between two item transfers to or from the hopper
- } ;
-
- /// Constructor used while generating a chunk; sets m_World to NULL
- cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ);
-
- // tolua_end
-
- /// Constructor used for normal operation
- cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
-
- // tolua_begin
-
- /** Returns the block coords of the block receiving the output items, based on the meta
- Returns false if unattached
- */
- bool GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, int & a_OutputY, int & a_OutputZ);
-
- // tolua_end
-
- static const char * GetClassStatic(void) { return "cHopperEntity"; }
-
-protected:
-
- Int64 m_LastMoveItemsInTick;
- Int64 m_LastMoveItemsOutTick;
-
- // cBlockEntity overrides:
- virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
- virtual void SaveToJson(Json::Value & a_Value) override;
- virtual void SendTo(cClientHandle & a_Client) override;
- virtual void UsedBy(cPlayer * a_Player) override;
-
- /// Opens a new chest window for this chest. Scans for neighbors to open a double chest window, if appropriate.
- void OpenNewWindow(void);
-
- /// Moves items from the container above it into this hopper. Returns true if the contents have changed.
- bool MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick);
-
- /// Moves pickups from above this hopper into it. Returns true if the contents have changed.
- bool MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick);
-
- /// Moves items out from this hopper into the destination. Returns true if the contents have changed.
- bool MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick);
-
- /// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed.
- bool MoveItemsFromChest(cChunk & a_Chunk);
-
- /// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed.
- bool MoveItemsFromFurnace(cChunk & a_Chunk);
-
- /// Moves items from the specified ItemGrid into this hopper. Returns true if contents have changed.
- bool MoveItemsFromGrid(cItemGrid & a_Grid);
-
- /// Moves one piece from the specified itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack.
- bool MoveItemsFromSlot(const cItem & a_ItemStack, bool a_AllowNewStacks);
-
- /// Moves items to the chest at the specified coords. Returns true if contents have changed
- bool MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ);
-
- /// Moves items to the furnace at the specified coords. Returns true if contents have changed
- bool MoveItemsToFurnace(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_HopperMeta);
-
- /// Moves items to the specified ItemGrid. Returns true if contents have changed
- bool MoveItemsToGrid(cItemGrid & a_ItemGrid);
-
- /// Moves one piece to the specified ItemGrid's slot. Returns true if contents have changed.
- bool MoveItemsToSlot(cItemGrid & a_ItemGrid, int a_DestSlotNum);
-} ;
-
-
-
-
+
+// HopperEntity.h
+
+// Declares the cHopperEntity representing a hopper block entity
+
+
+
+
+
+#pragma once
+
+#include "BlockEntityWithItems.h"
+#include "../UI/WindowOwner.h"
+
+
+
+
+
+class cHopperEntity : // tolua_export
+ public cBlockEntityWindowOwner,
+ // tolua_begin
+ public cBlockEntityWithItems
+{
+ typedef cBlockEntityWithItems super;
+
+public:
+ enum {
+ ContentsHeight = 1,
+ ContentsWidth = 5,
+ TICKS_PER_TRANSFER = 8, ///< How many ticks at minimum between two item transfers to or from the hopper
+ } ;
+
+ /// Constructor used while generating a chunk; sets m_World to NULL
+ cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ // tolua_end
+
+ /// Constructor used for normal operation
+ cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
+
+ // tolua_begin
+
+ /** Returns the block coords of the block receiving the output items, based on the meta
+ Returns false if unattached
+ */
+ bool GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, int & a_OutputY, int & a_OutputZ);
+
+ // tolua_end
+
+ static const char * GetClassStatic(void) { return "cHopperEntity"; }
+
+protected:
+
+ Int64 m_LastMoveItemsInTick;
+ Int64 m_LastMoveItemsOutTick;
+
+ // cBlockEntity overrides:
+ virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void SaveToJson(Json::Value & a_Value) override;
+ virtual void SendTo(cClientHandle & a_Client) override;
+ virtual void UsedBy(cPlayer * a_Player) override;
+
+ /// Opens a new chest window for this chest. Scans for neighbors to open a double chest window, if appropriate.
+ void OpenNewWindow(void);
+
+ /// Moves items from the container above it into this hopper. Returns true if the contents have changed.
+ bool MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick);
+
+ /// Moves pickups from above this hopper into it. Returns true if the contents have changed.
+ bool MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick);
+
+ /// Moves items out from this hopper into the destination. Returns true if the contents have changed.
+ bool MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick);
+
+ /// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed.
+ bool MoveItemsFromChest(cChunk & a_Chunk);
+
+ /// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed.
+ bool MoveItemsFromFurnace(cChunk & a_Chunk);
+
+ /// Moves items from the specified ItemGrid into this hopper. Returns true if contents have changed.
+ bool MoveItemsFromGrid(cItemGrid & a_Grid);
+
+ /// Moves one piece from the specified itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack.
+ bool MoveItemsFromSlot(const cItem & a_ItemStack, bool a_AllowNewStacks);
+
+ /// Moves items to the chest at the specified coords. Returns true if contents have changed
+ bool MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Moves items to the furnace at the specified coords. Returns true if contents have changed
+ bool MoveItemsToFurnace(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_HopperMeta);
+
+ /// Moves items to the specified ItemGrid. Returns true if contents have changed
+ bool MoveItemsToGrid(cItemGrid & a_ItemGrid);
+
+ /// Moves one piece to the specified ItemGrid's slot. Returns true if contents have changed.
+ bool MoveItemsToSlot(cItemGrid & a_ItemGrid, int a_DestSlotNum);
+} ;
+
+
+
+
diff --git a/source/BlockEntities/JukeboxEntity.cpp b/source/BlockEntities/JukeboxEntity.cpp
index 617b7bd00..ec6d13282 100644
--- a/source/BlockEntities/JukeboxEntity.cpp
+++ b/source/BlockEntities/JukeboxEntity.cpp
@@ -1,123 +1,123 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "JukeboxEntity.h"
-#include "../World.h"
-#include <json/json.h>
-
-
-
-
-
-cJukeboxEntity::cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World)
- : cBlockEntity(E_BLOCK_JUKEBOX, a_BlockX, a_BlockY, a_BlockZ, a_World)
- , m_Record( 0 )
-{
-}
-
-
-
-
-
-cJukeboxEntity::~cJukeboxEntity()
-{
- if (m_Record >= 2256 && m_Record <= 2267)
- {
- EjectRecord();
- m_Record = 0;
- }
-}
-
-
-
-
-
-void cJukeboxEntity::UsedBy(cPlayer * a_Player)
-{
- if (m_Record == 0)
- {
- const cItem & HeldItem = a_Player->GetEquippedItem();
- if (HeldItem.m_ItemType >= 2256 && HeldItem.m_ItemType <= 2267)
- {
- m_Record = HeldItem.m_ItemType;
- a_Player->GetInventory().RemoveOneEquippedItem();
- PlayRecord();
- }
- }
- else if (m_Record >= 2256 && m_Record <= 2267)
- {
- EjectRecord();
- m_Record = 0;
- }
-}
-
-
-
-
-
-void cJukeboxEntity::PlayRecord( void )
-{
- m_World->BroadcastSoundParticleEffect(1005, m_PosX * 8, m_PosY * 8, m_PosZ * 8, m_Record);
-}
-
-
-
-
-
-void cJukeboxEntity::EjectRecord( void )
-{
- cItems Drops;
- Drops.push_back(cItem(m_Record, 1, 0));
- m_World->SpawnItemPickups(Drops, m_PosX, m_PosY+1, m_PosZ);
- m_World->BroadcastSoundParticleEffect(1005, m_PosX * 8, m_PosY * 8, m_PosZ * 8, 0);
-}
-
-
-
-
-
-int cJukeboxEntity::GetRecord( void )
-{
- return m_Record;
-}
-
-
-
-
-
-void cJukeboxEntity::SetRecord( int a_Record )
-{
- m_Record = a_Record;
-}
-
-
-
-
-
-bool cJukeboxEntity::LoadFromJson( const Json::Value & a_Value )
-{
- m_PosX = a_Value.get("x", 0).asInt();
- m_PosY = a_Value.get("y", 0).asInt();
- m_PosZ = a_Value.get("z", 0).asInt();
-
- m_Record = a_Value.get("Record", 0).asInt();
-
- return true;
-}
-
-
-
-
-
-void cJukeboxEntity::SaveToJson( Json::Value & a_Value )
-{
- a_Value["x"] = m_PosX;
- a_Value["y"] = m_PosY;
- a_Value["z"] = m_PosZ;
-
- a_Value["Record"] = m_Record;
-}
-
-
-
-
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "JukeboxEntity.h"
+#include "../World.h"
+#include <json/json.h>
+
+
+
+
+
+cJukeboxEntity::cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World)
+ : cBlockEntity(E_BLOCK_JUKEBOX, a_BlockX, a_BlockY, a_BlockZ, a_World)
+ , m_Record( 0 )
+{
+}
+
+
+
+
+
+cJukeboxEntity::~cJukeboxEntity()
+{
+ if (m_Record >= 2256 && m_Record <= 2267)
+ {
+ EjectRecord();
+ m_Record = 0;
+ }
+}
+
+
+
+
+
+void cJukeboxEntity::UsedBy(cPlayer * a_Player)
+{
+ if (m_Record == 0)
+ {
+ const cItem & HeldItem = a_Player->GetEquippedItem();
+ if (HeldItem.m_ItemType >= 2256 && HeldItem.m_ItemType <= 2267)
+ {
+ m_Record = HeldItem.m_ItemType;
+ a_Player->GetInventory().RemoveOneEquippedItem();
+ PlayRecord();
+ }
+ }
+ else if (m_Record >= 2256 && m_Record <= 2267)
+ {
+ EjectRecord();
+ m_Record = 0;
+ }
+}
+
+
+
+
+
+void cJukeboxEntity::PlayRecord( void )
+{
+ m_World->BroadcastSoundParticleEffect(1005, m_PosX * 8, m_PosY * 8, m_PosZ * 8, m_Record);
+}
+
+
+
+
+
+void cJukeboxEntity::EjectRecord( void )
+{
+ cItems Drops;
+ Drops.push_back(cItem(m_Record, 1, 0));
+ m_World->SpawnItemPickups(Drops, m_PosX, m_PosY+1, m_PosZ);
+ m_World->BroadcastSoundParticleEffect(1005, m_PosX * 8, m_PosY * 8, m_PosZ * 8, 0);
+}
+
+
+
+
+
+int cJukeboxEntity::GetRecord( void )
+{
+ return m_Record;
+}
+
+
+
+
+
+void cJukeboxEntity::SetRecord( int a_Record )
+{
+ m_Record = a_Record;
+}
+
+
+
+
+
+bool cJukeboxEntity::LoadFromJson( const Json::Value & a_Value )
+{
+ m_PosX = a_Value.get("x", 0).asInt();
+ m_PosY = a_Value.get("y", 0).asInt();
+ m_PosZ = a_Value.get("z", 0).asInt();
+
+ m_Record = a_Value.get("Record", 0).asInt();
+
+ return true;
+}
+
+
+
+
+
+void cJukeboxEntity::SaveToJson( Json::Value & a_Value )
+{
+ a_Value["x"] = m_PosX;
+ a_Value["y"] = m_PosY;
+ a_Value["z"] = m_PosZ;
+
+ a_Value["Record"] = m_Record;
+}
+
+
+
+
diff --git a/source/BlockEntities/JukeboxEntity.h b/source/BlockEntities/JukeboxEntity.h
index 063453607..74d40ecef 100644
--- a/source/BlockEntities/JukeboxEntity.h
+++ b/source/BlockEntities/JukeboxEntity.h
@@ -1,43 +1,43 @@
-
-#pragma once
-
-#include "BlockEntity.h"
-#include "../Player.h"
-
-
-
-
-
-namespace Json
-{
- class Value;
-}
-
-
-
-
-
-class cJukeboxEntity :
- public cBlockEntity
-{
-public:
- cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
- virtual ~cJukeboxEntity();
-
- bool LoadFromJson( const Json::Value& a_Value );
- virtual void SaveToJson( Json::Value& a_Value ) override;
-
- int GetRecord( void );
- void SetRecord( int a_Record );
- void PlayRecord( void );
- void EjectRecord( void );
- virtual void UsedBy( cPlayer * a_Player ) override;
- virtual void SendTo(cClientHandle & a_Client) override { };
-
-private:
- int m_Record;
-};
-
-
-
-
+
+#pragma once
+
+#include "BlockEntity.h"
+#include "../Player.h"
+
+
+
+
+
+namespace Json
+{
+ class Value;
+}
+
+
+
+
+
+class cJukeboxEntity :
+ public cBlockEntity
+{
+public:
+ cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
+ virtual ~cJukeboxEntity();
+
+ bool LoadFromJson( const Json::Value& a_Value );
+ virtual void SaveToJson( Json::Value& a_Value ) override;
+
+ int GetRecord( void );
+ void SetRecord( int a_Record );
+ void PlayRecord( void );
+ void EjectRecord( void );
+ virtual void UsedBy( cPlayer * a_Player ) override;
+ virtual void SendTo(cClientHandle & a_Client) override { };
+
+private:
+ int m_Record;
+};
+
+
+
+
diff --git a/source/BlockEntities/NoteEntity.cpp b/source/BlockEntities/NoteEntity.cpp
index 36da13692..6dc0e20a1 100644
--- a/source/BlockEntities/NoteEntity.cpp
+++ b/source/BlockEntities/NoteEntity.cpp
@@ -1,159 +1,159 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "NoteEntity.h"
-#include "../World.h"
-#include <json/json.h>
-
-
-cNoteEntity::cNoteEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World)
- : cBlockEntity(E_BLOCK_NOTE_BLOCK, a_BlockX, a_BlockY, a_BlockZ, a_World)
- , m_Pitch( 0 )
-{
-}
-
-
-
-
-
-cNoteEntity::~cNoteEntity()
-{
-}
-
-
-
-
-
-void cNoteEntity::UsedBy( cPlayer * a_Player )
-{
- IncrementPitch();
- MakeSound();
-}
-
-
-
-
-
-void cNoteEntity::MakeSound( void )
-{
- char instrument;
- AString sampleName;
-
- switch (m_World->GetBlock(m_PosX, m_PosY - 1, m_PosZ))
- {
- case E_BLOCK_PLANKS:
- case E_BLOCK_LOG:
- case E_BLOCK_NOTE_BLOCK:
- {
- // TODO: add other wood-based blocks if needed
- instrument = E_INST_DOUBLE_BASS;
- sampleName = "note.db";
- break;
- }
-
- case E_BLOCK_SAND:
- case E_BLOCK_GRAVEL:
- case E_BLOCK_SOULSAND:
- {
- instrument = E_INST_SNARE_DRUM;
- sampleName = "note.snare";
- break;
- }
-
- case E_BLOCK_GLASS:
- case E_BLOCK_GLASS_PANE:
- case E_BLOCK_GLOWSTONE:
- {
- instrument = E_INST_CLICKS;
- sampleName = "note.hat";
- break;
- }
-
- case E_BLOCK_STONE:
- case E_BLOCK_STONE_BRICKS:
- case E_BLOCK_COBBLESTONE:
- case E_BLOCK_OBSIDIAN:
- case E_BLOCK_NETHERRACK:
- case E_BLOCK_BRICK:
- case E_BLOCK_NETHER_BRICK:
- {
- // TODO: add other stone-based blocks if needed
- instrument = E_INST_BASS_DRUM;
- sampleName = "note.bassattack";
- break;
- }
-
- default:
- {
- instrument = E_INST_HARP_PIANO;
- sampleName = "note.harp";
- break;
- }
- }
-
- m_World->BroadcastBlockAction(m_PosX, m_PosY, m_PosZ, instrument, m_Pitch, E_BLOCK_NOTE_BLOCK);
-
- // TODO: instead of calculating the power function over and over, make a precalculated table - there's only 24 pitches after all
- float calcPitch = pow(2.0f, ((float)m_Pitch - 12.0f) / 12.0f);
- m_World->BroadcastSoundEffect(sampleName, m_PosX * 8, m_PosY * 8, m_PosZ * 8, 3.0f, calcPitch);
-}
-
-
-
-
-
-char cNoteEntity::GetPitch( void )
-{
- return m_Pitch;
-}
-
-
-
-
-
-void cNoteEntity::SetPitch( char a_Pitch )
-{
- m_Pitch = a_Pitch % 25;
-}
-
-
-
-
-
-void cNoteEntity::IncrementPitch( void )
-{
- SetPitch( m_Pitch + 1 );
-}
-
-
-
-
-
-bool cNoteEntity::LoadFromJson( const Json::Value & a_Value )
-{
-
- m_PosX = a_Value.get("x", 0).asInt();
- m_PosY = a_Value.get("y", 0).asInt();
- m_PosZ = a_Value.get("z", 0).asInt();
-
- m_Pitch = (char)a_Value.get("p", 0).asInt();
-
- return true;
-}
-
-
-
-
-
-void cNoteEntity::SaveToJson( Json::Value & a_Value )
-{
- a_Value["x"] = m_PosX;
- a_Value["y"] = m_PosY;
- a_Value["z"] = m_PosZ;
-
- a_Value["p"] = m_Pitch;
-}
-
-
-
-
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "NoteEntity.h"
+#include "../World.h"
+#include <json/json.h>
+
+
+cNoteEntity::cNoteEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World)
+ : cBlockEntity(E_BLOCK_NOTE_BLOCK, a_BlockX, a_BlockY, a_BlockZ, a_World)
+ , m_Pitch( 0 )
+{
+}
+
+
+
+
+
+cNoteEntity::~cNoteEntity()
+{
+}
+
+
+
+
+
+void cNoteEntity::UsedBy( cPlayer * a_Player )
+{
+ IncrementPitch();
+ MakeSound();
+}
+
+
+
+
+
+void cNoteEntity::MakeSound( void )
+{
+ char instrument;
+ AString sampleName;
+
+ switch (m_World->GetBlock(m_PosX, m_PosY - 1, m_PosZ))
+ {
+ case E_BLOCK_PLANKS:
+ case E_BLOCK_LOG:
+ case E_BLOCK_NOTE_BLOCK:
+ {
+ // TODO: add other wood-based blocks if needed
+ instrument = E_INST_DOUBLE_BASS;
+ sampleName = "note.db";
+ break;
+ }
+
+ case E_BLOCK_SAND:
+ case E_BLOCK_GRAVEL:
+ case E_BLOCK_SOULSAND:
+ {
+ instrument = E_INST_SNARE_DRUM;
+ sampleName = "note.snare";
+ break;
+ }
+
+ case E_BLOCK_GLASS:
+ case E_BLOCK_GLASS_PANE:
+ case E_BLOCK_GLOWSTONE:
+ {
+ instrument = E_INST_CLICKS;
+ sampleName = "note.hat";
+ break;
+ }
+
+ case E_BLOCK_STONE:
+ case E_BLOCK_STONE_BRICKS:
+ case E_BLOCK_COBBLESTONE:
+ case E_BLOCK_OBSIDIAN:
+ case E_BLOCK_NETHERRACK:
+ case E_BLOCK_BRICK:
+ case E_BLOCK_NETHER_BRICK:
+ {
+ // TODO: add other stone-based blocks if needed
+ instrument = E_INST_BASS_DRUM;
+ sampleName = "note.bassattack";
+ break;
+ }
+
+ default:
+ {
+ instrument = E_INST_HARP_PIANO;
+ sampleName = "note.harp";
+ break;
+ }
+ }
+
+ m_World->BroadcastBlockAction(m_PosX, m_PosY, m_PosZ, instrument, m_Pitch, E_BLOCK_NOTE_BLOCK);
+
+ // TODO: instead of calculating the power function over and over, make a precalculated table - there's only 24 pitches after all
+ float calcPitch = pow(2.0f, ((float)m_Pitch - 12.0f) / 12.0f);
+ m_World->BroadcastSoundEffect(sampleName, m_PosX * 8, m_PosY * 8, m_PosZ * 8, 3.0f, calcPitch);
+}
+
+
+
+
+
+char cNoteEntity::GetPitch( void )
+{
+ return m_Pitch;
+}
+
+
+
+
+
+void cNoteEntity::SetPitch( char a_Pitch )
+{
+ m_Pitch = a_Pitch % 25;
+}
+
+
+
+
+
+void cNoteEntity::IncrementPitch( void )
+{
+ SetPitch( m_Pitch + 1 );
+}
+
+
+
+
+
+bool cNoteEntity::LoadFromJson( const Json::Value & a_Value )
+{
+
+ m_PosX = a_Value.get("x", 0).asInt();
+ m_PosY = a_Value.get("y", 0).asInt();
+ m_PosZ = a_Value.get("z", 0).asInt();
+
+ m_Pitch = (char)a_Value.get("p", 0).asInt();
+
+ return true;
+}
+
+
+
+
+
+void cNoteEntity::SaveToJson( Json::Value & a_Value )
+{
+ a_Value["x"] = m_PosX;
+ a_Value["y"] = m_PosY;
+ a_Value["z"] = m_PosZ;
+
+ a_Value["p"] = m_Pitch;
+}
+
+
+
+
diff --git a/source/BlockEntities/NoteEntity.h b/source/BlockEntities/NoteEntity.h
index 29ce4ef8f..385591df6 100644
--- a/source/BlockEntities/NoteEntity.h
+++ b/source/BlockEntities/NoteEntity.h
@@ -1,52 +1,52 @@
-
-#pragma once
-
-#include "BlockEntity.h"
-
-
-namespace Json
-{
- class Value;
-}
-
-
-
-
-
-enum ENUM_NOTE_INSTRUMENTS
-{
- E_INST_HARP_PIANO = 0,
- E_INST_DOUBLE_BASS = 1,
- E_INST_SNARE_DRUM = 2,
- E_INST_CLICKS = 3,
- E_INST_BASS_DRUM = 4
-};
-
-
-
-
-
-class cNoteEntity :
- public cBlockEntity
-{
-public:
- cNoteEntity(int a_X, int a_Y, int a_Z, cWorld * a_World);
- virtual ~cNoteEntity();
-
- bool LoadFromJson( const Json::Value& a_Value );
- virtual void SaveToJson( Json::Value& a_Value ) override;
-
- char GetPitch( void );
- void SetPitch( char a_Pitch );
- void IncrementPitch( void );
- void MakeSound( void );
- virtual void UsedBy( cPlayer * a_Player ) override;
- virtual void SendTo(cClientHandle & a_Client) override { };
-
-private:
- unsigned char m_Pitch;
-};
-
-
-
-
+
+#pragma once
+
+#include "BlockEntity.h"
+
+
+namespace Json
+{
+ class Value;
+}
+
+
+
+
+
+enum ENUM_NOTE_INSTRUMENTS
+{
+ E_INST_HARP_PIANO = 0,
+ E_INST_DOUBLE_BASS = 1,
+ E_INST_SNARE_DRUM = 2,
+ E_INST_CLICKS = 3,
+ E_INST_BASS_DRUM = 4
+};
+
+
+
+
+
+class cNoteEntity :
+ public cBlockEntity
+{
+public:
+ cNoteEntity(int a_X, int a_Y, int a_Z, cWorld * a_World);
+ virtual ~cNoteEntity();
+
+ bool LoadFromJson( const Json::Value& a_Value );
+ virtual void SaveToJson( Json::Value& a_Value ) override;
+
+ char GetPitch( void );
+ void SetPitch( char a_Pitch );
+ void IncrementPitch( void );
+ void MakeSound( void );
+ virtual void UsedBy( cPlayer * a_Player ) override;
+ virtual void SendTo(cClientHandle & a_Client) override { };
+
+private:
+ unsigned char m_Pitch;
+};
+
+
+
+
diff --git a/source/Blocks/BlockBed.cpp b/source/Blocks/BlockBed.cpp
index 513874252..2ae6980ac 100644
--- a/source/Blocks/BlockBed.cpp
+++ b/source/Blocks/BlockBed.cpp
@@ -1,85 +1,85 @@
-#include "Globals.h"
-#include "BlockBed.h"
-
-
-
-
-
-void cBlockBedHandler::OnPlacedByPlayer(
- 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
-)
-{
- if (a_BlockMeta < 8)
- {
- Vector3i Direction = MetaDataToDirection(a_BlockMeta);
- a_World->SetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, a_BlockMeta | 0x8);
- }
-}
-
-
-
-
-
-void cBlockBedHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
-{
- NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
-
- Vector3i ThisPos( a_BlockX, a_BlockY, a_BlockZ );
- Vector3i Direction = MetaDataToDirection( OldMeta & 0x7 );
- if (OldMeta & 0x8)
- {
- // Was pillow
- if (a_World->GetBlock(ThisPos - Direction) == E_BLOCK_BED)
- {
- a_World->FastSetBlock(ThisPos - Direction, E_BLOCK_AIR, 0);
- }
- }
- else
- {
- // Was foot end
- if (a_World->GetBlock(ThisPos + Direction) == E_BLOCK_BED)
- {
- a_World->FastSetBlock(ThisPos + Direction, E_BLOCK_AIR, 0);
- }
- }
-}
-
-
-
-
-
-void cBlockBedHandler::OnUse(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)
-{
- if (a_World->GetDimension() != 0)
- {
- a_World->DoExplosiontAt(5, a_BlockX, a_BlockY, a_BlockZ);
- } else {
- if (a_World->GetTimeOfDay() > 13000)
- {
- NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
- if (Meta & 0x8)
- {
- // Is pillow
- a_World->BroadcastUseBed(*a_Player, a_BlockX, a_BlockY, a_BlockZ);
- }
- else
- {
- // Is foot end
- Vector3i Direction = MetaDataToDirection( Meta & 0x7 );
- if (a_World->GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) == E_BLOCK_BED) // Must always use pillow location for sleeping
- {
- a_World->BroadcastUseBed(*a_Player, a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z);
- }
- }
- } else {
- a_Player->SendMessage("You can only sleep at night");
- }
- }
-}
-
-
-
-
+#include "Globals.h"
+#include "BlockBed.h"
+
+
+
+
+
+void cBlockBedHandler::OnPlacedByPlayer(
+ 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
+)
+{
+ if (a_BlockMeta < 8)
+ {
+ Vector3i Direction = MetaDataToDirection(a_BlockMeta);
+ a_World->SetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, a_BlockMeta | 0x8);
+ }
+}
+
+
+
+
+
+void cBlockBedHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+
+ Vector3i ThisPos( a_BlockX, a_BlockY, a_BlockZ );
+ Vector3i Direction = MetaDataToDirection( OldMeta & 0x7 );
+ if (OldMeta & 0x8)
+ {
+ // Was pillow
+ if (a_World->GetBlock(ThisPos - Direction) == E_BLOCK_BED)
+ {
+ a_World->FastSetBlock(ThisPos - Direction, E_BLOCK_AIR, 0);
+ }
+ }
+ else
+ {
+ // Was foot end
+ if (a_World->GetBlock(ThisPos + Direction) == E_BLOCK_BED)
+ {
+ a_World->FastSetBlock(ThisPos + Direction, E_BLOCK_AIR, 0);
+ }
+ }
+}
+
+
+
+
+
+void cBlockBedHandler::OnUse(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)
+{
+ if (a_World->GetDimension() != 0)
+ {
+ a_World->DoExplosiontAt(5, a_BlockX, a_BlockY, a_BlockZ);
+ } else {
+ if (a_World->GetTimeOfDay() > 13000)
+ {
+ NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ if (Meta & 0x8)
+ {
+ // Is pillow
+ a_World->BroadcastUseBed(*a_Player, a_BlockX, a_BlockY, a_BlockZ);
+ }
+ else
+ {
+ // Is foot end
+ Vector3i Direction = MetaDataToDirection( Meta & 0x7 );
+ if (a_World->GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) == E_BLOCK_BED) // Must always use pillow location for sleeping
+ {
+ a_World->BroadcastUseBed(*a_Player, a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z);
+ }
+ }
+ } else {
+ a_Player->SendMessage("You can only sleep at night");
+ }
+ }
+}
+
+
+
+
diff --git a/source/Blocks/BlockBed.h b/source/Blocks/BlockBed.h
index bf8906750..8286ceb11 100644
--- a/source/Blocks/BlockBed.h
+++ b/source/Blocks/BlockBed.h
@@ -1,73 +1,73 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-#include "../World.h"
-#include "../Player.h"
-
-
-
-
-
-class cBlockBedHandler :
- public cBlockHandler
-{
-public:
- cBlockBedHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual void OnPlacedByPlayer(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;
- virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override;
- virtual void OnUse(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) override;
-
-
- virtual bool IsUseable(void) override
- {
- return true;
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- // Reset meta to zero
- a_Pickups.push_back(cItem(E_ITEM_BED, 1, 0));
- }
-
-
- virtual bool DoesAllowBlockOnTop() override
- {
- return false;
- }
-
-
- // Bed specific helper functions
- static NIBBLETYPE RotationToMetaData(double a_Rotation)
- {
- a_Rotation += 180 + (180 / 4); // So its not aligned with axis
- if (a_Rotation > 360) a_Rotation -= 360;
-
- a_Rotation = (a_Rotation / 360) * 4;
-
- return ((char)a_Rotation + 2) % 4;
- }
-
-
- static Vector3i MetaDataToDirection(NIBBLETYPE a_MetaData)
- {
- switch (a_MetaData)
- {
- case 0: return Vector3i(0, 0, 1);
- case 1: return Vector3i(-1, 0, 0);
- case 2: return Vector3i(0, 0, -1);
- case 3: return Vector3i(1, 0, 0);
- }
- return Vector3i();
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../World.h"
+#include "../Player.h"
+
+
+
+
+
+class cBlockBedHandler :
+ public cBlockHandler
+{
+public:
+ cBlockBedHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void OnPlacedByPlayer(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;
+ virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override;
+ virtual void OnUse(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) override;
+
+
+ virtual bool IsUseable(void) override
+ {
+ return true;
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Reset meta to zero
+ a_Pickups.push_back(cItem(E_ITEM_BED, 1, 0));
+ }
+
+
+ virtual bool DoesAllowBlockOnTop() override
+ {
+ return false;
+ }
+
+
+ // Bed specific helper functions
+ static NIBBLETYPE RotationToMetaData(double a_Rotation)
+ {
+ a_Rotation += 180 + (180 / 4); // So its not aligned with axis
+ if (a_Rotation > 360) a_Rotation -= 360;
+
+ a_Rotation = (a_Rotation / 360) * 4;
+
+ return ((char)a_Rotation + 2) % 4;
+ }
+
+
+ static Vector3i MetaDataToDirection(NIBBLETYPE a_MetaData)
+ {
+ switch (a_MetaData)
+ {
+ case 0: return Vector3i(0, 0, 1);
+ case 1: return Vector3i(-1, 0, 0);
+ case 2: return Vector3i(0, 0, -1);
+ case 3: return Vector3i(1, 0, 0);
+ }
+ return Vector3i();
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockBrewingStand.h b/source/Blocks/BlockBrewingStand.h
index 34a2b6c56..57642bcb6 100644
--- a/source/Blocks/BlockBrewingStand.h
+++ b/source/Blocks/BlockBrewingStand.h
@@ -1,32 +1,32 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockBrewingStandHandler :
- public cBlockHandler
-{
-public:
- cBlockBrewingStandHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- a_Pickups.push_back(cItem(E_ITEM_BREWING_STAND, 1, 0));
- }
-
- virtual bool IsUseable() override
- {
- return true;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockBrewingStandHandler :
+ public cBlockHandler
+{
+public:
+ cBlockBrewingStandHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_ITEM_BREWING_STAND, 1, 0));
+ }
+
+ virtual bool IsUseable() override
+ {
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockCactus.h b/source/Blocks/BlockCactus.h
index 516f024d8..1d123bc0a 100644
--- a/source/Blocks/BlockCactus.h
+++ b/source/Blocks/BlockCactus.h
@@ -1,88 +1,88 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockCactusHandler :
- public cBlockHandler
-{
-public:
- cBlockCactusHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- // Reset meta to 0
- a_Pickups.push_back(cItem(m_BlockType, 1, 0));
- }
-
-
- virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
- {
- if (a_RelY <= 0)
- {
- return false;
- }
- BLOCKTYPE Surface = a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ);
- if ((Surface != E_BLOCK_SAND) && (Surface != E_BLOCK_CACTUS))
- {
- // Cactus can only be placed on sand and itself
- return false;
- }
-
- // Check surroundings. Cacti may ONLY be surrounded by air
- static const struct
- {
- int x, z;
- } Coords[] =
- {
- {-1, 0},
- { 1, 0},
- { 0, -1},
- { 0, 1},
- } ;
- for (int i = 0; i < ARRAYCOUNT(Coords); i++)
- {
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
- if (
- a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta) &&
- (BlockType != E_BLOCK_AIR)
- )
- {
- return false;
- }
- } // for i - Coords[]
-
- return true;
- }
-
-
- virtual bool CanBePlacedOnSide(void) override
- {
- return false;
- }
-
-
- void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
- {
- a_World->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, 1);
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.cloth";
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockCactusHandler :
+ public cBlockHandler
+{
+public:
+ cBlockCactusHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Reset meta to 0
+ a_Pickups.push_back(cItem(m_BlockType, 1, 0));
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ if (a_RelY <= 0)
+ {
+ return false;
+ }
+ BLOCKTYPE Surface = a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ);
+ if ((Surface != E_BLOCK_SAND) && (Surface != E_BLOCK_CACTUS))
+ {
+ // Cactus can only be placed on sand and itself
+ return false;
+ }
+
+ // Check surroundings. Cacti may ONLY be surrounded by air
+ static const struct
+ {
+ int x, z;
+ } Coords[] =
+ {
+ {-1, 0},
+ { 1, 0},
+ { 0, -1},
+ { 0, 1},
+ } ;
+ for (int i = 0; i < ARRAYCOUNT(Coords); i++)
+ {
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (
+ a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta) &&
+ (BlockType != E_BLOCK_AIR)
+ )
+ {
+ return false;
+ }
+ } // for i - Coords[]
+
+ return true;
+ }
+
+
+ virtual bool CanBePlacedOnSide(void) override
+ {
+ return false;
+ }
+
+
+ void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ a_World->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, 1);
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.cloth";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockCauldron.h b/source/Blocks/BlockCauldron.h
index 571235441..b0e00f869 100644
--- a/source/Blocks/BlockCauldron.h
+++ b/source/Blocks/BlockCauldron.h
@@ -1,59 +1,59 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockCauldronHandler :
- public cBlockHandler
-{
-public:
- cBlockCauldronHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- a_Pickups.push_back(cItem(E_ITEM_CAULDRON, 1, 0));
- }
-
- void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
- {
- char Meta = a_World->GetBlockMeta( a_BlockX, a_BlockY, a_BlockZ );
- switch( a_Player->GetEquippedItem().m_ItemType )
- {
- case E_ITEM_WATER_BUCKET:
- {
- a_World->SetBlockMeta( a_BlockX, a_BlockY, a_BlockZ, 3 );
- a_Player->GetInventory().RemoveOneEquippedItem();
- cItem NewItem(E_ITEM_BUCKET, 1);
- a_Player->GetInventory().AddItem(NewItem);
- break;
- }
- case E_ITEM_GLASS_BOTTLE:
- {
- if( Meta > 0 )
- {
- a_World->SetBlockMeta( a_BlockX, a_BlockY, a_BlockZ, --Meta);
- a_Player->GetInventory().RemoveOneEquippedItem();
- cItem NewItem(E_ITEM_POTIONS, 1, 0);
- a_Player->GetInventory().AddItem(NewItem);
- }
- break;
- }
- }
- }
-
- virtual bool IsUseable() override
- {
- return true;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockCauldronHandler :
+ public cBlockHandler
+{
+public:
+ cBlockCauldronHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_ITEM_CAULDRON, 1, 0));
+ }
+
+ void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ char Meta = a_World->GetBlockMeta( a_BlockX, a_BlockY, a_BlockZ );
+ switch( a_Player->GetEquippedItem().m_ItemType )
+ {
+ case E_ITEM_WATER_BUCKET:
+ {
+ a_World->SetBlockMeta( a_BlockX, a_BlockY, a_BlockZ, 3 );
+ a_Player->GetInventory().RemoveOneEquippedItem();
+ cItem NewItem(E_ITEM_BUCKET, 1);
+ a_Player->GetInventory().AddItem(NewItem);
+ break;
+ }
+ case E_ITEM_GLASS_BOTTLE:
+ {
+ if( Meta > 0 )
+ {
+ a_World->SetBlockMeta( a_BlockX, a_BlockY, a_BlockZ, --Meta);
+ a_Player->GetInventory().RemoveOneEquippedItem();
+ cItem NewItem(E_ITEM_POTIONS, 1, 0);
+ a_Player->GetInventory().AddItem(NewItem);
+ }
+ break;
+ }
+ }
+ }
+
+ virtual bool IsUseable() override
+ {
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockChest.h b/source/Blocks/BlockChest.h
index 5a64a16bb..1975e11b2 100644
--- a/source/Blocks/BlockChest.h
+++ b/source/Blocks/BlockChest.h
@@ -1,222 +1,222 @@
-
-#pragma once
-
-#include "BlockEntity.h"
-#include "../World.h"
-#include "../Player.h"
-
-
-
-
-
-class cBlockChestHandler :
- public cBlockEntityHandler
-{
-public:
- cBlockChestHandler(BLOCKTYPE a_BlockType)
- : cBlockEntityHandler(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
- {
- a_BlockType = m_BlockType;
-
- // Is there a doublechest already next to this block?
- if (!CanBeAt(a_World, a_BlockX, a_BlockY, a_BlockZ))
- {
- // Yup, cannot form a triple-chest, refuse:
- return false;
- }
-
- // Check if this forms a doublechest, if so, need to adjust the meta:
- cBlockArea Area;
- if (!Area.Read(a_World, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1))
- {
- return false;
- }
- double rot = a_Player->GetRotation();
- if (
- (Area.GetRelBlockType(0, 0, 1) == E_BLOCK_CHEST) ||
- (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST)
- )
- {
- a_BlockMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3;
- return true;
- }
- if (
- (Area.GetRelBlockType(0, 0, 1) == E_BLOCK_CHEST) ||
- (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST)
- )
- {
- a_BlockMeta = (rot < 0) ? 4 : 5;
- return true;
- }
-
- // Single chest, get meta from rotation only
- a_BlockMeta = RotationToMetaData(rot);
- return true;
- }
-
-
- virtual void OnPlacedByPlayer(
- 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
- {
- // Check if this forms a doublechest, if so, need to adjust the meta:
- cBlockArea Area;
- if (!Area.Read(a_World, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1))
- {
- return;
- }
-
- double rot = a_Player->GetRotation();
- // Choose meta from player rotation, choose only between 2 or 3
- NIBBLETYPE NewMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3;
- if (
- CheckAndAdjustNeighbor(a_World, Area, 0, 1, NewMeta) ||
- CheckAndAdjustNeighbor(a_World, Area, 2, 1, NewMeta)
- )
- {
- // Forming a double chest in the X direction
- return;
- }
- // Choose meta from player rotation, choose only between 4 or 5
- NewMeta = (rot < 0) ? 4 : 5;
- if (
- CheckAndAdjustNeighbor(a_World, Area, 1, 0, NewMeta) ||
- CheckAndAdjustNeighbor(a_World, Area, 2, 2, NewMeta)
- )
- {
- // Forming a double chest in the Z direction
- return;
- }
-
- // Single chest, no further processing needed
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.wood";
- }
-
-
- virtual bool CanBeAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
- {
- cBlockArea Area;
- if (!Area.Read(a_World, a_BlockX - 2, a_BlockX + 2, a_BlockY, a_BlockY, a_BlockZ - 2, a_BlockZ + 2))
- {
- // Cannot read the surroundings, probably at the edge of loaded chunks. Disallow.
- return false;
- }
-
- int NumChestNeighbors = 0;
- if (Area.GetRelBlockType(1, 0, 2) == E_BLOCK_CHEST)
- {
- if (
- (Area.GetRelBlockType(0, 0, 2) == E_BLOCK_CHEST) ||
- (Area.GetRelBlockType(1, 0, 1) == E_BLOCK_CHEST) ||
- (Area.GetRelBlockType(1, 0, 3) == E_BLOCK_CHEST)
- )
- {
- // Already a doublechest neighbor, disallow:
- return false;
- }
- NumChestNeighbors += 1;
- }
- if (Area.GetRelBlockType(3, 0, 2) == E_BLOCK_CHEST)
- {
- if (
- (Area.GetRelBlockType(4, 0, 2) == E_BLOCK_CHEST) ||
- (Area.GetRelBlockType(3, 0, 1) == E_BLOCK_CHEST) ||
- (Area.GetRelBlockType(3, 0, 3) == E_BLOCK_CHEST)
- )
- {
- // Already a doublechest neighbor, disallow:
- return false;
- }
- NumChestNeighbors += 1;
- }
- if (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST)
- {
- if (
- (Area.GetRelBlockType(2, 0, 0) == E_BLOCK_CHEST) ||
- (Area.GetRelBlockType(1, 0, 1) == E_BLOCK_CHEST) ||
- (Area.GetRelBlockType(3, 0, 1) == E_BLOCK_CHEST)
- )
- {
- // Already a doublechest neighbor, disallow:
- return false;
- }
- NumChestNeighbors += 1;
- }
- if (Area.GetRelBlockType(2, 0, 3) == E_BLOCK_CHEST)
- {
- if (
- (Area.GetRelBlockType(2, 0, 4) == E_BLOCK_CHEST) ||
- (Area.GetRelBlockType(1, 0, 3) == E_BLOCK_CHEST) ||
- (Area.GetRelBlockType(3, 0, 3) == E_BLOCK_CHEST)
- )
- {
- // Already a doublechest neighbor, disallow:
- return false;
- }
- NumChestNeighbors += 1;
- }
- return (NumChestNeighbors < 2);
- }
-
-
- /// Translates player rotation when placing a chest into the chest block metadata. Valid for single chests only
- static NIBBLETYPE RotationToMetaData(double a_Rotation)
- {
- a_Rotation += 90 + 45; // So its not aligned with axis
-
- if (a_Rotation > 360.f)
- {
- a_Rotation -= 360.f;
- }
- if ((a_Rotation >= 0.f) && (a_Rotation < 90.f))
- {
- return 0x4;
- }
- else if ((a_Rotation >= 180) && (a_Rotation < 270))
- {
- return 0x5;
- }
- else if ((a_Rotation >= 90) && (a_Rotation < 180))
- {
- return 0x2;
- }
- else
- {
- return 0x3;
- }
- }
-
-
- /// If there's a chest in the a_Area in the specified coords, modifies its meta to a_NewMeta and returns true.
- bool CheckAndAdjustNeighbor(cWorld * a_World, const cBlockArea & a_Area, int a_RelX, int a_RelZ, NIBBLETYPE a_NewMeta)
- {
- if (a_Area.GetRelBlockType(a_RelX, 0, a_RelZ) != E_BLOCK_CHEST)
- {
- return false;
- }
- a_World->SetBlockMeta(a_Area.GetOriginX() + a_RelX, a_Area.GetOriginY(), a_Area.GetOriginZ() + a_RelZ, a_NewMeta);
- return true;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockEntity.h"
+#include "../World.h"
+#include "../Player.h"
+
+
+
+
+
+class cBlockChestHandler :
+ public cBlockEntityHandler
+{
+public:
+ cBlockChestHandler(BLOCKTYPE a_BlockType)
+ : cBlockEntityHandler(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
+ {
+ a_BlockType = m_BlockType;
+
+ // Is there a doublechest already next to this block?
+ if (!CanBeAt(a_World, a_BlockX, a_BlockY, a_BlockZ))
+ {
+ // Yup, cannot form a triple-chest, refuse:
+ return false;
+ }
+
+ // Check if this forms a doublechest, if so, need to adjust the meta:
+ cBlockArea Area;
+ if (!Area.Read(a_World, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1))
+ {
+ return false;
+ }
+ double rot = a_Player->GetRotation();
+ if (
+ (Area.GetRelBlockType(0, 0, 1) == E_BLOCK_CHEST) ||
+ (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST)
+ )
+ {
+ a_BlockMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3;
+ return true;
+ }
+ if (
+ (Area.GetRelBlockType(0, 0, 1) == E_BLOCK_CHEST) ||
+ (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST)
+ )
+ {
+ a_BlockMeta = (rot < 0) ? 4 : 5;
+ return true;
+ }
+
+ // Single chest, get meta from rotation only
+ a_BlockMeta = RotationToMetaData(rot);
+ return true;
+ }
+
+
+ virtual void OnPlacedByPlayer(
+ 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
+ {
+ // Check if this forms a doublechest, if so, need to adjust the meta:
+ cBlockArea Area;
+ if (!Area.Read(a_World, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1))
+ {
+ return;
+ }
+
+ double rot = a_Player->GetRotation();
+ // Choose meta from player rotation, choose only between 2 or 3
+ NIBBLETYPE NewMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3;
+ if (
+ CheckAndAdjustNeighbor(a_World, Area, 0, 1, NewMeta) ||
+ CheckAndAdjustNeighbor(a_World, Area, 2, 1, NewMeta)
+ )
+ {
+ // Forming a double chest in the X direction
+ return;
+ }
+ // Choose meta from player rotation, choose only between 4 or 5
+ NewMeta = (rot < 0) ? 4 : 5;
+ if (
+ CheckAndAdjustNeighbor(a_World, Area, 1, 0, NewMeta) ||
+ CheckAndAdjustNeighbor(a_World, Area, 2, 2, NewMeta)
+ )
+ {
+ // Forming a double chest in the Z direction
+ return;
+ }
+
+ // Single chest, no further processing needed
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+
+
+ virtual bool CanBeAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ cBlockArea Area;
+ if (!Area.Read(a_World, a_BlockX - 2, a_BlockX + 2, a_BlockY, a_BlockY, a_BlockZ - 2, a_BlockZ + 2))
+ {
+ // Cannot read the surroundings, probably at the edge of loaded chunks. Disallow.
+ return false;
+ }
+
+ int NumChestNeighbors = 0;
+ if (Area.GetRelBlockType(1, 0, 2) == E_BLOCK_CHEST)
+ {
+ if (
+ (Area.GetRelBlockType(0, 0, 2) == E_BLOCK_CHEST) ||
+ (Area.GetRelBlockType(1, 0, 1) == E_BLOCK_CHEST) ||
+ (Area.GetRelBlockType(1, 0, 3) == E_BLOCK_CHEST)
+ )
+ {
+ // Already a doublechest neighbor, disallow:
+ return false;
+ }
+ NumChestNeighbors += 1;
+ }
+ if (Area.GetRelBlockType(3, 0, 2) == E_BLOCK_CHEST)
+ {
+ if (
+ (Area.GetRelBlockType(4, 0, 2) == E_BLOCK_CHEST) ||
+ (Area.GetRelBlockType(3, 0, 1) == E_BLOCK_CHEST) ||
+ (Area.GetRelBlockType(3, 0, 3) == E_BLOCK_CHEST)
+ )
+ {
+ // Already a doublechest neighbor, disallow:
+ return false;
+ }
+ NumChestNeighbors += 1;
+ }
+ if (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST)
+ {
+ if (
+ (Area.GetRelBlockType(2, 0, 0) == E_BLOCK_CHEST) ||
+ (Area.GetRelBlockType(1, 0, 1) == E_BLOCK_CHEST) ||
+ (Area.GetRelBlockType(3, 0, 1) == E_BLOCK_CHEST)
+ )
+ {
+ // Already a doublechest neighbor, disallow:
+ return false;
+ }
+ NumChestNeighbors += 1;
+ }
+ if (Area.GetRelBlockType(2, 0, 3) == E_BLOCK_CHEST)
+ {
+ if (
+ (Area.GetRelBlockType(2, 0, 4) == E_BLOCK_CHEST) ||
+ (Area.GetRelBlockType(1, 0, 3) == E_BLOCK_CHEST) ||
+ (Area.GetRelBlockType(3, 0, 3) == E_BLOCK_CHEST)
+ )
+ {
+ // Already a doublechest neighbor, disallow:
+ return false;
+ }
+ NumChestNeighbors += 1;
+ }
+ return (NumChestNeighbors < 2);
+ }
+
+
+ /// Translates player rotation when placing a chest into the chest block metadata. Valid for single chests only
+ static NIBBLETYPE RotationToMetaData(double a_Rotation)
+ {
+ a_Rotation += 90 + 45; // So its not aligned with axis
+
+ if (a_Rotation > 360.f)
+ {
+ a_Rotation -= 360.f;
+ }
+ if ((a_Rotation >= 0.f) && (a_Rotation < 90.f))
+ {
+ return 0x4;
+ }
+ else if ((a_Rotation >= 180) && (a_Rotation < 270))
+ {
+ return 0x5;
+ }
+ else if ((a_Rotation >= 90) && (a_Rotation < 180))
+ {
+ return 0x2;
+ }
+ else
+ {
+ return 0x3;
+ }
+ }
+
+
+ /// If there's a chest in the a_Area in the specified coords, modifies its meta to a_NewMeta and returns true.
+ bool CheckAndAdjustNeighbor(cWorld * a_World, const cBlockArea & a_Area, int a_RelX, int a_RelZ, NIBBLETYPE a_NewMeta)
+ {
+ if (a_Area.GetRelBlockType(a_RelX, 0, a_RelZ) != E_BLOCK_CHEST)
+ {
+ return false;
+ }
+ a_World->SetBlockMeta(a_Area.GetOriginX() + a_RelX, a_Area.GetOriginY(), a_Area.GetOriginZ() + a_RelZ, a_NewMeta);
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockCloth.h b/source/Blocks/BlockCloth.h
index a95f12500..a136d3b9d 100644
--- a/source/Blocks/BlockCloth.h
+++ b/source/Blocks/BlockCloth.h
@@ -1,34 +1,34 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockClothHandler :
- public cBlockHandler
-{
-public:
- cBlockClothHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- a_Pickups.push_back(cItem(E_BLOCK_WOOL, 1, a_BlockMeta));
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.cloth";
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockClothHandler :
+ public cBlockHandler
+{
+public:
+ cBlockClothHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_BLOCK_WOOL, 1, a_BlockMeta));
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.cloth";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockCobWeb.h b/source/Blocks/BlockCobWeb.h
index 86bb6e773..982bfaa30 100644
--- a/source/Blocks/BlockCobWeb.h
+++ b/source/Blocks/BlockCobWeb.h
@@ -1,30 +1,30 @@
-
-// BlockCobWeb.h
-
-// Declares the cBlockCobWebHandler object representing the BlockHandler for cobwebs
-
-#pragma once
-
-
-
-
-
-class cBlockCobWebHandler :
- public cBlockHandler
-{
-public:
- cBlockCobWebHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override
- {
- a_Pickups.push_back(cItem(E_ITEM_STRING, 1, 0));
- }
-} ;
-
-
-
-
+
+// BlockCobWeb.h
+
+// Declares the cBlockCobWebHandler object representing the BlockHandler for cobwebs
+
+#pragma once
+
+
+
+
+
+class cBlockCobWebHandler :
+ public cBlockHandler
+{
+public:
+ cBlockCobWebHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override
+ {
+ a_Pickups.push_back(cItem(E_ITEM_STRING, 1, 0));
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockCrops.h b/source/Blocks/BlockCrops.h
index 7658d2633..4bc76fd50 100644
--- a/source/Blocks/BlockCrops.h
+++ b/source/Blocks/BlockCrops.h
@@ -1,108 +1,108 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-#include "../MersenneTwister.h"
-#include "../World.h"
-
-
-
-
-
-/// Common class that takes care of carrots, potatoes and wheat
-class cBlockCropsHandler :
- public cBlockHandler
-{
-public:
- cBlockCropsHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual bool DoesAllowBlockOnTop() override
- {
- return false;
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override
- {
- MTRand rand;
-
- if (a_Meta == 0x7)
- {
- // Is fully grown, drop the entire produce:
- switch (m_BlockType)
- {
- case E_BLOCK_CROPS:
- {
- a_Pickups.push_back(cItem(E_ITEM_WHEAT, 1, 0));
- a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2
- break;
- }
- case E_BLOCK_CARROTS:
- {
- a_Pickups.push_back(cItem(E_ITEM_CARROT, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2
- break;
- }
- case E_BLOCK_POTATOES:
- {
- a_Pickups.push_back(cItem(E_ITEM_POTATO, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2
- if (rand.randInt(20) == 0)
- {
- // With a 5% chance, drop a poisonous potato as well
- a_Pickups.push_back(cItem(E_ITEM_POISONOUS_POTATO, 1, 0));
- }
- break;
- }
- default:
- {
- ASSERT(!"Unhandled block type");
- break;
- }
- } // switch (m_BlockType)
- }
- else
- {
- // Drop 1 item of whatever is growing
- switch (m_BlockType)
- {
- case E_BLOCK_CROPS: a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1, 0)); break;
- case E_BLOCK_CARROTS: a_Pickups.push_back(cItem(E_ITEM_CARROT, 1, 0)); break;
- case E_BLOCK_POTATOES: a_Pickups.push_back(cItem(E_ITEM_POTATO, 1, 0)); break;
- default:
- {
- ASSERT(!"Unhandled block type");
- break;
- }
- }
- }
- }
-
-
- void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
- {
- NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
- if (Meta < 7)
- {
- a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_CROPS, ++Meta);
- }
- }
-
-
- virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
- {
- return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_FARMLAND));
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.grass";
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../MersenneTwister.h"
+#include "../World.h"
+
+
+
+
+
+/// Common class that takes care of carrots, potatoes and wheat
+class cBlockCropsHandler :
+ public cBlockHandler
+{
+public:
+ cBlockCropsHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual bool DoesAllowBlockOnTop() override
+ {
+ return false;
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override
+ {
+ MTRand rand;
+
+ if (a_Meta == 0x7)
+ {
+ // Is fully grown, drop the entire produce:
+ switch (m_BlockType)
+ {
+ case E_BLOCK_CROPS:
+ {
+ a_Pickups.push_back(cItem(E_ITEM_WHEAT, 1, 0));
+ a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2
+ break;
+ }
+ case E_BLOCK_CARROTS:
+ {
+ a_Pickups.push_back(cItem(E_ITEM_CARROT, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2
+ break;
+ }
+ case E_BLOCK_POTATOES:
+ {
+ a_Pickups.push_back(cItem(E_ITEM_POTATO, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2
+ if (rand.randInt(20) == 0)
+ {
+ // With a 5% chance, drop a poisonous potato as well
+ a_Pickups.push_back(cItem(E_ITEM_POISONOUS_POTATO, 1, 0));
+ }
+ break;
+ }
+ default:
+ {
+ ASSERT(!"Unhandled block type");
+ break;
+ }
+ } // switch (m_BlockType)
+ }
+ else
+ {
+ // Drop 1 item of whatever is growing
+ switch (m_BlockType)
+ {
+ case E_BLOCK_CROPS: a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1, 0)); break;
+ case E_BLOCK_CARROTS: a_Pickups.push_back(cItem(E_ITEM_CARROT, 1, 0)); break;
+ case E_BLOCK_POTATOES: a_Pickups.push_back(cItem(E_ITEM_POTATO, 1, 0)); break;
+ default:
+ {
+ ASSERT(!"Unhandled block type");
+ break;
+ }
+ }
+ }
+ }
+
+
+ void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ if (Meta < 7)
+ {
+ a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_CROPS, ++Meta);
+ }
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_FARMLAND));
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.grass";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockDeadBush.h b/source/Blocks/BlockDeadBush.h
index be0e77e55..379e8e5df 100644
--- a/source/Blocks/BlockDeadBush.h
+++ b/source/Blocks/BlockDeadBush.h
@@ -1,47 +1,47 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-#include "../World.h"
-
-
-
-
-
-class cBlockDeadBushHandler :
- public cBlockHandler
-{
-public:
- cBlockDeadBushHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- // Don't drop anything
- }
-
-
- virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
- {
- return (a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_SAND);
- }
-
-
- virtual bool DoesAllowBlockOnTop(void) override
- {
- return false;
- }
-
-
- virtual bool CanBePlacedOnSide() override
- {
- return false;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../World.h"
+
+
+
+
+
+class cBlockDeadBushHandler :
+ public cBlockHandler
+{
+public:
+ cBlockDeadBushHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Don't drop anything
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ return (a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_SAND);
+ }
+
+
+ virtual bool DoesAllowBlockOnTop(void) override
+ {
+ return false;
+ }
+
+
+ virtual bool CanBePlacedOnSide() override
+ {
+ return false;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockDirt.h b/source/Blocks/BlockDirt.h
index 6cec5d99e..b2bc4756c 100644
--- a/source/Blocks/BlockDirt.h
+++ b/source/Blocks/BlockDirt.h
@@ -1,88 +1,88 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-#include "../MersenneTwister.h"
-#include "../World.h"
-
-
-
-
-
-/// Handler used for both dirt and grass
-class cBlockDirtHandler :
- public cBlockHandler
-{
-public:
- cBlockDirtHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- a_Pickups.push_back(cItem(E_BLOCK_DIRT, 1, 0));
- }
-
-
- virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
- {
- if (m_BlockType != E_BLOCK_GRASS)
- {
- return;
- }
-
- // Grass becomes dirt if there is something on top of it:
- if (a_BlockY < cChunkDef::Height - 1)
- {
- BLOCKTYPE Above = a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ);
- if (!g_BlockTransparent[Above] && !g_BlockOneHitDig[Above])
- {
- a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DIRT, 0);
- return;
- }
- }
-
- // Grass spreads to adjacent blocks:
- MTRand rand;
- for (int i = 0; i < 2; i++) // Pick two blocks to grow to
- {
- int OfsX = rand.randInt(2) - 1; // [-1 .. 1]
- int OfsY = rand.randInt(4) - 3; // [-3 .. 1]
- int OfsZ = rand.randInt(2) - 1; // [-1 .. 1]
-
- BLOCKTYPE DestBlock;
- NIBBLETYPE DestMeta;
- if ((a_BlockY + OfsY < 0) || (a_BlockY + OfsY >= cChunkDef::Height - 1))
- {
- // Y Coord out of range
- continue;
- }
- bool IsValid = a_World->GetBlockTypeMeta(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ, DestBlock, DestMeta);
- if (!IsValid || (DestBlock != E_BLOCK_DIRT))
- {
- continue;
- }
-
- BLOCKTYPE AboveDest;
- NIBBLETYPE AboveMeta;
- IsValid = a_World->GetBlockTypeMeta(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ, AboveDest, AboveMeta);
- ASSERT(IsValid); // WTF - how did we get the DestBlock if AboveBlock is not valid?
- if (g_BlockOneHitDig[AboveDest] || g_BlockTransparent[AboveDest])
- {
- a_World->FastSetBlock(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ, E_BLOCK_GRASS, 0);
- }
- } // for i - repeat twice
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.gravel";
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../MersenneTwister.h"
+#include "../World.h"
+
+
+
+
+
+/// Handler used for both dirt and grass
+class cBlockDirtHandler :
+ public cBlockHandler
+{
+public:
+ cBlockDirtHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_BLOCK_DIRT, 1, 0));
+ }
+
+
+ virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ if (m_BlockType != E_BLOCK_GRASS)
+ {
+ return;
+ }
+
+ // Grass becomes dirt if there is something on top of it:
+ if (a_BlockY < cChunkDef::Height - 1)
+ {
+ BLOCKTYPE Above = a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ);
+ if (!g_BlockTransparent[Above] && !g_BlockOneHitDig[Above])
+ {
+ a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DIRT, 0);
+ return;
+ }
+ }
+
+ // Grass spreads to adjacent blocks:
+ MTRand rand;
+ for (int i = 0; i < 2; i++) // Pick two blocks to grow to
+ {
+ int OfsX = rand.randInt(2) - 1; // [-1 .. 1]
+ int OfsY = rand.randInt(4) - 3; // [-3 .. 1]
+ int OfsZ = rand.randInt(2) - 1; // [-1 .. 1]
+
+ BLOCKTYPE DestBlock;
+ NIBBLETYPE DestMeta;
+ if ((a_BlockY + OfsY < 0) || (a_BlockY + OfsY >= cChunkDef::Height - 1))
+ {
+ // Y Coord out of range
+ continue;
+ }
+ bool IsValid = a_World->GetBlockTypeMeta(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ, DestBlock, DestMeta);
+ if (!IsValid || (DestBlock != E_BLOCK_DIRT))
+ {
+ continue;
+ }
+
+ BLOCKTYPE AboveDest;
+ NIBBLETYPE AboveMeta;
+ IsValid = a_World->GetBlockTypeMeta(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ, AboveDest, AboveMeta);
+ ASSERT(IsValid); // WTF - how did we get the DestBlock if AboveBlock is not valid?
+ if (g_BlockOneHitDig[AboveDest] || g_BlockTransparent[AboveDest])
+ {
+ a_World->FastSetBlock(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ, E_BLOCK_GRASS, 0);
+ }
+ } // for i - repeat twice
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.gravel";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockDoor.cpp b/source/Blocks/BlockDoor.cpp
index 49ce56e07..b2fe273c5 100644
--- a/source/Blocks/BlockDoor.cpp
+++ b/source/Blocks/BlockDoor.cpp
@@ -1,88 +1,88 @@
-
-#include "Globals.h"
-#include "BlockDoor.h"
-#include "../Item.h"
-#include "../World.h"
-#include "../Doors.h"
-#include "../Player.h"
-
-
-
-
-
-cBlockDoorHandler::cBlockDoorHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
-{
-}
-
-
-
-
-
-void cBlockDoorHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
-{
- NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
-
- if (OldMeta & 8)
- {
- // Was upper part of door
- if (cDoors::IsDoor(a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)))
- {
- a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
- }
- }
- else
- {
- // Was lower part
- if (cDoors::IsDoor(a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)))
- {
- a_World->FastSetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_AIR, 0);
- }
- }
-}
-
-
-
-
-
-void cBlockDoorHandler::OnUse(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)
-{
- cDoors::ChangeDoor(a_World, a_BlockX, a_BlockY, a_BlockZ);
-}
-
-
-
-
-
-void cBlockDoorHandler::OnPlacedByPlayer(
- 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
-)
-{
- NIBBLETYPE a_TopBlockMeta = 8;
- if (
- (a_BlockMeta == 0) && (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1) == m_BlockType) ||
- (a_BlockMeta == 1) && (a_World->GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ) == m_BlockType) ||
- (a_BlockMeta == 2) && (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1) == m_BlockType) ||
- (a_BlockMeta == 3) && (a_World->GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ) == m_BlockType)
- )
- {
- a_TopBlockMeta = 9;
- }
- a_World->SetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, m_BlockType, a_TopBlockMeta);
-}
-
-
-
-
-
-const char * cBlockDoorHandler::GetStepSound(void)
-{
- return (m_BlockType == E_BLOCK_WOODEN_DOOR) ? "step.wood" : "step.stone";
-}
-
-
-
-
+
+#include "Globals.h"
+#include "BlockDoor.h"
+#include "../Item.h"
+#include "../World.h"
+#include "../Doors.h"
+#include "../Player.h"
+
+
+
+
+
+cBlockDoorHandler::cBlockDoorHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+{
+}
+
+
+
+
+
+void cBlockDoorHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+
+ if (OldMeta & 8)
+ {
+ // Was upper part of door
+ if (cDoors::IsDoor(a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)))
+ {
+ a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
+ }
+ }
+ else
+ {
+ // Was lower part
+ if (cDoors::IsDoor(a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)))
+ {
+ a_World->FastSetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_AIR, 0);
+ }
+ }
+}
+
+
+
+
+
+void cBlockDoorHandler::OnUse(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)
+{
+ cDoors::ChangeDoor(a_World, a_BlockX, a_BlockY, a_BlockZ);
+}
+
+
+
+
+
+void cBlockDoorHandler::OnPlacedByPlayer(
+ 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
+)
+{
+ NIBBLETYPE a_TopBlockMeta = 8;
+ if (
+ (a_BlockMeta == 0) && (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1) == m_BlockType) ||
+ (a_BlockMeta == 1) && (a_World->GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ) == m_BlockType) ||
+ (a_BlockMeta == 2) && (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1) == m_BlockType) ||
+ (a_BlockMeta == 3) && (a_World->GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ) == m_BlockType)
+ )
+ {
+ a_TopBlockMeta = 9;
+ }
+ a_World->SetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, m_BlockType, a_TopBlockMeta);
+}
+
+
+
+
+
+const char * cBlockDoorHandler::GetStepSound(void)
+{
+ return (m_BlockType == E_BLOCK_WOODEN_DOOR) ? "step.wood" : "step.stone";
+}
+
+
+
+
diff --git a/source/Blocks/BlockDoor.h b/source/Blocks/BlockDoor.h
index a988c2d4b..19701ffc9 100644
--- a/source/Blocks/BlockDoor.h
+++ b/source/Blocks/BlockDoor.h
@@ -1,105 +1,105 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-#include "../World.h"
-#include "../Doors.h"
-#include "../Player.h"
-
-
-
-
-
-class cBlockDoorHandler :
- public cBlockHandler
-{
-public:
- cBlockDoorHandler(BLOCKTYPE a_BlockType);
-
- virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override;
- virtual void OnUse(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) override;
- virtual const char * GetStepSound(void) override;
-
-
- 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
- {
- // If clicking a bottom face, place the door one block lower:
- if (a_BlockFace == BLOCK_FACE_BOTTOM)
- {
- a_BlockY--;
- }
-
- if (
- !CanReplaceBlock(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)) ||
- !CanReplaceBlock(a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ))
- )
- {
- return false;
- }
-
- a_BlockType = m_BlockType;
- a_BlockMeta = cDoors::RotationToMetaData(a_Player->GetRotation());
- return true;
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- a_Pickups.push_back(cItem((m_BlockType == E_BLOCK_WOODEN_DOOR) ? E_ITEM_WOODEN_DOOR : E_ITEM_IRON_DOOR, 1, 0));
- }
-
-
- virtual void OnPlacedByPlayer(
- 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;
-
-
- virtual bool IsUseable(void) override
- {
- return true;
- }
-
-
- virtual bool CanBePlacedOnSide(void) override
- {
- return false;
- }
-
-
- virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
- {
- return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR));
- }
-
-
- bool CanReplaceBlock(BLOCKTYPE a_BlockType)
- {
- switch (a_BlockType)
- {
- case E_BLOCK_AIR:
- case E_BLOCK_TALL_GRASS:
- case E_BLOCK_WATER:
- case E_BLOCK_STATIONARY_WATER:
- case E_BLOCK_LAVA:
- case E_BLOCK_STATIONARY_LAVA:
- case E_BLOCK_SNOW:
- case E_BLOCK_FIRE:
- {
- return true;
- }
- }
- return false;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../World.h"
+#include "../Doors.h"
+#include "../Player.h"
+
+
+
+
+
+class cBlockDoorHandler :
+ public cBlockHandler
+{
+public:
+ cBlockDoorHandler(BLOCKTYPE a_BlockType);
+
+ virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override;
+ virtual void OnUse(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) override;
+ virtual const char * GetStepSound(void) override;
+
+
+ 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
+ {
+ // If clicking a bottom face, place the door one block lower:
+ if (a_BlockFace == BLOCK_FACE_BOTTOM)
+ {
+ a_BlockY--;
+ }
+
+ if (
+ !CanReplaceBlock(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)) ||
+ !CanReplaceBlock(a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ))
+ )
+ {
+ return false;
+ }
+
+ a_BlockType = m_BlockType;
+ a_BlockMeta = cDoors::RotationToMetaData(a_Player->GetRotation());
+ return true;
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem((m_BlockType == E_BLOCK_WOODEN_DOOR) ? E_ITEM_WOODEN_DOOR : E_ITEM_IRON_DOOR, 1, 0));
+ }
+
+
+ virtual void OnPlacedByPlayer(
+ 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;
+
+
+ virtual bool IsUseable(void) override
+ {
+ return true;
+ }
+
+
+ virtual bool CanBePlacedOnSide(void) override
+ {
+ return false;
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR));
+ }
+
+
+ bool CanReplaceBlock(BLOCKTYPE a_BlockType)
+ {
+ switch (a_BlockType)
+ {
+ case E_BLOCK_AIR:
+ case E_BLOCK_TALL_GRASS:
+ case E_BLOCK_WATER:
+ case E_BLOCK_STATIONARY_WATER:
+ case E_BLOCK_LAVA:
+ case E_BLOCK_STATIONARY_LAVA:
+ case E_BLOCK_SNOW:
+ case E_BLOCK_FIRE:
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockDropSpenser.h b/source/Blocks/BlockDropSpenser.h
index bcc75758d..cfb607a7b 100644
--- a/source/Blocks/BlockDropSpenser.h
+++ b/source/Blocks/BlockDropSpenser.h
@@ -1,39 +1,39 @@
-
-// BlockDropSpenser.h
-
-// Declares the cBlockDropSpenserHandler class representing the BlockHandler for Dropper and Dispenser blocks
-
-#pragma once
-
-
-
-
-
-class cBlockDropSpenserHandler :
- public cBlockEntityHandler
-{
-public:
- cBlockDropSpenserHandler(BLOCKTYPE a_BlockType) :
- cBlockEntityHandler(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
- {
- a_BlockType = m_BlockType;
-
- // FIXME: Do not use cPiston class for dispenser placement!
- a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), 0);
- return true;
- }
-} ;
-
-
-
-
+
+// BlockDropSpenser.h
+
+// Declares the cBlockDropSpenserHandler class representing the BlockHandler for Dropper and Dispenser blocks
+
+#pragma once
+
+
+
+
+
+class cBlockDropSpenserHandler :
+ public cBlockEntityHandler
+{
+public:
+ cBlockDropSpenserHandler(BLOCKTYPE a_BlockType) :
+ cBlockEntityHandler(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
+ {
+ a_BlockType = m_BlockType;
+
+ // FIXME: Do not use cPiston class for dispenser placement!
+ a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), 0);
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockEnderchest.h b/source/Blocks/BlockEnderchest.h
index b40deb1f7..0ce813f1c 100644
--- a/source/Blocks/BlockEnderchest.h
+++ b/source/Blocks/BlockEnderchest.h
@@ -1,28 +1,28 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockEnderchestHandler :
- public cBlockHandler
-{
-public:
- cBlockEnderchestHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- //todo: Drop Ender Chest if using silk touch pickaxe
- a_Pickups.push_back(cItem(E_BLOCK_OBSIDIAN, 8, 0));
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockEnderchestHandler :
+ public cBlockHandler
+{
+public:
+ cBlockEnderchestHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ //todo: Drop Ender Chest if using silk touch pickaxe
+ a_Pickups.push_back(cItem(E_BLOCK_OBSIDIAN, 8, 0));
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockEntity.h b/source/Blocks/BlockEntity.h
index 8b82b43a6..9c6b23665 100644
--- a/source/Blocks/BlockEntity.h
+++ b/source/Blocks/BlockEntity.h
@@ -1,31 +1,31 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockEntityHandler : public cBlockHandler
-{
-public:
- cBlockEntityHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
- virtual void OnUse(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) override
- {
- a_World->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ);
- }
-
- virtual bool IsUseable() override
- {
- return true;
- }
-};
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockEntityHandler : public cBlockHandler
+{
+public:
+ cBlockEntityHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual void OnUse(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) override
+ {
+ a_World->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ);
+ }
+
+ virtual bool IsUseable() override
+ {
+ return true;
+ }
+};
+
+
+
+
diff --git a/source/Blocks/BlockFarmland.h b/source/Blocks/BlockFarmland.h
index fb2d91a86..6cab1fa38 100644
--- a/source/Blocks/BlockFarmland.h
+++ b/source/Blocks/BlockFarmland.h
@@ -1,99 +1,99 @@
-
-// BlockFarmland.h
-
-// Declares the cBlcokFarmlandHandler representing the block handler for farmland
-
-
-
-
-
-#pragma once
-
-#include "BlockHandler.h"
-#include "../BlockArea.h"
-
-
-
-
-
-class cBlockFarmlandHandler :
- public cBlockHandler
-{
- typedef cBlockHandler super;
-
-public:
- cBlockFarmlandHandler(void) :
- super(E_BLOCK_FARMLAND)
- {
- }
-
-
- virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
- {
- // TODO: Rain hydrates farmland, too. Check world weather, don't search for water if raining.
- // NOTE: The desert biomes do not get precipitation, so another check needs to be made.
-
- // Search for water in a close proximity:
- // Ref.: http://www.minecraftwiki.net/wiki/Farmland#Hydrated_Farmland_Tiles
- cBlockArea Area;
- if (!Area.Read(a_World, a_BlockX - 4, a_BlockX + 4, a_BlockY, a_BlockY + 1, a_BlockZ - 4, a_BlockZ + 4))
- {
- // Too close to the world edge, cannot check surroudnings; don't tick at all
- return;
- }
- bool Found = false;
- int NumBlocks = Area.GetBlockCount();
- BLOCKTYPE * BlockTypes = Area.GetBlockTypes();
- for (int i = 0; i < NumBlocks; i++)
- {
- if (
- (BlockTypes[i] == E_BLOCK_WATER) ||
- (BlockTypes[i] == E_BLOCK_STATIONARY_WATER)
- )
- {
- Found = true;
- break;
- }
- }
-
- NIBBLETYPE BlockMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
-
- if (Found)
- {
- // Water was found, hydrate the block until hydration reaches 7:
- if (BlockMeta < 7)
- {
- a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, ++BlockMeta);
- }
- return;
- }
-
- // Water wasn't found, de-hydrate block:
- if (BlockMeta > 0)
- {
- a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FARMLAND, --BlockMeta);
- return;
- }
-
- // Farmland too dry. If nothing is growing on top, turn back to dirt:
- switch (a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ))
- {
- case E_BLOCK_CROPS:
- case E_BLOCK_MELON_STEM:
- case E_BLOCK_PUMPKIN_STEM:
- {
- // Produce on top, don't revert
- break;
- }
- default:
- {
- a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DIRT, 0);
- break;
- }
- }
- }
-} ;
-
-
-
-
+
+// BlockFarmland.h
+
+// Declares the cBlcokFarmlandHandler representing the block handler for farmland
+
+
+
+
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../BlockArea.h"
+
+
+
+
+
+class cBlockFarmlandHandler :
+ public cBlockHandler
+{
+ typedef cBlockHandler super;
+
+public:
+ cBlockFarmlandHandler(void) :
+ super(E_BLOCK_FARMLAND)
+ {
+ }
+
+
+ virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ // TODO: Rain hydrates farmland, too. Check world weather, don't search for water if raining.
+ // NOTE: The desert biomes do not get precipitation, so another check needs to be made.
+
+ // Search for water in a close proximity:
+ // Ref.: http://www.minecraftwiki.net/wiki/Farmland#Hydrated_Farmland_Tiles
+ cBlockArea Area;
+ if (!Area.Read(a_World, a_BlockX - 4, a_BlockX + 4, a_BlockY, a_BlockY + 1, a_BlockZ - 4, a_BlockZ + 4))
+ {
+ // Too close to the world edge, cannot check surroudnings; don't tick at all
+ return;
+ }
+ bool Found = false;
+ int NumBlocks = Area.GetBlockCount();
+ BLOCKTYPE * BlockTypes = Area.GetBlockTypes();
+ for (int i = 0; i < NumBlocks; i++)
+ {
+ if (
+ (BlockTypes[i] == E_BLOCK_WATER) ||
+ (BlockTypes[i] == E_BLOCK_STATIONARY_WATER)
+ )
+ {
+ Found = true;
+ break;
+ }
+ }
+
+ NIBBLETYPE BlockMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+
+ if (Found)
+ {
+ // Water was found, hydrate the block until hydration reaches 7:
+ if (BlockMeta < 7)
+ {
+ a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, ++BlockMeta);
+ }
+ return;
+ }
+
+ // Water wasn't found, de-hydrate block:
+ if (BlockMeta > 0)
+ {
+ a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FARMLAND, --BlockMeta);
+ return;
+ }
+
+ // Farmland too dry. If nothing is growing on top, turn back to dirt:
+ switch (a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ))
+ {
+ case E_BLOCK_CROPS:
+ case E_BLOCK_MELON_STEM:
+ case E_BLOCK_PUMPKIN_STEM:
+ {
+ // Produce on top, don't revert
+ break;
+ }
+ default:
+ {
+ a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DIRT, 0);
+ break;
+ }
+ }
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockFenceGate.h b/source/Blocks/BlockFenceGate.h
index a84ce8303..d6f8aa85f 100644
--- a/source/Blocks/BlockFenceGate.h
+++ b/source/Blocks/BlockFenceGate.h
@@ -1,60 +1,60 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-#include "../Doors.h"
-
-
-
-
-
-class cBlockFenceGateHandler :
- public cBlockHandler
-{
-public:
- cBlockFenceGateHandler(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
- {
- a_BlockType = m_BlockType;
- a_BlockMeta = cDoors::RotationToMetaData(a_Player->GetRotation() + 270);
- return true;
- }
-
-
- virtual void OnUse(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) override
- {
- NIBBLETYPE OldMetaData = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
- NIBBLETYPE NewMetaData = cDoors::RotationToMetaData(a_Player->GetRotation() + 270);
- OldMetaData ^= 4; // Toggle the gate
- if ((OldMetaData & 1) == (NewMetaData & 1))
- {
- // Standing in front of the gate - apply new direction
- a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, (OldMetaData & 4) | (NewMetaData & 3));
- }
- else
- {
- // Standing aside - use last direction
- a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, OldMetaData);
- }
- }
-
-
- virtual bool IsUseable(void) override
- {
- return true;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../Doors.h"
+
+
+
+
+
+class cBlockFenceGateHandler :
+ public cBlockHandler
+{
+public:
+ cBlockFenceGateHandler(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
+ {
+ a_BlockType = m_BlockType;
+ a_BlockMeta = cDoors::RotationToMetaData(a_Player->GetRotation() + 270);
+ return true;
+ }
+
+
+ virtual void OnUse(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) override
+ {
+ NIBBLETYPE OldMetaData = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ NIBBLETYPE NewMetaData = cDoors::RotationToMetaData(a_Player->GetRotation() + 270);
+ OldMetaData ^= 4; // Toggle the gate
+ if ((OldMetaData & 1) == (NewMetaData & 1))
+ {
+ // Standing in front of the gate - apply new direction
+ a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, (OldMetaData & 4) | (NewMetaData & 3));
+ }
+ else
+ {
+ // Standing aside - use last direction
+ a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, OldMetaData);
+ }
+ }
+
+
+ virtual bool IsUseable(void) override
+ {
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockFire.h b/source/Blocks/BlockFire.h
index 7da66f982..d3ba499b1 100644
--- a/source/Blocks/BlockFire.h
+++ b/source/Blocks/BlockFire.h
@@ -1,42 +1,42 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockFireHandler :
- public cBlockHandler
-{
-public:
- cBlockFireHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
- 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);
- }
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- // No pickups from this block
- }
-
- virtual bool IsClickedThrough(void) override
- {
- return true;
- }
-
- virtual const char * GetStepSound(void) override
- {
- return "step.wood";
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockFireHandler :
+ public cBlockHandler
+{
+public:
+ cBlockFireHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ 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);
+ }
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // No pickups from this block
+ }
+
+ virtual bool IsClickedThrough(void) override
+ {
+ return true;
+ }
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockFlower.h b/source/Blocks/BlockFlower.h
index b46273c51..202609538 100644
--- a/source/Blocks/BlockFlower.h
+++ b/source/Blocks/BlockFlower.h
@@ -1,53 +1,53 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockFlowerHandler :
- public cBlockHandler
-{
-public:
- cBlockFlowerHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- // Reset meta to 0
- a_Pickups.push_back(cItem(m_BlockType, 1, 0));
- }
-
-
- virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
- {
- return (a_RelY > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ));
- }
-
-
- virtual bool DoesAllowBlockOnTop(void) override
- {
- return false;
- }
-
-
- virtual bool CanBePlacedOnSide(void) override
- {
- return false;
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.grass";
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockFlowerHandler :
+ public cBlockHandler
+{
+public:
+ cBlockFlowerHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Reset meta to 0
+ a_Pickups.push_back(cItem(m_BlockType, 1, 0));
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ return (a_RelY > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ));
+ }
+
+
+ virtual bool DoesAllowBlockOnTop(void) override
+ {
+ return false;
+ }
+
+
+ virtual bool CanBePlacedOnSide(void) override
+ {
+ return false;
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.grass";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockFlowerPot.h b/source/Blocks/BlockFlowerPot.h
index 12cd594de..b0faf5218 100644
--- a/source/Blocks/BlockFlowerPot.h
+++ b/source/Blocks/BlockFlowerPot.h
@@ -1,105 +1,105 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockFlowerPotHandler :
- public cBlockHandler
-{
-public:
- cBlockFlowerPotHandler(BLOCKTYPE a_BlockType) :
- cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- a_Pickups.push_back(cItem(E_ITEM_FLOWER_POT, 1, 0));
- if (a_BlockMeta == 0)
- {
- return;
- }
- cItem Plant;
- switch (a_BlockMeta)
- {
- case 1: Plant = cItem(E_BLOCK_RED_ROSE, 1, 0); break;
- case 2: Plant = cItem(E_BLOCK_YELLOW_FLOWER, 1, 0); break;
- case 3: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_APPLE); break;
- case 4: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_CONIFER); break;
- case 5: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_BIRCH); break;
- case 6: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_JUNGLE); break;
- case 7: Plant = cItem(E_BLOCK_RED_MUSHROOM, 1, 0); break;
- case 8: Plant = cItem(E_BLOCK_BROWN_MUSHROOM, 1, 0); break;
- case 9: Plant = cItem(E_BLOCK_CACTUS, 1, 0); break;
- case 10: Plant = cItem(E_BLOCK_DEAD_BUSH, 1, 0); break;
- case 11: Plant = cItem(E_BLOCK_TALL_GRASS, 1, E_META_TALL_GRASS_FERN); break;
- default: return;
- }
- a_Pickups.push_back(Plant);
- }
-
-
- void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
- {
- NIBBLETYPE Meta = a_World->GetBlockMeta( a_BlockX, a_BlockY, a_BlockZ );
- if (Meta != 0)
- {
- // Already filled
- return;
- }
-
- switch (a_Player->GetEquippedItem().m_ItemType)
- {
- case E_BLOCK_RED_ROSE: Meta = 1; break;
- case E_BLOCK_YELLOW_FLOWER: Meta = 2; break;
- case E_BLOCK_SAPLING:
- {
- switch (a_Player->GetEquippedItem().m_ItemDamage)
- {
- case E_META_SAPLING_APPLE: Meta = 3; break;
- case E_META_SAPLING_CONIFER: Meta = 4; break;
- case E_META_SAPLING_BIRCH: Meta = 5; break;
- case E_META_SAPLING_JUNGLE: Meta = 6; break;
- }
- break;
- }
- case E_BLOCK_RED_MUSHROOM: Meta = 7; break;
- case E_BLOCK_BROWN_MUSHROOM: Meta = 8; break;
- case E_BLOCK_CACTUS: Meta = 9; break;
- case E_BLOCK_DEAD_BUSH: Meta = 10; break;
- case E_BLOCK_TALL_GRASS:
- {
- if (a_Player->GetEquippedItem().m_ItemDamage == E_META_TALL_GRASS_FERN)
- {
- Meta = 11;
- }
- else
- {
- return;
- }
- break;
- }
- }
-
- if (a_Player->GetGameMode() != gmCreative)
- {
- a_Player->GetInventory().RemoveOneEquippedItem();
- }
- a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta);
- }
-
-
- virtual bool IsUseable(void) override
- {
- return true;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockFlowerPotHandler :
+ public cBlockHandler
+{
+public:
+ cBlockFlowerPotHandler(BLOCKTYPE a_BlockType) :
+ cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_ITEM_FLOWER_POT, 1, 0));
+ if (a_BlockMeta == 0)
+ {
+ return;
+ }
+ cItem Plant;
+ switch (a_BlockMeta)
+ {
+ case 1: Plant = cItem(E_BLOCK_RED_ROSE, 1, 0); break;
+ case 2: Plant = cItem(E_BLOCK_YELLOW_FLOWER, 1, 0); break;
+ case 3: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_APPLE); break;
+ case 4: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_CONIFER); break;
+ case 5: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_BIRCH); break;
+ case 6: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_JUNGLE); break;
+ case 7: Plant = cItem(E_BLOCK_RED_MUSHROOM, 1, 0); break;
+ case 8: Plant = cItem(E_BLOCK_BROWN_MUSHROOM, 1, 0); break;
+ case 9: Plant = cItem(E_BLOCK_CACTUS, 1, 0); break;
+ case 10: Plant = cItem(E_BLOCK_DEAD_BUSH, 1, 0); break;
+ case 11: Plant = cItem(E_BLOCK_TALL_GRASS, 1, E_META_TALL_GRASS_FERN); break;
+ default: return;
+ }
+ a_Pickups.push_back(Plant);
+ }
+
+
+ void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ NIBBLETYPE Meta = a_World->GetBlockMeta( a_BlockX, a_BlockY, a_BlockZ );
+ if (Meta != 0)
+ {
+ // Already filled
+ return;
+ }
+
+ switch (a_Player->GetEquippedItem().m_ItemType)
+ {
+ case E_BLOCK_RED_ROSE: Meta = 1; break;
+ case E_BLOCK_YELLOW_FLOWER: Meta = 2; break;
+ case E_BLOCK_SAPLING:
+ {
+ switch (a_Player->GetEquippedItem().m_ItemDamage)
+ {
+ case E_META_SAPLING_APPLE: Meta = 3; break;
+ case E_META_SAPLING_CONIFER: Meta = 4; break;
+ case E_META_SAPLING_BIRCH: Meta = 5; break;
+ case E_META_SAPLING_JUNGLE: Meta = 6; break;
+ }
+ break;
+ }
+ case E_BLOCK_RED_MUSHROOM: Meta = 7; break;
+ case E_BLOCK_BROWN_MUSHROOM: Meta = 8; break;
+ case E_BLOCK_CACTUS: Meta = 9; break;
+ case E_BLOCK_DEAD_BUSH: Meta = 10; break;
+ case E_BLOCK_TALL_GRASS:
+ {
+ if (a_Player->GetEquippedItem().m_ItemDamage == E_META_TALL_GRASS_FERN)
+ {
+ Meta = 11;
+ }
+ else
+ {
+ return;
+ }
+ break;
+ }
+ }
+
+ if (a_Player->GetGameMode() != gmCreative)
+ {
+ a_Player->GetInventory().RemoveOneEquippedItem();
+ }
+ a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta);
+ }
+
+
+ virtual bool IsUseable(void) override
+ {
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockFluid.h b/source/Blocks/BlockFluid.h
index b184a5b33..696bfb3ce 100644
--- a/source/Blocks/BlockFluid.h
+++ b/source/Blocks/BlockFluid.h
@@ -1,50 +1,50 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockFluidHandler :
- public cBlockHandler
-{
- typedef cBlockHandler super;
-
-public:
- cBlockFluidHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
-
- }
-
-
- virtual bool DoesIgnoreBuildCollision(void) override
- {
- return true;
- }
-
-
- virtual void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) override
- {
- switch (m_BlockType)
- {
- case E_BLOCK_STATIONARY_LAVA:
- {
- a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_LAVA, a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ));
- break;
- }
- case E_BLOCK_STATIONARY_WATER:
- {
- a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_WATER, a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ));
- break;
- }
- }
- super::Check(a_RelX, a_RelY, a_RelZ, a_Chunk);
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockFluidHandler :
+ public cBlockHandler
+{
+ typedef cBlockHandler super;
+
+public:
+ cBlockFluidHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+
+ }
+
+
+ virtual bool DoesIgnoreBuildCollision(void) override
+ {
+ return true;
+ }
+
+
+ virtual void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) override
+ {
+ switch (m_BlockType)
+ {
+ case E_BLOCK_STATIONARY_LAVA:
+ {
+ a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_LAVA, a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ));
+ break;
+ }
+ case E_BLOCK_STATIONARY_WATER:
+ {
+ a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_WATER, a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ));
+ break;
+ }
+ }
+ super::Check(a_RelX, a_RelY, a_RelZ, a_Chunk);
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockFurnace.h b/source/Blocks/BlockFurnace.h
index 7231c4d65..b358a1d71 100644
--- a/source/Blocks/BlockFurnace.h
+++ b/source/Blocks/BlockFurnace.h
@@ -1,47 +1,47 @@
-
-#pragma once
-
-#include "BlockEntity.h"
-#include "../World.h"
-#include "../Piston.h"
-#include "../Player.h"
-
-
-
-
-
-class cBlockFurnaceHandler :
- public cBlockEntityHandler
-{
-public:
- cBlockFurnaceHandler(BLOCKTYPE a_BlockType) :
- cBlockEntityHandler(a_BlockType)
- {
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- a_Pickups.push_back(cItem(E_BLOCK_FURNACE, 1, 0));
- }
-
-
- 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
- {
- a_BlockType = m_BlockType;
-
- // FIXME: Do not use cPiston class for furnace placement!
- a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), 0);
-
- return true;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockEntity.h"
+#include "../World.h"
+#include "../Piston.h"
+#include "../Player.h"
+
+
+
+
+
+class cBlockFurnaceHandler :
+ public cBlockEntityHandler
+{
+public:
+ cBlockFurnaceHandler(BLOCKTYPE a_BlockType) :
+ cBlockEntityHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_BLOCK_FURNACE, 1, 0));
+ }
+
+
+ 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
+ {
+ a_BlockType = m_BlockType;
+
+ // FIXME: Do not use cPiston class for furnace placement!
+ a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), 0);
+
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockGlass.h b/source/Blocks/BlockGlass.h
index d5147af96..f6958bbb6 100644
--- a/source/Blocks/BlockGlass.h
+++ b/source/Blocks/BlockGlass.h
@@ -1,26 +1,26 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockGlassHandler :
- public cBlockHandler
-{
-public:
- cBlockGlassHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockGlassHandler :
+ public cBlockHandler
+{
+public:
+ cBlockGlassHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockGlowstone.h b/source/Blocks/BlockGlowstone.h
index 5ff9394b7..5f0d95dee 100644
--- a/source/Blocks/BlockGlowstone.h
+++ b/source/Blocks/BlockGlowstone.h
@@ -1,30 +1,30 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockGlowstoneHandler :
- public cBlockHandler
-{
-public:
- cBlockGlowstoneHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- // Reset meta to 0
- // TODO: More drops?
- a_Pickups.push_back(cItem(E_ITEM_GLOWSTONE_DUST, 1, 0));
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockGlowstoneHandler :
+ public cBlockHandler
+{
+public:
+ cBlockGlowstoneHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Reset meta to 0
+ // TODO: More drops?
+ a_Pickups.push_back(cItem(E_ITEM_GLOWSTONE_DUST, 1, 0));
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockGravel.h b/source/Blocks/BlockGravel.h
index 6324fe06f..e1c9ff390 100644
--- a/source/Blocks/BlockGravel.h
+++ b/source/Blocks/BlockGravel.h
@@ -1,27 +1,27 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockGravelHandler :
- public cBlockHandler
-{
-public:
- cBlockGravelHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
- virtual const char * GetStepSound(void) override
- {
- return "step.gravel";
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockGravelHandler :
+ public cBlockHandler
+{
+public:
+ cBlockGravelHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.gravel";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockHandler.cpp b/source/Blocks/BlockHandler.cpp
index a853c6f68..550a6795c 100644
--- a/source/Blocks/BlockHandler.cpp
+++ b/source/Blocks/BlockHandler.cpp
@@ -1,453 +1,453 @@
-
-#include "Globals.h"
-#include "BlockHandler.h"
-#include "../Item.h"
-#include "../World.h"
-#include "../Root.h"
-#include "../PluginManager.h"
-#include "BlockSand.h"
-#include "BlockGravel.h"
-#include "BlockDoor.h"
-#include "BlockFire.h"
-#include "BlockRedstone.h"
-#include "BlockRedstoneTorch.h"
-#include "BlockRedstoneRepeater.h"
-#include "BlockPiston.h"
-#include "BlockWorkbench.h"
-#include "BlockEntity.h"
-#include "BlockVine.h"
-#include "BlockTallGrass.h"
-#include "BlockSnow.h"
-#include "BlockCloth.h"
-#include "BlockSlab.h"
-#include "BlockDirt.h"
-#include "BlockTorch.h"
-#include "BlockWood.h"
-#include "BlockLeaves.h"
-#include "BlockSapling.h"
-#include "BlockFluid.h"
-#include "BlockChest.h"
-#include "BlockFurnace.h"
-#include "BlockDropSpenser.h"
-#include "BlockStairs.h"
-#include "BlockLadder.h"
-#include "BlockLever.h"
-#include "BlockSign.h"
-#include "BlockCrops.h"
-#include "BlockSugarcane.h"
-#include "BlockFlower.h"
-#include "BlockMushroom.h"
-#include "BlockCactus.h"
-#include "BlockStems.h"
-#include "BlockGlowstone.h"
-#include "BlockStone.h"
-#include "BlockMelon.h"
-#include "BlockIce.h"
-#include "BlockOre.h"
-#include "BlockNote.h"
-#include "BlockBed.h"
-#include "BlockFarmland.h"
-#include "BlockMycelium.h"
-#include "BlockRail.h"
-#include "BlockGlass.h"
-#include "BlockEnderchest.h"
-#include "BlockFenceGate.h"
-#include "BlockFlowerPot.h"
-#include "BlockCauldron.h"
-#include "BlockBrewingStand.h"
-#include "BlockCobWeb.h"
-#include "BlockDeadBush.h"
-#include "BlockHopper.h"
-
-
-
-
-
-bool cBlockHandler::m_HandlerInitialized = false;
-cBlockHandler * cBlockHandler::m_BlockHandler[256];
-
-
-
-
-
-cBlockHandler * cBlockHandler::GetBlockHandler(BLOCKTYPE a_BlockType)
-{
- if (!m_HandlerInitialized)
- {
- // We have to initialize
- memset(m_BlockHandler, 0, sizeof(m_BlockHandler));
- m_HandlerInitialized = true;
- }
- if (m_BlockHandler[a_BlockType] != NULL)
- {
- return m_BlockHandler[a_BlockType];
- }
-
- return m_BlockHandler[a_BlockType] = CreateBlockHandler(a_BlockType);
-}
-
-
-
-
-
-cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType)
-{
- switch(a_BlockType)
- {
- // Block handlers, alphabetically sorted:
- case E_BLOCK_BED: return new cBlockBedHandler (a_BlockType);
- case E_BLOCK_BIRCH_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType);
- case E_BLOCK_BREWING_STAND: return new cBlockBrewingStandHandler (a_BlockType);
- case E_BLOCK_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType);
- case E_BLOCK_BROWN_MUSHROOM: return new cBlockMushroomHandler (a_BlockType);
- case E_BLOCK_CACTUS: return new cBlockCactusHandler (a_BlockType);
- case E_BLOCK_CARROTS: return new cBlockCropsHandler (a_BlockType);
- case E_BLOCK_CAULDRON: return new cBlockCauldronHandler (a_BlockType);
- case E_BLOCK_CHEST: return new cBlockChestHandler (a_BlockType);
- case E_BLOCK_COAL_ORE: return new cBlockOreHandler (a_BlockType);
- case E_BLOCK_COBBLESTONE: return new cBlockStoneHandler (a_BlockType);
- case E_BLOCK_COBBLESTONE_STAIRS: return new cBlockStairsHandler (a_BlockType);
- case E_BLOCK_COBWEB: return new cBlockCobWebHandler (a_BlockType);
- case E_BLOCK_CROPS: return new cBlockCropsHandler (a_BlockType);
- case E_BLOCK_DEAD_BUSH: return new cBlockDeadBushHandler (a_BlockType);
- case E_BLOCK_DETECTOR_RAIL: return new cBlockRailHandler (a_BlockType);
- case E_BLOCK_DIAMOND_ORE: return new cBlockOreHandler (a_BlockType);
- case E_BLOCK_DIRT: return new cBlockDirtHandler (a_BlockType);
- case E_BLOCK_DISPENSER: return new cBlockDropSpenserHandler (a_BlockType);
- case E_BLOCK_DOUBLE_STONE_SLAB: return new cBlockSlabHandler (a_BlockType);
- case E_BLOCK_DOUBLE_WOODEN_SLAB: return new cBlockSlabHandler (a_BlockType);
- case E_BLOCK_DROPPER: return new cBlockDropSpenserHandler (a_BlockType);
- case E_BLOCK_EMERALD_ORE: return new cBlockOreHandler (a_BlockType);
- case E_BLOCK_ENDER_CHEST: return new cBlockEnderchestHandler (a_BlockType);
- case E_BLOCK_FARMLAND: return new cBlockFarmlandHandler;
- case E_BLOCK_FENCE_GATE: return new cBlockFenceGateHandler (a_BlockType);
- case E_BLOCK_FIRE: return new cBlockFireHandler (a_BlockType);
- case E_BLOCK_FLOWER_POT: return new cBlockFlowerPotHandler (a_BlockType);
- case E_BLOCK_FURNACE: return new cBlockFurnaceHandler (a_BlockType);
- case E_BLOCK_GLOWSTONE: return new cBlockGlowstoneHandler (a_BlockType);
- case E_BLOCK_GOLD_ORE: return new cBlockOreHandler (a_BlockType);
- case E_BLOCK_GLASS: return new cBlockGlassHandler (a_BlockType);
- case E_BLOCK_GRASS: return new cBlockDirtHandler (a_BlockType);
- case E_BLOCK_GRAVEL: return new cBlockGravelHandler (a_BlockType);
- case E_BLOCK_HOPPER: return new cBlockHopperHandler (a_BlockType);
- case E_BLOCK_ICE: return new cBlockIceHandler (a_BlockType);
- case E_BLOCK_IRON_DOOR: return new cBlockDoorHandler (a_BlockType);
- case E_BLOCK_IRON_ORE: return new cBlockOreHandler (a_BlockType);
- case E_BLOCK_JUKEBOX: return new cBlockEntityHandler (a_BlockType);
- case E_BLOCK_JUNGLE_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType);
- case E_BLOCK_LADDER: return new cBlockLadderHandler (a_BlockType);
- case E_BLOCK_LEVER: return new cBlockLeverHandler (a_BlockType);
- case E_BLOCK_LAPIS_ORE: return new cBlockOreHandler (a_BlockType);
- case E_BLOCK_LAVA: return new cBlockFluidHandler (a_BlockType);
- case E_BLOCK_LEAVES: return new cBlockLeavesHandler (a_BlockType);
- case E_BLOCK_LIT_FURNACE: return new cBlockFurnaceHandler (a_BlockType);
- case E_BLOCK_LOG: return new cBlockWoodHandler (a_BlockType);
- case E_BLOCK_MELON: return new cBlockMelonHandler (a_BlockType);
- case E_BLOCK_MELON_STEM: return new cBlockStemsHandler (a_BlockType);
- case E_BLOCK_MYCELIUM: return new cBlockMyceliumHandler (a_BlockType);
- case E_BLOCK_NETHER_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType);
- case E_BLOCK_NOTE_BLOCK: return new cBlockNoteHandler (a_BlockType);
- case E_BLOCK_PISTON: return new cBlockPistonHandler (a_BlockType);
- case E_BLOCK_PLANKS: return new cBlockWoodHandler (a_BlockType);
- case E_BLOCK_PUMPKIN_STEM: return new cBlockStemsHandler (a_BlockType);
- case E_BLOCK_RAIL: return new cBlockRailHandler (a_BlockType);
- case E_BLOCK_POTATOES: return new cBlockCropsHandler (a_BlockType);
- case E_BLOCK_POWERED_RAIL: return new cBlockRailHandler (a_BlockType);
- case E_BLOCK_REDSTONE_ORE: return new cBlockOreHandler (a_BlockType);
- case E_BLOCK_REDSTONE_ORE_GLOWING: return new cBlockOreHandler (a_BlockType);
- case E_BLOCK_REDSTONE_REPEATER_OFF: return new cBlockRedstoneRepeaterHandler(a_BlockType);
- case E_BLOCK_REDSTONE_REPEATER_ON: return new cBlockRedstoneRepeaterHandler(a_BlockType);
- case E_BLOCK_REDSTONE_TORCH_OFF: return new cBlockRedstoneTorchHandler (a_BlockType);
- case E_BLOCK_REDSTONE_TORCH_ON: return new cBlockRedstoneTorchHandler (a_BlockType);
- case E_BLOCK_REDSTONE_WIRE: return new cBlockRedstoneHandler (a_BlockType);
- case E_BLOCK_RED_MUSHROOM: return new cBlockMushroomHandler (a_BlockType);
- case E_BLOCK_RED_ROSE: return new cBlockFlowerHandler (a_BlockType);
- case E_BLOCK_SAND: return new cBlockSandHandler (a_BlockType);
- case E_BLOCK_SANDSTONE_STAIRS: return new cBlockStairsHandler (a_BlockType);
- case E_BLOCK_SAPLING: return new cBlockSaplingHandler (a_BlockType);
- case E_BLOCK_SIGN_POST: return new cBlockSignHandler (a_BlockType);
- case E_BLOCK_SNOW: return new cBlockSnowHandler (a_BlockType);
- case E_BLOCK_SPRUCE_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType);
- case E_BLOCK_STATIONARY_LAVA: return new cBlockFluidHandler (a_BlockType);
- case E_BLOCK_STATIONARY_WATER: return new cBlockFluidHandler (a_BlockType);
- case E_BLOCK_STICKY_PISTON: return new cBlockPistonHandler (a_BlockType);
- case E_BLOCK_STONE: return new cBlockStoneHandler (a_BlockType);
- case E_BLOCK_STONE_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType);
- case E_BLOCK_STONE_SLAB: return new cBlockSlabHandler (a_BlockType);
- case E_BLOCK_SUGARCANE: return new cBlockSugarcaneHandler (a_BlockType);
- case E_BLOCK_TALL_GRASS: return new cBlockTallGrassHandler (a_BlockType);
- case E_BLOCK_TORCH: return new cBlockTorchHandler (a_BlockType);
- case E_BLOCK_VINES: return new cBlockVineHandler (a_BlockType);
- case E_BLOCK_WALLSIGN: return new cBlockSignHandler (a_BlockType);
- case E_BLOCK_WATER: return new cBlockFluidHandler (a_BlockType);
- case E_BLOCK_WOODEN_DOOR: return new cBlockDoorHandler (a_BlockType);
- case E_BLOCK_WOODEN_SLAB: return new cBlockSlabHandler (a_BlockType);
- case E_BLOCK_WOODEN_STAIRS: return new cBlockStairsHandler (a_BlockType);
- case E_BLOCK_WOOL: return new cBlockClothHandler (a_BlockType);
- case E_BLOCK_WORKBENCH: return new cBlockWorkbenchHandler (a_BlockType);
- case E_BLOCK_YELLOW_FLOWER: return new cBlockFlowerHandler (a_BlockType);
-
- default: return new cBlockHandler(a_BlockType);
- }
-}
-
-
-
-
-
-void cBlockHandler::Deinit()
-{
- for (int i = 0; i < 256; i++)
- {
- delete m_BlockHandler[i];
- }
- memset(m_BlockHandler, 0, sizeof(m_BlockHandler)); // Don't leave any dangling pointers around, just in case
- m_HandlerInitialized = false;
-}
-
-
-
-
-
-cBlockHandler::cBlockHandler(BLOCKTYPE a_BlockType)
-{
- m_BlockType = a_BlockType;
-}
-
-
-
-
-
-bool cBlockHandler::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
-)
-{
- // By default, all blocks can be placed and the meta is copied over from the item's damage value:
- a_BlockType = m_BlockType;
- a_BlockMeta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x0f);
- return true;
-}
-
-
-
-
-
-void cBlockHandler::OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
-{
-}
-
-
-
-
-
-void cBlockHandler::OnPlacedByPlayer(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)
-{
-}
-
-
-
-
-
-void cBlockHandler::OnDestroyedByPlayer(cWorld *a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
-{
-}
-
-
-
-
-
-void cBlockHandler::OnPlaced(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
-{
- // Notify the neighbors
- NeighborChanged(a_World, a_BlockX - 1, a_BlockY, a_BlockZ);
- NeighborChanged(a_World, a_BlockX + 1, a_BlockY, a_BlockZ);
- NeighborChanged(a_World, a_BlockX, a_BlockY - 1, a_BlockZ);
- NeighborChanged(a_World, a_BlockX, a_BlockY + 1, a_BlockZ);
- NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ - 1);
- NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ + 1);
-}
-
-
-
-
-
-void cBlockHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
-{
- // Notify the neighbors
- NeighborChanged(a_World, a_BlockX - 1, a_BlockY, a_BlockZ);
- NeighborChanged(a_World, a_BlockX + 1, a_BlockY, a_BlockZ);
- NeighborChanged(a_World, a_BlockX, a_BlockY - 1, a_BlockZ);
- NeighborChanged(a_World, a_BlockX, a_BlockY + 1, a_BlockZ);
- NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ - 1);
- NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ + 1);
-}
-
-
-
-
-
-void cBlockHandler::NeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
-{
- if ((a_BlockY >= 0) && (a_BlockY < cChunkDef::Height))
- {
- GetBlockHandler(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ))->OnNeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ);
- }
-}
-
-
-
-
-
-void cBlockHandler::OnNeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
-{
-}
-
-
-
-
-
-void cBlockHandler::OnDigging(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
-{
-}
-
-
-
-
-
-void cBlockHandler::OnUse(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)
-{
-}
-
-
-
-
-
-void cBlockHandler::ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta)
-{
- // Setting the meta to a_BlockMeta keeps most textures. The few other blocks have to override this.
- a_Pickups.push_back(cItem(m_BlockType, 1, a_BlockMeta));
-}
-
-
-
-
-
-void cBlockHandler::DropBlock(cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ)
-{
- cItems Pickups;
- NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
- ConvertToPickups(Pickups, Meta);
-
- // Allow plugins to modify the pickups:
- cRoot::Get()->GetPluginManager()->CallHookBlockToPickups(a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta, Pickups);
-
- if (!Pickups.empty())
- {
- a_World->SpawnItemPickups(Pickups, a_BlockX, a_BlockY, a_BlockZ);
- }
-}
-
-
-
-
-
-const char * cBlockHandler::GetStepSound()
-{
- return "step.stone";
-}
-
-
-
-
-
-bool cBlockHandler::CanBeAt(int a_BlockX, int a_BlockY, int a_BlockZ, const cChunk & a_Chunk)
-{
- return true;
-}
-
-
-
-
-
-bool cBlockHandler::IsUseable()
-{
- return false;
-}
-
-
-
-
-
-bool cBlockHandler::IsClickedThrough(void)
-{
- return false;
-}
-
-
-
-
-
-bool cBlockHandler::DoesIgnoreBuildCollision(void)
-{
- return (m_BlockType == E_BLOCK_AIR);
-}
-
-
-
-
-
-bool cBlockHandler::DoesAllowBlockOnTop(void)
-{
- return true;
-}
-
-
-
-
-
-bool cBlockHandler::CanBePlacedOnSide(void)
-{
- return true;
-}
-
-
-
-
-
-bool cBlockHandler::DoesDropOnUnsuitable(void)
-{
- return true;
-}
-
-
-
-
-
-void cBlockHandler::Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk)
-{
- if (!CanBeAt(a_RelX, a_RelY, a_RelZ, a_Chunk))
- {
- if (DoesDropOnUnsuitable())
- {
- int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
- int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
- DropBlock(a_Chunk.GetWorld(), NULL, BlockX, a_RelY, BlockZ);
- }
-
- a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0);
- }
- else
- {
- // Wake up the simulators for this block:
- int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
- int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
- a_Chunk.GetWorld()->GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, &a_Chunk);
- }
-}
-
-
-
-
+
+#include "Globals.h"
+#include "BlockHandler.h"
+#include "../Item.h"
+#include "../World.h"
+#include "../Root.h"
+#include "../PluginManager.h"
+#include "BlockSand.h"
+#include "BlockGravel.h"
+#include "BlockDoor.h"
+#include "BlockFire.h"
+#include "BlockRedstone.h"
+#include "BlockRedstoneTorch.h"
+#include "BlockRedstoneRepeater.h"
+#include "BlockPiston.h"
+#include "BlockWorkbench.h"
+#include "BlockEntity.h"
+#include "BlockVine.h"
+#include "BlockTallGrass.h"
+#include "BlockSnow.h"
+#include "BlockCloth.h"
+#include "BlockSlab.h"
+#include "BlockDirt.h"
+#include "BlockTorch.h"
+#include "BlockWood.h"
+#include "BlockLeaves.h"
+#include "BlockSapling.h"
+#include "BlockFluid.h"
+#include "BlockChest.h"
+#include "BlockFurnace.h"
+#include "BlockDropSpenser.h"
+#include "BlockStairs.h"
+#include "BlockLadder.h"
+#include "BlockLever.h"
+#include "BlockSign.h"
+#include "BlockCrops.h"
+#include "BlockSugarcane.h"
+#include "BlockFlower.h"
+#include "BlockMushroom.h"
+#include "BlockCactus.h"
+#include "BlockStems.h"
+#include "BlockGlowstone.h"
+#include "BlockStone.h"
+#include "BlockMelon.h"
+#include "BlockIce.h"
+#include "BlockOre.h"
+#include "BlockNote.h"
+#include "BlockBed.h"
+#include "BlockFarmland.h"
+#include "BlockMycelium.h"
+#include "BlockRail.h"
+#include "BlockGlass.h"
+#include "BlockEnderchest.h"
+#include "BlockFenceGate.h"
+#include "BlockFlowerPot.h"
+#include "BlockCauldron.h"
+#include "BlockBrewingStand.h"
+#include "BlockCobWeb.h"
+#include "BlockDeadBush.h"
+#include "BlockHopper.h"
+
+
+
+
+
+bool cBlockHandler::m_HandlerInitialized = false;
+cBlockHandler * cBlockHandler::m_BlockHandler[256];
+
+
+
+
+
+cBlockHandler * cBlockHandler::GetBlockHandler(BLOCKTYPE a_BlockType)
+{
+ if (!m_HandlerInitialized)
+ {
+ // We have to initialize
+ memset(m_BlockHandler, 0, sizeof(m_BlockHandler));
+ m_HandlerInitialized = true;
+ }
+ if (m_BlockHandler[a_BlockType] != NULL)
+ {
+ return m_BlockHandler[a_BlockType];
+ }
+
+ return m_BlockHandler[a_BlockType] = CreateBlockHandler(a_BlockType);
+}
+
+
+
+
+
+cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType)
+{
+ switch(a_BlockType)
+ {
+ // Block handlers, alphabetically sorted:
+ case E_BLOCK_BED: return new cBlockBedHandler (a_BlockType);
+ case E_BLOCK_BIRCH_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType);
+ case E_BLOCK_BREWING_STAND: return new cBlockBrewingStandHandler (a_BlockType);
+ case E_BLOCK_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType);
+ case E_BLOCK_BROWN_MUSHROOM: return new cBlockMushroomHandler (a_BlockType);
+ case E_BLOCK_CACTUS: return new cBlockCactusHandler (a_BlockType);
+ case E_BLOCK_CARROTS: return new cBlockCropsHandler (a_BlockType);
+ case E_BLOCK_CAULDRON: return new cBlockCauldronHandler (a_BlockType);
+ case E_BLOCK_CHEST: return new cBlockChestHandler (a_BlockType);
+ case E_BLOCK_COAL_ORE: return new cBlockOreHandler (a_BlockType);
+ case E_BLOCK_COBBLESTONE: return new cBlockStoneHandler (a_BlockType);
+ case E_BLOCK_COBBLESTONE_STAIRS: return new cBlockStairsHandler (a_BlockType);
+ case E_BLOCK_COBWEB: return new cBlockCobWebHandler (a_BlockType);
+ case E_BLOCK_CROPS: return new cBlockCropsHandler (a_BlockType);
+ case E_BLOCK_DEAD_BUSH: return new cBlockDeadBushHandler (a_BlockType);
+ case E_BLOCK_DETECTOR_RAIL: return new cBlockRailHandler (a_BlockType);
+ case E_BLOCK_DIAMOND_ORE: return new cBlockOreHandler (a_BlockType);
+ case E_BLOCK_DIRT: return new cBlockDirtHandler (a_BlockType);
+ case E_BLOCK_DISPENSER: return new cBlockDropSpenserHandler (a_BlockType);
+ case E_BLOCK_DOUBLE_STONE_SLAB: return new cBlockSlabHandler (a_BlockType);
+ case E_BLOCK_DOUBLE_WOODEN_SLAB: return new cBlockSlabHandler (a_BlockType);
+ case E_BLOCK_DROPPER: return new cBlockDropSpenserHandler (a_BlockType);
+ case E_BLOCK_EMERALD_ORE: return new cBlockOreHandler (a_BlockType);
+ case E_BLOCK_ENDER_CHEST: return new cBlockEnderchestHandler (a_BlockType);
+ case E_BLOCK_FARMLAND: return new cBlockFarmlandHandler;
+ case E_BLOCK_FENCE_GATE: return new cBlockFenceGateHandler (a_BlockType);
+ case E_BLOCK_FIRE: return new cBlockFireHandler (a_BlockType);
+ case E_BLOCK_FLOWER_POT: return new cBlockFlowerPotHandler (a_BlockType);
+ case E_BLOCK_FURNACE: return new cBlockFurnaceHandler (a_BlockType);
+ case E_BLOCK_GLOWSTONE: return new cBlockGlowstoneHandler (a_BlockType);
+ case E_BLOCK_GOLD_ORE: return new cBlockOreHandler (a_BlockType);
+ case E_BLOCK_GLASS: return new cBlockGlassHandler (a_BlockType);
+ case E_BLOCK_GRASS: return new cBlockDirtHandler (a_BlockType);
+ case E_BLOCK_GRAVEL: return new cBlockGravelHandler (a_BlockType);
+ case E_BLOCK_HOPPER: return new cBlockHopperHandler (a_BlockType);
+ case E_BLOCK_ICE: return new cBlockIceHandler (a_BlockType);
+ case E_BLOCK_IRON_DOOR: return new cBlockDoorHandler (a_BlockType);
+ case E_BLOCK_IRON_ORE: return new cBlockOreHandler (a_BlockType);
+ case E_BLOCK_JUKEBOX: return new cBlockEntityHandler (a_BlockType);
+ case E_BLOCK_JUNGLE_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType);
+ case E_BLOCK_LADDER: return new cBlockLadderHandler (a_BlockType);
+ case E_BLOCK_LEVER: return new cBlockLeverHandler (a_BlockType);
+ case E_BLOCK_LAPIS_ORE: return new cBlockOreHandler (a_BlockType);
+ case E_BLOCK_LAVA: return new cBlockFluidHandler (a_BlockType);
+ case E_BLOCK_LEAVES: return new cBlockLeavesHandler (a_BlockType);
+ case E_BLOCK_LIT_FURNACE: return new cBlockFurnaceHandler (a_BlockType);
+ case E_BLOCK_LOG: return new cBlockWoodHandler (a_BlockType);
+ case E_BLOCK_MELON: return new cBlockMelonHandler (a_BlockType);
+ case E_BLOCK_MELON_STEM: return new cBlockStemsHandler (a_BlockType);
+ case E_BLOCK_MYCELIUM: return new cBlockMyceliumHandler (a_BlockType);
+ case E_BLOCK_NETHER_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType);
+ case E_BLOCK_NOTE_BLOCK: return new cBlockNoteHandler (a_BlockType);
+ case E_BLOCK_PISTON: return new cBlockPistonHandler (a_BlockType);
+ case E_BLOCK_PLANKS: return new cBlockWoodHandler (a_BlockType);
+ case E_BLOCK_PUMPKIN_STEM: return new cBlockStemsHandler (a_BlockType);
+ case E_BLOCK_RAIL: return new cBlockRailHandler (a_BlockType);
+ case E_BLOCK_POTATOES: return new cBlockCropsHandler (a_BlockType);
+ case E_BLOCK_POWERED_RAIL: return new cBlockRailHandler (a_BlockType);
+ case E_BLOCK_REDSTONE_ORE: return new cBlockOreHandler (a_BlockType);
+ case E_BLOCK_REDSTONE_ORE_GLOWING: return new cBlockOreHandler (a_BlockType);
+ case E_BLOCK_REDSTONE_REPEATER_OFF: return new cBlockRedstoneRepeaterHandler(a_BlockType);
+ case E_BLOCK_REDSTONE_REPEATER_ON: return new cBlockRedstoneRepeaterHandler(a_BlockType);
+ case E_BLOCK_REDSTONE_TORCH_OFF: return new cBlockRedstoneTorchHandler (a_BlockType);
+ case E_BLOCK_REDSTONE_TORCH_ON: return new cBlockRedstoneTorchHandler (a_BlockType);
+ case E_BLOCK_REDSTONE_WIRE: return new cBlockRedstoneHandler (a_BlockType);
+ case E_BLOCK_RED_MUSHROOM: return new cBlockMushroomHandler (a_BlockType);
+ case E_BLOCK_RED_ROSE: return new cBlockFlowerHandler (a_BlockType);
+ case E_BLOCK_SAND: return new cBlockSandHandler (a_BlockType);
+ case E_BLOCK_SANDSTONE_STAIRS: return new cBlockStairsHandler (a_BlockType);
+ case E_BLOCK_SAPLING: return new cBlockSaplingHandler (a_BlockType);
+ case E_BLOCK_SIGN_POST: return new cBlockSignHandler (a_BlockType);
+ case E_BLOCK_SNOW: return new cBlockSnowHandler (a_BlockType);
+ case E_BLOCK_SPRUCE_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType);
+ case E_BLOCK_STATIONARY_LAVA: return new cBlockFluidHandler (a_BlockType);
+ case E_BLOCK_STATIONARY_WATER: return new cBlockFluidHandler (a_BlockType);
+ case E_BLOCK_STICKY_PISTON: return new cBlockPistonHandler (a_BlockType);
+ case E_BLOCK_STONE: return new cBlockStoneHandler (a_BlockType);
+ case E_BLOCK_STONE_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType);
+ case E_BLOCK_STONE_SLAB: return new cBlockSlabHandler (a_BlockType);
+ case E_BLOCK_SUGARCANE: return new cBlockSugarcaneHandler (a_BlockType);
+ case E_BLOCK_TALL_GRASS: return new cBlockTallGrassHandler (a_BlockType);
+ case E_BLOCK_TORCH: return new cBlockTorchHandler (a_BlockType);
+ case E_BLOCK_VINES: return new cBlockVineHandler (a_BlockType);
+ case E_BLOCK_WALLSIGN: return new cBlockSignHandler (a_BlockType);
+ case E_BLOCK_WATER: return new cBlockFluidHandler (a_BlockType);
+ case E_BLOCK_WOODEN_DOOR: return new cBlockDoorHandler (a_BlockType);
+ case E_BLOCK_WOODEN_SLAB: return new cBlockSlabHandler (a_BlockType);
+ case E_BLOCK_WOODEN_STAIRS: return new cBlockStairsHandler (a_BlockType);
+ case E_BLOCK_WOOL: return new cBlockClothHandler (a_BlockType);
+ case E_BLOCK_WORKBENCH: return new cBlockWorkbenchHandler (a_BlockType);
+ case E_BLOCK_YELLOW_FLOWER: return new cBlockFlowerHandler (a_BlockType);
+
+ default: return new cBlockHandler(a_BlockType);
+ }
+}
+
+
+
+
+
+void cBlockHandler::Deinit()
+{
+ for (int i = 0; i < 256; i++)
+ {
+ delete m_BlockHandler[i];
+ }
+ memset(m_BlockHandler, 0, sizeof(m_BlockHandler)); // Don't leave any dangling pointers around, just in case
+ m_HandlerInitialized = false;
+}
+
+
+
+
+
+cBlockHandler::cBlockHandler(BLOCKTYPE a_BlockType)
+{
+ m_BlockType = a_BlockType;
+}
+
+
+
+
+
+bool cBlockHandler::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
+)
+{
+ // By default, all blocks can be placed and the meta is copied over from the item's damage value:
+ a_BlockType = m_BlockType;
+ a_BlockMeta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x0f);
+ return true;
+}
+
+
+
+
+
+void cBlockHandler::OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+}
+
+
+
+
+
+void cBlockHandler::OnPlacedByPlayer(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)
+{
+}
+
+
+
+
+
+void cBlockHandler::OnDestroyedByPlayer(cWorld *a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+}
+
+
+
+
+
+void cBlockHandler::OnPlaced(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ // Notify the neighbors
+ NeighborChanged(a_World, a_BlockX - 1, a_BlockY, a_BlockZ);
+ NeighborChanged(a_World, a_BlockX + 1, a_BlockY, a_BlockZ);
+ NeighborChanged(a_World, a_BlockX, a_BlockY - 1, a_BlockZ);
+ NeighborChanged(a_World, a_BlockX, a_BlockY + 1, a_BlockZ);
+ NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ - 1);
+ NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ + 1);
+}
+
+
+
+
+
+void cBlockHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ // Notify the neighbors
+ NeighborChanged(a_World, a_BlockX - 1, a_BlockY, a_BlockZ);
+ NeighborChanged(a_World, a_BlockX + 1, a_BlockY, a_BlockZ);
+ NeighborChanged(a_World, a_BlockX, a_BlockY - 1, a_BlockZ);
+ NeighborChanged(a_World, a_BlockX, a_BlockY + 1, a_BlockZ);
+ NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ - 1);
+ NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ + 1);
+}
+
+
+
+
+
+void cBlockHandler::NeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ if ((a_BlockY >= 0) && (a_BlockY < cChunkDef::Height))
+ {
+ GetBlockHandler(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ))->OnNeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ);
+ }
+}
+
+
+
+
+
+void cBlockHandler::OnNeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+}
+
+
+
+
+
+void cBlockHandler::OnDigging(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+}
+
+
+
+
+
+void cBlockHandler::OnUse(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)
+{
+}
+
+
+
+
+
+void cBlockHandler::ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta)
+{
+ // Setting the meta to a_BlockMeta keeps most textures. The few other blocks have to override this.
+ a_Pickups.push_back(cItem(m_BlockType, 1, a_BlockMeta));
+}
+
+
+
+
+
+void cBlockHandler::DropBlock(cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ cItems Pickups;
+ NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ ConvertToPickups(Pickups, Meta);
+
+ // Allow plugins to modify the pickups:
+ cRoot::Get()->GetPluginManager()->CallHookBlockToPickups(a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta, Pickups);
+
+ if (!Pickups.empty())
+ {
+ a_World->SpawnItemPickups(Pickups, a_BlockX, a_BlockY, a_BlockZ);
+ }
+}
+
+
+
+
+
+const char * cBlockHandler::GetStepSound()
+{
+ return "step.stone";
+}
+
+
+
+
+
+bool cBlockHandler::CanBeAt(int a_BlockX, int a_BlockY, int a_BlockZ, const cChunk & a_Chunk)
+{
+ return true;
+}
+
+
+
+
+
+bool cBlockHandler::IsUseable()
+{
+ return false;
+}
+
+
+
+
+
+bool cBlockHandler::IsClickedThrough(void)
+{
+ return false;
+}
+
+
+
+
+
+bool cBlockHandler::DoesIgnoreBuildCollision(void)
+{
+ return (m_BlockType == E_BLOCK_AIR);
+}
+
+
+
+
+
+bool cBlockHandler::DoesAllowBlockOnTop(void)
+{
+ return true;
+}
+
+
+
+
+
+bool cBlockHandler::CanBePlacedOnSide(void)
+{
+ return true;
+}
+
+
+
+
+
+bool cBlockHandler::DoesDropOnUnsuitable(void)
+{
+ return true;
+}
+
+
+
+
+
+void cBlockHandler::Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk)
+{
+ if (!CanBeAt(a_RelX, a_RelY, a_RelZ, a_Chunk))
+ {
+ if (DoesDropOnUnsuitable())
+ {
+ int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
+ int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
+ DropBlock(a_Chunk.GetWorld(), NULL, BlockX, a_RelY, BlockZ);
+ }
+
+ a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0);
+ }
+ else
+ {
+ // Wake up the simulators for this block:
+ int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
+ int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
+ a_Chunk.GetWorld()->GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, &a_Chunk);
+ }
+}
+
+
+
+
diff --git a/source/Blocks/BlockHandler.h b/source/Blocks/BlockHandler.h
index cb52be497..228ce174b 100644
--- a/source/Blocks/BlockHandler.h
+++ b/source/Blocks/BlockHandler.h
@@ -1,158 +1,158 @@
-
-#pragma once
-
-#include "../Defines.h"
-#include "../Item.h"
-#include "../Chunk.h"
-
-
-
-
-
-// fwd:
-class cWorld;
-class cPlayer;
-
-
-
-
-
-class cBlockHandler
-{
-public:
- cBlockHandler(BLOCKTYPE a_BlockType);
-
- /// Called when the block gets ticked either by a random tick or by a queued tick
- virtual void OnUpdate(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ);
-
- /** Called before a block is placed into a world.
- The handler should return true to allow placement, false to refuse.
- Also, the handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block.
- Called by cItemHandler::GetPlacementBlockTypeMeta() if the item is a block
- */
- 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
- );
-
- /// Called by cWorld::SetBlock() after the block has been set
- virtual void OnPlaced(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
-
- /// Called by cClientHandle::HandlePlaceBlock() after the player has placed a new block. Called after OnPlaced().
- virtual void OnPlacedByPlayer(
- 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
- );
-
- /// Called before the player has destroyed a block
- virtual void OnDestroyedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ);
-
- /// Called before a block gets destroyed / replaced with air
- virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ);
-
- /// Called when a direct neighbor of this block has been changed (The position is the own position, not the neighbor position)
- virtual void OnNeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ);
-
- /// Notifies all neighbors of the given block about a change
- static void NeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ);
-
- /// Called while the player diggs the block.
- virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ);
-
- /// Called if the user right clicks the block and the block is useable
- virtual void OnUse(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);
-
- /// Called when the item is mined to convert it into pickups. Pickups may specify multiple items.
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta);
-
- /// Handles the dropping of a block based on what ConvertToDrops() returns. This will not destroy the block. a_Digger is the entity causing the drop; it may be NULL
- virtual void DropBlock(cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ);
-
- /// Returns step sound name of block
- virtual const char * GetStepSound(void);
-
- /// Checks if the block can stay at the specified relative coords in the chunk
- virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk);
-
- /** Checks if the block can be placed at this point.
- Default: CanBeAt(...)
- NOTE: This call doesn't actually place the block
- */
- // virtual bool CanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir);
-
- /// Called when the player tries to place a block on top of this block (Only if he aims directly on this block); return false to disallow
- virtual bool DoesAllowBlockOnTop(void);
-
- /// Called to check whether this block supports a rclk action. If it returns true, OnUse() is called
- virtual bool IsUseable(void);
-
- /** Indicates whether the client will click through this block.
- For example digging a fire will hit the block below the fire so fire is clicked through
- */
- virtual bool IsClickedThrough(void);
-
- /** Checks if the player can build "inside" this block.
- For example blocks placed "on" snow will be placed at the same position. So: Snow ignores Build collision
- */
- virtual bool DoesIgnoreBuildCollision(void);
-
- /// Indicates this block can be placed on the side of other blocks. Default: true
- virtual bool CanBePlacedOnSide(void);
-
- /// Does this block drop if it gets destroyed by an unsuitable situation? Default: true
- virtual bool DoesDropOnUnsuitable(void);
-
- /** Called when one of the neighbors gets set; equivalent to MC block update.
- By default drops if position no more suitable (CanBeAt(), DoesDropOnUnsuitable(), Drop()),
- and wakes up all simulators on the block.
- */
- virtual void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk);
-
- /// Returns the meta for a block after rotating it counter-clockwise from the specified meta. Default: no change
- virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) { return a_Meta; }
-
- /// Returns the meta for a block after rotating it clockwise from the specified meta. Default: no change
- virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) { return a_Meta; }
-
- /// Returns the meta for a block after mirroring it around the XY plane. Default: no change
- virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) { return a_Meta; }
-
- /// Returns the meta for a block after mirroring it around the XZ plane. Default: no change
- virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) { return a_Meta; }
-
- /// Returns the meta for a block after mirroring it around the YZ plane. Default: no change
- virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) { return a_Meta; }
-
-
- /// Get the blockhandler for a specific block id
- static cBlockHandler * GetBlockHandler(BLOCKTYPE a_BlockType);
-
- /// Deletes all initialised block handlers
- static void Deinit();
-
-protected:
- BLOCKTYPE m_BlockType;
-
- // Creates a new blockhandler for the given block type. For internal use only, use ::GetBlockHandler() instead.
- static cBlockHandler *CreateBlockHandler(BLOCKTYPE a_BlockType);
- static cBlockHandler *m_BlockHandler[256];
- static bool m_HandlerInitialized; //used to detect if the blockhandlers are initialized
-};
-
-
-
-
-
-// Shortcut to get the blockhandler for a specific block
-inline cBlockHandler * BlockHandler(BLOCKTYPE a_BlockType)
-{
- return cBlockHandler::GetBlockHandler(a_BlockType);
-}
-
-
-
-
+
+#pragma once
+
+#include "../Defines.h"
+#include "../Item.h"
+#include "../Chunk.h"
+
+
+
+
+
+// fwd:
+class cWorld;
+class cPlayer;
+
+
+
+
+
+class cBlockHandler
+{
+public:
+ cBlockHandler(BLOCKTYPE a_BlockType);
+
+ /// Called when the block gets ticked either by a random tick or by a queued tick
+ virtual void OnUpdate(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /** Called before a block is placed into a world.
+ The handler should return true to allow placement, false to refuse.
+ Also, the handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block.
+ Called by cItemHandler::GetPlacementBlockTypeMeta() if the item is a block
+ */
+ 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
+ );
+
+ /// Called by cWorld::SetBlock() after the block has been set
+ virtual void OnPlaced(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+
+ /// Called by cClientHandle::HandlePlaceBlock() after the player has placed a new block. Called after OnPlaced().
+ virtual void OnPlacedByPlayer(
+ 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
+ );
+
+ /// Called before the player has destroyed a block
+ virtual void OnDestroyedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Called before a block gets destroyed / replaced with air
+ virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Called when a direct neighbor of this block has been changed (The position is the own position, not the neighbor position)
+ virtual void OnNeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Notifies all neighbors of the given block about a change
+ static void NeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Called while the player diggs the block.
+ virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Called if the user right clicks the block and the block is useable
+ virtual void OnUse(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);
+
+ /// Called when the item is mined to convert it into pickups. Pickups may specify multiple items.
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta);
+
+ /// Handles the dropping of a block based on what ConvertToDrops() returns. This will not destroy the block. a_Digger is the entity causing the drop; it may be NULL
+ virtual void DropBlock(cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ);
+
+ /// Returns step sound name of block
+ virtual const char * GetStepSound(void);
+
+ /// Checks if the block can stay at the specified relative coords in the chunk
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk);
+
+ /** Checks if the block can be placed at this point.
+ Default: CanBeAt(...)
+ NOTE: This call doesn't actually place the block
+ */
+ // virtual bool CanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir);
+
+ /// Called when the player tries to place a block on top of this block (Only if he aims directly on this block); return false to disallow
+ virtual bool DoesAllowBlockOnTop(void);
+
+ /// Called to check whether this block supports a rclk action. If it returns true, OnUse() is called
+ virtual bool IsUseable(void);
+
+ /** Indicates whether the client will click through this block.
+ For example digging a fire will hit the block below the fire so fire is clicked through
+ */
+ virtual bool IsClickedThrough(void);
+
+ /** Checks if the player can build "inside" this block.
+ For example blocks placed "on" snow will be placed at the same position. So: Snow ignores Build collision
+ */
+ virtual bool DoesIgnoreBuildCollision(void);
+
+ /// Indicates this block can be placed on the side of other blocks. Default: true
+ virtual bool CanBePlacedOnSide(void);
+
+ /// Does this block drop if it gets destroyed by an unsuitable situation? Default: true
+ virtual bool DoesDropOnUnsuitable(void);
+
+ /** Called when one of the neighbors gets set; equivalent to MC block update.
+ By default drops if position no more suitable (CanBeAt(), DoesDropOnUnsuitable(), Drop()),
+ and wakes up all simulators on the block.
+ */
+ virtual void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk);
+
+ /// Returns the meta for a block after rotating it counter-clockwise from the specified meta. Default: no change
+ virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) { return a_Meta; }
+
+ /// Returns the meta for a block after rotating it clockwise from the specified meta. Default: no change
+ virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) { return a_Meta; }
+
+ /// Returns the meta for a block after mirroring it around the XY plane. Default: no change
+ virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) { return a_Meta; }
+
+ /// Returns the meta for a block after mirroring it around the XZ plane. Default: no change
+ virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) { return a_Meta; }
+
+ /// Returns the meta for a block after mirroring it around the YZ plane. Default: no change
+ virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) { return a_Meta; }
+
+
+ /// Get the blockhandler for a specific block id
+ static cBlockHandler * GetBlockHandler(BLOCKTYPE a_BlockType);
+
+ /// Deletes all initialised block handlers
+ static void Deinit();
+
+protected:
+ BLOCKTYPE m_BlockType;
+
+ // Creates a new blockhandler for the given block type. For internal use only, use ::GetBlockHandler() instead.
+ static cBlockHandler *CreateBlockHandler(BLOCKTYPE a_BlockType);
+ static cBlockHandler *m_BlockHandler[256];
+ static bool m_HandlerInitialized; //used to detect if the blockhandlers are initialized
+};
+
+
+
+
+
+// Shortcut to get the blockhandler for a specific block
+inline cBlockHandler * BlockHandler(BLOCKTYPE a_BlockType)
+{
+ return cBlockHandler::GetBlockHandler(a_BlockType);
+}
+
+
+
+
diff --git a/source/Blocks/BlockHopper.h b/source/Blocks/BlockHopper.h
index 10f865564..3998276d7 100644
--- a/source/Blocks/BlockHopper.h
+++ b/source/Blocks/BlockHopper.h
@@ -1,46 +1,46 @@
-
-// BlockHopper.h
-
-// Declares the cBlockHopperHandler class representing the handler for the Hopper block
-
-
-
-
-
-class cBlockHopperHandler :
- public cBlockEntityHandler
-{
-public:
- cBlockHopperHandler(BLOCKTYPE a_BlockType)
- : cBlockEntityHandler(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
- {
- a_BlockType = m_BlockType;
-
- // Convert the blockface into meta:
- switch (a_BlockFace)
- {
- case BLOCK_FACE_BOTTOM: a_BlockMeta = E_META_HOPPER_FACING_YM; break;
- case BLOCK_FACE_TOP: a_BlockMeta = E_META_HOPPER_FACING_YM; break;
- case BLOCK_FACE_EAST: a_BlockMeta = E_META_HOPPER_FACING_XM; break;
- case BLOCK_FACE_NORTH: a_BlockMeta = E_META_HOPPER_FACING_ZP; break;
- case BLOCK_FACE_SOUTH: a_BlockMeta = E_META_HOPPER_FACING_ZM; break;
- case BLOCK_FACE_WEST: a_BlockMeta = E_META_HOPPER_FACING_XP; break;
- default: a_BlockMeta = E_META_HOPPER_UNATTACHED; break;
- }
- return true;
- }
-} ;
-
-
-
-
+
+// BlockHopper.h
+
+// Declares the cBlockHopperHandler class representing the handler for the Hopper block
+
+
+
+
+
+class cBlockHopperHandler :
+ public cBlockEntityHandler
+{
+public:
+ cBlockHopperHandler(BLOCKTYPE a_BlockType)
+ : cBlockEntityHandler(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
+ {
+ a_BlockType = m_BlockType;
+
+ // Convert the blockface into meta:
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_BOTTOM: a_BlockMeta = E_META_HOPPER_FACING_YM; break;
+ case BLOCK_FACE_TOP: a_BlockMeta = E_META_HOPPER_FACING_YM; break;
+ case BLOCK_FACE_EAST: a_BlockMeta = E_META_HOPPER_FACING_XM; break;
+ case BLOCK_FACE_NORTH: a_BlockMeta = E_META_HOPPER_FACING_ZP; break;
+ case BLOCK_FACE_SOUTH: a_BlockMeta = E_META_HOPPER_FACING_ZM; break;
+ case BLOCK_FACE_WEST: a_BlockMeta = E_META_HOPPER_FACING_XP; break;
+ default: a_BlockMeta = E_META_HOPPER_UNATTACHED; break;
+ }
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockIce.h b/source/Blocks/BlockIce.h
index 605a8ae1d..11fe425f3 100644
--- a/source/Blocks/BlockIce.h
+++ b/source/Blocks/BlockIce.h
@@ -1,37 +1,37 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-#include "../World.h"
-
-
-
-
-
-class cBlockIceHandler :
- public cBlockHandler
-{
-public:
- cBlockIceHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- // No pickups
- }
-
-
- virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
- {
- // TODO: Ice destroyed with air below it should turn into air instead of water
- a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_STATIONARY_WATER, 8);
- // This is called later than the real destroying of this ice block
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../World.h"
+
+
+
+
+
+class cBlockIceHandler :
+ public cBlockHandler
+{
+public:
+ cBlockIceHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // No pickups
+ }
+
+
+ virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ // TODO: Ice destroyed with air below it should turn into air instead of water
+ a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_STATIONARY_WATER, 8);
+ // This is called later than the real destroying of this ice block
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockLadder.h b/source/Blocks/BlockLadder.h
index 93473f757..c0aa25f60 100644
--- a/source/Blocks/BlockLadder.h
+++ b/source/Blocks/BlockLadder.h
@@ -1,115 +1,115 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-#include "../World.h"
-
-
-
-
-
-class cBlockLadderHandler :
- public cBlockHandler
-{
-public:
- cBlockLadderHandler(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
- {
- if (!LadderCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace))
- {
- a_BlockFace = FindSuitableBlockFace(a_World, a_BlockX, a_BlockY, a_BlockZ);
-
- if (a_BlockFace == BLOCK_FACE_BOTTOM)
- {
- return false;
- }
- }
-
- a_BlockType = m_BlockType;
- a_BlockMeta = DirectionToMetaData(a_BlockFace);
- return true;
- }
-
-
- static NIBBLETYPE DirectionToMetaData(char a_Direction) // tolua_export
- { // tolua_export
- switch (a_Direction)
- {
- case 0x2: return 0x2;
- case 0x3: return 0x3;
- case 0x4: return 0x4;
- case 0x5: return 0x5;
- default: return 0x2;
- }
- } // tolua_export
-
-
- static char MetaDataToDirection(NIBBLETYPE a_MetaData) // tolua_export
- { // tolua_export
- switch (a_MetaData)
- {
- case 0x2: return 0x2;
- case 0x3: return 0x3;
- case 0x4: return 0x4;
- case 0x5: return 0x5;
- default: return 0x2;
- }
- } // tolua_export
-
-
- /// Finds a suitable Direction for the Ladder. Returns BLOCK_FACE_BOTTOM on failure
- static char FindSuitableBlockFace(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
- {
- for (int Face = 2; Face <= 5; Face++)
- {
- if (LadderCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, Face))
- {
- return Face;
- }
- }
- return BLOCK_FACE_BOTTOM;
- }
-
-
- static bool LadderCanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
- {
- if ((a_BlockFace == BLOCK_FACE_BOTTOM) || (a_BlockFace == BLOCK_FACE_TOP))
- {
- return false;
- }
-
- AddFaceDirection( a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true);
-
- return g_BlockIsSolid[a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)];
- }
-
-
- virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
- {
- // TODO: Use AdjustCoordsByMeta(), then cChunk::UnboundedRelGetBlock() and finally some comparison
- char BlockFace = MetaDataToDirection(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ));
- int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
- int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
- return LadderCanBePlacedAt(a_Chunk.GetWorld(), BlockX, a_RelY, BlockZ, BlockFace);
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.wood";
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../World.h"
+
+
+
+
+
+class cBlockLadderHandler :
+ public cBlockHandler
+{
+public:
+ cBlockLadderHandler(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
+ {
+ if (!LadderCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace))
+ {
+ a_BlockFace = FindSuitableBlockFace(a_World, a_BlockX, a_BlockY, a_BlockZ);
+
+ if (a_BlockFace == BLOCK_FACE_BOTTOM)
+ {
+ return false;
+ }
+ }
+
+ a_BlockType = m_BlockType;
+ a_BlockMeta = DirectionToMetaData(a_BlockFace);
+ return true;
+ }
+
+
+ static NIBBLETYPE DirectionToMetaData(char a_Direction) // tolua_export
+ { // tolua_export
+ switch (a_Direction)
+ {
+ case 0x2: return 0x2;
+ case 0x3: return 0x3;
+ case 0x4: return 0x4;
+ case 0x5: return 0x5;
+ default: return 0x2;
+ }
+ } // tolua_export
+
+
+ static char MetaDataToDirection(NIBBLETYPE a_MetaData) // tolua_export
+ { // tolua_export
+ switch (a_MetaData)
+ {
+ case 0x2: return 0x2;
+ case 0x3: return 0x3;
+ case 0x4: return 0x4;
+ case 0x5: return 0x5;
+ default: return 0x2;
+ }
+ } // tolua_export
+
+
+ /// Finds a suitable Direction for the Ladder. Returns BLOCK_FACE_BOTTOM on failure
+ static char FindSuitableBlockFace(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ for (int Face = 2; Face <= 5; Face++)
+ {
+ if (LadderCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, Face))
+ {
+ return Face;
+ }
+ }
+ return BLOCK_FACE_BOTTOM;
+ }
+
+
+ static bool LadderCanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
+ {
+ if ((a_BlockFace == BLOCK_FACE_BOTTOM) || (a_BlockFace == BLOCK_FACE_TOP))
+ {
+ return false;
+ }
+
+ AddFaceDirection( a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true);
+
+ return g_BlockIsSolid[a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)];
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ // TODO: Use AdjustCoordsByMeta(), then cChunk::UnboundedRelGetBlock() and finally some comparison
+ char BlockFace = MetaDataToDirection(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ));
+ int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
+ int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
+ return LadderCanBePlacedAt(a_Chunk.GetWorld(), BlockX, a_RelY, BlockZ, BlockFace);
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockLeaves.h b/source/Blocks/BlockLeaves.h
index 3563babcf..6e015b8fa 100644
--- a/source/Blocks/BlockLeaves.h
+++ b/source/Blocks/BlockLeaves.h
@@ -1,184 +1,184 @@
-#pragma once
-#include "BlockHandler.h"
-#include "../MersenneTwister.h"
-#include "../World.h"
-#include "../BlockArea.h"
-
-
-
-
-
-// Leaves can be this many blocks that away (inclusive) from the log not to decay
-#define LEAVES_CHECK_DISTANCE 6
-
-#define PROCESS_NEIGHBOR(x,y,z) \
- switch (a_Area.GetBlockType(x, y, z)) \
- { \
- case E_BLOCK_LEAVES: a_Area.SetBlockType(x, y, z, (BLOCKTYPE)(E_BLOCK_SPONGE + i + 1)); break; \
- case E_BLOCK_LOG: return true; \
- }
-
-bool HasNearLog(cBlockArea &a_Area, int a_BlockX, int a_BlockY, int a_BlockZ);
-
-
-
-
-
-class cBlockLeavesHandler :
- public cBlockHandler
-{
-public:
- cBlockLeavesHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- MTRand rand;
-
- // Only the first 2 bits contain the display information, the others are for growing
- if (rand.randInt(5) == 0)
- {
- a_Pickups.push_back(cItem(E_BLOCK_SAPLING, 1, a_BlockMeta & 3));
- }
- if ((a_BlockMeta & 3) == E_META_SAPLING_APPLE)
- {
- if (rand.rand(100) == 0)
- {
- a_Pickups.push_back(cItem(E_ITEM_RED_APPLE, 1, 0));
- }
- }
- }
-
-
- void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
- {
- cBlockHandler::OnDestroyed(a_World, a_BlockX, a_BlockY, a_BlockZ);
-
- //0.5% chance of dropping an apple
- NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
- //check if Oak (0x1 and 0x2 bit not set)
- MTRand rand;
- if(!(Meta & 3) && rand.randInt(200) == 100)
- {
- cItems Drops;
- Drops.push_back(cItem(E_ITEM_RED_APPLE, 1, 0));
- a_World->SpawnItemPickups(Drops, a_BlockX, a_BlockY, a_BlockZ);
- }
- }
-
-
- virtual void OnNeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
- {
- NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
- a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta & 0x7); // Unset 0x8 bit so it gets checked for decay
- }
-
-
- virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
- {
- NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
- if ((Meta & 0x04) != 0)
- {
- // Player-placed leaves, don't decay
- return;
- }
-
- if ((Meta & 0x8) != 0)
- {
- // These leaves have been checked for decay lately and nothing around them changed
- return;
- }
-
- // Get the data around the leaves:
- cBlockArea Area;
- if (!Area.Read(
- a_World,
- a_BlockX - LEAVES_CHECK_DISTANCE, a_BlockX + LEAVES_CHECK_DISTANCE,
- a_BlockY - LEAVES_CHECK_DISTANCE, a_BlockY + LEAVES_CHECK_DISTANCE,
- a_BlockZ - LEAVES_CHECK_DISTANCE, a_BlockZ + LEAVES_CHECK_DISTANCE,
- cBlockArea::baTypes)
- )
- {
- // Cannot check leaves, a chunk is missing too close
- return;
- }
-
- if (HasNearLog(Area, a_BlockX, a_BlockY, a_BlockZ))
- {
- // Wood found, the leaves stay; mark them as checked:
- a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta | 0x8);
- return;
- }
- // Decay the leaves:
- DropBlock(a_World, NULL, a_BlockX, a_BlockY, a_BlockZ);
-
- a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ);
-
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.grass";
- }
-} ;
-
-
-
-
-
-bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ)
-{
- // Filter the blocks into a {leaves, log, other (air)} set:
- BLOCKTYPE * Types = a_Area.GetBlockTypes();
- for (int i = a_Area.GetBlockCount() - 1; i > 0; i--)
- {
- switch (Types[i])
- {
- case E_BLOCK_LEAVES:
- case E_BLOCK_LOG:
- {
- break;
- }
- default:
- {
- Types[i] = E_BLOCK_AIR;
- break;
- }
- }
- } // for i - Types[]
-
- // Perform a breadth-first search to see if there's a log connected within 4 blocks of the leaves block:
- // Simply replace all reachable leaves blocks with a sponge block plus iteration (in the Area) and see if we can reach a log in 4 iterations
- a_Area.SetBlockType(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_SPONGE);
- for (int i = 0; i < LEAVES_CHECK_DISTANCE; i++)
- {
- for (int y = a_BlockY - i; y <= a_BlockY + i; y++)
- {
- for (int z = a_BlockZ - i; z <= a_BlockZ + i; z++)
- {
- for (int x = a_BlockX - i; x <= a_BlockX + i; x++)
- {
- if (a_Area.GetBlockType(x, y, z) != E_BLOCK_SPONGE + i)
- {
- continue;
- }
- PROCESS_NEIGHBOR(x - 1, y, z);
- PROCESS_NEIGHBOR(x + 1, y, z);
- PROCESS_NEIGHBOR(x, y, z - 1);
- PROCESS_NEIGHBOR(x, y, z + 1);
- PROCESS_NEIGHBOR(x, y + 1, z);
- PROCESS_NEIGHBOR(x, y - 1, z);
- } // for x
- } // for z
- } // for y
- } // for i - BFS iterations
- return false;
-}
-
-
-
-
+#pragma once
+#include "BlockHandler.h"
+#include "../MersenneTwister.h"
+#include "../World.h"
+#include "../BlockArea.h"
+
+
+
+
+
+// Leaves can be this many blocks that away (inclusive) from the log not to decay
+#define LEAVES_CHECK_DISTANCE 6
+
+#define PROCESS_NEIGHBOR(x,y,z) \
+ switch (a_Area.GetBlockType(x, y, z)) \
+ { \
+ case E_BLOCK_LEAVES: a_Area.SetBlockType(x, y, z, (BLOCKTYPE)(E_BLOCK_SPONGE + i + 1)); break; \
+ case E_BLOCK_LOG: return true; \
+ }
+
+bool HasNearLog(cBlockArea &a_Area, int a_BlockX, int a_BlockY, int a_BlockZ);
+
+
+
+
+
+class cBlockLeavesHandler :
+ public cBlockHandler
+{
+public:
+ cBlockLeavesHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ MTRand rand;
+
+ // Only the first 2 bits contain the display information, the others are for growing
+ if (rand.randInt(5) == 0)
+ {
+ a_Pickups.push_back(cItem(E_BLOCK_SAPLING, 1, a_BlockMeta & 3));
+ }
+ if ((a_BlockMeta & 3) == E_META_SAPLING_APPLE)
+ {
+ if (rand.rand(100) == 0)
+ {
+ a_Pickups.push_back(cItem(E_ITEM_RED_APPLE, 1, 0));
+ }
+ }
+ }
+
+
+ void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ cBlockHandler::OnDestroyed(a_World, a_BlockX, a_BlockY, a_BlockZ);
+
+ //0.5% chance of dropping an apple
+ NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ //check if Oak (0x1 and 0x2 bit not set)
+ MTRand rand;
+ if(!(Meta & 3) && rand.randInt(200) == 100)
+ {
+ cItems Drops;
+ Drops.push_back(cItem(E_ITEM_RED_APPLE, 1, 0));
+ a_World->SpawnItemPickups(Drops, a_BlockX, a_BlockY, a_BlockZ);
+ }
+ }
+
+
+ virtual void OnNeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta & 0x7); // Unset 0x8 bit so it gets checked for decay
+ }
+
+
+ virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ if ((Meta & 0x04) != 0)
+ {
+ // Player-placed leaves, don't decay
+ return;
+ }
+
+ if ((Meta & 0x8) != 0)
+ {
+ // These leaves have been checked for decay lately and nothing around them changed
+ return;
+ }
+
+ // Get the data around the leaves:
+ cBlockArea Area;
+ if (!Area.Read(
+ a_World,
+ a_BlockX - LEAVES_CHECK_DISTANCE, a_BlockX + LEAVES_CHECK_DISTANCE,
+ a_BlockY - LEAVES_CHECK_DISTANCE, a_BlockY + LEAVES_CHECK_DISTANCE,
+ a_BlockZ - LEAVES_CHECK_DISTANCE, a_BlockZ + LEAVES_CHECK_DISTANCE,
+ cBlockArea::baTypes)
+ )
+ {
+ // Cannot check leaves, a chunk is missing too close
+ return;
+ }
+
+ if (HasNearLog(Area, a_BlockX, a_BlockY, a_BlockZ))
+ {
+ // Wood found, the leaves stay; mark them as checked:
+ a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta | 0x8);
+ return;
+ }
+ // Decay the leaves:
+ DropBlock(a_World, NULL, a_BlockX, a_BlockY, a_BlockZ);
+
+ a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ);
+
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.grass";
+ }
+} ;
+
+
+
+
+
+bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ // Filter the blocks into a {leaves, log, other (air)} set:
+ BLOCKTYPE * Types = a_Area.GetBlockTypes();
+ for (int i = a_Area.GetBlockCount() - 1; i > 0; i--)
+ {
+ switch (Types[i])
+ {
+ case E_BLOCK_LEAVES:
+ case E_BLOCK_LOG:
+ {
+ break;
+ }
+ default:
+ {
+ Types[i] = E_BLOCK_AIR;
+ break;
+ }
+ }
+ } // for i - Types[]
+
+ // Perform a breadth-first search to see if there's a log connected within 4 blocks of the leaves block:
+ // Simply replace all reachable leaves blocks with a sponge block plus iteration (in the Area) and see if we can reach a log in 4 iterations
+ a_Area.SetBlockType(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_SPONGE);
+ for (int i = 0; i < LEAVES_CHECK_DISTANCE; i++)
+ {
+ for (int y = a_BlockY - i; y <= a_BlockY + i; y++)
+ {
+ for (int z = a_BlockZ - i; z <= a_BlockZ + i; z++)
+ {
+ for (int x = a_BlockX - i; x <= a_BlockX + i; x++)
+ {
+ if (a_Area.GetBlockType(x, y, z) != E_BLOCK_SPONGE + i)
+ {
+ continue;
+ }
+ PROCESS_NEIGHBOR(x - 1, y, z);
+ PROCESS_NEIGHBOR(x + 1, y, z);
+ PROCESS_NEIGHBOR(x, y, z - 1);
+ PROCESS_NEIGHBOR(x, y, z + 1);
+ PROCESS_NEIGHBOR(x, y + 1, z);
+ PROCESS_NEIGHBOR(x, y - 1, z);
+ } // for x
+ } // for z
+ } // for y
+ } // for i - BFS iterations
+ return false;
+}
+
+
+
+
diff --git a/source/Blocks/BlockLever.cpp b/source/Blocks/BlockLever.cpp
index a9ab1fbbb..6dbff10c1 100644
--- a/source/Blocks/BlockLever.cpp
+++ b/source/Blocks/BlockLever.cpp
@@ -1,48 +1,48 @@
-
-#include "Globals.h"
-#include "BlockLever.h"
-#include "../Item.h"
-#include "../World.h"
-#include "../Player.h"
-#include "../Simulator/RedstoneSimulator.h"
-
-
-
-
-
-cBlockLeverHandler::cBlockLeverHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
-{
-}
-
-
-
-
-
-void cBlockLeverHandler::OnUse(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)
-{
- // Flip the ON bit on/off. Using XOR bitwise operation to turn it on/off.
- NIBBLETYPE Meta = ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08) & 0x0f);
- a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta);
- if (Meta & 0x08)
- {
- a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f);
- }
- else
- {
- a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.5f);
- }
-}
-
-
-
-
-
-void cBlockLeverHandler::OnDigging(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
-{
- OnUse(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NONE, 8, 8, 8);
-}
-
-
-
-
+
+#include "Globals.h"
+#include "BlockLever.h"
+#include "../Item.h"
+#include "../World.h"
+#include "../Player.h"
+#include "../Simulator/RedstoneSimulator.h"
+
+
+
+
+
+cBlockLeverHandler::cBlockLeverHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+{
+}
+
+
+
+
+
+void cBlockLeverHandler::OnUse(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)
+{
+ // Flip the ON bit on/off. Using XOR bitwise operation to turn it on/off.
+ NIBBLETYPE Meta = ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08) & 0x0f);
+ a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta);
+ if (Meta & 0x08)
+ {
+ a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f);
+ }
+ else
+ {
+ a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.5f);
+ }
+}
+
+
+
+
+
+void cBlockLeverHandler::OnDigging(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ OnUse(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NONE, 8, 8, 8);
+}
+
+
+
+
diff --git a/source/Blocks/BlockLever.h b/source/Blocks/BlockLever.h
index 42a16fd9b..362cf563e 100644
--- a/source/Blocks/BlockLever.h
+++ b/source/Blocks/BlockLever.h
@@ -1,61 +1,61 @@
-#pragma once
-
-#include "BlockHandler.h"
-#include "../World.h"
-#include "../Simulator/RedstoneSimulator.h"
-
-
-
-
-
-class cBlockLeverHandler :
- public cBlockHandler
-{
-public:
- cBlockLeverHandler(BLOCKTYPE a_BlockType);
-
- virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override;
- virtual void OnUse(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) override;
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- // Reset meta to 0
- a_Pickups.push_back(cItem(E_BLOCK_LEVER, 1, 0));
- }
-
-
- virtual bool IsUseable(void) override
- {
- return true;
- }
-
-
- 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
- {
- a_BlockType = m_BlockType;
- a_BlockMeta = cRedstoneSimulator::LeverDirectionToMetaData(a_BlockFace);
- return true;
- }
-
-
- virtual bool DoesAllowBlockOnTop(void) override
- {
- return false;
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.wood";
- }
-} ;
-
-
-
-
+#pragma once
+
+#include "BlockHandler.h"
+#include "../World.h"
+#include "../Simulator/RedstoneSimulator.h"
+
+
+
+
+
+class cBlockLeverHandler :
+ public cBlockHandler
+{
+public:
+ cBlockLeverHandler(BLOCKTYPE a_BlockType);
+
+ virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override;
+ virtual void OnUse(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) override;
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Reset meta to 0
+ a_Pickups.push_back(cItem(E_BLOCK_LEVER, 1, 0));
+ }
+
+
+ virtual bool IsUseable(void) override
+ {
+ return true;
+ }
+
+
+ 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
+ {
+ a_BlockType = m_BlockType;
+ a_BlockMeta = cRedstoneSimulator::LeverDirectionToMetaData(a_BlockFace);
+ return true;
+ }
+
+
+ virtual bool DoesAllowBlockOnTop(void) override
+ {
+ return false;
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockMelon.h b/source/Blocks/BlockMelon.h
index d985ef126..2f7d9a461 100644
--- a/source/Blocks/BlockMelon.h
+++ b/source/Blocks/BlockMelon.h
@@ -1,35 +1,35 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockMelonHandler :
- public cBlockHandler
-{
-public:
- cBlockMelonHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- MTRand r1;
- a_Pickups.push_back(cItem(E_ITEM_MELON_SLICE, (char)(3 + r1.randInt(4)), 0));
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.wood";
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockMelonHandler :
+ public cBlockHandler
+{
+public:
+ cBlockMelonHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ MTRand r1;
+ a_Pickups.push_back(cItem(E_ITEM_MELON_SLICE, (char)(3 + r1.randInt(4)), 0));
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockMushroom.h b/source/Blocks/BlockMushroom.h
index 62cc898c0..b3b23e2ba 100644
--- a/source/Blocks/BlockMushroom.h
+++ b/source/Blocks/BlockMushroom.h
@@ -1,71 +1,71 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockMushroomHandler :
- public cBlockHandler
-{
-public:
- cBlockMushroomHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- // Reset meta to 0
- a_Pickups.push_back(cItem(m_BlockType, 1, 0));
- }
-
-
- virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
- {
- if (a_RelY <= 0)
- {
- return false;
- }
-
- // TODO: Cannot be at too much daylight
-
- switch (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ))
- {
- case E_BLOCK_GLASS:
- case E_BLOCK_CACTUS:
- case E_BLOCK_ICE:
- case E_BLOCK_LEAVES:
- case E_BLOCK_AIR:
- {
- return false;
- }
- }
- return true;
- }
-
-
- virtual bool DoesAllowBlockOnTop(void) override
- {
- return false;
- }
-
-
- virtual bool CanBePlacedOnSide(void) override
- {
- return false;
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.grass";
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockMushroomHandler :
+ public cBlockHandler
+{
+public:
+ cBlockMushroomHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Reset meta to 0
+ a_Pickups.push_back(cItem(m_BlockType, 1, 0));
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ if (a_RelY <= 0)
+ {
+ return false;
+ }
+
+ // TODO: Cannot be at too much daylight
+
+ switch (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ))
+ {
+ case E_BLOCK_GLASS:
+ case E_BLOCK_CACTUS:
+ case E_BLOCK_ICE:
+ case E_BLOCK_LEAVES:
+ case E_BLOCK_AIR:
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ virtual bool DoesAllowBlockOnTop(void) override
+ {
+ return false;
+ }
+
+
+ virtual bool CanBePlacedOnSide(void) override
+ {
+ return false;
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.grass";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockMycelium.h b/source/Blocks/BlockMycelium.h
index 8e1f577e1..0ed7162ac 100644
--- a/source/Blocks/BlockMycelium.h
+++ b/source/Blocks/BlockMycelium.h
@@ -1,27 +1,27 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockMyceliumHandler :
- public cBlockHandler
-{
-public:
- cBlockMyceliumHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- a_Pickups.push_back(cItem(E_BLOCK_DIRT, 1, 0));
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockMyceliumHandler :
+ public cBlockHandler
+{
+public:
+ cBlockMyceliumHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_BLOCK_DIRT, 1, 0));
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockNote.h b/source/Blocks/BlockNote.h
index 5884a1eb3..fef38d845 100644
--- a/source/Blocks/BlockNote.h
+++ b/source/Blocks/BlockNote.h
@@ -1,13 +1,13 @@
-#pragma once
-#include "BlockHandler.h"
-#include "BlockEntity.h"
-
-class cBlockNoteHandler : public cBlockEntityHandler
-{
-public:
- cBlockNoteHandler(BLOCKTYPE a_BlockType)
- : cBlockEntityHandler(a_BlockType)
- {
- }
-
-};
+#pragma once
+#include "BlockHandler.h"
+#include "BlockEntity.h"
+
+class cBlockNoteHandler : public cBlockEntityHandler
+{
+public:
+ cBlockNoteHandler(BLOCKTYPE a_BlockType)
+ : cBlockEntityHandler(a_BlockType)
+ {
+ }
+
+};
diff --git a/source/Blocks/BlockOre.h b/source/Blocks/BlockOre.h
index 1bfd8d17c..9684dbb19 100644
--- a/source/Blocks/BlockOre.h
+++ b/source/Blocks/BlockOre.h
@@ -1,80 +1,80 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-#include "../MersenneTwister.h"
-#include "../World.h"
-
-
-
-
-
-class cBlockOreHandler :
- public cBlockHandler
-{
-public:
- cBlockOreHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- short ItemType = m_BlockType;
- char Count = 1;
- short Meta = 0;
-
- MTRand r1;
- switch (m_BlockType)
- {
- case E_BLOCK_LAPIS_ORE:
- {
- ItemType = E_ITEM_DYE;
- Count = 4 + (char)r1.randInt(4);
- Meta = 4;
- break;
- }
- case E_BLOCK_REDSTONE_ORE:
- case E_BLOCK_REDSTONE_ORE_GLOWING:
- {
- Count = 4 + (char)r1.randInt(1);
- break;
- }
- default:
- {
- Count = 1;
- break;
- }
- }
-
- switch (m_BlockType)
- {
- case E_BLOCK_DIAMOND_ORE:
- {
- ItemType = E_ITEM_DIAMOND;
- break;
- }
- case E_BLOCK_REDSTONE_ORE:
- case E_BLOCK_REDSTONE_ORE_GLOWING:
- {
- ItemType = E_ITEM_REDSTONE_DUST;
- break;
- }
- case E_BLOCK_EMERALD_ORE:
- {
- ItemType = E_ITEM_EMERALD;
- break;
- }
- case E_BLOCK_COAL_ORE:
- {
- ItemType = E_ITEM_COAL;
- break;
- }
- }
- a_Pickups.push_back(cItem(ItemType, Count, Meta));
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../MersenneTwister.h"
+#include "../World.h"
+
+
+
+
+
+class cBlockOreHandler :
+ public cBlockHandler
+{
+public:
+ cBlockOreHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ short ItemType = m_BlockType;
+ char Count = 1;
+ short Meta = 0;
+
+ MTRand r1;
+ switch (m_BlockType)
+ {
+ case E_BLOCK_LAPIS_ORE:
+ {
+ ItemType = E_ITEM_DYE;
+ Count = 4 + (char)r1.randInt(4);
+ Meta = 4;
+ break;
+ }
+ case E_BLOCK_REDSTONE_ORE:
+ case E_BLOCK_REDSTONE_ORE_GLOWING:
+ {
+ Count = 4 + (char)r1.randInt(1);
+ break;
+ }
+ default:
+ {
+ Count = 1;
+ break;
+ }
+ }
+
+ switch (m_BlockType)
+ {
+ case E_BLOCK_DIAMOND_ORE:
+ {
+ ItemType = E_ITEM_DIAMOND;
+ break;
+ }
+ case E_BLOCK_REDSTONE_ORE:
+ case E_BLOCK_REDSTONE_ORE_GLOWING:
+ {
+ ItemType = E_ITEM_REDSTONE_DUST;
+ break;
+ }
+ case E_BLOCK_EMERALD_ORE:
+ {
+ ItemType = E_ITEM_EMERALD;
+ break;
+ }
+ case E_BLOCK_COAL_ORE:
+ {
+ ItemType = E_ITEM_COAL;
+ break;
+ }
+ }
+ a_Pickups.push_back(cItem(ItemType, Count, Meta));
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockPiston.cpp b/source/Blocks/BlockPiston.cpp
index 29312e97c..d0f90559e 100644
--- a/source/Blocks/BlockPiston.cpp
+++ b/source/Blocks/BlockPiston.cpp
@@ -1,69 +1,69 @@
-
-#include "Globals.h"
-#include "BlockPiston.h"
-#include "../Item.h"
-#include "../World.h"
-#include "../Player.h"
-#include "../Piston.h"
-
-
-
-
-
-#define AddPistonDir(x, y, z, dir, amount) \
- switch (dir) \
- { \
- case 0: (y) -= (amount); break; \
- case 1: (y) += (amount); break; \
- case 2: (z) -= (amount); break; \
- case 3: (z) += (amount); break; \
- case 4: (x) -= (amount); break; \
- case 5: (x) += (amount); break; \
- }
-
-
-
-
-cBlockPistonHandler::cBlockPistonHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
-{
-}
-
-
-
-
-
-void cBlockPistonHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
-{
- char OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
-
- int newX = a_BlockX;
- int newY = a_BlockY;
- int newZ = a_BlockZ;
- AddPistonDir(newX, newY, newZ, OldMeta & ~(8), 1);
-
- if (a_World->GetBlock(newX, newY, newZ) == E_BLOCK_PISTON_EXTENSION)
- {
- a_World->SetBlock(newX, newY, newZ, E_BLOCK_AIR, 0);
- }
-}
-
-
-
-
-
-bool cBlockPistonHandler::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
-)
-{
- a_BlockType = m_BlockType;
- a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), a_Player->GetPitch());
- return true;
-}
-
-
-
-
+
+#include "Globals.h"
+#include "BlockPiston.h"
+#include "../Item.h"
+#include "../World.h"
+#include "../Player.h"
+#include "../Piston.h"
+
+
+
+
+
+#define AddPistonDir(x, y, z, dir, amount) \
+ switch (dir) \
+ { \
+ case 0: (y) -= (amount); break; \
+ case 1: (y) += (amount); break; \
+ case 2: (z) -= (amount); break; \
+ case 3: (z) += (amount); break; \
+ case 4: (x) -= (amount); break; \
+ case 5: (x) += (amount); break; \
+ }
+
+
+
+
+cBlockPistonHandler::cBlockPistonHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+{
+}
+
+
+
+
+
+void cBlockPistonHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ char OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+
+ int newX = a_BlockX;
+ int newY = a_BlockY;
+ int newZ = a_BlockZ;
+ AddPistonDir(newX, newY, newZ, OldMeta & ~(8), 1);
+
+ if (a_World->GetBlock(newX, newY, newZ) == E_BLOCK_PISTON_EXTENSION)
+ {
+ a_World->SetBlock(newX, newY, newZ, E_BLOCK_AIR, 0);
+ }
+}
+
+
+
+
+
+bool cBlockPistonHandler::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
+)
+{
+ a_BlockType = m_BlockType;
+ a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), a_Player->GetPitch());
+ return true;
+}
+
+
+
+
diff --git a/source/Blocks/BlockPiston.h b/source/Blocks/BlockPiston.h
index 5d9d5c2b7..ef6a4fa97 100644
--- a/source/Blocks/BlockPiston.h
+++ b/source/Blocks/BlockPiston.h
@@ -1,28 +1,28 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockPistonHandler :
- public cBlockHandler
-{
-public:
- cBlockPistonHandler(BLOCKTYPE a_BlockType);
-
- virtual void OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override;
-
- 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;
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockPistonHandler :
+ public cBlockHandler
+{
+public:
+ cBlockPistonHandler(BLOCKTYPE a_BlockType);
+
+ virtual void OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override;
+
+ 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;
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockRail.h b/source/Blocks/BlockRail.h
index 60865abf5..65d120923 100644
--- a/source/Blocks/BlockRail.h
+++ b/source/Blocks/BlockRail.h
@@ -1,421 +1,421 @@
-
-#pragma once
-
-#include "BlockEntity.h"
-#include "../World.h"
-
-
-
-
-
-/// Meta values for the rail
-enum ENUM_RAIL_DIRECTIONS
-{
- E_RAIL_NORTH_SOUTH = 0,
- E_RAIL_EAST_WEST = 1,
- E_RAIL_ASCEND_EAST = 2,
- E_RAIL_ASCEND_WEST = 3,
- E_RAIL_ASCEND_NORTH = 4,
- E_RAIL_ASCEND_SOUTH = 5,
- E_RAIL_CURVED_SOUTH_EAST = 6,
- E_RAIL_CURVED_SOUTH_WEST = 7,
- E_RAIL_CURVED_NORTH_WEST = 8,
- E_RAIL_CURVED_NORTH_EAST = 9,
-
- // Some useful synonyms:
- E_RAIL_DIR_X = E_RAIL_EAST_WEST,
- E_RAIL_DIR_Z = E_RAIL_NORTH_SOUTH,
- E_RAIL_ASCEND_XP = E_RAIL_ASCEND_EAST,
- E_RAIL_ASCEND_XM = E_RAIL_ASCEND_WEST,
- E_RAIL_ASCEND_ZM = E_RAIL_ASCEND_NORTH,
- E_RAIL_ASCEND_ZP = E_RAIL_ASCEND_SOUTH,
- E_RAIL_CURVED_XPZP = E_RAIL_CURVED_SOUTH_EAST,
- E_RAIL_CURVED_XMZP = E_RAIL_CURVED_SOUTH_WEST,
- E_RAIL_CURVED_XMZM = E_RAIL_CURVED_NORTH_WEST,
- E_RAIL_CURVED_XPZM = E_RAIL_CURVED_NORTH_EAST,
-} ;
-
-
-
-
-
-enum ENUM_PURE
-{
- E_PURE_UPDOWN = 0,
- E_PURE_DOWN = 1,
- E_PURE_NONE = 2
-};
-
-
-
-
-
-class cBlockRailHandler :
- public cBlockHandler
-{
-public:
- cBlockRailHandler(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
- {
- a_BlockType = m_BlockType;
- a_BlockMeta = FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ);
- return true;
- }
-
-
- virtual void OnNeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
- {
- NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
- if (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ) && (Meta != FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ)))
- {
- a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ));
- }
- }
-
-
- virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
- {
- if (a_RelY <= 0)
- {
- return false;
- }
- if (!g_BlockIsSolid[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)])
- {
- return false;
- }
-
- NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
- switch (Meta)
- {
- case E_RAIL_ASCEND_EAST:
- case E_RAIL_ASCEND_WEST:
- case E_RAIL_ASCEND_NORTH:
- case E_RAIL_ASCEND_SOUTH:
- {
- // Mapping between the meta and the neighbors that need checking
- Meta -= E_RAIL_ASCEND_EAST; // Base index at zero
- static const struct
- {
- int x, z;
- } Coords[] =
- {
- { 1, 0}, // east, XP
- {-1, 0}, // west, XM
- { 0, -1}, // north, ZM
- { 0, 1}, // south, ZP
- } ;
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
- if (!a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[Meta].x, a_RelY, a_RelZ + Coords[Meta].z, BlockType, BlockMeta))
- {
- // Too close to the edge, cannot simulate
- return true;
- }
- return g_BlockIsSolid[BlockType];
- }
- }
- return true;
- }
-
- NIBBLETYPE FindMeta(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
- {
- NIBBLETYPE Meta = 0;
- char RailsCnt = 0;
- bool Neighbors[8]; // 0 - EAST, 1 - WEST, 2 - NORTH, 3 - SOUTH, 4 - EAST UP, 5 - WEST UP, 6 - NORTH UP, 7 - SOUTH UP
- memset(Neighbors, false, sizeof(Neighbors));
- Neighbors[0] = (IsUnstable(a_World, a_BlockX + 1, a_BlockY, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST, E_PURE_DOWN));
- Neighbors[1] = (IsUnstable(a_World, a_BlockX - 1, a_BlockY, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST, E_PURE_DOWN));
- Neighbors[2] = (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_DOWN));
- Neighbors[3] = (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_DOWN));
- Neighbors[4] = (IsUnstable(a_World, a_BlockX + 1, a_BlockY + 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_EAST, E_PURE_NONE));
- Neighbors[5] = (IsUnstable(a_World, a_BlockX - 1, a_BlockY + 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_WEST, E_PURE_NONE));
- Neighbors[6] = (IsUnstable(a_World, a_BlockX, a_BlockY + 1, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_NONE));
- Neighbors[7] = (IsUnstable(a_World, a_BlockX, a_BlockY + 1, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_NONE));
- if (IsUnstable(a_World, a_BlockX + 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_EAST))
- Neighbors[0] = true;
- if (IsUnstable(a_World, a_BlockX - 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_WEST))
- Neighbors[1] = true;
- if (IsUnstable(a_World, a_BlockX, a_BlockY - 1, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_NORTH))
- Neighbors[2] = true;
- if (IsUnstable(a_World, a_BlockX, a_BlockY - 1, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_SOUTH))
- Neighbors[3] = true;
- for (int i = 0; i < 8; i++)
- {
- if (Neighbors[i])
- {
- RailsCnt++;
- }
- }
- if (RailsCnt == 1)
- {
- if (Neighbors[7]) return E_RAIL_ASCEND_SOUTH;
- else if (Neighbors[6]) return E_RAIL_ASCEND_NORTH;
- else if (Neighbors[5]) return E_RAIL_ASCEND_WEST;
- else if (Neighbors[4]) return E_RAIL_ASCEND_EAST;
- else if (Neighbors[0] || Neighbors[1]) return E_RAIL_EAST_WEST;
- else if (Neighbors[2] || Neighbors[3]) return E_RAIL_NORTH_SOUTH;
- ASSERT(!"Weird neighbor count");
- return Meta;
- }
- for (int i = 0; i < 4; i++)
- {
- if (Neighbors[i + 4])
- {
- Neighbors[i] = true;
- }
- }
- if (RailsCnt > 1)
- {
- if (Neighbors[3] && Neighbors[0]) return E_RAIL_CURVED_SOUTH_EAST;
- else if (Neighbors[3] && Neighbors[1]) return E_RAIL_CURVED_SOUTH_WEST;
- else if (Neighbors[2] && Neighbors[0]) return E_RAIL_CURVED_NORTH_EAST;
- else if (Neighbors[2] && Neighbors[1]) return E_RAIL_CURVED_NORTH_WEST;
- else if (Neighbors[7] && Neighbors[2]) return E_RAIL_ASCEND_SOUTH;
- else if (Neighbors[3] && Neighbors[6]) return E_RAIL_ASCEND_NORTH;
- else if (Neighbors[5] && Neighbors[0]) return E_RAIL_ASCEND_WEST;
- else if (Neighbors[4] && Neighbors[1]) return E_RAIL_ASCEND_EAST;
- else if (Neighbors[0] && Neighbors[1]) return E_RAIL_EAST_WEST;
- else if (Neighbors[2] && Neighbors[3]) return E_RAIL_NORTH_SOUTH;
- ASSERT(!"Weird neighbor count");
- }
- return Meta;
- }
-
-
- bool IsUnstable(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
- {
- if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL)
- {
- return false;
- }
- NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
- switch (Meta)
- {
- case E_RAIL_NORTH_SOUTH:
- {
- if (
- IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_DOWN) ||
- IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_DOWN)
- )
- {
- return true;
- }
- break;
- }
-
- case E_RAIL_EAST_WEST:
- {
- if (
- IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST, E_PURE_DOWN) ||
- IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST, E_PURE_DOWN)
- )
- {
- return true;
- }
- break;
- }
-
- case E_RAIL_ASCEND_EAST:
- {
- if (
- IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_EAST) ||
- IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST)
- )
- {
- return true;
- }
- break;
- }
-
- case E_RAIL_ASCEND_WEST:
- {
- if (
- IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST) ||
- IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_WEST)
- )
- {
- return true;
- }
- break;
- }
-
- case E_RAIL_ASCEND_NORTH:
- {
- if (
- IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_NORTH) ||
- IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH)
- )
- {
- return true;
- }
- break;
- }
-
- case E_RAIL_ASCEND_SOUTH:
- {
- if (
- IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) ||
- IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_SOUTH)
- )
- {
- return true;
- }
- break;
- }
-
- case E_RAIL_CURVED_SOUTH_EAST:
- {
- if (
- IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) ||
- IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST)
- )
- {
- return true;
- }
- break;
- }
-
- case E_RAIL_CURVED_SOUTH_WEST:
- {
- if (
- IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) ||
- IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST)
- )
- {
- return true;
- }
- break;
- }
-
- case E_RAIL_CURVED_NORTH_WEST:
- {
- if (
- IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) ||
- IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST)
- )
- {
- return true;
- }
- break;
- }
-
- case E_RAIL_CURVED_NORTH_EAST:
- {
- if (
- IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) ||
- IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST)
- )
- {
- return true;
- }
- break;
- }
- }
- return false;
- }
-
-
- bool IsNotConnected(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Pure = 0)
- {
- AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, false);
- NIBBLETYPE Meta;
- if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL)
- {
- if ((a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure != E_PURE_UPDOWN))
- {
- if ((a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure == E_PURE_NONE))
- {
- return true;
- }
- else
- {
- Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ);
- }
- }
- else
- {
- Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY + 1, a_BlockZ);
- }
- }
- else
- {
- Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
- }
-
- switch (a_BlockFace)
- {
- case BLOCK_FACE_NORTH:
- {
- if (
- (Meta == E_RAIL_NORTH_SOUTH) ||
- (Meta == E_RAIL_ASCEND_NORTH) ||
- (Meta == E_RAIL_ASCEND_SOUTH) ||
- (Meta == E_RAIL_CURVED_SOUTH_EAST) ||
- (Meta == E_RAIL_CURVED_SOUTH_WEST)
- )
- {
- return false;
- }
- break;
- }
-
- case BLOCK_FACE_SOUTH:
- {
- if (
- (Meta == E_RAIL_NORTH_SOUTH) ||
- (Meta == E_RAIL_ASCEND_NORTH) ||
- (Meta == E_RAIL_ASCEND_SOUTH) ||
- (Meta == E_RAIL_CURVED_NORTH_EAST) ||
- (Meta == E_RAIL_CURVED_NORTH_WEST)
- )
- {
- return false;
- }
- break;
- }
-
- case BLOCK_FACE_EAST:
- {
- if (
- (Meta == E_RAIL_EAST_WEST) ||
- (Meta == E_RAIL_ASCEND_EAST) ||
- (Meta == E_RAIL_ASCEND_WEST) ||
- (Meta == E_RAIL_CURVED_SOUTH_WEST) ||
- (Meta == E_RAIL_CURVED_NORTH_WEST)
- )
- {
- return false;
- }
- break;
- }
- case BLOCK_FACE_WEST:
- {
- if (
- (Meta == E_RAIL_EAST_WEST) ||
- (Meta == E_RAIL_ASCEND_EAST) ||
- (Meta == E_RAIL_ASCEND_WEST) ||
- (Meta == E_RAIL_CURVED_SOUTH_EAST) ||
- (Meta == E_RAIL_CURVED_NORTH_EAST)
- )
- {
- return false;
- }
- break;
- }
- }
- return true;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockEntity.h"
+#include "../World.h"
+
+
+
+
+
+/// Meta values for the rail
+enum ENUM_RAIL_DIRECTIONS
+{
+ E_RAIL_NORTH_SOUTH = 0,
+ E_RAIL_EAST_WEST = 1,
+ E_RAIL_ASCEND_EAST = 2,
+ E_RAIL_ASCEND_WEST = 3,
+ E_RAIL_ASCEND_NORTH = 4,
+ E_RAIL_ASCEND_SOUTH = 5,
+ E_RAIL_CURVED_SOUTH_EAST = 6,
+ E_RAIL_CURVED_SOUTH_WEST = 7,
+ E_RAIL_CURVED_NORTH_WEST = 8,
+ E_RAIL_CURVED_NORTH_EAST = 9,
+
+ // Some useful synonyms:
+ E_RAIL_DIR_X = E_RAIL_EAST_WEST,
+ E_RAIL_DIR_Z = E_RAIL_NORTH_SOUTH,
+ E_RAIL_ASCEND_XP = E_RAIL_ASCEND_EAST,
+ E_RAIL_ASCEND_XM = E_RAIL_ASCEND_WEST,
+ E_RAIL_ASCEND_ZM = E_RAIL_ASCEND_NORTH,
+ E_RAIL_ASCEND_ZP = E_RAIL_ASCEND_SOUTH,
+ E_RAIL_CURVED_XPZP = E_RAIL_CURVED_SOUTH_EAST,
+ E_RAIL_CURVED_XMZP = E_RAIL_CURVED_SOUTH_WEST,
+ E_RAIL_CURVED_XMZM = E_RAIL_CURVED_NORTH_WEST,
+ E_RAIL_CURVED_XPZM = E_RAIL_CURVED_NORTH_EAST,
+} ;
+
+
+
+
+
+enum ENUM_PURE
+{
+ E_PURE_UPDOWN = 0,
+ E_PURE_DOWN = 1,
+ E_PURE_NONE = 2
+};
+
+
+
+
+
+class cBlockRailHandler :
+ public cBlockHandler
+{
+public:
+ cBlockRailHandler(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
+ {
+ a_BlockType = m_BlockType;
+ a_BlockMeta = FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ);
+ return true;
+ }
+
+
+ virtual void OnNeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ if (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ) && (Meta != FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ)))
+ {
+ a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ));
+ }
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ if (a_RelY <= 0)
+ {
+ return false;
+ }
+ if (!g_BlockIsSolid[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)])
+ {
+ return false;
+ }
+
+ NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
+ switch (Meta)
+ {
+ case E_RAIL_ASCEND_EAST:
+ case E_RAIL_ASCEND_WEST:
+ case E_RAIL_ASCEND_NORTH:
+ case E_RAIL_ASCEND_SOUTH:
+ {
+ // Mapping between the meta and the neighbors that need checking
+ Meta -= E_RAIL_ASCEND_EAST; // Base index at zero
+ static const struct
+ {
+ int x, z;
+ } Coords[] =
+ {
+ { 1, 0}, // east, XP
+ {-1, 0}, // west, XM
+ { 0, -1}, // north, ZM
+ { 0, 1}, // south, ZP
+ } ;
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (!a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[Meta].x, a_RelY, a_RelZ + Coords[Meta].z, BlockType, BlockMeta))
+ {
+ // Too close to the edge, cannot simulate
+ return true;
+ }
+ return g_BlockIsSolid[BlockType];
+ }
+ }
+ return true;
+ }
+
+ NIBBLETYPE FindMeta(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ NIBBLETYPE Meta = 0;
+ char RailsCnt = 0;
+ bool Neighbors[8]; // 0 - EAST, 1 - WEST, 2 - NORTH, 3 - SOUTH, 4 - EAST UP, 5 - WEST UP, 6 - NORTH UP, 7 - SOUTH UP
+ memset(Neighbors, false, sizeof(Neighbors));
+ Neighbors[0] = (IsUnstable(a_World, a_BlockX + 1, a_BlockY, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST, E_PURE_DOWN));
+ Neighbors[1] = (IsUnstable(a_World, a_BlockX - 1, a_BlockY, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST, E_PURE_DOWN));
+ Neighbors[2] = (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_DOWN));
+ Neighbors[3] = (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_DOWN));
+ Neighbors[4] = (IsUnstable(a_World, a_BlockX + 1, a_BlockY + 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_EAST, E_PURE_NONE));
+ Neighbors[5] = (IsUnstable(a_World, a_BlockX - 1, a_BlockY + 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_WEST, E_PURE_NONE));
+ Neighbors[6] = (IsUnstable(a_World, a_BlockX, a_BlockY + 1, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_NONE));
+ Neighbors[7] = (IsUnstable(a_World, a_BlockX, a_BlockY + 1, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_NONE));
+ if (IsUnstable(a_World, a_BlockX + 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_EAST))
+ Neighbors[0] = true;
+ if (IsUnstable(a_World, a_BlockX - 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_WEST))
+ Neighbors[1] = true;
+ if (IsUnstable(a_World, a_BlockX, a_BlockY - 1, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_NORTH))
+ Neighbors[2] = true;
+ if (IsUnstable(a_World, a_BlockX, a_BlockY - 1, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_SOUTH))
+ Neighbors[3] = true;
+ for (int i = 0; i < 8; i++)
+ {
+ if (Neighbors[i])
+ {
+ RailsCnt++;
+ }
+ }
+ if (RailsCnt == 1)
+ {
+ if (Neighbors[7]) return E_RAIL_ASCEND_SOUTH;
+ else if (Neighbors[6]) return E_RAIL_ASCEND_NORTH;
+ else if (Neighbors[5]) return E_RAIL_ASCEND_WEST;
+ else if (Neighbors[4]) return E_RAIL_ASCEND_EAST;
+ else if (Neighbors[0] || Neighbors[1]) return E_RAIL_EAST_WEST;
+ else if (Neighbors[2] || Neighbors[3]) return E_RAIL_NORTH_SOUTH;
+ ASSERT(!"Weird neighbor count");
+ return Meta;
+ }
+ for (int i = 0; i < 4; i++)
+ {
+ if (Neighbors[i + 4])
+ {
+ Neighbors[i] = true;
+ }
+ }
+ if (RailsCnt > 1)
+ {
+ if (Neighbors[3] && Neighbors[0]) return E_RAIL_CURVED_SOUTH_EAST;
+ else if (Neighbors[3] && Neighbors[1]) return E_RAIL_CURVED_SOUTH_WEST;
+ else if (Neighbors[2] && Neighbors[0]) return E_RAIL_CURVED_NORTH_EAST;
+ else if (Neighbors[2] && Neighbors[1]) return E_RAIL_CURVED_NORTH_WEST;
+ else if (Neighbors[7] && Neighbors[2]) return E_RAIL_ASCEND_SOUTH;
+ else if (Neighbors[3] && Neighbors[6]) return E_RAIL_ASCEND_NORTH;
+ else if (Neighbors[5] && Neighbors[0]) return E_RAIL_ASCEND_WEST;
+ else if (Neighbors[4] && Neighbors[1]) return E_RAIL_ASCEND_EAST;
+ else if (Neighbors[0] && Neighbors[1]) return E_RAIL_EAST_WEST;
+ else if (Neighbors[2] && Neighbors[3]) return E_RAIL_NORTH_SOUTH;
+ ASSERT(!"Weird neighbor count");
+ }
+ return Meta;
+ }
+
+
+ bool IsUnstable(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL)
+ {
+ return false;
+ }
+ NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ switch (Meta)
+ {
+ case E_RAIL_NORTH_SOUTH:
+ {
+ if (
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_DOWN) ||
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_DOWN)
+ )
+ {
+ return true;
+ }
+ break;
+ }
+
+ case E_RAIL_EAST_WEST:
+ {
+ if (
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST, E_PURE_DOWN) ||
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST, E_PURE_DOWN)
+ )
+ {
+ return true;
+ }
+ break;
+ }
+
+ case E_RAIL_ASCEND_EAST:
+ {
+ if (
+ IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_EAST) ||
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST)
+ )
+ {
+ return true;
+ }
+ break;
+ }
+
+ case E_RAIL_ASCEND_WEST:
+ {
+ if (
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST) ||
+ IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_WEST)
+ )
+ {
+ return true;
+ }
+ break;
+ }
+
+ case E_RAIL_ASCEND_NORTH:
+ {
+ if (
+ IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_NORTH) ||
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH)
+ )
+ {
+ return true;
+ }
+ break;
+ }
+
+ case E_RAIL_ASCEND_SOUTH:
+ {
+ if (
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) ||
+ IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_SOUTH)
+ )
+ {
+ return true;
+ }
+ break;
+ }
+
+ case E_RAIL_CURVED_SOUTH_EAST:
+ {
+ if (
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) ||
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST)
+ )
+ {
+ return true;
+ }
+ break;
+ }
+
+ case E_RAIL_CURVED_SOUTH_WEST:
+ {
+ if (
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) ||
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST)
+ )
+ {
+ return true;
+ }
+ break;
+ }
+
+ case E_RAIL_CURVED_NORTH_WEST:
+ {
+ if (
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) ||
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST)
+ )
+ {
+ return true;
+ }
+ break;
+ }
+
+ case E_RAIL_CURVED_NORTH_EAST:
+ {
+ if (
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) ||
+ IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST)
+ )
+ {
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+ }
+
+
+ bool IsNotConnected(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Pure = 0)
+ {
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, false);
+ NIBBLETYPE Meta;
+ if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL)
+ {
+ if ((a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure != E_PURE_UPDOWN))
+ {
+ if ((a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure == E_PURE_NONE))
+ {
+ return true;
+ }
+ else
+ {
+ Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ);
+ }
+ }
+ else
+ {
+ Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY + 1, a_BlockZ);
+ }
+ }
+ else
+ {
+ Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ }
+
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_NORTH:
+ {
+ if (
+ (Meta == E_RAIL_NORTH_SOUTH) ||
+ (Meta == E_RAIL_ASCEND_NORTH) ||
+ (Meta == E_RAIL_ASCEND_SOUTH) ||
+ (Meta == E_RAIL_CURVED_SOUTH_EAST) ||
+ (Meta == E_RAIL_CURVED_SOUTH_WEST)
+ )
+ {
+ return false;
+ }
+ break;
+ }
+
+ case BLOCK_FACE_SOUTH:
+ {
+ if (
+ (Meta == E_RAIL_NORTH_SOUTH) ||
+ (Meta == E_RAIL_ASCEND_NORTH) ||
+ (Meta == E_RAIL_ASCEND_SOUTH) ||
+ (Meta == E_RAIL_CURVED_NORTH_EAST) ||
+ (Meta == E_RAIL_CURVED_NORTH_WEST)
+ )
+ {
+ return false;
+ }
+ break;
+ }
+
+ case BLOCK_FACE_EAST:
+ {
+ if (
+ (Meta == E_RAIL_EAST_WEST) ||
+ (Meta == E_RAIL_ASCEND_EAST) ||
+ (Meta == E_RAIL_ASCEND_WEST) ||
+ (Meta == E_RAIL_CURVED_SOUTH_WEST) ||
+ (Meta == E_RAIL_CURVED_NORTH_WEST)
+ )
+ {
+ return false;
+ }
+ break;
+ }
+ case BLOCK_FACE_WEST:
+ {
+ if (
+ (Meta == E_RAIL_EAST_WEST) ||
+ (Meta == E_RAIL_ASCEND_EAST) ||
+ (Meta == E_RAIL_ASCEND_WEST) ||
+ (Meta == E_RAIL_CURVED_SOUTH_EAST) ||
+ (Meta == E_RAIL_CURVED_NORTH_EAST)
+ )
+ {
+ return false;
+ }
+ break;
+ }
+ }
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockRedstone.cpp b/source/Blocks/BlockRedstone.cpp
index f433be4ef..35cdc34cf 100644
--- a/source/Blocks/BlockRedstone.cpp
+++ b/source/Blocks/BlockRedstone.cpp
@@ -1,27 +1,27 @@
-
-#include "Globals.h"
-#include "BlockRedstone.h"
-#include "../Item.h"
-#include "../World.h"
-
-
-
-
-
-cBlockRedstoneHandler::cBlockRedstoneHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
-{
-}
-
-
-
-
-
-void cBlockRedstoneHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
-{
- // Nothing needed yet
-}
-
-
-
-
+
+#include "Globals.h"
+#include "BlockRedstone.h"
+#include "../Item.h"
+#include "../World.h"
+
+
+
+
+
+cBlockRedstoneHandler::cBlockRedstoneHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+{
+}
+
+
+
+
+
+void cBlockRedstoneHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ // Nothing needed yet
+}
+
+
+
+
diff --git a/source/Blocks/BlockRedstone.h b/source/Blocks/BlockRedstone.h
index 3a4649d7e..ae0466937 100644
--- a/source/Blocks/BlockRedstone.h
+++ b/source/Blocks/BlockRedstone.h
@@ -1,46 +1,46 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-#include "../World.h"
-
-
-
-
-
-class cBlockRedstoneHandler :
- public cBlockHandler
-{
-public:
- cBlockRedstoneHandler(BLOCKTYPE a_BlockType);
-
- virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override;
-
- virtual bool DoesAllowBlockOnTop(void) override
- {
- return false;
- }
-
-
- virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
- {
- return ((a_RelY > 0) && g_BlockIsSolid[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]);
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- // Reset meta to 0
- a_Pickups.push_back(cItem(E_ITEM_REDSTONE_DUST, 1));
- }
-
-
- virtual bool CanBePlacedOnSide(void) override
- {
- return false;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../World.h"
+
+
+
+
+
+class cBlockRedstoneHandler :
+ public cBlockHandler
+{
+public:
+ cBlockRedstoneHandler(BLOCKTYPE a_BlockType);
+
+ virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override;
+
+ virtual bool DoesAllowBlockOnTop(void) override
+ {
+ return false;
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ return ((a_RelY > 0) && g_BlockIsSolid[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]);
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Reset meta to 0
+ a_Pickups.push_back(cItem(E_ITEM_REDSTONE_DUST, 1));
+ }
+
+
+ virtual bool CanBePlacedOnSide(void) override
+ {
+ return false;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockRedstoneRepeater.cpp b/source/Blocks/BlockRedstoneRepeater.cpp
index 15b736cbc..9857dd223 100644
--- a/source/Blocks/BlockRedstoneRepeater.cpp
+++ b/source/Blocks/BlockRedstoneRepeater.cpp
@@ -1,47 +1,47 @@
-
-#include "Globals.h"
-#include "BlockRedstoneRepeater.h"
-#include "../Item.h"
-#include "../World.h"
-#include "../Player.h"
-#include "../Simulator/RedstoneSimulator.h"
-
-
-
-
-
-cBlockRedstoneRepeaterHandler::cBlockRedstoneRepeaterHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
-{
-}
-
-
-
-
-
-void cBlockRedstoneRepeaterHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
-{
- // Nothing needed yet
-}
-
-
-
-
-
-void cBlockRedstoneRepeaterHandler::OnUse(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)
-{
- a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) + 0x04) & 0x0f));
-}
-
-
-
-
-
-void cBlockRedstoneRepeaterHandler::OnDigging(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
-{
- OnUse(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NONE, 8, 8, 8);
-}
-
-
-
-
+
+#include "Globals.h"
+#include "BlockRedstoneRepeater.h"
+#include "../Item.h"
+#include "../World.h"
+#include "../Player.h"
+#include "../Simulator/RedstoneSimulator.h"
+
+
+
+
+
+cBlockRedstoneRepeaterHandler::cBlockRedstoneRepeaterHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+{
+}
+
+
+
+
+
+void cBlockRedstoneRepeaterHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ // Nothing needed yet
+}
+
+
+
+
+
+void cBlockRedstoneRepeaterHandler::OnUse(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)
+{
+ a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) + 0x04) & 0x0f));
+}
+
+
+
+
+
+void cBlockRedstoneRepeaterHandler::OnDigging(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ OnUse(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NONE, 8, 8, 8);
+}
+
+
+
+
diff --git a/source/Blocks/BlockRedstoneRepeater.h b/source/Blocks/BlockRedstoneRepeater.h
index 4a16149fd..f3e250963 100644
--- a/source/Blocks/BlockRedstoneRepeater.h
+++ b/source/Blocks/BlockRedstoneRepeater.h
@@ -1,60 +1,60 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-#include "../World.h"
-
-
-
-
-
-class cBlockRedstoneRepeaterHandler :
- public cBlockHandler
-{
-public:
- cBlockRedstoneRepeaterHandler(BLOCKTYPE a_BlockType);
- virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override;
-
- virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override;
- virtual void OnUse(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) override;
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- // Reset meta to 0
- a_Pickups.push_back(cItem(E_ITEM_REDSTONE_REPEATER, 1, 0));
- }
-
-
- virtual bool IsUseable(void) override
- {
- return true;
- }
-
-
- virtual bool DoesAllowBlockOnTop(void) override
- {
- return false;
- }
-
-
- virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
- {
- return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR));
- }
-
-
- virtual bool CanBePlacedOnSide(void) override
- {
- return false;
- }
-
- virtual const char * GetStepSound(void) override
- {
- return "step.wood";
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../World.h"
+
+
+
+
+
+class cBlockRedstoneRepeaterHandler :
+ public cBlockHandler
+{
+public:
+ cBlockRedstoneRepeaterHandler(BLOCKTYPE a_BlockType);
+ virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override;
+
+ virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override;
+ virtual void OnUse(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) override;
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Reset meta to 0
+ a_Pickups.push_back(cItem(E_ITEM_REDSTONE_REPEATER, 1, 0));
+ }
+
+
+ virtual bool IsUseable(void) override
+ {
+ return true;
+ }
+
+
+ virtual bool DoesAllowBlockOnTop(void) override
+ {
+ return false;
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR));
+ }
+
+
+ virtual bool CanBePlacedOnSide(void) override
+ {
+ return false;
+ }
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockRedstoneTorch.h b/source/Blocks/BlockRedstoneTorch.h
index c8256ce64..cb897ba3f 100644
--- a/source/Blocks/BlockRedstoneTorch.h
+++ b/source/Blocks/BlockRedstoneTorch.h
@@ -1,36 +1,36 @@
-
-#pragma once
-
-#include "BlockRedstone.h"
-#include "BlockTorch.h"
-
-
-
-
-
-class cBlockRedstoneTorchHandler :
- public cBlockTorchHandler
-{
-public:
- cBlockRedstoneTorchHandler(BLOCKTYPE a_BlockType)
- : cBlockTorchHandler(a_BlockType)
- {
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- // Always drop the ON torch, meta 0
- a_Pickups.push_back(cItem(E_BLOCK_REDSTONE_TORCH_ON, 1, 0));
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.wood";
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockRedstone.h"
+#include "BlockTorch.h"
+
+
+
+
+
+class cBlockRedstoneTorchHandler :
+ public cBlockTorchHandler
+{
+public:
+ cBlockRedstoneTorchHandler(BLOCKTYPE a_BlockType)
+ : cBlockTorchHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Always drop the ON torch, meta 0
+ a_Pickups.push_back(cItem(E_BLOCK_REDSTONE_TORCH_ON, 1, 0));
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockSand.h b/source/Blocks/BlockSand.h
index 878770d22..3fc271483 100644
--- a/source/Blocks/BlockSand.h
+++ b/source/Blocks/BlockSand.h
@@ -1,28 +1,28 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockSandHandler :
- public cBlockHandler
-{
-public:
- cBlockSandHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
- virtual const char * GetStepSound(void) override
- {
- return "step.sand";
- }
-
-};
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockSandHandler :
+ public cBlockHandler
+{
+public:
+ cBlockSandHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.sand";
+ }
+
+};
+
+
+
+
diff --git a/source/Blocks/BlockSapling.h b/source/Blocks/BlockSapling.h
index 957331886..17ef4984f 100644
--- a/source/Blocks/BlockSapling.h
+++ b/source/Blocks/BlockSapling.h
@@ -1,69 +1,69 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-#include "../World.h"
-
-
-
-
-
-class cBlockSaplingHandler :
- public cBlockHandler
-{
-public:
- cBlockSaplingHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- // Only the first 2 bits contain the display information, the others are for growing
- a_Pickups.push_back(cItem(E_BLOCK_SAPLING, 1, a_BlockMeta & 3));
- }
-
-
- virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
- {
- return (a_RelY > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ));
- }
-
-
- virtual bool DoesAllowBlockOnTop(void) override
- {
- return false;
- }
-
-
- void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
- {
- NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
-
- if ((Meta & 0x08) != 0)
- {
- a_World->GrowTree(a_BlockX, a_BlockY, a_BlockZ);
- }
- else
- {
- a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta | 0x08);
- }
- }
-
-
- virtual bool CanBePlacedOnSide() override
- {
- return false;
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.grass";
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../World.h"
+
+
+
+
+
+class cBlockSaplingHandler :
+ public cBlockHandler
+{
+public:
+ cBlockSaplingHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Only the first 2 bits contain the display information, the others are for growing
+ a_Pickups.push_back(cItem(E_BLOCK_SAPLING, 1, a_BlockMeta & 3));
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ return (a_RelY > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ));
+ }
+
+
+ virtual bool DoesAllowBlockOnTop(void) override
+ {
+ return false;
+ }
+
+
+ void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+
+ if ((Meta & 0x08) != 0)
+ {
+ a_World->GrowTree(a_BlockX, a_BlockY, a_BlockZ);
+ }
+ else
+ {
+ a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta | 0x08);
+ }
+ }
+
+
+ virtual bool CanBePlacedOnSide() override
+ {
+ return false;
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.grass";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockSign.h b/source/Blocks/BlockSign.h
index 4bdccb47e..09fa721b6 100644
--- a/source/Blocks/BlockSign.h
+++ b/source/Blocks/BlockSign.h
@@ -1,73 +1,84 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-#include "../World.h"
-#include "../Player.h"
-
-
-
-
-
-class cBlockSignHandler :
- public cBlockHandler
-{
-public:
- cBlockSignHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- a_Pickups.push_back(cItem(E_ITEM_SIGN, 1, 0));
- }
-
-
- virtual bool DoesAllowBlockOnTop(void) override
- {
- return false;
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.wood";
- }
-
-
- static char RotationToMetaData(double a_Rotation)
- {
- a_Rotation += 180 + (180 / 16); // So it's not aligned with axis
- if (a_Rotation > 360)
- {
- a_Rotation -= 360;
- }
-
- a_Rotation = (a_Rotation / 360) * 16;
-
- return ((char)a_Rotation) % 16;
- }
-
-
- static char DirectionToMetaData(char a_Direction)
- {
- switch (a_Direction)
- {
- case 0x2: return 0x2;
- case 0x3: return 0x3;
- case 0x4: return 0x4;
- case 0x5: return 0x5;
- default:
- {
- break;
- }
- }
- return 0x2;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../World.h"
+#include "../Player.h"
+
+
+
+
+
+class cBlockSignHandler :
+ public cBlockHandler
+{
+public:
+ cBlockSignHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_ITEM_SIGN, 1, 0));
+ }
+
+
+ virtual bool DoesAllowBlockOnTop(void) override
+ {
+ return false;
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+
+
+ static char RotationToMetaData(double a_Rotation)
+ {
+ a_Rotation += 180 + (180 / 16); // So it's not aligned with axis
+ if (a_Rotation > 360)
+ {
+ a_Rotation -= 360;
+ }
+
+ a_Rotation = (a_Rotation / 360) * 16;
+
+ return ((char)a_Rotation) % 16;
+ }
+
+
+ static char DirectionToMetaData(char a_Direction)
+ {
+ switch (a_Direction)
+ {
+ case 0x2: return 0x2;
+ case 0x3: return 0x3;
+ case 0x4: return 0x4;
+ case 0x5: return 0x5;
+ default:
+ {
+ break;
+ }
+ }
+ return 0x2;
+ }
+
+
+ virtual void OnPlacedByPlayer(
+ 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
+ {
+ a_Player->GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockSlab.h b/source/Blocks/BlockSlab.h
index 2abcc6364..f34f42bae 100644
--- a/source/Blocks/BlockSlab.h
+++ b/source/Blocks/BlockSlab.h
@@ -1,70 +1,70 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockSlabHandler :
- public cBlockHandler
-{
-public:
- cBlockSlabHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- char Count = ((m_BlockType == E_BLOCK_DOUBLE_STONE_SLAB) || (m_BlockType == E_BLOCK_DOUBLE_WOODEN_SLAB)) ? 2 : 1;
- a_Pickups.push_back(cItem(m_BlockType, Count, a_BlockMeta));
- }
-
-
- 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
- {
- a_BlockType = m_BlockType;
- NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x07);
- switch (a_BlockFace)
- {
- case BLOCK_FACE_TOP: a_BlockMeta = Meta & 0x7; break; // Always bottom half of the slab when placing on top of something
- case BLOCK_FACE_BOTTOM: a_BlockMeta = Meta | 0x8; break; // Always top half of the slab when placing on bottom of something
- case BLOCK_FACE_EAST:
- case BLOCK_FACE_NORTH:
- case BLOCK_FACE_SOUTH:
- case BLOCK_FACE_WEST:
- {
- if (a_CursorY > 7)
- {
- // Cursor at the top half of the face, place a top half of slab
- a_BlockMeta = Meta | 0x8;
- }
- else
- {
- // Cursor at the bottom half of the face, place a bottom half of slab:
- a_BlockMeta = Meta & 0x7;
- }
- break;
- }
- } // switch (a_BlockFace)
- return true;
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return ((m_BlockType == E_BLOCK_WOODEN_SLAB) || (m_BlockType == E_BLOCK_DOUBLE_WOODEN_SLAB)) ? "step.wood" : "step.stone";
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockSlabHandler :
+ public cBlockHandler
+{
+public:
+ cBlockSlabHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ char Count = ((m_BlockType == E_BLOCK_DOUBLE_STONE_SLAB) || (m_BlockType == E_BLOCK_DOUBLE_WOODEN_SLAB)) ? 2 : 1;
+ a_Pickups.push_back(cItem(m_BlockType, Count, a_BlockMeta));
+ }
+
+
+ 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
+ {
+ a_BlockType = m_BlockType;
+ NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x07);
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_TOP: a_BlockMeta = Meta & 0x7; break; // Always bottom half of the slab when placing on top of something
+ case BLOCK_FACE_BOTTOM: a_BlockMeta = Meta | 0x8; break; // Always top half of the slab when placing on bottom of something
+ case BLOCK_FACE_EAST:
+ case BLOCK_FACE_NORTH:
+ case BLOCK_FACE_SOUTH:
+ case BLOCK_FACE_WEST:
+ {
+ if (a_CursorY > 7)
+ {
+ // Cursor at the top half of the face, place a top half of slab
+ a_BlockMeta = Meta | 0x8;
+ }
+ else
+ {
+ // Cursor at the bottom half of the face, place a bottom half of slab:
+ a_BlockMeta = Meta & 0x7;
+ }
+ break;
+ }
+ } // switch (a_BlockFace)
+ return true;
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return ((m_BlockType == E_BLOCK_WOODEN_SLAB) || (m_BlockType == E_BLOCK_DOUBLE_WOODEN_SLAB)) ? "step.wood" : "step.stone";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockSnow.h b/source/Blocks/BlockSnow.h
index f150f497c..bdd9f0b87 100644
--- a/source/Blocks/BlockSnow.h
+++ b/source/Blocks/BlockSnow.h
@@ -1,52 +1,52 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockSnowHandler :
- public cBlockHandler
-{
-public:
- cBlockSnowHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual bool DoesIgnoreBuildCollision(void) override
- {
- return true;
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- a_Pickups.push_back(cItem(E_ITEM_SNOWBALL, 1, 0));
- }
-
-
- virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
- {
- return (a_RelY > 0) && g_BlockIsSnowable[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)];
- }
-
-
- virtual bool DoesDropOnUnsuitable(void) override
- {
- return false;
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.cloth";
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockSnowHandler :
+ public cBlockHandler
+{
+public:
+ cBlockSnowHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual bool DoesIgnoreBuildCollision(void) override
+ {
+ return true;
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_ITEM_SNOWBALL, 1, 0));
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ return (a_RelY > 0) && g_BlockIsSnowable[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)];
+ }
+
+
+ virtual bool DoesDropOnUnsuitable(void) override
+ {
+ return false;
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.cloth";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockStairs.h b/source/Blocks/BlockStairs.h
index 6fc316e45..485ebda1a 100644
--- a/source/Blocks/BlockStairs.h
+++ b/source/Blocks/BlockStairs.h
@@ -1,153 +1,153 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockStairsHandler :
- public cBlockHandler
-{
-public:
- cBlockStairsHandler(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
- {
- a_BlockType = m_BlockType;
- a_BlockMeta = RotationToMetaData(a_Player->GetRotation());
- switch (a_BlockFace)
- {
- case BLOCK_FACE_TOP: break;
- case BLOCK_FACE_BOTTOM: a_BlockMeta = a_BlockMeta | 0x4; break; // When placing onto a bottom face, always place an upside-down stairs block
- case BLOCK_FACE_EAST:
- case BLOCK_FACE_NORTH:
- case BLOCK_FACE_SOUTH:
- case BLOCK_FACE_WEST:
- {
- // When placing onto a sideways face, check cursor, if in top half, make it an upside-down stairs block
- if (a_CursorY > 8)
- {
- a_BlockMeta |= 0x4;
- }
- break;
- }
- }
- return true;
- }
-
- // TODO: step sound
-
-
- static NIBBLETYPE RotationToMetaData(double a_Rotation)
- {
- a_Rotation += 90 + 45; // So its not aligned with axis
- NIBBLETYPE result = 0x0;
- if (a_Rotation > 360)
- {
- a_Rotation -= 360;
- }
- if ((a_Rotation >= 0) && (a_Rotation < 90))
- {
- return 0x0;
- }
- else if ((a_Rotation >= 180) && (a_Rotation < 270))
- {
- return 0x1;
- }
- else if ((a_Rotation >= 90) && (a_Rotation < 180))
- {
- return 0x2;
- }
- else
- {
- return 0x3;
- }
- }
-
-
- virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override
- {
- // Bits 3 and 4 stay, the rest is swapped around according to a table:
- NIBBLETYPE TopBits = (a_Meta & 0x0c);
- switch (a_Meta & 0x03)
- {
- case 0x00: return TopBits | 0x03; // East -> North
- case 0x01: return TopBits | 0x02; // West -> South
- case 0x02: return TopBits | 0x00; // South -> East
- case 0x03: return TopBits | 0x01; // North -> West
- }
- // Not reachable, but to avoid a compiler warning:
- return 0;
- }
-
-
- virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override
- {
- // Bits 3 and 4 stay, the rest is swapped around according to a table:
- NIBBLETYPE TopBits = (a_Meta & 0x0c);
- switch (a_Meta & 0x03)
- {
- case 0x00: return TopBits | 0x02; // East -> South
- case 0x01: return TopBits | 0x03; // West -> North
- case 0x02: return TopBits | 0x01; // South -> West
- case 0x03: return TopBits | 0x00; // North -> East
- }
- // Not reachable, but to avoid a compiler warning:
- return 0;
- }
-
-
- virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override
- {
- // Bits 3 and 4 stay, the rest is swapped around according to a table:
- NIBBLETYPE TopBits = (a_Meta & 0x0c);
- switch (a_Meta & 0x03)
- {
- case 0x00: return TopBits | 0x00; // East -> East
- case 0x01: return TopBits | 0x01; // West -> West
- case 0x02: return TopBits | 0x03; // South -> North
- case 0x03: return TopBits | 0x02; // North -> South
- }
- // Not reachable, but to avoid a compiler warning:
- return 0;
- }
-
-
- virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override
- {
- // Toggle bit 3:
- return (a_Meta & 0x0b) | ((~a_Meta) & 0x04);
- }
-
-
- virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override
- {
- // Bits 3 and 4 stay, the rest is swapped around according to a table:
- NIBBLETYPE TopBits = (a_Meta & 0x0c);
- switch (a_Meta & 0x03)
- {
- case 0x00: return TopBits | 0x01; // East -> West
- case 0x01: return TopBits | 0x00; // West -> East
- case 0x02: return TopBits | 0x02; // South -> South
- case 0x03: return TopBits | 0x03; // North -> North
- }
- // Not reachable, but to avoid a compiler warning:
- return 0;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockStairsHandler :
+ public cBlockHandler
+{
+public:
+ cBlockStairsHandler(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
+ {
+ a_BlockType = m_BlockType;
+ a_BlockMeta = RotationToMetaData(a_Player->GetRotation());
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_TOP: break;
+ case BLOCK_FACE_BOTTOM: a_BlockMeta = a_BlockMeta | 0x4; break; // When placing onto a bottom face, always place an upside-down stairs block
+ case BLOCK_FACE_EAST:
+ case BLOCK_FACE_NORTH:
+ case BLOCK_FACE_SOUTH:
+ case BLOCK_FACE_WEST:
+ {
+ // When placing onto a sideways face, check cursor, if in top half, make it an upside-down stairs block
+ if (a_CursorY > 8)
+ {
+ a_BlockMeta |= 0x4;
+ }
+ break;
+ }
+ }
+ return true;
+ }
+
+ // TODO: step sound
+
+
+ static NIBBLETYPE RotationToMetaData(double a_Rotation)
+ {
+ a_Rotation += 90 + 45; // So its not aligned with axis
+ NIBBLETYPE result = 0x0;
+ if (a_Rotation > 360)
+ {
+ a_Rotation -= 360;
+ }
+ if ((a_Rotation >= 0) && (a_Rotation < 90))
+ {
+ return 0x0;
+ }
+ else if ((a_Rotation >= 180) && (a_Rotation < 270))
+ {
+ return 0x1;
+ }
+ else if ((a_Rotation >= 90) && (a_Rotation < 180))
+ {
+ return 0x2;
+ }
+ else
+ {
+ return 0x3;
+ }
+ }
+
+
+ virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override
+ {
+ // Bits 3 and 4 stay, the rest is swapped around according to a table:
+ NIBBLETYPE TopBits = (a_Meta & 0x0c);
+ switch (a_Meta & 0x03)
+ {
+ case 0x00: return TopBits | 0x03; // East -> North
+ case 0x01: return TopBits | 0x02; // West -> South
+ case 0x02: return TopBits | 0x00; // South -> East
+ case 0x03: return TopBits | 0x01; // North -> West
+ }
+ // Not reachable, but to avoid a compiler warning:
+ return 0;
+ }
+
+
+ virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override
+ {
+ // Bits 3 and 4 stay, the rest is swapped around according to a table:
+ NIBBLETYPE TopBits = (a_Meta & 0x0c);
+ switch (a_Meta & 0x03)
+ {
+ case 0x00: return TopBits | 0x02; // East -> South
+ case 0x01: return TopBits | 0x03; // West -> North
+ case 0x02: return TopBits | 0x01; // South -> West
+ case 0x03: return TopBits | 0x00; // North -> East
+ }
+ // Not reachable, but to avoid a compiler warning:
+ return 0;
+ }
+
+
+ virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override
+ {
+ // Bits 3 and 4 stay, the rest is swapped around according to a table:
+ NIBBLETYPE TopBits = (a_Meta & 0x0c);
+ switch (a_Meta & 0x03)
+ {
+ case 0x00: return TopBits | 0x00; // East -> East
+ case 0x01: return TopBits | 0x01; // West -> West
+ case 0x02: return TopBits | 0x03; // South -> North
+ case 0x03: return TopBits | 0x02; // North -> South
+ }
+ // Not reachable, but to avoid a compiler warning:
+ return 0;
+ }
+
+
+ virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override
+ {
+ // Toggle bit 3:
+ return (a_Meta & 0x0b) | ((~a_Meta) & 0x04);
+ }
+
+
+ virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override
+ {
+ // Bits 3 and 4 stay, the rest is swapped around according to a table:
+ NIBBLETYPE TopBits = (a_Meta & 0x0c);
+ switch (a_Meta & 0x03)
+ {
+ case 0x00: return TopBits | 0x01; // East -> West
+ case 0x01: return TopBits | 0x00; // West -> East
+ case 0x02: return TopBits | 0x02; // South -> South
+ case 0x03: return TopBits | 0x03; // North -> North
+ }
+ // Not reachable, but to avoid a compiler warning:
+ return 0;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockStems.h b/source/Blocks/BlockStems.h
index 44c4d9cc9..ce02d9cb8 100644
--- a/source/Blocks/BlockStems.h
+++ b/source/Blocks/BlockStems.h
@@ -1,58 +1,58 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-#include "../MersenneTwister.h"
-#include "../World.h"
-
-
-
-
-
-class cBlockStemsHandler :
- public cBlockHandler
-{
-public:
- cBlockStemsHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- int ItemType = (m_BlockType == E_BLOCK_MELON_STEM) ? E_ITEM_MELON_SEEDS : E_ITEM_PUMPKIN_SEEDS;
- a_Pickups.push_back(cItem(ItemType, 1, 0));
- }
-
-
- void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
- {
- NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
- if (Meta >= 7)
- {
- // Grow the produce:
- a_World->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, m_BlockType);
- }
- else
- {
- // Grow the stem:
- a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta + 1);
- }
- }
-
-
- virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
- {
- return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_FARMLAND));
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.wood";
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../MersenneTwister.h"
+#include "../World.h"
+
+
+
+
+
+class cBlockStemsHandler :
+ public cBlockHandler
+{
+public:
+ cBlockStemsHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ int ItemType = (m_BlockType == E_BLOCK_MELON_STEM) ? E_ITEM_MELON_SEEDS : E_ITEM_PUMPKIN_SEEDS;
+ a_Pickups.push_back(cItem(ItemType, 1, 0));
+ }
+
+
+ void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
+ if (Meta >= 7)
+ {
+ // Grow the produce:
+ a_World->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, m_BlockType);
+ }
+ else
+ {
+ // Grow the stem:
+ a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta + 1);
+ }
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_FARMLAND));
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockStone.h b/source/Blocks/BlockStone.h
index 89bef5969..af4c6509a 100644
--- a/source/Blocks/BlockStone.h
+++ b/source/Blocks/BlockStone.h
@@ -1,29 +1,29 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-#include "../MersenneTwister.h"
-#include "../World.h"
-
-
-
-
-
-class cBlockStoneHandler :
- public cBlockHandler
-{
-public:
- cBlockStoneHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- a_Pickups.push_back(cItem(E_BLOCK_COBBLESTONE, 1, 0));
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../MersenneTwister.h"
+#include "../World.h"
+
+
+
+
+
+class cBlockStoneHandler :
+ public cBlockHandler
+{
+public:
+ cBlockStoneHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_BLOCK_COBBLESTONE, 1, 0));
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockSugarcane.h b/source/Blocks/BlockSugarcane.h
index a9503ba37..9d66d6be6 100644
--- a/source/Blocks/BlockSugarcane.h
+++ b/source/Blocks/BlockSugarcane.h
@@ -1,96 +1,96 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockSugarcaneHandler :
- public cBlockHandler
-{
-public:
- cBlockSugarcaneHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- a_Pickups.push_back(cItem(E_ITEM_SUGARCANE, 1, 0));
- }
-
-
- virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
- {
- if (a_RelY <= 0)
- {
- return false;
- }
- switch (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ))
- {
- case E_BLOCK_DIRT:
- case E_BLOCK_GRASS:
- case E_BLOCK_FARMLAND:
- case E_BLOCK_SAND:
- {
- static const struct
- {
- int x, z;
- } Coords[] =
- {
- {-1, 0},
- { 1, 0},
- { 0, -1},
- { 0, 1},
- } ;
- a_RelY -= 1;
- for (int i = 0; i < ARRAYCOUNT(Coords); i++)
- {
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
- if (!a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta))
- {
- // Too close to the edge, cannot simulate
- return true;
- }
- if (IsBlockWater(BlockType))
- {
- return true;
- }
- } // for i - Coords[]
- // Not directly neighboring a water block
- return false;
- }
- case E_BLOCK_SUGARCANE:
- {
- return true;
- }
- }
- return false;
- }
-
-
- void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
- {
- a_World->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, 1);
- }
-
-
- virtual bool CanBePlacedOnSide() override
- {
- return false;
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.grass";
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockSugarcaneHandler :
+ public cBlockHandler
+{
+public:
+ cBlockSugarcaneHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_ITEM_SUGARCANE, 1, 0));
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ if (a_RelY <= 0)
+ {
+ return false;
+ }
+ switch (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ))
+ {
+ case E_BLOCK_DIRT:
+ case E_BLOCK_GRASS:
+ case E_BLOCK_FARMLAND:
+ case E_BLOCK_SAND:
+ {
+ static const struct
+ {
+ int x, z;
+ } Coords[] =
+ {
+ {-1, 0},
+ { 1, 0},
+ { 0, -1},
+ { 0, 1},
+ } ;
+ a_RelY -= 1;
+ for (int i = 0; i < ARRAYCOUNT(Coords); i++)
+ {
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (!a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta))
+ {
+ // Too close to the edge, cannot simulate
+ return true;
+ }
+ if (IsBlockWater(BlockType))
+ {
+ return true;
+ }
+ } // for i - Coords[]
+ // Not directly neighboring a water block
+ return false;
+ }
+ case E_BLOCK_SUGARCANE:
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
+ {
+ a_World->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, 1);
+ }
+
+
+ virtual bool CanBePlacedOnSide() override
+ {
+ return false;
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.grass";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockTallGrass.h b/source/Blocks/BlockTallGrass.h
index eea629bf5..cd27ab7e6 100644
--- a/source/Blocks/BlockTallGrass.h
+++ b/source/Blocks/BlockTallGrass.h
@@ -1,51 +1,51 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockTallGrassHandler :
- public cBlockHandler
-{
-public:
- cBlockTallGrassHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual bool DoesIgnoreBuildCollision(void) override
- {
- return true;
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- // Drop seeds, sometimes
- MTRand r1;
- if (r1.randInt(10) == 5)
- {
- a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1, 0));
- }
- }
-
-
- virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
- {
- return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR));
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.grass";
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockTallGrassHandler :
+ public cBlockHandler
+{
+public:
+ cBlockTallGrassHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual bool DoesIgnoreBuildCollision(void) override
+ {
+ return true;
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Drop seeds, sometimes
+ MTRand r1;
+ if (r1.randInt(10) == 5)
+ {
+ a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1, 0));
+ }
+ }
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR));
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.grass";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockTorch.h b/source/Blocks/BlockTorch.h
index 9951b8ea4..00e0a585c 100644
--- a/source/Blocks/BlockTorch.h
+++ b/source/Blocks/BlockTorch.h
@@ -1,259 +1,259 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-#include "../World.h"
-
-
-
-
-
-class cBlockTorchHandler :
- public cBlockHandler
-{
-public:
- cBlockTorchHandler(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
- {
- // Find proper placement. Use the player-supplied one as the default, but fix if not okay:
- if (!TorchCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace))
- {
- a_BlockFace = FindSuitableFace(a_World, a_BlockX, a_BlockY, a_BlockZ);
-
- if (a_BlockFace == BLOCK_FACE_BOTTOM)
- {
- return false;
- }
- }
- a_BlockType = m_BlockType;
- a_BlockMeta = DirectionToMetaData(a_BlockFace);
- return true;
- }
-
-
- static NIBBLETYPE DirectionToMetaData(char a_Direction) // tolua_export
- { // tolua_export
- switch (a_Direction)
- {
- case BLOCK_FACE_BOTTOM: ASSERT(!"Shouldn't be getting this face"); return 0;
- case BLOCK_FACE_TOP: return E_META_TORCH_FLOOR;
- case BLOCK_FACE_EAST: return E_META_TORCH_EAST;
- case BLOCK_FACE_WEST: return E_META_TORCH_WEST;
- case BLOCK_FACE_NORTH: return E_META_TORCH_NORTH;
- case BLOCK_FACE_SOUTH: return E_META_TORCH_SOUTH;
- default:
- {
- ASSERT(!"Unhandled torch direction!");
- break;
- }
- };
- return 0x0;
- } // tolua_export
-
-
- static char MetaDataToDirection(NIBBLETYPE a_MetaData) // tolua_export
- { // tolua_export
- switch (a_MetaData)
- {
- case 0: return BLOCK_FACE_TOP; // by default, the torches stand on the ground
- case E_META_TORCH_FLOOR: return BLOCK_FACE_TOP;
- case E_META_TORCH_EAST: return BLOCK_FACE_EAST;
- case E_META_TORCH_WEST: return BLOCK_FACE_WEST;
- case E_META_TORCH_NORTH: return BLOCK_FACE_NORTH;
- case E_META_TORCH_SOUTH: return BLOCK_FACE_SOUTH;
- default:
- {
- ASSERT(!"Unhandled torch metadata");
- break;
- }
- }
- return 0;
- } // tolua_export
-
-
- static bool IsAttachedTo(const Vector3i & a_TorchPos, char a_TorchMeta, const Vector3i & a_BlockPos)
- {
- switch (a_TorchMeta)
- {
- case 0x0:
- case E_META_TORCH_FLOOR: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 1, 0)));
- case E_META_TORCH_EAST: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 0, -1)));
- case E_META_TORCH_WEST: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 0, 1)));
- case E_META_TORCH_NORTH: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(-1, 0, 0)));
- case E_META_TORCH_SOUTH: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(1, 0, 0)));
- default:
- {
- ASSERT(!"Unhandled torch meta!");
- break;
- }
- }
- return false;
- }
-
-
- virtual bool DoesAllowBlockOnTop(void) override
- {
- return false;
- }
-
-
- static bool CanBePlacedOn(BLOCKTYPE a_BlockType, char a_Direction)
- {
- switch (a_BlockType)
- {
- case E_BLOCK_GLASS:
- case E_BLOCK_FENCE:
- case E_BLOCK_NETHER_BRICK_FENCE:
- {
- return (a_Direction == 0x1); // allow only direction "standing on floor"
- }
-
- default:
- {
- return g_BlockIsSolid[a_BlockType];
- }
- }
- }
-
-
- static bool TorchCanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
- {
- // TODO: If placing a torch from below, check all 4 XZ neighbors, place it on that neighbor instead
- // How to propagate that change up?
- // Simon: The easiest way is to calculate the position two times, shouldn´t cost much cpu power :)
-
- if (a_BlockFace == BLOCK_FACE_BOTTOM)
- {
- return false;
- }
-
- AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true);
-
- return CanBePlacedOn(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ), a_BlockFace);
- }
-
-
- /// Finds a suitable Face for the Torch. Returns BLOCK_FACE_BOTTOM on failure
- static char FindSuitableFace(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
- {
- for (int i = 1; i <= 5; i++)
- {
- if (TorchCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, i))
- {
- return i;
- }
- }
- return BLOCK_FACE_BOTTOM;
- }
-
-
- /*
- virtual bool CanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override
- {
- if (TorchCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace))
- {
- return true;
- }
-
- return (FindSuitableFace(a_World, a_BlockX, a_BlockY, a_BlockZ) != BLOCK_FACE_BOTTOM);
- }
- */
-
-
- virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
- {
- // TODO: Use AdjustCoordsByMeta(), then cChunk::UnboundedRelGetBlock() and finally some comparison
- char Face = MetaDataToDirection(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ));
- int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
- int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
- return TorchCanBePlacedAt(a_Chunk.GetWorld(), BlockX, a_RelY, BlockZ, Face);
- }
-
-
- virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
- {
- // Always drop meta = 0
- a_Pickups.push_back(cItem(m_BlockType, 1, 0));
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.wood";
- }
-
-
- virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override
- {
- // Bit 4 stays, the rest is swapped around according to a table:
- NIBBLETYPE TopBits = (a_Meta & 0x08);
- switch (a_Meta & 0x07)
- {
- case 0x01: return TopBits | 0x04; // East -> North
- case 0x02: return TopBits | 0x03; // West -> South
- case 0x03: return TopBits | 0x01; // South -> East
- case 0x04: return TopBits | 0x02; // North -> West
- default: return a_Meta; // Floor -> Floor
- }
- }
-
-
- virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override
- {
- // Bit 4 stays, the rest is swapped around according to a table:
- NIBBLETYPE TopBits = (a_Meta & 0x08);
- switch (a_Meta & 0x07)
- {
- case 0x01: return TopBits | 0x03; // East -> South
- case 0x02: return TopBits | 0x04; // West -> North
- case 0x03: return TopBits | 0x02; // South -> West
- case 0x04: return TopBits | 0x01; // North -> East
- default: return a_Meta; // Floor -> Floor
- }
- }
-
-
- virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override
- {
- // Bit 4 stays, the rest is swapped around according to a table:
- NIBBLETYPE TopBits = (a_Meta & 0x08);
- switch (a_Meta & 0x07)
- {
- case 0x03: return TopBits | 0x04; // South -> North
- case 0x04: return TopBits | 0x03; // North -> South
- default: return a_Meta; // Keep the rest
- }
- }
-
-
- // Mirroring around the XZ plane doesn't make sense for floor torches,
- // the others stay the same, so let's keep all the metas the same.
- // The base class does tht for us, no need to override MetaMirrorXZ()
-
-
- virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override
- {
- // Bit 4 stays, the rest is swapped around according to a table:
- NIBBLETYPE TopBits = (a_Meta & 0x08);
- switch (a_Meta & 0x07)
- {
- case 0x01: return TopBits | 0x02; // East -> West
- case 0x02: return TopBits | 0x01; // West -> East
- default: return a_Meta; // Keep the rest
- }
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../World.h"
+
+
+
+
+
+class cBlockTorchHandler :
+ public cBlockHandler
+{
+public:
+ cBlockTorchHandler(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
+ {
+ // Find proper placement. Use the player-supplied one as the default, but fix if not okay:
+ if (!TorchCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace))
+ {
+ a_BlockFace = FindSuitableFace(a_World, a_BlockX, a_BlockY, a_BlockZ);
+
+ if (a_BlockFace == BLOCK_FACE_BOTTOM)
+ {
+ return false;
+ }
+ }
+ a_BlockType = m_BlockType;
+ a_BlockMeta = DirectionToMetaData(a_BlockFace);
+ return true;
+ }
+
+
+ static NIBBLETYPE DirectionToMetaData(char a_Direction) // tolua_export
+ { // tolua_export
+ switch (a_Direction)
+ {
+ case BLOCK_FACE_BOTTOM: ASSERT(!"Shouldn't be getting this face"); return 0;
+ case BLOCK_FACE_TOP: return E_META_TORCH_FLOOR;
+ case BLOCK_FACE_EAST: return E_META_TORCH_EAST;
+ case BLOCK_FACE_WEST: return E_META_TORCH_WEST;
+ case BLOCK_FACE_NORTH: return E_META_TORCH_NORTH;
+ case BLOCK_FACE_SOUTH: return E_META_TORCH_SOUTH;
+ default:
+ {
+ ASSERT(!"Unhandled torch direction!");
+ break;
+ }
+ };
+ return 0x0;
+ } // tolua_export
+
+
+ static char MetaDataToDirection(NIBBLETYPE a_MetaData) // tolua_export
+ { // tolua_export
+ switch (a_MetaData)
+ {
+ case 0: return BLOCK_FACE_TOP; // by default, the torches stand on the ground
+ case E_META_TORCH_FLOOR: return BLOCK_FACE_TOP;
+ case E_META_TORCH_EAST: return BLOCK_FACE_EAST;
+ case E_META_TORCH_WEST: return BLOCK_FACE_WEST;
+ case E_META_TORCH_NORTH: return BLOCK_FACE_NORTH;
+ case E_META_TORCH_SOUTH: return BLOCK_FACE_SOUTH;
+ default:
+ {
+ ASSERT(!"Unhandled torch metadata");
+ break;
+ }
+ }
+ return 0;
+ } // tolua_export
+
+
+ static bool IsAttachedTo(const Vector3i & a_TorchPos, char a_TorchMeta, const Vector3i & a_BlockPos)
+ {
+ switch (a_TorchMeta)
+ {
+ case 0x0:
+ case E_META_TORCH_FLOOR: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 1, 0)));
+ case E_META_TORCH_EAST: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 0, -1)));
+ case E_META_TORCH_WEST: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 0, 1)));
+ case E_META_TORCH_NORTH: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(-1, 0, 0)));
+ case E_META_TORCH_SOUTH: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(1, 0, 0)));
+ default:
+ {
+ ASSERT(!"Unhandled torch meta!");
+ break;
+ }
+ }
+ return false;
+ }
+
+
+ virtual bool DoesAllowBlockOnTop(void) override
+ {
+ return false;
+ }
+
+
+ static bool CanBePlacedOn(BLOCKTYPE a_BlockType, char a_Direction)
+ {
+ switch (a_BlockType)
+ {
+ case E_BLOCK_GLASS:
+ case E_BLOCK_FENCE:
+ case E_BLOCK_NETHER_BRICK_FENCE:
+ {
+ return (a_Direction == 0x1); // allow only direction "standing on floor"
+ }
+
+ default:
+ {
+ return g_BlockIsSolid[a_BlockType];
+ }
+ }
+ }
+
+
+ static bool TorchCanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
+ {
+ // TODO: If placing a torch from below, check all 4 XZ neighbors, place it on that neighbor instead
+ // How to propagate that change up?
+ // Simon: The easiest way is to calculate the position two times, shouldn´t cost much cpu power :)
+
+ if (a_BlockFace == BLOCK_FACE_BOTTOM)
+ {
+ return false;
+ }
+
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true);
+
+ return CanBePlacedOn(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ), a_BlockFace);
+ }
+
+
+ /// Finds a suitable Face for the Torch. Returns BLOCK_FACE_BOTTOM on failure
+ static char FindSuitableFace(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ for (int i = 1; i <= 5; i++)
+ {
+ if (TorchCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, i))
+ {
+ return i;
+ }
+ }
+ return BLOCK_FACE_BOTTOM;
+ }
+
+
+ /*
+ virtual bool CanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override
+ {
+ if (TorchCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace))
+ {
+ return true;
+ }
+
+ return (FindSuitableFace(a_World, a_BlockX, a_BlockY, a_BlockZ) != BLOCK_FACE_BOTTOM);
+ }
+ */
+
+
+ virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
+ {
+ // TODO: Use AdjustCoordsByMeta(), then cChunk::UnboundedRelGetBlock() and finally some comparison
+ char Face = MetaDataToDirection(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ));
+ int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
+ int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
+ return TorchCanBePlacedAt(a_Chunk.GetWorld(), BlockX, a_RelY, BlockZ, Face);
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ // Always drop meta = 0
+ a_Pickups.push_back(cItem(m_BlockType, 1, 0));
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+
+
+ virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override
+ {
+ // Bit 4 stays, the rest is swapped around according to a table:
+ NIBBLETYPE TopBits = (a_Meta & 0x08);
+ switch (a_Meta & 0x07)
+ {
+ case 0x01: return TopBits | 0x04; // East -> North
+ case 0x02: return TopBits | 0x03; // West -> South
+ case 0x03: return TopBits | 0x01; // South -> East
+ case 0x04: return TopBits | 0x02; // North -> West
+ default: return a_Meta; // Floor -> Floor
+ }
+ }
+
+
+ virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override
+ {
+ // Bit 4 stays, the rest is swapped around according to a table:
+ NIBBLETYPE TopBits = (a_Meta & 0x08);
+ switch (a_Meta & 0x07)
+ {
+ case 0x01: return TopBits | 0x03; // East -> South
+ case 0x02: return TopBits | 0x04; // West -> North
+ case 0x03: return TopBits | 0x02; // South -> West
+ case 0x04: return TopBits | 0x01; // North -> East
+ default: return a_Meta; // Floor -> Floor
+ }
+ }
+
+
+ virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override
+ {
+ // Bit 4 stays, the rest is swapped around according to a table:
+ NIBBLETYPE TopBits = (a_Meta & 0x08);
+ switch (a_Meta & 0x07)
+ {
+ case 0x03: return TopBits | 0x04; // South -> North
+ case 0x04: return TopBits | 0x03; // North -> South
+ default: return a_Meta; // Keep the rest
+ }
+ }
+
+
+ // Mirroring around the XZ plane doesn't make sense for floor torches,
+ // the others stay the same, so let's keep all the metas the same.
+ // The base class does tht for us, no need to override MetaMirrorXZ()
+
+
+ virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override
+ {
+ // Bit 4 stays, the rest is swapped around according to a table:
+ NIBBLETYPE TopBits = (a_Meta & 0x08);
+ switch (a_Meta & 0x07)
+ {
+ case 0x01: return TopBits | 0x02; // East -> West
+ case 0x02: return TopBits | 0x01; // West -> East
+ default: return a_Meta; // Keep the rest
+ }
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockVine.h b/source/Blocks/BlockVine.h
index 2a88c9b68..0bc935272 100644
--- a/source/Blocks/BlockVine.h
+++ b/source/Blocks/BlockVine.h
@@ -1,200 +1,200 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockVineHandler :
- public cBlockHandler
-{
-public:
- cBlockVineHandler(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
- {
- // TODO: Disallow placement where the vine doesn't attach to something properly
- BLOCKTYPE BlockType = 0;
- NIBBLETYPE BlockMeta;
- a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
- if (BlockType == m_BlockType)
- {
- a_BlockMeta = BlockMeta | DirectionToMetaData(a_BlockFace);
- }
- else
- {
- a_BlockMeta = DirectionToMetaData(a_BlockFace);
- }
- a_BlockType = m_BlockType;
- return true;
- }
-
-
- static NIBBLETYPE DirectionToMetaData(char a_BlockFace)
- {
- switch (a_BlockFace)
- {
- case BLOCK_FACE_NORTH: return 0x1;
- case BLOCK_FACE_SOUTH: return 0x4;
- case BLOCK_FACE_WEST: return 0x8;
- case BLOCK_FACE_EAST: return 0x2;
- default: return 0x0;
- }
- }
-
-
- static char MetaDataToDirection(NIBBLETYPE a_MetaData)
- {
- switch(a_MetaData)
- {
- case 0x1: return BLOCK_FACE_NORTH;
- case 0x4: return BLOCK_FACE_SOUTH;
- case 0x8: return BLOCK_FACE_WEST;
- case 0x2: return BLOCK_FACE_EAST;
- default: return BLOCK_FACE_TOP;
- }
- }
-
-
- /// Returns true if the specified block type is good for vines to attach to
- static bool IsBlockAttachable(BLOCKTYPE a_BlockType)
- {
- return (a_BlockType == E_BLOCK_LEAVES) || g_BlockIsSolid[a_BlockType];
- }
-
-
- /// Returns the meta that has the maximum allowable sides of the vine, given the surroundings
- NIBBLETYPE GetMaxMeta(cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
- {
- static const struct
- {
- int x, z;
- int Bit;
- } Coords[] =
- {
- { 0, 1, 1}, // south, ZP
- {-1, 0, 2}, // west, XM
- { 0, -1, 4}, // north, ZM
- { 1, 0, 8}, // east, XP
- } ;
- int res = 0;
- for (int i = 0; i < ARRAYCOUNT(Coords); i++)
- {
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
- if (
- a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta) &&
- IsBlockAttachable(BlockType)
- )
- {
- res |= Coords[i].Bit;
- }
- }
- return res;
- }
-
-
- void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) override
- {
- NIBBLETYPE CurMeta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
- NIBBLETYPE MaxMeta = GetMaxMeta(a_Chunk, a_RelX, a_RelY, a_RelZ);
-
- // Check if vine above us, add its meta to MaxMeta
- if ((a_RelY < cChunkDef::Height - 1) && (a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ) == m_BlockType))
- {
- MaxMeta |= a_Chunk.GetMeta(a_RelX, a_RelY + 1, a_RelZ);
- }
-
- NIBBLETYPE Common = CurMeta & MaxMeta; // Neighbors that we have and are legal
- if (Common != CurMeta)
- {
- // There is a neighbor missing, need to update the meta or even destroy the block
- bool HasTop = (a_RelY < cChunkDef::Height - 1) && IsBlockAttachable(a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ));
- if ((Common == 0) && !HasTop)
- {
- // The vine just lost all its support, destroy the block:
- if (DoesDropOnUnsuitable())
- {
- int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
- int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
- DropBlock(a_Chunk.GetWorld(), NULL, BlockX, a_RelY, BlockZ);
- }
- a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0);
- return;
- }
- a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, m_BlockType, Common);
- }
- else
- {
- // Wake up the simulators for this block:
- int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
- int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
- a_Chunk.GetWorld()->GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, &a_Chunk);
- }
- }
-
-
- virtual bool DoesIgnoreBuildCollision(void) override
- {
- return true;
- }
-
-
- virtual bool DoesAllowBlockOnTop(void) override
- {
- return false;
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.grass";
- }
-
-
- virtual bool DoesDropOnUnsuitable(void) override
- {
- return false;
- }
-
-
- virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override
- {
- return ((a_Meta >> 1) | (a_Meta << 3)) & 0x0f; // Rotate bits to the right
- }
-
-
- virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override
- {
- return ((a_Meta << 1) | (a_Meta >> 3)) & 0x0f; // Rotate bits to the left
- }
-
-
- virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override
- {
- // Bits 2 and 4 stay, bits 1 and 3 swap
- return ((a_Meta & 0x0a) | ((a_Meta & 0x01) << 2) | ((a_Meta & 0x04) >> 2));
- }
-
-
- virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override
- {
- // Bits 1 and 3 stay, bits 2 and 4 swap
- return ((a_Meta & 0x05) | ((a_Meta & 0x02) << 2) | ((a_Meta & 0x08) >> 2));
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockVineHandler :
+ public cBlockHandler
+{
+public:
+ cBlockVineHandler(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
+ {
+ // TODO: Disallow placement where the vine doesn't attach to something properly
+ BLOCKTYPE BlockType = 0;
+ NIBBLETYPE BlockMeta;
+ a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
+ if (BlockType == m_BlockType)
+ {
+ a_BlockMeta = BlockMeta | DirectionToMetaData(a_BlockFace);
+ }
+ else
+ {
+ a_BlockMeta = DirectionToMetaData(a_BlockFace);
+ }
+ a_BlockType = m_BlockType;
+ return true;
+ }
+
+
+ static NIBBLETYPE DirectionToMetaData(char a_BlockFace)
+ {
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_NORTH: return 0x1;
+ case BLOCK_FACE_SOUTH: return 0x4;
+ case BLOCK_FACE_WEST: return 0x8;
+ case BLOCK_FACE_EAST: return 0x2;
+ default: return 0x0;
+ }
+ }
+
+
+ static char MetaDataToDirection(NIBBLETYPE a_MetaData)
+ {
+ switch(a_MetaData)
+ {
+ case 0x1: return BLOCK_FACE_NORTH;
+ case 0x4: return BLOCK_FACE_SOUTH;
+ case 0x8: return BLOCK_FACE_WEST;
+ case 0x2: return BLOCK_FACE_EAST;
+ default: return BLOCK_FACE_TOP;
+ }
+ }
+
+
+ /// Returns true if the specified block type is good for vines to attach to
+ static bool IsBlockAttachable(BLOCKTYPE a_BlockType)
+ {
+ return (a_BlockType == E_BLOCK_LEAVES) || g_BlockIsSolid[a_BlockType];
+ }
+
+
+ /// Returns the meta that has the maximum allowable sides of the vine, given the surroundings
+ NIBBLETYPE GetMaxMeta(cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
+ {
+ static const struct
+ {
+ int x, z;
+ int Bit;
+ } Coords[] =
+ {
+ { 0, 1, 1}, // south, ZP
+ {-1, 0, 2}, // west, XM
+ { 0, -1, 4}, // north, ZM
+ { 1, 0, 8}, // east, XP
+ } ;
+ int res = 0;
+ for (int i = 0; i < ARRAYCOUNT(Coords); i++)
+ {
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (
+ a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta) &&
+ IsBlockAttachable(BlockType)
+ )
+ {
+ res |= Coords[i].Bit;
+ }
+ }
+ return res;
+ }
+
+
+ void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) override
+ {
+ NIBBLETYPE CurMeta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
+ NIBBLETYPE MaxMeta = GetMaxMeta(a_Chunk, a_RelX, a_RelY, a_RelZ);
+
+ // Check if vine above us, add its meta to MaxMeta
+ if ((a_RelY < cChunkDef::Height - 1) && (a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ) == m_BlockType))
+ {
+ MaxMeta |= a_Chunk.GetMeta(a_RelX, a_RelY + 1, a_RelZ);
+ }
+
+ NIBBLETYPE Common = CurMeta & MaxMeta; // Neighbors that we have and are legal
+ if (Common != CurMeta)
+ {
+ // There is a neighbor missing, need to update the meta or even destroy the block
+ bool HasTop = (a_RelY < cChunkDef::Height - 1) && IsBlockAttachable(a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ));
+ if ((Common == 0) && !HasTop)
+ {
+ // The vine just lost all its support, destroy the block:
+ if (DoesDropOnUnsuitable())
+ {
+ int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
+ int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
+ DropBlock(a_Chunk.GetWorld(), NULL, BlockX, a_RelY, BlockZ);
+ }
+ a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0);
+ return;
+ }
+ a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, m_BlockType, Common);
+ }
+ else
+ {
+ // Wake up the simulators for this block:
+ int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
+ int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
+ a_Chunk.GetWorld()->GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, &a_Chunk);
+ }
+ }
+
+
+ virtual bool DoesIgnoreBuildCollision(void) override
+ {
+ return true;
+ }
+
+
+ virtual bool DoesAllowBlockOnTop(void) override
+ {
+ return false;
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.grass";
+ }
+
+
+ virtual bool DoesDropOnUnsuitable(void) override
+ {
+ return false;
+ }
+
+
+ virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override
+ {
+ return ((a_Meta >> 1) | (a_Meta << 3)) & 0x0f; // Rotate bits to the right
+ }
+
+
+ virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override
+ {
+ return ((a_Meta << 1) | (a_Meta >> 3)) & 0x0f; // Rotate bits to the left
+ }
+
+
+ virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override
+ {
+ // Bits 2 and 4 stay, bits 1 and 3 swap
+ return ((a_Meta & 0x0a) | ((a_Meta & 0x01) << 2) | ((a_Meta & 0x04) >> 2));
+ }
+
+
+ virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override
+ {
+ // Bits 1 and 3 stay, bits 2 and 4 swap
+ return ((a_Meta & 0x05) | ((a_Meta & 0x02) << 2) | ((a_Meta & 0x08) >> 2));
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockWood.h b/source/Blocks/BlockWood.h
index 907fb587c..4e2246506 100644
--- a/source/Blocks/BlockWood.h
+++ b/source/Blocks/BlockWood.h
@@ -1,27 +1,27 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-
-
-
-
-
-class cBlockWoodHandler : public cBlockHandler
-{
-public:
- cBlockWoodHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.wood";
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+
+
+
+
+
+class cBlockWoodHandler : public cBlockHandler
+{
+public:
+ cBlockWoodHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockWorkbench.h b/source/Blocks/BlockWorkbench.h
index ccd1ded9a..60aa1791b 100644
--- a/source/Blocks/BlockWorkbench.h
+++ b/source/Blocks/BlockWorkbench.h
@@ -1,43 +1,43 @@
-
-#pragma once
-
-#include "BlockHandler.h"
-#include "../UI/Window.h"
-#include "../Player.h"
-
-
-
-
-
-class cBlockWorkbenchHandler:
- public cBlockHandler
-{
-public:
- cBlockWorkbenchHandler(BLOCKTYPE a_BlockType)
- : cBlockHandler(a_BlockType)
- {
- }
-
-
- virtual void OnUse(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) override
- {
- cWindow * Window = new cCraftingWindow(a_BlockX, a_BlockY, a_BlockZ);
- a_Player->OpenWindow(Window);
- }
-
-
- virtual bool IsUseable(void) override
- {
- return true;
- }
-
-
- virtual const char * GetStepSound(void) override
- {
- return "step.wood";
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "BlockHandler.h"
+#include "../UI/Window.h"
+#include "../Player.h"
+
+
+
+
+
+class cBlockWorkbenchHandler:
+ public cBlockHandler
+{
+public:
+ cBlockWorkbenchHandler(BLOCKTYPE a_BlockType)
+ : cBlockHandler(a_BlockType)
+ {
+ }
+
+
+ virtual void OnUse(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) override
+ {
+ cWindow * Window = new cCraftingWindow(a_BlockX, a_BlockY, a_BlockZ);
+ a_Player->OpenWindow(Window);
+ }
+
+
+ virtual bool IsUseable(void) override
+ {
+ return true;
+ }
+
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.wood";
+ }
+} ;
+
+
+
+
diff --git a/source/ByteBuffer.cpp b/source/ByteBuffer.cpp
index 6d630068a..c82951271 100644
--- a/source/ByteBuffer.cpp
+++ b/source/ByteBuffer.cpp
@@ -1,661 +1,661 @@
-
-// ByteBuffer.cpp
-
-// Implements the cByteBuffer class representing a ringbuffer of bytes
-
-#include "Globals.h"
-
-#include "ByteBuffer.h"
-#include "Endianness.h"
-#include "OSSupport/IsThread.h"
-
-
-
-
-
-#define NEEDBYTES(Num) if (!CanReadBytes(Num)) return false;
-#define PUTBYTES(Num) if (!CanWriteBytes(Num)) return false;
-
-
-
-
-
-#ifdef _DEBUG
-
-/// Simple RAII class that uses one internal unsigned long for checking if two threads are using an object simultanously
-class cSingleThreadAccessChecker
-{
-public:
- cSingleThreadAccessChecker(unsigned long * a_ThreadID) :
- m_ThreadID(a_ThreadID)
- {
- ASSERT((*a_ThreadID == 0) || (*a_ThreadID == cIsThread::GetCurrentID()));
- }
-
- ~cSingleThreadAccessChecker()
- {
- *m_ThreadID = 0;
- }
-
-protected:
- unsigned long * m_ThreadID;
-} ;
-
-#define CHECK_THREAD cSingleThreadAccessChecker Checker(const_cast<unsigned long *>(&m_ThreadID))
-
-#else
- #define CHECK_THREAD
-#endif
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cByteBuffer:
-
-cByteBuffer::cByteBuffer(int a_BufferSize) :
- m_Buffer(new char[a_BufferSize + 1]),
- m_BufferSize(a_BufferSize + 1),
- #ifdef _DEBUG
- m_ThreadID(0),
- #endif // _DEBUG
- m_DataStart(0),
- m_WritePos(0),
- m_ReadPos(0)
-{
- // Allocating one byte more than the buffer size requested, so that we can distinguish between
- // completely-full and completely-empty states
-}
-
-
-
-
-
-cByteBuffer::~cByteBuffer()
-{
- CheckValid();
- delete[] m_Buffer;
-}
-
-
-
-
-
-bool cByteBuffer::Write(const char * a_Bytes, int a_Count)
-{
- CHECK_THREAD;
- CheckValid();
-
- // Store the current free space for a check after writing:
- int CurFreeSpace = GetFreeSpace();
- int CurReadableSpace = GetReadableSpace();
- int WrittenBytes = 0;
-
- if (GetFreeSpace() < a_Count)
- {
- return false;
- }
- int TillEnd = m_BufferSize - m_WritePos;
- if (TillEnd <= a_Count)
- {
- // Need to wrap around the ringbuffer end
- if (TillEnd > 0)
- {
- memcpy(m_Buffer + m_WritePos, a_Bytes, TillEnd);
- a_Bytes += TillEnd;
- a_Count -= TillEnd;
- WrittenBytes = TillEnd;
- }
- m_WritePos = 0;
- }
-
- // We're guaranteed that we'll fit in a single write op
- if (a_Count > 0)
- {
- memcpy(m_Buffer + m_WritePos, a_Bytes, a_Count);
- m_WritePos += a_Count;
- WrittenBytes += a_Count;
- }
-
- ASSERT(GetFreeSpace() == CurFreeSpace - WrittenBytes);
- ASSERT(GetReadableSpace() == CurReadableSpace + WrittenBytes);
- return true;
-}
-
-
-
-
-
-int cByteBuffer::GetFreeSpace(void) const
-{
- CHECK_THREAD;
- CheckValid();
- if (m_WritePos >= m_DataStart)
- {
- // Wrap around the buffer end:
- return m_BufferSize - m_WritePos + m_DataStart - 1;
- }
- // Single free space partition:
- return m_DataStart - m_WritePos - 1;
-}
-
-
-
-
-
-/// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes()
-int cByteBuffer::GetUsedSpace(void) const
-{
- CHECK_THREAD;
- CheckValid();
- return m_BufferSize - GetFreeSpace();
-}
-
-
-
-
-
-/// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already)
-int cByteBuffer::GetReadableSpace(void) const
-{
- CHECK_THREAD;
- CheckValid();
- if (m_ReadPos > m_WritePos)
- {
- // Wrap around the buffer end:
- return m_BufferSize - m_ReadPos + m_WritePos;
- }
- // Single readable space partition:
- return m_WritePos - m_ReadPos ;
-}
-
-
-
-
-
-bool cByteBuffer::CanReadBytes(int a_Count) const
-{
- CHECK_THREAD;
- CheckValid();
- return (a_Count <= GetReadableSpace());
-}
-
-
-
-
-
-bool cByteBuffer::CanWriteBytes(int a_Count) const
-{
- CHECK_THREAD;
- CheckValid();
- return (a_Count <= GetFreeSpace());
-}
-
-
-
-
-
-bool cByteBuffer::ReadChar(char & a_Value)
-{
- CHECK_THREAD;
- CheckValid();
- NEEDBYTES(1);
- ReadBuf(&a_Value, 1);
- return true;
-}
-
-
-
-
-
-bool cByteBuffer::ReadByte(unsigned char & a_Value)
-{
- CHECK_THREAD;
- CheckValid();
- NEEDBYTES(1);
- ReadBuf(&a_Value, 1);
- return true;
-}
-
-
-
-
-
-bool cByteBuffer::ReadBEShort(short & a_Value)
-{
- CHECK_THREAD;
- CheckValid();
- NEEDBYTES(2);
- ReadBuf(&a_Value, 2);
- a_Value = ntohs(a_Value);
- return true;
-}
-
-
-
-
-
-bool cByteBuffer::ReadBEInt(int & a_Value)
-{
- CHECK_THREAD;
- CheckValid();
- NEEDBYTES(4);
- ReadBuf(&a_Value, 4);
- a_Value = ntohl(a_Value);
- return true;
-}
-
-
-
-
-
-bool cByteBuffer::ReadBEInt64(Int64 & a_Value)
-{
- CHECK_THREAD;
- CheckValid();
- NEEDBYTES(8);
- ReadBuf(&a_Value, 8);
- a_Value = NetworkToHostLong8(&a_Value);
- return true;
-}
-
-
-
-
-
-bool cByteBuffer::ReadBEFloat(float & a_Value)
-{
- CHECK_THREAD;
- CheckValid();
- NEEDBYTES(4);
- ReadBuf(&a_Value, 4);
- a_Value = NetworkToHostFloat4(&a_Value);
- return true;
-}
-
-
-
-
-
-bool cByteBuffer::ReadBEDouble(double & a_Value)
-{
- CHECK_THREAD;
- CheckValid();
- NEEDBYTES(8);
- ReadBuf(&a_Value, 8);
- a_Value = NetworkToHostDouble8(&a_Value);
- return true;
-}
-
-
-
-
-
-bool cByteBuffer::ReadBool(bool & a_Value)
-{
- CHECK_THREAD;
- CheckValid();
- NEEDBYTES(1);
- char Value = 0;
- ReadBuf(&Value, 1);
- a_Value = (Value != 0);
- return true;
-}
-
-
-
-
-
-bool cByteBuffer::ReadBEUTF16String16(AString & a_Value)
-{
- CHECK_THREAD;
- CheckValid();
- short Length;
- if (!ReadBEShort(Length))
- {
- return false;
- }
- if (Length < 0)
- {
- ASSERT(!"Negative string length? Are you sure?");
- return true;
- }
- return ReadUTF16String(a_Value, Length);
-}
-
-
-
-
-
-bool cByteBuffer::WriteChar(char a_Value)
-{
- CHECK_THREAD;
- CheckValid();
- PUTBYTES(1);
- return WriteBuf(&a_Value, 1);
-}
-
-
-
-
-
-bool cByteBuffer::WriteByte(unsigned char a_Value)
-{
- CHECK_THREAD;
- CheckValid();
- PUTBYTES(1);
- return WriteBuf(&a_Value, 1);
-}
-
-
-
-
-
-bool cByteBuffer::WriteBEShort(short a_Value)
-{
- CHECK_THREAD;
- CheckValid();
- PUTBYTES(2);
- short Converted = htons(a_Value);
- return WriteBuf(&Converted, 2);
-}
-
-
-
-
-
-bool cByteBuffer::WriteBEInt(int a_Value)
-{
- CHECK_THREAD;
- CheckValid();
- PUTBYTES(4);
- int Converted = HostToNetwork4(&a_Value);
- return WriteBuf(&Converted, 4);
-}
-
-
-
-
-
-bool cByteBuffer::WriteBEInt64(Int64 a_Value)
-{
- CHECK_THREAD;
- CheckValid();
- PUTBYTES(8);
- Int64 Converted = HostToNetwork8(&a_Value);
- return WriteBuf(&Converted, 8);
-}
-
-
-
-
-
-bool cByteBuffer::WriteBEFloat(float a_Value)
-{
- CHECK_THREAD;
- CheckValid();
- PUTBYTES(4);
- int Converted = HostToNetwork4(&a_Value);
- return WriteBuf(&Converted, 4);
-}
-
-
-
-
-
-bool cByteBuffer::WriteBEDouble(double a_Value)
-{
- CHECK_THREAD;
- CheckValid();
- PUTBYTES(8);
- Int64 Converted = HostToNetwork8(&a_Value);
- return WriteBuf(&Converted, 8);
-}
-
-
-
-
-
-
-bool cByteBuffer::WriteBool(bool a_Value)
-{
- CHECK_THREAD;
- CheckValid();
- return WriteChar(a_Value ? 1 : 0);
-}
-
-
-
-
-
-bool cByteBuffer::WriteBEUTF16String16(const AString & a_Value)
-{
- CHECK_THREAD;
- CheckValid();
- PUTBYTES(2);
- AString UTF16BE;
- UTF8ToRawBEUTF16(a_Value.data(), a_Value.size(), UTF16BE);
- WriteBEShort((short)(UTF16BE.size() / 2));
- PUTBYTES(UTF16BE.size());
- WriteBuf(UTF16BE.data(), UTF16BE.size());
- return true;
-}
-
-
-
-
-
-bool cByteBuffer::ReadBuf(void * a_Buffer, int a_Count)
-{
- CHECK_THREAD;
- CheckValid();
- ASSERT(a_Count >= 0);
- NEEDBYTES(a_Count);
- char * Dst = (char *)a_Buffer; // So that we can do byte math
- int BytesToEndOfBuffer = m_BufferSize - m_ReadPos;
- ASSERT(BytesToEndOfBuffer >= 0); // Sanity check
- if (BytesToEndOfBuffer <= a_Count)
- {
- // Reading across the ringbuffer end, read the first part and adjust parameters:
- if (BytesToEndOfBuffer > 0)
- {
- memcpy(Dst, m_Buffer + m_ReadPos, BytesToEndOfBuffer);
- Dst += BytesToEndOfBuffer;
- a_Count -= BytesToEndOfBuffer;
- }
- m_ReadPos = 0;
- }
-
- // Read the rest of the bytes in a single read (guaranteed to fit):
- if (a_Count > 0)
- {
- memcpy(Dst, m_Buffer + m_ReadPos, a_Count);
- m_ReadPos += a_Count;
- }
- return true;
-}
-
-
-
-
-
-bool cByteBuffer::WriteBuf(const void * a_Buffer, int a_Count)
-{
- CHECK_THREAD;
- CheckValid();
- ASSERT(a_Count >= 0);
- PUTBYTES(a_Count);
- char * Src = (char *)a_Buffer; // So that we can do byte math
- int BytesToEndOfBuffer = m_BufferSize - m_WritePos;
- if (BytesToEndOfBuffer <= a_Count)
- {
- // Reading across the ringbuffer end, read the first part and adjust parameters:
- memcpy(m_Buffer + m_WritePos, Src, BytesToEndOfBuffer);
- Src += BytesToEndOfBuffer;
- a_Count -= BytesToEndOfBuffer;
- m_WritePos = 0;
- }
-
- // Read the rest of the bytes in a single read (guaranteed to fit):
- if (a_Count > 0)
- {
- memcpy(m_Buffer + m_WritePos, Src, a_Count);
- m_WritePos += a_Count;
- }
- return true;
-}
-
-
-
-
-
-bool cByteBuffer::ReadString(AString & a_String, int a_Count)
-{
- CHECK_THREAD;
- CheckValid();
- ASSERT(a_Count >= 0);
- NEEDBYTES(a_Count);
- a_String.clear();
- a_String.reserve(a_Count);
- int BytesToEndOfBuffer = m_BufferSize - m_ReadPos;
- ASSERT(BytesToEndOfBuffer >= 0); // Sanity check
- if (BytesToEndOfBuffer <= a_Count)
- {
- // Reading across the ringbuffer end, read the first part and adjust parameters:
- if (BytesToEndOfBuffer > 0)
- {
- a_String.assign(m_Buffer + m_ReadPos, BytesToEndOfBuffer);
- a_Count -= BytesToEndOfBuffer;
- }
- m_ReadPos = 0;
- }
-
- // Read the rest of the bytes in a single read (guaranteed to fit):
- if (a_Count > 0)
- {
- a_String.append(m_Buffer + m_ReadPos, a_Count);
- m_ReadPos += a_Count;
- }
- return true;
-}
-
-
-
-
-
-bool cByteBuffer::ReadUTF16String(AString & a_String, int a_NumChars)
-{
- // Reads 2 * a_NumChars bytes and interprets it as a UTF16 string, converting it into UTF8 string a_String
- CHECK_THREAD;
- CheckValid();
- ASSERT(a_NumChars >= 0);
- AString RawData;
- if (!ReadString(RawData, a_NumChars * 2))
- {
- return false;
- }
- RawBEToUTF8((short *)(RawData.data()), a_NumChars, a_String);
- return true;
-}
-
-
-
-
-
-bool cByteBuffer::SkipRead(int a_Count)
-{
- CHECK_THREAD;
- CheckValid();
- ASSERT(a_Count >= 0);
- if (!CanReadBytes(a_Count))
- {
- return false;
- }
- AdvanceReadPos(a_Count);
- return true;
-}
-
-
-
-
-
-void cByteBuffer::ReadAll(AString & a_Data)
-{
- CHECK_THREAD;
- CheckValid();
- ReadString(a_Data, GetReadableSpace());
-}
-
-
-
-
-
-void cByteBuffer::CommitRead(void)
-{
- CHECK_THREAD;
- CheckValid();
- m_DataStart = m_ReadPos;
-}
-
-
-
-
-
-void cByteBuffer::ResetRead(void)
-{
- CHECK_THREAD;
- CheckValid();
- m_ReadPos = m_DataStart;
-}
-
-
-
-
-
-void cByteBuffer::ReadAgain(AString & a_Out)
-{
- // Return the data between m_DataStart and m_ReadPos (the data that has been read but not committed)
- // Used by ProtoProxy to repeat communication twice, once for parsing and the other time for the remote party
- CHECK_THREAD;
- CheckValid();
- int DataStart = m_DataStart;
- if (m_ReadPos < m_DataStart)
- {
- // Across the ringbuffer end, read the first part and adjust next part's start:
- a_Out.append(m_Buffer + m_DataStart, m_BufferSize - m_DataStart);
- DataStart = 0;
- }
- a_Out.append(m_Buffer + DataStart, m_ReadPos - DataStart);
-}
-
-
-
-
-
-void cByteBuffer::AdvanceReadPos(int a_Count)
-{
- CHECK_THREAD;
- CheckValid();
- m_ReadPos += a_Count;
- if (m_ReadPos > m_BufferSize)
- {
- m_ReadPos -= m_BufferSize;
- }
-}
-
-
-
-
-
-void cByteBuffer::CheckValid(void) const
-{
- ASSERT(m_ReadPos >= 0);
- ASSERT(m_ReadPos < m_BufferSize);
- ASSERT(m_WritePos >= 0);
- ASSERT(m_WritePos < m_BufferSize);
-}
-
-
-
-
+
+// ByteBuffer.cpp
+
+// Implements the cByteBuffer class representing a ringbuffer of bytes
+
+#include "Globals.h"
+
+#include "ByteBuffer.h"
+#include "Endianness.h"
+#include "OSSupport/IsThread.h"
+
+
+
+
+
+#define NEEDBYTES(Num) if (!CanReadBytes(Num)) return false;
+#define PUTBYTES(Num) if (!CanWriteBytes(Num)) return false;
+
+
+
+
+
+#ifdef _DEBUG
+
+/// Simple RAII class that uses one internal unsigned long for checking if two threads are using an object simultanously
+class cSingleThreadAccessChecker
+{
+public:
+ cSingleThreadAccessChecker(unsigned long * a_ThreadID) :
+ m_ThreadID(a_ThreadID)
+ {
+ ASSERT((*a_ThreadID == 0) || (*a_ThreadID == cIsThread::GetCurrentID()));
+ }
+
+ ~cSingleThreadAccessChecker()
+ {
+ *m_ThreadID = 0;
+ }
+
+protected:
+ unsigned long * m_ThreadID;
+} ;
+
+#define CHECK_THREAD cSingleThreadAccessChecker Checker(const_cast<unsigned long *>(&m_ThreadID))
+
+#else
+ #define CHECK_THREAD
+#endif
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cByteBuffer:
+
+cByteBuffer::cByteBuffer(int a_BufferSize) :
+ m_Buffer(new char[a_BufferSize + 1]),
+ m_BufferSize(a_BufferSize + 1),
+ #ifdef _DEBUG
+ m_ThreadID(0),
+ #endif // _DEBUG
+ m_DataStart(0),
+ m_WritePos(0),
+ m_ReadPos(0)
+{
+ // Allocating one byte more than the buffer size requested, so that we can distinguish between
+ // completely-full and completely-empty states
+}
+
+
+
+
+
+cByteBuffer::~cByteBuffer()
+{
+ CheckValid();
+ delete[] m_Buffer;
+}
+
+
+
+
+
+bool cByteBuffer::Write(const char * a_Bytes, int a_Count)
+{
+ CHECK_THREAD;
+ CheckValid();
+
+ // Store the current free space for a check after writing:
+ int CurFreeSpace = GetFreeSpace();
+ int CurReadableSpace = GetReadableSpace();
+ int WrittenBytes = 0;
+
+ if (GetFreeSpace() < a_Count)
+ {
+ return false;
+ }
+ int TillEnd = m_BufferSize - m_WritePos;
+ if (TillEnd <= a_Count)
+ {
+ // Need to wrap around the ringbuffer end
+ if (TillEnd > 0)
+ {
+ memcpy(m_Buffer + m_WritePos, a_Bytes, TillEnd);
+ a_Bytes += TillEnd;
+ a_Count -= TillEnd;
+ WrittenBytes = TillEnd;
+ }
+ m_WritePos = 0;
+ }
+
+ // We're guaranteed that we'll fit in a single write op
+ if (a_Count > 0)
+ {
+ memcpy(m_Buffer + m_WritePos, a_Bytes, a_Count);
+ m_WritePos += a_Count;
+ WrittenBytes += a_Count;
+ }
+
+ ASSERT(GetFreeSpace() == CurFreeSpace - WrittenBytes);
+ ASSERT(GetReadableSpace() == CurReadableSpace + WrittenBytes);
+ return true;
+}
+
+
+
+
+
+int cByteBuffer::GetFreeSpace(void) const
+{
+ CHECK_THREAD;
+ CheckValid();
+ if (m_WritePos >= m_DataStart)
+ {
+ // Wrap around the buffer end:
+ return m_BufferSize - m_WritePos + m_DataStart - 1;
+ }
+ // Single free space partition:
+ return m_DataStart - m_WritePos - 1;
+}
+
+
+
+
+
+/// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes()
+int cByteBuffer::GetUsedSpace(void) const
+{
+ CHECK_THREAD;
+ CheckValid();
+ return m_BufferSize - GetFreeSpace();
+}
+
+
+
+
+
+/// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already)
+int cByteBuffer::GetReadableSpace(void) const
+{
+ CHECK_THREAD;
+ CheckValid();
+ if (m_ReadPos > m_WritePos)
+ {
+ // Wrap around the buffer end:
+ return m_BufferSize - m_ReadPos + m_WritePos;
+ }
+ // Single readable space partition:
+ return m_WritePos - m_ReadPos ;
+}
+
+
+
+
+
+bool cByteBuffer::CanReadBytes(int a_Count) const
+{
+ CHECK_THREAD;
+ CheckValid();
+ return (a_Count <= GetReadableSpace());
+}
+
+
+
+
+
+bool cByteBuffer::CanWriteBytes(int a_Count) const
+{
+ CHECK_THREAD;
+ CheckValid();
+ return (a_Count <= GetFreeSpace());
+}
+
+
+
+
+
+bool cByteBuffer::ReadChar(char & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ NEEDBYTES(1);
+ ReadBuf(&a_Value, 1);
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadByte(unsigned char & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ NEEDBYTES(1);
+ ReadBuf(&a_Value, 1);
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadBEShort(short & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ NEEDBYTES(2);
+ ReadBuf(&a_Value, 2);
+ a_Value = ntohs(a_Value);
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadBEInt(int & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ NEEDBYTES(4);
+ ReadBuf(&a_Value, 4);
+ a_Value = ntohl(a_Value);
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadBEInt64(Int64 & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ NEEDBYTES(8);
+ ReadBuf(&a_Value, 8);
+ a_Value = NetworkToHostLong8(&a_Value);
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadBEFloat(float & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ NEEDBYTES(4);
+ ReadBuf(&a_Value, 4);
+ a_Value = NetworkToHostFloat4(&a_Value);
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadBEDouble(double & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ NEEDBYTES(8);
+ ReadBuf(&a_Value, 8);
+ a_Value = NetworkToHostDouble8(&a_Value);
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadBool(bool & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ NEEDBYTES(1);
+ char Value = 0;
+ ReadBuf(&Value, 1);
+ a_Value = (Value != 0);
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadBEUTF16String16(AString & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ short Length;
+ if (!ReadBEShort(Length))
+ {
+ return false;
+ }
+ if (Length < 0)
+ {
+ ASSERT(!"Negative string length? Are you sure?");
+ return true;
+ }
+ return ReadUTF16String(a_Value, Length);
+}
+
+
+
+
+
+bool cByteBuffer::WriteChar(char a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ PUTBYTES(1);
+ return WriteBuf(&a_Value, 1);
+}
+
+
+
+
+
+bool cByteBuffer::WriteByte(unsigned char a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ PUTBYTES(1);
+ return WriteBuf(&a_Value, 1);
+}
+
+
+
+
+
+bool cByteBuffer::WriteBEShort(short a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ PUTBYTES(2);
+ short Converted = htons(a_Value);
+ return WriteBuf(&Converted, 2);
+}
+
+
+
+
+
+bool cByteBuffer::WriteBEInt(int a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ PUTBYTES(4);
+ int Converted = HostToNetwork4(&a_Value);
+ return WriteBuf(&Converted, 4);
+}
+
+
+
+
+
+bool cByteBuffer::WriteBEInt64(Int64 a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ PUTBYTES(8);
+ Int64 Converted = HostToNetwork8(&a_Value);
+ return WriteBuf(&Converted, 8);
+}
+
+
+
+
+
+bool cByteBuffer::WriteBEFloat(float a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ PUTBYTES(4);
+ int Converted = HostToNetwork4(&a_Value);
+ return WriteBuf(&Converted, 4);
+}
+
+
+
+
+
+bool cByteBuffer::WriteBEDouble(double a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ PUTBYTES(8);
+ Int64 Converted = HostToNetwork8(&a_Value);
+ return WriteBuf(&Converted, 8);
+}
+
+
+
+
+
+
+bool cByteBuffer::WriteBool(bool a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ return WriteChar(a_Value ? 1 : 0);
+}
+
+
+
+
+
+bool cByteBuffer::WriteBEUTF16String16(const AString & a_Value)
+{
+ CHECK_THREAD;
+ CheckValid();
+ PUTBYTES(2);
+ AString UTF16BE;
+ UTF8ToRawBEUTF16(a_Value.data(), a_Value.size(), UTF16BE);
+ WriteBEShort((short)(UTF16BE.size() / 2));
+ PUTBYTES(UTF16BE.size());
+ WriteBuf(UTF16BE.data(), UTF16BE.size());
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadBuf(void * a_Buffer, int a_Count)
+{
+ CHECK_THREAD;
+ CheckValid();
+ ASSERT(a_Count >= 0);
+ NEEDBYTES(a_Count);
+ char * Dst = (char *)a_Buffer; // So that we can do byte math
+ int BytesToEndOfBuffer = m_BufferSize - m_ReadPos;
+ ASSERT(BytesToEndOfBuffer >= 0); // Sanity check
+ if (BytesToEndOfBuffer <= a_Count)
+ {
+ // Reading across the ringbuffer end, read the first part and adjust parameters:
+ if (BytesToEndOfBuffer > 0)
+ {
+ memcpy(Dst, m_Buffer + m_ReadPos, BytesToEndOfBuffer);
+ Dst += BytesToEndOfBuffer;
+ a_Count -= BytesToEndOfBuffer;
+ }
+ m_ReadPos = 0;
+ }
+
+ // Read the rest of the bytes in a single read (guaranteed to fit):
+ if (a_Count > 0)
+ {
+ memcpy(Dst, m_Buffer + m_ReadPos, a_Count);
+ m_ReadPos += a_Count;
+ }
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::WriteBuf(const void * a_Buffer, int a_Count)
+{
+ CHECK_THREAD;
+ CheckValid();
+ ASSERT(a_Count >= 0);
+ PUTBYTES(a_Count);
+ char * Src = (char *)a_Buffer; // So that we can do byte math
+ int BytesToEndOfBuffer = m_BufferSize - m_WritePos;
+ if (BytesToEndOfBuffer <= a_Count)
+ {
+ // Reading across the ringbuffer end, read the first part and adjust parameters:
+ memcpy(m_Buffer + m_WritePos, Src, BytesToEndOfBuffer);
+ Src += BytesToEndOfBuffer;
+ a_Count -= BytesToEndOfBuffer;
+ m_WritePos = 0;
+ }
+
+ // Read the rest of the bytes in a single read (guaranteed to fit):
+ if (a_Count > 0)
+ {
+ memcpy(m_Buffer + m_WritePos, Src, a_Count);
+ m_WritePos += a_Count;
+ }
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadString(AString & a_String, int a_Count)
+{
+ CHECK_THREAD;
+ CheckValid();
+ ASSERT(a_Count >= 0);
+ NEEDBYTES(a_Count);
+ a_String.clear();
+ a_String.reserve(a_Count);
+ int BytesToEndOfBuffer = m_BufferSize - m_ReadPos;
+ ASSERT(BytesToEndOfBuffer >= 0); // Sanity check
+ if (BytesToEndOfBuffer <= a_Count)
+ {
+ // Reading across the ringbuffer end, read the first part and adjust parameters:
+ if (BytesToEndOfBuffer > 0)
+ {
+ a_String.assign(m_Buffer + m_ReadPos, BytesToEndOfBuffer);
+ a_Count -= BytesToEndOfBuffer;
+ }
+ m_ReadPos = 0;
+ }
+
+ // Read the rest of the bytes in a single read (guaranteed to fit):
+ if (a_Count > 0)
+ {
+ a_String.append(m_Buffer + m_ReadPos, a_Count);
+ m_ReadPos += a_Count;
+ }
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::ReadUTF16String(AString & a_String, int a_NumChars)
+{
+ // Reads 2 * a_NumChars bytes and interprets it as a UTF16 string, converting it into UTF8 string a_String
+ CHECK_THREAD;
+ CheckValid();
+ ASSERT(a_NumChars >= 0);
+ AString RawData;
+ if (!ReadString(RawData, a_NumChars * 2))
+ {
+ return false;
+ }
+ RawBEToUTF8((short *)(RawData.data()), a_NumChars, a_String);
+ return true;
+}
+
+
+
+
+
+bool cByteBuffer::SkipRead(int a_Count)
+{
+ CHECK_THREAD;
+ CheckValid();
+ ASSERT(a_Count >= 0);
+ if (!CanReadBytes(a_Count))
+ {
+ return false;
+ }
+ AdvanceReadPos(a_Count);
+ return true;
+}
+
+
+
+
+
+void cByteBuffer::ReadAll(AString & a_Data)
+{
+ CHECK_THREAD;
+ CheckValid();
+ ReadString(a_Data, GetReadableSpace());
+}
+
+
+
+
+
+void cByteBuffer::CommitRead(void)
+{
+ CHECK_THREAD;
+ CheckValid();
+ m_DataStart = m_ReadPos;
+}
+
+
+
+
+
+void cByteBuffer::ResetRead(void)
+{
+ CHECK_THREAD;
+ CheckValid();
+ m_ReadPos = m_DataStart;
+}
+
+
+
+
+
+void cByteBuffer::ReadAgain(AString & a_Out)
+{
+ // Return the data between m_DataStart and m_ReadPos (the data that has been read but not committed)
+ // Used by ProtoProxy to repeat communication twice, once for parsing and the other time for the remote party
+ CHECK_THREAD;
+ CheckValid();
+ int DataStart = m_DataStart;
+ if (m_ReadPos < m_DataStart)
+ {
+ // Across the ringbuffer end, read the first part and adjust next part's start:
+ a_Out.append(m_Buffer + m_DataStart, m_BufferSize - m_DataStart);
+ DataStart = 0;
+ }
+ a_Out.append(m_Buffer + DataStart, m_ReadPos - DataStart);
+}
+
+
+
+
+
+void cByteBuffer::AdvanceReadPos(int a_Count)
+{
+ CHECK_THREAD;
+ CheckValid();
+ m_ReadPos += a_Count;
+ if (m_ReadPos > m_BufferSize)
+ {
+ m_ReadPos -= m_BufferSize;
+ }
+}
+
+
+
+
+
+void cByteBuffer::CheckValid(void) const
+{
+ ASSERT(m_ReadPos >= 0);
+ ASSERT(m_ReadPos < m_BufferSize);
+ ASSERT(m_WritePos >= 0);
+ ASSERT(m_WritePos < m_BufferSize);
+}
+
+
+
+
diff --git a/source/ByteBuffer.h b/source/ByteBuffer.h
index ccc1ddfa1..650eda5b0 100644
--- a/source/ByteBuffer.h
+++ b/source/ByteBuffer.h
@@ -1,121 +1,121 @@
-
-// ByteStream.h
-
-// Interfaces to the cByteBuffer class representing a ringbuffer of bytes
-
-
-
-
-
-#pragma once
-
-
-
-
-
-/** An object that can store incoming bytes and lets its clients read the bytes sequentially
-The bytes are stored in a ringbuffer of constant size; if more than that size
-is requested, the write operation fails.
-The bytes stored can be retrieved using various ReadXXX functions; these assume that the needed
-number of bytes are present in the buffer (ASSERT; for performance reasons).
-The reading doesn't actually remove the bytes, it only moves the internal read ptr.
-To remove the bytes, call CommitRead().
-To re-start reading from the beginning, call ResetRead().
-This class doesn't implement thread safety, the clients of this class need to provide
-their own synchronization.
-*/
-class cByteBuffer
-{
-public:
- cByteBuffer(int a_BufferSize);
- ~cByteBuffer();
-
- /// Writes the bytes specified to the ringbuffer. Returns true if successful, false if not
- bool Write(const char * a_Bytes, int a_Count);
-
- /// Returns the number of bytes that can be successfully written to the ringbuffer
- int GetFreeSpace(void) const;
-
- /// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes()
- int GetUsedSpace(void) const;
-
- /// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already)
- int GetReadableSpace(void) const;
-
- /// Returns true if the specified amount of bytes are available for reading
- bool CanReadBytes(int a_Count) const;
-
- /// Returns true if the specified amount of bytes are available for writing
- bool CanWriteBytes(int a_Count) const;
-
- // Read the specified datatype and advance the read pointer; return true if successfully read:
- bool ReadChar (char & a_Value);
- bool ReadByte (unsigned char & a_Value);
- bool ReadBEShort (short & a_Value);
- bool ReadBEInt (int & a_Value);
- bool ReadBEInt64 (Int64 & a_Value);
- bool ReadBEFloat (float & a_Value);
- bool ReadBEDouble (double & a_Value);
- bool ReadBool (bool & a_Value);
- bool ReadBEUTF16String16(AString & a_Value);
-
- // Write the specified datatype; return true if successfully written
- bool WriteChar (char a_Value);
- bool WriteByte (unsigned char a_Value);
- bool WriteBEShort (short a_Value);
- bool WriteBEInt (int a_Value);
- bool WriteBEInt64 (Int64 a_Value);
- bool WriteBEFloat (float a_Value);
- bool WriteBEDouble (double a_Value);
- bool WriteBool (bool a_Value);
- bool WriteBEUTF16String16(const AString & a_Value);
-
- /// Reads a_Count bytes into a_Buffer; returns true if successful
- bool ReadBuf(void * a_Buffer, int a_Count);
-
- /// Writes a_Count bytes into a_Buffer; returns true if successful
- bool WriteBuf(const void * a_Buffer, int a_Count);
-
- /// Reads a_Count bytes into a_String; returns true if successful
- bool ReadString(AString & a_String, int a_Count);
-
- /// Reads 2 * a_NumChars bytes and interprets it as a UTF16-BE string, converting it into UTF8 string a_String
- bool ReadUTF16String(AString & a_String, int a_NumChars);
-
- /// Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer
- bool SkipRead(int a_Count);
-
- /// Reads all available data into a_Data
- void ReadAll(AString & a_Data);
-
- /// Removes the bytes that have been read from the ringbuffer
- void CommitRead(void);
-
- /// Restarts next reading operation at the start of the ringbuffer
- void ResetRead(void);
-
- /// Re-reads the data that has been read since the last commit to the current readpos. Used by ProtoProxy to duplicate communication
- void ReadAgain(AString & a_Out);
-
- /// Checks if the internal state is valid (read and write positions in the correct bounds) using ASSERTs
- void CheckValid(void) const;
-
-protected:
- char * m_Buffer;
- int m_BufferSize; // Total size of the ringbuffer
-
- #ifdef _DEBUG
- unsigned long m_ThreadID; // Thread that is currently accessing the object, checked via cSingleThreadAccessChecker
- #endif // _DEBUG
-
- int m_DataStart; // Where the data starts in the ringbuffer
- int m_WritePos; // Where the data ends in the ringbuffer
- int m_ReadPos; // Where the next read will start in the ringbuffer
-
- /// Advances the m_ReadPos by a_Count bytes
- void AdvanceReadPos(int a_Count);
-} ;
-
-
-
-
+
+// ByteStream.h
+
+// Interfaces to the cByteBuffer class representing a ringbuffer of bytes
+
+
+
+
+
+#pragma once
+
+
+
+
+
+/** An object that can store incoming bytes and lets its clients read the bytes sequentially
+The bytes are stored in a ringbuffer of constant size; if more than that size
+is requested, the write operation fails.
+The bytes stored can be retrieved using various ReadXXX functions; these assume that the needed
+number of bytes are present in the buffer (ASSERT; for performance reasons).
+The reading doesn't actually remove the bytes, it only moves the internal read ptr.
+To remove the bytes, call CommitRead().
+To re-start reading from the beginning, call ResetRead().
+This class doesn't implement thread safety, the clients of this class need to provide
+their own synchronization.
+*/
+class cByteBuffer
+{
+public:
+ cByteBuffer(int a_BufferSize);
+ ~cByteBuffer();
+
+ /// Writes the bytes specified to the ringbuffer. Returns true if successful, false if not
+ bool Write(const char * a_Bytes, int a_Count);
+
+ /// Returns the number of bytes that can be successfully written to the ringbuffer
+ int GetFreeSpace(void) const;
+
+ /// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes()
+ int GetUsedSpace(void) const;
+
+ /// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already)
+ int GetReadableSpace(void) const;
+
+ /// Returns true if the specified amount of bytes are available for reading
+ bool CanReadBytes(int a_Count) const;
+
+ /// Returns true if the specified amount of bytes are available for writing
+ bool CanWriteBytes(int a_Count) const;
+
+ // Read the specified datatype and advance the read pointer; return true if successfully read:
+ bool ReadChar (char & a_Value);
+ bool ReadByte (unsigned char & a_Value);
+ bool ReadBEShort (short & a_Value);
+ bool ReadBEInt (int & a_Value);
+ bool ReadBEInt64 (Int64 & a_Value);
+ bool ReadBEFloat (float & a_Value);
+ bool ReadBEDouble (double & a_Value);
+ bool ReadBool (bool & a_Value);
+ bool ReadBEUTF16String16(AString & a_Value);
+
+ // Write the specified datatype; return true if successfully written
+ bool WriteChar (char a_Value);
+ bool WriteByte (unsigned char a_Value);
+ bool WriteBEShort (short a_Value);
+ bool WriteBEInt (int a_Value);
+ bool WriteBEInt64 (Int64 a_Value);
+ bool WriteBEFloat (float a_Value);
+ bool WriteBEDouble (double a_Value);
+ bool WriteBool (bool a_Value);
+ bool WriteBEUTF16String16(const AString & a_Value);
+
+ /// Reads a_Count bytes into a_Buffer; returns true if successful
+ bool ReadBuf(void * a_Buffer, int a_Count);
+
+ /// Writes a_Count bytes into a_Buffer; returns true if successful
+ bool WriteBuf(const void * a_Buffer, int a_Count);
+
+ /// Reads a_Count bytes into a_String; returns true if successful
+ bool ReadString(AString & a_String, int a_Count);
+
+ /// Reads 2 * a_NumChars bytes and interprets it as a UTF16-BE string, converting it into UTF8 string a_String
+ bool ReadUTF16String(AString & a_String, int a_NumChars);
+
+ /// Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer
+ bool SkipRead(int a_Count);
+
+ /// Reads all available data into a_Data
+ void ReadAll(AString & a_Data);
+
+ /// Removes the bytes that have been read from the ringbuffer
+ void CommitRead(void);
+
+ /// Restarts next reading operation at the start of the ringbuffer
+ void ResetRead(void);
+
+ /// Re-reads the data that has been read since the last commit to the current readpos. Used by ProtoProxy to duplicate communication
+ void ReadAgain(AString & a_Out);
+
+ /// Checks if the internal state is valid (read and write positions in the correct bounds) using ASSERTs
+ void CheckValid(void) const;
+
+protected:
+ char * m_Buffer;
+ int m_BufferSize; // Total size of the ringbuffer
+
+ #ifdef _DEBUG
+ unsigned long m_ThreadID; // Thread that is currently accessing the object, checked via cSingleThreadAccessChecker
+ #endif // _DEBUG
+
+ int m_DataStart; // Where the data starts in the ringbuffer
+ int m_WritePos; // Where the data ends in the ringbuffer
+ int m_ReadPos; // Where the next read will start in the ringbuffer
+
+ /// Advances the m_ReadPos by a_Count bytes
+ void AdvanceReadPos(int a_Count);
+} ;
+
+
+
+
diff --git a/source/ClientHandle.cpp b/source/ClientHandle.cpp
index 155eac38a..52c4b3061 100644
--- a/source/ClientHandle.cpp
+++ b/source/ClientHandle.cpp
@@ -555,11 +555,8 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, ch
cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem());
if (ItemHandler->IsFood())
{
- if (PlgMgr->CallHookPlayerEating(*m_Player))
- {
- // A plugin doesn't agree with the action. The plugin itself is responsible for handling the consequences (possible inventory mismatch)
- return;
- }
+ m_Player->AbortEating();
+ return;
}
else
{
@@ -569,7 +566,7 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, ch
return;
}
}
- LOGINFO("%s: Status SHOOT / EAT not implemented", __FUNCTION__);
+ LOGINFO("%s: Status SHOOT not implemented", __FUNCTION__);
return;
}
@@ -804,15 +801,19 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, c
}
else if (ItemHandler->IsFood())
{
- cItem Item;
- Item.m_ItemType = Equipped.m_ItemType;
- Item.m_ItemCount = 1;
- if (ItemHandler->EatItem(m_Player, &Item))
+ if (m_Player->IsSatiated())
{
- ItemHandler->OnFoodEaten(World, m_Player, &Item);
- m_Player->GetInventory().RemoveOneEquippedItem();
+ // The player is satiated, they cannot eat
return;
}
+ m_Player->StartEating();
+ if (PlgMgr->CallHookPlayerEating(*m_Player))
+ {
+ // A plugin won't let us eat, abort (send the proper packets to the client, too):
+ m_Player->AbortEating();
+ return;
+ }
+ return;
}
else
{
@@ -1069,6 +1070,10 @@ void cClientHandle::HandleUseEntity(int a_TargetEntityID, bool a_IsLeftClick)
cPlayer & m_Player;
virtual bool Item(cEntity * a_Entity) override
{
+ if (cPluginManager::Get()->CallHookPlayerRightClickingEntity(m_Player, *a_Entity))
+ {
+ return false;
+ }
a_Entity->OnRightClicked(m_Player);
return false;
}
@@ -1234,6 +1239,23 @@ void cClientHandle::HandleUnmount(void)
+void cClientHandle::HandleTabCompletion(const AString & a_Text)
+{
+ AStringVector Results;
+ m_Player->GetWorld()->TabCompleteUserName(a_Text, Results);
+ cRoot::Get()->GetPluginManager()->TabCompleteCommand(a_Text, Results, m_Player);
+ if (Results.empty())
+ {
+ return;
+ }
+ std::sort(Results.begin(), Results.end());
+ SendTabCompletionResults(Results);
+}
+
+
+
+
+
void cClientHandle::SendData(const char * a_Data, int a_Size)
{
{
@@ -1490,6 +1512,15 @@ void cClientHandle::SendDisconnect(const AString & a_Reason)
+void cClientHandle::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ m_Protocol->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
+}
+
+
+
+
+
void cClientHandle::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
{
m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item);
@@ -1771,6 +1802,15 @@ void cClientHandle::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleTy
+void cClientHandle::SendTabCompletionResults(const AStringVector & a_Results)
+{
+ m_Protocol->SendTabCompletionResults(a_Results);
+}
+
+
+
+
+
void cClientHandle::SendTeleportEntity(const cEntity & a_Entity)
{
m_Protocol->SendTeleportEntity(a_Entity);
diff --git a/source/ClientHandle.h b/source/ClientHandle.h
index a06aca39f..73edaf73c 100644
--- a/source/ClientHandle.h
+++ b/source/ClientHandle.h
@@ -87,55 +87,57 @@ public:
// The following functions send the various packets:
// (Please keep these alpha-sorted)
- void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle);
- void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType);
- void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage);
- void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); // tolua_export
- void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes);
- void SendChat (const AString & a_Message);
- void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer);
- void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player);
- void SendDestroyEntity (const cEntity & a_Entity);
- void SendDisconnect (const AString & a_Reason);
- void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item);
- void SendEntityHeadLook (const cEntity & a_Entity);
- void SendEntityLook (const cEntity & a_Entity);
- void SendEntityMetadata (const cEntity & a_Entity);
- void SendEntityProperties (const cEntity & a_Entity);
- void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ);
- void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ);
- void SendEntityStatus (const cEntity & a_Entity, char a_Status);
- void SendEntityVelocity (const cEntity & a_Entity);
- void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion);
- void SendGameMode (eGameMode a_GameMode);
- void SendHealth (void);
- void SendInventoryProgress (char a_WindowID, short a_Progressbar, short a_Value);
- void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item);
- void SendPickupSpawn (const cPickup & a_Pickup);
- void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation);
- void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline);
- void SendPlayerMaxSpeed (void); ///< Informs the client of the maximum player speed (1.6.1+)
- void SendPlayerMoveLook (void);
- void SendPlayerPosition (void);
- void SendPlayerSpawn (const cPlayer & a_Player);
- void SendRespawn (void);
- void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch); // a_Src coords are Block * 8
- void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data);
- void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock);
- void SendSpawnMob (const cMonster & a_Mob);
- void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch);
- void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType);
- void SendTeleportEntity (const cEntity & a_Entity);
- void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ);
- void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay);
- void SendUnloadChunk (int a_ChunkX, int a_ChunkZ);
- void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4);
- void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ );
- void SendWeather (eWeather a_Weather);
- void SendWholeInventory (const cInventory & a_Inventory);
- void SendWholeInventory (const cWindow & a_Window);
- void SendWindowClose (const cWindow & a_Window);
- void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots);
+ void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle);
+ void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType);
+ void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage);
+ void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); // tolua_export
+ void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes);
+ void SendChat (const AString & a_Message);
+ void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer);
+ void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player);
+ void SendDestroyEntity (const cEntity & a_Entity);
+ void SendDisconnect (const AString & a_Reason);
+ void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ);
+ void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item);
+ void SendEntityHeadLook (const cEntity & a_Entity);
+ void SendEntityLook (const cEntity & a_Entity);
+ void SendEntityMetadata (const cEntity & a_Entity);
+ void SendEntityProperties (const cEntity & a_Entity);
+ void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ);
+ void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ);
+ void SendEntityStatus (const cEntity & a_Entity, char a_Status);
+ void SendEntityVelocity (const cEntity & a_Entity);
+ void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion);
+ void SendGameMode (eGameMode a_GameMode);
+ void SendHealth (void);
+ void SendInventoryProgress (char a_WindowID, short a_Progressbar, short a_Value);
+ void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item);
+ void SendPickupSpawn (const cPickup & a_Pickup);
+ void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation);
+ void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline);
+ void SendPlayerMaxSpeed (void); ///< Informs the client of the maximum player speed (1.6.1+)
+ void SendPlayerMoveLook (void);
+ void SendPlayerPosition (void);
+ void SendPlayerSpawn (const cPlayer & a_Player);
+ void SendRespawn (void);
+ void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch); // a_Src coords are Block * 8
+ void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data);
+ void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock);
+ void SendSpawnMob (const cMonster & a_Mob);
+ void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch);
+ void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType);
+ void SendTabCompletionResults(const AStringVector & a_Results);
+ void SendTeleportEntity (const cEntity & a_Entity);
+ void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ);
+ void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay);
+ void SendUnloadChunk (int a_ChunkX, int a_ChunkZ);
+ void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4);
+ void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ );
+ void SendWeather (eWeather a_Weather);
+ void SendWholeInventory (const cInventory & a_Inventory);
+ void SendWholeInventory (const cWindow & a_Window);
+ void SendWindowClose (const cWindow & a_Window);
+ void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots);
const AString & GetUsername(void) const; // tolua_export
void SetUsername( const AString & a_Username ); // tolua_export
@@ -159,31 +161,32 @@ public:
void PacketError(unsigned char a_PacketType);
// Calls that cProtocol descendants use for handling packets:
- void HandlePing (void);
+ void HandleAnimation (char a_Animation);
+ void HandleChat (const AString & a_Message);
void HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem);
- void HandlePlayerPos (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround);
+ void HandleDisconnect (const AString & a_Reason);
+ void HandleEntityAction (int a_EntityID, char a_ActionID);
+ bool HandleHandshake (const AString & a_Username);
+ void HandleKeepAlive (int a_KeepAliveID);
void HandleLeftClick (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status);
- void HandleRightClick (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_HeldItem);
- void HandleChat (const AString & a_Message);
+ void HandlePing (void);
void HandlePlayerLook (float a_Rotation, float a_Pitch, bool a_IsOnGround);
void HandlePlayerMoveLook (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround); // While m_bPositionConfirmed (normal gameplay)
- void HandleAnimation (char a_Animation);
+ void HandlePlayerPos (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround);
+ void HandleRespawn (void);
+ void HandleRightClick (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_HeldItem);
void HandleSlotSelected (short a_SlotNum);
- void HandleWindowClose (char a_WindowID);
- void HandleWindowClick (char a_WindowID, short a_SlotNum, eClickAction a_ClickAction, const cItem & a_HeldItem);
+ void HandleTabCompletion (const AString & a_Text);
void HandleUpdateSign (
int a_BlockX, int a_BlockY, int a_BlockZ,
const AString & a_Line1, const AString & a_Line2,
const AString & a_Line3, const AString & a_Line4
);
- void HandleUseEntity (int a_TargetEntityID, bool a_IsLeftClick);
- void HandleRespawn (void);
- void HandleDisconnect (const AString & a_Reason);
- void HandleKeepAlive (int a_KeepAliveID);
- bool HandleHandshake (const AString & a_Username);
- void HandleEntityAction (int a_EntityID, char a_ActionID);
void HandleUnmount (void);
-
+ void HandleUseEntity (int a_TargetEntityID, bool a_IsLeftClick);
+ void HandleWindowClick (char a_WindowID, short a_SlotNum, eClickAction a_ClickAction, const cItem & a_HeldItem);
+ void HandleWindowClose (char a_WindowID);
+
/** Called when the protocol has finished logging the user in.
Return true to allow the user in; false to kick them.
*/
@@ -281,6 +284,9 @@ private:
/// Buffer for received messages to be processed in the Tick thread
AStringList m_PendingMessages;
+
+ static int s_ClientCount;
+ int m_UniqueID;
@@ -307,14 +313,11 @@ private:
/// Processes the messages in m_PendingMessages; called from the Tick thread
void ProcessPendingMessages(void);
-
+
// cSocketThreads::cCallback overrides:
virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client
virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client
virtual void SocketClosed (void) override; // The socket has been closed for any reason
-
- static int s_ClientCount;
- int m_UniqueID;
}; // tolua_export
diff --git a/source/CommandOutput.cpp b/source/CommandOutput.cpp
index 49102b38c..c221682a1 100644
--- a/source/CommandOutput.cpp
+++ b/source/CommandOutput.cpp
@@ -1,71 +1,71 @@
-
-// CommandOutput.cpp
-
-// Implements the various classes that process command output
-
-#include "Globals.h"
-#include "CommandOutput.h"
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cCommandOutputCallback:
-
-void cCommandOutputCallback::Out(const char * a_Fmt, ...)
-{
- AString Output;
- va_list args;
- va_start(args, a_Fmt);
- AppendVPrintf(Output, a_Fmt, args);
- va_end(args);
- Output.append("\n");
- Out(Output);
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cLogCommandOutputCallback:
-
-void cLogCommandOutputCallback::Out(const AString & a_Text)
-{
- m_Buffer.append(a_Text);
-}
-
-
-
-
-
-void cLogCommandOutputCallback::Finished(void)
-{
- // Log each line separately:
- size_t len = m_Buffer.length();
- size_t last = 0;
- for (size_t i = 0; i < len; i++)
- {
- switch (m_Buffer[i])
- {
- case '\n':
- {
- LOG(m_Buffer.substr(last, i - last).c_str());
- last = i + 1;
- break;
- }
- }
- } // for i - m_Buffer[]
- if (last < len)
- {
- LOG(m_Buffer.substr(last).c_str());
- }
-
- // Clear the buffer for the next command output:
- m_Buffer.clear();
-}
-
-
-
-
+
+// CommandOutput.cpp
+
+// Implements the various classes that process command output
+
+#include "Globals.h"
+#include "CommandOutput.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCommandOutputCallback:
+
+void cCommandOutputCallback::Out(const char * a_Fmt, ...)
+{
+ AString Output;
+ va_list args;
+ va_start(args, a_Fmt);
+ AppendVPrintf(Output, a_Fmt, args);
+ va_end(args);
+ Output.append("\n");
+ Out(Output);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cLogCommandOutputCallback:
+
+void cLogCommandOutputCallback::Out(const AString & a_Text)
+{
+ m_Buffer.append(a_Text);
+}
+
+
+
+
+
+void cLogCommandOutputCallback::Finished(void)
+{
+ // Log each line separately:
+ size_t len = m_Buffer.length();
+ size_t last = 0;
+ for (size_t i = 0; i < len; i++)
+ {
+ switch (m_Buffer[i])
+ {
+ case '\n':
+ {
+ LOG(m_Buffer.substr(last, i - last).c_str());
+ last = i + 1;
+ break;
+ }
+ }
+ } // for i - m_Buffer[]
+ if (last < len)
+ {
+ LOG(m_Buffer.substr(last).c_str());
+ }
+
+ // Clear the buffer for the next command output:
+ m_Buffer.clear();
+}
+
+
+
+
diff --git a/source/CommandOutput.h b/source/CommandOutput.h
index 68217dbb4..bdf675238 100644
--- a/source/CommandOutput.h
+++ b/source/CommandOutput.h
@@ -1,82 +1,82 @@
-
-// CommandOutput.h
-
-// Declares various classes that process command output
-
-
-
-
-
-/** Interface for a callback that receives command output
-The Out() function is called for any output the command has produced.
-Descendants override that function to provide specific processing of the output.
-*/
-class cCommandOutputCallback
-{
-public:
- virtual ~cCommandOutputCallback() {}; // Force a virtual destructor in subclasses
-
- /// Syntax sugar function, calls Out() with Printf()-ed parameters; appends a "\n"
- void Out(const char * a_Fmt, ...);
-
- /// Called when the command wants to output anything; may be called multiple times
- virtual void Out(const AString & a_Text) = 0;
-
- /// Called when the command processing has been finished
- virtual void Finished(void) {};
-} ;
-
-
-
-
-
-/// Class that discards all command output
-class cNullCommandOutputCallback :
- public cCommandOutputCallback
-{
- // cCommandOutputCallback overrides:
- virtual void Out(const AString & a_Text) override
- {
- // Do nothing
- }
-} ;
-
-
-
-
-
-
-/// Sends all command output to a log, line by line, when the command finishes processing
-class cLogCommandOutputCallback :
- public cCommandOutputCallback
-{
-public:
- // cCommandOutputCallback overrides:
- virtual void Out(const AString & a_Text) override;
- virtual void Finished(void) override;
-
-protected:
- /// Output is stored here until the command finishes processing
- AString m_Buffer;
-} ;
-
-
-
-
-
-/// Sends all command output to a log, line by line; deletes self when command finishes processing
-class cLogCommandDeleteSelfOutputCallback :
- public cLogCommandOutputCallback
-{
- typedef cLogCommandOutputCallback super;
-
- virtual void Finished(void) override
- {
- super::Finished();
- delete this;
- }
-} ;
-
-
-
-
+
+// CommandOutput.h
+
+// Declares various classes that process command output
+
+
+
+
+
+/** Interface for a callback that receives command output
+The Out() function is called for any output the command has produced.
+Descendants override that function to provide specific processing of the output.
+*/
+class cCommandOutputCallback
+{
+public:
+ virtual ~cCommandOutputCallback() {}; // Force a virtual destructor in subclasses
+
+ /// Syntax sugar function, calls Out() with Printf()-ed parameters; appends a "\n"
+ void Out(const char * a_Fmt, ...);
+
+ /// Called when the command wants to output anything; may be called multiple times
+ virtual void Out(const AString & a_Text) = 0;
+
+ /// Called when the command processing has been finished
+ virtual void Finished(void) {};
+} ;
+
+
+
+
+
+/// Class that discards all command output
+class cNullCommandOutputCallback :
+ public cCommandOutputCallback
+{
+ // cCommandOutputCallback overrides:
+ virtual void Out(const AString & a_Text) override
+ {
+ // Do nothing
+ }
+} ;
+
+
+
+
+
+
+/// Sends all command output to a log, line by line, when the command finishes processing
+class cLogCommandOutputCallback :
+ public cCommandOutputCallback
+{
+public:
+ // cCommandOutputCallback overrides:
+ virtual void Out(const AString & a_Text) override;
+ virtual void Finished(void) override;
+
+protected:
+ /// Output is stored here until the command finishes processing
+ AString m_Buffer;
+} ;
+
+
+
+
+
+/// Sends all command output to a log, line by line; deletes self when command finishes processing
+class cLogCommandDeleteSelfOutputCallback :
+ public cLogCommandOutputCallback
+{
+ typedef cLogCommandOutputCallback super;
+
+ virtual void Finished(void) override
+ {
+ super::Finished();
+ delete this;
+ }
+} ;
+
+
+
+
diff --git a/source/Defines.h b/source/Defines.h
index f52050a9b..94e618eb0 100644
--- a/source/Defines.h
+++ b/source/Defines.h
@@ -116,6 +116,10 @@ enum eGameMode
gmSurvival = eGameMode_Survival,
gmCreative = eGameMode_Creative,
gmAdventure = eGameMode_Adventure,
+
+ // These two are used to check GameMode for validity when converting from integers.
+ gmMax, // Gets automatically assigned
+ gmMin = 0,
} ;
diff --git a/source/Enchantments.cpp b/source/Enchantments.cpp
index 08a24d8f3..0caf4eb11 100644
--- a/source/Enchantments.cpp
+++ b/source/Enchantments.cpp
@@ -1,298 +1,298 @@
-
-// Enchantments.cpp
-
-// Implements the cEnchantments class representing a storage for item enchantments and stored-enchantments
-
-#include "Globals.h"
-#include "Enchantments.h"
-#include "WorldStorage/FastNBT.h"
-
-
-
-
-
-cEnchantments::cEnchantments(void)
-{
- // Nothing needed yet, but the constructor needs to be declared and impemented in order to be usable
-}
-
-
-
-
-
-cEnchantments::cEnchantments(const AString & a_StringSpec)
-{
- AddFromString(a_StringSpec);
-}
-
-
-
-
-
-void cEnchantments::AddFromString(const AString & a_StringSpec)
-{
- // Add enchantments in the stringspec; if a specified enchantment already exists, overwrites it
-
- // Split the StringSpec into separate declarations, each in the form "id=lvl":
- AStringVector Decls = StringSplit(a_StringSpec, ";");
- for (AStringVector::const_iterator itr = Decls.begin(), end = Decls.end(); itr != end; ++itr)
- {
- // Split each declaration into the id and lvl part:
- if (itr->empty())
- {
- // The decl is empty (may happen if there's an extra semicolon at the end), ignore silently
- continue;
- }
- AStringVector Split = StringSplitAndTrim(*itr, "=");
- if (Split.size() != 2)
- {
- // Malformed decl
- LOG("%s: Malformed enchantment decl: \"%s\", skipping.", __FUNCTION__, itr->c_str());
- continue;
- }
- int id = atoi(Split[0].c_str());
- if ((id == 0) && (Split[0] != "0"))
- {
- id = StringToEnchantmentID(Split[0]);
- }
- int lvl = atoi(Split[1].c_str());
- if (
- ((id <= 0) && (Split[0] != "0")) ||
- ((lvl == 0) && (Split[1] != "0"))
- )
- {
- // Numbers failed to parse
- LOG("%s: Failed to parse enchantment declaration for numbers: \"%s\" and \"%s\", skipping.",
- __FUNCTION__, Split[0].c_str(), Split[1].c_str()
- );
- continue;
- }
- SetLevel(id, lvl);
- } // for itr - Decls[]
-}
-
-
-
-
-
-AString cEnchantments::ToString(void) const
-{
- // Serialize all the enchantments into a string
- AString res;
- for (cEnchantments::cMap::const_iterator itr = m_Enchantments.begin(), end = m_Enchantments.end(); itr != end; ++itr)
- {
- AppendPrintf(res, "%d=%d;", itr->first, itr->second);
- } // for itr - m_Enchantments[]
- return res;
-}
-
-
-
-
-
-int cEnchantments::GetLevel(int a_EnchantmentID) const
-{
- // Return the level for the specified enchantment; 0 if not stored
- cMap::const_iterator itr = m_Enchantments.find(a_EnchantmentID);
- if (itr != m_Enchantments.end())
- {
- return itr->second;
- }
-
- // Not stored, return zero
- return 0;
-}
-
-
-
-
-
-void cEnchantments::SetLevel(int a_EnchantmentID, int a_Level)
-{
- // Sets the level for the specified enchantment, adding it if not stored before or removing it if level <= 0
- if (a_Level == 0)
- {
- // Delete enchantment, if present:
- cMap::iterator itr = m_Enchantments.find(a_EnchantmentID);
- if (itr != m_Enchantments.end())
- {
- m_Enchantments.erase(itr);
- }
- }
- else
- {
- // Add / overwrite enchantment
- m_Enchantments[a_EnchantmentID] = a_Level;
- }
-}
-
-
-
-
-
-
-void cEnchantments::Clear(void)
-{
- m_Enchantments.clear();
-}
-
-
-
-
-
-bool cEnchantments::IsEmpty(void) const
-{
- return m_Enchantments.empty();
-}
-
-
-
-
-
-int cEnchantments::StringToEnchantmentID(const AString & a_EnchantmentName)
-{
- struct
- {
- int m_Value;
- const char * m_Name;
- } EnchantmentNames[] =
- {
- { enchProtection, "Protection"},
- { enchFireProtection, "FireProtection"},
- { enchFeatherFalling, "FeatherFalling"},
- { enchBlastProtection, "BlastProtection"},
- { enchProjectileProtection, "ProjectileProtection"},
- { enchRespiration, "Respiration"},
- { enchAquaAffinity, "AquaAffinity"},
- { enchThorns, "Thorns"},
- { enchSharpness, "Sharpness"},
- { enchSmite, "Smite"},
- { enchBaneOfArthropods, "BaneOfArthropods"},
- { enchKnockback, "Knockback"},
- { enchFireAspect, "FireAspect"},
- { enchLooting, "Looting"},
- { enchEfficiency, "Efficiency"},
- { enchSilkTouch, "SilkTouch"},
- { enchUnbreaking, "Unbreaking"},
- { enchFortune, "Fortune"},
- { enchPower, "Power"},
- { enchPunch, "Punch"},
- { enchFlame, "Flame"},
- { enchInfinity, "Infinity"},
- } ;
- for (int i = 0; i < ARRAYCOUNT(EnchantmentNames); i++)
- {
- if (NoCaseCompare(EnchantmentNames[i].m_Name, a_EnchantmentName) == 0)
- {
- return EnchantmentNames[i].m_Value;
- }
- } // for i - EnchantmentNames[]
- return -1;
-}
-
-
-
-
-
-bool cEnchantments::operator ==(const cEnchantments & a_Other) const
-{
- return m_Enchantments == a_Other.m_Enchantments;
-}
-
-
-
-
-
-bool cEnchantments::operator !=(const cEnchantments & a_Other) const
-{
- return m_Enchantments != a_Other.m_Enchantments;
-}
-
-
-
-
-
-void cEnchantments::WriteToNBTCompound(cFastNBTWriter & a_Writer, const AString & a_ListTagName) const
-{
- // Write the enchantments into the specified NBT writer
- // begin with the LIST tag of the specified name ("ench" or "StoredEnchantments")
-
- a_Writer.BeginList(a_ListTagName, TAG_Compound);
- for (cMap::const_iterator itr = m_Enchantments.begin(), end = m_Enchantments.end(); itr != end; ++itr)
- {
- a_Writer.BeginCompound("");
- a_Writer.AddShort("id", itr->first);
- a_Writer.AddShort("lvl", itr->second);
- a_Writer.EndCompound();
- } // for itr - m_Enchantments[]
- a_Writer.EndList();
-}
-
-
-
-
-
-void cEnchantments::ParseFromNBT(const cParsedNBT & a_NBT, int a_EnchListTagIdx)
-{
- // Read the enchantments from the specified NBT list tag (ench or StoredEnchantments)
-
- // Verify that the tag is a list:
- if (a_NBT.GetType(a_EnchListTagIdx) != TAG_List)
- {
- LOGWARNING("%s: Invalid EnchListTag type: exp %d, got %d. Enchantments not parsed",
- __FUNCTION__, TAG_List, a_NBT.GetType(a_EnchListTagIdx)
- );
- ASSERT(!"Bad EnchListTag type");
- return;
- }
-
- // Verify that the list is of Compounds:
- if (a_NBT.GetChildrenType(a_EnchListTagIdx) != TAG_Compound)
- {
- LOGWARNING("%s: Invalid NBT list children type: exp %d, got %d. Enchantments not parsed",
- __FUNCTION__, TAG_Compound, a_NBT.GetChildrenType(a_EnchListTagIdx)
- );
- ASSERT(!"Bad EnchListTag children type");
- return;
- }
-
- Clear();
-
- // Iterate over all the compound children, parse an enchantment from each:
- for (int tag = a_NBT.GetFirstChild(a_EnchListTagIdx); tag >= 0; tag = a_NBT.GetNextSibling(tag))
- {
- // tag is the compound inside the "ench" list tag
- ASSERT(a_NBT.GetType(tag) == TAG_Compound);
-
- // Search for the id and lvl tags' values:
- int id = -1, lvl = -1;
- for (int ch = a_NBT.GetFirstChild(tag); ch >= 0; ch = a_NBT.GetNextSibling(ch))
- {
- if (a_NBT.GetType(ch) != TAG_Short)
- {
- continue;
- }
- if (a_NBT.GetName(ch) == "id")
- {
- id = a_NBT.GetShort(ch);
- }
- else if (a_NBT.GetName(ch) == "lvl")
- {
- lvl = a_NBT.GetShort(ch);
- }
- } // for ch - children of the compound tag
-
- if ((id == -1) || (lvl <= 0))
- {
- // Failed to parse either the id or the lvl, skip this compound
- continue;
- }
-
- // Store the enchantment:
- m_Enchantments[id] = lvl;
- } // for tag - children of the ench list tag
-}
-
-
-
-
+
+// Enchantments.cpp
+
+// Implements the cEnchantments class representing a storage for item enchantments and stored-enchantments
+
+#include "Globals.h"
+#include "Enchantments.h"
+#include "WorldStorage/FastNBT.h"
+
+
+
+
+
+cEnchantments::cEnchantments(void)
+{
+ // Nothing needed yet, but the constructor needs to be declared and impemented in order to be usable
+}
+
+
+
+
+
+cEnchantments::cEnchantments(const AString & a_StringSpec)
+{
+ AddFromString(a_StringSpec);
+}
+
+
+
+
+
+void cEnchantments::AddFromString(const AString & a_StringSpec)
+{
+ // Add enchantments in the stringspec; if a specified enchantment already exists, overwrites it
+
+ // Split the StringSpec into separate declarations, each in the form "id=lvl":
+ AStringVector Decls = StringSplit(a_StringSpec, ";");
+ for (AStringVector::const_iterator itr = Decls.begin(), end = Decls.end(); itr != end; ++itr)
+ {
+ // Split each declaration into the id and lvl part:
+ if (itr->empty())
+ {
+ // The decl is empty (may happen if there's an extra semicolon at the end), ignore silently
+ continue;
+ }
+ AStringVector Split = StringSplitAndTrim(*itr, "=");
+ if (Split.size() != 2)
+ {
+ // Malformed decl
+ LOG("%s: Malformed enchantment decl: \"%s\", skipping.", __FUNCTION__, itr->c_str());
+ continue;
+ }
+ int id = atoi(Split[0].c_str());
+ if ((id == 0) && (Split[0] != "0"))
+ {
+ id = StringToEnchantmentID(Split[0]);
+ }
+ int lvl = atoi(Split[1].c_str());
+ if (
+ ((id <= 0) && (Split[0] != "0")) ||
+ ((lvl == 0) && (Split[1] != "0"))
+ )
+ {
+ // Numbers failed to parse
+ LOG("%s: Failed to parse enchantment declaration for numbers: \"%s\" and \"%s\", skipping.",
+ __FUNCTION__, Split[0].c_str(), Split[1].c_str()
+ );
+ continue;
+ }
+ SetLevel(id, lvl);
+ } // for itr - Decls[]
+}
+
+
+
+
+
+AString cEnchantments::ToString(void) const
+{
+ // Serialize all the enchantments into a string
+ AString res;
+ for (cEnchantments::cMap::const_iterator itr = m_Enchantments.begin(), end = m_Enchantments.end(); itr != end; ++itr)
+ {
+ AppendPrintf(res, "%d=%d;", itr->first, itr->second);
+ } // for itr - m_Enchantments[]
+ return res;
+}
+
+
+
+
+
+int cEnchantments::GetLevel(int a_EnchantmentID) const
+{
+ // Return the level for the specified enchantment; 0 if not stored
+ cMap::const_iterator itr = m_Enchantments.find(a_EnchantmentID);
+ if (itr != m_Enchantments.end())
+ {
+ return itr->second;
+ }
+
+ // Not stored, return zero
+ return 0;
+}
+
+
+
+
+
+void cEnchantments::SetLevel(int a_EnchantmentID, int a_Level)
+{
+ // Sets the level for the specified enchantment, adding it if not stored before or removing it if level <= 0
+ if (a_Level == 0)
+ {
+ // Delete enchantment, if present:
+ cMap::iterator itr = m_Enchantments.find(a_EnchantmentID);
+ if (itr != m_Enchantments.end())
+ {
+ m_Enchantments.erase(itr);
+ }
+ }
+ else
+ {
+ // Add / overwrite enchantment
+ m_Enchantments[a_EnchantmentID] = a_Level;
+ }
+}
+
+
+
+
+
+
+void cEnchantments::Clear(void)
+{
+ m_Enchantments.clear();
+}
+
+
+
+
+
+bool cEnchantments::IsEmpty(void) const
+{
+ return m_Enchantments.empty();
+}
+
+
+
+
+
+int cEnchantments::StringToEnchantmentID(const AString & a_EnchantmentName)
+{
+ struct
+ {
+ int m_Value;
+ const char * m_Name;
+ } EnchantmentNames[] =
+ {
+ { enchProtection, "Protection"},
+ { enchFireProtection, "FireProtection"},
+ { enchFeatherFalling, "FeatherFalling"},
+ { enchBlastProtection, "BlastProtection"},
+ { enchProjectileProtection, "ProjectileProtection"},
+ { enchRespiration, "Respiration"},
+ { enchAquaAffinity, "AquaAffinity"},
+ { enchThorns, "Thorns"},
+ { enchSharpness, "Sharpness"},
+ { enchSmite, "Smite"},
+ { enchBaneOfArthropods, "BaneOfArthropods"},
+ { enchKnockback, "Knockback"},
+ { enchFireAspect, "FireAspect"},
+ { enchLooting, "Looting"},
+ { enchEfficiency, "Efficiency"},
+ { enchSilkTouch, "SilkTouch"},
+ { enchUnbreaking, "Unbreaking"},
+ { enchFortune, "Fortune"},
+ { enchPower, "Power"},
+ { enchPunch, "Punch"},
+ { enchFlame, "Flame"},
+ { enchInfinity, "Infinity"},
+ } ;
+ for (int i = 0; i < ARRAYCOUNT(EnchantmentNames); i++)
+ {
+ if (NoCaseCompare(EnchantmentNames[i].m_Name, a_EnchantmentName) == 0)
+ {
+ return EnchantmentNames[i].m_Value;
+ }
+ } // for i - EnchantmentNames[]
+ return -1;
+}
+
+
+
+
+
+bool cEnchantments::operator ==(const cEnchantments & a_Other) const
+{
+ return m_Enchantments == a_Other.m_Enchantments;
+}
+
+
+
+
+
+bool cEnchantments::operator !=(const cEnchantments & a_Other) const
+{
+ return m_Enchantments != a_Other.m_Enchantments;
+}
+
+
+
+
+
+void cEnchantments::WriteToNBTCompound(cFastNBTWriter & a_Writer, const AString & a_ListTagName) const
+{
+ // Write the enchantments into the specified NBT writer
+ // begin with the LIST tag of the specified name ("ench" or "StoredEnchantments")
+
+ a_Writer.BeginList(a_ListTagName, TAG_Compound);
+ for (cMap::const_iterator itr = m_Enchantments.begin(), end = m_Enchantments.end(); itr != end; ++itr)
+ {
+ a_Writer.BeginCompound("");
+ a_Writer.AddShort("id", itr->first);
+ a_Writer.AddShort("lvl", itr->second);
+ a_Writer.EndCompound();
+ } // for itr - m_Enchantments[]
+ a_Writer.EndList();
+}
+
+
+
+
+
+void cEnchantments::ParseFromNBT(const cParsedNBT & a_NBT, int a_EnchListTagIdx)
+{
+ // Read the enchantments from the specified NBT list tag (ench or StoredEnchantments)
+
+ // Verify that the tag is a list:
+ if (a_NBT.GetType(a_EnchListTagIdx) != TAG_List)
+ {
+ LOGWARNING("%s: Invalid EnchListTag type: exp %d, got %d. Enchantments not parsed",
+ __FUNCTION__, TAG_List, a_NBT.GetType(a_EnchListTagIdx)
+ );
+ ASSERT(!"Bad EnchListTag type");
+ return;
+ }
+
+ // Verify that the list is of Compounds:
+ if (a_NBT.GetChildrenType(a_EnchListTagIdx) != TAG_Compound)
+ {
+ LOGWARNING("%s: Invalid NBT list children type: exp %d, got %d. Enchantments not parsed",
+ __FUNCTION__, TAG_Compound, a_NBT.GetChildrenType(a_EnchListTagIdx)
+ );
+ ASSERT(!"Bad EnchListTag children type");
+ return;
+ }
+
+ Clear();
+
+ // Iterate over all the compound children, parse an enchantment from each:
+ for (int tag = a_NBT.GetFirstChild(a_EnchListTagIdx); tag >= 0; tag = a_NBT.GetNextSibling(tag))
+ {
+ // tag is the compound inside the "ench" list tag
+ ASSERT(a_NBT.GetType(tag) == TAG_Compound);
+
+ // Search for the id and lvl tags' values:
+ int id = -1, lvl = -1;
+ for (int ch = a_NBT.GetFirstChild(tag); ch >= 0; ch = a_NBT.GetNextSibling(ch))
+ {
+ if (a_NBT.GetType(ch) != TAG_Short)
+ {
+ continue;
+ }
+ if (a_NBT.GetName(ch) == "id")
+ {
+ id = a_NBT.GetShort(ch);
+ }
+ else if (a_NBT.GetName(ch) == "lvl")
+ {
+ lvl = a_NBT.GetShort(ch);
+ }
+ } // for ch - children of the compound tag
+
+ if ((id == -1) || (lvl <= 0))
+ {
+ // Failed to parse either the id or the lvl, skip this compound
+ continue;
+ }
+
+ // Store the enchantment:
+ m_Enchantments[id] = lvl;
+ } // for tag - children of the ench list tag
+}
+
+
+
+
diff --git a/source/Enchantments.h b/source/Enchantments.h
index c20da654b..cda743daf 100644
--- a/source/Enchantments.h
+++ b/source/Enchantments.h
@@ -1,114 +1,114 @@
-
-// Enchantments.h
-
-// Declares the cEnchantments class representing a storage for item enchantments and stored-enchantments
-
-
-
-
-
-#pragma once
-
-
-
-
-
-// fwd: WorldStorage/FastNBT.h
-class cFastNBTWriter;
-class cParsedNBT;
-
-
-
-
-
-// tolua_begin
-
-/** Class that stores item enchantments or stored-enchantments
-The enchantments may be serialized to a stringspec and read back from such stringspec.
-The format for the stringspec is "id=lvl;id=lvl;id=lvl...", with an optional semicolon at the end,
-mapping each enchantment's id onto its level. ID may be either a number or the enchantment name.
-Level value of 0 means no such enchantment, and it will not be stored in the m_Enchantments.
-Serialization will never put zero-level enchantments into the stringspec and will always use numeric IDs.
-*/
-class cEnchantments
-{
-public:
- /// Individual enchantment IDs, corresponding to their NBT IDs ( http://www.minecraftwiki.net/wiki/Data_Values#Enchantment_IDs )
- enum
- {
- enchProtection = 0,
- enchFireProtection = 1,
- enchFeatherFalling = 2,
- enchBlastProtection = 3,
- enchProjectileProtection = 4,
- enchRespiration = 5,
- enchAquaAffinity = 6,
- enchThorns = 7,
- enchSharpness = 16,
- enchSmite = 17,
- enchBaneOfArthropods = 18,
- enchKnockback = 19,
- enchFireAspect = 20,
- enchLooting = 21,
- enchEfficiency = 32,
- enchSilkTouch = 33,
- enchUnbreaking = 34,
- enchFortune = 35,
- enchPower = 48,
- enchPunch = 49,
- enchFlame = 50,
- enchInfinity = 51,
- } ;
-
- /// Creates an empty enchantments container
- cEnchantments(void);
-
- /// Creates an enchantments container filled with enchantments parsed from stringspec
- cEnchantments(const AString & a_StringSpec);
-
- /// Adds enchantments in the stringspec; if a specified enchantment already exists, overwrites it
- void AddFromString(const AString & a_StringSpec);
-
- /// Serializes all the enchantments into a string
- AString ToString(void) const;
-
- /// Returns the level for the specified enchantment; 0 if not stored
- int GetLevel(int a_EnchantmentID) const;
-
- /// Sets the level for the specified enchantment, adding it if not stored before or removing it if level <= 0
- void SetLevel(int a_EnchantmentID, int a_Level);
-
- /// Removes all enchantments
- void Clear(void);
-
- /// Returns true if there are no enchantments
- bool IsEmpty(void) const;
-
- /// Converts enchantment name to the numeric representation; returns -1 if enchantment name not found; case insensitive
- static int StringToEnchantmentID(const AString & a_EnchantmentName);
-
- /// Returns true if a_Other contains exactly the same enchantments and levels
- bool operator ==(const cEnchantments & a_Other) const;
-
- // tolua_end
-
- /// Returns true if a_Other doesn't contain exactly the same enchantments and levels
- bool operator !=(const cEnchantments & a_Other) const;
-
- /// Writes the enchantments into the specified NBT writer; begins with the LIST tag of the specified name ("ench" or "StoredEnchantments")
- void WriteToNBTCompound(cFastNBTWriter & a_Writer, const AString & a_ListTagName) const;
-
- /// Reads the enchantments from the specified NBT list tag (ench or StoredEnchantments)
- void ParseFromNBT(const cParsedNBT & a_NBT, int a_EnchListTagIdx);
-
-protected:
- /// Maps enchantment ID -> enchantment level
- typedef std::map<int, int> cMap;
-
- /// Currently stored enchantments
- cMap m_Enchantments;
-} ; // tolua_export
-
-
-
-
+
+// Enchantments.h
+
+// Declares the cEnchantments class representing a storage for item enchantments and stored-enchantments
+
+
+
+
+
+#pragma once
+
+
+
+
+
+// fwd: WorldStorage/FastNBT.h
+class cFastNBTWriter;
+class cParsedNBT;
+
+
+
+
+
+// tolua_begin
+
+/** Class that stores item enchantments or stored-enchantments
+The enchantments may be serialized to a stringspec and read back from such stringspec.
+The format for the stringspec is "id=lvl;id=lvl;id=lvl...", with an optional semicolon at the end,
+mapping each enchantment's id onto its level. ID may be either a number or the enchantment name.
+Level value of 0 means no such enchantment, and it will not be stored in the m_Enchantments.
+Serialization will never put zero-level enchantments into the stringspec and will always use numeric IDs.
+*/
+class cEnchantments
+{
+public:
+ /// Individual enchantment IDs, corresponding to their NBT IDs ( http://www.minecraftwiki.net/wiki/Data_Values#Enchantment_IDs )
+ enum
+ {
+ enchProtection = 0,
+ enchFireProtection = 1,
+ enchFeatherFalling = 2,
+ enchBlastProtection = 3,
+ enchProjectileProtection = 4,
+ enchRespiration = 5,
+ enchAquaAffinity = 6,
+ enchThorns = 7,
+ enchSharpness = 16,
+ enchSmite = 17,
+ enchBaneOfArthropods = 18,
+ enchKnockback = 19,
+ enchFireAspect = 20,
+ enchLooting = 21,
+ enchEfficiency = 32,
+ enchSilkTouch = 33,
+ enchUnbreaking = 34,
+ enchFortune = 35,
+ enchPower = 48,
+ enchPunch = 49,
+ enchFlame = 50,
+ enchInfinity = 51,
+ } ;
+
+ /// Creates an empty enchantments container
+ cEnchantments(void);
+
+ /// Creates an enchantments container filled with enchantments parsed from stringspec
+ cEnchantments(const AString & a_StringSpec);
+
+ /// Adds enchantments in the stringspec; if a specified enchantment already exists, overwrites it
+ void AddFromString(const AString & a_StringSpec);
+
+ /// Serializes all the enchantments into a string
+ AString ToString(void) const;
+
+ /// Returns the level for the specified enchantment; 0 if not stored
+ int GetLevel(int a_EnchantmentID) const;
+
+ /// Sets the level for the specified enchantment, adding it if not stored before or removing it if level <= 0
+ void SetLevel(int a_EnchantmentID, int a_Level);
+
+ /// Removes all enchantments
+ void Clear(void);
+
+ /// Returns true if there are no enchantments
+ bool IsEmpty(void) const;
+
+ /// Converts enchantment name to the numeric representation; returns -1 if enchantment name not found; case insensitive
+ static int StringToEnchantmentID(const AString & a_EnchantmentName);
+
+ /// Returns true if a_Other contains exactly the same enchantments and levels
+ bool operator ==(const cEnchantments & a_Other) const;
+
+ // tolua_end
+
+ /// Returns true if a_Other doesn't contain exactly the same enchantments and levels
+ bool operator !=(const cEnchantments & a_Other) const;
+
+ /// Writes the enchantments into the specified NBT writer; begins with the LIST tag of the specified name ("ench" or "StoredEnchantments")
+ void WriteToNBTCompound(cFastNBTWriter & a_Writer, const AString & a_ListTagName) const;
+
+ /// Reads the enchantments from the specified NBT list tag (ench or StoredEnchantments)
+ void ParseFromNBT(const cParsedNBT & a_NBT, int a_EnchListTagIdx);
+
+protected:
+ /// Maps enchantment ID -> enchantment level
+ typedef std::map<int, int> cMap;
+
+ /// Currently stored enchantments
+ cMap m_Enchantments;
+} ; // tolua_export
+
+
+
+
diff --git a/source/FallingBlock.cpp b/source/FallingBlock.cpp
index c3f980303..5dc99c771 100644
--- a/source/FallingBlock.cpp
+++ b/source/FallingBlock.cpp
@@ -1,103 +1,103 @@
-#include "Globals.h"
-
-#include "FallingBlock.h"
-#include "World.h"
-#include "ClientHandle.h"
-#include "Simulator/SandSimulator.h"
-#include "Chunk.h"
-
-
-
-
-
-cFallingBlock::cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) :
- super(etFallingBlock, a_BlockPosition.x + 0.5f, a_BlockPosition.y + 0.5f, a_BlockPosition.z + 0.5f, 0.98, 0.98),
- m_BlockType(a_BlockType),
- m_BlockMeta(a_BlockMeta),
- m_OriginalPosition(a_BlockPosition)
-{
-}
-
-
-
-
-
-void cFallingBlock::Initialize(cWorld * a_World)
-{
- super::Initialize(a_World);
- a_World->BroadcastSpawnEntity(*this);
-}
-
-
-
-
-
-void cFallingBlock::SpawnOn(cClientHandle & a_ClientHandle)
-{
- a_ClientHandle.SendSpawnFallingBlock(*this);
-}
-
-
-
-
-
-void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk)
-{
- float MilliDt = a_Dt * 0.001f;
- AddSpeedY(MilliDt * -9.8f);
- AddPosY(GetSpeedY() * MilliDt);
-
- // GetWorld()->BroadcastTeleportEntity(*this); // Test position
-
- int BlockX = m_OriginalPosition.x;
- int BlockY = (int)(GetPosY() - 0.5);
- int BlockZ = m_OriginalPosition.z;
-
- if (BlockY < 0)
- {
- // Fallen out of this world, just continue falling until out of sight, then destroy:
- if (BlockY < 100)
- {
- Destroy(true);
- }
- return;
- }
-
- if (BlockY >= cChunkDef::Height)
- {
- // Above the world, just wait for it to fall back down
- return;
- }
-
- int idx = a_Chunk.MakeIndexNoCheck(BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width);
- BLOCKTYPE BlockBelow = a_Chunk.GetBlock(idx);
- NIBBLETYPE BelowMeta = a_Chunk.GetMeta(idx);
- if (cSandSimulator::DoesBreakFallingThrough(BlockBelow, BelowMeta))
- {
- // Fallen onto a block that breaks this into pickups (e. g. half-slab)
- // Must finish the fall with coords one below the block:
- cSandSimulator::FinishFalling(m_World, BlockX, BlockY, BlockZ, m_BlockType, m_BlockMeta);
- Destroy(true);
- return;
- }
- else if (!cSandSimulator::CanContinueFallThrough(BlockBelow))
- {
- // Fallen onto a solid block
- /*
- LOGD(
- "Sand: Checked below at {%d, %d, %d} (rel {%d, %d, %d}), it's %s, finishing the fall.",
- BlockX, BlockY, BlockZ,
- BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width,
- ItemTypeToString(BlockBelow).c_str()
- );
- */
-
- cSandSimulator::FinishFalling(m_World, BlockX, BlockY + 1, BlockZ, m_BlockType, m_BlockMeta);
- Destroy(true);
- return;
- }
-}
-
-
-
-
+#include "Globals.h"
+
+#include "FallingBlock.h"
+#include "World.h"
+#include "ClientHandle.h"
+#include "Simulator/SandSimulator.h"
+#include "Chunk.h"
+
+
+
+
+
+cFallingBlock::cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) :
+ super(etFallingBlock, a_BlockPosition.x + 0.5f, a_BlockPosition.y + 0.5f, a_BlockPosition.z + 0.5f, 0.98, 0.98),
+ m_BlockType(a_BlockType),
+ m_BlockMeta(a_BlockMeta),
+ m_OriginalPosition(a_BlockPosition)
+{
+}
+
+
+
+
+
+void cFallingBlock::Initialize(cWorld * a_World)
+{
+ super::Initialize(a_World);
+ a_World->BroadcastSpawnEntity(*this);
+}
+
+
+
+
+
+void cFallingBlock::SpawnOn(cClientHandle & a_ClientHandle)
+{
+ a_ClientHandle.SendSpawnFallingBlock(*this);
+}
+
+
+
+
+
+void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ float MilliDt = a_Dt * 0.001f;
+ AddSpeedY(MilliDt * -9.8f);
+ AddPosY(GetSpeedY() * MilliDt);
+
+ // GetWorld()->BroadcastTeleportEntity(*this); // Test position
+
+ int BlockX = m_OriginalPosition.x;
+ int BlockY = (int)(GetPosY() - 0.5);
+ int BlockZ = m_OriginalPosition.z;
+
+ if (BlockY < 0)
+ {
+ // Fallen out of this world, just continue falling until out of sight, then destroy:
+ if (BlockY < 100)
+ {
+ Destroy(true);
+ }
+ return;
+ }
+
+ if (BlockY >= cChunkDef::Height)
+ {
+ // Above the world, just wait for it to fall back down
+ return;
+ }
+
+ int idx = a_Chunk.MakeIndexNoCheck(BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width);
+ BLOCKTYPE BlockBelow = a_Chunk.GetBlock(idx);
+ NIBBLETYPE BelowMeta = a_Chunk.GetMeta(idx);
+ if (cSandSimulator::DoesBreakFallingThrough(BlockBelow, BelowMeta))
+ {
+ // Fallen onto a block that breaks this into pickups (e. g. half-slab)
+ // Must finish the fall with coords one below the block:
+ cSandSimulator::FinishFalling(m_World, BlockX, BlockY, BlockZ, m_BlockType, m_BlockMeta);
+ Destroy(true);
+ return;
+ }
+ else if (!cSandSimulator::CanContinueFallThrough(BlockBelow))
+ {
+ // Fallen onto a solid block
+ /*
+ LOGD(
+ "Sand: Checked below at {%d, %d, %d} (rel {%d, %d, %d}), it's %s, finishing the fall.",
+ BlockX, BlockY, BlockZ,
+ BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width,
+ ItemTypeToString(BlockBelow).c_str()
+ );
+ */
+
+ cSandSimulator::FinishFalling(m_World, BlockX, BlockY + 1, BlockZ, m_BlockType, m_BlockMeta);
+ Destroy(true);
+ return;
+ }
+}
+
+
+
+
diff --git a/source/FallingBlock.h b/source/FallingBlock.h
index b7d448327..887ae6268 100644
--- a/source/FallingBlock.h
+++ b/source/FallingBlock.h
@@ -1,45 +1,45 @@
-
-#pragma once
-
-#include "Entity.h"
-#include "Defines.h"
-
-
-
-
-class cPlayer;
-class cItem;
-
-
-
-
-
-
-class cFallingBlock :
- public cEntity
-{
- typedef cEntity super;
-
-public:
- CLASS_PROTODEF(cFallingBlock);
-
- /// Creates a new falling block. a_BlockPosition is expected in world coords
- cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
-
- BLOCKTYPE GetBlockType(void) const { return m_BlockType; }
- NIBBLETYPE GetBlockMeta(void) const { return m_BlockMeta; }
-
- // cEntity overrides:
- virtual void Initialize(cWorld * a_World) override;
- virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
-
-private:
- BLOCKTYPE m_BlockType;
- NIBBLETYPE m_BlockMeta;
- Vector3i m_OriginalPosition; // Position where the falling block has started, in world coords
-} ;
-
-
-
-
+
+#pragma once
+
+#include "Entity.h"
+#include "Defines.h"
+
+
+
+
+class cPlayer;
+class cItem;
+
+
+
+
+
+
+class cFallingBlock :
+ public cEntity
+{
+ typedef cEntity super;
+
+public:
+ CLASS_PROTODEF(cFallingBlock);
+
+ /// Creates a new falling block. a_BlockPosition is expected in world coords
+ cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+
+ BLOCKTYPE GetBlockType(void) const { return m_BlockType; }
+ NIBBLETYPE GetBlockMeta(void) const { return m_BlockMeta; }
+
+ // cEntity overrides:
+ virtual void Initialize(cWorld * a_World) override;
+ virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+
+private:
+ BLOCKTYPE m_BlockType;
+ NIBBLETYPE m_BlockMeta;
+ Vector3i m_OriginalPosition; // Position where the falling block has started, in world coords
+} ;
+
+
+
+
diff --git a/source/FastRandom.cpp b/source/FastRandom.cpp
index b778fb4bb..887e4426d 100644
--- a/source/FastRandom.cpp
+++ b/source/FastRandom.cpp
@@ -1,174 +1,174 @@
-
-// FastRandom.cpp
-
-// Implements the cFastRandom class representing a fast random number generator
-
-#include "Globals.h"
-#include <time.h>
-#include "FastRandom.h"
-
-
-
-
-
-#if 0 && defined(_DEBUG)
-// Self-test
-// Both ints and floats are quick-tested to see if the random is calculated correctly, checking the range in ASSERTs,
-// and if it performs well in terms of distribution (checked by avg, expected to be in the range midpoint
-class cFastRandomTest
-{
-public:
- cFastRandomTest(void)
- {
- TestInts();
- TestFloats();
- }
-
-
- void TestInts(void)
- {
- printf("Testing ints...\n");
- cFastRandom rnd;
- int sum = 0;
- const int BUCKETS = 8;
- int Counts[BUCKETS];
- memset(Counts, 0, sizeof(Counts));
- const int ITER = 10000;
- for (int i = 0; i < ITER; i++)
- {
- int v = rnd.NextInt(1000);
- ASSERT(v >= 0);
- ASSERT(v < 1000);
- Counts[v % BUCKETS]++;
- sum += v;
- }
- double avg = (double)sum / ITER;
- printf("avg: %f\n", avg);
- for (int i = 0; i < BUCKETS; i++)
- {
- printf(" bucket %d: %d\n", i, Counts[i]);
- }
- }
-
-
- void TestFloats(void)
- {
- printf("Testing floats...\n");
- cFastRandom rnd;
- float sum = 0;
- const int BUCKETS = 8;
- int Counts[BUCKETS];
- memset(Counts, 0, sizeof(Counts));
- const int ITER = 10000;
- for (int i = 0; i < ITER; i++)
- {
- float v = rnd.NextFloat(1000);
- ASSERT(v >= 0);
- ASSERT(v <= 1000);
- Counts[((int)v) % BUCKETS]++;
- sum += v;
- }
- sum = sum / ITER;
- printf("avg: %f\n", sum);
- for (int i = 0; i < BUCKETS; i++)
- {
- printf(" bucket %d: %d\n", i, Counts[i]);
- }
- }
-} g_Test;
-
-#endif
-
-
-
-
-
-
-int cFastRandom::m_SeedCounter = 0;
-
-
-
-
-
-cFastRandom::cFastRandom(void) :
- m_Seed(m_SeedCounter++)
-{
-}
-
-
-
-
-
-int cFastRandom::NextInt(int a_Range)
-{
- ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges
- ASSERT(a_Range > 0);
-
- // Make the m_Counter operations as minimal as possible, to emulate atomicity
- int Counter = m_Counter++;
-
- // Use a_Range, m_Counter and m_Seed as inputs to the pseudorandom function:
- int n = a_Range + m_Counter * 57 + m_Seed * 57 * 57;
- n = (n << 13) ^ n;
- n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
- return ((n / 11) % a_Range);
-}
-
-
-
-
-
-int cFastRandom::NextInt(int a_Range, int a_Salt)
-{
- ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges
- ASSERT(a_Range > 0);
-
- // Make the m_Counter operations as minimal as possible, to emulate atomicity
- int Counter = m_Counter++;
-
- // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function:
- int n = a_Range + m_Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57;
- n = (n << 13) ^ n;
- n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
- return ((n / 11) % a_Range);
-}
-
-
-
-
-
-float cFastRandom::NextFloat(float a_Range)
-{
- // Make the m_Counter operations as minimal as possible, to emulate atomicity
- int Counter = m_Counter++;
-
- // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function:
- int n = (int)a_Range + m_Counter * 57 + m_Seed * 57 * 57;
- n = (n << 13) ^ n;
- n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
-
- // Convert the integer into float with the specified range:
- return (((float)n / (float)0x7fffffff) * a_Range);
-}
-
-
-
-
-
-float cFastRandom::NextFloat(float a_Range, int a_Salt)
-{
- // Make the m_Counter operations as minimal as possible, to emulate atomicity
- int Counter = m_Counter++;
-
- // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function:
- int n = (int)a_Range + m_Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57;
- n = (n << 13) ^ n;
- n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
-
- // Convert the integer into float with the specified range:
- return (((float)n / (float)0x7fffffff) * a_Range);
-}
-
-
-
-
+
+// FastRandom.cpp
+
+// Implements the cFastRandom class representing a fast random number generator
+
+#include "Globals.h"
+#include <time.h>
+#include "FastRandom.h"
+
+
+
+
+
+#if 0 && defined(_DEBUG)
+// Self-test
+// Both ints and floats are quick-tested to see if the random is calculated correctly, checking the range in ASSERTs,
+// and if it performs well in terms of distribution (checked by avg, expected to be in the range midpoint
+class cFastRandomTest
+{
+public:
+ cFastRandomTest(void)
+ {
+ TestInts();
+ TestFloats();
+ }
+
+
+ void TestInts(void)
+ {
+ printf("Testing ints...\n");
+ cFastRandom rnd;
+ int sum = 0;
+ const int BUCKETS = 8;
+ int Counts[BUCKETS];
+ memset(Counts, 0, sizeof(Counts));
+ const int ITER = 10000;
+ for (int i = 0; i < ITER; i++)
+ {
+ int v = rnd.NextInt(1000);
+ ASSERT(v >= 0);
+ ASSERT(v < 1000);
+ Counts[v % BUCKETS]++;
+ sum += v;
+ }
+ double avg = (double)sum / ITER;
+ printf("avg: %f\n", avg);
+ for (int i = 0; i < BUCKETS; i++)
+ {
+ printf(" bucket %d: %d\n", i, Counts[i]);
+ }
+ }
+
+
+ void TestFloats(void)
+ {
+ printf("Testing floats...\n");
+ cFastRandom rnd;
+ float sum = 0;
+ const int BUCKETS = 8;
+ int Counts[BUCKETS];
+ memset(Counts, 0, sizeof(Counts));
+ const int ITER = 10000;
+ for (int i = 0; i < ITER; i++)
+ {
+ float v = rnd.NextFloat(1000);
+ ASSERT(v >= 0);
+ ASSERT(v <= 1000);
+ Counts[((int)v) % BUCKETS]++;
+ sum += v;
+ }
+ sum = sum / ITER;
+ printf("avg: %f\n", sum);
+ for (int i = 0; i < BUCKETS; i++)
+ {
+ printf(" bucket %d: %d\n", i, Counts[i]);
+ }
+ }
+} g_Test;
+
+#endif
+
+
+
+
+
+
+int cFastRandom::m_SeedCounter = 0;
+
+
+
+
+
+cFastRandom::cFastRandom(void) :
+ m_Seed(m_SeedCounter++)
+{
+}
+
+
+
+
+
+int cFastRandom::NextInt(int a_Range)
+{
+ ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges
+ ASSERT(a_Range > 0);
+
+ // Make the m_Counter operations as minimal as possible, to emulate atomicity
+ int Counter = m_Counter++;
+
+ // Use a_Range, m_Counter and m_Seed as inputs to the pseudorandom function:
+ int n = a_Range + m_Counter * 57 + m_Seed * 57 * 57;
+ n = (n << 13) ^ n;
+ n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
+ return ((n / 11) % a_Range);
+}
+
+
+
+
+
+int cFastRandom::NextInt(int a_Range, int a_Salt)
+{
+ ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges
+ ASSERT(a_Range > 0);
+
+ // Make the m_Counter operations as minimal as possible, to emulate atomicity
+ int Counter = m_Counter++;
+
+ // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function:
+ int n = a_Range + m_Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57;
+ n = (n << 13) ^ n;
+ n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
+ return ((n / 11) % a_Range);
+}
+
+
+
+
+
+float cFastRandom::NextFloat(float a_Range)
+{
+ // Make the m_Counter operations as minimal as possible, to emulate atomicity
+ int Counter = m_Counter++;
+
+ // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function:
+ int n = (int)a_Range + m_Counter * 57 + m_Seed * 57 * 57;
+ n = (n << 13) ^ n;
+ n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
+
+ // Convert the integer into float with the specified range:
+ return (((float)n / (float)0x7fffffff) * a_Range);
+}
+
+
+
+
+
+float cFastRandom::NextFloat(float a_Range, int a_Salt)
+{
+ // Make the m_Counter operations as minimal as possible, to emulate atomicity
+ int Counter = m_Counter++;
+
+ // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function:
+ int n = (int)a_Range + m_Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57;
+ n = (n << 13) ^ n;
+ n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
+
+ // Convert the integer into float with the specified range:
+ return (((float)n / (float)0x7fffffff) * a_Range);
+}
+
+
+
+
diff --git a/source/FastRandom.h b/source/FastRandom.h
index 011c0c8da..bf70822cf 100644
--- a/source/FastRandom.h
+++ b/source/FastRandom.h
@@ -1,57 +1,57 @@
-
-// FastRandom.h
-
-// Declares the cFastRandom class representing a fast random number generator
-
-/*
-The cFastRandom aims to provide a very fast, although not very cryptographically secure, random generator.
-It is fast to instantiate, fast to query next, and partially multi-thread-safe.
-It is multi-thread-safe in the sense that it can be accessed from multiple threads without crashing, but it may
-yield duplicate numbers in that case.
-
-Internally, this works similar to cNoise's integral noise generation, with some predefined inputs: the seed is
-taken from a global counter and the random is calculated using a counter that is incremented on each use (hence
-the multi-thread duplication). Two alternatives exists for each function, one that takes a range parameter,
-and another that takes an additional "salt" parameter; this salt is used as an additional input to the random,
-in order to avoid multi-thread duplication. If two threads both use the class at the same time with different
-salts, the values they get will be different.
-*/
-
-
-
-
-
-#pragma once
-
-
-
-
-
-class cFastRandom
-{
-public:
- cFastRandom(void);
-
- /// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M
- int NextInt(int a_Range);
-
- /// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M; a_Salt is additional source of randomness
- int NextInt(int a_Range, int a_Salt);
-
- /// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M
- float NextFloat(float a_Range);
-
- /// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M; a_Salt is additional source of randomness
- float NextFloat(float a_Range, int a_Salt);
-
-protected:
- int m_Seed;
- int m_Counter;
-
- /// Counter that is used to initialize the seed, incremented for each object created
- static int m_SeedCounter;
-} ;
-
-
-
-
+
+// FastRandom.h
+
+// Declares the cFastRandom class representing a fast random number generator
+
+/*
+The cFastRandom aims to provide a very fast, although not very cryptographically secure, random generator.
+It is fast to instantiate, fast to query next, and partially multi-thread-safe.
+It is multi-thread-safe in the sense that it can be accessed from multiple threads without crashing, but it may
+yield duplicate numbers in that case.
+
+Internally, this works similar to cNoise's integral noise generation, with some predefined inputs: the seed is
+taken from a global counter and the random is calculated using a counter that is incremented on each use (hence
+the multi-thread duplication). Two alternatives exists for each function, one that takes a range parameter,
+and another that takes an additional "salt" parameter; this salt is used as an additional input to the random,
+in order to avoid multi-thread duplication. If two threads both use the class at the same time with different
+salts, the values they get will be different.
+*/
+
+
+
+
+
+#pragma once
+
+
+
+
+
+class cFastRandom
+{
+public:
+ cFastRandom(void);
+
+ /// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M
+ int NextInt(int a_Range);
+
+ /// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M; a_Salt is additional source of randomness
+ int NextInt(int a_Range, int a_Salt);
+
+ /// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M
+ float NextFloat(float a_Range);
+
+ /// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M; a_Salt is additional source of randomness
+ float NextFloat(float a_Range, int a_Salt);
+
+protected:
+ int m_Seed;
+ int m_Counter;
+
+ /// Counter that is used to initialize the seed, incremented for each object created
+ static int m_SeedCounter;
+} ;
+
+
+
+
diff --git a/source/Generating/Caves.cpp b/source/Generating/Caves.cpp
index 2bfef8054..4221ea187 100644
--- a/source/Generating/Caves.cpp
+++ b/source/Generating/Caves.cpp
@@ -1,970 +1,970 @@
-
-// Caves.cpp
-
-// Implements the various cave structure generators:
-// - cStructGenWormNestCaves
-// - cStructGenDualRidgeCaves
-// - cStructGenMarbleCaves
-// - cStructGenNetherCaves
-
-/*
-WormNestCave generator:
-Caves are generated in "nests" - groups of tunnels generated from a single point.
-For each chunk, all the nests that could intersect it are generated.
-For each nest, first the schematic structure is generated (tunnel from ... to ..., branch, tunnel2 from ... to ...)
-Then each tunnel is randomized by inserting points in between its ends.
-Finally each tunnel is smoothed and Bresenham-3D-ed so that it is a collection of spheres with their centers next to each other.
-When the tunnels are ready, they are simply carved into the chunk, one by one.
-To optimize, each tunnel keeps track of its bounding box, so that it can be skipped for chunks that don't intersect it.
-
-MarbleCaves generator:
-For each voxel a 3D noise function is evaluated, if the value crosses a boundary, the voxel is dug out, otherwise it is kept.
-Problem with this is the amount of CPU work needed for each chunk.
-Also the overall shape of the generated holes is unsatisfactory - there are whole "sheets" of holes in the ground.
-
-DualRidgeCaves generator:
-Instead of evaluating a single noise function, two different noise functions are multiplied. This produces
-regular tunnels instead of sheets. However due to the sheer amount of CPU work needed, the noise functions need to be
-reduced in complexity in order for this generator to be useful, so the caves' shapes are "bubbly" at best.
-*/
-
-#include "Globals.h"
-#include "Caves.h"
-
-
-
-
-
-/// How many nests in each direction are generated for a given chunk. Must be an even number
-#define NEIGHBORHOOD_SIZE 8
-
-
-
-
-
-const int MIN_RADIUS = 3;
-const int MAX_RADIUS = 8;
-
-
-
-
-
-struct cCaveDefPoint
-{
- int m_BlockX;
- int m_BlockY;
- int m_BlockZ;
- int m_Radius;
-
- cCaveDefPoint(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Radius) :
- m_BlockX(a_BlockX),
- m_BlockY(a_BlockY),
- m_BlockZ(a_BlockZ),
- m_Radius(a_Radius)
- {
- }
-} ;
-
-typedef std::vector<cCaveDefPoint> cCaveDefPoints;
-
-
-
-
-
-/// A single non-branching tunnel of a WormNestCave
-class cCaveTunnel
-{
- // The bounding box, including the radii around defpoints:
- int m_MinBlockX, m_MaxBlockX;
- int m_MinBlockY, m_MaxBlockY;
- int m_MinBlockZ, m_MaxBlockZ;
-
- /// Generates the shaping defpoints for the ravine, based on the ravine block coords and noise
- void Randomize(cNoise & a_Noise);
-
- /// Refines (adds and smooths) defpoints from a_Src into a_Dst; returns false if no refinement possible (segments too short)
- bool RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints & a_Dst);
-
- /// Does rounds of smoothing, two passes of RefineDefPoints(), as long as they return true
- void Smooth(void);
-
- /// Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block
- void FinishLinear(void);
-
- /// Calculates the bounding box of the points present
- void CalcBoundingBox(void);
-
-public:
- cCaveDefPoints m_Points;
-
- cCaveTunnel(
- int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ, int a_StartRadius,
- int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ, int a_EndRadius,
- cNoise & a_Noise
- );
-
- /// Carves the tunnel into the chunk specified
- void ProcessChunk(
- int a_ChunkX, int a_ChunkZ,
- cChunkDef::BlockTypes & a_BlockTypes,
- cChunkDef::HeightMap & a_HeightMap
- );
-
- #ifdef _DEBUG
- AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const;
- #endif // _DEBUG
-} ;
-
-typedef std::vector<cCaveTunnel *> cCaveTunnels;
-
-
-
-
-
-/// A collection of connected tunnels, possibly branching.
-class cStructGenWormNestCaves::cCaveSystem
-{
-public:
- // The generating block position; is read directly in cStructGenWormNestCaves::GetCavesForChunk()
- int m_BlockX;
- int m_BlockZ;
-
- cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise);
- ~cCaveSystem();
-
- /// Carves the cave system into the chunk specified
- void ProcessChunk(
- int a_ChunkX, int a_ChunkZ,
- cChunkDef::BlockTypes & a_BlockTypes,
- cChunkDef::HeightMap & a_HeightMap
- );
-
- #ifdef _DEBUG
- AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const;
- #endif // _DEBUG
-
-protected:
- int m_Size;
- cCaveTunnels m_Tunnels;
-
- void Clear(void);
-
- /// Generates a_Segment successive tunnels, with possible branches. Generates the same output for the same [x, y, z, a_Segments]
- void GenerateTunnelsFromPoint(
- int a_OriginX, int a_OriginY, int a_OriginZ,
- cNoise & a_Noise, int a_Segments
- );
-
- /// Returns a radius based on the location provided.
- int GetRadius(cNoise & a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ);
-} ;
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cCaveTunnel:
-
-cCaveTunnel::cCaveTunnel(
- int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ, int a_StartRadius,
- int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ, int a_EndRadius,
- cNoise & a_Noise
-)
-{
- m_Points.push_back(cCaveDefPoint(a_BlockStartX, a_BlockStartY, a_BlockStartZ, a_StartRadius));
- m_Points.push_back(cCaveDefPoint(a_BlockEndX, a_BlockEndY, a_BlockEndZ, a_EndRadius));
-
- if ((a_BlockStartY <= 0) && (a_BlockEndY <= 0))
- {
- // Don't bother detailing this cave, it's under the world anyway
- return;
- }
-
- Randomize(a_Noise);
- Smooth();
-
- // We know that the linear finishing won't affect the bounding box, so let's calculate it now, as we have less data:
- CalcBoundingBox();
-
- FinishLinear();
-}
-
-
-
-
-
-void cCaveTunnel::Randomize(cNoise & a_Noise)
-{
- // Repeat 4 times:
- for (int i = 0; i < 4; i++)
- {
- // For each already present point, insert a point in between it and its predecessor, shifted randomly.
- int PrevX = m_Points.front().m_BlockX;
- int PrevY = m_Points.front().m_BlockY;
- int PrevZ = m_Points.front().m_BlockZ;
- int PrevR = m_Points.front().m_Radius;
- cCaveDefPoints Pts;
- Pts.reserve(m_Points.size() * 2 + 1);
- Pts.push_back(m_Points.front());
- for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr)
- {
- int Random = a_Noise.IntNoise3DInt(PrevX, PrevY, PrevZ + i) / 11;
- int len = (PrevX - itr->m_BlockX) * (PrevX - itr->m_BlockX);
- len += (PrevY - itr->m_BlockY) * (PrevY - itr->m_BlockY);
- len += (PrevZ - itr->m_BlockZ) * (PrevZ - itr->m_BlockZ);
- len = 3 * (int)sqrt((double)len) / 4;
- int Rad = std::min(MAX_RADIUS, std::max(MIN_RADIUS, (PrevR + itr->m_Radius) / 2 + (Random % 3) - 1));
- Random /= 4;
- int x = (itr->m_BlockX + PrevX) / 2 + (Random % (len + 1) - len / 2);
- Random /= 256;
- int y = (itr->m_BlockY + PrevY) / 2 + (Random % (len / 2 + 1) - len / 4);
- Random /= 256;
- int z = (itr->m_BlockZ + PrevZ) / 2 + (Random % (len + 1) - len / 2);
- Pts.push_back(cCaveDefPoint(x, y, z, Rad));
- Pts.push_back(*itr);
- PrevX = itr->m_BlockX;
- PrevY = itr->m_BlockY;
- PrevZ = itr->m_BlockZ;
- PrevR = itr->m_Radius;
- }
- std::swap(Pts, m_Points);
- }
-}
-
-
-
-
-
-bool cCaveTunnel::RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints & a_Dst)
-{
- // Smoothing: for each line segment, add points on its 1/4 lengths
- bool res = false;
- int Num = a_Src.size() - 2; // this many intermediary points
- a_Dst.clear();
- a_Dst.reserve(Num * 2 + 2);
- cCaveDefPoints::const_iterator itr = a_Src.begin() + 1;
- a_Dst.push_back(a_Src.front());
- int PrevX = a_Src.front().m_BlockX;
- int PrevY = a_Src.front().m_BlockY;
- int PrevZ = a_Src.front().m_BlockZ;
- int PrevR = a_Src.front().m_Radius;
- for (int i = 0; i <= Num; ++i, ++itr)
- {
- int dx = itr->m_BlockX - PrevX;
- int dy = itr->m_BlockY - PrevY;
- int dz = itr->m_BlockZ - PrevZ;
- if (abs(dx) + abs(dz) + abs(dy) < 6)
- {
- // Too short a segment to smooth-subdivide into quarters
- PrevX = itr->m_BlockX;
- PrevY = itr->m_BlockY;
- PrevZ = itr->m_BlockZ;
- PrevR = itr->m_Radius;
- continue;
- }
- int dr = itr->m_Radius - PrevR;
- int Rad1 = std::max(PrevR + 1 * dr / 4, 1);
- int Rad2 = std::max(PrevR + 3 * dr / 4, 1);
- a_Dst.push_back(cCaveDefPoint(PrevX + 1 * dx / 4, PrevY + 1 * dy / 4, PrevZ + 1 * dz / 4, Rad1));
- a_Dst.push_back(cCaveDefPoint(PrevX + 3 * dx / 4, PrevY + 3 * dy / 4, PrevZ + 3 * dz / 4, Rad2));
- PrevX = itr->m_BlockX;
- PrevY = itr->m_BlockY;
- PrevZ = itr->m_BlockZ;
- PrevR = itr->m_Radius;
- res = true;
- }
- a_Dst.push_back(a_Src.back());
- return res && (a_Src.size() < a_Dst.size());
-}
-
-
-
-
-
-void cCaveTunnel::Smooth(void)
-{
- cCaveDefPoints Pts;
- while (true)
- {
- if (!RefineDefPoints(m_Points, Pts))
- {
- std::swap(Pts, m_Points);
- return;
- }
- if (!RefineDefPoints(Pts, m_Points))
- {
- return;
- }
- }
-}
-
-
-
-
-
-void cCaveTunnel::FinishLinear(void)
-{
- // For each segment, use Bresenham's 3D line algorithm to draw a "line" of defpoints
- cCaveDefPoints Pts;
- std::swap(Pts, m_Points);
-
- m_Points.reserve(Pts.size() * 3);
- int PrevX = Pts.front().m_BlockX;
- int PrevY = Pts.front().m_BlockY;
- int PrevZ = Pts.front().m_BlockZ;
- for (cCaveDefPoints::const_iterator itr = Pts.begin() + 1, end = Pts.end(); itr != end; ++itr)
- {
- int x1 = itr->m_BlockX;
- int y1 = itr->m_BlockY;
- int z1 = itr->m_BlockZ;
- int dx = abs(x1 - PrevX);
- int dy = abs(y1 - PrevY);
- int dz = abs(z1 - PrevZ);
- int sx = (PrevX < x1) ? 1 : -1;
- int sy = (PrevY < y1) ? 1 : -1;
- int sz = (PrevZ < z1) ? 1 : -1;
- int err = dx - dz;
- int R = itr->m_Radius;
-
- if (dx >= std::max(dy, dz)) // x dominant
- {
- int yd = dy - dx / 2;
- int zd = dz - dx / 2;
-
- while (true)
- {
- m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R));
-
- if (PrevX == x1)
- {
- break;
- }
-
- if (yd >= 0) // move along y
- {
- PrevY += sy;
- yd -= dx;
- }
-
- if (zd >= 0) // move along z
- {
- PrevZ += sz;
- zd -= dx;
- }
-
- // move along x
- PrevX += sx;
- yd += dy;
- zd += dz;
- }
- }
- else if (dy >= std::max(dx, dz)) // y dominant
- {
- int xd = dx - dy / 2;
- int zd = dz - dy / 2;
-
- while (true)
- {
- m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R));
-
- if (PrevY == y1)
- {
- break;
- }
-
- if (xd >= 0) // move along x
- {
- PrevX += sx;
- xd -= dy;
- }
-
- if (zd >= 0) // move along z
- {
- PrevZ += sz;
- zd -= dy;
- }
-
- // move along y
- PrevY += sy;
- xd += dx;
- zd += dz;
- }
- }
- else
- {
- // z dominant
- ASSERT(dz >= std::max(dx, dy));
- int xd = dx - dz / 2;
- int yd = dy - dz / 2;
-
- while (true)
- {
- m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R));
-
- if (PrevZ == z1)
- {
- break;
- }
-
- if (xd >= 0) // move along x
- {
- PrevX += sx;
- xd -= dz;
- }
-
- if (yd >= 0) // move along y
- {
- PrevY += sy;
- yd -= dz;
- }
-
- // move along z
- PrevZ += sz;
- xd += dx;
- yd += dy;
- }
- } // if (which dimension is dominant)
- } // for itr
-}
-
-
-
-
-
-void cCaveTunnel::CalcBoundingBox(void)
-{
- m_MinBlockX = m_MaxBlockX = m_Points.front().m_BlockX;
- m_MinBlockY = m_MaxBlockY = m_Points.front().m_BlockY;
- m_MinBlockZ = m_MaxBlockZ = m_Points.front().m_BlockZ;
- for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr)
- {
- m_MinBlockX = std::min(m_MinBlockX, itr->m_BlockX - itr->m_Radius);
- m_MaxBlockX = std::max(m_MaxBlockX, itr->m_BlockX + itr->m_Radius);
- m_MinBlockY = std::min(m_MinBlockY, itr->m_BlockY - itr->m_Radius);
- m_MaxBlockY = std::max(m_MaxBlockY, itr->m_BlockY + itr->m_Radius);
- m_MinBlockZ = std::min(m_MinBlockZ, itr->m_BlockZ - itr->m_Radius);
- m_MaxBlockZ = std::max(m_MaxBlockZ, itr->m_BlockZ + itr->m_Radius);
- } // for itr - m_Points[]
-}
-
-
-
-
-
-void cCaveTunnel::ProcessChunk(
- int a_ChunkX, int a_ChunkZ,
- cChunkDef::BlockTypes & a_BlockTypes,
- cChunkDef::HeightMap & a_HeightMap
-)
-{
- int BaseX = a_ChunkX * cChunkDef::Width;
- int BaseZ = a_ChunkZ * cChunkDef::Width;
- if (
- (BaseX > m_MaxBlockX) || (BaseX + cChunkDef::Width < m_MinBlockX) ||
- (BaseX > m_MaxBlockX) || (BaseX + cChunkDef::Width < m_MinBlockX)
- )
- {
- // Tunnel does not intersect the chunk at all, bail out
- return;
- }
-
- int BlockStartX = a_ChunkX * cChunkDef::Width;
- int BlockStartZ = a_ChunkZ * cChunkDef::Width;
- int BlockEndX = BlockStartX + cChunkDef::Width;
- int BlockEndZ = BlockStartZ + cChunkDef::Width;
- for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr)
- {
- if (
- (itr->m_BlockX + itr->m_Radius < BlockStartX) ||
- (itr->m_BlockX - itr->m_Radius > BlockEndX) ||
- (itr->m_BlockZ + itr->m_Radius < BlockStartZ) ||
- (itr->m_BlockZ - itr->m_Radius > BlockEndZ)
- )
- {
- // Cannot intersect, bail out early
- continue;
- }
-
- // Carve out a sphere around the xyz point, m_Radius in diameter; skip 3/7 off the top and bottom:
- int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc
- int DifY = itr->m_BlockY;
- int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc
- int Bottom = std::max(itr->m_BlockY - 3 * itr->m_Radius / 7, 1);
- int Top = std::min(itr->m_BlockY + 3 * itr->m_Radius / 7, (int)(cChunkDef::Height));
- int SqRad = itr->m_Radius * itr->m_Radius;
- for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++)
- {
- for (int y = Bottom; y <= Top; y++)
- {
- int SqDist = (DifX - x) * (DifX - x) + (DifY - y) * (DifY - y) + (DifZ - z) * (DifZ - z);
- if (4 * SqDist <= SqRad)
- {
- switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z))
- {
- // Only carve out these specific block types
- case E_BLOCK_DIRT:
- case E_BLOCK_GRASS:
- case E_BLOCK_STONE:
- case E_BLOCK_COBBLESTONE:
- case E_BLOCK_GRAVEL:
- case E_BLOCK_SAND:
- case E_BLOCK_SANDSTONE:
- case E_BLOCK_NETHERRACK:
- case E_BLOCK_COAL_ORE:
- case E_BLOCK_IRON_ORE:
- case E_BLOCK_GOLD_ORE:
- case E_BLOCK_DIAMOND_ORE:
- case E_BLOCK_REDSTONE_ORE:
- case E_BLOCK_REDSTONE_ORE_GLOWING:
- {
- cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR);
- break;
- }
- default: break;
- }
- }
- } // for y
- } // for x, z
- } // for itr - m_Points[]
-
- /*
- #ifdef _DEBUG
- // For debugging purposes, outline the shape of the cave using glowstone, *after* carving the entire cave:
- for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr)
- {
- int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc
- int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc
- if (
- (DifX >= 0) && (DifX < cChunkDef::Width) &&
- (itr->m_BlockY > 0) && (itr->m_BlockY < cChunkDef::Height) &&
- (DifZ >= 0) && (DifZ < cChunkDef::Width)
- )
- {
- cChunkDef::SetBlock(a_BlockTypes, DifX, itr->m_BlockY, DifZ, E_BLOCK_GLOWSTONE);
- }
- } // for itr - m_Points[]
- #endif // _DEBUG
- //*/
-}
-
-
-
-
-
-#ifdef _DEBUG
-AString cCaveTunnel::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const
-{
- AString SVG;
- SVG.reserve(m_Points.size() * 20 + 200);
- AppendPrintf(SVG, "<path style=\"fill:none;stroke:#%06x;stroke-width:1px;\"\nd=\"", a_Color);
- char Prefix = 'M'; // The first point needs "M" prefix, all the others need "L"
- for (cCaveDefPoints::const_iterator itr = m_Points.begin(); itr != m_Points.end(); ++itr)
- {
- AppendPrintf(SVG, "%c %d,%d ", Prefix, a_OffsetX + itr->m_BlockX, a_OffsetZ + itr->m_BlockZ);
- Prefix = 'L';
- }
- SVG.append("\"/>\n");
- return SVG;
-}
-#endif // _DEBUG
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cStructGenWormNestCaves::cCaveSystem:
-
-cStructGenWormNestCaves::cCaveSystem::cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise) :
- m_BlockX(a_BlockX),
- m_BlockZ(a_BlockZ),
- m_Size(a_Size)
-{
- int Num = 1 + a_Noise.IntNoise2DInt(a_BlockX, a_BlockZ) % 3;
- for (int i = 0; i < Num; i++)
- {
- int OriginX = a_BlockX + (a_Noise.IntNoise3DInt(13 * a_BlockX, 17 * a_BlockZ, 11 * i) / 19) % a_MaxOffset;
- int OriginZ = a_BlockZ + (a_Noise.IntNoise3DInt(17 * a_BlockX, 13 * a_BlockZ, 11 * i) / 23) % a_MaxOffset;
- int OriginY = 20 + (a_Noise.IntNoise3DInt(19 * a_BlockX, 13 * a_BlockZ, 11 * i) / 17) % 20;
-
- // Generate three branches from the origin point:
- // The tunnels generated depend on X, Y, Z and Branches,
- // for the same set of numbers it generates the same offsets!
- // That's why we add a +1 to X in the third line
- GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 3);
- GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 2);
- GenerateTunnelsFromPoint(OriginX + 1, OriginY, OriginZ, a_Noise, 3);
- }
-}
-
-
-
-
-
-cStructGenWormNestCaves::cCaveSystem::~cCaveSystem()
-{
- Clear();
-}
-
-
-
-
-
-
-void cStructGenWormNestCaves::cCaveSystem::ProcessChunk(
- int a_ChunkX, int a_ChunkZ,
- cChunkDef::BlockTypes & a_BlockTypes,
- cChunkDef::HeightMap & a_HeightMap
-)
-{
- for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr)
- {
- (*itr)->ProcessChunk(a_ChunkX, a_ChunkZ, a_BlockTypes, a_HeightMap);
- } // for itr - m_Tunnels[]
-}
-
-
-
-
-
-#ifdef _DEBUG
-AString cStructGenWormNestCaves::cCaveSystem::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const
-{
- AString SVG;
- SVG.reserve(512 * 1024);
- for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr)
- {
- SVG.append((*itr)->ExportAsSVG(a_Color, a_OffsetX, a_OffsetZ));
- } // for itr - m_Tunnels[]
-
- // Base point highlight:
- AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
- a_OffsetX + m_BlockX - 5, a_OffsetZ + m_BlockZ, a_OffsetX + m_BlockX + 5, a_OffsetZ + m_BlockZ
- );
- AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
- a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ - 5, a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ + 5
- );
-
- // A gray line from the base point to the first point of the ravine, for identification:
- AppendPrintf(SVG, "<path style=\"fill:none;stroke:#cfcfcf;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
- a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ,
- a_OffsetX + m_Tunnels.front()->m_Points.front().m_BlockX,
- a_OffsetZ + m_Tunnels.front()->m_Points.front().m_BlockZ
- );
-
- // Offset guides:
- if (a_OffsetX > 0)
- {
- AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M %d,0 L %d,1024\"/>\n",
- a_OffsetX, a_OffsetX
- );
- }
- if (a_OffsetZ > 0)
- {
- AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M 0,%d L 1024,%d\"/>\n",
- a_OffsetZ, a_OffsetZ
- );
- }
-
- return SVG;
-}
-#endif // _DEBUG
-
-
-
-
-
-void cStructGenWormNestCaves::cCaveSystem::Clear(void)
-{
- for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr)
- {
- delete *itr;
- }
- m_Tunnels.clear();
-}
-
-
-
-
-
-void cStructGenWormNestCaves::cCaveSystem::GenerateTunnelsFromPoint(
- int a_OriginX, int a_OriginY, int a_OriginZ,
- cNoise & a_Noise, int a_NumSegments
-)
-{
- int DoubleSize = m_Size * 2;
- int Radius = GetRadius(a_Noise, a_OriginX + a_OriginY, a_OriginY + a_OriginZ, a_OriginZ + a_OriginX);
- for (int i = a_NumSegments - 1; i >= 0; --i)
- {
- int EndX = a_OriginX + (((a_Noise.IntNoise3DInt(a_OriginX, a_OriginY, a_OriginZ + 11 * a_NumSegments) / 7) % DoubleSize) - m_Size) / 2;
- int EndY = a_OriginY + (((a_Noise.IntNoise3DInt(a_OriginY, 13 * a_NumSegments, a_OriginZ + a_OriginX) / 7) % DoubleSize) - m_Size) / 4;
- int EndZ = a_OriginZ + (((a_Noise.IntNoise3DInt(a_OriginZ + 17 * a_NumSegments, a_OriginX, a_OriginY) / 7) % DoubleSize) - m_Size) / 2;
- int EndR = GetRadius(a_Noise, a_OriginX + 7 * i, a_OriginY + 11 * i, a_OriginZ + a_OriginX);
- m_Tunnels.push_back(new cCaveTunnel(a_OriginX, a_OriginY, a_OriginZ, Radius, EndX, EndY, EndZ, EndR, a_Noise));
- GenerateTunnelsFromPoint(EndX, EndY, EndZ, a_Noise, i);
- a_OriginX = EndX;
- a_OriginY = EndY;
- a_OriginZ = EndZ;
- Radius = EndR;
- } // for i - a_NumSegments
-}
-
-
-
-
-
-int cStructGenWormNestCaves::cCaveSystem::GetRadius(cNoise & a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ)
-{
- // Instead of a flat distribution noise function, we need to shape it, so that most caves are smallish and only a few select are large
- int rnd = a_Noise.IntNoise3DInt(a_OriginX, a_OriginY, a_OriginZ) / 11;
- /*
- // Not good enough:
- // The algorithm of choice: emulate gauss-distribution noise by adding 3 flat noises, then fold it in half using absolute value.
- // To save on processing, use one random value and extract 3 bytes to be separately added as the gaussian noise
- int sum = (rnd & 0xff) + ((rnd >> 8) & 0xff) + ((rnd >> 16) & 0xff);
- // sum is now a gaussian-distribution noise within [0 .. 767], with center at 384.
- // We want mapping 384 -> 3, 0 -> 19, 768 -> 19, so divide by 24 to get [0 .. 31] with center at 16, then use abs() to fold around the center
- int res = 3 + abs((sum / 24) - 16);
- */
-
- // Algorithm of choice: random value in the range of zero to random value - heavily towards zero
- int res = MIN_RADIUS + (rnd >> 8) % ((rnd % (MAX_RADIUS - MIN_RADIUS)) + 1);
- return res;
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cStructGenWormNestCaves:
-
-cStructGenWormNestCaves::~cStructGenWormNestCaves()
-{
- ClearCache();
-}
-
-
-
-
-
-void cStructGenWormNestCaves::ClearCache(void)
-{
- for (cCaveSystems::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr)
- {
- delete *itr;
- } // for itr - m_Cache[]
- m_Cache.clear();
-}
-
-
-
-
-
-void cStructGenWormNestCaves::GenStructures(cChunkDesc & a_ChunkDesc)
-{
- int ChunkX = a_ChunkDesc.GetChunkX();
- int ChunkZ = a_ChunkDesc.GetChunkZ();
- cCaveSystems Caves;
- GetCavesForChunk(ChunkX, ChunkZ, Caves);
- for (cCaveSystems::const_iterator itr = Caves.begin(); itr != Caves.end(); ++itr)
- {
- (*itr)->ProcessChunk(ChunkX, ChunkZ, a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap());
- } // for itr - Caves[]
-}
-
-
-
-
-
-void cStructGenWormNestCaves::GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenWormNestCaves::cCaveSystems & a_Caves)
-{
- int BaseX = a_ChunkX * cChunkDef::Width / m_Grid;
- int BaseZ = a_ChunkZ * cChunkDef::Width / m_Grid;
- if (BaseX < 0)
- {
- --BaseX;
- }
- if (BaseZ < 0)
- {
- --BaseZ;
- }
- BaseX -= NEIGHBORHOOD_SIZE / 2;
- BaseZ -= NEIGHBORHOOD_SIZE / 2;
-
- // Walk the cache, move each cave system that we want into a_Caves:
- int StartX = BaseX * m_Grid;
- int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_Grid;
- int StartZ = BaseZ * m_Grid;
- int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_Grid;
- for (cCaveSystems::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;)
- {
- if (
- ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) &&
- ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ)
- )
- {
- // want
- a_Caves.push_back(*itr);
- itr = m_Cache.erase(itr);
- }
- else
- {
- // don't want
- ++itr;
- }
- } // for itr - m_Cache[]
-
- for (int x = 0; x < NEIGHBORHOOD_SIZE; x++)
- {
- int RealX = (BaseX + x) * m_Grid;
- for (int z = 0; z < NEIGHBORHOOD_SIZE; z++)
- {
- int RealZ = (BaseZ + z) * m_Grid;
- bool Found = false;
- for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr)
- {
- if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ))
- {
- Found = true;
- break;
- }
- }
- if (!Found)
- {
- a_Caves.push_back(new cCaveSystem(RealX, RealZ, m_MaxOffset, m_Size, m_Noise));
- }
- }
- }
-
- // Copy a_Caves into m_Cache to the beginning:
- cCaveSystems CavesCopy(a_Caves);
- m_Cache.splice(m_Cache.begin(), CavesCopy, CavesCopy.begin(), CavesCopy.end());
-
- // Trim the cache if it's too long:
- if (m_Cache.size() > 100)
- {
- cCaveSystems::iterator itr = m_Cache.begin();
- std::advance(itr, 100);
- for (cCaveSystems::iterator end = m_Cache.end(); itr != end; ++itr)
- {
- delete *itr;
- }
- itr = m_Cache.begin();
- std::advance(itr, 100);
- m_Cache.erase(itr, m_Cache.end());
- }
-
- /*
- // Uncomment this block for debugging the caves' shapes in 2D using an SVG export
- #ifdef _DEBUG
- AString SVG;
- SVG.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1024\" height = \"1024\">\n");
- SVG.reserve(2 * 1024 * 1024);
- for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr)
- {
- int Color = 0x10 * abs((*itr)->m_BlockX / m_Grid);
- Color |= 0x1000 * abs((*itr)->m_BlockZ / m_Grid);
- SVG.append((*itr)->ExportAsSVG(Color, 512, 512));
- }
- SVG.append("</svg>\n");
-
- AString fnam;
- Printf(fnam, "wnc\\%03d_%03d.svg", a_ChunkX, a_ChunkZ);
- cFile File(fnam, cFile::fmWrite);
- File.Write(SVG.c_str(), SVG.size());
- #endif // _DEBUG
- //*/
-}
-
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cStructGenMarbleCaves:
-
-static float GetMarbleNoise( float x, float y, float z, cNoise & a_Noise )
-{
- static const float PI_2 = 1.57079633f;
- float oct1 = (a_Noise.CubicNoise3D(x * 0.1f, y * 0.1f, z * 0.1f )) * 4;
-
- oct1 = oct1 * oct1 * oct1;
- if (oct1 < 0.f) oct1 = PI_2;
- if (oct1 > PI_2) oct1 = PI_2;
-
- return oct1;
-}
-
-
-
-
-
-void cStructGenMarbleCaves::GenStructures(cChunkDesc & a_ChunkDesc)
-{
- cNoise Noise(m_Seed);
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- const float zz = (float)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z);
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- const float xx = (float)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x);
-
- int Top = a_ChunkDesc.GetHeight(x, z);
- for (int y = 1; y < Top; ++y )
- {
- if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_STONE)
- {
- continue;
- }
-
- const float yy = (float)y;
- const float WaveNoise = 1;
- if (cosf(GetMarbleNoise(xx, yy * 0.5f, zz, Noise)) * fabs(cosf(yy * 0.2f + WaveNoise * 2) * 0.75f + WaveNoise) > 0.0005f)
- {
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR);
- }
- } // for y
- } // for x
- } // for z
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cStructGenDualRidgeCaves:
-
-void cStructGenDualRidgeCaves::GenStructures(cChunkDesc & a_ChunkDesc)
-{
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- const float zz = (float)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z) / 10;
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- const float xx = (float)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x) / 10;
-
- int Top = a_ChunkDesc.GetHeight(x, z);
- for (int y = 1; y <= Top; ++y)
- {
- const float yy = (float)y / 10;
- const float WaveNoise = 1;
- float n1 = m_Noise1.CubicNoise3D(xx, yy, zz);
- float n2 = m_Noise2.CubicNoise3D(xx, yy, zz);
- float n3 = m_Noise1.CubicNoise3D(xx * 4, yy * 4, zz * 4) / 4;
- float n4 = m_Noise2.CubicNoise3D(xx * 4, yy * 4, zz * 4) / 4;
- if ((abs(n1 + n3) * abs(n2 + n4)) > m_Threshold)
- {
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR);
- }
- } // for y
- } // for x
- } // for z
-}
-
-
-
-
+
+// Caves.cpp
+
+// Implements the various cave structure generators:
+// - cStructGenWormNestCaves
+// - cStructGenDualRidgeCaves
+// - cStructGenMarbleCaves
+// - cStructGenNetherCaves
+
+/*
+WormNestCave generator:
+Caves are generated in "nests" - groups of tunnels generated from a single point.
+For each chunk, all the nests that could intersect it are generated.
+For each nest, first the schematic structure is generated (tunnel from ... to ..., branch, tunnel2 from ... to ...)
+Then each tunnel is randomized by inserting points in between its ends.
+Finally each tunnel is smoothed and Bresenham-3D-ed so that it is a collection of spheres with their centers next to each other.
+When the tunnels are ready, they are simply carved into the chunk, one by one.
+To optimize, each tunnel keeps track of its bounding box, so that it can be skipped for chunks that don't intersect it.
+
+MarbleCaves generator:
+For each voxel a 3D noise function is evaluated, if the value crosses a boundary, the voxel is dug out, otherwise it is kept.
+Problem with this is the amount of CPU work needed for each chunk.
+Also the overall shape of the generated holes is unsatisfactory - there are whole "sheets" of holes in the ground.
+
+DualRidgeCaves generator:
+Instead of evaluating a single noise function, two different noise functions are multiplied. This produces
+regular tunnels instead of sheets. However due to the sheer amount of CPU work needed, the noise functions need to be
+reduced in complexity in order for this generator to be useful, so the caves' shapes are "bubbly" at best.
+*/
+
+#include "Globals.h"
+#include "Caves.h"
+
+
+
+
+
+/// How many nests in each direction are generated for a given chunk. Must be an even number
+#define NEIGHBORHOOD_SIZE 8
+
+
+
+
+
+const int MIN_RADIUS = 3;
+const int MAX_RADIUS = 8;
+
+
+
+
+
+struct cCaveDefPoint
+{
+ int m_BlockX;
+ int m_BlockY;
+ int m_BlockZ;
+ int m_Radius;
+
+ cCaveDefPoint(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Radius) :
+ m_BlockX(a_BlockX),
+ m_BlockY(a_BlockY),
+ m_BlockZ(a_BlockZ),
+ m_Radius(a_Radius)
+ {
+ }
+} ;
+
+typedef std::vector<cCaveDefPoint> cCaveDefPoints;
+
+
+
+
+
+/// A single non-branching tunnel of a WormNestCave
+class cCaveTunnel
+{
+ // The bounding box, including the radii around defpoints:
+ int m_MinBlockX, m_MaxBlockX;
+ int m_MinBlockY, m_MaxBlockY;
+ int m_MinBlockZ, m_MaxBlockZ;
+
+ /// Generates the shaping defpoints for the ravine, based on the ravine block coords and noise
+ void Randomize(cNoise & a_Noise);
+
+ /// Refines (adds and smooths) defpoints from a_Src into a_Dst; returns false if no refinement possible (segments too short)
+ bool RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints & a_Dst);
+
+ /// Does rounds of smoothing, two passes of RefineDefPoints(), as long as they return true
+ void Smooth(void);
+
+ /// Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block
+ void FinishLinear(void);
+
+ /// Calculates the bounding box of the points present
+ void CalcBoundingBox(void);
+
+public:
+ cCaveDefPoints m_Points;
+
+ cCaveTunnel(
+ int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ, int a_StartRadius,
+ int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ, int a_EndRadius,
+ cNoise & a_Noise
+ );
+
+ /// Carves the tunnel into the chunk specified
+ void ProcessChunk(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes,
+ cChunkDef::HeightMap & a_HeightMap
+ );
+
+ #ifdef _DEBUG
+ AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const;
+ #endif // _DEBUG
+} ;
+
+typedef std::vector<cCaveTunnel *> cCaveTunnels;
+
+
+
+
+
+/// A collection of connected tunnels, possibly branching.
+class cStructGenWormNestCaves::cCaveSystem
+{
+public:
+ // The generating block position; is read directly in cStructGenWormNestCaves::GetCavesForChunk()
+ int m_BlockX;
+ int m_BlockZ;
+
+ cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise);
+ ~cCaveSystem();
+
+ /// Carves the cave system into the chunk specified
+ void ProcessChunk(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes,
+ cChunkDef::HeightMap & a_HeightMap
+ );
+
+ #ifdef _DEBUG
+ AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const;
+ #endif // _DEBUG
+
+protected:
+ int m_Size;
+ cCaveTunnels m_Tunnels;
+
+ void Clear(void);
+
+ /// Generates a_Segment successive tunnels, with possible branches. Generates the same output for the same [x, y, z, a_Segments]
+ void GenerateTunnelsFromPoint(
+ int a_OriginX, int a_OriginY, int a_OriginZ,
+ cNoise & a_Noise, int a_Segments
+ );
+
+ /// Returns a radius based on the location provided.
+ int GetRadius(cNoise & a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ);
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCaveTunnel:
+
+cCaveTunnel::cCaveTunnel(
+ int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ, int a_StartRadius,
+ int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ, int a_EndRadius,
+ cNoise & a_Noise
+)
+{
+ m_Points.push_back(cCaveDefPoint(a_BlockStartX, a_BlockStartY, a_BlockStartZ, a_StartRadius));
+ m_Points.push_back(cCaveDefPoint(a_BlockEndX, a_BlockEndY, a_BlockEndZ, a_EndRadius));
+
+ if ((a_BlockStartY <= 0) && (a_BlockEndY <= 0))
+ {
+ // Don't bother detailing this cave, it's under the world anyway
+ return;
+ }
+
+ Randomize(a_Noise);
+ Smooth();
+
+ // We know that the linear finishing won't affect the bounding box, so let's calculate it now, as we have less data:
+ CalcBoundingBox();
+
+ FinishLinear();
+}
+
+
+
+
+
+void cCaveTunnel::Randomize(cNoise & a_Noise)
+{
+ // Repeat 4 times:
+ for (int i = 0; i < 4; i++)
+ {
+ // For each already present point, insert a point in between it and its predecessor, shifted randomly.
+ int PrevX = m_Points.front().m_BlockX;
+ int PrevY = m_Points.front().m_BlockY;
+ int PrevZ = m_Points.front().m_BlockZ;
+ int PrevR = m_Points.front().m_Radius;
+ cCaveDefPoints Pts;
+ Pts.reserve(m_Points.size() * 2 + 1);
+ Pts.push_back(m_Points.front());
+ for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr)
+ {
+ int Random = a_Noise.IntNoise3DInt(PrevX, PrevY, PrevZ + i) / 11;
+ int len = (PrevX - itr->m_BlockX) * (PrevX - itr->m_BlockX);
+ len += (PrevY - itr->m_BlockY) * (PrevY - itr->m_BlockY);
+ len += (PrevZ - itr->m_BlockZ) * (PrevZ - itr->m_BlockZ);
+ len = 3 * (int)sqrt((double)len) / 4;
+ int Rad = std::min(MAX_RADIUS, std::max(MIN_RADIUS, (PrevR + itr->m_Radius) / 2 + (Random % 3) - 1));
+ Random /= 4;
+ int x = (itr->m_BlockX + PrevX) / 2 + (Random % (len + 1) - len / 2);
+ Random /= 256;
+ int y = (itr->m_BlockY + PrevY) / 2 + (Random % (len / 2 + 1) - len / 4);
+ Random /= 256;
+ int z = (itr->m_BlockZ + PrevZ) / 2 + (Random % (len + 1) - len / 2);
+ Pts.push_back(cCaveDefPoint(x, y, z, Rad));
+ Pts.push_back(*itr);
+ PrevX = itr->m_BlockX;
+ PrevY = itr->m_BlockY;
+ PrevZ = itr->m_BlockZ;
+ PrevR = itr->m_Radius;
+ }
+ std::swap(Pts, m_Points);
+ }
+}
+
+
+
+
+
+bool cCaveTunnel::RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints & a_Dst)
+{
+ // Smoothing: for each line segment, add points on its 1/4 lengths
+ bool res = false;
+ int Num = a_Src.size() - 2; // this many intermediary points
+ a_Dst.clear();
+ a_Dst.reserve(Num * 2 + 2);
+ cCaveDefPoints::const_iterator itr = a_Src.begin() + 1;
+ a_Dst.push_back(a_Src.front());
+ int PrevX = a_Src.front().m_BlockX;
+ int PrevY = a_Src.front().m_BlockY;
+ int PrevZ = a_Src.front().m_BlockZ;
+ int PrevR = a_Src.front().m_Radius;
+ for (int i = 0; i <= Num; ++i, ++itr)
+ {
+ int dx = itr->m_BlockX - PrevX;
+ int dy = itr->m_BlockY - PrevY;
+ int dz = itr->m_BlockZ - PrevZ;
+ if (abs(dx) + abs(dz) + abs(dy) < 6)
+ {
+ // Too short a segment to smooth-subdivide into quarters
+ PrevX = itr->m_BlockX;
+ PrevY = itr->m_BlockY;
+ PrevZ = itr->m_BlockZ;
+ PrevR = itr->m_Radius;
+ continue;
+ }
+ int dr = itr->m_Radius - PrevR;
+ int Rad1 = std::max(PrevR + 1 * dr / 4, 1);
+ int Rad2 = std::max(PrevR + 3 * dr / 4, 1);
+ a_Dst.push_back(cCaveDefPoint(PrevX + 1 * dx / 4, PrevY + 1 * dy / 4, PrevZ + 1 * dz / 4, Rad1));
+ a_Dst.push_back(cCaveDefPoint(PrevX + 3 * dx / 4, PrevY + 3 * dy / 4, PrevZ + 3 * dz / 4, Rad2));
+ PrevX = itr->m_BlockX;
+ PrevY = itr->m_BlockY;
+ PrevZ = itr->m_BlockZ;
+ PrevR = itr->m_Radius;
+ res = true;
+ }
+ a_Dst.push_back(a_Src.back());
+ return res && (a_Src.size() < a_Dst.size());
+}
+
+
+
+
+
+void cCaveTunnel::Smooth(void)
+{
+ cCaveDefPoints Pts;
+ while (true)
+ {
+ if (!RefineDefPoints(m_Points, Pts))
+ {
+ std::swap(Pts, m_Points);
+ return;
+ }
+ if (!RefineDefPoints(Pts, m_Points))
+ {
+ return;
+ }
+ }
+}
+
+
+
+
+
+void cCaveTunnel::FinishLinear(void)
+{
+ // For each segment, use Bresenham's 3D line algorithm to draw a "line" of defpoints
+ cCaveDefPoints Pts;
+ std::swap(Pts, m_Points);
+
+ m_Points.reserve(Pts.size() * 3);
+ int PrevX = Pts.front().m_BlockX;
+ int PrevY = Pts.front().m_BlockY;
+ int PrevZ = Pts.front().m_BlockZ;
+ for (cCaveDefPoints::const_iterator itr = Pts.begin() + 1, end = Pts.end(); itr != end; ++itr)
+ {
+ int x1 = itr->m_BlockX;
+ int y1 = itr->m_BlockY;
+ int z1 = itr->m_BlockZ;
+ int dx = abs(x1 - PrevX);
+ int dy = abs(y1 - PrevY);
+ int dz = abs(z1 - PrevZ);
+ int sx = (PrevX < x1) ? 1 : -1;
+ int sy = (PrevY < y1) ? 1 : -1;
+ int sz = (PrevZ < z1) ? 1 : -1;
+ int err = dx - dz;
+ int R = itr->m_Radius;
+
+ if (dx >= std::max(dy, dz)) // x dominant
+ {
+ int yd = dy - dx / 2;
+ int zd = dz - dx / 2;
+
+ while (true)
+ {
+ m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R));
+
+ if (PrevX == x1)
+ {
+ break;
+ }
+
+ if (yd >= 0) // move along y
+ {
+ PrevY += sy;
+ yd -= dx;
+ }
+
+ if (zd >= 0) // move along z
+ {
+ PrevZ += sz;
+ zd -= dx;
+ }
+
+ // move along x
+ PrevX += sx;
+ yd += dy;
+ zd += dz;
+ }
+ }
+ else if (dy >= std::max(dx, dz)) // y dominant
+ {
+ int xd = dx - dy / 2;
+ int zd = dz - dy / 2;
+
+ while (true)
+ {
+ m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R));
+
+ if (PrevY == y1)
+ {
+ break;
+ }
+
+ if (xd >= 0) // move along x
+ {
+ PrevX += sx;
+ xd -= dy;
+ }
+
+ if (zd >= 0) // move along z
+ {
+ PrevZ += sz;
+ zd -= dy;
+ }
+
+ // move along y
+ PrevY += sy;
+ xd += dx;
+ zd += dz;
+ }
+ }
+ else
+ {
+ // z dominant
+ ASSERT(dz >= std::max(dx, dy));
+ int xd = dx - dz / 2;
+ int yd = dy - dz / 2;
+
+ while (true)
+ {
+ m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R));
+
+ if (PrevZ == z1)
+ {
+ break;
+ }
+
+ if (xd >= 0) // move along x
+ {
+ PrevX += sx;
+ xd -= dz;
+ }
+
+ if (yd >= 0) // move along y
+ {
+ PrevY += sy;
+ yd -= dz;
+ }
+
+ // move along z
+ PrevZ += sz;
+ xd += dx;
+ yd += dy;
+ }
+ } // if (which dimension is dominant)
+ } // for itr
+}
+
+
+
+
+
+void cCaveTunnel::CalcBoundingBox(void)
+{
+ m_MinBlockX = m_MaxBlockX = m_Points.front().m_BlockX;
+ m_MinBlockY = m_MaxBlockY = m_Points.front().m_BlockY;
+ m_MinBlockZ = m_MaxBlockZ = m_Points.front().m_BlockZ;
+ for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr)
+ {
+ m_MinBlockX = std::min(m_MinBlockX, itr->m_BlockX - itr->m_Radius);
+ m_MaxBlockX = std::max(m_MaxBlockX, itr->m_BlockX + itr->m_Radius);
+ m_MinBlockY = std::min(m_MinBlockY, itr->m_BlockY - itr->m_Radius);
+ m_MaxBlockY = std::max(m_MaxBlockY, itr->m_BlockY + itr->m_Radius);
+ m_MinBlockZ = std::min(m_MinBlockZ, itr->m_BlockZ - itr->m_Radius);
+ m_MaxBlockZ = std::max(m_MaxBlockZ, itr->m_BlockZ + itr->m_Radius);
+ } // for itr - m_Points[]
+}
+
+
+
+
+
+void cCaveTunnel::ProcessChunk(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes,
+ cChunkDef::HeightMap & a_HeightMap
+)
+{
+ int BaseX = a_ChunkX * cChunkDef::Width;
+ int BaseZ = a_ChunkZ * cChunkDef::Width;
+ if (
+ (BaseX > m_MaxBlockX) || (BaseX + cChunkDef::Width < m_MinBlockX) ||
+ (BaseX > m_MaxBlockX) || (BaseX + cChunkDef::Width < m_MinBlockX)
+ )
+ {
+ // Tunnel does not intersect the chunk at all, bail out
+ return;
+ }
+
+ int BlockStartX = a_ChunkX * cChunkDef::Width;
+ int BlockStartZ = a_ChunkZ * cChunkDef::Width;
+ int BlockEndX = BlockStartX + cChunkDef::Width;
+ int BlockEndZ = BlockStartZ + cChunkDef::Width;
+ for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr)
+ {
+ if (
+ (itr->m_BlockX + itr->m_Radius < BlockStartX) ||
+ (itr->m_BlockX - itr->m_Radius > BlockEndX) ||
+ (itr->m_BlockZ + itr->m_Radius < BlockStartZ) ||
+ (itr->m_BlockZ - itr->m_Radius > BlockEndZ)
+ )
+ {
+ // Cannot intersect, bail out early
+ continue;
+ }
+
+ // Carve out a sphere around the xyz point, m_Radius in diameter; skip 3/7 off the top and bottom:
+ int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc
+ int DifY = itr->m_BlockY;
+ int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc
+ int Bottom = std::max(itr->m_BlockY - 3 * itr->m_Radius / 7, 1);
+ int Top = std::min(itr->m_BlockY + 3 * itr->m_Radius / 7, (int)(cChunkDef::Height));
+ int SqRad = itr->m_Radius * itr->m_Radius;
+ for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ for (int y = Bottom; y <= Top; y++)
+ {
+ int SqDist = (DifX - x) * (DifX - x) + (DifY - y) * (DifY - y) + (DifZ - z) * (DifZ - z);
+ if (4 * SqDist <= SqRad)
+ {
+ switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z))
+ {
+ // Only carve out these specific block types
+ case E_BLOCK_DIRT:
+ case E_BLOCK_GRASS:
+ case E_BLOCK_STONE:
+ case E_BLOCK_COBBLESTONE:
+ case E_BLOCK_GRAVEL:
+ case E_BLOCK_SAND:
+ case E_BLOCK_SANDSTONE:
+ case E_BLOCK_NETHERRACK:
+ case E_BLOCK_COAL_ORE:
+ case E_BLOCK_IRON_ORE:
+ case E_BLOCK_GOLD_ORE:
+ case E_BLOCK_DIAMOND_ORE:
+ case E_BLOCK_REDSTONE_ORE:
+ case E_BLOCK_REDSTONE_ORE_GLOWING:
+ {
+ cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR);
+ break;
+ }
+ default: break;
+ }
+ }
+ } // for y
+ } // for x, z
+ } // for itr - m_Points[]
+
+ /*
+ #ifdef _DEBUG
+ // For debugging purposes, outline the shape of the cave using glowstone, *after* carving the entire cave:
+ for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr)
+ {
+ int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc
+ int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc
+ if (
+ (DifX >= 0) && (DifX < cChunkDef::Width) &&
+ (itr->m_BlockY > 0) && (itr->m_BlockY < cChunkDef::Height) &&
+ (DifZ >= 0) && (DifZ < cChunkDef::Width)
+ )
+ {
+ cChunkDef::SetBlock(a_BlockTypes, DifX, itr->m_BlockY, DifZ, E_BLOCK_GLOWSTONE);
+ }
+ } // for itr - m_Points[]
+ #endif // _DEBUG
+ //*/
+}
+
+
+
+
+
+#ifdef _DEBUG
+AString cCaveTunnel::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const
+{
+ AString SVG;
+ SVG.reserve(m_Points.size() * 20 + 200);
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#%06x;stroke-width:1px;\"\nd=\"", a_Color);
+ char Prefix = 'M'; // The first point needs "M" prefix, all the others need "L"
+ for (cCaveDefPoints::const_iterator itr = m_Points.begin(); itr != m_Points.end(); ++itr)
+ {
+ AppendPrintf(SVG, "%c %d,%d ", Prefix, a_OffsetX + itr->m_BlockX, a_OffsetZ + itr->m_BlockZ);
+ Prefix = 'L';
+ }
+ SVG.append("\"/>\n");
+ return SVG;
+}
+#endif // _DEBUG
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenWormNestCaves::cCaveSystem:
+
+cStructGenWormNestCaves::cCaveSystem::cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise) :
+ m_BlockX(a_BlockX),
+ m_BlockZ(a_BlockZ),
+ m_Size(a_Size)
+{
+ int Num = 1 + a_Noise.IntNoise2DInt(a_BlockX, a_BlockZ) % 3;
+ for (int i = 0; i < Num; i++)
+ {
+ int OriginX = a_BlockX + (a_Noise.IntNoise3DInt(13 * a_BlockX, 17 * a_BlockZ, 11 * i) / 19) % a_MaxOffset;
+ int OriginZ = a_BlockZ + (a_Noise.IntNoise3DInt(17 * a_BlockX, 13 * a_BlockZ, 11 * i) / 23) % a_MaxOffset;
+ int OriginY = 20 + (a_Noise.IntNoise3DInt(19 * a_BlockX, 13 * a_BlockZ, 11 * i) / 17) % 20;
+
+ // Generate three branches from the origin point:
+ // The tunnels generated depend on X, Y, Z and Branches,
+ // for the same set of numbers it generates the same offsets!
+ // That's why we add a +1 to X in the third line
+ GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 3);
+ GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 2);
+ GenerateTunnelsFromPoint(OriginX + 1, OriginY, OriginZ, a_Noise, 3);
+ }
+}
+
+
+
+
+
+cStructGenWormNestCaves::cCaveSystem::~cCaveSystem()
+{
+ Clear();
+}
+
+
+
+
+
+
+void cStructGenWormNestCaves::cCaveSystem::ProcessChunk(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes,
+ cChunkDef::HeightMap & a_HeightMap
+)
+{
+ for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr)
+ {
+ (*itr)->ProcessChunk(a_ChunkX, a_ChunkZ, a_BlockTypes, a_HeightMap);
+ } // for itr - m_Tunnels[]
+}
+
+
+
+
+
+#ifdef _DEBUG
+AString cStructGenWormNestCaves::cCaveSystem::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const
+{
+ AString SVG;
+ SVG.reserve(512 * 1024);
+ for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr)
+ {
+ SVG.append((*itr)->ExportAsSVG(a_Color, a_OffsetX, a_OffsetZ));
+ } // for itr - m_Tunnels[]
+
+ // Base point highlight:
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
+ a_OffsetX + m_BlockX - 5, a_OffsetZ + m_BlockZ, a_OffsetX + m_BlockX + 5, a_OffsetZ + m_BlockZ
+ );
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
+ a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ - 5, a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ + 5
+ );
+
+ // A gray line from the base point to the first point of the ravine, for identification:
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#cfcfcf;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
+ a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ,
+ a_OffsetX + m_Tunnels.front()->m_Points.front().m_BlockX,
+ a_OffsetZ + m_Tunnels.front()->m_Points.front().m_BlockZ
+ );
+
+ // Offset guides:
+ if (a_OffsetX > 0)
+ {
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M %d,0 L %d,1024\"/>\n",
+ a_OffsetX, a_OffsetX
+ );
+ }
+ if (a_OffsetZ > 0)
+ {
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M 0,%d L 1024,%d\"/>\n",
+ a_OffsetZ, a_OffsetZ
+ );
+ }
+
+ return SVG;
+}
+#endif // _DEBUG
+
+
+
+
+
+void cStructGenWormNestCaves::cCaveSystem::Clear(void)
+{
+ for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr)
+ {
+ delete *itr;
+ }
+ m_Tunnels.clear();
+}
+
+
+
+
+
+void cStructGenWormNestCaves::cCaveSystem::GenerateTunnelsFromPoint(
+ int a_OriginX, int a_OriginY, int a_OriginZ,
+ cNoise & a_Noise, int a_NumSegments
+)
+{
+ int DoubleSize = m_Size * 2;
+ int Radius = GetRadius(a_Noise, a_OriginX + a_OriginY, a_OriginY + a_OriginZ, a_OriginZ + a_OriginX);
+ for (int i = a_NumSegments - 1; i >= 0; --i)
+ {
+ int EndX = a_OriginX + (((a_Noise.IntNoise3DInt(a_OriginX, a_OriginY, a_OriginZ + 11 * a_NumSegments) / 7) % DoubleSize) - m_Size) / 2;
+ int EndY = a_OriginY + (((a_Noise.IntNoise3DInt(a_OriginY, 13 * a_NumSegments, a_OriginZ + a_OriginX) / 7) % DoubleSize) - m_Size) / 4;
+ int EndZ = a_OriginZ + (((a_Noise.IntNoise3DInt(a_OriginZ + 17 * a_NumSegments, a_OriginX, a_OriginY) / 7) % DoubleSize) - m_Size) / 2;
+ int EndR = GetRadius(a_Noise, a_OriginX + 7 * i, a_OriginY + 11 * i, a_OriginZ + a_OriginX);
+ m_Tunnels.push_back(new cCaveTunnel(a_OriginX, a_OriginY, a_OriginZ, Radius, EndX, EndY, EndZ, EndR, a_Noise));
+ GenerateTunnelsFromPoint(EndX, EndY, EndZ, a_Noise, i);
+ a_OriginX = EndX;
+ a_OriginY = EndY;
+ a_OriginZ = EndZ;
+ Radius = EndR;
+ } // for i - a_NumSegments
+}
+
+
+
+
+
+int cStructGenWormNestCaves::cCaveSystem::GetRadius(cNoise & a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ)
+{
+ // Instead of a flat distribution noise function, we need to shape it, so that most caves are smallish and only a few select are large
+ int rnd = a_Noise.IntNoise3DInt(a_OriginX, a_OriginY, a_OriginZ) / 11;
+ /*
+ // Not good enough:
+ // The algorithm of choice: emulate gauss-distribution noise by adding 3 flat noises, then fold it in half using absolute value.
+ // To save on processing, use one random value and extract 3 bytes to be separately added as the gaussian noise
+ int sum = (rnd & 0xff) + ((rnd >> 8) & 0xff) + ((rnd >> 16) & 0xff);
+ // sum is now a gaussian-distribution noise within [0 .. 767], with center at 384.
+ // We want mapping 384 -> 3, 0 -> 19, 768 -> 19, so divide by 24 to get [0 .. 31] with center at 16, then use abs() to fold around the center
+ int res = 3 + abs((sum / 24) - 16);
+ */
+
+ // Algorithm of choice: random value in the range of zero to random value - heavily towards zero
+ int res = MIN_RADIUS + (rnd >> 8) % ((rnd % (MAX_RADIUS - MIN_RADIUS)) + 1);
+ return res;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenWormNestCaves:
+
+cStructGenWormNestCaves::~cStructGenWormNestCaves()
+{
+ ClearCache();
+}
+
+
+
+
+
+void cStructGenWormNestCaves::ClearCache(void)
+{
+ for (cCaveSystems::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr)
+ {
+ delete *itr;
+ } // for itr - m_Cache[]
+ m_Cache.clear();
+}
+
+
+
+
+
+void cStructGenWormNestCaves::GenStructures(cChunkDesc & a_ChunkDesc)
+{
+ int ChunkX = a_ChunkDesc.GetChunkX();
+ int ChunkZ = a_ChunkDesc.GetChunkZ();
+ cCaveSystems Caves;
+ GetCavesForChunk(ChunkX, ChunkZ, Caves);
+ for (cCaveSystems::const_iterator itr = Caves.begin(); itr != Caves.end(); ++itr)
+ {
+ (*itr)->ProcessChunk(ChunkX, ChunkZ, a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap());
+ } // for itr - Caves[]
+}
+
+
+
+
+
+void cStructGenWormNestCaves::GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenWormNestCaves::cCaveSystems & a_Caves)
+{
+ int BaseX = a_ChunkX * cChunkDef::Width / m_Grid;
+ int BaseZ = a_ChunkZ * cChunkDef::Width / m_Grid;
+ if (BaseX < 0)
+ {
+ --BaseX;
+ }
+ if (BaseZ < 0)
+ {
+ --BaseZ;
+ }
+ BaseX -= NEIGHBORHOOD_SIZE / 2;
+ BaseZ -= NEIGHBORHOOD_SIZE / 2;
+
+ // Walk the cache, move each cave system that we want into a_Caves:
+ int StartX = BaseX * m_Grid;
+ int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_Grid;
+ int StartZ = BaseZ * m_Grid;
+ int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_Grid;
+ for (cCaveSystems::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;)
+ {
+ if (
+ ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) &&
+ ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ)
+ )
+ {
+ // want
+ a_Caves.push_back(*itr);
+ itr = m_Cache.erase(itr);
+ }
+ else
+ {
+ // don't want
+ ++itr;
+ }
+ } // for itr - m_Cache[]
+
+ for (int x = 0; x < NEIGHBORHOOD_SIZE; x++)
+ {
+ int RealX = (BaseX + x) * m_Grid;
+ for (int z = 0; z < NEIGHBORHOOD_SIZE; z++)
+ {
+ int RealZ = (BaseZ + z) * m_Grid;
+ bool Found = false;
+ for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr)
+ {
+ if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ))
+ {
+ Found = true;
+ break;
+ }
+ }
+ if (!Found)
+ {
+ a_Caves.push_back(new cCaveSystem(RealX, RealZ, m_MaxOffset, m_Size, m_Noise));
+ }
+ }
+ }
+
+ // Copy a_Caves into m_Cache to the beginning:
+ cCaveSystems CavesCopy(a_Caves);
+ m_Cache.splice(m_Cache.begin(), CavesCopy, CavesCopy.begin(), CavesCopy.end());
+
+ // Trim the cache if it's too long:
+ if (m_Cache.size() > 100)
+ {
+ cCaveSystems::iterator itr = m_Cache.begin();
+ std::advance(itr, 100);
+ for (cCaveSystems::iterator end = m_Cache.end(); itr != end; ++itr)
+ {
+ delete *itr;
+ }
+ itr = m_Cache.begin();
+ std::advance(itr, 100);
+ m_Cache.erase(itr, m_Cache.end());
+ }
+
+ /*
+ // Uncomment this block for debugging the caves' shapes in 2D using an SVG export
+ #ifdef _DEBUG
+ AString SVG;
+ SVG.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1024\" height = \"1024\">\n");
+ SVG.reserve(2 * 1024 * 1024);
+ for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr)
+ {
+ int Color = 0x10 * abs((*itr)->m_BlockX / m_Grid);
+ Color |= 0x1000 * abs((*itr)->m_BlockZ / m_Grid);
+ SVG.append((*itr)->ExportAsSVG(Color, 512, 512));
+ }
+ SVG.append("</svg>\n");
+
+ AString fnam;
+ Printf(fnam, "wnc\\%03d_%03d.svg", a_ChunkX, a_ChunkZ);
+ cFile File(fnam, cFile::fmWrite);
+ File.Write(SVG.c_str(), SVG.size());
+ #endif // _DEBUG
+ //*/
+}
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenMarbleCaves:
+
+static float GetMarbleNoise( float x, float y, float z, cNoise & a_Noise )
+{
+ static const float PI_2 = 1.57079633f;
+ float oct1 = (a_Noise.CubicNoise3D(x * 0.1f, y * 0.1f, z * 0.1f )) * 4;
+
+ oct1 = oct1 * oct1 * oct1;
+ if (oct1 < 0.f) oct1 = PI_2;
+ if (oct1 > PI_2) oct1 = PI_2;
+
+ return oct1;
+}
+
+
+
+
+
+void cStructGenMarbleCaves::GenStructures(cChunkDesc & a_ChunkDesc)
+{
+ cNoise Noise(m_Seed);
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ const float zz = (float)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z);
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ const float xx = (float)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x);
+
+ int Top = a_ChunkDesc.GetHeight(x, z);
+ for (int y = 1; y < Top; ++y )
+ {
+ if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_STONE)
+ {
+ continue;
+ }
+
+ const float yy = (float)y;
+ const float WaveNoise = 1;
+ if (cosf(GetMarbleNoise(xx, yy * 0.5f, zz, Noise)) * fabs(cosf(yy * 0.2f + WaveNoise * 2) * 0.75f + WaveNoise) > 0.0005f)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR);
+ }
+ } // for y
+ } // for x
+ } // for z
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenDualRidgeCaves:
+
+void cStructGenDualRidgeCaves::GenStructures(cChunkDesc & a_ChunkDesc)
+{
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ const float zz = (float)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z) / 10;
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ const float xx = (float)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x) / 10;
+
+ int Top = a_ChunkDesc.GetHeight(x, z);
+ for (int y = 1; y <= Top; ++y)
+ {
+ const float yy = (float)y / 10;
+ const float WaveNoise = 1;
+ float n1 = m_Noise1.CubicNoise3D(xx, yy, zz);
+ float n2 = m_Noise2.CubicNoise3D(xx, yy, zz);
+ float n3 = m_Noise1.CubicNoise3D(xx * 4, yy * 4, zz * 4) / 4;
+ float n4 = m_Noise2.CubicNoise3D(xx * 4, yy * 4, zz * 4) / 4;
+ if ((abs(n1 + n3) * abs(n2 + n4)) > m_Threshold)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR);
+ }
+ } // for y
+ } // for x
+ } // for z
+}
+
+
+
+
diff --git a/source/Generating/Caves.h b/source/Generating/Caves.h
index bb69550a8..70cf6fe8c 100644
--- a/source/Generating/Caves.h
+++ b/source/Generating/Caves.h
@@ -1,102 +1,102 @@
-
-// Caves.h
-
-// Interfaces to the various cave structure generators:
-// - cStructGenWormNestCaves
-// - cStructGenMarbleCaves
-// - cStructGenNetherCaves
-
-
-
-
-
-#pragma once
-
-#include "ComposableGenerator.h"
-#include "../Noise.h"
-
-
-
-
-
-class cStructGenMarbleCaves :
- public cStructureGen
-{
-public:
- cStructGenMarbleCaves(int a_Seed) : m_Seed(a_Seed) {}
-
-protected:
-
- int m_Seed;
-
- // cStructureGen override:
- virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
-} ;
-
-
-
-
-
-class cStructGenDualRidgeCaves :
- public cStructureGen
-{
-public:
- cStructGenDualRidgeCaves(int a_Seed, float a_Threshold) :
- m_Noise1(a_Seed),
- m_Noise2(2 * a_Seed + 19999),
- m_Seed(a_Seed),
- m_Threshold(a_Threshold)
- {
- }
-
-protected:
- cNoise m_Noise1;
- cNoise m_Noise2;
- int m_Seed;
- float m_Threshold;
-
- // cStructureGen override:
- virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
-} ;
-
-
-
-
-
-class cStructGenWormNestCaves :
- public cStructureGen
-{
-public:
- cStructGenWormNestCaves(int a_Seed, int a_Size = 64, int a_Grid = 96, int a_MaxOffset = 128) :
- m_Noise(a_Seed),
- m_Size(a_Size),
- m_Grid(a_Grid),
- m_MaxOffset(a_MaxOffset)
- {
- }
-
- ~cStructGenWormNestCaves();
-
-protected:
- class cCaveSystem; // fwd: Caves.cpp
- typedef std::list<cCaveSystem *> cCaveSystems;
-
- cNoise m_Noise;
- int m_Size; // relative size of the cave systems' caves. Average number of blocks of each initial tunnel
- int m_MaxOffset; // maximum offset of the cave nest origin from the grid cell the nest belongs to
- int m_Grid; // average spacing of the nests
- cCaveSystems m_Cache;
-
- /// Clears everything from the cache
- void ClearCache(void);
-
- /// Returns all caves that *may* intersect the given chunk. All the caves are valid until the next call to this function.
- void GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cCaveSystems & a_Caves);
-
- // cStructGen override:
- virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
-} ;
-
-
-
-
+
+// Caves.h
+
+// Interfaces to the various cave structure generators:
+// - cStructGenWormNestCaves
+// - cStructGenMarbleCaves
+// - cStructGenNetherCaves
+
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+#include "../Noise.h"
+
+
+
+
+
+class cStructGenMarbleCaves :
+ public cStructureGen
+{
+public:
+ cStructGenMarbleCaves(int a_Seed) : m_Seed(a_Seed) {}
+
+protected:
+
+ int m_Seed;
+
+ // cStructureGen override:
+ virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
+class cStructGenDualRidgeCaves :
+ public cStructureGen
+{
+public:
+ cStructGenDualRidgeCaves(int a_Seed, float a_Threshold) :
+ m_Noise1(a_Seed),
+ m_Noise2(2 * a_Seed + 19999),
+ m_Seed(a_Seed),
+ m_Threshold(a_Threshold)
+ {
+ }
+
+protected:
+ cNoise m_Noise1;
+ cNoise m_Noise2;
+ int m_Seed;
+ float m_Threshold;
+
+ // cStructureGen override:
+ virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
+class cStructGenWormNestCaves :
+ public cStructureGen
+{
+public:
+ cStructGenWormNestCaves(int a_Seed, int a_Size = 64, int a_Grid = 96, int a_MaxOffset = 128) :
+ m_Noise(a_Seed),
+ m_Size(a_Size),
+ m_Grid(a_Grid),
+ m_MaxOffset(a_MaxOffset)
+ {
+ }
+
+ ~cStructGenWormNestCaves();
+
+protected:
+ class cCaveSystem; // fwd: Caves.cpp
+ typedef std::list<cCaveSystem *> cCaveSystems;
+
+ cNoise m_Noise;
+ int m_Size; // relative size of the cave systems' caves. Average number of blocks of each initial tunnel
+ int m_MaxOffset; // maximum offset of the cave nest origin from the grid cell the nest belongs to
+ int m_Grid; // average spacing of the nests
+ cCaveSystems m_Cache;
+
+ /// Clears everything from the cache
+ void ClearCache(void);
+
+ /// Returns all caves that *may* intersect the given chunk. All the caves are valid until the next call to this function.
+ void GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cCaveSystems & a_Caves);
+
+ // cStructGen override:
+ virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
diff --git a/source/Generating/ChunkDesc.cpp b/source/Generating/ChunkDesc.cpp
index 5c59055f7..dc6c74a3c 100644
--- a/source/Generating/ChunkDesc.cpp
+++ b/source/Generating/ChunkDesc.cpp
@@ -1,578 +1,578 @@
-
-// ChunkDesc.cpp
-
-// Implements the cChunkDesc class representing the chunk description used while generating a chunk. This class is also exported to Lua for HOOK_CHUNK_GENERATING.
-
-#include "Globals.h"
-#include "ChunkDesc.h"
-#include "../BlockArea.h"
-#include "../Cuboid.h"
-#include "../Noise.h"
-
-
-
-
-
-cChunkDesc::cChunkDesc(int a_ChunkX, int a_ChunkZ) :
- m_ChunkX(a_ChunkX),
- m_ChunkZ(a_ChunkZ),
- m_bUseDefaultBiomes(true),
- m_bUseDefaultHeight(true),
- m_bUseDefaultComposition(true),
- m_bUseDefaultStructures(true),
- m_bUseDefaultFinish(true)
-{
- m_BlockArea.Create(cChunkDef::Width, cChunkDef::Height, cChunkDef::Width);
- /*
- memset(m_BlockTypes, 0, sizeof(cChunkDef::BlockTypes));
- memset(m_BlockMeta, 0, sizeof(cChunkDef::BlockNibbles));
- */
- memset(m_BiomeMap, 0, sizeof(cChunkDef::BiomeMap));
- memset(m_HeightMap, 0, sizeof(cChunkDef::HeightMap));
-}
-
-
-
-
-
-cChunkDesc::~cChunkDesc()
-{
- // Nothing needed yet
-}
-
-
-
-
-
-void cChunkDesc::SetChunkCoords(int a_ChunkX, int a_ChunkZ)
-{
- m_ChunkX = a_ChunkX;
- m_ChunkZ = a_ChunkZ;
-}
-
-
-
-
-
-void cChunkDesc::FillBlocks(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
-{
- m_BlockArea.Fill(cBlockArea::baTypes | cBlockArea::baMetas, a_BlockType, a_BlockMeta);
-}
-
-
-
-
-
-void cChunkDesc::SetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
-{
- m_BlockArea.SetRelBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
-}
-
-
-
-
-
-void cChunkDesc::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta)
-{
- m_BlockArea.GetRelBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
-}
-
-
-
-
-
-void cChunkDesc::SetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType)
-{
- cChunkDef::SetBlock(m_BlockArea.GetBlockTypes(), a_RelX, a_RelY, a_RelZ, a_BlockType);
-}
-
-
-
-
-
-BLOCKTYPE cChunkDesc::GetBlockType(int a_RelX, int a_RelY, int a_RelZ)
-{
- return cChunkDef::GetBlock(m_BlockArea.GetBlockTypes(), a_RelX, a_RelY, a_RelZ);
-}
-
-
-
-
-
-NIBBLETYPE cChunkDesc::GetBlockMeta(int a_RelX, int a_RelY, int a_RelZ)
-{
- return m_BlockArea.GetRelBlockMeta(a_RelX, a_RelY, a_RelZ);
-}
-
-
-
-
-
-void cChunkDesc::SetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta)
-{
- m_BlockArea.SetRelBlockMeta(a_RelX, a_RelY, a_RelZ, a_BlockMeta);
-}
-
-
-
-
-
-void cChunkDesc::SetBiome(int a_RelX, int a_RelZ, int a_BiomeID)
-{
- cChunkDef::SetBiome(m_BiomeMap, a_RelX, a_RelZ, (EMCSBiome)a_BiomeID);
-}
-
-
-
-
-EMCSBiome cChunkDesc::GetBiome(int a_RelX, int a_RelZ)
-{
- return cChunkDef::GetBiome(m_BiomeMap, a_RelX, a_RelZ);
-}
-
-
-
-
-
-void cChunkDesc::SetHeight(int a_RelX, int a_RelZ, int a_Height)
-{
- cChunkDef::SetHeight(m_HeightMap, a_RelX, a_RelZ, a_Height);
-}
-
-
-
-
-
-int cChunkDesc::GetHeight(int a_RelX, int a_RelZ)
-{
- return cChunkDef::GetHeight(m_HeightMap, a_RelX, a_RelZ);
-}
-
-
-
-
-
-void cChunkDesc::SetUseDefaultBiomes(bool a_bUseDefaultBiomes)
-{
- m_bUseDefaultBiomes = a_bUseDefaultBiomes;
-}
-
-
-
-
-
-bool cChunkDesc::IsUsingDefaultBiomes(void) const
-{
- return m_bUseDefaultBiomes;
-}
-
-
-
-
-
-void cChunkDesc::SetUseDefaultHeight(bool a_bUseDefaultHeight)
-{
- m_bUseDefaultHeight = a_bUseDefaultHeight;
-}
-
-
-
-
-
-bool cChunkDesc::IsUsingDefaultHeight(void) const
-{
- return m_bUseDefaultHeight;
-}
-
-
-
-
-
-void cChunkDesc::SetUseDefaultComposition(bool a_bUseDefaultComposition)
-{
- m_bUseDefaultComposition = a_bUseDefaultComposition;
-}
-
-
-
-
-
-bool cChunkDesc::IsUsingDefaultComposition(void) const
-{
- return m_bUseDefaultComposition;
-}
-
-
-
-
-
-void cChunkDesc::SetUseDefaultStructures(bool a_bUseDefaultStructures)
-{
- m_bUseDefaultStructures = a_bUseDefaultStructures;
-}
-
-
-
-
-
-bool cChunkDesc::IsUsingDefaultStructures(void) const
-{
- return m_bUseDefaultStructures;
-}
-
-
-
-
-
-void cChunkDesc::SetUseDefaultFinish(bool a_bUseDefaultFinish)
-{
- m_bUseDefaultFinish = a_bUseDefaultFinish;
-}
-
-
-
-
-
-bool cChunkDesc::IsUsingDefaultFinish(void) const
-{
- return m_bUseDefaultFinish;
-}
-
-
-
-
-void cChunkDesc::WriteBlockArea(const cBlockArea & a_BlockArea, int a_RelX, int a_RelY, int a_RelZ, cBlockArea::eMergeStrategy a_MergeStrategy)
-{
- m_BlockArea.Merge(a_BlockArea, a_RelX, a_RelY, a_RelZ, a_MergeStrategy);
-}
-
-
-
-
-
-void cChunkDesc::ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ)
-{
- // Normalize the coords:
- if (a_MinRelX > a_MaxRelX)
- {
- std::swap(a_MinRelX, a_MaxRelX);
- }
- if (a_MinRelY > a_MaxRelY)
- {
- std::swap(a_MinRelY, a_MaxRelY);
- }
- if (a_MinRelZ > a_MaxRelZ)
- {
- std::swap(a_MinRelZ, a_MaxRelZ);
- }
-
- // Include the Max coords:
- a_MaxRelX += 1;
- a_MaxRelY += 1;
- a_MaxRelZ += 1;
-
- // Check coords validity:
- if (a_MinRelX < 0)
- {
- LOGWARNING("%s: MinRelX less than zero, adjusting to zero", __FUNCTION__);
- a_MinRelX = 0;
- }
- else if (a_MinRelX >= cChunkDef::Width)
- {
- LOGWARNING("%s: MinRelX more than chunk width, adjusting to chunk width", __FUNCTION__);
- a_MinRelX = cChunkDef::Width - 1;
- }
- if (a_MaxRelX < 0)
- {
- LOGWARNING("%s: MaxRelX less than zero, adjusting to zero", __FUNCTION__);
- a_MaxRelX = 0;
- }
- else if (a_MinRelX >= cChunkDef::Width)
- {
- LOGWARNING("%s: MaxRelX more than chunk width, adjusting to chunk width", __FUNCTION__);
- a_MaxRelX = cChunkDef::Width - 1;
- }
-
- if (a_MinRelY < 0)
- {
- LOGWARNING("%s: MinRelY less than zero, adjusting to zero", __FUNCTION__);
- a_MinRelY = 0;
- }
- else if (a_MinRelY >= cChunkDef::Height)
- {
- LOGWARNING("%s: MinRelY more than chunk height, adjusting to chunk height", __FUNCTION__);
- a_MinRelY = cChunkDef::Height - 1;
- }
- if (a_MaxRelY < 0)
- {
- LOGWARNING("%s: MaxRelY less than zero, adjusting to zero", __FUNCTION__);
- a_MaxRelY = 0;
- }
- else if (a_MinRelY >= cChunkDef::Height)
- {
- LOGWARNING("%s: MaxRelY more than chunk height, adjusting to chunk height", __FUNCTION__);
- a_MaxRelY = cChunkDef::Height - 1;
- }
-
- if (a_MinRelZ < 0)
- {
- LOGWARNING("%s: MinRelZ less than zero, adjusting to zero", __FUNCTION__);
- a_MinRelZ = 0;
- }
- else if (a_MinRelZ >= cChunkDef::Width)
- {
- LOGWARNING("%s: MinRelZ more than chunk width, adjusting to chunk width", __FUNCTION__);
- a_MinRelZ = cChunkDef::Width - 1;
- }
- if (a_MaxRelZ < 0)
- {
- LOGWARNING("%s: MaxRelZ less than zero, adjusting to zero", __FUNCTION__);
- a_MaxRelZ = 0;
- }
- else if (a_MinRelZ >= cChunkDef::Width)
- {
- LOGWARNING("%s: MaxRelZ more than chunk width, adjusting to chunk width", __FUNCTION__);
- a_MaxRelZ = cChunkDef::Width - 1;
- }
-
- // Prepare the block area:
- int SizeX = a_MaxRelX - a_MinRelX;
- int SizeY = a_MaxRelY - a_MinRelY;
- int SizeZ = a_MaxRelZ - a_MinRelZ;
- a_Dest.Clear();
- a_Dest.m_OriginX = m_ChunkX * cChunkDef::Width + a_MinRelX;
- a_Dest.m_OriginY = a_MinRelY;
- a_Dest.m_OriginZ = m_ChunkZ * cChunkDef::Width + a_MinRelZ;
- a_Dest.SetSize(SizeX, SizeY, SizeZ, cBlockArea::baTypes | cBlockArea::baMetas);
-
- for (int y = 0; y < SizeY; y++)
- {
- int CDY = a_MinRelY + y;
- for (int z = 0; z < SizeZ; z++)
- {
- int CDZ = a_MinRelZ + z;
- for (int x = 0; x < SizeX; x++)
- {
- int CDX = a_MinRelX + x;
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
- GetBlockTypeMeta(CDX, CDY, CDZ, BlockType, BlockMeta);
- a_Dest.SetRelBlockTypeMeta(x, y, z, BlockType, BlockMeta);
- } // for x
- } // for z
- } // for y
-}
-
-
-
-
-
-HEIGHTTYPE cChunkDesc::GetMaxHeight(void) const
-{
- HEIGHTTYPE MaxHeight = m_HeightMap[0];
- for (int i = 1; i < ARRAYCOUNT(m_HeightMap); i++)
- {
- if (m_HeightMap[i] > MaxHeight)
- {
- MaxHeight = m_HeightMap[i];
- }
- }
- return MaxHeight;
-}
-
-
-
-
-
-void cChunkDesc::FillRelCuboid(
- int a_MinX, int a_MaxX,
- int a_MinY, int a_MaxY,
- int a_MinZ, int a_MaxZ,
- BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
-)
-{
- int MinX = std::max(a_MinX, 0);
- int MinY = std::max(a_MinY, 0);
- int MinZ = std::max(a_MinZ, 0);
- int MaxX = std::min(a_MaxX, cChunkDef::Width - 1);
- int MaxY = std::min(a_MaxY, cChunkDef::Height - 1);
- int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1);
-
- for (int y = MinY; y <= MaxY; y++)
- {
- for (int z = MinZ; z <= MaxZ; z++)
- {
- for (int x = MinX; x <= MaxX; x++)
- {
- SetBlockTypeMeta(x, y, z, a_BlockType, a_BlockMeta);
- }
- } // for z
- } // for y
-}
-
-
-
-
-
-void cChunkDesc::ReplaceRelCuboid(
- int a_MinX, int a_MaxX,
- int a_MinY, int a_MaxY,
- int a_MinZ, int a_MaxZ,
- BLOCKTYPE a_SrcType, NIBBLETYPE a_SrcMeta,
- BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta
-)
-{
- int MinX = std::max(a_MinX, 0);
- int MinY = std::max(a_MinY, 0);
- int MinZ = std::max(a_MinZ, 0);
- int MaxX = std::min(a_MaxX, cChunkDef::Width - 1);
- int MaxY = std::min(a_MaxY, cChunkDef::Height - 1);
- int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1);
-
- for (int y = MinY; y <= MaxY; y++)
- {
- for (int z = MinZ; z <= MaxZ; z++)
- {
- for (int x = MinX; x <= MaxX; x++)
- {
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
- GetBlockTypeMeta(x, y, z, BlockType, BlockMeta);
- if ((BlockType == a_SrcType) && (BlockMeta == a_SrcMeta))
- {
- SetBlockTypeMeta(x, y, z, a_DstType, a_DstMeta);
- }
- }
- } // for z
- } // for y
-}
-
-
-
-
-
-void cChunkDesc::FloorRelCuboid(
- int a_MinX, int a_MaxX,
- int a_MinY, int a_MaxY,
- int a_MinZ, int a_MaxZ,
- BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta
-)
-{
- int MinX = std::max(a_MinX, 0);
- int MinY = std::max(a_MinY, 0);
- int MinZ = std::max(a_MinZ, 0);
- int MaxX = std::min(a_MaxX, cChunkDef::Width - 1);
- int MaxY = std::min(a_MaxY, cChunkDef::Height - 1);
- int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1);
-
- for (int y = MinY; y <= MaxY; y++)
- {
- for (int z = MinZ; z <= MaxZ; z++)
- {
- for (int x = MinX; x <= MaxX; x++)
- {
- switch (GetBlockType(x, y, z))
- {
- case E_BLOCK_AIR:
- case E_BLOCK_WATER:
- case E_BLOCK_STATIONARY_WATER:
- {
- SetBlockTypeMeta(x, y, z, a_DstType, a_DstMeta);
- break;
- }
- } // switch (GetBlockType)
- } // for x
- } // for z
- } // for y
-}
-
-
-
-
-
-void cChunkDesc::RandomFillRelCuboid(
- int a_MinX, int a_MaxX,
- int a_MinY, int a_MaxY,
- int a_MinZ, int a_MaxZ,
- BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
- int a_RandomSeed, int a_ChanceOutOf10k
-)
-{
- cNoise Noise(a_RandomSeed);
- int MinX = std::max(a_MinX, 0);
- int MinY = std::max(a_MinY, 0);
- int MinZ = std::max(a_MinZ, 0);
- int MaxX = std::min(a_MaxX, cChunkDef::Width - 1);
- int MaxY = std::min(a_MaxY, cChunkDef::Height - 1);
- int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1);
-
- for (int y = MinY; y <= MaxY; y++)
- {
- for (int z = MinZ; z <= MaxZ; z++)
- {
- for (int x = MinX; x <= MaxX; x++)
- {
- int rnd = (Noise.IntNoise3DInt(x, y, z) / 7) % 10000;
- if (rnd <= a_ChanceOutOf10k)
- {
- SetBlockTypeMeta(x, y, z, a_BlockType, a_BlockMeta);
- }
- }
- } // for z
- } // for y
-}
-
-
-
-
-
-void cChunkDesc::AddBlockEntity(cBlockEntity * a_BlockEntity)
-{
- m_BlockEntities.push_back(a_BlockEntity);
-}
-
-
-
-
-
-void cChunkDesc::CompressBlockMetas(cChunkDef::BlockNibbles & a_DestMetas)
-{
- const NIBBLETYPE * AreaMetas = m_BlockArea.GetBlockMetas();
- for (int i = 0; i < ARRAYCOUNT(a_DestMetas); i++)
- {
- a_DestMetas[i] = AreaMetas[2 * i] | (AreaMetas[2 * i + 1] << 4);
- }
-}
-
-
-
-
-
-#ifdef _DEBUG
-
-void cChunkDesc::VerifyHeightmap(void)
-{
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int y = cChunkDef::Height - 1; y > 0; y--)
- {
- BLOCKTYPE BlockType = GetBlockType(x, y, z);
- if (BlockType != E_BLOCK_AIR)
- {
- int Height = GetHeight(x, z);
- ASSERT(Height == y);
- break;
- }
- } // for y
- } // for z
- } // for x
-}
-
-#endif // _DEBUG
-
-
-
-
-
+
+// ChunkDesc.cpp
+
+// Implements the cChunkDesc class representing the chunk description used while generating a chunk. This class is also exported to Lua for HOOK_CHUNK_GENERATING.
+
+#include "Globals.h"
+#include "ChunkDesc.h"
+#include "../BlockArea.h"
+#include "../Cuboid.h"
+#include "../Noise.h"
+
+
+
+
+
+cChunkDesc::cChunkDesc(int a_ChunkX, int a_ChunkZ) :
+ m_ChunkX(a_ChunkX),
+ m_ChunkZ(a_ChunkZ),
+ m_bUseDefaultBiomes(true),
+ m_bUseDefaultHeight(true),
+ m_bUseDefaultComposition(true),
+ m_bUseDefaultStructures(true),
+ m_bUseDefaultFinish(true)
+{
+ m_BlockArea.Create(cChunkDef::Width, cChunkDef::Height, cChunkDef::Width);
+ /*
+ memset(m_BlockTypes, 0, sizeof(cChunkDef::BlockTypes));
+ memset(m_BlockMeta, 0, sizeof(cChunkDef::BlockNibbles));
+ */
+ memset(m_BiomeMap, 0, sizeof(cChunkDef::BiomeMap));
+ memset(m_HeightMap, 0, sizeof(cChunkDef::HeightMap));
+}
+
+
+
+
+
+cChunkDesc::~cChunkDesc()
+{
+ // Nothing needed yet
+}
+
+
+
+
+
+void cChunkDesc::SetChunkCoords(int a_ChunkX, int a_ChunkZ)
+{
+ m_ChunkX = a_ChunkX;
+ m_ChunkZ = a_ChunkZ;
+}
+
+
+
+
+
+void cChunkDesc::FillBlocks(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ m_BlockArea.Fill(cBlockArea::baTypes | cBlockArea::baMetas, a_BlockType, a_BlockMeta);
+}
+
+
+
+
+
+void cChunkDesc::SetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ m_BlockArea.SetRelBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
+}
+
+
+
+
+
+void cChunkDesc::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta)
+{
+ m_BlockArea.GetRelBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
+}
+
+
+
+
+
+void cChunkDesc::SetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType)
+{
+ cChunkDef::SetBlock(m_BlockArea.GetBlockTypes(), a_RelX, a_RelY, a_RelZ, a_BlockType);
+}
+
+
+
+
+
+BLOCKTYPE cChunkDesc::GetBlockType(int a_RelX, int a_RelY, int a_RelZ)
+{
+ return cChunkDef::GetBlock(m_BlockArea.GetBlockTypes(), a_RelX, a_RelY, a_RelZ);
+}
+
+
+
+
+
+NIBBLETYPE cChunkDesc::GetBlockMeta(int a_RelX, int a_RelY, int a_RelZ)
+{
+ return m_BlockArea.GetRelBlockMeta(a_RelX, a_RelY, a_RelZ);
+}
+
+
+
+
+
+void cChunkDesc::SetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta)
+{
+ m_BlockArea.SetRelBlockMeta(a_RelX, a_RelY, a_RelZ, a_BlockMeta);
+}
+
+
+
+
+
+void cChunkDesc::SetBiome(int a_RelX, int a_RelZ, int a_BiomeID)
+{
+ cChunkDef::SetBiome(m_BiomeMap, a_RelX, a_RelZ, (EMCSBiome)a_BiomeID);
+}
+
+
+
+
+EMCSBiome cChunkDesc::GetBiome(int a_RelX, int a_RelZ)
+{
+ return cChunkDef::GetBiome(m_BiomeMap, a_RelX, a_RelZ);
+}
+
+
+
+
+
+void cChunkDesc::SetHeight(int a_RelX, int a_RelZ, int a_Height)
+{
+ cChunkDef::SetHeight(m_HeightMap, a_RelX, a_RelZ, a_Height);
+}
+
+
+
+
+
+int cChunkDesc::GetHeight(int a_RelX, int a_RelZ)
+{
+ return cChunkDef::GetHeight(m_HeightMap, a_RelX, a_RelZ);
+}
+
+
+
+
+
+void cChunkDesc::SetUseDefaultBiomes(bool a_bUseDefaultBiomes)
+{
+ m_bUseDefaultBiomes = a_bUseDefaultBiomes;
+}
+
+
+
+
+
+bool cChunkDesc::IsUsingDefaultBiomes(void) const
+{
+ return m_bUseDefaultBiomes;
+}
+
+
+
+
+
+void cChunkDesc::SetUseDefaultHeight(bool a_bUseDefaultHeight)
+{
+ m_bUseDefaultHeight = a_bUseDefaultHeight;
+}
+
+
+
+
+
+bool cChunkDesc::IsUsingDefaultHeight(void) const
+{
+ return m_bUseDefaultHeight;
+}
+
+
+
+
+
+void cChunkDesc::SetUseDefaultComposition(bool a_bUseDefaultComposition)
+{
+ m_bUseDefaultComposition = a_bUseDefaultComposition;
+}
+
+
+
+
+
+bool cChunkDesc::IsUsingDefaultComposition(void) const
+{
+ return m_bUseDefaultComposition;
+}
+
+
+
+
+
+void cChunkDesc::SetUseDefaultStructures(bool a_bUseDefaultStructures)
+{
+ m_bUseDefaultStructures = a_bUseDefaultStructures;
+}
+
+
+
+
+
+bool cChunkDesc::IsUsingDefaultStructures(void) const
+{
+ return m_bUseDefaultStructures;
+}
+
+
+
+
+
+void cChunkDesc::SetUseDefaultFinish(bool a_bUseDefaultFinish)
+{
+ m_bUseDefaultFinish = a_bUseDefaultFinish;
+}
+
+
+
+
+
+bool cChunkDesc::IsUsingDefaultFinish(void) const
+{
+ return m_bUseDefaultFinish;
+}
+
+
+
+
+void cChunkDesc::WriteBlockArea(const cBlockArea & a_BlockArea, int a_RelX, int a_RelY, int a_RelZ, cBlockArea::eMergeStrategy a_MergeStrategy)
+{
+ m_BlockArea.Merge(a_BlockArea, a_RelX, a_RelY, a_RelZ, a_MergeStrategy);
+}
+
+
+
+
+
+void cChunkDesc::ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ)
+{
+ // Normalize the coords:
+ if (a_MinRelX > a_MaxRelX)
+ {
+ std::swap(a_MinRelX, a_MaxRelX);
+ }
+ if (a_MinRelY > a_MaxRelY)
+ {
+ std::swap(a_MinRelY, a_MaxRelY);
+ }
+ if (a_MinRelZ > a_MaxRelZ)
+ {
+ std::swap(a_MinRelZ, a_MaxRelZ);
+ }
+
+ // Include the Max coords:
+ a_MaxRelX += 1;
+ a_MaxRelY += 1;
+ a_MaxRelZ += 1;
+
+ // Check coords validity:
+ if (a_MinRelX < 0)
+ {
+ LOGWARNING("%s: MinRelX less than zero, adjusting to zero", __FUNCTION__);
+ a_MinRelX = 0;
+ }
+ else if (a_MinRelX >= cChunkDef::Width)
+ {
+ LOGWARNING("%s: MinRelX more than chunk width, adjusting to chunk width", __FUNCTION__);
+ a_MinRelX = cChunkDef::Width - 1;
+ }
+ if (a_MaxRelX < 0)
+ {
+ LOGWARNING("%s: MaxRelX less than zero, adjusting to zero", __FUNCTION__);
+ a_MaxRelX = 0;
+ }
+ else if (a_MinRelX >= cChunkDef::Width)
+ {
+ LOGWARNING("%s: MaxRelX more than chunk width, adjusting to chunk width", __FUNCTION__);
+ a_MaxRelX = cChunkDef::Width - 1;
+ }
+
+ if (a_MinRelY < 0)
+ {
+ LOGWARNING("%s: MinRelY less than zero, adjusting to zero", __FUNCTION__);
+ a_MinRelY = 0;
+ }
+ else if (a_MinRelY >= cChunkDef::Height)
+ {
+ LOGWARNING("%s: MinRelY more than chunk height, adjusting to chunk height", __FUNCTION__);
+ a_MinRelY = cChunkDef::Height - 1;
+ }
+ if (a_MaxRelY < 0)
+ {
+ LOGWARNING("%s: MaxRelY less than zero, adjusting to zero", __FUNCTION__);
+ a_MaxRelY = 0;
+ }
+ else if (a_MinRelY >= cChunkDef::Height)
+ {
+ LOGWARNING("%s: MaxRelY more than chunk height, adjusting to chunk height", __FUNCTION__);
+ a_MaxRelY = cChunkDef::Height - 1;
+ }
+
+ if (a_MinRelZ < 0)
+ {
+ LOGWARNING("%s: MinRelZ less than zero, adjusting to zero", __FUNCTION__);
+ a_MinRelZ = 0;
+ }
+ else if (a_MinRelZ >= cChunkDef::Width)
+ {
+ LOGWARNING("%s: MinRelZ more than chunk width, adjusting to chunk width", __FUNCTION__);
+ a_MinRelZ = cChunkDef::Width - 1;
+ }
+ if (a_MaxRelZ < 0)
+ {
+ LOGWARNING("%s: MaxRelZ less than zero, adjusting to zero", __FUNCTION__);
+ a_MaxRelZ = 0;
+ }
+ else if (a_MinRelZ >= cChunkDef::Width)
+ {
+ LOGWARNING("%s: MaxRelZ more than chunk width, adjusting to chunk width", __FUNCTION__);
+ a_MaxRelZ = cChunkDef::Width - 1;
+ }
+
+ // Prepare the block area:
+ int SizeX = a_MaxRelX - a_MinRelX;
+ int SizeY = a_MaxRelY - a_MinRelY;
+ int SizeZ = a_MaxRelZ - a_MinRelZ;
+ a_Dest.Clear();
+ a_Dest.m_OriginX = m_ChunkX * cChunkDef::Width + a_MinRelX;
+ a_Dest.m_OriginY = a_MinRelY;
+ a_Dest.m_OriginZ = m_ChunkZ * cChunkDef::Width + a_MinRelZ;
+ a_Dest.SetSize(SizeX, SizeY, SizeZ, cBlockArea::baTypes | cBlockArea::baMetas);
+
+ for (int y = 0; y < SizeY; y++)
+ {
+ int CDY = a_MinRelY + y;
+ for (int z = 0; z < SizeZ; z++)
+ {
+ int CDZ = a_MinRelZ + z;
+ for (int x = 0; x < SizeX; x++)
+ {
+ int CDX = a_MinRelX + x;
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ GetBlockTypeMeta(CDX, CDY, CDZ, BlockType, BlockMeta);
+ a_Dest.SetRelBlockTypeMeta(x, y, z, BlockType, BlockMeta);
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+HEIGHTTYPE cChunkDesc::GetMaxHeight(void) const
+{
+ HEIGHTTYPE MaxHeight = m_HeightMap[0];
+ for (int i = 1; i < ARRAYCOUNT(m_HeightMap); i++)
+ {
+ if (m_HeightMap[i] > MaxHeight)
+ {
+ MaxHeight = m_HeightMap[i];
+ }
+ }
+ return MaxHeight;
+}
+
+
+
+
+
+void cChunkDesc::FillRelCuboid(
+ int a_MinX, int a_MaxX,
+ int a_MinY, int a_MaxY,
+ int a_MinZ, int a_MaxZ,
+ BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
+)
+{
+ int MinX = std::max(a_MinX, 0);
+ int MinY = std::max(a_MinY, 0);
+ int MinZ = std::max(a_MinZ, 0);
+ int MaxX = std::min(a_MaxX, cChunkDef::Width - 1);
+ int MaxY = std::min(a_MaxY, cChunkDef::Height - 1);
+ int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1);
+
+ for (int y = MinY; y <= MaxY; y++)
+ {
+ for (int z = MinZ; z <= MaxZ; z++)
+ {
+ for (int x = MinX; x <= MaxX; x++)
+ {
+ SetBlockTypeMeta(x, y, z, a_BlockType, a_BlockMeta);
+ }
+ } // for z
+ } // for y
+}
+
+
+
+
+
+void cChunkDesc::ReplaceRelCuboid(
+ int a_MinX, int a_MaxX,
+ int a_MinY, int a_MaxY,
+ int a_MinZ, int a_MaxZ,
+ BLOCKTYPE a_SrcType, NIBBLETYPE a_SrcMeta,
+ BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta
+)
+{
+ int MinX = std::max(a_MinX, 0);
+ int MinY = std::max(a_MinY, 0);
+ int MinZ = std::max(a_MinZ, 0);
+ int MaxX = std::min(a_MaxX, cChunkDef::Width - 1);
+ int MaxY = std::min(a_MaxY, cChunkDef::Height - 1);
+ int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1);
+
+ for (int y = MinY; y <= MaxY; y++)
+ {
+ for (int z = MinZ; z <= MaxZ; z++)
+ {
+ for (int x = MinX; x <= MaxX; x++)
+ {
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ GetBlockTypeMeta(x, y, z, BlockType, BlockMeta);
+ if ((BlockType == a_SrcType) && (BlockMeta == a_SrcMeta))
+ {
+ SetBlockTypeMeta(x, y, z, a_DstType, a_DstMeta);
+ }
+ }
+ } // for z
+ } // for y
+}
+
+
+
+
+
+void cChunkDesc::FloorRelCuboid(
+ int a_MinX, int a_MaxX,
+ int a_MinY, int a_MaxY,
+ int a_MinZ, int a_MaxZ,
+ BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta
+)
+{
+ int MinX = std::max(a_MinX, 0);
+ int MinY = std::max(a_MinY, 0);
+ int MinZ = std::max(a_MinZ, 0);
+ int MaxX = std::min(a_MaxX, cChunkDef::Width - 1);
+ int MaxY = std::min(a_MaxY, cChunkDef::Height - 1);
+ int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1);
+
+ for (int y = MinY; y <= MaxY; y++)
+ {
+ for (int z = MinZ; z <= MaxZ; z++)
+ {
+ for (int x = MinX; x <= MaxX; x++)
+ {
+ switch (GetBlockType(x, y, z))
+ {
+ case E_BLOCK_AIR:
+ case E_BLOCK_WATER:
+ case E_BLOCK_STATIONARY_WATER:
+ {
+ SetBlockTypeMeta(x, y, z, a_DstType, a_DstMeta);
+ break;
+ }
+ } // switch (GetBlockType)
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+void cChunkDesc::RandomFillRelCuboid(
+ int a_MinX, int a_MaxX,
+ int a_MinY, int a_MaxY,
+ int a_MinZ, int a_MaxZ,
+ BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
+ int a_RandomSeed, int a_ChanceOutOf10k
+)
+{
+ cNoise Noise(a_RandomSeed);
+ int MinX = std::max(a_MinX, 0);
+ int MinY = std::max(a_MinY, 0);
+ int MinZ = std::max(a_MinZ, 0);
+ int MaxX = std::min(a_MaxX, cChunkDef::Width - 1);
+ int MaxY = std::min(a_MaxY, cChunkDef::Height - 1);
+ int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1);
+
+ for (int y = MinY; y <= MaxY; y++)
+ {
+ for (int z = MinZ; z <= MaxZ; z++)
+ {
+ for (int x = MinX; x <= MaxX; x++)
+ {
+ int rnd = (Noise.IntNoise3DInt(x, y, z) / 7) % 10000;
+ if (rnd <= a_ChanceOutOf10k)
+ {
+ SetBlockTypeMeta(x, y, z, a_BlockType, a_BlockMeta);
+ }
+ }
+ } // for z
+ } // for y
+}
+
+
+
+
+
+void cChunkDesc::AddBlockEntity(cBlockEntity * a_BlockEntity)
+{
+ m_BlockEntities.push_back(a_BlockEntity);
+}
+
+
+
+
+
+void cChunkDesc::CompressBlockMetas(cChunkDef::BlockNibbles & a_DestMetas)
+{
+ const NIBBLETYPE * AreaMetas = m_BlockArea.GetBlockMetas();
+ for (int i = 0; i < ARRAYCOUNT(a_DestMetas); i++)
+ {
+ a_DestMetas[i] = AreaMetas[2 * i] | (AreaMetas[2 * i + 1] << 4);
+ }
+}
+
+
+
+
+
+#ifdef _DEBUG
+
+void cChunkDesc::VerifyHeightmap(void)
+{
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int y = cChunkDef::Height - 1; y > 0; y--)
+ {
+ BLOCKTYPE BlockType = GetBlockType(x, y, z);
+ if (BlockType != E_BLOCK_AIR)
+ {
+ int Height = GetHeight(x, z);
+ ASSERT(Height == y);
+ break;
+ }
+ } // for y
+ } // for z
+ } // for x
+}
+
+#endif // _DEBUG
+
+
+
+
+
diff --git a/source/Generating/ComposableGenerator.cpp b/source/Generating/ComposableGenerator.cpp
index c038712aa..fb31ec7b2 100644
--- a/source/Generating/ComposableGenerator.cpp
+++ b/source/Generating/ComposableGenerator.cpp
@@ -1,524 +1,524 @@
-
-// ComposableGenerator.cpp
-
-// Implements the cComposableGenerator class representing the chunk generator that takes the composition approach to generating chunks
-
-#include "Globals.h"
-
-#include "ComposableGenerator.h"
-#include "../World.h"
-#include "../../iniFile/iniFile.h"
-#include "../Root.h"
-
-// Individual composed algorithms:
-#include "BioGen.h"
-#include "HeiGen.h"
-#include "CompoGen.h"
-#include "StructGen.h"
-#include "FinishGen.h"
-
-#include "Caves.h"
-#include "DistortedHeightmap.h"
-#include "EndGen.h"
-#include "MineShafts.h"
-#include "Noise3DGenerator.h"
-#include "Ravines.h"
-
-
-
-
-
-
-
-
-
-
-cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) :
- super(a_ChunkGenerator),
- m_BiomeGen(NULL),
- m_HeightGen(NULL),
- m_CompositionGen(NULL),
- m_UnderlyingBiomeGen(NULL),
- m_UnderlyingHeightGen(NULL),
- m_UnderlyingCompositionGen(NULL)
-{
-}
-
-
-
-
-
-cComposableGenerator::~cComposableGenerator()
-{
- // Delete the generating composition:
- for (cFinishGenList::const_iterator itr = m_FinishGens.begin(); itr != m_FinishGens.end(); ++itr)
- {
- delete *itr;
- }
- m_FinishGens.clear();
- for (cStructureGenList::const_iterator itr = m_StructureGens.begin(); itr != m_StructureGens.end(); ++itr)
- {
- delete *itr;
- }
- m_StructureGens.clear();
-
- delete m_CompositionGen;
- m_CompositionGen = NULL;
- delete m_HeightGen;
- m_HeightGen = NULL;
- delete m_BiomeGen;
- m_BiomeGen = NULL;
- delete m_UnderlyingCompositionGen;
- m_UnderlyingCompositionGen = NULL;
- delete m_UnderlyingHeightGen;
- m_UnderlyingHeightGen = NULL;
- delete m_UnderlyingBiomeGen;
- m_UnderlyingBiomeGen = NULL;
-}
-
-
-
-
-
-void cComposableGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile)
-{
- super::Initialize(a_World, a_IniFile);
-
- InitBiomeGen(a_IniFile);
- InitHeightGen(a_IniFile);
- InitCompositionGen(a_IniFile);
- InitStructureGens(a_IniFile);
- InitFinishGens(a_IniFile);
-}
-
-
-
-
-
-void cComposableGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
-{
- if (m_BiomeGen != NULL) // Quick fix for generator deinitializing before the world storage finishes loading
- {
- m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap);
- }
-}
-
-
-
-
-
-void cComposableGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc)
-{
- if (a_ChunkDesc.IsUsingDefaultBiomes())
- {
- m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetBiomeMap());
- }
-
- if (a_ChunkDesc.IsUsingDefaultHeight())
- {
- m_HeightGen->GenHeightMap(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetHeightMap());
- }
-
- if (a_ChunkDesc.IsUsingDefaultComposition())
- {
- m_CompositionGen->ComposeTerrain(a_ChunkDesc);
- }
-
- if (a_ChunkDesc.IsUsingDefaultStructures())
- {
- for (cStructureGenList::iterator itr = m_StructureGens.begin(); itr != m_StructureGens.end(); ++itr)
- {
- (*itr)->GenStructures(a_ChunkDesc);
- } // for itr - m_StructureGens[]
- }
-
- if (a_ChunkDesc.IsUsingDefaultFinish())
- {
- for (cFinishGenList::iterator itr = m_FinishGens.begin(); itr != m_FinishGens.end(); ++itr)
- {
- (*itr)->GenFinish(a_ChunkDesc);
- } // for itr - m_FinishGens[]
- }
-}
-
-
-
-
-
-void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile)
-{
- AString BiomeGenName = a_IniFile.GetValueSet("Generator", "BiomeGen", "");
- if (BiomeGenName.empty())
- {
- LOGWARN("[Generator]::BiomeGen value not found in world.ini, using \"MultiStepMap\".");
- BiomeGenName = "MultiStepMap";
- }
-
- int Seed = m_ChunkGenerator.GetSeed();
- bool CacheOffByDefault = false;
- if (NoCaseCompare(BiomeGenName, "constant") == 0)
- {
- m_BiomeGen = new cBioGenConstant;
- CacheOffByDefault = true; // we're generating faster than a cache would retrieve data :)
- }
- else if (NoCaseCompare(BiomeGenName, "checkerboard") == 0)
- {
- m_BiomeGen = new cBioGenCheckerboard;
- CacheOffByDefault = true; // we're (probably) generating faster than a cache would retrieve data
- }
- else if (NoCaseCompare(BiomeGenName, "voronoi") == 0)
- {
- m_BiomeGen = new cBioGenVoronoi(Seed);
- }
- else if (NoCaseCompare(BiomeGenName, "distortedvoronoi") == 0)
- {
- m_BiomeGen = new cBioGenDistortedVoronoi(Seed);
- }
- else
- {
- if (NoCaseCompare(BiomeGenName, "multistepmap") != 0)
- {
- LOGWARNING("Unknown BiomeGen \"%s\", using \"MultiStepMap\" instead.", BiomeGenName.c_str());
- }
- m_BiomeGen = new cBioGenMultiStepMap(Seed);
-
- /*
- // Performance-testing:
- LOGINFO("Measuring performance of cBioGenMultiStepMap...");
- clock_t BeginTick = clock();
- for (int x = 0; x < 5000; x++)
- {
- cChunkDef::BiomeMap Biomes;
- m_BiomeGen->GenBiomes(x * 5, x * 5, Biomes);
- }
- clock_t Duration = clock() - BeginTick;
- LOGINFO("cBioGenMultiStepMap for 5000 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
- //*/
- }
-
- // Add a cache, if requested:
- int CacheSize = a_IniFile.GetValueSetI("Generator", "BiomeGenCacheSize", CacheOffByDefault ? 0 : 64);
- if (CacheSize > 0)
- {
- if (CacheSize < 4)
- {
- LOGWARNING("Biomegen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d",
- CacheSize, 4
- );
- CacheSize = 4;
- }
- LOGINFO("Using a cache for biomegen of size %d.", CacheSize);
- m_UnderlyingBiomeGen = m_BiomeGen;
- m_BiomeGen = new cBioGenCache(m_UnderlyingBiomeGen, CacheSize);
- }
- m_BiomeGen->Initialize(a_IniFile);
-}
-
-
-
-
-
-void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile)
-{
- AString HeightGenName = a_IniFile.GetValueSet("Generator", "HeightGen", "");
- if (HeightGenName.empty())
- {
- LOGWARN("[Generator]::HeightGen value not found in world.ini, using \"Biomal\".");
- HeightGenName = "Biomal";
- }
-
- int Seed = m_ChunkGenerator.GetSeed();
- bool CacheOffByDefault = false;
- if (NoCaseCompare(HeightGenName, "flat") == 0)
- {
- int Height = a_IniFile.GetValueSetI("Generator", "FlatHeight", 5);
- m_HeightGen = new cHeiGenFlat(Height);
- CacheOffByDefault = true; // We're generating faster than a cache would retrieve data
- }
- else if (NoCaseCompare(HeightGenName, "classic") == 0)
- {
- // These used to be in terrain.ini, but now they are in world.ini (so that multiple worlds can have different values):
- float HeightFreq1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq1", 0.1);
- float HeightFreq2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq2", 1.0);
- float HeightFreq3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq3", 2.0);
- float HeightAmp1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp1", 1.0);
- float HeightAmp2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp2", 0.5);
- float HeightAmp3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp3", 0.5);
- m_HeightGen = new cHeiGenClassic(Seed, HeightFreq1, HeightAmp1, HeightFreq2, HeightAmp2, HeightFreq3, HeightAmp3);
- }
- else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0)
- {
- m_HeightGen = new cDistortedHeightmap(Seed, *m_BiomeGen);
- ((cDistortedHeightmap *)m_HeightGen)->Initialize(a_IniFile);
- }
- else if (NoCaseCompare(HeightGenName, "End") == 0)
- {
- m_HeightGen = new cEndGen(Seed);
- ((cEndGen *)m_HeightGen)->Initialize(a_IniFile);
- }
- else if (NoCaseCompare(HeightGenName, "Noise3D") == 0)
- {
- m_HeightGen = new cNoise3DComposable(Seed);
- ((cNoise3DComposable *)m_HeightGen)->Initialize(a_IniFile);
- }
- else // "biomal" or <not found>
- {
- if (NoCaseCompare(HeightGenName, "biomal") != 0)
- {
- LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str());
- }
- m_HeightGen = new cHeiGenBiomal(Seed, *m_BiomeGen);
-
- /*
- // Performance-testing:
- LOGINFO("Measuring performance of cHeiGenBiomal...");
- clock_t BeginTick = clock();
- for (int x = 0; x < 500; x++)
- {
- cChunkDef::HeightMap Heights;
- m_HeightGen->GenHeightMap(x * 5, x * 5, Heights);
- }
- clock_t Duration = clock() - BeginTick;
- LOGINFO("HeightGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
- //*/
- }
-
- // Add a cache, if requested:
- int CacheSize = a_IniFile.GetValueSetI("Generator", "HeightGenCacheSize", CacheOffByDefault ? 0 : 64);
- if (CacheSize > 0)
- {
- if (CacheSize < 4)
- {
- LOGWARNING("Heightgen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d",
- CacheSize, 4
- );
- CacheSize = 4;
- }
- LOGINFO("Using a cache for Heightgen of size %d.", CacheSize);
- m_UnderlyingHeightGen = m_HeightGen;
- m_HeightGen = new cHeiGenCache(m_UnderlyingHeightGen, CacheSize);
- }
-}
-
-
-
-
-
-void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile)
-{
- AString CompoGenName = a_IniFile.GetValueSet("Generator", "CompositionGen", "");
- if (CompoGenName.empty())
- {
- LOGWARN("[Generator]::CompositionGen value not found in world.ini, using \"Biomal\".");
- CompoGenName = "Biomal";
- }
- if (NoCaseCompare(CompoGenName, "sameblock") == 0)
- {
- int Block = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "SameBlockType", "stone");
- bool Bedrocked = (a_IniFile.GetValueSetI("Generator", "SameBlockBedrocked", 1) != 0);
- m_CompositionGen = new cCompoGenSameBlock((BLOCKTYPE)Block, Bedrocked);
- }
- else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0)
- {
- m_CompositionGen = new cCompoGenDebugBiomes;
- }
- else if (NoCaseCompare(CompoGenName, "classic") == 0)
- {
- int SeaLevel = a_IniFile.GetValueSetI("Generator", "ClassicSeaLevel", 60);
- int BeachHeight = a_IniFile.GetValueSetI("Generator", "ClassicBeachHeight", 2);
- int BeachDepth = a_IniFile.GetValueSetI("Generator", "ClassicBeachDepth", 4);
- BLOCKTYPE BlockTop = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockTop", "grass");
- BLOCKTYPE BlockMiddle = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockMiddle", "dirt");
- BLOCKTYPE BlockBottom = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockBottom", "stone");
- BLOCKTYPE BlockBeach = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockBeach", "sand");
- BLOCKTYPE BlockBeachBottom = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockBeachBottom", "sandstone");
- BLOCKTYPE BlockSea = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockSea", "stationarywater");
- m_CompositionGen = new cCompoGenClassic(
- SeaLevel, BeachHeight, BeachDepth, BlockTop, BlockMiddle, BlockBottom, BlockBeach,
- BlockBeachBottom, BlockSea
- );
- }
- else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0)
- {
- m_CompositionGen = new cDistortedHeightmap(m_ChunkGenerator.GetSeed(), *m_BiomeGen);
- ((cDistortedHeightmap *)m_CompositionGen)->Initialize(a_IniFile);
- }
- else if (NoCaseCompare(CompoGenName, "end") == 0)
- {
- m_CompositionGen = new cEndGen(m_ChunkGenerator.GetSeed());
- ((cEndGen *)m_CompositionGen)->Initialize(a_IniFile);
- }
- else if (NoCaseCompare(CompoGenName, "nether") == 0)
- {
- m_CompositionGen = new cCompoGenNether(m_ChunkGenerator.GetSeed());
- }
- else if (NoCaseCompare(CompoGenName, "Noise3D") == 0)
- {
- m_CompositionGen = new cNoise3DComposable(m_ChunkGenerator.GetSeed());
- ((cNoise3DComposable *)m_CompositionGen)->Initialize(a_IniFile);
- }
- else
- {
- if (NoCaseCompare(CompoGenName, "biomal") != 0)
- {
- LOGWARN("Unknown CompositionGen \"%s\", using \"biomal\" instead.", CompoGenName.c_str());
- }
- int SeaLevel = a_IniFile.GetValueSetI("Generator", "BiomalSeaLevel", 62);
- int Seed = m_ChunkGenerator.GetSeed();
- m_CompositionGen = new cCompoGenBiomal(Seed, SeaLevel);
-
- /*
- // Performance-testing:
- LOGINFO("Measuring performance of cCompoGenBiomal...");
- clock_t BeginTick = clock();
- for (int x = 0; x < 500; x++)
- {
- cChunkDesc Desc(200 + x * 8, 200 + x * 8);
- m_BiomeGen->GenBiomes(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetBiomeMap());
- m_HeightGen->GenHeightMap(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetHeightMap());
- m_CompositionGen->ComposeTerrain(Desc);
- }
- clock_t Duration = clock() - BeginTick;
- LOGINFO("CompositionGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
- //*/
- }
-
- int CompoGenCacheSize = a_IniFile.GetValueSetI("Generator", "CompositionGenCacheSize", 64);
- if (CompoGenCacheSize > 1)
- {
- m_UnderlyingCompositionGen = m_CompositionGen;
- m_CompositionGen = new cCompoGenCache(m_UnderlyingCompositionGen, 32);
- }
-}
-
-
-
-
-
-void cComposableGenerator::InitStructureGens(cIniFile & a_IniFile)
-{
- AString Structures = a_IniFile.GetValueSet("Generator", "Structures", "Ravines, WormNestCaves, WaterLakes, LavaLakes, OreNests, Trees");
-
- int Seed = m_ChunkGenerator.GetSeed();
- AStringVector Str = StringSplitAndTrim(Structures, ",");
- for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr)
- {
- if (NoCaseCompare(*itr, "DualRidgeCaves") == 0)
- {
- float Threshold = (float)a_IniFile.GetValueSetF("Generator", "DualRidgeCavesThreshold", 0.3);
- m_StructureGens.push_back(new cStructGenDualRidgeCaves(Seed, Threshold));
- }
- else if (NoCaseCompare(*itr, "DirectOverhangs") == 0)
- {
- m_StructureGens.push_back(new cStructGenDirectOverhangs(Seed));
- }
- else if (NoCaseCompare(*itr, "DistortedMembraneOverhangs") == 0)
- {
- m_StructureGens.push_back(new cStructGenDistortedMembraneOverhangs(Seed));
- }
- else if (NoCaseCompare(*itr, "LavaLakes") == 0)
- {
- int Probability = a_IniFile.GetValueSetI("Generator", "LavaLakesProbability", 10);
- m_StructureGens.push_back(new cStructGenLakes(Seed * 5 + 16873, E_BLOCK_STATIONARY_LAVA, *m_HeightGen, Probability));
- }
- else if (NoCaseCompare(*itr, "MarbleCaves") == 0)
- {
- m_StructureGens.push_back(new cStructGenMarbleCaves(Seed));
- }
- else if (NoCaseCompare(*itr, "MineShafts") == 0)
- {
- int GridSize = a_IniFile.GetValueSetI("Generator", "MineShaftsGridSize", 512);
- int MaxSystemSize = a_IniFile.GetValueSetI("Generator", "MineShaftsMaxSystemSize", 160);
- int ChanceCorridor = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceCorridor", 600);
- int ChanceCrossing = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceCrossing", 200);
- int ChanceStaircase = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceStaircase", 200);
- m_StructureGens.push_back(new cStructGenMineShafts(
- Seed, GridSize, MaxSystemSize,
- ChanceCorridor, ChanceCrossing, ChanceStaircase
- ));
- }
- else if (NoCaseCompare(*itr, "OreNests") == 0)
- {
- m_StructureGens.push_back(new cStructGenOreNests(Seed));
- }
- else if (NoCaseCompare(*itr, "Ravines") == 0)
- {
- m_StructureGens.push_back(new cStructGenRavines(Seed, 128));
- }
- else if (NoCaseCompare(*itr, "Trees") == 0)
- {
- m_StructureGens.push_back(new cStructGenTrees(Seed, m_BiomeGen, m_HeightGen, m_CompositionGen));
- }
- else if (NoCaseCompare(*itr, "WaterLakes") == 0)
- {
- int Probability = a_IniFile.GetValueSetI("Generator", "WaterLakesProbability", 25);
- m_StructureGens.push_back(new cStructGenLakes(Seed * 3 + 652, E_BLOCK_STATIONARY_WATER, *m_HeightGen, Probability));
- }
- else if (NoCaseCompare(*itr, "WormNestCaves") == 0)
- {
- m_StructureGens.push_back(new cStructGenWormNestCaves(Seed));
- }
- else
- {
- LOGWARNING("Unknown structure generator: \"%s\". Ignoring.", itr->c_str());
- }
- } // for itr - Str[]
-}
-
-
-
-
-
-void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
-{
- int Seed = m_ChunkGenerator.GetSeed();
- AString Structures = a_IniFile.GetValueSet("Generator", "Finishers", "SprinkleFoliage,Ice,Snow,Lilypads,BottomLava,DeadBushes,PreSimulator");
-
- AStringVector Str = StringSplitAndTrim(Structures, ",");
- for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr)
- {
- // Finishers, alpha-sorted:
- if (NoCaseCompare(*itr, "BottomLava") == 0)
- {
- int DefaultBottomLavaLevel = (m_World->GetDimension() == dimNether) ? 30 : 10;
- int BottomLavaLevel = a_IniFile.GetValueSetI("Generator", "BottomLavaLevel", DefaultBottomLavaLevel);
- m_FinishGens.push_back(new cFinishGenBottomLava(BottomLavaLevel));
- }
- else if (NoCaseCompare(*itr, "DeadBushes") == 0)
- {
- m_FinishGens.push_back(new cFinishGenSingleBiomeSingleTopBlock(Seed, E_BLOCK_DEAD_BUSH, biDesert, 2, E_BLOCK_SAND, E_BLOCK_SAND));
- }
- else if (NoCaseCompare(*itr, "Ice") == 0)
- {
- m_FinishGens.push_back(new cFinishGenIce);
- }
- else if (NoCaseCompare(*itr, "LavaSprings") == 0)
- {
- m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_LAVA, a_IniFile, *m_World));
- }
- else if (NoCaseCompare(*itr, "Lilypads") == 0)
- {
- m_FinishGens.push_back(new cFinishGenSingleBiomeSingleTopBlock(Seed, E_BLOCK_LILY_PAD, biSwampland, 4, E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER));
- }
- else if (NoCaseCompare(*itr, "PreSimulator") == 0)
- {
- m_FinishGens.push_back(new cFinishGenPreSimulator);
- }
- else if (NoCaseCompare(*itr, "Snow") == 0)
- {
- m_FinishGens.push_back(new cFinishGenSnow);
- }
- else if (NoCaseCompare(*itr, "SprinkleFoliage") == 0)
- {
- m_FinishGens.push_back(new cFinishGenSprinkleFoliage(Seed));
- }
- else if (NoCaseCompare(*itr, "WaterSprings") == 0)
- {
- m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_WATER, a_IniFile, *m_World));
- }
- } // for itr - Str[]
-}
-
-
-
-
+
+// ComposableGenerator.cpp
+
+// Implements the cComposableGenerator class representing the chunk generator that takes the composition approach to generating chunks
+
+#include "Globals.h"
+
+#include "ComposableGenerator.h"
+#include "../World.h"
+#include "../../iniFile/iniFile.h"
+#include "../Root.h"
+
+// Individual composed algorithms:
+#include "BioGen.h"
+#include "HeiGen.h"
+#include "CompoGen.h"
+#include "StructGen.h"
+#include "FinishGen.h"
+
+#include "Caves.h"
+#include "DistortedHeightmap.h"
+#include "EndGen.h"
+#include "MineShafts.h"
+#include "Noise3DGenerator.h"
+#include "Ravines.h"
+
+
+
+
+
+
+
+
+
+
+cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) :
+ super(a_ChunkGenerator),
+ m_BiomeGen(NULL),
+ m_HeightGen(NULL),
+ m_CompositionGen(NULL),
+ m_UnderlyingBiomeGen(NULL),
+ m_UnderlyingHeightGen(NULL),
+ m_UnderlyingCompositionGen(NULL)
+{
+}
+
+
+
+
+
+cComposableGenerator::~cComposableGenerator()
+{
+ // Delete the generating composition:
+ for (cFinishGenList::const_iterator itr = m_FinishGens.begin(); itr != m_FinishGens.end(); ++itr)
+ {
+ delete *itr;
+ }
+ m_FinishGens.clear();
+ for (cStructureGenList::const_iterator itr = m_StructureGens.begin(); itr != m_StructureGens.end(); ++itr)
+ {
+ delete *itr;
+ }
+ m_StructureGens.clear();
+
+ delete m_CompositionGen;
+ m_CompositionGen = NULL;
+ delete m_HeightGen;
+ m_HeightGen = NULL;
+ delete m_BiomeGen;
+ m_BiomeGen = NULL;
+ delete m_UnderlyingCompositionGen;
+ m_UnderlyingCompositionGen = NULL;
+ delete m_UnderlyingHeightGen;
+ m_UnderlyingHeightGen = NULL;
+ delete m_UnderlyingBiomeGen;
+ m_UnderlyingBiomeGen = NULL;
+}
+
+
+
+
+
+void cComposableGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile)
+{
+ super::Initialize(a_World, a_IniFile);
+
+ InitBiomeGen(a_IniFile);
+ InitHeightGen(a_IniFile);
+ InitCompositionGen(a_IniFile);
+ InitStructureGens(a_IniFile);
+ InitFinishGens(a_IniFile);
+}
+
+
+
+
+
+void cComposableGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
+{
+ if (m_BiomeGen != NULL) // Quick fix for generator deinitializing before the world storage finishes loading
+ {
+ m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap);
+ }
+}
+
+
+
+
+
+void cComposableGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc)
+{
+ if (a_ChunkDesc.IsUsingDefaultBiomes())
+ {
+ m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetBiomeMap());
+ }
+
+ if (a_ChunkDesc.IsUsingDefaultHeight())
+ {
+ m_HeightGen->GenHeightMap(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetHeightMap());
+ }
+
+ if (a_ChunkDesc.IsUsingDefaultComposition())
+ {
+ m_CompositionGen->ComposeTerrain(a_ChunkDesc);
+ }
+
+ if (a_ChunkDesc.IsUsingDefaultStructures())
+ {
+ for (cStructureGenList::iterator itr = m_StructureGens.begin(); itr != m_StructureGens.end(); ++itr)
+ {
+ (*itr)->GenStructures(a_ChunkDesc);
+ } // for itr - m_StructureGens[]
+ }
+
+ if (a_ChunkDesc.IsUsingDefaultFinish())
+ {
+ for (cFinishGenList::iterator itr = m_FinishGens.begin(); itr != m_FinishGens.end(); ++itr)
+ {
+ (*itr)->GenFinish(a_ChunkDesc);
+ } // for itr - m_FinishGens[]
+ }
+}
+
+
+
+
+
+void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile)
+{
+ AString BiomeGenName = a_IniFile.GetValueSet("Generator", "BiomeGen", "");
+ if (BiomeGenName.empty())
+ {
+ LOGWARN("[Generator]::BiomeGen value not found in world.ini, using \"MultiStepMap\".");
+ BiomeGenName = "MultiStepMap";
+ }
+
+ int Seed = m_ChunkGenerator.GetSeed();
+ bool CacheOffByDefault = false;
+ if (NoCaseCompare(BiomeGenName, "constant") == 0)
+ {
+ m_BiomeGen = new cBioGenConstant;
+ CacheOffByDefault = true; // we're generating faster than a cache would retrieve data :)
+ }
+ else if (NoCaseCompare(BiomeGenName, "checkerboard") == 0)
+ {
+ m_BiomeGen = new cBioGenCheckerboard;
+ CacheOffByDefault = true; // we're (probably) generating faster than a cache would retrieve data
+ }
+ else if (NoCaseCompare(BiomeGenName, "voronoi") == 0)
+ {
+ m_BiomeGen = new cBioGenVoronoi(Seed);
+ }
+ else if (NoCaseCompare(BiomeGenName, "distortedvoronoi") == 0)
+ {
+ m_BiomeGen = new cBioGenDistortedVoronoi(Seed);
+ }
+ else
+ {
+ if (NoCaseCompare(BiomeGenName, "multistepmap") != 0)
+ {
+ LOGWARNING("Unknown BiomeGen \"%s\", using \"MultiStepMap\" instead.", BiomeGenName.c_str());
+ }
+ m_BiomeGen = new cBioGenMultiStepMap(Seed);
+
+ /*
+ // Performance-testing:
+ LOGINFO("Measuring performance of cBioGenMultiStepMap...");
+ clock_t BeginTick = clock();
+ for (int x = 0; x < 5000; x++)
+ {
+ cChunkDef::BiomeMap Biomes;
+ m_BiomeGen->GenBiomes(x * 5, x * 5, Biomes);
+ }
+ clock_t Duration = clock() - BeginTick;
+ LOGINFO("cBioGenMultiStepMap for 5000 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
+ //*/
+ }
+
+ // Add a cache, if requested:
+ int CacheSize = a_IniFile.GetValueSetI("Generator", "BiomeGenCacheSize", CacheOffByDefault ? 0 : 64);
+ if (CacheSize > 0)
+ {
+ if (CacheSize < 4)
+ {
+ LOGWARNING("Biomegen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d",
+ CacheSize, 4
+ );
+ CacheSize = 4;
+ }
+ LOGINFO("Using a cache for biomegen of size %d.", CacheSize);
+ m_UnderlyingBiomeGen = m_BiomeGen;
+ m_BiomeGen = new cBioGenCache(m_UnderlyingBiomeGen, CacheSize);
+ }
+ m_BiomeGen->Initialize(a_IniFile);
+}
+
+
+
+
+
+void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile)
+{
+ AString HeightGenName = a_IniFile.GetValueSet("Generator", "HeightGen", "");
+ if (HeightGenName.empty())
+ {
+ LOGWARN("[Generator]::HeightGen value not found in world.ini, using \"Biomal\".");
+ HeightGenName = "Biomal";
+ }
+
+ int Seed = m_ChunkGenerator.GetSeed();
+ bool CacheOffByDefault = false;
+ if (NoCaseCompare(HeightGenName, "flat") == 0)
+ {
+ int Height = a_IniFile.GetValueSetI("Generator", "FlatHeight", 5);
+ m_HeightGen = new cHeiGenFlat(Height);
+ CacheOffByDefault = true; // We're generating faster than a cache would retrieve data
+ }
+ else if (NoCaseCompare(HeightGenName, "classic") == 0)
+ {
+ // These used to be in terrain.ini, but now they are in world.ini (so that multiple worlds can have different values):
+ float HeightFreq1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq1", 0.1);
+ float HeightFreq2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq2", 1.0);
+ float HeightFreq3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq3", 2.0);
+ float HeightAmp1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp1", 1.0);
+ float HeightAmp2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp2", 0.5);
+ float HeightAmp3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp3", 0.5);
+ m_HeightGen = new cHeiGenClassic(Seed, HeightFreq1, HeightAmp1, HeightFreq2, HeightAmp2, HeightFreq3, HeightAmp3);
+ }
+ else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0)
+ {
+ m_HeightGen = new cDistortedHeightmap(Seed, *m_BiomeGen);
+ ((cDistortedHeightmap *)m_HeightGen)->Initialize(a_IniFile);
+ }
+ else if (NoCaseCompare(HeightGenName, "End") == 0)
+ {
+ m_HeightGen = new cEndGen(Seed);
+ ((cEndGen *)m_HeightGen)->Initialize(a_IniFile);
+ }
+ else if (NoCaseCompare(HeightGenName, "Noise3D") == 0)
+ {
+ m_HeightGen = new cNoise3DComposable(Seed);
+ ((cNoise3DComposable *)m_HeightGen)->Initialize(a_IniFile);
+ }
+ else // "biomal" or <not found>
+ {
+ if (NoCaseCompare(HeightGenName, "biomal") != 0)
+ {
+ LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str());
+ }
+ m_HeightGen = new cHeiGenBiomal(Seed, *m_BiomeGen);
+
+ /*
+ // Performance-testing:
+ LOGINFO("Measuring performance of cHeiGenBiomal...");
+ clock_t BeginTick = clock();
+ for (int x = 0; x < 500; x++)
+ {
+ cChunkDef::HeightMap Heights;
+ m_HeightGen->GenHeightMap(x * 5, x * 5, Heights);
+ }
+ clock_t Duration = clock() - BeginTick;
+ LOGINFO("HeightGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
+ //*/
+ }
+
+ // Add a cache, if requested:
+ int CacheSize = a_IniFile.GetValueSetI("Generator", "HeightGenCacheSize", CacheOffByDefault ? 0 : 64);
+ if (CacheSize > 0)
+ {
+ if (CacheSize < 4)
+ {
+ LOGWARNING("Heightgen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d",
+ CacheSize, 4
+ );
+ CacheSize = 4;
+ }
+ LOGINFO("Using a cache for Heightgen of size %d.", CacheSize);
+ m_UnderlyingHeightGen = m_HeightGen;
+ m_HeightGen = new cHeiGenCache(m_UnderlyingHeightGen, CacheSize);
+ }
+}
+
+
+
+
+
+void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile)
+{
+ AString CompoGenName = a_IniFile.GetValueSet("Generator", "CompositionGen", "");
+ if (CompoGenName.empty())
+ {
+ LOGWARN("[Generator]::CompositionGen value not found in world.ini, using \"Biomal\".");
+ CompoGenName = "Biomal";
+ }
+ if (NoCaseCompare(CompoGenName, "sameblock") == 0)
+ {
+ int Block = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "SameBlockType", "stone");
+ bool Bedrocked = (a_IniFile.GetValueSetI("Generator", "SameBlockBedrocked", 1) != 0);
+ m_CompositionGen = new cCompoGenSameBlock((BLOCKTYPE)Block, Bedrocked);
+ }
+ else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0)
+ {
+ m_CompositionGen = new cCompoGenDebugBiomes;
+ }
+ else if (NoCaseCompare(CompoGenName, "classic") == 0)
+ {
+ int SeaLevel = a_IniFile.GetValueSetI("Generator", "ClassicSeaLevel", 60);
+ int BeachHeight = a_IniFile.GetValueSetI("Generator", "ClassicBeachHeight", 2);
+ int BeachDepth = a_IniFile.GetValueSetI("Generator", "ClassicBeachDepth", 4);
+ BLOCKTYPE BlockTop = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockTop", "grass");
+ BLOCKTYPE BlockMiddle = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockMiddle", "dirt");
+ BLOCKTYPE BlockBottom = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockBottom", "stone");
+ BLOCKTYPE BlockBeach = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockBeach", "sand");
+ BLOCKTYPE BlockBeachBottom = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockBeachBottom", "sandstone");
+ BLOCKTYPE BlockSea = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockSea", "stationarywater");
+ m_CompositionGen = new cCompoGenClassic(
+ SeaLevel, BeachHeight, BeachDepth, BlockTop, BlockMiddle, BlockBottom, BlockBeach,
+ BlockBeachBottom, BlockSea
+ );
+ }
+ else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0)
+ {
+ m_CompositionGen = new cDistortedHeightmap(m_ChunkGenerator.GetSeed(), *m_BiomeGen);
+ ((cDistortedHeightmap *)m_CompositionGen)->Initialize(a_IniFile);
+ }
+ else if (NoCaseCompare(CompoGenName, "end") == 0)
+ {
+ m_CompositionGen = new cEndGen(m_ChunkGenerator.GetSeed());
+ ((cEndGen *)m_CompositionGen)->Initialize(a_IniFile);
+ }
+ else if (NoCaseCompare(CompoGenName, "nether") == 0)
+ {
+ m_CompositionGen = new cCompoGenNether(m_ChunkGenerator.GetSeed());
+ }
+ else if (NoCaseCompare(CompoGenName, "Noise3D") == 0)
+ {
+ m_CompositionGen = new cNoise3DComposable(m_ChunkGenerator.GetSeed());
+ ((cNoise3DComposable *)m_CompositionGen)->Initialize(a_IniFile);
+ }
+ else
+ {
+ if (NoCaseCompare(CompoGenName, "biomal") != 0)
+ {
+ LOGWARN("Unknown CompositionGen \"%s\", using \"biomal\" instead.", CompoGenName.c_str());
+ }
+ int SeaLevel = a_IniFile.GetValueSetI("Generator", "BiomalSeaLevel", 62);
+ int Seed = m_ChunkGenerator.GetSeed();
+ m_CompositionGen = new cCompoGenBiomal(Seed, SeaLevel);
+
+ /*
+ // Performance-testing:
+ LOGINFO("Measuring performance of cCompoGenBiomal...");
+ clock_t BeginTick = clock();
+ for (int x = 0; x < 500; x++)
+ {
+ cChunkDesc Desc(200 + x * 8, 200 + x * 8);
+ m_BiomeGen->GenBiomes(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetBiomeMap());
+ m_HeightGen->GenHeightMap(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetHeightMap());
+ m_CompositionGen->ComposeTerrain(Desc);
+ }
+ clock_t Duration = clock() - BeginTick;
+ LOGINFO("CompositionGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
+ //*/
+ }
+
+ int CompoGenCacheSize = a_IniFile.GetValueSetI("Generator", "CompositionGenCacheSize", 64);
+ if (CompoGenCacheSize > 1)
+ {
+ m_UnderlyingCompositionGen = m_CompositionGen;
+ m_CompositionGen = new cCompoGenCache(m_UnderlyingCompositionGen, 32);
+ }
+}
+
+
+
+
+
+void cComposableGenerator::InitStructureGens(cIniFile & a_IniFile)
+{
+ AString Structures = a_IniFile.GetValueSet("Generator", "Structures", "Ravines, WormNestCaves, WaterLakes, LavaLakes, OreNests, Trees");
+
+ int Seed = m_ChunkGenerator.GetSeed();
+ AStringVector Str = StringSplitAndTrim(Structures, ",");
+ for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr)
+ {
+ if (NoCaseCompare(*itr, "DualRidgeCaves") == 0)
+ {
+ float Threshold = (float)a_IniFile.GetValueSetF("Generator", "DualRidgeCavesThreshold", 0.3);
+ m_StructureGens.push_back(new cStructGenDualRidgeCaves(Seed, Threshold));
+ }
+ else if (NoCaseCompare(*itr, "DirectOverhangs") == 0)
+ {
+ m_StructureGens.push_back(new cStructGenDirectOverhangs(Seed));
+ }
+ else if (NoCaseCompare(*itr, "DistortedMembraneOverhangs") == 0)
+ {
+ m_StructureGens.push_back(new cStructGenDistortedMembraneOverhangs(Seed));
+ }
+ else if (NoCaseCompare(*itr, "LavaLakes") == 0)
+ {
+ int Probability = a_IniFile.GetValueSetI("Generator", "LavaLakesProbability", 10);
+ m_StructureGens.push_back(new cStructGenLakes(Seed * 5 + 16873, E_BLOCK_STATIONARY_LAVA, *m_HeightGen, Probability));
+ }
+ else if (NoCaseCompare(*itr, "MarbleCaves") == 0)
+ {
+ m_StructureGens.push_back(new cStructGenMarbleCaves(Seed));
+ }
+ else if (NoCaseCompare(*itr, "MineShafts") == 0)
+ {
+ int GridSize = a_IniFile.GetValueSetI("Generator", "MineShaftsGridSize", 512);
+ int MaxSystemSize = a_IniFile.GetValueSetI("Generator", "MineShaftsMaxSystemSize", 160);
+ int ChanceCorridor = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceCorridor", 600);
+ int ChanceCrossing = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceCrossing", 200);
+ int ChanceStaircase = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceStaircase", 200);
+ m_StructureGens.push_back(new cStructGenMineShafts(
+ Seed, GridSize, MaxSystemSize,
+ ChanceCorridor, ChanceCrossing, ChanceStaircase
+ ));
+ }
+ else if (NoCaseCompare(*itr, "OreNests") == 0)
+ {
+ m_StructureGens.push_back(new cStructGenOreNests(Seed));
+ }
+ else if (NoCaseCompare(*itr, "Ravines") == 0)
+ {
+ m_StructureGens.push_back(new cStructGenRavines(Seed, 128));
+ }
+ else if (NoCaseCompare(*itr, "Trees") == 0)
+ {
+ m_StructureGens.push_back(new cStructGenTrees(Seed, m_BiomeGen, m_HeightGen, m_CompositionGen));
+ }
+ else if (NoCaseCompare(*itr, "WaterLakes") == 0)
+ {
+ int Probability = a_IniFile.GetValueSetI("Generator", "WaterLakesProbability", 25);
+ m_StructureGens.push_back(new cStructGenLakes(Seed * 3 + 652, E_BLOCK_STATIONARY_WATER, *m_HeightGen, Probability));
+ }
+ else if (NoCaseCompare(*itr, "WormNestCaves") == 0)
+ {
+ m_StructureGens.push_back(new cStructGenWormNestCaves(Seed));
+ }
+ else
+ {
+ LOGWARNING("Unknown structure generator: \"%s\". Ignoring.", itr->c_str());
+ }
+ } // for itr - Str[]
+}
+
+
+
+
+
+void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
+{
+ int Seed = m_ChunkGenerator.GetSeed();
+ AString Structures = a_IniFile.GetValueSet("Generator", "Finishers", "SprinkleFoliage,Ice,Snow,Lilypads,BottomLava,DeadBushes,PreSimulator");
+
+ AStringVector Str = StringSplitAndTrim(Structures, ",");
+ for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr)
+ {
+ // Finishers, alpha-sorted:
+ if (NoCaseCompare(*itr, "BottomLava") == 0)
+ {
+ int DefaultBottomLavaLevel = (m_World->GetDimension() == dimNether) ? 30 : 10;
+ int BottomLavaLevel = a_IniFile.GetValueSetI("Generator", "BottomLavaLevel", DefaultBottomLavaLevel);
+ m_FinishGens.push_back(new cFinishGenBottomLava(BottomLavaLevel));
+ }
+ else if (NoCaseCompare(*itr, "DeadBushes") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenSingleBiomeSingleTopBlock(Seed, E_BLOCK_DEAD_BUSH, biDesert, 2, E_BLOCK_SAND, E_BLOCK_SAND));
+ }
+ else if (NoCaseCompare(*itr, "Ice") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenIce);
+ }
+ else if (NoCaseCompare(*itr, "LavaSprings") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_LAVA, a_IniFile, *m_World));
+ }
+ else if (NoCaseCompare(*itr, "Lilypads") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenSingleBiomeSingleTopBlock(Seed, E_BLOCK_LILY_PAD, biSwampland, 4, E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER));
+ }
+ else if (NoCaseCompare(*itr, "PreSimulator") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenPreSimulator);
+ }
+ else if (NoCaseCompare(*itr, "Snow") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenSnow);
+ }
+ else if (NoCaseCompare(*itr, "SprinkleFoliage") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenSprinkleFoliage(Seed));
+ }
+ else if (NoCaseCompare(*itr, "WaterSprings") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_WATER, a_IniFile, *m_World));
+ }
+ } // for itr - Str[]
+}
+
+
+
+
diff --git a/source/Generating/ComposableGenerator.h b/source/Generating/ComposableGenerator.h
index c32e7181f..1d5c98e5d 100644
--- a/source/Generating/ComposableGenerator.h
+++ b/source/Generating/ComposableGenerator.h
@@ -1,175 +1,175 @@
-
-// ComposableGenerator.h
-
-// Declares the cComposableGenerator class representing the chunk generator that takes the composition approach to generating chunks
-
-/*
-Generating works by composing several algorithms:
-Biome, TerrainHeight, TerrainComposition, Ores, Structures and SmallFoliage
-Each algorithm may be chosen from a pool of available algorithms in the same class and combined with others,
-based on user's preferences in the world.ini.
-See http://forum.mc-server.org/showthread.php?tid=409 for details.
-*/
-
-
-
-
-
-#pragma once
-
-#include "ChunkGenerator.h"
-#include "ChunkDesc.h"
-
-
-
-
-
-// fwd: Noise3DGenerator.h
-class cNoise3DComposable;
-
-// fwd: DistortedHeightmap.h
-class cDistortedHeightmap;
-
-
-
-
-
-/** The interface that a biome generator must implement
-A biome generator takes chunk coords on input and outputs an array of biome indices for that chunk on output.
-The output array is sequenced in the same way as the MapChunk packet's biome data.
-*/
-class cBiomeGen
-{
-public:
- virtual ~cBiomeGen() {} // Force a virtual destructor in descendants
-
- /// Generates biomes for the given chunk
- virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0;
-
- /// Reads parameters from the ini file, prepares generator for use.
- virtual void Initialize(cIniFile & a_IniFile) {}
-} ;
-
-
-
-
-
-/** The interface that a terrain height generator must implement
-A terrain height generator takes chunk coords on input and outputs an array of terrain heights for that chunk.
-The output array is sequenced in the same way as the BiomeGen's biome data.
-The generator may request biome information from the underlying BiomeGen, it may even request information for
-other chunks than the one it's currently generating (possibly neighbors - for averaging)
-*/
-class cTerrainHeightGen
-{
-public:
- virtual ~cTerrainHeightGen() {} // Force a virtual destructor in descendants
-
- /// Generates heightmap for the given chunk
- virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0;
-} ;
-
-
-
-
-
-/** The interface that a terrain composition generator must implement
-Terrain composition takes chunk coords on input and outputs the blockdata for that entire chunk, along with
-the list of entities. It is supposed to make use of the underlying TerrainHeightGen and BiomeGen for that purpose,
-but it may request information for other chunks than the one it's currently generating from them.
-*/
-class cTerrainCompositionGen
-{
-public:
- virtual ~cTerrainCompositionGen() {} // Force a virtual destructor in descendants
-
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) = 0;
-} ;
-
-
-
-
-
-/** The interface that a structure generator must implement
-Structures are generated after the terrain composition took place. It should modify the blocktype data to account
-for whatever structures the generator is generating.
-Note that ores are considered structures too, at least from the interface point of view.
-Also note that a worldgenerator may contain multiple structure generators, one for each type of structure
-*/
-class cStructureGen
-{
-public:
- virtual ~cStructureGen() {} // Force a virtual destructor in descendants
-
- virtual void GenStructures(cChunkDesc & a_ChunkDesc) = 0;
-} ;
-
-typedef std::list<cStructureGen *> cStructureGenList;
-
-
-
-
-
-/** The interface that a finisher must implement
-Finisher implements small additions after all structures have been generated.
-*/
-class cFinishGen
-{
-public:
- virtual ~cFinishGen() {} // Force a virtual destructor in descendants
-
- virtual void GenFinish(cChunkDesc & a_ChunkDesc) = 0;
-} ;
-
-typedef std::list<cFinishGen *> cFinishGenList;
-
-
-
-
-
-class cComposableGenerator :
- public cChunkGenerator::cGenerator
-{
- typedef cChunkGenerator::cGenerator super;
-
-public:
- cComposableGenerator(cChunkGenerator & a_ChunkGenerator);
- virtual ~cComposableGenerator();
-
- virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override;
- virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
- virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override;
-
-protected:
- // The generation composition:
- cBiomeGen * m_BiomeGen;
- cTerrainHeightGen * m_HeightGen;
- cTerrainCompositionGen * m_CompositionGen;
- cStructureGenList m_StructureGens;
- cFinishGenList m_FinishGens;
-
- // Generators underlying the caches:
- cBiomeGen * m_UnderlyingBiomeGen;
- cTerrainHeightGen * m_UnderlyingHeightGen;
- cTerrainCompositionGen * m_UnderlyingCompositionGen;
-
-
- /// Reads the biome gen settings from the ini and initializes m_BiomeGen accordingly
- void InitBiomeGen(cIniFile & a_IniFile);
-
- /// Reads the HeightGen settings from the ini and initializes m_HeightGen accordingly
- void InitHeightGen(cIniFile & a_IniFile);
-
- /// Reads the CompositionGen settings from the ini and initializes m_CompositionGen accordingly
- void InitCompositionGen(cIniFile & a_IniFile);
-
- /// Reads the structures to generate from the ini and initializes m_StructureGens accordingly
- void InitStructureGens(cIniFile & a_IniFile);
-
- /// Reads the finishers from the ini and initializes m_FinishGens accordingly
- void InitFinishGens(cIniFile & a_IniFile);
-} ;
-
-
-
-
+
+// ComposableGenerator.h
+
+// Declares the cComposableGenerator class representing the chunk generator that takes the composition approach to generating chunks
+
+/*
+Generating works by composing several algorithms:
+Biome, TerrainHeight, TerrainComposition, Ores, Structures and SmallFoliage
+Each algorithm may be chosen from a pool of available algorithms in the same class and combined with others,
+based on user's preferences in the world.ini.
+See http://forum.mc-server.org/showthread.php?tid=409 for details.
+*/
+
+
+
+
+
+#pragma once
+
+#include "ChunkGenerator.h"
+#include "ChunkDesc.h"
+
+
+
+
+
+// fwd: Noise3DGenerator.h
+class cNoise3DComposable;
+
+// fwd: DistortedHeightmap.h
+class cDistortedHeightmap;
+
+
+
+
+
+/** The interface that a biome generator must implement
+A biome generator takes chunk coords on input and outputs an array of biome indices for that chunk on output.
+The output array is sequenced in the same way as the MapChunk packet's biome data.
+*/
+class cBiomeGen
+{
+public:
+ virtual ~cBiomeGen() {} // Force a virtual destructor in descendants
+
+ /// Generates biomes for the given chunk
+ virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0;
+
+ /// Reads parameters from the ini file, prepares generator for use.
+ virtual void Initialize(cIniFile & a_IniFile) {}
+} ;
+
+
+
+
+
+/** The interface that a terrain height generator must implement
+A terrain height generator takes chunk coords on input and outputs an array of terrain heights for that chunk.
+The output array is sequenced in the same way as the BiomeGen's biome data.
+The generator may request biome information from the underlying BiomeGen, it may even request information for
+other chunks than the one it's currently generating (possibly neighbors - for averaging)
+*/
+class cTerrainHeightGen
+{
+public:
+ virtual ~cTerrainHeightGen() {} // Force a virtual destructor in descendants
+
+ /// Generates heightmap for the given chunk
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0;
+} ;
+
+
+
+
+
+/** The interface that a terrain composition generator must implement
+Terrain composition takes chunk coords on input and outputs the blockdata for that entire chunk, along with
+the list of entities. It is supposed to make use of the underlying TerrainHeightGen and BiomeGen for that purpose,
+but it may request information for other chunks than the one it's currently generating from them.
+*/
+class cTerrainCompositionGen
+{
+public:
+ virtual ~cTerrainCompositionGen() {} // Force a virtual destructor in descendants
+
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) = 0;
+} ;
+
+
+
+
+
+/** The interface that a structure generator must implement
+Structures are generated after the terrain composition took place. It should modify the blocktype data to account
+for whatever structures the generator is generating.
+Note that ores are considered structures too, at least from the interface point of view.
+Also note that a worldgenerator may contain multiple structure generators, one for each type of structure
+*/
+class cStructureGen
+{
+public:
+ virtual ~cStructureGen() {} // Force a virtual destructor in descendants
+
+ virtual void GenStructures(cChunkDesc & a_ChunkDesc) = 0;
+} ;
+
+typedef std::list<cStructureGen *> cStructureGenList;
+
+
+
+
+
+/** The interface that a finisher must implement
+Finisher implements small additions after all structures have been generated.
+*/
+class cFinishGen
+{
+public:
+ virtual ~cFinishGen() {} // Force a virtual destructor in descendants
+
+ virtual void GenFinish(cChunkDesc & a_ChunkDesc) = 0;
+} ;
+
+typedef std::list<cFinishGen *> cFinishGenList;
+
+
+
+
+
+class cComposableGenerator :
+ public cChunkGenerator::cGenerator
+{
+ typedef cChunkGenerator::cGenerator super;
+
+public:
+ cComposableGenerator(cChunkGenerator & a_ChunkGenerator);
+ virtual ~cComposableGenerator();
+
+ virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override;
+ virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
+ virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override;
+
+protected:
+ // The generation composition:
+ cBiomeGen * m_BiomeGen;
+ cTerrainHeightGen * m_HeightGen;
+ cTerrainCompositionGen * m_CompositionGen;
+ cStructureGenList m_StructureGens;
+ cFinishGenList m_FinishGens;
+
+ // Generators underlying the caches:
+ cBiomeGen * m_UnderlyingBiomeGen;
+ cTerrainHeightGen * m_UnderlyingHeightGen;
+ cTerrainCompositionGen * m_UnderlyingCompositionGen;
+
+
+ /// Reads the biome gen settings from the ini and initializes m_BiomeGen accordingly
+ void InitBiomeGen(cIniFile & a_IniFile);
+
+ /// Reads the HeightGen settings from the ini and initializes m_HeightGen accordingly
+ void InitHeightGen(cIniFile & a_IniFile);
+
+ /// Reads the CompositionGen settings from the ini and initializes m_CompositionGen accordingly
+ void InitCompositionGen(cIniFile & a_IniFile);
+
+ /// Reads the structures to generate from the ini and initializes m_StructureGens accordingly
+ void InitStructureGens(cIniFile & a_IniFile);
+
+ /// Reads the finishers from the ini and initializes m_FinishGens accordingly
+ void InitFinishGens(cIniFile & a_IniFile);
+} ;
+
+
+
+
diff --git a/source/Generating/DistortedHeightmap.cpp b/source/Generating/DistortedHeightmap.cpp
index 5efc27895..6ac4d61d5 100644
--- a/source/Generating/DistortedHeightmap.cpp
+++ b/source/Generating/DistortedHeightmap.cpp
@@ -1,419 +1,419 @@
-
-// DistortedHeightmap.cpp
-
-// Implements the cDistortedHeightmap class representing the height and composition generator capable of overhangs
-
-#include "Globals.h"
-
-#include "DistortedHeightmap.h"
-#include "../OSSupport/File.h"
-#include "../../iniFile/iniFile.h"
-#include "../LinearUpscale.h"
-
-
-
-
-
-/** This table assigns a relative maximum overhang size in each direction to biomes.
-Both numbers indicate a number which will multiply the noise value for each coord;
-this means that you can have different-sized overhangs in each direction.
-Usually you'd want to keep both numbers the same.
-The numbers are "relative", not absolute maximum; overhangs of a slightly larger size are possible
-due to the way that noise is calculated.
-*/
-const cDistortedHeightmap::sGenParam cDistortedHeightmap::m_GenParam[biNumBiomes] =
-{
- /* Biome | AmpX | AmpZ */
- /* biOcean */ { 1.5f, 1.5f},
- /* biPlains */ { 0.5f, 0.5f},
- /* biDesert */ { 0.5f, 0.5f},
- /* biExtremeHills */ {16.0f, 16.0f},
- /* biForest */ { 3.0f, 3.0f},
- /* biTaiga */ { 1.5f, 1.5f},
-
- /* biSwampland */ { 0.0f, 0.0f},
- /* biRiver */ { 0.0f, 0.0f},
- /* biNether */ { 0.0f, 0.0f}, // Unused, but must be here due to indexing
- /* biSky */ { 0.0f, 0.0f}, // Unused, but must be here due to indexing
- /* biFrozenOcean */ { 0.0f, 0.0f},
- /* biFrozenRiver */ { 0.0f, 0.0f},
- /* biIcePlains */ { 0.0f, 0.0f},
- /* biIceMountains */ { 8.0f, 8.0f},
- /* biMushroomIsland */ { 4.0f, 4.0f},
- /* biMushroomShore */ { 0.0f, 0.0f},
- /* biBeach */ { 0.0f, 0.0f},
- /* biDesertHills */ { 5.0f, 5.0f},
- /* biForestHills */ { 6.0f, 6.0f},
- /* biTaigaHills */ { 8.0f, 8.0f},
- /* biExtremeHillsEdge */ { 7.0f, 7.0f},
- /* biJungle */ { 0.0f, 0.0f},
- /* biJungleHills */ { 8.0f, 8.0f},
-} ;
-
-
-
-
-
-cDistortedHeightmap::cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen) :
- m_NoiseDistortX(a_Seed + 1000),
- m_NoiseDistortZ(a_Seed + 2000),
- m_OceanFloorSelect(a_Seed + 3000),
- m_BiomeGen(a_BiomeGen),
- m_UnderlyingHeiGen(a_Seed, a_BiomeGen),
- m_HeightGen(&m_UnderlyingHeiGen, 64)
-{
- m_NoiseDistortX.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5);
- m_NoiseDistortX.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1);
- m_NoiseDistortX.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2);
-
- m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5);
- m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1);
- m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2);
-}
-
-
-
-
-
-void cDistortedHeightmap::Initialize(cIniFile & a_IniFile)
-{
- // Read the params from the INI file:
- m_SeaLevel = a_IniFile.GetValueSetI("Generator", "DistortedHeightmapSeaLevel", 62);
- m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyX", 10);
- m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyY", 10);
- m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyZ", 10);
-}
-
-
-
-
-
-void cDistortedHeightmap::PrepareState(int a_ChunkX, int a_ChunkZ)
-{
- if ((m_CurChunkX == a_ChunkX) && (m_CurChunkZ == a_ChunkZ))
- {
- return;
- }
- m_CurChunkX = a_ChunkX;
- m_CurChunkZ = a_ChunkZ;
-
-
- m_HeightGen.GenHeightMap(a_ChunkX, a_ChunkZ, m_CurChunkHeights);
- UpdateDistortAmps();
- GenerateHeightArray();
-}
-
-
-
-
-
-void cDistortedHeightmap::GenerateHeightArray(void)
-{
- // Generate distortion noise:
- NOISE_DATATYPE DistortNoiseX[DIM_X * DIM_Y * DIM_Z];
- NOISE_DATATYPE DistortNoiseZ[DIM_X * DIM_Y * DIM_Z];
- NOISE_DATATYPE Workspace[DIM_X * DIM_Y * DIM_Z];
- NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width)) / m_FrequencyX;
- NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((m_CurChunkX + 1) * cChunkDef::Width - 1)) / m_FrequencyX;
- NOISE_DATATYPE StartY = 0;
- NOISE_DATATYPE EndY = ((NOISE_DATATYPE)(257)) / m_FrequencyY;
- NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width)) / m_FrequencyZ;
- NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((m_CurChunkZ + 1) * cChunkDef::Width - 1)) / m_FrequencyZ;
-
- m_NoiseDistortX.Generate3D(DistortNoiseX, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, Workspace);
- m_NoiseDistortZ.Generate3D(DistortNoiseZ, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, Workspace);
-
- // The distorted heightmap, before linear upscaling
- NOISE_DATATYPE DistHei[DIM_X * DIM_Y * DIM_Z];
-
- // Distort the heightmap using the distortion:
- for (int z = 0; z < DIM_Z; z++)
- {
- int AmpIdx = z * DIM_X;
- for (int y = 0; y < DIM_Y; y++)
- {
- int NoiseArrayIdx = z * DIM_X * DIM_Y + y * DIM_X;
- for (int x = 0; x < DIM_X; x++)
- {
- NOISE_DATATYPE DistX = DistortNoiseX[NoiseArrayIdx + x] * m_DistortAmpX[AmpIdx + x];
- NOISE_DATATYPE DistZ = DistortNoiseZ[NoiseArrayIdx + x] * m_DistortAmpZ[AmpIdx + x];
- DistX += (NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + x * INTERPOL_X);
- DistZ += (NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + z * INTERPOL_Z);
- // Adding 0.5 helps alleviate the interpolation artifacts
- DistHei[NoiseArrayIdx + x] = (NOISE_DATATYPE)GetHeightmapAt(DistX, DistZ) + (NOISE_DATATYPE)0.5;
- }
- }
- }
-
- // Upscale the distorted heightmap into full dimensions:
- LinearUpscale3DArray(
- DistHei, DIM_X, DIM_Y, DIM_Z,
- m_DistortedHeightmap, INTERPOL_X, INTERPOL_Y, INTERPOL_Z
- );
-
- // DEBUG: Debug3DNoise(m_DistortedHeightmap, 17, 257, 17, Printf("DistortedHeightmap_%d_%d", m_CurChunkX, m_CurChunkZ));
-}
-
-
-
-
-
-void cDistortedHeightmap::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
-{
- PrepareState(a_ChunkX, a_ChunkZ);
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- int NoiseArrayIdx = x + 17 * 257 * z;
- cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel - 1);
- for (int y = cChunkDef::Height - 1; y > m_SeaLevel - 1; y--)
- {
- int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y];
- if (y < HeightMapHeight)
- {
- cChunkDef::SetHeight(a_HeightMap, x, z, y);
- break;
- }
- } // for y
- } // for x
- } // for z
-}
-
-
-
-
-
-void cDistortedHeightmap::ComposeTerrain(cChunkDesc & a_ChunkDesc)
-{
- // Frequencies for the ocean floor selecting noise:
- NOISE_DATATYPE FrequencyX = 3;
- NOISE_DATATYPE FrequencyZ = 3;
-
- // Prepare the internal state for generating this chunk:
- PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
-
- // Compose:
- a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- int NoiseArrayIdx = x + 17 * 257 * z;
- int LastAir = a_ChunkDesc.GetHeight(x, z) + 1;
- bool HasHadWater = false;
- for (int y = LastAir - 1; y > 0; y--)
- {
- int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y];
-
- if (y >= HeightMapHeight)
- {
- // "air" part
- LastAir = y;
- if (y < m_SeaLevel)
- {
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
- HasHadWater = true;
- }
- continue;
- }
- // "ground" part:
- if (y < LastAir - 4)
- {
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE);
- continue;
- }
- if (HasHadWater)
- {
- // Decide between clay, sand and dirt
- NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + x)) / FrequencyX;
- NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + z)) / FrequencyZ;
- NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
- if (Val < -0.95)
- {
- // Clay:
- switch (LastAir - y)
- {
- case 0:
- case 1:
- {
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_CLAY);
- break;
- }
- case 2:
- case 3:
- {
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND);
- break;
- }
- case 4:
- {
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SANDSTONE);
- break;
- }
- } // switch (floor depth)
- }
- else if (Val < 0)
- {
- a_ChunkDesc.SetBlockType(x, y, z, (y < LastAir - 3) ? E_BLOCK_SANDSTONE : E_BLOCK_SAND);
- }
- else
- {
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_DIRT);
- }
- }
- else
- {
- switch (a_ChunkDesc.GetBiome(x, z))
- {
- case biOcean:
- case biPlains:
- case biExtremeHills:
- case biForest:
- case biTaiga:
- case biSwampland:
- case biRiver:
- case biFrozenOcean:
- case biFrozenRiver:
- case biIcePlains:
- case biIceMountains:
- case biForestHills:
- case biTaigaHills:
- case biExtremeHillsEdge:
- case biJungle:
- case biJungleHills:
- {
- a_ChunkDesc.SetBlockType(x, y, z, (y == LastAir - 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT);
- break;
- }
- case biDesertHills:
- case biDesert:
- case biBeach:
- {
- a_ChunkDesc.SetBlockType(x, y, z, (y < LastAir - 3) ? E_BLOCK_SANDSTONE : E_BLOCK_SAND);
- break;
- }
- case biMushroomIsland:
- case biMushroomShore:
- {
- a_ChunkDesc.SetBlockType(x, y, z, (y == LastAir - 1) ? E_BLOCK_MYCELIUM : E_BLOCK_DIRT);
- break;
- }
- }
- }
- } // for y
- a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
- } // for x
- } // for z
-}
-
-
-
-
-
-int cDistortedHeightmap::GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z)
-{
- int ChunkX = (int)floor(a_X / (NOISE_DATATYPE)16);
- int ChunkZ = (int)floor(a_Z / (NOISE_DATATYPE)16);
- int RelX = (int)(a_X - (NOISE_DATATYPE)ChunkX * cChunkDef::Width);
- int RelZ = (int)(a_Z - (NOISE_DATATYPE)ChunkZ * cChunkDef::Width);
-
- // If we're withing the same chunk, return the pre-cached heightmap:
- if ((ChunkX == m_CurChunkX) && (ChunkZ == m_CurChunkZ))
- {
- return cChunkDef::GetHeight(m_CurChunkHeights, RelX, RelZ);
- }
-
- // Ask the cache:
- HEIGHTTYPE res = 0;
- if (m_HeightGen.GetHeightAt(ChunkX, ChunkZ, RelX, RelZ, res))
- {
- // The height was in the cache
- return res;
- }
-
- // The height is not in the cache, generate full heightmap and get it there:
- cChunkDef::HeightMap Heightmap;
- m_HeightGen.GenHeightMap(ChunkX, ChunkZ, Heightmap);
- return cChunkDef::GetHeight(Heightmap, RelX, RelZ);
-}
-
-
-
-
-
-void cDistortedHeightmap::UpdateDistortAmps(void)
-{
- BiomeNeighbors Biomes;
- for (int z = -1; z <= 1; z++)
- {
- for (int x = -1; x <= 1; x++)
- {
- m_BiomeGen.GenBiomes(m_CurChunkX + x, m_CurChunkZ + z, Biomes[x + 1][z + 1]);
- } // for x
- } // for z
-
- for (int z = 0; z < DIM_Z; z++)
- {
- for (int x = 0; x < DIM_Z; x++)
- {
- GetDistortAmpsAt(Biomes, x * INTERPOL_X, z * INTERPOL_Z, m_DistortAmpX[x + DIM_X * z], m_DistortAmpZ[x + DIM_X * z]);
- }
- }
-}
-
-
-
-
-
-void cDistortedHeightmap::GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ)
-{
- // Sum up how many biomes of each type there are in the neighborhood:
- int BiomeCounts[biNumBiomes];
- memset(BiomeCounts, 0, sizeof(BiomeCounts));
- int Sum = 0;
- for (int z = -8; z <= 8; z++)
- {
- int FinalZ = a_RelZ + z + cChunkDef::Width;
- int IdxZ = FinalZ / cChunkDef::Width;
- int ModZ = FinalZ % cChunkDef::Width;
- int WeightZ = 9 - abs(z);
- for (int x = -8; x <= 8; x++)
- {
- int FinalX = a_RelX + x + cChunkDef::Width;
- int IdxX = FinalX / cChunkDef::Width;
- int ModX = FinalX % cChunkDef::Width;
- EMCSBiome Biome = cChunkDef::GetBiome(a_Neighbors[IdxX][IdxZ], ModX, ModZ);
- if ((Biome < 0) || (Biome >= ARRAYCOUNT(BiomeCounts)))
- {
- continue;
- }
- int WeightX = 9 - abs(x);
- BiomeCounts[Biome] += WeightX + WeightZ;
- Sum += WeightX + WeightZ;
- } // for x
- } // for z
-
- if (Sum <= 0)
- {
- // No known biome around? Weird. Return a bogus value:
- ASSERT(!"cHeiGenBiomal: Biome sum failed, no known biome around");
- a_DistortAmpX = 16;
- a_DistortAmpZ = 16;
- }
-
- // For each biome type that has a nonzero count, calc its amps and add it:
- NOISE_DATATYPE AmpX = 0;
- NOISE_DATATYPE AmpZ = 0;
- for (int i = 0; i < ARRAYCOUNT(BiomeCounts); i++)
- {
- AmpX += BiomeCounts[i] * m_GenParam[i].m_DistortAmpX;
- AmpZ += BiomeCounts[i] * m_GenParam[i].m_DistortAmpZ;
- }
- a_DistortAmpX = AmpX / Sum;
- a_DistortAmpZ = AmpZ / Sum;
-}
-
-
-
-
+
+// DistortedHeightmap.cpp
+
+// Implements the cDistortedHeightmap class representing the height and composition generator capable of overhangs
+
+#include "Globals.h"
+
+#include "DistortedHeightmap.h"
+#include "../OSSupport/File.h"
+#include "../../iniFile/iniFile.h"
+#include "../LinearUpscale.h"
+
+
+
+
+
+/** This table assigns a relative maximum overhang size in each direction to biomes.
+Both numbers indicate a number which will multiply the noise value for each coord;
+this means that you can have different-sized overhangs in each direction.
+Usually you'd want to keep both numbers the same.
+The numbers are "relative", not absolute maximum; overhangs of a slightly larger size are possible
+due to the way that noise is calculated.
+*/
+const cDistortedHeightmap::sGenParam cDistortedHeightmap::m_GenParam[biNumBiomes] =
+{
+ /* Biome | AmpX | AmpZ */
+ /* biOcean */ { 1.5f, 1.5f},
+ /* biPlains */ { 0.5f, 0.5f},
+ /* biDesert */ { 0.5f, 0.5f},
+ /* biExtremeHills */ {16.0f, 16.0f},
+ /* biForest */ { 3.0f, 3.0f},
+ /* biTaiga */ { 1.5f, 1.5f},
+
+ /* biSwampland */ { 0.0f, 0.0f},
+ /* biRiver */ { 0.0f, 0.0f},
+ /* biNether */ { 0.0f, 0.0f}, // Unused, but must be here due to indexing
+ /* biSky */ { 0.0f, 0.0f}, // Unused, but must be here due to indexing
+ /* biFrozenOcean */ { 0.0f, 0.0f},
+ /* biFrozenRiver */ { 0.0f, 0.0f},
+ /* biIcePlains */ { 0.0f, 0.0f},
+ /* biIceMountains */ { 8.0f, 8.0f},
+ /* biMushroomIsland */ { 4.0f, 4.0f},
+ /* biMushroomShore */ { 0.0f, 0.0f},
+ /* biBeach */ { 0.0f, 0.0f},
+ /* biDesertHills */ { 5.0f, 5.0f},
+ /* biForestHills */ { 6.0f, 6.0f},
+ /* biTaigaHills */ { 8.0f, 8.0f},
+ /* biExtremeHillsEdge */ { 7.0f, 7.0f},
+ /* biJungle */ { 0.0f, 0.0f},
+ /* biJungleHills */ { 8.0f, 8.0f},
+} ;
+
+
+
+
+
+cDistortedHeightmap::cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen) :
+ m_NoiseDistortX(a_Seed + 1000),
+ m_NoiseDistortZ(a_Seed + 2000),
+ m_OceanFloorSelect(a_Seed + 3000),
+ m_BiomeGen(a_BiomeGen),
+ m_UnderlyingHeiGen(a_Seed, a_BiomeGen),
+ m_HeightGen(&m_UnderlyingHeiGen, 64)
+{
+ m_NoiseDistortX.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5);
+ m_NoiseDistortX.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1);
+ m_NoiseDistortX.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2);
+
+ m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5);
+ m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1);
+ m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2);
+}
+
+
+
+
+
+void cDistortedHeightmap::Initialize(cIniFile & a_IniFile)
+{
+ // Read the params from the INI file:
+ m_SeaLevel = a_IniFile.GetValueSetI("Generator", "DistortedHeightmapSeaLevel", 62);
+ m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyX", 10);
+ m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyY", 10);
+ m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyZ", 10);
+}
+
+
+
+
+
+void cDistortedHeightmap::PrepareState(int a_ChunkX, int a_ChunkZ)
+{
+ if ((m_CurChunkX == a_ChunkX) && (m_CurChunkZ == a_ChunkZ))
+ {
+ return;
+ }
+ m_CurChunkX = a_ChunkX;
+ m_CurChunkZ = a_ChunkZ;
+
+
+ m_HeightGen.GenHeightMap(a_ChunkX, a_ChunkZ, m_CurChunkHeights);
+ UpdateDistortAmps();
+ GenerateHeightArray();
+}
+
+
+
+
+
+void cDistortedHeightmap::GenerateHeightArray(void)
+{
+ // Generate distortion noise:
+ NOISE_DATATYPE DistortNoiseX[DIM_X * DIM_Y * DIM_Z];
+ NOISE_DATATYPE DistortNoiseZ[DIM_X * DIM_Y * DIM_Z];
+ NOISE_DATATYPE Workspace[DIM_X * DIM_Y * DIM_Z];
+ NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width)) / m_FrequencyX;
+ NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((m_CurChunkX + 1) * cChunkDef::Width - 1)) / m_FrequencyX;
+ NOISE_DATATYPE StartY = 0;
+ NOISE_DATATYPE EndY = ((NOISE_DATATYPE)(257)) / m_FrequencyY;
+ NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width)) / m_FrequencyZ;
+ NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((m_CurChunkZ + 1) * cChunkDef::Width - 1)) / m_FrequencyZ;
+
+ m_NoiseDistortX.Generate3D(DistortNoiseX, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, Workspace);
+ m_NoiseDistortZ.Generate3D(DistortNoiseZ, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, Workspace);
+
+ // The distorted heightmap, before linear upscaling
+ NOISE_DATATYPE DistHei[DIM_X * DIM_Y * DIM_Z];
+
+ // Distort the heightmap using the distortion:
+ for (int z = 0; z < DIM_Z; z++)
+ {
+ int AmpIdx = z * DIM_X;
+ for (int y = 0; y < DIM_Y; y++)
+ {
+ int NoiseArrayIdx = z * DIM_X * DIM_Y + y * DIM_X;
+ for (int x = 0; x < DIM_X; x++)
+ {
+ NOISE_DATATYPE DistX = DistortNoiseX[NoiseArrayIdx + x] * m_DistortAmpX[AmpIdx + x];
+ NOISE_DATATYPE DistZ = DistortNoiseZ[NoiseArrayIdx + x] * m_DistortAmpZ[AmpIdx + x];
+ DistX += (NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + x * INTERPOL_X);
+ DistZ += (NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + z * INTERPOL_Z);
+ // Adding 0.5 helps alleviate the interpolation artifacts
+ DistHei[NoiseArrayIdx + x] = (NOISE_DATATYPE)GetHeightmapAt(DistX, DistZ) + (NOISE_DATATYPE)0.5;
+ }
+ }
+ }
+
+ // Upscale the distorted heightmap into full dimensions:
+ LinearUpscale3DArray(
+ DistHei, DIM_X, DIM_Y, DIM_Z,
+ m_DistortedHeightmap, INTERPOL_X, INTERPOL_Y, INTERPOL_Z
+ );
+
+ // DEBUG: Debug3DNoise(m_DistortedHeightmap, 17, 257, 17, Printf("DistortedHeightmap_%d_%d", m_CurChunkX, m_CurChunkZ));
+}
+
+
+
+
+
+void cDistortedHeightmap::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+{
+ PrepareState(a_ChunkX, a_ChunkZ);
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int NoiseArrayIdx = x + 17 * 257 * z;
+ cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel - 1);
+ for (int y = cChunkDef::Height - 1; y > m_SeaLevel - 1; y--)
+ {
+ int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y];
+ if (y < HeightMapHeight)
+ {
+ cChunkDef::SetHeight(a_HeightMap, x, z, y);
+ break;
+ }
+ } // for y
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cDistortedHeightmap::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+{
+ // Frequencies for the ocean floor selecting noise:
+ NOISE_DATATYPE FrequencyX = 3;
+ NOISE_DATATYPE FrequencyZ = 3;
+
+ // Prepare the internal state for generating this chunk:
+ PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
+
+ // Compose:
+ a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int NoiseArrayIdx = x + 17 * 257 * z;
+ int LastAir = a_ChunkDesc.GetHeight(x, z) + 1;
+ bool HasHadWater = false;
+ for (int y = LastAir - 1; y > 0; y--)
+ {
+ int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y];
+
+ if (y >= HeightMapHeight)
+ {
+ // "air" part
+ LastAir = y;
+ if (y < m_SeaLevel)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
+ HasHadWater = true;
+ }
+ continue;
+ }
+ // "ground" part:
+ if (y < LastAir - 4)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE);
+ continue;
+ }
+ if (HasHadWater)
+ {
+ // Decide between clay, sand and dirt
+ NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + x)) / FrequencyX;
+ NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + z)) / FrequencyZ;
+ NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
+ if (Val < -0.95)
+ {
+ // Clay:
+ switch (LastAir - y)
+ {
+ case 0:
+ case 1:
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_CLAY);
+ break;
+ }
+ case 2:
+ case 3:
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND);
+ break;
+ }
+ case 4:
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SANDSTONE);
+ break;
+ }
+ } // switch (floor depth)
+ }
+ else if (Val < 0)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, (y < LastAir - 3) ? E_BLOCK_SANDSTONE : E_BLOCK_SAND);
+ }
+ else
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_DIRT);
+ }
+ }
+ else
+ {
+ switch (a_ChunkDesc.GetBiome(x, z))
+ {
+ case biOcean:
+ case biPlains:
+ case biExtremeHills:
+ case biForest:
+ case biTaiga:
+ case biSwampland:
+ case biRiver:
+ case biFrozenOcean:
+ case biFrozenRiver:
+ case biIcePlains:
+ case biIceMountains:
+ case biForestHills:
+ case biTaigaHills:
+ case biExtremeHillsEdge:
+ case biJungle:
+ case biJungleHills:
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, (y == LastAir - 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT);
+ break;
+ }
+ case biDesertHills:
+ case biDesert:
+ case biBeach:
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, (y < LastAir - 3) ? E_BLOCK_SANDSTONE : E_BLOCK_SAND);
+ break;
+ }
+ case biMushroomIsland:
+ case biMushroomShore:
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, (y == LastAir - 1) ? E_BLOCK_MYCELIUM : E_BLOCK_DIRT);
+ break;
+ }
+ }
+ }
+ } // for y
+ a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
+ } // for x
+ } // for z
+}
+
+
+
+
+
+int cDistortedHeightmap::GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z)
+{
+ int ChunkX = (int)floor(a_X / (NOISE_DATATYPE)16);
+ int ChunkZ = (int)floor(a_Z / (NOISE_DATATYPE)16);
+ int RelX = (int)(a_X - (NOISE_DATATYPE)ChunkX * cChunkDef::Width);
+ int RelZ = (int)(a_Z - (NOISE_DATATYPE)ChunkZ * cChunkDef::Width);
+
+ // If we're withing the same chunk, return the pre-cached heightmap:
+ if ((ChunkX == m_CurChunkX) && (ChunkZ == m_CurChunkZ))
+ {
+ return cChunkDef::GetHeight(m_CurChunkHeights, RelX, RelZ);
+ }
+
+ // Ask the cache:
+ HEIGHTTYPE res = 0;
+ if (m_HeightGen.GetHeightAt(ChunkX, ChunkZ, RelX, RelZ, res))
+ {
+ // The height was in the cache
+ return res;
+ }
+
+ // The height is not in the cache, generate full heightmap and get it there:
+ cChunkDef::HeightMap Heightmap;
+ m_HeightGen.GenHeightMap(ChunkX, ChunkZ, Heightmap);
+ return cChunkDef::GetHeight(Heightmap, RelX, RelZ);
+}
+
+
+
+
+
+void cDistortedHeightmap::UpdateDistortAmps(void)
+{
+ BiomeNeighbors Biomes;
+ for (int z = -1; z <= 1; z++)
+ {
+ for (int x = -1; x <= 1; x++)
+ {
+ m_BiomeGen.GenBiomes(m_CurChunkX + x, m_CurChunkZ + z, Biomes[x + 1][z + 1]);
+ } // for x
+ } // for z
+
+ for (int z = 0; z < DIM_Z; z++)
+ {
+ for (int x = 0; x < DIM_Z; x++)
+ {
+ GetDistortAmpsAt(Biomes, x * INTERPOL_X, z * INTERPOL_Z, m_DistortAmpX[x + DIM_X * z], m_DistortAmpZ[x + DIM_X * z]);
+ }
+ }
+}
+
+
+
+
+
+void cDistortedHeightmap::GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ)
+{
+ // Sum up how many biomes of each type there are in the neighborhood:
+ int BiomeCounts[biNumBiomes];
+ memset(BiomeCounts, 0, sizeof(BiomeCounts));
+ int Sum = 0;
+ for (int z = -8; z <= 8; z++)
+ {
+ int FinalZ = a_RelZ + z + cChunkDef::Width;
+ int IdxZ = FinalZ / cChunkDef::Width;
+ int ModZ = FinalZ % cChunkDef::Width;
+ int WeightZ = 9 - abs(z);
+ for (int x = -8; x <= 8; x++)
+ {
+ int FinalX = a_RelX + x + cChunkDef::Width;
+ int IdxX = FinalX / cChunkDef::Width;
+ int ModX = FinalX % cChunkDef::Width;
+ EMCSBiome Biome = cChunkDef::GetBiome(a_Neighbors[IdxX][IdxZ], ModX, ModZ);
+ if ((Biome < 0) || (Biome >= ARRAYCOUNT(BiomeCounts)))
+ {
+ continue;
+ }
+ int WeightX = 9 - abs(x);
+ BiomeCounts[Biome] += WeightX + WeightZ;
+ Sum += WeightX + WeightZ;
+ } // for x
+ } // for z
+
+ if (Sum <= 0)
+ {
+ // No known biome around? Weird. Return a bogus value:
+ ASSERT(!"cHeiGenBiomal: Biome sum failed, no known biome around");
+ a_DistortAmpX = 16;
+ a_DistortAmpZ = 16;
+ }
+
+ // For each biome type that has a nonzero count, calc its amps and add it:
+ NOISE_DATATYPE AmpX = 0;
+ NOISE_DATATYPE AmpZ = 0;
+ for (int i = 0; i < ARRAYCOUNT(BiomeCounts); i++)
+ {
+ AmpX += BiomeCounts[i] * m_GenParam[i].m_DistortAmpX;
+ AmpZ += BiomeCounts[i] * m_GenParam[i].m_DistortAmpZ;
+ }
+ a_DistortAmpX = AmpX / Sum;
+ a_DistortAmpZ = AmpZ / Sum;
+}
+
+
+
+
diff --git a/source/Generating/DistortedHeightmap.h b/source/Generating/DistortedHeightmap.h
index 41a781b84..b2b235e61 100644
--- a/source/Generating/DistortedHeightmap.h
+++ b/source/Generating/DistortedHeightmap.h
@@ -1,102 +1,102 @@
-
-// DistortedHeightmap.h
-
-// Declares the cDistortedHeightmap class representing the height and composition generator capable of overhangs
-
-
-
-
-
-#pragma once
-
-#include "ComposableGenerator.h"
-#include "HeiGen.h"
-#include "../Noise.h"
-
-
-
-
-
-#define NOISE_SIZE_Y (257 + 32)
-
-
-
-
-
-class cDistortedHeightmap :
- public cTerrainHeightGen,
- public cTerrainCompositionGen
-{
-public:
- cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen);
-
- void Initialize(cIniFile & a_IniFile);
-
-protected:
- typedef cChunkDef::BiomeMap BiomeNeighbors[3][3];
-
- // Linear upscaling step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively:
- static const int INTERPOL_X = 8;
- static const int INTERPOL_Y = 4;
- static const int INTERPOL_Z = 8;
-
- // Linear upscaling buffer dimensions, calculated from the step sizes:
- static const int DIM_X = 1 + (17 / INTERPOL_X);
- static const int DIM_Y = 1 + (257 / INTERPOL_Y);
- static const int DIM_Z = 1 + (17 / INTERPOL_Z);
-
- cPerlinNoise m_NoiseDistortX;
- cPerlinNoise m_NoiseDistortZ;
- cNoise m_OceanFloorSelect; ///< Used for selecting between dirt and sand on the ocean floor
-
- int m_SeaLevel;
- NOISE_DATATYPE m_FrequencyX;
- NOISE_DATATYPE m_FrequencyY;
- NOISE_DATATYPE m_FrequencyZ;
-
- int m_CurChunkX;
- int m_CurChunkZ;
- NOISE_DATATYPE m_DistortedHeightmap[17 * 257 * 17];
-
- cBiomeGen & m_BiomeGen;
- cHeiGenBiomal m_UnderlyingHeiGen; // This generator provides us with base heightmap (before distortion)
- cHeiGenCache m_HeightGen; // Cache above m_UnderlyingHeiGen
-
- /// Heightmap for the current chunk, before distortion (from m_HeightGen). Used for optimization.
- cChunkDef::HeightMap m_CurChunkHeights;
-
- // Per-biome terrain generator parameters:
- struct sGenParam
- {
- NOISE_DATATYPE m_DistortAmpX;
- NOISE_DATATYPE m_DistortAmpZ;
- } ;
- static const sGenParam m_GenParam[biNumBiomes];
-
- // Distortion amplitudes for each direction, before linear upscaling
- NOISE_DATATYPE m_DistortAmpX[DIM_X * DIM_Z];
- NOISE_DATATYPE m_DistortAmpZ[DIM_X * DIM_Z];
-
-
- /// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise arrays, heightmap)
- void PrepareState(int a_ChunkX, int a_ChunkZ);
-
- /// Generates the m_DistortedHeightmap array for the current chunk
- void GenerateHeightArray(void);
-
- /// Calculates the heightmap value (before distortion) at the specified (floating-point) coords
- int GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z);
-
- /// Updates m_DistortAmpX/Z[] based on m_CurChunkX and m_CurChunkZ
- void UpdateDistortAmps(void);
-
- /// Calculates the X and Z distortion amplitudes based on the neighbors' biomes
- void GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ);
-
-
- // cTerrainHeightGen overrides:
- virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
-
- // cTerrainCompositionGen overrides:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
-} ;
+
+// DistortedHeightmap.h
+
+// Declares the cDistortedHeightmap class representing the height and composition generator capable of overhangs
+
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+#include "HeiGen.h"
+#include "../Noise.h"
+
+
+
+
+
+#define NOISE_SIZE_Y (257 + 32)
+
+
+
+
+
+class cDistortedHeightmap :
+ public cTerrainHeightGen,
+ public cTerrainCompositionGen
+{
+public:
+ cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen);
+
+ void Initialize(cIniFile & a_IniFile);
+
+protected:
+ typedef cChunkDef::BiomeMap BiomeNeighbors[3][3];
+
+ // Linear upscaling step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively:
+ static const int INTERPOL_X = 8;
+ static const int INTERPOL_Y = 4;
+ static const int INTERPOL_Z = 8;
+
+ // Linear upscaling buffer dimensions, calculated from the step sizes:
+ static const int DIM_X = 1 + (17 / INTERPOL_X);
+ static const int DIM_Y = 1 + (257 / INTERPOL_Y);
+ static const int DIM_Z = 1 + (17 / INTERPOL_Z);
+
+ cPerlinNoise m_NoiseDistortX;
+ cPerlinNoise m_NoiseDistortZ;
+ cNoise m_OceanFloorSelect; ///< Used for selecting between dirt and sand on the ocean floor
+
+ int m_SeaLevel;
+ NOISE_DATATYPE m_FrequencyX;
+ NOISE_DATATYPE m_FrequencyY;
+ NOISE_DATATYPE m_FrequencyZ;
+
+ int m_CurChunkX;
+ int m_CurChunkZ;
+ NOISE_DATATYPE m_DistortedHeightmap[17 * 257 * 17];
+
+ cBiomeGen & m_BiomeGen;
+ cHeiGenBiomal m_UnderlyingHeiGen; // This generator provides us with base heightmap (before distortion)
+ cHeiGenCache m_HeightGen; // Cache above m_UnderlyingHeiGen
+
+ /// Heightmap for the current chunk, before distortion (from m_HeightGen). Used for optimization.
+ cChunkDef::HeightMap m_CurChunkHeights;
+
+ // Per-biome terrain generator parameters:
+ struct sGenParam
+ {
+ NOISE_DATATYPE m_DistortAmpX;
+ NOISE_DATATYPE m_DistortAmpZ;
+ } ;
+ static const sGenParam m_GenParam[biNumBiomes];
+
+ // Distortion amplitudes for each direction, before linear upscaling
+ NOISE_DATATYPE m_DistortAmpX[DIM_X * DIM_Z];
+ NOISE_DATATYPE m_DistortAmpZ[DIM_X * DIM_Z];
+
+
+ /// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise arrays, heightmap)
+ void PrepareState(int a_ChunkX, int a_ChunkZ);
+
+ /// Generates the m_DistortedHeightmap array for the current chunk
+ void GenerateHeightArray(void);
+
+ /// Calculates the heightmap value (before distortion) at the specified (floating-point) coords
+ int GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z);
+
+ /// Updates m_DistortAmpX/Z[] based on m_CurChunkX and m_CurChunkZ
+ void UpdateDistortAmps(void);
+
+ /// Calculates the X and Z distortion amplitudes based on the neighbors' biomes
+ void GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ);
+
+
+ // cTerrainHeightGen overrides:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+
+ // cTerrainCompositionGen overrides:
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+} ;
diff --git a/source/Generating/EndGen.cpp b/source/Generating/EndGen.cpp
index 30fa457db..3eba5c47b 100644
--- a/source/Generating/EndGen.cpp
+++ b/source/Generating/EndGen.cpp
@@ -1,217 +1,217 @@
-
-// EndGen.cpp
-
-// Implements the cEndGen class representing the generator for the End, both as a HeightGen and CompositionGen
-
-#include "Globals.h"
-#include "EndGen.h"
-#include "../../iniFile/iniFile.h"
-#include "../LinearUpscale.h"
-
-
-
-
-
-enum
-{
- // Interpolation cell size:
- INTERPOL_X = 4,
- INTERPOL_Y = 4,
- INTERPOL_Z = 4,
-
- // Size of chunk data, downscaled before interpolation:
- DIM_X = 16 / INTERPOL_X + 1,
- DIM_Y = 256 / INTERPOL_Y + 1,
- DIM_Z = 16 / INTERPOL_Z + 1,
-} ;
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cEndGen:
-
-cEndGen::cEndGen(int a_Seed) :
- m_Seed(a_Seed),
- m_IslandSizeX(256),
- m_IslandSizeY(96),
- m_IslandSizeZ(256),
- m_FrequencyX(80),
- m_FrequencyY(80),
- m_FrequencyZ(80)
-{
- m_Perlin.AddOctave(1, 1);
- m_Perlin.AddOctave(2, 0.5);
- m_Perlin.AddOctave(4, 0.25);
-}
-
-
-
-
-
-void cEndGen::Initialize(cIniFile & a_IniFile)
-{
- m_IslandSizeX = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeX", m_IslandSizeX);
- m_IslandSizeY = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeY", m_IslandSizeY);
- m_IslandSizeZ = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeZ", m_IslandSizeZ);
-
- m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyX", m_FrequencyX);
- m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyY", m_FrequencyY);
- m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyZ", m_FrequencyZ);
-
- // Recalculate the min and max chunk coords of the island
- m_MaxChunkX = (m_IslandSizeX + cChunkDef::Width - 1) / cChunkDef::Width;
- m_MinChunkX = -m_MaxChunkX;
- m_MaxChunkZ = (m_IslandSizeZ + cChunkDef::Width - 1) / cChunkDef::Width;
- m_MinChunkZ = -m_MaxChunkZ;
-}
-
-
-
-
-
-/// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise array)
-void cEndGen::PrepareState(int a_ChunkX, int a_ChunkZ)
-{
- ASSERT(!IsChunkOutsideRange(a_ChunkX, a_ChunkZ)); // Should be filtered before calling this function
-
- if ((m_LastChunkX == a_ChunkX) && (m_LastChunkZ == a_ChunkZ))
- {
- return;
- }
-
- m_LastChunkX = a_ChunkX;
- m_LastChunkZ = a_ChunkZ;
-
- GenerateNoiseArray();
-}
-
-
-
-
-
-/// Generates the m_NoiseArray array for the current chunk
-void cEndGen::GenerateNoiseArray(void)
-{
- NOISE_DATATYPE NoiseData[DIM_X * DIM_Y * DIM_Z]; // [x + DIM_X * z + DIM_X * DIM_Z * y]
- NOISE_DATATYPE Workspace[DIM_X * DIM_Y * DIM_Z]; // [x + DIM_X * z + DIM_X * DIM_Z * y]
-
- // Generate the downscaled noise:
- NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(m_LastChunkX * cChunkDef::Width)) / m_FrequencyX;
- NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((m_LastChunkX + 1) * cChunkDef::Width)) / m_FrequencyX;
- NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(m_LastChunkZ * cChunkDef::Width)) / m_FrequencyZ;
- NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((m_LastChunkZ + 1) * cChunkDef::Width)) / m_FrequencyZ;
- NOISE_DATATYPE StartY = 0;
- NOISE_DATATYPE EndY = ((NOISE_DATATYPE)257) / m_FrequencyY;
- m_Perlin.Generate3D(NoiseData, DIM_X, DIM_Z, DIM_Y, StartX, EndX, StartZ, EndZ, StartY, EndY, Workspace);
-
- // Add distance:
- int idx = 0;
- for (int y = 0; y < DIM_Y; y++)
- {
- NOISE_DATATYPE ValY = (NOISE_DATATYPE)(2 * INTERPOL_Y * y - m_IslandSizeY) / m_IslandSizeY;
- ValY = ValY * ValY;
- for (int z = 0; z < DIM_Z; z++)
- {
- NOISE_DATATYPE ValZ = (NOISE_DATATYPE)(m_LastChunkZ * cChunkDef::Width + (z * cChunkDef::Width / (DIM_Z - 1))) / m_IslandSizeZ;
- ValZ = ValZ * ValZ;
- for (int x = 0; x < DIM_X; x++)
- {
- // NOISE_DATATYPE ValX = StartX + (EndX - StartX) * x / (DIM_X - 1);
- NOISE_DATATYPE ValX = (NOISE_DATATYPE)(m_LastChunkX * cChunkDef::Width + (x * cChunkDef::Width / (DIM_X - 1))) / m_IslandSizeX;
- ValX = ValX * ValX;
- NoiseData[idx++] += ValX + ValZ + ValY;
- } // for x
- } // for z
- } // for y
-
- // Upscale into real chunk size:
- LinearUpscale3DArray(NoiseData, DIM_X, DIM_Z, DIM_Y, m_NoiseArray, INTERPOL_X, INTERPOL_Z, INTERPOL_Y);
-}
-
-
-
-
-
-/// Returns true if the chunk is outside of the island's dimensions
-bool cEndGen::IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ)
-{
- return (
- (a_ChunkX < m_MinChunkX) || (a_ChunkX > m_MaxChunkX) ||
- (a_ChunkZ < m_MinChunkZ) || (a_ChunkZ > m_MaxChunkZ)
- );
-}
-
-
-
-
-
-void cEndGen::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
-{
- if (IsChunkOutsideRange(a_ChunkX, a_ChunkZ))
- {
- for (int i = 0; i < ARRAYCOUNT(a_HeightMap); i++)
- {
- a_HeightMap[i] = 0;
- }
- return;
- }
-
- PrepareState(a_ChunkX, a_ChunkZ);
-
- int MaxY = std::min((int)(1.75 * m_IslandSizeY + 1), cChunkDef::Height - 1);
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- cChunkDef::SetHeight(a_HeightMap, x, z, MaxY);
- for (int y = MaxY; y > 0; y--)
- {
- if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0)
- {
- cChunkDef::SetHeight(a_HeightMap, x, z, y);
- break;
- }
- } // for y
- } // for x
- } // for z
-}
-
-
-
-
-
-void cEndGen::ComposeTerrain(cChunkDesc & a_ChunkDesc)
-{
- if (IsChunkOutsideRange(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()))
- {
- a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
- return;
- }
-
- PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
-
- int MaxY = std::min((int)(1.75 * m_IslandSizeY + 1), cChunkDef::Height - 1);
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- for (int y = MaxY; y > 0; y--)
- {
- if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0)
- {
- a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_END_STONE, 0);
- }
- else
- {
- a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_AIR, 0);
- }
- } // for y
- } // for x
- } // for z
-}
-
-
-
-
+
+// EndGen.cpp
+
+// Implements the cEndGen class representing the generator for the End, both as a HeightGen and CompositionGen
+
+#include "Globals.h"
+#include "EndGen.h"
+#include "../../iniFile/iniFile.h"
+#include "../LinearUpscale.h"
+
+
+
+
+
+enum
+{
+ // Interpolation cell size:
+ INTERPOL_X = 4,
+ INTERPOL_Y = 4,
+ INTERPOL_Z = 4,
+
+ // Size of chunk data, downscaled before interpolation:
+ DIM_X = 16 / INTERPOL_X + 1,
+ DIM_Y = 256 / INTERPOL_Y + 1,
+ DIM_Z = 16 / INTERPOL_Z + 1,
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cEndGen:
+
+cEndGen::cEndGen(int a_Seed) :
+ m_Seed(a_Seed),
+ m_IslandSizeX(256),
+ m_IslandSizeY(96),
+ m_IslandSizeZ(256),
+ m_FrequencyX(80),
+ m_FrequencyY(80),
+ m_FrequencyZ(80)
+{
+ m_Perlin.AddOctave(1, 1);
+ m_Perlin.AddOctave(2, 0.5);
+ m_Perlin.AddOctave(4, 0.25);
+}
+
+
+
+
+
+void cEndGen::Initialize(cIniFile & a_IniFile)
+{
+ m_IslandSizeX = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeX", m_IslandSizeX);
+ m_IslandSizeY = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeY", m_IslandSizeY);
+ m_IslandSizeZ = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeZ", m_IslandSizeZ);
+
+ m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyX", m_FrequencyX);
+ m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyY", m_FrequencyY);
+ m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyZ", m_FrequencyZ);
+
+ // Recalculate the min and max chunk coords of the island
+ m_MaxChunkX = (m_IslandSizeX + cChunkDef::Width - 1) / cChunkDef::Width;
+ m_MinChunkX = -m_MaxChunkX;
+ m_MaxChunkZ = (m_IslandSizeZ + cChunkDef::Width - 1) / cChunkDef::Width;
+ m_MinChunkZ = -m_MaxChunkZ;
+}
+
+
+
+
+
+/// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise array)
+void cEndGen::PrepareState(int a_ChunkX, int a_ChunkZ)
+{
+ ASSERT(!IsChunkOutsideRange(a_ChunkX, a_ChunkZ)); // Should be filtered before calling this function
+
+ if ((m_LastChunkX == a_ChunkX) && (m_LastChunkZ == a_ChunkZ))
+ {
+ return;
+ }
+
+ m_LastChunkX = a_ChunkX;
+ m_LastChunkZ = a_ChunkZ;
+
+ GenerateNoiseArray();
+}
+
+
+
+
+
+/// Generates the m_NoiseArray array for the current chunk
+void cEndGen::GenerateNoiseArray(void)
+{
+ NOISE_DATATYPE NoiseData[DIM_X * DIM_Y * DIM_Z]; // [x + DIM_X * z + DIM_X * DIM_Z * y]
+ NOISE_DATATYPE Workspace[DIM_X * DIM_Y * DIM_Z]; // [x + DIM_X * z + DIM_X * DIM_Z * y]
+
+ // Generate the downscaled noise:
+ NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(m_LastChunkX * cChunkDef::Width)) / m_FrequencyX;
+ NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((m_LastChunkX + 1) * cChunkDef::Width)) / m_FrequencyX;
+ NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(m_LastChunkZ * cChunkDef::Width)) / m_FrequencyZ;
+ NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((m_LastChunkZ + 1) * cChunkDef::Width)) / m_FrequencyZ;
+ NOISE_DATATYPE StartY = 0;
+ NOISE_DATATYPE EndY = ((NOISE_DATATYPE)257) / m_FrequencyY;
+ m_Perlin.Generate3D(NoiseData, DIM_X, DIM_Z, DIM_Y, StartX, EndX, StartZ, EndZ, StartY, EndY, Workspace);
+
+ // Add distance:
+ int idx = 0;
+ for (int y = 0; y < DIM_Y; y++)
+ {
+ NOISE_DATATYPE ValY = (NOISE_DATATYPE)(2 * INTERPOL_Y * y - m_IslandSizeY) / m_IslandSizeY;
+ ValY = ValY * ValY;
+ for (int z = 0; z < DIM_Z; z++)
+ {
+ NOISE_DATATYPE ValZ = (NOISE_DATATYPE)(m_LastChunkZ * cChunkDef::Width + (z * cChunkDef::Width / (DIM_Z - 1))) / m_IslandSizeZ;
+ ValZ = ValZ * ValZ;
+ for (int x = 0; x < DIM_X; x++)
+ {
+ // NOISE_DATATYPE ValX = StartX + (EndX - StartX) * x / (DIM_X - 1);
+ NOISE_DATATYPE ValX = (NOISE_DATATYPE)(m_LastChunkX * cChunkDef::Width + (x * cChunkDef::Width / (DIM_X - 1))) / m_IslandSizeX;
+ ValX = ValX * ValX;
+ NoiseData[idx++] += ValX + ValZ + ValY;
+ } // for x
+ } // for z
+ } // for y
+
+ // Upscale into real chunk size:
+ LinearUpscale3DArray(NoiseData, DIM_X, DIM_Z, DIM_Y, m_NoiseArray, INTERPOL_X, INTERPOL_Z, INTERPOL_Y);
+}
+
+
+
+
+
+/// Returns true if the chunk is outside of the island's dimensions
+bool cEndGen::IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ)
+{
+ return (
+ (a_ChunkX < m_MinChunkX) || (a_ChunkX > m_MaxChunkX) ||
+ (a_ChunkZ < m_MinChunkZ) || (a_ChunkZ > m_MaxChunkZ)
+ );
+}
+
+
+
+
+
+void cEndGen::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+{
+ if (IsChunkOutsideRange(a_ChunkX, a_ChunkZ))
+ {
+ for (int i = 0; i < ARRAYCOUNT(a_HeightMap); i++)
+ {
+ a_HeightMap[i] = 0;
+ }
+ return;
+ }
+
+ PrepareState(a_ChunkX, a_ChunkZ);
+
+ int MaxY = std::min((int)(1.75 * m_IslandSizeY + 1), cChunkDef::Height - 1);
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ cChunkDef::SetHeight(a_HeightMap, x, z, MaxY);
+ for (int y = MaxY; y > 0; y--)
+ {
+ if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0)
+ {
+ cChunkDef::SetHeight(a_HeightMap, x, z, y);
+ break;
+ }
+ } // for y
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cEndGen::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+{
+ if (IsChunkOutsideRange(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()))
+ {
+ a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
+ return;
+ }
+
+ PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
+
+ int MaxY = std::min((int)(1.75 * m_IslandSizeY + 1), cChunkDef::Height - 1);
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ for (int y = MaxY; y > 0; y--)
+ {
+ if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0)
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_END_STONE, 0);
+ }
+ else
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_AIR, 0);
+ }
+ } // for y
+ } // for x
+ } // for z
+}
+
+
+
+
diff --git a/source/Generating/EndGen.h b/source/Generating/EndGen.h
index cd316ed67..4904a0e3d 100644
--- a/source/Generating/EndGen.h
+++ b/source/Generating/EndGen.h
@@ -1,69 +1,69 @@
-
-// EndGen.h
-
-// Declares the cEndGen class representing the generator for the End, both as a HeightGen and CompositionGen
-
-
-
-
-
-#pragma once
-
-#include "ComposableGenerator.h"
-#include "../Noise.h"
-
-
-
-
-
-class cEndGen :
- public cTerrainHeightGen,
- public cTerrainCompositionGen
-{
-public:
- cEndGen(int a_Seed);
-
- void Initialize(cIniFile & a_IniFile);
-
-protected:
-
- /// Seed for the noise
- int m_Seed;
-
- /// The Perlin noise used for generating
- cPerlinNoise m_Perlin;
-
- // XYZ size of the "island", in blocks:
- int m_IslandSizeX;
- int m_IslandSizeY;
- int m_IslandSizeZ;
-
- // XYZ Frequencies of the noise functions:
- NOISE_DATATYPE m_FrequencyX;
- NOISE_DATATYPE m_FrequencyY;
- NOISE_DATATYPE m_FrequencyZ;
-
- // Minimum and maximum chunk coords for chunks inside the island area. Chunks outside won't get calculated at all
- int m_MinChunkX, m_MaxChunkX;
- int m_MinChunkZ, m_MaxChunkZ;
-
- // Noise array for the last chunk (in the noise range)
- int m_LastChunkX;
- int m_LastChunkZ;
- NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y
-
- /// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise array)
- void PrepareState(int a_ChunkX, int a_ChunkZ);
-
- /// Generates the m_NoiseArray array for the current chunk
- void GenerateNoiseArray(void);
-
- /// Returns true if the chunk is outside of the island's dimensions
- bool IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ);
-
- // cTerrainHeightGen overrides:
- virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
-
- // cTerrainCompositionGen overrides:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
-} ;
+
+// EndGen.h
+
+// Declares the cEndGen class representing the generator for the End, both as a HeightGen and CompositionGen
+
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+#include "../Noise.h"
+
+
+
+
+
+class cEndGen :
+ public cTerrainHeightGen,
+ public cTerrainCompositionGen
+{
+public:
+ cEndGen(int a_Seed);
+
+ void Initialize(cIniFile & a_IniFile);
+
+protected:
+
+ /// Seed for the noise
+ int m_Seed;
+
+ /// The Perlin noise used for generating
+ cPerlinNoise m_Perlin;
+
+ // XYZ size of the "island", in blocks:
+ int m_IslandSizeX;
+ int m_IslandSizeY;
+ int m_IslandSizeZ;
+
+ // XYZ Frequencies of the noise functions:
+ NOISE_DATATYPE m_FrequencyX;
+ NOISE_DATATYPE m_FrequencyY;
+ NOISE_DATATYPE m_FrequencyZ;
+
+ // Minimum and maximum chunk coords for chunks inside the island area. Chunks outside won't get calculated at all
+ int m_MinChunkX, m_MaxChunkX;
+ int m_MinChunkZ, m_MaxChunkZ;
+
+ // Noise array for the last chunk (in the noise range)
+ int m_LastChunkX;
+ int m_LastChunkZ;
+ NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y
+
+ /// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise array)
+ void PrepareState(int a_ChunkX, int a_ChunkZ);
+
+ /// Generates the m_NoiseArray array for the current chunk
+ void GenerateNoiseArray(void);
+
+ /// Returns true if the chunk is outside of the island's dimensions
+ bool IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ);
+
+ // cTerrainHeightGen overrides:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+
+ // cTerrainCompositionGen overrides:
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+} ;
diff --git a/source/Generating/MineShafts.cpp b/source/Generating/MineShafts.cpp
index 0f7f78e19..3131b5429 100644
--- a/source/Generating/MineShafts.cpp
+++ b/source/Generating/MineShafts.cpp
@@ -1,1423 +1,1423 @@
-
-// MineShafts.cpp
-
-// Implements the cStructGenMineShafts class representing the structure generator for abandoned mineshafts
-
-/*
-Algorithm:
-The cStructGenMineShafts::cMineShaftSystem class is the main controller, which knows what mineshaft
-classes there are and their random weights. It gets asked to produce a new class everytime a connection is to be made.
-The cMineShaft class is a base class for each mineshaft structure.
-Each cMineShaft descendant knows how large it is, how to imprint itself into the chunk data and where to connect to
-other descendants. Its PivotPoint is always a walkable column. Its Direction determines in which direction the structure
-is facing.
-
-The generation starts with the central dirt room, from there corridors, crossings and staircases are added
-in a depth-first processing. Each of the descendants will branch randomly, if not beyond the allowed recursion level
-*/
-
-#include "Globals.h"
-#include "MineShafts.h"
-#include "../Cuboid.h"
-#include "../BlockEntities/ChestEntity.h"
-
-
-
-
-
-static const int NEIGHBORHOOD_SIZE = 3;
-
-
-
-
-
-class cMineShaft abstract
-{
-public:
- enum eKind
- {
- mskDirtRoom,
- mskCorridor,
- mskCrossing,
- mskStaircase,
- } ;
-
-
- enum eDirection
- {
- dirXP,
- dirZP,
- dirXM,
- dirZM,
- } ;
-
-
- cStructGenMineShafts::cMineShaftSystem & m_ParentSystem;
- eKind m_Kind;
- cCuboid m_BoundingBox;
-
-
- cMineShaft(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, eKind a_Kind) :
- m_ParentSystem(a_ParentSystem),
- m_Kind(a_Kind)
- {
- }
-
- cMineShaft(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, eKind a_Kind, const cCuboid & a_BoundingBox) :
- m_ParentSystem(a_ParentSystem),
- m_Kind(a_Kind),
- m_BoundingBox(a_BoundingBox)
- {
- }
-
- /// Returns true if this mineshaft intersects the specified cuboid
- bool DoesIntersect(const cCuboid & a_Other)
- {
- return m_BoundingBox.DoesIntersect(a_Other);
- }
-
- /** If recursion level is not too large, appends more branches to the parent system,
- using exit points specific to this class.
- */
- virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) = 0;
-
- /// Imprints this shape into the specified chunk's data
- virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) = 0;
-} ;
-
-typedef std::vector<cMineShaft *> cMineShafts;
-
-
-
-
-
-class cMineShaftDirtRoom :
- public cMineShaft
-{
- typedef cMineShaft super;
-
-public:
- cMineShaftDirtRoom(cStructGenMineShafts::cMineShaftSystem & a_Parent, cNoise & a_Noise);
-
- // cMineShaft overrides:
- virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override;
- virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override;
-} ;
-
-
-
-
-
-class cMineShaftCorridor :
- public cMineShaft
-{
- typedef cMineShaft super;
-
-public:
- /** Creates a new Corridor attached to the specified pivot point and direction.
- Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit.
- May return NULL if cannot fit.
- */
- static cMineShaft * CreateAndFit(
- cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
- int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction,
- cNoise & a_Noise
- );
-
-protected:
- static const int MAX_SEGMENTS = 5;
-
- int m_NumSegments;
- eDirection m_Direction;
- bool m_HasFullBeam[MAX_SEGMENTS]; ///< If true, segment at that index has a full beam support (planks in the top center block)
- int m_ChestPosition; ///< If <0, no chest; otherwise an offset from m_BoundingBox's p1.x or p1.z, depenging on m_Direction
- int m_SpawnerPosition; ///< If <0, no spawner; otherwise an offset from m_BoundingBox's p1.x or p1.z, depenging on m_Direction
- bool m_HasTracks; ///< If true, random tracks will be placed on the floor
-
- cMineShaftCorridor(
- cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
- const cCuboid & a_BoundingBox, int a_NumSegments, eDirection a_Direction,
- cNoise & a_Noise
- );
-
- // cMineShaft overrides:
- virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override;
- virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override;
-
- /// Places a chest, if the corridor has one
- void PlaceChest(cChunkDesc & a_ChunkDesc);
-
- /// If this corridor has tracks, places them randomly
- void PlaceTracks(cChunkDesc & a_ChunkDesc);
-
- /// If this corridor has a spawner, places the spawner
- void PlaceSpawner(cChunkDesc & a_ChunkDesc);
-
- /// Randomly places torches around the central beam block
- void PlaceTorches(cChunkDesc & a_ChunkDesc);
-} ;
-
-
-
-
-
-class cMineShaftCrossing :
- public cMineShaft
-{
- typedef cMineShaft super;
-
-public:
- /** Creates a new Crossing attached to the specified pivot point and direction.
- Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit.
- May return NULL if cannot fit.
- */
- static cMineShaft * CreateAndFit(
- cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
- int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction,
- cNoise & a_Noise
- );
-
-protected:
- cMineShaftCrossing(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, const cCuboid & a_BoundingBox);
-
- // cMineShaft overrides:
- virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override;
- virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override;
-} ;
-
-
-
-
-
-class cMineShaftStaircase :
- public cMineShaft
-{
- typedef cMineShaft super;
-
-public:
- enum eSlope
- {
- sUp,
- sDown,
- } ;
-
- /** Creates a new Staircase attached to the specified pivot point and direction.
- Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit.
- May return NULL if cannot fit.
- */
- static cMineShaft * CreateAndFit(
- cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
- int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction,
- cNoise & a_Noise
- );
-
-protected:
- eDirection m_Direction;
- eSlope m_Slope;
-
-
- cMineShaftStaircase(
- cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
- const cCuboid & a_BoundingBox,
- eDirection a_Direction,
- eSlope a_Slope
- );
-
- // cMineShaft overrides:
- virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override;
- virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override;
-} ;
-
-
-
-
-
-class cStructGenMineShafts::cMineShaftSystem
-{
-public:
- int m_BlockX, m_BlockZ; ///< The pivot point on which the system is generated
- int m_GridSize; ///< Maximum offset of the dirtroom from grid center, * 2, in each direction
- int m_MaxRecursion; ///< Maximum recursion level (initialized from cStructGenMineShafts::m_MaxRecursion)
- int m_ProbLevelCorridor; ///< Probability level of a branch object being the corridor
- int m_ProbLevelCrossing; ///< Probability level of a branch object being the crossing, minus Corridor
- int m_ProbLevelStaircase; ///< Probability level of a branch object being the staircase, minus Crossing
- int m_ChanceChest; ///< Chance [0 .. 250] that a corridor has a chest in it
- int m_ChanceSpawner; ///< Chance [0 .. 250] that a corridor has a spawner in it
- int m_ChanceTorch; ///< Chance [0 .. 10k] for a torch appearing attached to a corridor's beam
- cMineShafts m_MineShafts; ///< List of cMineShaft descendants that comprise this system
- cCuboid m_BoundingBox; ///< Bounding box into which all of the components need to fit
-
- /// Creates and generates the entire system
- cMineShaftSystem(
- int a_BlockX, int a_BlockZ, int a_GridSize, int a_MaxSystemSize, cNoise & a_Noise,
- int a_ProbLevelCorridor, int a_ProbLevelCrossing, int a_ProbLevelStaircase
- );
-
- ~cMineShaftSystem();
-
- /// Carves the system into the chunk data
- void ProcessChunk(cChunkDesc & a_Chunk);
-
- /** Creates new cMineShaft descendant connected at the specified point, heading the specified direction,
- if it fits, appends it to the list and calls its AppendBranches()
- */
- void AppendBranch(
- int a_BlockX, int a_BlockY, int a_BlockZ,
- cMineShaft::eDirection a_Direction, cNoise & a_Noise,
- int a_RecursionLevel
- );
-
- /// Returns true if none of the objects in m_MineShafts intersect with the specified bounding box and the bounding box is valid
- bool CanAppend(const cCuboid & a_BoundingBox);
-} ;
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cStructGenMineShafts::cMineShaftSystem:
-
-cStructGenMineShafts::cMineShaftSystem::cMineShaftSystem(
- int a_BlockX, int a_BlockZ, int a_GridSize, int a_MaxSystemSize, cNoise & a_Noise,
- int a_ProbLevelCorridor, int a_ProbLevelCrossing, int a_ProbLevelStaircase
-) :
- m_BlockX(a_BlockX),
- m_BlockZ(a_BlockZ),
- m_GridSize(a_GridSize),
- m_MaxRecursion(8), // TODO: settable
- m_ProbLevelCorridor(a_ProbLevelCorridor),
- m_ProbLevelCrossing(a_ProbLevelCrossing),
- m_ProbLevelStaircase(a_ProbLevelStaircase + 1),
- m_ChanceChest(12), // TODO: settable
- m_ChanceSpawner(12), // TODO: settable
- m_ChanceTorch(1000) // TODO: settable
-{
- m_MineShafts.reserve(100);
-
- cMineShaft * Start = new cMineShaftDirtRoom(*this, a_Noise);
- m_MineShafts.push_back(Start);
-
- m_BoundingBox.Assign(
- Start->m_BoundingBox.p1.x - a_MaxSystemSize / 2, 2, Start->m_BoundingBox.p1.z - a_MaxSystemSize / 2,
- Start->m_BoundingBox.p2.x + a_MaxSystemSize / 2, 50, Start->m_BoundingBox.p2.z + a_MaxSystemSize / 2
- );
-
- Start->AppendBranches(0, a_Noise);
-
- for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr)
- {
- ASSERT((*itr)->m_BoundingBox.IsSorted());
- } // for itr - m_MineShafts[]
-}
-
-
-
-
-
-cStructGenMineShafts::cMineShaftSystem::~cMineShaftSystem()
-{
- for (cMineShafts::iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr)
- {
- delete *itr;
- } // for itr - m_MineShafts[]
- m_MineShafts.clear();
-}
-
-
-
-
-
-void cStructGenMineShafts::cMineShaftSystem::ProcessChunk(cChunkDesc & a_Chunk)
-{
- for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr)
- {
- (*itr)->ProcessChunk(a_Chunk);
- } // for itr - m_MineShafts[]
-}
-
-
-
-
-
-void cStructGenMineShafts::cMineShaftSystem::AppendBranch(
- int a_PivotX, int a_PivotY, int a_PivotZ,
- cMineShaft::eDirection a_Direction, cNoise & a_Noise,
- int a_RecursionLevel
-)
-{
- if (a_RecursionLevel > m_MaxRecursion)
- {
- return;
- }
-
- cMineShaft * Next = NULL;
- int rnd = (a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_RecursionLevel * 16, a_PivotZ) / 13) % m_ProbLevelStaircase;
- if (rnd < m_ProbLevelCorridor)
- {
- Next = cMineShaftCorridor::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise);
- }
- else if (rnd < m_ProbLevelCrossing)
- {
- Next = cMineShaftCrossing::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise);
- }
- else
- {
- Next = cMineShaftStaircase::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise);
- }
- if (Next == NULL)
- {
- return;
- }
- m_MineShafts.push_back(Next);
- Next->AppendBranches(a_RecursionLevel + 1, a_Noise);
-}
-
-
-
-
-
-bool cStructGenMineShafts::cMineShaftSystem::CanAppend(const cCuboid & a_BoundingBox)
-{
- if (!a_BoundingBox.IsCompletelyInside(m_BoundingBox))
- {
- // Too far away, or too low / too high
- return false;
- }
-
- // Check intersections:
- for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr)
- {
- if ((*itr)->DoesIntersect(a_BoundingBox))
- {
- return false;
- }
- } // for itr - m_MineShafts[]
- return true;
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cMineShaftDirtRoom:
-
-cMineShaftDirtRoom::cMineShaftDirtRoom(cStructGenMineShafts::cMineShaftSystem & a_Parent, cNoise & a_Noise) :
- super(a_Parent, mskDirtRoom)
-{
- // Make the room of random size, min 10 x 4 x 10; max 18 x 12 x 18:
- int rnd = a_Noise.IntNoise3DInt(a_Parent.m_BlockX, 0, a_Parent.m_BlockZ) / 7;
- int OfsX = (rnd % a_Parent.m_GridSize) - a_Parent.m_GridSize / 2;
- rnd >>= 12;
- int OfsZ = (rnd % a_Parent.m_GridSize) - a_Parent.m_GridSize / 2;
- rnd = a_Noise.IntNoise3DInt(a_Parent.m_BlockX, 1000, a_Parent.m_BlockZ) / 11;
- m_BoundingBox.p1.x = a_Parent.m_BlockX + OfsX;
- m_BoundingBox.p2.x = m_BoundingBox.p1.x + 10 + (rnd % 8);
- rnd >>= 4;
- m_BoundingBox.p1.z = a_Parent.m_BlockZ + OfsZ;
- m_BoundingBox.p2.z = m_BoundingBox.p1.z + 10 + (rnd % 8);
- rnd >>= 4;
- m_BoundingBox.p1.y = 20;
- m_BoundingBox.p2.y = 24 + rnd % 8;
-}
-
-
-
-
-
-void cMineShaftDirtRoom::AppendBranches(int a_RecursionLevel, cNoise & a_Noise)
-{
- int Height = m_BoundingBox.DifY() - 3;
- for (int x = m_BoundingBox.p1.x + 1; x < m_BoundingBox.p2.x; x += 4)
- {
- int rnd = a_Noise.IntNoise3DInt(x, a_RecursionLevel, m_BoundingBox.p1.z) / 7;
- m_ParentSystem.AppendBranch(x, m_BoundingBox.p1.y + (rnd % Height), m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel);
- rnd >>= 4;
- m_ParentSystem.AppendBranch(x, m_BoundingBox.p1.y + (rnd % Height), m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel);
- }
-
- for (int z = m_BoundingBox.p1.z + 1; z < m_BoundingBox.p2.z; z += 4)
- {
- int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x, a_RecursionLevel, z) / 13;
- m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, m_BoundingBox.p1.y + (rnd % Height), z, dirXM, a_Noise, a_RecursionLevel);
- rnd >>= 4;
- m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, m_BoundingBox.p1.y + (rnd % Height), z, dirXP, a_Noise, a_RecursionLevel);
- }
-}
-
-
-
-
-
-void cMineShaftDirtRoom::ProcessChunk(cChunkDesc & a_ChunkDesc)
-{
- int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
- int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
- if (
- (m_BoundingBox.p1.x > BlockX + cChunkDef::Width) ||
- (m_BoundingBox.p1.z > BlockZ + cChunkDef::Width) ||
- (m_BoundingBox.p2.x < BlockX) ||
- (m_BoundingBox.p2.z < BlockZ)
- )
- {
- // Early bailout - cannot intersect this chunk
- return;
- }
-
- // Chunk-relative coords of the boundaries:
- int MinX = std::max(BlockX, m_BoundingBox.p1.x) - BlockX;
- int MaxX = std::min(BlockX + cChunkDef::Width, m_BoundingBox.p2.x + 1) - BlockX;
- int MinZ = std::max(BlockZ, m_BoundingBox.p1.z) - BlockZ;
- int MaxZ = std::min(BlockZ + cChunkDef::Width, m_BoundingBox.p2.z + 1) - BlockZ;
-
- // Carve the room out:
- for (int z = MinZ; z < MaxZ; z++)
- {
- for (int x = MinX; x < MaxX; x++)
- {
- for (int y = m_BoundingBox.p1.y + 1; y < m_BoundingBox.p2.y; y++)
- {
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR);
- }
- if (a_ChunkDesc.GetBlockType(x, m_BoundingBox.p1.y, z) != E_BLOCK_AIR)
- {
- a_ChunkDesc.SetBlockType(x, m_BoundingBox.p1.y, z, E_BLOCK_DIRT);
- }
- } // for x
- } // for z
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cMineShaftCorridor:
-
-cMineShaftCorridor::cMineShaftCorridor(
- cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
- const cCuboid & a_BoundingBox, int a_NumSegments, eDirection a_Direction,
- cNoise & a_Noise
-) :
- super(a_ParentSystem, mskCorridor, a_BoundingBox),
- m_NumSegments(a_NumSegments),
- m_Direction(a_Direction),
- m_ChestPosition(-1),
- m_SpawnerPosition(-1)
-{
- int rnd = a_Noise.IntNoise3DInt(a_BoundingBox.p1.x, a_BoundingBox.p1.y, a_BoundingBox.p1.z) / 7;
- for (int i = 0; i < a_NumSegments; i++)
- {
- m_HasFullBeam[i] = (rnd % 4) < 3; // 75 % chance of full beam
- rnd >>= 2;
- }
- m_HasTracks = ((rnd % 4) < 2); // 50 % chance of tracks
-
- rnd = a_Noise.IntNoise3DInt(a_BoundingBox.p1.z, a_BoundingBox.p1.x, a_BoundingBox.p1.y) / 7;
- int ChestCheck = rnd % 250;
- rnd >>= 8;
- int SpawnerCheck = rnd % 250;
- rnd >>= 8;
- if (ChestCheck < a_ParentSystem.m_ChanceChest)
- {
- m_ChestPosition = rnd % (a_NumSegments * 5);
- }
- if ((a_NumSegments < 4) && (SpawnerCheck < a_ParentSystem.m_ChanceSpawner))
- {
- m_SpawnerPosition = rnd % (a_NumSegments * 5);
- }
-}
-
-
-
-
-
-cMineShaft * cMineShaftCorridor::CreateAndFit(
- cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
- int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction,
- cNoise & a_Noise
-)
-{
- cCuboid BoundingBox(a_PivotX, a_PivotY - 1, a_PivotZ);
- BoundingBox.p2.y += 3;
- int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7;
- int NumSegments = 2 + (rnd) % (MAX_SEGMENTS - 1); // 2 .. MAX_SEGMENTS
- switch (a_Direction)
- {
- case dirXP: BoundingBox.p2.x += NumSegments * 5 - 1; BoundingBox.p1.z -= 1; BoundingBox.p2.z += 1; break;
- case dirXM: BoundingBox.p1.x -= NumSegments * 5 - 1; BoundingBox.p1.z -= 1; BoundingBox.p2.z += 1; break;
- case dirZP: BoundingBox.p2.z += NumSegments * 5 - 1; BoundingBox.p1.x -= 1; BoundingBox.p2.x += 1; break;
- case dirZM: BoundingBox.p1.z -= NumSegments * 5 - 1; BoundingBox.p1.x -= 1; BoundingBox.p2.x += 1; break;
- }
- if (!a_ParentSystem.CanAppend(BoundingBox))
- {
- return NULL;
- }
- return new cMineShaftCorridor(a_ParentSystem, BoundingBox, NumSegments, a_Direction, a_Noise);
-}
-
-
-
-
-
-void cMineShaftCorridor::AppendBranches(int a_RecursionLevel, cNoise & a_Noise)
-{
- int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 7;
- // Prefer the same height, but allow for up to one block height displacement:
- int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2;
- switch (m_Direction)
- {
- case dirXM:
- {
- m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + 1, dirXM, a_Noise, a_RecursionLevel);
- for (int i = m_NumSegments; i >= 0; i--)
- {
- int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11;
- int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2;
- rnd >>= 6;
- int Ofs = 1 + rnd % (m_NumSegments * 5 - 2);
- m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel);
- m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel);
- }
- break;
- }
-
- case dirXP:
- {
- m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + 1, dirXP, a_Noise, a_RecursionLevel);
- for (int i = m_NumSegments; i >= 0; i--)
- {
- int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11;
- int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2;
- rnd >>= 6;
- int Ofs = 1 + rnd % (m_NumSegments * 5 - 2);
- m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel);
- m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel);
- }
- break;
- }
-
- case dirZM:
- {
- m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel);
- for (int i = m_NumSegments; i >= 0; i--)
- {
- int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11;
- int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2;
- rnd >>= 6;
- int Ofs = 1 + rnd % (m_NumSegments * 5 - 2);
- m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + Ofs, dirXM, a_Noise, a_RecursionLevel);
- m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + Ofs, dirXP, a_Noise, a_RecursionLevel);
- }
- break;
- }
-
- case dirZP:
- {
- m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel);
- for (int i = m_NumSegments; i >= 0; i--)
- {
- int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11;
- int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2;
- rnd >>= 6;
- int Ofs = 1 + rnd % (m_NumSegments * 5 - 2);
- m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + Ofs, dirXM, a_Noise, a_RecursionLevel);
- m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + Ofs, dirXP, a_Noise, a_RecursionLevel);
- }
- break;
- }
- } // switch (m_Direction)
-}
-
-
-
-
-
-void cMineShaftCorridor::ProcessChunk(cChunkDesc & a_ChunkDesc)
-{
- int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
- int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
- cCuboid RelBoundingBox(m_BoundingBox);
- RelBoundingBox.Move(-BlockX, 0, -BlockZ);
- RelBoundingBox.p1.y += 1;
- RelBoundingBox.p2.y -= 1;
- cCuboid Top(RelBoundingBox);
- Top.p2.y += 1;
- Top.p1.y = Top.p2.y;
- a_ChunkDesc.FillRelCuboid(RelBoundingBox, E_BLOCK_AIR, 0);
- a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_AIR, 0, BlockX ^ BlockZ + BlockX, 8000);
- if (m_SpawnerPosition >= 0)
- {
- // Cobwebs around the spider spawner
- a_ChunkDesc.RandomFillRelCuboid(RelBoundingBox, E_BLOCK_COBWEB, 0, BlockX ^ BlockZ + BlockZ, 8000);
- a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_COBWEB, 0, BlockX ^ BlockZ + BlockX, 5000);
- }
- a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_COBWEB, 0, BlockX ^ BlockZ + BlockX + 10, 500);
- RelBoundingBox.p1.y = m_BoundingBox.p1.y;
- RelBoundingBox.p2.y = m_BoundingBox.p1.y;
- a_ChunkDesc.FloorRelCuboid(RelBoundingBox, E_BLOCK_PLANKS, 0);
- switch (m_Direction)
- {
- case dirXM:
- case dirXP:
- {
- int y1 = m_BoundingBox.p1.y + 1;
- int y2 = m_BoundingBox.p1.y + 2;
- int y3 = m_BoundingBox.p1.y + 3;
- int z1 = m_BoundingBox.p1.z - BlockZ;
- int z2 = m_BoundingBox.p2.z - BlockZ;
- for (int i = 0; i < m_NumSegments; i++)
- {
- int x = m_BoundingBox.p1.x + i * 5 + 2 - BlockX;
- if ((x < 0) || (x >= cChunkDef::Width))
- {
- continue;
- }
- if ((z1 >= 0) && (z1 < cChunkDef::Width))
- {
- a_ChunkDesc.SetBlockTypeMeta(x, y1, z1, E_BLOCK_FENCE, 0);
- a_ChunkDesc.SetBlockTypeMeta(x, y2, z1, E_BLOCK_FENCE, 0);
- a_ChunkDesc.SetBlockTypeMeta(x, y3, z1, E_BLOCK_PLANKS, 0);
- }
- if ((z2 >= 0) && (z2 < cChunkDef::Width))
- {
- a_ChunkDesc.SetBlockTypeMeta(x, y1, z2, E_BLOCK_FENCE, 0);
- a_ChunkDesc.SetBlockTypeMeta(x, y2, z2, E_BLOCK_FENCE, 0);
- a_ChunkDesc.SetBlockTypeMeta(x, y3, z2, E_BLOCK_PLANKS, 0);
- }
- if ((z1 >= -1) && (z1 < cChunkDef::Width - 1) && m_HasFullBeam[i])
- {
- a_ChunkDesc.SetBlockTypeMeta(x, y3, z1 + 1, E_BLOCK_PLANKS, 0);
- }
- } // for i - NumSegments
- break;
- }
-
- case dirZM:
- case dirZP:
- {
- int y1 = m_BoundingBox.p1.y + 1;
- int y2 = m_BoundingBox.p1.y + 2;
- int y3 = m_BoundingBox.p1.y + 3;
- int x1 = m_BoundingBox.p1.x - BlockX;
- int x2 = m_BoundingBox.p2.x - BlockX;
- for (int i = 0; i < m_NumSegments; i++)
- {
- int z = m_BoundingBox.p1.z + i * 5 + 2 - BlockZ;
- if ((z < 0) || (z >= cChunkDef::Width))
- {
- continue;
- }
- if ((x1 >= 0) && (x1 < cChunkDef::Width))
- {
- a_ChunkDesc.SetBlockTypeMeta(x1, y1, z, E_BLOCK_FENCE, 0);
- a_ChunkDesc.SetBlockTypeMeta(x1, y2, z, E_BLOCK_FENCE, 0);
- a_ChunkDesc.SetBlockTypeMeta(x1, y3, z, E_BLOCK_PLANKS, 0);
- }
- if ((x2 >= 0) && (x2 < cChunkDef::Width))
- {
- a_ChunkDesc.SetBlockTypeMeta(x2, y1, z, E_BLOCK_FENCE, 0);
- a_ChunkDesc.SetBlockTypeMeta(x2, y2, z, E_BLOCK_FENCE, 0);
- a_ChunkDesc.SetBlockTypeMeta(x2, y3, z, E_BLOCK_PLANKS, 0);
- }
- if ((x1 >= -1) && (x1 < cChunkDef::Width - 1) && m_HasFullBeam[i])
- {
- a_ChunkDesc.SetBlockTypeMeta(x1 + 1, y3, z, E_BLOCK_PLANKS, 0);
- }
- } // for i - NumSegments
- break;
- } // case dirZ?
- } // for i
-
- PlaceChest(a_ChunkDesc);
- PlaceTracks(a_ChunkDesc);
- PlaceSpawner(a_ChunkDesc); // (must be after Tracks!)
- PlaceTorches(a_ChunkDesc);
-}
-
-
-
-
-
-void cMineShaftCorridor::PlaceChest(cChunkDesc & a_ChunkDesc)
-{
- static const cLootProbab LootProbab[] =
- {
- // Item, MinAmount, MaxAmount, Weight
- { cItem(E_ITEM_IRON), 1, 5, 10 },
- { cItem(E_ITEM_GOLD), 1, 3, 5 },
- { cItem(E_ITEM_REDSTONE_DUST), 4, 9, 5 },
- { cItem(E_ITEM_DIAMOND), 1, 2, 3 },
- { cItem(E_ITEM_DYE, 1, 4), 4, 9, 5 }, // lapis lazuli dye
- { cItem(E_ITEM_COAL), 3, 8, 10 },
- { cItem(E_ITEM_BREAD), 1, 3, 15 },
- { cItem(E_ITEM_IRON_PICKAXE), 1, 1, 1 },
- { cItem(E_BLOCK_MINECART_TRACKS), 4, 8, 1 },
- { cItem(E_ITEM_MELON_SEEDS), 2, 4, 10 },
- { cItem(E_ITEM_PUMPKIN_SEEDS), 2, 4, 10 },
- } ;
-
- if (m_ChestPosition < 0)
- {
- return;
- }
-
- int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
- int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
- int x, z;
- NIBBLETYPE Meta = 0;
- switch (m_Direction)
- {
- case dirXM:
- case dirXP:
- {
- x = m_BoundingBox.p1.x + m_ChestPosition - BlockX;
- z = m_BoundingBox.p1.z - BlockZ;
- Meta = E_META_CHEST_FACING_ZP;
- break;
- }
-
- case dirZM:
- case dirZP:
- {
- x = m_BoundingBox.p1.x - BlockX;
- z = m_BoundingBox.p1.z + m_ChestPosition - BlockZ;
- Meta = E_META_CHEST_FACING_XP;
- break;
- }
- } // switch (Dir)
-
- if (
- (x >= 0) && (x < cChunkDef::Width) &&
- (z >= 0) && (z < cChunkDef::Width)
- )
- {
- a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p1.y + 1, z, E_BLOCK_CHEST, Meta);
- cChestEntity * ChestEntity = new cChestEntity(BlockX + x, m_BoundingBox.p1.y + 1, BlockZ + z);
- cNoise Noise(a_ChunkDesc.GetChunkX() ^ a_ChunkDesc.GetChunkZ());
- int NumSlots = 3 + ((Noise.IntNoise3DInt(x, m_BoundingBox.p1.y, z) / 11) % 4);
- int Seed = Noise.IntNoise2DInt(x, z);
- ChestEntity->GetContents().GenerateRandomLootWithBooks(LootProbab, ARRAYCOUNT(LootProbab), NumSlots, Seed);
- a_ChunkDesc.AddBlockEntity(ChestEntity);
- }
-}
-
-
-
-
-
-void cMineShaftCorridor::PlaceTracks(cChunkDesc & a_ChunkDesc)
-{
- if (!m_HasTracks)
- {
- return;
- }
- cCuboid Box(m_BoundingBox);
- Box.Move(-a_ChunkDesc.GetChunkX() * cChunkDef::Width, 1, -a_ChunkDesc.GetChunkZ() * cChunkDef::Width);
- Box.p2.y = Box.p1.y;
- Box.p1.x += 1;
- Box.p2.x -= 1;
- Box.p1.z += 1;
- Box.p2.z -= 1;
- NIBBLETYPE Meta = 0;
- switch (m_Direction)
- {
- case dirXM:
- case dirXP:
- {
- Meta = E_META_TRACKS_X;
- break;
- }
-
- case dirZM:
- case dirZP:
- {
- Meta = E_META_TRACKS_Z;
- break;
- }
- } // switch (direction)
- a_ChunkDesc.RandomFillRelCuboid(Box, E_BLOCK_MINECART_TRACKS, Meta, a_ChunkDesc.GetChunkX() + a_ChunkDesc.GetChunkZ(), 6000);
-}
-
-
-
-
-
-void cMineShaftCorridor::PlaceSpawner(cChunkDesc & a_ChunkDesc)
-{
- if (m_SpawnerPosition < 0)
- {
- // No spawner in this corridor
- return;
- }
- int SpawnerRelX = m_BoundingBox.p1.x + 1 - a_ChunkDesc.GetChunkX() * cChunkDef::Width;
- int SpawnerRelZ = m_BoundingBox.p1.z + 1 - a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
- switch (m_Direction)
- {
- case dirXM:
- case dirXP:
- {
- SpawnerRelX += m_SpawnerPosition - 1;
- break;
- }
- case dirZM:
- case dirZP:
- {
- SpawnerRelZ += m_SpawnerPosition - 1;
- break;
- }
- }
- if (
- (SpawnerRelX >= 0) && (SpawnerRelX < cChunkDef::Width) &&
- (SpawnerRelZ >= 0) && (SpawnerRelZ < cChunkDef::Width)
- )
- {
- a_ChunkDesc.SetBlockTypeMeta(SpawnerRelX, m_BoundingBox.p1.y + 1, SpawnerRelZ, E_BLOCK_MOB_SPAWNER, 0);
- // TODO: The spawner needs its accompanying cMobSpawnerEntity, when implemented
- }
-}
-
-
-
-
-
-void cMineShaftCorridor::PlaceTorches(cChunkDesc & a_ChunkDesc)
-{
- cNoise Noise(m_BoundingBox.p1.x);
- switch (m_Direction)
- {
- case dirXM:
- case dirXP:
- {
- int z = m_BoundingBox.p1.z + 1 - a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
- if ((z < 0) || (z >= cChunkDef::Width))
- {
- return;
- }
- int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
- for (int i = 0; i < m_NumSegments; i++)
- {
- if (!m_HasFullBeam[i])
- {
- continue;
- }
- int x = m_BoundingBox.p1.x + i * 5 + 1 - BlockX;
- if ((x >= 0) && (x < cChunkDef::Width))
- {
- if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch)
- {
- a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_XP);
- }
- }
- x += 2;
- if ((x >= 0) && (x < cChunkDef::Width))
- {
- if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch)
- {
- a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_XM);
- }
- }
- } // for i
- break;
- }
-
- case dirZM:
- case dirZP:
- {
- int x = m_BoundingBox.p1.x + 1 - a_ChunkDesc.GetChunkX() * cChunkDef::Width;
- if ((x < 0) || (x >= cChunkDef::Width))
- {
- return;
- }
- int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
- for (int i = 0; i < m_NumSegments; i++)
- {
- if (!m_HasFullBeam[i])
- {
- continue;
- }
- int z = m_BoundingBox.p1.z + i * 5 + 1 - BlockZ;
- if ((z >= 0) && (z < cChunkDef::Width))
- {
- if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch)
- {
- a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_ZP);
- }
- }
- z += 2;
- if ((z >= 0) && (z < cChunkDef::Width))
- {
- if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch)
- {
- a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_ZM);
- }
- }
- } // for i
- break;
- }
- } // switch (direction)
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cMineShaftCrossing:
-
-cMineShaftCrossing::cMineShaftCrossing(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, const cCuboid & a_BoundingBox) :
- super(a_ParentSystem, mskCrossing, a_BoundingBox)
-{
-}
-
-
-
-
-
-cMineShaft * cMineShaftCrossing::CreateAndFit(
- cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
- int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction,
- cNoise & a_Noise
-)
-{
- cCuboid BoundingBox(a_PivotX, a_PivotY - 1, a_PivotZ);
- int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7;
- BoundingBox.p2.y += 3;
- if ((rnd % 4) < 2)
- {
- // 2-level crossing:
- BoundingBox.p2.y += 4;
- rnd >>= 2;
- if ((rnd % 4) < 2)
- {
- // This is the higher level:
- BoundingBox.p1.y -= 4;
- BoundingBox.p2.y -= 4;
- }
- }
- rnd >>= 2;
- switch (a_Direction)
- {
- case dirXP: BoundingBox.p2.x += 4; BoundingBox.p1.z -= 2; BoundingBox.p2.z += 2; break;
- case dirXM: BoundingBox.p1.x -= 4; BoundingBox.p1.z -= 2; BoundingBox.p2.z += 2; break;
- case dirZP: BoundingBox.p2.z += 4; BoundingBox.p1.x -= 2; BoundingBox.p2.x += 2; break;
- case dirZM: BoundingBox.p1.z -= 4; BoundingBox.p1.x -= 2; BoundingBox.p2.x += 2; break;
- }
- if (!a_ParentSystem.CanAppend(BoundingBox))
- {
- return NULL;
- }
- return new cMineShaftCrossing(a_ParentSystem, BoundingBox);
-}
-
-
-
-
-
-void cMineShaftCrossing::AppendBranches(int a_RecursionLevel, cNoise & a_Noise)
-{
- struct
- {
- int x, y, z;
- eDirection dir;
- } Exits[] =
- {
- // Bottom level:
- {-1, 1, 2, dirXM},
- { 2, 1, -1, dirZM},
- { 5, 1, 2, dirXP},
- { 2, 1, 5, dirZP},
- // Top level:
- {-1, 5, 2, dirXM},
- { 2, 5, -1, dirZM},
- { 5, 5, 2, dirXP},
- { 2, 5, 5, dirZP},
- } ;
- for (int i = 0; i < ARRAYCOUNT(Exits); i++)
- {
- if (m_BoundingBox.p1.y + Exits[i].y >= m_BoundingBox.p2.y)
- {
- // This exit is not available (two-level exit on a one-level crossing)
- continue;
- }
-
- int Height = m_BoundingBox.p1.y + Exits[i].y;
- m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Exits[i].x, Height, m_BoundingBox.p1.z + Exits[i].z, Exits[i].dir, a_Noise, a_RecursionLevel);
- } // for i
-}
-
-
-
-
-
-void cMineShaftCrossing::ProcessChunk(cChunkDesc & a_ChunkDesc)
-{
- int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
- int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
- cCuboid box(m_BoundingBox);
- box.Move(-BlockX, 0, -BlockZ);
- if ((box.p2.x < 0) || (box.p2.z < 0) || (box.p1.x >= cChunkDef::Width) || (box.p1.z > cChunkDef::Width))
- {
- // Does not intersect this chunk
- return;
- }
- int Floor = box.p1.y + 1;
- int Ceil = box.p2.y;
-
- // The supports:
- a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p1.x + 1, Floor, Ceil, box.p1.z + 1, box.p1.z + 1, E_BLOCK_PLANKS, 0);
- a_ChunkDesc.FillRelCuboid(box.p2.x - 1, box.p2.x - 1, Floor, Ceil, box.p1.z + 1, box.p1.z + 1, E_BLOCK_PLANKS, 0);
- a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p1.x + 1, Floor, Ceil, box.p2.z - 1, box.p2.z - 1, E_BLOCK_PLANKS, 0);
- a_ChunkDesc.FillRelCuboid(box.p2.x - 1, box.p2.x - 1, Floor, Ceil, box.p2.z - 1, box.p2.z - 1, E_BLOCK_PLANKS, 0);
-
- // The air in between:
- a_ChunkDesc.FillRelCuboid(box.p1.x + 2, box.p1.x + 2, Floor, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0);
- a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Ceil, box.p1.z + 2, box.p1.z + 2, E_BLOCK_AIR, 0);
-
- // The air on the edges:
- int Mid = Floor + 2;
- a_ChunkDesc.FillRelCuboid(box.p1.x, box.p1.x, Floor, Mid, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0);
- a_ChunkDesc.FillRelCuboid(box.p2.x, box.p2.x, Floor, Mid, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0);
- a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Mid, box.p1.z, box.p1.z, E_BLOCK_AIR, 0);
- a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Mid, box.p2.z, box.p2.z, E_BLOCK_AIR, 0);
- Mid += 2;
- if (Mid < Ceil)
- {
- a_ChunkDesc.FillRelCuboid(box.p1.x, box.p1.x, Mid, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0);
- a_ChunkDesc.FillRelCuboid(box.p2.x, box.p2.x, Mid, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0);
- a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Mid, Ceil, box.p1.z, box.p1.z, E_BLOCK_AIR, 0);
- a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Mid, Ceil, box.p2.z, box.p2.z, E_BLOCK_AIR, 0);
- }
-
- // The floor, if needed:
- box.p2.y = box.p1.y;
- a_ChunkDesc.FloorRelCuboid(box, E_BLOCK_PLANKS, 0);
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cMineShaftStaircase:
-
-cMineShaftStaircase::cMineShaftStaircase(
- cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
- const cCuboid & a_BoundingBox,
- eDirection a_Direction,
- eSlope a_Slope
-) :
- super(a_ParentSystem, mskStaircase, a_BoundingBox),
- m_Direction(a_Direction),
- m_Slope(a_Slope)
-{
-}
-
-
-
-
-
-cMineShaft * cMineShaftStaircase::CreateAndFit(
- cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
- int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction,
- cNoise & a_Noise
-)
-{
- int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7;
- cCuboid Box;
- switch (a_Direction)
- {
- case dirXM:
- {
- Box.Assign(a_PivotX - 7, a_PivotY - 1, a_PivotZ - 1, a_PivotX, a_PivotY + 6, a_PivotZ + 1);
- break;
- }
- case dirXP:
- {
- Box.Assign(a_PivotX, a_PivotY - 1, a_PivotZ - 1, a_PivotX + 7, a_PivotY + 6, a_PivotZ + 1);
- break;
- }
- case dirZM:
- {
- Box.Assign(a_PivotX - 1, a_PivotY - 1, a_PivotZ - 7, a_PivotX + 1, a_PivotY + 6, a_PivotZ);
- break;
- }
- case dirZP:
- {
- Box.Assign(a_PivotX - 1, a_PivotY - 1, a_PivotZ, a_PivotX + 1, a_PivotY + 6, a_PivotZ + 7);
- break;
- }
- }
- eSlope Slope = sUp;
- if ((rnd % 4) < 2) // 50 %
- {
- Slope = sDown;
- Box.Move(0, -4, 0);
- }
- if (!a_ParentSystem.CanAppend(Box))
- {
- return NULL;
- }
- return new cMineShaftStaircase(a_ParentSystem, Box, a_Direction, Slope);
-}
-
-
-
-
-
-void cMineShaftStaircase::AppendBranches(int a_RecursionLevel, cNoise & a_Noise)
-{
- int Height = m_BoundingBox.p1.y + ((m_Slope == sDown) ? 1 : 5);
- switch (m_Direction)
- {
- case dirXM: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + 1, dirXM, a_Noise, a_RecursionLevel); break;
- case dirXP: m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + 1, dirXP, a_Noise, a_RecursionLevel); break;
- case dirZM: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); break;
- case dirZP: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); break;
- }
-}
-
-
-
-
-
-void cMineShaftStaircase::ProcessChunk(cChunkDesc & a_ChunkDesc)
-{
- int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
- int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
- cCuboid RelB(m_BoundingBox);
- RelB.Move(-BlockX, 0, -BlockZ);
- if (
- (RelB.p1.x >= cChunkDef::Width) ||
- (RelB.p1.z >= cChunkDef::Width) ||
- (RelB.p2.x < 0) ||
- (RelB.p2.z < 0)
- )
- {
- // No intersection between this staircase and this chunk
- return;
- }
-
- int SFloor = RelB.p1.y + ((m_Slope == sDown) ? 5 : 1);
- int DFloor = RelB.p1.y + ((m_Slope == sDown) ? 1 : 5);
- int Add = (m_Slope == sDown) ? -1 : 1;
- int InitAdd = (m_Slope == sDown) ? -1 : 0;
- cCuboid Box;
- switch (m_Direction)
- {
- case dirXM:
- {
- a_ChunkDesc.FillRelCuboid (RelB.p2.x - 1, RelB.p2.x, SFloor, SFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0);
- a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p1.x + 1, DFloor, DFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0);
- a_ChunkDesc.FloorRelCuboid(RelB.p2.x - 1, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0);
- a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p1.x + 1, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0);
- Box.Assign(RelB.p2.x - 2, SFloor + InitAdd, RelB.p1.z, RelB.p2.x - 2, SFloor + 3 + InitAdd, RelB.p2.z);
- for (int i = 0; i < 4; i++)
- {
- a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0);
- a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0);
- Box.Move(-1, Add, 0);
- }
- break;
- }
-
- case dirXP:
- {
- a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p1.x + 1, SFloor, SFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0);
- a_ChunkDesc.FillRelCuboid (RelB.p2.x - 1, RelB.p2.x, DFloor, DFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0);
- a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p1.x + 1, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0);
- a_ChunkDesc.FloorRelCuboid(RelB.p2.x - 1, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0);
- Box.Assign(RelB.p1.x + 2, SFloor + InitAdd, RelB.p1.z, RelB.p1.x + 2, SFloor + 3 + InitAdd, RelB.p2.z);
- for (int i = 0; i < 4; i++)
- {
- a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0);
- a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0);
- Box.Move(1, Add, 0);
- }
- break;
- }
-
- case dirZM:
- {
- a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, SFloor, SFloor + 2, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_AIR, 0);
- a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, DFloor, DFloor + 2, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_AIR, 0);
- a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_PLANKS, 0);
- a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_PLANKS, 0);
- Box.Assign(RelB.p1.x, SFloor + InitAdd, RelB.p2.z - 2, RelB.p2.x, SFloor + 3 + InitAdd, RelB.p2.z - 2);
- for (int i = 0; i < 4; i++)
- {
- a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0);
- a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0);
- Box.Move(0, Add, -1);
- }
- break;
- }
-
- case dirZP:
- {
- a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, SFloor, SFloor + 2, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_AIR, 0);
- a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, DFloor, DFloor + 2, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_AIR, 0);
- a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_PLANKS, 0);
- a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_PLANKS, 0);
- Box.Assign(RelB.p1.x, SFloor + InitAdd, RelB.p1.z + 2, RelB.p2.x, SFloor + 3 + InitAdd, RelB.p1.z + 2);
- for (int i = 0; i < 4; i++)
- {
- a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0);
- a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0);
- Box.Move(0, Add, 1);
- }
- break;
- }
-
- } // switch (m_Direction)
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cStructGenMineShafts:
-
-cStructGenMineShafts::cStructGenMineShafts(
- int a_Seed, int a_GridSize, int a_MaxSystemSize,
- int a_ChanceCorridor, int a_ChanceCrossing, int a_ChanceStaircase
-) :
- m_Noise(a_Seed),
- m_GridSize(a_GridSize),
- m_MaxSystemSize(a_MaxSystemSize),
- m_ProbLevelCorridor(std::max(0, a_ChanceCorridor)),
- m_ProbLevelCrossing(std::max(0, a_ChanceCorridor + a_ChanceCrossing)),
- m_ProbLevelStaircase(std::max(0, a_ChanceCorridor + a_ChanceCrossing + a_ChanceStaircase))
-{
-}
-
-
-
-
-
-cStructGenMineShafts::~cStructGenMineShafts()
-{
- ClearCache();
-}
-
-
-
-
-
-void cStructGenMineShafts::ClearCache(void)
-{
- for (cMineShaftSystems::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr)
- {
- delete *itr;
- } // for itr - m_Cache[]
- m_Cache.clear();
-}
-
-
-
-
-
-void cStructGenMineShafts::GetMineShaftSystemsForChunk(
- int a_ChunkX, int a_ChunkZ,
- cStructGenMineShafts::cMineShaftSystems & a_MineShafts
-)
-{
- int BaseX = a_ChunkX * cChunkDef::Width / m_GridSize;
- int BaseZ = a_ChunkZ * cChunkDef::Width / m_GridSize;
- if (BaseX < 0)
- {
- --BaseX;
- }
- if (BaseZ < 0)
- {
- --BaseZ;
- }
- BaseX -= NEIGHBORHOOD_SIZE / 2;
- BaseZ -= NEIGHBORHOOD_SIZE / 2;
-
- // Walk the cache, move each cave system that we want into a_Caves:
- int StartX = BaseX * m_GridSize;
- int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_GridSize;
- int StartZ = BaseZ * m_GridSize;
- int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_GridSize;
- for (cMineShaftSystems::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;)
- {
- if (
- ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) &&
- ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ)
- )
- {
- // want
- a_MineShafts.push_back(*itr);
- itr = m_Cache.erase(itr);
- }
- else
- {
- // don't want
- ++itr;
- }
- } // for itr - m_Cache[]
-
- for (int x = 0; x < NEIGHBORHOOD_SIZE; x++)
- {
- int RealX = (BaseX + x) * m_GridSize;
- for (int z = 0; z < NEIGHBORHOOD_SIZE; z++)
- {
- int RealZ = (BaseZ + z) * m_GridSize;
- bool Found = false;
- for (cMineShaftSystems::const_iterator itr = a_MineShafts.begin(), end = a_MineShafts.end(); itr != end; ++itr)
- {
- if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ))
- {
- Found = true;
- break;
- }
- } // for itr - a_Mineshafts
- if (!Found)
- {
- a_MineShafts.push_back(new cMineShaftSystem(RealX, RealZ, m_GridSize, m_MaxSystemSize, m_Noise, m_ProbLevelCorridor, m_ProbLevelCrossing, m_ProbLevelStaircase));
- }
- } // for z
- } // for x
-
- // Copy a_MineShafts into m_Cache to the beginning:
- cMineShaftSystems MineShaftsCopy(a_MineShafts);
- m_Cache.splice(m_Cache.begin(), MineShaftsCopy, MineShaftsCopy.begin(), MineShaftsCopy.end());
-
- // Trim the cache if it's too long:
- if (m_Cache.size() > 100)
- {
- cMineShaftSystems::iterator itr = m_Cache.begin();
- std::advance(itr, 100);
- for (cMineShaftSystems::iterator end = m_Cache.end(); itr != end; ++itr)
- {
- delete *itr;
- }
- itr = m_Cache.begin();
- std::advance(itr, 100);
- m_Cache.erase(itr, m_Cache.end());
- }
-}
-
-
-
-
-
-
-void cStructGenMineShafts::GenStructures(cChunkDesc & a_ChunkDesc)
-{
- int ChunkX = a_ChunkDesc.GetChunkX();
- int ChunkZ = a_ChunkDesc.GetChunkZ();
- cMineShaftSystems MineShafts;
- GetMineShaftSystemsForChunk(ChunkX, ChunkZ, MineShafts);
- for (cMineShaftSystems::const_iterator itr = MineShafts.begin(); itr != MineShafts.end(); ++itr)
- {
- (*itr)->ProcessChunk(a_ChunkDesc);
- } // for itr - MineShafts[]
-}
-
-
-
-
+
+// MineShafts.cpp
+
+// Implements the cStructGenMineShafts class representing the structure generator for abandoned mineshafts
+
+/*
+Algorithm:
+The cStructGenMineShafts::cMineShaftSystem class is the main controller, which knows what mineshaft
+classes there are and their random weights. It gets asked to produce a new class everytime a connection is to be made.
+The cMineShaft class is a base class for each mineshaft structure.
+Each cMineShaft descendant knows how large it is, how to imprint itself into the chunk data and where to connect to
+other descendants. Its PivotPoint is always a walkable column. Its Direction determines in which direction the structure
+is facing.
+
+The generation starts with the central dirt room, from there corridors, crossings and staircases are added
+in a depth-first processing. Each of the descendants will branch randomly, if not beyond the allowed recursion level
+*/
+
+#include "Globals.h"
+#include "MineShafts.h"
+#include "../Cuboid.h"
+#include "../BlockEntities/ChestEntity.h"
+
+
+
+
+
+static const int NEIGHBORHOOD_SIZE = 3;
+
+
+
+
+
+class cMineShaft abstract
+{
+public:
+ enum eKind
+ {
+ mskDirtRoom,
+ mskCorridor,
+ mskCrossing,
+ mskStaircase,
+ } ;
+
+
+ enum eDirection
+ {
+ dirXP,
+ dirZP,
+ dirXM,
+ dirZM,
+ } ;
+
+
+ cStructGenMineShafts::cMineShaftSystem & m_ParentSystem;
+ eKind m_Kind;
+ cCuboid m_BoundingBox;
+
+
+ cMineShaft(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, eKind a_Kind) :
+ m_ParentSystem(a_ParentSystem),
+ m_Kind(a_Kind)
+ {
+ }
+
+ cMineShaft(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, eKind a_Kind, const cCuboid & a_BoundingBox) :
+ m_ParentSystem(a_ParentSystem),
+ m_Kind(a_Kind),
+ m_BoundingBox(a_BoundingBox)
+ {
+ }
+
+ /// Returns true if this mineshaft intersects the specified cuboid
+ bool DoesIntersect(const cCuboid & a_Other)
+ {
+ return m_BoundingBox.DoesIntersect(a_Other);
+ }
+
+ /** If recursion level is not too large, appends more branches to the parent system,
+ using exit points specific to this class.
+ */
+ virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) = 0;
+
+ /// Imprints this shape into the specified chunk's data
+ virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) = 0;
+} ;
+
+typedef std::vector<cMineShaft *> cMineShafts;
+
+
+
+
+
+class cMineShaftDirtRoom :
+ public cMineShaft
+{
+ typedef cMineShaft super;
+
+public:
+ cMineShaftDirtRoom(cStructGenMineShafts::cMineShaftSystem & a_Parent, cNoise & a_Noise);
+
+ // cMineShaft overrides:
+ virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override;
+ virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
+class cMineShaftCorridor :
+ public cMineShaft
+{
+ typedef cMineShaft super;
+
+public:
+ /** Creates a new Corridor attached to the specified pivot point and direction.
+ Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit.
+ May return NULL if cannot fit.
+ */
+ static cMineShaft * CreateAndFit(
+ cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
+ int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction,
+ cNoise & a_Noise
+ );
+
+protected:
+ static const int MAX_SEGMENTS = 5;
+
+ int m_NumSegments;
+ eDirection m_Direction;
+ bool m_HasFullBeam[MAX_SEGMENTS]; ///< If true, segment at that index has a full beam support (planks in the top center block)
+ int m_ChestPosition; ///< If <0, no chest; otherwise an offset from m_BoundingBox's p1.x or p1.z, depenging on m_Direction
+ int m_SpawnerPosition; ///< If <0, no spawner; otherwise an offset from m_BoundingBox's p1.x or p1.z, depenging on m_Direction
+ bool m_HasTracks; ///< If true, random tracks will be placed on the floor
+
+ cMineShaftCorridor(
+ cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
+ const cCuboid & a_BoundingBox, int a_NumSegments, eDirection a_Direction,
+ cNoise & a_Noise
+ );
+
+ // cMineShaft overrides:
+ virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override;
+ virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override;
+
+ /// Places a chest, if the corridor has one
+ void PlaceChest(cChunkDesc & a_ChunkDesc);
+
+ /// If this corridor has tracks, places them randomly
+ void PlaceTracks(cChunkDesc & a_ChunkDesc);
+
+ /// If this corridor has a spawner, places the spawner
+ void PlaceSpawner(cChunkDesc & a_ChunkDesc);
+
+ /// Randomly places torches around the central beam block
+ void PlaceTorches(cChunkDesc & a_ChunkDesc);
+} ;
+
+
+
+
+
+class cMineShaftCrossing :
+ public cMineShaft
+{
+ typedef cMineShaft super;
+
+public:
+ /** Creates a new Crossing attached to the specified pivot point and direction.
+ Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit.
+ May return NULL if cannot fit.
+ */
+ static cMineShaft * CreateAndFit(
+ cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
+ int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction,
+ cNoise & a_Noise
+ );
+
+protected:
+ cMineShaftCrossing(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, const cCuboid & a_BoundingBox);
+
+ // cMineShaft overrides:
+ virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override;
+ virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
+class cMineShaftStaircase :
+ public cMineShaft
+{
+ typedef cMineShaft super;
+
+public:
+ enum eSlope
+ {
+ sUp,
+ sDown,
+ } ;
+
+ /** Creates a new Staircase attached to the specified pivot point and direction.
+ Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit.
+ May return NULL if cannot fit.
+ */
+ static cMineShaft * CreateAndFit(
+ cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
+ int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction,
+ cNoise & a_Noise
+ );
+
+protected:
+ eDirection m_Direction;
+ eSlope m_Slope;
+
+
+ cMineShaftStaircase(
+ cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
+ const cCuboid & a_BoundingBox,
+ eDirection a_Direction,
+ eSlope a_Slope
+ );
+
+ // cMineShaft overrides:
+ virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override;
+ virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
+class cStructGenMineShafts::cMineShaftSystem
+{
+public:
+ int m_BlockX, m_BlockZ; ///< The pivot point on which the system is generated
+ int m_GridSize; ///< Maximum offset of the dirtroom from grid center, * 2, in each direction
+ int m_MaxRecursion; ///< Maximum recursion level (initialized from cStructGenMineShafts::m_MaxRecursion)
+ int m_ProbLevelCorridor; ///< Probability level of a branch object being the corridor
+ int m_ProbLevelCrossing; ///< Probability level of a branch object being the crossing, minus Corridor
+ int m_ProbLevelStaircase; ///< Probability level of a branch object being the staircase, minus Crossing
+ int m_ChanceChest; ///< Chance [0 .. 250] that a corridor has a chest in it
+ int m_ChanceSpawner; ///< Chance [0 .. 250] that a corridor has a spawner in it
+ int m_ChanceTorch; ///< Chance [0 .. 10k] for a torch appearing attached to a corridor's beam
+ cMineShafts m_MineShafts; ///< List of cMineShaft descendants that comprise this system
+ cCuboid m_BoundingBox; ///< Bounding box into which all of the components need to fit
+
+ /// Creates and generates the entire system
+ cMineShaftSystem(
+ int a_BlockX, int a_BlockZ, int a_GridSize, int a_MaxSystemSize, cNoise & a_Noise,
+ int a_ProbLevelCorridor, int a_ProbLevelCrossing, int a_ProbLevelStaircase
+ );
+
+ ~cMineShaftSystem();
+
+ /// Carves the system into the chunk data
+ void ProcessChunk(cChunkDesc & a_Chunk);
+
+ /** Creates new cMineShaft descendant connected at the specified point, heading the specified direction,
+ if it fits, appends it to the list and calls its AppendBranches()
+ */
+ void AppendBranch(
+ int a_BlockX, int a_BlockY, int a_BlockZ,
+ cMineShaft::eDirection a_Direction, cNoise & a_Noise,
+ int a_RecursionLevel
+ );
+
+ /// Returns true if none of the objects in m_MineShafts intersect with the specified bounding box and the bounding box is valid
+ bool CanAppend(const cCuboid & a_BoundingBox);
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenMineShafts::cMineShaftSystem:
+
+cStructGenMineShafts::cMineShaftSystem::cMineShaftSystem(
+ int a_BlockX, int a_BlockZ, int a_GridSize, int a_MaxSystemSize, cNoise & a_Noise,
+ int a_ProbLevelCorridor, int a_ProbLevelCrossing, int a_ProbLevelStaircase
+) :
+ m_BlockX(a_BlockX),
+ m_BlockZ(a_BlockZ),
+ m_GridSize(a_GridSize),
+ m_MaxRecursion(8), // TODO: settable
+ m_ProbLevelCorridor(a_ProbLevelCorridor),
+ m_ProbLevelCrossing(a_ProbLevelCrossing),
+ m_ProbLevelStaircase(a_ProbLevelStaircase + 1),
+ m_ChanceChest(12), // TODO: settable
+ m_ChanceSpawner(12), // TODO: settable
+ m_ChanceTorch(1000) // TODO: settable
+{
+ m_MineShafts.reserve(100);
+
+ cMineShaft * Start = new cMineShaftDirtRoom(*this, a_Noise);
+ m_MineShafts.push_back(Start);
+
+ m_BoundingBox.Assign(
+ Start->m_BoundingBox.p1.x - a_MaxSystemSize / 2, 2, Start->m_BoundingBox.p1.z - a_MaxSystemSize / 2,
+ Start->m_BoundingBox.p2.x + a_MaxSystemSize / 2, 50, Start->m_BoundingBox.p2.z + a_MaxSystemSize / 2
+ );
+
+ Start->AppendBranches(0, a_Noise);
+
+ for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr)
+ {
+ ASSERT((*itr)->m_BoundingBox.IsSorted());
+ } // for itr - m_MineShafts[]
+}
+
+
+
+
+
+cStructGenMineShafts::cMineShaftSystem::~cMineShaftSystem()
+{
+ for (cMineShafts::iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr)
+ {
+ delete *itr;
+ } // for itr - m_MineShafts[]
+ m_MineShafts.clear();
+}
+
+
+
+
+
+void cStructGenMineShafts::cMineShaftSystem::ProcessChunk(cChunkDesc & a_Chunk)
+{
+ for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr)
+ {
+ (*itr)->ProcessChunk(a_Chunk);
+ } // for itr - m_MineShafts[]
+}
+
+
+
+
+
+void cStructGenMineShafts::cMineShaftSystem::AppendBranch(
+ int a_PivotX, int a_PivotY, int a_PivotZ,
+ cMineShaft::eDirection a_Direction, cNoise & a_Noise,
+ int a_RecursionLevel
+)
+{
+ if (a_RecursionLevel > m_MaxRecursion)
+ {
+ return;
+ }
+
+ cMineShaft * Next = NULL;
+ int rnd = (a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_RecursionLevel * 16, a_PivotZ) / 13) % m_ProbLevelStaircase;
+ if (rnd < m_ProbLevelCorridor)
+ {
+ Next = cMineShaftCorridor::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise);
+ }
+ else if (rnd < m_ProbLevelCrossing)
+ {
+ Next = cMineShaftCrossing::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise);
+ }
+ else
+ {
+ Next = cMineShaftStaircase::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise);
+ }
+ if (Next == NULL)
+ {
+ return;
+ }
+ m_MineShafts.push_back(Next);
+ Next->AppendBranches(a_RecursionLevel + 1, a_Noise);
+}
+
+
+
+
+
+bool cStructGenMineShafts::cMineShaftSystem::CanAppend(const cCuboid & a_BoundingBox)
+{
+ if (!a_BoundingBox.IsCompletelyInside(m_BoundingBox))
+ {
+ // Too far away, or too low / too high
+ return false;
+ }
+
+ // Check intersections:
+ for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr)
+ {
+ if ((*itr)->DoesIntersect(a_BoundingBox))
+ {
+ return false;
+ }
+ } // for itr - m_MineShafts[]
+ return true;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cMineShaftDirtRoom:
+
+cMineShaftDirtRoom::cMineShaftDirtRoom(cStructGenMineShafts::cMineShaftSystem & a_Parent, cNoise & a_Noise) :
+ super(a_Parent, mskDirtRoom)
+{
+ // Make the room of random size, min 10 x 4 x 10; max 18 x 12 x 18:
+ int rnd = a_Noise.IntNoise3DInt(a_Parent.m_BlockX, 0, a_Parent.m_BlockZ) / 7;
+ int OfsX = (rnd % a_Parent.m_GridSize) - a_Parent.m_GridSize / 2;
+ rnd >>= 12;
+ int OfsZ = (rnd % a_Parent.m_GridSize) - a_Parent.m_GridSize / 2;
+ rnd = a_Noise.IntNoise3DInt(a_Parent.m_BlockX, 1000, a_Parent.m_BlockZ) / 11;
+ m_BoundingBox.p1.x = a_Parent.m_BlockX + OfsX;
+ m_BoundingBox.p2.x = m_BoundingBox.p1.x + 10 + (rnd % 8);
+ rnd >>= 4;
+ m_BoundingBox.p1.z = a_Parent.m_BlockZ + OfsZ;
+ m_BoundingBox.p2.z = m_BoundingBox.p1.z + 10 + (rnd % 8);
+ rnd >>= 4;
+ m_BoundingBox.p1.y = 20;
+ m_BoundingBox.p2.y = 24 + rnd % 8;
+}
+
+
+
+
+
+void cMineShaftDirtRoom::AppendBranches(int a_RecursionLevel, cNoise & a_Noise)
+{
+ int Height = m_BoundingBox.DifY() - 3;
+ for (int x = m_BoundingBox.p1.x + 1; x < m_BoundingBox.p2.x; x += 4)
+ {
+ int rnd = a_Noise.IntNoise3DInt(x, a_RecursionLevel, m_BoundingBox.p1.z) / 7;
+ m_ParentSystem.AppendBranch(x, m_BoundingBox.p1.y + (rnd % Height), m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel);
+ rnd >>= 4;
+ m_ParentSystem.AppendBranch(x, m_BoundingBox.p1.y + (rnd % Height), m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel);
+ }
+
+ for (int z = m_BoundingBox.p1.z + 1; z < m_BoundingBox.p2.z; z += 4)
+ {
+ int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x, a_RecursionLevel, z) / 13;
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, m_BoundingBox.p1.y + (rnd % Height), z, dirXM, a_Noise, a_RecursionLevel);
+ rnd >>= 4;
+ m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, m_BoundingBox.p1.y + (rnd % Height), z, dirXP, a_Noise, a_RecursionLevel);
+ }
+}
+
+
+
+
+
+void cMineShaftDirtRoom::ProcessChunk(cChunkDesc & a_ChunkDesc)
+{
+ int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ if (
+ (m_BoundingBox.p1.x > BlockX + cChunkDef::Width) ||
+ (m_BoundingBox.p1.z > BlockZ + cChunkDef::Width) ||
+ (m_BoundingBox.p2.x < BlockX) ||
+ (m_BoundingBox.p2.z < BlockZ)
+ )
+ {
+ // Early bailout - cannot intersect this chunk
+ return;
+ }
+
+ // Chunk-relative coords of the boundaries:
+ int MinX = std::max(BlockX, m_BoundingBox.p1.x) - BlockX;
+ int MaxX = std::min(BlockX + cChunkDef::Width, m_BoundingBox.p2.x + 1) - BlockX;
+ int MinZ = std::max(BlockZ, m_BoundingBox.p1.z) - BlockZ;
+ int MaxZ = std::min(BlockZ + cChunkDef::Width, m_BoundingBox.p2.z + 1) - BlockZ;
+
+ // Carve the room out:
+ for (int z = MinZ; z < MaxZ; z++)
+ {
+ for (int x = MinX; x < MaxX; x++)
+ {
+ for (int y = m_BoundingBox.p1.y + 1; y < m_BoundingBox.p2.y; y++)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR);
+ }
+ if (a_ChunkDesc.GetBlockType(x, m_BoundingBox.p1.y, z) != E_BLOCK_AIR)
+ {
+ a_ChunkDesc.SetBlockType(x, m_BoundingBox.p1.y, z, E_BLOCK_DIRT);
+ }
+ } // for x
+ } // for z
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cMineShaftCorridor:
+
+cMineShaftCorridor::cMineShaftCorridor(
+ cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
+ const cCuboid & a_BoundingBox, int a_NumSegments, eDirection a_Direction,
+ cNoise & a_Noise
+) :
+ super(a_ParentSystem, mskCorridor, a_BoundingBox),
+ m_NumSegments(a_NumSegments),
+ m_Direction(a_Direction),
+ m_ChestPosition(-1),
+ m_SpawnerPosition(-1)
+{
+ int rnd = a_Noise.IntNoise3DInt(a_BoundingBox.p1.x, a_BoundingBox.p1.y, a_BoundingBox.p1.z) / 7;
+ for (int i = 0; i < a_NumSegments; i++)
+ {
+ m_HasFullBeam[i] = (rnd % 4) < 3; // 75 % chance of full beam
+ rnd >>= 2;
+ }
+ m_HasTracks = ((rnd % 4) < 2); // 50 % chance of tracks
+
+ rnd = a_Noise.IntNoise3DInt(a_BoundingBox.p1.z, a_BoundingBox.p1.x, a_BoundingBox.p1.y) / 7;
+ int ChestCheck = rnd % 250;
+ rnd >>= 8;
+ int SpawnerCheck = rnd % 250;
+ rnd >>= 8;
+ if (ChestCheck < a_ParentSystem.m_ChanceChest)
+ {
+ m_ChestPosition = rnd % (a_NumSegments * 5);
+ }
+ if ((a_NumSegments < 4) && (SpawnerCheck < a_ParentSystem.m_ChanceSpawner))
+ {
+ m_SpawnerPosition = rnd % (a_NumSegments * 5);
+ }
+}
+
+
+
+
+
+cMineShaft * cMineShaftCorridor::CreateAndFit(
+ cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
+ int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction,
+ cNoise & a_Noise
+)
+{
+ cCuboid BoundingBox(a_PivotX, a_PivotY - 1, a_PivotZ);
+ BoundingBox.p2.y += 3;
+ int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7;
+ int NumSegments = 2 + (rnd) % (MAX_SEGMENTS - 1); // 2 .. MAX_SEGMENTS
+ switch (a_Direction)
+ {
+ case dirXP: BoundingBox.p2.x += NumSegments * 5 - 1; BoundingBox.p1.z -= 1; BoundingBox.p2.z += 1; break;
+ case dirXM: BoundingBox.p1.x -= NumSegments * 5 - 1; BoundingBox.p1.z -= 1; BoundingBox.p2.z += 1; break;
+ case dirZP: BoundingBox.p2.z += NumSegments * 5 - 1; BoundingBox.p1.x -= 1; BoundingBox.p2.x += 1; break;
+ case dirZM: BoundingBox.p1.z -= NumSegments * 5 - 1; BoundingBox.p1.x -= 1; BoundingBox.p2.x += 1; break;
+ }
+ if (!a_ParentSystem.CanAppend(BoundingBox))
+ {
+ return NULL;
+ }
+ return new cMineShaftCorridor(a_ParentSystem, BoundingBox, NumSegments, a_Direction, a_Noise);
+}
+
+
+
+
+
+void cMineShaftCorridor::AppendBranches(int a_RecursionLevel, cNoise & a_Noise)
+{
+ int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 7;
+ // Prefer the same height, but allow for up to one block height displacement:
+ int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2;
+ switch (m_Direction)
+ {
+ case dirXM:
+ {
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + 1, dirXM, a_Noise, a_RecursionLevel);
+ for (int i = m_NumSegments; i >= 0; i--)
+ {
+ int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11;
+ int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2;
+ rnd >>= 6;
+ int Ofs = 1 + rnd % (m_NumSegments * 5 - 2);
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel);
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel);
+ }
+ break;
+ }
+
+ case dirXP:
+ {
+ m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + 1, dirXP, a_Noise, a_RecursionLevel);
+ for (int i = m_NumSegments; i >= 0; i--)
+ {
+ int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11;
+ int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2;
+ rnd >>= 6;
+ int Ofs = 1 + rnd % (m_NumSegments * 5 - 2);
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel);
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel);
+ }
+ break;
+ }
+
+ case dirZM:
+ {
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel);
+ for (int i = m_NumSegments; i >= 0; i--)
+ {
+ int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11;
+ int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2;
+ rnd >>= 6;
+ int Ofs = 1 + rnd % (m_NumSegments * 5 - 2);
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + Ofs, dirXM, a_Noise, a_RecursionLevel);
+ m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + Ofs, dirXP, a_Noise, a_RecursionLevel);
+ }
+ break;
+ }
+
+ case dirZP:
+ {
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel);
+ for (int i = m_NumSegments; i >= 0; i--)
+ {
+ int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11;
+ int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2;
+ rnd >>= 6;
+ int Ofs = 1 + rnd % (m_NumSegments * 5 - 2);
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + Ofs, dirXM, a_Noise, a_RecursionLevel);
+ m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + Ofs, dirXP, a_Noise, a_RecursionLevel);
+ }
+ break;
+ }
+ } // switch (m_Direction)
+}
+
+
+
+
+
+void cMineShaftCorridor::ProcessChunk(cChunkDesc & a_ChunkDesc)
+{
+ int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ cCuboid RelBoundingBox(m_BoundingBox);
+ RelBoundingBox.Move(-BlockX, 0, -BlockZ);
+ RelBoundingBox.p1.y += 1;
+ RelBoundingBox.p2.y -= 1;
+ cCuboid Top(RelBoundingBox);
+ Top.p2.y += 1;
+ Top.p1.y = Top.p2.y;
+ a_ChunkDesc.FillRelCuboid(RelBoundingBox, E_BLOCK_AIR, 0);
+ a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_AIR, 0, BlockX ^ BlockZ + BlockX, 8000);
+ if (m_SpawnerPosition >= 0)
+ {
+ // Cobwebs around the spider spawner
+ a_ChunkDesc.RandomFillRelCuboid(RelBoundingBox, E_BLOCK_COBWEB, 0, BlockX ^ BlockZ + BlockZ, 8000);
+ a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_COBWEB, 0, BlockX ^ BlockZ + BlockX, 5000);
+ }
+ a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_COBWEB, 0, BlockX ^ BlockZ + BlockX + 10, 500);
+ RelBoundingBox.p1.y = m_BoundingBox.p1.y;
+ RelBoundingBox.p2.y = m_BoundingBox.p1.y;
+ a_ChunkDesc.FloorRelCuboid(RelBoundingBox, E_BLOCK_PLANKS, 0);
+ switch (m_Direction)
+ {
+ case dirXM:
+ case dirXP:
+ {
+ int y1 = m_BoundingBox.p1.y + 1;
+ int y2 = m_BoundingBox.p1.y + 2;
+ int y3 = m_BoundingBox.p1.y + 3;
+ int z1 = m_BoundingBox.p1.z - BlockZ;
+ int z2 = m_BoundingBox.p2.z - BlockZ;
+ for (int i = 0; i < m_NumSegments; i++)
+ {
+ int x = m_BoundingBox.p1.x + i * 5 + 2 - BlockX;
+ if ((x < 0) || (x >= cChunkDef::Width))
+ {
+ continue;
+ }
+ if ((z1 >= 0) && (z1 < cChunkDef::Width))
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, y1, z1, E_BLOCK_FENCE, 0);
+ a_ChunkDesc.SetBlockTypeMeta(x, y2, z1, E_BLOCK_FENCE, 0);
+ a_ChunkDesc.SetBlockTypeMeta(x, y3, z1, E_BLOCK_PLANKS, 0);
+ }
+ if ((z2 >= 0) && (z2 < cChunkDef::Width))
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, y1, z2, E_BLOCK_FENCE, 0);
+ a_ChunkDesc.SetBlockTypeMeta(x, y2, z2, E_BLOCK_FENCE, 0);
+ a_ChunkDesc.SetBlockTypeMeta(x, y3, z2, E_BLOCK_PLANKS, 0);
+ }
+ if ((z1 >= -1) && (z1 < cChunkDef::Width - 1) && m_HasFullBeam[i])
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, y3, z1 + 1, E_BLOCK_PLANKS, 0);
+ }
+ } // for i - NumSegments
+ break;
+ }
+
+ case dirZM:
+ case dirZP:
+ {
+ int y1 = m_BoundingBox.p1.y + 1;
+ int y2 = m_BoundingBox.p1.y + 2;
+ int y3 = m_BoundingBox.p1.y + 3;
+ int x1 = m_BoundingBox.p1.x - BlockX;
+ int x2 = m_BoundingBox.p2.x - BlockX;
+ for (int i = 0; i < m_NumSegments; i++)
+ {
+ int z = m_BoundingBox.p1.z + i * 5 + 2 - BlockZ;
+ if ((z < 0) || (z >= cChunkDef::Width))
+ {
+ continue;
+ }
+ if ((x1 >= 0) && (x1 < cChunkDef::Width))
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x1, y1, z, E_BLOCK_FENCE, 0);
+ a_ChunkDesc.SetBlockTypeMeta(x1, y2, z, E_BLOCK_FENCE, 0);
+ a_ChunkDesc.SetBlockTypeMeta(x1, y3, z, E_BLOCK_PLANKS, 0);
+ }
+ if ((x2 >= 0) && (x2 < cChunkDef::Width))
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x2, y1, z, E_BLOCK_FENCE, 0);
+ a_ChunkDesc.SetBlockTypeMeta(x2, y2, z, E_BLOCK_FENCE, 0);
+ a_ChunkDesc.SetBlockTypeMeta(x2, y3, z, E_BLOCK_PLANKS, 0);
+ }
+ if ((x1 >= -1) && (x1 < cChunkDef::Width - 1) && m_HasFullBeam[i])
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x1 + 1, y3, z, E_BLOCK_PLANKS, 0);
+ }
+ } // for i - NumSegments
+ break;
+ } // case dirZ?
+ } // for i
+
+ PlaceChest(a_ChunkDesc);
+ PlaceTracks(a_ChunkDesc);
+ PlaceSpawner(a_ChunkDesc); // (must be after Tracks!)
+ PlaceTorches(a_ChunkDesc);
+}
+
+
+
+
+
+void cMineShaftCorridor::PlaceChest(cChunkDesc & a_ChunkDesc)
+{
+ static const cLootProbab LootProbab[] =
+ {
+ // Item, MinAmount, MaxAmount, Weight
+ { cItem(E_ITEM_IRON), 1, 5, 10 },
+ { cItem(E_ITEM_GOLD), 1, 3, 5 },
+ { cItem(E_ITEM_REDSTONE_DUST), 4, 9, 5 },
+ { cItem(E_ITEM_DIAMOND), 1, 2, 3 },
+ { cItem(E_ITEM_DYE, 1, 4), 4, 9, 5 }, // lapis lazuli dye
+ { cItem(E_ITEM_COAL), 3, 8, 10 },
+ { cItem(E_ITEM_BREAD), 1, 3, 15 },
+ { cItem(E_ITEM_IRON_PICKAXE), 1, 1, 1 },
+ { cItem(E_BLOCK_MINECART_TRACKS), 4, 8, 1 },
+ { cItem(E_ITEM_MELON_SEEDS), 2, 4, 10 },
+ { cItem(E_ITEM_PUMPKIN_SEEDS), 2, 4, 10 },
+ } ;
+
+ if (m_ChestPosition < 0)
+ {
+ return;
+ }
+
+ int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ int x, z;
+ NIBBLETYPE Meta = 0;
+ switch (m_Direction)
+ {
+ case dirXM:
+ case dirXP:
+ {
+ x = m_BoundingBox.p1.x + m_ChestPosition - BlockX;
+ z = m_BoundingBox.p1.z - BlockZ;
+ Meta = E_META_CHEST_FACING_ZP;
+ break;
+ }
+
+ case dirZM:
+ case dirZP:
+ {
+ x = m_BoundingBox.p1.x - BlockX;
+ z = m_BoundingBox.p1.z + m_ChestPosition - BlockZ;
+ Meta = E_META_CHEST_FACING_XP;
+ break;
+ }
+ } // switch (Dir)
+
+ if (
+ (x >= 0) && (x < cChunkDef::Width) &&
+ (z >= 0) && (z < cChunkDef::Width)
+ )
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p1.y + 1, z, E_BLOCK_CHEST, Meta);
+ cChestEntity * ChestEntity = new cChestEntity(BlockX + x, m_BoundingBox.p1.y + 1, BlockZ + z);
+ cNoise Noise(a_ChunkDesc.GetChunkX() ^ a_ChunkDesc.GetChunkZ());
+ int NumSlots = 3 + ((Noise.IntNoise3DInt(x, m_BoundingBox.p1.y, z) / 11) % 4);
+ int Seed = Noise.IntNoise2DInt(x, z);
+ ChestEntity->GetContents().GenerateRandomLootWithBooks(LootProbab, ARRAYCOUNT(LootProbab), NumSlots, Seed);
+ a_ChunkDesc.AddBlockEntity(ChestEntity);
+ }
+}
+
+
+
+
+
+void cMineShaftCorridor::PlaceTracks(cChunkDesc & a_ChunkDesc)
+{
+ if (!m_HasTracks)
+ {
+ return;
+ }
+ cCuboid Box(m_BoundingBox);
+ Box.Move(-a_ChunkDesc.GetChunkX() * cChunkDef::Width, 1, -a_ChunkDesc.GetChunkZ() * cChunkDef::Width);
+ Box.p2.y = Box.p1.y;
+ Box.p1.x += 1;
+ Box.p2.x -= 1;
+ Box.p1.z += 1;
+ Box.p2.z -= 1;
+ NIBBLETYPE Meta = 0;
+ switch (m_Direction)
+ {
+ case dirXM:
+ case dirXP:
+ {
+ Meta = E_META_TRACKS_X;
+ break;
+ }
+
+ case dirZM:
+ case dirZP:
+ {
+ Meta = E_META_TRACKS_Z;
+ break;
+ }
+ } // switch (direction)
+ a_ChunkDesc.RandomFillRelCuboid(Box, E_BLOCK_MINECART_TRACKS, Meta, a_ChunkDesc.GetChunkX() + a_ChunkDesc.GetChunkZ(), 6000);
+}
+
+
+
+
+
+void cMineShaftCorridor::PlaceSpawner(cChunkDesc & a_ChunkDesc)
+{
+ if (m_SpawnerPosition < 0)
+ {
+ // No spawner in this corridor
+ return;
+ }
+ int SpawnerRelX = m_BoundingBox.p1.x + 1 - a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ int SpawnerRelZ = m_BoundingBox.p1.z + 1 - a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ switch (m_Direction)
+ {
+ case dirXM:
+ case dirXP:
+ {
+ SpawnerRelX += m_SpawnerPosition - 1;
+ break;
+ }
+ case dirZM:
+ case dirZP:
+ {
+ SpawnerRelZ += m_SpawnerPosition - 1;
+ break;
+ }
+ }
+ if (
+ (SpawnerRelX >= 0) && (SpawnerRelX < cChunkDef::Width) &&
+ (SpawnerRelZ >= 0) && (SpawnerRelZ < cChunkDef::Width)
+ )
+ {
+ a_ChunkDesc.SetBlockTypeMeta(SpawnerRelX, m_BoundingBox.p1.y + 1, SpawnerRelZ, E_BLOCK_MOB_SPAWNER, 0);
+ // TODO: The spawner needs its accompanying cMobSpawnerEntity, when implemented
+ }
+}
+
+
+
+
+
+void cMineShaftCorridor::PlaceTorches(cChunkDesc & a_ChunkDesc)
+{
+ cNoise Noise(m_BoundingBox.p1.x);
+ switch (m_Direction)
+ {
+ case dirXM:
+ case dirXP:
+ {
+ int z = m_BoundingBox.p1.z + 1 - a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ if ((z < 0) || (z >= cChunkDef::Width))
+ {
+ return;
+ }
+ int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ for (int i = 0; i < m_NumSegments; i++)
+ {
+ if (!m_HasFullBeam[i])
+ {
+ continue;
+ }
+ int x = m_BoundingBox.p1.x + i * 5 + 1 - BlockX;
+ if ((x >= 0) && (x < cChunkDef::Width))
+ {
+ if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch)
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_XP);
+ }
+ }
+ x += 2;
+ if ((x >= 0) && (x < cChunkDef::Width))
+ {
+ if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch)
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_XM);
+ }
+ }
+ } // for i
+ break;
+ }
+
+ case dirZM:
+ case dirZP:
+ {
+ int x = m_BoundingBox.p1.x + 1 - a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ if ((x < 0) || (x >= cChunkDef::Width))
+ {
+ return;
+ }
+ int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ for (int i = 0; i < m_NumSegments; i++)
+ {
+ if (!m_HasFullBeam[i])
+ {
+ continue;
+ }
+ int z = m_BoundingBox.p1.z + i * 5 + 1 - BlockZ;
+ if ((z >= 0) && (z < cChunkDef::Width))
+ {
+ if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch)
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_ZP);
+ }
+ }
+ z += 2;
+ if ((z >= 0) && (z < cChunkDef::Width))
+ {
+ if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch)
+ {
+ a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_ZM);
+ }
+ }
+ } // for i
+ break;
+ }
+ } // switch (direction)
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cMineShaftCrossing:
+
+cMineShaftCrossing::cMineShaftCrossing(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, const cCuboid & a_BoundingBox) :
+ super(a_ParentSystem, mskCrossing, a_BoundingBox)
+{
+}
+
+
+
+
+
+cMineShaft * cMineShaftCrossing::CreateAndFit(
+ cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
+ int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction,
+ cNoise & a_Noise
+)
+{
+ cCuboid BoundingBox(a_PivotX, a_PivotY - 1, a_PivotZ);
+ int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7;
+ BoundingBox.p2.y += 3;
+ if ((rnd % 4) < 2)
+ {
+ // 2-level crossing:
+ BoundingBox.p2.y += 4;
+ rnd >>= 2;
+ if ((rnd % 4) < 2)
+ {
+ // This is the higher level:
+ BoundingBox.p1.y -= 4;
+ BoundingBox.p2.y -= 4;
+ }
+ }
+ rnd >>= 2;
+ switch (a_Direction)
+ {
+ case dirXP: BoundingBox.p2.x += 4; BoundingBox.p1.z -= 2; BoundingBox.p2.z += 2; break;
+ case dirXM: BoundingBox.p1.x -= 4; BoundingBox.p1.z -= 2; BoundingBox.p2.z += 2; break;
+ case dirZP: BoundingBox.p2.z += 4; BoundingBox.p1.x -= 2; BoundingBox.p2.x += 2; break;
+ case dirZM: BoundingBox.p1.z -= 4; BoundingBox.p1.x -= 2; BoundingBox.p2.x += 2; break;
+ }
+ if (!a_ParentSystem.CanAppend(BoundingBox))
+ {
+ return NULL;
+ }
+ return new cMineShaftCrossing(a_ParentSystem, BoundingBox);
+}
+
+
+
+
+
+void cMineShaftCrossing::AppendBranches(int a_RecursionLevel, cNoise & a_Noise)
+{
+ struct
+ {
+ int x, y, z;
+ eDirection dir;
+ } Exits[] =
+ {
+ // Bottom level:
+ {-1, 1, 2, dirXM},
+ { 2, 1, -1, dirZM},
+ { 5, 1, 2, dirXP},
+ { 2, 1, 5, dirZP},
+ // Top level:
+ {-1, 5, 2, dirXM},
+ { 2, 5, -1, dirZM},
+ { 5, 5, 2, dirXP},
+ { 2, 5, 5, dirZP},
+ } ;
+ for (int i = 0; i < ARRAYCOUNT(Exits); i++)
+ {
+ if (m_BoundingBox.p1.y + Exits[i].y >= m_BoundingBox.p2.y)
+ {
+ // This exit is not available (two-level exit on a one-level crossing)
+ continue;
+ }
+
+ int Height = m_BoundingBox.p1.y + Exits[i].y;
+ m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Exits[i].x, Height, m_BoundingBox.p1.z + Exits[i].z, Exits[i].dir, a_Noise, a_RecursionLevel);
+ } // for i
+}
+
+
+
+
+
+void cMineShaftCrossing::ProcessChunk(cChunkDesc & a_ChunkDesc)
+{
+ int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ cCuboid box(m_BoundingBox);
+ box.Move(-BlockX, 0, -BlockZ);
+ if ((box.p2.x < 0) || (box.p2.z < 0) || (box.p1.x >= cChunkDef::Width) || (box.p1.z > cChunkDef::Width))
+ {
+ // Does not intersect this chunk
+ return;
+ }
+ int Floor = box.p1.y + 1;
+ int Ceil = box.p2.y;
+
+ // The supports:
+ a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p1.x + 1, Floor, Ceil, box.p1.z + 1, box.p1.z + 1, E_BLOCK_PLANKS, 0);
+ a_ChunkDesc.FillRelCuboid(box.p2.x - 1, box.p2.x - 1, Floor, Ceil, box.p1.z + 1, box.p1.z + 1, E_BLOCK_PLANKS, 0);
+ a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p1.x + 1, Floor, Ceil, box.p2.z - 1, box.p2.z - 1, E_BLOCK_PLANKS, 0);
+ a_ChunkDesc.FillRelCuboid(box.p2.x - 1, box.p2.x - 1, Floor, Ceil, box.p2.z - 1, box.p2.z - 1, E_BLOCK_PLANKS, 0);
+
+ // The air in between:
+ a_ChunkDesc.FillRelCuboid(box.p1.x + 2, box.p1.x + 2, Floor, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Ceil, box.p1.z + 2, box.p1.z + 2, E_BLOCK_AIR, 0);
+
+ // The air on the edges:
+ int Mid = Floor + 2;
+ a_ChunkDesc.FillRelCuboid(box.p1.x, box.p1.x, Floor, Mid, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid(box.p2.x, box.p2.x, Floor, Mid, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Mid, box.p1.z, box.p1.z, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Mid, box.p2.z, box.p2.z, E_BLOCK_AIR, 0);
+ Mid += 2;
+ if (Mid < Ceil)
+ {
+ a_ChunkDesc.FillRelCuboid(box.p1.x, box.p1.x, Mid, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid(box.p2.x, box.p2.x, Mid, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Mid, Ceil, box.p1.z, box.p1.z, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Mid, Ceil, box.p2.z, box.p2.z, E_BLOCK_AIR, 0);
+ }
+
+ // The floor, if needed:
+ box.p2.y = box.p1.y;
+ a_ChunkDesc.FloorRelCuboid(box, E_BLOCK_PLANKS, 0);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cMineShaftStaircase:
+
+cMineShaftStaircase::cMineShaftStaircase(
+ cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
+ const cCuboid & a_BoundingBox,
+ eDirection a_Direction,
+ eSlope a_Slope
+) :
+ super(a_ParentSystem, mskStaircase, a_BoundingBox),
+ m_Direction(a_Direction),
+ m_Slope(a_Slope)
+{
+}
+
+
+
+
+
+cMineShaft * cMineShaftStaircase::CreateAndFit(
+ cStructGenMineShafts::cMineShaftSystem & a_ParentSystem,
+ int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction,
+ cNoise & a_Noise
+)
+{
+ int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7;
+ cCuboid Box;
+ switch (a_Direction)
+ {
+ case dirXM:
+ {
+ Box.Assign(a_PivotX - 7, a_PivotY - 1, a_PivotZ - 1, a_PivotX, a_PivotY + 6, a_PivotZ + 1);
+ break;
+ }
+ case dirXP:
+ {
+ Box.Assign(a_PivotX, a_PivotY - 1, a_PivotZ - 1, a_PivotX + 7, a_PivotY + 6, a_PivotZ + 1);
+ break;
+ }
+ case dirZM:
+ {
+ Box.Assign(a_PivotX - 1, a_PivotY - 1, a_PivotZ - 7, a_PivotX + 1, a_PivotY + 6, a_PivotZ);
+ break;
+ }
+ case dirZP:
+ {
+ Box.Assign(a_PivotX - 1, a_PivotY - 1, a_PivotZ, a_PivotX + 1, a_PivotY + 6, a_PivotZ + 7);
+ break;
+ }
+ }
+ eSlope Slope = sUp;
+ if ((rnd % 4) < 2) // 50 %
+ {
+ Slope = sDown;
+ Box.Move(0, -4, 0);
+ }
+ if (!a_ParentSystem.CanAppend(Box))
+ {
+ return NULL;
+ }
+ return new cMineShaftStaircase(a_ParentSystem, Box, a_Direction, Slope);
+}
+
+
+
+
+
+void cMineShaftStaircase::AppendBranches(int a_RecursionLevel, cNoise & a_Noise)
+{
+ int Height = m_BoundingBox.p1.y + ((m_Slope == sDown) ? 1 : 5);
+ switch (m_Direction)
+ {
+ case dirXM: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + 1, dirXM, a_Noise, a_RecursionLevel); break;
+ case dirXP: m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + 1, dirXP, a_Noise, a_RecursionLevel); break;
+ case dirZM: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); break;
+ case dirZP: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); break;
+ }
+}
+
+
+
+
+
+void cMineShaftStaircase::ProcessChunk(cChunkDesc & a_ChunkDesc)
+{
+ int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ cCuboid RelB(m_BoundingBox);
+ RelB.Move(-BlockX, 0, -BlockZ);
+ if (
+ (RelB.p1.x >= cChunkDef::Width) ||
+ (RelB.p1.z >= cChunkDef::Width) ||
+ (RelB.p2.x < 0) ||
+ (RelB.p2.z < 0)
+ )
+ {
+ // No intersection between this staircase and this chunk
+ return;
+ }
+
+ int SFloor = RelB.p1.y + ((m_Slope == sDown) ? 5 : 1);
+ int DFloor = RelB.p1.y + ((m_Slope == sDown) ? 1 : 5);
+ int Add = (m_Slope == sDown) ? -1 : 1;
+ int InitAdd = (m_Slope == sDown) ? -1 : 0;
+ cCuboid Box;
+ switch (m_Direction)
+ {
+ case dirXM:
+ {
+ a_ChunkDesc.FillRelCuboid (RelB.p2.x - 1, RelB.p2.x, SFloor, SFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p1.x + 1, DFloor, DFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FloorRelCuboid(RelB.p2.x - 1, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0);
+ a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p1.x + 1, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0);
+ Box.Assign(RelB.p2.x - 2, SFloor + InitAdd, RelB.p1.z, RelB.p2.x - 2, SFloor + 3 + InitAdd, RelB.p2.z);
+ for (int i = 0; i < 4; i++)
+ {
+ a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0);
+ Box.Move(-1, Add, 0);
+ }
+ break;
+ }
+
+ case dirXP:
+ {
+ a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p1.x + 1, SFloor, SFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid (RelB.p2.x - 1, RelB.p2.x, DFloor, DFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p1.x + 1, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0);
+ a_ChunkDesc.FloorRelCuboid(RelB.p2.x - 1, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0);
+ Box.Assign(RelB.p1.x + 2, SFloor + InitAdd, RelB.p1.z, RelB.p1.x + 2, SFloor + 3 + InitAdd, RelB.p2.z);
+ for (int i = 0; i < 4; i++)
+ {
+ a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0);
+ Box.Move(1, Add, 0);
+ }
+ break;
+ }
+
+ case dirZM:
+ {
+ a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, SFloor, SFloor + 2, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, DFloor, DFloor + 2, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_PLANKS, 0);
+ a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_PLANKS, 0);
+ Box.Assign(RelB.p1.x, SFloor + InitAdd, RelB.p2.z - 2, RelB.p2.x, SFloor + 3 + InitAdd, RelB.p2.z - 2);
+ for (int i = 0; i < 4; i++)
+ {
+ a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0);
+ Box.Move(0, Add, -1);
+ }
+ break;
+ }
+
+ case dirZP:
+ {
+ a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, SFloor, SFloor + 2, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, DFloor, DFloor + 2, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_PLANKS, 0);
+ a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_PLANKS, 0);
+ Box.Assign(RelB.p1.x, SFloor + InitAdd, RelB.p1.z + 2, RelB.p2.x, SFloor + 3 + InitAdd, RelB.p1.z + 2);
+ for (int i = 0; i < 4; i++)
+ {
+ a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0);
+ a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0);
+ Box.Move(0, Add, 1);
+ }
+ break;
+ }
+
+ } // switch (m_Direction)
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenMineShafts:
+
+cStructGenMineShafts::cStructGenMineShafts(
+ int a_Seed, int a_GridSize, int a_MaxSystemSize,
+ int a_ChanceCorridor, int a_ChanceCrossing, int a_ChanceStaircase
+) :
+ m_Noise(a_Seed),
+ m_GridSize(a_GridSize),
+ m_MaxSystemSize(a_MaxSystemSize),
+ m_ProbLevelCorridor(std::max(0, a_ChanceCorridor)),
+ m_ProbLevelCrossing(std::max(0, a_ChanceCorridor + a_ChanceCrossing)),
+ m_ProbLevelStaircase(std::max(0, a_ChanceCorridor + a_ChanceCrossing + a_ChanceStaircase))
+{
+}
+
+
+
+
+
+cStructGenMineShafts::~cStructGenMineShafts()
+{
+ ClearCache();
+}
+
+
+
+
+
+void cStructGenMineShafts::ClearCache(void)
+{
+ for (cMineShaftSystems::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr)
+ {
+ delete *itr;
+ } // for itr - m_Cache[]
+ m_Cache.clear();
+}
+
+
+
+
+
+void cStructGenMineShafts::GetMineShaftSystemsForChunk(
+ int a_ChunkX, int a_ChunkZ,
+ cStructGenMineShafts::cMineShaftSystems & a_MineShafts
+)
+{
+ int BaseX = a_ChunkX * cChunkDef::Width / m_GridSize;
+ int BaseZ = a_ChunkZ * cChunkDef::Width / m_GridSize;
+ if (BaseX < 0)
+ {
+ --BaseX;
+ }
+ if (BaseZ < 0)
+ {
+ --BaseZ;
+ }
+ BaseX -= NEIGHBORHOOD_SIZE / 2;
+ BaseZ -= NEIGHBORHOOD_SIZE / 2;
+
+ // Walk the cache, move each cave system that we want into a_Caves:
+ int StartX = BaseX * m_GridSize;
+ int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_GridSize;
+ int StartZ = BaseZ * m_GridSize;
+ int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_GridSize;
+ for (cMineShaftSystems::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;)
+ {
+ if (
+ ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) &&
+ ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ)
+ )
+ {
+ // want
+ a_MineShafts.push_back(*itr);
+ itr = m_Cache.erase(itr);
+ }
+ else
+ {
+ // don't want
+ ++itr;
+ }
+ } // for itr - m_Cache[]
+
+ for (int x = 0; x < NEIGHBORHOOD_SIZE; x++)
+ {
+ int RealX = (BaseX + x) * m_GridSize;
+ for (int z = 0; z < NEIGHBORHOOD_SIZE; z++)
+ {
+ int RealZ = (BaseZ + z) * m_GridSize;
+ bool Found = false;
+ for (cMineShaftSystems::const_iterator itr = a_MineShafts.begin(), end = a_MineShafts.end(); itr != end; ++itr)
+ {
+ if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ))
+ {
+ Found = true;
+ break;
+ }
+ } // for itr - a_Mineshafts
+ if (!Found)
+ {
+ a_MineShafts.push_back(new cMineShaftSystem(RealX, RealZ, m_GridSize, m_MaxSystemSize, m_Noise, m_ProbLevelCorridor, m_ProbLevelCrossing, m_ProbLevelStaircase));
+ }
+ } // for z
+ } // for x
+
+ // Copy a_MineShafts into m_Cache to the beginning:
+ cMineShaftSystems MineShaftsCopy(a_MineShafts);
+ m_Cache.splice(m_Cache.begin(), MineShaftsCopy, MineShaftsCopy.begin(), MineShaftsCopy.end());
+
+ // Trim the cache if it's too long:
+ if (m_Cache.size() > 100)
+ {
+ cMineShaftSystems::iterator itr = m_Cache.begin();
+ std::advance(itr, 100);
+ for (cMineShaftSystems::iterator end = m_Cache.end(); itr != end; ++itr)
+ {
+ delete *itr;
+ }
+ itr = m_Cache.begin();
+ std::advance(itr, 100);
+ m_Cache.erase(itr, m_Cache.end());
+ }
+}
+
+
+
+
+
+
+void cStructGenMineShafts::GenStructures(cChunkDesc & a_ChunkDesc)
+{
+ int ChunkX = a_ChunkDesc.GetChunkX();
+ int ChunkZ = a_ChunkDesc.GetChunkZ();
+ cMineShaftSystems MineShafts;
+ GetMineShaftSystemsForChunk(ChunkX, ChunkZ, MineShafts);
+ for (cMineShaftSystems::const_iterator itr = MineShafts.begin(); itr != MineShafts.end(); ++itr)
+ {
+ (*itr)->ProcessChunk(a_ChunkDesc);
+ } // for itr - MineShafts[]
+}
+
+
+
+
diff --git a/source/Generating/MineShafts.h b/source/Generating/MineShafts.h
index a2191b665..c53d3bc53 100644
--- a/source/Generating/MineShafts.h
+++ b/source/Generating/MineShafts.h
@@ -1,61 +1,61 @@
-
-// MineShafts.h
-
-// Declares the cStructGenMineShafts class representing the structure generator for abandoned mineshafts
-
-
-
-
-
-#pragma once
-
-#include "ComposableGenerator.h"
-#include "../Noise.h"
-
-
-
-
-
-class cStructGenMineShafts :
- public cStructureGen
-{
-public:
- cStructGenMineShafts(
- int a_Seed, int a_GridSize, int a_MaxSystemSize,
- int a_ChanceCorridor, int a_ChanceCrossing, int a_ChanceStaircase
- );
-
- virtual ~cStructGenMineShafts();
-
-protected:
- friend class cMineShaft;
- friend class cMineShaftDirtRoom;
- friend class cMineShaftCorridor;
- friend class cMineShaftCrossing;
- friend class cMineShaftStaircase;
- class cMineShaftSystem; // fwd: MineShafts.cpp
- typedef std::list<cMineShaftSystem *> cMineShaftSystems;
-
- cNoise m_Noise;
- int m_GridSize; ///< Average spacing of the systems
- int m_MaxSystemSize; ///< Maximum blcok size of a mineshaft system
- int m_ProbLevelCorridor; ///< Probability level of a branch object being the corridor
- int m_ProbLevelCrossing; ///< Probability level of a branch object being the crossing, minus Corridor
- int m_ProbLevelStaircase; ///< Probability level of a branch object being the staircase, minus Crossing
- cMineShaftSystems m_Cache; ///< Cache of the most recently used systems. MoveToFront used.
-
- /// Clears everything from the cache
- void ClearCache(void);
-
- /** Returns all systems that *may* intersect the given chunk.
- All the systems are valid until the next call to this function (which may delete some of the pointers).
- */
- void GetMineShaftSystemsForChunk(int a_ChunkX, int a_ChunkZ, cMineShaftSystems & a_MineShaftSystems);
-
- // cStructureGen overrides:
- virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
-} ;
-
-
-
-
+
+// MineShafts.h
+
+// Declares the cStructGenMineShafts class representing the structure generator for abandoned mineshafts
+
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+#include "../Noise.h"
+
+
+
+
+
+class cStructGenMineShafts :
+ public cStructureGen
+{
+public:
+ cStructGenMineShafts(
+ int a_Seed, int a_GridSize, int a_MaxSystemSize,
+ int a_ChanceCorridor, int a_ChanceCrossing, int a_ChanceStaircase
+ );
+
+ virtual ~cStructGenMineShafts();
+
+protected:
+ friend class cMineShaft;
+ friend class cMineShaftDirtRoom;
+ friend class cMineShaftCorridor;
+ friend class cMineShaftCrossing;
+ friend class cMineShaftStaircase;
+ class cMineShaftSystem; // fwd: MineShafts.cpp
+ typedef std::list<cMineShaftSystem *> cMineShaftSystems;
+
+ cNoise m_Noise;
+ int m_GridSize; ///< Average spacing of the systems
+ int m_MaxSystemSize; ///< Maximum blcok size of a mineshaft system
+ int m_ProbLevelCorridor; ///< Probability level of a branch object being the corridor
+ int m_ProbLevelCrossing; ///< Probability level of a branch object being the crossing, minus Corridor
+ int m_ProbLevelStaircase; ///< Probability level of a branch object being the staircase, minus Crossing
+ cMineShaftSystems m_Cache; ///< Cache of the most recently used systems. MoveToFront used.
+
+ /// Clears everything from the cache
+ void ClearCache(void);
+
+ /** Returns all systems that *may* intersect the given chunk.
+ All the systems are valid until the next call to this function (which may delete some of the pointers).
+ */
+ void GetMineShaftSystemsForChunk(int a_ChunkX, int a_ChunkZ, cMineShaftSystems & a_MineShaftSystems);
+
+ // cStructureGen overrides:
+ virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
diff --git a/source/Generating/Noise3DGenerator.cpp b/source/Generating/Noise3DGenerator.cpp
index dcf84abeb..f47c64430 100644
--- a/source/Generating/Noise3DGenerator.cpp
+++ b/source/Generating/Noise3DGenerator.cpp
@@ -1,581 +1,581 @@
-
-// Nosie3DGenerator.cpp
-
-// Generates terrain using 3D noise, rather than composing. Is a test.
-
-#include "Globals.h"
-#include "Noise3DGenerator.h"
-#include "../OSSupport/File.h"
-#include "../../iniFile/iniFile.h"
-#include "../LinearInterpolation.h"
-#include "../LinearUpscale.h"
-
-
-
-
-
-/*
-// Perform an automatic test of upscaling upon program start (use breakpoints to debug):
-
-class Test
-{
-public:
- Test(void)
- {
- DoTest1();
- DoTest2();
- }
-
-
- void DoTest1(void)
- {
- float In[3 * 3 * 3];
- for (int i = 0; i < ARRAYCOUNT(In); i++)
- {
- In[i] = (float)(i % 5);
- }
- Debug3DNoise(In, 3, 3, 3, "Upscale3D in");
- float Out[17 * 33 * 35];
- LinearUpscale3DArray(In, 3, 3, 3, Out, 8, 16, 17);
- Debug3DNoise(Out, 17, 33, 35, "Upscale3D test");
- }
-
-
- void DoTest2(void)
- {
- float In[3 * 3];
- for (int i = 0; i < ARRAYCOUNT(In); i++)
- {
- In[i] = (float)(i % 5);
- }
- Debug2DNoise(In, 3, 3, "Upscale2D in");
- float Out[17 * 33];
- LinearUpscale2DArray(In, 3, 3, Out, 8, 16);
- Debug2DNoise(Out, 17, 33, "Upscale2D test");
- }
-
-} gTest;
-//*/
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cNoise3DGenerator:
-
-cNoise3DGenerator::cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator) :
- super(a_ChunkGenerator),
- m_Perlin(1000),
- m_Cubic(1000)
-{
- m_Perlin.AddOctave(1, (NOISE_DATATYPE)0.5);
- m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 1);
- m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 2);
-
- #if 0
- // DEBUG: Test the noise generation:
- // NOTE: In order to be able to run MCS with this code, you need to increase the default thread stack size
- // In MSVC, it is done in Project Settings -> Configuration Properties -> Linker -> System, set Stack reserve size to at least 64M
- m_SeaLevel = 62;
- m_HeightAmplification = 0;
- m_MidPoint = 75;
- m_FrequencyX = 4;
- m_FrequencyY = 4;
- m_FrequencyZ = 4;
- m_AirThreshold = 0.5;
-
- const int NumChunks = 4;
- NOISE_DATATYPE Noise[NumChunks][cChunkDef::Width * cChunkDef::Width * cChunkDef::Height];
- for (int x = 0; x < NumChunks; x++)
- {
- GenerateNoiseArray(x, 5, Noise[x]);
- }
-
- // Save in XY cuts:
- cFile f1;
- if (f1.Open("Test_XY.grab", cFile::fmWrite))
- {
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int y = 0; y < cChunkDef::Height; y++)
- {
- for (int i = 0; i < NumChunks; i++)
- {
- int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height;
- unsigned char buf[cChunkDef::Width];
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++]))));
- }
- f1.Write(buf, cChunkDef::Width);
- }
- } // for y
- } // for z
- } // if (XY file open)
-
- cFile f2;
- if (f2.Open("Test_XZ.grab", cFile::fmWrite))
- {
- for (int y = 0; y < cChunkDef::Height; y++)
- {
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int i = 0; i < NumChunks; i++)
- {
- int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height;
- unsigned char buf[cChunkDef::Width];
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++]))));
- }
- f2.Write(buf, cChunkDef::Width);
- }
- } // for z
- } // for y
- } // if (XZ file open)
- #endif // 0
-}
-
-
-
-
-
-cNoise3DGenerator::~cNoise3DGenerator()
-{
- // Nothing needed yet
-}
-
-
-
-
-
-void cNoise3DGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile)
-{
- m_World = a_World;
-
- // Params:
- m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62);
- m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0);
- m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75);
- m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 8);
- m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 8);
- m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 8);
- m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5);
-}
-
-
-
-
-
-void cNoise3DGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
-{
- for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++)
- {
- a_BiomeMap[i] = biExtremeHills;
- }
-}
-
-
-
-
-
-void cNoise3DGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc)
-{
- NOISE_DATATYPE Noise[17 * 257 * 17];
- GenerateNoiseArray(a_ChunkX, a_ChunkZ, Noise);
-
- // Output noise into chunk:
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int y = 0; y < cChunkDef::Height; y++)
- {
- int idx = z * 17 * 257 + y * 17;
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- NOISE_DATATYPE n = Noise[idx++];
- BLOCKTYPE BlockType;
- if (n > m_AirThreshold)
- {
- BlockType = (y > m_SeaLevel) ? E_BLOCK_AIR : E_BLOCK_STATIONARY_WATER;
- }
- else
- {
- BlockType = E_BLOCK_STONE;
- }
- a_ChunkDesc.SetBlockType(x, y, z, BlockType);
- }
- }
- }
-
- UpdateHeightmap(a_ChunkDesc);
- ComposeTerrain (a_ChunkDesc);
-}
-
-
-
-
-
-void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_OutNoise)
-{
- NOISE_DATATYPE NoiseO[DIM_X * DIM_Y * DIM_Z]; // Output for the Perlin noise
- NOISE_DATATYPE NoiseW[DIM_X * DIM_Y * DIM_Z]; // Workspace that the noise calculation can use and trash
-
- // Our noise array has different layout, XZY, instead of regular chunk's XYZ, that's why the coords are "renamed"
- NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width)) / m_FrequencyX;
- NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((a_ChunkX + 1) * cChunkDef::Width) - 1) / m_FrequencyX;
- NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width)) / m_FrequencyZ;
- NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((a_ChunkZ + 1) * cChunkDef::Width) - 1) / m_FrequencyZ;
- NOISE_DATATYPE StartY = 0;
- NOISE_DATATYPE EndY = ((NOISE_DATATYPE)256) / m_FrequencyY;
-
- m_Perlin.Generate3D(NoiseO, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, NoiseW);
-
- // DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_orig", a_ChunkX, a_ChunkZ));
-
- // Precalculate a "height" array:
- NOISE_DATATYPE Height[DIM_X * DIM_Z]; // Output for the cubic noise heightmap ("source")
- m_Cubic.Generate2D(Height, DIM_X, DIM_Z, StartX / 25, EndX / 25, StartZ / 25, EndZ / 25);
- for (int i = 0; i < ARRAYCOUNT(Height); i++)
- {
- Height[i] = abs(Height[i]) * m_HeightAmplification + 1;
- }
-
- // Modify the noise by height data:
- for (int y = 0; y < DIM_Y; y++)
- {
- NOISE_DATATYPE AddHeight = (y * UPSCALE_Y - m_MidPoint) / 20;
- AddHeight *= AddHeight * AddHeight;
- for (int z = 0; z < DIM_Z; z++)
- {
- NOISE_DATATYPE * CurRow = &(NoiseO[y * DIM_X + z * DIM_X * DIM_Y]);
- for (int x = 0; x < DIM_X; x++)
- {
- CurRow[x] += AddHeight / Height[x + DIM_X * z];
- }
- }
- }
-
- // DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_hei", a_ChunkX, a_ChunkZ));
-
- // Upscale the Perlin noise into full-blown chunk dimensions:
- LinearUpscale3DArray(
- NoiseO, DIM_X, DIM_Y, DIM_Z,
- a_OutNoise, UPSCALE_X, UPSCALE_Y, UPSCALE_Z
- );
-
- // DEBUG: Debug3DNoise(a_OutNoise, 17, 257, 17, Printf("Chunk_%d_%d_lerp", a_ChunkX, a_ChunkZ));
-}
-
-
-
-
-
-void cNoise3DGenerator::UpdateHeightmap(cChunkDesc & a_ChunkDesc)
-{
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- for (int y = cChunkDef::Height - 1; y > 0; y--)
- {
- if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR)
- {
- a_ChunkDesc.SetHeight(x, z, y);
- break;
- }
- } // for y
- } // for x
- } // for z
-}
-
-
-
-
-
-void cNoise3DGenerator::ComposeTerrain(cChunkDesc & a_ChunkDesc)
-{
- // Make basic terrain composition:
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- int LastAir = a_ChunkDesc.GetHeight(x, z) + 1;
- bool HasHadWater = false;
- for (int y = LastAir - 1; y > 0; y--)
- {
- switch (a_ChunkDesc.GetBlockType(x, y, z))
- {
- case E_BLOCK_AIR:
- {
- LastAir = y;
- break;
- }
- case E_BLOCK_STONE:
- {
- if (LastAir - y > 3)
- {
- break;
- }
- if (HasHadWater)
- {
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND);
- }
- else
- {
- a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT);
- }
- break;
- }
- case E_BLOCK_STATIONARY_WATER:
- {
- LastAir = y;
- HasHadWater = true;
- break;
- }
- } // switch (GetBlockType())
- } // for y
- a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
- } // for x
- } // for z
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cNoise3DComposable:
-
-cNoise3DComposable::cNoise3DComposable(int a_Seed) :
- m_Noise1(a_Seed + 1000),
- m_Noise2(a_Seed + 2000),
- m_Noise3(a_Seed + 3000)
-{
-}
-
-
-
-
-
-void cNoise3DComposable::Initialize(cIniFile & a_IniFile)
-{
- // Params:
- m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62);
- m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0);
- m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75);
- m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 10);
- m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 10);
- m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 10);
- m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5);
-}
-
-
-
-
-
-void cNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ)
-{
- if ((a_ChunkX == m_LastChunkX) && (a_ChunkZ == m_LastChunkZ))
- {
- // The noise for this chunk is already generated in m_Noise
- return;
- }
- m_LastChunkX = a_ChunkX;
- m_LastChunkZ = a_ChunkZ;
-
- // Upscaling parameters:
- const int UPSCALE_X = 8;
- const int UPSCALE_Y = 4;
- const int UPSCALE_Z = 8;
-
- const int DIM_X = 1 + cChunkDef::Width / UPSCALE_X;
- const int DIM_Y = 1 + cChunkDef::Height / UPSCALE_Y;
- const int DIM_Z = 1 + cChunkDef::Width / UPSCALE_Z;
-
- // Precalculate a "height" array:
- NOISE_DATATYPE Height[17 * 17]; // x + 17 * z
- for (int z = 0; z < 17; z += UPSCALE_Z)
- {
- NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ;
- for (int x = 0; x < 17; x += UPSCALE_X)
- {
- NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX;
- NOISE_DATATYPE val = abs(m_Noise1.CubicNoise2D(NoiseX / 5, NoiseZ / 5)) * m_HeightAmplification + 1;
- Height[x + 17 * z] = val * val * val;
- }
- }
-
- int idx = 0;
- for (int y = 0; y < 257; y += UPSCALE_Y)
- {
- NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)y) / m_FrequencyY;
- NOISE_DATATYPE AddHeight = (y - m_MidPoint) / 20;
- AddHeight *= AddHeight * AddHeight;
- NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]);
- for (int z = 0; z < 17; z += UPSCALE_Z)
- {
- NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ;
- for (int x = 0; x < 17; x += UPSCALE_X)
- {
- NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX;
- CurFloor[x + 17 * z] =
- m_Noise1.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * (NOISE_DATATYPE)0.5 +
- m_Noise2.CubicNoise3D(NoiseX / 2, NoiseY / 2, NoiseZ / 2) +
- m_Noise3.CubicNoise3D(NoiseX / 4, NoiseY / 4, NoiseZ / 4) * 2 +
- AddHeight / Height[x + 17 * z];
- }
- }
- // Linear-interpolate this XZ floor:
- LinearUpscale2DArrayInPlace(CurFloor, 17, 17, UPSCALE_X, UPSCALE_Z);
- }
-
- // Finish the 3D linear interpolation by interpolating between each XZ-floors on the Y axis
- for (int y = 1; y < cChunkDef::Height; y++)
- {
- if ((y % UPSCALE_Y) == 0)
- {
- // This is the interpolation source floor, already calculated
- continue;
- }
- int LoFloorY = (y / UPSCALE_Y) * UPSCALE_Y;
- int HiFloorY = LoFloorY + UPSCALE_Y;
- NOISE_DATATYPE * LoFloor = &(m_NoiseArray[LoFloorY * 17 * 17]);
- NOISE_DATATYPE * HiFloor = &(m_NoiseArray[HiFloorY * 17 * 17]);
- NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]);
- NOISE_DATATYPE Ratio = ((NOISE_DATATYPE)(y % UPSCALE_Y)) / UPSCALE_Y;
- int idx = 0;
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- CurFloor[idx] = LoFloor[idx] + (HiFloor[idx] - LoFloor[idx]) * Ratio;
- idx += 1;
- }
- idx += 1; // Skipping one X column
- }
- }
-
- // The noise array is now fully interpolated
- /*
- // DEBUG: Output two images of the array, sliced by XY and XZ:
- cFile f1;
- if (f1.Open(Printf("Chunk_%d_%d_XY.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite))
- {
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int y = 0; y < cChunkDef::Height; y++)
- {
- int idx = y * 17 * 17 + z * 17;
- unsigned char buf[16];
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++]))));
- }
- f1.Write(buf, 16);
- } // for y
- } // for z
- } // if (XY file open)
-
- cFile f2;
- if (f2.Open(Printf("Chunk_%d_%d_XZ.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite))
- {
- for (int y = 0; y < cChunkDef::Height; y++)
- {
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- int idx = y * 17 * 17 + z * 17;
- unsigned char buf[16];
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++]))));
- }
- f2.Write(buf, 16);
- } // for z
- } // for y
- } // if (XZ file open)
- */
-}
-
-
-
-
-
-void cNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
-{
- GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ);
-
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel);
- for (int y = cChunkDef::Height - 1; y > m_SeaLevel; y--)
- {
- if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= m_AirThreshold)
- {
- cChunkDef::SetHeight(a_HeightMap, x, z, y);
- break;
- }
- } // for y
- } // for x
- } // for z
-}
-
-
-
-
-
-void cNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc)
-{
- GenerateNoiseArrayIfNeeded(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
-
- a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
-
- // Make basic terrain composition:
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- int LastAir = a_ChunkDesc.GetHeight(x, z) + 1;
- bool HasHadWater = false;
- for (int y = LastAir; y < m_SeaLevel; y++)
- {
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
- }
- for (int y = LastAir - 1; y > 0; y--)
- {
- if (m_NoiseArray[x + 17 * z + 17 * 17 * y] > m_AirThreshold)
- {
- // "air" part
- LastAir = y;
- if (y < m_SeaLevel)
- {
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
- HasHadWater = true;
- }
- continue;
- }
- // "ground" part:
- if (LastAir - y > 4)
- {
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE);
- continue;
- }
- if (HasHadWater)
- {
- a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND);
- }
- else
- {
- a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT);
- }
- } // for y
- a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
- } // for x
- } // for z
-}
-
-
-
-
+
+// Nosie3DGenerator.cpp
+
+// Generates terrain using 3D noise, rather than composing. Is a test.
+
+#include "Globals.h"
+#include "Noise3DGenerator.h"
+#include "../OSSupport/File.h"
+#include "../../iniFile/iniFile.h"
+#include "../LinearInterpolation.h"
+#include "../LinearUpscale.h"
+
+
+
+
+
+/*
+// Perform an automatic test of upscaling upon program start (use breakpoints to debug):
+
+class Test
+{
+public:
+ Test(void)
+ {
+ DoTest1();
+ DoTest2();
+ }
+
+
+ void DoTest1(void)
+ {
+ float In[3 * 3 * 3];
+ for (int i = 0; i < ARRAYCOUNT(In); i++)
+ {
+ In[i] = (float)(i % 5);
+ }
+ Debug3DNoise(In, 3, 3, 3, "Upscale3D in");
+ float Out[17 * 33 * 35];
+ LinearUpscale3DArray(In, 3, 3, 3, Out, 8, 16, 17);
+ Debug3DNoise(Out, 17, 33, 35, "Upscale3D test");
+ }
+
+
+ void DoTest2(void)
+ {
+ float In[3 * 3];
+ for (int i = 0; i < ARRAYCOUNT(In); i++)
+ {
+ In[i] = (float)(i % 5);
+ }
+ Debug2DNoise(In, 3, 3, "Upscale2D in");
+ float Out[17 * 33];
+ LinearUpscale2DArray(In, 3, 3, Out, 8, 16);
+ Debug2DNoise(Out, 17, 33, "Upscale2D test");
+ }
+
+} gTest;
+//*/
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cNoise3DGenerator:
+
+cNoise3DGenerator::cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator) :
+ super(a_ChunkGenerator),
+ m_Perlin(1000),
+ m_Cubic(1000)
+{
+ m_Perlin.AddOctave(1, (NOISE_DATATYPE)0.5);
+ m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 1);
+ m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 2);
+
+ #if 0
+ // DEBUG: Test the noise generation:
+ // NOTE: In order to be able to run MCS with this code, you need to increase the default thread stack size
+ // In MSVC, it is done in Project Settings -> Configuration Properties -> Linker -> System, set Stack reserve size to at least 64M
+ m_SeaLevel = 62;
+ m_HeightAmplification = 0;
+ m_MidPoint = 75;
+ m_FrequencyX = 4;
+ m_FrequencyY = 4;
+ m_FrequencyZ = 4;
+ m_AirThreshold = 0.5;
+
+ const int NumChunks = 4;
+ NOISE_DATATYPE Noise[NumChunks][cChunkDef::Width * cChunkDef::Width * cChunkDef::Height];
+ for (int x = 0; x < NumChunks; x++)
+ {
+ GenerateNoiseArray(x, 5, Noise[x]);
+ }
+
+ // Save in XY cuts:
+ cFile f1;
+ if (f1.Open("Test_XY.grab", cFile::fmWrite))
+ {
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int y = 0; y < cChunkDef::Height; y++)
+ {
+ for (int i = 0; i < NumChunks; i++)
+ {
+ int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height;
+ unsigned char buf[cChunkDef::Width];
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++]))));
+ }
+ f1.Write(buf, cChunkDef::Width);
+ }
+ } // for y
+ } // for z
+ } // if (XY file open)
+
+ cFile f2;
+ if (f2.Open("Test_XZ.grab", cFile::fmWrite))
+ {
+ for (int y = 0; y < cChunkDef::Height; y++)
+ {
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int i = 0; i < NumChunks; i++)
+ {
+ int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height;
+ unsigned char buf[cChunkDef::Width];
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++]))));
+ }
+ f2.Write(buf, cChunkDef::Width);
+ }
+ } // for z
+ } // for y
+ } // if (XZ file open)
+ #endif // 0
+}
+
+
+
+
+
+cNoise3DGenerator::~cNoise3DGenerator()
+{
+ // Nothing needed yet
+}
+
+
+
+
+
+void cNoise3DGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile)
+{
+ m_World = a_World;
+
+ // Params:
+ m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62);
+ m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0);
+ m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75);
+ m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 8);
+ m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 8);
+ m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 8);
+ m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5);
+}
+
+
+
+
+
+void cNoise3DGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
+{
+ for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++)
+ {
+ a_BiomeMap[i] = biExtremeHills;
+ }
+}
+
+
+
+
+
+void cNoise3DGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc)
+{
+ NOISE_DATATYPE Noise[17 * 257 * 17];
+ GenerateNoiseArray(a_ChunkX, a_ChunkZ, Noise);
+
+ // Output noise into chunk:
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int y = 0; y < cChunkDef::Height; y++)
+ {
+ int idx = z * 17 * 257 + y * 17;
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ NOISE_DATATYPE n = Noise[idx++];
+ BLOCKTYPE BlockType;
+ if (n > m_AirThreshold)
+ {
+ BlockType = (y > m_SeaLevel) ? E_BLOCK_AIR : E_BLOCK_STATIONARY_WATER;
+ }
+ else
+ {
+ BlockType = E_BLOCK_STONE;
+ }
+ a_ChunkDesc.SetBlockType(x, y, z, BlockType);
+ }
+ }
+ }
+
+ UpdateHeightmap(a_ChunkDesc);
+ ComposeTerrain (a_ChunkDesc);
+}
+
+
+
+
+
+void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_OutNoise)
+{
+ NOISE_DATATYPE NoiseO[DIM_X * DIM_Y * DIM_Z]; // Output for the Perlin noise
+ NOISE_DATATYPE NoiseW[DIM_X * DIM_Y * DIM_Z]; // Workspace that the noise calculation can use and trash
+
+ // Our noise array has different layout, XZY, instead of regular chunk's XYZ, that's why the coords are "renamed"
+ NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width)) / m_FrequencyX;
+ NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((a_ChunkX + 1) * cChunkDef::Width) - 1) / m_FrequencyX;
+ NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width)) / m_FrequencyZ;
+ NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((a_ChunkZ + 1) * cChunkDef::Width) - 1) / m_FrequencyZ;
+ NOISE_DATATYPE StartY = 0;
+ NOISE_DATATYPE EndY = ((NOISE_DATATYPE)256) / m_FrequencyY;
+
+ m_Perlin.Generate3D(NoiseO, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, NoiseW);
+
+ // DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_orig", a_ChunkX, a_ChunkZ));
+
+ // Precalculate a "height" array:
+ NOISE_DATATYPE Height[DIM_X * DIM_Z]; // Output for the cubic noise heightmap ("source")
+ m_Cubic.Generate2D(Height, DIM_X, DIM_Z, StartX / 25, EndX / 25, StartZ / 25, EndZ / 25);
+ for (int i = 0; i < ARRAYCOUNT(Height); i++)
+ {
+ Height[i] = abs(Height[i]) * m_HeightAmplification + 1;
+ }
+
+ // Modify the noise by height data:
+ for (int y = 0; y < DIM_Y; y++)
+ {
+ NOISE_DATATYPE AddHeight = (y * UPSCALE_Y - m_MidPoint) / 20;
+ AddHeight *= AddHeight * AddHeight;
+ for (int z = 0; z < DIM_Z; z++)
+ {
+ NOISE_DATATYPE * CurRow = &(NoiseO[y * DIM_X + z * DIM_X * DIM_Y]);
+ for (int x = 0; x < DIM_X; x++)
+ {
+ CurRow[x] += AddHeight / Height[x + DIM_X * z];
+ }
+ }
+ }
+
+ // DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_hei", a_ChunkX, a_ChunkZ));
+
+ // Upscale the Perlin noise into full-blown chunk dimensions:
+ LinearUpscale3DArray(
+ NoiseO, DIM_X, DIM_Y, DIM_Z,
+ a_OutNoise, UPSCALE_X, UPSCALE_Y, UPSCALE_Z
+ );
+
+ // DEBUG: Debug3DNoise(a_OutNoise, 17, 257, 17, Printf("Chunk_%d_%d_lerp", a_ChunkX, a_ChunkZ));
+}
+
+
+
+
+
+void cNoise3DGenerator::UpdateHeightmap(cChunkDesc & a_ChunkDesc)
+{
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ for (int y = cChunkDef::Height - 1; y > 0; y--)
+ {
+ if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR)
+ {
+ a_ChunkDesc.SetHeight(x, z, y);
+ break;
+ }
+ } // for y
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cNoise3DGenerator::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+{
+ // Make basic terrain composition:
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int LastAir = a_ChunkDesc.GetHeight(x, z) + 1;
+ bool HasHadWater = false;
+ for (int y = LastAir - 1; y > 0; y--)
+ {
+ switch (a_ChunkDesc.GetBlockType(x, y, z))
+ {
+ case E_BLOCK_AIR:
+ {
+ LastAir = y;
+ break;
+ }
+ case E_BLOCK_STONE:
+ {
+ if (LastAir - y > 3)
+ {
+ break;
+ }
+ if (HasHadWater)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND);
+ }
+ else
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT);
+ }
+ break;
+ }
+ case E_BLOCK_STATIONARY_WATER:
+ {
+ LastAir = y;
+ HasHadWater = true;
+ break;
+ }
+ } // switch (GetBlockType())
+ } // for y
+ a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
+ } // for x
+ } // for z
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cNoise3DComposable:
+
+cNoise3DComposable::cNoise3DComposable(int a_Seed) :
+ m_Noise1(a_Seed + 1000),
+ m_Noise2(a_Seed + 2000),
+ m_Noise3(a_Seed + 3000)
+{
+}
+
+
+
+
+
+void cNoise3DComposable::Initialize(cIniFile & a_IniFile)
+{
+ // Params:
+ m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62);
+ m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0);
+ m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75);
+ m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 10);
+ m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 10);
+ m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 10);
+ m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5);
+}
+
+
+
+
+
+void cNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ)
+{
+ if ((a_ChunkX == m_LastChunkX) && (a_ChunkZ == m_LastChunkZ))
+ {
+ // The noise for this chunk is already generated in m_Noise
+ return;
+ }
+ m_LastChunkX = a_ChunkX;
+ m_LastChunkZ = a_ChunkZ;
+
+ // Upscaling parameters:
+ const int UPSCALE_X = 8;
+ const int UPSCALE_Y = 4;
+ const int UPSCALE_Z = 8;
+
+ const int DIM_X = 1 + cChunkDef::Width / UPSCALE_X;
+ const int DIM_Y = 1 + cChunkDef::Height / UPSCALE_Y;
+ const int DIM_Z = 1 + cChunkDef::Width / UPSCALE_Z;
+
+ // Precalculate a "height" array:
+ NOISE_DATATYPE Height[17 * 17]; // x + 17 * z
+ for (int z = 0; z < 17; z += UPSCALE_Z)
+ {
+ NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ;
+ for (int x = 0; x < 17; x += UPSCALE_X)
+ {
+ NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX;
+ NOISE_DATATYPE val = abs(m_Noise1.CubicNoise2D(NoiseX / 5, NoiseZ / 5)) * m_HeightAmplification + 1;
+ Height[x + 17 * z] = val * val * val;
+ }
+ }
+
+ int idx = 0;
+ for (int y = 0; y < 257; y += UPSCALE_Y)
+ {
+ NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)y) / m_FrequencyY;
+ NOISE_DATATYPE AddHeight = (y - m_MidPoint) / 20;
+ AddHeight *= AddHeight * AddHeight;
+ NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]);
+ for (int z = 0; z < 17; z += UPSCALE_Z)
+ {
+ NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ;
+ for (int x = 0; x < 17; x += UPSCALE_X)
+ {
+ NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX;
+ CurFloor[x + 17 * z] =
+ m_Noise1.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * (NOISE_DATATYPE)0.5 +
+ m_Noise2.CubicNoise3D(NoiseX / 2, NoiseY / 2, NoiseZ / 2) +
+ m_Noise3.CubicNoise3D(NoiseX / 4, NoiseY / 4, NoiseZ / 4) * 2 +
+ AddHeight / Height[x + 17 * z];
+ }
+ }
+ // Linear-interpolate this XZ floor:
+ LinearUpscale2DArrayInPlace(CurFloor, 17, 17, UPSCALE_X, UPSCALE_Z);
+ }
+
+ // Finish the 3D linear interpolation by interpolating between each XZ-floors on the Y axis
+ for (int y = 1; y < cChunkDef::Height; y++)
+ {
+ if ((y % UPSCALE_Y) == 0)
+ {
+ // This is the interpolation source floor, already calculated
+ continue;
+ }
+ int LoFloorY = (y / UPSCALE_Y) * UPSCALE_Y;
+ int HiFloorY = LoFloorY + UPSCALE_Y;
+ NOISE_DATATYPE * LoFloor = &(m_NoiseArray[LoFloorY * 17 * 17]);
+ NOISE_DATATYPE * HiFloor = &(m_NoiseArray[HiFloorY * 17 * 17]);
+ NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]);
+ NOISE_DATATYPE Ratio = ((NOISE_DATATYPE)(y % UPSCALE_Y)) / UPSCALE_Y;
+ int idx = 0;
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ CurFloor[idx] = LoFloor[idx] + (HiFloor[idx] - LoFloor[idx]) * Ratio;
+ idx += 1;
+ }
+ idx += 1; // Skipping one X column
+ }
+ }
+
+ // The noise array is now fully interpolated
+ /*
+ // DEBUG: Output two images of the array, sliced by XY and XZ:
+ cFile f1;
+ if (f1.Open(Printf("Chunk_%d_%d_XY.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite))
+ {
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int y = 0; y < cChunkDef::Height; y++)
+ {
+ int idx = y * 17 * 17 + z * 17;
+ unsigned char buf[16];
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++]))));
+ }
+ f1.Write(buf, 16);
+ } // for y
+ } // for z
+ } // if (XY file open)
+
+ cFile f2;
+ if (f2.Open(Printf("Chunk_%d_%d_XZ.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite))
+ {
+ for (int y = 0; y < cChunkDef::Height; y++)
+ {
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ int idx = y * 17 * 17 + z * 17;
+ unsigned char buf[16];
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++]))));
+ }
+ f2.Write(buf, 16);
+ } // for z
+ } // for y
+ } // if (XZ file open)
+ */
+}
+
+
+
+
+
+void cNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+{
+ GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ);
+
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel);
+ for (int y = cChunkDef::Height - 1; y > m_SeaLevel; y--)
+ {
+ if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= m_AirThreshold)
+ {
+ cChunkDef::SetHeight(a_HeightMap, x, z, y);
+ break;
+ }
+ } // for y
+ } // for x
+ } // for z
+}
+
+
+
+
+
+void cNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc)
+{
+ GenerateNoiseArrayIfNeeded(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
+
+ a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
+
+ // Make basic terrain composition:
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int LastAir = a_ChunkDesc.GetHeight(x, z) + 1;
+ bool HasHadWater = false;
+ for (int y = LastAir; y < m_SeaLevel; y++)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
+ }
+ for (int y = LastAir - 1; y > 0; y--)
+ {
+ if (m_NoiseArray[x + 17 * z + 17 * 17 * y] > m_AirThreshold)
+ {
+ // "air" part
+ LastAir = y;
+ if (y < m_SeaLevel)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
+ HasHadWater = true;
+ }
+ continue;
+ }
+ // "ground" part:
+ if (LastAir - y > 4)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE);
+ continue;
+ }
+ if (HasHadWater)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND);
+ }
+ else
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT);
+ }
+ } // for y
+ a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
+ } // for x
+ } // for z
+}
+
+
+
+
diff --git a/source/Generating/Noise3DGenerator.h b/source/Generating/Noise3DGenerator.h
index b23e8645b..0d211cddc 100644
--- a/source/Generating/Noise3DGenerator.h
+++ b/source/Generating/Noise3DGenerator.h
@@ -1,106 +1,106 @@
-
-// Noise3DGenerator.h
-
-// Generates terrain using 3D noise, rather than composing. Is a test.
-
-
-
-
-#pragma once
-
-#include "ComposableGenerator.h"
-#include "../Noise.h"
-
-
-
-
-
-class cNoise3DGenerator :
- public cChunkGenerator::cGenerator
-{
- typedef cChunkGenerator::cGenerator super;
-
-public:
- cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator);
- virtual ~cNoise3DGenerator();
-
- virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override;
- virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
- virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override;
-
-protected:
- // Linear interpolation step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively:
- static const int UPSCALE_X = 8;
- static const int UPSCALE_Y = 4;
- static const int UPSCALE_Z = 8;
-
- // Linear interpolation buffer dimensions, calculated from the step sizes:
- static const int DIM_X = 1 + cChunkDef::Width / UPSCALE_X;
- static const int DIM_Y = 1 + cChunkDef::Height / UPSCALE_Y;
- static const int DIM_Z = 1 + cChunkDef::Width / UPSCALE_Z;
-
- cPerlinNoise m_Perlin; // The base 3D noise source for the actual composition
- cCubicNoise m_Cubic; // The noise used for heightmap directing
-
- int m_SeaLevel;
- NOISE_DATATYPE m_HeightAmplification;
- NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be
- NOISE_DATATYPE m_FrequencyX;
- NOISE_DATATYPE m_FrequencyY;
- NOISE_DATATYPE m_FrequencyZ;
- NOISE_DATATYPE m_AirThreshold;
-
- /// Generates the 3D noise array used for terrain generation; a_Noise is of ChunkData-size
- void GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_Noise);
-
- /// Updates heightmap based on the chunk's contents
- void UpdateHeightmap(cChunkDesc & a_ChunkDesc);
-
- /// Composes terrain - adds dirt, grass and sand
- void ComposeTerrain(cChunkDesc & a_ChunkDesc);
-} ;
-
-
-
-
-
-class cNoise3DComposable :
- public cTerrainHeightGen,
- public cTerrainCompositionGen
-{
-public:
- cNoise3DComposable(int a_Seed);
-
- void Initialize(cIniFile & a_IniFile);
-
-protected:
- cNoise m_Noise1;
- cNoise m_Noise2;
- cNoise m_Noise3;
-
- int m_SeaLevel;
- NOISE_DATATYPE m_HeightAmplification;
- NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be
- NOISE_DATATYPE m_FrequencyX;
- NOISE_DATATYPE m_FrequencyY;
- NOISE_DATATYPE m_FrequencyZ;
- NOISE_DATATYPE m_AirThreshold;
-
- int m_LastChunkX;
- int m_LastChunkZ;
- NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y
-
-
- /// Generates the 3D noise array used for terrain generation, unless the LastChunk coords are equal to coords given
- void GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ);
-
- // cTerrainHeightGen overrides:
- virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
-
- // cTerrainCompositionGen overrides:
- virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
-} ;
-
-
-
-
+
+// Noise3DGenerator.h
+
+// Generates terrain using 3D noise, rather than composing. Is a test.
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+#include "../Noise.h"
+
+
+
+
+
+class cNoise3DGenerator :
+ public cChunkGenerator::cGenerator
+{
+ typedef cChunkGenerator::cGenerator super;
+
+public:
+ cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator);
+ virtual ~cNoise3DGenerator();
+
+ virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override;
+ virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
+ virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override;
+
+protected:
+ // Linear interpolation step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively:
+ static const int UPSCALE_X = 8;
+ static const int UPSCALE_Y = 4;
+ static const int UPSCALE_Z = 8;
+
+ // Linear interpolation buffer dimensions, calculated from the step sizes:
+ static const int DIM_X = 1 + cChunkDef::Width / UPSCALE_X;
+ static const int DIM_Y = 1 + cChunkDef::Height / UPSCALE_Y;
+ static const int DIM_Z = 1 + cChunkDef::Width / UPSCALE_Z;
+
+ cPerlinNoise m_Perlin; // The base 3D noise source for the actual composition
+ cCubicNoise m_Cubic; // The noise used for heightmap directing
+
+ int m_SeaLevel;
+ NOISE_DATATYPE m_HeightAmplification;
+ NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be
+ NOISE_DATATYPE m_FrequencyX;
+ NOISE_DATATYPE m_FrequencyY;
+ NOISE_DATATYPE m_FrequencyZ;
+ NOISE_DATATYPE m_AirThreshold;
+
+ /// Generates the 3D noise array used for terrain generation; a_Noise is of ChunkData-size
+ void GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_Noise);
+
+ /// Updates heightmap based on the chunk's contents
+ void UpdateHeightmap(cChunkDesc & a_ChunkDesc);
+
+ /// Composes terrain - adds dirt, grass and sand
+ void ComposeTerrain(cChunkDesc & a_ChunkDesc);
+} ;
+
+
+
+
+
+class cNoise3DComposable :
+ public cTerrainHeightGen,
+ public cTerrainCompositionGen
+{
+public:
+ cNoise3DComposable(int a_Seed);
+
+ void Initialize(cIniFile & a_IniFile);
+
+protected:
+ cNoise m_Noise1;
+ cNoise m_Noise2;
+ cNoise m_Noise3;
+
+ int m_SeaLevel;
+ NOISE_DATATYPE m_HeightAmplification;
+ NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be
+ NOISE_DATATYPE m_FrequencyX;
+ NOISE_DATATYPE m_FrequencyY;
+ NOISE_DATATYPE m_FrequencyZ;
+ NOISE_DATATYPE m_AirThreshold;
+
+ int m_LastChunkX;
+ int m_LastChunkZ;
+ NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y
+
+
+ /// Generates the 3D noise array used for terrain generation, unless the LastChunk coords are equal to coords given
+ void GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ);
+
+ // cTerrainHeightGen overrides:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+
+ // cTerrainCompositionGen overrides:
+ virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
diff --git a/source/Generating/Ravines.cpp b/source/Generating/Ravines.cpp
index 94d91c0bb..6413b963b 100644
--- a/source/Generating/Ravines.cpp
+++ b/source/Generating/Ravines.cpp
@@ -1,531 +1,531 @@
-
-// Ravines.cpp
-
-// Implements the cStructGenRavines class representing the ravine structure generator
-
-#include "Globals.h"
-#include "Ravines.h"
-
-
-
-
-/// How many ravines in each direction are generated for a given chunk. Must be an even number
-static const int NEIGHBORHOOD_SIZE = 8;
-
-static const int NUM_RAVINE_POINTS = 4;
-
-
-
-
-
-struct cRavDefPoint
-{
- int m_BlockX;
- int m_BlockZ;
- int m_Radius;
- int m_Top;
- int m_Bottom;
-
- cRavDefPoint(int a_BlockX, int a_BlockZ, int a_Radius, int a_Top, int a_Bottom) :
- m_BlockX(a_BlockX),
- m_BlockZ(a_BlockZ),
- m_Radius(a_Radius),
- m_Top (a_Top),
- m_Bottom(a_Bottom)
- {
- }
-} ;
-
-typedef std::vector<cRavDefPoint> cRavDefPoints;
-
-
-
-
-
-class cStructGenRavines::cRavine
-{
- cRavDefPoints m_Points;
-
- /// Generates the shaping defpoints for the ravine, based on the ravine block coords and noise
- void GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise);
-
- /// Refines (adds and smooths) defpoints from a_Src into a_Dst
- void RefineDefPoints(const cRavDefPoints & a_Src, cRavDefPoints & a_Dst);
-
- /// Does one round of smoothing, two passes of RefineDefPoints()
- void Smooth(void);
-
- /// Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block
- void FinishLinear(void);
-
-public:
- // Coords for which the ravine was generated (not necessarily the center)
- int m_BlockX;
- int m_BlockZ;
-
- cRavine(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise);
-
- /// Carves the ravine into the chunk specified
- void ProcessChunk(
- int a_ChunkX, int a_ChunkZ,
- cChunkDef::BlockTypes & a_BlockTypes,
- cChunkDef::HeightMap & a_HeightMap
- );
-
- #ifdef _DEBUG
- /// Exports itself as a SVG line definition
- AString ExportAsSVG(int a_Color, int a_OffsetX = 0, int a_OffsetZ = 0) const;
- #endif // _DEBUG
-} ;
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cStructGenRavines:
-
-cStructGenRavines::cStructGenRavines(int a_Seed, int a_Size) :
- m_Noise(a_Seed),
- m_Size(a_Size)
-{
-}
-
-
-
-
-
-cStructGenRavines::~cStructGenRavines()
-{
- ClearCache();
-}
-
-
-
-
-
-void cStructGenRavines::ClearCache(void)
-{
- for (cRavines::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr)
- {
- delete *itr;
- } // for itr - m_Cache[]
- m_Cache.clear();
-}
-
-
-
-
-
-void cStructGenRavines::GenStructures(cChunkDesc & a_ChunkDesc)
-{
- int ChunkX = a_ChunkDesc.GetChunkX();
- int ChunkZ = a_ChunkDesc.GetChunkZ();
- cRavines Ravines;
- GetRavinesForChunk(ChunkX, ChunkZ, Ravines);
- for (cRavines::const_iterator itr = Ravines.begin(), end = Ravines.end(); itr != end; ++itr)
- {
- (*itr)->ProcessChunk(ChunkX, ChunkZ, a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap());
- } // for itr - Ravines[]
-}
-
-
-
-
-
-void cStructGenRavines::GetRavinesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenRavines::cRavines & a_Ravines)
-{
- int BaseX = a_ChunkX * cChunkDef::Width / m_Size;
- int BaseZ = a_ChunkZ * cChunkDef::Width / m_Size;
- if (BaseX < 0)
- {
- --BaseX;
- }
- if (BaseZ < 0)
- {
- --BaseZ;
- }
- BaseX -= 4;
- BaseZ -= 4;
-
- // Walk the cache, move each ravine that we want into a_Ravines:
- int StartX = BaseX * m_Size;
- int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_Size;
- int StartZ = BaseZ * m_Size;
- int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_Size;
- for (cRavines::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;)
- {
- if (
- ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) &&
- ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ)
- )
- {
- // want
- a_Ravines.push_back(*itr);
- itr = m_Cache.erase(itr);
- }
- else
- {
- // don't want
- ++itr;
- }
- } // for itr - m_Cache[]
-
- for (int x = 0; x < NEIGHBORHOOD_SIZE; x++)
- {
- int RealX = (BaseX + x) * m_Size;
- for (int z = 0; z < NEIGHBORHOOD_SIZE; z++)
- {
- int RealZ = (BaseZ + z) * m_Size;
- bool Found = false;
- for (cRavines::const_iterator itr = a_Ravines.begin(), end = a_Ravines.end(); itr != end; ++itr)
- {
- if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ))
- {
- Found = true;
- break;
- }
- }
- if (!Found)
- {
- a_Ravines.push_back(new cRavine(RealX, RealZ, m_Size, m_Noise));
- }
- }
- }
-
- // Copy a_Ravines into m_Cache to the beginning:
- cRavines RavinesCopy(a_Ravines);
- m_Cache.splice(m_Cache.begin(), RavinesCopy, RavinesCopy.begin(), RavinesCopy.end());
-
- // Trim the cache if it's too long:
- if (m_Cache.size() > 100)
- {
- cRavines::iterator itr = m_Cache.begin();
- std::advance(itr, 100);
- for (cRavines::iterator end = m_Cache.end(); itr != end; ++itr)
- {
- delete *itr;
- }
- itr = m_Cache.begin();
- std::advance(itr, 100);
- m_Cache.erase(itr, m_Cache.end());
- }
-
- /*
- #ifdef _DEBUG
- // DEBUG: Export as SVG into a file specific for the chunk, for visual verification:
- AString SVG;
- SVG.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1024\" height = \"1024\">\n");
- for (cRavines::const_iterator itr = a_Ravines.begin(), end = a_Ravines.end(); itr != end; ++itr)
- {
- SVG.append((*itr)->ExportAsSVG(0, 512, 512));
- }
- SVG.append("</svg>\n");
-
- AString fnam;
- Printf(fnam, "ravines\\%03d_%03d.svg", a_ChunkX, a_ChunkZ);
- cFile File(fnam, cFile::fmWrite);
- File.Write(SVG.c_str(), SVG.size());
- #endif // _DEBUG
- //*/
-}
-
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cStructGenRavines::cRavine
-
-cStructGenRavines::cRavine::cRavine(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise) :
- m_BlockX(a_BlockX),
- m_BlockZ(a_BlockZ)
-{
- // Calculate the ravine shape-defining points:
- GenerateBaseDefPoints(a_BlockX, a_BlockZ, a_Size, a_Noise);
-
- // Smooth the ravine. A two passes are needed:
- Smooth();
- Smooth();
-
- // Linearly interpolate the neighbors so that they're close enough together:
- FinishLinear();
-}
-
-
-
-
-
-void cStructGenRavines::cRavine::GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise)
-{
- // Modify the size slightly to have different-sized ravines (1/2 to 1/1 of a_Size):
- a_Size = (512 + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 11 * a_BlockZ, a_BlockX + a_BlockZ) / 17) % 512)) * a_Size / 1024;
-
- // The complete offset of the ravine from its cellpoint, up to 2 * a_Size in each direction
- int OffsetX = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 0) / 9) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * m_BlockZ, 1000) / 7) % (2 * a_Size)) - 2 * a_Size) / 2;
- int OffsetZ = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 2000) / 7) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * m_BlockZ, 3000) / 9) % (2 * a_Size)) - 2 * a_Size) / 2;
- int CenterX = a_BlockX + OffsetX;
- int CenterZ = a_BlockZ + OffsetZ;
-
- // Get the base angle in which the ravine "axis" goes:
- float Angle = (float)(((float)((a_Noise.IntNoise3DInt(20 * a_BlockX, 70 * a_BlockZ, 6000) / 9) % 16384)) / 16384.0 * 3.141592653);
- float xc = sin(Angle);
- float zc = cos(Angle);
-
- // Calculate the definition points and radii:
- int MaxRadius = (int)(sqrt(12.0 + ((a_Noise.IntNoise2DInt(61 * a_BlockX, 97 * a_BlockZ) / 13) % a_Size) / 16));
- int Top = 32 + ((a_Noise.IntNoise2DInt(13 * a_BlockX, 17 * a_BlockZ) / 23) % 32);
- int Bottom = 5 + ((a_Noise.IntNoise2DInt(17 * a_BlockX, 29 * a_BlockZ) / 13) % 32);
- int Mid = (Top + Bottom) / 2;
- int PointX = CenterX - (int)(xc * a_Size / 2);
- int PointZ = CenterZ - (int)(zc * a_Size / 2);
- m_Points.push_back(cRavDefPoint(PointX, PointZ, 0, (Mid + Top) / 2, (Mid + Bottom) / 2));
- for (int i = 1; i < NUM_RAVINE_POINTS - 1; i++)
- {
- int LineX = CenterX + (int)(xc * a_Size * (i - NUM_RAVINE_POINTS / 2) / NUM_RAVINE_POINTS);
- int LineZ = CenterZ + (int)(zc * a_Size * (i - NUM_RAVINE_POINTS / 2) / NUM_RAVINE_POINTS);
- // Amplitude is the amount of blocks that this point is away from the ravine "axis"
- int Amplitude = (a_Noise.IntNoise3DInt(70 * a_BlockX, 20 * a_BlockZ + 31 * i, 10000 * i) / 9) % a_Size;
- Amplitude = Amplitude / 4 - a_Size / 8; // Amplitude is in interval [-a_Size / 4, a_Size / 4]
- int PointX = LineX + (int)(zc * Amplitude);
- int PointZ = LineZ - (int)(xc * Amplitude);
- int Radius = MaxRadius - abs(i - NUM_RAVINE_POINTS / 2); // TODO: better radius function
- int ThisTop = Top + ((a_Noise.IntNoise3DInt(7 * a_BlockX, 19 * a_BlockZ, i * 31) / 13) % 8) - 4;
- int ThisBottom = Bottom + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 7 * a_BlockZ, i * 31) / 13) % 8) - 4;
- m_Points.push_back(cRavDefPoint(PointX, PointZ, Radius, ThisTop, ThisBottom));
- } // for i - m_Points[]
- PointX = CenterX + (int)(xc * a_Size / 2);
- PointZ = CenterZ + (int)(zc * a_Size / 2);
- m_Points.push_back(cRavDefPoint(PointX, PointZ, 0, Mid, Mid));
-}
-
-
-
-
-
-void cStructGenRavines::cRavine::RefineDefPoints(const cRavDefPoints & a_Src, cRavDefPoints & a_Dst)
-{
- // Smoothing: for each line segment, add points on its 1/4 lengths
- int Num = a_Src.size() - 2; // this many intermediary points
- a_Dst.clear();
- a_Dst.reserve(Num * 2 + 2);
- cRavDefPoints::const_iterator itr = a_Src.begin() + 1;
- a_Dst.push_back(a_Src.front());
- int PrevX = a_Src.front().m_BlockX;
- int PrevZ = a_Src.front().m_BlockZ;
- int PrevR = a_Src.front().m_Radius;
- int PrevT = a_Src.front().m_Top;
- int PrevB = a_Src.front().m_Bottom;
- for (int i = 0; i <= Num; ++i, ++itr)
- {
- int dx = itr->m_BlockX - PrevX;
- int dz = itr->m_BlockZ - PrevZ;
- if (abs(dx) + abs(dz) < 4)
- {
- // Too short a segment to smooth-subdivide into quarters
- continue;
- }
- int dr = itr->m_Radius - PrevR;
- int dt = itr->m_Top - PrevT;
- int db = itr->m_Bottom - PrevB;
- int Rad1 = std::max(PrevR + 1 * dr / 4, 1);
- int Rad2 = std::max(PrevR + 3 * dr / 4, 1);
- a_Dst.push_back(cRavDefPoint(PrevX + 1 * dx / 4, PrevZ + 1 * dz / 4, Rad1, PrevT + 1 * dt / 4, PrevB + 1 * db / 4));
- a_Dst.push_back(cRavDefPoint(PrevX + 3 * dx / 4, PrevZ + 3 * dz / 4, Rad2, PrevT + 3 * dt / 4, PrevB + 3 * db / 4));
- PrevX = itr->m_BlockX;
- PrevZ = itr->m_BlockZ;
- PrevR = itr->m_Radius;
- PrevT = itr->m_Top;
- PrevB = itr->m_Bottom;
- }
- a_Dst.push_back(a_Src.back());
-}
-
-
-
-
-
-void cStructGenRavines::cRavine::Smooth(void)
-{
- cRavDefPoints Pts;
- RefineDefPoints(m_Points, Pts); // Refine m_Points -> Pts
- RefineDefPoints(Pts, m_Points); // Refine Pts -> m_Points
-}
-
-
-
-
-
-void cStructGenRavines::cRavine::FinishLinear(void)
-{
- // For each segment, use Bresenham's line algorithm to draw a "line" of defpoints
- // _X 2012_07_20: I tried modifying this algorithm to produce "thick" lines (only one coord change per point)
- // But the results were about the same as the original, so I disposed of it again - no need to use twice the count of points
-
- cRavDefPoints Pts;
- std::swap(Pts, m_Points);
-
- m_Points.reserve(Pts.size() * 3);
- int PrevX = Pts.front().m_BlockX;
- int PrevZ = Pts.front().m_BlockZ;
- for (cRavDefPoints::const_iterator itr = Pts.begin() + 1, end = Pts.end(); itr != end; ++itr)
- {
- int x1 = itr->m_BlockX;
- int z1 = itr->m_BlockZ;
- int dx = abs(x1 - PrevX);
- int dz = abs(z1 - PrevZ);
- int sx = (PrevX < x1) ? 1 : -1;
- int sz = (PrevZ < z1) ? 1 : -1;
- int err = dx - dz;
- int R = itr->m_Radius;
- int T = itr->m_Top;
- int B = itr->m_Bottom;
- while (true)
- {
- m_Points.push_back(cRavDefPoint(PrevX, PrevZ, R, T, B));
- if ((PrevX == x1) && (PrevZ == z1))
- {
- break;
- }
- int e2 = 2 * err;
- if (e2 > -dz)
- {
- err -= dz;
- PrevX += sx;
- }
- if (e2 < dx)
- {
- err += dx;
- PrevZ += sz;
- }
- } // while (true)
- } // for itr
-}
-
-
-
-
-
-#ifdef _DEBUG
-AString cStructGenRavines::cRavine::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const
-{
- AString SVG;
- AppendPrintf(SVG, "<path style=\"fill:none;stroke:#%06x;stroke-width:1px;\"\nd=\"", a_Color);
- char Prefix = 'M'; // The first point needs "M" prefix, all the others need "L"
- for (cRavDefPoints::const_iterator itr = m_Points.begin(); itr != m_Points.end(); ++itr)
- {
- AppendPrintf(SVG, "%c %d,%d ", Prefix, a_OffsetX + itr->m_BlockX, a_OffsetZ + itr->m_BlockZ);
- Prefix = 'L';
- }
- SVG.append("\"/>\n");
-
- // Base point highlight:
- AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
- a_OffsetX + m_BlockX - 5, a_OffsetZ + m_BlockZ, a_OffsetX + m_BlockX + 5, a_OffsetZ + m_BlockZ
- );
- AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
- a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ - 5, a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ + 5
- );
-
- // A gray line from the base point to the first point of the ravine, for identification:
- AppendPrintf(SVG, "<path style=\"fill:none;stroke:#cfcfcf;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
- a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ, a_OffsetX + m_Points.front().m_BlockX, a_OffsetZ + m_Points.front().m_BlockZ
- );
-
- // Offset guides:
- if (a_OffsetX > 0)
- {
- AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M %d,0 L %d,1024\"/>\n",
- a_OffsetX, a_OffsetX
- );
- }
- if (a_OffsetZ > 0)
- {
- AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M 0,%d L 1024,%d\"/>\n",
- a_OffsetZ, a_OffsetZ
- );
- }
- return SVG;
-}
-#endif // _DEBUG
-
-
-
-
-
-void cStructGenRavines::cRavine::ProcessChunk(
- int a_ChunkX, int a_ChunkZ,
- cChunkDef::BlockTypes & a_BlockTypes,
- cChunkDef::HeightMap & a_HeightMap
-)
-{
- int BlockStartX = a_ChunkX * cChunkDef::Width;
- int BlockStartZ = a_ChunkZ * cChunkDef::Width;
- int BlockEndX = BlockStartX + cChunkDef::Width;
- int BlockEndZ = BlockStartZ + cChunkDef::Width;
- for (cRavDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr)
- {
- if (
- (itr->m_BlockX + itr->m_Radius < BlockStartX) ||
- (itr->m_BlockX - itr->m_Radius > BlockEndX) ||
- (itr->m_BlockZ + itr->m_Radius < BlockStartZ) ||
- (itr->m_BlockZ - itr->m_Radius > BlockEndZ)
- )
- {
- // Cannot intersect, bail out early
- continue;
- }
-
- // Carve out a cylinder around the xz point, m_Radius in diameter, from Bottom to Top:
- int RadiusSq = itr->m_Radius * itr->m_Radius; // instead of doing sqrt for each distance, we do sqr of the radius
- int DifX = BlockStartX - itr->m_BlockX; // substitution for faster calc
- int DifZ = BlockStartZ - itr->m_BlockZ; // substitution for faster calc
- for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++)
- {
- #ifdef _DEBUG
- // DEBUG: Make the ravine shapepoints visible on a single layer (so that we can see with Minutor what's going on)
- if ((DifX + x == 0) && (DifZ + z == 0))
- {
- cChunkDef::SetBlock(a_BlockTypes, x, 4, z, E_BLOCK_LAPIS_ORE);
- }
- #endif // _DEBUG
-
- int DistSq = (DifX + x) * (DifX + x) + (DifZ + z) * (DifZ + z);
- if (DistSq <= RadiusSq)
- {
- int Top = std::min(itr->m_Top, (int)(cChunkDef::Height)); // Stupid gcc needs int cast
- for (int y = std::max(itr->m_Bottom, 1); y <= Top; y++)
- {
- switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z))
- {
- // Only carve out these specific block types
- case E_BLOCK_DIRT:
- case E_BLOCK_GRASS:
- case E_BLOCK_STONE:
- case E_BLOCK_COBBLESTONE:
- case E_BLOCK_GRAVEL:
- case E_BLOCK_SAND:
- case E_BLOCK_SANDSTONE:
- case E_BLOCK_NETHERRACK:
- case E_BLOCK_COAL_ORE:
- case E_BLOCK_IRON_ORE:
- case E_BLOCK_GOLD_ORE:
- case E_BLOCK_DIAMOND_ORE:
- case E_BLOCK_REDSTONE_ORE:
- case E_BLOCK_REDSTONE_ORE_GLOWING:
- {
- cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR);
- break;
- }
- default: break;
- }
- }
- }
- } // for x, z - a_BlockTypes
- } // for itr - m_Points[]
-}
-
-
-
-
+
+// Ravines.cpp
+
+// Implements the cStructGenRavines class representing the ravine structure generator
+
+#include "Globals.h"
+#include "Ravines.h"
+
+
+
+
+/// How many ravines in each direction are generated for a given chunk. Must be an even number
+static const int NEIGHBORHOOD_SIZE = 8;
+
+static const int NUM_RAVINE_POINTS = 4;
+
+
+
+
+
+struct cRavDefPoint
+{
+ int m_BlockX;
+ int m_BlockZ;
+ int m_Radius;
+ int m_Top;
+ int m_Bottom;
+
+ cRavDefPoint(int a_BlockX, int a_BlockZ, int a_Radius, int a_Top, int a_Bottom) :
+ m_BlockX(a_BlockX),
+ m_BlockZ(a_BlockZ),
+ m_Radius(a_Radius),
+ m_Top (a_Top),
+ m_Bottom(a_Bottom)
+ {
+ }
+} ;
+
+typedef std::vector<cRavDefPoint> cRavDefPoints;
+
+
+
+
+
+class cStructGenRavines::cRavine
+{
+ cRavDefPoints m_Points;
+
+ /// Generates the shaping defpoints for the ravine, based on the ravine block coords and noise
+ void GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise);
+
+ /// Refines (adds and smooths) defpoints from a_Src into a_Dst
+ void RefineDefPoints(const cRavDefPoints & a_Src, cRavDefPoints & a_Dst);
+
+ /// Does one round of smoothing, two passes of RefineDefPoints()
+ void Smooth(void);
+
+ /// Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block
+ void FinishLinear(void);
+
+public:
+ // Coords for which the ravine was generated (not necessarily the center)
+ int m_BlockX;
+ int m_BlockZ;
+
+ cRavine(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise);
+
+ /// Carves the ravine into the chunk specified
+ void ProcessChunk(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes,
+ cChunkDef::HeightMap & a_HeightMap
+ );
+
+ #ifdef _DEBUG
+ /// Exports itself as a SVG line definition
+ AString ExportAsSVG(int a_Color, int a_OffsetX = 0, int a_OffsetZ = 0) const;
+ #endif // _DEBUG
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenRavines:
+
+cStructGenRavines::cStructGenRavines(int a_Seed, int a_Size) :
+ m_Noise(a_Seed),
+ m_Size(a_Size)
+{
+}
+
+
+
+
+
+cStructGenRavines::~cStructGenRavines()
+{
+ ClearCache();
+}
+
+
+
+
+
+void cStructGenRavines::ClearCache(void)
+{
+ for (cRavines::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr)
+ {
+ delete *itr;
+ } // for itr - m_Cache[]
+ m_Cache.clear();
+}
+
+
+
+
+
+void cStructGenRavines::GenStructures(cChunkDesc & a_ChunkDesc)
+{
+ int ChunkX = a_ChunkDesc.GetChunkX();
+ int ChunkZ = a_ChunkDesc.GetChunkZ();
+ cRavines Ravines;
+ GetRavinesForChunk(ChunkX, ChunkZ, Ravines);
+ for (cRavines::const_iterator itr = Ravines.begin(), end = Ravines.end(); itr != end; ++itr)
+ {
+ (*itr)->ProcessChunk(ChunkX, ChunkZ, a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap());
+ } // for itr - Ravines[]
+}
+
+
+
+
+
+void cStructGenRavines::GetRavinesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenRavines::cRavines & a_Ravines)
+{
+ int BaseX = a_ChunkX * cChunkDef::Width / m_Size;
+ int BaseZ = a_ChunkZ * cChunkDef::Width / m_Size;
+ if (BaseX < 0)
+ {
+ --BaseX;
+ }
+ if (BaseZ < 0)
+ {
+ --BaseZ;
+ }
+ BaseX -= 4;
+ BaseZ -= 4;
+
+ // Walk the cache, move each ravine that we want into a_Ravines:
+ int StartX = BaseX * m_Size;
+ int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_Size;
+ int StartZ = BaseZ * m_Size;
+ int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_Size;
+ for (cRavines::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;)
+ {
+ if (
+ ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) &&
+ ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ)
+ )
+ {
+ // want
+ a_Ravines.push_back(*itr);
+ itr = m_Cache.erase(itr);
+ }
+ else
+ {
+ // don't want
+ ++itr;
+ }
+ } // for itr - m_Cache[]
+
+ for (int x = 0; x < NEIGHBORHOOD_SIZE; x++)
+ {
+ int RealX = (BaseX + x) * m_Size;
+ for (int z = 0; z < NEIGHBORHOOD_SIZE; z++)
+ {
+ int RealZ = (BaseZ + z) * m_Size;
+ bool Found = false;
+ for (cRavines::const_iterator itr = a_Ravines.begin(), end = a_Ravines.end(); itr != end; ++itr)
+ {
+ if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ))
+ {
+ Found = true;
+ break;
+ }
+ }
+ if (!Found)
+ {
+ a_Ravines.push_back(new cRavine(RealX, RealZ, m_Size, m_Noise));
+ }
+ }
+ }
+
+ // Copy a_Ravines into m_Cache to the beginning:
+ cRavines RavinesCopy(a_Ravines);
+ m_Cache.splice(m_Cache.begin(), RavinesCopy, RavinesCopy.begin(), RavinesCopy.end());
+
+ // Trim the cache if it's too long:
+ if (m_Cache.size() > 100)
+ {
+ cRavines::iterator itr = m_Cache.begin();
+ std::advance(itr, 100);
+ for (cRavines::iterator end = m_Cache.end(); itr != end; ++itr)
+ {
+ delete *itr;
+ }
+ itr = m_Cache.begin();
+ std::advance(itr, 100);
+ m_Cache.erase(itr, m_Cache.end());
+ }
+
+ /*
+ #ifdef _DEBUG
+ // DEBUG: Export as SVG into a file specific for the chunk, for visual verification:
+ AString SVG;
+ SVG.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1024\" height = \"1024\">\n");
+ for (cRavines::const_iterator itr = a_Ravines.begin(), end = a_Ravines.end(); itr != end; ++itr)
+ {
+ SVG.append((*itr)->ExportAsSVG(0, 512, 512));
+ }
+ SVG.append("</svg>\n");
+
+ AString fnam;
+ Printf(fnam, "ravines\\%03d_%03d.svg", a_ChunkX, a_ChunkZ);
+ cFile File(fnam, cFile::fmWrite);
+ File.Write(SVG.c_str(), SVG.size());
+ #endif // _DEBUG
+ //*/
+}
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenRavines::cRavine
+
+cStructGenRavines::cRavine::cRavine(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise) :
+ m_BlockX(a_BlockX),
+ m_BlockZ(a_BlockZ)
+{
+ // Calculate the ravine shape-defining points:
+ GenerateBaseDefPoints(a_BlockX, a_BlockZ, a_Size, a_Noise);
+
+ // Smooth the ravine. A two passes are needed:
+ Smooth();
+ Smooth();
+
+ // Linearly interpolate the neighbors so that they're close enough together:
+ FinishLinear();
+}
+
+
+
+
+
+void cStructGenRavines::cRavine::GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise)
+{
+ // Modify the size slightly to have different-sized ravines (1/2 to 1/1 of a_Size):
+ a_Size = (512 + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 11 * a_BlockZ, a_BlockX + a_BlockZ) / 17) % 512)) * a_Size / 1024;
+
+ // The complete offset of the ravine from its cellpoint, up to 2 * a_Size in each direction
+ int OffsetX = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 0) / 9) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * m_BlockZ, 1000) / 7) % (2 * a_Size)) - 2 * a_Size) / 2;
+ int OffsetZ = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 2000) / 7) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * m_BlockZ, 3000) / 9) % (2 * a_Size)) - 2 * a_Size) / 2;
+ int CenterX = a_BlockX + OffsetX;
+ int CenterZ = a_BlockZ + OffsetZ;
+
+ // Get the base angle in which the ravine "axis" goes:
+ float Angle = (float)(((float)((a_Noise.IntNoise3DInt(20 * a_BlockX, 70 * a_BlockZ, 6000) / 9) % 16384)) / 16384.0 * 3.141592653);
+ float xc = sin(Angle);
+ float zc = cos(Angle);
+
+ // Calculate the definition points and radii:
+ int MaxRadius = (int)(sqrt(12.0 + ((a_Noise.IntNoise2DInt(61 * a_BlockX, 97 * a_BlockZ) / 13) % a_Size) / 16));
+ int Top = 32 + ((a_Noise.IntNoise2DInt(13 * a_BlockX, 17 * a_BlockZ) / 23) % 32);
+ int Bottom = 5 + ((a_Noise.IntNoise2DInt(17 * a_BlockX, 29 * a_BlockZ) / 13) % 32);
+ int Mid = (Top + Bottom) / 2;
+ int PointX = CenterX - (int)(xc * a_Size / 2);
+ int PointZ = CenterZ - (int)(zc * a_Size / 2);
+ m_Points.push_back(cRavDefPoint(PointX, PointZ, 0, (Mid + Top) / 2, (Mid + Bottom) / 2));
+ for (int i = 1; i < NUM_RAVINE_POINTS - 1; i++)
+ {
+ int LineX = CenterX + (int)(xc * a_Size * (i - NUM_RAVINE_POINTS / 2) / NUM_RAVINE_POINTS);
+ int LineZ = CenterZ + (int)(zc * a_Size * (i - NUM_RAVINE_POINTS / 2) / NUM_RAVINE_POINTS);
+ // Amplitude is the amount of blocks that this point is away from the ravine "axis"
+ int Amplitude = (a_Noise.IntNoise3DInt(70 * a_BlockX, 20 * a_BlockZ + 31 * i, 10000 * i) / 9) % a_Size;
+ Amplitude = Amplitude / 4 - a_Size / 8; // Amplitude is in interval [-a_Size / 4, a_Size / 4]
+ int PointX = LineX + (int)(zc * Amplitude);
+ int PointZ = LineZ - (int)(xc * Amplitude);
+ int Radius = MaxRadius - abs(i - NUM_RAVINE_POINTS / 2); // TODO: better radius function
+ int ThisTop = Top + ((a_Noise.IntNoise3DInt(7 * a_BlockX, 19 * a_BlockZ, i * 31) / 13) % 8) - 4;
+ int ThisBottom = Bottom + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 7 * a_BlockZ, i * 31) / 13) % 8) - 4;
+ m_Points.push_back(cRavDefPoint(PointX, PointZ, Radius, ThisTop, ThisBottom));
+ } // for i - m_Points[]
+ PointX = CenterX + (int)(xc * a_Size / 2);
+ PointZ = CenterZ + (int)(zc * a_Size / 2);
+ m_Points.push_back(cRavDefPoint(PointX, PointZ, 0, Mid, Mid));
+}
+
+
+
+
+
+void cStructGenRavines::cRavine::RefineDefPoints(const cRavDefPoints & a_Src, cRavDefPoints & a_Dst)
+{
+ // Smoothing: for each line segment, add points on its 1/4 lengths
+ int Num = a_Src.size() - 2; // this many intermediary points
+ a_Dst.clear();
+ a_Dst.reserve(Num * 2 + 2);
+ cRavDefPoints::const_iterator itr = a_Src.begin() + 1;
+ a_Dst.push_back(a_Src.front());
+ int PrevX = a_Src.front().m_BlockX;
+ int PrevZ = a_Src.front().m_BlockZ;
+ int PrevR = a_Src.front().m_Radius;
+ int PrevT = a_Src.front().m_Top;
+ int PrevB = a_Src.front().m_Bottom;
+ for (int i = 0; i <= Num; ++i, ++itr)
+ {
+ int dx = itr->m_BlockX - PrevX;
+ int dz = itr->m_BlockZ - PrevZ;
+ if (abs(dx) + abs(dz) < 4)
+ {
+ // Too short a segment to smooth-subdivide into quarters
+ continue;
+ }
+ int dr = itr->m_Radius - PrevR;
+ int dt = itr->m_Top - PrevT;
+ int db = itr->m_Bottom - PrevB;
+ int Rad1 = std::max(PrevR + 1 * dr / 4, 1);
+ int Rad2 = std::max(PrevR + 3 * dr / 4, 1);
+ a_Dst.push_back(cRavDefPoint(PrevX + 1 * dx / 4, PrevZ + 1 * dz / 4, Rad1, PrevT + 1 * dt / 4, PrevB + 1 * db / 4));
+ a_Dst.push_back(cRavDefPoint(PrevX + 3 * dx / 4, PrevZ + 3 * dz / 4, Rad2, PrevT + 3 * dt / 4, PrevB + 3 * db / 4));
+ PrevX = itr->m_BlockX;
+ PrevZ = itr->m_BlockZ;
+ PrevR = itr->m_Radius;
+ PrevT = itr->m_Top;
+ PrevB = itr->m_Bottom;
+ }
+ a_Dst.push_back(a_Src.back());
+}
+
+
+
+
+
+void cStructGenRavines::cRavine::Smooth(void)
+{
+ cRavDefPoints Pts;
+ RefineDefPoints(m_Points, Pts); // Refine m_Points -> Pts
+ RefineDefPoints(Pts, m_Points); // Refine Pts -> m_Points
+}
+
+
+
+
+
+void cStructGenRavines::cRavine::FinishLinear(void)
+{
+ // For each segment, use Bresenham's line algorithm to draw a "line" of defpoints
+ // _X 2012_07_20: I tried modifying this algorithm to produce "thick" lines (only one coord change per point)
+ // But the results were about the same as the original, so I disposed of it again - no need to use twice the count of points
+
+ cRavDefPoints Pts;
+ std::swap(Pts, m_Points);
+
+ m_Points.reserve(Pts.size() * 3);
+ int PrevX = Pts.front().m_BlockX;
+ int PrevZ = Pts.front().m_BlockZ;
+ for (cRavDefPoints::const_iterator itr = Pts.begin() + 1, end = Pts.end(); itr != end; ++itr)
+ {
+ int x1 = itr->m_BlockX;
+ int z1 = itr->m_BlockZ;
+ int dx = abs(x1 - PrevX);
+ int dz = abs(z1 - PrevZ);
+ int sx = (PrevX < x1) ? 1 : -1;
+ int sz = (PrevZ < z1) ? 1 : -1;
+ int err = dx - dz;
+ int R = itr->m_Radius;
+ int T = itr->m_Top;
+ int B = itr->m_Bottom;
+ while (true)
+ {
+ m_Points.push_back(cRavDefPoint(PrevX, PrevZ, R, T, B));
+ if ((PrevX == x1) && (PrevZ == z1))
+ {
+ break;
+ }
+ int e2 = 2 * err;
+ if (e2 > -dz)
+ {
+ err -= dz;
+ PrevX += sx;
+ }
+ if (e2 < dx)
+ {
+ err += dx;
+ PrevZ += sz;
+ }
+ } // while (true)
+ } // for itr
+}
+
+
+
+
+
+#ifdef _DEBUG
+AString cStructGenRavines::cRavine::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const
+{
+ AString SVG;
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#%06x;stroke-width:1px;\"\nd=\"", a_Color);
+ char Prefix = 'M'; // The first point needs "M" prefix, all the others need "L"
+ for (cRavDefPoints::const_iterator itr = m_Points.begin(); itr != m_Points.end(); ++itr)
+ {
+ AppendPrintf(SVG, "%c %d,%d ", Prefix, a_OffsetX + itr->m_BlockX, a_OffsetZ + itr->m_BlockZ);
+ Prefix = 'L';
+ }
+ SVG.append("\"/>\n");
+
+ // Base point highlight:
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
+ a_OffsetX + m_BlockX - 5, a_OffsetZ + m_BlockZ, a_OffsetX + m_BlockX + 5, a_OffsetZ + m_BlockZ
+ );
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
+ a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ - 5, a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ + 5
+ );
+
+ // A gray line from the base point to the first point of the ravine, for identification:
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#cfcfcf;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
+ a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ, a_OffsetX + m_Points.front().m_BlockX, a_OffsetZ + m_Points.front().m_BlockZ
+ );
+
+ // Offset guides:
+ if (a_OffsetX > 0)
+ {
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M %d,0 L %d,1024\"/>\n",
+ a_OffsetX, a_OffsetX
+ );
+ }
+ if (a_OffsetZ > 0)
+ {
+ AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M 0,%d L 1024,%d\"/>\n",
+ a_OffsetZ, a_OffsetZ
+ );
+ }
+ return SVG;
+}
+#endif // _DEBUG
+
+
+
+
+
+void cStructGenRavines::cRavine::ProcessChunk(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes,
+ cChunkDef::HeightMap & a_HeightMap
+)
+{
+ int BlockStartX = a_ChunkX * cChunkDef::Width;
+ int BlockStartZ = a_ChunkZ * cChunkDef::Width;
+ int BlockEndX = BlockStartX + cChunkDef::Width;
+ int BlockEndZ = BlockStartZ + cChunkDef::Width;
+ for (cRavDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr)
+ {
+ if (
+ (itr->m_BlockX + itr->m_Radius < BlockStartX) ||
+ (itr->m_BlockX - itr->m_Radius > BlockEndX) ||
+ (itr->m_BlockZ + itr->m_Radius < BlockStartZ) ||
+ (itr->m_BlockZ - itr->m_Radius > BlockEndZ)
+ )
+ {
+ // Cannot intersect, bail out early
+ continue;
+ }
+
+ // Carve out a cylinder around the xz point, m_Radius in diameter, from Bottom to Top:
+ int RadiusSq = itr->m_Radius * itr->m_Radius; // instead of doing sqrt for each distance, we do sqr of the radius
+ int DifX = BlockStartX - itr->m_BlockX; // substitution for faster calc
+ int DifZ = BlockStartZ - itr->m_BlockZ; // substitution for faster calc
+ for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ #ifdef _DEBUG
+ // DEBUG: Make the ravine shapepoints visible on a single layer (so that we can see with Minutor what's going on)
+ if ((DifX + x == 0) && (DifZ + z == 0))
+ {
+ cChunkDef::SetBlock(a_BlockTypes, x, 4, z, E_BLOCK_LAPIS_ORE);
+ }
+ #endif // _DEBUG
+
+ int DistSq = (DifX + x) * (DifX + x) + (DifZ + z) * (DifZ + z);
+ if (DistSq <= RadiusSq)
+ {
+ int Top = std::min(itr->m_Top, (int)(cChunkDef::Height)); // Stupid gcc needs int cast
+ for (int y = std::max(itr->m_Bottom, 1); y <= Top; y++)
+ {
+ switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z))
+ {
+ // Only carve out these specific block types
+ case E_BLOCK_DIRT:
+ case E_BLOCK_GRASS:
+ case E_BLOCK_STONE:
+ case E_BLOCK_COBBLESTONE:
+ case E_BLOCK_GRAVEL:
+ case E_BLOCK_SAND:
+ case E_BLOCK_SANDSTONE:
+ case E_BLOCK_NETHERRACK:
+ case E_BLOCK_COAL_ORE:
+ case E_BLOCK_IRON_ORE:
+ case E_BLOCK_GOLD_ORE:
+ case E_BLOCK_DIAMOND_ORE:
+ case E_BLOCK_REDSTONE_ORE:
+ case E_BLOCK_REDSTONE_ORE_GLOWING:
+ {
+ cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR);
+ break;
+ }
+ default: break;
+ }
+ }
+ }
+ } // for x, z - a_BlockTypes
+ } // for itr - m_Points[]
+}
+
+
+
+
diff --git a/source/Generating/Ravines.h b/source/Generating/Ravines.h
index 1c559c70d..05164a5b2 100644
--- a/source/Generating/Ravines.h
+++ b/source/Generating/Ravines.h
@@ -1,46 +1,46 @@
-
-// Ravines.h
-
-// Interfaces to the cStructGenRavines class representing the ravine structure generator
-
-
-
-
-
-#pragma once
-
-#include "ComposableGenerator.h"
-#include "../Noise.h"
-
-
-
-
-
-class cStructGenRavines :
- public cStructureGen
-{
-public:
- cStructGenRavines(int a_Seed, int a_Size);
- ~cStructGenRavines();
-
-protected:
- class cRavine; // fwd: Ravines.cpp
- typedef std::list<cRavine *> cRavines;
-
- cNoise m_Noise;
- int m_Size; // Max size, in blocks, of the ravines generated
- cRavines m_Cache;
-
- /// Clears everything from the cache
- void ClearCache(void);
-
- /// Returns all ravines that *may* intersect the given chunk. All the ravines are valid until the next call to this function.
- void GetRavinesForChunk(int a_ChunkX, int a_ChunkZ, cRavines & a_Ravines);
-
- // cStructureGen override:
- virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
-} ;
-
-
-
-
+
+// Ravines.h
+
+// Interfaces to the cStructGenRavines class representing the ravine structure generator
+
+
+
+
+
+#pragma once
+
+#include "ComposableGenerator.h"
+#include "../Noise.h"
+
+
+
+
+
+class cStructGenRavines :
+ public cStructureGen
+{
+public:
+ cStructGenRavines(int a_Seed, int a_Size);
+ ~cStructGenRavines();
+
+protected:
+ class cRavine; // fwd: Ravines.cpp
+ typedef std::list<cRavine *> cRavines;
+
+ cNoise m_Noise;
+ int m_Size; // Max size, in blocks, of the ravines generated
+ cRavines m_Cache;
+
+ /// Clears everything from the cache
+ void ClearCache(void);
+
+ /// Returns all ravines that *may* intersect the given chunk. All the ravines are valid until the next call to this function.
+ void GetRavinesForChunk(int a_ChunkX, int a_ChunkZ, cRavines & a_Ravines);
+
+ // cStructureGen override:
+ virtual void GenStructures(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
diff --git a/source/ItemGrid.cpp b/source/ItemGrid.cpp
index 4e424a668..a977a9b6d 100644
--- a/source/ItemGrid.cpp
+++ b/source/ItemGrid.cpp
@@ -1,662 +1,662 @@
-
-// ItemGrid.cpp
-
-// Implements the cItemGrid class representing a storage for items in a XY grid (chests, dispensers, inventory etc.)
-
-#include "Globals.h"
-#include "ItemGrid.h"
-#include "Items/ItemHandler.h"
-#include "Noise.h"
-
-
-
-
-
-cItemGrid::cItemGrid(int a_Width, int a_Height) :
- m_Width(a_Width),
- m_Height(a_Height),
- m_NumSlots(a_Width * a_Height),
- m_Slots(new cItem[a_Width * a_Height]),
- m_IsInTriggerListeners(false)
-{
-}
-
-
-
-
-
-cItemGrid::~cItemGrid()
-{
- delete[] m_Slots;
-}
-
-
-
-
-
-int cItemGrid::GetSlotNum(int a_X, int a_Y) const
-{
- if (
- (a_X < 0) || (a_X >= m_Width) ||
- (a_Y < 0) || (a_Y >= m_Height)
- )
- {
- LOGWARNING("%s: coords out of range: (%d, %d) in grid of size (%d, %d)",
- __FUNCTION__, a_X, a_Y, m_Width, m_Height
- );
- return -1;
- }
- return a_X + m_Width * a_Y;
-}
-
-
-
-
-
-void cItemGrid::GetSlotCoords(int a_SlotNum, int & a_X, int & a_Y) const
-{
- if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
- {
- LOGWARNING("%s: SlotNum out of range: %d in grid of range %d",
- __FUNCTION__, a_SlotNum, m_NumSlots
- );
- a_X = -1;
- a_Y = -1;
- return;
- }
- a_X = a_SlotNum % m_Width;
- a_Y = a_SlotNum / m_Width;
-}
-
-
-
-
-
-const cItem & cItemGrid::GetSlot(int a_X, int a_Y) const
-{
- return GetSlot(GetSlotNum(a_X, a_Y));
-}
-
-
-
-
-
-const cItem & cItemGrid::GetSlot(int a_SlotNum) const
-{
- if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
- {
- LOGWARNING("%s: Invalid slot number, %d out of %d slots",
- __FUNCTION__, a_SlotNum, m_NumSlots
- );
- return m_Slots[0];
- }
- return m_Slots[a_SlotNum];
-}
-
-
-
-
-
-void cItemGrid::SetSlot(int a_X, int a_Y, const cItem & a_Item)
-{
- SetSlot(GetSlotNum(a_X, a_Y), a_Item);
-}
-
-
-
-
-
-void cItemGrid::SetSlot(int a_X, int a_Y, short a_ItemType, char a_ItemCount, short a_ItemDamage)
-{
- SetSlot(GetSlotNum(a_X, a_Y), cItem(a_ItemType, a_ItemCount, a_ItemDamage));
-}
-
-
-
-
-
-void cItemGrid::SetSlot(int a_SlotNum, const cItem & a_Item)
-{
- if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
- {
- LOGWARNING("%s: Invalid slot number %d out of %d slots",
- __FUNCTION__, a_SlotNum, m_NumSlots
- );
- return;
- }
- m_Slots[a_SlotNum] = a_Item;
- TriggerListeners(a_SlotNum);
-}
-
-
-
-
-
-void cItemGrid::SetSlot(int a_SlotNum, short a_ItemType, char a_ItemCount, short a_ItemDamage)
-{
- SetSlot(a_SlotNum, cItem(a_ItemType, a_ItemCount, a_ItemDamage));
-}
-
-
-
-
-
-void cItemGrid::EmptySlot(int a_X, int a_Y)
-{
- EmptySlot(GetSlotNum(a_X, a_Y));
-}
-
-
-
-
-
-void cItemGrid::EmptySlot(int a_SlotNum)
-{
- if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
- {
- LOGWARNING("%s: Invalid slot number %d out of %d slots",
- __FUNCTION__, a_SlotNum, m_NumSlots
- );
- return;
- }
-
- // Check if already empty:
- if (m_Slots[a_SlotNum].IsEmpty())
- {
- return;
- }
-
- // Empty and notify
- m_Slots[a_SlotNum].Empty();
- TriggerListeners(a_SlotNum);
-}
-
-
-
-
-
-bool cItemGrid::IsSlotEmpty(int a_SlotNum) const
-{
- if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
- {
- LOGWARNING("%s: Invalid slot number %d out of %d slots",
- __FUNCTION__, a_SlotNum, m_NumSlots
- );
- return true;
- }
- return m_Slots[a_SlotNum].IsEmpty();
-}
-
-
-
-
-
-bool cItemGrid::IsSlotEmpty(int a_X, int a_Y) const
-{
- return IsSlotEmpty(GetSlotNum(a_X, a_Y));
-}
-
-
-
-
-
-void cItemGrid::Clear(void)
-{
- for (int i = 0; i < m_NumSlots; i++)
- {
- m_Slots[i].Empty();
- TriggerListeners(i);
- }
-}
-
-
-
-
-
-int cItemGrid::HowManyCanFit(const cItem & a_ItemStack)
-{
- char NumLeft = a_ItemStack.m_ItemCount;
- int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize();
- for (int i = m_NumSlots - 1; i >= 0; i--)
- {
- if (m_Slots[i].IsEmpty())
- {
- NumLeft -= MaxStack;
- }
- else if (m_Slots[i].IsStackableWith(a_ItemStack))
- {
- NumLeft -= MaxStack - m_Slots[i].m_ItemCount;
- }
- if (NumLeft <= 0)
- {
- // All items fit
- return a_ItemStack.m_ItemCount;
- }
- } // for i - m_Slots[]
- return a_ItemStack.m_ItemCount - NumLeft;
-}
-
-
-
-
-
-int cItemGrid::AddItemToSlot(const cItem & a_ItemStack, int a_Slot, int a_Num, int a_MaxStack)
-{
- int PrevCount = 0;
- if (m_Slots[a_Slot].IsEmpty())
- {
- m_Slots[a_Slot] = a_ItemStack;
- PrevCount = 0;
- }
- else
- {
- PrevCount = m_Slots[a_Slot].m_ItemCount;
- }
- m_Slots[a_Slot].m_ItemCount = std::min(a_MaxStack, PrevCount + a_Num);
- int toReturn = m_Slots[a_Slot].m_ItemCount - PrevCount;
- TriggerListeners(a_Slot);
- return toReturn;
-}
-
-
-
-
-
-int cItemGrid::AddItem(cItem & a_ItemStack, bool a_AllowNewStacks, int a_PrioritarySlot)
-{
- int NumLeft = a_ItemStack.m_ItemCount;
- int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize();
-
- // Try prioritarySlot first:
- if (
- (a_PrioritarySlot != -1) &&
- (
- m_Slots[a_PrioritarySlot].IsEmpty() ||
- m_Slots[a_PrioritarySlot].IsStackableWith(a_ItemStack)
- )
- )
- {
- NumLeft -= AddItemToSlot(a_ItemStack, a_PrioritarySlot, NumLeft, MaxStack);
- }
-
- // Scan existing stacks:
- for (int i = m_NumSlots - 1; i >= 0; i--)
- {
- if (m_Slots[i].IsStackableWith(a_ItemStack))
- {
- NumLeft -= AddItemToSlot(a_ItemStack, i, NumLeft, MaxStack);
- }
- if (NumLeft <= 0)
- {
- // All items fit
- return a_ItemStack.m_ItemCount;
- }
- } // for i - m_Slots[]
-
- if (!a_AllowNewStacks)
- {
- return (a_ItemStack.m_ItemCount - NumLeft);
- }
-
- for (int i = m_NumSlots - 1; i >= 0; i--)
- {
- if (m_Slots[i].IsEmpty())
- {
- NumLeft -= AddItemToSlot(a_ItemStack, i, NumLeft, MaxStack);
- }
- if (NumLeft <= 0)
- {
- // All items fit
- return a_ItemStack.m_ItemCount;
- }
- } // for i - m_Slots[]
- return (a_ItemStack.m_ItemCount - NumLeft);
-}
-
-
-
-
-
-int cItemGrid::AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks, int a_PrioritarySlot)
-{
- int TotalAdded = 0;
- for (cItems::iterator itr = a_ItemStackList.begin(); itr != a_ItemStackList.end();)
- {
- int NumAdded = AddItem(*itr, a_AllowNewStacks, a_PrioritarySlot);
- if (itr->m_ItemCount == NumAdded)
- {
- itr = a_ItemStackList.erase(itr);
- }
- else
- {
- itr->m_ItemCount -= NumAdded;
- ++itr;
- }
- TotalAdded += NumAdded;
- }
- return TotalAdded;
-}
-
-
-
-
-
-int cItemGrid::ChangeSlotCount(int a_SlotNum, int a_AddToCount)
-{
- if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
- {
- LOGWARNING("%s: Invalid slot number %d out of %d slots, ignoring the call, returning -1",
- __FUNCTION__, a_SlotNum, m_NumSlots
- );
- return -1;
- }
-
- if (m_Slots[a_SlotNum].IsEmpty())
- {
- // The item is empty, it's not gonna change
- return 0;
- }
-
- if (m_Slots[a_SlotNum].m_ItemCount <= -a_AddToCount)
- {
- // Trying to remove more items than there already are, make the item empty
- m_Slots[a_SlotNum].Empty();
- TriggerListeners(a_SlotNum);
- return 0;
- }
-
- m_Slots[a_SlotNum].m_ItemCount += a_AddToCount;
- TriggerListeners(a_SlotNum);
- return m_Slots[a_SlotNum].m_ItemCount;
-}
-
-
-
-
-
-int cItemGrid::ChangeSlotCount(int a_X, int a_Y, int a_AddToCount)
-{
- return ChangeSlotCount(GetSlotNum(a_X, a_Y), a_AddToCount);
-}
-
-
-
-
-
-cItem cItemGrid::RemoveOneItem(int a_SlotNum)
-{
- if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
- {
- LOGWARNING("%s: Invalid slot number %d out of %d slots, ignoring the call, returning empty item",
- __FUNCTION__, a_SlotNum, m_NumSlots
- );
- return cItem();
- }
-
- // If the slot is empty, return an empty item
- if (m_Slots[a_SlotNum].IsEmpty())
- {
- return cItem();
- }
-
- // Make a copy of the item in slot, set count to 1 and remove one from the slot
- cItem res = m_Slots[a_SlotNum];
- res.m_ItemCount = 1;
- m_Slots[a_SlotNum].m_ItemCount -= 1;
-
- // Emptying the slot correctly if appropriate
- if (m_Slots[a_SlotNum].m_ItemCount == 0)
- {
- m_Slots[a_SlotNum].Empty();
- }
-
- // Notify everyone of the change
- TriggerListeners(a_SlotNum);
-
- // Return the stored one item
- return res;
-}
-
-
-
-
-
-cItem cItemGrid::RemoveOneItem(int a_X, int a_Y)
-{
- return RemoveOneItem(GetSlotNum(a_X, a_Y));
-}
-
-
-
-
-
-int cItemGrid::HowManyItems(const cItem & a_Item)
-{
- int res = 0;
- for (int i = 0; i < m_NumSlots; i++)
- {
- if (m_Slots[i].IsStackableWith(a_Item))
- {
- res += m_Slots[i].m_ItemCount;
- }
- }
- return res;
-}
-
-
-
-
-
-bool cItemGrid::HasItems(const cItem & a_ItemStack)
-{
- int CurrentlyHave = HowManyItems(a_ItemStack);
- return (CurrentlyHave >= a_ItemStack.m_ItemCount);
-}
-
-
-
-
-
-int cItemGrid::GetFirstEmptySlot(void) const
-{
- return GetNextEmptySlot(-1);
-}
-
-
-
-
-
-int cItemGrid::GetFirstUsedSlot(void) const
-{
- return GetNextUsedSlot(-1);
-}
-
-
-
-
-
-int cItemGrid::GetLastEmptySlot(void) const
-{
- for (int i = m_NumSlots - 1; i >= 0; i--)
- {
- if (m_Slots[i].IsEmpty())
- {
- return i;
- }
- }
- return -1;
-}
-
-
-
-
-
-int cItemGrid::GetLastUsedSlot(void) const
-{
- for (int i = m_NumSlots - 1; i >= 0; i--)
- {
- if (!m_Slots[i].IsEmpty())
- {
- return i;
- }
- }
- return -1;
-}
-
-
-
-
-
-int cItemGrid::GetNextEmptySlot(int a_StartFrom) const
-{
- for (int i = a_StartFrom + 1; i < m_NumSlots; i++)
- {
- if (m_Slots[i].IsEmpty())
- {
- return i;
- }
- }
- return -1;
-}
-
-
-
-
-
-int cItemGrid::GetNextUsedSlot(int a_StartFrom) const
-{
- for (int i = a_StartFrom + 1; i < m_NumSlots; i++)
- {
- if (!m_Slots[i].IsEmpty())
- {
- return i;
- }
- }
- return -1;
-}
-
-
-
-
-
-void cItemGrid::CopyToItems(cItems & a_Items) const
-{
- for (int i = 0; i < m_NumSlots; i++)
- {
- if (!m_Slots[i].IsEmpty())
- {
- a_Items.push_back(m_Slots[i]);
- }
- } // for i - m_Slots[]
-}
-
-
-
-
-
-bool cItemGrid::DamageItem(int a_SlotNum, short a_Amount)
-{
- if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
- {
- LOGWARNING("%s: invalid slot number %d out of %d slots, ignoring.", __FUNCTION__, a_SlotNum, m_NumSlots);
- return false;
- }
- return m_Slots[a_SlotNum].DamageItem(a_Amount);
-}
-
-
-
-
-
-bool cItemGrid::DamageItem(int a_X, int a_Y, short a_Amount)
-{
- return DamageItem(GetSlotNum(a_X, a_Y), a_Amount);
-}
-
-
-
-
-
-void cItemGrid::GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, int a_CountLootProbabs, int a_NumSlots, int a_Seed)
-{
- // Calculate the total weight:
- int TotalProbab = 1;
- for (int i = 0; i < a_CountLootProbabs; i++)
- {
- TotalProbab += a_LootProbabs[i].m_Weight;
- }
-
- // Pick the loot items:
- cNoise Noise(a_Seed);
- for (int i = 0; i < a_NumSlots; i++)
- {
- int Rnd = (Noise.IntNoise1DInt(i) / 7);
- int LootRnd = Rnd % TotalProbab;
- Rnd >>= 8;
- cItem CurrentLoot = cItem(E_ITEM_BOOK, 1, 0); // TODO: enchantment
- for (int j = 0; j < a_CountLootProbabs; j++)
- {
- LootRnd -= a_LootProbabs[i].m_Weight;
- if (LootRnd < 0)
- {
- CurrentLoot = a_LootProbabs[i].m_Item;
- CurrentLoot.m_ItemCount = a_LootProbabs[i].m_MinAmount + (Rnd % (a_LootProbabs[i].m_MaxAmount - a_LootProbabs[i].m_MinAmount));
- Rnd >>= 8;
- break;
- }
- } // for j - a_LootProbabs[]
- SetSlot(Rnd % m_NumSlots, CurrentLoot);
- } // for i - NumSlots
-}
-
-
-
-
-
-void cItemGrid::AddListener(cListener & a_Listener)
-{
- cCSLock Lock(m_CSListeners);
- ASSERT(!m_IsInTriggerListeners); // Must not call this while in TriggerListeners()
- m_Listeners.push_back(&a_Listener);
-}
-
-
-
-
-
-void cItemGrid::RemoveListener(cListener & a_Listener)
-{
- cCSLock Lock(m_CSListeners);
- ASSERT(!m_IsInTriggerListeners); // Must not call this while in TriggerListeners()
- for (cListeners::iterator itr = m_Listeners.begin(), end = m_Listeners.end(); itr != end; ++itr)
- {
- if (*itr == &a_Listener)
- {
- m_Listeners.erase(itr);
- return;
- }
- } // for itr - m_Listeners[]
-}
-
-
-
-
-
-void cItemGrid::TriggerListeners(int a_SlotNum)
-{
- cListeners Listeners;
- {
- cCSLock Lock(m_CSListeners);
- m_IsInTriggerListeners = true;
- Listeners = m_Listeners;
- }
- for (cListeners::iterator itr = Listeners.begin(), end = Listeners.end(); itr != end; ++itr)
- {
- (*itr)->OnSlotChanged(this, a_SlotNum);
- } // for itr - m_Listeners[]
- m_IsInTriggerListeners = false;
-}
-
-
-
-
+
+// ItemGrid.cpp
+
+// Implements the cItemGrid class representing a storage for items in a XY grid (chests, dispensers, inventory etc.)
+
+#include "Globals.h"
+#include "ItemGrid.h"
+#include "Items/ItemHandler.h"
+#include "Noise.h"
+
+
+
+
+
+cItemGrid::cItemGrid(int a_Width, int a_Height) :
+ m_Width(a_Width),
+ m_Height(a_Height),
+ m_NumSlots(a_Width * a_Height),
+ m_Slots(new cItem[a_Width * a_Height]),
+ m_IsInTriggerListeners(false)
+{
+}
+
+
+
+
+
+cItemGrid::~cItemGrid()
+{
+ delete[] m_Slots;
+}
+
+
+
+
+
+int cItemGrid::GetSlotNum(int a_X, int a_Y) const
+{
+ if (
+ (a_X < 0) || (a_X >= m_Width) ||
+ (a_Y < 0) || (a_Y >= m_Height)
+ )
+ {
+ LOGWARNING("%s: coords out of range: (%d, %d) in grid of size (%d, %d)",
+ __FUNCTION__, a_X, a_Y, m_Width, m_Height
+ );
+ return -1;
+ }
+ return a_X + m_Width * a_Y;
+}
+
+
+
+
+
+void cItemGrid::GetSlotCoords(int a_SlotNum, int & a_X, int & a_Y) const
+{
+ if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
+ {
+ LOGWARNING("%s: SlotNum out of range: %d in grid of range %d",
+ __FUNCTION__, a_SlotNum, m_NumSlots
+ );
+ a_X = -1;
+ a_Y = -1;
+ return;
+ }
+ a_X = a_SlotNum % m_Width;
+ a_Y = a_SlotNum / m_Width;
+}
+
+
+
+
+
+const cItem & cItemGrid::GetSlot(int a_X, int a_Y) const
+{
+ return GetSlot(GetSlotNum(a_X, a_Y));
+}
+
+
+
+
+
+const cItem & cItemGrid::GetSlot(int a_SlotNum) const
+{
+ if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
+ {
+ LOGWARNING("%s: Invalid slot number, %d out of %d slots",
+ __FUNCTION__, a_SlotNum, m_NumSlots
+ );
+ return m_Slots[0];
+ }
+ return m_Slots[a_SlotNum];
+}
+
+
+
+
+
+void cItemGrid::SetSlot(int a_X, int a_Y, const cItem & a_Item)
+{
+ SetSlot(GetSlotNum(a_X, a_Y), a_Item);
+}
+
+
+
+
+
+void cItemGrid::SetSlot(int a_X, int a_Y, short a_ItemType, char a_ItemCount, short a_ItemDamage)
+{
+ SetSlot(GetSlotNum(a_X, a_Y), cItem(a_ItemType, a_ItemCount, a_ItemDamage));
+}
+
+
+
+
+
+void cItemGrid::SetSlot(int a_SlotNum, const cItem & a_Item)
+{
+ if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
+ {
+ LOGWARNING("%s: Invalid slot number %d out of %d slots",
+ __FUNCTION__, a_SlotNum, m_NumSlots
+ );
+ return;
+ }
+ m_Slots[a_SlotNum] = a_Item;
+ TriggerListeners(a_SlotNum);
+}
+
+
+
+
+
+void cItemGrid::SetSlot(int a_SlotNum, short a_ItemType, char a_ItemCount, short a_ItemDamage)
+{
+ SetSlot(a_SlotNum, cItem(a_ItemType, a_ItemCount, a_ItemDamage));
+}
+
+
+
+
+
+void cItemGrid::EmptySlot(int a_X, int a_Y)
+{
+ EmptySlot(GetSlotNum(a_X, a_Y));
+}
+
+
+
+
+
+void cItemGrid::EmptySlot(int a_SlotNum)
+{
+ if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
+ {
+ LOGWARNING("%s: Invalid slot number %d out of %d slots",
+ __FUNCTION__, a_SlotNum, m_NumSlots
+ );
+ return;
+ }
+
+ // Check if already empty:
+ if (m_Slots[a_SlotNum].IsEmpty())
+ {
+ return;
+ }
+
+ // Empty and notify
+ m_Slots[a_SlotNum].Empty();
+ TriggerListeners(a_SlotNum);
+}
+
+
+
+
+
+bool cItemGrid::IsSlotEmpty(int a_SlotNum) const
+{
+ if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
+ {
+ LOGWARNING("%s: Invalid slot number %d out of %d slots",
+ __FUNCTION__, a_SlotNum, m_NumSlots
+ );
+ return true;
+ }
+ return m_Slots[a_SlotNum].IsEmpty();
+}
+
+
+
+
+
+bool cItemGrid::IsSlotEmpty(int a_X, int a_Y) const
+{
+ return IsSlotEmpty(GetSlotNum(a_X, a_Y));
+}
+
+
+
+
+
+void cItemGrid::Clear(void)
+{
+ for (int i = 0; i < m_NumSlots; i++)
+ {
+ m_Slots[i].Empty();
+ TriggerListeners(i);
+ }
+}
+
+
+
+
+
+int cItemGrid::HowManyCanFit(const cItem & a_ItemStack)
+{
+ char NumLeft = a_ItemStack.m_ItemCount;
+ int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize();
+ for (int i = m_NumSlots - 1; i >= 0; i--)
+ {
+ if (m_Slots[i].IsEmpty())
+ {
+ NumLeft -= MaxStack;
+ }
+ else if (m_Slots[i].IsStackableWith(a_ItemStack))
+ {
+ NumLeft -= MaxStack - m_Slots[i].m_ItemCount;
+ }
+ if (NumLeft <= 0)
+ {
+ // All items fit
+ return a_ItemStack.m_ItemCount;
+ }
+ } // for i - m_Slots[]
+ return a_ItemStack.m_ItemCount - NumLeft;
+}
+
+
+
+
+
+int cItemGrid::AddItemToSlot(const cItem & a_ItemStack, int a_Slot, int a_Num, int a_MaxStack)
+{
+ int PrevCount = 0;
+ if (m_Slots[a_Slot].IsEmpty())
+ {
+ m_Slots[a_Slot] = a_ItemStack;
+ PrevCount = 0;
+ }
+ else
+ {
+ PrevCount = m_Slots[a_Slot].m_ItemCount;
+ }
+ m_Slots[a_Slot].m_ItemCount = std::min(a_MaxStack, PrevCount + a_Num);
+ int toReturn = m_Slots[a_Slot].m_ItemCount - PrevCount;
+ TriggerListeners(a_Slot);
+ return toReturn;
+}
+
+
+
+
+
+int cItemGrid::AddItem(cItem & a_ItemStack, bool a_AllowNewStacks, int a_PrioritarySlot)
+{
+ int NumLeft = a_ItemStack.m_ItemCount;
+ int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize();
+
+ // Try prioritarySlot first:
+ if (
+ (a_PrioritarySlot != -1) &&
+ (
+ m_Slots[a_PrioritarySlot].IsEmpty() ||
+ m_Slots[a_PrioritarySlot].IsStackableWith(a_ItemStack)
+ )
+ )
+ {
+ NumLeft -= AddItemToSlot(a_ItemStack, a_PrioritarySlot, NumLeft, MaxStack);
+ }
+
+ // Scan existing stacks:
+ for (int i = m_NumSlots - 1; i >= 0; i--)
+ {
+ if (m_Slots[i].IsStackableWith(a_ItemStack))
+ {
+ NumLeft -= AddItemToSlot(a_ItemStack, i, NumLeft, MaxStack);
+ }
+ if (NumLeft <= 0)
+ {
+ // All items fit
+ return a_ItemStack.m_ItemCount;
+ }
+ } // for i - m_Slots[]
+
+ if (!a_AllowNewStacks)
+ {
+ return (a_ItemStack.m_ItemCount - NumLeft);
+ }
+
+ for (int i = m_NumSlots - 1; i >= 0; i--)
+ {
+ if (m_Slots[i].IsEmpty())
+ {
+ NumLeft -= AddItemToSlot(a_ItemStack, i, NumLeft, MaxStack);
+ }
+ if (NumLeft <= 0)
+ {
+ // All items fit
+ return a_ItemStack.m_ItemCount;
+ }
+ } // for i - m_Slots[]
+ return (a_ItemStack.m_ItemCount - NumLeft);
+}
+
+
+
+
+
+int cItemGrid::AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks, int a_PrioritarySlot)
+{
+ int TotalAdded = 0;
+ for (cItems::iterator itr = a_ItemStackList.begin(); itr != a_ItemStackList.end();)
+ {
+ int NumAdded = AddItem(*itr, a_AllowNewStacks, a_PrioritarySlot);
+ if (itr->m_ItemCount == NumAdded)
+ {
+ itr = a_ItemStackList.erase(itr);
+ }
+ else
+ {
+ itr->m_ItemCount -= NumAdded;
+ ++itr;
+ }
+ TotalAdded += NumAdded;
+ }
+ return TotalAdded;
+}
+
+
+
+
+
+int cItemGrid::ChangeSlotCount(int a_SlotNum, int a_AddToCount)
+{
+ if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
+ {
+ LOGWARNING("%s: Invalid slot number %d out of %d slots, ignoring the call, returning -1",
+ __FUNCTION__, a_SlotNum, m_NumSlots
+ );
+ return -1;
+ }
+
+ if (m_Slots[a_SlotNum].IsEmpty())
+ {
+ // The item is empty, it's not gonna change
+ return 0;
+ }
+
+ if (m_Slots[a_SlotNum].m_ItemCount <= -a_AddToCount)
+ {
+ // Trying to remove more items than there already are, make the item empty
+ m_Slots[a_SlotNum].Empty();
+ TriggerListeners(a_SlotNum);
+ return 0;
+ }
+
+ m_Slots[a_SlotNum].m_ItemCount += a_AddToCount;
+ TriggerListeners(a_SlotNum);
+ return m_Slots[a_SlotNum].m_ItemCount;
+}
+
+
+
+
+
+int cItemGrid::ChangeSlotCount(int a_X, int a_Y, int a_AddToCount)
+{
+ return ChangeSlotCount(GetSlotNum(a_X, a_Y), a_AddToCount);
+}
+
+
+
+
+
+cItem cItemGrid::RemoveOneItem(int a_SlotNum)
+{
+ if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
+ {
+ LOGWARNING("%s: Invalid slot number %d out of %d slots, ignoring the call, returning empty item",
+ __FUNCTION__, a_SlotNum, m_NumSlots
+ );
+ return cItem();
+ }
+
+ // If the slot is empty, return an empty item
+ if (m_Slots[a_SlotNum].IsEmpty())
+ {
+ return cItem();
+ }
+
+ // Make a copy of the item in slot, set count to 1 and remove one from the slot
+ cItem res = m_Slots[a_SlotNum];
+ res.m_ItemCount = 1;
+ m_Slots[a_SlotNum].m_ItemCount -= 1;
+
+ // Emptying the slot correctly if appropriate
+ if (m_Slots[a_SlotNum].m_ItemCount == 0)
+ {
+ m_Slots[a_SlotNum].Empty();
+ }
+
+ // Notify everyone of the change
+ TriggerListeners(a_SlotNum);
+
+ // Return the stored one item
+ return res;
+}
+
+
+
+
+
+cItem cItemGrid::RemoveOneItem(int a_X, int a_Y)
+{
+ return RemoveOneItem(GetSlotNum(a_X, a_Y));
+}
+
+
+
+
+
+int cItemGrid::HowManyItems(const cItem & a_Item)
+{
+ int res = 0;
+ for (int i = 0; i < m_NumSlots; i++)
+ {
+ if (m_Slots[i].IsStackableWith(a_Item))
+ {
+ res += m_Slots[i].m_ItemCount;
+ }
+ }
+ return res;
+}
+
+
+
+
+
+bool cItemGrid::HasItems(const cItem & a_ItemStack)
+{
+ int CurrentlyHave = HowManyItems(a_ItemStack);
+ return (CurrentlyHave >= a_ItemStack.m_ItemCount);
+}
+
+
+
+
+
+int cItemGrid::GetFirstEmptySlot(void) const
+{
+ return GetNextEmptySlot(-1);
+}
+
+
+
+
+
+int cItemGrid::GetFirstUsedSlot(void) const
+{
+ return GetNextUsedSlot(-1);
+}
+
+
+
+
+
+int cItemGrid::GetLastEmptySlot(void) const
+{
+ for (int i = m_NumSlots - 1; i >= 0; i--)
+ {
+ if (m_Slots[i].IsEmpty())
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+
+
+
+int cItemGrid::GetLastUsedSlot(void) const
+{
+ for (int i = m_NumSlots - 1; i >= 0; i--)
+ {
+ if (!m_Slots[i].IsEmpty())
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+
+
+
+int cItemGrid::GetNextEmptySlot(int a_StartFrom) const
+{
+ for (int i = a_StartFrom + 1; i < m_NumSlots; i++)
+ {
+ if (m_Slots[i].IsEmpty())
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+
+
+
+int cItemGrid::GetNextUsedSlot(int a_StartFrom) const
+{
+ for (int i = a_StartFrom + 1; i < m_NumSlots; i++)
+ {
+ if (!m_Slots[i].IsEmpty())
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+
+
+
+void cItemGrid::CopyToItems(cItems & a_Items) const
+{
+ for (int i = 0; i < m_NumSlots; i++)
+ {
+ if (!m_Slots[i].IsEmpty())
+ {
+ a_Items.push_back(m_Slots[i]);
+ }
+ } // for i - m_Slots[]
+}
+
+
+
+
+
+bool cItemGrid::DamageItem(int a_SlotNum, short a_Amount)
+{
+ if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
+ {
+ LOGWARNING("%s: invalid slot number %d out of %d slots, ignoring.", __FUNCTION__, a_SlotNum, m_NumSlots);
+ return false;
+ }
+ return m_Slots[a_SlotNum].DamageItem(a_Amount);
+}
+
+
+
+
+
+bool cItemGrid::DamageItem(int a_X, int a_Y, short a_Amount)
+{
+ return DamageItem(GetSlotNum(a_X, a_Y), a_Amount);
+}
+
+
+
+
+
+void cItemGrid::GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, int a_CountLootProbabs, int a_NumSlots, int a_Seed)
+{
+ // Calculate the total weight:
+ int TotalProbab = 1;
+ for (int i = 0; i < a_CountLootProbabs; i++)
+ {
+ TotalProbab += a_LootProbabs[i].m_Weight;
+ }
+
+ // Pick the loot items:
+ cNoise Noise(a_Seed);
+ for (int i = 0; i < a_NumSlots; i++)
+ {
+ int Rnd = (Noise.IntNoise1DInt(i) / 7);
+ int LootRnd = Rnd % TotalProbab;
+ Rnd >>= 8;
+ cItem CurrentLoot = cItem(E_ITEM_BOOK, 1, 0); // TODO: enchantment
+ for (int j = 0; j < a_CountLootProbabs; j++)
+ {
+ LootRnd -= a_LootProbabs[i].m_Weight;
+ if (LootRnd < 0)
+ {
+ CurrentLoot = a_LootProbabs[i].m_Item;
+ CurrentLoot.m_ItemCount = a_LootProbabs[i].m_MinAmount + (Rnd % (a_LootProbabs[i].m_MaxAmount - a_LootProbabs[i].m_MinAmount));
+ Rnd >>= 8;
+ break;
+ }
+ } // for j - a_LootProbabs[]
+ SetSlot(Rnd % m_NumSlots, CurrentLoot);
+ } // for i - NumSlots
+}
+
+
+
+
+
+void cItemGrid::AddListener(cListener & a_Listener)
+{
+ cCSLock Lock(m_CSListeners);
+ ASSERT(!m_IsInTriggerListeners); // Must not call this while in TriggerListeners()
+ m_Listeners.push_back(&a_Listener);
+}
+
+
+
+
+
+void cItemGrid::RemoveListener(cListener & a_Listener)
+{
+ cCSLock Lock(m_CSListeners);
+ ASSERT(!m_IsInTriggerListeners); // Must not call this while in TriggerListeners()
+ for (cListeners::iterator itr = m_Listeners.begin(), end = m_Listeners.end(); itr != end; ++itr)
+ {
+ if (*itr == &a_Listener)
+ {
+ m_Listeners.erase(itr);
+ return;
+ }
+ } // for itr - m_Listeners[]
+}
+
+
+
+
+
+void cItemGrid::TriggerListeners(int a_SlotNum)
+{
+ cListeners Listeners;
+ {
+ cCSLock Lock(m_CSListeners);
+ m_IsInTriggerListeners = true;
+ Listeners = m_Listeners;
+ }
+ for (cListeners::iterator itr = Listeners.begin(), end = Listeners.end(); itr != end; ++itr)
+ {
+ (*itr)->OnSlotChanged(this, a_SlotNum);
+ } // for itr - m_Listeners[]
+ m_IsInTriggerListeners = false;
+}
+
+
+
+
diff --git a/source/ItemGrid.h b/source/ItemGrid.h
index 45f978530..9eba5b452 100644
--- a/source/ItemGrid.h
+++ b/source/ItemGrid.h
@@ -1,191 +1,191 @@
-
-// ItemGrid.h
-
-// Declares the cItemGrid class representing a storage for items in a XY grid (chests, dispensers, inventory etc.)
-
-
-
-
-#pragma once
-
-#include "Item.h"
-
-
-
-
-
-// tolua_begin
-class cItemGrid
-{
-public:
- // tolua_end
-
- /// This class is used as a callback for when a slot changes
- class cListener
- {
- public:
- /// Called whenever a slot changes
- virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) = 0;
- } ;
- typedef std::vector<cListener *> cListeners;
-
- cItemGrid(int a_Width, int a_Height);
-
- ~cItemGrid();
-
- // tolua_begin
- int GetWidth (void) const { return m_Width; }
- int GetHeight (void) const { return m_Height; }
- int GetNumSlots(void) const { return m_NumSlots; }
-
- /// Converts XY coords into slot number; returns -1 on invalid coords
- int GetSlotNum(int a_X, int a_Y) const;
-
- // tolua_end
-
- /// Converts slot number into XY coords; sets coords to -1 on invalid slot number. Exported in ManualBindings.cpp
- void GetSlotCoords(int a_SlotNum, int & a_X, int & a_Y) const;
-
- // tolua_begin
-
- // Retrieve slots by coords or slot number; Logs warning and returns the first slot on invalid coords / slotnum
- const cItem & GetSlot(int a_X, int a_Y) const;
- const cItem & GetSlot(int a_SlotNum) const;
-
- // Set slot by coords or slot number; Logs warning and doesn't set on invalid coords / slotnum
- void SetSlot(int a_X, int a_Y, const cItem & a_Item);
- void SetSlot(int a_X, int a_Y, short a_ItemType, char a_ItemCount, short a_ItemDamage);
- void SetSlot(int a_SlotNum, const cItem & a_Item);
- void SetSlot(int a_SlotNum, short a_ItemType, char a_ItemCount, short a_ItemDamage);
-
- // Empty the specified slot; Logs warning and doesn't set on invalid coords / slotnum
- void EmptySlot(int a_X, int a_Y);
- void EmptySlot(int a_SlotNum);
-
- /// Returns true if the specified slot is empty or the slot doesn't exist
- bool IsSlotEmpty(int a_SlotNum) const;
-
- /// Returns true if the specified slot is empty or the slot doesn't exist
- bool IsSlotEmpty(int a_X, int a_Y) const;
-
- /// Sets all items as empty
- void Clear(void);
-
- /// Returns number of items out of a_ItemStack that can fit in the storage
- int HowManyCanFit(const cItem & a_ItemStack);
-
- /** Adds as many items out of a_ItemStack as can fit.
- If a_AllowNewStacks is set to false, only existing stacks can be topped up;
- if a_AllowNewStacks is set to true, empty slots can be used for the rest.
- If a_PrioritarySlot is set to a positive value, then the corresponding slot will be used in
- first (if empty or compatible with added items)
- if a_PrioritarySlot is set to -1, regular order apply
- Returns the number of items that fit.
- */
- int AddItem(cItem & a_ItemStack, bool a_AllowNewStacks = true, int a_PrioritarySlot = -1);
-
- /** Same as AddItem, but works on an entire list of item stacks.
- The a_ItemStackList is modified to reflect the leftover items.
- If a_AllowNewStacks is set to false, only existing stacks can be topped up;
- if a_AllowNewStacks is set to true, empty slots can be used for the rest.
- If a_PrioritarySlot is set to a positive value, then the corresponding slot will be used in
- first (if empty or compatible with added items)
- if a_PrioritarySlot is set to -1, regular order apply
- Returns the total number of items that fit.
- */
- int AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks = true, int a_PrioritarySlot = -1);
-
- /** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot.
- If the slot is empty, ignores the call.
- Returns the new count.
- */
- int ChangeSlotCount(int a_SlotNum, int a_AddToCount);
-
- /** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot.
- If the slot is empty, ignores the call.
- Returns the new count.
- */
- int ChangeSlotCount(int a_X, int a_Y, int a_AddToCount);
-
- /** Removes one item from the stack in the specified slot, and returns it.
- If the slot was empty, returns an empty item
- */
- cItem RemoveOneItem(int a_SlotNum);
-
- /** Removes one item from the stack in the specified slot, and returns it.
- If the slot was empty, returns an empty item
- */
- cItem RemoveOneItem(int a_X, int a_Y);
-
- /// Returns the number of items of type a_Item that are stored
- int HowManyItems(const cItem & a_Item);
-
- /// Returns true if there are at least as many items of type a_ItemStack as in a_ItemStack
- bool HasItems(const cItem & a_ItemStack);
-
- /// Returns the index of the first empty slot; -1 if all full
- int GetFirstEmptySlot(void) const;
-
- /// Returns the index of the first non-empty slot; -1 if all empty
- int GetFirstUsedSlot(void) const;
-
- /// Returns the index of the last empty slot; -1 if all full
- int GetLastEmptySlot(void) const;
-
- /// Returns the index of the last used slot; -1 if all empty
- int GetLastUsedSlot(void) const;
-
- /// Returns the index of the first empty slot following a_StartFrom (a_StartFrom is not checked)
- int GetNextEmptySlot(int a_StartFrom) const;
-
- /// Returns the index of the first used slot following a_StartFrom (a_StartFrom is not checked)
- int GetNextUsedSlot(int a_StartFrom) const;
-
- /// Copies the contents into a cItems object; preserves the original a_Items contents
- void CopyToItems(cItems & a_Items) const;
-
- /// Adds the specified damage to the specified item; returns true if the item broke (but the item is left intact)
- bool DamageItem(int a_SlotNum, short a_Amount);
-
- /// Adds the specified damage to the specified item; returns true if the item broke (but the item is left intact)
- bool DamageItem(int a_X, int a_Y, short a_Amount);
-
- // tolua_end
-
-
- /** Generates random loot from the specified loot probability table, with a chance of enchanted books added.
- A total of a_NumSlots are taken by the loot.
- Cannot export to Lua due to raw array a_LootProbabs. TODO: Make this exportable / export through ManualBindings.cpp with a Lua table as LootProbabs
- */
- void GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, int a_CountLootProbabs, int a_NumSlots, int a_Seed);
-
- /// Adds a callback that gets called whenever a slot changes. Must not be called from within the listener callback!
- void AddListener(cListener & a_Listener);
-
- /// Removes a slot-change-callback. Must not be called from within the listener callback!
- void RemoveListener(cListener & a_Listener);
-
- // tolua_begin
-
-protected:
- int m_Width;
- int m_Height;
- int m_NumSlots; // m_Width * m_Height, for easier validity checking in the access functions
- cItem * m_Slots; // x + m_Width * y
-
- cListeners m_Listeners; ///< Listeners which should be notified on slot changes; the pointers are not owned by this object
- cCriticalSection m_CSListeners; ///< CS that guards the m_Listeners against multi-thread access
- bool m_IsInTriggerListeners; ///< Set to true while TriggerListeners is running, to detect attempts to manipulate listener list while triggerring
-
- /// Calls all m_Listeners for the specified slot number
- void TriggerListeners(int a_SlotNum);
-
- /** Adds up to a_Num items out of a_ItemStack, as many as can fit, in specified slot
- Returns the number of items that did fit.
- */
- int AddItemToSlot(const cItem & a_ItemStack, int a_Slot, int a_Num, int a_MaxStack);
-} ;
-// tolua_end
-
-
-
+
+// ItemGrid.h
+
+// Declares the cItemGrid class representing a storage for items in a XY grid (chests, dispensers, inventory etc.)
+
+
+
+
+#pragma once
+
+#include "Item.h"
+
+
+
+
+
+// tolua_begin
+class cItemGrid
+{
+public:
+ // tolua_end
+
+ /// This class is used as a callback for when a slot changes
+ class cListener
+ {
+ public:
+ /// Called whenever a slot changes
+ virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) = 0;
+ } ;
+ typedef std::vector<cListener *> cListeners;
+
+ cItemGrid(int a_Width, int a_Height);
+
+ ~cItemGrid();
+
+ // tolua_begin
+ int GetWidth (void) const { return m_Width; }
+ int GetHeight (void) const { return m_Height; }
+ int GetNumSlots(void) const { return m_NumSlots; }
+
+ /// Converts XY coords into slot number; returns -1 on invalid coords
+ int GetSlotNum(int a_X, int a_Y) const;
+
+ // tolua_end
+
+ /// Converts slot number into XY coords; sets coords to -1 on invalid slot number. Exported in ManualBindings.cpp
+ void GetSlotCoords(int a_SlotNum, int & a_X, int & a_Y) const;
+
+ // tolua_begin
+
+ // Retrieve slots by coords or slot number; Logs warning and returns the first slot on invalid coords / slotnum
+ const cItem & GetSlot(int a_X, int a_Y) const;
+ const cItem & GetSlot(int a_SlotNum) const;
+
+ // Set slot by coords or slot number; Logs warning and doesn't set on invalid coords / slotnum
+ void SetSlot(int a_X, int a_Y, const cItem & a_Item);
+ void SetSlot(int a_X, int a_Y, short a_ItemType, char a_ItemCount, short a_ItemDamage);
+ void SetSlot(int a_SlotNum, const cItem & a_Item);
+ void SetSlot(int a_SlotNum, short a_ItemType, char a_ItemCount, short a_ItemDamage);
+
+ // Empty the specified slot; Logs warning and doesn't set on invalid coords / slotnum
+ void EmptySlot(int a_X, int a_Y);
+ void EmptySlot(int a_SlotNum);
+
+ /// Returns true if the specified slot is empty or the slot doesn't exist
+ bool IsSlotEmpty(int a_SlotNum) const;
+
+ /// Returns true if the specified slot is empty or the slot doesn't exist
+ bool IsSlotEmpty(int a_X, int a_Y) const;
+
+ /// Sets all items as empty
+ void Clear(void);
+
+ /// Returns number of items out of a_ItemStack that can fit in the storage
+ int HowManyCanFit(const cItem & a_ItemStack);
+
+ /** Adds as many items out of a_ItemStack as can fit.
+ If a_AllowNewStacks is set to false, only existing stacks can be topped up;
+ if a_AllowNewStacks is set to true, empty slots can be used for the rest.
+ If a_PrioritarySlot is set to a positive value, then the corresponding slot will be used in
+ first (if empty or compatible with added items)
+ if a_PrioritarySlot is set to -1, regular order apply
+ Returns the number of items that fit.
+ */
+ int AddItem(cItem & a_ItemStack, bool a_AllowNewStacks = true, int a_PrioritarySlot = -1);
+
+ /** Same as AddItem, but works on an entire list of item stacks.
+ The a_ItemStackList is modified to reflect the leftover items.
+ If a_AllowNewStacks is set to false, only existing stacks can be topped up;
+ if a_AllowNewStacks is set to true, empty slots can be used for the rest.
+ If a_PrioritarySlot is set to a positive value, then the corresponding slot will be used in
+ first (if empty or compatible with added items)
+ if a_PrioritarySlot is set to -1, regular order apply
+ Returns the total number of items that fit.
+ */
+ int AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks = true, int a_PrioritarySlot = -1);
+
+ /** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot.
+ If the slot is empty, ignores the call.
+ Returns the new count.
+ */
+ int ChangeSlotCount(int a_SlotNum, int a_AddToCount);
+
+ /** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot.
+ If the slot is empty, ignores the call.
+ Returns the new count.
+ */
+ int ChangeSlotCount(int a_X, int a_Y, int a_AddToCount);
+
+ /** Removes one item from the stack in the specified slot, and returns it.
+ If the slot was empty, returns an empty item
+ */
+ cItem RemoveOneItem(int a_SlotNum);
+
+ /** Removes one item from the stack in the specified slot, and returns it.
+ If the slot was empty, returns an empty item
+ */
+ cItem RemoveOneItem(int a_X, int a_Y);
+
+ /// Returns the number of items of type a_Item that are stored
+ int HowManyItems(const cItem & a_Item);
+
+ /// Returns true if there are at least as many items of type a_ItemStack as in a_ItemStack
+ bool HasItems(const cItem & a_ItemStack);
+
+ /// Returns the index of the first empty slot; -1 if all full
+ int GetFirstEmptySlot(void) const;
+
+ /// Returns the index of the first non-empty slot; -1 if all empty
+ int GetFirstUsedSlot(void) const;
+
+ /// Returns the index of the last empty slot; -1 if all full
+ int GetLastEmptySlot(void) const;
+
+ /// Returns the index of the last used slot; -1 if all empty
+ int GetLastUsedSlot(void) const;
+
+ /// Returns the index of the first empty slot following a_StartFrom (a_StartFrom is not checked)
+ int GetNextEmptySlot(int a_StartFrom) const;
+
+ /// Returns the index of the first used slot following a_StartFrom (a_StartFrom is not checked)
+ int GetNextUsedSlot(int a_StartFrom) const;
+
+ /// Copies the contents into a cItems object; preserves the original a_Items contents
+ void CopyToItems(cItems & a_Items) const;
+
+ /// Adds the specified damage to the specified item; returns true if the item broke (but the item is left intact)
+ bool DamageItem(int a_SlotNum, short a_Amount);
+
+ /// Adds the specified damage to the specified item; returns true if the item broke (but the item is left intact)
+ bool DamageItem(int a_X, int a_Y, short a_Amount);
+
+ // tolua_end
+
+
+ /** Generates random loot from the specified loot probability table, with a chance of enchanted books added.
+ A total of a_NumSlots are taken by the loot.
+ Cannot export to Lua due to raw array a_LootProbabs. TODO: Make this exportable / export through ManualBindings.cpp with a Lua table as LootProbabs
+ */
+ void GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, int a_CountLootProbabs, int a_NumSlots, int a_Seed);
+
+ /// Adds a callback that gets called whenever a slot changes. Must not be called from within the listener callback!
+ void AddListener(cListener & a_Listener);
+
+ /// Removes a slot-change-callback. Must not be called from within the listener callback!
+ void RemoveListener(cListener & a_Listener);
+
+ // tolua_begin
+
+protected:
+ int m_Width;
+ int m_Height;
+ int m_NumSlots; // m_Width * m_Height, for easier validity checking in the access functions
+ cItem * m_Slots; // x + m_Width * y
+
+ cListeners m_Listeners; ///< Listeners which should be notified on slot changes; the pointers are not owned by this object
+ cCriticalSection m_CSListeners; ///< CS that guards the m_Listeners against multi-thread access
+ bool m_IsInTriggerListeners; ///< Set to true while TriggerListeners is running, to detect attempts to manipulate listener list while triggerring
+
+ /// Calls all m_Listeners for the specified slot number
+ void TriggerListeners(int a_SlotNum);
+
+ /** Adds up to a_Num items out of a_ItemStack, as many as can fit, in specified slot
+ Returns the number of items that did fit.
+ */
+ int AddItemToSlot(const cItem & a_ItemStack, int a_Slot, int a_Num, int a_MaxStack);
+} ;
+// tolua_end
+
+
+
diff --git a/source/Items/ItemBed.h b/source/Items/ItemBed.h
index c9fec9064..ab4182eea 100644
--- a/source/Items/ItemBed.h
+++ b/source/Items/ItemBed.h
@@ -1,56 +1,56 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-#include "../World.h"
-#include "../Blocks/BlockBed.h"
-
-
-
-
-
-class cItemBedHandler :
- public cItemHandler
-{
-public:
- cItemBedHandler(int a_ItemType) :
- cItemHandler(a_ItemType)
- {
- }
-
-
- virtual bool IsPlaceable(void) override
- {
- return true;
- }
-
- 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
- {
- if (a_BlockFace != BLOCK_FACE_TOP)
- {
- // Can only be placed on the floor
- return false;
- }
-
- a_BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player->GetRotation());
-
- // Check if there is empty space for the foot section:
- Vector3i Direction = cBlockBedHandler::MetaDataToDirection(a_BlockMeta);
- if (a_World->GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) != E_BLOCK_AIR)
- {
- return false;
- }
-
- a_BlockType = E_BLOCK_BED;
- return true;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Blocks/BlockBed.h"
+
+
+
+
+
+class cItemBedHandler :
+ public cItemHandler
+{
+public:
+ cItemBedHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+ }
+
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
+ }
+
+ 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
+ {
+ if (a_BlockFace != BLOCK_FACE_TOP)
+ {
+ // Can only be placed on the floor
+ return false;
+ }
+
+ a_BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player->GetRotation());
+
+ // Check if there is empty space for the foot section:
+ Vector3i Direction = cBlockBedHandler::MetaDataToDirection(a_BlockMeta);
+ if (a_World->GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) != E_BLOCK_AIR)
+ {
+ return false;
+ }
+
+ a_BlockType = E_BLOCK_BED;
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/source/Items/ItemBrewingStand.h b/source/Items/ItemBrewingStand.h
index 07ee7dfeb..4ff14d4b4 100644
--- a/source/Items/ItemBrewingStand.h
+++ b/source/Items/ItemBrewingStand.h
@@ -1,41 +1,41 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-
-
-
-
-
-class cItemBrewingStandHandler :
- public cItemHandler
-{
-public:
- cItemBrewingStandHandler(int a_ItemType) :
- cItemHandler(a_ItemType)
- {
- }
-
-
- virtual bool IsPlaceable(void) override
- {
- return true;
- }
-
-
- 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
- {
- a_BlockType = E_BLOCK_BREWING_STAND;
- a_BlockMeta = 0;
- return true;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemBrewingStandHandler :
+ public cItemHandler
+{
+public:
+ cItemBrewingStandHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+ }
+
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
+ }
+
+
+ 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
+ {
+ a_BlockType = E_BLOCK_BREWING_STAND;
+ a_BlockMeta = 0;
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/source/Items/ItemBucket.h b/source/Items/ItemBucket.h
index e4adc98b8..fa3d48da1 100644
--- a/source/Items/ItemBucket.h
+++ b/source/Items/ItemBucket.h
@@ -1,160 +1,160 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-#include "../World.h"
-#include "../Simulator/FluidSimulator.h"
-#include "../Blocks/BlockHandler.h"
-
-
-
-
-
-class cItemBucketHandler :
- public cItemHandler
-{
-public:
- cItemBucketHandler(int a_ItemType) :
- cItemHandler(a_ItemType)
- {
-
- }
-
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
- {
- switch (m_ItemType)
- {
- case E_ITEM_BUCKET: return ScoopUpFluid(a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir);
- case E_ITEM_LAVA_BUCKET: return PlaceFluid (a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir, E_BLOCK_LAVA);
- case E_ITEM_WATER_BUCKET: return PlaceFluid (a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir, E_BLOCK_WATER);
- default:
- {
- ASSERT(!"Unhandled ItemType");
- return false;
- }
- }
- }
-
-
-
- bool ScoopUpFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
- {
- if (a_BlockFace < 0)
- {
- return false;
- }
- AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
- BLOCKTYPE ClickedBlock;
- NIBBLETYPE ClickedMeta;
- a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedMeta);
- LOGD("Bucket Clicked BlockType %d, meta %d", ClickedBlock, ClickedMeta);
- if (ClickedMeta != 0)
- {
- // Not a source block
- return false;
- }
-
- if (a_Player->GetGameMode() == gmCreative)
- {
- // In creative mode don't modify the inventory, just remove the fluid:
- a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
- return true;
- }
-
- ENUM_ITEM_ID NewItem = E_ITEM_EMPTY;
- switch (ClickedBlock)
- {
- case E_BLOCK_WATER:
- case E_BLOCK_STATIONARY_WATER:
- {
- NewItem = E_ITEM_WATER_BUCKET;
- break;
- }
- case E_BLOCK_LAVA:
- case E_BLOCK_STATIONARY_LAVA:
- {
- NewItem = E_ITEM_LAVA_BUCKET;
- break;
- }
-
- default: return false;
- }
-
- // Remove the bucket from the inventory
- if (!a_Player->GetInventory().RemoveOneEquippedItem())
- {
- LOG("Clicked with an empty bucket, but cannot remove one from the inventory? WTF?");
- ASSERT(!"Inventory bucket mismatch");
- return true;
- }
-
- // Give new bucket, filled with fluid:
- cItem Item(NewItem, 1);
- a_Player->GetInventory().AddItem(Item, true, true);
-
- // Remove water / lava block
- a_Player->GetWorld()->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
- return true;
- }
-
-
- bool PlaceFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_FluidBlock)
- {
- if (a_BlockFace < 0)
- {
- return false;
- }
-
- BLOCKTYPE CurrentBlock = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
- bool CanWashAway = cFluidSimulator::CanWashAway(CurrentBlock);
- if (!CanWashAway)
- {
- // The block pointed at cannot be washed away, so put fluid on top of it / on its sides
- AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
- CurrentBlock = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
- }
- if (
- !CanWashAway &&
- (CurrentBlock != E_BLOCK_AIR) &&
- (CurrentBlock != E_BLOCK_WATER) &&
- (CurrentBlock != E_BLOCK_STATIONARY_WATER) &&
- (CurrentBlock != E_BLOCK_LAVA) &&
- (CurrentBlock != E_BLOCK_STATIONARY_LAVA)
- )
- {
- // Cannot place water here
- return false;
- }
-
- if (a_Player->GetGameMode() != gmCreative)
- {
- // Remove fluid bucket, add empty bucket:
- if (!a_Player->GetInventory().RemoveOneEquippedItem())
- {
- LOG("Clicked with a full bucket, but cannot remove one from the inventory? WTF?");
- ASSERT(!"Inventory bucket mismatch");
- return false;
- }
- cItem Item(E_ITEM_BUCKET, 1);
- if (!a_Player->GetInventory().AddItem(Item,true,true))
- {
- return false;
- }
- }
-
- // Wash away anything that was there prior to placing:
- if (CanWashAway)
- {
- cBlockHandler * Handler = BlockHandler(CurrentBlock);
- if (Handler->DoesDropOnUnsuitable())
- {
- Handler->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ);
- }
- }
-
- a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_FluidBlock, 0);
-
- return true;
- }
-
-};
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Simulator/FluidSimulator.h"
+#include "../Blocks/BlockHandler.h"
+
+
+
+
+
+class cItemBucketHandler :
+ public cItemHandler
+{
+public:
+ cItemBucketHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+
+ }
+
+ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
+ {
+ switch (m_ItemType)
+ {
+ case E_ITEM_BUCKET: return ScoopUpFluid(a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir);
+ case E_ITEM_LAVA_BUCKET: return PlaceFluid (a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir, E_BLOCK_LAVA);
+ case E_ITEM_WATER_BUCKET: return PlaceFluid (a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir, E_BLOCK_WATER);
+ default:
+ {
+ ASSERT(!"Unhandled ItemType");
+ return false;
+ }
+ }
+ }
+
+
+
+ bool ScoopUpFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
+ {
+ if (a_BlockFace < 0)
+ {
+ return false;
+ }
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+ BLOCKTYPE ClickedBlock;
+ NIBBLETYPE ClickedMeta;
+ a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedMeta);
+ LOGD("Bucket Clicked BlockType %d, meta %d", ClickedBlock, ClickedMeta);
+ if (ClickedMeta != 0)
+ {
+ // Not a source block
+ return false;
+ }
+
+ if (a_Player->GetGameMode() == gmCreative)
+ {
+ // In creative mode don't modify the inventory, just remove the fluid:
+ a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
+ return true;
+ }
+
+ ENUM_ITEM_ID NewItem = E_ITEM_EMPTY;
+ switch (ClickedBlock)
+ {
+ case E_BLOCK_WATER:
+ case E_BLOCK_STATIONARY_WATER:
+ {
+ NewItem = E_ITEM_WATER_BUCKET;
+ break;
+ }
+ case E_BLOCK_LAVA:
+ case E_BLOCK_STATIONARY_LAVA:
+ {
+ NewItem = E_ITEM_LAVA_BUCKET;
+ break;
+ }
+
+ default: return false;
+ }
+
+ // Remove the bucket from the inventory
+ if (!a_Player->GetInventory().RemoveOneEquippedItem())
+ {
+ LOG("Clicked with an empty bucket, but cannot remove one from the inventory? WTF?");
+ ASSERT(!"Inventory bucket mismatch");
+ return true;
+ }
+
+ // Give new bucket, filled with fluid:
+ cItem Item(NewItem, 1);
+ a_Player->GetInventory().AddItem(Item, true, true);
+
+ // Remove water / lava block
+ a_Player->GetWorld()->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
+ return true;
+ }
+
+
+ bool PlaceFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_FluidBlock)
+ {
+ if (a_BlockFace < 0)
+ {
+ return false;
+ }
+
+ BLOCKTYPE CurrentBlock = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+ bool CanWashAway = cFluidSimulator::CanWashAway(CurrentBlock);
+ if (!CanWashAway)
+ {
+ // The block pointed at cannot be washed away, so put fluid on top of it / on its sides
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+ CurrentBlock = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+ }
+ if (
+ !CanWashAway &&
+ (CurrentBlock != E_BLOCK_AIR) &&
+ (CurrentBlock != E_BLOCK_WATER) &&
+ (CurrentBlock != E_BLOCK_STATIONARY_WATER) &&
+ (CurrentBlock != E_BLOCK_LAVA) &&
+ (CurrentBlock != E_BLOCK_STATIONARY_LAVA)
+ )
+ {
+ // Cannot place water here
+ return false;
+ }
+
+ if (a_Player->GetGameMode() != gmCreative)
+ {
+ // Remove fluid bucket, add empty bucket:
+ if (!a_Player->GetInventory().RemoveOneEquippedItem())
+ {
+ LOG("Clicked with a full bucket, but cannot remove one from the inventory? WTF?");
+ ASSERT(!"Inventory bucket mismatch");
+ return false;
+ }
+ cItem Item(E_ITEM_BUCKET, 1);
+ if (!a_Player->GetInventory().AddItem(Item,true,true))
+ {
+ return false;
+ }
+ }
+
+ // Wash away anything that was there prior to placing:
+ if (CanWashAway)
+ {
+ cBlockHandler * Handler = BlockHandler(CurrentBlock);
+ if (Handler->DoesDropOnUnsuitable())
+ {
+ Handler->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ);
+ }
+ }
+
+ a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_FluidBlock, 0);
+
+ return true;
+ }
+
+};
diff --git a/source/Items/ItemCauldron.h b/source/Items/ItemCauldron.h
index 216766cf8..8b2ddc29f 100644
--- a/source/Items/ItemCauldron.h
+++ b/source/Items/ItemCauldron.h
@@ -1,41 +1,41 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-
-
-
-
-
-class cItemCauldronHandler :
- public cItemHandler
-{
-public:
- cItemCauldronHandler(int a_ItemType) :
- cItemHandler(a_ItemType)
- {
- }
-
-
- virtual bool IsPlaceable(void) override
- {
- return true;
- }
-
-
- 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
- {
- a_BlockType = E_BLOCK_CAULDRON;
- a_BlockMeta = 0;
- return true;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemCauldronHandler :
+ public cItemHandler
+{
+public:
+ cItemCauldronHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+ }
+
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
+ }
+
+
+ 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
+ {
+ a_BlockType = E_BLOCK_CAULDRON;
+ a_BlockMeta = 0;
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/source/Items/ItemCloth.h b/source/Items/ItemCloth.h
index f6e2c1906..aca27a299 100644
--- a/source/Items/ItemCloth.h
+++ b/source/Items/ItemCloth.h
@@ -1,23 +1,23 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-
-
-
-
-
-class cItemClothHandler :
- public cItemHandler
-{
-public:
- cItemClothHandler(int a_ItemType)
- : cItemHandler(a_ItemType)
- {
-
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemClothHandler :
+ public cItemHandler
+{
+public:
+ cItemClothHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+
+ }
+} ;
+
+
+
+
diff --git a/source/Items/ItemDoor.h b/source/Items/ItemDoor.h
index 6ebeab43a..72ea0beed 100644
--- a/source/Items/ItemDoor.h
+++ b/source/Items/ItemDoor.h
@@ -1,45 +1,45 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-#include "../World.h"
-
-
-
-
-
-class cItemDoorHandler :
- public cItemHandler
-{
-public:
- cItemDoorHandler(int a_ItemType) :
- cItemHandler(a_ItemType)
- {
-
- }
-
- virtual bool IsPlaceable(void) override
- {
- return true;
- }
-
- 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
- {
- a_BlockType = (m_ItemType == E_ITEM_WOODEN_DOOR) ? E_BLOCK_WOODEN_DOOR : E_BLOCK_IRON_DOOR;
- return BlockHandler(a_BlockType)->GetPlacementBlockTypeMeta(
- a_World, a_Player,
- a_BlockX, a_BlockY, a_BlockZ, a_BlockFace,
- a_CursorX, a_CursorY, a_CursorZ,
- a_BlockType, a_BlockMeta
- );
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+
+
+
+
+
+class cItemDoorHandler :
+ public cItemHandler
+{
+public:
+ cItemDoorHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+
+ }
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
+ }
+
+ 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
+ {
+ a_BlockType = (m_ItemType == E_ITEM_WOODEN_DOOR) ? E_BLOCK_WOODEN_DOOR : E_BLOCK_IRON_DOOR;
+ return BlockHandler(a_BlockType)->GetPlacementBlockTypeMeta(
+ a_World, a_Player,
+ a_BlockX, a_BlockY, a_BlockZ, a_BlockFace,
+ a_CursorX, a_CursorY, a_CursorZ,
+ a_BlockType, a_BlockMeta
+ );
+ }
+} ;
+
+
+
+
diff --git a/source/Items/ItemDye.h b/source/Items/ItemDye.h
index e4c31fa4b..984d452b7 100644
--- a/source/Items/ItemDye.h
+++ b/source/Items/ItemDye.h
@@ -1,44 +1,44 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-#include "../World.h"
-#include "../Player.h"
-
-
-
-
-
-class cItemDyeHandler :
- public cItemHandler
-{
-public:
- cItemDyeHandler(int a_ItemType)
- : cItemHandler(a_ItemType)
- {
-
- }
-
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
- {
- // TODO: Handle coloring the sheep, too (OnItemUseOnEntity maybe)
-
- // Handle growing the plants:
- if (a_Item.m_ItemDamage == E_META_DYE_WHITE)
- {
- if (a_World->GrowRipePlant(a_BlockX, a_BlockY, a_BlockZ, true))
- {
- if (a_Player->GetGameMode() != gmCreative)
- {
- a_Player->GetInventory().RemoveOneEquippedItem();
- return true;
- }
- }
- }
- return false;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Player.h"
+
+
+
+
+
+class cItemDyeHandler :
+ public cItemHandler
+{
+public:
+ cItemDyeHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+
+ }
+
+ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
+ {
+ // TODO: Handle coloring the sheep, too (OnItemUseOnEntity maybe)
+
+ // Handle growing the plants:
+ if (a_Item.m_ItemDamage == E_META_DYE_WHITE)
+ {
+ if (a_World->GrowRipePlant(a_BlockX, a_BlockY, a_BlockZ, true))
+ {
+ if (a_Player->GetGameMode() != gmCreative)
+ {
+ a_Player->GetInventory().RemoveOneEquippedItem();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+} ;
+
+
+
+
diff --git a/source/Items/ItemFlowerPot.h b/source/Items/ItemFlowerPot.h
index 0e55ddb51..befa2ff21 100644
--- a/source/Items/ItemFlowerPot.h
+++ b/source/Items/ItemFlowerPot.h
@@ -1,41 +1,41 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-
-
-
-
-
-class cItemFlowerPotHandler :
- public cItemHandler
-{
-public:
- cItemFlowerPotHandler(int a_ItemType) :
- cItemHandler(a_ItemType)
- {
- }
-
-
- virtual bool IsPlaceable(void) override
- {
- return true;
- }
-
-
- 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
- {
- a_BlockType = E_BLOCK_FLOWER_POT;
- a_BlockMeta = 0;
- return true;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemFlowerPotHandler :
+ public cItemHandler
+{
+public:
+ cItemFlowerPotHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+ }
+
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
+ }
+
+
+ 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
+ {
+ a_BlockType = E_BLOCK_FLOWER_POT;
+ a_BlockMeta = 0;
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/source/Items/ItemFood.h b/source/Items/ItemFood.h
index d4c0a012a..6434f3dc7 100644
--- a/source/Items/ItemFood.h
+++ b/source/Items/ItemFood.h
@@ -1,56 +1,62 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-
-
-class cItemFoodHandler : public cItemHandler
-{
-public:
- cItemFoodHandler(int a_ItemType)
- : cItemHandler(a_ItemType)
- {
- }
-
- virtual bool IsFood() override
- {
- return true;
- }
-
- virtual FoodInfo GetFoodInfo() override
- {
- switch(m_ItemType)
- {
- case E_ITEM_BREAD:
- return FoodInfo(5, 6.f);
- case E_ITEM_COOKIE:
- return FoodInfo(2, 0.4f);
- case E_ITEM_MELON_SLICE:
- return FoodInfo(2, 1.2f);
- case E_ITEM_RAW_CHICKEN:
- return FoodInfo(2, 1.2f, 30);
- case E_ITEM_COOKED_CHICKEN:
- return FoodInfo(6, 7.2f);
- case E_ITEM_RAW_BEEF:
- case E_ITEM_RAW_PORKCHOP:
- return FoodInfo(3, 1.8f);
- case E_ITEM_STEAK:
- case E_ITEM_COOKED_PORKCHOP:
- return FoodInfo(8, 12.8f);
- case E_ITEM_RAW_FISH:
- return FoodInfo(2, 1.2f);
- case E_ITEM_COOKED_FISH:
- return FoodInfo(5, 6.f);
- case E_ITEM_RED_APPLE:
- return FoodInfo(4, 2.4f);
- case E_ITEM_GOLDEN_APPLE:
- return FoodInfo(4, 9.6f);
- case E_ITEM_ROTTEN_FLESH:
- return FoodInfo(4, 0.8f, 80);
- case E_ITEM_SPIDER_EYE:
- return FoodInfo(2, 3.2f, 100);
- }
- return FoodInfo(0, 0.f);
- }
-
-}; \ No newline at end of file
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemFoodHandler :
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+public:
+ cItemFoodHandler(int a_ItemType)
+ : super(a_ItemType)
+ {
+ }
+
+
+ virtual bool IsFood(void) override
+ {
+ return true;
+ }
+
+
+ virtual FoodInfo GetFoodInfo(void) override
+ {
+ switch(m_ItemType)
+ {
+ // Please keep alpha-sorted.
+ case E_ITEM_BAKED_POTATO: return FoodInfo(6, 7.2);
+ case E_ITEM_BREAD: return FoodInfo(5, 6);
+ case E_ITEM_CARROT: return FoodInfo(4, 4.8);
+ case E_ITEM_COOKED_CHICKEN: return FoodInfo(6, 7.2);
+ case E_ITEM_COOKED_FISH: return FoodInfo(5, 6);
+ case E_ITEM_COOKED_PORKCHOP: return FoodInfo(8, 12.8);
+ case E_ITEM_COOKIE: return FoodInfo(2, 0.4);
+ case E_ITEM_GOLDEN_APPLE: return FoodInfo(4, 9.6);
+ case E_ITEM_GOLDEN_CARROT: return FoodInfo(6, 14.4);
+ case E_ITEM_MELON_SLICE: return FoodInfo(2, 1.2);
+ case E_ITEM_POISONOUS_POTATO: return FoodInfo(2, 1.2, 60);
+ case E_ITEM_POTATO: return FoodInfo(1, 0.6);
+ case E_ITEM_PUMPKIN_PIE: return FoodInfo(8, 4.8);
+ case E_ITEM_RAW_BEEF: return FoodInfo(3, 1.8);
+ case E_ITEM_RAW_CHICKEN: return FoodInfo(2, 1.2, 30);
+ case E_ITEM_RAW_FISH: return FoodInfo(2, 1.2);
+ case E_ITEM_RAW_PORKCHOP: return FoodInfo(3, 1.8);
+ case E_ITEM_RED_APPLE: return FoodInfo(4, 2.4);
+ case E_ITEM_ROTTEN_FLESH: return FoodInfo(4, 0.8, 80);
+ case E_ITEM_SPIDER_EYE: return FoodInfo(2, 3.2, 100);
+ case E_ITEM_STEAK: return FoodInfo(8, 12.8);
+ }
+ LOGWARNING("%s: Unknown food item (%d), returning zero nutrition", __FUNCTION__, m_ItemType);
+ return FoodInfo(0, 0.f);
+ }
+
+};
+
+
+
+
diff --git a/source/Items/ItemHandler.cpp b/source/Items/ItemHandler.cpp
index d99457029..c81916f5f 100644
--- a/source/Items/ItemHandler.cpp
+++ b/source/Items/ItemHandler.cpp
@@ -1,497 +1,500 @@
-
-#include "Globals.h"
-#include "ItemHandler.h"
-#include "../Item.h"
-#include "../World.h"
-#include "../Player.h"
-
-// Handlers:
-#include "ItemBed.h"
-#include "ItemBrewingStand.h"
-#include "ItemBucket.h"
-#include "ItemCauldron.h"
-#include "ItemCloth.h"
-#include "ItemDoor.h"
-#include "ItemDye.h"
-#include "ItemFlowerPot.h"
-#include "ItemFood.h"
-#include "ItemHoe.h"
-#include "ItemLeaves.h"
-#include "ItemLighter.h"
-#include "ItemMinecart.h"
-#include "ItemPickaxe.h"
-#include "ItemRedstoneDust.h"
-#include "ItemRedstoneRepeater.h"
-#include "ItemSapling.h"
-#include "ItemSeeds.h"
-#include "ItemShears.h"
-#include "ItemShovel.h"
-#include "ItemSign.h"
-#include "ItemSlab.h"
-#include "ItemSpawnEgg.h"
-#include "ItemSugarcane.h"
-#include "ItemSword.h"
-#include "ItemWood.h"
-
-#include "../Blocks/BlockHandler.h"
-
-
-
-
-
-bool cItemHandler::m_HandlerInitialized = false;
-cItemHandler * cItemHandler::m_ItemHandler[2268];
-
-
-
-
-
-cItemHandler *cItemHandler::GetItemHandler(int a_ItemType)
-{
- if(a_ItemType < 0) a_ItemType = 0;
-
- if(!m_HandlerInitialized)
- { //We have to initialize
- memset(m_ItemHandler, 0, sizeof(m_ItemHandler));
- m_HandlerInitialized = true;
- }
- if(m_ItemHandler[a_ItemType])
- return m_ItemHandler[a_ItemType];
- m_ItemHandler[a_ItemType] = CreateItemHandler(a_ItemType);
- return m_ItemHandler[a_ItemType];
-}
-
-
-
-
-
-cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType)
-{
- switch(a_ItemType)
- {
- default: return new cItemHandler(a_ItemType);
-
- // Single item per handler, alphabetically sorted:
- case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType);
- case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType);
- case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType);
- case E_ITEM_BED: return new cItemBedHandler(a_ItemType);
- case E_ITEM_BREWING_STAND: return new cItemBrewingStandHandler(a_ItemType);
- case E_ITEM_CAULDRON: return new cItemCauldronHandler(a_ItemType);
- case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType);
- case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType);
- case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType);
- case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType);
- case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType);
- case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType);
- case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType);
- case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType);
- case E_ITEM_SUGARCANE: return new cItemSugarcaneHandler(a_ItemType);
-
- case E_ITEM_WOODEN_HOE:
- case E_ITEM_STONE_HOE:
- case E_ITEM_IRON_HOE:
- case E_ITEM_GOLD_HOE:
- case E_ITEM_DIAMOND_HOE:
- {
- return new cItemHoeHandler(a_ItemType);
- }
-
- case E_ITEM_WOODEN_PICKAXE:
- case E_ITEM_STONE_PICKAXE:
- case E_ITEM_IRON_PICKAXE:
- case E_ITEM_GOLD_PICKAXE:
- case E_ITEM_DIAMOND_PICKAXE:
- {
- return new cItemPickaxeHandler(a_ItemType);
- }
-
- case E_ITEM_WOODEN_SHOVEL:
- case E_ITEM_STONE_SHOVEL:
- case E_ITEM_IRON_SHOVEL:
- case E_ITEM_GOLD_SHOVEL:
- case E_ITEM_DIAMOND_SHOVEL:
- {
- return new cItemShovelHandler(a_ItemType);
- }
-
- case E_ITEM_WOODEN_SWORD:
- case E_ITEM_STONE_SWORD:
- case E_ITEM_IRON_SWORD:
- case E_ITEM_GOLD_SWORD:
- case E_ITEM_DIAMOND_SWORD:
- {
- return new cItemSwordHandler(a_ItemType);
- }
-
- case E_BLOCK_STONE_SLAB:
- case E_BLOCK_WOODEN_SLAB:
- {
- return new cItemSlabHandler(a_ItemType);
- }
-
- case E_BLOCK_LOG:
- case E_BLOCK_PLANKS:
- {
- return new cItemWoodHandler(a_ItemType);
- }
-
- case E_ITEM_BUCKET:
- case E_ITEM_WATER_BUCKET:
- case E_ITEM_LAVA_BUCKET:
- {
- return new cItemBucketHandler(a_ItemType);
- }
-
- case E_ITEM_CARROT:
- case E_ITEM_MELON_SEEDS:
- case E_ITEM_POTATO:
- case E_ITEM_PUMPKIN_SEEDS:
- case E_ITEM_SEEDS:
- {
- return new cItemSeedsHandler(a_ItemType);
- }
-
- case E_ITEM_IRON_DOOR:
- case E_ITEM_WOODEN_DOOR:
- {
- return new cItemDoorHandler(a_ItemType);
- }
-
- case E_ITEM_MINECART:
- case E_ITEM_CHEST_MINECART:
- case E_ITEM_FURNACE_MINECART:
- {
- return new cItemMinecartHandler(a_ItemType);
- }
-
- // Food:
- case E_ITEM_BREAD:
- case E_ITEM_COOKIE:
- case E_ITEM_MELON_SLICE:
- case E_ITEM_RAW_CHICKEN:
- case E_ITEM_COOKED_CHICKEN:
- case E_ITEM_RAW_BEEF:
- case E_ITEM_RAW_PORKCHOP:
- case E_ITEM_STEAK:
- case E_ITEM_COOKED_PORKCHOP:
- case E_ITEM_RAW_FISH:
- case E_ITEM_COOKED_FISH:
- case E_ITEM_RED_APPLE:
- case E_ITEM_GOLDEN_APPLE:
- case E_ITEM_ROTTEN_FLESH:
- case E_ITEM_SPIDER_EYE:
- {
- return new cItemFoodHandler(a_ItemType);
- }
- }
-}
-
-
-
-
-
-void cItemHandler::Deinit()
-{
- for(int i = 0; i < 2267; i++)
- {
- delete m_ItemHandler[i];
- }
- memset(m_ItemHandler, 0, sizeof(m_ItemHandler)); // Don't leave any dangling pointers around, just in case
- m_HandlerInitialized = false;
-}
-
-
-
-
-
-cItemHandler::cItemHandler(int a_ItemType)
-{
- m_ItemType = a_ItemType;
-}
-
-
-
-
-
-bool cItemHandler::OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir)
-{
- return false;
-}
-
-
-
-
-
-bool cItemHandler::OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir)
-{
- return false;
-}
-
-
-
-
-
-void cItemHandler::OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ)
-{
- BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
- cBlockHandler * Handler = cBlockHandler::GetBlockHandler(Block);
-
- if (a_Player->GetGameMode() == gmSurvival)
- {
- if (!BlockRequiresSpecialTool(Block) || CanHarvestBlock(Block))
- {
- Handler->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ);
- }
- }
-
- a_Player->UseEquippedItem();
-}
-
-
-
-
-
-void cItemHandler::OnFoodEaten(cWorld * a_World, cPlayer * a_Player, cItem * a_Item)
-{
-
-}
-
-
-
-
-
-char cItemHandler::GetMaxStackSize(void)
-{
- if (m_ItemType < 256)
- {
- // All blocks can stack up to 64
- return 64;
- }
-
- switch (m_ItemType) //sorted by id
- {
- case E_ITEM_ARROW: return 64;
- case E_ITEM_BAKED_POTATO: return 64;
- case E_ITEM_BLAZE_POWDER: return 64;
- case E_ITEM_BLAZE_ROD: return 64;
- case E_ITEM_BONE: return 64;
- case E_ITEM_BOOK: return 64;
- case E_ITEM_BOTTLE_O_ENCHANTING: return 64;
- case E_ITEM_BOWL: return 64;
- case E_ITEM_BREAD: return 64;
- case E_ITEM_BREWING_STAND: return 64;
- case E_ITEM_BUCKET: return 1; // TODO: change this to 16 when turning compatibility to 1.3
- case E_ITEM_CARROT: return 64;
- case E_ITEM_CAULDRON: return 64;
- case E_ITEM_CLAY: return 64;
- case E_ITEM_CLAY_BRICK: return 64;
- case E_ITEM_CLOCK: return 64;
- case E_ITEM_COAL: return 64;
- case E_ITEM_COMPARATOR: return 64;
- case E_ITEM_COMPASS: return 64;
- case E_ITEM_COOKED_CHICKEN: return 64;
- case E_ITEM_COOKED_FISH: return 64;
- case E_ITEM_COOKED_PORKCHOP: return 64;
- case E_ITEM_COOKIE: return 64;
- case E_ITEM_DIAMOND: return 64;
- case E_ITEM_DYE: return 64;
- case E_ITEM_EGG: return 16;
- case E_ITEM_EMERALD: return 64;
- case E_ITEM_ENDER_PEARL: return 16;
- case E_ITEM_EYE_OF_ENDER: return 64;
- case E_ITEM_FEATHER: return 64;
- case E_ITEM_FERMENTED_SPIDER_EYE: return 64;
- case E_ITEM_FIRE_CHARGE: return 64;
- case E_ITEM_FIREWORK_ROCKET: return 64;
- case E_ITEM_FIREWORK_STAR: return 64;
- case E_ITEM_FLINT: return 64;
- case E_ITEM_FLOWER_POT: return 64;
- case E_ITEM_GHAST_TEAR: return 64;
- case E_ITEM_GLASS_BOTTLE: return 64;
- case E_ITEM_GLISTERING_MELON: return 64;
- case E_ITEM_GLOWSTONE_DUST: return 64;
- case E_ITEM_GOLD: return 64;
- case E_ITEM_GOLDEN_APPLE: return 64;
- case E_ITEM_GOLDEN_CARROT: return 64;
- case E_ITEM_GOLD_NUGGET: return 64;
- case E_ITEM_GUNPOWDER: return 64;
- case E_ITEM_HEAD: return 64;
- case E_ITEM_IRON: return 64;
- case E_ITEM_LEATHER: return 64;
- case E_ITEM_MAGMA_CREAM: return 64;
- case E_ITEM_MAP: return 64;
- case E_ITEM_MELON_SEEDS: return 64;
- case E_ITEM_MELON_SLICE: return 64;
- case E_ITEM_NETHER_BRICK: return 64;
- case E_ITEM_NETHER_WART: return 64;
- case E_ITEM_PAINTINGS: return 64;
- case E_ITEM_PAPER: return 64;
- case E_ITEM_POISONOUS_POTATO: return 64;
- case E_ITEM_POTATO: return 64;
- case E_ITEM_PUMPKIN_PIE: return 64;
- case E_ITEM_PUMPKIN_SEEDS: return 64;
- case E_ITEM_RAW_BEEF: return 64;
- case E_ITEM_RAW_CHICKEN: return 64;
- case E_ITEM_RAW_FISH: return 64;
- case E_ITEM_RAW_PORKCHOP: return 64;
- case E_ITEM_RED_APPLE: return 64;
- case E_ITEM_REDSTONE_DUST: return 64;
- case E_ITEM_REDSTONE_REPEATER: return 64;
- case E_ITEM_ROTTEN_FLESH: return 64;
- case E_ITEM_SEEDS: return 64;
- case E_ITEM_SIGN: return 16;
- case E_ITEM_SLIMEBALL: return 64;
- case E_ITEM_SNOWBALL: return 16;
- case E_ITEM_SPAWN_EGG: return 64;
- case E_ITEM_SPIDER_EYE: return 64;
- case E_ITEM_STEAK: return 64;
- case E_ITEM_STICK: return 64;
- case E_ITEM_STRING: return 64;
- case E_ITEM_SUGAR: return 64;
- case E_ITEM_SUGAR_CANE: return 64;
- case E_ITEM_WHEAT: return 64;
- }
- // By default items don't stack:
- return 1;
-}
-
-
-
-
-
-bool cItemHandler::IsTool()
-{
- // TODO: Rewrite this to list all tools specifically
- return
- (m_ItemType >= 256 && m_ItemType <= 259)
- || (m_ItemType == 261)
- || (m_ItemType >= 267 && m_ItemType <= 279)
- || (m_ItemType >= 283 && m_ItemType <= 286)
- || (m_ItemType >= 290 && m_ItemType <= 294)
- || (m_ItemType >= 256 && m_ItemType <= 259)
- || (m_ItemType == 325)
- || (m_ItemType == 346);
-}
-
-
-
-
-
-bool cItemHandler::IsFood(void)
-{
- switch (m_ItemType)
- {
- case E_ITEM_RED_APPLE:
- case E_ITEM_GOLDEN_APPLE:
- case E_ITEM_MUSHROOM_SOUP:
- case E_ITEM_BREAD:
- case E_ITEM_RAW_PORKCHOP:
- case E_ITEM_COOKED_PORKCHOP:
- case E_ITEM_MILK:
- case E_ITEM_RAW_FISH:
- case E_ITEM_COOKED_FISH:
- case E_ITEM_COOKIE:
- case E_ITEM_MELON_SLICE:
- case E_ITEM_RAW_BEEF:
- case E_ITEM_STEAK:
- case E_ITEM_RAW_CHICKEN:
- case E_ITEM_COOKED_CHICKEN:
- case E_ITEM_ROTTEN_FLESH:
- case E_ITEM_SPIDER_EYE:
- case E_ITEM_CARROT:
- case E_ITEM_POTATO:
- case E_ITEM_BAKED_POTATO:
- case E_ITEM_POISONOUS_POTATO:
- {
- return true;
- }
- } // switch (m_ItemType)
- return false;
-}
-
-
-
-
-
-bool cItemHandler::IsPlaceable(void)
-{
- // We can place any block that has a corresponding E_BLOCK_TYPE:
- return (m_ItemType >= 1) && (m_ItemType <= E_BLOCK_MAX_TYPE_ID);
-}
-
-
-
-
-
-bool cItemHandler::CanHarvestBlock(BLOCKTYPE a_BlockType)
-{
- return false;
-}
-
-
-
-
-
-bool cItemHandler::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
-)
-{
- ASSERT(m_ItemType < 256); // Items with IDs above 255 should all be handled by specific handlers
-
- if (m_ItemType > 256)
- {
- LOGERROR("%s: Item %d has no valid block!", __FUNCTION__, m_ItemType);
- return false;
- }
-
- cBlockHandler * BlockH = BlockHandler(m_ItemType);
- return BlockH->GetPlacementBlockTypeMeta(
- a_World, a_Player,
- a_BlockX, a_BlockY, a_BlockZ, a_BlockFace,
- a_CursorX, a_CursorY, a_CursorZ,
- a_BlockType, a_BlockMeta
- );
-}
-
-
-
-
-
-bool cItemHandler::EatItem(cPlayer * a_Player, cItem * a_Item)
-{
- FoodInfo Info = GetFoodInfo();
-
- if(Info.FoodLevel > 0 || Info.Saturation > 0.f)
- {
- bool Success = a_Player->Feed(Info.FoodLevel, Info.Saturation);
- if(Success && Info.PoisionChance > 0)
- {
- MTRand r1;
- if((r1.randInt(100) - Info.PoisionChance) <= 0)
- { //Unlucky guy :D
- //TODO: Make player ill
- }
- }
-
- return Success;
- }
-
- return false;
-}
-
-
-
-
-
-cItemHandler::FoodInfo cItemHandler::GetFoodInfo()
-{
- return FoodInfo(0, 0.f);
-}
-
-
-
-
+
+#include "Globals.h"
+#include "ItemHandler.h"
+#include "../Item.h"
+#include "../World.h"
+#include "../Player.h"
+#include "../FastRandom.h"
+
+// Handlers:
+#include "ItemBed.h"
+#include "ItemBrewingStand.h"
+#include "ItemBucket.h"
+#include "ItemCauldron.h"
+#include "ItemCloth.h"
+#include "ItemDoor.h"
+#include "ItemDye.h"
+#include "ItemFlowerPot.h"
+#include "ItemFood.h"
+#include "ItemHoe.h"
+#include "ItemLeaves.h"
+#include "ItemLighter.h"
+#include "ItemMinecart.h"
+#include "ItemPickaxe.h"
+#include "ItemRedstoneDust.h"
+#include "ItemRedstoneRepeater.h"
+#include "ItemSapling.h"
+#include "ItemSeeds.h"
+#include "ItemShears.h"
+#include "ItemShovel.h"
+#include "ItemSign.h"
+#include "ItemSlab.h"
+#include "ItemSpawnEgg.h"
+#include "ItemSugarcane.h"
+#include "ItemSword.h"
+#include "ItemWood.h"
+
+#include "../Blocks/BlockHandler.h"
+
+
+
+
+
+bool cItemHandler::m_HandlerInitialized = false;
+cItemHandler * cItemHandler::m_ItemHandler[2268];
+
+
+
+
+
+cItemHandler *cItemHandler::GetItemHandler(int a_ItemType)
+{
+ if(a_ItemType < 0) a_ItemType = 0;
+
+ if(!m_HandlerInitialized)
+ { //We have to initialize
+ memset(m_ItemHandler, 0, sizeof(m_ItemHandler));
+ m_HandlerInitialized = true;
+ }
+ if(m_ItemHandler[a_ItemType])
+ return m_ItemHandler[a_ItemType];
+ m_ItemHandler[a_ItemType] = CreateItemHandler(a_ItemType);
+ return m_ItemHandler[a_ItemType];
+}
+
+
+
+
+
+cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType)
+{
+ switch(a_ItemType)
+ {
+ default: return new cItemHandler(a_ItemType);
+
+ // Single item per handler, alphabetically sorted:
+ case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType);
+ case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType);
+ case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType);
+ case E_ITEM_BED: return new cItemBedHandler(a_ItemType);
+ case E_ITEM_BREWING_STAND: return new cItemBrewingStandHandler(a_ItemType);
+ case E_ITEM_CAULDRON: return new cItemCauldronHandler(a_ItemType);
+ case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType);
+ case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType);
+ case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType);
+ case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType);
+ case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType);
+ case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType);
+ case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType);
+ case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType);
+ case E_ITEM_SUGARCANE: return new cItemSugarcaneHandler(a_ItemType);
+
+ case E_ITEM_WOODEN_HOE:
+ case E_ITEM_STONE_HOE:
+ case E_ITEM_IRON_HOE:
+ case E_ITEM_GOLD_HOE:
+ case E_ITEM_DIAMOND_HOE:
+ {
+ return new cItemHoeHandler(a_ItemType);
+ }
+
+ case E_ITEM_WOODEN_PICKAXE:
+ case E_ITEM_STONE_PICKAXE:
+ case E_ITEM_IRON_PICKAXE:
+ case E_ITEM_GOLD_PICKAXE:
+ case E_ITEM_DIAMOND_PICKAXE:
+ {
+ return new cItemPickaxeHandler(a_ItemType);
+ }
+
+ case E_ITEM_WOODEN_SHOVEL:
+ case E_ITEM_STONE_SHOVEL:
+ case E_ITEM_IRON_SHOVEL:
+ case E_ITEM_GOLD_SHOVEL:
+ case E_ITEM_DIAMOND_SHOVEL:
+ {
+ return new cItemShovelHandler(a_ItemType);
+ }
+
+ case E_ITEM_WOODEN_SWORD:
+ case E_ITEM_STONE_SWORD:
+ case E_ITEM_IRON_SWORD:
+ case E_ITEM_GOLD_SWORD:
+ case E_ITEM_DIAMOND_SWORD:
+ {
+ return new cItemSwordHandler(a_ItemType);
+ }
+
+ case E_BLOCK_STONE_SLAB:
+ case E_BLOCK_WOODEN_SLAB:
+ {
+ return new cItemSlabHandler(a_ItemType);
+ }
+
+ case E_BLOCK_LOG:
+ case E_BLOCK_PLANKS:
+ {
+ return new cItemWoodHandler(a_ItemType);
+ }
+
+ case E_ITEM_BUCKET:
+ case E_ITEM_WATER_BUCKET:
+ case E_ITEM_LAVA_BUCKET:
+ {
+ return new cItemBucketHandler(a_ItemType);
+ }
+
+ case E_ITEM_CARROT:
+ case E_ITEM_MELON_SEEDS:
+ case E_ITEM_POTATO:
+ case E_ITEM_PUMPKIN_SEEDS:
+ case E_ITEM_SEEDS:
+ {
+ return new cItemSeedsHandler(a_ItemType);
+ }
+
+ case E_ITEM_IRON_DOOR:
+ case E_ITEM_WOODEN_DOOR:
+ {
+ return new cItemDoorHandler(a_ItemType);
+ }
+
+ case E_ITEM_MINECART:
+ case E_ITEM_CHEST_MINECART:
+ case E_ITEM_FURNACE_MINECART:
+ {
+ return new cItemMinecartHandler(a_ItemType);
+ }
+
+ // Food:
+ case E_ITEM_BREAD:
+ case E_ITEM_COOKIE:
+ case E_ITEM_MELON_SLICE:
+ case E_ITEM_RAW_CHICKEN:
+ case E_ITEM_COOKED_CHICKEN:
+ case E_ITEM_RAW_BEEF:
+ case E_ITEM_RAW_PORKCHOP:
+ case E_ITEM_STEAK:
+ case E_ITEM_COOKED_PORKCHOP:
+ case E_ITEM_RAW_FISH:
+ case E_ITEM_COOKED_FISH:
+ case E_ITEM_RED_APPLE:
+ case E_ITEM_GOLDEN_APPLE:
+ case E_ITEM_ROTTEN_FLESH:
+ case E_ITEM_SPIDER_EYE:
+ {
+ return new cItemFoodHandler(a_ItemType);
+ }
+ }
+}
+
+
+
+
+
+void cItemHandler::Deinit()
+{
+ for(int i = 0; i < 2267; i++)
+ {
+ delete m_ItemHandler[i];
+ }
+ memset(m_ItemHandler, 0, sizeof(m_ItemHandler)); // Don't leave any dangling pointers around, just in case
+ m_HandlerInitialized = false;
+}
+
+
+
+
+
+cItemHandler::cItemHandler(int a_ItemType)
+{
+ m_ItemType = a_ItemType;
+}
+
+
+
+
+
+bool cItemHandler::OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir)
+{
+ return false;
+}
+
+
+
+
+
+bool cItemHandler::OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir)
+{
+ return false;
+}
+
+
+
+
+
+void cItemHandler::OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+ cBlockHandler * Handler = cBlockHandler::GetBlockHandler(Block);
+
+ if (a_Player->GetGameMode() == gmSurvival)
+ {
+ if (!BlockRequiresSpecialTool(Block) || CanHarvestBlock(Block))
+ {
+ Handler->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ);
+ }
+ }
+
+ a_Player->UseEquippedItem();
+}
+
+
+
+
+
+void cItemHandler::OnFoodEaten(cWorld * a_World, cPlayer * a_Player, cItem * a_Item)
+{
+
+}
+
+
+
+
+
+char cItemHandler::GetMaxStackSize(void)
+{
+ if (m_ItemType < 256)
+ {
+ // All blocks can stack up to 64
+ return 64;
+ }
+
+ switch (m_ItemType) //sorted by id
+ {
+ case E_ITEM_ARROW: return 64;
+ case E_ITEM_BAKED_POTATO: return 64;
+ case E_ITEM_BLAZE_POWDER: return 64;
+ case E_ITEM_BLAZE_ROD: return 64;
+ case E_ITEM_BONE: return 64;
+ case E_ITEM_BOOK: return 64;
+ case E_ITEM_BOTTLE_O_ENCHANTING: return 64;
+ case E_ITEM_BOWL: return 64;
+ case E_ITEM_BREAD: return 64;
+ case E_ITEM_BREWING_STAND: return 64;
+ case E_ITEM_BUCKET: return 1; // TODO: change this to 16 when turning compatibility to 1.3
+ case E_ITEM_CARROT: return 64;
+ case E_ITEM_CAULDRON: return 64;
+ case E_ITEM_CLAY: return 64;
+ case E_ITEM_CLAY_BRICK: return 64;
+ case E_ITEM_CLOCK: return 64;
+ case E_ITEM_COAL: return 64;
+ case E_ITEM_COMPARATOR: return 64;
+ case E_ITEM_COMPASS: return 64;
+ case E_ITEM_COOKED_CHICKEN: return 64;
+ case E_ITEM_COOKED_FISH: return 64;
+ case E_ITEM_COOKED_PORKCHOP: return 64;
+ case E_ITEM_COOKIE: return 64;
+ case E_ITEM_DIAMOND: return 64;
+ case E_ITEM_DYE: return 64;
+ case E_ITEM_EGG: return 16;
+ case E_ITEM_EMERALD: return 64;
+ case E_ITEM_ENDER_PEARL: return 16;
+ case E_ITEM_EYE_OF_ENDER: return 64;
+ case E_ITEM_FEATHER: return 64;
+ case E_ITEM_FERMENTED_SPIDER_EYE: return 64;
+ case E_ITEM_FIRE_CHARGE: return 64;
+ case E_ITEM_FIREWORK_ROCKET: return 64;
+ case E_ITEM_FIREWORK_STAR: return 64;
+ case E_ITEM_FLINT: return 64;
+ case E_ITEM_FLOWER_POT: return 64;
+ case E_ITEM_GHAST_TEAR: return 64;
+ case E_ITEM_GLASS_BOTTLE: return 64;
+ case E_ITEM_GLISTERING_MELON: return 64;
+ case E_ITEM_GLOWSTONE_DUST: return 64;
+ case E_ITEM_GOLD: return 64;
+ case E_ITEM_GOLDEN_APPLE: return 64;
+ case E_ITEM_GOLDEN_CARROT: return 64;
+ case E_ITEM_GOLD_NUGGET: return 64;
+ case E_ITEM_GUNPOWDER: return 64;
+ case E_ITEM_HEAD: return 64;
+ case E_ITEM_IRON: return 64;
+ case E_ITEM_LEATHER: return 64;
+ case E_ITEM_MAGMA_CREAM: return 64;
+ case E_ITEM_MAP: return 64;
+ case E_ITEM_MELON_SEEDS: return 64;
+ case E_ITEM_MELON_SLICE: return 64;
+ case E_ITEM_NETHER_BRICK: return 64;
+ case E_ITEM_NETHER_WART: return 64;
+ case E_ITEM_PAINTINGS: return 64;
+ case E_ITEM_PAPER: return 64;
+ case E_ITEM_POISONOUS_POTATO: return 64;
+ case E_ITEM_POTATO: return 64;
+ case E_ITEM_PUMPKIN_PIE: return 64;
+ case E_ITEM_PUMPKIN_SEEDS: return 64;
+ case E_ITEM_RAW_BEEF: return 64;
+ case E_ITEM_RAW_CHICKEN: return 64;
+ case E_ITEM_RAW_FISH: return 64;
+ case E_ITEM_RAW_PORKCHOP: return 64;
+ case E_ITEM_RED_APPLE: return 64;
+ case E_ITEM_REDSTONE_DUST: return 64;
+ case E_ITEM_REDSTONE_REPEATER: return 64;
+ case E_ITEM_ROTTEN_FLESH: return 64;
+ case E_ITEM_SEEDS: return 64;
+ case E_ITEM_SIGN: return 16;
+ case E_ITEM_SLIMEBALL: return 64;
+ case E_ITEM_SNOWBALL: return 16;
+ case E_ITEM_SPAWN_EGG: return 64;
+ case E_ITEM_SPIDER_EYE: return 64;
+ case E_ITEM_STEAK: return 64;
+ case E_ITEM_STICK: return 64;
+ case E_ITEM_STRING: return 64;
+ case E_ITEM_SUGAR: return 64;
+ case E_ITEM_SUGAR_CANE: return 64;
+ case E_ITEM_WHEAT: return 64;
+ }
+ // By default items don't stack:
+ return 1;
+}
+
+
+
+
+
+bool cItemHandler::IsTool()
+{
+ // TODO: Rewrite this to list all tools specifically
+ return
+ (m_ItemType >= 256 && m_ItemType <= 259)
+ || (m_ItemType == 261)
+ || (m_ItemType >= 267 && m_ItemType <= 279)
+ || (m_ItemType >= 283 && m_ItemType <= 286)
+ || (m_ItemType >= 290 && m_ItemType <= 294)
+ || (m_ItemType >= 256 && m_ItemType <= 259)
+ || (m_ItemType == 325)
+ || (m_ItemType == 346);
+}
+
+
+
+
+
+bool cItemHandler::IsFood(void)
+{
+ switch (m_ItemType)
+ {
+ case E_ITEM_RED_APPLE:
+ case E_ITEM_GOLDEN_APPLE:
+ case E_ITEM_MUSHROOM_SOUP:
+ case E_ITEM_BREAD:
+ case E_ITEM_RAW_PORKCHOP:
+ case E_ITEM_COOKED_PORKCHOP:
+ case E_ITEM_MILK:
+ case E_ITEM_RAW_FISH:
+ case E_ITEM_COOKED_FISH:
+ case E_ITEM_COOKIE:
+ case E_ITEM_MELON_SLICE:
+ case E_ITEM_RAW_BEEF:
+ case E_ITEM_STEAK:
+ case E_ITEM_RAW_CHICKEN:
+ case E_ITEM_COOKED_CHICKEN:
+ case E_ITEM_ROTTEN_FLESH:
+ case E_ITEM_SPIDER_EYE:
+ case E_ITEM_CARROT:
+ case E_ITEM_POTATO:
+ case E_ITEM_BAKED_POTATO:
+ case E_ITEM_POISONOUS_POTATO:
+ {
+ return true;
+ }
+ } // switch (m_ItemType)
+ return false;
+}
+
+
+
+
+
+bool cItemHandler::IsPlaceable(void)
+{
+ // We can place any block that has a corresponding E_BLOCK_TYPE:
+ return (m_ItemType >= 1) && (m_ItemType <= E_BLOCK_MAX_TYPE_ID);
+}
+
+
+
+
+
+bool cItemHandler::CanHarvestBlock(BLOCKTYPE a_BlockType)
+{
+ return false;
+}
+
+
+
+
+
+bool cItemHandler::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
+)
+{
+ ASSERT(m_ItemType < 256); // Items with IDs above 255 should all be handled by specific handlers
+
+ if (m_ItemType > 256)
+ {
+ LOGERROR("%s: Item %d has no valid block!", __FUNCTION__, m_ItemType);
+ return false;
+ }
+
+ cBlockHandler * BlockH = BlockHandler(m_ItemType);
+ return BlockH->GetPlacementBlockTypeMeta(
+ a_World, a_Player,
+ a_BlockX, a_BlockY, a_BlockZ, a_BlockFace,
+ a_CursorX, a_CursorY, a_CursorZ,
+ a_BlockType, a_BlockMeta
+ );
+}
+
+
+
+
+
+bool cItemHandler::EatItem(cPlayer * a_Player, cItem * a_Item)
+{
+ FoodInfo Info = GetFoodInfo();
+
+ if ((Info.FoodLevel > 0) || (Info.Saturation > 0.f))
+ {
+ bool Success = a_Player->Feed(Info.FoodLevel, Info.Saturation);
+
+ // If consumed and there's chance of foodpoisoning, do it:
+ if (Success && (Info.PoisonChance > 0))
+ {
+ cFastRandom r1;
+ if ((r1.NextInt(100, a_Player->GetUniqueID()) - Info.PoisonChance) <= 0)
+ {
+ a_Player->FoodPoison(300);
+ }
+ }
+
+ return Success;
+ }
+
+ return false;
+}
+
+
+
+
+
+cItemHandler::FoodInfo cItemHandler::GetFoodInfo()
+{
+ return FoodInfo(0, 0.f);
+}
+
+
+
+
diff --git a/source/Items/ItemHandler.h b/source/Items/ItemHandler.h
index 0c141dea0..44d43e8f7 100644
--- a/source/Items/ItemHandler.h
+++ b/source/Items/ItemHandler.h
@@ -1,95 +1,96 @@
-
-#pragma once
-
-#include "../Defines.h"
-#include "../Item.h"
-
-
-
-
-
-// fwd:
-class cWorld;
-class cPlayer;
-
-
-
-
-
-class cItemHandler
-{
-public:
- cItemHandler(int a_ItemType);
-
- /// Called when the player tries to use the item. Return false to make the item unusable. DEFAULT: False
- virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir); //eg for fishing or hoes
-
- /// Called while the player diggs a block using this item
- virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_HeldItem, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace);
-
- /// Called when the player destroys a block using this item. This also calls the drop function for the destroyed block
- virtual void OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_X, int a_Y, int a_Z);
-
- /// Called after the player has eaten this item.
- virtual void OnFoodEaten(cWorld *a_World, cPlayer *a_Player, cItem *a_Item);
-
- /// Returns the maximum stack size for a given item
- virtual char GetMaxStackSize(void);
-
- struct FoodInfo
- {
- FoodInfo(short a_FoodLevel, float a_Saturation, char a_PoisionChance = 0)
- {
- FoodLevel = a_FoodLevel;
- Saturation = a_Saturation;
- PoisionChance = a_PoisionChance;
- }
- short FoodLevel;
- float Saturation;
- char PoisionChance; //0 - 100
- };
-
- /// Returns the FoodInfo for this item. (FoodRecovery, Saturation and PoisionChance)
- virtual FoodInfo GetFoodInfo();
-
- /// Lets the player eat a selected item. Returns true if the player ate the item
- virtual bool EatItem(cPlayer *a_Player, cItem *a_Item);
-
- /// Indicates if this item is a tool
- virtual bool IsTool(void);
-
- /// Indicates if this item is food
- virtual bool IsFood(void);
-
- /// Blocks simply get placed
- virtual bool IsPlaceable(void);
-
- /** Called before a block is placed into a world.
- The handler should return true to allow placement, false to refuse.
- Also, the handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block.
- */
- 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
- );
-
- /// Returns whether this tool/item can harvest a specific block (e.g. wooden pickaxe can harvest stone, but wood can´t) DEFAULT: False
- virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType);
-
- static cItemHandler * GetItemHandler(int a_ItemType);
- static cItemHandler * GetItemHandler(const cItem & a_Item) { return GetItemHandler(a_Item.m_ItemType); }
-
- static void Deinit();
-
-protected:
- int m_ItemType;
- static cItemHandler *CreateItemHandler(int m_ItemType);
-
- static cItemHandler *m_ItemHandler[2268];
- static bool m_HandlerInitialized; //used to detect if the itemhandlers are initialized
-};
-
-//Short function
-inline cItemHandler *ItemHandler(int a_ItemType) { return cItemHandler::GetItemHandler(a_ItemType); }
+
+#pragma once
+
+#include "../Defines.h"
+#include "../Item.h"
+
+
+
+
+
+// fwd:
+class cWorld;
+class cPlayer;
+
+
+
+
+
+class cItemHandler
+{
+public:
+ cItemHandler(int a_ItemType);
+
+ /// Called when the player tries to use the item. Return false to make the item unusable. DEFAULT: False
+ virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir); //eg for fishing or hoes
+
+ /// Called while the player diggs a block using this item
+ virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_HeldItem, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace);
+
+ /// Called when the player destroys a block using this item. This also calls the drop function for the destroyed block
+ virtual void OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_X, int a_Y, int a_Z);
+
+ /// Called after the player has eaten this item.
+ virtual void OnFoodEaten(cWorld *a_World, cPlayer *a_Player, cItem *a_Item);
+
+ /// Returns the maximum stack size for a given item
+ virtual char GetMaxStackSize(void);
+
+ struct FoodInfo
+ {
+ int FoodLevel;
+ double Saturation;
+ int PoisonChance; // 0 - 100, in percent. 0 = no chance of poisoning, 100 = sure poisoning
+
+ FoodInfo(int a_FoodLevel, double a_Saturation, int a_PoisonChance = 0) :
+ FoodLevel(a_FoodLevel),
+ Saturation(a_Saturation),
+ PoisonChance(a_PoisonChance)
+ {
+ }
+ } ;
+
+ /// Returns the FoodInfo for this item. (FoodRecovery, Saturation and PoisionChance)
+ virtual FoodInfo GetFoodInfo();
+
+ /// Lets the player eat a selected item. Returns true if the player ate the item
+ virtual bool EatItem(cPlayer *a_Player, cItem *a_Item);
+
+ /// Indicates if this item is a tool
+ virtual bool IsTool(void);
+
+ /// Indicates if this item is food
+ virtual bool IsFood(void);
+
+ /// Blocks simply get placed
+ virtual bool IsPlaceable(void);
+
+ /** Called before a block is placed into a world.
+ The handler should return true to allow placement, false to refuse.
+ Also, the handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block.
+ */
+ 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
+ );
+
+ /// Returns whether this tool/item can harvest a specific block (e.g. wooden pickaxe can harvest stone, but wood can´t) DEFAULT: False
+ virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType);
+
+ static cItemHandler * GetItemHandler(int a_ItemType);
+ static cItemHandler * GetItemHandler(const cItem & a_Item) { return GetItemHandler(a_Item.m_ItemType); }
+
+ static void Deinit();
+
+protected:
+ int m_ItemType;
+ static cItemHandler *CreateItemHandler(int m_ItemType);
+
+ static cItemHandler *m_ItemHandler[2268];
+ static bool m_HandlerInitialized; //used to detect if the itemhandlers are initialized
+};
+
+//Short function
+inline cItemHandler *ItemHandler(int a_ItemType) { return cItemHandler::GetItemHandler(a_ItemType); }
diff --git a/source/Items/ItemHoe.h b/source/Items/ItemHoe.h
index 58f989341..577463f21 100644
--- a/source/Items/ItemHoe.h
+++ b/source/Items/ItemHoe.h
@@ -1,31 +1,31 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-#include "../World.h"
-#include "../Player.h"
-
-class cItemHoeHandler : public cItemHandler
-{
-public:
- cItemHoeHandler(int a_ItemType)
- : cItemHandler(a_ItemType)
- {
-
- }
-
- virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
- {
- BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
-
- if ((Block == E_BLOCK_DIRT) || (Block == E_BLOCK_GRASS))
- {
- a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FARMLAND, 0);
-
- a_Player->UseEquippedItem();
- return true;
-
- }
- return false;
- }
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Player.h"
+
+class cItemHoeHandler : public cItemHandler
+{
+public:
+ cItemHoeHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+
+ }
+
+ virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
+ {
+ BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+
+ if ((Block == E_BLOCK_DIRT) || (Block == E_BLOCK_GRASS))
+ {
+ a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FARMLAND, 0);
+
+ a_Player->UseEquippedItem();
+ return true;
+
+ }
+ return false;
+ }
}; \ No newline at end of file
diff --git a/source/Items/ItemLeaves.h b/source/Items/ItemLeaves.h
index 14d63a630..60222eaa9 100644
--- a/source/Items/ItemLeaves.h
+++ b/source/Items/ItemLeaves.h
@@ -1,41 +1,41 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-
-
-
-
-
-class cItemLeavesHandler :
- public cItemHandler
-{
- typedef cItemHandler super;
-
-public:
- cItemLeavesHandler(int a_ItemType)
- : cItemHandler(a_ItemType)
- {
- }
-
- 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
- {
- bool res = super::GetPlacementBlockTypeMeta(
- a_World, a_Player,
- a_BlockX, a_BlockY, a_BlockZ, a_BlockFace,
- a_CursorX, a_CursorY, a_CursorZ,
- a_BlockType, a_BlockMeta
- );
- a_BlockMeta = a_BlockMeta | 0x4; //0x4 bit set means this is a player-placed leaves block, not to be decayed
- return res;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemLeavesHandler :
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+public:
+ cItemLeavesHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+ }
+
+ 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
+ {
+ bool res = super::GetPlacementBlockTypeMeta(
+ a_World, a_Player,
+ a_BlockX, a_BlockY, a_BlockZ, a_BlockFace,
+ a_CursorX, a_CursorY, a_CursorZ,
+ a_BlockType, a_BlockMeta
+ );
+ a_BlockMeta = a_BlockMeta | 0x4; //0x4 bit set means this is a player-placed leaves block, not to be decayed
+ return res;
+ }
+} ;
+
+
+
+
diff --git a/source/Items/ItemLighter.h b/source/Items/ItemLighter.h
index dcca08d47..cf7822832 100644
--- a/source/Items/ItemLighter.h
+++ b/source/Items/ItemLighter.h
@@ -1,56 +1,56 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-#include "../World.h"
-#include "../Player.h"
-#include "../TNTEntity.h"
-
-
-
-
-
-class cItemLighterHandler :
- public cItemHandler
-{
-public:
- cItemLighterHandler(int a_ItemType) :
- cItemHandler(a_ItemType)
- {
- }
-
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override
- {
- if (a_BlockFace < 0)
- {
- return false;
- }
-
- a_Player->UseEquippedItem();
-
- switch (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ))
- {
- case E_BLOCK_TNT:
- {
- // Activate the TNT:
- a_World->BroadcastSoundEffect("random.fuse", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f);
- a_World->SpawnPrimedTNT(a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 4); // 4 seconds to boom
- a_World->SetBlock(a_BlockX,a_BlockY,a_BlockZ, E_BLOCK_AIR, 0);
- break;
- }
- default:
- {
- // Light a fire next to the block:
- AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
- a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 0);
- break;
- }
- }
-
- return false;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Player.h"
+#include "../TNTEntity.h"
+
+
+
+
+
+class cItemLighterHandler :
+ public cItemHandler
+{
+public:
+ cItemLighterHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+ }
+
+ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override
+ {
+ if (a_BlockFace < 0)
+ {
+ return false;
+ }
+
+ a_Player->UseEquippedItem();
+
+ switch (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ))
+ {
+ case E_BLOCK_TNT:
+ {
+ // Activate the TNT:
+ a_World->BroadcastSoundEffect("random.fuse", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f);
+ a_World->SpawnPrimedTNT(a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 4); // 4 seconds to boom
+ a_World->SetBlock(a_BlockX,a_BlockY,a_BlockZ, E_BLOCK_AIR, 0);
+ break;
+ }
+ default:
+ {
+ // Light a fire next to the block:
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+ a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 0);
+ break;
+ }
+ }
+
+ return false;
+ }
+} ;
+
+
+
+
diff --git a/source/Items/ItemMinecart.h b/source/Items/ItemMinecart.h
index ca8cc16aa..1470104a3 100644
--- a/source/Items/ItemMinecart.h
+++ b/source/Items/ItemMinecart.h
@@ -1,80 +1,80 @@
-
-// ItemMinecart.h
-
-// Declares the various minecart ItemHandlers
-
-
-
-
-
-#pragma once
-
-// Not needed, we're being included only from ItemHandler.cpp which already has this file: #include "ItemHandler.h"
-#include "../Minecart.h"
-
-
-
-
-
-class cItemMinecartHandler :
- public cItemHandler
-{
- typedef cItemHandler super;
-
-public:
- cItemMinecartHandler(int a_ItemType) :
- super(a_ItemType)
- {
- }
-
-
-
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
- {
- if (a_Dir < 0)
- {
- return false;
- }
-
- // Check that there's rail in there:
- BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
- switch (Block)
- {
- case E_BLOCK_MINECART_TRACKS:
- case E_BLOCK_POWERED_RAIL:
- case E_BLOCK_DETECTOR_RAIL:
- {
- // These are allowed
- break;
- }
- default:
- {
- LOGD("Used minecart on an unsuitable block %d (%s)", Block, ItemTypeToString(Block).c_str());
- return false;
- }
- }
-
- double x = (double)a_BlockX + 0.5;
- double y = (double)a_BlockY + 0.5;
- double z = (double)a_BlockZ + 0.5;
- cMinecart * Minecart = NULL;
- switch (m_ItemType)
- {
- case E_ITEM_MINECART: Minecart = new cEmptyMinecart (x, y, z); break;
- case E_ITEM_CHEST_MINECART: Minecart = new cMinecartWithChest (x, y, z); break;
- case E_ITEM_FURNACE_MINECART: Minecart = new cMinecartWithFurnace(x, y, z); break;
- default:
- {
- ASSERT(!"Unhandled minecart item");
- return false;
- }
- } // switch (m_ItemType)
- Minecart->Initialize(a_World);
- return true;
- }
-
-} ;
-
-
-
-
+
+// ItemMinecart.h
+
+// Declares the various minecart ItemHandlers
+
+
+
+
+
+#pragma once
+
+// Not needed, we're being included only from ItemHandler.cpp which already has this file: #include "ItemHandler.h"
+#include "../Minecart.h"
+
+
+
+
+
+class cItemMinecartHandler :
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+public:
+ cItemMinecartHandler(int a_ItemType) :
+ super(a_ItemType)
+ {
+ }
+
+
+
+ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
+ {
+ if (a_Dir < 0)
+ {
+ return false;
+ }
+
+ // Check that there's rail in there:
+ BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+ switch (Block)
+ {
+ case E_BLOCK_MINECART_TRACKS:
+ case E_BLOCK_POWERED_RAIL:
+ case E_BLOCK_DETECTOR_RAIL:
+ {
+ // These are allowed
+ break;
+ }
+ default:
+ {
+ LOGD("Used minecart on an unsuitable block %d (%s)", Block, ItemTypeToString(Block).c_str());
+ return false;
+ }
+ }
+
+ double x = (double)a_BlockX + 0.5;
+ double y = (double)a_BlockY + 0.5;
+ double z = (double)a_BlockZ + 0.5;
+ cMinecart * Minecart = NULL;
+ switch (m_ItemType)
+ {
+ case E_ITEM_MINECART: Minecart = new cEmptyMinecart (x, y, z); break;
+ case E_ITEM_CHEST_MINECART: Minecart = new cMinecartWithChest (x, y, z); break;
+ case E_ITEM_FURNACE_MINECART: Minecart = new cMinecartWithFurnace(x, y, z); break;
+ default:
+ {
+ ASSERT(!"Unhandled minecart item");
+ return false;
+ }
+ } // switch (m_ItemType)
+ Minecart->Initialize(a_World);
+ return true;
+ }
+
+} ;
+
+
+
+
diff --git a/source/Items/ItemPickaxe.h b/source/Items/ItemPickaxe.h
index 26d0d5ade..1a2c205c0 100644
--- a/source/Items/ItemPickaxe.h
+++ b/source/Items/ItemPickaxe.h
@@ -1,76 +1,76 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-#include "../World.h"
-#include "../Player.h"
-
-class cItemPickaxeHandler : public cItemHandler
-{
-public:
- cItemPickaxeHandler(int a_ItemType)
- : cItemHandler(a_ItemType)
- {
-
- }
-
- char PickaxeLevel()
- {
- switch(m_ItemType)
- {
- case E_ITEM_WOODEN_PICKAXE:
- case E_ITEM_GOLD_PICKAXE:
- return 1;
- case E_ITEM_STONE_PICKAXE:
- return 2;
- case E_ITEM_IRON_PICKAXE:
- return 3;
- case E_ITEM_DIAMOND_PICKAXE:
- return 4;
- default:
- return 0;
- }
- }
-
- virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override
- {
- switch(a_BlockType)
- {
- case E_BLOCK_OBSIDIAN:
- return PickaxeLevel() >= 4;
- case E_BLOCK_DIAMOND_BLOCK:
- case E_BLOCK_DIAMOND_ORE:
- case E_BLOCK_GOLD_BLOCK:
- case E_BLOCK_GOLD_ORE:
- case E_BLOCK_REDSTONE_ORE:
- case E_BLOCK_REDSTONE_ORE_GLOWING:
- case E_BLOCK_EMERALD_ORE:
- return PickaxeLevel() >= 3;
- case E_BLOCK_IRON_BLOCK:
- case E_BLOCK_IRON_ORE:
- case E_BLOCK_LAPIS_ORE:
- case E_BLOCK_LAPIS_BLOCK:
- return PickaxeLevel() >= 2;
- case E_BLOCK_COAL_ORE:
- case E_BLOCK_STONE:
- case E_BLOCK_COBBLESTONE:
- case E_BLOCK_END_STONE:
- case E_BLOCK_MOSSY_COBBLESTONE:
- case E_BLOCK_SANDSTONE_STAIRS:
- case E_BLOCK_SANDSTONE:
- case E_BLOCK_STONE_BRICKS:
- case E_BLOCK_NETHER_BRICK:
- case E_BLOCK_NETHERRACK:
- case E_BLOCK_STONE_SLAB:
- case E_BLOCK_DOUBLE_STONE_SLAB:
- case E_BLOCK_STONE_PRESSURE_PLATE:
- case E_BLOCK_BRICK:
- case E_BLOCK_COBBLESTONE_STAIRS:
- case E_BLOCK_STONE_BRICK_STAIRS:
- case E_BLOCK_NETHER_BRICK_STAIRS:
- case E_BLOCK_CAULDRON:
- return PickaxeLevel() >= 1;
- }
- return false;
- }
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Player.h"
+
+class cItemPickaxeHandler : public cItemHandler
+{
+public:
+ cItemPickaxeHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+
+ }
+
+ char PickaxeLevel()
+ {
+ switch(m_ItemType)
+ {
+ case E_ITEM_WOODEN_PICKAXE:
+ case E_ITEM_GOLD_PICKAXE:
+ return 1;
+ case E_ITEM_STONE_PICKAXE:
+ return 2;
+ case E_ITEM_IRON_PICKAXE:
+ return 3;
+ case E_ITEM_DIAMOND_PICKAXE:
+ return 4;
+ default:
+ return 0;
+ }
+ }
+
+ virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override
+ {
+ switch(a_BlockType)
+ {
+ case E_BLOCK_OBSIDIAN:
+ return PickaxeLevel() >= 4;
+ case E_BLOCK_DIAMOND_BLOCK:
+ case E_BLOCK_DIAMOND_ORE:
+ case E_BLOCK_GOLD_BLOCK:
+ case E_BLOCK_GOLD_ORE:
+ case E_BLOCK_REDSTONE_ORE:
+ case E_BLOCK_REDSTONE_ORE_GLOWING:
+ case E_BLOCK_EMERALD_ORE:
+ return PickaxeLevel() >= 3;
+ case E_BLOCK_IRON_BLOCK:
+ case E_BLOCK_IRON_ORE:
+ case E_BLOCK_LAPIS_ORE:
+ case E_BLOCK_LAPIS_BLOCK:
+ return PickaxeLevel() >= 2;
+ case E_BLOCK_COAL_ORE:
+ case E_BLOCK_STONE:
+ case E_BLOCK_COBBLESTONE:
+ case E_BLOCK_END_STONE:
+ case E_BLOCK_MOSSY_COBBLESTONE:
+ case E_BLOCK_SANDSTONE_STAIRS:
+ case E_BLOCK_SANDSTONE:
+ case E_BLOCK_STONE_BRICKS:
+ case E_BLOCK_NETHER_BRICK:
+ case E_BLOCK_NETHERRACK:
+ case E_BLOCK_STONE_SLAB:
+ case E_BLOCK_DOUBLE_STONE_SLAB:
+ case E_BLOCK_STONE_PRESSURE_PLATE:
+ case E_BLOCK_BRICK:
+ case E_BLOCK_COBBLESTONE_STAIRS:
+ case E_BLOCK_STONE_BRICK_STAIRS:
+ case E_BLOCK_NETHER_BRICK_STAIRS:
+ case E_BLOCK_CAULDRON:
+ return PickaxeLevel() >= 1;
+ }
+ return false;
+ }
}; \ No newline at end of file
diff --git a/source/Items/ItemRedstoneDust.h b/source/Items/ItemRedstoneDust.h
index 4179893d7..b7860b187 100644
--- a/source/Items/ItemRedstoneDust.h
+++ b/source/Items/ItemRedstoneDust.h
@@ -1,38 +1,38 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-
-
-
-
-
-class cItemRedstoneDustHandler : public cItemHandler
-{
-public:
- cItemRedstoneDustHandler(int a_ItemType)
- : cItemHandler(a_ItemType)
- {
- }
-
- virtual bool IsPlaceable(void) override
- {
- return true;
- }
-
- 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
- {
- a_BlockType = E_BLOCK_REDSTONE_WIRE;
- a_BlockMeta = 0;
- return true;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemRedstoneDustHandler : public cItemHandler
+{
+public:
+ cItemRedstoneDustHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+ }
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
+ }
+
+ 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
+ {
+ a_BlockType = E_BLOCK_REDSTONE_WIRE;
+ a_BlockMeta = 0;
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/source/Items/ItemRedstoneRepeater.h b/source/Items/ItemRedstoneRepeater.h
index ce3008d01..459070579 100644
--- a/source/Items/ItemRedstoneRepeater.h
+++ b/source/Items/ItemRedstoneRepeater.h
@@ -1,40 +1,40 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-#include "../Simulator/RedstoneSimulator.h"
-
-
-
-
-
-class cItemRedstoneRepeaterHandler :
- public cItemHandler
-{
-public:
- cItemRedstoneRepeaterHandler(int a_ItemType)
- : cItemHandler(a_ItemType)
- {
- }
-
- virtual bool IsPlaceable() override
- {
- return true;
- }
-
- 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
- {
- a_BlockType = E_BLOCK_REDSTONE_REPEATER_OFF;
- a_BlockMeta = cRedstoneSimulator::RepeaterRotationToMetaData(a_Player->GetRotation());
- return true;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../Simulator/RedstoneSimulator.h"
+
+
+
+
+
+class cItemRedstoneRepeaterHandler :
+ public cItemHandler
+{
+public:
+ cItemRedstoneRepeaterHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+ }
+
+ virtual bool IsPlaceable() override
+ {
+ return true;
+ }
+
+ 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
+ {
+ a_BlockType = E_BLOCK_REDSTONE_REPEATER_OFF;
+ a_BlockMeta = cRedstoneSimulator::RepeaterRotationToMetaData(a_Player->GetRotation());
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/source/Items/ItemSapling.h b/source/Items/ItemSapling.h
index 313968ea5..dc0810a45 100644
--- a/source/Items/ItemSapling.h
+++ b/source/Items/ItemSapling.h
@@ -1,42 +1,42 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-
-
-
-
-
-class cItemSaplingHandler : public cItemHandler
-{
- typedef cItemHandler super;
-
-public:
- cItemSaplingHandler(int a_ItemType)
- : cItemHandler(a_ItemType)
- {
-
- }
-
- 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
- {
- bool res = super::GetPlacementBlockTypeMeta(
- a_World, a_Player,
- a_BlockX, a_BlockY, a_BlockZ, a_BlockFace,
- a_CursorX, a_CursorY, a_CursorZ,
- a_BlockType, a_BlockMeta
- );
- // Only the lowest 3 bits are important
- a_BlockMeta = a_BlockMeta & 0x7;
- return res;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemSaplingHandler : public cItemHandler
+{
+ typedef cItemHandler super;
+
+public:
+ cItemSaplingHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+
+ }
+
+ 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
+ {
+ bool res = super::GetPlacementBlockTypeMeta(
+ a_World, a_Player,
+ a_BlockX, a_BlockY, a_BlockZ, a_BlockFace,
+ a_CursorX, a_CursorY, a_CursorZ,
+ a_BlockType, a_BlockMeta
+ );
+ // Only the lowest 3 bits are important
+ a_BlockMeta = a_BlockMeta & 0x7;
+ return res;
+ }
+} ;
+
+
+
+
diff --git a/source/Items/ItemSeeds.h b/source/Items/ItemSeeds.h
index 7a5f77b80..8ca86663f 100644
--- a/source/Items/ItemSeeds.h
+++ b/source/Items/ItemSeeds.h
@@ -1,65 +1,65 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-#include "../World.h"
-
-
-
-
-
-class cItemSeedsHandler :
- public cItemHandler
-{
-public:
- cItemSeedsHandler(int a_ItemType) :
- cItemHandler(a_ItemType)
- {
-
- }
-
- virtual bool IsPlaceable(void) override
- {
- return true;
- }
-
- 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
- {
- if (a_BlockFace != BLOCK_FACE_TOP)
- {
- // Only allow planting seeds from the top side of the block
- return false;
- }
-
- // Only allow placement on farmland
- int X = a_BlockX;
- int Y = a_BlockY;
- int Z = a_BlockZ;
- AddFaceDirection(X, Y, Z, a_BlockFace, true);
- if (a_World->GetBlock(X, Y, Z) != E_BLOCK_FARMLAND)
- {
- return false;
- }
-
- a_BlockMeta = 0;
- switch (m_ItemType)
- {
- case E_ITEM_CARROT: a_BlockType = E_BLOCK_CARROTS; return true;
- case E_ITEM_MELON_SEEDS: a_BlockType = E_BLOCK_MELON_STEM; return true;
- case E_ITEM_POTATO: a_BlockType = E_BLOCK_POTATOES; return true;
- case E_ITEM_PUMPKIN_SEEDS: a_BlockType = E_BLOCK_PUMPKIN_STEM; return true;
- case E_ITEM_SEEDS: a_BlockType = E_BLOCK_CROPS; return true;
- default: a_BlockType = E_BLOCK_AIR; return true;
- }
- return false;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+
+
+
+
+
+class cItemSeedsHandler :
+ public cItemHandler
+{
+public:
+ cItemSeedsHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+
+ }
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
+ }
+
+ 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
+ {
+ if (a_BlockFace != BLOCK_FACE_TOP)
+ {
+ // Only allow planting seeds from the top side of the block
+ return false;
+ }
+
+ // Only allow placement on farmland
+ int X = a_BlockX;
+ int Y = a_BlockY;
+ int Z = a_BlockZ;
+ AddFaceDirection(X, Y, Z, a_BlockFace, true);
+ if (a_World->GetBlock(X, Y, Z) != E_BLOCK_FARMLAND)
+ {
+ return false;
+ }
+
+ a_BlockMeta = 0;
+ switch (m_ItemType)
+ {
+ case E_ITEM_CARROT: a_BlockType = E_BLOCK_CARROTS; return true;
+ case E_ITEM_MELON_SEEDS: a_BlockType = E_BLOCK_MELON_STEM; return true;
+ case E_ITEM_POTATO: a_BlockType = E_BLOCK_POTATOES; return true;
+ case E_ITEM_PUMPKIN_SEEDS: a_BlockType = E_BLOCK_PUMPKIN_STEM; return true;
+ case E_ITEM_SEEDS: a_BlockType = E_BLOCK_CROPS; return true;
+ default: a_BlockType = E_BLOCK_AIR; return true;
+ }
+ return false;
+ }
+} ;
+
+
+
+
diff --git a/source/Items/ItemShears.h b/source/Items/ItemShears.h
index 5c1c248f4..a904dd1ae 100644
--- a/source/Items/ItemShears.h
+++ b/source/Items/ItemShears.h
@@ -1,63 +1,63 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-#include "../World.h"
-#include "../Player.h"
-
-
-
-
-
-class cItemShearsHandler :
- public cItemHandler
-{
-public:
- cItemShearsHandler(int a_ItemType) :
- cItemHandler(a_ItemType)
- {
- }
-
-
- virtual bool IsTool(void) override
- {
- return true;
- }
-
-
- virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
- {
- BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
- if (Block == E_BLOCK_LEAVES)
- {
- cItems Drops;
- Drops.push_back(cItem(E_BLOCK_LEAVES, 1, a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x03));
- a_World->SpawnItemPickups(Drops, a_BlockX, a_BlockY, a_BlockZ);
-
- a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
- a_Player->UseEquippedItem();
- return true;
- }
- // TODO: cobweb, vines
- return false;
- }
-
-
- virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override
- {
- switch (a_BlockType)
- {
- case E_BLOCK_COBWEB:
- case E_BLOCK_VINES:
- case E_BLOCK_LEAVES:
- {
- return true;
- }
- } // switch (a_BlockType)
- return false;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Player.h"
+
+
+
+
+
+class cItemShearsHandler :
+ public cItemHandler
+{
+public:
+ cItemShearsHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+ }
+
+
+ virtual bool IsTool(void) override
+ {
+ return true;
+ }
+
+
+ virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
+ {
+ BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+ if (Block == E_BLOCK_LEAVES)
+ {
+ cItems Drops;
+ Drops.push_back(cItem(E_BLOCK_LEAVES, 1, a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x03));
+ a_World->SpawnItemPickups(Drops, a_BlockX, a_BlockY, a_BlockZ);
+
+ a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
+ a_Player->UseEquippedItem();
+ return true;
+ }
+ // TODO: cobweb, vines
+ return false;
+ }
+
+
+ virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override
+ {
+ switch (a_BlockType)
+ {
+ case E_BLOCK_COBWEB:
+ case E_BLOCK_VINES:
+ case E_BLOCK_LEAVES:
+ {
+ return true;
+ }
+ } // switch (a_BlockType)
+ return false;
+ }
+} ;
+
+
+
+
diff --git a/source/Items/ItemShovel.h b/source/Items/ItemShovel.h
index f26e393c8..a26e7d34e 100644
--- a/source/Items/ItemShovel.h
+++ b/source/Items/ItemShovel.h
@@ -1,41 +1,41 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-#include "../World.h"
-#include "../Player.h"
-
-#include "../Blocks/BlockHandler.h"
-
-
-
-
-
-class cItemShovelHandler : public cItemHandler
-{
-public:
- cItemShovelHandler(int a_ItemType)
- : cItemHandler(a_ItemType)
- {
-
- }
-
- virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
- {
- BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
- if (Block == E_BLOCK_SNOW)
- {
- BlockHandler(Block)->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ);
-
- a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
- a_Player->UseEquippedItem();
- return true;
- }
- return false;
- }
-
- virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override
- {
- return (a_BlockType == E_BLOCK_SNOW);
- }
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Player.h"
+
+#include "../Blocks/BlockHandler.h"
+
+
+
+
+
+class cItemShovelHandler : public cItemHandler
+{
+public:
+ cItemShovelHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+
+ }
+
+ virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
+ {
+ BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
+ if (Block == E_BLOCK_SNOW)
+ {
+ BlockHandler(Block)->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ);
+
+ a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
+ a_Player->UseEquippedItem();
+ return true;
+ }
+ return false;
+ }
+
+ virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override
+ {
+ return (a_BlockType == E_BLOCK_SNOW);
+ }
}; \ No newline at end of file
diff --git a/source/Items/ItemSign.h b/source/Items/ItemSign.h
index 7a0924147..5ccd79e29 100644
--- a/source/Items/ItemSign.h
+++ b/source/Items/ItemSign.h
@@ -1,51 +1,51 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-#include "../World.h"
-#include "../Blocks/BlockSign.h"
-
-
-
-
-
-class cItemSignHandler :
- public cItemHandler
-{
-public:
- cItemSignHandler(int a_ItemType) :
- cItemHandler(a_ItemType)
- {
- }
-
-
- virtual bool IsPlaceable(void) override
- {
- return true;
- }
-
-
- 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
- {
- if (a_BlockFace == BLOCK_FACE_TOP)
- {
- a_BlockMeta = cBlockSignHandler::RotationToMetaData(a_Player->GetRotation());
- a_BlockType = E_BLOCK_SIGN_POST;
- }
- else
- {
- a_BlockMeta = cBlockSignHandler::DirectionToMetaData(a_BlockFace);
- a_BlockType = E_BLOCK_WALLSIGN;
- }
- return true;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Blocks/BlockSign.h"
+
+
+
+
+
+class cItemSignHandler :
+ public cItemHandler
+{
+public:
+ cItemSignHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+ }
+
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
+ }
+
+
+ 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
+ {
+ if (a_BlockFace == BLOCK_FACE_TOP)
+ {
+ a_BlockMeta = cBlockSignHandler::RotationToMetaData(a_Player->GetRotation());
+ a_BlockType = E_BLOCK_SIGN_POST;
+ }
+ else
+ {
+ a_BlockMeta = cBlockSignHandler::DirectionToMetaData(a_BlockFace);
+ a_BlockType = E_BLOCK_WALLSIGN;
+ }
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/source/Items/ItemSlab.h b/source/Items/ItemSlab.h
index 9b3aaed03..80de05eb5 100644
--- a/source/Items/ItemSlab.h
+++ b/source/Items/ItemSlab.h
@@ -1,52 +1,52 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-#include "../World.h"
-
-
-
-
-
-class cItemSlabHandler : public cItemHandler
-{
-public:
- cItemSlabHandler(int a_ItemType)
- : cItemHandler(a_ItemType)
- {
-
- }
-
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
- {
- BLOCKTYPE Block;
- NIBBLETYPE Meta;
- a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, Block, Meta);
-
- if (
- ((a_Dir == 0) || (a_Dir == 1)) // Only when clicking on top or on bottom of the block
- && ((Block == E_BLOCK_WOODEN_SLAB) || (Block == E_BLOCK_STONE_SLAB)) // It is a slab
- && (Block == a_Item.m_ItemType) // Same slab
- && ((Meta & 0x7) == (a_Item.m_ItemDamage & 0x7))) // Same Texture
- {
- if (a_Player->GetGameMode() == eGameMode_Creative)
- {
- a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, Block - 1, Meta); // Block - 1 simple hack to save one if statement
- return true;
- }
- else
- {
- if (a_Player->GetInventory().RemoveOneEquippedItem())
- {
- a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, Block - 1, Meta); // Block - 1 simple hack to save one if statement
- return true;
- }
- }
- }
- return false;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+
+
+
+
+
+class cItemSlabHandler : public cItemHandler
+{
+public:
+ cItemSlabHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+
+ }
+
+ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
+ {
+ BLOCKTYPE Block;
+ NIBBLETYPE Meta;
+ a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, Block, Meta);
+
+ if (
+ ((a_Dir == 0) || (a_Dir == 1)) // Only when clicking on top or on bottom of the block
+ && ((Block == E_BLOCK_WOODEN_SLAB) || (Block == E_BLOCK_STONE_SLAB)) // It is a slab
+ && (Block == a_Item.m_ItemType) // Same slab
+ && ((Meta & 0x7) == (a_Item.m_ItemDamage & 0x7))) // Same Texture
+ {
+ if (a_Player->GetGameMode() == eGameMode_Creative)
+ {
+ a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, Block - 1, Meta); // Block - 1 simple hack to save one if statement
+ return true;
+ }
+ else
+ {
+ if (a_Player->GetInventory().RemoveOneEquippedItem())
+ {
+ a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, Block - 1, Meta); // Block - 1 simple hack to save one if statement
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+} ;
+
+
+
+
diff --git a/source/Items/ItemSpawnEgg.h b/source/Items/ItemSpawnEgg.h
index 7f5b253ef..e563d7e15 100644
--- a/source/Items/ItemSpawnEgg.h
+++ b/source/Items/ItemSpawnEgg.h
@@ -1,52 +1,52 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-#include "../World.h"
-#include "../Player.h"
-
-
-
-
-
-class cItemSpawnEggHandler : public cItemHandler
-{
-public:
- cItemSpawnEggHandler(int a_ItemType) :
- cItemHandler(a_ItemType)
- {
-
- }
-
-
- virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override
- {
- if (a_BlockFace < 0)
- {
- return false;
- }
-
- AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
-
- if (a_BlockFace == BLOCK_FACE_BOTTOM)
- {
- a_BlockY--;
- }
-
- if (a_World->SpawnMob(a_BlockX + 0.5, a_BlockY, a_BlockZ + 0.5, a_Item.m_ItemDamage) >= 0)
- {
- if (a_Player->GetGameMode() != 1)
- {
- // The mob was spawned, "use" the item:
- a_Player->GetInventory().RemoveOneEquippedItem();
- }
- return true;
- }
-
- return false;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Player.h"
+
+
+
+
+
+class cItemSpawnEggHandler : public cItemHandler
+{
+public:
+ cItemSpawnEggHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+
+ }
+
+
+ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override
+ {
+ if (a_BlockFace < 0)
+ {
+ return false;
+ }
+
+ AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+
+ if (a_BlockFace == BLOCK_FACE_BOTTOM)
+ {
+ a_BlockY--;
+ }
+
+ if (a_World->SpawnMob(a_BlockX + 0.5, a_BlockY, a_BlockZ + 0.5, a_Item.m_ItemDamage) >= 0)
+ {
+ if (a_Player->GetGameMode() != 1)
+ {
+ // The mob was spawned, "use" the item:
+ a_Player->GetInventory().RemoveOneEquippedItem();
+ }
+ return true;
+ }
+
+ return false;
+ }
+} ;
+
+
+
+
diff --git a/source/Items/ItemSugarcane.h b/source/Items/ItemSugarcane.h
index 9a0875939..ce93aa3e5 100644
--- a/source/Items/ItemSugarcane.h
+++ b/source/Items/ItemSugarcane.h
@@ -1,39 +1,39 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-
-
-
-
-
-class cItemSugarcaneHandler :
- public cItemHandler
-{
-public:
- cItemSugarcaneHandler(int a_ItemType) :
- cItemHandler(a_ItemType)
- {
- }
-
- virtual bool IsPlaceable(void) override
- {
- return true;
- }
-
- 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
- {
- a_BlockType = E_BLOCK_SUGARCANE;
- a_BlockMeta = 0;
- return true;
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemSugarcaneHandler :
+ public cItemHandler
+{
+public:
+ cItemSugarcaneHandler(int a_ItemType) :
+ cItemHandler(a_ItemType)
+ {
+ }
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
+ }
+
+ 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
+ {
+ a_BlockType = E_BLOCK_SUGARCANE;
+ a_BlockMeta = 0;
+ return true;
+ }
+} ;
+
+
+
+
diff --git a/source/Items/ItemSword.h b/source/Items/ItemSword.h
index 37b9ef32d..15351b55c 100644
--- a/source/Items/ItemSword.h
+++ b/source/Items/ItemSword.h
@@ -1,21 +1,21 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-#include "../World.h"
-#include "../Player.h"
-
-class cItemSwordHandler : public cItemHandler
-{
-public:
- cItemSwordHandler(int a_ItemType)
- : cItemHandler(a_ItemType)
- {
-
- }
-
- virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override
- {
- return (a_BlockType == E_BLOCK_COBWEB);
- }
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "../World.h"
+#include "../Player.h"
+
+class cItemSwordHandler : public cItemHandler
+{
+public:
+ cItemSwordHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+
+ }
+
+ virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override
+ {
+ return (a_BlockType == E_BLOCK_COBWEB);
+ }
}; \ No newline at end of file
diff --git a/source/Items/ItemWood.h b/source/Items/ItemWood.h
index 5304ec316..476256c5d 100644
--- a/source/Items/ItemWood.h
+++ b/source/Items/ItemWood.h
@@ -1,22 +1,22 @@
-
-#pragma once
-
-#include "ItemHandler.h"
-
-
-
-
-
-class cItemWoodHandler :
- public cItemHandler
-{
-public:
- cItemWoodHandler(int a_ItemType)
- : cItemHandler(a_ItemType)
- {
- }
-} ;
-
-
-
-
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemWoodHandler :
+ public cItemHandler
+{
+public:
+ cItemWoodHandler(int a_ItemType)
+ : cItemHandler(a_ItemType)
+ {
+ }
+} ;
+
+
+
+
diff --git a/source/LinearInterpolation.cpp b/source/LinearInterpolation.cpp
index d4536662e..d4975418b 100644
--- a/source/LinearInterpolation.cpp
+++ b/source/LinearInterpolation.cpp
@@ -1,251 +1,251 @@
-
-// LinearInterpolation.cpp
-
-// Implements methods for linear interpolation over 1D, 2D and 3D arrays
-
-#include "Globals.h"
-#include "LinearInterpolation.h"
-
-
-
-
-
-/*
-// Perform an automatic test upon program start (use breakpoints to debug):
-
-extern void Debug3DNoise(float * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase);
-
-class Test
-{
-public:
- Test(void)
- {
- // DoTest1();
- DoTest2();
- }
-
-
- void DoTest1(void)
- {
- float In[8] = {0, 1, 2, 3, 1, 2, 2, 2};
- float Out[3 * 3 * 3];
- LinearInterpolate1DArray(In, 4, Out, 9);
- LinearInterpolate2DArray(In, 2, 2, Out, 3, 3);
- LinearInterpolate3DArray(In, 2, 2, 2, Out, 3, 3, 3);
- LOGD("Out[0]: %f", Out[0]);
- }
-
-
- void DoTest2(void)
- {
- float In[3 * 3 * 3];
- for (int i = 0; i < ARRAYCOUNT(In); i++)
- {
- In[i] = (float)(i % 5);
- }
- float Out[15 * 16 * 17];
- LinearInterpolate3DArray(In, 3, 3, 3, Out, 15, 16, 17);
- Debug3DNoise(Out, 15, 16, 17, "LERP test");
- }
-} gTest;
-//*/
-
-
-
-
-
-// Puts linearly interpolated values from one array into another array. 1D version
-void LinearInterpolate1DArray(
- float * a_Src,
- int a_SrcSizeX,
- float * a_Dst,
- int a_DstSizeX
-)
-{
- a_Dst[0] = a_Src[0];
- int DstSizeXm1 = a_DstSizeX - 1;
- int SrcSizeXm1 = a_SrcSizeX - 1;
- float fDstSizeXm1 = (float)DstSizeXm1;
- float fSrcSizeXm1 = (float)SrcSizeXm1;
- for (int x = 1; x < DstSizeXm1; x++)
- {
- int SrcIdx = x * SrcSizeXm1 / DstSizeXm1;
- float ValLo = a_Src[SrcIdx];
- float ValHi = a_Src[SrcIdx + 1];
- float Ratio = (float)x * fSrcSizeXm1 / fDstSizeXm1 - SrcIdx;
- a_Dst[x] = ValLo + (ValHi - ValLo) * Ratio;
- }
- a_Dst[a_DstSizeX - 1] = a_Src[a_SrcSizeX - 1];
-}
-
-
-
-
-
-// Puts linearly interpolated values from one array into another array. 2D version
-void LinearInterpolate2DArray(
- float * a_Src,
- int a_SrcSizeX, int a_SrcSizeY,
- float * a_Dst,
- int a_DstSizeX, int a_DstSizeY
-)
-{
- ASSERT(a_DstSizeX > 0);
- ASSERT(a_DstSizeX < MAX_INTERPOL_SIZEX);
- ASSERT(a_DstSizeY > 0);
- ASSERT(a_DstSizeY < MAX_INTERPOL_SIZEY);
-
- // Calculate interpolation ratios and src indices along each axis:
- float RatioX[MAX_INTERPOL_SIZEX];
- float RatioY[MAX_INTERPOL_SIZEY];
- int SrcIdxX[MAX_INTERPOL_SIZEX];
- int SrcIdxY[MAX_INTERPOL_SIZEY];
- for (int x = 1; x < a_DstSizeX; x++)
- {
- SrcIdxX[x] = x * (a_SrcSizeX - 1) / (a_DstSizeX - 1);
- RatioX[x] = ((float)(x * (a_SrcSizeX - 1)) / (a_DstSizeX - 1)) - SrcIdxX[x];
- }
- for (int y = 1; y < a_DstSizeY; y++)
- {
- SrcIdxY[y] = y * (a_SrcSizeY - 1) / (a_DstSizeY - 1);
- RatioY[y] = ((float)(y * (a_SrcSizeY - 1)) / (a_DstSizeY - 1)) - SrcIdxY[y];
- }
-
- // Special values at the ends. Notice especially the last indices being (size - 2) with ratio set to 1, to avoid index overflow:
- SrcIdxX[0] = 0;
- RatioX[0] = 0;
- SrcIdxY[0] = 0;
- RatioY[0] = 0;
- SrcIdxX[a_DstSizeX - 1] = a_SrcSizeX - 2;
- RatioX[a_DstSizeX - 1] = 1;
- SrcIdxY[a_DstSizeY - 1] = a_SrcSizeY - 2;
- RatioY[a_DstSizeY - 1] = 1;
-
- // Output all the dst array values using the indices and ratios:
- int idx = 0;
- for (int y = 0; y < a_DstSizeY; y++)
- {
- int idxLoY = a_SrcSizeX * SrcIdxY[y];
- int idxHiY = a_SrcSizeX * (SrcIdxY[y] + 1);
- float ry = RatioY[y];
- for (int x = 0; x < a_DstSizeX; x++)
- {
- // The four src corners of the current "cell":
- float LoXLoY = a_Src[SrcIdxX[x] + idxLoY];
- float HiXLoY = a_Src[SrcIdxX[x] + 1 + idxLoY];
- float LoXHiY = a_Src[SrcIdxX[x] + idxHiY];
- float HiXHiY = a_Src[SrcIdxX[x] + 1 + idxHiY];
-
- // Linear interpolation along the X axis:
- float InterpXLoY = LoXLoY + (HiXLoY - LoXLoY) * RatioX[x];
- float InterpXHiY = LoXHiY + (HiXHiY - LoXHiY) * RatioX[x];
-
- // Linear interpolation along the Y axis:
- a_Dst[idx] = InterpXLoY + (InterpXHiY - InterpXLoY) * ry;
- idx += 1;
- }
- }
-}
-
-
-
-
-
-/// Puts linearly interpolated values from one array into another array. 3D version
-void LinearInterpolate3DArray(
- float * a_Src,
- int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ,
- float * a_Dst,
- int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ
-)
-{
- ASSERT(a_DstSizeX > 0);
- ASSERT(a_DstSizeX < MAX_INTERPOL_SIZEX);
- ASSERT(a_DstSizeY > 0);
- ASSERT(a_DstSizeY < MAX_INTERPOL_SIZEY);
- ASSERT(a_DstSizeZ > 0);
- ASSERT(a_DstSizeZ < MAX_INTERPOL_SIZEZ);
-
- // Calculate interpolation ratios and src indices along each axis:
- float RatioX[MAX_INTERPOL_SIZEX];
- float RatioY[MAX_INTERPOL_SIZEY];
- float RatioZ[MAX_INTERPOL_SIZEZ];
- int SrcIdxX[MAX_INTERPOL_SIZEX];
- int SrcIdxY[MAX_INTERPOL_SIZEY];
- int SrcIdxZ[MAX_INTERPOL_SIZEZ];
- for (int x = 1; x < a_DstSizeX; x++)
- {
- SrcIdxX[x] = x * (a_SrcSizeX - 1) / (a_DstSizeX - 1);
- RatioX[x] = ((float)(x * (a_SrcSizeX - 1)) / (a_DstSizeX - 1)) - SrcIdxX[x];
- }
- for (int y = 1; y < a_DstSizeY; y++)
- {
- SrcIdxY[y] = y * (a_SrcSizeY - 1) / (a_DstSizeY - 1);
- RatioY[y] = ((float)(y * (a_SrcSizeY - 1)) / (a_DstSizeY - 1)) - SrcIdxY[y];
- }
- for (int z = 1; z < a_DstSizeZ; z++)
- {
- SrcIdxZ[z] = z * (a_SrcSizeZ - 1) / (a_DstSizeZ - 1);
- RatioZ[z] = ((float)(z * (a_SrcSizeZ - 1)) / (a_DstSizeZ - 1)) - SrcIdxZ[z];
- }
-
- // Special values at the ends. Notice especially the last indices being (size - 2) with ratio set to 1, to avoid index overflow:
- SrcIdxX[0] = 0;
- RatioX[0] = 0;
- SrcIdxY[0] = 0;
- RatioY[0] = 0;
- SrcIdxZ[0] = 0;
- RatioZ[0] = 0;
- SrcIdxX[a_DstSizeX - 1] = a_SrcSizeX - 2;
- RatioX[a_DstSizeX - 1] = 1;
- SrcIdxY[a_DstSizeY - 1] = a_SrcSizeY - 2;
- RatioY[a_DstSizeY - 1] = 1;
- SrcIdxZ[a_DstSizeZ - 1] = a_SrcSizeZ - 2;
- RatioZ[a_DstSizeZ - 1] = 1;
-
- // Output all the dst array values using the indices and ratios:
- int idx = 0;
- for (int z = 0; z < a_DstSizeZ; z++)
- {
- int idxLoZ = a_SrcSizeX * a_SrcSizeY * SrcIdxZ[z];
- int idxHiZ = a_SrcSizeX * a_SrcSizeY * (SrcIdxZ[z] + 1);
- float rz = RatioZ[z];
- for (int y = 0; y < a_DstSizeY; y++)
- {
- int idxLoY = a_SrcSizeX * SrcIdxY[y];
- int idxHiY = a_SrcSizeX * (SrcIdxY[y] + 1);
- float ry = RatioY[y];
- for (int x = 0; x < a_DstSizeX; x++)
- {
- // The eight src corners of the current "cell":
- float LoXLoYLoZ = a_Src[SrcIdxX[x] + idxLoY + idxLoZ];
- float HiXLoYLoZ = a_Src[SrcIdxX[x] + 1 + idxLoY + idxLoZ];
- float LoXHiYLoZ = a_Src[SrcIdxX[x] + idxHiY + idxLoZ];
- float HiXHiYLoZ = a_Src[SrcIdxX[x] + 1 + idxHiY + idxLoZ];
- float LoXLoYHiZ = a_Src[SrcIdxX[x] + idxLoY + idxHiZ];
- float HiXLoYHiZ = a_Src[SrcIdxX[x] + 1 + idxLoY + idxHiZ];
- float LoXHiYHiZ = a_Src[SrcIdxX[x] + idxHiY + idxHiZ];
- float HiXHiYHiZ = a_Src[SrcIdxX[x] + 1 + idxHiY + idxHiZ];
-
- // Linear interpolation along the Z axis:
- float LoXLoYInZ = LoXLoYLoZ + (LoXLoYHiZ - LoXLoYLoZ) * rz;
- float HiXLoYInZ = HiXLoYLoZ + (HiXLoYHiZ - HiXLoYLoZ) * rz;
- float LoXHiYInZ = LoXHiYLoZ + (LoXHiYHiZ - LoXHiYLoZ) * rz;
- float HiXHiYInZ = HiXHiYLoZ + (HiXHiYHiZ - HiXHiYLoZ) * rz;
-
- // Linear interpolation along the Y axis:
- float LoXInYInZ = LoXLoYInZ + (LoXHiYInZ - LoXLoYInZ) * ry;
- float HiXInYInZ = HiXLoYInZ + (HiXHiYInZ - HiXLoYInZ) * ry;
-
- // Linear interpolation along the X axis:
- a_Dst[idx] = LoXInYInZ + (HiXInYInZ - LoXInYInZ) * RatioX[x];
- idx += 1;
- } // for x
- } // for y
- } // for z
-}
-
-
-
-
-
+
+// LinearInterpolation.cpp
+
+// Implements methods for linear interpolation over 1D, 2D and 3D arrays
+
+#include "Globals.h"
+#include "LinearInterpolation.h"
+
+
+
+
+
+/*
+// Perform an automatic test upon program start (use breakpoints to debug):
+
+extern void Debug3DNoise(float * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase);
+
+class Test
+{
+public:
+ Test(void)
+ {
+ // DoTest1();
+ DoTest2();
+ }
+
+
+ void DoTest1(void)
+ {
+ float In[8] = {0, 1, 2, 3, 1, 2, 2, 2};
+ float Out[3 * 3 * 3];
+ LinearInterpolate1DArray(In, 4, Out, 9);
+ LinearInterpolate2DArray(In, 2, 2, Out, 3, 3);
+ LinearInterpolate3DArray(In, 2, 2, 2, Out, 3, 3, 3);
+ LOGD("Out[0]: %f", Out[0]);
+ }
+
+
+ void DoTest2(void)
+ {
+ float In[3 * 3 * 3];
+ for (int i = 0; i < ARRAYCOUNT(In); i++)
+ {
+ In[i] = (float)(i % 5);
+ }
+ float Out[15 * 16 * 17];
+ LinearInterpolate3DArray(In, 3, 3, 3, Out, 15, 16, 17);
+ Debug3DNoise(Out, 15, 16, 17, "LERP test");
+ }
+} gTest;
+//*/
+
+
+
+
+
+// Puts linearly interpolated values from one array into another array. 1D version
+void LinearInterpolate1DArray(
+ float * a_Src,
+ int a_SrcSizeX,
+ float * a_Dst,
+ int a_DstSizeX
+)
+{
+ a_Dst[0] = a_Src[0];
+ int DstSizeXm1 = a_DstSizeX - 1;
+ int SrcSizeXm1 = a_SrcSizeX - 1;
+ float fDstSizeXm1 = (float)DstSizeXm1;
+ float fSrcSizeXm1 = (float)SrcSizeXm1;
+ for (int x = 1; x < DstSizeXm1; x++)
+ {
+ int SrcIdx = x * SrcSizeXm1 / DstSizeXm1;
+ float ValLo = a_Src[SrcIdx];
+ float ValHi = a_Src[SrcIdx + 1];
+ float Ratio = (float)x * fSrcSizeXm1 / fDstSizeXm1 - SrcIdx;
+ a_Dst[x] = ValLo + (ValHi - ValLo) * Ratio;
+ }
+ a_Dst[a_DstSizeX - 1] = a_Src[a_SrcSizeX - 1];
+}
+
+
+
+
+
+// Puts linearly interpolated values from one array into another array. 2D version
+void LinearInterpolate2DArray(
+ float * a_Src,
+ int a_SrcSizeX, int a_SrcSizeY,
+ float * a_Dst,
+ int a_DstSizeX, int a_DstSizeY
+)
+{
+ ASSERT(a_DstSizeX > 0);
+ ASSERT(a_DstSizeX < MAX_INTERPOL_SIZEX);
+ ASSERT(a_DstSizeY > 0);
+ ASSERT(a_DstSizeY < MAX_INTERPOL_SIZEY);
+
+ // Calculate interpolation ratios and src indices along each axis:
+ float RatioX[MAX_INTERPOL_SIZEX];
+ float RatioY[MAX_INTERPOL_SIZEY];
+ int SrcIdxX[MAX_INTERPOL_SIZEX];
+ int SrcIdxY[MAX_INTERPOL_SIZEY];
+ for (int x = 1; x < a_DstSizeX; x++)
+ {
+ SrcIdxX[x] = x * (a_SrcSizeX - 1) / (a_DstSizeX - 1);
+ RatioX[x] = ((float)(x * (a_SrcSizeX - 1)) / (a_DstSizeX - 1)) - SrcIdxX[x];
+ }
+ for (int y = 1; y < a_DstSizeY; y++)
+ {
+ SrcIdxY[y] = y * (a_SrcSizeY - 1) / (a_DstSizeY - 1);
+ RatioY[y] = ((float)(y * (a_SrcSizeY - 1)) / (a_DstSizeY - 1)) - SrcIdxY[y];
+ }
+
+ // Special values at the ends. Notice especially the last indices being (size - 2) with ratio set to 1, to avoid index overflow:
+ SrcIdxX[0] = 0;
+ RatioX[0] = 0;
+ SrcIdxY[0] = 0;
+ RatioY[0] = 0;
+ SrcIdxX[a_DstSizeX - 1] = a_SrcSizeX - 2;
+ RatioX[a_DstSizeX - 1] = 1;
+ SrcIdxY[a_DstSizeY - 1] = a_SrcSizeY - 2;
+ RatioY[a_DstSizeY - 1] = 1;
+
+ // Output all the dst array values using the indices and ratios:
+ int idx = 0;
+ for (int y = 0; y < a_DstSizeY; y++)
+ {
+ int idxLoY = a_SrcSizeX * SrcIdxY[y];
+ int idxHiY = a_SrcSizeX * (SrcIdxY[y] + 1);
+ float ry = RatioY[y];
+ for (int x = 0; x < a_DstSizeX; x++)
+ {
+ // The four src corners of the current "cell":
+ float LoXLoY = a_Src[SrcIdxX[x] + idxLoY];
+ float HiXLoY = a_Src[SrcIdxX[x] + 1 + idxLoY];
+ float LoXHiY = a_Src[SrcIdxX[x] + idxHiY];
+ float HiXHiY = a_Src[SrcIdxX[x] + 1 + idxHiY];
+
+ // Linear interpolation along the X axis:
+ float InterpXLoY = LoXLoY + (HiXLoY - LoXLoY) * RatioX[x];
+ float InterpXHiY = LoXHiY + (HiXHiY - LoXHiY) * RatioX[x];
+
+ // Linear interpolation along the Y axis:
+ a_Dst[idx] = InterpXLoY + (InterpXHiY - InterpXLoY) * ry;
+ idx += 1;
+ }
+ }
+}
+
+
+
+
+
+/// Puts linearly interpolated values from one array into another array. 3D version
+void LinearInterpolate3DArray(
+ float * a_Src,
+ int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ,
+ float * a_Dst,
+ int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ
+)
+{
+ ASSERT(a_DstSizeX > 0);
+ ASSERT(a_DstSizeX < MAX_INTERPOL_SIZEX);
+ ASSERT(a_DstSizeY > 0);
+ ASSERT(a_DstSizeY < MAX_INTERPOL_SIZEY);
+ ASSERT(a_DstSizeZ > 0);
+ ASSERT(a_DstSizeZ < MAX_INTERPOL_SIZEZ);
+
+ // Calculate interpolation ratios and src indices along each axis:
+ float RatioX[MAX_INTERPOL_SIZEX];
+ float RatioY[MAX_INTERPOL_SIZEY];
+ float RatioZ[MAX_INTERPOL_SIZEZ];
+ int SrcIdxX[MAX_INTERPOL_SIZEX];
+ int SrcIdxY[MAX_INTERPOL_SIZEY];
+ int SrcIdxZ[MAX_INTERPOL_SIZEZ];
+ for (int x = 1; x < a_DstSizeX; x++)
+ {
+ SrcIdxX[x] = x * (a_SrcSizeX - 1) / (a_DstSizeX - 1);
+ RatioX[x] = ((float)(x * (a_SrcSizeX - 1)) / (a_DstSizeX - 1)) - SrcIdxX[x];
+ }
+ for (int y = 1; y < a_DstSizeY; y++)
+ {
+ SrcIdxY[y] = y * (a_SrcSizeY - 1) / (a_DstSizeY - 1);
+ RatioY[y] = ((float)(y * (a_SrcSizeY - 1)) / (a_DstSizeY - 1)) - SrcIdxY[y];
+ }
+ for (int z = 1; z < a_DstSizeZ; z++)
+ {
+ SrcIdxZ[z] = z * (a_SrcSizeZ - 1) / (a_DstSizeZ - 1);
+ RatioZ[z] = ((float)(z * (a_SrcSizeZ - 1)) / (a_DstSizeZ - 1)) - SrcIdxZ[z];
+ }
+
+ // Special values at the ends. Notice especially the last indices being (size - 2) with ratio set to 1, to avoid index overflow:
+ SrcIdxX[0] = 0;
+ RatioX[0] = 0;
+ SrcIdxY[0] = 0;
+ RatioY[0] = 0;
+ SrcIdxZ[0] = 0;
+ RatioZ[0] = 0;
+ SrcIdxX[a_DstSizeX - 1] = a_SrcSizeX - 2;
+ RatioX[a_DstSizeX - 1] = 1;
+ SrcIdxY[a_DstSizeY - 1] = a_SrcSizeY - 2;
+ RatioY[a_DstSizeY - 1] = 1;
+ SrcIdxZ[a_DstSizeZ - 1] = a_SrcSizeZ - 2;
+ RatioZ[a_DstSizeZ - 1] = 1;
+
+ // Output all the dst array values using the indices and ratios:
+ int idx = 0;
+ for (int z = 0; z < a_DstSizeZ; z++)
+ {
+ int idxLoZ = a_SrcSizeX * a_SrcSizeY * SrcIdxZ[z];
+ int idxHiZ = a_SrcSizeX * a_SrcSizeY * (SrcIdxZ[z] + 1);
+ float rz = RatioZ[z];
+ for (int y = 0; y < a_DstSizeY; y++)
+ {
+ int idxLoY = a_SrcSizeX * SrcIdxY[y];
+ int idxHiY = a_SrcSizeX * (SrcIdxY[y] + 1);
+ float ry = RatioY[y];
+ for (int x = 0; x < a_DstSizeX; x++)
+ {
+ // The eight src corners of the current "cell":
+ float LoXLoYLoZ = a_Src[SrcIdxX[x] + idxLoY + idxLoZ];
+ float HiXLoYLoZ = a_Src[SrcIdxX[x] + 1 + idxLoY + idxLoZ];
+ float LoXHiYLoZ = a_Src[SrcIdxX[x] + idxHiY + idxLoZ];
+ float HiXHiYLoZ = a_Src[SrcIdxX[x] + 1 + idxHiY + idxLoZ];
+ float LoXLoYHiZ = a_Src[SrcIdxX[x] + idxLoY + idxHiZ];
+ float HiXLoYHiZ = a_Src[SrcIdxX[x] + 1 + idxLoY + idxHiZ];
+ float LoXHiYHiZ = a_Src[SrcIdxX[x] + idxHiY + idxHiZ];
+ float HiXHiYHiZ = a_Src[SrcIdxX[x] + 1 + idxHiY + idxHiZ];
+
+ // Linear interpolation along the Z axis:
+ float LoXLoYInZ = LoXLoYLoZ + (LoXLoYHiZ - LoXLoYLoZ) * rz;
+ float HiXLoYInZ = HiXLoYLoZ + (HiXLoYHiZ - HiXLoYLoZ) * rz;
+ float LoXHiYInZ = LoXHiYLoZ + (LoXHiYHiZ - LoXHiYLoZ) * rz;
+ float HiXHiYInZ = HiXHiYLoZ + (HiXHiYHiZ - HiXHiYLoZ) * rz;
+
+ // Linear interpolation along the Y axis:
+ float LoXInYInZ = LoXLoYInZ + (LoXHiYInZ - LoXLoYInZ) * ry;
+ float HiXInYInZ = HiXLoYInZ + (HiXHiYInZ - HiXLoYInZ) * ry;
+
+ // Linear interpolation along the X axis:
+ a_Dst[idx] = LoXInYInZ + (HiXInYInZ - LoXInYInZ) * RatioX[x];
+ idx += 1;
+ } // for x
+ } // for y
+ } // for z
+}
+
+
+
+
+
diff --git a/source/LinearInterpolation.h b/source/LinearInterpolation.h
index ea9e93455..4b798d9bc 100644
--- a/source/LinearInterpolation.h
+++ b/source/LinearInterpolation.h
@@ -1,60 +1,60 @@
-
-// LinearInterpolation.h
-
-// Declares methods for linear interpolation over 1D, 2D and 3D arrays
-
-
-
-
-
-#pragma once
-
-
-
-
-
-// 2D and 3D Interpolation is optimized by precalculating the ratios into static-sized arrays
-// These arrays enforce a max size of the dest array, but the limits are settable here:
-const int MAX_INTERPOL_SIZEX = 256; ///< Maximum X-size of the interpolated array
-const int MAX_INTERPOL_SIZEY = 512; ///< Maximum Y-size of the interpolated array
-const int MAX_INTERPOL_SIZEZ = 256; ///< Maximum Z-size of the interpolated array
-
-
-
-
-
-/// Puts linearly interpolated values from one array into another array. 1D version
-void LinearInterpolate1DArray(
- float * a_Src, ///< Src array
- int a_SrcSizeX, ///< Count of the src array
- float * a_Dst, ///< Src array
- int a_DstSizeX ///< Count of the dst array
-);
-
-
-
-
-
-/// Puts linearly interpolated values from one array into another array. 2D version
-void LinearInterpolate2DArray(
- float * a_Src, ///< Src array, [x + a_SrcSizeX * y]
- int a_SrcSizeX, int a_SrcSizeY, ///< Count of the src array, in each direction
- float * a_Dst, ///< Dst array, [x + a_DstSizeX * y]
- int a_DstSizeX, int a_DstSizeY ///< Count of the dst array, in each direction
-);
-
-
-
-
-
-/// Puts linearly interpolated values from one array into another array. 3D version
-void LinearInterpolate3DArray(
- float * a_Src, ///< Src array, [x + a_SrcSizeX * y + a_SrcSizeX * a_SrcSizeY * z]
- int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, ///< Count of the src array, in each direction
- float * a_Dst, ///< Dst array, [x + a_DstSizeX * y + a_DstSizeX * a_DstSizeY * z]
- int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ ///< Count of the dst array, in each direction
-);
-
-
-
-
+
+// LinearInterpolation.h
+
+// Declares methods for linear interpolation over 1D, 2D and 3D arrays
+
+
+
+
+
+#pragma once
+
+
+
+
+
+// 2D and 3D Interpolation is optimized by precalculating the ratios into static-sized arrays
+// These arrays enforce a max size of the dest array, but the limits are settable here:
+const int MAX_INTERPOL_SIZEX = 256; ///< Maximum X-size of the interpolated array
+const int MAX_INTERPOL_SIZEY = 512; ///< Maximum Y-size of the interpolated array
+const int MAX_INTERPOL_SIZEZ = 256; ///< Maximum Z-size of the interpolated array
+
+
+
+
+
+/// Puts linearly interpolated values from one array into another array. 1D version
+void LinearInterpolate1DArray(
+ float * a_Src, ///< Src array
+ int a_SrcSizeX, ///< Count of the src array
+ float * a_Dst, ///< Src array
+ int a_DstSizeX ///< Count of the dst array
+);
+
+
+
+
+
+/// Puts linearly interpolated values from one array into another array. 2D version
+void LinearInterpolate2DArray(
+ float * a_Src, ///< Src array, [x + a_SrcSizeX * y]
+ int a_SrcSizeX, int a_SrcSizeY, ///< Count of the src array, in each direction
+ float * a_Dst, ///< Dst array, [x + a_DstSizeX * y]
+ int a_DstSizeX, int a_DstSizeY ///< Count of the dst array, in each direction
+);
+
+
+
+
+
+/// Puts linearly interpolated values from one array into another array. 3D version
+void LinearInterpolate3DArray(
+ float * a_Src, ///< Src array, [x + a_SrcSizeX * y + a_SrcSizeX * a_SrcSizeY * z]
+ int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, ///< Count of the src array, in each direction
+ float * a_Dst, ///< Dst array, [x + a_DstSizeX * y + a_DstSizeX * a_DstSizeY * z]
+ int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ ///< Count of the dst array, in each direction
+);
+
+
+
+
diff --git a/source/LinearUpscale.h b/source/LinearUpscale.h
index 9f40b5d81..b7ac84c6a 100644
--- a/source/LinearUpscale.h
+++ b/source/LinearUpscale.h
@@ -1,244 +1,244 @@
-
-// LinearUpscale.h
-
-// Declares the functions for linearly upscaling arrays
-
-/*
-Upscaling means that the array is divided into same-size "cells", and each cell is
-linearly interpolated between its corners. The array's dimensions are therefore
-1 + CellSize * NumCells, for each direction.
-
-Upscaling is more efficient than linear interpolation, because the cell sizes are integral
-and therefore the cells' boundaries are on the array points.
-
-However, upscaling usually requires generating the "1 +" in each direction.
-
-Upscaling is implemented in templates, so that it's compatible with multiple datatypes.
-Therefore, there is no cpp file.
-
-InPlace upscaling works on a single array and assumes that the values to work on have already
-been interspersed into the array to the cell boundaries.
-Specifically, a_Array[x * a_AnchorStepX + y * a_AnchorStepY] contains the anchor value.
-
-Regular upscaling takes two arrays and "moves" the input from src to dst; src is expected packed.
-*/
-
-
-
-
-/**
-Linearly interpolates values in the array between the equidistant anchor points (upscales).
-Works in-place (input is already present at the correct output coords)
-*/
-template<typename TYPE> void LinearUpscale2DArrayInPlace(
- TYPE * a_Array,
- int a_SizeX, int a_SizeY, // Dimensions of the array
- int a_AnchorStepX, int a_AnchorStepY // Distances between the anchor points in each direction
-)
-{
- // First interpolate columns where the anchor points are:
- int LastYCell = a_SizeY - a_AnchorStepY;
- for (int y = 0; y < LastYCell; y += a_AnchorStepY)
- {
- int Idx = a_SizeX * y;
- for (int x = 0; x < a_SizeX; x += a_AnchorStepX)
- {
- TYPE StartValue = a_Array[Idx];
- TYPE EndValue = a_Array[Idx + a_SizeX * a_AnchorStepY];
- TYPE Diff = EndValue - StartValue;
- for (int CellY = 1; CellY < a_AnchorStepY; CellY++)
- {
- a_Array[Idx + a_SizeX * CellY] = StartValue + Diff * CellY / a_AnchorStepY;
- } // for CellY
- Idx += a_AnchorStepX;
- } // for x
- } // for y
-
- // Now interpolate in rows, each row has values in the anchor columns
- int LastXCell = a_SizeX - a_AnchorStepX;
- for (int y = 0; y < a_SizeY; y++)
- {
- int Idx = a_SizeX * y;
- for (int x = 0; x < LastXCell; x += a_AnchorStepX)
- {
- TYPE StartValue = a_Array[Idx];
- TYPE EndValue = a_Array[Idx + a_AnchorStepX];
- TYPE Diff = EndValue - StartValue;
- for (int CellX = 1; CellX < a_AnchorStepX; CellX++)
- {
- a_Array[Idx + CellX] = StartValue + CellX * Diff / a_AnchorStepX;
- } // for CellY
- Idx += a_AnchorStepX;
- }
- }
-}
-
-
-
-
-
-/**
-Linearly interpolates values in the array between the equidistant anchor points (upscales).
-Works on two arrays, input is packed and output is to be completely constructed.
-*/
-template<typename TYPE> void LinearUpscale2DArray(
- TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY
- int a_SrcSizeX, int a_SrcSizeY, ///< Dimensions of the src array
- TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1)
- int a_UpscaleX, int a_UpscaleY ///< Upscale factor for each direction
-)
-{
- // For optimization reasons, we're storing the upscaling ratios in a fixed-size arrays of these sizes
- // Feel free to enlarge them if needed, but keep in mind that they're on the stack
- const int MAX_UPSCALE_X = 128;
- const int MAX_UPSCALE_Y = 128;
-
- ASSERT(a_Src != NULL);
- ASSERT(a_Dst != NULL);
- ASSERT(a_SrcSizeX > 0);
- ASSERT(a_SrcSizeY > 0);
- ASSERT(a_UpscaleX > 0);
- ASSERT(a_UpscaleY > 0);
- ASSERT(a_UpscaleX <= MAX_UPSCALE_X);
- ASSERT(a_UpscaleY <= MAX_UPSCALE_Y);
-
- // Pre-calculate the upscaling ratios:
- TYPE RatioX[MAX_UPSCALE_X];
- TYPE RatioY[MAX_UPSCALE_Y];
- for (int x = 0; x <= a_UpscaleX; x++)
- {
- RatioX[x] = (TYPE)x / a_UpscaleX;
- }
- for (int y = 0; y <= a_UpscaleY; y++)
- {
- RatioY[y] = (TYPE)y / a_UpscaleY;
- }
-
- // Interpolate each XY cell:
- int DstSizeX = (a_SrcSizeX - 1) * a_UpscaleX + 1;
- int DstSizeY = (a_SrcSizeY - 1) * a_UpscaleY + 1;
- for (int y = 0; y < (a_SrcSizeY - 1); y++)
- {
- int DstY = y * a_UpscaleY;
- int idx = y * a_SrcSizeX;
- for (int x = 0; x < (a_SrcSizeX - 1); x++, idx++)
- {
- int DstX = x * a_UpscaleX;
- TYPE LoXLoY = a_Src[idx];
- TYPE LoXHiY = a_Src[idx + a_SrcSizeX];
- TYPE HiXLoY = a_Src[idx + 1];
- TYPE HiXHiY = a_Src[idx + 1 + a_SrcSizeX];
- for (int CellY = 0; CellY <= a_UpscaleY; CellY++)
- {
- int DestIdx = (DstY + CellY) * DstSizeX + DstX;
- ASSERT(DestIdx + a_UpscaleX < DstSizeX * DstSizeY);
- TYPE LoXInY = LoXLoY + (LoXHiY - LoXLoY) * RatioY[CellY];
- TYPE HiXInY = HiXLoY + (HiXHiY - HiXLoY) * RatioY[CellY];
- for (int CellX = 0; CellX <= a_UpscaleX; CellX++, DestIdx++)
- {
- a_Dst[DestIdx] = LoXInY + (HiXInY - LoXInY) * RatioX[CellX];
- }
- } // for CellY
- } // for x
- } // for y
-}
-
-
-
-
-
-/**
-Linearly interpolates values in the array between the equidistant anchor points (upscales).
-Works on two arrays, input is packed and output is to be completely constructed.
-*/
-template<typename TYPE> void LinearUpscale3DArray(
- TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY x a_SrcSizeZ
- int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, ///< Dimensions of the src array
- TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1) x (a_SrcSizeZ * a_UpscaleZ + 1)
- int a_UpscaleX, int a_UpscaleY, int a_UpscaleZ ///< Upscale factor for each direction
-)
-{
- // For optimization reasons, we're storing the upscaling ratios in a fixed-size arrays of these sizes
- // Feel free to enlarge them if needed, but keep in mind that they're on the stack
- const int MAX_UPSCALE_X = 128;
- const int MAX_UPSCALE_Y = 128;
- const int MAX_UPSCALE_Z = 128;
-
- ASSERT(a_Src != NULL);
- ASSERT(a_Dst != NULL);
- ASSERT(a_SrcSizeX > 0);
- ASSERT(a_SrcSizeY > 0);
- ASSERT(a_SrcSizeZ > 0);
- ASSERT(a_UpscaleX > 0);
- ASSERT(a_UpscaleY > 0);
- ASSERT(a_UpscaleZ > 0);
- ASSERT(a_UpscaleX <= MAX_UPSCALE_X);
- ASSERT(a_UpscaleY <= MAX_UPSCALE_Y);
- ASSERT(a_UpscaleZ <= MAX_UPSCALE_Z);
-
- // Pre-calculate the upscaling ratios:
- TYPE RatioX[MAX_UPSCALE_X];
- TYPE RatioY[MAX_UPSCALE_Y];
- TYPE RatioZ[MAX_UPSCALE_Y];
- for (int x = 0; x <= a_UpscaleX; x++)
- {
- RatioX[x] = (TYPE)x / a_UpscaleX;
- }
- for (int y = 0; y <= a_UpscaleY; y++)
- {
- RatioY[y] = (TYPE)y / a_UpscaleY;
- }
- for (int z = 0; z <= a_UpscaleZ; z++)
- {
- RatioZ[z] = (TYPE)z / a_UpscaleZ;
- }
-
- // Interpolate each XYZ cell:
- int DstSizeX = (a_SrcSizeX - 1) * a_UpscaleX + 1;
- int DstSizeY = (a_SrcSizeY - 1) * a_UpscaleY + 1;
- int DstSizeZ = (a_SrcSizeZ - 1) * a_UpscaleZ + 1;
- for (int z = 0; z < (a_SrcSizeZ - 1); z++)
- {
- int DstZ = z * a_UpscaleZ;
- for (int y = 0; y < (a_SrcSizeY - 1); y++)
- {
- int DstY = y * a_UpscaleY;
- int idx = y * a_SrcSizeX + z * a_SrcSizeX * a_SrcSizeY;
- for (int x = 0; x < (a_SrcSizeX - 1); x++, idx++)
- {
- int DstX = x * a_UpscaleX;
- TYPE LoXLoYLoZ = a_Src[idx];
- TYPE LoXLoYHiZ = a_Src[idx + a_SrcSizeX * a_SrcSizeY];
- TYPE LoXHiYLoZ = a_Src[idx + a_SrcSizeX];
- TYPE LoXHiYHiZ = a_Src[idx + a_SrcSizeX + a_SrcSizeX * a_SrcSizeY];
- TYPE HiXLoYLoZ = a_Src[idx + 1];
- TYPE HiXLoYHiZ = a_Src[idx + 1 + a_SrcSizeX * a_SrcSizeY];
- TYPE HiXHiYLoZ = a_Src[idx + 1 + a_SrcSizeX];
- TYPE HiXHiYHiZ = a_Src[idx + 1 + a_SrcSizeX + a_SrcSizeX * a_SrcSizeY];
- for (int CellZ = 0; CellZ <= a_UpscaleZ; CellZ++)
- {
- TYPE LoXLoYInZ = LoXLoYLoZ + (LoXLoYHiZ - LoXLoYLoZ) * RatioZ[CellZ];
- TYPE LoXHiYInZ = LoXHiYLoZ + (LoXHiYHiZ - LoXHiYLoZ) * RatioZ[CellZ];
- TYPE HiXLoYInZ = HiXLoYLoZ + (HiXLoYHiZ - HiXLoYLoZ) * RatioZ[CellZ];
- TYPE HiXHiYInZ = HiXHiYLoZ + (HiXHiYHiZ - HiXHiYLoZ) * RatioZ[CellZ];
- for (int CellY = 0; CellY <= a_UpscaleY; CellY++)
- {
- int DestIdx = (DstZ + CellZ) * DstSizeX * DstSizeY + (DstY + CellY) * DstSizeX + DstX;
- ASSERT(DestIdx + a_UpscaleX < DstSizeX * DstSizeY * DstSizeZ);
- TYPE LoXInY = LoXLoYInZ + (LoXHiYInZ - LoXLoYInZ) * RatioY[CellY];
- TYPE HiXInY = HiXLoYInZ + (HiXHiYInZ - HiXLoYInZ) * RatioY[CellY];
- for (int CellX = 0; CellX <= a_UpscaleX; CellX++, DestIdx++)
- {
- a_Dst[DestIdx] = LoXInY + (HiXInY - LoXInY) * RatioX[CellX];
- }
- } // for CellY
- } // for CellZ
- } // for x
- } // for y
- } // for z
-}
-
-
-
-
-
+
+// LinearUpscale.h
+
+// Declares the functions for linearly upscaling arrays
+
+/*
+Upscaling means that the array is divided into same-size "cells", and each cell is
+linearly interpolated between its corners. The array's dimensions are therefore
+1 + CellSize * NumCells, for each direction.
+
+Upscaling is more efficient than linear interpolation, because the cell sizes are integral
+and therefore the cells' boundaries are on the array points.
+
+However, upscaling usually requires generating the "1 +" in each direction.
+
+Upscaling is implemented in templates, so that it's compatible with multiple datatypes.
+Therefore, there is no cpp file.
+
+InPlace upscaling works on a single array and assumes that the values to work on have already
+been interspersed into the array to the cell boundaries.
+Specifically, a_Array[x * a_AnchorStepX + y * a_AnchorStepY] contains the anchor value.
+
+Regular upscaling takes two arrays and "moves" the input from src to dst; src is expected packed.
+*/
+
+
+
+
+/**
+Linearly interpolates values in the array between the equidistant anchor points (upscales).
+Works in-place (input is already present at the correct output coords)
+*/
+template<typename TYPE> void LinearUpscale2DArrayInPlace(
+ TYPE * a_Array,
+ int a_SizeX, int a_SizeY, // Dimensions of the array
+ int a_AnchorStepX, int a_AnchorStepY // Distances between the anchor points in each direction
+)
+{
+ // First interpolate columns where the anchor points are:
+ int LastYCell = a_SizeY - a_AnchorStepY;
+ for (int y = 0; y < LastYCell; y += a_AnchorStepY)
+ {
+ int Idx = a_SizeX * y;
+ for (int x = 0; x < a_SizeX; x += a_AnchorStepX)
+ {
+ TYPE StartValue = a_Array[Idx];
+ TYPE EndValue = a_Array[Idx + a_SizeX * a_AnchorStepY];
+ TYPE Diff = EndValue - StartValue;
+ for (int CellY = 1; CellY < a_AnchorStepY; CellY++)
+ {
+ a_Array[Idx + a_SizeX * CellY] = StartValue + Diff * CellY / a_AnchorStepY;
+ } // for CellY
+ Idx += a_AnchorStepX;
+ } // for x
+ } // for y
+
+ // Now interpolate in rows, each row has values in the anchor columns
+ int LastXCell = a_SizeX - a_AnchorStepX;
+ for (int y = 0; y < a_SizeY; y++)
+ {
+ int Idx = a_SizeX * y;
+ for (int x = 0; x < LastXCell; x += a_AnchorStepX)
+ {
+ TYPE StartValue = a_Array[Idx];
+ TYPE EndValue = a_Array[Idx + a_AnchorStepX];
+ TYPE Diff = EndValue - StartValue;
+ for (int CellX = 1; CellX < a_AnchorStepX; CellX++)
+ {
+ a_Array[Idx + CellX] = StartValue + CellX * Diff / a_AnchorStepX;
+ } // for CellY
+ Idx += a_AnchorStepX;
+ }
+ }
+}
+
+
+
+
+
+/**
+Linearly interpolates values in the array between the equidistant anchor points (upscales).
+Works on two arrays, input is packed and output is to be completely constructed.
+*/
+template<typename TYPE> void LinearUpscale2DArray(
+ TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY
+ int a_SrcSizeX, int a_SrcSizeY, ///< Dimensions of the src array
+ TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1)
+ int a_UpscaleX, int a_UpscaleY ///< Upscale factor for each direction
+)
+{
+ // For optimization reasons, we're storing the upscaling ratios in a fixed-size arrays of these sizes
+ // Feel free to enlarge them if needed, but keep in mind that they're on the stack
+ const int MAX_UPSCALE_X = 128;
+ const int MAX_UPSCALE_Y = 128;
+
+ ASSERT(a_Src != NULL);
+ ASSERT(a_Dst != NULL);
+ ASSERT(a_SrcSizeX > 0);
+ ASSERT(a_SrcSizeY > 0);
+ ASSERT(a_UpscaleX > 0);
+ ASSERT(a_UpscaleY > 0);
+ ASSERT(a_UpscaleX <= MAX_UPSCALE_X);
+ ASSERT(a_UpscaleY <= MAX_UPSCALE_Y);
+
+ // Pre-calculate the upscaling ratios:
+ TYPE RatioX[MAX_UPSCALE_X];
+ TYPE RatioY[MAX_UPSCALE_Y];
+ for (int x = 0; x <= a_UpscaleX; x++)
+ {
+ RatioX[x] = (TYPE)x / a_UpscaleX;
+ }
+ for (int y = 0; y <= a_UpscaleY; y++)
+ {
+ RatioY[y] = (TYPE)y / a_UpscaleY;
+ }
+
+ // Interpolate each XY cell:
+ int DstSizeX = (a_SrcSizeX - 1) * a_UpscaleX + 1;
+ int DstSizeY = (a_SrcSizeY - 1) * a_UpscaleY + 1;
+ for (int y = 0; y < (a_SrcSizeY - 1); y++)
+ {
+ int DstY = y * a_UpscaleY;
+ int idx = y * a_SrcSizeX;
+ for (int x = 0; x < (a_SrcSizeX - 1); x++, idx++)
+ {
+ int DstX = x * a_UpscaleX;
+ TYPE LoXLoY = a_Src[idx];
+ TYPE LoXHiY = a_Src[idx + a_SrcSizeX];
+ TYPE HiXLoY = a_Src[idx + 1];
+ TYPE HiXHiY = a_Src[idx + 1 + a_SrcSizeX];
+ for (int CellY = 0; CellY <= a_UpscaleY; CellY++)
+ {
+ int DestIdx = (DstY + CellY) * DstSizeX + DstX;
+ ASSERT(DestIdx + a_UpscaleX < DstSizeX * DstSizeY);
+ TYPE LoXInY = LoXLoY + (LoXHiY - LoXLoY) * RatioY[CellY];
+ TYPE HiXInY = HiXLoY + (HiXHiY - HiXLoY) * RatioY[CellY];
+ for (int CellX = 0; CellX <= a_UpscaleX; CellX++, DestIdx++)
+ {
+ a_Dst[DestIdx] = LoXInY + (HiXInY - LoXInY) * RatioX[CellX];
+ }
+ } // for CellY
+ } // for x
+ } // for y
+}
+
+
+
+
+
+/**
+Linearly interpolates values in the array between the equidistant anchor points (upscales).
+Works on two arrays, input is packed and output is to be completely constructed.
+*/
+template<typename TYPE> void LinearUpscale3DArray(
+ TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY x a_SrcSizeZ
+ int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, ///< Dimensions of the src array
+ TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1) x (a_SrcSizeZ * a_UpscaleZ + 1)
+ int a_UpscaleX, int a_UpscaleY, int a_UpscaleZ ///< Upscale factor for each direction
+)
+{
+ // For optimization reasons, we're storing the upscaling ratios in a fixed-size arrays of these sizes
+ // Feel free to enlarge them if needed, but keep in mind that they're on the stack
+ const int MAX_UPSCALE_X = 128;
+ const int MAX_UPSCALE_Y = 128;
+ const int MAX_UPSCALE_Z = 128;
+
+ ASSERT(a_Src != NULL);
+ ASSERT(a_Dst != NULL);
+ ASSERT(a_SrcSizeX > 0);
+ ASSERT(a_SrcSizeY > 0);
+ ASSERT(a_SrcSizeZ > 0);
+ ASSERT(a_UpscaleX > 0);
+ ASSERT(a_UpscaleY > 0);
+ ASSERT(a_UpscaleZ > 0);
+ ASSERT(a_UpscaleX <= MAX_UPSCALE_X);
+ ASSERT(a_UpscaleY <= MAX_UPSCALE_Y);
+ ASSERT(a_UpscaleZ <= MAX_UPSCALE_Z);
+
+ // Pre-calculate the upscaling ratios:
+ TYPE RatioX[MAX_UPSCALE_X];
+ TYPE RatioY[MAX_UPSCALE_Y];
+ TYPE RatioZ[MAX_UPSCALE_Y];
+ for (int x = 0; x <= a_UpscaleX; x++)
+ {
+ RatioX[x] = (TYPE)x / a_UpscaleX;
+ }
+ for (int y = 0; y <= a_UpscaleY; y++)
+ {
+ RatioY[y] = (TYPE)y / a_UpscaleY;
+ }
+ for (int z = 0; z <= a_UpscaleZ; z++)
+ {
+ RatioZ[z] = (TYPE)z / a_UpscaleZ;
+ }
+
+ // Interpolate each XYZ cell:
+ int DstSizeX = (a_SrcSizeX - 1) * a_UpscaleX + 1;
+ int DstSizeY = (a_SrcSizeY - 1) * a_UpscaleY + 1;
+ int DstSizeZ = (a_SrcSizeZ - 1) * a_UpscaleZ + 1;
+ for (int z = 0; z < (a_SrcSizeZ - 1); z++)
+ {
+ int DstZ = z * a_UpscaleZ;
+ for (int y = 0; y < (a_SrcSizeY - 1); y++)
+ {
+ int DstY = y * a_UpscaleY;
+ int idx = y * a_SrcSizeX + z * a_SrcSizeX * a_SrcSizeY;
+ for (int x = 0; x < (a_SrcSizeX - 1); x++, idx++)
+ {
+ int DstX = x * a_UpscaleX;
+ TYPE LoXLoYLoZ = a_Src[idx];
+ TYPE LoXLoYHiZ = a_Src[idx + a_SrcSizeX * a_SrcSizeY];
+ TYPE LoXHiYLoZ = a_Src[idx + a_SrcSizeX];
+ TYPE LoXHiYHiZ = a_Src[idx + a_SrcSizeX + a_SrcSizeX * a_SrcSizeY];
+ TYPE HiXLoYLoZ = a_Src[idx + 1];
+ TYPE HiXLoYHiZ = a_Src[idx + 1 + a_SrcSizeX * a_SrcSizeY];
+ TYPE HiXHiYLoZ = a_Src[idx + 1 + a_SrcSizeX];
+ TYPE HiXHiYHiZ = a_Src[idx + 1 + a_SrcSizeX + a_SrcSizeX * a_SrcSizeY];
+ for (int CellZ = 0; CellZ <= a_UpscaleZ; CellZ++)
+ {
+ TYPE LoXLoYInZ = LoXLoYLoZ + (LoXLoYHiZ - LoXLoYLoZ) * RatioZ[CellZ];
+ TYPE LoXHiYInZ = LoXHiYLoZ + (LoXHiYHiZ - LoXHiYLoZ) * RatioZ[CellZ];
+ TYPE HiXLoYInZ = HiXLoYLoZ + (HiXLoYHiZ - HiXLoYLoZ) * RatioZ[CellZ];
+ TYPE HiXHiYInZ = HiXHiYLoZ + (HiXHiYHiZ - HiXHiYLoZ) * RatioZ[CellZ];
+ for (int CellY = 0; CellY <= a_UpscaleY; CellY++)
+ {
+ int DestIdx = (DstZ + CellZ) * DstSizeX * DstSizeY + (DstY + CellY) * DstSizeX + DstX;
+ ASSERT(DestIdx + a_UpscaleX < DstSizeX * DstSizeY * DstSizeZ);
+ TYPE LoXInY = LoXLoYInZ + (LoXHiYInZ - LoXLoYInZ) * RatioY[CellY];
+ TYPE HiXInY = HiXLoYInZ + (HiXHiYInZ - HiXLoYInZ) * RatioY[CellY];
+ for (int CellX = 0; CellX <= a_UpscaleX; CellX++, DestIdx++)
+ {
+ a_Dst[DestIdx] = LoXInY + (HiXInY - LoXInY) * RatioX[CellX];
+ }
+ } // for CellY
+ } // for CellZ
+ } // for x
+ } // for y
+ } // for z
+}
+
+
+
+
+
diff --git a/source/LuaScript.cpp b/source/LuaScript.cpp
new file mode 100644
index 000000000..8d92c238f
--- /dev/null
+++ b/source/LuaScript.cpp
@@ -0,0 +1,278 @@
+
+// LuaScript.cpp
+
+// Implements the cLuaScript class that loads a Lua script file to produce a web template out of it
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "LuaScript.h"
+
+extern "C"
+{
+#include "lualib.h"
+}
+
+#include "tolua++.h"
+#include "Bindings.h"
+#include "ManualBindings.h"
+
+// fwd: SQLite/lsqlite3.c
+extern "C"
+{
+ LUALIB_API int luaopen_lsqlite3(lua_State * L);
+}
+
+// fwd: LuaExpat/lxplib.c:
+extern "C"
+{
+ int luaopen_lxp(lua_State * L);
+}
+
+
+
+
+
+cLuaScript::cLuaScript()
+ : m_LuaState(NULL)
+{
+
+}
+
+
+
+
+
+cLuaScript::~cLuaScript()
+{
+ if( m_LuaState )
+ {
+ lua_close( m_LuaState );
+ m_LuaState = 0;
+ }
+}
+
+
+
+
+
+void cLuaScript::Initialize()
+{
+ // Check to see if this script has not been initialized before
+ ASSERT(!m_LuaState);
+
+ // Create a Lua state and bind all libraries to it
+ m_LuaState = lua_open();
+ luaL_openlibs(m_LuaState);
+ tolua_AllToLua_open(m_LuaState);
+ ManualBindings::Bind(m_LuaState);
+ luaopen_lsqlite3(m_LuaState);
+ luaopen_lxp(m_LuaState);
+}
+
+
+
+
+
+bool cLuaScript::LoadFile( const char* a_FilePath )
+{
+ // Make sure the plugin is initialized
+ ASSERT(m_LuaState);
+
+ // Load the file into the Lua state
+ int s = luaL_loadfile(m_LuaState, a_FilePath );
+ if (ReportErrors(s))
+ {
+ return false;
+ }
+ return true;
+}
+
+
+
+
+
+bool cLuaScript::Execute()
+{
+ // Make sure we got a Lua state
+ ASSERT(m_LuaState);
+
+ // Execute the script as it is right now
+ int s = lua_pcall(m_LuaState, 0, LUA_MULTRET, 0);
+ if( ReportErrors( s ) )
+ {
+ return false;
+ }
+ return true;
+}
+
+
+
+
+
+bool cLuaScript::ReportErrors( int a_Status )
+{
+ if (a_Status == 0)
+ {
+ // No error to report
+ return false;
+ }
+
+ // Status was set to error so get the error from the Lua state and log it
+ LOGERROR("LUA: %s", lua_tostring(m_LuaState, -1));
+ lua_pop(m_LuaState, 1);
+
+ // Return true to indicate that an error was returned
+ return true;
+}
+
+
+
+
+
+bool cLuaScript::LuaPushFunction( const char * a_FunctionName, bool a_bLogError /*= true*/ )
+{
+ ASSERT(m_LuaState);
+
+ // Find and push the function on the Lua stack
+ lua_getglobal(m_LuaState, a_FunctionName);
+
+ // Make sure we found a function
+ if (!lua_isfunction(m_LuaState, -1))
+ {
+ if (a_bLogError)
+ {
+ LOGWARN("LUA: Could not find function %s()", a_FunctionName);
+ }
+
+ // Pop the pushed 'object' back
+ lua_pop(m_LuaState, 1);
+ return false;
+ }
+
+ // Successfully pushed a function to the Lua stack
+ return true;
+}
+
+
+
+
+
+bool cLuaScript::LuaCallFunction( int a_NumArgs, int a_NumResults, const char * a_FunctionName )
+{
+ ASSERT(m_LuaState);
+
+ // Make sure there's a lua function on the stack
+ ASSERT(lua_isfunction(m_LuaState, -a_NumArgs - 1));
+
+ // Call the desired function
+ int s = lua_pcall(m_LuaState, a_NumArgs, a_NumResults, 0);
+
+ // Check for errors
+ if (ReportErrors(s))
+ {
+ LOGWARN("LUA: Error calling function %s()", a_FunctionName);
+ return false;
+ }
+
+ // Successfully executed function
+ return true;
+}
+
+
+
+
+
+bool cLuaScript::CallFunction( const char* a_Function, AString& ReturnedString )
+{
+ // Make sure we have the required things to call a function
+ ASSERT(m_LuaState);
+ ASSERT(a_Function);
+
+ // Push the desired function on the stack
+ if (!LuaPushFunction(a_Function))
+ {
+ return false;
+ }
+
+ if (!LuaCallFunction(0, 1, a_Function))
+ {
+ return false;
+ }
+
+ if (lua_isstring(m_LuaState, -1))
+ {
+ ReturnedString = tolua_tostring(m_LuaState, -1, "");
+ }
+ lua_pop(m_LuaState, 1);
+ return true;
+}
+
+
+
+
+
+bool cLuaScript::CallFunction( const char* a_Function, const sLuaUsertype& a_UserType, AString& ReturnedString )
+{
+ // Make sure we have the required things to call a function
+ ASSERT(m_LuaState);
+ ASSERT(a_Function);
+
+ // Push the desired function on the stack
+ if (!LuaPushFunction(a_Function))
+ {
+ return false;
+ }
+
+ tolua_pushusertype(m_LuaState, a_UserType.Object, a_UserType.ClassName);
+
+ if (!LuaCallFunction(1, 1, a_Function))
+ {
+ return false;
+ }
+
+ if (lua_isstring(m_LuaState, -1))
+ {
+ ReturnedString = tolua_tostring(m_LuaState, -1, "");
+ }
+ lua_pop(m_LuaState, 1);
+ return true;
+}
+
+
+
+
+
+bool cLuaScript::CallFunction( const char* a_Function, const sLuaUsertype& a_UserType1, const sLuaUsertype& a_UserType2, AString& ReturnedString )
+{
+ // Make sure we have the required things to call a function
+ ASSERT(m_LuaState);
+ ASSERT(a_Function);
+
+ // Push the desired function on the stack
+ if (!LuaPushFunction(a_Function))
+ {
+ return false;
+ }
+
+ tolua_pushusertype(m_LuaState, a_UserType1.Object, a_UserType1.ClassName);
+ tolua_pushusertype(m_LuaState, a_UserType2.Object, a_UserType2.ClassName);
+
+ if (!LuaCallFunction(2, 1, a_Function))
+ {
+ return false;
+ }
+
+ if (lua_isstring(m_LuaState, -1))
+ {
+ ReturnedString = tolua_tostring(m_LuaState, -1, "");
+ }
+ lua_pop(m_LuaState, 1);
+ return true;
+}
+
+
+
+
+
+
+
diff --git a/source/LuaScript.h b/source/LuaScript.h
new file mode 100644
index 000000000..f98b2e65b
--- /dev/null
+++ b/source/LuaScript.h
@@ -0,0 +1,63 @@
+
+// LuaScript.h
+
+// Declares the cLuaScript class that loads a Lua script file to produce a web template out of it
+
+
+
+
+
+#pragma once
+
+struct lua_State;
+
+
+
+
+
+struct sLuaUsertype
+{
+ sLuaUsertype(void* a_pObject, const char* a_pClassName) : Object(a_pObject), ClassName(a_pClassName) {}
+ //
+ void* Object;
+ const char* ClassName;
+} ;
+
+
+
+
+
+class cLuaScript
+{
+public:
+ cLuaScript();
+ ~cLuaScript();
+
+ /// Prepares a Lua state
+ void Initialize();
+
+ /// Load a Lua script on the given path
+ bool LoadFile(const char* a_FilePath);
+
+ /// Execute the loaded Lua script
+ bool Execute();
+
+ /// Call a function on the Lua script. Put all overloads here
+ bool CallFunction(const char* a_Function, AString& ReturnedString);
+ bool CallFunction(const char* a_Function, const sLuaUsertype& a_UserType, AString& ReturnedString);
+ bool CallFunction(const char* a_Function, const sLuaUsertype& a_UserType1, const sLuaUsertype& a_UserType2, AString& ReturnedString);
+
+protected:
+ /// Reports an error in the log if a_Status is flagged as an error. Returns true when a_Status is flagged as error, returns false when no error occured.
+ bool ReportErrors(int a_Status);
+
+ /// Helper functions for calling functions in Lua
+ bool LuaPushFunction(const char * a_FunctionName, bool a_bLogError = true);
+ bool LuaCallFunction(int a_NumArgs, int a_NumResults, const char * a_FunctionName ); // a_FunctionName is only used for error messages, nothing else
+private:
+ lua_State* m_LuaState;
+} ;
+
+
+
+
diff --git a/source/LuaWindow.cpp b/source/LuaWindow.cpp
index 13d06eeb6..c36fabd3c 100644
--- a/source/LuaWindow.cpp
+++ b/source/LuaWindow.cpp
@@ -1,175 +1,175 @@
-
-// LuaWindow.cpp
-
-// Implements the cLuaWindow class representing a virtual window that plugins may create and open for the player
-
-#include "Globals.h"
-#include "LuaWindow.h"
-#include "UI/SlotArea.h"
-#include "Plugin_NewLua.h"
-#include "Player.h"
-#include "lauxlib.h" // Needed for LUA_REFNIL
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cLuaWindow:
-
-cLuaWindow::cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title) :
- super(a_WindowType, a_Title),
- m_Contents(a_SlotsX, a_SlotsY),
- m_Plugin(NULL),
- m_LuaRef(LUA_REFNIL),
- m_OnClosingFnRef(LUA_REFNIL),
- m_OnSlotChangedFnRef(LUA_REFNIL)
-{
- m_Contents.AddListener(*this);
- m_SlotAreas.push_back(new cSlotAreaItemGrid(m_Contents, *this));
-
- // If appropriate, add an Armor slot area:
- switch (a_WindowType)
- {
- case cWindow::Inventory:
- case cWindow::Workbench:
- {
- m_SlotAreas.push_back(new cSlotAreaArmor(*this));
- break;
- }
- }
- m_SlotAreas.push_back(new cSlotAreaInventory(*this));
- m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
-}
-
-
-
-
-
-cLuaWindow::~cLuaWindow()
-{
- ASSERT(m_OpenedBy.empty());
-}
-
-
-
-
-
-void cLuaWindow::SetLuaRef(cPlugin_NewLua * a_Plugin, int a_LuaRef)
-{
- // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object
- ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin));
- ASSERT(m_LuaRef == LUA_REFNIL);
- m_Plugin = a_Plugin;
- m_LuaRef = a_LuaRef;
-}
-
-
-
-
-
-bool cLuaWindow::IsLuaReferenced(void) const
-{
- return ((m_Plugin != NULL) && (m_LuaRef != LUA_REFNIL));
-}
-
-
-
-
-
-void cLuaWindow::SetOnClosing(cPlugin_NewLua * a_Plugin, int a_FnRef)
-{
- // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object
- ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin));
-
- // If there already was a function, unreference it first
- if (m_OnClosingFnRef != LUA_REFNIL)
- {
- m_Plugin->Unreference(m_OnClosingFnRef);
- }
-
- // Store the new reference
- m_Plugin = a_Plugin;
- m_OnClosingFnRef = a_FnRef;
-}
-
-
-
-
-
-void cLuaWindow::SetOnSlotChanged(cPlugin_NewLua * a_Plugin, int a_FnRef)
-{
- // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object
- ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin));
-
- // If there already was a function, unreference it first
- if (m_OnSlotChangedFnRef != LUA_REFNIL)
- {
- m_Plugin->Unreference(m_OnSlotChangedFnRef);
- }
-
- // Store the new reference
- m_Plugin = a_Plugin;
- m_OnSlotChangedFnRef = a_FnRef;
-}
-
-
-
-
-
-bool cLuaWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse)
-{
- // First notify the plugin through the registered callback:
- if (m_OnClosingFnRef != LUA_REFNIL)
- {
- ASSERT(m_Plugin != NULL);
- if (m_Plugin->CallbackWindowClosing(m_OnClosingFnRef, *this, a_Player, a_CanRefuse))
- {
- // The callback disagrees (the higher levels check the CanRefuse flag compliance)
- return false;
- }
- }
-
- return super::ClosedByPlayer(a_Player, a_CanRefuse);
-}
-
-
-
-
-
-void cLuaWindow::Destroy(void)
-{
- super::Destroy();
-
- if ((m_LuaRef != LUA_REFNIL) && (m_Plugin != NULL))
- {
- // The object is referenced by Lua, un-reference it
- m_Plugin->Unreference(m_LuaRef);
- }
-
- // Lua will take care of this object, it will garbage-collect it, so we *must not* delete it!
- m_IsDestroyed = false;
-}
-
-
-
-
-
-void cLuaWindow::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
-{
- if (a_ItemGrid != &m_Contents)
- {
- ASSERT(!"Invalid ItemGrid in callback");
- return;
- }
-
- // If an OnSlotChanged callback has been registered, call it:
- if (m_OnSlotChangedFnRef != LUA_REFNIL)
- {
- m_Plugin->CallbackWindowSlotChanged(m_OnSlotChangedFnRef, *this, a_SlotNum);
- }
-}
-
-
-
-
+
+// LuaWindow.cpp
+
+// Implements the cLuaWindow class representing a virtual window that plugins may create and open for the player
+
+#include "Globals.h"
+#include "LuaWindow.h"
+#include "UI/SlotArea.h"
+#include "Plugin_NewLua.h"
+#include "Player.h"
+#include "lauxlib.h" // Needed for LUA_REFNIL
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cLuaWindow:
+
+cLuaWindow::cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title) :
+ super(a_WindowType, a_Title),
+ m_Contents(a_SlotsX, a_SlotsY),
+ m_Plugin(NULL),
+ m_LuaRef(LUA_REFNIL),
+ m_OnClosingFnRef(LUA_REFNIL),
+ m_OnSlotChangedFnRef(LUA_REFNIL)
+{
+ m_Contents.AddListener(*this);
+ m_SlotAreas.push_back(new cSlotAreaItemGrid(m_Contents, *this));
+
+ // If appropriate, add an Armor slot area:
+ switch (a_WindowType)
+ {
+ case cWindow::Inventory:
+ case cWindow::Workbench:
+ {
+ m_SlotAreas.push_back(new cSlotAreaArmor(*this));
+ break;
+ }
+ }
+ m_SlotAreas.push_back(new cSlotAreaInventory(*this));
+ m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
+}
+
+
+
+
+
+cLuaWindow::~cLuaWindow()
+{
+ ASSERT(m_OpenedBy.empty());
+}
+
+
+
+
+
+void cLuaWindow::SetLuaRef(cPlugin_NewLua * a_Plugin, int a_LuaRef)
+{
+ // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object
+ ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin));
+ ASSERT(m_LuaRef == LUA_REFNIL);
+ m_Plugin = a_Plugin;
+ m_LuaRef = a_LuaRef;
+}
+
+
+
+
+
+bool cLuaWindow::IsLuaReferenced(void) const
+{
+ return ((m_Plugin != NULL) && (m_LuaRef != LUA_REFNIL));
+}
+
+
+
+
+
+void cLuaWindow::SetOnClosing(cPlugin_NewLua * a_Plugin, int a_FnRef)
+{
+ // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object
+ ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin));
+
+ // If there already was a function, unreference it first
+ if (m_OnClosingFnRef != LUA_REFNIL)
+ {
+ m_Plugin->Unreference(m_OnClosingFnRef);
+ }
+
+ // Store the new reference
+ m_Plugin = a_Plugin;
+ m_OnClosingFnRef = a_FnRef;
+}
+
+
+
+
+
+void cLuaWindow::SetOnSlotChanged(cPlugin_NewLua * a_Plugin, int a_FnRef)
+{
+ // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object
+ ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin));
+
+ // If there already was a function, unreference it first
+ if (m_OnSlotChangedFnRef != LUA_REFNIL)
+ {
+ m_Plugin->Unreference(m_OnSlotChangedFnRef);
+ }
+
+ // Store the new reference
+ m_Plugin = a_Plugin;
+ m_OnSlotChangedFnRef = a_FnRef;
+}
+
+
+
+
+
+bool cLuaWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse)
+{
+ // First notify the plugin through the registered callback:
+ if (m_OnClosingFnRef != LUA_REFNIL)
+ {
+ ASSERT(m_Plugin != NULL);
+ if (m_Plugin->CallbackWindowClosing(m_OnClosingFnRef, *this, a_Player, a_CanRefuse))
+ {
+ // The callback disagrees (the higher levels check the CanRefuse flag compliance)
+ return false;
+ }
+ }
+
+ return super::ClosedByPlayer(a_Player, a_CanRefuse);
+}
+
+
+
+
+
+void cLuaWindow::Destroy(void)
+{
+ super::Destroy();
+
+ if ((m_LuaRef != LUA_REFNIL) && (m_Plugin != NULL))
+ {
+ // The object is referenced by Lua, un-reference it
+ m_Plugin->Unreference(m_LuaRef);
+ }
+
+ // Lua will take care of this object, it will garbage-collect it, so we *must not* delete it!
+ m_IsDestroyed = false;
+}
+
+
+
+
+
+void cLuaWindow::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
+{
+ if (a_ItemGrid != &m_Contents)
+ {
+ ASSERT(!"Invalid ItemGrid in callback");
+ return;
+ }
+
+ // If an OnSlotChanged callback has been registered, call it:
+ if (m_OnSlotChangedFnRef != LUA_REFNIL)
+ {
+ m_Plugin->CallbackWindowSlotChanged(m_OnSlotChangedFnRef, *this, a_SlotNum);
+ }
+}
+
+
+
+
diff --git a/source/LuaWindow.h b/source/LuaWindow.h
index de24170df..60c24996d 100644
--- a/source/LuaWindow.h
+++ b/source/LuaWindow.h
@@ -1,96 +1,96 @@
-
-// LuaWindow.h
-
-// Declares the cLuaWindow class representing a virtual window that plugins may create and open for the player
-
-
-
-
-
-#pragma once
-
-#include "UI/Window.h"
-#include "ItemGrid.h"
-
-
-
-
-
-// fwd: Plugin_NewLua.h
-class cPlugin_NewLua;
-
-
-
-
-
-// tolua_begin
-
-/** A window that has been created by a Lua plugin and is handled entirely by that plugin
-This object needs extra care with its lifetime management:
-- It is created by Lua, so Lua expects to garbage-collect it later
-- normal cWindow objects are deleted in their ClosedByPlayer() function if the last player closes them
-To overcome this, this object overloads the Destroy functions, which doesn't let the ClosedByPlayer()
-delete the window, but rather leaves it dangling, with only Lua having the reference to it.
-Additionally, to forbid Lua from deleting this object while it is used by players, the manual bindings for
-cPlayer:OpenWindow check if the window is of this class, and if so, make a global Lua reference for this object.
-This reference needs to be unreferenced in the Destroy() function.
-*/
-class cLuaWindow :
- public cWindow,
- public cItemGrid::cListener
-{
- typedef cWindow super;
-
-public:
- /// Create a window of the specified type, with a slot grid of a_SlotsX * a_SlotsY size
- cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title);
-
- virtual ~cLuaWindow();
-
- /// Returns the internal representation of the contents that are manipulated by Lua
- cItemGrid & GetContents(void) { return m_Contents; }
-
- // tolua_end
-
- /** Sets the plugin reference and the internal Lua object reference index
- used for preventing Lua's GC to collect this class while the window is open
- */
- void SetLuaRef(cPlugin_NewLua * a_Plugin, int a_LuaRef);
-
- /// Returns true if SetLuaRef() has been called
- bool IsLuaReferenced(void) const;
-
- /// Sets the callback function (Lua reference) to call when the window is about to close
- void SetOnClosing(cPlugin_NewLua * a_Plugin, int a_FnRef);
-
- /// Sets the callback function (Lua reference) to call when a slot is changed
- void SetOnSlotChanged(cPlugin_NewLua * a_Plugin, int a_FnRef);
-
-protected:
- /// Contents of the non-inventory part
- cItemGrid m_Contents;
-
- /// The plugin that has opened the window and owns the m_LuaRef
- cPlugin_NewLua * m_Plugin;
-
- /// The Lua object reference, used for keeping the object alive as long as any player has the window open
- int m_LuaRef;
-
- /// The Lua reference for the callback to call when the window is closing for any player
- int m_OnClosingFnRef;
-
- /// The Lua reference for the callback to call when a slot has changed
- int m_OnSlotChangedFnRef;
-
- // cWindow overrides:
- virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) override;
- virtual void Destroy(void) override;
-
- // cItemGrid::cListener overrides:
- virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override;
-} ; // tolua_export
-
-
-
-
-
+
+// LuaWindow.h
+
+// Declares the cLuaWindow class representing a virtual window that plugins may create and open for the player
+
+
+
+
+
+#pragma once
+
+#include "UI/Window.h"
+#include "ItemGrid.h"
+
+
+
+
+
+// fwd: Plugin_NewLua.h
+class cPlugin_NewLua;
+
+
+
+
+
+// tolua_begin
+
+/** A window that has been created by a Lua plugin and is handled entirely by that plugin
+This object needs extra care with its lifetime management:
+- It is created by Lua, so Lua expects to garbage-collect it later
+- normal cWindow objects are deleted in their ClosedByPlayer() function if the last player closes them
+To overcome this, this object overloads the Destroy functions, which doesn't let the ClosedByPlayer()
+delete the window, but rather leaves it dangling, with only Lua having the reference to it.
+Additionally, to forbid Lua from deleting this object while it is used by players, the manual bindings for
+cPlayer:OpenWindow check if the window is of this class, and if so, make a global Lua reference for this object.
+This reference needs to be unreferenced in the Destroy() function.
+*/
+class cLuaWindow :
+ public cWindow,
+ public cItemGrid::cListener
+{
+ typedef cWindow super;
+
+public:
+ /// Create a window of the specified type, with a slot grid of a_SlotsX * a_SlotsY size
+ cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title);
+
+ virtual ~cLuaWindow();
+
+ /// Returns the internal representation of the contents that are manipulated by Lua
+ cItemGrid & GetContents(void) { return m_Contents; }
+
+ // tolua_end
+
+ /** Sets the plugin reference and the internal Lua object reference index
+ used for preventing Lua's GC to collect this class while the window is open
+ */
+ void SetLuaRef(cPlugin_NewLua * a_Plugin, int a_LuaRef);
+
+ /// Returns true if SetLuaRef() has been called
+ bool IsLuaReferenced(void) const;
+
+ /// Sets the callback function (Lua reference) to call when the window is about to close
+ void SetOnClosing(cPlugin_NewLua * a_Plugin, int a_FnRef);
+
+ /// Sets the callback function (Lua reference) to call when a slot is changed
+ void SetOnSlotChanged(cPlugin_NewLua * a_Plugin, int a_FnRef);
+
+protected:
+ /// Contents of the non-inventory part
+ cItemGrid m_Contents;
+
+ /// The plugin that has opened the window and owns the m_LuaRef
+ cPlugin_NewLua * m_Plugin;
+
+ /// The Lua object reference, used for keeping the object alive as long as any player has the window open
+ int m_LuaRef;
+
+ /// The Lua reference for the callback to call when the window is closing for any player
+ int m_OnClosingFnRef;
+
+ /// The Lua reference for the callback to call when a slot has changed
+ int m_OnSlotChangedFnRef;
+
+ // cWindow overrides:
+ virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) override;
+ virtual void Destroy(void) override;
+
+ // cItemGrid::cListener overrides:
+ virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override;
+} ; // tolua_export
+
+
+
+
+
diff --git a/source/ManualBindings.cpp b/source/ManualBindings.cpp
index 30648a8b7..7a13d94c0 100644
--- a/source/ManualBindings.cpp
+++ b/source/ManualBindings.cpp
@@ -1272,6 +1272,59 @@ static int tolua_get_HTTPRequest_FormData(lua_State* tolua_S)
+static int tolua_cWebAdmin_GetPlugins(lua_State * tolua_S)
+{
+ cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S,1,0);
+
+ const cWebAdmin::PluginList & AllPlugins = self->GetPlugins();
+
+ lua_createtable(tolua_S, AllPlugins.size(), 0);
+ int newTable = lua_gettop(tolua_S);
+ int index = 1;
+ cWebAdmin::PluginList::const_iterator iter = AllPlugins.begin();
+ while(iter != AllPlugins.end())
+ {
+ const cWebPlugin* Plugin = *iter;
+ tolua_pushusertype( tolua_S, (void*)Plugin, "const cWebPlugin" );
+ lua_rawseti(tolua_S, newTable, index);
+ ++iter;
+ ++index;
+ }
+ return 1;
+}
+
+
+
+
+
+static int tolua_cWebPlugin_GetTabNames(lua_State * tolua_S)
+{
+ cWebPlugin* self = (cWebPlugin*) tolua_tousertype(tolua_S,1,0);
+
+ const cWebPlugin::TabNameList & TabNames = self->GetTabNames();
+
+ lua_newtable(tolua_S);
+ int newTable = lua_gettop(tolua_S);
+ int index = 1;
+ cWebPlugin::TabNameList::const_iterator iter = TabNames.begin();
+ while(iter != TabNames.end())
+ {
+ const AString & FancyName = iter->first;
+ const AString & WebName = iter->second;
+ tolua_pushstring( tolua_S, WebName.c_str() ); // Because the WebName is supposed to be unique, use it as key
+ tolua_pushstring( tolua_S, FancyName.c_str() );
+ //
+ lua_rawset(tolua_S, -3);
+ ++iter;
+ ++index;
+ }
+ return 1;
+}
+
+
+
+
+
static int Lua_ItemGrid_GetSlotCoords(lua_State * L)
{
tolua_Error tolua_err;
@@ -1377,6 +1430,14 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_variable(tolua_S,"PostParams",tolua_get_HTTPRequest_PostParams,0);
tolua_variable(tolua_S,"FormData",tolua_get_HTTPRequest_FormData,0);
tolua_endmodule(tolua_S);
+
+ tolua_beginmodule(tolua_S, "cWebAdmin");
+ tolua_function(tolua_S, "GetPlugins", tolua_cWebAdmin_GetPlugins);
+ tolua_endmodule(tolua_S);
+
+ tolua_beginmodule(tolua_S, "cWebPlugin");
+ tolua_function(tolua_S, "GetTabNames", tolua_cWebPlugin_GetTabNames);
+ tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cClientHandle");
tolua_constant(tolua_S, "MIN_VIEW_DISTANCE", cClientHandle::MIN_VIEW_DISTANCE);
diff --git a/source/Minecart.cpp b/source/Minecart.cpp
index 232f44f7d..bae56d582 100644
--- a/source/Minecart.cpp
+++ b/source/Minecart.cpp
@@ -1,160 +1,160 @@
-
-// Minecart.cpp
-
-// Implements the cMinecart class representing a minecart in the world
-
-#include "Globals.h"
-#include "Minecart.h"
-#include "World.h"
-#include "ClientHandle.h"
-#include "Player.h"
-
-
-
-
-
-cMinecart::cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z) :
- super(etMinecart, a_X, a_Y, a_Z, 0.98, 0.7),
- m_Payload(a_Payload)
-{
-}
-
-
-
-
-void cMinecart::Initialize(cWorld * a_World)
-{
- super::Initialize(a_World);
- a_World->BroadcastSpawnEntity(*this);
-}
-
-
-
-
-
-void cMinecart::SpawnOn(cClientHandle & a_ClientHandle)
-{
- char Type = 0;
- switch (m_Payload)
- {
- case mpNone: Type = 10; break;
- case mpChest: Type = 11; break;
- case mpFurnace: Type = 12; break;
- default:
- {
- ASSERT(!"Unknown payload, cannot spawn on client");
- return;
- }
- }
- a_ClientHandle.SendSpawnVehicle(*this, Type);
-}
-
-
-
-
-
-void cMinecart::Tick(float a_Dt, cChunk & a_Chunk)
-{
- // TODO: the physics
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cEmptyMinecart:
-
-cEmptyMinecart::cEmptyMinecart(double a_X, double a_Y, double a_Z) :
- super(mpNone, a_X, a_Y, a_Z)
-{
-}
-
-
-
-
-
-void cEmptyMinecart::OnRightClicked(cPlayer & a_Player)
-{
- if (m_Attachee != NULL)
- {
- if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID())
- {
- // This player is already sitting in, they want out.
- a_Player.Detach();
- return;
- }
-
- if (m_Attachee->IsPlayer())
- {
- // Another player is already sitting in here, cannot attach
- return;
- }
-
- // Detach whatever is sitting in this minecart now:
- m_Attachee->Detach();
- }
-
- // Attach the player to this minecart
- a_Player.AttachTo(this);
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cMinecartWithChest:
-
-cMinecartWithChest::cMinecartWithChest(double a_X, double a_Y, double a_Z) :
- super(mpChest, a_X, a_Y, a_Z)
-{
-}
-
-
-
-
-
-void cMinecartWithChest::SetSlot(int a_Idx, const cItem & a_Item)
-{
- ASSERT((a_Idx >= 0) && (a_Idx < ARRAYCOUNT(m_Items)));
-
- m_Items[a_Idx] = a_Item;
-}
-
-
-
-
-
-void cMinecartWithChest::OnRightClicked(cPlayer & a_Player)
-{
- // Show the chest UI window to the player
- // TODO
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cMinecartWithFurnace:
-
-cMinecartWithFurnace::cMinecartWithFurnace(double a_X, double a_Y, double a_Z) :
- super(mpFurnace, a_X, a_Y, a_Z)
-{
-}
-
-
-
-
-
-void cMinecartWithFurnace::OnRightClicked(cPlayer & a_Player)
-{
- // Try to power the furnace with whatever the player is holding
- // TODO
-}
-
-
-
-
-
+
+// Minecart.cpp
+
+// Implements the cMinecart class representing a minecart in the world
+
+#include "Globals.h"
+#include "Minecart.h"
+#include "World.h"
+#include "ClientHandle.h"
+#include "Player.h"
+
+
+
+
+
+cMinecart::cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z) :
+ super(etMinecart, a_X, a_Y, a_Z, 0.98, 0.7),
+ m_Payload(a_Payload)
+{
+}
+
+
+
+
+void cMinecart::Initialize(cWorld * a_World)
+{
+ super::Initialize(a_World);
+ a_World->BroadcastSpawnEntity(*this);
+}
+
+
+
+
+
+void cMinecart::SpawnOn(cClientHandle & a_ClientHandle)
+{
+ char Type = 0;
+ switch (m_Payload)
+ {
+ case mpNone: Type = 10; break;
+ case mpChest: Type = 11; break;
+ case mpFurnace: Type = 12; break;
+ default:
+ {
+ ASSERT(!"Unknown payload, cannot spawn on client");
+ return;
+ }
+ }
+ a_ClientHandle.SendSpawnVehicle(*this, Type);
+}
+
+
+
+
+
+void cMinecart::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ // TODO: the physics
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cEmptyMinecart:
+
+cEmptyMinecart::cEmptyMinecart(double a_X, double a_Y, double a_Z) :
+ super(mpNone, a_X, a_Y, a_Z)
+{
+}
+
+
+
+
+
+void cEmptyMinecart::OnRightClicked(cPlayer & a_Player)
+{
+ if (m_Attachee != NULL)
+ {
+ if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID())
+ {
+ // This player is already sitting in, they want out.
+ a_Player.Detach();
+ return;
+ }
+
+ if (m_Attachee->IsPlayer())
+ {
+ // Another player is already sitting in here, cannot attach
+ return;
+ }
+
+ // Detach whatever is sitting in this minecart now:
+ m_Attachee->Detach();
+ }
+
+ // Attach the player to this minecart
+ a_Player.AttachTo(this);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cMinecartWithChest:
+
+cMinecartWithChest::cMinecartWithChest(double a_X, double a_Y, double a_Z) :
+ super(mpChest, a_X, a_Y, a_Z)
+{
+}
+
+
+
+
+
+void cMinecartWithChest::SetSlot(int a_Idx, const cItem & a_Item)
+{
+ ASSERT((a_Idx >= 0) && (a_Idx < ARRAYCOUNT(m_Items)));
+
+ m_Items[a_Idx] = a_Item;
+}
+
+
+
+
+
+void cMinecartWithChest::OnRightClicked(cPlayer & a_Player)
+{
+ // Show the chest UI window to the player
+ // TODO
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cMinecartWithFurnace:
+
+cMinecartWithFurnace::cMinecartWithFurnace(double a_X, double a_Y, double a_Z) :
+ super(mpFurnace, a_X, a_Y, a_Z)
+{
+}
+
+
+
+
+
+void cMinecartWithFurnace::OnRightClicked(cPlayer & a_Player)
+{
+ // Try to power the furnace with whatever the player is holding
+ // TODO
+}
+
+
+
+
+
diff --git a/source/Minecart.h b/source/Minecart.h
index db61997d8..b7b4c2b04 100644
--- a/source/Minecart.h
+++ b/source/Minecart.h
@@ -1,117 +1,117 @@
-
-// Minecart.h
-
-// Declares the cMinecart class representing a minecart in the world
-
-
-
-
-
-#pragma once
-
-#include "Entity.h"
-#include "Item.h"
-
-
-
-
-
-class cMinecart :
- public cEntity
-{
- typedef cEntity super;
-
-public:
- CLASS_PROTODEF(cMinecart);
-
- enum ePayload
- {
- mpNone, // Empty minecart, ridable by player or mobs
- mpChest, // Minecart-with-chest, can store a grid of 3*8 items
- mpFurnace, // Minecart-with-furnace, can be powered
- // TODO: Other 1.5 features: hopper, tnt, dispenser, spawner
- } ;
-
- // cEntity overrides:
- virtual void Initialize(cWorld * a_World) override;
- virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
-
- ePayload GetPayload(void) const { return m_Payload; }
-
-protected:
- ePayload m_Payload;
-
- cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z);
-} ;
-
-
-
-
-
-class cEmptyMinecart :
- public cMinecart
-{
- typedef cMinecart super;
-
-public:
- CLASS_PROTODEF(cEmptyMinecart);
-
- cEmptyMinecart(double a_X, double a_Y, double a_Z);
-
- // cEntity overrides:
- virtual void OnRightClicked(cPlayer & a_Player) override;
-} ;
-
-
-
-
-
-class cMinecartWithChest :
- public cMinecart
-{
- typedef cMinecart super;
-
-public:
- CLASS_PROTODEF(cMinecartWithChest);
-
- /// Number of item slots in the chest
- static const int NumSlots = 9 * 3;
-
- cMinecartWithChest(double a_X, double a_Y, double a_Z);
-
- const cItem & GetSlot(int a_Idx) const { return m_Items[a_Idx]; }
- cItem & GetSlot(int a_Idx) { return m_Items[a_Idx]; }
-
- void SetSlot(int a_Idx, const cItem & a_Item);
-
-protected:
-
- /// The chest contents:
- cItem m_Items[NumSlots];
-
- // cEntity overrides:
- virtual void OnRightClicked(cPlayer & a_Player) override;
-} ;
-
-
-
-
-
-class cMinecartWithFurnace :
- public cMinecart
-{
- typedef cMinecart super;
-
-public:
- CLASS_PROTODEF(cMinecartWithFurnace);
-
- cMinecartWithFurnace(double a_X, double a_Y, double a_Z);
-
- // cEntity overrides:
- virtual void OnRightClicked(cPlayer & a_Player) override;
-} ;
-
-
-
-
+
+// Minecart.h
+
+// Declares the cMinecart class representing a minecart in the world
+
+
+
+
+
+#pragma once
+
+#include "Entity.h"
+#include "Item.h"
+
+
+
+
+
+class cMinecart :
+ public cEntity
+{
+ typedef cEntity super;
+
+public:
+ CLASS_PROTODEF(cMinecart);
+
+ enum ePayload
+ {
+ mpNone, // Empty minecart, ridable by player or mobs
+ mpChest, // Minecart-with-chest, can store a grid of 3*8 items
+ mpFurnace, // Minecart-with-furnace, can be powered
+ // TODO: Other 1.5 features: hopper, tnt, dispenser, spawner
+ } ;
+
+ // cEntity overrides:
+ virtual void Initialize(cWorld * a_World) override;
+ virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+
+ ePayload GetPayload(void) const { return m_Payload; }
+
+protected:
+ ePayload m_Payload;
+
+ cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z);
+} ;
+
+
+
+
+
+class cEmptyMinecart :
+ public cMinecart
+{
+ typedef cMinecart super;
+
+public:
+ CLASS_PROTODEF(cEmptyMinecart);
+
+ cEmptyMinecart(double a_X, double a_Y, double a_Z);
+
+ // cEntity overrides:
+ virtual void OnRightClicked(cPlayer & a_Player) override;
+} ;
+
+
+
+
+
+class cMinecartWithChest :
+ public cMinecart
+{
+ typedef cMinecart super;
+
+public:
+ CLASS_PROTODEF(cMinecartWithChest);
+
+ /// Number of item slots in the chest
+ static const int NumSlots = 9 * 3;
+
+ cMinecartWithChest(double a_X, double a_Y, double a_Z);
+
+ const cItem & GetSlot(int a_Idx) const { return m_Items[a_Idx]; }
+ cItem & GetSlot(int a_Idx) { return m_Items[a_Idx]; }
+
+ void SetSlot(int a_Idx, const cItem & a_Item);
+
+protected:
+
+ /// The chest contents:
+ cItem m_Items[NumSlots];
+
+ // cEntity overrides:
+ virtual void OnRightClicked(cPlayer & a_Player) override;
+} ;
+
+
+
+
+
+class cMinecartWithFurnace :
+ public cMinecart
+{
+ typedef cMinecart super;
+
+public:
+ CLASS_PROTODEF(cMinecartWithFurnace);
+
+ cMinecartWithFurnace(double a_X, double a_Y, double a_Z);
+
+ // cEntity overrides:
+ virtual void OnRightClicked(cPlayer & a_Player) override;
+} ;
+
+
+
+
diff --git a/source/Mobs/Bat.h b/source/Mobs/Bat.h
index df6745fe5..8e4cde29f 100644
--- a/source/Mobs/Bat.h
+++ b/source/Mobs/Bat.h
@@ -1,27 +1,27 @@
-
-#pragma once
-
-#include "PassiveMonster.h"
-
-
-
-
-
-class cBat :
- public cPassiveMonster
-{
- typedef cPassiveMonster super;
-
-public:
- cBat(void) :
- // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here
- super("Bat", 65, "mob.bat.hurt", "mob.bat.death", 0.7, 0.7)
- {
- }
-
- CLASS_PROTODEF(cBat);
-} ;
-
-
-
-
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cBat :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cBat(void) :
+ // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here
+ super("Bat", 65, "mob.bat.hurt", "mob.bat.death", 0.7, 0.7)
+ {
+ }
+
+ CLASS_PROTODEF(cBat);
+} ;
+
+
+
+
diff --git a/source/Mobs/Blaze.cpp b/source/Mobs/Blaze.cpp
index 069ee8934..dbbccf417 100644
--- a/source/Mobs/Blaze.cpp
+++ b/source/Mobs/Blaze.cpp
@@ -1,27 +1,27 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "Blaze.h"
-
-
-
-
-
-cBlaze::cBlaze(void) :
- // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here
- super("Blaze", 61, "mob.blaze.hit", "mob.blaze.death", 0.7, 1.8)
-{
-}
-
-
-
-
-
-void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-{
- AddRandomDropItem(a_Drops, 0, 1, E_ITEM_BLAZE_ROD);
-}
-
-
-
-
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Blaze.h"
+
+
+
+
+
+cBlaze::cBlaze(void) :
+ // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here
+ super("Blaze", 61, "mob.blaze.hit", "mob.blaze.death", 0.7, 1.8)
+{
+}
+
+
+
+
+
+void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 1, E_ITEM_BLAZE_ROD);
+}
+
+
+
+
diff --git a/source/Mobs/Blaze.h b/source/Mobs/Blaze.h
index 8b7a15848..9df57530e 100644
--- a/source/Mobs/Blaze.h
+++ b/source/Mobs/Blaze.h
@@ -1,25 +1,25 @@
-
-#pragma once
-
-#include "AggressiveMonster.h"
-
-
-
-
-
-class cBlaze :
- public cAggressiveMonster
-{
- typedef cAggressiveMonster super;
-
-public:
- cBlaze(void);
-
- CLASS_PROTODEF(cBlaze);
-
- virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
-} ;
-
-
-
-
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cBlaze :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cBlaze(void);
+
+ CLASS_PROTODEF(cBlaze);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+} ;
+
+
+
+
diff --git a/source/Mobs/Magmacube.cpp b/source/Mobs/Magmacube.cpp
index 156a29607..0b9b57e3c 100644
--- a/source/Mobs/Magmacube.cpp
+++ b/source/Mobs/Magmacube.cpp
@@ -1,27 +1,27 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "Magmacube.h"
-
-
-
-
-
-cMagmacube::cMagmacube(int a_Size) :
- super("Magmacube", 62, "mob.magmacube.big", "mob.magmacube.big", 0.6 * a_Size, 0.6 * a_Size),
- m_Size(a_Size)
-{
-}
-
-
-
-
-
-void cMagmacube::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-{
- AddRandomDropItem(a_Drops, 0, 1, E_ITEM_MAGMA_CREAM);
-}
-
-
-
-
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Magmacube.h"
+
+
+
+
+
+cMagmacube::cMagmacube(int a_Size) :
+ super("Magmacube", 62, "mob.magmacube.big", "mob.magmacube.big", 0.6 * a_Size, 0.6 * a_Size),
+ m_Size(a_Size)
+{
+}
+
+
+
+
+
+void cMagmacube::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 1, E_ITEM_MAGMA_CREAM);
+}
+
+
+
+
diff --git a/source/Mobs/Magmacube.h b/source/Mobs/Magmacube.h
index da62b9615..e4df4f33d 100644
--- a/source/Mobs/Magmacube.h
+++ b/source/Mobs/Magmacube.h
@@ -1,31 +1,31 @@
-
-#pragma once
-
-#include "AggressiveMonster.h"
-
-
-
-
-
-class cMagmacube :
- public cAggressiveMonster
-{
- typedef cAggressiveMonster super;
-
-public:
- /// Creates a magmacube of the specified size; size is 1 .. 3, with 1 being the smallest
- cMagmacube(int a_Size);
-
- CLASS_PROTODEF(cMagmacube);
-
- virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
-
-protected:
-
- /// Size of the magmacube, 1 .. 3, with 1 being the smallest
- int m_Size;
-} ;
-
-
-
-
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cMagmacube :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ /// Creates a magmacube of the specified size; size is 1 .. 3, with 1 being the smallest
+ cMagmacube(int a_Size);
+
+ CLASS_PROTODEF(cMagmacube);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+
+protected:
+
+ /// Size of the magmacube, 1 .. 3, with 1 being the smallest
+ int m_Size;
+} ;
+
+
+
+
diff --git a/source/Mobs/Mooshroom.cpp b/source/Mobs/Mooshroom.cpp
index c85ceda3a..5d2c901ba 100644
--- a/source/Mobs/Mooshroom.cpp
+++ b/source/Mobs/Mooshroom.cpp
@@ -1,33 +1,33 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "Mooshroom.h"
-
-
-
-
-
-// TODO: Milk Cow
-
-
-
-
-
-cMooshroom::cMooshroom(void) :
- super("Mooshroom", 96, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3)
-{
-}
-
-
-
-
-
-void cMooshroom::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-{
- AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER);
- AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF);
-}
-
-
-
-
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Mooshroom.h"
+
+
+
+
+
+// TODO: Milk Cow
+
+
+
+
+
+cMooshroom::cMooshroom(void) :
+ super("Mooshroom", 96, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3)
+{
+}
+
+
+
+
+
+void cMooshroom::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER);
+ AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF);
+}
+
+
+
+
diff --git a/source/Mobs/Mooshroom.h b/source/Mobs/Mooshroom.h
index 20fc293b9..73f6348b6 100644
--- a/source/Mobs/Mooshroom.h
+++ b/source/Mobs/Mooshroom.h
@@ -1,25 +1,25 @@
-
-#pragma once
-
-#include "PassiveMonster.h"
-
-
-
-
-
-class cMooshroom :
- public cPassiveMonster
-{
- typedef cPassiveMonster super;
-
-public:
- cMooshroom(void);
-
- CLASS_PROTODEF(cMooshroom);
-
- virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
-} ;
-
-
-
-
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cMooshroom :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cMooshroom(void);
+
+ CLASS_PROTODEF(cMooshroom);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+} ;
+
+
+
+
diff --git a/source/Mobs/Ocelot.h b/source/Mobs/Ocelot.h
index 4d22575f2..98ea224e2 100644
--- a/source/Mobs/Ocelot.h
+++ b/source/Mobs/Ocelot.h
@@ -1,27 +1,27 @@
-
-#pragma once
-
-#include "PassiveMonster.h"
-
-
-
-
-
-class cOcelot :
- public cPassiveMonster
-{
- typedef cPassiveMonster super;
-
-public:
- cOcelot(void) :
- // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here
- super("Ocelot", 98, "mob.cat.hitt", "mob.cat.hitt", 0.9, 0.5)
- {
- }
-
- CLASS_PROTODEF(cOcelot);
-} ;
-
-
-
-
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cOcelot :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cOcelot(void) :
+ // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here
+ super("Ocelot", 98, "mob.cat.hitt", "mob.cat.hitt", 0.9, 0.5)
+ {
+ }
+
+ CLASS_PROTODEF(cOcelot);
+} ;
+
+
+
+
diff --git a/source/Mobs/Villager.cpp b/source/Mobs/Villager.cpp
index 0ec2cf76c..98e5276e1 100644
--- a/source/Mobs/Villager.cpp
+++ b/source/Mobs/Villager.cpp
@@ -1,17 +1,17 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "Villager.h"
-
-
-
-
-
-cVillager::cVillager(void) :
- super("Villager", 120, "", "", 0.6, 1.8)
-{
-}
-
-
-
-
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Villager.h"
+
+
+
+
+
+cVillager::cVillager(void) :
+ super("Villager", 120, "", "", 0.6, 1.8)
+{
+}
+
+
+
+
diff --git a/source/Mobs/Villager.h b/source/Mobs/Villager.h
index 4b50ed35e..92267a979 100644
--- a/source/Mobs/Villager.h
+++ b/source/Mobs/Villager.h
@@ -1,23 +1,23 @@
-
-#pragma once
-
-#include "PassiveMonster.h"
-
-
-
-
-
-class cVillager :
- public cPassiveMonster
-{
- typedef cPassiveMonster super;
-
-public:
- cVillager();
-
- CLASS_PROTODEF(cVillager);
-} ;
-
-
-
-
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cVillager :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cVillager();
+
+ CLASS_PROTODEF(cVillager);
+} ;
+
+
+
+
diff --git a/source/Mobs/Witch.cpp b/source/Mobs/Witch.cpp
index 66295ed1b..b29783853 100644
--- a/source/Mobs/Witch.cpp
+++ b/source/Mobs/Witch.cpp
@@ -1,32 +1,32 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "Witch.h"
-
-
-
-
-
-cWitch::cWitch(void) :
- super("Witch", 66, "", "", 0.6, 1.8)
-{
-}
-
-
-
-
-
-void cWitch::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-{
- AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLASS_BOTTLE);
- AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLOWSTONE_DUST);
- AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GUNPOWDER);
- AddRandomDropItem(a_Drops, 0, 6, E_ITEM_REDSTONE_DUST);
- AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SPIDER_EYE);
- AddRandomDropItem(a_Drops, 0, 6, E_ITEM_STICK);
- AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SUGAR);
-}
-
-
-
-
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Witch.h"
+
+
+
+
+
+cWitch::cWitch(void) :
+ super("Witch", 66, "", "", 0.6, 1.8)
+{
+}
+
+
+
+
+
+void cWitch::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLASS_BOTTLE);
+ AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLOWSTONE_DUST);
+ AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GUNPOWDER);
+ AddRandomDropItem(a_Drops, 0, 6, E_ITEM_REDSTONE_DUST);
+ AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SPIDER_EYE);
+ AddRandomDropItem(a_Drops, 0, 6, E_ITEM_STICK);
+ AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SUGAR);
+}
+
+
+
+
diff --git a/source/Mobs/Witch.h b/source/Mobs/Witch.h
index 2ca2c081f..ce0b49deb 100644
--- a/source/Mobs/Witch.h
+++ b/source/Mobs/Witch.h
@@ -1,25 +1,25 @@
-
-#pragma once
-
-#include "AggressiveMonster.h"
-
-
-
-
-
-class cWitch :
- public cAggressiveMonster
-{
- typedef cAggressiveMonster super;
-
-public:
- cWitch();
-
- CLASS_PROTODEF(cWitch);
-
- virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
-} ;
-
-
-
-
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cWitch :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cWitch();
+
+ CLASS_PROTODEF(cWitch);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+} ;
+
+
+
+
diff --git a/source/OSSupport/GZipFile.cpp b/source/OSSupport/GZipFile.cpp
index c57de5198..cbf6be6c4 100644
--- a/source/OSSupport/GZipFile.cpp
+++ b/source/OSSupport/GZipFile.cpp
@@ -1,107 +1,107 @@
-
-// GZipFile.cpp
-
-// Implements the cGZipFile class representing a RAII wrapper over zlib's GZip file routines
-
-#include "Globals.h"
-#include "GZipFile.h"
-
-
-
-
-
-cGZipFile::cGZipFile(void) :
- m_File(NULL)
-{
-}
-
-
-
-
-
-cGZipFile::~cGZipFile()
-{
- Close();
-}
-
-
-
-
-
-bool cGZipFile::Open(const AString & a_FileName, eMode a_Mode)
-{
- if (m_File != NULL)
- {
- ASSERT(!"A file is already open in this object");
- return false;
- }
- m_File = gzopen(a_FileName.c_str(), (a_Mode == fmRead) ? "r" : "w");
- m_Mode = a_Mode;
- return (m_File != NULL);
-}
-
-
-
-
-
-void cGZipFile::Close(void)
-{
- if (m_File != NULL)
- {
- gzclose(m_File);
- m_File = NULL;
- }
-}
-
-
-
-
-
-int cGZipFile::ReadRestOfFile(AString & a_Contents)
-{
- if (m_File == NULL)
- {
- ASSERT(!"No file has been opened");
- return -1;
- }
-
- if (m_Mode != fmRead)
- {
- ASSERT(!"Bad file mode, cannot read");
- return -1;
- }
-
- // Since the gzip format doesn't really support getting the uncompressed length, we need to read incrementally. Yuck!
- int NumBytesRead = 0;
- char Buffer[64 KiB];
- while ((NumBytesRead = gzread(m_File, Buffer, sizeof(Buffer))) > 0)
- {
- a_Contents.append(Buffer, NumBytesRead);
- }
- return NumBytesRead;
-}
-
-
-
-
-
-bool cGZipFile::Write(const char * a_Contents, int a_Size)
-{
- if (m_File == NULL)
- {
- ASSERT(!"No file has been opened");
- return false;
- }
-
- if (m_Mode != fmWrite)
- {
- ASSERT(!"Bad file mode, cannot write");
- return false;
- }
-
- return (gzwrite(m_File, a_Contents, a_Size) != 0);
-}
-
-
-
-
+
+// GZipFile.cpp
+
+// Implements the cGZipFile class representing a RAII wrapper over zlib's GZip file routines
+
+#include "Globals.h"
+#include "GZipFile.h"
+
+
+
+
+
+cGZipFile::cGZipFile(void) :
+ m_File(NULL)
+{
+}
+
+
+
+
+
+cGZipFile::~cGZipFile()
+{
+ Close();
+}
+
+
+
+
+
+bool cGZipFile::Open(const AString & a_FileName, eMode a_Mode)
+{
+ if (m_File != NULL)
+ {
+ ASSERT(!"A file is already open in this object");
+ return false;
+ }
+ m_File = gzopen(a_FileName.c_str(), (a_Mode == fmRead) ? "r" : "w");
+ m_Mode = a_Mode;
+ return (m_File != NULL);
+}
+
+
+
+
+
+void cGZipFile::Close(void)
+{
+ if (m_File != NULL)
+ {
+ gzclose(m_File);
+ m_File = NULL;
+ }
+}
+
+
+
+
+
+int cGZipFile::ReadRestOfFile(AString & a_Contents)
+{
+ if (m_File == NULL)
+ {
+ ASSERT(!"No file has been opened");
+ return -1;
+ }
+
+ if (m_Mode != fmRead)
+ {
+ ASSERT(!"Bad file mode, cannot read");
+ return -1;
+ }
+
+ // Since the gzip format doesn't really support getting the uncompressed length, we need to read incrementally. Yuck!
+ int NumBytesRead = 0;
+ char Buffer[64 KiB];
+ while ((NumBytesRead = gzread(m_File, Buffer, sizeof(Buffer))) > 0)
+ {
+ a_Contents.append(Buffer, NumBytesRead);
+ }
+ return NumBytesRead;
+}
+
+
+
+
+
+bool cGZipFile::Write(const char * a_Contents, int a_Size)
+{
+ if (m_File == NULL)
+ {
+ ASSERT(!"No file has been opened");
+ return false;
+ }
+
+ if (m_Mode != fmWrite)
+ {
+ ASSERT(!"Bad file mode, cannot write");
+ return false;
+ }
+
+ return (gzwrite(m_File, a_Contents, a_Size) != 0);
+}
+
+
+
+
diff --git a/source/OSSupport/GZipFile.h b/source/OSSupport/GZipFile.h
index f20b4d11e..e5aa68afa 100644
--- a/source/OSSupport/GZipFile.h
+++ b/source/OSSupport/GZipFile.h
@@ -1,52 +1,52 @@
-
-// GZipFile.h
-
-// Declares the cGZipFile class representing a RAII wrapper over zlib's GZip file routines
-
-
-
-
-
-#pragma once
-
-#include "zlib.h"
-
-
-
-
-
-class cGZipFile
-{
-public:
- enum eMode
- {
- fmRead, // Read-only. If the file doesn't exist, object will not be valid
- fmWrite, // Write-only. If the file already exists, it will be overwritten
- } ;
-
- cGZipFile(void);
- ~cGZipFile();
-
- /// Opens the file. Returns true if successful. Fails if a file has already been opened through this object.
- bool Open(const AString & a_FileName, eMode a_Mode);
-
- /// Closes the file, flushing all buffers. This object may be then reused for a different file and / or mode
- void Close(void);
-
- /// Reads the rest of the file and decompresses it into a_Contents. Returns the number of decompressed bytes, <0 for error
- int ReadRestOfFile(AString & a_Contents);
-
- /// Writes a_Contents into file, compressing it along the way. Returns true if successful. Multiple writes are supported.
- bool Write(const AString & a_Contents) { return Write(a_Contents.data(), (int)(a_Contents.size())); }
-
- bool Write(const char * a_Data, int a_Size);
-
-protected:
- gzFile m_File;
- eMode m_Mode;
-} ;
-
-
-
-
-
+
+// GZipFile.h
+
+// Declares the cGZipFile class representing a RAII wrapper over zlib's GZip file routines
+
+
+
+
+
+#pragma once
+
+#include "zlib.h"
+
+
+
+
+
+class cGZipFile
+{
+public:
+ enum eMode
+ {
+ fmRead, // Read-only. If the file doesn't exist, object will not be valid
+ fmWrite, // Write-only. If the file already exists, it will be overwritten
+ } ;
+
+ cGZipFile(void);
+ ~cGZipFile();
+
+ /// Opens the file. Returns true if successful. Fails if a file has already been opened through this object.
+ bool Open(const AString & a_FileName, eMode a_Mode);
+
+ /// Closes the file, flushing all buffers. This object may be then reused for a different file and / or mode
+ void Close(void);
+
+ /// Reads the rest of the file and decompresses it into a_Contents. Returns the number of decompressed bytes, <0 for error
+ int ReadRestOfFile(AString & a_Contents);
+
+ /// Writes a_Contents into file, compressing it along the way. Returns true if successful. Multiple writes are supported.
+ bool Write(const AString & a_Contents) { return Write(a_Contents.data(), (int)(a_Contents.size())); }
+
+ bool Write(const char * a_Data, int a_Size);
+
+protected:
+ gzFile m_File;
+ eMode m_Mode;
+} ;
+
+
+
+
+
diff --git a/source/OSSupport/ListenThread.cpp b/source/OSSupport/ListenThread.cpp
index 89aa6ea75..4542326ef 100644
--- a/source/OSSupport/ListenThread.cpp
+++ b/source/OSSupport/ListenThread.cpp
@@ -1,231 +1,231 @@
-
-// ListenThread.cpp
-
-// Implements the cListenThread class representing the thread that listens for client connections
-
-#include "Globals.h"
-#include "ListenThread.h"
-
-
-
-
-
-cListenThread::cListenThread(cCallback & a_Callback, cSocket::eFamily a_Family, const AString & a_ServiceName) :
- super(Printf("ListenThread %s", a_ServiceName.c_str())),
- m_Callback(a_Callback),
- m_Family(a_Family),
- m_ShouldReuseAddr(false),
- m_ServiceName(a_ServiceName)
-{
-}
-
-
-
-
-
-cListenThread::~cListenThread()
-{
- // TODO
-}
-
-
-
-
-
-bool cListenThread::Initialize(const AString & a_PortsString)
-{
- ASSERT(m_Sockets.empty()); // Not yet started
-
- if (!CreateSockets(a_PortsString))
- {
- return false;
- }
-
- return true;
-}
-
-
-
-
-
-bool cListenThread::Start(void)
-{
- if (m_Sockets.empty())
- {
- // There are no sockets listening, either forgotten to initialize or the user specified no listening ports
- // Report as successful, though
- return true;
- }
- return super::Start();
-}
-
-
-
-
-
-void cListenThread::Stop(void)
-{
- if (m_Sockets.empty())
- {
- // No sockets means no thread was running in the first place
- return;
- }
-
- m_ShouldTerminate = true;
-
- // Close one socket to wake the thread up from the select() call
- m_Sockets[0].CloseSocket();
-
- // Wait for the thread to finish
- super::Wait();
-
- // Clean up all sockets
- m_Sockets.clear();
-}
-
-
-
-
-
-void cListenThread::SetReuseAddr(bool a_Reuse)
-{
- ASSERT(m_Sockets.empty()); // Must not have been Initialize()d yet
-
- m_ShouldReuseAddr = a_Reuse;
-}
-
-
-
-
-
-bool cListenThread::CreateSockets(const AString & a_PortsString)
-{
- AStringVector Ports = StringSplitAndTrim(a_PortsString, ",");
-
- if (Ports.empty())
- {
- return false;
- }
-
- AString FamilyStr = m_ServiceName;
- switch (m_Family)
- {
- case cSocket::IPv4: FamilyStr.append(" IPv4"); break;
- case cSocket::IPv6: FamilyStr.append(" IPv6"); break;
- default:
- {
- ASSERT(!"Unknown address family");
- break;
- }
- }
-
- for (AStringVector::const_iterator itr = Ports.begin(), end = Ports.end(); itr != end; ++itr)
- {
- int Port = atoi(itr->c_str());
- if ((Port <= 0) || (Port > 65535))
- {
- LOGWARNING("%s: Invalid port specified: \"%s\".", FamilyStr.c_str(), itr->c_str());
- continue;
- }
- m_Sockets.push_back(cSocket::CreateSocket(m_Family));
- if (!m_Sockets.back().IsValid())
- {
- LOGWARNING("%s: Cannot create listening socket for port %d: \"%s\"", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str());
- m_Sockets.pop_back();
- continue;
- }
-
- if (m_ShouldReuseAddr)
- {
- if (!m_Sockets.back().SetReuseAddress())
- {
- LOG("%s: Port %d cannot reuse addr, syscall failed: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str());
- }
- }
-
- // Bind to port:
- bool res = false;
- switch (m_Family)
- {
- case cSocket::IPv4: res = m_Sockets.back().BindToAnyIPv4(Port); break;
- case cSocket::IPv6: res = m_Sockets.back().BindToAnyIPv6(Port); break;
- default:
- {
- ASSERT(!"Unknown address family");
- res = false;
- }
- }
- if (!res)
- {
- LOGWARNING("%s: Cannot bind port %d: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str());
- m_Sockets.pop_back();
- continue;
- }
-
- if (!m_Sockets.back().Listen())
- {
- LOGWARNING("%s: Cannot listen on port %d: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str());
- m_Sockets.pop_back();
- continue;
- }
-
- LOGINFO("%s: Port %d is open for connections", FamilyStr.c_str(), Port);
- } // for itr - Ports[]
-
- return !(m_Sockets.empty());
-}
-
-
-
-
-
-void cListenThread::Execute(void)
-{
- if (m_Sockets.empty())
- {
- LOGD("Empty cListenThread, ending thread now.");
- return;
- }
-
- // Find the highest socket number:
- cSocket::xSocket Highest = m_Sockets[0].GetSocket();
- for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr)
- {
- if (itr->GetSocket() > Highest)
- {
- Highest = itr->GetSocket();
- }
- } // for itr - m_Sockets[]
-
- while (!m_ShouldTerminate)
- {
- // Put all sockets into a FD set:
- fd_set fdRead;
- FD_ZERO(&fdRead);
- for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr)
- {
- FD_SET(itr->GetSocket(), &fdRead);
- } // for itr - m_Sockets[]
-
- timeval tv; // On Linux select() doesn't seem to wake up when socket is closed, so let's kinda busy-wait:
- tv.tv_sec = 1;
- tv.tv_usec = 0;
- if (select(Highest + 1, &fdRead, NULL, NULL, &tv) == -1)
- {
- LOG("select(R) call failed in cListenThread: \"%s\"", cSocket::GetLastErrorString().c_str());
- continue;
- }
- for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr)
- {
- if (itr->IsValid() && FD_ISSET(itr->GetSocket(), &fdRead))
- {
- cSocket Client = (m_Family == cSocket::IPv4) ? itr->AcceptIPv4() : itr->AcceptIPv6();
- m_Callback.OnConnectionAccepted(Client);
- }
- } // for itr - m_Sockets[]
- } // while (!m_ShouldTerminate)
-}
-
-
-
-
+
+// ListenThread.cpp
+
+// Implements the cListenThread class representing the thread that listens for client connections
+
+#include "Globals.h"
+#include "ListenThread.h"
+
+
+
+
+
+cListenThread::cListenThread(cCallback & a_Callback, cSocket::eFamily a_Family, const AString & a_ServiceName) :
+ super(Printf("ListenThread %s", a_ServiceName.c_str())),
+ m_Callback(a_Callback),
+ m_Family(a_Family),
+ m_ShouldReuseAddr(false),
+ m_ServiceName(a_ServiceName)
+{
+}
+
+
+
+
+
+cListenThread::~cListenThread()
+{
+ // TODO
+}
+
+
+
+
+
+bool cListenThread::Initialize(const AString & a_PortsString)
+{
+ ASSERT(m_Sockets.empty()); // Not yet started
+
+ if (!CreateSockets(a_PortsString))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+
+
+
+bool cListenThread::Start(void)
+{
+ if (m_Sockets.empty())
+ {
+ // There are no sockets listening, either forgotten to initialize or the user specified no listening ports
+ // Report as successful, though
+ return true;
+ }
+ return super::Start();
+}
+
+
+
+
+
+void cListenThread::Stop(void)
+{
+ if (m_Sockets.empty())
+ {
+ // No sockets means no thread was running in the first place
+ return;
+ }
+
+ m_ShouldTerminate = true;
+
+ // Close one socket to wake the thread up from the select() call
+ m_Sockets[0].CloseSocket();
+
+ // Wait for the thread to finish
+ super::Wait();
+
+ // Clean up all sockets
+ m_Sockets.clear();
+}
+
+
+
+
+
+void cListenThread::SetReuseAddr(bool a_Reuse)
+{
+ ASSERT(m_Sockets.empty()); // Must not have been Initialize()d yet
+
+ m_ShouldReuseAddr = a_Reuse;
+}
+
+
+
+
+
+bool cListenThread::CreateSockets(const AString & a_PortsString)
+{
+ AStringVector Ports = StringSplitAndTrim(a_PortsString, ",");
+
+ if (Ports.empty())
+ {
+ return false;
+ }
+
+ AString FamilyStr = m_ServiceName;
+ switch (m_Family)
+ {
+ case cSocket::IPv4: FamilyStr.append(" IPv4"); break;
+ case cSocket::IPv6: FamilyStr.append(" IPv6"); break;
+ default:
+ {
+ ASSERT(!"Unknown address family");
+ break;
+ }
+ }
+
+ for (AStringVector::const_iterator itr = Ports.begin(), end = Ports.end(); itr != end; ++itr)
+ {
+ int Port = atoi(itr->c_str());
+ if ((Port <= 0) || (Port > 65535))
+ {
+ LOGWARNING("%s: Invalid port specified: \"%s\".", FamilyStr.c_str(), itr->c_str());
+ continue;
+ }
+ m_Sockets.push_back(cSocket::CreateSocket(m_Family));
+ if (!m_Sockets.back().IsValid())
+ {
+ LOGWARNING("%s: Cannot create listening socket for port %d: \"%s\"", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str());
+ m_Sockets.pop_back();
+ continue;
+ }
+
+ if (m_ShouldReuseAddr)
+ {
+ if (!m_Sockets.back().SetReuseAddress())
+ {
+ LOG("%s: Port %d cannot reuse addr, syscall failed: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str());
+ }
+ }
+
+ // Bind to port:
+ bool res = false;
+ switch (m_Family)
+ {
+ case cSocket::IPv4: res = m_Sockets.back().BindToAnyIPv4(Port); break;
+ case cSocket::IPv6: res = m_Sockets.back().BindToAnyIPv6(Port); break;
+ default:
+ {
+ ASSERT(!"Unknown address family");
+ res = false;
+ }
+ }
+ if (!res)
+ {
+ LOGWARNING("%s: Cannot bind port %d: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str());
+ m_Sockets.pop_back();
+ continue;
+ }
+
+ if (!m_Sockets.back().Listen())
+ {
+ LOGWARNING("%s: Cannot listen on port %d: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str());
+ m_Sockets.pop_back();
+ continue;
+ }
+
+ LOGINFO("%s: Port %d is open for connections", FamilyStr.c_str(), Port);
+ } // for itr - Ports[]
+
+ return !(m_Sockets.empty());
+}
+
+
+
+
+
+void cListenThread::Execute(void)
+{
+ if (m_Sockets.empty())
+ {
+ LOGD("Empty cListenThread, ending thread now.");
+ return;
+ }
+
+ // Find the highest socket number:
+ cSocket::xSocket Highest = m_Sockets[0].GetSocket();
+ for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr)
+ {
+ if (itr->GetSocket() > Highest)
+ {
+ Highest = itr->GetSocket();
+ }
+ } // for itr - m_Sockets[]
+
+ while (!m_ShouldTerminate)
+ {
+ // Put all sockets into a FD set:
+ fd_set fdRead;
+ FD_ZERO(&fdRead);
+ for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr)
+ {
+ FD_SET(itr->GetSocket(), &fdRead);
+ } // for itr - m_Sockets[]
+
+ timeval tv; // On Linux select() doesn't seem to wake up when socket is closed, so let's kinda busy-wait:
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ if (select(Highest + 1, &fdRead, NULL, NULL, &tv) == -1)
+ {
+ LOG("select(R) call failed in cListenThread: \"%s\"", cSocket::GetLastErrorString().c_str());
+ continue;
+ }
+ for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr)
+ {
+ if (itr->IsValid() && FD_ISSET(itr->GetSocket(), &fdRead))
+ {
+ cSocket Client = (m_Family == cSocket::IPv4) ? itr->AcceptIPv4() : itr->AcceptIPv6();
+ m_Callback.OnConnectionAccepted(Client);
+ }
+ } // for itr - m_Sockets[]
+ } // while (!m_ShouldTerminate)
+}
+
+
+
+
diff --git a/source/OSSupport/ListenThread.h b/source/OSSupport/ListenThread.h
index c29140ed3..4e337d814 100644
--- a/source/OSSupport/ListenThread.h
+++ b/source/OSSupport/ListenThread.h
@@ -1,83 +1,83 @@
-
-// ListenThread.h
-
-// Declares the cListenThread class representing the thread that listens for client connections
-
-
-
-
-
-#pragma once
-
-#include "IsThread.h"
-#include "Socket.h"
-
-
-
-
-
-// fwd:
-class cServer;
-
-
-
-
-
-class cListenThread :
- public cIsThread
-{
- typedef cIsThread super;
-
-public:
- /// Used as the callback for connection events
- class cCallback
- {
- public:
- /// This callback is called whenever a socket connection is accepted
- virtual void OnConnectionAccepted(cSocket & a_Socket) = 0;
- } ;
-
- cListenThread(cCallback & a_Callback, cSocket::eFamily a_Family, const AString & a_ServiceName = "");
- ~cListenThread();
-
- /// Creates all the sockets, returns trus if successful, false if not.
- bool Initialize(const AString & a_PortsString);
-
- bool Start(void);
-
- void Stop(void);
-
- /// Call before Initialize() to set the "reuse" flag on the sockets
- void SetReuseAddr(bool a_Reuse = true);
-
-protected:
- typedef std::vector<cSocket> cSockets;
-
- /// The callback which to notify of incoming connections
- cCallback & m_Callback;
-
- /// Socket address family to use
- cSocket::eFamily m_Family;
-
- /// Sockets that are being monitored
- cSockets m_Sockets;
-
- /// If set to true, the SO_REUSEADDR socket option is set to true
- bool m_ShouldReuseAddr;
-
- /// Name of the service that's listening on the ports; for logging purposes only
- AString m_ServiceName;
-
-
- /** Fills in m_Sockets with individual sockets, each for one port specified in a_PortsString.
- Returns true if successful and at least one socket has been created
- */
- bool CreateSockets(const AString & a_PortsString);
-
- // cIsThread override:
- virtual void Execute(void) override;
-} ;
-
-
-
-
+
+// ListenThread.h
+
+// Declares the cListenThread class representing the thread that listens for client connections
+
+
+
+
+
+#pragma once
+
+#include "IsThread.h"
+#include "Socket.h"
+
+
+
+
+
+// fwd:
+class cServer;
+
+
+
+
+
+class cListenThread :
+ public cIsThread
+{
+ typedef cIsThread super;
+
+public:
+ /// Used as the callback for connection events
+ class cCallback
+ {
+ public:
+ /// This callback is called whenever a socket connection is accepted
+ virtual void OnConnectionAccepted(cSocket & a_Socket) = 0;
+ } ;
+
+ cListenThread(cCallback & a_Callback, cSocket::eFamily a_Family, const AString & a_ServiceName = "");
+ ~cListenThread();
+
+ /// Creates all the sockets, returns trus if successful, false if not.
+ bool Initialize(const AString & a_PortsString);
+
+ bool Start(void);
+
+ void Stop(void);
+
+ /// Call before Initialize() to set the "reuse" flag on the sockets
+ void SetReuseAddr(bool a_Reuse = true);
+
+protected:
+ typedef std::vector<cSocket> cSockets;
+
+ /// The callback which to notify of incoming connections
+ cCallback & m_Callback;
+
+ /// Socket address family to use
+ cSocket::eFamily m_Family;
+
+ /// Sockets that are being monitored
+ cSockets m_Sockets;
+
+ /// If set to true, the SO_REUSEADDR socket option is set to true
+ bool m_ShouldReuseAddr;
+
+ /// Name of the service that's listening on the ports; for logging purposes only
+ AString m_ServiceName;
+
+
+ /** Fills in m_Sockets with individual sockets, each for one port specified in a_PortsString.
+ Returns true if successful and at least one socket has been created
+ */
+ bool CreateSockets(const AString & a_PortsString);
+
+ // cIsThread override:
+ virtual void Execute(void) override;
+} ;
+
+
+
+
diff --git a/source/Player.cpp b/source/Player.cpp
index 83181dda6..bbf39f789 100644
--- a/source/Player.cpp
+++ b/source/Player.cpp
@@ -1,1326 +1,1424 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "Player.h"
-#include "Server.h"
-#include "ClientHandle.h"
-#include "UI/Window.h"
-#include "UI/WindowOwner.h"
-#include "World.h"
-#include "Pickup.h"
-#include "PluginManager.h"
-#include "BlockEntities/BlockEntity.h"
-#include "GroupManager.h"
-#include "Group.h"
-#include "ChatColor.h"
-#include "Item.h"
-#include "Tracer.h"
-#include "Root.h"
-#include "OSSupport/MakeDir.h"
-#include "OSSupport/Timer.h"
-#include "MersenneTwister.h"
-#include "Chunk.h"
-
-#include "Vector3d.h"
-#include "Vector3f.h"
-
-#include "../iniFile/iniFile.h"
-#include <json/json.h>
-
-#define float2int(x) ((x)<0 ? ((int)(x))-1 : (int)(x))
-
-
-
-
-
-cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
- : super(etPlayer, 0.6, 1.8)
- , m_GameMode(eGameMode_NotSet)
- , m_IP("")
- , m_LastBlockActionTime( 0 )
- , m_LastBlockActionCnt( 0 )
- , m_bVisible( true )
- , m_LastGroundHeight( 0 )
- , m_bTouchGround( false )
- , m_Stance( 0.0 )
- , m_Inventory(*this)
- , m_CurrentWindow(NULL)
- , m_InventoryWindow(NULL)
- , m_TimeLastPickupCheck( 0.f )
- , m_Color('-')
- , m_ClientHandle( a_Client )
- , m_FoodLevel(20)
- , m_FoodSaturationLevel(5)
- , m_FoodTickTimer(0)
- , m_FoodExhaustionLevel(0)
- , m_FoodPoisonedTicksRemaining(0)
- , m_NormalMaxSpeed(0.1)
- , m_SprintingMaxSpeed(0.13)
- , m_IsCrouched(false)
- , m_IsSprinting(false)
-{
- LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d",
- a_PlayerName.c_str(), a_Client->GetIPString().c_str(),
- this, GetUniqueID()
- );
-
- m_InventoryWindow = new cInventoryWindow(*this);
- m_CurrentWindow = m_InventoryWindow;
- m_InventoryWindow->OpenedByPlayer(*this);
-
- SetMaxHealth(20);
-
- cTimer t1;
- m_LastPlayerListTime = t1.GetNowTime();
-
- m_TimeLastTeleportPacket = 0;
- m_TimeLastPickupCheck = 0;
-
- m_PlayerName = a_PlayerName;
- m_bDirtyPosition = true; // So chunks are streamed to player at spawn
-
- if (!LoadFromDisk())
- {
- m_Inventory.Clear();
- SetPosX(cRoot::Get()->GetDefaultWorld()->GetSpawnX());
- SetPosY(cRoot::Get()->GetDefaultWorld()->GetSpawnY());
- SetPosZ(cRoot::Get()->GetDefaultWorld()->GetSpawnZ());
-
- LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}",
- a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ()
- );
- }
- m_LastJumpHeight = (float)(GetPosY());
- m_LastGroundHeight = (float)(GetPosY());
- m_Stance = GetPosY() + 1.62;
-}
-
-
-
-
-
-cPlayer::~cPlayer(void)
-{
- LOG("Deleting cPlayer \"%s\" at %p, ID %d", m_PlayerName.c_str(), this, GetUniqueID());
-
- SaveToDisk();
-
- m_World->RemovePlayer( this );
-
- m_ClientHandle = NULL;
-
- delete m_InventoryWindow;
-
- LOGD("Player %p deleted", this);
-}
-
-
-
-
-
-void cPlayer::Initialize(cWorld * a_World)
-{
- super::Initialize(a_World);
- GetWorld()->AddPlayer(this);
-}
-
-
-
-
-
-void cPlayer::Destroyed()
-{
- CloseWindow(false);
- m_ClientHandle = NULL;
-}
-
-
-
-
-
-void cPlayer::SpawnOn(cClientHandle & a_Client)
-{
- /*
- LOGD("cPlayer::SpawnOn(%s) for \"%s\" at pos {%.2f, %.2f, %.2f}",
- a_Client.GetUsername().c_str(), m_PlayerName.c_str(), m_Pos.x, m_Pos.y, m_Pos.z
- );
- */
-
- if (m_bVisible && (m_ClientHandle != (&a_Client)))
- {
- a_Client.SendPlayerSpawn(*this);
- a_Client.SendEntityHeadLook(*this);
- a_Client.SendEntityEquipment(*this, 0, m_Inventory.GetEquippedItem() );
- a_Client.SendEntityEquipment(*this, 1, m_Inventory.GetEquippedBoots() );
- a_Client.SendEntityEquipment(*this, 2, m_Inventory.GetEquippedLeggings() );
- a_Client.SendEntityEquipment(*this, 3, m_Inventory.GetEquippedChestplate() );
- a_Client.SendEntityEquipment(*this, 4, m_Inventory.GetEquippedHelmet() );
- }
-}
-
-
-
-
-
-void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
-{
- if (!m_ClientHandle->IsPlaying())
- {
- // We're not yet in the game, ignore everything
- return;
- }
-
- super::Tick(a_Dt, a_Chunk);
- if (m_bDirtyPosition)
- {
- // Apply food exhaustion from movement:
- ApplyFoodExhaustionFromMovement(a_Chunk);
-
- cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*this);
- BroadcastMovementUpdate(m_ClientHandle);
- m_ClientHandle->StreamChunks();
- }
- else
- {
- BroadcastMovementUpdate(m_ClientHandle);
- }
-
- if (m_Health > 0) // make sure player is alive
- {
- m_World->CollectPickupsByPlayer(this);
-
- HandleFood();
- }
-
- // Send Player List (Once per m_LastPlayerListTime/1000 ms)
- cTimer t1;
- if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime())
- {
- m_World->SendPlayerList(this);
- m_LastPlayerListTime = t1.GetNowTime();
- }
-}
-
-
-
-
-
-void cPlayer::SetTouchGround(bool a_bTouchGround)
-{
- // If just
- m_bTouchGround = a_bTouchGround;
-
- if (!m_bTouchGround)
- {
- if (GetPosY() > m_LastJumpHeight)
- {
- m_LastJumpHeight = (float)GetPosY();
- }
- cWorld * World = GetWorld();
- if ((GetPosY() >= 0) && (GetPosY() < 256))
- {
- BLOCKTYPE BlockType = World->GetBlock( float2int(GetPosX()), float2int(GetPosY()), float2int(GetPosZ()) );
- if (BlockType != E_BLOCK_AIR)
- {
- // LOGD("TouchGround set to true by server");
- m_bTouchGround = true;
- }
- if (
- (BlockType == E_BLOCK_WATER) ||
- (BlockType == E_BLOCK_STATIONARY_WATER) ||
- (BlockType == E_BLOCK_LADDER) ||
- (BlockType == E_BLOCK_VINES)
- )
- {
- // LOGD("Water / Ladder / Torch");
- m_LastGroundHeight = (float)GetPosY();
- }
- }
- }
-
- if (m_bTouchGround)
- {
- float Dist = (float)(m_LastGroundHeight - floor(GetPosY()));
- int Damage = (int)(Dist - 3.f);
- if(m_LastJumpHeight > m_LastGroundHeight) Damage++;
- m_LastJumpHeight = (float)GetPosY();
- if (Damage > 0)
- {
- TakeDamage(dtFalling, NULL, Damage, Damage, 0);
- }
-
- m_LastGroundHeight = (float)GetPosY();
- }
-}
-
-
-
-
-
-void cPlayer::Heal(int a_Health)
-{
- if (m_Health < GetMaxHealth())
- {
- m_Health = (short)std::min((int)a_Health + m_Health, (int)GetMaxHealth());
- SendHealth();
- }
-}
-
-
-
-
-
-void cPlayer::SetFoodLevel(int a_FoodLevel)
-{
- m_FoodLevel = std::max(0, std::min(a_FoodLevel, (int)MAX_FOOD_LEVEL));
- SendHealth();
-}
-
-
-
-
-
-void cPlayer::SetFoodSaturationLevel(double a_FoodSaturationLevel)
-{
- m_FoodSaturationLevel = std::max(0.0, std::min(a_FoodSaturationLevel, (double)m_FoodLevel));
-}
-
-
-
-
-
-void cPlayer::SetFoodTickTimer(int a_FoodTickTimer)
-{
- m_FoodTickTimer = a_FoodTickTimer;
-}
-
-
-
-
-
-void cPlayer::SetFoodExhaustionLevel(double a_FoodSaturationLevel)
-{
- m_FoodExhaustionLevel = std::max(0.0, std::min(a_FoodSaturationLevel, 4.0));
-}
-
-
-
-
-
-void cPlayer::SetFoodPoisonedTicksRemaining(int a_FoodPoisonedTicksRemaining)
-{
- m_FoodPoisonedTicksRemaining = a_FoodPoisonedTicksRemaining;
-}
-
-
-
-
-
-bool cPlayer::Feed(int a_Food, double a_Saturation)
-{
- if (m_FoodLevel >= MAX_FOOD_LEVEL)
- {
- return false;
- }
-
- m_FoodLevel = std::min(a_Food + m_FoodLevel, (int)MAX_FOOD_LEVEL);
- m_FoodSaturationLevel = std::min(m_FoodSaturationLevel + a_Saturation, (double)m_FoodLevel);
-
- SendHealth();
- return true;
-}
-
-
-
-
-
-void cPlayer::FoodPoison(int a_NumTicks)
-{
- bool HasBeenFoodPoisoned = (m_FoodPoisonedTicksRemaining > 0);
- m_FoodPoisonedTicksRemaining = std::max(m_FoodPoisonedTicksRemaining, a_NumTicks);
- if (!HasBeenFoodPoisoned)
- {
- // TODO: Send the poisoning indication to the client - how?
- SendHealth();
- }
-}
-
-
-
-
-
-void cPlayer::SendHealth(void)
-{
- if (m_ClientHandle != NULL)
- {
- m_ClientHandle->SendHealth();
- }
-}
-
-
-
-
-
-void cPlayer::ClearInventoryPaintSlots(void)
-{
- // Clear the list of slots that are being inventory-painted. Used by cWindow only
- m_InventoryPaintSlots.clear();
-}
-
-
-
-
-
-void cPlayer::AddInventoryPaintSlot(int a_SlotNum)
-{
- // Add a slot to the list for inventory painting. Used by cWindow only
- m_InventoryPaintSlots.push_back(a_SlotNum);
-}
-
-
-
-
-
-const cSlotNums & cPlayer::GetInventoryPaintSlots(void) const
-{
- // Return the list of slots currently stored for inventory painting. Used by cWindow only
- return m_InventoryPaintSlots;
-}
-
-
-
-
-
-double cPlayer::GetMaxSpeed(void) const
-{
- return m_IsSprinting ? m_SprintingMaxSpeed : m_NormalMaxSpeed;
-}
-
-
-
-
-
-void cPlayer::SetNormalMaxSpeed(double a_Speed)
-{
- m_NormalMaxSpeed = a_Speed;
- if (!m_IsSprinting)
- {
- m_ClientHandle->SendPlayerMaxSpeed();
- }
-}
-
-
-
-
-
-void cPlayer::SetSprintingMaxSpeed(double a_Speed)
-{
- m_SprintingMaxSpeed = a_Speed;
- if (m_IsSprinting)
- {
- m_ClientHandle->SendPlayerMaxSpeed();
- }
-}
-
-
-
-
-
-void cPlayer::SetCrouch(bool a_IsCrouched)
-{
- // Set the crouch status, broadcast to all visible players
-
- if (a_IsCrouched == m_IsCrouched)
- {
- // No change
- return;
- }
- m_IsCrouched = a_IsCrouched;
- m_World->BroadcastEntityMetadata(*this);
-}
-
-
-
-
-
-void cPlayer::SetSprint(bool a_IsSprinting)
-{
- if (a_IsSprinting == m_IsSprinting)
- {
- // No change
- return;
- }
-
- m_IsSprinting = a_IsSprinting;
- m_ClientHandle->SendPlayerMaxSpeed();
-}
-
-
-
-
-
-void cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
-{
- if (m_GameMode == eGameMode_Creative)
- {
- // No damage / health in creative mode
- return;
- }
-
- super::DoTakeDamage(a_TDI);
-
- // Any kind of damage adds food exhaustion
- AddFoodExhaustion(0.3f);
-
- SendHealth();
-}
-
-
-
-
-
-void cPlayer::KilledBy(cEntity * a_Killer)
-{
- super::KilledBy(a_Killer);
-
- if (m_Health > 0)
- {
- return; // not dead yet =]
- }
-
- m_bVisible = false; // So new clients don't see the player
-
- // Puke out all the items
- cItems Pickups;
- m_Inventory.CopyToItems(Pickups);
- m_Inventory.Clear();
- m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10);
- SaveToDisk(); // Save it, yeah the world is a tough place !
-}
-
-
-
-
-
-void cPlayer::Respawn(void)
-{
- m_Health = GetMaxHealth();
- m_FoodLevel = 20;
-
- m_ClientHandle->SendRespawn();
-
- // Extinguish the fire:
- StopBurning();
-
- TeleportToCoords(GetWorld()->GetSpawnX(), GetWorld()->GetSpawnY(), GetWorld()->GetSpawnZ());
-
- SetVisible(true);
-}
-
-
-
-
-
-double cPlayer::GetEyeHeight(void) const
-{
- return m_Stance;
-}
-
-
-
-
-Vector3d cPlayer::GetEyePosition(void) const
-{
- return Vector3d( GetPosX(), m_Stance, GetPosZ() );
-}
-
-
-
-
-
-void cPlayer::OpenWindow(cWindow * a_Window)
-{
- if (a_Window != m_CurrentWindow)
- {
- CloseWindow(false);
- }
- a_Window->OpenedByPlayer(*this);
- m_CurrentWindow = a_Window;
- a_Window->SendWholeWindow(*GetClientHandle());
-}
-
-
-
-
-
-void cPlayer::CloseWindow(bool a_CanRefuse)
-{
- if (m_CurrentWindow == NULL)
- {
- m_CurrentWindow = m_InventoryWindow;
- return;
- }
-
- if (m_CurrentWindow->ClosedByPlayer(*this, a_CanRefuse) || !a_CanRefuse)
- {
- // Close accepted, go back to inventory window (the default):
- m_CurrentWindow = m_InventoryWindow;
- }
- else
- {
- // Re-open the window
- m_CurrentWindow->OpenedByPlayer(*this);
- m_CurrentWindow->SendWholeWindow(*GetClientHandle());
- }
-}
-
-
-
-
-
-void cPlayer::CloseWindowIfID(char a_WindowID, bool a_CanRefuse)
-{
- if ((m_CurrentWindow == NULL) || (m_CurrentWindow->GetWindowID() != a_WindowID))
- {
- return;
- }
- CloseWindow();
-}
-
-
-
-
-
-void cPlayer::SetLastBlockActionTime()
-{
- if (m_World != NULL)
- {
- m_LastBlockActionTime = m_World->GetWorldAge() / 20.0f;
- }
-}
-
-
-
-
-
-void cPlayer::SetLastBlockActionCnt( int a_LastBlockActionCnt )
-{
- m_LastBlockActionCnt = a_LastBlockActionCnt;
-}
-
-
-
-
-
-void cPlayer::SetGameMode(eGameMode a_GameMode)
-{
- if ((a_GameMode >= 3) || (a_GameMode < 0))
- {
- LOGWARNING("%s: Setting invalid gamemode: %d", GetName().c_str(), a_GameMode);
- return;
- }
-
- if (m_GameMode == a_GameMode)
- {
- // Gamemode already set
- return;
- }
-
- m_GameMode = a_GameMode;
- m_ClientHandle->SendGameMode(a_GameMode);
-}
-
-
-
-
-
-void cPlayer::LoginSetGameMode( eGameMode a_GameMode )
-{
- m_GameMode = a_GameMode;
-}
-
-
-
-
-
-void cPlayer::SetIP(const AString & a_IP)
-{
- m_IP = a_IP;
-}
-
-
-
-
-
-void cPlayer::SendMessage(const AString & a_Message)
-{
- m_ClientHandle->SendChat(a_Message);
-}
-
-
-
-
-
-void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ)
-{
- SetPosition( a_PosX, a_PosY, a_PosZ );
-
- m_World->BroadcastTeleportEntity(*this, GetClientHandle());
- m_ClientHandle->SendPlayerMoveLook();
-}
-
-
-
-
-
-void cPlayer::MoveTo( const Vector3d & a_NewPos )
-{
- if ((a_NewPos.y < -990) && (GetPosY() > -100))
- {
- // When attached to an entity, the client sends position packets with weird coords:
- // Y = -999 and X, Z = attempting to create speed, usually up to 0.03
- // We cannot test m_AttachedTo, because when deattaching, the server thinks the client is already deattached while
- // the client may still send more of these nonsensical packets.
- if (m_AttachedTo != NULL)
- {
- Vector3d AddSpeed(a_NewPos);
- AddSpeed.y = 0;
- m_AttachedTo->AddSpeed(AddSpeed);
- }
- return;
- }
-
- // TODO: should do some checks to see if player is not moving through terrain
- // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too
-
- SetPosition( a_NewPos );
- SetStance(a_NewPos.y + 1.62);
-}
-
-
-
-
-
-void cPlayer::SetVisible(bool a_bVisible)
-{
- if (a_bVisible && !m_bVisible) // Make visible
- {
- m_bVisible = true;
- m_World->BroadcastSpawnEntity(*this);
- }
- if (!a_bVisible && m_bVisible)
- {
- m_bVisible = false;
- m_World->BroadcastDestroyEntity(*this, m_ClientHandle); // Destroy on all clients
- }
-}
-
-
-
-
-
-void cPlayer::AddToGroup( const AString & a_GroupName )
-{
- cGroup* Group = cRoot::Get()->GetGroupManager()->GetGroup( a_GroupName );
- m_Groups.push_back( Group );
- LOGD("Added %s to group %s", m_PlayerName.c_str(), a_GroupName.c_str() );
- ResolveGroups();
- ResolvePermissions();
-}
-
-
-
-
-
-void cPlayer::RemoveFromGroup( const AString & a_GroupName )
-{
- bool bRemoved = false;
- for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr )
- {
- if( (*itr)->GetName().compare(a_GroupName ) == 0 )
- {
- m_Groups.erase( itr );
- bRemoved = true;
- break;
- }
- }
-
- if( bRemoved )
- {
- LOGD("Removed %s from group %s", m_PlayerName.c_str(), a_GroupName.c_str() );
- ResolveGroups();
- ResolvePermissions();
- }
- else
- {
- LOGWARN("Tried to remove %s from group %s but was not in that group", m_PlayerName.c_str(), a_GroupName.c_str() );
- }
-}
-
-
-
-
-
-bool cPlayer::CanUseCommand( const AString & a_Command )
-{
- for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr )
- {
- if( (*itr)->HasCommand( a_Command ) ) return true;
- }
- return false;
-}
-
-
-
-
-
-bool cPlayer::HasPermission(const AString & a_Permission)
-{
- if (a_Permission.empty())
- {
- // Empty permission request is always granted
- return true;
- }
-
- AStringVector Split = StringSplit( a_Permission, "." );
- PermissionMap Possibilities = m_ResolvedPermissions;
- // Now search the namespaces
- while( Possibilities.begin() != Possibilities.end() )
- {
- PermissionMap::iterator itr = Possibilities.begin();
- if( itr->second )
- {
- AStringVector OtherSplit = StringSplit( itr->first, "." );
- if( OtherSplit.size() <= Split.size() )
- {
- unsigned int i;
- for( i = 0; i < OtherSplit.size(); ++i )
- {
- if( OtherSplit[i].compare( Split[i] ) != 0 )
- {
- if( OtherSplit[i].compare("*") == 0 ) return true; // WildCard man!! WildCard!
- break;
- }
- }
- if( i == Split.size() ) return true;
- }
- }
- Possibilities.erase( itr );
- }
-
- // Nothing that matched :(
- return false;
-}
-
-
-
-
-
-bool cPlayer::IsInGroup( const AString & a_Group )
-{
- for( GroupList::iterator itr = m_ResolvedGroups.begin(); itr != m_ResolvedGroups.end(); ++itr )
- {
- if( a_Group.compare( (*itr)->GetName().c_str() ) == 0 )
- return true;
- }
- return false;
-}
-
-
-
-
-
-void cPlayer::ResolvePermissions()
-{
- m_ResolvedPermissions.clear(); // Start with an empty map yo~
-
- // Copy all player specific permissions into the resolved permissions map
- for( PermissionMap::iterator itr = m_Permissions.begin(); itr != m_Permissions.end(); ++itr )
- {
- m_ResolvedPermissions[ itr->first ] = itr->second;
- }
-
- for( GroupList::iterator GroupItr = m_ResolvedGroups.begin(); GroupItr != m_ResolvedGroups.end(); ++GroupItr )
- {
- const cGroup::PermissionMap & Permissions = (*GroupItr)->GetPermissions();
- for( cGroup::PermissionMap::const_iterator itr = Permissions.begin(); itr != Permissions.end(); ++itr )
- {
- m_ResolvedPermissions[ itr->first ] = itr->second;
- }
- }
-}
-
-
-
-
-
-void cPlayer::ResolveGroups()
-{
- // Clear resolved groups first
- m_ResolvedGroups.clear();
-
- // Get a complete resolved list of all groups the player is in
- std::map< cGroup*, bool > AllGroups; // Use a map, because it's faster than iterating through a list to find duplicates
- GroupList ToIterate;
- for( GroupList::iterator GroupItr = m_Groups.begin(); GroupItr != m_Groups.end(); ++GroupItr )
- {
- ToIterate.push_back( *GroupItr );
- }
- while( ToIterate.begin() != ToIterate.end() )
- {
- cGroup* CurrentGroup = *ToIterate.begin();
- if( AllGroups.find( CurrentGroup ) != AllGroups.end() )
- {
- LOGWARNING("ERROR: Player \"%s\" is in the group multiple times (\"%s\"). Please fix your settings in users.ini!",
- m_PlayerName.c_str(), CurrentGroup->GetName().c_str()
- );
- }
- else
- {
- AllGroups[ CurrentGroup ] = true;
- m_ResolvedGroups.push_back( CurrentGroup ); // Add group to resolved list
- const cGroup::GroupList & Inherits = CurrentGroup->GetInherits();
- for( cGroup::GroupList::const_iterator itr = Inherits.begin(); itr != Inherits.end(); ++itr )
- {
- if( AllGroups.find( *itr ) != AllGroups.end() )
- {
- LOGERROR("ERROR: Player %s is in the same group multiple times due to inheritance (%s). FIX IT!", m_PlayerName.c_str(), (*itr)->GetName().c_str() );
- continue;
- }
- ToIterate.push_back( *itr );
- }
- }
- ToIterate.erase( ToIterate.begin() );
- }
-}
-
-
-
-
-
-AString cPlayer::GetColor(void) const
-{
- if ( m_Color != '-' )
- {
- return cChatColor::MakeColor( m_Color );
- }
-
- if ( m_Groups.size() < 1 )
- {
- return cChatColor::White;
- }
-
- return (*m_Groups.begin())->GetColor();
-}
-
-
-
-
-
-void cPlayer::TossItem(
- bool a_bDraggingItem,
- char a_Amount /* = 1 */,
- short a_CreateType /* = 0 */,
- short a_CreateHealth /* = 0 */
-)
-{
- cItems Drops;
- if (a_CreateType != 0)
- {
- // Just create item without touching the inventory (used in creative mode)
- Drops.push_back(cItem(a_CreateType, a_Amount, a_CreateHealth));
- }
- else
- {
- // Drop an item from the inventory:
- if (a_bDraggingItem)
- {
- cItem & Item = GetDraggingItem();
- if (!Item.IsEmpty())
- {
- char OriginalItemAmount = Item.m_ItemCount;
- Item.m_ItemCount = std::min(OriginalItemAmount, a_Amount);
- Drops.push_back(Item);
- if (OriginalItemAmount > a_Amount)
- {
- Item.m_ItemCount = OriginalItemAmount - (char)a_Amount;
- }
- else
- {
- Item.Empty();
- }
- }
- }
- else
- {
- // Else drop equipped item
- cItem DroppedItem(GetInventory().GetEquippedItem());
- if (!DroppedItem.IsEmpty())
- {
- if (GetInventory().RemoveOneEquippedItem())
- {
- DroppedItem.m_ItemCount = 1; // RemoveItem decreases the count, so set it to 1 again
- Drops.push_back(DroppedItem);
- }
- }
- }
- }
- double vX = 0, vY = 0, vZ = 0;
- EulerToVector(-GetRotation(), GetPitch(), vZ, vX, vY);
- vY = -vY * 2 + 1.f;
- m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY() + 1.6f, GetPosZ(), vX * 2, vY * 2, vZ * 2);
-}
-
-
-
-
-
-bool cPlayer::MoveToWorld(const char * a_WorldName)
-{
- cWorld * World = cRoot::Get()->GetWorld(a_WorldName);
- if (World == NULL)
- {
- LOG("%s: Couldn't find world \"%s\".", __FUNCTION__, a_WorldName);
- return false;
- }
-
- eDimension OldDimension = m_World->GetDimension();
-
- // Remove all links to the old world
- m_World->RemovePlayer(this);
- m_ClientHandle->RemoveFromAllChunks();
- m_World->RemoveEntity(this);
-
- // Add player to all the necessary parts of the new world
- SetWorld(World);
- World->AddEntity(this);
- World->AddPlayer(this);
-
- // If the dimension is different, we can send the respawn packet
- // http://wiki.vg/Protocol#0x09 says "don't send if dimension is the same" as of 2013_07_02
- if (OldDimension != World->GetDimension())
- {
- m_ClientHandle->SendRespawn();
- }
-
- // Stream the new chunks:
- m_ClientHandle->StreamChunks();
- return true;
-}
-
-
-
-
-
-void cPlayer::LoadPermissionsFromDisk()
-{
- m_Groups.clear();
- m_Permissions.clear();
-
- cIniFile IniFile("users.ini");
- if( IniFile.ReadFile() )
- {
- std::string Groups = IniFile.GetValue(m_PlayerName, "Groups", "");
- if( Groups.size() > 0 )
- {
- AStringVector Split = StringSplit( Groups, "," );
- for( unsigned int i = 0; i < Split.size(); i++ )
- {
- AddToGroup( Split[i].c_str() );
- }
- }
- else
- {
- AddToGroup("Default");
- }
-
- m_Color = IniFile.GetValue(m_PlayerName, "Color", "-")[0];
- }
- else
- {
- LOGWARN("WARNING: Failed to read ini file users.ini");
- AddToGroup("Default");
- }
- ResolvePermissions();
-}
-
-
-
-
-bool cPlayer::LoadFromDisk()
-{
- LoadPermissionsFromDisk();
-
- // Log player permissions, cause it's what the cool kids do
- LOGINFO("Player %s has permissions:", m_PlayerName.c_str() );
- for( PermissionMap::iterator itr = m_ResolvedPermissions.begin(); itr != m_ResolvedPermissions.end(); ++itr )
- {
- if( itr->second ) LOGINFO("%s", itr->first.c_str() );
- }
-
- AString SourceFile;
- Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() );
-
- cFile f;
- if (!f.Open(SourceFile, cFile::fmRead))
- {
- LOGWARNING("Cannot open player data file \"%s\" for reading", SourceFile.c_str());
- return false;
- }
-
- AString buffer;
- if (f.ReadRestOfFile(buffer) != f.GetSize())
- {
- LOGWARNING("Cannot read player data from file \"%s\"", SourceFile.c_str());
- return false;
- }
- f.Close();
-
- Json::Value root;
- Json::Reader reader;
- if (!reader.parse(buffer, root, false))
- {
- LOGWARNING("Cannot parse player data in file \"%s\", player will be reset", SourceFile.c_str());
- }
-
- Json::Value & JSON_PlayerPosition = root["position"];
- if (JSON_PlayerPosition.size() == 3)
- {
- SetPosX(JSON_PlayerPosition[(unsigned int)0].asDouble());
- SetPosY(JSON_PlayerPosition[(unsigned int)1].asDouble());
- SetPosZ(JSON_PlayerPosition[(unsigned int)2].asDouble());
- m_LastPosX = GetPosX();
- m_LastPosY = GetPosY();
- m_LastPosZ = GetPosZ();
- m_LastFoodPos = GetPosition();
- }
-
- Json::Value & JSON_PlayerRotation = root["rotation"];
- if (JSON_PlayerRotation.size() == 3)
- {
- SetRotation ((float)JSON_PlayerRotation[(unsigned int)0].asDouble());
- SetPitch ((float)JSON_PlayerRotation[(unsigned int)1].asDouble());
- SetRoll ((float)JSON_PlayerRotation[(unsigned int)2].asDouble());
- }
-
- m_Health = root.get("health", 0).asInt();
-
- m_FoodLevel = root.get("food", MAX_FOOD_LEVEL).asInt();
- m_FoodSaturationLevel = root.get("foodSaturation", MAX_FOOD_LEVEL).asDouble();
- m_FoodTickTimer = root.get("foodTickTimer", 0).asInt();
- m_FoodExhaustionLevel = root.get("foodExhaustion", 0).asDouble();
-
- m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt();
-
- m_Inventory.LoadFromJson(root["inventory"]);
-
- m_LoadedWorldName = root.get("world", "world").asString();
-
- LOGD("Player \"%s\" was read from file, spawning at {%.2f, %.2f, %.2f} in world \"%s\"",
- m_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str()
- );
-
- return true;
-}
-
-
-
-
-
-bool cPlayer::SaveToDisk()
-{
- cMakeDir::MakeDir("players");
-
- // create the JSON data
- Json::Value JSON_PlayerPosition;
- JSON_PlayerPosition.append(Json::Value(GetPosX()));
- JSON_PlayerPosition.append(Json::Value(GetPosY()));
- JSON_PlayerPosition.append(Json::Value(GetPosZ()));
-
- Json::Value JSON_PlayerRotation;
- JSON_PlayerRotation.append(Json::Value(GetRotation()));
- JSON_PlayerRotation.append(Json::Value(GetPitch()));
- JSON_PlayerRotation.append(Json::Value(GetRoll()));
-
- Json::Value JSON_Inventory;
- m_Inventory.SaveToJson(JSON_Inventory);
-
- Json::Value root;
- root["position"] = JSON_PlayerPosition;
- root["rotation"] = JSON_PlayerRotation;
- root["inventory"] = JSON_Inventory;
- root["health"] = m_Health;
- root["food"] = m_FoodLevel;
- root["foodSaturation"] = m_FoodSaturationLevel;
- root["foodTickTimer"] = m_FoodTickTimer;
- root["foodExhaustion"] = m_FoodExhaustionLevel;
- root["world"] = GetWorld()->GetName();
-
- if (m_GameMode == GetWorld()->GetGameMode())
- {
- root["gamemode"] = (int) eGameMode_NotSet;
- }
- else
- {
- root["gamemode"] = (int) m_GameMode;
- }
-
- Json::StyledWriter writer;
- std::string JsonData = writer.write(root);
-
- AString SourceFile;
- Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() );
-
- cFile f;
- if (!f.Open(SourceFile, cFile::fmWrite))
- {
- LOGERROR("ERROR WRITING PLAYER \"%s\" TO FILE \"%s\" - cannot open file", m_PlayerName.c_str(), SourceFile.c_str());
- return false;
- }
- if (f.Write(JsonData.c_str(), JsonData.size()) != (int)JsonData.size())
- {
- LOGERROR("ERROR WRITING PLAYER JSON TO FILE \"%s\"", SourceFile.c_str());
- return false;
- }
- return true;
-}
-
-
-
-
-
-cPlayer::StringList cPlayer::GetResolvedPermissions()
-{
- StringList Permissions;
-
- const PermissionMap& ResolvedPermissions = m_ResolvedPermissions;
- for( PermissionMap::const_iterator itr = ResolvedPermissions.begin(); itr != ResolvedPermissions.end(); ++itr )
- {
- if( itr->second ) Permissions.push_back( itr->first );
- }
-
- return Permissions;
-}
-
-
-
-
-
-void cPlayer::UseEquippedItem()
-{
- if (GetGameMode() == gmCreative) // No damage in creative
- {
- return;
- }
-
- GetInventory().DamageEquippedItem();
-}
-
-
-
-
-
-void cPlayer::HandleFood(void)
-{
- // Ref.: http://www.minecraftwiki.net/wiki/Hunger
-
- // Remember the food level before processing, for later comparison
- int LastFoodLevel = m_FoodLevel;
-
- // Heal or damage, based on the food level, using the m_FoodTickTimer:
- if ((m_FoodLevel > 17) || (m_FoodLevel <= 0))
- {
- m_FoodTickTimer++;
- if (m_FoodTickTimer >= 80)
- {
- m_FoodTickTimer = 0;
-
- if (m_FoodLevel >= 17)
- {
- // Regenerate health from food, incur 3 pts of food exhaustion:
- Heal(1);
- m_FoodExhaustionLevel += 3;
- }
- else if (m_FoodLevel <= 0)
- {
- // Damage from starving
- TakeDamage(dtStarving, NULL, 1, 1, 0);
- }
- }
- }
-
- // Apply food poisoning food exhaustion:
- if (m_FoodPoisonedTicksRemaining > 0)
- {
- m_FoodPoisonedTicksRemaining--;
- m_FoodExhaustionLevel += 0.025; // 0.5 per second = 0.025 per tick
- }
-
- // Apply food exhaustion that has accumulated:
- if (m_FoodExhaustionLevel >= 4)
- {
- m_FoodExhaustionLevel -= 4;
-
- if (m_FoodSaturationLevel >= 1)
- {
- m_FoodSaturationLevel -= 1;
- }
- else
- {
- m_FoodLevel = std::max(m_FoodLevel - 1, 0);
- }
- }
-
- if (m_FoodLevel != LastFoodLevel)
- {
- SendHealth();
- }
-}
-
-
-
-
-
-void cPlayer::ApplyFoodExhaustionFromMovement(cChunk & a_Chunk)
-{
- // Calculate the distance travelled, update the last pos:
- Vector3d Movement(GetPosition() - m_LastFoodPos);
- m_LastFoodPos = GetPosition();
-
- // If riding anything, apply no food exhaustion
- if (m_AttachedTo != NULL)
- {
- return;
- }
-
- // Get the type of block the player's standing in:
- BLOCKTYPE BlockIn;
- int RelX = (int)floor(m_LastPosX) - a_Chunk.GetPosX() * cChunkDef::Width;
- int RelY = (int)floor(m_LastPosY + 0.1);
- int RelZ = (int)floor(m_LastPosZ) - a_Chunk.GetPosZ() * cChunkDef::Width;
- // Use Unbounded, because we're being called *after* processing super::Tick(), which could have changed our chunk
- VERIFY(a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockIn));
-
- // Apply the exhaustion based on distance travelled:
- double BaseExhaustion = Movement.Length();
- if (IsSprinting())
- {
- // 0.1 pt per meter sprinted
- BaseExhaustion = BaseExhaustion * 0.1;
- }
- else if (IsBlockWater(BlockIn))
- {
- // 0.015 pt per meter swum
- BaseExhaustion = BaseExhaustion * 0.015;
- }
- else
- {
- // 0.01 pt per meter walked / sneaked
- BaseExhaustion = BaseExhaustion * 0.01;
- }
- m_FoodExhaustionLevel += BaseExhaustion;
-}
-
-
-
-
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Player.h"
+#include "Server.h"
+#include "ClientHandle.h"
+#include "UI/Window.h"
+#include "UI/WindowOwner.h"
+#include "World.h"
+#include "Pickup.h"
+#include "PluginManager.h"
+#include "BlockEntities/BlockEntity.h"
+#include "GroupManager.h"
+#include "Group.h"
+#include "ChatColor.h"
+#include "Item.h"
+#include "Tracer.h"
+#include "Root.h"
+#include "OSSupport/MakeDir.h"
+#include "OSSupport/Timer.h"
+#include "MersenneTwister.h"
+#include "Chunk.h"
+#include "Items/ItemHandler.h"
+
+#include "Vector3d.h"
+#include "Vector3f.h"
+
+#include "../iniFile/iniFile.h"
+#include <json/json.h>
+
+#define float2int(x) ((x)<0 ? ((int)(x))-1 : (int)(x))
+
+
+
+
+
+cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
+ : super(etPlayer, 0.6, 1.8)
+ , m_GameMode(eGameMode_NotSet)
+ , m_IP("")
+ , m_LastBlockActionTime( 0 )
+ , m_LastBlockActionCnt( 0 )
+ , m_bVisible( true )
+ , m_LastGroundHeight( 0 )
+ , m_bTouchGround( false )
+ , m_Stance( 0.0 )
+ , m_Inventory(*this)
+ , m_CurrentWindow(NULL)
+ , m_InventoryWindow(NULL)
+ , m_TimeLastPickupCheck( 0.f )
+ , m_Color('-')
+ , m_ClientHandle( a_Client )
+ , m_FoodLevel(MAX_FOOD_LEVEL)
+ , m_FoodSaturationLevel(5)
+ , m_FoodTickTimer(0)
+ , m_FoodExhaustionLevel(0)
+ , m_FoodPoisonedTicksRemaining(0)
+ , m_NormalMaxSpeed(0.1)
+ , m_SprintingMaxSpeed(0.13)
+ , m_IsCrouched(false)
+ , m_IsSprinting(false)
+ , m_EatingFinishTick(-1)
+{
+ LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d",
+ a_PlayerName.c_str(), a_Client->GetIPString().c_str(),
+ this, GetUniqueID()
+ );
+
+ m_InventoryWindow = new cInventoryWindow(*this);
+ m_CurrentWindow = m_InventoryWindow;
+ m_InventoryWindow->OpenedByPlayer(*this);
+
+ SetMaxHealth(MAX_HEALTH);
+ m_Health = MAX_HEALTH;
+
+ cTimer t1;
+ m_LastPlayerListTime = t1.GetNowTime();
+
+ m_TimeLastTeleportPacket = 0;
+ m_TimeLastPickupCheck = 0;
+
+ m_PlayerName = a_PlayerName;
+ m_bDirtyPosition = true; // So chunks are streamed to player at spawn
+
+ if (!LoadFromDisk())
+ {
+ m_Inventory.Clear();
+ SetPosX(cRoot::Get()->GetDefaultWorld()->GetSpawnX());
+ SetPosY(cRoot::Get()->GetDefaultWorld()->GetSpawnY());
+ SetPosZ(cRoot::Get()->GetDefaultWorld()->GetSpawnZ());
+
+ LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}",
+ a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ()
+ );
+ }
+ m_LastJumpHeight = (float)(GetPosY());
+ m_LastGroundHeight = (float)(GetPosY());
+ m_Stance = GetPosY() + 1.62;
+}
+
+
+
+
+
+cPlayer::~cPlayer(void)
+{
+ LOG("Deleting cPlayer \"%s\" at %p, ID %d", m_PlayerName.c_str(), this, GetUniqueID());
+
+ SaveToDisk();
+
+ m_World->RemovePlayer( this );
+
+ m_ClientHandle = NULL;
+
+ delete m_InventoryWindow;
+
+ LOGD("Player %p deleted", this);
+}
+
+
+
+
+
+void cPlayer::Initialize(cWorld * a_World)
+{
+ super::Initialize(a_World);
+ GetWorld()->AddPlayer(this);
+}
+
+
+
+
+
+void cPlayer::Destroyed()
+{
+ CloseWindow(false);
+ m_ClientHandle = NULL;
+}
+
+
+
+
+
+void cPlayer::SpawnOn(cClientHandle & a_Client)
+{
+ /*
+ LOGD("cPlayer::SpawnOn(%s) for \"%s\" at pos {%.2f, %.2f, %.2f}",
+ a_Client.GetUsername().c_str(), m_PlayerName.c_str(), m_Pos.x, m_Pos.y, m_Pos.z
+ );
+ */
+
+ if (m_bVisible && (m_ClientHandle != (&a_Client)))
+ {
+ a_Client.SendPlayerSpawn(*this);
+ a_Client.SendEntityHeadLook(*this);
+ a_Client.SendEntityEquipment(*this, 0, m_Inventory.GetEquippedItem() );
+ a_Client.SendEntityEquipment(*this, 1, m_Inventory.GetEquippedBoots() );
+ a_Client.SendEntityEquipment(*this, 2, m_Inventory.GetEquippedLeggings() );
+ a_Client.SendEntityEquipment(*this, 3, m_Inventory.GetEquippedChestplate() );
+ a_Client.SendEntityEquipment(*this, 4, m_Inventory.GetEquippedHelmet() );
+ }
+}
+
+
+
+
+
+void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ if (!m_ClientHandle->IsPlaying())
+ {
+ // We're not yet in the game, ignore everything
+ return;
+ }
+
+ super::Tick(a_Dt, a_Chunk);
+ if (m_bDirtyPosition)
+ {
+ // Apply food exhaustion from movement:
+ ApplyFoodExhaustionFromMovement(a_Chunk);
+
+ cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*this);
+ BroadcastMovementUpdate(m_ClientHandle);
+ m_ClientHandle->StreamChunks();
+ }
+ else
+ {
+ BroadcastMovementUpdate(m_ClientHandle);
+ }
+
+ if (m_Health > 0) // make sure player is alive
+ {
+ m_World->CollectPickupsByPlayer(this);
+
+ if ((m_EatingFinishTick >= 0) && (m_EatingFinishTick <= m_World->GetWorldAge()))
+ {
+ FinishEating();
+ }
+
+ HandleFood();
+ }
+
+ // Send Player List (Once per m_LastPlayerListTime/1000 ms)
+ cTimer t1;
+ if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime())
+ {
+ m_World->SendPlayerList(this);
+ m_LastPlayerListTime = t1.GetNowTime();
+ }
+}
+
+
+
+
+
+void cPlayer::SetTouchGround(bool a_bTouchGround)
+{
+ // If just
+ m_bTouchGround = a_bTouchGround;
+
+ if (!m_bTouchGround)
+ {
+ if (GetPosY() > m_LastJumpHeight)
+ {
+ m_LastJumpHeight = (float)GetPosY();
+ }
+ cWorld * World = GetWorld();
+ if ((GetPosY() >= 0) && (GetPosY() < 256))
+ {
+ BLOCKTYPE BlockType = World->GetBlock( float2int(GetPosX()), float2int(GetPosY()), float2int(GetPosZ()) );
+ if (BlockType != E_BLOCK_AIR)
+ {
+ // LOGD("TouchGround set to true by server");
+ m_bTouchGround = true;
+ }
+ if (
+ (BlockType == E_BLOCK_WATER) ||
+ (BlockType == E_BLOCK_STATIONARY_WATER) ||
+ (BlockType == E_BLOCK_LADDER) ||
+ (BlockType == E_BLOCK_VINES)
+ )
+ {
+ // LOGD("Water / Ladder / Torch");
+ m_LastGroundHeight = (float)GetPosY();
+ }
+ }
+ }
+
+ if (m_bTouchGround)
+ {
+ float Dist = (float)(m_LastGroundHeight - floor(GetPosY()));
+ int Damage = (int)(Dist - 3.f);
+ if(m_LastJumpHeight > m_LastGroundHeight) Damage++;
+ m_LastJumpHeight = (float)GetPosY();
+ if (Damage > 0)
+ {
+ TakeDamage(dtFalling, NULL, Damage, Damage, 0);
+ }
+
+ m_LastGroundHeight = (float)GetPosY();
+ }
+}
+
+
+
+
+
+void cPlayer::Heal(int a_Health)
+{
+ if (m_Health < GetMaxHealth())
+ {
+ m_Health = (short)std::min((int)a_Health + m_Health, (int)GetMaxHealth());
+ SendHealth();
+ }
+}
+
+
+
+
+
+void cPlayer::SetFoodLevel(int a_FoodLevel)
+{
+ m_FoodLevel = std::max(0, std::min(a_FoodLevel, (int)MAX_FOOD_LEVEL));
+ SendHealth();
+}
+
+
+
+
+
+void cPlayer::SetFoodSaturationLevel(double a_FoodSaturationLevel)
+{
+ m_FoodSaturationLevel = std::max(0.0, std::min(a_FoodSaturationLevel, (double)m_FoodLevel));
+}
+
+
+
+
+
+void cPlayer::SetFoodTickTimer(int a_FoodTickTimer)
+{
+ m_FoodTickTimer = a_FoodTickTimer;
+}
+
+
+
+
+
+void cPlayer::SetFoodExhaustionLevel(double a_FoodSaturationLevel)
+{
+ m_FoodExhaustionLevel = std::max(0.0, std::min(a_FoodSaturationLevel, 4.0));
+}
+
+
+
+
+
+void cPlayer::SetFoodPoisonedTicksRemaining(int a_FoodPoisonedTicksRemaining)
+{
+ m_FoodPoisonedTicksRemaining = a_FoodPoisonedTicksRemaining;
+}
+
+
+
+
+
+bool cPlayer::Feed(int a_Food, double a_Saturation)
+{
+ if (m_FoodLevel >= MAX_FOOD_LEVEL)
+ {
+ return false;
+ }
+
+ m_FoodLevel = std::min(a_Food + m_FoodLevel, (int)MAX_FOOD_LEVEL);
+ m_FoodSaturationLevel = std::min(m_FoodSaturationLevel + a_Saturation, (double)m_FoodLevel);
+
+ SendHealth();
+ return true;
+}
+
+
+
+
+
+void cPlayer::FoodPoison(int a_NumTicks)
+{
+ bool HasBeenFoodPoisoned = (m_FoodPoisonedTicksRemaining > 0);
+ m_FoodPoisonedTicksRemaining = std::max(m_FoodPoisonedTicksRemaining, a_NumTicks);
+ if (!HasBeenFoodPoisoned)
+ {
+ // TODO: Send the poisoning indication to the client - how?
+ SendHealth();
+ }
+}
+
+
+
+
+
+void cPlayer::StartEating(void)
+{
+ // Set the timer:
+ m_EatingFinishTick = m_World->GetWorldAge() + EATING_TICKS;
+
+ // Send the packets:
+ m_World->BroadcastPlayerAnimation(*this, 5);
+ m_World->BroadcastEntityMetadata(*this);
+}
+
+
+
+
+
+void cPlayer::FinishEating(void)
+{
+ // Reset the timer:
+ m_EatingFinishTick = -1;
+
+ // Send the packets:
+ m_ClientHandle->SendEntityStatus(*this, ENTITY_STATUS_EATING_ACCEPTED);
+ m_World->BroadcastPlayerAnimation(*this, 0);
+ m_World->BroadcastEntityMetadata(*this);
+
+ // consume the item:
+ cItem Item(GetEquippedItem());
+ Item.m_ItemCount = 1;
+ cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Item.m_ItemType);
+ if (!ItemHandler->EatItem(this, &Item))
+ {
+ return;
+ }
+ ItemHandler->OnFoodEaten(m_World, this, &Item);
+ GetInventory().RemoveOneEquippedItem();
+}
+
+
+
+
+
+void cPlayer::AbortEating(void)
+{
+ m_EatingFinishTick = -1;
+ m_World->BroadcastPlayerAnimation(*this, 0);
+ m_World->BroadcastEntityMetadata(*this);
+}
+
+
+
+
+
+void cPlayer::SendHealth(void)
+{
+ if (m_ClientHandle != NULL)
+ {
+ m_ClientHandle->SendHealth();
+ }
+}
+
+
+
+
+
+void cPlayer::ClearInventoryPaintSlots(void)
+{
+ // Clear the list of slots that are being inventory-painted. Used by cWindow only
+ m_InventoryPaintSlots.clear();
+}
+
+
+
+
+
+void cPlayer::AddInventoryPaintSlot(int a_SlotNum)
+{
+ // Add a slot to the list for inventory painting. Used by cWindow only
+ m_InventoryPaintSlots.push_back(a_SlotNum);
+}
+
+
+
+
+
+const cSlotNums & cPlayer::GetInventoryPaintSlots(void) const
+{
+ // Return the list of slots currently stored for inventory painting. Used by cWindow only
+ return m_InventoryPaintSlots;
+}
+
+
+
+
+
+double cPlayer::GetMaxSpeed(void) const
+{
+ return m_IsSprinting ? m_SprintingMaxSpeed : m_NormalMaxSpeed;
+}
+
+
+
+
+
+void cPlayer::SetNormalMaxSpeed(double a_Speed)
+{
+ m_NormalMaxSpeed = a_Speed;
+ if (!m_IsSprinting)
+ {
+ m_ClientHandle->SendPlayerMaxSpeed();
+ }
+}
+
+
+
+
+
+void cPlayer::SetSprintingMaxSpeed(double a_Speed)
+{
+ m_SprintingMaxSpeed = a_Speed;
+ if (m_IsSprinting)
+ {
+ m_ClientHandle->SendPlayerMaxSpeed();
+ }
+}
+
+
+
+
+
+void cPlayer::SetCrouch(bool a_IsCrouched)
+{
+ // Set the crouch status, broadcast to all visible players
+
+ if (a_IsCrouched == m_IsCrouched)
+ {
+ // No change
+ return;
+ }
+ m_IsCrouched = a_IsCrouched;
+ m_World->BroadcastEntityMetadata(*this);
+}
+
+
+
+
+
+void cPlayer::SetSprint(bool a_IsSprinting)
+{
+ if (a_IsSprinting == m_IsSprinting)
+ {
+ // No change
+ return;
+ }
+
+ m_IsSprinting = a_IsSprinting;
+ m_ClientHandle->SendPlayerMaxSpeed();
+}
+
+
+
+
+
+void cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ if (m_GameMode == eGameMode_Creative)
+ {
+ // No damage / health in creative mode
+ return;
+ }
+
+ super::DoTakeDamage(a_TDI);
+
+ // Any kind of damage adds food exhaustion
+ AddFoodExhaustion(0.3f);
+
+ SendHealth();
+}
+
+
+
+
+
+void cPlayer::KilledBy(cEntity * a_Killer)
+{
+ super::KilledBy(a_Killer);
+
+ if (m_Health > 0)
+ {
+ return; // not dead yet =]
+ }
+
+ m_bVisible = false; // So new clients don't see the player
+
+ // Puke out all the items
+ cItems Pickups;
+ m_Inventory.CopyToItems(Pickups);
+ m_Inventory.Clear();
+ m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10);
+ SaveToDisk(); // Save it, yeah the world is a tough place !
+}
+
+
+
+
+
+void cPlayer::Respawn(void)
+{
+ m_Health = GetMaxHealth();
+
+ // Reset food level:
+ m_FoodLevel = MAX_FOOD_LEVEL;
+ m_FoodSaturationLevel = 5;
+
+ m_ClientHandle->SendRespawn();
+
+ // Extinguish the fire:
+ StopBurning();
+
+ TeleportToCoords(GetWorld()->GetSpawnX(), GetWorld()->GetSpawnY(), GetWorld()->GetSpawnZ());
+
+ SetVisible(true);
+}
+
+
+
+
+
+double cPlayer::GetEyeHeight(void) const
+{
+ return m_Stance;
+}
+
+
+
+
+Vector3d cPlayer::GetEyePosition(void) const
+{
+ return Vector3d( GetPosX(), m_Stance, GetPosZ() );
+}
+
+
+
+
+
+bool cPlayer::IsGameModeCreative(void) const
+{
+ return (m_GameMode == gmCreative) || // Either the player is explicitly in Creative
+ ((m_GameMode == gmNotSet) && m_World->IsGameModeCreative()); // or they inherit from the world and the world is Creative
+}
+
+
+
+
+
+bool cPlayer::IsGameModeSurvival(void) const
+{
+ return (m_GameMode == gmSurvival) || // Either the player is explicitly in Survival
+ ((m_GameMode == gmNotSet) && m_World->IsGameModeSurvival()); // or they inherit from the world and the world is Survival
+}
+
+
+
+
+
+bool cPlayer::IsGameModeAdventure(void) const
+{
+ return (m_GameMode == gmCreative) || // Either the player is explicitly in Adventure
+ ((m_GameMode == gmNotSet) && m_World->IsGameModeCreative()); // or they inherit from the world and the world is Adventure
+}
+
+
+
+
+
+void cPlayer::OpenWindow(cWindow * a_Window)
+{
+ if (a_Window != m_CurrentWindow)
+ {
+ CloseWindow(false);
+ }
+ a_Window->OpenedByPlayer(*this);
+ m_CurrentWindow = a_Window;
+ a_Window->SendWholeWindow(*GetClientHandle());
+}
+
+
+
+
+
+void cPlayer::CloseWindow(bool a_CanRefuse)
+{
+ if (m_CurrentWindow == NULL)
+ {
+ m_CurrentWindow = m_InventoryWindow;
+ return;
+ }
+
+ if (m_CurrentWindow->ClosedByPlayer(*this, a_CanRefuse) || !a_CanRefuse)
+ {
+ // Close accepted, go back to inventory window (the default):
+ m_CurrentWindow = m_InventoryWindow;
+ }
+ else
+ {
+ // Re-open the window
+ m_CurrentWindow->OpenedByPlayer(*this);
+ m_CurrentWindow->SendWholeWindow(*GetClientHandle());
+ }
+}
+
+
+
+
+
+void cPlayer::CloseWindowIfID(char a_WindowID, bool a_CanRefuse)
+{
+ if ((m_CurrentWindow == NULL) || (m_CurrentWindow->GetWindowID() != a_WindowID))
+ {
+ return;
+ }
+ CloseWindow();
+}
+
+
+
+
+
+void cPlayer::SetLastBlockActionTime()
+{
+ if (m_World != NULL)
+ {
+ m_LastBlockActionTime = m_World->GetWorldAge() / 20.0f;
+ }
+}
+
+
+
+
+
+void cPlayer::SetLastBlockActionCnt( int a_LastBlockActionCnt )
+{
+ m_LastBlockActionCnt = a_LastBlockActionCnt;
+}
+
+
+
+
+
+void cPlayer::SetGameMode(eGameMode a_GameMode)
+{
+ if ((a_GameMode < gmMin) || (a_GameMode >= gmMax))
+ {
+ LOGWARNING("%s: Setting invalid gamemode: %d", GetName().c_str(), a_GameMode);
+ return;
+ }
+
+ if (m_GameMode == a_GameMode)
+ {
+ // Gamemode already set
+ return;
+ }
+
+ m_GameMode = a_GameMode;
+ m_ClientHandle->SendGameMode(a_GameMode);
+}
+
+
+
+
+
+void cPlayer::LoginSetGameMode( eGameMode a_GameMode )
+{
+ m_GameMode = a_GameMode;
+}
+
+
+
+
+
+void cPlayer::SetIP(const AString & a_IP)
+{
+ m_IP = a_IP;
+}
+
+
+
+
+
+void cPlayer::SendMessage(const AString & a_Message)
+{
+ m_ClientHandle->SendChat(a_Message);
+}
+
+
+
+
+
+void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ)
+{
+ SetPosition( a_PosX, a_PosY, a_PosZ );
+
+ m_World->BroadcastTeleportEntity(*this, GetClientHandle());
+ m_ClientHandle->SendPlayerMoveLook();
+}
+
+
+
+
+
+void cPlayer::MoveTo( const Vector3d & a_NewPos )
+{
+ if ((a_NewPos.y < -990) && (GetPosY() > -100))
+ {
+ // When attached to an entity, the client sends position packets with weird coords:
+ // Y = -999 and X, Z = attempting to create speed, usually up to 0.03
+ // We cannot test m_AttachedTo, because when deattaching, the server thinks the client is already deattached while
+ // the client may still send more of these nonsensical packets.
+ if (m_AttachedTo != NULL)
+ {
+ Vector3d AddSpeed(a_NewPos);
+ AddSpeed.y = 0;
+ m_AttachedTo->AddSpeed(AddSpeed);
+ }
+ return;
+ }
+
+ // TODO: should do some checks to see if player is not moving through terrain
+ // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too
+
+ SetPosition( a_NewPos );
+ SetStance(a_NewPos.y + 1.62);
+}
+
+
+
+
+
+void cPlayer::SetVisible(bool a_bVisible)
+{
+ if (a_bVisible && !m_bVisible) // Make visible
+ {
+ m_bVisible = true;
+ m_World->BroadcastSpawnEntity(*this);
+ }
+ if (!a_bVisible && m_bVisible)
+ {
+ m_bVisible = false;
+ m_World->BroadcastDestroyEntity(*this, m_ClientHandle); // Destroy on all clients
+ }
+}
+
+
+
+
+
+void cPlayer::AddToGroup( const AString & a_GroupName )
+{
+ cGroup* Group = cRoot::Get()->GetGroupManager()->GetGroup( a_GroupName );
+ m_Groups.push_back( Group );
+ LOGD("Added %s to group %s", m_PlayerName.c_str(), a_GroupName.c_str() );
+ ResolveGroups();
+ ResolvePermissions();
+}
+
+
+
+
+
+void cPlayer::RemoveFromGroup( const AString & a_GroupName )
+{
+ bool bRemoved = false;
+ for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr )
+ {
+ if( (*itr)->GetName().compare(a_GroupName ) == 0 )
+ {
+ m_Groups.erase( itr );
+ bRemoved = true;
+ break;
+ }
+ }
+
+ if( bRemoved )
+ {
+ LOGD("Removed %s from group %s", m_PlayerName.c_str(), a_GroupName.c_str() );
+ ResolveGroups();
+ ResolvePermissions();
+ }
+ else
+ {
+ LOGWARN("Tried to remove %s from group %s but was not in that group", m_PlayerName.c_str(), a_GroupName.c_str() );
+ }
+}
+
+
+
+
+
+bool cPlayer::CanUseCommand( const AString & a_Command )
+{
+ for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr )
+ {
+ if( (*itr)->HasCommand( a_Command ) ) return true;
+ }
+ return false;
+}
+
+
+
+
+
+bool cPlayer::HasPermission(const AString & a_Permission)
+{
+ if (a_Permission.empty())
+ {
+ // Empty permission request is always granted
+ return true;
+ }
+
+ AStringVector Split = StringSplit( a_Permission, "." );
+ PermissionMap Possibilities = m_ResolvedPermissions;
+ // Now search the namespaces
+ while( Possibilities.begin() != Possibilities.end() )
+ {
+ PermissionMap::iterator itr = Possibilities.begin();
+ if( itr->second )
+ {
+ AStringVector OtherSplit = StringSplit( itr->first, "." );
+ if( OtherSplit.size() <= Split.size() )
+ {
+ unsigned int i;
+ for( i = 0; i < OtherSplit.size(); ++i )
+ {
+ if( OtherSplit[i].compare( Split[i] ) != 0 )
+ {
+ if( OtherSplit[i].compare("*") == 0 ) return true; // WildCard man!! WildCard!
+ break;
+ }
+ }
+ if( i == Split.size() ) return true;
+ }
+ }
+ Possibilities.erase( itr );
+ }
+
+ // Nothing that matched :(
+ return false;
+}
+
+
+
+
+
+bool cPlayer::IsInGroup( const AString & a_Group )
+{
+ for( GroupList::iterator itr = m_ResolvedGroups.begin(); itr != m_ResolvedGroups.end(); ++itr )
+ {
+ if( a_Group.compare( (*itr)->GetName().c_str() ) == 0 )
+ return true;
+ }
+ return false;
+}
+
+
+
+
+
+void cPlayer::ResolvePermissions()
+{
+ m_ResolvedPermissions.clear(); // Start with an empty map yo~
+
+ // Copy all player specific permissions into the resolved permissions map
+ for( PermissionMap::iterator itr = m_Permissions.begin(); itr != m_Permissions.end(); ++itr )
+ {
+ m_ResolvedPermissions[ itr->first ] = itr->second;
+ }
+
+ for( GroupList::iterator GroupItr = m_ResolvedGroups.begin(); GroupItr != m_ResolvedGroups.end(); ++GroupItr )
+ {
+ const cGroup::PermissionMap & Permissions = (*GroupItr)->GetPermissions();
+ for( cGroup::PermissionMap::const_iterator itr = Permissions.begin(); itr != Permissions.end(); ++itr )
+ {
+ m_ResolvedPermissions[ itr->first ] = itr->second;
+ }
+ }
+}
+
+
+
+
+
+void cPlayer::ResolveGroups()
+{
+ // Clear resolved groups first
+ m_ResolvedGroups.clear();
+
+ // Get a complete resolved list of all groups the player is in
+ std::map< cGroup*, bool > AllGroups; // Use a map, because it's faster than iterating through a list to find duplicates
+ GroupList ToIterate;
+ for( GroupList::iterator GroupItr = m_Groups.begin(); GroupItr != m_Groups.end(); ++GroupItr )
+ {
+ ToIterate.push_back( *GroupItr );
+ }
+ while( ToIterate.begin() != ToIterate.end() )
+ {
+ cGroup* CurrentGroup = *ToIterate.begin();
+ if( AllGroups.find( CurrentGroup ) != AllGroups.end() )
+ {
+ LOGWARNING("ERROR: Player \"%s\" is in the group multiple times (\"%s\"). Please fix your settings in users.ini!",
+ m_PlayerName.c_str(), CurrentGroup->GetName().c_str()
+ );
+ }
+ else
+ {
+ AllGroups[ CurrentGroup ] = true;
+ m_ResolvedGroups.push_back( CurrentGroup ); // Add group to resolved list
+ const cGroup::GroupList & Inherits = CurrentGroup->GetInherits();
+ for( cGroup::GroupList::const_iterator itr = Inherits.begin(); itr != Inherits.end(); ++itr )
+ {
+ if( AllGroups.find( *itr ) != AllGroups.end() )
+ {
+ LOGERROR("ERROR: Player %s is in the same group multiple times due to inheritance (%s). FIX IT!", m_PlayerName.c_str(), (*itr)->GetName().c_str() );
+ continue;
+ }
+ ToIterate.push_back( *itr );
+ }
+ }
+ ToIterate.erase( ToIterate.begin() );
+ }
+}
+
+
+
+
+
+AString cPlayer::GetColor(void) const
+{
+ if ( m_Color != '-' )
+ {
+ return cChatColor::MakeColor( m_Color );
+ }
+
+ if ( m_Groups.size() < 1 )
+ {
+ return cChatColor::White;
+ }
+
+ return (*m_Groups.begin())->GetColor();
+}
+
+
+
+
+
+void cPlayer::TossItem(
+ bool a_bDraggingItem,
+ char a_Amount /* = 1 */,
+ short a_CreateType /* = 0 */,
+ short a_CreateHealth /* = 0 */
+)
+{
+ cItems Drops;
+ if (a_CreateType != 0)
+ {
+ // Just create item without touching the inventory (used in creative mode)
+ Drops.push_back(cItem(a_CreateType, a_Amount, a_CreateHealth));
+ }
+ else
+ {
+ // Drop an item from the inventory:
+ if (a_bDraggingItem)
+ {
+ cItem & Item = GetDraggingItem();
+ if (!Item.IsEmpty())
+ {
+ char OriginalItemAmount = Item.m_ItemCount;
+ Item.m_ItemCount = std::min(OriginalItemAmount, a_Amount);
+ Drops.push_back(Item);
+ if (OriginalItemAmount > a_Amount)
+ {
+ Item.m_ItemCount = OriginalItemAmount - (char)a_Amount;
+ }
+ else
+ {
+ Item.Empty();
+ }
+ }
+ }
+ else
+ {
+ // Else drop equipped item
+ cItem DroppedItem(GetInventory().GetEquippedItem());
+ if (!DroppedItem.IsEmpty())
+ {
+ if (GetInventory().RemoveOneEquippedItem())
+ {
+ DroppedItem.m_ItemCount = 1; // RemoveItem decreases the count, so set it to 1 again
+ Drops.push_back(DroppedItem);
+ }
+ }
+ }
+ }
+ double vX = 0, vY = 0, vZ = 0;
+ EulerToVector(-GetRotation(), GetPitch(), vZ, vX, vY);
+ vY = -vY * 2 + 1.f;
+ m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY() + 1.6f, GetPosZ(), vX * 2, vY * 2, vZ * 2);
+}
+
+
+
+
+
+bool cPlayer::MoveToWorld(const char * a_WorldName)
+{
+ cWorld * World = cRoot::Get()->GetWorld(a_WorldName);
+ if (World == NULL)
+ {
+ LOG("%s: Couldn't find world \"%s\".", __FUNCTION__, a_WorldName);
+ return false;
+ }
+
+ eDimension OldDimension = m_World->GetDimension();
+
+ // Remove all links to the old world
+ m_World->RemovePlayer(this);
+ m_ClientHandle->RemoveFromAllChunks();
+ m_World->RemoveEntity(this);
+
+ // Add player to all the necessary parts of the new world
+ SetWorld(World);
+ World->AddEntity(this);
+ World->AddPlayer(this);
+
+ // If the dimension is different, we can send the respawn packet
+ // http://wiki.vg/Protocol#0x09 says "don't send if dimension is the same" as of 2013_07_02
+ if (OldDimension != World->GetDimension())
+ {
+ m_ClientHandle->SendRespawn();
+ }
+
+ // Stream the new chunks:
+ m_ClientHandle->StreamChunks();
+ return true;
+}
+
+
+
+
+
+void cPlayer::LoadPermissionsFromDisk()
+{
+ m_Groups.clear();
+ m_Permissions.clear();
+
+ cIniFile IniFile("users.ini");
+ if( IniFile.ReadFile() )
+ {
+ std::string Groups = IniFile.GetValue(m_PlayerName, "Groups", "");
+ if( Groups.size() > 0 )
+ {
+ AStringVector Split = StringSplit( Groups, "," );
+ for( unsigned int i = 0; i < Split.size(); i++ )
+ {
+ AddToGroup( Split[i].c_str() );
+ }
+ }
+ else
+ {
+ AddToGroup("Default");
+ }
+
+ m_Color = IniFile.GetValue(m_PlayerName, "Color", "-")[0];
+ }
+ else
+ {
+ LOGWARN("WARNING: Failed to read ini file users.ini");
+ AddToGroup("Default");
+ }
+ ResolvePermissions();
+}
+
+
+
+
+bool cPlayer::LoadFromDisk()
+{
+ LoadPermissionsFromDisk();
+
+ // Log player permissions, cause it's what the cool kids do
+ LOGINFO("Player %s has permissions:", m_PlayerName.c_str() );
+ for( PermissionMap::iterator itr = m_ResolvedPermissions.begin(); itr != m_ResolvedPermissions.end(); ++itr )
+ {
+ if( itr->second ) LOGINFO("%s", itr->first.c_str() );
+ }
+
+ AString SourceFile;
+ Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() );
+
+ cFile f;
+ if (!f.Open(SourceFile, cFile::fmRead))
+ {
+ // This is a new player whom we haven't seen yet, bail out, let them have the defaults
+ return false;
+ }
+
+ AString buffer;
+ if (f.ReadRestOfFile(buffer) != f.GetSize())
+ {
+ LOGWARNING("Cannot read player data from file \"%s\"", SourceFile.c_str());
+ return false;
+ }
+ f.Close();
+
+ Json::Value root;
+ Json::Reader reader;
+ if (!reader.parse(buffer, root, false))
+ {
+ LOGWARNING("Cannot parse player data in file \"%s\", player will be reset", SourceFile.c_str());
+ }
+
+ Json::Value & JSON_PlayerPosition = root["position"];
+ if (JSON_PlayerPosition.size() == 3)
+ {
+ SetPosX(JSON_PlayerPosition[(unsigned int)0].asDouble());
+ SetPosY(JSON_PlayerPosition[(unsigned int)1].asDouble());
+ SetPosZ(JSON_PlayerPosition[(unsigned int)2].asDouble());
+ m_LastPosX = GetPosX();
+ m_LastPosY = GetPosY();
+ m_LastPosZ = GetPosZ();
+ m_LastFoodPos = GetPosition();
+ }
+
+ Json::Value & JSON_PlayerRotation = root["rotation"];
+ if (JSON_PlayerRotation.size() == 3)
+ {
+ SetRotation ((float)JSON_PlayerRotation[(unsigned int)0].asDouble());
+ SetPitch ((float)JSON_PlayerRotation[(unsigned int)1].asDouble());
+ SetRoll ((float)JSON_PlayerRotation[(unsigned int)2].asDouble());
+ }
+
+ m_Health = root.get("health", 0).asInt();
+
+ m_FoodLevel = root.get("food", MAX_FOOD_LEVEL).asInt();
+ m_FoodSaturationLevel = root.get("foodSaturation", MAX_FOOD_LEVEL).asDouble();
+ m_FoodTickTimer = root.get("foodTickTimer", 0).asInt();
+ m_FoodExhaustionLevel = root.get("foodExhaustion", 0).asDouble();
+
+ m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt();
+
+ m_Inventory.LoadFromJson(root["inventory"]);
+
+ m_LoadedWorldName = root.get("world", "world").asString();
+
+ LOGD("Player \"%s\" was read from file, spawning at {%.2f, %.2f, %.2f} in world \"%s\"",
+ m_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str()
+ );
+
+ return true;
+}
+
+
+
+
+
+bool cPlayer::SaveToDisk()
+{
+ cMakeDir::MakeDir("players");
+
+ // create the JSON data
+ Json::Value JSON_PlayerPosition;
+ JSON_PlayerPosition.append(Json::Value(GetPosX()));
+ JSON_PlayerPosition.append(Json::Value(GetPosY()));
+ JSON_PlayerPosition.append(Json::Value(GetPosZ()));
+
+ Json::Value JSON_PlayerRotation;
+ JSON_PlayerRotation.append(Json::Value(GetRotation()));
+ JSON_PlayerRotation.append(Json::Value(GetPitch()));
+ JSON_PlayerRotation.append(Json::Value(GetRoll()));
+
+ Json::Value JSON_Inventory;
+ m_Inventory.SaveToJson(JSON_Inventory);
+
+ Json::Value root;
+ root["position"] = JSON_PlayerPosition;
+ root["rotation"] = JSON_PlayerRotation;
+ root["inventory"] = JSON_Inventory;
+ root["health"] = m_Health;
+ root["food"] = m_FoodLevel;
+ root["foodSaturation"] = m_FoodSaturationLevel;
+ root["foodTickTimer"] = m_FoodTickTimer;
+ root["foodExhaustion"] = m_FoodExhaustionLevel;
+ root["world"] = GetWorld()->GetName();
+
+ if (m_GameMode == GetWorld()->GetGameMode())
+ {
+ root["gamemode"] = (int) eGameMode_NotSet;
+ }
+ else
+ {
+ root["gamemode"] = (int) m_GameMode;
+ }
+
+ Json::StyledWriter writer;
+ std::string JsonData = writer.write(root);
+
+ AString SourceFile;
+ Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() );
+
+ cFile f;
+ if (!f.Open(SourceFile, cFile::fmWrite))
+ {
+ LOGERROR("ERROR WRITING PLAYER \"%s\" TO FILE \"%s\" - cannot open file", m_PlayerName.c_str(), SourceFile.c_str());
+ return false;
+ }
+ if (f.Write(JsonData.c_str(), JsonData.size()) != (int)JsonData.size())
+ {
+ LOGERROR("ERROR WRITING PLAYER JSON TO FILE \"%s\"", SourceFile.c_str());
+ return false;
+ }
+ return true;
+}
+
+
+
+
+
+cPlayer::StringList cPlayer::GetResolvedPermissions()
+{
+ StringList Permissions;
+
+ const PermissionMap& ResolvedPermissions = m_ResolvedPermissions;
+ for( PermissionMap::const_iterator itr = ResolvedPermissions.begin(); itr != ResolvedPermissions.end(); ++itr )
+ {
+ if( itr->second ) Permissions.push_back( itr->first );
+ }
+
+ return Permissions;
+}
+
+
+
+
+
+void cPlayer::UseEquippedItem()
+{
+ if (GetGameMode() == gmCreative) // No damage in creative
+ {
+ return;
+ }
+
+ GetInventory().DamageEquippedItem();
+}
+
+
+
+
+
+void cPlayer::HandleFood(void)
+{
+ // Ref.: http://www.minecraftwiki.net/wiki/Hunger
+
+ // Remember the food level before processing, for later comparison
+ int LastFoodLevel = m_FoodLevel;
+
+ // Heal or damage, based on the food level, using the m_FoodTickTimer:
+ if ((m_FoodLevel > 17) || (m_FoodLevel <= 0))
+ {
+ m_FoodTickTimer++;
+ if (m_FoodTickTimer >= 80)
+ {
+ m_FoodTickTimer = 0;
+
+ if (m_FoodLevel >= 17)
+ {
+ // Regenerate health from food, incur 3 pts of food exhaustion:
+ Heal(1);
+ m_FoodExhaustionLevel += 3;
+ }
+ else if (m_FoodLevel <= 0)
+ {
+ // Damage from starving
+ TakeDamage(dtStarving, NULL, 1, 1, 0);
+ }
+ }
+ }
+
+ // Apply food poisoning food exhaustion:
+ if (m_FoodPoisonedTicksRemaining > 0)
+ {
+ m_FoodPoisonedTicksRemaining--;
+ m_FoodExhaustionLevel += 0.025; // 0.5 per second = 0.025 per tick
+ }
+
+ // Apply food exhaustion that has accumulated:
+ if (m_FoodExhaustionLevel >= 4)
+ {
+ m_FoodExhaustionLevel -= 4;
+
+ if (m_FoodSaturationLevel >= 1)
+ {
+ m_FoodSaturationLevel -= 1;
+ }
+ else
+ {
+ m_FoodLevel = std::max(m_FoodLevel - 1, 0);
+ }
+ }
+
+ if (m_FoodLevel != LastFoodLevel)
+ {
+ SendHealth();
+ }
+}
+
+
+
+
+
+void cPlayer::ApplyFoodExhaustionFromMovement(cChunk & a_Chunk)
+{
+ if (IsGameModeCreative())
+ {
+ return;
+ }
+
+ // Calculate the distance travelled, update the last pos:
+ Vector3d Movement(GetPosition() - m_LastFoodPos);
+ Movement.y = 0; // Only take XZ movement into account
+ m_LastFoodPos = GetPosition();
+
+ // If riding anything, apply no food exhaustion
+ if (m_AttachedTo != NULL)
+ {
+ return;
+ }
+
+ // Get the type of block the player's standing in:
+ BLOCKTYPE BlockIn;
+ int RelX = (int)floor(m_LastPosX) - a_Chunk.GetPosX() * cChunkDef::Width;
+ int RelY = (int)floor(m_LastPosY + 0.1);
+ int RelZ = (int)floor(m_LastPosZ) - a_Chunk.GetPosZ() * cChunkDef::Width;
+ // Use Unbounded, because we're being called *after* processing super::Tick(), which could have changed our chunk
+ VERIFY(a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockIn));
+
+ // Apply the exhaustion based on distance travelled:
+ double BaseExhaustion = Movement.Length();
+ if (IsSprinting())
+ {
+ // 0.1 pt per meter sprinted
+ BaseExhaustion = BaseExhaustion * 0.1;
+ }
+ else if (IsBlockWater(BlockIn))
+ {
+ // 0.015 pt per meter swum
+ BaseExhaustion = BaseExhaustion * 0.015;
+ }
+ else
+ {
+ // 0.01 pt per meter walked / sneaked
+ BaseExhaustion = BaseExhaustion * 0.01;
+ }
+ m_FoodExhaustionLevel += BaseExhaustion;
+}
+
+
+
+
diff --git a/source/Player.h b/source/Player.h
index fed222157..eea8c1596 100644
--- a/source/Player.h
+++ b/source/Player.h
@@ -28,6 +28,7 @@ public:
{
MAX_HEALTH = 20,
MAX_FOOD_LEVEL = 20,
+ EATING_TICKS = 30, ///< Number of ticks it takes to eat an item
} ;
// tolua_end
@@ -65,7 +66,7 @@ public:
double GetEyeHeight(void) const; // tolua_export
Vector3d GetEyePosition(void) const; // tolua_export
inline bool IsOnGround(void) const {return m_bTouchGround; } // tolua_export
- inline const double GetStance(void) const { return GetPosY() + 1.62; } // tolua_export // TODO: Proper stance when crouching etc.
+ inline const double GetStance(void) const { return GetPosY() + 1.62; } // tolua_export // TODO: Proper stance when crouching etc.
inline cInventory & GetInventory(void) { return m_Inventory; } // tolua_export
inline const cInventory & GetInventory(void) const { return m_Inventory; }
@@ -73,18 +74,42 @@ public:
virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) override;
- eGameMode GetGameMode(void) const { return m_GameMode; } // tolua_export
- std::string GetIP() { return m_IP; } // tolua_export
- float GetLastBlockActionTime() { return m_LastBlockActionTime; } // tolua_export
- int GetLastBlockActionCnt() { return m_LastBlockActionCnt; } // tolua_export
- void SetLastBlockActionCnt( int ); // tolua_export
- void SetLastBlockActionTime(); // tolua_export
- void SetGameMode( eGameMode a_GameMode ); // tolua_export
- void LoginSetGameMode( eGameMode a_GameMode );
+ // tolua_begin
+
+ /// Returns the current gamemode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable
+ eGameMode GetGameMode(void) const { return m_GameMode; }
+
+ /** Sets the gamemode for the player.
+ The gamemode may be gmNotSet, in that case the player inherits the world's gamemode.
+ Updates the gamemode on the client (sends the packet)
+ */
+ void SetGameMode(eGameMode a_GameMode);
+
+ /// Returns true if the player is in Creative mode, either explicitly, or by inheriting from current world
+ bool IsGameModeCreative(void) const;
+
+ /// Returns true if the player is in Survival mode, either explicitly, or by inheriting from current world
+ bool IsGameModeSurvival(void) const;
+
+ /// Returns true if the player is in Adventure mode, either explicitly, or by inheriting from current world
+ bool IsGameModeAdventure(void) const;
+
+ AString GetIP(void) const { return m_IP; } // tolua_export
+
+ // tolua_end
+
void SetIP(const AString & a_IP);
+ float GetLastBlockActionTime() { return m_LastBlockActionTime; }
+ int GetLastBlockActionCnt() { return m_LastBlockActionCnt; }
+ void SetLastBlockActionCnt( int );
+ void SetLastBlockActionTime();
+
+ // Sets the current gamemode, doesn't check validity, doesn't send update packets to client
+ void LoginSetGameMode(eGameMode a_GameMode);
+
/// Tries to move to a new position, with collision checks and stuff
- virtual void MoveTo( const Vector3d & a_NewPos ); // tolua_export
+ virtual void MoveTo( const Vector3d & a_NewPos ); // tolua_export
cWindow * GetWindow(void) { return m_CurrentWindow; } // tolua_export
const cWindow * GetWindow(void) const { return m_CurrentWindow; }
@@ -136,6 +161,9 @@ public:
double GetFoodExhaustionLevel (void) const { return m_FoodExhaustionLevel; }
int GetFoodPoisonedTicksRemaining(void) const { return m_FoodPoisonedTicksRemaining; }
+ /// Returns true if the player is satiated, i. e. their foodlevel is at the max and they cannot eat anymore
+ bool IsSatiated(void) const { return (m_FoodLevel >= MAX_FOOD_LEVEL); }
+
void SetFoodLevel (int a_FoodLevel);
void SetFoodSaturationLevel (double a_FoodSaturationLevel);
void SetFoodTickTimer (int a_FoodTickTimer);
@@ -151,8 +179,20 @@ public:
/// Starts the food poisoning for the specified amount of ticks; if already foodpoisoned, sets FoodPoisonedTicksRemaining to the larger of the two
void FoodPoison(int a_NumTicks);
+ /// Returns true if the player is currently in the process of eating the currently equipped item
+ bool IsEating(void) const { return (m_EatingFinishTick >= 0); }
+
// tolua_end
+ /// Starts eating the currently equipped item. Resets the eating timer and sends the proper animation packet
+ void StartEating(void);
+
+ /// Finishes eating the currently equipped item. Consumes the item, updates health and broadcasts the packets
+ void FinishEating(void);
+
+ /// Aborts the current eating operation
+ void AbortEating(void);
+
virtual void KilledBy(cEntity * a_Killer) override;
void Respawn(void); // tolua_export
@@ -214,6 +254,7 @@ public:
// cEntity overrides:
virtual bool IsCrouched (void) const { return m_IsCrouched; }
virtual bool IsSprinting(void) const { return m_IsSprinting; }
+ virtual bool IsRclking (void) const { return IsEating(); }
protected:
typedef std::map< std::string, bool > PermissionMap;
@@ -285,6 +326,9 @@ protected:
bool m_IsCrouched;
bool m_IsSprinting;
+ /// The world tick in which eating will be finished. -1 if not eating
+ Int64 m_EatingFinishTick;
+
virtual void Destroyed(void);
diff --git a/source/Plugin.cpp b/source/Plugin.cpp
index 23757f492..20943b916 100644
--- a/source/Plugin.cpp
+++ b/source/Plugin.cpp
@@ -2,7 +2,6 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Plugin.h"
-// #include "Pawn.h"
#include "Player.h"
#include "World.h"
#include "CommandOutput.h"
@@ -342,6 +341,17 @@ bool cPlugin::OnPlayerRightClick(cPlayer & a_Player, int a_BlockX, int a_BlockY,
+bool cPlugin::OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity)
+{
+ UNUSED(a_Player);
+ UNUSED(a_Entity);
+ return false;
+}
+
+
+
+
+
bool cPlugin::OnPlayerShooting(cPlayer & a_Player)
{
UNUSED(a_Player);
diff --git a/source/Plugin.h b/source/Plugin.h
index edf3a7c9e..3b3feb99c 100644
--- a/source/Plugin.h
+++ b/source/Plugin.h
@@ -47,43 +47,44 @@ public:
* On all these functions, return true if you want to override default behavior and not call other plugins on that callback.
* You can also return false, so default behavior is used.
**/
- virtual bool OnBlockToPickups (cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups);
- virtual bool OnChat (cPlayer * a_Player, AString & a_Message);
- virtual bool OnChunkAvailable (cWorld * a_World, int a_ChunkX, int a_ChunkZ);
- virtual bool OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc);
- virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc);
- virtual bool OnChunkUnloaded (cWorld * a_World, int a_ChunkX, int a_ChunkZ);
- virtual bool OnChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ);
- virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup);
- virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
- virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason);
- virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split);
- virtual bool OnHandshake (cClientHandle * a_Client, const AString & a_Username);
- virtual bool OnKilling (cEntity & a_Victim, cEntity * a_Killer);
- virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username);
- virtual bool OnPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
- virtual bool OnPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
- virtual bool OnPlayerEating (cPlayer & a_Player);
- virtual bool OnPlayerJoined (cPlayer & a_Player);
- virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status);
- virtual bool OnPlayerMoved (cPlayer & a_Player);
- virtual bool OnPlayerPlacedBlock (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);
- virtual bool OnPlayerPlacingBlock (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);
- virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ);
- virtual bool OnPlayerShooting (cPlayer & a_Player);
- virtual bool OnPlayerSpawned (cPlayer & a_Player);
- virtual bool OnPlayerTossingItem (cPlayer & a_Player);
- virtual bool OnPlayerUsedBlock (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);
- virtual bool OnPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ);
- virtual bool OnPlayerUsingBlock (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);
- virtual bool OnPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ);
- virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
- virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
- virtual bool OnTakeDamage (cEntity & a_Receiver, TakeDamageInfo & a_TakeDamageInfo);
- virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player);
- virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player);
- virtual bool OnWeatherChanged (cWorld & a_World);
- virtual bool OnWeatherChanging (cWorld & a_World, eWeather & a_NewWeather);
+ virtual bool OnBlockToPickups (cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups);
+ virtual bool OnChat (cPlayer * a_Player, AString & a_Message);
+ virtual bool OnChunkAvailable (cWorld * a_World, int a_ChunkX, int a_ChunkZ);
+ virtual bool OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc);
+ virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc);
+ virtual bool OnChunkUnloaded (cWorld * a_World, int a_ChunkX, int a_ChunkZ);
+ virtual bool OnChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ);
+ virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup);
+ virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
+ virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason);
+ virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split);
+ virtual bool OnHandshake (cClientHandle * a_Client, const AString & a_Username);
+ virtual bool OnKilling (cEntity & a_Victim, cEntity * a_Killer);
+ virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username);
+ virtual bool OnPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+ virtual bool OnPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+ virtual bool OnPlayerEating (cPlayer & a_Player);
+ virtual bool OnPlayerJoined (cPlayer & a_Player);
+ virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status);
+ virtual bool OnPlayerMoved (cPlayer & a_Player);
+ virtual bool OnPlayerPlacedBlock (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);
+ virtual bool OnPlayerPlacingBlock (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);
+ virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ);
+ virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity);
+ virtual bool OnPlayerShooting (cPlayer & a_Player);
+ virtual bool OnPlayerSpawned (cPlayer & a_Player);
+ virtual bool OnPlayerTossingItem (cPlayer & a_Player);
+ virtual bool OnPlayerUsedBlock (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);
+ virtual bool OnPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ);
+ virtual bool OnPlayerUsingBlock (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);
+ virtual bool OnPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ);
+ virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
+ virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
+ virtual bool OnTakeDamage (cEntity & a_Receiver, TakeDamageInfo & a_TakeDamageInfo);
+ virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player);
+ virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player);
+ virtual bool OnWeatherChanged (cWorld & a_World);
+ virtual bool OnWeatherChanging (cWorld & a_World, eWeather & a_NewWeather);
/** Handles the command split into a_Split, issued by player a_Player.
Command permissions have already been checked.
diff --git a/source/PluginManager.cpp b/source/PluginManager.cpp
index b7a21a7ea..412200256 100644
--- a/source/PluginManager.cpp
+++ b/source/PluginManager.cpp
@@ -678,6 +678,27 @@ bool cPluginManager::CallHookPlayerRightClick(cPlayer & a_Player, int a_BlockX,
+bool cPluginManager::CallHookPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_RIGHT_CLICKING_ENTITY);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPlayerRightClickingEntity(a_Player, a_Entity))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
bool cPluginManager::CallHookPlayerShooting(cPlayer & a_Player)
{
HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_SHOOTING);
@@ -1335,6 +1356,28 @@ bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cComma
+void cPluginManager::TabCompleteCommand(const AString & a_Text, AStringVector & a_Results, cPlayer * a_Player)
+{
+ for (CommandMap::iterator itr = m_Commands.begin(), end = m_Commands.end(); itr != end; ++itr)
+ {
+ if (NoCaseCompare(itr->first.substr(0, a_Text.length()), a_Text) != 0)
+ {
+ // Command name doesn't match
+ continue;
+ }
+ if ((a_Player != NULL) && !a_Player->HasPermission(itr->second.m_Permission))
+ {
+ // Player doesn't have permission for the command
+ continue;
+ }
+ a_Results.push_back(itr->first);
+ }
+}
+
+
+
+
+
bool cPluginManager::AddPlugin(cPlugin * a_Plugin)
{
m_Plugins[a_Plugin->GetDirectory()] = a_Plugin;
diff --git a/source/PluginManager.h b/source/PluginManager.h
index 3832ee455..26bd34dc5 100644
--- a/source/PluginManager.h
+++ b/source/PluginManager.h
@@ -15,6 +15,9 @@ class cWorld;
// fwd: ChunkDesc.h
class cChunkDesc;
+// fwd: Entityes/Entity.h
+class cEntity;
+
// fwd: Player.h
class cPlayer;
@@ -69,6 +72,7 @@ public: // tolua_export
HOOK_PLAYER_PLACED_BLOCK,
HOOK_PLAYER_PLACING_BLOCK,
HOOK_PLAYER_RIGHT_CLICK,
+ HOOK_PLAYER_RIGHT_CLICKING_ENTITY,
HOOK_PLAYER_SHOOTING,
HOOK_PLAYER_SPAWNED,
HOOK_PLAYER_TOSSING_ITEM,
@@ -114,43 +118,44 @@ public: // tolua_export
unsigned int GetNumPlugins() const; // tolua_export
- bool CallHookBlockToPickups (cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups);
- bool CallHookChat (cPlayer * a_Player, AString & a_Message);
- bool CallHookChunkAvailable (cWorld * a_World, int a_ChunkX, int a_ChunkZ);
- bool CallHookChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc);
- bool CallHookChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc);
- bool CallHookChunkUnloaded (cWorld * a_World, int a_ChunkX, int a_ChunkZ);
- bool CallHookChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ);
- bool CallHookCollectingPickup (cPlayer * a_Player, cPickup & a_Pickup);
- bool CallHookCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
- bool CallHookDisconnect (cPlayer * a_Player, const AString & a_Reason);
- bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == NULL, it is a console cmd
- bool CallHookHandshake (cClientHandle * a_ClientHandle, const AString & a_Username);
- bool CallHookKilling (cEntity & a_Victim, cEntity * a_Killer);
- bool CallHookLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username);
- bool CallHookPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
- bool CallHookPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
- bool CallHookPlayerEating (cPlayer & a_Player);
- bool CallHookPlayerJoined (cPlayer & a_Player);
- bool CallHookPlayerMoving (cPlayer & a_Player);
- bool CallHookPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status);
- bool CallHookPlayerPlacedBlock (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);
- bool CallHookPlayerPlacingBlock (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);
- bool CallHookPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ);
- bool CallHookPlayerShooting (cPlayer & a_Player);
- bool CallHookPlayerSpawned (cPlayer & a_Player);
- bool CallHookPlayerTossingItem (cPlayer & a_Player);
- bool CallHookPlayerUsedBlock (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);
- bool CallHookPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ);
- bool CallHookPlayerUsingBlock (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);
- bool CallHookPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ);
- bool CallHookPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
- bool CallHookPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
- bool CallHookTakeDamage (cEntity & a_Receiver, TakeDamageInfo & a_TDI);
- bool CallHookUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player);
- bool CallHookUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player);
- bool CallHookWeatherChanged (cWorld & a_World);
- bool CallHookWeatherChanging (cWorld & a_World, eWeather & a_NewWeather);
+ bool CallHookBlockToPickups (cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups);
+ bool CallHookChat (cPlayer * a_Player, AString & a_Message);
+ bool CallHookChunkAvailable (cWorld * a_World, int a_ChunkX, int a_ChunkZ);
+ bool CallHookChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc);
+ bool CallHookChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc);
+ bool CallHookChunkUnloaded (cWorld * a_World, int a_ChunkX, int a_ChunkZ);
+ bool CallHookChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ);
+ bool CallHookCollectingPickup (cPlayer * a_Player, cPickup & a_Pickup);
+ bool CallHookCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
+ bool CallHookDisconnect (cPlayer * a_Player, const AString & a_Reason);
+ bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == NULL, it is a console cmd
+ bool CallHookHandshake (cClientHandle * a_ClientHandle, const AString & a_Username);
+ bool CallHookKilling (cEntity & a_Victim, cEntity * a_Killer);
+ bool CallHookLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username);
+ bool CallHookPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+ bool CallHookPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+ bool CallHookPlayerEating (cPlayer & a_Player);
+ bool CallHookPlayerJoined (cPlayer & a_Player);
+ bool CallHookPlayerMoving (cPlayer & a_Player);
+ bool CallHookPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status);
+ bool CallHookPlayerPlacedBlock (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);
+ bool CallHookPlayerPlacingBlock (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);
+ bool CallHookPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ);
+ bool CallHookPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity);
+ bool CallHookPlayerShooting (cPlayer & a_Player);
+ bool CallHookPlayerSpawned (cPlayer & a_Player);
+ bool CallHookPlayerTossingItem (cPlayer & a_Player);
+ bool CallHookPlayerUsedBlock (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);
+ bool CallHookPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ);
+ bool CallHookPlayerUsingBlock (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);
+ bool CallHookPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ);
+ bool CallHookPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
+ bool CallHookPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
+ bool CallHookTakeDamage (cEntity & a_Receiver, TakeDamageInfo & a_TDI);
+ bool CallHookUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player);
+ bool CallHookUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player);
+ bool CallHookWeatherChanged (cWorld & a_World);
+ bool CallHookWeatherChanging (cWorld & a_World, eWeather & a_NewWeather);
bool DisablePlugin(const AString & a_PluginName); // tolua_export
bool LoadPlugin (const AString & a_PluginName); // tolua_export
@@ -197,6 +202,11 @@ public: // tolua_export
/// Executes the command split into a_Split, as if it was given on the console. Returns true if executed. Output is sent to the a_Output callback
bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output);
+ /** Appends all commands beginning with a_Text (case-insensitive) into a_Results.
+ If a_Player is not NULL, only commands for which the player has permissions are added.
+ */
+ void TabCompleteCommand(const AString & a_Text, AStringVector & a_Results, cPlayer * a_Player);
+
private:
friend class cRoot;
diff --git a/source/Plugin_NewLua.cpp b/source/Plugin_NewLua.cpp
index c79106761..8b0f047cd 100644
--- a/source/Plugin_NewLua.cpp
+++ b/source/Plugin_NewLua.cpp
@@ -880,6 +880,33 @@ bool cPlugin_NewLua::OnPlayerRightClick(cPlayer & a_Player, int a_BlockX, int a_
+bool cPlugin_NewLua::OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity)
+{
+ cCSLock Lock(m_CriticalSection);
+ const char * FnName = GetHookFnName(cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY);
+ ASSERT(FnName != NULL);
+ if (!PushFunction(FnName))
+ {
+ return false;
+ }
+
+ tolua_pushusertype(m_LuaState, &a_Player, "cPlayer");
+ tolua_pushusertype(m_LuaState, &a_Entity, "cEntity");
+
+ if (!CallFunction(2, 1, FnName))
+ {
+ return false;
+ }
+
+ bool bRetVal = (tolua_toboolean(m_LuaState, -1, 0) > 0);
+ lua_pop(m_LuaState, 1);
+ return bRetVal;
+}
+
+
+
+
+
bool cPlugin_NewLua::OnPlayerShooting(cPlayer & a_Player)
{
cCSLock Lock(m_CriticalSection);
@@ -1519,44 +1546,45 @@ const char * cPlugin_NewLua::GetHookFnName(cPluginManager::PluginHook a_Hook)
{
switch (a_Hook)
{
- case cPluginManager::HOOK_BLOCK_TO_PICKUPS: return "OnBlockToPickups";
- case cPluginManager::HOOK_CHAT: return "OnChat";
- case cPluginManager::HOOK_CHUNK_AVAILABLE: return "OnChunkAvailable";
- case cPluginManager::HOOK_CHUNK_GENERATED: return "OnChunkGenerated";
- case cPluginManager::HOOK_CHUNK_GENERATING: return "OnChunkGenerating";
- case cPluginManager::HOOK_CHUNK_UNLOADED: return "OnChunkUnloaded";
- case cPluginManager::HOOK_CHUNK_UNLOADING: return "OnChunkUnloading";
- case cPluginManager::HOOK_COLLECTING_PICKUP: return "OnCollectingPickup";
- case cPluginManager::HOOK_CRAFTING_NO_RECIPE: return "OnCraftingNoRecipe";
- case cPluginManager::HOOK_DISCONNECT: return "OnDisconnect";
- case cPluginManager::HOOK_EXECUTE_COMMAND: return "OnExecuteCommand";
- case cPluginManager::HOOK_HANDSHAKE: return "OnHandshake";
- case cPluginManager::HOOK_KILLING: return "OnKilling";
- case cPluginManager::HOOK_LOGIN: return "OnLogin";
- case cPluginManager::HOOK_PLAYER_BREAKING_BLOCK: return "OnPlayerBreakingBlock";
- case cPluginManager::HOOK_PLAYER_BROKEN_BLOCK: return "OnPlayerBrokenBlock";
- case cPluginManager::HOOK_PLAYER_EATING: return "OnPlayerEating";
- case cPluginManager::HOOK_PLAYER_JOINED: return "OnPlayerJoined";
- case cPluginManager::HOOK_PLAYER_LEFT_CLICK: return "OnPlayerLeftClick";
- case cPluginManager::HOOK_PLAYER_MOVING: return "OnPlayerMoving";
- case cPluginManager::HOOK_PLAYER_PLACED_BLOCK: return "OnPlayerPlacedBlock";
- case cPluginManager::HOOK_PLAYER_PLACING_BLOCK: return "OnPlayerPlacingBlock";
- case cPluginManager::HOOK_PLAYER_RIGHT_CLICK: return "OnPlayerRightClick";
- case cPluginManager::HOOK_PLAYER_SHOOTING: return "OnPlayerShooting";
- case cPluginManager::HOOK_PLAYER_SPAWNED: return "OnPlayerSpawned";
- case cPluginManager::HOOK_PLAYER_TOSSING_ITEM: return "OnPlayerTossingItem";
- case cPluginManager::HOOK_PLAYER_USED_BLOCK: return "OnPlayerUsedBlock";
- case cPluginManager::HOOK_PLAYER_USED_ITEM: return "OnPlayerUsedItem";
- case cPluginManager::HOOK_PLAYER_USING_BLOCK: return "OnPlayerUsingBlock";
- case cPluginManager::HOOK_PLAYER_USING_ITEM: return "OnPlayerUsingItem";
- case cPluginManager::HOOK_POST_CRAFTING: return "OnPostCrafting";
- case cPluginManager::HOOK_PRE_CRAFTING: return "OnPreCrafting";
- case cPluginManager::HOOK_TAKE_DAMAGE: return "OnTakeDamage";
- case cPluginManager::HOOK_TICK: return "OnTick";
- case cPluginManager::HOOK_UPDATED_SIGN: return "OnUpdatedSign";
- case cPluginManager::HOOK_UPDATING_SIGN: return "OnUpdatingSign";
- case cPluginManager::HOOK_WEATHER_CHANGED: return "OnWeatherChanged";
- case cPluginManager::HOOK_WEATHER_CHANGING: return "OnWeatherChanging";
+ case cPluginManager::HOOK_BLOCK_TO_PICKUPS: return "OnBlockToPickups";
+ case cPluginManager::HOOK_CHAT: return "OnChat";
+ case cPluginManager::HOOK_CHUNK_AVAILABLE: return "OnChunkAvailable";
+ case cPluginManager::HOOK_CHUNK_GENERATED: return "OnChunkGenerated";
+ case cPluginManager::HOOK_CHUNK_GENERATING: return "OnChunkGenerating";
+ case cPluginManager::HOOK_CHUNK_UNLOADED: return "OnChunkUnloaded";
+ case cPluginManager::HOOK_CHUNK_UNLOADING: return "OnChunkUnloading";
+ case cPluginManager::HOOK_COLLECTING_PICKUP: return "OnCollectingPickup";
+ case cPluginManager::HOOK_CRAFTING_NO_RECIPE: return "OnCraftingNoRecipe";
+ case cPluginManager::HOOK_DISCONNECT: return "OnDisconnect";
+ case cPluginManager::HOOK_EXECUTE_COMMAND: return "OnExecuteCommand";
+ case cPluginManager::HOOK_HANDSHAKE: return "OnHandshake";
+ case cPluginManager::HOOK_KILLING: return "OnKilling";
+ case cPluginManager::HOOK_LOGIN: return "OnLogin";
+ case cPluginManager::HOOK_PLAYER_BREAKING_BLOCK: return "OnPlayerBreakingBlock";
+ case cPluginManager::HOOK_PLAYER_BROKEN_BLOCK: return "OnPlayerBrokenBlock";
+ case cPluginManager::HOOK_PLAYER_EATING: return "OnPlayerEating";
+ case cPluginManager::HOOK_PLAYER_JOINED: return "OnPlayerJoined";
+ case cPluginManager::HOOK_PLAYER_LEFT_CLICK: return "OnPlayerLeftClick";
+ case cPluginManager::HOOK_PLAYER_MOVING: return "OnPlayerMoving";
+ case cPluginManager::HOOK_PLAYER_PLACED_BLOCK: return "OnPlayerPlacedBlock";
+ case cPluginManager::HOOK_PLAYER_PLACING_BLOCK: return "OnPlayerPlacingBlock";
+ case cPluginManager::HOOK_PLAYER_RIGHT_CLICK: return "OnPlayerRightClick";
+ case cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY: return "OnPlayerRightClickingEntity";
+ case cPluginManager::HOOK_PLAYER_SHOOTING: return "OnPlayerShooting";
+ case cPluginManager::HOOK_PLAYER_SPAWNED: return "OnPlayerSpawned";
+ case cPluginManager::HOOK_PLAYER_TOSSING_ITEM: return "OnPlayerTossingItem";
+ case cPluginManager::HOOK_PLAYER_USED_BLOCK: return "OnPlayerUsedBlock";
+ case cPluginManager::HOOK_PLAYER_USED_ITEM: return "OnPlayerUsedItem";
+ case cPluginManager::HOOK_PLAYER_USING_BLOCK: return "OnPlayerUsingBlock";
+ case cPluginManager::HOOK_PLAYER_USING_ITEM: return "OnPlayerUsingItem";
+ case cPluginManager::HOOK_POST_CRAFTING: return "OnPostCrafting";
+ case cPluginManager::HOOK_PRE_CRAFTING: return "OnPreCrafting";
+ case cPluginManager::HOOK_TAKE_DAMAGE: return "OnTakeDamage";
+ case cPluginManager::HOOK_TICK: return "OnTick";
+ case cPluginManager::HOOK_UPDATED_SIGN: return "OnUpdatedSign";
+ case cPluginManager::HOOK_UPDATING_SIGN: return "OnUpdatingSign";
+ case cPluginManager::HOOK_WEATHER_CHANGED: return "OnWeatherChanged";
+ case cPluginManager::HOOK_WEATHER_CHANGING: return "OnWeatherChanging";
default: return NULL;
} // switch (a_Hook)
}
@@ -1565,7 +1593,7 @@ const char * cPlugin_NewLua::GetHookFnName(cPluginManager::PluginHook a_Hook)
-AString cPlugin_NewLua::HandleWebRequest( HTTPRequest * a_Request )
+AString cPlugin_NewLua::HandleWebRequest(const HTTPRequest * a_Request )
{
cCSLock Lock(m_CriticalSection);
std::string RetVal = "";
@@ -1592,7 +1620,7 @@ AString cPlugin_NewLua::HandleWebRequest( HTTPRequest * a_Request )
//LOGINFO("2. Stack size: %i", lua_gettop(m_LuaState) );
// Push HTTPRequest
- tolua_pushusertype( m_LuaState, a_Request, "HTTPRequest" );
+ tolua_pushusertype( m_LuaState, (void*)a_Request, "const HTTPRequest" );
//LOGINFO("Calling bound function! :D");
int s = lua_pcall( m_LuaState, 1, 1, 0);
diff --git a/source/Plugin_NewLua.h b/source/Plugin_NewLua.h
index 8d51c86f6..a79affd5d 100644
--- a/source/Plugin_NewLua.h
+++ b/source/Plugin_NewLua.h
@@ -37,43 +37,44 @@ public:
virtual void Tick(float a_Dt) override;
- virtual bool OnBlockToPickups (cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups) override;
- virtual bool OnChat (cPlayer * a_Player, AString & a_Message) override;
- virtual bool OnChunkAvailable (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override;
- virtual bool OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) override;
- virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) override;
- virtual bool OnChunkUnloaded (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override;
- virtual bool OnChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override;
- virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup) override;
- virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
- virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) override;
- virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) override;
- virtual bool OnHandshake (cClientHandle * a_Client, const AString & a_Username) override;
- virtual bool OnKilling (cEntity & a_Victim, cEntity * a_Killer) override;
- virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) override;
- virtual bool OnPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
- virtual bool OnPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
- virtual bool OnPlayerEating (cPlayer & a_Player) override;
- virtual bool OnPlayerJoined (cPlayer & a_Player) override;
- virtual bool OnPlayerMoved (cPlayer & a_Player) override;
- virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) override;
- virtual bool OnPlayerPlacedBlock (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;
- virtual bool OnPlayerPlacingBlock (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;
- virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override;
- virtual bool OnPlayerShooting (cPlayer & a_Player) override;
- virtual bool OnPlayerSpawned (cPlayer & a_Player) override;
- virtual bool OnPlayerTossingItem (cPlayer & a_Player) override;
- virtual bool OnPlayerUsedBlock (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;
- virtual bool OnPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override;
- virtual bool OnPlayerUsingBlock (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;
- virtual bool OnPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override;
- virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
- virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
- virtual bool OnTakeDamage (cEntity & a_Receiver, TakeDamageInfo & a_TakeDamageInfo) override;
- virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) override;
- virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) override;
- virtual bool OnWeatherChanged (cWorld & a_World) override;
- virtual bool OnWeatherChanging (cWorld & a_World, eWeather & a_NewWeather) override;
+ virtual bool OnBlockToPickups (cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups) override;
+ virtual bool OnChat (cPlayer * a_Player, AString & a_Message) override;
+ virtual bool OnChunkAvailable (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override;
+ virtual bool OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) override;
+ virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) override;
+ virtual bool OnChunkUnloaded (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override;
+ virtual bool OnChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override;
+ virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup) override;
+ virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
+ virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) override;
+ virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) override;
+ virtual bool OnHandshake (cClientHandle * a_Client, const AString & a_Username) override;
+ virtual bool OnKilling (cEntity & a_Victim, cEntity * a_Killer) override;
+ virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) override;
+ virtual bool OnPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
+ virtual bool OnPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
+ virtual bool OnPlayerEating (cPlayer & a_Player) override;
+ virtual bool OnPlayerJoined (cPlayer & a_Player) override;
+ virtual bool OnPlayerMoved (cPlayer & a_Player) override;
+ virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) override;
+ virtual bool OnPlayerPlacedBlock (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;
+ virtual bool OnPlayerPlacingBlock (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;
+ virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override;
+ virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) override;
+ virtual bool OnPlayerShooting (cPlayer & a_Player) override;
+ virtual bool OnPlayerSpawned (cPlayer & a_Player) override;
+ virtual bool OnPlayerTossingItem (cPlayer & a_Player) override;
+ virtual bool OnPlayerUsedBlock (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;
+ virtual bool OnPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override;
+ virtual bool OnPlayerUsingBlock (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;
+ virtual bool OnPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override;
+ virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
+ virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
+ virtual bool OnTakeDamage (cEntity & a_Receiver, TakeDamageInfo & a_TakeDamageInfo) override;
+ virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) override;
+ virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) override;
+ virtual bool OnWeatherChanged (cWorld & a_World) override;
+ virtual bool OnWeatherChanging (cWorld & a_World, eWeather & a_NewWeather) override;
virtual bool HandleCommand(const AStringVector & a_Split, cPlayer * a_Player) override;
@@ -86,10 +87,10 @@ public:
virtual bool CanAddHook(cPluginManager::PluginHook a_Hook) override;
// cWebPlugin override
- virtual const AString & GetWebTitle(void) const {return GetName(); }
+ virtual const AString GetWebTitle(void) const {return GetName(); }
// cWebPlugin and WebAdmin stuff
- virtual AString HandleWebRequest( HTTPRequest * a_Request ) override;
+ virtual AString HandleWebRequest(const HTTPRequest * a_Request ) override;
bool AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference); // >> EXPORTED IN MANUALBINDINGS <<
/// Binds the command to call the function specified by a Lua function reference. Simply adds to CommandMap.
diff --git a/source/Plugin_Squirrel.cpp b/source/Plugin_Squirrel.cpp
index 27fd01a09..adca4b519 100644
--- a/source/Plugin_Squirrel.cpp
+++ b/source/Plugin_Squirrel.cpp
@@ -1,409 +1,409 @@
-
-#include "Globals.h"
-
-
-
-
-#ifdef USE_SUIRREL
-
-
-
-
-
-#include "Plugin_Squirrel.h"
-#include "squirrelbindings/SquirrelFunctions.h"
-#include "squirrelbindings/SquirrelBindings.h"
-#include "squirrelbindings/SquirrelBaseClass.h"
-
-
-cPlugin_Squirrel::cPlugin_Squirrel( const char* a_PluginName )
- : cPlugin( a_PluginName )
-{
- SetLanguage( cPlugin::E_SQUIRREL );
- m_PluginName = a_PluginName;
-}
-
-cPlugin_Squirrel::~cPlugin_Squirrel()
-{
- delete m_Plugin;
-}
-
-bool cPlugin_Squirrel::Initialize()
-{
- cCSLock Lock(m_CriticalSection);
-
- std::string PluginPath = std::string("Plugins/") + m_PluginName + ".nut";
-
- Sqrat::Script script;
- script.CompileFile(PluginPath);
- if(script.IsNull())
- {
- LOGERROR("Unable to run script \"%s\"", m_PluginName);
- }
-
- try {
- script.Run();
-
- Sqrat::Function construct(Sqrat::RootTable(), m_PluginName);
-
- if(construct.IsNull())
- {
- LOGERROR("Constructor for Plugin \"%s\" not found.", m_PluginName);
- return false;
- }
-
- Sqrat::Object obj = construct.Evaluate<Sqrat::Object>();
-
- ((cSquirrelBaseClass *) obj.GetInstanceUP())->setInstance(this);
-
- m_Plugin = new SquirrelObject(obj);
-
-
- Sqrat::Object PluginName = obj.GetSlot("name");
- if(!PluginName.IsNull())
- {
- this->SetName(PluginName.Cast<const char*>());
- }
-
- Sqrat::Function init = m_Plugin->GetFunction("Initialize");
- if(init.IsNull())
- {
- LOGERROR("Can not initialize plugin \"%s\"", m_PluginName);
- return false;
- }
-
- return init.Evaluate<bool>();
-
- } catch(Sqrat::Exception &e)
- {
- LOGERROR("Initialisation of \"%s\" failed: %s", m_PluginName, e.Message().c_str());
- return false;
- }
-}
-
-
-
-
-
-void cPlugin_Squirrel::OnDisable()
-{
- cCSLock Lock(m_CriticalSection);
-
- if(!m_Plugin->HasFunction("OnDisable")) return;
-
- m_Plugin->GetFunction("OnDisable").Execute();
-}
-
-
-
-
-
-void cPlugin_Squirrel::Tick(float a_Dt)
-{
- cCSLock Lock( m_CriticalSection );
-
- if(!m_Plugin->HasFunction("OnTick")) return;
-
- m_Plugin->GetFunction("OnTick").Execute(a_Dt);
-}
-
-
-
-
-
-bool cPlugin_Squirrel::OnCollectPickup(cPlayer * a_Player, cPickup * a_Pickup)
-{
- cCSLock Lock(m_CriticalSection);
-
- if (!m_Plugin->HasFunction("OnCollectPickup"))
- {
- return false;
- }
-
- return m_Plugin->GetFunction("OnCollectPickup").Evaluate<bool>(a_Player, a_Pickup);
-}
-
-
-
-
-
-bool cPlugin_Squirrel::OnDisconnect(cPlayer* a_Player, const AString & a_Reason)
-{
- cCSLock Lock( m_CriticalSection );
-
- if (!m_Plugin->HasFunction("OnDisconnect")) return false;
-
- return m_Plugin->GetFunction("OnDisconnect").Evaluate<bool>(a_Player, a_Reason);
-}
-
-
-
-
-
-bool cPlugin_Squirrel::OnBlockPlace(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem)
-{
- cCSLock Lock(m_CriticalSection);
-
- if (!m_Plugin->HasFunction("OnBlockPlace")) return false;
-
- return m_Plugin->GetFunction("OnBlockPlace").Evaluate<bool>(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_HeldItem);
-}
-
-
-
-
-
-bool cPlugin_Squirrel::OnBlockDig(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta)
-{
- cCSLock Lock(m_CriticalSection);
-
- if (!m_Plugin->HasFunction("OnBlockDig")) return false;
-
- return m_Plugin->GetFunction("OnBlockDig").Evaluate<bool>(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status, a_OldBlock, a_OldMeta);
-}
-
-
-
-
-
-bool cPlugin_Squirrel::OnChat(cPlayer * a_Player, const AString & a_Message)
-{
- cCSLock Lock(m_CriticalSection);
-
- if (!m_Plugin->HasFunction("OnChat")) return false;
-
- return m_Plugin->GetFunction("OnChat").Evaluate<bool>(a_Player, a_Message);
-
-}
-
-
-
-
-
-bool cPlugin_Squirrel::OnLogin(cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username)
-{
- cCSLock Lock( m_CriticalSection );
-
- if (!m_Plugin->HasFunction("OnLogin"))
- {
- return false;
- }
-
- return m_Plugin->GetFunction("OnLogin").Evaluate<bool>(a_Client, a_ProtocolVersion, a_Username);
-}
-
-
-
-
-
-void cPlugin_Squirrel::OnPlayerSpawn( cPlayer* a_Player )
-{
- cCSLock Lock( m_CriticalSection );
-
- if(!m_Plugin->HasFunction("OnPlayerSpawn")) return;
-
- return m_Plugin->GetFunction("OnPlayerSpawn").Execute(a_Player);
-
-}
-
-
-
-
-
-bool cPlugin_Squirrel::OnPlayerJoin( cPlayer* a_Player )
-{
- cCSLock Lock( m_CriticalSection );
-
- if(!m_Plugin->HasFunction("OnPlayerJoin")) return false;
-
- return m_Plugin->GetFunction("OnPlayerJoin").Evaluate<bool>(a_Player);
-}
-
-
-
-
-
-void cPlugin_Squirrel::OnPlayerMove( cPlayer* a_Player )
-{
- cCSLock Lock( m_CriticalSection );
-
- if(!m_Plugin->HasFunction("OnPlayerMove")) return;
-
- return m_Plugin->GetFunction("OnPlayerMove").Execute(a_Player);
-
-}
-
-
-
-
-
-void cPlugin_Squirrel::OnTakeDamage( cPawn* a_Pawn, TakeDamageInfo* a_TakeDamageInfo )
-{
- cCSLock Lock( m_CriticalSection );
-
- if(!m_Plugin->HasFunction("OnTakeDamage")) return;
-
- return m_Plugin->GetFunction("OnTakeDamage")(a_Pawn, a_TakeDamageInfo);
-}
-
-
-
-
-
-bool cPlugin_Squirrel::OnKilled( cPawn* a_Killed, cEntity* a_Killer )
-{
- cCSLock Lock( m_CriticalSection );
- if(!m_Plugin->HasFunction("OnKilled")) return false;
- return m_Plugin->GetFunction("OnKilled").Evaluate<bool>(a_Killed, a_Killer);
-}
-
-
-
-
-
-void cPlugin_Squirrel::OnChunkGenerated(cWorld * a_World, int a_ChunkX, int a_ChunkZ)
-{
- cCSLock Lock(m_CriticalSection);
- if(!m_Plugin->HasFunction("OnChunkGenerated")) return;
- return m_Plugin->GetFunction("OnChunkGenerated")(a_World, a_ChunkX, a_ChunkZ);
-}
-
-
-
-
-
-bool cPlugin_Squirrel::OnChunkGenerating(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_pLuaChunk)
-{
- cCSLock Lock(m_CriticalSection);
- if(!m_Plugin->HasFunction("OnChunkGenerating")) return false;
- return m_Plugin->GetFunction("OnChunkGenerating").Evaluate<bool>(a_World, a_ChunkX, a_ChunkZ, a_pLuaChunk);
-}
-
-
-
-
-
-bool cPlugin_Squirrel::OnPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe)
-{
- cCSLock Lock(m_CriticalSection);
- if(!m_Plugin->HasFunction("OnPreCrafting")) return false;
- return m_Plugin->GetFunction("OnPreCrafting").Evaluate<bool>((cPlayer *) a_Player, (cCraftingGrid *) a_Grid, a_Recipe);
-
-}
-
-
-
-
-
-bool cPlugin_Squirrel::OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe)
-{
- cCSLock Lock(m_CriticalSection);
- if(!m_Plugin->HasFunction("OnCraftingNoRecipe")) return false;
- return m_Plugin->GetFunction("OnCraftingNoRecipe").Evaluate<bool>((cPlayer *) a_Player, (cCraftingGrid *) a_Grid, a_Recipe);
-}
-
-
-
-
-
-bool cPlugin_Squirrel::OnPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe)
-{
- cCSLock Lock(m_CriticalSection);
-
- if(!m_Plugin->HasFunction("OnPostCrafting")) return false;
- return m_Plugin->GetFunction("OnPostCrafting").Evaluate<bool>((cPlayer *) a_Player, (cCraftingGrid *) a_Grid, a_Recipe);
-}
-
-
-
-
-
-bool cPlugin_Squirrel::OnBlockToPickup(
- BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
- const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups
-)
-{
- cCSLock Lock(m_CriticalSection);
-
- if(!m_Plugin->HasFunction("OnBlockToPickup")) return false;
- return m_Plugin->GetFunction("OnBlockToPickup").Evaluate<bool>(a_BlockType, a_BlockMeta, (cPlayer *) a_Player, a_EquippedItem, a_Pickups);
-
-}
-
-
-
-
-
-bool cPlugin_Squirrel::OnWeatherChanged(cWorld * a_World)
-{
- cCSLock Lock(m_CriticalSection);
-
- if(!m_Plugin->HasFunction("OnWeatherChanged")) return false;
- return m_Plugin->GetFunction("OnWeatherChanged").Evaluate<bool>(a_World);
-}
-
-
-
-
-
-bool cPlugin_Squirrel::OnUpdatingSign(
- cWorld * a_World,
- int a_BlockX, int a_BlockY, int a_BlockZ,
- AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4,
- cPlayer * a_Player
-)
-{
- cCSLock Lock(m_CriticalSection);
-
- if(!m_Plugin->HasFunction("OnUpdatingSign")) return false;
- return m_Plugin->GetFunction("OnUpdatingSign")
- .Evaluate<bool>(
- a_World,
- a_BlockX,
- a_BlockY,
- a_BlockZ,
- a_Line1,
- a_Line2,
- a_Line3,
- a_Line4,
- a_Player
- );
-}
-
-
-
-
-
-bool cPlugin_Squirrel::OnUpdatedSign(
- cWorld * a_World,
- int a_BlockX, int a_BlockY, int a_BlockZ,
- const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4,
- cPlayer * a_Player
-)
-{
- cCSLock Lock(m_CriticalSection);
-
- if(!m_Plugin->HasFunction("OnUpdatedSign")) return false;
- return m_Plugin->GetFunction("OnUpdatedSign")
- .Evaluate<bool>(
- a_World,
- a_BlockX,
- a_BlockY,
- a_BlockZ,
- a_Line1,
- a_Line2,
- a_Line3,
- a_Line4,
- a_Player
- );
-}
-
-
-
-
-
-#endif // USE_SQUIRREL
-
-
-
-
+
+#include "Globals.h"
+
+
+
+
+#ifdef USE_SUIRREL
+
+
+
+
+
+#include "Plugin_Squirrel.h"
+#include "squirrelbindings/SquirrelFunctions.h"
+#include "squirrelbindings/SquirrelBindings.h"
+#include "squirrelbindings/SquirrelBaseClass.h"
+
+
+cPlugin_Squirrel::cPlugin_Squirrel( const char* a_PluginName )
+ : cPlugin( a_PluginName )
+{
+ SetLanguage( cPlugin::E_SQUIRREL );
+ m_PluginName = a_PluginName;
+}
+
+cPlugin_Squirrel::~cPlugin_Squirrel()
+{
+ delete m_Plugin;
+}
+
+bool cPlugin_Squirrel::Initialize()
+{
+ cCSLock Lock(m_CriticalSection);
+
+ std::string PluginPath = std::string("Plugins/") + m_PluginName + ".nut";
+
+ Sqrat::Script script;
+ script.CompileFile(PluginPath);
+ if(script.IsNull())
+ {
+ LOGERROR("Unable to run script \"%s\"", m_PluginName);
+ }
+
+ try {
+ script.Run();
+
+ Sqrat::Function construct(Sqrat::RootTable(), m_PluginName);
+
+ if(construct.IsNull())
+ {
+ LOGERROR("Constructor for Plugin \"%s\" not found.", m_PluginName);
+ return false;
+ }
+
+ Sqrat::Object obj = construct.Evaluate<Sqrat::Object>();
+
+ ((cSquirrelBaseClass *) obj.GetInstanceUP())->setInstance(this);
+
+ m_Plugin = new SquirrelObject(obj);
+
+
+ Sqrat::Object PluginName = obj.GetSlot("name");
+ if(!PluginName.IsNull())
+ {
+ this->SetName(PluginName.Cast<const char*>());
+ }
+
+ Sqrat::Function init = m_Plugin->GetFunction("Initialize");
+ if(init.IsNull())
+ {
+ LOGERROR("Can not initialize plugin \"%s\"", m_PluginName);
+ return false;
+ }
+
+ return init.Evaluate<bool>();
+
+ } catch(Sqrat::Exception &e)
+ {
+ LOGERROR("Initialisation of \"%s\" failed: %s", m_PluginName, e.Message().c_str());
+ return false;
+ }
+}
+
+
+
+
+
+void cPlugin_Squirrel::OnDisable()
+{
+ cCSLock Lock(m_CriticalSection);
+
+ if(!m_Plugin->HasFunction("OnDisable")) return;
+
+ m_Plugin->GetFunction("OnDisable").Execute();
+}
+
+
+
+
+
+void cPlugin_Squirrel::Tick(float a_Dt)
+{
+ cCSLock Lock( m_CriticalSection );
+
+ if(!m_Plugin->HasFunction("OnTick")) return;
+
+ m_Plugin->GetFunction("OnTick").Execute(a_Dt);
+}
+
+
+
+
+
+bool cPlugin_Squirrel::OnCollectPickup(cPlayer * a_Player, cPickup * a_Pickup)
+{
+ cCSLock Lock(m_CriticalSection);
+
+ if (!m_Plugin->HasFunction("OnCollectPickup"))
+ {
+ return false;
+ }
+
+ return m_Plugin->GetFunction("OnCollectPickup").Evaluate<bool>(a_Player, a_Pickup);
+}
+
+
+
+
+
+bool cPlugin_Squirrel::OnDisconnect(cPlayer* a_Player, const AString & a_Reason)
+{
+ cCSLock Lock( m_CriticalSection );
+
+ if (!m_Plugin->HasFunction("OnDisconnect")) return false;
+
+ return m_Plugin->GetFunction("OnDisconnect").Evaluate<bool>(a_Player, a_Reason);
+}
+
+
+
+
+
+bool cPlugin_Squirrel::OnBlockPlace(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem)
+{
+ cCSLock Lock(m_CriticalSection);
+
+ if (!m_Plugin->HasFunction("OnBlockPlace")) return false;
+
+ return m_Plugin->GetFunction("OnBlockPlace").Evaluate<bool>(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_HeldItem);
+}
+
+
+
+
+
+bool cPlugin_Squirrel::OnBlockDig(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta)
+{
+ cCSLock Lock(m_CriticalSection);
+
+ if (!m_Plugin->HasFunction("OnBlockDig")) return false;
+
+ return m_Plugin->GetFunction("OnBlockDig").Evaluate<bool>(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status, a_OldBlock, a_OldMeta);
+}
+
+
+
+
+
+bool cPlugin_Squirrel::OnChat(cPlayer * a_Player, const AString & a_Message)
+{
+ cCSLock Lock(m_CriticalSection);
+
+ if (!m_Plugin->HasFunction("OnChat")) return false;
+
+ return m_Plugin->GetFunction("OnChat").Evaluate<bool>(a_Player, a_Message);
+
+}
+
+
+
+
+
+bool cPlugin_Squirrel::OnLogin(cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username)
+{
+ cCSLock Lock( m_CriticalSection );
+
+ if (!m_Plugin->HasFunction("OnLogin"))
+ {
+ return false;
+ }
+
+ return m_Plugin->GetFunction("OnLogin").Evaluate<bool>(a_Client, a_ProtocolVersion, a_Username);
+}
+
+
+
+
+
+void cPlugin_Squirrel::OnPlayerSpawn( cPlayer* a_Player )
+{
+ cCSLock Lock( m_CriticalSection );
+
+ if(!m_Plugin->HasFunction("OnPlayerSpawn")) return;
+
+ return m_Plugin->GetFunction("OnPlayerSpawn").Execute(a_Player);
+
+}
+
+
+
+
+
+bool cPlugin_Squirrel::OnPlayerJoin( cPlayer* a_Player )
+{
+ cCSLock Lock( m_CriticalSection );
+
+ if(!m_Plugin->HasFunction("OnPlayerJoin")) return false;
+
+ return m_Plugin->GetFunction("OnPlayerJoin").Evaluate<bool>(a_Player);
+}
+
+
+
+
+
+void cPlugin_Squirrel::OnPlayerMove( cPlayer* a_Player )
+{
+ cCSLock Lock( m_CriticalSection );
+
+ if(!m_Plugin->HasFunction("OnPlayerMove")) return;
+
+ return m_Plugin->GetFunction("OnPlayerMove").Execute(a_Player);
+
+}
+
+
+
+
+
+void cPlugin_Squirrel::OnTakeDamage( cPawn* a_Pawn, TakeDamageInfo* a_TakeDamageInfo )
+{
+ cCSLock Lock( m_CriticalSection );
+
+ if(!m_Plugin->HasFunction("OnTakeDamage")) return;
+
+ return m_Plugin->GetFunction("OnTakeDamage")(a_Pawn, a_TakeDamageInfo);
+}
+
+
+
+
+
+bool cPlugin_Squirrel::OnKilled( cPawn* a_Killed, cEntity* a_Killer )
+{
+ cCSLock Lock( m_CriticalSection );
+ if(!m_Plugin->HasFunction("OnKilled")) return false;
+ return m_Plugin->GetFunction("OnKilled").Evaluate<bool>(a_Killed, a_Killer);
+}
+
+
+
+
+
+void cPlugin_Squirrel::OnChunkGenerated(cWorld * a_World, int a_ChunkX, int a_ChunkZ)
+{
+ cCSLock Lock(m_CriticalSection);
+ if(!m_Plugin->HasFunction("OnChunkGenerated")) return;
+ return m_Plugin->GetFunction("OnChunkGenerated")(a_World, a_ChunkX, a_ChunkZ);
+}
+
+
+
+
+
+bool cPlugin_Squirrel::OnChunkGenerating(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_pLuaChunk)
+{
+ cCSLock Lock(m_CriticalSection);
+ if(!m_Plugin->HasFunction("OnChunkGenerating")) return false;
+ return m_Plugin->GetFunction("OnChunkGenerating").Evaluate<bool>(a_World, a_ChunkX, a_ChunkZ, a_pLuaChunk);
+}
+
+
+
+
+
+bool cPlugin_Squirrel::OnPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe)
+{
+ cCSLock Lock(m_CriticalSection);
+ if(!m_Plugin->HasFunction("OnPreCrafting")) return false;
+ return m_Plugin->GetFunction("OnPreCrafting").Evaluate<bool>((cPlayer *) a_Player, (cCraftingGrid *) a_Grid, a_Recipe);
+
+}
+
+
+
+
+
+bool cPlugin_Squirrel::OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe)
+{
+ cCSLock Lock(m_CriticalSection);
+ if(!m_Plugin->HasFunction("OnCraftingNoRecipe")) return false;
+ return m_Plugin->GetFunction("OnCraftingNoRecipe").Evaluate<bool>((cPlayer *) a_Player, (cCraftingGrid *) a_Grid, a_Recipe);
+}
+
+
+
+
+
+bool cPlugin_Squirrel::OnPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe)
+{
+ cCSLock Lock(m_CriticalSection);
+
+ if(!m_Plugin->HasFunction("OnPostCrafting")) return false;
+ return m_Plugin->GetFunction("OnPostCrafting").Evaluate<bool>((cPlayer *) a_Player, (cCraftingGrid *) a_Grid, a_Recipe);
+}
+
+
+
+
+
+bool cPlugin_Squirrel::OnBlockToPickup(
+ BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
+ const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups
+)
+{
+ cCSLock Lock(m_CriticalSection);
+
+ if(!m_Plugin->HasFunction("OnBlockToPickup")) return false;
+ return m_Plugin->GetFunction("OnBlockToPickup").Evaluate<bool>(a_BlockType, a_BlockMeta, (cPlayer *) a_Player, a_EquippedItem, a_Pickups);
+
+}
+
+
+
+
+
+bool cPlugin_Squirrel::OnWeatherChanged(cWorld * a_World)
+{
+ cCSLock Lock(m_CriticalSection);
+
+ if(!m_Plugin->HasFunction("OnWeatherChanged")) return false;
+ return m_Plugin->GetFunction("OnWeatherChanged").Evaluate<bool>(a_World);
+}
+
+
+
+
+
+bool cPlugin_Squirrel::OnUpdatingSign(
+ cWorld * a_World,
+ int a_BlockX, int a_BlockY, int a_BlockZ,
+ AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4,
+ cPlayer * a_Player
+)
+{
+ cCSLock Lock(m_CriticalSection);
+
+ if(!m_Plugin->HasFunction("OnUpdatingSign")) return false;
+ return m_Plugin->GetFunction("OnUpdatingSign")
+ .Evaluate<bool>(
+ a_World,
+ a_BlockX,
+ a_BlockY,
+ a_BlockZ,
+ a_Line1,
+ a_Line2,
+ a_Line3,
+ a_Line4,
+ a_Player
+ );
+}
+
+
+
+
+
+bool cPlugin_Squirrel::OnUpdatedSign(
+ cWorld * a_World,
+ int a_BlockX, int a_BlockY, int a_BlockZ,
+ const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4,
+ cPlayer * a_Player
+)
+{
+ cCSLock Lock(m_CriticalSection);
+
+ if(!m_Plugin->HasFunction("OnUpdatedSign")) return false;
+ return m_Plugin->GetFunction("OnUpdatedSign")
+ .Evaluate<bool>(
+ a_World,
+ a_BlockX,
+ a_BlockY,
+ a_BlockZ,
+ a_Line1,
+ a_Line2,
+ a_Line3,
+ a_Line4,
+ a_Player
+ );
+}
+
+
+
+
+
+#endif // USE_SQUIRREL
+
+
+
+
diff --git a/source/Plugin_Squirrel.h b/source/Plugin_Squirrel.h
index 27b1dd909..1a56172e9 100644
--- a/source/Plugin_Squirrel.h
+++ b/source/Plugin_Squirrel.h
@@ -1,69 +1,69 @@
-
-#pragma once
-
-
-
-
-
-#ifdef USE_SQUIRREL
-
-
-
-
-
-#include "Plugin.h"
-#include <sqrat.h>
-#include "squirrelbindings/SquirrelObject.h"
-
-
-
-
-
-class cPlugin_Squirrel :
- public cPlugin
-{
-public:
- cPlugin_Squirrel(const char* a_PluginName);
- ~cPlugin_Squirrel();
-
- void OnDisable();
- bool Initialize();
-
- void Tick(float a_Dt);
-
- virtual bool OnBlockDig (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) override;
- virtual bool OnBlockPlace (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem) override;
- virtual bool OnBlockToPickup (BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups);
- virtual bool OnChat (cPlayer * a_Player, const AString & a_Message) override;
- virtual void OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override;
- virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_pLuaChunk ) override;
- virtual bool OnCollectPickup (cPlayer * a_Player, cPickup * a_Pickup) override;
- virtual bool OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
- virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) override;
- virtual bool OnKilled (cPawn* a_Killed, cEntity* a_Killer ) override;
- virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) override;
- virtual bool OnPlayerJoin (cPlayer* a_Player ) override;
- virtual void OnPlayerMove (cPlayer* a_Player ) override;
- virtual void OnPlayerSpawn (cPlayer* a_Player ) override;
- virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
- virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
- virtual void OnTakeDamage (cPawn* a_Pawn, TakeDamageInfo* a_TakeDamageInfo ) override;
- virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) override;
- virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) override;
- virtual bool OnWeatherChanged (cWorld * a_World) override;
-
-protected:
- const char * m_PluginName;
- cCriticalSection m_CriticalSection;
- SquirrelObject *m_Plugin;
-};
-
-
-
-
-
-#endif // USE_SQUIRREL
-
-
-
-
+
+#pragma once
+
+
+
+
+
+#ifdef USE_SQUIRREL
+
+
+
+
+
+#include "Plugin.h"
+#include <sqrat.h>
+#include "squirrelbindings/SquirrelObject.h"
+
+
+
+
+
+class cPlugin_Squirrel :
+ public cPlugin
+{
+public:
+ cPlugin_Squirrel(const char* a_PluginName);
+ ~cPlugin_Squirrel();
+
+ void OnDisable();
+ bool Initialize();
+
+ void Tick(float a_Dt);
+
+ virtual bool OnBlockDig (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) override;
+ virtual bool OnBlockPlace (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem) override;
+ virtual bool OnBlockToPickup (BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups);
+ virtual bool OnChat (cPlayer * a_Player, const AString & a_Message) override;
+ virtual void OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override;
+ virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_pLuaChunk ) override;
+ virtual bool OnCollectPickup (cPlayer * a_Player, cPickup * a_Pickup) override;
+ virtual bool OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
+ virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) override;
+ virtual bool OnKilled (cPawn* a_Killed, cEntity* a_Killer ) override;
+ virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) override;
+ virtual bool OnPlayerJoin (cPlayer* a_Player ) override;
+ virtual void OnPlayerMove (cPlayer* a_Player ) override;
+ virtual void OnPlayerSpawn (cPlayer* a_Player ) override;
+ virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
+ virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
+ virtual void OnTakeDamage (cPawn* a_Pawn, TakeDamageInfo* a_TakeDamageInfo ) override;
+ virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) override;
+ virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) override;
+ virtual bool OnWeatherChanged (cWorld * a_World) override;
+
+protected:
+ const char * m_PluginName;
+ cCriticalSection m_CriticalSection;
+ SquirrelObject *m_Plugin;
+};
+
+
+
+
+
+#endif // USE_SQUIRREL
+
+
+
+
diff --git a/source/ProbabDistrib.cpp b/source/ProbabDistrib.cpp
index 98c18b8e1..5fa17c276 100644
--- a/source/ProbabDistrib.cpp
+++ b/source/ProbabDistrib.cpp
@@ -1,142 +1,142 @@
-
-// ProbabDistrib.cpp
-
-// Implements the cProbabDistrib class representing a discrete probability distribution curve and random generator
-
-#include "Globals.h"
-#include "ProbabDistrib.h"
-#include "MersenneTwister.h"
-
-
-
-
-
-
-cProbabDistrib::cProbabDistrib(int a_MaxValue) :
- m_MaxValue(a_MaxValue),
- m_Sum(-1)
-{
-}
-
-
-
-
-
-
-void cProbabDistrib::SetPoints(const cProbabDistrib::cPoints & a_Points)
-{
- ASSERT(!a_Points.empty());
- m_Sum = 0;
- m_Cumulative.clear();
- m_Cumulative.reserve(a_Points.size() + 1);
- int ProbSum = 0;
- int LastProb = 0;
- int LastValue = -1;
- if (a_Points[0].m_Value != 0)
- {
- m_Cumulative.push_back(cPoint(0, 0)); // Always push in the [0, 0] point for easier search algorithm bounds
- LastValue = 0;
- }
- for (cPoints::const_iterator itr = a_Points.begin(), end = a_Points.end(); itr != end; ++itr)
- {
- if (itr->m_Value == LastValue)
- {
- continue;
- }
-
- // Add the current trapezoid to the sum:
- ProbSum += (LastProb + itr->m_Probability) * (itr->m_Value - LastValue) / 2;
- LastProb = itr->m_Probability;
- LastValue = itr->m_Value;
- m_Cumulative.push_back(cPoint(itr->m_Value, ProbSum));
- } // for itr - a_Points[]
- if (LastValue != m_MaxValue)
- {
- m_Cumulative.push_back(cPoint(m_MaxValue, 0)); // Always push in the last point for easier search algorithm bounds
- }
- m_Sum = ProbSum;
-}
-
-
-
-
-
-bool cProbabDistrib::SetDefString(const AString & a_DefString)
-{
- AStringVector Points = StringSplitAndTrim(a_DefString, ";");
- if (Points.empty())
- {
- return false;
- }
- cPoints Pts;
- for (AStringVector::const_iterator itr = Points.begin(), end = Points.end(); itr != end; ++itr)
- {
- AStringVector Split = StringSplitAndTrim(*itr, ",");
- if (Split.size() != 2)
- {
- // Bad format
- return false;
- }
- int Value = atoi(Split[0].c_str());
- int Prob = atoi(Split[1].c_str());
- if (
- ((Value == 0) && (Split[0] != "0")) ||
- ((Prob == 0) && (Split[1] != "0"))
- )
- {
- // Number parse error
- return false;
- }
- Pts.push_back(cPoint(Value, Prob));
- } // for itr - Points[]
-
- SetPoints(Pts);
- return true;
-}
-
-
-
-
-
-int cProbabDistrib::Random(MTRand & a_Rand) const
-{
- int v = a_Rand.randInt(m_Sum);
- return MapValue(v);
-}
-
-
-
-
-
-int cProbabDistrib::MapValue(int a_OrigValue) const
-{
- ASSERT(a_OrigValue >= 0);
- ASSERT(a_OrigValue < m_Sum);
-
- // Binary search through m_Cumulative for placement:
- size_t Lo = 0;
- size_t Hi = m_Cumulative.size() - 1;
- while (Hi - Lo > 1)
- {
- int Mid = (Lo + Hi) / 2;
- int MidProbab = m_Cumulative[Mid].m_Probability;
- if (MidProbab < a_OrigValue)
- {
- Lo = Mid;
- }
- else
- {
- Hi = Mid;
- }
- }
- ASSERT(Hi - Lo == 1);
-
- // Linearly interpolate between Lo and Hi:
- int ProbDif = m_Cumulative[Hi].m_Probability - m_Cumulative[Lo].m_Probability;
- int ValueDif = m_Cumulative[Hi].m_Value - m_Cumulative[Lo].m_Value;
- return m_Cumulative[Lo].m_Value + (a_OrigValue - m_Cumulative[Lo].m_Probability) * ValueDif / ProbDif;
-}
-
-
-
-
+
+// ProbabDistrib.cpp
+
+// Implements the cProbabDistrib class representing a discrete probability distribution curve and random generator
+
+#include "Globals.h"
+#include "ProbabDistrib.h"
+#include "MersenneTwister.h"
+
+
+
+
+
+
+cProbabDistrib::cProbabDistrib(int a_MaxValue) :
+ m_MaxValue(a_MaxValue),
+ m_Sum(-1)
+{
+}
+
+
+
+
+
+
+void cProbabDistrib::SetPoints(const cProbabDistrib::cPoints & a_Points)
+{
+ ASSERT(!a_Points.empty());
+ m_Sum = 0;
+ m_Cumulative.clear();
+ m_Cumulative.reserve(a_Points.size() + 1);
+ int ProbSum = 0;
+ int LastProb = 0;
+ int LastValue = -1;
+ if (a_Points[0].m_Value != 0)
+ {
+ m_Cumulative.push_back(cPoint(0, 0)); // Always push in the [0, 0] point for easier search algorithm bounds
+ LastValue = 0;
+ }
+ for (cPoints::const_iterator itr = a_Points.begin(), end = a_Points.end(); itr != end; ++itr)
+ {
+ if (itr->m_Value == LastValue)
+ {
+ continue;
+ }
+
+ // Add the current trapezoid to the sum:
+ ProbSum += (LastProb + itr->m_Probability) * (itr->m_Value - LastValue) / 2;
+ LastProb = itr->m_Probability;
+ LastValue = itr->m_Value;
+ m_Cumulative.push_back(cPoint(itr->m_Value, ProbSum));
+ } // for itr - a_Points[]
+ if (LastValue != m_MaxValue)
+ {
+ m_Cumulative.push_back(cPoint(m_MaxValue, 0)); // Always push in the last point for easier search algorithm bounds
+ }
+ m_Sum = ProbSum;
+}
+
+
+
+
+
+bool cProbabDistrib::SetDefString(const AString & a_DefString)
+{
+ AStringVector Points = StringSplitAndTrim(a_DefString, ";");
+ if (Points.empty())
+ {
+ return false;
+ }
+ cPoints Pts;
+ for (AStringVector::const_iterator itr = Points.begin(), end = Points.end(); itr != end; ++itr)
+ {
+ AStringVector Split = StringSplitAndTrim(*itr, ",");
+ if (Split.size() != 2)
+ {
+ // Bad format
+ return false;
+ }
+ int Value = atoi(Split[0].c_str());
+ int Prob = atoi(Split[1].c_str());
+ if (
+ ((Value == 0) && (Split[0] != "0")) ||
+ ((Prob == 0) && (Split[1] != "0"))
+ )
+ {
+ // Number parse error
+ return false;
+ }
+ Pts.push_back(cPoint(Value, Prob));
+ } // for itr - Points[]
+
+ SetPoints(Pts);
+ return true;
+}
+
+
+
+
+
+int cProbabDistrib::Random(MTRand & a_Rand) const
+{
+ int v = a_Rand.randInt(m_Sum);
+ return MapValue(v);
+}
+
+
+
+
+
+int cProbabDistrib::MapValue(int a_OrigValue) const
+{
+ ASSERT(a_OrigValue >= 0);
+ ASSERT(a_OrigValue < m_Sum);
+
+ // Binary search through m_Cumulative for placement:
+ size_t Lo = 0;
+ size_t Hi = m_Cumulative.size() - 1;
+ while (Hi - Lo > 1)
+ {
+ int Mid = (Lo + Hi) / 2;
+ int MidProbab = m_Cumulative[Mid].m_Probability;
+ if (MidProbab < a_OrigValue)
+ {
+ Lo = Mid;
+ }
+ else
+ {
+ Hi = Mid;
+ }
+ }
+ ASSERT(Hi - Lo == 1);
+
+ // Linearly interpolate between Lo and Hi:
+ int ProbDif = m_Cumulative[Hi].m_Probability - m_Cumulative[Lo].m_Probability;
+ int ValueDif = m_Cumulative[Hi].m_Value - m_Cumulative[Lo].m_Value;
+ return m_Cumulative[Lo].m_Value + (a_OrigValue - m_Cumulative[Lo].m_Probability) * ValueDif / ProbDif;
+}
+
+
+
+
diff --git a/source/ProbabDistrib.h b/source/ProbabDistrib.h
index ac5f24894..ddaadd9b7 100644
--- a/source/ProbabDistrib.h
+++ b/source/ProbabDistrib.h
@@ -1,74 +1,74 @@
-
-// ProbabDistrib.h
-
-// Declares the cProbabDistrib class representing a discrete probability distribution curve and random generator
-
-/*
-Usage:
-1, Create a cProbabDistrib instance
-2, Initialize the distribution either programmatically, using the SetPoints() function, or using a definition string
-3, Ask for random numbers in that probability distribution using the Random() function
-*/
-
-
-
-
-
-#pragma once
-
-
-
-
-
-// fwd:
-class MTRand;
-
-
-
-
-
-class cProbabDistrib
-{
-public:
- class cPoint
- {
- public:
- int m_Value;
- int m_Probability;
-
- cPoint(int a_Value, int a_Probability) :
- m_Value(a_Value),
- m_Probability(a_Probability)
- {
- }
- } ;
-
- typedef std::vector<cPoint> cPoints;
-
-
- cProbabDistrib(int a_MaxValue);
-
- /// Sets the distribution curve using an array of [value, probability] points, linearly interpolated. a_Points must not be empty.
- void SetPoints(const cPoints & a_Points);
-
- /// Sets the distribution curve using a definition string; returns true on successful parse
- bool SetDefString(const AString & a_DefString);
-
- /// Gets a random value from a_Rand, shapes it into the distribution curve and returns the value.
- int Random(MTRand & a_Rand) const;
-
- /// Maps value in range [0, m_Sum] into the range [0, m_MaxValue] using the stored probability
- int MapValue(int a_OrigValue) const;
-
- int GetSum(void) const { return m_Sum; }
-
-protected:
-
- int m_MaxValue;
- cPoints m_Cumulative; ///< Cumulative probability of the values, sorted, for fast bsearch lookup
- int m_Sum; ///< Sum of all the probabilities across all values in the domain; -1 if not set
-} ;
-
-
-
-
+
+// ProbabDistrib.h
+
+// Declares the cProbabDistrib class representing a discrete probability distribution curve and random generator
+
+/*
+Usage:
+1, Create a cProbabDistrib instance
+2, Initialize the distribution either programmatically, using the SetPoints() function, or using a definition string
+3, Ask for random numbers in that probability distribution using the Random() function
+*/
+
+
+
+
+
+#pragma once
+
+
+
+
+
+// fwd:
+class MTRand;
+
+
+
+
+
+class cProbabDistrib
+{
+public:
+ class cPoint
+ {
+ public:
+ int m_Value;
+ int m_Probability;
+
+ cPoint(int a_Value, int a_Probability) :
+ m_Value(a_Value),
+ m_Probability(a_Probability)
+ {
+ }
+ } ;
+
+ typedef std::vector<cPoint> cPoints;
+
+
+ cProbabDistrib(int a_MaxValue);
+
+ /// Sets the distribution curve using an array of [value, probability] points, linearly interpolated. a_Points must not be empty.
+ void SetPoints(const cPoints & a_Points);
+
+ /// Sets the distribution curve using a definition string; returns true on successful parse
+ bool SetDefString(const AString & a_DefString);
+
+ /// Gets a random value from a_Rand, shapes it into the distribution curve and returns the value.
+ int Random(MTRand & a_Rand) const;
+
+ /// Maps value in range [0, m_Sum] into the range [0, m_MaxValue] using the stored probability
+ int MapValue(int a_OrigValue) const;
+
+ int GetSum(void) const { return m_Sum; }
+
+protected:
+
+ int m_MaxValue;
+ cPoints m_Cumulative; ///< Cumulative probability of the values, sorted, for fast bsearch lookup
+ int m_Sum; ///< Sum of all the probabilities across all values in the domain; -1 if not set
+} ;
+
+
+
+
diff --git a/source/Protocol/ChunkDataSerializer.cpp b/source/Protocol/ChunkDataSerializer.cpp
index 7af07f331..2a9230fee 100644
--- a/source/Protocol/ChunkDataSerializer.cpp
+++ b/source/Protocol/ChunkDataSerializer.cpp
@@ -1,176 +1,176 @@
-
-// ChunkDataSerializer.cpp
-
-// Implements the cChunkDataSerializer class representing the object that can:
-// - serialize chunk data to different protocol versions
-// - cache such serialized data for multiple clients
-
-#include "Globals.h"
-#include "ChunkDataSerializer.h"
-#include "zlib.h"
-
-
-
-
-cChunkDataSerializer::cChunkDataSerializer(
- const cChunkDef::BlockTypes & a_BlockTypes,
- const cChunkDef::BlockNibbles & a_BlockMetas,
- const cChunkDef::BlockNibbles & a_BlockLight,
- const cChunkDef::BlockNibbles & a_BlockSkyLight,
- const unsigned char * a_BiomeData
-) :
- m_BlockTypes(a_BlockTypes),
- m_BlockMetas(a_BlockMetas),
- m_BlockLight(a_BlockLight),
- m_BlockSkyLight(a_BlockSkyLight),
- m_BiomeData(a_BiomeData)
-{
-}
-
-
-
-
-const AString & cChunkDataSerializer::Serialize(int a_Version)
-{
- Serializations::const_iterator itr = m_Serializations.find(a_Version);
- if (itr != m_Serializations.end())
- {
- return itr->second;
- }
-
- AString data;
- switch (a_Version)
- {
- case RELEASE_1_2_5: Serialize29(data); break;
- case RELEASE_1_3_2: Serialize39(data); break;
- // TODO: Other protocol versions may serialize the data differently; implement here
-
- default:
- {
- LOGERROR("cChunkDataSerializer::Serialize(): Unknown version: %d", a_Version);
- ASSERT(!"Unknown chunk data serialization version");
- break;
- }
- }
- m_Serializations[a_Version] = data;
- return m_Serializations[a_Version];
-}
-
-
-
-
-
-void cChunkDataSerializer::Serialize29(AString & a_Data)
-{
- // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream)
-
- const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width;
- const int MetadataOffset = sizeof(m_BlockTypes);
- const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas);
- const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight);
- const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight);
- const int DataSize = BiomeOffset + BiomeDataSize;
-
- // Temporary buffer for the composed data:
- char AllData [DataSize];
-
- memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes));
- memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas));
- memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight));
- memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight));
- memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize);
-
- // Compress the data:
- // In order not to use allocation, use a fixed-size buffer, with the size
- // that uses the same calculation as compressBound():
- const uLongf CompressedMaxSize = DataSize + (DataSize >> 12) + (DataSize >> 14) + (DataSize >> 25) + 16;
- char CompressedBlockData[CompressedMaxSize];
-
- uLongf CompressedSize = compressBound(DataSize);
-
- // Run-time check that our compile-time guess about CompressedMaxSize was enough:
- ASSERT(CompressedSize <= CompressedMaxSize);
-
- compress2((Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION);
-
- // Now put all those data into a_Data:
-
- // "Ground-up continuous", or rather, "biome data present" flag:
- a_Data.push_back('\x01');
-
- // Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively
- // Also, no endian flipping is needed because of the const values
- unsigned short BitMap1 = 0xffff;
- unsigned short BitMap2 = 0;
- a_Data.append((const char *)&BitMap1, sizeof(short));
- a_Data.append((const char *)&BitMap2, sizeof(short));
-
- Int32 CompressedSizeBE = htonl(CompressedSize);
- a_Data.append((const char *)&CompressedSizeBE, sizeof(CompressedSizeBE));
-
- Int32 UnusedInt32 = 0;
- a_Data.append((const char *)&UnusedInt32, sizeof(UnusedInt32));
-
- a_Data.append(CompressedBlockData, CompressedSize);
-}
-
-
-
-
-
-void cChunkDataSerializer::Serialize39(AString & a_Data)
-{
- // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream)
-
- const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width;
- const int MetadataOffset = sizeof(m_BlockTypes);
- const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas);
- const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight);
- const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight);
- const int DataSize = BiomeOffset + BiomeDataSize;
-
- // Temporary buffer for the composed data:
- char AllData [DataSize];
-
- memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes));
- memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas));
- memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight));
- memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight));
- memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize);
-
- // Compress the data:
- // In order not to use allocation, use a fixed-size buffer, with the size
- // that uses the same calculation as compressBound():
- const uLongf CompressedMaxSize = DataSize + (DataSize >> 12) + (DataSize >> 14) + (DataSize >> 25) + 16;
- char CompressedBlockData[CompressedMaxSize];
-
- uLongf CompressedSize = compressBound(DataSize);
-
- // Run-time check that our compile-time guess about CompressedMaxSize was enough:
- ASSERT(CompressedSize <= CompressedMaxSize);
-
- compress2((Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION);
-
- // Now put all those data into a_Data:
-
- // "Ground-up continuous", or rather, "biome data present" flag:
- a_Data.push_back('\x01');
-
- // Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively
- // Also, no endian flipping is needed because of the const values
- unsigned short BitMap1 = 0xffff;
- unsigned short BitMap2 = 0;
- a_Data.append((const char *)&BitMap1, sizeof(short));
- a_Data.append((const char *)&BitMap2, sizeof(short));
-
- Int32 CompressedSizeBE = htonl(CompressedSize);
- a_Data.append((const char *)&CompressedSizeBE, sizeof(CompressedSizeBE));
-
- // Unlike 29, 39 doesn't have the "unused" int
-
- a_Data.append(CompressedBlockData, CompressedSize);
-}
-
-
-
-
+
+// ChunkDataSerializer.cpp
+
+// Implements the cChunkDataSerializer class representing the object that can:
+// - serialize chunk data to different protocol versions
+// - cache such serialized data for multiple clients
+
+#include "Globals.h"
+#include "ChunkDataSerializer.h"
+#include "zlib.h"
+
+
+
+
+cChunkDataSerializer::cChunkDataSerializer(
+ const cChunkDef::BlockTypes & a_BlockTypes,
+ const cChunkDef::BlockNibbles & a_BlockMetas,
+ const cChunkDef::BlockNibbles & a_BlockLight,
+ const cChunkDef::BlockNibbles & a_BlockSkyLight,
+ const unsigned char * a_BiomeData
+) :
+ m_BlockTypes(a_BlockTypes),
+ m_BlockMetas(a_BlockMetas),
+ m_BlockLight(a_BlockLight),
+ m_BlockSkyLight(a_BlockSkyLight),
+ m_BiomeData(a_BiomeData)
+{
+}
+
+
+
+
+const AString & cChunkDataSerializer::Serialize(int a_Version)
+{
+ Serializations::const_iterator itr = m_Serializations.find(a_Version);
+ if (itr != m_Serializations.end())
+ {
+ return itr->second;
+ }
+
+ AString data;
+ switch (a_Version)
+ {
+ case RELEASE_1_2_5: Serialize29(data); break;
+ case RELEASE_1_3_2: Serialize39(data); break;
+ // TODO: Other protocol versions may serialize the data differently; implement here
+
+ default:
+ {
+ LOGERROR("cChunkDataSerializer::Serialize(): Unknown version: %d", a_Version);
+ ASSERT(!"Unknown chunk data serialization version");
+ break;
+ }
+ }
+ m_Serializations[a_Version] = data;
+ return m_Serializations[a_Version];
+}
+
+
+
+
+
+void cChunkDataSerializer::Serialize29(AString & a_Data)
+{
+ // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream)
+
+ const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width;
+ const int MetadataOffset = sizeof(m_BlockTypes);
+ const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas);
+ const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight);
+ const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight);
+ const int DataSize = BiomeOffset + BiomeDataSize;
+
+ // Temporary buffer for the composed data:
+ char AllData [DataSize];
+
+ memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes));
+ memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas));
+ memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight));
+ memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight));
+ memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize);
+
+ // Compress the data:
+ // In order not to use allocation, use a fixed-size buffer, with the size
+ // that uses the same calculation as compressBound():
+ const uLongf CompressedMaxSize = DataSize + (DataSize >> 12) + (DataSize >> 14) + (DataSize >> 25) + 16;
+ char CompressedBlockData[CompressedMaxSize];
+
+ uLongf CompressedSize = compressBound(DataSize);
+
+ // Run-time check that our compile-time guess about CompressedMaxSize was enough:
+ ASSERT(CompressedSize <= CompressedMaxSize);
+
+ compress2((Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION);
+
+ // Now put all those data into a_Data:
+
+ // "Ground-up continuous", or rather, "biome data present" flag:
+ a_Data.push_back('\x01');
+
+ // Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively
+ // Also, no endian flipping is needed because of the const values
+ unsigned short BitMap1 = 0xffff;
+ unsigned short BitMap2 = 0;
+ a_Data.append((const char *)&BitMap1, sizeof(short));
+ a_Data.append((const char *)&BitMap2, sizeof(short));
+
+ Int32 CompressedSizeBE = htonl(CompressedSize);
+ a_Data.append((const char *)&CompressedSizeBE, sizeof(CompressedSizeBE));
+
+ Int32 UnusedInt32 = 0;
+ a_Data.append((const char *)&UnusedInt32, sizeof(UnusedInt32));
+
+ a_Data.append(CompressedBlockData, CompressedSize);
+}
+
+
+
+
+
+void cChunkDataSerializer::Serialize39(AString & a_Data)
+{
+ // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream)
+
+ const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width;
+ const int MetadataOffset = sizeof(m_BlockTypes);
+ const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas);
+ const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight);
+ const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight);
+ const int DataSize = BiomeOffset + BiomeDataSize;
+
+ // Temporary buffer for the composed data:
+ char AllData [DataSize];
+
+ memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes));
+ memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas));
+ memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight));
+ memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight));
+ memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize);
+
+ // Compress the data:
+ // In order not to use allocation, use a fixed-size buffer, with the size
+ // that uses the same calculation as compressBound():
+ const uLongf CompressedMaxSize = DataSize + (DataSize >> 12) + (DataSize >> 14) + (DataSize >> 25) + 16;
+ char CompressedBlockData[CompressedMaxSize];
+
+ uLongf CompressedSize = compressBound(DataSize);
+
+ // Run-time check that our compile-time guess about CompressedMaxSize was enough:
+ ASSERT(CompressedSize <= CompressedMaxSize);
+
+ compress2((Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION);
+
+ // Now put all those data into a_Data:
+
+ // "Ground-up continuous", or rather, "biome data present" flag:
+ a_Data.push_back('\x01');
+
+ // Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively
+ // Also, no endian flipping is needed because of the const values
+ unsigned short BitMap1 = 0xffff;
+ unsigned short BitMap2 = 0;
+ a_Data.append((const char *)&BitMap1, sizeof(short));
+ a_Data.append((const char *)&BitMap2, sizeof(short));
+
+ Int32 CompressedSizeBE = htonl(CompressedSize);
+ a_Data.append((const char *)&CompressedSizeBE, sizeof(CompressedSizeBE));
+
+ // Unlike 29, 39 doesn't have the "unused" int
+
+ a_Data.append(CompressedBlockData, CompressedSize);
+}
+
+
+
+
diff --git a/source/Protocol/ChunkDataSerializer.h b/source/Protocol/ChunkDataSerializer.h
index 513a232b9..a42856356 100644
--- a/source/Protocol/ChunkDataSerializer.h
+++ b/source/Protocol/ChunkDataSerializer.h
@@ -1,48 +1,48 @@
-
-// ChunkDataSerializer.h
-
-// Interfaces to the cChunkDataSerializer class representing the object that can:
-// - serialize chunk data to different protocol versions
-// - cache such serialized data for multiple clients
-
-
-
-
-
-class cChunkDataSerializer
-{
-protected:
- const cChunkDef::BlockTypes & m_BlockTypes;
- const cChunkDef::BlockNibbles & m_BlockMetas;
- const cChunkDef::BlockNibbles & m_BlockLight;
- const cChunkDef::BlockNibbles & m_BlockSkyLight;
- const unsigned char * m_BiomeData;
-
- typedef std::map<int, AString> Serializations;
-
- Serializations m_Serializations;
-
- void Serialize29(AString & a_Data); // Release 1.2.4 and 1.2.5
- void Serialize39(AString & a_Data); // Release 1.3.1 and 1.3.2
-
-public:
- enum
- {
- RELEASE_1_2_5 = 29,
- RELEASE_1_3_2 = 39,
- } ;
-
- cChunkDataSerializer(
- const cChunkDef::BlockTypes & a_BlockTypes,
- const cChunkDef::BlockNibbles & a_BlockMetas,
- const cChunkDef::BlockNibbles & a_BlockLight,
- const cChunkDef::BlockNibbles & a_BlockSkyLight,
- const unsigned char * a_BiomeData
- );
-
- const AString & Serialize(int a_Version); // Returns one of the internal m_Serializations[]
-} ;
-
-
-
-
+
+// ChunkDataSerializer.h
+
+// Interfaces to the cChunkDataSerializer class representing the object that can:
+// - serialize chunk data to different protocol versions
+// - cache such serialized data for multiple clients
+
+
+
+
+
+class cChunkDataSerializer
+{
+protected:
+ const cChunkDef::BlockTypes & m_BlockTypes;
+ const cChunkDef::BlockNibbles & m_BlockMetas;
+ const cChunkDef::BlockNibbles & m_BlockLight;
+ const cChunkDef::BlockNibbles & m_BlockSkyLight;
+ const unsigned char * m_BiomeData;
+
+ typedef std::map<int, AString> Serializations;
+
+ Serializations m_Serializations;
+
+ void Serialize29(AString & a_Data); // Release 1.2.4 and 1.2.5
+ void Serialize39(AString & a_Data); // Release 1.3.1 and 1.3.2
+
+public:
+ enum
+ {
+ RELEASE_1_2_5 = 29,
+ RELEASE_1_3_2 = 39,
+ } ;
+
+ cChunkDataSerializer(
+ const cChunkDef::BlockTypes & a_BlockTypes,
+ const cChunkDef::BlockNibbles & a_BlockMetas,
+ const cChunkDef::BlockNibbles & a_BlockLight,
+ const cChunkDef::BlockNibbles & a_BlockSkyLight,
+ const unsigned char * a_BiomeData
+ );
+
+ const AString & Serialize(int a_Version); // Returns one of the internal m_Serializations[]
+} ;
+
+
+
+
diff --git a/source/Protocol/Protocol.h b/source/Protocol/Protocol.h
index fe9d4d10e..5c46b2726 100644
--- a/source/Protocol/Protocol.h
+++ b/source/Protocol/Protocol.h
@@ -1,192 +1,194 @@
-
-// Protocol.h
-
-// Interfaces to the cProtocol class representing the generic interface that a protocol
-// parser and serializer must implement
-
-
-
-
-
-#pragma once
-
-#include "../Defines.h"
-#include "../Endianness.h"
-
-
-
-
-class cPlayer;
-class cEntity;
-class cWindow;
-class cInventory;
-class cPawn;
-class cPickup;
-class cMonster;
-class cChunkDataSerializer;
-class cWorld;
-class cFallingBlock;
-
-
-
-
-
-typedef unsigned char Byte;
-
-
-
-
-
-class cProtocol
-{
-public:
- cProtocol(cClientHandle * a_Client) :
- m_Client(a_Client)
- {
- }
- virtual ~cProtocol() {}
-
- /// Called when client sends some data
- virtual void DataReceived(const char * a_Data, int a_Size) = 0;
-
- // Sending stuff to clients (alphabetically sorted):
- virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) = 0;
- virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) = 0;
- virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) = 0;
- virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
- virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) = 0;
- virtual void SendChat (const AString & a_Message) = 0;
- virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) = 0;
- virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) = 0;
- virtual void SendDestroyEntity (const cEntity & a_Entity) = 0;
- virtual void SendDisconnect (const AString & a_Reason) = 0;
- virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) = 0;
- virtual void SendEntityHeadLook (const cEntity & a_Entity) = 0;
- virtual void SendEntityLook (const cEntity & a_Entity) = 0;
- virtual void SendEntityMetadata (const cEntity & a_Entity) = 0;
- virtual void SendEntityProperties (const cEntity & a_Entity) = 0;
- virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0;
- virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0;
- virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) = 0;
- virtual void SendEntityVelocity (const cEntity & a_Entity) = 0;
- virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) = 0;
- virtual void SendGameMode (eGameMode a_GameMode) = 0;
- virtual void SendHealth (void) = 0;
- virtual void SendInventoryProgress (char a_WindowID, short a_Progressbar, short a_Value) = 0;
- virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) = 0;
- virtual void SendKeepAlive (int a_PingID) = 0;
- virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) = 0;
- virtual void SendPickupSpawn (const cPickup & a_Pickup) = 0;
- virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) = 0;
- virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) = 0;
- virtual void SendPlayerMaxSpeed (void) = 0; ///< Informs the client of the maximum player speed (1.6.1+)
- virtual void SendPlayerMoveLook (void) = 0;
- virtual void SendPlayerPosition (void) = 0;
- virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0;
- virtual void SendRespawn (void) = 0;
- virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) = 0; // a_Src coords are Block * 8
- virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) = 0;
- virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) = 0;
- virtual void SendSpawnMob (const cMonster & a_Mob) = 0;
- virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) = 0;
- virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType) = 0;
- virtual void SendTeleportEntity (const cEntity & a_Entity) = 0;
- virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) = 0;
- virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) = 0;
- virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) = 0;
- virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) = 0;
- virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) = 0;
- virtual void SendWeather (eWeather a_Weather) = 0;
- virtual void SendWholeInventory (const cInventory & a_Inventory) = 0;
- virtual void SendWholeInventory (const cWindow & a_Window) = 0;
- virtual void SendWindowClose (const cWindow & a_Window) = 0;
- virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) = 0;
-
- /// Returns the ServerID used for authentication through session.minecraft.net
- virtual AString GetAuthServerID(void) = 0;
-
-protected:
- cClientHandle * m_Client;
- cCriticalSection m_CSPacket; //< Each SendXYZ() function must acquire this CS in order to send the whole packet at once
-
- /// A generic data-sending routine, all outgoing packet data needs to be routed through this so that descendants may override it
- virtual void SendData(const char * a_Data, int a_Size) = 0;
-
- /// Called after writing each packet, enables descendants to flush their buffers
- virtual void Flush(void) {};
-
- // Helpers for writing partial packet data, write using SendData()
- void WriteByte(Byte a_Value)
- {
- SendData((const char *)&a_Value, 1);
- }
-
- void WriteShort(short a_Value)
- {
- a_Value = htons(a_Value);
- SendData((const char *)&a_Value, 2);
- }
-
- /*
- void WriteShort(unsigned short a_Value)
- {
- a_Value = htons(a_Value);
- SendData((const char *)&a_Value, 2);
- }
- */
-
- void WriteInt(int a_Value)
- {
- a_Value = htonl(a_Value);
- SendData((const char *)&a_Value, 4);
- }
-
- void WriteUInt(unsigned int a_Value)
- {
- a_Value = htonl(a_Value);
- SendData((const char *)&a_Value, 4);
- }
-
- void WriteInt64 (Int64 a_Value)
- {
- a_Value = HostToNetwork8(&a_Value);
- SendData((const char *)&a_Value, 8);
- }
-
- void WriteFloat (float a_Value)
- {
- unsigned int val = HostToNetwork4(&a_Value);
- SendData((const char *)&val, 4);
- }
-
- void WriteDouble(double a_Value)
- {
- unsigned long long val = HostToNetwork8(&a_Value);
- SendData((const char *)&val, 8);
- }
-
- void WriteString(const AString & a_Value)
- {
- AString UTF16;
- UTF8ToRawBEUTF16(a_Value.c_str(), a_Value.length(), UTF16);
- WriteShort((unsigned short)(UTF16.size() / 2));
- SendData(UTF16.data(), UTF16.size());
- }
-
- void WriteBool(bool a_Value)
- {
- WriteByte(a_Value ? 1 : 0);
- }
-
- void WriteVectorI(const Vector3i & a_Vector)
- {
- WriteInt(a_Vector.x);
- WriteInt(a_Vector.y);
- WriteInt(a_Vector.z);
- }
-} ;
-
-
-
-
-
+
+// Protocol.h
+
+// Interfaces to the cProtocol class representing the generic interface that a protocol
+// parser and serializer must implement
+
+
+
+
+
+#pragma once
+
+#include "../Defines.h"
+#include "../Endianness.h"
+
+
+
+
+class cPlayer;
+class cEntity;
+class cWindow;
+class cInventory;
+class cPawn;
+class cPickup;
+class cMonster;
+class cChunkDataSerializer;
+class cWorld;
+class cFallingBlock;
+
+
+
+
+
+typedef unsigned char Byte;
+
+
+
+
+
+class cProtocol
+{
+public:
+ cProtocol(cClientHandle * a_Client) :
+ m_Client(a_Client)
+ {
+ }
+ virtual ~cProtocol() {}
+
+ /// Called when client sends some data
+ virtual void DataReceived(const char * a_Data, int a_Size) = 0;
+
+ // Sending stuff to clients (alphabetically sorted):
+ virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) = 0;
+ virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) = 0;
+ virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) = 0;
+ virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
+ virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) = 0;
+ virtual void SendChat (const AString & a_Message) = 0;
+ virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) = 0;
+ virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) = 0;
+ virtual void SendDestroyEntity (const cEntity & a_Entity) = 0;
+ virtual void SendDisconnect (const AString & a_Reason) = 0;
+ virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) = 0; ///< Request the client to open up the sign editor for the sign (1.6+)
+ virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) = 0;
+ virtual void SendEntityHeadLook (const cEntity & a_Entity) = 0;
+ virtual void SendEntityLook (const cEntity & a_Entity) = 0;
+ virtual void SendEntityMetadata (const cEntity & a_Entity) = 0;
+ virtual void SendEntityProperties (const cEntity & a_Entity) = 0;
+ virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0;
+ virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0;
+ virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) = 0;
+ virtual void SendEntityVelocity (const cEntity & a_Entity) = 0;
+ virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) = 0;
+ virtual void SendGameMode (eGameMode a_GameMode) = 0;
+ virtual void SendHealth (void) = 0;
+ virtual void SendInventoryProgress (char a_WindowID, short a_Progressbar, short a_Value) = 0;
+ virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) = 0;
+ virtual void SendKeepAlive (int a_PingID) = 0;
+ virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) = 0;
+ virtual void SendPickupSpawn (const cPickup & a_Pickup) = 0;
+ virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) = 0;
+ virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) = 0;
+ virtual void SendPlayerMaxSpeed (void) = 0; ///< Informs the client of the maximum player speed (1.6.1+)
+ virtual void SendPlayerMoveLook (void) = 0;
+ virtual void SendPlayerPosition (void) = 0;
+ virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0;
+ virtual void SendRespawn (void) = 0;
+ virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) = 0; // a_Src coords are Block * 8
+ virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) = 0;
+ virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) = 0;
+ virtual void SendSpawnMob (const cMonster & a_Mob) = 0;
+ virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) = 0;
+ virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType) = 0;
+ virtual void SendTabCompletionResults(const AStringVector & a_Results) = 0;
+ virtual void SendTeleportEntity (const cEntity & a_Entity) = 0;
+ virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) = 0;
+ virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) = 0;
+ virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) = 0;
+ virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) = 0;
+ virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) = 0;
+ virtual void SendWeather (eWeather a_Weather) = 0;
+ virtual void SendWholeInventory (const cInventory & a_Inventory) = 0;
+ virtual void SendWholeInventory (const cWindow & a_Window) = 0;
+ virtual void SendWindowClose (const cWindow & a_Window) = 0;
+ virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) = 0;
+
+ /// Returns the ServerID used for authentication through session.minecraft.net
+ virtual AString GetAuthServerID(void) = 0;
+
+protected:
+ cClientHandle * m_Client;
+ cCriticalSection m_CSPacket; //< Each SendXYZ() function must acquire this CS in order to send the whole packet at once
+
+ /// A generic data-sending routine, all outgoing packet data needs to be routed through this so that descendants may override it
+ virtual void SendData(const char * a_Data, int a_Size) = 0;
+
+ /// Called after writing each packet, enables descendants to flush their buffers
+ virtual void Flush(void) {};
+
+ // Helpers for writing partial packet data, write using SendData()
+ void WriteByte(Byte a_Value)
+ {
+ SendData((const char *)&a_Value, 1);
+ }
+
+ void WriteShort(short a_Value)
+ {
+ a_Value = htons(a_Value);
+ SendData((const char *)&a_Value, 2);
+ }
+
+ /*
+ void WriteShort(unsigned short a_Value)
+ {
+ a_Value = htons(a_Value);
+ SendData((const char *)&a_Value, 2);
+ }
+ */
+
+ void WriteInt(int a_Value)
+ {
+ a_Value = htonl(a_Value);
+ SendData((const char *)&a_Value, 4);
+ }
+
+ void WriteUInt(unsigned int a_Value)
+ {
+ a_Value = htonl(a_Value);
+ SendData((const char *)&a_Value, 4);
+ }
+
+ void WriteInt64 (Int64 a_Value)
+ {
+ a_Value = HostToNetwork8(&a_Value);
+ SendData((const char *)&a_Value, 8);
+ }
+
+ void WriteFloat (float a_Value)
+ {
+ unsigned int val = HostToNetwork4(&a_Value);
+ SendData((const char *)&val, 4);
+ }
+
+ void WriteDouble(double a_Value)
+ {
+ unsigned long long val = HostToNetwork8(&a_Value);
+ SendData((const char *)&val, 8);
+ }
+
+ void WriteString(const AString & a_Value)
+ {
+ AString UTF16;
+ UTF8ToRawBEUTF16(a_Value.c_str(), a_Value.length(), UTF16);
+ WriteShort((unsigned short)(UTF16.size() / 2));
+ SendData(UTF16.data(), UTF16.size());
+ }
+
+ void WriteBool(bool a_Value)
+ {
+ WriteByte(a_Value ? 1 : 0);
+ }
+
+ void WriteVectorI(const Vector3i & a_Vector)
+ {
+ WriteInt(a_Vector.x);
+ WriteInt(a_Vector.y);
+ WriteInt(a_Vector.z);
+ }
+} ;
+
+
+
+
+
diff --git a/source/Protocol/Protocol125.cpp b/source/Protocol/Protocol125.cpp
index fddf7d324..1f2acf50c 100644
--- a/source/Protocol/Protocol125.cpp
+++ b/source/Protocol/Protocol125.cpp
@@ -1,1636 +1,1659 @@
-
-// Protocol125.cpp
-
-// Implements the cProtocol125 class representing the release 1.2.5 protocol (#29)
-/*
-Documentation:
- - protocol: http://wiki.vg/wiki/index.php?title=Protocol&oldid=2513
- - session handling: http://wiki.vg/wiki/index.php?title=Session&oldid=2262
- - slot format: http://wiki.vg/wiki/index.php?title=Slot_Data&oldid=2152
-*/
-
-#include "Globals.h"
-
-#include "Protocol125.h"
-
-#include "../ClientHandle.h"
-#include "ChunkDataSerializer.h"
-#include "../Entity.h"
-#include "../Mobs/Monster.h"
-#include "../Pickup.h"
-#include "../Player.h"
-#include "../ChatColor.h"
-#include "../UI/Window.h"
-#include "../Root.h"
-#include "../Server.h"
-#include "../FallingBlock.h"
-
-
-
-
-
-enum
-{
- PACKET_KEEP_ALIVE = 0x00,
- PACKET_LOGIN = 0x01,
- PACKET_HANDSHAKE = 0x02,
- PACKET_CHAT = 0x03,
- PACKET_UPDATE_TIME = 0x04,
- PACKET_ENTITY_EQUIPMENT = 0x05,
- PACKET_USE_ENTITY = 0x07,
- PACKET_UPDATE_HEALTH = 0x08,
- PACKET_RESPAWN = 0x09,
- PACKET_PLAYER_ON_GROUND = 0x0a,
- PACKET_PLAYER_POS = 0x0b,
- PACKET_PLAYER_LOOK = 0x0c,
- PACKET_PLAYER_MOVE_LOOK = 0x0d,
- PACKET_BLOCK_DIG = 0x0e,
- PACKET_BLOCK_PLACE = 0x0f,
- PACKET_SLOT_SELECTED = 0x10,
- PACKET_USE_BED = 0x11,
- PACKET_ANIMATION = 0x12,
- PACKET_PACKET_ENTITY_ACTION = 0x13,
- PACKET_PLAYER_SPAWN = 0x14,
- PACKET_PICKUP_SPAWN = 0x15,
- PACKET_COLLECT_PICKUP = 0x16,
- PACKET_SPAWN_OBJECT = 0x17,
- PACKET_SPAWN_MOB = 0x18,
- PACKET_ENTITY_VELOCITY = 0x1c,
- PACKET_DESTROY_ENTITY = 0x1d,
- PACKET_ENTITY = 0x1e,
- PACKET_ENT_REL_MOVE = 0x1f,
- PACKET_ENT_LOOK = 0x20,
- PACKET_ENT_REL_MOVE_LOOK = 0x21,
- PACKET_ENT_TELEPORT = 0x22,
- PACKET_ENT_HEAD_LOOK = 0x23,
- PACKET_ENT_STATUS = 0x26,
- PACKET_ATTACH_ENTITY = 0x27,
- PACKET_METADATA = 0x28,
- PACKET_PRE_CHUNK = 0x32,
- PACKET_MAP_CHUNK = 0x33,
- PACKET_MULTI_BLOCK = 0x34,
- PACKET_BLOCK_CHANGE = 0x35,
- PACKET_BLOCK_ACTION = 0x36,
- PACKET_EXPLOSION = 0x3C,
- PACKET_SOUND_EFFECT = 0x3e,
- PACKET_SOUND_PARTICLE_EFFECT = 0x3d,
- PACKET_CHANGE_GAME_STATE = 0x46,
- PACKET_THUNDERBOLT = 0x47,
- PACKET_WINDOW_OPEN = 0x64,
- PACKET_WINDOW_CLOSE = 0x65,
- PACKET_WINDOW_CLICK = 0x66,
- PACKET_INVENTORY_SLOT = 0x67,
- PACKET_INVENTORY_WHOLE = 0x68,
- PACKET_INVENTORY_PROGRESS = 0x69,
- PACKET_CREATIVE_INVENTORY_ACTION = 0x6B,
- PACKET_UPDATE_SIGN = 0x82,
- PACKET_PLAYER_LIST_ITEM = 0xC9,
- PACKET_PLAYER_ABILITIES = 0xca,
- PACKET_PLUGIN_MESSAGE = 0xfa,
- PACKET_PING = 0xfe,
- PACKET_DISCONNECT = 0xff
-} ;
-
-
-
-
-
-#define HANDLE_PACKET_READ(Proc, Type, Var) \
- Type Var; \
- { \
- if (!m_ReceivedData.Proc(Var)) \
- { \
- m_ReceivedData.CheckValid(); \
- return PARSE_INCOMPLETE; \
- } \
- m_ReceivedData.CheckValid(); \
- }
-
-
-
-
-typedef unsigned char Byte;
-
-
-
-
-
-cProtocol125::cProtocol125(cClientHandle * a_Client) :
- super(a_Client),
- m_ReceivedData(32 KiB)
-{
-}
-
-
-
-
-
-void cProtocol125::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_ATTACH_ENTITY);
- WriteInt(a_Entity.GetUniqueID());
- WriteInt((a_Vehicle == NULL) ? -1 : a_Vehicle->GetUniqueID());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
-{
- UNUSED(a_BlockType);
-
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_BLOCK_ACTION);
- WriteInt (a_BlockX);
- WriteShort((short)a_BlockY);
- WriteInt (a_BlockZ);
- WriteByte (a_Byte1);
- WriteByte (a_Byte2);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage)
-{
- // Not supported in this protocol version
-}
-
-
-
-
-
-void cProtocol125::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_BLOCK_CHANGE);
- WriteInt (a_BlockX);
- WriteByte((unsigned char)a_BlockY);
- WriteInt (a_BlockZ);
- WriteByte(a_BlockType);
- WriteByte(a_BlockMeta);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes)
-{
- cCSLock Lock(m_CSPacket);
- if (a_Changes.size() == 1)
- {
- // Special packet for single-block changes
- const sSetBlock & blk = a_Changes.front();
- SendBlockChange(a_ChunkX * cChunkDef::Width + blk.x, blk.y, a_ChunkZ * cChunkDef::Width + blk.z, blk.BlockType, blk.BlockMeta);
- return;
- }
-
- WriteByte (PACKET_MULTI_BLOCK);
- WriteInt (a_ChunkX);
- WriteInt (a_ChunkZ);
- WriteShort((unsigned short)a_Changes.size());
- WriteUInt (sizeof(int) * a_Changes.size());
- for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr)
- {
- unsigned int Coords = itr->y | (itr->z << 8) | (itr->x << 12);
- unsigned int Blocks = itr->BlockMeta | (itr->BlockType << 4);
- WriteUInt(Coords << 16 | Blocks);
- }
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendChat(const AString & a_Message)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_CHAT);
- WriteString(a_Message);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
-{
- cCSLock Lock(m_CSPacket);
-
- // Send the pre-chunk:
- SendPreChunk(a_ChunkX, a_ChunkZ, true);
-
- // Send the chunk data:
- AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_2_5);
- WriteByte(PACKET_MAP_CHUNK);
- WriteInt (a_ChunkX);
- WriteInt (a_ChunkZ);
- SendData(Serialized.data(), Serialized.size());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_COLLECT_PICKUP);
- WriteInt (a_Pickup.GetUniqueID());
- WriteInt (a_Player.GetUniqueID());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendDestroyEntity(const cEntity & a_Entity)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_DESTROY_ENTITY);
- WriteInt (a_Entity.GetUniqueID());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendDisconnect(const AString & a_Reason)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte ((unsigned char)PACKET_DISCONNECT);
- WriteString(a_Reason);
- Flush();
-}
-
-
-
-
-void cProtocol125::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_ENTITY_EQUIPMENT);
- WriteInt (a_Entity.GetUniqueID());
- WriteShort(a_SlotNum);
- WriteShort(a_Item.m_ItemType);
- WriteShort(a_Item.m_ItemDamage);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendEntityHeadLook(const cEntity & a_Entity)
-{
- ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
-
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_ENT_HEAD_LOOK);
- WriteInt (a_Entity.GetUniqueID());
- WriteByte((char)((a_Entity.GetHeadYaw() / 360.f) * 256));
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendEntityLook(const cEntity & a_Entity)
-{
- ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
-
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_ENT_LOOK);
- WriteInt (a_Entity.GetUniqueID());
- WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256));
- WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256));
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendEntityMetadata(const cEntity & a_Entity)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_METADATA);
- WriteInt (a_Entity.GetUniqueID());
- AString MetaData = GetEntityMetaData(a_Entity);
- SendData(MetaData.data(), MetaData.size());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendEntityProperties(const cEntity & a_Entity)
-{
- // Not supported in this protocol version
-}
-
-
-
-
-
-void cProtocol125::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
-{
- ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
-
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_ENT_REL_MOVE);
- WriteInt (a_Entity.GetUniqueID());
- WriteByte(a_RelX);
- WriteByte(a_RelY);
- WriteByte(a_RelZ);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
-{
- ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
-
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_ENT_REL_MOVE_LOOK);
- WriteInt (a_Entity.GetUniqueID());
- WriteByte(a_RelX);
- WriteByte(a_RelY);
- WriteByte(a_RelZ);
- WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256));
- WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256));
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendEntityStatus(const cEntity & a_Entity, char a_Status)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_ENT_STATUS);
- WriteInt (a_Entity.GetUniqueID());
- WriteByte(a_Status);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendEntityVelocity(const cEntity & a_Entity)
-{
- ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
-
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_ENTITY_VELOCITY);
- WriteInt (a_Entity.GetUniqueID());
- WriteShort((short) (a_Entity.GetSpeedX() * 400)); //400 = 8000 / 20
- WriteShort((short) (a_Entity.GetSpeedY() * 400));
- WriteShort((short) (a_Entity.GetSpeedZ() * 400));
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_EXPLOSION);
- WriteDouble (a_BlockX);
- WriteDouble (a_BlockY);
- WriteDouble (a_BlockZ);
- WriteFloat (a_Radius);
- WriteInt (a_BlocksAffected.size());
- for (cVector3iArray::const_iterator itr = a_BlocksAffected.begin(); itr != a_BlocksAffected.end(); ++itr)
- {
- WriteByte ((Byte)(itr->x - a_BlockX));
- WriteByte ((Byte)(itr->y - a_BlockY));
- WriteByte ((Byte)(itr->z - a_BlockZ));
- }
- WriteFloat ((float)a_PlayerMotion.x);
- WriteFloat ((float)a_PlayerMotion.y);
- WriteFloat ((float)a_PlayerMotion.z);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendGameMode(eGameMode a_GameMode)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_CHANGE_GAME_STATE);
- WriteByte(3);
- WriteByte((char)a_GameMode);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendHandshake(const AString & a_ConnectionHash)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_HANDSHAKE);
- WriteString(a_ConnectionHash);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendHealth(void)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_UPDATE_HEALTH);
- WriteShort((short)m_Client->GetPlayer()->GetHealth());
- WriteShort(m_Client->GetPlayer()->GetFoodLevel());
- WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendInventoryProgress(char a_WindowID, short a_ProgressBar, short a_Value)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_INVENTORY_PROGRESS);
- WriteByte (a_WindowID);
- WriteShort(a_ProgressBar);
- WriteShort(a_Value);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_INVENTORY_SLOT);
- WriteByte (a_WindowID);
- WriteShort(a_SlotNum);
- WriteItem (a_Item);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendKeepAlive(int a_PingID)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_KEEP_ALIVE);
- WriteInt (a_PingID);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
-{
- UNUSED(a_World);
- cCSLock Lock(m_CSPacket);
-
- WriteByte (PACKET_LOGIN);
- WriteInt (a_Player.GetUniqueID()); // EntityID of the player
- WriteString(""); // Username, not used
- WriteString("default"); // Level type
- WriteInt ((int)a_Player.GetGameMode());
- WriteInt ((int)(a_World.GetDimension()));
- WriteByte (2); // TODO: Difficulty
- WriteByte (0); // Unused
- WriteByte (60); // Client list width or something
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendPickupSpawn(const cPickup & a_Pickup)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_PICKUP_SPAWN);
- WriteInt (a_Pickup.GetUniqueID());
- WriteShort (a_Pickup.GetItem().m_ItemType);
- WriteByte (a_Pickup.GetItem().m_ItemCount);
- WriteShort (a_Pickup.GetItem().m_ItemDamage);
- WriteVectorI((Vector3i)(a_Pickup.GetPosition() * 32));
- WriteByte ((char)(a_Pickup.GetSpeed().x * 8));
- WriteByte ((char)(a_Pickup.GetSpeed().y * 8));
- WriteByte ((char)(a_Pickup.GetSpeed().z * 8));
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_ANIMATION);
- WriteInt (a_Player.GetUniqueID());
- WriteByte(a_Animation);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline)
-{
- cCSLock Lock(m_CSPacket);
- AString PlayerName(a_Player.GetColor());
- PlayerName.append(a_Player.GetName());
- if (PlayerName.length() > 14)
- {
- PlayerName.erase(14);
- }
- PlayerName += cChatColor::White;
-
- WriteByte ((unsigned char)PACKET_PLAYER_LIST_ITEM);
- WriteString(PlayerName);
- WriteBool (a_IsOnline);
- WriteShort (a_Player.GetClientHandle()->GetPing());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendPlayerMaxSpeed(void)
-{
- // Not supported by this protocol version
-}
-
-
-
-
-
-void cProtocol125::SendPlayerMoveLook(void)
-{
- cCSLock Lock(m_CSPacket);
-
- /*
- LOGD("Sending PlayerMoveLook: {%0.2f, %0.2f, %0.2f}, stance %0.2f, OnGround: %d",
- m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ(), m_Player->GetStance(), m_Player->IsOnGround() ? 1 : 0
- );
- */
-
- WriteByte (PACKET_PLAYER_MOVE_LOOK);
- cPlayer * Player = m_Client->GetPlayer();
- WriteDouble(Player->GetPosX());
- WriteDouble(Player->GetStance() + 0.03); // Add a small amount so that the player doesn't start inside a block
- WriteDouble(Player->GetPosY() + 0.03); // Add a small amount so that the player doesn't start inside a block
- WriteDouble(Player->GetPosZ());
- WriteFloat ((float)(Player->GetRotation()));
- WriteFloat ((float)(Player->GetPitch()));
- WriteBool (Player->IsOnGround());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendPlayerPosition(void)
-{
- cCSLock Lock(m_CSPacket);
- LOGD("Ignore send PlayerPos"); // PlayerPos is a C->S packet only now
-}
-
-
-
-
-
-void cProtocol125::SendPlayerSpawn(const cPlayer & a_Player)
-{
- const cItem & HeldItem = a_Player.GetEquippedItem();
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_PLAYER_SPAWN);
- WriteInt (a_Player.GetUniqueID());
- WriteString(a_Player.GetName());
- WriteInt ((int)(a_Player.GetPosX() * 32));
- WriteInt ((int)(a_Player.GetPosY() * 32));
- WriteInt ((int)(a_Player.GetPosZ() * 32));
- WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256));
- WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256));
- WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendRespawn(void)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_RESPAWN);
- WriteInt ((int)(m_Client->GetPlayer()->GetWorld()->GetDimension()));
- WriteByte (2); // TODO: Difficulty; 2 = Normal
- WriteByte ((char)m_Client->GetPlayer()->GetGameMode());
- WriteShort (256); // Current world height
- WriteString("default");
-}
-
-
-
-
-
-void cProtocol125::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch)
-{
- // Not needed in this protocol version
-}
-
-
-
-
-
-void cProtocol125::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
-{
- // Not implemented in this protocol version
-}
-
-
-
-
-
-void cProtocol125::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock)
-{
- // This protocol version implements falling blocks using the spawn object / vehicle packet:
- SendSpawnObject(a_FallingBlock, 70, a_FallingBlock.GetBlockType(), 0, 0);
-}
-
-
-
-
-
-void cProtocol125::SendSpawnMob(const cMonster & a_Mob)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_SPAWN_MOB);
- WriteInt (a_Mob.GetUniqueID());
- WriteByte (a_Mob.GetMobType());
- WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32));
- WriteByte (0);
- WriteByte (0);
- WriteByte (0);
- AString MetaData = GetEntityMetaData(a_Mob);
- SendData (MetaData.data(), MetaData.size());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch)
-{
- UNUSED(a_Yaw);
- UNUSED(a_Pitch);
-
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_SPAWN_OBJECT);
- WriteInt (a_Entity.GetUniqueID());
- WriteByte(a_ObjectType);
- WriteInt ((int)(a_Entity.GetPosX() * 32));
- WriteInt ((int)(a_Entity.GetPosY() * 32));
- WriteInt ((int)(a_Entity.GetPosZ() * 32));
- WriteByte(a_Pitch);
- WriteByte(a_Yaw);
- WriteInt (a_ObjectData);
- if (a_ObjectData != 0)
- {
- WriteShort((short)(a_Entity.GetSpeedX() * 400));
- WriteShort((short)(a_Entity.GetSpeedY() * 400));
- WriteShort((short)(a_Entity.GetSpeedZ() * 400));
- }
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_SPAWN_OBJECT);
- WriteInt (a_Vehicle.GetUniqueID());
- WriteByte (a_VehicleType);
- WriteInt ((int)(a_Vehicle.GetPosX() * 32));
- WriteInt ((int)(a_Vehicle.GetPosY() * 32));
- WriteInt ((int)(a_Vehicle.GetPosZ() * 32));
- WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256));
- WriteByte ((Byte)((a_Vehicle.GetRotation() / 360.f) * 256));
- WriteInt (1);
- WriteShort((short)(a_Vehicle.GetSpeedX() * 400));
- WriteShort((short)(a_Vehicle.GetSpeedY() * 400));
- WriteShort((short)(a_Vehicle.GetSpeedZ() * 400));
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendTeleportEntity(const cEntity & a_Entity)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_ENT_TELEPORT);
- WriteInt (a_Entity.GetUniqueID());
- WriteInt ((int)(floor(a_Entity.GetPosX() * 32)));
- WriteInt ((int)(floor(a_Entity.GetPosY() * 32)));
- WriteInt ((int)(floor(a_Entity.GetPosZ() * 32)));
- WriteByte ((char)((a_Entity.GetRotation() / 360.f) * 256));
- WriteByte ((char)((a_Entity.GetPitch() / 360.f) * 256));
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_THUNDERBOLT);
- WriteInt (0x7fffffff); // Entity ID of the thunderbolt; we use a constant one
- WriteBool(true); // Unknown bool
- WriteInt (a_BlockX * 32);
- WriteInt (a_BlockY * 32);
- WriteInt (a_BlockZ * 32);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_UPDATE_TIME);
- // Use a_WorldAge for daycount, and a_TimeOfDay for the proper time of day:
- WriteInt64((24000 * (a_WorldAge / 24000)) + (a_TimeOfDay % 24000));
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
-{
- cCSLock Lock(m_CSPacket);
- SendPreChunk(a_ChunkX, a_ChunkZ, false);
-}
-
-
-
-
-
-void cProtocol125::SendUpdateSign(
- int a_BlockX, int a_BlockY, int a_BlockZ,
- const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4
-)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte ((unsigned char)PACKET_UPDATE_SIGN);
- WriteInt (a_BlockX);
- WriteShort ((short)a_BlockY);
- WriteInt (a_BlockZ);
- WriteString(a_Line1);
- WriteString(a_Line2);
- WriteString(a_Line3);
- WriteString(a_Line4);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ )
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_USE_BED);
- WriteInt (a_Entity.GetUniqueID());
- WriteByte(0); // Unknown byte only 0 has been observed
- WriteInt (a_BlockX);
- WriteByte(a_BlockY);
- WriteInt (a_BlockZ);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendWeather(eWeather a_Weather)
-{
- cCSLock Lock(m_CSPacket);
- switch( a_Weather )
- {
- case eWeather_Sunny:
- {
- WriteByte(PACKET_CHANGE_GAME_STATE);
- WriteByte(2); // Stop rain
- WriteByte(0); // Unused
- Flush();
- break;
- }
-
- case eWeather_Rain:
- case eWeather_ThunderStorm:
- {
- WriteByte(PACKET_CHANGE_GAME_STATE);
- WriteByte(1); // Begin rain
- WriteByte(0); // Unused
- Flush();
- break;
- }
- }
-}
-
-
-
-
-
-void cProtocol125::SendWholeInventory(const cInventory & a_Inventory)
-{
- SendWholeInventory(*(a_Inventory.GetOwner().GetWindow()));
-}
-
-
-
-
-
-void cProtocol125::SendWholeInventory(const cWindow & a_Window)
-{
- cCSLock Lock(m_CSPacket);
- cItems Slots;
- a_Window.GetSlots(*(m_Client->GetPlayer()), Slots);
- SendWindowSlots(a_Window.GetWindowID(), Slots.size(), &(Slots[0]));
-}
-
-
-
-
-
-void cProtocol125::SendWindowClose(const cWindow & a_Window)
-{
- if (a_Window.GetWindowType() == cWindow::Inventory)
- {
- // Do not send inventory-window-close
- return;
- }
-
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_WINDOW_CLOSE);
- WriteByte(a_Window.GetWindowID());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots)
-{
- if (a_WindowType < 0)
- {
- // Do not send for inventory windows
- return;
- }
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_WINDOW_OPEN);
- WriteByte (a_WindowID);
- WriteByte (a_WindowType);
- WriteString(a_WindowTitle);
- WriteByte (a_NumSlots);
- Flush();
-}
-
-
-
-
-
-AString cProtocol125::GetAuthServerID(void)
-{
- // http://wiki.vg/wiki/index.php?title=Session&oldid=2262
- // The server generates a random hash and that is used for all clients, unmodified
- return cRoot::Get()->GetServer()->GetServerID();
-}
-
-
-
-
-
-void cProtocol125::SendData(const char * a_Data, int a_Size)
-{
- m_Client->SendData(a_Data, a_Size);
-}
-
-
-
-
-
-void cProtocol125::DataReceived(const char * a_Data, int a_Size)
-{
- if (!m_ReceivedData.Write(a_Data, a_Size))
- {
- // Too much data in the incoming queue, report to caller:
- m_Client->PacketBufferFull();
- return;
- }
-
- // Parse and handle all complete packets in m_ReceivedData:
- while (m_ReceivedData.CanReadBytes(1))
- {
- unsigned char PacketType;
- m_ReceivedData.ReadByte(PacketType);
- switch (ParsePacket(PacketType))
- {
- case PARSE_UNKNOWN:
- {
- // An unknown packet has been received, notify the client and abort:
- m_Client->PacketUnknown(PacketType);
- return;
- }
- case PARSE_ERROR:
- {
- // An error occurred while parsing a known packet, notify the client and abort:
- m_Client->PacketError(PacketType);
- return;
- }
- case PARSE_INCOMPLETE:
- {
- // Incomplete packet, bail out and process with the next batch of data
- m_ReceivedData.ResetRead();
- return;
- }
- default:
- {
- // Packet successfully parsed, commit the read data and try again one more packet
- m_ReceivedData.CommitRead();
- break;
- }
- }
- }
-}
-
-
-
-
-
-int cProtocol125::ParsePacket(unsigned char a_PacketType)
-{
- switch (a_PacketType)
- {
- default: return PARSE_UNKNOWN;
- case PACKET_ANIMATION: return ParseArmAnim();
- case PACKET_BLOCK_DIG: return ParseBlockDig();
- case PACKET_BLOCK_PLACE: return ParseBlockPlace();
- case PACKET_CHAT: return ParseChat();
- case PACKET_CREATIVE_INVENTORY_ACTION: return ParseCreativeInventoryAction();
- case PACKET_DISCONNECT: return ParseDisconnect();
- case PACKET_HANDSHAKE: return ParseHandshake();
- case PACKET_KEEP_ALIVE: return ParseKeepAlive();
- case PACKET_LOGIN: return ParseLogin();
- case PACKET_PACKET_ENTITY_ACTION: return ParseEntityAction();
- case PACKET_PING: return ParsePing();
- case PACKET_PLAYER_ABILITIES: return ParsePlayerAbilities();
- case PACKET_PLAYER_LOOK: return ParsePlayerLook();
- case PACKET_PLAYER_MOVE_LOOK: return ParsePlayerMoveLook();
- case PACKET_PLAYER_ON_GROUND: return ParsePlayerOnGround();
- case PACKET_PLAYER_POS: return ParsePlayerPosition();
- case PACKET_PLUGIN_MESSAGE: return ParsePluginMessage();
- case PACKET_RESPAWN: return ParseRespawn();
- case PACKET_SLOT_SELECTED: return ParseSlotSelected();
- case PACKET_UPDATE_SIGN: return ParseUpdateSign();
- case PACKET_USE_ENTITY: return ParseUseEntity();
- case PACKET_WINDOW_CLICK: return ParseWindowClick();
- case PACKET_WINDOW_CLOSE: return ParseWindowClose();
- }
-}
-
-
-
-
-
-#define HANDLE_PACKET_PARSE(Packet) \
- { \
- int res = Packet.Parse(m_ReceivedData); \
- if (res < 0) \
- { \
- return res; \
- } \
- }
-
-
-
-
-
-int cProtocol125::ParseArmAnim(void)
-{
- HANDLE_PACKET_READ(ReadBEInt, int, EntityID);
- HANDLE_PACKET_READ(ReadChar, char, Animation);
- m_Client->HandleAnimation(Animation);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseBlockDig(void)
-{
- HANDLE_PACKET_READ(ReadChar, char, Status);
- HANDLE_PACKET_READ(ReadBEInt, int, PosX);
- HANDLE_PACKET_READ(ReadByte, Byte, PosY);
- HANDLE_PACKET_READ(ReadBEInt, int, PosZ);
- HANDLE_PACKET_READ(ReadChar, char, BlockFace);
- m_Client->HandleLeftClick(PosX, PosY, PosZ, BlockFace, Status);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseBlockPlace(void)
-{
- HANDLE_PACKET_READ(ReadBEInt, int, PosX);
- HANDLE_PACKET_READ(ReadByte, Byte, PosY);
- HANDLE_PACKET_READ(ReadBEInt, int, PosZ);
- HANDLE_PACKET_READ(ReadChar, char, BlockFace);
-
- cItem HeldItem;
- int res = ParseItem(HeldItem);
- if (res < 0)
- {
- return res;
- }
-
- // 1.2.5 didn't have any cursor position, so use 8, 8, 8, so that halfslabs and stairs work correctly and the special value is recognizable.
- m_Client->HandleRightClick(PosX, PosY, PosZ, BlockFace, 8, 8, 8, HeldItem);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseChat(void)
-{
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Message);
- m_Client->HandleChat(Message);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseCreativeInventoryAction(void)
-{
- HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
- cItem HeldItem;
- int res = ParseItem(HeldItem);
- if (res < 0)
- {
- return res;
- }
- m_Client->HandleCreativeInventory(SlotNum, HeldItem);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseDisconnect(void)
-{
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Reason);
- m_Client->HandleDisconnect(Reason);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseEntityAction(void)
-{
- HANDLE_PACKET_READ(ReadBEInt, int, EntityID);
- HANDLE_PACKET_READ(ReadChar, char, ActionID);
- m_Client->HandleEntityAction(EntityID, ActionID);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseHandshake(void)
-{
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username);
-
- AStringVector UserData = StringSplit(Username, ";"); // "FakeTruth;localhost:25565"
- if (UserData.empty())
- {
- m_Client->Kick("Did not receive username");
- return PARSE_OK;
- }
- m_Username = UserData[0];
-
- LOGD("HANDSHAKE %s", Username.c_str());
-
- if (!m_Client->HandleHandshake( m_Username ))
- {
- return PARSE_OK; // Player is not allowed into the server
- }
-
- SendHandshake(cRoot::Get()->GetServer()->GetServerID());
- LOGD("User \"%s\" was sent a handshake response", m_Username.c_str());
-
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseKeepAlive(void)
-{
- HANDLE_PACKET_READ(ReadBEInt, int, KeepAliveID);
- m_Client->HandleKeepAlive(KeepAliveID);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseLogin(void)
-{
- HANDLE_PACKET_READ(ReadBEInt, int, ProtocolVersion);
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username);
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType);
- HANDLE_PACKET_READ(ReadBEInt, int, ServerMode);
- HANDLE_PACKET_READ(ReadBEInt, int, Dimension);
- HANDLE_PACKET_READ(ReadChar, char, Difficulty);
- HANDLE_PACKET_READ(ReadByte, Byte, WorldHeight);
- HANDLE_PACKET_READ(ReadByte, Byte, MaxPlayers);
-
- if (ProtocolVersion < 29)
- {
- m_Client->Kick("Your client is outdated!");
- return PARSE_OK;
- }
- else if (ProtocolVersion > 29)
- {
- m_Client->Kick("Your client version is higher than the server!");
- return PARSE_OK;
- }
-
- if (m_Username.compare(Username) != 0)
- {
- LOGWARNING("Login Username (\"%s\") does not match Handshake username (\"%s\") for client @ \"%s\", kicking",
- Username.c_str(),
- m_Username.c_str(),
- m_Client->GetIPString().c_str()
- );
- m_Client->Kick("Hacked client"); // Don't tell them why we don't want them
- return PARSE_OK;
- }
-
- m_Client->HandleLogin(ProtocolVersion, Username);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParsePing(void)
-{
- // Packet has no more data
- m_Client->HandlePing();
- return PARSE_OK;
-}
-
-
-
-
-
-
-int cProtocol125::ParsePlayerAbilities(void)
-{
- HANDLE_PACKET_READ(ReadBool, bool, Invulnerable);
- HANDLE_PACKET_READ(ReadBool, bool, IsFlying);
- HANDLE_PACKET_READ(ReadBool, bool, CanFly);
- HANDLE_PACKET_READ(ReadBool, bool, InstaMine);
- // TODO: m_Client->HandlePlayerAbilities(...);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParsePlayerLook(void)
-{
- HANDLE_PACKET_READ(ReadBEFloat, float, Rotation);
- HANDLE_PACKET_READ(ReadBEFloat, float, Pitch);
- HANDLE_PACKET_READ(ReadBool, bool, IsOnGround);
- m_Client->HandlePlayerLook(Rotation, Pitch, IsOnGround);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParsePlayerMoveLook(void)
-{
- HANDLE_PACKET_READ(ReadBEDouble, double, PosX);
- HANDLE_PACKET_READ(ReadBEDouble, double, PosY);
- HANDLE_PACKET_READ(ReadBEDouble, double, Stance);
- HANDLE_PACKET_READ(ReadBEDouble, double, PosZ);
- HANDLE_PACKET_READ(ReadBEFloat, float, Rotation);
- HANDLE_PACKET_READ(ReadBEFloat, float, Pitch);
- HANDLE_PACKET_READ(ReadBool, bool, IsOnGround);
- // LOGD("Recv PML: {%0.2f, %0.2f, %0.2f}, Stance %0.2f, Gnd: %d", PosX, PosY, PosZ, Stance, IsOnGround ? 1 : 0);
- m_Client->HandlePlayerMoveLook(PosX, PosY, PosZ, Stance, Rotation, Pitch, IsOnGround);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParsePlayerOnGround(void)
-{
- HANDLE_PACKET_READ(ReadBool, bool, IsOnGround);
- // TODO: m_Client->HandleFlying(IsOnGround);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParsePlayerPosition(void)
-{
- HANDLE_PACKET_READ(ReadBEDouble, double, PosX);
- HANDLE_PACKET_READ(ReadBEDouble, double, PosY);
- HANDLE_PACKET_READ(ReadBEDouble, double, Stance);
- HANDLE_PACKET_READ(ReadBEDouble, double, PosZ);
- HANDLE_PACKET_READ(ReadBool, bool, IsOnGround);
- m_Client->HandlePlayerPos(PosX, PosY, PosZ, Stance, IsOnGround);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParsePluginMessage(void)
-{
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ChannelName);
- HANDLE_PACKET_READ(ReadBEShort, short, Length);
- AString Data;
- if (!m_ReceivedData.ReadString(Data, Length))
- {
- m_ReceivedData.CheckValid();
- return PARSE_INCOMPLETE;
- }
- m_ReceivedData.CheckValid();
-
- // TODO: Process the data
- LOGD("Received %d bytes of plugin data on channel \"%s\".", Length, ChannelName.c_str());
-
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseRespawn(void)
-{
- HANDLE_PACKET_READ(ReadBEInt, int, Dimension);
- HANDLE_PACKET_READ(ReadChar, char, Difficulty);
- HANDLE_PACKET_READ(ReadChar, char, CreativeMode);
- HANDLE_PACKET_READ(ReadBEShort, short, WorldHeight);
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType);
- m_Client->HandleRespawn();
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseSlotSelected(void)
-{
- HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
- m_Client->HandleSlotSelected(SlotNum);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseUpdateSign(void)
-{
- HANDLE_PACKET_READ(ReadBEInt, int, BlockX);
- HANDLE_PACKET_READ(ReadBEShort, short, BlockY);
- HANDLE_PACKET_READ(ReadBEInt, int, BlockZ);
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line1);
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line2);
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line3);
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line4);
- m_Client->HandleUpdateSign(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseUseEntity(void)
-{
- HANDLE_PACKET_READ(ReadBEInt, int, SourceEntityID);
- HANDLE_PACKET_READ(ReadBEInt, int, TargetEntityID);
- HANDLE_PACKET_READ(ReadBool, bool, IsLeftClick);
- m_Client->HandleUseEntity(TargetEntityID, IsLeftClick);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseWindowClick(void)
-{
- HANDLE_PACKET_READ(ReadChar, char, WindowID);
- HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
- HANDLE_PACKET_READ(ReadBool, bool, IsRightClick);
- HANDLE_PACKET_READ(ReadBEShort, short, TransactionID);
- HANDLE_PACKET_READ(ReadBool, bool, IsShiftPressed);
- cItem HeldItem;
- int res = ParseItem(HeldItem);
- if (res < 0)
- {
- return res;
- }
-
- // Convert IsShiftPressed, IsRightClick, SlotNum and HeldItem into eClickAction used in the newer protocols:
- eClickAction Action;
- if (IsRightClick)
- {
- if (IsShiftPressed)
- {
- Action = caShiftRightClick;
- }
- else
- {
- if (SlotNum == -999)
- {
- Action = (HeldItem.IsEmpty()) ? caRightClickOutsideHoldNothing : caRightClickOutside;
- }
- else
- {
- Action = caRightClick;
- }
- }
- }
- else
- {
- // IsLeftClick
- if (IsShiftPressed)
- {
- Action = caShiftLeftClick;
- }
- else
- {
- if (SlotNum == -999)
- {
- Action = (HeldItem.IsEmpty()) ? caLeftClickOutsideHoldNothing : caRightClickOutside;
- }
- else
- {
- Action = caLeftClick;
- }
- }
- }
- m_Client->HandleWindowClick(WindowID, SlotNum, Action, HeldItem);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseWindowClose(void)
-{
- HANDLE_PACKET_READ(ReadChar, char, WindowID);
- m_Client->HandleWindowClose(WindowID);
- return PARSE_OK;
-}
-
-
-
-
-
-void cProtocol125::SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad)
-{
- WriteByte(PACKET_PRE_CHUNK);
- WriteInt (a_ChunkX);
- WriteInt (a_ChunkZ);
- WriteBool(a_ShouldLoad);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items)
-{
- WriteByte (PACKET_INVENTORY_WHOLE);
- WriteByte (a_WindowID);
- WriteShort((short)a_NumItems);
-
- for (int j = 0; j < a_NumItems; j++)
- {
- WriteItem(a_Items[j]);
- }
- Flush();
-}
-
-
-
-
-
-void cProtocol125::WriteItem(const cItem & a_Item)
-{
- short ItemType = a_Item.m_ItemType;
- ASSERT(ItemType >= -1); // Check validity of packets in debug runtime
- if (ItemType <= 0)
- {
- // Fix, to make sure no invalid values are sent.
- ItemType = -1;
- }
-
- WriteShort(ItemType);
- if (a_Item.IsEmpty())
- {
- return;
- }
-
- WriteByte (a_Item.m_ItemCount);
- WriteShort(a_Item.m_ItemDamage);
-
- if (cItem::IsEnchantable(a_Item.m_ItemType))
- {
- // TODO: Implement enchantments
- WriteShort(-1);
- }
-}
-
-
-
-
-
-int cProtocol125::ParseItem(cItem & a_Item)
-{
- HANDLE_PACKET_READ(ReadBEShort, short, ItemType);
-
- if (ItemType <= -1)
- {
- a_Item.Empty();
- return PARSE_OK;
- }
- a_Item.m_ItemType = ItemType;
-
- HANDLE_PACKET_READ(ReadChar, char, ItemCount);
- HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage);
- a_Item.m_ItemCount = ItemCount;
- a_Item.m_ItemDamage = ItemDamage;
- if (ItemCount <= 0)
- {
- a_Item.Empty();
- }
-
- if (!cItem::IsEnchantable(ItemType))
- {
- return PARSE_OK;
- }
-
- HANDLE_PACKET_READ(ReadBEShort, short, EnchantNumBytes);
-
- if (EnchantNumBytes <= 0)
- {
- return PARSE_OK;
- }
-
- // TODO: Enchantment not implemented yet!
- if (!m_ReceivedData.SkipRead(EnchantNumBytes))
- {
- return PARSE_INCOMPLETE;
- }
-
- return PARSE_OK;
-}
-
-
-
-
-
-AString cProtocol125::GetEntityMetaData(const cEntity & a_Entity)
-{
- // We should send all the metadata here
- AString MetaData;
- // Common metadata (index 0, byte):
- MetaData.push_back(0);
- MetaData.push_back(GetEntityMetadataFlags(a_Entity));
-
- // TODO: Add more entity-specific metadata
-
- MetaData.push_back(0x7f); // End metadata
- return MetaData;
-}
-
-
-
-
-
-char cProtocol125::GetEntityMetadataFlags(const cEntity & a_Entity)
-{
- char Flags = 0;
- if (a_Entity.IsOnFire())
- {
- Flags |= 1;
- }
- if (a_Entity.IsCrouched())
- {
- Flags |= 2;
- }
- if (a_Entity.IsRiding())
- {
- Flags |= 4;
- }
- if (a_Entity.IsSprinting())
- {
- Flags |= 8;
- }
- if (a_Entity.IsRclking())
- {
- Flags |= 16;
- }
- return Flags;
-}
-
-
-
-
+
+// Protocol125.cpp
+
+// Implements the cProtocol125 class representing the release 1.2.5 protocol (#29)
+/*
+Documentation:
+ - protocol: http://wiki.vg/wiki/index.php?title=Protocol&oldid=2513
+ - session handling: http://wiki.vg/wiki/index.php?title=Session&oldid=2262
+ - slot format: http://wiki.vg/wiki/index.php?title=Slot_Data&oldid=2152
+*/
+
+#include "Globals.h"
+
+#include "Protocol125.h"
+
+#include "../ClientHandle.h"
+#include "ChunkDataSerializer.h"
+#include "../Entity.h"
+#include "../Mobs/Monster.h"
+#include "../Pickup.h"
+#include "../Player.h"
+#include "../ChatColor.h"
+#include "../UI/Window.h"
+#include "../Root.h"
+#include "../Server.h"
+#include "../FallingBlock.h"
+
+
+
+
+
+enum
+{
+ PACKET_KEEP_ALIVE = 0x00,
+ PACKET_LOGIN = 0x01,
+ PACKET_HANDSHAKE = 0x02,
+ PACKET_CHAT = 0x03,
+ PACKET_UPDATE_TIME = 0x04,
+ PACKET_ENTITY_EQUIPMENT = 0x05,
+ PACKET_USE_ENTITY = 0x07,
+ PACKET_UPDATE_HEALTH = 0x08,
+ PACKET_RESPAWN = 0x09,
+ PACKET_PLAYER_ON_GROUND = 0x0a,
+ PACKET_PLAYER_POS = 0x0b,
+ PACKET_PLAYER_LOOK = 0x0c,
+ PACKET_PLAYER_MOVE_LOOK = 0x0d,
+ PACKET_BLOCK_DIG = 0x0e,
+ PACKET_BLOCK_PLACE = 0x0f,
+ PACKET_SLOT_SELECTED = 0x10,
+ PACKET_USE_BED = 0x11,
+ PACKET_ANIMATION = 0x12,
+ PACKET_PACKET_ENTITY_ACTION = 0x13,
+ PACKET_PLAYER_SPAWN = 0x14,
+ PACKET_PICKUP_SPAWN = 0x15,
+ PACKET_COLLECT_PICKUP = 0x16,
+ PACKET_SPAWN_OBJECT = 0x17,
+ PACKET_SPAWN_MOB = 0x18,
+ PACKET_ENTITY_VELOCITY = 0x1c,
+ PACKET_DESTROY_ENTITY = 0x1d,
+ PACKET_ENTITY = 0x1e,
+ PACKET_ENT_REL_MOVE = 0x1f,
+ PACKET_ENT_LOOK = 0x20,
+ PACKET_ENT_REL_MOVE_LOOK = 0x21,
+ PACKET_ENT_TELEPORT = 0x22,
+ PACKET_ENT_HEAD_LOOK = 0x23,
+ PACKET_ENT_STATUS = 0x26,
+ PACKET_ATTACH_ENTITY = 0x27,
+ PACKET_METADATA = 0x28,
+ PACKET_PRE_CHUNK = 0x32,
+ PACKET_MAP_CHUNK = 0x33,
+ PACKET_MULTI_BLOCK = 0x34,
+ PACKET_BLOCK_CHANGE = 0x35,
+ PACKET_BLOCK_ACTION = 0x36,
+ PACKET_EXPLOSION = 0x3C,
+ PACKET_SOUND_EFFECT = 0x3e,
+ PACKET_SOUND_PARTICLE_EFFECT = 0x3d,
+ PACKET_CHANGE_GAME_STATE = 0x46,
+ PACKET_THUNDERBOLT = 0x47,
+ PACKET_WINDOW_OPEN = 0x64,
+ PACKET_WINDOW_CLOSE = 0x65,
+ PACKET_WINDOW_CLICK = 0x66,
+ PACKET_INVENTORY_SLOT = 0x67,
+ PACKET_INVENTORY_WHOLE = 0x68,
+ PACKET_INVENTORY_PROGRESS = 0x69,
+ PACKET_CREATIVE_INVENTORY_ACTION = 0x6B,
+ PACKET_UPDATE_SIGN = 0x82,
+ PACKET_PLAYER_LIST_ITEM = 0xC9,
+ PACKET_PLAYER_ABILITIES = 0xca,
+ PACKET_PLUGIN_MESSAGE = 0xfa,
+ PACKET_PING = 0xfe,
+ PACKET_DISCONNECT = 0xff
+} ;
+
+
+
+
+
+#define HANDLE_PACKET_READ(Proc, Type, Var) \
+ Type Var; \
+ { \
+ if (!m_ReceivedData.Proc(Var)) \
+ { \
+ m_ReceivedData.CheckValid(); \
+ return PARSE_INCOMPLETE; \
+ } \
+ m_ReceivedData.CheckValid(); \
+ }
+
+
+
+
+typedef unsigned char Byte;
+
+
+
+
+
+cProtocol125::cProtocol125(cClientHandle * a_Client) :
+ super(a_Client),
+ m_ReceivedData(32 KiB)
+{
+}
+
+
+
+
+
+void cProtocol125::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ATTACH_ENTITY);
+ WriteInt(a_Entity.GetUniqueID());
+ WriteInt((a_Vehicle == NULL) ? -1 : a_Vehicle->GetUniqueID());
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
+{
+ UNUSED(a_BlockType);
+
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_BLOCK_ACTION);
+ WriteInt (a_BlockX);
+ WriteShort((short)a_BlockY);
+ WriteInt (a_BlockZ);
+ WriteByte (a_Byte1);
+ WriteByte (a_Byte2);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage)
+{
+ // Not supported in this protocol version
+}
+
+
+
+
+
+void cProtocol125::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_BLOCK_CHANGE);
+ WriteInt (a_BlockX);
+ WriteByte((unsigned char)a_BlockY);
+ WriteInt (a_BlockZ);
+ WriteByte(a_BlockType);
+ WriteByte(a_BlockMeta);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes)
+{
+ cCSLock Lock(m_CSPacket);
+ if (a_Changes.size() == 1)
+ {
+ // Special packet for single-block changes
+ const sSetBlock & blk = a_Changes.front();
+ SendBlockChange(a_ChunkX * cChunkDef::Width + blk.x, blk.y, a_ChunkZ * cChunkDef::Width + blk.z, blk.BlockType, blk.BlockMeta);
+ return;
+ }
+
+ WriteByte (PACKET_MULTI_BLOCK);
+ WriteInt (a_ChunkX);
+ WriteInt (a_ChunkZ);
+ WriteShort((unsigned short)a_Changes.size());
+ WriteUInt (sizeof(int) * a_Changes.size());
+ for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr)
+ {
+ unsigned int Coords = itr->y | (itr->z << 8) | (itr->x << 12);
+ unsigned int Blocks = itr->BlockMeta | (itr->BlockType << 4);
+ WriteUInt(Coords << 16 | Blocks);
+ }
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendChat(const AString & a_Message)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_CHAT);
+ WriteString(a_Message);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
+{
+ cCSLock Lock(m_CSPacket);
+
+ // Send the pre-chunk:
+ SendPreChunk(a_ChunkX, a_ChunkZ, true);
+
+ // Send the chunk data:
+ AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_2_5);
+ WriteByte(PACKET_MAP_CHUNK);
+ WriteInt (a_ChunkX);
+ WriteInt (a_ChunkZ);
+ SendData(Serialized.data(), Serialized.size());
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_COLLECT_PICKUP);
+ WriteInt (a_Pickup.GetUniqueID());
+ WriteInt (a_Player.GetUniqueID());
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendDestroyEntity(const cEntity & a_Entity)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_DESTROY_ENTITY);
+ WriteInt (a_Entity.GetUniqueID());
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendDisconnect(const AString & a_Reason)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte ((unsigned char)PACKET_DISCONNECT);
+ WriteString(a_Reason);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ // This protocol version doesn't support this packet, sign editor is invoked by the client automatically
+ UNUSED(a_BlockX);
+ UNUSED(a_BlockY);
+ UNUSED(a_BlockZ);
+}
+
+
+
+
+
+void cProtocol125::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_ENTITY_EQUIPMENT);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteShort(a_SlotNum);
+ WriteShort(a_Item.m_ItemType);
+ WriteShort(a_Item.m_ItemDamage);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendEntityHeadLook(const cEntity & a_Entity)
+{
+ ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
+
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ENT_HEAD_LOOK);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteByte((char)((a_Entity.GetHeadYaw() / 360.f) * 256));
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendEntityLook(const cEntity & a_Entity)
+{
+ ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
+
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ENT_LOOK);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256));
+ WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256));
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendEntityMetadata(const cEntity & a_Entity)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_METADATA);
+ WriteInt (a_Entity.GetUniqueID());
+ AString MetaData = GetEntityMetaData(a_Entity);
+ SendData(MetaData.data(), MetaData.size());
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendEntityProperties(const cEntity & a_Entity)
+{
+ // Not supported in this protocol version
+}
+
+
+
+
+
+void cProtocol125::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
+{
+ ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
+
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ENT_REL_MOVE);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteByte(a_RelX);
+ WriteByte(a_RelY);
+ WriteByte(a_RelZ);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
+{
+ ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
+
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ENT_REL_MOVE_LOOK);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteByte(a_RelX);
+ WriteByte(a_RelY);
+ WriteByte(a_RelZ);
+ WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256));
+ WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256));
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendEntityStatus(const cEntity & a_Entity, char a_Status)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ENT_STATUS);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteByte(a_Status);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendEntityVelocity(const cEntity & a_Entity)
+{
+ ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
+
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ENTITY_VELOCITY);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteShort((short) (a_Entity.GetSpeedX() * 400)); //400 = 8000 / 20
+ WriteShort((short) (a_Entity.GetSpeedY() * 400));
+ WriteShort((short) (a_Entity.GetSpeedZ() * 400));
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_EXPLOSION);
+ WriteDouble (a_BlockX);
+ WriteDouble (a_BlockY);
+ WriteDouble (a_BlockZ);
+ WriteFloat (a_Radius);
+ WriteInt (a_BlocksAffected.size());
+ for (cVector3iArray::const_iterator itr = a_BlocksAffected.begin(); itr != a_BlocksAffected.end(); ++itr)
+ {
+ WriteByte ((Byte)(itr->x - a_BlockX));
+ WriteByte ((Byte)(itr->y - a_BlockY));
+ WriteByte ((Byte)(itr->z - a_BlockZ));
+ }
+ WriteFloat ((float)a_PlayerMotion.x);
+ WriteFloat ((float)a_PlayerMotion.y);
+ WriteFloat ((float)a_PlayerMotion.z);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendGameMode(eGameMode a_GameMode)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_CHANGE_GAME_STATE);
+ WriteByte(3);
+ WriteByte((char)a_GameMode);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendHandshake(const AString & a_ConnectionHash)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_HANDSHAKE);
+ WriteString(a_ConnectionHash);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendHealth(void)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_UPDATE_HEALTH);
+ WriteShort((short)m_Client->GetPlayer()->GetHealth());
+ WriteShort(m_Client->GetPlayer()->GetFoodLevel());
+ WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel());
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendInventoryProgress(char a_WindowID, short a_ProgressBar, short a_Value)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_INVENTORY_PROGRESS);
+ WriteByte (a_WindowID);
+ WriteShort(a_ProgressBar);
+ WriteShort(a_Value);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_INVENTORY_SLOT);
+ WriteByte (a_WindowID);
+ WriteShort(a_SlotNum);
+ WriteItem (a_Item);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendKeepAlive(int a_PingID)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_KEEP_ALIVE);
+ WriteInt (a_PingID);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
+{
+ UNUSED(a_World);
+ cCSLock Lock(m_CSPacket);
+
+ WriteByte (PACKET_LOGIN);
+ WriteInt (a_Player.GetUniqueID()); // EntityID of the player
+ WriteString(""); // Username, not used
+ WriteString("default"); // Level type
+ WriteInt ((int)a_Player.GetGameMode());
+ WriteInt ((int)(a_World.GetDimension()));
+ WriteByte (2); // TODO: Difficulty
+ WriteByte (0); // Unused
+ WriteByte (60); // Client list width or something
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendPickupSpawn(const cPickup & a_Pickup)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_PICKUP_SPAWN);
+ WriteInt (a_Pickup.GetUniqueID());
+ WriteShort (a_Pickup.GetItem().m_ItemType);
+ WriteByte (a_Pickup.GetItem().m_ItemCount);
+ WriteShort (a_Pickup.GetItem().m_ItemDamage);
+ WriteVectorI((Vector3i)(a_Pickup.GetPosition() * 32));
+ WriteByte ((char)(a_Pickup.GetSpeed().x * 8));
+ WriteByte ((char)(a_Pickup.GetSpeed().y * 8));
+ WriteByte ((char)(a_Pickup.GetSpeed().z * 8));
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ANIMATION);
+ WriteInt (a_Player.GetUniqueID());
+ WriteByte(a_Animation);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline)
+{
+ cCSLock Lock(m_CSPacket);
+ AString PlayerName(a_Player.GetColor());
+ PlayerName.append(a_Player.GetName());
+ if (PlayerName.length() > 14)
+ {
+ PlayerName.erase(14);
+ }
+ PlayerName += cChatColor::White;
+
+ WriteByte ((unsigned char)PACKET_PLAYER_LIST_ITEM);
+ WriteString(PlayerName);
+ WriteBool (a_IsOnline);
+ WriteShort (a_Player.GetClientHandle()->GetPing());
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendPlayerMaxSpeed(void)
+{
+ // Not supported by this protocol version
+}
+
+
+
+
+
+void cProtocol125::SendPlayerMoveLook(void)
+{
+ cCSLock Lock(m_CSPacket);
+
+ /*
+ LOGD("Sending PlayerMoveLook: {%0.2f, %0.2f, %0.2f}, stance %0.2f, OnGround: %d",
+ m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ(), m_Player->GetStance(), m_Player->IsOnGround() ? 1 : 0
+ );
+ */
+
+ WriteByte (PACKET_PLAYER_MOVE_LOOK);
+ cPlayer * Player = m_Client->GetPlayer();
+ WriteDouble(Player->GetPosX());
+ WriteDouble(Player->GetStance() + 0.03); // Add a small amount so that the player doesn't start inside a block
+ WriteDouble(Player->GetPosY() + 0.03); // Add a small amount so that the player doesn't start inside a block
+ WriteDouble(Player->GetPosZ());
+ WriteFloat ((float)(Player->GetRotation()));
+ WriteFloat ((float)(Player->GetPitch()));
+ WriteBool (Player->IsOnGround());
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendPlayerPosition(void)
+{
+ cCSLock Lock(m_CSPacket);
+ LOGD("Ignore send PlayerPos"); // PlayerPos is a C->S packet only now
+}
+
+
+
+
+
+void cProtocol125::SendPlayerSpawn(const cPlayer & a_Player)
+{
+ const cItem & HeldItem = a_Player.GetEquippedItem();
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_PLAYER_SPAWN);
+ WriteInt (a_Player.GetUniqueID());
+ WriteString(a_Player.GetName());
+ WriteInt ((int)(a_Player.GetPosX() * 32));
+ WriteInt ((int)(a_Player.GetPosY() * 32));
+ WriteInt ((int)(a_Player.GetPosZ() * 32));
+ WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256));
+ WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256));
+ WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendRespawn(void)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_RESPAWN);
+ WriteInt ((int)(m_Client->GetPlayer()->GetWorld()->GetDimension()));
+ WriteByte (2); // TODO: Difficulty; 2 = Normal
+ WriteByte ((char)m_Client->GetPlayer()->GetGameMode());
+ WriteShort (256); // Current world height
+ WriteString("default");
+}
+
+
+
+
+
+void cProtocol125::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch)
+{
+ // Not needed in this protocol version
+}
+
+
+
+
+
+void cProtocol125::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
+{
+ // Not implemented in this protocol version
+}
+
+
+
+
+
+void cProtocol125::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock)
+{
+ // This protocol version implements falling blocks using the spawn object / vehicle packet:
+ SendSpawnObject(a_FallingBlock, 70, a_FallingBlock.GetBlockType(), 0, 0);
+}
+
+
+
+
+
+void cProtocol125::SendSpawnMob(const cMonster & a_Mob)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_SPAWN_MOB);
+ WriteInt (a_Mob.GetUniqueID());
+ WriteByte (a_Mob.GetMobType());
+ WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32));
+ WriteByte (0);
+ WriteByte (0);
+ WriteByte (0);
+ AString MetaData = GetEntityMetaData(a_Mob);
+ SendData (MetaData.data(), MetaData.size());
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch)
+{
+ UNUSED(a_Yaw);
+ UNUSED(a_Pitch);
+
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_SPAWN_OBJECT);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteByte(a_ObjectType);
+ WriteInt ((int)(a_Entity.GetPosX() * 32));
+ WriteInt ((int)(a_Entity.GetPosY() * 32));
+ WriteInt ((int)(a_Entity.GetPosZ() * 32));
+ WriteByte(a_Pitch);
+ WriteByte(a_Yaw);
+ WriteInt (a_ObjectData);
+ if (a_ObjectData != 0)
+ {
+ WriteShort((short)(a_Entity.GetSpeedX() * 400));
+ WriteShort((short)(a_Entity.GetSpeedY() * 400));
+ WriteShort((short)(a_Entity.GetSpeedZ() * 400));
+ }
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_SPAWN_OBJECT);
+ WriteInt (a_Vehicle.GetUniqueID());
+ WriteByte (a_VehicleType);
+ WriteInt ((int)(a_Vehicle.GetPosX() * 32));
+ WriteInt ((int)(a_Vehicle.GetPosY() * 32));
+ WriteInt ((int)(a_Vehicle.GetPosZ() * 32));
+ WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256));
+ WriteByte ((Byte)((a_Vehicle.GetRotation() / 360.f) * 256));
+ WriteInt (1);
+ WriteShort((short)(a_Vehicle.GetSpeedX() * 400));
+ WriteShort((short)(a_Vehicle.GetSpeedY() * 400));
+ WriteShort((short)(a_Vehicle.GetSpeedZ() * 400));
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendTabCompletionResults(const AStringVector & a_Results)
+{
+ // This protocol version doesn't support tab completion
+ UNUSED(a_Results);
+}
+
+
+
+
+
+void cProtocol125::SendTeleportEntity(const cEntity & a_Entity)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_ENT_TELEPORT);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteInt ((int)(floor(a_Entity.GetPosX() * 32)));
+ WriteInt ((int)(floor(a_Entity.GetPosY() * 32)));
+ WriteInt ((int)(floor(a_Entity.GetPosZ() * 32)));
+ WriteByte ((char)((a_Entity.GetRotation() / 360.f) * 256));
+ WriteByte ((char)((a_Entity.GetPitch() / 360.f) * 256));
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_THUNDERBOLT);
+ WriteInt (0x7fffffff); // Entity ID of the thunderbolt; we use a constant one
+ WriteBool(true); // Unknown bool
+ WriteInt (a_BlockX * 32);
+ WriteInt (a_BlockY * 32);
+ WriteInt (a_BlockZ * 32);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_UPDATE_TIME);
+ // Use a_WorldAge for daycount, and a_TimeOfDay for the proper time of day:
+ WriteInt64((24000 * (a_WorldAge / 24000)) + (a_TimeOfDay % 24000));
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
+{
+ cCSLock Lock(m_CSPacket);
+ SendPreChunk(a_ChunkX, a_ChunkZ, false);
+}
+
+
+
+
+
+void cProtocol125::SendUpdateSign(
+ int a_BlockX, int a_BlockY, int a_BlockZ,
+ const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4
+)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte ((unsigned char)PACKET_UPDATE_SIGN);
+ WriteInt (a_BlockX);
+ WriteShort ((short)a_BlockY);
+ WriteInt (a_BlockZ);
+ WriteString(a_Line1);
+ WriteString(a_Line2);
+ WriteString(a_Line3);
+ WriteString(a_Line4);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ )
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_USE_BED);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteByte(0); // Unknown byte only 0 has been observed
+ WriteInt (a_BlockX);
+ WriteByte(a_BlockY);
+ WriteInt (a_BlockZ);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendWeather(eWeather a_Weather)
+{
+ cCSLock Lock(m_CSPacket);
+ switch( a_Weather )
+ {
+ case eWeather_Sunny:
+ {
+ WriteByte(PACKET_CHANGE_GAME_STATE);
+ WriteByte(2); // Stop rain
+ WriteByte(0); // Unused
+ Flush();
+ break;
+ }
+
+ case eWeather_Rain:
+ case eWeather_ThunderStorm:
+ {
+ WriteByte(PACKET_CHANGE_GAME_STATE);
+ WriteByte(1); // Begin rain
+ WriteByte(0); // Unused
+ Flush();
+ break;
+ }
+ }
+}
+
+
+
+
+
+void cProtocol125::SendWholeInventory(const cInventory & a_Inventory)
+{
+ SendWholeInventory(*(a_Inventory.GetOwner().GetWindow()));
+}
+
+
+
+
+
+void cProtocol125::SendWholeInventory(const cWindow & a_Window)
+{
+ cCSLock Lock(m_CSPacket);
+ cItems Slots;
+ a_Window.GetSlots(*(m_Client->GetPlayer()), Slots);
+ SendWindowSlots(a_Window.GetWindowID(), Slots.size(), &(Slots[0]));
+}
+
+
+
+
+
+void cProtocol125::SendWindowClose(const cWindow & a_Window)
+{
+ if (a_Window.GetWindowType() == cWindow::Inventory)
+ {
+ // Do not send inventory-window-close
+ return;
+ }
+
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_WINDOW_CLOSE);
+ WriteByte(a_Window.GetWindowID());
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots)
+{
+ if (a_WindowType < 0)
+ {
+ // Do not send for inventory windows
+ return;
+ }
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_WINDOW_OPEN);
+ WriteByte (a_WindowID);
+ WriteByte (a_WindowType);
+ WriteString(a_WindowTitle);
+ WriteByte (a_NumSlots);
+ Flush();
+}
+
+
+
+
+
+AString cProtocol125::GetAuthServerID(void)
+{
+ // http://wiki.vg/wiki/index.php?title=Session&oldid=2262
+ // The server generates a random hash and that is used for all clients, unmodified
+ return cRoot::Get()->GetServer()->GetServerID();
+}
+
+
+
+
+
+void cProtocol125::SendData(const char * a_Data, int a_Size)
+{
+ m_Client->SendData(a_Data, a_Size);
+}
+
+
+
+
+
+void cProtocol125::DataReceived(const char * a_Data, int a_Size)
+{
+ if (!m_ReceivedData.Write(a_Data, a_Size))
+ {
+ // Too much data in the incoming queue, report to caller:
+ m_Client->PacketBufferFull();
+ return;
+ }
+
+ // Parse and handle all complete packets in m_ReceivedData:
+ while (m_ReceivedData.CanReadBytes(1))
+ {
+ unsigned char PacketType;
+ m_ReceivedData.ReadByte(PacketType);
+ switch (ParsePacket(PacketType))
+ {
+ case PARSE_UNKNOWN:
+ {
+ // An unknown packet has been received, notify the client and abort:
+ m_Client->PacketUnknown(PacketType);
+ return;
+ }
+ case PARSE_ERROR:
+ {
+ // An error occurred while parsing a known packet, notify the client and abort:
+ m_Client->PacketError(PacketType);
+ return;
+ }
+ case PARSE_INCOMPLETE:
+ {
+ // Incomplete packet, bail out and process with the next batch of data
+ m_ReceivedData.ResetRead();
+ return;
+ }
+ default:
+ {
+ // Packet successfully parsed, commit the read data and try again one more packet
+ m_ReceivedData.CommitRead();
+ break;
+ }
+ }
+ }
+}
+
+
+
+
+
+int cProtocol125::ParsePacket(unsigned char a_PacketType)
+{
+ switch (a_PacketType)
+ {
+ default: return PARSE_UNKNOWN;
+ case PACKET_ANIMATION: return ParseArmAnim();
+ case PACKET_BLOCK_DIG: return ParseBlockDig();
+ case PACKET_BLOCK_PLACE: return ParseBlockPlace();
+ case PACKET_CHAT: return ParseChat();
+ case PACKET_CREATIVE_INVENTORY_ACTION: return ParseCreativeInventoryAction();
+ case PACKET_DISCONNECT: return ParseDisconnect();
+ case PACKET_HANDSHAKE: return ParseHandshake();
+ case PACKET_KEEP_ALIVE: return ParseKeepAlive();
+ case PACKET_LOGIN: return ParseLogin();
+ case PACKET_PACKET_ENTITY_ACTION: return ParseEntityAction();
+ case PACKET_PING: return ParsePing();
+ case PACKET_PLAYER_ABILITIES: return ParsePlayerAbilities();
+ case PACKET_PLAYER_LOOK: return ParsePlayerLook();
+ case PACKET_PLAYER_MOVE_LOOK: return ParsePlayerMoveLook();
+ case PACKET_PLAYER_ON_GROUND: return ParsePlayerOnGround();
+ case PACKET_PLAYER_POS: return ParsePlayerPosition();
+ case PACKET_PLUGIN_MESSAGE: return ParsePluginMessage();
+ case PACKET_RESPAWN: return ParseRespawn();
+ case PACKET_SLOT_SELECTED: return ParseSlotSelected();
+ case PACKET_UPDATE_SIGN: return ParseUpdateSign();
+ case PACKET_USE_ENTITY: return ParseUseEntity();
+ case PACKET_WINDOW_CLICK: return ParseWindowClick();
+ case PACKET_WINDOW_CLOSE: return ParseWindowClose();
+ }
+}
+
+
+
+
+
+#define HANDLE_PACKET_PARSE(Packet) \
+ { \
+ int res = Packet.Parse(m_ReceivedData); \
+ if (res < 0) \
+ { \
+ return res; \
+ } \
+ }
+
+
+
+
+
+int cProtocol125::ParseArmAnim(void)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, EntityID);
+ HANDLE_PACKET_READ(ReadChar, char, Animation);
+ m_Client->HandleAnimation(Animation);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseBlockDig(void)
+{
+ HANDLE_PACKET_READ(ReadChar, char, Status);
+ HANDLE_PACKET_READ(ReadBEInt, int, PosX);
+ HANDLE_PACKET_READ(ReadByte, Byte, PosY);
+ HANDLE_PACKET_READ(ReadBEInt, int, PosZ);
+ HANDLE_PACKET_READ(ReadChar, char, BlockFace);
+ m_Client->HandleLeftClick(PosX, PosY, PosZ, BlockFace, Status);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseBlockPlace(void)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, PosX);
+ HANDLE_PACKET_READ(ReadByte, Byte, PosY);
+ HANDLE_PACKET_READ(ReadBEInt, int, PosZ);
+ HANDLE_PACKET_READ(ReadChar, char, BlockFace);
+
+ cItem HeldItem;
+ int res = ParseItem(HeldItem);
+ if (res < 0)
+ {
+ return res;
+ }
+
+ // 1.2.5 didn't have any cursor position, so use 8, 8, 8, so that halfslabs and stairs work correctly and the special value is recognizable.
+ m_Client->HandleRightClick(PosX, PosY, PosZ, BlockFace, 8, 8, 8, HeldItem);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseChat(void)
+{
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Message);
+ m_Client->HandleChat(Message);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseCreativeInventoryAction(void)
+{
+ HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
+ cItem HeldItem;
+ int res = ParseItem(HeldItem);
+ if (res < 0)
+ {
+ return res;
+ }
+ m_Client->HandleCreativeInventory(SlotNum, HeldItem);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseDisconnect(void)
+{
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Reason);
+ m_Client->HandleDisconnect(Reason);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseEntityAction(void)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, EntityID);
+ HANDLE_PACKET_READ(ReadChar, char, ActionID);
+ m_Client->HandleEntityAction(EntityID, ActionID);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseHandshake(void)
+{
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username);
+
+ AStringVector UserData = StringSplit(Username, ";"); // "FakeTruth;localhost:25565"
+ if (UserData.empty())
+ {
+ m_Client->Kick("Did not receive username");
+ return PARSE_OK;
+ }
+ m_Username = UserData[0];
+
+ LOGD("HANDSHAKE %s", Username.c_str());
+
+ if (!m_Client->HandleHandshake( m_Username ))
+ {
+ return PARSE_OK; // Player is not allowed into the server
+ }
+
+ SendHandshake(cRoot::Get()->GetServer()->GetServerID());
+ LOGD("User \"%s\" was sent a handshake response", m_Username.c_str());
+
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseKeepAlive(void)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, KeepAliveID);
+ m_Client->HandleKeepAlive(KeepAliveID);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseLogin(void)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, ProtocolVersion);
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username);
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType);
+ HANDLE_PACKET_READ(ReadBEInt, int, ServerMode);
+ HANDLE_PACKET_READ(ReadBEInt, int, Dimension);
+ HANDLE_PACKET_READ(ReadChar, char, Difficulty);
+ HANDLE_PACKET_READ(ReadByte, Byte, WorldHeight);
+ HANDLE_PACKET_READ(ReadByte, Byte, MaxPlayers);
+
+ if (ProtocolVersion < 29)
+ {
+ m_Client->Kick("Your client is outdated!");
+ return PARSE_OK;
+ }
+ else if (ProtocolVersion > 29)
+ {
+ m_Client->Kick("Your client version is higher than the server!");
+ return PARSE_OK;
+ }
+
+ if (m_Username.compare(Username) != 0)
+ {
+ LOGWARNING("Login Username (\"%s\") does not match Handshake username (\"%s\") for client @ \"%s\", kicking",
+ Username.c_str(),
+ m_Username.c_str(),
+ m_Client->GetIPString().c_str()
+ );
+ m_Client->Kick("Hacked client"); // Don't tell them why we don't want them
+ return PARSE_OK;
+ }
+
+ m_Client->HandleLogin(ProtocolVersion, Username);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParsePing(void)
+{
+ // Packet has no more data
+ m_Client->HandlePing();
+ return PARSE_OK;
+}
+
+
+
+
+
+
+int cProtocol125::ParsePlayerAbilities(void)
+{
+ HANDLE_PACKET_READ(ReadBool, bool, Invulnerable);
+ HANDLE_PACKET_READ(ReadBool, bool, IsFlying);
+ HANDLE_PACKET_READ(ReadBool, bool, CanFly);
+ HANDLE_PACKET_READ(ReadBool, bool, InstaMine);
+ // TODO: m_Client->HandlePlayerAbilities(...);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParsePlayerLook(void)
+{
+ HANDLE_PACKET_READ(ReadBEFloat, float, Rotation);
+ HANDLE_PACKET_READ(ReadBEFloat, float, Pitch);
+ HANDLE_PACKET_READ(ReadBool, bool, IsOnGround);
+ m_Client->HandlePlayerLook(Rotation, Pitch, IsOnGround);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParsePlayerMoveLook(void)
+{
+ HANDLE_PACKET_READ(ReadBEDouble, double, PosX);
+ HANDLE_PACKET_READ(ReadBEDouble, double, PosY);
+ HANDLE_PACKET_READ(ReadBEDouble, double, Stance);
+ HANDLE_PACKET_READ(ReadBEDouble, double, PosZ);
+ HANDLE_PACKET_READ(ReadBEFloat, float, Rotation);
+ HANDLE_PACKET_READ(ReadBEFloat, float, Pitch);
+ HANDLE_PACKET_READ(ReadBool, bool, IsOnGround);
+ // LOGD("Recv PML: {%0.2f, %0.2f, %0.2f}, Stance %0.2f, Gnd: %d", PosX, PosY, PosZ, Stance, IsOnGround ? 1 : 0);
+ m_Client->HandlePlayerMoveLook(PosX, PosY, PosZ, Stance, Rotation, Pitch, IsOnGround);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParsePlayerOnGround(void)
+{
+ HANDLE_PACKET_READ(ReadBool, bool, IsOnGround);
+ // TODO: m_Client->HandleFlying(IsOnGround);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParsePlayerPosition(void)
+{
+ HANDLE_PACKET_READ(ReadBEDouble, double, PosX);
+ HANDLE_PACKET_READ(ReadBEDouble, double, PosY);
+ HANDLE_PACKET_READ(ReadBEDouble, double, Stance);
+ HANDLE_PACKET_READ(ReadBEDouble, double, PosZ);
+ HANDLE_PACKET_READ(ReadBool, bool, IsOnGround);
+ m_Client->HandlePlayerPos(PosX, PosY, PosZ, Stance, IsOnGround);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParsePluginMessage(void)
+{
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ChannelName);
+ HANDLE_PACKET_READ(ReadBEShort, short, Length);
+ AString Data;
+ if (!m_ReceivedData.ReadString(Data, Length))
+ {
+ m_ReceivedData.CheckValid();
+ return PARSE_INCOMPLETE;
+ }
+ m_ReceivedData.CheckValid();
+
+ // TODO: Process the data
+ LOGD("Received %d bytes of plugin data on channel \"%s\".", Length, ChannelName.c_str());
+
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseRespawn(void)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, Dimension);
+ HANDLE_PACKET_READ(ReadChar, char, Difficulty);
+ HANDLE_PACKET_READ(ReadChar, char, CreativeMode);
+ HANDLE_PACKET_READ(ReadBEShort, short, WorldHeight);
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType);
+ m_Client->HandleRespawn();
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseSlotSelected(void)
+{
+ HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
+ m_Client->HandleSlotSelected(SlotNum);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseUpdateSign(void)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, BlockX);
+ HANDLE_PACKET_READ(ReadBEShort, short, BlockY);
+ HANDLE_PACKET_READ(ReadBEInt, int, BlockZ);
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line1);
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line2);
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line3);
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line4);
+ m_Client->HandleUpdateSign(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseUseEntity(void)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, SourceEntityID);
+ HANDLE_PACKET_READ(ReadBEInt, int, TargetEntityID);
+ HANDLE_PACKET_READ(ReadBool, bool, IsLeftClick);
+ m_Client->HandleUseEntity(TargetEntityID, IsLeftClick);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseWindowClick(void)
+{
+ HANDLE_PACKET_READ(ReadChar, char, WindowID);
+ HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
+ HANDLE_PACKET_READ(ReadBool, bool, IsRightClick);
+ HANDLE_PACKET_READ(ReadBEShort, short, TransactionID);
+ HANDLE_PACKET_READ(ReadBool, bool, IsShiftPressed);
+ cItem HeldItem;
+ int res = ParseItem(HeldItem);
+ if (res < 0)
+ {
+ return res;
+ }
+
+ // Convert IsShiftPressed, IsRightClick, SlotNum and HeldItem into eClickAction used in the newer protocols:
+ eClickAction Action;
+ if (IsRightClick)
+ {
+ if (IsShiftPressed)
+ {
+ Action = caShiftRightClick;
+ }
+ else
+ {
+ if (SlotNum == -999)
+ {
+ Action = (HeldItem.IsEmpty()) ? caRightClickOutsideHoldNothing : caRightClickOutside;
+ }
+ else
+ {
+ Action = caRightClick;
+ }
+ }
+ }
+ else
+ {
+ // IsLeftClick
+ if (IsShiftPressed)
+ {
+ Action = caShiftLeftClick;
+ }
+ else
+ {
+ if (SlotNum == -999)
+ {
+ Action = (HeldItem.IsEmpty()) ? caLeftClickOutsideHoldNothing : caRightClickOutside;
+ }
+ else
+ {
+ Action = caLeftClick;
+ }
+ }
+ }
+ m_Client->HandleWindowClick(WindowID, SlotNum, Action, HeldItem);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol125::ParseWindowClose(void)
+{
+ HANDLE_PACKET_READ(ReadChar, char, WindowID);
+ m_Client->HandleWindowClose(WindowID);
+ return PARSE_OK;
+}
+
+
+
+
+
+void cProtocol125::SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad)
+{
+ WriteByte(PACKET_PRE_CHUNK);
+ WriteInt (a_ChunkX);
+ WriteInt (a_ChunkZ);
+ WriteBool(a_ShouldLoad);
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items)
+{
+ WriteByte (PACKET_INVENTORY_WHOLE);
+ WriteByte (a_WindowID);
+ WriteShort((short)a_NumItems);
+
+ for (int j = 0; j < a_NumItems; j++)
+ {
+ WriteItem(a_Items[j]);
+ }
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::WriteItem(const cItem & a_Item)
+{
+ short ItemType = a_Item.m_ItemType;
+ ASSERT(ItemType >= -1); // Check validity of packets in debug runtime
+ if (ItemType <= 0)
+ {
+ // Fix, to make sure no invalid values are sent.
+ ItemType = -1;
+ }
+
+ WriteShort(ItemType);
+ if (a_Item.IsEmpty())
+ {
+ return;
+ }
+
+ WriteByte (a_Item.m_ItemCount);
+ WriteShort(a_Item.m_ItemDamage);
+
+ if (cItem::IsEnchantable(a_Item.m_ItemType))
+ {
+ // TODO: Implement enchantments
+ WriteShort(-1);
+ }
+}
+
+
+
+
+
+int cProtocol125::ParseItem(cItem & a_Item)
+{
+ HANDLE_PACKET_READ(ReadBEShort, short, ItemType);
+
+ if (ItemType <= -1)
+ {
+ a_Item.Empty();
+ return PARSE_OK;
+ }
+ a_Item.m_ItemType = ItemType;
+
+ HANDLE_PACKET_READ(ReadChar, char, ItemCount);
+ HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage);
+ a_Item.m_ItemCount = ItemCount;
+ a_Item.m_ItemDamage = ItemDamage;
+ if (ItemCount <= 0)
+ {
+ a_Item.Empty();
+ }
+
+ if (!cItem::IsEnchantable(ItemType))
+ {
+ return PARSE_OK;
+ }
+
+ HANDLE_PACKET_READ(ReadBEShort, short, EnchantNumBytes);
+
+ if (EnchantNumBytes <= 0)
+ {
+ return PARSE_OK;
+ }
+
+ // TODO: Enchantment not implemented yet!
+ if (!m_ReceivedData.SkipRead(EnchantNumBytes))
+ {
+ return PARSE_INCOMPLETE;
+ }
+
+ return PARSE_OK;
+}
+
+
+
+
+
+AString cProtocol125::GetEntityMetaData(const cEntity & a_Entity)
+{
+ // We should send all the metadata here
+ AString MetaData;
+ // Common metadata (index 0, byte):
+ MetaData.push_back(0);
+ MetaData.push_back(GetEntityMetadataFlags(a_Entity));
+
+ // TODO: Add more entity-specific metadata
+
+ MetaData.push_back(0x7f); // End metadata
+ return MetaData;
+}
+
+
+
+
+
+char cProtocol125::GetEntityMetadataFlags(const cEntity & a_Entity)
+{
+ char Flags = 0;
+ if (a_Entity.IsOnFire())
+ {
+ Flags |= 1;
+ }
+ if (a_Entity.IsCrouched())
+ {
+ Flags |= 2;
+ }
+ if (a_Entity.IsRiding())
+ {
+ Flags |= 4;
+ }
+ if (a_Entity.IsSprinting())
+ {
+ Flags |= 8;
+ }
+ if (a_Entity.IsRclking())
+ {
+ Flags |= 16;
+ }
+ return Flags;
+}
+
+
+
+
diff --git a/source/Protocol/Protocol125.h b/source/Protocol/Protocol125.h
index 2f769f362..ee8fd94eb 100644
--- a/source/Protocol/Protocol125.h
+++ b/source/Protocol/Protocol125.h
@@ -1,152 +1,154 @@
-
-// Protocol125.h
-
-// Interfaces to the cProtocol125 class representing the release 1.2.5 protocol (#29)
-
-
-
-
-
-#pragma once
-
-#include "Protocol.h"
-#include "../ByteBuffer.h"
-
-
-
-
-
-class cProtocol125 :
- public cProtocol
-{
- typedef cProtocol super;
-public:
- cProtocol125(cClientHandle * a_Client);
-
- /// Called when client sends some data:
- virtual void DataReceived(const char * a_Data, int a_Size) override;
-
- /// Sending stuff to clients (alphabetically sorted):
- virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
- virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
- virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
- virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
- virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
- virtual void SendChat (const AString & a_Message) override;
- virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
- virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override;
- virtual void SendDestroyEntity (const cEntity & a_Entity) override;
- virtual void SendDisconnect (const AString & a_Reason) override;
- virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
- virtual void SendEntityHeadLook (const cEntity & a_Entity) override;
- virtual void SendEntityLook (const cEntity & a_Entity) override;
- virtual void SendEntityMetadata (const cEntity & a_Entity) override;
- virtual void SendEntityProperties (const cEntity & a_Entity) override;
- virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
- virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
- virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override;
- virtual void SendEntityVelocity (const cEntity & a_Entity) override;
- virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override;
- virtual void SendGameMode (eGameMode a_GameMode) override;
- virtual void SendHealth (void) override;
- virtual void SendInventoryProgress (char a_WindowID, short a_Progressbar, short a_Value) override;
- virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
- virtual void SendKeepAlive (int a_PingID) override;
- virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
- virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
- virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override;
- virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override;
- virtual void SendPlayerMaxSpeed (void) override;
- virtual void SendPlayerMoveLook (void) override;
- virtual void SendPlayerPosition (void) override;
- virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
- virtual void SendRespawn (void) override;
- virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8
- virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
- virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override;
- virtual void SendSpawnMob (const cMonster & a_Mob) override;
- virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
- virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType) override;
- virtual void SendTeleportEntity (const cEntity & a_Entity) override;
- virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
- virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
- virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
- virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override;
- virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override;
- virtual void SendWeather (eWeather a_Weather) override;
- virtual void SendWholeInventory (const cInventory & a_Inventory) override;
- virtual void SendWholeInventory (const cWindow & a_Window) override;
- virtual void SendWindowClose (const cWindow & a_Window) override;
- virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) override;
-
- virtual AString GetAuthServerID(void) override;
-
-protected:
- /// Results of packet-parsing:
- enum {
- PARSE_OK = 1,
- PARSE_ERROR = -1,
- PARSE_UNKNOWN = -2,
- PARSE_INCOMPLETE = -3,
- } ;
-
- cByteBuffer m_ReceivedData; //< Buffer for the received data
-
- AString m_Username; //< Stored in ParseHandshake(), compared to Login username
-
- virtual void SendData(const char * a_Data, int a_Size) override;
-
- /// Sends the Handshake packet
- void SendHandshake(const AString & a_ConnectionHash);
-
- /// Parse the packet of the specified type from m_ReceivedData (switch into ParseXYZ() )
- virtual int ParsePacket(unsigned char a_PacketType);
-
- // Specific packet parsers:
- virtual int ParseArmAnim (void);
- virtual int ParseBlockDig (void);
- virtual int ParseBlockPlace (void);
- virtual int ParseChat (void);
- virtual int ParseCreativeInventoryAction(void);
- virtual int ParseDisconnect (void);
- virtual int ParseEntityAction (void);
- virtual int ParseHandshake (void);
- virtual int ParseKeepAlive (void);
- virtual int ParseLogin (void);
- virtual int ParsePing (void);
- virtual int ParsePlayerAbilities (void);
- virtual int ParsePlayerLook (void);
- virtual int ParsePlayerMoveLook (void);
- virtual int ParsePlayerOnGround (void);
- virtual int ParsePlayerPosition (void);
- virtual int ParsePluginMessage (void);
- virtual int ParseRespawn (void);
- virtual int ParseSlotSelected (void);
- virtual int ParseUpdateSign (void);
- virtual int ParseUseEntity (void);
- virtual int ParseWindowClick (void);
- virtual int ParseWindowClose (void);
-
- // Utility functions:
- /// Writes a "pre-chunk" packet
- void SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad);
-
- /// Writes a "set window items" packet with the specified params
- void SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items);
-
- /// Writes one item, "slot" as the protocol wiki calls it
- virtual void WriteItem(const cItem & a_Item);
-
- /// Parses one item, "slot" as the protocol wiki calls it, from m_ReceivedData; returns the usual ParsePacket() codes
- virtual int ParseItem(cItem & a_Item);
-
- /// Returns the entity metadata representation
- AString GetEntityMetaData(const cEntity & a_Entity);
-
- /// Returns the entity common metadata, index 0 (generic flags)
- char GetEntityMetadataFlags(const cEntity & a_Entity);
-} ;
-
-
-
-
+
+// Protocol125.h
+
+// Interfaces to the cProtocol125 class representing the release 1.2.5 protocol (#29)
+
+
+
+
+
+#pragma once
+
+#include "Protocol.h"
+#include "../ByteBuffer.h"
+
+
+
+
+
+class cProtocol125 :
+ public cProtocol
+{
+ typedef cProtocol super;
+public:
+ cProtocol125(cClientHandle * a_Client);
+
+ /// Called when client sends some data:
+ virtual void DataReceived(const char * a_Data, int a_Size) override;
+
+ /// Sending stuff to clients (alphabetically sorted):
+ virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
+ virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
+ virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
+ virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
+ virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
+ virtual void SendChat (const AString & a_Message) override;
+ virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
+ virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override;
+ virtual void SendDestroyEntity (const cEntity & a_Entity) override;
+ virtual void SendDisconnect (const AString & a_Reason) override;
+ virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)
+ virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
+ virtual void SendEntityHeadLook (const cEntity & a_Entity) override;
+ virtual void SendEntityLook (const cEntity & a_Entity) override;
+ virtual void SendEntityMetadata (const cEntity & a_Entity) override;
+ virtual void SendEntityProperties (const cEntity & a_Entity) override;
+ virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
+ virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
+ virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override;
+ virtual void SendEntityVelocity (const cEntity & a_Entity) override;
+ virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override;
+ virtual void SendGameMode (eGameMode a_GameMode) override;
+ virtual void SendHealth (void) override;
+ virtual void SendInventoryProgress (char a_WindowID, short a_Progressbar, short a_Value) override;
+ virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
+ virtual void SendKeepAlive (int a_PingID) override;
+ virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
+ virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
+ virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override;
+ virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override;
+ virtual void SendPlayerMaxSpeed (void) override;
+ virtual void SendPlayerMoveLook (void) override;
+ virtual void SendPlayerPosition (void) override;
+ virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
+ virtual void SendRespawn (void) override;
+ virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8
+ virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
+ virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override;
+ virtual void SendSpawnMob (const cMonster & a_Mob) override;
+ virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
+ virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType) override;
+ virtual void SendTabCompletionResults(const AStringVector & a_Results) override;
+ virtual void SendTeleportEntity (const cEntity & a_Entity) override;
+ virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
+ virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
+ virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
+ virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override;
+ virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override;
+ virtual void SendWeather (eWeather a_Weather) override;
+ virtual void SendWholeInventory (const cInventory & a_Inventory) override;
+ virtual void SendWholeInventory (const cWindow & a_Window) override;
+ virtual void SendWindowClose (const cWindow & a_Window) override;
+ virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) override;
+
+ virtual AString GetAuthServerID(void) override;
+
+protected:
+ /// Results of packet-parsing:
+ enum {
+ PARSE_OK = 1,
+ PARSE_ERROR = -1,
+ PARSE_UNKNOWN = -2,
+ PARSE_INCOMPLETE = -3,
+ } ;
+
+ cByteBuffer m_ReceivedData; //< Buffer for the received data
+
+ AString m_Username; //< Stored in ParseHandshake(), compared to Login username
+
+ virtual void SendData(const char * a_Data, int a_Size) override;
+
+ /// Sends the Handshake packet
+ void SendHandshake(const AString & a_ConnectionHash);
+
+ /// Parse the packet of the specified type from m_ReceivedData (switch into ParseXYZ() )
+ virtual int ParsePacket(unsigned char a_PacketType);
+
+ // Specific packet parsers:
+ virtual int ParseArmAnim (void);
+ virtual int ParseBlockDig (void);
+ virtual int ParseBlockPlace (void);
+ virtual int ParseChat (void);
+ virtual int ParseCreativeInventoryAction(void);
+ virtual int ParseDisconnect (void);
+ virtual int ParseEntityAction (void);
+ virtual int ParseHandshake (void);
+ virtual int ParseKeepAlive (void);
+ virtual int ParseLogin (void);
+ virtual int ParsePing (void);
+ virtual int ParsePlayerAbilities (void);
+ virtual int ParsePlayerLook (void);
+ virtual int ParsePlayerMoveLook (void);
+ virtual int ParsePlayerOnGround (void);
+ virtual int ParsePlayerPosition (void);
+ virtual int ParsePluginMessage (void);
+ virtual int ParseRespawn (void);
+ virtual int ParseSlotSelected (void);
+ virtual int ParseUpdateSign (void);
+ virtual int ParseUseEntity (void);
+ virtual int ParseWindowClick (void);
+ virtual int ParseWindowClose (void);
+
+ // Utility functions:
+ /// Writes a "pre-chunk" packet
+ void SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad);
+
+ /// Writes a "set window items" packet with the specified params
+ void SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items);
+
+ /// Writes one item, "slot" as the protocol wiki calls it
+ virtual void WriteItem(const cItem & a_Item);
+
+ /// Parses one item, "slot" as the protocol wiki calls it, from m_ReceivedData; returns the usual ParsePacket() codes
+ virtual int ParseItem(cItem & a_Item);
+
+ /// Returns the entity metadata representation
+ AString GetEntityMetaData(const cEntity & a_Entity);
+
+ /// Returns the entity common metadata, index 0 (generic flags)
+ char GetEntityMetadataFlags(const cEntity & a_Entity);
+} ;
+
+
+
+
diff --git a/source/Protocol/Protocol132.cpp b/source/Protocol/Protocol132.cpp
index f34401b55..7a3975537 100644
--- a/source/Protocol/Protocol132.cpp
+++ b/source/Protocol/Protocol132.cpp
@@ -1,902 +1,940 @@
-
-// Protocol132.cpp
-
-// Implements the cProtocol132 class representing the release 1.3.2 protocol (#39)
-
-#include "Globals.h"
-#include "Protocol132.h"
-#include "../Root.h"
-#include "../Server.h"
-#include "../ClientHandle.h"
-#include "../../CryptoPP/randpool.h"
-#include "../Item.h"
-#include "ChunkDataSerializer.h"
-#include "../Player.h"
-#include "../Mobs/Monster.h"
-#include "../UI/Window.h"
-#include "../Pickup.h"
-#include "../WorldStorage/FastNBT.h"
-#include "../StringCompression.h"
-
-
-
-
-
-#define HANDLE_PACKET_READ(Proc, Type, Var) \
- Type Var; \
- { \
- if (!m_ReceivedData.Proc(Var)) \
- { \
- m_ReceivedData.CheckValid(); \
- return PARSE_INCOMPLETE; \
- } \
- m_ReceivedData.CheckValid(); \
- }
-
-
-
-
-typedef unsigned char Byte;
-
-
-
-
-
-using namespace CryptoPP;
-
-
-
-
-
-const int MAX_ENC_LEN = 512; // Maximum size of the encrypted message; should be 128, but who knows...
-
-
-
-
-
-enum
-{
- PACKET_KEEP_ALIVE = 0x00,
- PACKET_LOGIN = 0x01,
- PACKET_ENTITY_EQUIPMENT = 0x05,
- PACKET_COMPASS = 0x06,
- PACKET_PLAYER_SPAWN = 0x14,
- PACKET_COLLECT_PICKUP = 0x16,
- PACKET_SPAWN_MOB = 0x18,
- PACKET_DESTROY_ENTITIES = 0x1d,
- PACKET_CHUNK_DATA = 0x33,
- PACKET_BLOCK_CHANGE = 0x35,
- PACKET_BLOCK_ACTION = 0x36,
- PACKET_BLOCK_BREAK_ANIM = 0x37,
- PACKET_SOUND_EFFECT = 0x3e,
- PACKET_SOUND_PARTICLE_EFFECT = 0x3d,
- PACKET_LOCALE_VIEW_DISTANCE = 0xcc,
- PACKET_CLIENT_STATUSES = 0xcd,
- PACKET_ENCRYPTION_KEY_RESP = 0xfc,
-} ;
-
-
-
-
-
-// Converts a raw 160-bit SHA1 digest into a Java Hex representation
-// According to http://wiki.vg/wiki/index.php?title=Protocol_Encryption&oldid=2802
-static void DigestToJava(byte a_Digest[20], AString & a_Out)
-{
- bool IsNegative = (a_Digest[0] >= 0x80);
- if (IsNegative)
- {
- // Two's complement:
- bool carry = true; // Add one to the whole number
- for (int i = 19; i >= 0; i--)
- {
- a_Digest[i] = ~a_Digest[i];
- if (carry)
- {
- carry = (a_Digest[i] == 0xff);
- a_Digest[i]++;
- }
- }
- }
- a_Out.clear();
- a_Out.reserve(40);
- for (int i = 0; i < 20; i++)
- {
- AppendPrintf(a_Out, "%02x", a_Digest[i]);
- }
- while ((a_Out.length() > 0) && (a_Out[0] == '0'))
- {
- a_Out.erase(0, 1);
- }
- if (IsNegative)
- {
- a_Out.insert(0, "-");
- }
-}
-
-
-
-
-
-/*
-// Self-test the hash formatting for known values:
-// sha1(Notch) : 4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48
-// sha1(jeb_) : -7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1
-// sha1(simon) : 88e16a1019277b15d58faf0541e11910eb756f6
-
-class Test
-{
-public:
- Test(void)
- {
- AString DigestNotch, DigestJeb, DigestSimon;
- byte Digest[20];
- CryptoPP::SHA1 Checksum;
- Checksum.Update((const byte *)"Notch", 5);
- Checksum.Final(Digest);
- DigestToJava(Digest, DigestNotch);
- Checksum.Restart();
- Checksum.Update((const byte *)"jeb_", 4);
- Checksum.Final(Digest);
- DigestToJava(Digest, DigestJeb);
- Checksum.Restart();
- Checksum.Update((const byte *)"simon", 5);
- Checksum.Final(Digest);
- DigestToJava(Digest, DigestSimon);
- printf("Notch: \"%s\"", DigestNotch.c_str());
- printf("jeb_: \"%s\"", DigestJeb.c_str());
- printf("simon: \"%s\"", DigestSimon.c_str());
- }
-} test;
-*/
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cProtocol132:
-
-cProtocol132::cProtocol132(cClientHandle * a_Client) :
- super(a_Client),
- m_IsEncrypted(false)
-{
-}
-
-
-
-
-
-cProtocol132::~cProtocol132()
-{
- if (!m_DataToSend.empty())
- {
- LOGD("There are %d unsent bytes while deleting cProtocol132", m_DataToSend.size());
- }
-}
-
-
-
-
-
-void cProtocol132::DataReceived(const char * a_Data, int a_Size)
-{
- if (m_IsEncrypted)
- {
- byte Decrypted[512];
- while (a_Size > 0)
- {
- int NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size;
- m_Decryptor.ProcessData(Decrypted, (byte *)a_Data, NumBytes);
- super::DataReceived((const char *)Decrypted, NumBytes);
- a_Size -= NumBytes;
- a_Data += NumBytes;
- }
- }
- else
- {
- super::DataReceived(a_Data, a_Size);
- }
-}
-
-
-
-
-
-void cProtocol132::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_BLOCK_ACTION);
- WriteInt (a_BlockX);
- WriteShort((short)a_BlockY);
- WriteInt (a_BlockZ);
- WriteByte (a_Byte1);
- WriteByte (a_Byte2);
- WriteShort(a_BlockType);
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_BLOCK_BREAK_ANIM);
- WriteInt (a_entityID);
- WriteInt (a_BlockX);
- WriteInt (a_BlockY);
- WriteInt (a_BlockZ);
- WriteByte (stage);
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_BLOCK_CHANGE);
- WriteInt (a_BlockX);
- WriteByte ((unsigned char)a_BlockY);
- WriteInt (a_BlockZ);
- WriteShort(a_BlockType);
- WriteByte (a_BlockMeta);
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
-{
- cCSLock Lock(m_CSPacket);
-
- // Pre-chunk not used in 1.3.2. Finally.
-
- // Send the chunk data:
- AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_3_2);
- WriteByte(PACKET_CHUNK_DATA);
- WriteInt (a_ChunkX);
- WriteInt (a_ChunkZ);
- SendData(Serialized.data(), Serialized.size());
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_COLLECT_PICKUP);
- WriteInt (a_Pickup.GetUniqueID());
- WriteInt (a_Player.GetUniqueID());
- Flush();
-
- // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;)
- SendSoundEffect(
- "random.pop",
- (int)(a_Pickup.GetPosX() * 8), (int)(a_Pickup.GetPosY() * 8), (int)(a_Pickup.GetPosZ() * 8),
- 0.5, (float)(0.75 + ((float)((a_Pickup.GetUniqueID() * 23) % 32)) / 64)
- );
-}
-
-
-
-
-
-void cProtocol132::SendDestroyEntity(const cEntity & a_Entity)
-{
- if (a_Entity.GetUniqueID() == m_Client->GetPlayer()->GetUniqueID())
- {
- // Do not send "destroy self" to the client, the client would crash (FS #254)
- return;
- }
-
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_DESTROY_ENTITIES);
- WriteByte(1); // entity count
- WriteInt (a_Entity.GetUniqueID());
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_ENTITY_EQUIPMENT);
- WriteInt (a_Entity.GetUniqueID());
- WriteShort(a_SlotNum);
- WriteItem (a_Item);
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_LOGIN);
- WriteInt (a_Player.GetUniqueID()); // EntityID of the player
- WriteString("default"); // Level type
- WriteByte ((int)a_Player.GetGameMode());
- WriteByte ((Byte)(a_World.GetDimension()));
- WriteByte (2); // TODO: Difficulty
- WriteByte (0); // Unused, used to be world height
- WriteByte (8); // Client list width or something
- Flush();
-
- SendCompass(a_World);
-
- // Send the initial position (so that confirmation works, FS #245):
- SendPlayerMoveLook();
-}
-
-
-
-
-
-void cProtocol132::SendPlayerSpawn(const cPlayer & a_Player)
-{
- const cItem & HeldItem = a_Player.GetEquippedItem();
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_PLAYER_SPAWN);
- WriteInt (a_Player.GetUniqueID());
- WriteString(a_Player.GetName());
- WriteInt ((int)(a_Player.GetPosX() * 32));
- WriteInt ((int)(a_Player.GetPosY() * 32));
- WriteInt ((int)(a_Player.GetPosZ() * 32));
- WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256));
- WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256));
- WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType);
- // Player metadata: just use a default metadata value, since the client doesn't like starting without any metadata:
- WriteByte (0); // Index 0, byte (flags)
- WriteByte (0); // Flags, empty
- WriteByte (0x7f); // End of metadata
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_SOUND_EFFECT);
- WriteString (a_SoundName);
- WriteInt (a_SrcX);
- WriteInt (a_SrcY);
- WriteInt (a_SrcZ);
- WriteFloat (a_Volume);
- WriteByte ((char)(a_Pitch * 63.0f));
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_SOUND_PARTICLE_EFFECT);
- WriteInt (a_EffectID);
- WriteInt (a_SrcX / 8);
- WriteByte(a_SrcY / 8);
- WriteInt (a_SrcZ / 8);
- WriteInt (a_Data);
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendSpawnMob(const cMonster & a_Mob)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_SPAWN_MOB);
- WriteInt (a_Mob.GetUniqueID());
- WriteByte (a_Mob.GetMobType());
- WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32));
- WriteByte ((Byte)((a_Mob.GetRotation() / 360.f) * 256));
- WriteByte ((Byte)((a_Mob.GetPitch() / 360.f) * 256));
- WriteByte ((Byte)((a_Mob.GetHeadYaw() / 360.f) * 256));
- WriteShort ((short)(a_Mob.GetSpeedX() * 400));
- WriteShort ((short)(a_Mob.GetSpeedY() * 400));
- WriteShort ((short)(a_Mob.GetSpeedZ() * 400));
- AString MetaData = GetEntityMetaData(a_Mob);
- SendData (MetaData.data(), MetaData.size());
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
-{
- // Not used in 1.3.2
- // Does it unload chunks on its own?
-}
-
-
-
-
-
-void cProtocol132::SendWholeInventory(const cWindow & a_Window)
-{
- // 1.3.2 requires player inventory slots to be sent as SetSlot packets,
- // otherwise it sometimes fails to update the window
-
- // Send the entire window:
- super::SendWholeInventory(a_Window);
-
- // Send the player inventory and hotbar:
- const cInventory & Inventory = m_Client->GetPlayer()->GetInventory();
- int BaseOffset = a_Window.GetNumSlots() - (cInventory::invNumSlots - cInventory::invInventoryOffset); // Number of non-inventory slots
- char WindowID = a_Window.GetWindowID();
- for (int i = 0; i < cInventory::invInventoryCount; i++)
- {
- SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetInventorySlot(i));
- } // for i - Inventory[]
- BaseOffset += cInventory::invInventoryCount;
- for (int i = 0; i < cInventory::invHotbarCount; i++)
- {
- SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetHotbarSlot(i));
- } // for i - Hotbar[]
-
- // Send even the item being dragged:
- SendInventorySlot(-1, -1, m_Client->GetPlayer()->GetDraggingItem());
-}
-
-
-
-
-
-AString cProtocol132::GetAuthServerID(void)
-{
- // http://wiki.vg/wiki/index.php?title=Session&oldid=2615
- // Server uses SHA1 to mix ServerID, Client secret and server public key together
- // The mixing is done in StartEncryption, the result is in m_AuthServerID
-
- return m_AuthServerID;
-}
-
-
-
-
-
-int cProtocol132::ParsePacket(unsigned char a_PacketType)
-{
- switch (a_PacketType)
- {
- default: return super::ParsePacket(a_PacketType); // off-load previously known packets into cProtocol125
- case PACKET_LOCALE_VIEW_DISTANCE: return ParseLocaleViewDistance();
- case PACKET_CLIENT_STATUSES: return ParseClientStatuses();
- case PACKET_ENCRYPTION_KEY_RESP: return ParseEncryptionKeyResponse();
- }
-}
-
-
-
-
-
-int cProtocol132::ParseBlockPlace(void)
-{
- HANDLE_PACKET_READ(ReadBEInt, int, PosX);
- HANDLE_PACKET_READ(ReadByte, Byte, PosY);
- HANDLE_PACKET_READ(ReadBEInt, int, PosZ);
- HANDLE_PACKET_READ(ReadChar, char, BlockFace);
-
- cItem HeldItem;
- int res = ParseItem(HeldItem);
- if (res < 0)
- {
- return res;
- }
-
- HANDLE_PACKET_READ(ReadChar, char, CursorX);
- HANDLE_PACKET_READ(ReadChar, char, CursorY);
- HANDLE_PACKET_READ(ReadChar, char, CursorZ);
-
- m_Client->HandleRightClick(PosX, PosY, PosZ, BlockFace, CursorX, CursorY, CursorZ, HeldItem);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol132::ParseHandshake(void)
-{
- HANDLE_PACKET_READ(ReadByte, Byte, ProtocolVersion);
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username);
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ServerHost);
- HANDLE_PACKET_READ(ReadBEInt, int, ServerPort);
- m_Username = Username;
-
- if (!m_Client->HandleHandshake( m_Username ))
- {
- return PARSE_OK; // Player is not allowed into the server
- }
-
- // Send a 0xFD Encryption Key Request http://wiki.vg/Protocol#0xFD
- CryptoPP::StringSink sink(m_ServerPublicKey); // GCC won't allow inline instantiation in the following line, damned temporary refs
- cRoot::Get()->GetServer()->GetPublicKey().Save(sink);
- SendEncryptionKeyRequest();
-
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol132::ParseClientStatuses(void)
-{
- HANDLE_PACKET_READ(ReadByte, byte, Status);
- if ((Status & 1) == 0)
- {
- m_Client->HandleLogin(39, m_Username);
- }
- else
- {
- m_Client->HandleRespawn();
- }
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol132::ParseEncryptionKeyResponse(void)
-{
- HANDLE_PACKET_READ(ReadBEShort, short, EncKeyLength);
- AString EncKey;
- if (!m_ReceivedData.ReadString(EncKey, EncKeyLength))
- {
- return PARSE_INCOMPLETE;
- }
- HANDLE_PACKET_READ(ReadBEShort, short, EncNonceLength);
- AString EncNonce;
- if (!m_ReceivedData.ReadString(EncNonce, EncNonceLength))
- {
- return PARSE_INCOMPLETE;
- }
- if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN))
- {
- LOGD("Too long encryption");
- m_Client->Kick("Hacked client");
- return PARSE_OK;
- }
-
- HandleEncryptionKeyResponse(EncKey, EncNonce);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol132::ParseLocaleViewDistance(void)
-{
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale);
- HANDLE_PACKET_READ(ReadChar, char, ViewDistance);
- HANDLE_PACKET_READ(ReadChar, char, ChatFlags);
- HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty);
- // TODO: m_Client->HandleLocale(Locale);
- // TODO: m_Client->HandleViewDistance(ViewDistance);
- // TODO: m_Client->HandleChatFlags(ChatFlags);
- // Ignoring client difficulty
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol132::ParseLogin(void)
-{
- // Login packet not used in 1.3.2
- return PARSE_ERROR;
-}
-
-
-
-
-
-int cProtocol132::ParsePlayerAbilities(void)
-{
- HANDLE_PACKET_READ(ReadBool, bool, Flags);
- HANDLE_PACKET_READ(ReadChar, char, FlyingSpeed);
- HANDLE_PACKET_READ(ReadChar, char, WalkingSpeed);
- // TODO: m_Client->HandlePlayerAbilities(...);
- return PARSE_OK;
-}
-
-
-
-
-
-void cProtocol132::SendData(const char * a_Data, int a_Size)
-{
- m_DataToSend.append(a_Data, a_Size);
-}
-
-
-
-
-
-void cProtocol132::Flush(void)
-{
- ASSERT(m_CSPacket.IsLockedByCurrentThread()); // Did all packets lock the CS properly?
-
- if (m_DataToSend.empty())
- {
- LOGD("Flushing empty");
- return;
- }
- const char * a_Data = m_DataToSend.data();
- int a_Size = m_DataToSend.size();
- if (m_IsEncrypted)
- {
- byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks)
- while (a_Size > 0)
- {
- int NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size;
- m_Encryptor.ProcessData(Encrypted, (byte *)a_Data, NumBytes);
- super::SendData((const char *)Encrypted, NumBytes);
- a_Size -= NumBytes;
- a_Data += NumBytes;
- }
- }
- else
- {
- super::SendData(a_Data, a_Size);
- }
- m_DataToSend.clear();
-}
-
-
-
-
-
-void cProtocol132::WriteItem(const cItem & a_Item)
-{
- short ItemType = a_Item.m_ItemType;
- ASSERT(ItemType >= -1); // Check validity of packets in debug runtime
- if (ItemType <= 0)
- {
- // Fix, to make sure no invalid values are sent.
- ItemType = -1;
- }
-
- if (a_Item.IsEmpty())
- {
- WriteShort(-1);
- return;
- }
-
- WriteShort(ItemType);
- WriteByte (a_Item.m_ItemCount);
- WriteShort(a_Item.m_ItemDamage);
-
- if (a_Item.m_Enchantments.IsEmpty())
- {
- WriteShort(-1);
- return;
- }
-
- // Send the enchantments:
- cFastNBTWriter Writer;
- const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
- a_Item.m_Enchantments.WriteToNBTCompound(Writer, TagName);
- Writer.Finish();
- AString Compressed;
- CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed);
- WriteShort(Compressed.size());
- SendData(Compressed.data(), Compressed.size());
-}
-
-
-
-
-
-int cProtocol132::ParseItem(cItem & a_Item)
-{
- HANDLE_PACKET_READ(ReadBEShort, short, ItemType);
-
- if (ItemType <= -1)
- {
- a_Item.Empty();
- return PARSE_OK;
- }
- a_Item.m_ItemType = ItemType;
-
- HANDLE_PACKET_READ(ReadChar, char, ItemCount);
- HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage);
- a_Item.m_ItemCount = ItemCount;
- a_Item.m_ItemDamage = ItemDamage;
- if (ItemCount <= 0)
- {
- a_Item.Empty();
- }
-
- HANDLE_PACKET_READ(ReadBEShort, short, MetadataLength);
- if (MetadataLength <= 0)
- {
- return PARSE_OK;
- }
-
- // Read the metadata
- AString Metadata;
- Metadata.resize(MetadataLength);
- if (!m_ReceivedData.ReadBuf((void *)Metadata.data(), MetadataLength))
- {
- return PARSE_INCOMPLETE;
- }
-
- return ParseItemMetadata(a_Item, Metadata);
-}
-
-
-
-
-
-int cProtocol132::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata)
-{
- // Uncompress the GZIPped data:
- AString Uncompressed;
- if (UncompressStringGZIP(a_Metadata.data(), a_Metadata.size(), Uncompressed) != Z_OK)
- {
- AString HexDump;
- CreateHexDump(HexDump, a_Metadata.data(), a_Metadata.size(), 16);
- LOG("Cannot unGZIP item metadata:\n%s", HexDump.c_str());
- return PARSE_ERROR;
- }
-
- // Parse into NBT:
- cParsedNBT NBT(Uncompressed.data(), Uncompressed.size());
- if (!NBT.IsValid())
- {
- AString HexDump;
- CreateHexDump(HexDump, Uncompressed.data(), Uncompressed.size(), 16);
- LOG("Cannot parse NBT item metadata:\n%s", HexDump.c_str());
- return PARSE_ERROR;
- }
-
- // Load enchantments from the NBT:
- for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag))
- {
- if (
- (NBT.GetType(tag) == TAG_List) &&
- (
- (NBT.GetName(tag) == "ench") ||
- (NBT.GetName(tag) == "StoredEnchantments")
- )
- )
- {
- a_Item.m_Enchantments.ParseFromNBT(NBT, tag);
- }
- }
-
- return PARSE_OK;
-}
-
-
-
-
-
-void cProtocol132::SendCompass(const cWorld & a_World)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_COMPASS);
- WriteInt((int)(a_World.GetSpawnX()));
- WriteInt((int)(a_World.GetSpawnY()));
- WriteInt((int)(a_World.GetSpawnZ()));
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendEncryptionKeyRequest(void)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte((char)0xfd);
- WriteString(cRoot::Get()->GetServer()->GetServerID());
- WriteShort((short)m_ServerPublicKey.size());
- SendData(m_ServerPublicKey.data(), m_ServerPublicKey.size());
- WriteShort(4);
- WriteInt((int)(intptr_t)this); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :)
- Flush();
-}
-
-
-
-
-
-void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce)
-{
- // Decrypt EncNonce using privkey
- RSAES<PKCS1v15>::Decryptor rsaDecryptor(cRoot::Get()->GetServer()->GetPrivateKey());
- time_t CurTime = time(NULL);
- CryptoPP::RandomPool rng;
- rng.Put((const byte *)&CurTime, sizeof(CurTime));
- byte DecryptedNonce[MAX_ENC_LEN];
- DecodingResult res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncNonce.data(), a_EncNonce.size(), DecryptedNonce);
- if (!res.isValidCoding || (res.messageLength != 4))
- {
- LOGD("Bad nonce length");
- m_Client->Kick("Hacked client");
- return;
- }
- if (ntohl(*((int *)DecryptedNonce)) != (unsigned)(uintptr_t)this)
- {
- LOGD("Bad nonce value");
- m_Client->Kick("Hacked client");
- return;
- }
-
- // Decrypt the symmetric encryption key using privkey:
- byte DecryptedKey[MAX_ENC_LEN];
- res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncKey.data(), a_EncKey.size(), DecryptedKey);
- if (!res.isValidCoding || (res.messageLength != 16))
- {
- LOGD("Bad key length");
- m_Client->Kick("Hacked client");
- return;
- }
-
- {
- // Send encryption key response:
- cCSLock Lock(m_CSPacket);
- WriteByte((char)0xfc);
- WriteShort(0);
- WriteShort(0);
- Flush();
- }
-
- StartEncryption(DecryptedKey);
- return;
-}
-
-
-
-
-
-void cProtocol132::StartEncryption(const byte * a_Key)
-{
- m_Encryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1));
- m_Decryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1));
- m_IsEncrypted = true;
-
- // Prepare the m_AuthServerID:
- CryptoPP::SHA1 Checksum;
- AString ServerID = cRoot::Get()->GetServer()->GetServerID();
- Checksum.Update((const byte *)ServerID.c_str(), ServerID.length());
- Checksum.Update(a_Key, 16);
- Checksum.Update((const byte *)m_ServerPublicKey.c_str(), m_ServerPublicKey.length());
- byte Digest[20];
- Checksum.Final(Digest);
- DigestToJava(Digest, m_AuthServerID);
-}
-
-
-
-
+
+// Protocol132.cpp
+
+// Implements the cProtocol132 class representing the release 1.3.2 protocol (#39)
+
+#include "Globals.h"
+#include "Protocol132.h"
+#include "../Root.h"
+#include "../Server.h"
+#include "../ClientHandle.h"
+#include "../../CryptoPP/randpool.h"
+#include "../Item.h"
+#include "ChunkDataSerializer.h"
+#include "../Player.h"
+#include "../Mobs/Monster.h"
+#include "../UI/Window.h"
+#include "../Pickup.h"
+#include "../WorldStorage/FastNBT.h"
+#include "../StringCompression.h"
+
+
+
+
+
+#define HANDLE_PACKET_READ(Proc, Type, Var) \
+ Type Var; \
+ { \
+ if (!m_ReceivedData.Proc(Var)) \
+ { \
+ m_ReceivedData.CheckValid(); \
+ return PARSE_INCOMPLETE; \
+ } \
+ m_ReceivedData.CheckValid(); \
+ }
+
+
+
+
+typedef unsigned char Byte;
+
+
+
+
+
+using namespace CryptoPP;
+
+
+
+
+
+const int MAX_ENC_LEN = 512; // Maximum size of the encrypted message; should be 128, but who knows...
+
+
+
+
+
+enum
+{
+ PACKET_KEEP_ALIVE = 0x00,
+ PACKET_LOGIN = 0x01,
+ PACKET_ENTITY_EQUIPMENT = 0x05,
+ PACKET_COMPASS = 0x06,
+ PACKET_PLAYER_SPAWN = 0x14,
+ PACKET_COLLECT_PICKUP = 0x16,
+ PACKET_SPAWN_MOB = 0x18,
+ PACKET_DESTROY_ENTITIES = 0x1d,
+ PACKET_CHUNK_DATA = 0x33,
+ PACKET_BLOCK_CHANGE = 0x35,
+ PACKET_BLOCK_ACTION = 0x36,
+ PACKET_BLOCK_BREAK_ANIM = 0x37,
+ PACKET_SOUND_EFFECT = 0x3e,
+ PACKET_SOUND_PARTICLE_EFFECT = 0x3d,
+ PACKET_TAB_COMPLETION = 0xcb,
+ PACKET_LOCALE_VIEW_DISTANCE = 0xcc,
+ PACKET_CLIENT_STATUSES = 0xcd,
+ PACKET_ENCRYPTION_KEY_RESP = 0xfc,
+} ;
+
+
+
+
+
+// Converts a raw 160-bit SHA1 digest into a Java Hex representation
+// According to http://wiki.vg/wiki/index.php?title=Protocol_Encryption&oldid=2802
+static void DigestToJava(byte a_Digest[20], AString & a_Out)
+{
+ bool IsNegative = (a_Digest[0] >= 0x80);
+ if (IsNegative)
+ {
+ // Two's complement:
+ bool carry = true; // Add one to the whole number
+ for (int i = 19; i >= 0; i--)
+ {
+ a_Digest[i] = ~a_Digest[i];
+ if (carry)
+ {
+ carry = (a_Digest[i] == 0xff);
+ a_Digest[i]++;
+ }
+ }
+ }
+ a_Out.clear();
+ a_Out.reserve(40);
+ for (int i = 0; i < 20; i++)
+ {
+ AppendPrintf(a_Out, "%02x", a_Digest[i]);
+ }
+ while ((a_Out.length() > 0) && (a_Out[0] == '0'))
+ {
+ a_Out.erase(0, 1);
+ }
+ if (IsNegative)
+ {
+ a_Out.insert(0, "-");
+ }
+}
+
+
+
+
+
+/*
+// Self-test the hash formatting for known values:
+// sha1(Notch) : 4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48
+// sha1(jeb_) : -7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1
+// sha1(simon) : 88e16a1019277b15d58faf0541e11910eb756f6
+
+class Test
+{
+public:
+ Test(void)
+ {
+ AString DigestNotch, DigestJeb, DigestSimon;
+ byte Digest[20];
+ CryptoPP::SHA1 Checksum;
+ Checksum.Update((const byte *)"Notch", 5);
+ Checksum.Final(Digest);
+ DigestToJava(Digest, DigestNotch);
+ Checksum.Restart();
+ Checksum.Update((const byte *)"jeb_", 4);
+ Checksum.Final(Digest);
+ DigestToJava(Digest, DigestJeb);
+ Checksum.Restart();
+ Checksum.Update((const byte *)"simon", 5);
+ Checksum.Final(Digest);
+ DigestToJava(Digest, DigestSimon);
+ printf("Notch: \"%s\"", DigestNotch.c_str());
+ printf("jeb_: \"%s\"", DigestJeb.c_str());
+ printf("simon: \"%s\"", DigestSimon.c_str());
+ }
+} test;
+*/
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cProtocol132:
+
+cProtocol132::cProtocol132(cClientHandle * a_Client) :
+ super(a_Client),
+ m_IsEncrypted(false)
+{
+}
+
+
+
+
+
+cProtocol132::~cProtocol132()
+{
+ if (!m_DataToSend.empty())
+ {
+ LOGD("There are %d unsent bytes while deleting cProtocol132", m_DataToSend.size());
+ }
+}
+
+
+
+
+
+void cProtocol132::DataReceived(const char * a_Data, int a_Size)
+{
+ if (m_IsEncrypted)
+ {
+ byte Decrypted[512];
+ while (a_Size > 0)
+ {
+ int NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size;
+ m_Decryptor.ProcessData(Decrypted, (byte *)a_Data, NumBytes);
+ super::DataReceived((const char *)Decrypted, NumBytes);
+ a_Size -= NumBytes;
+ a_Data += NumBytes;
+ }
+ }
+ else
+ {
+ super::DataReceived(a_Data, a_Size);
+ }
+}
+
+
+
+
+
+void cProtocol132::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_BLOCK_ACTION);
+ WriteInt (a_BlockX);
+ WriteShort((short)a_BlockY);
+ WriteInt (a_BlockZ);
+ WriteByte (a_Byte1);
+ WriteByte (a_Byte2);
+ WriteShort(a_BlockType);
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_BLOCK_BREAK_ANIM);
+ WriteInt (a_entityID);
+ WriteInt (a_BlockX);
+ WriteInt (a_BlockY);
+ WriteInt (a_BlockZ);
+ WriteByte (stage);
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_BLOCK_CHANGE);
+ WriteInt (a_BlockX);
+ WriteByte ((unsigned char)a_BlockY);
+ WriteInt (a_BlockZ);
+ WriteShort(a_BlockType);
+ WriteByte (a_BlockMeta);
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
+{
+ cCSLock Lock(m_CSPacket);
+
+ // Pre-chunk not used in 1.3.2. Finally.
+
+ // Send the chunk data:
+ AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_3_2);
+ WriteByte(PACKET_CHUNK_DATA);
+ WriteInt (a_ChunkX);
+ WriteInt (a_ChunkZ);
+ SendData(Serialized.data(), Serialized.size());
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_COLLECT_PICKUP);
+ WriteInt (a_Pickup.GetUniqueID());
+ WriteInt (a_Player.GetUniqueID());
+ Flush();
+
+ // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;)
+ SendSoundEffect(
+ "random.pop",
+ (int)(a_Pickup.GetPosX() * 8), (int)(a_Pickup.GetPosY() * 8), (int)(a_Pickup.GetPosZ() * 8),
+ 0.5, (float)(0.75 + ((float)((a_Pickup.GetUniqueID() * 23) % 32)) / 64)
+ );
+}
+
+
+
+
+
+void cProtocol132::SendDestroyEntity(const cEntity & a_Entity)
+{
+ if (a_Entity.GetUniqueID() == m_Client->GetPlayer()->GetUniqueID())
+ {
+ // Do not send "destroy self" to the client, the client would crash (FS #254)
+ return;
+ }
+
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_DESTROY_ENTITIES);
+ WriteByte(1); // entity count
+ WriteInt (a_Entity.GetUniqueID());
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_ENTITY_EQUIPMENT);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteShort(a_SlotNum);
+ WriteItem (a_Item);
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_LOGIN);
+ WriteInt (a_Player.GetUniqueID()); // EntityID of the player
+ WriteString("default"); // Level type
+ WriteByte ((int)a_Player.GetGameMode());
+ WriteByte ((Byte)(a_World.GetDimension()));
+ WriteByte (2); // TODO: Difficulty
+ WriteByte (0); // Unused, used to be world height
+ WriteByte (8); // Client list width or something
+ Flush();
+
+ SendCompass(a_World);
+
+ // Send the initial position (so that confirmation works, FS #245):
+ SendPlayerMoveLook();
+}
+
+
+
+
+
+void cProtocol132::SendPlayerSpawn(const cPlayer & a_Player)
+{
+ const cItem & HeldItem = a_Player.GetEquippedItem();
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_PLAYER_SPAWN);
+ WriteInt (a_Player.GetUniqueID());
+ WriteString(a_Player.GetName());
+ WriteInt ((int)(a_Player.GetPosX() * 32));
+ WriteInt ((int)(a_Player.GetPosY() * 32));
+ WriteInt ((int)(a_Player.GetPosZ() * 32));
+ WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256));
+ WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256));
+ WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType);
+ // Player metadata: just use a default metadata value, since the client doesn't like starting without any metadata:
+ WriteByte (0); // Index 0, byte (flags)
+ WriteByte (0); // Flags, empty
+ WriteByte (0x7f); // End of metadata
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_SOUND_EFFECT);
+ WriteString (a_SoundName);
+ WriteInt (a_SrcX);
+ WriteInt (a_SrcY);
+ WriteInt (a_SrcZ);
+ WriteFloat (a_Volume);
+ WriteByte ((char)(a_Pitch * 63.0f));
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_SOUND_PARTICLE_EFFECT);
+ WriteInt (a_EffectID);
+ WriteInt (a_SrcX / 8);
+ WriteByte(a_SrcY / 8);
+ WriteInt (a_SrcZ / 8);
+ WriteInt (a_Data);
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendSpawnMob(const cMonster & a_Mob)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_SPAWN_MOB);
+ WriteInt (a_Mob.GetUniqueID());
+ WriteByte (a_Mob.GetMobType());
+ WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32));
+ WriteByte ((Byte)((a_Mob.GetRotation() / 360.f) * 256));
+ WriteByte ((Byte)((a_Mob.GetPitch() / 360.f) * 256));
+ WriteByte ((Byte)((a_Mob.GetHeadYaw() / 360.f) * 256));
+ WriteShort ((short)(a_Mob.GetSpeedX() * 400));
+ WriteShort ((short)(a_Mob.GetSpeedY() * 400));
+ WriteShort ((short)(a_Mob.GetSpeedZ() * 400));
+ AString MetaData = GetEntityMetaData(a_Mob);
+ SendData (MetaData.data(), MetaData.size());
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendTabCompletionResults(const AStringVector & a_Results)
+{
+ if (a_Results.empty())
+ {
+ // No results to send
+ return;
+ }
+
+ AString Serialized(a_Results[0]);
+ for (AStringVector::const_iterator itr = a_Results.begin() + 1, end = a_Results.end(); itr != end; ++itr)
+ {
+ Serialized.push_back(0);
+ Serialized.append(*itr);
+ } // for itr - a_Results[]
+
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_TAB_COMPLETION);
+ WriteString(Serialized);
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
+{
+ // Not used in 1.3.2
+ // Does it unload chunks on its own?
+}
+
+
+
+
+
+void cProtocol132::SendWholeInventory(const cWindow & a_Window)
+{
+ // 1.3.2 requires player inventory slots to be sent as SetSlot packets,
+ // otherwise it sometimes fails to update the window
+
+ // Send the entire window:
+ super::SendWholeInventory(a_Window);
+
+ // Send the player inventory and hotbar:
+ const cInventory & Inventory = m_Client->GetPlayer()->GetInventory();
+ int BaseOffset = a_Window.GetNumSlots() - (cInventory::invNumSlots - cInventory::invInventoryOffset); // Number of non-inventory slots
+ char WindowID = a_Window.GetWindowID();
+ for (int i = 0; i < cInventory::invInventoryCount; i++)
+ {
+ SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetInventorySlot(i));
+ } // for i - Inventory[]
+ BaseOffset += cInventory::invInventoryCount;
+ for (int i = 0; i < cInventory::invHotbarCount; i++)
+ {
+ SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetHotbarSlot(i));
+ } // for i - Hotbar[]
+
+ // Send even the item being dragged:
+ SendInventorySlot(-1, -1, m_Client->GetPlayer()->GetDraggingItem());
+}
+
+
+
+
+
+AString cProtocol132::GetAuthServerID(void)
+{
+ // http://wiki.vg/wiki/index.php?title=Session&oldid=2615
+ // Server uses SHA1 to mix ServerID, Client secret and server public key together
+ // The mixing is done in StartEncryption, the result is in m_AuthServerID
+
+ return m_AuthServerID;
+}
+
+
+
+
+
+int cProtocol132::ParsePacket(unsigned char a_PacketType)
+{
+ switch (a_PacketType)
+ {
+ default: return super::ParsePacket(a_PacketType); // off-load previously known packets into cProtocol125
+ case PACKET_CLIENT_STATUSES: return ParseClientStatuses();
+ case PACKET_ENCRYPTION_KEY_RESP: return ParseEncryptionKeyResponse();
+ case PACKET_LOCALE_VIEW_DISTANCE: return ParseLocaleViewDistance();
+ case PACKET_TAB_COMPLETION: return ParseTabCompletion();
+ }
+}
+
+
+
+
+
+int cProtocol132::ParseBlockPlace(void)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, PosX);
+ HANDLE_PACKET_READ(ReadByte, Byte, PosY);
+ HANDLE_PACKET_READ(ReadBEInt, int, PosZ);
+ HANDLE_PACKET_READ(ReadChar, char, BlockFace);
+
+ cItem HeldItem;
+ int res = ParseItem(HeldItem);
+ if (res < 0)
+ {
+ return res;
+ }
+
+ HANDLE_PACKET_READ(ReadChar, char, CursorX);
+ HANDLE_PACKET_READ(ReadChar, char, CursorY);
+ HANDLE_PACKET_READ(ReadChar, char, CursorZ);
+
+ m_Client->HandleRightClick(PosX, PosY, PosZ, BlockFace, CursorX, CursorY, CursorZ, HeldItem);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol132::ParseHandshake(void)
+{
+ HANDLE_PACKET_READ(ReadByte, Byte, ProtocolVersion);
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username);
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ServerHost);
+ HANDLE_PACKET_READ(ReadBEInt, int, ServerPort);
+ m_Username = Username;
+
+ if (!m_Client->HandleHandshake( m_Username ))
+ {
+ return PARSE_OK; // Player is not allowed into the server
+ }
+
+ // Send a 0xFD Encryption Key Request http://wiki.vg/Protocol#0xFD
+ CryptoPP::StringSink sink(m_ServerPublicKey); // GCC won't allow inline instantiation in the following line, damned temporary refs
+ cRoot::Get()->GetServer()->GetPublicKey().Save(sink);
+ SendEncryptionKeyRequest();
+
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol132::ParseClientStatuses(void)
+{
+ HANDLE_PACKET_READ(ReadByte, byte, Status);
+ if ((Status & 1) == 0)
+ {
+ m_Client->HandleLogin(39, m_Username);
+ }
+ else
+ {
+ m_Client->HandleRespawn();
+ }
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol132::ParseEncryptionKeyResponse(void)
+{
+ HANDLE_PACKET_READ(ReadBEShort, short, EncKeyLength);
+ AString EncKey;
+ if (!m_ReceivedData.ReadString(EncKey, EncKeyLength))
+ {
+ return PARSE_INCOMPLETE;
+ }
+ HANDLE_PACKET_READ(ReadBEShort, short, EncNonceLength);
+ AString EncNonce;
+ if (!m_ReceivedData.ReadString(EncNonce, EncNonceLength))
+ {
+ return PARSE_INCOMPLETE;
+ }
+ if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN))
+ {
+ LOGD("Too long encryption");
+ m_Client->Kick("Hacked client");
+ return PARSE_OK;
+ }
+
+ HandleEncryptionKeyResponse(EncKey, EncNonce);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol132::ParseLocaleViewDistance(void)
+{
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale);
+ HANDLE_PACKET_READ(ReadChar, char, ViewDistance);
+ HANDLE_PACKET_READ(ReadChar, char, ChatFlags);
+ HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty);
+ // TODO: m_Client->HandleLocale(Locale);
+ // TODO: m_Client->HandleViewDistance(ViewDistance);
+ // TODO: m_Client->HandleChatFlags(ChatFlags);
+ // Ignoring client difficulty
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol132::ParseLogin(void)
+{
+ // Login packet not used in 1.3.2
+ return PARSE_ERROR;
+}
+
+
+
+
+
+int cProtocol132::ParsePlayerAbilities(void)
+{
+ HANDLE_PACKET_READ(ReadBool, bool, Flags);
+ HANDLE_PACKET_READ(ReadChar, char, FlyingSpeed);
+ HANDLE_PACKET_READ(ReadChar, char, WalkingSpeed);
+ // TODO: m_Client->HandlePlayerAbilities(...);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol132::ParseTabCompletion(void)
+{
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Text);
+ m_Client->HandleTabCompletion(Text);
+ return PARSE_OK;
+}
+
+
+
+
+
+void cProtocol132::SendData(const char * a_Data, int a_Size)
+{
+ m_DataToSend.append(a_Data, a_Size);
+}
+
+
+
+
+
+void cProtocol132::Flush(void)
+{
+ ASSERT(m_CSPacket.IsLockedByCurrentThread()); // Did all packets lock the CS properly?
+
+ if (m_DataToSend.empty())
+ {
+ LOGD("Flushing empty");
+ return;
+ }
+ const char * a_Data = m_DataToSend.data();
+ int a_Size = m_DataToSend.size();
+ if (m_IsEncrypted)
+ {
+ byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks)
+ while (a_Size > 0)
+ {
+ int NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size;
+ m_Encryptor.ProcessData(Encrypted, (byte *)a_Data, NumBytes);
+ super::SendData((const char *)Encrypted, NumBytes);
+ a_Size -= NumBytes;
+ a_Data += NumBytes;
+ }
+ }
+ else
+ {
+ super::SendData(a_Data, a_Size);
+ }
+ m_DataToSend.clear();
+}
+
+
+
+
+
+void cProtocol132::WriteItem(const cItem & a_Item)
+{
+ short ItemType = a_Item.m_ItemType;
+ ASSERT(ItemType >= -1); // Check validity of packets in debug runtime
+ if (ItemType <= 0)
+ {
+ // Fix, to make sure no invalid values are sent.
+ ItemType = -1;
+ }
+
+ if (a_Item.IsEmpty())
+ {
+ WriteShort(-1);
+ return;
+ }
+
+ WriteShort(ItemType);
+ WriteByte (a_Item.m_ItemCount);
+ WriteShort(a_Item.m_ItemDamage);
+
+ if (a_Item.m_Enchantments.IsEmpty())
+ {
+ WriteShort(-1);
+ return;
+ }
+
+ // Send the enchantments:
+ cFastNBTWriter Writer;
+ const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
+ a_Item.m_Enchantments.WriteToNBTCompound(Writer, TagName);
+ Writer.Finish();
+ AString Compressed;
+ CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed);
+ WriteShort(Compressed.size());
+ SendData(Compressed.data(), Compressed.size());
+}
+
+
+
+
+
+int cProtocol132::ParseItem(cItem & a_Item)
+{
+ HANDLE_PACKET_READ(ReadBEShort, short, ItemType);
+
+ if (ItemType <= -1)
+ {
+ a_Item.Empty();
+ return PARSE_OK;
+ }
+ a_Item.m_ItemType = ItemType;
+
+ HANDLE_PACKET_READ(ReadChar, char, ItemCount);
+ HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage);
+ a_Item.m_ItemCount = ItemCount;
+ a_Item.m_ItemDamage = ItemDamage;
+ if (ItemCount <= 0)
+ {
+ a_Item.Empty();
+ }
+
+ HANDLE_PACKET_READ(ReadBEShort, short, MetadataLength);
+ if (MetadataLength <= 0)
+ {
+ return PARSE_OK;
+ }
+
+ // Read the metadata
+ AString Metadata;
+ Metadata.resize(MetadataLength);
+ if (!m_ReceivedData.ReadBuf((void *)Metadata.data(), MetadataLength))
+ {
+ return PARSE_INCOMPLETE;
+ }
+
+ return ParseItemMetadata(a_Item, Metadata);
+}
+
+
+
+
+
+int cProtocol132::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata)
+{
+ // Uncompress the GZIPped data:
+ AString Uncompressed;
+ if (UncompressStringGZIP(a_Metadata.data(), a_Metadata.size(), Uncompressed) != Z_OK)
+ {
+ AString HexDump;
+ CreateHexDump(HexDump, a_Metadata.data(), a_Metadata.size(), 16);
+ LOG("Cannot unGZIP item metadata:\n%s", HexDump.c_str());
+ return PARSE_ERROR;
+ }
+
+ // Parse into NBT:
+ cParsedNBT NBT(Uncompressed.data(), Uncompressed.size());
+ if (!NBT.IsValid())
+ {
+ AString HexDump;
+ CreateHexDump(HexDump, Uncompressed.data(), Uncompressed.size(), 16);
+ LOG("Cannot parse NBT item metadata:\n%s", HexDump.c_str());
+ return PARSE_ERROR;
+ }
+
+ // Load enchantments from the NBT:
+ for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag))
+ {
+ if (
+ (NBT.GetType(tag) == TAG_List) &&
+ (
+ (NBT.GetName(tag) == "ench") ||
+ (NBT.GetName(tag) == "StoredEnchantments")
+ )
+ )
+ {
+ a_Item.m_Enchantments.ParseFromNBT(NBT, tag);
+ }
+ }
+
+ return PARSE_OK;
+}
+
+
+
+
+
+void cProtocol132::SendCompass(const cWorld & a_World)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_COMPASS);
+ WriteInt((int)(a_World.GetSpawnX()));
+ WriteInt((int)(a_World.GetSpawnY()));
+ WriteInt((int)(a_World.GetSpawnZ()));
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::SendEncryptionKeyRequest(void)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte((char)0xfd);
+ WriteString(cRoot::Get()->GetServer()->GetServerID());
+ WriteShort((short)m_ServerPublicKey.size());
+ SendData(m_ServerPublicKey.data(), m_ServerPublicKey.size());
+ WriteShort(4);
+ WriteInt((int)(intptr_t)this); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :)
+ Flush();
+}
+
+
+
+
+
+void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce)
+{
+ // Decrypt EncNonce using privkey
+ RSAES<PKCS1v15>::Decryptor rsaDecryptor(cRoot::Get()->GetServer()->GetPrivateKey());
+ time_t CurTime = time(NULL);
+ CryptoPP::RandomPool rng;
+ rng.Put((const byte *)&CurTime, sizeof(CurTime));
+ byte DecryptedNonce[MAX_ENC_LEN];
+ DecodingResult res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncNonce.data(), a_EncNonce.size(), DecryptedNonce);
+ if (!res.isValidCoding || (res.messageLength != 4))
+ {
+ LOGD("Bad nonce length");
+ m_Client->Kick("Hacked client");
+ return;
+ }
+ if (ntohl(*((int *)DecryptedNonce)) != (unsigned)(uintptr_t)this)
+ {
+ LOGD("Bad nonce value");
+ m_Client->Kick("Hacked client");
+ return;
+ }
+
+ // Decrypt the symmetric encryption key using privkey:
+ byte DecryptedKey[MAX_ENC_LEN];
+ res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncKey.data(), a_EncKey.size(), DecryptedKey);
+ if (!res.isValidCoding || (res.messageLength != 16))
+ {
+ LOGD("Bad key length");
+ m_Client->Kick("Hacked client");
+ return;
+ }
+
+ {
+ // Send encryption key response:
+ cCSLock Lock(m_CSPacket);
+ WriteByte((char)0xfc);
+ WriteShort(0);
+ WriteShort(0);
+ Flush();
+ }
+
+ StartEncryption(DecryptedKey);
+ return;
+}
+
+
+
+
+
+void cProtocol132::StartEncryption(const byte * a_Key)
+{
+ m_Encryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1));
+ m_Decryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1));
+ m_IsEncrypted = true;
+
+ // Prepare the m_AuthServerID:
+ CryptoPP::SHA1 Checksum;
+ AString ServerID = cRoot::Get()->GetServer()->GetServerID();
+ Checksum.Update((const byte *)ServerID.c_str(), ServerID.length());
+ Checksum.Update(a_Key, 16);
+ Checksum.Update((const byte *)m_ServerPublicKey.c_str(), m_ServerPublicKey.length());
+ byte Digest[20];
+ Checksum.Final(Digest);
+ DigestToJava(Digest, m_AuthServerID);
+}
+
+
+
+
diff --git a/source/Protocol/Protocol132.h b/source/Protocol/Protocol132.h
index cf559f1ca..dc4d8aeef 100644
--- a/source/Protocol/Protocol132.h
+++ b/source/Protocol/Protocol132.h
@@ -1,100 +1,102 @@
-
-// Protocol132.h
-
-// Interfaces to the cProtocol132 class representing the release 1.3.2 protocol (#39)
-
-
-
-
-
-#pragma once
-
-#include "Protocol125.h"
-#include "../../CryptoPP/modes.h"
-#include "../../CryptoPP/aes.h"
-
-
-
-
-
-class cProtocol132 :
- public cProtocol125
-{
- typedef cProtocol125 super;
-public:
-
- cProtocol132(cClientHandle * a_Client);
- virtual ~cProtocol132();
-
- /// Called when client sends some data:
- virtual void DataReceived(const char * a_Data, int a_Size) override;
-
- // Sending commands (alphabetically sorted):
- virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
- virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
- virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
- virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
- virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override;
- virtual void SendDestroyEntity (const cEntity & a_Entity) override;
- virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
- virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
- virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
- virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8
- virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
- virtual void SendSpawnMob (const cMonster & a_Mob) override;
- virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
- virtual void SendWholeInventory (const cWindow & a_Window) override;
-
- virtual AString GetAuthServerID(void) override;
-
- /// Handling of the additional packets:
- virtual int ParsePacket(unsigned char a_PacketType) override;
-
- // Modified packets:
- virtual int ParseBlockPlace (void) override;
- virtual int ParseHandshake (void) override;
- virtual int ParseLogin (void) override;
- virtual int ParsePlayerAbilities(void) override;
-
- // New packets:
- virtual int ParseClientStatuses (void);
- virtual int ParseEncryptionKeyResponse(void);
- virtual int ParseLocaleViewDistance (void);
-
-protected:
- bool m_IsEncrypted;
- CryptoPP::CFB_Mode<CryptoPP::AES>::Decryption m_Decryptor;
- CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption m_Encryptor;
- AString m_DataToSend;
-
- /// The ServerID used for session authentication; set in StartEncryption(), used in GetAuthServerID()
- AString m_AuthServerID;
-
- /// The server's public key, as used by SendEncryptionKeyRequest() and StartEncryption()
- AString m_ServerPublicKey;
-
- virtual void SendData(const char * a_Data, int a_Size) override;
-
- // DEBUG:
- virtual void Flush(void) override;
-
- // Items in slots are sent differently
- virtual void WriteItem(const cItem & a_Item) override;
- virtual int ParseItem(cItem & a_Item) override;
-
- /// Parses the metadata that may come with the item.
- int ParseItemMetadata(cItem & a_Item, const AString & a_Metadata);
-
- virtual void SendCompass(const cWorld & a_World);
- virtual void SendEncryptionKeyRequest(void);
-
- /// Decrypts the key and nonce, checks nonce, starts the symmetric encryption
- void HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce);
-
- /// Starts the symmetric encryption with the specified key; also sets m_AuthServerID
- void StartEncryption(const byte * a_Key);
-} ;
-
-
-
-
+
+// Protocol132.h
+
+// Interfaces to the cProtocol132 class representing the release 1.3.2 protocol (#39)
+
+
+
+
+
+#pragma once
+
+#include "Protocol125.h"
+#include "../../CryptoPP/modes.h"
+#include "../../CryptoPP/aes.h"
+
+
+
+
+
+class cProtocol132 :
+ public cProtocol125
+{
+ typedef cProtocol125 super;
+public:
+
+ cProtocol132(cClientHandle * a_Client);
+ virtual ~cProtocol132();
+
+ /// Called when client sends some data:
+ virtual void DataReceived(const char * a_Data, int a_Size) override;
+
+ // Sending commands (alphabetically sorted):
+ virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
+ virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
+ virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
+ virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
+ virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override;
+ virtual void SendDestroyEntity (const cEntity & a_Entity) override;
+ virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
+ virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
+ virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
+ virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8
+ virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
+ virtual void SendSpawnMob (const cMonster & a_Mob) override;
+ virtual void SendTabCompletionResults(const AStringVector & a_Results) override;
+ virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
+ virtual void SendWholeInventory (const cWindow & a_Window) override;
+
+ virtual AString GetAuthServerID(void) override;
+
+ /// Handling of the additional packets:
+ virtual int ParsePacket(unsigned char a_PacketType) override;
+
+ // Modified packets:
+ virtual int ParseBlockPlace (void) override;
+ virtual int ParseHandshake (void) override;
+ virtual int ParseLogin (void) override;
+ virtual int ParsePlayerAbilities(void) override;
+
+ // New packets:
+ virtual int ParseClientStatuses (void);
+ virtual int ParseEncryptionKeyResponse(void);
+ virtual int ParseLocaleViewDistance (void);
+ virtual int ParseTabCompletion (void);
+
+protected:
+ bool m_IsEncrypted;
+ CryptoPP::CFB_Mode<CryptoPP::AES>::Decryption m_Decryptor;
+ CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption m_Encryptor;
+ AString m_DataToSend;
+
+ /// The ServerID used for session authentication; set in StartEncryption(), used in GetAuthServerID()
+ AString m_AuthServerID;
+
+ /// The server's public key, as used by SendEncryptionKeyRequest() and StartEncryption()
+ AString m_ServerPublicKey;
+
+ virtual void SendData(const char * a_Data, int a_Size) override;
+
+ // DEBUG:
+ virtual void Flush(void) override;
+
+ // Items in slots are sent differently
+ virtual void WriteItem(const cItem & a_Item) override;
+ virtual int ParseItem(cItem & a_Item) override;
+
+ /// Parses the metadata that may come with the item.
+ int ParseItemMetadata(cItem & a_Item, const AString & a_Metadata);
+
+ virtual void SendCompass(const cWorld & a_World);
+ virtual void SendEncryptionKeyRequest(void);
+
+ /// Decrypts the key and nonce, checks nonce, starts the symmetric encryption
+ void HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce);
+
+ /// Starts the symmetric encryption with the specified key; also sets m_AuthServerID
+ void StartEncryption(const byte * a_Key);
+} ;
+
+
+
+
diff --git a/source/Protocol/Protocol14x.cpp b/source/Protocol/Protocol14x.cpp
index b7d3c91df..35fe0ce1c 100644
--- a/source/Protocol/Protocol14x.cpp
+++ b/source/Protocol/Protocol14x.cpp
@@ -1,253 +1,253 @@
-
-// Protocol14x.cpp
-
-/*
-Implements the 1.4.x protocol classes representing these protocols:
-- cProtocol142:
- - release 1.4.2 protocol (#47)
- - release 1.4.4 protocol (#49) - the same protocol class is used, because the only difference is in a packet that MCServer doesn't implement yet (ITEM_DATA)
- - release 1.4.5 protocol (same as 1.4.4)
-- cProtocol146:
- - release 1.4.6 protocol (#51)
-*/
-
-#include "Globals.h"
-#include "Protocol14x.h"
-#include "../Root.h"
-#include "../Server.h"
-#include "../ClientHandle.h"
-#include "../../CryptoPP/randpool.h"
-#include "../Item.h"
-#include "ChunkDataSerializer.h"
-#include "../Player.h"
-#include "../Mobs/Monster.h"
-#include "../UI/Window.h"
-#include "../Pickup.h"
-#include "../FallingBlock.h"
-
-
-
-
-
-#define HANDLE_PACKET_READ(Proc, Type, Var) \
- Type Var; \
- { \
- if (!m_ReceivedData.Proc(Var)) \
- { \
- m_ReceivedData.CheckValid(); \
- return PARSE_INCOMPLETE; \
- } \
- m_ReceivedData.CheckValid(); \
- }
-
-
-
-
-
-enum
-{
- PACKET_UPDATE_TIME = 0x04,
- PACKET_PICKUP_SPAWN = 0x15,
- PACKET_SPAWN_OBJECT = 0x17,
- PACKET_ENTITY_METADATA = 0x28,
- PACKET_SOUND_PARTICLE_EFFECT = 0x3d
-} ;
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cProtocol142:
-
-cProtocol142::cProtocol142(cClientHandle * a_Client) :
- super(a_Client)
-{
-}
-
-
-
-
-
-int cProtocol142::ParseLocaleViewDistance(void)
-{
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale);
- HANDLE_PACKET_READ(ReadChar, char, ViewDistance);
- HANDLE_PACKET_READ(ReadChar, char, ChatFlags);
- HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty);
- HANDLE_PACKET_READ(ReadChar, char, ShouldShowCape); // <-- new in 1.4.2
- // TODO: m_Client->HandleLocale(Locale);
- // TODO: m_Client->HandleViewDistance(ViewDistance);
- // TODO: m_Client->HandleChatFlags(ChatFlags);
- // Ignoring client difficulty
- return PARSE_OK;
-}
-
-
-
-
-
-void cProtocol142::SendPickupSpawn(const cPickup & a_Pickup)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_PICKUP_SPAWN);
- WriteInt (a_Pickup.GetUniqueID());
- WriteItem (a_Pickup.GetItem());
- WriteVectorI((Vector3i)(a_Pickup.GetPosition() * 32));
- WriteByte ((char)(a_Pickup.GetSpeed().x * 8));
- WriteByte ((char)(a_Pickup.GetSpeed().y * 8));
- WriteByte ((char)(a_Pickup.GetSpeed().z * 8));
- Flush();
-}
-
-
-
-
-
-void cProtocol142::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_SOUND_PARTICLE_EFFECT);
- WriteInt (a_EffectID);
- WriteInt (a_SrcX / 8);
- WriteByte(a_SrcY / 8);
- WriteInt (a_SrcZ / 8);
- WriteInt (a_Data);
- WriteBool(0);
- Flush();
-}
-
-
-
-
-
-void cProtocol142::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_UPDATE_TIME);
- WriteInt64(a_WorldAge);
- WriteInt64(a_TimeOfDay);
- Flush();
-}
-
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cProtocol146:
-
-cProtocol146::cProtocol146(cClientHandle * a_Client) :
- super(a_Client)
-{
-}
-
-
-
-
-
-void cProtocol146::SendPickupSpawn(const cPickup & a_Pickup)
-{
- ASSERT(!a_Pickup.GetItem().IsEmpty());
-
- cCSLock Lock(m_CSPacket);
-
- // Send a SPAWN_OBJECT packet for the base entity:
- WriteByte(PACKET_SPAWN_OBJECT);
- WriteInt (a_Pickup.GetUniqueID());
- WriteByte(0x02);
- WriteInt ((int)(a_Pickup.GetPosX() * 32));
- WriteInt ((int)(a_Pickup.GetPosY() * 32));
- WriteInt ((int)(a_Pickup.GetPosZ() * 32));
- WriteInt (1);
- WriteShort((short)(a_Pickup.GetSpeed().x * 32));
- WriteShort((short)(a_Pickup.GetSpeed().y * 32));
- WriteShort((short)(a_Pickup.GetSpeed().z * 32));
- WriteByte(0);
- WriteByte(0);
-
- // Send a ENTITY_METADATA packet with the slot info:
- WriteByte(PACKET_ENTITY_METADATA);
- WriteInt(a_Pickup.GetUniqueID());
- WriteByte(0xaa); // a slot value at index 10
- WriteItem(a_Pickup.GetItem());
- WriteByte(0x7f); // End of metadata
- Flush();
-}
-
-
-
-
-
-void cProtocol146::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock)
-{
- // Send a spawn object / vehicle packet
- cCSLock Lock(m_CSPacket);
-
- WriteByte(PACKET_SPAWN_OBJECT);
- WriteInt (a_FallingBlock.GetUniqueID());
- WriteByte(70);
- WriteInt ((int)(a_FallingBlock.GetPosX() * 32));
- WriteInt ((int)(a_FallingBlock.GetPosY() * 32));
- WriteInt ((int)(a_FallingBlock.GetPosZ() * 32));
- WriteByte (0); // Pitch
- WriteByte (0); // Yaw
- WriteInt (a_FallingBlock.GetBlockType()); // data indicator = blocktype
- WriteShort((short)(a_FallingBlock.GetSpeedX() * 400));
- WriteShort((short)(a_FallingBlock.GetSpeedY() * 400));
- WriteShort((short)(a_FallingBlock.GetSpeedZ() * 400));
- Flush();
-}
-
-
-
-
-
-void cProtocol146::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_SPAWN_OBJECT);
- WriteInt (a_Entity.GetUniqueID());
- WriteByte(a_ObjectType);
- WriteInt ((int)(a_Entity.GetPosX() * 32));
- WriteInt ((int)(a_Entity.GetPosY() * 32));
- WriteInt ((int)(a_Entity.GetPosZ() * 32));
- WriteByte(a_Pitch);
- WriteByte(a_Yaw);
- WriteInt (a_ObjectData);
- if (a_ObjectData != 0)
- {
- WriteShort((short)(a_Entity.GetSpeedX() * 400));
- WriteShort((short)(a_Entity.GetSpeedY() * 400));
- WriteShort((short)(a_Entity.GetSpeedZ() * 400));
- }
- Flush();
-}
-
-
-
-
-
-void cProtocol146::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_SPAWN_OBJECT);
- WriteInt (a_Vehicle.GetUniqueID());
- WriteByte(a_VehicleType);
- WriteInt ((int)(a_Vehicle.GetPosX() * 32));
- WriteInt ((int)(a_Vehicle.GetPosY() * 32));
- WriteInt ((int)(a_Vehicle.GetPosZ() * 32));
- WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256));
- WriteByte ((Byte)((a_Vehicle.GetRotation() / 360.f) * 256));
- WriteInt (1);
- WriteShort((short)(a_Vehicle.GetSpeedX() * 400));
- WriteShort((short)(a_Vehicle.GetSpeedY() * 400));
- WriteShort((short)(a_Vehicle.GetSpeedZ() * 400));
- Flush();
-}
-
-
-
-
-
+
+// Protocol14x.cpp
+
+/*
+Implements the 1.4.x protocol classes representing these protocols:
+- cProtocol142:
+ - release 1.4.2 protocol (#47)
+ - release 1.4.4 protocol (#49) - the same protocol class is used, because the only difference is in a packet that MCServer doesn't implement yet (ITEM_DATA)
+ - release 1.4.5 protocol (same as 1.4.4)
+- cProtocol146:
+ - release 1.4.6 protocol (#51)
+*/
+
+#include "Globals.h"
+#include "Protocol14x.h"
+#include "../Root.h"
+#include "../Server.h"
+#include "../ClientHandle.h"
+#include "../../CryptoPP/randpool.h"
+#include "../Item.h"
+#include "ChunkDataSerializer.h"
+#include "../Player.h"
+#include "../Mobs/Monster.h"
+#include "../UI/Window.h"
+#include "../Pickup.h"
+#include "../FallingBlock.h"
+
+
+
+
+
+#define HANDLE_PACKET_READ(Proc, Type, Var) \
+ Type Var; \
+ { \
+ if (!m_ReceivedData.Proc(Var)) \
+ { \
+ m_ReceivedData.CheckValid(); \
+ return PARSE_INCOMPLETE; \
+ } \
+ m_ReceivedData.CheckValid(); \
+ }
+
+
+
+
+
+enum
+{
+ PACKET_UPDATE_TIME = 0x04,
+ PACKET_PICKUP_SPAWN = 0x15,
+ PACKET_SPAWN_OBJECT = 0x17,
+ PACKET_ENTITY_METADATA = 0x28,
+ PACKET_SOUND_PARTICLE_EFFECT = 0x3d
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cProtocol142:
+
+cProtocol142::cProtocol142(cClientHandle * a_Client) :
+ super(a_Client)
+{
+}
+
+
+
+
+
+int cProtocol142::ParseLocaleViewDistance(void)
+{
+ HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale);
+ HANDLE_PACKET_READ(ReadChar, char, ViewDistance);
+ HANDLE_PACKET_READ(ReadChar, char, ChatFlags);
+ HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty);
+ HANDLE_PACKET_READ(ReadChar, char, ShouldShowCape); // <-- new in 1.4.2
+ // TODO: m_Client->HandleLocale(Locale);
+ // TODO: m_Client->HandleViewDistance(ViewDistance);
+ // TODO: m_Client->HandleChatFlags(ChatFlags);
+ // Ignoring client difficulty
+ return PARSE_OK;
+}
+
+
+
+
+
+void cProtocol142::SendPickupSpawn(const cPickup & a_Pickup)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_PICKUP_SPAWN);
+ WriteInt (a_Pickup.GetUniqueID());
+ WriteItem (a_Pickup.GetItem());
+ WriteVectorI((Vector3i)(a_Pickup.GetPosition() * 32));
+ WriteByte ((char)(a_Pickup.GetSpeed().x * 8));
+ WriteByte ((char)(a_Pickup.GetSpeed().y * 8));
+ WriteByte ((char)(a_Pickup.GetSpeed().z * 8));
+ Flush();
+}
+
+
+
+
+
+void cProtocol142::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_SOUND_PARTICLE_EFFECT);
+ WriteInt (a_EffectID);
+ WriteInt (a_SrcX / 8);
+ WriteByte(a_SrcY / 8);
+ WriteInt (a_SrcZ / 8);
+ WriteInt (a_Data);
+ WriteBool(0);
+ Flush();
+}
+
+
+
+
+
+void cProtocol142::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_UPDATE_TIME);
+ WriteInt64(a_WorldAge);
+ WriteInt64(a_TimeOfDay);
+ Flush();
+}
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cProtocol146:
+
+cProtocol146::cProtocol146(cClientHandle * a_Client) :
+ super(a_Client)
+{
+}
+
+
+
+
+
+void cProtocol146::SendPickupSpawn(const cPickup & a_Pickup)
+{
+ ASSERT(!a_Pickup.GetItem().IsEmpty());
+
+ cCSLock Lock(m_CSPacket);
+
+ // Send a SPAWN_OBJECT packet for the base entity:
+ WriteByte(PACKET_SPAWN_OBJECT);
+ WriteInt (a_Pickup.GetUniqueID());
+ WriteByte(0x02);
+ WriteInt ((int)(a_Pickup.GetPosX() * 32));
+ WriteInt ((int)(a_Pickup.GetPosY() * 32));
+ WriteInt ((int)(a_Pickup.GetPosZ() * 32));
+ WriteInt (1);
+ WriteShort((short)(a_Pickup.GetSpeed().x * 32));
+ WriteShort((short)(a_Pickup.GetSpeed().y * 32));
+ WriteShort((short)(a_Pickup.GetSpeed().z * 32));
+ WriteByte(0);
+ WriteByte(0);
+
+ // Send a ENTITY_METADATA packet with the slot info:
+ WriteByte(PACKET_ENTITY_METADATA);
+ WriteInt(a_Pickup.GetUniqueID());
+ WriteByte(0xaa); // a slot value at index 10
+ WriteItem(a_Pickup.GetItem());
+ WriteByte(0x7f); // End of metadata
+ Flush();
+}
+
+
+
+
+
+void cProtocol146::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock)
+{
+ // Send a spawn object / vehicle packet
+ cCSLock Lock(m_CSPacket);
+
+ WriteByte(PACKET_SPAWN_OBJECT);
+ WriteInt (a_FallingBlock.GetUniqueID());
+ WriteByte(70);
+ WriteInt ((int)(a_FallingBlock.GetPosX() * 32));
+ WriteInt ((int)(a_FallingBlock.GetPosY() * 32));
+ WriteInt ((int)(a_FallingBlock.GetPosZ() * 32));
+ WriteByte (0); // Pitch
+ WriteByte (0); // Yaw
+ WriteInt (a_FallingBlock.GetBlockType()); // data indicator = blocktype
+ WriteShort((short)(a_FallingBlock.GetSpeedX() * 400));
+ WriteShort((short)(a_FallingBlock.GetSpeedY() * 400));
+ WriteShort((short)(a_FallingBlock.GetSpeedZ() * 400));
+ Flush();
+}
+
+
+
+
+
+void cProtocol146::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_SPAWN_OBJECT);
+ WriteInt (a_Entity.GetUniqueID());
+ WriteByte(a_ObjectType);
+ WriteInt ((int)(a_Entity.GetPosX() * 32));
+ WriteInt ((int)(a_Entity.GetPosY() * 32));
+ WriteInt ((int)(a_Entity.GetPosZ() * 32));
+ WriteByte(a_Pitch);
+ WriteByte(a_Yaw);
+ WriteInt (a_ObjectData);
+ if (a_ObjectData != 0)
+ {
+ WriteShort((short)(a_Entity.GetSpeedX() * 400));
+ WriteShort((short)(a_Entity.GetSpeedY() * 400));
+ WriteShort((short)(a_Entity.GetSpeedZ() * 400));
+ }
+ Flush();
+}
+
+
+
+
+
+void cProtocol146::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_SPAWN_OBJECT);
+ WriteInt (a_Vehicle.GetUniqueID());
+ WriteByte(a_VehicleType);
+ WriteInt ((int)(a_Vehicle.GetPosX() * 32));
+ WriteInt ((int)(a_Vehicle.GetPosY() * 32));
+ WriteInt ((int)(a_Vehicle.GetPosZ() * 32));
+ WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256));
+ WriteByte ((Byte)((a_Vehicle.GetRotation() / 360.f) * 256));
+ WriteInt (1);
+ WriteShort((short)(a_Vehicle.GetSpeedX() * 400));
+ WriteShort((short)(a_Vehicle.GetSpeedY() * 400));
+ WriteShort((short)(a_Vehicle.GetSpeedZ() * 400));
+ Flush();
+}
+
+
+
+
+
diff --git a/source/Protocol/Protocol14x.h b/source/Protocol/Protocol14x.h
index a9ff14d84..c3193a3e7 100644
--- a/source/Protocol/Protocol14x.h
+++ b/source/Protocol/Protocol14x.h
@@ -1,63 +1,63 @@
-
-// Protocol14x.h
-
-/*
-Interfaces to the 1.4.x protocol classes representing these protocols:
-- cProtocol142:
- - release 1.4.2 protocol (#47)
- - release 1.4.4 protocol (#49) - the same protocol class is used, because the only difference is in a packet that MCServer doesn't implement yet (ITEM_DATA)
- - release 1.4.5 protocol (same as 1.4.4)
-- cProtocol146:
- - release 1.4.6 protocol (#51)
-*/
-
-
-
-
-
-#pragma once
-
-#include "Protocol132.h"
-
-
-
-
-
-class cProtocol142 :
- public cProtocol132
-{
- typedef cProtocol132 super;
-
-public:
- cProtocol142(cClientHandle * a_Client);
-
- // Sending commands (alphabetically sorted):
- virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
- virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
- virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
-
- // Specific packet parsers:
- virtual int ParseLocaleViewDistance(void) override;
-} ;
-
-
-
-
-
-class cProtocol146 :
- public cProtocol142
-{
- typedef cProtocol142 super;
-
-public:
- cProtocol146(cClientHandle * a_Client);
-
- virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
- virtual void SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) override;
- virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
- virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType) override;
-} ;
-
-
-
-
+
+// Protocol14x.h
+
+/*
+Interfaces to the 1.4.x protocol classes representing these protocols:
+- cProtocol142:
+ - release 1.4.2 protocol (#47)
+ - release 1.4.4 protocol (#49) - the same protocol class is used, because the only difference is in a packet that MCServer doesn't implement yet (ITEM_DATA)
+ - release 1.4.5 protocol (same as 1.4.4)
+- cProtocol146:
+ - release 1.4.6 protocol (#51)
+*/
+
+
+
+
+
+#pragma once
+
+#include "Protocol132.h"
+
+
+
+
+
+class cProtocol142 :
+ public cProtocol132
+{
+ typedef cProtocol132 super;
+
+public:
+ cProtocol142(cClientHandle * a_Client);
+
+ // Sending commands (alphabetically sorted):
+ virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
+ virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
+ virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
+
+ // Specific packet parsers:
+ virtual int ParseLocaleViewDistance(void) override;
+} ;
+
+
+
+
+
+class cProtocol146 :
+ public cProtocol142
+{
+ typedef cProtocol142 super;
+
+public:
+ cProtocol146(cClientHandle * a_Client);
+
+ virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
+ virtual void SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) override;
+ virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
+ virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType) override;
+} ;
+
+
+
+
diff --git a/source/Protocol/Protocol15x.cpp b/source/Protocol/Protocol15x.cpp
index cb7fc9fb6..cbae0700e 100644
--- a/source/Protocol/Protocol15x.cpp
+++ b/source/Protocol/Protocol15x.cpp
@@ -1,137 +1,137 @@
-
-// Protocol15x.cpp
-
-/*
-Implements the 1.5.x protocol classes:
- - cProtocol150
- - release 1.5 protocol (#60)
- - release 1.5.2 protocol (#61, no relevant changes found)
-*/
-
-#include "Globals.h"
-#include "Protocol15x.h"
-#include "../ClientHandle.h"
-#include "../Item.h"
-
-
-
-
-
-#define HANDLE_PACKET_READ(Proc, Type, Var) \
- Type Var; \
- { \
- if (!m_ReceivedData.Proc(Var)) \
- { \
- m_ReceivedData.CheckValid(); \
- return PARSE_INCOMPLETE; \
- } \
- m_ReceivedData.CheckValid(); \
- }
-
-
-
-
-
-enum
-{
- PACKET_WINDOW_OPEN = 0x64,
-} ;
-
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cProtocol150:
-
-cProtocol150::cProtocol150(cClientHandle * a_Client) :
- super(a_Client)
-{
-}
-
-
-
-
-
-void cProtocol150::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots)
-{
- if (a_WindowType < 0)
- {
- // Do not send for inventory windows
- return;
- }
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_WINDOW_OPEN);
- WriteByte (a_WindowID);
- WriteByte (a_WindowType);
- WriteString(a_WindowTitle);
- WriteByte (a_NumSlots);
- WriteByte (1); // Use title
- Flush();
-}
-
-
-
-
-
-int cProtocol150::ParseWindowClick(void)
-{
- HANDLE_PACKET_READ(ReadChar, char, WindowID);
- HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
- HANDLE_PACKET_READ(ReadByte, Byte, Button);
- HANDLE_PACKET_READ(ReadBEShort, short, TransactionID);
- HANDLE_PACKET_READ(ReadByte, Byte, Mode);
- cItem HeldItem;
- int res = ParseItem(HeldItem);
- if (res < 0)
- {
- return res;
- }
-
- // Convert Button, Mode, SlotNum and HeldItem into eClickAction:
- eClickAction Action;
- switch ((Mode << 8) | Button)
- {
- case 0x0000: Action = (SlotNum != -999) ? caLeftClick : caLeftClickOutside; break;
- case 0x0001: Action = (SlotNum != -999) ? caRightClick : caRightClickOutside; break;
- case 0x0100: Action = caShiftLeftClick; break;
- case 0x0101: Action = caShiftRightClick; break;
- case 0x0200: Action = caNumber1; break;
- case 0x0201: Action = caNumber2; break;
- case 0x0202: Action = caNumber3; break;
- case 0x0203: Action = caNumber4; break;
- case 0x0204: Action = caNumber5; break;
- case 0x0205: Action = caNumber6; break;
- case 0x0206: Action = caNumber7; break;
- case 0x0207: Action = caNumber8; break;
- case 0x0208: Action = caNumber9; break;
- case 0x0300: Action = caMiddleClick; break;
- case 0x0400: Action = (SlotNum == -999) ? caLeftClickOutsideHoldNothing : caDropKey; break;
- case 0x0401: Action = (SlotNum == -999) ? caRightClickOutsideHoldNothing : caCtrlDropKey; break;
- case 0x0500: Action = (SlotNum == -999) ? caLeftPaintBegin : caUnknown; break;
- case 0x0501: Action = (SlotNum != -999) ? caLeftPaintProgress : caUnknown; break;
- case 0x0502: Action = (SlotNum == -999) ? caLeftPaintEnd : caUnknown; break;
- case 0x0504: Action = (SlotNum == -999) ? caRightPaintBegin : caUnknown; break;
- case 0x0505: Action = (SlotNum != -999) ? caRightPaintProgress : caUnknown; break;
- case 0x0506: Action = (SlotNum == -999) ? caRightPaintEnd : caUnknown; break;
- case 0x0600: Action = caDblClick; break;
- }
-
- if (Action == caUnknown)
- {
- LOGWARNING("Received an unknown click action combination: Mode = %d, Button = %d, Slot = %d, HeldItem = %s. Ignoring packet.",
- Mode, Button, SlotNum, ItemToFullString(HeldItem).c_str()
- );
- ASSERT(!"Unknown click action");
- return PARSE_OK;
- }
-
- m_Client->HandleWindowClick(WindowID, SlotNum, Action, HeldItem);
- return PARSE_OK;
-}
-
-
-
-
-
+
+// Protocol15x.cpp
+
+/*
+Implements the 1.5.x protocol classes:
+ - cProtocol150
+ - release 1.5 protocol (#60)
+ - release 1.5.2 protocol (#61, no relevant changes found)
+*/
+
+#include "Globals.h"
+#include "Protocol15x.h"
+#include "../ClientHandle.h"
+#include "../Item.h"
+
+
+
+
+
+#define HANDLE_PACKET_READ(Proc, Type, Var) \
+ Type Var; \
+ { \
+ if (!m_ReceivedData.Proc(Var)) \
+ { \
+ m_ReceivedData.CheckValid(); \
+ return PARSE_INCOMPLETE; \
+ } \
+ m_ReceivedData.CheckValid(); \
+ }
+
+
+
+
+
+enum
+{
+ PACKET_WINDOW_OPEN = 0x64,
+} ;
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cProtocol150:
+
+cProtocol150::cProtocol150(cClientHandle * a_Client) :
+ super(a_Client)
+{
+}
+
+
+
+
+
+void cProtocol150::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots)
+{
+ if (a_WindowType < 0)
+ {
+ // Do not send for inventory windows
+ return;
+ }
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_WINDOW_OPEN);
+ WriteByte (a_WindowID);
+ WriteByte (a_WindowType);
+ WriteString(a_WindowTitle);
+ WriteByte (a_NumSlots);
+ WriteByte (1); // Use title
+ Flush();
+}
+
+
+
+
+
+int cProtocol150::ParseWindowClick(void)
+{
+ HANDLE_PACKET_READ(ReadChar, char, WindowID);
+ HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
+ HANDLE_PACKET_READ(ReadByte, Byte, Button);
+ HANDLE_PACKET_READ(ReadBEShort, short, TransactionID);
+ HANDLE_PACKET_READ(ReadByte, Byte, Mode);
+ cItem HeldItem;
+ int res = ParseItem(HeldItem);
+ if (res < 0)
+ {
+ return res;
+ }
+
+ // Convert Button, Mode, SlotNum and HeldItem into eClickAction:
+ eClickAction Action;
+ switch ((Mode << 8) | Button)
+ {
+ case 0x0000: Action = (SlotNum != -999) ? caLeftClick : caLeftClickOutside; break;
+ case 0x0001: Action = (SlotNum != -999) ? caRightClick : caRightClickOutside; break;
+ case 0x0100: Action = caShiftLeftClick; break;
+ case 0x0101: Action = caShiftRightClick; break;
+ case 0x0200: Action = caNumber1; break;
+ case 0x0201: Action = caNumber2; break;
+ case 0x0202: Action = caNumber3; break;
+ case 0x0203: Action = caNumber4; break;
+ case 0x0204: Action = caNumber5; break;
+ case 0x0205: Action = caNumber6; break;
+ case 0x0206: Action = caNumber7; break;
+ case 0x0207: Action = caNumber8; break;
+ case 0x0208: Action = caNumber9; break;
+ case 0x0300: Action = caMiddleClick; break;
+ case 0x0400: Action = (SlotNum == -999) ? caLeftClickOutsideHoldNothing : caDropKey; break;
+ case 0x0401: Action = (SlotNum == -999) ? caRightClickOutsideHoldNothing : caCtrlDropKey; break;
+ case 0x0500: Action = (SlotNum == -999) ? caLeftPaintBegin : caUnknown; break;
+ case 0x0501: Action = (SlotNum != -999) ? caLeftPaintProgress : caUnknown; break;
+ case 0x0502: Action = (SlotNum == -999) ? caLeftPaintEnd : caUnknown; break;
+ case 0x0504: Action = (SlotNum == -999) ? caRightPaintBegin : caUnknown; break;
+ case 0x0505: Action = (SlotNum != -999) ? caRightPaintProgress : caUnknown; break;
+ case 0x0506: Action = (SlotNum == -999) ? caRightPaintEnd : caUnknown; break;
+ case 0x0600: Action = caDblClick; break;
+ }
+
+ if (Action == caUnknown)
+ {
+ LOGWARNING("Received an unknown click action combination: Mode = %d, Button = %d, Slot = %d, HeldItem = %s. Ignoring packet.",
+ Mode, Button, SlotNum, ItemToFullString(HeldItem).c_str()
+ );
+ ASSERT(!"Unknown click action");
+ return PARSE_OK;
+ }
+
+ m_Client->HandleWindowClick(WindowID, SlotNum, Action, HeldItem);
+ return PARSE_OK;
+}
+
+
+
+
+
diff --git a/source/Protocol/Protocol15x.h b/source/Protocol/Protocol15x.h
index 73d73780d..3e1547df8 100644
--- a/source/Protocol/Protocol15x.h
+++ b/source/Protocol/Protocol15x.h
@@ -1,38 +1,38 @@
-
-// Protocol15x.h
-
-/*
-Declares the 1.5.x protocol classes:
- - cProtocol150
- - release 1.5 and 1.5.1 protocol (#60)
- - release 1.5.2 protocol (#61; no relevant changes found)
-*/
-
-
-
-
-
-#pragma once
-
-#include "Protocol14x.h"
-
-
-
-
-
-class cProtocol150 :
- public cProtocol146
-{
- typedef cProtocol146 super;
-
-public:
- cProtocol150(cClientHandle * a_Client);
-
- virtual void SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) override;
-
- virtual int ParseWindowClick(void);
-} ;
-
-
-
-
+
+// Protocol15x.h
+
+/*
+Declares the 1.5.x protocol classes:
+ - cProtocol150
+ - release 1.5 and 1.5.1 protocol (#60)
+ - release 1.5.2 protocol (#61; no relevant changes found)
+*/
+
+
+
+
+
+#pragma once
+
+#include "Protocol14x.h"
+
+
+
+
+
+class cProtocol150 :
+ public cProtocol146
+{
+ typedef cProtocol146 super;
+
+public:
+ cProtocol150(cClientHandle * a_Client);
+
+ virtual void SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) override;
+
+ virtual int ParseWindowClick(void);
+} ;
+
+
+
+
diff --git a/source/Protocol/Protocol16x.cpp b/source/Protocol/Protocol16x.cpp
index d06980228..4e8fd1887 100644
--- a/source/Protocol/Protocol16x.cpp
+++ b/source/Protocol/Protocol16x.cpp
@@ -1,245 +1,262 @@
-
-// Protocol16x.cpp
-
-/*
-Implements the 1.6.x protocol classes:
- - cProtocol161
- - release 1.6.1 protocol (#73)
- - cProtocol162
- - release 1.6.2 protocol (#74)
-(others may be added later in the future for the 1.6 release series)
-*/
-
-#include "Globals.h"
-#include "Protocol16x.h"
-#include "../ClientHandle.h"
-#include "../Entity.h"
-#include "../Player.h"
-
-
-
-
-
-#define HANDLE_PACKET_READ(Proc, Type, Var) \
- Type Var; \
- { \
- if (!m_ReceivedData.Proc(Var)) \
- { \
- m_ReceivedData.CheckValid(); \
- return PARSE_INCOMPLETE; \
- } \
- m_ReceivedData.CheckValid(); \
- }
-
-
-
-
-
-enum
-{
- PACKET_CHAT = 0x03,
- PACKET_UPDATE_HEALTH = 0x08,
- PACKET_STEER_VEHICLE = 0x1b,
- PACKET_ATTACH_ENTITY = 0x27,
- PACKET_ENTITY_PROPERTIES = 0x2c,
- PACKET_WINDOW_OPEN = 0x64,
- PACKET_PLAYER_ABILITIES = 0xca,
-} ;
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cProtocol161:
-
-cProtocol161::cProtocol161(cClientHandle * a_Client) :
- super(a_Client)
-{
-}
-
-
-
-
-
-void cProtocol161::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_ATTACH_ENTITY);
- WriteInt(a_Entity.GetUniqueID());
- WriteInt((a_Vehicle == NULL) ? -1 : a_Vehicle->GetUniqueID());
- WriteBool(false); // TODO: "Should use leash?" -> no
- Flush();
-}
-
-
-
-
-
-void cProtocol161::SendChat(const AString & a_Message)
-{
- super::SendChat(Printf("{\"text\":\"%s\"}", EscapeString(a_Message).c_str()));
-}
-
-
-
-
-void cProtocol161::SendGameMode(eGameMode a_GameMode)
-{
- super::SendGameMode(a_GameMode);
- SendPlayerMaxSpeed();
-}
-
-
-
-
-
-void cProtocol161::SendHealth(void)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_UPDATE_HEALTH);
- WriteFloat((float)m_Client->GetPlayer()->GetHealth());
- WriteShort(m_Client->GetPlayer()->GetFoodLevel());
- WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel());
- Flush();
-}
-
-
-
-
-
-void cProtocol161::SendPlayerMaxSpeed(void)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_ENTITY_PROPERTIES);
- WriteInt(m_Client->GetPlayer()->GetUniqueID());
- WriteInt(1);
- WriteString("generic.movementSpeed");
- WriteDouble(m_Client->GetPlayer()->GetMaxSpeed());
- Flush();
-}
-
-
-
-
-
-void cProtocol161::SendRespawn(void)
-{
- // Besides sending the respawn, we need to also send the player max speed, otherwise the client reverts to super-fast
- super::SendRespawn();
- SendPlayerMaxSpeed();
-}
-
-
-
-
-
-void cProtocol161::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots)
-{
- if (a_WindowType < 0)
- {
- // Do not send for inventory windows
- return;
- }
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_WINDOW_OPEN);
- WriteByte (a_WindowID);
- WriteByte (a_WindowType);
- WriteString(a_WindowTitle);
- WriteByte (a_NumSlots);
- WriteByte (1); // Use title
- if (a_WindowType == 11) // horse / donkey
- {
- WriteInt(0); // Unknown value sent only when window type is 11 (horse / donkey)
- }
- Flush();
-}
-
-
-
-
-
-int cProtocol161::ParseEntityAction(void)
-{
- HANDLE_PACKET_READ(ReadBEInt, int, EntityID);
- HANDLE_PACKET_READ(ReadChar, char, ActionID);
- HANDLE_PACKET_READ(ReadBEInt, int, UnknownHorseVal);
- m_Client->HandleEntityAction(EntityID, ActionID);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol161::ParsePlayerAbilities(void)
-{
- HANDLE_PACKET_READ(ReadByte, Byte, Flags);
- HANDLE_PACKET_READ(ReadBEFloat, float, FlyingSpeed);
- HANDLE_PACKET_READ(ReadBEFloat, float, WalkingSpeed);
- // TODO: m_Client->HandlePlayerAbilities(...);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol161::ParseSteerVehicle(void)
-{
- HANDLE_PACKET_READ(ReadBEFloat, float, Sideways);
- HANDLE_PACKET_READ(ReadBEFloat, float, Forward);
- HANDLE_PACKET_READ(ReadBool, bool, Jump);
- HANDLE_PACKET_READ(ReadBool, bool, Unmount);
- // TODO: m_Client->HandleSteerVehicle(...);
- if (Unmount)
- {
- m_Client->HandleUnmount();
- }
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol161::ParsePacket(unsigned char a_PacketType)
-{
- switch (a_PacketType)
- {
- case PACKET_STEER_VEHICLE: return ParseSteerVehicle();
- default: return super::ParsePacket(a_PacketType);
- }
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cProtocol162:
-
-cProtocol162::cProtocol162(cClientHandle * a_Client) :
- super(a_Client)
-{
-}
-
-
-
-
-
-void cProtocol162::SendPlayerMaxSpeed(void)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_ENTITY_PROPERTIES);
- WriteInt(m_Client->GetPlayer()->GetUniqueID());
- WriteInt(1);
- WriteString("generic.movementSpeed");
- WriteDouble(m_Client->GetPlayer()->GetMaxSpeed());
- WriteShort(0);
- Flush();
-}
-
-
-
-
+
+// Protocol16x.cpp
+
+/*
+Implements the 1.6.x protocol classes:
+ - cProtocol161
+ - release 1.6.1 protocol (#73)
+ - cProtocol162
+ - release 1.6.2 protocol (#74)
+(others may be added later in the future for the 1.6 release series)
+*/
+
+#include "Globals.h"
+#include "Protocol16x.h"
+#include "../ClientHandle.h"
+#include "../Entity.h"
+#include "../Player.h"
+
+
+
+
+
+#define HANDLE_PACKET_READ(Proc, Type, Var) \
+ Type Var; \
+ { \
+ if (!m_ReceivedData.Proc(Var)) \
+ { \
+ m_ReceivedData.CheckValid(); \
+ return PARSE_INCOMPLETE; \
+ } \
+ m_ReceivedData.CheckValid(); \
+ }
+
+
+
+
+
+enum
+{
+ PACKET_CHAT = 0x03,
+ PACKET_UPDATE_HEALTH = 0x08,
+ PACKET_STEER_VEHICLE = 0x1b,
+ PACKET_ATTACH_ENTITY = 0x27,
+ PACKET_ENTITY_PROPERTIES = 0x2c,
+ PACKET_WINDOW_OPEN = 0x64,
+ PACKET_TILE_EDITOR_OPEN = 0x85,
+ PACKET_PLAYER_ABILITIES = 0xca,
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cProtocol161:
+
+cProtocol161::cProtocol161(cClientHandle * a_Client) :
+ super(a_Client)
+{
+}
+
+
+
+
+
+void cProtocol161::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ATTACH_ENTITY);
+ WriteInt(a_Entity.GetUniqueID());
+ WriteInt((a_Vehicle == NULL) ? -1 : a_Vehicle->GetUniqueID());
+ WriteBool(false); // TODO: "Should use leash?" -> no
+ Flush();
+}
+
+
+
+
+
+void cProtocol161::SendChat(const AString & a_Message)
+{
+ super::SendChat(Printf("{\"text\":\"%s\"}", EscapeString(a_Message).c_str()));
+}
+
+
+
+
+
+void cProtocol161::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_TILE_EDITOR_OPEN);
+ WriteByte(0);
+ WriteInt(a_BlockX);
+ WriteInt(a_BlockY);
+ WriteInt(a_BlockZ);
+ Flush();
+}
+
+
+
+
+
+void cProtocol161::SendGameMode(eGameMode a_GameMode)
+{
+ super::SendGameMode(a_GameMode);
+ SendPlayerMaxSpeed();
+}
+
+
+
+
+
+void cProtocol161::SendHealth(void)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_UPDATE_HEALTH);
+ WriteFloat((float)m_Client->GetPlayer()->GetHealth());
+ WriteShort(m_Client->GetPlayer()->GetFoodLevel());
+ WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel());
+ Flush();
+}
+
+
+
+
+
+void cProtocol161::SendPlayerMaxSpeed(void)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ENTITY_PROPERTIES);
+ WriteInt(m_Client->GetPlayer()->GetUniqueID());
+ WriteInt(1);
+ WriteString("generic.movementSpeed");
+ WriteDouble(m_Client->GetPlayer()->GetMaxSpeed());
+ Flush();
+}
+
+
+
+
+
+void cProtocol161::SendRespawn(void)
+{
+ // Besides sending the respawn, we need to also send the player max speed, otherwise the client reverts to super-fast
+ super::SendRespawn();
+ SendPlayerMaxSpeed();
+}
+
+
+
+
+
+void cProtocol161::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots)
+{
+ if (a_WindowType < 0)
+ {
+ // Do not send for inventory windows
+ return;
+ }
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_WINDOW_OPEN);
+ WriteByte (a_WindowID);
+ WriteByte (a_WindowType);
+ WriteString(a_WindowTitle);
+ WriteByte (a_NumSlots);
+ WriteByte (1); // Use title
+ if (a_WindowType == 11) // horse / donkey
+ {
+ WriteInt(0); // Unknown value sent only when window type is 11 (horse / donkey)
+ }
+ Flush();
+}
+
+
+
+
+
+int cProtocol161::ParseEntityAction(void)
+{
+ HANDLE_PACKET_READ(ReadBEInt, int, EntityID);
+ HANDLE_PACKET_READ(ReadChar, char, ActionID);
+ HANDLE_PACKET_READ(ReadBEInt, int, UnknownHorseVal);
+ m_Client->HandleEntityAction(EntityID, ActionID);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol161::ParsePlayerAbilities(void)
+{
+ HANDLE_PACKET_READ(ReadByte, Byte, Flags);
+ HANDLE_PACKET_READ(ReadBEFloat, float, FlyingSpeed);
+ HANDLE_PACKET_READ(ReadBEFloat, float, WalkingSpeed);
+ // TODO: m_Client->HandlePlayerAbilities(...);
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol161::ParseSteerVehicle(void)
+{
+ HANDLE_PACKET_READ(ReadBEFloat, float, Sideways);
+ HANDLE_PACKET_READ(ReadBEFloat, float, Forward);
+ HANDLE_PACKET_READ(ReadBool, bool, Jump);
+ HANDLE_PACKET_READ(ReadBool, bool, Unmount);
+ // TODO: m_Client->HandleSteerVehicle(...);
+ if (Unmount)
+ {
+ m_Client->HandleUnmount();
+ }
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol161::ParsePacket(unsigned char a_PacketType)
+{
+ switch (a_PacketType)
+ {
+ case PACKET_STEER_VEHICLE: return ParseSteerVehicle();
+ default: return super::ParsePacket(a_PacketType);
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cProtocol162:
+
+cProtocol162::cProtocol162(cClientHandle * a_Client) :
+ super(a_Client)
+{
+}
+
+
+
+
+
+void cProtocol162::SendPlayerMaxSpeed(void)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_ENTITY_PROPERTIES);
+ WriteInt(m_Client->GetPlayer()->GetUniqueID());
+ WriteInt(1);
+ WriteString("generic.movementSpeed");
+ WriteDouble(m_Client->GetPlayer()->GetMaxSpeed());
+ WriteShort(0);
+ Flush();
+}
+
+
+
+
diff --git a/source/Protocol/Protocol16x.h b/source/Protocol/Protocol16x.h
index e012585d1..077c7958b 100644
--- a/source/Protocol/Protocol16x.h
+++ b/source/Protocol/Protocol16x.h
@@ -1,73 +1,74 @@
-
-// Protocol16x.h
-
-/*
-Declares the 1.6.x protocol classes:
- - cProtocol161
- - release 1.6.1 protocol (#73)
- - cProtocol162
- - release 1.6.2 protocol (#74)
-(others may be added later in the future for the 1.6 release series)
-*/
-
-
-
-
-
-#pragma once
-
-#include "Protocol15x.h"
-
-
-
-
-
-class cProtocol161 :
- public cProtocol150
-{
- typedef cProtocol150 super;
-
-public:
- cProtocol161(cClientHandle * a_Client);
-
-protected:
-
- // cProtocol150 overrides:
- virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
- virtual void SendChat (const AString & a_Message) override;
- virtual void SendGameMode (eGameMode a_GameMode) override;
- virtual void SendHealth (void) override;
- virtual void SendPlayerMaxSpeed(void) override;
- virtual void SendRespawn (void) override;
- virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) override;
-
- virtual int ParseEntityAction (void) override;
- virtual int ParsePlayerAbilities(void) override;
-
- // New packets:
- virtual int ParseSteerVehicle(void);
-
- // Enable new packets' handling
- virtual int ParsePacket(unsigned char a_PacketType) override;
-} ;
-
-
-
-
-
-class cProtocol162 :
- public cProtocol161
-{
- typedef cProtocol161 super;
-
-public:
- cProtocol162(cClientHandle * a_Client);
-
-protected:
- // cProtocol161 overrides:
- virtual void SendPlayerMaxSpeed(void) override;
-} ;
-
-
-
-
+
+// Protocol16x.h
+
+/*
+Declares the 1.6.x protocol classes:
+ - cProtocol161
+ - release 1.6.1 protocol (#73)
+ - cProtocol162
+ - release 1.6.2 protocol (#74)
+(others may be added later in the future for the 1.6 release series)
+*/
+
+
+
+
+
+#pragma once
+
+#include "Protocol15x.h"
+
+
+
+
+
+class cProtocol161 :
+ public cProtocol150
+{
+ typedef cProtocol150 super;
+
+public:
+ cProtocol161(cClientHandle * a_Client);
+
+protected:
+
+ // cProtocol150 overrides:
+ virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
+ virtual void SendChat (const AString & a_Message) override;
+ virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)
+ virtual void SendGameMode (eGameMode a_GameMode) override;
+ virtual void SendHealth (void) override;
+ virtual void SendPlayerMaxSpeed(void) override;
+ virtual void SendRespawn (void) override;
+ virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) override;
+
+ virtual int ParseEntityAction (void) override;
+ virtual int ParsePlayerAbilities(void) override;
+
+ // New packets:
+ virtual int ParseSteerVehicle(void);
+
+ // Enable new packets' handling
+ virtual int ParsePacket(unsigned char a_PacketType) override;
+} ;
+
+
+
+
+
+class cProtocol162 :
+ public cProtocol161
+{
+ typedef cProtocol161 super;
+
+public:
+ cProtocol162(cClientHandle * a_Client);
+
+protected:
+ // cProtocol161 overrides:
+ virtual void SendPlayerMaxSpeed(void) override;
+} ;
+
+
+
+
diff --git a/source/Protocol/ProtocolRecognizer.cpp b/source/Protocol/ProtocolRecognizer.cpp
index ec10eeb12..290f2df75 100644
--- a/source/Protocol/ProtocolRecognizer.cpp
+++ b/source/Protocol/ProtocolRecognizer.cpp
@@ -1,771 +1,791 @@
-
-// ProtocolRecognizer.cpp
-
-// Implements the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple
-// protocol versions and redirects everything to them
-
-#include "Globals.h"
-
-#include "ProtocolRecognizer.h"
-#include "Protocol125.h"
-#include "Protocol132.h"
-#include "Protocol14x.h"
-#include "Protocol15x.h"
-#include "Protocol16x.h"
-#include "../ClientHandle.h"
-#include "../Root.h"
-#include "../World.h"
-#include "../ChatColor.h"
-
-
-
-
-
-cProtocolRecognizer::cProtocolRecognizer(cClientHandle * a_Client) :
- super(a_Client),
- m_Protocol(NULL),
- m_Buffer(512)
-{
-}
-
-
-
-
-
-cProtocolRecognizer::~cProtocolRecognizer()
-{
- delete m_Protocol;
-}
-
-
-
-
-
-AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion)
-{
- switch (a_ProtocolVersion)
- {
- case PROTO_VERSION_1_2_5: return "1.2.5";
- case PROTO_VERSION_1_3_2: return "1.3.2";
- case PROTO_VERSION_1_4_2: return "1.4.2";
- case PROTO_VERSION_1_4_4: return "1.4.4";
- case PROTO_VERSION_1_4_6: return "1.4.6";
- case PROTO_VERSION_1_5_0: return "1.5";
- case PROTO_VERSION_1_5_2: return "1.5.2";
- case PROTO_VERSION_1_6_1: return "1.6.1";
- case PROTO_VERSION_1_6_2: return "1.6.2";
- }
- ASSERT(!"Unknown protocol version");
- return Printf("Unknown protocol (%d)", a_ProtocolVersion);
-}
-
-
-
-
-
-void cProtocolRecognizer::DataReceived(const char * a_Data, int a_Size)
-{
- if (m_Protocol == NULL)
- {
- if (!m_Buffer.Write(a_Data, a_Size))
- {
- m_Client->Kick("Unsupported protocol version");
- return;
- }
-
- if (!TryRecognizeProtocol())
- {
- return;
- }
-
- // The protocol has just been recognized, dump the whole m_Buffer contents into it for parsing:
- AString Dump;
- m_Buffer.ResetRead();
- m_Buffer.ReadAll(Dump);
- m_Protocol->DataReceived(Dump.data(), Dump.size());
- }
- else
- {
- m_Protocol->DataReceived(a_Data, a_Size);
- }
-}
-
-
-
-
-
-void cProtocolRecognizer::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendAttachEntity(a_Entity, a_Vehicle);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendBlockBreakAnim(a_entityID, a_BlockX, a_BlockY, a_BlockZ, stage);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendBlockChange(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendChat(const AString & a_Message)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendChat(a_Message);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendCollectPickup(a_Pickup, a_Player);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendDestroyEntity(const cEntity & a_Entity)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendDestroyEntity(a_Entity);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendDisconnect(const AString & a_Reason)
-{
- if (m_Protocol != NULL)
- {
- m_Protocol->SendDisconnect(a_Reason);
- }
- else
- {
- // This is used when the client sends a server-ping, respond with the default packet:
- WriteByte ((char)0xff); // PACKET_DISCONNECT
- WriteString(a_Reason);
- }
-}
-
-
-
-
-void cProtocolRecognizer::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendEntityHeadLook(const cEntity & a_Entity)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendEntityHeadLook(a_Entity);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendEntityLook(const cEntity & a_Entity)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendEntityLook(a_Entity);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendEntityMetadata(const cEntity & a_Entity)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendEntityMetadata(a_Entity);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendEntityProperties(const cEntity & a_Entity)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendEntityProperties(a_Entity);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendEntityStatus(const cEntity & a_Entity, char a_Status)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendEntityStatus(a_Entity, a_Status);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendEntityVelocity(const cEntity & a_Entity)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendEntityVelocity(a_Entity);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendExplosion(a_BlockX,a_BlockY,a_BlockZ,a_Radius, a_BlocksAffected, a_PlayerMotion);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendGameMode(eGameMode a_GameMode)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendGameMode(a_GameMode);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendHealth(void)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendHealth();
-}
-
-
-
-
-
-void cProtocolRecognizer::SendInventoryProgress(char a_WindowID, short a_Progressbar, short a_Value)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendInventoryProgress(a_WindowID, a_Progressbar, a_Value);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendKeepAlive(int a_PingID)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendKeepAlive(a_PingID);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendLogin(a_Player, a_World);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendPickupSpawn(const cPickup & a_Pickup)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendPickupSpawn(a_Pickup);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendPlayerAnimation(a_Player, a_Animation);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendPlayerListItem(a_Player, a_IsOnline);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendPlayerMaxSpeed(void)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendPlayerMaxSpeed();
-}
-
-
-
-
-
-void cProtocolRecognizer::SendPlayerMoveLook(void)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendPlayerMoveLook();
-}
-
-
-
-
-
-void cProtocolRecognizer::SendPlayerPosition(void)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendPlayerPosition();
-}
-
-
-
-
-
-void cProtocolRecognizer::SendPlayerSpawn(const cPlayer & a_Player)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendPlayerSpawn(a_Player);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendRespawn(void)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendRespawn();
-}
-
-
-
-
-
-void cProtocolRecognizer::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendSpawnFallingBlock(a_FallingBlock);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendSpawnMob(const cMonster & a_Mob)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendSpawnMob(a_Mob);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendSpawnObject(a_Entity, a_ObjectType, a_ObjectData, a_Yaw, a_Pitch);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendSpawnVehicle(a_Vehicle, a_VehicleType);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendTeleportEntity(const cEntity & a_Entity)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendTeleportEntity(a_Entity);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendTimeUpdate(a_WorldAge, a_TimeOfDay);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendUnloadChunk(a_ChunkX, a_ChunkZ);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendUpdateSign(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ )
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendWeather(eWeather a_Weather)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendWeather(a_Weather);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendWholeInventory(const cInventory & a_Inventory)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendWholeInventory(a_Inventory);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendWholeInventory(const cWindow & a_Window)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendWholeInventory(a_Window);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendWindowClose(const cWindow & a_Window)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendWindowClose(a_Window);
-}
-
-
-
-
-
-void cProtocolRecognizer::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots)
-{
- ASSERT(m_Protocol != NULL);
- m_Protocol->SendWindowOpen(a_WindowID, a_WindowType, a_WindowTitle, a_NumSlots);
-}
-
-
-
-
-
-AString cProtocolRecognizer::GetAuthServerID(void)
-{
- ASSERT(m_Protocol != NULL);
- return m_Protocol->GetAuthServerID();
-}
-
-
-
-
-
-void cProtocolRecognizer::SendData(const char * a_Data, int a_Size)
-{
- // This is used only when handling the server ping
- m_Client->SendData(a_Data, a_Size);
-}
-
-
-
-
-
-bool cProtocolRecognizer::TryRecognizeProtocol(void)
-{
- // NOTE: If a new protocol is added or an old one is removed, adjust MCS_CLIENT_VERSIONS and
- // MCS_PROTOCOL_VERSIONS macros in the header file, as well as PROTO_VERSION_LATEST macro
-
- // The first packet should be a Handshake, 0x02:
- unsigned char PacketType;
- if (!m_Buffer.ReadByte(PacketType))
- {
- return false;
- }
- switch (PacketType)
- {
- case 0x02: break; // Handshake, continue recognizing
- case 0xfe: HandleServerPing(); return false;
- default: return false;
- }
-
- // 1.3.2 starts with 0x02 0x39 <name-length-short>
- // 1.2.5 starts with 0x02 <name-length-short> and name is expected to less than 0x3900 long :)
- char ch;
- if (!m_Buffer.ReadChar(ch))
- {
- return false;
- }
- switch (ch)
- {
- case PROTO_VERSION_1_3_2:
- {
- m_Protocol = new cProtocol132(m_Client);
- return true;
- }
- case PROTO_VERSION_1_4_2:
- case PROTO_VERSION_1_4_4:
- {
- m_Protocol = new cProtocol142(m_Client);
- return true;
- }
- case PROTO_VERSION_1_4_6:
- {
- m_Protocol = new cProtocol146(m_Client);
- return true;
- }
- case PROTO_VERSION_1_5_0:
- case PROTO_VERSION_1_5_2:
- {
- m_Protocol = new cProtocol150(m_Client);
- return true;
- }
- case PROTO_VERSION_1_6_1:
- {
- m_Protocol = new cProtocol161(m_Client);
- return true;
- }
- case PROTO_VERSION_1_6_2:
- {
- m_Protocol = new cProtocol162(m_Client);
- return true;
- }
- }
- m_Protocol = new cProtocol125(m_Client);
- return true;
-}
-
-
-
-
-
-void cProtocolRecognizer::HandleServerPing(void)
-{
- AString Reply;
- switch (cRoot::Get()->m_PrimaryServerVersion)
- {
- case PROTO_VERSION_1_2_5:
- case PROTO_VERSION_1_3_2:
- {
- // http://wiki.vg/wiki/index.php?title=Protocol&oldid=3099#Server_List_Ping_.280xFE.29
- Printf(Reply, "%s%s%i%s%i",
- cRoot::Get()->GetDefaultWorld()->GetDescription().c_str(),
- cChatColor::Delimiter.c_str(),
- cRoot::Get()->GetDefaultWorld()->GetNumPlayers(),
- cChatColor::Delimiter.c_str(),
- cRoot::Get()->GetDefaultWorld()->GetMaxPlayers()
- );
- break;
- }
-
- case PROTO_VERSION_1_4_2:
- case PROTO_VERSION_1_4_4:
- case PROTO_VERSION_1_4_6:
- case PROTO_VERSION_1_5_0:
- case PROTO_VERSION_1_5_2:
- case PROTO_VERSION_1_6_1:
- case PROTO_VERSION_1_6_2:
- {
- // The server list ping now has 1 more byte of "magic". Mojang just loves to complicate stuff.
- // http://wiki.vg/wiki/index.php?title=Protocol&oldid=3101#Server_List_Ping_.280xFE.29
- // _X 2012_10_31: I know that this needn't eat the byte, since it still may be in transit.
- // Who cares? We're disconnecting anyway.
- if (m_Buffer.CanReadBytes(1))
- {
- byte val;
- m_Buffer.ReadByte(val);
- ASSERT(val == 0x01);
- }
-
- // http://wiki.vg/wiki/index.php?title=Server_List_Ping&oldid=3100
- AString NumPlayers;
- Printf(NumPlayers, "%d", cRoot::Get()->GetDefaultWorld()->GetNumPlayers());
- AString MaxPlayers;
- Printf(MaxPlayers, "%d", cRoot::Get()->GetDefaultWorld()->GetMaxPlayers());
-
- AString ProtocolVersionNum;
- Printf(ProtocolVersionNum, "%d", cRoot::Get()->m_PrimaryServerVersion);
- AString ProtocolVersionTxt(GetVersionTextFromInt(cRoot::Get()->m_PrimaryServerVersion));
-
- // Cannot use Printf() because of in-string NUL bytes.
- Reply = cChatColor::Delimiter;
- Reply.append("1");
- Reply.push_back(0);
- Reply.append(ProtocolVersionNum);
- Reply.push_back(0);
- Reply.append(ProtocolVersionTxt);
- Reply.push_back(0);
- Reply.append(cRoot::Get()->GetDefaultWorld()->GetDescription());
- Reply.push_back(0);
- Reply.append(NumPlayers);
- Reply.push_back(0);
- Reply.append(MaxPlayers);
- break;
- }
- } // switch (m_PrimaryServerVersion)
- m_Client->Kick(Reply);
-}
-
-
-
-
+
+// ProtocolRecognizer.cpp
+
+// Implements the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple
+// protocol versions and redirects everything to them
+
+#include "Globals.h"
+
+#include "ProtocolRecognizer.h"
+#include "Protocol125.h"
+#include "Protocol132.h"
+#include "Protocol14x.h"
+#include "Protocol15x.h"
+#include "Protocol16x.h"
+#include "../ClientHandle.h"
+#include "../Root.h"
+#include "../World.h"
+#include "../ChatColor.h"
+
+
+
+
+
+cProtocolRecognizer::cProtocolRecognizer(cClientHandle * a_Client) :
+ super(a_Client),
+ m_Protocol(NULL),
+ m_Buffer(512)
+{
+}
+
+
+
+
+
+cProtocolRecognizer::~cProtocolRecognizer()
+{
+ delete m_Protocol;
+}
+
+
+
+
+
+AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion)
+{
+ switch (a_ProtocolVersion)
+ {
+ case PROTO_VERSION_1_2_5: return "1.2.5";
+ case PROTO_VERSION_1_3_2: return "1.3.2";
+ case PROTO_VERSION_1_4_2: return "1.4.2";
+ case PROTO_VERSION_1_4_4: return "1.4.4";
+ case PROTO_VERSION_1_4_6: return "1.4.6";
+ case PROTO_VERSION_1_5_0: return "1.5";
+ case PROTO_VERSION_1_5_2: return "1.5.2";
+ case PROTO_VERSION_1_6_1: return "1.6.1";
+ case PROTO_VERSION_1_6_2: return "1.6.2";
+ }
+ ASSERT(!"Unknown protocol version");
+ return Printf("Unknown protocol (%d)", a_ProtocolVersion);
+}
+
+
+
+
+
+void cProtocolRecognizer::DataReceived(const char * a_Data, int a_Size)
+{
+ if (m_Protocol == NULL)
+ {
+ if (!m_Buffer.Write(a_Data, a_Size))
+ {
+ m_Client->Kick("Unsupported protocol version");
+ return;
+ }
+
+ if (!TryRecognizeProtocol())
+ {
+ return;
+ }
+
+ // The protocol has just been recognized, dump the whole m_Buffer contents into it for parsing:
+ AString Dump;
+ m_Buffer.ResetRead();
+ m_Buffer.ReadAll(Dump);
+ m_Protocol->DataReceived(Dump.data(), Dump.size());
+ }
+ else
+ {
+ m_Protocol->DataReceived(a_Data, a_Size);
+ }
+}
+
+
+
+
+
+void cProtocolRecognizer::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendAttachEntity(a_Entity, a_Vehicle);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendBlockBreakAnim(a_entityID, a_BlockX, a_BlockY, a_BlockZ, stage);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendBlockChange(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendChat(const AString & a_Message)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendChat(a_Message);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendCollectPickup(a_Pickup, a_Player);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendDestroyEntity(const cEntity & a_Entity)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendDestroyEntity(a_Entity);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendDisconnect(const AString & a_Reason)
+{
+ if (m_Protocol != NULL)
+ {
+ m_Protocol->SendDisconnect(a_Reason);
+ }
+ else
+ {
+ // This is used when the client sends a server-ping, respond with the default packet:
+ WriteByte ((char)0xff); // PACKET_DISCONNECT
+ WriteString(a_Reason);
+ }
+}
+
+
+
+
+void cProtocolRecognizer::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendEntityHeadLook(const cEntity & a_Entity)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendEntityHeadLook(a_Entity);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendEntityLook(const cEntity & a_Entity)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendEntityLook(a_Entity);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendEntityMetadata(const cEntity & a_Entity)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendEntityMetadata(a_Entity);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendEntityProperties(const cEntity & a_Entity)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendEntityProperties(a_Entity);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendEntityStatus(const cEntity & a_Entity, char a_Status)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendEntityStatus(a_Entity, a_Status);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendEntityVelocity(const cEntity & a_Entity)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendEntityVelocity(a_Entity);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendExplosion(a_BlockX,a_BlockY,a_BlockZ,a_Radius, a_BlocksAffected, a_PlayerMotion);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendGameMode(eGameMode a_GameMode)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendGameMode(a_GameMode);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendHealth(void)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendHealth();
+}
+
+
+
+
+
+void cProtocolRecognizer::SendInventoryProgress(char a_WindowID, short a_Progressbar, short a_Value)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendInventoryProgress(a_WindowID, a_Progressbar, a_Value);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendKeepAlive(int a_PingID)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendKeepAlive(a_PingID);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendLogin(a_Player, a_World);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendPickupSpawn(const cPickup & a_Pickup)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendPickupSpawn(a_Pickup);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendPlayerAnimation(a_Player, a_Animation);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendPlayerListItem(a_Player, a_IsOnline);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendPlayerMaxSpeed(void)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendPlayerMaxSpeed();
+}
+
+
+
+
+
+void cProtocolRecognizer::SendPlayerMoveLook(void)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendPlayerMoveLook();
+}
+
+
+
+
+
+void cProtocolRecognizer::SendPlayerPosition(void)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendPlayerPosition();
+}
+
+
+
+
+
+void cProtocolRecognizer::SendPlayerSpawn(const cPlayer & a_Player)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendPlayerSpawn(a_Player);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendRespawn(void)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendRespawn();
+}
+
+
+
+
+
+void cProtocolRecognizer::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendSpawnFallingBlock(a_FallingBlock);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendSpawnMob(const cMonster & a_Mob)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendSpawnMob(a_Mob);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendSpawnObject(a_Entity, a_ObjectType, a_ObjectData, a_Yaw, a_Pitch);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendSpawnVehicle(a_Vehicle, a_VehicleType);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendTabCompletionResults(const AStringVector & a_Results)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendTabCompletionResults(a_Results);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendTeleportEntity(const cEntity & a_Entity)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendTeleportEntity(a_Entity);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendTimeUpdate(a_WorldAge, a_TimeOfDay);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendUnloadChunk(a_ChunkX, a_ChunkZ);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendUpdateSign(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ )
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendWeather(eWeather a_Weather)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendWeather(a_Weather);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendWholeInventory(const cInventory & a_Inventory)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendWholeInventory(a_Inventory);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendWholeInventory(const cWindow & a_Window)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendWholeInventory(a_Window);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendWindowClose(const cWindow & a_Window)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendWindowClose(a_Window);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendWindowOpen(a_WindowID, a_WindowType, a_WindowTitle, a_NumSlots);
+}
+
+
+
+
+
+AString cProtocolRecognizer::GetAuthServerID(void)
+{
+ ASSERT(m_Protocol != NULL);
+ return m_Protocol->GetAuthServerID();
+}
+
+
+
+
+
+void cProtocolRecognizer::SendData(const char * a_Data, int a_Size)
+{
+ // This is used only when handling the server ping
+ m_Client->SendData(a_Data, a_Size);
+}
+
+
+
+
+
+bool cProtocolRecognizer::TryRecognizeProtocol(void)
+{
+ // NOTE: If a new protocol is added or an old one is removed, adjust MCS_CLIENT_VERSIONS and
+ // MCS_PROTOCOL_VERSIONS macros in the header file, as well as PROTO_VERSION_LATEST macro
+
+ // The first packet should be a Handshake, 0x02:
+ unsigned char PacketType;
+ if (!m_Buffer.ReadByte(PacketType))
+ {
+ return false;
+ }
+ switch (PacketType)
+ {
+ case 0x02: break; // Handshake, continue recognizing
+ case 0xfe: HandleServerPing(); return false;
+ default: return false;
+ }
+
+ // 1.3.2 starts with 0x02 0x39 <name-length-short>
+ // 1.2.5 starts with 0x02 <name-length-short> and name is expected to less than 0x3900 long :)
+ char ch;
+ if (!m_Buffer.ReadChar(ch))
+ {
+ return false;
+ }
+ switch (ch)
+ {
+ case PROTO_VERSION_1_3_2:
+ {
+ m_Protocol = new cProtocol132(m_Client);
+ return true;
+ }
+ case PROTO_VERSION_1_4_2:
+ case PROTO_VERSION_1_4_4:
+ {
+ m_Protocol = new cProtocol142(m_Client);
+ return true;
+ }
+ case PROTO_VERSION_1_4_6:
+ {
+ m_Protocol = new cProtocol146(m_Client);
+ return true;
+ }
+ case PROTO_VERSION_1_5_0:
+ case PROTO_VERSION_1_5_2:
+ {
+ m_Protocol = new cProtocol150(m_Client);
+ return true;
+ }
+ case PROTO_VERSION_1_6_1:
+ {
+ m_Protocol = new cProtocol161(m_Client);
+ return true;
+ }
+ case PROTO_VERSION_1_6_2:
+ {
+ m_Protocol = new cProtocol162(m_Client);
+ return true;
+ }
+ }
+ m_Protocol = new cProtocol125(m_Client);
+ return true;
+}
+
+
+
+
+
+void cProtocolRecognizer::HandleServerPing(void)
+{
+ AString Reply;
+ switch (cRoot::Get()->m_PrimaryServerVersion)
+ {
+ case PROTO_VERSION_1_2_5:
+ case PROTO_VERSION_1_3_2:
+ {
+ // http://wiki.vg/wiki/index.php?title=Protocol&oldid=3099#Server_List_Ping_.280xFE.29
+ Printf(Reply, "%s%s%i%s%i",
+ cRoot::Get()->GetDefaultWorld()->GetDescription().c_str(),
+ cChatColor::Delimiter.c_str(),
+ cRoot::Get()->GetDefaultWorld()->GetNumPlayers(),
+ cChatColor::Delimiter.c_str(),
+ cRoot::Get()->GetDefaultWorld()->GetMaxPlayers()
+ );
+ break;
+ }
+
+ case PROTO_VERSION_1_4_2:
+ case PROTO_VERSION_1_4_4:
+ case PROTO_VERSION_1_4_6:
+ case PROTO_VERSION_1_5_0:
+ case PROTO_VERSION_1_5_2:
+ case PROTO_VERSION_1_6_1:
+ case PROTO_VERSION_1_6_2:
+ {
+ // The server list ping now has 1 more byte of "magic". Mojang just loves to complicate stuff.
+ // http://wiki.vg/wiki/index.php?title=Protocol&oldid=3101#Server_List_Ping_.280xFE.29
+ // _X 2012_10_31: I know that this needn't eat the byte, since it still may be in transit.
+ // Who cares? We're disconnecting anyway.
+ if (m_Buffer.CanReadBytes(1))
+ {
+ byte val;
+ m_Buffer.ReadByte(val);
+ ASSERT(val == 0x01);
+ }
+
+ // http://wiki.vg/wiki/index.php?title=Server_List_Ping&oldid=3100
+ AString NumPlayers;
+ Printf(NumPlayers, "%d", cRoot::Get()->GetDefaultWorld()->GetNumPlayers());
+ AString MaxPlayers;
+ Printf(MaxPlayers, "%d", cRoot::Get()->GetDefaultWorld()->GetMaxPlayers());
+
+ AString ProtocolVersionNum;
+ Printf(ProtocolVersionNum, "%d", cRoot::Get()->m_PrimaryServerVersion);
+ AString ProtocolVersionTxt(GetVersionTextFromInt(cRoot::Get()->m_PrimaryServerVersion));
+
+ // Cannot use Printf() because of in-string NUL bytes.
+ Reply = cChatColor::Delimiter;
+ Reply.append("1");
+ Reply.push_back(0);
+ Reply.append(ProtocolVersionNum);
+ Reply.push_back(0);
+ Reply.append(ProtocolVersionTxt);
+ Reply.push_back(0);
+ Reply.append(cRoot::Get()->GetDefaultWorld()->GetDescription());
+ Reply.push_back(0);
+ Reply.append(NumPlayers);
+ Reply.push_back(0);
+ Reply.append(MaxPlayers);
+ break;
+ }
+ } // switch (m_PrimaryServerVersion)
+ m_Client->Kick(Reply);
+}
+
+
+
+
diff --git a/source/Protocol/ProtocolRecognizer.h b/source/Protocol/ProtocolRecognizer.h
index 96d03082d..43d4a15e2 100644
--- a/source/Protocol/ProtocolRecognizer.h
+++ b/source/Protocol/ProtocolRecognizer.h
@@ -1,130 +1,132 @@
-
-// ProtocolRecognizer.h
-
-// Interfaces to the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple
-// protocol versions and redirects everything to them
-
-
-
-
-
-#pragma once
-
-#include "Protocol.h"
-#include "../ByteBuffer.h"
-
-
-
-
-
-// Adjust these if a new protocol is added or an old one is removed:
-#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2"
-#define MCS_PROTOCOL_VERSIONS "29, 39, 47, 49, 51, 60, 61, 73, 74"
-
-
-
-
-
-class cProtocolRecognizer :
- public cProtocol
-{
- typedef cProtocol super;
-
-public:
- enum
- {
- PROTO_VERSION_1_2_5 = 29,
- PROTO_VERSION_1_3_2 = 39,
- PROTO_VERSION_1_4_2 = 47,
- PROTO_VERSION_1_4_4 = 49,
- PROTO_VERSION_1_4_6 = 51,
- PROTO_VERSION_1_5_0 = 60,
- PROTO_VERSION_1_5_2 = 61,
- PROTO_VERSION_1_6_1 = 73,
- PROTO_VERSION_1_6_2 = 74,
-
- PROTO_VERSION_NEXT,
- PROTO_VERSION_LATEST = PROTO_VERSION_NEXT - 1, ///< Automatically assigned to the last protocol version, this serves as the default for PrimaryServerVersion
- } ;
-
- cProtocolRecognizer(cClientHandle * a_Client);
- virtual ~cProtocolRecognizer();
-
- /// Translates protocol version number into protocol version text: 49 -> "1.4.4"
- static AString GetVersionTextFromInt(int a_ProtocolVersion);
-
- /// Called when client sends some data:
- virtual void DataReceived(const char * a_Data, int a_Size) override;
-
- /// Sending stuff to clients (alphabetically sorted):
- virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
- virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
- virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
- virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
- virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
- virtual void SendChat (const AString & a_Message) override;
- virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
- virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override;
- virtual void SendDestroyEntity (const cEntity & a_Entity) override;
- virtual void SendDisconnect (const AString & a_Reason) override;
- virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
- virtual void SendEntityHeadLook (const cEntity & a_Entity) override;
- virtual void SendEntityLook (const cEntity & a_Entity) override;
- virtual void SendEntityMetadata (const cEntity & a_Entity) override;
- virtual void SendEntityProperties (const cEntity & a_Entity) override;
- virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
- virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
- virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override;
- virtual void SendEntityVelocity (const cEntity & a_Entity) override;
- virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override;
- virtual void SendGameMode (eGameMode a_GameMode) override;
- virtual void SendHealth (void) override;
- virtual void SendInventoryProgress (char a_WindowID, short a_Progressbar, short a_Value) override;
- virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
- virtual void SendKeepAlive (int a_PingID) override;
- virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
- virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
- virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override;
- virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override;
- virtual void SendPlayerMaxSpeed (void) override;
- virtual void SendPlayerMoveLook (void) override;
- virtual void SendPlayerPosition (void) override;
- virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
- virtual void SendRespawn (void) override;
- virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override;
- virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
- virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override;
- virtual void SendSpawnMob (const cMonster & a_Mob) override;
- virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
- virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType) override;
- virtual void SendTeleportEntity (const cEntity & a_Entity) override;
- virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
- virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
- virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
- virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override;
- virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override;
- virtual void SendWeather (eWeather a_Weather) override;
- virtual void SendWholeInventory (const cInventory & a_Inventory) override;
- virtual void SendWholeInventory (const cWindow & a_Window) override;
- virtual void SendWindowClose (const cWindow & a_Window) override;
- virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) override;
-
- virtual AString GetAuthServerID(void) override;
-
- virtual void SendData(const char * a_Data, int a_Size) override;
-
-protected:
- cProtocol * m_Protocol; //< The recognized protocol
- cByteBuffer m_Buffer; //< Buffer for the incoming data until we recognize the protocol
-
- /// Tries to recognize protocol based on m_Buffer contents; returns true if recognized
- bool TryRecognizeProtocol(void);
-
- /// Called when the recognizer gets a server ping packet; responds with server stats and destroys the client
- void HandleServerPing(void);
-} ;
-
-
-
-
-
+
+// ProtocolRecognizer.h
+
+// Interfaces to the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple
+// protocol versions and redirects everything to them
+
+
+
+
+
+#pragma once
+
+#include "Protocol.h"
+#include "../ByteBuffer.h"
+
+
+
+
+
+// Adjust these if a new protocol is added or an old one is removed:
+#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2"
+#define MCS_PROTOCOL_VERSIONS "29, 39, 47, 49, 51, 60, 61, 73, 74"
+
+
+
+
+
+class cProtocolRecognizer :
+ public cProtocol
+{
+ typedef cProtocol super;
+
+public:
+ enum
+ {
+ PROTO_VERSION_1_2_5 = 29,
+ PROTO_VERSION_1_3_2 = 39,
+ PROTO_VERSION_1_4_2 = 47,
+ PROTO_VERSION_1_4_4 = 49,
+ PROTO_VERSION_1_4_6 = 51,
+ PROTO_VERSION_1_5_0 = 60,
+ PROTO_VERSION_1_5_2 = 61,
+ PROTO_VERSION_1_6_1 = 73,
+ PROTO_VERSION_1_6_2 = 74,
+
+ PROTO_VERSION_NEXT,
+ PROTO_VERSION_LATEST = PROTO_VERSION_NEXT - 1, ///< Automatically assigned to the last protocol version, this serves as the default for PrimaryServerVersion
+ } ;
+
+ cProtocolRecognizer(cClientHandle * a_Client);
+ virtual ~cProtocolRecognizer();
+
+ /// Translates protocol version number into protocol version text: 49 -> "1.4.4"
+ static AString GetVersionTextFromInt(int a_ProtocolVersion);
+
+ /// Called when client sends some data:
+ virtual void DataReceived(const char * a_Data, int a_Size) override;
+
+ /// Sending stuff to clients (alphabetically sorted):
+ virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
+ virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
+ virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
+ virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
+ virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
+ virtual void SendChat (const AString & a_Message) override;
+ virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
+ virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override;
+ virtual void SendDestroyEntity (const cEntity & a_Entity) override;
+ virtual void SendDisconnect (const AString & a_Reason) override;
+ virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)
+ virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
+ virtual void SendEntityHeadLook (const cEntity & a_Entity) override;
+ virtual void SendEntityLook (const cEntity & a_Entity) override;
+ virtual void SendEntityMetadata (const cEntity & a_Entity) override;
+ virtual void SendEntityProperties (const cEntity & a_Entity) override;
+ virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
+ virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
+ virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override;
+ virtual void SendEntityVelocity (const cEntity & a_Entity) override;
+ virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override;
+ virtual void SendGameMode (eGameMode a_GameMode) override;
+ virtual void SendHealth (void) override;
+ virtual void SendInventoryProgress (char a_WindowID, short a_Progressbar, short a_Value) override;
+ virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
+ virtual void SendKeepAlive (int a_PingID) override;
+ virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
+ virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
+ virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override;
+ virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override;
+ virtual void SendPlayerMaxSpeed (void) override;
+ virtual void SendPlayerMoveLook (void) override;
+ virtual void SendPlayerPosition (void) override;
+ virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
+ virtual void SendRespawn (void) override;
+ virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override;
+ virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
+ virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override;
+ virtual void SendSpawnMob (const cMonster & a_Mob) override;
+ virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
+ virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType) override;
+ virtual void SendTabCompletionResults(const AStringVector & a_Results) override;
+ virtual void SendTeleportEntity (const cEntity & a_Entity) override;
+ virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
+ virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
+ virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
+ virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override;
+ virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override;
+ virtual void SendWeather (eWeather a_Weather) override;
+ virtual void SendWholeInventory (const cInventory & a_Inventory) override;
+ virtual void SendWholeInventory (const cWindow & a_Window) override;
+ virtual void SendWindowClose (const cWindow & a_Window) override;
+ virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) override;
+
+ virtual AString GetAuthServerID(void) override;
+
+ virtual void SendData(const char * a_Data, int a_Size) override;
+
+protected:
+ cProtocol * m_Protocol; //< The recognized protocol
+ cByteBuffer m_Buffer; //< Buffer for the incoming data until we recognize the protocol
+
+ /// Tries to recognize protocol based on m_Buffer contents; returns true if recognized
+ bool TryRecognizeProtocol(void);
+
+ /// Called when the recognizer gets a server ping packet; responds with server stats and destroys the client
+ void HandleServerPing(void);
+} ;
+
+
+
+
+
diff --git a/source/RCONServer.cpp b/source/RCONServer.cpp
index cf6ed8b0c..04c06c887 100644
--- a/source/RCONServer.cpp
+++ b/source/RCONServer.cpp
@@ -1,332 +1,332 @@
-
-// RCONServer.cpp
-
-// Implements the cRCONServer class representing the RCON server
-
-#include "Globals.h"
-#include "../iniFile/iniFile.h"
-#include "RCONServer.h"
-#include "Server.h"
-#include "Root.h"
-#include "CommandOutput.h"
-
-
-
-
-
-// Disable MSVC warnings:
-#if defined(_MSC_VER)
- #pragma warning(push)
- #pragma warning(disable:4355) // 'this' : used in base member initializer list
-#endif
-
-
-
-
-
-enum
-{
- // Client -> Server:
- RCON_PACKET_COMMAND = 2,
- RCON_PACKET_LOGIN = 3,
-
- // Server -> Client:
- RCON_PACKET_RESPONSE = 2,
-} ;
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cRCONCommandOutput:
-
-class cRCONCommandOutput :
- public cCommandOutputCallback
-{
-public:
- cRCONCommandOutput(cRCONServer::cConnection & a_Connection, int a_RequestID) :
- m_Connection(a_Connection),
- m_RequestID(a_RequestID)
- {
- }
-
- // cCommandOutputCallback overrides:
- virtual void Out(const AString & a_Text) override
- {
- m_Buffer.append(a_Text);
- }
-
- virtual void Finished(void) override
- {
- m_Connection.SendResponse(m_RequestID, RCON_PACKET_RESPONSE, m_Buffer.size(), m_Buffer.c_str());
- delete this;
- }
-
-protected:
- cRCONServer::cConnection & m_Connection;
- int m_RequestID;
- AString m_Buffer;
-} ;
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cRCONServer:
-
-cRCONServer::cRCONServer(cServer & a_Server) :
- m_Server(a_Server),
- m_ListenThread4(*this, cSocket::IPv4, "RCON"),
- m_ListenThread6(*this, cSocket::IPv6, "RCON")
-{
-}
-
-
-
-
-
-cRCONServer::~cRCONServer()
-{
- m_ListenThread4.Stop();
- m_ListenThread6.Stop();
-}
-
-
-
-
-
-void cRCONServer::Initialize(cIniFile & a_IniFile)
-{
- if (!a_IniFile.GetValueSetB("RCON", "Enabled", false))
- {
- return;
- }
-
- // Read the password, don't allow an empty one:
- m_Password = a_IniFile.GetValueSet("RCON", "Password", "");
- if (m_Password.empty())
- {
- LOGWARNING("RCON is requested, but the password is not set. RCON is now disabled.");
- return;
- }
-
- // Read and initialize both IPv4 and IPv6 ports for RCON
- bool HasAnyPorts = false;
- AString Ports4 = a_IniFile.GetValueSet("RCON", "PortsIPv4", "25575");
- if (m_ListenThread4.Initialize(Ports4))
- {
- HasAnyPorts = true;
- m_ListenThread4.Start();
- }
- AString Ports6 = a_IniFile.GetValueSet("RCON", "PortsIPv6", "25575");
- if (m_ListenThread6.Initialize(Ports6))
- {
- HasAnyPorts = true;
- m_ListenThread6.Start();
- }
- if (!HasAnyPorts)
- {
- LOGWARNING("RCON is requested, but no ports are specified. Specify at least one port in PortsIPv4 or PortsIPv6. RCON is now disabled.");
- return;
- }
-}
-
-
-
-
-
-void cRCONServer::OnConnectionAccepted(cSocket & a_Socket)
-{
- if (!a_Socket.IsValid())
- {
- return;
- }
-
- LOG("RCON Client \"%s\" connected!", a_Socket.GetIPString().c_str());
-
- // Create a new cConnection object, it will be deleted when the connection is closed
- m_SocketThreads.AddClient(a_Socket, new cConnection(*this, a_Socket));
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cRCONServer::cConnection:
-
-cRCONServer::cConnection::cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket) :
- m_IsAuthenticated(false),
- m_RCONServer(a_RCONServer),
- m_Socket(a_Socket),
- m_IPAddress(a_Socket.GetIPString())
-{
-}
-
-
-
-
-
-void cRCONServer::cConnection::DataReceived(const char * a_Data, int a_Size)
-{
- // Append data to the buffer:
- m_Buffer.append(a_Data, a_Size);
-
- // Process the packets in the buffer:
- while (m_Buffer.size() >= 14)
- {
- int Length = IntFromBuffer(m_Buffer.data());
- if (Length > 1500)
- {
- // Too long, drop the connection
- LOGWARNING("Received an invalid RCON packet length (%d), dropping RCON connection to %s.",
- Length, m_IPAddress.c_str()
- );
- m_RCONServer.m_SocketThreads.RemoveClient(this);
- m_Socket.CloseSocket();
- delete this;
- return;
- }
- if (Length > (int)(m_Buffer.size() + 4))
- {
- // Incomplete packet yet, wait for more data to come
- return;
- }
-
- int RequestID = IntFromBuffer(m_Buffer.data() + 4);
- int PacketType = IntFromBuffer(m_Buffer.data() + 8);
- if (!ProcessPacket(RequestID, PacketType, Length - 10, m_Buffer.data() + 12))
- {
- m_RCONServer.m_SocketThreads.RemoveClient(this);
- m_Socket.CloseSocket();
- delete this;
- return;
- }
- m_Buffer.erase(0, Length + 4);
- } // while (m_Buffer.size() >= 14)
-}
-
-
-
-
-
-void cRCONServer::cConnection::GetOutgoingData(AString & a_Data)
-{
- a_Data.assign(m_Outgoing);
- m_Outgoing.clear();
-}
-
-
-
-
-
-void cRCONServer::cConnection::SocketClosed(void)
-{
- m_RCONServer.m_SocketThreads.RemoveClient(this);
- delete this;
-}
-
-
-
-
-
-bool cRCONServer::cConnection::ProcessPacket(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload)
-{
- switch (a_PacketType)
- {
- case RCON_PACKET_LOGIN:
- {
- if (strncmp(a_Payload, m_RCONServer.m_Password.c_str(), a_PayloadLength) != 0)
- {
- LOGINFO("RCON: Invalid password from client %s, dropping connection.", m_IPAddress.c_str());
- return false;
- }
- m_IsAuthenticated = true;
-
- LOGD("RCON: Client at %s has successfully authenticated", m_IPAddress.c_str());
-
- // Send OK response:
- SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, NULL);
- return true;
- }
-
- case RCON_PACKET_COMMAND:
- {
- if (!m_IsAuthenticated)
- {
- char AuthNeeded[] = "You need to authenticate first!";
- SendResponse(a_RequestID, RCON_PACKET_RESPONSE, sizeof(AuthNeeded), AuthNeeded);
- return false;
- }
-
- AString cmd(a_Payload, a_PayloadLength);
- LOGD("RCON command from %s: \"%s\"", m_IPAddress.c_str(), cmd.c_str());
- cRoot::Get()->ExecuteConsoleCommand(cmd, *(new cRCONCommandOutput(*this, a_RequestID)));
-
- // Send an empty response:
- SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, NULL);
- return true;
- }
- }
-
- // Unknown packet type, drop the connection:
- LOGWARNING("RCON: Client at %s has sent an unknown packet type %d, dropping connection.",
- m_IPAddress.c_str(), a_PacketType
- );
- return false;
-}
-
-
-
-
-
-/// Reads 4 bytes from a_Buffer and returns the int they represent
-int cRCONServer::cConnection::IntFromBuffer(const char * a_Buffer)
-{
- return ((unsigned char)a_Buffer[3] << 24) | ((unsigned char)a_Buffer[2] << 16) | ((unsigned char)a_Buffer[1] << 8) | (unsigned char)a_Buffer[0];
-}
-
-
-
-
-
-/// Puts 4 bytes representing the int into the buffer
-void cRCONServer::cConnection::IntToBuffer(int a_Value, char * a_Buffer)
-{
- a_Buffer[0] = a_Value & 0xff;
- a_Buffer[1] = (a_Value >> 8) & 0xff;
- a_Buffer[2] = (a_Value >> 16) & 0xff;
- a_Buffer[3] = (a_Value >> 24) & 0xff;
-}
-
-
-
-
-
-/// Sends a RCON packet back to the client
-void cRCONServer::cConnection::SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload)
-{
- ASSERT((a_PayloadLength == 0) || (a_Payload != NULL)); // Either zero data to send, or a valid payload ptr
-
- char Buffer[4];
- int Length = a_PayloadLength + 10;
- IntToBuffer(Length, Buffer);
- m_Outgoing.append(Buffer, 4);
- IntToBuffer(a_RequestID, Buffer);
- m_Outgoing.append(Buffer, 4);
- IntToBuffer(a_PacketType, Buffer);
- m_Outgoing.append(Buffer, 4);
- if (a_PayloadLength > 0)
- {
- m_Outgoing.append(a_Payload, a_PayloadLength);
- }
- m_Outgoing.push_back(0);
- m_Outgoing.push_back(0);
- m_RCONServer.m_SocketThreads.NotifyWrite(this);
-}
-
-
-
-
+
+// RCONServer.cpp
+
+// Implements the cRCONServer class representing the RCON server
+
+#include "Globals.h"
+#include "../iniFile/iniFile.h"
+#include "RCONServer.h"
+#include "Server.h"
+#include "Root.h"
+#include "CommandOutput.h"
+
+
+
+
+
+// Disable MSVC warnings:
+#if defined(_MSC_VER)
+ #pragma warning(push)
+ #pragma warning(disable:4355) // 'this' : used in base member initializer list
+#endif
+
+
+
+
+
+enum
+{
+ // Client -> Server:
+ RCON_PACKET_COMMAND = 2,
+ RCON_PACKET_LOGIN = 3,
+
+ // Server -> Client:
+ RCON_PACKET_RESPONSE = 2,
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cRCONCommandOutput:
+
+class cRCONCommandOutput :
+ public cCommandOutputCallback
+{
+public:
+ cRCONCommandOutput(cRCONServer::cConnection & a_Connection, int a_RequestID) :
+ m_Connection(a_Connection),
+ m_RequestID(a_RequestID)
+ {
+ }
+
+ // cCommandOutputCallback overrides:
+ virtual void Out(const AString & a_Text) override
+ {
+ m_Buffer.append(a_Text);
+ }
+
+ virtual void Finished(void) override
+ {
+ m_Connection.SendResponse(m_RequestID, RCON_PACKET_RESPONSE, m_Buffer.size(), m_Buffer.c_str());
+ delete this;
+ }
+
+protected:
+ cRCONServer::cConnection & m_Connection;
+ int m_RequestID;
+ AString m_Buffer;
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cRCONServer:
+
+cRCONServer::cRCONServer(cServer & a_Server) :
+ m_Server(a_Server),
+ m_ListenThread4(*this, cSocket::IPv4, "RCON"),
+ m_ListenThread6(*this, cSocket::IPv6, "RCON")
+{
+}
+
+
+
+
+
+cRCONServer::~cRCONServer()
+{
+ m_ListenThread4.Stop();
+ m_ListenThread6.Stop();
+}
+
+
+
+
+
+void cRCONServer::Initialize(cIniFile & a_IniFile)
+{
+ if (!a_IniFile.GetValueSetB("RCON", "Enabled", false))
+ {
+ return;
+ }
+
+ // Read the password, don't allow an empty one:
+ m_Password = a_IniFile.GetValueSet("RCON", "Password", "");
+ if (m_Password.empty())
+ {
+ LOGWARNING("RCON is requested, but the password is not set. RCON is now disabled.");
+ return;
+ }
+
+ // Read and initialize both IPv4 and IPv6 ports for RCON
+ bool HasAnyPorts = false;
+ AString Ports4 = a_IniFile.GetValueSet("RCON", "PortsIPv4", "25575");
+ if (m_ListenThread4.Initialize(Ports4))
+ {
+ HasAnyPorts = true;
+ m_ListenThread4.Start();
+ }
+ AString Ports6 = a_IniFile.GetValueSet("RCON", "PortsIPv6", "25575");
+ if (m_ListenThread6.Initialize(Ports6))
+ {
+ HasAnyPorts = true;
+ m_ListenThread6.Start();
+ }
+ if (!HasAnyPorts)
+ {
+ LOGWARNING("RCON is requested, but no ports are specified. Specify at least one port in PortsIPv4 or PortsIPv6. RCON is now disabled.");
+ return;
+ }
+}
+
+
+
+
+
+void cRCONServer::OnConnectionAccepted(cSocket & a_Socket)
+{
+ if (!a_Socket.IsValid())
+ {
+ return;
+ }
+
+ LOG("RCON Client \"%s\" connected!", a_Socket.GetIPString().c_str());
+
+ // Create a new cConnection object, it will be deleted when the connection is closed
+ m_SocketThreads.AddClient(a_Socket, new cConnection(*this, a_Socket));
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cRCONServer::cConnection:
+
+cRCONServer::cConnection::cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket) :
+ m_IsAuthenticated(false),
+ m_RCONServer(a_RCONServer),
+ m_Socket(a_Socket),
+ m_IPAddress(a_Socket.GetIPString())
+{
+}
+
+
+
+
+
+void cRCONServer::cConnection::DataReceived(const char * a_Data, int a_Size)
+{
+ // Append data to the buffer:
+ m_Buffer.append(a_Data, a_Size);
+
+ // Process the packets in the buffer:
+ while (m_Buffer.size() >= 14)
+ {
+ int Length = IntFromBuffer(m_Buffer.data());
+ if (Length > 1500)
+ {
+ // Too long, drop the connection
+ LOGWARNING("Received an invalid RCON packet length (%d), dropping RCON connection to %s.",
+ Length, m_IPAddress.c_str()
+ );
+ m_RCONServer.m_SocketThreads.RemoveClient(this);
+ m_Socket.CloseSocket();
+ delete this;
+ return;
+ }
+ if (Length > (int)(m_Buffer.size() + 4))
+ {
+ // Incomplete packet yet, wait for more data to come
+ return;
+ }
+
+ int RequestID = IntFromBuffer(m_Buffer.data() + 4);
+ int PacketType = IntFromBuffer(m_Buffer.data() + 8);
+ if (!ProcessPacket(RequestID, PacketType, Length - 10, m_Buffer.data() + 12))
+ {
+ m_RCONServer.m_SocketThreads.RemoveClient(this);
+ m_Socket.CloseSocket();
+ delete this;
+ return;
+ }
+ m_Buffer.erase(0, Length + 4);
+ } // while (m_Buffer.size() >= 14)
+}
+
+
+
+
+
+void cRCONServer::cConnection::GetOutgoingData(AString & a_Data)
+{
+ a_Data.assign(m_Outgoing);
+ m_Outgoing.clear();
+}
+
+
+
+
+
+void cRCONServer::cConnection::SocketClosed(void)
+{
+ m_RCONServer.m_SocketThreads.RemoveClient(this);
+ delete this;
+}
+
+
+
+
+
+bool cRCONServer::cConnection::ProcessPacket(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload)
+{
+ switch (a_PacketType)
+ {
+ case RCON_PACKET_LOGIN:
+ {
+ if (strncmp(a_Payload, m_RCONServer.m_Password.c_str(), a_PayloadLength) != 0)
+ {
+ LOGINFO("RCON: Invalid password from client %s, dropping connection.", m_IPAddress.c_str());
+ return false;
+ }
+ m_IsAuthenticated = true;
+
+ LOGD("RCON: Client at %s has successfully authenticated", m_IPAddress.c_str());
+
+ // Send OK response:
+ SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, NULL);
+ return true;
+ }
+
+ case RCON_PACKET_COMMAND:
+ {
+ if (!m_IsAuthenticated)
+ {
+ char AuthNeeded[] = "You need to authenticate first!";
+ SendResponse(a_RequestID, RCON_PACKET_RESPONSE, sizeof(AuthNeeded), AuthNeeded);
+ return false;
+ }
+
+ AString cmd(a_Payload, a_PayloadLength);
+ LOGD("RCON command from %s: \"%s\"", m_IPAddress.c_str(), cmd.c_str());
+ cRoot::Get()->ExecuteConsoleCommand(cmd, *(new cRCONCommandOutput(*this, a_RequestID)));
+
+ // Send an empty response:
+ SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, NULL);
+ return true;
+ }
+ }
+
+ // Unknown packet type, drop the connection:
+ LOGWARNING("RCON: Client at %s has sent an unknown packet type %d, dropping connection.",
+ m_IPAddress.c_str(), a_PacketType
+ );
+ return false;
+}
+
+
+
+
+
+/// Reads 4 bytes from a_Buffer and returns the int they represent
+int cRCONServer::cConnection::IntFromBuffer(const char * a_Buffer)
+{
+ return ((unsigned char)a_Buffer[3] << 24) | ((unsigned char)a_Buffer[2] << 16) | ((unsigned char)a_Buffer[1] << 8) | (unsigned char)a_Buffer[0];
+}
+
+
+
+
+
+/// Puts 4 bytes representing the int into the buffer
+void cRCONServer::cConnection::IntToBuffer(int a_Value, char * a_Buffer)
+{
+ a_Buffer[0] = a_Value & 0xff;
+ a_Buffer[1] = (a_Value >> 8) & 0xff;
+ a_Buffer[2] = (a_Value >> 16) & 0xff;
+ a_Buffer[3] = (a_Value >> 24) & 0xff;
+}
+
+
+
+
+
+/// Sends a RCON packet back to the client
+void cRCONServer::cConnection::SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload)
+{
+ ASSERT((a_PayloadLength == 0) || (a_Payload != NULL)); // Either zero data to send, or a valid payload ptr
+
+ char Buffer[4];
+ int Length = a_PayloadLength + 10;
+ IntToBuffer(Length, Buffer);
+ m_Outgoing.append(Buffer, 4);
+ IntToBuffer(a_RequestID, Buffer);
+ m_Outgoing.append(Buffer, 4);
+ IntToBuffer(a_PacketType, Buffer);
+ m_Outgoing.append(Buffer, 4);
+ if (a_PayloadLength > 0)
+ {
+ m_Outgoing.append(a_Payload, a_PayloadLength);
+ }
+ m_Outgoing.push_back(0);
+ m_Outgoing.push_back(0);
+ m_RCONServer.m_SocketThreads.NotifyWrite(this);
+}
+
+
+
+
diff --git a/source/RCONServer.h b/source/RCONServer.h
index a4a0d20fe..0e89800a2 100644
--- a/source/RCONServer.h
+++ b/source/RCONServer.h
@@ -1,109 +1,109 @@
-
-// RCONServer.h
-
-// Declares the cRCONServer class representing the RCON server
-
-
-
-
-
-#pragma once
-
-#include "OSSupport/SocketThreads.h"
-#include "OSSupport/ListenThread.h"
-
-
-
-
-
-// fwd:
-class cServer;
-class cIniFile;
-
-
-
-
-
-class cRCONServer :
- public cListenThread::cCallback
-{
-public:
- cRCONServer(cServer & a_Server);
- ~cRCONServer();
-
- void Initialize(cIniFile & a_IniFile);
-
-protected:
- friend class cRCONCommandOutput;
-
- class cConnection :
- public cSocketThreads::cCallback
- {
- public:
- cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket);
-
- protected:
- friend class cRCONCommandOutput;
-
- /// Set to true if the client has successfully authenticated
- bool m_IsAuthenticated;
-
- /// Buffer for the incoming data
- AString m_Buffer;
-
- /// Buffer for the outgoing data
- AString m_Outgoing;
-
- /// Server that owns this connection and processes requests
- cRCONServer & m_RCONServer;
-
- /// The socket belonging to the client
- cSocket & m_Socket;
-
- /// Address of the client
- AString m_IPAddress;
-
-
- // cSocketThreads::cCallback overrides:
- virtual void DataReceived(const char * a_Data, int a_Size) override;
- virtual void GetOutgoingData(AString & a_Data) override;
- virtual void SocketClosed(void) override;
-
- /// Processes the given packet and sends the response; returns true if successful, false if the connection is to be dropped
- bool ProcessPacket(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload);
-
- /// Reads 4 bytes from a_Buffer and returns the int they represent
- int IntFromBuffer(const char * a_Buffer);
-
- /// Puts 4 bytes representing the int into the buffer
- void IntToBuffer(int a_Value, char * a_Buffer);
-
- /// Sends a RCON packet back to the client
- void SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload);
- } ;
-
-
- /// The server object that will process the commands received
- cServer & m_Server;
-
- /// The thread(s) that take care of all the traffic on the RCON ports
- cSocketThreads m_SocketThreads;
-
- /// The thread for accepting IPv4 RCON connections
- cListenThread m_ListenThread4;
-
- /// The thread for accepting IPv6 RCON connections
- cListenThread m_ListenThread6;
-
- /// Password for authentication
- AString m_Password;
-
-
- // cListenThread::cCallback overrides:
- virtual void OnConnectionAccepted(cSocket & a_Socket) override;
-} ;
-
-
-
-
-
+
+// RCONServer.h
+
+// Declares the cRCONServer class representing the RCON server
+
+
+
+
+
+#pragma once
+
+#include "OSSupport/SocketThreads.h"
+#include "OSSupport/ListenThread.h"
+
+
+
+
+
+// fwd:
+class cServer;
+class cIniFile;
+
+
+
+
+
+class cRCONServer :
+ public cListenThread::cCallback
+{
+public:
+ cRCONServer(cServer & a_Server);
+ ~cRCONServer();
+
+ void Initialize(cIniFile & a_IniFile);
+
+protected:
+ friend class cRCONCommandOutput;
+
+ class cConnection :
+ public cSocketThreads::cCallback
+ {
+ public:
+ cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket);
+
+ protected:
+ friend class cRCONCommandOutput;
+
+ /// Set to true if the client has successfully authenticated
+ bool m_IsAuthenticated;
+
+ /// Buffer for the incoming data
+ AString m_Buffer;
+
+ /// Buffer for the outgoing data
+ AString m_Outgoing;
+
+ /// Server that owns this connection and processes requests
+ cRCONServer & m_RCONServer;
+
+ /// The socket belonging to the client
+ cSocket & m_Socket;
+
+ /// Address of the client
+ AString m_IPAddress;
+
+
+ // cSocketThreads::cCallback overrides:
+ virtual void DataReceived(const char * a_Data, int a_Size) override;
+ virtual void GetOutgoingData(AString & a_Data) override;
+ virtual void SocketClosed(void) override;
+
+ /// Processes the given packet and sends the response; returns true if successful, false if the connection is to be dropped
+ bool ProcessPacket(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload);
+
+ /// Reads 4 bytes from a_Buffer and returns the int they represent
+ int IntFromBuffer(const char * a_Buffer);
+
+ /// Puts 4 bytes representing the int into the buffer
+ void IntToBuffer(int a_Value, char * a_Buffer);
+
+ /// Sends a RCON packet back to the client
+ void SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload);
+ } ;
+
+
+ /// The server object that will process the commands received
+ cServer & m_Server;
+
+ /// The thread(s) that take care of all the traffic on the RCON ports
+ cSocketThreads m_SocketThreads;
+
+ /// The thread for accepting IPv4 RCON connections
+ cListenThread m_ListenThread4;
+
+ /// The thread for accepting IPv6 RCON connections
+ cListenThread m_ListenThread6;
+
+ /// Password for authentication
+ AString m_Password;
+
+
+ // cListenThread::cCallback overrides:
+ virtual void OnConnectionAccepted(cSocket & a_Socket) override;
+} ;
+
+
+
+
+
diff --git a/source/SQLite/urls.txt b/source/SQLite/urls.txt
index 8b5869be6..131d70bbf 100644
--- a/source/SQLite/urls.txt
+++ b/source/SQLite/urls.txt
@@ -1,9 +1,9 @@
-
-SQLite:
-http://www.sqlite.org
-SQLite is in public domain
-
-LuaSQLite3:
-http://lua.sqlite.org
-http://lua.sqlite.org/index.cgi/doc/tip/doc/lsqlite3.wiki -- documentation
+
+SQLite:
+http://www.sqlite.org
+SQLite is in public domain
+
+LuaSQLite3:
+http://lua.sqlite.org
+http://lua.sqlite.org/index.cgi/doc/tip/doc/lsqlite3.wiki -- documentation
License for LuaSQLite is stored in $/install/LuaSQLite3-LICENSE.txt and distributed with the executables \ No newline at end of file
diff --git a/source/Server.h b/source/Server.h
index 542673b49..dd7a08735 100644
--- a/source/Server.h
+++ b/source/Server.h
@@ -60,7 +60,7 @@ public: // tolua_export
void KickUser(int a_ClientID, const AString & a_Reason);
void AuthenticateUser(int a_ClientID); // Called by cAuthenticator to auth the specified user
- const AString & GetServerID(void) const;
+ const AString & GetServerID(void) const; // tolua_export
void ClientDestroying(const cClientHandle * a_Client); // Called by cClientHandle::Destroy(); stop m_SocketThreads from calling back into a_Client
diff --git a/source/Simulator/DelayedFluidSimulator.cpp b/source/Simulator/DelayedFluidSimulator.cpp
index 6cc982e3d..a4645ca09 100644
--- a/source/Simulator/DelayedFluidSimulator.cpp
+++ b/source/Simulator/DelayedFluidSimulator.cpp
@@ -1,158 +1,158 @@
-
-// DelayedFluidSimulator.cpp
-
-// Interfaces to the cDelayedFluidSimulator class representing a fluid simulator that has a configurable delay
-// before simulating a block. Each tick it takes a consecutive delay "slot" and simulates only blocks in that slot.
-
-#include "Globals.h"
-
-#include "DelayedFluidSimulator.h"
-#include "../World.h"
-#include "../Chunk.h"
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cDelayedFluidSimulatorChunkData::cSlot
-
-bool cDelayedFluidSimulatorChunkData::cSlot::Add(int a_RelX, int a_RelY, int a_RelZ)
-{
- ASSERT(a_RelZ >= 0);
- ASSERT(a_RelZ < ARRAYCOUNT(m_Blocks));
-
- cCoordWithIntVector & Blocks = m_Blocks[a_RelZ];
- int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
- for (cCoordWithIntVector::const_iterator itr = Blocks.begin(), end = Blocks.end(); itr != end; ++itr)
- {
- if (itr->Data == Index)
- {
- // Already present
- return false;
- }
- } // for itr - Blocks[]
- Blocks.push_back(cCoordWithInt(a_RelX, a_RelY, a_RelZ, Index));
- return true;
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cDelayedFluidSimulatorChunkData:
-
-cDelayedFluidSimulatorChunkData::cDelayedFluidSimulatorChunkData(int a_TickDelay) :
- m_Slots(new cSlot[a_TickDelay])
-{
-}
-
-
-
-
-
-cDelayedFluidSimulatorChunkData::~cDelayedFluidSimulatorChunkData()
-{
- delete[] m_Slots;
- m_Slots = NULL;
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cDelayedFluidSimulator:
-
-cDelayedFluidSimulator::cDelayedFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid, int a_TickDelay) :
- super(a_World, a_Fluid, a_StationaryFluid),
- m_TickDelay(a_TickDelay),
- m_AddSlotNum(a_TickDelay - 1),
- m_SimSlotNum(0),
- m_TotalBlocks(0)
-{
-}
-
-
-
-
-
-void cDelayedFluidSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk)
-{
- if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
- {
- // Not inside the world (may happen when rclk with a full bucket - the client sends Y = -1)
- return;
- }
-
- if ((a_Chunk == NULL) || !a_Chunk->IsValid())
- {
- return;
- }
-
- int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width;
- int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width;
- BLOCKTYPE BlockType = a_Chunk->GetBlock(RelX, a_BlockY, RelZ);
- if (BlockType != m_FluidBlock)
- {
- return;
- }
-
- void * ChunkDataRaw = (m_FluidBlock == E_BLOCK_WATER) ? a_Chunk->GetWaterSimulatorData() : a_Chunk->GetLavaSimulatorData();
- cDelayedFluidSimulatorChunkData * ChunkData = (cDelayedFluidSimulatorChunkData *)ChunkDataRaw;
- cDelayedFluidSimulatorChunkData::cSlot & Slot = ChunkData->m_Slots[m_AddSlotNum];
-
- // Add, if not already present:
- if (!Slot.Add(RelX, a_BlockY, RelZ))
- {
- return;
- }
-
- ++m_TotalBlocks;
-}
-
-
-
-
-
-void cDelayedFluidSimulator::Simulate(float a_Dt)
-{
- m_AddSlotNum = m_SimSlotNum;
- m_SimSlotNum += 1;
- if (m_SimSlotNum >= m_TickDelay)
- {
- m_SimSlotNum = 0;
- }
-}
-
-
-
-
-
-void cDelayedFluidSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
-{
- void * ChunkDataRaw = (m_FluidBlock == E_BLOCK_WATER) ? a_Chunk->GetWaterSimulatorData() : a_Chunk->GetLavaSimulatorData();
- cDelayedFluidSimulatorChunkData * ChunkData = (cDelayedFluidSimulatorChunkData *)ChunkDataRaw;
- cDelayedFluidSimulatorChunkData::cSlot & Slot = ChunkData->m_Slots[m_SimSlotNum];
-
- // Simulate all the blocks in the scheduled slot:
- for (int i = 0; i < ARRAYCOUNT(Slot.m_Blocks); i++)
- {
- cCoordWithIntVector & Blocks = Slot.m_Blocks[i];
- if (Blocks.empty())
- {
- continue;
- }
- for (cCoordWithIntVector::iterator itr = Blocks.begin(), end = Blocks.end(); itr != end; ++itr)
- {
- SimulateBlock(a_Chunk, itr->x, itr->y, itr->z);
- }
- m_TotalBlocks -= Blocks.size();
- Blocks.clear();
- }
-}
-
-
-
-
+
+// DelayedFluidSimulator.cpp
+
+// Interfaces to the cDelayedFluidSimulator class representing a fluid simulator that has a configurable delay
+// before simulating a block. Each tick it takes a consecutive delay "slot" and simulates only blocks in that slot.
+
+#include "Globals.h"
+
+#include "DelayedFluidSimulator.h"
+#include "../World.h"
+#include "../Chunk.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cDelayedFluidSimulatorChunkData::cSlot
+
+bool cDelayedFluidSimulatorChunkData::cSlot::Add(int a_RelX, int a_RelY, int a_RelZ)
+{
+ ASSERT(a_RelZ >= 0);
+ ASSERT(a_RelZ < ARRAYCOUNT(m_Blocks));
+
+ cCoordWithIntVector & Blocks = m_Blocks[a_RelZ];
+ int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
+ for (cCoordWithIntVector::const_iterator itr = Blocks.begin(), end = Blocks.end(); itr != end; ++itr)
+ {
+ if (itr->Data == Index)
+ {
+ // Already present
+ return false;
+ }
+ } // for itr - Blocks[]
+ Blocks.push_back(cCoordWithInt(a_RelX, a_RelY, a_RelZ, Index));
+ return true;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cDelayedFluidSimulatorChunkData:
+
+cDelayedFluidSimulatorChunkData::cDelayedFluidSimulatorChunkData(int a_TickDelay) :
+ m_Slots(new cSlot[a_TickDelay])
+{
+}
+
+
+
+
+
+cDelayedFluidSimulatorChunkData::~cDelayedFluidSimulatorChunkData()
+{
+ delete[] m_Slots;
+ m_Slots = NULL;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cDelayedFluidSimulator:
+
+cDelayedFluidSimulator::cDelayedFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid, int a_TickDelay) :
+ super(a_World, a_Fluid, a_StationaryFluid),
+ m_TickDelay(a_TickDelay),
+ m_AddSlotNum(a_TickDelay - 1),
+ m_SimSlotNum(0),
+ m_TotalBlocks(0)
+{
+}
+
+
+
+
+
+void cDelayedFluidSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk)
+{
+ if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
+ {
+ // Not inside the world (may happen when rclk with a full bucket - the client sends Y = -1)
+ return;
+ }
+
+ if ((a_Chunk == NULL) || !a_Chunk->IsValid())
+ {
+ return;
+ }
+
+ int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width;
+ int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width;
+ BLOCKTYPE BlockType = a_Chunk->GetBlock(RelX, a_BlockY, RelZ);
+ if (BlockType != m_FluidBlock)
+ {
+ return;
+ }
+
+ void * ChunkDataRaw = (m_FluidBlock == E_BLOCK_WATER) ? a_Chunk->GetWaterSimulatorData() : a_Chunk->GetLavaSimulatorData();
+ cDelayedFluidSimulatorChunkData * ChunkData = (cDelayedFluidSimulatorChunkData *)ChunkDataRaw;
+ cDelayedFluidSimulatorChunkData::cSlot & Slot = ChunkData->m_Slots[m_AddSlotNum];
+
+ // Add, if not already present:
+ if (!Slot.Add(RelX, a_BlockY, RelZ))
+ {
+ return;
+ }
+
+ ++m_TotalBlocks;
+}
+
+
+
+
+
+void cDelayedFluidSimulator::Simulate(float a_Dt)
+{
+ m_AddSlotNum = m_SimSlotNum;
+ m_SimSlotNum += 1;
+ if (m_SimSlotNum >= m_TickDelay)
+ {
+ m_SimSlotNum = 0;
+ }
+}
+
+
+
+
+
+void cDelayedFluidSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
+{
+ void * ChunkDataRaw = (m_FluidBlock == E_BLOCK_WATER) ? a_Chunk->GetWaterSimulatorData() : a_Chunk->GetLavaSimulatorData();
+ cDelayedFluidSimulatorChunkData * ChunkData = (cDelayedFluidSimulatorChunkData *)ChunkDataRaw;
+ cDelayedFluidSimulatorChunkData::cSlot & Slot = ChunkData->m_Slots[m_SimSlotNum];
+
+ // Simulate all the blocks in the scheduled slot:
+ for (int i = 0; i < ARRAYCOUNT(Slot.m_Blocks); i++)
+ {
+ cCoordWithIntVector & Blocks = Slot.m_Blocks[i];
+ if (Blocks.empty())
+ {
+ continue;
+ }
+ for (cCoordWithIntVector::iterator itr = Blocks.begin(), end = Blocks.end(); itr != end; ++itr)
+ {
+ SimulateBlock(a_Chunk, itr->x, itr->y, itr->z);
+ }
+ m_TotalBlocks -= Blocks.size();
+ Blocks.clear();
+ }
+}
+
+
+
+
diff --git a/source/Simulator/DelayedFluidSimulator.h b/source/Simulator/DelayedFluidSimulator.h
index 6f7433877..c81500741 100644
--- a/source/Simulator/DelayedFluidSimulator.h
+++ b/source/Simulator/DelayedFluidSimulator.h
@@ -1,82 +1,82 @@
-
-// DelayedFluidSimulator.h
-
-// Interfaces to the cDelayedFluidSimulator class representing a fluid simulator that has a configurable delay
-// before simulating a block. Each tick it takes a consecutive delay "slot" and simulates only blocks in that slot.
-
-
-
-
-#pragma once
-
-#include "FluidSimulator.h"
-
-
-
-
-
-class cDelayedFluidSimulatorChunkData :
- public cFluidSimulatorData
-{
-public:
- class cSlot
- {
- public:
- /// Returns true if the specified block is stored
- bool HasBlock(int a_RelX, int a_RelY, int a_RelZ);
-
- /// Adds the specified block unless already present; returns true if added, false if the block was already present
- bool Add(int a_RelX, int a_RelY, int a_RelZ);
-
- /** Array of block containers, each item stores blocks for one Z coord
- Int param is the block index (for faster duplicate comparison in Add())
- */
- cCoordWithIntVector m_Blocks[16];
- } ;
-
- cDelayedFluidSimulatorChunkData(int a_TickDelay);
- virtual ~cDelayedFluidSimulatorChunkData();
-
- /// Slots, one for each delay tick, each containing the blocks to simulate
- cSlot * m_Slots;
-} ;
-
-
-
-
-
-class cDelayedFluidSimulator :
- public cFluidSimulator
-{
- typedef cFluidSimulator super;
-
-public:
- cDelayedFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid, int a_TickDelay);
-
- // cSimulator overrides:
- virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override;
- virtual void Simulate(float a_Dt) override;
- virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override;
- virtual cFluidSimulatorData * CreateChunkData(void) override { return new cDelayedFluidSimulatorChunkData(m_TickDelay); }
-
-protected:
-
- int m_TickDelay; // Count of the m_Slots array in each ChunkData
- int m_AddSlotNum; // Index into m_Slots[] where to add new blocks in each ChunkData
- int m_SimSlotNum; // Index into m_Slots[] where to simulate blocks in each ChunkData
-
- int m_TotalBlocks; // Statistics only: the total number of blocks currently queued
-
- /*
- Slots:
- | 0 | 1 | ... | m_AddSlotNum | m_SimSlotNum | ... | m_TickDelay - 1 |
- adding blocks here ^ | ^ simulating here
- */
-
- /// Called from SimulateChunk() to simulate each block in one slot of blocks. Descendants override this method to provide custom simulation.
- virtual void SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) = 0;
-} ;
-
-
-
-
+
+// DelayedFluidSimulator.h
+
+// Interfaces to the cDelayedFluidSimulator class representing a fluid simulator that has a configurable delay
+// before simulating a block. Each tick it takes a consecutive delay "slot" and simulates only blocks in that slot.
+
+
+
+
+#pragma once
+
+#include "FluidSimulator.h"
+
+
+
+
+
+class cDelayedFluidSimulatorChunkData :
+ public cFluidSimulatorData
+{
+public:
+ class cSlot
+ {
+ public:
+ /// Returns true if the specified block is stored
+ bool HasBlock(int a_RelX, int a_RelY, int a_RelZ);
+
+ /// Adds the specified block unless already present; returns true if added, false if the block was already present
+ bool Add(int a_RelX, int a_RelY, int a_RelZ);
+
+ /** Array of block containers, each item stores blocks for one Z coord
+ Int param is the block index (for faster duplicate comparison in Add())
+ */
+ cCoordWithIntVector m_Blocks[16];
+ } ;
+
+ cDelayedFluidSimulatorChunkData(int a_TickDelay);
+ virtual ~cDelayedFluidSimulatorChunkData();
+
+ /// Slots, one for each delay tick, each containing the blocks to simulate
+ cSlot * m_Slots;
+} ;
+
+
+
+
+
+class cDelayedFluidSimulator :
+ public cFluidSimulator
+{
+ typedef cFluidSimulator super;
+
+public:
+ cDelayedFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid, int a_TickDelay);
+
+ // cSimulator overrides:
+ virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override;
+ virtual void Simulate(float a_Dt) override;
+ virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override;
+ virtual cFluidSimulatorData * CreateChunkData(void) override { return new cDelayedFluidSimulatorChunkData(m_TickDelay); }
+
+protected:
+
+ int m_TickDelay; // Count of the m_Slots array in each ChunkData
+ int m_AddSlotNum; // Index into m_Slots[] where to add new blocks in each ChunkData
+ int m_SimSlotNum; // Index into m_Slots[] where to simulate blocks in each ChunkData
+
+ int m_TotalBlocks; // Statistics only: the total number of blocks currently queued
+
+ /*
+ Slots:
+ | 0 | 1 | ... | m_AddSlotNum | m_SimSlotNum | ... | m_TickDelay - 1 |
+ adding blocks here ^ | ^ simulating here
+ */
+
+ /// Called from SimulateChunk() to simulate each block in one slot of blocks. Descendants override this method to provide custom simulation.
+ virtual void SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) = 0;
+} ;
+
+
+
+
diff --git a/source/Simulator/FloodyFluidSimulator.cpp b/source/Simulator/FloodyFluidSimulator.cpp
index f2cc068e8..9374bbab3 100644
--- a/source/Simulator/FloodyFluidSimulator.cpp
+++ b/source/Simulator/FloodyFluidSimulator.cpp
@@ -1,334 +1,334 @@
-
-// FloodyFluidSimulator.cpp
-
-// Interfaces to the cFloodyFluidSimulator that represents a fluid simulator that tries to flood everything :)
-// http://forum.mc-server.org/showthread.php?tid=565
-
-#include "Globals.h"
-
-#include "FloodyFluidSimulator.h"
-#include "../World.h"
-#include "../Chunk.h"
-#include "../BlockArea.h"
-#include "../Blocks/BlockHandler.h"
-
-
-
-
-
-// Enable or disable detailed logging
-#if 0
- #define FLOG LOGD
-#else
- #define FLOG(...)
-#endif
-
-
-
-
-
-cFloodyFluidSimulator::cFloodyFluidSimulator(
- cWorld & a_World,
- BLOCKTYPE a_Fluid,
- BLOCKTYPE a_StationaryFluid,
- NIBBLETYPE a_Falloff,
- int a_TickDelay,
- int a_NumNeighborsForSource
-) :
- super(a_World, a_Fluid, a_StationaryFluid, a_TickDelay),
- m_Falloff(a_Falloff),
- m_NumNeighborsForSource(a_NumNeighborsForSource)
-{
-}
-
-
-
-
-
-void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
-{
- FLOG("Simulating block {%d, %d, %d}: block %d, meta %d",
- a_Chunk->GetPosX() * cChunkDef::Width + a_RelX, a_RelY, a_Chunk->GetPosZ() * cChunkDef::Width + a_RelZ,
- a_Chunk->GetBlock(a_RelX, a_RelY, a_RelZ),
- a_Chunk->GetMeta(a_RelX, a_RelY, a_RelZ)
- );
-
- NIBBLETYPE MyMeta = a_Chunk->GetMeta(a_RelX, a_RelY, a_RelZ);
- if (!IsAnyFluidBlock(a_Chunk->GetBlock(a_RelX, a_RelY, a_RelZ)))
- {
- // Can happen - if a block is scheduled for simulating and gets replaced in the meantime.
- FLOG(" BadBlockType exit");
- return;
- }
-
- if (MyMeta != 0)
- {
- // Source blocks aren't checked for tributaries, others are.
- if (CheckTributaries(a_Chunk, a_RelX, a_RelY, a_RelZ, MyMeta))
- {
- // Has no tributary, has been decreased (in CheckTributaries()),
- // no more processing needed (neighbors have been scheduled by the decrease)
- FLOG(" CheckTributaries exit");
- return;
- }
- }
-
- // New meta for the spreading to neighbors:
- // If this is a source block or was falling, the new meta is just the falloff
- // Otherwise it is the current meta plus falloff (may be larger than max height, will be checked later)
- NIBBLETYPE NewMeta = ((MyMeta == 0) || ((MyMeta & 0x08) != 0)) ? m_Falloff : (MyMeta + m_Falloff);
- bool SpreadFurther = true;
- if (a_RelY > 0)
- {
- BLOCKTYPE Below = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ);
- if (IsPassableForFluid(Below) || IsBlockLava(Below) || IsBlockWater(Below))
- {
- // Spread only down, possibly washing away what's there or turning lava to stone / cobble / obsidian:
- SpreadToNeighbor(a_Chunk, a_RelX, a_RelY - 1, a_RelZ, 8);
- SpreadFurther = false;
- }
- // If source creation is on, check for it here:
- else if (
- (m_NumNeighborsForSource > 0) && // Source creation is on
- (MyMeta == m_Falloff) && // Only exactly one block away from a source (fast bail-out)
- !IsPassableForFluid(Below) && // Only exactly 1 block deep
- CheckNeighborsForSource(a_Chunk, a_RelX, a_RelY, a_RelZ) // Did we create a source?
- )
- {
- // We created a source, no more spreading is to be done now
- // Also has been re-scheduled for ticking in the next wave, so no marking is needed
- return;
- }
- }
-
- if (SpreadFurther && (NewMeta < 8))
- {
- // Spread to the neighbors:
- SpreadToNeighbor(a_Chunk, a_RelX - 1, a_RelY, a_RelZ, NewMeta);
- SpreadToNeighbor(a_Chunk, a_RelX + 1, a_RelY, a_RelZ, NewMeta);
- SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ - 1, NewMeta);
- SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ + 1, NewMeta);
- }
-
- // Mark as processed:
- a_Chunk->FastSetBlock(a_RelX, a_RelY, a_RelZ, m_StationaryFluidBlock, MyMeta);
-}
-
-
-
-
-
-bool cFloodyFluidSimulator::CheckTributaries(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_MyMeta)
-{
- // If we have a section above, check if there's fluid above this block that would feed it:
- if (a_RelY < cChunkDef::Height - 1)
- {
- if (IsAnyFluidBlock(a_Chunk->GetBlock(a_RelX, a_RelY + 1, a_RelZ)))
- {
- // This block is fed from above, no more processing needed
- FLOG(" Fed from above");
- return false;
- }
- }
-
- // Not fed from above, check if there's a feed from the side (but not if it's a downward-flowing block):
- if (a_MyMeta != 8)
- {
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
- static const Vector3i Coords[] =
- {
- Vector3i( 1, 0, 0),
- Vector3i(-1, 0, 0),
- Vector3i( 0, 0, 1),
- Vector3i( 0, 0, -1),
- } ;
- for (int i = 0; i < ARRAYCOUNT(Coords); i++)
- {
- if (!a_Chunk->UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta))
- {
- continue;
- }
- if (IsAllowedBlock(BlockType) && IsHigherMeta(BlockMeta, a_MyMeta))
- {
- // This block is fed, no more processing needed
- FLOG(" Fed from {%d, %d, %d}, type %d, meta %d",
- a_Chunk->GetPosX() * cChunkDef::Width + a_RelX + Coords[i].x,
- a_RelY,
- a_Chunk->GetPosZ() * cChunkDef::Width + a_RelZ + Coords[i].z,
- BlockType, BlockMeta
- );
- return false;
- }
- } // for i - Coords[]
- } // if not fed from above
-
- // Block is not fed, decrease by m_Falloff levels:
- if (a_MyMeta >= 8)
- {
- FLOG(" Not fed and downwards, turning into non-downwards meta %d", m_Falloff);
- a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, m_StationaryFluidBlock, m_Falloff);
- }
- else
- {
- a_MyMeta += m_Falloff;
- if (a_MyMeta < 8)
- {
- FLOG(" Not fed, decreasing from %d to %d", a_MyMeta - m_Falloff, a_MyMeta);
- a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, m_StationaryFluidBlock, a_MyMeta);
- }
- else
- {
- FLOG(" Not fed, meta %d, erasing altogether", a_MyMeta);
- a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0);
- }
- }
- return true;
-}
-
-
-
-
-
-void cFloodyFluidSimulator::SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta)
-{
- ASSERT(a_NewMeta <= 8); // Invalid meta values
- ASSERT(a_NewMeta > 0); // Source blocks aren't spread
-
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
- if (!a_NearChunk->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta))
- {
- // Chunk not available
- return;
- }
-
- if (IsAllowedBlock(BlockType))
- {
- if ((BlockMeta == a_NewMeta) || IsHigherMeta(BlockMeta, a_NewMeta))
- {
- // Don't spread there, there's already a higher or same level there
- return;
- }
- }
-
- // Check water - lava interaction:
- if (m_FluidBlock == E_BLOCK_LAVA)
- {
- if (IsBlockWater(BlockType))
- {
- // Lava flowing into water, change to stone / cobblestone based on direction:
- BLOCKTYPE NewBlock = (a_NewMeta == 8) ? E_BLOCK_STONE : E_BLOCK_COBBLESTONE;
- FLOG(" Lava flowing into water, turning water at rel {%d, %d, %d} into stone",
- a_RelX, a_RelY, a_RelZ,
- ItemTypeToString(NewBlock).c_str()
- );
- a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, NewBlock, 0);
-
- // TODO: Sound effect
-
- return;
- }
- }
- else if (m_FluidBlock == E_BLOCK_WATER)
- {
- if (IsBlockLava(BlockType))
- {
- // Water flowing into lava, change to cobblestone / obsidian based on dest block:
- BLOCKTYPE NewBlock = (BlockMeta == 0) ? E_BLOCK_OBSIDIAN : E_BLOCK_COBBLESTONE;
- FLOG(" Water flowing into lava, turning lava at rel {%d, %d, %d} into %s",
- a_RelX, a_RelY, a_RelZ, ItemTypeToString(NewBlock).c_str()
- );
- a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, NewBlock, 0);
-
- // TODO: Sound effect
-
- return;
- }
- }
- else
- {
- ASSERT(!"Unknown fluid!");
- }
-
- if (!IsPassableForFluid(BlockType))
- {
- // Can't spread there
- return;
- }
-
- // Wash away the block there, if possible:
- if (CanWashAway(BlockType))
- {
- cBlockHandler * Handler = BlockHandler(BlockType);
- if (Handler->DoesDropOnUnsuitable())
- {
- Handler->DropBlock(
- &m_World, NULL,
- a_NearChunk->GetPosX() * cChunkDef::Width + a_RelX,
- a_RelY,
- a_NearChunk->GetPosZ() * cChunkDef::Width + a_RelZ
- );
- }
- } // if (CanWashAway)
-
- // Spread:
- FLOG(" Spreading to {%d, %d, %d} with meta %d",
- a_NearChunk->GetPosX() * cChunkDef::Width + a_RelX,
- a_RelY,
- a_NearChunk->GetPosZ() * cChunkDef::Width + a_RelZ,
- a_NewMeta
- );
- a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta);
-}
-
-
-
-
-
-bool cFloodyFluidSimulator::CheckNeighborsForSource(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
-{
- FLOG(" Checking neighbors for source creation");
-
- static const Vector3i NeighborCoords[] =
- {
- Vector3i(-1, 0, 0),
- Vector3i( 1, 0, 0),
- Vector3i( 0, 0, -1),
- Vector3i( 0, 0, 1),
- } ;
-
- int NumNeeded = m_NumNeighborsForSource;
- for (int i = 0; i < ARRAYCOUNT(NeighborCoords); i++)
- {
- int x = a_RelX + NeighborCoords[i].x;
- int y = a_RelY + NeighborCoords[i].y;
- int z = a_RelZ + NeighborCoords[i].z;
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
- if (!a_Chunk->UnboundedRelGetBlock(x, y, z, BlockType, BlockMeta))
- {
- // Neighbor not available, skip it
- continue;
- }
- // FLOG(" Neighbor at {%d, %d, %d}: %s", x, y, z, ItemToFullString(cItem(BlockType, 1, BlockMeta)).c_str());
- if ((BlockMeta == 0) && IsAnyFluidBlock(BlockType))
- {
- NumNeeded--;
- // FLOG(" Found a neighbor source at {%d, %d, %d}, NumNeeded := %d", x, y, z, NumNeeded);
- if (NumNeeded == 0)
- {
- // Found enough, turn into a source and bail out
- // FLOG(" Found enough neighbor sources, turning into a source");
- a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, m_FluidBlock, 0);
- return true;
- }
- }
- }
- // FLOG(" Not enough neighbors for turning into a source, NumNeeded = %d", NumNeeded);
- return false;
-}
-
-
-
-
+
+// FloodyFluidSimulator.cpp
+
+// Interfaces to the cFloodyFluidSimulator that represents a fluid simulator that tries to flood everything :)
+// http://forum.mc-server.org/showthread.php?tid=565
+
+#include "Globals.h"
+
+#include "FloodyFluidSimulator.h"
+#include "../World.h"
+#include "../Chunk.h"
+#include "../BlockArea.h"
+#include "../Blocks/BlockHandler.h"
+
+
+
+
+
+// Enable or disable detailed logging
+#if 0
+ #define FLOG LOGD
+#else
+ #define FLOG(...)
+#endif
+
+
+
+
+
+cFloodyFluidSimulator::cFloodyFluidSimulator(
+ cWorld & a_World,
+ BLOCKTYPE a_Fluid,
+ BLOCKTYPE a_StationaryFluid,
+ NIBBLETYPE a_Falloff,
+ int a_TickDelay,
+ int a_NumNeighborsForSource
+) :
+ super(a_World, a_Fluid, a_StationaryFluid, a_TickDelay),
+ m_Falloff(a_Falloff),
+ m_NumNeighborsForSource(a_NumNeighborsForSource)
+{
+}
+
+
+
+
+
+void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
+{
+ FLOG("Simulating block {%d, %d, %d}: block %d, meta %d",
+ a_Chunk->GetPosX() * cChunkDef::Width + a_RelX, a_RelY, a_Chunk->GetPosZ() * cChunkDef::Width + a_RelZ,
+ a_Chunk->GetBlock(a_RelX, a_RelY, a_RelZ),
+ a_Chunk->GetMeta(a_RelX, a_RelY, a_RelZ)
+ );
+
+ NIBBLETYPE MyMeta = a_Chunk->GetMeta(a_RelX, a_RelY, a_RelZ);
+ if (!IsAnyFluidBlock(a_Chunk->GetBlock(a_RelX, a_RelY, a_RelZ)))
+ {
+ // Can happen - if a block is scheduled for simulating and gets replaced in the meantime.
+ FLOG(" BadBlockType exit");
+ return;
+ }
+
+ if (MyMeta != 0)
+ {
+ // Source blocks aren't checked for tributaries, others are.
+ if (CheckTributaries(a_Chunk, a_RelX, a_RelY, a_RelZ, MyMeta))
+ {
+ // Has no tributary, has been decreased (in CheckTributaries()),
+ // no more processing needed (neighbors have been scheduled by the decrease)
+ FLOG(" CheckTributaries exit");
+ return;
+ }
+ }
+
+ // New meta for the spreading to neighbors:
+ // If this is a source block or was falling, the new meta is just the falloff
+ // Otherwise it is the current meta plus falloff (may be larger than max height, will be checked later)
+ NIBBLETYPE NewMeta = ((MyMeta == 0) || ((MyMeta & 0x08) != 0)) ? m_Falloff : (MyMeta + m_Falloff);
+ bool SpreadFurther = true;
+ if (a_RelY > 0)
+ {
+ BLOCKTYPE Below = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ);
+ if (IsPassableForFluid(Below) || IsBlockLava(Below) || IsBlockWater(Below))
+ {
+ // Spread only down, possibly washing away what's there or turning lava to stone / cobble / obsidian:
+ SpreadToNeighbor(a_Chunk, a_RelX, a_RelY - 1, a_RelZ, 8);
+ SpreadFurther = false;
+ }
+ // If source creation is on, check for it here:
+ else if (
+ (m_NumNeighborsForSource > 0) && // Source creation is on
+ (MyMeta == m_Falloff) && // Only exactly one block away from a source (fast bail-out)
+ !IsPassableForFluid(Below) && // Only exactly 1 block deep
+ CheckNeighborsForSource(a_Chunk, a_RelX, a_RelY, a_RelZ) // Did we create a source?
+ )
+ {
+ // We created a source, no more spreading is to be done now
+ // Also has been re-scheduled for ticking in the next wave, so no marking is needed
+ return;
+ }
+ }
+
+ if (SpreadFurther && (NewMeta < 8))
+ {
+ // Spread to the neighbors:
+ SpreadToNeighbor(a_Chunk, a_RelX - 1, a_RelY, a_RelZ, NewMeta);
+ SpreadToNeighbor(a_Chunk, a_RelX + 1, a_RelY, a_RelZ, NewMeta);
+ SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ - 1, NewMeta);
+ SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ + 1, NewMeta);
+ }
+
+ // Mark as processed:
+ a_Chunk->FastSetBlock(a_RelX, a_RelY, a_RelZ, m_StationaryFluidBlock, MyMeta);
+}
+
+
+
+
+
+bool cFloodyFluidSimulator::CheckTributaries(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_MyMeta)
+{
+ // If we have a section above, check if there's fluid above this block that would feed it:
+ if (a_RelY < cChunkDef::Height - 1)
+ {
+ if (IsAnyFluidBlock(a_Chunk->GetBlock(a_RelX, a_RelY + 1, a_RelZ)))
+ {
+ // This block is fed from above, no more processing needed
+ FLOG(" Fed from above");
+ return false;
+ }
+ }
+
+ // Not fed from above, check if there's a feed from the side (but not if it's a downward-flowing block):
+ if (a_MyMeta != 8)
+ {
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ static const Vector3i Coords[] =
+ {
+ Vector3i( 1, 0, 0),
+ Vector3i(-1, 0, 0),
+ Vector3i( 0, 0, 1),
+ Vector3i( 0, 0, -1),
+ } ;
+ for (int i = 0; i < ARRAYCOUNT(Coords); i++)
+ {
+ if (!a_Chunk->UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta))
+ {
+ continue;
+ }
+ if (IsAllowedBlock(BlockType) && IsHigherMeta(BlockMeta, a_MyMeta))
+ {
+ // This block is fed, no more processing needed
+ FLOG(" Fed from {%d, %d, %d}, type %d, meta %d",
+ a_Chunk->GetPosX() * cChunkDef::Width + a_RelX + Coords[i].x,
+ a_RelY,
+ a_Chunk->GetPosZ() * cChunkDef::Width + a_RelZ + Coords[i].z,
+ BlockType, BlockMeta
+ );
+ return false;
+ }
+ } // for i - Coords[]
+ } // if not fed from above
+
+ // Block is not fed, decrease by m_Falloff levels:
+ if (a_MyMeta >= 8)
+ {
+ FLOG(" Not fed and downwards, turning into non-downwards meta %d", m_Falloff);
+ a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, m_StationaryFluidBlock, m_Falloff);
+ }
+ else
+ {
+ a_MyMeta += m_Falloff;
+ if (a_MyMeta < 8)
+ {
+ FLOG(" Not fed, decreasing from %d to %d", a_MyMeta - m_Falloff, a_MyMeta);
+ a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, m_StationaryFluidBlock, a_MyMeta);
+ }
+ else
+ {
+ FLOG(" Not fed, meta %d, erasing altogether", a_MyMeta);
+ a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0);
+ }
+ }
+ return true;
+}
+
+
+
+
+
+void cFloodyFluidSimulator::SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta)
+{
+ ASSERT(a_NewMeta <= 8); // Invalid meta values
+ ASSERT(a_NewMeta > 0); // Source blocks aren't spread
+
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (!a_NearChunk->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta))
+ {
+ // Chunk not available
+ return;
+ }
+
+ if (IsAllowedBlock(BlockType))
+ {
+ if ((BlockMeta == a_NewMeta) || IsHigherMeta(BlockMeta, a_NewMeta))
+ {
+ // Don't spread there, there's already a higher or same level there
+ return;
+ }
+ }
+
+ // Check water - lava interaction:
+ if (m_FluidBlock == E_BLOCK_LAVA)
+ {
+ if (IsBlockWater(BlockType))
+ {
+ // Lava flowing into water, change to stone / cobblestone based on direction:
+ BLOCKTYPE NewBlock = (a_NewMeta == 8) ? E_BLOCK_STONE : E_BLOCK_COBBLESTONE;
+ FLOG(" Lava flowing into water, turning water at rel {%d, %d, %d} into stone",
+ a_RelX, a_RelY, a_RelZ,
+ ItemTypeToString(NewBlock).c_str()
+ );
+ a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, NewBlock, 0);
+
+ // TODO: Sound effect
+
+ return;
+ }
+ }
+ else if (m_FluidBlock == E_BLOCK_WATER)
+ {
+ if (IsBlockLava(BlockType))
+ {
+ // Water flowing into lava, change to cobblestone / obsidian based on dest block:
+ BLOCKTYPE NewBlock = (BlockMeta == 0) ? E_BLOCK_OBSIDIAN : E_BLOCK_COBBLESTONE;
+ FLOG(" Water flowing into lava, turning lava at rel {%d, %d, %d} into %s",
+ a_RelX, a_RelY, a_RelZ, ItemTypeToString(NewBlock).c_str()
+ );
+ a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, NewBlock, 0);
+
+ // TODO: Sound effect
+
+ return;
+ }
+ }
+ else
+ {
+ ASSERT(!"Unknown fluid!");
+ }
+
+ if (!IsPassableForFluid(BlockType))
+ {
+ // Can't spread there
+ return;
+ }
+
+ // Wash away the block there, if possible:
+ if (CanWashAway(BlockType))
+ {
+ cBlockHandler * Handler = BlockHandler(BlockType);
+ if (Handler->DoesDropOnUnsuitable())
+ {
+ Handler->DropBlock(
+ &m_World, NULL,
+ a_NearChunk->GetPosX() * cChunkDef::Width + a_RelX,
+ a_RelY,
+ a_NearChunk->GetPosZ() * cChunkDef::Width + a_RelZ
+ );
+ }
+ } // if (CanWashAway)
+
+ // Spread:
+ FLOG(" Spreading to {%d, %d, %d} with meta %d",
+ a_NearChunk->GetPosX() * cChunkDef::Width + a_RelX,
+ a_RelY,
+ a_NearChunk->GetPosZ() * cChunkDef::Width + a_RelZ,
+ a_NewMeta
+ );
+ a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta);
+}
+
+
+
+
+
+bool cFloodyFluidSimulator::CheckNeighborsForSource(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
+{
+ FLOG(" Checking neighbors for source creation");
+
+ static const Vector3i NeighborCoords[] =
+ {
+ Vector3i(-1, 0, 0),
+ Vector3i( 1, 0, 0),
+ Vector3i( 0, 0, -1),
+ Vector3i( 0, 0, 1),
+ } ;
+
+ int NumNeeded = m_NumNeighborsForSource;
+ for (int i = 0; i < ARRAYCOUNT(NeighborCoords); i++)
+ {
+ int x = a_RelX + NeighborCoords[i].x;
+ int y = a_RelY + NeighborCoords[i].y;
+ int z = a_RelZ + NeighborCoords[i].z;
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+ if (!a_Chunk->UnboundedRelGetBlock(x, y, z, BlockType, BlockMeta))
+ {
+ // Neighbor not available, skip it
+ continue;
+ }
+ // FLOG(" Neighbor at {%d, %d, %d}: %s", x, y, z, ItemToFullString(cItem(BlockType, 1, BlockMeta)).c_str());
+ if ((BlockMeta == 0) && IsAnyFluidBlock(BlockType))
+ {
+ NumNeeded--;
+ // FLOG(" Found a neighbor source at {%d, %d, %d}, NumNeeded := %d", x, y, z, NumNeeded);
+ if (NumNeeded == 0)
+ {
+ // Found enough, turn into a source and bail out
+ // FLOG(" Found enough neighbor sources, turning into a source");
+ a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, m_FluidBlock, 0);
+ return true;
+ }
+ }
+ }
+ // FLOG(" Not enough neighbors for turning into a source, NumNeeded = %d", NumNeeded);
+ return false;
+}
+
+
+
+
diff --git a/source/Simulator/FloodyFluidSimulator.h b/source/Simulator/FloodyFluidSimulator.h
index e6ce38d61..c4af2e246 100644
--- a/source/Simulator/FloodyFluidSimulator.h
+++ b/source/Simulator/FloodyFluidSimulator.h
@@ -1,53 +1,53 @@
-
-// FloodyFluidSimulator.h
-
-// Interfaces to the cFloodyFluidSimulator that represents a fluid simulator that tries to flood everything :)
-// http://forum.mc-server.org/showthread.php?tid=565
-
-
-
-
-
-#pragma once
-
-#include "DelayedFluidSimulator.h"
-
-
-
-
-
-// fwd:
-class cBlockArea;
-
-
-
-
-
-class cFloodyFluidSimulator :
- public cDelayedFluidSimulator
-{
- typedef cDelayedFluidSimulator super;
-
-public:
- cFloodyFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid, NIBBLETYPE a_Falloff, int a_TickDelay, int a_NumNeighborsForSource);
-
-protected:
- NIBBLETYPE m_Falloff;
- int m_NumNeighborsForSource;
-
- // cDelayedFluidSimulator overrides:
- virtual void SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override;
-
- /// Checks tributaries, if not fed, decreases the block's level and returns true
- bool CheckTributaries(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_MyMeta);
-
- /// Spreads into the specified block, if the blocktype there allows. a_Area is for checking.
- void SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta);
-
- /// Checks if there are enough neighbors to create a source at the coords specified; turns into source and returns true if so
- bool CheckNeighborsForSource(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ);
-} ;
-
-
-
-
+
+// FloodyFluidSimulator.h
+
+// Interfaces to the cFloodyFluidSimulator that represents a fluid simulator that tries to flood everything :)
+// http://forum.mc-server.org/showthread.php?tid=565
+
+
+
+
+
+#pragma once
+
+#include "DelayedFluidSimulator.h"
+
+
+
+
+
+// fwd:
+class cBlockArea;
+
+
+
+
+
+class cFloodyFluidSimulator :
+ public cDelayedFluidSimulator
+{
+ typedef cDelayedFluidSimulator super;
+
+public:
+ cFloodyFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid, NIBBLETYPE a_Falloff, int a_TickDelay, int a_NumNeighborsForSource);
+
+protected:
+ NIBBLETYPE m_Falloff;
+ int m_NumNeighborsForSource;
+
+ // cDelayedFluidSimulator overrides:
+ virtual void SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override;
+
+ /// Checks tributaries, if not fed, decreases the block's level and returns true
+ bool CheckTributaries(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_MyMeta);
+
+ /// Spreads into the specified block, if the blocktype there allows. a_Area is for checking.
+ void SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta);
+
+ /// Checks if there are enough neighbors to create a source at the coords specified; turns into source and returns true if so
+ bool CheckNeighborsForSource(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ);
+} ;
+
+
+
+
diff --git a/source/Simulator/NoopFluidSimulator.h b/source/Simulator/NoopFluidSimulator.h
index 9120fe868..8f894433f 100644
--- a/source/Simulator/NoopFluidSimulator.h
+++ b/source/Simulator/NoopFluidSimulator.h
@@ -1,36 +1,36 @@
-
-// NoopFluidSimulator.h
-
-// Declares the cNoopFluidSimulator class representing a fluid simulator that performs nothing, it ignores all blocks
-
-
-
-
-
-#pragma once
-
-#include "FluidSimulator.h"
-
-
-
-
-
-class cNoopFluidSimulator :
- public cFluidSimulator
-{
- typedef cFluidSimulator super;
-
-public:
- cNoopFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid) :
- super(a_World, a_Fluid, a_StationaryFluid)
- {
- }
-
- // cSimulator overrides:
- virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override {}
- virtual void Simulate(float a_Dt) override {}
-} ;
-
-
-
-
+
+// NoopFluidSimulator.h
+
+// Declares the cNoopFluidSimulator class representing a fluid simulator that performs nothing, it ignores all blocks
+
+
+
+
+
+#pragma once
+
+#include "FluidSimulator.h"
+
+
+
+
+
+class cNoopFluidSimulator :
+ public cFluidSimulator
+{
+ typedef cFluidSimulator super;
+
+public:
+ cNoopFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid) :
+ super(a_World, a_Fluid, a_StationaryFluid)
+ {
+ }
+
+ // cSimulator overrides:
+ virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override {}
+ virtual void Simulate(float a_Dt) override {}
+} ;
+
+
+
+
diff --git a/source/Simulator/VaporizeFluidSimulator.cpp b/source/Simulator/VaporizeFluidSimulator.cpp
index 792783915..4206c64d1 100644
--- a/source/Simulator/VaporizeFluidSimulator.cpp
+++ b/source/Simulator/VaporizeFluidSimulator.cpp
@@ -1,53 +1,53 @@
-
-// VaporizeFluidSimulator.cpp
-
-// Implements the cVaporizeFluidSimulator class representing a fluid simulator that replaces all fluid blocks with air
-
-#include "Globals.h"
-#include "VaporizeFluidSimulator.h"
-#include "../Chunk.h"
-
-
-
-
-
-cVaporizeFluidSimulator::cVaporizeFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid) :
- super(a_World, a_Fluid, a_StationaryFluid)
-{
-}
-
-
-
-
-
-void cVaporizeFluidSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk)
-{
- if (a_Chunk == NULL)
- {
- return;
- }
- int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width;
- int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width;
- BLOCKTYPE BlockType = a_Chunk->GetBlock(RelX, a_BlockY, RelZ);
- if (
- (BlockType == m_FluidBlock) ||
- (BlockType == m_StationaryFluidBlock)
- )
- {
- a_Chunk->SetBlock(RelX, a_BlockY, RelZ, E_BLOCK_AIR, 0);
- a_Chunk->BroadcastSoundEffect("random.fizz", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 1.0f, 0.6f);
- }
-}
-
-
-
-
-
-void cVaporizeFluidSimulator::Simulate(float a_Dt)
-{
- // Nothing needed
-}
-
-
-
-
+
+// VaporizeFluidSimulator.cpp
+
+// Implements the cVaporizeFluidSimulator class representing a fluid simulator that replaces all fluid blocks with air
+
+#include "Globals.h"
+#include "VaporizeFluidSimulator.h"
+#include "../Chunk.h"
+
+
+
+
+
+cVaporizeFluidSimulator::cVaporizeFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid) :
+ super(a_World, a_Fluid, a_StationaryFluid)
+{
+}
+
+
+
+
+
+void cVaporizeFluidSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk)
+{
+ if (a_Chunk == NULL)
+ {
+ return;
+ }
+ int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width;
+ int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width;
+ BLOCKTYPE BlockType = a_Chunk->GetBlock(RelX, a_BlockY, RelZ);
+ if (
+ (BlockType == m_FluidBlock) ||
+ (BlockType == m_StationaryFluidBlock)
+ )
+ {
+ a_Chunk->SetBlock(RelX, a_BlockY, RelZ, E_BLOCK_AIR, 0);
+ a_Chunk->BroadcastSoundEffect("random.fizz", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 1.0f, 0.6f);
+ }
+}
+
+
+
+
+
+void cVaporizeFluidSimulator::Simulate(float a_Dt)
+{
+ // Nothing needed
+}
+
+
+
+
diff --git a/source/Simulator/VaporizeFluidSimulator.h b/source/Simulator/VaporizeFluidSimulator.h
index c179c8ec4..c8eb7802b 100644
--- a/source/Simulator/VaporizeFluidSimulator.h
+++ b/source/Simulator/VaporizeFluidSimulator.h
@@ -1,34 +1,34 @@
-
-// VaporizeFluidSimulator.h
-
-// Declares the cVaporizeFluidSimulator class representing a fluid simulator that replaces all fluid blocks with air
-// Useful for water simulation in the Nether
-
-
-
-
-
-#pragma once
-
-#include "FluidSimulator.h"
-
-
-
-
-
-class cVaporizeFluidSimulator :
- public cFluidSimulator
-{
- typedef cFluidSimulator super;
-
-public:
- cVaporizeFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid);
-
- // cSimulator overrides:
- virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override;
- virtual void Simulate(float a_Dt) override;
-} ;
-
-
-
-
+
+// VaporizeFluidSimulator.h
+
+// Declares the cVaporizeFluidSimulator class representing a fluid simulator that replaces all fluid blocks with air
+// Useful for water simulation in the Nether
+
+
+
+
+
+#pragma once
+
+#include "FluidSimulator.h"
+
+
+
+
+
+class cVaporizeFluidSimulator :
+ public cFluidSimulator
+{
+ typedef cFluidSimulator super;
+
+public:
+ cVaporizeFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid);
+
+ // cSimulator overrides:
+ virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override;
+ virtual void Simulate(float a_Dt) override;
+} ;
+
+
+
+
diff --git a/source/SquirrelCommandBinder.cpp b/source/SquirrelCommandBinder.cpp
index 9ff8a43f9..66b456ace 100644
--- a/source/SquirrelCommandBinder.cpp
+++ b/source/SquirrelCommandBinder.cpp
@@ -1,118 +1,118 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-
-
-
-
-#ifdef USE_SQUIRREL
-
-
-
-
-
-#include "SquirrelCommandBinder.h"
-#include "Player.h"
-#include "Plugin.h"
-#include "Plugin_Squirrel.h"
-#include "squirrelbindings/SquirrelArray.h"
-
-
-cSquirrelCommandBinder::cSquirrelCommandBinder()
-{
-}
-
-cSquirrelCommandBinder::~cSquirrelCommandBinder()
-{
-}
-
-void cSquirrelCommandBinder::ClearBindings()
-{
- m_BoundCommands.clear();
-}
-
-void cSquirrelCommandBinder::RemoveBindingsForPlugin( cPlugin* a_Plugin )
-{
- for( CommandMap::iterator itr = m_BoundCommands.begin(); itr != m_BoundCommands.end(); )
- {
- if( itr->second.Plugin == a_Plugin )
- {
- LOGINFO("Unbinding %s ", itr->first.c_str( ) );
- CommandMap::iterator eraseme = itr;
- ++itr;
- m_BoundCommands.erase( eraseme );
- continue;
- }
- ++itr;
- }
-}
-
-bool cSquirrelCommandBinder::BindCommand( const std::string & a_Command, const std::string & a_Permission, cPlugin* a_Plugin, Sqrat::Function a_Callback )
-{
- if( !a_Plugin->CanBindCommands() )
- {
- LOGERROR("ERROR: Trying to bind command \"%s\" to a plugin that is not initialized.", a_Command.c_str() );
- return false;
- }
- if( m_BoundCommands.find( a_Command ) != m_BoundCommands.end() )
- {
- LOGERROR("ERROR: Trying to bind command \"%s\" that has already been bound.", a_Command.c_str() );
- return false;
- }
- LOGINFO("Binding %s (%s)", a_Command.c_str(), a_Permission.c_str() );
-
- BoundFunction Callback;
- Callback.Callback = a_Callback;
- Callback.Plugin = a_Plugin;
- Callback.Permission = a_Permission;
-
- m_BoundCommands[ a_Command ] = Callback;
- return true;
-}
-
-bool cSquirrelCommandBinder::HandleCommand( const std::string & a_Command, cPlayer* a_Player )
-{
- AStringVector Split = StringSplit(a_Command, " ");
- if (Split.size() == 0)
- {
- return false;
- }
-
- CommandMap::iterator FoundCommand = m_BoundCommands.find( Split[0] );
- if( FoundCommand != m_BoundCommands.end() )
- {
- const BoundFunction & func = FoundCommand->second;
- if( func.Permission.size() > 0 )
- {
- if( !a_Player->HasPermission( func.Permission.c_str() ) )
- {
- return false;
- }
- }
-
-
- // Push the split
- SquirrelStringArray SplitData;
-
- std::vector<std::string>::const_iterator iter = Split.begin();
- while(iter != Split.end()) {
- SplitData.Add(*iter);
- ++iter;
- }
-
- // Push player
- Sqrat::Function callback = func.Callback;
- return callback.Evaluate<bool>(&SplitData, a_Player);
- }
- return false;
-}
-
-
-
-
-
-#endif // USE_SQUIRREL
-
-
-
-
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+
+
+
+
+#ifdef USE_SQUIRREL
+
+
+
+
+
+#include "SquirrelCommandBinder.h"
+#include "Player.h"
+#include "Plugin.h"
+#include "Plugin_Squirrel.h"
+#include "squirrelbindings/SquirrelArray.h"
+
+
+cSquirrelCommandBinder::cSquirrelCommandBinder()
+{
+}
+
+cSquirrelCommandBinder::~cSquirrelCommandBinder()
+{
+}
+
+void cSquirrelCommandBinder::ClearBindings()
+{
+ m_BoundCommands.clear();
+}
+
+void cSquirrelCommandBinder::RemoveBindingsForPlugin( cPlugin* a_Plugin )
+{
+ for( CommandMap::iterator itr = m_BoundCommands.begin(); itr != m_BoundCommands.end(); )
+ {
+ if( itr->second.Plugin == a_Plugin )
+ {
+ LOGINFO("Unbinding %s ", itr->first.c_str( ) );
+ CommandMap::iterator eraseme = itr;
+ ++itr;
+ m_BoundCommands.erase( eraseme );
+ continue;
+ }
+ ++itr;
+ }
+}
+
+bool cSquirrelCommandBinder::BindCommand( const std::string & a_Command, const std::string & a_Permission, cPlugin* a_Plugin, Sqrat::Function a_Callback )
+{
+ if( !a_Plugin->CanBindCommands() )
+ {
+ LOGERROR("ERROR: Trying to bind command \"%s\" to a plugin that is not initialized.", a_Command.c_str() );
+ return false;
+ }
+ if( m_BoundCommands.find( a_Command ) != m_BoundCommands.end() )
+ {
+ LOGERROR("ERROR: Trying to bind command \"%s\" that has already been bound.", a_Command.c_str() );
+ return false;
+ }
+ LOGINFO("Binding %s (%s)", a_Command.c_str(), a_Permission.c_str() );
+
+ BoundFunction Callback;
+ Callback.Callback = a_Callback;
+ Callback.Plugin = a_Plugin;
+ Callback.Permission = a_Permission;
+
+ m_BoundCommands[ a_Command ] = Callback;
+ return true;
+}
+
+bool cSquirrelCommandBinder::HandleCommand( const std::string & a_Command, cPlayer* a_Player )
+{
+ AStringVector Split = StringSplit(a_Command, " ");
+ if (Split.size() == 0)
+ {
+ return false;
+ }
+
+ CommandMap::iterator FoundCommand = m_BoundCommands.find( Split[0] );
+ if( FoundCommand != m_BoundCommands.end() )
+ {
+ const BoundFunction & func = FoundCommand->second;
+ if( func.Permission.size() > 0 )
+ {
+ if( !a_Player->HasPermission( func.Permission.c_str() ) )
+ {
+ return false;
+ }
+ }
+
+
+ // Push the split
+ SquirrelStringArray SplitData;
+
+ std::vector<std::string>::const_iterator iter = Split.begin();
+ while(iter != Split.end()) {
+ SplitData.Add(*iter);
+ ++iter;
+ }
+
+ // Push player
+ Sqrat::Function callback = func.Callback;
+ return callback.Evaluate<bool>(&SplitData, a_Player);
+ }
+ return false;
+}
+
+
+
+
+
+#endif // USE_SQUIRREL
+
+
+
+
diff --git a/source/SquirrelCommandBinder.h b/source/SquirrelCommandBinder.h
index c2bc4bafe..49e6fd003 100644
--- a/source/SquirrelCommandBinder.h
+++ b/source/SquirrelCommandBinder.h
@@ -1,51 +1,51 @@
-
-#pragma once
-
-
-
-
-
-#ifdef USE_SQUIRREL
-
-
-
-
-
-#include <sqrat.h>
-
-class cPlugin;
-class cPlayer;
-
-class cSquirrelCommandBinder
-{
-public:
- cSquirrelCommandBinder();
- ~cSquirrelCommandBinder();
-
- bool HandleCommand( const std::string & a_Command, cPlayer* a_Player );
-
- bool BindCommand( const std::string & a_Command, const std::string & a_Permission, cPlugin* a_Plugin, Sqrat::Function a_Callback);
-
- void ClearBindings();
- void RemoveBindingsForPlugin( cPlugin* a_Plugin );
-private:
- struct BoundFunction
- {
- Sqrat::Function Callback;
- cPlugin *Plugin;
- std::string Permission;
- };
-
- typedef std::map< std::string, BoundFunction > CommandMap;
- CommandMap m_BoundCommands;
-};
-
-
-
-
-
-#endif // USE_SQUIRREL
-
-
-
-
+
+#pragma once
+
+
+
+
+
+#ifdef USE_SQUIRREL
+
+
+
+
+
+#include <sqrat.h>
+
+class cPlugin;
+class cPlayer;
+
+class cSquirrelCommandBinder
+{
+public:
+ cSquirrelCommandBinder();
+ ~cSquirrelCommandBinder();
+
+ bool HandleCommand( const std::string & a_Command, cPlayer* a_Player );
+
+ bool BindCommand( const std::string & a_Command, const std::string & a_Permission, cPlugin* a_Plugin, Sqrat::Function a_Callback);
+
+ void ClearBindings();
+ void RemoveBindingsForPlugin( cPlugin* a_Plugin );
+private:
+ struct BoundFunction
+ {
+ Sqrat::Function Callback;
+ cPlugin *Plugin;
+ std::string Permission;
+ };
+
+ typedef std::map< std::string, BoundFunction > CommandMap;
+ CommandMap m_BoundCommands;
+};
+
+
+
+
+
+#endif // USE_SQUIRREL
+
+
+
+
diff --git a/source/TNTEntity.cpp b/source/TNTEntity.cpp
index 22f343e88..e431464b3 100644
--- a/source/TNTEntity.cpp
+++ b/source/TNTEntity.cpp
@@ -1,72 +1,72 @@
-#include "Globals.h"
-
-#include "TNTEntity.h"
-#include "World.h"
-#include "ClientHandle.h"
-
-
-
-
-
-cTNTEntity::cTNTEntity(double a_X, double a_Y, double a_Z, float a_FuseTimeInSec) :
- super(etTNT, a_X, a_Y, a_Z, 0.98, 0.98),
- m_Counter(0),
- m_MaxFuseTime(a_FuseTimeInSec)
-{
-}
-
-
-
-
-
-cTNTEntity::cTNTEntity(const Vector3d & a_Pos, float a_FuseTimeInSec) :
- super(etTNT, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98),
- m_Counter(0),
- m_MaxFuseTime(a_FuseTimeInSec)
-{
-}
-
-
-
-
-void cTNTEntity::Initialize(cWorld * a_World)
-{
- super::Initialize(a_World);
- a_World->BroadcastSpawnEntity(*this);
-}
-
-
-
-
-
-void cTNTEntity::SpawnOn(cClientHandle & a_ClientHandle)
-{
- a_ClientHandle.SendSpawnObject(*this, 50, 1, 0, 0); // 50 means TNT
- m_bDirtyPosition = false;
- m_bDirtySpeed = false;
- m_bDirtyOrientation = false;
- m_bDirtyHead = false;
-}
-
-
-
-
-
-void cTNTEntity::Tick(float a_Dt, cChunk & a_Chunk)
-{
- super::Tick(a_Dt, a_Chunk);
- BroadcastMovementUpdate();
- float delta_time = a_Dt / 1000; // Convert miliseconds to seconds
- m_Counter += delta_time;
- if (m_Counter > m_MaxFuseTime) // Check if we go KABOOOM
- {
- Destroy(true);
- LOGD("BOOM at {%f,%f,%f}", GetPosX(), GetPosY(), GetPosZ());
- m_World->DoExplosiontAt(4.0, (int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()));
- return;
- }
-}
-
-
-
-
+#include "Globals.h"
+
+#include "TNTEntity.h"
+#include "World.h"
+#include "ClientHandle.h"
+
+
+
+
+
+cTNTEntity::cTNTEntity(double a_X, double a_Y, double a_Z, float a_FuseTimeInSec) :
+ super(etTNT, a_X, a_Y, a_Z, 0.98, 0.98),
+ m_Counter(0),
+ m_MaxFuseTime(a_FuseTimeInSec)
+{
+}
+
+
+
+
+
+cTNTEntity::cTNTEntity(const Vector3d & a_Pos, float a_FuseTimeInSec) :
+ super(etTNT, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98),
+ m_Counter(0),
+ m_MaxFuseTime(a_FuseTimeInSec)
+{
+}
+
+
+
+
+void cTNTEntity::Initialize(cWorld * a_World)
+{
+ super::Initialize(a_World);
+ a_World->BroadcastSpawnEntity(*this);
+}
+
+
+
+
+
+void cTNTEntity::SpawnOn(cClientHandle & a_ClientHandle)
+{
+ a_ClientHandle.SendSpawnObject(*this, 50, 1, 0, 0); // 50 means TNT
+ m_bDirtyPosition = false;
+ m_bDirtySpeed = false;
+ m_bDirtyOrientation = false;
+ m_bDirtyHead = false;
+}
+
+
+
+
+
+void cTNTEntity::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+ BroadcastMovementUpdate();
+ float delta_time = a_Dt / 1000; // Convert miliseconds to seconds
+ m_Counter += delta_time;
+ if (m_Counter > m_MaxFuseTime) // Check if we go KABOOOM
+ {
+ Destroy(true);
+ LOGD("BOOM at {%f,%f,%f}", GetPosX(), GetPosY(), GetPosZ());
+ m_World->DoExplosiontAt(4.0, (int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()));
+ return;
+ }
+}
+
+
+
+
diff --git a/source/TNTEntity.h b/source/TNTEntity.h
index d20b6aca4..5dbfd2d43 100644
--- a/source/TNTEntity.h
+++ b/source/TNTEntity.h
@@ -1,34 +1,34 @@
-
-#pragma once
-
-#include "Entity.h"
-#include "Defines.h"
-
-
-
-
-
-class cTNTEntity :
- public cEntity
-{
- typedef cEntity super;
-
-public:
- CLASS_PROTODEF(cTNTEntity);
-
- cTNTEntity(double a_X, double a_Y, double a_Z, float a_FuseTimeInSec);
- cTNTEntity(const Vector3d & a_Pos, float a_FuseTimeInSec);
-
- // cEntity overrides:
- virtual void Initialize(cWorld * a_World) override;
- virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
- virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
-
-protected:
- float m_Counter; ///< How much time has elapsed since the object was created, in seconds
- float m_MaxFuseTime; ///< How long the fuse is, in seconds
-};
-
-
-
-
+
+#pragma once
+
+#include "Entity.h"
+#include "Defines.h"
+
+
+
+
+
+class cTNTEntity :
+ public cEntity
+{
+ typedef cEntity super;
+
+public:
+ CLASS_PROTODEF(cTNTEntity);
+
+ cTNTEntity(double a_X, double a_Y, double a_Z, float a_FuseTimeInSec);
+ cTNTEntity(const Vector3d & a_Pos, float a_FuseTimeInSec);
+
+ // cEntity overrides:
+ virtual void Initialize(cWorld * a_World) override;
+ virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+
+protected:
+ float m_Counter; ///< How much time has elapsed since the object was created, in seconds
+ float m_MaxFuseTime; ///< How long the fuse is, in seconds
+};
+
+
+
+
diff --git a/source/UI/SlotArea.cpp b/source/UI/SlotArea.cpp
index fc3f248f5..d255e0cd9 100644
--- a/source/UI/SlotArea.cpp
+++ b/source/UI/SlotArea.cpp
@@ -1,815 +1,815 @@
-
-// SlotArea.cpp
-
-// Implements the cSlotArea class and its descendants
-
-#include "Globals.h"
-#include "SlotArea.h"
-#include "../Player.h"
-#include "../BlockEntities/ChestEntity.h"
-#include "../BlockEntities/DropSpenserEntity.h"
-#include "../BlockEntities/FurnaceEntity.h"
-#include "../Items/ItemHandler.h"
-#include "Window.h"
-#include "../CraftingRecipes.h"
-#include "../Root.h"
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cSlotArea:
-
-cSlotArea::cSlotArea(int a_NumSlots, cWindow & a_ParentWindow) :
- m_NumSlots(a_NumSlots),
- m_ParentWindow(a_ParentWindow)
-{
-}
-
-
-
-
-
-void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
-{
- /*
- LOGD("Slot area with %d slots clicked at slot number %d, clicked item %s, slot item %s",
- GetNumSlots(), a_SlotNum,
- ItemToFullString(a_ClickedItem).c_str(),
- ItemToFullString(*GetSlot(a_SlotNum, a_Player)).c_str()
- );
- */
-
- ASSERT((a_SlotNum >= 0) && (a_SlotNum < GetNumSlots()));
-
- bool bAsync = false;
- if (GetSlot(a_SlotNum, a_Player) == NULL)
- {
- LOGWARNING("GetSlot(%d) returned NULL! Ignoring click", a_SlotNum);
- return;
- }
-
- if ((a_ClickAction == caShiftLeftClick) || (a_ClickAction == caShiftRightClick))
- {
- if (!a_Player.IsDraggingItem())
- {
- ShiftClicked(a_Player, a_SlotNum, a_ClickedItem);
- return;
- }
- LOGD("Shift clicked, but the player is dragging an item: %s", ItemToFullString(a_Player.GetDraggingItem()).c_str());
- return;
- }
-
- cItem Slot(*GetSlot(a_SlotNum, a_Player));
- if (!Slot.IsSameType(a_ClickedItem))
- {
- LOGWARNING("*** Window lost sync at item %d in SlotArea with %d items ***", a_SlotNum, m_NumSlots);
- LOGWARNING("My item: %s", ItemToFullString(Slot).c_str());
- LOGWARNING("Their item: %s", ItemToFullString(a_ClickedItem).c_str());
- bAsync = true;
- }
- cItem & DraggingItem = a_Player.GetDraggingItem();
- switch (a_ClickAction)
- {
- case caRightClick:
- {
- if (DraggingItem.m_ItemType <= 0) // Empty-handed?
- {
- DraggingItem.m_ItemCount = (char)(((float)Slot.m_ItemCount) / 2.f + 0.5f);
- Slot.m_ItemCount -= DraggingItem.m_ItemCount;
- DraggingItem.m_ItemType = Slot.m_ItemType;
- DraggingItem.m_ItemDamage = Slot.m_ItemDamage;
-
- if (Slot.m_ItemCount <= 0)
- {
- Slot.Empty();
- }
- }
- else if ((Slot.m_ItemType <= 0) || DraggingItem.IsEqual(Slot))
- {
- // Drop one item in slot
- cItemHandler * Handler = ItemHandler(Slot.m_ItemType);
- if ((DraggingItem.m_ItemCount > 0) && (Slot.m_ItemCount < Handler->GetMaxStackSize()))
- {
- Slot.m_ItemType = DraggingItem.m_ItemType;
- Slot.m_ItemCount++;
- Slot.m_ItemDamage = DraggingItem.m_ItemDamage;
- DraggingItem.m_ItemCount--;
- }
- if (DraggingItem.m_ItemCount <= 0)
- {
- DraggingItem.Empty();
- }
- }
- else if (!DraggingItem.IsEqual(Slot))
- {
- // Swap contents
- cItem tmp(DraggingItem);
- DraggingItem = Slot;
- Slot = tmp;
- }
- break;
- }
-
- case caLeftClick:
- {
- // Left-clicked
- if (!DraggingItem.IsEqual(Slot))
- {
- // Switch contents
- cItem tmp(DraggingItem);
- DraggingItem = Slot;
- Slot = tmp;
- }
- else
- {
- // Same type, add items:
- cItemHandler * Handler = ItemHandler(DraggingItem.m_ItemType);
- int FreeSlots = Handler->GetMaxStackSize() - Slot.m_ItemCount;
- if (FreeSlots < 0)
- {
- ASSERT(!"Bad item stack size - where did we get more items in a slot than allowed?");
- FreeSlots = 0;
- }
- int Filling = (FreeSlots > DraggingItem.m_ItemCount) ? DraggingItem.m_ItemCount : FreeSlots;
- Slot.m_ItemCount += (char)Filling;
- DraggingItem.m_ItemCount -= (char)Filling;
- if (DraggingItem.m_ItemCount <= 0)
- {
- DraggingItem.Empty();
- }
- }
- break;
- }
- default:
- {
- LOGWARNING("SlotArea: Unhandled click action: %d (%s)", a_ClickAction, ClickActionToString(a_ClickAction));
- m_ParentWindow.BroadcastWholeWindow();
- return;
- }
- } // switch (a_ClickAction
-
- SetSlot(a_SlotNum, a_Player, Slot);
- if (bAsync)
- {
- m_ParentWindow.BroadcastWholeWindow();
- }
-
-}
-
-
-
-
-
-void cSlotArea::ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem)
-{
- // Make a copy of the slot, distribute it among the other areas, then update the slot to contain the leftover:
- cItem Slot(*GetSlot(a_SlotNum, a_Player));
- m_ParentWindow.DistributeStack(Slot, a_Player, this, true);
- if (Slot.IsEmpty())
- {
- // Empty the slot completely, the cilent doesn't like left-over ItemType with zero count
- Slot.Empty();
- }
- SetSlot(a_SlotNum, a_Player, Slot);
-
- // Some clients try to guess our actions and not always right (armor slots in 1.2.5), so we fix them:
- m_ParentWindow.BroadcastWholeWindow();
-}
-
-
-
-
-
-void cSlotArea::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_Apply, bool a_KeepEmptySlots)
-{
- for (int i = 0; i < m_NumSlots; i++)
- {
- const cItem * Slot = GetSlot(i, a_Player);
- if (!Slot->IsSameType(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots))
- {
- // Different items
- continue;
- }
- int NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount;
- if (NumFit <= 0)
- {
- // Full stack already
- continue;
- }
- if (NumFit > a_ItemStack.m_ItemCount)
- {
- NumFit = a_ItemStack.m_ItemCount;
- }
- if (a_Apply)
- {
- cItem NewSlot(a_ItemStack);
- NewSlot.m_ItemCount = Slot->m_ItemCount + NumFit;
- SetSlot(i, a_Player, NewSlot);
- }
- a_ItemStack.m_ItemCount -= NumFit;
- if (a_ItemStack.IsEmpty())
- {
- return;
- }
- } // for i - Slots
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cSlotAreaChest:
-
-cSlotAreaChest::cSlotAreaChest(cChestEntity * a_Chest, cWindow & a_ParentWindow) :
- cSlotArea(27, a_ParentWindow),
- m_Chest(a_Chest)
-{
-}
-
-
-
-
-
-const cItem * cSlotAreaChest::GetSlot(int a_SlotNum, cPlayer & a_Player) const
-{
- // a_SlotNum ranges from 0 to 26, use that to index the chest entity's inventory directly:
- return &(m_Chest->GetSlot(a_SlotNum));
-}
-
-
-
-
-
-void cSlotAreaChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
-{
- m_Chest->SetSlot(a_SlotNum, a_Item);
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cSlotAreaDoubleChest:
-
-cSlotAreaDoubleChest::cSlotAreaDoubleChest(cChestEntity * a_TopChest, cChestEntity * a_BottomChest, cWindow & a_ParentWindow) :
- cSlotArea(54, a_ParentWindow),
- m_TopChest(a_TopChest),
- m_BottomChest(a_BottomChest)
-{
-}
-
-
-
-
-
-const cItem * cSlotAreaDoubleChest::GetSlot(int a_SlotNum, cPlayer & a_Player) const
-{
- // a_SlotNum ranges from 0 to 53, use that to index the correct chest's inventory:
- if (a_SlotNum < 27)
- {
- return &(m_TopChest->GetSlot(a_SlotNum));
- }
- else
- {
- return &(m_BottomChest->GetSlot(a_SlotNum - 27));
- }
-}
-
-
-
-
-
-void cSlotAreaDoubleChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
-{
- if (a_SlotNum < 27)
- {
- m_TopChest->SetSlot(a_SlotNum, a_Item);
- }
- else
- {
- m_BottomChest->SetSlot(a_SlotNum - 27, a_Item);
- }
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cSlotAreaCrafting:
-
-cSlotAreaCrafting::cSlotAreaCrafting(int a_GridSize, cWindow & a_ParentWindow) :
- cSlotAreaTemporary(1 + a_GridSize * a_GridSize, a_ParentWindow),
- m_GridSize(a_GridSize)
-{
- ASSERT((a_GridSize == 2) || (a_GridSize == 3));
-}
-
-
-
-
-
-void cSlotAreaCrafting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
-{
- // Override for craft result slot
- if (a_SlotNum == 0)
- {
- if ((a_ClickAction == caShiftLeftClick) || (a_ClickAction == caShiftRightClick))
- {
- ShiftClickedResult(a_Player);
- }
- else
- {
- ClickedResult(a_Player);
- }
- return;
- }
- super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem);
- UpdateRecipe(a_Player);
-}
-
-
-
-
-
-void cSlotAreaCrafting::OnPlayerRemoved(cPlayer & a_Player)
-{
- // Toss all items on the crafting grid:
- TossItems(a_Player, 1, m_NumSlots);
-
- // Remove the current recipe from the player -> recipe map:
- for (cRecipeMap::iterator itr = m_Recipes.begin(), end = m_Recipes.end(); itr != end; ++itr)
- {
- if (itr->first == a_Player.GetUniqueID())
- {
- // Remove the player from the recipe map:
- m_Recipes.erase(itr);
- return;
- }
- } // for itr - m_Recipes[]
- // Player not found - that is acceptable
-}
-
-
-
-
-
-void cSlotAreaCrafting::ClickedResult(cPlayer & a_Player)
-{
- const cItem * ResultSlot = GetSlot(0, a_Player);
- cItem & DraggingItem = a_Player.GetDraggingItem();
-
- // Get the current recipe:
- cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player);
-
- cItem * PlayerSlots = GetPlayerSlots(a_Player) + 1;
- cCraftingGrid Grid(PlayerSlots, m_GridSize, m_GridSize);
-
- // If possible, craft:
- if (DraggingItem.IsEmpty())
- {
- DraggingItem = Recipe.GetResult();
- Recipe.ConsumeIngredients(Grid);
- Grid.CopyToItems(PlayerSlots);
- }
- else if (DraggingItem.IsEqual(Recipe.GetResult()))
- {
- cItemHandler * Handler = ItemHandler(Recipe.GetResult().m_ItemType);
- if (DraggingItem.m_ItemCount + Recipe.GetResult().m_ItemCount <= Handler->GetMaxStackSize())
- {
- DraggingItem.m_ItemCount += Recipe.GetResult().m_ItemCount;
- Recipe.ConsumeIngredients(Grid);
- Grid.CopyToItems(PlayerSlots);
- }
- }
-
- // Get the new recipe and update the result slot:
- UpdateRecipe(a_Player);
-
- // We're done. Send all changes to the client and bail out:
- m_ParentWindow.BroadcastWholeWindow();
-}
-
-
-
-
-
-void cSlotAreaCrafting::ShiftClickedResult(cPlayer & a_Player)
-{
- cItem Result(*GetSlot(0, a_Player));
- if (Result.IsEmpty())
- {
- return;
- }
- cItem * PlayerSlots = GetPlayerSlots(a_Player) + 1;
- do
- {
- // Try distributing the result. If it fails, bail out:
- cItem ResultCopy(Result);
- m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, false);
- if (!ResultCopy.IsEmpty())
- {
- // Couldn't distribute all of it. Bail out
- return;
- }
-
- // Distribute the result, this time for real:
- ResultCopy = Result;
- m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, true);
-
- // Remove the ingredients from the crafting grid and update the recipe:
- cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player);
- cCraftingGrid Grid(PlayerSlots, m_GridSize, m_GridSize);
- Recipe.ConsumeIngredients(Grid);
- Grid.CopyToItems(PlayerSlots);
- UpdateRecipe(a_Player);
- if (!Recipe.GetResult().IsEqual(Result))
- {
- // The recipe has changed, bail out
- return;
- }
- } while (true);
-}
-
-
-
-
-
-void cSlotAreaCrafting::UpdateRecipe(cPlayer & a_Player)
-{
- cCraftingGrid Grid(GetPlayerSlots(a_Player) + 1, m_GridSize, m_GridSize);
- cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player);
- cRoot::Get()->GetCraftingRecipes()->GetRecipe(&a_Player, Grid, Recipe);
- SetSlot(0, a_Player, Recipe.GetResult());
- m_ParentWindow.SendSlot(a_Player, this, 0);
-}
-
-
-
-
-
-cCraftingRecipe & cSlotAreaCrafting::GetRecipeForPlayer(cPlayer & a_Player)
-{
- for (cRecipeMap::iterator itr = m_Recipes.begin(), end = m_Recipes.end(); itr != end; ++itr)
- {
- if (itr->first == a_Player.GetUniqueID())
- {
- return itr->second;
- }
- } // for itr - m_Recipes[]
-
- // Not found. Add a new one:
- cCraftingGrid Grid(GetPlayerSlots(a_Player) + 1, m_GridSize, m_GridSize);
- cCraftingRecipe Recipe(Grid);
- cRoot::Get()->GetCraftingRecipes()->GetRecipe(&a_Player, Grid, Recipe);
- m_Recipes.push_back(std::make_pair(a_Player.GetUniqueID(), Recipe));
- return m_Recipes.back().second;
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cSlotAreaFurnace:
-
-cSlotAreaFurnace::cSlotAreaFurnace(cFurnaceEntity * a_Furnace, cWindow & a_ParentWindow) :
- cSlotArea(3, a_ParentWindow),
- m_Furnace(a_Furnace)
-{
- m_Furnace->GetContents().AddListener(*this);
-}
-
-
-
-
-
-cSlotAreaFurnace::~cSlotAreaFurnace()
-{
- m_Furnace->GetContents().RemoveListener(*this);
-}
-
-
-
-
-
-void cSlotAreaFurnace::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
-{
- super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem);
-
- if (m_Furnace == NULL)
- {
- LOGERROR("cSlotAreaFurnace::Clicked(): m_Furnace == NULL");
- ASSERT(!"cSlotAreaFurnace::Clicked(): m_Furnace == NULL");
- return;
- }
-}
-
-
-
-
-
-const cItem * cSlotAreaFurnace::GetSlot(int a_SlotNum, cPlayer & a_Player) const
-{
- // a_SlotNum ranges from 0 to 2, query the items from the underlying furnace:
- return &(m_Furnace->GetSlot(a_SlotNum));
-}
-
-
-
-
-
-void cSlotAreaFurnace::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
-{
- m_Furnace->SetSlot(a_SlotNum, a_Item);
-}
-
-
-
-
-
-void cSlotAreaFurnace::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
-{
- // Something has changed in the window, broadcast the entire window to all clients
- ASSERT(a_ItemGrid == &(m_Furnace->GetContents()));
-
- m_ParentWindow.BroadcastWholeWindow();
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cSlotAreaInventoryBase:
-
-cSlotAreaInventoryBase::cSlotAreaInventoryBase(int a_NumSlots, int a_SlotOffset, cWindow & a_ParentWindow) :
- cSlotArea(a_NumSlots, a_ParentWindow),
- m_SlotOffset(a_SlotOffset)
-{
-}
-
-
-
-
-
-void cSlotAreaInventoryBase::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
-{
- if ((a_Player.GetGameMode() == eGameMode_Creative) && (m_ParentWindow.GetWindowType() == cWindow::Inventory))
- {
- // Creative inventory must treat a_ClickedItem as a DraggedItem instead, replacing the inventory slot with it
- SetSlot(a_SlotNum, a_Player, a_ClickedItem);
- return;
- }
-
- // Survival inventory and all other windows' inventory has the same handling as normal slot areas
- super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem);
- return;
-}
-
-
-
-
-
-const cItem * cSlotAreaInventoryBase::GetSlot(int a_SlotNum, cPlayer & a_Player) const
-{
- // a_SlotNum ranges from 0 to 35, map that to the player's inventory slots according to the internal offset
- return &a_Player.GetInventory().GetSlot(a_SlotNum + m_SlotOffset);
-}
-
-
-
-
-
-void cSlotAreaInventoryBase::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
-{
- a_Player.GetInventory().SetSlot(a_SlotNum + m_SlotOffset, a_Item);
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cSlotAreaArmor:
-
-void cSlotAreaArmor::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots)
-{
- if (ItemCategory::IsHelmet(a_ItemStack.m_ItemType) && GetSlot(0, a_Player)->IsEmpty())
- {
- if (a_ShouldApply)
- {
- SetSlot(0, a_Player, a_ItemStack.CopyOne());
- }
- a_ItemStack.m_ItemCount -= 1;
- }
- else if (ItemCategory::IsChestPlate(a_ItemStack.m_ItemType) && GetSlot(1, a_Player)->IsEmpty())
- {
- if (a_ShouldApply)
- {
- SetSlot(1, a_Player, a_ItemStack.CopyOne());
- }
- a_ItemStack.m_ItemCount -= 1;
- }
- else if (ItemCategory::IsLeggings(a_ItemStack.m_ItemType) && GetSlot(2, a_Player)->IsEmpty())
- {
- if (a_ShouldApply)
- {
- SetSlot(2, a_Player, a_ItemStack.CopyOne());
- }
- a_ItemStack.m_ItemCount -= 1;
- }
- else if (ItemCategory::IsBoots(a_ItemStack.m_ItemType) && GetSlot(3, a_Player)->IsEmpty())
- {
- if (a_ShouldApply)
- {
- SetSlot(3, a_Player, a_ItemStack.CopyOne());
- }
- a_ItemStack.m_ItemCount -= 1;
- }
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cSlotAreaItemGrid:
-
-cSlotAreaItemGrid::cSlotAreaItemGrid(cItemGrid & a_ItemGrid, cWindow & a_ParentWindow) :
- super(a_ItemGrid.GetNumSlots(), a_ParentWindow),
- m_ItemGrid(a_ItemGrid)
-{
- m_ItemGrid.AddListener(*this);
-}
-
-
-
-
-
-cSlotAreaItemGrid::~cSlotAreaItemGrid()
-{
- m_ItemGrid.RemoveListener(*this);
-}
-
-
-
-
-
-const cItem * cSlotAreaItemGrid::GetSlot(int a_SlotNum, cPlayer & a_Player) const
-{
- return &m_ItemGrid.GetSlot(a_SlotNum);
-}
-
-
-
-
-
-void cSlotAreaItemGrid::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
-{
- m_ItemGrid.SetSlot(a_SlotNum, a_Item);
-}
-
-
-
-
-
-void cSlotAreaItemGrid::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
-{
- ASSERT(a_ItemGrid == &m_ItemGrid);
- m_ParentWindow.BroadcastSlot(this, a_SlotNum);
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cSlotAreaTemporary:
-
-cSlotAreaTemporary::cSlotAreaTemporary(int a_NumSlots, cWindow & a_ParentWindow) :
- cSlotArea(a_NumSlots, a_ParentWindow)
-{
-}
-
-
-
-
-
-const cItem * cSlotAreaTemporary::GetSlot(int a_SlotNum, cPlayer & a_Player) const
-{
- cItemMap::const_iterator itr = m_Items.find(a_Player.GetUniqueID());
- if (itr == m_Items.end())
- {
- LOGERROR("cSlotAreaTemporary: player \"%s\" not found for slot %d!", a_Player.GetName().c_str(), a_SlotNum);
- ASSERT(!"cSlotAreaTemporary: player not found!");
-
- // Player not found, this should not happen, ever! Return NULL, but things may break by this.
- return NULL;
- }
-
- if (a_SlotNum >= (int)(itr->second.size()))
- {
- LOGERROR("cSlotAreaTemporary: asking for more slots than actually stored!");
- ASSERT(!"cSlotAreaTemporary: asking for more slots than actually stored!");
- return NULL;
- }
-
- return &(itr->second[a_SlotNum]);
-}
-
-
-
-
-
-void cSlotAreaTemporary::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
-{
- cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID());
- if (itr == m_Items.end())
- {
- // Player not found
- LOGWARNING("cSlotAreaTemporary: player not found!");
- return;
- }
-
- if (a_SlotNum >= (int)(itr->second.size()))
- {
- LOGERROR("cSlotAreaTemporary: asking for more slots than actually stored!");
- return;
- }
-
- itr->second[a_SlotNum] = a_Item;
-}
-
-
-
-
-
-void cSlotAreaTemporary::OnPlayerAdded(cPlayer & a_Player)
-{
- ASSERT(m_Items.find(a_Player.GetUniqueID()) == m_Items.end()); // The player shouldn't be in the itemmap, otherwise we probably have a leak
- m_Items[a_Player.GetUniqueID()].resize(m_NumSlots); // Make the vector the specified size of empty items
-}
-
-
-
-
-
-void cSlotAreaTemporary::OnPlayerRemoved(cPlayer & a_Player)
-{
- cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID());
- ASSERT(itr != m_Items.end()); // The player should be in the list, otherwise a call to OnPlayerAdded() was mismatched
- m_Items.erase(itr);
-}
-
-
-
-
-
-void cSlotAreaTemporary::TossItems(cPlayer & a_Player, int a_Begin, int a_End)
-{
- cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID());
- if (itr == m_Items.end())
- {
- LOGWARNING("Player tossing items (%s) not found in the item map", a_Player.GetName().c_str());
- return;
- }
-
- cItems Drops;
- for (int i = a_Begin; i < a_End; i++)
- {
- cItem & Item = itr->second[i];
- if (!Item.IsEmpty())
- {
- Drops.push_back(Item);
- }
- Item.Empty();
- } // for i - itr->second[]
-
- double vX = 0, vY = 0, vZ = 0;
- EulerToVector(-a_Player.GetRotation(), a_Player.GetPitch(), vZ, vX, vY);
- vY = -vY * 2 + 1.f;
- a_Player.GetWorld()->SpawnItemPickups(Drops, a_Player.GetPosX(), a_Player.GetPosY() + 1.6f, a_Player.GetPosZ(), vX * 2, vY * 2, vZ * 2);
-}
-
-
-
-
-
-cItem * cSlotAreaTemporary::GetPlayerSlots(cPlayer & a_Player)
-{
- cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID());
- if (itr == m_Items.end())
- {
- return NULL;
- }
- return &(itr->second[0]);
-}
-
-
-
-
+
+// SlotArea.cpp
+
+// Implements the cSlotArea class and its descendants
+
+#include "Globals.h"
+#include "SlotArea.h"
+#include "../Player.h"
+#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/DropSpenserEntity.h"
+#include "../BlockEntities/FurnaceEntity.h"
+#include "../Items/ItemHandler.h"
+#include "Window.h"
+#include "../CraftingRecipes.h"
+#include "../Root.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotArea:
+
+cSlotArea::cSlotArea(int a_NumSlots, cWindow & a_ParentWindow) :
+ m_NumSlots(a_NumSlots),
+ m_ParentWindow(a_ParentWindow)
+{
+}
+
+
+
+
+
+void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
+{
+ /*
+ LOGD("Slot area with %d slots clicked at slot number %d, clicked item %s, slot item %s",
+ GetNumSlots(), a_SlotNum,
+ ItemToFullString(a_ClickedItem).c_str(),
+ ItemToFullString(*GetSlot(a_SlotNum, a_Player)).c_str()
+ );
+ */
+
+ ASSERT((a_SlotNum >= 0) && (a_SlotNum < GetNumSlots()));
+
+ bool bAsync = false;
+ if (GetSlot(a_SlotNum, a_Player) == NULL)
+ {
+ LOGWARNING("GetSlot(%d) returned NULL! Ignoring click", a_SlotNum);
+ return;
+ }
+
+ if ((a_ClickAction == caShiftLeftClick) || (a_ClickAction == caShiftRightClick))
+ {
+ if (!a_Player.IsDraggingItem())
+ {
+ ShiftClicked(a_Player, a_SlotNum, a_ClickedItem);
+ return;
+ }
+ LOGD("Shift clicked, but the player is dragging an item: %s", ItemToFullString(a_Player.GetDraggingItem()).c_str());
+ return;
+ }
+
+ cItem Slot(*GetSlot(a_SlotNum, a_Player));
+ if (!Slot.IsSameType(a_ClickedItem))
+ {
+ LOGWARNING("*** Window lost sync at item %d in SlotArea with %d items ***", a_SlotNum, m_NumSlots);
+ LOGWARNING("My item: %s", ItemToFullString(Slot).c_str());
+ LOGWARNING("Their item: %s", ItemToFullString(a_ClickedItem).c_str());
+ bAsync = true;
+ }
+ cItem & DraggingItem = a_Player.GetDraggingItem();
+ switch (a_ClickAction)
+ {
+ case caRightClick:
+ {
+ if (DraggingItem.m_ItemType <= 0) // Empty-handed?
+ {
+ DraggingItem.m_ItemCount = (char)(((float)Slot.m_ItemCount) / 2.f + 0.5f);
+ Slot.m_ItemCount -= DraggingItem.m_ItemCount;
+ DraggingItem.m_ItemType = Slot.m_ItemType;
+ DraggingItem.m_ItemDamage = Slot.m_ItemDamage;
+
+ if (Slot.m_ItemCount <= 0)
+ {
+ Slot.Empty();
+ }
+ }
+ else if ((Slot.m_ItemType <= 0) || DraggingItem.IsEqual(Slot))
+ {
+ // Drop one item in slot
+ cItemHandler * Handler = ItemHandler(Slot.m_ItemType);
+ if ((DraggingItem.m_ItemCount > 0) && (Slot.m_ItemCount < Handler->GetMaxStackSize()))
+ {
+ Slot.m_ItemType = DraggingItem.m_ItemType;
+ Slot.m_ItemCount++;
+ Slot.m_ItemDamage = DraggingItem.m_ItemDamage;
+ DraggingItem.m_ItemCount--;
+ }
+ if (DraggingItem.m_ItemCount <= 0)
+ {
+ DraggingItem.Empty();
+ }
+ }
+ else if (!DraggingItem.IsEqual(Slot))
+ {
+ // Swap contents
+ cItem tmp(DraggingItem);
+ DraggingItem = Slot;
+ Slot = tmp;
+ }
+ break;
+ }
+
+ case caLeftClick:
+ {
+ // Left-clicked
+ if (!DraggingItem.IsEqual(Slot))
+ {
+ // Switch contents
+ cItem tmp(DraggingItem);
+ DraggingItem = Slot;
+ Slot = tmp;
+ }
+ else
+ {
+ // Same type, add items:
+ cItemHandler * Handler = ItemHandler(DraggingItem.m_ItemType);
+ int FreeSlots = Handler->GetMaxStackSize() - Slot.m_ItemCount;
+ if (FreeSlots < 0)
+ {
+ ASSERT(!"Bad item stack size - where did we get more items in a slot than allowed?");
+ FreeSlots = 0;
+ }
+ int Filling = (FreeSlots > DraggingItem.m_ItemCount) ? DraggingItem.m_ItemCount : FreeSlots;
+ Slot.m_ItemCount += (char)Filling;
+ DraggingItem.m_ItemCount -= (char)Filling;
+ if (DraggingItem.m_ItemCount <= 0)
+ {
+ DraggingItem.Empty();
+ }
+ }
+ break;
+ }
+ default:
+ {
+ LOGWARNING("SlotArea: Unhandled click action: %d (%s)", a_ClickAction, ClickActionToString(a_ClickAction));
+ m_ParentWindow.BroadcastWholeWindow();
+ return;
+ }
+ } // switch (a_ClickAction
+
+ SetSlot(a_SlotNum, a_Player, Slot);
+ if (bAsync)
+ {
+ m_ParentWindow.BroadcastWholeWindow();
+ }
+
+}
+
+
+
+
+
+void cSlotArea::ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem)
+{
+ // Make a copy of the slot, distribute it among the other areas, then update the slot to contain the leftover:
+ cItem Slot(*GetSlot(a_SlotNum, a_Player));
+ m_ParentWindow.DistributeStack(Slot, a_Player, this, true);
+ if (Slot.IsEmpty())
+ {
+ // Empty the slot completely, the cilent doesn't like left-over ItemType with zero count
+ Slot.Empty();
+ }
+ SetSlot(a_SlotNum, a_Player, Slot);
+
+ // Some clients try to guess our actions and not always right (armor slots in 1.2.5), so we fix them:
+ m_ParentWindow.BroadcastWholeWindow();
+}
+
+
+
+
+
+void cSlotArea::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_Apply, bool a_KeepEmptySlots)
+{
+ for (int i = 0; i < m_NumSlots; i++)
+ {
+ const cItem * Slot = GetSlot(i, a_Player);
+ if (!Slot->IsStackableWith(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots))
+ {
+ // Different items
+ continue;
+ }
+ int NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount;
+ if (NumFit <= 0)
+ {
+ // Full stack already
+ continue;
+ }
+ if (NumFit > a_ItemStack.m_ItemCount)
+ {
+ NumFit = a_ItemStack.m_ItemCount;
+ }
+ if (a_Apply)
+ {
+ cItem NewSlot(a_ItemStack);
+ NewSlot.m_ItemCount = Slot->m_ItemCount + NumFit;
+ SetSlot(i, a_Player, NewSlot);
+ }
+ a_ItemStack.m_ItemCount -= NumFit;
+ if (a_ItemStack.IsEmpty())
+ {
+ return;
+ }
+ } // for i - Slots
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaChest:
+
+cSlotAreaChest::cSlotAreaChest(cChestEntity * a_Chest, cWindow & a_ParentWindow) :
+ cSlotArea(27, a_ParentWindow),
+ m_Chest(a_Chest)
+{
+}
+
+
+
+
+
+const cItem * cSlotAreaChest::GetSlot(int a_SlotNum, cPlayer & a_Player) const
+{
+ // a_SlotNum ranges from 0 to 26, use that to index the chest entity's inventory directly:
+ return &(m_Chest->GetSlot(a_SlotNum));
+}
+
+
+
+
+
+void cSlotAreaChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ m_Chest->SetSlot(a_SlotNum, a_Item);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaDoubleChest:
+
+cSlotAreaDoubleChest::cSlotAreaDoubleChest(cChestEntity * a_TopChest, cChestEntity * a_BottomChest, cWindow & a_ParentWindow) :
+ cSlotArea(54, a_ParentWindow),
+ m_TopChest(a_TopChest),
+ m_BottomChest(a_BottomChest)
+{
+}
+
+
+
+
+
+const cItem * cSlotAreaDoubleChest::GetSlot(int a_SlotNum, cPlayer & a_Player) const
+{
+ // a_SlotNum ranges from 0 to 53, use that to index the correct chest's inventory:
+ if (a_SlotNum < 27)
+ {
+ return &(m_TopChest->GetSlot(a_SlotNum));
+ }
+ else
+ {
+ return &(m_BottomChest->GetSlot(a_SlotNum - 27));
+ }
+}
+
+
+
+
+
+void cSlotAreaDoubleChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ if (a_SlotNum < 27)
+ {
+ m_TopChest->SetSlot(a_SlotNum, a_Item);
+ }
+ else
+ {
+ m_BottomChest->SetSlot(a_SlotNum - 27, a_Item);
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaCrafting:
+
+cSlotAreaCrafting::cSlotAreaCrafting(int a_GridSize, cWindow & a_ParentWindow) :
+ cSlotAreaTemporary(1 + a_GridSize * a_GridSize, a_ParentWindow),
+ m_GridSize(a_GridSize)
+{
+ ASSERT((a_GridSize == 2) || (a_GridSize == 3));
+}
+
+
+
+
+
+void cSlotAreaCrafting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
+{
+ // Override for craft result slot
+ if (a_SlotNum == 0)
+ {
+ if ((a_ClickAction == caShiftLeftClick) || (a_ClickAction == caShiftRightClick))
+ {
+ ShiftClickedResult(a_Player);
+ }
+ else
+ {
+ ClickedResult(a_Player);
+ }
+ return;
+ }
+ super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem);
+ UpdateRecipe(a_Player);
+}
+
+
+
+
+
+void cSlotAreaCrafting::OnPlayerRemoved(cPlayer & a_Player)
+{
+ // Toss all items on the crafting grid:
+ TossItems(a_Player, 1, m_NumSlots);
+
+ // Remove the current recipe from the player -> recipe map:
+ for (cRecipeMap::iterator itr = m_Recipes.begin(), end = m_Recipes.end(); itr != end; ++itr)
+ {
+ if (itr->first == a_Player.GetUniqueID())
+ {
+ // Remove the player from the recipe map:
+ m_Recipes.erase(itr);
+ return;
+ }
+ } // for itr - m_Recipes[]
+ // Player not found - that is acceptable
+}
+
+
+
+
+
+void cSlotAreaCrafting::ClickedResult(cPlayer & a_Player)
+{
+ const cItem * ResultSlot = GetSlot(0, a_Player);
+ cItem & DraggingItem = a_Player.GetDraggingItem();
+
+ // Get the current recipe:
+ cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player);
+
+ cItem * PlayerSlots = GetPlayerSlots(a_Player) + 1;
+ cCraftingGrid Grid(PlayerSlots, m_GridSize, m_GridSize);
+
+ // If possible, craft:
+ if (DraggingItem.IsEmpty())
+ {
+ DraggingItem = Recipe.GetResult();
+ Recipe.ConsumeIngredients(Grid);
+ Grid.CopyToItems(PlayerSlots);
+ }
+ else if (DraggingItem.IsEqual(Recipe.GetResult()))
+ {
+ cItemHandler * Handler = ItemHandler(Recipe.GetResult().m_ItemType);
+ if (DraggingItem.m_ItemCount + Recipe.GetResult().m_ItemCount <= Handler->GetMaxStackSize())
+ {
+ DraggingItem.m_ItemCount += Recipe.GetResult().m_ItemCount;
+ Recipe.ConsumeIngredients(Grid);
+ Grid.CopyToItems(PlayerSlots);
+ }
+ }
+
+ // Get the new recipe and update the result slot:
+ UpdateRecipe(a_Player);
+
+ // We're done. Send all changes to the client and bail out:
+ m_ParentWindow.BroadcastWholeWindow();
+}
+
+
+
+
+
+void cSlotAreaCrafting::ShiftClickedResult(cPlayer & a_Player)
+{
+ cItem Result(*GetSlot(0, a_Player));
+ if (Result.IsEmpty())
+ {
+ return;
+ }
+ cItem * PlayerSlots = GetPlayerSlots(a_Player) + 1;
+ do
+ {
+ // Try distributing the result. If it fails, bail out:
+ cItem ResultCopy(Result);
+ m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, false);
+ if (!ResultCopy.IsEmpty())
+ {
+ // Couldn't distribute all of it. Bail out
+ return;
+ }
+
+ // Distribute the result, this time for real:
+ ResultCopy = Result;
+ m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, true);
+
+ // Remove the ingredients from the crafting grid and update the recipe:
+ cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player);
+ cCraftingGrid Grid(PlayerSlots, m_GridSize, m_GridSize);
+ Recipe.ConsumeIngredients(Grid);
+ Grid.CopyToItems(PlayerSlots);
+ UpdateRecipe(a_Player);
+ if (!Recipe.GetResult().IsEqual(Result))
+ {
+ // The recipe has changed, bail out
+ return;
+ }
+ } while (true);
+}
+
+
+
+
+
+void cSlotAreaCrafting::UpdateRecipe(cPlayer & a_Player)
+{
+ cCraftingGrid Grid(GetPlayerSlots(a_Player) + 1, m_GridSize, m_GridSize);
+ cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player);
+ cRoot::Get()->GetCraftingRecipes()->GetRecipe(&a_Player, Grid, Recipe);
+ SetSlot(0, a_Player, Recipe.GetResult());
+ m_ParentWindow.SendSlot(a_Player, this, 0);
+}
+
+
+
+
+
+cCraftingRecipe & cSlotAreaCrafting::GetRecipeForPlayer(cPlayer & a_Player)
+{
+ for (cRecipeMap::iterator itr = m_Recipes.begin(), end = m_Recipes.end(); itr != end; ++itr)
+ {
+ if (itr->first == a_Player.GetUniqueID())
+ {
+ return itr->second;
+ }
+ } // for itr - m_Recipes[]
+
+ // Not found. Add a new one:
+ cCraftingGrid Grid(GetPlayerSlots(a_Player) + 1, m_GridSize, m_GridSize);
+ cCraftingRecipe Recipe(Grid);
+ cRoot::Get()->GetCraftingRecipes()->GetRecipe(&a_Player, Grid, Recipe);
+ m_Recipes.push_back(std::make_pair(a_Player.GetUniqueID(), Recipe));
+ return m_Recipes.back().second;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaFurnace:
+
+cSlotAreaFurnace::cSlotAreaFurnace(cFurnaceEntity * a_Furnace, cWindow & a_ParentWindow) :
+ cSlotArea(3, a_ParentWindow),
+ m_Furnace(a_Furnace)
+{
+ m_Furnace->GetContents().AddListener(*this);
+}
+
+
+
+
+
+cSlotAreaFurnace::~cSlotAreaFurnace()
+{
+ m_Furnace->GetContents().RemoveListener(*this);
+}
+
+
+
+
+
+void cSlotAreaFurnace::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
+{
+ super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem);
+
+ if (m_Furnace == NULL)
+ {
+ LOGERROR("cSlotAreaFurnace::Clicked(): m_Furnace == NULL");
+ ASSERT(!"cSlotAreaFurnace::Clicked(): m_Furnace == NULL");
+ return;
+ }
+}
+
+
+
+
+
+const cItem * cSlotAreaFurnace::GetSlot(int a_SlotNum, cPlayer & a_Player) const
+{
+ // a_SlotNum ranges from 0 to 2, query the items from the underlying furnace:
+ return &(m_Furnace->GetSlot(a_SlotNum));
+}
+
+
+
+
+
+void cSlotAreaFurnace::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ m_Furnace->SetSlot(a_SlotNum, a_Item);
+}
+
+
+
+
+
+void cSlotAreaFurnace::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
+{
+ // Something has changed in the window, broadcast the entire window to all clients
+ ASSERT(a_ItemGrid == &(m_Furnace->GetContents()));
+
+ m_ParentWindow.BroadcastWholeWindow();
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaInventoryBase:
+
+cSlotAreaInventoryBase::cSlotAreaInventoryBase(int a_NumSlots, int a_SlotOffset, cWindow & a_ParentWindow) :
+ cSlotArea(a_NumSlots, a_ParentWindow),
+ m_SlotOffset(a_SlotOffset)
+{
+}
+
+
+
+
+
+void cSlotAreaInventoryBase::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem)
+{
+ if ((a_Player.GetGameMode() == eGameMode_Creative) && (m_ParentWindow.GetWindowType() == cWindow::Inventory))
+ {
+ // Creative inventory must treat a_ClickedItem as a DraggedItem instead, replacing the inventory slot with it
+ SetSlot(a_SlotNum, a_Player, a_ClickedItem);
+ return;
+ }
+
+ // Survival inventory and all other windows' inventory has the same handling as normal slot areas
+ super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem);
+ return;
+}
+
+
+
+
+
+const cItem * cSlotAreaInventoryBase::GetSlot(int a_SlotNum, cPlayer & a_Player) const
+{
+ // a_SlotNum ranges from 0 to 35, map that to the player's inventory slots according to the internal offset
+ return &a_Player.GetInventory().GetSlot(a_SlotNum + m_SlotOffset);
+}
+
+
+
+
+
+void cSlotAreaInventoryBase::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ a_Player.GetInventory().SetSlot(a_SlotNum + m_SlotOffset, a_Item);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaArmor:
+
+void cSlotAreaArmor::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots)
+{
+ if (ItemCategory::IsHelmet(a_ItemStack.m_ItemType) && GetSlot(0, a_Player)->IsEmpty())
+ {
+ if (a_ShouldApply)
+ {
+ SetSlot(0, a_Player, a_ItemStack.CopyOne());
+ }
+ a_ItemStack.m_ItemCount -= 1;
+ }
+ else if (ItemCategory::IsChestPlate(a_ItemStack.m_ItemType) && GetSlot(1, a_Player)->IsEmpty())
+ {
+ if (a_ShouldApply)
+ {
+ SetSlot(1, a_Player, a_ItemStack.CopyOne());
+ }
+ a_ItemStack.m_ItemCount -= 1;
+ }
+ else if (ItemCategory::IsLeggings(a_ItemStack.m_ItemType) && GetSlot(2, a_Player)->IsEmpty())
+ {
+ if (a_ShouldApply)
+ {
+ SetSlot(2, a_Player, a_ItemStack.CopyOne());
+ }
+ a_ItemStack.m_ItemCount -= 1;
+ }
+ else if (ItemCategory::IsBoots(a_ItemStack.m_ItemType) && GetSlot(3, a_Player)->IsEmpty())
+ {
+ if (a_ShouldApply)
+ {
+ SetSlot(3, a_Player, a_ItemStack.CopyOne());
+ }
+ a_ItemStack.m_ItemCount -= 1;
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaItemGrid:
+
+cSlotAreaItemGrid::cSlotAreaItemGrid(cItemGrid & a_ItemGrid, cWindow & a_ParentWindow) :
+ super(a_ItemGrid.GetNumSlots(), a_ParentWindow),
+ m_ItemGrid(a_ItemGrid)
+{
+ m_ItemGrid.AddListener(*this);
+}
+
+
+
+
+
+cSlotAreaItemGrid::~cSlotAreaItemGrid()
+{
+ m_ItemGrid.RemoveListener(*this);
+}
+
+
+
+
+
+const cItem * cSlotAreaItemGrid::GetSlot(int a_SlotNum, cPlayer & a_Player) const
+{
+ return &m_ItemGrid.GetSlot(a_SlotNum);
+}
+
+
+
+
+
+void cSlotAreaItemGrid::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ m_ItemGrid.SetSlot(a_SlotNum, a_Item);
+}
+
+
+
+
+
+void cSlotAreaItemGrid::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
+{
+ ASSERT(a_ItemGrid == &m_ItemGrid);
+ m_ParentWindow.BroadcastSlot(this, a_SlotNum);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSlotAreaTemporary:
+
+cSlotAreaTemporary::cSlotAreaTemporary(int a_NumSlots, cWindow & a_ParentWindow) :
+ cSlotArea(a_NumSlots, a_ParentWindow)
+{
+}
+
+
+
+
+
+const cItem * cSlotAreaTemporary::GetSlot(int a_SlotNum, cPlayer & a_Player) const
+{
+ cItemMap::const_iterator itr = m_Items.find(a_Player.GetUniqueID());
+ if (itr == m_Items.end())
+ {
+ LOGERROR("cSlotAreaTemporary: player \"%s\" not found for slot %d!", a_Player.GetName().c_str(), a_SlotNum);
+ ASSERT(!"cSlotAreaTemporary: player not found!");
+
+ // Player not found, this should not happen, ever! Return NULL, but things may break by this.
+ return NULL;
+ }
+
+ if (a_SlotNum >= (int)(itr->second.size()))
+ {
+ LOGERROR("cSlotAreaTemporary: asking for more slots than actually stored!");
+ ASSERT(!"cSlotAreaTemporary: asking for more slots than actually stored!");
+ return NULL;
+ }
+
+ return &(itr->second[a_SlotNum]);
+}
+
+
+
+
+
+void cSlotAreaTemporary::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID());
+ if (itr == m_Items.end())
+ {
+ // Player not found
+ LOGWARNING("cSlotAreaTemporary: player not found!");
+ return;
+ }
+
+ if (a_SlotNum >= (int)(itr->second.size()))
+ {
+ LOGERROR("cSlotAreaTemporary: asking for more slots than actually stored!");
+ return;
+ }
+
+ itr->second[a_SlotNum] = a_Item;
+}
+
+
+
+
+
+void cSlotAreaTemporary::OnPlayerAdded(cPlayer & a_Player)
+{
+ ASSERT(m_Items.find(a_Player.GetUniqueID()) == m_Items.end()); // The player shouldn't be in the itemmap, otherwise we probably have a leak
+ m_Items[a_Player.GetUniqueID()].resize(m_NumSlots); // Make the vector the specified size of empty items
+}
+
+
+
+
+
+void cSlotAreaTemporary::OnPlayerRemoved(cPlayer & a_Player)
+{
+ cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID());
+ ASSERT(itr != m_Items.end()); // The player should be in the list, otherwise a call to OnPlayerAdded() was mismatched
+ m_Items.erase(itr);
+}
+
+
+
+
+
+void cSlotAreaTemporary::TossItems(cPlayer & a_Player, int a_Begin, int a_End)
+{
+ cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID());
+ if (itr == m_Items.end())
+ {
+ LOGWARNING("Player tossing items (%s) not found in the item map", a_Player.GetName().c_str());
+ return;
+ }
+
+ cItems Drops;
+ for (int i = a_Begin; i < a_End; i++)
+ {
+ cItem & Item = itr->second[i];
+ if (!Item.IsEmpty())
+ {
+ Drops.push_back(Item);
+ }
+ Item.Empty();
+ } // for i - itr->second[]
+
+ double vX = 0, vY = 0, vZ = 0;
+ EulerToVector(-a_Player.GetRotation(), a_Player.GetPitch(), vZ, vX, vY);
+ vY = -vY * 2 + 1.f;
+ a_Player.GetWorld()->SpawnItemPickups(Drops, a_Player.GetPosX(), a_Player.GetPosY() + 1.6f, a_Player.GetPosZ(), vX * 2, vY * 2, vZ * 2);
+}
+
+
+
+
+
+cItem * cSlotAreaTemporary::GetPlayerSlots(cPlayer & a_Player)
+{
+ cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID());
+ if (itr == m_Items.end())
+ {
+ return NULL;
+ }
+ return &(itr->second[0]);
+}
+
+
+
+
diff --git a/source/UI/SlotArea.h b/source/UI/SlotArea.h
index 516e6d644..943452feb 100644
--- a/source/UI/SlotArea.h
+++ b/source/UI/SlotArea.h
@@ -1,303 +1,303 @@
-
-// SlotArea.h
-
-// Interfaces to the cSlotArea class representing a contiguous area of slots in a UI window
-
-
-
-
-#pragma once
-
-#include "../Inventory.h"
-
-
-
-class cWindow;
-class cPlayer;
-class cChestEntity;
-class cDropSpenserEntity;
-class cFurnaceEntity;
-class cCraftingRecipe;
-
-
-
-
-
-class cSlotArea
-{
-public:
- cSlotArea(int a_NumSlots, cWindow & a_ParentWindow);
- virtual ~cSlotArea() {} // force a virtual destructor in all subclasses
-
- int GetNumSlots(void) const { return m_NumSlots; }
-
- /// Called to retrieve an item in the specified slot for the specified player. Must return a valid cItem.
- virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const = 0;
-
- /// Called to set an item in the specified slot for the specified player
- virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) = 0;
-
- /// Called when a player clicks in the window. Parameters taken from the click packet.
- virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem);
-
- /// Called from Clicked if it is a valid shiftclick
- virtual void ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem);
-
- /// Called when a new player opens the same parent window. The window already tracks the player. CS-locked.
- virtual void OnPlayerAdded(cPlayer & a_Player) {} ;
-
- /// Called when one of the players closes the parent window. The window already doesn't track the player. CS-locked.
- virtual void OnPlayerRemoved(cPlayer & a_Player) {} ;
-
- /** Called to store as much of a_ItemStack in the area as possible. a_ItemStack is modified to reflect the change.
- The default implementation searches each slot for available space and distributes the stack there.
- if a_ShouldApply is true, the changes are written into the slots;
- if a_ShouldApply is false, only a_ItemStack is modified to reflect the number of fits (for fit-testing purposes)
- If a_KeepEmptySlots is true, empty slots will be skipped and won't be filled
- */
- virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots);
-
-protected:
- int m_NumSlots;
- cWindow & m_ParentWindow;
-} ;
-
-
-
-
-
-/// Handles any part of the inventory, using parameters in constructor to distinguish between the parts
-class cSlotAreaInventoryBase :
- public cSlotArea
-{
- typedef cSlotArea super;
-
-public:
- cSlotAreaInventoryBase(int a_NumSlots, int a_SlotOffset, cWindow & a_ParentWindow);
-
- // Creative inventory's click handling is somewhat different from survival inventory's, handle that here:
- virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override;
-
- virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
- virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
-
-protected:
- int m_SlotOffset; // Index that this area's slot 0 has in the underlying cInventory
-} ;
-
-
-
-
-
-/// Handles the main inventory of each player, excluding the armor and hotbar
-class cSlotAreaInventory :
- public cSlotAreaInventoryBase
-{
- typedef cSlotAreaInventoryBase super;
-
-public:
- cSlotAreaInventory(cWindow & a_ParentWindow) :
- cSlotAreaInventoryBase(cInventory::invInventoryCount, cInventory::invInventoryOffset, a_ParentWindow)
- {
- }
-} ;
-
-
-
-
-
-/// Handles the hotbar of each player
-class cSlotAreaHotBar :
- public cSlotAreaInventoryBase
-{
- typedef cSlotAreaInventoryBase super;
-
-public:
- cSlotAreaHotBar(cWindow & a_ParentWindow) :
- cSlotAreaInventoryBase(cInventory::invHotbarCount, cInventory::invHotbarOffset, a_ParentWindow)
- {
- }
-} ;
-
-
-
-
-
-/// Handles the armor area of the player's inventory
-class cSlotAreaArmor :
- public cSlotAreaInventoryBase
-{
-public:
- cSlotAreaArmor(cWindow & a_ParentWindow) :
- cSlotAreaInventoryBase(cInventory::invArmorCount, cInventory::invArmorOffset, a_ParentWindow)
- {
- }
-
- // Distributing the stack is allowed only for compatible items (helmets into helmet slot etc.)
- virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override;
-} ;
-
-
-
-
-
-/// Handles any slot area that is representing a cItemGrid; same items for all the players
-class cSlotAreaItemGrid :
- public cSlotArea,
- public cItemGrid::cListener
-{
- typedef cSlotArea super;
-
-public:
- cSlotAreaItemGrid(cItemGrid & a_ItemGrid, cWindow & a_ParentWindow);
-
- virtual ~cSlotAreaItemGrid();
-
- virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
- virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
-
-protected:
- cItemGrid & m_ItemGrid;
-
- // cItemGrid::cListener overrides:
- virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override;
-} ;
-
-
-
-
-
-/** A cSlotArea with items layout that is private to each player and is temporary, such as
-a crafting grid or an enchantment table.
-This common ancestor stores the items in a per-player map. It also implements tossing items from the map.
-*/
-class cSlotAreaTemporary :
- public cSlotArea
-{
- typedef cSlotArea super;
-
-public:
- cSlotAreaTemporary(int a_NumSlots, cWindow & a_ParentWindow);
-
- // cSlotArea overrides:
- virtual const cItem * GetSlot (int a_SlotNum, cPlayer & a_Player) const override;
- virtual void SetSlot (int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
- virtual void OnPlayerAdded (cPlayer & a_Player) override;
- virtual void OnPlayerRemoved(cPlayer & a_Player) override;
-
- /// Tosses the player's items in slots [a_Begin, a_End) (ie. incl. a_Begin, but excl. a_End)
- void TossItems(cPlayer & a_Player, int a_Begin, int a_End);
-
-protected:
- typedef std::map<int, std::vector<cItem> > cItemMap; // Maps EntityID -> items
-
- cItemMap m_Items;
-
- /// Returns the pointer to the slot array for the player specified.
- cItem * GetPlayerSlots(cPlayer & a_Player);
-} ;
-
-
-
-
-
-class cSlotAreaCrafting :
- public cSlotAreaTemporary
-{
- typedef cSlotAreaTemporary super;
-
-public:
- /// a_GridSize is allowed to be only 2 or 3
- cSlotAreaCrafting(int a_GridSize, cWindow & a_ParentWindow);
-
- // cSlotAreaTemporary overrides:
- virtual void Clicked (cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override;
- virtual void OnPlayerRemoved(cPlayer & a_Player) override;
-
- // Distributing items into this area is completely disabled
- virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override {}
-
-protected:
- /// Maps player's EntityID -> current recipe; not a std::map because cCraftingGrid needs proper constructor params
- typedef std::list<std::pair<int, cCraftingRecipe> > cRecipeMap;
-
- int m_GridSize;
- cRecipeMap m_Recipes;
-
- /// Handles a click in the result slot. Crafts using the current recipe, if possible
- void ClickedResult(cPlayer & a_Player);
-
- /// Handles a shift-click in the result slot. Crafts using the current recipe until it changes or no more space for result.
- void ShiftClickedResult(cPlayer & a_Player);
-
- /// Updates the current recipe and result slot based on the ingredients currently in the crafting grid of the specified player
- void UpdateRecipe(cPlayer & a_Player);
-
- /// Retrieves the recipe for the specified player from the map, or creates one if not found
- cCraftingRecipe & GetRecipeForPlayer(cPlayer & a_Player);
-} ;
-
-
-
-
-
-class cSlotAreaChest :
- public cSlotArea
-{
-public:
- cSlotAreaChest(cChestEntity * a_Chest, cWindow & a_ParentWindow);
-
- virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
- virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
-
-protected:
- cChestEntity * m_Chest;
-} ;
-
-
-
-
-
-class cSlotAreaDoubleChest :
- public cSlotArea
-{
-public:
- cSlotAreaDoubleChest(cChestEntity * a_TopChest, cChestEntity * a_BottomChest, cWindow & a_ParentWindow);
-
- virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
- virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
-
-protected:
- cChestEntity * m_TopChest;
- cChestEntity * m_BottomChest;
-} ;
-
-
-
-
-
-class cSlotAreaFurnace :
- public cSlotArea,
- public cItemGrid::cListener
-{
- typedef cSlotArea super;
-
-public:
- cSlotAreaFurnace(cFurnaceEntity * a_Furnace, cWindow & a_ParentWindow);
-
- virtual ~cSlotAreaFurnace();
-
- virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override;
- virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
- virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
-
-protected:
- cFurnaceEntity * m_Furnace;
-
- // cItemGrid::cListener overrides:
- virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override;
-} ;
-
-
-
-
+
+// SlotArea.h
+
+// Interfaces to the cSlotArea class representing a contiguous area of slots in a UI window
+
+
+
+
+#pragma once
+
+#include "../Inventory.h"
+
+
+
+class cWindow;
+class cPlayer;
+class cChestEntity;
+class cDropSpenserEntity;
+class cFurnaceEntity;
+class cCraftingRecipe;
+
+
+
+
+
+class cSlotArea
+{
+public:
+ cSlotArea(int a_NumSlots, cWindow & a_ParentWindow);
+ virtual ~cSlotArea() {} // force a virtual destructor in all subclasses
+
+ int GetNumSlots(void) const { return m_NumSlots; }
+
+ /// Called to retrieve an item in the specified slot for the specified player. Must return a valid cItem.
+ virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const = 0;
+
+ /// Called to set an item in the specified slot for the specified player
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) = 0;
+
+ /// Called when a player clicks in the window. Parameters taken from the click packet.
+ virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem);
+
+ /// Called from Clicked if it is a valid shiftclick
+ virtual void ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem);
+
+ /// Called when a new player opens the same parent window. The window already tracks the player. CS-locked.
+ virtual void OnPlayerAdded(cPlayer & a_Player) {} ;
+
+ /// Called when one of the players closes the parent window. The window already doesn't track the player. CS-locked.
+ virtual void OnPlayerRemoved(cPlayer & a_Player) {} ;
+
+ /** Called to store as much of a_ItemStack in the area as possible. a_ItemStack is modified to reflect the change.
+ The default implementation searches each slot for available space and distributes the stack there.
+ if a_ShouldApply is true, the changes are written into the slots;
+ if a_ShouldApply is false, only a_ItemStack is modified to reflect the number of fits (for fit-testing purposes)
+ If a_KeepEmptySlots is true, empty slots will be skipped and won't be filled
+ */
+ virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots);
+
+protected:
+ int m_NumSlots;
+ cWindow & m_ParentWindow;
+} ;
+
+
+
+
+
+/// Handles any part of the inventory, using parameters in constructor to distinguish between the parts
+class cSlotAreaInventoryBase :
+ public cSlotArea
+{
+ typedef cSlotArea super;
+
+public:
+ cSlotAreaInventoryBase(int a_NumSlots, int a_SlotOffset, cWindow & a_ParentWindow);
+
+ // Creative inventory's click handling is somewhat different from survival inventory's, handle that here:
+ virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override;
+
+ virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
+
+protected:
+ int m_SlotOffset; // Index that this area's slot 0 has in the underlying cInventory
+} ;
+
+
+
+
+
+/// Handles the main inventory of each player, excluding the armor and hotbar
+class cSlotAreaInventory :
+ public cSlotAreaInventoryBase
+{
+ typedef cSlotAreaInventoryBase super;
+
+public:
+ cSlotAreaInventory(cWindow & a_ParentWindow) :
+ cSlotAreaInventoryBase(cInventory::invInventoryCount, cInventory::invInventoryOffset, a_ParentWindow)
+ {
+ }
+} ;
+
+
+
+
+
+/// Handles the hotbar of each player
+class cSlotAreaHotBar :
+ public cSlotAreaInventoryBase
+{
+ typedef cSlotAreaInventoryBase super;
+
+public:
+ cSlotAreaHotBar(cWindow & a_ParentWindow) :
+ cSlotAreaInventoryBase(cInventory::invHotbarCount, cInventory::invHotbarOffset, a_ParentWindow)
+ {
+ }
+} ;
+
+
+
+
+
+/// Handles the armor area of the player's inventory
+class cSlotAreaArmor :
+ public cSlotAreaInventoryBase
+{
+public:
+ cSlotAreaArmor(cWindow & a_ParentWindow) :
+ cSlotAreaInventoryBase(cInventory::invArmorCount, cInventory::invArmorOffset, a_ParentWindow)
+ {
+ }
+
+ // Distributing the stack is allowed only for compatible items (helmets into helmet slot etc.)
+ virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override;
+} ;
+
+
+
+
+
+/// Handles any slot area that is representing a cItemGrid; same items for all the players
+class cSlotAreaItemGrid :
+ public cSlotArea,
+ public cItemGrid::cListener
+{
+ typedef cSlotArea super;
+
+public:
+ cSlotAreaItemGrid(cItemGrid & a_ItemGrid, cWindow & a_ParentWindow);
+
+ virtual ~cSlotAreaItemGrid();
+
+ virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
+
+protected:
+ cItemGrid & m_ItemGrid;
+
+ // cItemGrid::cListener overrides:
+ virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override;
+} ;
+
+
+
+
+
+/** A cSlotArea with items layout that is private to each player and is temporary, such as
+a crafting grid or an enchantment table.
+This common ancestor stores the items in a per-player map. It also implements tossing items from the map.
+*/
+class cSlotAreaTemporary :
+ public cSlotArea
+{
+ typedef cSlotArea super;
+
+public:
+ cSlotAreaTemporary(int a_NumSlots, cWindow & a_ParentWindow);
+
+ // cSlotArea overrides:
+ virtual const cItem * GetSlot (int a_SlotNum, cPlayer & a_Player) const override;
+ virtual void SetSlot (int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
+ virtual void OnPlayerAdded (cPlayer & a_Player) override;
+ virtual void OnPlayerRemoved(cPlayer & a_Player) override;
+
+ /// Tosses the player's items in slots [a_Begin, a_End) (ie. incl. a_Begin, but excl. a_End)
+ void TossItems(cPlayer & a_Player, int a_Begin, int a_End);
+
+protected:
+ typedef std::map<int, std::vector<cItem> > cItemMap; // Maps EntityID -> items
+
+ cItemMap m_Items;
+
+ /// Returns the pointer to the slot array for the player specified.
+ cItem * GetPlayerSlots(cPlayer & a_Player);
+} ;
+
+
+
+
+
+class cSlotAreaCrafting :
+ public cSlotAreaTemporary
+{
+ typedef cSlotAreaTemporary super;
+
+public:
+ /// a_GridSize is allowed to be only 2 or 3
+ cSlotAreaCrafting(int a_GridSize, cWindow & a_ParentWindow);
+
+ // cSlotAreaTemporary overrides:
+ virtual void Clicked (cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override;
+ virtual void OnPlayerRemoved(cPlayer & a_Player) override;
+
+ // Distributing items into this area is completely disabled
+ virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override {}
+
+protected:
+ /// Maps player's EntityID -> current recipe; not a std::map because cCraftingGrid needs proper constructor params
+ typedef std::list<std::pair<int, cCraftingRecipe> > cRecipeMap;
+
+ int m_GridSize;
+ cRecipeMap m_Recipes;
+
+ /// Handles a click in the result slot. Crafts using the current recipe, if possible
+ void ClickedResult(cPlayer & a_Player);
+
+ /// Handles a shift-click in the result slot. Crafts using the current recipe until it changes or no more space for result.
+ void ShiftClickedResult(cPlayer & a_Player);
+
+ /// Updates the current recipe and result slot based on the ingredients currently in the crafting grid of the specified player
+ void UpdateRecipe(cPlayer & a_Player);
+
+ /// Retrieves the recipe for the specified player from the map, or creates one if not found
+ cCraftingRecipe & GetRecipeForPlayer(cPlayer & a_Player);
+} ;
+
+
+
+
+
+class cSlotAreaChest :
+ public cSlotArea
+{
+public:
+ cSlotAreaChest(cChestEntity * a_Chest, cWindow & a_ParentWindow);
+
+ virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
+
+protected:
+ cChestEntity * m_Chest;
+} ;
+
+
+
+
+
+class cSlotAreaDoubleChest :
+ public cSlotArea
+{
+public:
+ cSlotAreaDoubleChest(cChestEntity * a_TopChest, cChestEntity * a_BottomChest, cWindow & a_ParentWindow);
+
+ virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
+
+protected:
+ cChestEntity * m_TopChest;
+ cChestEntity * m_BottomChest;
+} ;
+
+
+
+
+
+class cSlotAreaFurnace :
+ public cSlotArea,
+ public cItemGrid::cListener
+{
+ typedef cSlotArea super;
+
+public:
+ cSlotAreaFurnace(cFurnaceEntity * a_Furnace, cWindow & a_ParentWindow);
+
+ virtual ~cSlotAreaFurnace();
+
+ virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override;
+ virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override;
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
+
+protected:
+ cFurnaceEntity * m_Furnace;
+
+ // cItemGrid::cListener overrides:
+ virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override;
+} ;
+
+
+
+
diff --git a/source/WebAdmin.cpp b/source/WebAdmin.cpp
index dd1a695ee..3d04ce8f3 100644
--- a/source/WebAdmin.cpp
+++ b/source/WebAdmin.cpp
@@ -13,6 +13,7 @@
#include "Player.h"
#include "Server.h"
#include "Root.h"
+#include "LuaScript.h"
#include "../iniFile/iniFile.h"
@@ -59,6 +60,7 @@ cWebAdmin::cWebAdmin( int a_Port /* = 8080 */ )
{
WebAdmin = this;
m_Event = new cEvent();
+ m_pTemplate = new cLuaScript();
Init( m_Port );
}
@@ -68,10 +70,12 @@ cWebAdmin::cWebAdmin( int a_Port /* = 8080 */ )
cWebAdmin::~cWebAdmin()
{
+
WebAdmin = 0;
m_WebServer->Stop();
delete m_WebServer;
+ delete m_pTemplate;
delete m_IniFile;
m_Event->Wait();
@@ -146,40 +150,17 @@ void cWebAdmin::Request_Handler(webserver::http_request* r)
bDontShowTemplate = true;
}
- std::string UserPassword = WebAdmin->m_IniFile->GetValue( "User:"+r->username_, "Password", "");
+ AString UserPassword = WebAdmin->m_IniFile->GetValue( "User:"+r->username_, "Password", "");
if ((UserPassword != "") && (r->password_ == UserPassword))
{
- std::string BaseURL = "./";
- if (Split.size() > 1)
- {
- for (unsigned int i = 0; i < Split.size(); i++)
- {
- BaseURL += "../";
- }
- BaseURL += "webadmin/";
- }
+ AString Template;
- std::string Menu;
- std::string Content;
- std::string Template = bDontShowTemplate ? "{CONTENT}" : WebAdmin->GetTemplate();
- std::string FoundPlugin;
-
- for (PluginList::iterator itr = WebAdmin->m_Plugins.begin(); itr != WebAdmin->m_Plugins.end(); ++itr)
- {
- cWebPlugin* WebPlugin = *itr;
- std::list< std::pair<std::string, std::string> > NameList = WebPlugin->GetTabNames();
- for( std::list< std::pair<std::string, std::string> >::iterator Names = NameList.begin(); Names != NameList.end(); ++Names )
- {
- Menu += "<li><a href='" + BaseURL + WebPlugin->GetWebTitle().c_str() + "/" + (*Names).second + "'>" + (*Names).first + "</a></li>";
- }
- }
-
- HTTPRequest Request;
- Request.Username = r->username_;
- Request.Method = r->method_;
- Request.Params = r->params_;
- Request.PostParams = r->params_post_;
- Request.Path = r->path_.substr(1);
+ HTTPTemplateRequest TemplateRequest;
+ TemplateRequest.Request.Username = r->username_;
+ TemplateRequest.Request.Method = r->method_;
+ TemplateRequest.Request.Params = r->params_;
+ TemplateRequest.Request.PostParams = r->params_post_;
+ TemplateRequest.Request.Path = r->path_.substr(1);
for( unsigned int i = 0; i < r->multipart_formdata_.size(); ++i )
{
@@ -190,101 +171,90 @@ void cWebAdmin::Request_Handler(webserver::http_request* r)
HTTPfd.Type = fd.content_type_;
HTTPfd.Name = fd.name_;
LOGINFO("Form data name: %s", fd.name_.c_str() );
- Request.FormData[ fd.name_ ] = HTTPfd;
+ TemplateRequest.Request.FormData[ fd.name_ ] = HTTPfd;
}
- if (Split.size() > 1)
+ bool bLuaTemplateSuccessful = false;
+ if (!bDontShowTemplate)
{
+ // New Lua web template
+ bLuaTemplateSuccessful = WebAdmin->m_pTemplate->CallFunction("ShowPage", sLuaUsertype(WebAdmin, "cWebAdmin"), sLuaUsertype(&TemplateRequest, "HTTPTemplateRequest"), Template);
+ }
+
+ if (!bLuaTemplateSuccessful)
+ {
+ AString BaseURL = WebAdmin->GetBaseURL(Split);
+ AString Menu;
+ Template = bDontShowTemplate ? "{CONTENT}" : WebAdmin->GetTemplate();
+ AString FoundPlugin;
+
for (PluginList::iterator itr = WebAdmin->m_Plugins.begin(); itr != WebAdmin->m_Plugins.end(); ++itr)
{
- if ((*itr)->GetWebTitle() == Split[1])
+ cWebPlugin* WebPlugin = *itr;
+ std::list< std::pair<AString, AString> > NameList = WebPlugin->GetTabNames();
+ for( std::list< std::pair<AString, AString> >::iterator Names = NameList.begin(); Names != NameList.end(); ++Names )
{
- Content = (*itr)->HandleWebRequest(&Request);
- cWebPlugin * WebPlugin = *itr;
- FoundPlugin = WebPlugin->GetWebTitle();
- AString TabName = WebPlugin->GetTabNameForRequest(&Request).first;
- if (!TabName.empty())
- {
- FoundPlugin += " - " + TabName;
- }
- break;
+ Menu += "<li><a href='" + BaseURL + WebPlugin->GetWebTitle().c_str() + "/" + (*Names).second + "'>" + (*Names).first + "</a></li>";
}
}
- }
- if( FoundPlugin.empty() ) // Default page
- {
- Content.clear();
- FoundPlugin = "Current Game";
- Content += "<h4>Server Name:</h4>";
- Content += "<p>" + std::string( cRoot::Get()->GetServer()->GetServerID() ) + "</p>";
-
- Content += "<h4>Plugins:</h4><ul>";
- cPluginManager* PM = cRoot::Get()->GetPluginManager();
- if( PM )
+ sWebAdminPage Page = WebAdmin->GetPage(TemplateRequest.Request);
+ AString Content = Page.Content;
+ FoundPlugin = Page.PluginName;
+ if (!Page.TabName.empty())
+ FoundPlugin += " - " + Page.TabName;
+
+ if( FoundPlugin.empty() ) // Default page
{
- const cPluginManager::PluginMap & List = PM->GetAllPlugins();
- for( cPluginManager::PluginMap::const_iterator itr = List.begin(); itr != List.end(); ++itr )
+ Content.clear();
+ FoundPlugin = "Current Game";
+ Content += "<h4>Server Name:</h4>";
+ Content += "<p>" + AString( cRoot::Get()->GetServer()->GetServerID() ) + "</p>";
+
+ Content += "<h4>Plugins:</h4><ul>";
+ cPluginManager* PM = cRoot::Get()->GetPluginManager();
+ if( PM )
{
- if( itr->second == NULL ) continue;
- AString VersionNum;
- AppendPrintf(Content, "<li>%s V.%i</li>", itr->second->GetName().c_str(), itr->second->GetVersion());
+ const cPluginManager::PluginMap & List = PM->GetAllPlugins();
+ for( cPluginManager::PluginMap::const_iterator itr = List.begin(); itr != List.end(); ++itr )
+ {
+ if( itr->second == NULL ) continue;
+ AString VersionNum;
+ AppendPrintf(Content, "<li>%s V.%i</li>", itr->second->GetName().c_str(), itr->second->GetVersion());
+ }
}
- }
- Content += "</ul>";
- Content += "<h4>Players:</h4><ul>";
+ Content += "</ul>";
+ Content += "<h4>Players:</h4><ul>";
- cPlayerAccum PlayerAccum;
- cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players
- if( World != NULL )
- {
- World->ForEachPlayer(PlayerAccum);
- Content.append(PlayerAccum.m_Contents);
+ cPlayerAccum PlayerAccum;
+ cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players
+ if( World != NULL )
+ {
+ World->ForEachPlayer(PlayerAccum);
+ Content.append(PlayerAccum.m_Contents);
+ }
+ Content += "</ul><br>";
}
- Content += "</ul><br>";
- }
-
+
- if (!bDontShowTemplate && (Split.size() > 1))
- {
- Content += "\n<p><a href='" + BaseURL + "'>Go back</a></p>";
- }
+ if (!bDontShowTemplate && (Split.size() > 1))
+ {
+ Content += "\n<p><a href='" + BaseURL + "'>Go back</a></p>";
+ }
- // mem usage
-#ifndef _WIN32
- rusage resource_usage;
- if (getrusage(RUSAGE_SELF, &resource_usage) != 0)
- {
- ReplaceString( Template, std::string("{MEM}"), "Error :(" );
+ AString MemUsage = GetMemoryUsage();
+ ReplaceString(Template, "{MEM}", MemUsage);
+ ReplaceString(Template, "{USERNAME}", r->username_);
+ ReplaceString(Template, "{MENU}", Menu);
+ ReplaceString(Template, "{PLUGIN_NAME}", FoundPlugin);
+ ReplaceString(Template, "{CONTENT}", Content);
+ ReplaceString(Template, "{TITLE}", "MCServer");
+
+ AString NumChunks;
+ Printf(NumChunks, "%d", cRoot::Get()->GetTotalChunkCount());
+ ReplaceString(Template, "{NUMCHUNKS}", NumChunks);
}
- else
- {
- AString MemUsage;
- Printf(MemUsage, "%0.2f", ((double)resource_usage.ru_maxrss / 1024 / 1024) );
- ReplaceString(Template, std::string("{MEM}"), MemUsage);
- }
-#else
- HANDLE hProcess = GetCurrentProcess();
- PROCESS_MEMORY_COUNTERS pmc;
- if( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc) ) )
- {
- AString MemUsage;
- Printf(MemUsage, "%0.2f", (pmc.WorkingSetSize / 1024.f / 1024.f) );
- ReplaceString( Template, "{MEM}", MemUsage );
- }
-#endif
- // end mem usage
-
- ReplaceString( Template, "{USERNAME}", r->username_ );
- ReplaceString( Template, "{MENU}", Menu );
- ReplaceString( Template, "{PLUGIN_NAME}", FoundPlugin );
- ReplaceString( Template, "{CONTENT}", Content );
- ReplaceString( Template, "{TITLE}", "MCServer" );
-
- AString NumChunks;
- Printf(NumChunks, "%d", cRoot::Get()->GetTotalChunkCount());
- ReplaceString(Template, "{NUMCHUNKS}", NumChunks);
r->answer_ = Template;
}
@@ -309,6 +279,14 @@ bool cWebAdmin::Init( int a_Port )
m_Port = m_IniFile->GetValueI("WebAdmin", "Port", 8080 );
}
+ // Initialize the WebAdmin template script and load the file
+ m_pTemplate->Initialize();
+ if (!m_pTemplate->LoadFile( FILE_IO_PREFIX "webadmin/template.lua") || !m_pTemplate->Execute())
+ {
+ LOGWARN("Could not load WebAdmin template.");
+ }
+
+
LOG("Starting WebAdmin on port %i", m_Port);
#ifdef _WIN32
@@ -354,9 +332,9 @@ void *cWebAdmin::ListenThread( void *lpParam )
-std::string cWebAdmin::GetTemplate()
+AString cWebAdmin::GetTemplate()
{
- std::string retVal = "";
+ AString retVal = "";
char SourceFile[] = "webadmin/template.html";
@@ -370,4 +348,91 @@ std::string cWebAdmin::GetTemplate()
f.ReadRestOfFile(retVal);
return retVal;
-} \ No newline at end of file
+}
+
+
+
+
+
+sWebAdminPage cWebAdmin::GetPage(const HTTPRequest& a_Request)
+{
+ sWebAdminPage Page;
+ AStringVector Split = StringSplit(a_Request.Path, "/");
+
+ // Find the plugin that corresponds to the requested path
+ AString FoundPlugin;
+ if (Split.size() > 1)
+ {
+ for (PluginList::iterator itr = WebAdmin->m_Plugins.begin(); itr != WebAdmin->m_Plugins.end(); ++itr)
+ {
+ if ((*itr)->GetWebTitle() == Split[1])
+ {
+ Page.Content = (*itr)->HandleWebRequest(&a_Request);
+ cWebPlugin * WebPlugin = *itr;
+ FoundPlugin = WebPlugin->GetWebTitle();
+ AString TabName = WebPlugin->GetTabNameForRequest(&a_Request).first;
+ Page.PluginName = FoundPlugin;
+ Page.TabName = TabName;
+ break;
+ }
+ }
+ }
+
+ // Return the page contents
+ return Page;
+}
+
+
+
+
+
+AString cWebAdmin::GetBaseURL( const AString& a_URL )
+{
+ return GetBaseURL(StringSplit(a_URL, "/"));
+}
+
+
+
+
+
+AString cWebAdmin::GetBaseURL( const AStringVector& a_URLSplit )
+{
+ AString BaseURL = "./";
+ if (a_URLSplit.size() > 1)
+ {
+ for (unsigned int i = 0; i < a_URLSplit.size(); i++)
+ {
+ BaseURL += "../";
+ }
+ BaseURL += "webadmin/";
+ }
+ return BaseURL;
+}
+
+
+
+
+
+AString cWebAdmin::GetMemoryUsage(void)
+{
+ AString MemUsage;
+#ifndef _WIN32
+ rusage resource_usage;
+ if (getrusage(RUSAGE_SELF, &resource_usage) != 0)
+ {
+ MemUsage = "Error :(";
+ }
+ else
+ {
+ Printf(MemUsage, "%0.2f", ((double)resource_usage.ru_maxrss / 1024 / 1024) );
+ }
+#else
+ HANDLE hProcess = GetCurrentProcess();
+ PROCESS_MEMORY_COUNTERS pmc;
+ if( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc) ) )
+ {
+ Printf(MemUsage, "%0.2f", (pmc.WorkingSetSize / 1024.f / 1024.f) );
+ }
+#endif
+ return MemUsage;
+}
diff --git a/source/WebAdmin.h b/source/WebAdmin.h
index ad733e704..4b12b955e 100644
--- a/source/WebAdmin.h
+++ b/source/WebAdmin.h
@@ -4,66 +4,93 @@
#include "OSSupport/Socket.h"
class cStringMap;
+class cLuaScript;
-struct HTTPFormData // tolua_export
-{ // tolua_export
- std::string Name; // tolua_export
- std::string Value; // tolua_export
- std::string Type; // tolua_export
+struct HTTPFormData // tolua_export
+{ // tolua_export
+ std::string Name; // tolua_export
+ std::string Value; // tolua_export
+ std::string Type; // tolua_export
};// tolua_export
-struct HTTPRequest // tolua_export
-{ // tolua_export
+struct HTTPRequest // tolua_export
+{ // tolua_export
typedef std::map< std::string, std::string > StringStringMap;
typedef std::map< std::string, HTTPFormData > FormDataMap;
- std::string Method; // tolua_export
- std::string Path; // tolua_export
- StringStringMap Params; // >> EXPORTED IN MANUALBINDINGS <<
- StringStringMap PostParams; // >> EXPORTED IN MANUALBINDINGS <<
- std::string Username; // tolua_export
- FormDataMap FormData; // >> EXPORTED IN MANUALBINDINGS <<
+ AString Method; // tolua_export
+ AString Path; // tolua_export
+ StringStringMap Params; // >> EXPORTED IN MANUALBINDINGS <<
+ StringStringMap PostParams; // >> EXPORTED IN MANUALBINDINGS <<
+ AString Username; // tolua_export
+ FormDataMap FormData; // >> EXPORTED IN MANUALBINDINGS <<
}; // tolua_export
+struct HTTPTemplateRequest // tolua_export
+{ // tolua_export
+ HTTPRequest Request; // tolua_export
+
+}; // tolua_export
+
+// tolua_begin
+struct sWebAdminPage
+{
+ AString Content;
+ AString PluginName;
+ AString TabName;
+};
+// tolua_end
+
struct lua_State;
class cEvent;
class cIniFile;
class cWebPlugin;
-class cWebAdmin
-{
-public:
+
+class cWebAdmin // tolua_export
+{ // tolua_export
+public: // tolua_export
cWebAdmin( int a_Port = 8080 );
~cWebAdmin();
- bool Init( int a_Port );
+ bool Init( int a_Port );
- void AddPlugin( cWebPlugin* a_Plugin );
- void RemovePlugin( cWebPlugin* a_Plugin );
+ void AddPlugin( cWebPlugin* a_Plugin );
+ void RemovePlugin( cWebPlugin* a_Plugin );
typedef std::list< cWebPlugin* > PluginList;
- PluginList GetPlugins() { return m_Plugins; }
- static void Request_Handler(webserver::http_request* r);
+ // TODO: Convert this to the auto-locking callback mechanism used for looping players in worlds and such
+ PluginList GetPlugins() const { return m_Plugins; } // >> EXPORTED IN MANUALBINDINGS <<
+
+ static void Request_Handler(webserver::http_request* r);
+
+ int GetPort() { return m_Port; } // tolua_export
- int GetPort() { return m_Port; }
+ sWebAdminPage GetPage(const HTTPRequest& a_Request); // tolua_export
+ AString GetBaseURL(const AString& a_URL); // tolua_export
+ AString GetBaseURL(const AStringVector& a_URLSplit);
+
+ static AString GetMemoryUsage(void); // tolua_export
private:
#ifdef _WIN32
static DWORD WINAPI ListenThread(LPVOID lpParam);
#else
- static void *ListenThread( void *lpParam );
+ static void * ListenThread( void *lpParam );
#endif
- std::string GetTemplate();
+ AString GetTemplate();
+
+ cLuaScript* m_pTemplate;
- int m_Port;
+ int m_Port;
- bool m_bConnected;
- cSocket m_ListenSocket;
+ bool m_bConnected;
+ cSocket m_ListenSocket;
- cIniFile* m_IniFile;
- PluginList m_Plugins;
+ cIniFile* m_IniFile;
+ PluginList m_Plugins;
- cEvent* m_Event;
+ cEvent* m_Event;
- webserver* m_WebServer;
-}; \ No newline at end of file
+ webserver* m_WebServer;
+}; // tolua_export \ No newline at end of file
diff --git a/source/WebPlugin.cpp b/source/WebPlugin.cpp
index 343ca64fa..48ddb2076 100644
--- a/source/WebPlugin.cpp
+++ b/source/WebPlugin.cpp
@@ -59,7 +59,7 @@ std::list<std::pair<AString, AString> > cWebPlugin::GetTabNames(void)
-std::pair< AString, AString > cWebPlugin::GetTabNameForRequest(HTTPRequest * a_Request)
+std::pair< AString, AString > cWebPlugin::GetTabNameForRequest(const HTTPRequest * a_Request)
{
std::pair< AString, AString > Names;
AStringVector Split = StringSplit(a_Request->Path, "/");
diff --git a/source/WebPlugin.h b/source/WebPlugin.h
index 160c3c126..22587b892 100644
--- a/source/WebPlugin.h
+++ b/source/WebPlugin.h
@@ -16,10 +16,10 @@ public:
cWebPlugin();
virtual ~cWebPlugin();
- virtual const AString & GetWebTitle(void) const = 0;
// tolua_begin
+ virtual const AString GetWebTitle(void) const = 0;
- virtual AString HandleWebRequest( HTTPRequest * a_Request ) = 0;
+ virtual AString HandleWebRequest(const HTTPRequest * a_Request ) = 0;
static AString SafeString( const AString & a_String );
// tolua_end
@@ -35,8 +35,9 @@ public:
typedef std::list< sWebPluginTab* > TabList;
TabList & GetTabs() { return m_Tabs; }
- std::list< std::pair<AString, AString> > GetTabNames();
- std::pair< AString, AString > GetTabNameForRequest( HTTPRequest* a_Request );
+ typedef std::list< std::pair<AString, AString> > TabNameList;
+ TabNameList GetTabNames(); // >> EXPORTED IN MANUALBINDINGS <<
+ std::pair< AString, AString > GetTabNameForRequest(const HTTPRequest* a_Request );
private:
TabList m_Tabs;
diff --git a/source/World.cpp b/source/World.cpp
index b632fe7f9..880f6e5b2 100644
--- a/source/World.cpp
+++ b/source/World.cpp
@@ -2364,6 +2364,24 @@ int cWorld::SpawnMob(double a_PosX, double a_PosY, double a_PosZ, int a_EntityTy
+void cWorld::TabCompleteUserName(const AString & a_Text, AStringVector & a_Results)
+{
+ cCSLock Lock(m_CSPlayers);
+ for (cPlayerList::iterator itr = m_Players.begin(), end = m_Players.end(); itr != end; ++itr)
+ {
+ if (NoCaseCompare((*itr)->GetName().substr(0, a_Text.length()), a_Text) != 0)
+ {
+ // Player name doesn't match
+ continue;
+ }
+ a_Results.push_back((*itr)->GetName());
+ }
+}
+
+
+
+
+
cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const char * a_FluidName, BLOCKTYPE a_SimulateBlock, BLOCKTYPE a_StationaryBlock)
{
AString SimulatorNameKey;
diff --git a/source/World.h b/source/World.h
index 5da7218e2..9de00ceec 100644
--- a/source/World.h
+++ b/source/World.h
@@ -106,7 +106,18 @@ public:
SetTimeOfDay(a_TimeOfDay);
}
+ /// Returns the current game mode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable
eGameMode GetGameMode(void) const { return m_GameMode; }
+
+ /// Returns true if the world is in Creative mode
+ bool IsGameModeCreative(void) const { return (m_GameMode == gmCreative); }
+
+ /// Returns true if the world is in Survival mode
+ bool IsGameModeSurvival(void) const { return (m_GameMode == gmSurvival); }
+
+ /// Returns true if the world is in Adventure mode
+ bool IsGameModeAdventure(void) const { return (m_GameMode == gmAdventure); }
+
bool IsPVPEnabled(void) const { return m_bEnabledPVP; }
bool IsDeepSnowEnabled(void) const { return m_IsDeepSnowEnabled; }
@@ -508,6 +519,9 @@ public:
/// Returns a random number from the m_TickRand in range [0 .. a_Range]. To be used only in the tick thread!
int GetTickRandomNumber(unsigned a_Range) { return (int)(m_TickRand.randInt(a_Range)); }
+ /// Appends all usernames starting with a_Text (case-insensitive) into Results
+ void TabCompleteUserName(const AString & a_Text, AStringVector & a_Results);
+
private:
friend class cRoot;
diff --git a/source/WorldStorage/NBTChunkSerializer.cpp b/source/WorldStorage/NBTChunkSerializer.cpp
index d391325c9..da1b9e1c4 100644
--- a/source/WorldStorage/NBTChunkSerializer.cpp
+++ b/source/WorldStorage/NBTChunkSerializer.cpp
@@ -1,459 +1,459 @@
-
-// NBTChunkSerializer.cpp
-
-
-#include "Globals.h"
-#include "NBTChunkSerializer.h"
-#include "../BlockID.h"
-#include "../BlockEntities/ChestEntity.h"
-#include "../BlockEntities/DispenserEntity.h"
-#include "../BlockEntities/DropperEntity.h"
-#include "../BlockEntities/FurnaceEntity.h"
-#include "../BlockEntities/HopperEntity.h"
-#include "../BlockEntities/JukeboxEntity.h"
-#include "../BlockEntities/NoteEntity.h"
-#include "../BlockEntities/SignEntity.h"
-#include "../ItemGrid.h"
-#include "../StringCompression.h"
-#include "../Entity.h"
-#include "../OSSupport/MakeDir.h"
-#include "FastNBT.h"
-#include "../FallingBlock.h"
-#include "../Minecart.h"
-#include "../Mobs/Monster.h"
-#include "../Pickup.h"
-
-
-
-
-cNBTChunkSerializer::cNBTChunkSerializer(cFastNBTWriter & a_Writer) :
- m_BiomesAreValid(false),
- m_Writer(a_Writer),
- m_IsTagOpen(false),
- m_HasHadEntity(false),
- m_HasHadBlockEntity(false),
- m_IsLightValid(false)
-{
-}
-
-
-
-
-
-void cNBTChunkSerializer::Finish(void)
-{
- if (m_IsTagOpen)
- {
- m_Writer.EndList();
- }
-
- // If light not valid, reset it to all zeroes:
- if (!m_IsLightValid)
- {
- memset(m_BlockLight, 0, sizeof(m_BlockLight));
- memset(m_BlockSkyLight, 0, sizeof(m_BlockSkyLight));
- }
-}
-
-
-
-
-
-void cNBTChunkSerializer::AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName)
-{
- m_Writer.BeginCompound(a_CompoundName);
- m_Writer.AddShort("id", (short)(a_Item.m_ItemType));
- m_Writer.AddShort("Damage", a_Item.m_ItemDamage);
- m_Writer.AddByte ("Count", a_Item.m_ItemCount);
- if (a_Slot >= 0)
- {
- m_Writer.AddByte ("Slot", (unsigned char)a_Slot);
- }
-
- // Write the enchantments:
- if (!a_Item.m_Enchantments.IsEmpty())
- {
- const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
- m_Writer.BeginCompound("tag");
- a_Item.m_Enchantments.WriteToNBTCompound(m_Writer, TagName);
- m_Writer.EndCompound();
- }
-
- m_Writer.EndCompound();
-}
-
-
-
-
-
-void cNBTChunkSerializer::AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum)
-{
- int NumSlots = a_Grid.GetNumSlots();
- for (int i = 0; i < NumSlots; i++)
- {
- const cItem & Item = a_Grid.GetSlot(i);
- if (Item.IsEmpty())
- {
- continue;
- }
- AddItem(Item, i + a_BeginSlotNum);
- } // for i - chest slots[]
-}
-
-
-
-
-
-void cNBTChunkSerializer::AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID)
-{
- m_Writer.AddInt ("x", a_Entity->GetPosX());
- m_Writer.AddInt ("y", a_Entity->GetPosY());
- m_Writer.AddInt ("z", a_Entity->GetPosZ());
- m_Writer.AddString("id", a_EntityTypeID);
-}
-
-
-
-
-
-void cNBTChunkSerializer::AddChestEntity(cChestEntity * a_Entity)
-{
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Entity, "Chest");
- m_Writer.BeginList("Items", TAG_Compound);
- AddItemGrid(a_Entity->GetContents());
- m_Writer.EndList();
- m_Writer.EndCompound();
-}
-
-
-
-
-
-void cNBTChunkSerializer::AddDispenserEntity(cDispenserEntity * a_Entity)
-{
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Entity, "Trap");
- m_Writer.BeginList("Items", TAG_Compound);
- AddItemGrid(a_Entity->GetContents());
- m_Writer.EndList();
- m_Writer.EndCompound();
-}
-
-
-
-
-
-void cNBTChunkSerializer::AddDropperEntity(cDropperEntity * a_Entity)
-{
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Entity, "Dropper");
- m_Writer.BeginList("Items", TAG_Compound);
- AddItemGrid(a_Entity->GetContents());
- m_Writer.EndList();
- m_Writer.EndCompound();
-}
-
-
-
-
-
-void cNBTChunkSerializer::AddFurnaceEntity(cFurnaceEntity * a_Furnace)
-{
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Furnace, "Furnace");
- m_Writer.BeginList("Items", TAG_Compound);
- AddItemGrid(a_Furnace->GetContents());
- m_Writer.EndList();
- m_Writer.AddShort("BurnTime", a_Furnace->GetFuelBurnTimeLeft());
- m_Writer.AddShort("CookTime", a_Furnace->GetTimeCooked());
- m_Writer.EndCompound();
-}
-
-
-
-
-
-void cNBTChunkSerializer::AddHopperEntity(cHopperEntity * a_Entity)
-{
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Entity, "Hopper");
- m_Writer.BeginList("Items", TAG_Compound);
- AddItemGrid(a_Entity->GetContents());
- m_Writer.EndList();
- m_Writer.EndCompound();
-}
-
-
-
-
-
-void cNBTChunkSerializer::AddJukeboxEntity(cJukeboxEntity * a_Jukebox)
-{
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Jukebox, "RecordPlayer");
- m_Writer.AddInt("Record", a_Jukebox->GetRecord());
- m_Writer.EndCompound();
-}
-
-
-
-
-
-void cNBTChunkSerializer::AddNoteEntity(cNoteEntity * a_Note)
-{
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Note, "Music");
- m_Writer.AddByte("note", a_Note->GetPitch());
- m_Writer.EndCompound();
-}
-
-
-
-
-
-void cNBTChunkSerializer::AddSignEntity(cSignEntity * a_Sign)
-{
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Sign, "Sign");
- m_Writer.AddString("Text1", a_Sign->GetLine(0));
- m_Writer.AddString("Text2", a_Sign->GetLine(1));
- m_Writer.AddString("Text3", a_Sign->GetLine(2));
- m_Writer.AddString("Text4", a_Sign->GetLine(3));
- m_Writer.EndCompound();
-}
-
-
-
-
-
-void cNBTChunkSerializer::AddBasicEntity(cEntity * a_Entity, const AString & a_ClassName)
-{
- m_Writer.AddString("id", a_ClassName);
- m_Writer.BeginList("Pos", TAG_Double);
- m_Writer.AddDouble("", a_Entity->GetPosX());
- m_Writer.AddDouble("", a_Entity->GetPosY());
- m_Writer.AddDouble("", a_Entity->GetPosZ());
- m_Writer.EndList();
- m_Writer.BeginList("Motion", TAG_Double);
- m_Writer.AddDouble("", a_Entity->GetSpeedX());
- m_Writer.AddDouble("", a_Entity->GetSpeedY());
- m_Writer.AddDouble("", a_Entity->GetSpeedZ());
- m_Writer.EndList();
- m_Writer.BeginList("Rotation", TAG_Double);
- m_Writer.AddDouble("", a_Entity->GetRotation());
- m_Writer.AddDouble("", a_Entity->GetPitch());
- m_Writer.EndList();
-}
-
-
-
-
-
-void cNBTChunkSerializer::AddFallingBlockEntity(cFallingBlock * a_FallingBlock)
-{
- m_Writer.BeginCompound("");
- AddBasicEntity(a_FallingBlock, "FallingSand");
- m_Writer.AddInt("TileID", a_FallingBlock->GetBlockType());
- m_Writer.AddByte("Data", a_FallingBlock->GetBlockMeta());
- m_Writer.AddByte("Time", 1); // Unused in MCServer, Vanilla said to need nonzero
- m_Writer.AddByte("DropItem", 1);
- m_Writer.AddByte("HurtEntities", a_FallingBlock->GetBlockType() == E_BLOCK_ANVIL);
- m_Writer.EndCompound();
-}
-
-
-
-
-
-void cNBTChunkSerializer::AddMinecartEntity(cMinecart * a_Minecart)
-{
- const char * EntityClass = NULL;
- switch (a_Minecart->GetPayload())
- {
- case cMinecart::mpNone: EntityClass = "MinecartRideable"; break;
- case cMinecart::mpChest: EntityClass = "MinecartChest"; break;
- case cMinecart::mpFurnace: EntityClass = "MinecartFurnace"; break;
- default:
- {
- ASSERT(!"Unhandled minecart payload type");
- return;
- }
- } // switch (payload)
-
- m_Writer.BeginCompound("");
- AddBasicEntity(a_Minecart, EntityClass);
- switch (a_Minecart->GetPayload())
- {
- case cMinecart::mpChest:
- {
- // Add chest contents into the Items tag:
- AddMinecartChestContents((cMinecartWithChest *)a_Minecart);
- break;
- }
-
- case cMinecart::mpFurnace:
- {
- // TODO: Add "Push" and "Fuel" tags
- break;
- }
- } // switch (Payload)
- m_Writer.EndCompound();
-}
-
-
-
-
-
-void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster)
-{
- // TODO
-}
-
-
-
-
-
-void cNBTChunkSerializer::AddPickupEntity(cPickup * a_Pickup)
-{
- m_Writer.BeginCompound("");
- AddBasicEntity(a_Pickup, "Item");
- AddItem(a_Pickup->GetItem(), -1, "Item");
- m_Writer.AddShort("Health", a_Pickup->GetHealth());
- m_Writer.AddShort("Age", a_Pickup->GetAge());
- m_Writer.EndCompound();
-}
-
-
-
-
-
-void cNBTChunkSerializer::AddMinecartChestContents(cMinecartWithChest * a_Minecart)
-{
- m_Writer.BeginList("Items", TAG_Compound);
- for (int i = 0; i < cMinecartWithChest::NumSlots; i++)
- {
- const cItem & Item = a_Minecart->GetSlot(i);
- if (Item.IsEmpty())
- {
- continue;
- }
- AddItem(Item, i);
- }
- m_Writer.EndList();
-}
-
-
-
-
-
-bool cNBTChunkSerializer::LightIsValid(bool a_IsLightValid)
-{
- m_IsLightValid = a_IsLightValid;
- return a_IsLightValid; // We want lighting only if it's valid, otherwise don't bother
-}
-
-
-
-
-
-void cNBTChunkSerializer::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap)
-{
- memcpy(m_Biomes, a_BiomeMap, sizeof(m_Biomes));
- for (int i = 0; i < ARRAYCOUNT(m_Biomes); i++)
- {
- if ((*a_BiomeMap)[i] < 255)
- {
- // Normal MC biome, copy as-is:
- m_VanillaBiomes[i] = (unsigned char)((*a_BiomeMap)[i]);
- }
- else
- {
- // TODO: MCS-specific biome, need to map to some basic MC biome:
- ASSERT(!"Unimplemented MCS-specific biome");
- return;
- }
- } // for i - m_BiomeMap[]
- m_BiomesAreValid = true;
-}
-
-
-
-
-
-void cNBTChunkSerializer::Entity(cEntity * a_Entity)
-{
- // Add entity into NBT:
- if (m_IsTagOpen)
- {
- if (!m_HasHadEntity)
- {
- m_Writer.EndList();
- m_Writer.BeginList("Entities", TAG_Compound);
- }
- }
- else
- {
- m_Writer.BeginList("Entities", TAG_Compound);
- }
- m_IsTagOpen = true;
- m_HasHadEntity = true;
-
- switch (a_Entity->GetEntityType())
- {
- case cEntity::etFallingBlock: AddFallingBlockEntity((cFallingBlock *)a_Entity); break;
- case cEntity::etMinecart: AddMinecartEntity ((cMinecart *) a_Entity); break;
- case cEntity::etMonster: AddMonsterEntity ((cMonster *) a_Entity); break;
- case cEntity::etPickup: AddPickupEntity ((cPickup *) a_Entity); break;
- case cEntity::etPlayer: return; // Players aren't saved into the world
- default:
- {
- ASSERT(!"Unhandled entity type is being saved");
- break;
- }
- }
-}
-
-
-
-
-
-void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity)
-{
- if (m_IsTagOpen)
- {
- if (!m_HasHadBlockEntity)
- {
- m_Writer.EndList();
- m_Writer.BeginList("TileEntities", TAG_Compound);
- }
- }
- else
- {
- m_Writer.BeginList("TileEntities", TAG_Compound);
- }
- m_IsTagOpen = true;
-
- // Add tile-entity into NBT:
- switch (a_Entity->GetBlockType())
- {
- case E_BLOCK_CHEST: AddChestEntity ((cChestEntity *) a_Entity); break;
- case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break;
- case E_BLOCK_DROPPER: AddDropperEntity ((cDropperEntity *) a_Entity); break;
- case E_BLOCK_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break;
- case E_BLOCK_HOPPER: AddHopperEntity ((cHopperEntity *) a_Entity); break;
- case E_BLOCK_SIGN_POST:
- case E_BLOCK_WALLSIGN: AddSignEntity ((cSignEntity *) a_Entity); break;
- case E_BLOCK_NOTE_BLOCK: AddNoteEntity ((cNoteEntity *) a_Entity); break;
- case E_BLOCK_JUKEBOX: AddJukeboxEntity ((cJukeboxEntity *) a_Entity); break;
- default:
- {
- ASSERT(!"Unhandled block entity saved into Anvil");
- }
- }
- m_HasHadBlockEntity = true;
-}
-
-
-
-
+
+// NBTChunkSerializer.cpp
+
+
+#include "Globals.h"
+#include "NBTChunkSerializer.h"
+#include "../BlockID.h"
+#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/DispenserEntity.h"
+#include "../BlockEntities/DropperEntity.h"
+#include "../BlockEntities/FurnaceEntity.h"
+#include "../BlockEntities/HopperEntity.h"
+#include "../BlockEntities/JukeboxEntity.h"
+#include "../BlockEntities/NoteEntity.h"
+#include "../BlockEntities/SignEntity.h"
+#include "../ItemGrid.h"
+#include "../StringCompression.h"
+#include "../Entity.h"
+#include "../OSSupport/MakeDir.h"
+#include "FastNBT.h"
+#include "../FallingBlock.h"
+#include "../Minecart.h"
+#include "../Mobs/Monster.h"
+#include "../Pickup.h"
+
+
+
+
+cNBTChunkSerializer::cNBTChunkSerializer(cFastNBTWriter & a_Writer) :
+ m_BiomesAreValid(false),
+ m_Writer(a_Writer),
+ m_IsTagOpen(false),
+ m_HasHadEntity(false),
+ m_HasHadBlockEntity(false),
+ m_IsLightValid(false)
+{
+}
+
+
+
+
+
+void cNBTChunkSerializer::Finish(void)
+{
+ if (m_IsTagOpen)
+ {
+ m_Writer.EndList();
+ }
+
+ // If light not valid, reset it to all zeroes:
+ if (!m_IsLightValid)
+ {
+ memset(m_BlockLight, 0, sizeof(m_BlockLight));
+ memset(m_BlockSkyLight, 0, sizeof(m_BlockSkyLight));
+ }
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName)
+{
+ m_Writer.BeginCompound(a_CompoundName);
+ m_Writer.AddShort("id", (short)(a_Item.m_ItemType));
+ m_Writer.AddShort("Damage", a_Item.m_ItemDamage);
+ m_Writer.AddByte ("Count", a_Item.m_ItemCount);
+ if (a_Slot >= 0)
+ {
+ m_Writer.AddByte ("Slot", (unsigned char)a_Slot);
+ }
+
+ // Write the enchantments:
+ if (!a_Item.m_Enchantments.IsEmpty())
+ {
+ const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
+ m_Writer.BeginCompound("tag");
+ a_Item.m_Enchantments.WriteToNBTCompound(m_Writer, TagName);
+ m_Writer.EndCompound();
+ }
+
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum)
+{
+ int NumSlots = a_Grid.GetNumSlots();
+ for (int i = 0; i < NumSlots; i++)
+ {
+ const cItem & Item = a_Grid.GetSlot(i);
+ if (Item.IsEmpty())
+ {
+ continue;
+ }
+ AddItem(Item, i + a_BeginSlotNum);
+ } // for i - chest slots[]
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID)
+{
+ m_Writer.AddInt ("x", a_Entity->GetPosX());
+ m_Writer.AddInt ("y", a_Entity->GetPosY());
+ m_Writer.AddInt ("z", a_Entity->GetPosZ());
+ m_Writer.AddString("id", a_EntityTypeID);
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddChestEntity(cChestEntity * a_Entity)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Entity, "Chest");
+ m_Writer.BeginList("Items", TAG_Compound);
+ AddItemGrid(a_Entity->GetContents());
+ m_Writer.EndList();
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddDispenserEntity(cDispenserEntity * a_Entity)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Entity, "Trap");
+ m_Writer.BeginList("Items", TAG_Compound);
+ AddItemGrid(a_Entity->GetContents());
+ m_Writer.EndList();
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddDropperEntity(cDropperEntity * a_Entity)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Entity, "Dropper");
+ m_Writer.BeginList("Items", TAG_Compound);
+ AddItemGrid(a_Entity->GetContents());
+ m_Writer.EndList();
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddFurnaceEntity(cFurnaceEntity * a_Furnace)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Furnace, "Furnace");
+ m_Writer.BeginList("Items", TAG_Compound);
+ AddItemGrid(a_Furnace->GetContents());
+ m_Writer.EndList();
+ m_Writer.AddShort("BurnTime", a_Furnace->GetFuelBurnTimeLeft());
+ m_Writer.AddShort("CookTime", a_Furnace->GetTimeCooked());
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddHopperEntity(cHopperEntity * a_Entity)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Entity, "Hopper");
+ m_Writer.BeginList("Items", TAG_Compound);
+ AddItemGrid(a_Entity->GetContents());
+ m_Writer.EndList();
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddJukeboxEntity(cJukeboxEntity * a_Jukebox)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Jukebox, "RecordPlayer");
+ m_Writer.AddInt("Record", a_Jukebox->GetRecord());
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddNoteEntity(cNoteEntity * a_Note)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Note, "Music");
+ m_Writer.AddByte("note", a_Note->GetPitch());
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddSignEntity(cSignEntity * a_Sign)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Sign, "Sign");
+ m_Writer.AddString("Text1", a_Sign->GetLine(0));
+ m_Writer.AddString("Text2", a_Sign->GetLine(1));
+ m_Writer.AddString("Text3", a_Sign->GetLine(2));
+ m_Writer.AddString("Text4", a_Sign->GetLine(3));
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddBasicEntity(cEntity * a_Entity, const AString & a_ClassName)
+{
+ m_Writer.AddString("id", a_ClassName);
+ m_Writer.BeginList("Pos", TAG_Double);
+ m_Writer.AddDouble("", a_Entity->GetPosX());
+ m_Writer.AddDouble("", a_Entity->GetPosY());
+ m_Writer.AddDouble("", a_Entity->GetPosZ());
+ m_Writer.EndList();
+ m_Writer.BeginList("Motion", TAG_Double);
+ m_Writer.AddDouble("", a_Entity->GetSpeedX());
+ m_Writer.AddDouble("", a_Entity->GetSpeedY());
+ m_Writer.AddDouble("", a_Entity->GetSpeedZ());
+ m_Writer.EndList();
+ m_Writer.BeginList("Rotation", TAG_Double);
+ m_Writer.AddDouble("", a_Entity->GetRotation());
+ m_Writer.AddDouble("", a_Entity->GetPitch());
+ m_Writer.EndList();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddFallingBlockEntity(cFallingBlock * a_FallingBlock)
+{
+ m_Writer.BeginCompound("");
+ AddBasicEntity(a_FallingBlock, "FallingSand");
+ m_Writer.AddInt("TileID", a_FallingBlock->GetBlockType());
+ m_Writer.AddByte("Data", a_FallingBlock->GetBlockMeta());
+ m_Writer.AddByte("Time", 1); // Unused in MCServer, Vanilla said to need nonzero
+ m_Writer.AddByte("DropItem", 1);
+ m_Writer.AddByte("HurtEntities", a_FallingBlock->GetBlockType() == E_BLOCK_ANVIL);
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddMinecartEntity(cMinecart * a_Minecart)
+{
+ const char * EntityClass = NULL;
+ switch (a_Minecart->GetPayload())
+ {
+ case cMinecart::mpNone: EntityClass = "MinecartRideable"; break;
+ case cMinecart::mpChest: EntityClass = "MinecartChest"; break;
+ case cMinecart::mpFurnace: EntityClass = "MinecartFurnace"; break;
+ default:
+ {
+ ASSERT(!"Unhandled minecart payload type");
+ return;
+ }
+ } // switch (payload)
+
+ m_Writer.BeginCompound("");
+ AddBasicEntity(a_Minecart, EntityClass);
+ switch (a_Minecart->GetPayload())
+ {
+ case cMinecart::mpChest:
+ {
+ // Add chest contents into the Items tag:
+ AddMinecartChestContents((cMinecartWithChest *)a_Minecart);
+ break;
+ }
+
+ case cMinecart::mpFurnace:
+ {
+ // TODO: Add "Push" and "Fuel" tags
+ break;
+ }
+ } // switch (Payload)
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster)
+{
+ // TODO
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddPickupEntity(cPickup * a_Pickup)
+{
+ m_Writer.BeginCompound("");
+ AddBasicEntity(a_Pickup, "Item");
+ AddItem(a_Pickup->GetItem(), -1, "Item");
+ m_Writer.AddShort("Health", a_Pickup->GetHealth());
+ m_Writer.AddShort("Age", a_Pickup->GetAge());
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddMinecartChestContents(cMinecartWithChest * a_Minecart)
+{
+ m_Writer.BeginList("Items", TAG_Compound);
+ for (int i = 0; i < cMinecartWithChest::NumSlots; i++)
+ {
+ const cItem & Item = a_Minecart->GetSlot(i);
+ if (Item.IsEmpty())
+ {
+ continue;
+ }
+ AddItem(Item, i);
+ }
+ m_Writer.EndList();
+}
+
+
+
+
+
+bool cNBTChunkSerializer::LightIsValid(bool a_IsLightValid)
+{
+ m_IsLightValid = a_IsLightValid;
+ return a_IsLightValid; // We want lighting only if it's valid, otherwise don't bother
+}
+
+
+
+
+
+void cNBTChunkSerializer::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap)
+{
+ memcpy(m_Biomes, a_BiomeMap, sizeof(m_Biomes));
+ for (int i = 0; i < ARRAYCOUNT(m_Biomes); i++)
+ {
+ if ((*a_BiomeMap)[i] < 255)
+ {
+ // Normal MC biome, copy as-is:
+ m_VanillaBiomes[i] = (unsigned char)((*a_BiomeMap)[i]);
+ }
+ else
+ {
+ // TODO: MCS-specific biome, need to map to some basic MC biome:
+ ASSERT(!"Unimplemented MCS-specific biome");
+ return;
+ }
+ } // for i - m_BiomeMap[]
+ m_BiomesAreValid = true;
+}
+
+
+
+
+
+void cNBTChunkSerializer::Entity(cEntity * a_Entity)
+{
+ // Add entity into NBT:
+ if (m_IsTagOpen)
+ {
+ if (!m_HasHadEntity)
+ {
+ m_Writer.EndList();
+ m_Writer.BeginList("Entities", TAG_Compound);
+ }
+ }
+ else
+ {
+ m_Writer.BeginList("Entities", TAG_Compound);
+ }
+ m_IsTagOpen = true;
+ m_HasHadEntity = true;
+
+ switch (a_Entity->GetEntityType())
+ {
+ case cEntity::etFallingBlock: AddFallingBlockEntity((cFallingBlock *)a_Entity); break;
+ case cEntity::etMinecart: AddMinecartEntity ((cMinecart *) a_Entity); break;
+ case cEntity::etMonster: AddMonsterEntity ((cMonster *) a_Entity); break;
+ case cEntity::etPickup: AddPickupEntity ((cPickup *) a_Entity); break;
+ case cEntity::etPlayer: return; // Players aren't saved into the world
+ default:
+ {
+ ASSERT(!"Unhandled entity type is being saved");
+ break;
+ }
+ }
+}
+
+
+
+
+
+void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity)
+{
+ if (m_IsTagOpen)
+ {
+ if (!m_HasHadBlockEntity)
+ {
+ m_Writer.EndList();
+ m_Writer.BeginList("TileEntities", TAG_Compound);
+ }
+ }
+ else
+ {
+ m_Writer.BeginList("TileEntities", TAG_Compound);
+ }
+ m_IsTagOpen = true;
+
+ // Add tile-entity into NBT:
+ switch (a_Entity->GetBlockType())
+ {
+ case E_BLOCK_CHEST: AddChestEntity ((cChestEntity *) a_Entity); break;
+ case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break;
+ case E_BLOCK_DROPPER: AddDropperEntity ((cDropperEntity *) a_Entity); break;
+ case E_BLOCK_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break;
+ case E_BLOCK_HOPPER: AddHopperEntity ((cHopperEntity *) a_Entity); break;
+ case E_BLOCK_SIGN_POST:
+ case E_BLOCK_WALLSIGN: AddSignEntity ((cSignEntity *) a_Entity); break;
+ case E_BLOCK_NOTE_BLOCK: AddNoteEntity ((cNoteEntity *) a_Entity); break;
+ case E_BLOCK_JUKEBOX: AddJukeboxEntity ((cJukeboxEntity *) a_Entity); break;
+ default:
+ {
+ ASSERT(!"Unhandled block entity saved into Anvil");
+ }
+ }
+ m_HasHadBlockEntity = true;
+}
+
+
+
+
diff --git a/source/WorldStorage/NBTChunkSerializer.h b/source/WorldStorage/NBTChunkSerializer.h
index c71286797..1ccd356b0 100644
--- a/source/WorldStorage/NBTChunkSerializer.h
+++ b/source/WorldStorage/NBTChunkSerializer.h
@@ -1,110 +1,110 @@
-
-// NBTChunkSerializer.h
-
-// Declares the cNBTChunkSerializer class that is used for saving individual chunks into NBT format used by Anvil
-
-
-
-
-
-#pragma once
-
-#include "../ChunkDef.h"
-
-
-
-
-
-// fwd:
-class cFastNBTWriter;
-class cEntity;
-class cBlockEntity;
-class cChestEntity;
-class cDispenserEntity;
-class cDropperEntity;
-class cFurnaceEntity;
-class cHopperEntity;
-class cJukeboxEntity;
-class cNoteEntity;
-class cSignEntity;
-class cFallingBlock;
-class cMinecart;
-class cMinecartWithChest;
-class cMinecartWithFurnace;
-class cMonster;
-class cPickup;
-class cItemGrid;
-
-
-
-
-
-class cNBTChunkSerializer :
- public cChunkDataSeparateCollector
-{
-public:
- cChunkDef::BiomeMap m_Biomes;
- unsigned char m_VanillaBiomes[cChunkDef::Width * cChunkDef::Width];
- bool m_BiomesAreValid;
-
-
- cNBTChunkSerializer(cFastNBTWriter & a_Writer);
-
- /// Close NBT tags that we've opened
- void Finish(void);
-
- bool IsLightValid(void) const {return m_IsLightValid; }
-
-protected:
-
- /* From cChunkDataSeparateCollector we inherit:
- - m_BlockTypes[]
- - m_BlockMetas[]
- - m_BlockLight[]
- - m_BlockSkyLight[]
- */
-
- cFastNBTWriter & m_Writer;
-
- bool m_IsTagOpen; // True if a tag has been opened in the callbacks and not yet closed.
- bool m_HasHadEntity; // True if any Entity has already been received and processed
- bool m_HasHadBlockEntity; // True if any BlockEntity has already been received and processed
- bool m_IsLightValid; // True if the chunk lighting is valid
-
-
- /// Writes an item into the writer, if slot >= 0, adds the Slot tag. The compound is named as requested.
- void AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName = "");
-
- /// Writes an item grid into the writer; begins the stored slot numbers with a_BeginSlotNum. Note that it doesn't begin nor end the list tag
- void AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum = 0);
-
- // Block entities:
- void AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID);
- void AddChestEntity (cChestEntity * a_Entity);
- void AddDispenserEntity(cDispenserEntity * a_Entity);
- void AddDropperEntity (cDropperEntity * a_Entity);
- void AddFurnaceEntity (cFurnaceEntity * a_Furnace);
- void AddHopperEntity (cHopperEntity * a_Entity);
- void AddJukeboxEntity (cJukeboxEntity * a_Jukebox);
- void AddNoteEntity (cNoteEntity * a_Note);
- void AddSignEntity (cSignEntity * a_Sign);
-
- // Entities:
- void AddBasicEntity (cEntity * a_Entity, const AString & a_ClassName);
- void AddFallingBlockEntity(cFallingBlock * a_FallingBlock);
- void AddMinecartEntity (cMinecart * a_Minecart);
- void AddMonsterEntity (cMonster * a_Monster);
- void AddPickupEntity (cPickup * a_Pickup);
-
- void AddMinecartChestContents(cMinecartWithChest * a_Minecart);
-
- // cChunkDataSeparateCollector overrides:
- virtual bool LightIsValid(bool a_IsLightValid) override;
- virtual void BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) override;
- virtual void Entity(cEntity * a_Entity) override;
- virtual void BlockEntity(cBlockEntity * a_Entity) override;
-} ; // class cNBTChunkSerializer
-
-
-
-
+
+// NBTChunkSerializer.h
+
+// Declares the cNBTChunkSerializer class that is used for saving individual chunks into NBT format used by Anvil
+
+
+
+
+
+#pragma once
+
+#include "../ChunkDef.h"
+
+
+
+
+
+// fwd:
+class cFastNBTWriter;
+class cEntity;
+class cBlockEntity;
+class cChestEntity;
+class cDispenserEntity;
+class cDropperEntity;
+class cFurnaceEntity;
+class cHopperEntity;
+class cJukeboxEntity;
+class cNoteEntity;
+class cSignEntity;
+class cFallingBlock;
+class cMinecart;
+class cMinecartWithChest;
+class cMinecartWithFurnace;
+class cMonster;
+class cPickup;
+class cItemGrid;
+
+
+
+
+
+class cNBTChunkSerializer :
+ public cChunkDataSeparateCollector
+{
+public:
+ cChunkDef::BiomeMap m_Biomes;
+ unsigned char m_VanillaBiomes[cChunkDef::Width * cChunkDef::Width];
+ bool m_BiomesAreValid;
+
+
+ cNBTChunkSerializer(cFastNBTWriter & a_Writer);
+
+ /// Close NBT tags that we've opened
+ void Finish(void);
+
+ bool IsLightValid(void) const {return m_IsLightValid; }
+
+protected:
+
+ /* From cChunkDataSeparateCollector we inherit:
+ - m_BlockTypes[]
+ - m_BlockMetas[]
+ - m_BlockLight[]
+ - m_BlockSkyLight[]
+ */
+
+ cFastNBTWriter & m_Writer;
+
+ bool m_IsTagOpen; // True if a tag has been opened in the callbacks and not yet closed.
+ bool m_HasHadEntity; // True if any Entity has already been received and processed
+ bool m_HasHadBlockEntity; // True if any BlockEntity has already been received and processed
+ bool m_IsLightValid; // True if the chunk lighting is valid
+
+
+ /// Writes an item into the writer, if slot >= 0, adds the Slot tag. The compound is named as requested.
+ void AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName = "");
+
+ /// Writes an item grid into the writer; begins the stored slot numbers with a_BeginSlotNum. Note that it doesn't begin nor end the list tag
+ void AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum = 0);
+
+ // Block entities:
+ void AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID);
+ void AddChestEntity (cChestEntity * a_Entity);
+ void AddDispenserEntity(cDispenserEntity * a_Entity);
+ void AddDropperEntity (cDropperEntity * a_Entity);
+ void AddFurnaceEntity (cFurnaceEntity * a_Furnace);
+ void AddHopperEntity (cHopperEntity * a_Entity);
+ void AddJukeboxEntity (cJukeboxEntity * a_Jukebox);
+ void AddNoteEntity (cNoteEntity * a_Note);
+ void AddSignEntity (cSignEntity * a_Sign);
+
+ // Entities:
+ void AddBasicEntity (cEntity * a_Entity, const AString & a_ClassName);
+ void AddFallingBlockEntity(cFallingBlock * a_FallingBlock);
+ void AddMinecartEntity (cMinecart * a_Minecart);
+ void AddMonsterEntity (cMonster * a_Monster);
+ void AddPickupEntity (cPickup * a_Pickup);
+
+ void AddMinecartChestContents(cMinecartWithChest * a_Minecart);
+
+ // cChunkDataSeparateCollector overrides:
+ virtual bool LightIsValid(bool a_IsLightValid) override;
+ virtual void BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) override;
+ virtual void Entity(cEntity * a_Entity) override;
+ virtual void BlockEntity(cBlockEntity * a_Entity) override;
+} ; // class cNBTChunkSerializer
+
+
+
+
diff --git a/source/XMLParser.h b/source/XMLParser.h
index a6b571862..f492d1a5d 100644
--- a/source/XMLParser.h
+++ b/source/XMLParser.h
@@ -1,701 +1,701 @@
-
-// XMLParser.h
-
-// Interfaces to the CXMLParser class representing the base class for XML parsing
-
-// To use, derive a class from this base and override its OnStartElement(), OnEndElement() and OnCharacters() functions
-
-
-
-
-
-#pragma once
-
-#include "expat/expat.h"
-
-
-
-
-
-class CXMLParser
-{
-public:
- CXMLParser(void);
- virtual ~CXMLParser();
-
- // The actual parsing, may be called several times; the last time needs iIsFinal == true (-> flush)
- int Parse(const char * iData, size_t iLength, bool iIsFinal = false);
-
-private:
- // LibExpat stuff:
- XML_Parser mParser;
-
- static void StartElementHandler(void * iContext, const XML_Char * iElement, const XML_Char ** iAttributes)
- {
- ((CXMLParser *)iContext)->OnStartElement(iElement, iAttributes);
- }
-
- static void EndElementHandler (void * iContext, const XML_Char * iElement)
- {
- ((CXMLParser *)iContext)->OnEndElement(iElement);
- }
-
- static void CharacterDataHandler (void * iContext, const XML_Char * iData, int iLength)
- {
- ((CXMLParser *)iContext)->OnCharacters(iData, iLength);
- }
-
-protected:
- virtual void OnStartElement(const XML_Char * iElement, const XML_Char ** iAttributes) = 0;
- virtual void OnEndElement (const XML_Char * iElement) = 0;
- virtual void OnCharacters (const XML_Char * iCharacters, int iLength) = 0;
-} ;
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// The following template has been modified from code available at
-// http://www.codeproject.com/Articles/1847/C-Wrappers-for-the-Expat-XML-Parser
-// It uses templates to remove the virtual function call penalty (both size and speed) for each callback
-
-/* Usage:
-1, Declare a subclass:
- class CMyParser : public CExpatImpl<CMyParser>
-2, Declare handlers that you want in that subclass:
- void CMyParser::OnEndElement(const XML_Char * iTagName);
-3, Create an instance of your class:
- CMyParser Parser;
-4, Call Create():
- Parser.Create(NULL, NULL);
-4, Call Parse(), repeatedly:
- Parser.Parse(Buffer, Length);
-*/
-
-template <class _T>
-class CExpatImpl
-{
-
-// @access Constructors and destructors
-public:
-
- // @cmember General constructor
-
- CExpatImpl ()
- {
- m_p = NULL;
- }
-
- // @cmember Destructor
-
- ~CExpatImpl ()
- {
- Destroy ();
- }
-
-// @access Parser creation and deletion methods
-public:
-
- // @cmember Create a parser
-
- bool Create (const XML_Char * pszEncoding = NULL, const XML_Char * pszSep = NULL)
- {
- // Destroy the old parser
- Destroy ();
-
- // If the encoding or seperator are empty, then NULL
- if (pszEncoding != NULL && pszEncoding [0] == 0)
- {
- pszEncoding = NULL;
- }
- if (pszSep != NULL && pszSep [0] == 0)
- {
- pszSep = NULL;
- }
-
- // Create the new parser
- m_p = XML_ParserCreate_MM (pszEncoding, NULL, pszSep);
- if (m_p == NULL)
- {
- return false;
- }
-
- // Invoke the post create routine
- _T * pThis = static_cast <_T *> (this);
- pThis ->OnPostCreate ();
-
- // Set the user data used in callbacks
- XML_SetUserData (m_p, (void *) this);
- return true;
- }
-
- // @cmember Destroy the parser
-
- void Destroy (void)
- {
- if (m_p != NULL)
- {
- XML_ParserFree (m_p);
- }
- m_p = NULL;
- }
-
-
- // @cmember Parse a block of data
-
- bool Parse (const char *pszBuffer, int nLength, bool fIsFinal = true)
- {
- assert (m_p != NULL);
- return XML_Parse (m_p, pszBuffer, nLength, fIsFinal) != 0;
- }
-
- // @cmember Parse internal buffer
-
- bool ParseBuffer (int nLength, bool fIsFinal = true)
- {
- assert (m_p != NULL);
- return XML_ParseBuffer (m_p, nLength, fIsFinal) != 0;
- }
-
- // @cmember Get the internal buffer
-
- void *GetBuffer (int nLength)
- {
- assert (m_p != NULL);
- return XML_GetBuffer (m_p, nLength);
- }
-
-
-protected:
- // Parser callback enable/disable methods:
-
- // @cmember Enable/Disable the start element handler
-
- void EnableStartElementHandler (bool fEnable = true)
- {
- assert (m_p != NULL);
- XML_SetStartElementHandler (m_p, fEnable ? StartElementHandler : NULL);
- }
-
- // @cmember Enable/Disable the end element handler
-
- void EnableEndElementHandler (bool fEnable = true)
- {
- assert (m_p != NULL);
- XML_SetEndElementHandler (m_p, fEnable ? EndElementHandler : NULL);
- }
-
- // @cmember Enable/Disable the element handlers
-
- void EnableElementHandler (bool fEnable = true)
- {
- assert (m_p != NULL);
- EnableStartElementHandler (fEnable);
- EnableEndElementHandler (fEnable);
- }
-
- // @cmember Enable/Disable the character data handler
-
- void EnableCharacterDataHandler (bool fEnable = true)
- {
- assert (m_p != NULL);
- XML_SetCharacterDataHandler (m_p, fEnable ? CharacterDataHandler : NULL);
- }
-
- // @cmember Enable/Disable the processing instruction handler
-
- void EnableProcessingInstructionHandler (bool fEnable = true)
- {
- assert (m_p != NULL);
- XML_SetProcessingInstructionHandler (m_p, fEnable ? ProcessingInstructionHandler : NULL);
- }
-
- // @cmember Enable/Disable the comment handler
-
- void EnableCommentHandler (bool fEnable = true)
- {
- assert (m_p != NULL);
- XML_SetCommentHandler (m_p, fEnable ? CommentHandler : NULL);
- }
-
- // @cmember Enable/Disable the start CDATA section handler
-
- void EnableStartCdataSectionHandler (bool fEnable = true)
- {
- assert (m_p != NULL);
- XML_SetStartCdataSectionHandler (m_p, fEnable ? StartCdataSectionHandler : NULL);
- }
-
- // @cmember Enable/Disable the end CDATA section handler
-
- void EnableEndCdataSectionHandler (bool fEnable = true)
- {
- assert (m_p != NULL);
- XML_SetEndCdataSectionHandler (m_p, fEnable ? EndCdataSectionHandler : NULL);
- }
-
- // @cmember Enable/Disable the CDATA section handlers
-
- void EnableCdataSectionHandler (bool fEnable = true)
- {
- assert (m_p != NULL);
- EnableStartCdataSectionHandler (fEnable);
- EnableEndCdataSectionHandler (fEnable);
- }
-
- // @cmember Enable/Disable default handler
-
- void EnableDefaultHandler (bool fEnable = true, bool fExpand = true)
- {
- assert (m_p != NULL);
- if (fExpand)
- {
- XML_SetDefaultHandlerExpand (m_p, fEnable ? DefaultHandler : NULL);
- }
- else
- XML_SetDefaultHandler (m_p, fEnable ? DefaultHandler : NULL);
- }
-
- // @cmember Enable/Disable external entity ref handler
-
- void EnableExternalEntityRefHandler (bool fEnable = true)
- {
- assert (m_p != NULL);
- XML_SetExternalEntityRefHandler (m_p, fEnable ? ExternalEntityRefHandler : NULL);
- }
-
- // @cmember Enable/Disable unknown encoding handler
-
- void EnableUnknownEncodingHandler (bool fEnable = true)
- {
- assert (m_p != NULL);
- XML_SetUnknownEncodingHandler (m_p, fEnable ? UnknownEncodingHandler : NULL);
- }
-
- // @cmember Enable/Disable start namespace handler
-
- void EnableStartNamespaceDeclHandler (bool fEnable = true)
- {
- assert (m_p != NULL);
- XML_SetStartNamespaceDeclHandler (m_p, fEnable ? StartNamespaceDeclHandler : NULL);
- }
-
- // @cmember Enable/Disable end namespace handler
-
- void EnableEndNamespaceDeclHandler (bool fEnable = true)
- {
- assert (m_p != NULL);
- XML_SetEndNamespaceDeclHandler (m_p, fEnable ? EndNamespaceDeclHandler : NULL);
- }
-
- // @cmember Enable/Disable namespace handlers
-
- void EnableNamespaceDeclHandler (bool fEnable = true)
- {
- EnableStartNamespaceDeclHandler (fEnable);
- EnableEndNamespaceDeclHandler (fEnable);
- }
-
- // @cmember Enable/Disable the XML declaration handler
-
- void EnableXmlDeclHandler (bool fEnable = true)
- {
- assert (m_p != NULL);
- XML_SetXmlDeclHandler (m_p, fEnable ? XmlDeclHandler : NULL);
- }
-
- // @cmember Enable/Disable the start DOCTYPE declaration handler
-
- void EnableStartDoctypeDeclHandler (bool fEnable = true)
- {
- assert (m_p != NULL);
- XML_SetStartDoctypeDeclHandler (m_p, fEnable ? StartDoctypeDeclHandler : NULL);
- }
-
- // @cmember Enable/Disable the end DOCTYPE declaration handler
-
- void EnableEndDoctypeDeclHandler (bool fEnable = true)
- {
- assert (m_p != NULL);
- XML_SetEndDoctypeDeclHandler (m_p,
- fEnable ? EndDoctypeDeclHandler : NULL);
- }
-
- // @cmember Enable/Disable the DOCTYPE declaration handler
-
- void EnableDoctypeDeclHandler (bool fEnable = true)
- {
- assert (m_p != NULL);
- EnableStartDoctypeDeclHandler (fEnable);
- EnableEndDoctypeDeclHandler (fEnable);
- }
-
-public:
- // Parser error reporting methods
-
- // @cmember Get last error
-
- enum XML_Error GetErrorCode ()
- {
- assert (m_p != NULL);
- return XML_GetErrorCode (m_p);
- }
-
- // @cmember Get the current byte index
-
- long GetCurrentByteIndex ()
- {
- assert (m_p != NULL);
- return XML_GetCurrentByteIndex (m_p);
- }
-
- // @cmember Get the current line number
-
- int GetCurrentLineNumber ()
- {
- assert (m_p != NULL);
- return XML_GetCurrentLineNumber (m_p);
- }
-
- // @cmember Get the current column number
-
- int GetCurrentColumnNumber ()
- {
- assert (m_p != NULL);
- return XML_GetCurrentColumnNumber (m_p);
- }
-
- // @cmember Get the current byte count
-
- int GetCurrentByteCount ()
- {
- assert (m_p != NULL);
- return XML_GetCurrentByteCount (m_p);
- }
-
- // @cmember Get the input context
-
- const char *GetInputContext (int *pnOffset, int *pnSize)
- {
- assert (m_p != NULL);
- return XML_GetInputContext (m_p, pnOffset, pnSize);
- }
-
- // @cmember Get last error string
-
- const XML_LChar *GetErrorString ()
- {
- return XML_ErrorString (GetErrorCode ());
- }
-
- // @cmember Return the version string
-
- static const XML_LChar *GetExpatVersion ()
- {
- return XML_ExpatVersion ();
- }
-
- // @cmember Get the version information
-
- static void GetExpatVersion (int *pnMajor, int *pnMinor, int *pnMicro)
- {
- XML_expat_version v = XML_ExpatVersionInfo ();
- if (pnMajor)
- *pnMajor = v .major;
- if (pnMinor)
- *pnMinor = v .minor;
- if (pnMicro)
- *pnMicro = v .micro;
- }
-
- // @cmember Get last error string
-
- static const XML_LChar *GetErrorString (enum XML_Error nError)
- {
- return XML_ErrorString (nError);
- }
-
-
- // Public handler methods:
- // The template parameter should provide their own implementation for those handlers that they want
-
- // @cmember Start element handler
-
- void OnStartElement (const XML_Char *pszName, const XML_Char **papszAttrs)
- {
- return;
- }
-
- // @cmember End element handler
-
- void OnEndElement (const XML_Char *pszName)
- {
- return;
- }
-
- // @cmember Character data handler
-
- void OnCharacterData (const XML_Char *pszData, int nLength)
- {
- return;
- }
-
- // @cmember Processing instruction handler
-
- void OnProcessingInstruction (const XML_Char *pszTarget,
- const XML_Char *pszData)
- {
- return;
- }
-
- // @cmember Comment handler
-
- void OnComment (const XML_Char *pszData)
- {
- return;
- }
-
- // @cmember Start CDATA section handler
-
- void OnStartCdataSection ()
- {
- return;
- }
-
- // @cmember End CDATA section handler
-
- void OnEndCdataSection ()
- {
- return;
- }
-
- // @cmember Default handler
-
- void OnDefault (const XML_Char *pszData, int nLength)
- {
- return;
- }
-
- // @cmember External entity ref handler
-
- bool OnExternalEntityRef (const XML_Char *pszContext,
- const XML_Char *pszBase, const XML_Char *pszSystemID,
- const XML_Char *pszPublicID)
- {
- return false;
- }
-
- // @cmember Unknown encoding handler
-
- bool OnUnknownEncoding (const XML_Char *pszName, XML_Encoding *pInfo)
- {
- return false;
- }
-
- // @cmember Start namespace declaration handler
-
- void OnStartNamespaceDecl (const XML_Char *pszPrefix,
- const XML_Char *pszURI)
- {
- return;
- }
-
- // @cmember End namespace declaration handler
-
- void OnEndNamespaceDecl (const XML_Char *pszPrefix)
- {
- return;
- }
-
- // @cmember XML declaration handler
-
- void OnXmlDecl (const XML_Char *pszVersion, const XML_Char *pszEncoding,
- bool fStandalone)
- {
- return;
- }
-
- // @cmember Start DOCTYPE declaration handler
-
- void OnStartDoctypeDecl (const XML_Char *pszDoctypeName,
- const XML_Char *pszSysID, const XML_Char *pszPubID,
- bool fHasInternalSubset)
- {
- return;
- }
-
- // @cmember End DOCTYPE declaration handler
-
- void OnEndDoctypeDecl ()
- {
- return;
- }
-
-// @access Protected methods
-protected:
-
- // @cmember Handle any post creation
-
- void OnPostCreate ()
- {
- }
-
-// @access Protected static methods
-protected:
-
- // @cmember Start element handler wrapper
-
- static void __cdecl StartElementHandler (void *pUserData,
- const XML_Char *pszName, const XML_Char **papszAttrs)
- {
- _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
- pThis ->OnStartElement (pszName, papszAttrs);
- }
-
- // @cmember End element handler wrapper
-
- static void __cdecl EndElementHandler (void *pUserData,
- const XML_Char *pszName)
- {
- _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
- pThis ->OnEndElement (pszName);
- }
-
- // @cmember Character data handler wrapper
-
- static void __cdecl CharacterDataHandler (void *pUserData,
- const XML_Char *pszData, int nLength)
- {
- _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
- pThis ->OnCharacterData (pszData, nLength);
- }
-
- // @cmember Processing instruction handler wrapper
-
- static void __cdecl ProcessingInstructionHandler (void *pUserData,
- const XML_Char *pszTarget, const XML_Char *pszData)
- {
- _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
- pThis ->OnProcessingInstruction (pszTarget, pszData);
- }
-
- // @cmember Comment handler wrapper
-
- static void __cdecl CommentHandler (void *pUserData,
- const XML_Char *pszData)
- {
- _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
- pThis ->OnComment (pszData);
- }
-
- // @cmember Start CDATA section wrapper
-
- static void __cdecl StartCdataSectionHandler (void *pUserData)
- {
- _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
- pThis ->OnStartCdataSection ();
- }
-
- // @cmember End CDATA section wrapper
-
- static void __cdecl EndCdataSectionHandler (void *pUserData)
- {
- _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
- pThis ->OnEndCdataSection ();
- }
-
- // @cmember Default wrapper
-
- static void __cdecl DefaultHandler (void *pUserData,
- const XML_Char *pszData, int nLength)
- {
- _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
- pThis ->OnDefault (pszData, nLength);
- }
-
- // @cmember External entity ref wrapper
-
- static int __cdecl ExternalEntityRefHandler (void *pUserData,
- const XML_Char *pszContext, const XML_Char *pszBase,
- const XML_Char *pszSystemID, const XML_Char *pszPublicID)
- {
- _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
- return pThis ->OnExternalEntityRef (pszContext,
- pszBase, pszSystemID, pszPublicID) ? 1 : 0;
- }
-
- // @cmember Unknown encoding wrapper
-
- static int __cdecl UnknownEncodingHandler (void * pUserData, const XML_Char * pszName, XML_Encoding * pInfo)
- {
- _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
- return pThis ->OnUnknownEncoding (pszName, pInfo) ? 1 : 0;
- }
-
- // @cmember Start namespace decl wrapper
-
- static void __cdecl StartNamespaceDeclHandler (void * pUserData, const XML_Char * pszPrefix, const XML_Char * pszURI)
- {
- _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
- pThis ->OnStartNamespaceDecl (pszPrefix, pszURI);
- }
-
- // @cmember End namespace decl wrapper
-
- static void __cdecl EndNamespaceDeclHandler (void * pUserData, const XML_Char * pszPrefix)
- {
- _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
- pThis ->OnEndNamespaceDecl (pszPrefix);
- }
-
- // @cmember XML declaration wrapper
-
- static void __cdecl XmlDeclHandler (void *pUserData, const XML_Char *pszVersion, const XML_Char *pszEncoding, int nStandalone)
- {
- _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
- pThis ->OnXmlDecl (pszVersion, pszEncoding, nStandalone != 0);
- }
-
- // @cmember Start Doctype declaration wrapper
-
- static void __cdecl StartDoctypeDeclHandler (
- void *pUserData, const XML_Char *pszDoctypeName, const XML_Char *pszSysID,
- const XML_Char *pszPubID, int nHasInternalSubset
- )
- {
- _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
- pThis ->OnStartDoctypeDecl (pszDoctypeName, pszSysID,
- pszPubID, nHasInternalSubset != 0);
- }
-
- // @cmember End Doctype declaration wrapper
-
- static void __cdecl EndDoctypeDeclHandler (void *pUserData)
- {
- _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
- pThis ->OnEndDoctypeDecl ();
- }
-
-
-protected:
-
- XML_Parser m_p;
-
- /// Returns the value of the specified attribute, if found; NULL otherwise
- static const XML_Char * FindAttr(const XML_Char ** iAttrs, const XML_Char * iAttrToFind)
- {
- for (const XML_Char ** Attr = iAttrs; *Attr != NULL; Attr += 2)
- {
- if (strcmp(*Attr, iAttrToFind) == 0)
- {
- return *(Attr + 1);
- }
- } // for Attr - iAttrs[]
- return NULL;
- }
-} ;
-
-
-
-
+
+// XMLParser.h
+
+// Interfaces to the CXMLParser class representing the base class for XML parsing
+
+// To use, derive a class from this base and override its OnStartElement(), OnEndElement() and OnCharacters() functions
+
+
+
+
+
+#pragma once
+
+#include "expat/expat.h"
+
+
+
+
+
+class CXMLParser
+{
+public:
+ CXMLParser(void);
+ virtual ~CXMLParser();
+
+ // The actual parsing, may be called several times; the last time needs iIsFinal == true (-> flush)
+ int Parse(const char * iData, size_t iLength, bool iIsFinal = false);
+
+private:
+ // LibExpat stuff:
+ XML_Parser mParser;
+
+ static void StartElementHandler(void * iContext, const XML_Char * iElement, const XML_Char ** iAttributes)
+ {
+ ((CXMLParser *)iContext)->OnStartElement(iElement, iAttributes);
+ }
+
+ static void EndElementHandler (void * iContext, const XML_Char * iElement)
+ {
+ ((CXMLParser *)iContext)->OnEndElement(iElement);
+ }
+
+ static void CharacterDataHandler (void * iContext, const XML_Char * iData, int iLength)
+ {
+ ((CXMLParser *)iContext)->OnCharacters(iData, iLength);
+ }
+
+protected:
+ virtual void OnStartElement(const XML_Char * iElement, const XML_Char ** iAttributes) = 0;
+ virtual void OnEndElement (const XML_Char * iElement) = 0;
+ virtual void OnCharacters (const XML_Char * iCharacters, int iLength) = 0;
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// The following template has been modified from code available at
+// http://www.codeproject.com/Articles/1847/C-Wrappers-for-the-Expat-XML-Parser
+// It uses templates to remove the virtual function call penalty (both size and speed) for each callback
+
+/* Usage:
+1, Declare a subclass:
+ class CMyParser : public CExpatImpl<CMyParser>
+2, Declare handlers that you want in that subclass:
+ void CMyParser::OnEndElement(const XML_Char * iTagName);
+3, Create an instance of your class:
+ CMyParser Parser;
+4, Call Create():
+ Parser.Create(NULL, NULL);
+4, Call Parse(), repeatedly:
+ Parser.Parse(Buffer, Length);
+*/
+
+template <class _T>
+class CExpatImpl
+{
+
+// @access Constructors and destructors
+public:
+
+ // @cmember General constructor
+
+ CExpatImpl ()
+ {
+ m_p = NULL;
+ }
+
+ // @cmember Destructor
+
+ ~CExpatImpl ()
+ {
+ Destroy ();
+ }
+
+// @access Parser creation and deletion methods
+public:
+
+ // @cmember Create a parser
+
+ bool Create (const XML_Char * pszEncoding = NULL, const XML_Char * pszSep = NULL)
+ {
+ // Destroy the old parser
+ Destroy ();
+
+ // If the encoding or seperator are empty, then NULL
+ if (pszEncoding != NULL && pszEncoding [0] == 0)
+ {
+ pszEncoding = NULL;
+ }
+ if (pszSep != NULL && pszSep [0] == 0)
+ {
+ pszSep = NULL;
+ }
+
+ // Create the new parser
+ m_p = XML_ParserCreate_MM (pszEncoding, NULL, pszSep);
+ if (m_p == NULL)
+ {
+ return false;
+ }
+
+ // Invoke the post create routine
+ _T * pThis = static_cast <_T *> (this);
+ pThis ->OnPostCreate ();
+
+ // Set the user data used in callbacks
+ XML_SetUserData (m_p, (void *) this);
+ return true;
+ }
+
+ // @cmember Destroy the parser
+
+ void Destroy (void)
+ {
+ if (m_p != NULL)
+ {
+ XML_ParserFree (m_p);
+ }
+ m_p = NULL;
+ }
+
+
+ // @cmember Parse a block of data
+
+ bool Parse (const char *pszBuffer, int nLength, bool fIsFinal = true)
+ {
+ assert (m_p != NULL);
+ return XML_Parse (m_p, pszBuffer, nLength, fIsFinal) != 0;
+ }
+
+ // @cmember Parse internal buffer
+
+ bool ParseBuffer (int nLength, bool fIsFinal = true)
+ {
+ assert (m_p != NULL);
+ return XML_ParseBuffer (m_p, nLength, fIsFinal) != 0;
+ }
+
+ // @cmember Get the internal buffer
+
+ void *GetBuffer (int nLength)
+ {
+ assert (m_p != NULL);
+ return XML_GetBuffer (m_p, nLength);
+ }
+
+
+protected:
+ // Parser callback enable/disable methods:
+
+ // @cmember Enable/Disable the start element handler
+
+ void EnableStartElementHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetStartElementHandler (m_p, fEnable ? StartElementHandler : NULL);
+ }
+
+ // @cmember Enable/Disable the end element handler
+
+ void EnableEndElementHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetEndElementHandler (m_p, fEnable ? EndElementHandler : NULL);
+ }
+
+ // @cmember Enable/Disable the element handlers
+
+ void EnableElementHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ EnableStartElementHandler (fEnable);
+ EnableEndElementHandler (fEnable);
+ }
+
+ // @cmember Enable/Disable the character data handler
+
+ void EnableCharacterDataHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetCharacterDataHandler (m_p, fEnable ? CharacterDataHandler : NULL);
+ }
+
+ // @cmember Enable/Disable the processing instruction handler
+
+ void EnableProcessingInstructionHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetProcessingInstructionHandler (m_p, fEnable ? ProcessingInstructionHandler : NULL);
+ }
+
+ // @cmember Enable/Disable the comment handler
+
+ void EnableCommentHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetCommentHandler (m_p, fEnable ? CommentHandler : NULL);
+ }
+
+ // @cmember Enable/Disable the start CDATA section handler
+
+ void EnableStartCdataSectionHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetStartCdataSectionHandler (m_p, fEnable ? StartCdataSectionHandler : NULL);
+ }
+
+ // @cmember Enable/Disable the end CDATA section handler
+
+ void EnableEndCdataSectionHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetEndCdataSectionHandler (m_p, fEnable ? EndCdataSectionHandler : NULL);
+ }
+
+ // @cmember Enable/Disable the CDATA section handlers
+
+ void EnableCdataSectionHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ EnableStartCdataSectionHandler (fEnable);
+ EnableEndCdataSectionHandler (fEnable);
+ }
+
+ // @cmember Enable/Disable default handler
+
+ void EnableDefaultHandler (bool fEnable = true, bool fExpand = true)
+ {
+ assert (m_p != NULL);
+ if (fExpand)
+ {
+ XML_SetDefaultHandlerExpand (m_p, fEnable ? DefaultHandler : NULL);
+ }
+ else
+ XML_SetDefaultHandler (m_p, fEnable ? DefaultHandler : NULL);
+ }
+
+ // @cmember Enable/Disable external entity ref handler
+
+ void EnableExternalEntityRefHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetExternalEntityRefHandler (m_p, fEnable ? ExternalEntityRefHandler : NULL);
+ }
+
+ // @cmember Enable/Disable unknown encoding handler
+
+ void EnableUnknownEncodingHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetUnknownEncodingHandler (m_p, fEnable ? UnknownEncodingHandler : NULL);
+ }
+
+ // @cmember Enable/Disable start namespace handler
+
+ void EnableStartNamespaceDeclHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetStartNamespaceDeclHandler (m_p, fEnable ? StartNamespaceDeclHandler : NULL);
+ }
+
+ // @cmember Enable/Disable end namespace handler
+
+ void EnableEndNamespaceDeclHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetEndNamespaceDeclHandler (m_p, fEnable ? EndNamespaceDeclHandler : NULL);
+ }
+
+ // @cmember Enable/Disable namespace handlers
+
+ void EnableNamespaceDeclHandler (bool fEnable = true)
+ {
+ EnableStartNamespaceDeclHandler (fEnable);
+ EnableEndNamespaceDeclHandler (fEnable);
+ }
+
+ // @cmember Enable/Disable the XML declaration handler
+
+ void EnableXmlDeclHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetXmlDeclHandler (m_p, fEnable ? XmlDeclHandler : NULL);
+ }
+
+ // @cmember Enable/Disable the start DOCTYPE declaration handler
+
+ void EnableStartDoctypeDeclHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetStartDoctypeDeclHandler (m_p, fEnable ? StartDoctypeDeclHandler : NULL);
+ }
+
+ // @cmember Enable/Disable the end DOCTYPE declaration handler
+
+ void EnableEndDoctypeDeclHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ XML_SetEndDoctypeDeclHandler (m_p,
+ fEnable ? EndDoctypeDeclHandler : NULL);
+ }
+
+ // @cmember Enable/Disable the DOCTYPE declaration handler
+
+ void EnableDoctypeDeclHandler (bool fEnable = true)
+ {
+ assert (m_p != NULL);
+ EnableStartDoctypeDeclHandler (fEnable);
+ EnableEndDoctypeDeclHandler (fEnable);
+ }
+
+public:
+ // Parser error reporting methods
+
+ // @cmember Get last error
+
+ enum XML_Error GetErrorCode ()
+ {
+ assert (m_p != NULL);
+ return XML_GetErrorCode (m_p);
+ }
+
+ // @cmember Get the current byte index
+
+ long GetCurrentByteIndex ()
+ {
+ assert (m_p != NULL);
+ return XML_GetCurrentByteIndex (m_p);
+ }
+
+ // @cmember Get the current line number
+
+ int GetCurrentLineNumber ()
+ {
+ assert (m_p != NULL);
+ return XML_GetCurrentLineNumber (m_p);
+ }
+
+ // @cmember Get the current column number
+
+ int GetCurrentColumnNumber ()
+ {
+ assert (m_p != NULL);
+ return XML_GetCurrentColumnNumber (m_p);
+ }
+
+ // @cmember Get the current byte count
+
+ int GetCurrentByteCount ()
+ {
+ assert (m_p != NULL);
+ return XML_GetCurrentByteCount (m_p);
+ }
+
+ // @cmember Get the input context
+
+ const char *GetInputContext (int *pnOffset, int *pnSize)
+ {
+ assert (m_p != NULL);
+ return XML_GetInputContext (m_p, pnOffset, pnSize);
+ }
+
+ // @cmember Get last error string
+
+ const XML_LChar *GetErrorString ()
+ {
+ return XML_ErrorString (GetErrorCode ());
+ }
+
+ // @cmember Return the version string
+
+ static const XML_LChar *GetExpatVersion ()
+ {
+ return XML_ExpatVersion ();
+ }
+
+ // @cmember Get the version information
+
+ static void GetExpatVersion (int *pnMajor, int *pnMinor, int *pnMicro)
+ {
+ XML_expat_version v = XML_ExpatVersionInfo ();
+ if (pnMajor)
+ *pnMajor = v .major;
+ if (pnMinor)
+ *pnMinor = v .minor;
+ if (pnMicro)
+ *pnMicro = v .micro;
+ }
+
+ // @cmember Get last error string
+
+ static const XML_LChar *GetErrorString (enum XML_Error nError)
+ {
+ return XML_ErrorString (nError);
+ }
+
+
+ // Public handler methods:
+ // The template parameter should provide their own implementation for those handlers that they want
+
+ // @cmember Start element handler
+
+ void OnStartElement (const XML_Char *pszName, const XML_Char **papszAttrs)
+ {
+ return;
+ }
+
+ // @cmember End element handler
+
+ void OnEndElement (const XML_Char *pszName)
+ {
+ return;
+ }
+
+ // @cmember Character data handler
+
+ void OnCharacterData (const XML_Char *pszData, int nLength)
+ {
+ return;
+ }
+
+ // @cmember Processing instruction handler
+
+ void OnProcessingInstruction (const XML_Char *pszTarget,
+ const XML_Char *pszData)
+ {
+ return;
+ }
+
+ // @cmember Comment handler
+
+ void OnComment (const XML_Char *pszData)
+ {
+ return;
+ }
+
+ // @cmember Start CDATA section handler
+
+ void OnStartCdataSection ()
+ {
+ return;
+ }
+
+ // @cmember End CDATA section handler
+
+ void OnEndCdataSection ()
+ {
+ return;
+ }
+
+ // @cmember Default handler
+
+ void OnDefault (const XML_Char *pszData, int nLength)
+ {
+ return;
+ }
+
+ // @cmember External entity ref handler
+
+ bool OnExternalEntityRef (const XML_Char *pszContext,
+ const XML_Char *pszBase, const XML_Char *pszSystemID,
+ const XML_Char *pszPublicID)
+ {
+ return false;
+ }
+
+ // @cmember Unknown encoding handler
+
+ bool OnUnknownEncoding (const XML_Char *pszName, XML_Encoding *pInfo)
+ {
+ return false;
+ }
+
+ // @cmember Start namespace declaration handler
+
+ void OnStartNamespaceDecl (const XML_Char *pszPrefix,
+ const XML_Char *pszURI)
+ {
+ return;
+ }
+
+ // @cmember End namespace declaration handler
+
+ void OnEndNamespaceDecl (const XML_Char *pszPrefix)
+ {
+ return;
+ }
+
+ // @cmember XML declaration handler
+
+ void OnXmlDecl (const XML_Char *pszVersion, const XML_Char *pszEncoding,
+ bool fStandalone)
+ {
+ return;
+ }
+
+ // @cmember Start DOCTYPE declaration handler
+
+ void OnStartDoctypeDecl (const XML_Char *pszDoctypeName,
+ const XML_Char *pszSysID, const XML_Char *pszPubID,
+ bool fHasInternalSubset)
+ {
+ return;
+ }
+
+ // @cmember End DOCTYPE declaration handler
+
+ void OnEndDoctypeDecl ()
+ {
+ return;
+ }
+
+// @access Protected methods
+protected:
+
+ // @cmember Handle any post creation
+
+ void OnPostCreate ()
+ {
+ }
+
+// @access Protected static methods
+protected:
+
+ // @cmember Start element handler wrapper
+
+ static void __cdecl StartElementHandler (void *pUserData,
+ const XML_Char *pszName, const XML_Char **papszAttrs)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnStartElement (pszName, papszAttrs);
+ }
+
+ // @cmember End element handler wrapper
+
+ static void __cdecl EndElementHandler (void *pUserData,
+ const XML_Char *pszName)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnEndElement (pszName);
+ }
+
+ // @cmember Character data handler wrapper
+
+ static void __cdecl CharacterDataHandler (void *pUserData,
+ const XML_Char *pszData, int nLength)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnCharacterData (pszData, nLength);
+ }
+
+ // @cmember Processing instruction handler wrapper
+
+ static void __cdecl ProcessingInstructionHandler (void *pUserData,
+ const XML_Char *pszTarget, const XML_Char *pszData)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnProcessingInstruction (pszTarget, pszData);
+ }
+
+ // @cmember Comment handler wrapper
+
+ static void __cdecl CommentHandler (void *pUserData,
+ const XML_Char *pszData)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnComment (pszData);
+ }
+
+ // @cmember Start CDATA section wrapper
+
+ static void __cdecl StartCdataSectionHandler (void *pUserData)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnStartCdataSection ();
+ }
+
+ // @cmember End CDATA section wrapper
+
+ static void __cdecl EndCdataSectionHandler (void *pUserData)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnEndCdataSection ();
+ }
+
+ // @cmember Default wrapper
+
+ static void __cdecl DefaultHandler (void *pUserData,
+ const XML_Char *pszData, int nLength)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnDefault (pszData, nLength);
+ }
+
+ // @cmember External entity ref wrapper
+
+ static int __cdecl ExternalEntityRefHandler (void *pUserData,
+ const XML_Char *pszContext, const XML_Char *pszBase,
+ const XML_Char *pszSystemID, const XML_Char *pszPublicID)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ return pThis ->OnExternalEntityRef (pszContext,
+ pszBase, pszSystemID, pszPublicID) ? 1 : 0;
+ }
+
+ // @cmember Unknown encoding wrapper
+
+ static int __cdecl UnknownEncodingHandler (void * pUserData, const XML_Char * pszName, XML_Encoding * pInfo)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ return pThis ->OnUnknownEncoding (pszName, pInfo) ? 1 : 0;
+ }
+
+ // @cmember Start namespace decl wrapper
+
+ static void __cdecl StartNamespaceDeclHandler (void * pUserData, const XML_Char * pszPrefix, const XML_Char * pszURI)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnStartNamespaceDecl (pszPrefix, pszURI);
+ }
+
+ // @cmember End namespace decl wrapper
+
+ static void __cdecl EndNamespaceDeclHandler (void * pUserData, const XML_Char * pszPrefix)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnEndNamespaceDecl (pszPrefix);
+ }
+
+ // @cmember XML declaration wrapper
+
+ static void __cdecl XmlDeclHandler (void *pUserData, const XML_Char *pszVersion, const XML_Char *pszEncoding, int nStandalone)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnXmlDecl (pszVersion, pszEncoding, nStandalone != 0);
+ }
+
+ // @cmember Start Doctype declaration wrapper
+
+ static void __cdecl StartDoctypeDeclHandler (
+ void *pUserData, const XML_Char *pszDoctypeName, const XML_Char *pszSysID,
+ const XML_Char *pszPubID, int nHasInternalSubset
+ )
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnStartDoctypeDecl (pszDoctypeName, pszSysID,
+ pszPubID, nHasInternalSubset != 0);
+ }
+
+ // @cmember End Doctype declaration wrapper
+
+ static void __cdecl EndDoctypeDeclHandler (void *pUserData)
+ {
+ _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData);
+ pThis ->OnEndDoctypeDecl ();
+ }
+
+
+protected:
+
+ XML_Parser m_p;
+
+ /// Returns the value of the specified attribute, if found; NULL otherwise
+ static const XML_Char * FindAttr(const XML_Char ** iAttrs, const XML_Char * iAttrToFind)
+ {
+ for (const XML_Char ** Attr = iAttrs; *Attr != NULL; Attr += 2)
+ {
+ if (strcmp(*Attr, iAttrToFind) == 0)
+ {
+ return *(Attr + 1);
+ }
+ } // for Attr - iAttrs[]
+ return NULL;
+ }
+} ;
+
+
+
+
diff --git a/source/squirrelbindings/SquirrelArray.h b/source/squirrelbindings/SquirrelArray.h
index c54315aae..de5027f86 100644
--- a/source/squirrelbindings/SquirrelArray.h
+++ b/source/squirrelbindings/SquirrelArray.h
@@ -1,56 +1,56 @@
-
-#pragma once
-
-
-
-
-
-#ifdef USE_SQUIRREL
-
-
-
-
-
-template <typename T>
-class SquirrelArray
-{
-public:
- SquirrelArray()
- {
- }
-
- unsigned int Size()
- {
- return m_Values.size();
- }
-
- T Get(unsigned int a_Index)
- {
- if(m_Values.size() < a_Index)
- {
- return T();
- }
- return m_Values.at(a_Index);
- }
-
- void Add(T a_Value)
- {
- m_Values.push_back(a_Value);
- }
-
-protected:
- std::vector<T> m_Values;
-
-};
-
-class SquirrelStringArray : public SquirrelArray<std::string> { };
-
-
-
-
-
-#endif // USE_SQUIRREL
-
-
-
-
+
+#pragma once
+
+
+
+
+
+#ifdef USE_SQUIRREL
+
+
+
+
+
+template <typename T>
+class SquirrelArray
+{
+public:
+ SquirrelArray()
+ {
+ }
+
+ unsigned int Size()
+ {
+ return m_Values.size();
+ }
+
+ T Get(unsigned int a_Index)
+ {
+ if(m_Values.size() < a_Index)
+ {
+ return T();
+ }
+ return m_Values.at(a_Index);
+ }
+
+ void Add(T a_Value)
+ {
+ m_Values.push_back(a_Value);
+ }
+
+protected:
+ std::vector<T> m_Values;
+
+};
+
+class SquirrelStringArray : public SquirrelArray<std::string> { };
+
+
+
+
+
+#endif // USE_SQUIRREL
+
+
+
+
diff --git a/source/squirrelbindings/SquirrelBaseClass.h b/source/squirrelbindings/SquirrelBaseClass.h
index 36f532611..fb5e95c05 100644
--- a/source/squirrelbindings/SquirrelBaseClass.h
+++ b/source/squirrelbindings/SquirrelBaseClass.h
@@ -1,66 +1,66 @@
-
-#pragma once
-
-
-
-
-#ifdef USE_SQUIRREL
-
-
-
-
-
-#include "SquirrelBindings.h"
-#include "../Plugin_Squirrel.h"
-#include "../PluginManager.h"
-#include "../Root.h"
-#include "../SquirrelCommandBinder.h"
-
-
-
-
-
-// The baseclass for squirrel plugins
-class cSquirrelBaseClass
-{
-public:
- cSquirrelBaseClass()
- : m_Instance(0)
- {
- }
-
- void setInstance(cPlugin_Squirrel *a_Instance)
- {
- m_Instance = a_Instance;
- }
-
- void AddHook(short a_Hook)
- {
- if(m_Instance)
- cRoot::Get()->GetPluginManager()->AddHook(m_Instance, (cPluginManager::PluginHook) a_Hook);
- }
-
- void AddCommand( std::string a_Command, std::string a_Description, std::string a_Permission )
- {
- if(m_Instance) m_Instance->AddCommand(a_Command, a_Description, a_Permission);
- }
-
- bool BindCommand( const std::string a_Command, const std::string a_Permission, Sqrat::Function a_Callback)
- {
- if(!m_Instance) return false;
- return cRoot::Get()->GetPluginManager()->GetSquirrelCommandBinder()->BindCommand(a_Command, a_Permission, m_Instance, a_Callback);
- }
-
-protected:
- cPlugin_Squirrel *m_Instance;
-};
-
-
-
-
-
-#endif // USE_SQUIRREL
-
-
-
-
+
+#pragma once
+
+
+
+
+#ifdef USE_SQUIRREL
+
+
+
+
+
+#include "SquirrelBindings.h"
+#include "../Plugin_Squirrel.h"
+#include "../PluginManager.h"
+#include "../Root.h"
+#include "../SquirrelCommandBinder.h"
+
+
+
+
+
+// The baseclass for squirrel plugins
+class cSquirrelBaseClass
+{
+public:
+ cSquirrelBaseClass()
+ : m_Instance(0)
+ {
+ }
+
+ void setInstance(cPlugin_Squirrel *a_Instance)
+ {
+ m_Instance = a_Instance;
+ }
+
+ void AddHook(short a_Hook)
+ {
+ if(m_Instance)
+ cRoot::Get()->GetPluginManager()->AddHook(m_Instance, (cPluginManager::PluginHook) a_Hook);
+ }
+
+ void AddCommand( std::string a_Command, std::string a_Description, std::string a_Permission )
+ {
+ if(m_Instance) m_Instance->AddCommand(a_Command, a_Description, a_Permission);
+ }
+
+ bool BindCommand( const std::string a_Command, const std::string a_Permission, Sqrat::Function a_Callback)
+ {
+ if(!m_Instance) return false;
+ return cRoot::Get()->GetPluginManager()->GetSquirrelCommandBinder()->BindCommand(a_Command, a_Permission, m_Instance, a_Callback);
+ }
+
+protected:
+ cPlugin_Squirrel *m_Instance;
+};
+
+
+
+
+
+#endif // USE_SQUIRREL
+
+
+
+
diff --git a/source/squirrelbindings/SquirrelBindings.cpp b/source/squirrelbindings/SquirrelBindings.cpp
index ac212b101..51acc184f 100644
--- a/source/squirrelbindings/SquirrelBindings.cpp
+++ b/source/squirrelbindings/SquirrelBindings.cpp
@@ -1,195 +1,195 @@
-
-#include "Globals.h"
-
-
-
-
-
-#ifdef USE_SQUIRREL
-
-
-
-
-#include "SquirrelBindings.h"
-#include "SquirrelFunctions.h"
-
-#include "SquirrelBaseClass.h"
-#include "SquirrelArray.h"
-
-#include "../Player.h"
-
-
-
-
-
-using namespace Sqrat;
-
-
-
-
-
-void BindSquirrel(HSQUIRRELVM vm)
-{
- RootTable()
- .Bind("Plugin", Class<cSquirrelBaseClass>()
- .Func("AddHook", &cSquirrelBaseClass::AddHook)
- .Func("AddCommand", &cSquirrelBaseClass::AddCommand)
- .Func("BindCommand", &cSquirrelBaseClass::BindCommand)
- );
-
- RootTable().Bind("Vector3f", Class<Vector3f, NoConstructor>()
- .Func("Set", &Vector3f::Set)
- .Func("Normalize", &Vector3f::Normalize)
- .Func("Length", &Vector3f::Length)
- .Func("SqrLength", &Vector3f::SqrLength)
- .Var("x", &Vector3f::x)
- .Var("y", &Vector3f::y)
- .Var("z", &Vector3f::z)
- );
-
- RootTable().Bind("Vector3d", Class<Vector3d, NoConstructor>()
- .Func("Set", &Vector3d::Set)
- .Func("Normalize", &Vector3d::Normalize)
- .Func("Length", &Vector3d::Length)
- .Func("SqrLength", &Vector3d::SqrLength)
- .Var("x", &Vector3d::x)
- .Var("y", &Vector3d::y)
- .Var("z", &Vector3d::z)
- );
-
- RootTable().Bind("cEntity", Class<cEntity, NoConstructor>()
- .Func("GetEntityType", &cEntity::GetEntityType)
- .Func("IsA", &cEntity::IsA)
- .Func("GetWorld", &cEntity::GetWorld)
- .Func("GetPosition", &cEntity::GetPosition)
- .Func("GetPosX", &cEntity::GetPosX)
- .Func("GetPosY", &cEntity::GetPosY)
- .Func("GetPosZ", &cEntity::GetPosZ)
- .Func("GetRot", &cEntity::GetRot)
- .Func("GetRotation", &cEntity::GetRotation)
- .Func("GetPitch", &cEntity::GetPitch)
- .Func("GetRoll", &cEntity::GetRoll)
- .Func("SetRotation", &cEntity::SetRotation)
- .Func("SetPitch", &cEntity::SetPitch)
- .Func("SetRoll", &cEntity::SetRoll)
- .Func("GetLookVector", &cEntity::GetLookVector)
- .Func("GetChunkX", &cEntity::GetChunkX)
- .Func("GetChunkY", &cEntity::GetChunkY)
- .Func("GetChunkZ", &cEntity::GetChunkZ)
- .Func("SetPosX", &cEntity::SetPosX)
- .Func("SetPosY", &cEntity::SetPosY)
- .Func("SetPosZ", &cEntity::SetPosZ)
- .Func<void (cEntity::*) (double, double, double)>("SetPosition", &cEntity::SetPosition)
- .Func("GetUniqueID", &cEntity::GetUniqueID)
- .Func("IsDestroyed", &cEntity::IsDestroyed)
- .Func("Destroy", &cEntity::Destroy)
- );
-
- ConstTable().Enum("MetaData", Enumeration()
- .Const("Normal", cPawn::NORMAL)
- .Const("Burning", cPawn::BURNING)
- .Const("Crouched", cPawn::CROUCHED)
- .Const("Riding", cPawn::RIDING)
- .Const("Sprinting", cPawn::SPRINTING)
- .Const("Eating", cPawn::EATING)
- .Const("Blocking", cPawn::BLOCKING)
- );
-
- RootTable().Bind("cPawn", DerivedClass<cPawn, cEntity, NoConstructor>()
- .Func("TeleportToEntity", &cPawn::TeleportToEntity)
- .Func("TeleportTo", &cPawn::TeleportTo)
- .Func("Heal", &cPawn::Heal)
- .Func("TakeDamage", &cPawn::TakeDamage)
- .Func("KilledBy", &cPawn::KilledBy)
- .Func("GetHealth", &cPawn::GetHealth)
- .Func("SetMetaData", &cPawn::SetMetaData)
- .Func("GetMetaData", &cPawn::GetMetaData)
- .Func("SetMaxHealth", &cPawn::SetMaxHealth)
- .Func("GetMaxHealth", &cPawn::GetMaxHealth)
- );
-
- RootTable().Bind("cPlayer", DerivedClass<cPlayer, cPawn, NoConstructor>()
- .Func("GetName", &cPlayer::GetName)
- .Func("SetTouchGround", &cPlayer::SetTouchGround)
- .Func("SetStance", &cPlayer::SetStance)
- .Func("GetEyeHeight", &cPlayer::GetEyeHeight)
- .Func("GetEyePosition", &cPlayer::GetEyePosition)
- .Func("GetFlying", &cPlayer::GetFlying)
- .Func("GetStance", &cPlayer::GetStance)
- //TODO .Func("GetInventory", &cPlayer::GetInventory)
- .Func("TeleportTo", &cPlayer::TeleportTo)
- .Func("GetGameMode", &cPlayer::GetGameMode)
- .Func("GetIP", &cPlayer::GetIP)
- .Func("GetLastBlockActionTime", &cPlayer::GetLastBlockActionTime)
- .Func("GetLastBlockActionCnt", &cPlayer::GetLastBlockActionCnt)
- .Func("SetLastBlockActionCnt", &cPlayer::SetLastBlockActionCnt)
- .Func("SetLastBlockActionTime", &cPlayer::SetLastBlockActionTime)
- .Func("SetGameMode", &cPlayer::SetGameMode)
- .Func("LoginSetGameMode", &cPlayer::LoginSetGameMode)
- .Func("SetIP", &cPlayer::SetIP)
- .Func("MoveTo", &cPlayer::MoveTo)
- .Func("GetClientHandle", &cPlayer::GetClientHandle)
- .Func("SendMessage", &cPlayer::SendMessage)
- .Func("AddToGroup", &cPlayer::AddToGroup)
- .Func("CanUseCommand", &cPlayer::CanUseCommand)
- .Func("HasPermission", &cPlayer::HasPermission)
- .Func("IsInGroup", &cPlayer::IsInGroup)
- .Func("GetColor", &cPlayer::GetColor)
- .Func("TossItem", &cPlayer::TossItem)
- .Func("Heal", &cPlayer::Heal)
- .Func("Feed", &cPlayer::Feed)
- .Func("TakeDamage", &cPlayer::TakeDamage)
- .Func("KilledBy", &cPlayer::KilledBy)
- .Func("Respawn", &cPlayer::Respawn)
- .Func("SetVisible", &cPlayer::SetVisible)
- .Func("IsVisible", &cPlayer::IsVisible)
- .Func("MoveToWorld", &cPlayer::MoveToWorld)
- .Func("GetLoadedWorldName", &cPlayer::GetLoadedWorldName)
- .Func("UseEquippedItem", &cPlayer::UseEquippedItem)
- );
-
- RootTable().Bind("StringArray", Class<SquirrelStringArray>()
- .Func("Get", &SquirrelStringArray::Get)
- .Func("Add", &SquirrelStringArray::Add)
- .Func("Size", &SquirrelStringArray::Size)
- );
-
-
- RootTable().Func("print", &sqPrint);
-
-
- ConstTable().Enum("Hook", Enumeration()
- .Const("Tick", cPluginManager::HOOK_TICK)
- .Const("Chat", cPluginManager::HOOK_CHAT)
- .Const("CollectPickup", cPluginManager::HOOK_COLLECT_PICKUP)
- .Const("BlockDig", cPluginManager::HOOK_BLOCK_DIG)
- .Const("BlockPlace", cPluginManager::HOOK_BLOCK_PLACE)
- .Const("Disconnect", cPluginManager::HOOK_DISCONNECT)
- .Const("Handshake", cPluginManager::HOOK_HANDSHAKE)
- .Const("Login", cPluginManager::HOOK_LOGIN)
- .Const("PlayerSpawn", cPluginManager::HOOK_PLAYER_SPAWN)
- .Const("PlayerJoin", cPluginManager::HOOK_PLAYER_JOIN)
- .Const("PlayerMove", cPluginManager::HOOK_PLAYER_MOVE)
- .Const("TakeDamage", cPluginManager::HOOK_TAKE_DAMAGE)
- .Const("Killed", cPluginManager::HOOK_KILLED)
- .Const("ChunkGenerated", cPluginManager::HOOK_CHUNK_GENERATED)
- .Const("ChunkGenerating", cPluginManager::HOOK_CHUNK_GENERATING)
- .Const("BlockToDrops", cPluginManager::HOOK_BLOCK_TO_DROPS)
- .Const("PreCrafting", cPluginManager::HOOK_PRE_CRAFTING)
- .Const("CraftingNoRecipe", cPluginManager::HOOK_CRAFTING_NO_RECIPE)
- .Const("PostCrafting", cPluginManager::HOOK_POST_CRAFTING)
- .Const("BlockToPickup", cPluginManager::HOOK_BLOCK_TO_PICKUP)
- .Const("WeatherChanged", cPluginManager::HOOK_WEATHER_CHANGED)
- .Const("UpdatingSign", cPluginManager::HOOK_UPDATING_SIGN)
- .Const("UpdatedSign", cPluginManager::HOOK_UPDATED_SIGN));
-}
-
-
-
-
-
-#endif // USE_SQUIRREL
-
-
-
-
+
+#include "Globals.h"
+
+
+
+
+
+#ifdef USE_SQUIRREL
+
+
+
+
+#include "SquirrelBindings.h"
+#include "SquirrelFunctions.h"
+
+#include "SquirrelBaseClass.h"
+#include "SquirrelArray.h"
+
+#include "../Player.h"
+
+
+
+
+
+using namespace Sqrat;
+
+
+
+
+
+void BindSquirrel(HSQUIRRELVM vm)
+{
+ RootTable()
+ .Bind("Plugin", Class<cSquirrelBaseClass>()
+ .Func("AddHook", &cSquirrelBaseClass::AddHook)
+ .Func("AddCommand", &cSquirrelBaseClass::AddCommand)
+ .Func("BindCommand", &cSquirrelBaseClass::BindCommand)
+ );
+
+ RootTable().Bind("Vector3f", Class<Vector3f, NoConstructor>()
+ .Func("Set", &Vector3f::Set)
+ .Func("Normalize", &Vector3f::Normalize)
+ .Func("Length", &Vector3f::Length)
+ .Func("SqrLength", &Vector3f::SqrLength)
+ .Var("x", &Vector3f::x)
+ .Var("y", &Vector3f::y)
+ .Var("z", &Vector3f::z)
+ );
+
+ RootTable().Bind("Vector3d", Class<Vector3d, NoConstructor>()
+ .Func("Set", &Vector3d::Set)
+ .Func("Normalize", &Vector3d::Normalize)
+ .Func("Length", &Vector3d::Length)
+ .Func("SqrLength", &Vector3d::SqrLength)
+ .Var("x", &Vector3d::x)
+ .Var("y", &Vector3d::y)
+ .Var("z", &Vector3d::z)
+ );
+
+ RootTable().Bind("cEntity", Class<cEntity, NoConstructor>()
+ .Func("GetEntityType", &cEntity::GetEntityType)
+ .Func("IsA", &cEntity::IsA)
+ .Func("GetWorld", &cEntity::GetWorld)
+ .Func("GetPosition", &cEntity::GetPosition)
+ .Func("GetPosX", &cEntity::GetPosX)
+ .Func("GetPosY", &cEntity::GetPosY)
+ .Func("GetPosZ", &cEntity::GetPosZ)
+ .Func("GetRot", &cEntity::GetRot)
+ .Func("GetRotation", &cEntity::GetRotation)
+ .Func("GetPitch", &cEntity::GetPitch)
+ .Func("GetRoll", &cEntity::GetRoll)
+ .Func("SetRotation", &cEntity::SetRotation)
+ .Func("SetPitch", &cEntity::SetPitch)
+ .Func("SetRoll", &cEntity::SetRoll)
+ .Func("GetLookVector", &cEntity::GetLookVector)
+ .Func("GetChunkX", &cEntity::GetChunkX)
+ .Func("GetChunkY", &cEntity::GetChunkY)
+ .Func("GetChunkZ", &cEntity::GetChunkZ)
+ .Func("SetPosX", &cEntity::SetPosX)
+ .Func("SetPosY", &cEntity::SetPosY)
+ .Func("SetPosZ", &cEntity::SetPosZ)
+ .Func<void (cEntity::*) (double, double, double)>("SetPosition", &cEntity::SetPosition)
+ .Func("GetUniqueID", &cEntity::GetUniqueID)
+ .Func("IsDestroyed", &cEntity::IsDestroyed)
+ .Func("Destroy", &cEntity::Destroy)
+ );
+
+ ConstTable().Enum("MetaData", Enumeration()
+ .Const("Normal", cPawn::NORMAL)
+ .Const("Burning", cPawn::BURNING)
+ .Const("Crouched", cPawn::CROUCHED)
+ .Const("Riding", cPawn::RIDING)
+ .Const("Sprinting", cPawn::SPRINTING)
+ .Const("Eating", cPawn::EATING)
+ .Const("Blocking", cPawn::BLOCKING)
+ );
+
+ RootTable().Bind("cPawn", DerivedClass<cPawn, cEntity, NoConstructor>()
+ .Func("TeleportToEntity", &cPawn::TeleportToEntity)
+ .Func("TeleportTo", &cPawn::TeleportTo)
+ .Func("Heal", &cPawn::Heal)
+ .Func("TakeDamage", &cPawn::TakeDamage)
+ .Func("KilledBy", &cPawn::KilledBy)
+ .Func("GetHealth", &cPawn::GetHealth)
+ .Func("SetMetaData", &cPawn::SetMetaData)
+ .Func("GetMetaData", &cPawn::GetMetaData)
+ .Func("SetMaxHealth", &cPawn::SetMaxHealth)
+ .Func("GetMaxHealth", &cPawn::GetMaxHealth)
+ );
+
+ RootTable().Bind("cPlayer", DerivedClass<cPlayer, cPawn, NoConstructor>()
+ .Func("GetName", &cPlayer::GetName)
+ .Func("SetTouchGround", &cPlayer::SetTouchGround)
+ .Func("SetStance", &cPlayer::SetStance)
+ .Func("GetEyeHeight", &cPlayer::GetEyeHeight)
+ .Func("GetEyePosition", &cPlayer::GetEyePosition)
+ .Func("GetFlying", &cPlayer::GetFlying)
+ .Func("GetStance", &cPlayer::GetStance)
+ //TODO .Func("GetInventory", &cPlayer::GetInventory)
+ .Func("TeleportTo", &cPlayer::TeleportTo)
+ .Func("GetGameMode", &cPlayer::GetGameMode)
+ .Func("GetIP", &cPlayer::GetIP)
+ .Func("GetLastBlockActionTime", &cPlayer::GetLastBlockActionTime)
+ .Func("GetLastBlockActionCnt", &cPlayer::GetLastBlockActionCnt)
+ .Func("SetLastBlockActionCnt", &cPlayer::SetLastBlockActionCnt)
+ .Func("SetLastBlockActionTime", &cPlayer::SetLastBlockActionTime)
+ .Func("SetGameMode", &cPlayer::SetGameMode)
+ .Func("LoginSetGameMode", &cPlayer::LoginSetGameMode)
+ .Func("SetIP", &cPlayer::SetIP)
+ .Func("MoveTo", &cPlayer::MoveTo)
+ .Func("GetClientHandle", &cPlayer::GetClientHandle)
+ .Func("SendMessage", &cPlayer::SendMessage)
+ .Func("AddToGroup", &cPlayer::AddToGroup)
+ .Func("CanUseCommand", &cPlayer::CanUseCommand)
+ .Func("HasPermission", &cPlayer::HasPermission)
+ .Func("IsInGroup", &cPlayer::IsInGroup)
+ .Func("GetColor", &cPlayer::GetColor)
+ .Func("TossItem", &cPlayer::TossItem)
+ .Func("Heal", &cPlayer::Heal)
+ .Func("Feed", &cPlayer::Feed)
+ .Func("TakeDamage", &cPlayer::TakeDamage)
+ .Func("KilledBy", &cPlayer::KilledBy)
+ .Func("Respawn", &cPlayer::Respawn)
+ .Func("SetVisible", &cPlayer::SetVisible)
+ .Func("IsVisible", &cPlayer::IsVisible)
+ .Func("MoveToWorld", &cPlayer::MoveToWorld)
+ .Func("GetLoadedWorldName", &cPlayer::GetLoadedWorldName)
+ .Func("UseEquippedItem", &cPlayer::UseEquippedItem)
+ );
+
+ RootTable().Bind("StringArray", Class<SquirrelStringArray>()
+ .Func("Get", &SquirrelStringArray::Get)
+ .Func("Add", &SquirrelStringArray::Add)
+ .Func("Size", &SquirrelStringArray::Size)
+ );
+
+
+ RootTable().Func("print", &sqPrint);
+
+
+ ConstTable().Enum("Hook", Enumeration()
+ .Const("Tick", cPluginManager::HOOK_TICK)
+ .Const("Chat", cPluginManager::HOOK_CHAT)
+ .Const("CollectPickup", cPluginManager::HOOK_COLLECT_PICKUP)
+ .Const("BlockDig", cPluginManager::HOOK_BLOCK_DIG)
+ .Const("BlockPlace", cPluginManager::HOOK_BLOCK_PLACE)
+ .Const("Disconnect", cPluginManager::HOOK_DISCONNECT)
+ .Const("Handshake", cPluginManager::HOOK_HANDSHAKE)
+ .Const("Login", cPluginManager::HOOK_LOGIN)
+ .Const("PlayerSpawn", cPluginManager::HOOK_PLAYER_SPAWN)
+ .Const("PlayerJoin", cPluginManager::HOOK_PLAYER_JOIN)
+ .Const("PlayerMove", cPluginManager::HOOK_PLAYER_MOVE)
+ .Const("TakeDamage", cPluginManager::HOOK_TAKE_DAMAGE)
+ .Const("Killed", cPluginManager::HOOK_KILLED)
+ .Const("ChunkGenerated", cPluginManager::HOOK_CHUNK_GENERATED)
+ .Const("ChunkGenerating", cPluginManager::HOOK_CHUNK_GENERATING)
+ .Const("BlockToDrops", cPluginManager::HOOK_BLOCK_TO_DROPS)
+ .Const("PreCrafting", cPluginManager::HOOK_PRE_CRAFTING)
+ .Const("CraftingNoRecipe", cPluginManager::HOOK_CRAFTING_NO_RECIPE)
+ .Const("PostCrafting", cPluginManager::HOOK_POST_CRAFTING)
+ .Const("BlockToPickup", cPluginManager::HOOK_BLOCK_TO_PICKUP)
+ .Const("WeatherChanged", cPluginManager::HOOK_WEATHER_CHANGED)
+ .Const("UpdatingSign", cPluginManager::HOOK_UPDATING_SIGN)
+ .Const("UpdatedSign", cPluginManager::HOOK_UPDATED_SIGN));
+}
+
+
+
+
+
+#endif // USE_SQUIRREL
+
+
+
+
diff --git a/source/squirrelbindings/SquirrelBindings.h b/source/squirrelbindings/SquirrelBindings.h
index fef563f4a..dd2affda0 100644
--- a/source/squirrelbindings/SquirrelBindings.h
+++ b/source/squirrelbindings/SquirrelBindings.h
@@ -1,32 +1,32 @@
-
-#pragma once
-
-
-
-
-
-#ifdef USE_SQUIRREL
-
-
-
-
-
-#include <squirrel.h>
-#include <sqrat.h>
-
-
-
-
-
-void BindSquirrel(HSQUIRRELVM vm);
-
-
-
-
-
-#endif // USE_SQUIRREL
-
-
-
-
-
+
+#pragma once
+
+
+
+
+
+#ifdef USE_SQUIRREL
+
+
+
+
+
+#include <squirrel.h>
+#include <sqrat.h>
+
+
+
+
+
+void BindSquirrel(HSQUIRRELVM vm);
+
+
+
+
+
+#endif // USE_SQUIRREL
+
+
+
+
+
diff --git a/source/squirrelbindings/SquirrelFunctions.cpp b/source/squirrelbindings/SquirrelFunctions.cpp
index 0e35f7361..9407670d4 100644
--- a/source/squirrelbindings/SquirrelFunctions.cpp
+++ b/source/squirrelbindings/SquirrelFunctions.cpp
@@ -1,94 +1,94 @@
-
-#include "Globals.h"
-
-
-
-
-
-#ifdef USE_SQUIRREL
-
-
-
-
-
-#include "SquirrelFunctions.h"
-#include "SquirrelBindings.h"
-
-
-
-
-
-static HSQUIRRELVM squirrelvm = NULL;
-
-
-
-
-
-SQInteger runtimeErrorHandler(HSQUIRRELVM a_VM)
-{
- const SQChar *sErr = 0;
- if(sq_gettop(a_VM) >= 1)
- {
- if(SQ_SUCCEEDED(sq_getstring(a_VM, 2, &sErr)))
- {
- LOGERROR("Squirrel Error: %s", sErr);
- }
- else
- {
- LOGERROR("Squirrel Error: Unknown Error");
- }
- }
- return 0;
-}
-
-void compilerErrorHandler(HSQUIRRELVM v,
- const SQChar* a_Desc,
- const SQChar* a_Source,
- SQInteger a_Line,
- SQInteger a_Column)
-{
-
- LOGERROR("Squirrel Error: %s (%d:%d) %s", a_Source, a_Line, a_Column, a_Desc);
-}
-
-HSQUIRRELVM OpenSquirrelVM()
-{
- if(!squirrelvm)
- {
- squirrelvm = sq_open(1024);
- Sqrat::DefaultVM::Set(squirrelvm);
-
- sq_newclosure(squirrelvm, runtimeErrorHandler, 0);
- sq_seterrorhandler(squirrelvm);
-
- sq_setcompilererrorhandler(squirrelvm, compilerErrorHandler);
-
- BindSquirrel(squirrelvm);
- }
- return squirrelvm;
-}
-
-void CloseSquirrelVM()
-{
- if(squirrelvm)
- {
- sq_close(squirrelvm);
- squirrelvm = NULL;
- }
-}
-
-
-void sqPrint(SQChar * text)
-{
- LOGINFO("%s", text);
-}
-
-
-
-
-
-#endif // USE_SQUIRREL
-
-
-
-
+
+#include "Globals.h"
+
+
+
+
+
+#ifdef USE_SQUIRREL
+
+
+
+
+
+#include "SquirrelFunctions.h"
+#include "SquirrelBindings.h"
+
+
+
+
+
+static HSQUIRRELVM squirrelvm = NULL;
+
+
+
+
+
+SQInteger runtimeErrorHandler(HSQUIRRELVM a_VM)
+{
+ const SQChar *sErr = 0;
+ if(sq_gettop(a_VM) >= 1)
+ {
+ if(SQ_SUCCEEDED(sq_getstring(a_VM, 2, &sErr)))
+ {
+ LOGERROR("Squirrel Error: %s", sErr);
+ }
+ else
+ {
+ LOGERROR("Squirrel Error: Unknown Error");
+ }
+ }
+ return 0;
+}
+
+void compilerErrorHandler(HSQUIRRELVM v,
+ const SQChar* a_Desc,
+ const SQChar* a_Source,
+ SQInteger a_Line,
+ SQInteger a_Column)
+{
+
+ LOGERROR("Squirrel Error: %s (%d:%d) %s", a_Source, a_Line, a_Column, a_Desc);
+}
+
+HSQUIRRELVM OpenSquirrelVM()
+{
+ if(!squirrelvm)
+ {
+ squirrelvm = sq_open(1024);
+ Sqrat::DefaultVM::Set(squirrelvm);
+
+ sq_newclosure(squirrelvm, runtimeErrorHandler, 0);
+ sq_seterrorhandler(squirrelvm);
+
+ sq_setcompilererrorhandler(squirrelvm, compilerErrorHandler);
+
+ BindSquirrel(squirrelvm);
+ }
+ return squirrelvm;
+}
+
+void CloseSquirrelVM()
+{
+ if(squirrelvm)
+ {
+ sq_close(squirrelvm);
+ squirrelvm = NULL;
+ }
+}
+
+
+void sqPrint(SQChar * text)
+{
+ LOGINFO("%s", text);
+}
+
+
+
+
+
+#endif // USE_SQUIRREL
+
+
+
+
diff --git a/source/squirrelbindings/SquirrelFunctions.h b/source/squirrelbindings/SquirrelFunctions.h
index 2b74fb52d..272b79c3e 100644
--- a/source/squirrelbindings/SquirrelFunctions.h
+++ b/source/squirrelbindings/SquirrelFunctions.h
@@ -1,28 +1,28 @@
-
-#pragma once
-
-#ifdef USE_SQUIRREL
-
-
-
-
-#include <sqrat.h>
-
-
-
-
-
-HSQUIRRELVM OpenSquirrelVM();
-void CloseSquirrelVM();
-
-void sqPrint(SQChar * text);
-
-
-
-
-
-#endif // USE_SQUIRREL
-
-
-
-
+
+#pragma once
+
+#ifdef USE_SQUIRREL
+
+
+
+
+#include <sqrat.h>
+
+
+
+
+
+HSQUIRRELVM OpenSquirrelVM();
+void CloseSquirrelVM();
+
+void sqPrint(SQChar * text);
+
+
+
+
+
+#endif // USE_SQUIRREL
+
+
+
+
diff --git a/source/squirrelbindings/SquirrelObject.h b/source/squirrelbindings/SquirrelObject.h
index a7a44d531..248a5e02c 100644
--- a/source/squirrelbindings/SquirrelObject.h
+++ b/source/squirrelbindings/SquirrelObject.h
@@ -1,55 +1,55 @@
-
-#pragma once
-
-
-
-
-
-#ifdef USE_SQUIRREL
-
-
-
-
-
-#include <sqrat.h>
-
-
-
-
-
-class SquirrelObject
-{
-public:
- SquirrelObject(Sqrat::Object a_Obj)
- {
- m_SquirrelObject = a_Obj;
- }
-
- Sqrat::Function GetFunction(const char *a_MethodName)
- {
- if(m_SquirrelObject.IsNull())
- return Sqrat::Function();
-
- Sqrat::Function method(m_SquirrelObject, a_MethodName);
- return method;
- }
-
- bool HasFunction(const char *a_MethodName)
- {
- return !this->GetFunction(a_MethodName).IsNull();
- }
-
-protected:
- Sqrat::Object m_SquirrelObject;
-
-};
-
-
-
-
-
-#endif // USE_SQUIRREL
-
-
-
-
+
+#pragma once
+
+
+
+
+
+#ifdef USE_SQUIRREL
+
+
+
+
+
+#include <sqrat.h>
+
+
+
+
+
+class SquirrelObject
+{
+public:
+ SquirrelObject(Sqrat::Object a_Obj)
+ {
+ m_SquirrelObject = a_Obj;
+ }
+
+ Sqrat::Function GetFunction(const char *a_MethodName)
+ {
+ if(m_SquirrelObject.IsNull())
+ return Sqrat::Function();
+
+ Sqrat::Function method(m_SquirrelObject, a_MethodName);
+ return method;
+ }
+
+ bool HasFunction(const char *a_MethodName)
+ {
+ return !this->GetFunction(a_MethodName).IsNull();
+ }
+
+protected:
+ Sqrat::Object m_SquirrelObject;
+
+};
+
+
+
+
+
+#endif // USE_SQUIRREL
+
+
+
+
diff --git a/source/virtual_method_hooks.lua b/source/virtual_method_hooks.lua
index 407b61627..b6cbe8268 100644
--- a/source/virtual_method_hooks.lua
+++ b/source/virtual_method_hooks.lua
@@ -1,506 +1,506 @@
--- flags
-local disable_virtual_hooks = false
-local enable_pure_virtual = true
-local default_private_access = false
-
-local access = {public = 0, protected = 1, private = 2}
-
-function preparse_hook(p)
-
- if default_private_access then
- -- we need to make all structs 'public' by default
- p.code = string.gsub(p.code, "(struct[^;]*{)", "%1\npublic:\n")
- end
-end
-
-
-function parser_hook(s)
-
- local container = classContainer.curr -- get the current container
-
- if default_private_access then
- if not container.curr_member_access and container.classtype == 'class' then
- -- default access for classes is private
- container.curr_member_access = access.private
- end
- end
-
- -- try labels (public, private, etc)
- do
- local b,e,label = string.find(s, "^%s*(%w*)%s*:[^:]") -- we need to check for [^:], otherwise it would match 'namespace::type'
- if b then
-
- -- found a label, get the new access value from the global 'access' table
- if access[label] then
- container.curr_member_access = access[label]
- end -- else ?
-
- return strsub(s, e) -- normally we would use 'e+1', but we need to preserve the [^:]
- end
- end
-
-
- local ret = nil
-
- if disable_virtual_hooks then
-
- return ret
- end
-
- local b,e,decl,arg = string.find(s, "^%s*virtual%s+([^%({~]+)(%b())")
- local const
- if b then
- local ret = string.sub(s, e+1)
- if string.find(ret, "^%s*const") then
- const = "const"
- ret = string.gsub(ret, "^%s*const", "")
- end
- local purev = false
- if string.find(ret, "^%s*=%s*0") then
- purev = true
- ret = string.gsub(ret, "^%s*=%s*0", "")
- end
- ret = string.gsub(ret, "^%s*%b{}", "")
-
- local func = Function(decl, arg, const)
- func.pure_virtual = purev
- --func.access = access
- func.original_sig = decl
-
- local curflags = classContainer.curr.flags
- if not curflags.virtual_class then
-
- curflags.virtual_class = VirtualClass()
- end
- curflags.virtual_class:add(func)
- curflags.pure_virtual = curflags.pure_virtual or purev
-
- return ret
- end
-
- return ret
-end
-
-
--- class VirtualClass
-classVirtualClass = {
- classtype = 'class',
- name = '',
- base = '',
- type = '',
- btype = '',
- ctype = '',
-}
-classVirtualClass.__index = classVirtualClass
-setmetatable(classVirtualClass,classClass)
-
-function classVirtualClass:add(f)
-
- local parent = classContainer.curr
- pop()
-
- table.insert(self.methods, {f=f})
-
- local name,sig
-
- -- doble negative means positive
- if f.name == 'new' and ((not self.flags.parent_object.flags.pure_virtual) or (enable_pure_virtual)) then
-
- name = self.original_name
- elseif f.name == 'delete' then
- name = '~'..self.original_name
- else
- if f.access ~= 2 and (not f.pure_virtual) and f.name ~= 'new' and f.name ~= 'delete' then
- name = f.mod.." "..f.type..f.ptr.." "..self.flags.parent_object.lname.."__"..f.name
- end
- end
-
- if name then
- sig = name..self:get_arg_list(f, true)..";\n"
- push(self)
- sig = preprocess(sig)
- self:parse(sig)
- pop()
- end
-
- push(parent)
-end
-
-function preprocess(sig)
-
- sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*'
- sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*'
- sig = gsub(sig,"([^%w_])char%s*%*","%1_cstring ") -- substitute 'char*'
- sig = gsub(sig,"([^%w_])lua_State%s*%*","%1_lstate ") -- substitute 'lua_State*'
-
- return sig
-end
-
-function classVirtualClass:get_arg_list(f, decl)
-
- local ret = ""
- local sep = ""
- local i=1
- while f.args[i] do
-
- local arg = f.args[i]
- if decl then
- local ptr
- if arg.ret ~= '' then
- ptr = arg.ret
- else
- ptr = arg.ptr
- end
- local def = ""
- if arg.def and arg.def ~= "" then
-
- def = " = "..arg.def
- end
- ret = ret..sep..arg.mod.." "..arg.type..ptr.." "..arg.name..def
- else
- ret = ret..sep..arg.name
- end
-
- sep = ","
- i = i+1
- end
-
- return "("..ret..")"
-end
-
-function classVirtualClass:add_parent_virtual_methods(parent)
-
- parent = parent or _global_classes[self.flags.parent_object.btype]
-
- if not parent then return end
-
- if parent.flags.virtual_class then
-
- local vclass = parent.flags.virtual_class
- for k,v in ipairs(vclass.methods) do
- if v.f.name ~= 'new' and v.f.name ~= 'delete' and (not self:has_method(v.f)) then
- table.insert(self.methods, {f=v.f})
- end
- end
- end
-
- parent = _global_classes[parent.btype]
- if parent then
- self:add_parent_virtual_methods(parent)
- end
-end
-
-function classVirtualClass:has_method(f)
-
- for k,v in pairs(self.methods) do
- -- just match name for now
- if v.f.name == f.name then
- return true
- end
- end
-
- return false
-end
-
-function classVirtualClass:add_constructors()
-
- local i=1
- while self.flags.parent_object[i] do
-
- local v = self.flags.parent_object[i]
- if getmetatable(v) == classFunction and (v.name == 'new' or v.name == 'delete') then
-
- self:add(v)
- end
-
- i = i+1
- end
-
-end
-
---[[
-function classVirtualClass:requirecollection(t)
-
- self:add_constructors()
- local req = classClass.requirecollection(self, t)
- if req then
- output('class ',self.name,";")
- end
- return req
-end
---]]
-
-function classVirtualClass:supcode()
-
- -- pure virtual classes can have no default constructors on gcc 4
-
- if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
- output('#if (__GNUC__ == 4) || (__GNUC__ > 4 ) // I hope this works on Microsoft Visual studio .net server 2003 XP Compiler\n')
- end
-
- local ns
- if self.prox.classtype == 'namespace' then
- output('namespace ',self.prox.name, " {")
- ns = true
- end
-
- output("class "..self.original_name.." : public "..self.btype..", public ToluaBase {")
-
- output("public:\n")
-
- self:add_parent_virtual_methods()
-
- self:output_methods(self.btype)
- self:output_parent_methods()
-
- self:add_constructors()
-
- -- no constructor for pure virtual classes
- if (not self.flags.parent_object.flags.pure_virtual) or enable_pure_virtual then
-
- self:output_constructors()
- end
-
- output("};\n\n")
-
- if ns then
- output("};")
- end
-
- classClass.supcode(self)
-
- if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
- output('#endif // __GNUC__ >= 4\n')
- end
-
- -- output collector for custom class if required
- if self:requirecollection(_collect) and _collect[self.type] then
-
- output('\n')
- output('/* function to release collected object via destructor */')
- output('#ifdef __cplusplus\n')
- --for i,v in pairs(collect) do
- i,v = self.type, _collect[self.type]
- output('\nstatic int '..v..' (lua_State* tolua_S)')
- output('{')
- output(' '..i..'* self = ('..i..'*) tolua_tousertype(tolua_S,1,0);')
- output(' delete self;')
- output(' return 0;')
- output('}')
- --end
- output('#endif\n\n')
- end
-
-end
-
-function classVirtualClass:register(pre)
-
- -- pure virtual classes can have no default constructors on gcc 4
- if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
- output('#if (__GNUC__ == 4) || (__GNUC__ > 4 )\n')
- end
-
- classClass.register(self, pre)
-
- if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
- output('#endif // __GNUC__ >= 4\n')
- end
-end
-
-
---function classVirtualClass:requirecollection(_c)
--- if self.flags.parent_object.flags.pure_virtual then
--- return false
--- end
--- return classClass.requirecollection(self, _c)
---end
-
-function classVirtualClass:output_parent_methods()
-
- for k,v in ipairs(self.methods) do
-
- if v.f.access ~= 2 and (not v.f.pure_virtual) and v.f.name ~= 'new' and v.f.name ~= 'delete' then
-
- local rettype = v.f.mod.." "..v.f.type..v.f.ptr.." "
- local parent_name = rettype..self.btype.."__"..v.f.name
-
- local par_list = self:get_arg_list(v.f, true)
- local var_list = self:get_arg_list(v.f, false)
-
- -- the parent's virtual function
- output("\t"..parent_name..par_list.." {")
-
- output("\t\treturn (",rettype,")"..self.btype.."::"..v.f.name..var_list..";")
- output("\t};")
- end
- end
-end
-
-function classVirtualClass:output_methods(btype)
-
- for k,v in ipairs(self.methods) do
-
- if v.f.name ~= 'new' and v.f.name ~= 'delete' then
-
- self:output_method(v.f, btype)
- end
- end
- output("\n")
-end
-
-function classVirtualClass:output_constructors()
-
- for k,v in ipairs(self.methods) do
-
- if v.f.name == 'new' then
-
- local par_list = self:get_arg_list(v.f, true)
- local var_list = self:get_arg_list(v.f, false)
-
- output("\t",self.original_name,par_list,":",self.btype,var_list,"{};")
- end
- end
-end
-
-function classVirtualClass:output_method(f, btype)
-
- if f.access == 2 then -- private
- return
- end
-
- local ptr
- if f.ret ~= '' then
- ptr = f.ret
- else
- ptr = f.ptr
- end
-
- local rettype = f.mod.." "..f.type..f.ptr.." "
- local par_list = self:get_arg_list(f, true)
- local var_list = self:get_arg_list(f, false)
-
- if string.find(rettype, "%s*LuaQtGenericFlags%s*") then
-
- _,_,rettype = string.find(f.original_sig, "^%s*([^%s]+)%s+")
- end
-
- -- the caller of the lua method
- output("\t"..rettype.." "..f.name..par_list..f.const.." {")
- local fn = f.cname
- if f.access == 1 then
- fn = "NULL"
- end
- output('\t\tif (push_method("',f.lname,'", ',fn,')) {')
-
- --if f.type ~= 'void' then
- -- output("\t\t\tint top = lua_gettop(lua_state)-1;")
- --end
-
- -- push the parameters
- local argn = 0
- for i,arg in ipairs(f.args) do
- if arg.type ~= 'void' then
- local t,ct = isbasic(arg.type)
- if t and t ~= '' then
- if arg.ret == "*" then
- t = 'userdata'
- ct = 'void*'
- end
- output("\t\t\ttolua_push"..t.."(lua_state, ("..ct..")"..arg.name..");");
- else
- local m = arg.ptr
- if m and m~= "" then
- if m == "*" then m = "" end
- output("\t\t\ttolua_pushusertype(lua_state, (void*)"..m..arg.name..", \""..arg.type.."\");")
- else
- output("\t\t\tvoid* tolua_obj" .. argn .." = (void*)new "..arg.type.."("..arg.name..");\n")
- output('\t\t\ttolua_pushusertype_and_takeownership(lua_state, tolua_obj' .. argn .. ', "'..arg.type..'");\n')
- end
- end
- argn = argn+1
- end
- end
-
- -- call the function
- output("\t\t\tToluaBase::dbcall(lua_state, ",argn+1,", ")
-
- -- return value
- if f.type ~= 'void' then
- output("1);")
-
- local t,ct = isbasic(f.type)
- if t and t ~= '' then
- --output("\t\t\treturn ("..rettype..")tolua_to"..t.."(lua_state, top, 0);")
- output("\t\t\t",rettype,"tolua_ret = ("..rettype..")tolua_to"..t.."(lua_state, -1, 0);")
- else
-
- local mod = ""
- if f.ptr ~= "*" then
- mod = "*("..f.type.."*)"
- end
-
- --output("\t\t\treturn ("..rettype..")"..mod.."tolua_tousertype(lua_state, top, 0);")
- output("\t\t\t",rettype,"tolua_ret = ("..rettype..")"..mod.."tolua_tousertype(lua_state, -1, 0);")
- end
- output("\t\t\tlua_pop(lua_state, 1);")
- output("\t\t\treturn tolua_ret;")
- else
- output("0);")
- end
-
- -- handle non-implemeted function
- output("\t\t} else {")
-
- if f.pure_virtual then
-
- output('\t\t\tif (lua_state)')
- --output('\t\t\t\ttolua_error(lua_state, "pure-virtual method '..btype.."::"..f.name..' not implemented.", NULL);')
- output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' not implemented.");')
- output('\t\t\telse {')
- output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' called with no lua_state. Aborting");')
- output('\t\t\t\t::abort();')
- output('\t\t\t};')
- if( rettype == " std::string " ) then
- output('\t\t\treturn "";')
- else
- output('\t\t\treturn (',rettype,')0;')
- end
- else
-
- output('\t\t\treturn (',rettype,')',btype,'::',f.name,var_list,';')
- end
-
- output("\t\t};")
-
- output("\t};")
-end
-
-function VirtualClass()
-
- local parent = classContainer.curr
- pop()
-
- local name = "Lua__"..parent.original_name
-
- local c = _Class(_Container{name=name, base=parent.name, extra_bases=nil})
- setmetatable(c, classVirtualClass)
-
- local ft = getnamespace(c.parent)..c.original_name
- append_global_type(ft, c)
-
- push(parent)
-
- c.flags.parent_object = parent
- c.methods = {}
-
- push(c)
- c:parse("\nvoid tolua__set_instance(_lstate L, lua_Object lo);\n")
- pop()
-
- return c
-end
-
-
-
-
-
+-- flags
+local disable_virtual_hooks = false
+local enable_pure_virtual = true
+local default_private_access = false
+
+local access = {public = 0, protected = 1, private = 2}
+
+function preparse_hook(p)
+
+ if default_private_access then
+ -- we need to make all structs 'public' by default
+ p.code = string.gsub(p.code, "(struct[^;]*{)", "%1\npublic:\n")
+ end
+end
+
+
+function parser_hook(s)
+
+ local container = classContainer.curr -- get the current container
+
+ if default_private_access then
+ if not container.curr_member_access and container.classtype == 'class' then
+ -- default access for classes is private
+ container.curr_member_access = access.private
+ end
+ end
+
+ -- try labels (public, private, etc)
+ do
+ local b,e,label = string.find(s, "^%s*(%w*)%s*:[^:]") -- we need to check for [^:], otherwise it would match 'namespace::type'
+ if b then
+
+ -- found a label, get the new access value from the global 'access' table
+ if access[label] then
+ container.curr_member_access = access[label]
+ end -- else ?
+
+ return strsub(s, e) -- normally we would use 'e+1', but we need to preserve the [^:]
+ end
+ end
+
+
+ local ret = nil
+
+ if disable_virtual_hooks then
+
+ return ret
+ end
+
+ local b,e,decl,arg = string.find(s, "^%s*virtual%s+([^%({~]+)(%b())")
+ local const
+ if b then
+ local ret = string.sub(s, e+1)
+ if string.find(ret, "^%s*const") then
+ const = "const"
+ ret = string.gsub(ret, "^%s*const", "")
+ end
+ local purev = false
+ if string.find(ret, "^%s*=%s*0") then
+ purev = true
+ ret = string.gsub(ret, "^%s*=%s*0", "")
+ end
+ ret = string.gsub(ret, "^%s*%b{}", "")
+
+ local func = Function(decl, arg, const)
+ func.pure_virtual = purev
+ --func.access = access
+ func.original_sig = decl
+
+ local curflags = classContainer.curr.flags
+ if not curflags.virtual_class then
+
+ curflags.virtual_class = VirtualClass()
+ end
+ curflags.virtual_class:add(func)
+ curflags.pure_virtual = curflags.pure_virtual or purev
+
+ return ret
+ end
+
+ return ret
+end
+
+
+-- class VirtualClass
+classVirtualClass = {
+ classtype = 'class',
+ name = '',
+ base = '',
+ type = '',
+ btype = '',
+ ctype = '',
+}
+classVirtualClass.__index = classVirtualClass
+setmetatable(classVirtualClass,classClass)
+
+function classVirtualClass:add(f)
+
+ local parent = classContainer.curr
+ pop()
+
+ table.insert(self.methods, {f=f})
+
+ local name,sig
+
+ -- doble negative means positive
+ if f.name == 'new' and ((not self.flags.parent_object.flags.pure_virtual) or (enable_pure_virtual)) then
+
+ name = self.original_name
+ elseif f.name == 'delete' then
+ name = '~'..self.original_name
+ else
+ if f.access ~= 2 and (not f.pure_virtual) and f.name ~= 'new' and f.name ~= 'delete' then
+ name = f.mod.." "..f.type..f.ptr.." "..self.flags.parent_object.lname.."__"..f.name
+ end
+ end
+
+ if name then
+ sig = name..self:get_arg_list(f, true)..";\n"
+ push(self)
+ sig = preprocess(sig)
+ self:parse(sig)
+ pop()
+ end
+
+ push(parent)
+end
+
+function preprocess(sig)
+
+ sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*'
+ sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*'
+ sig = gsub(sig,"([^%w_])char%s*%*","%1_cstring ") -- substitute 'char*'
+ sig = gsub(sig,"([^%w_])lua_State%s*%*","%1_lstate ") -- substitute 'lua_State*'
+
+ return sig
+end
+
+function classVirtualClass:get_arg_list(f, decl)
+
+ local ret = ""
+ local sep = ""
+ local i=1
+ while f.args[i] do
+
+ local arg = f.args[i]
+ if decl then
+ local ptr
+ if arg.ret ~= '' then
+ ptr = arg.ret
+ else
+ ptr = arg.ptr
+ end
+ local def = ""
+ if arg.def and arg.def ~= "" then
+
+ def = " = "..arg.def
+ end
+ ret = ret..sep..arg.mod.." "..arg.type..ptr.." "..arg.name..def
+ else
+ ret = ret..sep..arg.name
+ end
+
+ sep = ","
+ i = i+1
+ end
+
+ return "("..ret..")"
+end
+
+function classVirtualClass:add_parent_virtual_methods(parent)
+
+ parent = parent or _global_classes[self.flags.parent_object.btype]
+
+ if not parent then return end
+
+ if parent.flags.virtual_class then
+
+ local vclass = parent.flags.virtual_class
+ for k,v in ipairs(vclass.methods) do
+ if v.f.name ~= 'new' and v.f.name ~= 'delete' and (not self:has_method(v.f)) then
+ table.insert(self.methods, {f=v.f})
+ end
+ end
+ end
+
+ parent = _global_classes[parent.btype]
+ if parent then
+ self:add_parent_virtual_methods(parent)
+ end
+end
+
+function classVirtualClass:has_method(f)
+
+ for k,v in pairs(self.methods) do
+ -- just match name for now
+ if v.f.name == f.name then
+ return true
+ end
+ end
+
+ return false
+end
+
+function classVirtualClass:add_constructors()
+
+ local i=1
+ while self.flags.parent_object[i] do
+
+ local v = self.flags.parent_object[i]
+ if getmetatable(v) == classFunction and (v.name == 'new' or v.name == 'delete') then
+
+ self:add(v)
+ end
+
+ i = i+1
+ end
+
+end
+
+--[[
+function classVirtualClass:requirecollection(t)
+
+ self:add_constructors()
+ local req = classClass.requirecollection(self, t)
+ if req then
+ output('class ',self.name,";")
+ end
+ return req
+end
+--]]
+
+function classVirtualClass:supcode()
+
+ -- pure virtual classes can have no default constructors on gcc 4
+
+ if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
+ output('#if (__GNUC__ == 4) || (__GNUC__ > 4 ) // I hope this works on Microsoft Visual studio .net server 2003 XP Compiler\n')
+ end
+
+ local ns
+ if self.prox.classtype == 'namespace' then
+ output('namespace ',self.prox.name, " {")
+ ns = true
+ end
+
+ output("class "..self.original_name.." : public "..self.btype..", public ToluaBase {")
+
+ output("public:\n")
+
+ self:add_parent_virtual_methods()
+
+ self:output_methods(self.btype)
+ self:output_parent_methods()
+
+ self:add_constructors()
+
+ -- no constructor for pure virtual classes
+ if (not self.flags.parent_object.flags.pure_virtual) or enable_pure_virtual then
+
+ self:output_constructors()
+ end
+
+ output("};\n\n")
+
+ if ns then
+ output("};")
+ end
+
+ classClass.supcode(self)
+
+ if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
+ output('#endif // __GNUC__ >= 4\n')
+ end
+
+ -- output collector for custom class if required
+ if self:requirecollection(_collect) and _collect[self.type] then
+
+ output('\n')
+ output('/* function to release collected object via destructor */')
+ output('#ifdef __cplusplus\n')
+ --for i,v in pairs(collect) do
+ i,v = self.type, _collect[self.type]
+ output('\nstatic int '..v..' (lua_State* tolua_S)')
+ output('{')
+ output(' '..i..'* self = ('..i..'*) tolua_tousertype(tolua_S,1,0);')
+ output(' delete self;')
+ output(' return 0;')
+ output('}')
+ --end
+ output('#endif\n\n')
+ end
+
+end
+
+function classVirtualClass:register(pre)
+
+ -- pure virtual classes can have no default constructors on gcc 4
+ if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
+ output('#if (__GNUC__ == 4) || (__GNUC__ > 4 )\n')
+ end
+
+ classClass.register(self, pre)
+
+ if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
+ output('#endif // __GNUC__ >= 4\n')
+ end
+end
+
+
+--function classVirtualClass:requirecollection(_c)
+-- if self.flags.parent_object.flags.pure_virtual then
+-- return false
+-- end
+-- return classClass.requirecollection(self, _c)
+--end
+
+function classVirtualClass:output_parent_methods()
+
+ for k,v in ipairs(self.methods) do
+
+ if v.f.access ~= 2 and (not v.f.pure_virtual) and v.f.name ~= 'new' and v.f.name ~= 'delete' then
+
+ local rettype = v.f.mod.." "..v.f.type..v.f.ptr.." "
+ local parent_name = rettype..self.btype.."__"..v.f.name
+
+ local par_list = self:get_arg_list(v.f, true)
+ local var_list = self:get_arg_list(v.f, false)
+
+ -- the parent's virtual function
+ output("\t"..parent_name..par_list.." {")
+
+ output("\t\treturn (",rettype,")"..self.btype.."::"..v.f.name..var_list..";")
+ output("\t};")
+ end
+ end
+end
+
+function classVirtualClass:output_methods(btype)
+
+ for k,v in ipairs(self.methods) do
+
+ if v.f.name ~= 'new' and v.f.name ~= 'delete' then
+
+ self:output_method(v.f, btype)
+ end
+ end
+ output("\n")
+end
+
+function classVirtualClass:output_constructors()
+
+ for k,v in ipairs(self.methods) do
+
+ if v.f.name == 'new' then
+
+ local par_list = self:get_arg_list(v.f, true)
+ local var_list = self:get_arg_list(v.f, false)
+
+ output("\t",self.original_name,par_list,":",self.btype,var_list,"{};")
+ end
+ end
+end
+
+function classVirtualClass:output_method(f, btype)
+
+ if f.access == 2 then -- private
+ return
+ end
+
+ local ptr
+ if f.ret ~= '' then
+ ptr = f.ret
+ else
+ ptr = f.ptr
+ end
+
+ local rettype = f.mod.." "..f.type..f.ptr.." "
+ local par_list = self:get_arg_list(f, true)
+ local var_list = self:get_arg_list(f, false)
+
+ if string.find(rettype, "%s*LuaQtGenericFlags%s*") then
+
+ _,_,rettype = string.find(f.original_sig, "^%s*([^%s]+)%s+")
+ end
+
+ -- the caller of the lua method
+ output("\t"..rettype.." "..f.name..par_list..f.const.." {")
+ local fn = f.cname
+ if f.access == 1 then
+ fn = "NULL"
+ end
+ output('\t\tif (push_method("',f.lname,'", ',fn,')) {')
+
+ --if f.type ~= 'void' then
+ -- output("\t\t\tint top = lua_gettop(lua_state)-1;")
+ --end
+
+ -- push the parameters
+ local argn = 0
+ for i,arg in ipairs(f.args) do
+ if arg.type ~= 'void' then
+ local t,ct = isbasic(arg.type)
+ if t and t ~= '' then
+ if arg.ret == "*" then
+ t = 'userdata'
+ ct = 'void*'
+ end
+ output("\t\t\ttolua_push"..t.."(lua_state, ("..ct..")"..arg.name..");");
+ else
+ local m = arg.ptr
+ if m and m~= "" then
+ if m == "*" then m = "" end
+ output("\t\t\ttolua_pushusertype(lua_state, (void*)"..m..arg.name..", \""..arg.type.."\");")
+ else
+ output("\t\t\tvoid* tolua_obj" .. argn .." = (void*)new "..arg.type.."("..arg.name..");\n")
+ output('\t\t\ttolua_pushusertype_and_takeownership(lua_state, tolua_obj' .. argn .. ', "'..arg.type..'");\n')
+ end
+ end
+ argn = argn+1
+ end
+ end
+
+ -- call the function
+ output("\t\t\tToluaBase::dbcall(lua_state, ",argn+1,", ")
+
+ -- return value
+ if f.type ~= 'void' then
+ output("1);")
+
+ local t,ct = isbasic(f.type)
+ if t and t ~= '' then
+ --output("\t\t\treturn ("..rettype..")tolua_to"..t.."(lua_state, top, 0);")
+ output("\t\t\t",rettype,"tolua_ret = ("..rettype..")tolua_to"..t.."(lua_state, -1, 0);")
+ else
+
+ local mod = ""
+ if f.ptr ~= "*" then
+ mod = "*("..f.type.."*)"
+ end
+
+ --output("\t\t\treturn ("..rettype..")"..mod.."tolua_tousertype(lua_state, top, 0);")
+ output("\t\t\t",rettype,"tolua_ret = ("..rettype..")"..mod.."tolua_tousertype(lua_state, -1, 0);")
+ end
+ output("\t\t\tlua_pop(lua_state, 1);")
+ output("\t\t\treturn tolua_ret;")
+ else
+ output("0);")
+ end
+
+ -- handle non-implemeted function
+ output("\t\t} else {")
+
+ if f.pure_virtual then
+
+ output('\t\t\tif (lua_state)')
+ --output('\t\t\t\ttolua_error(lua_state, "pure-virtual method '..btype.."::"..f.name..' not implemented.", NULL);')
+ output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' not implemented.");')
+ output('\t\t\telse {')
+ output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' called with no lua_state. Aborting");')
+ output('\t\t\t\t::abort();')
+ output('\t\t\t};')
+ if( rettype == " std::string " ) then
+ output('\t\t\treturn "";')
+ else
+ output('\t\t\treturn (',rettype,')0;')
+ end
+ else
+
+ output('\t\t\treturn (',rettype,')',btype,'::',f.name,var_list,';')
+ end
+
+ output("\t\t};")
+
+ output("\t};")
+end
+
+function VirtualClass()
+
+ local parent = classContainer.curr
+ pop()
+
+ local name = "Lua__"..parent.original_name
+
+ local c = _Class(_Container{name=name, base=parent.name, extra_bases=nil})
+ setmetatable(c, classVirtualClass)
+
+ local ft = getnamespace(c.parent)..c.original_name
+ append_global_type(ft, c)
+
+ push(parent)
+
+ c.flags.parent_object = parent
+ c.methods = {}
+
+ push(c)
+ c:parse("\nvoid tolua__set_instance(_lstate L, lua_Object lo);\n")
+ pop()
+
+ return c
+end
+
+
+
+
+