view Engine/Graphics/Outdoor.cpp @ 2541:a902abdfc7f2

1. Renamed class Game to class Engine. 2. Separated game logic as state of FSM from game logic as engine. 3. Found out that many UI screen initializers were optimized away, intially they all returned newly created window as separate object like it is done in CharacterUI_Initialize.
author a.parshin
date Sun, 10 May 2015 01:29:11 +0200
parents 1bcadc6dd203
children b6140dfeac27
line wrap: on
line source

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

#define _CRT_SECURE_NO_WARNINGS

#include "Engine/Engine.h"

#include "stru6.h"
#include "Weather.h"
#include "Sprites.h"
#include "LightmapBuilder.h"
#include "Outdoor.h"
#include "Engine/Party.h"
#include "Engine/Objects/SpriteObject.h"
#include "Engine/LOD.h"
#include "PaletteManager.h"
#include "GUI/GUIProgressBar.h"
#include "Media/Audio/AudioPlayer.h"
#include "DecorationList.h"
#include "Engine/OurMath.h"
#include "Engine/Objects/ObjectList.h"
#include "Engine/Objects/Actor.h"
#include "Engine/Objects/Chest.h"
#include "Engine/stru123.h"
#include "Engine/Timer.h"
#include "Viewport.h"
#include "Engine/Events.h"
#include "ParticleEngine.h"
#include "Engine/TurnEngine/TurnEngine.h"

#include "Lights.h"

#include "GUI/GUIWindow.h"
#include "Level/Decoration.h"
#include "Engine/ZlibWrapper.h"
#include "Engine/MMT.h"

MapStartPoint uLevel_StartingPointType; // weak

OutdoorLocation *pOutdoor = new OutdoorLocation;
ODMRenderParams *pODMRenderParams;


stru149 stru_8019C8;
std::array<struct Polygon, 2000 + 18000> array_77EC08;


struct FogProbabilityTableEntry
{
  unsigned char small_fog_chance;
  unsigned char average_fog_chance;
  unsigned char dense_fog_chance;
  unsigned char __unused;
} fog_probability_table[15] =
{
  { 20,  10,   5,   0}, // MAP_EMERALD_ISLE
  { 20,  10,   5,   0}, // MAP_HARMONDALE
  { 20,  10,   5,   0}, // MAP_STEADWICK
  { 20,  10,   5,   0}, // MAP_PIERPONT
  { 20,  10,   5,   0}, // MAP_DEYJA
  { 10,   5,   0,   0}, // MAP_BRAKADA_DESERT
  {  0,   0,   0,   0}, // MAP_CELESTIA
  {  0,   0,   0,   0}, // MAP_THE_PIT
  { 20,  30,  50,   0}, // MAP_EVENMORN_ISLE
  { 30,  20,  10,   0}, // MAP_MOUNT_NIGHON
  { 10,   5,   0,   0}, // MAP_BARROW_DOWNS
  { 20,  10,   5,   0}, // MAP_LAND_OF_GIANTS
  { 20,  10,   5,   0}, // MAP_TATALIA
  { 20,  10,   5,   0}, // MAP_AVLEE
  {  0, 100,   0,   0}  // MAP_SHOALS
};

//for future sky textures?
std::array<int, 9> dword_4EC268={{3,3,3,3,3,3,3,3,3}}; // weak
std::array<int, 7> dword_4EC28C={{3,3,3,3,3,3,3}}; // weak
int dword_4EC2A8=9; // weak
int dword_4EC2AC=7; // weak



//----- (0047A59E) --------------------------------------------------------
void OutdoorLocation::ExecDraw(unsigned int bRedraw)
{

  pEngine->pIndoorCameraD3D->debug_flags = 0;
  if (viewparams->draw_d3d_outlines)
    pEngine->pIndoorCameraD3D->debug_flags |= ODM_RENDER_DRAW_D3D_OUTLINES;

  if (bRedraw || true/*pRenderer->pRenderD3D*/)
  {
    //pODMRenderParams->RotationToInts();
    sub_481ED9_MessWithODMRenderParams();
  }

  pODMRenderParams->uMapGridCellX = WorldPosToGridCellX(pParty->vPosition.x);
  pODMRenderParams->uMapGridCellZ = WorldPosToGridCellZ(pParty->vPosition.y);
  assert(pODMRenderParams->uMapGridCellX <= 127 && pODMRenderParams->uMapGridCellZ <= 127);

  if (bRedraw)
  {
    sub_487DA9();
  }
  if ( pParty->uCurrentMinute != pOutdoor->uLastSunlightUpdateMinute )
    pOutdoor->UpdateSunlightVectors();
  pOutdoor->UpdateFog();
  //pEngine->pIndoorCameraD3D->sr_Reset_list_0037C();

  //if (pRenderer->pRenderD3D) // d3d - redraw always
  {
    pRenderer->DrawOutdoorSkyD3D();
    pRenderer->DrawBuildingsD3D();
    pRenderer->RenderTerrainD3D();
    //pRenderer->DrawBezierTerrain();
  }
  /*else
  {
    if (!bRedraw)
      pRenderer->OnOutdoorRedrawSW();
    else
    {
      pRenderer->DrawBuildingsSW();  
      pRenderer->DrawBezierTerrain();
      sr_sub_486F92_MessWithEdgesAndSpans();
      pODMRenderParams->ApplyLightmapsSW();
    }
  }*/


  pMobileLightsStack->uNumLightsActive = 0;
  pStationaryLightsStack->uNumLightsActive = 0;
  /*if ( !pRenderer->pRenderD3D )
  {
    pRenderer->ExecOutdoorDrawSW();
    pEngine->pIndoorCameraD3D->sr_438240_draw_lits();
  }*/
  pEngine->PushStationaryLights(-1);
  pEngine->PrepareBloodsplats();
  if (bRedraw)
    pOutdoor->UpdateDiscoveredArea(WorldPosToGridCellX(pParty->vPosition.x), WorldPosToGridCellZ(pParty->vPosition.y), 1);
  pEngine->uFlags2 &= 0xFFFFFFFEu;//~0x00000001
  if (/*pRenderer->pRenderD3D*/true && pRenderer->bUsingSpecular)
    pEngine->pLightmapBuilder->uFlags |= 1;
  else
    pEngine->pLightmapBuilder->uFlags &= 0xFFFFFFFEu;//~0x00000001

  uNumDecorationsDrawnThisFrame = 0;
  uNumSpritesDrawnThisFrame = 0;
  uNumBillboardsToDraw = 0;

  PrepareActorsDrawList();
  if (!pODMRenderParams->bDoNotRenderDecorations)
    pRenderer->PrepareDecorationsRenderList_ODM();

  pRenderer->DrawSpriteObjects_ODM();
  pRenderer->TransformBillboardsAndSetPalettesODM();
}


//----- (00441CFF) --------------------------------------------------------
void OutdoorLocation::Draw()
{
  bool redrawWorld = true;
  if ( !(pParty->uFlags & 2) && !(pEngine->uFlags2 & 1) )
    redrawWorld = false;
  pOutdoor->ExecDraw(redrawWorld);

  pEngine->DrawParticles();
  //pWeather->Draw();//если раскомментировать скорость снега быстрее
  trail_particle_generator.UpdateParticles();
}

//----- (00488E23) --------------------------------------------------------
double OutdoorLocation::GetFogDensityByTime()
{
  if ( pParty->uCurrentHour < 5 )//ночь
  {
    pWeather->bNight = true;
    return 60.0 * 0.016666668;
  }
  else if ( pParty->uCurrentHour >= 5 && pParty->uCurrentHour < 6 )//рассвет
  {
    pWeather->bNight = false;
    return (60.0 - (double)(60 * pParty->uCurrentHour + pParty->uCurrentMinute - 300)) * 0.016666668;
  }
  else if ( pParty->uCurrentHour >= 6 && pParty->uCurrentHour < 20 )//день
  {
    pWeather->bNight = false;
     return 0.0;
  }
  else if ( pParty->uCurrentHour >= 20 && pParty->uCurrentHour < 21 )//сумерки
  {
    pWeather->bNight = false;
    return ((double)(pParty->uCurrentHour - 20) * 60.0 + (double)(signed int)pParty->uCurrentMinute) * 0.016666668;
  }
  else//ночь
  {
    pWeather->bNight = true;
    return 60.0 * 0.016666668;
  }
}

//----- (00488EB1) --------------------------------------------------------
int OutdoorLocation::GetSomeOtherTileInfo(int sX, int sY)
{
  //OutdoorLocation *v3; // esi@1
  unsigned int v4; // edi@1
  unsigned int v5; // eax@1
//  int result; // eax@5

/*  v3 = this;
  v4 = WorldPosToGridCellZ(sY);
  v5 = WorldPosToGridCellX(sX);
  if ( (v5 & 0x80000000u) != 0 || (signed int)v5 > 127 || (v4 & 0x80000000u) != 0 || (signed int)v4 > 127 )
    result = 0;
  else
    result = ActuallyGetSomeOtherTileInfo(v5, v4);
  return result;*/
 v4 = WorldPosToGridCellZ(sY);
 v5 = WorldPosToGridCellX(sX);
 if ( v5 < 0 || v5 > 127 || v4 < 0 || v4 > 127 )
   return 0;
 return ActuallyGetSomeOtherTileInfo(v5, v4);
}
// 47F44B: using guessed type int __stdcall WorldPosToGridCellX(int);
// 47F458: using guessed type int __stdcall WorldPosToGridCellZ(int);

//----- (00488EEF) --------------------------------------------------------
unsigned int OutdoorLocation::GetTileTexture(signed int sX, signed int sY)
{
  //OutdoorLocation *v3; // esi@1
  signed int v4; // edi@1
  signed int v5; // eax@1
//  unsigned int result; // eax@5

  /*v3 = this;
  v4 = WorldPosToGridCellZ(sZ);
  v5 = WorldPosToGridCellX(sX);
  if ( v5< 0 || (signed int)v5 > 127 || v4 < 0 || (signed int)v4 > 127 )//if ( (v5 & 0x80000000u) != 0 || (signed int)v5 > 127 || (v4 & 0x80000000u) != 0 || (signed int)v4 > 127 )
    result = -1;
  else
    result = DoGetTileTexture(v5, v4);
  return result;*/
  v4 = WorldPosToGridCellZ(sY);
  v5 = WorldPosToGridCellX(sX);
  if ( v5 < 0 || v5 > 127 || v4 < 0 || v4 > 127 )
    return -1;
  return DoGetTileTexture(v5, v4);
}
// 47F44B: using guessed type int __stdcall WorldPosToGridCellX(int);
// 47F458: using guessed type int __stdcall WorldPosToGridCellZ(int);

//----- (00488F2E) --------------------------------------------------------
int OutdoorLocation::GetHeightOnTerrain(int sX, int sZ)
   /* Функция предоставляет возможность перемещать камеру таким образом, чтобы она имитировала ходьбу по ландшафту. 
   То есть нам надо менять высоту камеры (координату Y) в зависимости от того, в каком месте ландшафта мы находимся. 
   Для этого мы сначала должны определить по координатам X и Z камеры квадрат ландшафта в котором мы находимся. 
   Все это делает функция Terrain::getHeight; в своих параметрах она получает координаты X и Z камеры и возвращает высоту, 
   на которой должна быть расположена камера, чтобы она оказалась над ландшафтом.*/
{
  int result; // eax@5

  if ( sX < 0 || sX > 127 || sZ < 0 || sZ > 127 )
    result = 0;
  else
    result = DoGetHeightOnTerrain(sX, sZ);
  return result;
}

//----- (00488F5C) --------------------------------------------------------
bool OutdoorLocation::Initialize(const char *pFilename, int File, size_t uRespawnInterval, int *thisa)
{
  OutdoorLocation *v5; // esi@1
  bool result; // eax@2

  v5 = this;
  if ( pFilename )
  {
    Release();
    pBitmaps_LOD->ReleaseAll2();
    pSprites_LOD->DeleteSomeOtherSprites();
    pSpriteFrameTable->ResetSomeSpriteFlags();
    pIcons_LOD->ReleaseAll2();

    if ( !Load(pFilename, (ODMFace *)File, uRespawnInterval, thisa) )
    {
      MessageBoxA(0, "Error!", "Couldn't Load Map!", 0);
      CreateDebugLocation();
    }
    ::day_attrib = v5->loc_time.day_attrib;
    ::day_fogrange_1 = v5->loc_time.day_fogrange_1;
    ::day_fogrange_2 = v5->loc_time.day_fogrange_2;
    if ( Is_out15odm_underwater() )
      SetUnderwaterFog();
    _6BE134_odm_main_tile_group = v5->pTileTypes[0].tileset;
    result = 1;
  }
  else
  {
    result = 0;
  }
  return result;
}



char foot_travel_destinations[15][4] =
{
// north           south               east              west                    from
  {MAP_INVALID,    MAP_INVALID,        MAP_INVALID,      MAP_INVALID},        // MAP_EMERALD_ISLE
  {MAP_PIERPONT,   MAP_BARROW_DOWNS,   MAP_PIERPONT,     MAP_STEADWICK},      // MAP_HARMONDALE
  {MAP_DEYJA,      MAP_BRAKADA_DESERT, MAP_HARMONDALE,   MAP_TATALIA},        // MAP_STEADWICK
  {MAP_AVLEE,      MAP_HARMONDALE,     MAP_INVALID,      MAP_DEYJA},          // MAP_PIERPONT
  {MAP_PIERPONT,   MAP_STEADWICK,      MAP_PIERPONT,     MAP_STEADWICK},      // MAP_DEYJA
  {MAP_STEADWICK,  MAP_INVALID,        MAP_BARROW_DOWNS, MAP_INVALID},        // MAP_BRAKADA_DESERT
  {MAP_INVALID,    MAP_INVALID,        MAP_INVALID,      MAP_INVALID},        // MAP_CELESTIA
  {MAP_INVALID,    MAP_INVALID,        MAP_INVALID,      MAP_INVALID},        // MAP_THE_PIT
  {MAP_INVALID,    MAP_INVALID,        MAP_INVALID,      MAP_INVALID},        // MAP_EVENMORN_ISLE
  {MAP_INVALID,    MAP_INVALID,        MAP_INVALID,      MAP_INVALID},        // MAP_MOUNT_NIGHON
  {MAP_HARMONDALE, MAP_BRAKADA_DESERT, MAP_HARMONDALE,   MAP_BRAKADA_DESERT}, // MAP_BARROW_DOWNS
  {MAP_INVALID,    MAP_INVALID,        MAP_INVALID,      MAP_INVALID},        // MAP_LAND_OF_GIANTS
  {MAP_INVALID,    MAP_INVALID,        MAP_STEADWICK,    MAP_INVALID},        // MAP_TATALIA
  {MAP_INVALID,    MAP_PIERPONT,       MAP_PIERPONT,     MAP_INVALID},        // MAP_AVLEE
  {MAP_INVALID,    MAP_INVALID,        MAP_INVALID,      MAP_INVALID}         // MAP_SHOALS
};
unsigned char foot_travel_times[15][4] =
{
// north south east  west    from
  {0,    0,    0,    0},  // MAP_EMERALD_ISLE
  {5,    5,    7,    5},  // MAP_HARMONDALE
  {5,    5,    5,    5},  // MAP_STEADWICK
  {5,    5,    0,    5},  // MAP_PIERPONT
  {7,    5,    5,    4},  // MAP_DEYJA
  {5,    0,    5,    0},  // MAP_BRAKADA_DESERT
  {0,    0,    0,    0},  // MAP_CELESTIA
  {0,    0,    0,    0},  // MAP_THE_PIT
  {0,    0,    0,    0},  // MAP_EVENMORN_ISLE
  {0,    0,    0,    0},  // MAP_MOUNT_NIGHON
  {5,    7,    7,    5},  // MAP_BARROW_DOWNS
  {0,    0,    0,    0},  // MAP_LAND_OF_GIANTS
  {0,    0,    5,    0},  // MAP_TATALIA
  {0,    7,    5,    0},  // MAP_AVLEE
  {0,    0,    0,    0},  // MAP_SHOALS
};


MapStartPoint foot_travel_arrival_points[15][4] =
{
// north                south                east                 west                     from
  {MapStartPoint_Party, MapStartPoint_Party, MapStartPoint_Party, MapStartPoint_Party}, // MAP_EMERALD_ISLE
  {MapStartPoint_South, MapStartPoint_North, MapStartPoint_South, MapStartPoint_East},  // MAP_HARMONDALE
  {MapStartPoint_South, MapStartPoint_North, MapStartPoint_West,  MapStartPoint_East},  // MAP_STEADWICK
  {MapStartPoint_East,  MapStartPoint_North, MapStartPoint_Party, MapStartPoint_East},  // MAP_PIERPONT
  {MapStartPoint_West,  MapStartPoint_North, MapStartPoint_West,  MapStartPoint_North}, // MAP_DEYJA
  {MapStartPoint_South, MapStartPoint_Party, MapStartPoint_West,  MapStartPoint_Party}, // MAP_BRAKADA_DESERT
  {MapStartPoint_Party, MapStartPoint_Party, MapStartPoint_Party, MapStartPoint_Party}, // MAP_CELESTIA
  {MapStartPoint_Party, MapStartPoint_Party, MapStartPoint_Party, MapStartPoint_Party}, // MAP_THE_PIT
  {MapStartPoint_Party, MapStartPoint_Party, MapStartPoint_Party, MapStartPoint_Party}, // MAP_EVENMORN_ISLE
  {MapStartPoint_Party, MapStartPoint_Party, MapStartPoint_Party, MapStartPoint_Party}, // MAP_MOUNT_NIGHON
  {MapStartPoint_South, MapStartPoint_East,  MapStartPoint_South, MapStartPoint_East},  // MAP_BARROW_DOWNS
  {MapStartPoint_Party, MapStartPoint_Party, MapStartPoint_Party, MapStartPoint_Party}, // MAP_LAND_OF_GIANTS
  {MapStartPoint_Party, MapStartPoint_Party, MapStartPoint_West,  MapStartPoint_Party}, // MAP_TATALIA
  {MapStartPoint_Party, MapStartPoint_North, MapStartPoint_North, MapStartPoint_Party}, // MAP_AVLEE
  {MapStartPoint_Party, MapStartPoint_Party, MapStartPoint_Party, MapStartPoint_Party}, // MAP_SHOALS
};


//----- (0048902E) --------------------------------------------------------
bool OutdoorLocation::GetTravelDestination(signed int sPartyX, signed int sPartyZ, char *pOut, signed int a5)
{
  char *mapNumberAsStr; // eax@3
  int mapNumberAsInt; // eax@3
  signed int direction; // esi@7
  signed int destinationMap; // eax@23
  char Str[140]; // [sp+8h] [bp-78h]@3

  strcpy(Str, this->pLevelFilename);//настоящая локация
  _strlwr(Str);
  mapNumberAsStr = strtok(Str, "out");
  mapNumberAsStr[2] = 0;
  mapNumberAsInt = atoi(mapNumberAsStr);
  if ( a5 < 10 || strlen(this->pLevelFilename) != 9 || mapNumberAsInt < 1 || mapNumberAsInt > 15 ) //длина  .odm и количество локаций
    return 0;
  if ( sPartyX < -22528 )//граница карты
    direction = 4;
  else if ( sPartyX > 22528 )
    direction = 3;
  else if ( sPartyZ < -22528 )
    direction = 2;
  else if ( sPartyZ > 22528 )
    direction = 1;
  else
    return false;

  if ( mapNumberAsInt == MAP_AVLEE && direction == 4) // to Shoals
  {
      if ( pPlayers[1]->HasUnderwaterSuitEquipped() &&
           pPlayers[2]->HasUnderwaterSuitEquipped() &&
           pPlayers[3]->HasUnderwaterSuitEquipped() &&
           pPlayers[4]->HasUnderwaterSuitEquipped())
      {
        uDefaultTravelTime_ByFoot = 1;
        strcpy(pOut, "out15.odm");
        uLevel_StartingPointType = MapStartPoint_East;
        LOWORD(pParty->uFlags) &= 0xFD7Bu;
        return true;
      }
  }
  else if ( mapNumberAsInt == MAP_SHOALS && direction == 3 ) //from Shoals
  {
    uDefaultTravelTime_ByFoot = 1;
    strcpy(pOut, "out14.odm");//Авли
    uLevel_StartingPointType = MapStartPoint_West;
    LOWORD(pParty->uFlags) &= 0xFD7Bu;
    return true;
  }
  destinationMap = foot_travel_destinations[mapNumberAsInt - 1][direction - 1];
  if (destinationMap == MAP_INVALID)
    return false;

  assert(destinationMap <= MAP_SHOALS);

  uDefaultTravelTime_ByFoot = foot_travel_times[mapNumberAsInt - 1][direction - 1];
  uLevel_StartingPointType = foot_travel_arrival_points[mapNumberAsInt - 1][direction - 1];
  sprintf(pOut, "out%02d.odm", destinationMap); //локация направления
  return true;
}
// 6BD07C: using guessed type int uDefaultTravelTime_ByFoot;
// 6BE35C: using guessed type int uLevel_StartingPointType;

//----- (0048917E) --------------------------------------------------------
void OutdoorLocation::MessWithLUN()
{
  this->pSpriteIDs_LUN[0] = -1;
  this->pSpriteIDs_LUN[1] = 0;
  this->pSpriteIDs_LUN[2] = pSpriteFrameTable->FastFindSprite("LUN1-4");
  this->pSpriteIDs_LUN[3] = 0;
  this->pSpriteIDs_LUN[4] = pSpriteFrameTable->FastFindSprite("LUN1-2");
  this->pSpriteIDs_LUN[5] = 0;
  this->pSpriteIDs_LUN[6] = pSpriteFrameTable->FastFindSprite("LUN3-4");
  this->pSpriteIDs_LUN[7] = 0;
  this->uSpriteID_LUNFULL = pSpriteFrameTable->FastFindSprite("LUNFULL");
  this->uSpriteID_LUN1_2_cp = pSpriteFrameTable->FastFindSprite("LUN1-2");
  this->uSpriteID_LUN1_4_cp = pSpriteFrameTable->FastFindSprite("LUN1-4");
  this->uSpriteID_LUN3_4_cp = pSpriteFrameTable->FastFindSprite("LUN3-4");
  this->field_D60 = -1;
  this->field_CF0 = 4;
  this->field_CF8 = 4;
  this->field_D00 = 4;
  this->field_CE8 = 0;
  this->field_D3C = (int)this->pSpriteIDs_LUN;
  this->field_D40 = 0;
  this->field_D44 = 0;
  this->field_D48 = 0;
  this->field_D4C = 131072;
  this->field_D5C = 0;
  this->field_D64 = 0;
  this->field_D28 = -1;
  this->field_D08 = 0;
  this->field_D0C = 0;
  this->field_D10 = 0;
  this->field_D24 = 0;
  this->field_D2C = 0;
  this->uSpriteID_LUN_SUN = pSpriteFrameTable->FastFindSprite("LUN-SUN");
  this->field_D14 = -131072;
  for ( uint i = 0; i < 8; i++ )
    pSpriteFrameTable->InitializeSprite(this->pSpriteIDs_LUN[i]);//v2 += 2;
  pSpriteFrameTable->InitializeSprite(this->uSpriteID_LUN_SUN);
}

//----- (004892E6) --------------------------------------------------------
void OutdoorLocation::UpdateSunlightVectors()
{
  unsigned int v3; // edi@3
  double v8; // st7@4

  if ( pParty->uCurrentHour >= 5 && pParty->uCurrentHour < 21 )
  {
    v3 = pParty->uCurrentMinute + 60 * (pParty->uCurrentHour - 5);
    this->inv_sunlight_y = 0;
    this->inv_sunlight_x = stru_5C6E00->Cos((v3 * stru_5C6E00->uIntegerPi) / 960);
    this->inv_sunlight_z = stru_5C6E00->Sin((v3 * stru_5C6E00->uIntegerPi) / 960);
    this->vSunlight.x = -this->inv_sunlight_x;
    this->vSunlight.y = -this->inv_sunlight_y;
    this->vSunlight.z = -this->inv_sunlight_z;
    if ( v3 >= 480 )
      v8 = 960 - v3;
    else
      v8 = v3;
    this->max_terrain_dimming_level = (int)(20.0 - v8 / 480.0 * 20.0);
    this->uLastSunlightUpdateMinute = pParty->uCurrentMinute;
  }
}

//----- (004893C1) --------------------------------------------------------
void OutdoorLocation::UpdateFog()
{
  fFogDensity = GetFogDensityByTime();
}

//----- (004893CF) --------------------------------------------------------
int OutdoorLocation::GetNumFoodRequiredToRestInCurrentPos(int x, signed int y, int z)
{
  int v7; // eax@4
  int is_on_water; // [sp+8h] [bp-8h]@2
  int bmodel_standing_on_pid; // [sp+Ch] [bp-4h]@2

  is_on_water = 0;
  bmodel_standing_on_pid = 0;
  ODM_GetFloorLevel(x, y, z, pParty->uDefaultPartyHeight, &is_on_water, &bmodel_standing_on_pid, 0);
  if ( pParty->uFlags & 8 || bmodel_standing_on_pid || is_on_water )//на bmodel,и или на воде
    return 2;
  v7 = _47ED83(WorldPosToGridCellX(pParty->vPosition.x), WorldPosToGridCellZ(pParty->vPosition.y) - 1);
  switch ( pTileTable->pTiles[GetTileIdByTileMapId(v7)].tileset )
  {
    case Tileset_Grass://на траве
      return 1;
    case Tileset_Snow://на снегу
      return 3;
    case Tilset_Desert://на песке
      return 5;
    case Tileset_CooledLava:
    case Tileset_Dirt:// на грязи
      return 4;
    case Tileset_Water:// on water(на воде)
      return 3;//еденицы еды
    default:
      return 2;
  }
}

//----- (00489487) --------------------------------------------------------
void OutdoorLocation::SetFog()
{
  strcpy(pOutdoor->pLevelFilename, pCurrentMapName);

  MAP_TYPE map_id = pMapStats->GetMapInfo(pCurrentMapName);
  if (map_id == MAP_INVALID || map_id == MAP_CELESTIA || map_id == MAP_THE_PIT || map_id > MAP_SHOALS)
    return;

  uint chance = rand() % 100;

  if (chance < fog_probability_table[map_id - 1].small_fog_chance)
  {
    ::day_fogrange_1 = 4096;
    ::day_fogrange_2 = 8192;
    ::day_attrib |= DAY_ATTRIB_FOG;
  }
  else if (chance < fog_probability_table[map_id - 1].small_fog_chance +
                    fog_probability_table[map_id - 1].average_fog_chance)
  {
    ::day_fogrange_2 = 4096;
    ::day_fogrange_1 = 0;
    ::day_attrib |= DAY_ATTRIB_FOG;
  }
  else if (fog_probability_table[map_id - 1].dense_fog_chance &&
           chance < fog_probability_table[map_id - 1].small_fog_chance + 
                    fog_probability_table[map_id - 1].average_fog_chance +
                    fog_probability_table[map_id - 1].dense_fog_chance)
  {
    ::day_fogrange_2 = 2048;
    ::day_fogrange_1 = 0;
    ::day_attrib |= DAY_ATTRIB_FOG;
  }
  else
    ::day_attrib &= ~DAY_ATTRIB_FOG;

  if ( Is_out15odm_underwater() )
    SetUnderwaterFog();
  pOutdoor->loc_time.day_fogrange_1 = ::day_fogrange_1;
  pOutdoor->loc_time.day_fogrange_2 = ::day_fogrange_2;
  pOutdoor->loc_time.day_attrib = ::day_attrib;
}

//----- (00482170) --------------------------------------------------------
bool ODMFace::IsBackfaceNotCulled(RenderVertexSoft *a2, struct Polygon *polygon)
{
  unsigned int numOfVertices; // edx@1
  RenderVertexSoft *currVertex; // ecx@2
  double v7; // st7@5
  double v8; // st6@5
  double v9; // st5@5
  float v18; // [sp+8h] [bp-38h]@5
  float v19; // [sp+10h] [bp-30h]@5
  float v20; // [sp+14h] [bp-2Ch]@5
  float v21; // [sp+18h] [bp-28h]@5
  float v22; // [sp+1Ch] [bp-24h]@5
  float v23; // [sp+24h] [bp-1Ch]@5
  float v24; // [sp+28h] [bp-18h]@5
  float v25; // [sp+30h] [bp-10h]@5
  float v26; // [sp+34h] [bp-Ch]@5
  float v27; // [sp+38h] [bp-8h]@5
  float v28; // [sp+3Ch] [bp-4h]@5
  float a3a; // [sp+48h] [bp+8h]@5
  float a3b; // [sp+48h] [bp+8h]@17
  float a3c; // [sp+48h] [bp+8h]@17
  float a3d; // [sp+48h] [bp+8h]@17
  float a3e; // [sp+48h] [bp+8h]@17

  numOfVertices = polygon->uNumVertices;
  if ( numOfVertices < 3 )
    return false;
  currVertex = &a2[numOfVertices - 1];
  if ( a2->vWorldPosition.z == a2[1].vWorldPosition.z && a2[1].vWorldPosition.z == currVertex->vWorldPosition.z )
    polygon->flags |= 0x10u;

  v28 = a2[1].vWorldPosition.x - a2->vWorldPosition.x;
  v27 = a2[1].vWorldPosition.y - a2->vWorldPosition.y;
  a3a = a2[1].vWorldPosition.z - a2->vWorldPosition.z;


  for (int i = 0; i < numOfVertices; i++)
  {
    v7 = currVertex->vWorldPosition.x - a2->vWorldPosition.x;
    v8 = currVertex->vWorldPosition.y - a2->vWorldPosition.y;
    v9 = currVertex->vWorldPosition.z - a2->vWorldPosition.z;
    v26 = v27 * v9 - v8 * a3a;
    v24 = v7 * a3a - v9 * v28;
    v25 = v8 * v28 - v7 * v27;
    if ( v24 != 0.0 || v25 != 0.0 || v26 != 0.0 )
      break;
    currVertex--;
  }

  if ( ((double)pEngine->pIndoorCameraD3D->vPartyPos.x - a2->vWorldPosition.x) * v26
    + ((double)pEngine->pIndoorCameraD3D->vPartyPos.z - a2->vWorldPosition.z) * v25
    + ((double)pEngine->pIndoorCameraD3D->vPartyPos.y - a2->vWorldPosition.y) * v24 > 0.0 )
  {

    v19 = a2[1].vWorldViewPosition.x - a2->vWorldViewPosition.x;
    v18 = a2[1].vWorldViewPosition.y - a2->vWorldViewPosition.y;
    v20 = a2[1].vWorldViewPosition.z - a2->vWorldViewPosition.z;
    v21 = currVertex->vWorldViewPosition.x - a2->vWorldViewPosition.x;
    v22 = currVertex->vWorldViewPosition.y - a2->vWorldViewPosition.y;
    v23 = currVertex->vWorldViewPosition.z - a2->vWorldViewPosition.z;

    a3b = v23 * v18 - v22 * v20;
    polygon->v_18.x = bankersRounding(a3b);
    a3c = v21 * v20 - v23 * v19;
    polygon->v_18.y = bankersRounding(a3c);
    a3d = v22 * v19 - v21 * v18;
    polygon->v_18.z = bankersRounding(a3d);
    polygon->_normalize_v_18();
    a3e = -((double)polygon->v_18.x * a2->vWorldViewPosition.x)
      - (double)polygon->v_18.y * a2->vWorldViewPosition.y
      - (double)polygon->v_18.z * a2->vWorldViewPosition.z;
    polygon->field_24 = bankersRounding(a3e);
    return true;
  }
  else
    return false;
}

//----- (0047C7A9) --------------------------------------------------------
void OutdoorLocationTerrain::_47C7A9()
{
  this->field_10 = 0;
  this->field_12 = 0;
  this->field_16 = 0;
  this->field_14 = 0;
  this->field_1C = 0;
  this->field_18 = 0;
}

//----- (0047C7C2) --------------------------------------------------------
void OutdoorLocationTerrain::Release()//очистить локацию
{
  free(this->pHeightmap);
  pHeightmap = nullptr;
  free(pTilemap);
  pTilemap = nullptr;
  free(pAttributemap);
  pAttributemap = nullptr;
  free(pDmap);
  pDmap = nullptr;

  _47C7A9();
}

//----- (0047C80A) --------------------------------------------------------
void OutdoorLocationTerrain::FillDMap( int X, int Y, int W, int Z )
{
  double v6; // st7@1
  double v7; // st7@2
  double v8; // st7@2
  int result; // eax@3
  int v10; // eax@4
  int v11; // ecx@5
  int v12; // ecx@6
  int v13; // edi@7
  int v14; // edx@9
//  int v15; // eax@15
  unsigned __int8 *pMapHeight; // ebx@15
  int v17; // eax@15
  int v18; // ecx@15
  int v19; // esi@15
  int v20; // edi@15
  int v21; // edx@15
  int v22; // ecx@15
  int v23; // ebx@15
  int v24; // ecx@15
  int v25; // ST28_4@15
  double v26; // st7@15
  double v27; // st6@15
  double v28; // st5@15
  double v29; // st7@15
  double v30; // st7@16
  double v31; // st7@17
  int v32; // eax@21
  double v33; // st7@21
  double v34; // st6@21
  double v35; // st5@21
  double v36; // st7@21
  double v37; // st7@22
  double v38; // st7@23
  int v39; // [sp+14h] [bp-34h]@8
  int v40; // [sp+18h] [bp-30h]@15
  int v41; // [sp+1Ch] [bp-2Ch]@15
  int v42; // [sp+20h] [bp-28h]@15
  int v44; // [sp+28h] [bp-20h]@21
  float v45; // [sp+2Ch] [bp-1Ch]@1
  float v46; // [sp+30h] [bp-18h]@1
  float v47; // [sp+34h] [bp-14h]@1
  //int v48; // [sp+38h] [bp-10h]@7
  int v49; // [sp+3Ch] [bp-Ch]@10
  int v50; // [sp+40h] [bp-8h]@9
  float v51; // [sp+44h] [bp-4h]@15
  float v52; // [sp+44h] [bp-4h]@21
  float v53; // [sp+50h] [bp+8h]@15
  float v54; // [sp+50h] [bp+8h]@21
//  int v55; // [sp+54h] [bp+Ch]@15
  float v56; // [sp+54h] [bp+Ch]@15
  float v57; // [sp+54h] [bp+Ch]@21

  v46 = -64.0;
  v47 = -64.0;
  v45 = 64.0;
  v6 = sqrt(12288.0);
  if ( v6 != 0.0 )
  {
    v7 = 1.0 / v6;
    v45 = 64.0 * v7;
    v8 = v7 * -64.0;
    v46 = v8;
    v47 = v8;
  }
  result = Y;
  if ( Y > Z )
  {
    v10 = Z ^ Y;
    Z ^= Y ^ Z;
    result = Z ^ v10;
  }
  v11 = X;
  if ( X > W )
  {
    v12 = W ^ X;
    W ^= X ^ W;
    v11 = W ^ v12;
  }
  //v48 = result - 1;
  if ( result - 1 <= Z )
  {
    v39 = v11 - 1;
    for ( v13 = result - 1; v13 <= Z; v13++ )
    {
      v50 = v39;
      if ( v39 <= W )
      {
        result = (v39 - 63) << 9;
        v49 = (v39 - 63) << 9;
        for ( v14 = v39; v14 <= W; v14++ )
        {
          if ( v13 >= 0 && result >= -32256 && v13 <= 127 && result <= 32768 )
          {
            //v15 = pOutLocTerrain->field_10;
            //v55 = pOutLocTerrain->field_10;
            pMapHeight = this->pHeightmap;
            v17 = (int)(&pMapHeight[v13 * this->field_10] + v14);
            v18 = -v13;
            v19 = (64 - v13) << 9;
            v20 = 32 * *(char *)v17;
            v21 = 32 * *(char *)(v17 + 1);

            v22 = (v18 + 63) << 9;
            v41 = v22;
            v23 = (int)(&pMapHeight[this->field_10 * (v13 + 1)] + v14);
            v24 = v22 - v19;
            v40 = 32 * *(char *)v23;
            v42 = 32 * *(char *)(v23 + 1);

            v25 = v49 - 512 - v49;
            v26 = (double)-((v20 - v21) * v24);
            v51 = v26;
            v27 = (double)-(v25 * (v42 - v21));
            v53 = v27;
            v28 = (double)(v25 * v24);
            v56 = v28;
            v29 = sqrt(v28 * v28 + v27 * v27 + v26 * v26);
            if ( v29 != 0.0 )
            {
              v30 = 1.0 / v29;
              v51 = v51 * v30;
              v53 = v53 * v30;
              v56 = v30 * v56;
            }
            v31 = (v56 * v47 + v53 * v46 + v51 * v45) * 31.0;
            if ( v31 < 0.0 )
              v31 = 0.0;
            if ( v31 > 31.0 )
              v31 = 31.0;
            v44 = 2 * (v14 + v13 * this->field_10);
            //pOutLocTerrain = pOutLocTerrain2;
            *((char *)this->pDmap + v44 + 1) = (signed __int64)v31;

            v32 = v49 - (v49 - 512);
            v33 = (double)-((v42 - v40) * (v19 - v41));
            v52 = v33;
            v34 = (double)-(v32 * (v20 - v40));
            v54 = v34;
            v35 = (double)(v32 * (v19 - v41));
            v57 = v35;
            v36 = sqrt(v35 * v35 + v34 * v34 + v33 * v33);
            if ( v36 != 0.0 )
            {
              v37 = 1.0 / v36;
              v52 = v52 * v37;
              v54 = v54 * v37;
              v57 = v37 * v57;
            }
            v38 = (v57 * v47 + v54 * v46 + v52 * v45) * 31.0;
            if ( v38 < 0.0 )
              v38 = 0.0;
            if ( v38 > 31.0 )
              v38 = 31.0;
            //v13 = v48;
            *((char *)this->pDmap + v44) = (signed __int64)v38;
            //v14 = v50;
            result = v49;
          }
          //++v14;
          result += 512;
          //v50 = v14;
          v49 = result;
        }
      }
      //++v13;
      //v48 = v13;
    }
    //while ( v13 <= Z );
  }
}

//----- (0047CB57) --------------------------------------------------------
int OutdoorLocationTerrain::_47CB57(int a1, int a2, int a3)
{
  signed int result; // eax@2
//  unsigned __int16 *v5; // edx@3
//  double v6; // st7@3
//  int v8; // eax@3
//  int v9; // eax@4
//  int v10; // eax@5
//  double v11; // st6@7
//  signed int v12; // edi@7
//  int v13; // esi@9
//  char *v14; // esi@10
//  signed int v15; // ecx@10
//  char v16[256]; // [sp+4h] [bp-124h]@9
//  unsigned __int16 *v17; // [sp+104h] [bp-24h]@3
//  float v22; // [sp+118h] [bp-10h]@3
//  float v23; // [sp+11Ch] [bp-Ch]@3
//  int i; // [sp+120h] [bp-8h]@3
//  unsigned int v25; // [sp+124h] [bp-4h]@5
//  signed int a2a; // [sp+134h] [bp+Ch]@3
//  unsigned int a2b; // [sp+134h] [bp+Ch]@7
//  float a3a; // [sp+138h] [bp+10h]@7
//  int a3b; // [sp+138h] [bp+10h]@9

  int num_r_bits = 5;
  int num_g_bits = 6;
  int num_b_bits = 5;

  int r_mask = 0xF800;
  int g_mask = 0x7E0;
  int b_mask = 0x1F;

  //if ( pRenderer->pRenderD3D )
    result = 0;
  /*else
  {
    __debugbreak();
    v5 = PaletteManager::Get_Dark_or_Red_LUT(a2, 0, 1);
    v6 = 0.0;
    v22 = 0.0;
    v8 = 0;
    v17 = v5;
    v23 = 0.0;
    a2a = 0;
    for ( i = 0; i < a3; ++i )
    {
      v9 = *(char *)(v8 + a1);
      if ( v9 )
      {
        v10 = v5[v9];
        v6 = v6 + (double)((signed int)(r_mask & v10) >> (num_b_bits + num_g_bits));
        ++a2a;
        v25 = b_mask & v10;
        v22 = (double)((signed int)(g_mask & v10) >> num_b_bits) + v22;
        v23 = (double)(signed int)(b_mask & v10) + v23;
      }
      v8 = i + 1;
    }
    v11 = 1.0 / (double)a2a;
    a3a = v11;
    v25 = (signed __int64)(a3a * v22);
    i = (signed __int64)(a3a * v23);
    v12 = 0;
    a2b = num_b_bits + num_g_bits;
    while ( 1 )
    {
      v13 = v17[v12];
      a3b = abs((__int64)(signed __int64)(v11 * v6) - ((signed int)(r_mask & v17[v12]) >> a2b));
      BYTE3(a3b) = abs((signed)v25 - ((signed int)(g_mask & v13) >> num_b_bits)) + a3b;
      v16[v12++] = abs((signed)i - (signed)(b_mask & v13)) + BYTE3(a3b);
      if ( v12 >= 256 )
        break;
    }
    result = 0;
    v14 = (char *)&pPaletteManager->field_D1600[42][23][116];
    v15 = 0;
    do
    {
      if ( (unsigned __int8)v16[v15] < (signed int)v14 )
      {
        v14 = (char *)(unsigned __int8)v16[v15];
        result = v15;
      }
      ++v15;
    }
    while ( v15 < 256 );
  }*/
  return result;
}

//----- (0047CCE2) --------------------------------------------------------
bool OutdoorLocationTerrain::ZeroLandscape()
{
  memset(this->pHeightmap, 0, 0x4000u);
  memset(this->pTilemap, 90, 0x4000u);
  memset(this->pAttributemap, 0, 0x4000u);
  memset(this->pDmap, 0, 0x8000u);
  this->field_12 = 128;
  this->field_10 = 128;
  this->field_16 = 7;
  this->field_14 = 7;
  this->field_1C = 127;
  this->field_18 = 127;
  return true;
}

//----- (0047CD44) --------------------------------------------------------
bool OutdoorLocationTerrain::Initialize()
{
  pHeightmap = (unsigned __int8 *)malloc(0x4000);//height map
  pTilemap = (unsigned __int8 *)malloc(0x4000);//tile map
  pAttributemap = (unsigned __int8 *)malloc(0x4000);//карта атрибутов
  pDmap = (struct DMap *)malloc(0x8000);
  if (pHeightmap && pTilemap && pAttributemap && pDmap )
    return true;
  else
    return false;
}

//----- (0047CDE2) --------------------------------------------------------
void OutdoorLocation::CreateDebugLocation()
{
  //OutdoorLocation *v1; // esi@1
  void *v2; // eax@1
  void *v3; // ST14_4@1
  void *v4; // eax@1
  void *v5; // ST14_4@1
  void *v6; // eax@1
  //unsigned int v7; // eax@1
  //char v8; // zf@1

  //v1 = this;
  strcpy(this->pLevelFilename, "blank");
  strcpy(this->pLocationFileName, "i6.odm");
  strcpy(this->pLocationFileDescription, "MM6 Outdoor v1.00");
  this->uNumBModels = 0;
  this->pTileTypes[0].tileset = Tileset_Grass;
  this->pTileTypes[1].tileset = Tileset_Water;
  this->pTileTypes[2].tileset = Tileset_Badlands;
  this->pTileTypes[3].tileset = Tileset_RoadGrassCobble;
  this->LoadTileGroupIds();
  this->LoadRoadTileset();
  free(this->pBModels);
  free(this->pSpawnPoints);
  this->pBModels = 0;
  this->pSpawnPoints = 0;
  this->pTerrain.Initialize();
  this->pTerrain.ZeroLandscape();
  this->pTerrain.FillDMap(0, 0, 128, 128);
  free(this->pCmap);
  this->pCmap = 0;
  v2 = malloc(0x8000u);
  v3 = this->pOMAP;
  this->pCmap = v2;
  free(v3);
  this->pOMAP = 0;
  v4 = malloc(0x10000u);
  this->pOMAP = (unsigned int *)v4;
  memset(v4, 0, 0x10000u);
  v5 = this->pFaceIDLIST;
  this->numFaceIDListElems = 0;
  free(v5);
  this->pFaceIDLIST = 0;
  v6 = malloc(2);
  this->pFaceIDLIST = (unsigned __int16 *)v6;
  *(short *)v6 = 0;
  strcpy(this->pSkyTextureName, pDefaultSkyTexture.data());
  this->sSky_TextureID = pBitmaps_LOD->LoadTexture(this->pSkyTextureName);
  strcpy(this->pGroundTileset, byte_6BE124_cfg_textures_DefaultGroundTexture.data());
  //v7 = pBitmaps_LOD->LoadTexture(this->pGroundTileset);
  this->sMainTile_BitmapID = pBitmaps_LOD->LoadTexture(this->pGroundTileset);

  if ( this->sSky_TextureID == -1 )
    Error("Invalid Sky Tex Handle");

  if ( this->sMainTile_BitmapID == -1 )
    Error("Invalid Ground Tex Handle");
}

//----- (0047CF9C) --------------------------------------------------------
void OutdoorLocation::Release()
{
  strcpy(pLevelFilename, "blank");
  strcpy(pLocationFileName, "default.odm");
  strcpy(pLocationFileDescription, "MM6 Outdoor v1.00");
  strcpy(pSkyTextureName, "sky043");
  strcpy(pGroundTileset, "hm005");

  if (pBModels)
  {
    for (uint i = 0; i < uNumBModels; ++i)
      pBModels[i].Release();

    free(pBModels);
    pBModels = nullptr;
    uNumBModels = 0;
  }

  free(pSpawnPoints);
  pSpawnPoints = nullptr;
  uNumSpawnPoints = 0;

  pTerrain.Release();

  free(pCmap);
  pCmap = nullptr;
  free(pOMAP);
  pOMAP = nullptr;
  free(pFaceIDLIST);
  pFaceIDLIST = nullptr;
  free(pTerrainNormals);
  pTerrainNormals = nullptr;
}

//----- (0047D0A6) --------------------------------------------------------
bool OutdoorLocation::Load(const char *pFilename, ODMFace *File, size_t pNumItems, int *thisa)//загрузка локации
{
  FILE *pFile; // eax@50
  unsigned __int16 v62; // ax@65
  int v108; // [sp+0h] [bp-B80h]@10
  char Src[968]; // [sp+10h] [bp-B70h]@110
  char Dst[968]; // [sp+3D8h] [bp-7A8h]@50
  char Str[256]; // [sp+7A0h] [bp-3E0h]@50
  ODMHeader header; // [sp+B58h] [bp-28h]@50
  char *Str2; // [sp+B74h] [bp-Ch]@12

  //pOutdoorLocation = this;
  //strcpy(pContainer, pFilename);

  if (bUnderwater)
  {
    pPaletteManager->pPalette_tintColor[0] = 0x10;
    pPaletteManager->pPalette_tintColor[1] = 0xC2;
    pPaletteManager->pPalette_tintColor[2] = 0x99;
    pPaletteManager->SetMistColor(37, 143, 92);
  }
  else
  {
    pPaletteManager->pPalette_tintColor[0] = 0;
    pPaletteManager->pPalette_tintColor[1] = 0;
    pPaletteManager->pPalette_tintColor[2] = 0;
    if (pPaletteManager->pPalette_mistColor[0] != 0x80 ||
        pPaletteManager->pPalette_mistColor[1] != 0x80 ||
        pPaletteManager->pPalette_mistColor[2] != 0x80)
    {
      pPaletteManager->SetMistColor(128, 128, 128);
      pPaletteManager->RecalculateAll();
    }
  }

  _6807E0_num_decorations_with_sounds_6807B8 = 0;
  /*sprintf(FileName, "levels\\%s", pContainer);
  if ( GetFileAttributesA(FileName) != -1 )
  {
    result = (bool)fopen(FileName, "rb");
    v7 = result;
    File = (ODMFace *)result;
    if ( !result )
      return result;
    *(int *)thisa = 1;
    v8 = strlen(pContainer);
    v108 = 2;
    *((char *)&v139 + v8) = 0;
    viewparams->uTextureID_LocationMap = pIcons_LOD->LoadTexture(pContainer, (enum TEXTURE_TYPE)v108);
    fread(&DstBuf, 0x180u, 1u, (FILE *)v7);
    fseek((FILE *)v7, Offset, 0);
    fread(this, 0xB0u, 1u, (FILE *)v7);
    LoadTileGroupIds();
    LoadRoadTileset();
    strcpy(pGroundTileset, "grastyl");
    fseek((FILE *)v7, v114, 0);
    fread(&uNumBModels, 4u, 1u, (FILE *)v7);
    fseek((FILE *)v7, v115, 0);
    fread(&uNumSpriteObjects, 4u, 1u, (FILE *)v7);
    fseek((FILE *)v7, v116, 0);
    fread(&uNumLevelDecorations, 4u, 1u, (FILE *)v7);
    fseek((FILE *)v7, v117, 0);
    fread(&uNumActors, 4u, 1u, (FILE *)v7);
    fseek((FILE *)v7, v118, 0);
    fread(&uNumChests, 4u, 1u, (FILE *)v7);
    pTerrain.Initialize();
    fseek((FILE *)v7, v119, 0);
    fread(pTerrain.pHeightmap, 1u, 0x4000u, (FILE *)v7);
    fseek((FILE *)v7, v120, 0);
    fread(pTerrain.pTilemap, 1u, 0x4000u, (FILE *)v7);
    fseek((FILE *)v7, v121, 0);
    fread(pTerrain.pAttributemap, 1u, 0x4000u, (FILE *)v7);
    pTerrain._47C80A(0, 0, 128, 128);
    free(ptr_D4);
    ptr_D4 = 0;
    v9 = malloc(0, 0x8000u, "CMAP");
    v108 = (int)pOMAP;
    ptr_D4 = v9;
    free((void *)v108);
    pOMAP = 0;
    v10 = malloc(0, 0x10000u, "OMAP");
    v108 = 0;
    pOMAP = (unsigned int *)v10;
    fseek((FILE *)v7, v136, v108);
    fread(&uNumTerrainNormals, 4u, 1u, (FILE *)v7);
    fread(pTerrainSomeOtherData, 1u, 0x20000u, (FILE *)v7);
    fread(pTerrainNormalIndices, 1u, 0x10000u, (FILE *)v7);
    pTerrainNormals = (Vec3_float_ *)malloc(pTerrainNormals, 12 * uNumTerrainNormals, "TerNorm");
    fread(pTerrainNormals, 1u, 12 * uNumTerrainNormals, (FILE *)v7);
    v11 = malloc(pBModels, 188 * uNumBModels, "BDdata");
    v108 = 0;
    pBModels = (BSPModel *)v11;
    fseek((FILE *)v7, v122, v108);
    fread(pBModels, 0xBCu, uNumBModels, (FILE *)v7);
    fseek((FILE *)v7, v123, 0);
    pNumItems = 0;
    if ( (signed int)uNumBModels > 0 )
    {
      v12 = 0;
      while ( 1 )
      {
        pBModels[v12].pVertices.pVertices = 0;
        pBModels[v12].pFaces = 0;
        pBModels[v12].pFacesOrdering = 0;
        pBModels[v12].pNodes = 0;
        FileName[0] = 0;
        v108 = (int)&pBModels[v12];
        sprintfex(FileName, "%s", v108);
        v13 = pBModels;
        v138 = 0;
        pBModels[v12].pVertices.pVertices = (Vec3_int_ *)malloc(v13[v12].pVertices.pVertices, 12 * v13[v12].pVertices.uNumVertices,
                                                               FileName);
        pBModels[v12].pFaces = (ODMFace *)malloc(pBModels[v12].pFaces, 308 * pBModels[v12].uNumFaces,
                                                FileName);
        pBModels[v12].pFacesOrdering = (unsigned __int16 *)malloc(pBModels[v12].pFacesOrdering,
                                                                 2 * pBModels[v12].uNumFaces, FileName);
        v14 = malloc(pBModels[v12].pNodes, 8 * pBModels[v12].uNumNodes, FileName);
        v15 = pBModels;
        v108 = (int)File;
        v15[v12].pNodes = (BSPNode *)v14;
        fread(pBModels[v12].pVertices.pVertices, 0xCu, pBModels[v12].pVertices.uNumVertices, (FILE *)v108);
        fread(pBModels[v12].pFaces, 0x134u, pBModels[v12].uNumFaces, (FILE *)File);
        fread(pBModels[v12].pFacesOrdering, 2u, pBModels[v12].uNumFaces, (FILE *)File);
        fread(pBModels[v12].pNodes, 8u, pBModels[v12].uNumNodes, (FILE *)File);
        v16 = malloc(10 * pBModels[v12].uNumFaces);
        v107 = (int)File;
        v17 = pBModels;
        ptr = (FILE *)v16;
        fread(v16, 0xAu, v17[v12].uNumFaces, (FILE *)File);
        v18 = pBModels;
        Str2 = 0;
        if ( (signed int)v18[v12].uNumFaces > 0 )
          break;
LABEL_25:
        free(ptr);
        ++pNumItems;
        ++v12;
        if ( (signed int)pNumItems >= (signed int)uNumBModels )
          goto LABEL_26;
      }
      pDestLen = 0;
      pFilename = (char *)ptr;
      while ( 1 )
      {
        thisa = (int)((char *)v18[v12].pFaces + pDestLen);
        if ( !(*(char *)(thisa + 29) & 0x40) )
          break;
        v19 = pTextureFrameTable->FindTextureByName(pFilename);
        *(short *)(thisa + 272) = v19;
        if ( !v19 )
        {
          v20 = pBitmaps_LOD->LoadTexture(pFilename);
          v21 = thisa;
          *(char *)(v21 + 29) &= 0xBFu;
LABEL_19:
          *(short *)(v21 + 272) = v20;
          v149 = (void *)(v20 != -1 ? &pBitmaps_LOD->pTextures[v20] : 0);
          auto pTex = (Texture *)v149;
          if (pTex)
            pTex->palette_id2 = pPaletteManager->LoadPalette(pTex->palette_id1);
          goto LABEL_20;
        }
        pTextureFrameTable->LoadAnimationSequenceAndPalettes(*(unsigned __int16 *)((char *)&pBModels[v12].pFaces->uTextureID + pDestLen));
LABEL_20:
        if ( *(short *)(thisa + 292) )
        {
          if ( ((ODMFace *)thisa)->HasEventHint() )
            *(char *)(thisa + 30) |= 0x10u;
          else
            *(char *)(thisa + 30) &= 0xEFu;
        }
        ++Str2;
        v18 = pBModels;
        pDestLen += 308;
        pFilename += 10;
        if ( (signed int)Str2 >= (signed int)v18[v12].uNumFaces )
          goto LABEL_25;
      }
      v20 = pBitmaps_LOD->LoadTexture(pFilename);
      v21 = thisa;
      goto LABEL_19;
    }
LABEL_26:
    v22 = File;
    fseek((FILE *)File, v124, 0);
    fread(pSpriteObjects, 0x70u, uNumSpriteObjects, (FILE *)v22);
    if ( (signed int)uNumSpriteObjects > 0 )
    {
      pItems = pSpriteObjects;
      pNumItems = uNumSpriteObjects;
      do
      {
        v24 = pItems->stru_24.uItemID;
        thisa = 0;
        v27 = (ODMFace *)(48 * v24);
        v25 = pObjectList->uNumObjects == 0;
        v26 = (pObjectList->uNumObjects & 0x80000000u) != 0;
        LOWORD(v27) = *(short *)((char *)&v27->pFacePlane.vNormal.x + (int)((char *)&pItemsTable + 24));
        File = v27;
        pItems->uItemType = (unsigned __int16)v27;
        if ( v26 | v25 )
        {
LABEL_33:
          v29 = 0;
        }
        else
        {
          for ( i = (const char *)&pObjectList->pObjects->uObjectID; (short)v27 != *(short *)i; i = pFilename )
          {
            ++thisa;
            pFilename = (char *)i + 56;
            if ( thisa >= (signed int)pObjectList->uNumObjects )
              goto LABEL_33;
            LOWORD(v27) = (short)File;
          }
          v29 = thisa;
        }
        pItems->uObjectDescID = v29;
        ++pItems;
        --pNumItems;
      }
      while ( pNumItems );
    }
    fseek((FILE *)v22, v125, 0);
    fread(pLevelDecorations, 0x20u, uNumLevelDecorations, (FILE *)v22);
    pNumItems = 0;
    if ( (signed int)uNumLevelDecorations > 0 )
    {
      thisa = (int)pLevelDecorations;
      do
      {
        fread(FileName, 1u, 0x20u, (FILE *)v22);
        v30 = pDecorationList->GetDecorIdByName(FileName);
        v31 = thisa;
        ++pNumItems;
        thisa += 32;
        *(short *)v31 = v30;
      }
      while ( (signed int)pNumItems < (signed int)uNumLevelDecorations );
    }
    fseek((FILE *)v22, v126, 0);
    fread(pActors, 0x344u, uNumActors, (FILE *)v22);
    fseek((FILE *)v22, v127, 0);
    fread(pChests, 0x14CCu, uNumChests, (FILE *)v22);
    fseek((FILE *)v22, v128, 0);
    fread(&field_DC, 4u, 1u, (FILE *)v22);
    free(pFaceIDLIST);
    v32 = field_DC;
    pFaceIDLIST = 0;
    v33 = malloc(0, 2 * v32, "IDLIST");
    v108 = (int)v22;
    pFaceIDLIST = (unsigned __int16 *)v33;
    fread(v33, 2u, field_DC, (FILE *)v108);
    fseek((FILE *)v22, v129, 0);
    fread(pOMAP, 4u, 0x4000u, (FILE *)v22);
    fseek((FILE *)v22, v130, 0);
    fread(&uNumSpawnPoints, 4u, 1u, (FILE *)v22);
    pSpawnPoints = (SpawnPointMM7 *)malloc(pSpawnPoints, 24 * uNumSpawnPoints, "Spawn");
    fseek((FILE *)v22, v131, 0);
    fread(pSpawnPoints, 0x18u, uNumSpawnPoints, (FILE *)v22);
    fseek((FILE *)v22, v132, 0);
    fread(&ddm, 0x28u, 1u, (FILE *)v22);
    fseek((FILE *)v22, v133, 0);
    fread(&stru_5E4C90, 1u, 0xC8u, (FILE *)v22);
    fseek((FILE *)v22, v134, 0);
    fread(&uLastVisitDay, 1u, 0x38u, (FILE *)v22);
    fseek((FILE *)v22, v135, 0);
    fread(&uLastVisitDay, 1u, 4u, (FILE *)v22);
    thisa = (int)pTileTypes;
    pTileTable->InitializeTileset(4);
    pTileTable->InitializeTileset(pTileTypes[0].uTileGroup);
    pTileTable->InitializeTileset(pTileTypes[1].uTileGroup);
    pTileTable->InitializeTileset(pTileTypes[2].uTileGroup);
    pTileTable->InitializeTileset(pTileTypes[3].uTileGroup);
    if ( this != (OutdoorLocation *)-96 && pSkyTextureName[0] )
    {
      v108 = 0;
      v107 = (int)pSkyTextureName;
    }
    else
    {
      v108 = 0;
      v107 = (int)pDefaultSkyTexture;
    }
    sSky_TextureID = pBitmaps_LOD->LoadTexture((const char *)v107, (enum TEXTURE_TYPE)v108);
    strcpy(pGroundTileset, byte_6BE124_cfg_textures_DefaultGroundTexture);
    v34 = pTileTable->GetTileById(pTileTypes[0].uTileID);
    v35 = pBitmaps_LOD->LoadTexture(v34->pTileName);
    v36 = sSky_TextureID;
    sMainTile_BitmapID = v35;
    if ( v36 != -1 )
      pBitmaps_LOD->pTextures[v36].palette_id2 = pPaletteManager->LoadPalette(pBitmaps_LOD->pTextures[v36].palette_id1);

    v37 = sMainTile_BitmapID;
    if ( v37 != -1 )
      pBitmaps_LOD->pTextures[v37].palette_id2 = pPaletteManager->LoadPalette(pBitmaps_LOD->pTextures[v37].palette_id1);

    _47F0E2();
    pGameLoadingUI_ProgressBar->Progress();
    fclose((FILE *)v22);
    goto LABEL_150;
  }*/

  assert(sizeof(BSPModel) == 188);

  if (!pGames_LOD->DoesContainerExist(pFilename))
    Error("Unable to find %s in Games.LOD", pFilename);


  char pMinimapTextureFilename[1024];
  strcpy(pMinimapTextureFilename, pFilename);
  pMinimapTextureFilename[strlen(pMinimapTextureFilename) - 4] = 0;
  viewparams->uTextureID_LocationMap = pIcons_LOD->LoadTexture(pMinimapTextureFilename, TEXTURE_16BIT_PALETTE);

  //strcpy(FileName, pContainer);
  strcpy(Str, pFilename);
  strcpy(Str + strlen(Str) - 4, ".odm");
  //v141 = &v139;
  //v38 = strlen(pFilename);
  //strcpy((char *)&v139 + v38, ".odm");
  pFile = pGames_LOD->FindContainer(Str, true);
  //Str[strlen(Str) - 4] = 0;

  header.uCompressedSize = 0;
  header.uDecompressedSize = 0;
  //ptr = v39;
  header.uVersion = 91969;
  header.pMagic[0] = 'm';
  header.pMagic[1] = 'v';
  header.pMagic[2] = 'i';
  header.pMagic[3] = 'i';
  fread(&header, 0x10, 1, pFile);
  if (header.uVersion != 91969 ||
      header.pMagic[0] != 'm' ||
      header.pMagic[1] != 'v' ||
      header.pMagic[2] != 'i' ||
      header.pMagic[3] != 'i')
  {
    MessageBoxW(nullptr, L"Can't load file!",
                L"E:\\WORK\\MSDEV\\MM7\\MM7\\Code\\Odmap.cpp:507", 0);
  }
  //v40 = header.uCompressedSize;
  //pSource = header.uDecompressedSize;
  //v41 = malloc(header.uDecompressedSize);
  uchar* pSrcMem = (unsigned char *)malloc(header.uDecompressedSize);
  uchar* pSrc = pSrcMem;
  //v42 = v41;
  //HIDWORD(v142) = (uint32)pSrc;
  if (header.uCompressedSize < header.uDecompressedSize)
  {
    char* pComressedSrc = (char *)malloc(header.uCompressedSize);
    fread(pComressedSrc, header.uCompressedSize, 1, pFile);

    uint actualDecompressedSize = header.uDecompressedSize;
    zlib::MemUnzip(pSrc, &actualDecompressedSize, pComressedSrc, header.uCompressedSize);
    free(pComressedSrc);
  }
  else
  {
    fread(pSrc, header.uDecompressedSize, 1, pFile);
  }

  memcpy(pLevelFilename, pSrc, 0x20);
  memcpy(pLocationFileName, pSrc + 0x20, 0x20);
  memcpy(pLocationFileDescription, pSrc + 0x40, 0x20);
  memcpy(pSkyTextureName, pSrc + 3 * 32, 32);
  memcpy(pGroundTileset, pSrc + 0x80, 0x20);
  memcpy(pTileTypes, pSrc + 0xA0, 0x10);
  pSrc += 0xB0;

  //v43 = (char *)pSrc + 176;
  LoadTileGroupIds();
  LoadRoadTileset();
  strcpy(pGroundTileset, "grastyl");

  pGameLoadingUI_ProgressBar->Progress();					//прогресс загрузки

  //*******************Terrain**************************//
  pTerrain.Initialize();
  memcpy(pTerrain.pHeightmap, pSrc, 0x4000);				//карта высот
  pSrc += 0x4000;

  memcpy(pTerrain.pTilemap, pSrc, 0x4000);					//карта тайлов
  pSrc += 0x4000;

  memcpy(pTerrain.pAttributemap, pSrc, 0x4000);				// карта аттрибутов
  pSrc += 0x4000;

  //v43 = (char *)v43 + 16384;
  //v108 = (int)ptr_D4;
  free(pCmap);
  pCmap = malloc(0x8000);
  pTerrain.FillDMap(0, 0, 128, 128);						//

  pGameLoadingUI_ProgressBar->Progress();					//прогресс загрузки

  memcpy(&uNumTerrainNormals, pSrc, 4);						// количество нормалей
  memcpy(pTerrainSomeOtherData.data(), pSrc + 4, 0x20000);
  pSrc += 4 + 0x20000;
  //v43 = (char *)v43 + 131072;
  memcpy(pTerrainNormalIndices.data(), pSrc, 0x10000);		//индексы нормалей
  pSrc += 0x10000;
  //v43 = (char *)v43 + 65536;

  //pFilename = (char *)(12 * uNumTerrainNormals);
  pTerrainNormals = (Vec3_float_ *)malloc(sizeof(Vec3_float_) * uNumTerrainNormals);//карта нормалей
  memcpy(pTerrainNormals, pSrc, 12 * uNumTerrainNormals);
  pSrc += 12 * uNumTerrainNormals;
  //v44 = (char *)v43 + (int)pFilename;
  //v44 = (char *)v44 + 4;
  //v45 = uNumBModels;
  //v108 = (int)"BDdata";

  pGameLoadingUI_ProgressBar->Progress();					//прогресс загрузки
  
  //************BModels************************//
  //v107 = 188 * v45;
  //v106 = (char *)pBModels;
  //v46 = (BSPModel *)malloc(v106, 188 * v45, "BDdata");
  //v47 = uNumBModels;
  memcpy(&uNumBModels, pSrc, 4);							//количество BModel'ей
  pBModels = (BSPModel *)malloc(188 * uNumBModels);
  memcpy(pBModels, pSrc + 4, 188 * uNumBModels);			//BModel'и
  pSrc += 4 + 188 * uNumBModels;
  
  pGameLoadingUI_ProgressBar->Progress();					//прогресс загрузки

  //uSourceLen = (char *)v44 + (int)pFilename;
  //v151 = 0;
  for (uint i = 0; i < uNumBModels; ++i)
  {
    //v48 = 0;
    //BSPModel* model = &pBModels[i];

    pBModels[i].pVertices.pVertices = nullptr;
    pBModels[i].pFaces = nullptr;
    pBModels[i].pFacesOrdering = nullptr;
    pBModels[i].pNodes = nullptr;
      //FileName[0] = 0;
      //v108 = (int)&pBModels[i];
      //sprintf(FileName, "%s", v108);
      //v49 = pBModels;
      //v138 = 0;
      //v50 = &pBModels[v48];
      //v108 = (int)FileName;
      //v107 = 12 * v50->pVertices.uNumVertices;
      //v106 = (char *)v50->pVertices.pVertices;
    assert(sizeof(Vec3_int_) == 12);
    uint verticesSize = pBModels[i].pVertices.uNumVertices * sizeof(Vec3_int_);
    pBModels[i].pVertices.pVertices = (Vec3_int_ *)malloc(verticesSize);
    memcpy(pBModels[i].pVertices.pVertices, pSrc, verticesSize);
    pSrc += verticesSize;
      //v51 = &pBModels[v48];
      //v108 = (int)FileName;
      //v107 = 308 * v51->uNumFaces;
      //v106 = (char *)v51->pFaces;
    assert(sizeof(ODMFace) == 308);
    uint facesSize = pBModels[i].uNumFaces * sizeof(ODMFace);
    pBModels[i].pFaces = (ODMFace *)malloc(facesSize);
    memcpy(pBModels[i].pFaces, pSrc, facesSize);
    pSrc += facesSize;
      //v52 = &pBModels[v48];
      //v108 = (int)FileName;
      //v107 = 2 * v52->uNumFaces;
      //v106 = (char *)v52->pFacesOrdering;
    uint facesOrderingSize = pBModels[i].uNumFaces * sizeof(short);
    pBModels[i].pFacesOrdering = (unsigned __int16 *)malloc(facesOrderingSize);
    memcpy(pBModels[i].pFacesOrdering, pSrc, facesOrderingSize);
    pSrc += facesOrderingSize;
      //v53 = &pBModels[v48];
      //v108 = (int)FileName;
      //v107 = 8 * v53->uNumNodes;
      //v106 = (char *)v53->pNodes;
    assert(sizeof(BSPNode) == 8);
    uint nodesSize = pBModels[i].uNumNodes * sizeof(BSPNode);
    pBModels[i].pNodes = (BSPNode *)malloc(nodesSize);
    memcpy(pBModels[i].pNodes, pSrc, nodesSize);
    pSrc += nodesSize;
      //v54 = &pBModels[v48];
      //v108 = 12 * v54->pVertices.uNumVertices;
      //pFilename = (char *)v108;
      //v107 = (int)uSourceLen;
      //v106 = (char *)v54->pVertices.pVertices;
      //memcpy(v106, uSourceLen, v108);
      //uSourceLen = (char *)uSourceLen + (int)pFilename;
      //v55 = &pBModels[v48];
      //v105 = 308 * v55->uNumFaces;
      //v104 = uSourceLen;
      //v103 = v55->pFaces;
      //pFilename = (char *)v105;
      //memcpy(v103, uSourceLen, v105);
      //v56 = &pBModels[v48];
      //uSourceLen = (char *)uSourceLen + (int)pFilename;
      //v57 = v56->pFacesOrdering;
      //pFilename = (char *)(2 * v56->uNumFaces);
      //memcpy(v57, uSourceLen, (size_t)pFilename);
      //v58 = &pBModels[v48];
      //uSourceLen = (char *)uSourceLen + (int)pFilename;
      //v59 = v58->pNodes;
      //pFilename = (char *)(8 * v58->uNumNodes);
      //memcpy(v59, uSourceLen, (size_t)pFilename);
      //uSourceLen = (char *)uSourceLen + (int)pFilename;
    //ptr = (FILE *)malloc(10 * model->uNumFaces);
    const char* textureFilenames = (const char *)malloc(10 * pBModels[i].uNumFaces);
      //pFilename = (char *)(10 * pBModels[v48].uNumFaces);
    memcpy((char *)textureFilenames, pSrc, 10 * pBModels[i].uNumFaces);
    pSrc += 10 * pBModels[i].uNumFaces;
      //v144 = 0;
      //uSourceLen = (char *)uSourceLen + (int)pFilename;
      //v60 = pBModels;
    for (uint j = 0; j < pBModels[i].uNumFaces; ++j)
    {
      const char* texFilename = &textureFilenames[j * 10];
    //v149 = 0;
    //Str2 = (char *)ptr;

      //ODMFace* face = &pBModels[i].pFaces[j];
      //pFilename = (char *)v149 + (unsigned int)v60[v48].pFaces;
      if ( !(pBModels[i].pFaces[j].uAttributes & FACE_DONT_CACHE_TEXTURE))
      {
        v62 = pBitmaps_LOD->LoadTexture(texFilename);
//        v63 = (ODMFace *)pFilename;
		pBModels[i].pFaces[j].uTextureID = v62;
		//v145 = (signed __int16)v62 != -1 ? &pBitmaps_LOD->pTextures[(signed __int16)v62] : 0;
		//v108 = ((signed __int16)v62 != -1 ? pBitmaps_LOD->pTextures[(signed __int16)v62].palette_id1 : 36);
		if ((signed __int16)v62 != -1)
			pBitmaps_LOD->pTextures[v62].palette_id2 = pPaletteManager->LoadPalette(pBitmaps_LOD->pTextures[v62].palette_id1);
		//goto LABEL_69;
		//goto LABEL_68;
      }
	  else
	  {
		  //v61 = pTextureFrameTable->FindTextureByName(texFilename);
		  pBModels[i].pFaces[j].uTextureID = pTextureFrameTable->FindTextureByName(texFilename);
		  if (!pBModels[i].pFaces[j].uTextureID)
		  {
			  v62 = pBitmaps_LOD->LoadTexture(texFilename);
			  //v63 = (ODMFace *)pFilename;
			  pBModels[i].pFaces[j].uAttributes &= ~FACE_DONT_CACHE_TEXTURE;
			  //LABEL_68:
			  pBModels[i].pFaces[j].uTextureID = v62;
			  //v145 = (signed __int16)v62 != -1 ? &pBitmaps_LOD->pTextures[(signed __int16)v62] : 0;
			  //v108 = ((signed __int16)v62 != -1 ? pBitmaps_LOD->pTextures[(signed __int16)v62].palette_id1 : 36);
			  if ((signed __int16)v62 != -1)
				  pBitmaps_LOD->pTextures[v62].palette_id2 = pPaletteManager->LoadPalette(pBitmaps_LOD->pTextures[v62].palette_id1);
			  //goto LABEL_69;
		  }
		  else
			  pTextureFrameTable->LoadAnimationSequenceAndPalettes(pBModels[i].pFaces[j].uTextureID);
	  }
//LABEL_69:
      if (pBModels[i].pFaces[j].sCogTriggeredID)
      {
        if (pBModels[i].pFaces[j].HasEventHint())
          pBModels[i].pFaces[j].uAttributes |= FACE_HAS_EVENT;
        else
          pBModels[i].pFaces[j].uAttributes &= ~FACE_HAS_EVENT;
      }
      //++v144;
      //v60 = pBModels;
      //v149 = (char *)v149 + 308;
      //Str2 += 10;
      //if ( v144 >= (signed int)v60[v48].uNumFaces )
        //goto LABEL_74;
      }

//LABEL_74:
      free((void *)textureFilenames);
//      ++v151;
//      ++v48;
//      if ( v151 >= (signed int)uNumBModels )
//        goto LABEL_75;
  }
//LABEL_75:
  pGameLoadingUI_ProgressBar->Progress();					//прогресс загрузки

  //******************Decorations**********************//
  memcpy(&uNumLevelDecorations, pSrc, 4);
  //uSourceLen = (char *)uSourceLen + 4;
  if (uNumLevelDecorations > 3000)
    MessageBoxW(nullptr, L"Can't load file!",
                L"E:\\WORK\\MSDEV\\MM7\\MM7\\Code\\Odmap.cpp:678", 0);

  assert(sizeof(LevelDecoration) == 32);
  //pFilename = (char *)(32 * uNumLevelDecorations);
  memcpy(pLevelDecorations.data(), pSrc + 4, uNumLevelDecorations * sizeof(LevelDecoration));
  pSrc += 4 + sizeof(LevelDecoration) * uNumLevelDecorations;

  pGameLoadingUI_ProgressBar->Progress();

  //v151 = 0;
  //uSourceLen = (char *)uSourceLen + (int)pFilename;
  for (uint i = 0; i < uNumLevelDecorations; ++i)
  {
    char name[256];
    memcpy(name, pSrc, sizeof(LevelDecoration));
    pSrc += sizeof(LevelDecoration);

    pLevelDecorations[i].uDecorationDescID = pDecorationList->GetDecorIdByName(name);
  }

  pGameLoadingUI_ProgressBar->Progress();					//прогресс загрузки

  memcpy(&numFaceIDListElems, pSrc, 4);

  //uSourceLen = (char *)uSourceLen + 4;
  //v108 = (int)pFaceIDLIST;
  free(pFaceIDLIST);
  pFaceIDLIST = nullptr;
  //v66 = field_DC;
  //pFaceIDLIST = 0;
  //v67 = malloc(0, 2 * v66, "IDLIST");
  uint faceIDListSize = 2 * numFaceIDListElems;
  pFaceIDLIST = (unsigned short *)malloc(faceIDListSize);
  //v68 = field_DC;
  //pFaceIDLIST = (unsigned __int16 *)v67;
  //pFilename = (char *)(2 * v68);
  memcpy(pFaceIDLIST, pSrc + 4, faceIDListSize);
  pSrc += 4 + faceIDListSize;

  //uSourceLen = (char *)uSourceLen + (int)pFilename;
  pGameLoadingUI_ProgressBar->Progress();					//прогресс загрузки

  //v108 = (int)pOMAP;
  //free((void *)v108);
  //pOMAP = 0;
  free(pOMAP);
  //v69 = malloc(0, 0x10000u, "OMAP");
  pOMAP = (unsigned int *)malloc(0x10000);
  //v108 = 65536;
  //pOMAP = (unsigned int *)v69;
  memcpy(pOMAP, pSrc, 65536);
  pSrc += 65536;

  //uSourceLen = (char *)uSourceLen + 65536;
  pGameLoadingUI_ProgressBar->Progress();

  memcpy(&uNumSpawnPoints, pSrc, 4);
  //uSourceLen = (char *)uSourceLen + 4;
  pGameLoadingUI_ProgressBar->Progress();					//прогресс загрузки
  //v70 = uNumSpawnPoints;
  //v108 = (int)"Spawn";
  //v107 = 24 * v70;
  //v106 = (char *)pSpawnPoints;
  assert(sizeof(SpawnPointMM7) == 24);
  uint spawnPointsSize = uNumSpawnPoints * sizeof(SpawnPointMM7);
  pSpawnPoints = (SpawnPointMM7 *)malloc(spawnPointsSize);
  //v72 = uNumSpawnPoints;
  //pSpawnPoints = v71;
  memcpy(pSpawnPoints, pSrc + 4, spawnPointsSize);
  pSrc += 4 + spawnPointsSize;

  pGameLoadingUI_ProgressBar->Progress();					//прогресс загрузки

  //****************.ddm file*********************//
  free(pSrcMem);
  strcpy(Str + strlen(Str) - 4, ".ddm");
  pFile = pNew_LOD->FindContainer(Str, 1);
  fread(&header, 0x10, 1, pFile);
  Str2 = 0;
  if (header.uVersion != 91969 ||
      header.pMagic[0] != 'm' ||
      header.pMagic[1] != 'v' ||
      header.pMagic[2] != 'i' ||
      header.pMagic[3] != 'i' )
  {
    MessageBoxW(nullptr, L"Can't load file!", L"E:\\WORK\\MSDEV\\MM7\\MM7\\Code\\Odmap.cpp:746", 0);
    Str2 = (char *)1;
  }
  //v74 = 0;
  //pFilename = (char *)header.uCompressedSize;
  //v149 = 0;
  //pDestLen = header.uDecompressedSize;
  if ( !Str2 )
  {
    pSrcMem = (unsigned char *)malloc(header.uDecompressedSize);
    pSrc = pSrcMem;
    //v149 = v75;
    if (header.uCompressedSize == header.uDecompressedSize)
      fread(pSrc, header.uDecompressedSize, 1, pFile);
    else if (header.uCompressedSize < header.uDecompressedSize)
    {
      void* compressedMem = malloc(header.uCompressedSize);
      fread(compressedMem, header.uCompressedSize, 1, pFile);

      uint actualDecompressedSize = header.uDecompressedSize;
      zlib::MemUnzip(pSrc, &actualDecompressedSize, compressedMem, header.uCompressedSize);
      free(compressedMem);
    }
    else
      MessageBoxW(nullptr, L"Can't load file!", L"E:\\WORK\\MSDEV\\MM7\\MM7\\Code\\Odmap.cpp:765", 0);

    assert(sizeof(DDM_DLV_Header) == 0x28);
    memcpy(&ddm, pSrc, sizeof(DDM_DLV_Header));
    pSrc += sizeof(DDM_DLV_Header);
    //v74 = (int)((char *)v75 + 40);
  }
  uint actualNumFacesInLevel = 0;
  for (uint i = 0; i < uNumBModels; ++i)
    actualNumFacesInLevel += pBModels[i].uNumFaces;

  if (ddm.uNumFacesInBModels)
  {
    if ( ddm.uNumBModels )
    {
      if (ddm.uNumDecorations)
      {
        if (ddm.uNumFacesInBModels != actualNumFacesInLevel ||
            ddm.uNumBModels != uNumBModels ||
            ddm.uNumDecorations != uNumLevelDecorations )
          Str2 = (char *)1;
      }
    }
  }

  if (dword_6BE364_game_settings_1 & GAME_SETTINGS_2000)
    pNumItems = 0x1BAF800;

  if (Str2 || ((unsigned int)((char *)File - ddm.uLastRepawnDay) >= pNumItems || !ddm.uLastRepawnDay))
  {

	  if (Str2)
	  {
		  memset(Dst, 0, 0x3C8);
		  memset(Src, 0, 0x3C8);
	  }
	  if ((unsigned int)((char *)File - ddm.uLastRepawnDay) >= pNumItems || !ddm.uLastRepawnDay)
	  {
		  memcpy(Dst, pSrc, 968);
		  memcpy(Src, pSrc + 968, 968);
	  }
		  free(pSrcMem);

		  ddm.uLastRepawnDay = (int)File;
		  if (Str2 == 0)
			  ++ddm.uNumRespawns;
		  v108 = 0;
		  *thisa = 1;
		  pFile = pGames_LOD->FindContainer(Str, 0);
		  fread(&header, 0x10, 1, pFile);
		  //pFilename = (char *)header.uCompressedSize;
		  //pDestLen = header.uDecompressedSize;
		  //v82 = malloc(header.uDecompressedSize);
		  pSrcMem = (unsigned char *)malloc(header.uDecompressedSize);
		  //v149 = v82;
		  if (header.uCompressedSize == header.uDecompressedSize)
			  fread(pSrcMem, header.uDecompressedSize, 1, pFile);
		  else if (header.uCompressedSize < header.uDecompressedSize)
		  {
			  void* compressedMem = malloc(header.uCompressedSize);
			  fread(compressedMem, header.uCompressedSize, 1, pFile);

			  uint actualDecompressedSize = header.uDecompressedSize;
			  zlib::MemUnzip(pSrcMem, &actualDecompressedSize, compressedMem, header.uCompressedSize);
			  free(compressedMem);
		  }
		  else
			  MessageBoxW(nullptr, L"Can't load file!", L"E:\\WORK\\MSDEV\\MM7\\MM7\\Code\\Odmap.cpp:857", 0);

		  pSrc = pSrcMem + 40;
  }
  else
	*thisa = 0;
  memcpy(uFullyRevealedCellOnMap, pSrc, 968);
  memcpy(uPartiallyRevealedCellOnMap, pSrc + 968, 968);
  pSrc += 2 * 968;

  pGameLoadingUI_ProgressBar->Progress();					//прогресс загрузки

  if ( *thisa )
  {
    memcpy(uFullyRevealedCellOnMap, Dst, 968);
    memcpy(uPartiallyRevealedCellOnMap, Src, 968);
  }

  for (uint i = 0; i < uNumBModels; ++i)
  {
    BSPModel model = pBModels[i];
    for (uint j = 0; j < model.uNumFaces; ++j)
    {
      ODMFace face = model.pFaces[j];
      memcpy(&face.uAttributes, pSrc, 4);
      pSrc += 4;
    }

    for (uint j = 0; j < model.uNumFaces; ++j)
    {
      ODMFace face = model.pFaces[j];
      if (face.sCogTriggeredID)
      {
        if (face.HasEventHint())
          face.uAttributes |= FACE_HAS_EVENT_HINT;
        else
          face.uAttributes &= ~FACE_HAS_EVENT_HINT;//~0x00001000
      }
    }
  }

  pGameLoadingUI_ProgressBar->Progress();					//прогресс загрузки

  for (uint i = 0; i < uNumLevelDecorations; ++i)
  {
    memcpy(&pLevelDecorations[i].uFlags, pSrc, 2);
    pSrc += 2;
  }

  pGameLoadingUI_ProgressBar->Progress();					//прогресс загрузки

  memcpy(&uNumActors, pSrc, 4);
  if (uNumActors > 500)
    MessageBoxW(nullptr, L"Can't load file!", L"E:\\WORK\\MSDEV\\MM7\\MM7\\Code\\Odmap.cpp:939", 0);

  pGameLoadingUI_ProgressBar->Progress();					//прогресс загрузки

  assert(sizeof(Actor) == 836);
  //pFilename = (char *)(836 * uNumActors);
  memcpy(pActors.data(), pSrc + 4, uNumActors * sizeof(Actor));
  pSrc += 4 + uNumActors * sizeof(Actor);
  //v92 = (char *)v91 + (int)pFilename;
  pGameLoadingUI_ProgressBar->Progress();					//прогресс загрузки

  memcpy(&uNumSpriteObjects, pSrc, 4);
  assert(uNumSpriteObjects <= 1000 && "Too many objects");
  assert(sizeof(SpriteObject) == 112);

  pGameLoadingUI_ProgressBar->Progress();					//прогресс загрузки

  //pFilename = (char *)(112 * uNumSpriteObjects);
  memcpy(pSpriteObjects.data(), pSrc + 4, uNumSpriteObjects * sizeof(SpriteObject));
  pSrc += 4 + uNumSpriteObjects * sizeof(SpriteObject);

  //v94 = (char *)v93 + (int)pFilename;
  pGameLoadingUI_ProgressBar->Progress();					//прогресс загрузки

  memcpy(&uNumChests, pSrc, 4);
  //v95 = (char *)v94 + 4;
  if (uNumChests > 20)
    MessageBoxW(nullptr, L"Can't load file!",
                L"E:\\WORK\\MSDEV\\MM7\\MM7\\Code\\Odmap.cpp:968", 0);

  pGameLoadingUI_ProgressBar->Progress();					//прогресс загрузки

  assert(sizeof(Chest) == 5324);
  //pFilename = (char *)(5324 * uNumChests);
  memcpy(pChests.data(), pSrc + 4 , uNumChests * sizeof(Chest));
  pSrc += 4 + uNumChests * sizeof(Chest);
  //v96 = (char *)v95 + (int)pFilename;
  pGameLoadingUI_ProgressBar->Progress();					//прогресс загрузки

  memcpy(&stru_5E4C90_MapPersistVars, pSrc, 0xC8);
  pSrc += 0xC8;

  pGameLoadingUI_ProgressBar->Progress();					//прогресс загрузки
  memcpy(&loc_time, pSrc, 0x38u);

  free(pSrcMem);

  pTileTable->InitializeTileset(Tileset_Dirt);
  pTileTable->InitializeTileset(Tileset_Snow);
  pTileTable->InitializeTileset(pTileTypes[0].tileset);
  pTileTable->InitializeTileset(pTileTypes[1].tileset);
  pTileTable->InitializeTileset(pTileTypes[2].tileset);
  pTileTable->InitializeTileset(pTileTypes[3].tileset);
  strcpy(pGroundTileset, byte_6BE124_cfg_textures_DefaultGroundTexture.data());
  TileDesc* v98 = pTileTable->GetTileById(pTileTypes[0].uTileID);
  sMainTile_BitmapID = pBitmaps_LOD->LoadTexture(v98->pTileName, TEXTURE_DEFAULT);
  if (sMainTile_BitmapID != -1)
    pBitmaps_LOD->pTextures[sMainTile_BitmapID].palette_id2 = pPaletteManager->LoadPalette(pBitmaps_LOD->pTextures[sMainTile_BitmapID].palette_id1);

  _47F0E2();

//LABEL_150:
  if ( pWeather->bRenderSnow ) //Ritor1: it's include for snow
    strcpy(loc_time.sky_texture_name, "sky19");
  else if (loc_time.uLastVisitDay)
  {
    if ( (signed int)((signed int)(signed __int64)((double)loc_time.uLastVisitDay * 0.234375) / 60 / 60 / 24) % 28 != pParty->uDaysPlayed )
    {
      if ( rand() % 100 >= 20 )
        v108 = dword_4EC268[rand() % dword_4EC2A8];
      else
        v108 = dword_4EC28C[rand() % dword_4EC2AC];
      sprintf(loc_time.sky_texture_name, "plansky%d", v108);
    }
  }
  else
    strcpy(loc_time.sky_texture_name, "plansky3");

  //New_SKY_NIGHT_ID = pBitmaps_LOD->LoadTexture("SKY13");
  //if (New_SKY_NIGHT_ID != -1)
   // pBitmaps_LOD->pTextures[New_SKY_NIGHT_ID].palette_id2 = pPaletteManager->LoadPalette(pBitmaps_LOD->pTextures[New_SKY_NIGHT_ID].palette_id1);

  //v101 = pBitmaps_LOD->LoadTexture(field_4F8);
  sSky_TextureID = pBitmaps_LOD->LoadTexture(loc_time.sky_texture_name);
  if (sSky_TextureID != -1)
    pBitmaps_LOD->pTextures[sSky_TextureID].palette_id2 = pPaletteManager->LoadPalette(pBitmaps_LOD->pTextures[sSky_TextureID].palette_id1);

  pPaletteManager->RecalculateAll();
  pSoundList->LoadSound(SOUND_RunDirt, 0);			//For Dirt tyle(для звука хождения по грязи)
  pSoundList->LoadSound(SOUND_WalkDirt, 0);			//для бега
  pSoundList->LoadSound(SOUND_RunRoad, 0);			//для звука хождения по дороге
  pSoundList->LoadSound(SOUND_WalkRoad, 0);
  pSoundList->LoadSound(SOUND_RunWood, 0);			//для звука хождения по дереву
  pSoundList->LoadSound(SOUND_WalkWood, 0);
  for (int i=0; i < 3;++i) 
  {
    switch ( pTileTypes[i].tileset )
    {
      case Tileset_Grass:
        pSoundList->LoadSound(SOUND_RunGrass, 0);	//для звука хождения по траве
        pSoundList->LoadSound(SOUND_WalkGrass, 0);
        break;
      case Tileset_Snow:
        pSoundList->LoadSound(SOUND_RunSnow, 0);	//по снегу
        pSoundList->LoadSound(SOUND_WalkSnow, 0);
        break;
      case Tilset_Desert:
        pSoundList->LoadSound(SOUND_RunDesert, 0);	//по пустыне
         pSoundList->LoadSound(SOUND_WalkDesert, 0);
        break;
      case Tileset_CooledLava:
        pSoundList->LoadSound(SOUND_RunCooledLava, 0);//по лаве
        pSoundList->LoadSound(SOUND_WalkCooledLava, 0);
        break;
      case Tileset_Water:
        pSoundList->LoadSound(SOUND_RunWater, 0);		//по воде
        pSoundList->LoadSound(SOUND_WalkWater, 0);
        break;
      case Tileset_Badlands:
        pSoundList->LoadSound(SOUND_RunBadlands, 0);	//для звука ходьбы по бесплодным землям
        pSoundList->LoadSound(SOUND_WalkBadlands, 0);
        break;
      case Tileset_Swamp:
        pSoundList->LoadSound(SOUND_RunSwamp, 0);		//по болоту
        pSoundList->LoadSound(SOUND_WalkSwamp, 0);
        break;
    }
  }
  return true;
}

//----- (0047ECC1) --------------------------------------------------------
int OutdoorLocation::GetTileIdByTileMapId(signed int a2)
{
  signed int result; // eax@2
  int v3; // eax@3

  if ( a2 >= 90 )
  {
    v3 = (a2 - 90) / 36;
    if ( v3 && v3 != 1 && v3 != 2 )
    {
      if ( v3 == Tileset_CooledLava )
        result = this->pTileTypes[3].uTileID;
      else
        result = a2;
    }
    else
      result = this->pTileTypes[v3].uTileID;
  }
  else
    result = 0;
  return result;
}

//----- (0047ED08) --------------------------------------------------------
unsigned int OutdoorLocation::DoGetTileTexture(signed int sX, signed int sY)
{
  int v3; // esi@5
//  unsigned int result; // eax@9

  assert(sX < 128 && sY < 128);

 v3 = this->pTerrain.pTilemap[sY * 128 + sX];
 if (v3 < 198) // < Tileset_3
 {
  if (v3 >= 90)
    v3 = v3 + this->pTileTypes[(v3 - 90) / 36].uTileID - 36 * ((v3 - 90) / 36) - 90;
 }
 else
   v3 = v3 + this->pTileTypes[3].uTileID - 198;

  #pragma region "New: seasons change"

  if (change_seasons)
    switch (pParty->uCurrentMonth)
    {
      case 11: case 0: case 1: // winter
        if (v3 >= 90) // Tileset_Grass begins at TileID = 90
        {
          if (v3 <= 95) // some grastyl entries
            v3 = 348;
          else if (v3 <= 113)  // rest of grastyl & all grdrt*
            v3 = 348 + (v3 - 96);
        }
      /*switch (v3)
      {
        case 102: v3 = 354; break;  // grdrtNE -> SNdrtne
        case 104: v3 = 356; break;  // grdrtNW -> SNdrtnw
        case 108: v3 = 360; break;  // grdrtN  -> SNdrtn
      }*/
      break;

      case 2: case 3: case 4: // spring
      case 8: case 9: case 10: // autumn
        if (v3 >= 90 && v3 <= 113) // just convert all Tileset_Grass to dirt
          v3 = 1;
      break;

      case 5: case 6: case 7: // summer
        //all tiles are green grass by default
      break;

      default: assert(pParty->uCurrentMonth >= 0 && pParty->uCurrentMonth < 12);
    }
  #pragma endregion

 return pTileTable->pTiles[v3].uBitmapID;
}

//----- (0047ED83) --------------------------------------------------------
int OutdoorLocation::_47ED83(signed int a2, signed int a3)
{
  assert(a2 < 128 && a3 < 128);

  return *(&this->pTerrain.pTilemap[128 * a3] + a2);
}

//----- (0047EDB3) --------------------------------------------------------
int OutdoorLocation::ActuallyGetSomeOtherTileInfo(signed int sX, signed int sY)
{
  assert(sX < 128 && sY < 128);
  int v3; // esi@5

  v3 = this->pTerrain.pTilemap[sY * 128 + sX];
  if ( v3 >= 90 )
    v3 = v3 + this->pTileTypes[(v3 - 90) / 36].uTileID - 36 * ((v3 - 90) / 36) - 90;
  return pTileTable->pTiles[v3].uAttributes;
}

//----- (0047EE16) --------------------------------------------------------
int OutdoorLocation::DoGetHeightOnTerrain(signed int sX, signed int sZ)
{
  assert(sX < 128 && sZ < 128);

  return 32 * pTerrain.pHeightmap[sZ * 128 + sX];
}

//----- (0047EE49) --------------------------------------------------------
int OutdoorLocation::GetSoundIdByPosition( signed int X_pos, signed int Y_pos, int running )
    {
  signed int v4; // eax@5
  signed int v5; // eax@7
//  int v6; // eax@8
  signed int v8; // eax@9
  int modif=0;

  if ( X_pos < 0 || X_pos > 127 || Y_pos < 0 || Y_pos > 127 )
    v4 = 0;
  else
    v4 = this->pTerrain.pTilemap[128 * Y_pos + X_pos];
  v5 = GetTileIdByTileMapId(v4);
  if (running)
      modif=-39;
  if ( !v5 )
    return 92+modif;
   
  switch (pTileTable->pTiles[v5].tileset)
  {
  case 0:  return 93+ modif; 
  case 1:  return 97+ modif; 
  case 2:  return 91+ modif;
  case 3:  return 90+ modif;
  case 4:  return 101+ modif;
  case 5:  return 95+ modif;
  case 6:  return 88+ modif;
  case 7: return 100+ modif;
  case 8: return 93+ modif;
 default:
     v8=pTileTable->pTiles[v5].tileset;
     if ( (v8 > 9 && v8 <= 17) || (v8 > 21 && v8 <= 27) )
        return 96+ modif;
     else
        return 95+ modif;
  }

}

//----- (0047EF60) --------------------------------------------------------
int OutdoorLocation::UpdateDiscoveredArea(int X_grid_pos, int Y_grid_poa, int a4)
{
  for (int i = -10; i < 10; i++)
  {
    int currYpos = Y_grid_poa + i - 20;
    for (int j = -10; j < 10; j++)
    {
      int currXpos = X_grid_pos + j - 20;
      int distanceSquared = i * i + j * j;
      if ( distanceSquared <= 100 && currYpos >= 0 && currYpos <= 87 && currXpos >= 0 && currXpos <= 87 )
      {
        unsigned char v13 = 1 << (7 - currXpos % 8);
        this->uPartiallyRevealedCellOnMap[currYpos][currXpos / 8] |= v13;
        if ( distanceSquared <= 49 )
          this->uFullyRevealedCellOnMap[currYpos][currXpos / 8] |= v13;
      }
    }
  }
  return 1;
}


//----- (0047F04C) --------------------------------------------------------
bool OutdoorLocation::IsMapCellFullyRevealed(signed int x_pos, signed int y_pos)
{
  if ( x_pos < 0 || x_pos >= 88 || y_pos < 0 || y_pos >= 88 )
    return false;
  else
    return (uFullyRevealedCellOnMap[y_pos][x_pos/8] & (1 << (7 - (x_pos) % 8))) != 0;
}

//----- (0047F097) --------------------------------------------------------
bool OutdoorLocation::IsMapCellPartiallyRevealed(signed int x_pos, signed int y_pos)
{
  if ( x_pos < 0 || x_pos >= 88 || y_pos < 0 || y_pos >= 88 )
    return false;
  else
    return (uPartiallyRevealedCellOnMap[y_pos][x_pos/8] & (1 << (7 - (x_pos) % 8))) != 0;
}

//----- (0047F0E2) --------------------------------------------------------
bool OutdoorLocation::_47F0E2()
{
  for ( uint i = 0; i < (signed int)pBitmaps_LOD->uNumLoadedFiles; ++i )
  {
    //if ( i != -1 ? (int)&pBitmaps_LOD->pTextures[i] : 0 )
      pBitmaps_LOD->pTextures[i].uDecompressedSize = this->pTerrain._47CB57((int)pBitmaps_LOD->pTextures[i].pLevelOfDetail0_prolly_alpha_mask,
                                                    pBitmaps_LOD->pTextures[i].palette_id2,
                                                    pBitmaps_LOD->pTextures[i].uTextureWidth * pBitmaps_LOD->pTextures[i].uTextureHeight);
  }
  return 1;
}

//----- (0047F138) --------------------------------------------------------
bool OutdoorLocation::PrepareDecorations()
{
  signed int v1; // ebx@1
  signed int v8; // [sp+Ch] [bp-4h]@1

  v1 = 0;
  v8 = 0;
  if ( !_stricmp(pCurrentMapName, "out09.odm") )
    v8 = 1;

  for (uint i = 0; i < uNumLevelDecorations; ++i)
  {
    LevelDecoration* decor = &pLevelDecorations[i];

      pDecorationList->InitializeDecorationSprite(decor->uDecorationDescID);
      if ( pDecorationList->pDecorations[decor->uDecorationDescID].uSoundID && _6807E0_num_decorations_with_sounds_6807B8 < 9 )
      {
        pSoundList->LoadSound(pDecorationList->pDecorations[decor->uDecorationDescID].uSoundID, 0);
        _6807B8_level_decorations_ids[_6807E0_num_decorations_with_sounds_6807B8++] = i;
      }
      if ( v8 && decor->uCog == 20 )
        decor->uFlags |= LEVEL_DECORATION_OBELISK_CHEST;
      if ( !decor->uEventID )
      {
        if ( decor->IsInteractive() )
        {
          if ( v1 < 124 )
          {
            decor->_idx_in_stru123 = v1 + 75;
            if ( !stru_5E4C90_MapPersistVars._decor_events[v1++] )
              decor->uFlags |= LEVEL_DECORATION_INVISIBLE;
          }
        }
      }
  }

  pGameLoadingUI_ProgressBar->Progress();
  return true;
}
// 6807E0: using guessed type int _6807E0_num_decorations_6807B8;

//----- (0047F223) --------------------------------------------------------
void OutdoorLocation::ArrangeSpriteObjects()
{
  OutdoorLocation *v5; // [sp+0h] [bp-4h]@1

  v5 = this;
  if ( (signed int)uNumSpriteObjects > 0 )
  {
    for ( int i = 0; i < (signed int)uNumSpriteObjects; ++i )
    {
      if ( pSpriteObjects[i].uObjectDescID )
      {
        if ( !(pSpriteObjects[i].uAttributes & 8) && !(pObjectList->pObjects[pSpriteObjects[i].uObjectDescID].uFlags & 0x10) )
          pSpriteObjects[i].vPosition.z = GetTerrainHeightsAroundParty2(pSpriteObjects[i].vPosition.x, pSpriteObjects[i].vPosition.y, (int *)&v5, 0);
        if ( pSpriteObjects[i].stru_24.uItemID )
        {
          if ( pSpriteObjects[i].stru_24.uItemID != 220
            && pItemsTable->pItems[pSpriteObjects[i].stru_24.uItemID].uEquipType == EQUIP_POTION
            && !pSpriteObjects[i].stru_24.uEnchantmentType )
             pSpriteObjects[i].stru_24.uEnchantmentType = rand() % 15 + 5;
          pItemsTable->SetSpecialBonus(&pSpriteObjects[i].stru_24);
        }
      }
    }
  }
  pGameLoadingUI_ProgressBar->Progress();
}

//----- (0047F2D3) --------------------------------------------------------
bool OutdoorLocation::InitalizeActors(int a1)
{
  int alert_status; // [sp+348h] [bp-8h]@1
//  int v9; // [sp+34Ch] [bp-4h]@1

  alert_status = 0;
  for( int i = 0; i < uNumActors; ++i ) 
  {
    if ( !(pActors[i].uAttributes & ACTOR_UNKNOW7) )
    {
      if ( alert_status != 1 )
      {
        pActors[i].uCurrentActionTime = 0;
        pActors[i].uCurrentActionLength = 0;
        if ( pActors[i].uAttributes & ACTOR_UNKNOW11 )
          pActors[i].uAIState = AIState::Disabled;
        if ( pActors[i].uAIState != AIState::Removed && pActors[i].uAIState != AIState::Disabled &&
           (pActors[i].sCurrentHP == 0 || pActors[i].pMonsterInfo.uHP == 0) )
          pActors[i].uAIState = AIState::Dead;
        pActors[i].vVelocity.x = 0;
        pActors[i].vVelocity.y = 0;
        pActors[i].vVelocity.z = 0;
        pActors[i].UpdateAnimation();
        pActors[i].pMonsterInfo.uHostilityType = MonsterInfo::Hostility_Friendly;
        pActors[i].PrepareSprites(0);
      }
      else
      {
        pActors[i].uAIState = AIState::Disabled;
        pActors[i].uAttributes |= ACTOR_UNKNOW11;
      }
    }
    else if ( a1 == 0 )
    {
      pActors[i].uAIState = AIState::Disabled;
      pActors[i].uAttributes |= ACTOR_UNKNOW11;
    }
    else if ( alert_status != 0 )
    {
      pActors[i].uCurrentActionTime = 0;
      pActors[i].uCurrentActionLength = 0;
      if ( pActors[i].uAttributes & ACTOR_UNKNOW11 )
        pActors[i].uAIState = AIState::Disabled;
      if ( pActors[i].uAIState != AIState::Removed && pActors[i].uAIState != AIState::Disabled && 
         (pActors[i].sCurrentHP == 0 || pActors[i].pMonsterInfo.uHP == 0) )
        pActors[i].uAIState = AIState::Dead;
      pActors[i].vVelocity.x = 0;
      pActors[i].vVelocity.y = 0;
      pActors[i].vVelocity.z = 0;
      pActors[i].UpdateAnimation();
      pActors[i].pMonsterInfo.uHostilityType = MonsterInfo::Hostility_Friendly;
      pActors[i].PrepareSprites(0);
    }
    else 
    {
      pActors[i].uAIState = AIState::Disabled;
      pActors[i].uAttributes |= ACTOR_UNKNOW11;
      alert_status = GetAlertStatus();
    }
  }

  pGameLoadingUI_ProgressBar->Progress();
  //no use for this
//  Actor thisa;
//  thisa.pMonsterInfo.uID = 45;
//  thisa.PrepareSprites(0);
  return 1;
}

//----- (0047F3EA) --------------------------------------------------------
bool OutdoorLocation::LoadRoadTileset()
{
  pTileTypes[3].uTileID = pTileTable->GetTileForTerrainType(pTileTypes[3].tileset, 1);
  pTileTable->InitializeTileset(pTileTypes[3].tileset);
  return 1;
}

//----- (0047F420) --------------------------------------------------------
bool OutdoorLocation::LoadTileGroupIds()
{
  for (uint i = 0; i < 3; ++i)
    pTileTypes[i].uTileID = pTileTable->GetTileForTerrainType(pTileTypes[i].tileset, 1);

  return true;
}

//----- (0047B42C) --------------------------------------------------------
void OutdoorLocation::PrepareActorsDrawList()
{
  unsigned int result; // eax@1
  int z; // esi@5
  float v4; // ST48_4@8
  unsigned int v8; // eax@11
  int v9; // edx@11
  signed int v12; // eax@16
  SpriteFrame *v14; // eax@24
  SpriteFrame *v15; // ebx@25
  int v17; // eax@35
  int v18; // ST78_4@36
  int v19; // eax@36
  int v20; // ecx@38
  int v21; // eax@38
  int v22; // ecx@41
  int v23; // ST5C_4@43
  int v24; // esi@44
  signed __int64 v25; // qtt@45
  int v26; // ST54_4@45
  int v27; // ecx@45
  int v34; // ecx@54
  int v41; // [sp+24h] [bp-3Ch]@11
  int v42; // [sp+28h] [bp-38h]@38
  int v43; // [sp+28h] [bp-38h]@45
  int v44; // [sp+2Ch] [bp-34h]@36
  int v45; // [sp+2Ch] [bp-34h]@44
  int v46; // [sp+2Ch] [bp-34h]@45
  int v47; // [sp+30h] [bp-30h]@36
  int v48; // [sp+30h] [bp-30h]@41
  signed int v49; // [sp+34h] [bp-2Ch]@5
  int v50; // [sp+34h] [bp-2Ch]@36
  int v51; // [sp+34h] [bp-2Ch]@41
  int v53; // [sp+38h] [bp-28h]@36
  int y; // [sp+40h] [bp-20h]@5
  int x; // [sp+44h] [bp-1Ch]@5
  int v57; // [sp+48h] [bp-18h]@45
  int v58; // [sp+4Ch] [bp-14h]@45
  int X; // [sp+54h] [bp-Ch]@36
  signed __int16 v62; // [sp+5Ch] [bp-4h]@25

  //result = 0;
  //v59 = 0;
  for (int i = 0; i < uNumActors; ++i)
  {
    //v54 = 0;
    //v1 = pActors;//[0].vPosition.z;
    //do
    //{
    //Actor* actor = &pActors[i];
	  //v2 = actor->uAIState;

    pActors[i].uAttributes &= 0xFFFFFFF7;//~0x8
    if (pActors[i].uAIState == Removed || pActors[i].uAIState == Disabled)
      continue;

	  z = pActors[i].vPosition.z;
      v49 = 0;
	  x = pActors[i].vPosition.x;
	  y = pActors[i].vPosition.y;
      if (pActors[i].uAIState == Summoned)
      {
		if (PID_TYPE(pActors[i].uSummonerID) != OBJECT_Actor ||
             pActors[PID_ID(pActors[i].uSummonerID)].pMonsterInfo.uSpecialAbilityDamageDiceSides != 1 )
        {
          z += floorf(pActors[i].uActorHeight * 0.5f + 0.5f);
        }
        else
        {
          v49 = 1;
		  pEngine->pStru6Instance->_4A7F74(pActors[i].vPosition.x, pActors[i].vPosition.y, z);
          v4 = (1.0 - (double)pActors[i].uCurrentActionTime / (double)pActors[i].uCurrentActionLength) *
			   (double)(2 * pActors[i].uActorHeight);
          z -= floorf(v4 + 0.5f);
          if ( z > pActors[i].vPosition.z )
            z = pActors[i].vPosition.z;
        }
      }
      v8 = stru_5C6E00->Atan2(pActors[i].vPosition.x - pEngine->pIndoorCameraD3D->vPartyPos.x,
                              pActors[i].vPosition.y - pEngine->pIndoorCameraD3D->vPartyPos.y);
	  LOWORD(v9) = pActors[i].uYawAngle;
      v41 = ((signed int)(stru_5C6E00->uIntegerPi + ((signed int)stru_5C6E00->uIntegerPi >> 3) + v9 - v8) >> 8) & 7;
      if ( pParty->bTurnBasedModeOn )
      {
        v12 = pActors[i].uCurrentActionTime;
        if ( pActors[i].uCurrentActionAnimation == 1 )
          v12 = 32 * i + pMiscTimer->uTotalGameTimeElapsed;
      }
      else
      {
        v12 = pActors[i].uCurrentActionTime;
        if ( pActors[i].uCurrentActionAnimation == 1 )
          v12 = 32 * i + pEventTimer->uTotalGameTimeElapsed;
      }
      if ( (signed __int64)pActors[i].pActorBuffs[ACTOR_BUFF_STONED].uExpireTime > 0 || (signed __int64)pActors[i].pActorBuffs[ACTOR_BUFF_PARALYZED].uExpireTime > 0 )
        v12 = 0;
      if ( pActors[i].uAIState == 17 && !v49 )
        v14 = pSpriteFrameTable->GetFrame(uSpriteID_Spell11, v12);
      else if ( pActors[i].uAIState == 16 )
        v14 = pSpriteFrameTable->GetFrameBy_x(pActors[i].pSpriteIDs[pActors[i].uCurrentActionAnimation], v12);
      else
        v14 = pSpriteFrameTable->GetFrame(pActors[i].pSpriteIDs[pActors[i].uCurrentActionAnimation], v12);
      v62 = 0;
      v15 = v14;
      //v16 = (int *)v14->uFlags;
      if (v14->uFlags & 2)
        v62 = 2;
      if (v14->uFlags & 0x40000)
        v62 |= 0x40;
      if (v14->uFlags & 0x20000)
        LOBYTE(v62) = v62 | 0x80;
      if ((256 << v41) & v14->uFlags)
        v62 |= 4;
      if ( v15->uGlowRadius )
      {
        //LOBYTE(v16) = _4E94D3_light_type;
        pMobileLightsStack->AddLight(x, y, z, 0, v15->uGlowRadius, 0xFFu, 0xFFu, 0xFFu, _4E94D3_light_type);
      }
      v17 = (x - pEngine->pIndoorCameraD3D->vPartyPos.x) << 16;
      if (pEngine->pIndoorCameraD3D->sRotationX)
      {
        v18 = (y - pEngine->pIndoorCameraD3D->vPartyPos.y) << 16;
        v47 = (fixpoint_mul(v17, pEngine->pIndoorCameraD3D->int_cosine_y) + fixpoint_mul(v18, pEngine->pIndoorCameraD3D->int_sine_y));
        v50 = fixpoint_mul(v17, pEngine->pIndoorCameraD3D->int_sine_y);
        v53 = fixpoint_mul(v18, pEngine->pIndoorCameraD3D->int_cosine_y);
        v44 = (z - pEngine->pIndoorCameraD3D->vPartyPos.z) << 16;
        v19 = (fixpoint_mul(v44, pEngine->pIndoorCameraD3D->int_sine_x) + fixpoint_mul(v47, pEngine->pIndoorCameraD3D->int_cosine_x));
        X = fixpoint_mul(v44, pEngine->pIndoorCameraD3D->int_sine_x) + fixpoint_mul(v47, pEngine->pIndoorCameraD3D->int_cosine_x);
        if ( v19 < 262144 || v19 > pODMRenderParams->shading_dist_mist << 16 )
          continue;
        v20 = v53 - v50;
        v42 = v53 - v50;
        v21 = (fixpoint_mul(v44, pEngine->pIndoorCameraD3D->int_cosine_x) - fixpoint_mul(v47, pEngine->pIndoorCameraD3D->int_sine_x));
      }
      else
      {
        v48 = (y - pEngine->pIndoorCameraD3D->vPartyPos.y) << 16;
        v51 = fixpoint_mul(v17, pEngine->pIndoorCameraD3D->int_cosine_y);
        v22 = fixpoint_mul(v48, pEngine->pIndoorCameraD3D->int_sine_y);
        X = v22 + v51;
        if ( v22 + v51 < 262144 || v22 + v51 > pODMRenderParams->shading_dist_mist << 16 )
          continue;
        v23 = fixpoint_mul(((x - pEngine->pIndoorCameraD3D->vPartyPos.x) << 16), pEngine->pIndoorCameraD3D->int_sine_y);
        v20 = fixpoint_mul(v48, pEngine->pIndoorCameraD3D->int_cosine_y) - v23;
        v42 = fixpoint_mul(v48, pEngine->pIndoorCameraD3D->int_cosine_y) - v23;
        v21 = (z - pEngine->pIndoorCameraD3D->vPartyPos.z) << 16;
      }
      v45 = v21;
      v24 = abs(v20);
      if ( abs(X) >= v24 )
      {
        LODWORD(v25) = 0;
        HIDWORD(v25) = SLOWORD(pODMRenderParams->int_fov_rad);
        v58 = v25 / X;
        v26 = v25 / X;
        LODWORD(v25) = 0;
        HIDWORD(v25) = SLOWORD(pODMRenderParams->int_fov_rad);
        v57 = v25 / X;
        v27 = pViewport->uScreenCenterX - ((signed int)(fixpoint_mul(v26, v42) + 0x8000) >> 16);
        v43 = pViewport->uScreenCenterX - ((signed int)(fixpoint_mul(v26, v42) + 0x8000) >> 16);
        v46 = pViewport->uScreenCenterY - ((signed int)(fixpoint_mul(v25 / X, v45) + 0x8000) >> 16);
        result = uNumBillboardsToDraw;
        //v28 = &pBillboardRenderList[uNumBillboardsToDraw];
        if (uNumBillboardsToDraw >= 500)
          return;
        ++uNumBillboardsToDraw;
        ++uNumSpritesDrawnThisFrame;
        pActors[i].uAttributes |= ACTOR_UNKNOW2;
        pBillboardRenderList[uNumBillboardsToDraw - 1].HwSpriteID = v15->pHwSpriteIDs[v41];
        pBillboardRenderList[uNumBillboardsToDraw - 1].uIndoorSectorID = 0;
        pBillboardRenderList[uNumBillboardsToDraw - 1].uPalette = v15->uPaletteIndex;
        pBillboardRenderList[uNumBillboardsToDraw - 1]._screenspace_x_scaler_packedfloat = fixpoint_mul(v15->scale, v58);
        pBillboardRenderList[uNumBillboardsToDraw - 1]._screenspace_y_scaler_packedfloat = fixpoint_mul(v15->scale, v57);
        if ( pActors[i].pActorBuffs[ACTOR_BUFF_SHRINK].uExpireTime <= 0 )
        {
          if ( pActors[i].pActorBuffs[ACTOR_BUFF_MASS_DISTORTION].uExpireTime > 0 )
          {
            pBillboardRenderList[uNumBillboardsToDraw - 1]._screenspace_y_scaler_packedfloat = fixpoint_mul(pEngine->pStru6Instance->_4A806F(&pActors[i]),
                   pBillboardRenderList[uNumBillboardsToDraw - 1]._screenspace_y_scaler_packedfloat);
            LOWORD(v27) = v43;
          }
        }
        else
        {
          if ( pActors[i].pActorBuffs[ACTOR_BUFF_SHRINK].uPower )
          {
            pBillboardRenderList[uNumBillboardsToDraw - 1]._screenspace_y_scaler_packedfloat = fixpoint_mul(65536 / pActors[i].pActorBuffs[ACTOR_BUFF_SHRINK].uPower,
                     pBillboardRenderList[uNumBillboardsToDraw - 1]._screenspace_y_scaler_packedfloat);
            LOWORD(v27) = v43;
          }
        }
        pBillboardRenderList[uNumBillboardsToDraw - 1].uScreenSpaceX = v27;
        pBillboardRenderList[uNumBillboardsToDraw - 1].uScreenSpaceY = v46;
        pBillboardRenderList[uNumBillboardsToDraw - 1].world_x = x;
        pBillboardRenderList[uNumBillboardsToDraw - 1].world_y = y;
        pBillboardRenderList[uNumBillboardsToDraw - 1].world_z = z;
        HIWORD(v34) = HIWORD(X);
        LOWORD(v34) = 0;
        pBillboardRenderList[uNumBillboardsToDraw - 1].dimming_level = 0;
        pBillboardRenderList[uNumBillboardsToDraw - 1].sZValue = v34 + PID(OBJECT_Actor, i);
        pBillboardRenderList[uNumBillboardsToDraw - 1].field_14_actor_id = i;
        //v35 = pMonsterList->pMonsters;
        //v36 = pActors[i].pMonsterInfo.uID;
        pBillboardRenderList[uNumBillboardsToDraw - 1].field_1E = v62 | 0x200;
        pBillboardRenderList[uNumBillboardsToDraw - 1].pSpriteFrame = v15;
        pBillboardRenderList[uNumBillboardsToDraw - 1].sTintColor = pMonsterList->pMonsters[pActors[i].pMonsterInfo.uID - 1].sTintColor;//*((int *)&v35[v36] - 36);
        if (pActors[i].pActorBuffs[ACTOR_BUFF_STONED].uExpireTime)
          pBillboardRenderList[uNumBillboardsToDraw - 1].field_1E = v62 | 0x200;
      }
//LABEL_58:
      //++v59;
      //v54 += 32;
      //result = v59;
      //++v1;
    //}
    //while ( v59 < (signed int)uNumActors );
  }
  //return result;
}

//----- (0044C1E8) --------------------------------------------------------
bool ODMFace::HasEventHint()
{
  signed int event_index; // eax@1
  _evt_raw* start_evt;
  _evt_raw* end_evt;

  event_index = 0;
  if ( (uLevelEVT_NumEvents - 1) <= 0 )
    return false;
  while ( pLevelEVT_Index[event_index].uEventID != this->sCogTriggeredID )
  {
    ++event_index;
    if ( event_index >= (signed int)(uLevelEVT_NumEvents - 1) )
      return false;
  }
  end_evt=(_evt_raw*)&pLevelEVT[pLevelEVT_Index[event_index+1].uEventOffsetInEVT];
  start_evt=(_evt_raw*)&pLevelEVT[pLevelEVT_Index[event_index].uEventOffsetInEVT];
  if ( (end_evt->_e_type != EVENT_Exit) || (start_evt->_e_type!= EVENT_MouseOver) )
    return false;
  else
    return true;
}
//----- (0046D49E) --------------------------------------------------------
int ODM_GetFloorLevel(int X, signed int Y, int Z, int __unused, int *pIsOnWater, int *bmodel_pid, int bWaterWalk)
{
  BSPModel *pBModel; // esi@4
  ODMFace *pFace; // ecx@11
//  int v14; // edx@20
  signed int v18; // edx@26
  int v19; // eax@28
//  int v20; // edx@30
//  int v21; // ST1C_4@30
  signed int v22; // edx@30
  signed __int64 v23; // qtt@30
  int v24; // eax@36
  signed int v25; // ecx@38
//  int result; // eax@42
  signed int current_floor_level; // ecx@43
//  int v28; // edi@44
  signed int v29; // edx@44
//  int v30; // esi@45
//  int v31; // eax@45
//  int v33; // ecx@59
//  int v36; // [sp+14h] [bp-2Ch]@24
//  int v38; // [sp+1Ch] [bp-24h]@2
  int v39; // [sp+20h] [bp-20h]@9
  signed int pBModelNum; // [sp+28h] [bp-18h]@1
  int pFaceNum; // [sp+2Ch] [bp-14h]@8
  bool current_vertices_Y; // [sp+30h] [bp-10h]@22
  bool next_vertices_Y; // [sp+34h] [bp-Ch]@24
  signed int v46; // [sp+3Ch] [bp-4h]@1
  signed int number_hits; // [sp+58h] [bp+18h]@22
  signed int next_floor_level; // [sp+58h] [bp+18h]@43

  v46 = 1;
  current_BModel_id[0] = -1;
  current_Face_id[0] = -1;
  odm_floor_level[0] = GetTerrainHeightsAroundParty2(X, Y, pIsOnWater, bWaterWalk);
  
  for ( pBModelNum = 0; pBModelNum < pOutdoor->uNumBModels; ++pBModelNum )
  {
    pBModel = &pOutdoor->pBModels[pBModelNum];
    if ( X <= pBModel->sMaxX && X >= pBModel->sMinX &&
         Y <= pBModel->sMaxY && Y >= pBModel->sMinY )
    {
      if ( pBModel->uNumFaces > 0 )
      {
        v39 = 0;
        for ( pFaceNum = 0; pFaceNum < pBModel->uNumFaces; ++pFaceNum )
        {
          pFace = &pBModel->pFaces[pFaceNum];
          if ( pFace->Ethereal() )
            continue;
          if ( (pFace->uPolygonType == POLYGON_Floor || pFace->uPolygonType == POLYGON_InBetweenFloorAndWall)
            && X <= pFace->pBoundingBox.x2 && X >= pFace->pBoundingBox.x1
            && Y <= pFace->pBoundingBox.y2 && Y >= pFace->pBoundingBox.y1 )
          {
            for ( uint i = 0; i < pFace->uNumVertices; ++i)
            {
              odm_floor_face_vert_coord_X[2 * i] = pFace->pXInterceptDisplacements[i] + pBModel->pVertices.pVertices[pFace->pVertexIDs[i]].x;
              odm_floor_face_vert_coord_Y[2 * i] = pFace->pYInterceptDisplacements[i] + pBModel->pVertices.pVertices[pFace->pVertexIDs[i]].y;
              odm_floor_face_vert_coord_X[2 * i + 1] = pFace->pXInterceptDisplacements[i] + pBModel->pVertices.pVertices[pFace->pVertexIDs[i + 1]].x;
              odm_floor_face_vert_coord_Y[2 * i + 1] = pFace->pYInterceptDisplacements[i] + pBModel->pVertices.pVertices[pFace->pVertexIDs[i + 1]].y;
            }
            odm_floor_face_vert_coord_X[2 * pFace->uNumVertices] = odm_floor_face_vert_coord_X[0];
            odm_floor_face_vert_coord_Y[2 * pFace->uNumVertices] = odm_floor_face_vert_coord_Y[0];

            current_vertices_Y = odm_floor_face_vert_coord_Y[0] >= Y;
            number_hits = 0;
            if ( 2 * pFace->uNumVertices > 0 )
            {
              for ( int i = 0; i < 2 * pFace->uNumVertices; ++i )
              {
                if ( number_hits >= 2 )
                  break;
                //v36 = odm_floor_face_vert_coord_Y[i + 1];
                next_vertices_Y = odm_floor_face_vert_coord_Y[i + 1] >= Y;
                if ( current_vertices_Y != next_vertices_Y )//проверка по Y
                {
                  v18 = odm_floor_face_vert_coord_X[i + 1] >= X ? 0 : 2;
                  v19 = v18 | (odm_floor_face_vert_coord_X[i] < X);
                  if ( v19 != 3 )
                  {
                    if ( !v19 )
                      ++number_hits;
                    else
                    {
                      LODWORD(v23) = (Y - odm_floor_face_vert_coord_Y[i]) << 16;
                      HIDWORD(v23) = (Y - odm_floor_face_vert_coord_Y[i]) >> 16;
                      v22 = ((((odm_floor_face_vert_coord_X[i + 1] - odm_floor_face_vert_coord_X[i]) * v23 / (odm_floor_face_vert_coord_Y[i + 1]
                              - odm_floor_face_vert_coord_Y[i])) >> 16) + odm_floor_face_vert_coord_X[i]);
                      if ( v22 >= X) 
                        ++number_hits;
                    }
                  }
                }
                current_vertices_Y = next_vertices_Y;
              }
              if ( number_hits == 1 )
              {
                if ( v46 >= 20 )
                  break;
                if ( pFace->uPolygonType == POLYGON_Floor )
                  v24 = pBModel->pVertices.pVertices[pFace->pVertexIDs[0]].z;
                else
                {
                  int a = fixpoint_mul(pFace->zCalc1, X);
                  int b = fixpoint_mul(pFace->zCalc2, Y);
                  int c = ((signed __int64)pFace->zCalc3 >> 16);
                  v24 = a + b + c;
                }
                v25 = v46++;
                odm_floor_level[v25] = v24;
                current_BModel_id[v25] = pBModelNum;
                current_Face_id[v25] = pFaceNum;
              }
            }
          }

        }
      }
    }
  }
  if ( v46 == 1 )
  {
    *bmodel_pid = 0;
    return odm_floor_level[0];
  }
  current_floor_level = 0;
  v29 = 0;
  if ( v46 <= 1 )
    *bmodel_pid = 0;
  else
  {
    current_floor_level = odm_floor_level[0];
    for ( uint i = 1; i < v46; ++i )
    {
      next_floor_level = odm_floor_level[i];
      if ( current_floor_level <= Z + 5 )
      {
        if ( next_floor_level > current_floor_level && next_floor_level <= Z + 5 )
        {
          current_floor_level = next_floor_level;
          v29 = i;
        }
      }
      else if ( next_floor_level < current_floor_level )
      {
        current_floor_level = next_floor_level;
        v29 = i;
      }
    }
    if ( !v29 )
      *bmodel_pid = 0;
    else
      *bmodel_pid = current_Face_id[v29] | (current_BModel_id[v29] << 6);
  }
  if ( v29 )
  {
    *pIsOnWater = false;
    if ( pOutdoor->pBModels[current_BModel_id[v29]].pFaces[current_Face_id[v29]].Fluid())
      *pIsOnWater = true;
  }
  if ( odm_floor_level[v29] >= odm_floor_level[0] )
    odm_floor_level[0] = odm_floor_level[v29];
  return odm_floor_level[0];
}

//not sure if right- or left-handed coordinate space assumed, so this could be normal of inverse normal
// for a right-handed system, that would be an inverse normal
//----- (0046DCC8) --------------------------------------------------------
void ODM_GetTerrainNormalAt(int pos_x, int pos_z, Vec3_int_ *out)
{
  uint grid_x = WorldPosToGridCellX(pos_x);
  uint grid_z = WorldPosToGridCellZ(pos_z) - 1;

  int grid_pos_x1 = GridCellToWorldPosX(grid_x);
  int grid_pos_x2 = GridCellToWorldPosX(grid_x + 1);
  int grid_pos_z1 = GridCellToWorldPosZ(grid_z);
  int grid_pos_z2 = GridCellToWorldPosZ(grid_z + 1);

  int x1z1_y = pOutdoor->DoGetHeightOnTerrain(grid_x, grid_z);
  int x2z1_y = pOutdoor->DoGetHeightOnTerrain(grid_x + 1, grid_z);
  int x2z2_y = pOutdoor->DoGetHeightOnTerrain(grid_x + 1, grid_z + 1);
  int x1z2_y = pOutdoor->DoGetHeightOnTerrain(grid_x, grid_z + 1);

  float side1_dx, side1_dy, side1_dz,
        side2_dx, side2_dy, side2_dz;

  int dx = abs(pos_x - grid_pos_x1),
       dz = abs(grid_pos_z1 - pos_z);
  if (dz >= dx)
  {
    side2_dx = (double)(grid_pos_x2 - grid_pos_x1);
    side2_dz = 0.0;//(double)(grid_pos_z2 - grid_pos_z2);  // bug?  z2 - z2 
    side2_dy = (double)(x2z2_y - x1z2_y);

    side1_dx = 0.0;//(double)(grid_pos_x1 - grid_pos_x1);
    side1_dz = (double)(grid_pos_z1 - grid_pos_z2);  //       z1 - z2 yes
    side1_dy = (double)(x1z1_y - x1z2_y);
    //Log::Warning(L"%S %S %u\n", __FILE__, __FUNCTION__, __LINE__);
    /*       |\
       side1 |  \
             |____\
             side 2      */
  }
  else
  {
    side2_dx = (double)(grid_pos_x1 - grid_pos_x2);
    side2_dz = 0.0;//(double)(grid_pos_z1 - grid_pos_z1);
    side2_dy = (double)(x1z1_y - x2z1_y);

    side1_dx = 0.0;//(double)(grid_pos_x2 - grid_pos_x1);
    side1_dz = (double)(grid_pos_z2 - grid_pos_z1);
    side1_dy = (double)(x2z2_y - x2z1_y);
    /*   side 2
         _____
         \    |
           \  | side 1
             \|       */
  }
  
  float nx = side1_dy * side2_dz - side1_dz * side2_dy;
  float ny = side1_dx * side2_dy - side1_dy * side2_dx;
  float nz = side1_dz * side2_dx - side1_dx * side2_dz;

  float mag = sqrt(nx * nx + ny * ny + nz * nz);
  if (fabsf(mag) < 1e-6f)
  {
    out->y = 0;
    out->x = 0;
    out->z = 65536;
  }
  else
  {
    float invmag = 1.0 / mag;
    out->x = invmag * nx * 65536.0;
    out->y = invmag * ny * 65536.0;
    out->z = invmag * nz * 65536.0;
  }
}
//----- (0046BE0A) --------------------------------------------------------
void ODM_UpdateUserInputAndOther()
{
  bool v0; // eax@5
  char pOut[32]; // [sp+8h] [bp-20h]@5

  UpdateObjects();
  ODM_ProcessPartyActions();
  if ( pParty->vPosition.x < -22528
    || pParty->vPosition.x > 22528
    || pParty->vPosition.y < -22528
    || pParty->vPosition.y > 22528 )
  {
    strcpy(pOutdoor->pLevelFilename, pCurrentMapName);
    v0 = pOutdoor->GetTravelDestination(pParty->vPosition.x, pParty->vPosition.y, pOut, 32);
    if ( !bUnderwater && (pParty->uFlags & (PARTY_FLAGS_1_STANDING_ON_WATER | PARTY_FLAGS_1_FALLING | 0x04) || pParty->uFlags & 0x0200 || pParty->bFlying) || !v0 )
    {
      if ( pParty->vPosition.x < -22528 )
        pParty->vPosition.x = -22528;
      if ( pParty->vPosition.x > 22528 )
        pParty->vPosition.x = 22528;
      if ( pParty->vPosition.y < -22528 )
        pParty->vPosition.y = -22528;
      if ( pParty->vPosition.y > 22528 )
        pParty->vPosition.y = 22528;
    }
    else
    {
      pAudioPlayer->StopChannels(-1, -1);
      TravelUI_Load();
    }
  }
  UpdateActors_ODM();
  check_event_triggers();
}
//----- (0041F54A) --------------------------------------------------------
void OutdoorLocation::LoadActualSkyFrame()
{
  if ( pTexture_RestUI_CurrentSkyFrame )
    pTexture_RestUI_CurrentSkyFrame->Release();
  if ( pTexture_RestUI_CurrentHourglassFrame )
    pTexture_RestUI_CurrentHourglassFrame->Release();
  pIcons_LOD->SyncLoadedFilesCount();
  sprintf(pTmpBuf.data(), "TERRA%03d", pParty->uCurrentMinute / 6 + 10 * pParty->uCurrentHour);
  pTexture_RestUI_CurrentSkyFrame = pIcons_LOD->LoadTexturePtr(pTmpBuf.data(), TEXTURE_16BIT_PALETTE);
}


//----- (004626BA) --------------------------------------------------------
OutdoorLocation::OutdoorLocation()
{
  subconstuctor();
  uLastSunlightUpdateMinute = 0;

  uNumBModels = 0;
  pBModels = nullptr;
}

//----- (004626CD) --------------------------------------------------------
void OutdoorLocation::subconstuctor()
{
  //OutdoorLocationTerrain::OutdoorLocationTerrain(&this->pTerrain);
  field_F0 = 0;
  field_F4 = 0x40000000u;
  //DLVHeader::DLVHeader(&v1->ddm);
  pSpawnPoints = 0;
  pBModels = 0;
  pCmap = 0;
  pFaceIDLIST = 0;
  pOMAP = 0;
}

//----- (00481E55) --------------------------------------------------------
void ODM_Project(unsigned int uNumVertices)
{
  for ( uint i = 0; i < uNumVertices; i++ )
   {
    memcpy(&array_50AC10[i], &array_507D30[i], sizeof(array_50AC10[i]));
    array_50AC10[i].vWorldViewProjX = (double)pViewport->uScreenCenterX 
        - ((double)pODMRenderParams->int_fov_rad * array_507D30[i]._rhw) * array_507D30[i].vWorldViewPosition.y;
    array_50AC10[i].vWorldViewProjY = (double)pViewport->uScreenCenterY
        - ((double)pODMRenderParams->int_fov_rad * array_507D30[i]._rhw) * array_507D30[i].vWorldViewPosition.z;
   }
 }
//----- (00485F64) --------------------------------------------------------
void ODMRenderParams::Initialize()
{
  int v1; // eax@1
  int v2; // eax@2
  signed __int64 v3; // qtt@4
  int v4; // eax@4

  this->uCameraFovInDegrees = 75;
  v1 = stru_5C6E00->uPiMask & 0xD5;
  if ( v1 >= (signed int)stru_5C6E00->uIntegerHalfPi )
    v2 = -stru_5C6E00->pTanTable[stru_5C6E00->uIntegerPi - v1];
  else
    v2 = stru_5C6E00->pTanTable[v1];
  LODWORD(v3) = (viewparams->uSomeZ - viewparams->uSomeX) << 31;
  HIDWORD(v3) = (viewparams->uSomeZ - viewparams->uSomeX) << 15 >> 16;
  v4 = (signed int)(v3 / v2) >> 16;
  this->int_fov_rad = v4;
  this->field_4C = 360000;
  this->int_fov_rad_inv = 65536 / v4;
  this->field_50 = 115;
  //sr_6BE060[1] = 1;
  //RotationToInts();
}
//----- (00473893) --------------------------------------------------------
void ODM_ProcessPartyActions()
{
  int v1; // edi@1
  int v2; // ebx@1
  int floor_level; // eax@14
  ODMFace *face; // ecx@45
  int v34; // esi@143
  int v35; // esi@147
  int v36; // eax@155
  int v40; // esi@162
  bool v42; // eax@180
  signed int v43; // ecx@184
  signed int v44; // edx@184
  int v45; // ecx@200
  BSPModel *pModel; // eax@203
  bool pModel_;
  ODMFace *pODMFace; // esi@203
  int v48; // eax@203
  int v54; // eax@215
  int v55; // eax@217
  unsigned int v66; // esi@263
  signed int v68; // ecx@263
  int v69; // eax@263
  bool v77; // edx@297
  bool v78; // ecx@303
  int v79; // ecx@314
  __int16 v80; // dx@317
  int pTerrainHeight; // eax@321
  int v87; // [sp-20h] [bp-B4h]@248
  int v97; // [sp+Ch] [bp-88h]@180
  Vec3_int_ v98;
  bool not_high_fall; // [sp+1Ch] [bp-78h]@33
  int v102; // [sp+20h] [bp-74h]@1
  int trigger_id; // [sp+24h] [bp-70h]@1
  bool bFeatherFall; // [sp+28h] [bp-6Ch]@4
  int bonus;
  int on_ground; // [sp+2Ch] [bp-68h]@24
  bool bWaterWalk; // [sp+30h] [bp-64h]@1
  int ceiling_height; // [sp+3Ch] [bp-58h]@28
  int v110; // [sp+40h] [bp-54h]@180
  int v111; // [sp+44h] [bp-50h]@14
  bool hovering; // [sp+48h] [bp-4Ch]@1
  int v113; // [sp+4Ch] [bp-48h]@1
  bool party_running_flag; // [sp+50h] [bp-44h]@1
  int _walk_speed; // [sp+54h] [bp-40h]@48
  int pX; // [sp+58h] [bp-3Ch]@1
  int pY; // [sp+5Ch] [bp-38h]@1
  int party_new_Z; // [sp+74h] [bp-20h]@1
  int v118; // [sp+60h] [bp-34h]@1
  int _angle_x; // [sp+68h] [bp-2Ch]@48
  unsigned int v122; // [sp+70h] [bp-24h]@180

  bool party_walking_flag; // [sp+78h] [bp-1Ch]@1
  int _angle_y; // [sp+7Ch] [bp-18h]@48
  int v128; // [sp+88h] [bp-Ch]@1
  int v129; // [sp+8Ch] [bp-8h]@92

  v1 = 0;
  trigger_id = 0;
  v2 = 0;
  //*(float *)&v128 = 0.0;
  int fall_speed = pParty->uFallSpeed;
  v128 = 0;
  v129 = 0;

  pX = pParty->vPosition.x;
  pY = pParty->vPosition.y;
  party_new_Z = pParty->vPosition.z;

  v113 = pParty->field_6F0;
  hovering = false;
  bool partyAtHighSlope = IsTerrainSlopeTooHigh(pParty->vPosition.x, pParty->vPosition.y);
  party_running_flag = false;
  party_walking_flag = false;
  v102 = 0;
  pModel_ = false;
  bWaterWalk = false;
  //************************************
  //Проверка падение пера
  if (!pParty->FeatherFallActive())
  {
    bFeatherFall = false;
    for (int i = 0; i < 4; ++i)
      if (pParty->pPlayers[i].WearsItemAnyWhere(ITEM_ARTIFACT_LADYS_ESCORT))  // seems like flying boots
      {
        bFeatherFall = true;
        break;
      }
  }
  else
    bFeatherFall = true;
  //************************************
  //Проверка хождения по воде
  pParty->uFlags &= ~PARTY_FLAGS_1_STANDING_ON_WATER;
  if (pParty->WaterWalkActive())
  {
    //LOBYTE(pParty->uFlags) &= 0x7Fu;
    bWaterWalk = true;
    *(short *)&stru_5E4C90_MapPersistVars._decor_events[20 * pParty->pPartyBuffs[PARTY_BUFF_WATER_WALK].uOverlayID + 119] |= 1;
    if (!(pParty->pPartyBuffs[PARTY_BUFF_WATER_WALK].uFlags & 1) &&
          pParty->pPlayers[pParty->pPartyBuffs[PARTY_BUFF_WATER_WALK].uCaster - 1].sMana <= 0 )
      bWaterWalk = false;
  }
  //*************************************
  //определение уровня пола
  int bmodel_standing_on_pid;                           //данные 3D model'и
  int is_on_water = false;                              //на воду
  floor_level = ODM_GetFloorLevel(pX, pY, party_new_Z, pParty->uPartyHeight, &is_on_water, &bmodel_standing_on_pid, bWaterWalk);
  int is_not_on_bmodel = bmodel_standing_on_pid == 0;   //не на 3D model

  v111 = floor_level;                 //???
  //************************************
  //определение высоты падения
  if ( bFeatherFall )                 //падение пера
    pParty->uFallStartY = floor_level;
  else
    floor_level = pParty->uFallStartY;
  //*************************************
  //падение на 3D Model
  if ( floor_level - party_new_Z > 512 && !bFeatherFall && party_new_Z <= v111 + 1 )
  {
    if (pParty->uFlags & PARTY_FLAGS_1_LANDING)
      pParty->uFlags &= ~PARTY_FLAGS_1_LANDING;
    else for (int i = 0; i < 4; ++i)     // receive falling damage
    {
      if ( !pParty->pPlayers[i].HasEnchantedItemEquipped(72) && !pParty->pPlayers[i].WearsItem(ITEM_ARTIFACT_HERMES_SANDALS, EQUIP_BOOTS) )
      {
        pParty->pPlayers[i].ReceiveDamage(
            (signed int)((pParty->uFallStartY - party_new_Z) * (unsigned __int64)(pParty->pPlayers[i].GetMaxHealth() / 10)) / 256, DMGT_PHISYCAL);
        bonus = 20 - pParty->pPlayers[i].GetParameterBonus(pParty->pPlayers[i].GetActualEndurance());
        pParty->pPlayers[i].SetRecoveryTime((signed __int64)((double)bonus * flt_6BE3A4_debug_recmod1 * 2.133333333333333));
      }
    }
  }
  //*********************************
  //определение высоты потолка
  ceiling_height = -1;
  if ( pParty->bFlying )    //в полёте
    ceiling_height = GetCeilingHeight(pX, pY, party_new_Z + pParty->uPartyHeight, (int)&v102);//высота потолка
  //v107 = bmodel_standing_on_pid == 0;
  on_ground = v111 + 1;     //на земле
  //**************************************

  if ( party_new_Z <= on_ground )//полёт: посадка
  {
    ceiling_height = -1;
    pParty->bFlying = false;
  }
  else
    hovering = true;
  not_high_fall = party_new_Z - v111 <= 32;
  //****************************************
  //timer update(обновить таймер звука ходьбы)
  if ( bWalkSound && pParty->walk_sound_timer)
  {
    if (pParty->walk_sound_timer >= pEventTimer->uTimeElapsed)
      pParty->walk_sound_timer -= pEventTimer->uTimeElapsed;
    else
      pParty->walk_sound_timer = 0;
  }
  //****************************************
  // конец действия полёта
  if (!bUnderwater && pParty->pPartyBuffs[PARTY_BUFF_FLY].uExpireTime <= 0)//не под водой и время закла полёта закончилось
    pParty->bFlying = false;
  //*****************************************
  // установить на чём стоит группа
  if (!hovering)//не в воздухе
  {
    if ( pParty->floor_face_pid != PID(OBJECT_BModel, bmodel_standing_on_pid) )
    {
      if (bmodel_standing_on_pid)
      {
	    int BModel_id = bmodel_standing_on_pid >> 6;
        if ( BModel_id < pOutdoor->uNumBModels )
        {
          face = pOutdoor->pBModels[BModel_id].pFaces;
          int face_id = bmodel_standing_on_pid & 0x3F;
          /*if ( *(char *)(v7->pFacePlane.vNormal.x + 308 * v6 + 31) & 4 )
          {
            pParty->field_6F4_packedid = PID(OBJECT_BModel,v108);
            v103 = *(short *)(v7->pFacePlane.vNormal.x + 308 * v6 + 292);
          }*/
          if ( face[face_id].uAttributes & FACE_PRESSURE_PLATE )
          {
            pParty->floor_face_pid = PID(OBJECT_BModel, bmodel_standing_on_pid);
            trigger_id = face[face_id].sCogTriggeredID; //EVT, панель имеет событие
          }
        }
      }
    }
    pParty->floor_face_pid = PID(OBJECT_BModel, bmodel_standing_on_pid);//6 - на земле
  }
  //***********************************************
  _walk_speed = pParty->uWalkSpeed;
  _angle_y = pParty->sRotationY;
  _angle_x = pParty->sRotationX;
  //v126 = pEventTimer->dt_in_some_format;
  /*v119 = (Player **)((unsigned __int64)(pEventTimer->dt_in_some_format
                                      * (signed __int64)((signed int)(pParty->field_20_prolly_turn_speed
                                                                    * stru_5C6E00->uIntegerPi)
                                                       / 180)) >> 16);*/
  __int64 dturn = (unsigned __int64)(pEventTimer->dt_in_some_format * (signed __int64)((signed int)(pParty->y_rotation_speed * stru_5C6E00->uIntegerPi) / 180)) >> 16;
  while (pPartyActionQueue->uNumActions)
  {
    switch (pPartyActionQueue->Next())
    {
      case PARTY_FlyUp://полёт вверх
      {
        if (!pParty->FlyActive() && !bUnderwater)
          break;

        pParty->bFlying = false;
        if (bUnderwater ||
            pParty->pPartyBuffs[PARTY_BUFF_FLY].uFlags & 1 ||
            pParty->pPlayers[pParty->pPartyBuffs[PARTY_BUFF_FLY].uCaster - 1].sMana > 0 )
        {
          extern int max_flight_height;
          if ( pParty->vPosition.z < max_flight_height || hovering )
          {
            party_new_Z += 30;
            v113 += 30;
            pParty->bFlying = true;
            if ( party_new_Z > max_flight_height )
            {
              party_new_Z = max_flight_height;
              v113 = max_flight_height;
            }
            v1 = 0;
            v2 = 0;
            fall_speed = 0;
            *(float *)&v128 = 0.0;
            if ( v102 && party_new_Z < ceiling_height && (signed int)(pParty->uPartyHeight + party_new_Z) >= ceiling_height )//столкновение с потолком
            {
              pParty->field_6E0 = 0;
              pParty->field_6E4 = 0;
              pPartyActionQueue->uNumActions = 0;
              pParty->uFlags |= PARTY_FLAGS_1_LANDING;
              pParty->vPosition.z = ceiling_height - pParty->uPartyHeight - 31;
              pParty->field_6F0 = party_new_Z;
              pParty->bFlying = false;
              party_new_Z = ceiling_height - pParty->uPartyHeight - 31;
              v113 = pParty->field_6F0;
            }
            pParty->uFallSpeed = 0;
            pModel_ = true;
          }
        }
      }
      break;

      case PARTY_FlyDown://полёт вниз
        if (pParty->FlyActive() || bUnderwater)
        {
          pParty->bFlying = false;
          if ( bUnderwater
            || pParty->pPartyBuffs[PARTY_BUFF_FLY].uFlags & 1
            || pParty->pPlayers[pParty->pPartyBuffs[PARTY_BUFF_FLY].uCaster - 1].sMana > 0 )//*(int *)&pParty->pArtifactsFound[6972 * pParty->pPartyBuffs[PARTY_BUFF_FLY].uCaster + 10] > 0 )
          {
            party_new_Z -= 30;
            v113 -= 30;
            pParty->uFallSpeed = 0;
            fall_speed = 0;
            pParty->bFlying = true;
            pModel_ = true;
            if ( party_new_Z <= v111 )
            {
              pParty->bFlying = false;
              pPartyActionQueue->uNumActions = 0;
            }
          }
        }
        break;

      case PARTY_TurnLeft://поворот влево
        if (uTurnSpeed)
          _angle_y += uTurnSpeed;   //descrete turn
        else
          _angle_y += dturn * fTurnSpeedMultiplier;  // time-based smooth turn

        _angle_y &= stru_5C6E00->uDoublePiMask;
      break;

      case PARTY_TurnRight://поворот вправо
        if (uTurnSpeed)
          _angle_y -= uTurnSpeed;
        else
          _angle_y -= dturn * fTurnSpeedMultiplier;

        _angle_y &= stru_5C6E00->uDoublePiMask;
      break;

      case PARTY_FastTurnLeft://быстрый поворот влево
        if (uTurnSpeed)
          _angle_y += uTurnSpeed;
        else
          _angle_y += 2.0f * fTurnSpeedMultiplier * (double)dturn;

        _angle_y &= stru_5C6E00->uDoublePiMask;
      break;

      case PARTY_FastTurnRight://быстрый поворот вправо
        if (!uTurnSpeed)
          _angle_y -= 2.0f * fTurnSpeedMultiplier * (double)dturn;
        else
          _angle_y -= uTurnSpeed;

        _angle_y &= stru_5C6E00->uDoublePiMask;
      break;

      case PARTY_StrafeLeft://хождение боком в влево
      {
        *(float *)&v128 = pParty->uWalkSpeed;

        float sin_y = sinf(2 * pi_double * _angle_y / 2048.0);
        int dx = sin_y * pParty->uWalkSpeed * fWalkSpeedMultiplier;
        v2 -= 3 * dx / 4;
        
        float cos_y = cosf(2 * pi_double * _angle_y / 2048.0);
        int dy = cos_y * pParty->uWalkSpeed * fWalkSpeedMultiplier;
        v1 += 3 * dy / 4;

        v128 = v1;
        party_walking_flag = true;
      }
      break;

      case PARTY_StrafeRight://хождение боком в вправо
      {
        *(float *)&v128 = pParty->uWalkSpeed;

        float sin_y = sinf(2 * pi_double * _angle_y / 2048.0);
        int dx = sin_y * pParty->uWalkSpeed * fWalkSpeedMultiplier;
        v2 += 3 * dx / 4;
        
        float cos_y = cosf(2 * pi_double * _angle_y / 2048.0);
        int dy = cos_y * pParty->uWalkSpeed * fWalkSpeedMultiplier;
        v1 -= 3 * dy / 4;

        v128 = v1;
        party_walking_flag = true;
      }
      break;

      case PARTY_WalkForward:// идти вперёд
      {
        *(float *)&v128 = _walk_speed;

        float sin_y = sinf(2 * pi_double * _angle_y / 2048.0),
              cos_y = cosf(2 * pi_double * _angle_y / 2048.0);

        int dx = cos_y * pParty->uWalkSpeed * fWalkSpeedMultiplier;
        int dy = sin_y * pParty->uWalkSpeed * fWalkSpeedMultiplier;

        if ( new_speed )
        {
          v2 += dx * 12;
          v1 += dy * 12;
        }
        else
        {
          v2 += dx;
          v1 += dy;
        }

        v128 = v1;
        party_walking_flag = true;
      }
      break;

      case PARTY_RunForward://бежать вперёд
      {
        *(float *)&v128 = _walk_speed;

        float sin_y = sinf(2 * pi_double * _angle_y / 2048.0);
        float cos_y = cosf(2 * pi_double * _angle_y / 2048.0);

        int dx = cos_y * pParty->uWalkSpeed * fWalkSpeedMultiplier;
        int dy = sin_y * pParty->uWalkSpeed * fWalkSpeedMultiplier;

        if (pParty->bFlying)//лететь вперёд
        {
          v2 += 4 * dx;
          v1 += 4 * dy;

          v128 = v1;
        }
        else if (partyAtHighSlope && !bmodel_standing_on_pid)//сбегание со склона
        {
          v2 += dx;
          v1 += dy;

          v128 = v1;
          party_walking_flag = true;
        }
        else
        {
          /*v2 += (unsigned __int64)(stru_5C6E00->Cos(_angle_y)
                           * (signed __int64)(signed int)(2 * (unsigned __int64)(signed __int64)((double)_walk_speed * fWalkSpeedMultiplier))) >> 16;
          v1 += (unsigned __int64)((signed int)stru_5C6E00->Sin(_angle_y)
                           * (signed __int64)(signed int)(2 * (unsigned __int64)(signed __int64)((double)_walk_speed * fWalkSpeedMultiplier))) >> 16;*/

          v2 += 2 * dx;
          v1 += 2 * dy;
          
          v128 = v1;
          party_running_flag = true;
        }
      }
      break;


      case PARTY_WalkBackward://идти назад
      {
        *(float *)&v128 = _walk_speed;

        float sin_y = sinf(2 * 3.141592653589 * _angle_y / 2048.0),
              cos_y = cosf(2 * 3.141592653589 * _angle_y / 2048.0);

        int dx = cos_y * pParty->uWalkSpeed * fBackwardWalkSpeedMultiplier;
        v2 -= dx;
        
        int dy = sin_y * pParty->uWalkSpeed * fBackwardWalkSpeedMultiplier;
        v1 -= dy;

        v128 = v1;
        party_walking_flag = true;
      }
      break;


      case PARTY_RunBackward://бежать назад
      {
        float sin_y = sinf(2 * 3.141592653589 * _angle_y / 2048.0),
              cos_y = cosf(2 * 3.141592653589 * _angle_y / 2048.0);

        int dx = cos_y * pParty->uWalkSpeed * fBackwardWalkSpeedMultiplier;        
        int dy = sin_y * pParty->uWalkSpeed * fBackwardWalkSpeedMultiplier;

        if (pParty->bFlying)
        {
          v2 -= 4 * dx;
          v1 -= 4 * dy;
          v128 = v1;
        }
        else
        {
          v2 -= dx;
          v1 -= dy;

          v128 = v1;
          party_walking_flag = true;
        }
      }
      break;

      case PARTY_CenterView://смотреть прямо
        _angle_x = 0;
      break;

      case PARTY_LookUp://смотреть вверх
        _angle_x += (signed __int64)(flt_6BE150_look_up_down_dangle * 25.0);
        if ( _angle_x > 128 )
          _angle_x = 128;
        if (uActiveCharacter)
          pPlayers[uActiveCharacter]->PlaySound(SPEECH_63, 0);
      break;

      case PARTY_LookDown://смотреть вниз
        _angle_x += (signed __int64)(flt_6BE150_look_up_down_dangle * -25.0);
        if ( _angle_x < -128 )
          _angle_x = -128;
        if (uActiveCharacter)
          pPlayers[uActiveCharacter]->PlaySound(SPEECH_64, 0);
      break;

      case PARTY_Jump://прыжок
        if ( (!partyAtHighSlope || bmodel_standing_on_pid) && !hovering && pParty->field_24 && !(pParty->uFlags & 4) && !(pParty->uFlags & 0x200) )
        {
          //v126 = pParty->field_24 << 6;
          hovering = true;
          fall_speed = (signed __int64)((double)(pParty->field_24 << 6) * 1.5 + (double)fall_speed);
        }
      break;

      case PARTY_Land://приземление(клавиша Home)
        if (pParty->bFlying)
        {
          pParty->uFlags |= PARTY_FLAGS_1_LANDING;
          pParty->uFallSpeed = 0;
        }
        pParty->bFlying = false;
        pPartyActionQueue->uNumActions = 0;
      break;

      default:
        assert(false);


    }
  }

  pParty->sRotationY = _angle_y;
  pParty->sRotationX = _angle_x;
  //-------------------------------------------
  if ( pParty->bFlying )
  {
    v129 = fixpoint_mul(4, stru_5C6E00->Cos(GetTickCount()));
    party_new_Z = v113 + v129;
    if ( pModel_ )
      party_new_Z = v113;
    if (pParty->FlyActive())
      stru_5E4C90_MapPersistVars._decor_events[20 * pParty->pPartyBuffs[PARTY_BUFF_FLY].uOverlayID + 119] &= 0xFE;
    pParty->uFallStartY = party_new_Z;
  }
  else if ( party_new_Z < v111 )
  {
    if ( is_on_water && fall_speed )
      SpriteObject::sub_42F960_create_object(pX, pY, v111);
    fall_speed = 0;
    party_new_Z = v111;
    pParty->uFallStartY = v111;
    v113 = party_new_Z;
    if (pParty->FlyActive())
      stru_5E4C90_MapPersistVars._decor_events[20 * pParty->pPartyBuffs[PARTY_BUFF_FLY].uOverlayID + 119] |= 1;
  }
  else
  {
    v113 = party_new_Z;
    if (pParty->FlyActive())
      stru_5E4C90_MapPersistVars._decor_events[20 * pParty->pPartyBuffs[PARTY_BUFF_FLY].uOverlayID + 119] |= 1;
  }
  //------------------------------------------
  if (hovering && !pParty->bFlying)//расчёт скорости падения
  {
    //v33 = -(pEventTimer->uTimeElapsed * GetGravityStrength());
    v34 = fall_speed + (-(pEventTimer->uTimeElapsed * GetGravityStrength()) << 1);
    fall_speed += (-(pEventTimer->uTimeElapsed * GetGravityStrength()) << 1 ); //y(t) = 2*gt
  }
  else if (!partyAtHighSlope)
    v34 = fall_speed;
  else if (!hovering)
  {
    if ( !bmodel_standing_on_pid )
    {
      // rolling down the hill
      // how it's done: you get a little bit pushed in the air along terrain normal, getting in the air
      // and falling to the gravity, gradually sliding downwards. nice trick
      party_new_Z = v111;
      ODM_GetTerrainNormalAt(pX, pY, &v98);
      v35 = fall_speed + (8 * -(pEventTimer->uTimeElapsed * GetGravityStrength()));
      v129 = abs(v2 * v98.x + v1 * v98.y + v35 * v98.z) >> 16;
      v2 += fixpoint_mul(v129, v98.x);
      v1 += fixpoint_mul(v129, v98.y);
      v34 = v35 + fixpoint_mul(v129, v98.z);
      v128 = v1;
      fall_speed = v34;
    }
  }
  else
    v34 = fall_speed;

  if ( hovering )//блок для крика падения
  {
    if ( !bUnderwater && v34 <= 0)
    {
      if ( v34 < -500 && !pParty->bFlying && pParty->vPosition.z - v111 > 1000 && !pParty->FeatherFallActive())
      { // falling scream
        for (int i = 0; i < 4; ++i)
        {
          if (!pParty->pPlayers[i].HasEnchantedItemEquipped(72)
           && !pParty->pPlayers[i].WearsItem(ITEM_ARTIFACT_HERMES_SANDALS, EQUIP_BOOTS)
           && pParty->pPlayers[i].CanAct())
            pParty->pPlayers[i].PlaySound(SPEECH_Falling_scream, 0);//крик падения
        }
      }
    }
  }
  else
    pParty->uFallStartY = party_new_Z;

  if ( v2 * v2 + v1 * v1 < 400 && !partyAtHighSlope )
  {
    *(float *)&v128 = 0.0;
    v2 = 0;
  }
//--(столкновения)-------------------------------------------------------------------
  stru_721530.field_84 = -1;
  stru_721530.field_70 = 0;
  stru_721530.prolly_normal_d = pParty->field_14_radius;
  stru_721530.field_8_radius = pParty->field_14_radius / 2;
  stru_721530.field_0 = 1;
  stru_721530.height = pParty->uPartyHeight - 32;
  for ( uint i = 0; i < 100; i++ )
  {
    stru_721530.position.x = pX;
    stru_721530.position.y = pY;
    stru_721530.position.z = stru_721530.height + party_new_Z + 1;

    stru_721530.normal.x = pX;
    stru_721530.normal.y = pY;
    stru_721530.normal.z = stru_721530.prolly_normal_d + party_new_Z + 1;

    stru_721530.velocity.x = v2;
    stru_721530.velocity.y = v128;
    stru_721530.velocity.z = fall_speed;

    stru_721530.uSectorID = 0;
    v36 = 0;
    if ( pParty->bTurnBasedModeOn == true && pTurnEngine->turn_stage == TE_MOVEMENT )
      v36 = 13312;
    if ( stru_721530._47050A(v36) )
      break;
    _46E889_collide_against_bmodels(1);
    //v37 = WorldPosToGridCellZ(pParty->vPosition.y);
    //v38 = WorldPosToGridCellX(pParty->vPosition.x);
    _46E26D_collide_against_sprites(WorldPosToGridCellX(pParty->vPosition.x), WorldPosToGridCellZ(pParty->vPosition.y));
    _46ED8A_collide_against_sprite_objects(4);
    for ( uint actor_id = 0; actor_id < (signed int)uNumActors; ++actor_id )
      Actor::_46DF1A_collide_against_actor(actor_id, 0);
    if ( stru_721530.field_7C >= stru_721530.field_6C )
    {
      _angle_x = stru_721530.normal2.x;
      _angle_y = stru_721530.normal2.y;
      v40 = stru_721530.normal2.z - stru_721530.prolly_normal_d - 1;
    }
    else
    {
      _angle_x = pX + fixpoint_mul(stru_721530.field_7C, stru_721530.direction.x);
      _angle_y = pY + fixpoint_mul(stru_721530.field_7C, stru_721530.direction.y);
      //pModel = (BSPModel *)fixpoint_mul(stru_721530.field_7C, stru_721530.direction.z);
      v40 = fixpoint_mul(stru_721530.field_7C, stru_721530.direction.z) + party_new_Z;
    }
    v122 = v40;
    ODM_GetFloorLevel(_angle_x, _angle_y, v40, pParty->uPartyHeight, &is_on_water, &bmodel_standing_on_pid, 0);
    v129 = ODM_GetFloorLevel(_angle_x, pY, v40, pParty->uPartyHeight, &is_on_water, &v97, 0);
    int v119 = ODM_GetFloorLevel(pX, _angle_y, v40, pParty->uPartyHeight, &is_on_water, &v110, 0);
    bool v42_ = (BSPModel *)IsTerrainSlopeTooHigh(_angle_x, pY);
    v42 = IsTerrainSlopeTooHigh(pX, _angle_y);
    is_not_on_bmodel = false;
    //v118 = v42;
    if ( !v97 && !v110 && !bmodel_standing_on_pid )
      is_not_on_bmodel = true;
    v43 = 1;
    v44 = 1;
    if ( bUnderwater || !is_not_on_bmodel )
    {
      pX = _angle_x;
      if ( v43 )
        pY = _angle_y;
    }
    else
    {
      if ( v42_ && v129 > party_new_Z )
        v44 = 0;
      if ( v42 && v119 > party_new_Z )
        v43 = 0;
      if ( v44 )
      {
        pX = _angle_x;
        if ( v43 )
          pY = _angle_y;
      }
      else if ( v43 )
        pY = _angle_y;
      else
      {
        int new_ = ODM_GetFloorLevel(_angle_x, _angle_y, v40, pParty->uPartyHeight, &is_on_water, &bmodel_standing_on_pid, 0);
        if ( IsTerrainSlopeTooHigh(_angle_x, _angle_y) && new_ <= party_new_Z )
        {
          v43 = 1;
          pX = _angle_x;
          if ( v43 )
            pY = _angle_y;
        }
      }
    }
    if ( stru_721530.field_7C >= stru_721530.field_6C )
    {
      if ( !is_not_on_bmodel )
      {
        pX = stru_721530.normal2.x;
        pY = stru_721530.normal2.y;
      }
      party_new_Z = stru_721530.normal2.z - stru_721530.prolly_normal_d - 1;
      break;
    }
    stru_721530.field_70 += stru_721530.field_7C;
    pX = _angle_x;
    pY = _angle_y;
    v45 = stru_721530.uFaceID;
    party_new_Z = v40;

    if ( PID_TYPE(stru_721530.uFaceID) == OBJECT_Actor)
    {
      if (pParty->Invisible())
        pParty->pPartyBuffs[PARTY_BUFF_INVISIBILITY].Reset();
      viewparams->bRedrawGameUI = true;
    }

    if ( PID_TYPE(stru_721530.uFaceID) == OBJECT_Decoration)
    {
      v129 = stru_5C6E00->Atan2(_angle_x - pLevelDecorations[(signed int)stru_721530.uFaceID >> 3].vPosition.x,
                               _angle_y - pLevelDecorations[(signed int)stru_721530.uFaceID >> 3].vPosition.y);
      v2 = fixpoint_mul(stru_5C6E00->Cos(v129), integer_sqrt(v2 * v2 + v128 * v128));
      v122 = fixpoint_mul(stru_5C6E00->Sin(v129), integer_sqrt(v2 * v2 + v128 * v128));
      v128 = fixpoint_mul(stru_5C6E00->Sin(v129), integer_sqrt(v2 * v2 + v128 * v128));
    }

    if ( PID_TYPE(stru_721530.uFaceID) == OBJECT_BModel)
    {
      pParty->bFlying = false;
      pModel = &pOutdoor->pBModels[(signed int)stru_721530.uFaceID >> 9];
      pODMFace = &pModel->pFaces[((signed int)stru_721530.uFaceID >> 3) & 0x3F];
      v48 = pODMFace->pBoundingBox.z2 - pODMFace->pBoundingBox.z1;
      v129 = v48 <= 32;
      v119 = pODMFace->pFacePlane.vNormal.z < 46378;
      if ( bUnderwater == 1 )
        v119 = 0;
      if ( pODMFace->uPolygonType == POLYGON_Floor )
      {
        if ( fall_speed < 0 )
          fall_speed = 0;
        party_new_Z = pModel->pVertices.pVertices[pODMFace->pVertexIDs[0]].z + 1;
        if ( v2 * v2 + v128 * v128 < 400 )
        {
          v2 = 0;
          *(float *)&v128 = 0.0;
        }
        if ( pParty->floor_face_pid != v45 && pODMFace->Pressure_Plate() )
        {
          pParty->floor_face_pid = v45;
          trigger_id = pODMFace->sCogTriggeredID; 
        }
      }
      if ( !v129 && (pODMFace->uPolygonType != POLYGON_InBetweenFloorAndWall || v119) )// упёрся в столб
      {
        v118 = abs(v128 * pODMFace->pFacePlane.vNormal.y + fall_speed * pODMFace->pFacePlane.vNormal.z
             + v2 * pODMFace->pFacePlane.vNormal.x) >> 16;
        if ((stru_721530.speed >> 3) > v118 )
          v118 = stru_721530.speed >> 3;
        v2 += fixpoint_mul(v118, pODMFace->pFacePlane.vNormal.x);
        v128 += fixpoint_mul(v118, pODMFace->pFacePlane.vNormal.y);
        v54 = 0;
        if ( !v119 )
          v54 = fixpoint_mul(v118, pODMFace->pFacePlane.vNormal.z);
        pParty->uFallSpeed += v54;
        v55 = stru_721530.prolly_normal_d - ((signed int)(pODMFace->pFacePlane.dist + v122 * pODMFace->pFacePlane.vNormal.z
                    + _angle_y * pODMFace->pFacePlane.vNormal.y + _angle_x * pODMFace->pFacePlane.vNormal.x) >> 16);
        if ( v55 > 0 )
        {
          pX = _angle_x + fixpoint_mul(pODMFace->pFacePlane.vNormal.x, v55);
          pY = _angle_y + fixpoint_mul(pODMFace->pFacePlane.vNormal.y, v55);
          if ( !v119 )
            party_new_Z = v122 + fixpoint_mul(pODMFace->pFacePlane.vNormal.z, v55);
        }
        if ( pParty->floor_face_pid != stru_721530.uFaceID && pODMFace->Pressure_Plate() )
        {
          pParty->floor_face_pid = stru_721530.uFaceID;
          trigger_id = pODMFace->sCogTriggeredID; //
        }
      }
      if ( pODMFace->uPolygonType == POLYGON_InBetweenFloorAndWall )
      {
        v118 = abs(v128 * pODMFace->pFacePlane.vNormal.y
             + fall_speed * pODMFace->pFacePlane.vNormal.z + v2 * pODMFace->pFacePlane.vNormal.x) >> 16;
        if ((stru_721530.speed >> 3) > v118 )
          v118 = stru_721530.speed >> 3;
        v2 += fixpoint_mul(v118, pODMFace->pFacePlane.vNormal.x);
        v128 += fixpoint_mul(v118, pODMFace->pFacePlane.vNormal.y);
        fall_speed += fixpoint_mul(v118, pODMFace->pFacePlane.vNormal.z);
        if ( v2 * v2 + v128 * v128 >= 400 )
        {
          if ( pParty->floor_face_pid != stru_721530.uFaceID && pODMFace->Pressure_Plate() )
          {
            pParty->floor_face_pid = stru_721530.uFaceID;
            trigger_id = pODMFace->sCogTriggeredID; //
          }
        }
        else
        {
          v2 = 0;
          fall_speed = 0;
          *(float *)&v128 = 0.0;
        }
      }
    }
    v2 = fixpoint_mul(58500, v2);
    v128 = fixpoint_mul(58500, v128);
    v122 = fixpoint_mul(58500, v122);
    fall_speed = fixpoint_mul(58500, fall_speed);
  }

  //Воспроизведение звуков ходьбы/бега------------------------
  uint pX_ = abs(pParty->vPosition.x - pX);
  uint pY_ = abs(pParty->vPosition.y - pY);
  uint pZ_ = abs(pParty->vPosition.z - party_new_Z);
  if ( bWalkSound && pParty->walk_sound_timer <= 0 )
  {
    pAudioPlayer->_4AA258(804);//stop sound
    if ( party_running_flag && (!hovering || not_high_fall) )
    {
      if ( integer_sqrt(pX_ * pX_ + pY_ * pY_ + pZ_ * pZ_) >= 16 )
      {
        if ( !is_not_on_bmodel && pOutdoor->pBModels[pParty->floor_face_pid >> 9].pFaces[(pParty->floor_face_pid >> 3) & 0x3F].Visible() )
          pAudioPlayer->PlaySound(SOUND_RunWood, 804, 1, -1, 0, 0, 0, 0);//бег на 3D Modelи
        else
        {
          v87 = pOutdoor->GetSoundIdByPosition(WorldPosToGridCellX(pParty->vPosition.x), WorldPosToGridCellZ(pParty->vPosition.y) - 1, 1);
          pAudioPlayer->PlaySound((SoundID)v87, 804, 1, -1, 0, 0, 0, 0);//бег по земле 56
        }
        pParty->walk_sound_timer = 96;//таймер для бега
      }
    }
    else if( party_walking_flag && (!hovering || not_high_fall) )
    {
      if ( integer_sqrt(pX_ * pX_ + pY_ * pY_ + pZ_ * pZ_) >= 8 )
      {
        if ( !is_not_on_bmodel && pOutdoor->pBModels[pParty->floor_face_pid >> 9].pFaces[(pParty->floor_face_pid >> 3) & 0x3F].Visible() )
          pAudioPlayer->PlaySound(SOUND_WalkWood, 804, 1, -1, 0, 0, 0, 0);// хождение на 3D Modelи
        else
        {
          v87 = pOutdoor->GetSoundIdByPosition(WorldPosToGridCellX(pParty->vPosition.x), WorldPosToGridCellZ(pParty->vPosition.y) - 1, 0);
          pAudioPlayer->PlaySound((SoundID)v87, 804, 1, -1, 0, 0, 0, 0);// хождение по земле
        }
        pParty->walk_sound_timer = 144;//таймер для ходьбы
      }
    }
  }
  if ( integer_sqrt(pX_ * pX_ + pY_ * pY_ + pZ_ * pZ_) < 8 )//отключить  звук ходьбы при остановке
    pAudioPlayer->_4AA258(804);
//------------------------------------------------------------------------
  if ( !hovering || !not_high_fall )//  или не высокое падение
    pParty->uFlags &= ~PARTY_FLAGS_1_FALLING;
  else
    pParty->uFlags |= PARTY_FLAGS_1_FALLING;
  int pMap_X = WorldPosToGridCellX(pParty->vPosition.x);
  int pMap_Y = WorldPosToGridCellZ(pParty->vPosition.y) - 1;
  unsigned int v114_a = WorldPosToGridCellX(pX);
  v66 = WorldPosToGridCellZ(pY) - 1;
  unsigned int v122_a = (~(unsigned int)pOutdoor->ActuallyGetSomeOtherTileInfo(pMap_X, pMap_Y) / 2) & 1;
  v122 = (~(unsigned int)pOutdoor->ActuallyGetSomeOtherTileInfo(v114_a, pMap_Y) / 2) & 1;
  v69 = (~(unsigned int)pOutdoor->ActuallyGetSomeOtherTileInfo(pMap_X, v66) / 2) & 1;

  //-(обновление координат группы)---------------------------------------
  v68 = 0;
  if ( v114_a == pMap_X && v66 == pMap_Y && v122 && v69 )
    v68 = 1;
  if ( !is_not_on_bmodel ) // на bmodel,и
    v68 = 1;
  if ( v68 )
  {
    pParty->vPosition.x = pX;
    pParty->vPosition.y = pY;
    pParty->vPosition.z = party_new_Z;
    pParty->field_6F0 = v113;
    pParty->uFallSpeed = fall_speed;
    if ( party_new_Z > 8160 ) //ограничение высоты
    {
      party_new_Z = 8160;
      pParty->uFallStartY = 8160;
      pParty->vPosition.z = 8160;
    }

    if ( !trigger_id //падение на землю
      || (EventProcessor(trigger_id, 0, 1),
         pParty->vPosition.x == pX)
      && pParty->vPosition.y == pY
      && pParty->vPosition.z == party_new_Z )
    {
      if ( pParty->vPosition.z < v111 )
      {
        pParty->uFallSpeed = 0;
        //v73 = v105;
        pParty->vPosition.z = on_ground;
        if ( pParty->uFallStartY - party_new_Z > 512 && !bFeatherFall
			&& party_new_Z <= on_ground && !bUnderwater )//Fall to the ground(падение на землю с высоты)
        {
          if ( pParty->uFlags & PARTY_FLAGS_1_LANDING )
            pParty->uFlags &= ~PARTY_FLAGS_1_LANDING;
          else
          {
            for ( uint i = 1; i <= 4; ++i )
            {
              pPlayers[i]->ReceiveDamage((signed int)((pParty->uFallStartY - party_new_Z) * (unsigned __int64)(signed __int64)((double)pPlayers[i]->GetMaxHealth() * 0.1)) / 256,
                DMGT_PHISYCAL);
              v110 = 20 - pPlayers[i]->GetParameterBonus(pPlayers[i]->GetActualEndurance());
              pPlayers[i]->SetRecoveryTime((signed __int64)((double)v110 * flt_6BE3A4_debug_recmod1 * 2.133333333333333));
            }
            //v73 = pParty->vPosition.z;
          }
        }
        pParty->uFallStartY = party_new_Z;
      }
      if ( v102 && pParty->vPosition.z < ceiling_height )
      {
        if ( (signed int)(pParty->uPartyHeight + pParty->vPosition.z) >= ceiling_height )
        {
          pParty->vPosition.z = ceiling_height - pParty->uPartyHeight - 1;
          pParty->field_6F0 = ceiling_height - pParty->uPartyHeight - 1;
        }
      }
      pParty->uFlags &= ~0x204;
    }
    return;
  }
//-----------------------------------------------------------------
  //v76 = pParty->bFlying;
  if ( pParty->bFlying || !not_high_fall || bWaterWalk || !v122_a )// полёт или высокое падение или хождение по воде или 
    v77 = 1;
  else
    v77 = v122 != 0;
  bool party_drowning_flag = false;
  if ( !pParty->bFlying && not_high_fall && !bWaterWalk ) //не полёт и не высокое падение и не хождение по воде
  {
    if ( v122_a )
      v78 = v69 != 0;
    else
    {
      party_drowning_flag = true;//утопление
      v78 = true;
    }
  }
  else
    v78 = true;

  if ( v77 )
    pParty->vPosition.x = pX;
  if ( v78 )
    pParty->vPosition.y = pY;

  if ( v78 || v77)
  {
    if ( bWaterWalk )
    {
      pParty->uFlags &= ~PARTY_FLAGS_1_STANDING_ON_WATER;
      //v79 = 20 * pParty->pPartyBuffs[PARTY_BUFF_WATER_WALK].uOverlayID + 6180178;
      //*(short *)&stru_5E4C90._decor_events[20 * pParty->pPartyBuffs[PARTY_BUFF_WATER_WALK].uOverlayID + 119] |= 1u;
      v79 = (int)&stru_5E4C90_MapPersistVars._decor_events[20 * pParty->pPartyBuffs[PARTY_BUFF_WATER_WALK].uOverlayID + 119];
      *(short *)v79 |= 1;
      if ( !v122 || !v69 )
      {
        if ( !pParty->bFlying )
        {
          v80 = *(short *)v79;
          pParty->uFlags |= PARTY_FLAGS_1_STANDING_ON_WATER;
          *(short *)v79 = v80 & 0xFFFE;
        }
      }
    }
  }
  else if ( bWalkSound && pParty->walk_sound_timer <= 0 )
  {
    pAudioPlayer->_4AA258(804);
    pParty->walk_sound_timer = 64;
  }

  //v81 = pZ;
  //v82 = pZ;
  pParty->vPosition.z = party_new_Z;
  if ( party_new_Z > 8160 )//опять ограничение высоты
  {
    //v82 = 8160;
    pParty->uFallStartY = 8160;
    pParty->vPosition.z = 8160;
  }
  LOWORD(pParty->uFlags) &= 0xFDFBu;
  pParty->uFallSpeed = fall_speed;
  pParty->field_6F0 = v113;
  if ( party_drowning_flag )//группа тонет
  {
    pTerrainHeight = GetTerrainHeightsAroundParty2(pParty->vPosition.x, pParty->vPosition.y, &v110, 1);
    if ( pParty->vPosition.z <= pTerrainHeight + 1 )//положение группы всегда +1
      pParty->uFlags |= PARTY_FLAGS_1_WATER_DAMAGE;
  }

  if ( !trigger_id//падение на воду
    || (EventProcessor(trigger_id, 0, 1),
       pParty->vPosition.x == pX)
    && pParty->vPosition.y == pY
    && pParty->vPosition.z == party_new_Z )
  {
    if ( pParty->vPosition.z < v111 )
    {
      //v82 = on_ground;
      pParty->uFallSpeed = 0;
      pParty->vPosition.z = on_ground;
      if ( pParty->uFallStartY - party_new_Z > 512
		  && !bFeatherFall && party_new_Z <= on_ground && !bUnderwater )//Fall to the water(падение на воду с высоты)
      {
        if ( pParty->uFlags & PARTY_FLAGS_1_LANDING )
          pParty->uFlags &= ~PARTY_FLAGS_1_LANDING;
        else
        {
          for ( uint i = 1; i <= 4; ++i )
          {
            v110 = pPlayers[i]->GetMaxHealth();
            pPlayers[i]->ReceiveDamage((signed int)((pParty->uFallStartY - party_new_Z) * (unsigned __int64)(signed __int64)((double)v110 * 0.1)) / 256,
              DMGT_PHISYCAL);
            v110 = 20 - pPlayers[i]->GetParameterBonus(pPlayers[i]->GetActualEndurance());
            pPlayers[i]->SetRecoveryTime((signed __int64)((double)v110 * flt_6BE3A4_debug_recmod1 * 2.133333333333333));
          }
          //v82 = pParty->vPosition.z;
        }
      }
      pParty->uFallStartY = party_new_Z;
    }
    if ( v102 && pParty->vPosition.z < ceiling_height && (signed int)(pParty->uPartyHeight + pParty->vPosition.z) >= ceiling_height )
    {
      pParty->vPosition.z = pParty->vPosition.z + pParty->uPartyHeight - ceiling_height + 1;
      pParty->field_6F0 = pParty->vPosition.z + pParty->uPartyHeight - ceiling_height + 1;
    }
  }
}

//----- (0046D8E3) --------------------------------------------------------
int GetCeilingHeight(int Party_X, signed int Party_Y, int Party_ZHeight, int pFaceID)
{
  signed int v13; // eax@25
  int v14; // edx@27
  int v16; // ST18_4@29
  signed int v17; // edx@29
  signed __int64 v18; // qtt@29
  int v19; // eax@35
  signed int v20; // ecx@37
  signed int v22; // ebx@42
//  int v24; // edx@44
//  int v25; // eax@44
  int v27; // [sp+10h] [bp-34h]@21
  bool v34; // [sp+30h] [bp-14h]@21
  bool v35; // [sp+34h] [bp-10h]@23
  signed int v37; // [sp+38h] [bp-Ch]@21
  signed int v38; // [sp+38h] [bp-Ch]@42
  signed int v39; // [sp+3Ch] [bp-8h]@1

  dword_720ED0[0] = -1;
  dword_720E80[0] = -1;
  v39 = 1;
  ceiling_height_level[0] = 10000;//нет потолка
  for ( uint i = 0; i < (signed int)pOutdoor->uNumBModels; ++i )
  {
    if ( Party_X <= pOutdoor->pBModels[i].sMaxX && Party_X >= pOutdoor->pBModels[i].sMinX
      && Party_Y <= pOutdoor->pBModels[i].sMaxY && Party_Y >= pOutdoor->pBModels[i].sMinY )
    {
      for ( uint j = 0; j < pOutdoor->pBModels[i].uNumFaces; ++j )
      {
        if ( (pOutdoor->pBModels[i].pFaces[j].uPolygonType == POLYGON_Ceiling
           || pOutdoor->pBModels[i].pFaces[j].uPolygonType == POLYGON_InBetweenCeilingAndWall)
           && !pOutdoor->pBModels[i].pFaces[j].Ethereal()
           && Party_X <= pOutdoor->pBModels[i].pFaces[j].pBoundingBox.x2 && Party_X >= pOutdoor->pBModels[i].pFaces[j].pBoundingBox.x1
           && Party_Y <= pOutdoor->pBModels[i].pFaces[j].pBoundingBox.y2 && Party_Y >= pOutdoor->pBModels[i].pFaces[j].pBoundingBox.y1 )
        {
          for ( uint v = 0; v < pOutdoor->pBModels[i].pFaces[j].uNumVertices; v++ )
          {
            word_720DB0_xs[2 * v] = pOutdoor->pBModels[i].pFaces[j].pXInterceptDisplacements[v] + LOWORD(pOutdoor->pBModels[i].pVertices.pVertices[pOutdoor->pBModels[i].pFaces[j].pVertexIDs[v]].x);
            word_720CE0_ys[2 * v] = pOutdoor->pBModels[i].pFaces[j].pXInterceptDisplacements[v] + LOWORD(pOutdoor->pBModels[i].pVertices.pVertices[pOutdoor->pBModels[i].pFaces[j].pVertexIDs[v]].y);
            word_720DB0_xs[2 * v + 1] = pOutdoor->pBModels[i].pFaces[j].pXInterceptDisplacements[v] + LOWORD(pOutdoor->pBModels[i].pVertices.pVertices[pOutdoor->pBModels[i].pFaces[j].pVertexIDs[v + 1]].x);
            word_720CE0_ys[2 * v + 1] = pOutdoor->pBModels[i].pFaces[j].pXInterceptDisplacements[v] + LOWORD(pOutdoor->pBModels[i].pVertices.pVertices[pOutdoor->pBModels[i].pFaces[j].pVertexIDs[v + 1]].y);
          }
          v27 = 2 * pOutdoor->pBModels[i].pFaces[j].uNumVertices;
          word_720DB0_xs[2 * pOutdoor->pBModels[i].pFaces[j].uNumVertices] = word_720DB0_xs[0];
          word_720CE0_ys[2 * pOutdoor->pBModels[i].pFaces[j].uNumVertices] = word_720CE0_ys[0];
          v34 = word_720CE0_ys[0] >= Party_Y;
          v37 = 0;
          for ( uint v = 0; v < v27; ++v )
          {
            if ( v37 >= 2 )
              break;
            v35 = word_720CE0_ys[v + 1] >= Party_Y;
            if ( v34 != v35 )
            {
              v13 = word_720DB0_xs[v + 1] >= Party_X ? 0 : 2;
              v14 = v13 | (word_720DB0_xs[v] < Party_X);
              if ( v14 != 3 )
              {
                if ( !v14 || ( v16 = word_720CE0_ys[v + 1] - word_720CE0_ys[v],
                  v17 = Party_Y - word_720CE0_ys[v],
                  LODWORD(v18) = v17 << 16,
                  HIDWORD(v18) = v17 >> 16,
                  (signed int)(((unsigned __int64)(((signed int)word_720DB0_xs[v + 1]
                  - (signed int)word_720DB0_xs[v]) * v18 / v16) >> 16) + word_720DB0_xs[v]) >= Party_X) )
                  ++v37;
              }
            }
            v34 = v35;
          }
          if ( v37 == 1 )
          {
            if ( v39 >= 20 )
              break;
            if ( pOutdoor->pBModels[i].pFaces[j].uPolygonType == POLYGON_Ceiling )
              v19 = pOutdoor->pBModels[i].pVertices.pVertices[pOutdoor->pBModels[i].pFaces[j].pVertexIDs[0]].z;
            else
              v19 = fixpoint_mul(pOutdoor->pBModels[i].pFaces[j].zCalc1, Party_X) + fixpoint_mul(pOutdoor->pBModels[i].pFaces[j].zCalc2, Party_Y)
                  + HIWORD(pOutdoor->pBModels[i].pFaces[j].zCalc3);
            v20 = v39++;
            ceiling_height_level[v20] = v19;
            dword_720ED0[v20] = i;
            dword_720E80[v20] = j;
          }
        }
      }
    }
  }
  if ( !v39 )
  {
    pFaceID = 0;
    return ceiling_height_level[0];
  }
  v22 = 0;
  for ( v38 = 0; v38 < v39; ++v38 )
  {
    if ( ceiling_height_level[v38] == ceiling_height_level[0] )
      v22 = v38;
    else if ( ceiling_height_level[v38] < ceiling_height_level[0] && ceiling_height_level[0] > Party_ZHeight + 15 )
      v22 = v38;
    else if ( ceiling_height_level[v38] > ceiling_height_level[0] && ceiling_height_level[v38] <= Party_ZHeight + 15 )
      v22 = v38;
  }
  if ( v22 )
  {
    *(int *)pFaceID = dword_720E80[v22] | (dword_720ED0[v22] << 6);
    return ceiling_height_level[v22];//если есть преграда
  }
  pFaceID = 0;
  return ceiling_height_level[v22];// нет никакой преграды
}


//----- (00464839) --------------------------------------------------------
char Is_out15odm_underwater()
{
  return _stricmp(pCurrentMapName, "out15.odm") == 0;
}

//----- (00464851) --------------------------------------------------------
void SetUnderwaterFog()
{
  day_fogrange_1 = 50;
  day_fogrange_2 = 5000;
}

//----- (00487DA9) --------------------------------------------------------
void sub_487DA9()
{
	for (int i = 0; i < 20000; ++i)
		array_77EC08[i].field_108 = 0;
}

//----- (004706C6) --------------------------------------------------------
void UpdateActors_ODM()
{
	int v3; // ebx@6
	int v5; // eax@10
	//int v6; // ecx@10
	signed int v8; // ebx@17
	//  unsigned __int8 v10; // sf@17
	//  unsigned __int16 v11; // ax@21
	__int16 v20; // ax@42
	int v25; // eax@45
	signed int v26; // ecx@50
	int v28; // eax@54
	signed int v29; // ebx@57
	signed int v30; // eax@57
	int v31; // edi@57
	signed int i; // ebx@57
	unsigned int v33; // ecx@58
	int v35; // edi@64
	int v36; // eax@64
	unsigned int v39; // edi@71
	ODMFace *face; // edi@75
	int v46; // ecx@82
	signed int v47; // ebx@85
	int v48; // edi@85
	//  int v55; // eax@107
	//  unsigned int v56; // edi@107
	//  int v57; // ST10_4@107
	unsigned int v58; // edi@107
	unsigned int v59; // ebx@107
	//  signed int v60; // eax@107
	int v61; // eax@124
	Vec3_int_ v62; // [sp+Ch] [bp-44h]@42
	int v63; // [sp+18h] [bp-38h]@64
	int v64; // [sp+1Ch] [bp-34h]@64
	bool v67; // [sp+28h] [bp-28h]@10
	unsigned int v69; // [sp+30h] [bp-20h]@6
	unsigned int v70; // [sp+34h] [bp-1Ch]@10
	int v71; // [sp+38h] [bp-18h]@62
	int uIsAboveFloor; // [sp+3Ch] [bp-14h]@10
	int v72b;
	int uIsFlying; // [sp+44h] [bp-Ch]@8
	unsigned int v75; // [sp+48h] [bp-8h]@1
	int uIsOnWater; // [sp+4Ch] [bp-4h]@10

	for (v75 = 0; v75 < uNumActors; ++v75)
	{
		if (pActors[v75].uAIState == Removed
			|| pActors[v75].uAIState == Disabled
			|| pActors[v75].uAIState == Summoned
			|| !pActors[v75].uMovementSpeed)
			continue;
		v3 = 0;
		v69 = 0;
		if (MonsterStats::BelongsToSupertype(pActors[v75].pMonsterInfo.uID, MONSTER_SUPERTYPE_WATER_ELEMENTAL))
			v3 = 1;
		pActors[v75].uSectorID = 0;
		uIsFlying = pActors[v75].pMonsterInfo.uFlying;
		if (!pActors[v75].CanAct())
			uIsFlying = 0;
		v70 = IsTerrainSlopeTooHigh(pActors[v75].vPosition.x, pActors[v75].vPosition.y);
		v5 = ODM_GetFloorLevel(pActors[v75].vPosition.x, pActors[v75].vPosition.y, pActors[v75].vPosition.z,
			pActors[v75].uActorHeight, &uIsOnWater, (int *)&v69, v3);
		//v6 = pActors[v75].vPosition.z;
		uIsAboveFloor = 0;
		v67 = v69 == 0;
		if (pActors[v75].vPosition.z > v5 + 1)
			uIsAboveFloor = 1;
		if (pActors[v75].uAIState == Dead && uIsOnWater && !uIsAboveFloor)
		{
			pActors[v75].uAIState = Removed;
			continue;
		}
		if (pActors[v75].uCurrentActionAnimation == ANIM_Walking)
		{
			v8 = pActors[v75].uMovementSpeed;
			if ((signed __int64)pActors[v75].pActorBuffs[ACTOR_BUFF_SLOWED].uExpireTime > 0)
				v8 = (signed __int64)((double)v8 * 0.5);
			if (pActors[v75].uAIState == Fleeing || pActors[v75].uAIState == Pursuing)
				v8 *= 2;
			if (pParty->bTurnBasedModeOn == true && pTurnEngine->turn_stage == TE_WAIT)
				v8 *= flt_6BE3AC_debug_recmod1_x_1_6;
			if (v8 > 1000)
				v8 = 1000;

			pActors[v75].vVelocity.x = fixpoint_mul(stru_5C6E00->Cos(pActors[v75].uYawAngle), v8);
			pActors[v75].vVelocity.y = fixpoint_mul(stru_5C6E00->Sin(pActors[v75].uYawAngle), v8);
			if (uIsFlying)
			{
				pActors[v75].vVelocity.z = fixpoint_mul(stru_5C6E00->Sin(pActors[v75].uPitchAngle), v8);
			}
			//v7 = v68;
		}
		else
		{
			pActors[v75].vVelocity.x = fixpoint_mul(55000, pActors[v75].vVelocity.x);
			pActors[v75].vVelocity.y = fixpoint_mul(55000, pActors[v75].vVelocity.y);
			if (uIsFlying)
				pActors[v75].vVelocity.z = fixpoint_mul(55000, pActors[v75].vVelocity.z);
		}
		if (pActors[v75].vPosition.z < v5)
		{
			pActors[v75].vPosition.z = v5;
			pActors[v75].vVelocity.z = uIsFlying != 0 ? 0x14 : 0;
		}
		//v17 = 0;
		if (!uIsAboveFloor || uIsFlying)
		{
			if (v70 && !uIsAboveFloor && v67)
			{
				pActors[v75].vPosition.z = v5;
				ODM_GetTerrainNormalAt(pActors[v75].vPosition.x, pActors[v75].vPosition.y, &v62);
				v20 = GetGravityStrength();
				//v21 = v62.y;
				//v22 = v62.z;
				//v23 = v62.y * v0->vVelocity.y;
				pActors[v75].vVelocity.z += -8 * LOWORD(pEventTimer->uTimeElapsed) * v20;
				int v73 = abs(v62.x * pActors[v75].vVelocity.x + v62.z * pActors[v75].vVelocity.z + v62.y * pActors[v75].vVelocity.y) >> 16;
				//v72b = v21;
				pActors[v75].vVelocity.x += fixpoint_mul(v73, v62.x);
				pActors[v75].vVelocity.y += fixpoint_mul(v73, v62.y);
				pActors[v75].vVelocity.z += fixpoint_mul(v73, v62.z);
				//v17 = 0;
			}
		}
		else
		{
			pActors[v75].vVelocity.z -= LOWORD(pEventTimer->uTimeElapsed) * GetGravityStrength();
		}
		if (pParty->armageddon_timer != 0 && pActors[v75].CanAct())
		{
			pActors[v75].vVelocity.x += rand() % 100 - 50;
			pActors[v75].vVelocity.y += rand() % 100 - 50;
			pActors[v75].vVelocity.z += rand() % 100 - 20;
			v25 = rand();
			pActors[v75].uAIState = Stunned;
			pActors[v75].uYawAngle += v25 % 32 - 16;
			pActors[v75].UpdateAnimation();
		}
		if (pActors[v75].vVelocity.x * pActors[v75].vVelocity.x + pActors[v75].vVelocity.y * pActors[v75].vVelocity.y < 400 && v70 == 0)
		{
			pActors[v75].vVelocity.y = 0;
			pActors[v75].vVelocity.x = 0;
		}
		stru_721530.field_0 = 1;
		if (!uIsFlying)
			v26 = 40;
		else
			v26 = pActors[v75].uActorRadius;

		stru_721530.field_84 = -1;
		stru_721530.field_8_radius = v26;
		stru_721530.prolly_normal_d = v26;
		stru_721530.height = pActors[v75].uActorHeight;
		stru_721530.field_70 = 0;

		for (v69 = 0; v69 < 100; ++v69)
		{
			stru_721530.position.x = pActors[v75].vPosition.x;
			stru_721530.normal.x = stru_721530.position.x;
			stru_721530.position.y = pActors[v75].vPosition.y;
			stru_721530.normal.y = stru_721530.position.y;
			v28 = pActors[v75].vPosition.z;
			stru_721530.normal.z = v28 + v26 + 1;
			stru_721530.position.z = v28 - v26 + stru_721530.height - 1;
			if (stru_721530.position.z < stru_721530.normal.z)
				stru_721530.position.z = v28 + v26 + 1;
			stru_721530.velocity.x = pActors[v75].vVelocity.x;
			stru_721530.uSectorID = 0;
			stru_721530.velocity.y = pActors[v75].vVelocity.y;
			stru_721530.velocity.z = pActors[v75].vVelocity.z;
			if (stru_721530._47050A(0))
				break;
			_46E889_collide_against_bmodels(1);
			v29 = WorldPosToGridCellZ(pActors[v75].vPosition.y);
			v30 = WorldPosToGridCellX(pActors[v75].vPosition.x);
			_46E26D_collide_against_sprites(v30, v29);
			_46EF01_collision_chech_player(0);
			_46ED8A_collide_against_sprite_objects(PID(OBJECT_Actor, v75));
			v31 = 0;
			for (i = 0; v31 < ai_arrays_size; ++v31)
			{
				v33 = ai_near_actors_ids[v31];
				if (v33 != v75 && Actor::_46DF1A_collide_against_actor(v33, 40))
					++i;
			}
			v71 = i > 1;
			if (stru_721530.field_7C < stru_721530.field_6C)
				v70 = fixpoint_mul(stru_721530.field_7C, stru_721530.direction.z);
			//v34 = 0;
			v35 = stru_721530.normal2.z - stru_721530.prolly_normal_d - 1;
			v36 = ODM_GetFloorLevel(stru_721530.normal2.x, stru_721530.normal2.y,
				stru_721530.normal2.z - stru_721530.prolly_normal_d - 1,
				pActors[v75].uActorHeight, (int *)&v63, &v64, 0);
			if (uIsOnWater)
			{
				if (v35 < v36 + 60)
				{
					if (pActors[v75].uAIState == Dead || pActors[v75].uAIState == Dying || pActors[v75].uAIState == Removed
						|| pActors[v75].uAIState == Disabled)
					{
						if (v64)
							v61 = v36 + 30;
						else
							v61 = v5 + 60;
						SpriteObject::sub_42F960_create_object(pActors[v75].vPosition.x, pActors[v75].vPosition.y, v61);
						pActors[v75].uAIState = Removed;
						return;
					}
				}
			}
			if (stru_721530.field_7C >= stru_721530.field_6C)
			{
				pActors[v75].vPosition.x = LOWORD(stru_721530.normal2.x);
				pActors[v75].vPosition.y = LOWORD(stru_721530.normal2.y);
				pActors[v75].vPosition.z = LOWORD(stru_721530.normal2.z) - LOWORD(stru_721530.prolly_normal_d) - 1;
				break;
			}
			//v72b = fixpoint_mul(stru_721530.field_7C, stru_721530.field_58.x);
			pActors[v75].vPosition.x += fixpoint_mul(stru_721530.field_7C, stru_721530.direction.x);
			//v72b = (unsigned __int64)(stru_721530.field_7C * (signed __int64)stru_721530.field_58.y) >> 16;
			pActors[v75].vPosition.y += fixpoint_mul(stru_721530.field_7C, stru_721530.direction.y);
			//v72b = (unsigned __int64)(stru_721530.field_7C * (signed __int64)stru_721530.field_58.z) >> 16;
			pActors[v75].vPosition.z += fixpoint_mul(stru_721530.field_7C, stru_721530.direction.z);
			stru_721530.field_70 += stru_721530.field_7C;
			v39 = PID_ID(stru_721530.uFaceID);
			switch (PID_TYPE(stru_721530.uFaceID))
			{
			case OBJECT_Actor:
				if (pTurnEngine->turn_stage != TE_ATTACK && pTurnEngine->turn_stage != TE_MOVEMENT || pParty->bTurnBasedModeOn != TE_WAIT)
				{
					//if(pParty->bTurnBasedModeOn == 1)
					//v34 = 0;
					if (pActors[v75].pMonsterInfo.uHostilityType)
					{
						if (v71 == 0)
							Actor::AI_Flee(v75, stru_721530.uFaceID, 0, (AIDirection *)0);
						else
							Actor::AI_StandOrBored(v75, 4, 0, (AIDirection *)0);
					}
					else if (v71)
						Actor::AI_StandOrBored(v75, 4, 0, (AIDirection *)0);
					else if (pActors[v39].pMonsterInfo.uHostilityType == MonsterInfo::Hostility_Friendly)
						Actor::AI_Flee(v75, stru_721530.uFaceID, 0, (AIDirection *)0);
					else
						Actor::AI_FaceObject(v75, stru_721530.uFaceID, 0, (AIDirection *)0);
				}
				break;
			case OBJECT_Player:
				if (!pActors[v75].GetActorsRelation(0))
				{
					Actor::AI_FaceObject(v75, stru_721530.uFaceID, 0, (AIDirection *)0);
					break;
				}
				//v52 = HIDWORD(pParty->pPartyBuffs[PARTY_BUFF_INVISIBILITY].uExpireTime) == 0;
				//v53 = SHIDWORD(pParty->pPartyBuffs[PARTY_BUFF_INVISIBILITY].uExpireTime) < 0;
				pActors[v75].vVelocity.y = 0;
				pActors[v75].vVelocity.x = 0;
				//if ( !v53 && (!(v53 | v52) || LODWORD(pParty->pPartyBuffs[PARTY_BUFF_INVISIBILITY].uExpireTime) > 0) )
				if ((signed __int64)pParty->pPartyBuffs[PARTY_BUFF_INVISIBILITY].uExpireTime < 0)
					pParty->pPartyBuffs[PARTY_BUFF_INVISIBILITY].Reset();
				viewparams->bRedrawGameUI = 1;
				break;
			case OBJECT_Decoration:
				v47 = integer_sqrt(pActors[v75].vVelocity.x * pActors[v75].vVelocity.x + pActors[v75].vVelocity.y * pActors[v75].vVelocity.y);
				v48 = stru_5C6E00->Atan2(pActors[v75].vPosition.x - pLevelDecorations[v39].vPosition.x,
					pActors[v75].vPosition.y - pLevelDecorations[v39].vPosition.y);
				//v49 = v48;
				pActors[v75].vVelocity.x = fixpoint_mul(stru_5C6E00->Cos(v48), v47);
				pActors[v75].vVelocity.y = fixpoint_mul(stru_5C6E00->Sin(v48), v47);
				break;
			case OBJECT_BModel:
				face = &pOutdoor->pBModels[stru_721530.uFaceID >> 9].pFaces[v39 & 0x3F];
				if (!face->Ethereal())
				{
					if (face->uPolygonType == 3)
					{
						pActors[v75].vVelocity.z = 0;
						pActors[v75].vPosition.z = LOWORD(pOutdoor->pBModels[stru_721530.uFaceID >> 9].pVertices.pVertices[face->pVertexIDs[0]].z) + 1;
						if (pActors[v75].vVelocity.x * pActors[v75].vVelocity.x
							+ pActors[v75].vVelocity.y * pActors[v75].vVelocity.y < 400)
						{
							pActors[v75].vVelocity.y = 0;
							pActors[v75].vVelocity.x = 0;
						}
					}
					else
					{
						v72b = abs(face->pFacePlane.vNormal.y * pActors[v75].vVelocity.y + face->pFacePlane.vNormal.z * pActors[v75].vVelocity.z
							+ face->pFacePlane.vNormal.x * pActors[v75].vVelocity.x) >> 16;
						if ((stru_721530.speed >> 3) > v72b)
							v72b = stru_721530.speed >> 3;

						pActors[v75].vVelocity.x += fixpoint_mul(v72b, face->pFacePlane.vNormal.x);
						pActors[v75].vVelocity.y += fixpoint_mul(v72b, face->pFacePlane.vNormal.y);
						pActors[v75].vVelocity.z += fixpoint_mul(v72b, face->pFacePlane.vNormal.z);
						if (face->uPolygonType != 4)
						{
							v46 = stru_721530.prolly_normal_d
								- ((face->pFacePlane.dist
								+ face->pFacePlane.vNormal.x * pActors[v75].vPosition.x
								+ face->pFacePlane.vNormal.y * pActors[v75].vPosition.y
								+ face->pFacePlane.vNormal.z * pActors[v75].vPosition.z) >> 16);
							if (v46 > 0)
							{
								pActors[v75].vPosition.x += fixpoint_mul(v46, face->pFacePlane.vNormal.x);
								pActors[v75].vPosition.y += fixpoint_mul(v46, face->pFacePlane.vNormal.y);
								pActors[v75].vPosition.z += fixpoint_mul(v46, face->pFacePlane.vNormal.z);
							}
							pActors[v75].uYawAngle = stru_5C6E00->Atan2(pActors[v75].vVelocity.x, pActors[v75].vVelocity.y);
						}
					}
				}
				break;
			}

			pActors[v75].vVelocity.x = fixpoint_mul(58500, pActors[v75].vVelocity.x);
			pActors[v75].vVelocity.y = fixpoint_mul(58500, pActors[v75].vVelocity.y);
			pActors[v75].vVelocity.z = fixpoint_mul(58500, pActors[v75].vVelocity.z);

			v26 = stru_721530.prolly_normal_d;
		}

		v58 = ((unsigned int)~pOutdoor->ActuallyGetSomeOtherTileInfo(WorldPosToGridCellX(pActors[v75].vPosition.x), WorldPosToGridCellZ(pActors[v75].vPosition.y) - 1) >> 1) & 1;
		v59 = ((unsigned int)~pOutdoor->ActuallyGetSomeOtherTileInfo(WorldPosToGridCellX(pActors[v75].vPosition.x), WorldPosToGridCellZ(pActors[v75].vPosition.y) - 1) >> 1) & 1;
		if (WorldPosToGridCellX(pActors[v75].vPosition.x) == WorldPosToGridCellX(pActors[v75].vPosition.x)
			&& WorldPosToGridCellZ(pActors[v75].vPosition.y) == WorldPosToGridCellZ(pActors[v75].vPosition.y)
			&& v58 || v67 != 0)
		{
			if (MonsterStats::BelongsToSupertype(pActors[v75].pMonsterInfo.uID, MONSTER_SUPERTYPE_WATER_ELEMENTAL))
			{
				v58 = v58 == 0;
				v59 = v59 == 0;
			}
			if (!uIsFlying && v58 && !v59)
			{
				pActors[v75].vPosition.x = pActors[v75].vPosition.x;
				pActors[v75].vPosition.y = pActors[v75].vPosition.y;
				if (pActors[v75].CanAct())
				{
					pActors[v75].uYawAngle -= 32;
					pActors[v75].uCurrentActionTime = 0;
					pActors[v75].uCurrentActionLength = 128;
					pActors[v75].uAIState = Fleeing;
				}
			}
		}
	}
}

//----- (0047A384) --------------------------------------------------------
void ODM_LoadAndInitialize(const char *pLevelFilename, ODMRenderParams *thisa)
{
	int v2; // ebx@3
	unsigned int v3; // eax@3
	MapInfo *v4; // edi@4
	//int v5; // eax@8
	//SpawnPointMM7 *v6; // edx@14
	size_t v7; // eax@19
	//char *v8; // eax@19
	//char *v9; // eax@21
	char Source[120]; // [sp+Ch] [bp-84h]@19
	const char *pFilename; // [sp+84h] [bp-Ch]@1
	//unsigned int v12; // [sp+88h] [bp-8h]@12
	//int v13; // [sp+8Ch] [bp-4h]@11
	int v;

	pFilename = pLevelFilename;
	//thisa->AllocSoftwareDrawBuffers();
	pODMRenderParams->Initialize();
	pWeather->bRenderSnow = false;
	pRenderer->ClearZBuffer(0, 479);
	//thisa = (ODMRenderParams *)1;
	GetAlertStatus();
	if (_A750D8_player_speech_timer)
		_A750D8_player_speech_timer = 0;
	v2 = pMapStats->GetMapInfo(pCurrentMapName);
	v3 = 0;
	if (v2)
	{
		v4 = &pMapStats->pInfos[v2];
		v3 = v4->uRespawnIntervalDays;
	}
	else
		v4 = (MapInfo *)1;
	day_attrib &= ~DAY_ATTRIB_FOG;
	dword_6BE13C_uCurrentlyLoadedLocationID = v2;
	pOutdoor->Initialize(
		pFilename,
		(unsigned int)(signed __int64)((double)(signed __int64)pParty->uTimePlayed * 0.234375) / 0x3C / 0x3C / 0x18 + 1,
		v3,
		&v);
	if (!(dword_6BE364_game_settings_1 & GAME_SETTINGS_2000))
	{
		Actor::InitializeActors();
		SpriteObject::InitializeSpriteObjects();
	}
	dword_6BE364_game_settings_1 &= ~GAME_SETTINGS_2000;
	//v5 = 0;
	if (!v2)
		v = 0;
	if (v == 1)
	{
		//v13 = 0;
		for (uint i = 0; i < pOutdoor->uNumSpawnPoints; ++i)
		{
			//v12 = 0;
			//while ( 1 )
			//{
			SpawnPointMM7* spawn = pOutdoor->pSpawnPoints + i;
			//v6 = &pOutdoor->pSpawnPoints[v12 / 0x18];
			if (spawn->uKind == 3)
				SpawnEncounter(v4, spawn, 0, 0, 0);
			else
				v4->SpawnRandomTreasure(spawn);
			//++v13;
			//v12 += 24;
			//if ( v13 >= (signed int)pOutdoor->uNumSpawnPoints )
			//  break;
			//v5 = 0;
			//}
		}
		RespawnGlobalDecorations();
	}
	pOutdoor->PrepareDecorations();
	pOutdoor->ArrangeSpriteObjects();
	pOutdoor->InitalizeActors(v2);
	pOutdoor->MessWithLUN();
	v7 = strlen("levels\\");
	strcpy(Source, &pFilename[v7]);
	strcpy(pOutdoor->pLevelFilename, Source);
	pWeather->Initialize();
	pEngine->pIndoorCameraD3D->sRotationY = pParty->sRotationY;
	pEngine->pIndoorCameraD3D->sRotationX = pParty->sRotationX;
	//pODMRenderParams->RotationToInts();
	pOutdoor->UpdateSunlightVectors();

	float fov_rad;
	float fov_rad_inv;
	//----- (0042394D) --------------------------------------------------------
	//void IndoorCamera::Initialize(int degFov, unsigned int uViewportWidth, unsigned int uViewportHeight)
	{
		//pIndoorCamera->Initialize(65, viewparams->uScreen_BttmR_X - viewparams->uScreen_topL_X + 1,
		//                              viewparams->uScreen_BttmR_Y - viewparams->uScreen_topL_Y + 1);

		int uViewportWidth = viewparams->uScreen_BttmR_X - viewparams->uScreen_topL_X + 1;

		extern float _calc_fov(int viewport_width, int angle_degree);
		fov_rad = _calc_fov(uViewportWidth, 65);
		fov_rad_inv = 65536.0 / fov_rad;
	}
	pODMRenderParams->int_fov_rad = (signed __int64)fov_rad;
	pODMRenderParams->int_fov_rad_inv = (signed __int64)fov_rad_inv;

	for (int i = 0; i < 20000; ++i)
	{
		array_77EC08[i].ptr_38 = &stru_8019C8;

		array_77EC08[i].ptr_48 = nullptr;
	}

	MM7Initialization();
}

//----- (0047C370) --------------------------------------------------------
unsigned int GetLevelFogColor()
{
	signed __int64 v1; // qax@5
	int v2; // eax@6

	if (bUnderwater)
		return 0xFF258F5C;

	if (day_attrib & DAY_ATTRIB_FOG)
	{
		if (pWeather->bNight) // night-time fog
		{
          if (for_refactoring)
           {
              MessageBoxA(nullptr, "Nomad: decompilation can be inaccurate, please send savegame to Nomad", "", 0);
              __debugbreak();
           }
			v2 = -(pWeather->bNight != 1);
			return (v2 & 0xE0E0E1) - 0xE0E0E1;
		}
		else
		{
			v1 = (signed __int64)((1.0 - pOutdoor->fFogDensity) * 200.0 + pOutdoor->fFogDensity * 31.0);
			return v1 | (((unsigned int)v1 | (((unsigned int)v1 | 0xFFFFFF00) << 8)) << 8);
		}
	}

	return 0;
}

//----- (0047C3D7) --------------------------------------------------------
int __fastcall sub_47C3D7_get_fog_specular(int a1, int a2, float a3)
{
	int v3; // ecx@1
	signed int v7; // ecx@11

	v3 = pWeather->bNight;
	if (bUnderwater == 1)
		v3 = 0;
	if (pParty->armageddon_timer || !(day_attrib & DAY_ATTRIB_FOG) && !bUnderwater)
		return 0xFF000000;
	if (v3)
	{
		if (a3 < (double)day_fogrange_1)
		{
			v7 = 0;
			if (a3 == 0.0)
				v7 = 216;
			if (a2)
				v7 = 248;
			return (-1 - v7) << 24;
		}
		else
		{
			if (a3 > (double)day_fogrange_2)
			{
				v7 = 216;
				if (a3 == 0.0)
					v7 = 216;
				if (a2)
					v7 = 248;
				return (-1 - v7) << 24;
			}
			v7 = (signed __int64)((a3 - (double)day_fogrange_1) / ((double)day_fogrange_2 - (double)day_fogrange_1) * 216.0);
		}
	}
	else
	{
		if (a3 < (double)day_fogrange_1)
		{
			v7 = 0;
			if (a3 == 0.0)
				v7 = 216;
			if (a2)
				v7 = 248;
			return (-1 - v7) << 24;
		}
		else
		{
			if (a3 > (double)day_fogrange_2)
			{
				v7 = 216;
				if (a3 == 0.0)
					v7 = 216;
				if (a2)
					v7 = 248;
				return (-1 - v7) << 24;
			}
			else
				v7 = floorf(((a3 - (double)day_fogrange_1) * 216.0 / ((double)day_fogrange_2 - (double)day_fogrange_1)) + 0.5f);
		}
	}
	if (v7 > 216)
		v7 = 216;
	else
	{
		if (a3 == 0.0)
			v7 = 216;
	}
	if (a2)
		v7 = 248;
	return (-1 - v7) << 24;
}


//----- (0047F44B) --------------------------------------------------------
unsigned int WorldPosToGridCellX(int sWorldPosX)
{
	return (sWorldPosX >> 9) + 64; // sar is in original exe, resulting -880 / 512 = -1
	//                               and -880 sar 9 = -2
}

//----- (0047F458) --------------------------------------------------------
unsigned int WorldPosToGridCellZ(int sWorldPosZ)
{
	return 64 - (sWorldPosZ >> 9); // sar is in original exe, resulting -880 / 512 = -1
	//                               and -880 sar 9 = -2
}

//----- (0047F469) --------------------------------------------------------
int GridCellToWorldPosX(int a1)
{
	return (a1 - 64) << 9;
}

//----- (0047F476) --------------------------------------------------------
int GridCellToWorldPosZ(int a1)
{
	return (64 - a1) << 9;
}



//----- (00481ED9) --------------------------------------------------------
void  sub_481ED9_MessWithODMRenderParams()
{
	stru_8019C8._48616B_frustum_odm(65536, 0, 0, 0, 65536, 0);
	pODMRenderParams->uNumPolygons = 0;
	//pODMRenderParams->uNumEdges = 0;
	//pODMRenderParams->uNumSpans = 0;
	//pODMRenderParams->uNumSurfs = 0;
	pODMRenderParams->uNumBillboards = 0;
	pODMRenderParams->field_44 = 0;
}

//----- (004823F4) --------------------------------------------------------
bool IsTerrainSlopeTooHigh(int pos_x, int pos_z)
{
	//unsigned int v2; // ebx@1
	//unsigned int v3; // edi@1
	//int v4; // eax@1
	//int v6; // esi@5
	//int v7; // ecx@6
	//int v8; // edx@6
	//int v9; // eax@6
	//int y_min; // esi@10
	//int v11; // [sp+14h] [bp-8h]@1
	//int v12; // [sp+18h] [bp-4h]@1

	//v12 = a1;
	//v11 = a2;
	unsigned int grid_x = WorldPosToGridCellX(pos_x);
	unsigned int grid_z = WorldPosToGridCellZ(pos_z) - 1;

	int party_grid_x1 = GridCellToWorldPosX(grid_x);
	//dword_76D56C_terrain_cell_world_pos_around_party_x = GridCellToWorldPosX(grid_x + 1);
	//dword_76D570_terrain_cell_world_pos_around_party_x = GridCellToWorldPosX(grid_x + 1);
	//dword_76D574_terrain_cell_world_pos_around_party_x = GridCellToWorldPosX(grid_x);
	int party_grid_z1 = GridCellToWorldPosZ(grid_z);
	//dword_76D55C_terrain_cell_world_pos_around_party_z = GridCellToWorldPosZ(grid_z);
	//dword_76D560_terrain_cell_world_pos_around_party_z = GridCellToWorldPosZ(grid_z + 1);
	//dword_76D564_terrain_cell_world_pos_around_party_z = GridCellToWorldPosZ(grid_z + 1);
	int party_x1z1_y = pOutdoor->DoGetHeightOnTerrain(grid_x, grid_z);
	int party_x2z1_y = pOutdoor->DoGetHeightOnTerrain(grid_x + 1, grid_z);
	int party_x2z2_y = pOutdoor->DoGetHeightOnTerrain(grid_x + 1, grid_z + 1);
	int party_x1z2_y = pOutdoor->DoGetHeightOnTerrain(grid_x, grid_z + 1);
	//dword_76D554_terrain_cell_world_pos_around_party_y = v4;
	if (party_x1z1_y == party_x2z1_y &&
		party_x2z1_y == party_x2z2_y &&
		party_x2z2_y == party_x1z2_y)
		return false;

	int dx = abs(pos_x - party_grid_x1),
		dz = abs(party_grid_z1 - pos_z);

	int y1, y2, y3;
	if (dz >= dx)
	{
		y1 = party_x1z2_y;  //  lower-left triangle
		y2 = party_x2z2_y;  //  y3 | \ 
		y3 = party_x1z1_y;  //     |   \ 
		/*     |     \
		|______ \
		y1           y2   */
	}
	else
	{
		y1 = party_x2z1_y;  // upper-right
		y2 = party_x1z1_y;  //  y2_______ y1
		y3 = party_x2z2_y;  //    \     |
		/*      \   |
		\ |
		y3     */
	}

	int y_min = min(y1, min(y2, y3));// не верно при подъёме на склон
	int y_max = max(y1, max(y2, y3));
	return (y_max - y_min) > 512;
}

//----- (0048257A) --------------------------------------------------------
int __fastcall GetTerrainHeightsAroundParty2(int a1, int a2, int *pIsOnWater, int bFloatAboveWater)
{
	//  int result; // eax@9
	int v8; // ebx@11
	int v9; // eax@11
	int v10; // ecx@11
	int v13; // [sp+10h] [bp-8h]@11
	signed int v14; // [sp+14h] [bp-4h]@3
	int v15; // [sp+24h] [bp+Ch]@11

	unsigned int grid_x = WorldPosToGridCellX(a1);
	unsigned int grid_z = WorldPosToGridCellZ(a2) - 1;

	int grid_x1 = GridCellToWorldPosX(grid_x),
		grid_x2 = GridCellToWorldPosX(grid_x + 1);
	int grid_z1 = GridCellToWorldPosZ(grid_z),
		grid_z2 = GridCellToWorldPosZ(grid_z + 1);

	int y_x1z1 = pOutdoor->DoGetHeightOnTerrain(grid_x, grid_z),
		y_x2z1 = pOutdoor->DoGetHeightOnTerrain(grid_x + 1, grid_z),
		y_x2z2 = pOutdoor->DoGetHeightOnTerrain(grid_x + 1, grid_z + 1),
		y_x1z2 = pOutdoor->DoGetHeightOnTerrain(grid_x, grid_z + 1);
	//v4 = WorldPosToGridCellX(a1);
	//v5 = WorldPosToGridCellZ(v12) - 1;
	//dword_76D538_terrain_cell_world_pos_around_party_x = GridCellToWorldPosX(v4);
	//dword_76D53C_terrain_cell_world_pos_around_party_x = GridCellToWorldPosX(v4 + 1);
	//dword_76D540_terrain_cell_world_pos_around_party_x = GridCellToWorldPosX(v4 + 1);
	//dword_76D544_terrain_cell_world_pos_around_party_x = GridCellToWorldPosX(v4);
	//dword_76D528_terrain_cell_world_pos_around_party_z = GridCellToWorldPosZ(v5);
	//dword_76D52C_terrain_cell_world_pos_around_party_z = GridCellToWorldPosZ(v5);
	//dword_76D530_terrain_cell_world_pos_around_party_z = GridCellToWorldPosZ(v5 + 1);
	//dword_76D534_terrain_cell_world_pos_around_party_z = GridCellToWorldPosZ(v5 + 1);
	//dword_76D518_terrain_cell_world_pos_around_party_y = pOutdoor->DoGetHeightOnTerrain(v4, v5);
	//dword_76D51C_terrain_cell_world_pos_around_party_y = pOutdoor->DoGetHeightOnTerrain(v4 + 1, v5);
	//dword_76D520_terrain_cell_world_pos_around_party_y = pOutdoor->DoGetHeightOnTerrain(v4 + 1, v5 + 1);
	//dword_76D524_terrain_cell_world_pos_around_party_y = pOutdoor->DoGetHeightOnTerrain(v4, v5 + 1);
	*pIsOnWater = false;
	if (pOutdoor->ActuallyGetSomeOtherTileInfo(grid_x, grid_z) & 2)
		*pIsOnWater = true;
	v14 = 0;
	if (!bFloatAboveWater && *pIsOnWater)
		v14 = -60;
	if (y_x1z1 != y_x2z1 ||
		y_x2z1 != y_x2z2 ||
		y_x2z2 != y_x1z2)
	{
		if (abs(grid_z1 - a2) >= abs(a1 - grid_x1))
		{
			v8 = y_x1z2;
			v9 = y_x2z2;
			v10 = y_x1z1;
			v15 = a1 - grid_x1;
			v13 = a2 - grid_z2;
		}
		else
		{
			v8 = y_x2z1;
			v9 = y_x1z1;
			v10 = y_x2z2;
			v15 = grid_x2 - a1;
			v13 = grid_z1 - a2;
		}
		return v14 + v8 + fixpoint_mul(v13, (v10 - v8) * 128) + fixpoint_mul(v15, (v9 - v8) * 128);
	}
	else
		return y_x1z1;
}