static int l_anim_get_tile(lua_State *L) { THAnimation* pAnimation = luaT_testuserdata<THAnimation>(L); lua_settop(L, 1); lua_getfenv(L, 1); lua_getfield(L, 2, "map"); lua_replace(L, 2); if(lua_isnil(L, 2)) { return 0; } THMap* pMap = (THMap*)lua_touserdata(L, 2); const THLinkList* pListNode = pAnimation->getPrevious(); while(pListNode->m_pPrev) { pListNode = pListNode->m_pPrev; } // Casting pListNode to a THMapNode* is slightly dubious, but it should // work. If on the normal list, then pListNode will be a THMapNode*, and // all is fine. However, if on the early list, pListNode will be pointing // to a member of a THMapNode, so we're relying on pointer arithmetic // being a subtract and integer divide by sizeof(THMapNode) to yield the // correct map node. const THMapNode *pRootNode = pMap->getNodeUnchecked(0, 0); uintptr_t iDiff = reinterpret_cast<const char*>(pListNode) - reinterpret_cast<const char*>(pRootNode); int iIndex = (int)(iDiff / sizeof(THMapNode)); int iY = iIndex / pMap->getWidth(); int iX = iIndex - (iY * pMap->getWidth()); lua_pushinteger(L, iX + 1); lua_pushinteger(L, iY + 1); return 3; // map, x, y }
static int l_map_unmark_room(lua_State *L) { THMap* pMap = luaT_testuserdata<THMap>(L); int iX_ = static_cast<int>(luaL_checkinteger(L, 2) - 1); int iY_ = static_cast<int>(luaL_checkinteger(L, 3) - 1); int iW = static_cast<int>(luaL_checkinteger(L, 4)); int iH = static_cast<int>(luaL_checkinteger(L, 5)); if(iX_ < 0 || iY_ < 0 || (iX_ + iW) > pMap->getWidth() || (iY_ + iH) > pMap->getHeight()) luaL_argerror(L, 2, "Rectangle is out of bounds"); for(int iY = iY_; iY < iY_ + iH; ++iY) { for(int iX = iX_; iX < iX_ + iW; ++iX) { THMapNode *pNode = pMap->getNodeUnchecked(iX, iY); pNode->iBlock[0] = pMap->getOriginalNodeUnchecked(iX, iY)->iBlock[0]; pNode->flags.room = false; pNode->iRoomId = 0; } } pMap->updatePathfinding(); pMap->updateShadows(); lua_settop(L, 1); return 1; }
static int l_map_mark_room(lua_State *L) { THMap* pMap = luaT_testuserdata<THMap>(L); int iX_ = static_cast<int>(luaL_checkinteger(L, 2) - 1); int iY_ = static_cast<int>(luaL_checkinteger(L, 3) - 1); int iW = static_cast<int>(luaL_checkinteger(L, 4)); int iH = static_cast<int>(luaL_checkinteger(L, 5)); uint16_t iTile = static_cast<uint16_t>(luaL_checkinteger(L, 6)); uint16_t iRoomId = static_cast<uint16_t>(luaL_optinteger(L, 7, 0)); if(iX_ < 0 || iY_ < 0 || (iX_ + iW) > pMap->getWidth() || (iY_ + iH) > pMap->getHeight()) luaL_argerror(L, 2, "Rectangle is out of bounds"); for(int iY = iY_; iY < iY_ + iH; ++iY) { for(int iX = iX_; iX < iX_ + iW; ++iX) { THMapNode *pNode = pMap->getNodeUnchecked(iX, iY); pNode->iBlock[0] = iTile; pNode->iBlock[3] = 0; pNode->flags.room = true; pNode->flags.passable |= pNode->flags.passable_if_not_for_blueprint; pNode->flags.passable_if_not_for_blueprint = false; pNode->iRoomId = iRoomId; } } pMap->updatePathfinding(); pMap->updateShadows(); lua_settop(L, 1); return 1; }
/* Compute the fraction of corridor tiles with litter, of the parcels owned by the given player. */ static int l_map_get_litter_fraction(lua_State *L) { THMap* pMap = luaT_testuserdata<THMap>(L); int owner = static_cast<int>(luaL_checkinteger(L, 2)); if (owner == 0) { lua_pushnumber(L, 0.0); // Outside has no litter. return 1; } double tile_count = 0; double litter_count = 0; for (int x = 0; x < pMap->getWidth(); x++) { for (int y = 0; y < pMap->getHeight(); y++) { const THMapNode* pNode = pMap->getNodeUnchecked(x, y); if (pNode->iParcelId == 0 || owner != pMap->getParcelOwner(pNode->iParcelId) || pNode->iRoomId != 0) { continue; } tile_count++; for(auto iter = pNode->objects.begin(); iter != pNode->objects.end(); iter++) { if(*iter == THObjectType::litter) { litter_count++; break; } } } } double fraction = (tile_count == 0) ? 0.0 : litter_count / tile_count; lua_pushnumber(L, fraction); return 1; }
static int l_map_updateblueprint(lua_State *L) { // NB: This function can be implemented in Lua, but is implemented in C for // efficiency. const unsigned short iFloorTileGood = 24 + (THDF_Alpha50 << 8); const unsigned short iFloorTileGoodCenter = 37 + (THDF_Alpha50 << 8); const unsigned short iFloorTileBad = 67 + (THDF_Alpha50 << 8); const unsigned int iWallAnimTopCorner = 124; const unsigned int iWallAnim = 120; THMap* pMap = luaT_testuserdata<THMap>(L); int iOldX = static_cast<int>(luaL_checkinteger(L, 2)) - 1; int iOldY = static_cast<int>(luaL_checkinteger(L, 3)) - 1; int iOldW = static_cast<int>(luaL_checkinteger(L, 4)); int iOldH = static_cast<int>(luaL_checkinteger(L, 5)); int iNewX = static_cast<int>(luaL_checkinteger(L, 6)) - 1; int iNewY = static_cast<int>(luaL_checkinteger(L, 7)) - 1; int iNewW = static_cast<int>(luaL_checkinteger(L, 8)); int iNewH = static_cast<int>(luaL_checkinteger(L, 9)); luaL_checktype(L, 10, LUA_TTABLE); // Animation list THAnimationManager* pAnims = luaT_testuserdata<THAnimationManager>(L, 11, luaT_upvalueindex(1)); bool entire_invalid = lua_toboolean(L, 12) != 0; bool valid = !entire_invalid; if(iOldX < 0 || iOldY < 0 || (iOldX + iOldW) > pMap->getWidth() || (iOldY + iOldH) > pMap->getHeight()) luaL_argerror(L, 2, "Old rectangle is out of bounds"); if(iNewX < 0 || iNewY < 0 || (iNewX + iNewW) >= pMap->getWidth() || (iNewY + iNewH) >= pMap->getHeight()) luaL_argerror(L, 6, "New rectangle is out of bounds"); // Clear blueprint flag from previous selected floor tiles (copying it to the passable flag). for(int iY = iOldY; iY < iOldY + iOldH; ++iY) { for(int iX = iOldX; iX < iOldX + iOldW; ++iX) { THMapNode *pNode = pMap->getNodeUnchecked(iX, iY); pNode->iBlock[3] = 0; uint32_t iFlags = pNode->iFlags; iFlags |= ((iFlags & THMN_PassableIfNotForBlueprint) != 0) ? THMN_Passable : 0; iFlags &= ~THMN_PassableIfNotForBlueprint; pNode->iFlags = iFlags; } } // Add blueprint flag to new floor tiles. for(int iY = iNewY; iY < iNewY + iNewH; ++iY) { for(int iX = iNewX; iX < iNewX + iNewW; ++iX) { THMapNode *pNode = pMap->getNodeUnchecked(iX, iY); if(is_valid(entire_invalid, pNode)) pNode->iBlock[3] = iFloorTileGood; else { pNode->iBlock[3] = iFloorTileBad; valid = false; } pNode->iFlags |= ((pNode->iFlags & THMN_Passable) != 0) ? THMN_PassableIfNotForBlueprint : 0; } } // Set center floor tiles if(iNewW >= 2 && iNewH >= 2) { int iCenterX = iNewX + (iNewW - 2) / 2; int iCenterY = iNewY + (iNewH - 2) / 2; THMapNode *pNode = pMap->getNodeUnchecked(iCenterX, iCenterY); if(pNode->iBlock[3] == iFloorTileGood) pNode->iBlock[3] = iFloorTileGoodCenter + 2; pNode = pMap->getNodeUnchecked(iCenterX + 1, iCenterY); if(pNode->iBlock[3] == iFloorTileGood) pNode->iBlock[3] = iFloorTileGoodCenter + 1; pNode = pMap->getNodeUnchecked(iCenterX, iCenterY + 1); if(pNode->iBlock[3] == iFloorTileGood) pNode->iBlock[3] = iFloorTileGoodCenter + 0; pNode = pMap->getNodeUnchecked(iCenterX + 1, iCenterY + 1); if(pNode->iBlock[3] == iFloorTileGood) pNode->iBlock[3] = iFloorTileGoodCenter + 3; } // Set wall animations int iNextAnim = 1; THAnimation *pAnim = l_map_updateblueprint_getnextanim(L, iNextAnim); THMapNode *pNode = pMap->getNodeUnchecked(iNewX, iNewY); pAnim->setAnimation(pAnims, iWallAnimTopCorner); pAnim->setFlags(THDF_ListBottom | (is_valid(entire_invalid, pNode) ? 0 : THDF_AltPalette)); pAnim->attachToTile(pNode, 0); for(int iX = iNewX; iX < iNewX + iNewW; ++iX) { if(iX != iNewX) { pAnim = l_map_updateblueprint_getnextanim(L, iNextAnim); pNode = pMap->getNodeUnchecked(iX, iNewY); pAnim->setAnimation(pAnims, iWallAnim); pAnim->setFlags(THDF_ListBottom | (is_valid(entire_invalid, pNode) ? 0 : THDF_AltPalette)); pAnim->attachToTile(pNode, 0); pAnim->setPosition(0, 0); } pAnim = l_map_updateblueprint_getnextanim(L, iNextAnim); pNode = pMap->getNodeUnchecked(iX, iNewY + iNewH - 1); pAnim->setAnimation(pAnims, iWallAnim); pAnim->setFlags(THDF_ListBottom | (is_valid(entire_invalid, pNode) ? 0 : THDF_AltPalette)); pNode = pMap->getNodeUnchecked(iX, iNewY + iNewH); pAnim->attachToTile(pNode, 0); pAnim->setPosition(0, -1); } for(int iY = iNewY; iY < iNewY + iNewH; ++iY) { if(iY != iNewY) { pAnim = l_map_updateblueprint_getnextanim(L, iNextAnim); pNode = pMap->getNodeUnchecked(iNewX, iY); pAnim->setAnimation(pAnims, iWallAnim); pAnim->setFlags(THDF_ListBottom | THDF_FlipHorizontal | (is_valid(entire_invalid, pNode) ? 0 : THDF_AltPalette)); pAnim->attachToTile(pNode, 0); pAnim->setPosition(2, 0); } pAnim = l_map_updateblueprint_getnextanim(L, iNextAnim); pNode = pMap->getNodeUnchecked(iNewX + iNewW - 1, iY); pAnim->setAnimation(pAnims, iWallAnim); pAnim->setFlags(THDF_ListBottom | THDF_FlipHorizontal | (is_valid(entire_invalid, pNode) ? 0 : THDF_AltPalette)); pNode = pMap->getNodeUnchecked(iNewX + iNewW, iY); pAnim->attachToTile(pNode, 0); pAnim->setPosition(2, -1); } // Clear away extra animations int iAnimCount = (int)lua_objlen(L, 10); if(iAnimCount >= iNextAnim) { for(int i = iNextAnim; i <= iAnimCount; ++i) { pAnim = l_map_updateblueprint_getnextanim(L, iNextAnim); pAnim->removeFromTile(); lua_pushnil(L); lua_rawseti(L, 10, i); } } lua_pushboolean(L, valid ? 1 : 0); return 1; }
static int l_town_map_draw(lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); THMap *pMap = luaT_testuserdata<THMap>(L, 2); THRenderTarget *pCanvas = luaT_testuserdata<THRenderTarget>(L, 3); int iCanvasXBase = luaL_checkint(L, 4); int iCanvasYBase = luaL_checkint(L, 5); bool bShowHeat = lua_toboolean(L, 6) != 0; uint32_t iColourMyHosp = pCanvas->mapColour(0, 0, 70); uint32_t iColourWall = pCanvas->mapColour(255, 255, 255); uint32_t iColourDoor = pCanvas->mapColour(200, 200, 200); uint32_t iColourPurchasable = pCanvas->mapColour(255, 0, 0); const THMapNode *pNode = pMap->getNodeUnchecked(0, 0); const THMapNode *pOriginalNode = pMap->getOriginalNodeUnchecked(0, 0); int iCanvasY = iCanvasYBase + 3; for(int iY = 0; iY < pMap->getHeight(); ++iY, iCanvasY += 3) { int iCanvasX = iCanvasXBase; for(int iX = 0; iX < pMap->getWidth(); ++iX, ++pNode, ++pOriginalNode, iCanvasX += 3) { if(pOriginalNode->iFlags & THMN_Hospital) { uint32_t iColour = iColourMyHosp; if(!(pNode->iFlags & THMN_Hospital)) { // TODO: Replace 1 with player number if(pMap->isParcelPurchasable(pNode->iParcelId, 1)) iColour = iColourPurchasable; else goto dont_paint_tile; } else if(bShowHeat) { uint16_t iTemp = pMap->getNodeTemperature(pNode); if(iTemp < 5200) // Less than 4 degrees iTemp = 0; else if(iTemp > 32767) // More than 25 degrees iTemp = 255; else // NB: 108 == (32767 - 5200) / 255 iTemp = (iTemp - 5200) / 108; iColour = pCanvas->mapColour(static_cast<uint8_t>(iTemp), 0, 70); } pCanvas->fillRect(iColour, iCanvasX, iCanvasY, 3, 3); } dont_paint_tile: #define IsWall(blk) ((82 <= ((blk) & 0xFF)) && (((blk) & 0xFF) <= 164)) #define IsWallDrawn(n) pMap->getNodeOwner(pNode) != 0 ? \ IsWall(pNode->iBlock[n]) : IsWall(pOriginalNode->iBlock[n]) if(IsWallDrawn(1)) pCanvas->fillRect(iColourWall, iCanvasX, iCanvasY, 3, 1); if(IsWallDrawn(2)) pCanvas->fillRect(iColourWall, iCanvasX, iCanvasY, 1, 3); #undef IsWallDrawn #undef IsWall if(pNode->iFlags & THMN_DoorNorth) pCanvas->fillRect(iColourDoor, iCanvasX, iCanvasY - 2, 2, 3); if(pNode->iFlags & THMN_DoorWest) pCanvas->fillRect(iColourDoor, iCanvasX - 3, iCanvasY, 3, 2); } } return 0; }
static int l_town_map_draw(lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); THMap *pMap = luaT_testuserdata<THMap>(L, 2); THRenderTarget *pCanvas = luaT_testuserdata<THRenderTarget>(L, 3); int iCanvasXBase = luaL_checkint(L, 4); int iCanvasYBase = luaL_checkint(L, 5); bool bShowHeat = lua_toboolean(L, 6) != 0; uint32_t iColourMyHosp = pCanvas->mapColour(0, 0, 70); uint32_t iColourWall = pCanvas->mapColour(255, 255, 255); uint32_t iColourDoor = pCanvas->mapColour(200, 200, 200); uint32_t iColourPurchasable = pCanvas->mapColour(255, 0, 0); const THMapNode *pNode = pMap->getNodeUnchecked(0, 0); const THMapNode *pOriginalNode = pMap->getOriginalNodeUnchecked(0, 0); int iCanvasY = iCanvasYBase + 3; int iMapWidth = pMap->getWidth(); for(int iY = 0; iY < pMap->getHeight(); ++iY, iCanvasY += 3) { int iCanvasX = iCanvasXBase; for(int iX = 0; iX < iMapWidth; ++iX, ++pNode, ++pOriginalNode, iCanvasX += 3) { if(pOriginalNode->iFlags & THMN_Hospital) { uint32_t iColour = iColourMyHosp; if(!(pNode->iFlags & THMN_Hospital)) { // TODO: Replace 1 with player number if(pMap->isParcelPurchasable(pNode->iParcelId, 1)) iColour = iColourPurchasable; else goto dont_paint_tile; } else if(bShowHeat) { uint16_t iTemp = pMap->getNodeTemperature(pNode); if(iTemp < 5200) // Less than 4 degrees iTemp = 0; else if(iTemp > 32767) // More than 25 degrees iTemp = 255; else // NB: 108 == (32767 - 5200) / 255 iTemp = (iTemp - 5200) / 108; #define MIN_OK_TEMP 140 #define MAX_OK_TEMP 180 #define RangeScale(low, high, val, start, end) \ Clamp8(start + (end - start) * (val - low) / (high - low)) switch(pMap->getTemperatureDisplay()) { case THMT_MultiColour: { uint8_t iR = 0; uint8_t iG = 0; uint8_t iB = 70; if(iTemp < MIN_OK_TEMP) iB = RangeScale(0, MIN_OK_TEMP - 1, iTemp, 200, 60); else if(iTemp < MAX_OK_TEMP) iG = RangeScale(MIN_OK_TEMP, MAX_OK_TEMP - 1, iTemp, 140, 224); else iR = RangeScale(MAX_OK_TEMP, 255, iTemp, 224, 255); iColour = pCanvas->mapColour(iR, iG, iB); break; } case THMT_YellowRed: if(iTemp < MIN_OK_TEMP) // Below 11 degrees { uint8_t iR = RangeScale(0, MIN_OK_TEMP - 1, iTemp, 100, 213); uint8_t iG = RangeScale(0, MIN_OK_TEMP - 1, iTemp, 80, 180); iColour = pCanvas->mapColour(iR, iG, 0); } else { uint8_t iR = RangeScale(MIN_OK_TEMP, 255, iTemp, 223, 235); uint8_t iG = RangeScale(MIN_OK_TEMP, 255, iTemp, 184, 104); uint8_t iB = RangeScale(MIN_OK_TEMP, 255, iTemp, 0, 53); iColour = pCanvas->mapColour(iR, iG, iB); } break; default: case THMT_Red: iColour = pCanvas->mapColour(static_cast<uint8_t>(iTemp), 0, 70); break; } #undef RangeScale } pCanvas->fillRect(iColour, iCanvasX, iCanvasY, 3, 3); } dont_paint_tile: #define IsWall(blk) ((82 <= ((blk) & 0xFF)) && (((blk) & 0xFF) <= 164)) #define IsWallDrawn(n) pMap->getNodeOwner(pNode) != 0 ? \ IsWall(pNode->iBlock[n]) : IsWall(pOriginalNode->iBlock[n]) if(IsWallDrawn(1)) { pCanvas->fillRect(iColourWall, iCanvasX, iCanvasY, 3, 1); // Draw entrance door if((pNode-1)->iFlags >> 24 == THOB_EntranceRightDoor) { if (pNode->iFlags & THMN_Hospital) { pCanvas->fillRect(iColourDoor, iCanvasX-6, iCanvasY-2, 9, 3); } else { pCanvas->fillRect(iColourDoor, iCanvasX-6, iCanvasY, 9, 3); } } } if(IsWallDrawn(2)) { pCanvas->fillRect(iColourWall, iCanvasX, iCanvasY, 1, 3); // Draw entrance door if((pNode-iMapWidth)->iFlags >> 24 == THOB_EntranceRightDoor) { if (pNode->iFlags & THMN_Hospital) { pCanvas->fillRect(iColourDoor, iCanvasX-2, iCanvasY-6, 3, 9); } else { pCanvas->fillRect(iColourDoor, iCanvasX, iCanvasY-6, 3, 9); } } } #undef IsWallDrawn #undef IsWall }