diff options
Diffstat (limited to 'source')
70 files changed, 2118 insertions, 797 deletions
diff --git a/source/AllToLua.pkg b/source/AllToLua.pkg index 00257e460..6d4a4083a 100644 --- a/source/AllToLua.pkg +++ b/source/AllToLua.pkg @@ -47,6 +47,7 @@ $cfile "BlockEntities/DropSpenserEntity.h" $cfile "BlockEntities/DispenserEntity.h" $cfile "BlockEntities/DropperEntity.h" $cfile "BlockEntities/FurnaceEntity.h" +$cfile "BlockEntities/HopperEntity.h" $cfile "WebAdmin.h" $cfile "WebPlugin.h" $cfile "Root.h" diff --git a/source/Bindings.cpp b/source/Bindings.cpp index c241bad75..54a3e161b 100644 --- a/source/Bindings.cpp +++ b/source/Bindings.cpp @@ -1,6 +1,6 @@ /* ** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 10/13/13 18:01:21. +** Generated automatically by tolua++-1.0.92 on 10/23/13 13:30:23. */ #ifndef __cplusplus @@ -46,6 +46,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S); #include "BlockEntities/DispenserEntity.h" #include "BlockEntities/DropperEntity.h" #include "BlockEntities/FurnaceEntity.h" +#include "BlockEntities/HopperEntity.h" #include "WebAdmin.h" #include "WebPlugin.h" #include "Root.h" @@ -74,9 +75,9 @@ static int tolua_collect_cItem (lua_State* tolua_S) return 0; } -static int tolua_collect_cFurnaceEntity (lua_State* tolua_S) +static int tolua_collect_Vector3f (lua_State* tolua_S) { - cFurnaceEntity* self = (cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); + Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); Mtolua_delete(self); return 0; } @@ -144,9 +145,9 @@ static int tolua_collect_cPickup (lua_State* tolua_S) return 0; } -static int tolua_collect_sWebAdminPage (lua_State* tolua_S) +static int tolua_collect_cItems (lua_State* tolua_S) { - sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); + cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); Mtolua_delete(self); return 0; } @@ -172,9 +173,16 @@ static int tolua_collect_cBoundingBox (lua_State* tolua_S) return 0; } -static int tolua_collect_Vector3f (lua_State* tolua_S) +static int tolua_collect_sWebAdminPage (lua_State* tolua_S) { - Vector3f* self = (Vector3f*) tolua_tousertype(tolua_S,1,0); + sWebAdminPage* self = (sWebAdminPage*) tolua_tousertype(tolua_S,1,0); + Mtolua_delete(self); + return 0; +} + +static int tolua_collect_cHopperEntity (lua_State* tolua_S) +{ + cHopperEntity* self = (cHopperEntity*) tolua_tousertype(tolua_S,1,0); Mtolua_delete(self); return 0; } @@ -186,16 +194,16 @@ static int tolua_collect_Vector3i (lua_State* tolua_S) return 0; } -static int tolua_collect_cIniFile (lua_State* tolua_S) +static int tolua_collect_cFurnaceEntity (lua_State* tolua_S) { - cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); + cFurnaceEntity* self = (cFurnaceEntity*) tolua_tousertype(tolua_S,1,0); Mtolua_delete(self); return 0; } -static int tolua_collect_cItems (lua_State* tolua_S) +static int tolua_collect_cIniFile (lua_State* tolua_S) { - cItems* self = (cItems*) tolua_tousertype(tolua_S,1,0); + cIniFile* self = (cIniFile*) tolua_tousertype(tolua_S,1,0); Mtolua_delete(self); return 0; } @@ -244,7 +252,7 @@ static void tolua_reg_types (lua_State* tolua_S) tolua_usertype(tolua_S,"cHTTPServer::cCallbacks"); tolua_usertype(tolua_S,"cLuaWindow"); tolua_usertype(tolua_S,"cInventory"); - tolua_usertype(tolua_S,"cBoundingBox"); + tolua_usertype(tolua_S,"cHopperEntity"); tolua_usertype(tolua_S,"cBlockEntityWithItems"); tolua_usertype(tolua_S,"cWindow"); tolua_usertype(tolua_S,"cGroup"); @@ -255,24 +263,24 @@ static void tolua_reg_types (lua_State* tolua_S) tolua_usertype(tolua_S,"cBlockArea"); tolua_usertype(tolua_S,"cTracer"); tolua_usertype(tolua_S,"cStringMap"); + tolua_usertype(tolua_S,"cBoundingBox"); tolua_usertype(tolua_S,"cServer"); - tolua_usertype(tolua_S,"Vector3i"); tolua_usertype(tolua_S,"cBlockEntity"); tolua_usertype(tolua_S,"cCriticalSection"); tolua_usertype(tolua_S,"HTTPTemplateRequest"); + tolua_usertype(tolua_S,"Vector3i"); tolua_usertype(tolua_S,"cFile"); tolua_usertype(tolua_S,"std::vector<std::string>"); tolua_usertype(tolua_S,"cClientHandle"); tolua_usertype(tolua_S,"cChatColor"); tolua_usertype(tolua_S,"cWebPlugin"); - tolua_usertype(tolua_S,"cWebAdmin"); tolua_usertype(tolua_S,"cIniFile"); + tolua_usertype(tolua_S,"cWebAdmin"); tolua_usertype(tolua_S,"sWebAdminPage"); - tolua_usertype(tolua_S,"cItem"); tolua_usertype(tolua_S,"cPawn"); tolua_usertype(tolua_S,"cPlayer"); tolua_usertype(tolua_S,"cGroupManager"); - tolua_usertype(tolua_S,"cBlockEntityWindowOwner"); + tolua_usertype(tolua_S,"cItem"); tolua_usertype(tolua_S,"HTTPRequest"); tolua_usertype(tolua_S,"cProjectileEntity"); tolua_usertype(tolua_S,"cItemGrid::cListener"); @@ -18309,23 +18317,6 @@ static int tolua_AllToLua_cDropSpenserEntity_SetRedstonePower00(lua_State* tolua } #endif //#ifndef TOLUA_DISABLE -/* get function: __cBlockEntityWindowOwner__ of class cDropSpenserEntity */ -#ifndef TOLUA_DISABLE_tolua_get_cDropSpenserEntity___cBlockEntityWindowOwner__ -static int tolua_get_cDropSpenserEntity___cBlockEntityWindowOwner__(lua_State* tolua_S) -{ - cDropSpenserEntity* self = (cDropSpenserEntity*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable '__cBlockEntityWindowOwner__'",NULL); -#endif -#ifdef __cplusplus - tolua_pushusertype(tolua_S,(void*)static_cast<cBlockEntityWindowOwner*>(self), "cBlockEntityWindowOwner"); -#else - tolua_pushusertype(tolua_S,(void*)((cBlockEntityWindowOwner*)self), "cBlockEntityWindowOwner"); -#endif - return 1; -} -#endif //#ifndef TOLUA_DISABLE - /* method: new of class cDispenserEntity */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cDispenserEntity_new00 static int tolua_AllToLua_cDispenserEntity_new00(lua_State* tolua_S) @@ -18864,6 +18855,75 @@ static int tolua_AllToLua_cFurnaceEntity_HasFuelTimeLeft00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE +/* method: new of class cHopperEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cHopperEntity_new00 +static int tolua_AllToLua_cHopperEntity_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cHopperEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + { + cHopperEntity* tolua_ret = (cHopperEntity*) Mtolua_new((cHopperEntity)(a_BlockX,a_BlockY,a_BlockZ)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cHopperEntity"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class cHopperEntity */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cHopperEntity_new00_local +static int tolua_AllToLua_cHopperEntity_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cHopperEntity",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0)); + int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0)); + int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0)); + { + cHopperEntity* tolua_ret = (cHopperEntity*) Mtolua_new((cHopperEntity)(a_BlockX,a_BlockY,a_BlockZ)); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"cHopperEntity"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* get function: Name of class HTTPFormData */ #ifndef TOLUA_DISABLE_tolua_get_HTTPFormData_Name static int tolua_get_HTTPFormData_Name(lua_State* tolua_S) @@ -19164,34 +19224,6 @@ static int tolua_set_sWebAdminPage_TabName(lua_State* tolua_S) } #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 - { - { - int tolua_ret = (int) cWebAdmin::GetMemoryUsage(); - tolua_pushnumber(tolua_S,(lua_Number)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: GetPage of class cWebAdmin */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetPage00 static int tolua_AllToLua_cWebAdmin_GetPage00(lua_State* tolua_S) @@ -19303,6 +19335,37 @@ static int tolua_AllToLua_cWebAdmin_GetBaseURL00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE +/* method: GetHTMLEscapedString of class cWebAdmin */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWebAdmin_GetHTMLEscapedString00 +static int tolua_AllToLua_cWebAdmin_GetHTMLEscapedString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(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 + { + const AString a_Input = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + AString tolua_ret = (AString) cWebAdmin::GetHTMLEscapedString(a_Input); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Input); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetHTMLEscapedString'.",&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) @@ -19400,36 +19463,6 @@ static int tolua_AllToLua_cWebPlugin_SafeString00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE -/* get function: m_PrimaryServerVersion of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_get_cRoot_m_PrimaryServerVersion -static int tolua_get_cRoot_m_PrimaryServerVersion(lua_State* tolua_S) -{ - cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_PrimaryServerVersion'",NULL); -#endif - tolua_pushnumber(tolua_S,(lua_Number)self->m_PrimaryServerVersion); - return 1; -} -#endif //#ifndef TOLUA_DISABLE - -/* set function: m_PrimaryServerVersion of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_set_cRoot_m_PrimaryServerVersion -static int tolua_set_cRoot_m_PrimaryServerVersion(lua_State* tolua_S) -{ - cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'm_PrimaryServerVersion'",NULL); - if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) - tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); -#endif - self->m_PrimaryServerVersion = ((int) tolua_tonumber(tolua_S,2,0)) -; - return 0; -} -#endif //#ifndef TOLUA_DISABLE - /* method: Get of class cRoot */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_Get00 static int tolua_AllToLua_cRoot_Get00(lua_State* tolua_S) @@ -29145,50 +29178,186 @@ static int tolua_AllToLua_cLuaWindow_GetContents00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE -/* get function: __cItemGrid of class cLuaWindow */ -#ifndef TOLUA_DISABLE_tolua_get_cLuaWindow___cItemGrid__cListener__ -static int tolua_get_cLuaWindow___cItemGrid__cListener__(lua_State* tolua_S) +/* method: GetMobType of class cMonster */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetMobType00 +static int tolua_AllToLua_cMonster_GetMobType00(lua_State* tolua_S) { - cLuaWindow* self = (cLuaWindow*) tolua_tousertype(tolua_S,1,0); #ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable '__cItemGrid'",NULL); + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cMonster",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else #endif -#ifdef __cplusplus - tolua_pushusertype(tolua_S,(void*)static_cast<cItemGrid::cListener*>(self), "cItemGrid::cListener"); -#else - tolua_pushusertype(tolua_S,(void*)((cItemGrid::cListener*)self), "cItemGrid::cListener"); + { + const cMonster* self = (const cMonster*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMobType'", NULL); #endif + { + cMonster::eType tolua_ret = (cMonster::eType) self->GetMobType(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetMobType'.",&tolua_err); + return 0; +#endif } #endif //#ifndef TOLUA_DISABLE -/* method: GetMobType of class cMonster */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetMobType00 -static int tolua_AllToLua_cMonster_GetMobType00(lua_State* tolua_S) +/* method: GetMobFamily of class cMonster */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetMobFamily00 +static int tolua_AllToLua_cMonster_GetMobFamily00(lua_State* tolua_S) { #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( - !tolua_isusertype(tolua_S,1,"cMonster",0,&tolua_err) || + !tolua_isusertype(tolua_S,1,"const cMonster",0,&tolua_err) || !tolua_isnoobj(tolua_S,2,&tolua_err) ) goto tolua_lerror; else #endif { - cMonster* self = (cMonster*) tolua_tousertype(tolua_S,1,0); + const cMonster* self = (const cMonster*) tolua_tousertype(tolua_S,1,0); #ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMobType'", NULL); + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMobFamily'", NULL); #endif { - int tolua_ret = (int) self->GetMobType(); + cMonster::eFamily tolua_ret = (cMonster::eFamily) self->GetMobFamily(); tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); } } return 1; #ifndef TOLUA_RELEASE tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'GetMobType'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'GetMobFamily'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: MobTypeToString of class cMonster */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_MobTypeToString00 +static int tolua_AllToLua_cMonster_MobTypeToString00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cMonster::eType a_MobType = ((cMonster::eType) (int) tolua_tonumber(tolua_S,2,0)); + { + AString tolua_ret = (AString) cMonster::MobTypeToString(a_MobType); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MobTypeToString'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: StringToMobType of class cMonster */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_StringToMobType00 +static int tolua_AllToLua_cMonster_StringToMobType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const AString a_MobTypeName = ((const AString) tolua_tocppstring(tolua_S,2,0)); + { + cMonster::eType tolua_ret = (cMonster::eType) cMonster::StringToMobType(a_MobTypeName); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_MobTypeName); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'StringToMobType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: FamilyFromType of class cMonster */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_FamilyFromType00 +static int tolua_AllToLua_cMonster_FamilyFromType00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cMonster::eType a_MobType = ((cMonster::eType) (int) tolua_tonumber(tolua_S,2,0)); + { + cMonster::eFamily tolua_ret = (cMonster::eFamily) cMonster::FamilyFromType(a_MobType); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'FamilyFromType'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetSpawnRate of class cMonster */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cMonster_GetSpawnRate00 +static int tolua_AllToLua_cMonster_GetSpawnRate00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cMonster",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cMonster::eFamily a_MobFamily = ((cMonster::eFamily) (int) tolua_tonumber(tolua_S,2,0)); + { + int tolua_ret = (int) cMonster::GetSpawnRate(a_MobFamily); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSpawnRate'.",&tolua_err); return 0; #endif } @@ -30747,7 +30916,6 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"AddDropSpenserDir",tolua_AllToLua_cDropSpenserEntity_AddDropSpenserDir00); tolua_function(tolua_S,"Activate",tolua_AllToLua_cDropSpenserEntity_Activate00); tolua_function(tolua_S,"SetRedstonePower",tolua_AllToLua_cDropSpenserEntity_SetRedstonePower00); - tolua_variable(tolua_S,"__cBlockEntityWindowOwner__",tolua_get_cDropSpenserEntity___cBlockEntityWindowOwner__,NULL); tolua_endmodule(tolua_S); #ifdef __cplusplus tolua_cclass(tolua_S,"cDispenserEntity","cDispenserEntity","cDropSpenserEntity",tolua_collect_cDispenserEntity); @@ -30794,6 +30962,19 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"GetFuelBurnTimeLeft",tolua_AllToLua_cFurnaceEntity_GetFuelBurnTimeLeft00); tolua_function(tolua_S,"HasFuelTimeLeft",tolua_AllToLua_cFurnaceEntity_HasFuelTimeLeft00); tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"cHopperEntity","cHopperEntity","cBlockEntityWithItems",tolua_collect_cHopperEntity); + #else + tolua_cclass(tolua_S,"cHopperEntity","cHopperEntity","cBlockEntityWithItems",NULL); + #endif + tolua_beginmodule(tolua_S,"cHopperEntity"); + tolua_constant(tolua_S,"ContentsHeight",cHopperEntity::ContentsHeight); + tolua_constant(tolua_S,"ContentsWidth",cHopperEntity::ContentsWidth); + tolua_constant(tolua_S,"TICKS_PER_TRANSFER",cHopperEntity::TICKS_PER_TRANSFER); + tolua_function(tolua_S,"new",tolua_AllToLua_cHopperEntity_new00); + tolua_function(tolua_S,"new_local",tolua_AllToLua_cHopperEntity_new00_local); + tolua_function(tolua_S,".call",tolua_AllToLua_cHopperEntity_new00_local); + tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"HTTPFormData","HTTPFormData","",NULL); tolua_beginmodule(tolua_S,"HTTPFormData"); tolua_variable(tolua_S,"Name",tolua_get_HTTPFormData_Name,tolua_set_HTTPFormData_Name); @@ -30822,10 +31003,10 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cWebAdmin","cWebAdmin","cHTTPServer::cCallbacks",NULL); tolua_beginmodule(tolua_S,"cWebAdmin"); - tolua_function(tolua_S,"GetMemoryUsage",tolua_AllToLua_cWebAdmin_GetMemoryUsage00); tolua_function(tolua_S,"GetPage",tolua_AllToLua_cWebAdmin_GetPage00); tolua_function(tolua_S,"GetDefaultPage",tolua_AllToLua_cWebAdmin_GetDefaultPage00); tolua_function(tolua_S,"GetBaseURL",tolua_AllToLua_cWebAdmin_GetBaseURL00); + tolua_function(tolua_S,"GetHTMLEscapedString",tolua_AllToLua_cWebAdmin_GetHTMLEscapedString00); tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cWebPlugin","cWebPlugin","",NULL); tolua_beginmodule(tolua_S,"cWebPlugin"); @@ -30835,7 +31016,6 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cRoot","cRoot","",NULL); tolua_beginmodule(tolua_S,"cRoot"); - tolua_variable(tolua_S,"m_PrimaryServerVersion",tolua_get_cRoot_m_PrimaryServerVersion,tolua_set_cRoot_m_PrimaryServerVersion); tolua_function(tolua_S,"Get",tolua_AllToLua_cRoot_Get00); tolua_function(tolua_S,"GetServer",tolua_AllToLua_cRoot_GetServer00); tolua_function(tolua_S,"GetDefaultWorld",tolua_AllToLua_cRoot_GetDefaultWorld00); @@ -31244,10 +31424,10 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,".call",tolua_AllToLua_cLuaWindow_new00_local); tolua_function(tolua_S,"delete",tolua_AllToLua_cLuaWindow_delete00); tolua_function(tolua_S,"GetContents",tolua_AllToLua_cLuaWindow_GetContents00); - tolua_variable(tolua_S,"__cItemGrid__cListener__",tolua_get_cLuaWindow___cItemGrid__cListener__,NULL); tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cMonster","cMonster","cPawn",NULL); tolua_beginmodule(tolua_S,"cMonster"); + tolua_constant(tolua_S,"mtInvalidType",cMonster::mtInvalidType); tolua_constant(tolua_S,"mtBat",cMonster::mtBat); tolua_constant(tolua_S,"mtBlaze",cMonster::mtBlaze); tolua_constant(tolua_S,"mtCaveSpider",cMonster::mtCaveSpider); @@ -31277,7 +31457,17 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_constant(tolua_S,"mtWolf",cMonster::mtWolf); tolua_constant(tolua_S,"mtZombie",cMonster::mtZombie); tolua_constant(tolua_S,"mtZombiePigman",cMonster::mtZombiePigman); + tolua_constant(tolua_S,"mfHostile",cMonster::mfHostile); + tolua_constant(tolua_S,"mfPassive",cMonster::mfPassive); + tolua_constant(tolua_S,"mfAmbient",cMonster::mfAmbient); + tolua_constant(tolua_S,"mfWater",cMonster::mfWater); + tolua_constant(tolua_S,"mfMaxplusone",cMonster::mfMaxplusone); tolua_function(tolua_S,"GetMobType",tolua_AllToLua_cMonster_GetMobType00); + tolua_function(tolua_S,"GetMobFamily",tolua_AllToLua_cMonster_GetMobFamily00); + tolua_function(tolua_S,"MobTypeToString",tolua_AllToLua_cMonster_MobTypeToString00); + tolua_function(tolua_S,"StringToMobType",tolua_AllToLua_cMonster_StringToMobType00); + tolua_function(tolua_S,"FamilyFromType",tolua_AllToLua_cMonster_FamilyFromType00); + tolua_function(tolua_S,"GetSpawnRate",tolua_AllToLua_cMonster_GetSpawnRate00); tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cLineBlockTracer","cLineBlockTracer","",NULL); tolua_beginmodule(tolua_S,"cLineBlockTracer"); diff --git a/source/Bindings.h b/source/Bindings.h index 1d567520c..620dbea84 100644 --- a/source/Bindings.h +++ b/source/Bindings.h @@ -1,6 +1,6 @@ /*
** Lua binding: AllToLua
-** Generated automatically by tolua++-1.0.92 on 10/13/13 18:01:22.
+** Generated automatically by tolua++-1.0.92 on 10/23/13 13:30:24.
*/
/* Exported function */
diff --git a/source/BlockEntities/DropSpenserEntity.h b/source/BlockEntities/DropSpenserEntity.h index f2f1eba36..0e9039915 100644 --- a/source/BlockEntities/DropSpenserEntity.h +++ b/source/BlockEntities/DropSpenserEntity.h @@ -29,10 +29,10 @@ class cServer; -// tolua_begin -class cDropSpenserEntity : - public cBlockEntityWithItems, - public cBlockEntityWindowOwner +class cDropSpenserEntity : // tolua_export + public cBlockEntityWindowOwner, + // tolua_begin + public cBlockEntityWithItems { typedef cBlockEntityWithItems super; diff --git a/source/BlockEntities/HopperEntity.h b/source/BlockEntities/HopperEntity.h index a49868660..1a7650581 100644 --- a/source/BlockEntities/HopperEntity.h +++ b/source/BlockEntities/HopperEntity.h @@ -38,15 +38,12 @@ public: /// 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 + Returns false if unattached. + Exported in ManualBindings.cpp */ bool GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, int & a_OutputY, int & a_OutputZ); - // tolua_end - static const char * GetClassStatic(void) { return "cHopperEntity"; } protected: @@ -95,7 +92,7 @@ protected: /// Moves one piece to the specified entity's contents' slot. Returns true if contents have changed. bool MoveItemsToSlot(cBlockEntityWithItems & a_Entity, int a_DstSlotNum); -} ; +} ; // tolua_export diff --git a/source/BlockID.cpp b/source/BlockID.cpp index 177652a46..95e1a63bf 100644 --- a/source/BlockID.cpp +++ b/source/BlockID.cpp @@ -79,40 +79,43 @@ public: bool ResolveItem(const AString & a_ItemName, cItem & a_Item) { - ItemMap::iterator itr = m_Map.find(a_ItemName); + // Split into parts divided by either ':' or '^' + AStringVector Split = StringSplitAndTrim(a_ItemName, ":^"); + if (Split.empty()) + { + return false; + } + + ItemMap::iterator itr = m_Map.find(Split[0]); if (itr != m_Map.end()) { + // Resolved as a string, assign the type and the default damage / count a_Item.m_ItemType = itr->second.first; a_Item.m_ItemDamage = itr->second.second; if (a_Item.m_ItemDamage == -1) { a_Item.m_ItemDamage = 0; } - a_Item.m_ItemCount = 1; - return true; - } - - // Not a resolvable string, try pure numbers: "45:6", "45^6" etc. - AStringVector Split = StringSplit(a_ItemName, ":"); - if (Split.size() == 1) - { - Split = StringSplit(a_ItemName, "^"); } - if (Split.empty()) - { - return false; - } - a_Item.m_ItemType = (short)atoi(Split[0].c_str()); - if ((a_Item.m_ItemType == 0) && (Split[0] != "0")) + else { - // Parsing the number failed - return false; + // Not a resolvable string, try pure numbers: "45:6", "45^6" etc. + a_Item.m_ItemType = (short)atoi(Split[0].c_str()); + if ((a_Item.m_ItemType == 0) && (Split[0] != "0")) + { + // Parsing the number failed + return false; + } } + + // Parse the damage, if present: if (Split.size() < 2) { + // Not present, set the item as valid and return success: a_Item.m_ItemCount = 1; return true; } + a_Item.m_ItemDamage = atoi(Split[1].c_str()); if ((a_Item.m_ItemDamage == 0) && (Split[1] != "0")) { @@ -662,6 +665,7 @@ public: g_BlockTransparent[E_BLOCK_GLASS_PANE] = true; g_BlockTransparent[E_BLOCK_ICE] = true; g_BlockTransparent[E_BLOCK_IRON_DOOR] = true; + g_BlockTransparent[E_BLOCK_LAVA] = true; g_BlockTransparent[E_BLOCK_LEAVES] = true; g_BlockTransparent[E_BLOCK_LEVER] = true; g_BlockTransparent[E_BLOCK_MELON_STEM] = true; @@ -674,11 +678,14 @@ public: g_BlockTransparent[E_BLOCK_RED_MUSHROOM] = true; g_BlockTransparent[E_BLOCK_RED_ROSE] = true; g_BlockTransparent[E_BLOCK_SIGN_POST] = true; + g_BlockTransparent[E_BLOCK_STATIONARY_LAVA] = true; + g_BlockTransparent[E_BLOCK_STATIONARY_WATER] = true; g_BlockTransparent[E_BLOCK_STONE_PRESSURE_PLATE] = true; g_BlockTransparent[E_BLOCK_SNOW] = true; g_BlockTransparent[E_BLOCK_TALL_GRASS] = true; g_BlockTransparent[E_BLOCK_TORCH] = true; g_BlockTransparent[E_BLOCK_VINES] = true; + g_BlockTransparent[E_BLOCK_WATER] = true; g_BlockTransparent[E_BLOCK_WALLSIGN] = true; g_BlockTransparent[E_BLOCK_WOODEN_DOOR] = true; g_BlockTransparent[E_BLOCK_WOODEN_PRESSURE_PLATE] = true; diff --git a/source/Chunk.cpp b/source/Chunk.cpp index db533f642..c7bac879a 100644 --- a/source/Chunk.cpp +++ b/source/Chunk.cpp @@ -30,6 +30,9 @@ #include "PluginManager.h" #include "Blocks/BlockHandler.h" #include "Simulator/FluidSimulator.h" +#include "MobCensus.h" +#include "MobSpawner.h" + #include <json/json.h> @@ -429,6 +432,133 @@ void cChunk::Stay(bool a_Stay) +void cChunk::CollectMobCensus(cMobCensus& toFill) +{ + toFill.CollectSpawnableChunk(*this); + std::list<const Vector3d*> playerPositions; + cPlayer* currentPlayer; + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr) + { + currentPlayer = (*itr)->GetPlayer(); + playerPositions.push_back(&(currentPlayer->GetPosition())); + } + + Vector3d currentPosition; + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) + { + //LOGD("Counting entity #%i (%s)", (*itr)->GetUniqueID(), (*itr)->GetClass()); + if ((*itr)->IsMob()) + { + cMonster& Monster = (cMonster&)(**itr); + currentPosition = Monster.GetPosition(); + for (std::list<const Vector3d*>::const_iterator itr2 = playerPositions.begin(); itr2 != playerPositions.end(); itr2 ++) + { + toFill.CollectMob(Monster,*this,(currentPosition-**itr2).SqrLength()); + } + } + } // for itr - m_Entitites[] +} + + + + +void cChunk::getThreeRandomNumber(int& a_X, int& a_Y, int& a_Z,int a_MaxX, int a_MaxY, int a_MaxZ) +{ + ASSERT(a_MaxX * a_MaxY * a_MaxZ * 8 < 0x00ffffff); + int Random = m_World->GetTickRandomNumber(0x00ffffff); + a_X = Random % (a_MaxX * 2); + a_Y = (Random / (a_MaxX * 2)) % (a_MaxY * 2); + a_Z = ((Random / (a_MaxX * 2)) / (a_MaxY * 2)) % (a_MaxZ * 2); + a_X /= 2; + a_Y /= 2; + a_Z /= 2; +} + + + + + +void cChunk::getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z) +{ + // MG TODO : check if this kind of optimization (only one random call) is still needed + // MG TODO : if so propagate it + + getThreeRandomNumber(a_X, a_Y, a_Z, Width, Height-2, Width); + a_Y++; +} + + + + + +void cChunk::SpawnMobs(cMobSpawner& a_MobSpawner) +{ + int Center_X,Center_Y,Center_Z; + getRandomBlockCoords(Center_X,Center_Y,Center_Z); + + BLOCKTYPE PackCenterBlock = GetBlock(Center_X, Center_Y, Center_Z); + if (a_MobSpawner.CheckPackCenter(PackCenterBlock)) + { + a_MobSpawner.NewPack(); + int NumberOfTries = 0; + int NumberOfSuccess = 0; + int MaxNbOfSuccess = 4; // this can be changed during the process for Wolves and Ghass + while (NumberOfTries < 12 && NumberOfSuccess < MaxNbOfSuccess) + { + const int HorizontalRange = 20; // MG TODO : relocate + const int VerticalRange = 0; // MG TODO : relocate + int Try_X, Try_Y, Try_Z; + getThreeRandomNumber(Try_X, Try_Y, Try_Z, 2*HorizontalRange+1 , 2*VerticalRange+1 , 2*HorizontalRange+1); + Try_X -= HorizontalRange; + Try_Y -= VerticalRange; + Try_Z -= HorizontalRange; + Try_X += Center_X; + Try_Y += Center_Y; + Try_Z += Center_Z; + + ASSERT(Try_Y > 0); + ASSERT(Try_Y < cChunkDef::Height-1); + + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + BLOCKTYPE BlockType_below; + NIBBLETYPE BlockMeta_below; + BLOCKTYPE BlockType_above; + NIBBLETYPE BlockMeta_above; + if (UnboundedRelGetBlock(Try_X, Try_Y , Try_Z, BlockType, BlockMeta) && + UnboundedRelGetBlock(Try_X, Try_Y-1, Try_Z, BlockType_below, BlockMeta_below)&& + UnboundedRelGetBlock(Try_X, Try_Y+1, Try_Z, BlockType_above, BlockMeta_above) + ) + { + EMCSBiome Biome = m_ChunkMap->GetBiomeAt (Try_X, Try_Z); + // MG TODO : + // Moon cycle (for slime) + // check player and playerspawn presence < 24 blocks + // check mobs presence on the block + + // MG TODO: fix the "light" thing, I'm pretty sure that UnboundedRelGetBlock s not returning the right thing + + // MG TODO : check that "Level" really means Y + cEntity* newMob = a_MobSpawner.TryToSpawnHere(BlockType, BlockMeta, BlockType_below, BlockMeta_below, BlockType_above, BlockMeta_above, Biome, Try_Y, MaxNbOfSuccess); + if (newMob) + { + int WorldX, WorldY, WorldZ; + PositionToWorldPosition(Try_X, Try_Y, Try_Z, WorldX, WorldY, WorldZ); + newMob->SetPosition(WorldX, WorldY, WorldZ); + LOGD("Spawning %s #%i at %d,%d,%d",newMob->GetClass(),newMob->GetUniqueID(),WorldX, WorldY, WorldZ); + NumberOfSuccess++; + } + } + + NumberOfTries++; + } + } + +} + + + + void cChunk::Tick(float a_Dt) { @@ -457,10 +587,14 @@ void cChunk::Tick(float a_Dt) m_IsDirty = (*itr)->Tick(a_Dt, *this) | m_IsDirty; } - // Tick all entities in this chunk: + // Tick all entities in this chunk (except mobs): for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) { - (*itr)->Tick(a_Dt, *this); + // Mobs are tickes inside MobTick (as we don't have to tick them if they are far away from players) + if (!((*itr)->IsMob())) + { + (*itr)->Tick(a_Dt, *this); + } } // for itr - m_Entitites[] // Remove all entities that were scheduled for removal: @@ -923,45 +1057,14 @@ bool cChunk::UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); return false; } - - // Is it in this chunk? - if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width)) - { - if (!IsValid()) - { - return false; - } - int BlockIdx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); - a_BlockType = GetBlock(BlockIdx); - a_BlockMeta = GetMeta(BlockIdx); - return true; - } - - // Not in this chunk, try walking the neighbors first: - if ((a_RelX < 0) && (m_NeighborXM != NULL)) - { - return m_NeighborXM->UnboundedRelGetBlock(a_RelX + cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); - } - if ((a_RelX >= cChunkDef::Width) && (m_NeighborXP != NULL)) - { - return m_NeighborXP->UnboundedRelGetBlock(a_RelX - cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); - } - if ((a_RelZ < 0) && (m_NeighborZM != NULL)) - { - return m_NeighborZM->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ + cChunkDef::Width, a_BlockType, a_BlockMeta); - } - if ((a_RelZ >= cChunkDef::Width) && (m_NeighborZP != NULL)) + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) { - return m_NeighborZP->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ - cChunkDef::Width, a_BlockType, a_BlockMeta); + // The chunk is not available, bail out + return false; } - - // Neighbors not available, use the chunkmap to locate the chunk: - return m_ChunkMap->LockedGetBlock( - m_PosX * cChunkDef::Width + a_RelX, - ZERO_CHUNK_Y * cChunkDef::Height + a_RelY, - m_PosZ * cChunkDef::Width + a_RelZ, - a_BlockType, a_BlockMeta - ); + Chunk->GetBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + return true; } @@ -975,44 +1078,14 @@ bool cChunk::UnboundedRelGetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKT LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); return false; } - - // Is it in this chunk? - if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width)) - { - if (!IsValid()) - { - return false; - } - int BlockIdx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); - a_BlockType = GetBlock(BlockIdx); - return true; - } - - // Not in this chunk, try walking the neighbors first: - if ((a_RelX < 0) && (m_NeighborXM != NULL)) - { - return m_NeighborXM->UnboundedRelGetBlockType(a_RelX + cChunkDef::Width, a_RelY, a_RelZ, a_BlockType); - } - if ((a_RelX >= cChunkDef::Width) && (m_NeighborXP != NULL)) + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) { - return m_NeighborXP->UnboundedRelGetBlockType(a_RelX - cChunkDef::Width, a_RelY, a_RelZ, a_BlockType); - } - if ((a_RelZ < 0) && (m_NeighborZM != NULL)) - { - return m_NeighborZM->UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ + cChunkDef::Width, a_BlockType); - } - if ((a_RelZ >= cChunkDef::Width) && (m_NeighborZP != NULL)) - { - return m_NeighborZP->UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ - cChunkDef::Width, a_BlockType); + // The chunk is not available, bail out + return false; } - - // Neighbors not available, use the chunkmap to locate the chunk: - return m_ChunkMap->LockedGetBlockType( - m_PosX * cChunkDef::Width + a_RelX, - ZERO_CHUNK_Y * cChunkDef::Height + a_RelY, - m_PosZ * cChunkDef::Width + a_RelZ, - a_BlockType - ); + a_BlockType = Chunk->GetBlock(a_RelX, a_RelY, a_RelZ); + return true; } @@ -1026,44 +1099,56 @@ bool cChunk::UnboundedRelGetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLE LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); return false; } - - // Is it in this chunk? - if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width)) + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) { - if (!IsValid()) - { - return false; - } - int BlockIdx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); - a_BlockMeta = GetMeta(BlockIdx); - return true; + // The chunk is not available, bail out + return false; } + a_BlockMeta = Chunk->GetMeta(a_RelX, a_RelY, a_RelZ); + return true; +} + + - // Not in this chunk, try walking the neighbors first: - if ((a_RelX < 0) && (m_NeighborXM != NULL)) + + +bool cChunk::UnboundedRelGetBlockBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockBlockLight) const +{ + if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) { - return m_NeighborXM->UnboundedRelGetBlockMeta(a_RelX + cChunkDef::Width, a_RelY, a_RelZ, a_BlockMeta); + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); + return false; } - if ((a_RelX >= cChunkDef::Width) && (m_NeighborXP != NULL)) + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) { - return m_NeighborXP->UnboundedRelGetBlockMeta(a_RelX - cChunkDef::Width, a_RelY, a_RelZ, a_BlockMeta); + // The chunk is not available, bail out + return false; } - if ((a_RelZ < 0) && (m_NeighborZM != NULL)) + a_BlockBlockLight = Chunk->GetBlockLight(a_RelX, a_RelY, a_RelZ); + return true; +} + + + + + +bool cChunk::UnboundedRelGetBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockSkyLight) const +{ + if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height)) { - return m_NeighborZM->UnboundedRelGetBlockMeta(a_RelX, a_RelY, a_RelZ + cChunkDef::Width, a_BlockMeta); + LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelY); + return false; } - if ((a_RelZ >= cChunkDef::Width) && (m_NeighborZP != NULL)) + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) { - return m_NeighborZP->UnboundedRelGetBlockMeta(a_RelX, a_RelY, a_RelZ - cChunkDef::Width, a_BlockMeta); + // The chunk is not available, bail out + return false; } - - // Neighbors not available, use the chunkmap to locate the chunk: - return m_ChunkMap->LockedGetBlockMeta( - m_PosX * cChunkDef::Width + a_RelX, - ZERO_CHUNK_Y * cChunkDef::Height + a_RelY, - m_PosZ * cChunkDef::Width + a_RelZ, - a_BlockMeta - ); + a_BlockSkyLight = Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ); + return true; } @@ -1077,44 +1162,15 @@ bool cChunk::UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE LOGWARNING("UnboundedRelSetBlock(): requesting a block with a_RelY out of range: %d", a_RelY); return false; } - - // Is it in this chunk? - if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width)) + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) { - if (!IsValid()) - { - return false; - } - SetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); - return true; - } - - // Not in this chunk, try walking the neighbors first: - if ((a_RelX < 0) && (m_NeighborXM != NULL)) - { - return m_NeighborXM->UnboundedRelSetBlock(a_RelX + cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); - } - if ((a_RelX >= cChunkDef::Width) && (m_NeighborXP != NULL)) - { - return m_NeighborXP->UnboundedRelSetBlock(a_RelX - cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); - } - if ((a_RelZ < 0) && (m_NeighborZM != NULL)) - { - return m_NeighborZM->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ + cChunkDef::Width, a_BlockType, a_BlockMeta); - } - if ((a_RelZ >= cChunkDef::Width) && (m_NeighborZP != NULL)) - { - return m_NeighborZP->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ - cChunkDef::Width, a_BlockType, a_BlockMeta); + // The chunk is not available, bail out + return false; } - - // Neighbors not available, use the chunkmap to locate the chunk: - return m_ChunkMap->LockedSetBlock( - m_PosX * cChunkDef::Width + a_RelX, - ZERO_CHUNK_Y * cChunkDef::Height + a_RelY, - m_PosZ * cChunkDef::Width + a_RelZ, - a_BlockType, a_BlockMeta - ); -} + Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + return true; +} @@ -1127,43 +1183,14 @@ bool cChunk::UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKT LOGWARNING("UnboundedRelFastSetBlock(): requesting a block with a_RelY out of range: %d", a_RelY); return false; } - - // Is it in this chunk? - if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width)) - { - if (!IsValid()) - { - return false; - } - FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); - return true; - } - - // Not in this chunk, try walking the neighbors first: - if ((a_RelX < 0) && (m_NeighborXM != NULL)) - { - return m_NeighborXM->UnboundedRelFastSetBlock(a_RelX + cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); - } - if ((a_RelX >= cChunkDef::Width) && (m_NeighborXP != NULL)) + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk == NULL) || !Chunk->IsValid()) { - return m_NeighborXP->UnboundedRelFastSetBlock(a_RelX - cChunkDef::Width, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); - } - if ((a_RelZ < 0) && (m_NeighborZM != NULL)) - { - return m_NeighborZM->UnboundedRelFastSetBlock(a_RelX, a_RelY, a_RelZ + cChunkDef::Width, a_BlockType, a_BlockMeta); - } - if ((a_RelZ >= cChunkDef::Width) && (m_NeighborZP != NULL)) - { - return m_NeighborZP->UnboundedRelFastSetBlock(a_RelX, a_RelY, a_RelZ - cChunkDef::Width, a_BlockType, a_BlockMeta); + // The chunk is not available, bail out + return false; } - - // Neighbors not available, use the chunkmap to locate the chunk: - return m_ChunkMap->LockedFastSetBlock( - m_PosX * cChunkDef::Width + a_RelX, - ZERO_CHUNK_Y * cChunkDef::Height + a_RelY, - m_PosZ * cChunkDef::Width + a_RelZ, - a_BlockType, a_BlockMeta - ); + Chunk->FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + return true; } @@ -1177,44 +1204,18 @@ void cChunk::UnboundedQueueTickBlock(int a_RelX, int a_RelY, int a_RelZ) // Outside of chunkmap return; } - - // Is it in this chunk? - if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width)) - { - QueueTickBlock(a_RelX, a_RelY, a_RelZ); - return; - } - - // Not in this chunk, try walking the neighbors first: - if ((a_RelX < 0) && (m_NeighborXM != NULL)) - { - m_NeighborXM->UnboundedQueueTickBlock(a_RelX + cChunkDef::Width, a_RelY, a_RelZ); - return; - } - if ((a_RelX >= cChunkDef::Width) && (m_NeighborXP != NULL)) - { - m_NeighborXP->UnboundedQueueTickBlock(a_RelX - cChunkDef::Width, a_RelY, a_RelZ); - return; - } - if ((a_RelZ < 0) && (m_NeighborZM != NULL)) - { - m_NeighborZM->UnboundedQueueTickBlock(a_RelX, a_RelY, a_RelZ + cChunkDef::Width); - return; - } - if ((a_RelZ >= cChunkDef::Width) && (m_NeighborZP != NULL)) + cChunk * Chunk = GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((Chunk != NULL) && Chunk->IsValid()) { - m_NeighborZP->UnboundedQueueTickBlock(a_RelX, a_RelY, a_RelZ - cChunkDef::Width); - return; + Chunk->QueueTickBlock(a_RelX, a_RelY, a_RelZ); } - - // Neighbors not available, ignore altogether } -int cChunk::GetHeight( int a_X, int a_Z ) +int cChunk::GetHeight(int a_X, int a_Z) { ASSERT((a_X >= 0) && (a_X < Width) && (a_Z >= 0) && (a_Z < Width)); @@ -2340,66 +2341,58 @@ cChunk * cChunk::GetRelNeighborChunk(int a_RelX, int a_RelZ) -cChunk * cChunk::GetRelNeighborChunkAdjustCoords(int & a_RelX, int & a_RelZ) +cChunk * cChunk::GetRelNeighborChunkAdjustCoords(int & a_RelX, int & a_RelZ) const { - bool ReturnThis = true; - int RelX = a_RelX; + cChunk * ToReturn = const_cast<cChunk *>(this); + + // The most common case: inside this chunk: + if ( + (a_RelX >= 0) && (a_RelX < Width) && + (a_RelZ >= 0) && (a_RelZ < Width) + ) + { + return ToReturn; + } + + // Request for a different chunk, calculate chunk offset: + int RelX = a_RelX; // Make a local copy of the coords (faster access) int RelZ = a_RelZ; - if (a_RelX < 0) + while ((RelX >= Width) && (ToReturn != NULL)) { - if (m_NeighborXM != NULL) - { - RelX = a_RelX + cChunkDef::Width; - cChunk * Candidate = m_NeighborXM->GetRelNeighborChunkAdjustCoords(RelX, RelZ); - if (Candidate != NULL) - { - a_RelX = RelX; - a_RelZ = RelZ; - return Candidate; - } - } - // Going X-first failed, but if the request is crossing Z as well, let's try the Z-first later on. - ReturnThis = false; + RelX -= Width; + ToReturn = ToReturn->m_NeighborXP; } - else if (a_RelX >= cChunkDef::Width) + while ((RelX < 0) && (ToReturn != NULL)) { - if (m_NeighborXP != NULL) - { - RelX = a_RelX - cChunkDef::Width; - cChunk * Candidate = m_NeighborXP->GetRelNeighborChunkAdjustCoords(RelX, RelZ); - if (Candidate != NULL) - { - a_RelX = RelX; - a_RelZ = RelZ; - return Candidate; - } - } - // Going X-first failed, but if the request is crossing Z as well, let's try the Z-first later on. - ReturnThis = false; + RelX += Width; + ToReturn = ToReturn->m_NeighborXM; } - - if (a_RelZ < 0) + while ((RelZ >= Width) && (ToReturn != NULL)) { - if (m_NeighborZM != NULL) - { - a_RelZ += cChunkDef::Width; - return m_NeighborZM->GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); - // For requests crossing both X and Z, the X-first way has been already tried - } - return NULL; + RelZ -= Width; + ToReturn = ToReturn->m_NeighborZP; } - else if (a_RelZ >= cChunkDef::Width) + while ((RelZ < 0) && (ToReturn != NULL)) { - if (m_NeighborZP != NULL) - { - a_RelZ -= cChunkDef::Width; - return m_NeighborZP->GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); - // For requests crossing both X and Z, the X-first way has been already tried - } - return NULL; + RelZ += Width; + ToReturn = ToReturn->m_NeighborZM; + } + if (ToReturn != NULL) + { + a_RelX = RelX; + a_RelZ = RelZ; + return ToReturn; } - return (ReturnThis ? this : NULL); + // The chunk cannot be walked through neighbors, find it through the chunkmap: + int AbsX = a_RelX + m_PosX * Width; + int AbsZ = a_RelZ + m_PosZ * Width; + int DstChunkX, DstChunkZ; + BlockToChunk(AbsX, AbsZ, DstChunkX, DstChunkZ); + ToReturn = m_ChunkMap->FindChunk(DstChunkX, DstChunkZ); + a_RelX = AbsX - DstChunkX * Width; + a_RelZ = AbsZ - DstChunkZ * Width; + return ToReturn; } diff --git a/source/Chunk.h b/source/Chunk.h index c979b7928..e709a4718 100644 --- a/source/Chunk.h +++ b/source/Chunk.h @@ -49,6 +49,8 @@ class cPickup; class cChunkDataSerializer; class cBlockArea; class cFluidSimulatorData; +class cMobCensus; +class cMobSpawner; typedef std::list<cClientHandle *> cClientHandleList; typedef cItemCallback<cEntity> cEntityCallback; @@ -124,6 +126,12 @@ public: /// Sets or resets the internal flag that prevents chunk from being unloaded void Stay(bool a_Stay = true); + /// Recence all mobs proximities to players in order to know what to do with them + void CollectMobCensus(cMobCensus& toFill); + + /// Try to Spawn Monsters inside chunk + void SpawnMobs(cMobSpawner& a_MobSpawner); + void Tick(float a_Dt); int GetPosX(void) const { return m_PosX; } @@ -163,11 +171,12 @@ public: cChunk * GetRelNeighborChunk(int a_RelX, int a_RelZ); /** - Returns the chunk into which the relatively-specified block belongs, by walking the neighbors. + Returns the chunk into which the relatively-specified block belongs. Also modifies the relative coords from this-relative to return-relative. - Will return self if appropriate. Returns NULL if not reachable through neighbors. + Will return self if appropriate. + Will try walking the neighbors first; if that fails, will query the chunkmap */ - cChunk * GetRelNeighborChunkAdjustCoords(int & a_RelX, int & a_RelZ); + cChunk * GetRelNeighborChunkAdjustCoords(int & a_RelX, int & a_RelZ) const; EMCSBiome GetBiomeAt(int a_RelX, int a_RelZ) const {return cChunkDef::GetBiome(m_BiomeMap, a_RelX, a_RelZ); } @@ -291,19 +300,25 @@ public: inline NIBBLETYPE GetBlockLight(int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockLight, a_RelX, a_RelY, a_RelZ); } inline NIBBLETYPE GetSkyLight (int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_RelX, a_RelY, a_RelZ); } - /// Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success; only usable in Tick() + /// Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success bool UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const; - /// Same as GetBlockType(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success; only usable in Tick() + /// Same as GetBlockType(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success bool UnboundedRelGetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType) const; - /// Same as GetBlockMeta(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success; only usable in Tick() + /// Same as GetBlockMeta(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success bool UnboundedRelGetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockMeta) const; - /// Same as SetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success; only usable in Tick() + /// Same as GetBlockBlockLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success + bool UnboundedRelGetBlockBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight) const; + + /// Same as GetBlockSkyLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success + bool UnboundedRelGetBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_SkyLight) const; + + /// Same as SetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success bool UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - /// Same as FastSetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success; only usable in Tick() + /// Same as FastSetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success bool UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); /// Same as QueueTickBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s in such a case), ignores unsuccessful attempts @@ -385,6 +400,10 @@ private: cSandSimulatorChunkData m_SandSimulatorData; + // pick up a random block of this chunk + void getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z); + void getThreeRandomNumber(int& a_X, int& a_Y, int& a_Z,int a_MaxX, int a_MaxY, int a_MaxZ); + void RemoveBlockEntity(cBlockEntity * a_BlockEntity); void AddBlockEntity (cBlockEntity * a_BlockEntity); diff --git a/source/ChunkDef.h b/source/ChunkDef.h index 9db88f293..d6630df7e 100644 --- a/source/ChunkDef.h +++ b/source/ChunkDef.h @@ -208,10 +208,15 @@ public: inline static unsigned int MakeIndex(int x, int y, int z ) { - if( x < cChunkDef::Width && x > -1 && y < cChunkDef::Height && y > -1 && z < cChunkDef::Width && z > -1 ) + if ( + (x < Width) && (x > -1) && + (y < Height) && (y > -1) && + (z < Width) && (z > -1) + ) { return MakeIndexNoCheck(x, y, z); } + ASSERT(!"cChunkDef::MakeIndex(): coords out of chunk range!"); return INDEX_OUT_OF_RANGE; } @@ -256,6 +261,7 @@ public: inline static void SetBlock(BLOCKTYPE * a_BlockTypes, int a_Index, BLOCKTYPE a_Type) { + ASSERT((a_Index >= 0) && (a_Index <= NumBlocks)); a_BlockTypes[a_Index] = a_Type; } @@ -271,41 +277,50 @@ public: inline static BLOCKTYPE GetBlock(const BLOCKTYPE * a_BlockTypes, int a_Idx) { - ASSERT((a_Idx >= 0) && (a_Idx < Width * Width * Height)); + ASSERT((a_Idx >= 0) && (a_Idx < NumBlocks)); return a_BlockTypes[a_Idx]; } inline static int GetHeight(const HeightMap & a_HeightMap, int a_X, int a_Z) { + ASSERT((a_X >= 0) && (a_X <= Width)); + ASSERT((a_Z >= 0) && (a_Z <= Width)); return a_HeightMap[a_X + Width * a_Z]; } inline static void SetHeight(HeightMap & a_HeightMap, int a_X, int a_Z, unsigned char a_Height) { + ASSERT((a_X >= 0) && (a_X <= Width)); + ASSERT((a_Z >= 0) && (a_Z <= Width)); a_HeightMap[a_X + Width * a_Z] = a_Height; } inline static EMCSBiome GetBiome(const BiomeMap & a_BiomeMap, int a_X, int a_Z) { + ASSERT((a_X >= 0) && (a_X <= Width)); + ASSERT((a_Z >= 0) && (a_Z <= Width)); return a_BiomeMap[a_X + Width * a_Z]; } inline static void SetBiome(BiomeMap & a_BiomeMap, int a_X, int a_Z, EMCSBiome a_Biome) { + ASSERT((a_X >= 0) && (a_X <= Width)); + ASSERT((a_Z >= 0) && (a_Z <= Width)); a_BiomeMap[a_X + Width * a_Z] = a_Biome; } static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, int a_BlockIdx) { - if ((a_BlockIdx > -1) && (a_BlockIdx < cChunkDef::NumBlocks)) + if ((a_BlockIdx > -1) && (a_BlockIdx < NumBlocks)) { return (a_Buffer[a_BlockIdx / 2] >> ((a_BlockIdx & 1) * 4)) & 0x0f; } + ASSERT(!"cChunkDef::GetNibble(): index out of chunk range!"); return 0; } @@ -317,38 +332,48 @@ public: int Index = MakeIndexNoCheck(x, y, z); return (a_Buffer[Index / 2] >> ((Index & 1) * 4)) & 0x0f; } + ASSERT(!"cChunkDef::GetNibble(): coords out of chunk range!"); return 0; } static void SetNibble(NIBBLETYPE * a_Buffer, int a_BlockIdx, NIBBLETYPE a_Nibble) { - if ((a_BlockIdx > -1) && (a_BlockIdx < cChunkDef::NumBlocks)) + if ((a_BlockIdx < 0) || (a_BlockIdx >= NumBlocks)) { - a_Buffer[a_BlockIdx / 2] = ( - (a_Buffer[a_BlockIdx / 2] & (0xf0 >> ((a_BlockIdx & 1) * 4))) | // The untouched nibble - ((a_Nibble & 0x0f) << ((a_BlockIdx & 1) * 4)) // The nibble being set - ); + ASSERT(!"cChunkDef::SetNibble(): index out of range!"); + return; } + a_Buffer[a_BlockIdx / 2] = ( + (a_Buffer[a_BlockIdx / 2] & (0xf0 >> ((a_BlockIdx & 1) * 4))) | // The untouched nibble + ((a_Nibble & 0x0f) << ((a_BlockIdx & 1) * 4)) // The nibble being set + ); } static void SetNibble(NIBBLETYPE * a_Buffer, int x, int y, int z, NIBBLETYPE a_Nibble) { - if ((x < cChunkDef::Width) && (x > -1) && (y < cChunkDef::Height) && (y > -1) && (z < cChunkDef::Width) && (z > -1)) + if ( + (x >= Width) || (x < 0) || + (y >= Height) || (y < 0) || + (z >= Width) || (z < 0) + ) { - int Index = MakeIndexNoCheck(x, y, z); - a_Buffer[Index / 2] = ( - (a_Buffer[Index / 2] & (0xf0 >> ((Index & 1) * 4))) | // The untouched nibble - ((a_Nibble & 0x0f) << ((Index & 1) * 4)) // The nibble being set - ); + ASSERT(!"cChunkDef::SetNibble(): index out of range!"); + return; } + + int Index = MakeIndexNoCheck(x, y, z); + a_Buffer[Index / 2] = ( + (a_Buffer[Index / 2] & (0xf0 >> ((Index & 1) * 4))) | // The untouched nibble + ((a_Nibble & 0x0f) << ((Index & 1) * 4)) // The nibble being set + ); } inline static char GetNibble(const NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos ) { - return GetNibble( a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z ); + return GetNibble(a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z ); } diff --git a/source/ChunkMap.cpp b/source/ChunkMap.cpp index 3c098fdfe..c3bd5f33d 100644 --- a/source/ChunkMap.cpp +++ b/source/ChunkMap.cpp @@ -12,6 +12,8 @@ #include "BlockArea.h" #include "PluginManager.h" #include "Entities/TNTEntity.h" +#include "MobCensus.h" +#include "MobSpawner.h" #ifndef _WIN32 #include <cstdlib> // abs @@ -2152,6 +2154,32 @@ void cChunkMap::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ) +void cChunkMap::CollectMobCensus(cMobCensus& a_ToFill) +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + (*itr)->CollectMobCensus(a_ToFill); + } // for itr - m_Layers +} + + + + + + +void cChunkMap::SpawnMobs(cMobSpawner& a_MobSpawner) +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + (*itr)->SpawnMobs(a_MobSpawner); + } // for itr - m_Layers +} + + + + void cChunkMap::Tick(float a_Dt) { @@ -2310,6 +2338,38 @@ cChunk * cChunkMap::cChunkLayer::FindChunk(int a_ChunkX, int a_ChunkZ) +void cChunkMap::cChunkLayer::CollectMobCensus(cMobCensus& a_ToFill) +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + // We do count every Mobs in the world. But we are assuming that every chunk not loaded by any client + // doesn't affect us. Normally they should not have mobs because every "too far" mobs despawn + // If they have (f.i. when player disconnect) we assume we don't have to make them live or despawn + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients()) + { + m_Chunks[i]->CollectMobCensus(a_ToFill); + } + } // for i - m_Chunks[] +} + + + + + + +void cChunkMap::cChunkLayer::SpawnMobs(cMobSpawner& a_MobSpawner) +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + // We only spawn close to players + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients()) + { + m_Chunks[i]->SpawnMobs(a_MobSpawner); + } + } // for i - m_Chunks[] +} + + void cChunkMap::cChunkLayer::Tick(float a_Dt) { diff --git a/source/ChunkMap.h b/source/ChunkMap.h index fcb164f7b..f68cb6472 100644 --- a/source/ChunkMap.h +++ b/source/ChunkMap.h @@ -26,6 +26,8 @@ class cPawn; class cPickup; class cChunkDataSerializer; class cBlockArea; +class cMobCensus; +class cMobSpawner; typedef std::list<cClientHandle *> cClientHandleList; typedef cChunk * cChunkPtr; @@ -263,9 +265,15 @@ public: /// Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config void GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); - /// Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call + /// Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ); + /// Make a Mob census, of all mobs, their family, their chunk and theyr distance to closest player + void CollectMobCensus(cMobCensus& a_ToFill); + + /// Try to Spawn Monsters inside all Chunks + void SpawnMobs(cMobSpawner& a_MobSpawner); + void Tick(float a_Dt); void UnloadUnusedChunks(void); @@ -309,6 +317,11 @@ private: void Save(void); void UnloadUnusedChunks(void); + /// Collect a mob census, of all mobs, their megatype, their chunk and their distance o closest player + void CollectMobCensus(cMobCensus& a_ToFill); + /// Try to Spawn Monsters inside all Chunks + void SpawnMobs(cMobSpawner& a_MobSpawner); + void Tick(float a_Dt); void RemoveClient(cClientHandle * a_Client); diff --git a/source/Globals.h b/source/Globals.h index 1e531f7f3..ef79e4cf1 100644 --- a/source/Globals.h +++ b/source/Globals.h @@ -12,24 +12,24 @@ #if defined(_MSC_VER) // MSVC produces warning C4481 on the override keyword usage, so disable the warning altogether #pragma warning(disable:4481) - + // Disable some warnings that we don't care about: #pragma warning(disable:4100) #define OBSOLETE __declspec(deprecated) - + // No alignment needed in MSVC #define ALIGN_8 #define ALIGN_16 - + #elif defined(__GNUC__) // TODO: Can GCC explicitly mark classes as abstract (no instances can be created)? #define abstract - + // TODO: Can GCC mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class) #define override - + #define OBSOLETE __attribute__((deprecated)) #define ALIGN_8 __attribute__((aligned(8))) @@ -41,13 +41,13 @@ #else #error "You are using an unsupported compiler, you might need to #define some stuff here for your compiler" - + /* // Copy and uncomment this into another #elif section based on your compiler identification - + // Explicitly mark classes as abstract (no instances can be created) #define abstract - + // Mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class) #define override @@ -92,17 +92,17 @@ typedef unsigned short UInt16; // OS-dependent stuff: #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN - + #define _WIN32_WINNT 0x501 // We want to target WinXP and higher - + #include <Windows.h> #include <winsock2.h> #include <Ws2tcpip.h> // IPv6 stuff - + // Windows SDK defines min and max macros, messing up with our std::min and std::max usage #undef min #undef max - + // Windows SDK defines GetFreeSpace as a constant, probably a Win16 API remnant #ifdef GetFreeSpace #undef GetFreeSpace diff --git a/source/Inventory.cpp b/source/Inventory.cpp index c104db4c7..d5fc7f0d8 100644 --- a/source/Inventory.cpp +++ b/source/Inventory.cpp @@ -374,7 +374,7 @@ bool cInventory::DamageItem(int a_SlotNum, short a_Amount) } // The item has broken, remove it: - Grid->EmptySlot(a_SlotNum); + Grid->EmptySlot(GridSlotNum); return true; } diff --git a/source/LuaWindow.h b/source/LuaWindow.h index 5a0685ebb..4c32c263e 100644 --- a/source/LuaWindow.h +++ b/source/LuaWindow.h @@ -23,8 +23,6 @@ class cPluginLua; -// 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 @@ -35,9 +33,10 @@ Additionally, to forbid Lua from deleting this object while it is used by player 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 +class cLuaWindow : // tolua_export + public cItemGrid::cListener, + // tolua_begin + public cWindow { typedef cWindow super; diff --git a/source/ManualBindings.cpp b/source/ManualBindings.cpp index e6605ddb0..466701bf8 100644 --- a/source/ManualBindings.cpp +++ b/source/ManualBindings.cpp @@ -17,6 +17,7 @@ #include "BlockEntities/DispenserEntity.h" #include "BlockEntities/DropperEntity.h" #include "BlockEntities/FurnaceEntity.h" +#include "BlockEntities/HopperEntity.h" #include "md5/md5.h" #include "LuaWindow.h" #include "LineBlockTracer.h" @@ -2059,6 +2060,45 @@ static int tolua_cLineBlockTracer_Trace(lua_State * tolua_S) +static int tolua_cHopperEntity_GetOutputBlockPos(lua_State * tolua_S) +{ + // function cHopperEntity::GetOutputBlockPos() + // Exported manually because tolua would require meaningless params + + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cHopperEntity") || + !L.CheckParamNumber (2) || + !L.CheckParamEnd (3) + ) + { + return 0; + } + cHopperEntity * self = (cHopperEntity *)tolua_tousertype(tolua_S, 1, 0); + if (self == NULL) + { + tolua_error(tolua_S, "invalid 'self' in function 'cHopperEntity::GetOutputBlockPos()'", NULL); + return 0; + } + + NIBBLETYPE a_BlockMeta = ((NIBBLETYPE)tolua_tonumber(tolua_S, 2, 0)); + int a_OutputX, a_OutputY, a_OutputZ; + bool res = self->GetOutputBlockPos(a_BlockMeta, a_OutputX, a_OutputY, a_OutputZ); + tolua_pushboolean(tolua_S, res); + if (res) + { + tolua_pushnumber(tolua_S, (lua_Number)a_OutputX); + tolua_pushnumber(tolua_S, (lua_Number)a_OutputY); + tolua_pushnumber(tolua_S, (lua_Number)a_OutputZ); + return 4; + } + return 1; +} + + + + + void ManualBindings::Bind(lua_State * tolua_S) { tolua_beginmodule(tolua_S, NULL); @@ -2070,6 +2110,10 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "LOGWARNING", tolua_LOGWARN); tolua_function(tolua_S, "LOGERROR", tolua_LOGERROR); + tolua_beginmodule(tolua_S, "cHopperEntity"); + tolua_function(tolua_S, "GetOutputBlockPos", tolua_cHopperEntity_GetOutputBlockPos); + tolua_endmodule(tolua_S); + tolua_beginmodule(tolua_S, "cLineBlockTracer"); tolua_function(tolua_S, "Trace", tolua_cLineBlockTracer_Trace); tolua_endmodule(tolua_S); diff --git a/source/MobCensus.cpp b/source/MobCensus.cpp new file mode 100644 index 000000000..66b5932bc --- /dev/null +++ b/source/MobCensus.cpp @@ -0,0 +1,92 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "MobCensus.h" + + + + + +void cMobCensus::CollectMob(cMonster & a_Monster, cChunk & a_Chunk, double a_Distance) +{ + m_ProximityCounter.CollectMob(a_Monster, a_Chunk, a_Distance); + m_MobFamilyCollecter.CollectMob(a_Monster); +} + + + + + +bool cMobCensus::IsCapped(cMonster::eFamily a_MobFamily) +{ + bool toReturn = true; + const int ratio = 319; // this should be 256 as we are only supposed to take account from chunks that are in 17x17 from a player + // but for now, we use all chunks loaded by players. that means 19 x 19 chunks. That's why we use 256 * (19*19) / (17*17) = 319 + // MG TODO : code the correct count + if ((GetCapMultiplier(a_MobFamily) * GetNumChunks()) / ratio >= m_MobFamilyCollecter.GetNumberOfCollectedMobs(a_MobFamily)) + { + return false; + } + return true; +} + + + + + +int cMobCensus::GetCapMultiplier(cMonster::eFamily a_MobFamily) +{ + switch (a_MobFamily) + { + case cMonster::mfHostile: return 79; + case cMonster::mfPassive: return 11; + case cMonster::mfAmbient: return 16; + case cMonster::mfWater: return 5; + } + ASSERT(!"Unhandled mob family"); + return -1; +} + + + + + +void cMobCensus::CollectSpawnableChunk(cChunk & a_Chunk) +{ + m_EligibleForSpawnChunks.insert(&a_Chunk); +} + + + + + +int cMobCensus::GetNumChunks(void) +{ + return m_EligibleForSpawnChunks.size(); +} + + + + + +cMobProximityCounter & cMobCensus::GetProximityCounter(void) +{ + return m_ProximityCounter; +} + + + + + +void cMobCensus::Logd() +{ + LOGD("Hostile mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfHostile), IsCapped(cMonster::mfHostile) ? "(capped)" : ""); + LOGD("Ambient mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfAmbient), IsCapped(cMonster::mfAmbient) ? "(capped)" : ""); + LOGD("Water mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfWater), IsCapped(cMonster::mfWater) ? "(capped)" : ""); + LOGD("Passive mobs : %d %s", m_MobFamilyCollecter.GetNumberOfCollectedMobs(cMonster::mfPassive), IsCapped(cMonster::mfPassive) ? "(capped)" : ""); +} + + + + + diff --git a/source/MobCensus.h b/source/MobCensus.h new file mode 100644 index 000000000..e3892bec6 --- /dev/null +++ b/source/MobCensus.h @@ -0,0 +1,59 @@ + +#pragma once + +#include "MobProximityCounter.h" +#include "MobFamilyCollecter.h" + + + + +// fwd: +class cChunk; +class cMonster; + + + + + +/** This class is used to collect information, for each Mob, what is the distance of the closest player +it was first being designed in order to make mobs spawn / despawn / act +as the behaviour and even life of mobs depends on the distance to closest player + +as side effect : it also collect the chunks that are elligible for spawning +as side effect 2 : it also know the caps for mobs number and can compare census to this numbers +*/ +class cMobCensus +{ +public: + /// Returns the nested proximity counter + cMobProximityCounter & GetProximityCounter(void); + + // collect an elligible Chunk for Mob Spawning + // MG TODO : code the correct rule (not loaded chunk but short distant from players) + void CollectSpawnableChunk(cChunk & a_Chunk); + + /// Collect a mob - it's distance to player, it's family ... + void CollectMob(cMonster& a_Monster, cChunk& a_Chunk, double a_Distance); + + /// Returns true if the family is capped (i.e. there are more mobs of this family than max) + bool IsCapped(cMonster::eFamily a_MobFamily); + + /// log the results of census to server console + void Logd(void); + +protected : + cMobProximityCounter m_ProximityCounter; + cMobFamilyCollecter m_MobFamilyCollecter; + + std::set<cChunk *> m_EligibleForSpawnChunks; + + /// Returns the number of chunks that are elligible for spawning (for now, the loaded, valid chunks) + int GetNumChunks(); + + /// Returns the cap multiplier value of the given monster family + static int GetCapMultiplier(cMonster::eFamily a_MobFamily); +} ; + + + + diff --git a/source/MobFamilyCollecter.cpp b/source/MobFamilyCollecter.cpp new file mode 100644 index 000000000..e9c69e078 --- /dev/null +++ b/source/MobFamilyCollecter.cpp @@ -0,0 +1,26 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "MobFamilyCollecter.h" +#include "Mobs/Monster.h" + + + +void cMobFamilyCollecter::CollectMob(cMonster & a_Monster) +{ + cMonster::eFamily MobFamily = a_Monster.GetMobFamily(); + m_Mobs[MobFamily].insert(&a_Monster); +} + + + + + +int cMobFamilyCollecter::GetNumberOfCollectedMobs(cMonster::eFamily a_Family) +{ + return m_Mobs[a_Family].size(); +} + + + + diff --git a/source/MobFamilyCollecter.h b/source/MobFamilyCollecter.h new file mode 100644 index 000000000..6cef133b5 --- /dev/null +++ b/source/MobFamilyCollecter.h @@ -0,0 +1,39 @@ + +#pragma once + +#include <map> +#include <set> +#include "BlockID.h" +#include "Mobs/Monster.h" //this is a side-effect of keeping Mobfamily inside Monster class. I'd prefer to keep both (Mobfamily and Monster) inside a "Monster" namespace MG TODO : do it + + + + +// fwd: +class cChunk; + + + + + +/** This class is used to collect the list of mobs for each family +*/ +class cMobFamilyCollecter +{ +public : + typedef const std::set<cMonster::eFamily> tMobFamilyList; + + // collect a mob + void CollectMob(cMonster & a_Monster); + + // return the number of mobs for this family + int GetNumberOfCollectedMobs(cMonster::eFamily a_Family); + +protected : + std::map<cMonster::eFamily, std::set<cMonster *> > m_Mobs; + +} ; + + + + diff --git a/source/MobProximityCounter.cpp b/source/MobProximityCounter.cpp new file mode 100644 index 000000000..583a71579 --- /dev/null +++ b/source/MobProximityCounter.cpp @@ -0,0 +1,83 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "MobProximityCounter.h" + +#include "Entities/Entity.h" +#include "Chunk.h" + +void cMobProximityCounter::CollectMob(cEntity& a_Monster, cChunk& a_Chunk, double a_Distance) +{ +// LOGD("Collecting monster %s, with distance %f",a_Monster->GetClass(),a_Distance); + tMonsterToDistance::iterator it = m_MonsterToDistance.find(&a_Monster); + if (it == m_MonsterToDistance.end()) + { + sDistanceAndChunk newDistanceAndChunk(a_Distance,a_Chunk); + std::pair<tMonsterToDistance::iterator,bool> result = m_MonsterToDistance.insert(tMonsterToDistance::value_type(&a_Monster,newDistanceAndChunk)); + if (!result.second) + { + ASSERT(!"A collected Monster was not found inside distance map using find(), but insert() said there already is a key for it"); + } + } + else + { + if (a_Distance < it->second.m_Distance) + { + it->second.m_Distance = a_Distance; + it->second.m_Chunk = a_Chunk; + } + } + + m_EligibleForSpawnChunks.insert(&a_Chunk); + +} + +void cMobProximityCounter::convertMaps() +{ + for(tMonsterToDistance::const_iterator itr = m_MonsterToDistance.begin(); itr != m_MonsterToDistance.end(); itr++) + { + m_DistanceToMonster.insert(tDistanceToMonster::value_type(itr->second.m_Distance,sMonsterAndChunk(*itr->first,itr->second.m_Chunk))); + } +} + +cMobProximityCounter::sIterablePair cMobProximityCounter::getMobWithinThosesDistances(double a_DistanceMin, double a_DistanceMax) +{ + sIterablePair toReturn; + toReturn.m_Count = 0; + toReturn.m_Begin = m_DistanceToMonster.end(); + toReturn.m_End = m_DistanceToMonster.end(); + + a_DistanceMin *= a_DistanceMin;// this is because is use square distance + a_DistanceMax *= a_DistanceMax; + + if (m_DistanceToMonster.size() <= 0) + { + convertMaps(); + } + + for(tDistanceToMonster::const_iterator itr = m_DistanceToMonster.begin(); itr != m_DistanceToMonster.end(); itr++) + { + if (toReturn.m_Begin == m_DistanceToMonster.end()) + { + if (a_DistanceMin == -1 || itr->first > a_DistanceMin) + { + toReturn.m_Begin = itr; // this is the first one with distance > a_DistanceMin; + } + } + + if (toReturn.m_Begin != m_DistanceToMonster.end()) + { + if (a_DistanceMax != -1 && itr->first > a_DistanceMax) + { + toReturn.m_End = itr; // this is just after the last one with distance < a_DistanceMax + // Note : if we are not going through this, it's ok, toReturn.m_End will be end(); + break; + } + else + { + toReturn.m_Count ++; + } + } + } + return toReturn; +} diff --git a/source/MobProximityCounter.h b/source/MobProximityCounter.h new file mode 100644 index 000000000..8a67139aa --- /dev/null +++ b/source/MobProximityCounter.h @@ -0,0 +1,65 @@ + +#pragma once + +#include <set> + +class cChunk; +class cEntity; + + +// This class is used to collect, for each Mob, what is the distance of the closest player +// it was first being designed in order to make mobs spawn / despawn / act +// as the behaviour and even life of mobs depends on the distance to closest player +class cMobProximityCounter +{ +protected : + // structs used for later maps (see m_MonsterToDistance and m_DistanceToMonster) + struct sDistanceAndChunk + { + sDistanceAndChunk(double a_Distance, cChunk& a_Chunk) : m_Distance(a_Distance), m_Chunk(a_Chunk) {} + double m_Distance; + cChunk& m_Chunk; + }; + struct sMonsterAndChunk + { + sMonsterAndChunk(cEntity& a_Monster, cChunk& a_Chunk) : m_Monster(a_Monster), m_Chunk(a_Chunk) {} + cEntity& m_Monster; + cChunk& m_Chunk; + }; + +public : + typedef std::map<cEntity*,sDistanceAndChunk> tMonsterToDistance; + typedef std::multimap<double,sMonsterAndChunk> tDistanceToMonster; + +protected : + // this map is filled during collection phase, it will be later transformed into DistanceToMonster + tMonsterToDistance m_MonsterToDistance; + + // this map is generated after collection phase, in order to access monster by distance to player + tDistanceToMonster m_DistanceToMonster; + + // this are the collected chunks. Used to determinate the number of elligible chunk for spawning. + std::set<cChunk*> m_EligibleForSpawnChunks; + +protected : + // transform monsterToDistance map (that was usefull for collecting) into distanceToMonster + // that will be usefull for picking up. + void convertMaps(); + +public : + // count a mob on a specified chunk with specified distance to an unkown player + // if the distance is shortest than the one collected, this become the new closest + // distance and the chunk become the "hosting" chunk (that is the one that will perform the action) + void CollectMob(cEntity& a_Monster, cChunk& a_Chunk, double a_Distance); + + // return the mobs that are within the range of distance of the closest player they are + // that means that if a mob is 30 m from a player and 150 m from another one. It will be + // in the range [0..50] but not in [100..200] + struct sIterablePair{ + tDistanceToMonster::const_iterator m_Begin; + tDistanceToMonster::const_iterator m_End; + int m_Count; + }; + sIterablePair getMobWithinThosesDistances(double a_DistanceMin, double a_DistanceMax); + +}; diff --git a/source/MobSpawner.cpp b/source/MobSpawner.cpp new file mode 100644 index 000000000..1b3796f70 --- /dev/null +++ b/source/MobSpawner.cpp @@ -0,0 +1,270 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "MobSpawner.h" +#include "Mobs/IncludeAllMonsters.h" + + + + + +cMobSpawner::cMobSpawner(cMonster::eFamily a_MonsterFamily,const std::set<cMonster::eType>& a_AllowedTypes) : + m_MonsterFamily(a_MonsterFamily), + m_NewPack(true), + m_MobType(cMonster::mtInvalidType) +{ + for (std::set<cMonster::eType>::const_iterator itr = a_AllowedTypes.begin(); itr != a_AllowedTypes.end(); itr++) + { + if (cMonster::FamilyFromType(*itr) == a_MonsterFamily) + { + m_AllowedTypes.insert(*itr); + } + } +} + + + + + +bool cMobSpawner::CheckPackCenter(BLOCKTYPE a_BlockType) +{ + // Packs of non-water mobs can only be centered on an air block + // Packs of water mobs can only be centered on a water block + if (m_MonsterFamily == cMonster::mfWater) + { + return IsBlockWater(a_BlockType); + } + else + { + return a_BlockType == E_BLOCK_AIR; + } +} + + + + + +void cMobSpawner::addIfAllowed(cMonster::eType toAdd, std::set<cMonster::eType>& toAddIn) +{ + std::set<cMonster::eType>::iterator itr = m_AllowedTypes.find(toAdd); + if (itr != m_AllowedTypes.end()) + { + toAddIn.insert(toAdd); + } +} + + + + + +cMonster::eType cMobSpawner::ChooseMobType(EMCSBiome a_Biome) +{ + std::set<cMonster::eType> allowedMobs; + + if (a_Biome == biMushroomIsland || a_Biome == biMushroomShore) + { + addIfAllowed(cMonster::mtMooshroom, allowedMobs); + } + else if (a_Biome == biNether) + { + addIfAllowed(cMonster::mtGhast, allowedMobs); + addIfAllowed(cMonster::mtZombiePigman, allowedMobs); + addIfAllowed(cMonster::mtMagmaCube, allowedMobs); + } + /*else if (a_Biome == biEnder) MG TODO : figure out what are the biomes of the ender + { + addIfAllowed(cMonster::mtEnderman, allowedMobs); + }*/ + else + { + addIfAllowed(cMonster::mtBat, allowedMobs); + addIfAllowed(cMonster::mtSpider, allowedMobs); + addIfAllowed(cMonster::mtZombie, allowedMobs); + addIfAllowed(cMonster::mtSkeleton, allowedMobs); + addIfAllowed(cMonster::mtCreeper, allowedMobs); + addIfAllowed(cMonster::mtSquid, allowedMobs); + + if (a_Biome != biDesert && a_Biome != biBeach && a_Biome != biOcean) + { + addIfAllowed(cMonster::mtSheep, allowedMobs); + addIfAllowed(cMonster::mtPig, allowedMobs); + addIfAllowed(cMonster::mtCow, allowedMobs); + addIfAllowed(cMonster::mtChicken, allowedMobs); + addIfAllowed(cMonster::mtEnderman, allowedMobs); + addIfAllowed(cMonster::mtSlime, allowedMobs); // MG TODO : much more complicated rule + + if (a_Biome == biForest || a_Biome == biForestHills || a_Biome == biTaiga || a_Biome == biTaigaHills) + { + addIfAllowed(cMonster::mtWolf, allowedMobs); + } + else if (a_Biome == biJungle || a_Biome == biJungleHills) + { + addIfAllowed(cMonster::mtOcelot, allowedMobs); + } + } + } + + int allowedMobsSize = allowedMobs.size(); + if (allowedMobsSize > 0) + { + std::set<cMonster::eType>::iterator itr = allowedMobs.begin(); + int iRandom = m_Random.NextInt(allowedMobsSize,a_Biome); + + for(int i = 0; i < iRandom; i++) + { + itr++; + } + + return *itr; + } + return cMonster::mtInvalidType; +} + + + + + +bool cMobSpawner::CanSpawnHere(cMonster::eType a_MobType, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, BLOCKTYPE a_BlockType_below, NIBBLETYPE a_BlockMeta_below, BLOCKTYPE a_BlockType_above, NIBBLETYPE a_BlockMeta_above, EMCSBiome a_Biome, int a_Level) +{ + bool toReturn = false; + std::set<cMonster::eType>::iterator itr = m_AllowedTypes.find(a_MobType); + if (itr != m_AllowedTypes.end()) + { + // MG TODO : find a nicer paging + if (a_MobType == cMonster::mtSquid) + { + toReturn = ( + IsBlockLiquid(a_BlockType) && + a_Level >= 45 && + a_Level <= 62 + ); + } + else if (a_MobType == cMonster::mtBat) + { + toReturn = a_Level <= 60; // MG TODO : find a real rule + } + else + { + if ( + a_BlockType == E_BLOCK_AIR && + a_BlockType_above == E_BLOCK_AIR && + ! (g_BlockTransparent[a_BlockType_below]) + ) + { + if (a_MobType == cMonster::mtChicken || a_MobType == cMonster::mtPig || a_MobType == cMonster::mtCow || a_MobType == cMonster::mtSheep) + { + toReturn = ( + a_BlockType_below == E_BLOCK_GRASS /*&& // MG TODO + a_LightLevel >= 9 */ + ); + } + else if (a_MobType == cMonster::mtOcelot) + { + toReturn = ( + a_Level >= 62 && + ( + a_BlockType_below == E_BLOCK_GRASS || + a_BlockType_below == E_BLOCK_LEAVES + ) && + m_Random.NextInt(3,a_Biome) != 0 + ); + } + else if (a_MobType == cMonster::mtCreeper || a_MobType == cMonster::mtSkeleton || a_MobType == cMonster::mtZombie || a_MobType == cMonster::mtSpider || a_MobType == cMonster::mtEnderman || a_MobType == cMonster::mtZombiePigman) + { + toReturn = true /*a_LightLevel <= 7 MG TODO*/; + /*if (a_SunLight) MG TODO + { + if (m_Random.NextInt(2,a_Biome) != 0) + { + toReturn = false; + } + }*/ + } + else if (a_MobType == cMonster::mtSlime) + { + toReturn = a_Level <= 40; + // MG TODO : much more complicated rules + } + else if (a_MobType == cMonster::mtGhast) + { + toReturn = m_Random.NextInt(20,a_Biome) == 0; + } + else + { + LOGD("MG TODO : check I've got a Rule to write for type %d",a_MobType); + toReturn = true; + } + } + } + } + return toReturn; +} + + + + + +cMonster* cMobSpawner::TryToSpawnHere(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, BLOCKTYPE a_BlockType_below, NIBBLETYPE a_BlockMeta_below, BLOCKTYPE a_BlockType_above, NIBBLETYPE a_BlockMeta_above, EMCSBiome a_Biome, int a_Level, int& a_MaxPackSize) +{ + cMonster* toReturn = NULL; + if (m_NewPack) + { + m_MobType = ChooseMobType(a_Biome); + if (m_MobType == cMonster::mtInvalidType) + { + return toReturn; + } + if (m_MobType == cMonster::mtWolf) + { + a_MaxPackSize = 8; + } + else if (m_MobType == cMonster::mtGhast) + { + a_MaxPackSize = 1; + } + m_NewPack = false; + } + + + if (CanSpawnHere(m_MobType, a_BlockType, a_BlockMeta, a_BlockType_below, a_BlockMeta_below, a_BlockType_above, a_BlockMeta_above, a_Biome, a_Level)) + { + cMonster * newMob = cMonster::NewMonsterFromType(m_MobType); + if (newMob) + { + m_Spawned.insert(newMob); + } + toReturn = newMob; + } + return toReturn; +} + + + + + +void cMobSpawner::NewPack() +{ + m_NewPack = true; +} + + + + + +cMobSpawner::tSpawnedContainer & cMobSpawner::getSpawned(void) +{ + return m_Spawned; +} + + + + + +bool cMobSpawner::CanSpawnAnything(void) +{ + return !m_AllowedTypes.empty(); +} + + + + diff --git a/source/MobSpawner.h b/source/MobSpawner.h new file mode 100644 index 000000000..ba2a18f2e --- /dev/null +++ b/source/MobSpawner.h @@ -0,0 +1,75 @@ + +#pragma once + +#include <set> +#include "BlockID.h" +#include "ChunkDef.h" +#include "FastRandom.h" +#include "Mobs/Monster.h" //this is a side-effect of keeping Mobfamily inside Monster class. I'd prefer to keep both (Mobfamily and Monster) inside a "Monster" namespace MG TODO : do it + + + + +// fwd: +class cChunk; + + + + + +/** This class is used to determine which monster can be spawned in which place +it is essentially static (eg. Squids spawn in water, Zombies spawn in dark places) +but it also has dynamic part depending on the world.ini settings. +*/ +class cMobSpawner +{ +public : + // constructor + // a_MobFamily is the Family of mobs that this spawner will spawn + // a_AllowedTypes is the set of types allowed for mobs it will spawn. Empty set + // would result in no spawn at all + // Allowed mobs thah are not of the right Family will not be include (no warning) + cMobSpawner(cMonster::eFamily MobFamily, const std::set<cMonster::eType> & a_AllowedTypes); + + /// Check if specified block can be a Pack center for this spawner + bool CheckPackCenter(BLOCKTYPE a_BlockType); + + // Try to create a monster here + // if this is the first of a Pack : determine the type of monster + // BlockType & BlockMeta are used to decide what kind of Mob can Spawn here + // MaxPackSize is set to the maximal size for a pack this type of mob + cMonster * TryToSpawnHere(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, BLOCKTYPE a_BlockType_below, NIBBLETYPE a_BlockMeta_below, BLOCKTYPE a_BlockType_above, NIBBLETYPE a_BlockMeta_above, EMCSBiome a_Biome, int a_Level, int& a_MaxPackSize); + + // mark the beginning of a new Pack + // all mobs of the same Pack are the same type + void NewPack(void); + + // return true if there is at least one allowed type + bool CanSpawnAnything(void); + + typedef const std::set<cMonster *> tSpawnedContainer; + tSpawnedContainer & getSpawned(void); + +protected : + // return true if specified type of mob can spawn on specified block + bool CanSpawnHere(cMonster::eType a_MobType, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, BLOCKTYPE a_BlockType_below, NIBBLETYPE a_BlockMeta_below, BLOCKTYPE a_BlockType_above, NIBBLETYPE a_BlockMeta_above, EMCSBiome a_Biome, int a_Level); + + // return a random type that can spawn on specified biome. + // returns E_ENTITY_TYPE_DONOTUSE if none is possible + cMonster::eType ChooseMobType(EMCSBiome a_Biome); + + // add toAdd inside toAddIn, if toAdd is in m_AllowedTypes + void addIfAllowed(cMonster::eType toAdd, std::set<cMonster::eType> & toAddIn); + +protected : + cMonster::eFamily m_MonsterFamily; + std::set<cMonster::eType> m_AllowedTypes; + bool m_NewPack; + cMonster::eType m_MobType; + std::set<cMonster*> m_Spawned; + cFastRandom m_Random; +} ; + + + + diff --git a/source/Mobs/AggressiveMonster.cpp b/source/Mobs/AggressiveMonster.cpp index 2eae772d7..ee6252656 100644 --- a/source/Mobs/AggressiveMonster.cpp +++ b/source/Mobs/AggressiveMonster.cpp @@ -12,8 +12,8 @@ -cAggressiveMonster::cAggressiveMonster(const AString & a_ConfigName, char a_ProtocolMobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) : - super(a_ConfigName, a_ProtocolMobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height), +cAggressiveMonster::cAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) : + super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height), m_ChaseTime(999999) { m_EMPersonality = AGGRESSIVE; @@ -95,5 +95,3 @@ void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk) } - - diff --git a/source/Mobs/AggressiveMonster.h b/source/Mobs/AggressiveMonster.h index 1eff1831e..5a0d93f3d 100644 --- a/source/Mobs/AggressiveMonster.h +++ b/source/Mobs/AggressiveMonster.h @@ -13,12 +13,13 @@ class cAggressiveMonster : typedef cMonster super; public: - cAggressiveMonster(const AString & a_ConfigName, char a_ProtocolMobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); + cAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); virtual void Tick (float a_Dt, cChunk & a_Chunk) override; virtual void InStateChasing(float a_Dt) override; virtual void EventSeePlayer(cEntity *) override; + protected: float m_ChaseTime; diff --git a/source/Mobs/Bat.cpp b/source/Mobs/Bat.cpp new file mode 100644 index 000000000..b9c82996b --- /dev/null +++ b/source/Mobs/Bat.cpp @@ -0,0 +1,15 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Bat.h" +#include "../Vector3d.h" +#include "../Chunk.h" + + +cBat::cBat(void) : + // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here + super("Bat", mtBat, "mob.bat.hurt", "mob.bat.death", 0.7, 0.7) +{ +} + + diff --git a/source/Mobs/Bat.h b/source/Mobs/Bat.h index 0b50e06cd..e878d0ee8 100644 --- a/source/Mobs/Bat.h +++ b/source/Mobs/Bat.h @@ -13,13 +13,10 @@ class cBat : typedef cPassiveMonster super; public: - cBat(void) : - super("Bat", 65, "mob.bat.hurt", "mob.bat.death", 0.5, 0.9) - { - } + cBat(void); CLASS_PROTODEF(cBat); - + bool IsHanging(void) const {return false; } } ; diff --git a/source/Mobs/Blaze.cpp b/source/Mobs/Blaze.cpp index dbbccf417..74c82c081 100644 --- a/source/Mobs/Blaze.cpp +++ b/source/Mobs/Blaze.cpp @@ -9,7 +9,7 @@ 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) + super("Blaze", mtBlaze, "mob.blaze.hit", "mob.blaze.death", 0.7, 1.8) { } diff --git a/source/Mobs/Cavespider.cpp b/source/Mobs/Cavespider.cpp index 2d50b391a..aba1ff9f5 100644 --- a/source/Mobs/Cavespider.cpp +++ b/source/Mobs/Cavespider.cpp @@ -9,7 +9,7 @@ cCavespider::cCavespider(void) : - super("Cavespider", 59, "mob.spider.say", "mob.spider.death", 0.7, 0.5) + super("Cavespider", mtCaveSpider, "mob.spider.say", "mob.spider.death", 0.7, 0.5) { } diff --git a/source/Mobs/Chicken.cpp b/source/Mobs/Chicken.cpp index 3da9781d3..434a32f94 100644 --- a/source/Mobs/Chicken.cpp +++ b/source/Mobs/Chicken.cpp @@ -14,7 +14,7 @@ cChicken::cChicken(void) : - super("Chicken", 93, "mob.chicken.hurt", "mob.chicken.hurt", 0.3, 0.4) + super("Chicken", mtChicken, "mob.chicken.hurt", "mob.chicken.hurt", 0.3, 0.4) { } diff --git a/source/Mobs/Cow.cpp b/source/Mobs/Cow.cpp index 431a6916d..9eb74dac2 100644 --- a/source/Mobs/Cow.cpp +++ b/source/Mobs/Cow.cpp @@ -2,6 +2,7 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Cow.h" +#include "../Entities/Player.h" @@ -10,7 +11,7 @@ cCow::cCow(void) : - super("Cow", 92, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3) + super("Cow", mtCow, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3) { } @@ -24,6 +25,10 @@ void cCow::GetDrops(cItems & a_Drops, cEntity * a_Killer) AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF); } + + + + void cCow::OnRightClicked(cPlayer & a_Player) { if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_BUCKET)) @@ -31,9 +36,8 @@ void cCow::OnRightClicked(cPlayer & a_Player) if (!a_Player.IsGameModeCreative()) { a_Player.GetInventory().RemoveOneEquippedItem(); - a_Player.GetInventory().AddItem(E_ITEM_MILK) + a_Player.GetInventory().AddItem(E_ITEM_MILK); } - } } diff --git a/source/Mobs/Creeper.cpp b/source/Mobs/Creeper.cpp index b41b05f42..4e11ae13e 100644 --- a/source/Mobs/Creeper.cpp +++ b/source/Mobs/Creeper.cpp @@ -9,7 +9,7 @@ cCreeper::cCreeper(void) : - super("Creeper", 50, "mob.creeper.say", "mob.creeper.say", 0.6, 1.8), + super("Creeper", mtCreeper, "mob.creeper.say", "mob.creeper.say", 0.6, 1.8), m_bIsBlowing(false), m_bIsCharged(false) { diff --git a/source/Mobs/EnderDragon.cpp b/source/Mobs/EnderDragon.cpp index 64f2bedfa..acd81cde1 100644 --- a/source/Mobs/EnderDragon.cpp +++ b/source/Mobs/EnderDragon.cpp @@ -9,7 +9,7 @@ cEnderDragon::cEnderDragon(void) : // TODO: Vanilla source says this, but is it right? Dragons fly, they don't stand - super("EnderDragon", 63, "mob.enderdragon.hit", "mob.enderdragon.end", 16.0, 8.0) + super("EnderDragon", mtEnderDragon, "mob.enderdragon.hit", "mob.enderdragon.end", 16.0, 8.0) { } diff --git a/source/Mobs/Enderman.cpp b/source/Mobs/Enderman.cpp index c0ea3d6aa..a784131e4 100644 --- a/source/Mobs/Enderman.cpp +++ b/source/Mobs/Enderman.cpp @@ -8,7 +8,7 @@ cEnderman::cEnderman(void) : - super("Enderman", 58, "mob.endermen.hit", "mob.endermen.death", 0.5, 2.9), + super("Enderman", mtEnderman, "mob.endermen.hit", "mob.endermen.death", 0.5, 2.9), m_bIsScreaming(false), CarriedBlock(E_BLOCK_AIR), CarriedMeta(0) diff --git a/source/Mobs/Ghast.cpp b/source/Mobs/Ghast.cpp index 288d0c28a..419c8474d 100644 --- a/source/Mobs/Ghast.cpp +++ b/source/Mobs/Ghast.cpp @@ -8,7 +8,7 @@ cGhast::cGhast(void) : - super("Ghast", 56, "mob.ghast.scream", "mob.ghast.death", 4, 4) + super("Ghast", mtGhast, "mob.ghast.scream", "mob.ghast.death", 4, 4) { } diff --git a/source/Mobs/Giant.cpp b/source/Mobs/Giant.cpp index a02758a43..f41977535 100644 --- a/source/Mobs/Giant.cpp +++ b/source/Mobs/Giant.cpp @@ -9,7 +9,7 @@ cGiant::cGiant(void) : // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here - super("Giant", 53, "mob.zombie.hurt", "mob.zombie.death", 2.0, 13.5) + super("Giant", mtGiant, "mob.zombie.hurt", "mob.zombie.death", 2.0, 13.5) { } diff --git a/source/Mobs/Horse.cpp b/source/Mobs/Horse.cpp index c2a8f6ed0..f9705a451 100644 --- a/source/Mobs/Horse.cpp +++ b/source/Mobs/Horse.cpp @@ -10,7 +10,7 @@ cHorse::cHorse(int Type, int Color, int Style, int TameTimes) : - super("Horse", 100, "mob.horse.hit", "mob.horse.death", 1.4, 1.6), + super("Horse", mtHorse, "mob.horse.hit", "mob.horse.death", 1.4, 1.6), m_bHasChest(false), m_bIsEating(false), m_bIsRearing(false), @@ -76,8 +76,12 @@ void cHorse::Tick(float a_Dt, cChunk & a_Chunk) if (m_RearTickCount == 20) { m_bIsRearing = false; + m_RearTickCount = 0; + } + else + { + m_RearTickCount++; } - else { m_RearTickCount++;} } m_World->BroadcastEntityMetadata(*this); @@ -89,35 +93,45 @@ void cHorse::Tick(float a_Dt, cChunk & a_Chunk) void cHorse::OnRightClicked(cPlayer & a_Player) { - if (m_Attachee != NULL) + if (!m_bIsSaddled && m_bIsTame) { - if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) + if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE) { - a_Player.Detach(); - return; + // Saddle the horse: + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + m_bIsSaddled = true; + m_World->BroadcastEntityMetadata(*this); } - - if (m_Attachee->IsPlayer()) + else if (!a_Player.GetEquippedItem().IsEmpty()) { - return; + // The horse doesn't like being hit, make it rear: + m_bIsRearing = true; + m_RearTickCount = 0; } - - m_Attachee->Detach(); } - - m_TameAttemptTimes++; - a_Player.AttachTo(this); - - if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE) + else { - if (!a_Player.IsGameModeCreative()) + if (m_Attachee != NULL) { - a_Player.GetInventory().RemoveOneEquippedItem(); + if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) + { + a_Player.Detach(); + return; + } + + if (m_Attachee->IsPlayer()) + { + return; + } + + m_Attachee->Detach(); } - // Set saddle state & broadcast metadata - m_bIsSaddled = true; - m_World->BroadcastEntityMetadata(*this); + m_TameAttemptTimes++; + a_Player.AttachTo(this); } } diff --git a/source/Mobs/IncludeAllMonsters.h b/source/Mobs/IncludeAllMonsters.h new file mode 100644 index 000000000..1b436a11f --- /dev/null +++ b/source/Mobs/IncludeAllMonsters.h @@ -0,0 +1,29 @@ +#include "Bat.h" +#include "Blaze.h" +#include "Cavespider.h" +#include "Chicken.h" +#include "Cow.h" +#include "Creeper.h" +#include "Enderman.h" +#include "EnderDragon.h" +#include "Ghast.h" +#include "Giant.h" +#include "Horse.h" +#include "IronGolem.h" +#include "Magmacube.h" +#include "Mooshroom.h" +#include "Ocelot.h" +#include "Pig.h" +#include "Sheep.h" +#include "Silverfish.h" +#include "Skeleton.h" +#include "Slime.h" +#include "SnowGolem.h" +#include "Spider.h" +#include "Squid.h" +#include "Villager.h" +#include "Witch.h" +#include "Wither.h" +#include "Wolf.h" +#include "Zombie.h" +#include "Zombiepigman.h" diff --git a/source/Mobs/IronGolem.cpp b/source/Mobs/IronGolem.cpp index 42d312c23..47c961098 100644 --- a/source/Mobs/IronGolem.cpp +++ b/source/Mobs/IronGolem.cpp @@ -8,7 +8,7 @@ cIronGolem::cIronGolem(void) : - super("IronGolem", 99, "mob.IronGolem.hit", "mob.IronGolem.death", 1.4, 2.9) + super("IronGolem", mtIronGolem, "mob.IronGolem.hit", "mob.IronGolem.death", 1.4, 2.9) { } diff --git a/source/Mobs/Magmacube.cpp b/source/Mobs/Magmacube.cpp index c72b4831b..86447ff6b 100644 --- a/source/Mobs/Magmacube.cpp +++ b/source/Mobs/Magmacube.cpp @@ -8,7 +8,7 @@ cMagmaCube::cMagmaCube(int a_Size) : - super("MagmaCube", 62, "mob.MagmaCube.big", "mob.MagmaCube.big", 0.6 * a_Size, 0.6 * a_Size), + super("MagmaCube", mtMagmaCube, "mob.MagmaCube.big", "mob.MagmaCube.big", 0.6 * a_Size, 0.6 * a_Size), m_Size(a_Size) { } diff --git a/source/Mobs/Monster.cpp b/source/Mobs/Monster.cpp index 334229a42..7e35b97f1 100644 --- a/source/Mobs/Monster.cpp +++ b/source/Mobs/Monster.cpp @@ -1,7 +1,7 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules -#include "Monster.h" +#include "IncludeAllMonsters.h" #include "../Root.h" #include "../Server.h" #include "../ClientHandle.h" @@ -16,14 +16,56 @@ #include "../Vector3d.h" #include "../Tracer.h" #include "../Chunk.h" +#include "../FastRandom.h" -// #include "../../iniFile/iniFile.h" - -cMonster::cMonster(const AString & a_ConfigName, char a_ProtocolMobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) +/** Map for eType <-> string +Needs to be alpha-sorted by the strings, because binary search is used in StringToMobType() +The strings need to be lowercase (for more efficient comparisons in StringToMobType()) +*/ +static const struct +{ + cMonster::eType m_Type; + const char * m_lcName; +} g_MobTypeNames[] = +{ + {cMonster::mtBat, "bat"}, + {cMonster::mtBlaze, "blaze"}, + {cMonster::mtCaveSpider, "cavespider"}, + {cMonster::mtChicken, "chicken"}, + {cMonster::mtCow, "cow"}, + {cMonster::mtCreeper, "creeper"}, + {cMonster::mtEnderman, "enderman"}, + {cMonster::mtGhast, "ghast"}, + {cMonster::mtHorse, "horse"}, + {cMonster::mtMagmaCube, "magmacube"}, + {cMonster::mtMooshroom, "mooshroom"}, + {cMonster::mtOcelot, "ocelot"}, + {cMonster::mtPig, "pig"}, + {cMonster::mtSheep, "sheep"}, + {cMonster::mtSilverfish, "silverfish"}, + {cMonster::mtSkeleton, "skeleton"}, + {cMonster::mtSlime, "slime"}, + {cMonster::mtSpider, "spider"}, + {cMonster::mtSquid, "squid"}, + {cMonster::mtVillager, "villager"}, + {cMonster::mtWitch, "witch"}, + {cMonster::mtWolf, "wolf"}, + {cMonster::mtZombie, "zombie"}, + {cMonster::mtZombiePigman, "zombiepigman"}, +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMonster: + +cMonster::cMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) : super(etMonster, a_Width, a_Height) , m_Target(NULL) , m_AttackRate(3) @@ -32,7 +74,7 @@ cMonster::cMonster(const AString & a_ConfigName, char a_ProtocolMobType, const A , m_DestinationTime( 0 ) , m_DestroyTimer( 0 ) , m_Jump(0) - , m_MobType(a_ProtocolMobType) + , m_MobType(a_MobType) , m_SoundHurt(a_SoundHurt) , m_SoundDeath(a_SoundDeath) , m_EMState(IDLE) @@ -465,6 +507,189 @@ void cMonster::SetSightDistance(float sd) +AString cMonster::MobTypeToString(cMonster::eType a_MobType) +{ + // Mob types aren't sorted, so we need to search linearly: + for (int i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) + { + if (g_MobTypeNames[i].m_Type == a_MobType) + { + return g_MobTypeNames[i].m_lcName; + } + } + + // Not found: + return ""; +} + + + + + +cMonster::eType cMonster::StringToMobType(const AString & a_Name) +{ + AString lcName(a_Name); + StrToLower(lcName); + + // Binary-search for the lowercase name: + int lo = 0, hi = ARRAYCOUNT(g_MobTypeNames) - 1; + while (hi - lo > 1) + { + int mid = (lo + hi) / 2; + int res = strcmp(g_MobTypeNames[mid].m_lcName, lcName.c_str()); + if (res == 0) + { + return g_MobTypeNames[mid].m_Type; + } + if (res < 0) + { + lo = mid; + } + else + { + hi = mid; + } + } + // Range has collapsed to at most two elements, compare each: + if (strcmp(g_MobTypeNames[lo].m_lcName, lcName.c_str()) == 0) + { + return g_MobTypeNames[lo].m_Type; + } + if ((lo != hi) && (strcmp(g_MobTypeNames[hi].m_lcName, lcName.c_str()) == 0)) + { + return g_MobTypeNames[hi].m_Type; + } + + // Not found: + return mtInvalidType; +} + + + + + +cMonster::eFamily cMonster::FamilyFromType(eType a_Type) +{ + switch (a_Type) + { + case mtBat: return mfAmbient; + case mtBlaze: return mfHostile; + case mtCaveSpider: return mfHostile; + case mtChicken: return mfPassive; + case mtCow: return mfPassive; + case mtCreeper: return mfHostile; + case mtEnderman: return mfHostile; + case mtGhast: return mfHostile; + case mtHorse: return mfPassive; + case mtMagmaCube: return mfHostile; + case mtMooshroom: return mfHostile; + case mtOcelot: return mfHostile; + case mtPig: return mfPassive; + case mtSheep: return mfPassive; + case mtSilverfish: return mfHostile; + case mtSkeleton: return mfHostile; + case mtSlime: return mfHostile; + case mtSpider: return mfHostile; + case mtSquid: return mfWater; + case mtVillager: return mfPassive; + case mtWitch: return mfHostile; + case mtWolf: return mfHostile; + case mtZombie: return mfHostile; + case mtZombiePigman: return mfHostile; + } ; + ASSERT(!"Unhandled mob type"); + return mfMaxplusone; +} + + + + + +int cMonster::GetSpawnRate(cMonster::eFamily a_MobFamily) +{ + switch (a_MobFamily) + { + case mfHostile: return 1; + case mfPassive: return 400; + case mfAmbient: return 400; + case mfWater: return 400; + } + ASSERT(!"Unhandled mob family"); + return -1; +} + + + + + +cMonster * cMonster::NewMonsterFromType(cMonster::eType a_MobType, int a_Size) +{ + cFastRandom Random; + + cMonster * toReturn = NULL; + + // unspecified size get rand[1,3] for Monsters that need size + switch (a_MobType) + { + case mtMagmaCube: + case mtSlime: + { + if (a_Size == -1) + { + a_Size = Random.NextInt(2, a_MobType) + 1; + } + if ((a_Size <= 0) || (a_Size >= 4)) + { + ASSERT(!"Random for size was supposed to pick in [1..3] and picked outside"); + a_Size = 1; + } + break; + } + default: break; + } // switch (a_MobType) + + // Create the mob entity + switch (a_MobType) + { + case mtMagmaCube: toReturn = new cMagmaCube(a_Size); break; + case mtSlime: toReturn = new cSlime(a_Size); break; + case mtBat: toReturn = new cBat(); break; + case mtBlaze: toReturn = new cBlaze(); break; + case mtCaveSpider: toReturn = new cCavespider(); break; + case mtChicken: toReturn = new cChicken(); break; + case mtCow: toReturn = new cCow(); break; + case mtCreeper: toReturn = new cCreeper(); break; + case mtEnderman: toReturn = new cEnderman(); break; + case mtGhast: toReturn = new cGhast(); break; + // TODO: + // case cMonster::mtHorse: toReturn = new cHorse(); break; + case mtMooshroom: toReturn = new cMooshroom(); break; + case mtOcelot: toReturn = new cOcelot(); break; + case mtPig: toReturn = new cPig(); break; + // TODO: Implement sheep color + case mtSheep: toReturn = new cSheep(0); break; + case mtSilverfish: toReturn = new cSilverfish(); break; + // TODO: Implement wither skeleton geration + case mtSkeleton: toReturn = new cSkeleton(false); break; + case mtSpider: toReturn = new cSpider(); break; + case mtSquid: toReturn = new cSquid(); break; + case mtVillager: toReturn = new cVillager(cVillager::vtFarmer); break; + case mtWitch: toReturn = new cWitch(); break; + case mtWolf: toReturn = new cWolf(); break; + case mtZombie: toReturn = new cZombie(false); break; + case mtZombiePigman: toReturn = new cZombiePigman(); break; + default: + { + ASSERT(!"Unhandled Mob type"); + } + } + return toReturn; +} + + + + + void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth) { MTRand r1; @@ -493,8 +718,8 @@ void cMonster::HandleDaylightBurning(cChunk & a_Chunk) return; } - int RelX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width; - int RelZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width; + int RelX = (int)floor(GetPosX()) - GetChunkX() * cChunkDef::Width; + int RelZ = (int)floor(GetPosZ()) - GetChunkZ() * cChunkDef::Width; if ( (a_Chunk.GetSkyLight(RelX, RelY, RelZ) == 15) && // In the daylight (a_Chunk.GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) && // Not on soulsand @@ -510,3 +735,11 @@ void cMonster::HandleDaylightBurning(cChunk & a_Chunk) +cMonster::eFamily cMonster::GetMobFamily(void) const +{ + return FamilyFromType(m_MobType); +} + + + + diff --git a/source/Mobs/Monster.h b/source/Mobs/Monster.h index d784f2eec..14c72ed73 100644 --- a/source/Mobs/Monster.h +++ b/source/Mobs/Monster.h @@ -26,6 +26,8 @@ public: /// This identifies individual monster type, as well as their network type-ID enum eType { + mtInvalidType = -1, + mtBat = E_META_SPAWN_EGG_BAT, mtBlaze = E_META_SPAWN_EGG_BLAZE, mtCaveSpider = E_META_SPAWN_EGG_CAVE_SPIDER, @@ -55,19 +57,31 @@ public: mtWolf = E_META_SPAWN_EGG_WOLF, mtZombie = E_META_SPAWN_EGG_ZOMBIE, mtZombiePigman = E_META_SPAWN_EGG_ZOMBIE_PIGMAN, + } ; + + enum eFamily + { + mfHostile = 0, // Spider, Zombies ... + mfPassive = 1, // Cows, Pigs + mfAmbient = 2, // Bats + mfWater = 3, // Squid + mfMaxplusone, // Nothing. Be sure this is the last and the others are in order } ; // tolua_end + enum MState{ATTACKING, IDLE, CHASING, ESCAPING} m_EMState; + enum MPersonality{PASSIVE,AGGRESSIVE,COWARDLY} m_EMPersonality; + float m_SightDistance; /** Creates the mob object. * If a_ConfigName is not empty, the configuration is loaded using GetMonsterConfig() - * a_ProtocolMobType is the ID of the mob used in the protocol ( http://wiki.vg/Entities#Mobs , 2012_12_22) + * a_MobType is the type of the mob (also used in the protocol ( http://wiki.vg/Entities#Mobs , 2012_12_22)) * a_SoundHurt and a_SoundDeath are assigned into m_SoundHurt and m_SoundDeath, respectively */ - cMonster(const AString & a_ConfigName, char a_ProtocolMobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); + cMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); CLASS_PROTODEF(cMonster); @@ -82,7 +96,11 @@ public: virtual void MoveToPosition(const Vector3f & a_Position); virtual bool ReachedDestination(void); - char GetMobType(void) const {return m_MobType; } + // tolua_begin + eType GetMobType(void) const {return m_MobType; } + eFamily GetMobFamily(void) const; + // tolua_end + const char * GetState(); void SetState(const AString & str); @@ -103,8 +121,6 @@ public: virtual void Attack(float a_Dt); - int GetMobType() { return m_MobType; } // tolua_export - int GetAttackRate(){return (int)m_AttackRate;} void SetAttackRate(int ar); void SetAttackRange(float ar); @@ -119,9 +135,31 @@ public: virtual bool IsTame (void) const { return false; } virtual bool IsSitting (void) const { return false; } - enum MState{ATTACKING, IDLE, CHASING, ESCAPING} m_EMState; - enum MPersonality{PASSIVE,AGGRESSIVE,COWARDLY} m_EMPersonality; + // tolua_begin + + /// Translates MobType enum to a string + static AString MobTypeToString(eType a_MobType); + + /// Translates MobType string to the enum + static eType StringToMobType(const AString & a_MobTypeName); + /// Returns the mob family based on the type + static eFamily FamilyFromType(eType a_MobType); + + /// Returns the spawn rate (number of game ticks between spawn attempts) for the given mob family + static int GetSpawnRate(cMonster::eFamily a_MobFamily); + + // tolua_end + + /** Creates a new object of the specified mob. + a_MobType is the type of the mob to be created + a_Size is the size (for mobs with size) + if a_Size is let to -1 for entities that need size, size will be random + asserts and returns null if mob type is not specified + asserts if invalid size for mobs that need size + */ + static cMonster * NewMonsterFromType(eType a_MobType, int a_Size = -1); + protected: cEntity * m_Target; @@ -137,7 +175,7 @@ protected: float m_DestroyTimer; float m_Jump; - char m_MobType; + eType m_MobType; AString m_SoundHurt; AString m_SoundDeath; diff --git a/source/Mobs/Mooshroom.cpp b/source/Mobs/Mooshroom.cpp index 5d2c901ba..940e2db44 100644 --- a/source/Mobs/Mooshroom.cpp +++ b/source/Mobs/Mooshroom.cpp @@ -14,7 +14,7 @@ cMooshroom::cMooshroom(void) : - super("Mooshroom", 96, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3) + super("Mooshroom", mtMooshroom, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3) { } diff --git a/source/Mobs/Ocelot.h b/source/Mobs/Ocelot.h index 6d24c5672..adb7a1f75 100644 --- a/source/Mobs/Ocelot.h +++ b/source/Mobs/Ocelot.h @@ -14,7 +14,7 @@ class cOcelot : public: cOcelot(void) : - super("Ocelot", 98, "mob.cat.hitt", "mob.cat.hitt", 0.6, 0.8) + super("Ocelot", mtOcelot, "mob.cat.hitt", "mob.cat.hitt", 0.6, 0.8) { } diff --git a/source/Mobs/PassiveAggressiveMonster.cpp b/source/Mobs/PassiveAggressiveMonster.cpp index e473137a9..28de65905 100644 --- a/source/Mobs/PassiveAggressiveMonster.cpp +++ b/source/Mobs/PassiveAggressiveMonster.cpp @@ -9,8 +9,8 @@ -cPassiveAggressiveMonster::cPassiveAggressiveMonster(const AString & a_ConfigName, char a_ProtocolMobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) : - super(a_ConfigName, a_ProtocolMobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height) +cPassiveAggressiveMonster::cPassiveAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) : + super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height) { m_EMPersonality = PASSIVE; } diff --git a/source/Mobs/PassiveAggressiveMonster.h b/source/Mobs/PassiveAggressiveMonster.h index 243dfff38..2c5ef30b1 100644 --- a/source/Mobs/PassiveAggressiveMonster.h +++ b/source/Mobs/PassiveAggressiveMonster.h @@ -13,7 +13,7 @@ class cPassiveAggressiveMonster : typedef cAggressiveMonster super; public: - cPassiveAggressiveMonster(const AString & a_ConfigName, char a_ProtocolMobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); + cPassiveAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; } ; diff --git a/source/Mobs/PassiveMonster.cpp b/source/Mobs/PassiveMonster.cpp index 7a6140c04..91ceb5a53 100644 --- a/source/Mobs/PassiveMonster.cpp +++ b/source/Mobs/PassiveMonster.cpp @@ -9,8 +9,8 @@ -cPassiveMonster::cPassiveMonster(const AString & a_ConfigName, char a_ProtocolMobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) : - super(a_ConfigName, a_ProtocolMobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height) +cPassiveMonster::cPassiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) : + super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height) { m_EMPersonality = PASSIVE; } @@ -56,3 +56,4 @@ void cPassiveMonster::Tick(float a_Dt, cChunk & a_Chunk) + diff --git a/source/Mobs/PassiveMonster.h b/source/Mobs/PassiveMonster.h index ae0bea3fb..14a6be6b1 100644 --- a/source/Mobs/PassiveMonster.h +++ b/source/Mobs/PassiveMonster.h @@ -13,12 +13,13 @@ class cPassiveMonster : typedef cMonster super; public: - cPassiveMonster(const AString & a_ConfigName, char a_ProtocolMobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); + cPassiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); virtual void Tick(float a_Dt, cChunk & a_Chunk) override; /// When hit by someone, run away virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + } ; diff --git a/source/Mobs/Pig.cpp b/source/Mobs/Pig.cpp index cd18c087f..5427cf35f 100644 --- a/source/Mobs/Pig.cpp +++ b/source/Mobs/Pig.cpp @@ -10,7 +10,7 @@ cPig::cPig(void) : - super("Pig", 90, "mob.pig.say", "mob.pig.death", 0.9, 0.9), + super("Pig", mtPig, "mob.pig.say", "mob.pig.death", 0.9, 0.9), m_bIsSaddled(false) { } diff --git a/source/Mobs/Sheep.cpp b/source/Mobs/Sheep.cpp index 440c5c2b9..0d7d43e27 100644 --- a/source/Mobs/Sheep.cpp +++ b/source/Mobs/Sheep.cpp @@ -11,7 +11,7 @@ cSheep::cSheep(int a_Color) : - super("Sheep", 91, "mob.sheep.say", "mob.sheep.say", 0.6, 1.3), + super("Sheep", mtSheep, "mob.sheep.say", "mob.sheep.say", 0.6, 1.3), m_IsSheared(false), m_WoolColor(a_Color) { diff --git a/source/Mobs/Silverfish.h b/source/Mobs/Silverfish.h index d632ac169..a6e11c49d 100644 --- a/source/Mobs/Silverfish.h +++ b/source/Mobs/Silverfish.h @@ -14,7 +14,7 @@ class cSilverfish : public: cSilverfish(void) : - super("Silverfish", 60, "mob.silverfish.hit", "mob.silverfish.kill", 0.3, 0.7) + super("Silverfish", mtSilverfish, "mob.silverfish.hit", "mob.silverfish.kill", 0.3, 0.7) { } diff --git a/source/Mobs/Skeleton.cpp b/source/Mobs/Skeleton.cpp index 6297b867c..37a724848 100644 --- a/source/Mobs/Skeleton.cpp +++ b/source/Mobs/Skeleton.cpp @@ -9,7 +9,7 @@ cSkeleton::cSkeleton(bool IsWither) : - super("Skeleton", 51, "mob.skeleton.hurt", "mob.skeleton.death", 0.6, 1.8), + super("Skeleton", mtSkeleton, "mob.skeleton.hurt", "mob.skeleton.death", 0.6, 1.8), m_bIsWither(IsWither) { SetBurnsInDaylight(true); diff --git a/source/Mobs/Slime.cpp b/source/Mobs/Slime.cpp index 7a9487a06..19f376c21 100644 --- a/source/Mobs/Slime.cpp +++ b/source/Mobs/Slime.cpp @@ -9,7 +9,7 @@ /// Creates a slime of the specified size; size is 1 .. 3, with 1 being the smallest cSlime::cSlime(int a_Size) : - super("Slime", 55, "mob.slime.attack", "mob.slime.attack", 0.6 * a_Size, 0.6 * a_Size), + super("Slime", mtSlime, "mob.slime.attack", "mob.slime.attack", 0.6 * a_Size, 0.6 * a_Size), m_Size(a_Size) { } diff --git a/source/Mobs/SnowGolem.cpp b/source/Mobs/SnowGolem.cpp index 51125542d..9e199f87e 100644 --- a/source/Mobs/SnowGolem.cpp +++ b/source/Mobs/SnowGolem.cpp @@ -8,7 +8,7 @@ cSnowGolem::cSnowGolem(void) : - super("SnowGolem", 97, "", "", 0.4, 1.8) + super("SnowGolem", mtSnowGolem, "", "", 0.4, 1.8) { } diff --git a/source/Mobs/Spider.cpp b/source/Mobs/Spider.cpp index 2f244cdbc..b19a5dcef 100644 --- a/source/Mobs/Spider.cpp +++ b/source/Mobs/Spider.cpp @@ -8,7 +8,7 @@ cSpider::cSpider(void) : - super("Spider", 52, "mob.spider.say", "mob.spider.death", 1.4, 0.9) + super("Spider", mtSpider, "mob.spider.say", "mob.spider.death", 1.4, 0.9) { } diff --git a/source/Mobs/Squid.cpp b/source/Mobs/Squid.cpp index cb796f5ec..a311108ae 100644 --- a/source/Mobs/Squid.cpp +++ b/source/Mobs/Squid.cpp @@ -10,7 +10,7 @@ cSquid::cSquid(void) : - super("Squid", 94, "", "", 0.95, 0.95) + super("Squid", mtSquid, "", "", 0.95, 0.95) { } @@ -54,4 +54,3 @@ void cSquid::Tick(float a_Dt, cChunk & a_Chunk) - diff --git a/source/Mobs/Squid.h b/source/Mobs/Squid.h index 35d7295b3..ad299b95c 100644 --- a/source/Mobs/Squid.h +++ b/source/Mobs/Squid.h @@ -20,6 +20,7 @@ public: CLASS_PROTODEF(cSquid); virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + } ; diff --git a/source/Mobs/Villager.cpp b/source/Mobs/Villager.cpp index 97d6dc3ca..7f89fb6cc 100644 --- a/source/Mobs/Villager.cpp +++ b/source/Mobs/Villager.cpp @@ -9,7 +9,7 @@ cVillager::cVillager(eVillagerType VillagerType) : - super("Villager", 120, "", "", 0.6, 1.8), + super("Villager", mtVillager, "", "", 0.6, 1.8), m_Type(VillagerType) { } diff --git a/source/Mobs/Witch.cpp b/source/Mobs/Witch.cpp index b29783853..25d27041f 100644 --- a/source/Mobs/Witch.cpp +++ b/source/Mobs/Witch.cpp @@ -8,7 +8,7 @@ cWitch::cWitch(void) : - super("Witch", 66, "", "", 0.6, 1.8) + super("Witch", mtWitch, "", "", 0.6, 1.8) { } diff --git a/source/Mobs/Wither.cpp b/source/Mobs/Wither.cpp index 8b77284c8..c46e0beab 100644 --- a/source/Mobs/Wither.cpp +++ b/source/Mobs/Wither.cpp @@ -8,7 +8,7 @@ cWither::cWither(void) : - super("Wither", 64, "mob.wither.hurt", "mob.wither.death", 0.9, 4.0) + super("Wither", mtWither, "mob.wither.hurt", "mob.wither.death", 0.9, 4.0) { } diff --git a/source/Mobs/Wolf.cpp b/source/Mobs/Wolf.cpp index e76f991dc..2baeb4b7b 100644 --- a/source/Mobs/Wolf.cpp +++ b/source/Mobs/Wolf.cpp @@ -10,7 +10,7 @@ cWolf::cWolf(void) : - super("Wolf", 95, "mob.wolf.hurt", "mob.wolf.death", 0.6, 0.8), + super("Wolf", mtWolf, "mob.wolf.hurt", "mob.wolf.death", 0.6, 0.8), m_bIsAngry(false), m_bIsTame(false), m_bIsSitting(false), diff --git a/source/Mobs/Zombie.cpp b/source/Mobs/Zombie.cpp index f495fe5ee..1752ec390 100644 --- a/source/Mobs/Zombie.cpp +++ b/source/Mobs/Zombie.cpp @@ -9,7 +9,7 @@ cZombie::cZombie(bool IsVillagerZombie) : - super("Zombie", 54, "mob.zombie.hurt", "mob.zombie.death", 0.6, 1.8), + super("Zombie", mtZombie, "mob.zombie.hurt", "mob.zombie.death", 0.6, 1.8), m_bIsConverting(false), m_bIsVillagerZombie(IsVillagerZombie) { diff --git a/source/Mobs/Zombiepigman.cpp b/source/Mobs/Zombiepigman.cpp index 1e31a72d9..6ac89ed4c 100644 --- a/source/Mobs/Zombiepigman.cpp +++ b/source/Mobs/Zombiepigman.cpp @@ -9,7 +9,7 @@ cZombiePigman::cZombiePigman(void) : - super("ZombiePigman", 57, "mob.zombiepig.zpighurt", "mob.zombiepig.zpigdeath", 0.6, 1.8) + super("ZombiePigman", mtZombiePigman, "mob.zombiepig.zpighurt", "mob.zombiepig.zpigdeath", 0.6, 1.8) { } diff --git a/source/Protocol/ProtocolRecognizer.cpp b/source/Protocol/ProtocolRecognizer.cpp index fe99b22e1..ceada1944 100644 --- a/source/Protocol/ProtocolRecognizer.cpp +++ b/source/Protocol/ProtocolRecognizer.cpp @@ -727,7 +727,7 @@ bool cProtocolRecognizer::TryRecognizeProtocol(void) void cProtocolRecognizer::HandleServerPing(void) { AString Reply; - switch (cRoot::Get()->m_PrimaryServerVersion) + switch (cRoot::Get()->GetPrimaryServerVersion()) { case PROTO_VERSION_1_2_5: case PROTO_VERSION_1_3_2: @@ -771,8 +771,8 @@ void cProtocolRecognizer::HandleServerPing(void) Printf(MaxPlayers, "%d", cRoot::Get()->GetServer()->GetMaxPlayers()); AString ProtocolVersionNum; - Printf(ProtocolVersionNum, "%d", cRoot::Get()->m_PrimaryServerVersion); - AString ProtocolVersionTxt(GetVersionTextFromInt(cRoot::Get()->m_PrimaryServerVersion)); + Printf(ProtocolVersionNum, "%d", cRoot::Get()->GetPrimaryServerVersion()); + AString ProtocolVersionTxt(GetVersionTextFromInt(cRoot::Get()->GetPrimaryServerVersion())); // Cannot use Printf() because of in-string NUL bytes. Reply = cChatColor::Delimiter; diff --git a/source/Root.cpp b/source/Root.cpp index 290a5269a..1f6437784 100644 --- a/source/Root.cpp +++ b/source/Root.cpp @@ -140,7 +140,6 @@ void cRoot::Start(void) } IniFile.WriteFile(); - LOG("Initialising WebAdmin..."); m_WebAdmin = new cWebAdmin(); m_WebAdmin->Init(); @@ -172,7 +171,6 @@ void cRoot::Start(void) LOGD("Finalising startup..."); m_Server->Start(); - LOG("Starting WebAdmin..."); m_WebAdmin->Start(); #if !defined(ANDROID_NDK) @@ -210,7 +208,6 @@ void cRoot::Start(void) LOGD("Freeing MonsterConfig..."); delete m_MonsterConfig; m_MonsterConfig = NULL; - LOGD("Stopping WebAdmin..."); delete m_WebAdmin; m_WebAdmin = NULL; LOGD("Unloading recipes..."); delete m_FurnaceRecipe; m_FurnaceRecipe = NULL; diff --git a/source/Root.h b/source/Root.h index 2b15d3461..c05b29d14 100644 --- a/source/Root.h +++ b/source/Root.h @@ -32,10 +32,7 @@ typedef cItemCallback<cWorld> cWorldListCallback; class cRoot // tolua_export { // tolua_export public: - /// The version of the protocol that is primary for the server (reported in the server list). All versions are still supported. - int m_PrimaryServerVersion; // tolua_export - - static cRoot* Get() { return s_Root; } // tolua_export + static cRoot * Get() { return s_Root; } // tolua_export cRoot(void); ~cRoot(); @@ -55,7 +52,7 @@ public: int GetPrimaryServerVersion(void) const { return m_PrimaryServerVersion; } // tolua_export void SetPrimaryServerVersion(int a_Version) { m_PrimaryServerVersion = a_Version; } // tolua_export - cMonsterConfig * GetMonsterConfig() { return m_MonsterConfig; } + cMonsterConfig * GetMonsterConfig(void) { return m_MonsterConfig; } cGroupManager * GetGroupManager (void) { return m_GroupManager; } // tolua_export cCraftingRecipes * GetCraftingRecipes(void) { return m_CraftingRecipes; } // tolua_export @@ -135,6 +132,9 @@ private: typedef std::map<AString, cWorld *> WorldMap; typedef std::vector<cCommand> cCommandQueue; + /// The version of the protocol that is primary for the server (reported in the server list). All versions are still supported. + int m_PrimaryServerVersion; + cWorld * m_pDefaultWorld; WorldMap m_WorldsByName; diff --git a/source/WebAdmin.cpp b/source/WebAdmin.cpp index 316513f11..882969746 100644 --- a/source/WebAdmin.cpp +++ b/source/WebAdmin.cpp @@ -32,7 +32,7 @@ class cPlayerAccum : m_Contents.append("</li>"); return false; } - + public: AString m_Contents; @@ -53,6 +53,18 @@ cWebAdmin::cWebAdmin(void) : +cWebAdmin::~cWebAdmin() +{ + if (m_IsInitialized) + { + LOG("Stopping WebAdmin..."); + } +} + + + + + void cWebAdmin::AddPlugin( cWebPlugin * a_Plugin ) { m_Plugins.remove( a_Plugin ); @@ -78,16 +90,18 @@ bool cWebAdmin::Init(void) { return false; } - + + LOG("Initialising WebAdmin..."); + if (!m_IniFile.GetValueSetB("WebAdmin", "Enabled", true)) { // WebAdmin is disabled, bail out faking a success return true; } - + AString PortsIPv4 = m_IniFile.GetValueSet("WebAdmin", "Port", "8080"); AString PortsIPv6 = m_IniFile.GetValueSet("WebAdmin", "PortsIPv6", ""); - + if (!m_HTTPServer.Initialize(PortsIPv4, PortsIPv6)) { return false; @@ -107,7 +121,9 @@ bool cWebAdmin::Start(void) // Not initialized return false; } - + + LOG("Starting WebAdmin..."); + // Initialize the WebAdmin template script and load the file m_TemplateScript.Create(); if (!m_TemplateScript.LoadFile(FILE_IO_PREFIX "webadmin/template.lua")) @@ -160,12 +176,12 @@ void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPReque a_Connection.SendNeedAuth("MCServer WebAdmin - bad username or password"); return; } - + // Check if the contents should be wrapped in the template: AString URL = a_Request.GetBareURL(); ASSERT(URL.length() > 0); bool ShouldWrapInTemplate = ((URL.length() > 1) && (URL[1] != '~')); - + // Retrieve the request data: cWebadminRequestData * Data = (cWebadminRequestData *)(a_Request.GetUserData()); if (Data == NULL) @@ -173,14 +189,14 @@ void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPReque a_Connection.SendStatusAndReason(500, "Bad UserData"); return; } - + // Wrap it all up for the Lua call: AString Template; HTTPTemplateRequest TemplateRequest; TemplateRequest.Request.Username = a_Request.GetAuthUsername(); TemplateRequest.Request.Method = a_Request.GetMethod(); TemplateRequest.Request.Path = URL.substr(1); - + if (Data->m_Form.Finish()) { for (cHTTPFormParser::const_iterator itr = Data->m_Form.begin(), end = Data->m_Form.end(); itr != end; ++itr) @@ -192,7 +208,7 @@ void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPReque TemplateRequest.Request.FormData[itr->first] = HTTPfd; TemplateRequest.Request.PostParams[itr->first] = itr->second; } // for itr - Data->m_Form[] - + // Parse the URL into individual params: size_t idxQM = a_Request.GetURL().find('?'); if (idxQM != AString::npos) @@ -205,7 +221,7 @@ void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPReque } // for itr - URLParams[] } } - + // Try to get the template from the Lua template script if (ShouldWrapInTemplate) { @@ -220,7 +236,7 @@ void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPReque a_Connection.SendStatusAndReason(500, "m_TemplateScript failed"); return; } - + AString BaseURL = GetBaseURL(URL); AString Menu; Template = "{CONTENT}"; @@ -254,7 +270,7 @@ void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPReque Content += "\n<p><a href='" + BaseURL + "'>Go back</a></p>"; } - int MemUsageKiB = GetMemoryUsage(); + int MemUsageKiB = cRoot::GetPhysicalRAMUsage(); if (MemUsageKiB > 0) { ReplaceString(Template, "{MEM}", Printf("%.02f", (double)MemUsageKiB / 1024)); @@ -381,7 +397,38 @@ AString cWebAdmin::GetBaseURL( const AString& a_URL ) -AString cWebAdmin::GetBaseURL( const AStringVector& a_URLSplit ) +AString cWebAdmin::GetHTMLEscapedString(const AString & a_Input) +{ + AString dst; + dst.reserve(a_Input.length()); + + // Loop over input and substitute HTML characters for their alternatives: + size_t len = a_Input.length(); + for (size_t i = 0; i < len; i++) + { + switch (a_Input[i]) + { + case '&': dst.append("&"); break; + case '\'': dst.append("'"); break; + case '"': dst.append("""); break; + case '<': dst.append("<"); break; + case '>': dst.append(">"); break; + default: + { + dst.push_back(a_Input[i]); + break; + } + } // switch (a_Input[i]) + } // for i - a_Input[] + + return dst; +} + + + + + +AString cWebAdmin::GetBaseURL(const AStringVector & a_URLSplit) { AString BaseURL = "./"; if (a_URLSplit.size() > 1) @@ -399,16 +446,6 @@ AString cWebAdmin::GetBaseURL( const AStringVector& a_URLSplit ) -int cWebAdmin::GetMemoryUsage(void) -{ - LOGWARNING("%s: This function is obsolete, use cRoot::GetPhysicalRAMUsage() or cRoot::GetVirtualRAMUsage() instead", __FUNCTION__); - return cRoot::GetPhysicalRAMUsage(); -} - - - - - void cWebAdmin::OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) { const AString & URL = a_Request.GetURL(); @@ -465,7 +502,7 @@ void cWebAdmin::OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & { // TODO: Handle other requests } - + // Delete any request data assigned to the request: cRequestData * Data = (cRequestData *)(a_Request.GetUserData()); delete Data; diff --git a/source/WebAdmin.h b/source/WebAdmin.h index 72c77ddfb..acd81ebfa 100644 --- a/source/WebAdmin.h +++ b/source/WebAdmin.h @@ -51,18 +51,18 @@ struct HTTPRequest { typedef std::map< std::string, std::string > StringStringMap; typedef std::map< std::string, HTTPFormData > FormDataMap; - + AString Method; AString Path; AString Username; // tolua_end - + /// Parameters given in the URL, after the questionmark StringStringMap Params; // >> EXPORTED IN MANUALBINDINGS << - + /// Parameters posted as a part of a form - either in the URL (GET method) or in the body (POST method) StringStringMap PostParams; // >> EXPORTED IN MANUALBINDINGS << - + /// Same as PostParams FormDataMap FormData; // >> EXPORTED IN MANUALBINDINGS << } ; // tolua_export @@ -101,15 +101,16 @@ class cWebAdmin : { public: // tolua_end - + typedef std::list< cWebPlugin* > PluginList; cWebAdmin(void); + ~cWebAdmin(); /// Initializes the object. Returns true if successfully initialized and ready to start bool Init(void); - + /// Starts the HTTP server taking care of the admin. Returns true if successful bool Start(void); @@ -117,35 +118,37 @@ public: void RemovePlugin( cWebPlugin* a_Plugin ); // 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 << + PluginList GetPlugins() const { return m_Plugins; } // >> EXPORTED IN MANUALBINDINGS << // tolua_begin - - /// Returns the amount of currently used memory, in KiB, or -1 if it cannot be queried - static int GetMemoryUsage(void); - sWebAdminPage GetPage(const HTTPRequest& a_Request); - + sWebAdminPage GetPage(const HTTPRequest & a_Request); + /// Returns the contents of the default page - the list of plugins and players AString GetDefaultPage(void); - - AString GetBaseURL(const AString& a_URL); - + + /// Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) + AString GetBaseURL(const AString & a_URL); + + /// Escapes text passed into it, so it can be embedded into html. + static AString GetHTMLEscapedString(const AString & a_Input); + // tolua_end - AString GetBaseURL(const AStringVector& a_URLSplit); - + /// Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) + AString GetBaseURL(const AStringVector& a_URLSplit); + protected: /// Common base class for request body data handlers class cRequestData { public: virtual ~cRequestData() {} // Force a virtual destructor in all descendants - + /// Called when a new chunk of body data is received virtual void OnBody(const char * a_Data, int a_Size) = 0; } ; - + /// The body handler for requests in the "/webadmin" and "/~webadmin" paths class cWebadminRequestData : public cRequestData, @@ -153,13 +156,13 @@ protected: { public: cHTTPFormParser m_Form; - - + + cWebadminRequestData(cHTTPRequest & a_Request) : m_Form(a_Request, *this) { } - + // cRequestData overrides: virtual void OnBody(const char * a_Data, int a_Size) override; @@ -168,31 +171,31 @@ protected: virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) override {} virtual void OnFileEnd(cHTTPFormParser & a_Parser) override {} } ; - - + + /// Set to true if Init() succeeds and the webadmin isn't to be disabled bool m_IsInitialized; /// The webadmin.ini file, used for the settings and allowed logins cIniFile m_IniFile; - + PluginList m_Plugins; /// The Lua template script to provide templates: cLuaState m_TemplateScript; - + /// The HTTP server which provides the underlying HTTP parsing, serialization and events cHTTPServer m_HTTPServer; AString GetTemplate(void); - + /// Handles requests coming to the "/webadmin" or "/~webadmin" URLs void HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); - + /// Handles requests for the root page void HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); - + // cHTTPServer::cCallbacks overrides: virtual void OnRequestBegun (cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override; virtual void OnRequestBody (cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) override; diff --git a/source/World.cpp b/source/World.cpp index 28c73f591..e2db77666 100644 --- a/source/World.cpp +++ b/source/World.cpp @@ -28,35 +28,9 @@ #include "Simulator/VaporizeFluidSimulator.h" // Mobs: -#include "Mobs/Bat.h" -#include "Mobs/Blaze.h" -#include "Mobs/Cavespider.h" -#include "Mobs/Chicken.h" -#include "Mobs/Cow.h" -#include "Mobs/Creeper.h" -#include "Mobs/Enderman.h" -#include "Mobs/EnderDragon.h" -#include "Mobs/Ghast.h" -#include "Mobs/Giant.h" -#include "Mobs/Horse.h" -#include "Mobs/IronGolem.h" -#include "Mobs/Magmacube.h" -#include "Mobs/Mooshroom.h" -#include "Mobs/Ocelot.h" -#include "Mobs/Pig.h" -#include "Mobs/Sheep.h" -#include "Mobs/Silverfish.h" -#include "Mobs/Skeleton.h" -#include "Mobs/Slime.h" -#include "Mobs/SnowGolem.h" -#include "Mobs/Spider.h" -#include "Mobs/Squid.h" -#include "Mobs/Villager.h" -#include "Mobs/Witch.h" -#include "Mobs/Wither.h" -#include "Mobs/Wolf.h" -#include "Mobs/Zombie.h" -#include "Mobs/Zombiepigman.h" +#include "Mobs/IncludeAllMonsters.h" +#include "MobCensus.h" +#include "MobSpawner.h" #include "MersenneTwister.h" #include "Generating/Trees.h" @@ -252,7 +226,6 @@ cWorld::cWorld(const AString & a_WorldName) : m_WorldAge(0), m_TimeOfDay(0), m_LastTimeUpdate(0), - m_LastSpawnMonster(0), m_RSList(0), m_Weather(eWeather_Sunny), m_WeatherInterval(24000), // Guaranteed 1 day of sunshine at server start :) @@ -515,14 +488,35 @@ void cWorld::Start(void) m_GameMode = (eGameMode)IniFile.GetValueSetI("GameMode", "GameMode", m_GameMode); - m_bAnimals = true; - m_SpawnMonsterRate = 200; // 1 mob each 10 seconds - cIniFile IniFile2("settings.ini"); - if (IniFile2.ReadFile()) + // Load allowed mobs: + const char * DefaultMonsters = ""; + switch (m_Dimension) { - m_bAnimals = IniFile2.GetValueB("Monsters", "AnimalsOn", true); - m_SpawnMonsterRate = (Int64)(IniFile2.GetValueF("Monsters", "AnimalSpawnInterval", 10) * 20); // Convert from secs to ticks - + case dimOverworld: DefaultMonsters = "bat, cavespider, chicken, cow, creeper, enderman, horse, mooshroom, ocelot, pig, sheep, silverfish, skeleton, slime, spider, squid, wolf, zombie"; break; + case dimNether: DefaultMonsters = "blaze, ghast, magmacube, skeleton, zombie, zombiepigman"; break; + case dimEnd: DefaultMonsters = "enderman"; break; + default: + { + ASSERT(!"Unhandled world dimension"); + DefaultMonsters = "wither"; + break; + } + } + m_bAnimals = IniFile.GetValueB("Monsters", "AnimalsOn", true); + AString AllMonsters = IniFile.GetValueSet("Monsters", "Types", DefaultMonsters); + AStringVector SplitList = StringSplitAndTrim(AllMonsters, ","); + for (AStringVector::const_iterator itr = SplitList.begin(), end = SplitList.end(); itr != end; ++itr) + { + cMonster::eType ToAdd = cMonster::StringToMobType(*itr); + if (ToAdd != cMonster::mtInvalidType) + { + m_AllowedMobs.insert(ToAdd); + LOGD("Allowed mob: %s", itr->c_str()); + } + else + { + LOG("World \"%s\": Unknown mob type: %s", m_WorldName.c_str(), itr->c_str()); + } } m_ChunkMap = new cChunkMap(this); @@ -553,6 +547,13 @@ void cWorld::Start(void) m_ChunkSender.Start(this); m_TickThread.Start(); + // Init of the spawn monster time (as they are supposed to have different spawn rate) + m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfHostile, 0)); + m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfPassive, 0)); + m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfAmbient, 0)); + m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfWater, 0)); + + // Save any changes that the defaults may have done to the ini file: if (!IniFile.WriteFile()) { @@ -648,7 +649,7 @@ void cWorld::Tick(float a_Dt) UnloadUnusedChunks(); } - TickSpawnMobs(a_Dt); + TickMobs(a_Dt); std::vector<int> m_RSList_copy(m_RSList); @@ -733,125 +734,61 @@ void cWorld::TickWeather(float a_Dt) -void cWorld::TickSpawnMobs(float a_Dt) +void cWorld::TickMobs(float a_Dt) { - if (!m_bAnimals || (m_WorldAge - m_LastSpawnMonster <= m_SpawnMonsterRate)) - { - return; - } - - m_LastSpawnMonster = m_WorldAge; - Vector3d SpawnPos; - { - cCSLock Lock(m_CSPlayers); - if (m_Players.size() <= 0) - { - return; - } - int RandomPlayerIdx = m_TickRand.randInt() & m_Players.size(); - cPlayerList::iterator itr = m_Players.begin(); - for (int i = 1; i < RandomPlayerIdx; i++) - { - itr++; - } - SpawnPos = (*itr)->GetPosition(); - } - - int dayRand = (m_TickRand.randInt() / 7) % 6; - int nightRand = (m_TickRand.randInt() / 11) % 10; + // _X 2013_10_22: This is a quick fix for #283 - the world needs to be locked while ticking mobs + cWorld::cLock Lock(*this); - SpawnPos += Vector3d((double)(m_TickRand.randInt() % 64) - 32, (double)(m_TickRand.randInt() % 64) - 32, (double)(m_TickRand.randInt() % 64) - 32); - int Height = GetHeight((int)SpawnPos.x, (int)SpawnPos.z); - - int MobType = -1; - int Biome = GetBiomeAt((int)SpawnPos.x, (int)SpawnPos.z); - switch (Biome) + // before every Mob action, we have to count them depending on the distance to players, on their family ... + cMobCensus MobCensus; + m_ChunkMap->CollectMobCensus(MobCensus); + if (m_bAnimals) { - case biNether: + // Spawning is enabled, spawn now: + static const cMonster::eFamily AllFamilies[] = { - // Spawn nether mobs - switch (nightRand) - { - case 0: MobType = cMonster::mtBlaze; break; - case 1: MobType = cMonster::mtGhast; break; - case 2: MobType = cMonster::mtGhast; break; - case 3: MobType = cMonster::mtGhast; break; - case 4: MobType = cMonster::mtZombiePigman; break; - case 5: MobType = cMonster::mtZombiePigman; break; - case 6: MobType = cMonster::mtZombiePigman; break; - case 7: MobType = cMonster::mtZombiePigman; break; - case 8: MobType = cMonster::mtZombiePigman; break; - case 9: MobType = cMonster::mtZombiePigman; break; - } - break; - } - - case biEnd: + cMonster::mfHostile, + cMonster::mfPassive, + cMonster::mfAmbient, + cMonster::mfWater, + } ; + for (int i = 0; i < ARRAYCOUNT(AllFamilies); i++) { - // Spawn only The End mobs - switch (nightRand) + cMonster::eFamily Family = AllFamilies[i]; + int spawnrate = cMonster::GetSpawnRate(Family); + if ( + (m_LastSpawnMonster[Family] > m_WorldAge - spawnrate) || // Not reached the needed tiks before the next round + MobCensus.IsCapped(Family) + ) { - case 0: MobType = cMonster::mtEnderDragon; break; - case 1: MobType = cMonster::mtEnderman; break; - case 2: MobType = cMonster::mtEnderman; break; - case 3: MobType = cMonster::mtEnderman; break; - case 4: MobType = cMonster::mtEnderman; break; - case 5: MobType = cMonster::mtEnderman; break; - case 6: MobType = cMonster::mtEnderman; break; - case 7: MobType = cMonster::mtEnderman; break; - case 8: MobType = cMonster::mtEnderman; break; - case 9: MobType = cMonster::mtEnderman; break; + continue; } - break; - } - - case biMushroomIsland: - case biMushroomShore: - { - // Mushroom land gets only mooshrooms - MobType = cMonster::mtMooshroom; - break; - } - - default: - { - // Overworld biomes depend on whether it's night or day: - if (m_TimeOfDay >= 12000 + 1000) + m_LastSpawnMonster[Family] = m_WorldAge; + cMobSpawner Spawner(Family, m_AllowedMobs); + if (Spawner.CanSpawnAnything()) { - // Night mobs: - switch (nightRand) - { - case 0: MobType = cMonster::mtSpider; break; - case 1: MobType = cMonster::mtZombie; break; - case 2: MobType = cMonster::mtEnderman; break; - case 3: MobType = cMonster::mtCreeper; break; - case 4: MobType = cMonster::mtCaveSpider; break; - case 7: MobType = cMonster::mtSlime; break; - case 8: MobType = cMonster::mtSilverfish; break; - case 9: MobType = cMonster::mtSkeleton; break; - } - } // if (night) - else - { - // During the day: - switch (dayRand) + m_ChunkMap->SpawnMobs(Spawner); + // do the spawn + for (cMobSpawner::tSpawnedContainer::const_iterator itr2 = Spawner.getSpawned().begin(); itr2 != Spawner.getSpawned().end(); itr2++) { - case 0: MobType = cMonster::mtChicken; break; - case 1: MobType = cMonster::mtCow; break; - case 2: MobType = cMonster::mtPig; break; - case 3: MobType = cMonster::mtSheep; break; - case 4: MobType = cMonster::mtSquid; break; - case 5: MobType = cMonster::mtWolf; break; - case 6: MobType = cMonster::mtHorse; break; + SpawnMobFinalize(*itr2); } - } // else (night) - } // case overworld biomes - } // switch (biome) + } + } // for i - AllFamilies[] + } // if (Spawning enabled) + + // move close mobs + cMobProximityCounter::sIterablePair allCloseEnoughToMoveMobs = MobCensus.GetProximityCounter().getMobWithinThosesDistances(-1, 64 * 16);// MG TODO : deal with this magic number (the 16 is the size of a block) + for(cMobProximityCounter::tDistanceToMonster::const_iterator itr = allCloseEnoughToMoveMobs.m_Begin; itr != allCloseEnoughToMoveMobs.m_End; itr++) + { + itr->second.m_Monster.Tick(a_Dt, itr->second.m_Chunk); + } - if (MobType >= 0) + // remove too far mobs + cMobProximityCounter::sIterablePair allTooFarMobs = MobCensus.GetProximityCounter().getMobWithinThosesDistances(128 * 16, -1);// MG TODO : deal with this magic number (the 16 is the size of a block) + for(cMobProximityCounter::tDistanceToMonster::const_iterator itr = allTooFarMobs.m_Begin; itr != allTooFarMobs.m_End; itr++) { - // A proper mob type was selected, now spawn the mob: - SpawnMob(SpawnPos.x, SpawnPos.y, SpawnPos.z, (cMonster::eType)MobType); + itr->second.m_Monster.Destroy(true); } } @@ -2592,82 +2529,39 @@ int cWorld::SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eTyp { cMonster * Monster = NULL; - int SlSize = GetTickRandomNumber(2) + 1; // 1 .. 3 - Slime int ShColor = GetTickRandomNumber(15); // 0 .. 15 - Sheep - bool SkType = GetDimension() == biNether; // Skeleton + bool SkType = GetDimension() == dimNether ; // Skeleton - int VilType = GetTickRandomNumber(cVillager::vtMax); // 0 .. 6 - Villager - if (VilType == 6) { VilType = 0; } // Give farmers a better chance of spawning - - int HseType = GetTickRandomNumber(7); // 0 .. 7 - Horse Type (donkey, zombie, etc.) - int HseColor = GetTickRandomNumber(6); // 0 .. 6 - Horse - int HseStyle = GetTickRandomNumber(4); // 0 .. 4 - Horse - int HseTameTimes = GetTickRandomNumber(6) + 1; // 1 .. 7 - Horse tame amount - if ((HseType == 5) || (HseType == 6) || (HseType == 7)) { HseType = 0; } // 5,6,7 = 0 because little chance of getting 0 with TickRand - - switch (a_MonsterType) - { - case cMonster::mtBat: Monster = new cBat(); break; - case cMonster::mtBlaze: Monster = new cBlaze(); break; - case cMonster::mtCaveSpider: Monster = new cCavespider(); break; - case cMonster::mtChicken: Monster = new cChicken(); break; - case cMonster::mtCow: Monster = new cCow(); break; - case cMonster::mtCreeper: Monster = new cCreeper(); break; - case cMonster::mtEnderman: Monster = new cEnderman(); break; - case cMonster::mtEnderDragon: Monster = new cEnderDragon(); break; - case cMonster::mtGhast: Monster = new cGhast(); break; - case cMonster::mtGiant: Monster = new cGiant(); break; - case cMonster::mtHorse: - { - Monster = new cHorse(HseType, HseColor, HseStyle, HseTameTimes); break; - } - case cMonster::mtIronGolem: Monster = new cIronGolem(); break; - case cMonster::mtMagmaCube: Monster = new cMagmaCube(SlSize); break; - case cMonster::mtMooshroom: Monster = new cMooshroom(); break; - case cMonster::mtOcelot: Monster = new cOcelot(); break; - case cMonster::mtPig: Monster = new cPig(); break; - case cMonster::mtSheep: Monster = new cSheep(ShColor); break; - case cMonster::mtSilverfish: Monster = new cSilverfish(); break; - case cMonster::mtSkeleton: Monster = new cSkeleton(SkType); break; - case cMonster::mtSlime: Monster = new cSlime(SlSize); break; - case cMonster::mtSnowGolem: Monster = new cSnowGolem(); break; - case cMonster::mtSpider: Monster = new cSpider(); break; - case cMonster::mtSquid: Monster = new cSquid(); break; - case cMonster::mtVillager: - { - Monster = new cVillager((cVillager::eVillagerType)VilType); break; - } - case cMonster::mtWitch: Monster = new cWitch(); break; - case cMonster::mtWither: Monster = new cWither(); break; - case cMonster::mtWolf: Monster = new cWolf(); break; - case cMonster::mtZombie: Monster = new cZombie(false); break; // TODO: Villager infection - case cMonster::mtZombiePigman: Monster = new cZombiePigman(); break; - - default: - { - LOGWARNING("%s: Unhandled monster type: %d. Not spawning.", __FUNCTION__, a_MonsterType); - return -1; - } + Monster = cMonster::NewMonsterFromType(a_MonsterType); + if (Monster != NULL) + { + Monster->SetPosition(a_PosX, a_PosY, a_PosZ); } - Monster->SetPosition(a_PosX, a_PosY, a_PosZ); - Monster->SetHealth(Monster->GetMaxHealth()); - if (cPluginManager::Get()->CallHookSpawningMonster(*this, *Monster)) + return SpawnMobFinalize(Monster); +} + + + + +int cWorld::SpawnMobFinalize(cMonster * a_Monster) +{ + if (!a_Monster) + return -1; + a_Monster->SetHealth(a_Monster->GetMaxHealth()); + if (cPluginManager::Get()->CallHookSpawningMonster(*this, *a_Monster)) { - delete Monster; + delete a_Monster; return -1; } - if (!Monster->Initialize(this)) + if (!a_Monster->Initialize(this)) { - delete Monster; + delete a_Monster; return -1; } + BroadcastSpawnEntity(*a_Monster); + cPluginManager::Get()->CallHookSpawnedMonster(*this, *a_Monster); - BroadcastSpawnEntity(*Monster); - // Because it's logical that ALL mob spawns need spawn effects, not just spawners - BroadcastSoundParticleEffect(2004, (int)(floor(a_PosX) * 8), (int)(floor(a_PosY) * 8), (int)(floor(a_PosZ) * 8), 0); - - cPluginManager::Get()->CallHookSpawnedMonster(*this, *Monster); - return Monster->GetUniqueID(); + return a_Monster->GetUniqueID(); } diff --git a/source/World.h b/source/World.h index 633ce969e..f174a1c2c 100644 --- a/source/World.h +++ b/source/World.h @@ -42,6 +42,7 @@ class cChunkGenerator; // The thread responsible for generating chunks class cChestEntity;
class cDispenserEntity;
class cFurnaceEntity;
+class cMobCensus;
typedef std::list< cPlayer * > cPlayerList;
@@ -580,6 +581,7 @@ public: /// Spawns a mob of the specified type. Returns the mob's EntityID if recognized and spawned, <0 otherwise
int SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eType a_MonsterType); // tolua_export
+ int SpawnMobFinalize(cMonster* a_Monster);
/// Creates a projectile of the specified type. Returns the projectile's EntityID if successful, <0 otherwise
int CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const Vector3d * a_Speed = NULL); // tolua_export
@@ -632,7 +634,7 @@ private: Int64 m_LastTimeUpdate; // The tick in which the last time update has been sent.
Int64 m_LastUnload; // The last WorldAge (in ticks) in which unloading was triggerred
Int64 m_LastSave; // The last WorldAge (in ticks) in which save-all was triggerred
- Int64 m_LastSpawnMonster; // The last WorldAge (in ticks) in which a monster was spawned
+ std::map<cMonster::eFamily,Int64> m_LastSpawnMonster; // The last WorldAge (in ticks) in which a monster was spawned (for each megatype of monster) // MG TODO : find a way to optimize without creating unmaintenability (if mob IDs are becoming unrowed)
eGameMode m_GameMode;
bool m_bEnabledPVP;
@@ -662,7 +664,7 @@ private: cChunkMap * m_ChunkMap;
bool m_bAnimals;
- Int64 m_SpawnMonsterRate;
+ std::set<cMonster::eType> m_AllowedMobs;
eWeather m_Weather;
int m_WeatherInterval;
@@ -717,8 +719,8 @@ private: /// Handles the weather in each tick
void TickWeather(float a_Dt);
- /// Handles the mob spawning each tick
- void TickSpawnMobs(float a_Dt);
+ /// Handles the mob spawning/moving/destroying each tick
+ void TickMobs(float a_Dt);
/// Executes all tasks queued onto the tick thread
void TickQueuedTasks(void);
|